X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fopenwrt.git;a=blobdiff_plain;f=target%2Flinux%2Fgeneric%2Ffiles%2Fdrivers%2Fnet%2Fphy%2Far8216.c;h=e1c3dc95c30eb0ece265775a18c5222760ee79f4;hp=7f3d5115ab0979081731ee1a2369250c12a90e36;hb=cc817392f4ec9c3c3cc04bd95952cf9222fd8ad1;hpb=4d8a66d9346373c2a7fcac5bdae3f662a9dbd9df diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c index 7f3d5115ab..e1c3dc95c3 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c @@ -23,12 +23,14 @@ #include #include #include +#include +#include +#include #include #include #include #include #include -#include #include #include #include @@ -49,12 +51,6 @@ extern const struct ar8xxx_chip ar8337_chip; .name = (_n), \ } -#define AR8216_MIB_RXB_ID 14 /* RxGoodByte */ -#define AR8216_MIB_TXB_ID 29 /* TxByte */ - -#define AR8236_MIB_RXB_ID 15 /* RxGoodByte */ -#define AR8236_MIB_TXB_ID 31 /* TxByte */ - static const struct ar8xxx_mib_desc ar8216_mibs[] = { MIB_DESC(1, AR8216_STATS_RXBROAD, "RxBroad"), MIB_DESC(1, AR8216_STATS_RXPAUSE, "RxPause"), @@ -194,7 +190,7 @@ ar8xxx_phy_init(struct ar8xxx_priv *priv) int i; struct mii_bus *bus; - bus = priv->mii_bus; + bus = priv->sw_mii_bus ?: priv->mii_bus; for (i = 0; i < AR8XXX_NUM_PHYS; i++) { if (priv->chip->phy_fixup) priv->chip->phy_fixup(priv, i); @@ -301,6 +297,17 @@ ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) return ret; } +void +ar8xxx_phy_dbg_read(struct ar8xxx_priv *priv, int phy_addr, + u16 dbg_addr, u16 *dbg_data) +{ + struct mii_bus *bus = priv->mii_bus; + + mutex_lock(&bus->mdio_lock); + bus->write(bus, phy_addr, MII_ATH_DBG_ADDR, dbg_addr); + *dbg_data = bus->read(bus, phy_addr, MII_ATH_DBG_DATA); + mutex_unlock(&bus->mdio_lock); +} void ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr, @@ -361,6 +368,7 @@ ar8xxx_reg_wait(struct ar8xxx_priv *priv, u32 reg, u32 mask, u32 val, return 0; usleep_range(1000, 2000); + cond_resched(); } return -ETIMEDOUT; @@ -432,6 +440,7 @@ ar8xxx_mib_fetch_port_stat(struct ar8xxx_priv *priv, int port, bool flush) mib_stats[i] = 0; else mib_stats[i] += t; + cond_resched(); } } @@ -571,6 +580,7 @@ ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) break; udelay(10); + cond_resched(); } pr_err("ar8216: timeout on reg %08x: %08x & %08x != %08x\n", @@ -706,7 +716,8 @@ ar8216_init_globals(struct ar8xxx_priv *priv) } static void -ar8216_init_port(struct ar8xxx_priv *priv, int port) +__ar8216_init_port(struct ar8xxx_priv *priv, int port, + bool cpu_ge, bool flow_en) { /* Enable port learning and tx */ ar8xxx_write(priv, AR8216_REG_PORT_CTRL(port), @@ -718,12 +729,11 @@ ar8216_init_port(struct ar8xxx_priv *priv, int port) if (port == AR8216_PORT_CPU) { ar8xxx_write(priv, AR8216_REG_PORT_STATUS(port), AR8216_PORT_STATUS_LINK_UP | - (ar8xxx_has_gige(priv) ? - AR8216_PORT_SPEED_1000M : AR8216_PORT_SPEED_100M) | + (cpu_ge ? AR8216_PORT_SPEED_1000M : AR8216_PORT_SPEED_100M) | AR8216_PORT_STATUS_TXMAC | AR8216_PORT_STATUS_RXMAC | - (chip_is_ar8316(priv) ? AR8216_PORT_STATUS_RXFLOW : 0) | - (chip_is_ar8316(priv) ? AR8216_PORT_STATUS_TXFLOW : 0) | + (flow_en ? AR8216_PORT_STATUS_RXFLOW : 0) | + (flow_en ? AR8216_PORT_STATUS_TXFLOW : 0) | AR8216_PORT_STATUS_DUPLEX); } else { ar8xxx_write(priv, AR8216_REG_PORT_STATUS(port), @@ -731,13 +741,22 @@ ar8216_init_port(struct ar8xxx_priv *priv, int port) } } +static void +ar8216_init_port(struct ar8xxx_priv *priv, int port) +{ + __ar8216_init_port(priv, port, ar8xxx_has_gige(priv), + chip_is_ar8316(priv)); +} + static void ar8216_wait_atu_ready(struct ar8xxx_priv *priv, u16 r2, u16 r1) { int timeout = 20; - while (ar8xxx_mii_read32(priv, r2, r1) & AR8216_ATU_ACTIVE && --timeout) - udelay(10); + while (ar8xxx_mii_read32(priv, r2, r1) & AR8216_ATU_ACTIVE && --timeout) { + udelay(10); + cond_resched(); + } if (!timeout) pr_err("ar8216: timeout waiting for atu to become ready\n"); @@ -750,7 +769,6 @@ static void ar8216_get_arl_entry(struct ar8xxx_priv *priv, u16 r2, page; u16 r1_func0, r1_func1, r1_func2; u32 t, val0, val1, val2; - int i; split_addr(AR8216_REG_ATU_FUNC0, &r1_func0, &r2, &page); r2 |= 0x10; @@ -786,12 +804,7 @@ static void ar8216_get_arl_entry(struct ar8xxx_priv *priv, if (!*status) break; - i = 0; - t = AR8216_ATU_PORT0; - while (!(val2 & t) && ++i < priv->dev.ports) - t <<= 1; - - a->port = i; + a->portmap = (val2 & AR8216_ATU_PORTS) >> AR8216_ATU_PORTS_S; a->mac[0] = (val0 & AR8216_ATU_ADDR5) >> AR8216_ATU_ADDR5_S; a->mac[1] = (val0 & AR8216_ATU_ADDR4) >> AR8216_ATU_ADDR4_S; a->mac[2] = (val1 & AR8216_ATU_ADDR3) >> AR8216_ATU_ADDR3_S; @@ -802,6 +815,85 @@ static void ar8216_get_arl_entry(struct ar8xxx_priv *priv, } } +static int +ar8229_hw_init(struct ar8xxx_priv *priv) +{ + int phy_if_mode; + + if (priv->initialized) + return 0; + + ar8xxx_write(priv, AR8216_REG_CTRL, AR8216_CTRL_RESET); + ar8xxx_reg_wait(priv, AR8216_REG_CTRL, AR8216_CTRL_RESET, 0, 1000); + + phy_if_mode = of_get_phy_mode(priv->pdev->of_node); + + if (phy_if_mode == PHY_INTERFACE_MODE_GMII) { + ar8xxx_write(priv, AR8229_REG_OPER_MODE0, + AR8229_OPER_MODE0_MAC_GMII_EN); + } else if (phy_if_mode == PHY_INTERFACE_MODE_MII) { + ar8xxx_write(priv, AR8229_REG_OPER_MODE0, + AR8229_OPER_MODE0_PHY_MII_EN); + } else { + pr_err("ar8229: unsupported mii mode\n"); + return -EINVAL; + } + + if (priv->port4_phy) + ar8xxx_write(priv, AR8229_REG_OPER_MODE1, + AR8229_REG_OPER_MODE1_PHY4_MII_EN); + + ar8xxx_phy_init(priv); + + priv->initialized = true; + return 0; +} + +static void +ar8229_init_globals(struct ar8xxx_priv *priv) +{ + + /* Enable CPU port, and disable mirror port */ + ar8xxx_write(priv, AR8216_REG_GLOBAL_CPUPORT, + AR8216_GLOBAL_CPUPORT_EN | + (15 << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S)); + + /* Setup TAG priority mapping */ + ar8xxx_write(priv, AR8216_REG_TAG_PRIORITY, 0xfa50); + + /* Enable aging, MAC replacing */ + ar8xxx_write(priv, AR8216_REG_ATU_CTRL, + 0x2b /* 5 min age time */ | + AR8216_ATU_CTRL_AGE_EN | + AR8216_ATU_CTRL_LEARN_CHANGE); + + /* Enable ARP frame acknowledge */ + ar8xxx_reg_set(priv, AR8229_REG_QM_CTRL, + AR8229_QM_CTRL_ARP_EN); + + /* Enable Broadcast/Multicast frames transmitted to the CPU */ + ar8xxx_reg_set(priv, AR8216_REG_FLOOD_MASK, + AR8229_FLOOD_MASK_BC_DP(0) | + AR8229_FLOOD_MASK_MC_DP(0)); + + /* setup MTU */ + ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL, + AR8236_GCTRL_MTU, AR8236_GCTRL_MTU); + + /* Enable MIB counters */ + ar8xxx_reg_set(priv, AR8216_REG_MIB_FUNC, + AR8236_MIB_EN); + + /* setup Service TAG */ + ar8xxx_rmw(priv, AR8216_REG_SERVICE_TAG, AR8216_SERVICE_TAG_M, 0); +} + +static void +ar8229_init_port(struct ar8xxx_priv *priv, int port) +{ + __ar8216_init_port(priv, port, true, true); +} + static void ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 members) { @@ -1517,8 +1609,12 @@ ar8xxx_sw_get_arl_table(struct switch_dev *dev, */ 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; + if (!memcmp(a->mac, a1->mac, sizeof(a->mac))) { + /* ignore ports already seen in former entry */ + a->portmap &= ~a1->portmap; + if (!a->portmap) + goto duplicate; + } } } @@ -1535,7 +1631,7 @@ ar8xxx_sw_get_arl_table(struct switch_dev *dev, for (j = 0; j < priv->dev.ports; ++j) { for (k = 0; k < i; ++k) { a = &priv->arl_table[k]; - if (a->port != j) + if (!(a->portmap & BIT(j))) continue; len += snprintf(buf + len, sizeof(priv->arl_buf) - len, "Port %d: MAC %02x:%02x:%02x:%02x:%02x:%02x\n", @@ -1587,54 +1683,19 @@ ar8xxx_sw_set_flush_port_arl_table(struct switch_dev *dev, return ret; } -int -ar8xxx_sw_get_port_stats(struct switch_dev *dev, int port, - struct switch_port_stats *stats) +static int +ar8xxx_phy_read(struct mii_bus *bus, int phy_addr, int reg_addr) { - struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); - u64 *mib_stats; - int ret; - int mib_txb_id, mib_rxb_id; - - if (!ar8xxx_has_mib_counters(priv)) - return -EOPNOTSUPP; - - if (port >= dev->ports) - return -EINVAL; - - switch (priv->chip_ver) { - case AR8XXX_VER_AR8216: - mib_txb_id = AR8216_MIB_TXB_ID; - mib_rxb_id = AR8216_MIB_RXB_ID; - break; - case AR8XXX_VER_AR8236: - case AR8XXX_VER_AR8316: - case AR8XXX_VER_AR8327: - case AR8XXX_VER_AR8337: - mib_txb_id = AR8236_MIB_TXB_ID; - mib_rxb_id = AR8236_MIB_RXB_ID; - break; - default: - return -EOPNOTSUPP; - } - - mutex_lock(&priv->mib_lock); - ret = ar8xxx_mib_capture(priv); - if (ret) - goto unlock; - - ar8xxx_mib_fetch_port_stat(priv, port, false); - - mib_stats = &priv->mib_stats[port * priv->chip->num_mibs]; - - stats->tx_bytes = mib_stats[mib_txb_id]; - stats->rx_bytes = mib_stats[mib_rxb_id]; - - ret = 0; + struct ar8xxx_priv *priv = bus->priv; + return priv->chip->phy_read(priv, phy_addr, reg_addr); +} -unlock: - mutex_unlock(&priv->mib_lock); - return ret; +static int +ar8xxx_phy_write(struct mii_bus *bus, int phy_addr, int reg_addr, + u16 reg_val) +{ + struct ar8xxx_priv *priv = bus->priv; + return priv->chip->phy_write(priv, phy_addr, reg_addr, reg_val); } static const struct switch_attr ar8xxx_sw_attr_globals[] = { @@ -1752,7 +1813,16 @@ static const struct switch_dev_ops ar8xxx_sw_ops = { .apply_config = ar8xxx_sw_hw_apply, .reset_switch = ar8xxx_sw_reset_switch, .get_port_link = ar8xxx_sw_get_port_link, +/* The following op is disabled as it hogs the CPU and degrades performance. + An implementation has been attempted in 4d8a66d but reading MIB data is slow + on ar8xxx switches. + + The high CPU load has been traced down to the ar8xxx_reg_wait() call in + ar8xxx_mib_op(), which has to usleep_range() till the MIB busy flag set by + the request to update the MIB counter is cleared. */ +#if 0 .get_port_stats = ar8xxx_sw_get_port_stats, +#endif }; static const struct ar8xxx_chip ar8216_chip = { @@ -1785,6 +1855,36 @@ static const struct ar8xxx_chip ar8216_chip = { .mib_func = AR8216_REG_MIB_FUNC }; +static const struct ar8xxx_chip ar8229_chip = { + .caps = AR8XXX_CAP_MIB_COUNTERS, + + .reg_port_stats_start = 0x20000, + .reg_port_stats_length = 0x100, + .reg_arl_ctrl = AR8216_REG_ATU_CTRL, + + .name = "Atheros AR8229", + .ports = AR8216_NUM_PORTS, + .vlans = AR8216_NUM_VLANS, + .swops = &ar8xxx_sw_ops, + + .hw_init = ar8229_hw_init, + .init_globals = ar8229_init_globals, + .init_port = ar8229_init_port, + .setup_port = ar8236_setup_port, + .read_port_status = ar8216_read_port_status, + .atu_flush = ar8216_atu_flush, + .atu_flush_port = ar8216_atu_flush_port, + .vtu_flush = ar8216_vtu_flush, + .vtu_load_vlan = ar8216_vtu_load_vlan, + .set_mirror_regs = ar8216_set_mirror_regs, + .get_arl_entry = ar8216_get_arl_entry, + .sw_hw_apply = ar8xxx_sw_hw_apply, + + .num_mibs = ARRAY_SIZE(ar8236_mibs), + .mib_decs = ar8236_mibs, + .mib_func = AR8216_REG_MIB_FUNC +}; + static const struct ar8xxx_chip ar8236_chip = { .caps = AR8XXX_CAP_MIB_COUNTERS, @@ -1846,7 +1946,7 @@ static const struct ar8xxx_chip ar8316_chip = { }; static int -ar8xxx_id_chip(struct ar8xxx_priv *priv) +ar8xxx_read_id(struct ar8xxx_priv *priv) { u32 val; u16 id; @@ -1871,6 +1971,17 @@ ar8xxx_id_chip(struct ar8xxx_priv *priv) priv->chip_ver = (id & AR8216_CTRL_VERSION) >> AR8216_CTRL_VERSION_S; priv->chip_rev = (id & AR8216_CTRL_REVISION); + return 0; +} + +static int +ar8xxx_id_chip(struct ar8xxx_priv *priv) +{ + int ret; + + ret = ar8xxx_read_id(priv); + if(ret) + return ret; switch (priv->chip_ver) { case AR8XXX_VER_AR8216: @@ -1997,10 +2108,6 @@ ar8xxx_probe_switch(struct ar8xxx_priv *priv) struct switch_dev *swdev; int ret; - ret = ar8xxx_id_chip(priv); - if (ret) - return ret; - chip = priv->chip; swdev = &priv->dev; @@ -2146,7 +2253,8 @@ ar8xxx_phy_read_status(struct phy_device *phydev) phydev->state = PHY_RUNNING; netif_carrier_on(phydev->attached_dev); - phydev->adjust_link(phydev->attached_dev); + if (phydev->adjust_link) + phydev->adjust_link(phydev->attached_dev); return 0; } @@ -2209,7 +2317,7 @@ ar8xxx_phy_probe(struct phy_device *phydev) int ret; /* skip PHYs at unused adresses */ - if (phydev->mdio.addr != 0 && phydev->mdio.addr != 4) + if (phydev->mdio.addr != 0 && phydev->mdio.addr != 3 && phydev->mdio.addr != 4) return -ENODEV; if (!ar8xxx_is_possible(phydev->mdio.bus)) @@ -2227,6 +2335,11 @@ ar8xxx_phy_probe(struct phy_device *phydev) } priv->mii_bus = phydev->mdio.bus; + priv->pdev = &phydev->mdio.dev; + + ret = ar8xxx_id_chip(priv); + if (ret) + goto free_priv; ret = ar8xxx_probe_switch(priv); if (ret) @@ -2268,6 +2381,8 @@ found: phydev->supported |= SUPPORTED_1000baseT_Full; phydev->advertising |= ADVERTISED_1000baseT_Full; } + if (priv->chip->phy_rgmii_set) + priv->chip->phy_rgmii_set(priv, phydev); } phydev->priv = priv; @@ -2351,5 +2466,158 @@ static struct phy_driver ar8xxx_phy_driver[] = { } }; -module_phy_driver(ar8xxx_phy_driver); +static const struct of_device_id ar8xxx_mdiodev_of_match[] = { + { + .compatible = "qca,ar8229", + .data = &ar8229_chip, + }, { + .compatible = "qca,ar8236", + .data = &ar8236_chip, + }, { + .compatible = "qca,ar8327", + .data = &ar8327_chip, + }, + { /* sentinel */ }, +}; + +static int +ar8xxx_mdiodev_probe(struct mdio_device *mdiodev) +{ + const struct of_device_id *match; + struct ar8xxx_priv *priv; + struct switch_dev *swdev; + struct device_node *mdio_node; + int ret; + + match = of_match_device(ar8xxx_mdiodev_of_match, &mdiodev->dev); + if (!match) + return -EINVAL; + + priv = ar8xxx_create(); + if (priv == NULL) + return -ENOMEM; + + priv->mii_bus = mdiodev->bus; + priv->pdev = &mdiodev->dev; + priv->chip = (const struct ar8xxx_chip *) match->data; + + ret = ar8xxx_read_id(priv); + if (ret) + goto free_priv; + + ret = ar8xxx_probe_switch(priv); + if (ret) + goto free_priv; + + if (priv->chip->phy_read && priv->chip->phy_write) { + priv->sw_mii_bus = devm_mdiobus_alloc(&mdiodev->dev); + priv->sw_mii_bus->name = "ar8xxx-mdio"; + priv->sw_mii_bus->read = ar8xxx_phy_read; + priv->sw_mii_bus->write = ar8xxx_phy_write; + priv->sw_mii_bus->priv = priv; + priv->sw_mii_bus->parent = &mdiodev->dev; + snprintf(priv->sw_mii_bus->id, MII_BUS_ID_SIZE, "%s", + dev_name(&mdiodev->dev)); + mdio_node = of_get_child_by_name(priv->pdev->of_node, "mdio-bus"); + ret = of_mdiobus_register(priv->sw_mii_bus, mdio_node); + if (ret) + goto free_priv; + } + + swdev = &priv->dev; + swdev->alias = dev_name(&mdiodev->dev); + ret = register_switch(swdev, NULL); + if (ret) + goto free_priv; + + pr_info("%s: %s rev. %u switch registered on %s\n", + swdev->devname, swdev->name, priv->chip_rev, + dev_name(&priv->mii_bus->dev)); + + mutex_lock(&ar8xxx_dev_list_lock); + list_add(&priv->list, &ar8xxx_dev_list); + mutex_unlock(&ar8xxx_dev_list_lock); + + priv->use_count++; + + ret = ar8xxx_start(priv); + if (ret) + goto err_unregister_switch; + + dev_set_drvdata(&mdiodev->dev, priv); + + return 0; + +err_unregister_switch: + if (--priv->use_count) + return ret; + + unregister_switch(&priv->dev); + +free_priv: + ar8xxx_free(priv); + return ret; +} + +static void +ar8xxx_mdiodev_remove(struct mdio_device *mdiodev) +{ + struct ar8xxx_priv *priv = dev_get_drvdata(&mdiodev->dev); + + if (WARN_ON(!priv)) + return; + + mutex_lock(&ar8xxx_dev_list_lock); + + if (--priv->use_count > 0) { + mutex_unlock(&ar8xxx_dev_list_lock); + return; + } + + list_del(&priv->list); + mutex_unlock(&ar8xxx_dev_list_lock); + + unregister_switch(&priv->dev); + ar8xxx_mib_stop(priv); + if(priv->sw_mii_bus) + mdiobus_unregister(priv->sw_mii_bus); + ar8xxx_free(priv); +} + +static struct mdio_driver ar8xxx_mdio_driver = { + .probe = ar8xxx_mdiodev_probe, + .remove = ar8xxx_mdiodev_remove, + .mdiodrv.driver = { + .name = "ar8xxx-switch", + .of_match_table = ar8xxx_mdiodev_of_match, + }, +}; + +static int __init ar8216_init(void) +{ + int ret; + + ret = phy_drivers_register(ar8xxx_phy_driver, + ARRAY_SIZE(ar8xxx_phy_driver), + THIS_MODULE); + if (ret) + return ret; + + ret = mdio_driver_register(&ar8xxx_mdio_driver); + if (ret) + phy_drivers_unregister(ar8xxx_phy_driver, + ARRAY_SIZE(ar8xxx_phy_driver)); + + return ret; +} +module_init(ar8216_init); + +static void __exit ar8216_exit(void) +{ + mdio_driver_unregister(&ar8xxx_mdio_driver); + phy_drivers_unregister(ar8xxx_phy_driver, + ARRAY_SIZE(ar8xxx_phy_driver)); +} +module_exit(ar8216_exit); + MODULE_LICENSE("GPL");