162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2013
462306a36Sopenharmony_ci * Authors: Vicram Arv
562306a36Sopenharmony_ci *	    Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
662306a36Sopenharmony_ci *	    Sjur Brendeland
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/if_arp.h>
1062306a36Sopenharmony_ci#include <linux/virtio.h>
1162306a36Sopenharmony_ci#include <linux/vringh.h>
1262306a36Sopenharmony_ci#include <linux/debugfs.h>
1362306a36Sopenharmony_ci#include <linux/spinlock.h>
1462306a36Sopenharmony_ci#include <linux/genalloc.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/netdevice.h>
1762306a36Sopenharmony_ci#include <linux/rtnetlink.h>
1862306a36Sopenharmony_ci#include <linux/virtio_ids.h>
1962306a36Sopenharmony_ci#include <linux/virtio_caif.h>
2062306a36Sopenharmony_ci#include <linux/virtio_ring.h>
2162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2262306a36Sopenharmony_ci#include <net/caif/caif_dev.h>
2362306a36Sopenharmony_ci#include <linux/virtio_config.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
2662306a36Sopenharmony_ciMODULE_AUTHOR("Vicram Arv");
2762306a36Sopenharmony_ciMODULE_AUTHOR("Sjur Brendeland");
2862306a36Sopenharmony_ciMODULE_DESCRIPTION("Virtio CAIF Driver");
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* NAPI schedule quota */
3162306a36Sopenharmony_ci#define CFV_DEFAULT_QUOTA 32
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* Defaults used if virtio config space is unavailable */
3462306a36Sopenharmony_ci#define CFV_DEF_MTU_SIZE 4096
3562306a36Sopenharmony_ci#define CFV_DEF_HEADROOM 32
3662306a36Sopenharmony_ci#define CFV_DEF_TAILROOM 32
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* Required IP header alignment */
3962306a36Sopenharmony_ci#define IP_HDR_ALIGN 4
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* struct cfv_napi_contxt - NAPI context info
4262306a36Sopenharmony_ci * @riov: IOV holding data read from the ring. Note that riov may
4362306a36Sopenharmony_ci *	  still hold data when cfv_rx_poll() returns.
4462306a36Sopenharmony_ci * @head: Last descriptor ID we received from vringh_getdesc_kern.
4562306a36Sopenharmony_ci *	  We use this to put descriptor back on the used ring. USHRT_MAX is
4662306a36Sopenharmony_ci *	  used to indicate invalid head-id.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_cistruct cfv_napi_context {
4962306a36Sopenharmony_ci	struct vringh_kiov riov;
5062306a36Sopenharmony_ci	unsigned short head;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* struct cfv_stats - statistics for debugfs
5462306a36Sopenharmony_ci * @rx_napi_complete:	Number of NAPI completions (RX)
5562306a36Sopenharmony_ci * @rx_napi_resched:	Number of calls where the full quota was used (RX)
5662306a36Sopenharmony_ci * @rx_nomem:		Number of SKB alloc failures (RX)
5762306a36Sopenharmony_ci * @rx_kicks:		Number of RX kicks
5862306a36Sopenharmony_ci * @tx_full_ring:	Number times TX ring was full
5962306a36Sopenharmony_ci * @tx_no_mem:		Number of times TX went out of memory
6062306a36Sopenharmony_ci * @tx_flow_on:		Number of flow on (TX)
6162306a36Sopenharmony_ci * @tx_kicks:		Number of TX kicks
6262306a36Sopenharmony_ci */
6362306a36Sopenharmony_cistruct cfv_stats {
6462306a36Sopenharmony_ci	u32 rx_napi_complete;
6562306a36Sopenharmony_ci	u32 rx_napi_resched;
6662306a36Sopenharmony_ci	u32 rx_nomem;
6762306a36Sopenharmony_ci	u32 rx_kicks;
6862306a36Sopenharmony_ci	u32 tx_full_ring;
6962306a36Sopenharmony_ci	u32 tx_no_mem;
7062306a36Sopenharmony_ci	u32 tx_flow_on;
7162306a36Sopenharmony_ci	u32 tx_kicks;
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/* struct cfv_info - Caif Virtio control structure
7562306a36Sopenharmony_ci * @cfdev:	caif common header
7662306a36Sopenharmony_ci * @vdev:	Associated virtio device
7762306a36Sopenharmony_ci * @vr_rx:	rx/downlink host vring
7862306a36Sopenharmony_ci * @vq_tx:	tx/uplink virtqueue
7962306a36Sopenharmony_ci * @ndev:	CAIF link layer device
8062306a36Sopenharmony_ci * @watermark_tx: indicates number of free descriptors we need
8162306a36Sopenharmony_ci *		to reopen the tx-queues after overload.
8262306a36Sopenharmony_ci * @tx_lock:	protects vq_tx from concurrent use
8362306a36Sopenharmony_ci * @tx_release_tasklet: Tasklet for freeing consumed TX buffers
8462306a36Sopenharmony_ci * @napi:       Napi context used in cfv_rx_poll()
8562306a36Sopenharmony_ci * @ctx:        Context data used in cfv_rx_poll()
8662306a36Sopenharmony_ci * @tx_hr:	transmit headroom
8762306a36Sopenharmony_ci * @rx_hr:	receive headroom
8862306a36Sopenharmony_ci * @tx_tr:	transmit tail room
8962306a36Sopenharmony_ci * @rx_tr:	receive tail room
9062306a36Sopenharmony_ci * @mtu:	transmit max size
9162306a36Sopenharmony_ci * @mru:	receive max size
9262306a36Sopenharmony_ci * @allocsz:    size of dma memory reserved for TX buffers
9362306a36Sopenharmony_ci * @alloc_addr: virtual address to dma memory for TX buffers
9462306a36Sopenharmony_ci * @alloc_dma:  dma address to dma memory for TX buffers
9562306a36Sopenharmony_ci * @genpool:    Gen Pool used for allocating TX buffers
9662306a36Sopenharmony_ci * @reserved_mem: Pointer to memory reserve allocated from genpool
9762306a36Sopenharmony_ci * @reserved_size: Size of memory reserve allocated from genpool
9862306a36Sopenharmony_ci * @stats:       Statistics exposed in sysfs
9962306a36Sopenharmony_ci * @debugfs:    Debugfs dentry for statistic counters
10062306a36Sopenharmony_ci */
10162306a36Sopenharmony_cistruct cfv_info {
10262306a36Sopenharmony_ci	struct caif_dev_common cfdev;
10362306a36Sopenharmony_ci	struct virtio_device *vdev;
10462306a36Sopenharmony_ci	struct vringh *vr_rx;
10562306a36Sopenharmony_ci	struct virtqueue *vq_tx;
10662306a36Sopenharmony_ci	struct net_device *ndev;
10762306a36Sopenharmony_ci	unsigned int watermark_tx;
10862306a36Sopenharmony_ci	/* Protect access to vq_tx */
10962306a36Sopenharmony_ci	spinlock_t tx_lock;
11062306a36Sopenharmony_ci	struct tasklet_struct tx_release_tasklet;
11162306a36Sopenharmony_ci	struct napi_struct napi;
11262306a36Sopenharmony_ci	struct cfv_napi_context ctx;
11362306a36Sopenharmony_ci	u16 tx_hr;
11462306a36Sopenharmony_ci	u16 rx_hr;
11562306a36Sopenharmony_ci	u16 tx_tr;
11662306a36Sopenharmony_ci	u16 rx_tr;
11762306a36Sopenharmony_ci	u32 mtu;
11862306a36Sopenharmony_ci	u32 mru;
11962306a36Sopenharmony_ci	size_t allocsz;
12062306a36Sopenharmony_ci	void *alloc_addr;
12162306a36Sopenharmony_ci	dma_addr_t alloc_dma;
12262306a36Sopenharmony_ci	struct gen_pool *genpool;
12362306a36Sopenharmony_ci	unsigned long reserved_mem;
12462306a36Sopenharmony_ci	size_t reserved_size;
12562306a36Sopenharmony_ci	struct cfv_stats stats;
12662306a36Sopenharmony_ci	struct dentry *debugfs;
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/* struct buf_info - maintains transmit buffer data handle
13062306a36Sopenharmony_ci * @size:	size of transmit buffer
13162306a36Sopenharmony_ci * @dma_handle: handle to allocated dma device memory area
13262306a36Sopenharmony_ci * @vaddr:	virtual address mapping to allocated memory area
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_cistruct buf_info {
13562306a36Sopenharmony_ci	size_t size;
13662306a36Sopenharmony_ci	u8 *vaddr;
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* Called from virtio device, in IRQ context */
14062306a36Sopenharmony_cistatic void cfv_release_cb(struct virtqueue *vq_tx)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct cfv_info *cfv = vq_tx->vdev->priv;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	++cfv->stats.tx_kicks;
14562306a36Sopenharmony_ci	tasklet_schedule(&cfv->tx_release_tasklet);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic void free_buf_info(struct cfv_info *cfv, struct buf_info *buf_info)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	if (!buf_info)
15162306a36Sopenharmony_ci		return;
15262306a36Sopenharmony_ci	gen_pool_free(cfv->genpool, (unsigned long) buf_info->vaddr,
15362306a36Sopenharmony_ci		      buf_info->size);
15462306a36Sopenharmony_ci	kfree(buf_info);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci/* This is invoked whenever the remote processor completed processing
15862306a36Sopenharmony_ci * a TX msg we just sent, and the buffer is put back to the used ring.
15962306a36Sopenharmony_ci */
16062306a36Sopenharmony_cistatic void cfv_release_used_buf(struct virtqueue *vq_tx)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct cfv_info *cfv = vq_tx->vdev->priv;
16362306a36Sopenharmony_ci	unsigned long flags;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	BUG_ON(vq_tx != cfv->vq_tx);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	for (;;) {
16862306a36Sopenharmony_ci		unsigned int len;
16962306a36Sopenharmony_ci		struct buf_info *buf_info;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		/* Get used buffer from used ring to recycle used descriptors */
17262306a36Sopenharmony_ci		spin_lock_irqsave(&cfv->tx_lock, flags);
17362306a36Sopenharmony_ci		buf_info = virtqueue_get_buf(vq_tx, &len);
17462306a36Sopenharmony_ci		spin_unlock_irqrestore(&cfv->tx_lock, flags);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		/* Stop looping if there are no more buffers to free */
17762306a36Sopenharmony_ci		if (!buf_info)
17862306a36Sopenharmony_ci			break;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci		free_buf_info(cfv, buf_info);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		/* watermark_tx indicates if we previously stopped the tx
18362306a36Sopenharmony_ci		 * queues. If we have enough free stots in the virtio ring,
18462306a36Sopenharmony_ci		 * re-establish memory reserved and open up tx queues.
18562306a36Sopenharmony_ci		 */
18662306a36Sopenharmony_ci		if (cfv->vq_tx->num_free <= cfv->watermark_tx)
18762306a36Sopenharmony_ci			continue;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		/* Re-establish memory reserve */
19062306a36Sopenharmony_ci		if (cfv->reserved_mem == 0 && cfv->genpool)
19162306a36Sopenharmony_ci			cfv->reserved_mem =
19262306a36Sopenharmony_ci				gen_pool_alloc(cfv->genpool,
19362306a36Sopenharmony_ci					       cfv->reserved_size);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		/* Open up the tx queues */
19662306a36Sopenharmony_ci		if (cfv->reserved_mem) {
19762306a36Sopenharmony_ci			cfv->watermark_tx =
19862306a36Sopenharmony_ci				virtqueue_get_vring_size(cfv->vq_tx);
19962306a36Sopenharmony_ci			netif_tx_wake_all_queues(cfv->ndev);
20062306a36Sopenharmony_ci			/* Buffers are recycled in cfv_netdev_tx, so
20162306a36Sopenharmony_ci			 * disable notifications when queues are opened.
20262306a36Sopenharmony_ci			 */
20362306a36Sopenharmony_ci			virtqueue_disable_cb(cfv->vq_tx);
20462306a36Sopenharmony_ci			++cfv->stats.tx_flow_on;
20562306a36Sopenharmony_ci		} else {
20662306a36Sopenharmony_ci			/* if no memory reserve, wait for more free slots */
20762306a36Sopenharmony_ci			WARN_ON(cfv->watermark_tx >
20862306a36Sopenharmony_ci			       virtqueue_get_vring_size(cfv->vq_tx));
20962306a36Sopenharmony_ci			cfv->watermark_tx +=
21062306a36Sopenharmony_ci				virtqueue_get_vring_size(cfv->vq_tx) / 4;
21162306a36Sopenharmony_ci		}
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci/* Allocate a SKB and copy packet data to it */
21662306a36Sopenharmony_cistatic struct sk_buff *cfv_alloc_and_copy_skb(int *err,
21762306a36Sopenharmony_ci					      struct cfv_info *cfv,
21862306a36Sopenharmony_ci					      u8 *frm, u32 frm_len)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct sk_buff *skb;
22162306a36Sopenharmony_ci	u32 cfpkt_len, pad_len;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	*err = 0;
22462306a36Sopenharmony_ci	/* Verify that packet size with down-link header and mtu size */
22562306a36Sopenharmony_ci	if (frm_len > cfv->mru || frm_len <= cfv->rx_hr + cfv->rx_tr) {
22662306a36Sopenharmony_ci		netdev_err(cfv->ndev,
22762306a36Sopenharmony_ci			   "Invalid frmlen:%u  mtu:%u hr:%d tr:%d\n",
22862306a36Sopenharmony_ci			   frm_len, cfv->mru,  cfv->rx_hr,
22962306a36Sopenharmony_ci			   cfv->rx_tr);
23062306a36Sopenharmony_ci		*err = -EPROTO;
23162306a36Sopenharmony_ci		return NULL;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	cfpkt_len = frm_len - (cfv->rx_hr + cfv->rx_tr);
23562306a36Sopenharmony_ci	pad_len = (unsigned long)(frm + cfv->rx_hr) & (IP_HDR_ALIGN - 1);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	skb = netdev_alloc_skb(cfv->ndev, frm_len + pad_len);
23862306a36Sopenharmony_ci	if (!skb) {
23962306a36Sopenharmony_ci		*err = -ENOMEM;
24062306a36Sopenharmony_ci		return NULL;
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	skb_reserve(skb, cfv->rx_hr + pad_len);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	skb_put_data(skb, frm + cfv->rx_hr, cfpkt_len);
24662306a36Sopenharmony_ci	return skb;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci/* Get packets from the host vring */
25062306a36Sopenharmony_cistatic int cfv_rx_poll(struct napi_struct *napi, int quota)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct cfv_info *cfv = container_of(napi, struct cfv_info, napi);
25362306a36Sopenharmony_ci	int rxcnt = 0;
25462306a36Sopenharmony_ci	int err = 0;
25562306a36Sopenharmony_ci	void *buf;
25662306a36Sopenharmony_ci	struct sk_buff *skb;
25762306a36Sopenharmony_ci	struct vringh_kiov *riov = &cfv->ctx.riov;
25862306a36Sopenharmony_ci	unsigned int skb_len;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	do {
26162306a36Sopenharmony_ci		skb = NULL;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		/* Put the previous iovec back on the used ring and
26462306a36Sopenharmony_ci		 * fetch a new iovec if we have processed all elements.
26562306a36Sopenharmony_ci		 */
26662306a36Sopenharmony_ci		if (riov->i == riov->used) {
26762306a36Sopenharmony_ci			if (cfv->ctx.head != USHRT_MAX) {
26862306a36Sopenharmony_ci				vringh_complete_kern(cfv->vr_rx,
26962306a36Sopenharmony_ci						     cfv->ctx.head,
27062306a36Sopenharmony_ci						     0);
27162306a36Sopenharmony_ci				cfv->ctx.head = USHRT_MAX;
27262306a36Sopenharmony_ci			}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci			err = vringh_getdesc_kern(
27562306a36Sopenharmony_ci				cfv->vr_rx,
27662306a36Sopenharmony_ci				riov,
27762306a36Sopenharmony_ci				NULL,
27862306a36Sopenharmony_ci				&cfv->ctx.head,
27962306a36Sopenharmony_ci				GFP_ATOMIC);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci			if (err <= 0)
28262306a36Sopenharmony_ci				goto exit;
28362306a36Sopenharmony_ci		}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		buf = phys_to_virt((unsigned long) riov->iov[riov->i].iov_base);
28662306a36Sopenharmony_ci		/* TODO: Add check on valid buffer address */
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		skb = cfv_alloc_and_copy_skb(&err, cfv, buf,
28962306a36Sopenharmony_ci					     riov->iov[riov->i].iov_len);
29062306a36Sopenharmony_ci		if (unlikely(err))
29162306a36Sopenharmony_ci			goto exit;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		/* Push received packet up the stack. */
29462306a36Sopenharmony_ci		skb_len = skb->len;
29562306a36Sopenharmony_ci		skb->protocol = htons(ETH_P_CAIF);
29662306a36Sopenharmony_ci		skb_reset_mac_header(skb);
29762306a36Sopenharmony_ci		skb->dev = cfv->ndev;
29862306a36Sopenharmony_ci		err = netif_receive_skb(skb);
29962306a36Sopenharmony_ci		if (unlikely(err)) {
30062306a36Sopenharmony_ci			++cfv->ndev->stats.rx_dropped;
30162306a36Sopenharmony_ci		} else {
30262306a36Sopenharmony_ci			++cfv->ndev->stats.rx_packets;
30362306a36Sopenharmony_ci			cfv->ndev->stats.rx_bytes += skb_len;
30462306a36Sopenharmony_ci		}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci		++riov->i;
30762306a36Sopenharmony_ci		++rxcnt;
30862306a36Sopenharmony_ci	} while (rxcnt < quota);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	++cfv->stats.rx_napi_resched;
31162306a36Sopenharmony_ci	goto out;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ciexit:
31462306a36Sopenharmony_ci	switch (err) {
31562306a36Sopenharmony_ci	case 0:
31662306a36Sopenharmony_ci		++cfv->stats.rx_napi_complete;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		/* Really out of packets? (stolen from virtio_net)*/
31962306a36Sopenharmony_ci		napi_complete(napi);
32062306a36Sopenharmony_ci		if (unlikely(!vringh_notify_enable_kern(cfv->vr_rx)) &&
32162306a36Sopenharmony_ci		    napi_schedule_prep(napi)) {
32262306a36Sopenharmony_ci			vringh_notify_disable_kern(cfv->vr_rx);
32362306a36Sopenharmony_ci			__napi_schedule(napi);
32462306a36Sopenharmony_ci		}
32562306a36Sopenharmony_ci		break;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	case -ENOMEM:
32862306a36Sopenharmony_ci		++cfv->stats.rx_nomem;
32962306a36Sopenharmony_ci		dev_kfree_skb(skb);
33062306a36Sopenharmony_ci		/* Stop NAPI poll on OOM, we hope to be polled later */
33162306a36Sopenharmony_ci		napi_complete(napi);
33262306a36Sopenharmony_ci		vringh_notify_enable_kern(cfv->vr_rx);
33362306a36Sopenharmony_ci		break;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	default:
33662306a36Sopenharmony_ci		/* We're doomed, any modem fault is fatal */
33762306a36Sopenharmony_ci		netdev_warn(cfv->ndev, "Bad ring, disable device\n");
33862306a36Sopenharmony_ci		cfv->ndev->stats.rx_dropped = riov->used - riov->i;
33962306a36Sopenharmony_ci		napi_complete(napi);
34062306a36Sopenharmony_ci		vringh_notify_disable_kern(cfv->vr_rx);
34162306a36Sopenharmony_ci		netif_carrier_off(cfv->ndev);
34262306a36Sopenharmony_ci		break;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ciout:
34562306a36Sopenharmony_ci	if (rxcnt && vringh_need_notify_kern(cfv->vr_rx) > 0)
34662306a36Sopenharmony_ci		vringh_notify(cfv->vr_rx);
34762306a36Sopenharmony_ci	return rxcnt;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic void cfv_recv(struct virtio_device *vdev, struct vringh *vr_rx)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct cfv_info *cfv = vdev->priv;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	++cfv->stats.rx_kicks;
35562306a36Sopenharmony_ci	vringh_notify_disable_kern(cfv->vr_rx);
35662306a36Sopenharmony_ci	napi_schedule(&cfv->napi);
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic void cfv_destroy_genpool(struct cfv_info *cfv)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	if (cfv->alloc_addr)
36262306a36Sopenharmony_ci		dma_free_coherent(cfv->vdev->dev.parent->parent,
36362306a36Sopenharmony_ci				  cfv->allocsz, cfv->alloc_addr,
36462306a36Sopenharmony_ci				  cfv->alloc_dma);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (!cfv->genpool)
36762306a36Sopenharmony_ci		return;
36862306a36Sopenharmony_ci	gen_pool_free(cfv->genpool,  cfv->reserved_mem,
36962306a36Sopenharmony_ci		      cfv->reserved_size);
37062306a36Sopenharmony_ci	gen_pool_destroy(cfv->genpool);
37162306a36Sopenharmony_ci	cfv->genpool = NULL;
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic int cfv_create_genpool(struct cfv_info *cfv)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	int err;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/* dma_alloc can only allocate whole pages, and we need a more
37962306a36Sopenharmony_ci	 * fine graned allocation so we use genpool. We ask for space needed
38062306a36Sopenharmony_ci	 * by IP and a full ring. If the dma allcoation fails we retry with a
38162306a36Sopenharmony_ci	 * smaller allocation size.
38262306a36Sopenharmony_ci	 */
38362306a36Sopenharmony_ci	err = -ENOMEM;
38462306a36Sopenharmony_ci	cfv->allocsz = (virtqueue_get_vring_size(cfv->vq_tx) *
38562306a36Sopenharmony_ci			(ETH_DATA_LEN + cfv->tx_hr + cfv->tx_tr) * 11)/10;
38662306a36Sopenharmony_ci	if (cfv->allocsz <= (num_possible_cpus() + 1) * cfv->ndev->mtu)
38762306a36Sopenharmony_ci		return -EINVAL;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	for (;;) {
39062306a36Sopenharmony_ci		if (cfv->allocsz <= num_possible_cpus() * cfv->ndev->mtu) {
39162306a36Sopenharmony_ci			netdev_info(cfv->ndev, "Not enough device memory\n");
39262306a36Sopenharmony_ci			return -ENOMEM;
39362306a36Sopenharmony_ci		}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci		cfv->alloc_addr = dma_alloc_coherent(
39662306a36Sopenharmony_ci						cfv->vdev->dev.parent->parent,
39762306a36Sopenharmony_ci						cfv->allocsz, &cfv->alloc_dma,
39862306a36Sopenharmony_ci						GFP_ATOMIC);
39962306a36Sopenharmony_ci		if (cfv->alloc_addr)
40062306a36Sopenharmony_ci			break;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		cfv->allocsz = (cfv->allocsz * 3) >> 2;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	netdev_dbg(cfv->ndev, "Allocated %zd bytes from dma-memory\n",
40662306a36Sopenharmony_ci		   cfv->allocsz);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	/* Allocate on 128 bytes boundaries (1 << 7)*/
40962306a36Sopenharmony_ci	cfv->genpool = gen_pool_create(7, -1);
41062306a36Sopenharmony_ci	if (!cfv->genpool)
41162306a36Sopenharmony_ci		goto err;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	err = gen_pool_add_virt(cfv->genpool, (unsigned long)cfv->alloc_addr,
41462306a36Sopenharmony_ci				(phys_addr_t)virt_to_phys(cfv->alloc_addr),
41562306a36Sopenharmony_ci				cfv->allocsz, -1);
41662306a36Sopenharmony_ci	if (err)
41762306a36Sopenharmony_ci		goto err;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/* Reserve some memory for low memory situations. If we hit the roof
42062306a36Sopenharmony_ci	 * in the memory pool, we stop TX flow and release the reserve.
42162306a36Sopenharmony_ci	 */
42262306a36Sopenharmony_ci	cfv->reserved_size = num_possible_cpus() * cfv->ndev->mtu;
42362306a36Sopenharmony_ci	cfv->reserved_mem = gen_pool_alloc(cfv->genpool,
42462306a36Sopenharmony_ci					   cfv->reserved_size);
42562306a36Sopenharmony_ci	if (!cfv->reserved_mem) {
42662306a36Sopenharmony_ci		err = -ENOMEM;
42762306a36Sopenharmony_ci		goto err;
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	cfv->watermark_tx = virtqueue_get_vring_size(cfv->vq_tx);
43162306a36Sopenharmony_ci	return 0;
43262306a36Sopenharmony_cierr:
43362306a36Sopenharmony_ci	cfv_destroy_genpool(cfv);
43462306a36Sopenharmony_ci	return err;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci/* Enable the CAIF interface and allocate the memory-pool */
43862306a36Sopenharmony_cistatic int cfv_netdev_open(struct net_device *netdev)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	struct cfv_info *cfv = netdev_priv(netdev);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (cfv_create_genpool(cfv))
44362306a36Sopenharmony_ci		return -ENOMEM;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	netif_carrier_on(netdev);
44662306a36Sopenharmony_ci	napi_enable(&cfv->napi);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/* Schedule NAPI to read any pending packets */
44962306a36Sopenharmony_ci	napi_schedule(&cfv->napi);
45062306a36Sopenharmony_ci	return 0;
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci/* Disable the CAIF interface and free the memory-pool */
45462306a36Sopenharmony_cistatic int cfv_netdev_close(struct net_device *netdev)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	struct cfv_info *cfv = netdev_priv(netdev);
45762306a36Sopenharmony_ci	unsigned long flags;
45862306a36Sopenharmony_ci	struct buf_info *buf_info;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/* Disable interrupts, queues and NAPI polling */
46162306a36Sopenharmony_ci	netif_carrier_off(netdev);
46262306a36Sopenharmony_ci	virtqueue_disable_cb(cfv->vq_tx);
46362306a36Sopenharmony_ci	vringh_notify_disable_kern(cfv->vr_rx);
46462306a36Sopenharmony_ci	napi_disable(&cfv->napi);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	/* Release any TX buffers on both used and available rings */
46762306a36Sopenharmony_ci	cfv_release_used_buf(cfv->vq_tx);
46862306a36Sopenharmony_ci	spin_lock_irqsave(&cfv->tx_lock, flags);
46962306a36Sopenharmony_ci	while ((buf_info = virtqueue_detach_unused_buf(cfv->vq_tx)))
47062306a36Sopenharmony_ci		free_buf_info(cfv, buf_info);
47162306a36Sopenharmony_ci	spin_unlock_irqrestore(&cfv->tx_lock, flags);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	/* Release all dma allocated memory and destroy the pool */
47462306a36Sopenharmony_ci	cfv_destroy_genpool(cfv);
47562306a36Sopenharmony_ci	return 0;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci/* Allocate a buffer in dma-memory and copy skb to it */
47962306a36Sopenharmony_cistatic struct buf_info *cfv_alloc_and_copy_to_shm(struct cfv_info *cfv,
48062306a36Sopenharmony_ci						       struct sk_buff *skb,
48162306a36Sopenharmony_ci						       struct scatterlist *sg)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	struct caif_payload_info *info = (void *)&skb->cb;
48462306a36Sopenharmony_ci	struct buf_info *buf_info = NULL;
48562306a36Sopenharmony_ci	u8 pad_len, hdr_ofs;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (!cfv->genpool)
48862306a36Sopenharmony_ci		goto err;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if (unlikely(cfv->tx_hr + skb->len + cfv->tx_tr > cfv->mtu)) {
49162306a36Sopenharmony_ci		netdev_warn(cfv->ndev, "Invalid packet len (%d > %d)\n",
49262306a36Sopenharmony_ci			    cfv->tx_hr + skb->len + cfv->tx_tr, cfv->mtu);
49362306a36Sopenharmony_ci		goto err;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	buf_info = kmalloc(sizeof(struct buf_info), GFP_ATOMIC);
49762306a36Sopenharmony_ci	if (unlikely(!buf_info))
49862306a36Sopenharmony_ci		goto err;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	/* Make the IP header aligned in the buffer */
50162306a36Sopenharmony_ci	hdr_ofs = cfv->tx_hr + info->hdr_len;
50262306a36Sopenharmony_ci	pad_len = hdr_ofs & (IP_HDR_ALIGN - 1);
50362306a36Sopenharmony_ci	buf_info->size = cfv->tx_hr + skb->len + cfv->tx_tr + pad_len;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	/* allocate dma memory buffer */
50662306a36Sopenharmony_ci	buf_info->vaddr = (void *)gen_pool_alloc(cfv->genpool, buf_info->size);
50762306a36Sopenharmony_ci	if (unlikely(!buf_info->vaddr))
50862306a36Sopenharmony_ci		goto err;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	/* copy skbuf contents to send buffer */
51162306a36Sopenharmony_ci	skb_copy_bits(skb, 0, buf_info->vaddr + cfv->tx_hr + pad_len, skb->len);
51262306a36Sopenharmony_ci	sg_init_one(sg, buf_info->vaddr + pad_len,
51362306a36Sopenharmony_ci		    skb->len + cfv->tx_hr + cfv->rx_hr);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	return buf_info;
51662306a36Sopenharmony_cierr:
51762306a36Sopenharmony_ci	kfree(buf_info);
51862306a36Sopenharmony_ci	return NULL;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci/* Put the CAIF packet on the virtio ring and kick the receiver */
52262306a36Sopenharmony_cistatic netdev_tx_t cfv_netdev_tx(struct sk_buff *skb, struct net_device *netdev)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	struct cfv_info *cfv = netdev_priv(netdev);
52562306a36Sopenharmony_ci	struct buf_info *buf_info;
52662306a36Sopenharmony_ci	struct scatterlist sg;
52762306a36Sopenharmony_ci	unsigned long flags;
52862306a36Sopenharmony_ci	bool flow_off = false;
52962306a36Sopenharmony_ci	int ret;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	/* garbage collect released buffers */
53262306a36Sopenharmony_ci	cfv_release_used_buf(cfv->vq_tx);
53362306a36Sopenharmony_ci	spin_lock_irqsave(&cfv->tx_lock, flags);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* Flow-off check takes into account number of cpus to make sure
53662306a36Sopenharmony_ci	 * virtqueue will not be overfilled in any possible smp conditions.
53762306a36Sopenharmony_ci	 *
53862306a36Sopenharmony_ci	 * Flow-on is triggered when sufficient buffers are freed
53962306a36Sopenharmony_ci	 */
54062306a36Sopenharmony_ci	if (unlikely(cfv->vq_tx->num_free <= num_present_cpus())) {
54162306a36Sopenharmony_ci		flow_off = true;
54262306a36Sopenharmony_ci		cfv->stats.tx_full_ring++;
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	/* If we run out of memory, we release the memory reserve and retry
54662306a36Sopenharmony_ci	 * allocation.
54762306a36Sopenharmony_ci	 */
54862306a36Sopenharmony_ci	buf_info = cfv_alloc_and_copy_to_shm(cfv, skb, &sg);
54962306a36Sopenharmony_ci	if (unlikely(!buf_info)) {
55062306a36Sopenharmony_ci		cfv->stats.tx_no_mem++;
55162306a36Sopenharmony_ci		flow_off = true;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci		if (cfv->reserved_mem && cfv->genpool) {
55462306a36Sopenharmony_ci			gen_pool_free(cfv->genpool,  cfv->reserved_mem,
55562306a36Sopenharmony_ci				      cfv->reserved_size);
55662306a36Sopenharmony_ci			cfv->reserved_mem = 0;
55762306a36Sopenharmony_ci			buf_info = cfv_alloc_and_copy_to_shm(cfv, skb, &sg);
55862306a36Sopenharmony_ci		}
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	if (unlikely(flow_off)) {
56262306a36Sopenharmony_ci		/* Turn flow on when a 1/4 of the descriptors are released */
56362306a36Sopenharmony_ci		cfv->watermark_tx = virtqueue_get_vring_size(cfv->vq_tx) / 4;
56462306a36Sopenharmony_ci		/* Enable notifications of recycled TX buffers */
56562306a36Sopenharmony_ci		virtqueue_enable_cb(cfv->vq_tx);
56662306a36Sopenharmony_ci		netif_tx_stop_all_queues(netdev);
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (unlikely(!buf_info)) {
57062306a36Sopenharmony_ci		/* If the memory reserve does it's job, this shouldn't happen */
57162306a36Sopenharmony_ci		netdev_warn(cfv->ndev, "Out of gen_pool memory\n");
57262306a36Sopenharmony_ci		goto err;
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	ret = virtqueue_add_outbuf(cfv->vq_tx, &sg, 1, buf_info, GFP_ATOMIC);
57662306a36Sopenharmony_ci	if (unlikely((ret < 0))) {
57762306a36Sopenharmony_ci		/* If flow control works, this shouldn't happen */
57862306a36Sopenharmony_ci		netdev_warn(cfv->ndev, "Failed adding buffer to TX vring:%d\n",
57962306a36Sopenharmony_ci			    ret);
58062306a36Sopenharmony_ci		goto err;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/* update netdev statistics */
58462306a36Sopenharmony_ci	cfv->ndev->stats.tx_packets++;
58562306a36Sopenharmony_ci	cfv->ndev->stats.tx_bytes += skb->len;
58662306a36Sopenharmony_ci	spin_unlock_irqrestore(&cfv->tx_lock, flags);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	/* tell the remote processor it has a pending message to read */
58962306a36Sopenharmony_ci	virtqueue_kick(cfv->vq_tx);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	dev_kfree_skb(skb);
59262306a36Sopenharmony_ci	return NETDEV_TX_OK;
59362306a36Sopenharmony_cierr:
59462306a36Sopenharmony_ci	spin_unlock_irqrestore(&cfv->tx_lock, flags);
59562306a36Sopenharmony_ci	cfv->ndev->stats.tx_dropped++;
59662306a36Sopenharmony_ci	free_buf_info(cfv, buf_info);
59762306a36Sopenharmony_ci	dev_kfree_skb(skb);
59862306a36Sopenharmony_ci	return NETDEV_TX_OK;
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic void cfv_tx_release_tasklet(struct tasklet_struct *t)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	struct cfv_info *cfv = from_tasklet(cfv, t, tx_release_tasklet);
60462306a36Sopenharmony_ci	cfv_release_used_buf(cfv->vq_tx);
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic const struct net_device_ops cfv_netdev_ops = {
60862306a36Sopenharmony_ci	.ndo_open = cfv_netdev_open,
60962306a36Sopenharmony_ci	.ndo_stop = cfv_netdev_close,
61062306a36Sopenharmony_ci	.ndo_start_xmit = cfv_netdev_tx,
61162306a36Sopenharmony_ci};
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic void cfv_netdev_setup(struct net_device *netdev)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	netdev->netdev_ops = &cfv_netdev_ops;
61662306a36Sopenharmony_ci	netdev->type = ARPHRD_CAIF;
61762306a36Sopenharmony_ci	netdev->tx_queue_len = 100;
61862306a36Sopenharmony_ci	netdev->flags = IFF_POINTOPOINT | IFF_NOARP;
61962306a36Sopenharmony_ci	netdev->mtu = CFV_DEF_MTU_SIZE;
62062306a36Sopenharmony_ci	netdev->needs_free_netdev = true;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci/* Create debugfs counters for the device */
62462306a36Sopenharmony_cistatic inline void debugfs_init(struct cfv_info *cfv)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	cfv->debugfs = debugfs_create_dir(netdev_name(cfv->ndev), NULL);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	debugfs_create_u32("rx-napi-complete", 0400, cfv->debugfs,
62962306a36Sopenharmony_ci			   &cfv->stats.rx_napi_complete);
63062306a36Sopenharmony_ci	debugfs_create_u32("rx-napi-resched", 0400, cfv->debugfs,
63162306a36Sopenharmony_ci			   &cfv->stats.rx_napi_resched);
63262306a36Sopenharmony_ci	debugfs_create_u32("rx-nomem", 0400, cfv->debugfs,
63362306a36Sopenharmony_ci			   &cfv->stats.rx_nomem);
63462306a36Sopenharmony_ci	debugfs_create_u32("rx-kicks", 0400, cfv->debugfs,
63562306a36Sopenharmony_ci			   &cfv->stats.rx_kicks);
63662306a36Sopenharmony_ci	debugfs_create_u32("tx-full-ring", 0400, cfv->debugfs,
63762306a36Sopenharmony_ci			   &cfv->stats.tx_full_ring);
63862306a36Sopenharmony_ci	debugfs_create_u32("tx-no-mem", 0400, cfv->debugfs,
63962306a36Sopenharmony_ci			   &cfv->stats.tx_no_mem);
64062306a36Sopenharmony_ci	debugfs_create_u32("tx-kicks", 0400, cfv->debugfs,
64162306a36Sopenharmony_ci			   &cfv->stats.tx_kicks);
64262306a36Sopenharmony_ci	debugfs_create_u32("tx-flow-on", 0400, cfv->debugfs,
64362306a36Sopenharmony_ci			   &cfv->stats.tx_flow_on);
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci/* Setup CAIF for the a virtio device */
64762306a36Sopenharmony_cistatic int cfv_probe(struct virtio_device *vdev)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	vq_callback_t *vq_cbs = cfv_release_cb;
65062306a36Sopenharmony_ci	vrh_callback_t *vrh_cbs = cfv_recv;
65162306a36Sopenharmony_ci	const char *names =  "output";
65262306a36Sopenharmony_ci	const char *cfv_netdev_name = "cfvrt";
65362306a36Sopenharmony_ci	struct net_device *netdev;
65462306a36Sopenharmony_ci	struct cfv_info *cfv;
65562306a36Sopenharmony_ci	int err;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	netdev = alloc_netdev(sizeof(struct cfv_info), cfv_netdev_name,
65862306a36Sopenharmony_ci			      NET_NAME_UNKNOWN, cfv_netdev_setup);
65962306a36Sopenharmony_ci	if (!netdev)
66062306a36Sopenharmony_ci		return -ENOMEM;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	cfv = netdev_priv(netdev);
66362306a36Sopenharmony_ci	cfv->vdev = vdev;
66462306a36Sopenharmony_ci	cfv->ndev = netdev;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	spin_lock_init(&cfv->tx_lock);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	/* Get the RX virtio ring. This is a "host side vring". */
66962306a36Sopenharmony_ci	err = -ENODEV;
67062306a36Sopenharmony_ci	if (!vdev->vringh_config || !vdev->vringh_config->find_vrhs)
67162306a36Sopenharmony_ci		goto err;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	err = vdev->vringh_config->find_vrhs(vdev, 1, &cfv->vr_rx, &vrh_cbs);
67462306a36Sopenharmony_ci	if (err)
67562306a36Sopenharmony_ci		goto err;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	/* Get the TX virtio ring. This is a "guest side vring". */
67862306a36Sopenharmony_ci	err = virtio_find_vqs(vdev, 1, &cfv->vq_tx, &vq_cbs, &names, NULL);
67962306a36Sopenharmony_ci	if (err)
68062306a36Sopenharmony_ci		goto err;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	/* Get the CAIF configuration from virtio config space, if available */
68362306a36Sopenharmony_ci	if (vdev->config->get) {
68462306a36Sopenharmony_ci		virtio_cread(vdev, struct virtio_caif_transf_config, headroom,
68562306a36Sopenharmony_ci			     &cfv->tx_hr);
68662306a36Sopenharmony_ci		virtio_cread(vdev, struct virtio_caif_transf_config, headroom,
68762306a36Sopenharmony_ci			     &cfv->rx_hr);
68862306a36Sopenharmony_ci		virtio_cread(vdev, struct virtio_caif_transf_config, tailroom,
68962306a36Sopenharmony_ci			     &cfv->tx_tr);
69062306a36Sopenharmony_ci		virtio_cread(vdev, struct virtio_caif_transf_config, tailroom,
69162306a36Sopenharmony_ci			     &cfv->rx_tr);
69262306a36Sopenharmony_ci		virtio_cread(vdev, struct virtio_caif_transf_config, mtu,
69362306a36Sopenharmony_ci			     &cfv->mtu);
69462306a36Sopenharmony_ci		virtio_cread(vdev, struct virtio_caif_transf_config, mtu,
69562306a36Sopenharmony_ci			     &cfv->mru);
69662306a36Sopenharmony_ci	} else {
69762306a36Sopenharmony_ci		cfv->tx_hr = CFV_DEF_HEADROOM;
69862306a36Sopenharmony_ci		cfv->rx_hr = CFV_DEF_HEADROOM;
69962306a36Sopenharmony_ci		cfv->tx_tr = CFV_DEF_TAILROOM;
70062306a36Sopenharmony_ci		cfv->rx_tr = CFV_DEF_TAILROOM;
70162306a36Sopenharmony_ci		cfv->mtu = CFV_DEF_MTU_SIZE;
70262306a36Sopenharmony_ci		cfv->mru = CFV_DEF_MTU_SIZE;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	netdev->needed_headroom = cfv->tx_hr;
70662306a36Sopenharmony_ci	netdev->needed_tailroom = cfv->tx_tr;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	/* Disable buffer release interrupts unless we have stopped TX queues */
70962306a36Sopenharmony_ci	virtqueue_disable_cb(cfv->vq_tx);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	netdev->mtu = cfv->mtu - cfv->tx_tr;
71262306a36Sopenharmony_ci	vdev->priv = cfv;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	/* Initialize NAPI poll context data */
71562306a36Sopenharmony_ci	vringh_kiov_init(&cfv->ctx.riov, NULL, 0);
71662306a36Sopenharmony_ci	cfv->ctx.head = USHRT_MAX;
71762306a36Sopenharmony_ci	netif_napi_add_weight(netdev, &cfv->napi, cfv_rx_poll,
71862306a36Sopenharmony_ci			      CFV_DEFAULT_QUOTA);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	tasklet_setup(&cfv->tx_release_tasklet, cfv_tx_release_tasklet);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	/* Carrier is off until netdevice is opened */
72362306a36Sopenharmony_ci	netif_carrier_off(netdev);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	/* serialize netdev register + virtio_device_ready() with ndo_open() */
72662306a36Sopenharmony_ci	rtnl_lock();
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	/* register Netdev */
72962306a36Sopenharmony_ci	err = register_netdevice(netdev);
73062306a36Sopenharmony_ci	if (err) {
73162306a36Sopenharmony_ci		rtnl_unlock();
73262306a36Sopenharmony_ci		dev_err(&vdev->dev, "Unable to register netdev (%d)\n", err);
73362306a36Sopenharmony_ci		goto err;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	virtio_device_ready(vdev);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	rtnl_unlock();
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	debugfs_init(cfv);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	return 0;
74362306a36Sopenharmony_cierr:
74462306a36Sopenharmony_ci	netdev_warn(cfv->ndev, "CAIF Virtio probe failed:%d\n", err);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	if (cfv->vr_rx)
74762306a36Sopenharmony_ci		vdev->vringh_config->del_vrhs(cfv->vdev);
74862306a36Sopenharmony_ci	if (cfv->vdev)
74962306a36Sopenharmony_ci		vdev->config->del_vqs(cfv->vdev);
75062306a36Sopenharmony_ci	free_netdev(netdev);
75162306a36Sopenharmony_ci	return err;
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_cistatic void cfv_remove(struct virtio_device *vdev)
75562306a36Sopenharmony_ci{
75662306a36Sopenharmony_ci	struct cfv_info *cfv = vdev->priv;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	rtnl_lock();
75962306a36Sopenharmony_ci	dev_close(cfv->ndev);
76062306a36Sopenharmony_ci	rtnl_unlock();
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	tasklet_kill(&cfv->tx_release_tasklet);
76362306a36Sopenharmony_ci	debugfs_remove_recursive(cfv->debugfs);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	vringh_kiov_cleanup(&cfv->ctx.riov);
76662306a36Sopenharmony_ci	virtio_reset_device(vdev);
76762306a36Sopenharmony_ci	vdev->vringh_config->del_vrhs(cfv->vdev);
76862306a36Sopenharmony_ci	cfv->vr_rx = NULL;
76962306a36Sopenharmony_ci	vdev->config->del_vqs(cfv->vdev);
77062306a36Sopenharmony_ci	unregister_netdev(cfv->ndev);
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic struct virtio_device_id id_table[] = {
77462306a36Sopenharmony_ci	{ VIRTIO_ID_CAIF, VIRTIO_DEV_ANY_ID },
77562306a36Sopenharmony_ci	{ 0 },
77662306a36Sopenharmony_ci};
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_cistatic unsigned int features[] = {
77962306a36Sopenharmony_ci};
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_cistatic struct virtio_driver caif_virtio_driver = {
78262306a36Sopenharmony_ci	.feature_table		= features,
78362306a36Sopenharmony_ci	.feature_table_size	= ARRAY_SIZE(features),
78462306a36Sopenharmony_ci	.driver.name		= KBUILD_MODNAME,
78562306a36Sopenharmony_ci	.driver.owner		= THIS_MODULE,
78662306a36Sopenharmony_ci	.id_table		= id_table,
78762306a36Sopenharmony_ci	.probe			= cfv_probe,
78862306a36Sopenharmony_ci	.remove			= cfv_remove,
78962306a36Sopenharmony_ci};
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cimodule_virtio_driver(caif_virtio_driver);
79262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(virtio, id_table);
793