X-Git-Url: https://git.archive.openwrt.org/?a=blobdiff_plain;f=target%2Flinux%2Fgeneric%2Ffiles%2Fdrivers%2Fnet%2Fphy%2Far8216.c;h=d514ec724f6a21b9f23b1e598ace52eac7587d9e;hb=c097ab2bbd8d45e3cc0075cf27dca6f76cb5efe5;hp=8e84e9d70a143fdc70d05709f9f5459a4f3a4944;hpb=1130aa078f6e1a877345d6e78fcd1a97e75a6a0c;p=openwrt.git diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c index 8e84e9d70a..d514ec724f 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c @@ -29,12 +29,25 @@ #include #include #include +#include #include "ar8216.h" /* size of the vlan table */ #define AR8X16_MAX_VLANS 128 #define AR8X16_PROBE_RETRIES 10 +struct ar8216_priv; + +struct ar8xxx_chip { + int (*hw_init)(struct ar8216_priv *priv); + void (*init_port)(struct ar8216_priv *priv, int port); + void (*setup_port)(struct ar8216_priv *priv, int port, u32 egress, + u32 ingress, u32 members, u32 pvid); + int (*atu_flush)(struct ar8216_priv *priv); + void (*vtu_flush)(struct ar8216_priv *priv); + void (*vtu_load_vlan)(struct ar8216_priv *priv, u32 vid, u32 port_mask); +}; + struct ar8216_priv { struct switch_dev dev; struct phy_device *phy; @@ -43,7 +56,8 @@ struct ar8216_priv { const struct net_device_ops *ndo_old; struct net_device_ops ndo; struct mutex reg_mutex; - int chip; + int chip_type; + const struct ar8xxx_chip *chip; bool initialized; bool port4_phy; char buf[80]; @@ -86,7 +100,7 @@ ar8216_mii_read(struct ar8216_priv *priv, int reg) mutex_lock(&bus->mdio_lock); bus->write(bus, 0x18, 0, page); - msleep(1); /* wait for the page switch to propagate */ + usleep_range(1000, 2000); /* wait for the page switch to propagate */ lo = bus->read(bus, 0x10 | r2, r1); hi = bus->read(bus, 0x10 | r2, r1 + 1); @@ -110,18 +124,32 @@ ar8216_mii_write(struct ar8216_priv *priv, int reg, u32 val) mutex_lock(&bus->mdio_lock); bus->write(bus, 0x18, 0, r3); - msleep(1); /* wait for the page switch to propagate */ + usleep_range(1000, 2000); /* wait for the page switch to propagate */ bus->write(bus, 0x10 | r2, r1 + 1, hi); bus->write(bus, 0x10 | r2, r1, lo); mutex_unlock(&bus->mdio_lock); } +static void +ar8216_phy_dbg_write(struct ar8216_priv *priv, int phy_addr, + u16 dbg_addr, u16 dbg_data) +{ + struct mii_bus *bus = priv->phy->bus; + + mutex_lock(&bus->mdio_lock); + bus->write(bus, phy_addr, MII_ATH_DBG_ADDR, dbg_addr); + bus->write(bus, phy_addr, MII_ATH_DBG_DATA, dbg_data); + mutex_unlock(&bus->mdio_lock); +} + static u32 ar8216_rmw(struct ar8216_priv *priv, int reg, u32 mask, u32 val) { u32 v; + lockdep_assert_held(&priv->reg_mutex); + v = priv->read(priv, reg); v &= ~mask; v |= val; @@ -130,50 +158,6 @@ 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 0x0301: - return AR8236; - case 0x1000: - 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), - mdiobus_read(priv->phy->bus, priv->phy->addr, 2), - mdiobus_read(priv->phy->bus, priv->phy->addr, 3)); - - return UNKNOWN; - } -} - static void ar8216_read_port_link(struct ar8216_priv *priv, int port, struct switch_port_link *link) @@ -219,7 +203,7 @@ ar8216_read_port_link(struct ar8216_priv *priv, int port, static int ar8216_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, - struct switch_val *val) + struct switch_val *val) { struct ar8216_priv *priv = to_ar8216(dev); priv->vlan = !!val->value.i; @@ -228,7 +212,7 @@ ar8216_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, static int ar8216_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, - struct switch_val *val) + struct switch_val *val) { struct ar8216_priv *priv = to_ar8216(dev); val->value.i = priv->vlan; @@ -260,7 +244,7 @@ ar8216_get_pvid(struct switch_dev *dev, int port, int *vlan) static int ar8216_set_vid(struct switch_dev *dev, const struct switch_attr *attr, - struct switch_val *val) + struct switch_val *val) { struct ar8216_priv *priv = to_ar8216(dev); priv->vlan_id[val->port_vlan] = val->value.i; @@ -269,7 +253,7 @@ ar8216_set_vid(struct switch_dev *dev, const struct switch_attr *attr, static int ar8216_get_vid(struct switch_dev *dev, const struct switch_attr *attr, - struct switch_val *val) + struct switch_val *val) { struct ar8216_priv *priv = to_ar8216(dev); val->value.i = priv->vlan_id[val->port_vlan]; @@ -292,8 +276,8 @@ 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 (unlikely(!priv)) + goto error; if (!priv->vlan) goto send; @@ -444,9 +428,9 @@ ar8216_set_ports(struct switch_dev *dev, struct switch_val *val) for (i = 0; i < val->len; i++) { struct switch_port *p = &val->value.ports[i]; - if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) + if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) { priv->vlan_tagged |= (1 << p->id); - else { + } else { priv->vlan_tagged &= ~(1 << p->id); priv->pvid[p->id] = val->port_vlan; @@ -501,12 +485,39 @@ ar8216_vtu_op(struct ar8216_priv *priv, u32 op, u32 val) } static void +ar8216_vtu_flush(struct ar8216_priv *priv) +{ + ar8216_vtu_op(priv, AR8216_VTU_OP_FLUSH, 0); +} + +static void +ar8216_vtu_load_vlan(struct ar8216_priv *priv, u32 vid, u32 port_mask) +{ + u32 op; + + op = AR8216_VTU_OP_LOAD | (vid << AR8216_VTU_VID_S); + ar8216_vtu_op(priv, op, port_mask); +} + +static int +ar8216_atu_flush(struct ar8216_priv *priv) +{ + int ret; + + ret = ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0); + if (!ret) + priv->write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH); + + return ret; +} + +static void ar8216_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress, u32 members, u32 pvid) { u32 header; - if (priv->vlan && port == AR8216_PORT_CPU && priv->chip == AR8216) + if (priv->vlan && port == AR8216_PORT_CPU && priv->chip_type == AR8216) header = AR8216_PORT_CTRL_HEADER; else header = 0; @@ -559,7 +570,7 @@ ar8216_hw_apply(struct switch_dev *dev) mutex_lock(&priv->reg_mutex); /* flush all vlan translation unit entries */ - ar8216_vtu_op(priv, AR8216_VTU_OP_FLUSH, 0); + priv->chip->vtu_flush(priv); memset(portmask, 0, sizeof(portmask)); if (!priv->init) { @@ -577,10 +588,8 @@ ar8216_hw_apply(struct switch_dev *dev) portmask[i] |= vp & ~mask; } - ar8216_vtu_op(priv, - AR8216_VTU_OP_LOAD | - (priv->vlan_id[j] << AR8216_VTU_VID_S), - priv->vlan_table[j]); + priv->chip->vtu_load_vlan(priv, priv->vlan_id[j], + priv->vlan_table[j]); } } else { /* vlan disabled: @@ -601,42 +610,37 @@ ar8216_hw_apply(struct switch_dev *dev) if (priv->vlan) { pvid = priv->vlan_id[priv->pvid[i]]; - } else { - pvid = i; - } - - if (priv->vlan) { if (priv->vlan_tagged & (1 << i)) egress = AR8216_OUT_ADD_VLAN; else egress = AR8216_OUT_STRIP_VLAN; - } else { - egress = AR8216_OUT_KEEP; - } - if (priv->vlan) { ingress = AR8216_IN_SECURE; } else { + pvid = i; + egress = AR8216_OUT_KEEP; ingress = AR8216_IN_PORT_ONLY; } - if (priv->chip == AR8236) - ar8236_setup_port(priv, i, egress, ingress, portmask[i], - pvid); - else - ar8216_setup_port(priv, i, egress, ingress, portmask[i], - pvid); + priv->chip->setup_port(priv, i, egress, ingress, portmask[i], + pvid); } mutex_unlock(&priv->reg_mutex); return 0; } static int -ar8236_hw_init(struct ar8216_priv *priv) { - static int initialized; +ar8216_hw_init(struct ar8216_priv *priv) +{ + return 0; +} + +static int +ar8236_hw_init(struct ar8216_priv *priv) +{ int i; struct mii_bus *bus; - if (initialized) + if (priv->initialized) return 0; /* Initialize the PHYs */ @@ -649,12 +653,13 @@ ar8236_hw_init(struct ar8216_priv *priv) { } msleep(1000); - initialized = true; + priv->initialized = true; return 0; } static int -ar8316_hw_init(struct ar8216_priv *priv) { +ar8316_hw_init(struct ar8216_priv *priv) +{ int i; u32 val, newval; struct mii_bus *bus; @@ -685,23 +690,17 @@ ar8316_hw_init(struct ar8216_priv *priv) { priv->write(priv, 0x8, newval); - /* 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->port4_phy && priv->phy->interface == PHY_INTERFACE_MODE_RGMII) { /* work around for phy4 rgmii mode */ - mdiobus_write(bus, i, MII_ATH_DBG_ADDR, 0x12); - mdiobus_write(bus, i, MII_ATH_DBG_DATA, 0x480c); + ar8216_phy_dbg_write(priv, i, 0x12, 0x480c); /* rx delay */ - mdiobus_write(bus, i, MII_ATH_DBG_ADDR, 0x0); - mdiobus_write(bus, i, MII_ATH_DBG_DATA, 0x824e); + ar8216_phy_dbg_write(priv, i, 0x0, 0x824e); /* tx delay */ - mdiobus_write(bus, i, MII_ATH_DBG_ADDR, 0x5); - mdiobus_write(bus, i, MII_ATH_DBG_DATA, 0x3d47); + ar8216_phy_dbg_write(priv, i, 0x5, 0x3d47); msleep(1000); } @@ -719,6 +718,33 @@ out: } static void +ar8216_init_globals(struct ar8216_priv *priv) +{ + switch (priv->chip_type) { + case AR8216: + /* standard atheros magic */ + priv->write(priv, 0x38, 0xc000050e); + + ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, + AR8216_GCTRL_MTU, 1518 + 8 + 2); + break; + case AR8316: + /* standard atheros magic */ + priv->write(priv, 0x38, 0xc000050e); + + /* enable cpu port to receive multicast and broadcast frames */ + priv->write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f); + + /* fall through */ + case AR8236: + /* enable jumbo frames */ + ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, + AR8316_GCTRL_MTU, 9018 + 8 + 2); + break; + } +} + +static void ar8216_init_port(struct ar8216_priv *priv, int port) { /* Enable port learning and tx */ @@ -731,12 +757,12 @@ ar8216_init_port(struct ar8216_priv *priv, int port) if (port == AR8216_PORT_CPU) { priv->write(priv, AR8216_REG_PORT_STATUS(port), AR8216_PORT_STATUS_LINK_UP | - ((priv->chip == AR8316) ? + ((priv->chip_type == 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) | + ((priv->chip_type == AR8316) ? AR8216_PORT_STATUS_RXFLOW : 0) | + ((priv->chip_type == AR8316) ? AR8216_PORT_STATUS_TXFLOW : 0) | AR8216_PORT_STATUS_DUPLEX); } else { priv->write(priv, AR8216_REG_PORT_STATUS(port), @@ -744,6 +770,33 @@ ar8216_init_port(struct ar8216_priv *priv, int port) } } +static const struct ar8xxx_chip ar8216_chip = { + .hw_init = ar8216_hw_init, + .init_port = ar8216_init_port, + .setup_port = ar8216_setup_port, + .atu_flush = ar8216_atu_flush, + .vtu_flush = ar8216_vtu_flush, + .vtu_load_vlan = ar8216_vtu_load_vlan, +}; + +static const struct ar8xxx_chip ar8236_chip = { + .hw_init = ar8236_hw_init, + .init_port = ar8216_init_port, + .setup_port = ar8236_setup_port, + .atu_flush = ar8216_atu_flush, + .vtu_flush = ar8216_vtu_flush, + .vtu_load_vlan = ar8216_vtu_load_vlan, +}; + +static const struct ar8xxx_chip ar8316_chip = { + .hw_init = ar8316_hw_init, + .init_port = ar8216_init_port, + .setup_port = ar8216_setup_port, + .atu_flush = ar8216_atu_flush, + .vtu_flush = ar8216_vtu_flush, + .vtu_load_vlan = ar8216_vtu_load_vlan, +}; + static int ar8216_reset_switch(struct switch_dev *dev) { @@ -753,37 +806,21 @@ ar8216_reset_switch(struct switch_dev *dev) mutex_lock(&priv->reg_mutex); memset(&priv->vlan, 0, sizeof(struct ar8216_priv) - offsetof(struct ar8216_priv, vlan)); - for (i = 0; i < AR8X16_MAX_VLANS; i++) { + + for (i = 0; i < AR8X16_MAX_VLANS; i++) priv->vlan_id[i] = i; - } /* Configure all ports */ for (i = 0; i < AR8216_NUM_PORTS; i++) - ar8216_init_port(priv, i); - - /* 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 || - priv->chip == AR8236) { - /* enable jumbo frames */ - ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, - AR8316_GCTRL_MTU, 9018 + 8 + 2); - } + priv->chip->init_port(priv, i); - if (priv->chip == AR8316) { - /* enable cpu port to receive multicast and broadcast frames */ - priv->write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f); - } + ar8216_init_globals(priv); mutex_unlock(&priv->reg_mutex); + return ar8216_hw_apply(dev); } - -static const struct switch_dev_ops ar8216_ops = { +static const struct switch_dev_ops ar8216_sw_ops = { .attr_global = { .attr = ar8216_globals, .n_attr = ARRAY_SIZE(ar8216_globals), @@ -806,6 +843,60 @@ static const struct switch_dev_ops ar8216_ops = { }; static int +ar8216_id_chip(struct ar8216_priv *priv) +{ + u32 val; + u16 id; + int i; + + priv->chip_type = UNKNOWN; + + val = ar8216_mii_read(priv, AR8216_REG_CTRL); + if (val == ~0) + return -ENODEV; + + 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 -ENODEV; + + t = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION); + if (t != id) + return -ENODEV; + } + + switch (id) { + case 0x0101: + priv->chip_type = AR8216; + priv->chip = &ar8216_chip; + break; + case 0x0301: + priv->chip_type = AR8236; + priv->chip = &ar8236_chip; + break; + case 0x1000: + case 0x1001: + priv->chip_type = AR8316; + priv->chip = &ar8316_chip; + break; + 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), + mdiobus_read(priv->phy->bus, priv->phy->addr, 2), + mdiobus_read(priv->phy->bus, priv->phy->addr, 3)); + + return -ENODEV; + } + + return 0; +} + +static int ar8216_config_init(struct phy_device *pdev) { struct ar8216_priv *priv = pdev->priv; @@ -821,10 +912,12 @@ ar8216_config_init(struct phy_device *pdev) priv->phy = pdev; - priv->chip = ar8216_id_chip(priv); + ret = ar8216_id_chip(priv); + if (ret) + goto err_free_priv; if (pdev->addr != 0) { - if (priv->chip == AR8316) { + if (priv->chip_type == AR8316) { pdev->supported |= SUPPORTED_1000baseT_Full; pdev->advertising |= ADVERTISED_1000baseT_Full; @@ -858,9 +951,9 @@ ar8216_config_init(struct phy_device *pdev) } printk(KERN_INFO "%s: AR%d switch driver attached.\n", - pdev->attached_dev->name, priv->chip); + pdev->attached_dev->name, priv->chip_type); - pdev->supported = priv->chip == AR8316 ? + pdev->supported = priv->chip_type == AR8316 ? SUPPORTED_1000baseT_Full : SUPPORTED_100baseT_Full; pdev->advertising = pdev->supported; @@ -872,10 +965,10 @@ ar8216_config_init(struct phy_device *pdev) swdev = &priv->dev; swdev->cpu_port = AR8216_PORT_CPU; - swdev->ops = &ar8216_ops; + swdev->ops = &ar8216_sw_ops; swdev->ports = AR8216_NUM_PORTS; - if (priv->chip == AR8316) { + if (priv->chip_type == AR8316) { swdev->name = "Atheros AR8316"; swdev->vlans = AR8X16_MAX_VLANS; @@ -883,7 +976,7 @@ ar8216_config_init(struct phy_device *pdev) /* port 5 connected to the other mac, therefore unusable */ swdev->ports = (AR8216_NUM_PORTS - 1); } - } else if (priv->chip == AR8236) { + } else if (priv->chip_type == AR8236) { swdev->name = "Atheros AR8236"; swdev->vlans = AR8216_NUM_VLANS; swdev->ports = AR8216_NUM_PORTS; @@ -898,12 +991,7 @@ ar8216_config_init(struct phy_device *pdev) priv->init = true; - ret = 0; - if (priv->chip == AR8236) - ret = ar8236_hw_init(priv); - else if (priv->chip == AR8316) - ret = ar8316_hw_init(priv); - + ret = priv->chip->hw_init(priv); if (ret) goto err_free_priv; @@ -914,7 +1002,7 @@ ar8216_config_init(struct phy_device *pdev) dev->phy_ptr = priv; /* VID fixup only needed on ar8216 */ - if (pdev->addr == 0 && priv->chip == AR8216) { + if (pdev->addr == 0 && priv->chip_type == AR8216) { pdev->pkt_align = 2; pdev->netif_receive_skb = ar8216_netif_receive_skb; pdev->netif_rx = ar8216_netif_rx; @@ -939,9 +1027,9 @@ ar8216_read_status(struct phy_device *phydev) struct ar8216_priv *priv = phydev->priv; struct switch_port_link link; int ret; - if (phydev->addr != 0) { + + if (phydev->addr != 0) return genphy_read_status(phydev); - } ar8216_read_port_link(priv, phydev->addr, &link); phydev->link = !!link.link; @@ -950,13 +1038,13 @@ ar8216_read_status(struct phy_device *phydev) switch (link.speed) { case SWITCH_PORT_SPEED_10: - phydev->speed = SPEED_10; + phydev->speed = SPEED_10; break; case SWITCH_PORT_SPEED_100: - phydev->speed = SPEED_100; + phydev->speed = SPEED_100; break; case SWITCH_PORT_SPEED_1000: - phydev->speed = SPEED_1000; + phydev->speed = SPEED_1000; break; default: phydev->speed = 0; @@ -965,9 +1053,7 @@ ar8216_read_status(struct phy_device *phydev) /* flush the address translation unit */ 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); + ret = priv->chip->atu_flush(priv); mutex_unlock(&priv->reg_mutex); phydev->state = PHY_RUNNING; @@ -990,14 +1076,9 @@ static int ar8216_probe(struct phy_device *pdev) { struct ar8216_priv priv; - u16 chip; priv.phy = pdev; - chip = ar8216_id_chip(&priv); - if (chip == UNKNOWN) - return -ENODEV; - - return 0; + return ar8216_id_chip(&priv); } static void