Skip to content

Commit

Permalink
net: dsa: b53: Implement ARL add/del/dump operations
Browse files Browse the repository at this point in the history
Adds support for FDB add/delete/dump using the ARL read/write logic and
the ARL search logic for faster dumps. The code is made flexible enough
it could support devices with a different register layout like BCM5325
and BCM5365 which have fewer number of entries or pack values into a
single 64 bits register.

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
ffainelli authored and davem330 committed Jun 10, 2016
1 parent 0830c98 commit 1da6df8
Show file tree
Hide file tree
Showing 3 changed files with 382 additions and 0 deletions.
261 changes: 261 additions & 0 deletions drivers/net/dsa/b53/b53_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
#include <linux/module.h>
#include <linux/platform_data/b53.h>
#include <linux/phy.h>
#include <linux/etherdevice.h>
#include <net/dsa.h>
#include <net/switchdev.h>

#include "b53_regs.h"
#include "b53_priv.h"
Expand Down Expand Up @@ -777,6 +779,246 @@ static void b53_adjust_link(struct dsa_switch *ds, int port,
}
}

/* Address Resolution Logic routines */
static int b53_arl_op_wait(struct b53_device *dev)
{
unsigned int timeout = 10;
u8 reg;

do {
b53_read8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, &reg);
if (!(reg & ARLTBL_START_DONE))
return 0;

usleep_range(1000, 2000);
} while (timeout--);

dev_warn(dev->dev, "timeout waiting for ARL to finish: 0x%02x\n", reg);

return -ETIMEDOUT;
}

static int b53_arl_rw_op(struct b53_device *dev, unsigned int op)
{
u8 reg;

if (op > ARLTBL_RW)
return -EINVAL;

b53_read8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, &reg);
reg |= ARLTBL_START_DONE;
if (op)
reg |= ARLTBL_RW;
else
reg &= ~ARLTBL_RW;
b53_write8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, reg);

return b53_arl_op_wait(dev);
}

static int b53_arl_read(struct b53_device *dev, u64 mac,
u16 vid, struct b53_arl_entry *ent, u8 *idx,
bool is_valid)
{
unsigned int i;
int ret;

ret = b53_arl_op_wait(dev);
if (ret)
return ret;

/* Read the bins */
for (i = 0; i < dev->num_arl_entries; i++) {
u64 mac_vid;
u32 fwd_entry;

b53_read64(dev, B53_ARLIO_PAGE,
B53_ARLTBL_MAC_VID_ENTRY(i), &mac_vid);
b53_read32(dev, B53_ARLIO_PAGE,
B53_ARLTBL_DATA_ENTRY(i), &fwd_entry);
b53_arl_to_entry(ent, mac_vid, fwd_entry);

if (!(fwd_entry & ARLTBL_VALID))
continue;
if ((mac_vid & ARLTBL_MAC_MASK) != mac)
continue;
*idx = i;
}

return -ENOENT;
}

static int b53_arl_op(struct b53_device *dev, int op, int port,
const unsigned char *addr, u16 vid, bool is_valid)
{
struct b53_arl_entry ent;
u32 fwd_entry;
u64 mac, mac_vid = 0;
u8 idx = 0;
int ret;

/* Convert the array into a 64-bit MAC */
mac = b53_mac_to_u64(addr);

/* Perform a read for the given MAC and VID */
b53_write48(dev, B53_ARLIO_PAGE, B53_MAC_ADDR_IDX, mac);
b53_write16(dev, B53_ARLIO_PAGE, B53_VLAN_ID_IDX, vid);

/* Issue a read operation for this MAC */
ret = b53_arl_rw_op(dev, 1);
if (ret)
return ret;

ret = b53_arl_read(dev, mac, vid, &ent, &idx, is_valid);
/* If this is a read, just finish now */
if (op)
return ret;

/* We could not find a matching MAC, so reset to a new entry */
if (ret) {
fwd_entry = 0;
idx = 1;
}

memset(&ent, 0, sizeof(ent));
ent.port = port;
ent.is_valid = is_valid;
ent.vid = vid;
ent.is_static = true;
memcpy(ent.mac, addr, ETH_ALEN);
b53_arl_from_entry(&mac_vid, &fwd_entry, &ent);

b53_write64(dev, B53_ARLIO_PAGE,
B53_ARLTBL_MAC_VID_ENTRY(idx), mac_vid);
b53_write32(dev, B53_ARLIO_PAGE,
B53_ARLTBL_DATA_ENTRY(idx), fwd_entry);

return b53_arl_rw_op(dev, 0);
}

