162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2012-2015,2017 Qualcomm Atheros, Inc.
462306a36Sopenharmony_ci * Copyright (c) 2018, The Linux Foundation. All rights reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/types.h>
862306a36Sopenharmony_ci#include <linux/errno.h>
962306a36Sopenharmony_ci#include <linux/fs.h>
1062306a36Sopenharmony_ci#include <linux/seq_file.h>
1162306a36Sopenharmony_ci#include "wmi.h"
1262306a36Sopenharmony_ci#include "wil6210.h"
1362306a36Sopenharmony_ci#include "txrx.h"
1462306a36Sopenharmony_ci#include "pmc.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistruct desc_alloc_info {
1762306a36Sopenharmony_ci	dma_addr_t pa;
1862306a36Sopenharmony_ci	void	  *va;
1962306a36Sopenharmony_ci};
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic int wil_is_pmc_allocated(struct pmc_ctx *pmc)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	return !!pmc->pring_va;
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_civoid wil_pmc_init(struct wil6210_priv *wil)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	memset(&wil->pmc, 0, sizeof(struct pmc_ctx));
2962306a36Sopenharmony_ci	mutex_init(&wil->pmc.lock);
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* Allocate the physical ring (p-ring) and the required
3362306a36Sopenharmony_ci * number of descriptors of required size.
3462306a36Sopenharmony_ci * Initialize the descriptors as required by pmc dma.
3562306a36Sopenharmony_ci * The descriptors' buffers dwords are initialized to hold
3662306a36Sopenharmony_ci * dword's serial number in the lsw and reserved value
3762306a36Sopenharmony_ci * PCM_DATA_INVALID_DW_VAL in the msw.
3862306a36Sopenharmony_ci */
3962306a36Sopenharmony_civoid wil_pmc_alloc(struct wil6210_priv *wil,
4062306a36Sopenharmony_ci		   int num_descriptors,
4162306a36Sopenharmony_ci		   int descriptor_size)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	u32 i;
4462306a36Sopenharmony_ci	struct pmc_ctx *pmc = &wil->pmc;
4562306a36Sopenharmony_ci	struct device *dev = wil_to_dev(wil);
4662306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
4762306a36Sopenharmony_ci	struct wmi_pmc_cmd pmc_cmd = {0};
4862306a36Sopenharmony_ci	int last_cmd_err = -ENOMEM;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	mutex_lock(&pmc->lock);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (wil_is_pmc_allocated(pmc)) {
5362306a36Sopenharmony_ci		/* sanity check */
5462306a36Sopenharmony_ci		wil_err(wil, "ERROR pmc is already allocated\n");
5562306a36Sopenharmony_ci		goto no_release_err;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci	if ((num_descriptors <= 0) || (descriptor_size <= 0)) {
5862306a36Sopenharmony_ci		wil_err(wil,
5962306a36Sopenharmony_ci			"Invalid params num_descriptors(%d), descriptor_size(%d)\n",
6062306a36Sopenharmony_ci			num_descriptors, descriptor_size);
6162306a36Sopenharmony_ci		last_cmd_err = -EINVAL;
6262306a36Sopenharmony_ci		goto no_release_err;
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (num_descriptors > (1 << WIL_RING_SIZE_ORDER_MAX)) {
6662306a36Sopenharmony_ci		wil_err(wil,
6762306a36Sopenharmony_ci			"num_descriptors(%d) exceeds max ring size %d\n",
6862306a36Sopenharmony_ci			num_descriptors, 1 << WIL_RING_SIZE_ORDER_MAX);
6962306a36Sopenharmony_ci		last_cmd_err = -EINVAL;
7062306a36Sopenharmony_ci		goto no_release_err;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (num_descriptors > INT_MAX / descriptor_size) {
7462306a36Sopenharmony_ci		wil_err(wil,
7562306a36Sopenharmony_ci			"Overflow in num_descriptors(%d)*descriptor_size(%d)\n",
7662306a36Sopenharmony_ci			num_descriptors, descriptor_size);
7762306a36Sopenharmony_ci		last_cmd_err = -EINVAL;
7862306a36Sopenharmony_ci		goto no_release_err;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	pmc->num_descriptors = num_descriptors;
8262306a36Sopenharmony_ci	pmc->descriptor_size = descriptor_size;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	wil_dbg_misc(wil, "pmc_alloc: %d descriptors x %d bytes each\n",
8562306a36Sopenharmony_ci		     num_descriptors, descriptor_size);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* allocate descriptors info list in pmc context*/
8862306a36Sopenharmony_ci	pmc->descriptors = kcalloc(num_descriptors,
8962306a36Sopenharmony_ci				  sizeof(struct desc_alloc_info),
9062306a36Sopenharmony_ci				  GFP_KERNEL);
9162306a36Sopenharmony_ci	if (!pmc->descriptors) {
9262306a36Sopenharmony_ci		wil_err(wil, "ERROR allocating pmc skb list\n");
9362306a36Sopenharmony_ci		goto no_release_err;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	wil_dbg_misc(wil, "pmc_alloc: allocated descriptors info list %p\n",
9762306a36Sopenharmony_ci		     pmc->descriptors);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	/* Allocate pring buffer and descriptors.
10062306a36Sopenharmony_ci	 * vring->va should be aligned on its size rounded up to power of 2
10162306a36Sopenharmony_ci	 * This is granted by the dma_alloc_coherent.
10262306a36Sopenharmony_ci	 *
10362306a36Sopenharmony_ci	 * HW has limitation that all vrings addresses must share the same
10462306a36Sopenharmony_ci	 * upper 16 msb bits part of 48 bits address. To workaround that,
10562306a36Sopenharmony_ci	 * if we are using more than 32 bit addresses switch to 32 bit
10662306a36Sopenharmony_ci	 * allocation before allocating vring memory.
10762306a36Sopenharmony_ci	 *
10862306a36Sopenharmony_ci	 * There's no check for the return value of dma_set_mask_and_coherent,
10962306a36Sopenharmony_ci	 * since we assume if we were able to set the mask during
11062306a36Sopenharmony_ci	 * initialization in this system it will not fail if we set it again
11162306a36Sopenharmony_ci	 */
11262306a36Sopenharmony_ci	if (wil->dma_addr_size > 32)
11362306a36Sopenharmony_ci		dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	pmc->pring_va = dma_alloc_coherent(dev,
11662306a36Sopenharmony_ci			sizeof(struct vring_tx_desc) * num_descriptors,
11762306a36Sopenharmony_ci			&pmc->pring_pa,
11862306a36Sopenharmony_ci			GFP_KERNEL);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (wil->dma_addr_size > 32)
12162306a36Sopenharmony_ci		dma_set_mask_and_coherent(dev,
12262306a36Sopenharmony_ci					  DMA_BIT_MASK(wil->dma_addr_size));
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	wil_dbg_misc(wil,
12562306a36Sopenharmony_ci		     "pmc_alloc: allocated pring %p => %pad. %zd x %d = total %zd bytes\n",
12662306a36Sopenharmony_ci		     pmc->pring_va, &pmc->pring_pa,
12762306a36Sopenharmony_ci		     sizeof(struct vring_tx_desc),
12862306a36Sopenharmony_ci		     num_descriptors,
12962306a36Sopenharmony_ci		     sizeof(struct vring_tx_desc) * num_descriptors);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (!pmc->pring_va) {
13262306a36Sopenharmony_ci		wil_err(wil, "ERROR allocating pmc pring\n");
13362306a36Sopenharmony_ci		goto release_pmc_skb_list;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* initially, all descriptors are SW owned
13762306a36Sopenharmony_ci	 * For Tx, Rx, and PMC, ownership bit is at the same location, thus
13862306a36Sopenharmony_ci	 * we can use any
13962306a36Sopenharmony_ci	 */
14062306a36Sopenharmony_ci	for (i = 0; i < num_descriptors; i++) {
14162306a36Sopenharmony_ci		struct vring_tx_desc *_d = &pmc->pring_va[i];
14262306a36Sopenharmony_ci		struct vring_tx_desc dd = {}, *d = &dd;
14362306a36Sopenharmony_ci		int j = 0;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		pmc->descriptors[i].va = dma_alloc_coherent(dev,
14662306a36Sopenharmony_ci			descriptor_size,
14762306a36Sopenharmony_ci			&pmc->descriptors[i].pa,
14862306a36Sopenharmony_ci			GFP_KERNEL);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci		if (unlikely(!pmc->descriptors[i].va)) {
15162306a36Sopenharmony_ci			wil_err(wil, "ERROR allocating pmc descriptor %d", i);
15262306a36Sopenharmony_ci			goto release_pmc_skbs;
15362306a36Sopenharmony_ci		}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci		for (j = 0; j < descriptor_size / sizeof(u32); j++) {
15662306a36Sopenharmony_ci			u32 *p = (u32 *)pmc->descriptors[i].va + j;
15762306a36Sopenharmony_ci			*p = PCM_DATA_INVALID_DW_VAL | j;
15862306a36Sopenharmony_ci		}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		/* configure dma descriptor */
16162306a36Sopenharmony_ci		d->dma.addr.addr_low =
16262306a36Sopenharmony_ci			cpu_to_le32(lower_32_bits(pmc->descriptors[i].pa));
16362306a36Sopenharmony_ci		d->dma.addr.addr_high =
16462306a36Sopenharmony_ci			cpu_to_le16((u16)upper_32_bits(pmc->descriptors[i].pa));
16562306a36Sopenharmony_ci		d->dma.status = 0; /* 0 = HW_OWNED */
16662306a36Sopenharmony_ci		d->dma.length = cpu_to_le16(descriptor_size);
16762306a36Sopenharmony_ci		d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT;
16862306a36Sopenharmony_ci		*_d = *d;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	wil_dbg_misc(wil, "pmc_alloc: allocated successfully\n");
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	pmc_cmd.op = WMI_PMC_ALLOCATE;
17462306a36Sopenharmony_ci	pmc_cmd.ring_size = cpu_to_le16(pmc->num_descriptors);
17562306a36Sopenharmony_ci	pmc_cmd.mem_base = cpu_to_le64(pmc->pring_pa);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	wil_dbg_misc(wil, "pmc_alloc: send WMI_PMC_CMD with ALLOCATE op\n");
17862306a36Sopenharmony_ci	pmc->last_cmd_status = wmi_send(wil,
17962306a36Sopenharmony_ci					WMI_PMC_CMDID,
18062306a36Sopenharmony_ci					vif->mid,
18162306a36Sopenharmony_ci					&pmc_cmd,
18262306a36Sopenharmony_ci					sizeof(pmc_cmd));
18362306a36Sopenharmony_ci	if (pmc->last_cmd_status) {
18462306a36Sopenharmony_ci		wil_err(wil,
18562306a36Sopenharmony_ci			"WMI_PMC_CMD with ALLOCATE op failed with status %d",
18662306a36Sopenharmony_ci			pmc->last_cmd_status);
18762306a36Sopenharmony_ci		goto release_pmc_skbs;
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	mutex_unlock(&pmc->lock);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cirelease_pmc_skbs:
19562306a36Sopenharmony_ci	wil_err(wil, "exit on error: Releasing skbs...\n");
19662306a36Sopenharmony_ci	for (i = 0; i < num_descriptors && pmc->descriptors[i].va; i++) {
19762306a36Sopenharmony_ci		dma_free_coherent(dev,
19862306a36Sopenharmony_ci				  descriptor_size,
19962306a36Sopenharmony_ci				  pmc->descriptors[i].va,
20062306a36Sopenharmony_ci				  pmc->descriptors[i].pa);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		pmc->descriptors[i].va = NULL;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci	wil_err(wil, "exit on error: Releasing pring...\n");
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	dma_free_coherent(dev,
20762306a36Sopenharmony_ci			  sizeof(struct vring_tx_desc) * num_descriptors,
20862306a36Sopenharmony_ci			  pmc->pring_va,
20962306a36Sopenharmony_ci			  pmc->pring_pa);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	pmc->pring_va = NULL;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cirelease_pmc_skb_list:
21462306a36Sopenharmony_ci	wil_err(wil, "exit on error: Releasing descriptors info list...\n");
21562306a36Sopenharmony_ci	kfree(pmc->descriptors);
21662306a36Sopenharmony_ci	pmc->descriptors = NULL;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cino_release_err:
21962306a36Sopenharmony_ci	pmc->last_cmd_status = last_cmd_err;
22062306a36Sopenharmony_ci	mutex_unlock(&pmc->lock);
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci/* Traverse the p-ring and release all buffers.
22462306a36Sopenharmony_ci * At the end release the p-ring memory
22562306a36Sopenharmony_ci */
22662306a36Sopenharmony_civoid wil_pmc_free(struct wil6210_priv *wil, int send_pmc_cmd)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct pmc_ctx *pmc = &wil->pmc;
22962306a36Sopenharmony_ci	struct device *dev = wil_to_dev(wil);
23062306a36Sopenharmony_ci	struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
23162306a36Sopenharmony_ci	struct wmi_pmc_cmd pmc_cmd = {0};
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	mutex_lock(&pmc->lock);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	pmc->last_cmd_status = 0;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if (!wil_is_pmc_allocated(pmc)) {
23862306a36Sopenharmony_ci		wil_dbg_misc(wil,
23962306a36Sopenharmony_ci			     "pmc_free: Error, can't free - not allocated\n");
24062306a36Sopenharmony_ci		pmc->last_cmd_status = -EPERM;
24162306a36Sopenharmony_ci		mutex_unlock(&pmc->lock);
24262306a36Sopenharmony_ci		return;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (send_pmc_cmd) {
24662306a36Sopenharmony_ci		wil_dbg_misc(wil, "send WMI_PMC_CMD with RELEASE op\n");
24762306a36Sopenharmony_ci		pmc_cmd.op = WMI_PMC_RELEASE;
24862306a36Sopenharmony_ci		pmc->last_cmd_status =
24962306a36Sopenharmony_ci				wmi_send(wil, WMI_PMC_CMDID, vif->mid,
25062306a36Sopenharmony_ci					 &pmc_cmd, sizeof(pmc_cmd));
25162306a36Sopenharmony_ci		if (pmc->last_cmd_status) {
25262306a36Sopenharmony_ci			wil_err(wil,
25362306a36Sopenharmony_ci				"WMI_PMC_CMD with RELEASE op failed, status %d",
25462306a36Sopenharmony_ci				pmc->last_cmd_status);
25562306a36Sopenharmony_ci			/* There's nothing we can do with this error.
25662306a36Sopenharmony_ci			 * Normally, it should never occur.
25762306a36Sopenharmony_ci			 * Continue to freeing all memory allocated for pmc.
25862306a36Sopenharmony_ci			 */
25962306a36Sopenharmony_ci		}
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (pmc->pring_va) {
26362306a36Sopenharmony_ci		size_t buf_size = sizeof(struct vring_tx_desc) *
26462306a36Sopenharmony_ci				  pmc->num_descriptors;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		wil_dbg_misc(wil, "pmc_free: free pring va %p\n",
26762306a36Sopenharmony_ci			     pmc->pring_va);
26862306a36Sopenharmony_ci		dma_free_coherent(dev, buf_size, pmc->pring_va, pmc->pring_pa);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		pmc->pring_va = NULL;
27162306a36Sopenharmony_ci	} else {
27262306a36Sopenharmony_ci		pmc->last_cmd_status = -ENOENT;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (pmc->descriptors) {
27662306a36Sopenharmony_ci		int i;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		for (i = 0;
27962306a36Sopenharmony_ci		     i < pmc->num_descriptors && pmc->descriptors[i].va; i++) {
28062306a36Sopenharmony_ci			dma_free_coherent(dev,
28162306a36Sopenharmony_ci					  pmc->descriptor_size,
28262306a36Sopenharmony_ci					  pmc->descriptors[i].va,
28362306a36Sopenharmony_ci					  pmc->descriptors[i].pa);
28462306a36Sopenharmony_ci			pmc->descriptors[i].va = NULL;
28562306a36Sopenharmony_ci		}
28662306a36Sopenharmony_ci		wil_dbg_misc(wil, "pmc_free: free descriptor info %d/%d\n", i,
28762306a36Sopenharmony_ci			     pmc->num_descriptors);
28862306a36Sopenharmony_ci		wil_dbg_misc(wil,
28962306a36Sopenharmony_ci			     "pmc_free: free pmc descriptors info list %p\n",
29062306a36Sopenharmony_ci			     pmc->descriptors);
29162306a36Sopenharmony_ci		kfree(pmc->descriptors);
29262306a36Sopenharmony_ci		pmc->descriptors = NULL;
29362306a36Sopenharmony_ci	} else {
29462306a36Sopenharmony_ci		pmc->last_cmd_status = -ENOENT;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	mutex_unlock(&pmc->lock);
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci/* Status of the last operation requested via debugfs: alloc/free/read.
30162306a36Sopenharmony_ci * 0 - success or negative errno
30262306a36Sopenharmony_ci */
30362306a36Sopenharmony_ciint wil_pmc_last_cmd_status(struct wil6210_priv *wil)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	wil_dbg_misc(wil, "pmc_last_cmd_status: status %d\n",
30662306a36Sopenharmony_ci		     wil->pmc.last_cmd_status);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return wil->pmc.last_cmd_status;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci/* Read from required position up to the end of current descriptor,
31262306a36Sopenharmony_ci * depends on descriptor size configured during alloc request.
31362306a36Sopenharmony_ci */
31462306a36Sopenharmony_cissize_t wil_pmc_read(struct file *filp, char __user *buf, size_t count,
31562306a36Sopenharmony_ci		     loff_t *f_pos)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct wil6210_priv *wil = filp->private_data;
31862306a36Sopenharmony_ci	struct pmc_ctx *pmc = &wil->pmc;
31962306a36Sopenharmony_ci	size_t retval = 0;
32062306a36Sopenharmony_ci	unsigned long long idx;
32162306a36Sopenharmony_ci	loff_t offset;
32262306a36Sopenharmony_ci	size_t pmc_size;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	mutex_lock(&pmc->lock);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (!wil_is_pmc_allocated(pmc)) {
32762306a36Sopenharmony_ci		wil_err(wil, "error, pmc is not allocated!\n");
32862306a36Sopenharmony_ci		pmc->last_cmd_status = -EPERM;
32962306a36Sopenharmony_ci		mutex_unlock(&pmc->lock);
33062306a36Sopenharmony_ci		return -EPERM;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	pmc_size = pmc->descriptor_size * pmc->num_descriptors;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	wil_dbg_misc(wil,
33662306a36Sopenharmony_ci		     "pmc_read: size %u, pos %lld\n",
33762306a36Sopenharmony_ci		     (u32)count, *f_pos);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	pmc->last_cmd_status = 0;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	idx = *f_pos;
34262306a36Sopenharmony_ci	do_div(idx, pmc->descriptor_size);
34362306a36Sopenharmony_ci	offset = *f_pos - (idx * pmc->descriptor_size);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	if (*f_pos >= pmc_size) {
34662306a36Sopenharmony_ci		wil_dbg_misc(wil,
34762306a36Sopenharmony_ci			     "pmc_read: reached end of pmc buf: %lld >= %u\n",
34862306a36Sopenharmony_ci			     *f_pos, (u32)pmc_size);
34962306a36Sopenharmony_ci		pmc->last_cmd_status = -ERANGE;
35062306a36Sopenharmony_ci		goto out;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	wil_dbg_misc(wil,
35462306a36Sopenharmony_ci		     "pmc_read: read from pos %lld (descriptor %llu, offset %llu) %zu bytes\n",
35562306a36Sopenharmony_ci		     *f_pos, idx, offset, count);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	/* if no errors, return the copied byte count */
35862306a36Sopenharmony_ci	retval = simple_read_from_buffer(buf,
35962306a36Sopenharmony_ci					 count,
36062306a36Sopenharmony_ci					 &offset,
36162306a36Sopenharmony_ci					 pmc->descriptors[idx].va,
36262306a36Sopenharmony_ci					 pmc->descriptor_size);
36362306a36Sopenharmony_ci	*f_pos += retval;
36462306a36Sopenharmony_ciout:
36562306a36Sopenharmony_ci	mutex_unlock(&pmc->lock);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	return retval;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ciloff_t wil_pmc_llseek(struct file *filp, loff_t off, int whence)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	loff_t newpos;
37362306a36Sopenharmony_ci	struct wil6210_priv *wil = filp->private_data;
37462306a36Sopenharmony_ci	struct pmc_ctx *pmc = &wil->pmc;
37562306a36Sopenharmony_ci	size_t pmc_size;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	mutex_lock(&pmc->lock);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (!wil_is_pmc_allocated(pmc)) {
38062306a36Sopenharmony_ci		wil_err(wil, "error, pmc is not allocated!\n");
38162306a36Sopenharmony_ci		pmc->last_cmd_status = -EPERM;
38262306a36Sopenharmony_ci		mutex_unlock(&pmc->lock);
38362306a36Sopenharmony_ci		return -EPERM;
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	pmc_size = pmc->descriptor_size * pmc->num_descriptors;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	switch (whence) {
38962306a36Sopenharmony_ci	case 0: /* SEEK_SET */
39062306a36Sopenharmony_ci		newpos = off;
39162306a36Sopenharmony_ci		break;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	case 1: /* SEEK_CUR */
39462306a36Sopenharmony_ci		newpos = filp->f_pos + off;
39562306a36Sopenharmony_ci		break;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	case 2: /* SEEK_END */
39862306a36Sopenharmony_ci		newpos = pmc_size;
39962306a36Sopenharmony_ci		break;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	default: /* can't happen */
40262306a36Sopenharmony_ci		newpos = -EINVAL;
40362306a36Sopenharmony_ci		goto out;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (newpos < 0) {
40762306a36Sopenharmony_ci		newpos = -EINVAL;
40862306a36Sopenharmony_ci		goto out;
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci	if (newpos > pmc_size)
41162306a36Sopenharmony_ci		newpos = pmc_size;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	filp->f_pos = newpos;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ciout:
41662306a36Sopenharmony_ci	mutex_unlock(&pmc->lock);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	return newpos;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ciint wil_pmcring_read(struct seq_file *s, void *data)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct wil6210_priv *wil = s->private;
42462306a36Sopenharmony_ci	struct pmc_ctx *pmc = &wil->pmc;
42562306a36Sopenharmony_ci	size_t pmc_ring_size =
42662306a36Sopenharmony_ci		sizeof(struct vring_rx_desc) * pmc->num_descriptors;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	mutex_lock(&pmc->lock);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (!wil_is_pmc_allocated(pmc)) {
43162306a36Sopenharmony_ci		wil_err(wil, "error, pmc is not allocated!\n");
43262306a36Sopenharmony_ci		pmc->last_cmd_status = -EPERM;
43362306a36Sopenharmony_ci		mutex_unlock(&pmc->lock);
43462306a36Sopenharmony_ci		return -EPERM;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	wil_dbg_misc(wil, "pmcring_read: size %zu\n", pmc_ring_size);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	seq_write(s, pmc->pring_va, pmc_ring_size);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	mutex_unlock(&pmc->lock);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	return 0;
44462306a36Sopenharmony_ci}
445