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