X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fstaging%2Fchunkeey.git;a=blobdiff_plain;f=target%2Flinux%2Fgeneric%2Ffiles%2Fdrivers%2Fnet%2Fphy%2Far8216.c;h=91e090af813c448c0e7fc6c3752b31710cb9b8c1;hp=4410fbba5085ac911cd3931388b5f0857f8278e2;hb=5b16fd1bf88e34af4fa18e74bb4be524420c11b2;hpb=76a54a424ca2118da632b43e79feb83b2e7f79b3 diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c index 4410fbba50..91e090af81 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "ar8216.h" @@ -51,6 +52,11 @@ struct ar8xxx_priv; #define AR8XXX_CAP_GIGE BIT(0) #define AR8XXX_CAP_MIB_COUNTERS BIT(1) +#define AR8XXX_NUM_PHYS 5 + +static void ar8216_set_mirror_regs(struct ar8xxx_priv *priv); +static void ar8327_set_mirror_regs(struct ar8xxx_priv *priv); + enum { AR8XXX_VER_AR8216 = 0x01, AR8XXX_VER_AR8236 = 0x03, @@ -67,6 +73,12 @@ struct ar8xxx_mib_desc { struct ar8xxx_chip { unsigned long caps; + bool config_at_probe; + bool mii_lo_first; + + /* parameters to calculate REG_PORT_STATS_BASE */ + unsigned reg_port_stats_start; + unsigned reg_port_stats_length; int (*hw_init)(struct ar8xxx_priv *priv); void (*cleanup)(struct ar8xxx_priv *priv); @@ -78,9 +90,12 @@ struct ar8xxx_chip { int (*atu_flush)(struct ar8xxx_priv *priv); void (*vtu_flush)(struct ar8xxx_priv *priv); void (*vtu_load_vlan)(struct ar8xxx_priv *priv, u32 vid, u32 port_mask); + void (*phy_fixup)(struct ar8xxx_priv *priv, int phy); + void (*set_mirror_regs)(struct ar8xxx_priv *priv); const struct ar8xxx_mib_desc *mib_decs; unsigned num_mibs; + unsigned mib_func; }; enum ar8327_led_pattern { @@ -136,15 +151,12 @@ struct ar8xxx_priv { u8 chip_ver; u8 chip_rev; const struct ar8xxx_chip *chip; - union { - struct ar8327_data ar8327; - } chip_data; + void *chip_data; bool initialized; bool port4_phy; char buf[2048]; bool init; - bool mii_lo_first; struct mutex mib_lock; struct delayed_work mib_work; @@ -314,6 +326,76 @@ split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) *page = regaddr & 0x1ff; } +/* inspired by phy_poll_reset in drivers/net/phy/phy_device.c */ +static int +ar8xxx_phy_poll_reset(struct mii_bus *bus) +{ + unsigned int sleep_msecs = 20; + int ret, elapsed, i; + + for (elapsed = sleep_msecs; elapsed <= 600; + elapsed += sleep_msecs) { + msleep(sleep_msecs); + for (i = 0; i < AR8XXX_NUM_PHYS; i++) { + ret = mdiobus_read(bus, i, MII_BMCR); + if (ret < 0) + return ret; + if (ret & BMCR_RESET) + break; + if (i == AR8XXX_NUM_PHYS - 1) { + usleep_range(1000, 2000); + return 0; + } + } + } + return -ETIMEDOUT; +} + +static int +ar8xxx_phy_check_aneg(struct phy_device *phydev) +{ + int ret; + + if (phydev->autoneg != AUTONEG_ENABLE) + return 0; + /* + * BMCR_ANENABLE might have been cleared + * by phy_init_hw in certain kernel versions + * therefore check for it + */ + ret = phy_read(phydev, MII_BMCR); + if (ret < 0) + return ret; + if (ret & BMCR_ANENABLE) + return 0; + + dev_info(&phydev->dev, "ANEG disabled, re-enabling ...\n"); + ret |= BMCR_ANENABLE | BMCR_ANRESTART; + return phy_write(phydev, MII_BMCR, ret); +} + +static void +ar8xxx_phy_init(struct ar8xxx_priv *priv) +{ + int i; + struct mii_bus *bus; + + bus = priv->mii_bus; + for (i = 0; i < AR8XXX_NUM_PHYS; i++) { + if (priv->chip->phy_fixup) + priv->chip->phy_fixup(priv, i); + + /* initialize the port itself */ + mdiobus_write(bus, i, MII_ADVERTISE, + ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); + if (ar8xxx_has_gige(priv)) + mdiobus_write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL); + mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); + } + + ar8xxx_phy_poll_reset(bus); +} + static u32 ar8xxx_mii_read(struct ar8xxx_priv *priv, int reg) { @@ -350,7 +432,7 @@ ar8xxx_mii_write(struct ar8xxx_priv *priv, int reg, u32 val) bus->write(bus, 0x18, 0, r3); usleep_range(1000, 2000); /* wait for the page switch to propagate */ - if (priv->mii_lo_first) { + if (priv->chip->mii_lo_first) { bus->write(bus, 0x10 | r2, r1, lo); bus->write(bus, 0x10 | r2, r1 + 1, hi); } else { @@ -386,7 +468,7 @@ ar8xxx_mii_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) lo = ret & 0xffff; hi = (u16) (ret >> 16); - if (priv->mii_lo_first) { + if (priv->chip->mii_lo_first) { bus->write(bus, 0x10 | r2, r1, lo); bus->write(bus, 0x10 | r2, r1 + 1, hi); } else { @@ -457,16 +539,11 @@ ar8xxx_reg_wait(struct ar8xxx_priv *priv, u32 reg, u32 mask, u32 val, static int ar8xxx_mib_op(struct ar8xxx_priv *priv, u32 op) { - unsigned mib_func; + unsigned mib_func = priv->chip->mib_func; int ret; lockdep_assert_held(&priv->mib_lock); - if (chip_is_ar8327(priv) || chip_is_ar8337(priv)) - mib_func = AR8327_REG_MIB_FUNC; - else - mib_func = AR8216_REG_MIB_FUNC; - /* Capture the hardware statistics for all ports */ ar8xxx_rmw(priv, mib_func, AR8216_MIB_FUNC, (op << AR8216_MIB_FUNC_S)); @@ -504,13 +581,8 @@ ar8xxx_mib_fetch_port_stat(struct ar8xxx_priv *priv, int port, bool flush) lockdep_assert_held(&priv->mib_lock); - if (chip_is_ar8327(priv) || chip_is_ar8337(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); + base = priv->chip->reg_port_stats_start + + priv->chip->reg_port_stats_length * port; mib_stats = &priv->mib_stats[port * priv->chip->num_mibs]; for (i = 0; i < priv->chip->num_mibs; i++) { @@ -764,6 +836,12 @@ ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members) static int ar8216_hw_init(struct ar8xxx_priv *priv) { + if (priv->initialized) + return 0; + + ar8xxx_phy_init(priv); + + priv->initialized = true; return 0; } @@ -806,6 +884,9 @@ ar8216_init_port(struct ar8xxx_priv *priv, int port) static const struct ar8xxx_chip ar8216_chip = { .caps = AR8XXX_CAP_MIB_COUNTERS, + .reg_port_stats_start = 0x19000, + .reg_port_stats_length = 0xa0, + .hw_init = ar8216_hw_init, .init_globals = ar8216_init_globals, .init_port = ar8216_init_port, @@ -814,9 +895,11 @@ static const struct ar8xxx_chip ar8216_chip = { .atu_flush = ar8216_atu_flush, .vtu_flush = ar8216_vtu_flush, .vtu_load_vlan = ar8216_vtu_load_vlan, + .set_mirror_regs = ar8216_set_mirror_regs, .num_mibs = ARRAY_SIZE(ar8216_mibs), .mib_decs = ar8216_mibs, + .mib_func = AR8216_REG_MIB_FUNC }; static void @@ -857,29 +940,6 @@ ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 members) (members << AR8236_PORT_VLAN2_MEMBER_S)); } -static int -ar8236_hw_init(struct ar8xxx_priv *priv) -{ - int i; - struct mii_bus *bus; - - if (priv->initialized) - return 0; - - /* Initialize the PHYs */ - bus = priv->mii_bus; - for (i = 0; i < 5; i++) { - mdiobus_write(bus, i, MII_ADVERTISE, - ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | - ADVERTISE_PAUSE_ASYM); - mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); - } - msleep(1000); - - priv->initialized = true; - return 0; -} - static void ar8236_init_globals(struct ar8xxx_priv *priv) { @@ -887,6 +947,15 @@ ar8236_init_globals(struct ar8xxx_priv *priv) ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL, AR8316_GCTRL_MTU, 9018 + 8 + 2); + /* enable cpu port to receive arp frames */ + ar8xxx_rmw(priv, AR8216_REG_ATU_CTRL, + AR8236_ATU_CTRL_RES, AR8236_ATU_CTRL_RES); + + /* enable cpu port to receive multicast and broadcast frames */ + ar8xxx_rmw(priv, AR8216_REG_FLOOD_MASK, + AR8236_FM_CPU_BROADCAST_EN | AR8236_FM_CPU_BCAST_FWD_EN, + AR8236_FM_CPU_BROADCAST_EN | AR8236_FM_CPU_BCAST_FWD_EN); + /* Enable MIB counters */ ar8xxx_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN, (AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) | @@ -895,7 +964,11 @@ ar8236_init_globals(struct ar8xxx_priv *priv) static const struct ar8xxx_chip ar8236_chip = { .caps = AR8XXX_CAP_MIB_COUNTERS, - .hw_init = ar8236_hw_init, + + .reg_port_stats_start = 0x20000, + .reg_port_stats_length = 0x100, + + .hw_init = ar8216_hw_init, .init_globals = ar8236_init_globals, .init_port = ar8216_init_port, .setup_port = ar8236_setup_port, @@ -903,17 +976,17 @@ static const struct ar8xxx_chip ar8236_chip = { .atu_flush = ar8216_atu_flush, .vtu_flush = ar8216_vtu_flush, .vtu_load_vlan = ar8216_vtu_load_vlan, + .set_mirror_regs = ar8216_set_mirror_regs, .num_mibs = ARRAY_SIZE(ar8236_mibs), .mib_decs = ar8236_mibs, + .mib_func = AR8216_REG_MIB_FUNC }; static int ar8316_hw_init(struct ar8xxx_priv *priv) { - int i; u32 val, newval; - struct mii_bus *bus; val = priv->read(priv, AR8316_REG_POSTRIP); @@ -952,17 +1025,7 @@ ar8316_hw_init(struct ar8xxx_priv *priv) msleep(1000); } - /* Initialize the ports */ - bus = priv->mii_bus; - for (i = 0; i < 5; i++) { - /* initialize the port itself */ - 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); + ar8xxx_phy_init(priv); out: priv->initialized = true; @@ -990,6 +1053,10 @@ ar8316_init_globals(struct ar8xxx_priv *priv) static const struct ar8xxx_chip ar8316_chip = { .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS, + + .reg_port_stats_start = 0x20000, + .reg_port_stats_length = 0x100, + .hw_init = ar8316_hw_init, .init_globals = ar8316_init_globals, .init_port = ar8216_init_port, @@ -998,9 +1065,11 @@ static const struct ar8xxx_chip ar8316_chip = { .atu_flush = ar8216_atu_flush, .vtu_flush = ar8216_vtu_flush, .vtu_load_vlan = ar8216_vtu_load_vlan, + .set_mirror_regs = ar8216_set_mirror_regs, .num_mibs = ARRAY_SIZE(ar8236_mibs), .mib_decs = ar8236_mibs, + .mib_func = AR8216_REG_MIB_FUNC }; static u32 @@ -1360,7 +1429,7 @@ static int ar8327_led_create(struct ar8xxx_priv *priv, const struct ar8327_led_info *led_info) { - struct ar8327_data *data = &priv->chip_data.ar8327; + struct ar8327_data *data = priv->chip_data; struct ar8327_led *aled; int ret; @@ -1421,14 +1490,12 @@ ar8327_led_destroy(struct ar8327_led *aled) static void ar8327_leds_init(struct ar8xxx_priv *priv) { - struct ar8327_data *data; + struct ar8327_data *data = priv->chip_data; unsigned i; if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS)) return; - data = &priv->chip_data.ar8327; - for (i = 0; i < data->num_leds; i++) { struct ar8327_led *aled; @@ -1446,7 +1513,7 @@ ar8327_leds_init(struct ar8xxx_priv *priv) static void ar8327_leds_cleanup(struct ar8xxx_priv *priv) { - struct ar8327_data *data = &priv->chip_data.ar8327; + struct ar8327_data *data = priv->chip_data; unsigned i; if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS)) @@ -1467,7 +1534,7 @@ ar8327_hw_config_pdata(struct ar8xxx_priv *priv, struct ar8327_platform_data *pdata) { struct ar8327_led_cfg *led_cfg; - struct ar8327_data *data; + struct ar8327_data *data = priv->chip_data; u32 pos, new_pos; u32 t; @@ -1476,8 +1543,6 @@ ar8327_hw_config_pdata(struct ar8xxx_priv *priv, priv->get_port_link = pdata->get_port_link; - data = &priv->chip_data.ar8327; - data->port0_status = ar8327_get_port_init_status(&pdata->port0_cfg); data->port6_status = ar8327_get_port_init_status(&pdata->port6_cfg); @@ -1550,6 +1615,7 @@ ar8327_hw_config_pdata(struct ar8xxx_priv *priv, static int ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np) { + struct ar8327_data *data = priv->chip_data; const __be32 *paddr; int len; int i; @@ -1569,10 +1635,10 @@ ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np) switch (reg) { case AR8327_REG_PORT_STATUS(0): - priv->chip_data.ar8327.port0_status = val; + data->port0_status = val; break; case AR8327_REG_PORT_STATUS(6): - priv->chip_data.ar8327.port6_status = val; + data->port6_status = val; break; default: priv->write(priv, reg, val); @@ -1593,9 +1659,11 @@ ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np) static int ar8327_hw_init(struct ar8xxx_priv *priv) { - struct mii_bus *bus; int ret; - int i; + + priv->chip_data = kzalloc(sizeof(struct ar8327_data), GFP_KERNEL); + if (!priv->chip_data) + return -ENOMEM; if (priv->phy->dev.of_node) ret = ar8327_hw_config_of(priv, priv->phy->dev.of_node); @@ -1608,19 +1676,7 @@ ar8327_hw_init(struct ar8xxx_priv *priv) ar8327_leds_init(priv); - bus = priv->mii_bus; - for (i = 0; i < AR8327_NUM_PHYS; i++) { - ar8327_phy_fixup(priv, i); - - /* 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); + ar8xxx_phy_init(priv); return 0; } @@ -1668,12 +1724,13 @@ ar8327_init_globals(struct ar8xxx_priv *priv) static void ar8327_init_port(struct ar8xxx_priv *priv, int port) { + struct ar8327_data *data = priv->chip_data; u32 t; if (port == AR8216_PORT_CPU) - t = priv->chip_data.ar8327.port0_status; + t = data->port0_status; else if (port == 6) - t = priv->chip_data.ar8327.port6_status; + t = data->port6_status; else t = AR8216_PORT_STATUS_LINK_AUTO; @@ -1790,6 +1847,12 @@ ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 members) static const struct ar8xxx_chip ar8327_chip = { .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS, + .config_at_probe = true, + .mii_lo_first = true, + + .reg_port_stats_start = 0x1000, + .reg_port_stats_length = 0x100, + .hw_init = ar8327_hw_init, .cleanup = ar8327_cleanup, .init_globals = ar8327_init_globals, @@ -1799,9 +1862,12 @@ static const struct ar8xxx_chip ar8327_chip = { .atu_flush = ar8327_atu_flush, .vtu_flush = ar8327_vtu_flush, .vtu_load_vlan = ar8327_vtu_load_vlan, + .phy_fixup = ar8327_phy_fixup, + .set_mirror_regs = ar8327_set_mirror_regs, .num_mibs = ARRAY_SIZE(ar8236_mibs), .mib_decs = ar8236_mibs, + .mib_func = AR8327_REG_MIB_FUNC }; static int @@ -2059,16 +2125,6 @@ ar8216_set_mirror_regs(struct ar8xxx_priv *priv) AR8216_PORT_CTRL_MIRROR_TX); } -static void -ar8xxx_set_mirror_regs(struct ar8xxx_priv *priv) -{ - if (chip_is_ar8327(priv) || chip_is_ar8337(priv)) { - ar8327_set_mirror_regs(priv); - } else { - ar8216_set_mirror_regs(priv); - } -} - static int ar8xxx_sw_hw_apply(struct switch_dev *dev) { @@ -2116,7 +2172,7 @@ ar8xxx_sw_hw_apply(struct switch_dev *dev) priv->chip->setup_port(priv, i, portmask[i]); } - ar8xxx_set_mirror_regs(priv); + priv->chip->set_mirror_regs(priv); mutex_unlock(&priv->reg_mutex); return 0; @@ -2188,7 +2244,7 @@ ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev, mutex_lock(&priv->reg_mutex); priv->mirror_rx = !!val->value.i; - ar8xxx_set_mirror_regs(priv); + priv->chip->set_mirror_regs(priv); mutex_unlock(&priv->reg_mutex); return 0; @@ -2213,7 +2269,7 @@ ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev, mutex_lock(&priv->reg_mutex); priv->mirror_tx = !!val->value.i; - ar8xxx_set_mirror_regs(priv); + priv->chip->set_mirror_regs(priv); mutex_unlock(&priv->reg_mutex); return 0; @@ -2238,7 +2294,7 @@ ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev, mutex_lock(&priv->reg_mutex); priv->monitor_port = val->value.i; - ar8xxx_set_mirror_regs(priv); + priv->chip->set_mirror_regs(priv); mutex_unlock(&priv->reg_mutex); return 0; @@ -2263,7 +2319,7 @@ ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev, mutex_lock(&priv->reg_mutex); priv->source_port = val->value.i; - ar8xxx_set_mirror_regs(priv); + priv->chip->set_mirror_regs(priv); mutex_unlock(&priv->reg_mutex); return 0; @@ -2564,11 +2620,9 @@ ar8xxx_id_chip(struct ar8xxx_priv *priv) priv->chip = &ar8316_chip; break; case AR8XXX_VER_AR8327: - priv->mii_lo_first = true; priv->chip = &ar8327_chip; break; case AR8XXX_VER_AR8337: - priv->mii_lo_first = true; priv->chip = &ar8327_chip; break; default: @@ -2668,6 +2722,7 @@ ar8xxx_free(struct ar8xxx_priv *priv) if (priv->chip && priv->chip->cleanup) priv->chip->cleanup(priv); + kfree(priv->chip_data); kfree(priv->mib_stats); kfree(priv); } @@ -2765,8 +2820,8 @@ ar8xxx_phy_config_init(struct phy_device *phydev) if (WARN_ON(!priv)) return -ENODEV; - if (chip_is_ar8327(priv) || chip_is_ar8337(priv)) - return 0; + if (priv->chip->config_at_probe) + return ar8xxx_phy_check_aneg(phydev); priv->phy = phydev; @@ -2941,7 +2996,7 @@ found: phydev->advertising = ADVERTISED_100baseT_Full; } - if (chip_is_ar8327(priv) || chip_is_ar8337(priv)) { + if (priv->chip->config_at_probe) { priv->phy = phydev; ret = ar8xxx_start(priv); @@ -3011,6 +3066,15 @@ ar8xxx_phy_remove(struct phy_device *phydev) ar8xxx_free(priv); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0) +static int +ar8xxx_phy_soft_reset(struct phy_device *phydev) +{ + /* we don't need an extra reset */ + return 0; +} +#endif + static struct phy_driver ar8xxx_phy_driver = { .phy_id = 0x004d0000, .name = "Atheros AR8216/AR8236/AR8316", @@ -3022,6 +3086,9 @@ static struct phy_driver ar8xxx_phy_driver = { .config_init = ar8xxx_phy_config_init, .config_aneg = ar8xxx_phy_config_aneg, .read_status = ar8xxx_phy_read_status, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0) + .soft_reset = ar8xxx_phy_soft_reset, +#endif .driver = { .owner = THIS_MODULE }, };