12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23703f53bSSrinivas Pandruvada /*
33703f53bSSrinivas Pandruvada  * ISHTP Ring Buffers
43703f53bSSrinivas Pandruvada  *
53703f53bSSrinivas Pandruvada  * Copyright (c) 2003-2016, Intel Corporation.
63703f53bSSrinivas Pandruvada  */
73703f53bSSrinivas Pandruvada 
83703f53bSSrinivas Pandruvada #include <linux/slab.h>
93703f53bSSrinivas Pandruvada #include "client.h"
103703f53bSSrinivas Pandruvada 
113703f53bSSrinivas Pandruvada /**
123703f53bSSrinivas Pandruvada  * ishtp_cl_alloc_rx_ring() - Allocate RX ring buffers
133703f53bSSrinivas Pandruvada  * @cl: client device instance
143703f53bSSrinivas Pandruvada  *
153703f53bSSrinivas Pandruvada  * Allocate and initialize RX ring buffers
163703f53bSSrinivas Pandruvada  *
173703f53bSSrinivas Pandruvada  * Return: 0 on success else -ENOMEM
183703f53bSSrinivas Pandruvada  */
ishtp_cl_alloc_rx_ring(struct ishtp_cl * cl)193703f53bSSrinivas Pandruvada int ishtp_cl_alloc_rx_ring(struct ishtp_cl *cl)
203703f53bSSrinivas Pandruvada {
213703f53bSSrinivas Pandruvada 	size_t	len = cl->device->fw_client->props.max_msg_length;
223703f53bSSrinivas Pandruvada 	int	j;
233703f53bSSrinivas Pandruvada 	struct ishtp_cl_rb *rb;
243703f53bSSrinivas Pandruvada 	int	ret = 0;
253703f53bSSrinivas Pandruvada 	unsigned long	flags;
263703f53bSSrinivas Pandruvada 
273703f53bSSrinivas Pandruvada 	for (j = 0; j < cl->rx_ring_size; ++j) {
283703f53bSSrinivas Pandruvada 		rb = ishtp_io_rb_init(cl);
293703f53bSSrinivas Pandruvada 		if (!rb) {
303703f53bSSrinivas Pandruvada 			ret = -ENOMEM;
313703f53bSSrinivas Pandruvada 			goto out;
323703f53bSSrinivas Pandruvada 		}
333703f53bSSrinivas Pandruvada 		ret = ishtp_io_rb_alloc_buf(rb, len);
343703f53bSSrinivas Pandruvada 		if (ret)
353703f53bSSrinivas Pandruvada 			goto out;
363703f53bSSrinivas Pandruvada 		spin_lock_irqsave(&cl->free_list_spinlock, flags);
373703f53bSSrinivas Pandruvada 		list_add_tail(&rb->list, &cl->free_rb_list.list);
383703f53bSSrinivas Pandruvada 		spin_unlock_irqrestore(&cl->free_list_spinlock, flags);
393703f53bSSrinivas Pandruvada 	}
403703f53bSSrinivas Pandruvada 
413703f53bSSrinivas Pandruvada 	return	0;
423703f53bSSrinivas Pandruvada 
433703f53bSSrinivas Pandruvada out:
443703f53bSSrinivas Pandruvada 	dev_err(&cl->device->dev, "error in allocating Rx buffers\n");
453703f53bSSrinivas Pandruvada 	ishtp_cl_free_rx_ring(cl);
463703f53bSSrinivas Pandruvada 	return	ret;
473703f53bSSrinivas Pandruvada }
483703f53bSSrinivas Pandruvada 
493703f53bSSrinivas Pandruvada /**
503703f53bSSrinivas Pandruvada  * ishtp_cl_alloc_tx_ring() - Allocate TX ring buffers
513703f53bSSrinivas Pandruvada  * @cl: client device instance
523703f53bSSrinivas Pandruvada  *
533703f53bSSrinivas Pandruvada  * Allocate and initialize TX ring buffers
543703f53bSSrinivas Pandruvada  *
553703f53bSSrinivas Pandruvada  * Return: 0 on success else -ENOMEM
563703f53bSSrinivas Pandruvada  */
ishtp_cl_alloc_tx_ring(struct ishtp_cl * cl)573703f53bSSrinivas Pandruvada int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl)
583703f53bSSrinivas Pandruvada {
593703f53bSSrinivas Pandruvada 	size_t	len = cl->device->fw_client->props.max_msg_length;
603703f53bSSrinivas Pandruvada 	int	j;
613703f53bSSrinivas Pandruvada 	unsigned long	flags;
623703f53bSSrinivas Pandruvada 
6318c0b546SSrinivas Pandruvada 	cl->tx_ring_free_size = 0;
6418c0b546SSrinivas Pandruvada 
653703f53bSSrinivas Pandruvada 	/* Allocate pool to free Tx bufs */
663703f53bSSrinivas Pandruvada 	for (j = 0; j < cl->tx_ring_size; ++j) {
673703f53bSSrinivas Pandruvada 		struct ishtp_cl_tx_ring	*tx_buf;
683703f53bSSrinivas Pandruvada 
696cf5c1c7SWei Yongjun 		tx_buf = kzalloc(sizeof(struct ishtp_cl_tx_ring), GFP_KERNEL);
703703f53bSSrinivas Pandruvada 		if (!tx_buf)
713703f53bSSrinivas Pandruvada 			goto	out;
723703f53bSSrinivas Pandruvada 
733703f53bSSrinivas Pandruvada 		tx_buf->send_buf.data = kmalloc(len, GFP_KERNEL);
743703f53bSSrinivas Pandruvada 		if (!tx_buf->send_buf.data) {
753703f53bSSrinivas Pandruvada 			kfree(tx_buf);
763703f53bSSrinivas Pandruvada 			goto	out;
773703f53bSSrinivas Pandruvada 		}
783703f53bSSrinivas Pandruvada 
793703f53bSSrinivas Pandruvada 		spin_lock_irqsave(&cl->tx_free_list_spinlock, flags);
803703f53bSSrinivas Pandruvada 		list_add_tail(&tx_buf->list, &cl->tx_free_list.list);
8118c0b546SSrinivas Pandruvada 		++cl->tx_ring_free_size;
823703f53bSSrinivas Pandruvada 		spin_unlock_irqrestore(&cl->tx_free_list_spinlock, flags);
833703f53bSSrinivas Pandruvada 	}
843703f53bSSrinivas Pandruvada 	return	0;
853703f53bSSrinivas Pandruvada out:
863703f53bSSrinivas Pandruvada 	dev_err(&cl->device->dev, "error in allocating Tx pool\n");
8716ff7bf6SZhang Lixu 	ishtp_cl_free_tx_ring(cl);
883703f53bSSrinivas Pandruvada 	return	-ENOMEM;
893703f53bSSrinivas Pandruvada }
903703f53bSSrinivas Pandruvada 
913703f53bSSrinivas Pandruvada /**
923703f53bSSrinivas Pandruvada  * ishtp_cl_free_rx_ring() - Free RX ring buffers
933703f53bSSrinivas Pandruvada  * @cl: client device instance
943703f53bSSrinivas Pandruvada  *
953703f53bSSrinivas Pandruvada  * Free RX ring buffers
963703f53bSSrinivas Pandruvada  */
ishtp_cl_free_rx_ring(struct ishtp_cl * cl)973703f53bSSrinivas Pandruvada void ishtp_cl_free_rx_ring(struct ishtp_cl *cl)
983703f53bSSrinivas Pandruvada {
993703f53bSSrinivas Pandruvada 	struct ishtp_cl_rb *rb;
1003703f53bSSrinivas Pandruvada 	unsigned long	flags;
1013703f53bSSrinivas Pandruvada 
1023703f53bSSrinivas Pandruvada 	/* release allocated memory - pass over free_rb_list */
1033703f53bSSrinivas Pandruvada 	spin_lock_irqsave(&cl->free_list_spinlock, flags);
1043703f53bSSrinivas Pandruvada 	while (!list_empty(&cl->free_rb_list.list)) {
1053703f53bSSrinivas Pandruvada 		rb = list_entry(cl->free_rb_list.list.next, struct ishtp_cl_rb,
1063703f53bSSrinivas Pandruvada 				list);
1073703f53bSSrinivas Pandruvada 		list_del(&rb->list);
1083703f53bSSrinivas Pandruvada 		kfree(rb->buffer.data);
1093703f53bSSrinivas Pandruvada 		kfree(rb);
1103703f53bSSrinivas Pandruvada 	}
1113703f53bSSrinivas Pandruvada 	spin_unlock_irqrestore(&cl->free_list_spinlock, flags);
1123703f53bSSrinivas Pandruvada 	/* release allocated memory - pass over in_process_list */
1133703f53bSSrinivas Pandruvada 	spin_lock_irqsave(&cl->in_process_spinlock, flags);
1143703f53bSSrinivas Pandruvada 	while (!list_empty(&cl->in_process_list.list)) {
1153703f53bSSrinivas Pandruvada 		rb = list_entry(cl->in_process_list.list.next,
1163703f53bSSrinivas Pandruvada 				struct ishtp_cl_rb, list);
1173703f53bSSrinivas Pandruvada 		list_del(&rb->list);
1183703f53bSSrinivas Pandruvada 		kfree(rb->buffer.data);
1193703f53bSSrinivas Pandruvada 		kfree(rb);
1203703f53bSSrinivas Pandruvada 	}
1213703f53bSSrinivas Pandruvada 	spin_unlock_irqrestore(&cl->in_process_spinlock, flags);
1223703f53bSSrinivas Pandruvada }
1233703f53bSSrinivas Pandruvada 
1243703f53bSSrinivas Pandruvada /**
1253703f53bSSrinivas Pandruvada  * ishtp_cl_free_tx_ring() - Free TX ring buffers
1263703f53bSSrinivas Pandruvada  * @cl: client device instance
1273703f53bSSrinivas Pandruvada  *
1283703f53bSSrinivas Pandruvada  * Free TX ring buffers
1293703f53bSSrinivas Pandruvada  */
ishtp_cl_free_tx_ring(struct ishtp_cl * cl)1303703f53bSSrinivas Pandruvada void ishtp_cl_free_tx_ring(struct ishtp_cl *cl)
1313703f53bSSrinivas Pandruvada {
1323703f53bSSrinivas Pandruvada 	struct ishtp_cl_tx_ring	*tx_buf;
1333703f53bSSrinivas Pandruvada 	unsigned long	flags;
1343703f53bSSrinivas Pandruvada 
1353703f53bSSrinivas Pandruvada 	spin_lock_irqsave(&cl->tx_free_list_spinlock, flags);
1363703f53bSSrinivas Pandruvada 	/* release allocated memory - pass over tx_free_list */
1373703f53bSSrinivas Pandruvada 	while (!list_empty(&cl->tx_free_list.list)) {
1383703f53bSSrinivas Pandruvada 		tx_buf = list_entry(cl->tx_free_list.list.next,
1393703f53bSSrinivas Pandruvada 				    struct ishtp_cl_tx_ring, list);
1403703f53bSSrinivas Pandruvada 		list_del(&tx_buf->list);
14118c0b546SSrinivas Pandruvada 		--cl->tx_ring_free_size;
1423703f53bSSrinivas Pandruvada 		kfree(tx_buf->send_buf.data);
1433703f53bSSrinivas Pandruvada 		kfree(tx_buf);
1443703f53bSSrinivas Pandruvada 	}
1453703f53bSSrinivas Pandruvada 	spin_unlock_irqrestore(&cl->tx_free_list_spinlock, flags);
1463703f53bSSrinivas Pandruvada 
1473703f53bSSrinivas Pandruvada 	spin_lock_irqsave(&cl->tx_list_spinlock, flags);
1483703f53bSSrinivas Pandruvada 	/* release allocated memory - pass over tx_list */
1493703f53bSSrinivas Pandruvada 	while (!list_empty(&cl->tx_list.list)) {
1503703f53bSSrinivas Pandruvada 		tx_buf = list_entry(cl->tx_list.list.next,
1513703f53bSSrinivas Pandruvada 				    struct ishtp_cl_tx_ring, list);
1523703f53bSSrinivas Pandruvada 		list_del(&tx_buf->list);
1533703f53bSSrinivas Pandruvada 		kfree(tx_buf->send_buf.data);
1543703f53bSSrinivas Pandruvada 		kfree(tx_buf);
1553703f53bSSrinivas Pandruvada 	}
1563703f53bSSrinivas Pandruvada 	spin_unlock_irqrestore(&cl->tx_list_spinlock, flags);
1573703f53bSSrinivas Pandruvada }
1583703f53bSSrinivas Pandruvada 
1593703f53bSSrinivas Pandruvada /**
1603703f53bSSrinivas Pandruvada  * ishtp_io_rb_free() - Free IO request block
1613703f53bSSrinivas Pandruvada  * @rb: IO request block
1623703f53bSSrinivas Pandruvada  *
1633703f53bSSrinivas Pandruvada  * Free io request block memory
1643703f53bSSrinivas Pandruvada  */
ishtp_io_rb_free(struct ishtp_cl_rb * rb)1653703f53bSSrinivas Pandruvada void ishtp_io_rb_free(struct ishtp_cl_rb *rb)
1663703f53bSSrinivas Pandruvada {
1673703f53bSSrinivas Pandruvada 	if (rb == NULL)
1683703f53bSSrinivas Pandruvada 		return;
1693703f53bSSrinivas Pandruvada 
1703703f53bSSrinivas Pandruvada 	kfree(rb->buffer.data);
1713703f53bSSrinivas Pandruvada 	kfree(rb);
1723703f53bSSrinivas Pandruvada }
1733703f53bSSrinivas Pandruvada 
1743703f53bSSrinivas Pandruvada /**
1753703f53bSSrinivas Pandruvada  * ishtp_io_rb_init() - Allocate and init IO request block
1763703f53bSSrinivas Pandruvada  * @cl: client device instance
1773703f53bSSrinivas Pandruvada  *
1783703f53bSSrinivas Pandruvada  * Allocate and initialize request block
1793703f53bSSrinivas Pandruvada  *
1803703f53bSSrinivas Pandruvada  * Return: Allocted IO request block pointer
1813703f53bSSrinivas Pandruvada  */
ishtp_io_rb_init(struct ishtp_cl * cl)1823703f53bSSrinivas Pandruvada struct ishtp_cl_rb *ishtp_io_rb_init(struct ishtp_cl *cl)
1833703f53bSSrinivas Pandruvada {
1843703f53bSSrinivas Pandruvada 	struct ishtp_cl_rb *rb;
1853703f53bSSrinivas Pandruvada 
1863703f53bSSrinivas Pandruvada 	rb = kzalloc(sizeof(struct ishtp_cl_rb), GFP_KERNEL);
1873703f53bSSrinivas Pandruvada 	if (!rb)
1883703f53bSSrinivas Pandruvada 		return NULL;
1893703f53bSSrinivas Pandruvada 
1903703f53bSSrinivas Pandruvada 	INIT_LIST_HEAD(&rb->list);
1913703f53bSSrinivas Pandruvada 	rb->cl = cl;
1923703f53bSSrinivas Pandruvada 	rb->buf_idx = 0;
1933703f53bSSrinivas Pandruvada 	return rb;
1943703f53bSSrinivas Pandruvada }
1953703f53bSSrinivas Pandruvada 
1963703f53bSSrinivas Pandruvada /**
1973703f53bSSrinivas Pandruvada  * ishtp_io_rb_alloc_buf() - Allocate and init response buffer
1983703f53bSSrinivas Pandruvada  * @rb: IO request block
1993703f53bSSrinivas Pandruvada  * @length: length of response buffer
2003703f53bSSrinivas Pandruvada  *
2013703f53bSSrinivas Pandruvada  * Allocate respose buffer
2023703f53bSSrinivas Pandruvada  *
2033703f53bSSrinivas Pandruvada  * Return: 0 on success else -ENOMEM
2043703f53bSSrinivas Pandruvada  */
ishtp_io_rb_alloc_buf(struct ishtp_cl_rb * rb,size_t length)2053703f53bSSrinivas Pandruvada int ishtp_io_rb_alloc_buf(struct ishtp_cl_rb *rb, size_t length)
2063703f53bSSrinivas Pandruvada {
2073703f53bSSrinivas Pandruvada 	if (!rb)
2083703f53bSSrinivas Pandruvada 		return -EINVAL;
2093703f53bSSrinivas Pandruvada 
2103703f53bSSrinivas Pandruvada 	if (length == 0)
2113703f53bSSrinivas Pandruvada 		return 0;
2123703f53bSSrinivas Pandruvada 
2133703f53bSSrinivas Pandruvada 	rb->buffer.data = kmalloc(length, GFP_KERNEL);
2143703f53bSSrinivas Pandruvada 	if (!rb->buffer.data)
2153703f53bSSrinivas Pandruvada 		return -ENOMEM;
2163703f53bSSrinivas Pandruvada 
2173703f53bSSrinivas Pandruvada 	rb->buffer.size = length;
2183703f53bSSrinivas Pandruvada 	return 0;
2193703f53bSSrinivas Pandruvada }
2203703f53bSSrinivas Pandruvada 
2213703f53bSSrinivas Pandruvada /**
2223703f53bSSrinivas Pandruvada  * ishtp_cl_io_rb_recycle() - Recycle IO request blocks
2233703f53bSSrinivas Pandruvada  * @rb: IO request block
2243703f53bSSrinivas Pandruvada  *
2253703f53bSSrinivas Pandruvada  * Re-append rb to its client's free list and send flow control if needed
2263703f53bSSrinivas Pandruvada  *
2273703f53bSSrinivas Pandruvada  * Return: 0 on success else -EFAULT
2283703f53bSSrinivas Pandruvada  */
ishtp_cl_io_rb_recycle(struct ishtp_cl_rb * rb)2293703f53bSSrinivas Pandruvada int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb)
2303703f53bSSrinivas Pandruvada {
2313703f53bSSrinivas Pandruvada 	struct ishtp_cl *cl;
2323703f53bSSrinivas Pandruvada 	int	rets = 0;
2333703f53bSSrinivas Pandruvada 	unsigned long	flags;
2343703f53bSSrinivas Pandruvada 
2353703f53bSSrinivas Pandruvada 	if (!rb || !rb->cl)
2363703f53bSSrinivas Pandruvada 		return	-EFAULT;
2373703f53bSSrinivas Pandruvada 
2383703f53bSSrinivas Pandruvada 	cl = rb->cl;
2393703f53bSSrinivas Pandruvada 	spin_lock_irqsave(&cl->free_list_spinlock, flags);
2403703f53bSSrinivas Pandruvada 	list_add_tail(&rb->list, &cl->free_rb_list.list);
2413703f53bSSrinivas Pandruvada 	spin_unlock_irqrestore(&cl->free_list_spinlock, flags);
2423703f53bSSrinivas Pandruvada 
2433703f53bSSrinivas Pandruvada 	/*
2443703f53bSSrinivas Pandruvada 	 * If we returned the first buffer to empty 'free' list,
2453703f53bSSrinivas Pandruvada 	 * send flow control
2463703f53bSSrinivas Pandruvada 	 */
2473703f53bSSrinivas Pandruvada 	if (!cl->out_flow_ctrl_creds)
2483703f53bSSrinivas Pandruvada 		rets = ishtp_cl_read_start(cl);
2493703f53bSSrinivas Pandruvada 
2503703f53bSSrinivas Pandruvada 	return	rets;
2513703f53bSSrinivas Pandruvada }
2523703f53bSSrinivas Pandruvada EXPORT_SYMBOL(ishtp_cl_io_rb_recycle);
253a1c40ce6SEven Xu 
254a1c40ce6SEven Xu /**
255a1c40ce6SEven Xu  * ishtp_cl_tx_empty() -test whether client device tx buffer is empty
256a1c40ce6SEven Xu  * @cl: Pointer to client device instance
257a1c40ce6SEven Xu  *
258a1c40ce6SEven Xu  * Look client device tx buffer list, and check whether this list is empty
259a1c40ce6SEven Xu  *
260a1c40ce6SEven Xu  * Return: true if client tx buffer list is empty else false
261a1c40ce6SEven Xu  */
ishtp_cl_tx_empty(struct ishtp_cl * cl)262a1c40ce6SEven Xu bool ishtp_cl_tx_empty(struct ishtp_cl *cl)
263a1c40ce6SEven Xu {
264a1c40ce6SEven Xu 	int tx_list_empty;
265a1c40ce6SEven Xu 	unsigned long tx_flags;
266a1c40ce6SEven Xu 
267a1c40ce6SEven Xu 	spin_lock_irqsave(&cl->tx_list_spinlock, tx_flags);
268a1c40ce6SEven Xu 	tx_list_empty = list_empty(&cl->tx_list.list);
269a1c40ce6SEven Xu 	spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags);
270a1c40ce6SEven Xu 
271a1c40ce6SEven Xu 	return !!tx_list_empty;
272a1c40ce6SEven Xu }
273a1c40ce6SEven Xu EXPORT_SYMBOL(ishtp_cl_tx_empty);
274a1c40ce6SEven Xu 
275a1c40ce6SEven Xu /**
276a1c40ce6SEven Xu  * ishtp_cl_rx_get_rb() -Get a rb from client device rx buffer list
277a1c40ce6SEven Xu  * @cl: Pointer to client device instance
278a1c40ce6SEven Xu  *
279a1c40ce6SEven Xu  * Check client device in-processing buffer list and get a rb from it.
280a1c40ce6SEven Xu  *
281a1c40ce6SEven Xu  * Return: rb pointer if buffer list isn't empty else NULL
282a1c40ce6SEven Xu  */
ishtp_cl_rx_get_rb(struct ishtp_cl * cl)283a1c40ce6SEven Xu struct ishtp_cl_rb *ishtp_cl_rx_get_rb(struct ishtp_cl *cl)
284a1c40ce6SEven Xu {
285a1c40ce6SEven Xu 	unsigned long rx_flags;
286a1c40ce6SEven Xu 	struct ishtp_cl_rb *rb;
287a1c40ce6SEven Xu 
288a1c40ce6SEven Xu 	spin_lock_irqsave(&cl->in_process_spinlock, rx_flags);
289a1c40ce6SEven Xu 	rb = list_first_entry_or_null(&cl->in_process_list.list,
290a1c40ce6SEven Xu 				struct ishtp_cl_rb, list);
291a1c40ce6SEven Xu 	if (rb)
292a1c40ce6SEven Xu 		list_del_init(&rb->list);
293a1c40ce6SEven Xu 	spin_unlock_irqrestore(&cl->in_process_spinlock, rx_flags);
294a1c40ce6SEven Xu 
295a1c40ce6SEven Xu 	return rb;
296a1c40ce6SEven Xu }
297a1c40ce6SEven Xu EXPORT_SYMBOL(ishtp_cl_rx_get_rb);
298