diff options
Diffstat (limited to 'drivers/net/dsa/microchip')
-rw-r--r-- | drivers/net/dsa/microchip/ksz9477.c | 390 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz9477_spi.c | 4 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz_common.c | 150 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz_common.h | 23 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz_priv.h | 15 |
5 files changed, 508 insertions, 74 deletions
diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 89ed059bb576..f16e1d7d8615 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -2,24 +2,26 @@ /* * Microchip KSZ9477 switch driver main logic * - * Copyright (C) 2017-2018 Microchip Technology Inc. + * Copyright (C) 2017-2019 Microchip Technology Inc. */ -#include <linux/delay.h> -#include <linux/export.h> -#include <linux/gpio.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/iopoll.h> #include <linux/platform_data/microchip-ksz.h> #include <linux/phy.h> -#include <linux/etherdevice.h> #include <linux/if_bridge.h> #include <net/dsa.h> #include <net/switchdev.h> #include "ksz_priv.h" -#include "ksz_common.h" #include "ksz9477_reg.h" +#include "ksz_common.h" + +/* Used with variable features to indicate capabilities. */ +#define GBIT_SUPPORT BIT(0) +#define NEW_XMII BIT(1) +#define IS_9893 BIT(2) static const struct { int index; @@ -259,10 +261,84 @@ static int ksz9477_reset_switch(struct ksz_device *dev) return 0; } +static void ksz9477_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, + u64 *cnt) +{ + struct ksz_poll_ctx ctx = { + .dev = dev, + .port = port, + .offset = REG_PORT_MIB_CTRL_STAT__4, + }; + struct ksz_port *p = &dev->ports[port]; + u32 data; + int ret; + + /* retain the flush/freeze bit */ + data = p->freeze ? MIB_COUNTER_FLUSH_FREEZE : 0; + data |= MIB_COUNTER_READ; + data |= (addr << MIB_COUNTER_INDEX_S); + ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, data); + + ret = readx_poll_timeout(ksz_pread32_poll, &ctx, data, + !(data & MIB_COUNTER_READ), 10, 1000); + + /* failed to read MIB. get out of loop */ + if (ret < 0) { + dev_dbg(dev->dev, "Failed to get MIB\n"); + return; + } + + /* count resets upon read */ + ksz_pread32(dev, port, REG_PORT_MIB_DATA, &data); + *cnt += data; +} + +static void ksz9477_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, + u64 *dropped, u64 *cnt) +{ + addr = ksz9477_mib_names[addr].index; + ksz9477_r_mib_cnt(dev, port, addr, cnt); +} + +static void ksz9477_freeze_mib(struct ksz_device *dev, int port, bool freeze) +{ + u32 val = freeze ? MIB_COUNTER_FLUSH_FREEZE : 0; + struct ksz_port *p = &dev->ports[port]; + + /* enable/disable the port for flush/freeze function */ + mutex_lock(&p->mib.cnt_mutex); + ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, val); + + /* used by MIB counter reading code to know freeze is enabled */ + p->freeze = freeze; + mutex_unlock(&p->mib.cnt_mutex); +} + +static void ksz9477_port_init_cnt(struct ksz_device *dev, int port) +{ + struct ksz_port_mib *mib = &dev->ports[port].mib; + + /* flush all enabled port MIB counters */ + mutex_lock(&mib->cnt_mutex); + ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, + MIB_COUNTER_FLUSH_FREEZE); + ksz_write8(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FLUSH); + ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, 0); + mutex_unlock(&mib->cnt_mutex); + + mib->cnt_ptr = 0; + memset(mib->counters, 0, dev->mib_cnt * sizeof(u64)); +} + static enum dsa_tag_protocol ksz9477_get_tag_protocol(struct dsa_switch *ds, int port) { - return DSA_TAG_PROTO_KSZ9477; + enum dsa_tag_protocol proto = DSA_TAG_PROTO_KSZ9477; + struct ksz_device *dev = ds->priv; + + if (dev->features & IS_9893) + proto = DSA_TAG_PROTO_KSZ9893; + return proto; } static int ksz9477_phy_read16(struct dsa_switch *ds, int addr, int reg) @@ -323,6 +399,10 @@ static int ksz9477_phy_write16(struct dsa_switch *ds, int addr, int reg, /* No real PHY after this. */ if (addr >= dev->phy_port_cnt) return 0; + + /* No gigabit support. Do not write to this register. */ + if (!(dev->features & GBIT_SUPPORT) && reg == MII_CTRL1000) + return 0; ksz_pwrite16(dev, addr, 0x100 + (reg << 1), val); return 0; @@ -342,47 +422,6 @@ static void ksz9477_get_strings(struct dsa_switch *ds, int port, } } -static void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, - uint64_t *buf) -{ - struct ksz_device *dev = ds->priv; - int i; - u32 data; - int timeout; - - mutex_lock(&dev->stats_mutex); - - for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) { - data = MIB_COUNTER_READ; - data |= ((ksz9477_mib_names[i].index & 0xFF) << - MIB_COUNTER_INDEX_S); - ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, data); - - timeout = 1000; - do { - ksz_pread32(dev, port, REG_PORT_MIB_CTRL_STAT__4, - &data); - usleep_range(1, 10); - if (!(data & MIB_COUNTER_READ)) - break; - } while (timeout-- > 0); - - /* failed to read MIB. get out of loop */ - if (!timeout) { - dev_dbg(dev->dev, "Failed to get MIB\n"); - break; - } - - /* count resets upon read */ - ksz_pread32(dev, port, REG_PORT_MIB_DATA, &data); - - dev->mib_value[i] += (uint64_t)data; - buf[i] = dev->mib_value[i]; - } - - mutex_unlock(&dev->stats_mutex); -} - static void ksz9477_cfg_port_member(struct ksz_device *dev, int port, u8 member) { @@ -397,6 +436,7 @@ static void ksz9477_port_stp_state_set(struct dsa_switch *ds, int port, struct ksz_port *p = &dev->ports[port]; u8 data; int member = -1; + int forward = dev->member; ksz_pread8(dev, port, P_STP_CTRL, &data); data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE); @@ -424,12 +464,14 @@ static void ksz9477_port_stp_state_set(struct dsa_switch *ds, int port, break; member = dev->host_mask | p->vid_member; + mutex_lock(&dev->dev_mutex); /* Port is a member of a bridge. */ if (dev->br_member & (1 << port)) { dev->member |= (1 << port); member = dev->member; } + mutex_unlock(&dev->dev_mutex); break; case BR_STATE_BLOCKING: data |= PORT_LEARN_DISABLE; @@ -444,6 +486,7 @@ static void ksz9477_port_stp_state_set(struct dsa_switch *ds, int port, ksz_pwrite8(dev, port, P_STP_CTRL, data); p->stp_state = state; + mutex_lock(&dev->dev_mutex); if (data & PORT_RX_ENABLE) dev->rx_ports |= (1 << port); else @@ -464,10 +507,11 @@ static void ksz9477_port_stp_state_set(struct dsa_switch *ds, int port, } /* When topology has changed the function ksz_update_port_member - * should be called to modify port forwarding behavior. However - * as the offload_fwd_mark indication cannot be reported here - * the switch forwarding function is not enabled. + * should be called to modify port forwarding behavior. */ + if (forward != dev->member) + ksz_update_port_member(dev, port); + mutex_unlock(&dev->dev_mutex); } static void ksz9477_flush_dyn_mac_table(struct ksz_device *dev, int port) @@ -965,6 +1009,161 @@ static void ksz9477_port_mirror_del(struct dsa_switch *ds, int port, PORT_MIRROR_SNIFFER, false); } +static void ksz9477_phy_setup(struct ksz_device *dev, int port, + struct phy_device *phy) +{ + /* Only apply to port with PHY. */ + if (port >= dev->phy_port_cnt) + return; + + /* The MAC actually cannot run in 1000 half-duplex mode. */ + phy_remove_link_mode(phy, + ETHTOOL_LINK_MODE_1000baseT_Half_BIT); + + /* PHY does not support gigabit. */ + if (!(dev->features & GBIT_SUPPORT)) + phy_remove_link_mode(phy, + ETHTOOL_LINK_MODE_1000baseT_Full_BIT); +} + +static bool ksz9477_get_gbit(struct ksz_device *dev, u8 data) +{ + bool gbit; + + if (dev->features & NEW_XMII) + gbit = !(data & PORT_MII_NOT_1GBIT); + else + gbit = !!(data & PORT_MII_1000MBIT_S1); + return gbit; +} + +static void ksz9477_set_gbit(struct ksz_device *dev, bool gbit, u8 *data) +{ + if (dev->features & NEW_XMII) { + if (gbit) + *data &= ~PORT_MII_NOT_1GBIT; + else + *data |= PORT_MII_NOT_1GBIT; + } else { + if (gbit) + *data |= PORT_MII_1000MBIT_S1; + else + *data &= ~PORT_MII_1000MBIT_S1; + } +} + +static int ksz9477_get_xmii(struct ksz_device *dev, u8 data) +{ + int mode; + + if (dev->features & NEW_XMII) { + switch (data & PORT_MII_SEL_M) { + case PORT_MII_SEL: + mode = 0; + break; + case PORT_RMII_SEL: + mode = 1; + break; + case PORT_GMII_SEL: + mode = 2; + break; + default: + mode = 3; + } + } else { + switch (data & PORT_MII_SEL_M) { + case PORT_MII_SEL_S1: + mode = 0; + break; + case PORT_RMII_SEL_S1: + mode = 1; + break; + case PORT_GMII_SEL_S1: + mode = 2; + break; + default: + mode = 3; + } + } + return mode; +} + +static void ksz9477_set_xmii(struct ksz_device *dev, int mode, u8 *data) +{ + u8 xmii; + + if (dev->features & NEW_XMII) { + switch (mode) { + case 0: + xmii = PORT_MII_SEL; + break; + case 1: + xmii = PORT_RMII_SEL; + break; + case 2: + xmii = PORT_GMII_SEL; + break; + default: + xmii = PORT_RGMII_SEL; + break; + } + } else { + switch (mode) { + case 0: + xmii = PORT_MII_SEL_S1; + break; + case 1: + xmii = PORT_RMII_SEL_S1; + break; + case 2: + xmii = PORT_GMII_SEL_S1; + break; + default: + xmii = PORT_RGMII_SEL_S1; + break; + } + } + *data &= ~PORT_MII_SEL_M; + *data |= xmii; +} + +static phy_interface_t ksz9477_get_interface(struct ksz_device *dev, int port) +{ + phy_interface_t interface; + bool gbit; + int mode; + u8 data8; + + if (port < dev->phy_port_cnt) + return PHY_INTERFACE_MODE_NA; + ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8); + gbit = ksz9477_get_gbit(dev, data8); + mode = ksz9477_get_xmii(dev, data8); + switch (mode) { + case 2: + interface = PHY_INTERFACE_MODE_GMII; + if (gbit) + break; + case 0: + interface = PHY_INTERFACE_MODE_MII; + break; + case 1: + interface = PHY_INTERFACE_MODE_RMII; + break; + default: + interface = PHY_INTERFACE_MODE_RGMII; + if (data8 & PORT_RGMII_ID_EG_ENABLE) + interface = PHY_INTERFACE_MODE_RGMII_TXID; + if (data8 & PORT_RGMII_ID_IG_ENABLE) { + interface = PHY_INTERFACE_MODE_RGMII_RXID; + if (data8 & PORT_RGMII_ID_EG_ENABLE) + interface = PHY_INTERFACE_MODE_RGMII_ID; + } + break; + } + return interface; +} + static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) { u8 data8; @@ -1011,24 +1210,25 @@ static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) /* configure MAC to 1G & RGMII mode */ ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8); - data8 &= ~PORT_MII_NOT_1GBIT; - data8 &= ~PORT_MII_SEL_M; switch (dev->interface) { case PHY_INTERFACE_MODE_MII: - data8 |= PORT_MII_NOT_1GBIT; - data8 |= PORT_MII_SEL; + ksz9477_set_xmii(dev, 0, &data8); + ksz9477_set_gbit(dev, false, &data8); p->phydev.speed = SPEED_100; break; case PHY_INTERFACE_MODE_RMII: - data8 |= PORT_MII_NOT_1GBIT; - data8 |= PORT_RMII_SEL; + ksz9477_set_xmii(dev, 1, &data8); + ksz9477_set_gbit(dev, false, &data8); p->phydev.speed = SPEED_100; break; case PHY_INTERFACE_MODE_GMII: - data8 |= PORT_GMII_SEL; + ksz9477_set_xmii(dev, 2, &data8); + ksz9477_set_gbit(dev, true, &data8); p->phydev.speed = SPEED_1000; break; default: + ksz9477_set_xmii(dev, 3, &data8); + ksz9477_set_gbit(dev, true, &data8); data8 &= ~PORT_RGMII_ID_IG_ENABLE; data8 &= ~PORT_RGMII_ID_EG_ENABLE; if (dev->interface == PHY_INTERFACE_MODE_RGMII_ID || @@ -1037,13 +1237,13 @@ static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) if (dev->interface == PHY_INTERFACE_MODE_RGMII_ID || dev->interface == PHY_INTERFACE_MODE_RGMII_TXID) data8 |= PORT_RGMII_ID_EG_ENABLE; - data8 |= PORT_RGMII_SEL; p->phydev.speed = SPEED_1000; break; } ksz_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, data8); p->phydev.duplex = 1; } + mutex_lock(&dev->dev_mutex); if (cpu_port) { member = dev->port_mask; dev->on_ports = dev->host_mask; @@ -1056,6 +1256,7 @@ static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) if (p->phydev.link) dev->live_ports |= (1 << port); } + mutex_unlock(&dev->dev_mutex); ksz9477_cfg_port_member(dev, port, member); /* clear pending interrupts */ @@ -1073,10 +1274,25 @@ static void ksz9477_config_cpu_port(struct dsa_switch *ds) for (i = 0; i < dev->port_cnt; i++) { if (dsa_is_cpu_port(ds, i) && (dev->cpu_ports & (1 << i))) { + phy_interface_t interface; + dev->cpu_port = i; dev->host_mask = (1 << dev->cpu_port); dev->port_mask |= dev->host_mask; + /* Read from XMII register to determine host port + * interface. If set specifically in device tree + * note the difference to help debugging. + */ + interface = ksz9477_get_interface(dev, i); + if (!dev->interface) + dev->interface = interface; + if (interface && interface != dev->interface) + dev_info(dev->dev, + "use %s instead of %s\n", + phy_modes(dev->interface), + phy_modes(interface)); + /* enable cpu port */ ksz9477_port_setup(dev, i, true); p = &dev->ports[dev->cpu_port]; @@ -1130,6 +1346,9 @@ static int ksz9477_setup(struct dsa_switch *ds) ksz9477_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY, true); + /* Do not work correctly with tail tagging. */ + ksz_cfg(dev, REG_SW_MAC_CTRL_0, SW_CHECK_LENGTH, false); + /* accept packet up to 2000bytes */ ksz_cfg(dev, REG_SW_MAC_CTRL_1, SW_LEGAL_PACKET_DISABLE, true); @@ -1140,9 +1359,14 @@ static int ksz9477_setup(struct dsa_switch *ds) /* queue based egress rate limit */ ksz_cfg(dev, REG_SW_MAC_CTRL_5, SW_OUT_RATE_LIMIT_QUEUE_BASED, true); + /* enable global MIB counter freeze function */ + ksz_cfg(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FREEZE, true); + /* start switch */ ksz_cfg(dev, REG_SW_OPERATION, SW_START, true); + ksz_init_mib_timer(dev); + return 0; } @@ -1151,6 +1375,7 @@ static const struct dsa_switch_ops ksz9477_switch_ops = { .setup = ksz9477_setup, .phy_read = ksz9477_phy_read16, .phy_write = ksz9477_phy_write16, + .adjust_link = ksz_adjust_link, .port_enable = ksz_enable_port, .port_disable = ksz_disable_port, .get_strings = ksz9477_get_strings, @@ -1182,6 +1407,8 @@ static u32 ksz9477_get_port_addr(int port, int offset) static int ksz9477_switch_detect(struct ksz_device *dev) { u8 data8; + u8 id_hi; + u8 id_lo; u32 id32; int ret; @@ -1199,11 +1426,40 @@ static int ksz9477_switch_detect(struct ksz_device *dev) ret = ksz_read32(dev, REG_CHIP_ID0__1, &id32); if (ret) return ret; + ret = ksz_read8(dev, REG_GLOBAL_OPTIONS, &data8); + if (ret) + return ret; /* Number of ports can be reduced depending on chip. */ dev->mib_port_cnt = TOTAL_PORT_NUM; dev->phy_port_cnt = 5; + /* Default capability is gigabit capable. */ + dev->features = GBIT_SUPPORT; + + id_hi = (u8)(id32 >> 16); + id_lo = (u8)(id32 >> 8); + if ((id_lo & 0xf) == 3) { + /* Chip is from KSZ9893 design. */ + dev->features |= IS_9893; + + /* Chip does not support gigabit. */ + if (data8 & SW_QW_ABLE) + dev->features &= ~GBIT_SUPPORT; + dev->mib_port_cnt = 3; + dev->phy_port_cnt = 2; + } else { + /* Chip uses new XMII register definitions. */ + dev->features |= NEW_XMII; + + /* Chip does not support gigabit. */ + if (!(data8 & SW_GIGABIT_ABLE)) + dev->features &= ~GBIT_SUPPORT; + } + + /* Change chip id to known ones so it can be matched against them. */ + id32 = (id_hi << 16) | (id_lo << 8); + dev->chip_id = id32; return 0; @@ -1238,6 +1494,15 @@ static const struct ksz_chip_data ksz9477_switch_chips[] = { .cpu_ports = 0x7F, /* can be configured as cpu port */ .port_cnt = 7, /* total physical port count */ }, + { + .chip_id = 0x00989300, + .dev_name = "KSZ9893", + .num_vlans = 4096, + .num_alus = 4096, + .num_statics = 16, + .cpu_ports = 0x07, /* can be configured as cpu port */ + .port_cnt = 3, /* total port count */ + }, }; static int ksz9477_switch_init(struct ksz_device *dev) @@ -1276,6 +1541,7 @@ static int ksz9477_switch_init(struct ksz_device *dev) if (!dev->ports) return -ENOMEM; for (i = 0; i < dev->mib_port_cnt; i++) { + mutex_init(&dev->ports[i].mib.cnt_mutex); dev->ports[i].mib.counters = devm_kzalloc(dev->dev, sizeof(u64) * @@ -1284,7 +1550,6 @@ static int ksz9477_switch_init(struct ksz_device *dev) if (!dev->ports[i].mib.counters) return -ENOMEM; } - dev->interface = PHY_INTERFACE_MODE_RGMII_TXID; return 0; } @@ -1298,7 +1563,12 @@ static const struct ksz_dev_ops ksz9477_dev_ops = { .get_port_addr = ksz9477_get_port_addr, .cfg_port_member = ksz9477_cfg_port_member, .flush_dyn_mac_table = ksz9477_flush_dyn_mac_table, + .phy_setup = ksz9477_phy_setup, .port_setup = ksz9477_port_setup, + .r_mib_cnt = ksz9477_r_mib_cnt, + .r_mib_pkt = ksz9477_r_mib_pkt, + .freeze_mib = ksz9477_freeze_mib, + .port_init_cnt = ksz9477_port_init_cnt, .shutdown = ksz9477_reset_switch, .detect = ksz9477_switch_detect, .init = ksz9477_switch_init, diff --git a/drivers/net/dsa/microchip/ksz9477_spi.c b/drivers/net/dsa/microchip/ksz9477_spi.c index d757ba151cb1..75178624d3f5 100644 --- a/drivers/net/dsa/microchip/ksz9477_spi.c +++ b/drivers/net/dsa/microchip/ksz9477_spi.c @@ -2,7 +2,7 @@ /* * Microchip KSZ9477 series register access through SPI * - * Copyright (C) 2017-2018 Microchip Technology Inc. + * Copyright (C) 2017-2019 Microchip Technology Inc. */ #include <asm/unaligned.h> @@ -155,6 +155,8 @@ static void ksz9477_spi_shutdown(struct spi_device *spi) static const struct of_device_id ksz9477_dt_ids[] = { { .compatible = "microchip,ksz9477" }, { .compatible = "microchip,ksz9897" }, + { .compatible = "microchip,ksz9893" }, + { .compatible = "microchip,ksz9563" }, {}, }; MODULE_DEVICE_TABLE(of, ksz9477_dt_ids); diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 3b12e2dcff31..39dace8e3512 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -2,12 +2,11 @@ /* * Microchip switch driver main logic * - * Copyright (C) 2017-2018 Microchip Technology Inc. + * Copyright (C) 2017-2019 Microchip Technology Inc. */ #include <linux/delay.h> #include <linux/export.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/kernel.h> #include <linux/module.h> @@ -15,13 +14,22 @@ #include <linux/phy.h> #include <linux/etherdevice.h> #include <linux/if_bridge.h> -#include <linux/of_gpio.h> #include <linux/of_net.h> #include <net/dsa.h> #include <net/switchdev.h> #include "ksz_priv.h" +void ksz_port_cleanup(struct ksz_device *dev, int port) +{ + /* Common code for port cleanup. */ + mutex_lock(&dev->dev_mutex); + dev->on_ports &= ~(1 << port); + dev->live_ports &= ~(1 << port); + mutex_unlock(&dev->dev_mutex); +} +EXPORT_SYMBOL_GPL(ksz_port_cleanup); + void ksz_update_port_member(struct ksz_device *dev, int port) { struct ksz_port *p; @@ -42,6 +50,85 @@ void ksz_update_port_member(struct ksz_device *dev, int port) } EXPORT_SYMBOL_GPL(ksz_update_port_member); +static void port_r_cnt(struct ksz_device *dev, int port) +{ + struct ksz_port_mib *mib = &dev->ports[port].mib; + u64 *dropped; + + /* Some ports may not have MIB counters before SWITCH_COUNTER_NUM. */ + while (mib->cnt_ptr < dev->reg_mib_cnt) { + dev->dev_ops->r_mib_cnt(dev, port, mib->cnt_ptr, + &mib->counters[mib->cnt_ptr]); + ++mib->cnt_ptr; + } + + /* last one in storage */ + dropped = &mib->counters[dev->mib_cnt]; + + /* Some ports may not have MIB counters after SWITCH_COUNTER_NUM. */ + while (mib->cnt_ptr < dev->mib_cnt) { + dev->dev_ops->r_mib_pkt(dev, port, mib->cnt_ptr, + dropped, &mib->counters[mib->cnt_ptr]); + ++mib->cnt_ptr; + } + mib->cnt_ptr = 0; +} + +static void ksz_mib_read_work(struct work_struct *work) +{ + struct ksz_device *dev = container_of(work, struct ksz_device, + mib_read); + struct ksz_port_mib *mib; + struct ksz_port *p; + int i; + + for (i = 0; i < dev->mib_port_cnt; i++) { + p = &dev->ports[i]; + mib = &p->mib; + mutex_lock(&mib->cnt_mutex); + + /* Only read MIB counters when the port is told to do. + * If not, read only dropped counters when link is not up. + */ + if (!p->read) { + const struct dsa_port *dp = dsa_to_port(dev->ds, i); + + if (!netif_carrier_ok(dp->slave)) + mib->cnt_ptr = dev->reg_mib_cnt; + } + port_r_cnt(dev, i); + p->read = false; + mutex_unlock(&mib->cnt_mutex); + } +} + +static void mib_monitor(struct timer_list *t) +{ + struct ksz_device *dev = from_timer(dev, t, mib_read_timer); + + mod_timer(&dev->mib_read_timer, jiffies + dev->mib_read_interval); + schedule_work(&dev->mib_read); +} + +void ksz_init_mib_timer(struct ksz_device *dev) +{ + int i; + + /* Read MIB counters every 30 seconds to avoid overflow. */ + dev->mib_read_interval = msecs_to_jiffies(30000); + + INIT_WORK(&dev->mib_read, ksz_mib_read_work); + timer_setup(&dev->mib_read_timer, mib_monitor, 0); + + for (i = 0; i < dev->mib_port_cnt; i++) + dev->dev_ops->port_init_cnt(dev, i); + + /* Start the timer 2 seconds later. */ + dev->mib_read_timer.expires = jiffies + msecs_to_jiffies(2000); + add_timer(&dev->mib_read_timer); +} +EXPORT_SYMBOL_GPL(ksz_init_mib_timer); + int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg) { struct ksz_device *dev = ds->priv; @@ -63,6 +150,27 @@ int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val) } EXPORT_SYMBOL_GPL(ksz_phy_write16); +void ksz_adjust_link(struct dsa_switch *ds, int port, + struct phy_device *phydev) +{ + struct ksz_device *dev = ds->priv; + struct ksz_port *p = &dev->ports[port]; + + /* Read all MIB counters when the link is going down. */ + if (!phydev->link) { + p->read = true; + schedule_work(&dev->mib_read); + } + mutex_lock(&dev->dev_mutex); + if (!phydev->link) + dev->live_ports &= ~(1 << port); + else + /* Remember which port is connected and active. */ + dev->live_ports |= (1 << port) & dev->on_ports; + mutex_unlock(&dev->dev_mutex); +} +EXPORT_SYMBOL_GPL(ksz_adjust_link); + int ksz_sset_count(struct dsa_switch *ds, int port, int sset) { struct ksz_device *dev = ds->priv; @@ -74,12 +182,32 @@ int ksz_sset_count(struct dsa_switch *ds, int port, int sset) } EXPORT_SYMBOL_GPL(ksz_sset_count); +void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf) +{ + const struct dsa_port *dp = dsa_to_port(ds, port); + struct ksz_device *dev = ds->priv; + struct ksz_port_mib *mib; + + mib = &dev->ports[port].mib; + mutex_lock(&mib->cnt_mutex); + + /* Only read dropped counters if no link. */ + if (!netif_carrier_ok(dp->slave)) + mib->cnt_ptr = dev->reg_mib_cnt; + port_r_cnt(dev, port); + memcpy(buf, mib->counters, dev->mib_cnt * sizeof(u64)); + mutex_unlock(&mib->cnt_mutex); +} +EXPORT_SYMBOL_GPL(ksz_get_ethtool_stats); + int ksz_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br) { struct ksz_device *dev = ds->priv; + mutex_lock(&dev->dev_mutex); dev->br_member |= (1 << port); + mutex_unlock(&dev->dev_mutex); /* port_stp_state_set() will be called after to put the port in * appropriate state so there is no need to do anything. @@ -94,8 +222,10 @@ void ksz_port_bridge_leave(struct dsa_switch *ds, int port, { struct ksz_device *dev = ds->priv; + mutex_lock(&dev->dev_mutex); dev->br_member &= ~(1 << port); dev->member &= ~(1 << port); + mutex_unlock(&dev->dev_mutex); /* port_stp_state_set() will be called after to put the port in * forwarding state so there is no need to do anything. @@ -240,6 +370,7 @@ int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) /* setup slave port */ dev->dev_ops->port_setup(dev, port, false); + dev->dev_ops->phy_setup(dev, port, phy); /* port_stp_state_set() will be called after to enable the port so * there is no need to do anything. @@ -249,7 +380,7 @@ int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) } EXPORT_SYMBOL_GPL(ksz_enable_port); -void ksz_disable_port(struct dsa_switch *ds, int port, struct phy_device *phy) +void ksz_disable_port(struct dsa_switch *ds, int port) { struct ksz_device *dev = ds->priv; @@ -307,6 +438,7 @@ int ksz_switch_register(struct ksz_device *dev, gpiod_set_value(dev->reset_gpio, 0); } + mutex_init(&dev->dev_mutex); mutex_init(&dev->reg_mutex); mutex_init(&dev->stats_mutex); mutex_init(&dev->alu_mutex); @@ -321,7 +453,9 @@ int ksz_switch_register(struct ksz_device *dev, if (ret) return ret; - dev->interface = PHY_INTERFACE_MODE_MII; + /* Host port interface will be self detected, or specifically set in + * device tree. + */ if (dev->dev->of_node) { ret = of_get_phy_mode(dev->dev->of_node); if (ret >= 0) @@ -340,6 +474,12 @@ EXPORT_SYMBOL(ksz_switch_register); void ksz_switch_remove(struct ksz_device *dev) { + /* timer started */ + if (dev->mib_read_timer.expires) { + del_timer_sync(&dev->mib_read_timer); + flush_work(&dev->mib_read); + } + dev->dev_ops->exit(dev); dsa_unregister_switch(dev->ds); diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 2dd832de0d52..21cd794e18f1 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -1,19 +1,24 @@ /* SPDX-License-Identifier: GPL-2.0 * Microchip switch driver common header * - * Copyright (C) 2017-2018 Microchip Technology Inc. + * Copyright (C) 2017-2019 Microchip Technology Inc. */ #ifndef __KSZ_COMMON_H #define __KSZ_COMMON_H +void ksz_port_cleanup(struct ksz_device *dev, int port); void ksz_update_port_member(struct ksz_device *dev, int port); +void ksz_init_mib_timer(struct ksz_device *dev); /* Common DSA access functions */ int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg); int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val); +void ksz_adjust_link(struct dsa_switch *ds, int port, + struct phy_device *phydev); int ksz_sset_count(struct dsa_switch *ds, int port, int sset); +void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf); int ksz_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br); void ksz_port_bridge_leave(struct dsa_switch *ds, int port, @@ -30,7 +35,7 @@ void ksz_port_mdb_add(struct dsa_switch *ds, int port, int ksz_port_mdb_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb); int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy); -void ksz_disable_port(struct dsa_switch *ds, int port, struct phy_device *phy); +void ksz_disable_port(struct dsa_switch *ds, int port); /* Common register access functions */ @@ -211,4 +216,18 @@ static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits, ksz_write8(dev, addr, data); } +struct ksz_poll_ctx { + struct ksz_device *dev; + int port; + int offset; +}; + +static inline u32 ksz_pread32_poll(struct ksz_poll_ctx *ctx) +{ + u32 data; + + ksz_pread32(ctx->dev, ctx->port, ctx->offset, &data); + return data; +} + #endif diff --git a/drivers/net/dsa/microchip/ksz_priv.h b/drivers/net/dsa/microchip/ksz_priv.h index 60b49010904b..b52e5ca17ab4 100644 --- a/drivers/net/dsa/microchip/ksz_priv.h +++ b/drivers/net/dsa/microchip/ksz_priv.h @@ -2,7 +2,7 @@ * * Microchip KSZ series switch common definitions * - * Copyright (C) 2017-2018 Microchip Technology Inc. + * Copyright (C) 2017-2019 Microchip Technology Inc. */ #ifndef __KSZ_PRIV_H @@ -14,8 +14,6 @@ #include <linux/etherdevice.h> #include <net/dsa.h> -#include "ksz9477_reg.h" - struct ksz_io_ops; struct vlan_table { @@ -23,6 +21,7 @@ struct vlan_table { }; struct ksz_port_mib { + struct mutex cnt_mutex; /* structure access */ u8 cnt_ptr; u64 *counters; }; @@ -38,7 +37,8 @@ struct ksz_port { u32 fiber:1; /* port is fiber */ u32 sgmii:1; /* port is SGMII */ u32 force:1; - u32 link_just_down:1; /* link just goes down */ + u32 read:1; /* read MIB counters in background */ + u32 freeze:1; /* MIB counter freeze is enabled */ struct ksz_port_mib mib; }; @@ -48,6 +48,7 @@ struct ksz_device { struct ksz_platform_data *pdata; const char *name; + struct mutex dev_mutex; /* device access */ struct mutex reg_mutex; /* register access */ struct mutex stats_mutex; /* status access */ struct mutex alu_mutex; /* ALU access */ @@ -79,8 +80,6 @@ struct ksz_device { struct vlan_table *vlan_cache; - u64 mib_value[TOTAL_SWITCH_COUNTER_NUM]; - u8 *txbuf; struct ksz_port *ports; @@ -137,6 +136,9 @@ struct ksz_dev_ops { u32 (*get_port_addr)(int port, int offset); void (*cfg_port_member)(struct ksz_device *dev, int port, u8 member); void (*flush_dyn_mac_table)(struct ksz_device *dev, int port); + void (*phy_setup)(struct ksz_device *dev, int port, + struct phy_device *phy); + void (*port_cleanup)(struct ksz_device *dev, int port); void (*port_setup)(struct ksz_device *dev, int port, bool cpu_port); void (*r_phy)(struct ksz_device *dev, u16 phy, u16 reg, u16 *val); void (*w_phy)(struct ksz_device *dev, u16 phy, u16 reg, u16 val); @@ -151,6 +153,7 @@ struct ksz_dev_ops { u64 *cnt); void (*r_mib_pkt)(struct ksz_device *dev, int port, u16 addr, u64 *dropped, u64 *cnt); + void (*freeze_mib)(struct ksz_device *dev, int port, bool freeze); void (*port_init_cnt)(struct ksz_device *dev, int port); int (*shutdown)(struct ksz_device *dev); int (*detect)(struct ksz_device *dev); |