162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright(c) 2019 Intel Corporation. All rights rsvd. */
362306a36Sopenharmony_ci#include <linux/init.h>
462306a36Sopenharmony_ci#include <linux/kernel.h>
562306a36Sopenharmony_ci#include <linux/module.h>
662306a36Sopenharmony_ci#include <linux/pci.h>
762306a36Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h>
862306a36Sopenharmony_ci#include <linux/dmaengine.h>
962306a36Sopenharmony_ci#include <linux/irq.h>
1062306a36Sopenharmony_ci#include <uapi/linux/idxd.h>
1162306a36Sopenharmony_ci#include "../dmaengine.h"
1262306a36Sopenharmony_ci#include "idxd.h"
1362306a36Sopenharmony_ci#include "registers.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic void idxd_cmd_exec(struct idxd_device *idxd, int cmd_code, u32 operand,
1662306a36Sopenharmony_ci			  u32 *status);
1762306a36Sopenharmony_cistatic void idxd_device_wqs_clear_state(struct idxd_device *idxd);
1862306a36Sopenharmony_cistatic void idxd_wq_disable_cleanup(struct idxd_wq *wq);
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* Interrupt control bits */
2162306a36Sopenharmony_civoid idxd_unmask_error_interrupts(struct idxd_device *idxd)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	union genctrl_reg genctrl;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	genctrl.bits = ioread32(idxd->reg_base + IDXD_GENCTRL_OFFSET);
2662306a36Sopenharmony_ci	genctrl.softerr_int_en = 1;
2762306a36Sopenharmony_ci	genctrl.halt_int_en = 1;
2862306a36Sopenharmony_ci	iowrite32(genctrl.bits, idxd->reg_base + IDXD_GENCTRL_OFFSET);
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_civoid idxd_mask_error_interrupts(struct idxd_device *idxd)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	union genctrl_reg genctrl;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	genctrl.bits = ioread32(idxd->reg_base + IDXD_GENCTRL_OFFSET);
3662306a36Sopenharmony_ci	genctrl.softerr_int_en = 0;
3762306a36Sopenharmony_ci	genctrl.halt_int_en = 0;
3862306a36Sopenharmony_ci	iowrite32(genctrl.bits, idxd->reg_base + IDXD_GENCTRL_OFFSET);
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic void free_hw_descs(struct idxd_wq *wq)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	int i;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	for (i = 0; i < wq->num_descs; i++)
4662306a36Sopenharmony_ci		kfree(wq->hw_descs[i]);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	kfree(wq->hw_descs);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int alloc_hw_descs(struct idxd_wq *wq, int num)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct device *dev = &wq->idxd->pdev->dev;
5462306a36Sopenharmony_ci	int i;
5562306a36Sopenharmony_ci	int node = dev_to_node(dev);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	wq->hw_descs = kcalloc_node(num, sizeof(struct dsa_hw_desc *),
5862306a36Sopenharmony_ci				    GFP_KERNEL, node);
5962306a36Sopenharmony_ci	if (!wq->hw_descs)
6062306a36Sopenharmony_ci		return -ENOMEM;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	for (i = 0; i < num; i++) {
6362306a36Sopenharmony_ci		wq->hw_descs[i] = kzalloc_node(sizeof(*wq->hw_descs[i]),
6462306a36Sopenharmony_ci					       GFP_KERNEL, node);
6562306a36Sopenharmony_ci		if (!wq->hw_descs[i]) {
6662306a36Sopenharmony_ci			free_hw_descs(wq);
6762306a36Sopenharmony_ci			return -ENOMEM;
6862306a36Sopenharmony_ci		}
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return 0;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void free_descs(struct idxd_wq *wq)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	int i;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	for (i = 0; i < wq->num_descs; i++)
7962306a36Sopenharmony_ci		kfree(wq->descs[i]);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	kfree(wq->descs);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int alloc_descs(struct idxd_wq *wq, int num)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct device *dev = &wq->idxd->pdev->dev;
8762306a36Sopenharmony_ci	int i;
8862306a36Sopenharmony_ci	int node = dev_to_node(dev);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	wq->descs = kcalloc_node(num, sizeof(struct idxd_desc *),
9162306a36Sopenharmony_ci				 GFP_KERNEL, node);
9262306a36Sopenharmony_ci	if (!wq->descs)
9362306a36Sopenharmony_ci		return -ENOMEM;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	for (i = 0; i < num; i++) {
9662306a36Sopenharmony_ci		wq->descs[i] = kzalloc_node(sizeof(*wq->descs[i]),
9762306a36Sopenharmony_ci					    GFP_KERNEL, node);
9862306a36Sopenharmony_ci		if (!wq->descs[i]) {
9962306a36Sopenharmony_ci			free_descs(wq);
10062306a36Sopenharmony_ci			return -ENOMEM;
10162306a36Sopenharmony_ci		}
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	return 0;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/* WQ control bits */
10862306a36Sopenharmony_ciint idxd_wq_alloc_resources(struct idxd_wq *wq)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
11162306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
11262306a36Sopenharmony_ci	int rc, num_descs, i;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (wq->type != IDXD_WQT_KERNEL)
11562306a36Sopenharmony_ci		return 0;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	num_descs = wq_dedicated(wq) ? wq->size : wq->threshold;
11862306a36Sopenharmony_ci	wq->num_descs = num_descs;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	rc = alloc_hw_descs(wq, num_descs);
12162306a36Sopenharmony_ci	if (rc < 0)
12262306a36Sopenharmony_ci		return rc;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	wq->compls_size = num_descs * idxd->data->compl_size;
12562306a36Sopenharmony_ci	wq->compls = dma_alloc_coherent(dev, wq->compls_size, &wq->compls_addr, GFP_KERNEL);
12662306a36Sopenharmony_ci	if (!wq->compls) {
12762306a36Sopenharmony_ci		rc = -ENOMEM;
12862306a36Sopenharmony_ci		goto fail_alloc_compls;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	rc = alloc_descs(wq, num_descs);
13262306a36Sopenharmony_ci	if (rc < 0)
13362306a36Sopenharmony_ci		goto fail_alloc_descs;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	rc = sbitmap_queue_init_node(&wq->sbq, num_descs, -1, false, GFP_KERNEL,
13662306a36Sopenharmony_ci				     dev_to_node(dev));
13762306a36Sopenharmony_ci	if (rc < 0)
13862306a36Sopenharmony_ci		goto fail_sbitmap_init;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	for (i = 0; i < num_descs; i++) {
14162306a36Sopenharmony_ci		struct idxd_desc *desc = wq->descs[i];
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		desc->hw = wq->hw_descs[i];
14462306a36Sopenharmony_ci		if (idxd->data->type == IDXD_TYPE_DSA)
14562306a36Sopenharmony_ci			desc->completion = &wq->compls[i];
14662306a36Sopenharmony_ci		else if (idxd->data->type == IDXD_TYPE_IAX)
14762306a36Sopenharmony_ci			desc->iax_completion = &wq->iax_compls[i];
14862306a36Sopenharmony_ci		desc->compl_dma = wq->compls_addr + idxd->data->compl_size * i;
14962306a36Sopenharmony_ci		desc->id = i;
15062306a36Sopenharmony_ci		desc->wq = wq;
15162306a36Sopenharmony_ci		desc->cpu = -1;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return 0;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci fail_sbitmap_init:
15762306a36Sopenharmony_ci	free_descs(wq);
15862306a36Sopenharmony_ci fail_alloc_descs:
15962306a36Sopenharmony_ci	dma_free_coherent(dev, wq->compls_size, wq->compls, wq->compls_addr);
16062306a36Sopenharmony_ci fail_alloc_compls:
16162306a36Sopenharmony_ci	free_hw_descs(wq);
16262306a36Sopenharmony_ci	return rc;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_civoid idxd_wq_free_resources(struct idxd_wq *wq)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct device *dev = &wq->idxd->pdev->dev;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (wq->type != IDXD_WQT_KERNEL)
17062306a36Sopenharmony_ci		return;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	free_hw_descs(wq);
17362306a36Sopenharmony_ci	free_descs(wq);
17462306a36Sopenharmony_ci	dma_free_coherent(dev, wq->compls_size, wq->compls, wq->compls_addr);
17562306a36Sopenharmony_ci	sbitmap_queue_free(&wq->sbq);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ciint idxd_wq_enable(struct idxd_wq *wq)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
18162306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
18262306a36Sopenharmony_ci	u32 status;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (wq->state == IDXD_WQ_ENABLED) {
18562306a36Sopenharmony_ci		dev_dbg(dev, "WQ %d already enabled\n", wq->id);
18662306a36Sopenharmony_ci		return 0;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	idxd_cmd_exec(idxd, IDXD_CMD_ENABLE_WQ, wq->id, &status);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (status != IDXD_CMDSTS_SUCCESS &&
19262306a36Sopenharmony_ci	    status != IDXD_CMDSTS_ERR_WQ_ENABLED) {
19362306a36Sopenharmony_ci		dev_dbg(dev, "WQ enable failed: %#x\n", status);
19462306a36Sopenharmony_ci		return -ENXIO;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	wq->state = IDXD_WQ_ENABLED;
19862306a36Sopenharmony_ci	set_bit(wq->id, idxd->wq_enable_map);
19962306a36Sopenharmony_ci	dev_dbg(dev, "WQ %d enabled\n", wq->id);
20062306a36Sopenharmony_ci	return 0;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ciint idxd_wq_disable(struct idxd_wq *wq, bool reset_config)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
20662306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
20762306a36Sopenharmony_ci	u32 status, operand;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	dev_dbg(dev, "Disabling WQ %d\n", wq->id);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (wq->state != IDXD_WQ_ENABLED) {
21262306a36Sopenharmony_ci		dev_dbg(dev, "WQ %d in wrong state: %d\n", wq->id, wq->state);
21362306a36Sopenharmony_ci		return 0;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	operand = BIT(wq->id % 16) | ((wq->id / 16) << 16);
21762306a36Sopenharmony_ci	idxd_cmd_exec(idxd, IDXD_CMD_DISABLE_WQ, operand, &status);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	if (status != IDXD_CMDSTS_SUCCESS) {
22062306a36Sopenharmony_ci		dev_dbg(dev, "WQ disable failed: %#x\n", status);
22162306a36Sopenharmony_ci		return -ENXIO;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (reset_config)
22562306a36Sopenharmony_ci		idxd_wq_disable_cleanup(wq);
22662306a36Sopenharmony_ci	clear_bit(wq->id, idxd->wq_enable_map);
22762306a36Sopenharmony_ci	wq->state = IDXD_WQ_DISABLED;
22862306a36Sopenharmony_ci	dev_dbg(dev, "WQ %d disabled\n", wq->id);
22962306a36Sopenharmony_ci	return 0;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_civoid idxd_wq_drain(struct idxd_wq *wq)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
23562306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
23662306a36Sopenharmony_ci	u32 operand;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (wq->state != IDXD_WQ_ENABLED) {
23962306a36Sopenharmony_ci		dev_dbg(dev, "WQ %d in wrong state: %d\n", wq->id, wq->state);
24062306a36Sopenharmony_ci		return;
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	dev_dbg(dev, "Draining WQ %d\n", wq->id);
24462306a36Sopenharmony_ci	operand = BIT(wq->id % 16) | ((wq->id / 16) << 16);
24562306a36Sopenharmony_ci	idxd_cmd_exec(idxd, IDXD_CMD_DRAIN_WQ, operand, NULL);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_civoid idxd_wq_reset(struct idxd_wq *wq)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
25162306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
25262306a36Sopenharmony_ci	u32 operand;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (wq->state != IDXD_WQ_ENABLED) {
25562306a36Sopenharmony_ci		dev_dbg(dev, "WQ %d in wrong state: %d\n", wq->id, wq->state);
25662306a36Sopenharmony_ci		return;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	operand = BIT(wq->id % 16) | ((wq->id / 16) << 16);
26062306a36Sopenharmony_ci	idxd_cmd_exec(idxd, IDXD_CMD_RESET_WQ, operand, NULL);
26162306a36Sopenharmony_ci	idxd_wq_disable_cleanup(wq);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ciint idxd_wq_map_portal(struct idxd_wq *wq)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
26762306a36Sopenharmony_ci	struct pci_dev *pdev = idxd->pdev;
26862306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
26962306a36Sopenharmony_ci	resource_size_t start;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	start = pci_resource_start(pdev, IDXD_WQ_BAR);
27262306a36Sopenharmony_ci	start += idxd_get_wq_portal_full_offset(wq->id, IDXD_PORTAL_LIMITED);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	wq->portal = devm_ioremap(dev, start, IDXD_PORTAL_SIZE);
27562306a36Sopenharmony_ci	if (!wq->portal)
27662306a36Sopenharmony_ci		return -ENOMEM;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	return 0;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_civoid idxd_wq_unmap_portal(struct idxd_wq *wq)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct device *dev = &wq->idxd->pdev->dev;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	devm_iounmap(dev, wq->portal);
28662306a36Sopenharmony_ci	wq->portal = NULL;
28762306a36Sopenharmony_ci	wq->portal_offset = 0;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_civoid idxd_wqs_unmap_portal(struct idxd_device *idxd)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	int i;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	for (i = 0; i < idxd->max_wqs; i++) {
29562306a36Sopenharmony_ci		struct idxd_wq *wq = idxd->wqs[i];
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		if (wq->portal)
29862306a36Sopenharmony_ci			idxd_wq_unmap_portal(wq);
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic void __idxd_wq_set_pasid_locked(struct idxd_wq *wq, int pasid)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
30562306a36Sopenharmony_ci	union wqcfg wqcfg;
30662306a36Sopenharmony_ci	unsigned int offset;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	offset = WQCFG_OFFSET(idxd, wq->id, WQCFG_PASID_IDX);
30962306a36Sopenharmony_ci	spin_lock(&idxd->dev_lock);
31062306a36Sopenharmony_ci	wqcfg.bits[WQCFG_PASID_IDX] = ioread32(idxd->reg_base + offset);
31162306a36Sopenharmony_ci	wqcfg.pasid_en = 1;
31262306a36Sopenharmony_ci	wqcfg.pasid = pasid;
31362306a36Sopenharmony_ci	wq->wqcfg->bits[WQCFG_PASID_IDX] = wqcfg.bits[WQCFG_PASID_IDX];
31462306a36Sopenharmony_ci	iowrite32(wqcfg.bits[WQCFG_PASID_IDX], idxd->reg_base + offset);
31562306a36Sopenharmony_ci	spin_unlock(&idxd->dev_lock);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ciint idxd_wq_set_pasid(struct idxd_wq *wq, int pasid)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	int rc;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	rc = idxd_wq_disable(wq, false);
32362306a36Sopenharmony_ci	if (rc < 0)
32462306a36Sopenharmony_ci		return rc;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	__idxd_wq_set_pasid_locked(wq, pasid);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	rc = idxd_wq_enable(wq);
32962306a36Sopenharmony_ci	if (rc < 0)
33062306a36Sopenharmony_ci		return rc;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	return 0;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ciint idxd_wq_disable_pasid(struct idxd_wq *wq)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
33862306a36Sopenharmony_ci	int rc;
33962306a36Sopenharmony_ci	union wqcfg wqcfg;
34062306a36Sopenharmony_ci	unsigned int offset;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	rc = idxd_wq_disable(wq, false);
34362306a36Sopenharmony_ci	if (rc < 0)
34462306a36Sopenharmony_ci		return rc;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	offset = WQCFG_OFFSET(idxd, wq->id, WQCFG_PASID_IDX);
34762306a36Sopenharmony_ci	spin_lock(&idxd->dev_lock);
34862306a36Sopenharmony_ci	wqcfg.bits[WQCFG_PASID_IDX] = ioread32(idxd->reg_base + offset);
34962306a36Sopenharmony_ci	wqcfg.pasid_en = 0;
35062306a36Sopenharmony_ci	wqcfg.pasid = 0;
35162306a36Sopenharmony_ci	iowrite32(wqcfg.bits[WQCFG_PASID_IDX], idxd->reg_base + offset);
35262306a36Sopenharmony_ci	spin_unlock(&idxd->dev_lock);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	rc = idxd_wq_enable(wq);
35562306a36Sopenharmony_ci	if (rc < 0)
35662306a36Sopenharmony_ci		return rc;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	return 0;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic void idxd_wq_disable_cleanup(struct idxd_wq *wq)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	lockdep_assert_held(&wq->wq_lock);
36662306a36Sopenharmony_ci	wq->state = IDXD_WQ_DISABLED;
36762306a36Sopenharmony_ci	memset(wq->wqcfg, 0, idxd->wqcfg_size);
36862306a36Sopenharmony_ci	wq->type = IDXD_WQT_NONE;
36962306a36Sopenharmony_ci	wq->threshold = 0;
37062306a36Sopenharmony_ci	wq->priority = 0;
37162306a36Sopenharmony_ci	wq->enqcmds_retries = IDXD_ENQCMDS_RETRIES;
37262306a36Sopenharmony_ci	wq->flags = 0;
37362306a36Sopenharmony_ci	memset(wq->name, 0, WQ_NAME_SIZE);
37462306a36Sopenharmony_ci	wq->max_xfer_bytes = WQ_DEFAULT_MAX_XFER;
37562306a36Sopenharmony_ci	idxd_wq_set_max_batch_size(idxd->data->type, wq, WQ_DEFAULT_MAX_BATCH);
37662306a36Sopenharmony_ci	if (wq->opcap_bmap)
37762306a36Sopenharmony_ci		bitmap_copy(wq->opcap_bmap, idxd->opcap_bmap, IDXD_MAX_OPCAP_BITS);
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic void idxd_wq_device_reset_cleanup(struct idxd_wq *wq)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	lockdep_assert_held(&wq->wq_lock);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	wq->size = 0;
38562306a36Sopenharmony_ci	wq->group = NULL;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic void idxd_wq_ref_release(struct percpu_ref *ref)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct idxd_wq *wq = container_of(ref, struct idxd_wq, wq_active);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	complete(&wq->wq_dead);
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ciint idxd_wq_init_percpu_ref(struct idxd_wq *wq)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	int rc;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	memset(&wq->wq_active, 0, sizeof(wq->wq_active));
40062306a36Sopenharmony_ci	rc = percpu_ref_init(&wq->wq_active, idxd_wq_ref_release,
40162306a36Sopenharmony_ci			     PERCPU_REF_ALLOW_REINIT, GFP_KERNEL);
40262306a36Sopenharmony_ci	if (rc < 0)
40362306a36Sopenharmony_ci		return rc;
40462306a36Sopenharmony_ci	reinit_completion(&wq->wq_dead);
40562306a36Sopenharmony_ci	reinit_completion(&wq->wq_resurrect);
40662306a36Sopenharmony_ci	return 0;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_civoid __idxd_wq_quiesce(struct idxd_wq *wq)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	lockdep_assert_held(&wq->wq_lock);
41262306a36Sopenharmony_ci	reinit_completion(&wq->wq_resurrect);
41362306a36Sopenharmony_ci	percpu_ref_kill(&wq->wq_active);
41462306a36Sopenharmony_ci	complete_all(&wq->wq_resurrect);
41562306a36Sopenharmony_ci	wait_for_completion(&wq->wq_dead);
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_civoid idxd_wq_quiesce(struct idxd_wq *wq)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	mutex_lock(&wq->wq_lock);
42162306a36Sopenharmony_ci	__idxd_wq_quiesce(wq);
42262306a36Sopenharmony_ci	mutex_unlock(&wq->wq_lock);
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci/* Device control bits */
42662306a36Sopenharmony_cistatic inline bool idxd_is_enabled(struct idxd_device *idxd)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	union gensts_reg gensts;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	gensts.bits = ioread32(idxd->reg_base + IDXD_GENSTATS_OFFSET);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (gensts.state == IDXD_DEVICE_STATE_ENABLED)
43362306a36Sopenharmony_ci		return true;
43462306a36Sopenharmony_ci	return false;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic inline bool idxd_device_is_halted(struct idxd_device *idxd)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	union gensts_reg gensts;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	gensts.bits = ioread32(idxd->reg_base + IDXD_GENSTATS_OFFSET);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	return (gensts.state == IDXD_DEVICE_STATE_HALT);
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci/*
44762306a36Sopenharmony_ci * This is function is only used for reset during probe and will
44862306a36Sopenharmony_ci * poll for completion. Once the device is setup with interrupts,
44962306a36Sopenharmony_ci * all commands will be done via interrupt completion.
45062306a36Sopenharmony_ci */
45162306a36Sopenharmony_ciint idxd_device_init_reset(struct idxd_device *idxd)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
45462306a36Sopenharmony_ci	union idxd_command_reg cmd;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if (idxd_device_is_halted(idxd)) {
45762306a36Sopenharmony_ci		dev_warn(&idxd->pdev->dev, "Device is HALTED!\n");
45862306a36Sopenharmony_ci		return -ENXIO;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
46262306a36Sopenharmony_ci	cmd.cmd = IDXD_CMD_RESET_DEVICE;
46362306a36Sopenharmony_ci	dev_dbg(dev, "%s: sending reset for init.\n", __func__);
46462306a36Sopenharmony_ci	spin_lock(&idxd->cmd_lock);
46562306a36Sopenharmony_ci	iowrite32(cmd.bits, idxd->reg_base + IDXD_CMD_OFFSET);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	while (ioread32(idxd->reg_base + IDXD_CMDSTS_OFFSET) &
46862306a36Sopenharmony_ci	       IDXD_CMDSTS_ACTIVE)
46962306a36Sopenharmony_ci		cpu_relax();
47062306a36Sopenharmony_ci	spin_unlock(&idxd->cmd_lock);
47162306a36Sopenharmony_ci	return 0;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic void idxd_cmd_exec(struct idxd_device *idxd, int cmd_code, u32 operand,
47562306a36Sopenharmony_ci			  u32 *status)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	union idxd_command_reg cmd;
47862306a36Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(done);
47962306a36Sopenharmony_ci	u32 stat;
48062306a36Sopenharmony_ci	unsigned long flags;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (idxd_device_is_halted(idxd)) {
48362306a36Sopenharmony_ci		dev_warn(&idxd->pdev->dev, "Device is HALTED!\n");
48462306a36Sopenharmony_ci		if (status)
48562306a36Sopenharmony_ci			*status = IDXD_CMDSTS_HW_ERR;
48662306a36Sopenharmony_ci		return;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
49062306a36Sopenharmony_ci	cmd.cmd = cmd_code;
49162306a36Sopenharmony_ci	cmd.operand = operand;
49262306a36Sopenharmony_ci	cmd.int_req = 1;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	spin_lock_irqsave(&idxd->cmd_lock, flags);
49562306a36Sopenharmony_ci	wait_event_lock_irq(idxd->cmd_waitq,
49662306a36Sopenharmony_ci			    !test_bit(IDXD_FLAG_CMD_RUNNING, &idxd->flags),
49762306a36Sopenharmony_ci			    idxd->cmd_lock);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	dev_dbg(&idxd->pdev->dev, "%s: sending cmd: %#x op: %#x\n",
50062306a36Sopenharmony_ci		__func__, cmd_code, operand);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	idxd->cmd_status = 0;
50362306a36Sopenharmony_ci	__set_bit(IDXD_FLAG_CMD_RUNNING, &idxd->flags);
50462306a36Sopenharmony_ci	idxd->cmd_done = &done;
50562306a36Sopenharmony_ci	iowrite32(cmd.bits, idxd->reg_base + IDXD_CMD_OFFSET);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	/*
50862306a36Sopenharmony_ci	 * After command submitted, release lock and go to sleep until
50962306a36Sopenharmony_ci	 * the command completes via interrupt.
51062306a36Sopenharmony_ci	 */
51162306a36Sopenharmony_ci	spin_unlock_irqrestore(&idxd->cmd_lock, flags);
51262306a36Sopenharmony_ci	wait_for_completion(&done);
51362306a36Sopenharmony_ci	stat = ioread32(idxd->reg_base + IDXD_CMDSTS_OFFSET);
51462306a36Sopenharmony_ci	spin_lock(&idxd->cmd_lock);
51562306a36Sopenharmony_ci	if (status)
51662306a36Sopenharmony_ci		*status = stat;
51762306a36Sopenharmony_ci	idxd->cmd_status = stat & GENMASK(7, 0);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	__clear_bit(IDXD_FLAG_CMD_RUNNING, &idxd->flags);
52062306a36Sopenharmony_ci	/* Wake up other pending commands */
52162306a36Sopenharmony_ci	wake_up(&idxd->cmd_waitq);
52262306a36Sopenharmony_ci	spin_unlock(&idxd->cmd_lock);
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ciint idxd_device_enable(struct idxd_device *idxd)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
52862306a36Sopenharmony_ci	u32 status;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	if (idxd_is_enabled(idxd)) {
53162306a36Sopenharmony_ci		dev_dbg(dev, "Device already enabled\n");
53262306a36Sopenharmony_ci		return -ENXIO;
53362306a36Sopenharmony_ci	}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	idxd_cmd_exec(idxd, IDXD_CMD_ENABLE_DEVICE, 0, &status);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	/* If the command is successful or if the device was enabled */
53862306a36Sopenharmony_ci	if (status != IDXD_CMDSTS_SUCCESS &&
53962306a36Sopenharmony_ci	    status != IDXD_CMDSTS_ERR_DEV_ENABLED) {
54062306a36Sopenharmony_ci		dev_dbg(dev, "%s: err_code: %#x\n", __func__, status);
54162306a36Sopenharmony_ci		return -ENXIO;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	idxd->state = IDXD_DEV_ENABLED;
54562306a36Sopenharmony_ci	return 0;
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ciint idxd_device_disable(struct idxd_device *idxd)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
55162306a36Sopenharmony_ci	u32 status;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if (!idxd_is_enabled(idxd)) {
55462306a36Sopenharmony_ci		dev_dbg(dev, "Device is not enabled\n");
55562306a36Sopenharmony_ci		return 0;
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	idxd_cmd_exec(idxd, IDXD_CMD_DISABLE_DEVICE, 0, &status);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	/* If the command is successful or if the device was disabled */
56162306a36Sopenharmony_ci	if (status != IDXD_CMDSTS_SUCCESS &&
56262306a36Sopenharmony_ci	    !(status & IDXD_CMDSTS_ERR_DIS_DEV_EN)) {
56362306a36Sopenharmony_ci		dev_dbg(dev, "%s: err_code: %#x\n", __func__, status);
56462306a36Sopenharmony_ci		return -ENXIO;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	idxd_device_clear_state(idxd);
56862306a36Sopenharmony_ci	return 0;
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_civoid idxd_device_reset(struct idxd_device *idxd)
57262306a36Sopenharmony_ci{
57362306a36Sopenharmony_ci	idxd_cmd_exec(idxd, IDXD_CMD_RESET_DEVICE, 0, NULL);
57462306a36Sopenharmony_ci	idxd_device_clear_state(idxd);
57562306a36Sopenharmony_ci	spin_lock(&idxd->dev_lock);
57662306a36Sopenharmony_ci	idxd_unmask_error_interrupts(idxd);
57762306a36Sopenharmony_ci	spin_unlock(&idxd->dev_lock);
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_civoid idxd_device_drain_pasid(struct idxd_device *idxd, int pasid)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
58362306a36Sopenharmony_ci	u32 operand;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	operand = pasid;
58662306a36Sopenharmony_ci	dev_dbg(dev, "cmd: %u operand: %#x\n", IDXD_CMD_DRAIN_PASID, operand);
58762306a36Sopenharmony_ci	idxd_cmd_exec(idxd, IDXD_CMD_DRAIN_PASID, operand, NULL);
58862306a36Sopenharmony_ci	dev_dbg(dev, "pasid %d drained\n", pasid);
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ciint idxd_device_request_int_handle(struct idxd_device *idxd, int idx, int *handle,
59262306a36Sopenharmony_ci				   enum idxd_interrupt_type irq_type)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
59562306a36Sopenharmony_ci	u32 operand, status;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (!(idxd->hw.cmd_cap & BIT(IDXD_CMD_REQUEST_INT_HANDLE)))
59862306a36Sopenharmony_ci		return -EOPNOTSUPP;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	dev_dbg(dev, "get int handle, idx %d\n", idx);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	operand = idx & GENMASK(15, 0);
60362306a36Sopenharmony_ci	if (irq_type == IDXD_IRQ_IMS)
60462306a36Sopenharmony_ci		operand |= CMD_INT_HANDLE_IMS;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	dev_dbg(dev, "cmd: %u operand: %#x\n", IDXD_CMD_REQUEST_INT_HANDLE, operand);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	idxd_cmd_exec(idxd, IDXD_CMD_REQUEST_INT_HANDLE, operand, &status);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	if ((status & IDXD_CMDSTS_ERR_MASK) != IDXD_CMDSTS_SUCCESS) {
61162306a36Sopenharmony_ci		dev_dbg(dev, "request int handle failed: %#x\n", status);
61262306a36Sopenharmony_ci		return -ENXIO;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	*handle = (status >> IDXD_CMDSTS_RES_SHIFT) & GENMASK(15, 0);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	dev_dbg(dev, "int handle acquired: %u\n", *handle);
61862306a36Sopenharmony_ci	return 0;
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ciint idxd_device_release_int_handle(struct idxd_device *idxd, int handle,
62262306a36Sopenharmony_ci				   enum idxd_interrupt_type irq_type)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
62562306a36Sopenharmony_ci	u32 operand, status;
62662306a36Sopenharmony_ci	union idxd_command_reg cmd;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if (!(idxd->hw.cmd_cap & BIT(IDXD_CMD_RELEASE_INT_HANDLE)))
62962306a36Sopenharmony_ci		return -EOPNOTSUPP;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	dev_dbg(dev, "release int handle, handle %d\n", handle);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
63462306a36Sopenharmony_ci	operand = handle & GENMASK(15, 0);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	if (irq_type == IDXD_IRQ_IMS)
63762306a36Sopenharmony_ci		operand |= CMD_INT_HANDLE_IMS;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	cmd.cmd = IDXD_CMD_RELEASE_INT_HANDLE;
64062306a36Sopenharmony_ci	cmd.operand = operand;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	dev_dbg(dev, "cmd: %u operand: %#x\n", IDXD_CMD_RELEASE_INT_HANDLE, operand);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	spin_lock(&idxd->cmd_lock);
64562306a36Sopenharmony_ci	iowrite32(cmd.bits, idxd->reg_base + IDXD_CMD_OFFSET);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	while (ioread32(idxd->reg_base + IDXD_CMDSTS_OFFSET) & IDXD_CMDSTS_ACTIVE)
64862306a36Sopenharmony_ci		cpu_relax();
64962306a36Sopenharmony_ci	status = ioread32(idxd->reg_base + IDXD_CMDSTS_OFFSET);
65062306a36Sopenharmony_ci	spin_unlock(&idxd->cmd_lock);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	if ((status & IDXD_CMDSTS_ERR_MASK) != IDXD_CMDSTS_SUCCESS) {
65362306a36Sopenharmony_ci		dev_dbg(dev, "release int handle failed: %#x\n", status);
65462306a36Sopenharmony_ci		return -ENXIO;
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	dev_dbg(dev, "int handle released.\n");
65862306a36Sopenharmony_ci	return 0;
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci/* Device configuration bits */
66262306a36Sopenharmony_cistatic void idxd_engines_clear_state(struct idxd_device *idxd)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	struct idxd_engine *engine;
66562306a36Sopenharmony_ci	int i;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	lockdep_assert_held(&idxd->dev_lock);
66862306a36Sopenharmony_ci	for (i = 0; i < idxd->max_engines; i++) {
66962306a36Sopenharmony_ci		engine = idxd->engines[i];
67062306a36Sopenharmony_ci		engine->group = NULL;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic void idxd_groups_clear_state(struct idxd_device *idxd)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	struct idxd_group *group;
67762306a36Sopenharmony_ci	int i;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	lockdep_assert_held(&idxd->dev_lock);
68062306a36Sopenharmony_ci	for (i = 0; i < idxd->max_groups; i++) {
68162306a36Sopenharmony_ci		group = idxd->groups[i];
68262306a36Sopenharmony_ci		memset(&group->grpcfg, 0, sizeof(group->grpcfg));
68362306a36Sopenharmony_ci		group->num_engines = 0;
68462306a36Sopenharmony_ci		group->num_wqs = 0;
68562306a36Sopenharmony_ci		group->use_rdbuf_limit = false;
68662306a36Sopenharmony_ci		/*
68762306a36Sopenharmony_ci		 * The default value is the same as the value of
68862306a36Sopenharmony_ci		 * total read buffers in GRPCAP.
68962306a36Sopenharmony_ci		 */
69062306a36Sopenharmony_ci		group->rdbufs_allowed = idxd->max_rdbufs;
69162306a36Sopenharmony_ci		group->rdbufs_reserved = 0;
69262306a36Sopenharmony_ci		if (idxd->hw.version <= DEVICE_VERSION_2 && !tc_override) {
69362306a36Sopenharmony_ci			group->tc_a = 1;
69462306a36Sopenharmony_ci			group->tc_b = 1;
69562306a36Sopenharmony_ci		} else {
69662306a36Sopenharmony_ci			group->tc_a = -1;
69762306a36Sopenharmony_ci			group->tc_b = -1;
69862306a36Sopenharmony_ci		}
69962306a36Sopenharmony_ci		group->desc_progress_limit = 0;
70062306a36Sopenharmony_ci		group->batch_progress_limit = 0;
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic void idxd_device_wqs_clear_state(struct idxd_device *idxd)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	int i;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	for (i = 0; i < idxd->max_wqs; i++) {
70962306a36Sopenharmony_ci		struct idxd_wq *wq = idxd->wqs[i];
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci		mutex_lock(&wq->wq_lock);
71262306a36Sopenharmony_ci		idxd_wq_disable_cleanup(wq);
71362306a36Sopenharmony_ci		idxd_wq_device_reset_cleanup(wq);
71462306a36Sopenharmony_ci		mutex_unlock(&wq->wq_lock);
71562306a36Sopenharmony_ci	}
71662306a36Sopenharmony_ci}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_civoid idxd_device_clear_state(struct idxd_device *idxd)
71962306a36Sopenharmony_ci{
72062306a36Sopenharmony_ci	/* IDXD is always disabled. Other states are cleared only when IDXD is configurable. */
72162306a36Sopenharmony_ci	if (test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags)) {
72262306a36Sopenharmony_ci		/*
72362306a36Sopenharmony_ci		 * Clearing wq state is protected by wq lock.
72462306a36Sopenharmony_ci		 * So no need to be protected by device lock.
72562306a36Sopenharmony_ci		 */
72662306a36Sopenharmony_ci		idxd_device_wqs_clear_state(idxd);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci		spin_lock(&idxd->dev_lock);
72962306a36Sopenharmony_ci		idxd_groups_clear_state(idxd);
73062306a36Sopenharmony_ci		idxd_engines_clear_state(idxd);
73162306a36Sopenharmony_ci	} else {
73262306a36Sopenharmony_ci		spin_lock(&idxd->dev_lock);
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	idxd->state = IDXD_DEV_DISABLED;
73662306a36Sopenharmony_ci	spin_unlock(&idxd->dev_lock);
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_cistatic int idxd_device_evl_setup(struct idxd_device *idxd)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	union gencfg_reg gencfg;
74262306a36Sopenharmony_ci	union evlcfg_reg evlcfg;
74362306a36Sopenharmony_ci	union genctrl_reg genctrl;
74462306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
74562306a36Sopenharmony_ci	void *addr;
74662306a36Sopenharmony_ci	dma_addr_t dma_addr;
74762306a36Sopenharmony_ci	int size;
74862306a36Sopenharmony_ci	struct idxd_evl *evl = idxd->evl;
74962306a36Sopenharmony_ci	unsigned long *bmap;
75062306a36Sopenharmony_ci	int rc;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	if (!evl)
75362306a36Sopenharmony_ci		return 0;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	size = evl_size(idxd);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	bmap = bitmap_zalloc(size, GFP_KERNEL);
75862306a36Sopenharmony_ci	if (!bmap) {
75962306a36Sopenharmony_ci		rc = -ENOMEM;
76062306a36Sopenharmony_ci		goto err_bmap;
76162306a36Sopenharmony_ci	}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	/*
76462306a36Sopenharmony_ci	 * Address needs to be page aligned. However, dma_alloc_coherent() provides
76562306a36Sopenharmony_ci	 * at minimal page size aligned address. No manual alignment required.
76662306a36Sopenharmony_ci	 */
76762306a36Sopenharmony_ci	addr = dma_alloc_coherent(dev, size, &dma_addr, GFP_KERNEL);
76862306a36Sopenharmony_ci	if (!addr) {
76962306a36Sopenharmony_ci		rc = -ENOMEM;
77062306a36Sopenharmony_ci		goto err_alloc;
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	spin_lock(&evl->lock);
77462306a36Sopenharmony_ci	evl->log = addr;
77562306a36Sopenharmony_ci	evl->dma = dma_addr;
77662306a36Sopenharmony_ci	evl->log_size = size;
77762306a36Sopenharmony_ci	evl->bmap = bmap;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	memset(&evlcfg, 0, sizeof(evlcfg));
78062306a36Sopenharmony_ci	evlcfg.bits[0] = dma_addr & GENMASK(63, 12);
78162306a36Sopenharmony_ci	evlcfg.size = evl->size;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	iowrite64(evlcfg.bits[0], idxd->reg_base + IDXD_EVLCFG_OFFSET);
78462306a36Sopenharmony_ci	iowrite64(evlcfg.bits[1], idxd->reg_base + IDXD_EVLCFG_OFFSET + 8);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	genctrl.bits = ioread32(idxd->reg_base + IDXD_GENCTRL_OFFSET);
78762306a36Sopenharmony_ci	genctrl.evl_int_en = 1;
78862306a36Sopenharmony_ci	iowrite32(genctrl.bits, idxd->reg_base + IDXD_GENCTRL_OFFSET);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	gencfg.bits = ioread32(idxd->reg_base + IDXD_GENCFG_OFFSET);
79162306a36Sopenharmony_ci	gencfg.evl_en = 1;
79262306a36Sopenharmony_ci	iowrite32(gencfg.bits, idxd->reg_base + IDXD_GENCFG_OFFSET);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	spin_unlock(&evl->lock);
79562306a36Sopenharmony_ci	return 0;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_cierr_alloc:
79862306a36Sopenharmony_ci	bitmap_free(bmap);
79962306a36Sopenharmony_cierr_bmap:
80062306a36Sopenharmony_ci	return rc;
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic void idxd_device_evl_free(struct idxd_device *idxd)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	void *evl_log;
80662306a36Sopenharmony_ci	unsigned int evl_log_size;
80762306a36Sopenharmony_ci	dma_addr_t evl_dma;
80862306a36Sopenharmony_ci	union gencfg_reg gencfg;
80962306a36Sopenharmony_ci	union genctrl_reg genctrl;
81062306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
81162306a36Sopenharmony_ci	struct idxd_evl *evl = idxd->evl;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	gencfg.bits = ioread32(idxd->reg_base + IDXD_GENCFG_OFFSET);
81462306a36Sopenharmony_ci	if (!gencfg.evl_en)
81562306a36Sopenharmony_ci		return;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	spin_lock(&evl->lock);
81862306a36Sopenharmony_ci	gencfg.evl_en = 0;
81962306a36Sopenharmony_ci	iowrite32(gencfg.bits, idxd->reg_base + IDXD_GENCFG_OFFSET);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	genctrl.bits = ioread32(idxd->reg_base + IDXD_GENCTRL_OFFSET);
82262306a36Sopenharmony_ci	genctrl.evl_int_en = 0;
82362306a36Sopenharmony_ci	iowrite32(genctrl.bits, idxd->reg_base + IDXD_GENCTRL_OFFSET);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	iowrite64(0, idxd->reg_base + IDXD_EVLCFG_OFFSET);
82662306a36Sopenharmony_ci	iowrite64(0, idxd->reg_base + IDXD_EVLCFG_OFFSET + 8);
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	bitmap_free(evl->bmap);
82962306a36Sopenharmony_ci	evl_log = evl->log;
83062306a36Sopenharmony_ci	evl_log_size = evl->log_size;
83162306a36Sopenharmony_ci	evl_dma = evl->dma;
83262306a36Sopenharmony_ci	evl->log = NULL;
83362306a36Sopenharmony_ci	evl->size = IDXD_EVL_SIZE_MIN;
83462306a36Sopenharmony_ci	spin_unlock(&evl->lock);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	dma_free_coherent(dev, evl_log_size, evl_log, evl_dma);
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_cistatic void idxd_group_config_write(struct idxd_group *group)
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	struct idxd_device *idxd = group->idxd;
84262306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
84362306a36Sopenharmony_ci	int i;
84462306a36Sopenharmony_ci	u32 grpcfg_offset;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	dev_dbg(dev, "Writing group %d cfg registers\n", group->id);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	/* setup GRPWQCFG */
84962306a36Sopenharmony_ci	for (i = 0; i < GRPWQCFG_STRIDES; i++) {
85062306a36Sopenharmony_ci		grpcfg_offset = GRPWQCFG_OFFSET(idxd, group->id, i);
85162306a36Sopenharmony_ci		iowrite64(group->grpcfg.wqs[i], idxd->reg_base + grpcfg_offset);
85262306a36Sopenharmony_ci		dev_dbg(dev, "GRPCFG wq[%d:%d: %#x]: %#llx\n",
85362306a36Sopenharmony_ci			group->id, i, grpcfg_offset,
85462306a36Sopenharmony_ci			ioread64(idxd->reg_base + grpcfg_offset));
85562306a36Sopenharmony_ci	}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	/* setup GRPENGCFG */
85862306a36Sopenharmony_ci	grpcfg_offset = GRPENGCFG_OFFSET(idxd, group->id);
85962306a36Sopenharmony_ci	iowrite64(group->grpcfg.engines, idxd->reg_base + grpcfg_offset);
86062306a36Sopenharmony_ci	dev_dbg(dev, "GRPCFG engs[%d: %#x]: %#llx\n", group->id,
86162306a36Sopenharmony_ci		grpcfg_offset, ioread64(idxd->reg_base + grpcfg_offset));
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	/* setup GRPFLAGS */
86462306a36Sopenharmony_ci	grpcfg_offset = GRPFLGCFG_OFFSET(idxd, group->id);
86562306a36Sopenharmony_ci	iowrite64(group->grpcfg.flags.bits, idxd->reg_base + grpcfg_offset);
86662306a36Sopenharmony_ci	dev_dbg(dev, "GRPFLAGS flags[%d: %#x]: %#llx\n",
86762306a36Sopenharmony_ci		group->id, grpcfg_offset,
86862306a36Sopenharmony_ci		ioread64(idxd->reg_base + grpcfg_offset));
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cistatic int idxd_groups_config_write(struct idxd_device *idxd)
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci	union gencfg_reg reg;
87562306a36Sopenharmony_ci	int i;
87662306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	/* Setup bandwidth rdbuf limit */
87962306a36Sopenharmony_ci	if (idxd->hw.gen_cap.config_en && idxd->rdbuf_limit) {
88062306a36Sopenharmony_ci		reg.bits = ioread32(idxd->reg_base + IDXD_GENCFG_OFFSET);
88162306a36Sopenharmony_ci		reg.rdbuf_limit = idxd->rdbuf_limit;
88262306a36Sopenharmony_ci		iowrite32(reg.bits, idxd->reg_base + IDXD_GENCFG_OFFSET);
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	dev_dbg(dev, "GENCFG(%#x): %#x\n", IDXD_GENCFG_OFFSET,
88662306a36Sopenharmony_ci		ioread32(idxd->reg_base + IDXD_GENCFG_OFFSET));
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	for (i = 0; i < idxd->max_groups; i++) {
88962306a36Sopenharmony_ci		struct idxd_group *group = idxd->groups[i];
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci		idxd_group_config_write(group);
89262306a36Sopenharmony_ci	}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	return 0;
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_cistatic bool idxd_device_pasid_priv_enabled(struct idxd_device *idxd)
89862306a36Sopenharmony_ci{
89962306a36Sopenharmony_ci	struct pci_dev *pdev = idxd->pdev;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	if (pdev->pasid_enabled && (pdev->pasid_features & PCI_PASID_CAP_PRIV))
90262306a36Sopenharmony_ci		return true;
90362306a36Sopenharmony_ci	return false;
90462306a36Sopenharmony_ci}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_cistatic int idxd_wq_config_write(struct idxd_wq *wq)
90762306a36Sopenharmony_ci{
90862306a36Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
90962306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
91062306a36Sopenharmony_ci	u32 wq_offset;
91162306a36Sopenharmony_ci	int i, n;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	if (!wq->group)
91462306a36Sopenharmony_ci		return 0;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	/*
91762306a36Sopenharmony_ci	 * Instead of memset the entire shadow copy of WQCFG, copy from the hardware after
91862306a36Sopenharmony_ci	 * wq reset. This will copy back the sticky values that are present on some devices.
91962306a36Sopenharmony_ci	 */
92062306a36Sopenharmony_ci	for (i = 0; i < WQCFG_STRIDES(idxd); i++) {
92162306a36Sopenharmony_ci		wq_offset = WQCFG_OFFSET(idxd, wq->id, i);
92262306a36Sopenharmony_ci		wq->wqcfg->bits[i] |= ioread32(idxd->reg_base + wq_offset);
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	if (wq->size == 0 && wq->type != IDXD_WQT_NONE)
92662306a36Sopenharmony_ci		wq->size = WQ_DEFAULT_QUEUE_DEPTH;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	/* byte 0-3 */
92962306a36Sopenharmony_ci	wq->wqcfg->wq_size = wq->size;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	/* bytes 4-7 */
93262306a36Sopenharmony_ci	wq->wqcfg->wq_thresh = wq->threshold;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	/* byte 8-11 */
93562306a36Sopenharmony_ci	if (wq_dedicated(wq))
93662306a36Sopenharmony_ci		wq->wqcfg->mode = 1;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	/*
93962306a36Sopenharmony_ci	 * The WQ priv bit is set depending on the WQ type. priv = 1 if the
94062306a36Sopenharmony_ci	 * WQ type is kernel to indicate privileged access. This setting only
94162306a36Sopenharmony_ci	 * matters for dedicated WQ. According to the DSA spec:
94262306a36Sopenharmony_ci	 * If the WQ is in dedicated mode, WQ PASID Enable is 1, and the
94362306a36Sopenharmony_ci	 * Privileged Mode Enable field of the PCI Express PASID capability
94462306a36Sopenharmony_ci	 * is 0, this field must be 0.
94562306a36Sopenharmony_ci	 *
94662306a36Sopenharmony_ci	 * In the case of a dedicated kernel WQ that is not able to support
94762306a36Sopenharmony_ci	 * the PASID cap, then the configuration will be rejected.
94862306a36Sopenharmony_ci	 */
94962306a36Sopenharmony_ci	if (wq_dedicated(wq) && wq->wqcfg->pasid_en &&
95062306a36Sopenharmony_ci	    !idxd_device_pasid_priv_enabled(idxd) &&
95162306a36Sopenharmony_ci	    wq->type == IDXD_WQT_KERNEL) {
95262306a36Sopenharmony_ci		idxd->cmd_status = IDXD_SCMD_WQ_NO_PRIV;
95362306a36Sopenharmony_ci		return -EOPNOTSUPP;
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	wq->wqcfg->priority = wq->priority;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (idxd->hw.gen_cap.block_on_fault &&
95962306a36Sopenharmony_ci	    test_bit(WQ_FLAG_BLOCK_ON_FAULT, &wq->flags) &&
96062306a36Sopenharmony_ci	    !test_bit(WQ_FLAG_PRS_DISABLE, &wq->flags))
96162306a36Sopenharmony_ci		wq->wqcfg->bof = 1;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	if (idxd->hw.wq_cap.wq_ats_support)
96462306a36Sopenharmony_ci		wq->wqcfg->wq_ats_disable = test_bit(WQ_FLAG_ATS_DISABLE, &wq->flags);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	if (idxd->hw.wq_cap.wq_prs_support)
96762306a36Sopenharmony_ci		wq->wqcfg->wq_prs_disable = test_bit(WQ_FLAG_PRS_DISABLE, &wq->flags);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	/* bytes 12-15 */
97062306a36Sopenharmony_ci	wq->wqcfg->max_xfer_shift = ilog2(wq->max_xfer_bytes);
97162306a36Sopenharmony_ci	idxd_wqcfg_set_max_batch_shift(idxd->data->type, wq->wqcfg, ilog2(wq->max_batch_size));
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	/* bytes 32-63 */
97462306a36Sopenharmony_ci	if (idxd->hw.wq_cap.op_config && wq->opcap_bmap) {
97562306a36Sopenharmony_ci		memset(wq->wqcfg->op_config, 0, IDXD_MAX_OPCAP_BITS / 8);
97662306a36Sopenharmony_ci		for_each_set_bit(n, wq->opcap_bmap, IDXD_MAX_OPCAP_BITS) {
97762306a36Sopenharmony_ci			int pos = n % BITS_PER_LONG_LONG;
97862306a36Sopenharmony_ci			int idx = n / BITS_PER_LONG_LONG;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci			wq->wqcfg->op_config[idx] |= BIT(pos);
98162306a36Sopenharmony_ci		}
98262306a36Sopenharmony_ci	}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	dev_dbg(dev, "WQ %d CFGs\n", wq->id);
98562306a36Sopenharmony_ci	for (i = 0; i < WQCFG_STRIDES(idxd); i++) {
98662306a36Sopenharmony_ci		wq_offset = WQCFG_OFFSET(idxd, wq->id, i);
98762306a36Sopenharmony_ci		iowrite32(wq->wqcfg->bits[i], idxd->reg_base + wq_offset);
98862306a36Sopenharmony_ci		dev_dbg(dev, "WQ[%d][%d][%#x]: %#x\n",
98962306a36Sopenharmony_ci			wq->id, i, wq_offset,
99062306a36Sopenharmony_ci			ioread32(idxd->reg_base + wq_offset));
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	return 0;
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_cistatic int idxd_wqs_config_write(struct idxd_device *idxd)
99762306a36Sopenharmony_ci{
99862306a36Sopenharmony_ci	int i, rc;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	for (i = 0; i < idxd->max_wqs; i++) {
100162306a36Sopenharmony_ci		struct idxd_wq *wq = idxd->wqs[i];
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci		rc = idxd_wq_config_write(wq);
100462306a36Sopenharmony_ci		if (rc < 0)
100562306a36Sopenharmony_ci			return rc;
100662306a36Sopenharmony_ci	}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	return 0;
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_cistatic void idxd_group_flags_setup(struct idxd_device *idxd)
101262306a36Sopenharmony_ci{
101362306a36Sopenharmony_ci	int i;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	/* TC-A 0 and TC-B 1 should be defaults */
101662306a36Sopenharmony_ci	for (i = 0; i < idxd->max_groups; i++) {
101762306a36Sopenharmony_ci		struct idxd_group *group = idxd->groups[i];
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci		if (group->tc_a == -1)
102062306a36Sopenharmony_ci			group->tc_a = group->grpcfg.flags.tc_a = 0;
102162306a36Sopenharmony_ci		else
102262306a36Sopenharmony_ci			group->grpcfg.flags.tc_a = group->tc_a;
102362306a36Sopenharmony_ci		if (group->tc_b == -1)
102462306a36Sopenharmony_ci			group->tc_b = group->grpcfg.flags.tc_b = 1;
102562306a36Sopenharmony_ci		else
102662306a36Sopenharmony_ci			group->grpcfg.flags.tc_b = group->tc_b;
102762306a36Sopenharmony_ci		group->grpcfg.flags.use_rdbuf_limit = group->use_rdbuf_limit;
102862306a36Sopenharmony_ci		group->grpcfg.flags.rdbufs_reserved = group->rdbufs_reserved;
102962306a36Sopenharmony_ci		group->grpcfg.flags.rdbufs_allowed = group->rdbufs_allowed;
103062306a36Sopenharmony_ci		group->grpcfg.flags.desc_progress_limit = group->desc_progress_limit;
103162306a36Sopenharmony_ci		group->grpcfg.flags.batch_progress_limit = group->batch_progress_limit;
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci}
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_cistatic int idxd_engines_setup(struct idxd_device *idxd)
103662306a36Sopenharmony_ci{
103762306a36Sopenharmony_ci	int i, engines = 0;
103862306a36Sopenharmony_ci	struct idxd_engine *eng;
103962306a36Sopenharmony_ci	struct idxd_group *group;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	for (i = 0; i < idxd->max_groups; i++) {
104262306a36Sopenharmony_ci		group = idxd->groups[i];
104362306a36Sopenharmony_ci		group->grpcfg.engines = 0;
104462306a36Sopenharmony_ci	}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	for (i = 0; i < idxd->max_engines; i++) {
104762306a36Sopenharmony_ci		eng = idxd->engines[i];
104862306a36Sopenharmony_ci		group = eng->group;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci		if (!group)
105162306a36Sopenharmony_ci			continue;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci		group->grpcfg.engines |= BIT(eng->id);
105462306a36Sopenharmony_ci		engines++;
105562306a36Sopenharmony_ci	}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	if (!engines)
105862306a36Sopenharmony_ci		return -EINVAL;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	return 0;
106162306a36Sopenharmony_ci}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_cistatic int idxd_wqs_setup(struct idxd_device *idxd)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	struct idxd_wq *wq;
106662306a36Sopenharmony_ci	struct idxd_group *group;
106762306a36Sopenharmony_ci	int i, j, configured = 0;
106862306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	for (i = 0; i < idxd->max_groups; i++) {
107162306a36Sopenharmony_ci		group = idxd->groups[i];
107262306a36Sopenharmony_ci		for (j = 0; j < 4; j++)
107362306a36Sopenharmony_ci			group->grpcfg.wqs[j] = 0;
107462306a36Sopenharmony_ci	}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	for (i = 0; i < idxd->max_wqs; i++) {
107762306a36Sopenharmony_ci		wq = idxd->wqs[i];
107862306a36Sopenharmony_ci		group = wq->group;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci		if (!wq->group)
108162306a36Sopenharmony_ci			continue;
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci		if (wq_shared(wq) && !wq_shared_supported(wq)) {
108462306a36Sopenharmony_ci			idxd->cmd_status = IDXD_SCMD_WQ_NO_SWQ_SUPPORT;
108562306a36Sopenharmony_ci			dev_warn(dev, "No shared wq support but configured.\n");
108662306a36Sopenharmony_ci			return -EINVAL;
108762306a36Sopenharmony_ci		}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci		group->grpcfg.wqs[wq->id / 64] |= BIT(wq->id % 64);
109062306a36Sopenharmony_ci		configured++;
109162306a36Sopenharmony_ci	}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	if (configured == 0) {
109462306a36Sopenharmony_ci		idxd->cmd_status = IDXD_SCMD_WQ_NONE_CONFIGURED;
109562306a36Sopenharmony_ci		return -EINVAL;
109662306a36Sopenharmony_ci	}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	return 0;
109962306a36Sopenharmony_ci}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ciint idxd_device_config(struct idxd_device *idxd)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	int rc;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	lockdep_assert_held(&idxd->dev_lock);
110662306a36Sopenharmony_ci	rc = idxd_wqs_setup(idxd);
110762306a36Sopenharmony_ci	if (rc < 0)
110862306a36Sopenharmony_ci		return rc;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	rc = idxd_engines_setup(idxd);
111162306a36Sopenharmony_ci	if (rc < 0)
111262306a36Sopenharmony_ci		return rc;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	idxd_group_flags_setup(idxd);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	rc = idxd_wqs_config_write(idxd);
111762306a36Sopenharmony_ci	if (rc < 0)
111862306a36Sopenharmony_ci		return rc;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	rc = idxd_groups_config_write(idxd);
112162306a36Sopenharmony_ci	if (rc < 0)
112262306a36Sopenharmony_ci		return rc;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	return 0;
112562306a36Sopenharmony_ci}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_cistatic int idxd_wq_load_config(struct idxd_wq *wq)
112862306a36Sopenharmony_ci{
112962306a36Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
113062306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
113162306a36Sopenharmony_ci	int wqcfg_offset;
113262306a36Sopenharmony_ci	int i;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	wqcfg_offset = WQCFG_OFFSET(idxd, wq->id, 0);
113562306a36Sopenharmony_ci	memcpy_fromio(wq->wqcfg, idxd->reg_base + wqcfg_offset, idxd->wqcfg_size);
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	wq->size = wq->wqcfg->wq_size;
113862306a36Sopenharmony_ci	wq->threshold = wq->wqcfg->wq_thresh;
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	/* The driver does not support shared WQ mode in read-only config yet */
114162306a36Sopenharmony_ci	if (wq->wqcfg->mode == 0 || wq->wqcfg->pasid_en)
114262306a36Sopenharmony_ci		return -EOPNOTSUPP;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	set_bit(WQ_FLAG_DEDICATED, &wq->flags);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	wq->priority = wq->wqcfg->priority;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	wq->max_xfer_bytes = 1ULL << wq->wqcfg->max_xfer_shift;
114962306a36Sopenharmony_ci	idxd_wq_set_max_batch_size(idxd->data->type, wq, 1U << wq->wqcfg->max_batch_shift);
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	for (i = 0; i < WQCFG_STRIDES(idxd); i++) {
115262306a36Sopenharmony_ci		wqcfg_offset = WQCFG_OFFSET(idxd, wq->id, i);
115362306a36Sopenharmony_ci		dev_dbg(dev, "WQ[%d][%d][%#x]: %#x\n", wq->id, i, wqcfg_offset, wq->wqcfg->bits[i]);
115462306a36Sopenharmony_ci	}
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	return 0;
115762306a36Sopenharmony_ci}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_cistatic void idxd_group_load_config(struct idxd_group *group)
116062306a36Sopenharmony_ci{
116162306a36Sopenharmony_ci	struct idxd_device *idxd = group->idxd;
116262306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
116362306a36Sopenharmony_ci	int i, j, grpcfg_offset;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	/*
116662306a36Sopenharmony_ci	 * Load WQS bit fields
116762306a36Sopenharmony_ci	 * Iterate through all 256 bits 64 bits at a time
116862306a36Sopenharmony_ci	 */
116962306a36Sopenharmony_ci	for (i = 0; i < GRPWQCFG_STRIDES; i++) {
117062306a36Sopenharmony_ci		struct idxd_wq *wq;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci		grpcfg_offset = GRPWQCFG_OFFSET(idxd, group->id, i);
117362306a36Sopenharmony_ci		group->grpcfg.wqs[i] = ioread64(idxd->reg_base + grpcfg_offset);
117462306a36Sopenharmony_ci		dev_dbg(dev, "GRPCFG wq[%d:%d: %#x]: %#llx\n",
117562306a36Sopenharmony_ci			group->id, i, grpcfg_offset, group->grpcfg.wqs[i]);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci		if (i * 64 >= idxd->max_wqs)
117862306a36Sopenharmony_ci			break;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci		/* Iterate through all 64 bits and check for wq set */
118162306a36Sopenharmony_ci		for (j = 0; j < 64; j++) {
118262306a36Sopenharmony_ci			int id = i * 64 + j;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci			/* No need to check beyond max wqs */
118562306a36Sopenharmony_ci			if (id >= idxd->max_wqs)
118662306a36Sopenharmony_ci				break;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci			/* Set group assignment for wq if wq bit is set */
118962306a36Sopenharmony_ci			if (group->grpcfg.wqs[i] & BIT(j)) {
119062306a36Sopenharmony_ci				wq = idxd->wqs[id];
119162306a36Sopenharmony_ci				wq->group = group;
119262306a36Sopenharmony_ci			}
119362306a36Sopenharmony_ci		}
119462306a36Sopenharmony_ci	}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	grpcfg_offset = GRPENGCFG_OFFSET(idxd, group->id);
119762306a36Sopenharmony_ci	group->grpcfg.engines = ioread64(idxd->reg_base + grpcfg_offset);
119862306a36Sopenharmony_ci	dev_dbg(dev, "GRPCFG engs[%d: %#x]: %#llx\n", group->id,
119962306a36Sopenharmony_ci		grpcfg_offset, group->grpcfg.engines);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	/* Iterate through all 64 bits to check engines set */
120262306a36Sopenharmony_ci	for (i = 0; i < 64; i++) {
120362306a36Sopenharmony_ci		if (i >= idxd->max_engines)
120462306a36Sopenharmony_ci			break;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci		if (group->grpcfg.engines & BIT(i)) {
120762306a36Sopenharmony_ci			struct idxd_engine *engine = idxd->engines[i];
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci			engine->group = group;
121062306a36Sopenharmony_ci		}
121162306a36Sopenharmony_ci	}
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	grpcfg_offset = GRPFLGCFG_OFFSET(idxd, group->id);
121462306a36Sopenharmony_ci	group->grpcfg.flags.bits = ioread64(idxd->reg_base + grpcfg_offset);
121562306a36Sopenharmony_ci	dev_dbg(dev, "GRPFLAGS flags[%d: %#x]: %#llx\n",
121662306a36Sopenharmony_ci		group->id, grpcfg_offset, group->grpcfg.flags.bits);
121762306a36Sopenharmony_ci}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ciint idxd_device_load_config(struct idxd_device *idxd)
122062306a36Sopenharmony_ci{
122162306a36Sopenharmony_ci	union gencfg_reg reg;
122262306a36Sopenharmony_ci	int i, rc;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	reg.bits = ioread32(idxd->reg_base + IDXD_GENCFG_OFFSET);
122562306a36Sopenharmony_ci	idxd->rdbuf_limit = reg.rdbuf_limit;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	for (i = 0; i < idxd->max_groups; i++) {
122862306a36Sopenharmony_ci		struct idxd_group *group = idxd->groups[i];
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci		idxd_group_load_config(group);
123162306a36Sopenharmony_ci	}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	for (i = 0; i < idxd->max_wqs; i++) {
123462306a36Sopenharmony_ci		struct idxd_wq *wq = idxd->wqs[i];
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci		rc = idxd_wq_load_config(wq);
123762306a36Sopenharmony_ci		if (rc < 0)
123862306a36Sopenharmony_ci			return rc;
123962306a36Sopenharmony_ci	}
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	return 0;
124262306a36Sopenharmony_ci}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_cistatic void idxd_flush_pending_descs(struct idxd_irq_entry *ie)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	struct idxd_desc *desc, *itr;
124762306a36Sopenharmony_ci	struct llist_node *head;
124862306a36Sopenharmony_ci	LIST_HEAD(flist);
124962306a36Sopenharmony_ci	enum idxd_complete_type ctype;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	spin_lock(&ie->list_lock);
125262306a36Sopenharmony_ci	head = llist_del_all(&ie->pending_llist);
125362306a36Sopenharmony_ci	if (head) {
125462306a36Sopenharmony_ci		llist_for_each_entry_safe(desc, itr, head, llnode)
125562306a36Sopenharmony_ci			list_add_tail(&desc->list, &ie->work_list);
125662306a36Sopenharmony_ci	}
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	list_for_each_entry_safe(desc, itr, &ie->work_list, list)
125962306a36Sopenharmony_ci		list_move_tail(&desc->list, &flist);
126062306a36Sopenharmony_ci	spin_unlock(&ie->list_lock);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	list_for_each_entry_safe(desc, itr, &flist, list) {
126362306a36Sopenharmony_ci		struct dma_async_tx_descriptor *tx;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci		list_del(&desc->list);
126662306a36Sopenharmony_ci		ctype = desc->completion->status ? IDXD_COMPLETE_NORMAL : IDXD_COMPLETE_ABORT;
126762306a36Sopenharmony_ci		/*
126862306a36Sopenharmony_ci		 * wq is being disabled. Any remaining descriptors are
126962306a36Sopenharmony_ci		 * likely to be stuck and can be dropped. callback could
127062306a36Sopenharmony_ci		 * point to code that is no longer accessible, for example
127162306a36Sopenharmony_ci		 * if dmatest module has been unloaded.
127262306a36Sopenharmony_ci		 */
127362306a36Sopenharmony_ci		tx = &desc->txd;
127462306a36Sopenharmony_ci		tx->callback = NULL;
127562306a36Sopenharmony_ci		tx->callback_result = NULL;
127662306a36Sopenharmony_ci		idxd_dma_complete_txd(desc, ctype, true);
127762306a36Sopenharmony_ci	}
127862306a36Sopenharmony_ci}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_cistatic void idxd_device_set_perm_entry(struct idxd_device *idxd,
128162306a36Sopenharmony_ci				       struct idxd_irq_entry *ie)
128262306a36Sopenharmony_ci{
128362306a36Sopenharmony_ci	union msix_perm mperm;
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	if (ie->pasid == IOMMU_PASID_INVALID)
128662306a36Sopenharmony_ci		return;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	mperm.bits = 0;
128962306a36Sopenharmony_ci	mperm.pasid = ie->pasid;
129062306a36Sopenharmony_ci	mperm.pasid_en = 1;
129162306a36Sopenharmony_ci	iowrite32(mperm.bits, idxd->reg_base + idxd->msix_perm_offset + ie->id * 8);
129262306a36Sopenharmony_ci}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_cistatic void idxd_device_clear_perm_entry(struct idxd_device *idxd,
129562306a36Sopenharmony_ci					 struct idxd_irq_entry *ie)
129662306a36Sopenharmony_ci{
129762306a36Sopenharmony_ci	iowrite32(0, idxd->reg_base + idxd->msix_perm_offset + ie->id * 8);
129862306a36Sopenharmony_ci}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_civoid idxd_wq_free_irq(struct idxd_wq *wq)
130162306a36Sopenharmony_ci{
130262306a36Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
130362306a36Sopenharmony_ci	struct idxd_irq_entry *ie = &wq->ie;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	if (wq->type != IDXD_WQT_KERNEL)
130662306a36Sopenharmony_ci		return;
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	free_irq(ie->vector, ie);
130962306a36Sopenharmony_ci	idxd_flush_pending_descs(ie);
131062306a36Sopenharmony_ci	if (idxd->request_int_handles)
131162306a36Sopenharmony_ci		idxd_device_release_int_handle(idxd, ie->int_handle, IDXD_IRQ_MSIX);
131262306a36Sopenharmony_ci	idxd_device_clear_perm_entry(idxd, ie);
131362306a36Sopenharmony_ci	ie->vector = -1;
131462306a36Sopenharmony_ci	ie->int_handle = INVALID_INT_HANDLE;
131562306a36Sopenharmony_ci	ie->pasid = IOMMU_PASID_INVALID;
131662306a36Sopenharmony_ci}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ciint idxd_wq_request_irq(struct idxd_wq *wq)
131962306a36Sopenharmony_ci{
132062306a36Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
132162306a36Sopenharmony_ci	struct pci_dev *pdev = idxd->pdev;
132262306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
132362306a36Sopenharmony_ci	struct idxd_irq_entry *ie;
132462306a36Sopenharmony_ci	int rc;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	if (wq->type != IDXD_WQT_KERNEL)
132762306a36Sopenharmony_ci		return 0;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	ie = &wq->ie;
133062306a36Sopenharmony_ci	ie->vector = pci_irq_vector(pdev, ie->id);
133162306a36Sopenharmony_ci	ie->pasid = device_pasid_enabled(idxd) ? idxd->pasid : IOMMU_PASID_INVALID;
133262306a36Sopenharmony_ci	idxd_device_set_perm_entry(idxd, ie);
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	rc = request_threaded_irq(ie->vector, NULL, idxd_wq_thread, 0, "idxd-portal", ie);
133562306a36Sopenharmony_ci	if (rc < 0) {
133662306a36Sopenharmony_ci		dev_err(dev, "Failed to request irq %d.\n", ie->vector);
133762306a36Sopenharmony_ci		goto err_irq;
133862306a36Sopenharmony_ci	}
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	if (idxd->request_int_handles) {
134162306a36Sopenharmony_ci		rc = idxd_device_request_int_handle(idxd, ie->id, &ie->int_handle,
134262306a36Sopenharmony_ci						    IDXD_IRQ_MSIX);
134362306a36Sopenharmony_ci		if (rc < 0)
134462306a36Sopenharmony_ci			goto err_int_handle;
134562306a36Sopenharmony_ci	} else {
134662306a36Sopenharmony_ci		ie->int_handle = ie->id;
134762306a36Sopenharmony_ci	}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	return 0;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_cierr_int_handle:
135262306a36Sopenharmony_ci	ie->int_handle = INVALID_INT_HANDLE;
135362306a36Sopenharmony_ci	free_irq(ie->vector, ie);
135462306a36Sopenharmony_cierr_irq:
135562306a36Sopenharmony_ci	idxd_device_clear_perm_entry(idxd, ie);
135662306a36Sopenharmony_ci	ie->pasid = IOMMU_PASID_INVALID;
135762306a36Sopenharmony_ci	return rc;
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ciint drv_enable_wq(struct idxd_wq *wq)
136162306a36Sopenharmony_ci{
136262306a36Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
136362306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
136462306a36Sopenharmony_ci	int rc = -ENXIO;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	lockdep_assert_held(&wq->wq_lock);
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	if (idxd->state != IDXD_DEV_ENABLED) {
136962306a36Sopenharmony_ci		idxd->cmd_status = IDXD_SCMD_DEV_NOT_ENABLED;
137062306a36Sopenharmony_ci		goto err;
137162306a36Sopenharmony_ci	}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	if (wq->state != IDXD_WQ_DISABLED) {
137462306a36Sopenharmony_ci		dev_dbg(dev, "wq %d already enabled.\n", wq->id);
137562306a36Sopenharmony_ci		idxd->cmd_status = IDXD_SCMD_WQ_ENABLED;
137662306a36Sopenharmony_ci		rc = -EBUSY;
137762306a36Sopenharmony_ci		goto err;
137862306a36Sopenharmony_ci	}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	if (!wq->group) {
138162306a36Sopenharmony_ci		dev_dbg(dev, "wq %d not attached to group.\n", wq->id);
138262306a36Sopenharmony_ci		idxd->cmd_status = IDXD_SCMD_WQ_NO_GRP;
138362306a36Sopenharmony_ci		goto err;
138462306a36Sopenharmony_ci	}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	if (strlen(wq->name) == 0) {
138762306a36Sopenharmony_ci		idxd->cmd_status = IDXD_SCMD_WQ_NO_NAME;
138862306a36Sopenharmony_ci		dev_dbg(dev, "wq %d name not set.\n", wq->id);
138962306a36Sopenharmony_ci		goto err;
139062306a36Sopenharmony_ci	}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	/* Shared WQ checks */
139362306a36Sopenharmony_ci	if (wq_shared(wq)) {
139462306a36Sopenharmony_ci		if (!wq_shared_supported(wq)) {
139562306a36Sopenharmony_ci			idxd->cmd_status = IDXD_SCMD_WQ_NO_SVM;
139662306a36Sopenharmony_ci			dev_dbg(dev, "PASID not enabled and shared wq.\n");
139762306a36Sopenharmony_ci			goto err;
139862306a36Sopenharmony_ci		}
139962306a36Sopenharmony_ci		/*
140062306a36Sopenharmony_ci		 * Shared wq with the threshold set to 0 means the user
140162306a36Sopenharmony_ci		 * did not set the threshold or transitioned from a
140262306a36Sopenharmony_ci		 * dedicated wq but did not set threshold. A value
140362306a36Sopenharmony_ci		 * of 0 would effectively disable the shared wq. The
140462306a36Sopenharmony_ci		 * driver does not allow a value of 0 to be set for
140562306a36Sopenharmony_ci		 * threshold via sysfs.
140662306a36Sopenharmony_ci		 */
140762306a36Sopenharmony_ci		if (wq->threshold == 0) {
140862306a36Sopenharmony_ci			idxd->cmd_status = IDXD_SCMD_WQ_NO_THRESH;
140962306a36Sopenharmony_ci			dev_dbg(dev, "Shared wq and threshold 0.\n");
141062306a36Sopenharmony_ci			goto err;
141162306a36Sopenharmony_ci		}
141262306a36Sopenharmony_ci	}
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	/*
141562306a36Sopenharmony_ci	 * In the event that the WQ is configurable for pasid, the driver
141662306a36Sopenharmony_ci	 * should setup the pasid, pasid_en bit. This is true for both kernel
141762306a36Sopenharmony_ci	 * and user shared workqueues. There is no need to setup priv bit in
141862306a36Sopenharmony_ci	 * that in-kernel DMA will also do user privileged requests.
141962306a36Sopenharmony_ci	 * A dedicated wq that is not 'kernel' type will configure pasid and
142062306a36Sopenharmony_ci	 * pasid_en later on so there is no need to setup.
142162306a36Sopenharmony_ci	 */
142262306a36Sopenharmony_ci	if (test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags)) {
142362306a36Sopenharmony_ci		if (wq_pasid_enabled(wq)) {
142462306a36Sopenharmony_ci			if (is_idxd_wq_kernel(wq) || wq_shared(wq)) {
142562306a36Sopenharmony_ci				u32 pasid = wq_dedicated(wq) ? idxd->pasid : 0;
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci				__idxd_wq_set_pasid_locked(wq, pasid);
142862306a36Sopenharmony_ci			}
142962306a36Sopenharmony_ci		}
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	rc = 0;
143362306a36Sopenharmony_ci	spin_lock(&idxd->dev_lock);
143462306a36Sopenharmony_ci	if (test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags))
143562306a36Sopenharmony_ci		rc = idxd_device_config(idxd);
143662306a36Sopenharmony_ci	spin_unlock(&idxd->dev_lock);
143762306a36Sopenharmony_ci	if (rc < 0) {
143862306a36Sopenharmony_ci		dev_dbg(dev, "Writing wq %d config failed: %d\n", wq->id, rc);
143962306a36Sopenharmony_ci		goto err;
144062306a36Sopenharmony_ci	}
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	rc = idxd_wq_enable(wq);
144362306a36Sopenharmony_ci	if (rc < 0) {
144462306a36Sopenharmony_ci		dev_dbg(dev, "wq %d enabling failed: %d\n", wq->id, rc);
144562306a36Sopenharmony_ci		goto err;
144662306a36Sopenharmony_ci	}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	rc = idxd_wq_map_portal(wq);
144962306a36Sopenharmony_ci	if (rc < 0) {
145062306a36Sopenharmony_ci		idxd->cmd_status = IDXD_SCMD_WQ_PORTAL_ERR;
145162306a36Sopenharmony_ci		dev_dbg(dev, "wq %d portal mapping failed: %d\n", wq->id, rc);
145262306a36Sopenharmony_ci		goto err_map_portal;
145362306a36Sopenharmony_ci	}
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	wq->client_count = 0;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	rc = idxd_wq_request_irq(wq);
145862306a36Sopenharmony_ci	if (rc < 0) {
145962306a36Sopenharmony_ci		idxd->cmd_status = IDXD_SCMD_WQ_IRQ_ERR;
146062306a36Sopenharmony_ci		dev_dbg(dev, "WQ %d irq setup failed: %d\n", wq->id, rc);
146162306a36Sopenharmony_ci		goto err_irq;
146262306a36Sopenharmony_ci	}
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	rc = idxd_wq_alloc_resources(wq);
146562306a36Sopenharmony_ci	if (rc < 0) {
146662306a36Sopenharmony_ci		idxd->cmd_status = IDXD_SCMD_WQ_RES_ALLOC_ERR;
146762306a36Sopenharmony_ci		dev_dbg(dev, "WQ resource alloc failed\n");
146862306a36Sopenharmony_ci		goto err_res_alloc;
146962306a36Sopenharmony_ci	}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	rc = idxd_wq_init_percpu_ref(wq);
147262306a36Sopenharmony_ci	if (rc < 0) {
147362306a36Sopenharmony_ci		idxd->cmd_status = IDXD_SCMD_PERCPU_ERR;
147462306a36Sopenharmony_ci		dev_dbg(dev, "percpu_ref setup failed\n");
147562306a36Sopenharmony_ci		goto err_ref;
147662306a36Sopenharmony_ci	}
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	return 0;
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_cierr_ref:
148162306a36Sopenharmony_ci	idxd_wq_free_resources(wq);
148262306a36Sopenharmony_cierr_res_alloc:
148362306a36Sopenharmony_ci	idxd_wq_free_irq(wq);
148462306a36Sopenharmony_cierr_irq:
148562306a36Sopenharmony_ci	idxd_wq_unmap_portal(wq);
148662306a36Sopenharmony_cierr_map_portal:
148762306a36Sopenharmony_ci	if (idxd_wq_disable(wq, false))
148862306a36Sopenharmony_ci		dev_dbg(dev, "wq %s disable failed\n", dev_name(wq_confdev(wq)));
148962306a36Sopenharmony_cierr:
149062306a36Sopenharmony_ci	return rc;
149162306a36Sopenharmony_ci}
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_civoid drv_disable_wq(struct idxd_wq *wq)
149462306a36Sopenharmony_ci{
149562306a36Sopenharmony_ci	struct idxd_device *idxd = wq->idxd;
149662306a36Sopenharmony_ci	struct device *dev = &idxd->pdev->dev;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	lockdep_assert_held(&wq->wq_lock);
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	if (idxd_wq_refcount(wq))
150162306a36Sopenharmony_ci		dev_warn(dev, "Clients has claim on wq %d: %d\n",
150262306a36Sopenharmony_ci			 wq->id, idxd_wq_refcount(wq));
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	idxd_wq_unmap_portal(wq);
150562306a36Sopenharmony_ci	idxd_wq_drain(wq);
150662306a36Sopenharmony_ci	idxd_wq_free_irq(wq);
150762306a36Sopenharmony_ci	idxd_wq_reset(wq);
150862306a36Sopenharmony_ci	idxd_wq_free_resources(wq);
150962306a36Sopenharmony_ci	percpu_ref_exit(&wq->wq_active);
151062306a36Sopenharmony_ci	wq->type = IDXD_WQT_NONE;
151162306a36Sopenharmony_ci	wq->client_count = 0;
151262306a36Sopenharmony_ci}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ciint idxd_device_drv_probe(struct idxd_dev *idxd_dev)
151562306a36Sopenharmony_ci{
151662306a36Sopenharmony_ci	struct idxd_device *idxd = idxd_dev_to_idxd(idxd_dev);
151762306a36Sopenharmony_ci	int rc = 0;
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	/*
152062306a36Sopenharmony_ci	 * Device should be in disabled state for the idxd_drv to load. If it's in
152162306a36Sopenharmony_ci	 * enabled state, then the device was altered outside of driver's control.
152262306a36Sopenharmony_ci	 * If the state is in halted state, then we don't want to proceed.
152362306a36Sopenharmony_ci	 */
152462306a36Sopenharmony_ci	if (idxd->state != IDXD_DEV_DISABLED) {
152562306a36Sopenharmony_ci		idxd->cmd_status = IDXD_SCMD_DEV_ENABLED;
152662306a36Sopenharmony_ci		return -ENXIO;
152762306a36Sopenharmony_ci	}
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	/* Device configuration */
153062306a36Sopenharmony_ci	spin_lock(&idxd->dev_lock);
153162306a36Sopenharmony_ci	if (test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags))
153262306a36Sopenharmony_ci		rc = idxd_device_config(idxd);
153362306a36Sopenharmony_ci	spin_unlock(&idxd->dev_lock);
153462306a36Sopenharmony_ci	if (rc < 0)
153562306a36Sopenharmony_ci		return -ENXIO;
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	/*
153862306a36Sopenharmony_ci	 * System PASID is preserved across device disable/enable cycle, but
153962306a36Sopenharmony_ci	 * genconfig register content gets cleared during device reset. We
154062306a36Sopenharmony_ci	 * need to re-enable user interrupts for kernel work queue completion
154162306a36Sopenharmony_ci	 * IRQ to function.
154262306a36Sopenharmony_ci	 */
154362306a36Sopenharmony_ci	if (idxd->pasid != IOMMU_PASID_INVALID)
154462306a36Sopenharmony_ci		idxd_set_user_intr(idxd, 1);
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	rc = idxd_device_evl_setup(idxd);
154762306a36Sopenharmony_ci	if (rc < 0) {
154862306a36Sopenharmony_ci		idxd->cmd_status = IDXD_SCMD_DEV_EVL_ERR;
154962306a36Sopenharmony_ci		return rc;
155062306a36Sopenharmony_ci	}
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	/* Start device */
155362306a36Sopenharmony_ci	rc = idxd_device_enable(idxd);
155462306a36Sopenharmony_ci	if (rc < 0) {
155562306a36Sopenharmony_ci		idxd_device_evl_free(idxd);
155662306a36Sopenharmony_ci		return rc;
155762306a36Sopenharmony_ci	}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	/* Setup DMA device without channels */
156062306a36Sopenharmony_ci	rc = idxd_register_dma_device(idxd);
156162306a36Sopenharmony_ci	if (rc < 0) {
156262306a36Sopenharmony_ci		idxd_device_disable(idxd);
156362306a36Sopenharmony_ci		idxd_device_evl_free(idxd);
156462306a36Sopenharmony_ci		idxd->cmd_status = IDXD_SCMD_DEV_DMA_ERR;
156562306a36Sopenharmony_ci		return rc;
156662306a36Sopenharmony_ci	}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	idxd->cmd_status = 0;
156962306a36Sopenharmony_ci	return 0;
157062306a36Sopenharmony_ci}
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_civoid idxd_device_drv_remove(struct idxd_dev *idxd_dev)
157362306a36Sopenharmony_ci{
157462306a36Sopenharmony_ci	struct device *dev = &idxd_dev->conf_dev;
157562306a36Sopenharmony_ci	struct idxd_device *idxd = idxd_dev_to_idxd(idxd_dev);
157662306a36Sopenharmony_ci	int i;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	for (i = 0; i < idxd->max_wqs; i++) {
157962306a36Sopenharmony_ci		struct idxd_wq *wq = idxd->wqs[i];
158062306a36Sopenharmony_ci		struct device *wq_dev = wq_confdev(wq);
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci		if (wq->state == IDXD_WQ_DISABLED)
158362306a36Sopenharmony_ci			continue;
158462306a36Sopenharmony_ci		dev_warn(dev, "Active wq %d on disable %s.\n", i, dev_name(wq_dev));
158562306a36Sopenharmony_ci		device_release_driver(wq_dev);
158662306a36Sopenharmony_ci	}
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	idxd_unregister_dma_device(idxd);
158962306a36Sopenharmony_ci	idxd_device_disable(idxd);
159062306a36Sopenharmony_ci	if (test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags))
159162306a36Sopenharmony_ci		idxd_device_reset(idxd);
159262306a36Sopenharmony_ci	idxd_device_evl_free(idxd);
159362306a36Sopenharmony_ci}
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_cistatic enum idxd_dev_type dev_types[] = {
159662306a36Sopenharmony_ci	IDXD_DEV_DSA,
159762306a36Sopenharmony_ci	IDXD_DEV_IAX,
159862306a36Sopenharmony_ci	IDXD_DEV_NONE,
159962306a36Sopenharmony_ci};
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_cistruct idxd_device_driver idxd_drv = {
160262306a36Sopenharmony_ci	.type = dev_types,
160362306a36Sopenharmony_ci	.probe = idxd_device_drv_probe,
160462306a36Sopenharmony_ci	.remove = idxd_device_drv_remove,
160562306a36Sopenharmony_ci	.name = "idxd",
160662306a36Sopenharmony_ci};
160762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(idxd_drv);
1608