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:
parent
6ce848f622
commit
3a313a3e11
4 changed files with 220 additions and 15 deletions
|
@ -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] = {
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue