2019-05-27 14:55:01 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2016-11-04 10:23:26 +08:00
|
|
|
/*
|
|
|
|
* Marvell 88E6xxx Switch Port Registers support
|
|
|
|
*
|
|
|
|
* Copyright (c) 2008 Marvell Semiconductor
|
|
|
|
*
|
2017-03-29 03:10:36 +08:00
|
|
|
* Copyright (c) 2016-2017 Savoir-faire Linux Inc.
|
|
|
|
* Vivien Didelot <vivien.didelot@savoirfairelinux.com>
|
2016-11-04 10:23:26 +08:00
|
|
|
*/
|
|
|
|
|
2017-06-18 11:07:14 +08:00
|
|
|
#include <linux/bitfield.h>
|
2017-06-09 06:34:10 +08:00
|
|
|
#include <linux/if_bridge.h>
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
#include <linux/phy.h>
|
2018-05-11 04:17:35 +08:00
|
|
|
#include <linux/phylink.h>
|
2017-06-03 05:06:15 +08:00
|
|
|
|
|
|
|
#include "chip.h"
|
2021-03-17 21:46:42 +08:00
|
|
|
#include "global2.h"
|
2016-11-04 10:23:26 +08:00
|
|
|
#include "port.h"
|
2018-08-09 21:38:46 +08:00
|
|
|
#include "serdes.h"
|
2016-11-04 10:23:26 +08:00
|
|
|
|
|
|
|
int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
|
|
|
|
u16 *val)
|
|
|
|
{
|
|
|
|
int addr = chip->info->port_base_addr + port;
|
|
|
|
|
|
|
|
return mv88e6xxx_read(chip, addr, reg, val);
|
|
|
|
}
|
|
|
|
|
2021-03-17 21:46:42 +08:00
|
|
|
int mv88e6xxx_port_wait_bit(struct mv88e6xxx_chip *chip, int port, int reg,
|
|
|
|
int bit, int val)
|
|
|
|
{
|
|
|
|
int addr = chip->info->port_base_addr + port;
|
|
|
|
|
|
|
|
return mv88e6xxx_wait_bit(chip, addr, reg, bit, val);
|
|
|
|
}
|
|
|
|
|
2016-11-04 10:23:26 +08:00
|
|
|
int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
|
|
|
|
u16 val)
|
|
|
|
{
|
|
|
|
int addr = chip->info->port_base_addr + port;
|
|
|
|
|
|
|
|
return mv88e6xxx_write(chip, addr, reg, val);
|
|
|
|
}
|
2016-11-04 10:23:27 +08:00
|
|
|
|
2018-08-09 21:38:37 +08:00
|
|
|
/* Offset 0x00: MAC (or PCS or Physical) Status Register
|
|
|
|
*
|
|
|
|
* For most devices, this is read only. However the 6185 has the MyPause
|
|
|
|
* bit read/write.
|
|
|
|
*/
|
|
|
|
int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
int pause)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (pause)
|
|
|
|
reg |= MV88E6XXX_PORT_STS_MY_PAUSE;
|
|
|
|
else
|
|
|
|
reg &= ~MV88E6XXX_PORT_STS_MY_PAUSE;
|
|
|
|
|
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg);
|
|
|
|
}
|
|
|
|
|
2016-11-04 10:23:32 +08:00
|
|
|
/* Offset 0x01: MAC (or PCS or Physical) Control Register
|
|
|
|
*
|
|
|
|
* Link, Duplex and Flow Control have one force bit, one value bit.
|
2016-11-04 10:23:35 +08:00
|
|
|
*
|
|
|
|
* For port's MAC speed, ForceSpd (or SpdValue) bits 1:0 program the value.
|
|
|
|
* Alternative values require the 200BASE (or AltSpeed) bit 12 set.
|
|
|
|
* Newer chips need a ForcedSpd bit 13 set to consider the value.
|
2016-11-04 10:23:32 +08:00
|
|
|
*/
|
|
|
|
|
2016-11-04 10:23:34 +08:00
|
|
|
static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
phy_interface_t mode)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
int err;
|
|
|
|
|
2017-06-13 00:37:34 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®);
|
2016-11-04 10:23:34 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:34 +08:00
|
|
|
reg &= ~(MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK |
|
|
|
|
MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK);
|
2016-11-04 10:23:34 +08:00
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case PHY_INTERFACE_MODE_RGMII_RXID:
|
2017-06-13 00:37:34 +08:00
|
|
|
reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK;
|
2016-11-04 10:23:34 +08:00
|
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_RGMII_TXID:
|
2017-06-13 00:37:34 +08:00
|
|
|
reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK;
|
2016-11-04 10:23:34 +08:00
|
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_RGMII_ID:
|
2017-06-13 00:37:34 +08:00
|
|
|
reg |= MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK |
|
|
|
|
MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK;
|
2016-11-04 10:23:34 +08:00
|
|
|
break;
|
2016-11-10 22:44:00 +08:00
|
|
|
case PHY_INTERFACE_MODE_RGMII:
|
2016-11-04 10:23:34 +08:00
|
|
|
break;
|
2016-11-10 22:44:00 +08:00
|
|
|
default:
|
|
|
|
return 0;
|
2016-11-04 10:23:34 +08:00
|
|
|
}
|
|
|
|
|
2017-06-13 00:37:34 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
|
2016-11-04 10:23:34 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-09 06:34:08 +08:00
|
|
|
dev_dbg(chip->dev, "p%d: delay RXCLK %s, TXCLK %s\n", port,
|
2017-06-13 00:37:34 +08:00
|
|
|
reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK ? "yes" : "no",
|
|
|
|
reg & MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK ? "yes" : "no");
|
2016-11-04 10:23:34 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
phy_interface_t mode)
|
|
|
|
{
|
|
|
|
if (port < 5)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return mv88e6xxx_port_set_rgmii_delay(chip, port, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
phy_interface_t mode)
|
|
|
|
{
|
|
|
|
if (port != 0)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
return mv88e6xxx_port_set_rgmii_delay(chip, port, mode);
|
|
|
|
}
|
|
|
|
|
2016-11-04 10:23:32 +08:00
|
|
|
int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
int err;
|
|
|
|
|
2017-06-13 00:37:34 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®);
|
2016-11-04 10:23:32 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:34 +08:00
|
|
|
reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_LINK |
|
|
|
|
MV88E6XXX_PORT_MAC_CTL_LINK_UP);
|
2016-11-04 10:23:32 +08:00
|
|
|
|
|
|
|
switch (link) {
|
|
|
|
case LINK_FORCED_DOWN:
|
2017-06-13 00:37:34 +08:00
|
|
|
reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_LINK;
|
2016-11-04 10:23:32 +08:00
|
|
|
break;
|
|
|
|
case LINK_FORCED_UP:
|
2017-06-13 00:37:34 +08:00
|
|
|
reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_LINK |
|
|
|
|
MV88E6XXX_PORT_MAC_CTL_LINK_UP;
|
2016-11-04 10:23:32 +08:00
|
|
|
break;
|
|
|
|
case LINK_UNFORCED:
|
|
|
|
/* normal link detection */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-06-13 00:37:34 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
|
2016-11-04 10:23:32 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-09 06:34:08 +08:00
|
|
|
dev_dbg(chip->dev, "p%d: %s link %s\n", port,
|
2017-06-13 00:37:34 +08:00
|
|
|
reg & MV88E6XXX_PORT_MAC_CTL_FORCE_LINK ? "Force" : "Unforce",
|
|
|
|
reg & MV88E6XXX_PORT_MAC_CTL_LINK_UP ? "up" : "down");
|
2016-11-04 10:23:32 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-24 12:34:37 +08:00
|
|
|
int mv88e6xxx_port_sync_link(struct mv88e6xxx_chip *chip, int port, unsigned int mode, bool isup)
|
|
|
|
{
|
|
|
|
const struct mv88e6xxx_ops *ops = chip->info->ops;
|
|
|
|
int err = 0;
|
|
|
|
int link;
|
|
|
|
|
|
|
|
if (isup)
|
|
|
|
link = LINK_FORCED_UP;
|
|
|
|
else
|
|
|
|
link = LINK_FORCED_DOWN;
|
|
|
|
|
|
|
|
if (ops->port_set_link)
|
|
|
|
err = ops->port_set_link(chip, port, link);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6185_port_sync_link(struct mv88e6xxx_chip *chip, int port, unsigned int mode, bool isup)
|
|
|
|
{
|
|
|
|
const struct mv88e6xxx_ops *ops = chip->info->ops;
|
|
|
|
int err = 0;
|
|
|
|
int link;
|
|
|
|
|
|
|
|
if (mode == MLO_AN_INBAND)
|
|
|
|
link = LINK_UNFORCED;
|
|
|
|
else if (isup)
|
|
|
|
link = LINK_FORCED_UP;
|
|
|
|
else
|
|
|
|
link = LINK_FORCED_DOWN;
|
|
|
|
|
|
|
|
if (ops->port_set_link)
|
|
|
|
err = ops->port_set_link(chip, port, link);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2020-03-14 18:15:53 +08:00
|
|
|
static int mv88e6xxx_port_set_speed_duplex(struct mv88e6xxx_chip *chip,
|
|
|
|
int port, int speed, bool alt_bit,
|
|
|
|
bool force_bit, int duplex)
|
2016-11-04 10:23:35 +08:00
|
|
|
{
|
|
|
|
u16 reg, ctrl;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
switch (speed) {
|
|
|
|
case 10:
|
2017-06-13 00:37:34 +08:00
|
|
|
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_10;
|
2016-11-04 10:23:35 +08:00
|
|
|
break;
|
|
|
|
case 100:
|
2017-06-13 00:37:34 +08:00
|
|
|
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100;
|
2016-11-04 10:23:35 +08:00
|
|
|
break;
|
|
|
|
case 200:
|
|
|
|
if (alt_bit)
|
2017-06-13 00:37:34 +08:00
|
|
|
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100 |
|
|
|
|
MV88E6390_PORT_MAC_CTL_ALTSPEED;
|
2016-11-04 10:23:35 +08:00
|
|
|
else
|
2017-06-13 00:37:34 +08:00
|
|
|
ctrl = MV88E6065_PORT_MAC_CTL_SPEED_200;
|
2016-11-04 10:23:35 +08:00
|
|
|
break;
|
|
|
|
case 1000:
|
2017-06-13 00:37:34 +08:00
|
|
|
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000;
|
2016-11-04 10:23:35 +08:00
|
|
|
break;
|
|
|
|
case 2500:
|
2018-10-13 20:40:31 +08:00
|
|
|
if (alt_bit)
|
|
|
|
ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000 |
|
|
|
|
MV88E6390_PORT_MAC_CTL_ALTSPEED;
|
|
|
|
else
|
|
|
|
ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000;
|
2016-11-04 10:23:35 +08:00
|
|
|
break;
|
|
|
|
case 10000:
|
|
|
|
/* all bits set, fall through... */
|
|
|
|
case SPEED_UNFORCED:
|
2017-06-13 00:37:34 +08:00
|
|
|
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_UNFORCED;
|
2016-11-04 10:23:35 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2020-03-14 18:15:53 +08:00
|
|
|
switch (duplex) {
|
|
|
|
case DUPLEX_HALF:
|
|
|
|
ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX;
|
|
|
|
break;
|
|
|
|
case DUPLEX_FULL:
|
|
|
|
ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
|
|
|
|
MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL;
|
|
|
|
break;
|
|
|
|
case DUPLEX_UNFORCED:
|
|
|
|
/* normal duplex detection */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2017-06-13 00:37:34 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®);
|
2016-11-04 10:23:35 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2020-03-14 18:15:53 +08:00
|
|
|
reg &= ~(MV88E6XXX_PORT_MAC_CTL_SPEED_MASK |
|
|
|
|
MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
|
|
|
|
MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL);
|
|
|
|
|
2016-11-04 10:23:35 +08:00
|
|
|
if (alt_bit)
|
2017-06-13 00:37:34 +08:00
|
|
|
reg &= ~MV88E6390_PORT_MAC_CTL_ALTSPEED;
|
2016-11-04 10:23:35 +08:00
|
|
|
if (force_bit) {
|
2017-06-13 00:37:34 +08:00
|
|
|
reg &= ~MV88E6390_PORT_MAC_CTL_FORCE_SPEED;
|
2016-11-16 11:26:48 +08:00
|
|
|
if (speed != SPEED_UNFORCED)
|
2017-06-13 00:37:34 +08:00
|
|
|
ctrl |= MV88E6390_PORT_MAC_CTL_FORCE_SPEED;
|
2016-11-04 10:23:35 +08:00
|
|
|
}
|
|
|
|
reg |= ctrl;
|
|
|
|
|
2017-06-13 00:37:34 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
|
2016-11-04 10:23:35 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2021-12-16 01:30:32 +08:00
|
|
|
if (speed != SPEED_UNFORCED)
|
2017-06-09 06:34:08 +08:00
|
|
|
dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed);
|
2016-11-04 10:23:35 +08:00
|
|
|
else
|
2017-06-09 06:34:08 +08:00
|
|
|
dev_dbg(chip->dev, "p%d: Speed unforced\n", port);
|
2020-03-14 18:15:53 +08:00
|
|
|
dev_dbg(chip->dev, "p%d: %s %s duplex\n", port,
|
|
|
|
reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce",
|
|
|
|
reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half");
|
2016-11-04 10:23:35 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */
|
2020-03-14 18:15:53 +08:00
|
|
|
int mv88e6065_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
int speed, int duplex)
|
2016-11-04 10:23:35 +08:00
|
|
|
{
|
|
|
|
if (speed == SPEED_MAX)
|
|
|
|
speed = 200;
|
|
|
|
|
|
|
|
if (speed > 200)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
/* Setting 200 Mbps on port 0 to 3 selects 100 Mbps */
|
2020-03-14 18:15:53 +08:00
|
|
|
return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false,
|
|
|
|
duplex);
|
2016-11-04 10:23:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */
|
2020-03-14 18:15:53 +08:00
|
|
|
int mv88e6185_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
int speed, int duplex)
|
2016-11-04 10:23:35 +08:00
|
|
|
{
|
|
|
|
if (speed == SPEED_MAX)
|
|
|
|
speed = 1000;
|
|
|
|
|
|
|
|
if (speed == 200 || speed > 1000)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2020-03-14 18:15:53 +08:00
|
|
|
return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false,
|
|
|
|
duplex);
|
2016-11-04 10:23:35 +08:00
|
|
|
}
|
|
|
|
|
2019-06-04 15:34:29 +08:00
|
|
|
/* Support 10, 100 Mbps (e.g. 88E6250 family) */
|
2020-03-14 18:15:53 +08:00
|
|
|
int mv88e6250_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
int speed, int duplex)
|
2019-06-04 15:34:29 +08:00
|
|
|
{
|
|
|
|
if (speed == SPEED_MAX)
|
|
|
|
speed = 100;
|
|
|
|
|
|
|
|
if (speed > 100)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2020-03-14 18:15:53 +08:00
|
|
|
return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false,
|
|
|
|
duplex);
|
2019-06-04 15:34:29 +08:00
|
|
|
}
|
|
|
|
|
2018-10-13 20:40:31 +08:00
|
|
|
/* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6341) */
|
2020-03-14 18:15:53 +08:00
|
|
|
int mv88e6341_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
int speed, int duplex)
|
2018-10-13 20:40:31 +08:00
|
|
|
{
|
|
|
|
if (speed == SPEED_MAX)
|
|
|
|
speed = port < 5 ? 1000 : 2500;
|
|
|
|
|
|
|
|
if (speed > 2500)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (speed == 200 && port != 0)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (speed == 2500 && port < 5)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2020-03-14 18:15:53 +08:00
|
|
|
return mv88e6xxx_port_set_speed_duplex(chip, port, speed, !port, true,
|
|
|
|
duplex);
|
2018-10-13 20:40:31 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 08:21:27 +08:00
|
|
|
phy_interface_t mv88e6341_port_max_speed_mode(int port)
|
|
|
|
{
|
|
|
|
if (port == 5)
|
|
|
|
return PHY_INTERFACE_MODE_2500BASEX;
|
|
|
|
|
|
|
|
return PHY_INTERFACE_MODE_NA;
|
|
|
|
}
|
|
|
|
|
2016-11-04 10:23:35 +08:00
|
|
|
/* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */
|
2020-03-14 18:15:53 +08:00
|
|
|
int mv88e6352_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
int speed, int duplex)
|
2016-11-04 10:23:35 +08:00
|
|
|
{
|
|
|
|
if (speed == SPEED_MAX)
|
|
|
|
speed = 1000;
|
|
|
|
|
|
|
|
if (speed > 1000)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (speed == 200 && port < 5)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2020-03-14 18:15:53 +08:00
|
|
|
return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, false,
|
|
|
|
duplex);
|
2016-11-04 10:23:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */
|
2020-03-14 18:15:53 +08:00
|
|
|
int mv88e6390_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
int speed, int duplex)
|
2016-11-04 10:23:35 +08:00
|
|
|
{
|
|
|
|
if (speed == SPEED_MAX)
|
|
|
|
speed = port < 9 ? 1000 : 2500;
|
|
|
|
|
|
|
|
if (speed > 2500)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (speed == 200 && port != 0)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (speed == 2500 && port < 9)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2020-03-14 18:15:53 +08:00
|
|
|
return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, true,
|
|
|
|
duplex);
|
2016-11-04 10:23:35 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 08:21:27 +08:00
|
|
|
phy_interface_t mv88e6390_port_max_speed_mode(int port)
|
|
|
|
{
|
|
|
|
if (port == 9 || port == 10)
|
|
|
|
return PHY_INTERFACE_MODE_2500BASEX;
|
|
|
|
|
|
|
|
return PHY_INTERFACE_MODE_NA;
|
|
|
|
}
|
|
|
|
|
2016-11-04 10:23:35 +08:00
|
|
|
/* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */
|
2020-03-14 18:15:53 +08:00
|
|
|
int mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
int speed, int duplex)
|
2016-11-04 10:23:35 +08:00
|
|
|
{
|
|
|
|
if (speed == SPEED_MAX)
|
|
|
|
speed = port < 9 ? 1000 : 10000;
|
|
|
|
|
|
|
|
if (speed == 200 && port != 0)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (speed >= 2500 && port < 9)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2020-03-14 18:15:53 +08:00
|
|
|
return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, true,
|
|
|
|
duplex);
|
2016-11-04 10:23:35 +08:00
|
|
|
}
|
|
|
|
|
2019-03-08 08:21:27 +08:00
|
|
|
phy_interface_t mv88e6390x_port_max_speed_mode(int port)
|
|
|
|
{
|
|
|
|
if (port == 9 || port == 10)
|
|
|
|
return PHY_INTERFACE_MODE_XAUI;
|
|
|
|
|
|
|
|
return PHY_INTERFACE_MODE_NA;
|
|
|
|
}
|
|
|
|
|
2021-03-17 21:46:42 +08:00
|
|
|
/* Support 10, 100, 200, 1000, 2500, 5000, 10000 Mbps (e.g. 88E6393X)
|
|
|
|
* Function mv88e6xxx_port_set_speed_duplex() can't be used as the register
|
|
|
|
* values for speeds 2500 & 5000 conflict.
|
|
|
|
*/
|
|
|
|
int mv88e6393x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
int speed, int duplex)
|
|
|
|
{
|
|
|
|
u16 reg, ctrl;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (speed == SPEED_MAX)
|
|
|
|
speed = (port > 0 && port < 9) ? 1000 : 10000;
|
|
|
|
|
|
|
|
if (speed == 200 && port != 0)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (speed >= 2500 && port > 0 && port < 9)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
switch (speed) {
|
|
|
|
case 10:
|
|
|
|
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_10;
|
|
|
|
break;
|
|
|
|
case 100:
|
|
|
|
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100;
|
|
|
|
break;
|
|
|
|
case 200:
|
|
|
|
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_100 |
|
|
|
|
MV88E6390_PORT_MAC_CTL_ALTSPEED;
|
|
|
|
break;
|
|
|
|
case 1000:
|
|
|
|
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000;
|
|
|
|
break;
|
|
|
|
case 2500:
|
|
|
|
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_1000 |
|
|
|
|
MV88E6390_PORT_MAC_CTL_ALTSPEED;
|
|
|
|
break;
|
|
|
|
case 5000:
|
|
|
|
ctrl = MV88E6390_PORT_MAC_CTL_SPEED_10000 |
|
|
|
|
MV88E6390_PORT_MAC_CTL_ALTSPEED;
|
|
|
|
break;
|
|
|
|
case 10000:
|
|
|
|
case SPEED_UNFORCED:
|
|
|
|
ctrl = MV88E6XXX_PORT_MAC_CTL_SPEED_UNFORCED;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (duplex) {
|
|
|
|
case DUPLEX_HALF:
|
|
|
|
ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX;
|
|
|
|
break;
|
|
|
|
case DUPLEX_FULL:
|
|
|
|
ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX |
|
|
|
|
MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL;
|
|
|
|
break;
|
|
|
|
case DUPLEX_UNFORCED:
|
|
|
|
/* normal duplex detection */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
reg &= ~(MV88E6XXX_PORT_MAC_CTL_SPEED_MASK |
|
|
|
|
MV88E6390_PORT_MAC_CTL_ALTSPEED |
|
|
|
|
MV88E6390_PORT_MAC_CTL_FORCE_SPEED);
|
|
|
|
|
|
|
|
if (speed != SPEED_UNFORCED)
|
|
|
|
reg |= MV88E6390_PORT_MAC_CTL_FORCE_SPEED;
|
|
|
|
|
|
|
|
reg |= ctrl;
|
|
|
|
|
|
|
|
err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2021-12-16 01:30:32 +08:00
|
|
|
if (speed != SPEED_UNFORCED)
|
2021-03-17 21:46:42 +08:00
|
|
|
dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed);
|
|
|
|
else
|
|
|
|
dev_dbg(chip->dev, "p%d: Speed unforced\n", port);
|
|
|
|
dev_dbg(chip->dev, "p%d: %s %s duplex\n", port,
|
|
|
|
reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce",
|
|
|
|
reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
phy_interface_t mv88e6393x_port_max_speed_mode(int port)
|
|
|
|
{
|
|
|
|
if (port == 0 || port == 9 || port == 10)
|
|
|
|
return PHY_INTERFACE_MODE_10GBASER;
|
|
|
|
|
|
|
|
return PHY_INTERFACE_MODE_NA;
|
|
|
|
}
|
|
|
|
|
2019-08-27 05:31:55 +08:00
|
|
|
static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
|
2019-12-19 17:48:22 +08:00
|
|
|
phy_interface_t mode, bool force)
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
{
|
|
|
|
u16 cmode;
|
2021-03-17 21:46:40 +08:00
|
|
|
int lane;
|
2018-08-09 21:38:49 +08:00
|
|
|
u16 reg;
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
int err;
|
|
|
|
|
2018-11-11 07:32:16 +08:00
|
|
|
/* Default to a slow mode, so freeing up SERDES interfaces for
|
|
|
|
* other ports which might use them for SFPs.
|
|
|
|
*/
|
|
|
|
if (mode == PHY_INTERFACE_MODE_NA)
|
|
|
|
mode = PHY_INTERFACE_MODE_1000BASEX;
|
|
|
|
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
switch (mode) {
|
|
|
|
case PHY_INTERFACE_MODE_1000BASEX:
|
2019-08-27 05:31:54 +08:00
|
|
|
cmode = MV88E6XXX_PORT_STS_CMODE_1000BASEX;
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_SGMII:
|
2017-06-13 00:37:33 +08:00
|
|
|
cmode = MV88E6XXX_PORT_STS_CMODE_SGMII;
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_2500BASEX:
|
2017-06-13 00:37:33 +08:00
|
|
|
cmode = MV88E6XXX_PORT_STS_CMODE_2500BASEX;
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
break;
|
2021-03-17 21:46:42 +08:00
|
|
|
case PHY_INTERFACE_MODE_5GBASER:
|
|
|
|
cmode = MV88E6393X_PORT_STS_CMODE_5GBASER;
|
|
|
|
break;
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
case PHY_INTERFACE_MODE_XGMII:
|
2017-12-12 17:29:46 +08:00
|
|
|
case PHY_INTERFACE_MODE_XAUI:
|
2017-06-13 00:37:33 +08:00
|
|
|
cmode = MV88E6XXX_PORT_STS_CMODE_XAUI;
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_RXAUI:
|
2017-06-13 00:37:33 +08:00
|
|
|
cmode = MV88E6XXX_PORT_STS_CMODE_RXAUI;
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
break;
|
2021-03-17 21:46:42 +08:00
|
|
|
case PHY_INTERFACE_MODE_10GBASER:
|
|
|
|
cmode = MV88E6393X_PORT_STS_CMODE_10GBASER;
|
|
|
|
break;
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
default:
|
|
|
|
cmode = 0;
|
|
|
|
}
|
|
|
|
|
2019-12-19 17:48:22 +08:00
|
|
|
/* cmode doesn't change, nothing to do for us unless forced */
|
|
|
|
if (cmode == chip->ports[port].cmode && !force)
|
2019-02-28 14:39:15 +08:00
|
|
|
return 0;
|
|
|
|
|
2019-09-01 04:18:30 +08:00
|
|
|
lane = mv88e6xxx_serdes_get_lane(chip, port);
|
2021-03-17 21:46:40 +08:00
|
|
|
if (lane >= 0) {
|
2019-03-24 02:41:32 +08:00
|
|
|
if (chip->ports[port].serdes_irq) {
|
2019-09-01 04:18:34 +08:00
|
|
|
err = mv88e6xxx_serdes_irq_disable(chip, port, lane);
|
2019-03-24 02:41:32 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-09-01 04:18:33 +08:00
|
|
|
err = mv88e6xxx_serdes_power_down(chip, port, lane);
|
2018-08-09 21:38:49 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-03-24 02:41:32 +08:00
|
|
|
chip->ports[port].cmode = 0;
|
2018-08-09 21:38:46 +08:00
|
|
|
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
if (cmode) {
|
2017-06-13 00:37:33 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®);
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:33 +08:00
|
|
|
reg &= ~MV88E6XXX_PORT_STS_CMODE_MASK;
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
reg |= cmode;
|
|
|
|
|
2017-06-13 00:37:33 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg);
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
2018-08-09 21:38:46 +08:00
|
|
|
|
2019-03-24 02:41:32 +08:00
|
|
|
chip->ports[port].cmode = cmode;
|
|
|
|
|
2019-09-01 04:18:30 +08:00
|
|
|
lane = mv88e6xxx_serdes_get_lane(chip, port);
|
2021-03-17 21:46:40 +08:00
|
|
|
if (lane < 0)
|
|
|
|
return lane;
|
2019-03-24 02:41:32 +08:00
|
|
|
|
2019-09-01 04:18:33 +08:00
|
|
|
err = mv88e6xxx_serdes_power_up(chip, port, lane);
|
2018-08-09 21:38:46 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
2018-08-09 21:38:49 +08:00
|
|
|
|
|
|
|
if (chip->ports[port].serdes_irq) {
|
2019-09-01 04:18:34 +08:00
|
|
|
err = mv88e6xxx_serdes_irq_enable(chip, port, lane);
|
2018-08-09 21:38:49 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-08-27 05:31:55 +08:00
|
|
|
int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
phy_interface_t mode)
|
|
|
|
{
|
|
|
|
if (port != 9 && port != 10)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2019-12-19 17:48:22 +08:00
|
|
|
return mv88e6xxx_port_set_cmode(chip, port, mode, false);
|
2019-08-27 05:31:55 +08:00
|
|
|
}
|
|
|
|
|
2018-11-11 07:32:15 +08:00
|
|
|
int mv88e6390_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
phy_interface_t mode)
|
|
|
|
{
|
2019-08-27 05:31:55 +08:00
|
|
|
if (port != 9 && port != 10)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case PHY_INTERFACE_MODE_NA:
|
|
|
|
return 0;
|
|
|
|
case PHY_INTERFACE_MODE_XGMII:
|
|
|
|
case PHY_INTERFACE_MODE_XAUI:
|
|
|
|
case PHY_INTERFACE_MODE_RXAUI:
|
|
|
|
return -EINVAL;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-12-19 17:48:22 +08:00
|
|
|
return mv88e6xxx_port_set_cmode(chip, port, mode, false);
|
2019-08-27 05:31:55 +08:00
|
|
|
}
|
|
|
|
|
2021-03-17 21:46:42 +08:00
|
|
|
int mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
phy_interface_t mode)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
u16 reg;
|
|
|
|
|
|
|
|
if (port != 0 && port != 9 && port != 10)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
/* mv88e6393x errata 4.5: EEE should be disabled on SERDES ports */
|
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
reg &= ~MV88E6XXX_PORT_MAC_CTL_EEE;
|
|
|
|
reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_EEE;
|
|
|
|
err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
return mv88e6xxx_port_set_cmode(chip, port, mode, false);
|
|
|
|
}
|
|
|
|
|
2019-08-29 00:26:59 +08:00
|
|
|
static int mv88e6341_port_set_cmode_writable(struct mv88e6xxx_chip *chip,
|
|
|
|
int port)
|
2019-08-27 05:31:55 +08:00
|
|
|
{
|
|
|
|
int err, addr;
|
|
|
|
u16 reg, bits;
|
|
|
|
|
|
|
|
if (port != 5)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
addr = chip->info->port_base_addr + port;
|
|
|
|
|
|
|
|
err = mv88e6xxx_port_hidden_read(chip, 0x7, addr, 0, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
bits = MV88E6341_PORT_RESERVED_1A_FORCE_CMODE |
|
|
|
|
MV88E6341_PORT_RESERVED_1A_SGMII_AN;
|
|
|
|
|
|
|
|
if ((reg & bits) == bits)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
reg |= bits;
|
|
|
|
return mv88e6xxx_port_hidden_write(chip, 0x7, addr, 0, reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6341_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
phy_interface_t mode)
|
|
|
|
{
|
2019-08-29 00:26:59 +08:00
|
|
|
int err;
|
|
|
|
|
2019-08-27 05:31:55 +08:00
|
|
|
if (port != 5)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2018-11-11 07:32:15 +08:00
|
|
|
switch (mode) {
|
2019-02-25 19:39:54 +08:00
|
|
|
case PHY_INTERFACE_MODE_NA:
|
|
|
|
return 0;
|
2018-11-11 07:32:15 +08:00
|
|
|
case PHY_INTERFACE_MODE_XGMII:
|
|
|
|
case PHY_INTERFACE_MODE_XAUI:
|
|
|
|
case PHY_INTERFACE_MODE_RXAUI:
|
|
|
|
return -EINVAL;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-08-29 00:26:59 +08:00
|
|
|
err = mv88e6341_port_set_cmode_writable(chip, port);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2019-12-19 17:48:22 +08:00
|
|
|
return mv88e6xxx_port_set_cmode(chip, port, mode, true);
|
2018-11-11 07:32:15 +08:00
|
|
|
}
|
|
|
|
|
2018-08-09 21:38:45 +08:00
|
|
|
int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
|
2018-08-09 21:38:39 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
u16 reg;
|
|
|
|
|
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2018-08-09 21:38:45 +08:00
|
|
|
*cmode = reg & MV88E6185_PORT_STS_CMODE_MASK;
|
|
|
|
|
|
|
|
return 0;
|
2018-08-09 21:38:39 +08:00
|
|
|
}
|
|
|
|
|
2018-08-09 21:38:45 +08:00
|
|
|
int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
u16 reg;
|
|
|
|
|
2017-06-13 00:37:33 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®);
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:33 +08:00
|
|
|
*cmode = reg & MV88E6XXX_PORT_STS_CMODE_MASK;
|
net: dsa: mv88e6xxx: Set the CMODE for mv88e6390 ports 9 & 10
Unlike most ports, ports 9 and 10 of the 6390X family have configurable
PHY modes. Set the mode as part of adjust_link().
Ordering is important, because the SERDES interfaces connected to
ports 9 and 10 can be split and assigned to other ports. The CMODE has
to be correctly set before the SERDES interface on another port can be
configured. Such configuration is likely to be performed in
port_enable() and port_disabled(), called on slave_open() and
slave_close().
The simple case is port 9 and 10 are used for 'CPU' or 'DSA'. In this
case, the CMODE is set via a phy-mode in dsa_cpu_dsa_setup(), which is
called early in the switch setup.
When ports 9 or 10 are used as user ports, and have a fixed-phy, when
the fixed fixed-phy is attached, dsa_slave_adjust_link() is called,
which results in the adjust_link function being called, setting the
cmode. The port_enable() will for other ports will be called much
later.
When ports 9 or 10 are used as user ports and have a real phy attached
which does not use all the available SERDES interface, e.g. a 1Gbps
SGMII, there is currently no mechanism in place to set the CMODE of
the port from software. It must be hoped the stripping resistors are
correct.
At the same time, add a function to get the cmode. This will be needed
when configuring the SERDES interfaces.
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-02-05 03:02:50 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-13 00:37:35 +08:00
|
|
|
/* Offset 0x02: Jamming Control
|
2016-12-03 11:45:19 +08:00
|
|
|
*
|
|
|
|
* Do not limit the period of time that this port can be paused for by
|
|
|
|
* the remote end or the period of time that this port can pause the
|
|
|
|
* remote end.
|
|
|
|
*/
|
2017-06-09 06:34:12 +08:00
|
|
|
int mv88e6097_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
|
|
|
|
u8 out)
|
2016-12-03 11:45:19 +08:00
|
|
|
{
|
2017-06-13 00:37:35 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6097_PORT_JAM_CTL,
|
|
|
|
out << 8 | in);
|
2016-12-03 11:45:19 +08:00
|
|
|
}
|
|
|
|
|
2017-06-09 06:34:12 +08:00
|
|
|
int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
|
|
|
|
u8 out)
|
2016-12-03 11:45:20 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2017-06-13 00:37:35 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, MV88E6390_PORT_FLOW_CTL,
|
|
|
|
MV88E6390_PORT_FLOW_CTL_UPDATE |
|
|
|
|
MV88E6390_PORT_FLOW_CTL_LIMIT_IN | in);
|
2016-12-03 11:45:20 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:35 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6390_PORT_FLOW_CTL,
|
|
|
|
MV88E6390_PORT_FLOW_CTL_UPDATE |
|
|
|
|
MV88E6390_PORT_FLOW_CTL_LIMIT_OUT | out);
|
2016-12-03 11:45:20 +08:00
|
|
|
}
|
|
|
|
|
2016-11-04 10:23:27 +08:00
|
|
|
/* Offset 0x04: Port Control Register */
|
|
|
|
|
|
|
|
static const char * const mv88e6xxx_port_state_names[] = {
|
2017-06-13 00:37:37 +08:00
|
|
|
[MV88E6XXX_PORT_CTL0_STATE_DISABLED] = "Disabled",
|
|
|
|
[MV88E6XXX_PORT_CTL0_STATE_BLOCKING] = "Blocking/Listening",
|
|
|
|
[MV88E6XXX_PORT_CTL0_STATE_LEARNING] = "Learning",
|
|
|
|
[MV88E6XXX_PORT_CTL0_STATE_FORWARDING] = "Forwarding",
|
2016-11-04 10:23:27 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
int err;
|
|
|
|
|
2017-06-13 00:37:37 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, ®);
|
2016-11-04 10:23:27 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:37 +08:00
|
|
|
reg &= ~MV88E6XXX_PORT_CTL0_STATE_MASK;
|
2017-06-09 06:34:10 +08:00
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case BR_STATE_DISABLED:
|
2017-06-13 00:37:37 +08:00
|
|
|
state = MV88E6XXX_PORT_CTL0_STATE_DISABLED;
|
2017-06-09 06:34:10 +08:00
|
|
|
break;
|
|
|
|
case BR_STATE_BLOCKING:
|
|
|
|
case BR_STATE_LISTENING:
|
2017-06-13 00:37:37 +08:00
|
|
|
state = MV88E6XXX_PORT_CTL0_STATE_BLOCKING;
|
2017-06-09 06:34:10 +08:00
|
|
|
break;
|
|
|
|
case BR_STATE_LEARNING:
|
2017-06-13 00:37:37 +08:00
|
|
|
state = MV88E6XXX_PORT_CTL0_STATE_LEARNING;
|
2017-06-09 06:34:10 +08:00
|
|
|
break;
|
|
|
|
case BR_STATE_FORWARDING:
|
2017-06-13 00:37:37 +08:00
|
|
|
state = MV88E6XXX_PORT_CTL0_STATE_FORWARDING;
|
2017-06-09 06:34:10 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2016-11-04 10:23:27 +08:00
|
|
|
reg |= state;
|
|
|
|
|
2017-06-13 00:37:37 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
|
2016-11-04 10:23:27 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-09 06:34:08 +08:00
|
|
|
dev_dbg(chip->dev, "p%d: PortState set to %s\n", port,
|
|
|
|
mv88e6xxx_port_state_names[state]);
|
2016-11-04 10:23:27 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-11-04 10:23:28 +08:00
|
|
|
|
2016-12-03 11:35:19 +08:00
|
|
|
int mv88e6xxx_port_set_egress_mode(struct mv88e6xxx_chip *chip, int port,
|
2017-06-09 06:34:09 +08:00
|
|
|
enum mv88e6xxx_egress_mode mode)
|
2016-12-03 11:35:19 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
u16 reg;
|
|
|
|
|
2017-06-13 00:37:37 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, ®);
|
2016-12-03 11:35:19 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:37 +08:00
|
|
|
reg &= ~MV88E6XXX_PORT_CTL0_EGRESS_MODE_MASK;
|
2017-06-09 06:34:09 +08:00
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case MV88E6XXX_EGRESS_MODE_UNMODIFIED:
|
2017-06-13 00:37:37 +08:00
|
|
|
reg |= MV88E6XXX_PORT_CTL0_EGRESS_MODE_UNMODIFIED;
|
2017-06-09 06:34:09 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_EGRESS_MODE_UNTAGGED:
|
2017-06-13 00:37:37 +08:00
|
|
|
reg |= MV88E6XXX_PORT_CTL0_EGRESS_MODE_UNTAGGED;
|
2017-06-09 06:34:09 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_EGRESS_MODE_TAGGED:
|
2017-06-13 00:37:37 +08:00
|
|
|
reg |= MV88E6XXX_PORT_CTL0_EGRESS_MODE_TAGGED;
|
2017-06-09 06:34:09 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_EGRESS_MODE_ETHERTYPE:
|
2017-06-13 00:37:37 +08:00
|
|
|
reg |= MV88E6XXX_PORT_CTL0_EGRESS_MODE_ETHER_TYPE_DSA;
|
2017-06-09 06:34:09 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2016-12-03 11:35:19 +08:00
|
|
|
|
2017-06-13 00:37:37 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
|
2016-12-03 11:35:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6085_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
enum mv88e6xxx_frame_mode mode)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
u16 reg;
|
|
|
|
|
2017-06-13 00:37:37 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, ®);
|
2016-12-03 11:35:19 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:37 +08:00
|
|
|
reg &= ~MV88E6XXX_PORT_CTL0_FRAME_MODE_MASK;
|
2016-12-03 11:35:19 +08:00
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case MV88E6XXX_FRAME_MODE_NORMAL:
|
2017-06-13 00:37:37 +08:00
|
|
|
reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_NORMAL;
|
2016-12-03 11:35:19 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_FRAME_MODE_DSA:
|
2017-06-13 00:37:37 +08:00
|
|
|
reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_DSA;
|
2016-12-03 11:35:19 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-06-13 00:37:37 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
|
2016-12-03 11:35:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
enum mv88e6xxx_frame_mode mode)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
u16 reg;
|
|
|
|
|
2017-06-13 00:37:37 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, ®);
|
2016-12-03 11:35:19 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:37 +08:00
|
|
|
reg &= ~MV88E6XXX_PORT_CTL0_FRAME_MODE_MASK;
|
2016-12-03 11:35:19 +08:00
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case MV88E6XXX_FRAME_MODE_NORMAL:
|
2017-06-13 00:37:37 +08:00
|
|
|
reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_NORMAL;
|
2016-12-03 11:35:19 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_FRAME_MODE_DSA:
|
2017-06-13 00:37:37 +08:00
|
|
|
reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_DSA;
|
2016-12-03 11:35:19 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_FRAME_MODE_PROVIDER:
|
2017-06-13 00:37:37 +08:00
|
|
|
reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_PROVIDER;
|
2016-12-03 11:35:19 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_FRAME_MODE_ETHERTYPE:
|
2017-06-13 00:37:37 +08:00
|
|
|
reg |= MV88E6XXX_PORT_CTL0_FRAME_MODE_ETHER_TYPE_DSA;
|
2016-12-03 11:35:19 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-06-13 00:37:37 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
|
2016-12-03 11:35:19 +08:00
|
|
|
}
|
|
|
|
|
net: dsa: act as passthrough for bridge port flags
There are multiple ways in which a PORT_BRIDGE_FLAGS attribute can be
expressed by the bridge through switchdev, and not all of them can be
emulated by DSA mid-layer API at the same time.
One possible configuration is when the bridge offloads the port flags
using a mask that has a single bit set - therefore only one feature
should change. However, DSA currently groups together unicast and
multicast flooding in the .port_egress_floods method, which limits our
options when we try to add support for turning off broadcast flooding:
do we extend .port_egress_floods with a third parameter which b53 and
mv88e6xxx will ignore? But that means that the DSA layer, which
currently implements the PRE_BRIDGE_FLAGS attribute all by itself, will
see that .port_egress_floods is implemented, and will report that all 3
types of flooding are supported - not necessarily true.
Another configuration is when the user specifies more than one flag at
the same time, in the same netlink message. If we were to create one
individual function per offloadable bridge port flag, we would limit the
expressiveness of the switch driver of refusing certain combinations of
flag values. For example, a switch may not have an explicit knob for
flooding of unknown multicast, just for flooding in general. In that
case, the only correct thing to do is to allow changes to BR_FLOOD and
BR_MCAST_FLOOD in tandem, and never allow mismatched values. But having
a separate .port_set_unicast_flood and .port_set_multicast_flood would
not allow the driver to possibly reject that.
Also, DSA doesn't consider it necessary to inform the driver that a
SWITCHDEV_ATTR_ID_BRIDGE_MROUTER attribute was offloaded, because it
just calls .port_egress_floods for the CPU port. When we'll add support
for the plain SWITCHDEV_ATTR_ID_PORT_MROUTER, that will become a real
problem because the flood settings will need to be held statefully in
the DSA middle layer, otherwise changing the mrouter port attribute will
impact the flooding attribute. And that's _assuming_ that the underlying
hardware doesn't have anything else to do when a multicast router
attaches to a port than flood unknown traffic to it. If it does, there
will need to be a dedicated .port_set_mrouter anyway.
So we need to let the DSA drivers see the exact form that the bridge
passes this switchdev attribute in, otherwise we are standing in the
way. Therefore we also need to use this form of language when
communicating to the driver that it needs to configure its initial
(before bridge join) and final (after bridge leave) port flags.
The b53 and mv88e6xxx drivers are converted to the passthrough API and
their implementation of .port_egress_floods is split into two: a
function that configures unicast flooding and another for multicast.
The mv88e6xxx implementation is quite hairy, and it turns out that
the implementations of unknown unicast flooding are actually the same
for 6185 and for 6352:
behind the confusing names actually lie two individual bits:
NO_UNKNOWN_MC -> FLOOD_UC = 0x4 = BIT(2)
NO_UNKNOWN_UC -> FLOOD_MC = 0x8 = BIT(3)
so there was no reason to entangle them in the first place.
Whereas the 6185 writes to MV88E6185_PORT_CTL0_FORWARD_UNKNOWN of
PORT_CTL0, which has the exact same bit index. I have left the
implementations separate though, for the only reason that the names are
different enough to confuse me, since I am not able to double-check with
a user manual. The multicast flooding setting for 6185 is in a different
register than for 6352 though.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2021-02-12 23:15:56 +08:00
|
|
|
int mv88e6185_port_set_forward_unknown(struct mv88e6xxx_chip *chip,
|
|
|
|
int port, bool unicast)
|
2016-12-03 11:35:19 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
u16 reg;
|
|
|
|
|
2017-06-13 00:37:37 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, ®);
|
2016-12-03 11:35:19 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-03-12 05:13:00 +08:00
|
|
|
if (unicast)
|
2017-06-13 00:37:37 +08:00
|
|
|
reg |= MV88E6185_PORT_CTL0_FORWARD_UNKNOWN;
|
2016-12-03 11:35:19 +08:00
|
|
|
else
|
2017-06-13 00:37:37 +08:00
|
|
|
reg &= ~MV88E6185_PORT_CTL0_FORWARD_UNKNOWN;
|
2016-12-03 11:35:19 +08:00
|
|
|
|
2017-06-13 00:37:37 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
|
2016-12-03 11:35:19 +08:00
|
|
|
}
|
|
|
|
|
net: dsa: act as passthrough for bridge port flags
There are multiple ways in which a PORT_BRIDGE_FLAGS attribute can be
expressed by the bridge through switchdev, and not all of them can be
emulated by DSA mid-layer API at the same time.
One possible configuration is when the bridge offloads the port flags
using a mask that has a single bit set - therefore only one feature
should change. However, DSA currently groups together unicast and
multicast flooding in the .port_egress_floods method, which limits our
options when we try to add support for turning off broadcast flooding:
do we extend .port_egress_floods with a third parameter which b53 and
mv88e6xxx will ignore? But that means that the DSA layer, which
currently implements the PRE_BRIDGE_FLAGS attribute all by itself, will
see that .port_egress_floods is implemented, and will report that all 3
types of flooding are supported - not necessarily true.
Another configuration is when the user specifies more than one flag at
the same time, in the same netlink message. If we were to create one
individual function per offloadable bridge port flag, we would limit the
expressiveness of the switch driver of refusing certain combinations of
flag values. For example, a switch may not have an explicit knob for
flooding of unknown multicast, just for flooding in general. In that
case, the only correct thing to do is to allow changes to BR_FLOOD and
BR_MCAST_FLOOD in tandem, and never allow mismatched values. But having
a separate .port_set_unicast_flood and .port_set_multicast_flood would
not allow the driver to possibly reject that.
Also, DSA doesn't consider it necessary to inform the driver that a
SWITCHDEV_ATTR_ID_BRIDGE_MROUTER attribute was offloaded, because it
just calls .port_egress_floods for the CPU port. When we'll add support
for the plain SWITCHDEV_ATTR_ID_PORT_MROUTER, that will become a real
problem because the flood settings will need to be held statefully in
the DSA middle layer, otherwise changing the mrouter port attribute will
impact the flooding attribute. And that's _assuming_ that the underlying
hardware doesn't have anything else to do when a multicast router
attaches to a port than flood unknown traffic to it. If it does, there
will need to be a dedicated .port_set_mrouter anyway.
So we need to let the DSA drivers see the exact form that the bridge
passes this switchdev attribute in, otherwise we are standing in the
way. Therefore we also need to use this form of language when
communicating to the driver that it needs to configure its initial
(before bridge join) and final (after bridge leave) port flags.
The b53 and mv88e6xxx drivers are converted to the passthrough API and
their implementation of .port_egress_floods is split into two: a
function that configures unicast flooding and another for multicast.
The mv88e6xxx implementation is quite hairy, and it turns out that
the implementations of unknown unicast flooding are actually the same
for 6185 and for 6352:
behind the confusing names actually lie two individual bits:
NO_UNKNOWN_MC -> FLOOD_UC = 0x4 = BIT(2)
NO_UNKNOWN_UC -> FLOOD_MC = 0x8 = BIT(3)
so there was no reason to entangle them in the first place.
Whereas the 6185 writes to MV88E6185_PORT_CTL0_FORWARD_UNKNOWN of
PORT_CTL0, which has the exact same bit index. I have left the
implementations separate though, for the only reason that the names are
different enough to confuse me, since I am not able to double-check with
a user manual. The multicast flooding setting for 6185 is in a different
register than for 6352 though.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2021-02-12 23:15:56 +08:00
|
|
|
int mv88e6352_port_set_ucast_flood(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
bool unicast)
|
2016-12-03 11:35:19 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
u16 reg;
|
|
|
|
|
2017-06-13 00:37:37 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, ®);
|
2016-12-03 11:35:19 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
net: dsa: act as passthrough for bridge port flags
There are multiple ways in which a PORT_BRIDGE_FLAGS attribute can be
expressed by the bridge through switchdev, and not all of them can be
emulated by DSA mid-layer API at the same time.
One possible configuration is when the bridge offloads the port flags
using a mask that has a single bit set - therefore only one feature
should change. However, DSA currently groups together unicast and
multicast flooding in the .port_egress_floods method, which limits our
options when we try to add support for turning off broadcast flooding:
do we extend .port_egress_floods with a third parameter which b53 and
mv88e6xxx will ignore? But that means that the DSA layer, which
currently implements the PRE_BRIDGE_FLAGS attribute all by itself, will
see that .port_egress_floods is implemented, and will report that all 3
types of flooding are supported - not necessarily true.
Another configuration is when the user specifies more than one flag at
the same time, in the same netlink message. If we were to create one
individual function per offloadable bridge port flag, we would limit the
expressiveness of the switch driver of refusing certain combinations of
flag values. For example, a switch may not have an explicit knob for
flooding of unknown multicast, just for flooding in general. In that
case, the only correct thing to do is to allow changes to BR_FLOOD and
BR_MCAST_FLOOD in tandem, and never allow mismatched values. But having
a separate .port_set_unicast_flood and .port_set_multicast_flood would
not allow the driver to possibly reject that.
Also, DSA doesn't consider it necessary to inform the driver that a
SWITCHDEV_ATTR_ID_BRIDGE_MROUTER attribute was offloaded, because it
just calls .port_egress_floods for the CPU port. When we'll add support
for the plain SWITCHDEV_ATTR_ID_PORT_MROUTER, that will become a real
problem because the flood settings will need to be held statefully in
the DSA middle layer, otherwise changing the mrouter port attribute will
impact the flooding attribute. And that's _assuming_ that the underlying
hardware doesn't have anything else to do when a multicast router
attaches to a port than flood unknown traffic to it. If it does, there
will need to be a dedicated .port_set_mrouter anyway.
So we need to let the DSA drivers see the exact form that the bridge
passes this switchdev attribute in, otherwise we are standing in the
way. Therefore we also need to use this form of language when
communicating to the driver that it needs to configure its initial
(before bridge join) and final (after bridge leave) port flags.
The b53 and mv88e6xxx drivers are converted to the passthrough API and
their implementation of .port_egress_floods is split into two: a
function that configures unicast flooding and another for multicast.
The mv88e6xxx implementation is quite hairy, and it turns out that
the implementations of unknown unicast flooding are actually the same
for 6185 and for 6352:
behind the confusing names actually lie two individual bits:
NO_UNKNOWN_MC -> FLOOD_UC = 0x4 = BIT(2)
NO_UNKNOWN_UC -> FLOOD_MC = 0x8 = BIT(3)
so there was no reason to entangle them in the first place.
Whereas the 6185 writes to MV88E6185_PORT_CTL0_FORWARD_UNKNOWN of
PORT_CTL0, which has the exact same bit index. I have left the
implementations separate though, for the only reason that the names are
different enough to confuse me, since I am not able to double-check with
a user manual. The multicast flooding setting for 6185 is in a different
register than for 6352 though.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2021-02-12 23:15:56 +08:00
|
|
|
if (unicast)
|
|
|
|
reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_UC;
|
|
|
|
else
|
|
|
|
reg &= ~MV88E6352_PORT_CTL0_EGRESS_FLOODS_UC;
|
|
|
|
|
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6352_port_set_mcast_flood(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
bool multicast)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
u16 reg;
|
|
|
|
|
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL0, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2017-03-12 05:13:00 +08:00
|
|
|
|
net: dsa: act as passthrough for bridge port flags
There are multiple ways in which a PORT_BRIDGE_FLAGS attribute can be
expressed by the bridge through switchdev, and not all of them can be
emulated by DSA mid-layer API at the same time.
One possible configuration is when the bridge offloads the port flags
using a mask that has a single bit set - therefore only one feature
should change. However, DSA currently groups together unicast and
multicast flooding in the .port_egress_floods method, which limits our
options when we try to add support for turning off broadcast flooding:
do we extend .port_egress_floods with a third parameter which b53 and
mv88e6xxx will ignore? But that means that the DSA layer, which
currently implements the PRE_BRIDGE_FLAGS attribute all by itself, will
see that .port_egress_floods is implemented, and will report that all 3
types of flooding are supported - not necessarily true.
Another configuration is when the user specifies more than one flag at
the same time, in the same netlink message. If we were to create one
individual function per offloadable bridge port flag, we would limit the
expressiveness of the switch driver of refusing certain combinations of
flag values. For example, a switch may not have an explicit knob for
flooding of unknown multicast, just for flooding in general. In that
case, the only correct thing to do is to allow changes to BR_FLOOD and
BR_MCAST_FLOOD in tandem, and never allow mismatched values. But having
a separate .port_set_unicast_flood and .port_set_multicast_flood would
not allow the driver to possibly reject that.
Also, DSA doesn't consider it necessary to inform the driver that a
SWITCHDEV_ATTR_ID_BRIDGE_MROUTER attribute was offloaded, because it
just calls .port_egress_floods for the CPU port. When we'll add support
for the plain SWITCHDEV_ATTR_ID_PORT_MROUTER, that will become a real
problem because the flood settings will need to be held statefully in
the DSA middle layer, otherwise changing the mrouter port attribute will
impact the flooding attribute. And that's _assuming_ that the underlying
hardware doesn't have anything else to do when a multicast router
attaches to a port than flood unknown traffic to it. If it does, there
will need to be a dedicated .port_set_mrouter anyway.
So we need to let the DSA drivers see the exact form that the bridge
passes this switchdev attribute in, otherwise we are standing in the
way. Therefore we also need to use this form of language when
communicating to the driver that it needs to configure its initial
(before bridge join) and final (after bridge leave) port flags.
The b53 and mv88e6xxx drivers are converted to the passthrough API and
their implementation of .port_egress_floods is split into two: a
function that configures unicast flooding and another for multicast.
The mv88e6xxx implementation is quite hairy, and it turns out that
the implementations of unknown unicast flooding are actually the same
for 6185 and for 6352:
behind the confusing names actually lie two individual bits:
NO_UNKNOWN_MC -> FLOOD_UC = 0x4 = BIT(2)
NO_UNKNOWN_UC -> FLOOD_MC = 0x8 = BIT(3)
so there was no reason to entangle them in the first place.
Whereas the 6185 writes to MV88E6185_PORT_CTL0_FORWARD_UNKNOWN of
PORT_CTL0, which has the exact same bit index. I have left the
implementations separate though, for the only reason that the names are
different enough to confuse me, since I am not able to double-check with
a user manual. The multicast flooding setting for 6185 is in a different
register than for 6352 though.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2021-02-12 23:15:56 +08:00
|
|
|
if (multicast)
|
|
|
|
reg |= MV88E6352_PORT_CTL0_EGRESS_FLOODS_MC;
|
2016-12-03 11:35:19 +08:00
|
|
|
else
|
net: dsa: act as passthrough for bridge port flags
There are multiple ways in which a PORT_BRIDGE_FLAGS attribute can be
expressed by the bridge through switchdev, and not all of them can be
emulated by DSA mid-layer API at the same time.
One possible configuration is when the bridge offloads the port flags
using a mask that has a single bit set - therefore only one feature
should change. However, DSA currently groups together unicast and
multicast flooding in the .port_egress_floods method, which limits our
options when we try to add support for turning off broadcast flooding:
do we extend .port_egress_floods with a third parameter which b53 and
mv88e6xxx will ignore? But that means that the DSA layer, which
currently implements the PRE_BRIDGE_FLAGS attribute all by itself, will
see that .port_egress_floods is implemented, and will report that all 3
types of flooding are supported - not necessarily true.
Another configuration is when the user specifies more than one flag at
the same time, in the same netlink message. If we were to create one
individual function per offloadable bridge port flag, we would limit the
expressiveness of the switch driver of refusing certain combinations of
flag values. For example, a switch may not have an explicit knob for
flooding of unknown multicast, just for flooding in general. In that
case, the only correct thing to do is to allow changes to BR_FLOOD and
BR_MCAST_FLOOD in tandem, and never allow mismatched values. But having
a separate .port_set_unicast_flood and .port_set_multicast_flood would
not allow the driver to possibly reject that.
Also, DSA doesn't consider it necessary to inform the driver that a
SWITCHDEV_ATTR_ID_BRIDGE_MROUTER attribute was offloaded, because it
just calls .port_egress_floods for the CPU port. When we'll add support
for the plain SWITCHDEV_ATTR_ID_PORT_MROUTER, that will become a real
problem because the flood settings will need to be held statefully in
the DSA middle layer, otherwise changing the mrouter port attribute will
impact the flooding attribute. And that's _assuming_ that the underlying
hardware doesn't have anything else to do when a multicast router
attaches to a port than flood unknown traffic to it. If it does, there
will need to be a dedicated .port_set_mrouter anyway.
So we need to let the DSA drivers see the exact form that the bridge
passes this switchdev attribute in, otherwise we are standing in the
way. Therefore we also need to use this form of language when
communicating to the driver that it needs to configure its initial
(before bridge join) and final (after bridge leave) port flags.
The b53 and mv88e6xxx drivers are converted to the passthrough API and
their implementation of .port_egress_floods is split into two: a
function that configures unicast flooding and another for multicast.
The mv88e6xxx implementation is quite hairy, and it turns out that
the implementations of unknown unicast flooding are actually the same
for 6185 and for 6352:
behind the confusing names actually lie two individual bits:
NO_UNKNOWN_MC -> FLOOD_UC = 0x4 = BIT(2)
NO_UNKNOWN_UC -> FLOOD_MC = 0x8 = BIT(3)
so there was no reason to entangle them in the first place.
Whereas the 6185 writes to MV88E6185_PORT_CTL0_FORWARD_UNKNOWN of
PORT_CTL0, which has the exact same bit index. I have left the
implementations separate though, for the only reason that the names are
different enough to confuse me, since I am not able to double-check with
a user manual. The multicast flooding setting for 6185 is in a different
register than for 6352 though.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2021-02-12 23:15:56 +08:00
|
|
|
reg &= ~MV88E6352_PORT_CTL0_EGRESS_FLOODS_MC;
|
2016-12-03 11:35:19 +08:00
|
|
|
|
2017-06-13 00:37:37 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
|
2016-12-03 11:35:19 +08:00
|
|
|
}
|
|
|
|
|
2016-11-04 10:23:29 +08:00
|
|
|
/* Offset 0x05: Port Control 1 */
|
|
|
|
|
2017-03-12 05:12:50 +08:00
|
|
|
int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
bool message_port)
|
|
|
|
{
|
|
|
|
u16 val;
|
|
|
|
int err;
|
|
|
|
|
2017-06-13 00:37:38 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1, &val);
|
2017-03-12 05:12:50 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (message_port)
|
2017-06-13 00:37:38 +08:00
|
|
|
val |= MV88E6XXX_PORT_CTL1_MESSAGE_PORT;
|
2017-03-12 05:12:50 +08:00
|
|
|
else
|
2017-06-13 00:37:38 +08:00
|
|
|
val &= ~MV88E6XXX_PORT_CTL1_MESSAGE_PORT;
|
2017-03-12 05:12:50 +08:00
|
|
|
|
2017-06-13 00:37:38 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val);
|
2021-01-13 16:42:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6xxx_port_set_trunk(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
bool trunk, u8 id)
|
|
|
|
{
|
|
|
|
u16 val;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1, &val);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
val &= ~MV88E6XXX_PORT_CTL1_TRUNK_ID_MASK;
|
|
|
|
|
|
|
|
if (trunk)
|
|
|
|
val |= MV88E6XXX_PORT_CTL1_TRUNK_PORT |
|
|
|
|
(id << MV88E6XXX_PORT_CTL1_TRUNK_ID_SHIFT);
|
|
|
|
else
|
|
|
|
val &= ~MV88E6XXX_PORT_CTL1_TRUNK_PORT;
|
|
|
|
|
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val);
|
2017-03-12 05:12:50 +08:00
|
|
|
}
|
|
|
|
|
2016-11-04 10:23:28 +08:00
|
|
|
/* Offset 0x06: Port Based VLAN Map */
|
|
|
|
|
|
|
|
int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)
|
|
|
|
{
|
2017-03-12 05:12:47 +08:00
|
|
|
const u16 mask = mv88e6xxx_port_mask(chip);
|
2016-11-04 10:23:28 +08:00
|
|
|
u16 reg;
|
|
|
|
int err;
|
|
|
|
|
2017-06-13 00:37:39 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_BASE_VLAN, ®);
|
2016-11-04 10:23:28 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
reg &= ~mask;
|
|
|
|
reg |= map & mask;
|
|
|
|
|
2017-06-13 00:37:39 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_BASE_VLAN, reg);
|
2016-11-04 10:23:28 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-09 06:34:08 +08:00
|
|
|
dev_dbg(chip->dev, "p%d: VLANTable set to %.3x\n", port, map);
|
2016-11-04 10:23:28 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-11-04 10:23:29 +08:00
|
|
|
|
|
|
|
int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid)
|
|
|
|
{
|
|
|
|
const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
|
|
|
|
u16 reg;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
|
2017-06-13 00:37:39 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_BASE_VLAN, ®);
|
2016-11-04 10:23:29 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
*fid = (reg & 0xf000) >> 12;
|
|
|
|
|
|
|
|
/* Port's default FID upper bits are located in reg 0x05, offset 0 */
|
|
|
|
if (upper_mask) {
|
2017-06-13 00:37:38 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1,
|
|
|
|
®);
|
2016-11-04 10:23:29 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
*fid |= (reg & upper_mask) << 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid)
|
|
|
|
{
|
|
|
|
const u16 upper_mask = (mv88e6xxx_num_databases(chip) - 1) >> 4;
|
|
|
|
u16 reg;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (fid >= mv88e6xxx_num_databases(chip))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Port's default FID lower 4 bits are located in reg 0x06, offset 12 */
|
2017-06-13 00:37:39 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_BASE_VLAN, ®);
|
2016-11-04 10:23:29 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
reg &= 0x0fff;
|
|
|
|
reg |= (fid & 0x000f) << 12;
|
|
|
|
|
2017-06-13 00:37:39 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_BASE_VLAN, reg);
|
2016-11-04 10:23:29 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* Port's default FID upper bits are located in reg 0x05, offset 0 */
|
|
|
|
if (upper_mask) {
|
2017-06-13 00:37:38 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1,
|
|
|
|
®);
|
2016-11-04 10:23:29 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
reg &= ~upper_mask;
|
|
|
|
reg |= (fid >> 4) & upper_mask;
|
|
|
|
|
2017-06-13 00:37:38 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1,
|
|
|
|
reg);
|
2016-11-04 10:23:29 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2017-06-09 06:34:08 +08:00
|
|
|
dev_dbg(chip->dev, "p%d: FID set to %u\n", port, fid);
|
2016-11-04 10:23:29 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-11-04 10:23:30 +08:00
|
|
|
|
|
|
|
/* Offset 0x07: Default Port VLAN ID & Priority */
|
|
|
|
|
|
|
|
int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
int err;
|
|
|
|
|
2017-06-13 00:37:40 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_DEFAULT_VLAN,
|
|
|
|
®);
|
2016-11-04 10:23:30 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:40 +08:00
|
|
|
*pvid = reg & MV88E6XXX_PORT_DEFAULT_VLAN_MASK;
|
2016-11-04 10:23:30 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
int err;
|
|
|
|
|
2017-06-13 00:37:40 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_DEFAULT_VLAN,
|
|
|
|
®);
|
2016-11-04 10:23:30 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:40 +08:00
|
|
|
reg &= ~MV88E6XXX_PORT_DEFAULT_VLAN_MASK;
|
|
|
|
reg |= pvid & MV88E6XXX_PORT_DEFAULT_VLAN_MASK;
|
2016-11-04 10:23:30 +08:00
|
|
|
|
2017-06-13 00:37:40 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_DEFAULT_VLAN,
|
|
|
|
reg);
|
2016-11-04 10:23:30 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-09 06:34:08 +08:00
|
|
|
dev_dbg(chip->dev, "p%d: DefaultVID set to %u\n", port, pvid);
|
2016-11-04 10:23:30 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-11-04 10:23:31 +08:00
|
|
|
|
|
|
|
/* Offset 0x08: Port Control 2 Register */
|
|
|
|
|
|
|
|
static const char * const mv88e6xxx_port_8021q_mode_names[] = {
|
2017-06-13 00:37:41 +08:00
|
|
|
[MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED] = "Disabled",
|
|
|
|
[MV88E6XXX_PORT_CTL2_8021Q_MODE_FALLBACK] = "Fallback",
|
|
|
|
[MV88E6XXX_PORT_CTL2_8021Q_MODE_CHECK] = "Check",
|
|
|
|
[MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE] = "Secure",
|
2016-11-04 10:23:31 +08:00
|
|
|
};
|
|
|
|
|
net: dsa: act as passthrough for bridge port flags
There are multiple ways in which a PORT_BRIDGE_FLAGS attribute can be
expressed by the bridge through switchdev, and not all of them can be
emulated by DSA mid-layer API at the same time.
One possible configuration is when the bridge offloads the port flags
using a mask that has a single bit set - therefore only one feature
should change. However, DSA currently groups together unicast and
multicast flooding in the .port_egress_floods method, which limits our
options when we try to add support for turning off broadcast flooding:
do we extend .port_egress_floods with a third parameter which b53 and
mv88e6xxx will ignore? But that means that the DSA layer, which
currently implements the PRE_BRIDGE_FLAGS attribute all by itself, will
see that .port_egress_floods is implemented, and will report that all 3
types of flooding are supported - not necessarily true.
Another configuration is when the user specifies more than one flag at
the same time, in the same netlink message. If we were to create one
individual function per offloadable bridge port flag, we would limit the
expressiveness of the switch driver of refusing certain combinations of
flag values. For example, a switch may not have an explicit knob for
flooding of unknown multicast, just for flooding in general. In that
case, the only correct thing to do is to allow changes to BR_FLOOD and
BR_MCAST_FLOOD in tandem, and never allow mismatched values. But having
a separate .port_set_unicast_flood and .port_set_multicast_flood would
not allow the driver to possibly reject that.
Also, DSA doesn't consider it necessary to inform the driver that a
SWITCHDEV_ATTR_ID_BRIDGE_MROUTER attribute was offloaded, because it
just calls .port_egress_floods for the CPU port. When we'll add support
for the plain SWITCHDEV_ATTR_ID_PORT_MROUTER, that will become a real
problem because the flood settings will need to be held statefully in
the DSA middle layer, otherwise changing the mrouter port attribute will
impact the flooding attribute. And that's _assuming_ that the underlying
hardware doesn't have anything else to do when a multicast router
attaches to a port than flood unknown traffic to it. If it does, there
will need to be a dedicated .port_set_mrouter anyway.
So we need to let the DSA drivers see the exact form that the bridge
passes this switchdev attribute in, otherwise we are standing in the
way. Therefore we also need to use this form of language when
communicating to the driver that it needs to configure its initial
(before bridge join) and final (after bridge leave) port flags.
The b53 and mv88e6xxx drivers are converted to the passthrough API and
their implementation of .port_egress_floods is split into two: a
function that configures unicast flooding and another for multicast.
The mv88e6xxx implementation is quite hairy, and it turns out that
the implementations of unknown unicast flooding are actually the same
for 6185 and for 6352:
behind the confusing names actually lie two individual bits:
NO_UNKNOWN_MC -> FLOOD_UC = 0x4 = BIT(2)
NO_UNKNOWN_UC -> FLOOD_MC = 0x8 = BIT(3)
so there was no reason to entangle them in the first place.
Whereas the 6185 writes to MV88E6185_PORT_CTL0_FORWARD_UNKNOWN of
PORT_CTL0, which has the exact same bit index. I have left the
implementations separate though, for the only reason that the names are
different enough to confuse me, since I am not able to double-check with
a user manual. The multicast flooding setting for 6185 is in a different
register than for 6352 though.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2021-02-12 23:15:56 +08:00
|
|
|
int mv88e6185_port_set_default_forward(struct mv88e6xxx_chip *chip,
|
|
|
|
int port, bool multicast)
|
2017-02-05 03:15:28 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
u16 reg;
|
|
|
|
|
2017-06-13 00:37:41 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, ®);
|
2017-02-05 03:15:28 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-03-12 05:13:00 +08:00
|
|
|
if (multicast)
|
2017-06-13 00:37:41 +08:00
|
|
|
reg |= MV88E6XXX_PORT_CTL2_DEFAULT_FORWARD;
|
2017-02-05 03:15:28 +08:00
|
|
|
else
|
2017-06-13 00:37:41 +08:00
|
|
|
reg &= ~MV88E6XXX_PORT_CTL2_DEFAULT_FORWARD;
|
2017-02-05 03:15:28 +08:00
|
|
|
|
2017-06-13 00:37:41 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
|
2017-02-05 03:15:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
int upstream_port)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
u16 reg;
|
|
|
|
|
2017-06-13 00:37:41 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, ®);
|
2017-02-05 03:15:28 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:41 +08:00
|
|
|
reg &= ~MV88E6095_PORT_CTL2_CPU_PORT_MASK;
|
2017-02-05 03:15:28 +08:00
|
|
|
reg |= upstream_port;
|
|
|
|
|
2017-06-13 00:37:41 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
|
2017-02-05 03:15:28 +08:00
|
|
|
}
|
|
|
|
|
2019-11-08 05:11:14 +08:00
|
|
|
int mv88e6xxx_port_set_mirror(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
enum mv88e6xxx_egress_direction direction,
|
|
|
|
bool mirror)
|
|
|
|
{
|
|
|
|
bool *mirror_port;
|
|
|
|
u16 reg;
|
|
|
|
u16 bit;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
switch (direction) {
|
|
|
|
case MV88E6XXX_EGRESS_DIR_INGRESS:
|
|
|
|
bit = MV88E6XXX_PORT_CTL2_INGRESS_MONITOR;
|
|
|
|
mirror_port = &chip->ports[port].mirror_ingress;
|
|
|
|
break;
|
|
|
|
case MV88E6XXX_EGRESS_DIR_EGRESS:
|
|
|
|
bit = MV88E6XXX_PORT_CTL2_EGRESS_MONITOR;
|
|
|
|
mirror_port = &chip->ports[port].mirror_egress;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
reg &= ~bit;
|
|
|
|
if (mirror)
|
|
|
|
reg |= bit;
|
|
|
|
|
|
|
|
err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
|
|
|
|
if (!err)
|
|
|
|
*mirror_port = mirror;
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-11-04 10:23:31 +08:00
|
|
|
int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
u16 mode)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
int err;
|
|
|
|
|
2017-06-13 00:37:41 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, ®);
|
2016-11-04 10:23:31 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:41 +08:00
|
|
|
reg &= ~MV88E6XXX_PORT_CTL2_8021Q_MODE_MASK;
|
|
|
|
reg |= mode & MV88E6XXX_PORT_CTL2_8021Q_MODE_MASK;
|
2016-11-04 10:23:31 +08:00
|
|
|
|
2017-06-13 00:37:41 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
|
2016-11-04 10:23:31 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-09 06:34:08 +08:00
|
|
|
dev_dbg(chip->dev, "p%d: 802.1QMode set to %s\n", port,
|
|
|
|
mv88e6xxx_port_8021q_mode_names[mode]);
|
2016-11-04 10:23:31 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-12-03 11:35:16 +08:00
|
|
|
|
net: dsa: mv88e6xxx: keep the pvid at 0 when VLAN-unaware
The VLAN support in mv88e6xxx has a loaded history. Commit 2ea7a679ca2a
("net: dsa: Don't add vlans when vlan filtering is disabled") noticed
some issues with VLAN and decided the best way to deal with them was to
make the DSA core ignore VLANs added by the bridge while VLAN awareness
is turned off. Those issues were never explained, just presented as
"at least one corner case".
That approach had problems of its own, presented by
commit 54a0ed0df496 ("net: dsa: provide an option for drivers to always
receive bridge VLANs") for the DSA core, followed by
commit 1fb74191988f ("net: dsa: mv88e6xxx: fix vlan setup") which
applied ds->configure_vlan_while_not_filtering = true for mv88e6xxx in
particular.
We still don't know what corner case Andrew saw when he wrote
commit 2ea7a679ca2a ("net: dsa: Don't add vlans when vlan filtering is
disabled"), but Tobias now reports that when we use TX forwarding
offload, pinging an external station from the bridge device is broken if
the front-facing DSA user port has flooding turned off. The full
description is in the link below, but for short, when a mv88e6xxx port
is under a VLAN-unaware bridge, it inherits that bridge's pvid.
So packets ingressing a user port will be classified to e.g. VID 1
(assuming that value for the bridge_default_pvid), whereas when
tag_dsa.c xmits towards a user port, it always sends packets using a VID
of 0 if that port is standalone or under a VLAN-unaware bridge - or at
least it did so prior to commit d82f8ab0d874 ("net: dsa: tag_dsa:
offload the bridge forwarding process").
In any case, when there is a conversation between the CPU and a station
connected to a user port, the station's MAC address is learned in VID 1
but the CPU tries to transmit through VID 0. The packets reach the
intended station, but via flooding and not by virtue of matching the
existing ATU entry.
DSA has established (and enforced in other drivers: sja1105, felix,
mt7530) that a VLAN-unaware port should use a private pvid, and not
inherit the one from the bridge. The bridge's pvid should only be
inherited when that bridge is VLAN-aware, so all state transitions need
to be handled. On the other hand, all bridge VLANs should sit in the VTU
starting with the moment when the bridge offloads them via switchdev,
they are just not used.
This solves the problem that Tobias sees because packets ingressing on
VLAN-unaware user ports now get classified to VID 0, which is also the
VID used by tag_dsa.c on xmit.
Fixes: d82f8ab0d874 ("net: dsa: tag_dsa: offload the bridge forwarding process")
Link: https://patchwork.kernel.org/project/netdevbpf/patch/20211003222312.284175-2-vladimir.oltean@nxp.com/#24491503
Reported-by: Tobias Waldekranz <tobias@waldekranz.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2021-10-08 00:47:10 +08:00
|
|
|
int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
bool drop_untagged)
|
|
|
|
{
|
|
|
|
u16 old, new;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &old);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (drop_untagged)
|
|
|
|
new = old | MV88E6XXX_PORT_CTL2_DISCARD_UNTAGGED;
|
|
|
|
else
|
|
|
|
new = old & ~MV88E6XXX_PORT_CTL2_DISCARD_UNTAGGED;
|
|
|
|
|
|
|
|
if (new == old)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, new);
|
|
|
|
}
|
|
|
|
|
2017-02-05 03:15:28 +08:00
|
|
|
int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
int err;
|
|
|
|
|
2017-06-13 00:37:41 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, ®);
|
2017-02-05 03:15:28 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:41 +08:00
|
|
|
reg |= MV88E6XXX_PORT_CTL2_MAP_DA;
|
2017-02-05 03:15:28 +08:00
|
|
|
|
2017-06-13 00:37:41 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
|
2017-02-05 03:15:28 +08:00
|
|
|
}
|
|
|
|
|
2017-06-09 06:34:13 +08:00
|
|
|
int mv88e6165_port_set_jumbo_size(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
size_t size)
|
2016-12-03 11:45:17 +08:00
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
int err;
|
|
|
|
|
2021-09-27 01:41:25 +08:00
|
|
|
size += VLAN_ETH_HLEN + ETH_FCS_LEN;
|
|
|
|
|
2017-06-13 00:37:41 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, ®);
|
2016-12-03 11:45:17 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:41 +08:00
|
|
|
reg &= ~MV88E6XXX_PORT_CTL2_JUMBO_MODE_MASK;
|
2017-06-09 06:34:13 +08:00
|
|
|
|
|
|
|
if (size <= 1522)
|
2017-06-13 00:37:41 +08:00
|
|
|
reg |= MV88E6XXX_PORT_CTL2_JUMBO_MODE_1522;
|
2017-06-09 06:34:13 +08:00
|
|
|
else if (size <= 2048)
|
2017-06-13 00:37:41 +08:00
|
|
|
reg |= MV88E6XXX_PORT_CTL2_JUMBO_MODE_2048;
|
2017-06-09 06:34:13 +08:00
|
|
|
else if (size <= 10240)
|
2017-06-13 00:37:41 +08:00
|
|
|
reg |= MV88E6XXX_PORT_CTL2_JUMBO_MODE_10240;
|
2017-06-09 06:34:13 +08:00
|
|
|
else
|
|
|
|
return -ERANGE;
|
2016-12-03 11:45:17 +08:00
|
|
|
|
2017-06-13 00:37:41 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg);
|
2016-12-03 11:45:17 +08:00
|
|
|
}
|
|
|
|
|
2016-12-03 11:45:18 +08:00
|
|
|
/* Offset 0x09: Port Rate Control */
|
|
|
|
|
|
|
|
int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port)
|
|
|
|
{
|
2017-06-13 00:37:42 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_EGRESS_RATE_CTL1,
|
|
|
|
0x0000);
|
2016-12-03 11:45:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port)
|
|
|
|
{
|
2017-06-13 00:37:42 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_EGRESS_RATE_CTL1,
|
|
|
|
0x0001);
|
2016-12-03 11:45:18 +08:00
|
|
|
}
|
|
|
|
|
2021-03-19 03:25:39 +08:00
|
|
|
/* Offset 0x0B: Port Association Vector */
|
|
|
|
|
|
|
|
int mv88e6xxx_port_set_assoc_vector(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
u16 pav)
|
|
|
|
{
|
|
|
|
u16 reg, mask;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR,
|
|
|
|
®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
mask = mv88e6xxx_port_mask(chip);
|
|
|
|
reg &= ~mask;
|
|
|
|
reg |= pav & mask;
|
|
|
|
|
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR,
|
|
|
|
reg);
|
|
|
|
}
|
|
|
|
|
2017-03-12 05:13:01 +08:00
|
|
|
/* Offset 0x0C: Port ATU Control */
|
|
|
|
|
|
|
|
int mv88e6xxx_port_disable_learn_limit(struct mv88e6xxx_chip *chip, int port)
|
|
|
|
{
|
2017-06-13 00:37:45 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ATU_CTL, 0);
|
2017-03-12 05:13:01 +08:00
|
|
|
}
|
|
|
|
|
2017-03-12 05:13:02 +08:00
|
|
|
/* Offset 0x0D: (Priority) Override Register */
|
|
|
|
|
|
|
|
int mv88e6xxx_port_disable_pri_override(struct mv88e6xxx_chip *chip, int port)
|
|
|
|
{
|
2017-06-13 00:37:45 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_PRI_OVERRIDE, 0);
|
2017-03-12 05:13:02 +08:00
|
|
|
}
|
|
|
|
|
2021-03-17 21:46:42 +08:00
|
|
|
/* Offset 0x0E: Policy & MGMT Control Register for FAMILY 6191X 6193X 6393X */
|
|
|
|
|
2021-03-17 21:46:43 +08:00
|
|
|
static int mv88e6393x_port_policy_read(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
u16 pointer, u8 *data)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_POLICY_MGMT_CTL,
|
|
|
|
pointer);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6393X_PORT_POLICY_MGMT_CTL,
|
|
|
|
®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
*data = reg;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-17 21:46:42 +08:00
|
|
|
static int mv88e6393x_port_policy_write(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
u16 pointer, u8 data)
|
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
|
|
|
|
reg = MV88E6393X_PORT_POLICY_MGMT_CTL_UPDATE | pointer | data;
|
|
|
|
|
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_POLICY_MGMT_CTL,
|
|
|
|
reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mv88e6393x_port_policy_write_all(struct mv88e6xxx_chip *chip,
|
|
|
|
u16 pointer, u8 data)
|
|
|
|
{
|
|
|
|
int err, port;
|
|
|
|
|
|
|
|
for (port = 0; port < mv88e6xxx_num_ports(chip); port++) {
|
|
|
|
if (dsa_is_unused_port(chip->ds, port))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
err = mv88e6393x_port_policy_write(chip, port, pointer, data);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6393x_set_egress_port(struct mv88e6xxx_chip *chip,
|
|
|
|
enum mv88e6xxx_egress_direction direction,
|
|
|
|
int port)
|
|
|
|
{
|
|
|
|
u16 ptr;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
switch (direction) {
|
|
|
|
case MV88E6XXX_EGRESS_DIR_INGRESS:
|
|
|
|
ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_INGRESS_DEST;
|
|
|
|
err = mv88e6393x_port_policy_write_all(chip, ptr, port);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
break;
|
|
|
|
case MV88E6XXX_EGRESS_DIR_EGRESS:
|
|
|
|
ptr = MV88E6393X_G2_EGRESS_MONITOR_DEST;
|
|
|
|
err = mv88e6xxx_g2_write(chip, ptr, port);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6393x_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
int upstream_port)
|
|
|
|
{
|
|
|
|
u16 ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_CPU_DEST;
|
|
|
|
u8 data = MV88E6393X_PORT_POLICY_MGMT_CTL_CPU_DEST_MGMTPRI |
|
|
|
|
upstream_port;
|
|
|
|
|
|
|
|
return mv88e6393x_port_policy_write(chip, port, ptr, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6393x_port_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
|
|
|
|
{
|
|
|
|
u16 ptr;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* Consider the frames with reserved multicast destination
|
|
|
|
* addresses matching 01:80:c2:00:00:00 and
|
|
|
|
* 01:80:c2:00:00:02 as MGMT.
|
|
|
|
*/
|
|
|
|
ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000000XLO;
|
|
|
|
err = mv88e6393x_port_policy_write_all(chip, ptr, 0xff);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000000XHI;
|
|
|
|
err = mv88e6393x_port_policy_write_all(chip, ptr, 0xff);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000002XLO;
|
|
|
|
err = mv88e6393x_port_policy_write_all(chip, ptr, 0xff);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
ptr = MV88E6393X_PORT_POLICY_MGMT_CTL_PTR_01C280000002XHI;
|
|
|
|
err = mv88e6393x_port_policy_write_all(chip, ptr, 0xff);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Offset 0x10 & 0x11: EPC */
|
|
|
|
|
|
|
|
static int mv88e6393x_port_epc_wait_ready(struct mv88e6xxx_chip *chip, int port)
|
|
|
|
{
|
|
|
|
int bit = __bf_shf(MV88E6393X_PORT_EPC_CMD_BUSY);
|
|
|
|
|
|
|
|
return mv88e6xxx_port_wait_bit(chip, port, MV88E6393X_PORT_EPC_CMD, bit, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Port Ether type for 6393X family */
|
|
|
|
|
|
|
|
int mv88e6393x_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
u16 etype)
|
|
|
|
{
|
|
|
|
u16 val;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = mv88e6393x_port_epc_wait_ready(chip, port);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_EPC_DATA, etype);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
val = MV88E6393X_PORT_EPC_CMD_BUSY |
|
|
|
|
MV88E6393X_PORT_EPC_CMD_WRITE |
|
|
|
|
MV88E6393X_PORT_EPC_INDEX_PORT_ETYPE;
|
|
|
|
|
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_EPC_CMD, val);
|
|
|
|
}
|
|
|
|
|
2016-12-03 11:35:19 +08:00
|
|
|
/* Offset 0x0f: Port Ether type */
|
|
|
|
|
|
|
|
int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
u16 etype)
|
|
|
|
{
|
2017-06-13 00:37:45 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ETH_TYPE, etype);
|
2016-12-03 11:35:19 +08:00
|
|
|
}
|
|
|
|
|
2016-12-03 11:35:16 +08:00
|
|
|
/* Offset 0x18: Port IEEE Priority Remapping Registers [0-3]
|
|
|
|
* Offset 0x19: Port IEEE Priority Remapping Registers [4-7]
|
|
|
|
*/
|
|
|
|
|
|
|
|
int mv88e6095_port_tag_remap(struct mv88e6xxx_chip *chip, int port)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* Use a direct priority mapping for all IEEE tagged frames */
|
2017-06-13 00:37:44 +08:00
|
|
|
err = mv88e6xxx_port_write(chip, port,
|
|
|
|
MV88E6095_PORT_IEEE_PRIO_REMAP_0123,
|
|
|
|
0x3210);
|
2016-12-03 11:35:16 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:44 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port,
|
|
|
|
MV88E6095_PORT_IEEE_PRIO_REMAP_4567,
|
|
|
|
0x7654);
|
2016-12-03 11:35:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mv88e6xxx_port_ieeepmt_write(struct mv88e6xxx_chip *chip,
|
2017-06-18 11:07:14 +08:00
|
|
|
int port, u16 table, u8 ptr, u16 data)
|
2016-12-03 11:35:16 +08:00
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
|
2017-06-18 11:07:14 +08:00
|
|
|
reg = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_UPDATE | table |
|
|
|
|
(ptr << __bf_shf(MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_PTR_MASK)) |
|
|
|
|
(data & MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_DATA_MASK);
|
2016-12-03 11:35:16 +08:00
|
|
|
|
2017-06-13 00:37:44 +08:00
|
|
|
return mv88e6xxx_port_write(chip, port,
|
|
|
|
MV88E6390_PORT_IEEE_PRIO_MAP_TABLE, reg);
|
2016-12-03 11:35:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port)
|
|
|
|
{
|
|
|
|
int err, i;
|
2017-06-13 00:37:44 +08:00
|
|
|
u16 table;
|
2016-12-03 11:35:16 +08:00
|
|
|
|
|
|
|
for (i = 0; i <= 7; i++) {
|
2017-06-13 00:37:44 +08:00
|
|
|
table = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_INGRESS_PCP;
|
|
|
|
err = mv88e6xxx_port_ieeepmt_write(chip, port, table, i,
|
|
|
|
(i | i << 4));
|
2016-12-03 11:35:16 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:44 +08:00
|
|
|
table = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_GREEN_PCP;
|
|
|
|
err = mv88e6xxx_port_ieeepmt_write(chip, port, table, i, i);
|
2016-12-03 11:35:16 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:44 +08:00
|
|
|
table = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_YELLOW_PCP;
|
|
|
|
err = mv88e6xxx_port_ieeepmt_write(chip, port, table, i, i);
|
2016-12-03 11:35:16 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-06-13 00:37:44 +08:00
|
|
|
table = MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_EGRESS_AVB_PCP;
|
|
|
|
err = mv88e6xxx_port_ieeepmt_write(chip, port, table, i, i);
|
2016-12-03 11:35:16 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-09-08 04:00:48 +08:00
|
|
|
|
|
|
|
/* Offset 0x0E: Policy Control Register */
|
|
|
|
|
2021-03-17 21:46:43 +08:00
|
|
|
static int
|
|
|
|
mv88e6xxx_port_policy_mapping_get_pos(enum mv88e6xxx_policy_mapping mapping,
|
|
|
|
enum mv88e6xxx_policy_action action,
|
|
|
|
u16 *mask, u16 *val, int *shift)
|
2019-09-08 04:00:48 +08:00
|
|
|
{
|
|
|
|
switch (mapping) {
|
|
|
|
case MV88E6XXX_POLICY_MAPPING_DA:
|
2021-03-17 21:46:43 +08:00
|
|
|
*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_DA_MASK);
|
|
|
|
*mask = MV88E6XXX_PORT_POLICY_CTL_DA_MASK;
|
2019-09-08 04:00:48 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_POLICY_MAPPING_SA:
|
2021-03-17 21:46:43 +08:00
|
|
|
*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_SA_MASK);
|
|
|
|
*mask = MV88E6XXX_PORT_POLICY_CTL_SA_MASK;
|
2019-09-08 04:00:48 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_POLICY_MAPPING_VTU:
|
2021-03-17 21:46:43 +08:00
|
|
|
*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VTU_MASK);
|
|
|
|
*mask = MV88E6XXX_PORT_POLICY_CTL_VTU_MASK;
|
2019-09-08 04:00:48 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_POLICY_MAPPING_ETYPE:
|
2021-03-17 21:46:43 +08:00
|
|
|
*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK);
|
|
|
|
*mask = MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK;
|
2019-09-08 04:00:48 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_POLICY_MAPPING_PPPOE:
|
2021-03-17 21:46:43 +08:00
|
|
|
*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK);
|
|
|
|
*mask = MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK;
|
2019-09-08 04:00:48 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_POLICY_MAPPING_VBAS:
|
2021-03-17 21:46:43 +08:00
|
|
|
*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK);
|
|
|
|
*mask = MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK;
|
2019-09-08 04:00:48 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_POLICY_MAPPING_OPT82:
|
2021-03-17 21:46:43 +08:00
|
|
|
*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK);
|
|
|
|
*mask = MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK;
|
2019-09-08 04:00:48 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_POLICY_MAPPING_UDP:
|
2021-03-17 21:46:43 +08:00
|
|
|
*shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_UDP_MASK);
|
|
|
|
*mask = MV88E6XXX_PORT_POLICY_CTL_UDP_MASK;
|
2019-09-08 04:00:48 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (action) {
|
|
|
|
case MV88E6XXX_POLICY_ACTION_NORMAL:
|
2021-03-17 21:46:43 +08:00
|
|
|
*val = MV88E6XXX_PORT_POLICY_CTL_NORMAL;
|
2019-09-08 04:00:48 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_POLICY_ACTION_MIRROR:
|
2021-03-17 21:46:43 +08:00
|
|
|
*val = MV88E6XXX_PORT_POLICY_CTL_MIRROR;
|
2019-09-08 04:00:48 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_POLICY_ACTION_TRAP:
|
2021-03-17 21:46:43 +08:00
|
|
|
*val = MV88E6XXX_PORT_POLICY_CTL_TRAP;
|
2019-09-08 04:00:48 +08:00
|
|
|
break;
|
|
|
|
case MV88E6XXX_POLICY_ACTION_DISCARD:
|
2021-03-17 21:46:43 +08:00
|
|
|
*val = MV88E6XXX_PORT_POLICY_CTL_DISCARD;
|
2019-09-08 04:00:48 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2021-03-17 21:46:43 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
enum mv88e6xxx_policy_mapping mapping,
|
|
|
|
enum mv88e6xxx_policy_action action)
|
|
|
|
{
|
|
|
|
u16 reg, mask, val;
|
|
|
|
int shift;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = mv88e6xxx_port_policy_mapping_get_pos(mapping, action, &mask,
|
|
|
|
&val, &shift);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2019-09-08 04:00:48 +08:00
|
|
|
err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_POLICY_CTL, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
reg &= ~mask;
|
|
|
|
reg |= (val << shift) & mask;
|
|
|
|
|
|
|
|
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_POLICY_CTL, reg);
|
|
|
|
}
|
2021-03-17 21:46:43 +08:00
|
|
|
|
|
|
|
int mv88e6393x_port_set_policy(struct mv88e6xxx_chip *chip, int port,
|
|
|
|
enum mv88e6xxx_policy_mapping mapping,
|
|
|
|
enum mv88e6xxx_policy_action action)
|
|
|
|
{
|
|
|
|
u16 mask, val;
|
|
|
|
int shift;
|
|
|
|
int err;
|
|
|
|
u16 ptr;
|
|
|
|
u8 reg;
|
|
|
|
|
|
|
|
err = mv88e6xxx_port_policy_mapping_get_pos(mapping, action, &mask,
|
|
|
|
&val, &shift);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* The 16-bit Port Policy CTL register from older chips is on 6393x
|
|
|
|
* changed to Port Policy MGMT CTL, which can access more data, but
|
|
|
|
* indirectly. The original 16-bit value is divided into two 8-bit
|
|
|
|
* registers.
|
|
|
|
*/
|
|
|
|
ptr = shift / 8;
|
|
|
|
shift %= 8;
|
|
|
|
mask >>= ptr * 8;
|
|
|
|
|
|
|
|
err = mv88e6393x_port_policy_read(chip, port, ptr, ®);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
reg &= ~mask;
|
|
|
|
reg |= (val << shift) & mask;
|
|
|
|
|
|
|
|
return mv88e6393x_port_policy_write(chip, port, ptr, reg);
|
|
|
|
}
|