Skip to content

Commit

Permalink
net: qca_spi: Introduce write register verification
Browse files Browse the repository at this point in the history
The SPI protocol for the QCA7000 doesn't have any fault detection.
In order to increase the drivers reliability in noisy environments,
we could implement a write verification inspired by the enc28j60.
This should avoid situations were the driver wrongly assumes the
receive interrupt is enabled and miss all incoming packets.

This function is disabled per default and can be controlled via module
parameter wr_verify.

Signed-off-by: Michael Heimpold <michael.heimpold@i2se.com>
Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Stefan Wahren authored and David S. Miller committed Sep 24, 2018
1 parent 4128c0c commit 48c1699
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 10 deletions.
34 changes: 32 additions & 2 deletions drivers/net/ethernet/qualcomm/qca_7k.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ qcaspi_read_register(struct qcaspi *qca, u16 reg, u16 *result)
return ret;
}

int
qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value)
static int
__qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value)
{
__be16 tx_data[2];
struct spi_transfer transfer[2];
Expand Down Expand Up @@ -117,3 +117,33 @@ qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value)

return ret;
}

int
qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value, int retry)
{
int ret, i = 0;
u16 confirmed;

do {
ret = __qcaspi_write_register(qca, reg, value);
if (ret)
return ret;

if (!retry)
return 0;

ret = qcaspi_read_register(qca, reg, &confirmed);
if (ret)
return ret;

ret = confirmed != value;
if (!ret)
return 0;

i++;
qca->stats.write_verify_failed++;

} while (i <= retry);

return ret;
}
2 changes: 1 addition & 1 deletion drivers/net/ethernet/qualcomm/qca_7k.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,6 @@

void qcaspi_spi_error(struct qcaspi *qca);
int qcaspi_read_register(struct qcaspi *qca, u16 reg, u16 *result);
int qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value);
int qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value, int retry);

#endif /* _QCA_7K_H */
1 change: 1 addition & 0 deletions drivers/net/ethernet/qualcomm/qca_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ static const char qcaspi_gstrings_stats[][ETH_GSTRING_LEN] = {
"Write buffer misses",
"Transmit ring full",
"SPI errors",
"Write verify errors",
};

#ifdef CONFIG_DEBUG_FS
Expand Down
28 changes: 21 additions & 7 deletions drivers/net/ethernet/qualcomm/qca_spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ static int qcaspi_pluggable = QCASPI_PLUGGABLE_MIN;
module_param(qcaspi_pluggable, int, 0);
MODULE_PARM_DESC(qcaspi_pluggable, "Pluggable SPI connection (yes/no).");

#define QCASPI_WRITE_VERIFY_MIN 0
#define QCASPI_WRITE_VERIFY_MAX 3
static int wr_verify = QCASPI_WRITE_VERIFY_MIN;
module_param(wr_verify, int, 0);
MODULE_PARM_DESC(wr_verify, "SPI register write verify trails. Use 0-3.");

#define QCASPI_TX_TIMEOUT (1 * HZ)
#define QCASPI_QCA7K_REBOOT_TIME_MS 1000

Expand All @@ -77,7 +83,7 @@ start_spi_intr_handling(struct qcaspi *qca, u16 *intr_cause)
{
*intr_cause = 0;

qcaspi_write_register(qca, SPI_REG_INTR_ENABLE, 0);
qcaspi_write_register(qca, SPI_REG_INTR_ENABLE, 0, wr_verify);
qcaspi_read_register(qca, SPI_REG_INTR_CAUSE, intr_cause);
netdev_dbg(qca->net_dev, "interrupts: 0x%04x\n", *intr_cause);
}
Expand All @@ -90,8 +96,8 @@ end_spi_intr_handling(struct qcaspi *qca, u16 intr_cause)
SPI_INT_RDBUF_ERR |
SPI_INT_WRBUF_ERR);

qcaspi_write_register(qca, SPI_REG_INTR_CAUSE, intr_cause);
qcaspi_write_register(qca, SPI_REG_INTR_ENABLE, intr_enable);
qcaspi_write_register(qca, SPI_REG_INTR_CAUSE, intr_cause, 0);
qcaspi_write_register(qca, SPI_REG_INTR_ENABLE, intr_enable, wr_verify);
netdev_dbg(qca->net_dev, "acking int: 0x%04x\n", intr_cause);
}

Expand Down Expand Up @@ -239,7 +245,7 @@ qcaspi_tx_frame(struct qcaspi *qca, struct sk_buff *skb)

len = skb->len;

qcaspi_write_register(qca, SPI_REG_BFR_SIZE, len);
qcaspi_write_register(qca, SPI_REG_BFR_SIZE, len, wr_verify);
if (qca->legacy_mode)
qcaspi_tx_cmd(qca, QCA7K_SPI_WRITE | QCA7K_SPI_EXTERNAL);

Expand Down Expand Up @@ -345,6 +351,7 @@ qcaspi_receive(struct qcaspi *qca)

/* Read the packet size. */
qcaspi_read_register(qca, SPI_REG_RDBUF_BYTE_AVA, &available);

netdev_dbg(net_dev, "qcaspi_receive: SPI_REG_RDBUF_BYTE_AVA: Value: %08x\n",
available);

Expand All @@ -353,7 +360,7 @@ qcaspi_receive(struct qcaspi *qca)
return -1;
}

qcaspi_write_register(qca, SPI_REG_BFR_SIZE, available);
qcaspi_write_register(qca, SPI_REG_BFR_SIZE, available, wr_verify);

if (qca->legacy_mode)
qcaspi_tx_cmd(qca, QCA7K_SPI_READ | QCA7K_SPI_EXTERNAL);
Expand Down Expand Up @@ -524,7 +531,7 @@ qcaspi_qca7k_sync(struct qcaspi *qca, int event)
netdev_dbg(qca->net_dev, "sync: resetting device.\n");
qcaspi_read_register(qca, SPI_REG_SPI_CONFIG, &spi_config);
spi_config |= QCASPI_SLAVE_RESET_BIT;
qcaspi_write_register(qca, SPI_REG_SPI_CONFIG, spi_config);
qcaspi_write_register(qca, SPI_REG_SPI_CONFIG, spi_config, 0);

qca->sync = QCASPI_SYNC_RESET;
qca->stats.trig_reset++;
Expand Down Expand Up @@ -684,7 +691,7 @@ qcaspi_netdev_close(struct net_device *dev)

netif_stop_queue(dev);

qcaspi_write_register(qca, SPI_REG_INTR_ENABLE, 0);
qcaspi_write_register(qca, SPI_REG_INTR_ENABLE, 0, wr_verify);
free_irq(qca->spi_dev->irq, qca);

kthread_stop(qca->spi_thread);
Expand Down Expand Up @@ -904,6 +911,13 @@ qca_spi_probe(struct spi_device *spi)
return -EINVAL;
}

if (wr_verify < QCASPI_WRITE_VERIFY_MIN ||
wr_verify > QCASPI_WRITE_VERIFY_MAX) {
dev_err(&spi->dev, "Invalid write verify: %d\n",
wr_verify);
return -EINVAL;
}

dev_info(&spi->dev, "ver=%s, clkspeed=%d, burst_len=%d, pluggable=%d\n",
QCASPI_DRV_VERSION,
qcaspi_clkspeed,
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/qualcomm/qca_spi.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ struct qcaspi_stats {
u64 write_buf_miss;
u64 ring_full;
u64 spi_err;
u64 write_verify_failed;
};

struct qcaspi {
Expand Down

0 comments on commit 48c1699

Please sign in to comment.