X-Git-Url: http://git.rohieb.name/openwrt.git/blobdiff_plain/8bdac63e6f074730f550a6659e62fc4b81f9c143..714e378fed66a8aaa76b935f1039522cfdb99771:/target/linux/generic-2.6/files/drivers/net/phy/ar8216.c 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 93e4b33d9..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 @@ -31,6 +31,9 @@ #include #include "ar8216.h" +/* size of the vlan table */ +#define AR8X16_MAX_VLANS 128 +#define AR8X16_PROBE_RETRIES 10 struct ar8216_priv { struct switch_dev dev; @@ -39,11 +42,13 @@ struct ar8216_priv { 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]; }; @@ -110,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) @@ -133,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; } @@ -228,7 +280,7 @@ ar8216_mangle_rx(struct sk_buff *skb, int napi) goto recv; /* lookup port vid from local table, the switch passes an invalid vlan id */ - vlan = priv->pvid[port]; + vlan = priv->vlan_id[priv->pvid[port]]; buf[14 + 2] &= 0xf0; buf[14 + 2] |= vlan >> 8; @@ -245,7 +297,7 @@ recv: error: /* no vlan? eat the packet! */ dev_kfree_skb_any(skb); - return 0; + return NET_RX_DROP; } static int @@ -282,7 +334,7 @@ static struct switch_attr ar8216_vlan[] = { .description = "VLAN ID", .set = ar8216_set_vid, .get = ar8216_get_vid, - .max = 4095, + .max = 4094, }, }; @@ -330,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); @@ -377,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); @@ -384,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) @@ -396,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), @@ -432,14 +482,18 @@ ar8216_hw_apply(struct switch_dev *dev) } else { egress = AR8216_OUT_STRIP_VLAN; } - ingress = AR8216_IN_SECURE; + if (priv->vlan) { + ingress = AR8216_IN_SECURE; + } else { + ingress = AR8216_IN_PORT_ONLY; + } 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->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)); @@ -451,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; } @@ -461,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++) { @@ -478,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), @@ -490,9 +614,20 @@ ar8216_reset_switch(struct switch_dev *dev) /* XXX: undocumented magic from atheros, required! */ priv->write(priv, 0x38, 0xc000050e); - ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, - AR8216_GCTRL_MTU, 1518 + 8 + 2); + 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); } @@ -503,37 +638,76 @@ ar8216_config_init(struct phy_device *pdev) 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) + if (ret) { + kfree(priv); goto done; + } dev->phy_ptr = priv; - 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; + /* 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; @@ -543,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; @@ -592,7 +777,8 @@ ar8216_remove(struct phy_device *pdev) if (priv->ndo_old && dev) dev->netdev_ops = priv->ndo_old; - unregister_switch(&priv->dev); + if (pdev->addr == 0) + unregister_switch(&priv->dev); kfree(priv); } @@ -623,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,