]> git.baikalelectronics.ru Git - kernel.git/commitdiff
net: qca_spi: Introduce write register verification
authorStefan Wahren <stefan.wahren@i2se.com>
Mon, 24 Sep 2018 11:20:10 +0000 (13:20 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 24 Sep 2018 19:26:06 +0000 (12:26 -0700)
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>
drivers/net/ethernet/qualcomm/qca_7k.c
drivers/net/ethernet/qualcomm/qca_7k.h
drivers/net/ethernet/qualcomm/qca_debug.c
drivers/net/ethernet/qualcomm/qca_spi.c
drivers/net/ethernet/qualcomm/qca_spi.h

index 6c8543fb90c0a3ac780edd36aa701b01e7c9d94a..4292c89bd35c3572abc548f065e05601eb3ab776 100644 (file)
@@ -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];
@@ -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;
+}
index 27124c2bb77acad774952193e9bf6db8130382b5..356de8ec5d48b770bc0b09ebd24239c42b695e0f 100644 (file)
@@ -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 */
index 51d89c86e60f56a5c2dded631605f9f5e56be761..a9f1bc013364763a818a1626184adc87fa38830d 100644 (file)
@@ -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
index 66b775d462fd8ed111dbb18e845ad4d9af1b6d2b..d5310504f4364349bf235f8d8f82eb7a14348a70 100644 (file)
@@ -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
 
@@ -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);
 }
@@ -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);
 }
 
@@ -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);
 
@@ -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);
 
@@ -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);
@@ -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++;
@@ -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);
@@ -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,
index fc0e98726b3613ddd3774169fa13aa6099a5c6e5..2d2c4972649245e8ffb24abadaaf107cb9a15bd3 100644 (file)
@@ -73,6 +73,7 @@ struct qcaspi_stats {
        u64 write_buf_miss;
        u64 ring_full;
        u64 spi_err;
+       u64 write_verify_failed;
 };
 
 struct qcaspi {