18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2012-2015,2017 Qualcomm Atheros, Inc. 48c2ecf20Sopenharmony_ci * Copyright (c) 2018, The Linux Foundation. All rights reserved. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/types.h> 88c2ecf20Sopenharmony_ci#include <linux/errno.h> 98c2ecf20Sopenharmony_ci#include <linux/fs.h> 108c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 118c2ecf20Sopenharmony_ci#include "wmi.h" 128c2ecf20Sopenharmony_ci#include "wil6210.h" 138c2ecf20Sopenharmony_ci#include "txrx.h" 148c2ecf20Sopenharmony_ci#include "pmc.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistruct desc_alloc_info { 178c2ecf20Sopenharmony_ci dma_addr_t pa; 188c2ecf20Sopenharmony_ci void *va; 198c2ecf20Sopenharmony_ci}; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic int wil_is_pmc_allocated(struct pmc_ctx *pmc) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci return !!pmc->pring_va; 248c2ecf20Sopenharmony_ci} 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_civoid wil_pmc_init(struct wil6210_priv *wil) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci memset(&wil->pmc, 0, sizeof(struct pmc_ctx)); 298c2ecf20Sopenharmony_ci mutex_init(&wil->pmc.lock); 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* Allocate the physical ring (p-ring) and the required 338c2ecf20Sopenharmony_ci * number of descriptors of required size. 348c2ecf20Sopenharmony_ci * Initialize the descriptors as required by pmc dma. 358c2ecf20Sopenharmony_ci * The descriptors' buffers dwords are initialized to hold 368c2ecf20Sopenharmony_ci * dword's serial number in the lsw and reserved value 378c2ecf20Sopenharmony_ci * PCM_DATA_INVALID_DW_VAL in the msw. 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_civoid wil_pmc_alloc(struct wil6210_priv *wil, 408c2ecf20Sopenharmony_ci int num_descriptors, 418c2ecf20Sopenharmony_ci int descriptor_size) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci u32 i; 448c2ecf20Sopenharmony_ci struct pmc_ctx *pmc = &wil->pmc; 458c2ecf20Sopenharmony_ci struct device *dev = wil_to_dev(wil); 468c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 478c2ecf20Sopenharmony_ci struct wmi_pmc_cmd pmc_cmd = {0}; 488c2ecf20Sopenharmony_ci int last_cmd_err = -ENOMEM; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci mutex_lock(&pmc->lock); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (wil_is_pmc_allocated(pmc)) { 538c2ecf20Sopenharmony_ci /* sanity check */ 548c2ecf20Sopenharmony_ci wil_err(wil, "ERROR pmc is already allocated\n"); 558c2ecf20Sopenharmony_ci goto no_release_err; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci if ((num_descriptors <= 0) || (descriptor_size <= 0)) { 588c2ecf20Sopenharmony_ci wil_err(wil, 598c2ecf20Sopenharmony_ci "Invalid params num_descriptors(%d), descriptor_size(%d)\n", 608c2ecf20Sopenharmony_ci num_descriptors, descriptor_size); 618c2ecf20Sopenharmony_ci last_cmd_err = -EINVAL; 628c2ecf20Sopenharmony_ci goto no_release_err; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (num_descriptors > (1 << WIL_RING_SIZE_ORDER_MAX)) { 668c2ecf20Sopenharmony_ci wil_err(wil, 678c2ecf20Sopenharmony_ci "num_descriptors(%d) exceeds max ring size %d\n", 688c2ecf20Sopenharmony_ci num_descriptors, 1 << WIL_RING_SIZE_ORDER_MAX); 698c2ecf20Sopenharmony_ci last_cmd_err = -EINVAL; 708c2ecf20Sopenharmony_ci goto no_release_err; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (num_descriptors > INT_MAX / descriptor_size) { 748c2ecf20Sopenharmony_ci wil_err(wil, 758c2ecf20Sopenharmony_ci "Overflow in num_descriptors(%d)*descriptor_size(%d)\n", 768c2ecf20Sopenharmony_ci num_descriptors, descriptor_size); 778c2ecf20Sopenharmony_ci last_cmd_err = -EINVAL; 788c2ecf20Sopenharmony_ci goto no_release_err; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci pmc->num_descriptors = num_descriptors; 828c2ecf20Sopenharmony_ci pmc->descriptor_size = descriptor_size; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci wil_dbg_misc(wil, "pmc_alloc: %d descriptors x %d bytes each\n", 858c2ecf20Sopenharmony_ci num_descriptors, descriptor_size); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* allocate descriptors info list in pmc context*/ 888c2ecf20Sopenharmony_ci pmc->descriptors = kcalloc(num_descriptors, 898c2ecf20Sopenharmony_ci sizeof(struct desc_alloc_info), 908c2ecf20Sopenharmony_ci GFP_KERNEL); 918c2ecf20Sopenharmony_ci if (!pmc->descriptors) { 928c2ecf20Sopenharmony_ci wil_err(wil, "ERROR allocating pmc skb list\n"); 938c2ecf20Sopenharmony_ci goto no_release_err; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci wil_dbg_misc(wil, "pmc_alloc: allocated descriptors info list %p\n", 978c2ecf20Sopenharmony_ci pmc->descriptors); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* Allocate pring buffer and descriptors. 1008c2ecf20Sopenharmony_ci * vring->va should be aligned on its size rounded up to power of 2 1018c2ecf20Sopenharmony_ci * This is granted by the dma_alloc_coherent. 1028c2ecf20Sopenharmony_ci * 1038c2ecf20Sopenharmony_ci * HW has limitation that all vrings addresses must share the same 1048c2ecf20Sopenharmony_ci * upper 16 msb bits part of 48 bits address. To workaround that, 1058c2ecf20Sopenharmony_ci * if we are using more than 32 bit addresses switch to 32 bit 1068c2ecf20Sopenharmony_ci * allocation before allocating vring memory. 1078c2ecf20Sopenharmony_ci * 1088c2ecf20Sopenharmony_ci * There's no check for the return value of dma_set_mask_and_coherent, 1098c2ecf20Sopenharmony_ci * since we assume if we were able to set the mask during 1108c2ecf20Sopenharmony_ci * initialization in this system it will not fail if we set it again 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_ci if (wil->dma_addr_size > 32) 1138c2ecf20Sopenharmony_ci dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci pmc->pring_va = dma_alloc_coherent(dev, 1168c2ecf20Sopenharmony_ci sizeof(struct vring_tx_desc) * num_descriptors, 1178c2ecf20Sopenharmony_ci &pmc->pring_pa, 1188c2ecf20Sopenharmony_ci GFP_KERNEL); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (wil->dma_addr_size > 32) 1218c2ecf20Sopenharmony_ci dma_set_mask_and_coherent(dev, 1228c2ecf20Sopenharmony_ci DMA_BIT_MASK(wil->dma_addr_size)); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci wil_dbg_misc(wil, 1258c2ecf20Sopenharmony_ci "pmc_alloc: allocated pring %p => %pad. %zd x %d = total %zd bytes\n", 1268c2ecf20Sopenharmony_ci pmc->pring_va, &pmc->pring_pa, 1278c2ecf20Sopenharmony_ci sizeof(struct vring_tx_desc), 1288c2ecf20Sopenharmony_ci num_descriptors, 1298c2ecf20Sopenharmony_ci sizeof(struct vring_tx_desc) * num_descriptors); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (!pmc->pring_va) { 1328c2ecf20Sopenharmony_ci wil_err(wil, "ERROR allocating pmc pring\n"); 1338c2ecf20Sopenharmony_ci goto release_pmc_skb_list; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* initially, all descriptors are SW owned 1378c2ecf20Sopenharmony_ci * For Tx, Rx, and PMC, ownership bit is at the same location, thus 1388c2ecf20Sopenharmony_ci * we can use any 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci for (i = 0; i < num_descriptors; i++) { 1418c2ecf20Sopenharmony_ci struct vring_tx_desc *_d = &pmc->pring_va[i]; 1428c2ecf20Sopenharmony_ci struct vring_tx_desc dd = {}, *d = ⅆ 1438c2ecf20Sopenharmony_ci int j = 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci pmc->descriptors[i].va = dma_alloc_coherent(dev, 1468c2ecf20Sopenharmony_ci descriptor_size, 1478c2ecf20Sopenharmony_ci &pmc->descriptors[i].pa, 1488c2ecf20Sopenharmony_ci GFP_KERNEL); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (unlikely(!pmc->descriptors[i].va)) { 1518c2ecf20Sopenharmony_ci wil_err(wil, "ERROR allocating pmc descriptor %d", i); 1528c2ecf20Sopenharmony_ci goto release_pmc_skbs; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci for (j = 0; j < descriptor_size / sizeof(u32); j++) { 1568c2ecf20Sopenharmony_ci u32 *p = (u32 *)pmc->descriptors[i].va + j; 1578c2ecf20Sopenharmony_ci *p = PCM_DATA_INVALID_DW_VAL | j; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* configure dma descriptor */ 1618c2ecf20Sopenharmony_ci d->dma.addr.addr_low = 1628c2ecf20Sopenharmony_ci cpu_to_le32(lower_32_bits(pmc->descriptors[i].pa)); 1638c2ecf20Sopenharmony_ci d->dma.addr.addr_high = 1648c2ecf20Sopenharmony_ci cpu_to_le16((u16)upper_32_bits(pmc->descriptors[i].pa)); 1658c2ecf20Sopenharmony_ci d->dma.status = 0; /* 0 = HW_OWNED */ 1668c2ecf20Sopenharmony_ci d->dma.length = cpu_to_le16(descriptor_size); 1678c2ecf20Sopenharmony_ci d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT; 1688c2ecf20Sopenharmony_ci *_d = *d; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci wil_dbg_misc(wil, "pmc_alloc: allocated successfully\n"); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci pmc_cmd.op = WMI_PMC_ALLOCATE; 1748c2ecf20Sopenharmony_ci pmc_cmd.ring_size = cpu_to_le16(pmc->num_descriptors); 1758c2ecf20Sopenharmony_ci pmc_cmd.mem_base = cpu_to_le64(pmc->pring_pa); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci wil_dbg_misc(wil, "pmc_alloc: send WMI_PMC_CMD with ALLOCATE op\n"); 1788c2ecf20Sopenharmony_ci pmc->last_cmd_status = wmi_send(wil, 1798c2ecf20Sopenharmony_ci WMI_PMC_CMDID, 1808c2ecf20Sopenharmony_ci vif->mid, 1818c2ecf20Sopenharmony_ci &pmc_cmd, 1828c2ecf20Sopenharmony_ci sizeof(pmc_cmd)); 1838c2ecf20Sopenharmony_ci if (pmc->last_cmd_status) { 1848c2ecf20Sopenharmony_ci wil_err(wil, 1858c2ecf20Sopenharmony_ci "WMI_PMC_CMD with ALLOCATE op failed with status %d", 1868c2ecf20Sopenharmony_ci pmc->last_cmd_status); 1878c2ecf20Sopenharmony_ci goto release_pmc_skbs; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci mutex_unlock(&pmc->lock); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cirelease_pmc_skbs: 1958c2ecf20Sopenharmony_ci wil_err(wil, "exit on error: Releasing skbs...\n"); 1968c2ecf20Sopenharmony_ci for (i = 0; i < num_descriptors && pmc->descriptors[i].va; i++) { 1978c2ecf20Sopenharmony_ci dma_free_coherent(dev, 1988c2ecf20Sopenharmony_ci descriptor_size, 1998c2ecf20Sopenharmony_ci pmc->descriptors[i].va, 2008c2ecf20Sopenharmony_ci pmc->descriptors[i].pa); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci pmc->descriptors[i].va = NULL; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci wil_err(wil, "exit on error: Releasing pring...\n"); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci dma_free_coherent(dev, 2078c2ecf20Sopenharmony_ci sizeof(struct vring_tx_desc) * num_descriptors, 2088c2ecf20Sopenharmony_ci pmc->pring_va, 2098c2ecf20Sopenharmony_ci pmc->pring_pa); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci pmc->pring_va = NULL; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cirelease_pmc_skb_list: 2148c2ecf20Sopenharmony_ci wil_err(wil, "exit on error: Releasing descriptors info list...\n"); 2158c2ecf20Sopenharmony_ci kfree(pmc->descriptors); 2168c2ecf20Sopenharmony_ci pmc->descriptors = NULL; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cino_release_err: 2198c2ecf20Sopenharmony_ci pmc->last_cmd_status = last_cmd_err; 2208c2ecf20Sopenharmony_ci mutex_unlock(&pmc->lock); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci/* Traverse the p-ring and release all buffers. 2248c2ecf20Sopenharmony_ci * At the end release the p-ring memory 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_civoid wil_pmc_free(struct wil6210_priv *wil, int send_pmc_cmd) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct pmc_ctx *pmc = &wil->pmc; 2298c2ecf20Sopenharmony_ci struct device *dev = wil_to_dev(wil); 2308c2ecf20Sopenharmony_ci struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); 2318c2ecf20Sopenharmony_ci struct wmi_pmc_cmd pmc_cmd = {0}; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci mutex_lock(&pmc->lock); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci pmc->last_cmd_status = 0; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (!wil_is_pmc_allocated(pmc)) { 2388c2ecf20Sopenharmony_ci wil_dbg_misc(wil, 2398c2ecf20Sopenharmony_ci "pmc_free: Error, can't free - not allocated\n"); 2408c2ecf20Sopenharmony_ci pmc->last_cmd_status = -EPERM; 2418c2ecf20Sopenharmony_ci mutex_unlock(&pmc->lock); 2428c2ecf20Sopenharmony_ci return; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (send_pmc_cmd) { 2468c2ecf20Sopenharmony_ci wil_dbg_misc(wil, "send WMI_PMC_CMD with RELEASE op\n"); 2478c2ecf20Sopenharmony_ci pmc_cmd.op = WMI_PMC_RELEASE; 2488c2ecf20Sopenharmony_ci pmc->last_cmd_status = 2498c2ecf20Sopenharmony_ci wmi_send(wil, WMI_PMC_CMDID, vif->mid, 2508c2ecf20Sopenharmony_ci &pmc_cmd, sizeof(pmc_cmd)); 2518c2ecf20Sopenharmony_ci if (pmc->last_cmd_status) { 2528c2ecf20Sopenharmony_ci wil_err(wil, 2538c2ecf20Sopenharmony_ci "WMI_PMC_CMD with RELEASE op failed, status %d", 2548c2ecf20Sopenharmony_ci pmc->last_cmd_status); 2558c2ecf20Sopenharmony_ci /* There's nothing we can do with this error. 2568c2ecf20Sopenharmony_ci * Normally, it should never occur. 2578c2ecf20Sopenharmony_ci * Continue to freeing all memory allocated for pmc. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (pmc->pring_va) { 2638c2ecf20Sopenharmony_ci size_t buf_size = sizeof(struct vring_tx_desc) * 2648c2ecf20Sopenharmony_ci pmc->num_descriptors; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci wil_dbg_misc(wil, "pmc_free: free pring va %p\n", 2678c2ecf20Sopenharmony_ci pmc->pring_va); 2688c2ecf20Sopenharmony_ci dma_free_coherent(dev, buf_size, pmc->pring_va, pmc->pring_pa); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci pmc->pring_va = NULL; 2718c2ecf20Sopenharmony_ci } else { 2728c2ecf20Sopenharmony_ci pmc->last_cmd_status = -ENOENT; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (pmc->descriptors) { 2768c2ecf20Sopenharmony_ci int i; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci for (i = 0; 2798c2ecf20Sopenharmony_ci i < pmc->num_descriptors && pmc->descriptors[i].va; i++) { 2808c2ecf20Sopenharmony_ci dma_free_coherent(dev, 2818c2ecf20Sopenharmony_ci pmc->descriptor_size, 2828c2ecf20Sopenharmony_ci pmc->descriptors[i].va, 2838c2ecf20Sopenharmony_ci pmc->descriptors[i].pa); 2848c2ecf20Sopenharmony_ci pmc->descriptors[i].va = NULL; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci wil_dbg_misc(wil, "pmc_free: free descriptor info %d/%d\n", i, 2878c2ecf20Sopenharmony_ci pmc->num_descriptors); 2888c2ecf20Sopenharmony_ci wil_dbg_misc(wil, 2898c2ecf20Sopenharmony_ci "pmc_free: free pmc descriptors info list %p\n", 2908c2ecf20Sopenharmony_ci pmc->descriptors); 2918c2ecf20Sopenharmony_ci kfree(pmc->descriptors); 2928c2ecf20Sopenharmony_ci pmc->descriptors = NULL; 2938c2ecf20Sopenharmony_ci } else { 2948c2ecf20Sopenharmony_ci pmc->last_cmd_status = -ENOENT; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci mutex_unlock(&pmc->lock); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/* Status of the last operation requested via debugfs: alloc/free/read. 3018c2ecf20Sopenharmony_ci * 0 - success or negative errno 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_ciint wil_pmc_last_cmd_status(struct wil6210_priv *wil) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci wil_dbg_misc(wil, "pmc_last_cmd_status: status %d\n", 3068c2ecf20Sopenharmony_ci wil->pmc.last_cmd_status); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return wil->pmc.last_cmd_status; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/* Read from required position up to the end of current descriptor, 3128c2ecf20Sopenharmony_ci * depends on descriptor size configured during alloc request. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_cissize_t wil_pmc_read(struct file *filp, char __user *buf, size_t count, 3158c2ecf20Sopenharmony_ci loff_t *f_pos) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct wil6210_priv *wil = filp->private_data; 3188c2ecf20Sopenharmony_ci struct pmc_ctx *pmc = &wil->pmc; 3198c2ecf20Sopenharmony_ci size_t retval = 0; 3208c2ecf20Sopenharmony_ci unsigned long long idx; 3218c2ecf20Sopenharmony_ci loff_t offset; 3228c2ecf20Sopenharmony_ci size_t pmc_size; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci mutex_lock(&pmc->lock); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (!wil_is_pmc_allocated(pmc)) { 3278c2ecf20Sopenharmony_ci wil_err(wil, "error, pmc is not allocated!\n"); 3288c2ecf20Sopenharmony_ci pmc->last_cmd_status = -EPERM; 3298c2ecf20Sopenharmony_ci mutex_unlock(&pmc->lock); 3308c2ecf20Sopenharmony_ci return -EPERM; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci pmc_size = pmc->descriptor_size * pmc->num_descriptors; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci wil_dbg_misc(wil, 3368c2ecf20Sopenharmony_ci "pmc_read: size %u, pos %lld\n", 3378c2ecf20Sopenharmony_ci (u32)count, *f_pos); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci pmc->last_cmd_status = 0; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci idx = *f_pos; 3428c2ecf20Sopenharmony_ci do_div(idx, pmc->descriptor_size); 3438c2ecf20Sopenharmony_ci offset = *f_pos - (idx * pmc->descriptor_size); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (*f_pos >= pmc_size) { 3468c2ecf20Sopenharmony_ci wil_dbg_misc(wil, 3478c2ecf20Sopenharmony_ci "pmc_read: reached end of pmc buf: %lld >= %u\n", 3488c2ecf20Sopenharmony_ci *f_pos, (u32)pmc_size); 3498c2ecf20Sopenharmony_ci pmc->last_cmd_status = -ERANGE; 3508c2ecf20Sopenharmony_ci goto out; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci wil_dbg_misc(wil, 3548c2ecf20Sopenharmony_ci "pmc_read: read from pos %lld (descriptor %llu, offset %llu) %zu bytes\n", 3558c2ecf20Sopenharmony_ci *f_pos, idx, offset, count); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* if no errors, return the copied byte count */ 3588c2ecf20Sopenharmony_ci retval = simple_read_from_buffer(buf, 3598c2ecf20Sopenharmony_ci count, 3608c2ecf20Sopenharmony_ci &offset, 3618c2ecf20Sopenharmony_ci pmc->descriptors[idx].va, 3628c2ecf20Sopenharmony_ci pmc->descriptor_size); 3638c2ecf20Sopenharmony_ci *f_pos += retval; 3648c2ecf20Sopenharmony_ciout: 3658c2ecf20Sopenharmony_ci mutex_unlock(&pmc->lock); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return retval; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ciloff_t wil_pmc_llseek(struct file *filp, loff_t off, int whence) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci loff_t newpos; 3738c2ecf20Sopenharmony_ci struct wil6210_priv *wil = filp->private_data; 3748c2ecf20Sopenharmony_ci struct pmc_ctx *pmc = &wil->pmc; 3758c2ecf20Sopenharmony_ci size_t pmc_size; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci mutex_lock(&pmc->lock); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (!wil_is_pmc_allocated(pmc)) { 3808c2ecf20Sopenharmony_ci wil_err(wil, "error, pmc is not allocated!\n"); 3818c2ecf20Sopenharmony_ci pmc->last_cmd_status = -EPERM; 3828c2ecf20Sopenharmony_ci mutex_unlock(&pmc->lock); 3838c2ecf20Sopenharmony_ci return -EPERM; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci pmc_size = pmc->descriptor_size * pmc->num_descriptors; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci switch (whence) { 3898c2ecf20Sopenharmony_ci case 0: /* SEEK_SET */ 3908c2ecf20Sopenharmony_ci newpos = off; 3918c2ecf20Sopenharmony_ci break; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci case 1: /* SEEK_CUR */ 3948c2ecf20Sopenharmony_ci newpos = filp->f_pos + off; 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci case 2: /* SEEK_END */ 3988c2ecf20Sopenharmony_ci newpos = pmc_size; 3998c2ecf20Sopenharmony_ci break; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci default: /* can't happen */ 4028c2ecf20Sopenharmony_ci newpos = -EINVAL; 4038c2ecf20Sopenharmony_ci goto out; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (newpos < 0) { 4078c2ecf20Sopenharmony_ci newpos = -EINVAL; 4088c2ecf20Sopenharmony_ci goto out; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci if (newpos > pmc_size) 4118c2ecf20Sopenharmony_ci newpos = pmc_size; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci filp->f_pos = newpos; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ciout: 4168c2ecf20Sopenharmony_ci mutex_unlock(&pmc->lock); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return newpos; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ciint wil_pmcring_read(struct seq_file *s, void *data) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct wil6210_priv *wil = s->private; 4248c2ecf20Sopenharmony_ci struct pmc_ctx *pmc = &wil->pmc; 4258c2ecf20Sopenharmony_ci size_t pmc_ring_size = 4268c2ecf20Sopenharmony_ci sizeof(struct vring_rx_desc) * pmc->num_descriptors; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci mutex_lock(&pmc->lock); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (!wil_is_pmc_allocated(pmc)) { 4318c2ecf20Sopenharmony_ci wil_err(wil, "error, pmc is not allocated!\n"); 4328c2ecf20Sopenharmony_ci pmc->last_cmd_status = -EPERM; 4338c2ecf20Sopenharmony_ci mutex_unlock(&pmc->lock); 4348c2ecf20Sopenharmony_ci return -EPERM; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci wil_dbg_misc(wil, "pmcring_read: size %zu\n", pmc_ring_size); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci seq_write(s, pmc->pring_va, pmc_ring_size); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci mutex_unlock(&pmc->lock); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return 0; 4448c2ecf20Sopenharmony_ci} 445