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