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=817aca57d593e7435498bebd78cedf3b6e2cbef4;hp=76dfcf4c015e7bf34c73b1ebf38f4b6edd143dbe;hb=bdf8124cd22c1d9a027f6ca7f56230e353cf83ff;hpb=b97299dc68f0dc2ae3fc7df2303427da1980b347 diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c index 76dfcf4c01..817aca57d5 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c @@ -134,19 +134,6 @@ const struct ar8xxx_mib_desc ar8236_mibs[39] = { static DEFINE_MUTEX(ar8xxx_dev_list_lock); static LIST_HEAD(ar8xxx_dev_list); -static inline void -split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) -{ - regaddr >>= 1; - *r1 = regaddr & 0x1e; - - regaddr >>= 5; - *r2 = regaddr & 0x7; - - regaddr >>= 3; - *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) @@ -217,8 +204,8 @@ ar8xxx_phy_init(struct ar8xxx_priv *priv) ar8xxx_phy_poll_reset(bus); } -static u32 -mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum) +u32 +ar8xxx_mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum) { struct mii_bus *bus = priv->mii_bus; u16 lo, hi; @@ -229,8 +216,8 @@ mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum) return (hi << 16) | lo; } -static void -mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val) +void +ar8xxx_mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val) { struct mii_bus *bus = priv->mii_bus; u16 lo, hi; @@ -260,8 +247,8 @@ ar8xxx_read(struct ar8xxx_priv *priv, int reg) mutex_lock(&bus->mdio_lock); bus->write(bus, 0x18, 0, page); - usleep_range(1000, 2000); /* wait for the page switch to propagate */ - val = mii_read32(priv, 0x10 | r2, r1); + wait_for_page_switch(); + val = ar8xxx_mii_read32(priv, 0x10 | r2, r1); mutex_unlock(&bus->mdio_lock); @@ -279,8 +266,8 @@ ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val) mutex_lock(&bus->mdio_lock); bus->write(bus, 0x18, 0, page); - usleep_range(1000, 2000); /* wait for the page switch to propagate */ - mii_write32(priv, 0x10 | r2, r1, val); + wait_for_page_switch(); + ar8xxx_mii_write32(priv, 0x10 | r2, r1, val); mutex_unlock(&bus->mdio_lock); } @@ -297,12 +284,12 @@ ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) mutex_lock(&bus->mdio_lock); bus->write(bus, 0x18, 0, page); - usleep_range(1000, 2000); /* wait for the page switch to propagate */ + wait_for_page_switch(); - ret = mii_read32(priv, 0x10 | r2, r1); + ret = ar8xxx_mii_read32(priv, 0x10 | r2, r1); ret &= ~mask; ret |= val; - mii_write32(priv, 0x10 | r2, r1, ret); + ar8xxx_mii_write32(priv, 0x10 | r2, r1, ret); mutex_unlock(&bus->mdio_lock); @@ -332,6 +319,20 @@ ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 data) mutex_unlock(&bus->mdio_lock); } +u16 +ar8xxx_phy_mmd_read(struct ar8xxx_priv *priv, int phy_addr, u16 addr) +{ + struct mii_bus *bus = priv->mii_bus; + u16 data; + + mutex_lock(&bus->mdio_lock); + bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr); + data = bus->read(bus, phy_addr, MII_ATH_MMD_DATA); + mutex_unlock(&bus->mdio_lock); + + return data; +} + static int ar8xxx_reg_wait(struct ar8xxx_priv *priv, u32 reg, u32 mask, u32 val, unsigned timeout) @@ -453,6 +454,9 @@ ar8216_read_port_link(struct ar8xxx_priv *priv, int port, link->tx_flow = !!(status & AR8216_PORT_STATUS_TXFLOW); link->rx_flow = !!(status & AR8216_PORT_STATUS_RXFLOW); + if (link->aneg && link->duplex && priv->chip->read_port_eee_status) + link->eee = priv->chip->read_port_eee_status(priv, port); + speed = (status & AR8216_PORT_STATUS_SPEED) >> AR8216_PORT_STATUS_SPEED_S; @@ -594,9 +598,26 @@ ar8216_atu_flush(struct ar8xxx_priv *priv) { int ret; - ret = ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0); + ret = ar8216_wait_bit(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_ACTIVE, 0); if (!ret) - ar8xxx_write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH); + ar8xxx_write(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_OP_FLUSH | + AR8216_ATU_ACTIVE); + + return ret; +} + +static int +ar8216_atu_flush_port(struct ar8xxx_priv *priv, int port) +{ + u32 t; + int ret; + + ret = ar8216_wait_bit(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_ACTIVE, 0); + if (!ret) { + t = (port << AR8216_ATU_PORT_NUM_S) | AR8216_ATU_OP_FLUSH_PORT; + t |= AR8216_ATU_ACTIVE; + ar8xxx_write(priv, AR8216_REG_ATU_FUNC0, t); + } return ret; } @@ -697,6 +718,77 @@ ar8216_init_port(struct ar8xxx_priv *priv, int port) } 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); + + if (!timeout) + pr_err("ar8216: timeout waiting for atu to become ready\n"); +} + +static void ar8216_get_arl_entry(struct ar8xxx_priv *priv, + struct arl_entry *a, u32 *status, enum arl_op op) +{ + struct mii_bus *bus = priv->mii_bus; + 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; + + r1_func1 = (AR8216_REG_ATU_FUNC1 >> 1) & 0x1e; + r1_func2 = (AR8216_REG_ATU_FUNC2 >> 1) & 0x1e; + + switch (op) { + case AR8XXX_ARL_INITIALIZE: + /* all ATU registers are on the same page + * therefore set page only once + */ + bus->write(bus, 0x18, 0, page); + wait_for_page_switch(); + + ar8216_wait_atu_ready(priv, r2, r1_func0); + + ar8xxx_mii_write32(priv, r2, r1_func0, AR8216_ATU_OP_GET_NEXT); + ar8xxx_mii_write32(priv, r2, r1_func1, 0); + ar8xxx_mii_write32(priv, r2, r1_func2, 0); + break; + case AR8XXX_ARL_GET_NEXT: + t = ar8xxx_mii_read32(priv, r2, r1_func0); + t |= AR8216_ATU_ACTIVE; + ar8xxx_mii_write32(priv, r2, r1_func0, t); + ar8216_wait_atu_ready(priv, r2, r1_func0); + + val0 = ar8xxx_mii_read32(priv, r2, r1_func0); + val1 = ar8xxx_mii_read32(priv, r2, r1_func1); + val2 = ar8xxx_mii_read32(priv, r2, r1_func2); + + *status = (val2 & AR8216_ATU_STATUS) >> AR8216_ATU_STATUS_S; + if (!*status) + break; + + i = 0; + t = AR8216_ATU_PORT0; + while (!(val2 & t) && ++i < priv->dev.ports) + t <<= 1; + + a->port = i; + 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; + a->mac[3] = (val1 & AR8216_ATU_ADDR2) >> AR8216_ATU_ADDR2_S; + a->mac[4] = (val1 & AR8216_ATU_ADDR1) >> AR8216_ATU_ADDR1_S; + a->mac[5] = (val1 & AR8216_ATU_ADDR0) >> AR8216_ATU_ADDR0_S; + break; + } +} + +static void ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 members) { u32 egress, ingress; @@ -957,13 +1049,11 @@ ar8216_set_mirror_regs(struct ar8xxx_priv *priv) AR8216_GLOBAL_CPUPORT_MIRROR_PORT, (0xF << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S)); for (port = 0; port < AR8216_NUM_PORTS; port++) { - ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port), - AR8216_PORT_CTRL_MIRROR_RX, - 0); + ar8xxx_reg_clear(priv, AR8216_REG_PORT_CTRL(port), + AR8216_PORT_CTRL_MIRROR_RX); - ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port), - AR8216_PORT_CTRL_MIRROR_TX, - 0); + ar8xxx_reg_clear(priv, AR8216_REG_PORT_CTRL(port), + AR8216_PORT_CTRL_MIRROR_TX); } /* now enable mirroring if necessary */ @@ -986,10 +1076,25 @@ ar8216_set_mirror_regs(struct ar8xxx_priv *priv) AR8216_PORT_CTRL_MIRROR_TX); } +static inline u32 +ar8xxx_age_time_val(int age_time) +{ + return (age_time + AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS / 2) / + AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS; +} + +static inline void +ar8xxx_set_age_time(struct ar8xxx_priv *priv, int reg) +{ + u32 age_time = ar8xxx_age_time_val(priv->arl_age_time); + ar8xxx_rmw(priv, reg, AR8216_ATU_CTRL_AGE_TIME, age_time << AR8216_ATU_CTRL_AGE_TIME_S); +} + int ar8xxx_sw_hw_apply(struct switch_dev *dev) { struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + const struct ar8xxx_chip *chip = priv->chip; u8 portmask[AR8X16_MAX_PORTS]; int i, j; @@ -1013,8 +1118,8 @@ ar8xxx_sw_hw_apply(struct switch_dev *dev) portmask[i] |= vp & ~mask; } - priv->chip->vtu_load_vlan(priv, priv->vlan_id[j], - priv->vlan_table[j]); + chip->vtu_load_vlan(priv, priv->vlan_id[j], + priv->vlan_table[j]); } } else { /* vlan disabled: @@ -1030,10 +1135,14 @@ ar8xxx_sw_hw_apply(struct switch_dev *dev) /* update the port destination mask registers and tag settings */ for (i = 0; i < dev->ports; i++) { - priv->chip->setup_port(priv, i, portmask[i]); + chip->setup_port(priv, i, portmask[i]); } - priv->chip->set_mirror_regs(priv); + chip->set_mirror_regs(priv); + + /* set age time */ + if (chip->reg_arl_ctrl) + ar8xxx_set_age_time(priv, chip->reg_arl_ctrl); mutex_unlock(&priv->reg_mutex); return 0; @@ -1043,6 +1152,7 @@ int ar8xxx_sw_reset_switch(struct switch_dev *dev) { struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + const struct ar8xxx_chip *chip = priv->chip; int i; mutex_lock(&priv->reg_mutex); @@ -1054,18 +1164,19 @@ ar8xxx_sw_reset_switch(struct switch_dev *dev) /* Configure all ports */ for (i = 0; i < dev->ports; i++) - priv->chip->init_port(priv, i); + chip->init_port(priv, i); priv->mirror_rx = false; priv->mirror_tx = false; priv->source_port = 0; priv->monitor_port = 0; + priv->arl_age_time = AR8XXX_DEFAULT_ARL_AGE_TIME; - priv->chip->init_globals(priv); + chip->init_globals(priv); mutex_unlock(&priv->reg_mutex); - return ar8xxx_sw_hw_apply(dev); + return chip->sw_hw_apply(dev); } int @@ -1196,7 +1307,7 @@ ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev, return 0; } -static int +int ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) @@ -1226,18 +1337,46 @@ unlock: return ret; } -static int +static void +ar8xxx_byte_to_str(char *buf, int len, u64 byte) +{ + unsigned long b; + const char *unit; + + if (byte >= 0x40000000) { /* 1 GiB */ + b = byte * 10 / 0x40000000; + unit = "GiB"; + } else if (byte >= 0x100000) { /* 1 MiB */ + b = byte * 10 / 0x100000; + unit = "MiB"; + } else if (byte >= 0x400) { /* 1 KiB */ + b = byte * 10 / 0x400; + unit = "KiB"; + } else { + b = byte; + unit = "Byte"; + } + if (strcmp(unit, "Byte")) + snprintf(buf, len, "%lu.%lu %s", b / 10, b % 10, unit); + else + snprintf(buf, len, "%lu %s", b, unit); +} + +int ar8xxx_sw_get_port_mib(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); const struct ar8xxx_chip *chip = priv->chip; - u64 *mib_stats; + u64 *mib_stats, mib_data; int port; int ret; char *buf = priv->buf; + char buf1[64]; + const char *mib_name; int i, len = 0; + bool mib_stats_empty = true; if (!ar8xxx_has_mib_counters(priv)) return -EOPNOTSUPP; @@ -1254,15 +1393,28 @@ ar8xxx_sw_get_port_mib(struct switch_dev *dev, ar8xxx_mib_fetch_port_stat(priv, port, false); len += snprintf(buf + len, sizeof(priv->buf) - len, - "Port %d MIB counters\n", - port); + "MIB counters\n"); mib_stats = &priv->mib_stats[port * chip->num_mibs]; - for (i = 0; i < chip->num_mibs; i++) + for (i = 0; i < chip->num_mibs; i++) { + mib_name = chip->mib_decs[i].name; + mib_data = mib_stats[i]; len += snprintf(buf + len, sizeof(priv->buf) - len, - "%-12s: %llu\n", - chip->mib_decs[i].name, - mib_stats[i]); + "%-12s: %llu\n", mib_name, mib_data); + if ((!strcmp(mib_name, "TxByte") || + !strcmp(mib_name, "RxGoodByte")) && + mib_data >= 1024) { + ar8xxx_byte_to_str(buf1, sizeof(buf1), mib_data); + --len; /* discard newline at the end of buf */ + len += snprintf(buf + len, sizeof(priv->buf) - len, + " (%s)\n", buf1); + } + if (mib_stats_empty && mib_data) + mib_stats_empty = false; + } + + if (mib_stats_empty) + len = snprintf(buf, sizeof(priv->buf), "No MIB data"); val->value.s = buf; val->len = len; @@ -1274,6 +1426,139 @@ unlock: return ret; } +int +ar8xxx_sw_set_arl_age_time(struct switch_dev *dev, const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + int age_time = val->value.i; + u32 age_time_val; + + if (age_time < 0) + return -EINVAL; + + age_time_val = ar8xxx_age_time_val(age_time); + if (age_time_val == 0 || age_time_val > 0xffff) + return -EINVAL; + + priv->arl_age_time = age_time; + return 0; +} + +int +ar8xxx_sw_get_arl_age_time(struct switch_dev *dev, const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + val->value.i = priv->arl_age_time; + return 0; +} + +int +ar8xxx_sw_get_arl_table(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + struct mii_bus *bus = priv->mii_bus; + const struct ar8xxx_chip *chip = priv->chip; + char *buf = priv->arl_buf; + int i, j, k, len = 0; + struct arl_entry *a, *a1; + u32 status; + + if (!chip->get_arl_entry) + return -EOPNOTSUPP; + + mutex_lock(&priv->reg_mutex); + mutex_lock(&bus->mdio_lock); + + chip->get_arl_entry(priv, NULL, NULL, AR8XXX_ARL_INITIALIZE); + + for(i = 0; i < AR8XXX_NUM_ARL_RECORDS; ++i) { + a = &priv->arl_table[i]; + duplicate: + chip->get_arl_entry(priv, a, &status, AR8XXX_ARL_GET_NEXT); + + if (!status) + break; + + /* avoid duplicates + * ARL table can include multiple valid entries + * per MAC, just with differing status codes + */ + 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; + } + } + + mutex_unlock(&bus->mdio_lock); + + len += snprintf(buf + len, sizeof(priv->arl_buf) - len, + "address resolution table\n"); + + if (i == AR8XXX_NUM_ARL_RECORDS) + len += snprintf(buf + len, sizeof(priv->arl_buf) - len, + "Too many entries found, displaying the first %d only!\n", + AR8XXX_NUM_ARL_RECORDS); + + for (j = 0; j < priv->dev.ports; ++j) { + for (k = 0; k < i; ++k) { + a = &priv->arl_table[k]; + if (a->port != j) + continue; + len += snprintf(buf + len, sizeof(priv->arl_buf) - len, + "Port %d: MAC %02x:%02x:%02x:%02x:%02x:%02x\n", + j, + a->mac[5], a->mac[4], a->mac[3], + a->mac[2], a->mac[1], a->mac[0]); + } + } + + val->value.s = buf; + val->len = len; + + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +int +ar8xxx_sw_set_flush_arl_table(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + int ret; + + mutex_lock(&priv->reg_mutex); + ret = priv->chip->atu_flush(priv); + mutex_unlock(&priv->reg_mutex); + + return ret; +} + +int +ar8xxx_sw_set_flush_port_arl_table(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + int port, ret; + + port = val->port_vlan; + if (port >= dev->ports) + return -EINVAL; + + mutex_lock(&priv->reg_mutex); + ret = priv->chip->atu_flush_port(priv, port); + mutex_unlock(&priv->reg_mutex); + + return ret; +} + static const struct switch_attr ar8xxx_sw_attr_globals[] = { { .type = SWITCH_TYPE_INT, @@ -1321,9 +1606,22 @@ static const struct switch_attr ar8xxx_sw_attr_globals[] = { .get = ar8xxx_sw_get_mirror_source_port, .max = AR8216_NUM_PORTS - 1 }, + { + .type = SWITCH_TYPE_STRING, + .name = "arl_table", + .description = "Get ARL table", + .set = NULL, + .get = ar8xxx_sw_get_arl_table, + }, + { + .type = SWITCH_TYPE_NOVAL, + .name = "flush_arl_table", + .description = "Flush ARL table", + .set = ar8xxx_sw_set_flush_arl_table, + }, }; -const struct switch_attr ar8xxx_sw_attr_port[2] = { +const struct switch_attr ar8xxx_sw_attr_port[] = { { .type = SWITCH_TYPE_NOVAL, .name = "reset_mib", @@ -1337,6 +1635,12 @@ const struct switch_attr ar8xxx_sw_attr_port[2] = { .set = NULL, .get = ar8xxx_sw_get_port_mib, }, + { + .type = SWITCH_TYPE_NOVAL, + .name = "flush_arl_table", + .description = "Flush port's ARL table entries", + .set = ar8xxx_sw_set_flush_port_arl_table, + }, }; const struct switch_attr ar8xxx_sw_attr_vlan[1] = { @@ -1377,6 +1681,7 @@ static const struct ar8xxx_chip ar8216_chip = { .reg_port_stats_start = 0x19000, .reg_port_stats_length = 0xa0, + .reg_arl_ctrl = AR8216_REG_ATU_CTRL, .name = "Atheros AR8216", .ports = AR8216_NUM_PORTS, @@ -1389,9 +1694,12 @@ static const struct ar8xxx_chip ar8216_chip = { .setup_port = ar8216_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(ar8216_mibs), .mib_decs = ar8216_mibs, @@ -1403,6 +1711,7 @@ static const struct ar8xxx_chip ar8236_chip = { .reg_port_stats_start = 0x20000, .reg_port_stats_length = 0x100, + .reg_arl_ctrl = AR8216_REG_ATU_CTRL, .name = "Atheros AR8236", .ports = AR8216_NUM_PORTS, @@ -1415,9 +1724,12 @@ static const struct ar8xxx_chip ar8236_chip = { .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, @@ -1429,6 +1741,7 @@ static const struct ar8xxx_chip ar8316_chip = { .reg_port_stats_start = 0x20000, .reg_port_stats_length = 0x100, + .reg_arl_ctrl = AR8216_REG_ATU_CTRL, .name = "Atheros AR8316", .ports = AR8216_NUM_PORTS, @@ -1441,9 +1754,12 @@ static const struct ar8xxx_chip ar8316_chip = { .setup_port = ar8216_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, @@ -1687,12 +2003,44 @@ ar8xxx_phy_config_init(struct phy_device *phydev) return 0; } +static bool +ar8xxx_check_link_states(struct ar8xxx_priv *priv) +{ + bool link_new, changed = false; + u32 status; + int i; + + mutex_lock(&priv->reg_mutex); + + for (i = 0; i < priv->dev.ports; i++) { + status = priv->chip->read_port_status(priv, i); + link_new = !!(status & AR8216_PORT_STATUS_LINK_UP); + if (link_new == priv->link_up[i]) + continue; + + priv->link_up[i] = link_new; + changed = true; + /* flush ARL entries for this port if it went down*/ + if (!link_new) + priv->chip->atu_flush_port(priv, i); + dev_info(&priv->phy->dev, "Port %d is %s\n", + i, link_new ? "up" : "down"); + } + + mutex_unlock(&priv->reg_mutex); + + return changed; +} + static int ar8xxx_phy_read_status(struct phy_device *phydev) { struct ar8xxx_priv *priv = phydev->priv; struct switch_port_link link; - int ret; + + /* check for switch port link changes */ + if (phydev->state == PHY_CHANGELINK) + ar8xxx_check_link_states(priv); if (phydev->addr != 0) return genphy_read_status(phydev); @@ -1717,16 +2065,11 @@ ar8xxx_phy_read_status(struct phy_device *phydev) } phydev->duplex = link.duplex ? DUPLEX_FULL : DUPLEX_HALF; - /* flush the address translation unit */ - mutex_lock(&priv->reg_mutex); - ret = priv->chip->atu_flush(priv); - mutex_unlock(&priv->reg_mutex); - phydev->state = PHY_RUNNING; netif_carrier_on(phydev->attached_dev); phydev->adjust_link(phydev->attached_dev); - return ret; + return 0; } static int