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=a2c42d18e6c3570ff39132cb7e1299d52c391c53;hp=e1c3dc95c30eb0ece265775a18c5222760ee79f4;hb=47eef3a5ce981c6754836c8c996b4cc724617604;hpb=cc817392f4ec9c3c3cc04bd95952cf9222fd8ad1 diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c index e1c3dc95c3..a2c42d18e6 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c @@ -653,7 +653,8 @@ ar8216_read_port_status(struct ar8xxx_priv *priv, int port) } static void -ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members) +__ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members, + bool ath_hdr_en) { u32 header; u32 egress, ingress; @@ -672,10 +673,7 @@ ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members) ingress = AR8216_IN_PORT_ONLY; } - if (chip_is_ar8216(priv) && priv->vlan && port == AR8216_PORT_CPU) - header = AR8216_PORT_CTRL_HEADER; - else - header = 0; + header = ath_hdr_en ? AR8216_PORT_CTRL_HEADER : 0; ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port), AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE | @@ -693,12 +691,23 @@ ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members) (pvid << AR8216_PORT_VLAN_DEFAULT_ID_S)); } +static void +ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members) +{ + return __ar8216_setup_port(priv, port, members, + chip_is_ar8216(priv) && priv->vlan && + port == AR8216_PORT_CPU); +} + static int ar8216_hw_init(struct ar8xxx_priv *priv) { 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); + ar8xxx_phy_init(priv); priv->initialized = true; @@ -815,6 +824,52 @@ static void ar8216_get_arl_entry(struct ar8xxx_priv *priv, } } +static int +ar8216_phy_read(struct ar8xxx_priv *priv, int addr, int regnum) +{ + u32 t, val = 0xffff; + int err; + + if (addr >= AR8216_NUM_PORTS) + return 0xffff; + t = (regnum << AR8216_MDIO_CTRL_REG_ADDR_S) | + (addr << AR8216_MDIO_CTRL_PHY_ADDR_S) | + AR8216_MDIO_CTRL_MASTER_EN | + AR8216_MDIO_CTRL_BUSY | + AR8216_MDIO_CTRL_CMD_READ; + + ar8xxx_write(priv, AR8216_REG_MDIO_CTRL, t); + err = ar8xxx_reg_wait(priv, AR8216_REG_MDIO_CTRL, + AR8216_MDIO_CTRL_BUSY, 0, 5); + if (!err) + val = ar8xxx_read(priv, AR8216_REG_MDIO_CTRL); + + return val & AR8216_MDIO_CTRL_DATA_M; +} + +static int +ar8216_phy_write(struct ar8xxx_priv *priv, int addr, int regnum, u16 val) +{ + u32 t; + int ret; + + if (addr >= AR8216_NUM_PORTS) + return -EINVAL; + + t = (addr << AR8216_MDIO_CTRL_PHY_ADDR_S) | + (regnum << AR8216_MDIO_CTRL_REG_ADDR_S) | + AR8216_MDIO_CTRL_MASTER_EN | + AR8216_MDIO_CTRL_BUSY | + AR8216_MDIO_CTRL_CMD_WRITE | + val; + + ar8xxx_write(priv, AR8216_REG_MDIO_CTRL, t); + ret = ar8xxx_reg_wait(priv, AR8216_REG_MDIO_CTRL, + AR8216_MDIO_CTRL_BUSY, 0, 5); + + return ret; +} + static int ar8229_hw_init(struct ar8xxx_priv *priv) { @@ -839,9 +894,12 @@ ar8229_hw_init(struct ar8xxx_priv *priv) return -EINVAL; } - if (priv->port4_phy) + if (priv->port4_phy) { ar8xxx_write(priv, AR8229_REG_OPER_MODE1, AR8229_REG_OPER_MODE1_PHY4_MII_EN); + /* disable port5 to prevent mii conflict */ + ar8xxx_write(priv, AR8216_REG_PORT_STATUS(5), 0); + } ar8xxx_phy_init(priv); @@ -894,6 +952,65 @@ ar8229_init_port(struct ar8xxx_priv *priv, int port) __ar8216_init_port(priv, port, true, true); } + +static int +ar7240sw_hw_init(struct ar8xxx_priv *priv) +{ + 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); + + priv->port4_phy = 1; + /* disable port5 to prevent mii conflict */ + ar8xxx_write(priv, AR8216_REG_PORT_STATUS(5), 0); + + ar8xxx_phy_init(priv); + + priv->initialized = true; + return 0; +} + +static void +ar7240sw_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 ARP frame acknowledge, aging, MAC replacing */ + ar8xxx_write(priv, AR8216_REG_ATU_CTRL, + AR8216_ATU_CTRL_RESERVED | + 0x2b /* 5 min age time */ | + AR8216_ATU_CTRL_AGE_EN | + AR8216_ATU_CTRL_ARP_EN | + AR8216_ATU_CTRL_LEARN_CHANGE); + + /* Enable Broadcast frames transmitted to the CPU */ + ar8xxx_reg_set(priv, AR8216_REG_FLOOD_MASK, + AR8236_FM_CPU_BROADCAST_EN); + + /* setup MTU */ + ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL, + AR8216_GCTRL_MTU, + AR8216_GCTRL_MTU); + + /* setup Service TAG */ + ar8xxx_rmw(priv, AR8216_REG_SERVICE_TAG, AR8216_SERVICE_TAG_M, 0); +} + +static void +ar7240sw_setup_port(struct ar8xxx_priv *priv, int port, u32 members) +{ + return __ar8216_setup_port(priv, port, members, false); +} + static void ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 members) { @@ -1683,6 +1800,33 @@ 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) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + u64 *mib_stats; + + if (!ar8xxx_has_mib_counters(priv)) + return -EOPNOTSUPP; + + if (!(priv->chip->mib_rxb_id || priv->chip->mib_txb_id)) + return -EOPNOTSUPP; + + if (port >= dev->ports) + return -EINVAL; + + mutex_lock(&priv->mib_lock); + + mib_stats = &priv->mib_stats[port * priv->chip->num_mibs]; + + stats->tx_bytes = mib_stats[priv->chip->mib_txb_id]; + stats->rx_bytes = mib_stats[priv->chip->mib_rxb_id]; + + mutex_unlock(&priv->mib_lock); + return 0; +} + static int ar8xxx_phy_read(struct mii_bus *bus, int phy_addr, int reg_addr) { @@ -1813,16 +1957,41 @@ 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 ar7240sw_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 AR724X/AR933X built-in", + .ports = AR7240SW_NUM_PORTS, + .vlans = AR8216_NUM_VLANS, + .swops = &ar8xxx_sw_ops, + + .hw_init = ar7240sw_hw_init, + .init_globals = ar7240sw_init_globals, + .init_port = ar8229_init_port, + .phy_read = ar8216_phy_read, + .phy_write = ar8216_phy_write, + .setup_port = ar7240sw_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, + .mib_rxb_id = AR8236_MIB_RXB_ID, + .mib_txb_id = AR8236_MIB_TXB_ID, }; static const struct ar8xxx_chip ar8216_chip = { @@ -1852,7 +2021,9 @@ static const struct ar8xxx_chip ar8216_chip = { .num_mibs = ARRAY_SIZE(ar8216_mibs), .mib_decs = ar8216_mibs, - .mib_func = AR8216_REG_MIB_FUNC + .mib_func = AR8216_REG_MIB_FUNC, + .mib_rxb_id = AR8216_MIB_RXB_ID, + .mib_txb_id = AR8216_MIB_TXB_ID, }; static const struct ar8xxx_chip ar8229_chip = { @@ -1870,6 +2041,8 @@ static const struct ar8xxx_chip ar8229_chip = { .hw_init = ar8229_hw_init, .init_globals = ar8229_init_globals, .init_port = ar8229_init_port, + .phy_read = ar8216_phy_read, + .phy_write = ar8216_phy_write, .setup_port = ar8236_setup_port, .read_port_status = ar8216_read_port_status, .atu_flush = ar8216_atu_flush, @@ -1882,7 +2055,9 @@ static const struct ar8xxx_chip ar8229_chip = { .num_mibs = ARRAY_SIZE(ar8236_mibs), .mib_decs = ar8236_mibs, - .mib_func = AR8216_REG_MIB_FUNC + .mib_func = AR8216_REG_MIB_FUNC, + .mib_rxb_id = AR8236_MIB_RXB_ID, + .mib_txb_id = AR8236_MIB_TXB_ID, }; static const struct ar8xxx_chip ar8236_chip = { @@ -1912,7 +2087,9 @@ static const struct ar8xxx_chip ar8236_chip = { .num_mibs = ARRAY_SIZE(ar8236_mibs), .mib_decs = ar8236_mibs, - .mib_func = AR8216_REG_MIB_FUNC + .mib_func = AR8216_REG_MIB_FUNC, + .mib_rxb_id = AR8236_MIB_RXB_ID, + .mib_txb_id = AR8236_MIB_TXB_ID, }; static const struct ar8xxx_chip ar8316_chip = { @@ -1942,7 +2119,9 @@ static const struct ar8xxx_chip ar8316_chip = { .num_mibs = ARRAY_SIZE(ar8236_mibs), .mib_decs = ar8236_mibs, - .mib_func = AR8216_REG_MIB_FUNC + .mib_func = AR8216_REG_MIB_FUNC, + .mib_rxb_id = AR8236_MIB_RXB_ID, + .mib_txb_id = AR8236_MIB_TXB_ID, }; static int @@ -2013,7 +2192,7 @@ static void ar8xxx_mib_work_func(struct work_struct *work) { struct ar8xxx_priv *priv; - int err; + int err, i; priv = container_of(work, struct ar8xxx_priv, mib_work.work); @@ -2021,18 +2200,15 @@ ar8xxx_mib_work_func(struct work_struct *work) err = ar8xxx_mib_capture(priv); if (err) - goto next_port; - - ar8xxx_mib_fetch_port_stat(priv, priv->mib_next_port, false); + goto next_attempt; -next_port: - priv->mib_next_port++; - if (priv->mib_next_port >= priv->dev.ports) - priv->mib_next_port = 0; + for (i = 0; i < priv->dev.ports; i++) + ar8xxx_mib_fetch_port_stat(priv, i, false); +next_attempt: mutex_unlock(&priv->mib_lock); schedule_delayed_work(&priv->mib_work, - msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY)); + msecs_to_jiffies(priv->mib_poll_interval)); } static int @@ -2062,7 +2238,7 @@ ar8xxx_mib_start(struct ar8xxx_priv *priv) return; schedule_delayed_work(&priv->mib_work, - msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY)); + msecs_to_jiffies(priv->mib_poll_interval)); } static void @@ -2337,6 +2513,11 @@ ar8xxx_phy_probe(struct phy_device *phydev) priv->mii_bus = phydev->mdio.bus; priv->pdev = &phydev->mdio.dev; + ret = of_property_read_u32(priv->pdev->of_node, "qca,mib-poll-interval", + &priv->mib_poll_interval); + if (ret) + priv->mib_poll_interval = AR8XXX_MIB_WORK_DELAY; + ret = ar8xxx_id_chip(priv); if (ret) goto free_priv; @@ -2468,6 +2649,9 @@ static struct phy_driver ar8xxx_phy_driver[] = { static const struct of_device_id ar8xxx_mdiodev_of_match[] = { { + .compatible = "qca,ar7240sw", + .data = &ar7240sw_chip, + }, { .compatible = "qca,ar8229", .data = &ar8229_chip, }, { @@ -2501,6 +2685,11 @@ ar8xxx_mdiodev_probe(struct mdio_device *mdiodev) priv->pdev = &mdiodev->dev; priv->chip = (const struct ar8xxx_chip *) match->data; + ret = of_property_read_u32(priv->pdev->of_node, "qca,mib-poll-interval", + &priv->mib_poll_interval); + if (ret) + priv->mib_poll_interval = AR8XXX_MIB_WORK_DELAY; + ret = ar8xxx_read_id(priv); if (ret) goto free_priv; @@ -2526,6 +2715,12 @@ ar8xxx_mdiodev_probe(struct mdio_device *mdiodev) swdev = &priv->dev; swdev->alias = dev_name(&mdiodev->dev); + + if (of_property_read_bool(priv->pdev->of_node, "qca,phy4-mii-enable")) { + priv->port4_phy = true; + swdev->ports--; + } + ret = register_switch(swdev, NULL); if (ret) goto free_priv;