static int b53_fdb_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans)
{
struct b53_device *priv = ds_to_priv(ds);

/* 5325 and 5365 require some more massaging, but could
* be supported eventually
*/
if (is5325(priv) || is5365(priv))
return -EOPNOTSUPP;

return 0;
}

static void b53_fdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb,
struct switchdev_trans *trans)
{
struct b53_device *priv = ds_to_priv(ds);

if (b53_arl_op(priv, 0, port, fdb->addr, fdb->vid, true))
pr_err("%s: failed to add MAC address\n", __func__);
}

static int b53_fdb_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_fdb *fdb)
{
struct b53_device *priv = ds_to_priv(ds);

return b53_arl_op(priv, 0, port, fdb->addr, fdb->vid, false);
}

static int b53_arl_search_wait(struct b53_device *dev)
{
unsigned int timeout = 1000;
u8 reg;

do {
b53_read8(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_CTL, &reg);
if (!(reg & ARL_SRCH_STDN))
return 0;

if (reg & ARL_SRCH_VLID)
return 0;

usleep_range(1000, 2000);
} while (timeout--);

return -ETIMEDOUT;
}

static void b53_arl_search_rd(struct b53_device *dev, u8 idx,
struct b53_arl_entry *ent)
{
u64 mac_vid;
u32 fwd_entry;

b53_read64(dev, B53_ARLIO_PAGE,
B53_ARL_SRCH_RSTL_MACVID(idx), &mac_vid);
b53_read32(dev, B53_ARLIO_PAGE,
B53_ARL_SRCH_RSTL(idx), &fwd_entry);
b53_arl_to_entry(ent, mac_vid, fwd_entry);
}

static int b53_fdb_copy(struct net_device *dev, int port,
const struct b53_arl_entry *ent,
struct switchdev_obj_port_fdb *fdb,
int (*cb)(struct switchdev_obj *obj))
{
if (!ent->is_valid)
return 0;

if (port != ent->port)
return 0;

ether_addr_copy(fdb->addr, ent->mac);
fdb->vid = ent->vid;
fdb->ndm_state = ent->is_static ? NUD_NOARP : NUD_REACHABLE;

return cb(&fdb->obj);
}

static int b53_fdb_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_fdb *fdb,
int (*cb)(struct switchdev_obj *obj))
{
struct b53_device *priv = ds_to_priv(ds);
struct net_device *dev = ds->ports[port].netdev;
struct b53_arl_entry results[2];
unsigned int count = 0;
int ret;
u8 reg;

/* Start search operation */
reg = ARL_SRCH_STDN;
b53_write8(priv, B53_ARLIO_PAGE, B53_ARL_SRCH_CTL, reg);

do {
ret = b53_arl_search_wait(priv);
if (ret)
return ret;

b53_arl_search_rd(priv, 0, &results[0]);
ret = b53_fdb_copy(dev, port, &results[0], fdb, cb);
if (ret)
return ret;

if (priv->num_arl_entries > 2) {
b53_arl_search_rd(priv, 1, &results[1]);
ret = b53_fdb_copy(dev, port, &results[1], fdb, cb);
if (ret)
return ret;

if (!results[0].is_valid && !results[1].is_valid)
break;
}

} while (count++ < 1024);

return 0;
}

