On some ar71xx devices the MII register access is unreliable, so we need to use special atheros packets for accessing the switch registers.
Signed-off-by: Jonas Gorski <[email protected]> --- .../linux/generic/files/drivers/net/phy/ar8216.c | 222 +++++++++++++++++--- .../linux/generic/files/drivers/net/phy/ar8216.h | 26 +++ 2 files changed, 218 insertions(+), 30 deletions(-) diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c index 587f41f..875196f 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c @@ -29,6 +29,8 @@ #include <linux/phy.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> +#include <linux/sched.h> +#include <linux/wait.h> #include "ar8216.h" /* size of the vlan table */ @@ -46,6 +48,13 @@ struct ar8216_priv { int chip; bool initialized; bool port4_phy; + bool hdr_reg_acc; + + /* fields used for register access through packets */ + wait_queue_head_t wait; + bool finished; + u32 seq; + u32 val; /* all fields below are cleared on reset */ bool vlan; @@ -103,6 +112,102 @@ ar8216_mii_write(struct ar8216_priv *priv, int reg, u32 val) phy->bus->write(phy->bus, 0x10 | r2, r1, lo); } +static inline struct sk_buff * +ar8216_create_rw_reg_skb(u8 *data, int seq, int cmd, u32 reg) +{ + struct sk_buff *skb = dev_alloc_skb(ETH_ZLEN + ETH_FCS_LEN); + + if (!skb) + return ERR_PTR(-ENOMEM); + + skb_put(skb, ETH_ZLEN); + + skb->data[0] = AR8216_HDR_FROM_CPU | AR8216_HDR_VER_1; + skb->data[1] = AR8216_HDR_TYPE_RW_REG; + + skb->data[2] = reg & 0xff; + skb->data[3] = (reg >> 8) & 0xff; + skb->data[4] = (reg >> 16) & 0x3; + skb->data[4] |= (4 << AR8216_HDR_DATA_LEN_S); + skb->data[5] = cmd | AR8216_HDR_CHECK_CODE; + + skb->data[6] = (seq & 0x7f) << 1; + skb->data[7] = (seq >> 7) & 0xff; + skb->data[8] = (seq >> 15) & 0xff; + skb->data[9] = (seq >> 23) & 0xff; + + return skb; +} + +static u32 +ar8216_header_read(struct ar8216_priv *priv, int reg) +{ + struct sk_buff *skb; + int ret; + + skb = ar8216_create_rw_reg_skb(skb->data, ++priv->seq, + AR8216_HDR_READ_REG, reg); + + if (IS_ERR(skb)) { + pr_err("skb allocation failed: %li\n", PTR_ERR(skb)); + return ~0; + } + + skb->dev = priv->phy->attached_dev; + + priv->finished = 0; + + ret = priv->ndo_old->ndo_start_xmit(skb, priv->phy->attached_dev); + + if (ret) { + pr_err("xmit failed: %d\n", ret); + return ~0; + } + + if (!wait_event_timeout(priv->wait, priv->finished == 1, + msecs_to_jiffies(1000))) { + pr_err("reading register 0x%08x timed out!\n", reg); + return ~0; + } + + return priv->val; +} + +static void +ar8216_header_write(struct ar8216_priv *priv, int reg, u32 val) +{ + struct sk_buff *skb; + int ret; + u32 tmp; + + skb = ar8216_create_rw_reg_skb(skb->data, ++priv->seq, + AR8216_HDR_WRITE_REG, reg); + + if (IS_ERR(skb)) { + pr_err("skb allocation failed: %li\n", PTR_ERR(skb)); + return; + } + + + tmp = cpu_to_le32(val); + memcpy(skb->data + 10, &tmp, sizeof(u32)); + + skb->dev = priv->phy->attached_dev; + + priv->finished = 0; + + ret = priv->ndo_old->ndo_start_xmit(skb, priv->phy->attached_dev); + + if (ret) { + pr_err("xmit failed: %d\n", ret); + return; + } + + if (!wait_event_timeout(priv->wait, priv->finished == 1, + msecs_to_jiffies(1000))) + pr_err("writing register %08x timed out!\n", reg); +} + static u32 ar8216_rmw(struct ar8216_priv *priv, int reg, u32 mask, u32 val) { @@ -124,20 +229,28 @@ ar8216_id_chip(struct ar8216_priv *priv) int i; val = ar8216_mii_read(priv, AR8216_REG_CTRL); - if (val == ~0) - return UNKNOWN; + + /* AR8216 connected with a too high frequency will fail on register + * accesses most of the time. In that case fall back to register access + * through special atheros packets. + */ id = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION); + for (i = 0; i < AR8X16_PROBE_RETRIES; i++) { u16 t; val = ar8216_mii_read(priv, AR8216_REG_CTRL); - if (val == ~0) - return UNKNOWN; + if (val == ~0) { + priv->hdr_reg_acc = true; + return AR8216; + } t = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION); - if (t != id) - return UNKNOWN; + if (t != id) { + priv->hdr_reg_acc = true; + return AR8216; + } } switch (id) { @@ -227,7 +340,7 @@ ar8216_mangle_tx(struct sk_buff *skb, struct net_device *dev) if (unlikely(!priv)) goto error; - if (!priv->vlan) + if (!priv->vlan && !priv->hdr_reg_acc) goto send; if (unlikely(skb_headroom(skb) < 2)) { @@ -263,19 +376,50 @@ ar8216_mangle_rx(struct sk_buff *skb, int napi) if (!priv) goto error; - /* don't strip the header if vlan mode is disabled */ - if (!priv->vlan) + /* don't strip the header if vlan mode is disabled and are using MII + * bus switch register access */ + if (!priv->vlan && !priv->hdr_reg_acc) goto recv; - /* strip header, get vlan id */ buf = skb->data; + + switch (buf[1] & AR8216_HDR_TYPE) { + case AR8216_HDR_TYPE_NORMAL: + break; + case AR8216_HDR_TYPE_RW_REG_ACK: { + /* Sanity check: make sure the sequence number is the same + * we sent. */ + + u32 seq = (buf[6] >> 1) | (buf[7] << 7) | (buf[8] << 15) | + (buf[9] << 23); + + if (seq != (priv->seq & 0x7fffffff)) { + pr_warn("invalid sequence counter: expected 0x%08x, got 0x%08x\n", + priv->seq, seq); + } else { + if ((buf[4] >> AR8216_HDR_DATA_LEN_S) == 4) + priv->val = le32_to_cpu(*((u32 *)(buf + 10))); + priv->finished = 1; + wake_up(&priv->wait); + } + + dev_kfree_skb_any(skb); + return NET_RX_SUCCESS; + } + default: + pr_err("ar8216: unexpected packet type: %d\n", + buf[1] & AR8216_HDR_TYPE); + goto error; + } + + /* strip header, get vlan id */ skb_pull(skb, 2); /* check for vlan header presence */ if ((buf[12 + 2] != 0x81) || (buf[13 + 2] != 0x00)) goto recv; - port = buf[0] & 0xf; + port = buf[0] & AR8216_HDR_PORT; /* no need to fix up packets coming from a tagged source */ if (priv->vlan_tagged & (1 << port)) @@ -472,6 +616,7 @@ ar8216_hw_apply(struct switch_dev *dev) for (i = 0; i < AR8216_NUM_PORTS; i++) { int egress, ingress; int pvid; + u32 port_ctrl; if (priv->vlan) { pvid = priv->vlan_id[priv->pvid[i]]; @@ -490,15 +635,19 @@ ar8216_hw_apply(struct switch_dev *dev) ingress = AR8216_IN_PORT_ONLY; } + port_ctrl = AR8216_PORT_CTRL_LEARN | + (egress << AR8216_PORT_CTRL_VLAN_MODE_S) | + (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S); + + if (i == AR8216_PORT_CPU && priv->chip == AR8216 && + (priv->vlan || priv->hdr_reg_acc)) + port_ctrl |= AR8216_PORT_CTRL_HEADER; + ar8216_rmw(priv, AR8216_REG_PORT_CTRL(i), - AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE | - AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE | - AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK, - AR8216_PORT_CTRL_LEARN | - (priv->vlan && i == AR8216_PORT_CPU && (priv->chip == AR8216) ? - AR8216_PORT_CTRL_HEADER : 0) | - (egress << AR8216_PORT_CTRL_VLAN_MODE_S) | - (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S)); + AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE | + AR8216_PORT_CTRL_SINGLE_VLAN | + AR8216_PORT_CTRL_STATE | AR8216_PORT_CTRL_HEADER | + AR8216_PORT_CTRL_LEARN_LOCK, port_ctrl); ar8216_rmw(priv, AR8216_REG_PORT_VLAN(i), AR8216_PORT_VLAN_DEST_PORTS | AR8216_PORT_VLAN_MODE | @@ -591,6 +740,7 @@ ar8216_reset_switch(struct switch_dev *dev) for (i = 0; i < AR8216_NUM_PORTS; i++) { /* Enable port learning and tx */ priv->write(priv, AR8216_REG_PORT_CTRL(i), + ((i == 0 && priv->hdr_reg_acc) ? AR8216_PORT_CTRL_HEADER : 0) | AR8216_PORT_CTRL_LEARN | (4 << AR8216_PORT_CTRL_STATE_S)); @@ -714,8 +864,14 @@ ar8216_config_init(struct phy_device *pdev) pdev->advertising = pdev->supported; mutex_init(&priv->reg_mutex); - priv->read = ar8216_mii_read; - priv->write = ar8216_mii_write; + if (priv->hdr_reg_acc) { + priv->read = ar8216_header_read; + priv->write = ar8216_header_write; + init_waitqueue_head(&priv->wait); + } else { + priv->read = ar8216_mii_read; + priv->write = ar8216_mii_write; + } pdev->priv = priv; @@ -750,7 +906,10 @@ ar8216_config_init(struct phy_device *pdev) } } - ret = ar8216_reset_switch(&priv->dev); + /* we can't send packets at this stage, so skip reset */ + if (!priv->hdr_reg_acc) + ret = ar8216_reset_switch(&priv->dev); + if (ret) { kfree(priv); goto done; @@ -777,7 +936,7 @@ static int ar8216_read_status(struct phy_device *phydev) { struct ar8216_priv *priv = phydev->priv; - int ret; + int ret = 0; if (phydev->addr != 0) { return genphy_read_status(phydev); } @@ -787,14 +946,17 @@ ar8216_read_status(struct phy_device *phydev) phydev->link = 1; /* flush the address translation unit */ - mutex_lock(&priv->reg_mutex); - ret = ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0); + if (!priv->hdr_reg_acc || + phydev->attached_dev->operstate == IF_OPER_UP) { + mutex_lock(&priv->reg_mutex); + ret = ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0); - if (!ret) - priv->write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH); - else - ret = -ETIMEDOUT; - mutex_unlock(&priv->reg_mutex); + if (!ret) + priv->write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH); + else + ret = -ETIMEDOUT; + mutex_unlock(&priv->reg_mutex); + } phydev->state = PHY_RUNNING; netif_carrier_on(phydev->attached_dev); diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.h b/target/linux/generic/files/drivers/net/phy/ar8216.h index 5a8fa3c..db1b7ad 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.h +++ b/target/linux/generic/files/drivers/net/phy/ar8216.h @@ -184,4 +184,30 @@ enum { AR8316 = 8316 }; +/* Atheros header format */ + +/* byte 0 */ +#define AR8216_HDR_PORT 0x0f +#define AR8216_HDR_VER_1 (1 << 4) +#define AR8216_HDR_FROM_CPU BIT(6) + +/* byte 1 */ +#define AR8216_HDR_TYPE 0x0f + +/* byte 4 */ +#define AR8216_HDR_DATA_LEN_S 4 + +/* byte 5 */ +#define AR8216_HDR_READ_REG BIT(4) +#define AR8216_HDR_WRITE_REG 0 +#define AR8216_HDR_CHECK_CODE (5 << 5) + + +/* atheros header packet type */ +enum { + AR8216_HDR_TYPE_NORMAL = 0, + AR8216_HDR_TYPE_RW_REG = 5, + AR8216_HDR_TYPE_RW_REG_ACK = 6 +}; + #endif -- 1.7.2.5 _______________________________________________ openwrt-devel mailing list [email protected] https://lists.openwrt.org/mailman/listinfo/openwrt-devel
