18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ISHTP Ring Buffers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2003-2016, Intel Corporation. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include "client.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/** 128c2ecf20Sopenharmony_ci * ishtp_cl_alloc_rx_ring() - Allocate RX ring buffers 138c2ecf20Sopenharmony_ci * @cl: client device instance 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Allocate and initialize RX ring buffers 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Return: 0 on success else -ENOMEM 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ciint ishtp_cl_alloc_rx_ring(struct ishtp_cl *cl) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci size_t len = cl->device->fw_client->props.max_msg_length; 228c2ecf20Sopenharmony_ci int j; 238c2ecf20Sopenharmony_ci struct ishtp_cl_rb *rb; 248c2ecf20Sopenharmony_ci int ret = 0; 258c2ecf20Sopenharmony_ci unsigned long flags; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci for (j = 0; j < cl->rx_ring_size; ++j) { 288c2ecf20Sopenharmony_ci rb = ishtp_io_rb_init(cl); 298c2ecf20Sopenharmony_ci if (!rb) { 308c2ecf20Sopenharmony_ci ret = -ENOMEM; 318c2ecf20Sopenharmony_ci goto out; 328c2ecf20Sopenharmony_ci } 338c2ecf20Sopenharmony_ci ret = ishtp_io_rb_alloc_buf(rb, len); 348c2ecf20Sopenharmony_ci if (ret) 358c2ecf20Sopenharmony_ci goto out; 368c2ecf20Sopenharmony_ci spin_lock_irqsave(&cl->free_list_spinlock, flags); 378c2ecf20Sopenharmony_ci list_add_tail(&rb->list, &cl->free_rb_list.list); 388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cl->free_list_spinlock, flags); 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return 0; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ciout: 448c2ecf20Sopenharmony_ci dev_err(&cl->device->dev, "error in allocating Rx buffers\n"); 458c2ecf20Sopenharmony_ci ishtp_cl_free_rx_ring(cl); 468c2ecf20Sopenharmony_ci return ret; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/** 508c2ecf20Sopenharmony_ci * ishtp_cl_alloc_tx_ring() - Allocate TX ring buffers 518c2ecf20Sopenharmony_ci * @cl: client device instance 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * Allocate and initialize TX ring buffers 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * Return: 0 on success else -ENOMEM 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ciint ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci size_t len = cl->device->fw_client->props.max_msg_length; 608c2ecf20Sopenharmony_ci int j; 618c2ecf20Sopenharmony_ci unsigned long flags; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci cl->tx_ring_free_size = 0; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* Allocate pool to free Tx bufs */ 668c2ecf20Sopenharmony_ci for (j = 0; j < cl->tx_ring_size; ++j) { 678c2ecf20Sopenharmony_ci struct ishtp_cl_tx_ring *tx_buf; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci tx_buf = kzalloc(sizeof(struct ishtp_cl_tx_ring), GFP_KERNEL); 708c2ecf20Sopenharmony_ci if (!tx_buf) 718c2ecf20Sopenharmony_ci goto out; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci tx_buf->send_buf.data = kmalloc(len, GFP_KERNEL); 748c2ecf20Sopenharmony_ci if (!tx_buf->send_buf.data) { 758c2ecf20Sopenharmony_ci kfree(tx_buf); 768c2ecf20Sopenharmony_ci goto out; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci spin_lock_irqsave(&cl->tx_free_list_spinlock, flags); 808c2ecf20Sopenharmony_ci list_add_tail(&tx_buf->list, &cl->tx_free_list.list); 818c2ecf20Sopenharmony_ci ++cl->tx_ring_free_size; 828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cl->tx_free_list_spinlock, flags); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ciout: 868c2ecf20Sopenharmony_ci dev_err(&cl->device->dev, "error in allocating Tx pool\n"); 878c2ecf20Sopenharmony_ci ishtp_cl_free_tx_ring(cl); 888c2ecf20Sopenharmony_ci return -ENOMEM; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/** 928c2ecf20Sopenharmony_ci * ishtp_cl_free_rx_ring() - Free RX ring buffers 938c2ecf20Sopenharmony_ci * @cl: client device instance 948c2ecf20Sopenharmony_ci * 958c2ecf20Sopenharmony_ci * Free RX ring buffers 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_civoid ishtp_cl_free_rx_ring(struct ishtp_cl *cl) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct ishtp_cl_rb *rb; 1008c2ecf20Sopenharmony_ci unsigned long flags; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* release allocated memory - pass over free_rb_list */ 1038c2ecf20Sopenharmony_ci spin_lock_irqsave(&cl->free_list_spinlock, flags); 1048c2ecf20Sopenharmony_ci while (!list_empty(&cl->free_rb_list.list)) { 1058c2ecf20Sopenharmony_ci rb = list_entry(cl->free_rb_list.list.next, struct ishtp_cl_rb, 1068c2ecf20Sopenharmony_ci list); 1078c2ecf20Sopenharmony_ci list_del(&rb->list); 1088c2ecf20Sopenharmony_ci kfree(rb->buffer.data); 1098c2ecf20Sopenharmony_ci kfree(rb); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cl->free_list_spinlock, flags); 1128c2ecf20Sopenharmony_ci /* release allocated memory - pass over in_process_list */ 1138c2ecf20Sopenharmony_ci spin_lock_irqsave(&cl->in_process_spinlock, flags); 1148c2ecf20Sopenharmony_ci while (!list_empty(&cl->in_process_list.list)) { 1158c2ecf20Sopenharmony_ci rb = list_entry(cl->in_process_list.list.next, 1168c2ecf20Sopenharmony_ci struct ishtp_cl_rb, list); 1178c2ecf20Sopenharmony_ci list_del(&rb->list); 1188c2ecf20Sopenharmony_ci kfree(rb->buffer.data); 1198c2ecf20Sopenharmony_ci kfree(rb); 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cl->in_process_spinlock, flags); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/** 1258c2ecf20Sopenharmony_ci * ishtp_cl_free_tx_ring() - Free TX ring buffers 1268c2ecf20Sopenharmony_ci * @cl: client device instance 1278c2ecf20Sopenharmony_ci * 1288c2ecf20Sopenharmony_ci * Free TX ring buffers 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_civoid ishtp_cl_free_tx_ring(struct ishtp_cl *cl) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct ishtp_cl_tx_ring *tx_buf; 1338c2ecf20Sopenharmony_ci unsigned long flags; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci spin_lock_irqsave(&cl->tx_free_list_spinlock, flags); 1368c2ecf20Sopenharmony_ci /* release allocated memory - pass over tx_free_list */ 1378c2ecf20Sopenharmony_ci while (!list_empty(&cl->tx_free_list.list)) { 1388c2ecf20Sopenharmony_ci tx_buf = list_entry(cl->tx_free_list.list.next, 1398c2ecf20Sopenharmony_ci struct ishtp_cl_tx_ring, list); 1408c2ecf20Sopenharmony_ci list_del(&tx_buf->list); 1418c2ecf20Sopenharmony_ci --cl->tx_ring_free_size; 1428c2ecf20Sopenharmony_ci kfree(tx_buf->send_buf.data); 1438c2ecf20Sopenharmony_ci kfree(tx_buf); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cl->tx_free_list_spinlock, flags); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci spin_lock_irqsave(&cl->tx_list_spinlock, flags); 1488c2ecf20Sopenharmony_ci /* release allocated memory - pass over tx_list */ 1498c2ecf20Sopenharmony_ci while (!list_empty(&cl->tx_list.list)) { 1508c2ecf20Sopenharmony_ci tx_buf = list_entry(cl->tx_list.list.next, 1518c2ecf20Sopenharmony_ci struct ishtp_cl_tx_ring, list); 1528c2ecf20Sopenharmony_ci list_del(&tx_buf->list); 1538c2ecf20Sopenharmony_ci kfree(tx_buf->send_buf.data); 1548c2ecf20Sopenharmony_ci kfree(tx_buf); 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cl->tx_list_spinlock, flags); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/** 1608c2ecf20Sopenharmony_ci * ishtp_io_rb_free() - Free IO request block 1618c2ecf20Sopenharmony_ci * @rb: IO request block 1628c2ecf20Sopenharmony_ci * 1638c2ecf20Sopenharmony_ci * Free io request block memory 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_civoid ishtp_io_rb_free(struct ishtp_cl_rb *rb) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci if (rb == NULL) 1688c2ecf20Sopenharmony_ci return; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci kfree(rb->buffer.data); 1718c2ecf20Sopenharmony_ci kfree(rb); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/** 1758c2ecf20Sopenharmony_ci * ishtp_io_rb_init() - Allocate and init IO request block 1768c2ecf20Sopenharmony_ci * @cl: client device instance 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci * Allocate and initialize request block 1798c2ecf20Sopenharmony_ci * 1808c2ecf20Sopenharmony_ci * Return: Allocted IO request block pointer 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_cistruct ishtp_cl_rb *ishtp_io_rb_init(struct ishtp_cl *cl) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct ishtp_cl_rb *rb; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci rb = kzalloc(sizeof(struct ishtp_cl_rb), GFP_KERNEL); 1878c2ecf20Sopenharmony_ci if (!rb) 1888c2ecf20Sopenharmony_ci return NULL; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rb->list); 1918c2ecf20Sopenharmony_ci rb->cl = cl; 1928c2ecf20Sopenharmony_ci rb->buf_idx = 0; 1938c2ecf20Sopenharmony_ci return rb; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/** 1978c2ecf20Sopenharmony_ci * ishtp_io_rb_alloc_buf() - Allocate and init response buffer 1988c2ecf20Sopenharmony_ci * @rb: IO request block 1998c2ecf20Sopenharmony_ci * @length: length of response buffer 2008c2ecf20Sopenharmony_ci * 2018c2ecf20Sopenharmony_ci * Allocate respose buffer 2028c2ecf20Sopenharmony_ci * 2038c2ecf20Sopenharmony_ci * Return: 0 on success else -ENOMEM 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_ciint ishtp_io_rb_alloc_buf(struct ishtp_cl_rb *rb, size_t length) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci if (!rb) 2088c2ecf20Sopenharmony_ci return -EINVAL; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (length == 0) 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci rb->buffer.data = kmalloc(length, GFP_KERNEL); 2148c2ecf20Sopenharmony_ci if (!rb->buffer.data) 2158c2ecf20Sopenharmony_ci return -ENOMEM; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci rb->buffer.size = length; 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/** 2228c2ecf20Sopenharmony_ci * ishtp_cl_io_rb_recycle() - Recycle IO request blocks 2238c2ecf20Sopenharmony_ci * @rb: IO request block 2248c2ecf20Sopenharmony_ci * 2258c2ecf20Sopenharmony_ci * Re-append rb to its client's free list and send flow control if needed 2268c2ecf20Sopenharmony_ci * 2278c2ecf20Sopenharmony_ci * Return: 0 on success else -EFAULT 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_ciint ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct ishtp_cl *cl; 2328c2ecf20Sopenharmony_ci int rets = 0; 2338c2ecf20Sopenharmony_ci unsigned long flags; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (!rb || !rb->cl) 2368c2ecf20Sopenharmony_ci return -EFAULT; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci cl = rb->cl; 2398c2ecf20Sopenharmony_ci spin_lock_irqsave(&cl->free_list_spinlock, flags); 2408c2ecf20Sopenharmony_ci list_add_tail(&rb->list, &cl->free_rb_list.list); 2418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cl->free_list_spinlock, flags); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* 2448c2ecf20Sopenharmony_ci * If we returned the first buffer to empty 'free' list, 2458c2ecf20Sopenharmony_ci * send flow control 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_ci if (!cl->out_flow_ctrl_creds) 2488c2ecf20Sopenharmony_ci rets = ishtp_cl_read_start(cl); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return rets; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_cl_io_rb_recycle); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/** 2558c2ecf20Sopenharmony_ci * ishtp_cl_tx_empty() -test whether client device tx buffer is empty 2568c2ecf20Sopenharmony_ci * @cl: Pointer to client device instance 2578c2ecf20Sopenharmony_ci * 2588c2ecf20Sopenharmony_ci * Look client device tx buffer list, and check whether this list is empty 2598c2ecf20Sopenharmony_ci * 2608c2ecf20Sopenharmony_ci * Return: true if client tx buffer list is empty else false 2618c2ecf20Sopenharmony_ci */ 2628c2ecf20Sopenharmony_cibool ishtp_cl_tx_empty(struct ishtp_cl *cl) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci int tx_list_empty; 2658c2ecf20Sopenharmony_ci unsigned long tx_flags; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci spin_lock_irqsave(&cl->tx_list_spinlock, tx_flags); 2688c2ecf20Sopenharmony_ci tx_list_empty = list_empty(&cl->tx_list.list); 2698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return !!tx_list_empty; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_cl_tx_empty); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci/** 2768c2ecf20Sopenharmony_ci * ishtp_cl_rx_get_rb() -Get a rb from client device rx buffer list 2778c2ecf20Sopenharmony_ci * @cl: Pointer to client device instance 2788c2ecf20Sopenharmony_ci * 2798c2ecf20Sopenharmony_ci * Check client device in-processing buffer list and get a rb from it. 2808c2ecf20Sopenharmony_ci * 2818c2ecf20Sopenharmony_ci * Return: rb pointer if buffer list isn't empty else NULL 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_cistruct ishtp_cl_rb *ishtp_cl_rx_get_rb(struct ishtp_cl *cl) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci unsigned long rx_flags; 2868c2ecf20Sopenharmony_ci struct ishtp_cl_rb *rb; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci spin_lock_irqsave(&cl->in_process_spinlock, rx_flags); 2898c2ecf20Sopenharmony_ci rb = list_first_entry_or_null(&cl->in_process_list.list, 2908c2ecf20Sopenharmony_ci struct ishtp_cl_rb, list); 2918c2ecf20Sopenharmony_ci if (rb) 2928c2ecf20Sopenharmony_ci list_del_init(&rb->list); 2938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cl->in_process_spinlock, rx_flags); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return rb; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ishtp_cl_rx_get_rb); 298