18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2014 Broadcom Corporation 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/types.h> 78c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <brcmu_utils.h> 108c2ecf20Sopenharmony_ci#include <brcmu_wifi.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "core.h" 138c2ecf20Sopenharmony_ci#include "commonring.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_civoid brcmf_commonring_register_cb(struct brcmf_commonring *commonring, 168c2ecf20Sopenharmony_ci int (*cr_ring_bell)(void *ctx), 178c2ecf20Sopenharmony_ci int (*cr_update_rptr)(void *ctx), 188c2ecf20Sopenharmony_ci int (*cr_update_wptr)(void *ctx), 198c2ecf20Sopenharmony_ci int (*cr_write_rptr)(void *ctx), 208c2ecf20Sopenharmony_ci int (*cr_write_wptr)(void *ctx), void *ctx) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci commonring->cr_ring_bell = cr_ring_bell; 238c2ecf20Sopenharmony_ci commonring->cr_update_rptr = cr_update_rptr; 248c2ecf20Sopenharmony_ci commonring->cr_update_wptr = cr_update_wptr; 258c2ecf20Sopenharmony_ci commonring->cr_write_rptr = cr_write_rptr; 268c2ecf20Sopenharmony_ci commonring->cr_write_wptr = cr_write_wptr; 278c2ecf20Sopenharmony_ci commonring->cr_ctx = ctx; 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_civoid brcmf_commonring_config(struct brcmf_commonring *commonring, u16 depth, 328c2ecf20Sopenharmony_ci u16 item_len, void *buf_addr) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci commonring->depth = depth; 358c2ecf20Sopenharmony_ci commonring->item_len = item_len; 368c2ecf20Sopenharmony_ci commonring->buf_addr = buf_addr; 378c2ecf20Sopenharmony_ci if (!commonring->inited) { 388c2ecf20Sopenharmony_ci spin_lock_init(&commonring->lock); 398c2ecf20Sopenharmony_ci commonring->inited = true; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci commonring->r_ptr = 0; 428c2ecf20Sopenharmony_ci if (commonring->cr_write_rptr) 438c2ecf20Sopenharmony_ci commonring->cr_write_rptr(commonring->cr_ctx); 448c2ecf20Sopenharmony_ci commonring->w_ptr = 0; 458c2ecf20Sopenharmony_ci if (commonring->cr_write_wptr) 468c2ecf20Sopenharmony_ci commonring->cr_write_wptr(commonring->cr_ctx); 478c2ecf20Sopenharmony_ci commonring->f_ptr = 0; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_civoid brcmf_commonring_lock(struct brcmf_commonring *commonring) 528c2ecf20Sopenharmony_ci __acquires(&commonring->lock) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci unsigned long flags; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci spin_lock_irqsave(&commonring->lock, flags); 578c2ecf20Sopenharmony_ci commonring->flags = flags; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_civoid brcmf_commonring_unlock(struct brcmf_commonring *commonring) 628c2ecf20Sopenharmony_ci __releases(&commonring->lock) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&commonring->lock, commonring->flags); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cibool brcmf_commonring_write_available(struct brcmf_commonring *commonring) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci u16 available; 718c2ecf20Sopenharmony_ci bool retry = true; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ciagain: 748c2ecf20Sopenharmony_ci if (commonring->r_ptr <= commonring->w_ptr) 758c2ecf20Sopenharmony_ci available = commonring->depth - commonring->w_ptr + 768c2ecf20Sopenharmony_ci commonring->r_ptr; 778c2ecf20Sopenharmony_ci else 788c2ecf20Sopenharmony_ci available = commonring->r_ptr - commonring->w_ptr; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (available > 1) { 818c2ecf20Sopenharmony_ci if (!commonring->was_full) 828c2ecf20Sopenharmony_ci return true; 838c2ecf20Sopenharmony_ci if (available > commonring->depth / 8) { 848c2ecf20Sopenharmony_ci commonring->was_full = false; 858c2ecf20Sopenharmony_ci return true; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci if (retry) { 888c2ecf20Sopenharmony_ci if (commonring->cr_update_rptr) 898c2ecf20Sopenharmony_ci commonring->cr_update_rptr(commonring->cr_ctx); 908c2ecf20Sopenharmony_ci retry = false; 918c2ecf20Sopenharmony_ci goto again; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci return false; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (retry) { 978c2ecf20Sopenharmony_ci if (commonring->cr_update_rptr) 988c2ecf20Sopenharmony_ci commonring->cr_update_rptr(commonring->cr_ctx); 998c2ecf20Sopenharmony_ci retry = false; 1008c2ecf20Sopenharmony_ci goto again; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci commonring->was_full = true; 1048c2ecf20Sopenharmony_ci return false; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_civoid *brcmf_commonring_reserve_for_write(struct brcmf_commonring *commonring) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci void *ret_ptr; 1118c2ecf20Sopenharmony_ci u16 available; 1128c2ecf20Sopenharmony_ci bool retry = true; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ciagain: 1158c2ecf20Sopenharmony_ci if (commonring->r_ptr <= commonring->w_ptr) 1168c2ecf20Sopenharmony_ci available = commonring->depth - commonring->w_ptr + 1178c2ecf20Sopenharmony_ci commonring->r_ptr; 1188c2ecf20Sopenharmony_ci else 1198c2ecf20Sopenharmony_ci available = commonring->r_ptr - commonring->w_ptr; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (available > 1) { 1228c2ecf20Sopenharmony_ci ret_ptr = commonring->buf_addr + 1238c2ecf20Sopenharmony_ci (commonring->w_ptr * commonring->item_len); 1248c2ecf20Sopenharmony_ci commonring->w_ptr++; 1258c2ecf20Sopenharmony_ci if (commonring->w_ptr == commonring->depth) 1268c2ecf20Sopenharmony_ci commonring->w_ptr = 0; 1278c2ecf20Sopenharmony_ci return ret_ptr; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (retry) { 1318c2ecf20Sopenharmony_ci if (commonring->cr_update_rptr) 1328c2ecf20Sopenharmony_ci commonring->cr_update_rptr(commonring->cr_ctx); 1338c2ecf20Sopenharmony_ci retry = false; 1348c2ecf20Sopenharmony_ci goto again; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci commonring->was_full = true; 1388c2ecf20Sopenharmony_ci return NULL; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_civoid * 1438c2ecf20Sopenharmony_cibrcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring *commonring, 1448c2ecf20Sopenharmony_ci u16 n_items, u16 *alloced) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci void *ret_ptr; 1478c2ecf20Sopenharmony_ci u16 available; 1488c2ecf20Sopenharmony_ci bool retry = true; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ciagain: 1518c2ecf20Sopenharmony_ci if (commonring->r_ptr <= commonring->w_ptr) 1528c2ecf20Sopenharmony_ci available = commonring->depth - commonring->w_ptr + 1538c2ecf20Sopenharmony_ci commonring->r_ptr; 1548c2ecf20Sopenharmony_ci else 1558c2ecf20Sopenharmony_ci available = commonring->r_ptr - commonring->w_ptr; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (available > 1) { 1588c2ecf20Sopenharmony_ci ret_ptr = commonring->buf_addr + 1598c2ecf20Sopenharmony_ci (commonring->w_ptr * commonring->item_len); 1608c2ecf20Sopenharmony_ci *alloced = min_t(u16, n_items, available - 1); 1618c2ecf20Sopenharmony_ci if (*alloced + commonring->w_ptr > commonring->depth) 1628c2ecf20Sopenharmony_ci *alloced = commonring->depth - commonring->w_ptr; 1638c2ecf20Sopenharmony_ci commonring->w_ptr += *alloced; 1648c2ecf20Sopenharmony_ci if (commonring->w_ptr == commonring->depth) 1658c2ecf20Sopenharmony_ci commonring->w_ptr = 0; 1668c2ecf20Sopenharmony_ci return ret_ptr; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (retry) { 1708c2ecf20Sopenharmony_ci if (commonring->cr_update_rptr) 1718c2ecf20Sopenharmony_ci commonring->cr_update_rptr(commonring->cr_ctx); 1728c2ecf20Sopenharmony_ci retry = false; 1738c2ecf20Sopenharmony_ci goto again; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci commonring->was_full = true; 1778c2ecf20Sopenharmony_ci return NULL; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ciint brcmf_commonring_write_complete(struct brcmf_commonring *commonring) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci if (commonring->f_ptr > commonring->w_ptr) 1848c2ecf20Sopenharmony_ci commonring->f_ptr = 0; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci commonring->f_ptr = commonring->w_ptr; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (commonring->cr_write_wptr) 1898c2ecf20Sopenharmony_ci commonring->cr_write_wptr(commonring->cr_ctx); 1908c2ecf20Sopenharmony_ci if (commonring->cr_ring_bell) 1918c2ecf20Sopenharmony_ci return commonring->cr_ring_bell(commonring->cr_ctx); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return -EIO; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_civoid brcmf_commonring_write_cancel(struct brcmf_commonring *commonring, 1988c2ecf20Sopenharmony_ci u16 n_items) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci if (commonring->w_ptr == 0) 2018c2ecf20Sopenharmony_ci commonring->w_ptr = commonring->depth - n_items; 2028c2ecf20Sopenharmony_ci else 2038c2ecf20Sopenharmony_ci commonring->w_ptr -= n_items; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_civoid *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring, 2088c2ecf20Sopenharmony_ci u16 *n_items) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci if (commonring->cr_update_wptr) 2118c2ecf20Sopenharmony_ci commonring->cr_update_wptr(commonring->cr_ctx); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci *n_items = (commonring->w_ptr >= commonring->r_ptr) ? 2148c2ecf20Sopenharmony_ci (commonring->w_ptr - commonring->r_ptr) : 2158c2ecf20Sopenharmony_ci (commonring->depth - commonring->r_ptr); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (*n_items == 0) 2188c2ecf20Sopenharmony_ci return NULL; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci return commonring->buf_addr + 2218c2ecf20Sopenharmony_ci (commonring->r_ptr * commonring->item_len); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ciint brcmf_commonring_read_complete(struct brcmf_commonring *commonring, 2268c2ecf20Sopenharmony_ci u16 n_items) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci commonring->r_ptr += n_items; 2298c2ecf20Sopenharmony_ci if (commonring->r_ptr == commonring->depth) 2308c2ecf20Sopenharmony_ci commonring->r_ptr = 0; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (commonring->cr_write_rptr) 2338c2ecf20Sopenharmony_ci return commonring->cr_write_rptr(commonring->cr_ctx); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return -EIO; 2368c2ecf20Sopenharmony_ci} 237