162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ISHTP Ring Buffers 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2003-2016, Intel Corporation. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include "client.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/** 1262306a36Sopenharmony_ci * ishtp_cl_alloc_rx_ring() - Allocate RX ring buffers 1362306a36Sopenharmony_ci * @cl: client device instance 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * Allocate and initialize RX ring buffers 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Return: 0 on success else -ENOMEM 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ciint ishtp_cl_alloc_rx_ring(struct ishtp_cl *cl) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci size_t len = cl->device->fw_client->props.max_msg_length; 2262306a36Sopenharmony_ci int j; 2362306a36Sopenharmony_ci struct ishtp_cl_rb *rb; 2462306a36Sopenharmony_ci int ret = 0; 2562306a36Sopenharmony_ci unsigned long flags; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci for (j = 0; j < cl->rx_ring_size; ++j) { 2862306a36Sopenharmony_ci rb = ishtp_io_rb_init(cl); 2962306a36Sopenharmony_ci if (!rb) { 3062306a36Sopenharmony_ci ret = -ENOMEM; 3162306a36Sopenharmony_ci goto out; 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci ret = ishtp_io_rb_alloc_buf(rb, len); 3462306a36Sopenharmony_ci if (ret) 3562306a36Sopenharmony_ci goto out; 3662306a36Sopenharmony_ci spin_lock_irqsave(&cl->free_list_spinlock, flags); 3762306a36Sopenharmony_ci list_add_tail(&rb->list, &cl->free_rb_list.list); 3862306a36Sopenharmony_ci spin_unlock_irqrestore(&cl->free_list_spinlock, flags); 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci return 0; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciout: 4462306a36Sopenharmony_ci dev_err(&cl->device->dev, "error in allocating Rx buffers\n"); 4562306a36Sopenharmony_ci ishtp_cl_free_rx_ring(cl); 4662306a36Sopenharmony_ci return ret; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/** 5062306a36Sopenharmony_ci * ishtp_cl_alloc_tx_ring() - Allocate TX ring buffers 5162306a36Sopenharmony_ci * @cl: client device instance 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * Allocate and initialize TX ring buffers 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * Return: 0 on success else -ENOMEM 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ciint ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci size_t len = cl->device->fw_client->props.max_msg_length; 6062306a36Sopenharmony_ci int j; 6162306a36Sopenharmony_ci unsigned long flags; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci cl->tx_ring_free_size = 0; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* Allocate pool to free Tx bufs */ 6662306a36Sopenharmony_ci for (j = 0; j < cl->tx_ring_size; ++j) { 6762306a36Sopenharmony_ci struct ishtp_cl_tx_ring *tx_buf; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci tx_buf = kzalloc(sizeof(struct ishtp_cl_tx_ring), GFP_KERNEL); 7062306a36Sopenharmony_ci if (!tx_buf) 7162306a36Sopenharmony_ci goto out; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci tx_buf->send_buf.data = kmalloc(len, GFP_KERNEL); 7462306a36Sopenharmony_ci if (!tx_buf->send_buf.data) { 7562306a36Sopenharmony_ci kfree(tx_buf); 7662306a36Sopenharmony_ci goto out; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci spin_lock_irqsave(&cl->tx_free_list_spinlock, flags); 8062306a36Sopenharmony_ci list_add_tail(&tx_buf->list, &cl->tx_free_list.list); 8162306a36Sopenharmony_ci ++cl->tx_ring_free_size; 8262306a36Sopenharmony_ci spin_unlock_irqrestore(&cl->tx_free_list_spinlock, flags); 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ciout: 8662306a36Sopenharmony_ci dev_err(&cl->device->dev, "error in allocating Tx pool\n"); 8762306a36Sopenharmony_ci ishtp_cl_free_tx_ring(cl); 8862306a36Sopenharmony_ci return -ENOMEM; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/** 9262306a36Sopenharmony_ci * ishtp_cl_free_rx_ring() - Free RX ring buffers 9362306a36Sopenharmony_ci * @cl: client device instance 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * Free RX ring buffers 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_civoid ishtp_cl_free_rx_ring(struct ishtp_cl *cl) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct ishtp_cl_rb *rb; 10062306a36Sopenharmony_ci unsigned long flags; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* release allocated memory - pass over free_rb_list */ 10362306a36Sopenharmony_ci spin_lock_irqsave(&cl->free_list_spinlock, flags); 10462306a36Sopenharmony_ci while (!list_empty(&cl->free_rb_list.list)) { 10562306a36Sopenharmony_ci rb = list_entry(cl->free_rb_list.list.next, struct ishtp_cl_rb, 10662306a36Sopenharmony_ci list); 10762306a36Sopenharmony_ci list_del(&rb->list); 10862306a36Sopenharmony_ci kfree(rb->buffer.data); 10962306a36Sopenharmony_ci kfree(rb); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci spin_unlock_irqrestore(&cl->free_list_spinlock, flags); 11262306a36Sopenharmony_ci /* release allocated memory - pass over in_process_list */ 11362306a36Sopenharmony_ci spin_lock_irqsave(&cl->in_process_spinlock, flags); 11462306a36Sopenharmony_ci while (!list_empty(&cl->in_process_list.list)) { 11562306a36Sopenharmony_ci rb = list_entry(cl->in_process_list.list.next, 11662306a36Sopenharmony_ci struct ishtp_cl_rb, list); 11762306a36Sopenharmony_ci list_del(&rb->list); 11862306a36Sopenharmony_ci kfree(rb->buffer.data); 11962306a36Sopenharmony_ci kfree(rb); 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci spin_unlock_irqrestore(&cl->in_process_spinlock, flags); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/** 12562306a36Sopenharmony_ci * ishtp_cl_free_tx_ring() - Free TX ring buffers 12662306a36Sopenharmony_ci * @cl: client device instance 12762306a36Sopenharmony_ci * 12862306a36Sopenharmony_ci * Free TX ring buffers 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_civoid ishtp_cl_free_tx_ring(struct ishtp_cl *cl) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct ishtp_cl_tx_ring *tx_buf; 13362306a36Sopenharmony_ci unsigned long flags; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci spin_lock_irqsave(&cl->tx_free_list_spinlock, flags); 13662306a36Sopenharmony_ci /* release allocated memory - pass over tx_free_list */ 13762306a36Sopenharmony_ci while (!list_empty(&cl->tx_free_list.list)) { 13862306a36Sopenharmony_ci tx_buf = list_entry(cl->tx_free_list.list.next, 13962306a36Sopenharmony_ci struct ishtp_cl_tx_ring, list); 14062306a36Sopenharmony_ci list_del(&tx_buf->list); 14162306a36Sopenharmony_ci --cl->tx_ring_free_size; 14262306a36Sopenharmony_ci kfree(tx_buf->send_buf.data); 14362306a36Sopenharmony_ci kfree(tx_buf); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci spin_unlock_irqrestore(&cl->tx_free_list_spinlock, flags); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci spin_lock_irqsave(&cl->tx_list_spinlock, flags); 14862306a36Sopenharmony_ci /* release allocated memory - pass over tx_list */ 14962306a36Sopenharmony_ci while (!list_empty(&cl->tx_list.list)) { 15062306a36Sopenharmony_ci tx_buf = list_entry(cl->tx_list.list.next, 15162306a36Sopenharmony_ci struct ishtp_cl_tx_ring, list); 15262306a36Sopenharmony_ci list_del(&tx_buf->list); 15362306a36Sopenharmony_ci kfree(tx_buf->send_buf.data); 15462306a36Sopenharmony_ci kfree(tx_buf); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci spin_unlock_irqrestore(&cl->tx_list_spinlock, flags); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/** 16062306a36Sopenharmony_ci * ishtp_io_rb_free() - Free IO request block 16162306a36Sopenharmony_ci * @rb: IO request block 16262306a36Sopenharmony_ci * 16362306a36Sopenharmony_ci * Free io request block memory 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_civoid ishtp_io_rb_free(struct ishtp_cl_rb *rb) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci if (rb == NULL) 16862306a36Sopenharmony_ci return; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci kfree(rb->buffer.data); 17162306a36Sopenharmony_ci kfree(rb); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/** 17562306a36Sopenharmony_ci * ishtp_io_rb_init() - Allocate and init IO request block 17662306a36Sopenharmony_ci * @cl: client device instance 17762306a36Sopenharmony_ci * 17862306a36Sopenharmony_ci * Allocate and initialize request block 17962306a36Sopenharmony_ci * 18062306a36Sopenharmony_ci * Return: Allocted IO request block pointer 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_cistruct ishtp_cl_rb *ishtp_io_rb_init(struct ishtp_cl *cl) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct ishtp_cl_rb *rb; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci rb = kzalloc(sizeof(struct ishtp_cl_rb), GFP_KERNEL); 18762306a36Sopenharmony_ci if (!rb) 18862306a36Sopenharmony_ci return NULL; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci INIT_LIST_HEAD(&rb->list); 19162306a36Sopenharmony_ci rb->cl = cl; 19262306a36Sopenharmony_ci rb->buf_idx = 0; 19362306a36Sopenharmony_ci return rb; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/** 19762306a36Sopenharmony_ci * ishtp_io_rb_alloc_buf() - Allocate and init response buffer 19862306a36Sopenharmony_ci * @rb: IO request block 19962306a36Sopenharmony_ci * @length: length of response buffer 20062306a36Sopenharmony_ci * 20162306a36Sopenharmony_ci * Allocate respose buffer 20262306a36Sopenharmony_ci * 20362306a36Sopenharmony_ci * Return: 0 on success else -ENOMEM 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ciint ishtp_io_rb_alloc_buf(struct ishtp_cl_rb *rb, size_t length) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci if (!rb) 20862306a36Sopenharmony_ci return -EINVAL; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (length == 0) 21162306a36Sopenharmony_ci return 0; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci rb->buffer.data = kmalloc(length, GFP_KERNEL); 21462306a36Sopenharmony_ci if (!rb->buffer.data) 21562306a36Sopenharmony_ci return -ENOMEM; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci rb->buffer.size = length; 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/** 22262306a36Sopenharmony_ci * ishtp_cl_io_rb_recycle() - Recycle IO request blocks 22362306a36Sopenharmony_ci * @rb: IO request block 22462306a36Sopenharmony_ci * 22562306a36Sopenharmony_ci * Re-append rb to its client's free list and send flow control if needed 22662306a36Sopenharmony_ci * 22762306a36Sopenharmony_ci * Return: 0 on success else -EFAULT 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ciint ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct ishtp_cl *cl; 23262306a36Sopenharmony_ci int rets = 0; 23362306a36Sopenharmony_ci unsigned long flags; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (!rb || !rb->cl) 23662306a36Sopenharmony_ci return -EFAULT; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci cl = rb->cl; 23962306a36Sopenharmony_ci spin_lock_irqsave(&cl->free_list_spinlock, flags); 24062306a36Sopenharmony_ci list_add_tail(&rb->list, &cl->free_rb_list.list); 24162306a36Sopenharmony_ci spin_unlock_irqrestore(&cl->free_list_spinlock, flags); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* 24462306a36Sopenharmony_ci * If we returned the first buffer to empty 'free' list, 24562306a36Sopenharmony_ci * send flow control 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_ci if (!cl->out_flow_ctrl_creds) 24862306a36Sopenharmony_ci rets = ishtp_cl_read_start(cl); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return rets; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_cl_io_rb_recycle); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/** 25562306a36Sopenharmony_ci * ishtp_cl_tx_empty() -test whether client device tx buffer is empty 25662306a36Sopenharmony_ci * @cl: Pointer to client device instance 25762306a36Sopenharmony_ci * 25862306a36Sopenharmony_ci * Look client device tx buffer list, and check whether this list is empty 25962306a36Sopenharmony_ci * 26062306a36Sopenharmony_ci * Return: true if client tx buffer list is empty else false 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_cibool ishtp_cl_tx_empty(struct ishtp_cl *cl) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci int tx_list_empty; 26562306a36Sopenharmony_ci unsigned long tx_flags; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci spin_lock_irqsave(&cl->tx_list_spinlock, tx_flags); 26862306a36Sopenharmony_ci tx_list_empty = list_empty(&cl->tx_list.list); 26962306a36Sopenharmony_ci spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return !!tx_list_empty; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_cl_tx_empty); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci/** 27662306a36Sopenharmony_ci * ishtp_cl_rx_get_rb() -Get a rb from client device rx buffer list 27762306a36Sopenharmony_ci * @cl: Pointer to client device instance 27862306a36Sopenharmony_ci * 27962306a36Sopenharmony_ci * Check client device in-processing buffer list and get a rb from it. 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * Return: rb pointer if buffer list isn't empty else NULL 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_cistruct ishtp_cl_rb *ishtp_cl_rx_get_rb(struct ishtp_cl *cl) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci unsigned long rx_flags; 28662306a36Sopenharmony_ci struct ishtp_cl_rb *rb; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci spin_lock_irqsave(&cl->in_process_spinlock, rx_flags); 28962306a36Sopenharmony_ci rb = list_first_entry_or_null(&cl->in_process_list.list, 29062306a36Sopenharmony_ci struct ishtp_cl_rb, list); 29162306a36Sopenharmony_ci if (rb) 29262306a36Sopenharmony_ci list_del_init(&rb->list); 29362306a36Sopenharmony_ci spin_unlock_irqrestore(&cl->in_process_spinlock, rx_flags); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci return rb; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ciEXPORT_SYMBOL(ishtp_cl_rx_get_rb); 298