ar8216: add swconfig attribute to display ARL table on AR8327/AR8337

Add global read-only swconfig attribute "arl_table" to display the
address resolution table.
So far the chip-specific part is implemented for AR8327/AR8337 only
as I don't have the datasheets for the other AR8XXX chips.

Successfully tested on TL-WDR4300 (AR8327rev2)
and TL-WDR4900 (AR8327rev4).

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>

SVN-Revision: 44104
This commit is contained in:
Felix Fietkau 2015-01-24 19:42:06 +00:00
parent 6ce848f622
commit 3a313a3e11
4 changed files with 220 additions and 15 deletions

View file

@ -134,19 +134,6 @@ const struct ar8xxx_mib_desc ar8236_mibs[39] = {
static DEFINE_MUTEX(ar8xxx_dev_list_lock);
static LIST_HEAD(ar8xxx_dev_list);
static inline void
split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
{
regaddr >>= 1;
*r1 = regaddr & 0x1e;
regaddr >>= 5;
*r2 = regaddr & 0x7;
regaddr >>= 3;
*page = regaddr & 0x1ff;
}
/* inspired by phy_poll_reset in drivers/net/phy/phy_device.c */
static int
ar8xxx_phy_poll_reset(struct mii_bus *bus)
@ -217,7 +204,7 @@ ar8xxx_phy_init(struct ar8xxx_priv *priv)
ar8xxx_phy_poll_reset(bus);
}
static u32
u32
mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum)
{
struct mii_bus *bus = priv->mii_bus;
@ -229,7 +216,7 @@ mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum)
return (hi << 16) | lo;
}
static void
void
mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val)
{
struct mii_bus *bus = priv->mii_bus;
@ -1291,6 +1278,78 @@ unlock:
return ret;
}
int
ar8xxx_sw_get_arl_table(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
struct mii_bus *bus = priv->mii_bus;
const struct ar8xxx_chip *chip = priv->chip;
char *buf = priv->arl_buf;
int i, j, k, len = 0;
struct arl_entry *a, *a1;
u32 status;
if (!chip->get_arl_entry)
return -EOPNOTSUPP;
mutex_lock(&priv->reg_mutex);
mutex_lock(&bus->mdio_lock);
chip->get_arl_entry(priv, NULL, NULL, AR8XXX_ARL_INITIALIZE);
for(i = 0; i < AR8XXX_NUM_ARL_RECORDS; ++i) {
a = &priv->arl_table[i];
duplicate:
chip->get_arl_entry(priv, a, &status, AR8XXX_ARL_GET_NEXT);
if (!status)
break;
/* avoid duplicates
* ARL table can include multiple valid entries
* per MAC, just with differing status codes
*/
for (j = 0; j < i; ++j) {
a1 = &priv->arl_table[j];
if (a->port == a1->port && !memcmp(a->mac, a1->mac, sizeof(a->mac)))
goto duplicate;
}
}
mutex_unlock(&bus->mdio_lock);
len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
"address resolution table\n");
if (i == AR8XXX_NUM_ARL_RECORDS)
len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
"Too many entries found, displaying the first %d only!\n",
AR8XXX_NUM_ARL_RECORDS);
for (j = 0; j < priv->dev.ports; ++j) {
for (k = 0; k < i; ++k) {
a = &priv->arl_table[k];
if (a->port != j)
continue;
len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
"Port %d: MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
j,
a->mac[5], a->mac[4], a->mac[3],
a->mac[2], a->mac[1], a->mac[0]);
}
}
val->value.s = buf;
val->len = len;
mutex_unlock(&priv->reg_mutex);
return 0;
}
static const struct switch_attr ar8xxx_sw_attr_globals[] = {
{
.type = SWITCH_TYPE_INT,
@ -1338,6 +1397,13 @@ static const struct switch_attr ar8xxx_sw_attr_globals[] = {
.get = ar8xxx_sw_get_mirror_source_port,
.max = AR8216_NUM_PORTS - 1
},
{
.type = SWITCH_TYPE_STRING,
.name = "arl_table",
.description = "Get ARL table",
.set = NULL,
.get = ar8xxx_sw_get_arl_table,
},
};
const struct switch_attr ar8xxx_sw_attr_port[2] = {

View file

@ -337,6 +337,18 @@ enum {
AR8XXX_VER_AR8337 = 0x13,
};
#define AR8XXX_NUM_ARL_RECORDS 100
enum arl_op {
AR8XXX_ARL_INITIALIZE,
AR8XXX_ARL_GET_NEXT
};
struct arl_entry {
u8 port;
u8 mac[6];
};
struct ar8xxx_priv;
struct ar8xxx_mib_desc {
@ -372,6 +384,8 @@ struct ar8xxx_chip {
void (*vtu_load_vlan)(struct ar8xxx_priv *priv, u32 vid, u32 port_mask);
void (*phy_fixup)(struct ar8xxx_priv *priv, int phy);
void (*set_mirror_regs)(struct ar8xxx_priv *priv);
void (*get_arl_entry)(struct ar8xxx_priv *priv, struct arl_entry *a,
u32 *status, enum arl_op op);
int (*sw_hw_apply)(struct switch_dev *dev);
const struct ar8xxx_mib_desc *mib_decs;
@ -396,6 +410,8 @@ struct ar8xxx_priv {
bool initialized;
bool port4_phy;
char buf[2048];
struct arl_entry arl_table[AR8XXX_NUM_ARL_RECORDS];
char arl_buf[AR8XXX_NUM_ARL_RECORDS * 32 + 256];
bool link_up[AR8X16_MAX_PORTS];
bool init;
@ -422,6 +438,10 @@ struct ar8xxx_priv {
int monitor_port;
};
u32
mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum);
void
mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val);
u32
ar8xxx_read(struct ar8xxx_priv *priv, int reg);
void
@ -500,6 +520,10 @@ ar8xxx_sw_get_port_mib(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8xxx_sw_get_arl_table(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
int
ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val);
static inline struct ar8xxx_priv *
@ -555,6 +579,19 @@ ar8xxx_reg_clear(struct ar8xxx_priv *priv, int reg, u32 val)
ar8xxx_rmw(priv, reg, val, 0);
}
static inline void
split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
{
regaddr >>= 1;
*r1 = regaddr & 0x1e;
regaddr >>= 5;
*r2 = regaddr & 0x7;
regaddr >>= 3;
*page = regaddr & 0x1ff;
}
static inline void
wait_for_page_switch(void)
{

View file

@ -971,6 +971,78 @@ ar8327_sw_get_eee(struct switch_dev *dev,
return 0;
}
static void
ar8327_wait_atu_ready(struct ar8xxx_priv *priv, u16 r2, u16 r1)
{
int timeout = 20;
while (mii_read32(priv, r2, r1) & AR8327_ATU_FUNC_BUSY && --timeout)
udelay(10);
if (!timeout)
pr_err("ar8327: timeout waiting for atu to become ready\n");
}
static void ar8327_get_arl_entry(struct ar8xxx_priv *priv,
struct arl_entry *a, u32 *status, enum arl_op op)
{
struct mii_bus *bus = priv->mii_bus;
u16 r2, page;
u16 r1_data0, r1_data1, r1_data2, r1_func;
u32 t, val0, val1, val2;
int i;
split_addr(AR8327_REG_ATU_DATA0, &r1_data0, &r2, &page);
r2 |= 0x10;
r1_data1 = (AR8327_REG_ATU_DATA1 >> 1) & 0x1e;
r1_data2 = (AR8327_REG_ATU_DATA2 >> 1) & 0x1e;
r1_func = (AR8327_REG_ATU_FUNC >> 1) & 0x1e;
switch (op) {
case AR8XXX_ARL_INITIALIZE:
/* all ATU registers are on the same page
* therefore set page only once
*/
bus->write(bus, 0x18, 0, page);
wait_for_page_switch();
ar8327_wait_atu_ready(priv, r2, r1_func);
mii_write32(priv, r2, r1_data0, 0);
mii_write32(priv, r2, r1_data1, 0);
mii_write32(priv, r2, r1_data2, 0);
break;
case AR8XXX_ARL_GET_NEXT:
mii_write32(priv, r2, r1_func,
AR8327_ATU_FUNC_OP_GET_NEXT |
AR8327_ATU_FUNC_BUSY);
ar8327_wait_atu_ready(priv, r2, r1_func);
val0 = mii_read32(priv, r2, r1_data0);
val1 = mii_read32(priv, r2, r1_data1);
val2 = mii_read32(priv, r2, r1_data2);
*status = val2 & AR8327_ATU_STATUS;
if (!*status)
break;
i = 0;
t = AR8327_ATU_PORT0;
while (!(val1 & t) && ++i < AR8327_NUM_PORTS)
t <<= 1;
a->port = i;
a->mac[0] = (val0 & AR8327_ATU_ADDR0) >> AR8327_ATU_ADDR0_S;
a->mac[1] = (val0 & AR8327_ATU_ADDR1) >> AR8327_ATU_ADDR1_S;
a->mac[2] = (val0 & AR8327_ATU_ADDR2) >> AR8327_ATU_ADDR2_S;
a->mac[3] = (val0 & AR8327_ATU_ADDR3) >> AR8327_ATU_ADDR3_S;
a->mac[4] = (val1 & AR8327_ATU_ADDR4) >> AR8327_ATU_ADDR4_S;
a->mac[5] = (val1 & AR8327_ATU_ADDR5) >> AR8327_ATU_ADDR5_S;
break;
}
}
static int
ar8327_sw_hw_apply(struct switch_dev *dev)
{
@ -1041,6 +1113,13 @@ static const struct switch_attr ar8327_sw_attr_globals[] = {
.get = ar8xxx_sw_get_mirror_source_port,
.max = AR8327_NUM_PORTS - 1
},
{
.type = SWITCH_TYPE_STRING,
.name = "arl_table",
.description = "Get ARL table",
.set = NULL,
.get = ar8xxx_sw_get_arl_table,
},
};
static const struct switch_attr ar8327_sw_attr_port[] = {
@ -1114,6 +1193,7 @@ const struct ar8xxx_chip ar8327_chip = {
.vtu_load_vlan = ar8327_vtu_load_vlan,
.phy_fixup = ar8327_phy_fixup,
.set_mirror_regs = ar8327_set_mirror_regs,
.get_arl_entry = ar8327_get_arl_entry,
.sw_hw_apply = ar8327_sw_hw_apply,
.num_mibs = ARRAY_SIZE(ar8236_mibs),
@ -1146,6 +1226,7 @@ const struct ar8xxx_chip ar8337_chip = {
.vtu_load_vlan = ar8327_vtu_load_vlan,
.phy_fixup = ar8327_phy_fixup,
.set_mirror_regs = ar8327_set_mirror_regs,
.get_arl_entry = ar8327_get_arl_entry,
.sw_hw_apply = ar8327_sw_hw_apply,
.num_mibs = ARRAY_SIZE(ar8236_mibs),

View file

@ -114,8 +114,29 @@
#define AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH 3
#define AR8327_REG_ATU_DATA0 0x600
#define AR8327_ATU_ADDR0 BITS(0, 8)
#define AR8327_ATU_ADDR0_S 0
#define AR8327_ATU_ADDR1 BITS(8, 8)
#define AR8327_ATU_ADDR1_S 8
#define AR8327_ATU_ADDR2 BITS(16, 8)
#define AR8327_ATU_ADDR2_S 16
#define AR8327_ATU_ADDR3 BITS(24, 8)
#define AR8327_ATU_ADDR3_S 24
#define AR8327_REG_ATU_DATA1 0x604
#define AR8327_ATU_ADDR4 BITS(0, 8)
#define AR8327_ATU_ADDR4_S 0
#define AR8327_ATU_ADDR5 BITS(8, 8)
#define AR8327_ATU_ADDR5_S 8
#define AR8327_ATU_PORTS BITS(16, 7)
#define AR8327_ATU_PORT0 BIT(16)
#define AR8327_ATU_PORT1 BIT(17)
#define AR8327_ATU_PORT2 BIT(18)
#define AR8327_ATU_PORT3 BIT(19)
#define AR8327_ATU_PORT4 BIT(20)
#define AR8327_ATU_PORT5 BIT(21)
#define AR8327_ATU_PORT6 BIT(22)
#define AR8327_REG_ATU_DATA2 0x608
#define AR8327_ATU_STATUS BITS(0, 4)
#define AR8327_REG_ATU_FUNC 0x60c
#define AR8327_ATU_FUNC_OP BITS(0, 4)