From cf4ce59b3b70238352c7f1729f0f7223214828ad Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Fri, 20 Sep 2024 01:46:19 +0000 Subject: [PATCH] rtl88x2CE_WiFi_linux add concurrent mode --- kernel/drivers/net/dsa/mv88e6xxx/chip.c | 2353 ++++++++++++++++++++++++++++++++++++++-------------------- 1 files changed, 1,538 insertions(+), 815 deletions(-) diff --git a/kernel/drivers/net/dsa/mv88e6xxx/chip.c b/kernel/drivers/net/dsa/mv88e6xxx/chip.c index 1df7aed..53fbef9 100644 --- a/kernel/drivers/net/dsa/mv88e6xxx/chip.c +++ b/kernel/drivers/net/dsa/mv88e6xxx/chip.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Marvell 88e6xxx Ethernet switch single-chip support * @@ -7,13 +8,9 @@ * * Copyright (c) 2016-2017 Savoir-faire Linux Inc. * Vivien Didelot <vivien.didelot@savoirfairelinux.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ +#include <linux/bitfield.h> #include <linux/delay.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> @@ -31,11 +28,11 @@ #include <linux/platform_data/mv88e6xxx.h> #include <linux/netdevice.h> #include <linux/gpio/consumer.h> -#include <linux/phy.h> #include <linux/phylink.h> #include <net/dsa.h> #include "chip.h" +#include "devlink.h" #include "global1.h" #include "global2.h" #include "hwtstamp.h" @@ -43,6 +40,7 @@ #include "port.h" #include "ptp.h" #include "serdes.h" +#include "smi.h" static void assert_reg_lock(struct mv88e6xxx_chip *chip) { @@ -51,149 +49,6 @@ dump_stack(); } } - -/* The switch ADDR[4:1] configuration pins define the chip SMI device address - * (ADDR[0] is always zero, thus only even SMI addresses can be strapped). - * - * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it - * is the only device connected to the SMI master. In this mode it responds to - * all 32 possible SMI addresses, and thus maps directly the internal devices. - * - * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing - * multiple devices to share the SMI interface. In this mode it responds to only - * 2 registers, used to indirectly access the internal SMI devices. - */ - -static int mv88e6xxx_smi_read(struct mv88e6xxx_chip *chip, - int addr, int reg, u16 *val) -{ - if (!chip->smi_ops) - return -EOPNOTSUPP; - - return chip->smi_ops->read(chip, addr, reg, val); -} - -static int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip, - int addr, int reg, u16 val) -{ - if (!chip->smi_ops) - return -EOPNOTSUPP; - - return chip->smi_ops->write(chip, addr, reg, val); -} - -static int mv88e6xxx_smi_single_chip_read(struct mv88e6xxx_chip *chip, - int addr, int reg, u16 *val) -{ - int ret; - - ret = mdiobus_read_nested(chip->bus, addr, reg); - if (ret < 0) - return ret; - - *val = ret & 0xffff; - - return 0; -} - -static int mv88e6xxx_smi_single_chip_write(struct mv88e6xxx_chip *chip, - int addr, int reg, u16 val) -{ - int ret; - - ret = mdiobus_write_nested(chip->bus, addr, reg, val); - if (ret < 0) - return ret; - - return 0; -} - -static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_single_chip_ops = { - .read = mv88e6xxx_smi_single_chip_read, - .write = mv88e6xxx_smi_single_chip_write, -}; - -static int mv88e6xxx_smi_multi_chip_wait(struct mv88e6xxx_chip *chip) -{ - int ret; - int i; - - for (i = 0; i < 16; i++) { - ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_CMD); - if (ret < 0) - return ret; - - if ((ret & SMI_CMD_BUSY) == 0) - return 0; - } - - return -ETIMEDOUT; -} - -static int mv88e6xxx_smi_multi_chip_read(struct mv88e6xxx_chip *chip, - int addr, int reg, u16 *val) -{ - int ret; - - /* Wait for the bus to become free. */ - ret = mv88e6xxx_smi_multi_chip_wait(chip); - if (ret < 0) - return ret; - - /* Transmit the read command. */ - ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD, - SMI_CMD_OP_22_READ | (addr << 5) | reg); - if (ret < 0) - return ret; - - /* Wait for the read command to complete. */ - ret = mv88e6xxx_smi_multi_chip_wait(chip); - if (ret < 0) - return ret; - - /* Read the data. */ - ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_DATA); - if (ret < 0) - return ret; - - *val = ret & 0xffff; - - return 0; -} - -static int mv88e6xxx_smi_multi_chip_write(struct mv88e6xxx_chip *chip, - int addr, int reg, u16 val) -{ - int ret; - - /* Wait for the bus to become free. */ - ret = mv88e6xxx_smi_multi_chip_wait(chip); - if (ret < 0) - return ret; - - /* Transmit the data to write. */ - ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_DATA, val); - if (ret < 0) - return ret; - - /* Transmit the write command. */ - ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD, - SMI_CMD_OP_22_WRITE | (addr << 5) | reg); - if (ret < 0) - return ret; - - /* Wait for the write command to complete. */ - ret = mv88e6xxx_smi_multi_chip_wait(chip); - if (ret < 0) - return ret; - - return 0; -} - -static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_multi_chip_ops = { - .read = mv88e6xxx_smi_multi_chip_read, - .write = mv88e6xxx_smi_multi_chip_write, -}; int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val) { @@ -225,6 +80,36 @@ addr, reg, val); return 0; +} + +int mv88e6xxx_wait_mask(struct mv88e6xxx_chip *chip, int addr, int reg, + u16 mask, u16 val) +{ + u16 data; + int err; + int i; + + /* There's no bus specific operation to wait for a mask */ + for (i = 0; i < 16; i++) { + err = mv88e6xxx_read(chip, addr, reg, &data); + if (err) + return err; + + if ((data & mask) == val) + return 0; + + usleep_range(1000, 2000); + } + + dev_err(chip->dev, "Timeout while waiting for switch\n"); + return -ETIMEDOUT; +} + +int mv88e6xxx_wait_bit(struct mv88e6xxx_chip *chip, int addr, int reg, + int bit, int val) +{ + return mv88e6xxx_wait_mask(chip, addr, reg, BIT(bit), + val ? BIT(bit) : 0x0000); } struct mii_bus *mv88e6xxx_default_mdio_bus(struct mv88e6xxx_chip *chip) @@ -264,9 +149,9 @@ u16 ctl1; int err; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, ®); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); if (err) goto out; @@ -281,13 +166,13 @@ } } - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_CTL1, &ctl1); if (err) goto unlock; err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, ®); unlock: - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); if (err) goto out; ctl1 &= GENMASK(chip->g1_irq.nirqs, 0); @@ -308,7 +193,7 @@ { struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); } static void mv88e6xxx_g1_irq_bus_sync_unlock(struct irq_data *d) @@ -330,7 +215,7 @@ goto out; out: - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); } static const struct irq_chip mv88e6xxx_g1_irq_chip = { @@ -385,9 +270,9 @@ */ free_irq(chip->irq, chip); - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); mv88e6xxx_g1_irq_free_common(chip); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); } static int mv88e6xxx_g1_irq_setup_common(struct mv88e6xxx_chip *chip) @@ -456,12 +341,15 @@ */ irq_set_lockdep_class(chip->irq, &lock_key, &request_key); - mutex_unlock(&chip->reg_lock); + snprintf(chip->irq_name, sizeof(chip->irq_name), + "mv88e6xxx-%s", dev_name(chip->dev)); + + mv88e6xxx_reg_unlock(chip); err = request_threaded_irq(chip->irq, NULL, mv88e6xxx_g1_irq_thread_fn, - IRQF_ONESHOT, - dev_name(chip->dev), chip); - mutex_lock(&chip->reg_lock); + IRQF_ONESHOT | IRQF_SHARED, + chip->irq_name, chip); + mv88e6xxx_reg_lock(chip); if (err) mv88e6xxx_g1_irq_free_common(chip); @@ -490,7 +378,7 @@ kthread_init_delayed_work(&chip->irq_poll_work, mv88e6xxx_irq_poll); - chip->kworker = kthread_create_worker(0, dev_name(chip->dev)); + chip->kworker = kthread_create_worker(0, "%s", dev_name(chip->dev)); if (IS_ERR(chip->kworker)) return PTR_ERR(chip->kworker); @@ -505,48 +393,31 @@ kthread_cancel_delayed_work_sync(&chip->irq_poll_work); kthread_destroy_worker(chip->kworker); - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); mv88e6xxx_g1_irq_free_common(chip); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); } -int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask) +static int mv88e6xxx_port_config_interface(struct mv88e6xxx_chip *chip, + int port, phy_interface_t interface) { - int i; - - for (i = 0; i < 16; i++) { - u16 val; - int err; - - err = mv88e6xxx_read(chip, addr, reg, &val); - if (err) - return err; - - if (!(val & mask)) - return 0; - - usleep_range(1000, 2000); - } - - dev_err(chip->dev, "Timeout while waiting for switch\n"); - return -ETIMEDOUT; -} - -/* Indirect write to single pointer-data register with an Update bit */ -int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, u16 update) -{ - u16 val; int err; - /* Wait until the previous operation is completed */ - err = mv88e6xxx_wait(chip, addr, reg, BIT(15)); - if (err) - return err; + if (chip->info->ops->port_set_rgmii_delay) { + err = chip->info->ops->port_set_rgmii_delay(chip, port, + interface); + if (err && err != -EOPNOTSUPP) + return err; + } - /* Set the Update bit to trigger a write operation */ - val = BIT(15) | update; + if (chip->info->ops->port_set_cmode) { + err = chip->info->ops->port_set_cmode(chip, port, + interface); + if (err && err != -EOPNOTSUPP) + return err; + } - return mv88e6xxx_write(chip, addr, reg, val); + return 0; } static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, @@ -559,15 +430,19 @@ return 0; /* Port's MAC control must not be changed unless the link is down */ - err = chip->info->ops->port_set_link(chip, port, 0); + err = chip->info->ops->port_set_link(chip, port, LINK_FORCED_DOWN); if (err) return err; - if (chip->info->ops->port_set_speed) { - err = chip->info->ops->port_set_speed(chip, port, speed); + if (chip->info->ops->port_set_speed_duplex) { + err = chip->info->ops->port_set_speed_duplex(chip, port, + speed, duplex); if (err && err != -EOPNOTSUPP) goto restore_link; } + + if (speed == SPEED_MAX && chip->info->ops->port_max_speed_mode) + mode = chip->info->ops->port_max_speed_mode(port); if (chip->info->ops->port_set_pause) { err = chip->info->ops->port_set_pause(chip, port, pause); @@ -575,25 +450,7 @@ goto restore_link; } - if (chip->info->ops->port_set_duplex) { - err = chip->info->ops->port_set_duplex(chip, port, duplex); - if (err && err != -EOPNOTSUPP) - goto restore_link; - } - - if (chip->info->ops->port_set_rgmii_delay) { - err = chip->info->ops->port_set_rgmii_delay(chip, port, mode); - if (err && err != -EOPNOTSUPP) - goto restore_link; - } - - if (chip->info->ops->port_set_cmode) { - err = chip->info->ops->port_set_cmode(chip, port, mode); - if (err && err != -EOPNOTSUPP) - goto restore_link; - } - - err = 0; + err = mv88e6xxx_port_config_interface(chip, port, mode); restore_link: if (chip->info->ops->port_set_link(chip, port, link)) dev_err(chip->dev, "p%d: failed to restore MAC's link\n", port); @@ -601,27 +458,102 @@ return err; } -/* We expect the switch to perform auto negotiation if there is a real - * phy. However, in the case of a fixed link phy, we force the port - * settings from the fixed link settings. - */ -static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, - struct phy_device *phydev) +static int mv88e6xxx_phy_is_internal(struct dsa_switch *ds, int port) { struct mv88e6xxx_chip *chip = ds->priv; + + return port < chip->info->num_internal_phys; +} + +static int mv88e6xxx_port_ppu_updates(struct mv88e6xxx_chip *chip, int port) +{ + u16 reg; int err; - if (!phy_is_pseudo_fixed_link(phydev)) - return; + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); + if (err) { + dev_err(chip->dev, + "p%d: %s: failed to read port status\n", + port, __func__); + return err; + } - mutex_lock(&chip->reg_lock); - err = mv88e6xxx_port_setup_mac(chip, port, phydev->link, phydev->speed, - phydev->duplex, phydev->pause, - phydev->interface); - mutex_unlock(&chip->reg_lock); + return !!(reg & MV88E6XXX_PORT_STS_PHY_DETECT); +} - if (err && err != -EOPNOTSUPP) - dev_err(ds->dev, "p%d: failed to configure MAC\n", port); +static int mv88e6xxx_serdes_pcs_get_state(struct dsa_switch *ds, int port, + struct phylink_link_state *state) +{ + struct mv88e6xxx_chip *chip = ds->priv; + u8 lane; + int err; + + mv88e6xxx_reg_lock(chip); + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane && chip->info->ops->serdes_pcs_get_state) + err = chip->info->ops->serdes_pcs_get_state(chip, port, lane, + state); + else + err = -EOPNOTSUPP; + mv88e6xxx_reg_unlock(chip); + + return err; +} + +static int mv88e6xxx_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, + unsigned int mode, + phy_interface_t interface, + const unsigned long *advertise) +{ + const struct mv88e6xxx_ops *ops = chip->info->ops; + u8 lane; + + if (ops->serdes_pcs_config) { + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane) + return ops->serdes_pcs_config(chip, port, lane, mode, + interface, advertise); + } + + return 0; +} + +static void mv88e6xxx_serdes_pcs_an_restart(struct dsa_switch *ds, int port) +{ + struct mv88e6xxx_chip *chip = ds->priv; + const struct mv88e6xxx_ops *ops; + int err = 0; + u8 lane; + + ops = chip->info->ops; + + if (ops->serdes_pcs_an_restart) { + mv88e6xxx_reg_lock(chip); + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane) + err = ops->serdes_pcs_an_restart(chip, port, lane); + mv88e6xxx_reg_unlock(chip); + + if (err) + dev_err(ds->dev, "p%d: failed to restart AN\n", port); + } +} + +static int mv88e6xxx_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, + unsigned int mode, + int speed, int duplex) +{ + const struct mv88e6xxx_ops *ops = chip->info->ops; + u8 lane; + + if (!phylink_autoneg_inband(mode) && ops->serdes_pcs_link_up) { + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane) + return ops->serdes_pcs_link_up(chip, port, lane, + speed, duplex); + } + + return 0; } static void mv88e6065_phylink_validate(struct mv88e6xxx_chip *chip, int port, @@ -650,6 +582,20 @@ mv88e6065_phylink_validate(chip, port, mask, state); } +static void mv88e6341_phylink_validate(struct mv88e6xxx_chip *chip, int port, + unsigned long *mask, + struct phylink_link_state *state) +{ + if (port >= 5) + phylink_set(mask, 2500baseX_Full); + + /* No ethtool bits for 200Mbps */ + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + + mv88e6065_phylink_validate(chip, port, mask, state); +} + static void mv88e6352_phylink_validate(struct mv88e6xxx_chip *chip, int port, unsigned long *mask, struct phylink_link_state *state) @@ -665,8 +611,10 @@ unsigned long *mask, struct phylink_link_state *state) { - if (port >= 9) + if (port >= 9) { phylink_set(mask, 2500baseX_Full); + phylink_set(mask, 2500baseT_Full); + } /* No ethtool bits for 200Mbps */ phylink_set(mask, 1000baseT_Full); @@ -712,79 +660,136 @@ phylink_helper_basex_speed(state); } -static int mv88e6xxx_link_state(struct dsa_switch *ds, int port, - struct phylink_link_state *state) -{ - struct mv88e6xxx_chip *chip = ds->priv; - int err; - - mutex_lock(&chip->reg_lock); - if (chip->info->ops->port_link_state) - err = chip->info->ops->port_link_state(chip, port, state); - else - err = -EOPNOTSUPP; - mutex_unlock(&chip->reg_lock); - - return err; -} - static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port, unsigned int mode, const struct phylink_link_state *state) { struct mv88e6xxx_chip *chip = ds->priv; - int speed, duplex, link, pause, err; + struct mv88e6xxx_port *p; + int err = 0; - if (mode == MLO_AN_PHY) - return; + p = &chip->ports[port]; - if (mode == MLO_AN_FIXED) { - link = LINK_FORCED_UP; - speed = state->speed; - duplex = state->duplex; - } else { - speed = SPEED_UNFORCED; - duplex = DUPLEX_UNFORCED; - link = LINK_UNFORCED; + mv88e6xxx_reg_lock(chip); + + if (mode != MLO_AN_PHY || !mv88e6xxx_phy_is_internal(ds, port)) { + /* In inband mode, the link may come up at any time while the + * link is not forced down. Force the link down while we + * reconfigure the interface mode. + */ + if (mode == MLO_AN_INBAND && + p->interface != state->interface && + chip->info->ops->port_set_link) + chip->info->ops->port_set_link(chip, port, + LINK_FORCED_DOWN); + + err = mv88e6xxx_port_config_interface(chip, port, + state->interface); + if (err && err != -EOPNOTSUPP) + goto err_unlock; + + err = mv88e6xxx_serdes_pcs_config(chip, port, mode, + state->interface, + state->advertising); + /* FIXME: we should restart negotiation if something changed - + * which is something we get if we convert to using phylinks + * PCS operations. + */ + if (err > 0) + err = 0; } - pause = !!phylink_test(state->advertising, Pause); - mutex_lock(&chip->reg_lock); - err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex, pause, - state->interface); - mutex_unlock(&chip->reg_lock); + /* Undo the forced down state above after completing configuration + * irrespective of its state on entry, which allows the link to come + * up in the in-band case where there is no separate SERDES. Also + * ensure that the link can come up if the PPU is in use and we are + * in PHY mode (we treat the PPU as an effective in-band mechanism.) + */ + if (chip->info->ops->port_set_link && + ((mode == MLO_AN_INBAND && p->interface != state->interface) || + (mode == MLO_AN_PHY && mv88e6xxx_port_ppu_updates(chip, port)))) + chip->info->ops->port_set_link(chip, port, LINK_UNFORCED); + + p->interface = state->interface; + +err_unlock: + mv88e6xxx_reg_unlock(chip); if (err && err != -EOPNOTSUPP) - dev_err(ds->dev, "p%d: failed to configure MAC\n", port); -} - -static void mv88e6xxx_mac_link_force(struct dsa_switch *ds, int port, int link) -{ - struct mv88e6xxx_chip *chip = ds->priv; - int err; - - mutex_lock(&chip->reg_lock); - err = chip->info->ops->port_set_link(chip, port, link); - mutex_unlock(&chip->reg_lock); - - if (err) - dev_err(chip->dev, "p%d: failed to force MAC link\n", port); + dev_err(ds->dev, "p%d: failed to configure MAC/PCS\n", port); } static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface) { - if (mode == MLO_AN_FIXED) - mv88e6xxx_mac_link_force(ds, port, LINK_FORCED_DOWN); + struct mv88e6xxx_chip *chip = ds->priv; + const struct mv88e6xxx_ops *ops; + int err = 0; + + ops = chip->info->ops; + + mv88e6xxx_reg_lock(chip); + /* Internal PHYs propagate their configuration directly to the MAC. + * External PHYs depend on whether the PPU is enabled for this port. + */ + if (((!mv88e6xxx_phy_is_internal(ds, port) && + !mv88e6xxx_port_ppu_updates(chip, port)) || + mode == MLO_AN_FIXED) && ops->port_set_link) + err = ops->port_set_link(chip, port, LINK_FORCED_DOWN); + mv88e6xxx_reg_unlock(chip); + + if (err) + dev_err(chip->dev, + "p%d: failed to force MAC link down\n", port); } static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { - if (mode == MLO_AN_FIXED) - mv88e6xxx_mac_link_force(ds, port, LINK_FORCED_UP); + struct mv88e6xxx_chip *chip = ds->priv; + const struct mv88e6xxx_ops *ops; + int err = 0; + + ops = chip->info->ops; + + mv88e6xxx_reg_lock(chip); + /* Internal PHYs propagate their configuration directly to the MAC. + * External PHYs depend on whether the PPU is enabled for this port. + */ + if ((!mv88e6xxx_phy_is_internal(ds, port) && + !mv88e6xxx_port_ppu_updates(chip, port)) || + mode == MLO_AN_FIXED) { + /* FIXME: for an automedia port, should we force the link + * down here - what if the link comes up due to "other" media + * while we're bringing the port up, how is the exclusivity + * handled in the Marvell hardware? E.g. port 2 on 88E6390 + * shared between internal PHY and Serdes. + */ + err = mv88e6xxx_serdes_pcs_link_up(chip, port, mode, speed, + duplex); + if (err) + goto error; + + if (ops->port_set_speed_duplex) { + err = ops->port_set_speed_duplex(chip, port, + speed, duplex); + if (err && err != -EOPNOTSUPP) + goto error; + } + + if (ops->port_set_link) + err = ops->port_set_link(chip, port, LINK_FORCED_UP); + } +error: + mv88e6xxx_reg_unlock(chip); + + if (err && err != -EOPNOTSUPP) + dev_err(ds->dev, + "p%d: failed to configure MAC link up\n", port); } static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port) @@ -884,7 +889,7 @@ break; case STATS_TYPE_BANK1: reg = bank1_select; - /* fall through */ + fallthrough; case STATS_TYPE_BANK0: reg |= s->reg | histogram; mv88e6xxx_g1_stats_read(chip, reg, &low); @@ -923,6 +928,12 @@ STATS_TYPE_BANK0 | STATS_TYPE_PORT); } +static int mv88e6250_stats_get_strings(struct mv88e6xxx_chip *chip, + uint8_t *data) +{ + return mv88e6xxx_stats_get_strings(chip, data, STATS_TYPE_BANK0); +} + static int mv88e6320_stats_get_strings(struct mv88e6xxx_chip *chip, uint8_t *data) { @@ -957,7 +968,7 @@ if (stringset != ETH_SS_STATS) return; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); if (chip->info->ops->stats_get_strings) count = chip->info->ops->stats_get_strings(chip, data); @@ -970,7 +981,7 @@ data += count * ETH_GSTRING_LEN; mv88e6xxx_atu_vtu_get_strings(data); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); } static int mv88e6xxx_stats_get_sset_count(struct mv88e6xxx_chip *chip, @@ -993,6 +1004,11 @@ STATS_TYPE_PORT); } +static int mv88e6250_stats_get_sset_count(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_stats_get_sset_count(chip, STATS_TYPE_BANK0); +} + static int mv88e6320_stats_get_sset_count(struct mv88e6xxx_chip *chip) { return mv88e6xxx_stats_get_sset_count(chip, STATS_TYPE_BANK0 | @@ -1008,7 +1024,7 @@ if (sset != ETH_SS_STATS) return 0; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); if (chip->info->ops->stats_get_sset_count) count = chip->info->ops->stats_get_sset_count(chip); if (count < 0) @@ -1025,7 +1041,7 @@ count += ARRAY_SIZE(mv88e6xxx_atu_vtu_stats_strings); out: - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); return count; } @@ -1040,11 +1056,11 @@ for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) { stat = &mv88e6xxx_hw_stats[i]; if (stat->type & types) { - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port, bank1_select, histogram); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); j++; } @@ -1057,6 +1073,13 @@ { return mv88e6xxx_stats_get_stats(chip, port, data, STATS_TYPE_BANK0 | STATS_TYPE_PORT, + 0, MV88E6XXX_G1_STATS_OP_HIST_RX_TX); +} + +static int mv88e6250_stats_get_stats(struct mv88e6xxx_chip *chip, int port, + uint64_t *data) +{ + return mv88e6xxx_stats_get_stats(chip, port, data, STATS_TYPE_BANK0, 0, MV88E6XXX_G1_STATS_OP_HIST_RX_TX); } @@ -1096,14 +1119,14 @@ if (chip->info->ops->stats_get_stats) count = chip->info->ops->stats_get_stats(chip, port, data); - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); if (chip->info->ops->serdes_get_stats) { data += count; count = chip->info->ops->serdes_get_stats(chip, port, data); } data += count; mv88e6xxx_atu_vtu_get_stats(chip, port, data); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); } static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, @@ -1112,10 +1135,10 @@ struct mv88e6xxx_chip *chip = ds->priv; int ret; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); ret = mv88e6xxx_stats_snapshot(chip, port); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); if (ret < 0) return; @@ -1126,7 +1149,14 @@ static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port) { - return 32 * sizeof(u16); + struct mv88e6xxx_chip *chip = ds->priv; + int len; + + len = 32 * sizeof(u16); + if (chip->info->ops->serdes_get_regs_len) + len += chip->info->ops->serdes_get_regs_len(chip, port); + + return len; } static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, @@ -1138,11 +1168,11 @@ u16 *p = _p; int i; - regs->version = 0; + regs->version = chip->info->prod_num; memset(p, 0xff, 32 * sizeof(u16)); - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); for (i = 0; i < 32; i++) { @@ -1151,7 +1181,10 @@ p[i] = reg; } - mutex_unlock(&chip->reg_lock); + if (chip->info->ops->serdes_get_regs) + chip->info->ops->serdes_get_regs(chip, port, &p[i]); + + mv88e6xxx_reg_unlock(chip); } static int mv88e6xxx_get_mac_eee(struct dsa_switch *ds, int port, @@ -1168,35 +1201,43 @@ return 0; } +/* Mask of the local ports allowed to receive frames from a given fabric port */ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port) { - struct dsa_switch *ds = NULL; + struct dsa_switch *ds = chip->ds; + struct dsa_switch_tree *dst = ds->dst; struct net_device *br; + struct dsa_port *dp; + bool found = false; u16 pvlan; - int i; - if (dev < DSA_MAX_SWITCHES) - ds = chip->ds->dst->ds[dev]; + list_for_each_entry(dp, &dst->ports, list) { + if (dp->ds->index == dev && dp->index == port) { + found = true; + break; + } + } /* Prevent frames from unknown switch or port */ - if (!ds || port >= ds->num_ports) + if (!found) return 0; /* Frames from DSA links and CPU ports can egress any local port */ - if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) + if (dp->type == DSA_PORT_TYPE_CPU || dp->type == DSA_PORT_TYPE_DSA) return mv88e6xxx_port_mask(chip); - br = ds->ports[port].bridge_dev; + br = dp->bridge_dev; pvlan = 0; /* Frames from user ports can egress any local DSA links and CPU ports, * as well as any local member of their bridge group. */ - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) - if (dsa_is_cpu_port(chip->ds, i) || - dsa_is_dsa_port(chip->ds, i) || - (br && dsa_to_port(chip->ds, i)->bridge_dev == br)) - pvlan |= BIT(i); + list_for_each_entry(dp, &dst->ports, list) + if (dp->ds == ds && + (dp->type == DSA_PORT_TYPE_CPU || + dp->type == DSA_PORT_TYPE_DSA || + (br && dp->bridge_dev == br))) + pvlan |= BIT(dp->index); return pvlan; } @@ -1217,9 +1258,9 @@ struct mv88e6xxx_chip *chip = ds->priv; int err; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_port_set_state(chip, port, state); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); if (err) dev_err(ds->dev, "p%d: failed to update state\n", port); @@ -1246,6 +1287,7 @@ static int mv88e6xxx_devmap_setup(struct mv88e6xxx_chip *chip) { + struct dsa_switch *ds = chip->ds; int target, port; int err; @@ -1254,10 +1296,9 @@ /* Initialize the routing port to the 32 possible target devices */ for (target = 0; target < 32; target++) { - port = 0x1f; - if (target < DSA_MAX_SWITCHES) - if (chip->ds->rtable[target] != DSA_RTABLE_NONE) - port = chip->ds->rtable[target]; + port = dsa_routing_port(ds, target); + if (port == ds->num_ports) + port = 0x1f; err = mv88e6xxx_g2_device_mapping_write(chip, target, port); if (err) @@ -1364,7 +1405,7 @@ u16 pvlan = 0; if (!mv88e6xxx_has_pvt(chip)) - return -EOPNOTSUPP; + return 0; /* Skip the local source device, which uses in-chip port VLAN */ if (dev != chip->ds->index) @@ -1404,9 +1445,9 @@ struct mv88e6xxx_chip *chip = ds->priv; int err; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_g1_atu_remove(chip, 0, port, false); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); if (err) dev_err(ds->dev, "p%d: failed to flush ATU\n", port); @@ -1438,26 +1479,27 @@ return chip->info->ops->vtu_loadpurge(chip, entry); } -static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) +int mv88e6xxx_fid_map(struct mv88e6xxx_chip *chip, unsigned long *fid_bitmap) { - DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); - struct mv88e6xxx_vtu_entry vlan = { - .vid = chip->info->max_vid, - }; + struct mv88e6xxx_vtu_entry vlan; int i, err; + u16 fid; bitmap_zero(fid_bitmap, MV88E6XXX_N_FID); /* Set every FID bit used by the (un)bridged ports */ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { - err = mv88e6xxx_port_get_fid(chip, i, fid); + err = mv88e6xxx_port_get_fid(chip, i, &fid); if (err) return err; - set_bit(*fid, fid_bitmap); + set_bit(fid, fid_bitmap); } /* Set every FID bit used by the VLAN entries */ + vlan.vid = chip->info->max_vid; + vlan.valid = false; + do { err = mv88e6xxx_vtu_getnext(chip, &vlan); if (err) @@ -1468,6 +1510,18 @@ set_bit(vlan.fid, fid_bitmap); } while (vlan.vid < chip->info->max_vid); + + return 0; +} + +static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) +{ + DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); + int err; + + err = mv88e6xxx_fid_map(chip, fid_bitmap); + if (err) + return err; /* The reset value 0x000 is used to indicate that multiple address * databases are not needed. Return the next positive available. @@ -1480,51 +1534,11 @@ return mv88e6xxx_g1_atu_flush(chip, *fid, true); } -static int mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid, - struct mv88e6xxx_vtu_entry *entry, bool new) -{ - int err; - - if (!vid) - return -EOPNOTSUPP; - - entry->vid = vid - 1; - entry->valid = false; - - err = mv88e6xxx_vtu_getnext(chip, entry); - if (err) - return err; - - if (entry->vid == vid && entry->valid) - return 0; - - if (new) { - int i; - - /* Initialize a fresh VLAN entry */ - memset(entry, 0, sizeof(*entry)); - entry->valid = true; - entry->vid = vid; - - /* Exclude all ports */ - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) - entry->member[i] = - MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER; - - return mv88e6xxx_atu_new(chip, &entry->fid); - } - - /* switchdev expects -EOPNOTSUPP to honor software VLANs */ - return -EOPNOTSUPP; -} - static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, u16 vid_begin, u16 vid_end) { struct mv88e6xxx_chip *chip = ds->priv; - struct mv88e6xxx_vtu_entry vlan = { - .vid = vid_begin - 1, - }; + struct mv88e6xxx_vtu_entry vlan; int i, err; /* DSA and CPU ports have to be members of multiple vlans */ @@ -1534,12 +1548,13 @@ if (!vid_begin) return -EOPNOTSUPP; - mutex_lock(&chip->reg_lock); + vlan.vid = vid_begin - 1; + vlan.valid = false; do { err = mv88e6xxx_vtu_getnext(chip, &vlan); if (err) - goto unlock; + return err; if (!vlan.valid) break; @@ -1551,7 +1566,7 @@ if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i)) continue; - if (!ds->ports[i].slave) + if (!dsa_to_port(ds, i)->slave) continue; if (vlan.member[i] == @@ -1559,7 +1574,7 @@ continue; if (dsa_to_port(ds, i)->bridge_dev == - ds->ports[port].bridge_dev) + dsa_to_port(ds, port)->bridge_dev) break; /* same bridge, check next VLAN */ if (!dsa_to_port(ds, i)->bridge_dev) @@ -1568,31 +1583,28 @@ dev_err(ds->dev, "p%d: hw VLAN %d already used by port %d in %s\n", port, vlan.vid, i, netdev_name(dsa_to_port(ds, i)->bridge_dev)); - err = -EOPNOTSUPP; - goto unlock; + return -EOPNOTSUPP; } } while (vlan.vid < vid_end); -unlock: - mutex_unlock(&chip->reg_lock); - - return err; + return 0; } static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, - bool vlan_filtering) + bool vlan_filtering, + struct switchdev_trans *trans) { struct mv88e6xxx_chip *chip = ds->priv; u16 mode = vlan_filtering ? MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE : MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED; int err; - if (!chip->info->max_vid) - return -EOPNOTSUPP; + if (switchdev_trans_ph_prepare(trans)) + return chip->info->max_vid ? 0 : -EOPNOTSUPP; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_port_set_8021q_mode(chip, port, mode); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); return err; } @@ -1610,53 +1622,65 @@ /* 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. */ + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin, vlan->vid_end); - if (err) - return err; + mv88e6xxx_reg_unlock(chip); /* We don't need any dynamic resource from the kernel (yet), * so skip the prepare phase. */ - return 0; + return err; } static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, const unsigned char *addr, u16 vid, u8 state) { - struct mv88e6xxx_vtu_entry vlan; struct mv88e6xxx_atu_entry entry; + struct mv88e6xxx_vtu_entry vlan; + u16 fid; int err; /* Null VLAN ID corresponds to the port private database */ - if (vid == 0) - err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid); - else - err = mv88e6xxx_vtu_get(chip, vid, &vlan, false); - if (err) - return err; + if (vid == 0) { + err = mv88e6xxx_port_get_fid(chip, port, &fid); + if (err) + return err; + } else { + vlan.vid = vid - 1; + vlan.valid = false; - entry.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED; + err = mv88e6xxx_vtu_getnext(chip, &vlan); + if (err) + return err; + + /* switchdev expects -EOPNOTSUPP to honor software VLANs */ + if (vlan.vid != vid || !vlan.valid) + return -EOPNOTSUPP; + + fid = vlan.fid; + } + + entry.state = 0; ether_addr_copy(entry.mac, addr); eth_addr_dec(entry.mac); - err = mv88e6xxx_g1_atu_getnext(chip, vlan.fid, &entry); + err = mv88e6xxx_g1_atu_getnext(chip, fid, &entry); if (err) return err; /* Initialize a fresh ATU entry if it isn't found */ - if (entry.state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED || - !ether_addr_equal(entry.mac, addr)) { + if (!entry.state || !ether_addr_equal(entry.mac, addr)) { memset(&entry, 0, sizeof(entry)); ether_addr_copy(entry.mac, addr); } /* Purge the ATU entry only if no port is using it anymore */ - if (state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) { + if (!state) { entry.portvec &= ~BIT(port); if (!entry.portvec) - entry.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED; + entry.state = 0; } else { if (state == MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC) entry.portvec = BIT(port); @@ -1666,7 +1690,217 @@ entry.state = state; } - return mv88e6xxx_g1_atu_loadpurge(chip, vlan.fid, &entry); + return mv88e6xxx_g1_atu_loadpurge(chip, fid, &entry); +} + +static int mv88e6xxx_policy_apply(struct mv88e6xxx_chip *chip, int port, + const struct mv88e6xxx_policy *policy) +{ + enum mv88e6xxx_policy_mapping mapping = policy->mapping; + enum mv88e6xxx_policy_action action = policy->action; + const u8 *addr = policy->addr; + u16 vid = policy->vid; + u8 state; + int err; + int id; + + if (!chip->info->ops->port_set_policy) + return -EOPNOTSUPP; + + switch (mapping) { + case MV88E6XXX_POLICY_MAPPING_DA: + case MV88E6XXX_POLICY_MAPPING_SA: + if (action == MV88E6XXX_POLICY_ACTION_NORMAL) + state = 0; /* Dissociate the port and address */ + else if (action == MV88E6XXX_POLICY_ACTION_DISCARD && + is_multicast_ether_addr(addr)) + state = MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_POLICY; + else if (action == MV88E6XXX_POLICY_ACTION_DISCARD && + is_unicast_ether_addr(addr)) + state = MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_POLICY; + else + return -EOPNOTSUPP; + + err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, + state); + if (err) + return err; + break; + default: + return -EOPNOTSUPP; + } + + /* Skip the port's policy clearing if the mapping is still in use */ + if (action == MV88E6XXX_POLICY_ACTION_NORMAL) + idr_for_each_entry(&chip->policies, policy, id) + if (policy->port == port && + policy->mapping == mapping && + policy->action != action) + return 0; + + return chip->info->ops->port_set_policy(chip, port, mapping, action); +} + +static int mv88e6xxx_policy_insert(struct mv88e6xxx_chip *chip, int port, + struct ethtool_rx_flow_spec *fs) +{ + struct ethhdr *mac_entry = &fs->h_u.ether_spec; + struct ethhdr *mac_mask = &fs->m_u.ether_spec; + enum mv88e6xxx_policy_mapping mapping; + enum mv88e6xxx_policy_action action; + struct mv88e6xxx_policy *policy; + u16 vid = 0; + u8 *addr; + int err; + int id; + + if (fs->location != RX_CLS_LOC_ANY) + return -EINVAL; + + if (fs->ring_cookie == RX_CLS_FLOW_DISC) + action = MV88E6XXX_POLICY_ACTION_DISCARD; + else + return -EOPNOTSUPP; + + switch (fs->flow_type & ~FLOW_EXT) { + case ETHER_FLOW: + if (!is_zero_ether_addr(mac_mask->h_dest) && + is_zero_ether_addr(mac_mask->h_source)) { + mapping = MV88E6XXX_POLICY_MAPPING_DA; + addr = mac_entry->h_dest; + } else if (is_zero_ether_addr(mac_mask->h_dest) && + !is_zero_ether_addr(mac_mask->h_source)) { + mapping = MV88E6XXX_POLICY_MAPPING_SA; + addr = mac_entry->h_source; + } else { + /* Cannot support DA and SA mapping in the same rule */ + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; + } + + if ((fs->flow_type & FLOW_EXT) && fs->m_ext.vlan_tci) { + if (fs->m_ext.vlan_tci != htons(0xffff)) + return -EOPNOTSUPP; + vid = be16_to_cpu(fs->h_ext.vlan_tci) & VLAN_VID_MASK; + } + + idr_for_each_entry(&chip->policies, policy, id) { + if (policy->port == port && policy->mapping == mapping && + policy->action == action && policy->vid == vid && + ether_addr_equal(policy->addr, addr)) + return -EEXIST; + } + + policy = devm_kzalloc(chip->dev, sizeof(*policy), GFP_KERNEL); + if (!policy) + return -ENOMEM; + + fs->location = 0; + err = idr_alloc_u32(&chip->policies, policy, &fs->location, 0xffffffff, + GFP_KERNEL); + if (err) { + devm_kfree(chip->dev, policy); + return err; + } + + memcpy(&policy->fs, fs, sizeof(*fs)); + ether_addr_copy(policy->addr, addr); + policy->mapping = mapping; + policy->action = action; + policy->port = port; + policy->vid = vid; + + err = mv88e6xxx_policy_apply(chip, port, policy); + if (err) { + idr_remove(&chip->policies, fs->location); + devm_kfree(chip->dev, policy); + return err; + } + + return 0; +} + +static int mv88e6xxx_get_rxnfc(struct dsa_switch *ds, int port, + struct ethtool_rxnfc *rxnfc, u32 *rule_locs) +{ + struct ethtool_rx_flow_spec *fs = &rxnfc->fs; + struct mv88e6xxx_chip *chip = ds->priv; + struct mv88e6xxx_policy *policy; + int err; + int id; + + mv88e6xxx_reg_lock(chip); + + switch (rxnfc->cmd) { + case ETHTOOL_GRXCLSRLCNT: + rxnfc->data = 0; + rxnfc->data |= RX_CLS_LOC_SPECIAL; + rxnfc->rule_cnt = 0; + idr_for_each_entry(&chip->policies, policy, id) + if (policy->port == port) + rxnfc->rule_cnt++; + err = 0; + break; + case ETHTOOL_GRXCLSRULE: + err = -ENOENT; + policy = idr_find(&chip->policies, fs->location); + if (policy) { + memcpy(fs, &policy->fs, sizeof(*fs)); + err = 0; + } + break; + case ETHTOOL_GRXCLSRLALL: + rxnfc->data = 0; + rxnfc->rule_cnt = 0; + idr_for_each_entry(&chip->policies, policy, id) + if (policy->port == port) + rule_locs[rxnfc->rule_cnt++] = id; + err = 0; + break; + default: + err = -EOPNOTSUPP; + break; + } + + mv88e6xxx_reg_unlock(chip); + + return err; +} + +static int mv88e6xxx_set_rxnfc(struct dsa_switch *ds, int port, + struct ethtool_rxnfc *rxnfc) +{ + struct ethtool_rx_flow_spec *fs = &rxnfc->fs; + struct mv88e6xxx_chip *chip = ds->priv; + struct mv88e6xxx_policy *policy; + int err; + + mv88e6xxx_reg_lock(chip); + + switch (rxnfc->cmd) { + case ETHTOOL_SRXCLSRLINS: + err = mv88e6xxx_policy_insert(chip, port, fs); + break; + case ETHTOOL_SRXCLSRLDEL: + err = -ENOENT; + policy = idr_remove(&chip->policies, fs->location); + if (policy) { + policy->action = MV88E6XXX_POLICY_ACTION_NORMAL; + err = mv88e6xxx_policy_apply(chip, port, policy); + devm_kfree(chip->dev, policy); + } + break; + default: + err = -EOPNOTSUPP; + break; + } + + mv88e6xxx_reg_unlock(chip); + + return err; } static int mv88e6xxx_port_add_broadcast(struct mv88e6xxx_chip *chip, int port, @@ -1692,23 +1926,58 @@ return 0; } -static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port, - u16 vid, u8 member) +static int mv88e6xxx_port_vlan_join(struct mv88e6xxx_chip *chip, int port, + u16 vid, u8 member, bool warn) { + const u8 non_member = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER; struct mv88e6xxx_vtu_entry vlan; - int err; + int i, err; - err = mv88e6xxx_vtu_get(chip, vid, &vlan, true); + if (!vid) + return -EOPNOTSUPP; + + vlan.vid = vid - 1; + vlan.valid = false; + + err = mv88e6xxx_vtu_getnext(chip, &vlan); if (err) return err; - vlan.member[port] = member; + if (vlan.vid != vid || !vlan.valid) { + memset(&vlan, 0, sizeof(vlan)); - err = mv88e6xxx_vtu_loadpurge(chip, &vlan); - if (err) - return err; + err = mv88e6xxx_atu_new(chip, &vlan.fid); + if (err) + return err; - return mv88e6xxx_broadcast_setup(chip, vid); + for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) + if (i == port) + vlan.member[i] = member; + else + vlan.member[i] = non_member; + + vlan.vid = vid; + vlan.valid = true; + + err = mv88e6xxx_vtu_loadpurge(chip, &vlan); + if (err) + return err; + + err = mv88e6xxx_broadcast_setup(chip, vlan.vid); + if (err) + return err; + } else if (vlan.member[port] != member) { + vlan.member[port] = member; + + err = mv88e6xxx_vtu_loadpurge(chip, &vlan); + if (err) + return err; + } else if (warn) { + dev_info(chip->dev, "p%d: already a member of VLAN %d\n", + port, vid); + } + + return 0; } static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, @@ -1717,6 +1986,7 @@ struct mv88e6xxx_chip *chip = ds->priv; bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; + bool warn; u8 member; u16 vid; @@ -1730,10 +2000,15 @@ else member = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_TAGGED; - mutex_lock(&chip->reg_lock); + /* net/dsa/slave.c will call dsa_port_vlan_add() for the affected port + * and then the CPU port. Do not warn for duplicates for the CPU port. + */ + warn = !dsa_is_cpu_port(ds, port) && !dsa_is_dsa_port(ds, port); + + mv88e6xxx_reg_lock(chip); for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) - if (_mv88e6xxx_port_vlan_add(chip, port, vid, member)) + if (mv88e6xxx_port_vlan_join(chip, port, vid, member, warn)) dev_err(ds->dev, "p%d: failed to add VLAN %d%c\n", port, vid, untagged ? 'u' : 't'); @@ -1741,21 +2016,30 @@ dev_err(ds->dev, "p%d: failed to set PVID %d\n", port, vlan->vid_end); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); } -static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip, - int port, u16 vid) +static int mv88e6xxx_port_vlan_leave(struct mv88e6xxx_chip *chip, + int port, u16 vid) { struct mv88e6xxx_vtu_entry vlan; int i, err; - err = mv88e6xxx_vtu_get(chip, vid, &vlan, false); + if (!vid) + return -EOPNOTSUPP; + + vlan.vid = vid - 1; + vlan.valid = false; + + err = mv88e6xxx_vtu_getnext(chip, &vlan); if (err) return err; - /* Tell switchdev if this VLAN is handled in software */ - if (vlan.member[port] == MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER) + /* If the VLAN doesn't exist in hardware or the port isn't a member, + * tell switchdev that this VLAN is likely handled in software. + */ + if (vlan.vid != vid || !vlan.valid || + vlan.member[port] == MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER) return -EOPNOTSUPP; vlan.member[port] = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER; @@ -1787,14 +2071,14 @@ if (!chip->info->max_vid) return -EOPNOTSUPP; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_port_get_pvid(chip, port, &pvid); if (err) goto unlock; for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { - err = _mv88e6xxx_port_vlan_del(chip, port, vid); + err = mv88e6xxx_port_vlan_leave(chip, port, vid); if (err) goto unlock; @@ -1806,7 +2090,7 @@ } unlock: - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); return err; } @@ -1817,10 +2101,10 @@ struct mv88e6xxx_chip *chip = ds->priv; int err; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); return err; } @@ -1831,10 +2115,9 @@ struct mv88e6xxx_chip *chip = ds->priv; int err; - mutex_lock(&chip->reg_lock); - err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, - MV88E6XXX_G1_ATU_DATA_STATE_UNUSED); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, 0); + mv88e6xxx_reg_unlock(chip); return err; } @@ -1847,17 +2130,15 @@ bool is_static; int err; - addr.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED; + addr.state = 0; eth_broadcast_addr(addr.mac); do { - mutex_lock(&chip->reg_lock); err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr); - mutex_unlock(&chip->reg_lock); if (err) return err; - if (addr.state == MV88E6XXX_G1_ATU_DATA_STATE_UNUSED) + if (!addr.state) break; if (addr.trunk || (addr.portvec & BIT(port)) == 0) @@ -1879,17 +2160,12 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, dsa_fdb_dump_cb_t *cb, void *data) { - struct mv88e6xxx_vtu_entry vlan = { - .vid = chip->info->max_vid, - }; + struct mv88e6xxx_vtu_entry vlan; u16 fid; int err; /* Dump port's default Filtering Information Database (VLAN ID 0) */ - mutex_lock(&chip->reg_lock); err = mv88e6xxx_port_get_fid(chip, port, &fid); - mutex_unlock(&chip->reg_lock); - if (err) return err; @@ -1898,10 +2174,11 @@ return err; /* Dump VLANs' Filtering Information Databases */ + vlan.vid = chip->info->max_vid; + vlan.valid = false; + do { - mutex_lock(&chip->reg_lock); err = mv88e6xxx_vtu_getnext(chip, &vlan); - mutex_unlock(&chip->reg_lock); if (err) return err; @@ -1921,39 +2198,38 @@ dsa_fdb_dump_cb_t *cb, void *data) { struct mv88e6xxx_chip *chip = ds->priv; + int err; - return mv88e6xxx_port_db_dump(chip, port, cb, data); + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_port_db_dump(chip, port, cb, data); + mv88e6xxx_reg_unlock(chip); + + return err; } static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip, struct net_device *br) { - struct dsa_switch *ds; - int port; - int dev; + struct dsa_switch *ds = chip->ds; + struct dsa_switch_tree *dst = ds->dst; + struct dsa_port *dp; int err; - /* Remap the Port VLAN of each local bridge group member */ - for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) { - if (chip->ds->ports[port].bridge_dev == br) { - err = mv88e6xxx_port_vlan_map(chip, port); - if (err) - return err; - } - } - - if (!mv88e6xxx_has_pvt(chip)) - return 0; - - /* Remap the Port VLAN of each cross-chip bridge group member */ - for (dev = 0; dev < DSA_MAX_SWITCHES; ++dev) { - ds = chip->ds->dst->ds[dev]; - if (!ds) - break; - - for (port = 0; port < ds->num_ports; ++port) { - if (ds->ports[port].bridge_dev == br) { - err = mv88e6xxx_pvt_map(chip, dev, port); + list_for_each_entry(dp, &dst->ports, list) { + if (dp->bridge_dev == br) { + if (dp->ds == ds) { + /* This is a local bridge group member, + * remap its Port VLAN Map. + */ + err = mv88e6xxx_port_vlan_map(chip, dp->index); + if (err) + return err; + } else { + /* This is an external bridge group member, + * remap its cross-chip Port VLAN Table entry. + */ + err = mv88e6xxx_pvt_map(chip, dp->ds->index, + dp->index); if (err) return err; } @@ -1969,9 +2245,9 @@ struct mv88e6xxx_chip *chip = ds->priv; int err; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_bridge_map(chip, br); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); return err; } @@ -1981,41 +2257,43 @@ { struct mv88e6xxx_chip *chip = ds->priv; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); if (mv88e6xxx_bridge_map(chip, br) || mv88e6xxx_port_vlan_map(chip, port)) dev_err(ds->dev, "failed to remap in-chip Port VLAN\n"); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); } -static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds, int dev, +static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds, + int tree_index, int sw_index, int port, struct net_device *br) { struct mv88e6xxx_chip *chip = ds->priv; int err; - if (!mv88e6xxx_has_pvt(chip)) + if (tree_index != ds->dst->index) return 0; - mutex_lock(&chip->reg_lock); - err = mv88e6xxx_pvt_map(chip, dev, port); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_pvt_map(chip, sw_index, port); + mv88e6xxx_reg_unlock(chip); return err; } -static void mv88e6xxx_crosschip_bridge_leave(struct dsa_switch *ds, int dev, +static void mv88e6xxx_crosschip_bridge_leave(struct dsa_switch *ds, + int tree_index, int sw_index, int port, struct net_device *br) { struct mv88e6xxx_chip *chip = ds->priv; - if (!mv88e6xxx_has_pvt(chip)) + if (tree_index != ds->dst->index) return; - mutex_lock(&chip->reg_lock); - if (mv88e6xxx_pvt_map(chip, dev, port)) + mv88e6xxx_reg_lock(chip); + if (mv88e6xxx_pvt_map(chip, sw_index, port)) dev_err(ds->dev, "failed to remap cross-chip Port VLAN\n"); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); } static int mv88e6xxx_software_reset(struct mv88e6xxx_chip *chip) @@ -2032,10 +2310,22 @@ /* If there is a GPIO connected to the reset pin, toggle it */ if (gpiod) { + /* If the switch has just been reset and not yet completed + * loading EEPROM, the reset may interrupt the I2C transaction + * mid-byte, causing the first EEPROM read after the reset + * from the wrong location resulting in the switch booting + * to wrong mode and inoperable. + */ + if (chip->info->ops->get_eeprom) + mv88e6xxx_g2_eeprom_wait(chip); + gpiod_set_value_cansleep(gpiod, 1); usleep_range(10000, 20000); gpiod_set_value_cansleep(gpiod, 0); usleep_range(10000, 20000); + + if (chip->info->ops->get_eeprom) + mv88e6xxx_g2_eeprom_wait(chip); } } @@ -2155,13 +2445,100 @@ return 0; } +static irqreturn_t mv88e6xxx_serdes_irq_thread_fn(int irq, void *dev_id) +{ + struct mv88e6xxx_port *mvp = dev_id; + struct mv88e6xxx_chip *chip = mvp->chip; + irqreturn_t ret = IRQ_NONE; + int port = mvp->port; + u8 lane; + + mv88e6xxx_reg_lock(chip); + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (lane) + ret = mv88e6xxx_serdes_irq_status(chip, port, lane); + mv88e6xxx_reg_unlock(chip); + + return ret; +} + +static int mv88e6xxx_serdes_irq_request(struct mv88e6xxx_chip *chip, int port, + u8 lane) +{ + struct mv88e6xxx_port *dev_id = &chip->ports[port]; + unsigned int irq; + int err; + + /* Nothing to request if this SERDES port has no IRQ */ + irq = mv88e6xxx_serdes_irq_mapping(chip, port); + if (!irq) + return 0; + + snprintf(dev_id->serdes_irq_name, sizeof(dev_id->serdes_irq_name), + "mv88e6xxx-%s-serdes-%d", dev_name(chip->dev), port); + + /* Requesting the IRQ will trigger IRQ callbacks, so release the lock */ + mv88e6xxx_reg_unlock(chip); + err = request_threaded_irq(irq, NULL, mv88e6xxx_serdes_irq_thread_fn, + IRQF_ONESHOT, dev_id->serdes_irq_name, + dev_id); + mv88e6xxx_reg_lock(chip); + if (err) + return err; + + dev_id->serdes_irq = irq; + + return mv88e6xxx_serdes_irq_enable(chip, port, lane); +} + +static int mv88e6xxx_serdes_irq_free(struct mv88e6xxx_chip *chip, int port, + u8 lane) +{ + struct mv88e6xxx_port *dev_id = &chip->ports[port]; + unsigned int irq = dev_id->serdes_irq; + int err; + + /* Nothing to free if no IRQ has been requested */ + if (!irq) + return 0; + + err = mv88e6xxx_serdes_irq_disable(chip, port, lane); + + /* Freeing the IRQ will trigger IRQ callbacks, so release the lock */ + mv88e6xxx_reg_unlock(chip); + free_irq(irq, dev_id); + mv88e6xxx_reg_lock(chip); + + dev_id->serdes_irq = 0; + + return err; +} + static int mv88e6xxx_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) { - if (chip->info->ops->serdes_power) - return chip->info->ops->serdes_power(chip, port, on); + u8 lane; + int err; - return 0; + lane = mv88e6xxx_serdes_get_lane(chip, port); + if (!lane) + return 0; + + if (on) { + err = mv88e6xxx_serdes_power_up(chip, port, lane); + if (err) + return err; + + err = mv88e6xxx_serdes_irq_request(chip, port, lane); + } else { + err = mv88e6xxx_serdes_irq_free(chip, port, lane); + if (err) + return err; + + err = mv88e6xxx_serdes_power_down(chip, port, lane); + } + + return err; } static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port) @@ -2188,7 +2565,14 @@ if (chip->info->ops->set_egress_port) { err = chip->info->ops->set_egress_port(chip, - upstream_port); + MV88E6XXX_EGRESS_DIR_INGRESS, + upstream_port); + if (err) + return err; + + err = chip->info->ops->set_egress_port(chip, + MV88E6XXX_EGRESS_DIR_EGRESS, + upstream_port); if (err) return err; } @@ -2237,9 +2621,14 @@ * If this is the upstream port for this switch, enable * forwarding of unknown unicasts and multicasts. */ - reg = MV88E6XXX_PORT_CTL0_IGMP_MLD_SNOOP | - MV88E6185_PORT_CTL0_USE_TAG | MV88E6185_PORT_CTL0_USE_IP | + reg = MV88E6185_PORT_CTL0_USE_TAG | MV88E6185_PORT_CTL0_USE_IP | MV88E6XXX_PORT_CTL0_STATE_FORWARDING; + /* Forward any IPv4 IGMP or IPv6 MLD frames received + * by a USER port to the CPU port to allow snooping. + */ + if (dsa_is_user_port(ds, port)) + reg |= MV88E6XXX_PORT_CTL0_IGMP_MLD_SNOOP; + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg); if (err) return err; @@ -2252,18 +2641,8 @@ if (err) return err; - /* Enable the SERDES interface for DSA and CPU ports. Normal - * ports SERDES are enabled when the port is enabled, thus - * saving a bit of power. - */ - if ((dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) { - err = mv88e6xxx_serdes_power(chip, port, true); - if (err) - return err; - } - - /* Port Control 2: don't force a good FCS, set the maximum frame size to - * 10240 bytes, disable 802.1q tags checking, don't discard tagged or + /* Port Control 2: don't force a good FCS, set the MTU size to + * 10222 bytes, disable 802.1q tags checking, don't discard tagged or * untagged frames on this port, do a destination address lookup on all * received packets as usual, disable ARP mirroring and don't send a * copy of all transmitted/received frames on this port to the CPU. @@ -2282,7 +2661,7 @@ return err; if (chip->info->ops->port_set_jumbo_size) { - err = chip->info->ops->port_set_jumbo_size(chip, port, 10240); + err = chip->info->ops->port_set_jumbo_size(chip, port, 10218); if (err) return err; } @@ -2338,9 +2717,11 @@ return err; } - err = mv88e6xxx_setup_message_port(chip, port); - if (err) - return err; + if (chip->info->ops->port_setup_message_port) { + err = chip->info->ops->port_setup_message_port(chip, port); + if (err) + return err; + } /* Port based VLAN map: give each port the same default address * database, and allow bidirectional communication between the @@ -2360,38 +2741,67 @@ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_DEFAULT_VLAN, 0); } +static int mv88e6xxx_get_max_mtu(struct dsa_switch *ds, int port) +{ + struct mv88e6xxx_chip *chip = ds->priv; + + if (chip->info->ops->port_set_jumbo_size) + return 10240 - VLAN_ETH_HLEN - EDSA_HLEN - ETH_FCS_LEN; + else if (chip->info->ops->set_max_frame_size) + return 1632 - VLAN_ETH_HLEN - EDSA_HLEN - ETH_FCS_LEN; + return ETH_DATA_LEN; +} + +static int mv88e6xxx_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int ret = 0; + + /* For families where we don't know how to alter the MTU, + * just accept any value up to ETH_DATA_LEN + */ + if (!chip->info->ops->port_set_jumbo_size && + !chip->info->ops->set_max_frame_size) { + if (new_mtu > ETH_DATA_LEN) + return -EINVAL; + + return 0; + } + + if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port)) + new_mtu += EDSA_HLEN; + + mv88e6xxx_reg_lock(chip); + if (chip->info->ops->port_set_jumbo_size) + ret = chip->info->ops->port_set_jumbo_size(chip, port, new_mtu); + else if (chip->info->ops->set_max_frame_size) + ret = chip->info->ops->set_max_frame_size(chip, new_mtu); + mv88e6xxx_reg_unlock(chip); + + return ret; +} + static int mv88e6xxx_port_enable(struct dsa_switch *ds, int port, struct phy_device *phydev) { struct mv88e6xxx_chip *chip = ds->priv; int err; - mutex_lock(&chip->reg_lock); - + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_serdes_power(chip, port, true); - - if (!err && chip->info->ops->serdes_irq_setup) - err = chip->info->ops->serdes_irq_setup(chip, port); - - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); return err; } -static void mv88e6xxx_port_disable(struct dsa_switch *ds, int port, - struct phy_device *phydev) +static void mv88e6xxx_port_disable(struct dsa_switch *ds, int port) { struct mv88e6xxx_chip *chip = ds->priv; - mutex_lock(&chip->reg_lock); - - if (chip->info->ops->serdes_irq_free) - chip->info->ops->serdes_irq_free(chip, port); - + mv88e6xxx_reg_lock(chip); if (mv88e6xxx_serdes_power(chip, port, false)) dev_err(chip->dev, "failed to power off SERDES\n"); - - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); } static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds, @@ -2400,9 +2810,9 @@ struct mv88e6xxx_chip *chip = ds->priv; int err; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_g1_atu_set_age_time(chip, ageing_time); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); return err; } @@ -2421,58 +2831,6 @@ return mv88e6xxx_g1_stats_clear(chip); } -/* The mv88e6390 has some hidden registers used for debug and - * development. The errata also makes use of them. - */ -static int mv88e6390_hidden_write(struct mv88e6xxx_chip *chip, int port, - int reg, u16 val) -{ - u16 ctrl; - int err; - - err = mv88e6xxx_port_write(chip, PORT_RESERVED_1A_DATA_PORT, - PORT_RESERVED_1A, val); - if (err) - return err; - - ctrl = PORT_RESERVED_1A_BUSY | PORT_RESERVED_1A_WRITE | - PORT_RESERVED_1A_BLOCK | port << PORT_RESERVED_1A_PORT_SHIFT | - reg; - - return mv88e6xxx_port_write(chip, PORT_RESERVED_1A_CTRL_PORT, - PORT_RESERVED_1A, ctrl); -} - -static int mv88e6390_hidden_wait(struct mv88e6xxx_chip *chip) -{ - return mv88e6xxx_wait(chip, PORT_RESERVED_1A_CTRL_PORT, - PORT_RESERVED_1A, PORT_RESERVED_1A_BUSY); -} - - -static int mv88e6390_hidden_read(struct mv88e6xxx_chip *chip, int port, - int reg, u16 *val) -{ - u16 ctrl; - int err; - - ctrl = PORT_RESERVED_1A_BUSY | PORT_RESERVED_1A_READ | - PORT_RESERVED_1A_BLOCK | port << PORT_RESERVED_1A_PORT_SHIFT | - reg; - - err = mv88e6xxx_port_write(chip, PORT_RESERVED_1A_CTRL_PORT, - PORT_RESERVED_1A, ctrl); - if (err) - return err; - - err = mv88e6390_hidden_wait(chip); - if (err) - return err; - - return mv88e6xxx_port_read(chip, PORT_RESERVED_1A_DATA_PORT, - PORT_RESERVED_1A, val); -} - /* Check if the errata has already been applied. */ static bool mv88e6390_setup_errata_applied(struct mv88e6xxx_chip *chip) { @@ -2481,7 +2839,7 @@ u16 val; for (port = 0; port < mv88e6xxx_num_ports(chip); port++) { - err = mv88e6390_hidden_read(chip, port, 0, &val); + err = mv88e6xxx_port_hidden_read(chip, 0xf, port, 0, &val); if (err) { dev_err(chip->dev, "Error reading hidden register: %d\n", err); @@ -2514,12 +2872,19 @@ } for (port = 0; port < mv88e6xxx_num_ports(chip); port++) { - err = mv88e6390_hidden_write(chip, port, 0, 0x01c0); + err = mv88e6xxx_port_hidden_write(chip, 0xf, port, 0, 0x01c0); if (err) return err; } return mv88e6xxx_software_reset(chip); +} + +static void mv88e6xxx_teardown(struct dsa_switch *ds) +{ + mv88e6xxx_teardown_devlink_params(ds); + dsa_devlink_resources_unregister(ds); + mv88e6xxx_teardown_devlink_regions(ds); } static int mv88e6xxx_setup(struct dsa_switch *ds) @@ -2532,7 +2897,7 @@ chip->ds = ds; ds->slave_mii_bus = mv88e6xxx_default_mdio_bus(chip); - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); if (chip->info->ops->setup_errata) { err = chip->info->ops->setup_errata(chip); @@ -2555,6 +2920,13 @@ for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { if (dsa_is_unused_port(ds, i)) continue; + + /* Prevent the use of an invalid port. */ + if (mv88e6xxx_is_invalid_port(chip, i)) { + dev_err(chip->dev, "port %d is invalid\n", i); + err = -EINVAL; + goto unlock; + } err = mv88e6xxx_setup_port(chip, i); if (err) @@ -2629,7 +3001,34 @@ goto unlock; unlock: - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); + + if (err) + return err; + + /* Have to be called without holding the register lock, since + * they take the devlink lock, and we later take the locks in + * the reverse order when getting/setting parameters or + * resource occupancy. + */ + err = mv88e6xxx_setup_devlink_resources(ds); + if (err) + return err; + + err = mv88e6xxx_setup_devlink_params(ds); + if (err) + goto out_resources; + + err = mv88e6xxx_setup_devlink_regions(ds); + if (err) + goto out_params; + + return 0; + +out_params: + mv88e6xxx_teardown_devlink_params(ds); +out_resources: + dsa_devlink_resources_unregister(ds); return err; } @@ -2651,9 +3050,9 @@ if (!chip->info->ops->phy_read) return -EOPNOTSUPP; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = chip->info->ops->phy_read(chip, bus, phy, reg, &val); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); /* Some internal PHYs don't have a model number. */ if (reg == MII_PHYSID2 && !(val & 0x3f0) && @@ -2675,9 +3074,9 @@ if (!chip->info->ops->phy_write) return -EOPNOTSUPP; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = chip->info->ops->phy_write(chip, bus, phy, reg, val); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); return err; } @@ -2692,15 +3091,15 @@ int err; if (external) { - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_g2_scratch_gpio_set_smi(chip, true); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); if (err) return err; } - bus = devm_mdiobus_alloc_size(chip->dev, sizeof(*mdio_bus)); + bus = mdiobus_alloc_size(sizeof(*mdio_bus)); if (!bus) return -ENOMEM; @@ -2725,14 +3124,14 @@ if (!external) { err = mv88e6xxx_g2_irq_mdio_setup(chip, bus); if (err) - return err; + goto out; } err = of_mdiobus_register(bus, np); if (err) { dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err); mv88e6xxx_g2_irq_mdio_free(chip, bus); - return err; + goto out; } if (external) @@ -2741,34 +3140,32 @@ list_add(&mdio_bus->list, &chip->mdios); return 0; -} -static const struct of_device_id mv88e6xxx_mdio_external_match[] = { - { .compatible = "marvell,mv88e6xxx-mdio-external", - .data = (void *)true }, - { }, -}; +out: + mdiobus_free(bus); + return err; +} static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip) { - struct mv88e6xxx_mdio_bus *mdio_bus; + struct mv88e6xxx_mdio_bus *mdio_bus, *p; struct mii_bus *bus; - list_for_each_entry(mdio_bus, &chip->mdios, list) { + list_for_each_entry_safe(mdio_bus, p, &chip->mdios, list) { bus = mdio_bus->bus; if (!mdio_bus->external) mv88e6xxx_g2_irq_mdio_free(chip, bus); mdiobus_unregister(bus); + mdiobus_free(bus); } } static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip, struct device_node *np) { - const struct of_device_id *match; struct device_node *child; int err; @@ -2778,6 +3175,7 @@ */ child = of_get_child_by_name(np, "mdio"); err = mv88e6xxx_mdio_register(chip, child, false); + of_node_put(child); if (err) return err; @@ -2786,11 +3184,12 @@ * bus. */ for_each_available_child_of_node(np, child) { - match = of_match_node(mv88e6xxx_mdio_external_match, child); - if (match) { + if (of_device_is_compatible( + child, "marvell,mv88e6xxx-mdio-external")) { err = mv88e6xxx_mdio_register(chip, child, true); if (err) { mv88e6xxx_mdios_unregister(chip); + of_node_put(child); return err; } } @@ -2815,9 +3214,9 @@ if (!chip->info->ops->get_eeprom) return -EOPNOTSUPP; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = chip->info->ops->get_eeprom(chip, eeprom, data); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); if (err) return err; @@ -2839,9 +3238,9 @@ if (eeprom->magic != 0xc3ec4951) return -EINVAL; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = chip->info->ops->set_eeprom(chip, eeprom, data); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); return err; } @@ -2855,8 +3254,7 @@ .phy_read = mv88e6185_phy_ppu_read, .phy_write = mv88e6185_phy_ppu_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -2865,8 +3263,8 @@ .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2884,6 +3282,7 @@ .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; static const struct mv88e6xxx_ops mv88e6095_ops = { @@ -2894,13 +3293,12 @@ .phy_read = mv88e6185_phy_ppu_read, .phy_write = mv88e6185_phy_ppu_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_set_frame_mode = mv88e6085_port_set_frame_mode, .port_set_egress_floods = mv88e6185_port_set_egress_floods, .port_set_upstream_port = mv88e6095_port_set_upstream_port, - .port_link_state = mv88e6185_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2913,6 +3311,7 @@ .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; static const struct mv88e6xxx_ops mv88e6097_ops = { @@ -2924,9 +3323,9 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, @@ -2934,8 +3333,8 @@ .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2951,6 +3350,7 @@ .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; static const struct mv88e6xxx_ops mv88e6123_ops = { @@ -2962,14 +3362,13 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_set_frame_mode = mv88e6085_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2981,9 +3380,12 @@ .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; static const struct mv88e6xxx_ops mv88e6131_ops = { @@ -2994,8 +3396,7 @@ .phy_read = mv88e6185_phy_ppu_read, .phy_write = mv88e6185_phy_ppu_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6185_port_set_egress_floods, @@ -3005,8 +3406,8 @@ .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_limit = mv88e6097_port_pause_limit, .port_set_pause = mv88e6185_port_set_pause, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3036,9 +3437,9 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6341_port_set_speed, + .port_set_speed_duplex = mv88e6341_port_set_speed_duplex, + .port_max_speed_mode = mv88e6341_port_max_speed_mode, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3048,8 +3449,9 @@ .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_set_cmode = mv88e6341_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3061,11 +3463,28 @@ .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, + .rmu_disable = mv88e6390_g1_rmu_disable, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .serdes_power = mv88e6341_serdes_power, + .serdes_power = mv88e6390_serdes_power, + .serdes_get_lane = mv88e6341_serdes_get_lane, + /* Check status register pause & lpa register */ + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6390_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, + .serdes_irq_enable = mv88e6390_serdes_irq_enable, + .serdes_irq_status = mv88e6390_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, - .phylink_validate = mv88e6390_phylink_validate, + .serdes_get_sset_count = mv88e6390_serdes_get_sset_count, + .serdes_get_strings = mv88e6390_serdes_get_strings, + .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, + .phylink_validate = mv88e6341_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -3077,19 +3496,17 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, - .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3101,11 +3518,14 @@ .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .avb_ops = &mv88e6165_avb_ops, .ptp_ops = &mv88e6165_ptp_ops, .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; static const struct mv88e6xxx_ops mv88e6165_ops = { @@ -3117,12 +3537,11 @@ .phy_read = mv88e6165_phy_read, .phy_write = mv88e6165_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3134,6 +3553,8 @@ .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .avb_ops = &mv88e6165_avb_ops, @@ -3150,9 +3571,8 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3162,8 +3582,8 @@ .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3175,6 +3595,8 @@ .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .phylink_validate = mv88e6185_phylink_validate, @@ -3191,10 +3613,10 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6352_port_set_speed, + .port_set_speed_duplex = mv88e6352_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, @@ -3203,8 +3625,8 @@ .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3217,9 +3639,18 @@ .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .rmu_disable = mv88e6352_g1_rmu_disable, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .serdes_get_lane = mv88e6352_serdes_get_lane, + .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6352_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up, .serdes_power = mv88e6352_serdes_power, + .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, + .serdes_get_regs = mv88e6352_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6352_phylink_validate, }; @@ -3233,10 +3664,10 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, @@ -3245,8 +3676,8 @@ .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3258,6 +3689,8 @@ .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .phylink_validate = mv88e6185_phylink_validate, @@ -3274,10 +3707,10 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6352_port_set_speed, + .port_set_speed_duplex = mv88e6352_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, @@ -3286,8 +3719,8 @@ .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3300,9 +3733,21 @@ .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .rmu_disable = mv88e6352_g1_rmu_disable, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .serdes_get_lane = mv88e6352_serdes_get_lane, + .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6352_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up, .serdes_power = mv88e6352_serdes_power, + .serdes_irq_mapping = mv88e6352_serdes_irq_mapping, + .serdes_irq_enable = mv88e6352_serdes_irq_enable, + .serdes_irq_status = mv88e6352_serdes_irq_status, + .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, + .serdes_get_regs = mv88e6352_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6352_phylink_validate, }; @@ -3315,15 +3760,14 @@ .phy_read = mv88e6185_phy_ppu_read, .phy_write = mv88e6185_phy_ppu_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_set_frame_mode = mv88e6085_port_set_frame_mode, .port_set_egress_floods = mv88e6185_port_set_egress_floods, .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, .port_set_upstream_port = mv88e6095_port_set_upstream_port, .port_set_pause = mv88e6185_port_set_pause, - .port_link_state = mv88e6185_port_link_state, .port_get_cmode = mv88e6185_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3340,6 +3784,7 @@ .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, .phylink_validate = mv88e6185_phylink_validate, + .set_max_frame_size = mv88e6185_g1_set_max_frame_size, }; static const struct mv88e6xxx_ops mv88e6190_ops = { @@ -3352,18 +3797,21 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6390_port_set_speed, + .port_set_speed_duplex = mv88e6390_port_set_speed_duplex, + .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_set_cmode = mv88e6390_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3376,11 +3824,24 @@ .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .rmu_disable = mv88e6390_g1_rmu_disable, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, - .serdes_irq_setup = mv88e6390_serdes_irq_setup, - .serdes_irq_free = mv88e6390_serdes_irq_free, + .serdes_get_lane = mv88e6390_serdes_get_lane, + /* Check status register pause & lpa register */ + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6390_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, + .serdes_irq_enable = mv88e6390_serdes_irq_enable, + .serdes_irq_status = mv88e6390_serdes_irq_status, + .serdes_get_strings = mv88e6390_serdes_get_strings, + .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6390_phylink_validate, }; @@ -3395,18 +3856,21 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6390x_port_set_speed, + .port_set_speed_duplex = mv88e6390x_port_set_speed_duplex, + .port_max_speed_mode = mv88e6390x_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_set_cmode = mv88e6390x_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3419,11 +3883,24 @@ .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .rmu_disable = mv88e6390_g1_rmu_disable, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, - .serdes_power = mv88e6390x_serdes_power, - .serdes_irq_setup = mv88e6390_serdes_irq_setup, - .serdes_irq_free = mv88e6390_serdes_irq_free, + .serdes_power = mv88e6390_serdes_power, + .serdes_get_lane = mv88e6390x_serdes_get_lane, + /* Check status register pause & lpa register */ + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6390_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, + .serdes_irq_enable = mv88e6390_serdes_irq_enable, + .serdes_irq_status = mv88e6390_serdes_irq_status, + .serdes_get_strings = mv88e6390_serdes_get_strings, + .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6390x_phylink_validate, }; @@ -3438,9 +3915,9 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6390_port_set_speed, + .port_set_speed_duplex = mv88e6390_port_set_speed_duplex, + .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3448,8 +3925,9 @@ .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_set_cmode = mv88e6390_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3462,11 +3940,24 @@ .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .rmu_disable = mv88e6390_g1_rmu_disable, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, - .serdes_irq_setup = mv88e6390_serdes_irq_setup, - .serdes_irq_free = mv88e6390_serdes_irq_free, + .serdes_get_lane = mv88e6390_serdes_get_lane, + /* Check status register pause & lpa register */ + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6390_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, + .serdes_irq_enable = mv88e6390_serdes_irq_enable, + .serdes_irq_status = mv88e6390_serdes_irq_status, + .serdes_get_strings = mv88e6390_serdes_get_strings, + .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, .phylink_validate = mv88e6390_phylink_validate, @@ -3483,10 +3974,10 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6352_port_set_speed, + .port_set_speed_duplex = mv88e6352_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, @@ -3495,8 +3986,8 @@ .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3509,13 +4000,63 @@ .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .rmu_disable = mv88e6352_g1_rmu_disable, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .serdes_get_lane = mv88e6352_serdes_get_lane, + .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6352_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up, .serdes_power = mv88e6352_serdes_power, + .serdes_irq_mapping = mv88e6352_serdes_irq_mapping, + .serdes_irq_enable = mv88e6352_serdes_irq_enable, + .serdes_irq_status = mv88e6352_serdes_irq_status, + .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, + .serdes_get_regs = mv88e6352_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, .phylink_validate = mv88e6352_phylink_validate, +}; + +static const struct mv88e6xxx_ops mv88e6250_ops = { + /* MV88E6XXX_FAMILY_6250 */ + .ieee_pri_map = mv88e6250_g1_ieee_pri_map, + .ip_pri_map = mv88e6085_g1_ip_pri_map, + .irl_init_all = mv88e6352_g2_irl_init_all, + .get_eeprom = mv88e6xxx_g2_get_eeprom16, + .set_eeprom = mv88e6xxx_g2_set_eeprom16, + .set_switch_mac = mv88e6xxx_g2_set_switch_mac, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed_duplex = mv88e6250_port_set_speed_duplex, + .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, + .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_limit = mv88e6097_port_pause_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_set_histogram = mv88e6095_g1_stats_set_histogram, + .stats_get_sset_count = mv88e6250_stats_get_sset_count, + .stats_get_strings = mv88e6250_stats_get_strings, + .stats_get_stats = mv88e6250_stats_get_stats, + .set_cpu_port = mv88e6095_g1_set_cpu_port, + .set_egress_port = mv88e6095_g1_set_egress_port, + .watchdog_ops = &mv88e6250_watchdog_ops, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .pot_clear = mv88e6xxx_g2_pot_clear, + .reset = mv88e6250_g1_reset, + .vtu_getnext = mv88e6250_g1_vtu_getnext, + .vtu_loadpurge = mv88e6250_g1_vtu_loadpurge, + .avb_ops = &mv88e6352_avb_ops, + .ptp_ops = &mv88e6250_ptp_ops, + .phylink_validate = mv88e6065_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6290_ops = { @@ -3528,19 +4069,20 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6390_port_set_speed, + .port_set_speed_duplex = mv88e6390_port_set_speed_duplex, + .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_pause_limit = mv88e6390_port_pause_limit, - .port_set_cmode = mv88e6390x_port_set_cmode, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_set_cmode = mv88e6390_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3553,11 +4095,24 @@ .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .rmu_disable = mv88e6390_g1_rmu_disable, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, - .serdes_irq_setup = mv88e6390_serdes_irq_setup, - .serdes_irq_free = mv88e6390_serdes_irq_free, + .serdes_get_lane = mv88e6390_serdes_get_lane, + /* Check status register pause & lpa register */ + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6390_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, + .serdes_irq_enable = mv88e6390_serdes_irq_enable, + .serdes_irq_status = mv88e6390_serdes_irq_status, + .serdes_get_strings = mv88e6390_serdes_get_strings, + .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, @@ -3575,8 +4130,7 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3586,8 +4140,8 @@ .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3595,6 +4149,7 @@ .stats_get_stats = mv88e6320_stats_get_stats, .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, + .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, @@ -3617,8 +4172,7 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3628,8 +4182,8 @@ .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3637,6 +4191,8 @@ .stats_get_stats = mv88e6320_stats_get_stats, .set_cpu_port = mv88e6095_g1_set_cpu_port, .set_egress_port = mv88e6095_g1_set_egress_port, + .watchdog_ops = &mv88e6390_watchdog_ops, + .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, @@ -3657,9 +4213,9 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6341_port_set_speed, + .port_set_speed_duplex = mv88e6341_port_set_speed_duplex, + .port_max_speed_mode = mv88e6341_port_max_speed_mode, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3669,8 +4225,9 @@ .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_set_cmode = mv88e6341_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3683,13 +4240,29 @@ .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .rmu_disable = mv88e6390_g1_rmu_disable, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, - .serdes_power = mv88e6341_serdes_power, + .serdes_power = mv88e6390_serdes_power, + .serdes_get_lane = mv88e6341_serdes_get_lane, + /* Check status register pause & lpa register */ + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6390_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, + .serdes_irq_enable = mv88e6390_serdes_irq_enable, + .serdes_irq_status = mv88e6390_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, - .phylink_validate = mv88e6390_phylink_validate, + .serdes_get_sset_count = mv88e6390_serdes_get_sset_count, + .serdes_get_strings = mv88e6390_serdes_get_strings, + .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, + .phylink_validate = mv88e6341_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6350_ops = { @@ -3701,9 +4274,8 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, @@ -3713,8 +4285,8 @@ .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3726,6 +4298,8 @@ .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .phylink_validate = mv88e6185_phylink_validate, @@ -3740,10 +4314,10 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6185_port_set_speed, + .port_set_speed_duplex = mv88e6185_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, @@ -3752,8 +4326,8 @@ .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3765,7 +4339,8 @@ .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, - .rmu_disable = mv88e6390_g1_rmu_disable, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .avb_ops = &mv88e6352_avb_ops, @@ -3784,10 +4359,10 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, - .port_set_speed = mv88e6352_port_set_speed, + .port_set_speed_duplex = mv88e6352_port_set_speed_duplex, .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, @@ -3796,8 +4371,8 @@ .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3810,15 +4385,27 @@ .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .rmu_disable = mv88e6352_g1_rmu_disable, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .serdes_get_lane = mv88e6352_serdes_get_lane, + .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6352_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up, .serdes_power = mv88e6352_serdes_power, + .serdes_irq_mapping = mv88e6352_serdes_irq_mapping, + .serdes_irq_enable = mv88e6352_serdes_irq_enable, + .serdes_irq_status = mv88e6352_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, .serdes_get_sset_count = mv88e6352_serdes_get_sset_count, .serdes_get_strings = mv88e6352_serdes_get_strings, .serdes_get_stats = mv88e6352_serdes_get_stats, + .serdes_get_regs_len = mv88e6352_serdes_get_regs_len, + .serdes_get_regs = mv88e6352_serdes_get_regs, .phylink_validate = mv88e6352_phylink_validate, }; @@ -3832,21 +4419,22 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6390_port_set_speed, + .port_set_speed_duplex = mv88e6390_port_set_speed_duplex, + .port_max_speed_mode = mv88e6390_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_limit = mv88e6390_port_pause_limit, - .port_set_cmode = mv88e6390x_port_set_cmode, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_set_cmode = mv88e6390_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3859,14 +4447,28 @@ .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .rmu_disable = mv88e6390_g1_rmu_disable, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, - .serdes_irq_setup = mv88e6390_serdes_irq_setup, - .serdes_irq_free = mv88e6390_serdes_irq_free, + .serdes_get_lane = mv88e6390_serdes_get_lane, + /* Check status register pause & lpa register */ + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6390_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, + .serdes_irq_enable = mv88e6390_serdes_irq_enable, + .serdes_irq_status = mv88e6390_serdes_irq_status, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .serdes_get_sset_count = mv88e6390_serdes_get_sset_count, + .serdes_get_strings = mv88e6390_serdes_get_strings, + .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, .phylink_validate = mv88e6390_phylink_validate, }; @@ -3880,21 +4482,22 @@ .phy_read = mv88e6xxx_g2_smi_phy_read, .phy_write = mv88e6xxx_g2_smi_phy_write, .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6390x_port_set_speed, + .port_set_speed_duplex = mv88e6390x_port_set_speed_duplex, + .port_max_speed_mode = mv88e6390x_port_max_speed_mode, .port_tag_remap = mv88e6390_port_tag_remap, + .port_set_policy = mv88e6352_port_set_policy, .port_set_frame_mode = mv88e6351_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_limit = mv88e6390_port_pause_limit, - .port_set_cmode = mv88e6390x_port_set_cmode, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .port_link_state = mv88e6352_port_link_state, .port_get_cmode = mv88e6352_port_get_cmode, + .port_set_cmode = mv88e6390x_port_set_cmode, + .port_setup_message_port = mv88e6xxx_setup_message_port, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3907,11 +4510,24 @@ .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .rmu_disable = mv88e6390_g1_rmu_disable, + .atu_get_hash = mv88e6165_g1_atu_get_hash, + .atu_set_hash = mv88e6165_g1_atu_set_hash, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, - .serdes_power = mv88e6390x_serdes_power, - .serdes_irq_setup = mv88e6390_serdes_irq_setup, - .serdes_irq_free = mv88e6390_serdes_irq_free, + .serdes_power = mv88e6390_serdes_power, + .serdes_get_lane = mv88e6390x_serdes_get_lane, + .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state, + .serdes_pcs_config = mv88e6390_serdes_pcs_config, + .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart, + .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up, + .serdes_irq_mapping = mv88e6390_serdes_irq_mapping, + .serdes_irq_enable = mv88e6390_serdes_irq_enable, + .serdes_irq_status = mv88e6390_serdes_irq_status, + .serdes_get_sset_count = mv88e6390_serdes_get_sset_count, + .serdes_get_strings = mv88e6390_serdes_get_strings, + .serdes_get_stats = mv88e6390_serdes_get_stats, + .serdes_get_regs_len = mv88e6390_serdes_get_regs_len, + .serdes_get_regs = mv88e6390_serdes_get_regs, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, @@ -3924,6 +4540,7 @@ .family = MV88E6XXX_FAMILY_6097, .name = "Marvell 88E6085", .num_databases = 4096, + .num_macs = 8192, .num_ports = 10, .num_internal_phys = 5, .max_vid = 4095, @@ -3946,6 +4563,7 @@ .family = MV88E6XXX_FAMILY_6095, .name = "Marvell 88E6095/88E6095F", .num_databases = 256, + .num_macs = 8192, .num_ports = 11, .num_internal_phys = 0, .max_vid = 4095, @@ -3966,6 +4584,7 @@ .family = MV88E6XXX_FAMILY_6097, .name = "Marvell 88E6097/88E6097F", .num_databases = 4096, + .num_macs = 8192, .num_ports = 11, .num_internal_phys = 8, .max_vid = 4095, @@ -3988,6 +4607,7 @@ .family = MV88E6XXX_FAMILY_6165, .name = "Marvell 88E6123", .num_databases = 4096, + .num_macs = 1024, .num_ports = 3, .num_internal_phys = 5, .max_vid = 4095, @@ -4010,6 +4630,7 @@ .family = MV88E6XXX_FAMILY_6185, .name = "Marvell 88E6131", .num_databases = 256, + .num_macs = 8192, .num_ports = 8, .num_internal_phys = 0, .max_vid = 4095, @@ -4030,6 +4651,7 @@ .family = MV88E6XXX_FAMILY_6341, .name = "Marvell 88E6141", .num_databases = 4096, + .num_macs = 2048, .num_ports = 6, .num_internal_phys = 5, .num_gpio = 11, @@ -4053,6 +4675,7 @@ .family = MV88E6XXX_FAMILY_6165, .name = "Marvell 88E6161", .num_databases = 4096, + .num_macs = 1024, .num_ports = 6, .num_internal_phys = 5, .max_vid = 4095, @@ -4076,6 +4699,7 @@ .family = MV88E6XXX_FAMILY_6165, .name = "Marvell 88E6165", .num_databases = 4096, + .num_macs = 8192, .num_ports = 6, .num_internal_phys = 0, .max_vid = 4095, @@ -4099,6 +4723,7 @@ .family = MV88E6XXX_FAMILY_6351, .name = "Marvell 88E6171", .num_databases = 4096, + .num_macs = 8192, .num_ports = 7, .num_internal_phys = 5, .max_vid = 4095, @@ -4121,6 +4746,7 @@ .family = MV88E6XXX_FAMILY_6352, .name = "Marvell 88E6172", .num_databases = 4096, + .num_macs = 8192, .num_ports = 7, .num_internal_phys = 5, .num_gpio = 15, @@ -4144,6 +4770,7 @@ .family = MV88E6XXX_FAMILY_6351, .name = "Marvell 88E6175", .num_databases = 4096, + .num_macs = 8192, .num_ports = 7, .num_internal_phys = 5, .max_vid = 4095, @@ -4166,6 +4793,7 @@ .family = MV88E6XXX_FAMILY_6352, .name = "Marvell 88E6176", .num_databases = 4096, + .num_macs = 8192, .num_ports = 7, .num_internal_phys = 5, .num_gpio = 15, @@ -4189,6 +4817,7 @@ .family = MV88E6XXX_FAMILY_6185, .name = "Marvell 88E6185", .num_databases = 256, + .num_macs = 8192, .num_ports = 10, .num_internal_phys = 0, .max_vid = 4095, @@ -4209,6 +4838,7 @@ .family = MV88E6XXX_FAMILY_6390, .name = "Marvell 88E6190", .num_databases = 4096, + .num_macs = 16384, .num_ports = 11, /* 10 + Z80 */ .num_internal_phys = 9, .num_gpio = 16, @@ -4232,6 +4862,7 @@ .family = MV88E6XXX_FAMILY_6390, .name = "Marvell 88E6190X", .num_databases = 4096, + .num_macs = 16384, .num_ports = 11, /* 10 + Z80 */ .num_internal_phys = 9, .num_gpio = 16, @@ -4255,6 +4886,7 @@ .family = MV88E6XXX_FAMILY_6390, .name = "Marvell 88E6191", .num_databases = 4096, + .num_macs = 16384, .num_ports = 11, /* 10 + Z80 */ .num_internal_phys = 9, .max_vid = 8191, @@ -4273,11 +4905,39 @@ .ops = &mv88e6191_ops, }, + [MV88E6220] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6220, + .family = MV88E6XXX_FAMILY_6250, + .name = "Marvell 88E6220", + .num_databases = 64, + + /* Ports 2-4 are not routed to pins + * => usable ports 0, 1, 5, 6 + */ + .num_ports = 7, + .num_internal_phys = 2, + .invalid_port_mask = BIT(2) | BIT(3) | BIT(4), + .max_vid = 4095, + .port_base_addr = 0x08, + .phy_base_addr = 0x00, + .global1_addr = 0x0f, + .global2_addr = 0x07, + .age_time_coeff = 15000, + .g1_irqs = 9, + .g2_irqs = 10, + .atu_move_port_mask = 0xf, + .dual_chip = true, + .tag_protocol = DSA_TAG_PROTO_DSA, + .ptp_support = true, + .ops = &mv88e6250_ops, + }, + [MV88E6240] = { .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6240, .family = MV88E6XXX_FAMILY_6352, .name = "Marvell 88E6240", .num_databases = 4096, + .num_macs = 8192, .num_ports = 7, .num_internal_phys = 5, .num_gpio = 15, @@ -4295,6 +4955,28 @@ .tag_protocol = DSA_TAG_PROTO_EDSA, .ptp_support = true, .ops = &mv88e6240_ops, + }, + + [MV88E6250] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6250, + .family = MV88E6XXX_FAMILY_6250, + .name = "Marvell 88E6250", + .num_databases = 64, + .num_ports = 7, + .num_internal_phys = 5, + .max_vid = 4095, + .port_base_addr = 0x08, + .phy_base_addr = 0x00, + .global1_addr = 0x0f, + .global2_addr = 0x07, + .age_time_coeff = 15000, + .g1_irqs = 9, + .g2_irqs = 10, + .atu_move_port_mask = 0xf, + .dual_chip = true, + .tag_protocol = DSA_TAG_PROTO_DSA, + .ptp_support = true, + .ops = &mv88e6250_ops, }, [MV88E6290] = { @@ -4326,6 +5008,7 @@ .family = MV88E6XXX_FAMILY_6320, .name = "Marvell 88E6320", .num_databases = 4096, + .num_macs = 8192, .num_ports = 7, .num_internal_phys = 5, .num_gpio = 15, @@ -4350,6 +5033,7 @@ .family = MV88E6XXX_FAMILY_6320, .name = "Marvell 88E6321", .num_databases = 4096, + .num_macs = 8192, .num_ports = 7, .num_internal_phys = 5, .num_gpio = 15, @@ -4373,6 +5057,7 @@ .family = MV88E6XXX_FAMILY_6341, .name = "Marvell 88E6341", .num_databases = 4096, + .num_macs = 2048, .num_internal_phys = 5, .num_ports = 6, .num_gpio = 11, @@ -4397,6 +5082,7 @@ .family = MV88E6XXX_FAMILY_6351, .name = "Marvell 88E6350", .num_databases = 4096, + .num_macs = 8192, .num_ports = 7, .num_internal_phys = 5, .max_vid = 4095, @@ -4419,6 +5105,7 @@ .family = MV88E6XXX_FAMILY_6351, .name = "Marvell 88E6351", .num_databases = 4096, + .num_macs = 8192, .num_ports = 7, .num_internal_phys = 5, .max_vid = 4095, @@ -4441,6 +5128,7 @@ .family = MV88E6XXX_FAMILY_6352, .name = "Marvell 88E6352", .num_databases = 4096, + .num_macs = 8192, .num_ports = 7, .num_internal_phys = 5, .num_gpio = 15, @@ -4464,6 +5152,7 @@ .family = MV88E6XXX_FAMILY_6390, .name = "Marvell 88E6390", .num_databases = 4096, + .num_macs = 16384, .num_ports = 11, /* 10 + Z80 */ .num_internal_phys = 9, .num_gpio = 16, @@ -4487,6 +5176,7 @@ .family = MV88E6XXX_FAMILY_6390, .name = "Marvell 88E6390X", .num_databases = 4096, + .num_macs = 16384, .num_ports = 11, /* 10 + Z80 */ .num_internal_phys = 9, .num_gpio = 16, @@ -4525,9 +5215,9 @@ u16 id; int err; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_port_read(chip, 0, MV88E6XXX_PORT_SWITCH_ID, &id); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); if (err) return err; @@ -4563,93 +5253,19 @@ mutex_init(&chip->reg_lock); INIT_LIST_HEAD(&chip->mdios); + idr_init(&chip->policies); return chip; } -static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, - struct mii_bus *bus, int sw_addr) -{ - if (sw_addr == 0) - chip->smi_ops = &mv88e6xxx_smi_single_chip_ops; - else if (chip->info->multi_chip) - chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops; - else - return -EINVAL; - - chip->bus = bus; - chip->sw_addr = sw_addr; - - return 0; -} - -static void mv88e6xxx_ports_cmode_init(struct mv88e6xxx_chip *chip) -{ - int i; - - for (i = 0; i < mv88e6xxx_num_ports(chip); i++) - chip->ports[i].cmode = MV88E6XXX_PORT_STS_CMODE_INVALID; -} - static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds, - int port) + int port, + enum dsa_tag_protocol m) { struct mv88e6xxx_chip *chip = ds->priv; return chip->info->tag_protocol; } - -#if IS_ENABLED(CONFIG_NET_DSA_LEGACY) -static const char *mv88e6xxx_drv_probe(struct device *dsa_dev, - struct device *host_dev, int sw_addr, - void **priv) -{ - struct mv88e6xxx_chip *chip; - struct mii_bus *bus; - int err; - - bus = dsa_host_dev_to_mii_bus(host_dev); - if (!bus) - return NULL; - - chip = mv88e6xxx_alloc_chip(dsa_dev); - if (!chip) - return NULL; - - /* Legacy SMI probing will only support chips similar to 88E6085 */ - chip->info = &mv88e6xxx_table[MV88E6085]; - - err = mv88e6xxx_smi_init(chip, bus, sw_addr); - if (err) - goto free; - - err = mv88e6xxx_detect(chip); - if (err) - goto free; - - mv88e6xxx_ports_cmode_init(chip); - - mutex_lock(&chip->reg_lock); - err = mv88e6xxx_switch_reset(chip); - mutex_unlock(&chip->reg_lock); - if (err) - goto free; - - mv88e6xxx_phy_init(chip); - - err = mv88e6xxx_mdios_register(chip, NULL); - if (err) - goto free; - - *priv = chip; - - return chip->info->name; -free: - devm_kfree(dsa_dev, chip); - - return NULL; -} -#endif static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb) @@ -4666,12 +5282,12 @@ { struct mv88e6xxx_chip *chip = ds->priv; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); if (mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid, MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC)) dev_err(ds->dev, "p%d: failed to load multicast MAC address\n", port); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); } static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port, @@ -4680,24 +5296,111 @@ struct mv88e6xxx_chip *chip = ds->priv; int err; + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid, 0); + mv88e6xxx_reg_unlock(chip); + + return err; +} + +static int mv88e6xxx_port_mirror_add(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress) +{ + enum mv88e6xxx_egress_direction direction = ingress ? + MV88E6XXX_EGRESS_DIR_INGRESS : + MV88E6XXX_EGRESS_DIR_EGRESS; + struct mv88e6xxx_chip *chip = ds->priv; + bool other_mirrors = false; + int i; + int err; + + if (!chip->info->ops->set_egress_port) + return -EOPNOTSUPP; + mutex_lock(&chip->reg_lock); - err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid, - MV88E6XXX_G1_ATU_DATA_STATE_UNUSED); + if ((ingress ? chip->ingress_dest_port : chip->egress_dest_port) != + mirror->to_local_port) { + for (i = 0; i < mv88e6xxx_num_ports(chip); i++) + other_mirrors |= ingress ? + chip->ports[i].mirror_ingress : + chip->ports[i].mirror_egress; + + /* Can't change egress port when other mirror is active */ + if (other_mirrors) { + err = -EBUSY; + goto out; + } + + err = chip->info->ops->set_egress_port(chip, + direction, + mirror->to_local_port); + if (err) + goto out; + } + + err = mv88e6xxx_port_set_mirror(chip, port, direction, true); +out: mutex_unlock(&chip->reg_lock); return err; } +static void mv88e6xxx_port_mirror_del(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror) +{ + enum mv88e6xxx_egress_direction direction = mirror->ingress ? + MV88E6XXX_EGRESS_DIR_INGRESS : + MV88E6XXX_EGRESS_DIR_EGRESS; + struct mv88e6xxx_chip *chip = ds->priv; + bool other_mirrors = false; + int i; + + mutex_lock(&chip->reg_lock); + if (mv88e6xxx_port_set_mirror(chip, port, direction, false)) + dev_err(ds->dev, "p%d: failed to disable mirroring\n", port); + + for (i = 0; i < mv88e6xxx_num_ports(chip); i++) + other_mirrors |= mirror->ingress ? + chip->ports[i].mirror_ingress : + chip->ports[i].mirror_egress; + + /* Reset egress port when no other mirror is active */ + if (!other_mirrors) { + if (chip->info->ops->set_egress_port(chip, + direction, + dsa_upstream_port(ds, + port))) + dev_err(ds->dev, "failed to set egress port\n"); + } + + mutex_unlock(&chip->reg_lock); +} + +static int mv88e6xxx_port_egress_floods(struct dsa_switch *ds, int port, + bool unicast, bool multicast) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err = -EOPNOTSUPP; + + mv88e6xxx_reg_lock(chip); + if (chip->info->ops->port_set_egress_floods) + err = chip->info->ops->port_set_egress_floods(chip, port, + unicast, + multicast); + mv88e6xxx_reg_unlock(chip); + + return err; +} + static const struct dsa_switch_ops mv88e6xxx_switch_ops = { -#if IS_ENABLED(CONFIG_NET_DSA_LEGACY) - .probe = mv88e6xxx_drv_probe, -#endif .get_tag_protocol = mv88e6xxx_get_tag_protocol, .setup = mv88e6xxx_setup, - .adjust_link = mv88e6xxx_adjust_link, + .teardown = mv88e6xxx_teardown, .phylink_validate = mv88e6xxx_validate, - .phylink_mac_link_state = mv88e6xxx_link_state, + .phylink_mac_link_state = mv88e6xxx_serdes_pcs_get_state, .phylink_mac_config = mv88e6xxx_mac_config, + .phylink_mac_an_restart = mv88e6xxx_serdes_pcs_an_restart, .phylink_mac_link_down = mv88e6xxx_mac_link_down, .phylink_mac_link_up = mv88e6xxx_mac_link_up, .get_strings = mv88e6xxx_get_strings, @@ -4705,6 +5408,8 @@ .get_sset_count = mv88e6xxx_get_sset_count, .port_enable = mv88e6xxx_port_enable, .port_disable = mv88e6xxx_port_disable, + .port_max_mtu = mv88e6xxx_get_max_mtu, + .port_change_mtu = mv88e6xxx_change_mtu, .get_mac_eee = mv88e6xxx_get_mac_eee, .set_mac_eee = mv88e6xxx_set_mac_eee, .get_eeprom_len = mv88e6xxx_get_eeprom_len, @@ -4712,9 +5417,12 @@ .set_eeprom = mv88e6xxx_set_eeprom, .get_regs_len = mv88e6xxx_get_regs_len, .get_regs = mv88e6xxx_get_regs, + .get_rxnfc = mv88e6xxx_get_rxnfc, + .set_rxnfc = mv88e6xxx_set_rxnfc, .set_ageing_time = mv88e6xxx_set_ageing_time, .port_bridge_join = mv88e6xxx_port_bridge_join, .port_bridge_leave = mv88e6xxx_port_bridge_leave, + .port_egress_floods = mv88e6xxx_port_egress_floods, .port_stp_state_set = mv88e6xxx_port_stp_state_set, .port_fast_age = mv88e6xxx_port_fast_age, .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, @@ -4727,6 +5435,8 @@ .port_mdb_prepare = mv88e6xxx_port_mdb_prepare, .port_mdb_add = mv88e6xxx_port_mdb_add, .port_mdb_del = mv88e6xxx_port_mdb_del, + .port_mirror_add = mv88e6xxx_port_mirror_add, + .port_mirror_del = mv88e6xxx_port_mirror_del, .crosschip_bridge_join = mv88e6xxx_crosschip_bridge_join, .crosschip_bridge_leave = mv88e6xxx_crosschip_bridge_leave, .port_hwtstamp_set = mv88e6xxx_port_hwtstamp_set, @@ -4734,10 +5444,9 @@ .port_txtstamp = mv88e6xxx_port_txtstamp, .port_rxtstamp = mv88e6xxx_port_rxtstamp, .get_ts_info = mv88e6xxx_get_ts_info, -}; - -static struct dsa_switch_driver mv88e6xxx_switch_drv = { - .ops = &mv88e6xxx_switch_ops, + .devlink_param_get = mv88e6xxx_devlink_param_get, + .devlink_param_set = mv88e6xxx_devlink_param_set, + .devlink_info_get = mv88e6xxx_devlink_info_get, }; static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip) @@ -4745,10 +5454,12 @@ struct device *dev = chip->dev; struct dsa_switch *ds; - ds = dsa_switch_alloc(dev, mv88e6xxx_num_ports(chip)); + ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); if (!ds) return -ENOMEM; + ds->dev = dev; + ds->num_ports = mv88e6xxx_num_ports(chip); ds->priv = chip; ds->dev = dev; ds->ops = &mv88e6xxx_switch_ops; @@ -4777,6 +5488,21 @@ } return NULL; } + +/* There is no suspend to RAM support at DSA level yet, the switch configuration + * would be lost after a power cycle so prevent it to be suspended. + */ +static int __maybe_unused mv88e6xxx_suspend(struct device *dev) +{ + return -EOPNOTSUPP; +} + +static int __maybe_unused mv88e6xxx_resume(struct device *dev) +{ + return 0; +} + +static SIMPLE_DEV_PM_OPS(mv88e6xxx_pm_ops, mv88e6xxx_suspend, mv88e6xxx_resume); static int mv88e6xxx_probe(struct mdio_device *mdiodev) { @@ -4831,13 +5557,12 @@ goto out; } if (chip->reset) - usleep_range(1000, 2000); + usleep_range(10000, 20000); err = mv88e6xxx_detect(chip); if (err) goto out; - mv88e6xxx_ports_cmode_init(chip); mv88e6xxx_phy_init(chip); if (chip->info->ops->get_eeprom) { @@ -4848,28 +5573,33 @@ chip->eeprom_len = pdata->eeprom_len; } - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_switch_reset(chip); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); if (err) goto out; - chip->irq = of_irq_get(np, 0); - if (chip->irq == -EPROBE_DEFER) { - err = chip->irq; - goto out; + if (np) { + chip->irq = of_irq_get(np, 0); + if (chip->irq == -EPROBE_DEFER) { + err = chip->irq; + goto out; + } } + + if (pdata) + chip->irq = pdata->irq; /* Has to be performed before the MDIO bus is created, because * the PHYs will link their interrupts to these interrupt * controllers */ - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); if (chip->irq > 0) err = mv88e6xxx_g1_irq_setup(chip); else err = mv88e6xxx_irq_poll_setup(chip); - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); if (err) goto out; @@ -4954,6 +5684,10 @@ .compatible = "marvell,mv88e6190", .data = &mv88e6xxx_table[MV88E6190], }, + { + .compatible = "marvell,mv88e6250", + .data = &mv88e6xxx_table[MV88E6250], + }, { /* sentinel */ }, }; @@ -4965,22 +5699,11 @@ .mdiodrv.driver = { .name = "mv88e6085", .of_match_table = mv88e6xxx_of_match, + .pm = &mv88e6xxx_pm_ops, }, }; -static int __init mv88e6xxx_init(void) -{ - register_switch_driver(&mv88e6xxx_switch_drv); - return mdio_driver_register(&mv88e6xxx_driver); -} -module_init(mv88e6xxx_init); - -static void __exit mv88e6xxx_cleanup(void) -{ - mdio_driver_unregister(&mv88e6xxx_driver); - unregister_switch_driver(&mv88e6xxx_switch_drv); -} -module_exit(mv88e6xxx_cleanup); +mdio_module_driver(mv88e6xxx_driver); MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>"); MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips"); -- Gitblit v1.6.2