summaryrefslogtreecommitdiff
path: root/drivers/net/dsa/sja1105/sja1105_spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/dsa/sja1105/sja1105_spi.c')
-rw-r--r--drivers/net/dsa/sja1105/sja1105_spi.c37
1 files changed, 37 insertions, 0 deletions
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 <linux/packing.h>
#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},