From patchwork Tue Sep 11 15:26:47 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Wahren X-Patchwork-Id: 968607 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=i2se.com Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 428phG0yM2z9s2P for ; Wed, 12 Sep 2018 01:27:58 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727150AbeIKU1e (ORCPT ); Tue, 11 Sep 2018 16:27:34 -0400 Received: from mout.kundenserver.de ([212.227.126.187]:54543 "EHLO mout.kundenserver.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726840AbeIKU1d (ORCPT ); Tue, 11 Sep 2018 16:27:33 -0400 Received: from stefan-Vostro-260.fritz.box ([109.104.52.203]) by mrelayeu.kundenserver.de (mreue011 [212.227.15.167]) with ESMTPSA (Nemesis) id 1MKbTo-1gIMp40u1k-00KzVy; Tue, 11 Sep 2018 17:27:38 +0200 From: Stefan Wahren To: "David S. Miller" Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Stefan Wahren , Michael Heimpold Subject: [PATCH RFC] net: qca_spi: Introduce write register verification Date: Tue, 11 Sep 2018 17:26:47 +0200 Message-Id: <1536679607-20422-1-git-send-email-stefan.wahren@i2se.com> X-Mailer: git-send-email 2.7.4 X-Provags-ID: V03:K1:a4aZ76U/Dy5CLJuPZgxKe9KWE9Kpl15hv83py8l7gopqtQaHcd1 Xpxr5CJmCFAGO9cbXyKxG7M7yuVb/mKn5lQeNGOt3zdrkavJkjduP8wpXRqjyp6gLF/GP0B 8K/lCvea4LjqgSHC3birc4VrHa1ZXDLHunMnrBHWK33LNms8NajYZmfZp+w8VyjEtyVZhji NgPzgu+j8Z/wBddChAaLg== X-UI-Out-Filterresults: notjunk:1; V01:K0:3ZchTU0GB3Q=:kAHJ2t3u30Jeu5TijHJvE3 FzCr5v+N7kHVKWi06iHiHbHemoiMGqnBCa+PezsxwOm2Pi1zWfstHRYhAmik3+pvU81CjFPwH g02oqrYovjJo84I9YZPSAbBSbpRpp05KAwDeo4Wz+4k5+sz4NFzP3P3+tF6ZBnhEuIsvXkwe0 GH0kYv923d/G1GoK+NrVWOSjerm13uWAv9m77OCyPMkSaixGsyFJUNF8G19qS56W4gZdHitBY sRNZfabtdkk6YqTkR/WSAKdJkEix+jmqGTOSUHvfyYT+39wGfiP/DzhKrnTfpFookhVL69lYx qMLL9XkS5kHMKT6oagZd1oRyW37xrOVCb3znpl3AOwKYgOSHNLYHoJEKDebYw0J8sBAHXbSAy jBxwXJW9hTABitOqKc6FAqvQwYgeZ6AMSUlDey6g2fHpXjRQELk/Nfr/j8MPiepKBswSsEPkr caOP0Si6ga54pvPY32Bk9CyK2pgz8Q2FIbMPi4oRsvNfvZ1PRXtHQ/lFMfKMg7OOR8K5/vy9p tgvRHqqnG3uaSxB7zdtjCWa4KFYXQ2mRjbTgDnL8EA4+j9DMnnubB52CeYooZMyAko6Hl8GRL xq/uD+Ufmo6ls68txsOf1C+tg6iX0WinDmW4jAxdEkMfirLidJL3Bsavr7RXlo653q7BnJcbA CzXTjtY5sLEUrKlC9Iu5nrXj1gRGg0Oox0LWCYBEbCbgNMirO7bcjv4VM7bPzGu69bpw= Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 Signed-off-by: Stefan Wahren --- drivers/net/ethernet/qualcomm/qca_7k.c | 34 +++++++++++++++++++++++++++++-- drivers/net/ethernet/qualcomm/qca_7k.h | 2 +- drivers/net/ethernet/qualcomm/qca_debug.c | 1 + drivers/net/ethernet/qualcomm/qca_spi.c | 28 ++++++++++++++++++------- drivers/net/ethernet/qualcomm/qca_spi.h | 1 + 5 files changed, 56 insertions(+), 10 deletions(-) Hi, this patch requires "net: qca_spi: Fix race condition in spi transfers" to be applied. Stefan diff --git a/drivers/net/ethernet/qualcomm/qca_7k.c b/drivers/net/ethernet/qualcomm/qca_7k.c index 6c8543f..4292c89 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) 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; +} diff --git a/drivers/net/ethernet/qualcomm/qca_7k.h b/drivers/net/ethernet/qualcomm/qca_7k.h index 27124c2..356de8e 100644 --- a/drivers/net/ethernet/qualcomm/qca_7k.h +++ b/drivers/net/ethernet/qualcomm/qca_7k.h @@ -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 */ diff --git a/drivers/net/ethernet/qualcomm/qca_debug.c b/drivers/net/ethernet/qualcomm/qca_debug.c index 51d89c8..a9f1bc0 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] = { "Write buffer misses", "Transmit ring full", "SPI errors", + "Write verify errors", }; #ifdef CONFIG_DEBUG_FS diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index 66b775d..d531050 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; 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, diff --git a/drivers/net/ethernet/qualcomm/qca_spi.h b/drivers/net/ethernet/qualcomm/qca_spi.h index fc0e987..2d2c497 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.h +++ b/drivers/net/ethernet/qualcomm/qca_spi.h @@ -73,6 +73,7 @@ struct qcaspi_stats { u64 write_buf_miss; u64 ring_full; u64 spi_err; + u64 write_verify_failed; }; struct qcaspi {