aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Wahren <stefan.wahren@i2se.com>2018-09-24 07:20:10 -0400
committerDavid S. Miller <davem@davemloft.net>2018-09-24 15:26:06 -0400
commit48c1699ec298dd9fbcdcac4fd63ec6db2bd5cc84 (patch)
tree3c70b5d658daf2b02bfae32ba2190df0e9943653
parent4128c0cfb1d74cb1fa2a2fb6824348b349d30f04 (diff)
net: qca_spi: Introduce write register verification
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>
-rw-r--r--drivers/net/ethernet/qualcomm/qca_7k.c34
-rw-r--r--drivers/net/ethernet/qualcomm/qca_7k.h2
-rw-r--r--drivers/net/ethernet/qualcomm/qca_debug.c1
-rw-r--r--drivers/net/ethernet/qualcomm/qca_spi.c28
-rw-r--r--drivers/net/ethernet/qualcomm/qca_spi.h1
5 files changed, 56 insertions, 10 deletions
diff --git a/drivers/net/ethernet/qualcomm/qca_7k.c b/drivers/net/ethernet/qualcomm/qca_7k.c
index 6c8543fb90c0..4292c89bd35c 100644
--- a/drivers/net/ethernet/qualcomm/qca_7k.c
+++ b/drivers/net/ethernet/qualcomm/qca_7k.c
@@ -81,8 +81,8 @@ qcaspi_read_register(struct qcaspi *qca, u16 reg, u16 *result)
81 return ret; 81 return ret;
82} 82}
83 83
84int 84static int
85qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value) 85__qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value)
86{ 86{
87 __be16 tx_data[2]; 87 __be16 tx_data[2];
88 struct spi_transfer transfer[2]; 88 struct spi_transfer transfer[2];
@@ -117,3 +117,33 @@ qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value)
117 117
118 return ret; 118 return ret;
119} 119}
120
121int
122qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value, int retry)
123{
124 int ret, i = 0;
125 u16 confirmed;
126
127 do {
128 ret = __qcaspi_write_register(qca, reg, value);
129 if (ret)
130 return ret;
131
132 if (!retry)
133 return 0;
134
135 ret = qcaspi_read_register(qca, reg, &confirmed);
136 if (ret)
137 return ret;
138
139 ret = confirmed != value;
140 if (!ret)
141 return 0;
142
143 i++;
144 qca->stats.write_verify_failed++;
145
146 } while (i <= retry);
147
148 return ret;
149}
diff --git a/drivers/net/ethernet/qualcomm/qca_7k.h b/drivers/net/ethernet/qualcomm/qca_7k.h
index 27124c2bb77a..356de8ec5d48 100644
--- a/drivers/net/ethernet/qualcomm/qca_7k.h
+++ b/drivers/net/ethernet/qualcomm/qca_7k.h
@@ -66,6 +66,6 @@
66 66
67void qcaspi_spi_error(struct qcaspi *qca); 67void qcaspi_spi_error(struct qcaspi *qca);
68int qcaspi_read_register(struct qcaspi *qca, u16 reg, u16 *result); 68int qcaspi_read_register(struct qcaspi *qca, u16 reg, u16 *result);
69int qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value); 69int qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value, int retry);
70 70
71#endif /* _QCA_7K_H */ 71#endif /* _QCA_7K_H */
diff --git a/drivers/net/ethernet/qualcomm/qca_debug.c b/drivers/net/ethernet/qualcomm/qca_debug.c
index 51d89c86e60f..a9f1bc013364 100644
--- a/drivers/net/ethernet/qualcomm/qca_debug.c
+++ b/drivers/net/ethernet/qualcomm/qca_debug.c
@@ -60,6 +60,7 @@ static const char qcaspi_gstrings_stats[][ETH_GSTRING_LEN] = {
60 "Write buffer misses", 60 "Write buffer misses",
61 "Transmit ring full", 61 "Transmit ring full",
62 "SPI errors", 62 "SPI errors",
63 "Write verify errors",
63}; 64};
64 65
65#ifdef CONFIG_DEBUG_FS 66#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c
index 66b775d462fd..d5310504f436 100644
--- a/drivers/net/ethernet/qualcomm/qca_spi.c
+++ b/drivers/net/ethernet/qualcomm/qca_spi.c
@@ -69,6 +69,12 @@ static int qcaspi_pluggable = QCASPI_PLUGGABLE_MIN;
69module_param(qcaspi_pluggable, int, 0); 69module_param(qcaspi_pluggable, int, 0);
70MODULE_PARM_DESC(qcaspi_pluggable, "Pluggable SPI connection (yes/no)."); 70MODULE_PARM_DESC(qcaspi_pluggable, "Pluggable SPI connection (yes/no).");
71 71
72#define QCASPI_WRITE_VERIFY_MIN 0
73#define QCASPI_WRITE_VERIFY_MAX 3
74static int wr_verify = QCASPI_WRITE_VERIFY_MIN;
75module_param(wr_verify, int, 0);
76MODULE_PARM_DESC(wr_verify, "SPI register write verify trails. Use 0-3.");
77
72#define QCASPI_TX_TIMEOUT (1 * HZ) 78#define QCASPI_TX_TIMEOUT (1 * HZ)
73#define QCASPI_QCA7K_REBOOT_TIME_MS 1000 79#define QCASPI_QCA7K_REBOOT_TIME_MS 1000
74 80
@@ -77,7 +83,7 @@ start_spi_intr_handling(struct qcaspi *qca, u16 *intr_cause)
77{ 83{
78 *intr_cause = 0; 84 *intr_cause = 0;
79 85
80 qcaspi_write_register(qca, SPI_REG_INTR_ENABLE, 0); 86 qcaspi_write_register(qca, SPI_REG_INTR_ENABLE, 0, wr_verify);
81 qcaspi_read_register(qca, SPI_REG_INTR_CAUSE, intr_cause); 87 qcaspi_read_register(qca, SPI_REG_INTR_CAUSE, intr_cause);
82 netdev_dbg(qca->net_dev, "interrupts: 0x%04x\n", *intr_cause); 88 netdev_dbg(qca->net_dev, "interrupts: 0x%04x\n", *intr_cause);
83} 89}
@@ -90,8 +96,8 @@ end_spi_intr_handling(struct qcaspi *qca, u16 intr_cause)
90 SPI_INT_RDBUF_ERR | 96 SPI_INT_RDBUF_ERR |
91 SPI_INT_WRBUF_ERR); 97 SPI_INT_WRBUF_ERR);
92 98
93 qcaspi_write_register(qca, SPI_REG_INTR_CAUSE, intr_cause); 99 qcaspi_write_register(qca, SPI_REG_INTR_CAUSE, intr_cause, 0);
94 qcaspi_write_register(qca, SPI_REG_INTR_ENABLE, intr_enable); 100 qcaspi_write_register(qca, SPI_REG_INTR_ENABLE, intr_enable, wr_verify);
95 netdev_dbg(qca->net_dev, "acking int: 0x%04x\n", intr_cause); 101 netdev_dbg(qca->net_dev, "acking int: 0x%04x\n", intr_cause);
96} 102}
97 103
@@ -239,7 +245,7 @@ qcaspi_tx_frame(struct qcaspi *qca, struct sk_buff *skb)
239 245
240 len = skb->len; 246 len = skb->len;
241 247
242 qcaspi_write_register(qca, SPI_REG_BFR_SIZE, len); 248 qcaspi_write_register(qca, SPI_REG_BFR_SIZE, len, wr_verify);
243 if (qca->legacy_mode) 249 if (qca->legacy_mode)
244 qcaspi_tx_cmd(qca, QCA7K_SPI_WRITE | QCA7K_SPI_EXTERNAL); 250 qcaspi_tx_cmd(qca, QCA7K_SPI_WRITE | QCA7K_SPI_EXTERNAL);
245 251
@@ -345,6 +351,7 @@ qcaspi_receive(struct qcaspi *qca)
345 351
346 /* Read the packet size. */ 352 /* Read the packet size. */
347 qcaspi_read_register(qca, SPI_REG_RDBUF_BYTE_AVA, &available); 353 qcaspi_read_register(qca, SPI_REG_RDBUF_BYTE_AVA, &available);
354
348 netdev_dbg(net_dev, "qcaspi_receive: SPI_REG_RDBUF_BYTE_AVA: Value: %08x\n", 355 netdev_dbg(net_dev, "qcaspi_receive: SPI_REG_RDBUF_BYTE_AVA: Value: %08x\n",
349 available); 356 available);
350 357
@@ -353,7 +360,7 @@ qcaspi_receive(struct qcaspi *qca)
353 return -1; 360 return -1;
354 } 361 }
355 362
356 qcaspi_write_register(qca, SPI_REG_BFR_SIZE, available); 363 qcaspi_write_register(qca, SPI_REG_BFR_SIZE, available, wr_verify);
357 364
358 if (qca->legacy_mode) 365 if (qca->legacy_mode)
359 qcaspi_tx_cmd(qca, QCA7K_SPI_READ | QCA7K_SPI_EXTERNAL); 366 qcaspi_tx_cmd(qca, QCA7K_SPI_READ | QCA7K_SPI_EXTERNAL);
@@ -524,7 +531,7 @@ qcaspi_qca7k_sync(struct qcaspi *qca, int event)
524 netdev_dbg(qca->net_dev, "sync: resetting device.\n"); 531 netdev_dbg(qca->net_dev, "sync: resetting device.\n");
525 qcaspi_read_register(qca, SPI_REG_SPI_CONFIG, &spi_config); 532 qcaspi_read_register(qca, SPI_REG_SPI_CONFIG, &spi_config);
526 spi_config |= QCASPI_SLAVE_RESET_BIT; 533 spi_config |= QCASPI_SLAVE_RESET_BIT;
527 qcaspi_write_register(qca, SPI_REG_SPI_CONFIG, spi_config); 534 qcaspi_write_register(qca, SPI_REG_SPI_CONFIG, spi_config, 0);
528 535
529 qca->sync = QCASPI_SYNC_RESET; 536 qca->sync = QCASPI_SYNC_RESET;
530 qca->stats.trig_reset++; 537 qca->stats.trig_reset++;
@@ -684,7 +691,7 @@ qcaspi_netdev_close(struct net_device *dev)
684 691
685 netif_stop_queue(dev); 692 netif_stop_queue(dev);
686 693
687 qcaspi_write_register(qca, SPI_REG_INTR_ENABLE, 0); 694 qcaspi_write_register(qca, SPI_REG_INTR_ENABLE, 0, wr_verify);
688 free_irq(qca->spi_dev->irq, qca); 695 free_irq(qca->spi_dev->irq, qca);
689 696
690 kthread_stop(qca->spi_thread); 697 kthread_stop(qca->spi_thread);
@@ -904,6 +911,13 @@ qca_spi_probe(struct spi_device *spi)
904 return -EINVAL; 911 return -EINVAL;
905 } 912 }
906 913
914 if (wr_verify < QCASPI_WRITE_VERIFY_MIN ||
915 wr_verify > QCASPI_WRITE_VERIFY_MAX) {
916 dev_err(&spi->dev, "Invalid write verify: %d\n",
917 wr_verify);
918 return -EINVAL;
919 }
920
907 dev_info(&spi->dev, "ver=%s, clkspeed=%d, burst_len=%d, pluggable=%d\n", 921 dev_info(&spi->dev, "ver=%s, clkspeed=%d, burst_len=%d, pluggable=%d\n",
908 QCASPI_DRV_VERSION, 922 QCASPI_DRV_VERSION,
909 qcaspi_clkspeed, 923 qcaspi_clkspeed,
diff --git a/drivers/net/ethernet/qualcomm/qca_spi.h b/drivers/net/ethernet/qualcomm/qca_spi.h
index fc0e98726b36..2d2c49726492 100644
--- a/drivers/net/ethernet/qualcomm/qca_spi.h
+++ b/drivers/net/ethernet/qualcomm/qca_spi.h
@@ -73,6 +73,7 @@ struct qcaspi_stats {
73 u64 write_buf_miss; 73 u64 write_buf_miss;
74 u64 ring_full; 74 u64 ring_full;
75 u64 spi_err; 75 u64 spi_err;
76 u64 write_verify_failed;
76}; 77};
77 78
78struct qcaspi { 79struct qcaspi {