Commit 99949a74 authored by David S. Miller's avatar David S. Miller

Merge branch 'dsa-next'

Andrew Lunn says:

====================
DSA Mavell drivers refactoring and cleanup

v1->v2:
 * Add missing signed-of-by: For patches authored by Guenter Roeck.
 * Add Reviewed by from Guenter Roack to patch #5.

This is a collection of patches again net-next from today containing
refactoring and consolidate of code, cleanups and using #define's to
replace register numbers.

Patch #1 Swaps the 6131 driver to use the consolidated setup code.

Patch #2 Moves the Switch IDs used during probe into a central
         location.  We need these later so that we can differentiate
         the different features the devices have.

Patch #3 Makes the 6131 driver set the number of ports in the private
         state structure. It then uses this, rather than hard coded
         maximum number of ports.

Patch #4 Similar to Patch #3, but for the 6123_61_65 driver.

Patch #5 Similar to Patch #3, and #4, but for all the remaining
         drivers.  This greatly increases the similarity of the code
         between drivers, allow further patches to consolidate the
         duplicated code.

Patch #6 Consolidate the switch reset code, which has two minor
         variants. Removes around 35 lines per driver.

Patch #7 Moves phy page access functions out of the 6352 driver into
         the shared code. Currently only the 6352 driver uses this,
         but it is likely other devices will come along wanting this
         functionality.

Patch #8 Consolidates the code used to access phy registers. Removes
         around 40 lines of code per driver.

Patch #9 Fixes missing mutex locking in the EEE code, and refactors
         the code a bit to make it more understandable with respect to
         locks.

Patch #10 Consolidates reading statistics. This is very similar code
          for all devices, but the number of available statistics
          differ, which can be determined from the product ID. Removes
	  around 65 lines per driver.

Patch #11 Add #defines for registers, and bits within the
          registers. For the moment, this is limited to the shared
          code. The individual drivers will be converted once the
          remaining duplicated code is consolidated

Patch #12 Fix broken statistic counters on the 6172. The 6352 family
          requires the port number is poked into a different set of
          bits in the register compared to other devices.

