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 = &dd;
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