1dfc768fbSStefan Wahren /* 2dfc768fbSStefan Wahren * Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc. 3dfc768fbSStefan Wahren * Copyright (c) 2017, I2SE GmbH 4dfc768fbSStefan Wahren * 5dfc768fbSStefan Wahren * Permission to use, copy, modify, and/or distribute this software 6dfc768fbSStefan Wahren * for any purpose with or without fee is hereby granted, provided 7dfc768fbSStefan Wahren * that the above copyright notice and this permission notice appear 8dfc768fbSStefan Wahren * in all copies. 9dfc768fbSStefan Wahren * 10dfc768fbSStefan Wahren * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11dfc768fbSStefan Wahren * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12dfc768fbSStefan Wahren * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 13dfc768fbSStefan Wahren * THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 14dfc768fbSStefan Wahren * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15dfc768fbSStefan Wahren * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 16dfc768fbSStefan Wahren * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17dfc768fbSStefan Wahren * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18dfc768fbSStefan Wahren */ 19dfc768fbSStefan Wahren 20dfc768fbSStefan Wahren /* This module implements the Qualcomm Atheros UART protocol for 21dfc768fbSStefan Wahren * kernel-based UART device; it is essentially an Ethernet-to-UART 22dfc768fbSStefan Wahren * serial converter; 23dfc768fbSStefan Wahren */ 24dfc768fbSStefan Wahren 25dfc768fbSStefan Wahren #include <linux/device.h> 26dfc768fbSStefan Wahren #include <linux/errno.h> 27dfc768fbSStefan Wahren #include <linux/etherdevice.h> 28dfc768fbSStefan Wahren #include <linux/if_arp.h> 29dfc768fbSStefan Wahren #include <linux/if_ether.h> 30dfc768fbSStefan Wahren #include <linux/jiffies.h> 31dfc768fbSStefan Wahren #include <linux/kernel.h> 32dfc768fbSStefan Wahren #include <linux/module.h> 33dfc768fbSStefan Wahren #include <linux/netdevice.h> 34dfc768fbSStefan Wahren #include <linux/of.h> 35dfc768fbSStefan Wahren #include <linux/of_device.h> 36dfc768fbSStefan Wahren #include <linux/of_net.h> 37dfc768fbSStefan Wahren #include <linux/sched.h> 38dfc768fbSStefan Wahren #include <linux/serdev.h> 39dfc768fbSStefan Wahren #include <linux/skbuff.h> 40dfc768fbSStefan Wahren #include <linux/types.h> 41dfc768fbSStefan Wahren 42dfc768fbSStefan Wahren #include "qca_7k_common.h" 43dfc768fbSStefan Wahren 44dfc768fbSStefan Wahren #define QCAUART_DRV_VERSION "0.1.0" 45dfc768fbSStefan Wahren #define QCAUART_DRV_NAME "qcauart" 46dfc768fbSStefan Wahren #define QCAUART_TX_TIMEOUT (1 * HZ) 47dfc768fbSStefan Wahren 48dfc768fbSStefan Wahren struct qcauart { 49dfc768fbSStefan Wahren struct net_device *net_dev; 50dfc768fbSStefan Wahren spinlock_t lock; /* transmit lock */ 51dfc768fbSStefan Wahren struct work_struct tx_work; /* Flushes transmit buffer */ 52dfc768fbSStefan Wahren 53dfc768fbSStefan Wahren struct serdev_device *serdev; 54dfc768fbSStefan Wahren struct qcafrm_handle frm_handle; 55dfc768fbSStefan Wahren struct sk_buff *rx_skb; 56dfc768fbSStefan Wahren 57dfc768fbSStefan Wahren unsigned char *tx_head; /* pointer to next XMIT byte */ 58dfc768fbSStefan Wahren int tx_left; /* bytes left in XMIT queue */ 59dfc768fbSStefan Wahren unsigned char *tx_buffer; 60dfc768fbSStefan Wahren }; 61dfc768fbSStefan Wahren 62dfc768fbSStefan Wahren static int 63dfc768fbSStefan Wahren qca_tty_receive(struct serdev_device *serdev, const unsigned char *data, 64dfc768fbSStefan Wahren size_t count) 65dfc768fbSStefan Wahren { 66dfc768fbSStefan Wahren struct qcauart *qca = serdev_device_get_drvdata(serdev); 67dfc768fbSStefan Wahren struct net_device *netdev = qca->net_dev; 68dfc768fbSStefan Wahren struct net_device_stats *n_stats = &netdev->stats; 69dfc768fbSStefan Wahren size_t i; 70dfc768fbSStefan Wahren 71dfc768fbSStefan Wahren if (!qca->rx_skb) { 72dfc768fbSStefan Wahren qca->rx_skb = netdev_alloc_skb_ip_align(netdev, 73dfc768fbSStefan Wahren netdev->mtu + 74dfc768fbSStefan Wahren VLAN_ETH_HLEN); 75dfc768fbSStefan Wahren if (!qca->rx_skb) { 76dfc768fbSStefan Wahren n_stats->rx_errors++; 77dfc768fbSStefan Wahren n_stats->rx_dropped++; 78dfc768fbSStefan Wahren return 0; 79dfc768fbSStefan Wahren } 80dfc768fbSStefan Wahren } 81dfc768fbSStefan Wahren 82dfc768fbSStefan Wahren for (i = 0; i < count; i++) { 83dfc768fbSStefan Wahren s32 retcode; 84dfc768fbSStefan Wahren 85dfc768fbSStefan Wahren retcode = qcafrm_fsm_decode(&qca->frm_handle, 86dfc768fbSStefan Wahren qca->rx_skb->data, 87dfc768fbSStefan Wahren skb_tailroom(qca->rx_skb), 88dfc768fbSStefan Wahren data[i]); 89dfc768fbSStefan Wahren 90dfc768fbSStefan Wahren switch (retcode) { 91dfc768fbSStefan Wahren case QCAFRM_GATHER: 92dfc768fbSStefan Wahren case QCAFRM_NOHEAD: 93dfc768fbSStefan Wahren break; 94dfc768fbSStefan Wahren case QCAFRM_NOTAIL: 95dfc768fbSStefan Wahren netdev_dbg(netdev, "recv: no RX tail\n"); 96dfc768fbSStefan Wahren n_stats->rx_errors++; 97dfc768fbSStefan Wahren n_stats->rx_dropped++; 98dfc768fbSStefan Wahren break; 99dfc768fbSStefan Wahren case QCAFRM_INVLEN: 100dfc768fbSStefan Wahren netdev_dbg(netdev, "recv: invalid RX length\n"); 101dfc768fbSStefan Wahren n_stats->rx_errors++; 102dfc768fbSStefan Wahren n_stats->rx_dropped++; 103dfc768fbSStefan Wahren break; 104dfc768fbSStefan Wahren default: 105dfc768fbSStefan Wahren n_stats->rx_packets++; 106dfc768fbSStefan Wahren n_stats->rx_bytes += retcode; 107dfc768fbSStefan Wahren skb_put(qca->rx_skb, retcode); 108dfc768fbSStefan Wahren qca->rx_skb->protocol = eth_type_trans( 109dfc768fbSStefan Wahren qca->rx_skb, qca->rx_skb->dev); 110dfc768fbSStefan Wahren qca->rx_skb->ip_summed = CHECKSUM_UNNECESSARY; 111dfc768fbSStefan Wahren netif_rx_ni(qca->rx_skb); 112dfc768fbSStefan Wahren qca->rx_skb = netdev_alloc_skb_ip_align(netdev, 113dfc768fbSStefan Wahren netdev->mtu + 114dfc768fbSStefan Wahren VLAN_ETH_HLEN); 115dfc768fbSStefan Wahren if (!qca->rx_skb) { 116dfc768fbSStefan Wahren netdev_dbg(netdev, "recv: out of RX resources\n"); 117dfc768fbSStefan Wahren n_stats->rx_errors++; 118dfc768fbSStefan Wahren return i; 119dfc768fbSStefan Wahren } 120dfc768fbSStefan Wahren } 121dfc768fbSStefan Wahren } 122dfc768fbSStefan Wahren 123dfc768fbSStefan Wahren return i; 124dfc768fbSStefan Wahren } 125dfc768fbSStefan Wahren 126dfc768fbSStefan Wahren /* Write out any remaining transmit buffer. Scheduled when tty is writable */ 127dfc768fbSStefan Wahren static void qcauart_transmit(struct work_struct *work) 128dfc768fbSStefan Wahren { 129dfc768fbSStefan Wahren struct qcauart *qca = container_of(work, struct qcauart, tx_work); 130dfc768fbSStefan Wahren struct net_device_stats *n_stats = &qca->net_dev->stats; 131dfc768fbSStefan Wahren int written; 132dfc768fbSStefan Wahren 133dfc768fbSStefan Wahren spin_lock_bh(&qca->lock); 134dfc768fbSStefan Wahren 135dfc768fbSStefan Wahren /* First make sure we're connected. */ 136dfc768fbSStefan Wahren if (!netif_running(qca->net_dev)) { 137dfc768fbSStefan Wahren spin_unlock_bh(&qca->lock); 138dfc768fbSStefan Wahren return; 139dfc768fbSStefan Wahren } 140dfc768fbSStefan Wahren 141dfc768fbSStefan Wahren if (qca->tx_left <= 0) { 142dfc768fbSStefan Wahren /* Now serial buffer is almost free & we can start 143dfc768fbSStefan Wahren * transmission of another packet 144dfc768fbSStefan Wahren */ 145dfc768fbSStefan Wahren n_stats->tx_packets++; 146dfc768fbSStefan Wahren spin_unlock_bh(&qca->lock); 147dfc768fbSStefan Wahren netif_wake_queue(qca->net_dev); 148dfc768fbSStefan Wahren return; 149dfc768fbSStefan Wahren } 150dfc768fbSStefan Wahren 151dfc768fbSStefan Wahren written = serdev_device_write_buf(qca->serdev, qca->tx_head, 152dfc768fbSStefan Wahren qca->tx_left); 153dfc768fbSStefan Wahren if (written > 0) { 154dfc768fbSStefan Wahren qca->tx_left -= written; 155dfc768fbSStefan Wahren qca->tx_head += written; 156dfc768fbSStefan Wahren } 157dfc768fbSStefan Wahren spin_unlock_bh(&qca->lock); 158dfc768fbSStefan Wahren } 159dfc768fbSStefan Wahren 160dfc768fbSStefan Wahren /* Called by the driver when there's room for more data. 161dfc768fbSStefan Wahren * Schedule the transmit. 162dfc768fbSStefan Wahren */ 163dfc768fbSStefan Wahren static void qca_tty_wakeup(struct serdev_device *serdev) 164dfc768fbSStefan Wahren { 165dfc768fbSStefan Wahren struct qcauart *qca = serdev_device_get_drvdata(serdev); 166dfc768fbSStefan Wahren 167dfc768fbSStefan Wahren schedule_work(&qca->tx_work); 168dfc768fbSStefan Wahren } 169dfc768fbSStefan Wahren 170715d0871SRikard Falkeborn static const struct serdev_device_ops qca_serdev_ops = { 171dfc768fbSStefan Wahren .receive_buf = qca_tty_receive, 172dfc768fbSStefan Wahren .write_wakeup = qca_tty_wakeup, 173dfc768fbSStefan Wahren }; 174dfc768fbSStefan Wahren 175dfc768fbSStefan Wahren static int qcauart_netdev_open(struct net_device *dev) 176dfc768fbSStefan Wahren { 177dfc768fbSStefan Wahren struct qcauart *qca = netdev_priv(dev); 178dfc768fbSStefan Wahren 179dfc768fbSStefan Wahren netif_start_queue(qca->net_dev); 180dfc768fbSStefan Wahren 181dfc768fbSStefan Wahren return 0; 182dfc768fbSStefan Wahren } 183dfc768fbSStefan Wahren 184dfc768fbSStefan Wahren static int qcauart_netdev_close(struct net_device *dev) 185dfc768fbSStefan Wahren { 186dfc768fbSStefan Wahren struct qcauart *qca = netdev_priv(dev); 187dfc768fbSStefan Wahren 188dfc768fbSStefan Wahren netif_stop_queue(dev); 189dfc768fbSStefan Wahren flush_work(&qca->tx_work); 190dfc768fbSStefan Wahren 191dfc768fbSStefan Wahren spin_lock_bh(&qca->lock); 192dfc768fbSStefan Wahren qca->tx_left = 0; 193dfc768fbSStefan Wahren spin_unlock_bh(&qca->lock); 194dfc768fbSStefan Wahren 195dfc768fbSStefan Wahren return 0; 196dfc768fbSStefan Wahren } 197dfc768fbSStefan Wahren 198dfc768fbSStefan Wahren static netdev_tx_t 199dfc768fbSStefan Wahren qcauart_netdev_xmit(struct sk_buff *skb, struct net_device *dev) 200dfc768fbSStefan Wahren { 201dfc768fbSStefan Wahren struct net_device_stats *n_stats = &dev->stats; 202dfc768fbSStefan Wahren struct qcauart *qca = netdev_priv(dev); 203dfc768fbSStefan Wahren u8 pad_len = 0; 204dfc768fbSStefan Wahren int written; 205dfc768fbSStefan Wahren u8 *pos; 206dfc768fbSStefan Wahren 207dfc768fbSStefan Wahren spin_lock(&qca->lock); 208dfc768fbSStefan Wahren 209dfc768fbSStefan Wahren WARN_ON(qca->tx_left); 210dfc768fbSStefan Wahren 211dfc768fbSStefan Wahren if (!netif_running(dev)) { 212dfc768fbSStefan Wahren spin_unlock(&qca->lock); 213dfc768fbSStefan Wahren netdev_warn(qca->net_dev, "xmit: iface is down\n"); 214dfc768fbSStefan Wahren goto out; 215dfc768fbSStefan Wahren } 216dfc768fbSStefan Wahren 217dfc768fbSStefan Wahren pos = qca->tx_buffer; 218dfc768fbSStefan Wahren 219dfc768fbSStefan Wahren if (skb->len < QCAFRM_MIN_LEN) 220dfc768fbSStefan Wahren pad_len = QCAFRM_MIN_LEN - skb->len; 221dfc768fbSStefan Wahren 222dfc768fbSStefan Wahren pos += qcafrm_create_header(pos, skb->len + pad_len); 223dfc768fbSStefan Wahren 224dfc768fbSStefan Wahren memcpy(pos, skb->data, skb->len); 225dfc768fbSStefan Wahren pos += skb->len; 226dfc768fbSStefan Wahren 227dfc768fbSStefan Wahren if (pad_len) { 228dfc768fbSStefan Wahren memset(pos, 0, pad_len); 229dfc768fbSStefan Wahren pos += pad_len; 230dfc768fbSStefan Wahren } 231dfc768fbSStefan Wahren 232dfc768fbSStefan Wahren pos += qcafrm_create_footer(pos); 233dfc768fbSStefan Wahren 234dfc768fbSStefan Wahren netif_stop_queue(qca->net_dev); 235dfc768fbSStefan Wahren 236dfc768fbSStefan Wahren written = serdev_device_write_buf(qca->serdev, qca->tx_buffer, 237dfc768fbSStefan Wahren pos - qca->tx_buffer); 238dfc768fbSStefan Wahren if (written > 0) { 239dfc768fbSStefan Wahren qca->tx_left = (pos - qca->tx_buffer) - written; 240dfc768fbSStefan Wahren qca->tx_head = qca->tx_buffer + written; 241dfc768fbSStefan Wahren n_stats->tx_bytes += written; 242dfc768fbSStefan Wahren } 243dfc768fbSStefan Wahren spin_unlock(&qca->lock); 244dfc768fbSStefan Wahren 245dfc768fbSStefan Wahren netif_trans_update(dev); 246dfc768fbSStefan Wahren out: 247dfc768fbSStefan Wahren dev_kfree_skb_any(skb); 248dfc768fbSStefan Wahren return NETDEV_TX_OK; 249dfc768fbSStefan Wahren } 250dfc768fbSStefan Wahren 2510290bd29SMichael S. Tsirkin static void qcauart_netdev_tx_timeout(struct net_device *dev, unsigned int txqueue) 252dfc768fbSStefan Wahren { 253dfc768fbSStefan Wahren struct qcauart *qca = netdev_priv(dev); 254dfc768fbSStefan Wahren 255dfc768fbSStefan Wahren netdev_info(qca->net_dev, "Transmit timeout at %ld, latency %ld\n", 256dfc768fbSStefan Wahren jiffies, dev_trans_start(dev)); 257dfc768fbSStefan Wahren dev->stats.tx_errors++; 258dfc768fbSStefan Wahren dev->stats.tx_dropped++; 259dfc768fbSStefan Wahren } 260dfc768fbSStefan Wahren 261dfc768fbSStefan Wahren static int qcauart_netdev_init(struct net_device *dev) 262dfc768fbSStefan Wahren { 263dfc768fbSStefan Wahren struct qcauart *qca = netdev_priv(dev); 264dfc768fbSStefan Wahren size_t len; 265dfc768fbSStefan Wahren 266dfc768fbSStefan Wahren /* Finish setting up the device info. */ 267dfc768fbSStefan Wahren dev->mtu = QCAFRM_MAX_MTU; 268dfc768fbSStefan Wahren dev->type = ARPHRD_ETHER; 269dfc768fbSStefan Wahren 270dfc768fbSStefan Wahren len = QCAFRM_HEADER_LEN + QCAFRM_MAX_LEN + QCAFRM_FOOTER_LEN; 271dfc768fbSStefan Wahren qca->tx_buffer = devm_kmalloc(&qca->serdev->dev, len, GFP_KERNEL); 272dfc768fbSStefan Wahren if (!qca->tx_buffer) 273dfc768fbSStefan Wahren return -ENOMEM; 274dfc768fbSStefan Wahren 275dfc768fbSStefan Wahren qca->rx_skb = netdev_alloc_skb_ip_align(qca->net_dev, 276dfc768fbSStefan Wahren qca->net_dev->mtu + 277dfc768fbSStefan Wahren VLAN_ETH_HLEN); 278dfc768fbSStefan Wahren if (!qca->rx_skb) 279dfc768fbSStefan Wahren return -ENOBUFS; 280dfc768fbSStefan Wahren 281dfc768fbSStefan Wahren return 0; 282dfc768fbSStefan Wahren } 283dfc768fbSStefan Wahren 284dfc768fbSStefan Wahren static void qcauart_netdev_uninit(struct net_device *dev) 285dfc768fbSStefan Wahren { 286dfc768fbSStefan Wahren struct qcauart *qca = netdev_priv(dev); 287dfc768fbSStefan Wahren 288dfc768fbSStefan Wahren dev_kfree_skb(qca->rx_skb); 289dfc768fbSStefan Wahren } 290dfc768fbSStefan Wahren 291dfc768fbSStefan Wahren static const struct net_device_ops qcauart_netdev_ops = { 292dfc768fbSStefan Wahren .ndo_init = qcauart_netdev_init, 293dfc768fbSStefan Wahren .ndo_uninit = qcauart_netdev_uninit, 294dfc768fbSStefan Wahren .ndo_open = qcauart_netdev_open, 295dfc768fbSStefan Wahren .ndo_stop = qcauart_netdev_close, 296dfc768fbSStefan Wahren .ndo_start_xmit = qcauart_netdev_xmit, 297dfc768fbSStefan Wahren .ndo_set_mac_address = eth_mac_addr, 298dfc768fbSStefan Wahren .ndo_tx_timeout = qcauart_netdev_tx_timeout, 299dfc768fbSStefan Wahren .ndo_validate_addr = eth_validate_addr, 300dfc768fbSStefan Wahren }; 301dfc768fbSStefan Wahren 302dfc768fbSStefan Wahren static void qcauart_netdev_setup(struct net_device *dev) 303dfc768fbSStefan Wahren { 304dfc768fbSStefan Wahren dev->netdev_ops = &qcauart_netdev_ops; 305dfc768fbSStefan Wahren dev->watchdog_timeo = QCAUART_TX_TIMEOUT; 306dfc768fbSStefan Wahren dev->priv_flags &= ~IFF_TX_SKB_SHARING; 307dfc768fbSStefan Wahren dev->tx_queue_len = 100; 308dfc768fbSStefan Wahren 309dfc768fbSStefan Wahren /* MTU range: 46 - 1500 */ 310dfc768fbSStefan Wahren dev->min_mtu = QCAFRM_MIN_MTU; 311dfc768fbSStefan Wahren dev->max_mtu = QCAFRM_MAX_MTU; 312dfc768fbSStefan Wahren } 313dfc768fbSStefan Wahren 314dfc768fbSStefan Wahren static const struct of_device_id qca_uart_of_match[] = { 315dfc768fbSStefan Wahren { 316dfc768fbSStefan Wahren .compatible = "qca,qca7000", 317dfc768fbSStefan Wahren }, 318dfc768fbSStefan Wahren {} 319dfc768fbSStefan Wahren }; 320dfc768fbSStefan Wahren MODULE_DEVICE_TABLE(of, qca_uart_of_match); 321dfc768fbSStefan Wahren 322dfc768fbSStefan Wahren static int qca_uart_probe(struct serdev_device *serdev) 323dfc768fbSStefan Wahren { 324dfc768fbSStefan Wahren struct net_device *qcauart_dev = alloc_etherdev(sizeof(struct qcauart)); 325dfc768fbSStefan Wahren struct qcauart *qca; 326dfc768fbSStefan Wahren const char *mac; 327dfc768fbSStefan Wahren u32 speed = 115200; 328dfc768fbSStefan Wahren int ret; 329dfc768fbSStefan Wahren 330dfc768fbSStefan Wahren if (!qcauart_dev) 331dfc768fbSStefan Wahren return -ENOMEM; 332dfc768fbSStefan Wahren 333dfc768fbSStefan Wahren qcauart_netdev_setup(qcauart_dev); 334dfc768fbSStefan Wahren SET_NETDEV_DEV(qcauart_dev, &serdev->dev); 335dfc768fbSStefan Wahren 336dfc768fbSStefan Wahren qca = netdev_priv(qcauart_dev); 337dfc768fbSStefan Wahren if (!qca) { 338dfc768fbSStefan Wahren pr_err("qca_uart: Fail to retrieve private structure\n"); 339dfc768fbSStefan Wahren ret = -ENOMEM; 340dfc768fbSStefan Wahren goto free; 341dfc768fbSStefan Wahren } 342dfc768fbSStefan Wahren qca->net_dev = qcauart_dev; 343dfc768fbSStefan Wahren qca->serdev = serdev; 344dfc768fbSStefan Wahren qcafrm_fsm_init_uart(&qca->frm_handle); 345dfc768fbSStefan Wahren 346dfc768fbSStefan Wahren spin_lock_init(&qca->lock); 347dfc768fbSStefan Wahren INIT_WORK(&qca->tx_work, qcauart_transmit); 348dfc768fbSStefan Wahren 349dfc768fbSStefan Wahren of_property_read_u32(serdev->dev.of_node, "current-speed", &speed); 350dfc768fbSStefan Wahren 351dfc768fbSStefan Wahren mac = of_get_mac_address(serdev->dev.of_node); 352dfc768fbSStefan Wahren 353a51645f7SPetr Štetiar if (!IS_ERR(mac)) 354dfc768fbSStefan Wahren ether_addr_copy(qca->net_dev->dev_addr, mac); 355dfc768fbSStefan Wahren 356dfc768fbSStefan Wahren if (!is_valid_ether_addr(qca->net_dev->dev_addr)) { 357dfc768fbSStefan Wahren eth_hw_addr_random(qca->net_dev); 358dfc768fbSStefan Wahren dev_info(&serdev->dev, "Using random MAC address: %pM\n", 359dfc768fbSStefan Wahren qca->net_dev->dev_addr); 360dfc768fbSStefan Wahren } 361dfc768fbSStefan Wahren 362dfc768fbSStefan Wahren netif_carrier_on(qca->net_dev); 363dfc768fbSStefan Wahren serdev_device_set_drvdata(serdev, qca); 364dfc768fbSStefan Wahren serdev_device_set_client_ops(serdev, &qca_serdev_ops); 365dfc768fbSStefan Wahren 366dfc768fbSStefan Wahren ret = serdev_device_open(serdev); 367dfc768fbSStefan Wahren if (ret) { 368dfc768fbSStefan Wahren dev_err(&serdev->dev, "Unable to open device %s\n", 369dfc768fbSStefan Wahren qcauart_dev->name); 370dfc768fbSStefan Wahren goto free; 371dfc768fbSStefan Wahren } 372dfc768fbSStefan Wahren 373dfc768fbSStefan Wahren speed = serdev_device_set_baudrate(serdev, speed); 374dfc768fbSStefan Wahren dev_info(&serdev->dev, "Using baudrate: %u\n", speed); 375dfc768fbSStefan Wahren 376dfc768fbSStefan Wahren serdev_device_set_flow_control(serdev, false); 377dfc768fbSStefan Wahren 378dfc768fbSStefan Wahren ret = register_netdev(qcauart_dev); 379dfc768fbSStefan Wahren if (ret) { 380dfc768fbSStefan Wahren dev_err(&serdev->dev, "Unable to register net device %s\n", 381dfc768fbSStefan Wahren qcauart_dev->name); 382dfc768fbSStefan Wahren serdev_device_close(serdev); 383dfc768fbSStefan Wahren cancel_work_sync(&qca->tx_work); 384dfc768fbSStefan Wahren goto free; 385dfc768fbSStefan Wahren } 386dfc768fbSStefan Wahren 387dfc768fbSStefan Wahren return 0; 388dfc768fbSStefan Wahren 389dfc768fbSStefan Wahren free: 390dfc768fbSStefan Wahren free_netdev(qcauart_dev); 391dfc768fbSStefan Wahren return ret; 392dfc768fbSStefan Wahren } 393dfc768fbSStefan Wahren 394dfc768fbSStefan Wahren static void qca_uart_remove(struct serdev_device *serdev) 395dfc768fbSStefan Wahren { 396dfc768fbSStefan Wahren struct qcauart *qca = serdev_device_get_drvdata(serdev); 397dfc768fbSStefan Wahren 398dfc768fbSStefan Wahren unregister_netdev(qca->net_dev); 399dfc768fbSStefan Wahren 400dfc768fbSStefan Wahren /* Flush any pending characters in the driver. */ 401dfc768fbSStefan Wahren serdev_device_close(serdev); 402dfc768fbSStefan Wahren cancel_work_sync(&qca->tx_work); 403dfc768fbSStefan Wahren 404dfc768fbSStefan Wahren free_netdev(qca->net_dev); 405dfc768fbSStefan Wahren } 406dfc768fbSStefan Wahren 407dfc768fbSStefan Wahren static struct serdev_device_driver qca_uart_driver = { 408dfc768fbSStefan Wahren .probe = qca_uart_probe, 409dfc768fbSStefan Wahren .remove = qca_uart_remove, 410dfc768fbSStefan Wahren .driver = { 411dfc768fbSStefan Wahren .name = QCAUART_DRV_NAME, 412dfc768fbSStefan Wahren .of_match_table = of_match_ptr(qca_uart_of_match), 413dfc768fbSStefan Wahren }, 414dfc768fbSStefan Wahren }; 415dfc768fbSStefan Wahren 416dfc768fbSStefan Wahren module_serdev_device_driver(qca_uart_driver); 417dfc768fbSStefan Wahren 418dfc768fbSStefan Wahren MODULE_DESCRIPTION("Qualcomm Atheros QCA7000 UART Driver"); 419dfc768fbSStefan Wahren MODULE_AUTHOR("Qualcomm Atheros Communications"); 420dfc768fbSStefan Wahren MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>"); 421dfc768fbSStefan Wahren MODULE_LICENSE("Dual BSD/GPL"); 422dfc768fbSStefan Wahren MODULE_VERSION(QCAUART_DRV_VERSION); 423