int mv88e6xxx_wait_mask(struct mv88e6xxx_chip *chip, int addr, int reg,
u16 mask, u16 val)
{ constunsignedlong timeout = jiffies + msecs_to_jiffies(50);
u16 data; int err; int i;
/* There's no bus specific operation to wait for a mask. Even * if the initial poll takes longer than 50ms, always do at * least one more attempt.
*/ for (i = 0; time_before(jiffies, timeout) || (i < 2); i++) {
err = mv88e6xxx_read(chip, addr, reg, &data); if (err) return err;
if ((data & mask) == val) return 0;
if (i < 2)
cpu_relax(); else
usleep_range(1000, 2000);
}
err = mv88e6xxx_read(chip, addr, reg, &data); if (err) return err;
if ((data & mask) == val) return 0;
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);
}
staticvoid mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
{ /* * free_irq must be called without reg_lock taken because the irq * handler takes this lock, too.
*/
free_irq(chip->irq, chip);
err = mv88e6xxx_g1_irq_setup_common(chip); if (err) return err;
/* These lock classes tells lockdep that global 1 irqs are in * a different category than their parent GPIO, so it won't * report false recursion.
*/
irq_set_lockdep_class(chip->irq, &lock_key, &request_key);
staticint mv88e6xxx_port_config_interface(struct mv88e6xxx_chip *chip, int port, phy_interface_t interface)
{ int 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;
}
if (chip->info->ops->port_set_cmode) {
err = chip->info->ops->port_set_cmode(chip, port,
interface); if (err && err != -EOPNOTSUPP) return err;
}
return 0;
}
staticint mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link, int speed, int duplex, int pause,
phy_interface_t mode)
{ int err;
if (!chip->info->ops->port_set_link) return 0;
/* Port's MAC control must not be changed unless the link is down */
err = chip->info->ops->port_set_link(chip, port, LINK_FORCED_DOWN); if (err) return err;
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 (chip->info->ops->port_set_pause) {
err = chip->info->ops->port_set_pause(chip, port, pause); if (err) goto restore_link;
}
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);
return err;
}
staticint mv88e6xxx_phy_is_internal(struct mv88e6xxx_chip *chip, int port)
{ return port >= chip->info->internal_phys_offset &&
port < chip->info->num_internal_phys +
chip->info->internal_phys_offset;
}
staticint mv88e6xxx_port_ppu_updates(struct mv88e6xxx_chip *chip, int port)
{
u16 reg; int err;
/* The 88e6250 family does not have the PHY detect bit. Instead, * report whether the port is internal.
*/ if (chip->info->family == MV88E6XXX_FAMILY_6250) return mv88e6xxx_phy_is_internal(chip, port);
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;
}
staticvoid
mv88e6250_setup_supported_interfaces(struct mv88e6xxx_chip *chip, int port, struct phylink_config *config)
{ unsignedlong *supported = config->supported_interfaces; int err;
u16 reg;
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); if (err) {
dev_err(chip->dev, "p%d: failed to read port status\n", port); return;
}
switch (reg & MV88E6250_PORT_STS_PORTMODE_MASK) { case MV88E6250_PORT_STS_PORTMODE_MII_10_HALF_PHY: case MV88E6250_PORT_STS_PORTMODE_MII_100_HALF_PHY: case MV88E6250_PORT_STS_PORTMODE_MII_10_FULL_PHY: case MV88E6250_PORT_STS_PORTMODE_MII_100_FULL_PHY:
__set_bit(PHY_INTERFACE_MODE_REVMII, supported); break;
case MV88E6250_PORT_STS_PORTMODE_MII_HALF: case MV88E6250_PORT_STS_PORTMODE_MII_FULL:
__set_bit(PHY_INTERFACE_MODE_MII, supported); break;
case MV88E6250_PORT_STS_PORTMODE_MII_DUAL_100_RMII_FULL_PHY: case MV88E6250_PORT_STS_PORTMODE_MII_200_RMII_FULL_PHY: case MV88E6250_PORT_STS_PORTMODE_MII_10_100_RMII_HALF_PHY: case MV88E6250_PORT_STS_PORTMODE_MII_10_100_RMII_FULL_PHY:
__set_bit(PHY_INTERFACE_MODE_REVRMII, supported); break;
case MV88E6250_PORT_STS_PORTMODE_MII_DUAL_100_RMII_FULL: case MV88E6250_PORT_STS_PORTMODE_MII_10_100_RMII_FULL:
__set_bit(PHY_INTERFACE_MODE_RMII, supported); break;
case MV88E6250_PORT_STS_PORTMODE_MII_100_RGMII:
__set_bit(PHY_INTERFACE_MODE_RGMII, supported); break;
default:
dev_err(chip->dev, "p%d: invalid port mode in status register: %04x\n",
port, reg);
}
}
staticvoid mv88e6250_phylink_get_caps(struct mv88e6xxx_chip *chip, int port, struct phylink_config *config)
{ if (!mv88e6xxx_phy_is_internal(chip, port))
mv88e6250_setup_supported_interfaces(chip, port, config);
/* Port 4 supports automedia if the serdes is associated with it. */ if (port == 4) {
err = mv88e6352_g2_scratch_port_has_serdes(chip, port); if (err < 0)
dev_err(chip->dev, "p%d: failed to read scratch\n",
port); if (err <= 0) return;
/* Translate the default cmode */
mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported);
/* No ethtool bits for 200Mbps */
config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 |
MAC_1000FD;
/* The C_Mode field is programmable on port 5 */ if (port == 5) {
__set_bit(PHY_INTERFACE_MODE_SGMII, supported);
__set_bit(PHY_INTERFACE_MODE_1000BASEX, supported);
__set_bit(PHY_INTERFACE_MODE_2500BASEX, supported);
/* Translate the default cmode */
mv88e6xxx_translate_cmode(chip->ports[port].cmode, supported);
/* No ethtool bits for 200Mbps */
config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 |
MAC_1000FD;
/* The C_Mode field is programmable on ports 9 and 10 */ if (port == 9 || port == 10) {
__set_bit(PHY_INTERFACE_MODE_SGMII, supported);
__set_bit(PHY_INTERFACE_MODE_1000BASEX, supported);
__set_bit(PHY_INTERFACE_MODE_2500BASEX, supported);
/* For the 6x90X, ports 2-7 can be in automedia mode. * (Note that 6x90 doesn't support RXAUI nor XAUI). * * Port 2 can also support 1000BASE-X in automedia mode if port 9 is * configured for 1000BASE-X, SGMII or 2500BASE-X. * Port 3-4 can also support 1000BASE-X in automedia mode if port 9 is * configured for RXAUI, 1000BASE-X, SGMII or 2500BASE-X. * * Port 5 can also support 1000BASE-X in automedia mode if port 10 is * configured for 1000BASE-X, SGMII or 2500BASE-X. * Port 6-7 can also support 1000BASE-X in automedia mode if port 10 is * configured for RXAUI, 1000BASE-X, SGMII or 2500BASE-X. * * For now, be permissive (as the old code was) and allow 1000BASE-X * on ports 2..7.
*/ if (port >= 2 && port <= 7)
__set_bit(PHY_INTERFACE_MODE_1000BASEX, supported);
/* The C_Mode field can also be programmed for 10G speeds */ if (port == 9 || port == 10) {
__set_bit(PHY_INTERFACE_MODE_XAUI, supported);
__set_bit(PHY_INTERFACE_MODE_RXAUI, supported);
/* The C_Mode field can be programmed for ports 0, 9 and 10 */ if (port == 0 || port == 9 || port == 10) {
__set_bit(PHY_INTERFACE_MODE_SGMII, supported);
__set_bit(PHY_INTERFACE_MODE_1000BASEX, supported);
/* 6191X supports >1G modes only on port 10 */ if (!is_6191x || port == 10) {
__set_bit(PHY_INTERFACE_MODE_2500BASEX, supported);
config->mac_capabilities |= MAC_2500FD;
/* 6361 only supports up to 2500BaseX */ if (!is_6361) {
__set_bit(PHY_INTERFACE_MODE_5GBASER, supported);
__set_bit(PHY_INTERFACE_MODE_10GBASER, supported);
__set_bit(PHY_INTERFACE_MODE_USXGMII, supported);
config->mac_capabilities |= MAC_5000FD |
MAC_10000FD;
}
}
}
if (mv88e6xxx_phy_is_internal(chip, port)) {
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
config->supported_interfaces); /* Internal ports with no phy-mode need GMII for PHYLIB */
__set_bit(PHY_INTERFACE_MODE_GMII,
config->supported_interfaces);
}
}
if (chip->info->ops->pcs_ops)
pcs = chip->info->ops->pcs_ops->pcs_select(chip, dp->index,
interface);
return pcs;
}
staticint mv88e6xxx_mac_prepare(struct phylink_config *config, unsignedint mode, phy_interface_t interface)
{ struct dsa_port *dp = dsa_phylink_to_port(config); struct mv88e6xxx_chip *chip = dp->ds->priv; int port = dp->index; int err = 0;
/* 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 &&
chip->ports[port].interface != interface &&
chip->info->ops->port_set_link) {
mv88e6xxx_reg_lock(chip);
err = chip->info->ops->port_set_link(chip, port,
LINK_FORCED_DOWN);
mv88e6xxx_reg_unlock(chip);
}
return err;
}
staticvoid mv88e6xxx_mac_config(struct phylink_config *config, unsignedint mode, conststruct phylink_link_state *state)
{ struct dsa_port *dp = dsa_phylink_to_port(config); struct mv88e6xxx_chip *chip = dp->ds->priv; int port = dp->index; int err = 0;
if (err && err != -EOPNOTSUPP)
dev_err(chip->dev, "p%d: failed to configure MAC/PCS\n", port);
}
staticint mv88e6xxx_mac_finish(struct phylink_config *config, unsignedint mode, phy_interface_t interface)
{ struct dsa_port *dp = dsa_phylink_to_port(config); struct mv88e6xxx_chip *chip = dp->ds->priv; int port = dp->index; int err = 0;
/* 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.)
*/
mv88e6xxx_reg_lock(chip);
staticvoid mv88e6xxx_mac_link_down(struct phylink_config *config, unsignedint mode,
phy_interface_t interface)
{ struct dsa_port *dp = dsa_phylink_to_port(config); struct mv88e6xxx_chip *chip = dp->ds->priv; conststruct mv88e6xxx_ops *ops; int port = dp->index; int err = 0;
ops = chip->info->ops;
mv88e6xxx_reg_lock(chip); /* Force the link down if we know the port may not be automatically * updated by the switch or if we are using fixed-link mode.
*/ if ((!mv88e6xxx_port_ppu_updates(chip, port) ||
mode == MLO_AN_FIXED) && ops->port_sync_link)
err = ops->port_sync_link(chip, port, mode, false);
if (err)
dev_err(chip->dev, "p%d: failed to force MAC link down\n", port);
}
staticvoid mv88e6xxx_mac_link_up(struct phylink_config *config, struct phy_device *phydev, unsignedint mode, phy_interface_t interface, int speed, int duplex, bool tx_pause, bool rx_pause)
{ struct dsa_port *dp = dsa_phylink_to_port(config); struct mv88e6xxx_chip *chip = dp->ds->priv; conststruct mv88e6xxx_ops *ops; int port = dp->index; int err = 0;
ops = chip->info->ops;
mv88e6xxx_reg_lock(chip); /* Configure and force the link up if we know that the port may not * automatically updated by the switch or if we are using fixed-link * mode.
*/ if (!mv88e6xxx_port_ppu_updates(chip, port) ||
mode == MLO_AN_FIXED) { if (ops->port_set_speed_duplex) {
err = ops->port_set_speed_duplex(chip, port,
speed, duplex); if (err && err != -EOPNOTSUPP) goto error;
}
staticint mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
{ 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;
}
staticvoid mv88e6xxx_get_regs(struct dsa_switch *ds, int port, struct ethtool_regs *regs, void *_p)
{ struct mv88e6xxx_chip *chip = ds->priv; int err;
u16 reg;
u16 *p = _p; int i;
regs->version = chip->info->prod_num;
memset(p, 0xff, 32 * sizeof(u16));
mv88e6xxx_reg_lock(chip);
for (i = 0; i < 32; i++) {
err = mv88e6xxx_port_read(chip, port, i, ®); if (!err)
p[i] = reg;
}
if (chip->info->ops->serdes_get_regs)
chip->info->ops->serdes_get_regs(chip, port, &p[i]);
mv88e6xxx_reg_unlock(chip);
}
staticint mv88e6xxx_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e)
{ /* Nothing to do on the port's MAC */ 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 = chip->ds; struct dsa_switch_tree *dst = ds->dst; struct dsa_port *dp, *other_dp; bool found = false;
u16 pvlan;
/* dev is a physical switch */ if (dev <= dst->last_switch) {
list_for_each_entry(dp, &dst->ports, list) { if (dp->ds->index == dev && dp->index == port) { /* dp might be a DSA link or a user port, so it * might or might not have a bridge. * Use the "found" variable for both cases.
*/
found = true; break;
}
} /* dev is a virtual bridge */
} else {
list_for_each_entry(dp, &dst->ports, list) { unsignedint bridge_num = dsa_port_bridge_num_get(dp);
if (!bridge_num) continue;
if (bridge_num + dst->last_switch != dev) continue;
found = true; break;
}
}
/* Prevent frames from unknown switch or virtual bridge */ if (!found) return 0;
/* Frames from DSA links and CPU ports can egress any local port */ if (dp->type == DSA_PORT_TYPE_CPU || dp->type == DSA_PORT_TYPE_DSA) return mv88e6xxx_port_mask(chip);
pvlan = 0;
/* Frames from standalone user ports can only egress on the * upstream port.
*/ if (!dsa_port_bridge_dev_get(dp)) return BIT(dsa_switch_upstream_port(ds));
/* Frames from bridged user ports can egress any local DSA * links and CPU ports, as well as any local member of their * bridge group.
*/
dsa_switch_for_each_port(other_dp, ds) if (other_dp->type == DSA_PORT_TYPE_CPU ||
other_dp->type == DSA_PORT_TYPE_DSA ||
dsa_port_bridge_same(dp, other_dp))
pvlan |= BIT(other_dp->index);
if (err)
dev_err(ds->dev, "p%d: failed to update state\n", port);
}
staticint mv88e6xxx_pri_setup(struct mv88e6xxx_chip *chip)
{ int err;
if (chip->info->ops->ieee_pri_map) {
err = chip->info->ops->ieee_pri_map(chip); if (err) return err;
}
if (chip->info->ops->ip_pri_map) {
err = chip->info->ops->ip_pri_map(chip); if (err) return err;
}
return 0;
}
staticint mv88e6xxx_devmap_setup(struct mv88e6xxx_chip *chip)
{ struct dsa_switch *ds = chip->ds; int target, port; int err;
if (!chip->info->global2_addr) return 0;
/* Initialize the routing port to the 32 possible target devices */ for (target = 0; target < 32; 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) return err;
}
if (chip->info->ops->set_cascade_port) {
port = MV88E6XXX_CASCADE_PORT_MULTIPLE;
err = chip->info->ops->set_cascade_port(chip, port); if (err) return err;
}
err = mv88e6xxx_g1_set_device_number(chip, chip->ds->index); if (err) return err;
return 0;
}
staticint mv88e6xxx_trunk_setup(struct mv88e6xxx_chip *chip)
{ /* Clear all trunk masks and mapping */ if (chip->info->global2_addr) return mv88e6xxx_g2_trunk_clear(chip);
return 0;
}
staticint mv88e6xxx_rmu_setup(struct mv88e6xxx_chip *chip)
{ if (chip->info->ops->rmu_disable) return chip->info->ops->rmu_disable(chip);
return 0;
}
staticint mv88e6xxx_pot_setup(struct mv88e6xxx_chip *chip)
{ if (chip->info->ops->pot_clear) return chip->info->ops->pot_clear(chip);
return 0;
}
staticint mv88e6xxx_rsvd2cpu_setup(struct mv88e6xxx_chip *chip)
{ if (chip->info->ops->mgmt_rsvd2cpu) return chip->info->ops->mgmt_rsvd2cpu(chip);
return 0;
}
staticint mv88e6xxx_atu_setup(struct mv88e6xxx_chip *chip)
{ int err;
err = mv88e6xxx_g1_atu_flush(chip, 0, true); if (err) return err;
/* The chips that have a "learn2all" bit in Global1, ATU * Control are precisely those whose port registers have a * Message Port bit in Port Control 1 and hence implement * ->port_setup_message_port.
*/ if (chip->info->ops->port_setup_message_port) {
err = mv88e6xxx_g1_atu_set_learn2all(chip, true); if (err) return err;
}
staticint mv88e6xxx_irl_setup(struct mv88e6xxx_chip *chip)
{ int port; int err;
if (!chip->info->ops->irl_init_all) return 0;
for (port = 0; port < mv88e6xxx_num_ports(chip); port++) { /* Disable ingress rate limiting by resetting all per port * ingress rate limit resources to their initial state.
*/
err = chip->info->ops->irl_init_all(chip, port); if (err) return err;
}
return 0;
}
staticint mv88e6xxx_mac_setup(struct mv88e6xxx_chip *chip)
{ if (chip->info->ops->set_switch_mac) {
u8 addr[ETH_ALEN];
/* Skip the local source device, which uses in-chip port VLAN */ if (dev != chip->ds->index) {
pvlan = mv88e6xxx_port_vlan(chip, dev, port);
ds = dsa_switch_find(dst->index, dev);
dp = ds ? dsa_to_port(ds, port) : NULL; if (dp && dp->lag) { /* As the PVT is used to limit flooding of * FORWARD frames, which use the LAG ID as the * source port, we must translate dev/port to * the special "LAG device" in the PVT, using * the LAG ID (one-based) as the port number * (zero-based).
*/
dev = MV88E6XXX_G2_PVT_ADDR_DEV_TRUNK;
port = dsa_port_lag_id_get(dp) - 1;
}
}
staticint mv88e6xxx_pvt_setup(struct mv88e6xxx_chip *chip)
{ int dev, port; int err;
if (!mv88e6xxx_has_pvt(chip)) return 0;
/* Clear 5 Bit Port for usage with Marvell Link Street devices: * use 4 bits for the Src_Port/Src_Trunk and 5 bits for the Src_Dev.
*/
err = mv88e6xxx_g2_misc_4_bit_port(chip); if (err) return err;
for (dev = 0; dev < MV88E6XXX_MAX_PVT_SWITCHES; ++dev) { for (port = 0; port < MV88E6XXX_MAX_PVT_PORTS; ++port) {
err = mv88e6xxx_pvt_map(chip, dev, port); if (err) return err;
}
}
return 0;
}
staticint mv88e6xxx_port_fast_age_fid(struct mv88e6xxx_chip *chip, int port,
u16 fid)
{ if (dsa_to_port(chip->ds, port)->lag) /* Hardware is incapable of fast-aging a LAG through a * regular ATU move operation. Until we have something * more fancy in place this is a no-op.
*/ return -EOPNOTSUPP;
/* Make sure that SID 0 is always valid. This is used by VTU * entries that do not make use of the STU, e.g. when creating * a VLAN upper on a port that is also part of a VLAN * filtering bridge.
*/ return mv88e6xxx_stu_loadpurge(chip, &stu);
}
/* If the SID is zero, it is for a VLAN mapped to the default MSTI, * and mv88e6xxx_stu_setup() made sure it is always present, and thus, * should not be removed here. * * If the chip lacks STU support, numerically the "sid" variable will * happen to also be zero, but we don't want to rely on that fact, so * we explicitly test that first. In that case, there is also nothing * to do here.
*/ if (!mv88e6xxx_has_stu(chip) || !sid) return 0;
list_for_each_entry_safe(mst, tmp, &chip->msts, node) { if (mst->stu.sid != sid) continue;
if (!refcount_dec_and_test(&mst->refcnt)) return 0;
/* The bridge starts out all ports in the disabled state. But * a STU state of disabled means to go by the port-global * state. So we set all user port's initial state to blocking, * to match the bridge's behavior.
*/ for (i = 0; i < mv88e6xxx_num_ports(chip); i++)
mst->stu.state[i] = dsa_is_user_port(chip->ds, i) ?
MV88E6XXX_PORT_CTL0_STATE_BLOCKING :
MV88E6XXX_PORT_CTL0_STATE_DISABLED;
err = mv88e6xxx_stu_loadpurge(chip, &mst->stu); if (err) goto err_free;
switch (st->state) { case BR_STATE_DISABLED: case BR_STATE_BLOCKING: case BR_STATE_LISTENING:
state = MV88E6XXX_PORT_CTL0_STATE_BLOCKING; break; case BR_STATE_LEARNING:
state = MV88E6XXX_PORT_CTL0_STATE_LEARNING; break; case BR_STATE_FORWARDING:
state = MV88E6XXX_PORT_CTL0_STATE_FORWARDING; break; default: return -EINVAL;
}
list_for_each_entry(mst, &chip->msts, node) { if (mst->br == dsa_port_bridge_dev_get(dp) &&
mst->msti == st->msti) { if (mst->stu.state[port] == state) return 0;
err = mv88e6xxx_port_set_8021q_mode(chip, port, mode); if (err) goto unlock;
err = mv88e6xxx_port_commit_pvid(chip, port); if (err) goto unlock;
unlock:
mv88e6xxx_reg_unlock(chip);
return err;
}
staticint
mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, conststruct switchdev_obj_port_vlan *vlan)
{ struct mv88e6xxx_chip *chip = ds->priv; int err;
if (!mv88e6xxx_max_vid(chip)) return -EOPNOTSUPP;
/* If the requested port doesn't belong to the same bridge as the VLAN * members, do not support it (yet) and fallback to software VLAN.
*/
mv88e6xxx_reg_lock(chip);
err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid);
mv88e6xxx_reg_unlock(chip);
/* Ports have two private address databases: one for when the port is * standalone and one for when the port is under a bridge and the * 802.1Q mode is disabled. When the port is standalone, DSA wants its * address database to remain 100% empty, so we never load an ATU entry * into a standalone port's database. Therefore, translate the null * VLAN ID into the port's database used for VLAN-unaware bridging.
*/ if (vid == 0) {
*fid = MV88E6XXX_FID_BRIDGED;
} else {
err = mv88e6xxx_vtu_get(chip, vid, &vlan); if (err) return err;
/* switchdev expects -EOPNOTSUPP to honor software VLANs */ if (!vlan.valid) return -EOPNOTSUPP;
/* Initialize a fresh ATU entry if it isn't found */ 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) {
entry.portvec &= ~BIT(port); if (!entry.portvec)
entry.state = 0;
} else { if (state == MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC)
entry.portvec = BIT(port); else
entry.portvec |= BIT(port);
staticint mv88e6xxx_policy_apply(struct mv88e6xxx_chip *chip, int port, conststruct 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 */ elseif (action == MV88E6XXX_POLICY_ACTION_DISCARD &&
is_multicast_ether_addr(addr))
state = MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_POLICY; elseif (action == MV88E6XXX_POLICY_ACTION_DISCARD &&
is_unicast_ether_addr(addr))
state = MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_POLICY; else 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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.