1daeccac2SArend van Spriel // SPDX-License-Identifier: ISC
2daeccac2SArend van Spriel /*
3daeccac2SArend van Spriel  * Copyright (c) 2014 Broadcom Corporation
405491d2cSKalle Valo  */
505491d2cSKalle Valo 
605491d2cSKalle Valo #include <linux/types.h>
705491d2cSKalle Valo #include <linux/netdevice.h>
805491d2cSKalle Valo 
905491d2cSKalle Valo #include <brcmu_utils.h>
1005491d2cSKalle Valo #include <brcmu_wifi.h>
1105491d2cSKalle Valo 
1205491d2cSKalle Valo #include "core.h"
1305491d2cSKalle Valo #include "commonring.h"
1405491d2cSKalle Valo 
brcmf_commonring_register_cb(struct brcmf_commonring * commonring,int (* cr_ring_bell)(void * ctx),int (* cr_update_rptr)(void * ctx),int (* cr_update_wptr)(void * ctx),int (* cr_write_rptr)(void * ctx),int (* cr_write_wptr)(void * ctx),void * ctx)1505491d2cSKalle Valo void brcmf_commonring_register_cb(struct brcmf_commonring *commonring,
1605491d2cSKalle Valo 				  int (*cr_ring_bell)(void *ctx),
1705491d2cSKalle Valo 				  int (*cr_update_rptr)(void *ctx),
1805491d2cSKalle Valo 				  int (*cr_update_wptr)(void *ctx),
1905491d2cSKalle Valo 				  int (*cr_write_rptr)(void *ctx),
2005491d2cSKalle Valo 				  int (*cr_write_wptr)(void *ctx), void *ctx)
2105491d2cSKalle Valo {
2205491d2cSKalle Valo 	commonring->cr_ring_bell = cr_ring_bell;
2305491d2cSKalle Valo 	commonring->cr_update_rptr = cr_update_rptr;
2405491d2cSKalle Valo 	commonring->cr_update_wptr = cr_update_wptr;
2505491d2cSKalle Valo 	commonring->cr_write_rptr = cr_write_rptr;
2605491d2cSKalle Valo 	commonring->cr_write_wptr = cr_write_wptr;
2705491d2cSKalle Valo 	commonring->cr_ctx = ctx;
2805491d2cSKalle Valo }
2905491d2cSKalle Valo 
3005491d2cSKalle Valo 
brcmf_commonring_config(struct brcmf_commonring * commonring,u16 depth,u16 item_len,void * buf_addr)3105491d2cSKalle Valo void brcmf_commonring_config(struct brcmf_commonring *commonring, u16 depth,
3205491d2cSKalle Valo 			     u16 item_len, void *buf_addr)
3305491d2cSKalle Valo {
3405491d2cSKalle Valo 	commonring->depth = depth;
3505491d2cSKalle Valo 	commonring->item_len = item_len;
3605491d2cSKalle Valo 	commonring->buf_addr = buf_addr;
3705491d2cSKalle Valo 	if (!commonring->inited) {
3805491d2cSKalle Valo 		spin_lock_init(&commonring->lock);
3905491d2cSKalle Valo 		commonring->inited = true;
4005491d2cSKalle Valo 	}
4105491d2cSKalle Valo 	commonring->r_ptr = 0;
4205491d2cSKalle Valo 	if (commonring->cr_write_rptr)
4305491d2cSKalle Valo 		commonring->cr_write_rptr(commonring->cr_ctx);
4405491d2cSKalle Valo 	commonring->w_ptr = 0;
4505491d2cSKalle Valo 	if (commonring->cr_write_wptr)
4605491d2cSKalle Valo 		commonring->cr_write_wptr(commonring->cr_ctx);
4705491d2cSKalle Valo 	commonring->f_ptr = 0;
4805491d2cSKalle Valo }
4905491d2cSKalle Valo 
5005491d2cSKalle Valo 
brcmf_commonring_lock(struct brcmf_commonring * commonring)5105491d2cSKalle Valo void brcmf_commonring_lock(struct brcmf_commonring *commonring)
5205491d2cSKalle Valo 		__acquires(&commonring->lock)
5305491d2cSKalle Valo {
5405491d2cSKalle Valo 	unsigned long flags;
5505491d2cSKalle Valo 
5605491d2cSKalle Valo 	spin_lock_irqsave(&commonring->lock, flags);
5705491d2cSKalle Valo 	commonring->flags = flags;
5805491d2cSKalle Valo }
5905491d2cSKalle Valo 
6005491d2cSKalle Valo 
brcmf_commonring_unlock(struct brcmf_commonring * commonring)6105491d2cSKalle Valo void brcmf_commonring_unlock(struct brcmf_commonring *commonring)
6205491d2cSKalle Valo 		__releases(&commonring->lock)
6305491d2cSKalle Valo {
6405491d2cSKalle Valo 	spin_unlock_irqrestore(&commonring->lock, commonring->flags);
6505491d2cSKalle Valo }
6605491d2cSKalle Valo 
6705491d2cSKalle Valo 
brcmf_commonring_write_available(struct brcmf_commonring * commonring)6805491d2cSKalle Valo bool brcmf_commonring_write_available(struct brcmf_commonring *commonring)
6905491d2cSKalle Valo {
7005491d2cSKalle Valo 	u16 available;
7105491d2cSKalle Valo 	bool retry = true;
7205491d2cSKalle Valo 
7305491d2cSKalle Valo again:
7405491d2cSKalle Valo 	if (commonring->r_ptr <= commonring->w_ptr)
7505491d2cSKalle Valo 		available = commonring->depth - commonring->w_ptr +
7605491d2cSKalle Valo 			    commonring->r_ptr;
7705491d2cSKalle Valo 	else
7805491d2cSKalle Valo 		available = commonring->r_ptr - commonring->w_ptr;
7905491d2cSKalle Valo 
8005491d2cSKalle Valo 	if (available > 1) {
8105491d2cSKalle Valo 		if (!commonring->was_full)
8205491d2cSKalle Valo 			return true;
8305491d2cSKalle Valo 		if (available > commonring->depth / 8) {
8405491d2cSKalle Valo 			commonring->was_full = false;
8505491d2cSKalle Valo 			return true;
8605491d2cSKalle Valo 		}
8705491d2cSKalle Valo 		if (retry) {
8805491d2cSKalle Valo 			if (commonring->cr_update_rptr)
8905491d2cSKalle Valo 				commonring->cr_update_rptr(commonring->cr_ctx);
9005491d2cSKalle Valo 			retry = false;
9105491d2cSKalle Valo 			goto again;
9205491d2cSKalle Valo 		}
9305491d2cSKalle Valo 		return false;
9405491d2cSKalle Valo 	}
9505491d2cSKalle Valo 
9605491d2cSKalle Valo 	if (retry) {
9705491d2cSKalle Valo 		if (commonring->cr_update_rptr)
9805491d2cSKalle Valo 			commonring->cr_update_rptr(commonring->cr_ctx);
9905491d2cSKalle Valo 		retry = false;
10005491d2cSKalle Valo 		goto again;
10105491d2cSKalle Valo 	}
10205491d2cSKalle Valo 
10305491d2cSKalle Valo 	commonring->was_full = true;
10405491d2cSKalle Valo 	return false;
10505491d2cSKalle Valo }
10605491d2cSKalle Valo 
10705491d2cSKalle Valo 
brcmf_commonring_reserve_for_write(struct brcmf_commonring * commonring)10805491d2cSKalle Valo void *brcmf_commonring_reserve_for_write(struct brcmf_commonring *commonring)
10905491d2cSKalle Valo {
11005491d2cSKalle Valo 	void *ret_ptr;
11105491d2cSKalle Valo 	u16 available;
11205491d2cSKalle Valo 	bool retry = true;
11305491d2cSKalle Valo 
11405491d2cSKalle Valo again:
11505491d2cSKalle Valo 	if (commonring->r_ptr <= commonring->w_ptr)
11605491d2cSKalle Valo 		available = commonring->depth - commonring->w_ptr +
11705491d2cSKalle Valo 			    commonring->r_ptr;
11805491d2cSKalle Valo 	else
11905491d2cSKalle Valo 		available = commonring->r_ptr - commonring->w_ptr;
12005491d2cSKalle Valo 
12105491d2cSKalle Valo 	if (available > 1) {
12205491d2cSKalle Valo 		ret_ptr = commonring->buf_addr +
12305491d2cSKalle Valo 			  (commonring->w_ptr * commonring->item_len);
12405491d2cSKalle Valo 		commonring->w_ptr++;
12505491d2cSKalle Valo 		if (commonring->w_ptr == commonring->depth)
12605491d2cSKalle Valo 			commonring->w_ptr = 0;
12705491d2cSKalle Valo 		return ret_ptr;
12805491d2cSKalle Valo 	}
12905491d2cSKalle Valo 
13005491d2cSKalle Valo 	if (retry) {
13105491d2cSKalle Valo 		if (commonring->cr_update_rptr)
13205491d2cSKalle Valo 			commonring->cr_update_rptr(commonring->cr_ctx);
13305491d2cSKalle Valo 		retry = false;
13405491d2cSKalle Valo 		goto again;
13505491d2cSKalle Valo 	}
13605491d2cSKalle Valo 
13705491d2cSKalle Valo 	commonring->was_full = true;
13805491d2cSKalle Valo 	return NULL;
13905491d2cSKalle Valo }
14005491d2cSKalle Valo 
14105491d2cSKalle Valo 
14205491d2cSKalle Valo void *
brcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring * commonring,u16 n_items,u16 * alloced)14305491d2cSKalle Valo brcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring *commonring,
14405491d2cSKalle Valo 					    u16 n_items, u16 *alloced)
14505491d2cSKalle Valo {
14605491d2cSKalle Valo 	void *ret_ptr;
14705491d2cSKalle Valo 	u16 available;
14805491d2cSKalle Valo 	bool retry = true;
14905491d2cSKalle Valo 
15005491d2cSKalle Valo again:
15105491d2cSKalle Valo 	if (commonring->r_ptr <= commonring->w_ptr)
15205491d2cSKalle Valo 		available = commonring->depth - commonring->w_ptr +
15305491d2cSKalle Valo 			    commonring->r_ptr;
15405491d2cSKalle Valo 	else
15505491d2cSKalle Valo 		available = commonring->r_ptr - commonring->w_ptr;
15605491d2cSKalle Valo 
15705491d2cSKalle Valo 	if (available > 1) {
15805491d2cSKalle Valo 		ret_ptr = commonring->buf_addr +
15905491d2cSKalle Valo 			  (commonring->w_ptr * commonring->item_len);
16005491d2cSKalle Valo 		*alloced = min_t(u16, n_items, available - 1);
16105491d2cSKalle Valo 		if (*alloced + commonring->w_ptr > commonring->depth)
16205491d2cSKalle Valo 			*alloced = commonring->depth - commonring->w_ptr;
16305491d2cSKalle Valo 		commonring->w_ptr += *alloced;
16405491d2cSKalle Valo 		if (commonring->w_ptr == commonring->depth)
16505491d2cSKalle Valo 			commonring->w_ptr = 0;
16605491d2cSKalle Valo 		return ret_ptr;
16705491d2cSKalle Valo 	}
16805491d2cSKalle Valo 
16905491d2cSKalle Valo 	if (retry) {
17005491d2cSKalle Valo 		if (commonring->cr_update_rptr)
17105491d2cSKalle Valo 			commonring->cr_update_rptr(commonring->cr_ctx);
17205491d2cSKalle Valo 		retry = false;
17305491d2cSKalle Valo 		goto again;
17405491d2cSKalle Valo 	}
17505491d2cSKalle Valo 
17605491d2cSKalle Valo 	commonring->was_full = true;
17705491d2cSKalle Valo 	return NULL;
17805491d2cSKalle Valo }
17905491d2cSKalle Valo 
18005491d2cSKalle Valo 
brcmf_commonring_write_complete(struct brcmf_commonring * commonring)18105491d2cSKalle Valo int brcmf_commonring_write_complete(struct brcmf_commonring *commonring)
18205491d2cSKalle Valo {
18309667ea7SColin Ian King 	if (commonring->f_ptr > commonring->w_ptr)
18405491d2cSKalle Valo 		commonring->f_ptr = 0;
18505491d2cSKalle Valo 
18605491d2cSKalle Valo 	commonring->f_ptr = commonring->w_ptr;
18705491d2cSKalle Valo 
18805491d2cSKalle Valo 	if (commonring->cr_write_wptr)
18905491d2cSKalle Valo 		commonring->cr_write_wptr(commonring->cr_ctx);
19005491d2cSKalle Valo 	if (commonring->cr_ring_bell)
19105491d2cSKalle Valo 		return commonring->cr_ring_bell(commonring->cr_ctx);
19205491d2cSKalle Valo 
19305491d2cSKalle Valo 	return -EIO;
19405491d2cSKalle Valo }
19505491d2cSKalle Valo 
19605491d2cSKalle Valo 
brcmf_commonring_write_cancel(struct brcmf_commonring * commonring,u16 n_items)19705491d2cSKalle Valo void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring,
19805491d2cSKalle Valo 				   u16 n_items)
19905491d2cSKalle Valo {
20005491d2cSKalle Valo 	if (commonring->w_ptr == 0)
20105491d2cSKalle Valo 		commonring->w_ptr = commonring->depth - n_items;
20205491d2cSKalle Valo 	else
20305491d2cSKalle Valo 		commonring->w_ptr -= n_items;
20405491d2cSKalle Valo }
20505491d2cSKalle Valo 
20605491d2cSKalle Valo 
brcmf_commonring_get_read_ptr(struct brcmf_commonring * commonring,u16 * n_items)20705491d2cSKalle Valo void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring,
20805491d2cSKalle Valo 				    u16 *n_items)
20905491d2cSKalle Valo {
21005491d2cSKalle Valo 	if (commonring->cr_update_wptr)
21105491d2cSKalle Valo 		commonring->cr_update_wptr(commonring->cr_ctx);
21205491d2cSKalle Valo 
21305491d2cSKalle Valo 	*n_items = (commonring->w_ptr >= commonring->r_ptr) ?
21405491d2cSKalle Valo 				(commonring->w_ptr - commonring->r_ptr) :
21505491d2cSKalle Valo 				(commonring->depth - commonring->r_ptr);
21605491d2cSKalle Valo 
21705491d2cSKalle Valo 	if (*n_items == 0)
21805491d2cSKalle Valo 		return NULL;
21905491d2cSKalle Valo 
22005491d2cSKalle Valo 	return commonring->buf_addr +
22105491d2cSKalle Valo 	       (commonring->r_ptr * commonring->item_len);
22205491d2cSKalle Valo }
22305491d2cSKalle Valo 
22405491d2cSKalle Valo 
brcmf_commonring_read_complete(struct brcmf_commonring * commonring,u16 n_items)22505491d2cSKalle Valo int brcmf_commonring_read_complete(struct brcmf_commonring *commonring,
22605491d2cSKalle Valo 				   u16 n_items)
22705491d2cSKalle Valo {
22805491d2cSKalle Valo 	commonring->r_ptr += n_items;
22905491d2cSKalle Valo 	if (commonring->r_ptr == commonring->depth)
23005491d2cSKalle Valo 		commonring->r_ptr = 0;
23105491d2cSKalle Valo 
23205491d2cSKalle Valo 	if (commonring->cr_write_rptr)
23305491d2cSKalle Valo 		return commonring->cr_write_rptr(commonring->cr_ctx);
23405491d2cSKalle Valo 
23505491d2cSKalle Valo 	return -EIO;
23605491d2cSKalle Valo }
237