X-Git-Url: http://git.rohieb.name/openwrt.git/blobdiff_plain/681d7f126b95ad5640be5c20a71fcebb4528f84e..b4566a37d16109d0c97870cce89d18bc69d9b0fd:/target/linux/generic-2.6/files/drivers/net/phy/ar8216.c?ds=sidebyside diff --git a/target/linux/generic-2.6/files/drivers/net/phy/ar8216.c b/target/linux/generic-2.6/files/drivers/net/phy/ar8216.c index d83845c53..4ae61da23 100644 --- a/target/linux/generic-2.6/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic-2.6/files/drivers/net/phy/ar8216.c @@ -27,20 +27,28 @@ #include #include #include +#include +#include #include "ar8216.h" -#define AR8216_REG_PORT_RATE(_i) (AR8216_PORT_OFFSET(_i) + 0x000c) -#define AR8216_REG_PORT_PRIO(_i) (AR8216_PORT_OFFSET(_i) + 0x0010) +/* size of the vlan table */ +#define AR8X16_MAX_VLANS 128 +#define AR8X16_PROBE_RETRIES 10 struct ar8216_priv { struct switch_dev dev; struct phy_device *phy; u32 (*read)(struct ar8216_priv *priv, int reg); void (*write)(struct ar8216_priv *priv, int reg, u32 val); + const struct net_device_ops *ndo_old; + struct net_device_ops ndo; + struct mutex reg_mutex; + int chip; + /* all fields below are cleared on reset */ bool vlan; - u8 vlan_id[AR8216_NUM_VLANS]; - u8 vlan_table[AR8216_NUM_VLANS]; + u16 vlan_id[AR8X16_MAX_VLANS]; + u8 vlan_table[AR8X16_MAX_VLANS]; u8 vlan_tagged; u16 pvid[AR8216_NUM_PORTS]; }; @@ -70,6 +78,7 @@ ar8216_mii_read(struct ar8216_priv *priv, int reg) split_addr((u32) reg, &r1, &r2, &page); phy->bus->write(phy->bus, 0x18, 0, page); + msleep(1); /* wait for the page switch to propagate */ lo = phy->bus->read(phy->bus, 0x10 | r2, r1); hi = phy->bus->read(phy->bus, 0x10 | r2, r1 + 1); @@ -85,6 +94,7 @@ ar8216_mii_write(struct ar8216_priv *priv, int reg, u32 val) split_addr((u32) reg, &r1, &r2, &r3); phy->bus->write(phy->bus, 0x18, 0, r3); + msleep(1); /* wait for the page switch to propagate */ lo = val & 0xffff; hi = (u16) (val >> 16); @@ -105,6 +115,47 @@ ar8216_rmw(struct ar8216_priv *priv, int reg, u32 mask, u32 val) return v; } +static inline int +ar8216_id_chip(struct ar8216_priv *priv) +{ + u32 val; + u16 id; + int i; + + val = ar8216_mii_read(priv, AR8216_REG_CTRL); + if (val == ~0) + return UNKNOWN; + + 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; + + t = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION); + if (t != id) + return UNKNOWN; + } + + switch (id) { + case 0x0101: + return AR8216; + case 0x1001: + return AR8316; + default: + printk(KERN_DEBUG + "ar8216: Unknown Atheros device [ver=%d, rev=%d, phy_id=%04x%04x]\n", + (int)(id >> AR8216_CTRL_VERSION_S), + (int)(id & AR8216_CTRL_REVISION), + priv->phy->bus->read(priv->phy->bus, priv->phy->addr, 2), + priv->phy->bus->read(priv->phy->bus, priv->phy->addr, 3)); + + return UNKNOWN; + } +} + static int ar8216_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) @@ -128,6 +179,12 @@ static int ar8216_set_pvid(struct switch_dev *dev, int port, int vlan) { struct ar8216_priv *priv = to_ar8216(dev); + + /* make sure no invalid PVIDs get set */ + + if (vlan >= dev->vlans) + return -EINVAL; + priv->pvid[port] = vlan; return 0; } @@ -159,10 +216,107 @@ ar8216_get_vid(struct switch_dev *dev, const struct switch_attr *attr, } +static int +ar8216_mangle_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct ar8216_priv *priv = dev->phy_ptr; + unsigned char *buf; + + if (unlikely(!priv)) + goto error; + + if (!priv->vlan) + goto send; + + if (unlikely(skb_headroom(skb) < 2)) { + if (pskb_expand_head(skb, 2, 0, GFP_ATOMIC) < 0) + goto error; + } + + buf = skb_push(skb, 2); + buf[0] = 0x10; + buf[1] = 0x80; + +send: + return priv->ndo_old->ndo_start_xmit(skb, dev); + +error: + dev_kfree_skb_any(skb); + return 0; +} + +static int +ar8216_mangle_rx(struct sk_buff *skb, int napi) +{ + struct ar8216_priv *priv; + struct net_device *dev; + unsigned char *buf; + int port, vlan; + + dev = skb->dev; + if (!dev) + goto error; + + priv = dev->phy_ptr; + if (!priv) + goto error; + + /* don't strip the header if vlan mode is disabled */ + if (!priv->vlan) + goto recv; + + /* strip header, get vlan id */ + buf = skb->data; + skb_pull(skb, 2); + + /* check for vlan header presence */ + if ((buf[12 + 2] != 0x81) || (buf[13 + 2] != 0x00)) + goto recv; + + port = buf[0] & 0xf; + + /* no need to fix up packets coming from a tagged source */ + if (priv->vlan_tagged & (1 << port)) + goto recv; + + /* lookup port vid from local table, the switch passes an invalid vlan id */ + vlan = priv->vlan_id[priv->pvid[port]]; + + buf[14 + 2] &= 0xf0; + buf[14 + 2] |= vlan >> 8; + buf[15 + 2] = vlan & 0xff; + +recv: + skb->protocol = eth_type_trans(skb, skb->dev); + + if (napi) + return netif_receive_skb(skb); + else + return netif_rx(skb); + +error: + /* no vlan? eat the packet! */ + dev_kfree_skb_any(skb); + return NET_RX_DROP; +} + +static int +ar8216_netif_rx(struct sk_buff *skb) +{ + return ar8216_mangle_rx(skb, 0); +} + +static int +ar8216_netif_receive_skb(struct sk_buff *skb) +{ + return ar8216_mangle_rx(skb, 1); +} + + static struct switch_attr ar8216_globals[] = { { .type = SWITCH_TYPE_INT, - .name = "vlan", + .name = "enable_vlan", .description = "Enable VLAN mode", .set = ar8216_set_vlan, .get = ar8216_get_vlan, @@ -180,7 +334,7 @@ static struct switch_attr ar8216_vlan[] = { .description = "VLAN ID", .set = ar8216_set_vid, .get = ar8216_get_vid, - .max = 4095, + .max = 4094, }, }; @@ -228,7 +382,7 @@ ar8216_set_ports(struct switch_dev *dev, struct switch_val *val) /* make sure that an untagged port does not * appear in other vlans */ - for (j = 0; j < AR8216_NUM_VLANS; j++) { + for (j = 0; j < AR8X16_MAX_VLANS; j++) { if (j == val->port_vlan) continue; priv->vlan_table[j] &= ~(1 << p->id); @@ -275,6 +429,7 @@ ar8216_hw_apply(struct switch_dev *dev) u8 portmask[AR8216_NUM_PORTS]; int i, j; + mutex_lock(&priv->reg_mutex); /* flush all vlan translation unit entries */ ar8216_vtu_op(priv, AR8216_VTU_OP_FLUSH, 0); @@ -282,7 +437,7 @@ ar8216_hw_apply(struct switch_dev *dev) if (priv->vlan) { /* calculate the port destination masks and load vlans * into the vlan translation unit */ - for (j = 0; j < AR8216_NUM_VLANS; j++) { + for (j = 0; j < AR8X16_MAX_VLANS; j++) { u8 vp = priv->vlan_table[j]; if (!vp) @@ -294,9 +449,6 @@ ar8216_hw_apply(struct switch_dev *dev) portmask[i] |= vp & ~mask; } - if (!priv->vlan_table[j]) - continue; - ar8216_vtu_op(priv, AR8216_VTU_OP_LOAD | (priv->vlan_id[j] << AR8216_VTU_VID_S), @@ -327,10 +479,13 @@ ar8216_hw_apply(struct switch_dev *dev) if (priv->vlan && (priv->vlan_tagged & (1 << i))) { egress = AR8216_OUT_ADD_VLAN; - ingress = AR8216_IN_PORT_FALLBACK; } else { egress = AR8216_OUT_STRIP_VLAN; + } + if (priv->vlan) { ingress = AR8216_IN_SECURE; + } else { + ingress = AR8216_IN_PORT_ONLY; } ar8216_rmw(priv, AR8216_REG_PORT_CTRL(i), @@ -338,8 +493,9 @@ ar8216_hw_apply(struct switch_dev *dev) 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) | - (priv->vlan ? AR8216_PORT_CTRL_SINGLE_VLAN : 0) | (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S)); ar8216_rmw(priv, AR8216_REG_PORT_VLAN(i), @@ -349,7 +505,73 @@ ar8216_hw_apply(struct switch_dev *dev) (ingress << AR8216_PORT_VLAN_MODE_S) | (pvid << AR8216_PORT_VLAN_DEFAULT_ID_S)); } + mutex_unlock(&priv->reg_mutex); + return 0; +} +static int +ar8316_hw_init(struct ar8216_priv *priv) { + static int initialized; + int i; + u32 val; + struct mii_bus *bus; + + if (initialized) + return 0; + + val = priv->read(priv, 0x8); + + if (priv->phy->interface == PHY_INTERFACE_MODE_RGMII) { + /* value taken from Ubiquiti RouterStation Pro */ + if (val == 0x81461bea) { + /* switch already intialized by bootloader */ + initialized = true; + return 0; + } + priv->write(priv, 0x8, 0x81461bea); + } else if (priv->phy->interface == PHY_INTERFACE_MODE_GMII) { + /* value taken from AVM Fritz!Box 7390 sources */ + if (val == 0x010e5b71) { + /* switch already initialized by bootloader */ + initialized = true; + return 0; + } + priv->write(priv, 0x8, 0x010e5b71); + } else { + /* no known value for phy interface */ + printk(KERN_ERR "ar8316: unsupported mii mode: %d.\n", + priv->phy->interface); + return -EINVAL; + } + + /* standard atheros magic */ + priv->write(priv, 0x38, 0xc000050e); + + /* Initialize the ports */ + bus = priv->phy->bus; + for (i = 0; i < 5; i++) { + if ((i == 4) && + priv->phy->interface == PHY_INTERFACE_MODE_RGMII) { + /* work around for phy4 rgmii mode */ + bus->write(bus, i, MII_ATH_DBG_ADDR, 0x12); + bus->write(bus, i, MII_ATH_DBG_DATA, 0x480c); + /* rx delay */ + bus->write(bus, i, MII_ATH_DBG_ADDR, 0x0); + bus->write(bus, i, MII_ATH_DBG_DATA, 0x824e); + /* tx delay */ + bus->write(bus, i, MII_ATH_DBG_ADDR, 0x5); + bus->write(bus, i, MII_ATH_DBG_DATA, 0x3d47); + msleep(1000); + } + + /* initialize the port itself */ + bus->write(bus, i, MII_ADVERTISE, + ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); + bus->write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL); + bus->write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); + msleep(1000); + } + initialized = true; return 0; } @@ -359,9 +581,10 @@ ar8216_reset_switch(struct switch_dev *dev) struct ar8216_priv *priv = to_ar8216(dev); int i; + mutex_lock(&priv->reg_mutex); memset(&priv->vlan, 0, sizeof(struct ar8216_priv) - offsetof(struct ar8216_priv, vlan)); - for (i = 0; i < AR8216_NUM_VLANS; i++) { + for (i = 0; i < AR8X16_MAX_VLANS; i++) { priv->vlan_id[i] = i; } for (i = 0; i < AR8216_NUM_PORTS; i++) { @@ -376,9 +599,12 @@ ar8216_reset_switch(struct switch_dev *dev) if (i == AR8216_PORT_CPU) { priv->write(priv, AR8216_REG_PORT_STATUS(i), AR8216_PORT_STATUS_LINK_UP | - AR8216_PORT_STATUS_SPEED | + ((priv->chip == AR8316) ? + AR8216_PORT_SPEED_1000M : AR8216_PORT_SPEED_100M) | AR8216_PORT_STATUS_TXMAC | AR8216_PORT_STATUS_RXMAC | + ((priv->chip == AR8316) ? AR8216_PORT_STATUS_RXFLOW : 0) | + ((priv->chip == AR8316) ? AR8216_PORT_STATUS_TXFLOW : 0) | AR8216_PORT_STATUS_DUPLEX); } else { priv->write(priv, AR8216_REG_PORT_STATUS(i), @@ -387,6 +613,21 @@ ar8216_reset_switch(struct switch_dev *dev) } /* XXX: undocumented magic from atheros, required! */ priv->write(priv, 0x38, 0xc000050e); + + if (priv->chip == AR8216) { + ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, + AR8216_GCTRL_MTU, 1518 + 8 + 2); + } else if (priv->chip == AR8316) { + /* enable jumbo frames */ + ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, + AR8316_GCTRL_MTU, 9018 + 8 + 2); + } + + if (priv->chip == AR8316) { + /* enable cpu port to receive multicast and broadcast frames */ + priv->write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f); + } + mutex_unlock(&priv->reg_mutex); return ar8216_hw_apply(dev); } @@ -394,27 +635,80 @@ static int ar8216_config_init(struct phy_device *pdev) { struct ar8216_priv *priv; + struct net_device *dev = pdev->attached_dev; int ret; - printk("%s: AR8216 PHY driver attached.\n", pdev->attached_dev->name); - pdev->supported = ADVERTISED_100baseT_Full; - pdev->advertising = ADVERTISED_100baseT_Full; - priv = kzalloc(sizeof(struct ar8216_priv), GFP_KERNEL); if (priv == NULL) return -ENOMEM; priv->phy = pdev; + + priv->chip = ar8216_id_chip(priv); + + if (pdev->addr == 0) + printk(KERN_INFO "%s: AR%d switch driver attached.\n", + pdev->attached_dev->name, priv->chip); + + + if (pdev->addr != 0) { + if (priv->chip == AR8316) { + pdev->supported |= SUPPORTED_1000baseT_Full; + pdev->advertising |= ADVERTISED_1000baseT_Full; + } + kfree(priv); + return 0; + } + + pdev->supported = priv->chip == AR8316 ? + SUPPORTED_1000baseT_Full : SUPPORTED_100baseT_Full; + pdev->advertising = pdev->supported; + + mutex_init(&priv->reg_mutex); priv->read = ar8216_mii_read; priv->write = ar8216_mii_write; memcpy(&priv->dev, &athdev, sizeof(struct switch_dev)); pdev->priv = priv; + + if (priv->chip == AR8316) { + priv->dev.name = "Atheros AR8316"; + priv->dev.vlans = AR8X16_MAX_VLANS; + /* port 5 connected to the other mac, therefore unusable */ + priv->dev.ports = (AR8216_NUM_PORTS - 1); + } + if ((ret = register_switch(&priv->dev, pdev->attached_dev)) < 0) { kfree(priv); goto done; } + if (priv->chip == AR8316) { + ret = ar8316_hw_init(priv); + if (ret) { + kfree(priv); + goto done; + } + } + ret = ar8216_reset_switch(&priv->dev); + if (ret) { + kfree(priv); + goto done; + } + + dev->phy_ptr = priv; + + /* VID fixup only needed on ar8216 */ + if (pdev->addr == 0 && priv->chip == AR8216) { + pdev->pkt_align = 2; + pdev->netif_receive_skb = ar8216_netif_receive_skb; + pdev->netif_rx = ar8216_netif_rx; + priv->ndo_old = dev->netdev_ops; + memcpy(&priv->ndo, priv->ndo_old, sizeof(struct net_device_ops)); + priv->ndo.ndo_start_xmit = ar8216_mangle_tx; + dev->netdev_ops = &priv->ndo; + } + done: return ret; } @@ -423,39 +717,50 @@ static int ar8216_read_status(struct phy_device *phydev) { struct ar8216_priv *priv = phydev->priv; + int ret; + if (phydev->addr != 0) { + return genphy_read_status(phydev); + } - phydev->speed = SPEED_100; + phydev->speed = priv->chip == AR8316 ? SPEED_1000 : SPEED_100; phydev->duplex = DUPLEX_FULL; - phydev->state = PHY_UP; + phydev->link = 1; /* flush the address translation unit */ - if (ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0)) - return -ETIMEDOUT; + mutex_lock(&priv->reg_mutex); + ret = ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0); - priv->write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH); + if (!ret) + priv->write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH); + else + ret = -ETIMEDOUT; + mutex_unlock(&priv->reg_mutex); - return 0; + phydev->state = PHY_RUNNING; + netif_carrier_on(phydev->attached_dev); + phydev->adjust_link(phydev->attached_dev); + + return ret; } static int ar8216_config_aneg(struct phy_device *phydev) { - return 0; + if (phydev->addr == 0) + return 0; + + return genphy_config_aneg(phydev); } static int ar8216_probe(struct phy_device *pdev) { struct ar8216_priv priv; - - u8 id, rev; - u32 val; + u16 chip; priv.phy = pdev; - val = ar8216_mii_read(&priv, AR8216_REG_CTRL); - rev = val & 0xff; - id = (val >> 8) & 0xff; - if ((id != 1) || (rev != 1)) + chip = ar8216_id_chip(&priv); + if (chip == UNKNOWN) return -ENODEV; return 0; @@ -465,11 +770,15 @@ static void ar8216_remove(struct phy_device *pdev) { struct ar8216_priv *priv = pdev->priv; + struct net_device *dev = pdev->attached_dev; if (!priv) return; - unregister_switch(&priv->dev); + if (priv->ndo_old && dev) + dev->netdev_ops = priv->ndo_old; + if (pdev->addr == 0) + unregister_switch(&priv->dev); kfree(priv); } @@ -500,7 +809,9 @@ static struct switch_dev athdev = { }; static struct phy_driver ar8216_driver = { - .name = "Atheros AR8216", + .phy_id = 0x004d0000, + .name = "Atheros AR8216/AR8316", + .phy_id_mask = 0xffff0000, .features = PHY_BASIC_FEATURES, .probe = ar8216_probe, .remove = ar8216_remove,