123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 |
- /*
- * 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 <linux/device.h>
- #include <linux/errno.h>
- #include <linux/etherdevice.h>
- #include <linux/if_arp.h>
- #include <linux/if_ether.h>
- #include <linux/jiffies.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/netdevice.h>
- #include <linux/of.h>
- #include <linux/of_device.h>
- #include <linux/of_net.h>
- #include <linux/sched.h>
- #include <linux/serdev.h>
- #include <linux/skbuff.h>
- #include <linux/types.h>
- #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 <stefan.wahren@i2se.com>");
- MODULE_LICENSE("Dual BSD/GPL");
- MODULE_VERSION(QCAUART_DRV_VERSION);
|