static struct dsa_switch_driver b53_switch_ops = {
.tag_protocol = DSA_TAG_PROTO_NONE,
.setup = b53_setup,
Expand All @@ -789,6 +1031,10 @@ static struct dsa_switch_driver b53_switch_ops = {
.adjust_link = b53_adjust_link,
.port_enable = b53_enable_port,
.port_disable = b53_disable_port,
.port_fdb_prepare = b53_fdb_prepare,
.port_fdb_dump = b53_fdb_dump,
.port_fdb_add = b53_fdb_add,
.port_fdb_del = b53_fdb_del,
};

struct b53_chip_data {
Expand All @@ -798,6 +1044,7 @@ struct b53_chip_data {
u16 enabled_ports;
u8 cpu_port;
u8 vta_regs[3];
u8 arl_entries;
u8 duplex_reg;
u8 jumbo_pm_reg;
u8 jumbo_size_reg;
Expand All @@ -816,6 +1063,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.dev_name = "BCM5325",
.vlans = 16,
.enabled_ports = 0x1f,
.arl_entries = 2,
.cpu_port = B53_CPU_PORT_25,
.duplex_reg = B53_DUPLEX_STAT_FE,
},
Expand All @@ -824,6 +1072,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.dev_name = "BCM5365",
.vlans = 256,
.enabled_ports = 0x1f,
.arl_entries = 2,
.cpu_port = B53_CPU_PORT_25,
.duplex_reg = B53_DUPLEX_STAT_FE,
},
Expand All @@ -832,6 +1081,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.dev_name = "BCM5395",
.vlans = 4096,
.enabled_ports = 0x1f,
.arl_entries = 4,
.cpu_port = B53_CPU_PORT,
.vta_regs = B53_VTA_REGS,
.duplex_reg = B53_DUPLEX_STAT_GE,
Expand All @@ -843,6 +1093,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.dev_name = "BCM5397",
.vlans = 4096,
.enabled_ports = 0x1f,
.arl_entries = 4,
.cpu_port = B53_CPU_PORT,
.vta_regs = B53_VTA_REGS_9798,
.duplex_reg = B53_DUPLEX_STAT_GE,
Expand All @@ -854,6 +1105,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.dev_name = "BCM5398",
.vlans = 4096,
.enabled_ports = 0x7f,
.arl_entries = 4,
.cpu_port = B53_CPU_PORT,
.vta_regs = B53_VTA_REGS_9798,
.duplex_reg = B53_DUPLEX_STAT_GE,
Expand All @@ -865,6 +1117,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.dev_name = "BCM53115",
.vlans = 4096,
.enabled_ports = 0x1f,
.arl_entries = 4,
.vta_regs = B53_VTA_REGS,
.cpu_port = B53_CPU_PORT,
.duplex_reg = B53_DUPLEX_STAT_GE,
Expand All @@ -887,6 +1140,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.dev_name = "BCM53128",
.vlans = 4096,
.enabled_ports = 0x1ff,
.arl_entries = 4,
.cpu_port = B53_CPU_PORT,
.vta_regs = B53_VTA_REGS,
.duplex_reg = B53_DUPLEX_STAT_GE,
Expand All @@ -898,6 +1152,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.dev_name = "BCM63xx",
.vlans = 4096,
.enabled_ports = 0, /* pdata must provide them */
.arl_entries = 4,
.cpu_port = B53_CPU_PORT,
.vta_regs = B53_VTA_REGS_63XX,
.duplex_reg = B53_DUPLEX_STAT_63XX,
Expand All @@ -909,6 +1164,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.dev_name = "BCM53010",
.vlans = 4096,
.enabled_ports = 0x1f,
.arl_entries = 4,
.cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
.vta_regs = B53_VTA_REGS,
.duplex_reg = B53_DUPLEX_STAT_GE,
Expand All @@ -920,6 +1176,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.dev_name = "BCM53011",
.vlans = 4096,
.enabled_ports = 0x1bf,
.arl_entries = 4,
.cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
.vta_regs = B53_VTA_REGS,
.duplex_reg = B53_DUPLEX_STAT_GE,
Expand All @@ -931,6 +1188,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.dev_name = "BCM53012",
.vlans = 4096,
.enabled_ports = 0x1bf,
.arl_entries = 4,
.cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
.vta_regs = B53_VTA_REGS,
.duplex_reg = B53_DUPLEX_STAT_GE,
Expand All @@ -942,6 +1200,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.dev_name = "BCM53018",
.vlans = 4096,
.enabled_ports = 0x1f,
.arl_entries = 4,
.cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
.vta_regs = B53_VTA_REGS,
.duplex_reg = B53_DUPLEX_STAT_GE,
Expand All @@ -953,6 +1212,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.dev_name = "BCM53019",
.vlans = 4096,
.enabled_ports = 0x1f,
.arl_entries = 4,
.cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
.vta_regs = B53_VTA_REGS,
.duplex_reg = B53_DUPLEX_STAT_GE,
Expand Down Expand Up @@ -982,6 +1242,7 @@ static int b53_switch_init(struct b53_device *dev)
ds->drv = &b53_switch_ops;
dev->cpu_port = chip->cpu_port;
dev->num_vlans = chip->vlans;
dev->num_arl_entries = chip->arl_entries;
break;
}
}
Expand Down
Loading

0 comments on commit 1da6df8

Please sign in to comment.