Skip to content

Commit

Permalink
net: dsa: lan9303: Add port_fast_age and port_fdb_dump methods
Browse files Browse the repository at this point in the history
Add DSA method port_fast_age as a step to STP support.

Add low level functions for accessing the lan9303 ALR (Address Logic
Resolution).

Added DSA method port_fdb_dump

Signed-off-by: Egil Hjelmeland <privat@egil-hjelmeland.no>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
hjelmeland authored and davem330 committed Oct 22, 2017
1 parent 0d5fceb commit ab33534
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 0 deletions.
160 changes: 160 additions & 0 deletions drivers/net/dsa/lan9303-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,21 @@
#define LAN9303_MAC_RX_CFG_2 0x0c01
#define LAN9303_MAC_TX_CFG_2 0x0c40
#define LAN9303_SWE_ALR_CMD 0x1800
# define LAN9303_ALR_CMD_MAKE_ENTRY BIT(2)
# define LAN9303_ALR_CMD_GET_FIRST BIT(1)
# define LAN9303_ALR_CMD_GET_NEXT BIT(0)
#define LAN9303_SWE_ALR_WR_DAT_0 0x1801
#define LAN9303_SWE_ALR_WR_DAT_1 0x1802
# define LAN9303_ALR_DAT1_VALID BIT(26)
# define LAN9303_ALR_DAT1_END_OF_TABL BIT(25)
# define LAN9303_ALR_DAT1_AGE_OVERRID BIT(25)
# define LAN9303_ALR_DAT1_STATIC BIT(24)
# define LAN9303_ALR_DAT1_PORT_BITOFFS 16
# define LAN9303_ALR_DAT1_PORT_MASK (7 << LAN9303_ALR_DAT1_PORT_BITOFFS)
#define LAN9303_SWE_ALR_RD_DAT_0 0x1805
#define LAN9303_SWE_ALR_RD_DAT_1 0x1806
#define LAN9303_SWE_ALR_CMD_STS 0x1808
# define ALR_STS_MAKE_PEND BIT(0)
#define LAN9303_SWE_VLAN_CMD 0x180b
# define LAN9303_SWE_VLAN_CMD_RNW BIT(5)
# define LAN9303_SWE_VLAN_CMD_PVIDNVLAN BIT(4)
Expand Down Expand Up @@ -478,6 +493,122 @@ static int lan9303_detect_phy_setup(struct lan9303 *chip)
return 0;
}

/* Map ALR-port bits to port bitmap, and back */
static const int alrport_2_portmap[] = {1, 2, 4, 0, 3, 5, 6, 7 };
static const int portmap_2_alrport[] = {3, 0, 1, 4, 2, 5, 6, 7 };

/* Wait a while until mask & reg == value. Otherwise return timeout. */
static int lan9303_csr_reg_wait(struct lan9303 *chip, int regno,
int mask, char value)
{
int i;

for (i = 0; i < 0x1000; i++) {
u32 reg;

lan9303_read_switch_reg(chip, regno, &reg);
if ((reg & mask) == value)
return 0;
usleep_range(1000, 2000);
}
return -ETIMEDOUT;
}

static int lan9303_alr_make_entry_raw(struct lan9303 *chip, u32 dat0, u32 dat1)
{
lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_WR_DAT_0, dat0);
lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_WR_DAT_1, dat1);
lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD,
LAN9303_ALR_CMD_MAKE_ENTRY);
lan9303_csr_reg_wait(chip, LAN9303_SWE_ALR_CMD_STS, ALR_STS_MAKE_PEND,
0);
lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD, 0);

return 0;
}

typedef void alr_loop_cb_t(struct lan9303 *chip, u32 dat0, u32 dat1,
int portmap, void *ctx);

static void lan9303_alr_loop(struct lan9303 *chip, alr_loop_cb_t *cb, void *ctx)
{
int i;

lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD,
LAN9303_ALR_CMD_GET_FIRST);
lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD, 0);

