diff options
author | Jakub Kicinski <kuba@kernel.org> | 2024-04-22 14:20:15 -0700 |
---|---|---|
committer | Jakub Kicinski <kuba@kernel.org> | 2024-04-22 14:21:32 -0700 |
commit | b240fc56b8fd5d383bd00e59ddf67a4f66b86bb8 (patch) | |
tree | 3892e41bc52cb28a06d3d8707db5bde6d3c36ddc | |
parent | c51db4ac10d57c366f9a92121e3889bfc6c324cd (diff) | |
parent | 96944aafaaa64e6ead1d3d227a1493dd8196a827 (diff) |
Merge branch 'net-dsa-vsc73xx-convert-to-phylink-and-do-some-cleanup'
Pawel Dembicki says:
====================
net: dsa: vsc73xx: convert to PHYLINK and do some cleanup
This patch series is a result of splitting a larger patch series [0],
where some parts needed to be refactored.
The first patch switches from a poll loop to read_poll_timeout.
The second patch is a simple conversion to phylink because adjust_link
won't work anymore.
The third patch is preparation for future use. Using the
"phy_interface_mode_is_rgmii" macro allows for the proper recognition
of all RGMII modes.
Patches 4-5 involve some cleanup: The fourth patch introduces
a definition with the maximum number of ports to avoid using
magic numbers. The next one fills in documentation.
[0] https://patchwork.kernel.org/project/netdevbpf/list/?series=841034&state=%2A&archive=both
====================
Link: https://lore.kernel.org/r/20240417205048.3542839-1-paweldembicki@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r-- | drivers/net/dsa/vitesse-vsc73xx-core.c | 255 | ||||
-rw-r--r-- | drivers/net/dsa/vitesse-vsc73xx.h | 27 |
2 files changed, 144 insertions, 138 deletions
diff --git a/drivers/net/dsa/vitesse-vsc73xx-core.c b/drivers/net/dsa/vitesse-vsc73xx-core.c index ae70eac3be28..4b031fefcec6 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-core.c +++ b/drivers/net/dsa/vitesse-vsc73xx-core.c @@ -17,6 +17,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/device.h> +#include <linux/iopoll.h> #include <linux/of.h> #include <linux/of_mdio.h> #include <linux/bitops.h> @@ -268,6 +269,9 @@ #define IS_7398(a) ((a)->chipid == VSC73XX_CHIPID_ID_7398) #define IS_739X(a) (IS_7395(a) || IS_7398(a)) +#define VSC73XX_POLL_SLEEP_US 1000 +#define VSC73XX_POLL_TIMEOUT_US 10000 + struct vsc73xx_counter { u8 counter; const char *name; @@ -713,51 +717,44 @@ static void vsc73xx_init_port(struct vsc73xx *vsc, int port) port, VSC73XX_C_RX0, 0); } -static void vsc73xx_adjust_enable_port(struct vsc73xx *vsc, - int port, struct phy_device *phydev, - u32 initval) +static void vsc73xx_reset_port(struct vsc73xx *vsc, int port, u32 initval) { - u32 val = initval; - u8 seed; - - /* Reset this port FIXME: break out subroutine */ - val |= VSC73XX_MAC_CFG_RESET; - vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_MAC_CFG, val); - - /* Seed the port randomness with randomness */ - get_random_bytes(&seed, 1); - val |= seed << VSC73XX_MAC_CFG_SEED_OFFSET; - val |= VSC73XX_MAC_CFG_SEED_LOAD; - val |= VSC73XX_MAC_CFG_WEXC_DIS; - vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_MAC_CFG, val); + int ret, err; + u32 val; - /* Flow control for the PHY facing ports: - * Use a zero delay pause frame when pause condition is left - * Obey pause control frames - * When generating pause frames, use 0xff as pause value - */ - vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_FCCONF, - VSC73XX_FCCONF_ZERO_PAUSE_EN | - VSC73XX_FCCONF_FLOW_CTRL_OBEY | - 0xff); + /* Disable RX on this port */ + vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, + VSC73XX_MAC_CFG, + VSC73XX_MAC_CFG_RX_EN, 0); - /* Disallow backward dropping of frames from this port */ + /* Discard packets */ vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0, - VSC73XX_SBACKWDROP, BIT(port), 0); + VSC73XX_ARBDISC, BIT(port), BIT(port)); + + /* Wait until queue is empty */ + ret = read_poll_timeout(vsc73xx_read, err, + err < 0 || (val & BIT(port)), + VSC73XX_POLL_SLEEP_US, + VSC73XX_POLL_TIMEOUT_US, false, + vsc, VSC73XX_BLOCK_ARBITER, 0, + VSC73XX_ARBEMPTY, &val); + if (ret) + dev_err(vsc->dev, + "timeout waiting for block arbiter\n"); + else if (err < 0) + dev_err(vsc->dev, "error reading arbiter\n"); - /* Enable TX, RX, deassert reset, stop loading seed */ - vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, - VSC73XX_MAC_CFG, - VSC73XX_MAC_CFG_RESET | VSC73XX_MAC_CFG_SEED_LOAD | - VSC73XX_MAC_CFG_TX_EN | VSC73XX_MAC_CFG_RX_EN, - VSC73XX_MAC_CFG_TX_EN | VSC73XX_MAC_CFG_RX_EN); + /* Put this port into reset */ + vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_MAC_CFG, + VSC73XX_MAC_CFG_RESET | initval); } -static void vsc73xx_adjust_link(struct dsa_switch *ds, int port, - struct phy_device *phydev) +static void vsc73xx_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) { - struct vsc73xx *vsc = ds->priv; - u32 val; + struct dsa_port *dp = dsa_phylink_to_port(config); + struct vsc73xx *vsc = dp->ds->priv; + int port = dp->index; /* Special handling of the CPU-facing port */ if (port == CPU_PORT) { @@ -774,104 +771,93 @@ static void vsc73xx_adjust_link(struct dsa_switch *ds, int port, VSC73XX_ADVPORTM_ENA_GTX | VSC73XX_ADVPORTM_DDR_MODE); } +} + +static void vsc73xx_mac_link_down(struct phylink_config *config, + unsigned int mode, phy_interface_t interface) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct vsc73xx *vsc = dp->ds->priv; + int port = dp->index; - /* This is the MAC confiuration that always need to happen - * after a PHY or the CPU port comes up or down. + /* This routine is described in the datasheet (below ARBDISC register + * description) */ - if (!phydev->link) { - int maxloop = 10; - - dev_dbg(vsc->dev, "port %d: went down\n", - port); - - /* Disable RX on this port */ - vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, - VSC73XX_MAC_CFG, - VSC73XX_MAC_CFG_RX_EN, 0); - - /* Discard packets */ - vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0, - VSC73XX_ARBDISC, BIT(port), BIT(port)); - - /* Wait until queue is empty */ - vsc73xx_read(vsc, VSC73XX_BLOCK_ARBITER, 0, - VSC73XX_ARBEMPTY, &val); - while (!(val & BIT(port))) { - msleep(1); - vsc73xx_read(vsc, VSC73XX_BLOCK_ARBITER, 0, - VSC73XX_ARBEMPTY, &val); - if (--maxloop == 0) { - dev_err(vsc->dev, - "timeout waiting for block arbiter\n"); - /* Continue anyway */ - break; - } - } + vsc73xx_reset_port(vsc, port, 0); + + /* Allow backward dropping of frames from this port */ + vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0, + VSC73XX_SBACKWDROP, BIT(port), BIT(port)); + + /* Receive mask (disable forwarding) */ + vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, + VSC73XX_RECVMASK, BIT(port), 0); +} + +static void vsc73xx_mac_link_up(struct phylink_config *config, + struct phy_device *phy, unsigned int mode, + phy_interface_t interface, int speed, + int duplex, bool tx_pause, bool rx_pause) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct vsc73xx *vsc = dp->ds->priv; + int port = dp->index; + u32 val; + u8 seed; - /* Put this port into reset */ - vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_MAC_CFG, - VSC73XX_MAC_CFG_RESET); + if (speed == SPEED_1000) + val = VSC73XX_MAC_CFG_GIGA_MODE | VSC73XX_MAC_CFG_TX_IPG_1000M; + else + val = VSC73XX_MAC_CFG_TX_IPG_100_10M; - /* Accept packets again */ - vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0, - VSC73XX_ARBDISC, BIT(port), 0); + if (phy_interface_mode_is_rgmii(interface)) + val |= VSC73XX_MAC_CFG_CLK_SEL_1000M; + else + val |= VSC73XX_MAC_CFG_CLK_SEL_EXT; - /* Allow backward dropping of frames from this port */ - vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0, - VSC73XX_SBACKWDROP, BIT(port), BIT(port)); + if (duplex == DUPLEX_FULL) + val |= VSC73XX_MAC_CFG_FDX; - /* Receive mask (disable forwarding) */ - vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, - VSC73XX_RECVMASK, BIT(port), 0); + /* This routine is described in the datasheet (below ARBDISC register + * description) + */ + vsc73xx_reset_port(vsc, port, val); - return; - } + /* Seed the port randomness with randomness */ + get_random_bytes(&seed, 1); + val |= seed << VSC73XX_MAC_CFG_SEED_OFFSET; + val |= VSC73XX_MAC_CFG_SEED_LOAD; + val |= VSC73XX_MAC_CFG_WEXC_DIS; + vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_MAC_CFG, val); - /* Figure out what speed was negotiated */ - if (phydev->speed == SPEED_1000) { - dev_dbg(vsc->dev, "port %d: 1000 Mbit mode full duplex\n", - port); - - /* Set up default for internal port or external RGMII */ - if (phydev->interface == PHY_INTERFACE_MODE_RGMII) - val = VSC73XX_MAC_CFG_1000M_F_RGMII; - else - val = VSC73XX_MAC_CFG_1000M_F_PHY; - vsc73xx_adjust_enable_port(vsc, port, phydev, val); - } else if (phydev->speed == SPEED_100) { - if (phydev->duplex == DUPLEX_FULL) { - val = VSC73XX_MAC_CFG_100_10M_F_PHY; - dev_dbg(vsc->dev, - "port %d: 100 Mbit full duplex mode\n", - port); - } else { - val = VSC73XX_MAC_CFG_100_10M_H_PHY; - dev_dbg(vsc->dev, - "port %d: 100 Mbit half duplex mode\n", - port); - } - vsc73xx_adjust_enable_port(vsc, port, phydev, val); - } else if (phydev->speed == SPEED_10) { - if (phydev->duplex == DUPLEX_FULL) { - val = VSC73XX_MAC_CFG_100_10M_F_PHY; - dev_dbg(vsc->dev, - "port %d: 10 Mbit full duplex mode\n", - port); - } else { - val = VSC73XX_MAC_CFG_100_10M_H_PHY; - dev_dbg(vsc->dev, - "port %d: 10 Mbit half duplex mode\n", - port); - } - vsc73xx_adjust_enable_port(vsc, port, phydev, val); - } else { - dev_err(vsc->dev, - "could not adjust link: unknown speed\n"); - } + /* Flow control for the PHY facing ports: + * Use a zero delay pause frame when pause condition is left + * Obey pause control frames + * When generating pause frames, use 0xff as pause value + */ + vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_FCCONF, + VSC73XX_FCCONF_ZERO_PAUSE_EN | + VSC73XX_FCCONF_FLOW_CTRL_OBEY | + 0xff); - /* Enable port (forwarding) in the receieve mask */ + /* Accept packets again */ + vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0, + VSC73XX_ARBDISC, BIT(port), 0); + + /* Enable port (forwarding) in the receive mask */ vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_RECVMASK, BIT(port), BIT(port)); + + /* Disallow backward dropping of frames from this port */ + vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0, + VSC73XX_SBACKWDROP, BIT(port), 0); + + /* Enable TX, RX, deassert reset, stop loading seed */ + vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, + VSC73XX_MAC_CFG, + VSC73XX_MAC_CFG_RESET | VSC73XX_MAC_CFG_SEED_LOAD | + VSC73XX_MAC_CFG_TX_EN | VSC73XX_MAC_CFG_RX_EN, + VSC73XX_MAC_CFG_TX_EN | VSC73XX_MAC_CFG_RX_EN); } static int vsc73xx_port_enable(struct dsa_switch *ds, int port, @@ -1053,12 +1039,17 @@ static void vsc73xx_phylink_get_caps(struct dsa_switch *dsa, int port, config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000; } +static const struct phylink_mac_ops vsc73xx_phylink_mac_ops = { + .mac_config = vsc73xx_mac_config, + .mac_link_down = vsc73xx_mac_link_down, + .mac_link_up = vsc73xx_mac_link_up, +}; + static const struct dsa_switch_ops vsc73xx_ds_ops = { .get_tag_protocol = vsc73xx_get_tag_protocol, .setup = vsc73xx_setup, .phy_read = vsc73xx_phy_read, .phy_write = vsc73xx_phy_write, - .adjust_link = vsc73xx_adjust_link, .get_strings = vsc73xx_get_strings, .get_ethtool_stats = vsc73xx_get_ethtool_stats, .get_sset_count = vsc73xx_get_sset_count, @@ -1195,26 +1186,16 @@ int vsc73xx_probe(struct vsc73xx *vsc) vsc->addr[0], vsc->addr[1], vsc->addr[2], vsc->addr[3], vsc->addr[4], vsc->addr[5]); - /* The VSC7395 switch chips have 5+1 ports which means 5 - * ordinary ports and a sixth CPU port facing the processor - * with an RGMII interface. These ports are numbered 0..4 - * and 6, so they leave a "hole" in the port map for port 5, - * which is invalid. - * - * The VSC7398 has 8 ports, port 7 is again the CPU port. - * - * We allocate 8 ports and avoid access to the nonexistant - * ports. - */ vsc->ds = devm_kzalloc(dev, sizeof(*vsc->ds), GFP_KERNEL); if (!vsc->ds) return -ENOMEM; vsc->ds->dev = dev; - vsc->ds->num_ports = 8; + vsc->ds->num_ports = VSC73XX_MAX_NUM_PORTS; vsc->ds->priv = vsc; vsc->ds->ops = &vsc73xx_ds_ops; + vsc->ds->phylink_mac_ops = &vsc73xx_phylink_mac_ops; ret = dsa_register_switch(vsc->ds); if (ret) { dev_err(dev, "unable to register switch (%d)\n", ret); diff --git a/drivers/net/dsa/vitesse-vsc73xx.h b/drivers/net/dsa/vitesse-vsc73xx.h index 30b1f0a36566..2997f7e108b1 100644 --- a/drivers/net/dsa/vitesse-vsc73xx.h +++ b/drivers/net/dsa/vitesse-vsc73xx.h @@ -3,8 +3,28 @@ #include <linux/etherdevice.h> #include <linux/gpio/driver.h> +/* The VSC7395 switch chips have 5+1 ports which means 5 ordinary ports and + * a sixth CPU port facing the processor with an RGMII interface. These ports + * are numbered 0..4 and 6, so they leave a "hole" in the port map for port 5, + * which is invalid. + * + * The VSC7398 has 8 ports, port 7 is again the CPU port. + * + * We allocate 8 ports and avoid access to the nonexistent ports. + */ +#define VSC73XX_MAX_NUM_PORTS 8 + /** - * struct vsc73xx - VSC73xx state container + * struct vsc73xx - VSC73xx state container: main data structure + * @dev: The device pointer + * @reset: The descriptor for the GPIO line tied to the reset pin + * @ds: Pointer to the DSA core structure + * @gc: Main structure of the GPIO controller + * @chipid: Storage for the Chip ID value read from the CHIPID register of the + * switch + * @addr: MAC address used in flow control frames + * @ops: Structure with hardware-dependent operations + * @priv: Pointer to the configuration interface structure */ struct vsc73xx { struct device *dev; @@ -17,6 +37,11 @@ struct vsc73xx { void *priv; }; +/** + * struct vsc73xx_ops - VSC73xx methods container + * @read: Method for register reading over the hardware-dependent interface + * @write: Method for register writing over the hardware-dependent interface + */ struct vsc73xx_ops { int (*read)(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg, u32 *val); |