18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Mellanox BlueField SoC TmFifo driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2019 Mellanox Technologies
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/acpi.h>
98c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
108c2ecf20Sopenharmony_ci#include <linux/circ_buf.h>
118c2ecf20Sopenharmony_ci#include <linux/efi.h>
128c2ecf20Sopenharmony_ci#include <linux/irq.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/mutex.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/types.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/virtio_config.h>
198c2ecf20Sopenharmony_ci#include <linux/virtio_console.h>
208c2ecf20Sopenharmony_ci#include <linux/virtio_ids.h>
218c2ecf20Sopenharmony_ci#include <linux/virtio_net.h>
228c2ecf20Sopenharmony_ci#include <linux/virtio_ring.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "mlxbf-tmfifo-regs.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/* Vring size. */
278c2ecf20Sopenharmony_ci#define MLXBF_TMFIFO_VRING_SIZE			SZ_1K
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* Console Tx buffer size. */
308c2ecf20Sopenharmony_ci#define MLXBF_TMFIFO_CON_TX_BUF_SIZE		SZ_32K
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* Console Tx buffer reserved space. */
338c2ecf20Sopenharmony_ci#define MLXBF_TMFIFO_CON_TX_BUF_RSV_SIZE	8
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* House-keeping timer interval. */
368c2ecf20Sopenharmony_ci#define MLXBF_TMFIFO_TIMER_INTERVAL		(HZ / 10)
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* Virtual devices sharing the TM FIFO. */
398c2ecf20Sopenharmony_ci#define MLXBF_TMFIFO_VDEV_MAX		(VIRTIO_ID_CONSOLE + 1)
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/*
428c2ecf20Sopenharmony_ci * Reserve 1/16 of TmFifo space, so console messages are not starved by
438c2ecf20Sopenharmony_ci * the networking traffic.
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_ci#define MLXBF_TMFIFO_RESERVE_RATIO		16
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Message with data needs at least two words (for header & data). */
488c2ecf20Sopenharmony_ci#define MLXBF_TMFIFO_DATA_MIN_WORDS		2
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistruct mlxbf_tmfifo;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/**
538c2ecf20Sopenharmony_ci * mlxbf_tmfifo_vring - Structure of the TmFifo virtual ring
548c2ecf20Sopenharmony_ci * @va: virtual address of the ring
558c2ecf20Sopenharmony_ci * @dma: dma address of the ring
568c2ecf20Sopenharmony_ci * @vq: pointer to the virtio virtqueue
578c2ecf20Sopenharmony_ci * @desc: current descriptor of the pending packet
588c2ecf20Sopenharmony_ci * @desc_head: head descriptor of the pending packet
598c2ecf20Sopenharmony_ci * @drop_desc: dummy desc for packet dropping
608c2ecf20Sopenharmony_ci * @cur_len: processed length of the current descriptor
618c2ecf20Sopenharmony_ci * @rem_len: remaining length of the pending packet
628c2ecf20Sopenharmony_ci * @pkt_len: total length of the pending packet
638c2ecf20Sopenharmony_ci * @next_avail: next avail descriptor id
648c2ecf20Sopenharmony_ci * @num: vring size (number of descriptors)
658c2ecf20Sopenharmony_ci * @align: vring alignment size
668c2ecf20Sopenharmony_ci * @index: vring index
678c2ecf20Sopenharmony_ci * @vdev_id: vring virtio id (VIRTIO_ID_xxx)
688c2ecf20Sopenharmony_ci * @fifo: pointer to the tmfifo structure
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_cistruct mlxbf_tmfifo_vring {
718c2ecf20Sopenharmony_ci	void *va;
728c2ecf20Sopenharmony_ci	dma_addr_t dma;
738c2ecf20Sopenharmony_ci	struct virtqueue *vq;
748c2ecf20Sopenharmony_ci	struct vring_desc *desc;
758c2ecf20Sopenharmony_ci	struct vring_desc *desc_head;
768c2ecf20Sopenharmony_ci	struct vring_desc drop_desc;
778c2ecf20Sopenharmony_ci	int cur_len;
788c2ecf20Sopenharmony_ci	int rem_len;
798c2ecf20Sopenharmony_ci	u32 pkt_len;
808c2ecf20Sopenharmony_ci	u16 next_avail;
818c2ecf20Sopenharmony_ci	int num;
828c2ecf20Sopenharmony_ci	int align;
838c2ecf20Sopenharmony_ci	int index;
848c2ecf20Sopenharmony_ci	int vdev_id;
858c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo *fifo;
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/* Check whether vring is in drop mode. */
898c2ecf20Sopenharmony_ci#define IS_VRING_DROP(_r) ({ \
908c2ecf20Sopenharmony_ci	typeof(_r) (r) = (_r); \
918c2ecf20Sopenharmony_ci	(r->desc_head == &r->drop_desc ? true : false); })
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* A stub length to drop maximum length packet. */
948c2ecf20Sopenharmony_ci#define VRING_DROP_DESC_MAX_LEN		GENMASK(15, 0)
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci/* Interrupt types. */
978c2ecf20Sopenharmony_cienum {
988c2ecf20Sopenharmony_ci	MLXBF_TM_RX_LWM_IRQ,
998c2ecf20Sopenharmony_ci	MLXBF_TM_RX_HWM_IRQ,
1008c2ecf20Sopenharmony_ci	MLXBF_TM_TX_LWM_IRQ,
1018c2ecf20Sopenharmony_ci	MLXBF_TM_TX_HWM_IRQ,
1028c2ecf20Sopenharmony_ci	MLXBF_TM_MAX_IRQ
1038c2ecf20Sopenharmony_ci};
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/* Ring types (Rx & Tx). */
1068c2ecf20Sopenharmony_cienum {
1078c2ecf20Sopenharmony_ci	MLXBF_TMFIFO_VRING_RX,
1088c2ecf20Sopenharmony_ci	MLXBF_TMFIFO_VRING_TX,
1098c2ecf20Sopenharmony_ci	MLXBF_TMFIFO_VRING_MAX
1108c2ecf20Sopenharmony_ci};
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/**
1138c2ecf20Sopenharmony_ci * mlxbf_tmfifo_vdev - Structure of the TmFifo virtual device
1148c2ecf20Sopenharmony_ci * @vdev: virtio device, in which the vdev.id.device field has the
1158c2ecf20Sopenharmony_ci *        VIRTIO_ID_xxx id to distinguish the virtual device.
1168c2ecf20Sopenharmony_ci * @status: status of the device
1178c2ecf20Sopenharmony_ci * @features: supported features of the device
1188c2ecf20Sopenharmony_ci * @vrings: array of tmfifo vrings of this device
1198c2ecf20Sopenharmony_ci * @config.cons: virtual console config -
1208c2ecf20Sopenharmony_ci *               select if vdev.id.device is VIRTIO_ID_CONSOLE
1218c2ecf20Sopenharmony_ci * @config.net: virtual network config -
1228c2ecf20Sopenharmony_ci *              select if vdev.id.device is VIRTIO_ID_NET
1238c2ecf20Sopenharmony_ci * @tx_buf: tx buffer used to buffer data before writing into the FIFO
1248c2ecf20Sopenharmony_ci */
1258c2ecf20Sopenharmony_cistruct mlxbf_tmfifo_vdev {
1268c2ecf20Sopenharmony_ci	struct virtio_device vdev;
1278c2ecf20Sopenharmony_ci	u8 status;
1288c2ecf20Sopenharmony_ci	u64 features;
1298c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vring vrings[MLXBF_TMFIFO_VRING_MAX];
1308c2ecf20Sopenharmony_ci	union {
1318c2ecf20Sopenharmony_ci		struct virtio_console_config cons;
1328c2ecf20Sopenharmony_ci		struct virtio_net_config net;
1338c2ecf20Sopenharmony_ci	} config;
1348c2ecf20Sopenharmony_ci	struct circ_buf tx_buf;
1358c2ecf20Sopenharmony_ci};
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci/**
1388c2ecf20Sopenharmony_ci * mlxbf_tmfifo_irq_info - Structure of the interrupt information
1398c2ecf20Sopenharmony_ci * @fifo: pointer to the tmfifo structure
1408c2ecf20Sopenharmony_ci * @irq: interrupt number
1418c2ecf20Sopenharmony_ci * @index: index into the interrupt array
1428c2ecf20Sopenharmony_ci */
1438c2ecf20Sopenharmony_cistruct mlxbf_tmfifo_irq_info {
1448c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo *fifo;
1458c2ecf20Sopenharmony_ci	int irq;
1468c2ecf20Sopenharmony_ci	int index;
1478c2ecf20Sopenharmony_ci};
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/**
1508c2ecf20Sopenharmony_ci * mlxbf_tmfifo - Structure of the TmFifo
1518c2ecf20Sopenharmony_ci * @vdev: array of the virtual devices running over the TmFifo
1528c2ecf20Sopenharmony_ci * @lock: lock to protect the TmFifo access
1538c2ecf20Sopenharmony_ci * @rx_base: mapped register base address for the Rx FIFO
1548c2ecf20Sopenharmony_ci * @tx_base: mapped register base address for the Tx FIFO
1558c2ecf20Sopenharmony_ci * @rx_fifo_size: number of entries of the Rx FIFO
1568c2ecf20Sopenharmony_ci * @tx_fifo_size: number of entries of the Tx FIFO
1578c2ecf20Sopenharmony_ci * @pend_events: pending bits for deferred events
1588c2ecf20Sopenharmony_ci * @irq_info: interrupt information
1598c2ecf20Sopenharmony_ci * @work: work struct for deferred process
1608c2ecf20Sopenharmony_ci * @timer: background timer
1618c2ecf20Sopenharmony_ci * @vring: Tx/Rx ring
1628c2ecf20Sopenharmony_ci * @spin_lock: Tx/Rx spin lock
1638c2ecf20Sopenharmony_ci * @is_ready: ready flag
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_cistruct mlxbf_tmfifo {
1668c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vdev *vdev[MLXBF_TMFIFO_VDEV_MAX];
1678c2ecf20Sopenharmony_ci	struct mutex lock;		/* TmFifo lock */
1688c2ecf20Sopenharmony_ci	void __iomem *rx_base;
1698c2ecf20Sopenharmony_ci	void __iomem *tx_base;
1708c2ecf20Sopenharmony_ci	int rx_fifo_size;
1718c2ecf20Sopenharmony_ci	int tx_fifo_size;
1728c2ecf20Sopenharmony_ci	unsigned long pend_events;
1738c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_irq_info irq_info[MLXBF_TM_MAX_IRQ];
1748c2ecf20Sopenharmony_ci	struct work_struct work;
1758c2ecf20Sopenharmony_ci	struct timer_list timer;
1768c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vring *vring[2];
1778c2ecf20Sopenharmony_ci	spinlock_t spin_lock[2];	/* spin lock */
1788c2ecf20Sopenharmony_ci	bool is_ready;
1798c2ecf20Sopenharmony_ci};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/**
1828c2ecf20Sopenharmony_ci * mlxbf_tmfifo_msg_hdr - Structure of the TmFifo message header
1838c2ecf20Sopenharmony_ci * @type: message type
1848c2ecf20Sopenharmony_ci * @len: payload length in network byte order. Messages sent into the FIFO
1858c2ecf20Sopenharmony_ci *       will be read by the other side as data stream in the same byte order.
1868c2ecf20Sopenharmony_ci *       The length needs to be encoded into network order so both sides
1878c2ecf20Sopenharmony_ci *       could understand it.
1888c2ecf20Sopenharmony_ci */
1898c2ecf20Sopenharmony_cistruct mlxbf_tmfifo_msg_hdr {
1908c2ecf20Sopenharmony_ci	u8 type;
1918c2ecf20Sopenharmony_ci	__be16 len;
1928c2ecf20Sopenharmony_ci	u8 unused[5];
1938c2ecf20Sopenharmony_ci} __packed __aligned(sizeof(u64));
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/*
1968c2ecf20Sopenharmony_ci * Default MAC.
1978c2ecf20Sopenharmony_ci * This MAC address will be read from EFI persistent variable if configured.
1988c2ecf20Sopenharmony_ci * It can also be reconfigured with standard Linux tools.
1998c2ecf20Sopenharmony_ci */
2008c2ecf20Sopenharmony_cistatic u8 mlxbf_tmfifo_net_default_mac[ETH_ALEN] = {
2018c2ecf20Sopenharmony_ci	0x00, 0x1A, 0xCA, 0xFF, 0xFF, 0x01
2028c2ecf20Sopenharmony_ci};
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/* EFI variable name of the MAC address. */
2058c2ecf20Sopenharmony_cistatic efi_char16_t mlxbf_tmfifo_efi_name[] = L"RshimMacAddr";
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/* Maximum L2 header length. */
2088c2ecf20Sopenharmony_ci#define MLXBF_TMFIFO_NET_L2_OVERHEAD	(ETH_HLEN + VLAN_HLEN)
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci/* Supported virtio-net features. */
2118c2ecf20Sopenharmony_ci#define MLXBF_TMFIFO_NET_FEATURES \
2128c2ecf20Sopenharmony_ci	(BIT_ULL(VIRTIO_NET_F_MTU) | BIT_ULL(VIRTIO_NET_F_STATUS) | \
2138c2ecf20Sopenharmony_ci	 BIT_ULL(VIRTIO_NET_F_MAC))
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci#define mlxbf_vdev_to_tmfifo(d) container_of(d, struct mlxbf_tmfifo_vdev, vdev)
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci/* Free vrings of the FIFO device. */
2188c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_free_vrings(struct mlxbf_tmfifo *fifo,
2198c2ecf20Sopenharmony_ci				     struct mlxbf_tmfifo_vdev *tm_vdev)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vring *vring;
2228c2ecf20Sopenharmony_ci	int i, size;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tm_vdev->vrings); i++) {
2258c2ecf20Sopenharmony_ci		vring = &tm_vdev->vrings[i];
2268c2ecf20Sopenharmony_ci		if (vring->va) {
2278c2ecf20Sopenharmony_ci			size = vring_size(vring->num, vring->align);
2288c2ecf20Sopenharmony_ci			dma_free_coherent(tm_vdev->vdev.dev.parent, size,
2298c2ecf20Sopenharmony_ci					  vring->va, vring->dma);
2308c2ecf20Sopenharmony_ci			vring->va = NULL;
2318c2ecf20Sopenharmony_ci			if (vring->vq) {
2328c2ecf20Sopenharmony_ci				vring_del_virtqueue(vring->vq);
2338c2ecf20Sopenharmony_ci				vring->vq = NULL;
2348c2ecf20Sopenharmony_ci			}
2358c2ecf20Sopenharmony_ci		}
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci/* Allocate vrings for the FIFO. */
2408c2ecf20Sopenharmony_cistatic int mlxbf_tmfifo_alloc_vrings(struct mlxbf_tmfifo *fifo,
2418c2ecf20Sopenharmony_ci				     struct mlxbf_tmfifo_vdev *tm_vdev)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vring *vring;
2448c2ecf20Sopenharmony_ci	struct device *dev;
2458c2ecf20Sopenharmony_ci	dma_addr_t dma;
2468c2ecf20Sopenharmony_ci	int i, size;
2478c2ecf20Sopenharmony_ci	void *va;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tm_vdev->vrings); i++) {
2508c2ecf20Sopenharmony_ci		vring = &tm_vdev->vrings[i];
2518c2ecf20Sopenharmony_ci		vring->fifo = fifo;
2528c2ecf20Sopenharmony_ci		vring->num = MLXBF_TMFIFO_VRING_SIZE;
2538c2ecf20Sopenharmony_ci		vring->align = SMP_CACHE_BYTES;
2548c2ecf20Sopenharmony_ci		vring->index = i;
2558c2ecf20Sopenharmony_ci		vring->vdev_id = tm_vdev->vdev.id.device;
2568c2ecf20Sopenharmony_ci		vring->drop_desc.len = VRING_DROP_DESC_MAX_LEN;
2578c2ecf20Sopenharmony_ci		dev = &tm_vdev->vdev.dev;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci		size = vring_size(vring->num, vring->align);
2608c2ecf20Sopenharmony_ci		va = dma_alloc_coherent(dev->parent, size, &dma, GFP_KERNEL);
2618c2ecf20Sopenharmony_ci		if (!va) {
2628c2ecf20Sopenharmony_ci			mlxbf_tmfifo_free_vrings(fifo, tm_vdev);
2638c2ecf20Sopenharmony_ci			dev_err(dev->parent, "dma_alloc_coherent failed\n");
2648c2ecf20Sopenharmony_ci			return -ENOMEM;
2658c2ecf20Sopenharmony_ci		}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci		vring->va = va;
2688c2ecf20Sopenharmony_ci		vring->dma = dma;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	return 0;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci/* Disable interrupts of the FIFO device. */
2758c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_disable_irqs(struct mlxbf_tmfifo *fifo)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	int i, irq;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	for (i = 0; i < MLXBF_TM_MAX_IRQ; i++) {
2808c2ecf20Sopenharmony_ci		irq = fifo->irq_info[i].irq;
2818c2ecf20Sopenharmony_ci		fifo->irq_info[i].irq = 0;
2828c2ecf20Sopenharmony_ci		disable_irq(irq);
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci/* Interrupt handler. */
2878c2ecf20Sopenharmony_cistatic irqreturn_t mlxbf_tmfifo_irq_handler(int irq, void *arg)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_irq_info *irq_info = arg;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (!test_and_set_bit(irq_info->index, &irq_info->fifo->pend_events))
2928c2ecf20Sopenharmony_ci		schedule_work(&irq_info->fifo->work);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci/* Get the next packet descriptor from the vring. */
2988c2ecf20Sopenharmony_cistatic struct vring_desc *
2998c2ecf20Sopenharmony_cimlxbf_tmfifo_get_next_desc(struct mlxbf_tmfifo_vring *vring)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	const struct vring *vr = virtqueue_get_vring(vring->vq);
3028c2ecf20Sopenharmony_ci	struct virtio_device *vdev = vring->vq->vdev;
3038c2ecf20Sopenharmony_ci	unsigned int idx, head;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (vring->next_avail == virtio16_to_cpu(vdev, vr->avail->idx))
3068c2ecf20Sopenharmony_ci		return NULL;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	/* Make sure 'avail->idx' is visible already. */
3098c2ecf20Sopenharmony_ci	virtio_rmb(false);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	idx = vring->next_avail % vr->num;
3128c2ecf20Sopenharmony_ci	head = virtio16_to_cpu(vdev, vr->avail->ring[idx]);
3138c2ecf20Sopenharmony_ci	if (WARN_ON(head >= vr->num))
3148c2ecf20Sopenharmony_ci		return NULL;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	vring->next_avail++;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return &vr->desc[head];
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci/* Release virtio descriptor. */
3228c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_release_desc(struct mlxbf_tmfifo_vring *vring,
3238c2ecf20Sopenharmony_ci				      struct vring_desc *desc, u32 len)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	const struct vring *vr = virtqueue_get_vring(vring->vq);
3268c2ecf20Sopenharmony_ci	struct virtio_device *vdev = vring->vq->vdev;
3278c2ecf20Sopenharmony_ci	u16 idx, vr_idx;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	vr_idx = virtio16_to_cpu(vdev, vr->used->idx);
3308c2ecf20Sopenharmony_ci	idx = vr_idx % vr->num;
3318c2ecf20Sopenharmony_ci	vr->used->ring[idx].id = cpu_to_virtio32(vdev, desc - vr->desc);
3328c2ecf20Sopenharmony_ci	vr->used->ring[idx].len = cpu_to_virtio32(vdev, len);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	/*
3358c2ecf20Sopenharmony_ci	 * Virtio could poll and check the 'idx' to decide whether the desc is
3368c2ecf20Sopenharmony_ci	 * done or not. Add a memory barrier here to make sure the update above
3378c2ecf20Sopenharmony_ci	 * completes before updating the idx.
3388c2ecf20Sopenharmony_ci	 */
3398c2ecf20Sopenharmony_ci	virtio_mb(false);
3408c2ecf20Sopenharmony_ci	vr->used->idx = cpu_to_virtio16(vdev, vr_idx + 1);
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci/* Get the total length of the descriptor chain. */
3448c2ecf20Sopenharmony_cistatic u32 mlxbf_tmfifo_get_pkt_len(struct mlxbf_tmfifo_vring *vring,
3458c2ecf20Sopenharmony_ci				    struct vring_desc *desc)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	const struct vring *vr = virtqueue_get_vring(vring->vq);
3488c2ecf20Sopenharmony_ci	struct virtio_device *vdev = vring->vq->vdev;
3498c2ecf20Sopenharmony_ci	u32 len = 0, idx;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	while (desc) {
3528c2ecf20Sopenharmony_ci		len += virtio32_to_cpu(vdev, desc->len);
3538c2ecf20Sopenharmony_ci		if (!(virtio16_to_cpu(vdev, desc->flags) & VRING_DESC_F_NEXT))
3548c2ecf20Sopenharmony_ci			break;
3558c2ecf20Sopenharmony_ci		idx = virtio16_to_cpu(vdev, desc->next);
3568c2ecf20Sopenharmony_ci		desc = &vr->desc[idx];
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	return len;
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_release_pkt(struct mlxbf_tmfifo_vring *vring)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	struct vring_desc *desc_head;
3658c2ecf20Sopenharmony_ci	u32 len = 0;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	if (vring->desc_head) {
3688c2ecf20Sopenharmony_ci		desc_head = vring->desc_head;
3698c2ecf20Sopenharmony_ci		len = vring->pkt_len;
3708c2ecf20Sopenharmony_ci	} else {
3718c2ecf20Sopenharmony_ci		desc_head = mlxbf_tmfifo_get_next_desc(vring);
3728c2ecf20Sopenharmony_ci		len = mlxbf_tmfifo_get_pkt_len(vring, desc_head);
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	if (desc_head)
3768c2ecf20Sopenharmony_ci		mlxbf_tmfifo_release_desc(vring, desc_head, len);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	vring->pkt_len = 0;
3798c2ecf20Sopenharmony_ci	vring->desc = NULL;
3808c2ecf20Sopenharmony_ci	vring->desc_head = NULL;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_init_net_desc(struct mlxbf_tmfifo_vring *vring,
3848c2ecf20Sopenharmony_ci				       struct vring_desc *desc, bool is_rx)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	struct virtio_device *vdev = vring->vq->vdev;
3878c2ecf20Sopenharmony_ci	struct virtio_net_hdr *net_hdr;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	net_hdr = phys_to_virt(virtio64_to_cpu(vdev, desc->addr));
3908c2ecf20Sopenharmony_ci	memset(net_hdr, 0, sizeof(*net_hdr));
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci/* Get and initialize the next packet. */
3948c2ecf20Sopenharmony_cistatic struct vring_desc *
3958c2ecf20Sopenharmony_cimlxbf_tmfifo_get_next_pkt(struct mlxbf_tmfifo_vring *vring, bool is_rx)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	struct vring_desc *desc;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	desc = mlxbf_tmfifo_get_next_desc(vring);
4008c2ecf20Sopenharmony_ci	if (desc && is_rx && vring->vdev_id == VIRTIO_ID_NET)
4018c2ecf20Sopenharmony_ci		mlxbf_tmfifo_init_net_desc(vring, desc, is_rx);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	vring->desc_head = desc;
4048c2ecf20Sopenharmony_ci	vring->desc = desc;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	return desc;
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci/* House-keeping timer. */
4108c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_timer(struct timer_list *t)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo *fifo = container_of(t, struct mlxbf_tmfifo, timer);
4138c2ecf20Sopenharmony_ci	int rx, tx;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	rx = !test_and_set_bit(MLXBF_TM_RX_HWM_IRQ, &fifo->pend_events);
4168c2ecf20Sopenharmony_ci	tx = !test_and_set_bit(MLXBF_TM_TX_LWM_IRQ, &fifo->pend_events);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if (rx || tx)
4198c2ecf20Sopenharmony_ci		schedule_work(&fifo->work);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	mod_timer(&fifo->timer, jiffies + MLXBF_TMFIFO_TIMER_INTERVAL);
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci/* Copy one console packet into the output buffer. */
4258c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_console_output_one(struct mlxbf_tmfifo_vdev *cons,
4268c2ecf20Sopenharmony_ci					    struct mlxbf_tmfifo_vring *vring,
4278c2ecf20Sopenharmony_ci					    struct vring_desc *desc)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	const struct vring *vr = virtqueue_get_vring(vring->vq);
4308c2ecf20Sopenharmony_ci	struct virtio_device *vdev = &cons->vdev;
4318c2ecf20Sopenharmony_ci	u32 len, idx, seg;
4328c2ecf20Sopenharmony_ci	void *addr;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	while (desc) {
4358c2ecf20Sopenharmony_ci		addr = phys_to_virt(virtio64_to_cpu(vdev, desc->addr));
4368c2ecf20Sopenharmony_ci		len = virtio32_to_cpu(vdev, desc->len);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci		seg = CIRC_SPACE_TO_END(cons->tx_buf.head, cons->tx_buf.tail,
4398c2ecf20Sopenharmony_ci					MLXBF_TMFIFO_CON_TX_BUF_SIZE);
4408c2ecf20Sopenharmony_ci		if (len <= seg) {
4418c2ecf20Sopenharmony_ci			memcpy(cons->tx_buf.buf + cons->tx_buf.head, addr, len);
4428c2ecf20Sopenharmony_ci		} else {
4438c2ecf20Sopenharmony_ci			memcpy(cons->tx_buf.buf + cons->tx_buf.head, addr, seg);
4448c2ecf20Sopenharmony_ci			addr += seg;
4458c2ecf20Sopenharmony_ci			memcpy(cons->tx_buf.buf, addr, len - seg);
4468c2ecf20Sopenharmony_ci		}
4478c2ecf20Sopenharmony_ci		cons->tx_buf.head = (cons->tx_buf.head + len) %
4488c2ecf20Sopenharmony_ci			MLXBF_TMFIFO_CON_TX_BUF_SIZE;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci		if (!(virtio16_to_cpu(vdev, desc->flags) & VRING_DESC_F_NEXT))
4518c2ecf20Sopenharmony_ci			break;
4528c2ecf20Sopenharmony_ci		idx = virtio16_to_cpu(vdev, desc->next);
4538c2ecf20Sopenharmony_ci		desc = &vr->desc[idx];
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci/* Copy console data into the output buffer. */
4588c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_console_output(struct mlxbf_tmfifo_vdev *cons,
4598c2ecf20Sopenharmony_ci					struct mlxbf_tmfifo_vring *vring)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	struct vring_desc *desc;
4628c2ecf20Sopenharmony_ci	u32 len, avail;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	desc = mlxbf_tmfifo_get_next_desc(vring);
4658c2ecf20Sopenharmony_ci	while (desc) {
4668c2ecf20Sopenharmony_ci		/* Release the packet if not enough space. */
4678c2ecf20Sopenharmony_ci		len = mlxbf_tmfifo_get_pkt_len(vring, desc);
4688c2ecf20Sopenharmony_ci		avail = CIRC_SPACE(cons->tx_buf.head, cons->tx_buf.tail,
4698c2ecf20Sopenharmony_ci				   MLXBF_TMFIFO_CON_TX_BUF_SIZE);
4708c2ecf20Sopenharmony_ci		if (len + MLXBF_TMFIFO_CON_TX_BUF_RSV_SIZE > avail) {
4718c2ecf20Sopenharmony_ci			mlxbf_tmfifo_release_desc(vring, desc, len);
4728c2ecf20Sopenharmony_ci			break;
4738c2ecf20Sopenharmony_ci		}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci		mlxbf_tmfifo_console_output_one(cons, vring, desc);
4768c2ecf20Sopenharmony_ci		mlxbf_tmfifo_release_desc(vring, desc, len);
4778c2ecf20Sopenharmony_ci		desc = mlxbf_tmfifo_get_next_desc(vring);
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci/* Get the number of available words in Rx FIFO for receiving. */
4828c2ecf20Sopenharmony_cistatic int mlxbf_tmfifo_get_rx_avail(struct mlxbf_tmfifo *fifo)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	u64 sts;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	sts = readq(fifo->rx_base + MLXBF_TMFIFO_RX_STS);
4878c2ecf20Sopenharmony_ci	return FIELD_GET(MLXBF_TMFIFO_RX_STS__COUNT_MASK, sts);
4888c2ecf20Sopenharmony_ci}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci/* Get the number of available words in the TmFifo for sending. */
4918c2ecf20Sopenharmony_cistatic int mlxbf_tmfifo_get_tx_avail(struct mlxbf_tmfifo *fifo, int vdev_id)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	int tx_reserve;
4948c2ecf20Sopenharmony_ci	u32 count;
4958c2ecf20Sopenharmony_ci	u64 sts;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	/* Reserve some room in FIFO for console messages. */
4988c2ecf20Sopenharmony_ci	if (vdev_id == VIRTIO_ID_NET)
4998c2ecf20Sopenharmony_ci		tx_reserve = fifo->tx_fifo_size / MLXBF_TMFIFO_RESERVE_RATIO;
5008c2ecf20Sopenharmony_ci	else
5018c2ecf20Sopenharmony_ci		tx_reserve = 1;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	sts = readq(fifo->tx_base + MLXBF_TMFIFO_TX_STS);
5048c2ecf20Sopenharmony_ci	count = FIELD_GET(MLXBF_TMFIFO_TX_STS__COUNT_MASK, sts);
5058c2ecf20Sopenharmony_ci	return fifo->tx_fifo_size - tx_reserve - count;
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci/* Console Tx (move data from the output buffer into the TmFifo). */
5098c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_console_tx(struct mlxbf_tmfifo *fifo, int avail)
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_msg_hdr hdr;
5128c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vdev *cons;
5138c2ecf20Sopenharmony_ci	unsigned long flags;
5148c2ecf20Sopenharmony_ci	int size, seg;
5158c2ecf20Sopenharmony_ci	void *addr;
5168c2ecf20Sopenharmony_ci	u64 data;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	/* Return if not enough space available. */
5198c2ecf20Sopenharmony_ci	if (avail < MLXBF_TMFIFO_DATA_MIN_WORDS)
5208c2ecf20Sopenharmony_ci		return;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	cons = fifo->vdev[VIRTIO_ID_CONSOLE];
5238c2ecf20Sopenharmony_ci	if (!cons || !cons->tx_buf.buf)
5248c2ecf20Sopenharmony_ci		return;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/* Return if no data to send. */
5278c2ecf20Sopenharmony_ci	size = CIRC_CNT(cons->tx_buf.head, cons->tx_buf.tail,
5288c2ecf20Sopenharmony_ci			MLXBF_TMFIFO_CON_TX_BUF_SIZE);
5298c2ecf20Sopenharmony_ci	if (size == 0)
5308c2ecf20Sopenharmony_ci		return;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	/* Adjust the size to available space. */
5338c2ecf20Sopenharmony_ci	if (size + sizeof(hdr) > avail * sizeof(u64))
5348c2ecf20Sopenharmony_ci		size = avail * sizeof(u64) - sizeof(hdr);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	/* Write header. */
5378c2ecf20Sopenharmony_ci	hdr.type = VIRTIO_ID_CONSOLE;
5388c2ecf20Sopenharmony_ci	hdr.len = htons(size);
5398c2ecf20Sopenharmony_ci	writeq(*(u64 *)&hdr, fifo->tx_base + MLXBF_TMFIFO_TX_DATA);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	/* Use spin-lock to protect the 'cons->tx_buf'. */
5428c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fifo->spin_lock[0], flags);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	while (size > 0) {
5458c2ecf20Sopenharmony_ci		addr = cons->tx_buf.buf + cons->tx_buf.tail;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci		seg = CIRC_CNT_TO_END(cons->tx_buf.head, cons->tx_buf.tail,
5488c2ecf20Sopenharmony_ci				      MLXBF_TMFIFO_CON_TX_BUF_SIZE);
5498c2ecf20Sopenharmony_ci		if (seg >= sizeof(u64)) {
5508c2ecf20Sopenharmony_ci			memcpy(&data, addr, sizeof(u64));
5518c2ecf20Sopenharmony_ci		} else {
5528c2ecf20Sopenharmony_ci			memcpy(&data, addr, seg);
5538c2ecf20Sopenharmony_ci			memcpy((u8 *)&data + seg, cons->tx_buf.buf,
5548c2ecf20Sopenharmony_ci			       sizeof(u64) - seg);
5558c2ecf20Sopenharmony_ci		}
5568c2ecf20Sopenharmony_ci		writeq(data, fifo->tx_base + MLXBF_TMFIFO_TX_DATA);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci		if (size >= sizeof(u64)) {
5598c2ecf20Sopenharmony_ci			cons->tx_buf.tail = (cons->tx_buf.tail + sizeof(u64)) %
5608c2ecf20Sopenharmony_ci				MLXBF_TMFIFO_CON_TX_BUF_SIZE;
5618c2ecf20Sopenharmony_ci			size -= sizeof(u64);
5628c2ecf20Sopenharmony_ci		} else {
5638c2ecf20Sopenharmony_ci			cons->tx_buf.tail = (cons->tx_buf.tail + size) %
5648c2ecf20Sopenharmony_ci				MLXBF_TMFIFO_CON_TX_BUF_SIZE;
5658c2ecf20Sopenharmony_ci			size = 0;
5668c2ecf20Sopenharmony_ci		}
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fifo->spin_lock[0], flags);
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci/* Rx/Tx one word in the descriptor buffer. */
5738c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_rxtx_word(struct mlxbf_tmfifo_vring *vring,
5748c2ecf20Sopenharmony_ci				   struct vring_desc *desc,
5758c2ecf20Sopenharmony_ci				   bool is_rx, int len)
5768c2ecf20Sopenharmony_ci{
5778c2ecf20Sopenharmony_ci	struct virtio_device *vdev = vring->vq->vdev;
5788c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo *fifo = vring->fifo;
5798c2ecf20Sopenharmony_ci	void *addr;
5808c2ecf20Sopenharmony_ci	u64 data;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	/* Get the buffer address of this desc. */
5838c2ecf20Sopenharmony_ci	addr = phys_to_virt(virtio64_to_cpu(vdev, desc->addr));
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	/* Read a word from FIFO for Rx. */
5868c2ecf20Sopenharmony_ci	if (is_rx)
5878c2ecf20Sopenharmony_ci		data = readq(fifo->rx_base + MLXBF_TMFIFO_RX_DATA);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	if (vring->cur_len + sizeof(u64) <= len) {
5908c2ecf20Sopenharmony_ci		/* The whole word. */
5918c2ecf20Sopenharmony_ci		if (is_rx) {
5928c2ecf20Sopenharmony_ci			if (!IS_VRING_DROP(vring))
5938c2ecf20Sopenharmony_ci				memcpy(addr + vring->cur_len, &data,
5948c2ecf20Sopenharmony_ci				       sizeof(u64));
5958c2ecf20Sopenharmony_ci		} else {
5968c2ecf20Sopenharmony_ci			memcpy(&data, addr + vring->cur_len,
5978c2ecf20Sopenharmony_ci			       sizeof(u64));
5988c2ecf20Sopenharmony_ci		}
5998c2ecf20Sopenharmony_ci		vring->cur_len += sizeof(u64);
6008c2ecf20Sopenharmony_ci	} else {
6018c2ecf20Sopenharmony_ci		/* Leftover bytes. */
6028c2ecf20Sopenharmony_ci		if (is_rx) {
6038c2ecf20Sopenharmony_ci			if (!IS_VRING_DROP(vring))
6048c2ecf20Sopenharmony_ci				memcpy(addr + vring->cur_len, &data,
6058c2ecf20Sopenharmony_ci				       len - vring->cur_len);
6068c2ecf20Sopenharmony_ci		} else {
6078c2ecf20Sopenharmony_ci			data = 0;
6088c2ecf20Sopenharmony_ci			memcpy(&data, addr + vring->cur_len,
6098c2ecf20Sopenharmony_ci			       len - vring->cur_len);
6108c2ecf20Sopenharmony_ci		}
6118c2ecf20Sopenharmony_ci		vring->cur_len = len;
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	/* Write the word into FIFO for Tx. */
6158c2ecf20Sopenharmony_ci	if (!is_rx)
6168c2ecf20Sopenharmony_ci		writeq(data, fifo->tx_base + MLXBF_TMFIFO_TX_DATA);
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci/*
6208c2ecf20Sopenharmony_ci * Rx/Tx packet header.
6218c2ecf20Sopenharmony_ci *
6228c2ecf20Sopenharmony_ci * In Rx case, the packet might be found to belong to a different vring since
6238c2ecf20Sopenharmony_ci * the TmFifo is shared by different services. In such case, the 'vring_change'
6248c2ecf20Sopenharmony_ci * flag is set.
6258c2ecf20Sopenharmony_ci */
6268c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_rxtx_header(struct mlxbf_tmfifo_vring *vring,
6278c2ecf20Sopenharmony_ci				     struct vring_desc **desc,
6288c2ecf20Sopenharmony_ci				     bool is_rx, bool *vring_change)
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo *fifo = vring->fifo;
6318c2ecf20Sopenharmony_ci	struct virtio_net_config *config;
6328c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_msg_hdr hdr;
6338c2ecf20Sopenharmony_ci	int vdev_id, hdr_len;
6348c2ecf20Sopenharmony_ci	bool drop_rx = false;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	/* Read/Write packet header. */
6378c2ecf20Sopenharmony_ci	if (is_rx) {
6388c2ecf20Sopenharmony_ci		/* Drain one word from the FIFO. */
6398c2ecf20Sopenharmony_ci		*(u64 *)&hdr = readq(fifo->rx_base + MLXBF_TMFIFO_RX_DATA);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci		/* Skip the length 0 packets (keepalive). */
6428c2ecf20Sopenharmony_ci		if (hdr.len == 0)
6438c2ecf20Sopenharmony_ci			return;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci		/* Check packet type. */
6468c2ecf20Sopenharmony_ci		if (hdr.type == VIRTIO_ID_NET) {
6478c2ecf20Sopenharmony_ci			vdev_id = VIRTIO_ID_NET;
6488c2ecf20Sopenharmony_ci			hdr_len = sizeof(struct virtio_net_hdr);
6498c2ecf20Sopenharmony_ci			config = &fifo->vdev[vdev_id]->config.net;
6508c2ecf20Sopenharmony_ci			/* A legacy-only interface for now. */
6518c2ecf20Sopenharmony_ci			if (ntohs(hdr.len) >
6528c2ecf20Sopenharmony_ci			    __virtio16_to_cpu(virtio_legacy_is_little_endian(),
6538c2ecf20Sopenharmony_ci					      config->mtu) +
6548c2ecf20Sopenharmony_ci					      MLXBF_TMFIFO_NET_L2_OVERHEAD)
6558c2ecf20Sopenharmony_ci				drop_rx = true;
6568c2ecf20Sopenharmony_ci		} else {
6578c2ecf20Sopenharmony_ci			vdev_id = VIRTIO_ID_CONSOLE;
6588c2ecf20Sopenharmony_ci			hdr_len = 0;
6598c2ecf20Sopenharmony_ci		}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci		/*
6628c2ecf20Sopenharmony_ci		 * Check whether the new packet still belongs to this vring.
6638c2ecf20Sopenharmony_ci		 * If not, update the pkt_len of the new vring.
6648c2ecf20Sopenharmony_ci		 */
6658c2ecf20Sopenharmony_ci		if (vdev_id != vring->vdev_id) {
6668c2ecf20Sopenharmony_ci			struct mlxbf_tmfifo_vdev *tm_dev2 = fifo->vdev[vdev_id];
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci			if (!tm_dev2)
6698c2ecf20Sopenharmony_ci				return;
6708c2ecf20Sopenharmony_ci			vring->desc = *desc;
6718c2ecf20Sopenharmony_ci			vring = &tm_dev2->vrings[MLXBF_TMFIFO_VRING_RX];
6728c2ecf20Sopenharmony_ci			*vring_change = true;
6738c2ecf20Sopenharmony_ci		}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci		if (drop_rx && !IS_VRING_DROP(vring)) {
6768c2ecf20Sopenharmony_ci			if (vring->desc_head)
6778c2ecf20Sopenharmony_ci				mlxbf_tmfifo_release_pkt(vring);
6788c2ecf20Sopenharmony_ci			*desc = &vring->drop_desc;
6798c2ecf20Sopenharmony_ci			vring->desc_head = *desc;
6808c2ecf20Sopenharmony_ci			vring->desc = *desc;
6818c2ecf20Sopenharmony_ci		}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci		vring->pkt_len = ntohs(hdr.len) + hdr_len;
6848c2ecf20Sopenharmony_ci	} else {
6858c2ecf20Sopenharmony_ci		/* Network virtio has an extra header. */
6868c2ecf20Sopenharmony_ci		hdr_len = (vring->vdev_id == VIRTIO_ID_NET) ?
6878c2ecf20Sopenharmony_ci			   sizeof(struct virtio_net_hdr) : 0;
6888c2ecf20Sopenharmony_ci		vring->pkt_len = mlxbf_tmfifo_get_pkt_len(vring, *desc);
6898c2ecf20Sopenharmony_ci		hdr.type = (vring->vdev_id == VIRTIO_ID_NET) ?
6908c2ecf20Sopenharmony_ci			    VIRTIO_ID_NET : VIRTIO_ID_CONSOLE;
6918c2ecf20Sopenharmony_ci		hdr.len = htons(vring->pkt_len - hdr_len);
6928c2ecf20Sopenharmony_ci		writeq(*(u64 *)&hdr, fifo->tx_base + MLXBF_TMFIFO_TX_DATA);
6938c2ecf20Sopenharmony_ci	}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	vring->cur_len = hdr_len;
6968c2ecf20Sopenharmony_ci	vring->rem_len = vring->pkt_len;
6978c2ecf20Sopenharmony_ci	fifo->vring[is_rx] = vring;
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci/*
7018c2ecf20Sopenharmony_ci * Rx/Tx one descriptor.
7028c2ecf20Sopenharmony_ci *
7038c2ecf20Sopenharmony_ci * Return true to indicate more data available.
7048c2ecf20Sopenharmony_ci */
7058c2ecf20Sopenharmony_cistatic bool mlxbf_tmfifo_rxtx_one_desc(struct mlxbf_tmfifo_vring *vring,
7068c2ecf20Sopenharmony_ci				       bool is_rx, int *avail)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	const struct vring *vr = virtqueue_get_vring(vring->vq);
7098c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo *fifo = vring->fifo;
7108c2ecf20Sopenharmony_ci	struct virtio_device *vdev;
7118c2ecf20Sopenharmony_ci	bool vring_change = false;
7128c2ecf20Sopenharmony_ci	struct vring_desc *desc;
7138c2ecf20Sopenharmony_ci	unsigned long flags;
7148c2ecf20Sopenharmony_ci	u32 len, idx;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	vdev = &fifo->vdev[vring->vdev_id]->vdev;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	/* Get the descriptor of the next packet. */
7198c2ecf20Sopenharmony_ci	if (!vring->desc) {
7208c2ecf20Sopenharmony_ci		desc = mlxbf_tmfifo_get_next_pkt(vring, is_rx);
7218c2ecf20Sopenharmony_ci		if (!desc) {
7228c2ecf20Sopenharmony_ci			/* Drop next Rx packet to avoid stuck. */
7238c2ecf20Sopenharmony_ci			if (is_rx) {
7248c2ecf20Sopenharmony_ci				desc = &vring->drop_desc;
7258c2ecf20Sopenharmony_ci				vring->desc_head = desc;
7268c2ecf20Sopenharmony_ci				vring->desc = desc;
7278c2ecf20Sopenharmony_ci			} else {
7288c2ecf20Sopenharmony_ci				return false;
7298c2ecf20Sopenharmony_ci			}
7308c2ecf20Sopenharmony_ci		}
7318c2ecf20Sopenharmony_ci	} else {
7328c2ecf20Sopenharmony_ci		desc = vring->desc;
7338c2ecf20Sopenharmony_ci	}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	/* Beginning of a packet. Start to Rx/Tx packet header. */
7368c2ecf20Sopenharmony_ci	if (vring->pkt_len == 0) {
7378c2ecf20Sopenharmony_ci		mlxbf_tmfifo_rxtx_header(vring, &desc, is_rx, &vring_change);
7388c2ecf20Sopenharmony_ci		(*avail)--;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci		/* Return if new packet is for another ring. */
7418c2ecf20Sopenharmony_ci		if (vring_change)
7428c2ecf20Sopenharmony_ci			return false;
7438c2ecf20Sopenharmony_ci		goto mlxbf_tmfifo_desc_done;
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	/* Get the length of this desc. */
7478c2ecf20Sopenharmony_ci	len = virtio32_to_cpu(vdev, desc->len);
7488c2ecf20Sopenharmony_ci	if (len > vring->rem_len)
7498c2ecf20Sopenharmony_ci		len = vring->rem_len;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	/* Rx/Tx one word (8 bytes) if not done. */
7528c2ecf20Sopenharmony_ci	if (vring->cur_len < len) {
7538c2ecf20Sopenharmony_ci		mlxbf_tmfifo_rxtx_word(vring, desc, is_rx, len);
7548c2ecf20Sopenharmony_ci		(*avail)--;
7558c2ecf20Sopenharmony_ci	}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	/* Check again whether it's done. */
7588c2ecf20Sopenharmony_ci	if (vring->cur_len == len) {
7598c2ecf20Sopenharmony_ci		vring->cur_len = 0;
7608c2ecf20Sopenharmony_ci		vring->rem_len -= len;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci		/* Get the next desc on the chain. */
7638c2ecf20Sopenharmony_ci		if (!IS_VRING_DROP(vring) && vring->rem_len > 0 &&
7648c2ecf20Sopenharmony_ci		    (virtio16_to_cpu(vdev, desc->flags) & VRING_DESC_F_NEXT)) {
7658c2ecf20Sopenharmony_ci			idx = virtio16_to_cpu(vdev, desc->next);
7668c2ecf20Sopenharmony_ci			desc = &vr->desc[idx];
7678c2ecf20Sopenharmony_ci			goto mlxbf_tmfifo_desc_done;
7688c2ecf20Sopenharmony_ci		}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci		/* Done and release the packet. */
7718c2ecf20Sopenharmony_ci		desc = NULL;
7728c2ecf20Sopenharmony_ci		fifo->vring[is_rx] = NULL;
7738c2ecf20Sopenharmony_ci		if (!IS_VRING_DROP(vring)) {
7748c2ecf20Sopenharmony_ci			mlxbf_tmfifo_release_pkt(vring);
7758c2ecf20Sopenharmony_ci		} else {
7768c2ecf20Sopenharmony_ci			vring->pkt_len = 0;
7778c2ecf20Sopenharmony_ci			vring->desc_head = NULL;
7788c2ecf20Sopenharmony_ci			vring->desc = NULL;
7798c2ecf20Sopenharmony_ci			return false;
7808c2ecf20Sopenharmony_ci		}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci		/*
7838c2ecf20Sopenharmony_ci		 * Make sure the load/store are in order before
7848c2ecf20Sopenharmony_ci		 * returning back to virtio.
7858c2ecf20Sopenharmony_ci		 */
7868c2ecf20Sopenharmony_ci		virtio_mb(false);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci		/* Notify upper layer that packet is done. */
7898c2ecf20Sopenharmony_ci		spin_lock_irqsave(&fifo->spin_lock[is_rx], flags);
7908c2ecf20Sopenharmony_ci		vring_interrupt(0, vring->vq);
7918c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fifo->spin_lock[is_rx], flags);
7928c2ecf20Sopenharmony_ci	}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_cimlxbf_tmfifo_desc_done:
7958c2ecf20Sopenharmony_ci	/* Save the current desc. */
7968c2ecf20Sopenharmony_ci	vring->desc = desc;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	return true;
7998c2ecf20Sopenharmony_ci}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci/* Rx & Tx processing of a queue. */
8028c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_rxtx(struct mlxbf_tmfifo_vring *vring, bool is_rx)
8038c2ecf20Sopenharmony_ci{
8048c2ecf20Sopenharmony_ci	int avail = 0, devid = vring->vdev_id;
8058c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo *fifo;
8068c2ecf20Sopenharmony_ci	bool more;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	fifo = vring->fifo;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	/* Return if vdev is not ready. */
8118c2ecf20Sopenharmony_ci	if (!fifo->vdev[devid])
8128c2ecf20Sopenharmony_ci		return;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	/* Return if another vring is running. */
8158c2ecf20Sopenharmony_ci	if (fifo->vring[is_rx] && fifo->vring[is_rx] != vring)
8168c2ecf20Sopenharmony_ci		return;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	/* Only handle console and network for now. */
8198c2ecf20Sopenharmony_ci	if (WARN_ON(devid != VIRTIO_ID_NET && devid != VIRTIO_ID_CONSOLE))
8208c2ecf20Sopenharmony_ci		return;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	do {
8238c2ecf20Sopenharmony_ci		/* Get available FIFO space. */
8248c2ecf20Sopenharmony_ci		if (avail == 0) {
8258c2ecf20Sopenharmony_ci			if (is_rx)
8268c2ecf20Sopenharmony_ci				avail = mlxbf_tmfifo_get_rx_avail(fifo);
8278c2ecf20Sopenharmony_ci			else
8288c2ecf20Sopenharmony_ci				avail = mlxbf_tmfifo_get_tx_avail(fifo, devid);
8298c2ecf20Sopenharmony_ci			if (avail <= 0)
8308c2ecf20Sopenharmony_ci				break;
8318c2ecf20Sopenharmony_ci		}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci		/* Console output always comes from the Tx buffer. */
8348c2ecf20Sopenharmony_ci		if (!is_rx && devid == VIRTIO_ID_CONSOLE) {
8358c2ecf20Sopenharmony_ci			mlxbf_tmfifo_console_tx(fifo, avail);
8368c2ecf20Sopenharmony_ci			break;
8378c2ecf20Sopenharmony_ci		}
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci		/* Handle one descriptor. */
8408c2ecf20Sopenharmony_ci		more = mlxbf_tmfifo_rxtx_one_desc(vring, is_rx, &avail);
8418c2ecf20Sopenharmony_ci	} while (more);
8428c2ecf20Sopenharmony_ci}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci/* Handle Rx or Tx queues. */
8458c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_work_rxtx(struct mlxbf_tmfifo *fifo, int queue_id,
8468c2ecf20Sopenharmony_ci				   int irq_id, bool is_rx)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vdev *tm_vdev;
8498c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vring *vring;
8508c2ecf20Sopenharmony_ci	int i;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	if (!test_and_clear_bit(irq_id, &fifo->pend_events) ||
8538c2ecf20Sopenharmony_ci	    !fifo->irq_info[irq_id].irq)
8548c2ecf20Sopenharmony_ci		return;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	for (i = 0; i < MLXBF_TMFIFO_VDEV_MAX; i++) {
8578c2ecf20Sopenharmony_ci		tm_vdev = fifo->vdev[i];
8588c2ecf20Sopenharmony_ci		if (tm_vdev) {
8598c2ecf20Sopenharmony_ci			vring = &tm_vdev->vrings[queue_id];
8608c2ecf20Sopenharmony_ci			if (vring->vq)
8618c2ecf20Sopenharmony_ci				mlxbf_tmfifo_rxtx(vring, is_rx);
8628c2ecf20Sopenharmony_ci		}
8638c2ecf20Sopenharmony_ci	}
8648c2ecf20Sopenharmony_ci}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci/* Work handler for Rx and Tx case. */
8678c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_work_handler(struct work_struct *work)
8688c2ecf20Sopenharmony_ci{
8698c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo *fifo;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	fifo = container_of(work, struct mlxbf_tmfifo, work);
8728c2ecf20Sopenharmony_ci	if (!fifo->is_ready)
8738c2ecf20Sopenharmony_ci		return;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	mutex_lock(&fifo->lock);
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	/* Tx (Send data to the TmFifo). */
8788c2ecf20Sopenharmony_ci	mlxbf_tmfifo_work_rxtx(fifo, MLXBF_TMFIFO_VRING_TX,
8798c2ecf20Sopenharmony_ci			       MLXBF_TM_TX_LWM_IRQ, false);
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	/* Rx (Receive data from the TmFifo). */
8828c2ecf20Sopenharmony_ci	mlxbf_tmfifo_work_rxtx(fifo, MLXBF_TMFIFO_VRING_RX,
8838c2ecf20Sopenharmony_ci			       MLXBF_TM_RX_HWM_IRQ, true);
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	mutex_unlock(&fifo->lock);
8868c2ecf20Sopenharmony_ci}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci/* The notify function is called when new buffers are posted. */
8898c2ecf20Sopenharmony_cistatic bool mlxbf_tmfifo_virtio_notify(struct virtqueue *vq)
8908c2ecf20Sopenharmony_ci{
8918c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vring *vring = vq->priv;
8928c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vdev *tm_vdev;
8938c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo *fifo;
8948c2ecf20Sopenharmony_ci	unsigned long flags;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	fifo = vring->fifo;
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	/*
8998c2ecf20Sopenharmony_ci	 * Virtio maintains vrings in pairs, even number ring for Rx
9008c2ecf20Sopenharmony_ci	 * and odd number ring for Tx.
9018c2ecf20Sopenharmony_ci	 */
9028c2ecf20Sopenharmony_ci	if (vring->index & BIT(0)) {
9038c2ecf20Sopenharmony_ci		/*
9048c2ecf20Sopenharmony_ci		 * Console could make blocking call with interrupts disabled.
9058c2ecf20Sopenharmony_ci		 * In such case, the vring needs to be served right away. For
9068c2ecf20Sopenharmony_ci		 * other cases, just set the TX LWM bit to start Tx in the
9078c2ecf20Sopenharmony_ci		 * worker handler.
9088c2ecf20Sopenharmony_ci		 */
9098c2ecf20Sopenharmony_ci		if (vring->vdev_id == VIRTIO_ID_CONSOLE) {
9108c2ecf20Sopenharmony_ci			spin_lock_irqsave(&fifo->spin_lock[0], flags);
9118c2ecf20Sopenharmony_ci			tm_vdev = fifo->vdev[VIRTIO_ID_CONSOLE];
9128c2ecf20Sopenharmony_ci			mlxbf_tmfifo_console_output(tm_vdev, vring);
9138c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&fifo->spin_lock[0], flags);
9148c2ecf20Sopenharmony_ci			set_bit(MLXBF_TM_TX_LWM_IRQ, &fifo->pend_events);
9158c2ecf20Sopenharmony_ci		} else if (test_and_set_bit(MLXBF_TM_TX_LWM_IRQ,
9168c2ecf20Sopenharmony_ci					    &fifo->pend_events)) {
9178c2ecf20Sopenharmony_ci			return true;
9188c2ecf20Sopenharmony_ci		}
9198c2ecf20Sopenharmony_ci	} else {
9208c2ecf20Sopenharmony_ci		if (test_and_set_bit(MLXBF_TM_RX_HWM_IRQ, &fifo->pend_events))
9218c2ecf20Sopenharmony_ci			return true;
9228c2ecf20Sopenharmony_ci	}
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	schedule_work(&fifo->work);
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	return true;
9278c2ecf20Sopenharmony_ci}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci/* Get the array of feature bits for this device. */
9308c2ecf20Sopenharmony_cistatic u64 mlxbf_tmfifo_virtio_get_features(struct virtio_device *vdev)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev);
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	return tm_vdev->features;
9358c2ecf20Sopenharmony_ci}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci/* Confirm device features to use. */
9388c2ecf20Sopenharmony_cistatic int mlxbf_tmfifo_virtio_finalize_features(struct virtio_device *vdev)
9398c2ecf20Sopenharmony_ci{
9408c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev);
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	tm_vdev->features = vdev->features;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	return 0;
9458c2ecf20Sopenharmony_ci}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci/* Free virtqueues found by find_vqs(). */
9488c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_virtio_del_vqs(struct virtio_device *vdev)
9498c2ecf20Sopenharmony_ci{
9508c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev);
9518c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vring *vring;
9528c2ecf20Sopenharmony_ci	struct virtqueue *vq;
9538c2ecf20Sopenharmony_ci	int i;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tm_vdev->vrings); i++) {
9568c2ecf20Sopenharmony_ci		vring = &tm_vdev->vrings[i];
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci		/* Release the pending packet. */
9598c2ecf20Sopenharmony_ci		if (vring->desc)
9608c2ecf20Sopenharmony_ci			mlxbf_tmfifo_release_pkt(vring);
9618c2ecf20Sopenharmony_ci		vq = vring->vq;
9628c2ecf20Sopenharmony_ci		if (vq) {
9638c2ecf20Sopenharmony_ci			vring->vq = NULL;
9648c2ecf20Sopenharmony_ci			vring_del_virtqueue(vq);
9658c2ecf20Sopenharmony_ci		}
9668c2ecf20Sopenharmony_ci	}
9678c2ecf20Sopenharmony_ci}
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci/* Create and initialize the virtual queues. */
9708c2ecf20Sopenharmony_cistatic int mlxbf_tmfifo_virtio_find_vqs(struct virtio_device *vdev,
9718c2ecf20Sopenharmony_ci					unsigned int nvqs,
9728c2ecf20Sopenharmony_ci					struct virtqueue *vqs[],
9738c2ecf20Sopenharmony_ci					vq_callback_t *callbacks[],
9748c2ecf20Sopenharmony_ci					const char * const names[],
9758c2ecf20Sopenharmony_ci					const bool *ctx,
9768c2ecf20Sopenharmony_ci					struct irq_affinity *desc)
9778c2ecf20Sopenharmony_ci{
9788c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev);
9798c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vring *vring;
9808c2ecf20Sopenharmony_ci	struct virtqueue *vq;
9818c2ecf20Sopenharmony_ci	int i, ret, size;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	if (nvqs > ARRAY_SIZE(tm_vdev->vrings))
9848c2ecf20Sopenharmony_ci		return -EINVAL;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	for (i = 0; i < nvqs; ++i) {
9878c2ecf20Sopenharmony_ci		if (!names[i]) {
9888c2ecf20Sopenharmony_ci			ret = -EINVAL;
9898c2ecf20Sopenharmony_ci			goto error;
9908c2ecf20Sopenharmony_ci		}
9918c2ecf20Sopenharmony_ci		vring = &tm_vdev->vrings[i];
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci		/* zero vring */
9948c2ecf20Sopenharmony_ci		size = vring_size(vring->num, vring->align);
9958c2ecf20Sopenharmony_ci		memset(vring->va, 0, size);
9968c2ecf20Sopenharmony_ci		vq = vring_new_virtqueue(i, vring->num, vring->align, vdev,
9978c2ecf20Sopenharmony_ci					 false, false, vring->va,
9988c2ecf20Sopenharmony_ci					 mlxbf_tmfifo_virtio_notify,
9998c2ecf20Sopenharmony_ci					 callbacks[i], names[i]);
10008c2ecf20Sopenharmony_ci		if (!vq) {
10018c2ecf20Sopenharmony_ci			dev_err(&vdev->dev, "vring_new_virtqueue failed\n");
10028c2ecf20Sopenharmony_ci			ret = -ENOMEM;
10038c2ecf20Sopenharmony_ci			goto error;
10048c2ecf20Sopenharmony_ci		}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci		vqs[i] = vq;
10078c2ecf20Sopenharmony_ci		vring->vq = vq;
10088c2ecf20Sopenharmony_ci		vq->priv = vring;
10098c2ecf20Sopenharmony_ci	}
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	return 0;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_cierror:
10148c2ecf20Sopenharmony_ci	mlxbf_tmfifo_virtio_del_vqs(vdev);
10158c2ecf20Sopenharmony_ci	return ret;
10168c2ecf20Sopenharmony_ci}
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci/* Read the status byte. */
10198c2ecf20Sopenharmony_cistatic u8 mlxbf_tmfifo_virtio_get_status(struct virtio_device *vdev)
10208c2ecf20Sopenharmony_ci{
10218c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev);
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	return tm_vdev->status;
10248c2ecf20Sopenharmony_ci}
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci/* Write the status byte. */
10278c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_virtio_set_status(struct virtio_device *vdev,
10288c2ecf20Sopenharmony_ci					   u8 status)
10298c2ecf20Sopenharmony_ci{
10308c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev);
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	tm_vdev->status = status;
10338c2ecf20Sopenharmony_ci}
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci/* Reset the device. Not much here for now. */
10368c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_virtio_reset(struct virtio_device *vdev)
10378c2ecf20Sopenharmony_ci{
10388c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev);
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	tm_vdev->status = 0;
10418c2ecf20Sopenharmony_ci}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci/* Read the value of a configuration field. */
10448c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_virtio_get(struct virtio_device *vdev,
10458c2ecf20Sopenharmony_ci				    unsigned int offset,
10468c2ecf20Sopenharmony_ci				    void *buf,
10478c2ecf20Sopenharmony_ci				    unsigned int len)
10488c2ecf20Sopenharmony_ci{
10498c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev);
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	if ((u64)offset + len > sizeof(tm_vdev->config))
10528c2ecf20Sopenharmony_ci		return;
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	memcpy(buf, (u8 *)&tm_vdev->config + offset, len);
10558c2ecf20Sopenharmony_ci}
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci/* Write the value of a configuration field. */
10588c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_virtio_set(struct virtio_device *vdev,
10598c2ecf20Sopenharmony_ci				    unsigned int offset,
10608c2ecf20Sopenharmony_ci				    const void *buf,
10618c2ecf20Sopenharmony_ci				    unsigned int len)
10628c2ecf20Sopenharmony_ci{
10638c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev);
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	if ((u64)offset + len > sizeof(tm_vdev->config))
10668c2ecf20Sopenharmony_ci		return;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	memcpy((u8 *)&tm_vdev->config + offset, buf, len);
10698c2ecf20Sopenharmony_ci}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_cistatic void tmfifo_virtio_dev_release(struct device *device)
10728c2ecf20Sopenharmony_ci{
10738c2ecf20Sopenharmony_ci	struct virtio_device *vdev =
10748c2ecf20Sopenharmony_ci			container_of(device, struct virtio_device, dev);
10758c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	kfree(tm_vdev);
10788c2ecf20Sopenharmony_ci}
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci/* Virtio config operations. */
10818c2ecf20Sopenharmony_cistatic const struct virtio_config_ops mlxbf_tmfifo_virtio_config_ops = {
10828c2ecf20Sopenharmony_ci	.get_features = mlxbf_tmfifo_virtio_get_features,
10838c2ecf20Sopenharmony_ci	.finalize_features = mlxbf_tmfifo_virtio_finalize_features,
10848c2ecf20Sopenharmony_ci	.find_vqs = mlxbf_tmfifo_virtio_find_vqs,
10858c2ecf20Sopenharmony_ci	.del_vqs = mlxbf_tmfifo_virtio_del_vqs,
10868c2ecf20Sopenharmony_ci	.reset = mlxbf_tmfifo_virtio_reset,
10878c2ecf20Sopenharmony_ci	.set_status = mlxbf_tmfifo_virtio_set_status,
10888c2ecf20Sopenharmony_ci	.get_status = mlxbf_tmfifo_virtio_get_status,
10898c2ecf20Sopenharmony_ci	.get = mlxbf_tmfifo_virtio_get,
10908c2ecf20Sopenharmony_ci	.set = mlxbf_tmfifo_virtio_set,
10918c2ecf20Sopenharmony_ci};
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci/* Create vdev for the FIFO. */
10948c2ecf20Sopenharmony_cistatic int mlxbf_tmfifo_create_vdev(struct device *dev,
10958c2ecf20Sopenharmony_ci				    struct mlxbf_tmfifo *fifo,
10968c2ecf20Sopenharmony_ci				    int vdev_id, u64 features,
10978c2ecf20Sopenharmony_ci				    void *config, u32 size)
10988c2ecf20Sopenharmony_ci{
10998c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vdev *tm_vdev, *reg_dev = NULL;
11008c2ecf20Sopenharmony_ci	int ret;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	mutex_lock(&fifo->lock);
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	tm_vdev = fifo->vdev[vdev_id];
11058c2ecf20Sopenharmony_ci	if (tm_vdev) {
11068c2ecf20Sopenharmony_ci		dev_err(dev, "vdev %d already exists\n", vdev_id);
11078c2ecf20Sopenharmony_ci		ret = -EEXIST;
11088c2ecf20Sopenharmony_ci		goto fail;
11098c2ecf20Sopenharmony_ci	}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	tm_vdev = kzalloc(sizeof(*tm_vdev), GFP_KERNEL);
11128c2ecf20Sopenharmony_ci	if (!tm_vdev) {
11138c2ecf20Sopenharmony_ci		ret = -ENOMEM;
11148c2ecf20Sopenharmony_ci		goto fail;
11158c2ecf20Sopenharmony_ci	}
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	tm_vdev->vdev.id.device = vdev_id;
11188c2ecf20Sopenharmony_ci	tm_vdev->vdev.config = &mlxbf_tmfifo_virtio_config_ops;
11198c2ecf20Sopenharmony_ci	tm_vdev->vdev.dev.parent = dev;
11208c2ecf20Sopenharmony_ci	tm_vdev->vdev.dev.release = tmfifo_virtio_dev_release;
11218c2ecf20Sopenharmony_ci	tm_vdev->features = features;
11228c2ecf20Sopenharmony_ci	if (config)
11238c2ecf20Sopenharmony_ci		memcpy(&tm_vdev->config, config, size);
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	if (mlxbf_tmfifo_alloc_vrings(fifo, tm_vdev)) {
11268c2ecf20Sopenharmony_ci		dev_err(dev, "unable to allocate vring\n");
11278c2ecf20Sopenharmony_ci		ret = -ENOMEM;
11288c2ecf20Sopenharmony_ci		goto vdev_fail;
11298c2ecf20Sopenharmony_ci	}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	/* Allocate an output buffer for the console device. */
11328c2ecf20Sopenharmony_ci	if (vdev_id == VIRTIO_ID_CONSOLE)
11338c2ecf20Sopenharmony_ci		tm_vdev->tx_buf.buf = devm_kmalloc(dev,
11348c2ecf20Sopenharmony_ci						   MLXBF_TMFIFO_CON_TX_BUF_SIZE,
11358c2ecf20Sopenharmony_ci						   GFP_KERNEL);
11368c2ecf20Sopenharmony_ci	fifo->vdev[vdev_id] = tm_vdev;
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	/* Register the virtio device. */
11398c2ecf20Sopenharmony_ci	ret = register_virtio_device(&tm_vdev->vdev);
11408c2ecf20Sopenharmony_ci	reg_dev = tm_vdev;
11418c2ecf20Sopenharmony_ci	if (ret) {
11428c2ecf20Sopenharmony_ci		dev_err(dev, "register_virtio_device failed\n");
11438c2ecf20Sopenharmony_ci		goto vdev_fail;
11448c2ecf20Sopenharmony_ci	}
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	mutex_unlock(&fifo->lock);
11478c2ecf20Sopenharmony_ci	return 0;
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_civdev_fail:
11508c2ecf20Sopenharmony_ci	mlxbf_tmfifo_free_vrings(fifo, tm_vdev);
11518c2ecf20Sopenharmony_ci	fifo->vdev[vdev_id] = NULL;
11528c2ecf20Sopenharmony_ci	if (reg_dev)
11538c2ecf20Sopenharmony_ci		put_device(&tm_vdev->vdev.dev);
11548c2ecf20Sopenharmony_ci	else
11558c2ecf20Sopenharmony_ci		kfree(tm_vdev);
11568c2ecf20Sopenharmony_cifail:
11578c2ecf20Sopenharmony_ci	mutex_unlock(&fifo->lock);
11588c2ecf20Sopenharmony_ci	return ret;
11598c2ecf20Sopenharmony_ci}
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci/* Delete vdev for the FIFO. */
11628c2ecf20Sopenharmony_cistatic int mlxbf_tmfifo_delete_vdev(struct mlxbf_tmfifo *fifo, int vdev_id)
11638c2ecf20Sopenharmony_ci{
11648c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo_vdev *tm_vdev;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	mutex_lock(&fifo->lock);
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	/* Unregister vdev. */
11698c2ecf20Sopenharmony_ci	tm_vdev = fifo->vdev[vdev_id];
11708c2ecf20Sopenharmony_ci	if (tm_vdev) {
11718c2ecf20Sopenharmony_ci		unregister_virtio_device(&tm_vdev->vdev);
11728c2ecf20Sopenharmony_ci		mlxbf_tmfifo_free_vrings(fifo, tm_vdev);
11738c2ecf20Sopenharmony_ci		fifo->vdev[vdev_id] = NULL;
11748c2ecf20Sopenharmony_ci	}
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	mutex_unlock(&fifo->lock);
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	return 0;
11798c2ecf20Sopenharmony_ci}
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci/* Read the configured network MAC address from efi variable. */
11828c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_get_cfg_mac(u8 *mac)
11838c2ecf20Sopenharmony_ci{
11848c2ecf20Sopenharmony_ci	efi_guid_t guid = EFI_GLOBAL_VARIABLE_GUID;
11858c2ecf20Sopenharmony_ci	unsigned long size = ETH_ALEN;
11868c2ecf20Sopenharmony_ci	u8 buf[ETH_ALEN];
11878c2ecf20Sopenharmony_ci	efi_status_t rc;
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	rc = efi.get_variable(mlxbf_tmfifo_efi_name, &guid, NULL, &size, buf);
11908c2ecf20Sopenharmony_ci	if (rc == EFI_SUCCESS && size == ETH_ALEN)
11918c2ecf20Sopenharmony_ci		ether_addr_copy(mac, buf);
11928c2ecf20Sopenharmony_ci	else
11938c2ecf20Sopenharmony_ci		ether_addr_copy(mac, mlxbf_tmfifo_net_default_mac);
11948c2ecf20Sopenharmony_ci}
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci/* Set TmFifo thresolds which is used to trigger interrupts. */
11978c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_set_threshold(struct mlxbf_tmfifo *fifo)
11988c2ecf20Sopenharmony_ci{
11998c2ecf20Sopenharmony_ci	u64 ctl;
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	/* Get Tx FIFO size and set the low/high watermark. */
12028c2ecf20Sopenharmony_ci	ctl = readq(fifo->tx_base + MLXBF_TMFIFO_TX_CTL);
12038c2ecf20Sopenharmony_ci	fifo->tx_fifo_size =
12048c2ecf20Sopenharmony_ci		FIELD_GET(MLXBF_TMFIFO_TX_CTL__MAX_ENTRIES_MASK, ctl);
12058c2ecf20Sopenharmony_ci	ctl = (ctl & ~MLXBF_TMFIFO_TX_CTL__LWM_MASK) |
12068c2ecf20Sopenharmony_ci		FIELD_PREP(MLXBF_TMFIFO_TX_CTL__LWM_MASK,
12078c2ecf20Sopenharmony_ci			   fifo->tx_fifo_size / 2);
12088c2ecf20Sopenharmony_ci	ctl = (ctl & ~MLXBF_TMFIFO_TX_CTL__HWM_MASK) |
12098c2ecf20Sopenharmony_ci		FIELD_PREP(MLXBF_TMFIFO_TX_CTL__HWM_MASK,
12108c2ecf20Sopenharmony_ci			   fifo->tx_fifo_size - 1);
12118c2ecf20Sopenharmony_ci	writeq(ctl, fifo->tx_base + MLXBF_TMFIFO_TX_CTL);
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	/* Get Rx FIFO size and set the low/high watermark. */
12148c2ecf20Sopenharmony_ci	ctl = readq(fifo->rx_base + MLXBF_TMFIFO_RX_CTL);
12158c2ecf20Sopenharmony_ci	fifo->rx_fifo_size =
12168c2ecf20Sopenharmony_ci		FIELD_GET(MLXBF_TMFIFO_RX_CTL__MAX_ENTRIES_MASK, ctl);
12178c2ecf20Sopenharmony_ci	ctl = (ctl & ~MLXBF_TMFIFO_RX_CTL__LWM_MASK) |
12188c2ecf20Sopenharmony_ci		FIELD_PREP(MLXBF_TMFIFO_RX_CTL__LWM_MASK, 0);
12198c2ecf20Sopenharmony_ci	ctl = (ctl & ~MLXBF_TMFIFO_RX_CTL__HWM_MASK) |
12208c2ecf20Sopenharmony_ci		FIELD_PREP(MLXBF_TMFIFO_RX_CTL__HWM_MASK, 1);
12218c2ecf20Sopenharmony_ci	writeq(ctl, fifo->rx_base + MLXBF_TMFIFO_RX_CTL);
12228c2ecf20Sopenharmony_ci}
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_cistatic void mlxbf_tmfifo_cleanup(struct mlxbf_tmfifo *fifo)
12258c2ecf20Sopenharmony_ci{
12268c2ecf20Sopenharmony_ci	int i;
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	fifo->is_ready = false;
12298c2ecf20Sopenharmony_ci	del_timer_sync(&fifo->timer);
12308c2ecf20Sopenharmony_ci	mlxbf_tmfifo_disable_irqs(fifo);
12318c2ecf20Sopenharmony_ci	cancel_work_sync(&fifo->work);
12328c2ecf20Sopenharmony_ci	for (i = 0; i < MLXBF_TMFIFO_VDEV_MAX; i++)
12338c2ecf20Sopenharmony_ci		mlxbf_tmfifo_delete_vdev(fifo, i);
12348c2ecf20Sopenharmony_ci}
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci/* Probe the TMFIFO. */
12378c2ecf20Sopenharmony_cistatic int mlxbf_tmfifo_probe(struct platform_device *pdev)
12388c2ecf20Sopenharmony_ci{
12398c2ecf20Sopenharmony_ci	struct virtio_net_config net_config;
12408c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
12418c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo *fifo;
12428c2ecf20Sopenharmony_ci	int i, rc;
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	fifo = devm_kzalloc(dev, sizeof(*fifo), GFP_KERNEL);
12458c2ecf20Sopenharmony_ci	if (!fifo)
12468c2ecf20Sopenharmony_ci		return -ENOMEM;
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	spin_lock_init(&fifo->spin_lock[0]);
12498c2ecf20Sopenharmony_ci	spin_lock_init(&fifo->spin_lock[1]);
12508c2ecf20Sopenharmony_ci	INIT_WORK(&fifo->work, mlxbf_tmfifo_work_handler);
12518c2ecf20Sopenharmony_ci	mutex_init(&fifo->lock);
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	/* Get the resource of the Rx FIFO. */
12548c2ecf20Sopenharmony_ci	fifo->rx_base = devm_platform_ioremap_resource(pdev, 0);
12558c2ecf20Sopenharmony_ci	if (IS_ERR(fifo->rx_base))
12568c2ecf20Sopenharmony_ci		return PTR_ERR(fifo->rx_base);
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	/* Get the resource of the Tx FIFO. */
12598c2ecf20Sopenharmony_ci	fifo->tx_base = devm_platform_ioremap_resource(pdev, 1);
12608c2ecf20Sopenharmony_ci	if (IS_ERR(fifo->tx_base))
12618c2ecf20Sopenharmony_ci		return PTR_ERR(fifo->tx_base);
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, fifo);
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	timer_setup(&fifo->timer, mlxbf_tmfifo_timer, 0);
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	for (i = 0; i < MLXBF_TM_MAX_IRQ; i++) {
12688c2ecf20Sopenharmony_ci		fifo->irq_info[i].index = i;
12698c2ecf20Sopenharmony_ci		fifo->irq_info[i].fifo = fifo;
12708c2ecf20Sopenharmony_ci		fifo->irq_info[i].irq = platform_get_irq(pdev, i);
12718c2ecf20Sopenharmony_ci		rc = devm_request_irq(dev, fifo->irq_info[i].irq,
12728c2ecf20Sopenharmony_ci				      mlxbf_tmfifo_irq_handler, 0,
12738c2ecf20Sopenharmony_ci				      "tmfifo", &fifo->irq_info[i]);
12748c2ecf20Sopenharmony_ci		if (rc) {
12758c2ecf20Sopenharmony_ci			dev_err(dev, "devm_request_irq failed\n");
12768c2ecf20Sopenharmony_ci			fifo->irq_info[i].irq = 0;
12778c2ecf20Sopenharmony_ci			return rc;
12788c2ecf20Sopenharmony_ci		}
12798c2ecf20Sopenharmony_ci	}
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	mlxbf_tmfifo_set_threshold(fifo);
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	/* Create the console vdev. */
12848c2ecf20Sopenharmony_ci	rc = mlxbf_tmfifo_create_vdev(dev, fifo, VIRTIO_ID_CONSOLE, 0, NULL, 0);
12858c2ecf20Sopenharmony_ci	if (rc)
12868c2ecf20Sopenharmony_ci		goto fail;
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci	/* Create the network vdev. */
12898c2ecf20Sopenharmony_ci	memset(&net_config, 0, sizeof(net_config));
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	/* A legacy-only interface for now. */
12928c2ecf20Sopenharmony_ci	net_config.mtu = __cpu_to_virtio16(virtio_legacy_is_little_endian(),
12938c2ecf20Sopenharmony_ci					   ETH_DATA_LEN);
12948c2ecf20Sopenharmony_ci	net_config.status = __cpu_to_virtio16(virtio_legacy_is_little_endian(),
12958c2ecf20Sopenharmony_ci					      VIRTIO_NET_S_LINK_UP);
12968c2ecf20Sopenharmony_ci	mlxbf_tmfifo_get_cfg_mac(net_config.mac);
12978c2ecf20Sopenharmony_ci	rc = mlxbf_tmfifo_create_vdev(dev, fifo, VIRTIO_ID_NET,
12988c2ecf20Sopenharmony_ci				      MLXBF_TMFIFO_NET_FEATURES, &net_config,
12998c2ecf20Sopenharmony_ci				      sizeof(net_config));
13008c2ecf20Sopenharmony_ci	if (rc)
13018c2ecf20Sopenharmony_ci		goto fail;
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	mod_timer(&fifo->timer, jiffies + MLXBF_TMFIFO_TIMER_INTERVAL);
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	fifo->is_ready = true;
13068c2ecf20Sopenharmony_ci	return 0;
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_cifail:
13098c2ecf20Sopenharmony_ci	mlxbf_tmfifo_cleanup(fifo);
13108c2ecf20Sopenharmony_ci	return rc;
13118c2ecf20Sopenharmony_ci}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci/* Device remove function. */
13148c2ecf20Sopenharmony_cistatic int mlxbf_tmfifo_remove(struct platform_device *pdev)
13158c2ecf20Sopenharmony_ci{
13168c2ecf20Sopenharmony_ci	struct mlxbf_tmfifo *fifo = platform_get_drvdata(pdev);
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	mlxbf_tmfifo_cleanup(fifo);
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	return 0;
13218c2ecf20Sopenharmony_ci}
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_cistatic const struct acpi_device_id mlxbf_tmfifo_acpi_match[] = {
13248c2ecf20Sopenharmony_ci	{ "MLNXBF01", 0 },
13258c2ecf20Sopenharmony_ci	{}
13268c2ecf20Sopenharmony_ci};
13278c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, mlxbf_tmfifo_acpi_match);
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_cistatic struct platform_driver mlxbf_tmfifo_driver = {
13308c2ecf20Sopenharmony_ci	.probe = mlxbf_tmfifo_probe,
13318c2ecf20Sopenharmony_ci	.remove = mlxbf_tmfifo_remove,
13328c2ecf20Sopenharmony_ci	.driver = {
13338c2ecf20Sopenharmony_ci		.name = "bf-tmfifo",
13348c2ecf20Sopenharmony_ci		.acpi_match_table = mlxbf_tmfifo_acpi_match,
13358c2ecf20Sopenharmony_ci	},
13368c2ecf20Sopenharmony_ci};
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_cimodule_platform_driver(mlxbf_tmfifo_driver);
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Mellanox BlueField SoC TmFifo Driver");
13418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
13428c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mellanox Technologies");
1343