for (i = 1; i < LAN9303_NUM_ALR_RECORDS; i++) {
u32 dat0, dat1;
int alrport, portmap;

lan9303_read_switch_reg(chip, LAN9303_SWE_ALR_RD_DAT_0, &dat0);
lan9303_read_switch_reg(chip, LAN9303_SWE_ALR_RD_DAT_1, &dat1);
if (dat1 & LAN9303_ALR_DAT1_END_OF_TABL)
break;

alrport = (dat1 & LAN9303_ALR_DAT1_PORT_MASK) >>
LAN9303_ALR_DAT1_PORT_BITOFFS;
portmap = alrport_2_portmap[alrport];

cb(chip, dat0, dat1, portmap, ctx);

lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD,
LAN9303_ALR_CMD_GET_NEXT);
lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD, 0);
}
}

static void alr_reg_to_mac(u32 dat0, u32 dat1, u8 mac[6])
{
mac[0] = (dat0 >> 0) & 0xff;
mac[1] = (dat0 >> 8) & 0xff;
mac[2] = (dat0 >> 16) & 0xff;
mac[3] = (dat0 >> 24) & 0xff;
mac[4] = (dat1 >> 0) & 0xff;
mac[5] = (dat1 >> 8) & 0xff;
}

struct del_port_learned_ctx {
int port;
};

/* Clear learned (non-static) entry on given port */
static void alr_loop_cb_del_port_learned(struct lan9303 *chip, u32 dat0,
u32 dat1, int portmap, void *ctx)
{
struct del_port_learned_ctx *del_ctx = ctx;
int port = del_ctx->port;

if (((BIT(port) & portmap) == 0) || (dat1 & LAN9303_ALR_DAT1_STATIC))
return;

/* learned entries has only one port, we can just delete */
dat1 &= ~LAN9303_ALR_DAT1_VALID; /* delete entry */
lan9303_alr_make_entry_raw(chip, dat0, dat1);
}

struct port_fdb_dump_ctx {
int port;
void *data;
dsa_fdb_dump_cb_t *cb;
};

static void alr_loop_cb_fdb_port_dump(struct lan9303 *chip, u32 dat0,
u32 dat1, int portmap, void *ctx)
{
struct port_fdb_dump_ctx *dump_ctx = ctx;
u8 mac[ETH_ALEN];
bool is_static;

if ((BIT(dump_ctx->port) & portmap) == 0)
return;

alr_reg_to_mac(dat0, dat1, mac);
is_static = !!(dat1 & LAN9303_ALR_DAT1_STATIC);
dump_ctx->cb(mac, 0, is_static, dump_ctx->data);
}

static int lan9303_disable_processing_port(struct lan9303 *chip,
unsigned int port)
{
Expand Down Expand Up @@ -923,6 +1054,33 @@ static void lan9303_port_stp_state_set(struct dsa_switch *ds, int port,
/* else: touching SWE_PORT_STATE would break port separation */
}

static void lan9303_port_fast_age(struct dsa_switch *ds, int port)
{
struct lan9303 *chip = ds->priv;
struct del_port_learned_ctx del_ctx = {
.port = port,
};

dev_dbg(chip->dev, "%s(%d)\n", __func__, port);
lan9303_alr_loop(chip, alr_loop_cb_del_port_learned, &del_ctx);
}

static int lan9303_port_fdb_dump(struct dsa_switch *ds, int port,
dsa_fdb_dump_cb_t *cb, void *data)
{
struct lan9303 *chip = ds->priv;
struct port_fdb_dump_ctx dump_ctx = {
.port = port,
.data = data,
.cb = cb,
};

dev_dbg(chip->dev, "%s(%d)\n", __func__, port);
lan9303_alr_loop(chip, alr_loop_cb_fdb_port_dump, &dump_ctx);

return 0;
}

static const struct dsa_switch_ops lan9303_switch_ops = {
.get_tag_protocol = lan9303_get_tag_protocol,
.setup = lan9303_setup,
Expand All @@ -937,6 +1095,8 @@ static const struct dsa_switch_ops lan9303_switch_ops = {
.port_bridge_join = lan9303_port_bridge_join,
.port_bridge_leave = lan9303_port_bridge_leave,
.port_stp_state_set = lan9303_port_stp_state_set,
.port_fast_age = lan9303_port_fast_age,
.port_fdb_dump = lan9303_port_fdb_dump,
};

static int lan9303_register_switch(struct lan9303 *chip)
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/dsa/lan9303.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ struct lan9303_phy_ops {
int regnum, u16 val);
};

#define LAN9303_NUM_ALR_RECORDS 512

struct lan9303 {
struct device *dev;
struct regmap *regmap;
Expand Down

0 comments on commit ab33534

Please sign in to comment.