18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2013 48c2ecf20Sopenharmony_ci * Authors: Vicram Arv 58c2ecf20Sopenharmony_ci * Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> 68c2ecf20Sopenharmony_ci * Sjur Brendeland 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 108c2ecf20Sopenharmony_ci#include <linux/virtio.h> 118c2ecf20Sopenharmony_ci#include <linux/vringh.h> 128c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 138c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 148c2ecf20Sopenharmony_ci#include <linux/genalloc.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 178c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 188c2ecf20Sopenharmony_ci#include <linux/virtio_ids.h> 198c2ecf20Sopenharmony_ci#include <linux/virtio_caif.h> 208c2ecf20Sopenharmony_ci#include <linux/virtio_ring.h> 218c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 228c2ecf20Sopenharmony_ci#include <net/caif/caif_dev.h> 238c2ecf20Sopenharmony_ci#include <linux/virtio_config.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vicram Arv"); 278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sjur Brendeland"); 288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Virtio CAIF Driver"); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* NAPI schedule quota */ 318c2ecf20Sopenharmony_ci#define CFV_DEFAULT_QUOTA 32 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* Defaults used if virtio config space is unavailable */ 348c2ecf20Sopenharmony_ci#define CFV_DEF_MTU_SIZE 4096 358c2ecf20Sopenharmony_ci#define CFV_DEF_HEADROOM 32 368c2ecf20Sopenharmony_ci#define CFV_DEF_TAILROOM 32 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* Required IP header alignment */ 398c2ecf20Sopenharmony_ci#define IP_HDR_ALIGN 4 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* struct cfv_napi_contxt - NAPI context info 428c2ecf20Sopenharmony_ci * @riov: IOV holding data read from the ring. Note that riov may 438c2ecf20Sopenharmony_ci * still hold data when cfv_rx_poll() returns. 448c2ecf20Sopenharmony_ci * @head: Last descriptor ID we received from vringh_getdesc_kern. 458c2ecf20Sopenharmony_ci * We use this to put descriptor back on the used ring. USHRT_MAX is 468c2ecf20Sopenharmony_ci * used to indicate invalid head-id. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_cistruct cfv_napi_context { 498c2ecf20Sopenharmony_ci struct vringh_kiov riov; 508c2ecf20Sopenharmony_ci unsigned short head; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* struct cfv_stats - statistics for debugfs 548c2ecf20Sopenharmony_ci * @rx_napi_complete: Number of NAPI completions (RX) 558c2ecf20Sopenharmony_ci * @rx_napi_resched: Number of calls where the full quota was used (RX) 568c2ecf20Sopenharmony_ci * @rx_nomem: Number of SKB alloc failures (RX) 578c2ecf20Sopenharmony_ci * @rx_kicks: Number of RX kicks 588c2ecf20Sopenharmony_ci * @tx_full_ring: Number times TX ring was full 598c2ecf20Sopenharmony_ci * @tx_no_mem: Number of times TX went out of memory 608c2ecf20Sopenharmony_ci * @tx_flow_on: Number of flow on (TX) 618c2ecf20Sopenharmony_ci * @tx_kicks: Number of TX kicks 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_cistruct cfv_stats { 648c2ecf20Sopenharmony_ci u32 rx_napi_complete; 658c2ecf20Sopenharmony_ci u32 rx_napi_resched; 668c2ecf20Sopenharmony_ci u32 rx_nomem; 678c2ecf20Sopenharmony_ci u32 rx_kicks; 688c2ecf20Sopenharmony_ci u32 tx_full_ring; 698c2ecf20Sopenharmony_ci u32 tx_no_mem; 708c2ecf20Sopenharmony_ci u32 tx_flow_on; 718c2ecf20Sopenharmony_ci u32 tx_kicks; 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* struct cfv_info - Caif Virtio control structure 758c2ecf20Sopenharmony_ci * @cfdev: caif common header 768c2ecf20Sopenharmony_ci * @vdev: Associated virtio device 778c2ecf20Sopenharmony_ci * @vr_rx: rx/downlink host vring 788c2ecf20Sopenharmony_ci * @vq_tx: tx/uplink virtqueue 798c2ecf20Sopenharmony_ci * @ndev: CAIF link layer device 808c2ecf20Sopenharmony_ci * @watermark_tx: indicates number of free descriptors we need 818c2ecf20Sopenharmony_ci * to reopen the tx-queues after overload. 828c2ecf20Sopenharmony_ci * @tx_lock: protects vq_tx from concurrent use 838c2ecf20Sopenharmony_ci * @tx_release_tasklet: Tasklet for freeing consumed TX buffers 848c2ecf20Sopenharmony_ci * @napi: Napi context used in cfv_rx_poll() 858c2ecf20Sopenharmony_ci * @ctx: Context data used in cfv_rx_poll() 868c2ecf20Sopenharmony_ci * @tx_hr: transmit headroom 878c2ecf20Sopenharmony_ci * @rx_hr: receive headroom 888c2ecf20Sopenharmony_ci * @tx_tr: transmit tail room 898c2ecf20Sopenharmony_ci * @rx_tr: receive tail room 908c2ecf20Sopenharmony_ci * @mtu: transmit max size 918c2ecf20Sopenharmony_ci * @mru: receive max size 928c2ecf20Sopenharmony_ci * @allocsz: size of dma memory reserved for TX buffers 938c2ecf20Sopenharmony_ci * @alloc_addr: virtual address to dma memory for TX buffers 948c2ecf20Sopenharmony_ci * @alloc_dma: dma address to dma memory for TX buffers 958c2ecf20Sopenharmony_ci * @genpool: Gen Pool used for allocating TX buffers 968c2ecf20Sopenharmony_ci * @reserved_mem: Pointer to memory reserve allocated from genpool 978c2ecf20Sopenharmony_ci * @reserved_size: Size of memory reserve allocated from genpool 988c2ecf20Sopenharmony_ci * @stats: Statistics exposed in sysfs 998c2ecf20Sopenharmony_ci * @debugfs: Debugfs dentry for statistic counters 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_cistruct cfv_info { 1028c2ecf20Sopenharmony_ci struct caif_dev_common cfdev; 1038c2ecf20Sopenharmony_ci struct virtio_device *vdev; 1048c2ecf20Sopenharmony_ci struct vringh *vr_rx; 1058c2ecf20Sopenharmony_ci struct virtqueue *vq_tx; 1068c2ecf20Sopenharmony_ci struct net_device *ndev; 1078c2ecf20Sopenharmony_ci unsigned int watermark_tx; 1088c2ecf20Sopenharmony_ci /* Protect access to vq_tx */ 1098c2ecf20Sopenharmony_ci spinlock_t tx_lock; 1108c2ecf20Sopenharmony_ci struct tasklet_struct tx_release_tasklet; 1118c2ecf20Sopenharmony_ci struct napi_struct napi; 1128c2ecf20Sopenharmony_ci struct cfv_napi_context ctx; 1138c2ecf20Sopenharmony_ci u16 tx_hr; 1148c2ecf20Sopenharmony_ci u16 rx_hr; 1158c2ecf20Sopenharmony_ci u16 tx_tr; 1168c2ecf20Sopenharmony_ci u16 rx_tr; 1178c2ecf20Sopenharmony_ci u32 mtu; 1188c2ecf20Sopenharmony_ci u32 mru; 1198c2ecf20Sopenharmony_ci size_t allocsz; 1208c2ecf20Sopenharmony_ci void *alloc_addr; 1218c2ecf20Sopenharmony_ci dma_addr_t alloc_dma; 1228c2ecf20Sopenharmony_ci struct gen_pool *genpool; 1238c2ecf20Sopenharmony_ci unsigned long reserved_mem; 1248c2ecf20Sopenharmony_ci size_t reserved_size; 1258c2ecf20Sopenharmony_ci struct cfv_stats stats; 1268c2ecf20Sopenharmony_ci struct dentry *debugfs; 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* struct buf_info - maintains transmit buffer data handle 1308c2ecf20Sopenharmony_ci * @size: size of transmit buffer 1318c2ecf20Sopenharmony_ci * @dma_handle: handle to allocated dma device memory area 1328c2ecf20Sopenharmony_ci * @vaddr: virtual address mapping to allocated memory area 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_cistruct buf_info { 1358c2ecf20Sopenharmony_ci size_t size; 1368c2ecf20Sopenharmony_ci u8 *vaddr; 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/* Called from virtio device, in IRQ context */ 1408c2ecf20Sopenharmony_cistatic void cfv_release_cb(struct virtqueue *vq_tx) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct cfv_info *cfv = vq_tx->vdev->priv; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci ++cfv->stats.tx_kicks; 1458c2ecf20Sopenharmony_ci tasklet_schedule(&cfv->tx_release_tasklet); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void free_buf_info(struct cfv_info *cfv, struct buf_info *buf_info) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci if (!buf_info) 1518c2ecf20Sopenharmony_ci return; 1528c2ecf20Sopenharmony_ci gen_pool_free(cfv->genpool, (unsigned long) buf_info->vaddr, 1538c2ecf20Sopenharmony_ci buf_info->size); 1548c2ecf20Sopenharmony_ci kfree(buf_info); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/* This is invoked whenever the remote processor completed processing 1588c2ecf20Sopenharmony_ci * a TX msg we just sent, and the buffer is put back to the used ring. 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_cistatic void cfv_release_used_buf(struct virtqueue *vq_tx) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct cfv_info *cfv = vq_tx->vdev->priv; 1638c2ecf20Sopenharmony_ci unsigned long flags; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci BUG_ON(vq_tx != cfv->vq_tx); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci for (;;) { 1688c2ecf20Sopenharmony_ci unsigned int len; 1698c2ecf20Sopenharmony_ci struct buf_info *buf_info; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Get used buffer from used ring to recycle used descriptors */ 1728c2ecf20Sopenharmony_ci spin_lock_irqsave(&cfv->tx_lock, flags); 1738c2ecf20Sopenharmony_ci buf_info = virtqueue_get_buf(vq_tx, &len); 1748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cfv->tx_lock, flags); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* Stop looping if there are no more buffers to free */ 1778c2ecf20Sopenharmony_ci if (!buf_info) 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci free_buf_info(cfv, buf_info); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* watermark_tx indicates if we previously stopped the tx 1838c2ecf20Sopenharmony_ci * queues. If we have enough free stots in the virtio ring, 1848c2ecf20Sopenharmony_ci * re-establish memory reserved and open up tx queues. 1858c2ecf20Sopenharmony_ci */ 1868c2ecf20Sopenharmony_ci if (cfv->vq_tx->num_free <= cfv->watermark_tx) 1878c2ecf20Sopenharmony_ci continue; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* Re-establish memory reserve */ 1908c2ecf20Sopenharmony_ci if (cfv->reserved_mem == 0 && cfv->genpool) 1918c2ecf20Sopenharmony_ci cfv->reserved_mem = 1928c2ecf20Sopenharmony_ci gen_pool_alloc(cfv->genpool, 1938c2ecf20Sopenharmony_ci cfv->reserved_size); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* Open up the tx queues */ 1968c2ecf20Sopenharmony_ci if (cfv->reserved_mem) { 1978c2ecf20Sopenharmony_ci cfv->watermark_tx = 1988c2ecf20Sopenharmony_ci virtqueue_get_vring_size(cfv->vq_tx); 1998c2ecf20Sopenharmony_ci netif_tx_wake_all_queues(cfv->ndev); 2008c2ecf20Sopenharmony_ci /* Buffers are recycled in cfv_netdev_tx, so 2018c2ecf20Sopenharmony_ci * disable notifications when queues are opened. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci virtqueue_disable_cb(cfv->vq_tx); 2048c2ecf20Sopenharmony_ci ++cfv->stats.tx_flow_on; 2058c2ecf20Sopenharmony_ci } else { 2068c2ecf20Sopenharmony_ci /* if no memory reserve, wait for more free slots */ 2078c2ecf20Sopenharmony_ci WARN_ON(cfv->watermark_tx > 2088c2ecf20Sopenharmony_ci virtqueue_get_vring_size(cfv->vq_tx)); 2098c2ecf20Sopenharmony_ci cfv->watermark_tx += 2108c2ecf20Sopenharmony_ci virtqueue_get_vring_size(cfv->vq_tx) / 4; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci/* Allocate a SKB and copy packet data to it */ 2168c2ecf20Sopenharmony_cistatic struct sk_buff *cfv_alloc_and_copy_skb(int *err, 2178c2ecf20Sopenharmony_ci struct cfv_info *cfv, 2188c2ecf20Sopenharmony_ci u8 *frm, u32 frm_len) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct sk_buff *skb; 2218c2ecf20Sopenharmony_ci u32 cfpkt_len, pad_len; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci *err = 0; 2248c2ecf20Sopenharmony_ci /* Verify that packet size with down-link header and mtu size */ 2258c2ecf20Sopenharmony_ci if (frm_len > cfv->mru || frm_len <= cfv->rx_hr + cfv->rx_tr) { 2268c2ecf20Sopenharmony_ci netdev_err(cfv->ndev, 2278c2ecf20Sopenharmony_ci "Invalid frmlen:%u mtu:%u hr:%d tr:%d\n", 2288c2ecf20Sopenharmony_ci frm_len, cfv->mru, cfv->rx_hr, 2298c2ecf20Sopenharmony_ci cfv->rx_tr); 2308c2ecf20Sopenharmony_ci *err = -EPROTO; 2318c2ecf20Sopenharmony_ci return NULL; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci cfpkt_len = frm_len - (cfv->rx_hr + cfv->rx_tr); 2358c2ecf20Sopenharmony_ci pad_len = (unsigned long)(frm + cfv->rx_hr) & (IP_HDR_ALIGN - 1); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(cfv->ndev, frm_len + pad_len); 2388c2ecf20Sopenharmony_ci if (!skb) { 2398c2ecf20Sopenharmony_ci *err = -ENOMEM; 2408c2ecf20Sopenharmony_ci return NULL; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci skb_reserve(skb, cfv->rx_hr + pad_len); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci skb_put_data(skb, frm + cfv->rx_hr, cfpkt_len); 2468c2ecf20Sopenharmony_ci return skb; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/* Get packets from the host vring */ 2508c2ecf20Sopenharmony_cistatic int cfv_rx_poll(struct napi_struct *napi, int quota) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct cfv_info *cfv = container_of(napi, struct cfv_info, napi); 2538c2ecf20Sopenharmony_ci int rxcnt = 0; 2548c2ecf20Sopenharmony_ci int err = 0; 2558c2ecf20Sopenharmony_ci void *buf; 2568c2ecf20Sopenharmony_ci struct sk_buff *skb; 2578c2ecf20Sopenharmony_ci struct vringh_kiov *riov = &cfv->ctx.riov; 2588c2ecf20Sopenharmony_ci unsigned int skb_len; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci do { 2618c2ecf20Sopenharmony_ci skb = NULL; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* Put the previous iovec back on the used ring and 2648c2ecf20Sopenharmony_ci * fetch a new iovec if we have processed all elements. 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_ci if (riov->i == riov->used) { 2678c2ecf20Sopenharmony_ci if (cfv->ctx.head != USHRT_MAX) { 2688c2ecf20Sopenharmony_ci vringh_complete_kern(cfv->vr_rx, 2698c2ecf20Sopenharmony_ci cfv->ctx.head, 2708c2ecf20Sopenharmony_ci 0); 2718c2ecf20Sopenharmony_ci cfv->ctx.head = USHRT_MAX; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci err = vringh_getdesc_kern( 2758c2ecf20Sopenharmony_ci cfv->vr_rx, 2768c2ecf20Sopenharmony_ci riov, 2778c2ecf20Sopenharmony_ci NULL, 2788c2ecf20Sopenharmony_ci &cfv->ctx.head, 2798c2ecf20Sopenharmony_ci GFP_ATOMIC); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (err <= 0) 2828c2ecf20Sopenharmony_ci goto exit; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci buf = phys_to_virt((unsigned long) riov->iov[riov->i].iov_base); 2868c2ecf20Sopenharmony_ci /* TODO: Add check on valid buffer address */ 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci skb = cfv_alloc_and_copy_skb(&err, cfv, buf, 2898c2ecf20Sopenharmony_ci riov->iov[riov->i].iov_len); 2908c2ecf20Sopenharmony_ci if (unlikely(err)) 2918c2ecf20Sopenharmony_ci goto exit; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* Push received packet up the stack. */ 2948c2ecf20Sopenharmony_ci skb_len = skb->len; 2958c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_CAIF); 2968c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 2978c2ecf20Sopenharmony_ci skb->dev = cfv->ndev; 2988c2ecf20Sopenharmony_ci err = netif_receive_skb(skb); 2998c2ecf20Sopenharmony_ci if (unlikely(err)) { 3008c2ecf20Sopenharmony_ci ++cfv->ndev->stats.rx_dropped; 3018c2ecf20Sopenharmony_ci } else { 3028c2ecf20Sopenharmony_ci ++cfv->ndev->stats.rx_packets; 3038c2ecf20Sopenharmony_ci cfv->ndev->stats.rx_bytes += skb_len; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci ++riov->i; 3078c2ecf20Sopenharmony_ci ++rxcnt; 3088c2ecf20Sopenharmony_ci } while (rxcnt < quota); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci ++cfv->stats.rx_napi_resched; 3118c2ecf20Sopenharmony_ci goto out; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ciexit: 3148c2ecf20Sopenharmony_ci switch (err) { 3158c2ecf20Sopenharmony_ci case 0: 3168c2ecf20Sopenharmony_ci ++cfv->stats.rx_napi_complete; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* Really out of patckets? (stolen from virtio_net)*/ 3198c2ecf20Sopenharmony_ci napi_complete(napi); 3208c2ecf20Sopenharmony_ci if (unlikely(!vringh_notify_enable_kern(cfv->vr_rx)) && 3218c2ecf20Sopenharmony_ci napi_schedule_prep(napi)) { 3228c2ecf20Sopenharmony_ci vringh_notify_disable_kern(cfv->vr_rx); 3238c2ecf20Sopenharmony_ci __napi_schedule(napi); 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci break; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci case -ENOMEM: 3288c2ecf20Sopenharmony_ci ++cfv->stats.rx_nomem; 3298c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 3308c2ecf20Sopenharmony_ci /* Stop NAPI poll on OOM, we hope to be polled later */ 3318c2ecf20Sopenharmony_ci napi_complete(napi); 3328c2ecf20Sopenharmony_ci vringh_notify_enable_kern(cfv->vr_rx); 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci default: 3368c2ecf20Sopenharmony_ci /* We're doomed, any modem fault is fatal */ 3378c2ecf20Sopenharmony_ci netdev_warn(cfv->ndev, "Bad ring, disable device\n"); 3388c2ecf20Sopenharmony_ci cfv->ndev->stats.rx_dropped = riov->used - riov->i; 3398c2ecf20Sopenharmony_ci napi_complete(napi); 3408c2ecf20Sopenharmony_ci vringh_notify_disable_kern(cfv->vr_rx); 3418c2ecf20Sopenharmony_ci netif_carrier_off(cfv->ndev); 3428c2ecf20Sopenharmony_ci break; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ciout: 3458c2ecf20Sopenharmony_ci if (rxcnt && vringh_need_notify_kern(cfv->vr_rx) > 0) 3468c2ecf20Sopenharmony_ci vringh_notify(cfv->vr_rx); 3478c2ecf20Sopenharmony_ci return rxcnt; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic void cfv_recv(struct virtio_device *vdev, struct vringh *vr_rx) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct cfv_info *cfv = vdev->priv; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci ++cfv->stats.rx_kicks; 3558c2ecf20Sopenharmony_ci vringh_notify_disable_kern(cfv->vr_rx); 3568c2ecf20Sopenharmony_ci napi_schedule(&cfv->napi); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic void cfv_destroy_genpool(struct cfv_info *cfv) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci if (cfv->alloc_addr) 3628c2ecf20Sopenharmony_ci dma_free_coherent(cfv->vdev->dev.parent->parent, 3638c2ecf20Sopenharmony_ci cfv->allocsz, cfv->alloc_addr, 3648c2ecf20Sopenharmony_ci cfv->alloc_dma); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (!cfv->genpool) 3678c2ecf20Sopenharmony_ci return; 3688c2ecf20Sopenharmony_ci gen_pool_free(cfv->genpool, cfv->reserved_mem, 3698c2ecf20Sopenharmony_ci cfv->reserved_size); 3708c2ecf20Sopenharmony_ci gen_pool_destroy(cfv->genpool); 3718c2ecf20Sopenharmony_ci cfv->genpool = NULL; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int cfv_create_genpool(struct cfv_info *cfv) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci int err; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* dma_alloc can only allocate whole pages, and we need a more 3798c2ecf20Sopenharmony_ci * fine graned allocation so we use genpool. We ask for space needed 3808c2ecf20Sopenharmony_ci * by IP and a full ring. If the dma allcoation fails we retry with a 3818c2ecf20Sopenharmony_ci * smaller allocation size. 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ci err = -ENOMEM; 3848c2ecf20Sopenharmony_ci cfv->allocsz = (virtqueue_get_vring_size(cfv->vq_tx) * 3858c2ecf20Sopenharmony_ci (ETH_DATA_LEN + cfv->tx_hr + cfv->tx_tr) * 11)/10; 3868c2ecf20Sopenharmony_ci if (cfv->allocsz <= (num_possible_cpus() + 1) * cfv->ndev->mtu) 3878c2ecf20Sopenharmony_ci return -EINVAL; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci for (;;) { 3908c2ecf20Sopenharmony_ci if (cfv->allocsz <= num_possible_cpus() * cfv->ndev->mtu) { 3918c2ecf20Sopenharmony_ci netdev_info(cfv->ndev, "Not enough device memory\n"); 3928c2ecf20Sopenharmony_ci return -ENOMEM; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci cfv->alloc_addr = dma_alloc_coherent( 3968c2ecf20Sopenharmony_ci cfv->vdev->dev.parent->parent, 3978c2ecf20Sopenharmony_ci cfv->allocsz, &cfv->alloc_dma, 3988c2ecf20Sopenharmony_ci GFP_ATOMIC); 3998c2ecf20Sopenharmony_ci if (cfv->alloc_addr) 4008c2ecf20Sopenharmony_ci break; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci cfv->allocsz = (cfv->allocsz * 3) >> 2; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci netdev_dbg(cfv->ndev, "Allocated %zd bytes from dma-memory\n", 4068c2ecf20Sopenharmony_ci cfv->allocsz); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* Allocate on 128 bytes boundaries (1 << 7)*/ 4098c2ecf20Sopenharmony_ci cfv->genpool = gen_pool_create(7, -1); 4108c2ecf20Sopenharmony_ci if (!cfv->genpool) 4118c2ecf20Sopenharmony_ci goto err; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci err = gen_pool_add_virt(cfv->genpool, (unsigned long)cfv->alloc_addr, 4148c2ecf20Sopenharmony_ci (phys_addr_t)virt_to_phys(cfv->alloc_addr), 4158c2ecf20Sopenharmony_ci cfv->allocsz, -1); 4168c2ecf20Sopenharmony_ci if (err) 4178c2ecf20Sopenharmony_ci goto err; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* Reserve some memory for low memory situations. If we hit the roof 4208c2ecf20Sopenharmony_ci * in the memory pool, we stop TX flow and release the reserve. 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_ci cfv->reserved_size = num_possible_cpus() * cfv->ndev->mtu; 4238c2ecf20Sopenharmony_ci cfv->reserved_mem = gen_pool_alloc(cfv->genpool, 4248c2ecf20Sopenharmony_ci cfv->reserved_size); 4258c2ecf20Sopenharmony_ci if (!cfv->reserved_mem) { 4268c2ecf20Sopenharmony_ci err = -ENOMEM; 4278c2ecf20Sopenharmony_ci goto err; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci cfv->watermark_tx = virtqueue_get_vring_size(cfv->vq_tx); 4318c2ecf20Sopenharmony_ci return 0; 4328c2ecf20Sopenharmony_cierr: 4338c2ecf20Sopenharmony_ci cfv_destroy_genpool(cfv); 4348c2ecf20Sopenharmony_ci return err; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci/* Enable the CAIF interface and allocate the memory-pool */ 4388c2ecf20Sopenharmony_cistatic int cfv_netdev_open(struct net_device *netdev) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci struct cfv_info *cfv = netdev_priv(netdev); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (cfv_create_genpool(cfv)) 4438c2ecf20Sopenharmony_ci return -ENOMEM; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci netif_carrier_on(netdev); 4468c2ecf20Sopenharmony_ci napi_enable(&cfv->napi); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Schedule NAPI to read any pending packets */ 4498c2ecf20Sopenharmony_ci napi_schedule(&cfv->napi); 4508c2ecf20Sopenharmony_ci return 0; 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci/* Disable the CAIF interface and free the memory-pool */ 4548c2ecf20Sopenharmony_cistatic int cfv_netdev_close(struct net_device *netdev) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct cfv_info *cfv = netdev_priv(netdev); 4578c2ecf20Sopenharmony_ci unsigned long flags; 4588c2ecf20Sopenharmony_ci struct buf_info *buf_info; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* Disable interrupts, queues and NAPI polling */ 4618c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 4628c2ecf20Sopenharmony_ci virtqueue_disable_cb(cfv->vq_tx); 4638c2ecf20Sopenharmony_ci vringh_notify_disable_kern(cfv->vr_rx); 4648c2ecf20Sopenharmony_ci napi_disable(&cfv->napi); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* Release any TX buffers on both used and avilable rings */ 4678c2ecf20Sopenharmony_ci cfv_release_used_buf(cfv->vq_tx); 4688c2ecf20Sopenharmony_ci spin_lock_irqsave(&cfv->tx_lock, flags); 4698c2ecf20Sopenharmony_ci while ((buf_info = virtqueue_detach_unused_buf(cfv->vq_tx))) 4708c2ecf20Sopenharmony_ci free_buf_info(cfv, buf_info); 4718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cfv->tx_lock, flags); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* Release all dma allocated memory and destroy the pool */ 4748c2ecf20Sopenharmony_ci cfv_destroy_genpool(cfv); 4758c2ecf20Sopenharmony_ci return 0; 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci/* Allocate a buffer in dma-memory and copy skb to it */ 4798c2ecf20Sopenharmony_cistatic struct buf_info *cfv_alloc_and_copy_to_shm(struct cfv_info *cfv, 4808c2ecf20Sopenharmony_ci struct sk_buff *skb, 4818c2ecf20Sopenharmony_ci struct scatterlist *sg) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci struct caif_payload_info *info = (void *)&skb->cb; 4848c2ecf20Sopenharmony_ci struct buf_info *buf_info = NULL; 4858c2ecf20Sopenharmony_ci u8 pad_len, hdr_ofs; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (!cfv->genpool) 4888c2ecf20Sopenharmony_ci goto err; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (unlikely(cfv->tx_hr + skb->len + cfv->tx_tr > cfv->mtu)) { 4918c2ecf20Sopenharmony_ci netdev_warn(cfv->ndev, "Invalid packet len (%d > %d)\n", 4928c2ecf20Sopenharmony_ci cfv->tx_hr + skb->len + cfv->tx_tr, cfv->mtu); 4938c2ecf20Sopenharmony_ci goto err; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci buf_info = kmalloc(sizeof(struct buf_info), GFP_ATOMIC); 4978c2ecf20Sopenharmony_ci if (unlikely(!buf_info)) 4988c2ecf20Sopenharmony_ci goto err; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* Make the IP header aligned in tbe buffer */ 5018c2ecf20Sopenharmony_ci hdr_ofs = cfv->tx_hr + info->hdr_len; 5028c2ecf20Sopenharmony_ci pad_len = hdr_ofs & (IP_HDR_ALIGN - 1); 5038c2ecf20Sopenharmony_ci buf_info->size = cfv->tx_hr + skb->len + cfv->tx_tr + pad_len; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci /* allocate dma memory buffer */ 5068c2ecf20Sopenharmony_ci buf_info->vaddr = (void *)gen_pool_alloc(cfv->genpool, buf_info->size); 5078c2ecf20Sopenharmony_ci if (unlikely(!buf_info->vaddr)) 5088c2ecf20Sopenharmony_ci goto err; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* copy skbuf contents to send buffer */ 5118c2ecf20Sopenharmony_ci skb_copy_bits(skb, 0, buf_info->vaddr + cfv->tx_hr + pad_len, skb->len); 5128c2ecf20Sopenharmony_ci sg_init_one(sg, buf_info->vaddr + pad_len, 5138c2ecf20Sopenharmony_ci skb->len + cfv->tx_hr + cfv->rx_hr); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci return buf_info; 5168c2ecf20Sopenharmony_cierr: 5178c2ecf20Sopenharmony_ci kfree(buf_info); 5188c2ecf20Sopenharmony_ci return NULL; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci/* Put the CAIF packet on the virtio ring and kick the receiver */ 5228c2ecf20Sopenharmony_cistatic netdev_tx_t cfv_netdev_tx(struct sk_buff *skb, struct net_device *netdev) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci struct cfv_info *cfv = netdev_priv(netdev); 5258c2ecf20Sopenharmony_ci struct buf_info *buf_info; 5268c2ecf20Sopenharmony_ci struct scatterlist sg; 5278c2ecf20Sopenharmony_ci unsigned long flags; 5288c2ecf20Sopenharmony_ci bool flow_off = false; 5298c2ecf20Sopenharmony_ci int ret; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* garbage collect released buffers */ 5328c2ecf20Sopenharmony_ci cfv_release_used_buf(cfv->vq_tx); 5338c2ecf20Sopenharmony_ci spin_lock_irqsave(&cfv->tx_lock, flags); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* Flow-off check takes into account number of cpus to make sure 5368c2ecf20Sopenharmony_ci * virtqueue will not be overfilled in any possible smp conditions. 5378c2ecf20Sopenharmony_ci * 5388c2ecf20Sopenharmony_ci * Flow-on is triggered when sufficient buffers are freed 5398c2ecf20Sopenharmony_ci */ 5408c2ecf20Sopenharmony_ci if (unlikely(cfv->vq_tx->num_free <= num_present_cpus())) { 5418c2ecf20Sopenharmony_ci flow_off = true; 5428c2ecf20Sopenharmony_ci cfv->stats.tx_full_ring++; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* If we run out of memory, we release the memory reserve and retry 5468c2ecf20Sopenharmony_ci * allocation. 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_ci buf_info = cfv_alloc_and_copy_to_shm(cfv, skb, &sg); 5498c2ecf20Sopenharmony_ci if (unlikely(!buf_info)) { 5508c2ecf20Sopenharmony_ci cfv->stats.tx_no_mem++; 5518c2ecf20Sopenharmony_ci flow_off = true; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (cfv->reserved_mem && cfv->genpool) { 5548c2ecf20Sopenharmony_ci gen_pool_free(cfv->genpool, cfv->reserved_mem, 5558c2ecf20Sopenharmony_ci cfv->reserved_size); 5568c2ecf20Sopenharmony_ci cfv->reserved_mem = 0; 5578c2ecf20Sopenharmony_ci buf_info = cfv_alloc_and_copy_to_shm(cfv, skb, &sg); 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (unlikely(flow_off)) { 5628c2ecf20Sopenharmony_ci /* Turn flow on when a 1/4 of the descriptors are released */ 5638c2ecf20Sopenharmony_ci cfv->watermark_tx = virtqueue_get_vring_size(cfv->vq_tx) / 4; 5648c2ecf20Sopenharmony_ci /* Enable notifications of recycled TX buffers */ 5658c2ecf20Sopenharmony_ci virtqueue_enable_cb(cfv->vq_tx); 5668c2ecf20Sopenharmony_ci netif_tx_stop_all_queues(netdev); 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (unlikely(!buf_info)) { 5708c2ecf20Sopenharmony_ci /* If the memory reserve does it's job, this shouldn't happen */ 5718c2ecf20Sopenharmony_ci netdev_warn(cfv->ndev, "Out of gen_pool memory\n"); 5728c2ecf20Sopenharmony_ci goto err; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci ret = virtqueue_add_outbuf(cfv->vq_tx, &sg, 1, buf_info, GFP_ATOMIC); 5768c2ecf20Sopenharmony_ci if (unlikely((ret < 0))) { 5778c2ecf20Sopenharmony_ci /* If flow control works, this shouldn't happen */ 5788c2ecf20Sopenharmony_ci netdev_warn(cfv->ndev, "Failed adding buffer to TX vring:%d\n", 5798c2ecf20Sopenharmony_ci ret); 5808c2ecf20Sopenharmony_ci goto err; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* update netdev statistics */ 5848c2ecf20Sopenharmony_ci cfv->ndev->stats.tx_packets++; 5858c2ecf20Sopenharmony_ci cfv->ndev->stats.tx_bytes += skb->len; 5868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cfv->tx_lock, flags); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* tell the remote processor it has a pending message to read */ 5898c2ecf20Sopenharmony_ci virtqueue_kick(cfv->vq_tx); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 5928c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 5938c2ecf20Sopenharmony_cierr: 5948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cfv->tx_lock, flags); 5958c2ecf20Sopenharmony_ci cfv->ndev->stats.tx_dropped++; 5968c2ecf20Sopenharmony_ci free_buf_info(cfv, buf_info); 5978c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 5988c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic void cfv_tx_release_tasklet(unsigned long drv) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci struct cfv_info *cfv = (struct cfv_info *)drv; 6048c2ecf20Sopenharmony_ci cfv_release_used_buf(cfv->vq_tx); 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic const struct net_device_ops cfv_netdev_ops = { 6088c2ecf20Sopenharmony_ci .ndo_open = cfv_netdev_open, 6098c2ecf20Sopenharmony_ci .ndo_stop = cfv_netdev_close, 6108c2ecf20Sopenharmony_ci .ndo_start_xmit = cfv_netdev_tx, 6118c2ecf20Sopenharmony_ci}; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic void cfv_netdev_setup(struct net_device *netdev) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci netdev->netdev_ops = &cfv_netdev_ops; 6168c2ecf20Sopenharmony_ci netdev->type = ARPHRD_CAIF; 6178c2ecf20Sopenharmony_ci netdev->tx_queue_len = 100; 6188c2ecf20Sopenharmony_ci netdev->flags = IFF_POINTOPOINT | IFF_NOARP; 6198c2ecf20Sopenharmony_ci netdev->mtu = CFV_DEF_MTU_SIZE; 6208c2ecf20Sopenharmony_ci netdev->needs_free_netdev = true; 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci/* Create debugfs counters for the device */ 6248c2ecf20Sopenharmony_cistatic inline void debugfs_init(struct cfv_info *cfv) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci cfv->debugfs = debugfs_create_dir(netdev_name(cfv->ndev), NULL); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci debugfs_create_u32("rx-napi-complete", 0400, cfv->debugfs, 6298c2ecf20Sopenharmony_ci &cfv->stats.rx_napi_complete); 6308c2ecf20Sopenharmony_ci debugfs_create_u32("rx-napi-resched", 0400, cfv->debugfs, 6318c2ecf20Sopenharmony_ci &cfv->stats.rx_napi_resched); 6328c2ecf20Sopenharmony_ci debugfs_create_u32("rx-nomem", 0400, cfv->debugfs, 6338c2ecf20Sopenharmony_ci &cfv->stats.rx_nomem); 6348c2ecf20Sopenharmony_ci debugfs_create_u32("rx-kicks", 0400, cfv->debugfs, 6358c2ecf20Sopenharmony_ci &cfv->stats.rx_kicks); 6368c2ecf20Sopenharmony_ci debugfs_create_u32("tx-full-ring", 0400, cfv->debugfs, 6378c2ecf20Sopenharmony_ci &cfv->stats.tx_full_ring); 6388c2ecf20Sopenharmony_ci debugfs_create_u32("tx-no-mem", 0400, cfv->debugfs, 6398c2ecf20Sopenharmony_ci &cfv->stats.tx_no_mem); 6408c2ecf20Sopenharmony_ci debugfs_create_u32("tx-kicks", 0400, cfv->debugfs, 6418c2ecf20Sopenharmony_ci &cfv->stats.tx_kicks); 6428c2ecf20Sopenharmony_ci debugfs_create_u32("tx-flow-on", 0400, cfv->debugfs, 6438c2ecf20Sopenharmony_ci &cfv->stats.tx_flow_on); 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci/* Setup CAIF for the a virtio device */ 6478c2ecf20Sopenharmony_cistatic int cfv_probe(struct virtio_device *vdev) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci vq_callback_t *vq_cbs = cfv_release_cb; 6508c2ecf20Sopenharmony_ci vrh_callback_t *vrh_cbs = cfv_recv; 6518c2ecf20Sopenharmony_ci const char *names = "output"; 6528c2ecf20Sopenharmony_ci const char *cfv_netdev_name = "cfvrt"; 6538c2ecf20Sopenharmony_ci struct net_device *netdev; 6548c2ecf20Sopenharmony_ci struct cfv_info *cfv; 6558c2ecf20Sopenharmony_ci int err; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci netdev = alloc_netdev(sizeof(struct cfv_info), cfv_netdev_name, 6588c2ecf20Sopenharmony_ci NET_NAME_UNKNOWN, cfv_netdev_setup); 6598c2ecf20Sopenharmony_ci if (!netdev) 6608c2ecf20Sopenharmony_ci return -ENOMEM; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci cfv = netdev_priv(netdev); 6638c2ecf20Sopenharmony_ci cfv->vdev = vdev; 6648c2ecf20Sopenharmony_ci cfv->ndev = netdev; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci spin_lock_init(&cfv->tx_lock); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* Get the RX virtio ring. This is a "host side vring". */ 6698c2ecf20Sopenharmony_ci err = -ENODEV; 6708c2ecf20Sopenharmony_ci if (!vdev->vringh_config || !vdev->vringh_config->find_vrhs) 6718c2ecf20Sopenharmony_ci goto err; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci err = vdev->vringh_config->find_vrhs(vdev, 1, &cfv->vr_rx, &vrh_cbs); 6748c2ecf20Sopenharmony_ci if (err) 6758c2ecf20Sopenharmony_ci goto err; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci /* Get the TX virtio ring. This is a "guest side vring". */ 6788c2ecf20Sopenharmony_ci err = virtio_find_vqs(vdev, 1, &cfv->vq_tx, &vq_cbs, &names, NULL); 6798c2ecf20Sopenharmony_ci if (err) 6808c2ecf20Sopenharmony_ci goto err; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* Get the CAIF configuration from virtio config space, if available */ 6838c2ecf20Sopenharmony_ci if (vdev->config->get) { 6848c2ecf20Sopenharmony_ci virtio_cread(vdev, struct virtio_caif_transf_config, headroom, 6858c2ecf20Sopenharmony_ci &cfv->tx_hr); 6868c2ecf20Sopenharmony_ci virtio_cread(vdev, struct virtio_caif_transf_config, headroom, 6878c2ecf20Sopenharmony_ci &cfv->rx_hr); 6888c2ecf20Sopenharmony_ci virtio_cread(vdev, struct virtio_caif_transf_config, tailroom, 6898c2ecf20Sopenharmony_ci &cfv->tx_tr); 6908c2ecf20Sopenharmony_ci virtio_cread(vdev, struct virtio_caif_transf_config, tailroom, 6918c2ecf20Sopenharmony_ci &cfv->rx_tr); 6928c2ecf20Sopenharmony_ci virtio_cread(vdev, struct virtio_caif_transf_config, mtu, 6938c2ecf20Sopenharmony_ci &cfv->mtu); 6948c2ecf20Sopenharmony_ci virtio_cread(vdev, struct virtio_caif_transf_config, mtu, 6958c2ecf20Sopenharmony_ci &cfv->mru); 6968c2ecf20Sopenharmony_ci } else { 6978c2ecf20Sopenharmony_ci cfv->tx_hr = CFV_DEF_HEADROOM; 6988c2ecf20Sopenharmony_ci cfv->rx_hr = CFV_DEF_HEADROOM; 6998c2ecf20Sopenharmony_ci cfv->tx_tr = CFV_DEF_TAILROOM; 7008c2ecf20Sopenharmony_ci cfv->rx_tr = CFV_DEF_TAILROOM; 7018c2ecf20Sopenharmony_ci cfv->mtu = CFV_DEF_MTU_SIZE; 7028c2ecf20Sopenharmony_ci cfv->mru = CFV_DEF_MTU_SIZE; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci netdev->needed_headroom = cfv->tx_hr; 7068c2ecf20Sopenharmony_ci netdev->needed_tailroom = cfv->tx_tr; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* Disable buffer release interrupts unless we have stopped TX queues */ 7098c2ecf20Sopenharmony_ci virtqueue_disable_cb(cfv->vq_tx); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci netdev->mtu = cfv->mtu - cfv->tx_tr; 7128c2ecf20Sopenharmony_ci vdev->priv = cfv; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci /* Initialize NAPI poll context data */ 7158c2ecf20Sopenharmony_ci vringh_kiov_init(&cfv->ctx.riov, NULL, 0); 7168c2ecf20Sopenharmony_ci cfv->ctx.head = USHRT_MAX; 7178c2ecf20Sopenharmony_ci netif_napi_add(netdev, &cfv->napi, cfv_rx_poll, CFV_DEFAULT_QUOTA); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci tasklet_init(&cfv->tx_release_tasklet, 7208c2ecf20Sopenharmony_ci cfv_tx_release_tasklet, 7218c2ecf20Sopenharmony_ci (unsigned long)cfv); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci /* Carrier is off until netdevice is opened */ 7248c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci /* serialize netdev register + virtio_device_ready() with ndo_open() */ 7278c2ecf20Sopenharmony_ci rtnl_lock(); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci /* register Netdev */ 7308c2ecf20Sopenharmony_ci err = register_netdevice(netdev); 7318c2ecf20Sopenharmony_ci if (err) { 7328c2ecf20Sopenharmony_ci rtnl_unlock(); 7338c2ecf20Sopenharmony_ci dev_err(&vdev->dev, "Unable to register netdev (%d)\n", err); 7348c2ecf20Sopenharmony_ci goto err; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci virtio_device_ready(vdev); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci rtnl_unlock(); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci debugfs_init(cfv); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci return 0; 7448c2ecf20Sopenharmony_cierr: 7458c2ecf20Sopenharmony_ci netdev_warn(cfv->ndev, "CAIF Virtio probe failed:%d\n", err); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (cfv->vr_rx) 7488c2ecf20Sopenharmony_ci vdev->vringh_config->del_vrhs(cfv->vdev); 7498c2ecf20Sopenharmony_ci if (cfv->vdev) 7508c2ecf20Sopenharmony_ci vdev->config->del_vqs(cfv->vdev); 7518c2ecf20Sopenharmony_ci free_netdev(netdev); 7528c2ecf20Sopenharmony_ci return err; 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_cistatic void cfv_remove(struct virtio_device *vdev) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci struct cfv_info *cfv = vdev->priv; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci rtnl_lock(); 7608c2ecf20Sopenharmony_ci dev_close(cfv->ndev); 7618c2ecf20Sopenharmony_ci rtnl_unlock(); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci tasklet_kill(&cfv->tx_release_tasklet); 7648c2ecf20Sopenharmony_ci debugfs_remove_recursive(cfv->debugfs); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci vringh_kiov_cleanup(&cfv->ctx.riov); 7678c2ecf20Sopenharmony_ci vdev->config->reset(vdev); 7688c2ecf20Sopenharmony_ci vdev->vringh_config->del_vrhs(cfv->vdev); 7698c2ecf20Sopenharmony_ci cfv->vr_rx = NULL; 7708c2ecf20Sopenharmony_ci vdev->config->del_vqs(cfv->vdev); 7718c2ecf20Sopenharmony_ci unregister_netdev(cfv->ndev); 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic struct virtio_device_id id_table[] = { 7758c2ecf20Sopenharmony_ci { VIRTIO_ID_CAIF, VIRTIO_DEV_ANY_ID }, 7768c2ecf20Sopenharmony_ci { 0 }, 7778c2ecf20Sopenharmony_ci}; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic unsigned int features[] = { 7808c2ecf20Sopenharmony_ci}; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cistatic struct virtio_driver caif_virtio_driver = { 7838c2ecf20Sopenharmony_ci .feature_table = features, 7848c2ecf20Sopenharmony_ci .feature_table_size = ARRAY_SIZE(features), 7858c2ecf20Sopenharmony_ci .driver.name = KBUILD_MODNAME, 7868c2ecf20Sopenharmony_ci .driver.owner = THIS_MODULE, 7878c2ecf20Sopenharmony_ci .id_table = id_table, 7888c2ecf20Sopenharmony_ci .probe = cfv_probe, 7898c2ecf20Sopenharmony_ci .remove = cfv_remove, 7908c2ecf20Sopenharmony_ci}; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_cimodule_virtio_driver(caif_virtio_driver); 7938c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(virtio, id_table); 794