Commit b240fc56 authored by Jakub Kicinski's avatar Jakub Kicinski

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.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents c51db4ac 96944aaf
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/iopoll.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_mdio.h> #include <linux/of_mdio.h>
#include <linux/bitops.h> #include <linux/bitops.h>
...@@ -268,6 +269,9 @@ ...@@ -268,6 +269,9 @@
#define IS_7398(a) ((a)->chipid == VSC73XX_CHIPID_ID_7398) #define IS_7398(a) ((a)->chipid == VSC73XX_CHIPID_ID_7398)
#define IS_739X(a) (IS_7395(a) || IS_7398(a)) #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 { struct vsc73xx_counter {
u8 counter; u8 counter;
const char *name; const char *name;
...@@ -713,51 +717,44 @@ static void vsc73xx_init_port(struct vsc73xx *vsc, int port) ...@@ -713,51 +717,44 @@ static void vsc73xx_init_port(struct vsc73xx *vsc, int port)
port, VSC73XX_C_RX0, 0); port, VSC73XX_C_RX0, 0);
} }
static void vsc73xx_adjust_enable_port(struct vsc73xx *vsc, static void vsc73xx_reset_port(struct vsc73xx *vsc, int port, u32 initval)
int port, struct phy_device *phydev,
u32 initval)
{ {
u32 val = initval; int ret, err;
u8 seed; u32 val;
/* 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);
/* Flow control for the PHY facing ports: /* Disable RX on this port */
* Use a zero delay pause frame when pause condition is left vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port,
* Obey pause control frames VSC73XX_MAC_CFG,
* When generating pause frames, use 0xff as pause value VSC73XX_MAC_CFG_RX_EN, 0);
*/
vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_FCCONF,
VSC73XX_FCCONF_ZERO_PAUSE_EN |
VSC73XX_FCCONF_FLOW_CTRL_OBEY |
0xff);
/* Disallow backward dropping of frames from this port */ /* Discard packets */
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0, vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
VSC73XX_SBACKWDROP, BIT(port), 0); VSC73XX_ARBDISC, BIT(port), BIT(port));
/* Enable TX, RX, deassert reset, stop loading seed */ /* Wait until queue is empty */
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, ret = read_poll_timeout(vsc73xx_read, err,
VSC73XX_MAC_CFG, err < 0 || (val & BIT(port)),
VSC73XX_MAC_CFG_RESET | VSC73XX_MAC_CFG_SEED_LOAD | VSC73XX_POLL_SLEEP_US,
VSC73XX_MAC_CFG_TX_EN | VSC73XX_MAC_CFG_RX_EN, VSC73XX_POLL_TIMEOUT_US, false,
VSC73XX_MAC_CFG_TX_EN | VSC73XX_MAC_CFG_RX_EN); 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");
/* 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, static void vsc73xx_mac_config(struct phylink_config *config, unsigned int mode,
struct phy_device *phydev) const struct phylink_link_state *state)
{ {
struct vsc73xx *vsc = ds->priv; struct dsa_port *dp = dsa_phylink_to_port(config);
u32 val; struct vsc73xx *vsc = dp->ds->priv;
int port = dp->index;
/* Special handling of the CPU-facing port */ /* Special handling of the CPU-facing port */
if (port == CPU_PORT) { if (port == CPU_PORT) {
...@@ -774,47 +771,19 @@ static void vsc73xx_adjust_link(struct dsa_switch *ds, int port, ...@@ -774,47 +771,19 @@ static void vsc73xx_adjust_link(struct dsa_switch *ds, int port,
VSC73XX_ADVPORTM_ENA_GTX | VSC73XX_ADVPORTM_ENA_GTX |
VSC73XX_ADVPORTM_DDR_MODE); VSC73XX_ADVPORTM_DDR_MODE);
} }
}
/* This is the MAC confiuration that always need to happen static void vsc73xx_mac_link_down(struct phylink_config *config,
* after a PHY or the CPU port comes up or down. unsigned int mode, phy_interface_t interface)
*/ {
if (!phydev->link) { struct dsa_port *dp = dsa_phylink_to_port(config);
int maxloop = 10; struct vsc73xx *vsc = dp->ds->priv;
int port = dp->index;
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;
}
}
/* Put this port into reset */
vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_MAC_CFG,
VSC73XX_MAC_CFG_RESET);
/* Accept packets again */ /* This routine is described in the datasheet (below ARBDISC register
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0, * description)
VSC73XX_ARBDISC, BIT(port), 0); */
vsc73xx_reset_port(vsc, port, 0);
/* Allow backward dropping of frames from this port */ /* Allow backward dropping of frames from this port */
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0, vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
...@@ -823,55 +792,72 @@ static void vsc73xx_adjust_link(struct dsa_switch *ds, int port, ...@@ -823,55 +792,72 @@ static void vsc73xx_adjust_link(struct dsa_switch *ds, int port,
/* Receive mask (disable forwarding) */ /* Receive mask (disable forwarding) */
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
VSC73XX_RECVMASK, BIT(port), 0); VSC73XX_RECVMASK, BIT(port), 0);
}
return; 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;
/* Figure out what speed was negotiated */ if (speed == SPEED_1000)
if (phydev->speed == SPEED_1000) { val = VSC73XX_MAC_CFG_GIGA_MODE | VSC73XX_MAC_CFG_TX_IPG_1000M;
dev_dbg(vsc->dev, "port %d: 1000 Mbit mode full duplex\n", else
port); val = VSC73XX_MAC_CFG_TX_IPG_100_10M;
/* Set up default for internal port or external RGMII */ if (phy_interface_mode_is_rgmii(interface))
if (phydev->interface == PHY_INTERFACE_MODE_RGMII) val |= VSC73XX_MAC_CFG_CLK_SEL_1000M;
val = VSC73XX_MAC_CFG_1000M_F_RGMII;
else else
val = VSC73XX_MAC_CFG_1000M_F_PHY; val |= VSC73XX_MAC_CFG_CLK_SEL_EXT;
vsc73xx_adjust_enable_port(vsc, port, phydev, val);
} else if (phydev->speed == SPEED_100) { if (duplex == DUPLEX_FULL)
if (phydev->duplex == DUPLEX_FULL) { val |= VSC73XX_MAC_CFG_FDX;
val = VSC73XX_MAC_CFG_100_10M_F_PHY;
dev_dbg(vsc->dev, /* This routine is described in the datasheet (below ARBDISC register
"port %d: 100 Mbit full duplex mode\n", * description)
port); */
} else { vsc73xx_reset_port(vsc, port, val);
val = VSC73XX_MAC_CFG_100_10M_H_PHY;
dev_dbg(vsc->dev, /* Seed the port randomness with randomness */
"port %d: 100 Mbit half duplex mode\n", get_random_bytes(&seed, 1);
port); val |= seed << VSC73XX_MAC_CFG_SEED_OFFSET;
} val |= VSC73XX_MAC_CFG_SEED_LOAD;
vsc73xx_adjust_enable_port(vsc, port, phydev, val); val |= VSC73XX_MAC_CFG_WEXC_DIS;
} else if (phydev->speed == SPEED_10) { vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_MAC_CFG, val);
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");
}
/* Enable port (forwarding) in the receieve mask */ /* 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);
/* 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_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
VSC73XX_RECVMASK, BIT(port), BIT(port)); 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, 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, ...@@ -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; 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 = { static const struct dsa_switch_ops vsc73xx_ds_ops = {
.get_tag_protocol = vsc73xx_get_tag_protocol, .get_tag_protocol = vsc73xx_get_tag_protocol,
.setup = vsc73xx_setup, .setup = vsc73xx_setup,
.phy_read = vsc73xx_phy_read, .phy_read = vsc73xx_phy_read,
.phy_write = vsc73xx_phy_write, .phy_write = vsc73xx_phy_write,
.adjust_link = vsc73xx_adjust_link,
.get_strings = vsc73xx_get_strings, .get_strings = vsc73xx_get_strings,
.get_ethtool_stats = vsc73xx_get_ethtool_stats, .get_ethtool_stats = vsc73xx_get_ethtool_stats,
.get_sset_count = vsc73xx_get_sset_count, .get_sset_count = vsc73xx_get_sset_count,
...@@ -1195,26 +1186,16 @@ int vsc73xx_probe(struct vsc73xx *vsc) ...@@ -1195,26 +1186,16 @@ int vsc73xx_probe(struct vsc73xx *vsc)
vsc->addr[0], vsc->addr[1], vsc->addr[2], vsc->addr[0], vsc->addr[1], vsc->addr[2],
vsc->addr[3], vsc->addr[4], vsc->addr[5]); 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); vsc->ds = devm_kzalloc(dev, sizeof(*vsc->ds), GFP_KERNEL);
if (!vsc->ds) if (!vsc->ds)
return -ENOMEM; return -ENOMEM;
vsc->ds->dev = dev; vsc->ds->dev = dev;
vsc->ds->num_ports = 8; vsc->ds->num_ports = VSC73XX_MAX_NUM_PORTS;
vsc->ds->priv = vsc; vsc->ds->priv = vsc;
vsc->ds->ops = &vsc73xx_ds_ops; vsc->ds->ops = &vsc73xx_ds_ops;
vsc->ds->phylink_mac_ops = &vsc73xx_phylink_mac_ops;
ret = dsa_register_switch(vsc->ds); ret = dsa_register_switch(vsc->ds);
if (ret) { if (ret) {
dev_err(dev, "unable to register switch (%d)\n", ret); dev_err(dev, "unable to register switch (%d)\n", ret);
......
...@@ -3,8 +3,28 @@ ...@@ -3,8 +3,28 @@
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/gpio/driver.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 vsc73xx {
struct device *dev; struct device *dev;
...@@ -17,6 +37,11 @@ struct vsc73xx { ...@@ -17,6 +37,11 @@ struct vsc73xx {
void *priv; 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 { struct vsc73xx_ops {
int (*read)(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg, int (*read)(struct vsc73xx *vsc, u8 block, u8 subblock, u8 reg,
u32 *val); u32 *val);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment