diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index 0489d9adf957..b0a155b57e17 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -22,6 +22,7 @@ struct sja1105_regs { u64 device_id; u64 prod_id; u64 status; + u64 port_control; u64 rgu; u64 config; u64 rmii_pll1; diff --git a/drivers/net/dsa/sja1105/sja1105_spi.c b/drivers/net/dsa/sja1105/sja1105_spi.c index 07890bbf40f8..244a94ccfc18 100644 --- a/drivers/net/dsa/sja1105/sja1105_spi.c +++ b/drivers/net/dsa/sja1105/sja1105_spi.c @@ -7,6 +7,7 @@ #include #include "sja1105.h" +#define SJA1105_SIZE_PORT_CTRL 4 #define SJA1105_SIZE_RESET_CMD 4 #define SJA1105_SIZE_SPI_MSG_HEADER 4 #define SJA1105_SIZE_SPI_MSG_MAXLEN (64 * 4) @@ -282,6 +283,25 @@ static int sja1105_cold_reset(const struct sja1105_private *priv) return priv->info->reset_cmd(priv, &reset); } +static int sja1105_inhibit_tx(const struct sja1105_private *priv, + const unsigned long *port_bitmap) +{ + const struct sja1105_regs *regs = priv->info->regs; + u64 inhibit_cmd; + int port, rc; + + rc = sja1105_spi_send_int(priv, SPI_READ, regs->port_control, + &inhibit_cmd, SJA1105_SIZE_PORT_CTRL); + if (rc < 0) + return rc; + + for_each_set_bit(port, port_bitmap, SJA1105_NUM_PORTS) + inhibit_cmd |= BIT(port); + + return sja1105_spi_send_int(priv, SPI_WRITE, regs->port_control, + &inhibit_cmd, SJA1105_SIZE_PORT_CTRL); +} + struct sja1105_status { u64 configs; u64 crcchkl; @@ -370,6 +390,7 @@ static_config_buf_prepare_for_upload(struct sja1105_private *priv, int sja1105_static_config_upload(struct sja1105_private *priv) { + unsigned long port_bitmap = GENMASK_ULL(SJA1105_NUM_PORTS - 1, 0); struct sja1105_static_config *config = &priv->static_config; const struct sja1105_regs *regs = priv->info->regs; struct device *dev = &priv->spidev->dev; @@ -388,6 +409,20 @@ int sja1105_static_config_upload(struct sja1105_private *priv) dev_err(dev, "Invalid config, cannot upload\n"); return -EINVAL; } + /* Prevent PHY jabbering during switch reset by inhibiting + * Tx on all ports and waiting for current packet to drain. + * Otherwise, the PHY will see an unterminated Ethernet packet. + */ + rc = sja1105_inhibit_tx(priv, &port_bitmap); + if (rc < 0) { + dev_err(dev, "Failed to inhibit Tx on ports\n"); + return -ENXIO; + } + /* Wait for an eventual egress packet to finish transmission + * (reach IFG). It is guaranteed that a second one will not + * follow, and that switch cold reset is thus safe + */ + usleep_range(500, 1000); do { /* Put the SJA1105 in programming mode */ rc = sja1105_cold_reset(priv); @@ -452,6 +487,7 @@ struct sja1105_regs sja1105et_regs = { .device_id = 0x0, .prod_id = 0x100BC3, .status = 0x1, + .port_control = 0x11, .config = 0x020000, .rgu = 0x100440, .pad_mii_tx = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808}, @@ -476,6 +512,7 @@ struct sja1105_regs sja1105pqrs_regs = { .device_id = 0x0, .prod_id = 0x100BC3, .status = 0x1, + .port_control = 0x12, .config = 0x020000, .rgu = 0x100440, .pad_mii_tx = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808},