summaryrefslogtreecommitdiff
path: root/drivers/net/dsa/mv88e6352.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-04-15 19:00:47 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2015-04-15 19:00:47 +0300
commit6c373ca89399c5a3f7ef210ad8f63dc3437da345 (patch)
tree74d1ec65087df1da1021b43ac51acc1ee8601809 /drivers/net/dsa/mv88e6352.c
parentbb0fd7ab0986105765d11baa82e619c618a235aa (diff)
parent9f9151412dd7aae0e3f51a89ae4a1f8755fdb4d0 (diff)
downloadlinux-6c373ca89399c5a3f7ef210ad8f63dc3437da345.tar.xz
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: 1) Add BQL support to via-rhine, from Tino Reichardt. 2) Integrate SWITCHDEV layer support into the DSA layer, so DSA drivers can support hw switch offloading. From Floria Fainelli. 3) Allow 'ip address' commands to initiate multicast group join/leave, from Madhu Challa. 4) Many ipv4 FIB lookup optimizations from Alexander Duyck. 5) Support EBPF in cls_bpf classifier and act_bpf action, from Daniel Borkmann. 6) Remove the ugly compat support in ARP for ugly layers like ax25, rose, etc. And use this to clean up the neigh layer, then use it to implement MPLS support. All from Eric Biederman. 7) Support L3 forwarding offloading in switches, from Scott Feldman. 8) Collapse the LOCAL and MAIN ipv4 FIB tables when possible, to speed up route lookups even further. From Alexander Duyck. 9) Many improvements and bug fixes to the rhashtable implementation, from Herbert Xu and Thomas Graf. In particular, in the case where an rhashtable user bulk adds a large number of items into an empty table, we expand the table much more sanely. 10) Don't make the tcp_metrics hash table per-namespace, from Eric Biederman. 11) Extend EBPF to access SKB fields, from Alexei Starovoitov. 12) Split out new connection request sockets so that they can be established in the main hash table. Much less false sharing since hash lookups go direct to the request sockets instead of having to go first to the listener then to the request socks hashed underneath. From Eric Dumazet. 13) Add async I/O support for crytpo AF_ALG sockets, from Tadeusz Struk. 14) Support stable privacy address generation for RFC7217 in IPV6. From Hannes Frederic Sowa. 15) Hash network namespace into IP frag IDs, also from Hannes Frederic Sowa. 16) Convert PTP get/set methods to use 64-bit time, from Richard Cochran. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1816 commits) fm10k: Bump driver version to 0.15.2 fm10k: corrected VF multicast update fm10k: mbx_update_max_size does not drop all oversized messages fm10k: reset head instead of calling update_max_size fm10k: renamed mbx_tx_dropped to mbx_tx_oversized fm10k: update xcast mode before synchronizing multicast addresses fm10k: start service timer on probe fm10k: fix function header comment fm10k: comment next_vf_mbx flow fm10k: don't handle mailbox events in iov_event path and always process mailbox fm10k: use separate workqueue for fm10k driver fm10k: Set PF queues to unlimited bandwidth during virtualization fm10k: expose tx_timeout_count as an ethtool stat fm10k: only increment tx_timeout_count in Tx hang path fm10k: remove extraneous "Reset interface" message fm10k: separate PF only stats so that VF does not display them fm10k: use hw->mac.max_queues for stats fm10k: only show actual queues, not the maximum in hardware fm10k: allow creation of VLAN on default vid fm10k: fix unused warnings ...
Diffstat (limited to 'drivers/net/dsa/mv88e6352.c')
-rw-r--r--drivers/net/dsa/mv88e6352.c307
1 files changed, 38 insertions, 269 deletions
diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c
index e13adc7b3dda..126c11b81e75 100644
--- a/drivers/net/dsa/mv88e6352.c
+++ b/drivers/net/dsa/mv88e6352.c
@@ -22,59 +22,6 @@
#include <net/dsa.h>
#include "mv88e6xxx.h"
-static int mv88e6352_wait(struct dsa_switch *ds, int reg, int offset, u16 mask)
-{
- unsigned long timeout = jiffies + HZ / 10;
-
- while (time_before(jiffies, timeout)) {
- int ret;
-
- ret = REG_READ(reg, offset);
- if (!(ret & mask))
- return 0;
-
- usleep_range(1000, 2000);
- }
- return -ETIMEDOUT;
-}
-
-static inline int mv88e6352_phy_wait(struct dsa_switch *ds)
-{
- return mv88e6352_wait(ds, REG_GLOBAL2, 0x18, 0x8000);
-}
-
-static inline int mv88e6352_eeprom_load_wait(struct dsa_switch *ds)
-{
- return mv88e6352_wait(ds, REG_GLOBAL2, 0x14, 0x0800);
-}
-
-static inline int mv88e6352_eeprom_busy_wait(struct dsa_switch *ds)
-{
- return mv88e6352_wait(ds, REG_GLOBAL2, 0x14, 0x8000);
-}
-
-static int __mv88e6352_phy_read(struct dsa_switch *ds, int addr, int regnum)
-{
- int ret;
-
- REG_WRITE(REG_GLOBAL2, 0x18, 0x9800 | (addr << 5) | regnum);
-
- ret = mv88e6352_phy_wait(ds);
- if (ret < 0)
- return ret;
-
- return REG_READ(REG_GLOBAL2, 0x19);
-}
-
-static int __mv88e6352_phy_write(struct dsa_switch *ds, int addr, int regnum,
- u16 val)
-{
- REG_WRITE(REG_GLOBAL2, 0x19, val);
- REG_WRITE(REG_GLOBAL2, 0x18, 0x9400 | (addr << 5) | regnum);
-
- return mv88e6352_phy_wait(ds);
-}
-
static char *mv88e6352_probe(struct device *host_dev, int sw_addr)
{
struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev);
@@ -83,58 +30,24 @@ static char *mv88e6352_probe(struct device *host_dev, int sw_addr)
if (bus == 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 & 0xfff0) == 0x1760)
+ if ((ret & 0xfff0) == PORT_SWITCH_ID_6176)
return "Marvell 88E6176";
- if (ret == 0x3521)
+ if (ret == PORT_SWITCH_ID_6352_A0)
return "Marvell 88E6352 (A0)";
- if (ret == 0x3522)
+ if (ret == PORT_SWITCH_ID_6352_A1)
return "Marvell 88E6352 (A1)";
- if ((ret & 0xfff0) == 0x3520)
+ if ((ret & 0xfff0) == PORT_SWITCH_ID_6352)
return "Marvell 88E6352";
}
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)
{
+ struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
int ret;
int i;
@@ -205,7 +118,7 @@ static int mv88e6352_setup_global(struct dsa_switch *ds)
/* Disable ingress rate limiting by resetting all ingress
* 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));
/* Initialise cross-chip port VLAN table to reset defaults. */
@@ -268,28 +181,6 @@ static int mv88e6352_setup_port(struct dsa_switch *ds, int p)
val |= 0x000c;
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, set the maximum
* frame size to 10240 bytes, don't let the switch add or
* strip 802.1q tags, don't discard tagged or untagged frames
@@ -334,53 +225,18 @@ static int mv88e6352_setup_port(struct dsa_switch *ds, int p)
*/
REG_WRITE(addr, 0x19, 0x7654);
- return 0;
+ return mv88e6xxx_setup_port_common(ds, p);
}
#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 = __mv88e6352_phy_write(ds, port, 0x16, page);
- if (ret < 0)
- goto error;
- ret = __mv88e6352_phy_read(ds, port, reg);
-error:
- __mv88e6352_phy_write(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 = __mv88e6352_phy_write(ds, port, 0x16, page);
- if (ret < 0)
- goto error;
-
- ret = __mv88e6352_phy_write(ds, port, reg, val);
-error:
- __mv88e6352_phy_write(ds, port, 0x16, 0x0);
- mutex_unlock(&ps->phy_mutex);
- return ret;
-}
-
static int mv88e6352_get_temp(struct dsa_switch *ds, int *temp)
{
int ret;
*temp = 0;
- ret = mv88e6352_phy_page_read(ds, 0, 6, 27);
+ ret = mv88e6xxx_phy_page_read(ds, 0, 6, 27);
if (ret < 0)
return ret;
@@ -395,7 +251,7 @@ static int mv88e6352_get_temp_limit(struct dsa_switch *ds, int *temp)
*temp = 0;
- ret = mv88e6352_phy_page_read(ds, 0, 6, 26);
+ ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26);
if (ret < 0)
return ret;
@@ -408,11 +264,11 @@ static int mv88e6352_set_temp_limit(struct dsa_switch *ds, int temp)
{
int ret;
- ret = mv88e6352_phy_page_read(ds, 0, 6, 26);
+ ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26);
if (ret < 0)
return ret;
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));
}
@@ -422,7 +278,7 @@ static int mv88e6352_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
*alarm = false;
- ret = mv88e6352_phy_page_read(ds, 0, 6, 26);
+ ret = mv88e6xxx_phy_page_read(ds, 0, 6, 26);
if (ret < 0)
return ret;
@@ -438,14 +294,15 @@ static int mv88e6352_setup(struct dsa_switch *ds)
int ret;
int i;
- mutex_init(&ps->smi_mutex);
- mutex_init(&ps->stats_mutex);
- mutex_init(&ps->phy_mutex);
- mutex_init(&ps->eeprom_mutex);
+ ret = mv88e6xxx_setup_common(ds);
+ if (ret < 0)
+ return ret;
- ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0;
+ ps->num_ports = 7;
- ret = mv88e6352_switch_reset(ds);
+ mutex_init(&ps->eeprom_mutex);
+
+ ret = mv88e6xxx_switch_reset(ds, true);
if (ret < 0)
return ret;
@@ -455,7 +312,7 @@ static int mv88e6352_setup(struct dsa_switch *ds)
if (ret < 0)
return ret;
- for (i = 0; i < 7; i++) {
+ for (i = 0; i < ps->num_ports; i++) {
ret = mv88e6352_setup_port(ds, i);
if (ret < 0)
return ret;
@@ -464,83 +321,6 @@ static int mv88e6352_setup(struct dsa_switch *ds)
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 = __mv88e6352_phy_read(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 = __mv88e6352_phy_write(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)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
@@ -553,7 +333,7 @@ static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr)
if (ret < 0)
goto error;
- ret = mv88e6352_eeprom_busy_wait(ds);
+ ret = mv88e6xxx_eeprom_busy_wait(ds);
if (ret < 0)
goto error;
@@ -576,7 +356,7 @@ static int mv88e6352_get_eeprom(struct dsa_switch *ds,
eeprom->magic = 0xc3ec4951;
- ret = mv88e6352_eeprom_load_wait(ds);
+ ret = mv88e6xxx_eeprom_load_wait(ds);
if (ret < 0)
return ret;
@@ -657,7 +437,7 @@ static int mv88e6352_write_eeprom_word(struct dsa_switch *ds, int addr,
if (ret < 0)
goto error;
- ret = mv88e6352_eeprom_busy_wait(ds);
+ ret = mv88e6xxx_eeprom_busy_wait(ds);
error:
mutex_unlock(&ps->eeprom_mutex);
return ret;
@@ -681,7 +461,7 @@ static int mv88e6352_set_eeprom(struct dsa_switch *ds,
len = eeprom->len;
eeprom->len = 0;
- ret = mv88e6352_eeprom_load_wait(ds);
+ ret = mv88e6xxx_eeprom_load_wait(ds);
if (ret < 0)
return ret;
@@ -739,37 +519,20 @@ static int mv88e6352_set_eeprom(struct dsa_switch *ds,
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 = {
.tag_protocol = DSA_TAG_PROTO_EDSA,
.priv_size = sizeof(struct mv88e6xxx_priv_state),
.probe = mv88e6352_probe,
.setup = mv88e6352_setup,
.set_addr = mv88e6xxx_set_addr_indirect,
- .phy_read = mv88e6352_phy_read,
- .phy_write = mv88e6352_phy_write,
+ .phy_read = mv88e6xxx_phy_read_indirect,
+ .phy_write = mv88e6xxx_phy_write_indirect,
.poll_link = mv88e6xxx_poll_link,
- .get_strings = mv88e6352_get_strings,
- .get_ethtool_stats = mv88e6352_get_ethtool_stats,
- .get_sset_count = mv88e6352_get_sset_count,
+ .get_strings = mv88e6xxx_get_strings,
+ .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
+ .get_sset_count = mv88e6xxx_get_sset_count,
+ .set_eee = mv88e6xxx_set_eee,
+ .get_eee = mv88e6xxx_get_eee,
#ifdef CONFIG_NET_DSA_HWMON
.get_temp = mv88e6352_get_temp,
.get_temp_limit = mv88e6352_get_temp_limit,
@@ -780,6 +543,12 @@ struct dsa_switch_driver mv88e6352_switch_driver = {
.set_eeprom = mv88e6352_set_eeprom,
.get_regs_len = mv88e6xxx_get_regs_len,
.get_regs = mv88e6xxx_get_regs,
+ .port_join_bridge = mv88e6xxx_join_bridge,
+ .port_leave_bridge = mv88e6xxx_leave_bridge,
+ .port_stp_update = mv88e6xxx_port_stp_update,
+ .fdb_add = mv88e6xxx_port_fdb_add,
+ .fdb_del = mv88e6xxx_port_fdb_del,
+ .fdb_getnext = mv88e6xxx_port_fdb_getnext,
};
MODULE_ALIAS("platform:mv88e6352");