1// SPDX-License-Identifier: ISC
2/*
3 * Copyright (c) 2014 Broadcom Corporation
4 */
5
6#include <linux/types.h>
7#include <linux/netdevice.h>
8
9#include <brcmu_utils.h>
10#include <brcmu_wifi.h>
11
12#include "core.h"
13#include "commonring.h"
14
15void brcmf_commonring_register_cb(struct brcmf_commonring *commonring,
16				  int (*cr_ring_bell)(void *ctx),
17				  int (*cr_update_rptr)(void *ctx),
18				  int (*cr_update_wptr)(void *ctx),
19				  int (*cr_write_rptr)(void *ctx),
20				  int (*cr_write_wptr)(void *ctx), void *ctx)
21{
22	commonring->cr_ring_bell = cr_ring_bell;
23	commonring->cr_update_rptr = cr_update_rptr;
24	commonring->cr_update_wptr = cr_update_wptr;
25	commonring->cr_write_rptr = cr_write_rptr;
26	commonring->cr_write_wptr = cr_write_wptr;
27	commonring->cr_ctx = ctx;
28}
29
30
31void brcmf_commonring_config(struct brcmf_commonring *commonring, u16 depth,
32			     u16 item_len, void *buf_addr)
33{
34	commonring->depth = depth;
35	commonring->item_len = item_len;
36	commonring->buf_addr = buf_addr;
37	if (!commonring->inited) {
38		spin_lock_init(&commonring->lock);
39		commonring->inited = true;
40	}
41	commonring->r_ptr = 0;
42	if (commonring->cr_write_rptr)
43		commonring->cr_write_rptr(commonring->cr_ctx);
44	commonring->w_ptr = 0;
45	if (commonring->cr_write_wptr)
46		commonring->cr_write_wptr(commonring->cr_ctx);
47	commonring->f_ptr = 0;
48}
49
50
51void brcmf_commonring_lock(struct brcmf_commonring *commonring)
52		__acquires(&commonring->lock)
53{
54	unsigned long flags;
55
56	spin_lock_irqsave(&commonring->lock, flags);
57	commonring->flags = flags;
58}
59
60
61void brcmf_commonring_unlock(struct brcmf_commonring *commonring)
62		__releases(&commonring->lock)
63{
64	spin_unlock_irqrestore(&commonring->lock, commonring->flags);
65}
66
67
68bool brcmf_commonring_write_available(struct brcmf_commonring *commonring)
69{
70	u16 available;
71	bool retry = true;
72
73again:
74	if (commonring->r_ptr <= commonring->w_ptr)
75		available = commonring->depth - commonring->w_ptr +
76			    commonring->r_ptr;
77	else
78		available = commonring->r_ptr - commonring->w_ptr;
79
80	if (available > 1) {
81		if (!commonring->was_full)
82			return true;
83		if (available > commonring->depth / 8) {
84			commonring->was_full = false;
85			return true;
86		}
87		if (retry) {
88			if (commonring->cr_update_rptr)
89				commonring->cr_update_rptr(commonring->cr_ctx);
90			retry = false;
91			goto again;
92		}
93		return false;
94	}
95
96	if (retry) {
97		if (commonring->cr_update_rptr)
98			commonring->cr_update_rptr(commonring->cr_ctx);
99		retry = false;
100		goto again;
101	}
102
103	commonring->was_full = true;
104	return false;
105}
106
107
108void *brcmf_commonring_reserve_for_write(struct brcmf_commonring *commonring)
109{
110	void *ret_ptr;
111	u16 available;
112	bool retry = true;
113
114again:
115	if (commonring->r_ptr <= commonring->w_ptr)
116		available = commonring->depth - commonring->w_ptr +
117			    commonring->r_ptr;
118	else
119		available = commonring->r_ptr - commonring->w_ptr;
120
121	if (available > 1) {
122		ret_ptr = commonring->buf_addr +
123			  (commonring->w_ptr * commonring->item_len);
124		commonring->w_ptr++;
125		if (commonring->w_ptr == commonring->depth)
126			commonring->w_ptr = 0;
127		return ret_ptr;
128	}
129
130	if (retry) {
131		if (commonring->cr_update_rptr)
132			commonring->cr_update_rptr(commonring->cr_ctx);
133		retry = false;
134		goto again;
135	}
136
137	commonring->was_full = true;
138	return NULL;
139}
140
141
142void *
143brcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring *commonring,
144					    u16 n_items, u16 *alloced)
145{
146	void *ret_ptr;
147	u16 available;
148	bool retry = true;
149
150again:
151	if (commonring->r_ptr <= commonring->w_ptr)
152		available = commonring->depth - commonring->w_ptr +
153			    commonring->r_ptr;
154	else
155		available = commonring->r_ptr - commonring->w_ptr;
156
157	if (available > 1) {
158		ret_ptr = commonring->buf_addr +
159			  (commonring->w_ptr * commonring->item_len);
160		*alloced = min_t(u16, n_items, available - 1);
161		if (*alloced + commonring->w_ptr > commonring->depth)
162			*alloced = commonring->depth - commonring->w_ptr;
163		commonring->w_ptr += *alloced;
164		if (commonring->w_ptr == commonring->depth)
165			commonring->w_ptr = 0;
166		return ret_ptr;
167	}
168
169	if (retry) {
170		if (commonring->cr_update_rptr)
171			commonring->cr_update_rptr(commonring->cr_ctx);
172		retry = false;
173		goto again;
174	}
175
176	commonring->was_full = true;
177	return NULL;
178}
179
180
181int brcmf_commonring_write_complete(struct brcmf_commonring *commonring)
182{
183	if (commonring->f_ptr > commonring->w_ptr)
184		commonring->f_ptr = 0;
185
186	commonring->f_ptr = commonring->w_ptr;
187
188	if (commonring->cr_write_wptr)
189		commonring->cr_write_wptr(commonring->cr_ctx);
190	if (commonring->cr_ring_bell)
191		return commonring->cr_ring_bell(commonring->cr_ctx);
192
193	return -EIO;
194}
195
196
197void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring,
198				   u16 n_items)
199{
200	if (commonring->w_ptr == 0)
201		commonring->w_ptr = commonring->depth - n_items;
202	else
203		commonring->w_ptr -= n_items;
204}
205
206
207void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring,
208				    u16 *n_items)
209{
210	if (commonring->cr_update_wptr)
211		commonring->cr_update_wptr(commonring->cr_ctx);
212
213	*n_items = (commonring->w_ptr >= commonring->r_ptr) ?
214				(commonring->w_ptr - commonring->r_ptr) :
215				(commonring->depth - commonring->r_ptr);
216
217	if (*n_items == 0)
218		return NULL;
219
220	return commonring->buf_addr +
221	       (commonring->r_ptr * commonring->item_len);
222}
223
224
225int brcmf_commonring_read_complete(struct brcmf_commonring *commonring,
226				   u16 n_items)
227{
228	commonring->r_ptr += n_items;
229	if (commonring->r_ptr == commonring->depth)
230		commonring->r_ptr = 0;
231
232	if (commonring->cr_write_rptr)
233		return commonring->cr_write_rptr(commonring->cr_ctx);
234
235	return -EIO;
236}
237