From patchwork Thu Jan 22 21:57:46 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Heiner Kallweit X-Patchwork-Id: 432009 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from arrakis.dune.hu (arrakis.dune.hu [78.24.191.176]) (using TLSv1.1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 20B0B14028F for ; Fri, 23 Jan 2015 09:00:21 +1100 (AEDT) Received: from arrakis.dune.hu (localhost [127.0.0.1]) by arrakis.dune.hu (Postfix) with ESMTP id E2D0A28BE59; Thu, 22 Jan 2015 22:56:35 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on arrakis.dune.hu X-Spam-Level: X-Spam-Status: No, score=-1.2 required=5.0 tests=BAYES_00, FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM, T_DKIM_INVALID autolearn=no version=3.3.2 Received: from arrakis.dune.hu (localhost [127.0.0.1]) by arrakis.dune.hu (Postfix) with ESMTP id 2AA1728BE23 for ; Thu, 22 Jan 2015 22:56:16 +0100 (CET) X-policyd-weight: using cached result; rate: -8.5 Received: from mail-wg0-f44.google.com (mail-wg0-f44.google.com [74.125.82.44]) by arrakis.dune.hu (Postfix) with ESMTPS for ; Thu, 22 Jan 2015 22:56:12 +0100 (CET) Received: by mail-wg0-f44.google.com with SMTP id z12so4275195wgg.3 for ; Thu, 22 Jan 2015 13:58:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=message-id:date:from:user-agent:mime-version:to:cc:subject :content-type:content-transfer-encoding; bh=cSE5Iz4GTg+HA5kzHjCZui+ctyE8VAs1+fTYp7IVb8I=; b=QeYKaAuniIUTezXoF0gPqedeuDYKdNfKK022qHiJ6bG87YJ2V7c+CT2FnrAcExxoDy iLTxhRDTtBLvYt8BEqhKuM+QQ5yO3OHIpBcsZxa20H2LETiBiWLuxvSAwRqpMls07e8h idjI8jclAeWGmXlLGVBWzXw6SGI1gPz8PDK+1SwigRhMJ4WpM1wq98Z/eaHmjrGX1pOj qA1/uXd7g2FDAqt5Xx5YraGLYgKlT4AfkOUAYpSzUOVMZfkBxqRoZDv6iLIfN11mS1qS 1wVfDnriqJ+v0JWYQReVly3kDFtpum1f+3zR7rA8gSbVru09a2CfdevcWwBjUtzoLVVx WQcQ== X-Received: by 10.180.212.113 with SMTP id nj17mr15787728wic.54.1421963913129; Thu, 22 Jan 2015 13:58:33 -0800 (PST) Received: from ?IPv6:2003:63:231f:ff00:3535:671c:f47a:7bc7? (p20030063231FFF003535671CF47A7BC7.dip0.t-ipconnect.de. [2003:63:231f:ff00:3535:671c:f47a:7bc7]) by mx.google.com with ESMTPSA id u13sm5633680wjr.26.2015.01.22.13.58.32 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 22 Jan 2015 13:58:32 -0800 (PST) Message-ID: <54C1725A.90207@gmail.com> Date: Thu, 22 Jan 2015 22:57:46 +0100 From: Heiner Kallweit User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20100101 Thunderbird/31.4.0 MIME-Version: 1.0 To: Felix Fietkau Cc: OpenWrt Development List Subject: [OpenWrt-Devel] [PATCH 4/5] ar8216: add swconfig attribute to display ARL table on AR8327/AR8337 X-BeenThere: openwrt-devel@lists.openwrt.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: OpenWrt Development List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: openwrt-devel-bounces@lists.openwrt.org Sender: "openwrt-devel" 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 --- .../linux/generic/files/drivers/net/phy/ar8216.c | 96 ++++++++++++++++++---- .../linux/generic/files/drivers/net/phy/ar8216.h | 37 +++++++++ .../linux/generic/files/drivers/net/phy/ar8327.c | 81 ++++++++++++++++++ .../linux/generic/files/drivers/net/phy/ar8327.h | 21 +++++ 4 files changed, 220 insertions(+), 15 deletions(-) diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c index 8a40a32..9272e75 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c @@ -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] = { diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.h b/target/linux/generic/files/drivers/net/phy/ar8216.h index 77df506..ec640dd 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.h +++ b/target/linux/generic/files/drivers/net/phy/ar8216.h @@ -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; @@ -423,6 +439,10 @@ struct ar8xxx_priv { }; 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 ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val); @@ -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 * @@ -556,6 +580,19 @@ ar8xxx_reg_clear(struct ar8xxx_priv *priv, int reg, u32 val) } 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) { udelay(5); diff --git a/target/linux/generic/files/drivers/net/phy/ar8327.c b/target/linux/generic/files/drivers/net/phy/ar8327.c index fd5bcdc..6444c39 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8327.c +++ b/target/linux/generic/files/drivers/net/phy/ar8327.c @@ -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), diff --git a/target/linux/generic/files/drivers/net/phy/ar8327.h b/target/linux/generic/files/drivers/net/phy/ar8327.h index f0ef013..29eeb62 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8327.h +++ b/target/linux/generic/files/drivers/net/phy/ar8327.h @@ -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)