Many thanks to Guenter Roeck for repeatedly reviewing the patches and
testing them on his hardware.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 05e8bb86 f3a8b6b6
...@@ -25,66 +25,33 @@ static char *mv88e6123_61_65_probe(struct device *host_dev, int sw_addr) ...@@ -25,66 +25,33 @@ static char *mv88e6123_61_65_probe(struct device *host_dev, int sw_addr)
if (bus == NULL) if (bus == NULL)
return NULL; return NULL;
ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
if (ret >= 0) { if (ret >= 0) {
if (ret == 0x1212) if (ret == PORT_SWITCH_ID_6123_A1)
return "Marvell 88E6123 (A1)"; return "Marvell 88E6123 (A1)";
if (ret == 0x1213) if (ret == PORT_SWITCH_ID_6123_A2)
return "Marvell 88E6123 (A2)"; return "Marvell 88E6123 (A2)";
if ((ret & 0xfff0) == 0x1210) if ((ret & 0xfff0) == PORT_SWITCH_ID_6123)
return "Marvell 88E6123"; return "Marvell 88E6123";
if (ret == 0x1612) if (ret == PORT_SWITCH_ID_6161_A1)
return "Marvell 88E6161 (A1)"; return "Marvell 88E6161 (A1)";
if (ret == 0x1613) if (ret == PORT_SWITCH_ID_6161_A2)
return "Marvell 88E6161 (A2)"; return "Marvell 88E6161 (A2)";
if ((ret & 0xfff0) == 0x1610) if ((ret & 0xfff0) == PORT_SWITCH_ID_6161)
return "Marvell 88E6161"; return "Marvell 88E6161";
if (ret == 0x1652) if (ret == PORT_SWITCH_ID_6165_A1)
return "Marvell 88E6165 (A1)"; return "Marvell 88E6165 (A1)";
if (ret == 0x1653) if (ret == PORT_SWITCH_ID_6165_A2)
return "Marvell 88e6165 (A2)"; return "Marvell 88e6165 (A2)";
if ((ret & 0xfff0) == 0x1650) if ((ret & 0xfff0) == PORT_SWITCH_ID_6165)
return "Marvell 88E6165"; return "Marvell 88E6165";
} }
return NULL; return NULL;
} }
static int mv88e6123_61_65_switch_reset(struct dsa_switch *ds)
{
int i;
int ret;
unsigned long timeout;
/* Set all ports to the disabled state. */
for (i = 0; i < 8; i++) {
ret = REG_READ(REG_PORT(i), 0x04);
REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
}
/* Wait for transmit queues to drain. */
usleep_range(2000, 4000);
/* Reset the switch. */
REG_WRITE(REG_GLOBAL, 0x04, 0xc400);
/* Wait up to one second for reset to complete. */
timeout = jiffies + 1 * HZ;
while (time_before(jiffies, timeout)) {
ret = REG_READ(REG_GLOBAL, 0x00);
if ((ret & 0xc800) == 0xc800)
break;
usleep_range(1000, 2000);
}
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
return 0;
}
static int mv88e6123_61_65_setup_global(struct dsa_switch *ds) static int mv88e6123_61_65_setup_global(struct dsa_switch *ds)
{ {
int ret; int ret;
...@@ -271,6 +238,7 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p) ...@@ -271,6 +238,7 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p)
static int mv88e6123_61_65_setup(struct dsa_switch *ds) static int mv88e6123_61_65_setup(struct dsa_switch *ds)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int i; int i;
int ret; int ret;
...@@ -278,7 +246,19 @@ static int mv88e6123_61_65_setup(struct dsa_switch *ds) ...@@ -278,7 +246,19 @@ static int mv88e6123_61_65_setup(struct dsa_switch *ds)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = mv88e6123_61_65_switch_reset(ds); switch (ps->id) {
case PORT_SWITCH_ID_6123:
ps->num_ports = 3;
break;
case PORT_SWITCH_ID_6161:
case PORT_SWITCH_ID_6165:
ps->num_ports = 6;
break;
default:
return -ENODEV;
}
ret = mv88e6xxx_switch_reset(ds, false);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -288,7 +268,7 @@ static int mv88e6123_61_65_setup(struct dsa_switch *ds) ...@@ -288,7 +268,7 @@ static int mv88e6123_61_65_setup(struct dsa_switch *ds)
if (ret < 0) if (ret < 0)
return ret; return ret;
for (i = 0; i < 6; i++) { for (i = 0; i < ps->num_ports; i++) {
ret = mv88e6123_61_65_setup_port(ds, i); ret = mv88e6123_61_65_setup_port(ds, i);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -297,108 +277,18 @@ static int mv88e6123_61_65_setup(struct dsa_switch *ds) ...@@ -297,108 +277,18 @@ static int mv88e6123_61_65_setup(struct dsa_switch *ds)
return 0; return 0;
} }
static int mv88e6123_61_65_port_to_phy_addr(int port)
{
if (port >= 0 && port <= 4)
return port;
return -1;
}
static int
mv88e6123_61_65_phy_read(struct dsa_switch *ds, int port, int regnum)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int addr = mv88e6123_61_65_port_to_phy_addr(port);
int ret;
mutex_lock(&ps->phy_mutex);
ret = mv88e6xxx_phy_read(ds, addr, regnum);
mutex_unlock(&ps->phy_mutex);
return ret;
}
static int
mv88e6123_61_65_phy_write(struct dsa_switch *ds,
int port, int regnum, u16 val)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int addr = mv88e6123_61_65_port_to_phy_addr(port);
int ret;
mutex_lock(&ps->phy_mutex);
ret = mv88e6xxx_phy_write(ds, addr, regnum, val);
mutex_unlock(&ps->phy_mutex);
return ret;
}
static struct mv88e6xxx_hw_stat mv88e6123_61_65_hw_stats[] = {
{ "in_good_octets", 8, 0x00, },
{ "in_bad_octets", 4, 0x02, },
{ "in_unicast", 4, 0x04, },
{ "in_broadcasts", 4, 0x06, },
{ "in_multicasts", 4, 0x07, },
{ "in_pause", 4, 0x16, },
{ "in_undersize", 4, 0x18, },
{ "in_fragments", 4, 0x19, },
{ "in_oversize", 4, 0x1a, },
{ "in_jabber", 4, 0x1b, },
{ "in_rx_error", 4, 0x1c, },
{ "in_fcs_error", 4, 0x1d, },
{ "out_octets", 8, 0x0e, },
{ "out_unicast", 4, 0x10, },
{ "out_broadcasts", 4, 0x13, },
{ "out_multicasts", 4, 0x12, },
{ "out_pause", 4, 0x15, },
{ "excessive", 4, 0x11, },
{ "collisions", 4, 0x1e, },
{ "deferred", 4, 0x05, },
{ "single", 4, 0x14, },
{ "multiple", 4, 0x17, },
{ "out_fcs_error", 4, 0x03, },
{ "late", 4, 0x1f, },
{ "hist_64bytes", 4, 0x08, },
{ "hist_65_127bytes", 4, 0x09, },
{ "hist_128_255bytes", 4, 0x0a, },
{ "hist_256_511bytes", 4, 0x0b, },
{ "hist_512_1023bytes", 4, 0x0c, },
{ "hist_1024_max_bytes", 4, 0x0d, },
{ "sw_in_discards", 4, 0x110, },
{ "sw_in_filtered", 2, 0x112, },
{ "sw_out_filtered", 2, 0x113, },
};
static void
mv88e6123_61_65_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
{
mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6123_61_65_hw_stats),
mv88e6123_61_65_hw_stats, port, data);
}
static void
mv88e6123_61_65_get_ethtool_stats(struct dsa_switch *ds,
int port, uint64_t *data)
{
mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6123_61_65_hw_stats),
mv88e6123_61_65_hw_stats, port, data);
}
static int mv88e6123_61_65_get_sset_count(struct dsa_switch *ds)
{
return ARRAY_SIZE(mv88e6123_61_65_hw_stats);
}
struct dsa_switch_driver mv88e6123_61_65_switch_driver = { struct dsa_switch_driver mv88e6123_61_65_switch_driver = {
.tag_protocol = DSA_TAG_PROTO_EDSA, .tag_protocol = DSA_TAG_PROTO_EDSA,
.priv_size = sizeof(struct mv88e6xxx_priv_state), .priv_size = sizeof(struct mv88e6xxx_priv_state),
.probe = mv88e6123_61_65_probe, .probe = mv88e6123_61_65_probe,
.setup = mv88e6123_61_65_setup, .setup = mv88e6123_61_65_setup,
.set_addr = mv88e6xxx_set_addr_indirect, .set_addr = mv88e6xxx_set_addr_indirect,
.phy_read = mv88e6123_61_65_phy_read, .phy_read = mv88e6xxx_phy_read,
.phy_write = mv88e6123_61_65_phy_write, .phy_write = mv88e6xxx_phy_write,
.poll_link = mv88e6xxx_poll_link, .poll_link = mv88e6xxx_poll_link,
.get_strings = mv88e6123_61_65_get_strings, .get_strings = mv88e6xxx_get_strings,
.get_ethtool_stats = mv88e6123_61_65_get_ethtool_stats, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
.get_sset_count = mv88e6123_61_65_get_sset_count, .get_sset_count = mv88e6xxx_get_sset_count,
#ifdef CONFIG_NET_DSA_HWMON #ifdef CONFIG_NET_DSA_HWMON
.get_temp = mv88e6xxx_get_temp, .get_temp = mv88e6xxx_get_temp,
#endif #endif
......
...@@ -17,12 +17,6 @@ ...@@ -17,12 +17,6 @@
#include <net/dsa.h> #include <net/dsa.h>
#include "mv88e6xxx.h" #include "mv88e6xxx.h"
/* Switch product IDs */
#define ID_6085 0x04a0
#define ID_6095 0x0950
#define ID_6131 0x1060
#define ID_6131_B2 0x1066
static char *mv88e6131_probe(struct device *host_dev, int sw_addr) static char *mv88e6131_probe(struct device *host_dev, int sw_addr)
{ {
struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev); struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev);
...@@ -31,56 +25,23 @@ static char *mv88e6131_probe(struct device *host_dev, int sw_addr) ...@@ -31,56 +25,23 @@ static char *mv88e6131_probe(struct device *host_dev, int sw_addr)
if (bus == NULL) if (bus == NULL)
return NULL; return NULL;
ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
if (ret >= 0) { if (ret >= 0) {
int ret_masked = ret & 0xfff0; int ret_masked = ret & 0xfff0;
if (ret_masked == ID_6085) if (ret_masked == PORT_SWITCH_ID_6085)
return "Marvell 88E6085"; return "Marvell 88E6085";
if (ret_masked == ID_6095) if (ret_masked == PORT_SWITCH_ID_6095)
return "Marvell 88E6095/88E6095F"; return "Marvell 88E6095/88E6095F";
if (ret == ID_6131_B2) if (ret == PORT_SWITCH_ID_6131_B2)
return "Marvell 88E6131 (B2)"; return "Marvell 88E6131 (B2)";
if (ret_masked == ID_6131) if (ret_masked == PORT_SWITCH_ID_6131)
return "Marvell 88E6131"; return "Marvell 88E6131";
} }
return NULL; return NULL;
} }
static int mv88e6131_switch_reset(struct dsa_switch *ds)
{
int i;
int ret;
unsigned long timeout;
/* Set all ports to the disabled state. */
for (i = 0; i < 11; i++) {
ret = REG_READ(REG_PORT(i), 0x04);
REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
}
/* Wait for transmit queues to drain. */
usleep_range(2000, 4000);
/* Reset the switch. */
REG_WRITE(REG_GLOBAL, 0x04, 0xc400);
/* Wait up to one second for reset to complete. */
timeout = jiffies + 1 * HZ;
while (time_before(jiffies, timeout)) {
ret = REG_READ(REG_GLOBAL, 0x00);
if ((ret & 0xc800) == 0xc800)
break;
usleep_range(1000, 2000);
}
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
return 0;
}
static int mv88e6131_setup_global(struct dsa_switch *ds) static int mv88e6131_setup_global(struct dsa_switch *ds)
{ {
int ret; int ret;
...@@ -174,7 +135,7 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p) ...@@ -174,7 +135,7 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p)
* (100 Mb/s on 6085) full duplex. * (100 Mb/s on 6085) full duplex.
*/ */
if (dsa_is_cpu_port(ds, p) || ds->dsa_port_mask & (1 << p)) if (dsa_is_cpu_port(ds, p) || ds->dsa_port_mask & (1 << p))
if (ps->id == ID_6085) if (ps->id == PORT_SWITCH_ID_6085)
REG_WRITE(addr, 0x01, 0x003d); /* 100 Mb/s */ REG_WRITE(addr, 0x01, 0x003d); /* 100 Mb/s */
else else
REG_WRITE(addr, 0x01, 0x003e); /* 1000 Mb/s */ REG_WRITE(addr, 0x01, 0x003e); /* 1000 Mb/s */
...@@ -201,35 +162,13 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p) ...@@ -201,35 +162,13 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p)
/* On 6085, unknown multicast forward is controlled /* On 6085, unknown multicast forward is controlled
* here rather than in Port Control 2 register. * here rather than in Port Control 2 register.
*/ */
if (ps->id == ID_6085) if (ps->id == PORT_SWITCH_ID_6085)
val |= 0x0008; val |= 0x0008;
} }
if (ds->dsa_port_mask & (1 << p)) if (ds->dsa_port_mask & (1 << p))
val |= 0x0100; val |= 0x0100;
REG_WRITE(addr, 0x04, val); REG_WRITE(addr, 0x04, val);
/* Port Control 1: disable trunking. Also, if this is the
* CPU port, enable learn messages to be sent to this port.
*/
REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000);
/* Port based VLAN map: give each port its own address
* database, allow the CPU port to talk to each of the 'real'
* ports, and allow each of the 'real' ports to only talk to
* the upstream port.
*/
val = (p & 0xf) << 12;
if (dsa_is_cpu_port(ds, p))
val |= ds->phys_port_mask;
else
val |= 1 << dsa_upstream_port(ds);
REG_WRITE(addr, 0x06, val);
/* Default VLAN ID and priority: don't set a default VLAN
* ID, and set the default packet priority to zero.
*/
REG_WRITE(addr, 0x07, 0x0000);
/* Port Control 2: don't force a good FCS, don't use /* Port Control 2: don't force a good FCS, don't use
* VLAN-based, source address-based or destination * VLAN-based, source address-based or destination
* address-based priority overrides, don't let the switch * address-based priority overrides, don't let the switch
...@@ -242,7 +181,7 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p) ...@@ -242,7 +181,7 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p)
* If this is the upstream port for this switch, enable * If this is the upstream port for this switch, enable
* forwarding of unknown multicast addresses. * forwarding of unknown multicast addresses.
*/ */
if (ps->id == ID_6085) if (ps->id == PORT_SWITCH_ID_6085)
/* on 6085, bits 3:0 are reserved, bit 6 control ARP /* on 6085, bits 3:0 are reserved, bit 6 control ARP
* mirroring, and multicast forward is handled in * mirroring, and multicast forward is handled in
* Port Control register. * Port Control register.
...@@ -278,7 +217,7 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p) ...@@ -278,7 +217,7 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p)
*/ */
REG_WRITE(addr, 0x19, 0x7654); REG_WRITE(addr, 0x19, 0x7654);
return 0; return mv88e6xxx_setup_port_common(ds, p);
} }
static int mv88e6131_setup(struct dsa_switch *ds) static int mv88e6131_setup(struct dsa_switch *ds)
...@@ -287,13 +226,28 @@ static int mv88e6131_setup(struct dsa_switch *ds) ...@@ -287,13 +226,28 @@ static int mv88e6131_setup(struct dsa_switch *ds)
int i; int i;
int ret; int ret;
mutex_init(&ps->smi_mutex); ret = mv88e6xxx_setup_common(ds);
if (ret < 0)
return ret;
mv88e6xxx_ppu_state_init(ds); mv88e6xxx_ppu_state_init(ds);
mutex_init(&ps->stats_mutex);
ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0; switch (ps->id) {
case PORT_SWITCH_ID_6085:
ps->num_ports = 10;
break;
case PORT_SWITCH_ID_6095:
ps->num_ports = 11;
break;
case PORT_SWITCH_ID_6131:
case PORT_SWITCH_ID_6131_B2:
ps->num_ports = 8;
break;
default:
return -ENODEV;
}
ret = mv88e6131_switch_reset(ds); ret = mv88e6xxx_switch_reset(ds, false);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -303,7 +257,7 @@ static int mv88e6131_setup(struct dsa_switch *ds) ...@@ -303,7 +257,7 @@ static int mv88e6131_setup(struct dsa_switch *ds)
if (ret < 0) if (ret < 0)
return ret; return ret;
for (i = 0; i < 11; i++) { for (i = 0; i < ps->num_ports; i++) {
ret = mv88e6131_setup_port(ds, i); ret = mv88e6131_setup_port(ds, i);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -312,17 +266,24 @@ static int mv88e6131_setup(struct dsa_switch *ds) ...@@ -312,17 +266,24 @@ static int mv88e6131_setup(struct dsa_switch *ds)
return 0; return 0;
} }
static int mv88e6131_port_to_phy_addr(int port) static int mv88e6131_port_to_phy_addr(struct dsa_switch *ds, int port)
{ {
if (port >= 0 && port <= 11) struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
if (port >= 0 && port < ps->num_ports)
return port; return port;
return -1;
return -EINVAL;
} }
static int static int
mv88e6131_phy_read(struct dsa_switch *ds, int port, int regnum) mv88e6131_phy_read(struct dsa_switch *ds, int port, int regnum)
{ {
int addr = mv88e6131_port_to_phy_addr(port); int addr = mv88e6131_port_to_phy_addr(ds, port);
if (addr < 0)
return addr;
return mv88e6xxx_phy_read_ppu(ds, addr, regnum); return mv88e6xxx_phy_read_ppu(ds, addr, regnum);
} }
...@@ -330,61 +291,12 @@ static int ...@@ -330,61 +291,12 @@ static int
mv88e6131_phy_write(struct dsa_switch *ds, mv88e6131_phy_write(struct dsa_switch *ds,
int port, int regnum, u16 val) int port, int regnum, u16 val)
{ {
int addr = mv88e6131_port_to_phy_addr(port); int addr = mv88e6131_port_to_phy_addr(ds, port);
return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val);
}
static struct mv88e6xxx_hw_stat mv88e6131_hw_stats[] = {
{ "in_good_octets", 8, 0x00, },
{ "in_bad_octets", 4, 0x02, },
{ "in_unicast", 4, 0x04, },
{ "in_broadcasts", 4, 0x06, },
{ "in_multicasts", 4, 0x07, },
{ "in_pause", 4, 0x16, },
{ "in_undersize", 4, 0x18, },
{ "in_fragments", 4, 0x19, },
{ "in_oversize", 4, 0x1a, },
{ "in_jabber", 4, 0x1b, },
{ "in_rx_error", 4, 0x1c, },
{ "in_fcs_error", 4, 0x1d, },
{ "out_octets", 8, 0x0e, },
{ "out_unicast", 4, 0x10, },
{ "out_broadcasts", 4, 0x13, },
{ "out_multicasts", 4, 0x12, },
{ "out_pause", 4, 0x15, },
{ "excessive", 4, 0x11, },
{ "collisions", 4, 0x1e, },
{ "deferred", 4, 0x05, },
{ "single", 4, 0x14, },
{ "multiple", 4, 0x17, },
{ "out_fcs_error", 4, 0x03, },
{ "late", 4, 0x1f, },
{ "hist_64bytes", 4, 0x08, },
{ "hist_65_127bytes", 4, 0x09, },
{ "hist_128_255bytes", 4, 0x0a, },
{ "hist_256_511bytes", 4, 0x0b, },
{ "hist_512_1023bytes", 4, 0x0c, },
{ "hist_1024_max_bytes", 4, 0x0d, },
};
static void if (addr < 0)
mv88e6131_get_strings(struct dsa_switch *ds, int port, uint8_t *data) return addr;
{
mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6131_hw_stats),
mv88e6131_hw_stats, port, data);
}
static void return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val);
mv88e6131_get_ethtool_stats(struct dsa_switch *ds,
int port, uint64_t *data)
{
mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6131_hw_stats),
mv88e6131_hw_stats, port, data);
}
static int mv88e6131_get_sset_count(struct dsa_switch *ds)
{
return ARRAY_SIZE(mv88e6131_hw_stats);
} }
struct dsa_switch_driver mv88e6131_switch_driver = { struct dsa_switch_driver mv88e6131_switch_driver = {
...@@ -396,9 +308,9 @@ struct dsa_switch_driver mv88e6131_switch_driver = { ...@@ -396,9 +308,9 @@ struct dsa_switch_driver mv88e6131_switch_driver = {
.phy_read = mv88e6131_phy_read, .phy_read = mv88e6131_phy_read,
.phy_write = mv88e6131_phy_write, .phy_write = mv88e6131_phy_write,
.poll_link = mv88e6xxx_poll_link, .poll_link = mv88e6xxx_poll_link,
.get_strings = mv88e6131_get_strings, .get_strings = mv88e6xxx_get_strings,
.get_ethtool_stats = mv88e6131_get_ethtool_stats, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
.get_sset_count = mv88e6131_get_sset_count, .get_sset_count = mv88e6xxx_get_sset_count,
}; };
MODULE_ALIAS("platform:mv88e6085"); MODULE_ALIAS("platform:mv88e6085");
......
...@@ -17,10 +17,6 @@ ...@@ -17,10 +17,6 @@
#include <net/dsa.h> #include <net/dsa.h>
#include "mv88e6xxx.h" #include "mv88e6xxx.h"
/* Switch product IDs */
#define ID_6171 0x1710
#define ID_6172 0x1720
static char *mv88e6171_probe(struct device *host_dev, int sw_addr) static char *mv88e6171_probe(struct device *host_dev, int sw_addr)
{ {
struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev); struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev);
...@@ -29,64 +25,20 @@ static char *mv88e6171_probe(struct device *host_dev, int sw_addr) ...@@ -29,64 +25,20 @@ static char *mv88e6171_probe(struct device *host_dev, int sw_addr)
if (bus == NULL) if (bus == NULL)
return NULL; return NULL;
ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
if (ret >= 0) { if (ret >= 0) {
if ((ret & 0xfff0) == ID_6171) if ((ret & 0xfff0) == PORT_SWITCH_ID_6171)
return "Marvell 88E6171"; return "Marvell 88E6171";
if ((ret & 0xfff0) == ID_6172) if ((ret & 0xfff0) == PORT_SWITCH_ID_6172)
return "Marvell 88E6172"; return "Marvell 88E6172";
} }
return NULL; return NULL;
} }
static int mv88e6171_switch_reset(struct dsa_switch *ds)
{
int i;
int ret;
unsigned long timeout;
/* Set all ports to the disabled state. */
for (i = 0; i < 8; i++) {
ret = REG_READ(REG_PORT(i), 0x04);
REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
}
/* Wait for transmit queues to drain. */
usleep_range(2000, 4000);
/* Reset the switch. Keep PPU active. The PPU needs to be
* active to support indirect phy register accesses through
* global registers 0x18 and 0x19.
*/
REG_WRITE(REG_GLOBAL, 0x04, 0xc000);
/* Wait up to one second for reset to complete. */
timeout = jiffies + 1 * HZ;
while (time_before(jiffies, timeout)) {
ret = REG_READ(REG_GLOBAL, 0x00);
if ((ret & 0xc800) == 0xc800)
break;
usleep_range(1000, 2000);
}
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
/* Enable ports not under DSA, e.g. WAN port */
for (i = 0; i < 8; i++) {
if (dsa_is_cpu_port(ds, i) || ds->phys_port_mask & (1 << i))
continue;
ret = REG_READ(REG_PORT(i), 0x04);
REG_WRITE(REG_PORT(i), 0x04, ret | 0x03);
}
return 0;
}
static int mv88e6171_setup_global(struct dsa_switch *ds) static int mv88e6171_setup_global(struct dsa_switch *ds)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret; int ret;
int i; int i;
...@@ -151,7 +103,7 @@ static int mv88e6171_setup_global(struct dsa_switch *ds) ...@@ -151,7 +103,7 @@ static int mv88e6171_setup_global(struct dsa_switch *ds)
} }
/* Clear all trunk masks. */ /* Clear all trunk masks. */
for (i = 0; i < 8; i++) for (i = 0; i < ps->num_ports; i++)
REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0xff); REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0xff);
/* Clear all trunk mappings. */ /* Clear all trunk mappings. */
...@@ -274,6 +226,7 @@ static int mv88e6171_setup_port(struct dsa_switch *ds, int p) ...@@ -274,6 +226,7 @@ static int mv88e6171_setup_port(struct dsa_switch *ds, int p)
static int mv88e6171_setup(struct dsa_switch *ds) static int mv88e6171_setup(struct dsa_switch *ds)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int i; int i;
int ret; int ret;
...@@ -281,7 +234,9 @@ static int mv88e6171_setup(struct dsa_switch *ds) ...@@ -281,7 +234,9 @@ static int mv88e6171_setup(struct dsa_switch *ds)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = mv88e6171_switch_reset(ds); ps->num_ports = 7;
ret = mv88e6xxx_switch_reset(ds, true);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -291,7 +246,7 @@ static int mv88e6171_setup(struct dsa_switch *ds) ...@@ -291,7 +246,7 @@ static int mv88e6171_setup(struct dsa_switch *ds)
if (ret < 0) if (ret < 0)
return ret; return ret;
for (i = 0; i < 8; i++) { for (i = 0; i < ps->num_ports; i++) {
if (!(dsa_is_cpu_port(ds, i) || ds->phys_port_mask & (1 << i))) if (!(dsa_is_cpu_port(ds, i) || ds->phys_port_mask & (1 << i)))
continue; continue;
...@@ -303,99 +258,12 @@ static int mv88e6171_setup(struct dsa_switch *ds) ...@@ -303,99 +258,12 @@ static int mv88e6171_setup(struct dsa_switch *ds)
return 0; return 0;
} }
static int mv88e6171_port_to_phy_addr(int port)
{
if (port >= 0 && port <= 4)
return port;
return -1;
}
static int
mv88e6171_phy_read(struct dsa_switch *ds, int port, int regnum)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int addr = mv88e6171_port_to_phy_addr(port);
int ret;
mutex_lock(&ps->phy_mutex);
ret = mv88e6xxx_phy_read_indirect(ds, addr, regnum);
mutex_unlock(&ps->phy_mutex);
return ret;
}
static int
mv88e6171_phy_write(struct dsa_switch *ds,
int port, int regnum, u16 val)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int addr = mv88e6171_port_to_phy_addr(port);
int ret;
mutex_lock(&ps->phy_mutex);
ret = mv88e6xxx_phy_write_indirect(ds, addr, regnum, val);
mutex_unlock(&ps->phy_mutex);
return ret;
}
static struct mv88e6xxx_hw_stat mv88e6171_hw_stats[] = {
{ "in_good_octets", 8, 0x00, },
{ "in_bad_octets", 4, 0x02, },
{ "in_unicast", 4, 0x04, },
{ "in_broadcasts", 4, 0x06, },
{ "in_multicasts", 4, 0x07, },
{ "in_pause", 4, 0x16, },
{ "in_undersize", 4, 0x18, },
{ "in_fragments", 4, 0x19, },
{ "in_oversize", 4, 0x1a, },
{ "in_jabber", 4, 0x1b, },
{ "in_rx_error", 4, 0x1c, },
{ "in_fcs_error", 4, 0x1d, },
{ "out_octets", 8, 0x0e, },
{ "out_unicast", 4, 0x10, },
{ "out_broadcasts", 4, 0x13, },
{ "out_multicasts", 4, 0x12, },
{ "out_pause", 4, 0x15, },
{ "excessive", 4, 0x11, },
{ "collisions", 4, 0x1e, },
{ "deferred", 4, 0x05, },
{ "single", 4, 0x14, },
{ "multiple", 4, 0x17, },
{ "out_fcs_error", 4, 0x03, },
{ "late", 4, 0x1f, },
{ "hist_64bytes", 4, 0x08, },
{ "hist_65_127bytes", 4, 0x09, },
{ "hist_128_255bytes", 4, 0x0a, },
{ "hist_256_511bytes", 4, 0x0b, },
{ "hist_512_1023bytes", 4, 0x0c, },
{ "hist_1024_max_bytes", 4, 0x0d, },
};
static void
mv88e6171_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
{
mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6171_hw_stats),
mv88e6171_hw_stats, port, data);
}
static void
mv88e6171_get_ethtool_stats(struct dsa_switch *ds,
int port, uint64_t *data)
{
mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6171_hw_stats),
mv88e6171_hw_stats, port, data);
}
static int mv88e6171_get_sset_count(struct dsa_switch *ds)
{
return ARRAY_SIZE(mv88e6171_hw_stats);
}
static int mv88e6171_get_eee(struct dsa_switch *ds, int port, static int mv88e6171_get_eee(struct dsa_switch *ds, int port,
struct ethtool_eee *e) struct ethtool_eee *e)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
if (ps->id == ID_6172) if (ps->id == PORT_SWITCH_ID_6172)
return mv88e6xxx_get_eee(ds, port, e); return mv88e6xxx_get_eee(ds, port, e);
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -406,7 +274,7 @@ static int mv88e6171_set_eee(struct dsa_switch *ds, int port, ...@@ -406,7 +274,7 @@ static int mv88e6171_set_eee(struct dsa_switch *ds, int port,
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
if (ps->id == ID_6172) if (ps->id == PORT_SWITCH_ID_6172)
return mv88e6xxx_set_eee(ds, port, phydev, e); return mv88e6xxx_set_eee(ds, port, phydev, e);
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -418,12 +286,12 @@ struct dsa_switch_driver mv88e6171_switch_driver = { ...@@ -418,12 +286,12 @@ struct dsa_switch_driver mv88e6171_switch_driver = {
.probe = mv88e6171_probe, .probe = mv88e6171_probe,
.setup = mv88e6171_setup, .setup = mv88e6171_setup,
.set_addr = mv88e6xxx_set_addr_indirect, .set_addr = mv88e6xxx_set_addr_indirect,
.phy_read = mv88e6171_phy_read, .phy_read = mv88e6xxx_phy_read_indirect,
.phy_write = mv88e6171_phy_write, .phy_write = mv88e6xxx_phy_write_indirect,
.poll_link = mv88e6xxx_poll_link, .poll_link = mv88e6xxx_poll_link,
.get_strings = mv88e6171_get_strings, .get_strings = mv88e6xxx_get_strings,
.get_ethtool_stats = mv88e6171_get_ethtool_stats, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
.get_sset_count = mv88e6171_get_sset_count, .get_sset_count = mv88e6xxx_get_sset_count,
.set_eee = mv88e6171_set_eee, .set_eee = mv88e6171_set_eee,
.get_eee = mv88e6171_get_eee, .get_eee = mv88e6171_get_eee,
#ifdef CONFIG_NET_DSA_HWMON #ifdef CONFIG_NET_DSA_HWMON
......
...@@ -30,58 +30,24 @@ static char *mv88e6352_probe(struct device *host_dev, int sw_addr) ...@@ -30,58 +30,24 @@ static char *mv88e6352_probe(struct device *host_dev, int sw_addr)
if (bus == NULL) if (bus == NULL)
return NULL; return NULL;
ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
if (ret >= 0) { if (ret >= 0) {
if ((ret & 0xfff0) == 0x1760) if ((ret & 0xfff0) == PORT_SWITCH_ID_6176)
return "Marvell 88E6176"; return "Marvell 88E6176";
if (ret == 0x3521) if (ret == PORT_SWITCH_ID_6352_A0)
return "Marvell 88E6352 (A0)"; return "Marvell 88E6352 (A0)";
if (ret == 0x3522) if (ret == PORT_SWITCH_ID_6352_A1)
return "Marvell 88E6352 (A1)"; return "Marvell 88E6352 (A1)";
if ((ret & 0xfff0) == 0x3520) if ((ret & 0xfff0) == PORT_SWITCH_ID_6352)
return "Marvell 88E6352"; return "Marvell 88E6352";
} }
return NULL; return NULL;
} }
static int mv88e6352_switch_reset(struct dsa_switch *ds)
{
unsigned long timeout;
int ret;
int i;
/* Set all ports to the disabled state. */
for (i = 0; i < 7; i++) {
ret = REG_READ(REG_PORT(i), 0x04);
REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
}
/* Wait for transmit queues to drain. */
usleep_range(2000, 4000);
/* Reset the switch. Keep PPU active (bit 14, undocumented).
* The PPU needs to be active to support indirect phy register
* accesses through global registers 0x18 and 0x19.
*/
REG_WRITE(REG_GLOBAL, 0x04, 0xc000);
/* Wait up to one second for reset to complete. */
timeout = jiffies + 1 * HZ;
while (time_before(jiffies, timeout)) {
ret = REG_READ(REG_GLOBAL, 0x00);
if ((ret & 0x8800) == 0x8800)
break;
usleep_range(1000, 2000);
}
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
return 0;
}
static int mv88e6352_setup_global(struct dsa_switch *ds) static int mv88e6352_setup_global(struct dsa_switch *ds)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret; int ret;
int i; int i;
...@@ -152,7 +118,7 @@ static int mv88e6352_setup_global(struct dsa_switch *ds) ...@@ -152,7 +118,7 @@ static int mv88e6352_setup_global(struct dsa_switch *ds)
/* Disable ingress rate limiting by resetting all ingress /* Disable ingress rate limiting by resetting all ingress
* rate limit registers to their initial state. * rate limit registers to their initial state.
*/ */
for (i = 0; i < 7; i++) for (i = 0; i < ps->num_ports; i++)
REG_WRITE(REG_GLOBAL2, 0x09, 0x9000 | (i << 8)); REG_WRITE(REG_GLOBAL2, 0x09, 0x9000 | (i << 8));
/* Initialise cross-chip port VLAN table to reset defaults. */ /* Initialise cross-chip port VLAN table to reset defaults. */
...@@ -264,48 +230,13 @@ static int mv88e6352_setup_port(struct dsa_switch *ds, int p) ...@@ -264,48 +230,13 @@ static int mv88e6352_setup_port(struct dsa_switch *ds, int p)
#ifdef CONFIG_NET_DSA_HWMON #ifdef CONFIG_NET_DSA_HWMON
static int mv88e6352_phy_page_read(struct dsa_switch *ds,
int port, int page, int reg)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
mutex_lock(&ps->phy_mutex);
ret = mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
if (ret < 0)
goto error;
ret = mv88e6xxx_phy_read_indirect(ds, port, reg);
error:
mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
mutex_unlock(&ps->phy_mutex);
return ret;
}
static int mv88e6352_phy_page_write(struct dsa_switch *ds,
int port, int page, int reg, int val)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
mutex_lock(&ps->phy_mutex);
ret = mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
if (ret < 0)
goto error;
ret = mv88e6xxx_phy_write_indirect(ds, port, reg, val);
error:
mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
mutex_unlock(&ps->phy_mutex);
return ret;
}
static int mv88e6352_get_temp(struct dsa_switch *ds, int *temp) static int mv88e6352_get_temp(struct dsa_switch *ds, int *temp)
{ {
int ret; int ret;
*temp = 0; *temp = 0;
ret = mv88e6352_phy_page_read(ds, 0, 6, 27); ret = mv88e6xxx_phy_page_read(ds, 0, 6, 27);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -320,7 +251,7 @@ static int mv88e6352_get_temp_limit(struct dsa_switch *ds, int *temp) ...@@ -320,7 +251,7 @@ static int mv88e6352_get_temp_limit(struct dsa_switch *ds, int *temp)
*temp = 0; *temp = 0;
ret = mv88e6352_phy_page_read(ds, 0, 6, 26); ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -333,11 +264,11 @@ static int mv88e6352_set_temp_limit(struct dsa_switch *ds, int temp) ...@@ -333,11 +264,11 @@ static int mv88e6352_set_temp_limit(struct dsa_switch *ds, int temp)
{ {
int ret; int ret;
ret = mv88e6352_phy_page_read(ds, 0, 6, 26); ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26);
if (ret < 0) if (ret < 0)
return ret; return ret;
temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f); temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
return mv88e6352_phy_page_write(ds, 0, 6, 26, return mv88e6xxx_phy_page_write(ds, 0, 6, 26,
(ret & 0xe0ff) | (temp << 8)); (ret & 0xe0ff) | (temp << 8));
} }
...@@ -347,7 +278,7 @@ static int mv88e6352_get_temp_alarm(struct dsa_switch *ds, bool *alarm) ...@@ -347,7 +278,7 @@ static int mv88e6352_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
*alarm = false; *alarm = false;
ret = mv88e6352_phy_page_read(ds, 0, 6, 26); ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -367,9 +298,11 @@ static int mv88e6352_setup(struct dsa_switch *ds) ...@@ -367,9 +298,11 @@ static int mv88e6352_setup(struct dsa_switch *ds)
if (ret < 0) if (ret < 0)
return ret; return ret;
ps->num_ports = 7;
mutex_init(&ps->eeprom_mutex); mutex_init(&ps->eeprom_mutex);
ret = mv88e6352_switch_reset(ds); ret = mv88e6xxx_switch_reset(ds, true);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -379,7 +312,7 @@ static int mv88e6352_setup(struct dsa_switch *ds) ...@@ -379,7 +312,7 @@ static int mv88e6352_setup(struct dsa_switch *ds)
if (ret < 0) if (ret < 0)
return ret; return ret;
for (i = 0; i < 7; i++) { for (i = 0; i < ps->num_ports; i++) {
ret = mv88e6352_setup_port(ds, i); ret = mv88e6352_setup_port(ds, i);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -388,83 +321,6 @@ static int mv88e6352_setup(struct dsa_switch *ds) ...@@ -388,83 +321,6 @@ static int mv88e6352_setup(struct dsa_switch *ds)
return 0; return 0;
} }
static int mv88e6352_port_to_phy_addr(int port)
{
if (port >= 0 && port <= 4)
return port;
return -EINVAL;
}
static int
mv88e6352_phy_read(struct dsa_switch *ds, int port, int regnum)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int addr = mv88e6352_port_to_phy_addr(port);
int ret;
if (addr < 0)
return addr;
mutex_lock(&ps->phy_mutex);
ret = mv88e6xxx_phy_read_indirect(ds, addr, regnum);
mutex_unlock(&ps->phy_mutex);
return ret;
}
static int
mv88e6352_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int addr = mv88e6352_port_to_phy_addr(port);
int ret;
if (addr < 0)
return addr;
mutex_lock(&ps->phy_mutex);
ret = mv88e6xxx_phy_write_indirect(ds, addr, regnum, val);
mutex_unlock(&ps->phy_mutex);
return ret;
}
static struct mv88e6xxx_hw_stat mv88e6352_hw_stats[] = {
{ "in_good_octets", 8, 0x00, },
{ "in_bad_octets", 4, 0x02, },
{ "in_unicast", 4, 0x04, },
{ "in_broadcasts", 4, 0x06, },
{ "in_multicasts", 4, 0x07, },
{ "in_pause", 4, 0x16, },
{ "in_undersize", 4, 0x18, },
{ "in_fragments", 4, 0x19, },
{ "in_oversize", 4, 0x1a, },
{ "in_jabber", 4, 0x1b, },
{ "in_rx_error", 4, 0x1c, },
{ "in_fcs_error", 4, 0x1d, },
{ "out_octets", 8, 0x0e, },
{ "out_unicast", 4, 0x10, },
{ "out_broadcasts", 4, 0x13, },
{ "out_multicasts", 4, 0x12, },
{ "out_pause", 4, 0x15, },
{ "excessive", 4, 0x11, },
{ "collisions", 4, 0x1e, },
{ "deferred", 4, 0x05, },
{ "single", 4, 0x14, },
{ "multiple", 4, 0x17, },
{ "out_fcs_error", 4, 0x03, },
{ "late", 4, 0x1f, },
{ "hist_64bytes", 4, 0x08, },
{ "hist_65_127bytes", 4, 0x09, },
{ "hist_128_255bytes", 4, 0x0a, },
{ "hist_256_511bytes", 4, 0x0b, },
{ "hist_512_1023bytes", 4, 0x0c, },
{ "hist_1024_max_bytes", 4, 0x0d, },
{ "sw_in_discards", 4, 0x110, },
{ "sw_in_filtered", 2, 0x112, },
{ "sw_out_filtered", 2, 0x113, },
};
static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr) static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
...@@ -663,37 +519,18 @@ static int mv88e6352_set_eeprom(struct dsa_switch *ds, ...@@ -663,37 +519,18 @@ static int mv88e6352_set_eeprom(struct dsa_switch *ds,
return 0; return 0;
} }
static void
mv88e6352_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
{
mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6352_hw_stats),
mv88e6352_hw_stats, port, data);
}
static void
mv88e6352_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
{
mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6352_hw_stats),
mv88e6352_hw_stats, port, data);
}
static int mv88e6352_get_sset_count(struct dsa_switch *ds)
{
return ARRAY_SIZE(mv88e6352_hw_stats);
}
struct dsa_switch_driver mv88e6352_switch_driver = { struct dsa_switch_driver mv88e6352_switch_driver = {
.tag_protocol = DSA_TAG_PROTO_EDSA, .tag_protocol = DSA_TAG_PROTO_EDSA,
.priv_size = sizeof(struct mv88e6xxx_priv_state), .priv_size = sizeof(struct mv88e6xxx_priv_state),
.probe = mv88e6352_probe, .probe = mv88e6352_probe,
.setup = mv88e6352_setup, .setup = mv88e6352_setup,
.set_addr = mv88e6xxx_set_addr_indirect, .set_addr = mv88e6xxx_set_addr_indirect,
.phy_read = mv88e6352_phy_read, .phy_read = mv88e6xxx_phy_read_indirect,
.phy_write = mv88e6352_phy_write, .phy_write = mv88e6xxx_phy_write_indirect,
.poll_link = mv88e6xxx_poll_link, .poll_link = mv88e6xxx_poll_link,
.get_strings = mv88e6352_get_strings, .get_strings = mv88e6xxx_get_strings,
.get_ethtool_stats = mv88e6352_get_ethtool_stats, .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
.get_sset_count = mv88e6352_get_sset_count, .get_sset_count = mv88e6xxx_get_sset_count,
.set_eee = mv88e6xxx_set_eee, .set_eee = mv88e6xxx_set_eee,
.get_eee = mv88e6xxx_get_eee, .get_eee = mv88e6xxx_get_eee,
#ifdef CONFIG_NET_DSA_HWMON #ifdef CONFIG_NET_DSA_HWMON
......
...@@ -33,11 +33,11 @@ static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr) ...@@ -33,11 +33,11 @@ static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr)
int i; int i;
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
ret = mdiobus_read(bus, sw_addr, 0); ret = mdiobus_read(bus, sw_addr, SMI_CMD);
if (ret < 0) if (ret < 0)
return ret; return ret;
if ((ret & 0x8000) == 0) if ((ret & SMI_CMD_BUSY) == 0)
return 0; return 0;
} }
...@@ -57,7 +57,8 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg) ...@@ -57,7 +57,8 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg)
return ret; return ret;
/* Transmit the read command. */ /* Transmit the read command. */
ret = mdiobus_write(bus, sw_addr, 0, 0x9800 | (addr << 5) | reg); ret = mdiobus_write(bus, sw_addr, SMI_CMD,
SMI_CMD_OP_22_READ | (addr << 5) | reg);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -67,7 +68,7 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg) ...@@ -67,7 +68,7 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg)
return ret; return ret;
/* Read the data. */ /* Read the data. */
ret = mdiobus_read(bus, sw_addr, 1); ret = mdiobus_read(bus, sw_addr, SMI_DATA);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -119,12 +120,13 @@ int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr, ...@@ -119,12 +120,13 @@ int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
return ret; return ret;
/* Transmit the data to write. */ /* Transmit the data to write. */
ret = mdiobus_write(bus, sw_addr, 1, val); ret = mdiobus_write(bus, sw_addr, SMI_DATA, val);
if (ret < 0) if (ret < 0)
return ret; return ret;
/* Transmit the write command. */ /* Transmit the write command. */
ret = mdiobus_write(bus, sw_addr, 0, 0x9400 | (addr << 5) | reg); ret = mdiobus_write(bus, sw_addr, SMI_CMD,
SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -166,26 +168,26 @@ int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val) ...@@ -166,26 +168,26 @@ int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val)
int mv88e6xxx_config_prio(struct dsa_switch *ds) int mv88e6xxx_config_prio(struct dsa_switch *ds)
{ {
/* Configure the IP ToS mapping registers. */ /* Configure the IP ToS mapping registers. */
REG_WRITE(REG_GLOBAL, 0x10, 0x0000); REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
REG_WRITE(REG_GLOBAL, 0x11, 0x0000); REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
REG_WRITE(REG_GLOBAL, 0x12, 0x5555); REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
REG_WRITE(REG_GLOBAL, 0x13, 0x5555); REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
REG_WRITE(REG_GLOBAL, 0x14, 0xaaaa); REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
REG_WRITE(REG_GLOBAL, 0x15, 0xaaaa); REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
REG_WRITE(REG_GLOBAL, 0x16, 0xffff); REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
REG_WRITE(REG_GLOBAL, 0x17, 0xffff); REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
/* Configure the IEEE 802.1p priority mapping register. */ /* Configure the IEEE 802.1p priority mapping register. */
REG_WRITE(REG_GLOBAL, 0x18, 0xfa41); REG_WRITE(REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
return 0; return 0;
} }
int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr) int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr)
{ {
REG_WRITE(REG_GLOBAL, 0x01, (addr[0] << 8) | addr[1]); REG_WRITE(REG_GLOBAL, GLOBAL_MAC_01, (addr[0] << 8) | addr[1]);
REG_WRITE(REG_GLOBAL, 0x02, (addr[2] << 8) | addr[3]); REG_WRITE(REG_GLOBAL, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]);
REG_WRITE(REG_GLOBAL, 0x03, (addr[4] << 8) | addr[5]); REG_WRITE(REG_GLOBAL, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]);
return 0; return 0;
} }
...@@ -199,12 +201,13 @@ int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) ...@@ -199,12 +201,13 @@ int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
int j; int j;
/* Write the MAC address byte. */ /* Write the MAC address byte. */
REG_WRITE(REG_GLOBAL2, 0x0d, 0x8000 | (i << 8) | addr[i]); REG_WRITE(REG_GLOBAL2, GLOBAL2_SWITCH_MAC,
GLOBAL2_SWITCH_MAC_BUSY | (i << 8) | addr[i]);
/* Wait for the write to complete. */ /* Wait for the write to complete. */
for (j = 0; j < 16; j++) { for (j = 0; j < 16; j++) {
ret = REG_READ(REG_GLOBAL2, 0x0d); ret = REG_READ(REG_GLOBAL2, GLOBAL2_SWITCH_MAC);
if ((ret & 0x8000) == 0) if ((ret & GLOBAL2_SWITCH_MAC_BUSY) == 0)
break; break;
} }
if (j == 16) if (j == 16)
...@@ -214,14 +217,17 @@ int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) ...@@ -214,14 +217,17 @@ int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
return 0; return 0;
} }
int mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum) /* Must be called with phy mutex held */
static int _mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum)
{ {
if (addr >= 0) if (addr >= 0)
return mv88e6xxx_reg_read(ds, addr, regnum); return mv88e6xxx_reg_read(ds, addr, regnum);
return 0xffff; return 0xffff;
} }
int mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val) /* Must be called with phy mutex held */
static int _mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum,
u16 val)
{ {
if (addr >= 0) if (addr >= 0)
return mv88e6xxx_reg_write(ds, addr, regnum, val); return mv88e6xxx_reg_write(ds, addr, regnum, val);
...@@ -234,14 +240,16 @@ static int mv88e6xxx_ppu_disable(struct dsa_switch *ds) ...@@ -234,14 +240,16 @@ static int mv88e6xxx_ppu_disable(struct dsa_switch *ds)
int ret; int ret;
unsigned long timeout; unsigned long timeout;
ret = REG_READ(REG_GLOBAL, 0x04); ret = REG_READ(REG_GLOBAL, GLOBAL_CONTROL);
REG_WRITE(REG_GLOBAL, 0x04, ret & ~0x4000); REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL,
ret & ~GLOBAL_CONTROL_PPU_ENABLE);
timeout = jiffies + 1 * HZ; timeout = jiffies + 1 * HZ;
while (time_before(jiffies, timeout)) { while (time_before(jiffies, timeout)) {
ret = REG_READ(REG_GLOBAL, 0x00); ret = REG_READ(REG_GLOBAL, GLOBAL_STATUS);
usleep_range(1000, 2000); usleep_range(1000, 2000);
if ((ret & 0xc000) != 0xc000) if ((ret & GLOBAL_STATUS_PPU_MASK) !=
GLOBAL_STATUS_PPU_POLLING)
return 0; return 0;
} }
...@@ -253,14 +261,15 @@ static int mv88e6xxx_ppu_enable(struct dsa_switch *ds) ...@@ -253,14 +261,15 @@ static int mv88e6xxx_ppu_enable(struct dsa_switch *ds)
int ret; int ret;
unsigned long timeout; unsigned long timeout;
ret = REG_READ(REG_GLOBAL, 0x04); ret = REG_READ(REG_GLOBAL, GLOBAL_CONTROL);
REG_WRITE(REG_GLOBAL, 0x04, ret | 0x4000); REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, ret | GLOBAL_CONTROL_PPU_ENABLE);
timeout = jiffies + 1 * HZ; timeout = jiffies + 1 * HZ;
while (time_before(jiffies, timeout)) { while (time_before(jiffies, timeout)) {
ret = REG_READ(REG_GLOBAL, 0x00); ret = REG_READ(REG_GLOBAL, GLOBAL_STATUS);
usleep_range(1000, 2000); usleep_range(1000, 2000);
if ((ret & 0xc000) == 0xc000) if ((ret & GLOBAL_STATUS_PPU_MASK) ==
GLOBAL_STATUS_PPU_POLLING)
return 0; return 0;
} }
...@@ -381,11 +390,12 @@ void mv88e6xxx_poll_link(struct dsa_switch *ds) ...@@ -381,11 +390,12 @@ void mv88e6xxx_poll_link(struct dsa_switch *ds)
link = 0; link = 0;
if (dev->flags & IFF_UP) { if (dev->flags & IFF_UP) {
port_status = mv88e6xxx_reg_read(ds, REG_PORT(i), 0x00); port_status = mv88e6xxx_reg_read(ds, REG_PORT(i),
PORT_STATUS);
if (port_status < 0) if (port_status < 0)
continue; continue;
link = !!(port_status & 0x0800); link = !!(port_status & PORT_STATUS_LINK);
} }
if (!link) { if (!link) {
...@@ -396,22 +406,22 @@ void mv88e6xxx_poll_link(struct dsa_switch *ds) ...@@ -396,22 +406,22 @@ void mv88e6xxx_poll_link(struct dsa_switch *ds)
continue; continue;
} }
switch (port_status & 0x0300) { switch (port_status & PORT_STATUS_SPEED_MASK) {
case 0x0000: case PORT_STATUS_SPEED_10:
speed = 10; speed = 10;
break; break;
case 0x0100: case PORT_STATUS_SPEED_100:
speed = 100; speed = 100;
break; break;
case 0x0200: case PORT_STATUS_SPEED_1000:
speed = 1000; speed = 1000;
break; break;
default: default:
speed = -1; speed = -1;
break; break;
} }
duplex = (port_status & 0x0400) ? 1 : 0; duplex = (port_status & PORT_STATUS_DUPLEX) ? 1 : 0;
fc = (port_status & 0x8000) ? 1 : 0; fc = (port_status & PORT_STATUS_PAUSE_EN) ? 1 : 0;
if (!netif_carrier_ok(dev)) { if (!netif_carrier_ok(dev)) {
netdev_info(dev, netdev_info(dev,
...@@ -424,14 +434,27 @@ void mv88e6xxx_poll_link(struct dsa_switch *ds) ...@@ -424,14 +434,27 @@ void mv88e6xxx_poll_link(struct dsa_switch *ds)
} }
} }
static bool mv88e6xxx_6352_family(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
switch (ps->id) {
case PORT_SWITCH_ID_6352:
case PORT_SWITCH_ID_6172:
case PORT_SWITCH_ID_6176:
return true;
}
return false;
}
static int mv88e6xxx_stats_wait(struct dsa_switch *ds) static int mv88e6xxx_stats_wait(struct dsa_switch *ds)
{ {
int ret; int ret;
int i; int i;
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
ret = REG_READ(REG_GLOBAL, 0x1d); ret = REG_READ(REG_GLOBAL, GLOBAL_STATS_OP);
if ((ret & 0x8000) == 0) if ((ret & GLOBAL_STATS_OP_BUSY) == 0)
return 0; return 0;
} }
...@@ -442,8 +465,13 @@ static int mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port) ...@@ -442,8 +465,13 @@ static int mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port)
{ {
int ret; int ret;
if (mv88e6xxx_6352_family(ds))
port = (port + 1) << 5;
/* Snapshot the hardware statistics counters for this port. */ /* Snapshot the hardware statistics counters for this port. */
REG_WRITE(REG_GLOBAL, 0x1d, 0xdc00 | port); REG_WRITE(REG_GLOBAL, GLOBAL_STATS_OP,
GLOBAL_STATS_OP_CAPTURE_PORT |
GLOBAL_STATS_OP_HIST_RX_TX | port);
/* Wait for the snapshotting to complete. */ /* Wait for the snapshotting to complete. */
ret = mv88e6xxx_stats_wait(ds); ret = mv88e6xxx_stats_wait(ds);
...@@ -460,7 +488,9 @@ static void mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val) ...@@ -460,7 +488,9 @@ static void mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val)
*val = 0; *val = 0;
ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x1d, 0xcc00 | stat); ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP,
GLOBAL_STATS_OP_READ_CAPTURED |
GLOBAL_STATS_OP_HIST_RX_TX | stat);
if (ret < 0) if (ret < 0)
return; return;
...@@ -468,21 +498,76 @@ static void mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val) ...@@ -468,21 +498,76 @@ static void mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val)
if (ret < 0) if (ret < 0)
return; return;
ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x1e); ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
if (ret < 0) if (ret < 0)
return; return;
_val = ret << 16; _val = ret << 16;
ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x1f); ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
if (ret < 0) if (ret < 0)
return; return;
*val = _val | ret; *val = _val | ret;
} }
void mv88e6xxx_get_strings(struct dsa_switch *ds, static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
int nr_stats, struct mv88e6xxx_hw_stat *stats, { "in_good_octets", 8, 0x00, },
{ "in_bad_octets", 4, 0x02, },
{ "in_unicast", 4, 0x04, },
{ "in_broadcasts", 4, 0x06, },
{ "in_multicasts", 4, 0x07, },
{ "in_pause", 4, 0x16, },
{ "in_undersize", 4, 0x18, },
{ "in_fragments", 4, 0x19, },
{ "in_oversize", 4, 0x1a, },
{ "in_jabber", 4, 0x1b, },
{ "in_rx_error", 4, 0x1c, },
{ "in_fcs_error", 4, 0x1d, },
{ "out_octets", 8, 0x0e, },
{ "out_unicast", 4, 0x10, },
{ "out_broadcasts", 4, 0x13, },
{ "out_multicasts", 4, 0x12, },
{ "out_pause", 4, 0x15, },
{ "excessive", 4, 0x11, },
{ "collisions", 4, 0x1e, },
{ "deferred", 4, 0x05, },
{ "single", 4, 0x14, },
{ "multiple", 4, 0x17, },
{ "out_fcs_error", 4, 0x03, },
{ "late", 4, 0x1f, },
{ "hist_64bytes", 4, 0x08, },
{ "hist_65_127bytes", 4, 0x09, },
{ "hist_128_255bytes", 4, 0x0a, },
{ "hist_256_511bytes", 4, 0x0b, },
{ "hist_512_1023bytes", 4, 0x0c, },
{ "hist_1024_max_bytes", 4, 0x0d, },
/* Not all devices have the following counters */
{ "sw_in_discards", 4, 0x110, },
{ "sw_in_filtered", 2, 0x112, },
{ "sw_out_filtered", 2, 0x113, },
};
static bool have_sw_in_discards(struct dsa_switch *ds)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
switch (ps->id) {
case PORT_SWITCH_ID_6095: case PORT_SWITCH_ID_6161:
case PORT_SWITCH_ID_6165: case PORT_SWITCH_ID_6171:
case PORT_SWITCH_ID_6172: case PORT_SWITCH_ID_6176:
case PORT_SWITCH_ID_6182: case PORT_SWITCH_ID_6185:
case PORT_SWITCH_ID_6352:
return true;
default:
return false;
}
}
static void _mv88e6xxx_get_strings(struct dsa_switch *ds,
int nr_stats,
struct mv88e6xxx_hw_stat *stats,
int port, uint8_t *data) int port, uint8_t *data)
{ {
int i; int i;
...@@ -493,8 +578,9 @@ void mv88e6xxx_get_strings(struct dsa_switch *ds, ...@@ -493,8 +578,9 @@ void mv88e6xxx_get_strings(struct dsa_switch *ds,
} }
} }
void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
int nr_stats, struct mv88e6xxx_hw_stat *stats, int nr_stats,
struct mv88e6xxx_hw_stat *stats,
int port, uint64_t *data) int port, uint64_t *data)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
...@@ -543,6 +629,39 @@ void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, ...@@ -543,6 +629,39 @@ void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
mutex_unlock(&ps->stats_mutex); mutex_unlock(&ps->stats_mutex);
} }
/* All the statistics in the table */
void
mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
{
if (have_sw_in_discards(ds))
_mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats),
mv88e6xxx_hw_stats, port, data);
else
_mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3,
mv88e6xxx_hw_stats, port, data);
}
int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
{
if (have_sw_in_discards(ds))
return ARRAY_SIZE(mv88e6xxx_hw_stats);
return ARRAY_SIZE(mv88e6xxx_hw_stats) - 3;
}
void
mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
int port, uint64_t *data)
{
if (have_sw_in_discards(ds))
_mv88e6xxx_get_ethtool_stats(
ds, ARRAY_SIZE(mv88e6xxx_hw_stats),
mv88e6xxx_hw_stats, port, data);
else
_mv88e6xxx_get_ethtool_stats(
ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3,
mv88e6xxx_hw_stats, port, data);
}
int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port) int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
{ {
return 32 * sizeof(u16); return 32 * sizeof(u16);
...@@ -579,37 +698,37 @@ int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp) ...@@ -579,37 +698,37 @@ int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
mutex_lock(&ps->phy_mutex); mutex_lock(&ps->phy_mutex);
ret = mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x6); ret = _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x6);
if (ret < 0) if (ret < 0)
goto error; goto error;
/* Enable temperature sensor */ /* Enable temperature sensor */
ret = mv88e6xxx_phy_read(ds, 0x0, 0x1a); ret = _mv88e6xxx_phy_read(ds, 0x0, 0x1a);
if (ret < 0) if (ret < 0)
goto error; goto error;
ret = mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret | (1 << 5)); ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret | (1 << 5));
if (ret < 0) if (ret < 0)
goto error; goto error;
/* Wait for temperature to stabilize */ /* Wait for temperature to stabilize */
usleep_range(10000, 12000); usleep_range(10000, 12000);
val = mv88e6xxx_phy_read(ds, 0x0, 0x1a); val = _mv88e6xxx_phy_read(ds, 0x0, 0x1a);
if (val < 0) { if (val < 0) {
ret = val; ret = val;
goto error; goto error;
} }
/* Disable temperature sensor */ /* Disable temperature sensor */
ret = mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret & ~(1 << 5)); ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret & ~(1 << 5));
if (ret < 0) if (ret < 0)
goto error; goto error;
*temp = ((val & 0x1f) - 5) * 5; *temp = ((val & 0x1f) - 5) * 5;
error: error:
mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x0); _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x0);
mutex_unlock(&ps->phy_mutex); mutex_unlock(&ps->phy_mutex);
return ret; return ret;
} }
...@@ -633,17 +752,20 @@ static int mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask) ...@@ -633,17 +752,20 @@ static int mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask)
int mv88e6xxx_phy_wait(struct dsa_switch *ds) int mv88e6xxx_phy_wait(struct dsa_switch *ds)
{ {
return mv88e6xxx_wait(ds, REG_GLOBAL2, 0x18, 0x8000); return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_SMI_OP,
GLOBAL2_SMI_OP_BUSY);
} }
int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds) int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
{ {
return mv88e6xxx_wait(ds, REG_GLOBAL2, 0x14, 0x0800); return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
GLOBAL2_EEPROM_OP_LOAD);
} }
int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds) int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
{ {
return mv88e6xxx_wait(ds, REG_GLOBAL2, 0x14, 0x8000); return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
GLOBAL2_EEPROM_OP_BUSY);
} }
/* Must be called with SMI lock held */ /* Must be called with SMI lock held */
...@@ -668,80 +790,87 @@ static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask) ...@@ -668,80 +790,87 @@ static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask)
/* Must be called with SMI lock held */ /* Must be called with SMI lock held */
static int _mv88e6xxx_atu_wait(struct dsa_switch *ds) static int _mv88e6xxx_atu_wait(struct dsa_switch *ds)
{ {
return _mv88e6xxx_wait(ds, REG_GLOBAL, 0x0b, ATU_BUSY); return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_ATU_OP,
GLOBAL_ATU_OP_BUSY);
} }
int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr, int regnum) /* Must be called with phy mutex held */
static int _mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr,
int regnum)
{ {
int ret; int ret;
REG_WRITE(REG_GLOBAL2, 0x18, 0x9800 | (addr << 5) | regnum); REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_OP,
GLOBAL2_SMI_OP_22_READ | (addr << 5) | regnum);
ret = mv88e6xxx_phy_wait(ds); ret = mv88e6xxx_phy_wait(ds);
if (ret < 0) if (ret < 0)
return ret; return ret;
return REG_READ(REG_GLOBAL2, 0x19); return REG_READ(REG_GLOBAL2, GLOBAL2_SMI_DATA);
} }
int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, int regnum, /* Must be called with phy mutex held */
u16 val) static int _mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr,
int regnum, u16 val)
{ {
REG_WRITE(REG_GLOBAL2, 0x19, val); REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_DATA, val);
REG_WRITE(REG_GLOBAL2, 0x18, 0x9400 | (addr << 5) | regnum); REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_OP,
GLOBAL2_SMI_OP_22_WRITE | (addr << 5) | regnum);
return mv88e6xxx_phy_wait(ds); return mv88e6xxx_phy_wait(ds);
} }
int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int reg; int reg;
reg = mv88e6xxx_phy_read_indirect(ds, port, 16); mutex_lock(&ps->phy_mutex);
reg = _mv88e6xxx_phy_read_indirect(ds, port, 16);
if (reg < 0) if (reg < 0)
return -EOPNOTSUPP; goto out;
e->eee_enabled = !!(reg & 0x0200); e->eee_enabled = !!(reg & 0x0200);
e->tx_lpi_enabled = !!(reg & 0x0100); e->tx_lpi_enabled = !!(reg & 0x0100);
reg = REG_READ(REG_PORT(port), 0); reg = mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_STATUS);
e->eee_active = !!(reg & 0x0040);
return 0;
}
static int mv88e6xxx_eee_enable_set(struct dsa_switch *ds, int port,
bool eee_enabled, bool tx_lpi_enabled)
{
int reg, nreg;
reg = mv88e6xxx_phy_read_indirect(ds, port, 16);
if (reg < 0) if (reg < 0)
return reg; goto out;
nreg = reg & ~0x0300; e->eee_active = !!(reg & PORT_STATUS_EEE);
if (eee_enabled) reg = 0;
nreg |= 0x0200;
if (tx_lpi_enabled)
nreg |= 0x0100;
if (nreg != reg) out:
return mv88e6xxx_phy_write_indirect(ds, port, 16, nreg); mutex_unlock(&ps->phy_mutex);
return reg;
return 0;
} }
int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
struct phy_device *phydev, struct ethtool_eee *e) struct phy_device *phydev, struct ethtool_eee *e)
{ {
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int reg;
int ret; int ret;
ret = mv88e6xxx_eee_enable_set(ds, port, e->eee_enabled, mutex_lock(&ps->phy_mutex);
e->tx_lpi_enabled);
if (ret)
return -EOPNOTSUPP;
return 0; ret = _mv88e6xxx_phy_read_indirect(ds, port, 16);
if (ret < 0)
goto out;
reg = ret & ~0x0300;
if (e->eee_enabled)
reg |= 0x0200;
if (e->tx_lpi_enabled)
reg |= 0x0100;
ret = _mv88e6xxx_phy_write_indirect(ds, port, 16, reg);
out:
mutex_unlock(&ps->phy_mutex);
return ret;
} }
static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd) static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd)
...@@ -752,7 +881,7 @@ static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd) ...@@ -752,7 +881,7 @@ static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x0b, cmd); ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -767,7 +896,7 @@ static int _mv88e6xxx_flush_fid(struct dsa_switch *ds, int fid) ...@@ -767,7 +896,7 @@ static int _mv88e6xxx_flush_fid(struct dsa_switch *ds, int fid)
if (ret < 0) if (ret < 0)
return ret; return ret;
return _mv88e6xxx_atu_cmd(ds, fid, ATU_CMD_FLUSH_NONSTATIC_FID); return _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_FLUSH_NON_STATIC_DB);
} }
static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state) static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state)
...@@ -778,23 +907,25 @@ static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state) ...@@ -778,23 +907,25 @@ static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state)
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), 0x04); reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL);
if (reg < 0) if (reg < 0)
goto abort; goto abort;
oldstate = reg & PSTATE_MASK; oldstate = reg & PORT_CONTROL_STATE_MASK;
if (oldstate != state) { if (oldstate != state) {
/* Flush forwarding database if we're moving a port /* Flush forwarding database if we're moving a port
* from Learning or Forwarding state to Disabled or * from Learning or Forwarding state to Disabled or
* Blocking or Listening state. * Blocking or Listening state.
*/ */
if (oldstate >= PSTATE_LEARNING && state <= PSTATE_BLOCKING) { if (oldstate >= PORT_CONTROL_STATE_LEARNING &&
state <= PORT_CONTROL_STATE_BLOCKING) {
ret = _mv88e6xxx_flush_fid(ds, ps->fid[port]); ret = _mv88e6xxx_flush_fid(ds, ps->fid[port]);
if (ret) if (ret)
goto abort; goto abort;
} }
reg = (reg & ~PSTATE_MASK) | state; reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x04, reg); ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL,
reg);
} }
abort: abort:
...@@ -815,7 +946,7 @@ static int _mv88e6xxx_update_port_config(struct dsa_switch *ds, int port) ...@@ -815,7 +946,7 @@ static int _mv88e6xxx_update_port_config(struct dsa_switch *ds, int port)
reg |= (ps->bridge_mask[fid] | reg |= (ps->bridge_mask[fid] |
(1 << dsa_upstream_port(ds))) & ~(1 << port); (1 << dsa_upstream_port(ds))) & ~(1 << port);
return _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x06, reg); return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, reg);
} }
/* Must be called with smi lock held */ /* Must be called with smi lock held */
...@@ -927,18 +1058,18 @@ int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state) ...@@ -927,18 +1058,18 @@ int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state)
switch (state) { switch (state) {
case BR_STATE_DISABLED: case BR_STATE_DISABLED:
stp_state = PSTATE_DISABLED; stp_state = PORT_CONTROL_STATE_DISABLED;
break; break;
case BR_STATE_BLOCKING: case BR_STATE_BLOCKING:
case BR_STATE_LISTENING: case BR_STATE_LISTENING:
stp_state = PSTATE_BLOCKING; stp_state = PORT_CONTROL_STATE_BLOCKING;
break; break;
case BR_STATE_LEARNING: case BR_STATE_LEARNING:
stp_state = PSTATE_LEARNING; stp_state = PORT_CONTROL_STATE_LEARNING;
break; break;
case BR_STATE_FORWARDING: case BR_STATE_FORWARDING:
default: default:
stp_state = PSTATE_FORWARDING; stp_state = PORT_CONTROL_STATE_FORWARDING;
break; break;
} }
...@@ -960,7 +1091,8 @@ static int __mv88e6xxx_write_addr(struct dsa_switch *ds, ...@@ -960,7 +1091,8 @@ static int __mv88e6xxx_write_addr(struct dsa_switch *ds,
int i, ret; int i, ret;
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x0d + i, ret = _mv88e6xxx_reg_write(
ds, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
(addr[i * 2] << 8) | addr[i * 2 + 1]); (addr[i * 2] << 8) | addr[i * 2 + 1]);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -974,7 +1106,8 @@ static int __mv88e6xxx_read_addr(struct dsa_switch *ds, unsigned char *addr) ...@@ -974,7 +1106,8 @@ static int __mv88e6xxx_read_addr(struct dsa_switch *ds, unsigned char *addr)
int i, ret; int i, ret;
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x0d + i); ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
GLOBAL_ATU_MAC_01 + i);
if (ret < 0) if (ret < 0)
return ret; return ret;
addr[i * 2] = ret >> 8; addr[i * 2] = ret >> 8;
...@@ -999,12 +1132,12 @@ static int __mv88e6xxx_port_fdb_cmd(struct dsa_switch *ds, int port, ...@@ -999,12 +1132,12 @@ static int __mv88e6xxx_port_fdb_cmd(struct dsa_switch *ds, int port,
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x0c, ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_DATA,
(0x10 << port) | state); (0x10 << port) | state);
if (ret) if (ret)
return ret; return ret;
ret = _mv88e6xxx_atu_cmd(ds, fid, ATU_CMD_LOAD_FID); ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_LOAD_DB);
return ret; return ret;
} }
...@@ -1013,7 +1146,8 @@ int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, ...@@ -1013,7 +1146,8 @@ int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid) const unsigned char *addr, u16 vid)
{ {
int state = is_multicast_ether_addr(addr) ? int state = is_multicast_ether_addr(addr) ?
FDB_STATE_MC_STATIC : FDB_STATE_STATIC; GLOBAL_ATU_DATA_STATE_MC_STATIC :
GLOBAL_ATU_DATA_STATE_UC_STATIC;
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret; int ret;
...@@ -1031,7 +1165,8 @@ int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, ...@@ -1031,7 +1165,8 @@ int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
int ret; int ret;
mutex_lock(&ps->smi_mutex); mutex_lock(&ps->smi_mutex);
ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr, FDB_STATE_UNUSED); ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr,
GLOBAL_ATU_DATA_STATE_UNUSED);
mutex_unlock(&ps->smi_mutex); mutex_unlock(&ps->smi_mutex);
return ret; return ret;
...@@ -1053,15 +1188,15 @@ static int __mv88e6xxx_port_getnext(struct dsa_switch *ds, int port, ...@@ -1053,15 +1188,15 @@ static int __mv88e6xxx_port_getnext(struct dsa_switch *ds, int port,
return ret; return ret;
do { do {
ret = _mv88e6xxx_atu_cmd(ds, fid, ATU_CMD_GETNEXT_FID); ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x0c); ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_DATA);
if (ret < 0) if (ret < 0)
return ret; return ret;
state = ret & FDB_STATE_MASK; state = ret & GLOBAL_ATU_DATA_STATE_MASK;
if (state == FDB_STATE_UNUSED) if (state == GLOBAL_ATU_DATA_STATE_UNUSED)
return -ENOENT; return -ENOENT;
} while (!(((ret >> 4) & 0xff) & (1 << port))); } while (!(((ret >> 4) & 0xff) & (1 << port)));
...@@ -1070,7 +1205,8 @@ static int __mv88e6xxx_port_getnext(struct dsa_switch *ds, int port, ...@@ -1070,7 +1205,8 @@ static int __mv88e6xxx_port_getnext(struct dsa_switch *ds, int port,
return ret; return ret;
*is_static = state == (is_multicast_ether_addr(addr) ? *is_static = state == (is_multicast_ether_addr(addr) ?
FDB_STATE_MC_STATIC : FDB_STATE_STATIC); GLOBAL_ATU_DATA_STATE_MC_STATIC :
GLOBAL_ATU_DATA_STATE_UC_STATIC);
return 0; return 0;
} }
...@@ -1115,7 +1251,8 @@ int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port) ...@@ -1115,7 +1251,8 @@ int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port)
/* Port Control 1: disable trunking, disable sending /* Port Control 1: disable trunking, disable sending
* learning messages to this port. * learning messages to this port.
*/ */
ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x05, 0x0000); ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
0x0000);
if (ret) if (ret)
goto abort; goto abort;
...@@ -1152,7 +1289,7 @@ int mv88e6xxx_setup_common(struct dsa_switch *ds) ...@@ -1152,7 +1289,7 @@ int mv88e6xxx_setup_common(struct dsa_switch *ds)
mutex_init(&ps->stats_mutex); mutex_init(&ps->stats_mutex);
mutex_init(&ps->phy_mutex); mutex_init(&ps->phy_mutex);
ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0; ps->id = REG_READ(REG_PORT(0), PORT_SWITCH_ID) & 0xfff0;
ps->fid_mask = (1 << DSA_MAX_PORTS) - 1; ps->fid_mask = (1 << DSA_MAX_PORTS) - 1;
...@@ -1161,6 +1298,154 @@ int mv88e6xxx_setup_common(struct dsa_switch *ds) ...@@ -1161,6 +1298,154 @@ int mv88e6xxx_setup_common(struct dsa_switch *ds)
return 0; return 0;
} }
int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
unsigned long timeout;
int ret;
int i;
/* Set all ports to the disabled state. */
for (i = 0; i < ps->num_ports; i++) {
ret = REG_READ(REG_PORT(i), PORT_CONTROL);
REG_WRITE(REG_PORT(i), PORT_CONTROL, ret & 0xfffc);
}
/* Wait for transmit queues to drain. */
usleep_range(2000, 4000);
/* Reset the switch. Keep the PPU active if requested. The PPU
* needs to be active to support indirect phy register access
* through global registers 0x18 and 0x19.
*/
if (ppu_active)
REG_WRITE(REG_GLOBAL, 0x04, 0xc000);
else
REG_WRITE(REG_GLOBAL, 0x04, 0xc400);
/* Wait up to one second for reset to complete. */
timeout = jiffies + 1 * HZ;
while (time_before(jiffies, timeout)) {
ret = REG_READ(REG_GLOBAL, 0x00);
if ((ret & is_reset) == is_reset)
break;
usleep_range(1000, 2000);
}
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
return 0;
}
int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
mutex_lock(&ps->phy_mutex);
ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
if (ret < 0)
goto error;
ret = _mv88e6xxx_phy_read_indirect(ds, port, reg);
error:
_mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
mutex_unlock(&ps->phy_mutex);
return ret;
}
int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
int reg, int val)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
mutex_lock(&ps->phy_mutex);
ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
if (ret < 0)
goto error;
ret = _mv88e6xxx_phy_write_indirect(ds, port, reg, val);
error:
_mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
mutex_unlock(&ps->phy_mutex);
return ret;
}
static int mv88e6xxx_port_to_phy_addr(struct dsa_switch *ds, int port)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
if (port >= 0 && port < ps->num_ports)
return port;
return -EINVAL;
}
int
mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int addr = mv88e6xxx_port_to_phy_addr(ds, port);
int ret;
if (addr < 0)
return addr;
mutex_lock(&ps->phy_mutex);
ret = _mv88e6xxx_phy_read(ds, addr, regnum);
mutex_unlock(&ps->phy_mutex);
return ret;
}
int
mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int addr = mv88e6xxx_port_to_phy_addr(ds, port);
int ret;
if (addr < 0)
return addr;
mutex_lock(&ps->phy_mutex);
ret = _mv88e6xxx_phy_write(ds, addr, regnum, val);
mutex_unlock(&ps->phy_mutex);
return ret;
}
int
mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int addr = mv88e6xxx_port_to_phy_addr(ds, port);
int ret;
if (addr < 0)
return addr;
mutex_lock(&ps->phy_mutex);
ret = _mv88e6xxx_phy_read_indirect(ds, addr, regnum);
mutex_unlock(&ps->phy_mutex);
return ret;
}
int
mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum,
u16 val)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int addr = mv88e6xxx_port_to_phy_addr(ds, port);
int ret;
if (addr < 0)
return addr;
mutex_lock(&ps->phy_mutex);
ret = _mv88e6xxx_phy_write_indirect(ds, addr, regnum, val);
mutex_unlock(&ps->phy_mutex);
return ret;
}
static int __init mv88e6xxx_init(void) static int __init mv88e6xxx_init(void)
{ {
#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131) #if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
......
...@@ -11,33 +11,199 @@ ...@@ -11,33 +11,199 @@
#ifndef __MV88E6XXX_H #ifndef __MV88E6XXX_H
#define __MV88E6XXX_H #define __MV88E6XXX_H
#define REG_PORT(p) (0x10 + (p)) #define SMI_CMD 0x00
#define REG_GLOBAL 0x1b #define SMI_CMD_BUSY BIT(15)
#define REG_GLOBAL2 0x1c #define SMI_CMD_CLAUSE_22 BIT(12)
#define SMI_CMD_OP_22_WRITE ((1 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22)
/* ATU commands */ #define SMI_CMD_OP_22_READ ((2 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22)
#define SMI_CMD_OP_45_WRITE_ADDR ((0 << 10) | SMI_CMD_BUSY)
#define ATU_BUSY 0x8000 #define SMI_CMD_OP_45_WRITE_DATA ((1 << 10) | SMI_CMD_BUSY)
#define SMI_CMD_OP_45_READ_DATA ((2 << 10) | SMI_CMD_BUSY)
#define ATU_CMD_LOAD_FID (ATU_BUSY | 0x3000) #define SMI_CMD_OP_45_READ_DATA_INC ((3 << 10) | SMI_CMD_BUSY)
#define ATU_CMD_GETNEXT_FID (ATU_BUSY | 0x4000) #define SMI_DATA 0x01
#define ATU_CMD_FLUSH_NONSTATIC_FID (ATU_BUSY | 0x6000)
/* port states */
#define PSTATE_MASK 0x03 #define REG_PORT(p) (0x10 + (p))
#define PSTATE_DISABLED 0x00 #define PORT_STATUS 0x00
#define PSTATE_BLOCKING 0x01 #define PORT_STATUS_PAUSE_EN BIT(15)
#define PSTATE_LEARNING 0x02 #define PORT_STATUS_MY_PAUSE BIT(14)
#define PSTATE_FORWARDING 0x03 #define PORT_STATUS_HD_FLOW BIT(13)
#define PORT_STATUS_PHY_DETECT BIT(12)
/* FDB states */ #define PORT_STATUS_LINK BIT(11)
#define PORT_STATUS_DUPLEX BIT(10)
#define PORT_STATUS_SPEED_MASK 0x0300
#define PORT_STATUS_SPEED_10 0x0000
#define PORT_STATUS_SPEED_100 0x0100
#define PORT_STATUS_SPEED_1000 0x0200
#define PORT_STATUS_EEE BIT(6) /* 6352 */
#define PORT_STATUS_AM_DIS BIT(6) /* 6165 */
#define PORT_STATUS_MGMII BIT(6) /* 6185 */
#define PORT_STATUS_TX_PAUSED BIT(5)
#define PORT_STATUS_FLOW_CTRL BIT(4)
#define PORT_PCS_CTRL 0x01
#define PORT_SWITCH_ID 0x03
#define PORT_SWITCH_ID_6085 0x04a0
#define PORT_SWITCH_ID_6095 0x0950
#define PORT_SWITCH_ID_6123 0x1210
#define PORT_SWITCH_ID_6123_A1 0x1212
#define PORT_SWITCH_ID_6123_A2 0x1213
#define PORT_SWITCH_ID_6131 0x1060
#define PORT_SWITCH_ID_6131_B2 0x1066
#define PORT_SWITCH_ID_6152 0x1a40
#define PORT_SWITCH_ID_6155 0x1a50
#define PORT_SWITCH_ID_6161 0x1610
#define PORT_SWITCH_ID_6161_A1 0x1612
#define PORT_SWITCH_ID_6161_A2 0x1613
#define PORT_SWITCH_ID_6165 0x1650
#define PORT_SWITCH_ID_6165_A1 0x1652
#define PORT_SWITCH_ID_6165_A2 0x1653
#define PORT_SWITCH_ID_6171 0x1710
#define PORT_SWITCH_ID_6172 0x1720
#define PORT_SWITCH_ID_6176 0x1760
#define PORT_SWITCH_ID_6182 0x1a60
#define PORT_SWITCH_ID_6185 0x1a70
#define PORT_SWITCH_ID_6352 0x3520
#define PORT_SWITCH_ID_6352_A0 0x3521
#define PORT_SWITCH_ID_6352_A1 0x3522
#define PORT_CONTROL 0x04
#define PORT_CONTROL_STATE_MASK 0x03
#define PORT_CONTROL_STATE_DISABLED 0x00
#define PORT_CONTROL_STATE_BLOCKING 0x01
#define PORT_CONTROL_STATE_LEARNING 0x02
#define PORT_CONTROL_STATE_FORWARDING 0x03
#define PORT_CONTROL_1 0x05
#define PORT_BASE_VLAN 0x06
#define PORT_DEFAULT_VLAN 0x07
#define PORT_CONTROL_2 0x08
#define PORT_RATE_CONTROL 0x09
#define PORT_RATE_CONTROL_2 0x0a
#define PORT_ASSOC_VECTOR 0x0b
#define PORT_IN_DISCARD_LO 0x10
#define PORT_IN_DISCARD_HI 0x11
#define PORT_IN_FILTERED 0x12
#define PORT_OUT_FILTERED 0x13
#define PORT_TAG_REGMAP_0123 0x19
#define PORT_TAG_REGMAP_4567 0x1a
#define FDB_STATE_MASK 0x0f #define REG_GLOBAL 0x1b
#define GLOBAL_STATUS 0x00
#define GLOBAL_STATUS_PPU_STATE BIT(15) /* 6351 and 6171 */
/* Two bits for 6165, 6185 etc */
#define GLOBAL_STATUS_PPU_MASK (0x3 << 14)
#define GLOBAL_STATUS_PPU_DISABLED_RST (0x0 << 14)
#define GLOBAL_STATUS_PPU_INITIALIZING (0x1 << 14)
#define GLOBAL_STATUS_PPU_DISABLED (0x2 << 14)
#define GLOBAL_STATUS_PPU_POLLING (0x3 << 14)
#define GLOBAL_MAC_01 0x01
#define GLOBAL_MAC_23 0x02
#define GLOBAL_MAC_45 0x03
#define GLOBAL_CONTROL 0x04
#define GLOBAL_CONTROL_SW_RESET BIT(15)
#define GLOBAL_CONTROL_PPU_ENABLE BIT(14)
#define GLOBAL_CONTROL_DISCARD_EXCESS BIT(13) /* 6352 */
#define GLOBAL_CONTROL_SCHED_PRIO BIT(11) /* 6152 */
#define GLOBAL_CONTROL_MAX_FRAME_1632 BIT(10) /* 6152 */
#define GLOBAL_CONTROL_RELOAD_EEPROM BIT(9) /* 6152 */
#define GLOBAL_CONTROL_DEVICE_EN BIT(7)
#define GLOBAL_CONTROL_STATS_DONE_EN BIT(6)
#define GLOBAL_CONTROL_VTU_PROBLEM_EN BIT(5)
#define GLOBAL_CONTROL_VTU_DONE_EN BIT(4)
#define GLOBAL_CONTROL_ATU_PROBLEM_EN BIT(3)
#define GLOBAL_CONTROL_ATU_DONE_EN BIT(2)
#define GLOBAL_CONTROL_TCAM_EN BIT(1)
#define GLOBAL_CONTROL_EEPROM_DONE_EN BIT(0)
#define GLOBAL_VTU_OP 0x05
#define GLOBAL_VTU_VID 0x06
#define GLOBAL_VTU_DATA_0_3 0x07
#define GLOBAL_VTU_DATA_4_7 0x08
#define GLOBAL_VTU_DATA_8_11 0x09
#define GLOBAL_ATU_CONTROL 0x0a
#define GLOBAL_ATU_OP 0x0b
#define GLOBAL_ATU_OP_BUSY BIT(15)
#define GLOBAL_ATU_OP_NOP (0 << 12)
#define GLOBAL_ATU_OP_FLUSH_ALL ((1 << 12) | GLOBAL_ATU_OP_BUSY)
#define GLOBAL_ATU_OP_FLUSH_NON_STATIC ((2 << 12) | GLOBAL_ATU_OP_BUSY)
#define GLOBAL_ATU_OP_LOAD_DB ((3 << 12) | GLOBAL_ATU_OP_BUSY)
#define GLOBAL_ATU_OP_GET_NEXT_DB ((4 << 12) | GLOBAL_ATU_OP_BUSY)
#define GLOBAL_ATU_OP_FLUSH_DB ((5 << 12) | GLOBAL_ATU_OP_BUSY)
#define GLOBAL_ATU_OP_FLUSH_NON_STATIC_DB ((6 << 12) | GLOBAL_ATU_OP_BUSY)
#define GLOBAL_ATU_OP_GET_CLR_VIOLATION ((7 << 12) | GLOBAL_ATU_OP_BUSY)
#define GLOBAL_ATU_DATA 0x0c
#define GLOBAL_ATU_DATA_STATE_MASK 0x0f
#define GLOBAL_ATU_DATA_STATE_UNUSED 0x00
#define GLOBAL_ATU_DATA_STATE_UC_MGMT 0x0d
#define GLOBAL_ATU_DATA_STATE_UC_STATIC 0x0e
#define GLOBAL_ATU_DATA_STATE_UC_PRIO_OVER 0x0f
#define GLOBAL_ATU_DATA_STATE_MC_NONE_RATE 0x05
#define GLOBAL_ATU_DATA_STATE_MC_STATIC 0x07
#define GLOBAL_ATU_DATA_STATE_MC_MGMT 0x0e
#define GLOBAL_ATU_DATA_STATE_MC_PRIO_OVER 0x0f
#define GLOBAL_ATU_MAC_01 0x0d
#define GLOBAL_ATU_MAC_23 0x0e
#define GLOBAL_ATU_MAC_45 0x0f
#define GLOBAL_IP_PRI_0 0x10
#define GLOBAL_IP_PRI_1 0x11
#define GLOBAL_IP_PRI_2 0x12
#define GLOBAL_IP_PRI_3 0x13
#define GLOBAL_IP_PRI_4 0x14
#define GLOBAL_IP_PRI_5 0x15
#define GLOBAL_IP_PRI_6 0x16
#define GLOBAL_IP_PRI_7 0x17
#define GLOBAL_IEEE_PRI 0x18
#define GLOBAL_CORE_TAG_TYPE 0x19
#define GLOBAL_MONITOR_CONTROL 0x1a
#define GLOBAL_CONTROL_2 0x1c
#define GLOBAL_STATS_OP 0x1d
#define GLOBAL_STATS_OP_BUSY BIT(15)
#define GLOBAL_STATS_OP_NOP (0 << 12)
#define GLOBAL_STATS_OP_FLUSH_ALL ((1 << 12) | GLOBAL_STATS_OP_BUSY)
#define GLOBAL_STATS_OP_FLUSH_PORT ((2 << 12) | GLOBAL_STATS_OP_BUSY)
#define GLOBAL_STATS_OP_READ_CAPTURED ((4 << 12) | GLOBAL_STATS_OP_BUSY)
#define GLOBAL_STATS_OP_CAPTURE_PORT ((5 << 12) | GLOBAL_STATS_OP_BUSY)
#define GLOBAL_STATS_OP_HIST_RX ((1 << 10) | GLOBAL_STATS_OP_BUSY)
#define GLOBAL_STATS_OP_HIST_TX ((2 << 10) | GLOBAL_STATS_OP_BUSY)
#define GLOBAL_STATS_OP_HIST_RX_TX ((3 << 10) | GLOBAL_STATS_OP_BUSY)
#define GLOBAL_STATS_COUNTER_32 0x1e
#define GLOBAL_STATS_COUNTER_01 0x1f
#define FDB_STATE_UNUSED 0x00 #define REG_GLOBAL2 0x1c
#define FDB_STATE_MC_STATIC 0x07 /* static multicast */ #define GLOBAL2_INT_SOURCE 0x00
#define FDB_STATE_STATIC 0x0e /* static unicast */ #define GLOBAL2_INT_MASK 0x01
#define GLOBAL2_MGMT_EN_2X 0x02
#define GLOBAL2_MGMT_EN_0X 0x03
#define GLOBAL2_FLOW_CONTROL 0x04
#define GLOBAL2_SWITCH_MGMT 0x05
#define GLOBAL2_DEVICE_MAPPING 0x06
#define GLOBAL2_TRUNK_MASK 0x07
#define GLOBAL2_TRUNK_MAPPING 0x08
#define GLOBAL2_INGRESS_OP 0x09
#define GLOBAL2_INGRESS_DATA 0x0a
#define GLOBAL2_PVT_ADDR 0x0b
#define GLOBAL2_PVT_DATA 0x0c
#define GLOBAL2_SWITCH_MAC 0x0d
#define GLOBAL2_SWITCH_MAC_BUSY BIT(15)
#define GLOBAL2_ATU_STATS 0x0e
#define GLOBAL2_PRIO_OVERRIDE 0x0f
#define GLOBAL2_EEPROM_OP 0x14
#define GLOBAL2_EEPROM_OP_BUSY BIT(15)
#define GLOBAL2_EEPROM_OP_LOAD BIT(11)
#define GLOBAL2_EEPROM_DATA 0x15
#define GLOBAL2_PTP_AVB_OP 0x16
#define GLOBAL2_PTP_AVB_DATA 0x17
#define GLOBAL2_SMI_OP 0x18
#define GLOBAL2_SMI_OP_BUSY BIT(15)
#define GLOBAL2_SMI_OP_CLAUSE_22 BIT(12)
#define GLOBAL2_SMI_OP_22_WRITE ((1 << 10) | GLOBAL2_SMI_OP_BUSY | \
GLOBAL2_SMI_OP_CLAUSE_22)
#define GLOBAL2_SMI_OP_22_READ ((2 << 10) | GLOBAL2_SMI_OP_BUSY | \
GLOBAL2_SMI_OP_CLAUSE_22)
#define GLOBAL2_SMI_OP_45_WRITE_ADDR ((0 << 10) | GLOBAL2_SMI_OP_BUSY)
#define GLOBAL2_SMI_OP_45_WRITE_DATA ((1 << 10) | GLOBAL2_SMI_OP_BUSY)
#define GLOBAL2_SMI_OP_45_READ_DATA ((2 << 10) | GLOBAL2_SMI_OP_BUSY)
#define GLOBAL2_SMI_DATA 0x19
#define GLOBAL2_SCRATCH_MISC 0x1a
#define GLOBAL2_WDOG_CONTROL 0x1b
#define GLOBAL2_QOS_WEIGHT 0x1c
#define GLOBAL2_MISC 0x1d
struct mv88e6xxx_priv_state { struct mv88e6xxx_priv_state {
/* When using multi-chip addressing, this mutex protects /* When using multi-chip addressing, this mutex protects
...@@ -73,6 +239,7 @@ struct mv88e6xxx_priv_state { ...@@ -73,6 +239,7 @@ struct mv88e6xxx_priv_state {
struct mutex eeprom_mutex; struct mutex eeprom_mutex;
int id; /* switch product id */ int id; /* switch product id */
int num_ports; /* number of switch ports */
/* hw bridging */ /* hw bridging */
...@@ -92,6 +259,7 @@ struct mv88e6xxx_hw_stat { ...@@ -92,6 +259,7 @@ struct mv88e6xxx_hw_stat {
int reg; int reg;
}; };
int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active);
int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port); int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port);
int mv88e6xxx_setup_common(struct dsa_switch *ds); int mv88e6xxx_setup_common(struct dsa_switch *ds);
int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg); int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg);
...@@ -102,19 +270,21 @@ int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val); ...@@ -102,19 +270,21 @@ int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val);
int mv88e6xxx_config_prio(struct dsa_switch *ds); int mv88e6xxx_config_prio(struct dsa_switch *ds);
int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr); int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr);
int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr); int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr);
int mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum); int mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum);
int mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val); int mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val);
int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum);
int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum,
u16 val);
void mv88e6xxx_ppu_state_init(struct dsa_switch *ds); void mv88e6xxx_ppu_state_init(struct dsa_switch *ds);
int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum); int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum);
int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr, int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr,
int regnum, u16 val); int regnum, u16 val);
void mv88e6xxx_poll_link(struct dsa_switch *ds); void mv88e6xxx_poll_link(struct dsa_switch *ds);
void mv88e6xxx_get_strings(struct dsa_switch *ds, void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data);
int nr_stats, struct mv88e6xxx_hw_stat *stats, void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
int port, uint8_t *data); uint64_t *data);
void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int mv88e6xxx_get_sset_count(struct dsa_switch *ds);
int nr_stats, struct mv88e6xxx_hw_stat *stats, int mv88e6xxx_get_sset_count_basic(struct dsa_switch *ds);
int port, uint64_t *data);
int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port); int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port);
void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
struct ethtool_regs *regs, void *_p); struct ethtool_regs *regs, void *_p);
...@@ -137,7 +307,9 @@ int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, ...@@ -137,7 +307,9 @@ int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid); const unsigned char *addr, u16 vid);
int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port, int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port,
unsigned char *addr, bool *is_static); unsigned char *addr, bool *is_static);
int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg);
int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
int reg, int val);
extern struct dsa_switch_driver mv88e6131_switch_driver; extern struct dsa_switch_driver mv88e6131_switch_driver;
extern struct dsa_switch_driver mv88e6123_61_65_switch_driver; extern struct dsa_switch_driver mv88e6123_61_65_switch_driver;
extern struct dsa_switch_driver mv88e6352_switch_driver; extern struct dsa_switch_driver mv88e6352_switch_driver;
......
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