diff options
author | Felix Fietkau <nbd@openwrt.org> | 2015-07-15 08:17:23 +0000 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2015-07-15 08:17:23 +0000 |
commit | 7598d1194406091a37a76601c84c9b4965d67a84 (patch) | |
tree | a2dd90c059a7909d94488bbf63add11a16d839dc /target/linux/generic/files/drivers/net/phy/ar8216.c | |
parent | f1c187be921de56fec4760c22d7c45728337b95a (diff) | |
download | master-187ad058-7598d1194406091a37a76601c84c9b4965d67a84.tar.gz master-187ad058-7598d1194406091a37a76601c84c9b4965d67a84.tar.bz2 master-187ad058-7598d1194406091a37a76601c84c9b4965d67a84.zip |
ar8216: add reading ARL table for AR8216/AR8236/AR8316
Adds the chip-specific part of reading ARL table for AR8216/AR8236/AR8316.
It's based on the AR8236 datasheet and compile-tested only as I couldn't
find datasheets for AR8216/AR8316 and don't own devices with these chips.
The existing ar8216_atu_flush implementation was used for all three
chip types, therefore I guess they share a common ATU register layout.
More testing would be appreciated.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@46379 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/generic/files/drivers/net/phy/ar8216.c')
-rw-r--r-- | target/linux/generic/files/drivers/net/phy/ar8216.c | 81 |
1 files changed, 77 insertions, 4 deletions
diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c index e39d540a25..595f144ddb 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c @@ -598,10 +598,10 @@ ar8216_atu_flush(struct ar8xxx_priv *priv) { int ret; - ret = ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0); + ret = ar8216_wait_bit(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_ACTIVE, 0); if (!ret) - ar8xxx_write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH | - AR8216_ATU_ACTIVE); + ar8xxx_write(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_OP_FLUSH | + AR8216_ATU_ACTIVE); return ret; } @@ -702,6 +702,77 @@ ar8216_init_port(struct ar8xxx_priv *priv, int port) } static void +ar8216_wait_atu_ready(struct ar8xxx_priv *priv, u16 r2, u16 r1) +{ + int timeout = 20; + + while (ar8xxx_mii_read32(priv, r2, r1) & AR8216_ATU_ACTIVE && --timeout) + udelay(10); + + if (!timeout) + pr_err("ar8216: timeout waiting for atu to become ready\n"); +} + +static void ar8216_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_func0, r1_func1, r1_func2; + u32 t, val0, val1, val2; + int i; + + split_addr(AR8216_REG_ATU_FUNC0, &r1_func0, &r2, &page); + r2 |= 0x10; + + r1_func1 = (AR8216_REG_ATU_FUNC1 >> 1) & 0x1e; + r1_func2 = (AR8216_REG_ATU_FUNC2 >> 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(); + + ar8216_wait_atu_ready(priv, r2, r1_func0); + + ar8xxx_mii_write32(priv, r2, r1_func0, AR8216_ATU_OP_GET_NEXT); + ar8xxx_mii_write32(priv, r2, r1_func1, 0); + ar8xxx_mii_write32(priv, r2, r1_func2, 0); + break; + case AR8XXX_ARL_GET_NEXT: + t = ar8xxx_mii_read32(priv, r2, r1_func0); + t |= AR8216_ATU_ACTIVE; + ar8xxx_mii_write32(priv, r2, r1_func0, t); + ar8216_wait_atu_ready(priv, r2, r1_func0); + + val0 = ar8xxx_mii_read32(priv, r2, r1_func0); + val1 = ar8xxx_mii_read32(priv, r2, r1_func1); + val2 = ar8xxx_mii_read32(priv, r2, r1_func2); + + *status = (val2 & AR8216_ATU_STATUS) >> AR8216_ATU_STATUS_S; + if (!*status) + break; + + i = 0; + t = AR8216_ATU_PORT0; + while (!(val2 & t) && ++i < priv->dev.ports) + t <<= 1; + + a->port = i; + a->mac[0] = (val0 & AR8216_ATU_ADDR5) >> AR8216_ATU_ADDR5_S; + a->mac[1] = (val0 & AR8216_ATU_ADDR4) >> AR8216_ATU_ADDR4_S; + a->mac[2] = (val1 & AR8216_ATU_ADDR3) >> AR8216_ATU_ADDR3_S; + a->mac[3] = (val1 & AR8216_ATU_ADDR2) >> AR8216_ATU_ADDR2_S; + a->mac[4] = (val1 & AR8216_ATU_ADDR1) >> AR8216_ATU_ADDR1_S; + a->mac[5] = (val1 & AR8216_ATU_ADDR0) >> AR8216_ATU_ADDR0_S; + break; + } +} + +static void ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 members) { u32 egress, ingress; @@ -1349,7 +1420,6 @@ ar8xxx_sw_get_arl_table(struct switch_dev *dev, return 0; } - static const struct switch_attr ar8xxx_sw_attr_globals[] = { { .type = SWITCH_TYPE_INT, @@ -1475,6 +1545,7 @@ static const struct ar8xxx_chip ar8216_chip = { .vtu_flush = ar8216_vtu_flush, .vtu_load_vlan = ar8216_vtu_load_vlan, .set_mirror_regs = ar8216_set_mirror_regs, + .get_arl_entry = ar8216_get_arl_entry, .sw_hw_apply = ar8xxx_sw_hw_apply, .num_mibs = ARRAY_SIZE(ar8216_mibs), @@ -1502,6 +1573,7 @@ static const struct ar8xxx_chip ar8236_chip = { .vtu_flush = ar8216_vtu_flush, .vtu_load_vlan = ar8216_vtu_load_vlan, .set_mirror_regs = ar8216_set_mirror_regs, + .get_arl_entry = ar8216_get_arl_entry, .sw_hw_apply = ar8xxx_sw_hw_apply, .num_mibs = ARRAY_SIZE(ar8236_mibs), @@ -1529,6 +1601,7 @@ static const struct ar8xxx_chip ar8316_chip = { .vtu_flush = ar8216_vtu_flush, .vtu_load_vlan = ar8216_vtu_load_vlan, .set_mirror_regs = ar8216_set_mirror_regs, + .get_arl_entry = ar8216_get_arl_entry, .sw_hw_apply = ar8xxx_sw_hw_apply, .num_mibs = ARRAY_SIZE(ar8236_mibs), |