162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright IBM Corp. 2012
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author(s):
662306a36Sopenharmony_ci *   Jan Glauber <jang@linux.vnet.ibm.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define KMSG_COMPONENT "zpci"
1062306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/compat.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/miscdevice.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/err.h>
1762306a36Sopenharmony_ci#include <linux/delay.h>
1862306a36Sopenharmony_ci#include <linux/pci.h>
1962306a36Sopenharmony_ci#include <linux/uaccess.h>
2062306a36Sopenharmony_ci#include <asm/asm-extable.h>
2162306a36Sopenharmony_ci#include <asm/pci_debug.h>
2262306a36Sopenharmony_ci#include <asm/pci_clp.h>
2362306a36Sopenharmony_ci#include <asm/clp.h>
2462306a36Sopenharmony_ci#include <uapi/asm/clp.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "pci_bus.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cibool zpci_unique_uid;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_civoid update_uid_checking(bool new)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	if (zpci_unique_uid != new)
3362306a36Sopenharmony_ci		zpci_dbg(3, "uid checking:%d\n", new);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	zpci_unique_uid = new;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic inline void zpci_err_clp(unsigned int rsp, int rc)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct {
4162306a36Sopenharmony_ci		unsigned int rsp;
4262306a36Sopenharmony_ci		int rc;
4362306a36Sopenharmony_ci	} __packed data = {rsp, rc};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	zpci_err_hex(&data, sizeof(data));
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * Call Logical Processor with c=1, lps=0 and command 1
5062306a36Sopenharmony_ci * to get the bit mask of installed logical processors
5162306a36Sopenharmony_ci */
5262306a36Sopenharmony_cistatic inline int clp_get_ilp(unsigned long *ilp)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	unsigned long mask;
5562306a36Sopenharmony_ci	int cc = 3;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	asm volatile (
5862306a36Sopenharmony_ci		"	.insn	rrf,0xb9a00000,%[mask],%[cmd],8,0\n"
5962306a36Sopenharmony_ci		"0:	ipm	%[cc]\n"
6062306a36Sopenharmony_ci		"	srl	%[cc],28\n"
6162306a36Sopenharmony_ci		"1:\n"
6262306a36Sopenharmony_ci		EX_TABLE(0b, 1b)
6362306a36Sopenharmony_ci		: [cc] "+d" (cc), [mask] "=d" (mask) : [cmd] "a" (1)
6462306a36Sopenharmony_ci		: "cc");
6562306a36Sopenharmony_ci	*ilp = mask;
6662306a36Sopenharmony_ci	return cc;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * Call Logical Processor with c=0, the give constant lps and an lpcb request.
7162306a36Sopenharmony_ci */
7262306a36Sopenharmony_cistatic __always_inline int clp_req(void *data, unsigned int lps)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct { u8 _[CLP_BLK_SIZE]; } *req = data;
7562306a36Sopenharmony_ci	u64 ignored;
7662306a36Sopenharmony_ci	int cc = 3;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	asm volatile (
7962306a36Sopenharmony_ci		"	.insn	rrf,0xb9a00000,%[ign],%[req],0,%[lps]\n"
8062306a36Sopenharmony_ci		"0:	ipm	%[cc]\n"
8162306a36Sopenharmony_ci		"	srl	%[cc],28\n"
8262306a36Sopenharmony_ci		"1:\n"
8362306a36Sopenharmony_ci		EX_TABLE(0b, 1b)
8462306a36Sopenharmony_ci		: [cc] "+d" (cc), [ign] "=d" (ignored), "+m" (*req)
8562306a36Sopenharmony_ci		: [req] "a" (req), [lps] "i" (lps)
8662306a36Sopenharmony_ci		: "cc");
8762306a36Sopenharmony_ci	return cc;
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic void *clp_alloc_block(gfp_t gfp_mask)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	return (void *) __get_free_pages(gfp_mask, get_order(CLP_BLK_SIZE));
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic void clp_free_block(void *ptr)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	free_pages((unsigned long) ptr, get_order(CLP_BLK_SIZE));
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic void clp_store_query_pci_fngrp(struct zpci_dev *zdev,
10162306a36Sopenharmony_ci				      struct clp_rsp_query_pci_grp *response)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	zdev->tlb_refresh = response->refresh;
10462306a36Sopenharmony_ci	zdev->dma_mask = response->dasm;
10562306a36Sopenharmony_ci	zdev->msi_addr = response->msia;
10662306a36Sopenharmony_ci	zdev->max_msi = response->noi;
10762306a36Sopenharmony_ci	zdev->fmb_update = response->mui;
10862306a36Sopenharmony_ci	zdev->version = response->version;
10962306a36Sopenharmony_ci	zdev->maxstbl = response->maxstbl;
11062306a36Sopenharmony_ci	zdev->dtsm = response->dtsm;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	switch (response->version) {
11362306a36Sopenharmony_ci	case 1:
11462306a36Sopenharmony_ci		zdev->max_bus_speed = PCIE_SPEED_5_0GT;
11562306a36Sopenharmony_ci		break;
11662306a36Sopenharmony_ci	default:
11762306a36Sopenharmony_ci		zdev->max_bus_speed = PCI_SPEED_UNKNOWN;
11862306a36Sopenharmony_ci		break;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic int clp_query_pci_fngrp(struct zpci_dev *zdev, u8 pfgid)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct clp_req_rsp_query_pci_grp *rrb;
12562306a36Sopenharmony_ci	int rc;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	rrb = clp_alloc_block(GFP_KERNEL);
12862306a36Sopenharmony_ci	if (!rrb)
12962306a36Sopenharmony_ci		return -ENOMEM;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	memset(rrb, 0, sizeof(*rrb));
13262306a36Sopenharmony_ci	rrb->request.hdr.len = sizeof(rrb->request);
13362306a36Sopenharmony_ci	rrb->request.hdr.cmd = CLP_QUERY_PCI_FNGRP;
13462306a36Sopenharmony_ci	rrb->response.hdr.len = sizeof(rrb->response);
13562306a36Sopenharmony_ci	rrb->request.pfgid = pfgid;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	rc = clp_req(rrb, CLP_LPS_PCI);
13862306a36Sopenharmony_ci	if (!rc && rrb->response.hdr.rsp == CLP_RC_OK)
13962306a36Sopenharmony_ci		clp_store_query_pci_fngrp(zdev, &rrb->response);
14062306a36Sopenharmony_ci	else {
14162306a36Sopenharmony_ci		zpci_err("Q PCI FGRP:\n");
14262306a36Sopenharmony_ci		zpci_err_clp(rrb->response.hdr.rsp, rc);
14362306a36Sopenharmony_ci		rc = -EIO;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci	clp_free_block(rrb);
14662306a36Sopenharmony_ci	return rc;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic int clp_store_query_pci_fn(struct zpci_dev *zdev,
15062306a36Sopenharmony_ci				  struct clp_rsp_query_pci *response)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	int i;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	for (i = 0; i < PCI_STD_NUM_BARS; i++) {
15562306a36Sopenharmony_ci		zdev->bars[i].val = le32_to_cpu(response->bar[i]);
15662306a36Sopenharmony_ci		zdev->bars[i].size = response->bar_size[i];
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci	zdev->start_dma = response->sdma;
15962306a36Sopenharmony_ci	zdev->end_dma = response->edma;
16062306a36Sopenharmony_ci	zdev->pchid = response->pchid;
16162306a36Sopenharmony_ci	zdev->pfgid = response->pfgid;
16262306a36Sopenharmony_ci	zdev->pft = response->pft;
16362306a36Sopenharmony_ci	zdev->vfn = response->vfn;
16462306a36Sopenharmony_ci	zdev->port = response->port;
16562306a36Sopenharmony_ci	zdev->uid = response->uid;
16662306a36Sopenharmony_ci	zdev->fmb_length = sizeof(u32) * response->fmb_len;
16762306a36Sopenharmony_ci	zdev->rid_available = response->rid_avail;
16862306a36Sopenharmony_ci	zdev->is_physfn = response->is_physfn;
16962306a36Sopenharmony_ci	if (!s390_pci_no_rid && zdev->rid_available)
17062306a36Sopenharmony_ci		zdev->devfn = response->rid & ZPCI_RID_MASK_DEVFN;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	memcpy(zdev->pfip, response->pfip, sizeof(zdev->pfip));
17362306a36Sopenharmony_ci	if (response->util_str_avail) {
17462306a36Sopenharmony_ci		memcpy(zdev->util_str, response->util_str,
17562306a36Sopenharmony_ci		       sizeof(zdev->util_str));
17662306a36Sopenharmony_ci		zdev->util_str_avail = 1;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci	zdev->mio_capable = response->mio_addr_avail;
17962306a36Sopenharmony_ci	for (i = 0; i < PCI_STD_NUM_BARS; i++) {
18062306a36Sopenharmony_ci		if (!(response->mio.valid & (1 << (PCI_STD_NUM_BARS - i - 1))))
18162306a36Sopenharmony_ci			continue;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		zdev->bars[i].mio_wb = (void __iomem *) response->mio.addr[i].wb;
18462306a36Sopenharmony_ci		zdev->bars[i].mio_wt = (void __iomem *) response->mio.addr[i].wt;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci	return 0;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ciint clp_query_pci_fn(struct zpci_dev *zdev)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct clp_req_rsp_query_pci *rrb;
19262306a36Sopenharmony_ci	int rc;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	rrb = clp_alloc_block(GFP_KERNEL);
19562306a36Sopenharmony_ci	if (!rrb)
19662306a36Sopenharmony_ci		return -ENOMEM;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	memset(rrb, 0, sizeof(*rrb));
19962306a36Sopenharmony_ci	rrb->request.hdr.len = sizeof(rrb->request);
20062306a36Sopenharmony_ci	rrb->request.hdr.cmd = CLP_QUERY_PCI_FN;
20162306a36Sopenharmony_ci	rrb->response.hdr.len = sizeof(rrb->response);
20262306a36Sopenharmony_ci	rrb->request.fh = zdev->fh;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	rc = clp_req(rrb, CLP_LPS_PCI);
20562306a36Sopenharmony_ci	if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) {
20662306a36Sopenharmony_ci		rc = clp_store_query_pci_fn(zdev, &rrb->response);
20762306a36Sopenharmony_ci		if (rc)
20862306a36Sopenharmony_ci			goto out;
20962306a36Sopenharmony_ci		rc = clp_query_pci_fngrp(zdev, rrb->response.pfgid);
21062306a36Sopenharmony_ci	} else {
21162306a36Sopenharmony_ci		zpci_err("Q PCI FN:\n");
21262306a36Sopenharmony_ci		zpci_err_clp(rrb->response.hdr.rsp, rc);
21362306a36Sopenharmony_ci		rc = -EIO;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ciout:
21662306a36Sopenharmony_ci	clp_free_block(rrb);
21762306a36Sopenharmony_ci	return rc;
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci/**
22162306a36Sopenharmony_ci * clp_set_pci_fn() - Execute a command on a PCI function
22262306a36Sopenharmony_ci * @zdev: Function that will be affected
22362306a36Sopenharmony_ci * @fh: Out parameter for updated function handle
22462306a36Sopenharmony_ci * @nr_dma_as: DMA address space number
22562306a36Sopenharmony_ci * @command: The command code to execute
22662306a36Sopenharmony_ci *
22762306a36Sopenharmony_ci * Returns: 0 on success, < 0 for Linux errors (e.g. -ENOMEM), and
22862306a36Sopenharmony_ci * > 0 for non-success platform responses
22962306a36Sopenharmony_ci */
23062306a36Sopenharmony_cistatic int clp_set_pci_fn(struct zpci_dev *zdev, u32 *fh, u8 nr_dma_as, u8 command)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct clp_req_rsp_set_pci *rrb;
23362306a36Sopenharmony_ci	int rc, retries = 100;
23462306a36Sopenharmony_ci	u32 gisa = 0;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	*fh = 0;
23762306a36Sopenharmony_ci	rrb = clp_alloc_block(GFP_KERNEL);
23862306a36Sopenharmony_ci	if (!rrb)
23962306a36Sopenharmony_ci		return -ENOMEM;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (command != CLP_SET_DISABLE_PCI_FN)
24262306a36Sopenharmony_ci		gisa = zdev->gisa;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	do {
24562306a36Sopenharmony_ci		memset(rrb, 0, sizeof(*rrb));
24662306a36Sopenharmony_ci		rrb->request.hdr.len = sizeof(rrb->request);
24762306a36Sopenharmony_ci		rrb->request.hdr.cmd = CLP_SET_PCI_FN;
24862306a36Sopenharmony_ci		rrb->response.hdr.len = sizeof(rrb->response);
24962306a36Sopenharmony_ci		rrb->request.fh = zdev->fh;
25062306a36Sopenharmony_ci		rrb->request.oc = command;
25162306a36Sopenharmony_ci		rrb->request.ndas = nr_dma_as;
25262306a36Sopenharmony_ci		rrb->request.gisa = gisa;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		rc = clp_req(rrb, CLP_LPS_PCI);
25562306a36Sopenharmony_ci		if (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY) {
25662306a36Sopenharmony_ci			retries--;
25762306a36Sopenharmony_ci			if (retries < 0)
25862306a36Sopenharmony_ci				break;
25962306a36Sopenharmony_ci			msleep(20);
26062306a36Sopenharmony_ci		}
26162306a36Sopenharmony_ci	} while (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) {
26462306a36Sopenharmony_ci		*fh = rrb->response.fh;
26562306a36Sopenharmony_ci	} else {
26662306a36Sopenharmony_ci		zpci_err("Set PCI FN:\n");
26762306a36Sopenharmony_ci		zpci_err_clp(rrb->response.hdr.rsp, rc);
26862306a36Sopenharmony_ci		if (!rc)
26962306a36Sopenharmony_ci			rc = rrb->response.hdr.rsp;
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci	clp_free_block(rrb);
27262306a36Sopenharmony_ci	return rc;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ciint clp_setup_writeback_mio(void)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct clp_req_rsp_slpc_pci *rrb;
27862306a36Sopenharmony_ci	u8  wb_bit_pos;
27962306a36Sopenharmony_ci	int rc;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	rrb = clp_alloc_block(GFP_KERNEL);
28262306a36Sopenharmony_ci	if (!rrb)
28362306a36Sopenharmony_ci		return -ENOMEM;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	memset(rrb, 0, sizeof(*rrb));
28662306a36Sopenharmony_ci	rrb->request.hdr.len = sizeof(rrb->request);
28762306a36Sopenharmony_ci	rrb->request.hdr.cmd = CLP_SLPC;
28862306a36Sopenharmony_ci	rrb->response.hdr.len = sizeof(rrb->response);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	rc = clp_req(rrb, CLP_LPS_PCI);
29162306a36Sopenharmony_ci	if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) {
29262306a36Sopenharmony_ci		if (rrb->response.vwb) {
29362306a36Sopenharmony_ci			wb_bit_pos = rrb->response.mio_wb;
29462306a36Sopenharmony_ci			set_bit_inv(wb_bit_pos, &mio_wb_bit_mask);
29562306a36Sopenharmony_ci			zpci_dbg(3, "wb bit: %d\n", wb_bit_pos);
29662306a36Sopenharmony_ci		} else {
29762306a36Sopenharmony_ci			zpci_dbg(3, "wb bit: n.a.\n");
29862306a36Sopenharmony_ci		}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	} else {
30162306a36Sopenharmony_ci		zpci_err("SLPC PCI:\n");
30262306a36Sopenharmony_ci		zpci_err_clp(rrb->response.hdr.rsp, rc);
30362306a36Sopenharmony_ci		rc = -EIO;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci	clp_free_block(rrb);
30662306a36Sopenharmony_ci	return rc;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ciint clp_enable_fh(struct zpci_dev *zdev, u32 *fh, u8 nr_dma_as)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	int rc;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	rc = clp_set_pci_fn(zdev, fh, nr_dma_as, CLP_SET_ENABLE_PCI_FN);
31462306a36Sopenharmony_ci	zpci_dbg(3, "ena fid:%x, fh:%x, rc:%d\n", zdev->fid, *fh, rc);
31562306a36Sopenharmony_ci	if (!rc && zpci_use_mio(zdev)) {
31662306a36Sopenharmony_ci		rc = clp_set_pci_fn(zdev, fh, nr_dma_as, CLP_SET_ENABLE_MIO);
31762306a36Sopenharmony_ci		zpci_dbg(3, "ena mio fid:%x, fh:%x, rc:%d\n",
31862306a36Sopenharmony_ci				zdev->fid, *fh, rc);
31962306a36Sopenharmony_ci		if (rc)
32062306a36Sopenharmony_ci			clp_disable_fh(zdev, fh);
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci	return rc;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ciint clp_disable_fh(struct zpci_dev *zdev, u32 *fh)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	int rc;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (!zdev_enabled(zdev))
33062306a36Sopenharmony_ci		return 0;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	rc = clp_set_pci_fn(zdev, fh, 0, CLP_SET_DISABLE_PCI_FN);
33362306a36Sopenharmony_ci	zpci_dbg(3, "dis fid:%x, fh:%x, rc:%d\n", zdev->fid, *fh, rc);
33462306a36Sopenharmony_ci	return rc;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic int clp_list_pci_req(struct clp_req_rsp_list_pci *rrb,
33862306a36Sopenharmony_ci			    u64 *resume_token, int *nentries)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	int rc;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	memset(rrb, 0, sizeof(*rrb));
34362306a36Sopenharmony_ci	rrb->request.hdr.len = sizeof(rrb->request);
34462306a36Sopenharmony_ci	rrb->request.hdr.cmd = CLP_LIST_PCI;
34562306a36Sopenharmony_ci	/* store as many entries as possible */
34662306a36Sopenharmony_ci	rrb->response.hdr.len = CLP_BLK_SIZE - LIST_PCI_HDR_LEN;
34762306a36Sopenharmony_ci	rrb->request.resume_token = *resume_token;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/* Get PCI function handle list */
35062306a36Sopenharmony_ci	rc = clp_req(rrb, CLP_LPS_PCI);
35162306a36Sopenharmony_ci	if (rc || rrb->response.hdr.rsp != CLP_RC_OK) {
35262306a36Sopenharmony_ci		zpci_err("List PCI FN:\n");
35362306a36Sopenharmony_ci		zpci_err_clp(rrb->response.hdr.rsp, rc);
35462306a36Sopenharmony_ci		return -EIO;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	update_uid_checking(rrb->response.uid_checking);
35862306a36Sopenharmony_ci	WARN_ON_ONCE(rrb->response.entry_size !=
35962306a36Sopenharmony_ci		sizeof(struct clp_fh_list_entry));
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	*nentries = (rrb->response.hdr.len - LIST_PCI_HDR_LEN) /
36262306a36Sopenharmony_ci		rrb->response.entry_size;
36362306a36Sopenharmony_ci	*resume_token = rrb->response.resume_token;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return rc;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic int clp_list_pci(struct clp_req_rsp_list_pci *rrb, void *data,
36962306a36Sopenharmony_ci			void (*cb)(struct clp_fh_list_entry *, void *))
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	u64 resume_token = 0;
37262306a36Sopenharmony_ci	int nentries, i, rc;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	do {
37562306a36Sopenharmony_ci		rc = clp_list_pci_req(rrb, &resume_token, &nentries);
37662306a36Sopenharmony_ci		if (rc)
37762306a36Sopenharmony_ci			return rc;
37862306a36Sopenharmony_ci		for (i = 0; i < nentries; i++)
37962306a36Sopenharmony_ci			cb(&rrb->response.fh_list[i], data);
38062306a36Sopenharmony_ci	} while (resume_token);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	return rc;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic int clp_find_pci(struct clp_req_rsp_list_pci *rrb, u32 fid,
38662306a36Sopenharmony_ci			struct clp_fh_list_entry *entry)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct clp_fh_list_entry *fh_list;
38962306a36Sopenharmony_ci	u64 resume_token = 0;
39062306a36Sopenharmony_ci	int nentries, i, rc;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	do {
39362306a36Sopenharmony_ci		rc = clp_list_pci_req(rrb, &resume_token, &nentries);
39462306a36Sopenharmony_ci		if (rc)
39562306a36Sopenharmony_ci			return rc;
39662306a36Sopenharmony_ci		fh_list = rrb->response.fh_list;
39762306a36Sopenharmony_ci		for (i = 0; i < nentries; i++) {
39862306a36Sopenharmony_ci			if (fh_list[i].fid == fid) {
39962306a36Sopenharmony_ci				*entry = fh_list[i];
40062306a36Sopenharmony_ci				return 0;
40162306a36Sopenharmony_ci			}
40262306a36Sopenharmony_ci		}
40362306a36Sopenharmony_ci	} while (resume_token);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	return -ENODEV;
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic void __clp_add(struct clp_fh_list_entry *entry, void *data)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	struct zpci_dev *zdev;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (!entry->vendor_id)
41362306a36Sopenharmony_ci		return;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	zdev = get_zdev_by_fid(entry->fid);
41662306a36Sopenharmony_ci	if (zdev) {
41762306a36Sopenharmony_ci		zpci_zdev_put(zdev);
41862306a36Sopenharmony_ci		return;
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci	zpci_create_device(entry->fid, entry->fh, entry->config_state);
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ciint clp_scan_pci_devices(void)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	struct clp_req_rsp_list_pci *rrb;
42662306a36Sopenharmony_ci	int rc;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	rrb = clp_alloc_block(GFP_KERNEL);
42962306a36Sopenharmony_ci	if (!rrb)
43062306a36Sopenharmony_ci		return -ENOMEM;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	rc = clp_list_pci(rrb, NULL, __clp_add);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	clp_free_block(rrb);
43562306a36Sopenharmony_ci	return rc;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci/*
43962306a36Sopenharmony_ci * Get the current function handle of the function matching @fid
44062306a36Sopenharmony_ci */
44162306a36Sopenharmony_ciint clp_refresh_fh(u32 fid, u32 *fh)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	struct clp_req_rsp_list_pci *rrb;
44462306a36Sopenharmony_ci	struct clp_fh_list_entry entry;
44562306a36Sopenharmony_ci	int rc;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	rrb = clp_alloc_block(GFP_NOWAIT);
44862306a36Sopenharmony_ci	if (!rrb)
44962306a36Sopenharmony_ci		return -ENOMEM;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	rc = clp_find_pci(rrb, fid, &entry);
45262306a36Sopenharmony_ci	if (!rc)
45362306a36Sopenharmony_ci		*fh = entry.fh;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	clp_free_block(rrb);
45662306a36Sopenharmony_ci	return rc;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ciint clp_get_state(u32 fid, enum zpci_state *state)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	struct clp_req_rsp_list_pci *rrb;
46262306a36Sopenharmony_ci	struct clp_fh_list_entry entry;
46362306a36Sopenharmony_ci	int rc;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	rrb = clp_alloc_block(GFP_ATOMIC);
46662306a36Sopenharmony_ci	if (!rrb)
46762306a36Sopenharmony_ci		return -ENOMEM;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	rc = clp_find_pci(rrb, fid, &entry);
47062306a36Sopenharmony_ci	if (!rc) {
47162306a36Sopenharmony_ci		*state = entry.config_state;
47262306a36Sopenharmony_ci	} else if (rc == -ENODEV) {
47362306a36Sopenharmony_ci		*state = ZPCI_FN_STATE_RESERVED;
47462306a36Sopenharmony_ci		rc = 0;
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	clp_free_block(rrb);
47862306a36Sopenharmony_ci	return rc;
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cistatic int clp_base_slpc(struct clp_req *req, struct clp_req_rsp_slpc *lpcb)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	unsigned long limit = PAGE_SIZE - sizeof(lpcb->request);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	if (lpcb->request.hdr.len != sizeof(lpcb->request) ||
48662306a36Sopenharmony_ci	    lpcb->response.hdr.len > limit)
48762306a36Sopenharmony_ci		return -EINVAL;
48862306a36Sopenharmony_ci	return clp_req(lpcb, CLP_LPS_BASE) ? -EOPNOTSUPP : 0;
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic int clp_base_command(struct clp_req *req, struct clp_req_hdr *lpcb)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	switch (lpcb->cmd) {
49462306a36Sopenharmony_ci	case 0x0001: /* store logical-processor characteristics */
49562306a36Sopenharmony_ci		return clp_base_slpc(req, (void *) lpcb);
49662306a36Sopenharmony_ci	default:
49762306a36Sopenharmony_ci		return -EINVAL;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistatic int clp_pci_slpc(struct clp_req *req, struct clp_req_rsp_slpc_pci *lpcb)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	unsigned long limit = PAGE_SIZE - sizeof(lpcb->request);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	if (lpcb->request.hdr.len != sizeof(lpcb->request) ||
50662306a36Sopenharmony_ci	    lpcb->response.hdr.len > limit)
50762306a36Sopenharmony_ci		return -EINVAL;
50862306a36Sopenharmony_ci	return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0;
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic int clp_pci_list(struct clp_req *req, struct clp_req_rsp_list_pci *lpcb)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	unsigned long limit = PAGE_SIZE - sizeof(lpcb->request);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (lpcb->request.hdr.len != sizeof(lpcb->request) ||
51662306a36Sopenharmony_ci	    lpcb->response.hdr.len > limit)
51762306a36Sopenharmony_ci		return -EINVAL;
51862306a36Sopenharmony_ci	if (lpcb->request.reserved2 != 0)
51962306a36Sopenharmony_ci		return -EINVAL;
52062306a36Sopenharmony_ci	return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0;
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic int clp_pci_query(struct clp_req *req,
52462306a36Sopenharmony_ci			 struct clp_req_rsp_query_pci *lpcb)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	unsigned long limit = PAGE_SIZE - sizeof(lpcb->request);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	if (lpcb->request.hdr.len != sizeof(lpcb->request) ||
52962306a36Sopenharmony_ci	    lpcb->response.hdr.len > limit)
53062306a36Sopenharmony_ci		return -EINVAL;
53162306a36Sopenharmony_ci	if (lpcb->request.reserved2 != 0 || lpcb->request.reserved3 != 0)
53262306a36Sopenharmony_ci		return -EINVAL;
53362306a36Sopenharmony_ci	return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0;
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic int clp_pci_query_grp(struct clp_req *req,
53762306a36Sopenharmony_ci			     struct clp_req_rsp_query_pci_grp *lpcb)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	unsigned long limit = PAGE_SIZE - sizeof(lpcb->request);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	if (lpcb->request.hdr.len != sizeof(lpcb->request) ||
54262306a36Sopenharmony_ci	    lpcb->response.hdr.len > limit)
54362306a36Sopenharmony_ci		return -EINVAL;
54462306a36Sopenharmony_ci	if (lpcb->request.reserved2 != 0 || lpcb->request.reserved3 != 0 ||
54562306a36Sopenharmony_ci	    lpcb->request.reserved4 != 0)
54662306a36Sopenharmony_ci		return -EINVAL;
54762306a36Sopenharmony_ci	return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic int clp_pci_command(struct clp_req *req, struct clp_req_hdr *lpcb)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	switch (lpcb->cmd) {
55362306a36Sopenharmony_ci	case 0x0001: /* store logical-processor characteristics */
55462306a36Sopenharmony_ci		return clp_pci_slpc(req, (void *) lpcb);
55562306a36Sopenharmony_ci	case 0x0002: /* list PCI functions */
55662306a36Sopenharmony_ci		return clp_pci_list(req, (void *) lpcb);
55762306a36Sopenharmony_ci	case 0x0003: /* query PCI function */
55862306a36Sopenharmony_ci		return clp_pci_query(req, (void *) lpcb);
55962306a36Sopenharmony_ci	case 0x0004: /* query PCI function group */
56062306a36Sopenharmony_ci		return clp_pci_query_grp(req, (void *) lpcb);
56162306a36Sopenharmony_ci	default:
56262306a36Sopenharmony_ci		return -EINVAL;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic int clp_normal_command(struct clp_req *req)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct clp_req_hdr *lpcb;
56962306a36Sopenharmony_ci	void __user *uptr;
57062306a36Sopenharmony_ci	int rc;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	rc = -EINVAL;
57362306a36Sopenharmony_ci	if (req->lps != 0 && req->lps != 2)
57462306a36Sopenharmony_ci		goto out;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	rc = -ENOMEM;
57762306a36Sopenharmony_ci	lpcb = clp_alloc_block(GFP_KERNEL);
57862306a36Sopenharmony_ci	if (!lpcb)
57962306a36Sopenharmony_ci		goto out;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	rc = -EFAULT;
58262306a36Sopenharmony_ci	uptr = (void __force __user *)(unsigned long) req->data_p;
58362306a36Sopenharmony_ci	if (copy_from_user(lpcb, uptr, PAGE_SIZE) != 0)
58462306a36Sopenharmony_ci		goto out_free;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	rc = -EINVAL;
58762306a36Sopenharmony_ci	if (lpcb->fmt != 0 || lpcb->reserved1 != 0 || lpcb->reserved2 != 0)
58862306a36Sopenharmony_ci		goto out_free;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	switch (req->lps) {
59162306a36Sopenharmony_ci	case 0:
59262306a36Sopenharmony_ci		rc = clp_base_command(req, lpcb);
59362306a36Sopenharmony_ci		break;
59462306a36Sopenharmony_ci	case 2:
59562306a36Sopenharmony_ci		rc = clp_pci_command(req, lpcb);
59662306a36Sopenharmony_ci		break;
59762306a36Sopenharmony_ci	}
59862306a36Sopenharmony_ci	if (rc)
59962306a36Sopenharmony_ci		goto out_free;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	rc = -EFAULT;
60262306a36Sopenharmony_ci	if (copy_to_user(uptr, lpcb, PAGE_SIZE) != 0)
60362306a36Sopenharmony_ci		goto out_free;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	rc = 0;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ciout_free:
60862306a36Sopenharmony_ci	clp_free_block(lpcb);
60962306a36Sopenharmony_ciout:
61062306a36Sopenharmony_ci	return rc;
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic int clp_immediate_command(struct clp_req *req)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	void __user *uptr;
61662306a36Sopenharmony_ci	unsigned long ilp;
61762306a36Sopenharmony_ci	int exists;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (req->cmd > 1 || clp_get_ilp(&ilp) != 0)
62062306a36Sopenharmony_ci		return -EINVAL;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	uptr = (void __force __user *)(unsigned long) req->data_p;
62362306a36Sopenharmony_ci	if (req->cmd == 0) {
62462306a36Sopenharmony_ci		/* Command code 0: test for a specific processor */
62562306a36Sopenharmony_ci		exists = test_bit_inv(req->lps, &ilp);
62662306a36Sopenharmony_ci		return put_user(exists, (int __user *) uptr);
62762306a36Sopenharmony_ci	}
62862306a36Sopenharmony_ci	/* Command code 1: return bit mask of installed processors */
62962306a36Sopenharmony_ci	return put_user(ilp, (unsigned long __user *) uptr);
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic long clp_misc_ioctl(struct file *filp, unsigned int cmd,
63362306a36Sopenharmony_ci			   unsigned long arg)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	struct clp_req req;
63662306a36Sopenharmony_ci	void __user *argp;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	if (cmd != CLP_SYNC)
63962306a36Sopenharmony_ci		return -EINVAL;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	argp = is_compat_task() ? compat_ptr(arg) : (void __user *) arg;
64262306a36Sopenharmony_ci	if (copy_from_user(&req, argp, sizeof(req)))
64362306a36Sopenharmony_ci		return -EFAULT;
64462306a36Sopenharmony_ci	if (req.r != 0)
64562306a36Sopenharmony_ci		return -EINVAL;
64662306a36Sopenharmony_ci	return req.c ? clp_immediate_command(&req) : clp_normal_command(&req);
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_cistatic int clp_misc_release(struct inode *inode, struct file *filp)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	return 0;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic const struct file_operations clp_misc_fops = {
65562306a36Sopenharmony_ci	.owner = THIS_MODULE,
65662306a36Sopenharmony_ci	.open = nonseekable_open,
65762306a36Sopenharmony_ci	.release = clp_misc_release,
65862306a36Sopenharmony_ci	.unlocked_ioctl = clp_misc_ioctl,
65962306a36Sopenharmony_ci	.compat_ioctl = clp_misc_ioctl,
66062306a36Sopenharmony_ci	.llseek = no_llseek,
66162306a36Sopenharmony_ci};
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_cistatic struct miscdevice clp_misc_device = {
66462306a36Sopenharmony_ci	.minor = MISC_DYNAMIC_MINOR,
66562306a36Sopenharmony_ci	.name = "clp",
66662306a36Sopenharmony_ci	.fops = &clp_misc_fops,
66762306a36Sopenharmony_ci};
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cibuiltin_misc_device(clp_misc_device);
670