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