11357dfd7SLiming Sun // SPDX-License-Identifier: GPL-2.0+ 21357dfd7SLiming Sun /* 31357dfd7SLiming Sun * Mellanox BlueField SoC TmFifo driver 41357dfd7SLiming Sun * 51357dfd7SLiming Sun * Copyright (C) 2019 Mellanox Technologies 61357dfd7SLiming Sun */ 71357dfd7SLiming Sun 81357dfd7SLiming Sun #include <linux/acpi.h> 91357dfd7SLiming Sun #include <linux/bitfield.h> 101357dfd7SLiming Sun #include <linux/circ_buf.h> 111357dfd7SLiming Sun #include <linux/efi.h> 121357dfd7SLiming Sun #include <linux/irq.h> 131357dfd7SLiming Sun #include <linux/module.h> 141357dfd7SLiming Sun #include <linux/mutex.h> 151357dfd7SLiming Sun #include <linux/platform_device.h> 161357dfd7SLiming Sun #include <linux/types.h> 171357dfd7SLiming Sun 181357dfd7SLiming Sun #include <linux/virtio_config.h> 191357dfd7SLiming Sun #include <linux/virtio_console.h> 201357dfd7SLiming Sun #include <linux/virtio_ids.h> 211357dfd7SLiming Sun #include <linux/virtio_net.h> 221357dfd7SLiming Sun #include <linux/virtio_ring.h> 231357dfd7SLiming Sun 241357dfd7SLiming Sun #include "mlxbf-tmfifo-regs.h" 251357dfd7SLiming Sun 261357dfd7SLiming Sun /* Vring size. */ 271357dfd7SLiming Sun #define MLXBF_TMFIFO_VRING_SIZE SZ_1K 281357dfd7SLiming Sun 291357dfd7SLiming Sun /* Console Tx buffer size. */ 301357dfd7SLiming Sun #define MLXBF_TMFIFO_CON_TX_BUF_SIZE SZ_32K 311357dfd7SLiming Sun 321357dfd7SLiming Sun /* Console Tx buffer reserved space. */ 331357dfd7SLiming Sun #define MLXBF_TMFIFO_CON_TX_BUF_RSV_SIZE 8 341357dfd7SLiming Sun 351357dfd7SLiming Sun /* House-keeping timer interval. */ 361357dfd7SLiming Sun #define MLXBF_TMFIFO_TIMER_INTERVAL (HZ / 10) 371357dfd7SLiming Sun 381357dfd7SLiming Sun /* Virtual devices sharing the TM FIFO. */ 391357dfd7SLiming Sun #define MLXBF_TMFIFO_VDEV_MAX (VIRTIO_ID_CONSOLE + 1) 401357dfd7SLiming Sun 411357dfd7SLiming Sun /* 421357dfd7SLiming Sun * Reserve 1/16 of TmFifo space, so console messages are not starved by 431357dfd7SLiming Sun * the networking traffic. 441357dfd7SLiming Sun */ 451357dfd7SLiming Sun #define MLXBF_TMFIFO_RESERVE_RATIO 16 461357dfd7SLiming Sun 471357dfd7SLiming Sun /* Message with data needs at least two words (for header & data). */ 481357dfd7SLiming Sun #define MLXBF_TMFIFO_DATA_MIN_WORDS 2 491357dfd7SLiming Sun 501357dfd7SLiming Sun struct mlxbf_tmfifo; 511357dfd7SLiming Sun 521357dfd7SLiming Sun /** 531357dfd7SLiming Sun * mlxbf_tmfifo_vring - Structure of the TmFifo virtual ring 541357dfd7SLiming Sun * @va: virtual address of the ring 551357dfd7SLiming Sun * @dma: dma address of the ring 561357dfd7SLiming Sun * @vq: pointer to the virtio virtqueue 571357dfd7SLiming Sun * @desc: current descriptor of the pending packet 581357dfd7SLiming Sun * @desc_head: head descriptor of the pending packet 591357dfd7SLiming Sun * @cur_len: processed length of the current descriptor 601357dfd7SLiming Sun * @rem_len: remaining length of the pending packet 611357dfd7SLiming Sun * @pkt_len: total length of the pending packet 621357dfd7SLiming Sun * @next_avail: next avail descriptor id 631357dfd7SLiming Sun * @num: vring size (number of descriptors) 641357dfd7SLiming Sun * @align: vring alignment size 651357dfd7SLiming Sun * @index: vring index 661357dfd7SLiming Sun * @vdev_id: vring virtio id (VIRTIO_ID_xxx) 671357dfd7SLiming Sun * @fifo: pointer to the tmfifo structure 681357dfd7SLiming Sun */ 691357dfd7SLiming Sun struct mlxbf_tmfifo_vring { 701357dfd7SLiming Sun void *va; 711357dfd7SLiming Sun dma_addr_t dma; 721357dfd7SLiming Sun struct virtqueue *vq; 731357dfd7SLiming Sun struct vring_desc *desc; 741357dfd7SLiming Sun struct vring_desc *desc_head; 751357dfd7SLiming Sun int cur_len; 761357dfd7SLiming Sun int rem_len; 771357dfd7SLiming Sun u32 pkt_len; 781357dfd7SLiming Sun u16 next_avail; 791357dfd7SLiming Sun int num; 801357dfd7SLiming Sun int align; 811357dfd7SLiming Sun int index; 821357dfd7SLiming Sun int vdev_id; 831357dfd7SLiming Sun struct mlxbf_tmfifo *fifo; 841357dfd7SLiming Sun }; 851357dfd7SLiming Sun 861357dfd7SLiming Sun /* Interrupt types. */ 871357dfd7SLiming Sun enum { 881357dfd7SLiming Sun MLXBF_TM_RX_LWM_IRQ, 891357dfd7SLiming Sun MLXBF_TM_RX_HWM_IRQ, 901357dfd7SLiming Sun MLXBF_TM_TX_LWM_IRQ, 911357dfd7SLiming Sun MLXBF_TM_TX_HWM_IRQ, 921357dfd7SLiming Sun MLXBF_TM_MAX_IRQ 931357dfd7SLiming Sun }; 941357dfd7SLiming Sun 951357dfd7SLiming Sun /* Ring types (Rx & Tx). */ 961357dfd7SLiming Sun enum { 971357dfd7SLiming Sun MLXBF_TMFIFO_VRING_RX, 981357dfd7SLiming Sun MLXBF_TMFIFO_VRING_TX, 991357dfd7SLiming Sun MLXBF_TMFIFO_VRING_MAX 1001357dfd7SLiming Sun }; 1011357dfd7SLiming Sun 1021357dfd7SLiming Sun /** 1031357dfd7SLiming Sun * mlxbf_tmfifo_vdev - Structure of the TmFifo virtual device 1041357dfd7SLiming Sun * @vdev: virtio device, in which the vdev.id.device field has the 1051357dfd7SLiming Sun * VIRTIO_ID_xxx id to distinguish the virtual device. 1061357dfd7SLiming Sun * @status: status of the device 1071357dfd7SLiming Sun * @features: supported features of the device 1081357dfd7SLiming Sun * @vrings: array of tmfifo vrings of this device 1091357dfd7SLiming Sun * @config.cons: virtual console config - 1101357dfd7SLiming Sun * select if vdev.id.device is VIRTIO_ID_CONSOLE 1111357dfd7SLiming Sun * @config.net: virtual network config - 1121357dfd7SLiming Sun * select if vdev.id.device is VIRTIO_ID_NET 1131357dfd7SLiming Sun * @tx_buf: tx buffer used to buffer data before writing into the FIFO 1141357dfd7SLiming Sun */ 1151357dfd7SLiming Sun struct mlxbf_tmfifo_vdev { 1161357dfd7SLiming Sun struct virtio_device vdev; 1171357dfd7SLiming Sun u8 status; 1181357dfd7SLiming Sun u64 features; 1191357dfd7SLiming Sun struct mlxbf_tmfifo_vring vrings[MLXBF_TMFIFO_VRING_MAX]; 1201357dfd7SLiming Sun union { 1211357dfd7SLiming Sun struct virtio_console_config cons; 1221357dfd7SLiming Sun struct virtio_net_config net; 1231357dfd7SLiming Sun } config; 1241357dfd7SLiming Sun struct circ_buf tx_buf; 1251357dfd7SLiming Sun }; 1261357dfd7SLiming Sun 1271357dfd7SLiming Sun /** 1281357dfd7SLiming Sun * mlxbf_tmfifo_irq_info - Structure of the interrupt information 1291357dfd7SLiming Sun * @fifo: pointer to the tmfifo structure 1301357dfd7SLiming Sun * @irq: interrupt number 1311357dfd7SLiming Sun * @index: index into the interrupt array 1321357dfd7SLiming Sun */ 1331357dfd7SLiming Sun struct mlxbf_tmfifo_irq_info { 1341357dfd7SLiming Sun struct mlxbf_tmfifo *fifo; 1351357dfd7SLiming Sun int irq; 1361357dfd7SLiming Sun int index; 1371357dfd7SLiming Sun }; 1381357dfd7SLiming Sun 1391357dfd7SLiming Sun /** 1401357dfd7SLiming Sun * mlxbf_tmfifo - Structure of the TmFifo 1411357dfd7SLiming Sun * @vdev: array of the virtual devices running over the TmFifo 1421357dfd7SLiming Sun * @lock: lock to protect the TmFifo access 1431357dfd7SLiming Sun * @rx_base: mapped register base address for the Rx FIFO 1441357dfd7SLiming Sun * @tx_base: mapped register base address for the Tx FIFO 1451357dfd7SLiming Sun * @rx_fifo_size: number of entries of the Rx FIFO 1461357dfd7SLiming Sun * @tx_fifo_size: number of entries of the Tx FIFO 1471357dfd7SLiming Sun * @pend_events: pending bits for deferred events 1481357dfd7SLiming Sun * @irq_info: interrupt information 1491357dfd7SLiming Sun * @work: work struct for deferred process 1501357dfd7SLiming Sun * @timer: background timer 1511357dfd7SLiming Sun * @vring: Tx/Rx ring 152*638bc4caSLiming Sun * @spin_lock: Tx/Rx spin lock 1531357dfd7SLiming Sun * @is_ready: ready flag 1541357dfd7SLiming Sun */ 1551357dfd7SLiming Sun struct mlxbf_tmfifo { 1561357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *vdev[MLXBF_TMFIFO_VDEV_MAX]; 1571357dfd7SLiming Sun struct mutex lock; /* TmFifo lock */ 1581357dfd7SLiming Sun void __iomem *rx_base; 1591357dfd7SLiming Sun void __iomem *tx_base; 1601357dfd7SLiming Sun int rx_fifo_size; 1611357dfd7SLiming Sun int tx_fifo_size; 1621357dfd7SLiming Sun unsigned long pend_events; 1631357dfd7SLiming Sun struct mlxbf_tmfifo_irq_info irq_info[MLXBF_TM_MAX_IRQ]; 1641357dfd7SLiming Sun struct work_struct work; 1651357dfd7SLiming Sun struct timer_list timer; 1661357dfd7SLiming Sun struct mlxbf_tmfifo_vring *vring[2]; 167*638bc4caSLiming Sun spinlock_t spin_lock[2]; /* spin lock */ 1681357dfd7SLiming Sun bool is_ready; 1691357dfd7SLiming Sun }; 1701357dfd7SLiming Sun 1711357dfd7SLiming Sun /** 1721357dfd7SLiming Sun * mlxbf_tmfifo_msg_hdr - Structure of the TmFifo message header 1731357dfd7SLiming Sun * @type: message type 1741357dfd7SLiming Sun * @len: payload length in network byte order. Messages sent into the FIFO 1751357dfd7SLiming Sun * will be read by the other side as data stream in the same byte order. 1761357dfd7SLiming Sun * The length needs to be encoded into network order so both sides 1771357dfd7SLiming Sun * could understand it. 1781357dfd7SLiming Sun */ 1791357dfd7SLiming Sun struct mlxbf_tmfifo_msg_hdr { 1801357dfd7SLiming Sun u8 type; 1811357dfd7SLiming Sun __be16 len; 1821357dfd7SLiming Sun u8 unused[5]; 1831357dfd7SLiming Sun } __packed __aligned(sizeof(u64)); 1841357dfd7SLiming Sun 1851357dfd7SLiming Sun /* 1861357dfd7SLiming Sun * Default MAC. 1871357dfd7SLiming Sun * This MAC address will be read from EFI persistent variable if configured. 1881357dfd7SLiming Sun * It can also be reconfigured with standard Linux tools. 1891357dfd7SLiming Sun */ 1901357dfd7SLiming Sun static u8 mlxbf_tmfifo_net_default_mac[ETH_ALEN] = { 1911357dfd7SLiming Sun 0x00, 0x1A, 0xCA, 0xFF, 0xFF, 0x01 1921357dfd7SLiming Sun }; 1931357dfd7SLiming Sun 1941357dfd7SLiming Sun /* EFI variable name of the MAC address. */ 1951357dfd7SLiming Sun static efi_char16_t mlxbf_tmfifo_efi_name[] = L"RshimMacAddr"; 1961357dfd7SLiming Sun 1971357dfd7SLiming Sun /* Maximum L2 header length. */ 1981357dfd7SLiming Sun #define MLXBF_TMFIFO_NET_L2_OVERHEAD 36 1991357dfd7SLiming Sun 2001357dfd7SLiming Sun /* Supported virtio-net features. */ 2011357dfd7SLiming Sun #define MLXBF_TMFIFO_NET_FEATURES \ 2021357dfd7SLiming Sun (BIT_ULL(VIRTIO_NET_F_MTU) | BIT_ULL(VIRTIO_NET_F_STATUS) | \ 2031357dfd7SLiming Sun BIT_ULL(VIRTIO_NET_F_MAC)) 2041357dfd7SLiming Sun 2051357dfd7SLiming Sun #define mlxbf_vdev_to_tmfifo(d) container_of(d, struct mlxbf_tmfifo_vdev, vdev) 2061357dfd7SLiming Sun 2071357dfd7SLiming Sun /* Free vrings of the FIFO device. */ 2081357dfd7SLiming Sun static void mlxbf_tmfifo_free_vrings(struct mlxbf_tmfifo *fifo, 2091357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_vdev) 2101357dfd7SLiming Sun { 2111357dfd7SLiming Sun struct mlxbf_tmfifo_vring *vring; 2121357dfd7SLiming Sun int i, size; 2131357dfd7SLiming Sun 2141357dfd7SLiming Sun for (i = 0; i < ARRAY_SIZE(tm_vdev->vrings); i++) { 2151357dfd7SLiming Sun vring = &tm_vdev->vrings[i]; 2161357dfd7SLiming Sun if (vring->va) { 2171357dfd7SLiming Sun size = vring_size(vring->num, vring->align); 2181357dfd7SLiming Sun dma_free_coherent(tm_vdev->vdev.dev.parent, size, 2191357dfd7SLiming Sun vring->va, vring->dma); 2201357dfd7SLiming Sun vring->va = NULL; 2211357dfd7SLiming Sun if (vring->vq) { 2221357dfd7SLiming Sun vring_del_virtqueue(vring->vq); 2231357dfd7SLiming Sun vring->vq = NULL; 2241357dfd7SLiming Sun } 2251357dfd7SLiming Sun } 2261357dfd7SLiming Sun } 2271357dfd7SLiming Sun } 2281357dfd7SLiming Sun 2291357dfd7SLiming Sun /* Allocate vrings for the FIFO. */ 2301357dfd7SLiming Sun static int mlxbf_tmfifo_alloc_vrings(struct mlxbf_tmfifo *fifo, 2311357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_vdev) 2321357dfd7SLiming Sun { 2331357dfd7SLiming Sun struct mlxbf_tmfifo_vring *vring; 2341357dfd7SLiming Sun struct device *dev; 2351357dfd7SLiming Sun dma_addr_t dma; 2361357dfd7SLiming Sun int i, size; 2371357dfd7SLiming Sun void *va; 2381357dfd7SLiming Sun 2391357dfd7SLiming Sun for (i = 0; i < ARRAY_SIZE(tm_vdev->vrings); i++) { 2401357dfd7SLiming Sun vring = &tm_vdev->vrings[i]; 2411357dfd7SLiming Sun vring->fifo = fifo; 2421357dfd7SLiming Sun vring->num = MLXBF_TMFIFO_VRING_SIZE; 2431357dfd7SLiming Sun vring->align = SMP_CACHE_BYTES; 2441357dfd7SLiming Sun vring->index = i; 2451357dfd7SLiming Sun vring->vdev_id = tm_vdev->vdev.id.device; 2461357dfd7SLiming Sun dev = &tm_vdev->vdev.dev; 2471357dfd7SLiming Sun 2481357dfd7SLiming Sun size = vring_size(vring->num, vring->align); 2491357dfd7SLiming Sun va = dma_alloc_coherent(dev->parent, size, &dma, GFP_KERNEL); 2501357dfd7SLiming Sun if (!va) { 2511357dfd7SLiming Sun mlxbf_tmfifo_free_vrings(fifo, tm_vdev); 2521357dfd7SLiming Sun dev_err(dev->parent, "dma_alloc_coherent failed\n"); 2531357dfd7SLiming Sun return -ENOMEM; 2541357dfd7SLiming Sun } 2551357dfd7SLiming Sun 2561357dfd7SLiming Sun vring->va = va; 2571357dfd7SLiming Sun vring->dma = dma; 2581357dfd7SLiming Sun } 2591357dfd7SLiming Sun 2601357dfd7SLiming Sun return 0; 2611357dfd7SLiming Sun } 2621357dfd7SLiming Sun 2631357dfd7SLiming Sun /* Disable interrupts of the FIFO device. */ 2641357dfd7SLiming Sun static void mlxbf_tmfifo_disable_irqs(struct mlxbf_tmfifo *fifo) 2651357dfd7SLiming Sun { 2661357dfd7SLiming Sun int i, irq; 2671357dfd7SLiming Sun 2681357dfd7SLiming Sun for (i = 0; i < MLXBF_TM_MAX_IRQ; i++) { 2691357dfd7SLiming Sun irq = fifo->irq_info[i].irq; 2701357dfd7SLiming Sun fifo->irq_info[i].irq = 0; 2711357dfd7SLiming Sun disable_irq(irq); 2721357dfd7SLiming Sun } 2731357dfd7SLiming Sun } 2741357dfd7SLiming Sun 2751357dfd7SLiming Sun /* Interrupt handler. */ 2761357dfd7SLiming Sun static irqreturn_t mlxbf_tmfifo_irq_handler(int irq, void *arg) 2771357dfd7SLiming Sun { 2781357dfd7SLiming Sun struct mlxbf_tmfifo_irq_info *irq_info = arg; 2791357dfd7SLiming Sun 2801357dfd7SLiming Sun if (!test_and_set_bit(irq_info->index, &irq_info->fifo->pend_events)) 2811357dfd7SLiming Sun schedule_work(&irq_info->fifo->work); 2821357dfd7SLiming Sun 2831357dfd7SLiming Sun return IRQ_HANDLED; 2841357dfd7SLiming Sun } 2851357dfd7SLiming Sun 2861357dfd7SLiming Sun /* Get the next packet descriptor from the vring. */ 2871357dfd7SLiming Sun static struct vring_desc * 2881357dfd7SLiming Sun mlxbf_tmfifo_get_next_desc(struct mlxbf_tmfifo_vring *vring) 2891357dfd7SLiming Sun { 2901357dfd7SLiming Sun const struct vring *vr = virtqueue_get_vring(vring->vq); 2911357dfd7SLiming Sun struct virtio_device *vdev = vring->vq->vdev; 2921357dfd7SLiming Sun unsigned int idx, head; 2931357dfd7SLiming Sun 2941357dfd7SLiming Sun if (vring->next_avail == virtio16_to_cpu(vdev, vr->avail->idx)) 2951357dfd7SLiming Sun return NULL; 2961357dfd7SLiming Sun 2971357dfd7SLiming Sun idx = vring->next_avail % vr->num; 2981357dfd7SLiming Sun head = virtio16_to_cpu(vdev, vr->avail->ring[idx]); 2991357dfd7SLiming Sun if (WARN_ON(head >= vr->num)) 3001357dfd7SLiming Sun return NULL; 3011357dfd7SLiming Sun 3021357dfd7SLiming Sun vring->next_avail++; 3031357dfd7SLiming Sun 3041357dfd7SLiming Sun return &vr->desc[head]; 3051357dfd7SLiming Sun } 3061357dfd7SLiming Sun 3071357dfd7SLiming Sun /* Release virtio descriptor. */ 3081357dfd7SLiming Sun static void mlxbf_tmfifo_release_desc(struct mlxbf_tmfifo_vring *vring, 3091357dfd7SLiming Sun struct vring_desc *desc, u32 len) 3101357dfd7SLiming Sun { 3111357dfd7SLiming Sun const struct vring *vr = virtqueue_get_vring(vring->vq); 3121357dfd7SLiming Sun struct virtio_device *vdev = vring->vq->vdev; 3131357dfd7SLiming Sun u16 idx, vr_idx; 3141357dfd7SLiming Sun 3151357dfd7SLiming Sun vr_idx = virtio16_to_cpu(vdev, vr->used->idx); 3161357dfd7SLiming Sun idx = vr_idx % vr->num; 3171357dfd7SLiming Sun vr->used->ring[idx].id = cpu_to_virtio32(vdev, desc - vr->desc); 3181357dfd7SLiming Sun vr->used->ring[idx].len = cpu_to_virtio32(vdev, len); 3191357dfd7SLiming Sun 3201357dfd7SLiming Sun /* 3211357dfd7SLiming Sun * Virtio could poll and check the 'idx' to decide whether the desc is 3221357dfd7SLiming Sun * done or not. Add a memory barrier here to make sure the update above 3231357dfd7SLiming Sun * completes before updating the idx. 3241357dfd7SLiming Sun */ 3251357dfd7SLiming Sun mb(); 3261357dfd7SLiming Sun vr->used->idx = cpu_to_virtio16(vdev, vr_idx + 1); 3271357dfd7SLiming Sun } 3281357dfd7SLiming Sun 3291357dfd7SLiming Sun /* Get the total length of the descriptor chain. */ 3301357dfd7SLiming Sun static u32 mlxbf_tmfifo_get_pkt_len(struct mlxbf_tmfifo_vring *vring, 3311357dfd7SLiming Sun struct vring_desc *desc) 3321357dfd7SLiming Sun { 3331357dfd7SLiming Sun const struct vring *vr = virtqueue_get_vring(vring->vq); 3341357dfd7SLiming Sun struct virtio_device *vdev = vring->vq->vdev; 3351357dfd7SLiming Sun u32 len = 0, idx; 3361357dfd7SLiming Sun 3371357dfd7SLiming Sun while (desc) { 3381357dfd7SLiming Sun len += virtio32_to_cpu(vdev, desc->len); 3391357dfd7SLiming Sun if (!(virtio16_to_cpu(vdev, desc->flags) & VRING_DESC_F_NEXT)) 3401357dfd7SLiming Sun break; 3411357dfd7SLiming Sun idx = virtio16_to_cpu(vdev, desc->next); 3421357dfd7SLiming Sun desc = &vr->desc[idx]; 3431357dfd7SLiming Sun } 3441357dfd7SLiming Sun 3451357dfd7SLiming Sun return len; 3461357dfd7SLiming Sun } 3471357dfd7SLiming Sun 3481357dfd7SLiming Sun static void mlxbf_tmfifo_release_pending_pkt(struct mlxbf_tmfifo_vring *vring) 3491357dfd7SLiming Sun { 3501357dfd7SLiming Sun struct vring_desc *desc_head; 3511357dfd7SLiming Sun u32 len = 0; 3521357dfd7SLiming Sun 3531357dfd7SLiming Sun if (vring->desc_head) { 3541357dfd7SLiming Sun desc_head = vring->desc_head; 3551357dfd7SLiming Sun len = vring->pkt_len; 3561357dfd7SLiming Sun } else { 3571357dfd7SLiming Sun desc_head = mlxbf_tmfifo_get_next_desc(vring); 3581357dfd7SLiming Sun len = mlxbf_tmfifo_get_pkt_len(vring, desc_head); 3591357dfd7SLiming Sun } 3601357dfd7SLiming Sun 3611357dfd7SLiming Sun if (desc_head) 3621357dfd7SLiming Sun mlxbf_tmfifo_release_desc(vring, desc_head, len); 3631357dfd7SLiming Sun 3641357dfd7SLiming Sun vring->pkt_len = 0; 3651357dfd7SLiming Sun vring->desc = NULL; 3661357dfd7SLiming Sun vring->desc_head = NULL; 3671357dfd7SLiming Sun } 3681357dfd7SLiming Sun 3691357dfd7SLiming Sun static void mlxbf_tmfifo_init_net_desc(struct mlxbf_tmfifo_vring *vring, 3701357dfd7SLiming Sun struct vring_desc *desc, bool is_rx) 3711357dfd7SLiming Sun { 3721357dfd7SLiming Sun struct virtio_device *vdev = vring->vq->vdev; 3731357dfd7SLiming Sun struct virtio_net_hdr *net_hdr; 3741357dfd7SLiming Sun 3751357dfd7SLiming Sun net_hdr = phys_to_virt(virtio64_to_cpu(vdev, desc->addr)); 3761357dfd7SLiming Sun memset(net_hdr, 0, sizeof(*net_hdr)); 3771357dfd7SLiming Sun } 3781357dfd7SLiming Sun 3791357dfd7SLiming Sun /* Get and initialize the next packet. */ 3801357dfd7SLiming Sun static struct vring_desc * 3811357dfd7SLiming Sun mlxbf_tmfifo_get_next_pkt(struct mlxbf_tmfifo_vring *vring, bool is_rx) 3821357dfd7SLiming Sun { 3831357dfd7SLiming Sun struct vring_desc *desc; 3841357dfd7SLiming Sun 3851357dfd7SLiming Sun desc = mlxbf_tmfifo_get_next_desc(vring); 3861357dfd7SLiming Sun if (desc && is_rx && vring->vdev_id == VIRTIO_ID_NET) 3871357dfd7SLiming Sun mlxbf_tmfifo_init_net_desc(vring, desc, is_rx); 3881357dfd7SLiming Sun 3891357dfd7SLiming Sun vring->desc_head = desc; 3901357dfd7SLiming Sun vring->desc = desc; 3911357dfd7SLiming Sun 3921357dfd7SLiming Sun return desc; 3931357dfd7SLiming Sun } 3941357dfd7SLiming Sun 3951357dfd7SLiming Sun /* House-keeping timer. */ 3961357dfd7SLiming Sun static void mlxbf_tmfifo_timer(struct timer_list *t) 3971357dfd7SLiming Sun { 3981357dfd7SLiming Sun struct mlxbf_tmfifo *fifo = container_of(t, struct mlxbf_tmfifo, timer); 3991357dfd7SLiming Sun int rx, tx; 4001357dfd7SLiming Sun 4011357dfd7SLiming Sun rx = !test_and_set_bit(MLXBF_TM_RX_HWM_IRQ, &fifo->pend_events); 4021357dfd7SLiming Sun tx = !test_and_set_bit(MLXBF_TM_TX_LWM_IRQ, &fifo->pend_events); 4031357dfd7SLiming Sun 4041357dfd7SLiming Sun if (rx || tx) 4051357dfd7SLiming Sun schedule_work(&fifo->work); 4061357dfd7SLiming Sun 4071357dfd7SLiming Sun mod_timer(&fifo->timer, jiffies + MLXBF_TMFIFO_TIMER_INTERVAL); 4081357dfd7SLiming Sun } 4091357dfd7SLiming Sun 4101357dfd7SLiming Sun /* Copy one console packet into the output buffer. */ 4111357dfd7SLiming Sun static void mlxbf_tmfifo_console_output_one(struct mlxbf_tmfifo_vdev *cons, 4121357dfd7SLiming Sun struct mlxbf_tmfifo_vring *vring, 4131357dfd7SLiming Sun struct vring_desc *desc) 4141357dfd7SLiming Sun { 4151357dfd7SLiming Sun const struct vring *vr = virtqueue_get_vring(vring->vq); 4161357dfd7SLiming Sun struct virtio_device *vdev = &cons->vdev; 4171357dfd7SLiming Sun u32 len, idx, seg; 4181357dfd7SLiming Sun void *addr; 4191357dfd7SLiming Sun 4201357dfd7SLiming Sun while (desc) { 4211357dfd7SLiming Sun addr = phys_to_virt(virtio64_to_cpu(vdev, desc->addr)); 4221357dfd7SLiming Sun len = virtio32_to_cpu(vdev, desc->len); 4231357dfd7SLiming Sun 4241357dfd7SLiming Sun seg = CIRC_SPACE_TO_END(cons->tx_buf.head, cons->tx_buf.tail, 4251357dfd7SLiming Sun MLXBF_TMFIFO_CON_TX_BUF_SIZE); 4261357dfd7SLiming Sun if (len <= seg) { 4271357dfd7SLiming Sun memcpy(cons->tx_buf.buf + cons->tx_buf.head, addr, len); 4281357dfd7SLiming Sun } else { 4291357dfd7SLiming Sun memcpy(cons->tx_buf.buf + cons->tx_buf.head, addr, seg); 4301357dfd7SLiming Sun addr += seg; 4311357dfd7SLiming Sun memcpy(cons->tx_buf.buf, addr, len - seg); 4321357dfd7SLiming Sun } 4331357dfd7SLiming Sun cons->tx_buf.head = (cons->tx_buf.head + len) % 4341357dfd7SLiming Sun MLXBF_TMFIFO_CON_TX_BUF_SIZE; 4351357dfd7SLiming Sun 4361357dfd7SLiming Sun if (!(virtio16_to_cpu(vdev, desc->flags) & VRING_DESC_F_NEXT)) 4371357dfd7SLiming Sun break; 4381357dfd7SLiming Sun idx = virtio16_to_cpu(vdev, desc->next); 4391357dfd7SLiming Sun desc = &vr->desc[idx]; 4401357dfd7SLiming Sun } 4411357dfd7SLiming Sun } 4421357dfd7SLiming Sun 4431357dfd7SLiming Sun /* Copy console data into the output buffer. */ 4441357dfd7SLiming Sun static void mlxbf_tmfifo_console_output(struct mlxbf_tmfifo_vdev *cons, 4451357dfd7SLiming Sun struct mlxbf_tmfifo_vring *vring) 4461357dfd7SLiming Sun { 4471357dfd7SLiming Sun struct vring_desc *desc; 4481357dfd7SLiming Sun u32 len, avail; 4491357dfd7SLiming Sun 4501357dfd7SLiming Sun desc = mlxbf_tmfifo_get_next_desc(vring); 4511357dfd7SLiming Sun while (desc) { 4521357dfd7SLiming Sun /* Release the packet if not enough space. */ 4531357dfd7SLiming Sun len = mlxbf_tmfifo_get_pkt_len(vring, desc); 4541357dfd7SLiming Sun avail = CIRC_SPACE(cons->tx_buf.head, cons->tx_buf.tail, 4551357dfd7SLiming Sun MLXBF_TMFIFO_CON_TX_BUF_SIZE); 4561357dfd7SLiming Sun if (len + MLXBF_TMFIFO_CON_TX_BUF_RSV_SIZE > avail) { 4571357dfd7SLiming Sun mlxbf_tmfifo_release_desc(vring, desc, len); 4581357dfd7SLiming Sun break; 4591357dfd7SLiming Sun } 4601357dfd7SLiming Sun 4611357dfd7SLiming Sun mlxbf_tmfifo_console_output_one(cons, vring, desc); 4621357dfd7SLiming Sun mlxbf_tmfifo_release_desc(vring, desc, len); 4631357dfd7SLiming Sun desc = mlxbf_tmfifo_get_next_desc(vring); 4641357dfd7SLiming Sun } 4651357dfd7SLiming Sun } 4661357dfd7SLiming Sun 4671357dfd7SLiming Sun /* Get the number of available words in Rx FIFO for receiving. */ 4681357dfd7SLiming Sun static int mlxbf_tmfifo_get_rx_avail(struct mlxbf_tmfifo *fifo) 4691357dfd7SLiming Sun { 4701357dfd7SLiming Sun u64 sts; 4711357dfd7SLiming Sun 4721357dfd7SLiming Sun sts = readq(fifo->rx_base + MLXBF_TMFIFO_RX_STS); 4731357dfd7SLiming Sun return FIELD_GET(MLXBF_TMFIFO_RX_STS__COUNT_MASK, sts); 4741357dfd7SLiming Sun } 4751357dfd7SLiming Sun 4761357dfd7SLiming Sun /* Get the number of available words in the TmFifo for sending. */ 4771357dfd7SLiming Sun static int mlxbf_tmfifo_get_tx_avail(struct mlxbf_tmfifo *fifo, int vdev_id) 4781357dfd7SLiming Sun { 4791357dfd7SLiming Sun int tx_reserve; 4801357dfd7SLiming Sun u32 count; 4811357dfd7SLiming Sun u64 sts; 4821357dfd7SLiming Sun 4831357dfd7SLiming Sun /* Reserve some room in FIFO for console messages. */ 4841357dfd7SLiming Sun if (vdev_id == VIRTIO_ID_NET) 4851357dfd7SLiming Sun tx_reserve = fifo->tx_fifo_size / MLXBF_TMFIFO_RESERVE_RATIO; 4861357dfd7SLiming Sun else 4871357dfd7SLiming Sun tx_reserve = 1; 4881357dfd7SLiming Sun 4891357dfd7SLiming Sun sts = readq(fifo->tx_base + MLXBF_TMFIFO_TX_STS); 4901357dfd7SLiming Sun count = FIELD_GET(MLXBF_TMFIFO_TX_STS__COUNT_MASK, sts); 4911357dfd7SLiming Sun return fifo->tx_fifo_size - tx_reserve - count; 4921357dfd7SLiming Sun } 4931357dfd7SLiming Sun 4941357dfd7SLiming Sun /* Console Tx (move data from the output buffer into the TmFifo). */ 4951357dfd7SLiming Sun static void mlxbf_tmfifo_console_tx(struct mlxbf_tmfifo *fifo, int avail) 4961357dfd7SLiming Sun { 4971357dfd7SLiming Sun struct mlxbf_tmfifo_msg_hdr hdr; 4981357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *cons; 4991357dfd7SLiming Sun unsigned long flags; 5001357dfd7SLiming Sun int size, seg; 5011357dfd7SLiming Sun void *addr; 5021357dfd7SLiming Sun u64 data; 5031357dfd7SLiming Sun 5041357dfd7SLiming Sun /* Return if not enough space available. */ 5051357dfd7SLiming Sun if (avail < MLXBF_TMFIFO_DATA_MIN_WORDS) 5061357dfd7SLiming Sun return; 5071357dfd7SLiming Sun 5081357dfd7SLiming Sun cons = fifo->vdev[VIRTIO_ID_CONSOLE]; 5091357dfd7SLiming Sun if (!cons || !cons->tx_buf.buf) 5101357dfd7SLiming Sun return; 5111357dfd7SLiming Sun 5121357dfd7SLiming Sun /* Return if no data to send. */ 5131357dfd7SLiming Sun size = CIRC_CNT(cons->tx_buf.head, cons->tx_buf.tail, 5141357dfd7SLiming Sun MLXBF_TMFIFO_CON_TX_BUF_SIZE); 5151357dfd7SLiming Sun if (size == 0) 5161357dfd7SLiming Sun return; 5171357dfd7SLiming Sun 5181357dfd7SLiming Sun /* Adjust the size to available space. */ 5191357dfd7SLiming Sun if (size + sizeof(hdr) > avail * sizeof(u64)) 5201357dfd7SLiming Sun size = avail * sizeof(u64) - sizeof(hdr); 5211357dfd7SLiming Sun 5221357dfd7SLiming Sun /* Write header. */ 5231357dfd7SLiming Sun hdr.type = VIRTIO_ID_CONSOLE; 5241357dfd7SLiming Sun hdr.len = htons(size); 5251357dfd7SLiming Sun writeq(*(u64 *)&hdr, fifo->tx_base + MLXBF_TMFIFO_TX_DATA); 5261357dfd7SLiming Sun 5271357dfd7SLiming Sun /* Use spin-lock to protect the 'cons->tx_buf'. */ 528*638bc4caSLiming Sun spin_lock_irqsave(&fifo->spin_lock[0], flags); 5291357dfd7SLiming Sun 5301357dfd7SLiming Sun while (size > 0) { 5311357dfd7SLiming Sun addr = cons->tx_buf.buf + cons->tx_buf.tail; 5321357dfd7SLiming Sun 5331357dfd7SLiming Sun seg = CIRC_CNT_TO_END(cons->tx_buf.head, cons->tx_buf.tail, 5341357dfd7SLiming Sun MLXBF_TMFIFO_CON_TX_BUF_SIZE); 5351357dfd7SLiming Sun if (seg >= sizeof(u64)) { 5361357dfd7SLiming Sun memcpy(&data, addr, sizeof(u64)); 5371357dfd7SLiming Sun } else { 5381357dfd7SLiming Sun memcpy(&data, addr, seg); 5391357dfd7SLiming Sun memcpy((u8 *)&data + seg, cons->tx_buf.buf, 5401357dfd7SLiming Sun sizeof(u64) - seg); 5411357dfd7SLiming Sun } 5421357dfd7SLiming Sun writeq(data, fifo->tx_base + MLXBF_TMFIFO_TX_DATA); 5431357dfd7SLiming Sun 5441357dfd7SLiming Sun if (size >= sizeof(u64)) { 5451357dfd7SLiming Sun cons->tx_buf.tail = (cons->tx_buf.tail + sizeof(u64)) % 5461357dfd7SLiming Sun MLXBF_TMFIFO_CON_TX_BUF_SIZE; 5471357dfd7SLiming Sun size -= sizeof(u64); 5481357dfd7SLiming Sun } else { 5491357dfd7SLiming Sun cons->tx_buf.tail = (cons->tx_buf.tail + size) % 5501357dfd7SLiming Sun MLXBF_TMFIFO_CON_TX_BUF_SIZE; 5511357dfd7SLiming Sun size = 0; 5521357dfd7SLiming Sun } 5531357dfd7SLiming Sun } 5541357dfd7SLiming Sun 555*638bc4caSLiming Sun spin_unlock_irqrestore(&fifo->spin_lock[0], flags); 5561357dfd7SLiming Sun } 5571357dfd7SLiming Sun 5581357dfd7SLiming Sun /* Rx/Tx one word in the descriptor buffer. */ 5591357dfd7SLiming Sun static void mlxbf_tmfifo_rxtx_word(struct mlxbf_tmfifo_vring *vring, 5601357dfd7SLiming Sun struct vring_desc *desc, 5611357dfd7SLiming Sun bool is_rx, int len) 5621357dfd7SLiming Sun { 5631357dfd7SLiming Sun struct virtio_device *vdev = vring->vq->vdev; 5641357dfd7SLiming Sun struct mlxbf_tmfifo *fifo = vring->fifo; 5651357dfd7SLiming Sun void *addr; 5661357dfd7SLiming Sun u64 data; 5671357dfd7SLiming Sun 5681357dfd7SLiming Sun /* Get the buffer address of this desc. */ 5691357dfd7SLiming Sun addr = phys_to_virt(virtio64_to_cpu(vdev, desc->addr)); 5701357dfd7SLiming Sun 5711357dfd7SLiming Sun /* Read a word from FIFO for Rx. */ 5721357dfd7SLiming Sun if (is_rx) 5731357dfd7SLiming Sun data = readq(fifo->rx_base + MLXBF_TMFIFO_RX_DATA); 5741357dfd7SLiming Sun 5751357dfd7SLiming Sun if (vring->cur_len + sizeof(u64) <= len) { 5761357dfd7SLiming Sun /* The whole word. */ 5771357dfd7SLiming Sun if (is_rx) 5781357dfd7SLiming Sun memcpy(addr + vring->cur_len, &data, sizeof(u64)); 5791357dfd7SLiming Sun else 5801357dfd7SLiming Sun memcpy(&data, addr + vring->cur_len, sizeof(u64)); 5811357dfd7SLiming Sun vring->cur_len += sizeof(u64); 5821357dfd7SLiming Sun } else { 5831357dfd7SLiming Sun /* Leftover bytes. */ 5841357dfd7SLiming Sun if (is_rx) 5851357dfd7SLiming Sun memcpy(addr + vring->cur_len, &data, 5861357dfd7SLiming Sun len - vring->cur_len); 5871357dfd7SLiming Sun else 5881357dfd7SLiming Sun memcpy(&data, addr + vring->cur_len, 5891357dfd7SLiming Sun len - vring->cur_len); 5901357dfd7SLiming Sun vring->cur_len = len; 5911357dfd7SLiming Sun } 5921357dfd7SLiming Sun 5931357dfd7SLiming Sun /* Write the word into FIFO for Tx. */ 5941357dfd7SLiming Sun if (!is_rx) 5951357dfd7SLiming Sun writeq(data, fifo->tx_base + MLXBF_TMFIFO_TX_DATA); 5961357dfd7SLiming Sun } 5971357dfd7SLiming Sun 5981357dfd7SLiming Sun /* 5991357dfd7SLiming Sun * Rx/Tx packet header. 6001357dfd7SLiming Sun * 6011357dfd7SLiming Sun * In Rx case, the packet might be found to belong to a different vring since 6021357dfd7SLiming Sun * the TmFifo is shared by different services. In such case, the 'vring_change' 6031357dfd7SLiming Sun * flag is set. 6041357dfd7SLiming Sun */ 6051357dfd7SLiming Sun static void mlxbf_tmfifo_rxtx_header(struct mlxbf_tmfifo_vring *vring, 6061357dfd7SLiming Sun struct vring_desc *desc, 6071357dfd7SLiming Sun bool is_rx, bool *vring_change) 6081357dfd7SLiming Sun { 6091357dfd7SLiming Sun struct mlxbf_tmfifo *fifo = vring->fifo; 6101357dfd7SLiming Sun struct virtio_net_config *config; 6111357dfd7SLiming Sun struct mlxbf_tmfifo_msg_hdr hdr; 6121357dfd7SLiming Sun int vdev_id, hdr_len; 6131357dfd7SLiming Sun 6141357dfd7SLiming Sun /* Read/Write packet header. */ 6151357dfd7SLiming Sun if (is_rx) { 6161357dfd7SLiming Sun /* Drain one word from the FIFO. */ 6171357dfd7SLiming Sun *(u64 *)&hdr = readq(fifo->rx_base + MLXBF_TMFIFO_RX_DATA); 6181357dfd7SLiming Sun 6191357dfd7SLiming Sun /* Skip the length 0 packets (keepalive). */ 6201357dfd7SLiming Sun if (hdr.len == 0) 6211357dfd7SLiming Sun return; 6221357dfd7SLiming Sun 6231357dfd7SLiming Sun /* Check packet type. */ 6241357dfd7SLiming Sun if (hdr.type == VIRTIO_ID_NET) { 6251357dfd7SLiming Sun vdev_id = VIRTIO_ID_NET; 6261357dfd7SLiming Sun hdr_len = sizeof(struct virtio_net_hdr); 6271357dfd7SLiming Sun config = &fifo->vdev[vdev_id]->config.net; 6281357dfd7SLiming Sun if (ntohs(hdr.len) > config->mtu + 6291357dfd7SLiming Sun MLXBF_TMFIFO_NET_L2_OVERHEAD) 6301357dfd7SLiming Sun return; 6311357dfd7SLiming Sun } else { 6321357dfd7SLiming Sun vdev_id = VIRTIO_ID_CONSOLE; 6331357dfd7SLiming Sun hdr_len = 0; 6341357dfd7SLiming Sun } 6351357dfd7SLiming Sun 6361357dfd7SLiming Sun /* 6371357dfd7SLiming Sun * Check whether the new packet still belongs to this vring. 6381357dfd7SLiming Sun * If not, update the pkt_len of the new vring. 6391357dfd7SLiming Sun */ 6401357dfd7SLiming Sun if (vdev_id != vring->vdev_id) { 6411357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_dev2 = fifo->vdev[vdev_id]; 6421357dfd7SLiming Sun 6431357dfd7SLiming Sun if (!tm_dev2) 6441357dfd7SLiming Sun return; 6451357dfd7SLiming Sun vring->desc = desc; 6461357dfd7SLiming Sun vring = &tm_dev2->vrings[MLXBF_TMFIFO_VRING_RX]; 6471357dfd7SLiming Sun *vring_change = true; 6481357dfd7SLiming Sun } 6491357dfd7SLiming Sun vring->pkt_len = ntohs(hdr.len) + hdr_len; 6501357dfd7SLiming Sun } else { 6511357dfd7SLiming Sun /* Network virtio has an extra header. */ 6521357dfd7SLiming Sun hdr_len = (vring->vdev_id == VIRTIO_ID_NET) ? 6531357dfd7SLiming Sun sizeof(struct virtio_net_hdr) : 0; 6541357dfd7SLiming Sun vring->pkt_len = mlxbf_tmfifo_get_pkt_len(vring, desc); 6551357dfd7SLiming Sun hdr.type = (vring->vdev_id == VIRTIO_ID_NET) ? 6561357dfd7SLiming Sun VIRTIO_ID_NET : VIRTIO_ID_CONSOLE; 6571357dfd7SLiming Sun hdr.len = htons(vring->pkt_len - hdr_len); 6581357dfd7SLiming Sun writeq(*(u64 *)&hdr, fifo->tx_base + MLXBF_TMFIFO_TX_DATA); 6591357dfd7SLiming Sun } 6601357dfd7SLiming Sun 6611357dfd7SLiming Sun vring->cur_len = hdr_len; 6621357dfd7SLiming Sun vring->rem_len = vring->pkt_len; 6631357dfd7SLiming Sun fifo->vring[is_rx] = vring; 6641357dfd7SLiming Sun } 6651357dfd7SLiming Sun 6661357dfd7SLiming Sun /* 6671357dfd7SLiming Sun * Rx/Tx one descriptor. 6681357dfd7SLiming Sun * 6691357dfd7SLiming Sun * Return true to indicate more data available. 6701357dfd7SLiming Sun */ 6711357dfd7SLiming Sun static bool mlxbf_tmfifo_rxtx_one_desc(struct mlxbf_tmfifo_vring *vring, 6721357dfd7SLiming Sun bool is_rx, int *avail) 6731357dfd7SLiming Sun { 6741357dfd7SLiming Sun const struct vring *vr = virtqueue_get_vring(vring->vq); 6751357dfd7SLiming Sun struct mlxbf_tmfifo *fifo = vring->fifo; 6761357dfd7SLiming Sun struct virtio_device *vdev; 6771357dfd7SLiming Sun bool vring_change = false; 6781357dfd7SLiming Sun struct vring_desc *desc; 6791357dfd7SLiming Sun unsigned long flags; 6801357dfd7SLiming Sun u32 len, idx; 6811357dfd7SLiming Sun 6821357dfd7SLiming Sun vdev = &fifo->vdev[vring->vdev_id]->vdev; 6831357dfd7SLiming Sun 6841357dfd7SLiming Sun /* Get the descriptor of the next packet. */ 6851357dfd7SLiming Sun if (!vring->desc) { 6861357dfd7SLiming Sun desc = mlxbf_tmfifo_get_next_pkt(vring, is_rx); 6871357dfd7SLiming Sun if (!desc) 6881357dfd7SLiming Sun return false; 6891357dfd7SLiming Sun } else { 6901357dfd7SLiming Sun desc = vring->desc; 6911357dfd7SLiming Sun } 6921357dfd7SLiming Sun 6931357dfd7SLiming Sun /* Beginning of a packet. Start to Rx/Tx packet header. */ 6941357dfd7SLiming Sun if (vring->pkt_len == 0) { 6951357dfd7SLiming Sun mlxbf_tmfifo_rxtx_header(vring, desc, is_rx, &vring_change); 6961357dfd7SLiming Sun (*avail)--; 6971357dfd7SLiming Sun 6981357dfd7SLiming Sun /* Return if new packet is for another ring. */ 6991357dfd7SLiming Sun if (vring_change) 7001357dfd7SLiming Sun return false; 7011357dfd7SLiming Sun goto mlxbf_tmfifo_desc_done; 7021357dfd7SLiming Sun } 7031357dfd7SLiming Sun 7041357dfd7SLiming Sun /* Get the length of this desc. */ 7051357dfd7SLiming Sun len = virtio32_to_cpu(vdev, desc->len); 7061357dfd7SLiming Sun if (len > vring->rem_len) 7071357dfd7SLiming Sun len = vring->rem_len; 7081357dfd7SLiming Sun 7091357dfd7SLiming Sun /* Rx/Tx one word (8 bytes) if not done. */ 7101357dfd7SLiming Sun if (vring->cur_len < len) { 7111357dfd7SLiming Sun mlxbf_tmfifo_rxtx_word(vring, desc, is_rx, len); 7121357dfd7SLiming Sun (*avail)--; 7131357dfd7SLiming Sun } 7141357dfd7SLiming Sun 7151357dfd7SLiming Sun /* Check again whether it's done. */ 7161357dfd7SLiming Sun if (vring->cur_len == len) { 7171357dfd7SLiming Sun vring->cur_len = 0; 7181357dfd7SLiming Sun vring->rem_len -= len; 7191357dfd7SLiming Sun 7201357dfd7SLiming Sun /* Get the next desc on the chain. */ 7211357dfd7SLiming Sun if (vring->rem_len > 0 && 7221357dfd7SLiming Sun (virtio16_to_cpu(vdev, desc->flags) & VRING_DESC_F_NEXT)) { 7231357dfd7SLiming Sun idx = virtio16_to_cpu(vdev, desc->next); 7241357dfd7SLiming Sun desc = &vr->desc[idx]; 7251357dfd7SLiming Sun goto mlxbf_tmfifo_desc_done; 7261357dfd7SLiming Sun } 7271357dfd7SLiming Sun 7281357dfd7SLiming Sun /* Done and release the pending packet. */ 7291357dfd7SLiming Sun mlxbf_tmfifo_release_pending_pkt(vring); 7301357dfd7SLiming Sun desc = NULL; 7311357dfd7SLiming Sun fifo->vring[is_rx] = NULL; 7321357dfd7SLiming Sun 7331357dfd7SLiming Sun /* Notify upper layer that packet is done. */ 734*638bc4caSLiming Sun spin_lock_irqsave(&fifo->spin_lock[is_rx], flags); 7351357dfd7SLiming Sun vring_interrupt(0, vring->vq); 736*638bc4caSLiming Sun spin_unlock_irqrestore(&fifo->spin_lock[is_rx], flags); 7371357dfd7SLiming Sun } 7381357dfd7SLiming Sun 7391357dfd7SLiming Sun mlxbf_tmfifo_desc_done: 7401357dfd7SLiming Sun /* Save the current desc. */ 7411357dfd7SLiming Sun vring->desc = desc; 7421357dfd7SLiming Sun 7431357dfd7SLiming Sun return true; 7441357dfd7SLiming Sun } 7451357dfd7SLiming Sun 7461357dfd7SLiming Sun /* Rx & Tx processing of a queue. */ 7471357dfd7SLiming Sun static void mlxbf_tmfifo_rxtx(struct mlxbf_tmfifo_vring *vring, bool is_rx) 7481357dfd7SLiming Sun { 7491357dfd7SLiming Sun int avail = 0, devid = vring->vdev_id; 7501357dfd7SLiming Sun struct mlxbf_tmfifo *fifo; 7511357dfd7SLiming Sun bool more; 7521357dfd7SLiming Sun 7531357dfd7SLiming Sun fifo = vring->fifo; 7541357dfd7SLiming Sun 7551357dfd7SLiming Sun /* Return if vdev is not ready. */ 7561357dfd7SLiming Sun if (!fifo->vdev[devid]) 7571357dfd7SLiming Sun return; 7581357dfd7SLiming Sun 7591357dfd7SLiming Sun /* Return if another vring is running. */ 7601357dfd7SLiming Sun if (fifo->vring[is_rx] && fifo->vring[is_rx] != vring) 7611357dfd7SLiming Sun return; 7621357dfd7SLiming Sun 7631357dfd7SLiming Sun /* Only handle console and network for now. */ 7641357dfd7SLiming Sun if (WARN_ON(devid != VIRTIO_ID_NET && devid != VIRTIO_ID_CONSOLE)) 7651357dfd7SLiming Sun return; 7661357dfd7SLiming Sun 7671357dfd7SLiming Sun do { 7681357dfd7SLiming Sun /* Get available FIFO space. */ 7691357dfd7SLiming Sun if (avail == 0) { 7701357dfd7SLiming Sun if (is_rx) 7711357dfd7SLiming Sun avail = mlxbf_tmfifo_get_rx_avail(fifo); 7721357dfd7SLiming Sun else 7731357dfd7SLiming Sun avail = mlxbf_tmfifo_get_tx_avail(fifo, devid); 7741357dfd7SLiming Sun if (avail <= 0) 7751357dfd7SLiming Sun break; 7761357dfd7SLiming Sun } 7771357dfd7SLiming Sun 7781357dfd7SLiming Sun /* Console output always comes from the Tx buffer. */ 7791357dfd7SLiming Sun if (!is_rx && devid == VIRTIO_ID_CONSOLE) { 7801357dfd7SLiming Sun mlxbf_tmfifo_console_tx(fifo, avail); 7811357dfd7SLiming Sun break; 7821357dfd7SLiming Sun } 7831357dfd7SLiming Sun 7841357dfd7SLiming Sun /* Handle one descriptor. */ 7851357dfd7SLiming Sun more = mlxbf_tmfifo_rxtx_one_desc(vring, is_rx, &avail); 7861357dfd7SLiming Sun } while (more); 7871357dfd7SLiming Sun } 7881357dfd7SLiming Sun 7891357dfd7SLiming Sun /* Handle Rx or Tx queues. */ 7901357dfd7SLiming Sun static void mlxbf_tmfifo_work_rxtx(struct mlxbf_tmfifo *fifo, int queue_id, 7911357dfd7SLiming Sun int irq_id, bool is_rx) 7921357dfd7SLiming Sun { 7931357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_vdev; 7941357dfd7SLiming Sun struct mlxbf_tmfifo_vring *vring; 7951357dfd7SLiming Sun int i; 7961357dfd7SLiming Sun 7971357dfd7SLiming Sun if (!test_and_clear_bit(irq_id, &fifo->pend_events) || 7981357dfd7SLiming Sun !fifo->irq_info[irq_id].irq) 7991357dfd7SLiming Sun return; 8001357dfd7SLiming Sun 8011357dfd7SLiming Sun for (i = 0; i < MLXBF_TMFIFO_VDEV_MAX; i++) { 8021357dfd7SLiming Sun tm_vdev = fifo->vdev[i]; 8031357dfd7SLiming Sun if (tm_vdev) { 8041357dfd7SLiming Sun vring = &tm_vdev->vrings[queue_id]; 8051357dfd7SLiming Sun if (vring->vq) 8061357dfd7SLiming Sun mlxbf_tmfifo_rxtx(vring, is_rx); 8071357dfd7SLiming Sun } 8081357dfd7SLiming Sun } 8091357dfd7SLiming Sun } 8101357dfd7SLiming Sun 8111357dfd7SLiming Sun /* Work handler for Rx and Tx case. */ 8121357dfd7SLiming Sun static void mlxbf_tmfifo_work_handler(struct work_struct *work) 8131357dfd7SLiming Sun { 8141357dfd7SLiming Sun struct mlxbf_tmfifo *fifo; 8151357dfd7SLiming Sun 8161357dfd7SLiming Sun fifo = container_of(work, struct mlxbf_tmfifo, work); 8171357dfd7SLiming Sun if (!fifo->is_ready) 8181357dfd7SLiming Sun return; 8191357dfd7SLiming Sun 8201357dfd7SLiming Sun mutex_lock(&fifo->lock); 8211357dfd7SLiming Sun 8221357dfd7SLiming Sun /* Tx (Send data to the TmFifo). */ 8231357dfd7SLiming Sun mlxbf_tmfifo_work_rxtx(fifo, MLXBF_TMFIFO_VRING_TX, 8241357dfd7SLiming Sun MLXBF_TM_TX_LWM_IRQ, false); 8251357dfd7SLiming Sun 8261357dfd7SLiming Sun /* Rx (Receive data from the TmFifo). */ 8271357dfd7SLiming Sun mlxbf_tmfifo_work_rxtx(fifo, MLXBF_TMFIFO_VRING_RX, 8281357dfd7SLiming Sun MLXBF_TM_RX_HWM_IRQ, true); 8291357dfd7SLiming Sun 8301357dfd7SLiming Sun mutex_unlock(&fifo->lock); 8311357dfd7SLiming Sun } 8321357dfd7SLiming Sun 8331357dfd7SLiming Sun /* The notify function is called when new buffers are posted. */ 8341357dfd7SLiming Sun static bool mlxbf_tmfifo_virtio_notify(struct virtqueue *vq) 8351357dfd7SLiming Sun { 8361357dfd7SLiming Sun struct mlxbf_tmfifo_vring *vring = vq->priv; 8371357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_vdev; 8381357dfd7SLiming Sun struct mlxbf_tmfifo *fifo; 8391357dfd7SLiming Sun unsigned long flags; 8401357dfd7SLiming Sun 8411357dfd7SLiming Sun fifo = vring->fifo; 8421357dfd7SLiming Sun 8431357dfd7SLiming Sun /* 8441357dfd7SLiming Sun * Virtio maintains vrings in pairs, even number ring for Rx 8451357dfd7SLiming Sun * and odd number ring for Tx. 8461357dfd7SLiming Sun */ 8471357dfd7SLiming Sun if (vring->index & BIT(0)) { 8481357dfd7SLiming Sun /* 8491357dfd7SLiming Sun * Console could make blocking call with interrupts disabled. 8501357dfd7SLiming Sun * In such case, the vring needs to be served right away. For 8511357dfd7SLiming Sun * other cases, just set the TX LWM bit to start Tx in the 8521357dfd7SLiming Sun * worker handler. 8531357dfd7SLiming Sun */ 8541357dfd7SLiming Sun if (vring->vdev_id == VIRTIO_ID_CONSOLE) { 855*638bc4caSLiming Sun spin_lock_irqsave(&fifo->spin_lock[0], flags); 8561357dfd7SLiming Sun tm_vdev = fifo->vdev[VIRTIO_ID_CONSOLE]; 8571357dfd7SLiming Sun mlxbf_tmfifo_console_output(tm_vdev, vring); 858*638bc4caSLiming Sun spin_unlock_irqrestore(&fifo->spin_lock[0], flags); 8591357dfd7SLiming Sun } else if (test_and_set_bit(MLXBF_TM_TX_LWM_IRQ, 8601357dfd7SLiming Sun &fifo->pend_events)) { 8611357dfd7SLiming Sun return true; 8621357dfd7SLiming Sun } 8631357dfd7SLiming Sun } else { 8641357dfd7SLiming Sun if (test_and_set_bit(MLXBF_TM_RX_HWM_IRQ, &fifo->pend_events)) 8651357dfd7SLiming Sun return true; 8661357dfd7SLiming Sun } 8671357dfd7SLiming Sun 8681357dfd7SLiming Sun schedule_work(&fifo->work); 8691357dfd7SLiming Sun 8701357dfd7SLiming Sun return true; 8711357dfd7SLiming Sun } 8721357dfd7SLiming Sun 8731357dfd7SLiming Sun /* Get the array of feature bits for this device. */ 8741357dfd7SLiming Sun static u64 mlxbf_tmfifo_virtio_get_features(struct virtio_device *vdev) 8751357dfd7SLiming Sun { 8761357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev); 8771357dfd7SLiming Sun 8781357dfd7SLiming Sun return tm_vdev->features; 8791357dfd7SLiming Sun } 8801357dfd7SLiming Sun 8811357dfd7SLiming Sun /* Confirm device features to use. */ 8821357dfd7SLiming Sun static int mlxbf_tmfifo_virtio_finalize_features(struct virtio_device *vdev) 8831357dfd7SLiming Sun { 8841357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev); 8851357dfd7SLiming Sun 8861357dfd7SLiming Sun tm_vdev->features = vdev->features; 8871357dfd7SLiming Sun 8881357dfd7SLiming Sun return 0; 8891357dfd7SLiming Sun } 8901357dfd7SLiming Sun 8911357dfd7SLiming Sun /* Free virtqueues found by find_vqs(). */ 8921357dfd7SLiming Sun static void mlxbf_tmfifo_virtio_del_vqs(struct virtio_device *vdev) 8931357dfd7SLiming Sun { 8941357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev); 8951357dfd7SLiming Sun struct mlxbf_tmfifo_vring *vring; 8961357dfd7SLiming Sun struct virtqueue *vq; 8971357dfd7SLiming Sun int i; 8981357dfd7SLiming Sun 8991357dfd7SLiming Sun for (i = 0; i < ARRAY_SIZE(tm_vdev->vrings); i++) { 9001357dfd7SLiming Sun vring = &tm_vdev->vrings[i]; 9011357dfd7SLiming Sun 9021357dfd7SLiming Sun /* Release the pending packet. */ 9031357dfd7SLiming Sun if (vring->desc) 9041357dfd7SLiming Sun mlxbf_tmfifo_release_pending_pkt(vring); 9051357dfd7SLiming Sun vq = vring->vq; 9061357dfd7SLiming Sun if (vq) { 9071357dfd7SLiming Sun vring->vq = NULL; 9081357dfd7SLiming Sun vring_del_virtqueue(vq); 9091357dfd7SLiming Sun } 9101357dfd7SLiming Sun } 9111357dfd7SLiming Sun } 9121357dfd7SLiming Sun 9131357dfd7SLiming Sun /* Create and initialize the virtual queues. */ 9141357dfd7SLiming Sun static int mlxbf_tmfifo_virtio_find_vqs(struct virtio_device *vdev, 9151357dfd7SLiming Sun unsigned int nvqs, 9161357dfd7SLiming Sun struct virtqueue *vqs[], 9171357dfd7SLiming Sun vq_callback_t *callbacks[], 9181357dfd7SLiming Sun const char * const names[], 9191357dfd7SLiming Sun const bool *ctx, 9201357dfd7SLiming Sun struct irq_affinity *desc) 9211357dfd7SLiming Sun { 9221357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev); 9231357dfd7SLiming Sun struct mlxbf_tmfifo_vring *vring; 9241357dfd7SLiming Sun struct virtqueue *vq; 9251357dfd7SLiming Sun int i, ret, size; 9261357dfd7SLiming Sun 9271357dfd7SLiming Sun if (nvqs > ARRAY_SIZE(tm_vdev->vrings)) 9281357dfd7SLiming Sun return -EINVAL; 9291357dfd7SLiming Sun 9301357dfd7SLiming Sun for (i = 0; i < nvqs; ++i) { 9311357dfd7SLiming Sun if (!names[i]) { 9321357dfd7SLiming Sun ret = -EINVAL; 9331357dfd7SLiming Sun goto error; 9341357dfd7SLiming Sun } 9351357dfd7SLiming Sun vring = &tm_vdev->vrings[i]; 9361357dfd7SLiming Sun 9371357dfd7SLiming Sun /* zero vring */ 9381357dfd7SLiming Sun size = vring_size(vring->num, vring->align); 9391357dfd7SLiming Sun memset(vring->va, 0, size); 9401357dfd7SLiming Sun vq = vring_new_virtqueue(i, vring->num, vring->align, vdev, 9411357dfd7SLiming Sun false, false, vring->va, 9421357dfd7SLiming Sun mlxbf_tmfifo_virtio_notify, 9431357dfd7SLiming Sun callbacks[i], names[i]); 9441357dfd7SLiming Sun if (!vq) { 9451357dfd7SLiming Sun dev_err(&vdev->dev, "vring_new_virtqueue failed\n"); 9461357dfd7SLiming Sun ret = -ENOMEM; 9471357dfd7SLiming Sun goto error; 9481357dfd7SLiming Sun } 9491357dfd7SLiming Sun 9501357dfd7SLiming Sun vqs[i] = vq; 9511357dfd7SLiming Sun vring->vq = vq; 9521357dfd7SLiming Sun vq->priv = vring; 9531357dfd7SLiming Sun } 9541357dfd7SLiming Sun 9551357dfd7SLiming Sun return 0; 9561357dfd7SLiming Sun 9571357dfd7SLiming Sun error: 9581357dfd7SLiming Sun mlxbf_tmfifo_virtio_del_vqs(vdev); 9591357dfd7SLiming Sun return ret; 9601357dfd7SLiming Sun } 9611357dfd7SLiming Sun 9621357dfd7SLiming Sun /* Read the status byte. */ 9631357dfd7SLiming Sun static u8 mlxbf_tmfifo_virtio_get_status(struct virtio_device *vdev) 9641357dfd7SLiming Sun { 9651357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev); 9661357dfd7SLiming Sun 9671357dfd7SLiming Sun return tm_vdev->status; 9681357dfd7SLiming Sun } 9691357dfd7SLiming Sun 9701357dfd7SLiming Sun /* Write the status byte. */ 9711357dfd7SLiming Sun static void mlxbf_tmfifo_virtio_set_status(struct virtio_device *vdev, 9721357dfd7SLiming Sun u8 status) 9731357dfd7SLiming Sun { 9741357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev); 9751357dfd7SLiming Sun 9761357dfd7SLiming Sun tm_vdev->status = status; 9771357dfd7SLiming Sun } 9781357dfd7SLiming Sun 9791357dfd7SLiming Sun /* Reset the device. Not much here for now. */ 9801357dfd7SLiming Sun static void mlxbf_tmfifo_virtio_reset(struct virtio_device *vdev) 9811357dfd7SLiming Sun { 9821357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev); 9831357dfd7SLiming Sun 9841357dfd7SLiming Sun tm_vdev->status = 0; 9851357dfd7SLiming Sun } 9861357dfd7SLiming Sun 9871357dfd7SLiming Sun /* Read the value of a configuration field. */ 9881357dfd7SLiming Sun static void mlxbf_tmfifo_virtio_get(struct virtio_device *vdev, 9891357dfd7SLiming Sun unsigned int offset, 9901357dfd7SLiming Sun void *buf, 9911357dfd7SLiming Sun unsigned int len) 9921357dfd7SLiming Sun { 9931357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev); 9941357dfd7SLiming Sun 9951357dfd7SLiming Sun if ((u64)offset + len > sizeof(tm_vdev->config)) 9961357dfd7SLiming Sun return; 9971357dfd7SLiming Sun 9981357dfd7SLiming Sun memcpy(buf, (u8 *)&tm_vdev->config + offset, len); 9991357dfd7SLiming Sun } 10001357dfd7SLiming Sun 10011357dfd7SLiming Sun /* Write the value of a configuration field. */ 10021357dfd7SLiming Sun static void mlxbf_tmfifo_virtio_set(struct virtio_device *vdev, 10031357dfd7SLiming Sun unsigned int offset, 10041357dfd7SLiming Sun const void *buf, 10051357dfd7SLiming Sun unsigned int len) 10061357dfd7SLiming Sun { 10071357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev); 10081357dfd7SLiming Sun 10091357dfd7SLiming Sun if ((u64)offset + len > sizeof(tm_vdev->config)) 10101357dfd7SLiming Sun return; 10111357dfd7SLiming Sun 10121357dfd7SLiming Sun memcpy((u8 *)&tm_vdev->config + offset, buf, len); 10131357dfd7SLiming Sun } 10141357dfd7SLiming Sun 10151357dfd7SLiming Sun static void tmfifo_virtio_dev_release(struct device *device) 10161357dfd7SLiming Sun { 10171357dfd7SLiming Sun struct virtio_device *vdev = 10181357dfd7SLiming Sun container_of(device, struct virtio_device, dev); 10191357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_vdev = mlxbf_vdev_to_tmfifo(vdev); 10201357dfd7SLiming Sun 10211357dfd7SLiming Sun kfree(tm_vdev); 10221357dfd7SLiming Sun } 10231357dfd7SLiming Sun 10241357dfd7SLiming Sun /* Virtio config operations. */ 10251357dfd7SLiming Sun static const struct virtio_config_ops mlxbf_tmfifo_virtio_config_ops = { 10261357dfd7SLiming Sun .get_features = mlxbf_tmfifo_virtio_get_features, 10271357dfd7SLiming Sun .finalize_features = mlxbf_tmfifo_virtio_finalize_features, 10281357dfd7SLiming Sun .find_vqs = mlxbf_tmfifo_virtio_find_vqs, 10291357dfd7SLiming Sun .del_vqs = mlxbf_tmfifo_virtio_del_vqs, 10301357dfd7SLiming Sun .reset = mlxbf_tmfifo_virtio_reset, 10311357dfd7SLiming Sun .set_status = mlxbf_tmfifo_virtio_set_status, 10321357dfd7SLiming Sun .get_status = mlxbf_tmfifo_virtio_get_status, 10331357dfd7SLiming Sun .get = mlxbf_tmfifo_virtio_get, 10341357dfd7SLiming Sun .set = mlxbf_tmfifo_virtio_set, 10351357dfd7SLiming Sun }; 10361357dfd7SLiming Sun 10371357dfd7SLiming Sun /* Create vdev for the FIFO. */ 10381357dfd7SLiming Sun static int mlxbf_tmfifo_create_vdev(struct device *dev, 10391357dfd7SLiming Sun struct mlxbf_tmfifo *fifo, 10401357dfd7SLiming Sun int vdev_id, u64 features, 10411357dfd7SLiming Sun void *config, u32 size) 10421357dfd7SLiming Sun { 10431357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_vdev, *reg_dev = NULL; 10441357dfd7SLiming Sun int ret; 10451357dfd7SLiming Sun 10461357dfd7SLiming Sun mutex_lock(&fifo->lock); 10471357dfd7SLiming Sun 10481357dfd7SLiming Sun tm_vdev = fifo->vdev[vdev_id]; 10491357dfd7SLiming Sun if (tm_vdev) { 10501357dfd7SLiming Sun dev_err(dev, "vdev %d already exists\n", vdev_id); 10511357dfd7SLiming Sun ret = -EEXIST; 10521357dfd7SLiming Sun goto fail; 10531357dfd7SLiming Sun } 10541357dfd7SLiming Sun 10551357dfd7SLiming Sun tm_vdev = kzalloc(sizeof(*tm_vdev), GFP_KERNEL); 10561357dfd7SLiming Sun if (!tm_vdev) { 10571357dfd7SLiming Sun ret = -ENOMEM; 10581357dfd7SLiming Sun goto fail; 10591357dfd7SLiming Sun } 10601357dfd7SLiming Sun 10611357dfd7SLiming Sun tm_vdev->vdev.id.device = vdev_id; 10621357dfd7SLiming Sun tm_vdev->vdev.config = &mlxbf_tmfifo_virtio_config_ops; 10631357dfd7SLiming Sun tm_vdev->vdev.dev.parent = dev; 10641357dfd7SLiming Sun tm_vdev->vdev.dev.release = tmfifo_virtio_dev_release; 10651357dfd7SLiming Sun tm_vdev->features = features; 10661357dfd7SLiming Sun if (config) 10671357dfd7SLiming Sun memcpy(&tm_vdev->config, config, size); 10681357dfd7SLiming Sun 10691357dfd7SLiming Sun if (mlxbf_tmfifo_alloc_vrings(fifo, tm_vdev)) { 10701357dfd7SLiming Sun dev_err(dev, "unable to allocate vring\n"); 10711357dfd7SLiming Sun ret = -ENOMEM; 10721357dfd7SLiming Sun goto vdev_fail; 10731357dfd7SLiming Sun } 10741357dfd7SLiming Sun 10751357dfd7SLiming Sun /* Allocate an output buffer for the console device. */ 10761357dfd7SLiming Sun if (vdev_id == VIRTIO_ID_CONSOLE) 10771357dfd7SLiming Sun tm_vdev->tx_buf.buf = devm_kmalloc(dev, 10781357dfd7SLiming Sun MLXBF_TMFIFO_CON_TX_BUF_SIZE, 10791357dfd7SLiming Sun GFP_KERNEL); 10801357dfd7SLiming Sun fifo->vdev[vdev_id] = tm_vdev; 10811357dfd7SLiming Sun 10821357dfd7SLiming Sun /* Register the virtio device. */ 10831357dfd7SLiming Sun ret = register_virtio_device(&tm_vdev->vdev); 10841357dfd7SLiming Sun reg_dev = tm_vdev; 10851357dfd7SLiming Sun if (ret) { 10861357dfd7SLiming Sun dev_err(dev, "register_virtio_device failed\n"); 10871357dfd7SLiming Sun goto vdev_fail; 10881357dfd7SLiming Sun } 10891357dfd7SLiming Sun 10901357dfd7SLiming Sun mutex_unlock(&fifo->lock); 10911357dfd7SLiming Sun return 0; 10921357dfd7SLiming Sun 10931357dfd7SLiming Sun vdev_fail: 10941357dfd7SLiming Sun mlxbf_tmfifo_free_vrings(fifo, tm_vdev); 10951357dfd7SLiming Sun fifo->vdev[vdev_id] = NULL; 10961357dfd7SLiming Sun if (reg_dev) 10971357dfd7SLiming Sun put_device(&tm_vdev->vdev.dev); 10981357dfd7SLiming Sun else 10991357dfd7SLiming Sun kfree(tm_vdev); 11001357dfd7SLiming Sun fail: 11011357dfd7SLiming Sun mutex_unlock(&fifo->lock); 11021357dfd7SLiming Sun return ret; 11031357dfd7SLiming Sun } 11041357dfd7SLiming Sun 11051357dfd7SLiming Sun /* Delete vdev for the FIFO. */ 11061357dfd7SLiming Sun static int mlxbf_tmfifo_delete_vdev(struct mlxbf_tmfifo *fifo, int vdev_id) 11071357dfd7SLiming Sun { 11081357dfd7SLiming Sun struct mlxbf_tmfifo_vdev *tm_vdev; 11091357dfd7SLiming Sun 11101357dfd7SLiming Sun mutex_lock(&fifo->lock); 11111357dfd7SLiming Sun 11121357dfd7SLiming Sun /* Unregister vdev. */ 11131357dfd7SLiming Sun tm_vdev = fifo->vdev[vdev_id]; 11141357dfd7SLiming Sun if (tm_vdev) { 11151357dfd7SLiming Sun unregister_virtio_device(&tm_vdev->vdev); 11161357dfd7SLiming Sun mlxbf_tmfifo_free_vrings(fifo, tm_vdev); 11171357dfd7SLiming Sun fifo->vdev[vdev_id] = NULL; 11181357dfd7SLiming Sun } 11191357dfd7SLiming Sun 11201357dfd7SLiming Sun mutex_unlock(&fifo->lock); 11211357dfd7SLiming Sun 11221357dfd7SLiming Sun return 0; 11231357dfd7SLiming Sun } 11241357dfd7SLiming Sun 11251357dfd7SLiming Sun /* Read the configured network MAC address from efi variable. */ 11261357dfd7SLiming Sun static void mlxbf_tmfifo_get_cfg_mac(u8 *mac) 11271357dfd7SLiming Sun { 11281357dfd7SLiming Sun efi_guid_t guid = EFI_GLOBAL_VARIABLE_GUID; 11291357dfd7SLiming Sun unsigned long size = ETH_ALEN; 11301357dfd7SLiming Sun u8 buf[ETH_ALEN]; 11311357dfd7SLiming Sun efi_status_t rc; 11321357dfd7SLiming Sun 11331357dfd7SLiming Sun rc = efi.get_variable(mlxbf_tmfifo_efi_name, &guid, NULL, &size, buf); 11341357dfd7SLiming Sun if (rc == EFI_SUCCESS && size == ETH_ALEN) 11351357dfd7SLiming Sun ether_addr_copy(mac, buf); 11361357dfd7SLiming Sun else 11371357dfd7SLiming Sun ether_addr_copy(mac, mlxbf_tmfifo_net_default_mac); 11381357dfd7SLiming Sun } 11391357dfd7SLiming Sun 11401357dfd7SLiming Sun /* Set TmFifo thresolds which is used to trigger interrupts. */ 11411357dfd7SLiming Sun static void mlxbf_tmfifo_set_threshold(struct mlxbf_tmfifo *fifo) 11421357dfd7SLiming Sun { 11431357dfd7SLiming Sun u64 ctl; 11441357dfd7SLiming Sun 11451357dfd7SLiming Sun /* Get Tx FIFO size and set the low/high watermark. */ 11461357dfd7SLiming Sun ctl = readq(fifo->tx_base + MLXBF_TMFIFO_TX_CTL); 11471357dfd7SLiming Sun fifo->tx_fifo_size = 11481357dfd7SLiming Sun FIELD_GET(MLXBF_TMFIFO_TX_CTL__MAX_ENTRIES_MASK, ctl); 11491357dfd7SLiming Sun ctl = (ctl & ~MLXBF_TMFIFO_TX_CTL__LWM_MASK) | 11501357dfd7SLiming Sun FIELD_PREP(MLXBF_TMFIFO_TX_CTL__LWM_MASK, 11511357dfd7SLiming Sun fifo->tx_fifo_size / 2); 11521357dfd7SLiming Sun ctl = (ctl & ~MLXBF_TMFIFO_TX_CTL__HWM_MASK) | 11531357dfd7SLiming Sun FIELD_PREP(MLXBF_TMFIFO_TX_CTL__HWM_MASK, 11541357dfd7SLiming Sun fifo->tx_fifo_size - 1); 11551357dfd7SLiming Sun writeq(ctl, fifo->tx_base + MLXBF_TMFIFO_TX_CTL); 11561357dfd7SLiming Sun 11571357dfd7SLiming Sun /* Get Rx FIFO size and set the low/high watermark. */ 11581357dfd7SLiming Sun ctl = readq(fifo->rx_base + MLXBF_TMFIFO_RX_CTL); 11591357dfd7SLiming Sun fifo->rx_fifo_size = 11601357dfd7SLiming Sun FIELD_GET(MLXBF_TMFIFO_RX_CTL__MAX_ENTRIES_MASK, ctl); 11611357dfd7SLiming Sun ctl = (ctl & ~MLXBF_TMFIFO_RX_CTL__LWM_MASK) | 11621357dfd7SLiming Sun FIELD_PREP(MLXBF_TMFIFO_RX_CTL__LWM_MASK, 0); 11631357dfd7SLiming Sun ctl = (ctl & ~MLXBF_TMFIFO_RX_CTL__HWM_MASK) | 11641357dfd7SLiming Sun FIELD_PREP(MLXBF_TMFIFO_RX_CTL__HWM_MASK, 1); 11651357dfd7SLiming Sun writeq(ctl, fifo->rx_base + MLXBF_TMFIFO_RX_CTL); 11661357dfd7SLiming Sun } 11671357dfd7SLiming Sun 11681357dfd7SLiming Sun static void mlxbf_tmfifo_cleanup(struct mlxbf_tmfifo *fifo) 11691357dfd7SLiming Sun { 11701357dfd7SLiming Sun int i; 11711357dfd7SLiming Sun 11721357dfd7SLiming Sun fifo->is_ready = false; 11731357dfd7SLiming Sun del_timer_sync(&fifo->timer); 11741357dfd7SLiming Sun mlxbf_tmfifo_disable_irqs(fifo); 11751357dfd7SLiming Sun cancel_work_sync(&fifo->work); 11761357dfd7SLiming Sun for (i = 0; i < MLXBF_TMFIFO_VDEV_MAX; i++) 11771357dfd7SLiming Sun mlxbf_tmfifo_delete_vdev(fifo, i); 11781357dfd7SLiming Sun } 11791357dfd7SLiming Sun 11801357dfd7SLiming Sun /* Probe the TMFIFO. */ 11811357dfd7SLiming Sun static int mlxbf_tmfifo_probe(struct platform_device *pdev) 11821357dfd7SLiming Sun { 11831357dfd7SLiming Sun struct virtio_net_config net_config; 11841357dfd7SLiming Sun struct device *dev = &pdev->dev; 11851357dfd7SLiming Sun struct mlxbf_tmfifo *fifo; 11861357dfd7SLiming Sun int i, rc; 11871357dfd7SLiming Sun 11881357dfd7SLiming Sun fifo = devm_kzalloc(dev, sizeof(*fifo), GFP_KERNEL); 11891357dfd7SLiming Sun if (!fifo) 11901357dfd7SLiming Sun return -ENOMEM; 11911357dfd7SLiming Sun 1192*638bc4caSLiming Sun spin_lock_init(&fifo->spin_lock[0]); 1193*638bc4caSLiming Sun spin_lock_init(&fifo->spin_lock[1]); 11941357dfd7SLiming Sun INIT_WORK(&fifo->work, mlxbf_tmfifo_work_handler); 11951357dfd7SLiming Sun mutex_init(&fifo->lock); 11961357dfd7SLiming Sun 11971357dfd7SLiming Sun /* Get the resource of the Rx FIFO. */ 11981357dfd7SLiming Sun fifo->rx_base = devm_platform_ioremap_resource(pdev, 0); 11991357dfd7SLiming Sun if (IS_ERR(fifo->rx_base)) 12001357dfd7SLiming Sun return PTR_ERR(fifo->rx_base); 12011357dfd7SLiming Sun 12021357dfd7SLiming Sun /* Get the resource of the Tx FIFO. */ 12031357dfd7SLiming Sun fifo->tx_base = devm_platform_ioremap_resource(pdev, 1); 12041357dfd7SLiming Sun if (IS_ERR(fifo->tx_base)) 12051357dfd7SLiming Sun return PTR_ERR(fifo->tx_base); 12061357dfd7SLiming Sun 12071357dfd7SLiming Sun platform_set_drvdata(pdev, fifo); 12081357dfd7SLiming Sun 12091357dfd7SLiming Sun timer_setup(&fifo->timer, mlxbf_tmfifo_timer, 0); 12101357dfd7SLiming Sun 12111357dfd7SLiming Sun for (i = 0; i < MLXBF_TM_MAX_IRQ; i++) { 12121357dfd7SLiming Sun fifo->irq_info[i].index = i; 12131357dfd7SLiming Sun fifo->irq_info[i].fifo = fifo; 12141357dfd7SLiming Sun fifo->irq_info[i].irq = platform_get_irq(pdev, i); 12151357dfd7SLiming Sun rc = devm_request_irq(dev, fifo->irq_info[i].irq, 12161357dfd7SLiming Sun mlxbf_tmfifo_irq_handler, 0, 12171357dfd7SLiming Sun "tmfifo", &fifo->irq_info[i]); 12181357dfd7SLiming Sun if (rc) { 12191357dfd7SLiming Sun dev_err(dev, "devm_request_irq failed\n"); 12201357dfd7SLiming Sun fifo->irq_info[i].irq = 0; 12211357dfd7SLiming Sun return rc; 12221357dfd7SLiming Sun } 12231357dfd7SLiming Sun } 12241357dfd7SLiming Sun 12251357dfd7SLiming Sun mlxbf_tmfifo_set_threshold(fifo); 12261357dfd7SLiming Sun 12271357dfd7SLiming Sun /* Create the console vdev. */ 12281357dfd7SLiming Sun rc = mlxbf_tmfifo_create_vdev(dev, fifo, VIRTIO_ID_CONSOLE, 0, NULL, 0); 12291357dfd7SLiming Sun if (rc) 12301357dfd7SLiming Sun goto fail; 12311357dfd7SLiming Sun 12321357dfd7SLiming Sun /* Create the network vdev. */ 12331357dfd7SLiming Sun memset(&net_config, 0, sizeof(net_config)); 12341357dfd7SLiming Sun net_config.mtu = ETH_DATA_LEN; 12351357dfd7SLiming Sun net_config.status = VIRTIO_NET_S_LINK_UP; 12361357dfd7SLiming Sun mlxbf_tmfifo_get_cfg_mac(net_config.mac); 12371357dfd7SLiming Sun rc = mlxbf_tmfifo_create_vdev(dev, fifo, VIRTIO_ID_NET, 12381357dfd7SLiming Sun MLXBF_TMFIFO_NET_FEATURES, &net_config, 12391357dfd7SLiming Sun sizeof(net_config)); 12401357dfd7SLiming Sun if (rc) 12411357dfd7SLiming Sun goto fail; 12421357dfd7SLiming Sun 12431357dfd7SLiming Sun mod_timer(&fifo->timer, jiffies + MLXBF_TMFIFO_TIMER_INTERVAL); 12441357dfd7SLiming Sun 12451357dfd7SLiming Sun fifo->is_ready = true; 12461357dfd7SLiming Sun return 0; 12471357dfd7SLiming Sun 12481357dfd7SLiming Sun fail: 12491357dfd7SLiming Sun mlxbf_tmfifo_cleanup(fifo); 12501357dfd7SLiming Sun return rc; 12511357dfd7SLiming Sun } 12521357dfd7SLiming Sun 12531357dfd7SLiming Sun /* Device remove function. */ 12541357dfd7SLiming Sun static int mlxbf_tmfifo_remove(struct platform_device *pdev) 12551357dfd7SLiming Sun { 12561357dfd7SLiming Sun struct mlxbf_tmfifo *fifo = platform_get_drvdata(pdev); 12571357dfd7SLiming Sun 12581357dfd7SLiming Sun mlxbf_tmfifo_cleanup(fifo); 12591357dfd7SLiming Sun 12601357dfd7SLiming Sun return 0; 12611357dfd7SLiming Sun } 12621357dfd7SLiming Sun 12631357dfd7SLiming Sun static const struct acpi_device_id mlxbf_tmfifo_acpi_match[] = { 12641357dfd7SLiming Sun { "MLNXBF01", 0 }, 12651357dfd7SLiming Sun {} 12661357dfd7SLiming Sun }; 12671357dfd7SLiming Sun MODULE_DEVICE_TABLE(acpi, mlxbf_tmfifo_acpi_match); 12681357dfd7SLiming Sun 12691357dfd7SLiming Sun static struct platform_driver mlxbf_tmfifo_driver = { 12701357dfd7SLiming Sun .probe = mlxbf_tmfifo_probe, 12711357dfd7SLiming Sun .remove = mlxbf_tmfifo_remove, 12721357dfd7SLiming Sun .driver = { 12731357dfd7SLiming Sun .name = "bf-tmfifo", 12741357dfd7SLiming Sun .acpi_match_table = mlxbf_tmfifo_acpi_match, 12751357dfd7SLiming Sun }, 12761357dfd7SLiming Sun }; 12771357dfd7SLiming Sun 12781357dfd7SLiming Sun module_platform_driver(mlxbf_tmfifo_driver); 12791357dfd7SLiming Sun 12801357dfd7SLiming Sun MODULE_DESCRIPTION("Mellanox BlueField SoC TmFifo Driver"); 12811357dfd7SLiming Sun MODULE_LICENSE("GPL v2"); 12821357dfd7SLiming Sun MODULE_AUTHOR("Mellanox Technologies"); 1283