From ce68349a356d60d942ce5758d835e9cb00dcf61d Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:09 +0200 Subject: [PATCH 01/17] net: qualcomm: qca_7k: clean up header includes Currently the includes doesn't reflect the dependencies. So fix this up by removing all unnecessary entries and add the necessary ones explicit. Signed-off-by: Stefan Wahren Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/qca_7k.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/qca_7k.c b/drivers/net/ethernet/qualcomm/qca_7k.c index f0066fbb44a6..e9162e1f0c7f 100644 --- a/drivers/net/ethernet/qualcomm/qca_7k.c +++ b/drivers/net/ethernet/qualcomm/qca_7k.c @@ -23,11 +23,9 @@ * kernel-based SPI device. */ -#include -#include -#include +#include +#include #include -#include #include "qca_7k.h" From e75977b4b88c3b226c7d3964a048fa8c75659b02 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:10 +0200 Subject: [PATCH 02/17] net: qca_framing: use u16 for frame offset It doesn't make sense to use a signed variable for offset here, so fix it up. Signed-off-by: Stefan Wahren Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/qca_framing.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qualcomm/qca_framing.h b/drivers/net/ethernet/qualcomm/qca_framing.h index d5e795dcdf47..8b385e6f22c9 100644 --- a/drivers/net/ethernet/qualcomm/qca_framing.h +++ b/drivers/net/ethernet/qualcomm/qca_framing.h @@ -103,7 +103,7 @@ struct qcafrm_handle { enum qcafrm_state state; /* Offset in buffer (borrowed for length too) */ - s16 offset; + u16 offset; /* Frame length as kept by this module */ u16 len; From 661ec067b035029ee1c8d282636889a5fd2ddfb3 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:11 +0200 Subject: [PATCH 03/17] net: qca_7k: Use BIT macro Use the BIT macro for the CONFIG and INT register values. Signed-off-by: Stefan Wahren Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/qca_7k.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/qca_7k.h b/drivers/net/ethernet/qualcomm/qca_7k.h index 1cad851ee507..4047f0aeb47a 100644 --- a/drivers/net/ethernet/qualcomm/qca_7k.h +++ b/drivers/net/ethernet/qualcomm/qca_7k.h @@ -54,15 +54,15 @@ #define SPI_REG_ACTION_CTRL 0x1B00 /* SPI_CONFIG register definition; */ -#define QCASPI_SLAVE_RESET_BIT (1 << 6) +#define QCASPI_SLAVE_RESET_BIT BIT(6) /* INTR_CAUSE/ENABLE register definition. */ -#define SPI_INT_WRBUF_BELOW_WM (1 << 10) -#define SPI_INT_CPU_ON (1 << 6) -#define SPI_INT_ADDR_ERR (1 << 3) -#define SPI_INT_WRBUF_ERR (1 << 2) -#define SPI_INT_RDBUF_ERR (1 << 1) -#define SPI_INT_PKT_AVLBL (1 << 0) +#define SPI_INT_WRBUF_BELOW_WM BIT(10) +#define SPI_INT_CPU_ON BIT(6) +#define SPI_INT_ADDR_ERR BIT(3) +#define SPI_INT_WRBUF_ERR BIT(2) +#define SPI_INT_RDBUF_ERR BIT(1) +#define SPI_INT_PKT_AVLBL BIT(0) void qcaspi_spi_error(struct qcaspi *qca); int qcaspi_read_register(struct qcaspi *qca, u16 reg, u16 *result); From 00bc2e49e7bfd499ee925f552155c328d765bf8a Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:12 +0200 Subject: [PATCH 04/17] net: qca_spi: Use SET_NETDEV_DEV() Use SET_NETDEV_DEV() in qca_spi to create the "/sys/class/net//device" symlink. Signed-off-by: Stefan Wahren Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/qca_spi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index 24ca7df15d07..0c3fdeee27ea 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -894,6 +894,7 @@ qca_spi_probe(struct spi_device *spi) return -ENOMEM; qcaspi_netdev_setup(qcaspi_devs); + SET_NETDEV_DEV(qcaspi_devs, &spi->dev); qca = netdev_priv(qcaspi_devs); if (!qca) { From 0324e75db24bb99c6b7e096d84f273429eb94163 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:13 +0200 Subject: [PATCH 05/17] net: qualcomm: use net_device_ops instead of direct call There is no need to export qcaspi_netdev_open and qcaspi_netdev_close because they are also accessible via the net_device_ops. Signed-off-by: Stefan Wahren Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/qca_debug.c | 5 +++-- drivers/net/ethernet/qualcomm/qca_spi.c | 4 ++-- drivers/net/ethernet/qualcomm/qca_spi.h | 3 --- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/qca_debug.c b/drivers/net/ethernet/qualcomm/qca_debug.c index d145df98feff..92b6be9c4429 100644 --- a/drivers/net/ethernet/qualcomm/qca_debug.c +++ b/drivers/net/ethernet/qualcomm/qca_debug.c @@ -275,6 +275,7 @@ qcaspi_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ring) static int qcaspi_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ring) { + const struct net_device_ops *ops = dev->netdev_ops; struct qcaspi *qca = netdev_priv(dev); if ((ring->rx_pending) || @@ -283,13 +284,13 @@ qcaspi_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ring) return -EINVAL; if (netif_running(dev)) - qcaspi_netdev_close(dev); + ops->ndo_stop(dev); qca->txr.count = max_t(u32, ring->tx_pending, TX_RING_MIN_LEN); qca->txr.count = min_t(u16, qca->txr.count, TX_RING_MAX_LEN); if (netif_running(dev)) - qcaspi_netdev_open(dev); + ops->ndo_open(dev); return 0; } diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index 0c3fdeee27ea..7e039e37a3c7 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -603,7 +603,7 @@ qcaspi_intr_handler(int irq, void *data) return IRQ_HANDLED; } -int +static int qcaspi_netdev_open(struct net_device *dev) { struct qcaspi *qca = netdev_priv(dev); @@ -640,7 +640,7 @@ qcaspi_netdev_open(struct net_device *dev) return 0; } -int +static int qcaspi_netdev_close(struct net_device *dev) { struct qcaspi *qca = netdev_priv(dev); diff --git a/drivers/net/ethernet/qualcomm/qca_spi.h b/drivers/net/ethernet/qualcomm/qca_spi.h index 6e31a0e744a4..064853ddd678 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.h +++ b/drivers/net/ethernet/qualcomm/qca_spi.h @@ -108,7 +108,4 @@ struct qcaspi { u16 burst_len; }; -int qcaspi_netdev_open(struct net_device *dev); -int qcaspi_netdev_close(struct net_device *dev); - #endif /* _QCA_SPI_H */ From 2280e3cf1d7feb974d35b34863e9ed6e9c56b257 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:14 +0200 Subject: [PATCH 06/17] net: qualcomm: Improve readability of length defines In order to avoid mixing things up, make the MTU and frame length defines easier to read. Signed-off-by: Stefan Wahren Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/qca_framing.c | 2 +- drivers/net/ethernet/qualcomm/qca_framing.h | 8 ++++---- drivers/net/ethernet/qualcomm/qca_spi.c | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/qca_framing.c b/drivers/net/ethernet/qualcomm/qca_framing.c index faa924c85e29..2341f2bf574d 100644 --- a/drivers/net/ethernet/qualcomm/qca_framing.c +++ b/drivers/net/ethernet/qualcomm/qca_framing.c @@ -117,7 +117,7 @@ qcafrm_fsm_decode(struct qcafrm_handle *handle, u8 *buf, u16 buf_len, u8 recv_by break; case QCAFRM_WAIT_RSVD_BYTE2: len = handle->offset; - if (len > buf_len || len < QCAFRM_ETHMINLEN) { + if (len > buf_len || len < QCAFRM_MIN_LEN) { ret = QCAFRM_INVLEN; handle->state = QCAFRM_HW_LEN0; } else { diff --git a/drivers/net/ethernet/qualcomm/qca_framing.h b/drivers/net/ethernet/qualcomm/qca_framing.h index 8b385e6f22c9..5df7c65d887c 100644 --- a/drivers/net/ethernet/qualcomm/qca_framing.h +++ b/drivers/net/ethernet/qualcomm/qca_framing.h @@ -44,12 +44,12 @@ #define QCAFRM_INVFRAME (QCAFRM_ERR_BASE - 4) /* Min/Max Ethernet MTU: 46/1500 */ -#define QCAFRM_ETHMINMTU (ETH_ZLEN - ETH_HLEN) -#define QCAFRM_ETHMAXMTU ETH_DATA_LEN +#define QCAFRM_MIN_MTU (ETH_ZLEN - ETH_HLEN) +#define QCAFRM_MAX_MTU ETH_DATA_LEN /* Min/Max frame lengths */ -#define QCAFRM_ETHMINLEN (QCAFRM_ETHMINMTU + ETH_HLEN) -#define QCAFRM_ETHMAXLEN (QCAFRM_ETHMAXMTU + VLAN_ETH_HLEN) +#define QCAFRM_MIN_LEN (QCAFRM_MIN_MTU + ETH_HLEN) +#define QCAFRM_MAX_LEN (QCAFRM_MAX_MTU + VLAN_ETH_HLEN) /* QCA7K header len */ #define QCAFRM_HEADER_LEN 8 diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index 7e039e37a3c7..f15554867ef7 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -69,7 +69,7 @@ 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_MTU QCAFRM_ETHMAXMTU +#define QCASPI_MTU QCAFRM_MAX_MTU #define QCASPI_TX_TIMEOUT (1 * HZ) #define QCASPI_QCA7K_REBOOT_TIME_MS 1000 @@ -403,7 +403,7 @@ qcaspi_tx_ring_has_space(struct tx_ring *txr) if (txr->skb[txr->tail]) return 0; - return (txr->size + QCAFRM_ETHMAXLEN < QCASPI_HW_BUF_LEN) ? 1 : 0; + return (txr->size + QCAFRM_MAX_LEN < QCASPI_HW_BUF_LEN) ? 1 : 0; } /* Flush the tx ring. This function is only safe to @@ -667,8 +667,8 @@ qcaspi_netdev_xmit(struct sk_buff *skb, struct net_device *dev) struct sk_buff *tskb; u8 pad_len = 0; - if (skb->len < QCAFRM_ETHMINLEN) - pad_len = QCAFRM_ETHMINLEN - skb->len; + if (skb->len < QCAFRM_MIN_LEN) + pad_len = QCAFRM_MIN_LEN - skb->len; if (qca->txr.skb[qca->txr.tail]) { netdev_warn(qca->net_dev, "queue was unexpectedly full!\n"); @@ -805,8 +805,8 @@ qcaspi_netdev_setup(struct net_device *dev) dev->tx_queue_len = 100; /* MTU range: 46 - 1500 */ - dev->min_mtu = QCAFRM_ETHMINMTU; - dev->max_mtu = QCAFRM_ETHMAXMTU; + dev->min_mtu = QCAFRM_MIN_MTU; + dev->max_mtu = QCAFRM_MAX_MTU; qca = netdev_priv(dev); memset(qca, 0, sizeof(struct qcaspi)); From 96a2227c3ff006bf600f1c2ef08f470f3ccae90e Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:15 +0200 Subject: [PATCH 07/17] net: qca_spi: remove QCASPI_MTU There is no need for an additional MTU define. Signed-off-by: Stefan Wahren Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/qca_spi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index f15554867ef7..74646286fd0f 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -69,7 +69,6 @@ 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_MTU QCAFRM_MAX_MTU #define QCASPI_TX_TIMEOUT (1 * HZ) #define QCASPI_QCA7K_REBOOT_TIME_MS 1000 @@ -746,7 +745,7 @@ qcaspi_netdev_init(struct net_device *dev) { struct qcaspi *qca = netdev_priv(dev); - dev->mtu = QCASPI_MTU; + dev->mtu = QCAFRM_MAX_MTU; dev->type = ARPHRD_ETHER; qca->clkspeed = qcaspi_clkspeed; qca->burst_len = qcaspi_burst_len; From 55b7a770aef7beeb45e69c4833a22c94a074dcb3 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:16 +0200 Subject: [PATCH 08/17] net: qualcomm: move qcaspi_tx_cmd to qca_spi.c The function qcaspi_tx_cmd() is only called from qca_spi.c. So we better move it there. Signed-off-by: Stefan Wahren Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/qca_7k.c | 24 ------------------------ drivers/net/ethernet/qualcomm/qca_7k.h | 1 - drivers/net/ethernet/qualcomm/qca_spi.c | 24 ++++++++++++++++++++++++ 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/qca_7k.c b/drivers/net/ethernet/qualcomm/qca_7k.c index e9162e1f0c7f..ffe7a16bdfc8 100644 --- a/drivers/net/ethernet/qualcomm/qca_7k.c +++ b/drivers/net/ethernet/qualcomm/qca_7k.c @@ -121,27 +121,3 @@ qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value) return ret; } - -int -qcaspi_tx_cmd(struct qcaspi *qca, u16 cmd) -{ - __be16 tx_data; - struct spi_message *msg = &qca->spi_msg1; - struct spi_transfer *transfer = &qca->spi_xfer1; - int ret; - - tx_data = cpu_to_be16(cmd); - transfer->len = sizeof(tx_data); - transfer->tx_buf = &tx_data; - transfer->rx_buf = NULL; - - ret = spi_sync(qca->spi_dev, msg); - - if (!ret) - ret = msg->status; - - if (ret) - qcaspi_spi_error(qca); - - return ret; -} diff --git a/drivers/net/ethernet/qualcomm/qca_7k.h b/drivers/net/ethernet/qualcomm/qca_7k.h index 4047f0aeb47a..27124c2bb77a 100644 --- a/drivers/net/ethernet/qualcomm/qca_7k.h +++ b/drivers/net/ethernet/qualcomm/qca_7k.h @@ -67,6 +67,5 @@ 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_tx_cmd(struct qcaspi *qca, u16 cmd); #endif /* _QCA_7K_H */ diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index 74646286fd0f..50adc4f626e3 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -191,6 +191,30 @@ qcaspi_read_legacy(struct qcaspi *qca, u8 *dst, u32 len) return len; } +static int +qcaspi_tx_cmd(struct qcaspi *qca, u16 cmd) +{ + __be16 tx_data; + struct spi_message *msg = &qca->spi_msg1; + struct spi_transfer *transfer = &qca->spi_xfer1; + int ret; + + tx_data = cpu_to_be16(cmd); + transfer->len = sizeof(tx_data); + transfer->tx_buf = &tx_data; + transfer->rx_buf = NULL; + + ret = spi_sync(qca->spi_dev, msg); + + if (!ret) + ret = msg->status; + + if (ret) + qcaspi_spi_error(qca); + + return ret; +} + static int qcaspi_tx_frame(struct qcaspi *qca, struct sk_buff *skb) { From 0d78d5f73adb1f8a789ceaaa4aca58c52bd1d160 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:17 +0200 Subject: [PATCH 09/17] net: qca_spi: Clarify MODULE_DESCRIPTION Since this driver is specific to the QCA7000, we should make the module description more precisely. Signed-off-by: Stefan Wahren Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/qca_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index 50adc4f626e3..ee90af31bd65 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -999,7 +999,7 @@ static struct spi_driver qca_spi_driver = { }; module_spi_driver(qca_spi_driver); -MODULE_DESCRIPTION("Qualcomm Atheros SPI Driver"); +MODULE_DESCRIPTION("Qualcomm Atheros QCA7000 SPI Driver"); MODULE_AUTHOR("Qualcomm Atheros Communications"); MODULE_AUTHOR("Stefan Wahren "); MODULE_LICENSE("Dual BSD/GPL"); From f1789286e0200f6a40fc20b05ee0e5e62d044a9e Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:18 +0200 Subject: [PATCH 10/17] net: qualcomm: rename qca_framing.c to qca_7k_common.c As preparation for the upcoming UART driver we need a module which contains common functions for both interfaces. The module qca_framing is a good candidate but renaming to qca_7k_common would make it clear. Signed-off-by: Stefan Wahren Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/Makefile | 2 +- .../net/ethernet/qualcomm/{qca_framing.c => qca_7k_common.c} | 2 +- .../net/ethernet/qualcomm/{qca_framing.h => qca_7k_common.h} | 0 drivers/net/ethernet/qualcomm/qca_spi.c | 2 +- drivers/net/ethernet/qualcomm/qca_spi.h | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename drivers/net/ethernet/qualcomm/{qca_framing.c => qca_7k_common.c} (99%) rename drivers/net/ethernet/qualcomm/{qca_framing.h => qca_7k_common.h} (100%) diff --git a/drivers/net/ethernet/qualcomm/Makefile b/drivers/net/ethernet/qualcomm/Makefile index aacb0a585c68..5e17bf116e75 100644 --- a/drivers/net/ethernet/qualcomm/Makefile +++ b/drivers/net/ethernet/qualcomm/Makefile @@ -3,6 +3,6 @@ # obj-$(CONFIG_QCA7000) += qcaspi.o -qcaspi-objs := qca_spi.o qca_framing.o qca_7k.o qca_debug.o +qcaspi-objs := qca_spi.o qca_7k_common.o qca_7k.o qca_debug.o obj-y += emac/ diff --git a/drivers/net/ethernet/qualcomm/qca_framing.c b/drivers/net/ethernet/qualcomm/qca_7k_common.c similarity index 99% rename from drivers/net/ethernet/qualcomm/qca_framing.c rename to drivers/net/ethernet/qualcomm/qca_7k_common.c index 2341f2bf574d..6d17fbd47c6a 100644 --- a/drivers/net/ethernet/qualcomm/qca_framing.c +++ b/drivers/net/ethernet/qualcomm/qca_7k_common.c @@ -23,7 +23,7 @@ #include -#include "qca_framing.h" +#include "qca_7k_common.h" u16 qcafrm_create_header(u8 *buf, u16 length) diff --git a/drivers/net/ethernet/qualcomm/qca_framing.h b/drivers/net/ethernet/qualcomm/qca_7k_common.h similarity index 100% rename from drivers/net/ethernet/qualcomm/qca_framing.h rename to drivers/net/ethernet/qualcomm/qca_7k_common.h diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index ee90af31bd65..43cc7de0b395 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -43,8 +43,8 @@ #include #include "qca_7k.h" +#include "qca_7k_common.h" #include "qca_debug.h" -#include "qca_framing.h" #include "qca_spi.h" #define MAX_DMA_BURST_LEN 5000 diff --git a/drivers/net/ethernet/qualcomm/qca_spi.h b/drivers/net/ethernet/qualcomm/qca_spi.h index 064853ddd678..fc4beb1b32d1 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.h +++ b/drivers/net/ethernet/qualcomm/qca_spi.h @@ -32,7 +32,7 @@ #include #include -#include "qca_framing.h" +#include "qca_7k_common.h" #define QCASPI_DRV_VERSION "0.2.7-i" #define QCASPI_DRV_NAME "qcaspi" From 60d6702464b9d667312035eb3bd9d390af9626dd Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:19 +0200 Subject: [PATCH 11/17] net: qualcomm: prepare frame decoding for UART driver Unfortunately the frame format is not exactly identical between SPI and UART. In case of SPI there is an additional HW length at the beginning. So store the initial state to make the decoding state machine more flexible and easy to extend for UART support. Signed-off-by: Stefan Wahren Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/qca_7k_common.c | 12 ++++++------ drivers/net/ethernet/qualcomm/qca_7k_common.h | 8 ++++++-- drivers/net/ethernet/qualcomm/qca_spi.c | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/qca_7k_common.c b/drivers/net/ethernet/qualcomm/qca_7k_common.c index 6d17fbd47c6a..0d3daa95cc70 100644 --- a/drivers/net/ethernet/qualcomm/qca_7k_common.c +++ b/drivers/net/ethernet/qualcomm/qca_7k_common.c @@ -83,7 +83,7 @@ qcafrm_fsm_decode(struct qcafrm_handle *handle, u8 *buf, u16 buf_len, u8 recv_by if (recv_byte != 0x00) { /* first two bytes of length must be 0 */ - handle->state = QCAFRM_HW_LEN0; + handle->state = handle->init; } break; case QCAFRM_HW_LEN2: @@ -97,7 +97,7 @@ qcafrm_fsm_decode(struct qcafrm_handle *handle, u8 *buf, u16 buf_len, u8 recv_by case QCAFRM_WAIT_AA4: if (recv_byte != 0xAA) { ret = QCAFRM_NOHEAD; - handle->state = QCAFRM_HW_LEN0; + handle->state = handle->init; } else { handle->state--; } @@ -119,7 +119,7 @@ qcafrm_fsm_decode(struct qcafrm_handle *handle, u8 *buf, u16 buf_len, u8 recv_by len = handle->offset; if (len > buf_len || len < QCAFRM_MIN_LEN) { ret = QCAFRM_INVLEN; - handle->state = QCAFRM_HW_LEN0; + handle->state = handle->init; } else { handle->state = (enum qcafrm_state)(len + 1); /* Remaining number of bytes. */ @@ -135,7 +135,7 @@ qcafrm_fsm_decode(struct qcafrm_handle *handle, u8 *buf, u16 buf_len, u8 recv_by case QCAFRM_WAIT_551: if (recv_byte != 0x55) { ret = QCAFRM_NOTAIL; - handle->state = QCAFRM_HW_LEN0; + handle->state = handle->init; } else { handle->state = QCAFRM_WAIT_552; } @@ -143,11 +143,11 @@ qcafrm_fsm_decode(struct qcafrm_handle *handle, u8 *buf, u16 buf_len, u8 recv_by case QCAFRM_WAIT_552: if (recv_byte != 0x55) { ret = QCAFRM_NOTAIL; - handle->state = QCAFRM_HW_LEN0; + handle->state = handle->init; } else { ret = handle->offset; /* Frame is fully received. */ - handle->state = QCAFRM_HW_LEN0; + handle->state = handle->init; } break; } diff --git a/drivers/net/ethernet/qualcomm/qca_7k_common.h b/drivers/net/ethernet/qualcomm/qca_7k_common.h index 5df7c65d887c..07bdd6c4f728 100644 --- a/drivers/net/ethernet/qualcomm/qca_7k_common.h +++ b/drivers/net/ethernet/qualcomm/qca_7k_common.h @@ -61,6 +61,7 @@ #define QCAFRM_ERR_BASE -1000 enum qcafrm_state { + /* HW length is only available on SPI */ QCAFRM_HW_LEN0 = 0x8000, QCAFRM_HW_LEN1 = QCAFRM_HW_LEN0 - 1, QCAFRM_HW_LEN2 = QCAFRM_HW_LEN1 - 1, @@ -101,6 +102,8 @@ enum qcafrm_state { struct qcafrm_handle { /* Current decoding state */ enum qcafrm_state state; + /* Initial state depends on connection type */ + enum qcafrm_state init; /* Offset in buffer (borrowed for length too) */ u16 offset; @@ -113,9 +116,10 @@ u16 qcafrm_create_header(u8 *buf, u16 len); u16 qcafrm_create_footer(u8 *buf); -static inline void qcafrm_fsm_init(struct qcafrm_handle *handle) +static inline void qcafrm_fsm_init_spi(struct qcafrm_handle *handle) { - handle->state = QCAFRM_HW_LEN0; + handle->init = QCAFRM_HW_LEN0; + handle->state = handle->init; } /* Gather received bytes and try to extract a full Ethernet frame diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index 43cc7de0b395..de78f60309a0 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -638,7 +638,7 @@ qcaspi_netdev_open(struct net_device *dev) qca->intr_req = 1; qca->intr_svc = 0; qca->sync = QCASPI_SYNC_UNKNOWN; - qcafrm_fsm_init(&qca->frm_handle); + qcafrm_fsm_init_spi(&qca->frm_handle); qca->spi_thread = kthread_run((void *)qcaspi_spi_thread, qca, "%s", dev->name); From b2f98200c73cf4bd7c26438490b5a137210e9c7d Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:20 +0200 Subject: [PATCH 12/17] net: qualcomm: make qca_7k_common a separate kernel module In order to share common functions between QCA7000 SPI and UART protocol driver the qca_7k_common needs to be a separate kernel module. Signed-off-by: Stefan Wahren Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/Kconfig | 8 +++++++- drivers/net/ethernet/qualcomm/Makefile | 5 +++-- drivers/net/ethernet/qualcomm/qca_7k_common.c | 10 ++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/qualcomm/Kconfig b/drivers/net/ethernet/qualcomm/Kconfig index d7720bf92d49..b4c369dccde7 100644 --- a/drivers/net/ethernet/qualcomm/Kconfig +++ b/drivers/net/ethernet/qualcomm/Kconfig @@ -16,7 +16,13 @@ config NET_VENDOR_QUALCOMM if NET_VENDOR_QUALCOMM config QCA7000 - tristate "Qualcomm Atheros QCA7000 support" + tristate + help + This enables support for the Qualcomm Atheros QCA7000. + +config QCA7000_SPI + tristate "Qualcomm Atheros QCA7000 SPI support" + select QCA7000 depends on SPI_MASTER && OF ---help--- This SPI protocol driver supports the Qualcomm Atheros QCA7000. diff --git a/drivers/net/ethernet/qualcomm/Makefile b/drivers/net/ethernet/qualcomm/Makefile index 5e17bf116e75..65556ca185a6 100644 --- a/drivers/net/ethernet/qualcomm/Makefile +++ b/drivers/net/ethernet/qualcomm/Makefile @@ -2,7 +2,8 @@ # Makefile for the Qualcomm network device drivers. # -obj-$(CONFIG_QCA7000) += qcaspi.o -qcaspi-objs := qca_spi.o qca_7k_common.o qca_7k.o qca_debug.o +obj-$(CONFIG_QCA7000) += qca_7k_common.o +obj-$(CONFIG_QCA7000_SPI) += qcaspi.o +qcaspi-objs := qca_7k.o qca_debug.o qca_spi.o obj-y += emac/ diff --git a/drivers/net/ethernet/qualcomm/qca_7k_common.c b/drivers/net/ethernet/qualcomm/qca_7k_common.c index 0d3daa95cc70..6b511f05df61 100644 --- a/drivers/net/ethernet/qualcomm/qca_7k_common.c +++ b/drivers/net/ethernet/qualcomm/qca_7k_common.c @@ -21,7 +21,9 @@ * by an atheros frame while transmitted over a serial channel; */ +#include #include +#include #include "qca_7k_common.h" @@ -46,6 +48,7 @@ qcafrm_create_header(u8 *buf, u16 length) return QCAFRM_HEADER_LEN; } +EXPORT_SYMBOL_GPL(qcafrm_create_header); u16 qcafrm_create_footer(u8 *buf) @@ -57,6 +60,7 @@ qcafrm_create_footer(u8 *buf) buf[1] = 0x55; return QCAFRM_FOOTER_LEN; } +EXPORT_SYMBOL_GPL(qcafrm_create_footer); /* Gather received bytes and try to extract a full ethernet frame by * following a simple state machine. @@ -154,3 +158,9 @@ qcafrm_fsm_decode(struct qcafrm_handle *handle, u8 *buf, u16 buf_len, u8 recv_by return ret; } +EXPORT_SYMBOL_GPL(qcafrm_fsm_decode); + +MODULE_DESCRIPTION("Qualcomm Atheros QCA7000 common"); +MODULE_AUTHOR("Qualcomm Atheros Communications"); +MODULE_AUTHOR("Stefan Wahren "); +MODULE_LICENSE("Dual BSD/GPL"); From fd787048a9a8530c7138d629d3dbeabdfaaec000 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:21 +0200 Subject: [PATCH 13/17] dt-bindings: qca7000-spi: Rework binding In preparation for the QCA7000 UART binding rework the binding document. Signed-off-by: Stefan Wahren Signed-off-by: David S. Miller --- .../bindings/net/qca-qca7000-spi.txt | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/Documentation/devicetree/bindings/net/qca-qca7000-spi.txt b/Documentation/devicetree/bindings/net/qca-qca7000-spi.txt index c74989c0d8ac..a37f65678399 100644 --- a/Documentation/devicetree/bindings/net/qca-qca7000-spi.txt +++ b/Documentation/devicetree/bindings/net/qca-qca7000-spi.txt @@ -1,29 +1,38 @@ -* Qualcomm QCA7000 (Ethernet over SPI protocol) +* Qualcomm QCA7000 -Note: The QCA7000 is useable as a SPI device. In this case it must be defined -as a child of a SPI master in the device tree. +The QCA7000 is a serial-to-powerline bridge with a host interface which could +be configured either as SPI or UART slave. This configuration is done by +the QCA7000 firmware. + +(a) Ethernet over SPI + +In order to use the QCA7000 as SPI device it must be defined as a child of a +SPI master in the device tree. Required properties: -- compatible : Should be "qca,qca7000" -- reg : Should specify the SPI chip select -- interrupts : The first cell should specify the index of the source interrupt - and the second cell should specify the trigger type as rising edge -- spi-cpha : Must be set -- spi-cpol: Must be set +- compatible : Should be "qca,qca7000" +- reg : Should specify the SPI chip select +- interrupts : The first cell should specify the index of the source + interrupt and the second cell should specify the trigger + type as rising edge +- spi-cpha : Must be set +- spi-cpol : Must be set Optional properties: -- interrupt-parent : Specify the pHandle of the source interrupt +- interrupt-parent : Specify the pHandle of the source interrupt - spi-max-frequency : Maximum frequency of the SPI bus the chip can operate at. - Numbers smaller than 1000000 or greater than 16000000 are invalid. Missing - the property will set the SPI frequency to 8000000 Hertz. -- local-mac-address: 6 bytes, MAC address -- qca,legacy-mode : Set the SPI data transfer of the QCA7000 to legacy mode. - In this mode the SPI master must toggle the chip select between each data - word. In burst mode these gaps aren't necessary, which is faster. - This setting depends on how the QCA7000 is setup via GPIO pin strapping. - If the property is missing the driver defaults to burst mode. + Numbers smaller than 1000000 or greater than 16000000 + are invalid. Missing the property will set the SPI + frequency to 8000000 Hertz. +- local-mac-address : see ./ethernet.txt +- qca,legacy-mode : Set the SPI data transfer of the QCA7000 to legacy mode. + In this mode the SPI master must toggle the chip select + between each data word. In burst mode these gaps aren't + necessary, which is faster. This setting depends on how + the QCA7000 is setup via GPIO pin strapping. If the + property is missing the driver defaults to burst mode. -Example: +SPI Example: /* Freescale i.MX28 SPI master*/ ssp2: spi@80014000 { From 4aab28ca446f39ded6fb60c2344d349fd1771d5e Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:22 +0200 Subject: [PATCH 14/17] dt-bindings: qca7000: rename binding Before we can merge the QCA7000 UART binding the document needs to be renamed. Signed-off-by: Stefan Wahren Signed-off-by: David S. Miller --- .../bindings/net/{qca-qca7000-spi.txt => qca,qca7000.txt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Documentation/devicetree/bindings/net/{qca-qca7000-spi.txt => qca,qca7000.txt} (100%) diff --git a/Documentation/devicetree/bindings/net/qca-qca7000-spi.txt b/Documentation/devicetree/bindings/net/qca,qca7000.txt similarity index 100% rename from Documentation/devicetree/bindings/net/qca-qca7000-spi.txt rename to Documentation/devicetree/bindings/net/qca,qca7000.txt From 1820827949201041b233dbe7ac5b5c518944b9dd Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:23 +0200 Subject: [PATCH 15/17] dt-bindings: slave-device: add current-speed property This adds a new DT property to define the current baud rate of the slave device. Signed-off-by: Stefan Wahren Reviewed-by: Rob Herring Signed-off-by: David S. Miller --- .../devicetree/bindings/serial/slave-device.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/serial/slave-device.txt b/Documentation/devicetree/bindings/serial/slave-device.txt index f66037928f5f..40110e019620 100644 --- a/Documentation/devicetree/bindings/serial/slave-device.txt +++ b/Documentation/devicetree/bindings/serial/slave-device.txt @@ -21,6 +21,15 @@ Optional Properties: can support. For example, a particular board has some signal quality issue or the host processor can't support higher baud rates. +- current-speed : The current baud rate the device operates at. This should + only be present in case a driver has no chance to know + the baud rate of the slave device. + Examples: + * device supports auto-baud + * the rate is setup by a bootloader and there is no + way to reset the device + * device baud rate is configured by its firmware but + there is no way to request the actual settings Example: From 6a1db289d00b58ee5dec2e9139a92582e55ae8a8 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:24 +0200 Subject: [PATCH 16/17] dt-bindings: qca7000: append UART interface to binding This merges the serdev binding for the QCA7000 UART driver (Ethernet over UART) into the existing document. Signed-off-by: Stefan Wahren Signed-off-by: David S. Miller --- .../devicetree/bindings/net/qca,qca7000.txt | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Documentation/devicetree/bindings/net/qca,qca7000.txt b/Documentation/devicetree/bindings/net/qca,qca7000.txt index a37f65678399..6d9efb2eb9a5 100644 --- a/Documentation/devicetree/bindings/net/qca,qca7000.txt +++ b/Documentation/devicetree/bindings/net/qca,qca7000.txt @@ -54,3 +54,35 @@ ssp2: spi@80014000 { local-mac-address = [ A0 B0 C0 D0 E0 F0 ]; }; }; + +(b) Ethernet over UART + +In order to use the QCA7000 as UART slave it must be defined as a child of a +UART master in the device tree. It is possible to preconfigure the UART +settings of the QCA7000 firmware, but it's not possible to change them during +runtime. + +Required properties: +- compatible : Should be "qca,qca7000" + +Optional properties: +- local-mac-address : see ./ethernet.txt +- current-speed : current baud rate of QCA7000 which defaults to 115200 + if absent, see also ../serial/slave-device.txt + +UART Example: + +/* Freescale i.MX28 UART */ +auart0: serial@8006a000 { + compatible = "fsl,imx28-auart", "fsl,imx23-auart"; + reg = <0x8006a000 0x2000>; + pinctrl-names = "default"; + pinctrl-0 = <&auart0_2pins_a>; + status = "okay"; + + qca7000: ethernet { + compatible = "qca,qca7000"; + local-mac-address = [ A0 B0 C0 D0 E0 F0 ]; + current-speed = <38400>; + }; +}; From dfc768fbe618d97d0dde88c8e8e586201b0a7587 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Mon, 29 May 2017 13:57:25 +0200 Subject: [PATCH 17/17] net: qualcomm: add QCA7000 UART driver This patch adds the Ethernet over UART driver for the Qualcomm QCA7000 HomePlug GreenPHY. Signed-off-by: Stefan Wahren Reviewed-by: Lino Sanfilippo Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/Kconfig | 16 + drivers/net/ethernet/qualcomm/Makefile | 2 + drivers/net/ethernet/qualcomm/qca_7k_common.h | 6 + drivers/net/ethernet/qualcomm/qca_uart.c | 423 ++++++++++++++++++ 4 files changed, 447 insertions(+) create mode 100644 drivers/net/ethernet/qualcomm/qca_uart.c diff --git a/drivers/net/ethernet/qualcomm/Kconfig b/drivers/net/ethernet/qualcomm/Kconfig index b4c369dccde7..877675a27b9f 100644 --- a/drivers/net/ethernet/qualcomm/Kconfig +++ b/drivers/net/ethernet/qualcomm/Kconfig @@ -30,6 +30,22 @@ config QCA7000_SPI To compile this driver as a module, choose M here. The module will be called qcaspi. +config QCA7000_UART + tristate "Qualcomm Atheros QCA7000 UART support" + select QCA7000 + depends on SERIAL_DEV_BUS && OF + ---help--- + This UART protocol driver supports the Qualcomm Atheros QCA7000. + + Currently the driver assumes these device UART settings: + Data bits: 8 + Parity: None + Stop bits: 1 + Flow control: None + + To compile this driver as a module, choose M here. The module + will be called qcauart. + config QCOM_EMAC tristate "Qualcomm Technologies, Inc. EMAC Gigabit Ethernet support" depends on HAS_DMA && HAS_IOMEM diff --git a/drivers/net/ethernet/qualcomm/Makefile b/drivers/net/ethernet/qualcomm/Makefile index 65556ca185a6..92fa7c4da90a 100644 --- a/drivers/net/ethernet/qualcomm/Makefile +++ b/drivers/net/ethernet/qualcomm/Makefile @@ -5,5 +5,7 @@ obj-$(CONFIG_QCA7000) += qca_7k_common.o obj-$(CONFIG_QCA7000_SPI) += qcaspi.o qcaspi-objs := qca_7k.o qca_debug.o qca_spi.o +obj-$(CONFIG_QCA7000_UART) += qcauart.o +qcauart-objs := qca_uart.o obj-y += emac/ diff --git a/drivers/net/ethernet/qualcomm/qca_7k_common.h b/drivers/net/ethernet/qualcomm/qca_7k_common.h index 07bdd6c4f728..928554f11e35 100644 --- a/drivers/net/ethernet/qualcomm/qca_7k_common.h +++ b/drivers/net/ethernet/qualcomm/qca_7k_common.h @@ -122,6 +122,12 @@ static inline void qcafrm_fsm_init_spi(struct qcafrm_handle *handle) handle->state = handle->init; } +static inline void qcafrm_fsm_init_uart(struct qcafrm_handle *handle) +{ + handle->init = QCAFRM_WAIT_AA1; + handle->state = handle->init; +} + /* Gather received bytes and try to extract a full Ethernet frame * by following a simple state machine. * diff --git a/drivers/net/ethernet/qualcomm/qca_uart.c b/drivers/net/ethernet/qualcomm/qca_uart.c new file mode 100644 index 000000000000..db6068cd7a1f --- /dev/null +++ b/drivers/net/ethernet/qualcomm/qca_uart.c @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc. + * Copyright (c) 2017, I2SE GmbH + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice appear + * in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* This module implements the Qualcomm Atheros UART protocol for + * kernel-based UART device; it is essentially an Ethernet-to-UART + * serial converter; + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qca_7k_common.h" + +#define QCAUART_DRV_VERSION "0.1.0" +#define QCAUART_DRV_NAME "qcauart" +#define QCAUART_TX_TIMEOUT (1 * HZ) + +struct qcauart { + struct net_device *net_dev; + spinlock_t lock; /* transmit lock */ + struct work_struct tx_work; /* Flushes transmit buffer */ + + struct serdev_device *serdev; + struct qcafrm_handle frm_handle; + struct sk_buff *rx_skb; + + unsigned char *tx_head; /* pointer to next XMIT byte */ + int tx_left; /* bytes left in XMIT queue */ + unsigned char *tx_buffer; +}; + +static int +qca_tty_receive(struct serdev_device *serdev, const unsigned char *data, + size_t count) +{ + struct qcauart *qca = serdev_device_get_drvdata(serdev); + struct net_device *netdev = qca->net_dev; + struct net_device_stats *n_stats = &netdev->stats; + size_t i; + + if (!qca->rx_skb) { + qca->rx_skb = netdev_alloc_skb_ip_align(netdev, + netdev->mtu + + VLAN_ETH_HLEN); + if (!qca->rx_skb) { + n_stats->rx_errors++; + n_stats->rx_dropped++; + return 0; + } + } + + for (i = 0; i < count; i++) { + s32 retcode; + + retcode = qcafrm_fsm_decode(&qca->frm_handle, + qca->rx_skb->data, + skb_tailroom(qca->rx_skb), + data[i]); + + switch (retcode) { + case QCAFRM_GATHER: + case QCAFRM_NOHEAD: + break; + case QCAFRM_NOTAIL: + netdev_dbg(netdev, "recv: no RX tail\n"); + n_stats->rx_errors++; + n_stats->rx_dropped++; + break; + case QCAFRM_INVLEN: + netdev_dbg(netdev, "recv: invalid RX length\n"); + n_stats->rx_errors++; + n_stats->rx_dropped++; + break; + default: + n_stats->rx_packets++; + n_stats->rx_bytes += retcode; + skb_put(qca->rx_skb, retcode); + qca->rx_skb->protocol = eth_type_trans( + qca->rx_skb, qca->rx_skb->dev); + qca->rx_skb->ip_summed = CHECKSUM_UNNECESSARY; + netif_rx_ni(qca->rx_skb); + qca->rx_skb = netdev_alloc_skb_ip_align(netdev, + netdev->mtu + + VLAN_ETH_HLEN); + if (!qca->rx_skb) { + netdev_dbg(netdev, "recv: out of RX resources\n"); + n_stats->rx_errors++; + return i; + } + } + } + + return i; +} + +/* Write out any remaining transmit buffer. Scheduled when tty is writable */ +static void qcauart_transmit(struct work_struct *work) +{ + struct qcauart *qca = container_of(work, struct qcauart, tx_work); + struct net_device_stats *n_stats = &qca->net_dev->stats; + int written; + + spin_lock_bh(&qca->lock); + + /* First make sure we're connected. */ + if (!netif_running(qca->net_dev)) { + spin_unlock_bh(&qca->lock); + return; + } + + if (qca->tx_left <= 0) { + /* Now serial buffer is almost free & we can start + * transmission of another packet + */ + n_stats->tx_packets++; + spin_unlock_bh(&qca->lock); + netif_wake_queue(qca->net_dev); + return; + } + + written = serdev_device_write_buf(qca->serdev, qca->tx_head, + qca->tx_left); + if (written > 0) { + qca->tx_left -= written; + qca->tx_head += written; + } + spin_unlock_bh(&qca->lock); +} + +/* Called by the driver when there's room for more data. + * Schedule the transmit. + */ +static void qca_tty_wakeup(struct serdev_device *serdev) +{ + struct qcauart *qca = serdev_device_get_drvdata(serdev); + + schedule_work(&qca->tx_work); +} + +static struct serdev_device_ops qca_serdev_ops = { + .receive_buf = qca_tty_receive, + .write_wakeup = qca_tty_wakeup, +}; + +static int qcauart_netdev_open(struct net_device *dev) +{ + struct qcauart *qca = netdev_priv(dev); + + netif_start_queue(qca->net_dev); + + return 0; +} + +static int qcauart_netdev_close(struct net_device *dev) +{ + struct qcauart *qca = netdev_priv(dev); + + netif_stop_queue(dev); + flush_work(&qca->tx_work); + + spin_lock_bh(&qca->lock); + qca->tx_left = 0; + spin_unlock_bh(&qca->lock); + + return 0; +} + +static netdev_tx_t +qcauart_netdev_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct net_device_stats *n_stats = &dev->stats; + struct qcauart *qca = netdev_priv(dev); + u8 pad_len = 0; + int written; + u8 *pos; + + spin_lock(&qca->lock); + + WARN_ON(qca->tx_left); + + if (!netif_running(dev)) { + spin_unlock(&qca->lock); + netdev_warn(qca->net_dev, "xmit: iface is down\n"); + goto out; + } + + pos = qca->tx_buffer; + + if (skb->len < QCAFRM_MIN_LEN) + pad_len = QCAFRM_MIN_LEN - skb->len; + + pos += qcafrm_create_header(pos, skb->len + pad_len); + + memcpy(pos, skb->data, skb->len); + pos += skb->len; + + if (pad_len) { + memset(pos, 0, pad_len); + pos += pad_len; + } + + pos += qcafrm_create_footer(pos); + + netif_stop_queue(qca->net_dev); + + written = serdev_device_write_buf(qca->serdev, qca->tx_buffer, + pos - qca->tx_buffer); + if (written > 0) { + qca->tx_left = (pos - qca->tx_buffer) - written; + qca->tx_head = qca->tx_buffer + written; + n_stats->tx_bytes += written; + } + spin_unlock(&qca->lock); + + netif_trans_update(dev); +out: + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +static void qcauart_netdev_tx_timeout(struct net_device *dev) +{ + struct qcauart *qca = netdev_priv(dev); + + netdev_info(qca->net_dev, "Transmit timeout at %ld, latency %ld\n", + jiffies, dev_trans_start(dev)); + dev->stats.tx_errors++; + dev->stats.tx_dropped++; +} + +static int qcauart_netdev_init(struct net_device *dev) +{ + struct qcauart *qca = netdev_priv(dev); + size_t len; + + /* Finish setting up the device info. */ + dev->mtu = QCAFRM_MAX_MTU; + dev->type = ARPHRD_ETHER; + + len = QCAFRM_HEADER_LEN + QCAFRM_MAX_LEN + QCAFRM_FOOTER_LEN; + qca->tx_buffer = devm_kmalloc(&qca->serdev->dev, len, GFP_KERNEL); + if (!qca->tx_buffer) + return -ENOMEM; + + qca->rx_skb = netdev_alloc_skb_ip_align(qca->net_dev, + qca->net_dev->mtu + + VLAN_ETH_HLEN); + if (!qca->rx_skb) + return -ENOBUFS; + + return 0; +} + +static void qcauart_netdev_uninit(struct net_device *dev) +{ + struct qcauart *qca = netdev_priv(dev); + + if (qca->rx_skb) + dev_kfree_skb(qca->rx_skb); +} + +static const struct net_device_ops qcauart_netdev_ops = { + .ndo_init = qcauart_netdev_init, + .ndo_uninit = qcauart_netdev_uninit, + .ndo_open = qcauart_netdev_open, + .ndo_stop = qcauart_netdev_close, + .ndo_start_xmit = qcauart_netdev_xmit, + .ndo_set_mac_address = eth_mac_addr, + .ndo_tx_timeout = qcauart_netdev_tx_timeout, + .ndo_validate_addr = eth_validate_addr, +}; + +static void qcauart_netdev_setup(struct net_device *dev) +{ + dev->netdev_ops = &qcauart_netdev_ops; + dev->watchdog_timeo = QCAUART_TX_TIMEOUT; + dev->priv_flags &= ~IFF_TX_SKB_SHARING; + dev->tx_queue_len = 100; + + /* MTU range: 46 - 1500 */ + dev->min_mtu = QCAFRM_MIN_MTU; + dev->max_mtu = QCAFRM_MAX_MTU; +} + +static const struct of_device_id qca_uart_of_match[] = { + { + .compatible = "qca,qca7000", + }, + {} +}; +MODULE_DEVICE_TABLE(of, qca_uart_of_match); + +static int qca_uart_probe(struct serdev_device *serdev) +{ + struct net_device *qcauart_dev = alloc_etherdev(sizeof(struct qcauart)); + struct qcauart *qca; + const char *mac; + u32 speed = 115200; + int ret; + + if (!qcauart_dev) + return -ENOMEM; + + qcauart_netdev_setup(qcauart_dev); + SET_NETDEV_DEV(qcauart_dev, &serdev->dev); + + qca = netdev_priv(qcauart_dev); + if (!qca) { + pr_err("qca_uart: Fail to retrieve private structure\n"); + ret = -ENOMEM; + goto free; + } + qca->net_dev = qcauart_dev; + qca->serdev = serdev; + qcafrm_fsm_init_uart(&qca->frm_handle); + + spin_lock_init(&qca->lock); + INIT_WORK(&qca->tx_work, qcauart_transmit); + + of_property_read_u32(serdev->dev.of_node, "current-speed", &speed); + + mac = of_get_mac_address(serdev->dev.of_node); + + if (mac) + ether_addr_copy(qca->net_dev->dev_addr, mac); + + if (!is_valid_ether_addr(qca->net_dev->dev_addr)) { + eth_hw_addr_random(qca->net_dev); + dev_info(&serdev->dev, "Using random MAC address: %pM\n", + qca->net_dev->dev_addr); + } + + netif_carrier_on(qca->net_dev); + serdev_device_set_drvdata(serdev, qca); + serdev_device_set_client_ops(serdev, &qca_serdev_ops); + + ret = serdev_device_open(serdev); + if (ret) { + dev_err(&serdev->dev, "Unable to open device %s\n", + qcauart_dev->name); + goto free; + } + + speed = serdev_device_set_baudrate(serdev, speed); + dev_info(&serdev->dev, "Using baudrate: %u\n", speed); + + serdev_device_set_flow_control(serdev, false); + + ret = register_netdev(qcauart_dev); + if (ret) { + dev_err(&serdev->dev, "Unable to register net device %s\n", + qcauart_dev->name); + serdev_device_close(serdev); + cancel_work_sync(&qca->tx_work); + goto free; + } + + return 0; + +free: + free_netdev(qcauart_dev); + return ret; +} + +static void qca_uart_remove(struct serdev_device *serdev) +{ + struct qcauart *qca = serdev_device_get_drvdata(serdev); + + unregister_netdev(qca->net_dev); + + /* Flush any pending characters in the driver. */ + serdev_device_close(serdev); + cancel_work_sync(&qca->tx_work); + + free_netdev(qca->net_dev); +} + +static struct serdev_device_driver qca_uart_driver = { + .probe = qca_uart_probe, + .remove = qca_uart_remove, + .driver = { + .name = QCAUART_DRV_NAME, + .of_match_table = of_match_ptr(qca_uart_of_match), + }, +}; + +module_serdev_device_driver(qca_uart_driver); + +MODULE_DESCRIPTION("Qualcomm Atheros QCA7000 UART Driver"); +MODULE_AUTHOR("Qualcomm Atheros Communications"); +MODULE_AUTHOR("Stefan Wahren "); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(QCAUART_DRV_VERSION);