1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2a755a45dSJan Glauber /*
3a755a45dSJan Glauber * Copyright IBM Corp. 2012
4a755a45dSJan Glauber *
5a755a45dSJan Glauber * Author(s):
6a755a45dSJan Glauber * Jan Glauber <jang@linux.vnet.ibm.com>
7a755a45dSJan Glauber */
8a755a45dSJan Glauber
9896cb7e6SGerald Schaefer #define KMSG_COMPONENT "zpci"
10896cb7e6SGerald Schaefer #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
11a755a45dSJan Glauber
12988b86e6SMartin Schwidefsky #include <linux/compat.h>
13a755a45dSJan Glauber #include <linux/kernel.h>
14988b86e6SMartin Schwidefsky #include <linux/miscdevice.h>
15a755a45dSJan Glauber #include <linux/slab.h>
16a755a45dSJan Glauber #include <linux/err.h>
17a755a45dSJan Glauber #include <linux/delay.h>
18a755a45dSJan Glauber #include <linux/pci.h>
19988b86e6SMartin Schwidefsky #include <linux/uaccess.h>
20d09a307fSHeiko Carstens #include <asm/asm-extable.h>
21a2ab8333SSebastian Ott #include <asm/pci_debug.h>
22a755a45dSJan Glauber #include <asm/pci_clp.h>
23988b86e6SMartin Schwidefsky #include <asm/clp.h>
24988b86e6SMartin Schwidefsky #include <uapi/asm/clp.h>
25a755a45dSJan Glauber
26c122383dSNiklas Schnelle #include "pci_bus.h"
27c122383dSNiklas Schnelle
285c5afd02SSebastian Ott bool zpci_unique_uid;
295c5afd02SSebastian Ott
update_uid_checking(bool new)307a11c67aSNiklas Schnelle void update_uid_checking(bool new)
315db23179SSebastian Ott {
325db23179SSebastian Ott if (zpci_unique_uid != new)
3352c79e63SNiklas Schnelle zpci_dbg(3, "uid checking:%d\n", new);
345db23179SSebastian Ott
355db23179SSebastian Ott zpci_unique_uid = new;
365db23179SSebastian Ott }
375db23179SSebastian Ott
zpci_err_clp(unsigned int rsp,int rc)381f1dcbd4SSebastian Ott static inline void zpci_err_clp(unsigned int rsp, int rc)
391f1dcbd4SSebastian Ott {
401f1dcbd4SSebastian Ott struct {
411f1dcbd4SSebastian Ott unsigned int rsp;
421f1dcbd4SSebastian Ott int rc;
431f1dcbd4SSebastian Ott } __packed data = {rsp, rc};
441f1dcbd4SSebastian Ott
451f1dcbd4SSebastian Ott zpci_err_hex(&data, sizeof(data));
461f1dcbd4SSebastian Ott }
471f1dcbd4SSebastian Ott
48a755a45dSJan Glauber /*
49988b86e6SMartin Schwidefsky * Call Logical Processor with c=1, lps=0 and command 1
50988b86e6SMartin Schwidefsky * to get the bit mask of installed logical processors
51a755a45dSJan Glauber */
clp_get_ilp(unsigned long * ilp)52988b86e6SMartin Schwidefsky static inline int clp_get_ilp(unsigned long *ilp)
53988b86e6SMartin Schwidefsky {
54988b86e6SMartin Schwidefsky unsigned long mask;
55988b86e6SMartin Schwidefsky int cc = 3;
56988b86e6SMartin Schwidefsky
57988b86e6SMartin Schwidefsky asm volatile (
58988b86e6SMartin Schwidefsky " .insn rrf,0xb9a00000,%[mask],%[cmd],8,0\n"
59988b86e6SMartin Schwidefsky "0: ipm %[cc]\n"
60988b86e6SMartin Schwidefsky " srl %[cc],28\n"
61988b86e6SMartin Schwidefsky "1:\n"
62988b86e6SMartin Schwidefsky EX_TABLE(0b, 1b)
63988b86e6SMartin Schwidefsky : [cc] "+d" (cc), [mask] "=d" (mask) : [cmd] "a" (1)
64988b86e6SMartin Schwidefsky : "cc");
65988b86e6SMartin Schwidefsky *ilp = mask;
66988b86e6SMartin Schwidefsky return cc;
67988b86e6SMartin Schwidefsky }
68988b86e6SMartin Schwidefsky
69988b86e6SMartin Schwidefsky /*
70988b86e6SMartin Schwidefsky * Call Logical Processor with c=0, the give constant lps and an lpcb request.
71988b86e6SMartin Schwidefsky */
clp_req(void * data,unsigned int lps)72771c24f6SHeiko Carstens static __always_inline int clp_req(void *data, unsigned int lps)
73a755a45dSJan Glauber {
74bf4ec24fSSebastian Ott struct { u8 _[CLP_BLK_SIZE]; } *req = data;
75bf4ec24fSSebastian Ott u64 ignored;
76988b86e6SMartin Schwidefsky int cc = 3;
77a755a45dSJan Glauber
78a755a45dSJan Glauber asm volatile (
79988b86e6SMartin Schwidefsky " .insn rrf,0xb9a00000,%[ign],%[req],0,%[lps]\n"
80988b86e6SMartin Schwidefsky "0: ipm %[cc]\n"
81a755a45dSJan Glauber " srl %[cc],28\n"
82988b86e6SMartin Schwidefsky "1:\n"
83988b86e6SMartin Schwidefsky EX_TABLE(0b, 1b)
84988b86e6SMartin Schwidefsky : [cc] "+d" (cc), [ign] "=d" (ignored), "+m" (*req)
85988b86e6SMartin Schwidefsky : [req] "a" (req), [lps] "i" (lps)
86bf4ec24fSSebastian Ott : "cc");
87a755a45dSJan Glauber return cc;
88a755a45dSJan Glauber }
89a755a45dSJan Glauber
clp_alloc_block(gfp_t gfp_mask)901d578966SSebastian Ott static void *clp_alloc_block(gfp_t gfp_mask)
91a755a45dSJan Glauber {
921d578966SSebastian Ott return (void *) __get_free_pages(gfp_mask, get_order(CLP_BLK_SIZE));
93a755a45dSJan Glauber }
94a755a45dSJan Glauber
clp_free_block(void * ptr)95a755a45dSJan Glauber static void clp_free_block(void *ptr)
96a755a45dSJan Glauber {
97a755a45dSJan Glauber free_pages((unsigned long) ptr, get_order(CLP_BLK_SIZE));
98a755a45dSJan Glauber }
99a755a45dSJan Glauber
clp_store_query_pci_fngrp(struct zpci_dev * zdev,struct clp_rsp_query_pci_grp * response)100a755a45dSJan Glauber static void clp_store_query_pci_fngrp(struct zpci_dev *zdev,
101a755a45dSJan Glauber struct clp_rsp_query_pci_grp *response)
102a755a45dSJan Glauber {
103828b35f6SJan Glauber zdev->tlb_refresh = response->refresh;
104828b35f6SJan Glauber zdev->dma_mask = response->dasm;
1059a4da8a5SJan Glauber zdev->msi_addr = response->msia;
106b19148f6SSebastian Ott zdev->max_msi = response->noi;
107d0b08853SJan Glauber zdev->fmb_update = response->mui;
108dc8c638dSMatthew Rosato zdev->version = response->version;
109d1038467SMatthew Rosato zdev->maxstbl = response->maxstbl;
110d1038467SMatthew Rosato zdev->dtsm = response->dtsm;
1119a4da8a5SJan Glauber
112a755a45dSJan Glauber switch (response->version) {
113a755a45dSJan Glauber case 1:
114a755a45dSJan Glauber zdev->max_bus_speed = PCIE_SPEED_5_0GT;
115a755a45dSJan Glauber break;
116a755a45dSJan Glauber default:
117a755a45dSJan Glauber zdev->max_bus_speed = PCI_SPEED_UNKNOWN;
118a755a45dSJan Glauber break;
119a755a45dSJan Glauber }
120a755a45dSJan Glauber }
121a755a45dSJan Glauber
clp_query_pci_fngrp(struct zpci_dev * zdev,u8 pfgid)122a755a45dSJan Glauber static int clp_query_pci_fngrp(struct zpci_dev *zdev, u8 pfgid)
123a755a45dSJan Glauber {
124a755a45dSJan Glauber struct clp_req_rsp_query_pci_grp *rrb;
125a755a45dSJan Glauber int rc;
126a755a45dSJan Glauber
1271d578966SSebastian Ott rrb = clp_alloc_block(GFP_KERNEL);
128a755a45dSJan Glauber if (!rrb)
129a755a45dSJan Glauber return -ENOMEM;
130a755a45dSJan Glauber
131a755a45dSJan Glauber memset(rrb, 0, sizeof(*rrb));
132a755a45dSJan Glauber rrb->request.hdr.len = sizeof(rrb->request);
133a755a45dSJan Glauber rrb->request.hdr.cmd = CLP_QUERY_PCI_FNGRP;
134a755a45dSJan Glauber rrb->response.hdr.len = sizeof(rrb->response);
135a755a45dSJan Glauber rrb->request.pfgid = pfgid;
136a755a45dSJan Glauber
137988b86e6SMartin Schwidefsky rc = clp_req(rrb, CLP_LPS_PCI);
138a755a45dSJan Glauber if (!rc && rrb->response.hdr.rsp == CLP_RC_OK)
139a755a45dSJan Glauber clp_store_query_pci_fngrp(zdev, &rrb->response);
140a755a45dSJan Glauber else {
1411f1dcbd4SSebastian Ott zpci_err("Q PCI FGRP:\n");
1421f1dcbd4SSebastian Ott zpci_err_clp(rrb->response.hdr.rsp, rc);
143a755a45dSJan Glauber rc = -EIO;
144a755a45dSJan Glauber }
145a755a45dSJan Glauber clp_free_block(rrb);
146a755a45dSJan Glauber return rc;
147a755a45dSJan Glauber }
148a755a45dSJan Glauber
clp_store_query_pci_fn(struct zpci_dev * zdev,struct clp_rsp_query_pci * response)149a755a45dSJan Glauber static int clp_store_query_pci_fn(struct zpci_dev *zdev,
150a755a45dSJan Glauber struct clp_rsp_query_pci *response)
151a755a45dSJan Glauber {
152a755a45dSJan Glauber int i;
153a755a45dSJan Glauber
154c9c13ba4SDenis Efremov for (i = 0; i < PCI_STD_NUM_BARS; i++) {
155a755a45dSJan Glauber zdev->bars[i].val = le32_to_cpu(response->bar[i]);
156a755a45dSJan Glauber zdev->bars[i].size = response->bar_size[i];
157a755a45dSJan Glauber }
158828b35f6SJan Glauber zdev->start_dma = response->sdma;
159828b35f6SJan Glauber zdev->end_dma = response->edma;
160a755a45dSJan Glauber zdev->pchid = response->pchid;
161a755a45dSJan Glauber zdev->pfgid = response->pfgid;
162ac4995b9SSebastian Ott zdev->pft = response->pft;
163ac4995b9SSebastian Ott zdev->vfn = response->vfn;
164e6ab7490SAlexander Schmidt zdev->port = response->port;
165ac4995b9SSebastian Ott zdev->uid = response->uid;
1660b7589ecSSebastian Ott zdev->fmb_length = sizeof(u32) * response->fmb_len;
167c9a1752bSPierre Morel zdev->rid_available = response->rid_avail;
168e5794cf1SNiklas Schnelle zdev->is_physfn = response->is_physfn;
169c9a1752bSPierre Morel if (!s390_pci_no_rid && zdev->rid_available)
170c9a1752bSPierre Morel zdev->devfn = response->rid & ZPCI_RID_MASK_DEVFN;
171ac4995b9SSebastian Ott
172ac4995b9SSebastian Ott memcpy(zdev->pfip, response->pfip, sizeof(zdev->pfip));
173ac4995b9SSebastian Ott if (response->util_str_avail) {
174ac4995b9SSebastian Ott memcpy(zdev->util_str, response->util_str,
175ac4995b9SSebastian Ott sizeof(zdev->util_str));
176517fe298SMatthew Rosato zdev->util_str_avail = 1;
177ac4995b9SSebastian Ott }
17871ba41c9SSebastian Ott zdev->mio_capable = response->mio_addr_avail;
179c9c13ba4SDenis Efremov for (i = 0; i < PCI_STD_NUM_BARS; i++) {
180c9c13ba4SDenis Efremov if (!(response->mio.valid & (1 << (PCI_STD_NUM_BARS - i - 1))))
18171ba41c9SSebastian Ott continue;
182ac4995b9SSebastian Ott
1831354b38bSSebastian Ott zdev->bars[i].mio_wb = (void __iomem *) response->mio.addr[i].wb;
1841354b38bSSebastian Ott zdev->bars[i].mio_wt = (void __iomem *) response->mio.addr[i].wt;
18571ba41c9SSebastian Ott }
186a755a45dSJan Glauber return 0;
187a755a45dSJan Glauber }
188a755a45dSJan Glauber
clp_query_pci_fn(struct zpci_dev * zdev)189ba764dd7SNiklas Schnelle int clp_query_pci_fn(struct zpci_dev *zdev)
190a755a45dSJan Glauber {
191a755a45dSJan Glauber struct clp_req_rsp_query_pci *rrb;
192a755a45dSJan Glauber int rc;
193a755a45dSJan Glauber
1941d578966SSebastian Ott rrb = clp_alloc_block(GFP_KERNEL);
195a755a45dSJan Glauber if (!rrb)
196a755a45dSJan Glauber return -ENOMEM;
197a755a45dSJan Glauber
198a755a45dSJan Glauber memset(rrb, 0, sizeof(*rrb));
199a755a45dSJan Glauber rrb->request.hdr.len = sizeof(rrb->request);
200a755a45dSJan Glauber rrb->request.hdr.cmd = CLP_QUERY_PCI_FN;
201a755a45dSJan Glauber rrb->response.hdr.len = sizeof(rrb->response);
202ba764dd7SNiklas Schnelle rrb->request.fh = zdev->fh;
203a755a45dSJan Glauber
204988b86e6SMartin Schwidefsky rc = clp_req(rrb, CLP_LPS_PCI);
205a755a45dSJan Glauber if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) {
206a755a45dSJan Glauber rc = clp_store_query_pci_fn(zdev, &rrb->response);
207a755a45dSJan Glauber if (rc)
208a755a45dSJan Glauber goto out;
209a755a45dSJan Glauber rc = clp_query_pci_fngrp(zdev, rrb->response.pfgid);
210a755a45dSJan Glauber } else {
2111f1dcbd4SSebastian Ott zpci_err("Q PCI FN:\n");
2121f1dcbd4SSebastian Ott zpci_err_clp(rrb->response.hdr.rsp, rc);
213a755a45dSJan Glauber rc = -EIO;
214a755a45dSJan Glauber }
215a755a45dSJan Glauber out:
216a755a45dSJan Glauber clp_free_block(rrb);
217a755a45dSJan Glauber return rc;
218a755a45dSJan Glauber }
219a755a45dSJan Glauber
220f7addcddSNiklas Schnelle /**
221f7addcddSNiklas Schnelle * clp_set_pci_fn() - Execute a command on a PCI function
222f7addcddSNiklas Schnelle * @zdev: Function that will be affected
223cc049eecSNiklas Schnelle * @fh: Out parameter for updated function handle
224f7addcddSNiklas Schnelle * @nr_dma_as: DMA address space number
225f7addcddSNiklas Schnelle * @command: The command code to execute
226f7addcddSNiklas Schnelle *
227f7addcddSNiklas Schnelle * Returns: 0 on success, < 0 for Linux errors (e.g. -ENOMEM), and
228f7addcddSNiklas Schnelle * > 0 for non-success platform responses
229a755a45dSJan Glauber */
clp_set_pci_fn(struct zpci_dev * zdev,u32 * fh,u8 nr_dma_as,u8 command)230cc049eecSNiklas Schnelle static int clp_set_pci_fn(struct zpci_dev *zdev, u32 *fh, u8 nr_dma_as, u8 command)
231a755a45dSJan Glauber {
232a755a45dSJan Glauber struct clp_req_rsp_set_pci *rrb;
233d03abe58SSebastian Ott int rc, retries = 100;
234c68468edSMatthew Rosato u32 gisa = 0;
235a755a45dSJan Glauber
236cc049eecSNiklas Schnelle *fh = 0;
2371d578966SSebastian Ott rrb = clp_alloc_block(GFP_KERNEL);
238a755a45dSJan Glauber if (!rrb)
239a755a45dSJan Glauber return -ENOMEM;
240a755a45dSJan Glauber
241c68468edSMatthew Rosato if (command != CLP_SET_DISABLE_PCI_FN)
242c68468edSMatthew Rosato gisa = zdev->gisa;
243c68468edSMatthew Rosato
244a755a45dSJan Glauber do {
245a755a45dSJan Glauber memset(rrb, 0, sizeof(*rrb));
246a755a45dSJan Glauber rrb->request.hdr.len = sizeof(rrb->request);
247a755a45dSJan Glauber rrb->request.hdr.cmd = CLP_SET_PCI_FN;
248a755a45dSJan Glauber rrb->response.hdr.len = sizeof(rrb->response);
24917cdec96SNiklas Schnelle rrb->request.fh = zdev->fh;
250a755a45dSJan Glauber rrb->request.oc = command;
251a755a45dSJan Glauber rrb->request.ndas = nr_dma_as;
252c68468edSMatthew Rosato rrb->request.gisa = gisa;
253a755a45dSJan Glauber
254988b86e6SMartin Schwidefsky rc = clp_req(rrb, CLP_LPS_PCI);
255a755a45dSJan Glauber if (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY) {
256a755a45dSJan Glauber retries--;
257a755a45dSJan Glauber if (retries < 0)
258a755a45dSJan Glauber break;
259d03abe58SSebastian Ott msleep(20);
260a755a45dSJan Glauber }
261a755a45dSJan Glauber } while (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY);
262a755a45dSJan Glauber
26317cdec96SNiklas Schnelle if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) {
264cc049eecSNiklas Schnelle *fh = rrb->response.fh;
265f7addcddSNiklas Schnelle } else {
266f7addcddSNiklas Schnelle zpci_err("Set PCI FN:\n");
267f7addcddSNiklas Schnelle zpci_err_clp(rrb->response.hdr.rsp, rc);
268f7addcddSNiklas Schnelle if (!rc)
269f7addcddSNiklas Schnelle rc = rrb->response.hdr.rsp;
270a755a45dSJan Glauber }
271a755a45dSJan Glauber clp_free_block(rrb);
272a755a45dSJan Glauber return rc;
273a755a45dSJan Glauber }
274a755a45dSJan Glauber
clp_setup_writeback_mio(void)275b02002ccSNiklas Schnelle int clp_setup_writeback_mio(void)
276b02002ccSNiklas Schnelle {
277b02002ccSNiklas Schnelle struct clp_req_rsp_slpc_pci *rrb;
278b02002ccSNiklas Schnelle u8 wb_bit_pos;
279b02002ccSNiklas Schnelle int rc;
280b02002ccSNiklas Schnelle
281b02002ccSNiklas Schnelle rrb = clp_alloc_block(GFP_KERNEL);
282b02002ccSNiklas Schnelle if (!rrb)
283b02002ccSNiklas Schnelle return -ENOMEM;
284b02002ccSNiklas Schnelle
285b02002ccSNiklas Schnelle memset(rrb, 0, sizeof(*rrb));
286b02002ccSNiklas Schnelle rrb->request.hdr.len = sizeof(rrb->request);
287b02002ccSNiklas Schnelle rrb->request.hdr.cmd = CLP_SLPC;
288b02002ccSNiklas Schnelle rrb->response.hdr.len = sizeof(rrb->response);
289b02002ccSNiklas Schnelle
290b02002ccSNiklas Schnelle rc = clp_req(rrb, CLP_LPS_PCI);
291b02002ccSNiklas Schnelle if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) {
292b02002ccSNiklas Schnelle if (rrb->response.vwb) {
293b02002ccSNiklas Schnelle wb_bit_pos = rrb->response.mio_wb;
294b02002ccSNiklas Schnelle set_bit_inv(wb_bit_pos, &mio_wb_bit_mask);
295b02002ccSNiklas Schnelle zpci_dbg(3, "wb bit: %d\n", wb_bit_pos);
296b02002ccSNiklas Schnelle } else {
297b02002ccSNiklas Schnelle zpci_dbg(3, "wb bit: n.a.\n");
298b02002ccSNiklas Schnelle }
299b02002ccSNiklas Schnelle
300b02002ccSNiklas Schnelle } else {
301b02002ccSNiklas Schnelle zpci_err("SLPC PCI:\n");
302b02002ccSNiklas Schnelle zpci_err_clp(rrb->response.hdr.rsp, rc);
303b02002ccSNiklas Schnelle rc = -EIO;
304b02002ccSNiklas Schnelle }
305b02002ccSNiklas Schnelle clp_free_block(rrb);
306b02002ccSNiklas Schnelle return rc;
307b02002ccSNiklas Schnelle }
308b02002ccSNiklas Schnelle
clp_enable_fh(struct zpci_dev * zdev,u32 * fh,u8 nr_dma_as)309cc049eecSNiklas Schnelle int clp_enable_fh(struct zpci_dev *zdev, u32 *fh, u8 nr_dma_as)
310a755a45dSJan Glauber {
311a755a45dSJan Glauber int rc;
312a755a45dSJan Glauber
313cc049eecSNiklas Schnelle rc = clp_set_pci_fn(zdev, fh, nr_dma_as, CLP_SET_ENABLE_PCI_FN);
314cc049eecSNiklas Schnelle zpci_dbg(3, "ena fid:%x, fh:%x, rc:%d\n", zdev->fid, *fh, rc);
315f7addcddSNiklas Schnelle if (!rc && zpci_use_mio(zdev)) {
316cc049eecSNiklas Schnelle rc = clp_set_pci_fn(zdev, fh, nr_dma_as, CLP_SET_ENABLE_MIO);
31717cdec96SNiklas Schnelle zpci_dbg(3, "ena mio fid:%x, fh:%x, rc:%d\n",
318cc049eecSNiklas Schnelle zdev->fid, *fh, rc);
31971ba41c9SSebastian Ott if (rc)
320cc049eecSNiklas Schnelle clp_disable_fh(zdev, fh);
32171ba41c9SSebastian Ott }
322a755a45dSJan Glauber return rc;
323a755a45dSJan Glauber }
324a755a45dSJan Glauber
clp_disable_fh(struct zpci_dev * zdev,u32 * fh)325cc049eecSNiklas Schnelle int clp_disable_fh(struct zpci_dev *zdev, u32 *fh)
326a755a45dSJan Glauber {
327a755a45dSJan Glauber int rc;
328a755a45dSJan Glauber
329a755a45dSJan Glauber if (!zdev_enabled(zdev))
330a755a45dSJan Glauber return 0;
331a755a45dSJan Glauber
332cc049eecSNiklas Schnelle rc = clp_set_pci_fn(zdev, fh, 0, CLP_SET_DISABLE_PCI_FN);
333cc049eecSNiklas Schnelle zpci_dbg(3, "dis fid:%x, fh:%x, rc:%d\n", zdev->fid, *fh, rc);
334cc049eecSNiklas Schnelle return rc;
335cc049eecSNiklas Schnelle }
336cc049eecSNiklas Schnelle
clp_list_pci_req(struct clp_req_rsp_list_pci * rrb,u64 * resume_token,int * nentries)337cc049eecSNiklas Schnelle static int clp_list_pci_req(struct clp_req_rsp_list_pci *rrb,
338cc049eecSNiklas Schnelle u64 *resume_token, int *nentries)
339cc049eecSNiklas Schnelle {
340cc049eecSNiklas Schnelle int rc;
341cc049eecSNiklas Schnelle
342cc049eecSNiklas Schnelle memset(rrb, 0, sizeof(*rrb));
343cc049eecSNiklas Schnelle rrb->request.hdr.len = sizeof(rrb->request);
344cc049eecSNiklas Schnelle rrb->request.hdr.cmd = CLP_LIST_PCI;
345cc049eecSNiklas Schnelle /* store as many entries as possible */
346cc049eecSNiklas Schnelle rrb->response.hdr.len = CLP_BLK_SIZE - LIST_PCI_HDR_LEN;
347cc049eecSNiklas Schnelle rrb->request.resume_token = *resume_token;
348cc049eecSNiklas Schnelle
349cc049eecSNiklas Schnelle /* Get PCI function handle list */
350cc049eecSNiklas Schnelle rc = clp_req(rrb, CLP_LPS_PCI);
351cc049eecSNiklas Schnelle if (rc || rrb->response.hdr.rsp != CLP_RC_OK) {
352cc049eecSNiklas Schnelle zpci_err("List PCI FN:\n");
353cc049eecSNiklas Schnelle zpci_err_clp(rrb->response.hdr.rsp, rc);
354cc049eecSNiklas Schnelle return -EIO;
355cc049eecSNiklas Schnelle }
356cc049eecSNiklas Schnelle
357cc049eecSNiklas Schnelle update_uid_checking(rrb->response.uid_checking);
358cc049eecSNiklas Schnelle WARN_ON_ONCE(rrb->response.entry_size !=
359cc049eecSNiklas Schnelle sizeof(struct clp_fh_list_entry));
360cc049eecSNiklas Schnelle
361cc049eecSNiklas Schnelle *nentries = (rrb->response.hdr.len - LIST_PCI_HDR_LEN) /
362cc049eecSNiklas Schnelle rrb->response.entry_size;
363cc049eecSNiklas Schnelle *resume_token = rrb->response.resume_token;
364cc049eecSNiklas Schnelle
365a755a45dSJan Glauber return rc;
366a755a45dSJan Glauber }
367a755a45dSJan Glauber
clp_list_pci(struct clp_req_rsp_list_pci * rrb,void * data,void (* cb)(struct clp_fh_list_entry *,void *))368783684f1SSebastian Ott static int clp_list_pci(struct clp_req_rsp_list_pci *rrb, void *data,
369783684f1SSebastian Ott void (*cb)(struct clp_fh_list_entry *, void *))
370a755a45dSJan Glauber {
371a755a45dSJan Glauber u64 resume_token = 0;
372cc049eecSNiklas Schnelle int nentries, i, rc;
373a755a45dSJan Glauber
374a755a45dSJan Glauber do {
375cc049eecSNiklas Schnelle rc = clp_list_pci_req(rrb, &resume_token, &nentries);
376cc049eecSNiklas Schnelle if (rc)
377cc049eecSNiklas Schnelle return rc;
378cc049eecSNiklas Schnelle for (i = 0; i < nentries; i++)
379783684f1SSebastian Ott cb(&rrb->response.fh_list[i], data);
380a755a45dSJan Glauber } while (resume_token);
381cc049eecSNiklas Schnelle
3821d578966SSebastian Ott return rc;
3831d578966SSebastian Ott }
3841d578966SSebastian Ott
clp_find_pci(struct clp_req_rsp_list_pci * rrb,u32 fid,struct clp_fh_list_entry * entry)385cc049eecSNiklas Schnelle static int clp_find_pci(struct clp_req_rsp_list_pci *rrb, u32 fid,
386cc049eecSNiklas Schnelle struct clp_fh_list_entry *entry)
387cc049eecSNiklas Schnelle {
388cc049eecSNiklas Schnelle struct clp_fh_list_entry *fh_list;
389cc049eecSNiklas Schnelle u64 resume_token = 0;
390cc049eecSNiklas Schnelle int nentries, i, rc;
391cc049eecSNiklas Schnelle
392cc049eecSNiklas Schnelle do {
393cc049eecSNiklas Schnelle rc = clp_list_pci_req(rrb, &resume_token, &nentries);
394cc049eecSNiklas Schnelle if (rc)
395cc049eecSNiklas Schnelle return rc;
396cc049eecSNiklas Schnelle fh_list = rrb->response.fh_list;
39785ad2721SPierre Morel for (i = 0; i < nentries; i++) {
398cc049eecSNiklas Schnelle if (fh_list[i].fid == fid) {
399cc049eecSNiklas Schnelle *entry = fh_list[i];
400cc049eecSNiklas Schnelle return 0;
401cc049eecSNiklas Schnelle }
402cc049eecSNiklas Schnelle }
403cc049eecSNiklas Schnelle } while (resume_token);
404cc049eecSNiklas Schnelle
405cc049eecSNiklas Schnelle return -ENODEV;
406cc049eecSNiklas Schnelle }
407cc049eecSNiklas Schnelle
__clp_add(struct clp_fh_list_entry * entry,void * data)408783684f1SSebastian Ott static void __clp_add(struct clp_fh_list_entry *entry, void *data)
4091d578966SSebastian Ott {
4101d578966SSebastian Ott struct zpci_dev *zdev;
4111d578966SSebastian Ott
4121d578966SSebastian Ott if (!entry->vendor_id)
4131d578966SSebastian Ott return;
4141d578966SSebastian Ott
4151d578966SSebastian Ott zdev = get_zdev_by_fid(entry->fid);
416c122383dSNiklas Schnelle if (zdev) {
417c122383dSNiklas Schnelle zpci_zdev_put(zdev);
418c122383dSNiklas Schnelle return;
419c122383dSNiklas Schnelle }
420ba764dd7SNiklas Schnelle zpci_create_device(entry->fid, entry->fh, entry->config_state);
4211d578966SSebastian Ott }
4221d578966SSebastian Ott
clp_scan_pci_devices(void)4231d578966SSebastian Ott int clp_scan_pci_devices(void)
4241d578966SSebastian Ott {
4251d578966SSebastian Ott struct clp_req_rsp_list_pci *rrb;
4261d578966SSebastian Ott int rc;
4271d578966SSebastian Ott
4281d578966SSebastian Ott rrb = clp_alloc_block(GFP_KERNEL);
4291d578966SSebastian Ott if (!rrb)
4301d578966SSebastian Ott return -ENOMEM;
4311d578966SSebastian Ott
432783684f1SSebastian Ott rc = clp_list_pci(rrb, NULL, __clp_add);
4331d578966SSebastian Ott
4341d578966SSebastian Ott clp_free_block(rrb);
4351d578966SSebastian Ott return rc;
4361d578966SSebastian Ott }
4371d578966SSebastian Ott
438c3b2c906SNiklas Schnelle /*
439cc049eecSNiklas Schnelle * Get the current function handle of the function matching @fid
44017cdec96SNiklas Schnelle */
clp_refresh_fh(u32 fid,u32 * fh)441cc049eecSNiklas Schnelle int clp_refresh_fh(u32 fid, u32 *fh)
44257b5918cSSebastian Ott {
44357b5918cSSebastian Ott struct clp_req_rsp_list_pci *rrb;
444cc049eecSNiklas Schnelle struct clp_fh_list_entry entry;
44557b5918cSSebastian Ott int rc;
44657b5918cSSebastian Ott
44757b5918cSSebastian Ott rrb = clp_alloc_block(GFP_NOWAIT);
44857b5918cSSebastian Ott if (!rrb)
44957b5918cSSebastian Ott return -ENOMEM;
45057b5918cSSebastian Ott
451cc049eecSNiklas Schnelle rc = clp_find_pci(rrb, fid, &entry);
452cc049eecSNiklas Schnelle if (!rc)
453cc049eecSNiklas Schnelle *fh = entry.fh;
454783684f1SSebastian Ott
455783684f1SSebastian Ott clp_free_block(rrb);
456783684f1SSebastian Ott return rc;
457783684f1SSebastian Ott }
458783684f1SSebastian Ott
clp_get_state(u32 fid,enum zpci_state * state)459783684f1SSebastian Ott int clp_get_state(u32 fid, enum zpci_state *state)
460783684f1SSebastian Ott {
461783684f1SSebastian Ott struct clp_req_rsp_list_pci *rrb;
462cc049eecSNiklas Schnelle struct clp_fh_list_entry entry;
463783684f1SSebastian Ott int rc;
464783684f1SSebastian Ott
46598dfd326SSebastian Ott rrb = clp_alloc_block(GFP_ATOMIC);
466783684f1SSebastian Ott if (!rrb)
467783684f1SSebastian Ott return -ENOMEM;
468783684f1SSebastian Ott
469cc049eecSNiklas Schnelle rc = clp_find_pci(rrb, fid, &entry);
470ebd9cc65SNiklas Schnelle if (!rc) {
471cc049eecSNiklas Schnelle *state = entry.config_state;
472ebd9cc65SNiklas Schnelle } else if (rc == -ENODEV) {
473ebd9cc65SNiklas Schnelle *state = ZPCI_FN_STATE_RESERVED;
474ebd9cc65SNiklas Schnelle rc = 0;
475ebd9cc65SNiklas Schnelle }
47657b5918cSSebastian Ott
47757b5918cSSebastian Ott clp_free_block(rrb);
47857b5918cSSebastian Ott return rc;
47957b5918cSSebastian Ott }
480988b86e6SMartin Schwidefsky
clp_base_slpc(struct clp_req * req,struct clp_req_rsp_slpc * lpcb)481988b86e6SMartin Schwidefsky static int clp_base_slpc(struct clp_req *req, struct clp_req_rsp_slpc *lpcb)
482988b86e6SMartin Schwidefsky {
483988b86e6SMartin Schwidefsky unsigned long limit = PAGE_SIZE - sizeof(lpcb->request);
484988b86e6SMartin Schwidefsky
485988b86e6SMartin Schwidefsky if (lpcb->request.hdr.len != sizeof(lpcb->request) ||
486988b86e6SMartin Schwidefsky lpcb->response.hdr.len > limit)
487988b86e6SMartin Schwidefsky return -EINVAL;
488988b86e6SMartin Schwidefsky return clp_req(lpcb, CLP_LPS_BASE) ? -EOPNOTSUPP : 0;
489988b86e6SMartin Schwidefsky }
490988b86e6SMartin Schwidefsky
clp_base_command(struct clp_req * req,struct clp_req_hdr * lpcb)491988b86e6SMartin Schwidefsky static int clp_base_command(struct clp_req *req, struct clp_req_hdr *lpcb)
492988b86e6SMartin Schwidefsky {
493988b86e6SMartin Schwidefsky switch (lpcb->cmd) {
494988b86e6SMartin Schwidefsky case 0x0001: /* store logical-processor characteristics */
495988b86e6SMartin Schwidefsky return clp_base_slpc(req, (void *) lpcb);
496988b86e6SMartin Schwidefsky default:
497988b86e6SMartin Schwidefsky return -EINVAL;
498988b86e6SMartin Schwidefsky }
499988b86e6SMartin Schwidefsky }
500988b86e6SMartin Schwidefsky
clp_pci_slpc(struct clp_req * req,struct clp_req_rsp_slpc_pci * lpcb)501b02002ccSNiklas Schnelle static int clp_pci_slpc(struct clp_req *req, struct clp_req_rsp_slpc_pci *lpcb)
502988b86e6SMartin Schwidefsky {
503988b86e6SMartin Schwidefsky unsigned long limit = PAGE_SIZE - sizeof(lpcb->request);
504988b86e6SMartin Schwidefsky
505988b86e6SMartin Schwidefsky if (lpcb->request.hdr.len != sizeof(lpcb->request) ||
506988b86e6SMartin Schwidefsky lpcb->response.hdr.len > limit)
507988b86e6SMartin Schwidefsky return -EINVAL;
508988b86e6SMartin Schwidefsky return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0;
509988b86e6SMartin Schwidefsky }
510988b86e6SMartin Schwidefsky
clp_pci_list(struct clp_req * req,struct clp_req_rsp_list_pci * lpcb)511988b86e6SMartin Schwidefsky static int clp_pci_list(struct clp_req *req, struct clp_req_rsp_list_pci *lpcb)
512988b86e6SMartin Schwidefsky {
513988b86e6SMartin Schwidefsky unsigned long limit = PAGE_SIZE - sizeof(lpcb->request);
514988b86e6SMartin Schwidefsky
515988b86e6SMartin Schwidefsky if (lpcb->request.hdr.len != sizeof(lpcb->request) ||
516988b86e6SMartin Schwidefsky lpcb->response.hdr.len > limit)
517988b86e6SMartin Schwidefsky return -EINVAL;
518988b86e6SMartin Schwidefsky if (lpcb->request.reserved2 != 0)
519988b86e6SMartin Schwidefsky return -EINVAL;
520988b86e6SMartin Schwidefsky return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0;
521988b86e6SMartin Schwidefsky }
522988b86e6SMartin Schwidefsky
clp_pci_query(struct clp_req * req,struct clp_req_rsp_query_pci * lpcb)523988b86e6SMartin Schwidefsky static int clp_pci_query(struct clp_req *req,
524988b86e6SMartin Schwidefsky struct clp_req_rsp_query_pci *lpcb)
525988b86e6SMartin Schwidefsky {
526988b86e6SMartin Schwidefsky unsigned long limit = PAGE_SIZE - sizeof(lpcb->request);
527988b86e6SMartin Schwidefsky
528988b86e6SMartin Schwidefsky if (lpcb->request.hdr.len != sizeof(lpcb->request) ||
529988b86e6SMartin Schwidefsky lpcb->response.hdr.len > limit)
530988b86e6SMartin Schwidefsky return -EINVAL;
531988b86e6SMartin Schwidefsky if (lpcb->request.reserved2 != 0 || lpcb->request.reserved3 != 0)
532988b86e6SMartin Schwidefsky return -EINVAL;
533988b86e6SMartin Schwidefsky return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0;
534988b86e6SMartin Schwidefsky }
535988b86e6SMartin Schwidefsky
clp_pci_query_grp(struct clp_req * req,struct clp_req_rsp_query_pci_grp * lpcb)536988b86e6SMartin Schwidefsky static int clp_pci_query_grp(struct clp_req *req,
537988b86e6SMartin Schwidefsky struct clp_req_rsp_query_pci_grp *lpcb)
538988b86e6SMartin Schwidefsky {
539988b86e6SMartin Schwidefsky unsigned long limit = PAGE_SIZE - sizeof(lpcb->request);
540988b86e6SMartin Schwidefsky
541988b86e6SMartin Schwidefsky if (lpcb->request.hdr.len != sizeof(lpcb->request) ||
542988b86e6SMartin Schwidefsky lpcb->response.hdr.len > limit)
543988b86e6SMartin Schwidefsky return -EINVAL;
544988b86e6SMartin Schwidefsky if (lpcb->request.reserved2 != 0 || lpcb->request.reserved3 != 0 ||
545988b86e6SMartin Schwidefsky lpcb->request.reserved4 != 0)
546988b86e6SMartin Schwidefsky return -EINVAL;
547988b86e6SMartin Schwidefsky return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0;
548988b86e6SMartin Schwidefsky }
549988b86e6SMartin Schwidefsky
clp_pci_command(struct clp_req * req,struct clp_req_hdr * lpcb)550988b86e6SMartin Schwidefsky static int clp_pci_command(struct clp_req *req, struct clp_req_hdr *lpcb)
551988b86e6SMartin Schwidefsky {
552988b86e6SMartin Schwidefsky switch (lpcb->cmd) {
553988b86e6SMartin Schwidefsky case 0x0001: /* store logical-processor characteristics */
554988b86e6SMartin Schwidefsky return clp_pci_slpc(req, (void *) lpcb);
555988b86e6SMartin Schwidefsky case 0x0002: /* list PCI functions */
556988b86e6SMartin Schwidefsky return clp_pci_list(req, (void *) lpcb);
557988b86e6SMartin Schwidefsky case 0x0003: /* query PCI function */
558988b86e6SMartin Schwidefsky return clp_pci_query(req, (void *) lpcb);
559988b86e6SMartin Schwidefsky case 0x0004: /* query PCI function group */
560988b86e6SMartin Schwidefsky return clp_pci_query_grp(req, (void *) lpcb);
561988b86e6SMartin Schwidefsky default:
562988b86e6SMartin Schwidefsky return -EINVAL;
563988b86e6SMartin Schwidefsky }
564988b86e6SMartin Schwidefsky }
565988b86e6SMartin Schwidefsky
clp_normal_command(struct clp_req * req)566988b86e6SMartin Schwidefsky static int clp_normal_command(struct clp_req *req)
567988b86e6SMartin Schwidefsky {
568988b86e6SMartin Schwidefsky struct clp_req_hdr *lpcb;
569988b86e6SMartin Schwidefsky void __user *uptr;
570988b86e6SMartin Schwidefsky int rc;
571988b86e6SMartin Schwidefsky
572988b86e6SMartin Schwidefsky rc = -EINVAL;
573988b86e6SMartin Schwidefsky if (req->lps != 0 && req->lps != 2)
574988b86e6SMartin Schwidefsky goto out;
575988b86e6SMartin Schwidefsky
576988b86e6SMartin Schwidefsky rc = -ENOMEM;
577988b86e6SMartin Schwidefsky lpcb = clp_alloc_block(GFP_KERNEL);
578988b86e6SMartin Schwidefsky if (!lpcb)
579988b86e6SMartin Schwidefsky goto out;
580988b86e6SMartin Schwidefsky
581988b86e6SMartin Schwidefsky rc = -EFAULT;
582988b86e6SMartin Schwidefsky uptr = (void __force __user *)(unsigned long) req->data_p;
583988b86e6SMartin Schwidefsky if (copy_from_user(lpcb, uptr, PAGE_SIZE) != 0)
584988b86e6SMartin Schwidefsky goto out_free;
585988b86e6SMartin Schwidefsky
586988b86e6SMartin Schwidefsky rc = -EINVAL;
587988b86e6SMartin Schwidefsky if (lpcb->fmt != 0 || lpcb->reserved1 != 0 || lpcb->reserved2 != 0)
588988b86e6SMartin Schwidefsky goto out_free;
589988b86e6SMartin Schwidefsky
590988b86e6SMartin Schwidefsky switch (req->lps) {
591988b86e6SMartin Schwidefsky case 0:
592988b86e6SMartin Schwidefsky rc = clp_base_command(req, lpcb);
593988b86e6SMartin Schwidefsky break;
594988b86e6SMartin Schwidefsky case 2:
595988b86e6SMartin Schwidefsky rc = clp_pci_command(req, lpcb);
596988b86e6SMartin Schwidefsky break;
597988b86e6SMartin Schwidefsky }
598988b86e6SMartin Schwidefsky if (rc)
599988b86e6SMartin Schwidefsky goto out_free;
600988b86e6SMartin Schwidefsky
601988b86e6SMartin Schwidefsky rc = -EFAULT;
602988b86e6SMartin Schwidefsky if (copy_to_user(uptr, lpcb, PAGE_SIZE) != 0)
603988b86e6SMartin Schwidefsky goto out_free;
604988b86e6SMartin Schwidefsky
605988b86e6SMartin Schwidefsky rc = 0;
606988b86e6SMartin Schwidefsky
607988b86e6SMartin Schwidefsky out_free:
608988b86e6SMartin Schwidefsky clp_free_block(lpcb);
609988b86e6SMartin Schwidefsky out:
610988b86e6SMartin Schwidefsky return rc;
611988b86e6SMartin Schwidefsky }
612988b86e6SMartin Schwidefsky
clp_immediate_command(struct clp_req * req)613988b86e6SMartin Schwidefsky static int clp_immediate_command(struct clp_req *req)
614988b86e6SMartin Schwidefsky {
615988b86e6SMartin Schwidefsky void __user *uptr;
616988b86e6SMartin Schwidefsky unsigned long ilp;
617988b86e6SMartin Schwidefsky int exists;
618988b86e6SMartin Schwidefsky
619988b86e6SMartin Schwidefsky if (req->cmd > 1 || clp_get_ilp(&ilp) != 0)
620988b86e6SMartin Schwidefsky return -EINVAL;
621988b86e6SMartin Schwidefsky
622988b86e6SMartin Schwidefsky uptr = (void __force __user *)(unsigned long) req->data_p;
623988b86e6SMartin Schwidefsky if (req->cmd == 0) {
624988b86e6SMartin Schwidefsky /* Command code 0: test for a specific processor */
625988b86e6SMartin Schwidefsky exists = test_bit_inv(req->lps, &ilp);
626988b86e6SMartin Schwidefsky return put_user(exists, (int __user *) uptr);
627988b86e6SMartin Schwidefsky }
628988b86e6SMartin Schwidefsky /* Command code 1: return bit mask of installed processors */
629988b86e6SMartin Schwidefsky return put_user(ilp, (unsigned long __user *) uptr);
630988b86e6SMartin Schwidefsky }
631988b86e6SMartin Schwidefsky
clp_misc_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)632988b86e6SMartin Schwidefsky static long clp_misc_ioctl(struct file *filp, unsigned int cmd,
633988b86e6SMartin Schwidefsky unsigned long arg)
634988b86e6SMartin Schwidefsky {
635988b86e6SMartin Schwidefsky struct clp_req req;
636988b86e6SMartin Schwidefsky void __user *argp;
637988b86e6SMartin Schwidefsky
638988b86e6SMartin Schwidefsky if (cmd != CLP_SYNC)
639988b86e6SMartin Schwidefsky return -EINVAL;
640988b86e6SMartin Schwidefsky
641988b86e6SMartin Schwidefsky argp = is_compat_task() ? compat_ptr(arg) : (void __user *) arg;
642988b86e6SMartin Schwidefsky if (copy_from_user(&req, argp, sizeof(req)))
643988b86e6SMartin Schwidefsky return -EFAULT;
644988b86e6SMartin Schwidefsky if (req.r != 0)
645988b86e6SMartin Schwidefsky return -EINVAL;
646988b86e6SMartin Schwidefsky return req.c ? clp_immediate_command(&req) : clp_normal_command(&req);
647988b86e6SMartin Schwidefsky }
648988b86e6SMartin Schwidefsky
clp_misc_release(struct inode * inode,struct file * filp)649988b86e6SMartin Schwidefsky static int clp_misc_release(struct inode *inode, struct file *filp)
650988b86e6SMartin Schwidefsky {
651988b86e6SMartin Schwidefsky return 0;
652988b86e6SMartin Schwidefsky }
653988b86e6SMartin Schwidefsky
654988b86e6SMartin Schwidefsky static const struct file_operations clp_misc_fops = {
655988b86e6SMartin Schwidefsky .owner = THIS_MODULE,
656988b86e6SMartin Schwidefsky .open = nonseekable_open,
657988b86e6SMartin Schwidefsky .release = clp_misc_release,
658988b86e6SMartin Schwidefsky .unlocked_ioctl = clp_misc_ioctl,
659988b86e6SMartin Schwidefsky .compat_ioctl = clp_misc_ioctl,
660988b86e6SMartin Schwidefsky .llseek = no_llseek,
661988b86e6SMartin Schwidefsky };
662988b86e6SMartin Schwidefsky
663988b86e6SMartin Schwidefsky static struct miscdevice clp_misc_device = {
664988b86e6SMartin Schwidefsky .minor = MISC_DYNAMIC_MINOR,
665988b86e6SMartin Schwidefsky .name = "clp",
666988b86e6SMartin Schwidefsky .fops = &clp_misc_fops,
667988b86e6SMartin Schwidefsky };
668988b86e6SMartin Schwidefsky
669*dedf98ddSLi Zetao builtin_misc_device(clp_misc_device);
670