From 863981e96738983919de841ec669e157e6bdaeb0 Mon Sep 17 00:00:00 2001 From: André Fabian Silva Delgado Date: Sun, 11 Sep 2016 04:34:46 -0300 Subject: Linux-libre 4.7.1-gnu --- drivers/net/dsa/Kconfig | 45 +- drivers/net/dsa/Makefile | 15 +- drivers/net/dsa/bcm_sf2.c | 62 +- drivers/net/dsa/mv88e6060.c | 47 +- drivers/net/dsa/mv88e6060.h | 11 + drivers/net/dsa/mv88e6123.c | 124 --- drivers/net/dsa/mv88e6131.c | 177 ---- drivers/net/dsa/mv88e6171.c | 123 --- drivers/net/dsa/mv88e6352.c | 345 ------- drivers/net/dsa/mv88e6xxx.c | 2280 ++++++++++++++++++++++++++++--------------- drivers/net/dsa/mv88e6xxx.h | 381 +++++--- 11 files changed, 1781 insertions(+), 1829 deletions(-) delete mode 100644 drivers/net/dsa/mv88e6123.c delete mode 100644 drivers/net/dsa/mv88e6131.c delete mode 100644 drivers/net/dsa/mv88e6171.c delete mode 100644 drivers/net/dsa/mv88e6352.c (limited to 'drivers/net/dsa') diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 90ba003d8..200663c43 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -1,10 +1,6 @@ menu "Distributed Switch Architecture drivers" depends on HAVE_NET_DSA -config NET_DSA_MV88E6XXX - tristate - default n - config NET_DSA_MV88E6060 tristate "Marvell 88E6060 ethernet switch chip support" depends on NET_DSA @@ -13,46 +9,13 @@ config NET_DSA_MV88E6060 This enables support for the Marvell 88E6060 ethernet switch chip. -config NET_DSA_MV88E6XXX_NEED_PPU - bool - default n - -config NET_DSA_MV88E6131 - tristate "Marvell 88E6085/6095/6095F/6131 ethernet switch chip support" - depends on NET_DSA - select NET_DSA_MV88E6XXX - select NET_DSA_MV88E6XXX_NEED_PPU - select NET_DSA_TAG_DSA - ---help--- - This enables support for the Marvell 88E6085/6095/6095F/6131 - ethernet switch chips. - -config NET_DSA_MV88E6123 - tristate "Marvell 88E6123/6161/6165 ethernet switch chip support" - depends on NET_DSA - select NET_DSA_MV88E6XXX - select NET_DSA_TAG_EDSA - ---help--- - This enables support for the Marvell 88E6123/6161/6165 - ethernet switch chips. - -config NET_DSA_MV88E6171 - tristate "Marvell 88E6171/6175/6350/6351 ethernet switch chip support" - depends on NET_DSA - select NET_DSA_MV88E6XXX - select NET_DSA_TAG_EDSA - ---help--- - This enables support for the Marvell 88E6171/6175/6350/6351 - ethernet switches chips. - -config NET_DSA_MV88E6352 - tristate "Marvell 88E6172/6176/6320/6321/6352 ethernet switch chip support" +config NET_DSA_MV88E6XXX + tristate "Marvell 88E6xxx Ethernet switch chip support" depends on NET_DSA - select NET_DSA_MV88E6XXX select NET_DSA_TAG_EDSA ---help--- - This enables support for the Marvell 88E6172, 88E6176, 88E6320, - 88E6321 and 88E6352 ethernet switch chips. + This enables support for most of the Marvell 88E6xxx models of + Ethernet switch chips, except 88E6060. config NET_DSA_BCM_SF2 tristate "Broadcom Starfighter 2 Ethernet switch support" diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index a6e09939b..76b751dd9 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -1,16 +1,3 @@ obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o -obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx_drv.o -mv88e6xxx_drv-y += mv88e6xxx.o -ifdef CONFIG_NET_DSA_MV88E6123 -mv88e6xxx_drv-y += mv88e6123.o -endif -ifdef CONFIG_NET_DSA_MV88E6131 -mv88e6xxx_drv-y += mv88e6131.o -endif -ifdef CONFIG_NET_DSA_MV88E6352 -mv88e6xxx_drv-y += mv88e6352.o -endif -ifdef CONFIG_NET_DSA_MV88E6171 -mv88e6xxx_drv-y += mv88e6171.o -endif +obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm_sf2.o diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 95944d5e3..10ddd5a5d 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -135,8 +135,17 @@ static int bcm_sf2_sw_get_sset_count(struct dsa_switch *ds) return BCM_SF2_STATS_SIZE; } -static char *bcm_sf2_sw_probe(struct device *host_dev, int sw_addr) +static const char *bcm_sf2_sw_drv_probe(struct device *dsa_dev, + struct device *host_dev, int sw_addr, + void **_priv) { + struct bcm_sf2_priv *priv; + + priv = devm_kzalloc(dsa_dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return NULL; + *_priv = priv; + return "Broadcom Starfighter 2"; } @@ -151,7 +160,7 @@ static void bcm_sf2_imp_vlan_setup(struct dsa_switch *ds, int cpu_port) * the same VLAN. */ for (i = 0; i < priv->hw_params.num_ports; i++) { - if (!((1 << i) & ds->phys_port_mask)) + if (!((1 << i) & ds->enabled_port_mask)) continue; reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(i)); @@ -545,12 +554,11 @@ static void bcm_sf2_sw_br_leave(struct dsa_switch *ds, int port) priv->port_sts[port].bridge_dev = NULL; } -static int bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port, - u8 state) +static void 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)); @@ -574,7 +582,7 @@ static int bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port, break; default: pr_err("%s: invalid STP state: %d\n", __func__, state); - return -EINVAL; + return; } /* Fast-age ARL entries if we are moving a port from Learning or @@ -584,10 +592,9 @@ static int bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port, if (cur_hw_state != hw_state) { if (cur_hw_state >= G_MISTP_LEARN_STATE && hw_state <= G_MISTP_LISTEN_STATE) { - ret = bcm_sf2_sw_fast_age_port(ds, port); - if (ret) { + if (bcm_sf2_sw_fast_age_port(ds, port)) { pr_err("%s: fast-ageing failed\n", __func__); - return ret; + return; } } } @@ -596,8 +603,6 @@ static int bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port, reg &= ~(G_MISTP_STATE_MASK << G_MISTP_STATE_SHIFT); reg |= hw_state; core_writel(priv, reg, CORE_G_PCTL_PORT(port)); - - return 0; } /* Address Resolution Logic routines */ @@ -728,13 +733,14 @@ static int bcm_sf2_sw_fdb_prepare(struct dsa_switch *ds, int port, return 0; } -static int bcm_sf2_sw_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) +static void bcm_sf2_sw_fdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans) { struct bcm_sf2_priv *priv = ds_to_priv(ds); - return bcm_sf2_arl_op(priv, 0, port, fdb->addr, fdb->vid, true); + if (bcm_sf2_arl_op(priv, 0, port, fdb->addr, fdb->vid, true)) + pr_err("%s: failed to add MAC address\n", __func__); } static int bcm_sf2_sw_fdb_del(struct dsa_switch *ds, int port, @@ -943,8 +949,8 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds) /* All the interesting properties are at the parent device_node * level */ - dn = ds->pd->of_node->parent; - bcm_sf2_identify_ports(priv, ds->pd->of_node); + dn = ds->cd->of_node->parent; + bcm_sf2_identify_ports(priv, ds->cd->of_node); priv->irq0 = irq_of_parse_and_map(dn, 0); priv->irq1 = irq_of_parse_and_map(dn, 1); @@ -1003,7 +1009,7 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds) /* Enable all valid ports and disable those unused */ for (port = 0; port < priv->hw_params.num_ports; port++) { /* IMP port receives special treatment */ - if ((1 << port) & ds->phys_port_mask) + if ((1 << port) & ds->enabled_port_mask) bcm_sf2_port_setup(ds, port, NULL); else if (dsa_is_cpu_port(ds, port)) bcm_sf2_imp_setup(ds, port); @@ -1016,11 +1022,12 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds) * 7445D0, since 7445E0 disconnects the internal switch pseudo-PHY such * that we can use the regular SWITCH_MDIO master controller instead. * - * By default, DSA initializes ds->phys_mii_mask to ds->phys_port_mask - * to have a 1:1 mapping between Port address and PHY address in order - * to utilize the slave_mii_bus instance to read from Port PHYs. This is - * not what we want here, so we initialize phys_mii_mask 0 to always - * utilize the "master" MDIO bus backed by the "mdio-unimac" driver. + * By default, DSA initializes ds->phys_mii_mask to + * ds->enabled_port_mask to have a 1:1 mapping between Port address + * and PHY address in order to utilize the slave_mii_bus instance to + * read from Port PHYs. This is not what we want here, so we + * initialize phys_mii_mask 0 to always utilize the "master" MDIO + * bus backed by the "mdio-unimac" driver. */ if (of_machine_is_compatible("brcm,bcm7445d0")) ds->phys_mii_mask |= ((1 << BRCM_PSEUDO_PHY_ADDR) | (1 << 0)); @@ -1278,7 +1285,7 @@ static int bcm_sf2_sw_suspend(struct dsa_switch *ds) * bcm_sf2_sw_setup */ for (port = 0; port < DSA_MAX_PORTS; port++) { - if ((1 << port) & ds->phys_port_mask || + if ((1 << port) & ds->enabled_port_mask || dsa_is_cpu_port(ds, port)) bcm_sf2_port_disable(ds, port, NULL); } @@ -1302,7 +1309,7 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds) bcm_sf2_gphy_enable_set(ds, true); for (port = 0; port < DSA_MAX_PORTS; port++) { - if ((1 << port) & ds->phys_port_mask) + if ((1 << port) & ds->enabled_port_mask) bcm_sf2_port_setup(ds, port, NULL); else if (dsa_is_cpu_port(ds, port)) bcm_sf2_imp_setup(ds, port); @@ -1365,8 +1372,7 @@ static int bcm_sf2_sw_set_wol(struct dsa_switch *ds, int port, static struct dsa_switch_driver bcm_sf2_switch_driver = { .tag_protocol = DSA_TAG_PROTO_BRCM, - .priv_size = sizeof(struct bcm_sf2_priv), - .probe = bcm_sf2_sw_probe, + .probe = bcm_sf2_sw_drv_probe, .setup = bcm_sf2_sw_setup, .set_addr = bcm_sf2_sw_set_addr, .get_phy_flags = bcm_sf2_sw_get_phy_flags, @@ -1387,7 +1393,7 @@ static struct dsa_switch_driver bcm_sf2_switch_driver = { .set_eee = bcm_sf2_sw_set_eee, .port_bridge_join = bcm_sf2_sw_br_join, .port_bridge_leave = bcm_sf2_sw_br_leave, - .port_stp_update = bcm_sf2_sw_br_set_stp_state, + .port_stp_state_set = bcm_sf2_sw_br_set_stp_state, .port_fdb_prepare = bcm_sf2_sw_fdb_prepare, .port_fdb_add = bcm_sf2_sw_fdb_add, .port_fdb_del = bcm_sf2_sw_fdb_del, diff --git a/drivers/net/dsa/mv88e6060.c b/drivers/net/dsa/mv88e6060.c index 0527f485c..e36b40886 100644 --- a/drivers/net/dsa/mv88e6060.c +++ b/drivers/net/dsa/mv88e6060.c @@ -19,12 +19,9 @@ static int reg_read(struct dsa_switch *ds, int addr, int reg) { - struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); + struct mv88e6060_priv *priv = ds_to_priv(ds); - if (bus == NULL) - return -EINVAL; - - return mdiobus_read_nested(bus, ds->pd->sw_addr + addr, reg); + return mdiobus_read_nested(priv->bus, priv->sw_addr + addr, reg); } #define REG_READ(addr, reg) \ @@ -40,12 +37,9 @@ static int reg_read(struct dsa_switch *ds, int addr, int reg) static int reg_write(struct dsa_switch *ds, int addr, int reg, u16 val) { - struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); - - if (bus == NULL) - return -EINVAL; + struct mv88e6060_priv *priv = ds_to_priv(ds); - return mdiobus_write_nested(bus, ds->pd->sw_addr + addr, reg, val); + return mdiobus_write_nested(priv->bus, priv->sw_addr + addr, reg, val); } #define REG_WRITE(addr, reg, val) \ @@ -57,14 +51,10 @@ static int reg_write(struct dsa_switch *ds, int addr, int reg, u16 val) return __ret; \ }) -static char *mv88e6060_probe(struct device *host_dev, int sw_addr) +static const char *mv88e6060_get_name(struct mii_bus *bus, int sw_addr) { - struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev); int ret; - if (bus == NULL) - return NULL; - ret = mdiobus_read(bus, sw_addr + REG_PORT(0), PORT_SWITCH_ID); if (ret >= 0) { if (ret == PORT_SWITCH_ID_6060) @@ -79,6 +69,27 @@ static char *mv88e6060_probe(struct device *host_dev, int sw_addr) return NULL; } +static const char *mv88e6060_drv_probe(struct device *dsa_dev, + struct device *host_dev, int sw_addr, + void **_priv) +{ + struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev); + struct mv88e6060_priv *priv; + const char *name; + + name = mv88e6060_get_name(bus, sw_addr); + if (name) { + priv = devm_kzalloc(dsa_dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return NULL; + *_priv = priv; + priv->bus = bus; + priv->sw_addr = sw_addr; + } + + return name; +} + static int mv88e6060_switch_reset(struct dsa_switch *ds) { int i; @@ -159,7 +170,7 @@ static int mv88e6060_setup_port(struct dsa_switch *ds, int p) REG_WRITE(addr, PORT_VLAN_MAP, ((p & 0xf) << PORT_VLAN_MAP_DBNUM_SHIFT) | (dsa_is_cpu_port(ds, p) ? - ds->phys_port_mask : + ds->enabled_port_mask : BIT(ds->dst->cpu_port))); /* Port Association Vector: when learning source addresses @@ -174,8 +185,8 @@ static int mv88e6060_setup_port(struct dsa_switch *ds, int p) static int mv88e6060_setup(struct dsa_switch *ds) { - int i; int ret; + int i; ret = mv88e6060_switch_reset(ds); if (ret < 0) @@ -238,7 +249,7 @@ mv88e6060_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) static struct dsa_switch_driver mv88e6060_switch_driver = { .tag_protocol = DSA_TAG_PROTO_TRAILER, - .probe = mv88e6060_probe, + .probe = mv88e6060_drv_probe, .setup = mv88e6060_setup, .set_addr = mv88e6060_set_addr, .phy_read = mv88e6060_phy_read, diff --git a/drivers/net/dsa/mv88e6060.h b/drivers/net/dsa/mv88e6060.h index cc9b2ed4a..10249bd16 100644 --- a/drivers/net/dsa/mv88e6060.h +++ b/drivers/net/dsa/mv88e6060.h @@ -108,4 +108,15 @@ #define GLOBAL_ATU_MAC_23 0x0e #define GLOBAL_ATU_MAC_45 0x0f +struct mv88e6060_priv { + /* MDIO bus and address on bus to use. When in single chip + * mode, address is 0, and the switch uses multiple addresses + * on the bus. When in multi-chip mode, the switch uses a + * single address which contains two registers used for + * indirect access to more registers. + */ + struct mii_bus *bus; + int sw_addr; +}; + #endif diff --git a/drivers/net/dsa/mv88e6123.c b/drivers/net/dsa/mv88e6123.c deleted file mode 100644 index 69a6f79dc..000000000 --- a/drivers/net/dsa/mv88e6123.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * net/dsa/mv88e6123_61_65.c - Marvell 88e6123/6161/6165 switch chip support - * Copyright (c) 2008-2009 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include "mv88e6xxx.h" - -static const struct mv88e6xxx_switch_id mv88e6123_table[] = { - { PORT_SWITCH_ID_6123, "Marvell 88E6123" }, - { PORT_SWITCH_ID_6123_A1, "Marvell 88E6123 (A1)" }, - { PORT_SWITCH_ID_6123_A2, "Marvell 88E6123 (A2)" }, - { PORT_SWITCH_ID_6161, "Marvell 88E6161" }, - { PORT_SWITCH_ID_6161_A1, "Marvell 88E6161 (A1)" }, - { PORT_SWITCH_ID_6161_A2, "Marvell 88E6161 (A2)" }, - { PORT_SWITCH_ID_6165, "Marvell 88E6165" }, - { PORT_SWITCH_ID_6165_A1, "Marvell 88E6165 (A1)" }, - { PORT_SWITCH_ID_6165_A2, "Marvell 88e6165 (A2)" }, -}; - -static char *mv88e6123_probe(struct device *host_dev, int sw_addr) -{ - return mv88e6xxx_lookup_name(host_dev, sw_addr, mv88e6123_table, - ARRAY_SIZE(mv88e6123_table)); -} - -static int mv88e6123_setup_global(struct dsa_switch *ds) -{ - u32 upstream_port = dsa_upstream_port(ds); - int ret; - u32 reg; - - ret = mv88e6xxx_setup_global(ds); - if (ret) - return ret; - - /* 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. - */ - REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, 0x0000); - - /* Configure the upstream port, and configure the upstream - * port as the port to which ingress and egress monitor frames - * are to be sent. - */ - reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT; - REG_WRITE(REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg); - - /* Disable remote management for now, and set the switch's - * DSA device number. - */ - REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL_2, ds->index & 0x1f); - - return 0; -} - -static int mv88e6123_setup(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - 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 = mv88e6xxx_switch_reset(ds, false); - if (ret < 0) - return ret; - - ret = mv88e6123_setup_global(ds); - if (ret < 0) - return ret; - - return mv88e6xxx_setup_ports(ds); -} - -struct dsa_switch_driver mv88e6123_switch_driver = { - .tag_protocol = DSA_TAG_PROTO_EDSA, - .priv_size = sizeof(struct mv88e6xxx_priv_state), - .probe = mv88e6123_probe, - .setup = mv88e6123_setup, - .set_addr = mv88e6xxx_set_addr_indirect, - .phy_read = mv88e6xxx_phy_read, - .phy_write = mv88e6xxx_phy_write, - .get_strings = mv88e6xxx_get_strings, - .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, - .get_sset_count = mv88e6xxx_get_sset_count, - .adjust_link = mv88e6xxx_adjust_link, -#ifdef CONFIG_NET_DSA_HWMON - .get_temp = mv88e6xxx_get_temp, -#endif - .get_regs_len = mv88e6xxx_get_regs_len, - .get_regs = mv88e6xxx_get_regs, -}; - -MODULE_ALIAS("platform:mv88e6123"); -MODULE_ALIAS("platform:mv88e6161"); -MODULE_ALIAS("platform:mv88e6165"); diff --git a/drivers/net/dsa/mv88e6131.c b/drivers/net/dsa/mv88e6131.c deleted file mode 100644 index a92ca651c..000000000 --- a/drivers/net/dsa/mv88e6131.c +++ /dev/null @@ -1,177 +0,0 @@ -/* - * net/dsa/mv88e6131.c - Marvell 88e6095/6095f/6131 switch chip support - * Copyright (c) 2008-2009 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include "mv88e6xxx.h" - -static const struct mv88e6xxx_switch_id mv88e6131_table[] = { - { PORT_SWITCH_ID_6085, "Marvell 88E6085" }, - { PORT_SWITCH_ID_6095, "Marvell 88E6095/88E6095F" }, - { PORT_SWITCH_ID_6131, "Marvell 88E6131" }, - { PORT_SWITCH_ID_6131_B2, "Marvell 88E6131 (B2)" }, - { PORT_SWITCH_ID_6185, "Marvell 88E6185" }, -}; - -static char *mv88e6131_probe(struct device *host_dev, int sw_addr) -{ - return mv88e6xxx_lookup_name(host_dev, sw_addr, mv88e6131_table, - ARRAY_SIZE(mv88e6131_table)); -} - -static int mv88e6131_setup_global(struct dsa_switch *ds) -{ - u32 upstream_port = dsa_upstream_port(ds); - int ret; - u32 reg; - - ret = mv88e6xxx_setup_global(ds); - if (ret) - return ret; - - /* Enable the PHY polling unit, don't discard packets with - * excessive collisions, use a weighted fair queueing scheme - * to arbitrate between packet queues, set the maximum frame - * size to 1632, and mask all interrupt sources. - */ - REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, - GLOBAL_CONTROL_PPU_ENABLE | GLOBAL_CONTROL_MAX_FRAME_1632); - - /* Set the VLAN ethertype to 0x8100. */ - REG_WRITE(REG_GLOBAL, GLOBAL_CORE_TAG_TYPE, 0x8100); - - /* Disable ARP mirroring, and configure the upstream port as - * the port to which ingress and egress monitor frames are to - * be sent. - */ - reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT | - GLOBAL_MONITOR_CONTROL_ARP_DISABLED; - REG_WRITE(REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg); - - /* Disable cascade port functionality unless this device - * is used in a cascade configuration, and set the switch's - * DSA device number. - */ - if (ds->dst->pd->nr_chips > 1) - REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL_2, - GLOBAL_CONTROL_2_MULTIPLE_CASCADE | - (ds->index & 0x1f)); - else - REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL_2, - GLOBAL_CONTROL_2_NO_CASCADE | - (ds->index & 0x1f)); - - /* Force the priority of IGMP/MLD snoop frames and ARP frames - * to the highest setting. - */ - REG_WRITE(REG_GLOBAL2, GLOBAL2_PRIO_OVERRIDE, - GLOBAL2_PRIO_OVERRIDE_FORCE_SNOOP | - 7 << GLOBAL2_PRIO_OVERRIDE_SNOOP_SHIFT | - GLOBAL2_PRIO_OVERRIDE_FORCE_ARP | - 7 << GLOBAL2_PRIO_OVERRIDE_ARP_SHIFT); - - return 0; -} - -static int mv88e6131_setup(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - ret = mv88e6xxx_setup_common(ds); - if (ret < 0) - return ret; - - mv88e6xxx_ppu_state_init(ds); - - switch (ps->id) { - case PORT_SWITCH_ID_6085: - case PORT_SWITCH_ID_6185: - 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 = mv88e6xxx_switch_reset(ds, false); - if (ret < 0) - return ret; - - ret = mv88e6131_setup_global(ds); - if (ret < 0) - return ret; - - return mv88e6xxx_setup_ports(ds); -} - -static int mv88e6131_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; -} - -static int -mv88e6131_phy_read(struct dsa_switch *ds, int port, int regnum) -{ - int addr = mv88e6131_port_to_phy_addr(ds, port); - - if (addr < 0) - return addr; - - return mv88e6xxx_phy_read_ppu(ds, addr, regnum); -} - -static int -mv88e6131_phy_write(struct dsa_switch *ds, - int port, int regnum, u16 val) -{ - int addr = mv88e6131_port_to_phy_addr(ds, port); - - if (addr < 0) - return addr; - - return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val); -} - -struct dsa_switch_driver mv88e6131_switch_driver = { - .tag_protocol = DSA_TAG_PROTO_DSA, - .priv_size = sizeof(struct mv88e6xxx_priv_state), - .probe = mv88e6131_probe, - .setup = mv88e6131_setup, - .set_addr = mv88e6xxx_set_addr_direct, - .phy_read = mv88e6131_phy_read, - .phy_write = mv88e6131_phy_write, - .get_strings = mv88e6xxx_get_strings, - .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, - .get_sset_count = mv88e6xxx_get_sset_count, - .adjust_link = mv88e6xxx_adjust_link, -}; - -MODULE_ALIAS("platform:mv88e6085"); -MODULE_ALIAS("platform:mv88e6095"); -MODULE_ALIAS("platform:mv88e6095f"); -MODULE_ALIAS("platform:mv88e6131"); diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c deleted file mode 100644 index c0164b98f..000000000 --- a/drivers/net/dsa/mv88e6171.c +++ /dev/null @@ -1,123 +0,0 @@ -/* net/dsa/mv88e6171.c - Marvell 88e6171 switch chip support - * Copyright (c) 2008-2009 Marvell Semiconductor - * Copyright (c) 2014 Claudio Leite - * - * 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 -#include -#include -#include -#include -#include -#include -#include "mv88e6xxx.h" - -static const struct mv88e6xxx_switch_id mv88e6171_table[] = { - { PORT_SWITCH_ID_6171, "Marvell 88E6171" }, - { PORT_SWITCH_ID_6175, "Marvell 88E6175" }, - { PORT_SWITCH_ID_6350, "Marvell 88E6350" }, - { PORT_SWITCH_ID_6351, "Marvell 88E6351" }, -}; - -static char *mv88e6171_probe(struct device *host_dev, int sw_addr) -{ - return mv88e6xxx_lookup_name(host_dev, sw_addr, mv88e6171_table, - ARRAY_SIZE(mv88e6171_table)); -} - -static int mv88e6171_setup_global(struct dsa_switch *ds) -{ - u32 upstream_port = dsa_upstream_port(ds); - int ret; - u32 reg; - - ret = mv88e6xxx_setup_global(ds); - if (ret) - return ret; - - /* Discard packets with excessive collisions, mask all - * interrupt sources, enable PPU. - */ - REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, - GLOBAL_CONTROL_PPU_ENABLE | GLOBAL_CONTROL_DISCARD_EXCESS); - - /* Configure the upstream port, and configure the upstream - * port as the port to which ingress and egress monitor frames - * are to be sent. - */ - reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_MIRROR_SHIFT; - REG_WRITE(REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg); - - /* Disable remote management for now, and set the switch's - * DSA device number. - */ - REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL_2, ds->index & 0x1f); - - return 0; -} - -static int mv88e6171_setup(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - ret = mv88e6xxx_setup_common(ds); - if (ret < 0) - return ret; - - ps->num_ports = 7; - - ret = mv88e6xxx_switch_reset(ds, true); - if (ret < 0) - return ret; - - ret = mv88e6171_setup_global(ds); - if (ret < 0) - return ret; - - return mv88e6xxx_setup_ports(ds); -} - -struct dsa_switch_driver mv88e6171_switch_driver = { - .tag_protocol = DSA_TAG_PROTO_EDSA, - .priv_size = sizeof(struct mv88e6xxx_priv_state), - .probe = mv88e6171_probe, - .setup = mv88e6171_setup, - .set_addr = mv88e6xxx_set_addr_indirect, - .phy_read = mv88e6xxx_phy_read_indirect, - .phy_write = mv88e6xxx_phy_write_indirect, - .get_strings = mv88e6xxx_get_strings, - .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, - .get_sset_count = mv88e6xxx_get_sset_count, - .adjust_link = mv88e6xxx_adjust_link, -#ifdef CONFIG_NET_DSA_HWMON - .get_temp = mv88e6xxx_get_temp, -#endif - .get_regs_len = mv88e6xxx_get_regs_len, - .get_regs = mv88e6xxx_get_regs, - .port_bridge_join = mv88e6xxx_port_bridge_join, - .port_bridge_leave = mv88e6xxx_port_bridge_leave, - .port_stp_update = mv88e6xxx_port_stp_update, - .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, - .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, - .port_vlan_add = mv88e6xxx_port_vlan_add, - .port_vlan_del = mv88e6xxx_port_vlan_del, - .port_vlan_dump = mv88e6xxx_port_vlan_dump, - .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, - .port_fdb_add = mv88e6xxx_port_fdb_add, - .port_fdb_del = mv88e6xxx_port_fdb_del, - .port_fdb_dump = mv88e6xxx_port_fdb_dump, -}; - -MODULE_ALIAS("platform:mv88e6171"); -MODULE_ALIAS("platform:mv88e6175"); -MODULE_ALIAS("platform:mv88e6350"); -MODULE_ALIAS("platform:mv88e6351"); diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c deleted file mode 100644 index 5f528abc8..000000000 --- a/drivers/net/dsa/mv88e6352.c +++ /dev/null @@ -1,345 +0,0 @@ -/* - * net/dsa/mv88e6352.c - Marvell 88e6352 switch chip support - * - * Copyright (c) 2014 Guenter Roeck - * - * Derived from mv88e6123_61_65.c - * Copyright (c) 2008-2009 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "mv88e6xxx.h" - -static const struct mv88e6xxx_switch_id mv88e6352_table[] = { - { PORT_SWITCH_ID_6172, "Marvell 88E6172" }, - { PORT_SWITCH_ID_6176, "Marvell 88E6176" }, - { PORT_SWITCH_ID_6240, "Marvell 88E6240" }, - { PORT_SWITCH_ID_6320, "Marvell 88E6320" }, - { PORT_SWITCH_ID_6320_A1, "Marvell 88E6320 (A1)" }, - { PORT_SWITCH_ID_6320_A2, "Marvell 88e6320 (A2)" }, - { PORT_SWITCH_ID_6321, "Marvell 88E6321" }, - { PORT_SWITCH_ID_6321_A1, "Marvell 88E6321 (A1)" }, - { PORT_SWITCH_ID_6321_A2, "Marvell 88e6321 (A2)" }, - { PORT_SWITCH_ID_6352, "Marvell 88E6352" }, - { PORT_SWITCH_ID_6352_A0, "Marvell 88E6352 (A0)" }, - { PORT_SWITCH_ID_6352_A1, "Marvell 88E6352 (A1)" }, -}; - -static char *mv88e6352_probe(struct device *host_dev, int sw_addr) -{ - return mv88e6xxx_lookup_name(host_dev, sw_addr, mv88e6352_table, - ARRAY_SIZE(mv88e6352_table)); -} - -static int mv88e6352_setup_global(struct dsa_switch *ds) -{ - u32 upstream_port = dsa_upstream_port(ds); - int ret; - u32 reg; - - ret = mv88e6xxx_setup_global(ds); - if (ret) - return ret; - - /* Discard packets with excessive collisions, - * mask all interrupt sources, enable PPU (bit 14, undocumented). - */ - REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, - GLOBAL_CONTROL_PPU_ENABLE | GLOBAL_CONTROL_DISCARD_EXCESS); - - /* Configure the upstream port, and configure the upstream - * port as the port to which ingress and egress monitor frames - * are to be sent. - */ - reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT; - REG_WRITE(REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg); - - /* Disable remote management for now, and set the switch's - * DSA device number. - */ - REG_WRITE(REG_GLOBAL, 0x1c, ds->index & 0x1f); - - return 0; -} - -static int mv88e6352_setup(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - ret = mv88e6xxx_setup_common(ds); - if (ret < 0) - return ret; - - ps->num_ports = 7; - - mutex_init(&ps->eeprom_mutex); - - ret = mv88e6xxx_switch_reset(ds, true); - if (ret < 0) - return ret; - - ret = mv88e6352_setup_global(ds); - if (ret < 0) - return ret; - - return mv88e6xxx_setup_ports(ds); -} - -static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - mutex_lock(&ps->eeprom_mutex); - - ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP, - GLOBAL2_EEPROM_OP_READ | - (addr & GLOBAL2_EEPROM_OP_ADDR_MASK)); - if (ret < 0) - goto error; - - ret = mv88e6xxx_eeprom_busy_wait(ds); - if (ret < 0) - goto error; - - ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, GLOBAL2_EEPROM_DATA); -error: - mutex_unlock(&ps->eeprom_mutex); - return ret; -} - -static int mv88e6352_get_eeprom(struct dsa_switch *ds, - struct ethtool_eeprom *eeprom, u8 *data) -{ - int offset; - int len; - int ret; - - offset = eeprom->offset; - len = eeprom->len; - eeprom->len = 0; - - eeprom->magic = 0xc3ec4951; - - ret = mv88e6xxx_eeprom_load_wait(ds); - if (ret < 0) - return ret; - - if (offset & 1) { - int word; - - word = mv88e6352_read_eeprom_word(ds, offset >> 1); - if (word < 0) - return word; - - *data++ = (word >> 8) & 0xff; - - offset++; - len--; - eeprom->len++; - } - - while (len >= 2) { - int word; - - word = mv88e6352_read_eeprom_word(ds, offset >> 1); - if (word < 0) - return word; - - *data++ = word & 0xff; - *data++ = (word >> 8) & 0xff; - - offset += 2; - len -= 2; - eeprom->len += 2; - } - - if (len) { - int word; - - word = mv88e6352_read_eeprom_word(ds, offset >> 1); - if (word < 0) - return word; - - *data++ = word & 0xff; - - offset++; - len--; - eeprom->len++; - } - - return 0; -} - -static int mv88e6352_eeprom_is_readonly(struct dsa_switch *ds) -{ - int ret; - - ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP); - if (ret < 0) - return ret; - - if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN)) - return -EROFS; - - return 0; -} - -static int mv88e6352_write_eeprom_word(struct dsa_switch *ds, int addr, - u16 data) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - mutex_lock(&ps->eeprom_mutex); - - ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data); - if (ret < 0) - goto error; - - ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP, - GLOBAL2_EEPROM_OP_WRITE | - (addr & GLOBAL2_EEPROM_OP_ADDR_MASK)); - if (ret < 0) - goto error; - - ret = mv88e6xxx_eeprom_busy_wait(ds); -error: - mutex_unlock(&ps->eeprom_mutex); - return ret; -} - -static int mv88e6352_set_eeprom(struct dsa_switch *ds, - struct ethtool_eeprom *eeprom, u8 *data) -{ - int offset; - int ret; - int len; - - if (eeprom->magic != 0xc3ec4951) - return -EINVAL; - - ret = mv88e6352_eeprom_is_readonly(ds); - if (ret) - return ret; - - offset = eeprom->offset; - len = eeprom->len; - eeprom->len = 0; - - ret = mv88e6xxx_eeprom_load_wait(ds); - if (ret < 0) - return ret; - - if (offset & 1) { - int word; - - word = mv88e6352_read_eeprom_word(ds, offset >> 1); - if (word < 0) - return word; - - word = (*data++ << 8) | (word & 0xff); - - ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word); - if (ret < 0) - return ret; - - offset++; - len--; - eeprom->len++; - } - - while (len >= 2) { - int word; - - word = *data++; - word |= *data++ << 8; - - ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word); - if (ret < 0) - return ret; - - offset += 2; - len -= 2; - eeprom->len += 2; - } - - if (len) { - int word; - - word = mv88e6352_read_eeprom_word(ds, offset >> 1); - if (word < 0) - return word; - - word = (word & 0xff00) | *data++; - - ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word); - if (ret < 0) - return ret; - - offset++; - len--; - eeprom->len++; - } - - return 0; -} - -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 = mv88e6xxx_phy_read_indirect, - .phy_write = mv88e6xxx_phy_write_indirect, - .get_strings = mv88e6xxx_get_strings, - .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, - .get_sset_count = mv88e6xxx_get_sset_count, - .adjust_link = mv88e6xxx_adjust_link, - .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 = mv88e6352_get_eeprom, - .set_eeprom = mv88e6352_set_eeprom, - .get_regs_len = mv88e6xxx_get_regs_len, - .get_regs = mv88e6xxx_get_regs, - .port_bridge_join = mv88e6xxx_port_bridge_join, - .port_bridge_leave = mv88e6xxx_port_bridge_leave, - .port_stp_update = mv88e6xxx_port_stp_update, - .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, - .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, - .port_vlan_add = mv88e6xxx_port_vlan_add, - .port_vlan_del = mv88e6xxx_port_vlan_del, - .port_vlan_dump = mv88e6xxx_port_vlan_dump, - .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, - .port_fdb_add = mv88e6xxx_port_fdb_add, - .port_fdb_del = mv88e6xxx_port_fdb_del, - .port_fdb_dump = mv88e6xxx_port_fdb_dump, -}; - -MODULE_ALIAS("platform:mv88e6172"); -MODULE_ALIAS("platform:mv88e6176"); -MODULE_ALIAS("platform:mv88e6320"); -MODULE_ALIAS("platform:mv88e6321"); -MODULE_ALIAS("platform:mv88e6352"); diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 5e572b351..ba9dfc942 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -5,6 +5,8 @@ * Copyright (c) 2015 CMC Electronics, Inc. * Added support for VLAN Table Unit operations * + * Copyright (c) 2016 Andrew Lunn + * * 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 @@ -17,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -25,12 +28,10 @@ #include #include "mv88e6xxx.h" -static void assert_smi_lock(struct dsa_switch *ds) +static void assert_smi_lock(struct mv88e6xxx_priv_state *ps) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - if (unlikely(!mutex_is_locked(&ps->smi_mutex))) { - dev_err(ds->master_dev, "SMI lock not held!\n"); + dev_err(ps->dev, "SMI lock not held!\n"); dump_stack(); } } @@ -92,33 +93,29 @@ static int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, return ret & 0xffff; } -static int _mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg) +static int _mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps, + int addr, int reg) { - struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); int ret; - assert_smi_lock(ds); - - if (bus == NULL) - return -EINVAL; + assert_smi_lock(ps); - ret = __mv88e6xxx_reg_read(bus, ds->pd->sw_addr, addr, reg); + ret = __mv88e6xxx_reg_read(ps->bus, ps->sw_addr, addr, reg); if (ret < 0) return ret; - dev_dbg(ds->master_dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n", + dev_dbg(ps->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n", addr, reg, ret); return ret; } -int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg) +int mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps, 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); + ret = _mv88e6xxx_reg_read(ps, addr, reg); mutex_unlock(&ps->smi_mutex); return ret; @@ -156,58 +153,71 @@ static int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr, return 0; } -static int _mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, - u16 val) +static int _mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr, + int reg, u16 val) { - struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); - - assert_smi_lock(ds); - - if (bus == NULL) - return -EINVAL; + assert_smi_lock(ps); - dev_dbg(ds->master_dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n", + dev_dbg(ps->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); + return __mv88e6xxx_reg_write(ps->bus, ps->sw_addr, addr, reg, val); } -int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val) +int mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, 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(ds, addr, reg, val); + ret = _mv88e6xxx_reg_write(ps, addr, reg, val); mutex_unlock(&ps->smi_mutex); return ret; } -int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr) +static int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr) { - 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]); + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int err; - return 0; + err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_01, + (addr[0] << 8) | addr[1]); + if (err) + return err; + + err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_23, + (addr[2] << 8) | addr[3]); + if (err) + return err; + + return mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_45, + (addr[4] << 8) | addr[5]); } -int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) +static int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) { - int i; + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; + int i; for (i = 0; i < 6; i++) { int j; /* Write the MAC address byte. */ - REG_WRITE(REG_GLOBAL2, GLOBAL2_SWITCH_MAC, - GLOBAL2_SWITCH_MAC_BUSY | (i << 8) | addr[i]); + ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SWITCH_MAC, + GLOBAL2_SWITCH_MAC_BUSY | + (i << 8) | addr[i]); + if (ret) + return ret; /* Wait for the write to complete. */ for (j = 0; j < 16; j++) { - ret = REG_READ(REG_GLOBAL2, GLOBAL2_SWITCH_MAC); + ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, + GLOBAL2_SWITCH_MAC); + if (ret < 0) + return ret; + if ((ret & GLOBAL2_SWITCH_MAC_BUSY) == 0) break; } @@ -218,34 +228,52 @@ int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) return 0; } -static int _mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum) +int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SWITCH_MAC)) + return mv88e6xxx_set_addr_indirect(ds, addr); + else + return mv88e6xxx_set_addr_direct(ds, addr); +} + +static int _mv88e6xxx_phy_read(struct mv88e6xxx_priv_state *ps, int addr, + int regnum) { if (addr >= 0) - return _mv88e6xxx_reg_read(ds, addr, regnum); + return _mv88e6xxx_reg_read(ps, addr, regnum); return 0xffff; } -static int _mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, - u16 val) +static int _mv88e6xxx_phy_write(struct mv88e6xxx_priv_state *ps, int addr, + int regnum, u16 val) { if (addr >= 0) - return _mv88e6xxx_reg_write(ds, addr, regnum, val); + return _mv88e6xxx_reg_write(ps, addr, regnum, val); return 0; } -#ifdef CONFIG_NET_DSA_MV88E6XXX_NEED_PPU -static int mv88e6xxx_ppu_disable(struct dsa_switch *ds) +static int mv88e6xxx_ppu_disable(struct mv88e6xxx_priv_state *ps) { int ret; unsigned long timeout; - ret = REG_READ(REG_GLOBAL, GLOBAL_CONTROL); - REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, - ret & ~GLOBAL_CONTROL_PPU_ENABLE); + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL); + if (ret < 0) + return ret; + + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, + ret & ~GLOBAL_CONTROL_PPU_ENABLE); + if (ret) + return ret; timeout = jiffies + 1 * HZ; while (time_before(jiffies, timeout)) { - ret = REG_READ(REG_GLOBAL, GLOBAL_STATUS); + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS); + if (ret < 0) + return ret; + usleep_range(1000, 2000); if ((ret & GLOBAL_STATUS_PPU_MASK) != GLOBAL_STATUS_PPU_POLLING) @@ -255,17 +283,26 @@ static int mv88e6xxx_ppu_disable(struct dsa_switch *ds) return -ETIMEDOUT; } -static int mv88e6xxx_ppu_enable(struct dsa_switch *ds) +static int mv88e6xxx_ppu_enable(struct mv88e6xxx_priv_state *ps) { - int ret; + int ret, err; unsigned long timeout; - ret = REG_READ(REG_GLOBAL, GLOBAL_CONTROL); - REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, ret | GLOBAL_CONTROL_PPU_ENABLE); + ret = mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL); + if (ret < 0) + return ret; + + err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, + ret | GLOBAL_CONTROL_PPU_ENABLE); + if (err) + return err; timeout = jiffies + 1 * HZ; while (time_before(jiffies, timeout)) { - ret = REG_READ(REG_GLOBAL, GLOBAL_STATUS); + ret = mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS); + if (ret < 0) + return ret; + usleep_range(1000, 2000); if ((ret & GLOBAL_STATUS_PPU_MASK) == GLOBAL_STATUS_PPU_POLLING) @@ -281,9 +318,7 @@ static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly) ps = container_of(ugly, struct mv88e6xxx_priv_state, ppu_work); if (mutex_trylock(&ps->ppu_mutex)) { - struct dsa_switch *ds = ((struct dsa_switch *)ps) - 1; - - if (mv88e6xxx_ppu_enable(ds) == 0) + if (mv88e6xxx_ppu_enable(ps) == 0) ps->ppu_disabled = 0; mutex_unlock(&ps->ppu_mutex); } @@ -296,9 +331,8 @@ static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps) schedule_work(&ps->ppu_work); } -static int mv88e6xxx_ppu_access_get(struct dsa_switch *ds) +static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_priv_state *ps) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; mutex_lock(&ps->ppu_mutex); @@ -309,7 +343,7 @@ static int mv88e6xxx_ppu_access_get(struct dsa_switch *ds) * it. */ if (!ps->ppu_disabled) { - ret = mv88e6xxx_ppu_disable(ds); + ret = mv88e6xxx_ppu_disable(ps); if (ret < 0) { mutex_unlock(&ps->ppu_mutex); return ret; @@ -323,19 +357,15 @@ static int mv88e6xxx_ppu_access_get(struct dsa_switch *ds) return ret; } -static void mv88e6xxx_ppu_access_put(struct dsa_switch *ds) +static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_priv_state *ps) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - /* Schedule a timer to re-enable the PHY polling unit. */ mod_timer(&ps->ppu_timer, jiffies + msecs_to_jiffies(10)); mutex_unlock(&ps->ppu_mutex); } -void mv88e6xxx_ppu_state_init(struct dsa_switch *ds) +void mv88e6xxx_ppu_state_init(struct mv88e6xxx_priv_state *ps) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - mutex_init(&ps->ppu_mutex); INIT_WORK(&ps->ppu_work, mv88e6xxx_ppu_reenable_work); init_timer(&ps->ppu_timer); @@ -343,142 +373,86 @@ void mv88e6xxx_ppu_state_init(struct dsa_switch *ds) ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer; } -int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum) +static int mv88e6xxx_phy_read_ppu(struct mv88e6xxx_priv_state *ps, int addr, + int regnum) { int ret; - ret = mv88e6xxx_ppu_access_get(ds); + ret = mv88e6xxx_ppu_access_get(ps); if (ret >= 0) { - ret = mv88e6xxx_reg_read(ds, addr, regnum); - mv88e6xxx_ppu_access_put(ds); + ret = _mv88e6xxx_reg_read(ps, addr, regnum); + mv88e6xxx_ppu_access_put(ps); } return ret; } -int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr, - int regnum, u16 val) +static int mv88e6xxx_phy_write_ppu(struct mv88e6xxx_priv_state *ps, int addr, + int regnum, u16 val) { int ret; - ret = mv88e6xxx_ppu_access_get(ds); + ret = mv88e6xxx_ppu_access_get(ps); if (ret >= 0) { - ret = mv88e6xxx_reg_write(ds, addr, regnum, val); - mv88e6xxx_ppu_access_put(ds); + ret = _mv88e6xxx_reg_write(ps, addr, regnum, val); + mv88e6xxx_ppu_access_put(ps); } return ret; } -#endif -static bool mv88e6xxx_6065_family(struct dsa_switch *ds) +static bool mv88e6xxx_6065_family(struct mv88e6xxx_priv_state *ps) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - - switch (ps->id) { - case PORT_SWITCH_ID_6031: - case PORT_SWITCH_ID_6061: - case PORT_SWITCH_ID_6035: - case PORT_SWITCH_ID_6065: - return true; - } - return false; + return ps->info->family == MV88E6XXX_FAMILY_6065; } -static bool mv88e6xxx_6095_family(struct dsa_switch *ds) +static bool mv88e6xxx_6095_family(struct mv88e6xxx_priv_state *ps) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - - switch (ps->id) { - case PORT_SWITCH_ID_6092: - case PORT_SWITCH_ID_6095: - return true; - } - return false; + return ps->info->family == MV88E6XXX_FAMILY_6095; } -static bool mv88e6xxx_6097_family(struct dsa_switch *ds) +static bool mv88e6xxx_6097_family(struct mv88e6xxx_priv_state *ps) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - - switch (ps->id) { - case PORT_SWITCH_ID_6046: - case PORT_SWITCH_ID_6085: - case PORT_SWITCH_ID_6096: - case PORT_SWITCH_ID_6097: - return true; - } - return false; + return ps->info->family == MV88E6XXX_FAMILY_6097; } -static bool mv88e6xxx_6165_family(struct dsa_switch *ds) +static bool mv88e6xxx_6165_family(struct mv88e6xxx_priv_state *ps) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - - switch (ps->id) { - case PORT_SWITCH_ID_6123: - case PORT_SWITCH_ID_6161: - case PORT_SWITCH_ID_6165: - return true; - } - return false; + return ps->info->family == MV88E6XXX_FAMILY_6165; } -static bool mv88e6xxx_6185_family(struct dsa_switch *ds) +static bool mv88e6xxx_6185_family(struct mv88e6xxx_priv_state *ps) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - - switch (ps->id) { - case PORT_SWITCH_ID_6121: - case PORT_SWITCH_ID_6122: - case PORT_SWITCH_ID_6152: - case PORT_SWITCH_ID_6155: - case PORT_SWITCH_ID_6182: - case PORT_SWITCH_ID_6185: - case PORT_SWITCH_ID_6108: - case PORT_SWITCH_ID_6131: - return true; - } - return false; + return ps->info->family == MV88E6XXX_FAMILY_6185; } -static bool mv88e6xxx_6320_family(struct dsa_switch *ds) +static bool mv88e6xxx_6320_family(struct mv88e6xxx_priv_state *ps) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - - switch (ps->id) { - case PORT_SWITCH_ID_6320: - case PORT_SWITCH_ID_6321: - return true; - } - return false; + return ps->info->family == MV88E6XXX_FAMILY_6320; } -static bool mv88e6xxx_6351_family(struct dsa_switch *ds) +static bool mv88e6xxx_6351_family(struct mv88e6xxx_priv_state *ps) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + return ps->info->family == MV88E6XXX_FAMILY_6351; +} - switch (ps->id) { - case PORT_SWITCH_ID_6171: - case PORT_SWITCH_ID_6175: - case PORT_SWITCH_ID_6350: - case PORT_SWITCH_ID_6351: - return true; - } - return false; +static bool mv88e6xxx_6352_family(struct mv88e6xxx_priv_state *ps) +{ + return ps->info->family == MV88E6XXX_FAMILY_6352; } -static bool mv88e6xxx_6352_family(struct dsa_switch *ds) +static unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_priv_state *ps) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + return ps->info->num_databases; +} - switch (ps->id) { - case PORT_SWITCH_ID_6172: - case PORT_SWITCH_ID_6176: - case PORT_SWITCH_ID_6240: - case PORT_SWITCH_ID_6352: +static bool mv88e6xxx_has_fid_reg(struct mv88e6xxx_priv_state *ps) +{ + /* Does the device have dedicated FID registers for ATU and VTU ops? */ + if (mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) || + mv88e6xxx_6351_family(ps) || mv88e6xxx_6352_family(ps)) return true; - } + return false; } @@ -486,8 +460,8 @@ static bool mv88e6xxx_6352_family(struct dsa_switch *ds) * phy. However, in the case of a fixed link phy, we force the port * settings from the fixed link settings. */ -void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, - struct phy_device *phydev) +static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, + struct phy_device *phydev) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); u32 reg; @@ -498,7 +472,7 @@ void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_PCS_CTRL); + ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_PCS_CTRL); if (ret < 0) goto out; @@ -512,7 +486,7 @@ void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, if (phydev->link) reg |= PORT_PCS_CTRL_LINK_UP; - if (mv88e6xxx_6065_family(ds) && phydev->speed > SPEED_100) + if (mv88e6xxx_6065_family(ps) && phydev->speed > SPEED_100) goto out; switch (phydev->speed) { @@ -534,8 +508,8 @@ void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, if (phydev->duplex == DUPLEX_FULL) reg |= PORT_PCS_CTRL_DUPLEX_FULL; - if ((mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds)) && - (port >= ps->num_ports - 2)) { + if ((mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps)) && + (port >= ps->info->num_ports - 2)) { if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK; if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) @@ -544,19 +518,19 @@ void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK | PORT_PCS_CTRL_RGMII_DELAY_TXCLK); } - _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_PCS_CTRL, reg); + _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_PCS_CTRL, reg); out: mutex_unlock(&ps->smi_mutex); } -static int _mv88e6xxx_stats_wait(struct dsa_switch *ds) +static int _mv88e6xxx_stats_wait(struct mv88e6xxx_priv_state *ps) { int ret; int i; for (i = 0; i < 10; i++) { - ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_OP); + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_OP); if ((ret & GLOBAL_STATS_OP_BUSY) == 0) return 0; } @@ -564,52 +538,54 @@ static int _mv88e6xxx_stats_wait(struct dsa_switch *ds) return -ETIMEDOUT; } -static int _mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port) +static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_priv_state *ps, + int port) { int ret; - if (mv88e6xxx_6320_family(ds) || mv88e6xxx_6352_family(ds)) + if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps)) port = (port + 1) << 5; /* Snapshot the hardware statistics counters for this port. */ - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP, + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP, GLOBAL_STATS_OP_CAPTURE_PORT | GLOBAL_STATS_OP_HIST_RX_TX | port); if (ret < 0) return ret; /* Wait for the snapshotting to complete. */ - ret = _mv88e6xxx_stats_wait(ds); + ret = _mv88e6xxx_stats_wait(ps); if (ret < 0) return ret; return 0; } -static void _mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val) +static void _mv88e6xxx_stats_read(struct mv88e6xxx_priv_state *ps, + int stat, u32 *val) { u32 _val; int ret; *val = 0; - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP, + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP, GLOBAL_STATS_OP_READ_CAPTURED | GLOBAL_STATS_OP_HIST_RX_TX | stat); if (ret < 0) return; - ret = _mv88e6xxx_stats_wait(ds); + ret = _mv88e6xxx_stats_wait(ps); if (ret < 0) return; - ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_32); + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_COUNTER_32); if (ret < 0) return; _val = ret << 16; - ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_01); + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_COUNTER_01); if (ret < 0) return; @@ -678,26 +654,26 @@ static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = { { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, }, }; -static bool mv88e6xxx_has_stat(struct dsa_switch *ds, +static bool mv88e6xxx_has_stat(struct mv88e6xxx_priv_state *ps, struct mv88e6xxx_hw_stat *stat) { switch (stat->type) { case BANK0: return true; case BANK1: - return mv88e6xxx_6320_family(ds); + return mv88e6xxx_6320_family(ps); case PORT: - return mv88e6xxx_6095_family(ds) || - mv88e6xxx_6185_family(ds) || - mv88e6xxx_6097_family(ds) || - mv88e6xxx_6165_family(ds) || - mv88e6xxx_6351_family(ds) || - mv88e6xxx_6352_family(ds); + return mv88e6xxx_6095_family(ps) || + mv88e6xxx_6185_family(ps) || + mv88e6xxx_6097_family(ps) || + mv88e6xxx_6165_family(ps) || + mv88e6xxx_6351_family(ps) || + mv88e6xxx_6352_family(ps); } return false; } -static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds, +static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_priv_state *ps, struct mv88e6xxx_hw_stat *s, int port) { @@ -708,13 +684,13 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds, switch (s->type) { case PORT: - ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), s->reg); + ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), s->reg); if (ret < 0) return UINT64_MAX; low = ret; if (s->sizeof_stat == 4) { - ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), + ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), s->reg + 1); if (ret < 0) return UINT64_MAX; @@ -723,22 +699,24 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds, break; case BANK0: case BANK1: - _mv88e6xxx_stats_read(ds, s->reg, &low); + _mv88e6xxx_stats_read(ps, s->reg, &low); if (s->sizeof_stat == 8) - _mv88e6xxx_stats_read(ds, s->reg + 1, &high); + _mv88e6xxx_stats_read(ps, s->reg + 1, &high); } value = (((u64)high) << 16) | low; return value; } -void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data) +static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, + uint8_t *data) { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_hw_stat *stat; int i, j; for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { stat = &mv88e6xxx_hw_stats[i]; - if (mv88e6xxx_has_stat(ds, stat)) { + if (mv88e6xxx_has_stat(ps, stat)) { memcpy(data + j * ETH_GSTRING_LEN, stat->string, ETH_GSTRING_LEN); j++; @@ -746,22 +724,22 @@ void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data) } } -int mv88e6xxx_get_sset_count(struct dsa_switch *ds) +static int mv88e6xxx_get_sset_count(struct dsa_switch *ds) { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_hw_stat *stat; int i, j; for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { stat = &mv88e6xxx_hw_stats[i]; - if (mv88e6xxx_has_stat(ds, stat)) + if (mv88e6xxx_has_stat(ps, stat)) j++; } return j; } -void -mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, - int port, uint64_t *data) +static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, + uint64_t *data) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_hw_stat *stat; @@ -770,15 +748,15 @@ mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_stats_snapshot(ds, port); + ret = _mv88e6xxx_stats_snapshot(ps, port); if (ret < 0) { mutex_unlock(&ps->smi_mutex); return; } for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { stat = &mv88e6xxx_hw_stats[i]; - if (mv88e6xxx_has_stat(ds, stat)) { - data[j] = _mv88e6xxx_get_ethtool_stat(ds, stat, port); + if (mv88e6xxx_has_stat(ps, stat)) { + data[j] = _mv88e6xxx_get_ethtool_stat(ps, stat, port); j++; } } @@ -786,14 +764,15 @@ mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, mutex_unlock(&ps->smi_mutex); } -int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port) +static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port) { return 32 * sizeof(u16); } -void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, - struct ethtool_regs *regs, void *_p) +static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, + struct ethtool_regs *regs, void *_p) { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); u16 *p = _p; int i; @@ -801,16 +780,20 @@ void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, memset(p, 0xff, 32 * sizeof(u16)); + mutex_lock(&ps->smi_mutex); + for (i = 0; i < 32; i++) { int ret; - ret = mv88e6xxx_reg_read(ds, REG_PORT(port), i); + ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), i); if (ret >= 0) p[i] = ret; } + + mutex_unlock(&ps->smi_mutex); } -static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, +static int _mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg, int offset, u16 mask) { unsigned long timeout = jiffies + HZ / 10; @@ -818,7 +801,7 @@ static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, while (time_before(jiffies, timeout)) { int ret; - ret = _mv88e6xxx_reg_read(ds, reg, offset); + ret = _mv88e6xxx_reg_read(ps, reg, offset); if (ret < 0) return ret; if (!(ret & mask)) @@ -829,91 +812,320 @@ static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, return -ETIMEDOUT; } -static int mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask) +static int mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg, + int offset, u16 mask) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_wait(ds, reg, offset, mask); + ret = _mv88e6xxx_wait(ps, reg, offset, mask); mutex_unlock(&ps->smi_mutex); return ret; } -static int _mv88e6xxx_phy_wait(struct dsa_switch *ds) +static int _mv88e6xxx_phy_wait(struct mv88e6xxx_priv_state *ps) { - return _mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_SMI_OP, + return _mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_SMI_OP, GLOBAL2_SMI_OP_BUSY); } -int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds) +static int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds) { - return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP, + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + return mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP, GLOBAL2_EEPROM_OP_LOAD); } -int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds) +static int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds) { - return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP, + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + return mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP, GLOBAL2_EEPROM_OP_BUSY); } -static int _mv88e6xxx_atu_wait(struct dsa_switch *ds) +static int mv88e6xxx_read_eeprom_word(struct dsa_switch *ds, int addr) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret; + + mutex_lock(&ps->eeprom_mutex); + + ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP, + GLOBAL2_EEPROM_OP_READ | + (addr & GLOBAL2_EEPROM_OP_ADDR_MASK)); + if (ret < 0) + goto error; + + ret = mv88e6xxx_eeprom_busy_wait(ds); + if (ret < 0) + goto error; + + ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA); +error: + mutex_unlock(&ps->eeprom_mutex); + return ret; +} + +static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM)) + return ps->eeprom_len; + + return 0; +} + +static int mv88e6xxx_get_eeprom(struct dsa_switch *ds, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int offset; + int len; + int ret; + + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM)) + return -EOPNOTSUPP; + + offset = eeprom->offset; + len = eeprom->len; + eeprom->len = 0; + + eeprom->magic = 0xc3ec4951; + + ret = mv88e6xxx_eeprom_load_wait(ds); + if (ret < 0) + return ret; + + if (offset & 1) { + int word; + + word = mv88e6xxx_read_eeprom_word(ds, offset >> 1); + if (word < 0) + return word; + + *data++ = (word >> 8) & 0xff; + + offset++; + len--; + eeprom->len++; + } + + while (len >= 2) { + int word; + + word = mv88e6xxx_read_eeprom_word(ds, offset >> 1); + if (word < 0) + return word; + + *data++ = word & 0xff; + *data++ = (word >> 8) & 0xff; + + offset += 2; + len -= 2; + eeprom->len += 2; + } + + if (len) { + int word; + + word = mv88e6xxx_read_eeprom_word(ds, offset >> 1); + if (word < 0) + return word; + + *data++ = word & 0xff; + + offset++; + len--; + eeprom->len++; + } + + return 0; +} + +static int mv88e6xxx_eeprom_is_readonly(struct dsa_switch *ds) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret; + + ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP); + if (ret < 0) + return ret; + + if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN)) + return -EROFS; + + return 0; +} + +static int mv88e6xxx_write_eeprom_word(struct dsa_switch *ds, int addr, + u16 data) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret; + + mutex_lock(&ps->eeprom_mutex); + + ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data); + if (ret < 0) + goto error; + + ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP, + GLOBAL2_EEPROM_OP_WRITE | + (addr & GLOBAL2_EEPROM_OP_ADDR_MASK)); + if (ret < 0) + goto error; + + ret = mv88e6xxx_eeprom_busy_wait(ds); +error: + mutex_unlock(&ps->eeprom_mutex); + return ret; +} + +static int mv88e6xxx_set_eeprom(struct dsa_switch *ds, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int offset; + int ret; + int len; + + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM)) + return -EOPNOTSUPP; + + if (eeprom->magic != 0xc3ec4951) + return -EINVAL; + + ret = mv88e6xxx_eeprom_is_readonly(ds); + if (ret) + return ret; + + offset = eeprom->offset; + len = eeprom->len; + eeprom->len = 0; + + ret = mv88e6xxx_eeprom_load_wait(ds); + if (ret < 0) + return ret; + + if (offset & 1) { + int word; + + word = mv88e6xxx_read_eeprom_word(ds, offset >> 1); + if (word < 0) + return word; + + word = (*data++ << 8) | (word & 0xff); + + ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word); + if (ret < 0) + return ret; + + offset++; + len--; + eeprom->len++; + } + + while (len >= 2) { + int word; + + word = *data++; + word |= *data++ << 8; + + ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word); + if (ret < 0) + return ret; + + offset += 2; + len -= 2; + eeprom->len += 2; + } + + if (len) { + int word; + + word = mv88e6xxx_read_eeprom_word(ds, offset >> 1); + if (word < 0) + return word; + + word = (word & 0xff00) | *data++; + + ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word); + if (ret < 0) + return ret; + + offset++; + len--; + eeprom->len++; + } + + return 0; +} + +static int _mv88e6xxx_atu_wait(struct mv88e6xxx_priv_state *ps) { - return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_ATU_OP, + return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_ATU_OP, GLOBAL_ATU_OP_BUSY); } -static int _mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr, - int regnum) +static int _mv88e6xxx_phy_read_indirect(struct mv88e6xxx_priv_state *ps, + int addr, int regnum) { int ret; - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SMI_OP, + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_OP, GLOBAL2_SMI_OP_22_READ | (addr << 5) | regnum); if (ret < 0) return ret; - ret = _mv88e6xxx_phy_wait(ds); + ret = _mv88e6xxx_phy_wait(ps); if (ret < 0) return ret; - return _mv88e6xxx_reg_read(ds, REG_GLOBAL2, GLOBAL2_SMI_DATA); + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_SMI_DATA); + + return ret; } -static int _mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, - int regnum, u16 val) +static int _mv88e6xxx_phy_write_indirect(struct mv88e6xxx_priv_state *ps, + int addr, int regnum, u16 val) { int ret; - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SMI_DATA, val); + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_DATA, val); if (ret < 0) return ret; - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SMI_OP, + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_OP, GLOBAL2_SMI_OP_22_WRITE | (addr << 5) | regnum); - return _mv88e6xxx_phy_wait(ds); + return _mv88e6xxx_phy_wait(ps); } -int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) +static 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; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEE)) + return -EOPNOTSUPP; + mutex_lock(&ps->smi_mutex); - reg = _mv88e6xxx_phy_read_indirect(ds, port, 16); + reg = _mv88e6xxx_phy_read_indirect(ps, 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); + reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS); if (reg < 0) goto out; @@ -925,16 +1137,19 @@ out: return reg; } -int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, - struct phy_device *phydev, struct ethtool_eee *e) +static 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; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEE)) + return -EOPNOTSUPP; + mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_phy_read_indirect(ds, port, 16); + ret = _mv88e6xxx_phy_read_indirect(ps, port, 16); if (ret < 0) goto out; @@ -944,25 +1159,45 @@ int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, if (e->tx_lpi_enabled) reg |= 0x0100; - ret = _mv88e6xxx_phy_write_indirect(ds, port, 16, reg); + ret = _mv88e6xxx_phy_write_indirect(ps, port, 16, reg); out: mutex_unlock(&ps->smi_mutex); return ret; } -static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, u16 cmd) +static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_priv_state *ps, u16 fid, u16 cmd) { int ret; - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_OP, cmd); + if (mv88e6xxx_has_fid_reg(ps)) { + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_FID, fid); + if (ret < 0) + return ret; + } else if (mv88e6xxx_num_databases(ps) == 256) { + /* ATU DBNum[7:4] are located in ATU Control 15:12 */ + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL); + if (ret < 0) + return ret; + + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL, + (ret & 0xfff) | + ((fid << 8) & 0xf000)); + if (ret < 0) + return ret; + + /* ATU DBNum[3:0] are located in ATU Operation 3:0 */ + cmd |= fid & 0xf; + } + + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_OP, cmd); if (ret < 0) return ret; - return _mv88e6xxx_atu_wait(ds); + return _mv88e6xxx_atu_wait(ps); } -static int _mv88e6xxx_atu_data_write(struct dsa_switch *ds, +static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_priv_state *ps, struct mv88e6xxx_atu_entry *entry) { u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK; @@ -982,30 +1217,25 @@ static int _mv88e6xxx_atu_data_write(struct dsa_switch *ds, data |= (entry->portv_trunkid << shift) & mask; } - return _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_DATA, data); + return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_DATA, data); } -static int _mv88e6xxx_atu_flush_move(struct dsa_switch *ds, +static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_priv_state *ps, struct mv88e6xxx_atu_entry *entry, bool static_too) { int op; int err; - err = _mv88e6xxx_atu_wait(ds); + err = _mv88e6xxx_atu_wait(ps); if (err) return err; - err = _mv88e6xxx_atu_data_write(ds, entry); + err = _mv88e6xxx_atu_data_write(ps, entry); if (err) return err; if (entry->fid) { - err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, - entry->fid); - if (err) - return err; - op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB : GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB; } else { @@ -1013,21 +1243,22 @@ static int _mv88e6xxx_atu_flush_move(struct dsa_switch *ds, GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC; } - return _mv88e6xxx_atu_cmd(ds, op); + return _mv88e6xxx_atu_cmd(ps, entry->fid, op); } -static int _mv88e6xxx_atu_flush(struct dsa_switch *ds, u16 fid, bool static_too) +static int _mv88e6xxx_atu_flush(struct mv88e6xxx_priv_state *ps, + u16 fid, bool static_too) { struct mv88e6xxx_atu_entry entry = { .fid = fid, .state = 0, /* EntryState bits must be 0 */ }; - return _mv88e6xxx_atu_flush_move(ds, &entry, static_too); + return _mv88e6xxx_atu_flush_move(ps, &entry, static_too); } -static int _mv88e6xxx_atu_move(struct dsa_switch *ds, u16 fid, int from_port, - int to_port, bool static_too) +static int _mv88e6xxx_atu_move(struct mv88e6xxx_priv_state *ps, u16 fid, + int from_port, int to_port, bool static_too) { struct mv88e6xxx_atu_entry entry = { .trunk = false, @@ -1041,14 +1272,14 @@ static int _mv88e6xxx_atu_move(struct dsa_switch *ds, u16 fid, int from_port, entry.portv_trunkid = (to_port & 0x0f) << 4; entry.portv_trunkid |= from_port & 0x0f; - return _mv88e6xxx_atu_flush_move(ds, &entry, static_too); + return _mv88e6xxx_atu_flush_move(ps, &entry, static_too); } -static int _mv88e6xxx_atu_remove(struct dsa_switch *ds, u16 fid, int port, - bool static_too) +static int _mv88e6xxx_atu_remove(struct mv88e6xxx_priv_state *ps, u16 fid, + int port, bool static_too) { /* Destination port 0xF means remove the entries */ - return _mv88e6xxx_atu_move(ds, fid, port, 0x0f, static_too); + return _mv88e6xxx_atu_move(ps, fid, port, 0x0f, static_too); } static const char * const mv88e6xxx_port_state_names[] = { @@ -1058,12 +1289,14 @@ static const char * const mv88e6xxx_port_state_names[] = { [PORT_CONTROL_STATE_FORWARDING] = "Forwarding", }; -static int _mv88e6xxx_port_state(struct dsa_switch *ds, int port, u8 state) +static int _mv88e6xxx_port_state(struct mv88e6xxx_priv_state *ps, int port, + u8 state) { + struct dsa_switch *ds = ps->ds; int reg, ret = 0; u8 oldstate; - reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL); + reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL); if (reg < 0) return reg; @@ -1078,13 +1311,13 @@ static int _mv88e6xxx_port_state(struct dsa_switch *ds, int port, u8 state) oldstate == PORT_CONTROL_STATE_FORWARDING) && (state == PORT_CONTROL_STATE_DISABLED || state == PORT_CONTROL_STATE_BLOCKING)) { - ret = _mv88e6xxx_atu_remove(ds, 0, port, false); + ret = _mv88e6xxx_atu_remove(ps, 0, port, false); if (ret) return ret; } reg = (reg & ~PORT_CONTROL_STATE_MASK) | state; - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL, + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL, reg); if (ret) return ret; @@ -1097,11 +1330,12 @@ static int _mv88e6xxx_port_state(struct dsa_switch *ds, int port, u8 state) return ret; } -static int _mv88e6xxx_port_based_vlan_map(struct dsa_switch *ds, int port) +static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_priv_state *ps, + int port) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct net_device *bridge = ps->ports[port].bridge_dev; - const u16 mask = (1 << ps->num_ports) - 1; + const u16 mask = (1 << ps->info->num_ports) - 1; + struct dsa_switch *ds = ps->ds; u16 output_ports = 0; int reg; int i; @@ -1110,7 +1344,7 @@ static int _mv88e6xxx_port_based_vlan_map(struct dsa_switch *ds, int port) if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { output_ports = mask; } else { - for (i = 0; i < ps->num_ports; ++i) { + for (i = 0; i < ps->info->num_ports; ++i) { /* allow sending frames to every group member */ if (bridge && ps->ports[i].bridge_dev == bridge) output_ports |= BIT(i); @@ -1124,20 +1358,25 @@ static int _mv88e6xxx_port_based_vlan_map(struct dsa_switch *ds, int port) /* prevent frames from going back out of the port they came in on */ output_ports &= ~BIT(port); - reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_BASE_VLAN); + reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_BASE_VLAN); if (reg < 0) return reg; reg &= ~mask; reg |= output_ports & mask; - return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, reg); + return _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN, reg); } -int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state) +static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, + u8 state) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int stp_state; + int err; + + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_PORTSTATE)) + return; switch (state) { case BR_STATE_DISABLED: @@ -1156,23 +1395,23 @@ int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state) break; } - /* 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->ports[port].state = stp_state; - set_bit(port, ps->port_state_update_mask); - schedule_work(&ps->bridge_work); + mutex_lock(&ps->smi_mutex); + err = _mv88e6xxx_port_state(ps, port, stp_state); + mutex_unlock(&ps->smi_mutex); - return 0; + if (err) + netdev_err(ds->ports[port], "failed to update state to %s\n", + mv88e6xxx_port_state_names[stp_state]); } -static int _mv88e6xxx_port_pvid(struct dsa_switch *ds, int port, u16 *new, - u16 *old) +static int _mv88e6xxx_port_pvid(struct mv88e6xxx_priv_state *ps, int port, + u16 *new, u16 *old) { + struct dsa_switch *ds = ps->ds; u16 pvid; int ret; - ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_DEFAULT_VLAN); + ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_DEFAULT_VLAN); if (ret < 0) return ret; @@ -1182,7 +1421,7 @@ static int _mv88e6xxx_port_pvid(struct dsa_switch *ds, int port, u16 *new, ret &= ~PORT_DEFAULT_VLAN_MASK; ret |= *new & PORT_DEFAULT_VLAN_MASK; - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_DEFAULT_VLAN, ret); if (ret < 0) return ret; @@ -1197,55 +1436,56 @@ static int _mv88e6xxx_port_pvid(struct dsa_switch *ds, int port, u16 *new, return 0; } -static int _mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid) +static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_priv_state *ps, + int port, u16 *pvid) { - return _mv88e6xxx_port_pvid(ds, port, NULL, pvid); + return _mv88e6xxx_port_pvid(ps, port, NULL, pvid); } -static int _mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 pvid) +static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_priv_state *ps, + int port, u16 pvid) { - return _mv88e6xxx_port_pvid(ds, port, &pvid, NULL); + return _mv88e6xxx_port_pvid(ps, port, &pvid, NULL); } -static int _mv88e6xxx_vtu_wait(struct dsa_switch *ds) +static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_priv_state *ps) { - return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_VTU_OP, + return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY); } -static int _mv88e6xxx_vtu_cmd(struct dsa_switch *ds, u16 op) +static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_priv_state *ps, u16 op) { int ret; - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_OP, op); + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_OP, op); if (ret < 0) return ret; - return _mv88e6xxx_vtu_wait(ds); + return _mv88e6xxx_vtu_wait(ps); } -static int _mv88e6xxx_vtu_stu_flush(struct dsa_switch *ds) +static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_priv_state *ps) { int ret; - ret = _mv88e6xxx_vtu_wait(ds); + ret = _mv88e6xxx_vtu_wait(ps); if (ret < 0) return ret; - return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_FLUSH_ALL); + return _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_FLUSH_ALL); } -static int _mv88e6xxx_vtu_stu_data_read(struct dsa_switch *ds, +static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_priv_state *ps, struct mv88e6xxx_vtu_stu_entry *entry, unsigned int nibble_offset) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); u16 regs[3]; int i; int ret; for (i = 0; i < 3; ++i) { - ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_DATA_0_3 + i); if (ret < 0) return ret; @@ -1253,7 +1493,7 @@ static int _mv88e6xxx_vtu_stu_data_read(struct dsa_switch *ds, regs[i] = ret; } - for (i = 0; i < ps->num_ports; ++i) { + for (i = 0; i < ps->info->num_ports; ++i) { unsigned int shift = (i % 4) * 4 + nibble_offset; u16 reg = regs[i / 4]; @@ -1263,16 +1503,27 @@ static int _mv88e6xxx_vtu_stu_data_read(struct dsa_switch *ds, return 0; } -static int _mv88e6xxx_vtu_stu_data_write(struct dsa_switch *ds, +static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_priv_state *ps, + struct mv88e6xxx_vtu_stu_entry *entry) +{ + return _mv88e6xxx_vtu_stu_data_read(ps, entry, 0); +} + +static int mv88e6xxx_stu_data_read(struct mv88e6xxx_priv_state *ps, + struct mv88e6xxx_vtu_stu_entry *entry) +{ + return _mv88e6xxx_vtu_stu_data_read(ps, entry, 2); +} + +static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_priv_state *ps, struct mv88e6xxx_vtu_stu_entry *entry, unsigned int nibble_offset) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); u16 regs[3] = { 0 }; int i; int ret; - for (i = 0; i < ps->num_ports; ++i) { + for (i = 0; i < ps->info->num_ports; ++i) { unsigned int shift = (i % 4) * 4 + nibble_offset; u8 data = entry->data[i]; @@ -1280,7 +1531,7 @@ static int _mv88e6xxx_vtu_stu_data_write(struct dsa_switch *ds, } for (i = 0; i < 3; ++i) { - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_DATA_0_3 + i, regs[i]); if (ret < 0) return ret; @@ -1289,27 +1540,39 @@ static int _mv88e6xxx_vtu_stu_data_write(struct dsa_switch *ds, return 0; } -static int _mv88e6xxx_vtu_vid_write(struct dsa_switch *ds, u16 vid) +static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_priv_state *ps, + struct mv88e6xxx_vtu_stu_entry *entry) +{ + return _mv88e6xxx_vtu_stu_data_write(ps, entry, 0); +} + +static int mv88e6xxx_stu_data_write(struct mv88e6xxx_priv_state *ps, + struct mv88e6xxx_vtu_stu_entry *entry) +{ + return _mv88e6xxx_vtu_stu_data_write(ps, entry, 2); +} + +static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_priv_state *ps, u16 vid) { - return _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID, + return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, vid & GLOBAL_VTU_VID_MASK); } -static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds, +static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_priv_state *ps, struct mv88e6xxx_vtu_stu_entry *entry) { struct mv88e6xxx_vtu_stu_entry next = { 0 }; int ret; - ret = _mv88e6xxx_vtu_wait(ds); + ret = _mv88e6xxx_vtu_wait(ps); if (ret < 0) return ret; - ret = _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_VTU_GET_NEXT); + ret = _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_VTU_GET_NEXT); if (ret < 0) return ret; - ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_VID); + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_VID); if (ret < 0) return ret; @@ -1317,20 +1580,32 @@ static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds, next.valid = !!(ret & GLOBAL_VTU_VID_VALID); if (next.valid) { - ret = _mv88e6xxx_vtu_stu_data_read(ds, &next, 0); + ret = mv88e6xxx_vtu_data_read(ps, &next); if (ret < 0) return ret; - if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) || - mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) { - ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, + if (mv88e6xxx_has_fid_reg(ps)) { + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_FID); if (ret < 0) return ret; next.fid = ret & GLOBAL_VTU_FID_MASK; + } else if (mv88e6xxx_num_databases(ps) == 256) { + /* VTU DBNum[7:4] are located in VTU Operation 11:8, and + * VTU DBNum[3:0] are located in VTU Operation 3:0 + */ + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, + GLOBAL_VTU_OP); + if (ret < 0) + return ret; - ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, + next.fid = (ret & 0xf00) >> 4; + next.fid |= ret & 0xf; + } + + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_STU)) { + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_SID); if (ret < 0) return ret; @@ -1343,27 +1618,30 @@ static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds, return 0; } -int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_vlan *vlan, - int (*cb)(struct switchdev_obj *obj)) +static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, + struct switchdev_obj_port_vlan *vlan, + int (*cb)(struct switchdev_obj *obj)) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_vtu_stu_entry next; u16 pvid; int err; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU)) + return -EOPNOTSUPP; + mutex_lock(&ps->smi_mutex); - err = _mv88e6xxx_port_pvid_get(ds, port, &pvid); + err = _mv88e6xxx_port_pvid_get(ps, port, &pvid); if (err) goto unlock; - err = _mv88e6xxx_vtu_vid_write(ds, GLOBAL_VTU_VID_MASK); + err = _mv88e6xxx_vtu_vid_write(ps, GLOBAL_VTU_VID_MASK); if (err) goto unlock; do { - err = _mv88e6xxx_vtu_getnext(ds, &next); + err = _mv88e6xxx_vtu_getnext(ps, &next); if (err) break; @@ -1394,13 +1672,14 @@ unlock: return err; } -static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds, +static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_priv_state *ps, struct mv88e6xxx_vtu_stu_entry *entry) { + u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE; u16 reg = 0; int ret; - ret = _mv88e6xxx_vtu_wait(ds); + ret = _mv88e6xxx_vtu_wait(ps); if (ret < 0) return ret; @@ -1408,66 +1687,73 @@ static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds, goto loadpurge; /* Write port member tags */ - ret = _mv88e6xxx_vtu_stu_data_write(ds, entry, 0); + ret = mv88e6xxx_vtu_data_write(ps, entry); if (ret < 0) return ret; - if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) || - mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) { + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_STU)) { reg = entry->sid & GLOBAL_VTU_SID_MASK; - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg); + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg); if (ret < 0) return ret; + } + if (mv88e6xxx_has_fid_reg(ps)) { reg = entry->fid & GLOBAL_VTU_FID_MASK; - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_FID, reg); + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_FID, reg); if (ret < 0) return ret; + } else if (mv88e6xxx_num_databases(ps) == 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; - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID, reg); + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, reg); if (ret < 0) return ret; - return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_VTU_LOAD_PURGE); + return _mv88e6xxx_vtu_cmd(ps, op); } -static int _mv88e6xxx_stu_getnext(struct dsa_switch *ds, u8 sid, +static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_priv_state *ps, u8 sid, struct mv88e6xxx_vtu_stu_entry *entry) { struct mv88e6xxx_vtu_stu_entry next = { 0 }; int ret; - ret = _mv88e6xxx_vtu_wait(ds); + ret = _mv88e6xxx_vtu_wait(ps); if (ret < 0) return ret; - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, sid & GLOBAL_VTU_SID_MASK); if (ret < 0) return ret; - ret = _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_STU_GET_NEXT); + ret = _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_STU_GET_NEXT); if (ret < 0) return ret; - ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_SID); + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_SID); if (ret < 0) return ret; next.sid = ret & GLOBAL_VTU_SID_MASK; - ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_VID); + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_VID); if (ret < 0) return ret; next.valid = !!(ret & GLOBAL_VTU_VID_VALID); if (next.valid) { - ret = _mv88e6xxx_vtu_stu_data_read(ds, &next, 2); + ret = mv88e6xxx_stu_data_read(ps, &next); if (ret < 0) return ret; } @@ -1476,13 +1762,13 @@ static int _mv88e6xxx_stu_getnext(struct dsa_switch *ds, u8 sid, return 0; } -static int _mv88e6xxx_stu_loadpurge(struct dsa_switch *ds, +static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_priv_state *ps, struct mv88e6xxx_vtu_stu_entry *entry) { u16 reg = 0; int ret; - ret = _mv88e6xxx_vtu_wait(ds); + ret = _mv88e6xxx_vtu_wait(ps); if (ret < 0) return ret; @@ -1490,32 +1776,41 @@ static int _mv88e6xxx_stu_loadpurge(struct dsa_switch *ds, goto loadpurge; /* Write port states */ - ret = _mv88e6xxx_vtu_stu_data_write(ds, entry, 2); + ret = mv88e6xxx_stu_data_write(ps, entry); if (ret < 0) return ret; reg = GLOBAL_VTU_VID_VALID; loadpurge: - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID, reg); + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, reg); if (ret < 0) return ret; reg = entry->sid & GLOBAL_VTU_SID_MASK; - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg); + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg); if (ret < 0) return ret; - return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_STU_LOAD_PURGE); + return _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_STU_LOAD_PURGE); } -static int _mv88e6xxx_port_fid(struct dsa_switch *ds, int port, u16 *new, - u16 *old) +static int _mv88e6xxx_port_fid(struct mv88e6xxx_priv_state *ps, int port, + u16 *new, u16 *old) { + struct dsa_switch *ds = ps->ds; + u16 upper_mask; u16 fid; int ret; + if (mv88e6xxx_num_databases(ps) == 4096) + upper_mask = 0xff; + else if (mv88e6xxx_num_databases(ps) == 256) + upper_mask = 0xf; + else + return -EOPNOTSUPP; + /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */ - ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_BASE_VLAN); + ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_BASE_VLAN); if (ret < 0) return ret; @@ -1525,24 +1820,24 @@ static int _mv88e6xxx_port_fid(struct dsa_switch *ds, int port, u16 *new, ret &= ~PORT_BASE_VLAN_FID_3_0_MASK; ret |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK; - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN, ret); if (ret < 0) return ret; } /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */ - ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL_1); + ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_1); if (ret < 0) return ret; - fid |= (ret & PORT_CONTROL_1_FID_11_4_MASK) << 4; + fid |= (ret & upper_mask) << 4; if (new) { - ret &= ~PORT_CONTROL_1_FID_11_4_MASK; - ret |= (*new >> 4) & PORT_CONTROL_1_FID_11_4_MASK; + ret &= ~upper_mask; + ret |= (*new >> 4) & upper_mask; - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_1, + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1, ret); if (ret < 0) return ret; @@ -1556,19 +1851,20 @@ static int _mv88e6xxx_port_fid(struct dsa_switch *ds, int port, u16 *new, return 0; } -static int _mv88e6xxx_port_fid_get(struct dsa_switch *ds, int port, u16 *fid) +static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_priv_state *ps, + int port, u16 *fid) { - return _mv88e6xxx_port_fid(ds, port, NULL, fid); + return _mv88e6xxx_port_fid(ps, port, NULL, fid); } -static int _mv88e6xxx_port_fid_set(struct dsa_switch *ds, int port, u16 fid) +static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_priv_state *ps, + int port, u16 fid) { - return _mv88e6xxx_port_fid(ds, port, &fid, NULL); + return _mv88e6xxx_port_fid(ps, port, &fid, NULL); } -static int _mv88e6xxx_fid_new(struct dsa_switch *ds, u16 *fid) +static int _mv88e6xxx_fid_new(struct mv88e6xxx_priv_state *ps, u16 *fid) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); struct mv88e6xxx_vtu_stu_entry vlan; int i, err; @@ -1576,8 +1872,8 @@ static int _mv88e6xxx_fid_new(struct dsa_switch *ds, u16 *fid) bitmap_zero(fid_bitmap, MV88E6XXX_N_FID); /* Set every FID bit used by the (un)bridged ports */ - for (i = 0; i < ps->num_ports; ++i) { - err = _mv88e6xxx_port_fid_get(ds, i, fid); + for (i = 0; i < ps->info->num_ports; ++i) { + err = _mv88e6xxx_port_fid_get(ps, i, fid); if (err) return err; @@ -1585,12 +1881,12 @@ static int _mv88e6xxx_fid_new(struct dsa_switch *ds, u16 *fid) } /* Set every FID bit used by the VLAN entries */ - err = _mv88e6xxx_vtu_vid_write(ds, GLOBAL_VTU_VID_MASK); + err = _mv88e6xxx_vtu_vid_write(ps, GLOBAL_VTU_VID_MASK); if (err) return err; do { - err = _mv88e6xxx_vtu_getnext(ds, &vlan); + err = _mv88e6xxx_vtu_getnext(ps, &vlan); if (err) return err; @@ -1604,35 +1900,35 @@ static int _mv88e6xxx_fid_new(struct dsa_switch *ds, u16 *fid) * databases are not needed. Return the next positive available. */ *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1); - if (unlikely(*fid == MV88E6XXX_N_FID)) + if (unlikely(*fid >= mv88e6xxx_num_databases(ps))) return -ENOSPC; /* Clear the database */ - return _mv88e6xxx_atu_flush(ds, *fid, true); + return _mv88e6xxx_atu_flush(ps, *fid, true); } -static int _mv88e6xxx_vtu_new(struct dsa_switch *ds, u16 vid, +static int _mv88e6xxx_vtu_new(struct mv88e6xxx_priv_state *ps, u16 vid, struct mv88e6xxx_vtu_stu_entry *entry) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + struct dsa_switch *ds = ps->ds; struct mv88e6xxx_vtu_stu_entry vlan = { .valid = true, .vid = vid, }; int i, err; - err = _mv88e6xxx_fid_new(ds, &vlan.fid); + err = _mv88e6xxx_fid_new(ps, &vlan.fid); if (err) return err; /* exclude all ports except the CPU and DSA ports */ - for (i = 0; i < ps->num_ports; ++i) + for (i = 0; i < ps->info->num_ports; ++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(ds) || mv88e6xxx_6165_family(ds) || - mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) { + if (mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) || + mv88e6xxx_6351_family(ps) || mv88e6xxx_6352_family(ps)) { struct mv88e6xxx_vtu_stu_entry vstp; /* Adding a VTU entry requires a valid STU entry. As VSTP is not @@ -1640,7 +1936,7 @@ static int _mv88e6xxx_vtu_new(struct dsa_switch *ds, u16 vid, * entries. Thus, validate the SID 0. */ vlan.sid = 0; - err = _mv88e6xxx_stu_getnext(ds, GLOBAL_VTU_SID_MASK, &vstp); + err = _mv88e6xxx_stu_getnext(ps, GLOBAL_VTU_SID_MASK, &vstp); if (err) return err; @@ -1649,7 +1945,7 @@ static int _mv88e6xxx_vtu_new(struct dsa_switch *ds, u16 vid, vstp.valid = true; vstp.sid = vlan.sid; - err = _mv88e6xxx_stu_loadpurge(ds, &vstp); + err = _mv88e6xxx_stu_loadpurge(ps, &vstp); if (err) return err; } @@ -1659,7 +1955,7 @@ static int _mv88e6xxx_vtu_new(struct dsa_switch *ds, u16 vid, return 0; } -static int _mv88e6xxx_vtu_get(struct dsa_switch *ds, u16 vid, +static int _mv88e6xxx_vtu_get(struct mv88e6xxx_priv_state *ps, u16 vid, struct mv88e6xxx_vtu_stu_entry *entry, bool creat) { int err; @@ -1667,11 +1963,11 @@ static int _mv88e6xxx_vtu_get(struct dsa_switch *ds, u16 vid, if (!vid) return -EINVAL; - err = _mv88e6xxx_vtu_vid_write(ds, vid - 1); + err = _mv88e6xxx_vtu_vid_write(ps, vid - 1); if (err) return err; - err = _mv88e6xxx_vtu_getnext(ds, entry); + err = _mv88e6xxx_vtu_getnext(ps, entry); if (err) return err; @@ -1682,7 +1978,7 @@ static int _mv88e6xxx_vtu_get(struct dsa_switch *ds, u16 vid, * -EOPNOTSUPP to inform bridge about an eventual software VLAN. */ - err = _mv88e6xxx_vtu_new(ds, vid, entry); + err = _mv88e6xxx_vtu_new(ps, vid, entry); } return err; @@ -1700,12 +1996,12 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, mutex_lock(&ps->smi_mutex); - err = _mv88e6xxx_vtu_vid_write(ds, vid_begin - 1); + err = _mv88e6xxx_vtu_vid_write(ps, vid_begin - 1); if (err) goto unlock; do { - err = _mv88e6xxx_vtu_getnext(ds, &vlan); + err = _mv88e6xxx_vtu_getnext(ps, &vlan); if (err) goto unlock; @@ -1715,7 +2011,7 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, if (vlan.vid > vid_end) break; - for (i = 0; i < ps->num_ports; ++i) { + for (i = 0; i < ps->info->num_ports; ++i) { if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i)) continue; @@ -1749,17 +2045,20 @@ static const char * const mv88e6xxx_port_8021q_mode_names[] = { [PORT_CONTROL_2_8021Q_SECURE] = "Secure", }; -int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, - bool vlan_filtering) +static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, + bool vlan_filtering) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE : PORT_CONTROL_2_8021Q_DISABLED; int ret; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU)) + return -EOPNOTSUPP; + mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL_2); + ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_2); if (ret < 0) goto unlock; @@ -1769,7 +2068,7 @@ int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, ret &= ~PORT_CONTROL_2_8021Q_MASK; ret |= new & PORT_CONTROL_2_8021Q_MASK; - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_2, + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_2, ret); if (ret < 0) goto unlock; @@ -1786,12 +2085,16 @@ unlock: return ret; } -int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans) +static int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct switchdev_trans *trans) { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int err; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU)) + return -EOPNOTSUPP; + /* If the requested port doesn't belong to the same bridge as the VLAN * members, do not support it (yet) and fallback to software VLAN. */ @@ -1806,13 +2109,13 @@ int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, return 0; } -static int _mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid, - bool untagged) +static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_priv_state *ps, int port, + u16 vid, bool untagged) { struct mv88e6xxx_vtu_stu_entry vlan; int err; - err = _mv88e6xxx_vtu_get(ds, vid, &vlan, true); + err = _mv88e6xxx_vtu_get(ps, vid, &vlan, true); if (err) return err; @@ -1820,43 +2123,43 @@ static int _mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid, GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED : GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED; - return _mv88e6xxx_vtu_loadpurge(ds, &vlan); + return _mv88e6xxx_vtu_loadpurge(ps, &vlan); } -int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans) +static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct switchdev_trans *trans) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; u16 vid; - int err = 0; + + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU)) + return; mutex_lock(&ps->smi_mutex); - for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { - err = _mv88e6xxx_port_vlan_add(ds, port, vid, untagged); - if (err) - goto unlock; - } + for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) + if (_mv88e6xxx_port_vlan_add(ps, port, vid, untagged)) + netdev_err(ds->ports[port], "failed to add VLAN %d%c\n", + vid, untagged ? 'u' : 't'); - /* no PVID with ranges, otherwise it's a bug */ - if (pvid) - err = _mv88e6xxx_port_pvid_set(ds, port, vlan->vid_end); -unlock: - mutex_unlock(&ps->smi_mutex); + if (pvid && _mv88e6xxx_port_pvid_set(ps, port, vlan->vid_end)) + netdev_err(ds->ports[port], "failed to set PVID %d\n", + vlan->vid_end); - return err; + mutex_unlock(&ps->smi_mutex); } -static int _mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid) +static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_priv_state *ps, + int port, u16 vid) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + struct dsa_switch *ds = ps->ds; struct mv88e6xxx_vtu_stu_entry vlan; int i, err; - err = _mv88e6xxx_vtu_get(ds, vid, &vlan, false); + err = _mv88e6xxx_vtu_get(ps, vid, &vlan, false); if (err) return err; @@ -1868,7 +2171,7 @@ static int _mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid) /* keep the VLAN unless all ports are excluded */ vlan.valid = false; - for (i = 0; i < ps->num_ports; ++i) { + for (i = 0; i < ps->info->num_ports; ++i) { if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)) continue; @@ -1878,33 +2181,36 @@ static int _mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid) } } - err = _mv88e6xxx_vtu_loadpurge(ds, &vlan); + err = _mv88e6xxx_vtu_loadpurge(ps, &vlan); if (err) return err; - return _mv88e6xxx_atu_remove(ds, vlan.fid, port, false); + return _mv88e6xxx_atu_remove(ps, vlan.fid, port, false); } -int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); u16 pvid, vid; int err = 0; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU)) + return -EOPNOTSUPP; + mutex_lock(&ps->smi_mutex); - err = _mv88e6xxx_port_pvid_get(ds, port, &pvid); + err = _mv88e6xxx_port_pvid_get(ps, port, &pvid); if (err) goto unlock; for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { - err = _mv88e6xxx_port_vlan_del(ds, port, vid); + err = _mv88e6xxx_port_vlan_del(ps, port, vid); if (err) goto unlock; if (vid == pvid) { - err = _mv88e6xxx_port_pvid_set(ds, port, 0); + err = _mv88e6xxx_port_pvid_set(ps, port, 0); if (err) goto unlock; } @@ -1916,14 +2222,14 @@ unlock: return err; } -static int _mv88e6xxx_atu_mac_write(struct dsa_switch *ds, +static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_priv_state *ps, 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, + ps, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i, (addr[i * 2] << 8) | addr[i * 2 + 1]); if (ret < 0) return ret; @@ -1932,12 +2238,13 @@ static int _mv88e6xxx_atu_mac_write(struct dsa_switch *ds, return 0; } -static int _mv88e6xxx_atu_mac_read(struct dsa_switch *ds, unsigned char *addr) +static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_priv_state *ps, + unsigned char *addr) { int i, ret; for (i = 0; i < 3; i++) { - ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i); if (ret < 0) return ret; @@ -1948,31 +2255,27 @@ static int _mv88e6xxx_atu_mac_read(struct dsa_switch *ds, unsigned char *addr) return 0; } -static int _mv88e6xxx_atu_load(struct dsa_switch *ds, +static int _mv88e6xxx_atu_load(struct mv88e6xxx_priv_state *ps, struct mv88e6xxx_atu_entry *entry) { int ret; - ret = _mv88e6xxx_atu_wait(ds); + ret = _mv88e6xxx_atu_wait(ps); if (ret < 0) return ret; - ret = _mv88e6xxx_atu_mac_write(ds, entry->mac); + ret = _mv88e6xxx_atu_mac_write(ps, entry->mac); if (ret < 0) return ret; - ret = _mv88e6xxx_atu_data_write(ds, entry); + ret = _mv88e6xxx_atu_data_write(ps, entry); if (ret < 0) return ret; - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, entry->fid); - if (ret < 0) - return ret; - - return _mv88e6xxx_atu_cmd(ds, GLOBAL_ATU_OP_LOAD_DB); + return _mv88e6xxx_atu_cmd(ps, entry->fid, GLOBAL_ATU_OP_LOAD_DB); } -static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port, +static int _mv88e6xxx_port_fdb_load(struct mv88e6xxx_priv_state *ps, int port, const unsigned char *addr, u16 vid, u8 state) { @@ -1982,9 +2285,9 @@ static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port, /* Null VLAN ID corresponds to the port private database */ if (vid == 0) - err = _mv88e6xxx_port_fid_get(ds, port, &vlan.fid); + err = _mv88e6xxx_port_fid_get(ps, port, &vlan.fid); else - err = _mv88e6xxx_vtu_get(ds, vid, &vlan, false); + err = _mv88e6xxx_vtu_get(ps, vid, &vlan, false); if (err) return err; @@ -1996,51 +2299,60 @@ static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port, entry.portv_trunkid = BIT(port); } - return _mv88e6xxx_atu_load(ds, &entry); + return _mv88e6xxx_atu_load(ps, &entry); } -int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) +static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans) { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU)) + return -EOPNOTSUPP; + /* We don't need any dynamic resource from the kernel (yet), * so skip the prepare phase. */ return 0; } -int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) +static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans) { int state = is_multicast_ether_addr(fdb->addr) ? GLOBAL_ATU_DATA_STATE_MC_STATIC : GLOBAL_ATU_DATA_STATE_UC_STATIC; struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; + + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU)) + return; mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_port_fdb_load(ds, port, fdb->addr, fdb->vid, state); + if (_mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid, state)) + netdev_err(ds->ports[port], "failed to load MAC address\n"); mutex_unlock(&ps->smi_mutex); - - return ret; } -int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb) +static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_fdb *fdb) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU)) + return -EOPNOTSUPP; + mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_port_fdb_load(ds, port, fdb->addr, fdb->vid, + ret = _mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid, GLOBAL_ATU_DATA_STATE_UNUSED); mutex_unlock(&ps->smi_mutex); return ret; } -static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid, +static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_priv_state *ps, u16 fid, struct mv88e6xxx_atu_entry *entry) { struct mv88e6xxx_atu_entry next = { 0 }; @@ -2048,23 +2360,19 @@ static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid, next.fid = fid; - ret = _mv88e6xxx_atu_wait(ds); - if (ret < 0) - return ret; - - ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, fid); + ret = _mv88e6xxx_atu_wait(ps); if (ret < 0) return ret; - ret = _mv88e6xxx_atu_cmd(ds, GLOBAL_ATU_OP_GET_NEXT_DB); + ret = _mv88e6xxx_atu_cmd(ps, fid, GLOBAL_ATU_OP_GET_NEXT_DB); if (ret < 0) return ret; - ret = _mv88e6xxx_atu_mac_read(ds, next.mac); + ret = _mv88e6xxx_atu_mac_read(ps, next.mac); if (ret < 0) return ret; - ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_DATA); + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_DATA); if (ret < 0) return ret; @@ -2089,8 +2397,8 @@ static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid, return 0; } -static int _mv88e6xxx_port_fdb_dump_one(struct dsa_switch *ds, u16 fid, u16 vid, - int port, +static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_priv_state *ps, + u16 fid, u16 vid, int port, struct switchdev_obj_port_fdb *fdb, int (*cb)(struct switchdev_obj *obj)) { @@ -2099,12 +2407,12 @@ static int _mv88e6xxx_port_fdb_dump_one(struct dsa_switch *ds, u16 fid, u16 vid, }; int err; - err = _mv88e6xxx_atu_mac_write(ds, addr.mac); + err = _mv88e6xxx_atu_mac_write(ps, addr.mac); if (err) return err; do { - err = _mv88e6xxx_atu_getnext(ds, fid, &addr); + err = _mv88e6xxx_atu_getnext(ps, fid, &addr); if (err) break; @@ -2130,9 +2438,9 @@ static int _mv88e6xxx_port_fdb_dump_one(struct dsa_switch *ds, u16 fid, u16 vid, return err; } -int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - int (*cb)(struct switchdev_obj *obj)) +static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, + struct switchdev_obj_port_fdb *fdb, + int (*cb)(struct switchdev_obj *obj)) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_vtu_stu_entry vlan = { @@ -2141,31 +2449,34 @@ int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, u16 fid; int err; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU)) + return -EOPNOTSUPP; + mutex_lock(&ps->smi_mutex); /* Dump port's default Filtering Information Database (VLAN ID 0) */ - err = _mv88e6xxx_port_fid_get(ds, port, &fid); + err = _mv88e6xxx_port_fid_get(ps, port, &fid); if (err) goto unlock; - err = _mv88e6xxx_port_fdb_dump_one(ds, fid, 0, port, fdb, cb); + err = _mv88e6xxx_port_fdb_dump_one(ps, fid, 0, port, fdb, cb); if (err) goto unlock; /* Dump VLANs' Filtering Information Databases */ - err = _mv88e6xxx_vtu_vid_write(ds, vlan.vid); + err = _mv88e6xxx_vtu_vid_write(ps, vlan.vid); if (err) goto unlock; do { - err = _mv88e6xxx_vtu_getnext(ds, &vlan); + err = _mv88e6xxx_vtu_getnext(ps, &vlan); if (err) break; if (!vlan.valid) break; - err = _mv88e6xxx_port_fdb_dump_one(ds, vlan.fid, vlan.vid, port, + err = _mv88e6xxx_port_fdb_dump_one(ps, vlan.fid, vlan.vid, port, fdb, cb); if (err) break; @@ -2177,20 +2488,23 @@ unlock: return err; } -int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, - struct net_device *bridge) +static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, + struct net_device *bridge) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int i, err = 0; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VLANTABLE)) + return -EOPNOTSUPP; + mutex_lock(&ps->smi_mutex); /* Assign the bridge and remap each port's VLANTable */ ps->ports[port].bridge_dev = bridge; - for (i = 0; i < ps->num_ports; ++i) { + for (i = 0; i < ps->info->num_ports; ++i) { if (ps->ports[i].bridge_dev == bridge) { - err = _mv88e6xxx_port_based_vlan_map(ds, i); + err = _mv88e6xxx_port_based_vlan_map(ps, i); if (err) break; } @@ -2201,89 +2515,134 @@ int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, return err; } -void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port) +static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct net_device *bridge = ps->ports[port].bridge_dev; int i; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VLANTABLE)) + return; + mutex_lock(&ps->smi_mutex); /* Unassign the bridge and remap each port's VLANTable */ ps->ports[port].bridge_dev = NULL; - for (i = 0; i < ps->num_ports; ++i) + for (i = 0; i < ps->info->num_ports; ++i) if (i == port || ps->ports[i].bridge_dev == bridge) - if (_mv88e6xxx_port_based_vlan_map(ds, i)) + if (_mv88e6xxx_port_based_vlan_map(ps, i)) netdev_warn(ds->ports[i], "failed to remap\n"); mutex_unlock(&ps->smi_mutex); } -static void mv88e6xxx_bridge_work(struct work_struct *work) +static int _mv88e6xxx_phy_page_write(struct mv88e6xxx_priv_state *ps, + int port, int page, int reg, int val) { - 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; + int ret; - mutex_lock(&ps->smi_mutex); + ret = _mv88e6xxx_phy_write_indirect(ps, port, 0x16, page); + if (ret < 0) + goto restore_page_0; - for (port = 0; port < ps->num_ports; ++port) - if (test_and_clear_bit(port, ps->port_state_update_mask) && - _mv88e6xxx_port_state(ds, port, ps->ports[port].state)) - netdev_warn(ds->ports[port], "failed to update state to %s\n", - mv88e6xxx_port_state_names[ps->ports[port].state]); + ret = _mv88e6xxx_phy_write_indirect(ps, port, reg, val); +restore_page_0: + _mv88e6xxx_phy_write_indirect(ps, port, 0x16, 0x0); - mutex_unlock(&ps->smi_mutex); + return ret; } -static int _mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page, - int reg, int val) +static int _mv88e6xxx_phy_page_read(struct mv88e6xxx_priv_state *ps, + int port, int page, int reg) { int ret; - ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page); + ret = _mv88e6xxx_phy_write_indirect(ps, port, 0x16, page); if (ret < 0) goto restore_page_0; - ret = _mv88e6xxx_phy_write_indirect(ds, port, reg, val); + ret = _mv88e6xxx_phy_read_indirect(ps, port, reg); restore_page_0: - _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0); + _mv88e6xxx_phy_write_indirect(ps, port, 0x16, 0x0); return ret; } -static int _mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, - int reg) +static int mv88e6xxx_switch_reset(struct mv88e6xxx_priv_state *ps) { + bool ppu_active = mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU_ACTIVE); + u16 is_reset = (ppu_active ? 0x8800 : 0xc800); + struct gpio_desc *gpiod = ps->reset; + unsigned long timeout; int ret; + int i; - ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page); - if (ret < 0) - goto restore_page_0; + /* Set all ports to the disabled state. */ + for (i = 0; i < ps->info->num_ports; i++) { + ret = _mv88e6xxx_reg_read(ps, REG_PORT(i), PORT_CONTROL); + if (ret < 0) + return ret; - ret = _mv88e6xxx_phy_read_indirect(ds, port, reg); -restore_page_0: - _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0); + ret = _mv88e6xxx_reg_write(ps, REG_PORT(i), PORT_CONTROL, + ret & 0xfffc); + if (ret) + return ret; + } + + /* Wait for transmit queues to drain. */ + usleep_range(2000, 4000); + + /* If there is a gpio connected to the reset pin, toggle it */ + if (gpiod) { + gpiod_set_value_cansleep(gpiod, 1); + usleep_range(10000, 20000); + gpiod_set_value_cansleep(gpiod, 0); + usleep_range(10000, 20000); + } + + /* 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) + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc000); + else + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc400); + if (ret) + return ret; + + /* Wait up to one second for reset to complete. */ + timeout = jiffies + 1 * HZ; + while (time_before(jiffies, timeout)) { + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, 0x00); + if (ret < 0) + return ret; + + if ((ret & is_reset) == is_reset) + break; + usleep_range(1000, 2000); + } + if (time_after(jiffies, timeout)) + ret = -ETIMEDOUT; + else + ret = 0; return ret; } -static int mv88e6xxx_power_on_serdes(struct dsa_switch *ds) +static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_priv_state *ps) { int ret; - ret = _mv88e6xxx_phy_page_read(ds, REG_FIBER_SERDES, PAGE_FIBER_SERDES, + ret = _mv88e6xxx_phy_page_read(ps, REG_FIBER_SERDES, PAGE_FIBER_SERDES, MII_BMCR); if (ret < 0) return ret; if (ret & BMCR_PDOWN) { ret &= ~BMCR_PDOWN; - ret = _mv88e6xxx_phy_page_write(ds, REG_FIBER_SERDES, + ret = _mv88e6xxx_phy_page_write(ps, REG_FIBER_SERDES, PAGE_FIBER_SERDES, MII_BMCR, ret); } @@ -2291,32 +2650,30 @@ static int mv88e6xxx_power_on_serdes(struct dsa_switch *ds) return ret; } -static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) +static int mv88e6xxx_setup_port(struct mv88e6xxx_priv_state *ps, int port) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + struct dsa_switch *ds = ps->ds; int ret; u16 reg; - mutex_lock(&ps->smi_mutex); - - if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) || - mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) || - mv88e6xxx_6185_family(ds) || mv88e6xxx_6095_family(ds) || - mv88e6xxx_6065_family(ds) || mv88e6xxx_6320_family(ds)) { + if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || + mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) || + mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) || + mv88e6xxx_6065_family(ps) || mv88e6xxx_6320_family(ps)) { /* MAC Forcing register: don't force link, speed, * duplex or flow control state to any particular * values on physical ports, but force the CPU port * and all DSA ports to their maximum bandwidth and * full duplex. */ - reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_PCS_CTRL); + reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_PCS_CTRL); if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { reg &= ~PORT_PCS_CTRL_UNFORCED; reg |= PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP | PORT_PCS_CTRL_DUPLEX_FULL | PORT_PCS_CTRL_FORCE_DUPLEX; - if (mv88e6xxx_6065_family(ds)) + if (mv88e6xxx_6065_family(ps)) reg |= PORT_PCS_CTRL_100; else reg |= PORT_PCS_CTRL_1000; @@ -2324,10 +2681,10 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) reg |= PORT_PCS_CTRL_UNFORCED; } - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_PCS_CTRL, reg); if (ret) - goto abort; + return ret; } /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock, @@ -2345,19 +2702,19 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) * forwarding of unknown unicasts and multicasts. */ reg = 0; - if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) || - mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) || - mv88e6xxx_6095_family(ds) || mv88e6xxx_6065_family(ds) || - mv88e6xxx_6185_family(ds) || mv88e6xxx_6320_family(ds)) + if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || + mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) || + mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) || + mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps)) reg = PORT_CONTROL_IGMP_MLD_SNOOP | PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP | PORT_CONTROL_STATE_FORWARDING; if (dsa_is_cpu_port(ds, port)) { - if (mv88e6xxx_6095_family(ds) || mv88e6xxx_6185_family(ds)) + if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps)) reg |= PORT_CONTROL_DSA_TAG; - if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) || - mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) || - mv88e6xxx_6320_family(ds)) { + if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || + mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) || + mv88e6xxx_6320_family(ps)) { if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA) reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA; else @@ -2366,20 +2723,20 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) PORT_CONTROL_FORWARD_UNKNOWN_MC; } - if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) || - mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) || - mv88e6xxx_6095_family(ds) || mv88e6xxx_6065_family(ds) || - mv88e6xxx_6185_family(ds) || mv88e6xxx_6320_family(ds)) { + if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || + mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) || + mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) || + mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps)) { if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA) reg |= PORT_CONTROL_EGRESS_ADD_TAG; } } if (dsa_is_dsa_port(ds, port)) { - if (mv88e6xxx_6095_family(ds) || mv88e6xxx_6185_family(ds)) + if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps)) reg |= PORT_CONTROL_DSA_TAG; - if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) || - mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) || - mv88e6xxx_6320_family(ds)) { + if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || + mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) || + mv88e6xxx_6320_family(ps)) { reg |= PORT_CONTROL_FRAME_MODE_DSA; } @@ -2388,26 +2745,26 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) PORT_CONTROL_FORWARD_UNKNOWN_MC; } if (reg) { - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL, reg); if (ret) - goto abort; + return ret; } /* If this port is connected to a SerDes, make sure the SerDes is not * powered down. */ - if (mv88e6xxx_6352_family(ds)) { - ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_STATUS); + if (mv88e6xxx_6352_family(ps)) { + ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS); if (ret < 0) - goto abort; + return ret; ret &= PORT_STATUS_CMODE_MASK; if ((ret == PORT_STATUS_CMODE_100BASE_X) || (ret == PORT_STATUS_CMODE_1000BASE_X) || (ret == PORT_STATUS_CMODE_SGMII)) { - ret = mv88e6xxx_power_on_serdes(ds); + ret = mv88e6xxx_power_on_serdes(ps); if (ret < 0) - goto abort; + return ret; } } @@ -2418,16 +2775,17 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) * copy of all transmitted/received frames on this port to the CPU. */ reg = 0; - if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) || - mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) || - mv88e6xxx_6095_family(ds) || mv88e6xxx_6320_family(ds)) + if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || + mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) || + mv88e6xxx_6095_family(ps) || mv88e6xxx_6320_family(ps) || + mv88e6xxx_6185_family(ps)) reg = PORT_CONTROL_2_MAP_DA; - if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) || - mv88e6xxx_6165_family(ds) || mv88e6xxx_6320_family(ds)) + if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || + mv88e6xxx_6165_family(ps) || mv88e6xxx_6320_family(ps)) reg |= PORT_CONTROL_2_JUMBO_10240; - if (mv88e6xxx_6095_family(ds) || mv88e6xxx_6185_family(ds)) { + if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps)) { /* Set the upstream port this port should use */ reg |= dsa_upstream_port(ds); /* enable forwarding of unknown multicast addresses to @@ -2440,10 +2798,10 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) reg |= PORT_CONTROL_2_8021Q_DISABLED; if (reg) { - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_2, reg); if (ret) - goto abort; + return ret; } /* Port Association Vector: when learning source addresses @@ -2456,300 +2814,344 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) if (dsa_is_cpu_port(ds, port)) reg = 0; - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_ASSOC_VECTOR, reg); + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_ASSOC_VECTOR, reg); if (ret) - goto abort; + return ret; /* Egress rate control 2: disable egress rate control. */ - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_RATE_CONTROL_2, + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_RATE_CONTROL_2, 0x0000); if (ret) - goto abort; + return ret; - if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) || - mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) || - mv88e6xxx_6320_family(ds)) { + if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || + mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) || + mv88e6xxx_6320_family(ps)) { /* 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. */ - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_PAUSE_CTRL, 0x0000); if (ret) - goto abort; + return ret; /* Port ATU control: disable limiting the number of * address database entries that this port is allowed * to use. */ - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_ATU_CONTROL, 0x0000); /* Priority Override: disable DA, SA and VTU priority * override. */ - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_PRI_OVERRIDE, 0x0000); if (ret) - goto abort; + return ret; /* Port Ethertype: use the Ethertype DSA Ethertype * value. */ - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_ETH_TYPE, ETH_P_EDSA); if (ret) - goto abort; + return ret; /* Tag Remap: use an identity 802.1p prio -> switch * prio mapping. */ - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_TAG_REGMAP_0123, 0x3210); if (ret) - goto abort; + return ret; /* Tag Remap 2: use an identity 802.1p prio -> switch * prio mapping. */ - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_TAG_REGMAP_4567, 0x7654); if (ret) - goto abort; + return ret; } - if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) || - mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) || - mv88e6xxx_6185_family(ds) || mv88e6xxx_6095_family(ds) || - mv88e6xxx_6320_family(ds)) { + if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || + mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) || + mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) || + mv88e6xxx_6320_family(ps)) { /* Rate Control: disable ingress rate limiting. */ - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_RATE_CONTROL, 0x0001); if (ret) - goto abort; + return ret; } /* Port Control 1: disable trunking, disable sending * learning messages to this port. */ - ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_1, 0x0000); + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1, 0x0000); if (ret) - goto abort; + return ret; /* Port based VLAN map: give each port the same default address * database, and allow bidirectional communication between the * CPU and DSA port(s), and the other ports. */ - ret = _mv88e6xxx_port_fid_set(ds, port, 0); + ret = _mv88e6xxx_port_fid_set(ps, port, 0); if (ret) - goto abort; + return ret; - ret = _mv88e6xxx_port_based_vlan_map(ds, port); + ret = _mv88e6xxx_port_based_vlan_map(ps, port); if (ret) - goto abort; + return ret; /* 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), PORT_DEFAULT_VLAN, + ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_DEFAULT_VLAN, 0x0000); -abort: - mutex_unlock(&ps->smi_mutex); - return ret; -} - -int mv88e6xxx_setup_ports(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - int i; + if (ret) + return ret; - for (i = 0; i < ps->num_ports; i++) { - ret = mv88e6xxx_setup_port(ds, i); - if (ret < 0) - return ret; - } return 0; } -int mv88e6xxx_setup_common(struct dsa_switch *ds) +static int mv88e6xxx_setup_global(struct mv88e6xxx_priv_state *ps) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - - mutex_init(&ps->smi_mutex); + struct dsa_switch *ds = ps->ds; + u32 upstream_port = dsa_upstream_port(ds); + u16 reg; + int err; + int i; - ps->id = REG_READ(REG_PORT(0), PORT_SWITCH_ID) & 0xfff0; + /* Enable the PHY Polling Unit if present, don't discard any packets, + * and mask all interrupt sources. + */ + reg = 0; + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU) || + mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU_ACTIVE)) + reg |= GLOBAL_CONTROL_PPU_ENABLE; - INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work); + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, reg); + if (err) + return err; - return 0; -} + /* Configure the upstream port, and configure it as the port to which + * ingress and egress and ARP monitor frames are to be sent. + */ + reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | + upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT | + upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT; + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg); + if (err) + return err; -int mv88e6xxx_setup_global(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - int i; + /* Disable remote management, and set the switch's DSA device number. */ + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL_2, + GLOBAL_CONTROL_2_MULTIPLE_CASCADE | + (ds->index & 0x1f)); + if (err) + return err; /* Set the default address aging time to 5 minutes, and * enable address learn messages to be sent to all message * ports. */ - REG_WRITE(REG_GLOBAL, GLOBAL_ATU_CONTROL, - 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL); + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL, + 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL); + if (err) + return err; /* Configure the IP ToS mapping registers. */ - 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); + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000); + if (err) + return err; + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000); + if (err) + return err; + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555); + if (err) + return err; + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555); + if (err) + return err; + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa); + if (err) + return err; + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa); + if (err) + return err; + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff); + if (err) + return err; + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff); + if (err) + return err; /* Configure the IEEE 802.1p priority mapping register. */ - REG_WRITE(REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41); + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41); + if (err) + return err; /* Send all frames with destination addresses matching * 01:80:c2:00:00:0x to the CPU port. */ - REG_WRITE(REG_GLOBAL2, GLOBAL2_MGMT_EN_0X, 0xffff); + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X, 0xffff); + if (err) + return err; /* Ignore removed tag data on doubly tagged packets, disable * flow control messages, force flow control priority to the * highest, and send all special multicast frames to the CPU * port at the highest priority. */ - REG_WRITE(REG_GLOBAL2, GLOBAL2_SWITCH_MGMT, - 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 | - GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI); + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT, + 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 | + GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI); + if (err) + return err; /* Program the DSA routing table. */ for (i = 0; i < 32; i++) { int nexthop = 0x1f; - if (ds->pd->rtable && - i != ds->index && i < ds->dst->pd->nr_chips) - nexthop = ds->pd->rtable[i] & 0x1f; + if (ps->ds->cd->rtable && + i != ps->ds->index && i < ps->ds->dst->pd->nr_chips) + nexthop = ps->ds->cd->rtable[i] & 0x1f; - REG_WRITE(REG_GLOBAL2, GLOBAL2_DEVICE_MAPPING, - GLOBAL2_DEVICE_MAPPING_UPDATE | - (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | - nexthop); + err = _mv88e6xxx_reg_write( + ps, REG_GLOBAL2, + GLOBAL2_DEVICE_MAPPING, + GLOBAL2_DEVICE_MAPPING_UPDATE | + (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | nexthop); + if (err) + return err; } /* Clear all trunk masks. */ - for (i = 0; i < 8; i++) - REG_WRITE(REG_GLOBAL2, GLOBAL2_TRUNK_MASK, - 0x8000 | (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) | - ((1 << ps->num_ports) - 1)); + for (i = 0; i < 8; i++) { + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_TRUNK_MASK, + 0x8000 | + (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) | + ((1 << ps->info->num_ports) - 1)); + if (err) + return err; + } /* Clear all trunk mappings. */ - for (i = 0; i < 16; i++) - REG_WRITE(REG_GLOBAL2, GLOBAL2_TRUNK_MAPPING, - GLOBAL2_TRUNK_MAPPING_UPDATE | - (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT)); - - if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) || - mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) || - mv88e6xxx_6320_family(ds)) { + for (i = 0; i < 16; i++) { + err = _mv88e6xxx_reg_write( + ps, REG_GLOBAL2, + GLOBAL2_TRUNK_MAPPING, + GLOBAL2_TRUNK_MAPPING_UPDATE | + (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT)); + if (err) + return err; + } + + if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || + mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) || + mv88e6xxx_6320_family(ps)) { /* Send all frames with destination addresses matching * 01:80:c2:00:00:2x to the CPU port. */ - REG_WRITE(REG_GLOBAL2, GLOBAL2_MGMT_EN_2X, 0xffff); + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, + GLOBAL2_MGMT_EN_2X, 0xffff); + if (err) + return err; /* Initialise cross-chip port VLAN table to reset * defaults. */ - REG_WRITE(REG_GLOBAL2, GLOBAL2_PVT_ADDR, 0x9000); + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, + GLOBAL2_PVT_ADDR, 0x9000); + if (err) + return err; /* Clear the priority override table. */ - for (i = 0; i < 16; i++) - REG_WRITE(REG_GLOBAL2, GLOBAL2_PRIO_OVERRIDE, - 0x8000 | (i << 8)); + for (i = 0; i < 16; i++) { + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, + GLOBAL2_PRIO_OVERRIDE, + 0x8000 | (i << 8)); + if (err) + return err; + } } - if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) || - mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) || - mv88e6xxx_6185_family(ds) || mv88e6xxx_6095_family(ds) || - mv88e6xxx_6320_family(ds)) { + if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || + mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) || + mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) || + mv88e6xxx_6320_family(ps)) { /* Disable ingress rate limiting by resetting all * ingress rate limit registers to their initial * state. */ - for (i = 0; i < ps->num_ports; i++) - REG_WRITE(REG_GLOBAL2, GLOBAL2_INGRESS_OP, - 0x9000 | (i << 8)); + for (i = 0; i < ps->info->num_ports; i++) { + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, + GLOBAL2_INGRESS_OP, + 0x9000 | (i << 8)); + if (err) + return err; + } } /* Clear the statistics counters for all ports */ - REG_WRITE(REG_GLOBAL, GLOBAL_STATS_OP, GLOBAL_STATS_OP_FLUSH_ALL); + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP, + GLOBAL_STATS_OP_FLUSH_ALL); + if (err) + return err; /* Wait for the flush to complete. */ - mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_stats_wait(ds); - if (ret < 0) - goto unlock; + err = _mv88e6xxx_stats_wait(ps); + if (err) + return err; /* Clear all ATU entries */ - ret = _mv88e6xxx_atu_flush(ds, 0, true); - if (ret < 0) - goto unlock; + err = _mv88e6xxx_atu_flush(ps, 0, true); + if (err) + return err; /* Clear all the VTU and STU entries */ - ret = _mv88e6xxx_vtu_stu_flush(ds); -unlock: - mutex_unlock(&ps->smi_mutex); + err = _mv88e6xxx_vtu_stu_flush(ps); + if (err < 0) + return err; - return ret; + return err; } -int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active) +static int mv88e6xxx_setup(struct dsa_switch *ds) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - u16 is_reset = (ppu_active ? 0x8800 : 0xc800); - struct gpio_desc *gpiod = ds->pd->reset; - unsigned long timeout; - int ret; + int err; 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); - } + ps->ds = ds; - /* Wait for transmit queues to drain. */ - usleep_range(2000, 4000); + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM)) + mutex_init(&ps->eeprom_mutex); - /* If there is a gpio connected to the reset pin, toggle it */ - if (gpiod) { - gpiod_set_value_cansleep(gpiod, 1); - usleep_range(10000, 20000); - gpiod_set_value_cansleep(gpiod, 0); - usleep_range(10000, 20000); - } + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU)) + mv88e6xxx_ppu_state_init(ps); - /* 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); + mutex_lock(&ps->smi_mutex); - /* 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); + err = mv88e6xxx_switch_reset(ps); + if (err) + goto unlock; + + err = mv88e6xxx_setup_global(ps); + if (err) + goto unlock; + + for (i = 0; i < ps->info->num_ports; i++) { + err = mv88e6xxx_setup_port(ps, i); + if (err) + goto unlock; } - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - return 0; +unlock: + mutex_unlock(&ps->smi_mutex); + + return err; } int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg) @@ -2758,7 +3160,7 @@ int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg) int ret; mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_phy_page_read(ds, port, page, reg); + ret = _mv88e6xxx_phy_page_read(ps, port, page, reg); mutex_unlock(&ps->smi_mutex); return ret; @@ -2771,82 +3173,61 @@ int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page, int ret; mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_phy_page_write(ds, port, page, reg, val); + ret = _mv88e6xxx_phy_page_write(ps, port, page, reg, val); mutex_unlock(&ps->smi_mutex); return ret; } -static int mv88e6xxx_port_to_phy_addr(struct dsa_switch *ds, int port) +static int mv88e6xxx_port_to_phy_addr(struct mv88e6xxx_priv_state *ps, + int port) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - - if (port >= 0 && port < ps->num_ports) + if (port >= 0 && port < ps->info->num_ports) return port; return -EINVAL; } -int -mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum) +static 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 addr = mv88e6xxx_port_to_phy_addr(ps, port); int ret; if (addr < 0) - return addr; + return 0xffff; mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_phy_read(ds, addr, regnum); - mutex_unlock(&ps->smi_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; + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU)) + ret = mv88e6xxx_phy_read_ppu(ps, addr, regnum); + else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY)) + ret = _mv88e6xxx_phy_read_indirect(ps, addr, regnum); + else + ret = _mv88e6xxx_phy_read(ps, addr, regnum); - mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_phy_write(ds, addr, regnum, val); mutex_unlock(&ps->smi_mutex); return ret; } -int -mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum) +static 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 addr = mv88e6xxx_port_to_phy_addr(ps, port); int ret; if (addr < 0) - return addr; + return 0xffff; mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_phy_read_indirect(ds, addr, regnum); - mutex_unlock(&ps->smi_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; + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU)) + ret = mv88e6xxx_phy_write_ppu(ps, addr, regnum, val); + else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY)) + ret = _mv88e6xxx_phy_write_indirect(ps, addr, regnum, val); + else + ret = _mv88e6xxx_phy_write(ps, addr, regnum, val); - mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_phy_write_indirect(ds, addr, regnum, val); mutex_unlock(&ps->smi_mutex); return ret; } @@ -2863,44 +3244,45 @@ static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp) mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x6); + ret = _mv88e6xxx_phy_write(ps, 0x0, 0x16, 0x6); if (ret < 0) goto error; /* Enable temperature sensor */ - ret = _mv88e6xxx_phy_read(ds, 0x0, 0x1a); + ret = _mv88e6xxx_phy_read(ps, 0x0, 0x1a); if (ret < 0) goto error; - ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret | (1 << 5)); + ret = _mv88e6xxx_phy_write(ps, 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(ps, 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(ps, 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(ps, 0x0, 0x16, 0x0); mutex_unlock(&ps->smi_mutex); return ret; } static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp) { - int phy = mv88e6xxx_6320_family(ds) ? 3 : 0; + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int phy = mv88e6xxx_6320_family(ps) ? 3 : 0; int ret; *temp = 0; @@ -2914,20 +3296,26 @@ static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp) return 0; } -int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp) +static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp) { - if (mv88e6xxx_6320_family(ds) || mv88e6xxx_6352_family(ds)) + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP)) + return -EOPNOTSUPP; + + if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps)) return mv88e63xx_get_temp(ds, temp); return mv88e61xx_get_temp(ds, temp); } -int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp) +static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp) { - int phy = mv88e6xxx_6320_family(ds) ? 3 : 0; + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int phy = mv88e6xxx_6320_family(ps) ? 3 : 0; int ret; - if (!mv88e6xxx_6320_family(ds) && !mv88e6xxx_6352_family(ds)) + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT)) return -EOPNOTSUPP; *temp = 0; @@ -2941,12 +3329,13 @@ int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp) return 0; } -int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp) +static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp) { - int phy = mv88e6xxx_6320_family(ds) ? 3 : 0; + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int phy = mv88e6xxx_6320_family(ps) ? 3 : 0; int ret; - if (!mv88e6xxx_6320_family(ds) && !mv88e6xxx_6352_family(ds)) + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT)) return -EOPNOTSUPP; ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26); @@ -2957,12 +3346,13 @@ int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp) (ret & 0xe0ff) | (temp << 8)); } -int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm) +static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm) { - int phy = mv88e6xxx_6320_family(ds) ? 3 : 0; + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int phy = mv88e6xxx_6320_family(ps) ? 3 : 0; int ret; - if (!mv88e6xxx_6320_family(ds) && !mv88e6xxx_6352_family(ds)) + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT)) return -EOPNOTSUPP; *alarm = false; @@ -2977,70 +3367,354 @@ int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm) } #endif /* CONFIG_NET_DSA_HWMON */ -char *mv88e6xxx_lookup_name(struct device *host_dev, int sw_addr, - const struct mv88e6xxx_switch_id *table, - unsigned int num) +static const struct mv88e6xxx_info mv88e6xxx_table[] = { + [MV88E6085] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6085, + .family = MV88E6XXX_FAMILY_6097, + .name = "Marvell 88E6085", + .num_databases = 4096, + .num_ports = 10, + .flags = MV88E6XXX_FLAGS_FAMILY_6097, + }, + + [MV88E6095] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6095, + .family = MV88E6XXX_FAMILY_6095, + .name = "Marvell 88E6095/88E6095F", + .num_databases = 256, + .num_ports = 11, + .flags = MV88E6XXX_FLAGS_FAMILY_6095, + }, + + [MV88E6123] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6123, + .family = MV88E6XXX_FAMILY_6165, + .name = "Marvell 88E6123", + .num_databases = 4096, + .num_ports = 3, + .flags = MV88E6XXX_FLAGS_FAMILY_6165, + }, + + [MV88E6131] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6131, + .family = MV88E6XXX_FAMILY_6185, + .name = "Marvell 88E6131", + .num_databases = 256, + .num_ports = 8, + .flags = MV88E6XXX_FLAGS_FAMILY_6185, + }, + + [MV88E6161] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6161, + .family = MV88E6XXX_FAMILY_6165, + .name = "Marvell 88E6161", + .num_databases = 4096, + .num_ports = 6, + .flags = MV88E6XXX_FLAGS_FAMILY_6165, + }, + + [MV88E6165] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6165, + .family = MV88E6XXX_FAMILY_6165, + .name = "Marvell 88E6165", + .num_databases = 4096, + .num_ports = 6, + .flags = MV88E6XXX_FLAGS_FAMILY_6165, + }, + + [MV88E6171] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6171, + .family = MV88E6XXX_FAMILY_6351, + .name = "Marvell 88E6171", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6351, + }, + + [MV88E6172] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6172, + .family = MV88E6XXX_FAMILY_6352, + .name = "Marvell 88E6172", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6352, + }, + + [MV88E6175] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6175, + .family = MV88E6XXX_FAMILY_6351, + .name = "Marvell 88E6175", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6351, + }, + + [MV88E6176] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6176, + .family = MV88E6XXX_FAMILY_6352, + .name = "Marvell 88E6176", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6352, + }, + + [MV88E6185] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6185, + .family = MV88E6XXX_FAMILY_6185, + .name = "Marvell 88E6185", + .num_databases = 256, + .num_ports = 10, + .flags = MV88E6XXX_FLAGS_FAMILY_6185, + }, + + [MV88E6240] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6240, + .family = MV88E6XXX_FAMILY_6352, + .name = "Marvell 88E6240", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6352, + }, + + [MV88E6320] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6320, + .family = MV88E6XXX_FAMILY_6320, + .name = "Marvell 88E6320", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6320, + }, + + [MV88E6321] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6321, + .family = MV88E6XXX_FAMILY_6320, + .name = "Marvell 88E6321", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6320, + }, + + [MV88E6350] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6350, + .family = MV88E6XXX_FAMILY_6351, + .name = "Marvell 88E6350", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6351, + }, + + [MV88E6351] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6351, + .family = MV88E6XXX_FAMILY_6351, + .name = "Marvell 88E6351", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6351, + }, + + [MV88E6352] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6352, + .family = MV88E6XXX_FAMILY_6352, + .name = "Marvell 88E6352", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6352, + }, +}; + +static const struct mv88e6xxx_info * +mv88e6xxx_lookup_info(unsigned int prod_num, const struct mv88e6xxx_info *table, + unsigned int num) { - struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev); - int i, ret; + int i; + + for (i = 0; i < num; ++i) + if (table[i].prod_num == prod_num) + return &table[i]; + + return NULL; +} +static const char *mv88e6xxx_drv_probe(struct device *dsa_dev, + struct device *host_dev, int sw_addr, + void **priv) +{ + const struct mv88e6xxx_info *info; + struct mv88e6xxx_priv_state *ps; + struct mii_bus *bus; + const char *name; + int id, prod_num, rev; + + bus = dsa_host_dev_to_mii_bus(host_dev); if (!bus) return NULL; - ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID); - if (ret < 0) + id = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID); + if (id < 0) return NULL; - /* Look up the exact switch ID */ - for (i = 0; i < num; ++i) - if (table[i].id == ret) - return table[i].name; - - /* Look up only the product number */ - for (i = 0; i < num; ++i) { - if (table[i].id == (ret & PORT_SWITCH_ID_PROD_NUM_MASK)) { - dev_warn(host_dev, "unknown revision %d, using base switch 0x%x\n", - ret & PORT_SWITCH_ID_REV_MASK, - ret & PORT_SWITCH_ID_PROD_NUM_MASK); - return table[i].name; + prod_num = (id & 0xfff0) >> 4; + rev = id & 0x000f; + + info = mv88e6xxx_lookup_info(prod_num, mv88e6xxx_table, + ARRAY_SIZE(mv88e6xxx_table)); + if (!info) + return NULL; + + name = info->name; + + ps = devm_kzalloc(dsa_dev, sizeof(*ps), GFP_KERNEL); + if (!ps) + return NULL; + + ps->bus = bus; + ps->sw_addr = sw_addr; + ps->info = info; + mutex_init(&ps->smi_mutex); + + *priv = ps; + + dev_info(&ps->bus->dev, "switch 0x%x probed: %s, revision %u\n", + prod_num, name, rev); + + return name; +} + +struct dsa_switch_driver mv88e6xxx_switch_driver = { + .tag_protocol = DSA_TAG_PROTO_EDSA, + .probe = mv88e6xxx_drv_probe, + .setup = mv88e6xxx_setup, + .set_addr = mv88e6xxx_set_addr, + .phy_read = mv88e6xxx_phy_read, + .phy_write = mv88e6xxx_phy_write, + .adjust_link = mv88e6xxx_adjust_link, + .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 = 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, + .get_regs_len = mv88e6xxx_get_regs_len, + .get_regs = mv88e6xxx_get_regs, + .port_bridge_join = mv88e6xxx_port_bridge_join, + .port_bridge_leave = mv88e6xxx_port_bridge_leave, + .port_stp_state_set = mv88e6xxx_port_stp_state_set, + .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, + .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, + .port_vlan_add = mv88e6xxx_port_vlan_add, + .port_vlan_del = mv88e6xxx_port_vlan_del, + .port_vlan_dump = mv88e6xxx_port_vlan_dump, + .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, + .port_fdb_add = mv88e6xxx_port_fdb_add, + .port_fdb_del = mv88e6xxx_port_fdb_del, + .port_fdb_dump = mv88e6xxx_port_fdb_dump, +}; + +int mv88e6xxx_probe(struct mdio_device *mdiodev) +{ + struct device *dev = &mdiodev->dev; + struct device_node *np = dev->of_node; + struct mv88e6xxx_priv_state *ps; + int id, prod_num, rev; + struct dsa_switch *ds; + u32 eeprom_len; + int err; + + ds = devm_kzalloc(dev, sizeof(*ds) + sizeof(*ps), GFP_KERNEL); + if (!ds) + return -ENOMEM; + + ps = (struct mv88e6xxx_priv_state *)(ds + 1); + ds->priv = ps; + ds->dev = dev; + ps->dev = dev; + ps->ds = ds; + ps->bus = mdiodev->bus; + ps->sw_addr = mdiodev->addr; + mutex_init(&ps->smi_mutex); + + get_device(&ps->bus->dev); + + ds->drv = &mv88e6xxx_switch_driver; + + id = mv88e6xxx_reg_read(ps, REG_PORT(0), PORT_SWITCH_ID); + if (id < 0) + return id; + + prod_num = (id & 0xfff0) >> 4; + rev = id & 0x000f; + + ps->info = mv88e6xxx_lookup_info(prod_num, mv88e6xxx_table, + ARRAY_SIZE(mv88e6xxx_table)); + if (!ps->info) + return -ENODEV; + + ps->reset = devm_gpiod_get(&mdiodev->dev, "reset", GPIOD_ASIS); + if (IS_ERR(ps->reset)) { + err = PTR_ERR(ps->reset); + if (err == -ENOENT) { + /* Optional, so not an error */ + ps->reset = NULL; + } else { + return err; } } - return NULL; + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM) && + !of_property_read_u32(np, "eeprom-length", &eeprom_len)) + ps->eeprom_len = eeprom_len; + + dev_set_drvdata(dev, ds); + + dev_info(dev, "switch 0x%x probed: %s, revision %u\n", + prod_num, ps->info->name, rev); + + return 0; +} + +static void mv88e6xxx_remove(struct mdio_device *mdiodev) +{ + struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + put_device(&ps->bus->dev); } +static const struct of_device_id mv88e6xxx_of_match[] = { + { .compatible = "marvell,mv88e6085" }, + { /* sentinel */ }, +}; + +MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match); + +static struct mdio_driver mv88e6xxx_driver = { + .probe = mv88e6xxx_probe, + .remove = mv88e6xxx_remove, + .mdiodrv.driver = { + .name = "mv88e6085", + .of_match_table = mv88e6xxx_of_match, + }, +}; + static int __init mv88e6xxx_init(void) { -#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131) - register_switch_driver(&mv88e6131_switch_driver); -#endif -#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123) - register_switch_driver(&mv88e6123_switch_driver); -#endif -#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352) - register_switch_driver(&mv88e6352_switch_driver); -#endif -#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171) - register_switch_driver(&mv88e6171_switch_driver); -#endif - return 0; + register_switch_driver(&mv88e6xxx_switch_driver); + return mdio_driver_register(&mv88e6xxx_driver); } module_init(mv88e6xxx_init); static void __exit mv88e6xxx_cleanup(void) { -#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171) - unregister_switch_driver(&mv88e6171_switch_driver); -#endif -#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352) - unregister_switch_driver(&mv88e6352_switch_driver); -#endif -#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123) - unregister_switch_driver(&mv88e6123_switch_driver); -#endif -#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131) - unregister_switch_driver(&mv88e6131_switch_driver); -#endif + mdio_driver_unregister(&mv88e6xxx_driver); + unregister_switch_driver(&mv88e6xxx_switch_driver); } module_exit(mv88e6xxx_cleanup); diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 26a424acd..36d0e1504 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h @@ -12,6 +12,7 @@ #define __MV88E6XXX_H #include +#include #ifndef UINT64_MAX #define UINT64_MAX (u64)(~((u64)0)) @@ -68,52 +69,23 @@ #define PORT_PCS_CTRL_UNFORCED 0x03 #define PORT_PAUSE_CTRL 0x02 #define PORT_SWITCH_ID 0x03 -#define PORT_SWITCH_ID_PROD_NUM_MASK 0xfff0 -#define PORT_SWITCH_ID_REV_MASK 0x000f -#define PORT_SWITCH_ID_6031 0x0310 -#define PORT_SWITCH_ID_6035 0x0350 -#define PORT_SWITCH_ID_6046 0x0480 -#define PORT_SWITCH_ID_6061 0x0610 -#define PORT_SWITCH_ID_6065 0x0650 -#define PORT_SWITCH_ID_6085 0x04a0 -#define PORT_SWITCH_ID_6092 0x0970 -#define PORT_SWITCH_ID_6095 0x0950 -#define PORT_SWITCH_ID_6096 0x0980 -#define PORT_SWITCH_ID_6097 0x0990 -#define PORT_SWITCH_ID_6108 0x1070 -#define PORT_SWITCH_ID_6121 0x1040 -#define PORT_SWITCH_ID_6122 0x1050 -#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_6175 0x1750 -#define PORT_SWITCH_ID_6176 0x1760 -#define PORT_SWITCH_ID_6182 0x1a60 -#define PORT_SWITCH_ID_6185 0x1a70 -#define PORT_SWITCH_ID_6240 0x2400 -#define PORT_SWITCH_ID_6320 0x1150 -#define PORT_SWITCH_ID_6320_A1 0x1151 -#define PORT_SWITCH_ID_6320_A2 0x1152 -#define PORT_SWITCH_ID_6321 0x3100 -#define PORT_SWITCH_ID_6321_A1 0x3101 -#define PORT_SWITCH_ID_6321_A2 0x3102 -#define PORT_SWITCH_ID_6350 0x3710 -#define PORT_SWITCH_ID_6351 0x3750 -#define PORT_SWITCH_ID_6352 0x3520 -#define PORT_SWITCH_ID_6352_A0 0x3521 -#define PORT_SWITCH_ID_6352_A1 0x3522 +#define PORT_SWITCH_ID_PROD_NUM_6085 0x04a +#define PORT_SWITCH_ID_PROD_NUM_6095 0x095 +#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_6240 0x240 +#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_CONTROL 0x04 #define PORT_CONTROL_USE_CORE_TAG BIT(15) #define PORT_CONTROL_DROP_ON_LOCK BIT(14) @@ -367,9 +339,187 @@ #define MV88E6XXX_N_FID 4096 -struct mv88e6xxx_switch_id { - u16 id; - char *name; +/* List of supported models */ +enum mv88e6xxx_model { + MV88E6085, + MV88E6095, + MV88E6123, + MV88E6131, + MV88E6161, + MV88E6165, + MV88E6171, + MV88E6172, + MV88E6175, + MV88E6176, + MV88E6185, + MV88E6240, + MV88E6320, + MV88E6321, + MV88E6350, + MV88E6351, + MV88E6352, +}; + +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 */ +}; + +enum mv88e6xxx_cap { + /* Address Translation Unit. + * The ATU is used to lookup and learn MAC addresses. See GLOBAL_ATU_OP. + */ + MV88E6XXX_CAP_ATU, + + /* Energy Efficient Ethernet. + */ + MV88E6XXX_CAP_EEE, + + /* EEPROM Command and Data registers. + * See GLOBAL2_EEPROM_OP and GLOBAL2_EEPROM_DATA. + */ + MV88E6XXX_CAP_EEPROM, + + /* Port State Filtering for 802.1D Spanning Tree. + * See PORT_CONTROL_STATE_* values in the PORT_CONTROL register. + */ + MV88E6XXX_CAP_PORTSTATE, + + /* PHY Polling Unit. + * See GLOBAL_CONTROL_PPU_ENABLE and GLOBAL_STATUS_PPU_POLLING. + */ + MV88E6XXX_CAP_PPU, + MV88E6XXX_CAP_PPU_ACTIVE, + + /* SMI PHY Command and Data registers. + * This requires an indirect access to PHY registers through + * GLOBAL2_SMI_OP, otherwise direct access to PHY registers is done. + */ + MV88E6XXX_CAP_SMI_PHY, + + /* 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, + + /* Switch MAC/WoL/WoF register. + * This requires an indirect access to set the switch MAC address + * through GLOBAL2_SWITCH_MAC, otherwise GLOBAL_MAC_01, GLOBAL_MAC_23, + * and GLOBAL_MAC_45 are used with a direct access. + */ + MV88E6XXX_CAP_SWITCH_MAC_WOL_WOF, + + /* Internal temperature sensor. + * Available from any enabled port's PHY register 26, page 6. + */ + MV88E6XXX_CAP_TEMP, + MV88E6XXX_CAP_TEMP_LIMIT, + + /* In-chip Port Based VLANs. + * Each port VLANTable register (see PORT_BASE_VLAN) is used to restrict + * the output (or egress) ports to which it is allowed to send frames. + */ + MV88E6XXX_CAP_VLANTABLE, + + /* 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_ATU BIT(MV88E6XXX_CAP_ATU) +#define MV88E6XXX_FLAG_EEE BIT(MV88E6XXX_CAP_EEE) +#define MV88E6XXX_FLAG_EEPROM BIT(MV88E6XXX_CAP_EEPROM) +#define MV88E6XXX_FLAG_PORTSTATE BIT(MV88E6XXX_CAP_PORTSTATE) +#define MV88E6XXX_FLAG_PPU BIT(MV88E6XXX_CAP_PPU) +#define MV88E6XXX_FLAG_PPU_ACTIVE BIT(MV88E6XXX_CAP_PPU_ACTIVE) +#define MV88E6XXX_FLAG_SMI_PHY BIT(MV88E6XXX_CAP_SMI_PHY) +#define MV88E6XXX_FLAG_STU BIT(MV88E6XXX_CAP_STU) +#define MV88E6XXX_FLAG_SWITCH_MAC BIT(MV88E6XXX_CAP_SWITCH_MAC_WOL_WOF) +#define MV88E6XXX_FLAG_TEMP BIT(MV88E6XXX_CAP_TEMP) +#define MV88E6XXX_FLAG_TEMP_LIMIT BIT(MV88E6XXX_CAP_TEMP_LIMIT) +#define MV88E6XXX_FLAG_VLANTABLE BIT(MV88E6XXX_CAP_VLANTABLE) +#define MV88E6XXX_FLAG_VTU BIT(MV88E6XXX_CAP_VTU) + +#define MV88E6XXX_FLAGS_FAMILY_6095 \ + (MV88E6XXX_FLAG_ATU | \ + MV88E6XXX_FLAG_PPU | \ + MV88E6XXX_FLAG_VLANTABLE | \ + MV88E6XXX_FLAG_VTU) + +#define MV88E6XXX_FLAGS_FAMILY_6097 \ + (MV88E6XXX_FLAG_ATU | \ + MV88E6XXX_FLAG_PPU | \ + MV88E6XXX_FLAG_STU | \ + MV88E6XXX_FLAG_VLANTABLE | \ + MV88E6XXX_FLAG_VTU) + +#define MV88E6XXX_FLAGS_FAMILY_6165 \ + (MV88E6XXX_FLAG_STU | \ + MV88E6XXX_FLAG_SWITCH_MAC | \ + MV88E6XXX_FLAG_TEMP | \ + MV88E6XXX_FLAG_VTU) + +#define MV88E6XXX_FLAGS_FAMILY_6185 \ + (MV88E6XXX_FLAG_ATU | \ + MV88E6XXX_FLAG_PPU | \ + MV88E6XXX_FLAG_VLANTABLE | \ + MV88E6XXX_FLAG_VTU) + +#define MV88E6XXX_FLAGS_FAMILY_6320 \ + (MV88E6XXX_FLAG_ATU | \ + MV88E6XXX_FLAG_EEE | \ + MV88E6XXX_FLAG_EEPROM | \ + MV88E6XXX_FLAG_PORTSTATE | \ + MV88E6XXX_FLAG_PPU_ACTIVE | \ + MV88E6XXX_FLAG_SMI_PHY | \ + MV88E6XXX_FLAG_SWITCH_MAC | \ + MV88E6XXX_FLAG_TEMP | \ + MV88E6XXX_FLAG_TEMP_LIMIT | \ + MV88E6XXX_FLAG_VLANTABLE | \ + MV88E6XXX_FLAG_VTU) + +#define MV88E6XXX_FLAGS_FAMILY_6351 \ + (MV88E6XXX_FLAG_ATU | \ + MV88E6XXX_FLAG_PORTSTATE | \ + MV88E6XXX_FLAG_PPU_ACTIVE | \ + MV88E6XXX_FLAG_SMI_PHY | \ + MV88E6XXX_FLAG_STU | \ + MV88E6XXX_FLAG_SWITCH_MAC | \ + MV88E6XXX_FLAG_TEMP | \ + MV88E6XXX_FLAG_VLANTABLE | \ + MV88E6XXX_FLAG_VTU) + +#define MV88E6XXX_FLAGS_FAMILY_6352 \ + (MV88E6XXX_FLAG_ATU | \ + MV88E6XXX_FLAG_EEE | \ + MV88E6XXX_FLAG_EEPROM | \ + MV88E6XXX_FLAG_PORTSTATE | \ + MV88E6XXX_FLAG_PPU_ACTIVE | \ + MV88E6XXX_FLAG_SMI_PHY | \ + MV88E6XXX_FLAG_STU | \ + MV88E6XXX_FLAG_SWITCH_MAC | \ + MV88E6XXX_FLAG_TEMP | \ + MV88E6XXX_FLAG_TEMP_LIMIT | \ + MV88E6XXX_FLAG_VLANTABLE | \ + MV88E6XXX_FLAG_VTU) + +struct mv88e6xxx_info { + enum mv88e6xxx_family family; + u16 prod_num; + const char *name; + unsigned int num_databases; + unsigned int num_ports; + unsigned long flags; }; struct mv88e6xxx_atu_entry { @@ -393,17 +543,29 @@ struct mv88e6xxx_vtu_stu_entry { struct mv88e6xxx_priv_port { struct net_device *bridge_dev; - u8 state; }; struct mv88e6xxx_priv_state { + 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; + /* When using multi-chip addressing, this mutex protects * access to the indirect access registers. (In single-chip * mode, this mutex is effectively useless.) */ struct mutex smi_mutex; -#ifdef CONFIG_NET_DSA_MV88E6XXX_NEED_PPU + /* The MII bus and the address on the bus that is used to + * communication with the switch + */ + struct mii_bus *bus; + int sw_addr; + /* Handles automatic disabling and re-enabling of the PHY * polling unit. */ @@ -411,7 +573,6 @@ struct mv88e6xxx_priv_state { int ppu_disabled; struct work_struct ppu_work; struct timer_list ppu_timer; -#endif /* This mutex serialises access to the statistics unit. * Hold this mutex over snapshot + dump sequences. @@ -429,14 +590,16 @@ struct mv88e6xxx_priv_state { */ struct mutex eeprom_mutex; - int id; /* switch product id */ - int num_ports; /* number of switch ports */ - struct mv88e6xxx_priv_port ports[DSA_MAX_PORTS]; - DECLARE_BITMAP(port_state_update_mask, 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; - struct work_struct bridge_work; + /* set to size of eeprom if supported by the switch */ + int eeprom_len; }; enum stat_type { @@ -452,104 +615,10 @@ struct mv88e6xxx_hw_stat { enum stat_type type; }; -int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active); -char *mv88e6xxx_lookup_name(struct device *host_dev, int sw_addr, - const struct mv88e6xxx_switch_id *table, - unsigned int num); -int mv88e6xxx_setup_ports(struct dsa_switch *ds); -int mv88e6xxx_setup_common(struct dsa_switch *ds); -int mv88e6xxx_setup_global(struct dsa_switch *ds); -int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg); -int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val); -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 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_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); -void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, - struct phy_device *phydev); -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_get_temp_limit(struct dsa_switch *ds, int *temp); -int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp); -int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm); -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_port_bridge_join(struct dsa_switch *ds, int port, - struct net_device *bridge); -void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port); -int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state); -int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, - bool vlan_filtering); -int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans); -int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans); -int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan); -int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_vlan *vlan, - int (*cb)(struct switchdev_obj *obj)); -int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans); -int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans); -int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb); -int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - int (*cb)(struct switchdev_obj *obj)); -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_switch_driver; -extern struct dsa_switch_driver mv88e6352_switch_driver; -extern struct dsa_switch_driver mv88e6171_switch_driver; - -#define REG_READ(addr, reg) \ - ({ \ - int __ret; \ - \ - __ret = mv88e6xxx_reg_read(ds, addr, reg); \ - if (__ret < 0) \ - return __ret; \ - __ret; \ - }) - -#define REG_WRITE(addr, reg, val) \ - ({ \ - int __ret; \ - \ - __ret = mv88e6xxx_reg_write(ds, addr, reg, val); \ - if (__ret < 0) \ - return __ret; \ - }) - - +static inline bool mv88e6xxx_has(struct mv88e6xxx_priv_state *ps, + unsigned long flags) +{ + return (ps->info->flags & flags) == flags; +} #endif -- cgit v1.2.3-54-g00ecf