X-Git-Url: https://git.archive.openwrt.org/?p=openwrt.git;a=blobdiff_plain;f=target%2Flinux%2Fgeneric%2Ffiles%2Fdrivers%2Fnet%2Fphy%2Far8216.c;h=c02803b5e01d7c50dd901a4927ef4e8cfa0cc429;hp=46f0b6d2bc8c3e27a8b0280d94a3ad44a3ee6637;hb=e2cef5cb8fda4e4f7f9d7ff6543ee601c8055df8;hpb=6d10d89b2903077abc2d4a54d2057edc33805327 diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c index 46f0b6d2bc..c02803b5e0 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "ar8216.h" /* size of the vlan table */ @@ -39,9 +40,25 @@ #define AR8X16_PROBE_RETRIES 10 #define AR8X16_MAX_PORTS 8 +#define AR8XXX_MIB_WORK_DELAY 2000 /* msecs */ + struct ar8216_priv; -#define AR8XXX_CAP_GIGE BIT(0) +#define AR8XXX_CAP_GIGE BIT(0) +#define AR8XXX_CAP_MIB_COUNTERS BIT(1) + +enum { + AR8XXX_VER_AR8216 = 0x01, + AR8XXX_VER_AR8236 = 0x03, + AR8XXX_VER_AR8316 = 0x10, + AR8XXX_VER_AR8327 = 0x12, +}; + +struct ar8xxx_mib_desc { + unsigned int size; + unsigned int offset; + const char *name; +}; struct ar8xxx_chip { unsigned long caps; @@ -55,25 +72,35 @@ struct ar8xxx_chip { 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); + + const struct ar8xxx_mib_desc *mib_decs; + unsigned num_mibs; }; struct ar8216_priv { struct switch_dev dev; + struct mii_bus *mii_bus; 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_type; + u8 chip_ver; + u8 chip_rev; const struct ar8xxx_chip *chip; bool initialized; bool port4_phy; - char buf[80]; + char buf[2048]; bool init; bool mii_lo_first; + struct mutex mib_lock; + struct delayed_work mib_work; + int mib_next_port; + u64 *mib_stats; + /* all fields below are cleared on reset */ bool vlan; u16 vlan_id[AR8X16_MAX_VLANS]; @@ -82,31 +109,129 @@ struct ar8216_priv { u16 pvid[AR8X16_MAX_PORTS]; }; -#define to_ar8216(_dev) container_of(_dev, struct ar8216_priv, dev) +#define MIB_DESC(_s , _o, _n) \ + { \ + .size = (_s), \ + .offset = (_o), \ + .name = (_n), \ + } + +static const struct ar8xxx_mib_desc ar8216_mibs[] = { + MIB_DESC(1, AR8216_STATS_RXBROAD, "RxBroad"), + MIB_DESC(1, AR8216_STATS_RXPAUSE, "RxPause"), + MIB_DESC(1, AR8216_STATS_RXMULTI, "RxMulti"), + MIB_DESC(1, AR8216_STATS_RXFCSERR, "RxFcsErr"), + MIB_DESC(1, AR8216_STATS_RXALIGNERR, "RxAlignErr"), + MIB_DESC(1, AR8216_STATS_RXRUNT, "RxRunt"), + MIB_DESC(1, AR8216_STATS_RXFRAGMENT, "RxFragment"), + MIB_DESC(1, AR8216_STATS_RX64BYTE, "Rx64Byte"), + MIB_DESC(1, AR8216_STATS_RX128BYTE, "Rx128Byte"), + MIB_DESC(1, AR8216_STATS_RX256BYTE, "Rx256Byte"), + MIB_DESC(1, AR8216_STATS_RX512BYTE, "Rx512Byte"), + MIB_DESC(1, AR8216_STATS_RX1024BYTE, "Rx1024Byte"), + MIB_DESC(1, AR8216_STATS_RXMAXBYTE, "RxMaxByte"), + MIB_DESC(1, AR8216_STATS_RXTOOLONG, "RxTooLong"), + MIB_DESC(2, AR8216_STATS_RXGOODBYTE, "RxGoodByte"), + MIB_DESC(2, AR8216_STATS_RXBADBYTE, "RxBadByte"), + MIB_DESC(1, AR8216_STATS_RXOVERFLOW, "RxOverFlow"), + MIB_DESC(1, AR8216_STATS_FILTERED, "Filtered"), + MIB_DESC(1, AR8216_STATS_TXBROAD, "TxBroad"), + MIB_DESC(1, AR8216_STATS_TXPAUSE, "TxPause"), + MIB_DESC(1, AR8216_STATS_TXMULTI, "TxMulti"), + MIB_DESC(1, AR8216_STATS_TXUNDERRUN, "TxUnderRun"), + MIB_DESC(1, AR8216_STATS_TX64BYTE, "Tx64Byte"), + MIB_DESC(1, AR8216_STATS_TX128BYTE, "Tx128Byte"), + MIB_DESC(1, AR8216_STATS_TX256BYTE, "Tx256Byte"), + MIB_DESC(1, AR8216_STATS_TX512BYTE, "Tx512Byte"), + MIB_DESC(1, AR8216_STATS_TX1024BYTE, "Tx1024Byte"), + MIB_DESC(1, AR8216_STATS_TXMAXBYTE, "TxMaxByte"), + MIB_DESC(1, AR8216_STATS_TXOVERSIZE, "TxOverSize"), + MIB_DESC(2, AR8216_STATS_TXBYTE, "TxByte"), + MIB_DESC(1, AR8216_STATS_TXCOLLISION, "TxCollision"), + MIB_DESC(1, AR8216_STATS_TXABORTCOL, "TxAbortCol"), + MIB_DESC(1, AR8216_STATS_TXMULTICOL, "TxMultiCol"), + MIB_DESC(1, AR8216_STATS_TXSINGLECOL, "TxSingleCol"), + MIB_DESC(1, AR8216_STATS_TXEXCDEFER, "TxExcDefer"), + MIB_DESC(1, AR8216_STATS_TXDEFER, "TxDefer"), + MIB_DESC(1, AR8216_STATS_TXLATECOL, "TxLateCol"), +}; + +static const struct ar8xxx_mib_desc ar8236_mibs[] = { + MIB_DESC(1, AR8236_STATS_RXBROAD, "RxBroad"), + MIB_DESC(1, AR8236_STATS_RXPAUSE, "RxPause"), + MIB_DESC(1, AR8236_STATS_RXMULTI, "RxMulti"), + MIB_DESC(1, AR8236_STATS_RXFCSERR, "RxFcsErr"), + MIB_DESC(1, AR8236_STATS_RXALIGNERR, "RxAlignErr"), + MIB_DESC(1, AR8236_STATS_RXRUNT, "RxRunt"), + MIB_DESC(1, AR8236_STATS_RXFRAGMENT, "RxFragment"), + MIB_DESC(1, AR8236_STATS_RX64BYTE, "Rx64Byte"), + MIB_DESC(1, AR8236_STATS_RX128BYTE, "Rx128Byte"), + MIB_DESC(1, AR8236_STATS_RX256BYTE, "Rx256Byte"), + MIB_DESC(1, AR8236_STATS_RX512BYTE, "Rx512Byte"), + MIB_DESC(1, AR8236_STATS_RX1024BYTE, "Rx1024Byte"), + MIB_DESC(1, AR8236_STATS_RX1518BYTE, "Rx1518Byte"), + MIB_DESC(1, AR8236_STATS_RXMAXBYTE, "RxMaxByte"), + MIB_DESC(1, AR8236_STATS_RXTOOLONG, "RxTooLong"), + MIB_DESC(2, AR8236_STATS_RXGOODBYTE, "RxGoodByte"), + MIB_DESC(2, AR8236_STATS_RXBADBYTE, "RxBadByte"), + MIB_DESC(1, AR8236_STATS_RXOVERFLOW, "RxOverFlow"), + MIB_DESC(1, AR8236_STATS_FILTERED, "Filtered"), + MIB_DESC(1, AR8236_STATS_TXBROAD, "TxBroad"), + MIB_DESC(1, AR8236_STATS_TXPAUSE, "TxPause"), + MIB_DESC(1, AR8236_STATS_TXMULTI, "TxMulti"), + MIB_DESC(1, AR8236_STATS_TXUNDERRUN, "TxUnderRun"), + MIB_DESC(1, AR8236_STATS_TX64BYTE, "Tx64Byte"), + MIB_DESC(1, AR8236_STATS_TX128BYTE, "Tx128Byte"), + MIB_DESC(1, AR8236_STATS_TX256BYTE, "Tx256Byte"), + MIB_DESC(1, AR8236_STATS_TX512BYTE, "Tx512Byte"), + MIB_DESC(1, AR8236_STATS_TX1024BYTE, "Tx1024Byte"), + MIB_DESC(1, AR8236_STATS_TX1518BYTE, "Tx1518Byte"), + MIB_DESC(1, AR8236_STATS_TXMAXBYTE, "TxMaxByte"), + MIB_DESC(1, AR8236_STATS_TXOVERSIZE, "TxOverSize"), + MIB_DESC(2, AR8236_STATS_TXBYTE, "TxByte"), + MIB_DESC(1, AR8236_STATS_TXCOLLISION, "TxCollision"), + MIB_DESC(1, AR8236_STATS_TXABORTCOL, "TxAbortCol"), + MIB_DESC(1, AR8236_STATS_TXMULTICOL, "TxMultiCol"), + MIB_DESC(1, AR8236_STATS_TXSINGLECOL, "TxSingleCol"), + MIB_DESC(1, AR8236_STATS_TXEXCDEFER, "TxExcDefer"), + MIB_DESC(1, AR8236_STATS_TXDEFER, "TxDefer"), + MIB_DESC(1, AR8236_STATS_TXLATECOL, "TxLateCol"), +}; + +static inline struct ar8216_priv * +swdev_to_ar8216(struct switch_dev *swdev) +{ + return container_of(swdev, struct ar8216_priv, dev); +} static inline bool ar8xxx_has_gige(struct ar8216_priv *priv) { return priv->chip->caps & AR8XXX_CAP_GIGE; } +static inline bool ar8xxx_has_mib_counters(struct ar8216_priv *priv) +{ + return priv->chip->caps & AR8XXX_CAP_MIB_COUNTERS; +} + static inline bool chip_is_ar8216(struct ar8216_priv *priv) { - return priv->chip_type == AR8216; + return priv->chip_ver == AR8XXX_VER_AR8216; } static inline bool chip_is_ar8236(struct ar8216_priv *priv) { - return priv->chip_type == AR8236; + return priv->chip_ver == AR8XXX_VER_AR8236; } static inline bool chip_is_ar8316(struct ar8216_priv *priv) { - return priv->chip_type == AR8316; + return priv->chip_ver == AR8XXX_VER_AR8316; } static inline bool chip_is_ar8327(struct ar8216_priv *priv) { - return priv->chip_type == AR8327; + return priv->chip_ver == AR8XXX_VER_AR8327; } static inline void @@ -125,8 +250,7 @@ split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) static u32 ar8216_mii_read(struct ar8216_priv *priv, int reg) { - struct phy_device *phy = priv->phy; - struct mii_bus *bus = phy->bus; + struct mii_bus *bus = priv->mii_bus; u16 r1, r2, page; u16 lo, hi; @@ -147,8 +271,7 @@ ar8216_mii_read(struct ar8216_priv *priv, int reg) static void ar8216_mii_write(struct ar8216_priv *priv, int reg, u32 val) { - struct phy_device *phy = priv->phy; - struct mii_bus *bus = phy->bus; + struct mii_bus *bus = priv->mii_bus; u16 r1, r2, r3; u16 lo, hi; @@ -175,7 +298,7 @@ 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; + struct mii_bus *bus = priv->mii_bus; mutex_lock(&bus->mdio_lock); bus->write(bus, phy_addr, MII_ATH_DBG_ADDR, dbg_addr); @@ -183,6 +306,17 @@ ar8216_phy_dbg_write(struct ar8216_priv *priv, int phy_addr, mutex_unlock(&bus->mdio_lock); } +static void +ar8216_phy_mmd_write(struct ar8216_priv *priv, int phy_addr, u16 addr, u16 data) +{ + struct mii_bus *bus = priv->mii_bus; + + mutex_lock(&bus->mdio_lock); + bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr); + bus->write(bus, phy_addr, MII_ATH_MMD_DATA, data); + mutex_unlock(&bus->mdio_lock); +} + static u32 ar8216_rmw(struct ar8216_priv *priv, int reg, u32 mask, u32 val) { @@ -198,6 +332,118 @@ ar8216_rmw(struct ar8216_priv *priv, int reg, u32 mask, u32 val) return v; } +static inline void +ar8216_reg_set(struct ar8216_priv *priv, int reg, u32 val) +{ + u32 v; + + lockdep_assert_held(&priv->reg_mutex); + + v = priv->read(priv, reg); + v |= val; + priv->write(priv, reg, v); +} + +static int +ar8216_reg_wait(struct ar8216_priv *priv, u32 reg, u32 mask, u32 val, + unsigned timeout) +{ + int i; + + for (i = 0; i < timeout; i++) { + u32 t; + + t = priv->read(priv, reg); + if ((t & mask) == val) + return 0; + + usleep_range(1000, 2000); + } + + return -ETIMEDOUT; +} + +static int +ar8216_mib_op(struct ar8216_priv *priv, u32 op) +{ + unsigned mib_func; + int ret; + + lockdep_assert_held(&priv->mib_lock); + + if (chip_is_ar8327(priv)) + mib_func = AR8327_REG_MIB_FUNC; + else + mib_func = AR8216_REG_MIB_FUNC; + + mutex_lock(&priv->reg_mutex); + /* Capture the hardware statistics for all ports */ + ar8216_rmw(priv, mib_func, AR8216_MIB_FUNC, (op << AR8216_MIB_FUNC_S)); + mutex_unlock(&priv->reg_mutex); + + /* Wait for the capturing to complete. */ + ret = ar8216_reg_wait(priv, mib_func, AR8216_MIB_BUSY, 0, 10); + if (ret) + goto out; + + ret = 0; + +out: + return ret; +} + +static int +ar8216_mib_capture(struct ar8216_priv *priv) +{ + return ar8216_mib_op(priv, AR8216_MIB_FUNC_CAPTURE); +} + +static int +ar8216_mib_flush(struct ar8216_priv *priv) +{ + return ar8216_mib_op(priv, AR8216_MIB_FUNC_FLUSH); +} + +static void +ar8216_mib_fetch_port_stat(struct ar8216_priv *priv, int port, bool flush) +{ + unsigned int base; + u64 *mib_stats; + int i; + + WARN_ON(port >= priv->dev.ports); + + lockdep_assert_held(&priv->mib_lock); + + if (chip_is_ar8327(priv)) + base = AR8327_REG_PORT_STATS_BASE(port); + else if (chip_is_ar8236(priv) || + chip_is_ar8316(priv)) + base = AR8236_REG_PORT_STATS_BASE(port); + else + base = AR8216_REG_PORT_STATS_BASE(port); + + mib_stats = &priv->mib_stats[port * priv->chip->num_mibs]; + for (i = 0; i < priv->chip->num_mibs; i++) { + const struct ar8xxx_mib_desc *mib; + u64 t; + + mib = &priv->chip->mib_decs[i]; + t = priv->read(priv, base + mib->offset); + if (mib->size == 2) { + u64 hi; + + hi = priv->read(priv, base + mib->offset + 4); + t |= hi << 32; + } + + if (flush) + mib_stats[i] = 0; + else + mib_stats[i] += t; + } +} + static void ar8216_read_port_link(struct ar8216_priv *priv, int port, struct switch_port_link *link) @@ -446,6 +692,8 @@ ar8216_init_port(struct ar8216_priv *priv, int port) } static const struct ar8xxx_chip ar8216_chip = { + .caps = AR8XXX_CAP_MIB_COUNTERS, + .hw_init = ar8216_hw_init, .init_globals = ar8216_init_globals, .init_port = ar8216_init_port, @@ -454,6 +702,9 @@ static const struct ar8xxx_chip ar8216_chip = { .atu_flush = ar8216_atu_flush, .vtu_flush = ar8216_vtu_flush, .vtu_load_vlan = ar8216_vtu_load_vlan, + + .num_mibs = ARRAY_SIZE(ar8216_mibs), + .mib_decs = ar8216_mibs, }; static void @@ -489,7 +740,7 @@ ar8236_hw_init(struct ar8216_priv *priv) return 0; /* Initialize the PHYs */ - bus = priv->phy->bus; + bus = priv->mii_bus; for (i = 0; i < 5; i++) { mdiobus_write(bus, i, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | @@ -508,9 +759,15 @@ ar8236_init_globals(struct ar8216_priv *priv) /* enable jumbo frames */ ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, AR8316_GCTRL_MTU, 9018 + 8 + 2); + + /* Enable MIB counters */ + ar8216_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN, + (AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) | + AR8236_MIB_EN); } static const struct ar8xxx_chip ar8236_chip = { + .caps = AR8XXX_CAP_MIB_COUNTERS, .hw_init = ar8236_hw_init, .init_globals = ar8236_init_globals, .init_port = ar8216_init_port, @@ -519,6 +776,9 @@ static const struct ar8xxx_chip ar8236_chip = { .atu_flush = ar8216_atu_flush, .vtu_flush = ar8216_vtu_flush, .vtu_load_vlan = ar8216_vtu_load_vlan, + + .num_mibs = ARRAY_SIZE(ar8236_mibs), + .mib_decs = ar8236_mibs, }; static int @@ -555,7 +815,7 @@ ar8316_hw_init(struct ar8216_priv *priv) priv->write(priv, 0x8, newval); /* Initialize the ports */ - bus = priv->phy->bus; + bus = priv->mii_bus; for (i = 0; i < 5; i++) { if ((i == 4) && priv->port4_phy && priv->phy->interface == PHY_INTERFACE_MODE_RGMII) { @@ -593,10 +853,15 @@ ar8316_init_globals(struct ar8216_priv *priv) /* enable jumbo frames */ ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, AR8316_GCTRL_MTU, 9018 + 8 + 2); + + /* Enable MIB counters */ + ar8216_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN, + (AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) | + AR8236_MIB_EN); } static const struct ar8xxx_chip ar8316_chip = { - .caps = AR8XXX_CAP_GIGE, + .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS, .hw_init = ar8316_hw_init, .init_globals = ar8316_init_globals, .init_port = ar8216_init_port, @@ -605,6 +870,9 @@ static const struct ar8xxx_chip ar8316_chip = { .atu_flush = ar8216_atu_flush, .vtu_flush = ar8216_vtu_flush, .vtu_load_vlan = ar8216_vtu_load_vlan, + + .num_mibs = ARRAY_SIZE(ar8236_mibs), + .mib_decs = ar8236_mibs, }; static u32 @@ -638,6 +906,23 @@ ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg) case AR8327_PAD_MAC_SGMII: t = AR8327_PAD_SGMII_EN; + + /* + * WAR for the QUalcomm Atheros AP136 board. + * It seems that RGMII TX/RX delay settings needs to be + * applied for SGMII mode as well, The ethernet is not + * reliable without this. + */ + t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S; + t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S; + if (cfg->rxclk_delay_en) + t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN; + if (cfg->txclk_delay_en) + t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; + + if (cfg->sgmii_delay_en) + t |= AR8327_PAD_SGMII_DELAY_EN; + break; case AR8327_PAD_MAC2PHY_MII: @@ -684,10 +969,39 @@ ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg) return t; } +static void +ar8327_phy_fixup(struct ar8216_priv *priv, int phy) +{ + switch (priv->chip_rev) { + case 1: + /* For 100M waveform */ + ar8216_phy_dbg_write(priv, phy, 0, 0x02ea); + /* Turn on Gigabit clock */ + ar8216_phy_dbg_write(priv, phy, 0x3d, 0x68a0); + break; + + case 2: + ar8216_phy_mmd_write(priv, phy, 0x7, 0x3c); + ar8216_phy_mmd_write(priv, phy, 0x4007, 0x0); + /* fallthrough */ + case 4: + ar8216_phy_mmd_write(priv, phy, 0x3, 0x800d); + ar8216_phy_mmd_write(priv, phy, 0x4003, 0x803f); + + ar8216_phy_dbg_write(priv, phy, 0x3d, 0x6860); + ar8216_phy_dbg_write(priv, phy, 0x5, 0x2c46); + ar8216_phy_dbg_write(priv, phy, 0x3c, 0x6000); + break; + } +} + static int ar8327_hw_init(struct ar8216_priv *priv) { struct ar8327_platform_data *pdata; + struct ar8327_led_cfg *led_cfg; + struct mii_bus *bus; + u32 pos, new_pos; u32 t; int i; @@ -702,17 +1016,41 @@ ar8327_hw_init(struct ar8216_priv *priv) t = ar8327_get_pad_cfg(pdata->pad6_cfg); priv->write(priv, AR8327_REG_PAD6_MODE, t); - priv->write(priv, AR8327_REG_POWER_ON_STRIP, 0x40000000); + pos = priv->read(priv, AR8327_REG_POWER_ON_STRIP); + new_pos = pos; + + led_cfg = pdata->led_cfg; + if (led_cfg) { + if (led_cfg->open_drain) + new_pos |= AR8327_POWER_ON_STRIP_LED_OPEN_EN; + else + new_pos &= ~AR8327_POWER_ON_STRIP_LED_OPEN_EN; + + priv->write(priv, AR8327_REG_LED_CTRL0, led_cfg->led_ctrl0); + priv->write(priv, AR8327_REG_LED_CTRL1, led_cfg->led_ctrl1); + priv->write(priv, AR8327_REG_LED_CTRL2, led_cfg->led_ctrl2); + priv->write(priv, AR8327_REG_LED_CTRL3, led_cfg->led_ctrl3); + } + + if (new_pos != pos) { + new_pos |= AR8327_POWER_ON_STRIP_POWER_ON_SEL; + priv->write(priv, AR8327_REG_POWER_ON_STRIP, new_pos); + } - /* fixup PHYs */ + bus = priv->mii_bus; for (i = 0; i < AR8327_NUM_PHYS; i++) { - /* For 100M waveform */ - ar8216_phy_dbg_write(priv, i, 0, 0x02ea); + ar8327_phy_fixup(priv, i); - /* Turn on Gigabit clock */ - ar8216_phy_dbg_write(priv, i, 0x3d, 0x68a0); + /* start aneg on the PHY */ + mdiobus_write(bus, i, MII_ADVERTISE, ADVERTISE_ALL | + ADVERTISE_PAUSE_CAP | + ADVERTISE_PAUSE_ASYM); + mdiobus_write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL); + mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); } + msleep(1000); + return 0; } @@ -735,22 +1073,20 @@ ar8327_init_globals(struct ar8216_priv *priv) /* setup MTU */ ar8216_rmw(priv, AR8327_REG_MAX_FRAME_SIZE, AR8327_MAX_FRAME_SIZE_MTU, 1518 + 8 + 2); + + /* Enable MIB counters */ + ar8216_reg_set(priv, AR8327_REG_MODULE_EN, + AR8327_MODULE_EN_MIB); } static void -ar8327_init_cpuport(struct ar8216_priv *priv) +ar8327_config_port(struct ar8216_priv *priv, unsigned int port, + struct ar8327_port_cfg *cfg) { - struct ar8327_platform_data *pdata; - struct ar8327_port_cfg *cfg; u32 t; - pdata = priv->phy->dev.platform_data; - if (!pdata) - return; - - cfg = &pdata->cpuport_cfg; - if (!cfg->force_link) { - priv->write(priv, AR8327_REG_PORT_STATUS(AR8216_PORT_CPU), + if (!cfg || !cfg->force_link) { + priv->write(priv, AR8327_REG_PORT_STATUS(port), AR8216_PORT_STATUS_LINK_AUTO); return; } @@ -759,6 +1095,7 @@ ar8327_init_cpuport(struct ar8216_priv *priv) t |= cfg->duplex ? AR8216_PORT_STATUS_DUPLEX : 0; t |= cfg->rxpause ? AR8216_PORT_STATUS_RXFLOW : 0; t |= cfg->txpause ? AR8216_PORT_STATUS_TXFLOW : 0; + switch (cfg->speed) { case AR8327_PORT_SPEED_10: t |= AR8216_PORT_SPEED_10M; @@ -771,24 +1108,32 @@ ar8327_init_cpuport(struct ar8216_priv *priv) break; } - priv->write(priv, AR8327_REG_PORT_STATUS(AR8216_PORT_CPU), t); + priv->write(priv, AR8327_REG_PORT_STATUS(port), t); } static void ar8327_init_port(struct ar8216_priv *priv, int port) { + struct ar8327_platform_data *pdata; + struct ar8327_port_cfg *cfg; u32 t; - if (port == AR8216_PORT_CPU) { - ar8327_init_cpuport(priv); - } else { - t = AR8216_PORT_STATUS_LINK_AUTO; - priv->write(priv, AR8327_REG_PORT_STATUS(port), t); - } + pdata = priv->phy->dev.platform_data; + + if (pdata && port == AR8216_PORT_CPU) + cfg = &pdata->port0_cfg; + else if (pdata && port == 6) + cfg = &pdata->port6_cfg; + else + cfg = NULL; + + ar8327_config_port(priv, port, cfg); priv->write(priv, AR8327_REG_PORT_HEADER(port), 0); - priv->write(priv, AR8327_REG_PORT_VLAN0(port), 0); + t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S; + t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S; + priv->write(priv, AR8327_REG_PORT_VLAN0(port), t); t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S; priv->write(priv, AR8327_REG_PORT_VLAN1(port), t); @@ -900,7 +1245,7 @@ ar8327_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress, } static const struct ar8xxx_chip ar8327_chip = { - .caps = AR8XXX_CAP_GIGE, + .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS, .hw_init = ar8327_hw_init, .init_globals = ar8327_init_globals, .init_port = ar8327_init_port, @@ -909,13 +1254,16 @@ static const struct ar8xxx_chip ar8327_chip = { .atu_flush = ar8327_atu_flush, .vtu_flush = ar8327_vtu_flush, .vtu_load_vlan = ar8327_vtu_load_vlan, + + .num_mibs = ARRAY_SIZE(ar8236_mibs), + .mib_decs = ar8236_mibs, }; static int ar8216_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8216_priv *priv = swdev_to_ar8216(dev); priv->vlan = !!val->value.i; return 0; } @@ -924,7 +1272,7 @@ static int ar8216_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8216_priv *priv = swdev_to_ar8216(dev); val->value.i = priv->vlan; return 0; } @@ -933,7 +1281,7 @@ ar8216_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, static int ar8216_sw_set_pvid(struct switch_dev *dev, int port, int vlan) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8216_priv *priv = swdev_to_ar8216(dev); /* make sure no invalid PVIDs get set */ @@ -947,7 +1295,7 @@ ar8216_sw_set_pvid(struct switch_dev *dev, int port, int vlan) static int ar8216_sw_get_pvid(struct switch_dev *dev, int port, int *vlan) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8216_priv *priv = swdev_to_ar8216(dev); *vlan = priv->pvid[port]; return 0; } @@ -956,7 +1304,7 @@ static int ar8216_sw_set_vid(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8216_priv *priv = swdev_to_ar8216(dev); priv->vlan_id[val->port_vlan] = val->value.i; return 0; } @@ -965,7 +1313,7 @@ static int ar8216_sw_get_vid(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8216_priv *priv = swdev_to_ar8216(dev); val->value.i = priv->vlan_id[val->port_vlan]; return 0; } @@ -974,7 +1322,7 @@ static int ar8216_sw_get_port_link(struct switch_dev *dev, int port, struct switch_port_link *link) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8216_priv *priv = swdev_to_ar8216(dev); ar8216_read_port_link(priv, port, link); return 0; @@ -983,7 +1331,7 @@ ar8216_sw_get_port_link(struct switch_dev *dev, int port, static int ar8216_sw_get_ports(struct switch_dev *dev, struct switch_val *val) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8216_priv *priv = swdev_to_ar8216(dev); u8 ports = priv->vlan_table[val->port_vlan]; int i; @@ -1007,7 +1355,7 @@ ar8216_sw_get_ports(struct switch_dev *dev, struct switch_val *val) static int ar8216_sw_set_ports(struct switch_dev *dev, struct switch_val *val) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8216_priv *priv = swdev_to_ar8216(dev); u8 *vt = &priv->vlan_table[val->port_vlan]; int i, j; @@ -1038,7 +1386,7 @@ ar8216_sw_set_ports(struct switch_dev *dev, struct switch_val *val) static int ar8216_sw_hw_apply(struct switch_dev *dev) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8216_priv *priv = swdev_to_ar8216(dev); u8 portmask[AR8X16_MAX_PORTS]; int i, j; @@ -1105,7 +1453,7 @@ ar8216_sw_hw_apply(struct switch_dev *dev) static int ar8216_sw_reset_switch(struct switch_dev *dev) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8216_priv *priv = swdev_to_ar8216(dev); int i; mutex_lock(&priv->reg_mutex); @@ -1125,6 +1473,112 @@ ar8216_sw_reset_switch(struct switch_dev *dev) return ar8216_sw_hw_apply(dev); } +static int +ar8216_sw_set_reset_mibs(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8216_priv *priv = swdev_to_ar8216(dev); + unsigned int len; + int ret; + + if (!ar8xxx_has_mib_counters(priv)) + return -EOPNOTSUPP; + + mutex_lock(&priv->mib_lock); + + len = priv->dev.ports * priv->chip->num_mibs * + sizeof(*priv->mib_stats); + memset(priv->mib_stats, '\0', len); + ret = ar8216_mib_flush(priv); + if (ret) + goto unlock; + + ret = 0; + +unlock: + mutex_unlock(&priv->mib_lock); + return ret; +} + +static int +ar8216_sw_set_port_reset_mib(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8216_priv *priv = swdev_to_ar8216(dev); + int port; + int ret; + + if (!ar8xxx_has_mib_counters(priv)) + return -EOPNOTSUPP; + + port = val->port_vlan; + if (port >= dev->ports) + return -EINVAL; + + mutex_lock(&priv->mib_lock); + ret = ar8216_mib_capture(priv); + if (ret) + goto unlock; + + ar8216_mib_fetch_port_stat(priv, port, true); + + ret = 0; + +unlock: + mutex_unlock(&priv->mib_lock); + return ret; +} + +static int +ar8216_sw_get_port_mib(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8216_priv *priv = swdev_to_ar8216(dev); + const struct ar8xxx_chip *chip = priv->chip; + u64 *mib_stats; + int port; + int ret; + char *buf = priv->buf; + int i, len = 0; + + if (!ar8xxx_has_mib_counters(priv)) + return -EOPNOTSUPP; + + port = val->port_vlan; + if (port >= dev->ports) + return -EINVAL; + + mutex_lock(&priv->mib_lock); + ret = ar8216_mib_capture(priv); + if (ret) + goto unlock; + + ar8216_mib_fetch_port_stat(priv, port, false); + + len += snprintf(buf + len, sizeof(priv->buf) - len, + "Port %d MIB counters\n", + port); + + mib_stats = &priv->mib_stats[port * chip->num_mibs]; + for (i = 0; i < chip->num_mibs; i++) + len += snprintf(buf + len, sizeof(priv->buf) - len, + "%-12s: %llu\n", + chip->mib_decs[i].name, + mib_stats[i]); + + val->value.s = buf; + val->len = len; + + ret = 0; + +unlock: + mutex_unlock(&priv->mib_lock); + return ret; +} + static struct switch_attr ar8216_globals[] = { { .type = SWITCH_TYPE_INT, @@ -1134,9 +1588,29 @@ static struct switch_attr ar8216_globals[] = { .get = ar8216_sw_get_vlan, .max = 1 }, + { + .type = SWITCH_TYPE_NOVAL, + .name = "reset_mibs", + .description = "Reset all MIB counters", + .set = ar8216_sw_set_reset_mibs, + }, + }; static struct switch_attr ar8216_port[] = { + { + .type = SWITCH_TYPE_NOVAL, + .name = "reset_mib", + .description = "Reset single port MIB counters", + .set = ar8216_sw_set_port_reset_mib, + }, + { + .type = SWITCH_TYPE_STRING, + .name = "mib", + .description = "Get port's MIB counters", + .set = NULL, + .get = ar8216_sw_get_port_mib, + }, }; static struct switch_attr ar8216_vlan[] = { @@ -1179,8 +1653,6 @@ ar8216_id_chip(struct ar8216_priv *priv) u16 id; int i; - priv->chip_type = UNKNOWN; - val = ar8216_mii_read(priv, AR8216_REG_CTRL); if (val == ~0) return -ENODEV; @@ -1198,32 +1670,29 @@ ar8216_id_chip(struct ar8216_priv *priv) return -ENODEV; } - switch (id) { - case 0x0101: - priv->chip_type = AR8216; + priv->chip_ver = (id & AR8216_CTRL_VERSION) >> AR8216_CTRL_VERSION_S; + priv->chip_rev = (id & AR8216_CTRL_REVISION); + + switch (priv->chip_ver) { + case AR8XXX_VER_AR8216: priv->chip = &ar8216_chip; break; - case 0x0301: - priv->chip_type = AR8236; + case AR8XXX_VER_AR8236: priv->chip = &ar8236_chip; break; - case 0x1000: - case 0x1001: - priv->chip_type = AR8316; + case AR8XXX_VER_AR8316: priv->chip = &ar8316_chip; break; - case 0x1202: - priv->chip_type = AR8327; + case AR8XXX_VER_AR8327: priv->mii_lo_first = true; priv->chip = &ar8327_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)); + priv->chip_ver, priv->chip_rev, + mdiobus_read(priv->mii_bus, priv->phy->addr, 2), + mdiobus_read(priv->mii_bus, priv->phy->addr, 3)); return -ENODEV; } @@ -1231,6 +1700,75 @@ ar8216_id_chip(struct ar8216_priv *priv) return 0; } +static void +ar8xxx_mib_work_func(struct work_struct *work) +{ + struct ar8216_priv *priv; + int err; + + priv = container_of(work, struct ar8216_priv, mib_work.work); + + mutex_lock(&priv->mib_lock); + + err = ar8216_mib_capture(priv); + if (err) + goto next_port; + + ar8216_mib_fetch_port_stat(priv, priv->mib_next_port, false); + +next_port: + priv->mib_next_port++; + if (priv->mib_next_port >= priv->dev.ports) + priv->mib_next_port = 0; + + mutex_unlock(&priv->mib_lock); + schedule_delayed_work(&priv->mib_work, + msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY)); +} + +static int +ar8xxx_mib_init(struct ar8216_priv *priv) +{ + unsigned int len; + + if (!ar8xxx_has_mib_counters(priv)) + return 0; + + BUG_ON(!priv->chip->mib_decs || !priv->chip->num_mibs); + + len = priv->dev.ports * priv->chip->num_mibs * + sizeof(*priv->mib_stats); + priv->mib_stats = kzalloc(len, GFP_KERNEL); + + if (!priv->mib_stats) + return -ENOMEM; + + mutex_init(&priv->mib_lock); + INIT_DELAYED_WORK(&priv->mib_work, ar8xxx_mib_work_func); + + return 0; +} + +static void +ar8xxx_mib_start(struct ar8216_priv *priv) +{ + if (!ar8xxx_has_mib_counters(priv)) + return; + + schedule_delayed_work(&priv->mib_work, + msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY)); +} + +static void +ar8xxx_mib_cleanup(struct ar8216_priv *priv) +{ + if (!ar8xxx_has_mib_counters(priv)) + return; + + cancel_delayed_work(&priv->mib_work); + kfree(priv->mib_stats); +} + static int ar8216_config_init(struct phy_device *pdev) { @@ -1245,6 +1783,7 @@ ar8216_config_init(struct phy_device *pdev) return -ENOMEM; } + priv->mii_bus = pdev->bus; priv->phy = pdev; ret = ar8216_id_chip(priv); @@ -1287,9 +1826,6 @@ ar8216_config_init(struct phy_device *pdev) return 0; } - printk(KERN_INFO "%s: AR%d switch driver attached.\n", - pdev->attached_dev->name, priv->chip_type); - if (ar8xxx_has_gige(priv)) pdev->supported = SUPPORTED_1000baseT_Full; else @@ -1328,19 +1864,26 @@ ar8216_config_init(struct phy_device *pdev) swdev->vlans = AR8216_NUM_VLANS; } - ret = register_switch(&priv->dev, pdev->attached_dev); + ret = ar8xxx_mib_init(priv); if (ret) goto err_free_priv; + ret = register_switch(swdev, pdev->attached_dev); + if (ret) + goto err_cleanup_mib; + + printk(KERN_INFO "%s: %s switch driver attached.\n", + pdev->attached_dev->name, swdev->name); + priv->init = true; ret = priv->chip->hw_init(priv); if (ret) - goto err_free_priv; + goto err_unregister_switch; ret = ar8216_sw_reset_switch(&priv->dev); if (ret) - goto err_free_priv; + goto err_unregister_switch; dev->phy_ptr = priv; @@ -1353,10 +1896,17 @@ ar8216_config_init(struct phy_device *pdev) priv->init = false; + ar8xxx_mib_start(priv); + return 0; +err_unregister_switch: + unregister_switch(&priv->dev); +err_cleanup_mib: + ar8xxx_mib_cleanup(priv); err_free_priv: kfree(priv); + pdev->priv = NULL; return ret; } @@ -1414,27 +1964,50 @@ ar8216_config_aneg(struct phy_device *phydev) static int ar8216_probe(struct phy_device *pdev) { - struct ar8216_priv priv; + struct ar8216_priv *priv; + int ret; + + priv = kzalloc(sizeof(struct ar8216_priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; - priv.phy = pdev; - return ar8216_id_chip(&priv); + priv->mii_bus = pdev->bus; + priv->phy = pdev; + + ret = ar8216_id_chip(priv); + kfree(priv); + + return ret; } static void -ar8216_remove(struct phy_device *pdev) +ar8216_detach(struct phy_device *pdev) { - struct ar8216_priv *priv = pdev->priv; struct net_device *dev = pdev->attached_dev; - if (!priv) + if (!dev) return; + dev->phy_ptr = NULL; dev->priv_flags &= ~IFF_NO_IP_ALIGN; dev->eth_mangle_rx = NULL; dev->eth_mangle_tx = NULL; +} + +static void +ar8216_remove(struct phy_device *pdev) +{ + struct ar8216_priv *priv = pdev->priv; + + if (!priv) + return; + + pdev->priv = NULL; if (pdev->addr == 0) unregister_switch(&priv->dev); + + ar8xxx_mib_cleanup(priv); kfree(priv); } @@ -1445,6 +2018,7 @@ static struct phy_driver ar8216_driver = { .features = PHY_BASIC_FEATURES, .probe = ar8216_probe, .remove = ar8216_remove, + .detach = ar8216_detach, .config_init = &ar8216_config_init, .config_aneg = &ar8216_config_aneg, .read_status = &ar8216_read_status,