Commit 7836b16c authored by David S. Miller's avatar David S. Miller

Merge branch 'dsa-hw-bridging'

Guenter Roeck says:

====================
net: dsa: HW bridging, EEE support

Patch 1 to 7 of this series prepare the drivers using the mv88e6xxx code
for HW bridging support, without adding the code itself. For the most part
this factors out common port initialization code. There is no functional
change except for patch 3, which disables the message port bit for the
CPU port to prevent packet duplication if HW bridging is configured.

Patch 8 adds the infrastructure for hardware bridging support to the
mv88e6xxx code.

Patch 9 wires the MV88E6352 driver to support hardware bridging.

Patches 10 to 12 add support for ndo_fdb functions to the dsa subsystem,
and wire up the MV88E6352 driver to support those functions.

Patches 13 to 16 add EEE support and HW bridging support to the mv88e6171
driver. This set of patches is from Andrew, applied on top of the first
set of patches.

The series applies to net-next as of 3/24/2015.

Thanks a lot to Andrew Lunn for testing and valuable feedback.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5ce58c2f b2a6b93a
...@@ -222,28 +222,6 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p) ...@@ -222,28 +222,6 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p)
val |= 0x000c; val |= 0x000c;
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, set the maximum /* Port Control 2: don't force a good FCS, set the maximum
* frame size to 10240 bytes, don't let the switch add or * frame size to 10240 bytes, don't let the switch add or
* strip 802.1q tags, don't discard tagged or untagged frames * strip 802.1q tags, don't discard tagged or untagged frames
...@@ -288,18 +266,17 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p) ...@@ -288,18 +266,17 @@ static int mv88e6123_61_65_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 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;
mutex_init(&ps->smi_mutex); ret = mv88e6xxx_setup_common(ds);
mutex_init(&ps->stats_mutex); if (ret < 0)
mutex_init(&ps->phy_mutex); return ret;
ret = mv88e6123_61_65_switch_reset(ds); ret = mv88e6123_61_65_switch_reset(ds);
if (ret < 0) if (ret < 0)
......
...@@ -17,6 +17,10 @@ ...@@ -17,6 +17,10 @@
#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);
...@@ -27,9 +31,9 @@ static char *mv88e6171_probe(struct device *host_dev, int sw_addr) ...@@ -27,9 +31,9 @@ static char *mv88e6171_probe(struct device *host_dev, int sw_addr)
ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03);
if (ret >= 0) { if (ret >= 0) {
if ((ret & 0xfff0) == 0x1710) if ((ret & 0xfff0) == ID_6171)
return "Marvell 88E6171"; return "Marvell 88E6171";
if ((ret & 0xfff0) == 0x1720) if ((ret & 0xfff0) == ID_6172)
return "Marvell 88E6172"; return "Marvell 88E6172";
} }
...@@ -221,28 +225,6 @@ static int mv88e6171_setup_port(struct dsa_switch *ds, int p) ...@@ -221,28 +225,6 @@ static int mv88e6171_setup_port(struct dsa_switch *ds, int p)
val |= 0x000c; val |= 0x000c;
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, set the maximum /* Port Control 2: don't force a good FCS, set the maximum
* frame size to 10240 bytes, don't let the switch add or * frame size to 10240 bytes, don't let the switch add or
* strip 802.1q tags, don't discard tagged or untagged frames * strip 802.1q tags, don't discard tagged or untagged frames
...@@ -287,17 +269,17 @@ static int mv88e6171_setup_port(struct dsa_switch *ds, int p) ...@@ -287,17 +269,17 @@ static int mv88e6171_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 mv88e6171_setup(struct dsa_switch *ds) static int mv88e6171_setup(struct dsa_switch *ds)
{ {
struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
int i; int i;
int ret; int ret;
mutex_init(&ps->smi_mutex); ret = mv88e6xxx_setup_common(ds);
mutex_init(&ps->stats_mutex); if (ret < 0)
return ret;
ret = mv88e6171_switch_reset(ds); ret = mv88e6171_switch_reset(ds);
if (ret < 0) if (ret < 0)
...@@ -318,8 +300,6 @@ static int mv88e6171_setup(struct dsa_switch *ds) ...@@ -318,8 +300,6 @@ static int mv88e6171_setup(struct dsa_switch *ds)
return ret; return ret;
} }
mutex_init(&ps->phy_mutex);
return 0; return 0;
} }
...@@ -410,6 +390,28 @@ static int mv88e6171_get_sset_count(struct dsa_switch *ds) ...@@ -410,6 +390,28 @@ static int mv88e6171_get_sset_count(struct dsa_switch *ds)
return ARRAY_SIZE(mv88e6171_hw_stats); return ARRAY_SIZE(mv88e6171_hw_stats);
} }
static int mv88e6171_get_eee(struct dsa_switch *ds, int port,
struct ethtool_eee *e)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
if (ps->id == ID_6172)
return mv88e6xxx_get_eee(ds, port, e);
return -EOPNOTSUPP;
}
static int mv88e6171_set_eee(struct dsa_switch *ds, int port,
struct phy_device *phydev, struct ethtool_eee *e)
{
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
if (ps->id == ID_6172)
return mv88e6xxx_set_eee(ds, port, phydev, e);
return -EOPNOTSUPP;
}
struct dsa_switch_driver mv88e6171_switch_driver = { struct dsa_switch_driver mv88e6171_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),
...@@ -422,11 +424,19 @@ struct dsa_switch_driver mv88e6171_switch_driver = { ...@@ -422,11 +424,19 @@ struct dsa_switch_driver mv88e6171_switch_driver = {
.get_strings = mv88e6171_get_strings, .get_strings = mv88e6171_get_strings,
.get_ethtool_stats = mv88e6171_get_ethtool_stats, .get_ethtool_stats = mv88e6171_get_ethtool_stats,
.get_sset_count = mv88e6171_get_sset_count, .get_sset_count = mv88e6171_get_sset_count,
.set_eee = mv88e6171_set_eee,
.get_eee = mv88e6171_get_eee,
#ifdef CONFIG_NET_DSA_HWMON #ifdef CONFIG_NET_DSA_HWMON
.get_temp = mv88e6xxx_get_temp, .get_temp = mv88e6xxx_get_temp,
#endif #endif
.get_regs_len = mv88e6xxx_get_regs_len, .get_regs_len = mv88e6xxx_get_regs_len,
.get_regs = mv88e6xxx_get_regs, .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:mv88e6171"); MODULE_ALIAS("platform:mv88e6171");
......
...@@ -215,28 +215,6 @@ static int mv88e6352_setup_port(struct dsa_switch *ds, int p) ...@@ -215,28 +215,6 @@ static int mv88e6352_setup_port(struct dsa_switch *ds, int p)
val |= 0x000c; val |= 0x000c;
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, set the maximum /* Port Control 2: don't force a good FCS, set the maximum
* frame size to 10240 bytes, don't let the switch add or * frame size to 10240 bytes, don't let the switch add or
* strip 802.1q tags, don't discard tagged or untagged frames * strip 802.1q tags, don't discard tagged or untagged frames
...@@ -281,7 +259,7 @@ static int mv88e6352_setup_port(struct dsa_switch *ds, int p) ...@@ -281,7 +259,7 @@ static int mv88e6352_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);
} }
#ifdef CONFIG_NET_DSA_HWMON #ifdef CONFIG_NET_DSA_HWMON
...@@ -385,12 +363,11 @@ static int mv88e6352_setup(struct dsa_switch *ds) ...@@ -385,12 +363,11 @@ static int mv88e6352_setup(struct dsa_switch *ds)
int ret; int ret;
int i; int i;
mutex_init(&ps->smi_mutex); ret = mv88e6xxx_setup_common(ds);
mutex_init(&ps->stats_mutex); if (ret < 0)
mutex_init(&ps->phy_mutex); return ret;
mutex_init(&ps->eeprom_mutex);
ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0; mutex_init(&ps->eeprom_mutex);
ret = mv88e6352_switch_reset(ds); ret = mv88e6352_switch_reset(ds);
if (ret < 0) if (ret < 0)
...@@ -729,6 +706,12 @@ struct dsa_switch_driver mv88e6352_switch_driver = { ...@@ -729,6 +706,12 @@ struct dsa_switch_driver mv88e6352_switch_driver = {
.set_eeprom = mv88e6352_set_eeprom, .set_eeprom = mv88e6352_set_eeprom,
.get_regs_len = mv88e6xxx_get_regs_len, .get_regs_len = mv88e6xxx_get_regs_len,
.get_regs = mv88e6xxx_get_regs, .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"); MODULE_ALIAS("platform:mv88e6352");
This diff is collapsed.
...@@ -15,6 +15,30 @@ ...@@ -15,6 +15,30 @@
#define REG_GLOBAL 0x1b #define REG_GLOBAL 0x1b
#define REG_GLOBAL2 0x1c #define REG_GLOBAL2 0x1c
/* ATU commands */
#define ATU_BUSY 0x8000
#define ATU_CMD_LOAD_FID (ATU_BUSY | 0x3000)
#define ATU_CMD_GETNEXT_FID (ATU_BUSY | 0x4000)
#define ATU_CMD_FLUSH_NONSTATIC_FID (ATU_BUSY | 0x6000)
/* port states */
#define PSTATE_MASK 0x03
#define PSTATE_DISABLED 0x00
#define PSTATE_BLOCKING 0x01
#define PSTATE_LEARNING 0x02
#define PSTATE_FORWARDING 0x03
/* FDB states */
#define FDB_STATE_MASK 0x0f
#define FDB_STATE_UNUSED 0x00
#define FDB_STATE_MC_STATIC 0x07 /* static multicast */
#define FDB_STATE_STATIC 0x0e /* static unicast */
struct mv88e6xxx_priv_state { struct mv88e6xxx_priv_state {
/* When using multi-chip addressing, this mutex protects /* When using multi-chip addressing, this mutex protects
* access to the indirect access registers. (In single-chip * access to the indirect access registers. (In single-chip
...@@ -49,6 +73,17 @@ struct mv88e6xxx_priv_state { ...@@ -49,6 +73,17 @@ struct mv88e6xxx_priv_state {
struct mutex eeprom_mutex; struct mutex eeprom_mutex;
int id; /* switch product id */ int id; /* switch product id */
/* hw bridging */
u32 fid_mask;
u8 fid[DSA_MAX_PORTS];
u16 bridge_mask[DSA_MAX_PORTS];
unsigned long port_state_update_mask;
u8 port_state[DSA_MAX_PORTS];
struct work_struct bridge_work;
}; };
struct mv88e6xxx_hw_stat { struct mv88e6xxx_hw_stat {
...@@ -57,6 +92,8 @@ struct mv88e6xxx_hw_stat { ...@@ -57,6 +92,8 @@ struct mv88e6xxx_hw_stat {
int reg; int reg;
}; };
int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port);
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);
int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg); int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg);
int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr, int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
...@@ -91,6 +128,15 @@ int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, int regnum, ...@@ -91,6 +128,15 @@ int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, int regnum,
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);
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);
int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask);
int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask);
int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state);
int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid);
int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid);
int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port,
unsigned char *addr, bool *is_static);
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;
......
...@@ -296,6 +296,12 @@ struct dsa_switch_driver { ...@@ -296,6 +296,12 @@ struct dsa_switch_driver {
u32 br_port_mask); u32 br_port_mask);
int (*port_stp_update)(struct dsa_switch *ds, int port, int (*port_stp_update)(struct dsa_switch *ds, int port,
u8 state); u8 state);
int (*fdb_add)(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid);
int (*fdb_del)(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid);
int (*fdb_getnext)(struct dsa_switch *ds, int port,
unsigned char *addr, bool *is_static);
}; };
void register_switch_driver(struct dsa_switch_driver *type); void register_switch_driver(struct dsa_switch_driver *type);
......
...@@ -201,6 +201,105 @@ static int dsa_slave_set_mac_address(struct net_device *dev, void *a) ...@@ -201,6 +201,105 @@ static int dsa_slave_set_mac_address(struct net_device *dev, void *a)
return 0; return 0;
} }
static int dsa_slave_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid, u16 nlm_flags)
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_switch *ds = p->parent;
int ret = -EOPNOTSUPP;
if (ds->drv->fdb_add)
ret = ds->drv->fdb_add(ds, p->port, addr, vid);
return ret;
}
static int dsa_slave_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
const unsigned char *addr, u16 vid)
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_switch *ds = p->parent;
int ret = -EOPNOTSUPP;
if (ds->drv->fdb_del)
ret = ds->drv->fdb_del(ds, p->port, addr, vid);
return ret;
}
static int dsa_slave_fill_info(struct net_device *dev, struct sk_buff *skb,
const unsigned char *addr, u16 vid,
bool is_static,
u32 portid, u32 seq, int type,
unsigned int flags)
{
struct nlmsghdr *nlh;
struct ndmsg *ndm;
nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags);
if (!nlh)
return -EMSGSIZE;
ndm = nlmsg_data(nlh);
ndm->ndm_family = AF_BRIDGE;
ndm->ndm_pad1 = 0;
ndm->ndm_pad2 = 0;
ndm->ndm_flags = NTF_EXT_LEARNED;
ndm->ndm_type = 0;
ndm->ndm_ifindex = dev->ifindex;
ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
if (nla_put(skb, NDA_LLADDR, ETH_ALEN, addr))
goto nla_put_failure;
if (vid && nla_put_u16(skb, NDA_VLAN, vid))
goto nla_put_failure;
nlmsg_end(skb, nlh);
return 0;
nla_put_failure:
nlmsg_cancel(skb, nlh);
return -EMSGSIZE;
}
/* Dump information about entries, in response to GETNEIGH */
static int dsa_slave_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
struct net_device *dev,
struct net_device *filter_dev, int idx)
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_switch *ds = p->parent;
unsigned char addr[ETH_ALEN] = { 0 };
int ret;
if (!ds->drv->fdb_getnext)
return -EOPNOTSUPP;
for (; ; idx++) {
bool is_static;
ret = ds->drv->fdb_getnext(ds, p->port, addr, &is_static);
if (ret < 0)
break;
if (idx < cb->args[0])
continue;
ret = dsa_slave_fill_info(dev, skb, addr, 0,
is_static,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
RTM_NEWNEIGH, NLM_F_MULTI);
if (ret < 0)
break;
}
return idx;
}
static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{ {
struct dsa_slave_priv *p = netdev_priv(dev); struct dsa_slave_priv *p = netdev_priv(dev);
...@@ -572,6 +671,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = { ...@@ -572,6 +671,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
.ndo_change_rx_flags = dsa_slave_change_rx_flags, .ndo_change_rx_flags = dsa_slave_change_rx_flags,
.ndo_set_rx_mode = dsa_slave_set_rx_mode, .ndo_set_rx_mode = dsa_slave_set_rx_mode,
.ndo_set_mac_address = dsa_slave_set_mac_address, .ndo_set_mac_address = dsa_slave_set_mac_address,
.ndo_fdb_add = dsa_slave_fdb_add,
.ndo_fdb_del = dsa_slave_fdb_del,
.ndo_fdb_dump = dsa_slave_fdb_dump,
.ndo_do_ioctl = dsa_slave_ioctl, .ndo_do_ioctl = dsa_slave_ioctl,
}; };
......
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