Commit 0d53072a authored by David S. Miller's avatar David S. Miller

Merge branch 'dsa-mv88e6xxx-port-operation-refine'

Vivien Didelot says:

====================
net: dsa: mv88e6xxx: refine port operations

The Marvell chips have one internal SMI device per port, containing a
set of registers used to configure a port's link, STP state, default
VLAN or addresses database, etc.

This patchset creates port files to implement the port operations as
described in datasheets, and extend the chip ops structure with them.

Patches 1 to 6 implement accessors for port's STP state, port based VLAN
map, default FID, default VID, and 802.1Q mode.

Patches 7 to 11 implement the port's MAC setup of link state, duplex
mode, RGMII delay and speed, all accessed through port's register 0x01.

The new port's MAC setup code is used to re-implement the adjust_link
code and correctly force the link down before changing any of the MAC
settings, as requested by the datasheets.

The port's MAC accessors use values compatible with struct phy_device
(e.g. DUPLEX_FULL) and extend them when needed (e.g. SPEED_MAX).

Changes in v2:

  - Strictly use new _UNFORCED values instead of re-using _UNKNOWN ones.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5976c5f4 d78343d2
...@@ -2,3 +2,4 @@ obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o ...@@ -2,3 +2,4 @@ obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o
mv88e6xxx-objs := chip.o mv88e6xxx-objs := chip.o
mv88e6xxx-objs += global1.o mv88e6xxx-objs += global1.o
mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2.o mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2.o
mv88e6xxx-objs += port.o
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "mv88e6xxx.h" #include "mv88e6xxx.h"
#include "global1.h" #include "global1.h"
#include "global2.h" #include "global2.h"
#include "port.h"
static void assert_reg_lock(struct mv88e6xxx_chip *chip) static void assert_reg_lock(struct mv88e6xxx_chip *chip)
{ {
...@@ -221,22 +222,6 @@ int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val) ...@@ -221,22 +222,6 @@ int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val)
return 0; return 0;
} }
static 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);
}
static 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);
}
static int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy, static int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy,
int reg, u16 *val) int reg, u16 *val)
{ {
...@@ -716,6 +701,47 @@ static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip) ...@@ -716,6 +701,47 @@ static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip)
return chip->info->family == MV88E6XXX_FAMILY_6352; return chip->info->family == MV88E6XXX_FAMILY_6352;
} }
static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port,
int link, int speed, int duplex,
phy_interface_t mode)
{
int err;
if (!chip->info->ops->port_set_link)
return 0;
/* Port's MAC control must not be changed unless the link is down */
err = chip->info->ops->port_set_link(chip, port, 0);
if (err)
return err;
if (chip->info->ops->port_set_speed) {
err = chip->info->ops->port_set_speed(chip, port, speed);
if (err && err != -EOPNOTSUPP)
goto restore_link;
}
if (chip->info->ops->port_set_duplex) {
err = chip->info->ops->port_set_duplex(chip, port, duplex);
if (err && err != -EOPNOTSUPP)
goto restore_link;
}
if (chip->info->ops->port_set_rgmii_delay) {
err = chip->info->ops->port_set_rgmii_delay(chip, port, mode);
if (err && err != -EOPNOTSUPP)
goto restore_link;
}
err = 0;
restore_link:
if (chip->info->ops->port_set_link(chip, port, link))
netdev_err(chip->ds->ports[port].netdev,
"failed to restore MAC's link\n");
return err;
}
/* We expect the switch to perform auto negotiation if there is a real /* We expect the switch to perform auto negotiation if there is a real
* phy. However, in the case of a fixed link phy, we force the port * phy. However, in the case of a fixed link phy, we force the port
* settings from the fixed link settings. * settings from the fixed link settings.
...@@ -724,64 +750,18 @@ static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, ...@@ -724,64 +750,18 @@ static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
struct phy_device *phydev) struct phy_device *phydev)
{ {
struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_chip *chip = ds->priv;
u16 reg;
int err; int err;
if (!phy_is_pseudo_fixed_link(phydev)) if (!phy_is_pseudo_fixed_link(phydev))
return; return;
mutex_lock(&chip->reg_lock); mutex_lock(&chip->reg_lock);
err = mv88e6xxx_port_setup_mac(chip, port, phydev->link, phydev->speed,
err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg); phydev->duplex, phydev->interface);
if (err)
goto out;
reg &= ~(PORT_PCS_CTRL_LINK_UP |
PORT_PCS_CTRL_FORCE_LINK |
PORT_PCS_CTRL_DUPLEX_FULL |
PORT_PCS_CTRL_FORCE_DUPLEX |
PORT_PCS_CTRL_UNFORCED);
reg |= PORT_PCS_CTRL_FORCE_LINK;
if (phydev->link)
reg |= PORT_PCS_CTRL_LINK_UP;
if (mv88e6xxx_6065_family(chip) && phydev->speed > SPEED_100)
goto out;
switch (phydev->speed) {
case SPEED_1000:
reg |= PORT_PCS_CTRL_1000;
break;
case SPEED_100:
reg |= PORT_PCS_CTRL_100;
break;
case SPEED_10:
reg |= PORT_PCS_CTRL_10;
break;
default:
pr_info("Unknown speed");
goto out;
}
reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
if (phydev->duplex == DUPLEX_FULL)
reg |= PORT_PCS_CTRL_DUPLEX_FULL;
if ((mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip)) &&
(port >= mv88e6xxx_num_ports(chip) - 2)) {
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
}
mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
out:
mutex_unlock(&chip->reg_lock); mutex_unlock(&chip->reg_lock);
if (err && err != -EOPNOTSUPP)
netdev_err(ds->ports[port].netdev, "failed to configure MAC\n");
} }
static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip) static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip)
...@@ -1230,54 +1210,16 @@ static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, ...@@ -1230,54 +1210,16 @@ static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid,
return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too); return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too);
} }
static const char * const mv88e6xxx_port_state_names[] = {
[PORT_CONTROL_STATE_DISABLED] = "Disabled",
[PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
[PORT_CONTROL_STATE_LEARNING] = "Learning",
[PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
};
static int _mv88e6xxx_port_state(struct mv88e6xxx_chip *chip, int port,
u8 state)
{
struct dsa_switch *ds = chip->ds;
u16 reg;
int err;
u8 oldstate;
err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
if (err)
return err;
oldstate = reg & PORT_CONTROL_STATE_MASK;
reg &= ~PORT_CONTROL_STATE_MASK;
reg |= state;
err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
if (err)
return err;
netdev_dbg(ds->ports[port].netdev, "PortState %s (was %s)\n",
mv88e6xxx_port_state_names[state],
mv88e6xxx_port_state_names[oldstate]);
return 0;
}
static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port) static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)
{ {
struct net_device *bridge = chip->ports[port].bridge_dev; struct net_device *bridge = chip->ports[port].bridge_dev;
const u16 mask = (1 << mv88e6xxx_num_ports(chip)) - 1;
struct dsa_switch *ds = chip->ds; struct dsa_switch *ds = chip->ds;
u16 output_ports = 0; u16 output_ports = 0;
u16 reg;
int err;
int i; int i;
/* allow CPU port or DSA link(s) to send frames to every port */ /* allow CPU port or DSA link(s) to send frames to every port */
if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
output_ports = mask; output_ports = ~0;
} else { } else {
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
/* allow sending frames to every group member */ /* allow sending frames to every group member */
...@@ -1293,14 +1235,7 @@ static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port) ...@@ -1293,14 +1235,7 @@ static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)
/* prevent frames from going back out of the port they came in on */ /* prevent frames from going back out of the port they came in on */
output_ports &= ~BIT(port); output_ports &= ~BIT(port);
err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg); return mv88e6xxx_port_set_vlan_map(chip, port, output_ports);
if (err)
return err;
reg &= ~mask;
reg |= output_ports & mask;
return mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
} }
static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
...@@ -1328,13 +1263,11 @@ static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, ...@@ -1328,13 +1263,11 @@ static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
} }
mutex_lock(&chip->reg_lock); mutex_lock(&chip->reg_lock);
err = _mv88e6xxx_port_state(chip, port, stp_state); err = mv88e6xxx_port_set_state(chip, port, stp_state);
mutex_unlock(&chip->reg_lock); mutex_unlock(&chip->reg_lock);
if (err) if (err)
netdev_err(ds->ports[port].netdev, netdev_err(ds->ports[port].netdev, "failed to update state\n");
"failed to update state to %s\n",
mv88e6xxx_port_state_names[stp_state]);
} }
static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
...@@ -1350,49 +1283,6 @@ static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) ...@@ -1350,49 +1283,6 @@ static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
netdev_err(ds->ports[port].netdev, "failed to flush ATU\n"); netdev_err(ds->ports[port].netdev, "failed to flush ATU\n");
} }
static int _mv88e6xxx_port_pvid(struct mv88e6xxx_chip *chip, int port,
u16 *new, u16 *old)
{
struct dsa_switch *ds = chip->ds;
u16 pvid, reg;
int err;
err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
if (err)
return err;
pvid = reg & PORT_DEFAULT_VLAN_MASK;
if (new) {
reg &= ~PORT_DEFAULT_VLAN_MASK;
reg |= *new & PORT_DEFAULT_VLAN_MASK;
err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg);
if (err)
return err;
netdev_dbg(ds->ports[port].netdev,
"DefaultVID %d (was %d)\n", *new, pvid);
}
if (old)
*old = pvid;
return 0;
}
static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_chip *chip,
int port, u16 *pvid)
{
return _mv88e6xxx_port_pvid(chip, port, NULL, pvid);
}
static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_chip *chip,
int port, u16 pvid)
{
return _mv88e6xxx_port_pvid(chip, port, &pvid, NULL);
}
static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip) static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip)
{ {
return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY); return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY);
...@@ -1572,7 +1462,7 @@ static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, ...@@ -1572,7 +1462,7 @@ static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
mutex_lock(&chip->reg_lock); mutex_lock(&chip->reg_lock);
err = _mv88e6xxx_port_pvid_get(chip, port, &pvid); err = mv88e6xxx_port_get_pvid(chip, port, &pvid);
if (err) if (err)
goto unlock; goto unlock;
...@@ -1736,75 +1626,6 @@ static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip, ...@@ -1736,75 +1626,6 @@ static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip,
return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE); return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);
} }
static int _mv88e6xxx_port_fid(struct mv88e6xxx_chip *chip, int port,
u16 *new, u16 *old)
{
struct dsa_switch *ds = chip->ds;
u16 upper_mask;
u16 fid;
u16 reg;
int err;
if (mv88e6xxx_num_databases(chip) == 4096)
upper_mask = 0xff;
else if (mv88e6xxx_num_databases(chip) == 256)
upper_mask = 0xf;
else
return -EOPNOTSUPP;
/* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
if (err)
return err;
fid = (reg & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
if (new) {
reg &= ~PORT_BASE_VLAN_FID_3_0_MASK;
reg |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
if (err)
return err;
}
/* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
if (err)
return err;
fid |= (reg & upper_mask) << 4;
if (new) {
reg &= ~upper_mask;
reg |= (*new >> 4) & upper_mask;
err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg);
if (err)
return err;
netdev_dbg(ds->ports[port].netdev,
"FID %d (was %d)\n", *new, fid);
}
if (old)
*old = fid;
return 0;
}
static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_chip *chip,
int port, u16 *fid)
{
return _mv88e6xxx_port_fid(chip, port, NULL, fid);
}
static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_chip *chip,
int port, u16 fid)
{
return _mv88e6xxx_port_fid(chip, port, &fid, NULL);
}
static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid) static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)
{ {
DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
...@@ -1815,7 +1636,7 @@ static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid) ...@@ -1815,7 +1636,7 @@ static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)
/* Set every FID bit used by the (un)bridged ports */ /* Set every FID bit used by the (un)bridged ports */
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
err = _mv88e6xxx_port_fid_get(chip, i, fid); err = mv88e6xxx_port_get_fid(chip, i, fid);
if (err) if (err)
return err; return err;
...@@ -1980,48 +1801,19 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, ...@@ -1980,48 +1801,19 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
return err; return err;
} }
static const char * const mv88e6xxx_port_8021q_mode_names[] = {
[PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
[PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
[PORT_CONTROL_2_8021Q_CHECK] = "Check",
[PORT_CONTROL_2_8021Q_SECURE] = "Secure",
};
static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
bool vlan_filtering) bool vlan_filtering)
{ {
struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_chip *chip = ds->priv;
u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE : u16 mode = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
PORT_CONTROL_2_8021Q_DISABLED; PORT_CONTROL_2_8021Q_DISABLED;
u16 reg;
int err; int err;
if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
return -EOPNOTSUPP; return -EOPNOTSUPP;
mutex_lock(&chip->reg_lock); mutex_lock(&chip->reg_lock);
err = mv88e6xxx_port_set_8021q_mode(chip, port, mode);
err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
if (err)
goto unlock;
old = reg & PORT_CONTROL_2_8021Q_MASK;
if (new != old) {
reg &= ~PORT_CONTROL_2_8021Q_MASK;
reg |= new & PORT_CONTROL_2_8021Q_MASK;
err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
if (err)
goto unlock;
netdev_dbg(ds->ports[port].netdev, "802.1Q Mode %s (was %s)\n",
mv88e6xxx_port_8021q_mode_names[new],
mv88e6xxx_port_8021q_mode_names[old]);
}
err = 0;
unlock:
mutex_unlock(&chip->reg_lock); mutex_unlock(&chip->reg_lock);
return err; return err;
...@@ -2089,7 +1881,7 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, ...@@ -2089,7 +1881,7 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
"failed to add VLAN %d%c\n", "failed to add VLAN %d%c\n",
vid, untagged ? 'u' : 't'); vid, untagged ? 'u' : 't');
if (pvid && _mv88e6xxx_port_pvid_set(chip, port, vlan->vid_end)) if (pvid && mv88e6xxx_port_set_pvid(chip, port, vlan->vid_end))
netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n", netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n",
vlan->vid_end); vlan->vid_end);
...@@ -2144,7 +1936,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, ...@@ -2144,7 +1936,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
mutex_lock(&chip->reg_lock); mutex_lock(&chip->reg_lock);
err = _mv88e6xxx_port_pvid_get(chip, port, &pvid); err = mv88e6xxx_port_get_pvid(chip, port, &pvid);
if (err) if (err)
goto unlock; goto unlock;
...@@ -2154,7 +1946,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, ...@@ -2154,7 +1946,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
goto unlock; goto unlock;
if (vid == pvid) { if (vid == pvid) {
err = _mv88e6xxx_port_pvid_set(chip, port, 0); err = mv88e6xxx_port_set_pvid(chip, port, 0);
if (err) if (err)
goto unlock; goto unlock;
} }
...@@ -2265,7 +2057,7 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, ...@@ -2265,7 +2057,7 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
/* Null VLAN ID corresponds to the port private database */ /* Null VLAN ID corresponds to the port private database */
if (vid == 0) if (vid == 0)
err = _mv88e6xxx_port_fid_get(chip, port, &vlan.fid); err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid);
else else
err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false); err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
if (err) if (err)
...@@ -2441,7 +2233,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, ...@@ -2441,7 +2233,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
int err; int err;
/* Dump port's default Filtering Information Database (VLAN ID 0) */ /* Dump port's default Filtering Information Database (VLAN ID 0) */
err = _mv88e6xxx_port_fid_get(chip, port, &fid); err = mv88e6xxx_port_get_fid(chip, port, &fid);
if (err) if (err)
return err; return err;
...@@ -2541,12 +2333,8 @@ static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) ...@@ -2541,12 +2333,8 @@ static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
/* Set all ports to the disabled state. */ /* Set all ports to the disabled state. */
for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
err = mv88e6xxx_port_read(chip, i, PORT_CONTROL, &reg); err = mv88e6xxx_port_set_state(chip, i,
if (err) PORT_CONTROL_STATE_DISABLED);
return err;
err = mv88e6xxx_port_write(chip, i, PORT_CONTROL,
reg & 0xfffc);
if (err) if (err)
return err; return err;
} }
...@@ -2616,35 +2404,20 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) ...@@ -2616,35 +2404,20 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
int err; int err;
u16 reg; u16 reg;
if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || /* MAC Forcing register: don't force link, speed, duplex or flow control
mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || * state to any particular values on physical ports, but force the CPU
mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) || * port and all DSA ports to their maximum bandwidth and full duplex.
mv88e6xxx_6065_family(chip) || mv88e6xxx_6320_family(chip)) {
/* MAC Forcing register: don't force link, speed,
* duplex or flow control state to any particular
* values on physical ports, but force the CPU port
* and all DSA ports to their maximum bandwidth and
* full duplex.
*/ */
err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg); if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { err = mv88e6xxx_port_setup_mac(chip, port, LINK_FORCED_UP,
reg &= ~PORT_PCS_CTRL_UNFORCED; SPEED_MAX, DUPLEX_FULL,
reg |= PORT_PCS_CTRL_FORCE_LINK | PHY_INTERFACE_MODE_NA);
PORT_PCS_CTRL_LINK_UP |
PORT_PCS_CTRL_DUPLEX_FULL |
PORT_PCS_CTRL_FORCE_DUPLEX;
if (mv88e6xxx_6065_family(chip))
reg |= PORT_PCS_CTRL_100;
else else
reg |= PORT_PCS_CTRL_1000; err = mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED,
} else { SPEED_UNFORCED, DUPLEX_UNFORCED,
reg |= PORT_PCS_CTRL_UNFORCED; PHY_INTERFACE_MODE_NA);
}
err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
if (err) if (err)
return err; return err;
}
/* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock, /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
* disable Header mode, enable IGMP/MLD snooping, disable VLAN * disable Header mode, enable IGMP/MLD snooping, disable VLAN
...@@ -2848,7 +2621,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) ...@@ -2848,7 +2621,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
* database, and allow bidirectional communication between the * database, and allow bidirectional communication between the
* CPU and DSA port(s), and the other ports. * CPU and DSA port(s), and the other ports.
*/ */
err = _mv88e6xxx_port_fid_set(chip, port, 0); err = mv88e6xxx_port_set_fid(chip, port, 0);
if (err) if (err)
return err; return err;
...@@ -3367,42 +3140,63 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { ...@@ -3367,42 +3140,63 @@ static const struct mv88e6xxx_ops mv88e6085_ops = {
.set_switch_mac = mv88e6xxx_g1_set_switch_mac, .set_switch_mac = mv88e6xxx_g1_set_switch_mac,
.phy_read = mv88e6xxx_phy_ppu_read, .phy_read = mv88e6xxx_phy_ppu_read,
.phy_write = mv88e6xxx_phy_ppu_write, .phy_write = mv88e6xxx_phy_ppu_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
}; };
static const struct mv88e6xxx_ops mv88e6095_ops = { static const struct mv88e6xxx_ops mv88e6095_ops = {
.set_switch_mac = mv88e6xxx_g1_set_switch_mac, .set_switch_mac = mv88e6xxx_g1_set_switch_mac,
.phy_read = mv88e6xxx_phy_ppu_read, .phy_read = mv88e6xxx_phy_ppu_read,
.phy_write = mv88e6xxx_phy_ppu_write, .phy_write = mv88e6xxx_phy_ppu_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
}; };
static const struct mv88e6xxx_ops mv88e6123_ops = { static const struct mv88e6xxx_ops mv88e6123_ops = {
.set_switch_mac = mv88e6xxx_g2_set_switch_mac, .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_read, .phy_read = mv88e6xxx_read,
.phy_write = mv88e6xxx_write, .phy_write = mv88e6xxx_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
}; };
static const struct mv88e6xxx_ops mv88e6131_ops = { static const struct mv88e6xxx_ops mv88e6131_ops = {
.set_switch_mac = mv88e6xxx_g1_set_switch_mac, .set_switch_mac = mv88e6xxx_g1_set_switch_mac,
.phy_read = mv88e6xxx_phy_ppu_read, .phy_read = mv88e6xxx_phy_ppu_read,
.phy_write = mv88e6xxx_phy_ppu_write, .phy_write = mv88e6xxx_phy_ppu_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
}; };
static const struct mv88e6xxx_ops mv88e6161_ops = { static const struct mv88e6xxx_ops mv88e6161_ops = {
.set_switch_mac = mv88e6xxx_g2_set_switch_mac, .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_read, .phy_read = mv88e6xxx_read,
.phy_write = mv88e6xxx_write, .phy_write = mv88e6xxx_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
}; };
static const struct mv88e6xxx_ops mv88e6165_ops = { static const struct mv88e6xxx_ops mv88e6165_ops = {
.set_switch_mac = mv88e6xxx_g2_set_switch_mac, .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_read, .phy_read = mv88e6xxx_read,
.phy_write = mv88e6xxx_write, .phy_write = mv88e6xxx_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
}; };
static const struct mv88e6xxx_ops mv88e6171_ops = { static const struct mv88e6xxx_ops mv88e6171_ops = {
.set_switch_mac = mv88e6xxx_g2_set_switch_mac, .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read, .phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write, .phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
}; };
static const struct mv88e6xxx_ops mv88e6172_ops = { static const struct mv88e6xxx_ops mv88e6172_ops = {
...@@ -3411,12 +3205,19 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { ...@@ -3411,12 +3205,19 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
.set_switch_mac = mv88e6xxx_g2_set_switch_mac, .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read, .phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write, .phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
.port_set_speed = mv88e6352_port_set_speed,
}; };
static const struct mv88e6xxx_ops mv88e6175_ops = { static const struct mv88e6xxx_ops mv88e6175_ops = {
.set_switch_mac = mv88e6xxx_g2_set_switch_mac, .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read, .phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write, .phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
}; };
static const struct mv88e6xxx_ops mv88e6176_ops = { static const struct mv88e6xxx_ops mv88e6176_ops = {
...@@ -3425,12 +3226,19 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { ...@@ -3425,12 +3226,19 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
.set_switch_mac = mv88e6xxx_g2_set_switch_mac, .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read, .phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write, .phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
.port_set_speed = mv88e6352_port_set_speed,
}; };
static const struct mv88e6xxx_ops mv88e6185_ops = { static const struct mv88e6xxx_ops mv88e6185_ops = {
.set_switch_mac = mv88e6xxx_g1_set_switch_mac, .set_switch_mac = mv88e6xxx_g1_set_switch_mac,
.phy_read = mv88e6xxx_phy_ppu_read, .phy_read = mv88e6xxx_phy_ppu_read,
.phy_write = mv88e6xxx_phy_ppu_write, .phy_write = mv88e6xxx_phy_ppu_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
}; };
static const struct mv88e6xxx_ops mv88e6240_ops = { static const struct mv88e6xxx_ops mv88e6240_ops = {
...@@ -3439,6 +3247,10 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { ...@@ -3439,6 +3247,10 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
.set_switch_mac = mv88e6xxx_g2_set_switch_mac, .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read, .phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write, .phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
.port_set_speed = mv88e6352_port_set_speed,
}; };
static const struct mv88e6xxx_ops mv88e6320_ops = { static const struct mv88e6xxx_ops mv88e6320_ops = {
...@@ -3447,6 +3259,9 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { ...@@ -3447,6 +3259,9 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {
.set_switch_mac = mv88e6xxx_g2_set_switch_mac, .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read, .phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write, .phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
}; };
static const struct mv88e6xxx_ops mv88e6321_ops = { static const struct mv88e6xxx_ops mv88e6321_ops = {
...@@ -3455,18 +3270,27 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { ...@@ -3455,18 +3270,27 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
.set_switch_mac = mv88e6xxx_g2_set_switch_mac, .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read, .phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write, .phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
}; };
static const struct mv88e6xxx_ops mv88e6350_ops = { static const struct mv88e6xxx_ops mv88e6350_ops = {
.set_switch_mac = mv88e6xxx_g2_set_switch_mac, .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read, .phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write, .phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
}; };
static const struct mv88e6xxx_ops mv88e6351_ops = { static const struct mv88e6xxx_ops mv88e6351_ops = {
.set_switch_mac = mv88e6xxx_g2_set_switch_mac, .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read, .phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write, .phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_speed = mv88e6185_port_set_speed,
}; };
static const struct mv88e6xxx_ops mv88e6352_ops = { static const struct mv88e6xxx_ops mv88e6352_ops = {
...@@ -3475,6 +3299,10 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { ...@@ -3475,6 +3299,10 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
.set_switch_mac = mv88e6xxx_g2_set_switch_mac, .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
.phy_read = mv88e6xxx_g2_smi_phy_read, .phy_read = mv88e6xxx_g2_smi_phy_read,
.phy_write = mv88e6xxx_g2_smi_phy_write, .phy_write = mv88e6xxx_g2_smi_phy_write,
.port_set_link = mv88e6xxx_port_set_link,
.port_set_duplex = mv88e6xxx_port_set_duplex,
.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
.port_set_speed = mv88e6352_port_set_speed,
}; };
static const struct mv88e6xxx_info mv88e6xxx_table[] = { static const struct mv88e6xxx_info mv88e6xxx_table[] = {
......
...@@ -61,16 +61,22 @@ ...@@ -61,16 +61,22 @@
#define PORT_PCS_CTRL 0x01 #define PORT_PCS_CTRL 0x01
#define PORT_PCS_CTRL_RGMII_DELAY_RXCLK BIT(15) #define PORT_PCS_CTRL_RGMII_DELAY_RXCLK BIT(15)
#define PORT_PCS_CTRL_RGMII_DELAY_TXCLK BIT(14) #define PORT_PCS_CTRL_RGMII_DELAY_TXCLK BIT(14)
#define PORT_PCS_CTRL_FORCE_SPEED BIT(13) /* 6390 */
#define PORT_PCS_CTRL_ALTSPEED BIT(12) /* 6390 */
#define PORT_PCS_CTRL_200BASE BIT(12) /* 6352 */
#define PORT_PCS_CTRL_FC BIT(7) #define PORT_PCS_CTRL_FC BIT(7)
#define PORT_PCS_CTRL_FORCE_FC BIT(6) #define PORT_PCS_CTRL_FORCE_FC BIT(6)
#define PORT_PCS_CTRL_LINK_UP BIT(5) #define PORT_PCS_CTRL_LINK_UP BIT(5)
#define PORT_PCS_CTRL_FORCE_LINK BIT(4) #define PORT_PCS_CTRL_FORCE_LINK BIT(4)
#define PORT_PCS_CTRL_DUPLEX_FULL BIT(3) #define PORT_PCS_CTRL_DUPLEX_FULL BIT(3)
#define PORT_PCS_CTRL_FORCE_DUPLEX BIT(2) #define PORT_PCS_CTRL_FORCE_DUPLEX BIT(2)
#define PORT_PCS_CTRL_10 0x00 #define PORT_PCS_CTRL_SPEED_MASK (0x03)
#define PORT_PCS_CTRL_100 0x01 #define PORT_PCS_CTRL_SPEED_10 (0x00)
#define PORT_PCS_CTRL_1000 0x02 #define PORT_PCS_CTRL_SPEED_100 (0x01)
#define PORT_PCS_CTRL_UNFORCED 0x03 #define PORT_PCS_CTRL_SPEED_200 (0x02) /* 6065 and non Gb chips */
#define PORT_PCS_CTRL_SPEED_1000 (0x02)
#define PORT_PCS_CTRL_SPEED_10000 (0x03) /* 6390X */
#define PORT_PCS_CTRL_SPEED_UNFORCED (0x03)
#define PORT_PAUSE_CTRL 0x02 #define PORT_PAUSE_CTRL 0x02
#define PORT_SWITCH_ID 0x03 #define PORT_SWITCH_ID 0x03
#define PORT_SWITCH_ID_PROD_NUM_6085 0x04a #define PORT_SWITCH_ID_PROD_NUM_6085 0x04a
...@@ -727,6 +733,41 @@ struct mv88e6xxx_ops { ...@@ -727,6 +733,41 @@ struct mv88e6xxx_ops {
u16 *val); u16 *val);
int (*phy_write)(struct mv88e6xxx_chip *chip, int addr, int reg, int (*phy_write)(struct mv88e6xxx_chip *chip, int addr, int reg,
u16 val); u16 val);
/* RGMII Receive/Transmit Timing Control
* Add delay on PHY_INTERFACE_MODE_RGMII_*ID, no delay otherwise.
*/
int (*port_set_rgmii_delay)(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode);
#define LINK_FORCED_DOWN 0
#define LINK_FORCED_UP 1
#define LINK_UNFORCED -2
/* Port's MAC link state
* Use LINK_FORCED_UP or LINK_FORCED_DOWN to force link up or down,
* or LINK_UNFORCED for normal link detection.
*/
int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link);
#define DUPLEX_UNFORCED -2
/* Port's MAC duplex mode
*
* Use DUPLEX_HALF or DUPLEX_FULL to force half or full duplex,
* or DUPLEX_UNFORCED for normal duplex detection.
*/
int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup);
#define SPEED_MAX INT_MAX
#define SPEED_UNFORCED -2
/* Port's MAC speed (in Mbps)
*
* Depending on the chip, 10, 100, 200, 1000, 2500, 10000 are valid.
* Use SPEED_UNFORCED for normal detection, SPEED_MAX for max value.
*/
int (*port_set_speed)(struct mv88e6xxx_chip *chip, int port, int speed);
}; };
enum stat_type { enum stat_type {
......
/*
* Marvell 88E6xxx Switch Port Registers support
*
* Copyright (c) 2008 Marvell Semiconductor
*
* Copyright (c) 2016 Vivien Didelot <vivien.didelot@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include "mv88e6xxx.h"
#include "port.h"
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);
}
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);
}
/* Offset 0x01: MAC (or PCS or Physical) Control Register
*
* Link, Duplex and Flow Control have one force bit, one value bit.
*
* 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.
*/
static int mv88e6xxx_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode)
{
u16 reg;
int err;
err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
if (err)
return err;
reg &= ~(PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
switch (mode) {
case PHY_INTERFACE_MODE_RGMII_RXID:
reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
break;
case PHY_INTERFACE_MODE_RGMII_TXID:
reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
break;
case PHY_INTERFACE_MODE_RGMII_ID:
reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
break;
default:
/* no delay */
break;
}
err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
if (err)
return err;
netdev_dbg(chip->ds->ports[port].netdev, "delay RXCLK %s, TXCLK %s\n",
reg & PORT_PCS_CTRL_RGMII_DELAY_RXCLK ? "yes" : "no",
reg & PORT_PCS_CTRL_RGMII_DELAY_TXCLK ? "yes" : "no");
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);
}
int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link)
{
u16 reg;
int err;
err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
if (err)
return err;
reg &= ~(PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP);
switch (link) {
case LINK_FORCED_DOWN:
reg |= PORT_PCS_CTRL_FORCE_LINK;
break;
case LINK_FORCED_UP:
reg |= PORT_PCS_CTRL_FORCE_LINK | PORT_PCS_CTRL_LINK_UP;
break;
case LINK_UNFORCED:
/* normal link detection */
break;
default:
return -EINVAL;
}
err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
if (err)
return err;
netdev_dbg(chip->ds->ports[port].netdev, "%s link %s\n",
reg & PORT_PCS_CTRL_FORCE_LINK ? "Force" : "Unforce",
reg & PORT_PCS_CTRL_LINK_UP ? "up" : "down");
return 0;
}
int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup)
{
u16 reg;
int err;
err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
if (err)
return err;
reg &= ~(PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL);
switch (dup) {
case DUPLEX_HALF:
reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
break;
case DUPLEX_FULL:
reg |= PORT_PCS_CTRL_FORCE_DUPLEX | PORT_PCS_CTRL_DUPLEX_FULL;
break;
case DUPLEX_UNFORCED:
/* normal duplex detection */
break;
default:
return -EINVAL;
}
err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
if (err)
return err;
netdev_dbg(chip->ds->ports[port].netdev, "%s %s duplex\n",
reg & PORT_PCS_CTRL_FORCE_DUPLEX ? "Force" : "Unforce",
reg & PORT_PCS_CTRL_DUPLEX_FULL ? "full" : "half");
return 0;
}
static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port,
int speed, bool alt_bit, bool force_bit)
{
u16 reg, ctrl;
int err;
switch (speed) {
case 10:
ctrl = PORT_PCS_CTRL_SPEED_10;
break;
case 100:
ctrl = PORT_PCS_CTRL_SPEED_100;
break;
case 200:
if (alt_bit)
ctrl = PORT_PCS_CTRL_SPEED_100 | PORT_PCS_CTRL_ALTSPEED;
else
ctrl = PORT_PCS_CTRL_SPEED_200;
break;
case 1000:
ctrl = PORT_PCS_CTRL_SPEED_1000;
break;
case 2500:
ctrl = PORT_PCS_CTRL_SPEED_1000 | PORT_PCS_CTRL_ALTSPEED;
break;
case 10000:
/* all bits set, fall through... */
case SPEED_UNFORCED:
ctrl = PORT_PCS_CTRL_SPEED_UNFORCED;
break;
default:
return -EOPNOTSUPP;
}
err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
if (err)
return err;
reg &= ~PORT_PCS_CTRL_SPEED_MASK;
if (alt_bit)
reg &= ~PORT_PCS_CTRL_ALTSPEED;
if (force_bit) {
reg &= ~PORT_PCS_CTRL_FORCE_SPEED;
if (speed)
ctrl |= PORT_PCS_CTRL_FORCE_SPEED;
}
reg |= ctrl;
err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
if (err)
return err;
if (speed)
netdev_dbg(chip->ds->ports[port].netdev,
"Speed set to %d Mbps\n", speed);
else
netdev_dbg(chip->ds->ports[port].netdev, "Speed unforced\n");
return 0;
}
/* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */
int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
{
if (speed == SPEED_MAX)
speed = 200;
if (speed > 200)
return -EOPNOTSUPP;
/* Setting 200 Mbps on port 0 to 3 selects 100 Mbps */
return mv88e6xxx_port_set_speed(chip, port, speed, false, false);
}
/* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */
int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
{
if (speed == SPEED_MAX)
speed = 1000;
if (speed == 200 || speed > 1000)
return -EOPNOTSUPP;
return mv88e6xxx_port_set_speed(chip, port, speed, false, false);
}
/* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */
int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
{
if (speed == SPEED_MAX)
speed = 1000;
if (speed > 1000)
return -EOPNOTSUPP;
if (speed == 200 && port < 5)
return -EOPNOTSUPP;
return mv88e6xxx_port_set_speed(chip, port, speed, true, false);
}
/* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */
int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
{
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;
return mv88e6xxx_port_set_speed(chip, port, speed, true, true);
}
/* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */
int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed)
{
if (speed == SPEED_MAX)
speed = port < 9 ? 1000 : 10000;
if (speed == 200 && port != 0)
return -EOPNOTSUPP;
if (speed >= 2500 && port < 9)
return -EOPNOTSUPP;
return mv88e6xxx_port_set_speed(chip, port, speed, true, true);
}
/* Offset 0x04: Port Control Register */
static const char * const mv88e6xxx_port_state_names[] = {
[PORT_CONTROL_STATE_DISABLED] = "Disabled",
[PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
[PORT_CONTROL_STATE_LEARNING] = "Learning",
[PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
};
int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state)
{
u16 reg;
int err;
err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
if (err)
return err;
reg &= ~PORT_CONTROL_STATE_MASK;
reg |= state;
err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
if (err)
return err;
netdev_dbg(chip->ds->ports[port].netdev, "PortState set to %s\n",
mv88e6xxx_port_state_names[state]);
return 0;
}
/* Offset 0x05: Port Control 1 */
/* Offset 0x06: Port Based VLAN Map */
int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)
{
const u16 mask = GENMASK(mv88e6xxx_num_ports(chip) - 1, 0);
u16 reg;
int err;
err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
if (err)
return err;
reg &= ~mask;
reg |= map & mask;
err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
if (err)
return err;
netdev_dbg(chip->ds->ports[port].netdev, "VLANTable set to %.3x\n",
map);
return 0;
}
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 */
err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
if (err)
return err;
*fid = (reg & 0xf000) >> 12;
/* Port's default FID upper bits are located in reg 0x05, offset 0 */
if (upper_mask) {
err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
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 */
err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
if (err)
return err;
reg &= 0x0fff;
reg |= (fid & 0x000f) << 12;
err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
if (err)
return err;
/* Port's default FID upper bits are located in reg 0x05, offset 0 */
if (upper_mask) {
err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
if (err)
return err;
reg &= ~upper_mask;
reg |= (fid >> 4) & upper_mask;
err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg);
if (err)
return err;
}
netdev_dbg(chip->ds->ports[port].netdev, "FID set to %u\n", fid);
return 0;
}
/* Offset 0x07: Default Port VLAN ID & Priority */
int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid)
{
u16 reg;
int err;
err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
if (err)
return err;
*pvid = reg & PORT_DEFAULT_VLAN_MASK;
return 0;
}
int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid)
{
u16 reg;
int err;
err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
if (err)
return err;
reg &= ~PORT_DEFAULT_VLAN_MASK;
reg |= pvid & PORT_DEFAULT_VLAN_MASK;
err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg);
if (err)
return err;
netdev_dbg(chip->ds->ports[port].netdev, "DefaultVID set to %u\n",
pvid);
return 0;
}
/* Offset 0x08: Port Control 2 Register */
static const char * const mv88e6xxx_port_8021q_mode_names[] = {
[PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
[PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
[PORT_CONTROL_2_8021Q_CHECK] = "Check",
[PORT_CONTROL_2_8021Q_SECURE] = "Secure",
};
int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
u16 mode)
{
u16 reg;
int err;
err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
if (err)
return err;
reg &= ~PORT_CONTROL_2_8021Q_MASK;
reg |= mode & PORT_CONTROL_2_8021Q_MASK;
err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
if (err)
return err;
netdev_dbg(chip->ds->ports[port].netdev, "802.1QMode set to %s\n",
mv88e6xxx_port_8021q_mode_names[mode]);
return 0;
}
/*
* Marvell 88E6xxx Switch Port Registers support
*
* Copyright (c) 2008 Marvell Semiconductor
*
* Copyright (c) 2016 Vivien Didelot <vivien.didelot@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef _MV88E6XXX_PORT_H
#define _MV88E6XXX_PORT_H
#include "mv88e6xxx.h"
int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
u16 *val);
int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
u16 val);
int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode);
int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode);
int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link);
int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup);
int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed);
int mv88e6xxx_port_set_state(struct mv88e6xxx_chip *chip, int port, u8 state);
int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map);
int mv88e6xxx_port_get_fid(struct mv88e6xxx_chip *chip, int port, u16 *fid);
int mv88e6xxx_port_set_fid(struct mv88e6xxx_chip *chip, int port, u16 fid);
int mv88e6xxx_port_get_pvid(struct mv88e6xxx_chip *chip, int port, u16 *pvid);
int mv88e6xxx_port_set_pvid(struct mv88e6xxx_chip *chip, int port, u16 pvid);
int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
u16 mode);
#endif /* _MV88E6XXX_PORT_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment