18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2012 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author(s): 68c2ecf20Sopenharmony_ci * Jan Glauber <jang@linux.vnet.ibm.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "zpci" 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/compat.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/pci.h> 198c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 208c2ecf20Sopenharmony_ci#include <asm/pci_debug.h> 218c2ecf20Sopenharmony_ci#include <asm/pci_clp.h> 228c2ecf20Sopenharmony_ci#include <asm/clp.h> 238c2ecf20Sopenharmony_ci#include <uapi/asm/clp.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "pci_bus.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cibool zpci_unique_uid; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_civoid update_uid_checking(bool new) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci if (zpci_unique_uid != new) 328c2ecf20Sopenharmony_ci zpci_dbg(1, "uid checking:%d\n", new); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci zpci_unique_uid = new; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic inline void zpci_err_clp(unsigned int rsp, int rc) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct { 408c2ecf20Sopenharmony_ci unsigned int rsp; 418c2ecf20Sopenharmony_ci int rc; 428c2ecf20Sopenharmony_ci } __packed data = {rsp, rc}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci zpci_err_hex(&data, sizeof(data)); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * Call Logical Processor with c=1, lps=0 and command 1 498c2ecf20Sopenharmony_ci * to get the bit mask of installed logical processors 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_cistatic inline int clp_get_ilp(unsigned long *ilp) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci unsigned long mask; 548c2ecf20Sopenharmony_ci int cc = 3; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci asm volatile ( 578c2ecf20Sopenharmony_ci " .insn rrf,0xb9a00000,%[mask],%[cmd],8,0\n" 588c2ecf20Sopenharmony_ci "0: ipm %[cc]\n" 598c2ecf20Sopenharmony_ci " srl %[cc],28\n" 608c2ecf20Sopenharmony_ci "1:\n" 618c2ecf20Sopenharmony_ci EX_TABLE(0b, 1b) 628c2ecf20Sopenharmony_ci : [cc] "+d" (cc), [mask] "=d" (mask) : [cmd] "a" (1) 638c2ecf20Sopenharmony_ci : "cc"); 648c2ecf20Sopenharmony_ci *ilp = mask; 658c2ecf20Sopenharmony_ci return cc; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* 698c2ecf20Sopenharmony_ci * Call Logical Processor with c=0, the give constant lps and an lpcb request. 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_cistatic __always_inline int clp_req(void *data, unsigned int lps) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct { u8 _[CLP_BLK_SIZE]; } *req = data; 748c2ecf20Sopenharmony_ci u64 ignored; 758c2ecf20Sopenharmony_ci int cc = 3; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci asm volatile ( 788c2ecf20Sopenharmony_ci " .insn rrf,0xb9a00000,%[ign],%[req],0,%[lps]\n" 798c2ecf20Sopenharmony_ci "0: ipm %[cc]\n" 808c2ecf20Sopenharmony_ci " srl %[cc],28\n" 818c2ecf20Sopenharmony_ci "1:\n" 828c2ecf20Sopenharmony_ci EX_TABLE(0b, 1b) 838c2ecf20Sopenharmony_ci : [cc] "+d" (cc), [ign] "=d" (ignored), "+m" (*req) 848c2ecf20Sopenharmony_ci : [req] "a" (req), [lps] "i" (lps) 858c2ecf20Sopenharmony_ci : "cc"); 868c2ecf20Sopenharmony_ci return cc; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void *clp_alloc_block(gfp_t gfp_mask) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci return (void *) __get_free_pages(gfp_mask, get_order(CLP_BLK_SIZE)); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void clp_free_block(void *ptr) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci free_pages((unsigned long) ptr, get_order(CLP_BLK_SIZE)); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void clp_store_query_pci_fngrp(struct zpci_dev *zdev, 1008c2ecf20Sopenharmony_ci struct clp_rsp_query_pci_grp *response) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci zdev->tlb_refresh = response->refresh; 1038c2ecf20Sopenharmony_ci zdev->dma_mask = response->dasm; 1048c2ecf20Sopenharmony_ci zdev->msi_addr = response->msia; 1058c2ecf20Sopenharmony_ci zdev->max_msi = response->noi; 1068c2ecf20Sopenharmony_ci zdev->fmb_update = response->mui; 1078c2ecf20Sopenharmony_ci zdev->version = response->version; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci switch (response->version) { 1108c2ecf20Sopenharmony_ci case 1: 1118c2ecf20Sopenharmony_ci zdev->max_bus_speed = PCIE_SPEED_5_0GT; 1128c2ecf20Sopenharmony_ci break; 1138c2ecf20Sopenharmony_ci default: 1148c2ecf20Sopenharmony_ci zdev->max_bus_speed = PCI_SPEED_UNKNOWN; 1158c2ecf20Sopenharmony_ci break; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int clp_query_pci_fngrp(struct zpci_dev *zdev, u8 pfgid) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct clp_req_rsp_query_pci_grp *rrb; 1228c2ecf20Sopenharmony_ci int rc; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci rrb = clp_alloc_block(GFP_KERNEL); 1258c2ecf20Sopenharmony_ci if (!rrb) 1268c2ecf20Sopenharmony_ci return -ENOMEM; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci memset(rrb, 0, sizeof(*rrb)); 1298c2ecf20Sopenharmony_ci rrb->request.hdr.len = sizeof(rrb->request); 1308c2ecf20Sopenharmony_ci rrb->request.hdr.cmd = CLP_QUERY_PCI_FNGRP; 1318c2ecf20Sopenharmony_ci rrb->response.hdr.len = sizeof(rrb->response); 1328c2ecf20Sopenharmony_ci rrb->request.pfgid = pfgid; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci rc = clp_req(rrb, CLP_LPS_PCI); 1358c2ecf20Sopenharmony_ci if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) 1368c2ecf20Sopenharmony_ci clp_store_query_pci_fngrp(zdev, &rrb->response); 1378c2ecf20Sopenharmony_ci else { 1388c2ecf20Sopenharmony_ci zpci_err("Q PCI FGRP:\n"); 1398c2ecf20Sopenharmony_ci zpci_err_clp(rrb->response.hdr.rsp, rc); 1408c2ecf20Sopenharmony_ci rc = -EIO; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci clp_free_block(rrb); 1438c2ecf20Sopenharmony_ci return rc; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int clp_store_query_pci_fn(struct zpci_dev *zdev, 1478c2ecf20Sopenharmony_ci struct clp_rsp_query_pci *response) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci int i; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci for (i = 0; i < PCI_STD_NUM_BARS; i++) { 1528c2ecf20Sopenharmony_ci zdev->bars[i].val = le32_to_cpu(response->bar[i]); 1538c2ecf20Sopenharmony_ci zdev->bars[i].size = response->bar_size[i]; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci zdev->start_dma = response->sdma; 1568c2ecf20Sopenharmony_ci zdev->end_dma = response->edma; 1578c2ecf20Sopenharmony_ci zdev->pchid = response->pchid; 1588c2ecf20Sopenharmony_ci zdev->pfgid = response->pfgid; 1598c2ecf20Sopenharmony_ci zdev->pft = response->pft; 1608c2ecf20Sopenharmony_ci zdev->vfn = response->vfn; 1618c2ecf20Sopenharmony_ci zdev->port = response->port; 1628c2ecf20Sopenharmony_ci zdev->uid = response->uid; 1638c2ecf20Sopenharmony_ci zdev->fmb_length = sizeof(u32) * response->fmb_len; 1648c2ecf20Sopenharmony_ci zdev->rid_available = response->rid_avail; 1658c2ecf20Sopenharmony_ci zdev->is_physfn = response->is_physfn; 1668c2ecf20Sopenharmony_ci if (!s390_pci_no_rid && zdev->rid_available) 1678c2ecf20Sopenharmony_ci zdev->devfn = response->rid & ZPCI_RID_MASK_DEVFN; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci memcpy(zdev->pfip, response->pfip, sizeof(zdev->pfip)); 1708c2ecf20Sopenharmony_ci if (response->util_str_avail) { 1718c2ecf20Sopenharmony_ci memcpy(zdev->util_str, response->util_str, 1728c2ecf20Sopenharmony_ci sizeof(zdev->util_str)); 1738c2ecf20Sopenharmony_ci zdev->util_str_avail = 1; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci zdev->mio_capable = response->mio_addr_avail; 1768c2ecf20Sopenharmony_ci for (i = 0; i < PCI_STD_NUM_BARS; i++) { 1778c2ecf20Sopenharmony_ci if (!(response->mio.valid & (1 << (PCI_STD_NUM_BARS - i - 1)))) 1788c2ecf20Sopenharmony_ci continue; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci zdev->bars[i].mio_wb = (void __iomem *) response->mio.addr[i].wb; 1818c2ecf20Sopenharmony_ci zdev->bars[i].mio_wt = (void __iomem *) response->mio.addr[i].wt; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ciint clp_query_pci_fn(struct zpci_dev *zdev) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct clp_req_rsp_query_pci *rrb; 1898c2ecf20Sopenharmony_ci int rc; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci rrb = clp_alloc_block(GFP_KERNEL); 1928c2ecf20Sopenharmony_ci if (!rrb) 1938c2ecf20Sopenharmony_ci return -ENOMEM; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci memset(rrb, 0, sizeof(*rrb)); 1968c2ecf20Sopenharmony_ci rrb->request.hdr.len = sizeof(rrb->request); 1978c2ecf20Sopenharmony_ci rrb->request.hdr.cmd = CLP_QUERY_PCI_FN; 1988c2ecf20Sopenharmony_ci rrb->response.hdr.len = sizeof(rrb->response); 1998c2ecf20Sopenharmony_ci rrb->request.fh = zdev->fh; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci rc = clp_req(rrb, CLP_LPS_PCI); 2028c2ecf20Sopenharmony_ci if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) { 2038c2ecf20Sopenharmony_ci rc = clp_store_query_pci_fn(zdev, &rrb->response); 2048c2ecf20Sopenharmony_ci if (rc) 2058c2ecf20Sopenharmony_ci goto out; 2068c2ecf20Sopenharmony_ci rc = clp_query_pci_fngrp(zdev, rrb->response.pfgid); 2078c2ecf20Sopenharmony_ci } else { 2088c2ecf20Sopenharmony_ci zpci_err("Q PCI FN:\n"); 2098c2ecf20Sopenharmony_ci zpci_err_clp(rrb->response.hdr.rsp, rc); 2108c2ecf20Sopenharmony_ci rc = -EIO; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ciout: 2138c2ecf20Sopenharmony_ci clp_free_block(rrb); 2148c2ecf20Sopenharmony_ci return rc; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int clp_refresh_fh(u32 fid); 2188c2ecf20Sopenharmony_ci/** 2198c2ecf20Sopenharmony_ci * clp_set_pci_fn() - Execute a command on a PCI function 2208c2ecf20Sopenharmony_ci * @zdev: Function that will be affected 2218c2ecf20Sopenharmony_ci * @nr_dma_as: DMA address space number 2228c2ecf20Sopenharmony_ci * @command: The command code to execute 2238c2ecf20Sopenharmony_ci * 2248c2ecf20Sopenharmony_ci * Returns: 0 on success, < 0 for Linux errors (e.g. -ENOMEM), and 2258c2ecf20Sopenharmony_ci * > 0 for non-success platform responses 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_cistatic int clp_set_pci_fn(struct zpci_dev *zdev, u8 nr_dma_as, u8 command) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct clp_req_rsp_set_pci *rrb; 2308c2ecf20Sopenharmony_ci int rc, retries = 100; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci rrb = clp_alloc_block(GFP_KERNEL); 2338c2ecf20Sopenharmony_ci if (!rrb) 2348c2ecf20Sopenharmony_ci return -ENOMEM; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci do { 2378c2ecf20Sopenharmony_ci memset(rrb, 0, sizeof(*rrb)); 2388c2ecf20Sopenharmony_ci rrb->request.hdr.len = sizeof(rrb->request); 2398c2ecf20Sopenharmony_ci rrb->request.hdr.cmd = CLP_SET_PCI_FN; 2408c2ecf20Sopenharmony_ci rrb->response.hdr.len = sizeof(rrb->response); 2418c2ecf20Sopenharmony_ci rrb->request.fh = zdev->fh; 2428c2ecf20Sopenharmony_ci rrb->request.oc = command; 2438c2ecf20Sopenharmony_ci rrb->request.ndas = nr_dma_as; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci rc = clp_req(rrb, CLP_LPS_PCI); 2468c2ecf20Sopenharmony_ci if (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY) { 2478c2ecf20Sopenharmony_ci retries--; 2488c2ecf20Sopenharmony_ci if (retries < 0) 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci msleep(20); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci } while (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) { 2558c2ecf20Sopenharmony_ci zdev->fh = rrb->response.fh; 2568c2ecf20Sopenharmony_ci } else if (!rc && rrb->response.hdr.rsp == CLP_RC_SETPCIFN_ALRDY) { 2578c2ecf20Sopenharmony_ci /* Function is already in desired state - update handle */ 2588c2ecf20Sopenharmony_ci rc = clp_refresh_fh(zdev->fid); 2598c2ecf20Sopenharmony_ci } else { 2608c2ecf20Sopenharmony_ci zpci_err("Set PCI FN:\n"); 2618c2ecf20Sopenharmony_ci zpci_err_clp(rrb->response.hdr.rsp, rc); 2628c2ecf20Sopenharmony_ci if (!rc) 2638c2ecf20Sopenharmony_ci rc = rrb->response.hdr.rsp; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci clp_free_block(rrb); 2668c2ecf20Sopenharmony_ci return rc; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ciint clp_setup_writeback_mio(void) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct clp_req_rsp_slpc_pci *rrb; 2728c2ecf20Sopenharmony_ci u8 wb_bit_pos; 2738c2ecf20Sopenharmony_ci int rc; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci rrb = clp_alloc_block(GFP_KERNEL); 2768c2ecf20Sopenharmony_ci if (!rrb) 2778c2ecf20Sopenharmony_ci return -ENOMEM; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci memset(rrb, 0, sizeof(*rrb)); 2808c2ecf20Sopenharmony_ci rrb->request.hdr.len = sizeof(rrb->request); 2818c2ecf20Sopenharmony_ci rrb->request.hdr.cmd = CLP_SLPC; 2828c2ecf20Sopenharmony_ci rrb->response.hdr.len = sizeof(rrb->response); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci rc = clp_req(rrb, CLP_LPS_PCI); 2858c2ecf20Sopenharmony_ci if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) { 2868c2ecf20Sopenharmony_ci if (rrb->response.vwb) { 2878c2ecf20Sopenharmony_ci wb_bit_pos = rrb->response.mio_wb; 2888c2ecf20Sopenharmony_ci set_bit_inv(wb_bit_pos, &mio_wb_bit_mask); 2898c2ecf20Sopenharmony_ci zpci_dbg(3, "wb bit: %d\n", wb_bit_pos); 2908c2ecf20Sopenharmony_ci } else { 2918c2ecf20Sopenharmony_ci zpci_dbg(3, "wb bit: n.a.\n"); 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci } else { 2958c2ecf20Sopenharmony_ci zpci_err("SLPC PCI:\n"); 2968c2ecf20Sopenharmony_ci zpci_err_clp(rrb->response.hdr.rsp, rc); 2978c2ecf20Sopenharmony_ci rc = -EIO; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci clp_free_block(rrb); 3008c2ecf20Sopenharmony_ci return rc; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ciint clp_enable_fh(struct zpci_dev *zdev, u8 nr_dma_as) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci int rc; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci rc = clp_set_pci_fn(zdev, nr_dma_as, CLP_SET_ENABLE_PCI_FN); 3088c2ecf20Sopenharmony_ci zpci_dbg(3, "ena fid:%x, fh:%x, rc:%d\n", zdev->fid, zdev->fh, rc); 3098c2ecf20Sopenharmony_ci if (!rc && zpci_use_mio(zdev)) { 3108c2ecf20Sopenharmony_ci rc = clp_set_pci_fn(zdev, nr_dma_as, CLP_SET_ENABLE_MIO); 3118c2ecf20Sopenharmony_ci zpci_dbg(3, "ena mio fid:%x, fh:%x, rc:%d\n", 3128c2ecf20Sopenharmony_ci zdev->fid, zdev->fh, rc); 3138c2ecf20Sopenharmony_ci if (rc) 3148c2ecf20Sopenharmony_ci clp_disable_fh(zdev); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci return rc; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ciint clp_disable_fh(struct zpci_dev *zdev) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci int rc; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (!zdev_enabled(zdev)) 3248c2ecf20Sopenharmony_ci return 0; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci rc = clp_set_pci_fn(zdev, 0, CLP_SET_DISABLE_PCI_FN); 3278c2ecf20Sopenharmony_ci zpci_dbg(3, "dis fid:%x, fh:%x, rc:%d\n", zdev->fid, zdev->fh, rc); 3288c2ecf20Sopenharmony_ci return rc; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int clp_list_pci(struct clp_req_rsp_list_pci *rrb, void *data, 3328c2ecf20Sopenharmony_ci void (*cb)(struct clp_fh_list_entry *, void *)) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci u64 resume_token = 0; 3358c2ecf20Sopenharmony_ci int entries, i, rc; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci do { 3388c2ecf20Sopenharmony_ci memset(rrb, 0, sizeof(*rrb)); 3398c2ecf20Sopenharmony_ci rrb->request.hdr.len = sizeof(rrb->request); 3408c2ecf20Sopenharmony_ci rrb->request.hdr.cmd = CLP_LIST_PCI; 3418c2ecf20Sopenharmony_ci /* store as many entries as possible */ 3428c2ecf20Sopenharmony_ci rrb->response.hdr.len = CLP_BLK_SIZE - LIST_PCI_HDR_LEN; 3438c2ecf20Sopenharmony_ci rrb->request.resume_token = resume_token; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* Get PCI function handle list */ 3468c2ecf20Sopenharmony_ci rc = clp_req(rrb, CLP_LPS_PCI); 3478c2ecf20Sopenharmony_ci if (rc || rrb->response.hdr.rsp != CLP_RC_OK) { 3488c2ecf20Sopenharmony_ci zpci_err("List PCI FN:\n"); 3498c2ecf20Sopenharmony_ci zpci_err_clp(rrb->response.hdr.rsp, rc); 3508c2ecf20Sopenharmony_ci rc = -EIO; 3518c2ecf20Sopenharmony_ci goto out; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci update_uid_checking(rrb->response.uid_checking); 3558c2ecf20Sopenharmony_ci WARN_ON_ONCE(rrb->response.entry_size != 3568c2ecf20Sopenharmony_ci sizeof(struct clp_fh_list_entry)); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci entries = (rrb->response.hdr.len - LIST_PCI_HDR_LEN) / 3598c2ecf20Sopenharmony_ci rrb->response.entry_size; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci resume_token = rrb->response.resume_token; 3628c2ecf20Sopenharmony_ci for (i = 0; i < entries; i++) 3638c2ecf20Sopenharmony_ci cb(&rrb->response.fh_list[i], data); 3648c2ecf20Sopenharmony_ci } while (resume_token); 3658c2ecf20Sopenharmony_ciout: 3668c2ecf20Sopenharmony_ci return rc; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic void __clp_add(struct clp_fh_list_entry *entry, void *data) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci struct zpci_dev *zdev; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (!entry->vendor_id) 3748c2ecf20Sopenharmony_ci return; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci zdev = get_zdev_by_fid(entry->fid); 3778c2ecf20Sopenharmony_ci if (zdev) { 3788c2ecf20Sopenharmony_ci zpci_zdev_put(zdev); 3798c2ecf20Sopenharmony_ci return; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci zpci_create_device(entry->fid, entry->fh, entry->config_state); 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ciint clp_scan_pci_devices(void) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct clp_req_rsp_list_pci *rrb; 3878c2ecf20Sopenharmony_ci int rc; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci rrb = clp_alloc_block(GFP_KERNEL); 3908c2ecf20Sopenharmony_ci if (!rrb) 3918c2ecf20Sopenharmony_ci return -ENOMEM; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci rc = clp_list_pci(rrb, NULL, __clp_add); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci clp_free_block(rrb); 3968c2ecf20Sopenharmony_ci return rc; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic void __clp_refresh_fh(struct clp_fh_list_entry *entry, void *data) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct zpci_dev *zdev; 4028c2ecf20Sopenharmony_ci u32 fid = *((u32 *)data); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (!entry->vendor_id || fid != entry->fid) 4058c2ecf20Sopenharmony_ci return; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci zdev = get_zdev_by_fid(fid); 4088c2ecf20Sopenharmony_ci if (!zdev) 4098c2ecf20Sopenharmony_ci return; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci zdev->fh = entry->fh; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci/* 4158c2ecf20Sopenharmony_ci * Refresh the function handle of the function matching @fid 4168c2ecf20Sopenharmony_ci */ 4178c2ecf20Sopenharmony_cistatic int clp_refresh_fh(u32 fid) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct clp_req_rsp_list_pci *rrb; 4208c2ecf20Sopenharmony_ci int rc; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci rrb = clp_alloc_block(GFP_NOWAIT); 4238c2ecf20Sopenharmony_ci if (!rrb) 4248c2ecf20Sopenharmony_ci return -ENOMEM; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci rc = clp_list_pci(rrb, &fid, __clp_refresh_fh); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci clp_free_block(rrb); 4298c2ecf20Sopenharmony_ci return rc; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistruct clp_state_data { 4338c2ecf20Sopenharmony_ci u32 fid; 4348c2ecf20Sopenharmony_ci enum zpci_state state; 4358c2ecf20Sopenharmony_ci}; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic void __clp_get_state(struct clp_fh_list_entry *entry, void *data) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct clp_state_data *sd = data; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (entry->fid != sd->fid) 4428c2ecf20Sopenharmony_ci return; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci sd->state = entry->config_state; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ciint clp_get_state(u32 fid, enum zpci_state *state) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct clp_req_rsp_list_pci *rrb; 4508c2ecf20Sopenharmony_ci struct clp_state_data sd = {fid, ZPCI_FN_STATE_RESERVED}; 4518c2ecf20Sopenharmony_ci int rc; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci rrb = clp_alloc_block(GFP_ATOMIC); 4548c2ecf20Sopenharmony_ci if (!rrb) 4558c2ecf20Sopenharmony_ci return -ENOMEM; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci rc = clp_list_pci(rrb, &sd, __clp_get_state); 4588c2ecf20Sopenharmony_ci if (!rc) 4598c2ecf20Sopenharmony_ci *state = sd.state; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci clp_free_block(rrb); 4628c2ecf20Sopenharmony_ci return rc; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic int clp_base_slpc(struct clp_req *req, struct clp_req_rsp_slpc *lpcb) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (lpcb->request.hdr.len != sizeof(lpcb->request) || 4708c2ecf20Sopenharmony_ci lpcb->response.hdr.len > limit) 4718c2ecf20Sopenharmony_ci return -EINVAL; 4728c2ecf20Sopenharmony_ci return clp_req(lpcb, CLP_LPS_BASE) ? -EOPNOTSUPP : 0; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int clp_base_command(struct clp_req *req, struct clp_req_hdr *lpcb) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci switch (lpcb->cmd) { 4788c2ecf20Sopenharmony_ci case 0x0001: /* store logical-processor characteristics */ 4798c2ecf20Sopenharmony_ci return clp_base_slpc(req, (void *) lpcb); 4808c2ecf20Sopenharmony_ci default: 4818c2ecf20Sopenharmony_ci return -EINVAL; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic int clp_pci_slpc(struct clp_req *req, struct clp_req_rsp_slpc_pci *lpcb) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (lpcb->request.hdr.len != sizeof(lpcb->request) || 4908c2ecf20Sopenharmony_ci lpcb->response.hdr.len > limit) 4918c2ecf20Sopenharmony_ci return -EINVAL; 4928c2ecf20Sopenharmony_ci return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic int clp_pci_list(struct clp_req *req, struct clp_req_rsp_list_pci *lpcb) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (lpcb->request.hdr.len != sizeof(lpcb->request) || 5008c2ecf20Sopenharmony_ci lpcb->response.hdr.len > limit) 5018c2ecf20Sopenharmony_ci return -EINVAL; 5028c2ecf20Sopenharmony_ci if (lpcb->request.reserved2 != 0) 5038c2ecf20Sopenharmony_ci return -EINVAL; 5048c2ecf20Sopenharmony_ci return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int clp_pci_query(struct clp_req *req, 5088c2ecf20Sopenharmony_ci struct clp_req_rsp_query_pci *lpcb) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (lpcb->request.hdr.len != sizeof(lpcb->request) || 5138c2ecf20Sopenharmony_ci lpcb->response.hdr.len > limit) 5148c2ecf20Sopenharmony_ci return -EINVAL; 5158c2ecf20Sopenharmony_ci if (lpcb->request.reserved2 != 0 || lpcb->request.reserved3 != 0) 5168c2ecf20Sopenharmony_ci return -EINVAL; 5178c2ecf20Sopenharmony_ci return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic int clp_pci_query_grp(struct clp_req *req, 5218c2ecf20Sopenharmony_ci struct clp_req_rsp_query_pci_grp *lpcb) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (lpcb->request.hdr.len != sizeof(lpcb->request) || 5268c2ecf20Sopenharmony_ci lpcb->response.hdr.len > limit) 5278c2ecf20Sopenharmony_ci return -EINVAL; 5288c2ecf20Sopenharmony_ci if (lpcb->request.reserved2 != 0 || lpcb->request.reserved3 != 0 || 5298c2ecf20Sopenharmony_ci lpcb->request.reserved4 != 0) 5308c2ecf20Sopenharmony_ci return -EINVAL; 5318c2ecf20Sopenharmony_ci return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int clp_pci_command(struct clp_req *req, struct clp_req_hdr *lpcb) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci switch (lpcb->cmd) { 5378c2ecf20Sopenharmony_ci case 0x0001: /* store logical-processor characteristics */ 5388c2ecf20Sopenharmony_ci return clp_pci_slpc(req, (void *) lpcb); 5398c2ecf20Sopenharmony_ci case 0x0002: /* list PCI functions */ 5408c2ecf20Sopenharmony_ci return clp_pci_list(req, (void *) lpcb); 5418c2ecf20Sopenharmony_ci case 0x0003: /* query PCI function */ 5428c2ecf20Sopenharmony_ci return clp_pci_query(req, (void *) lpcb); 5438c2ecf20Sopenharmony_ci case 0x0004: /* query PCI function group */ 5448c2ecf20Sopenharmony_ci return clp_pci_query_grp(req, (void *) lpcb); 5458c2ecf20Sopenharmony_ci default: 5468c2ecf20Sopenharmony_ci return -EINVAL; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic int clp_normal_command(struct clp_req *req) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct clp_req_hdr *lpcb; 5538c2ecf20Sopenharmony_ci void __user *uptr; 5548c2ecf20Sopenharmony_ci int rc; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci rc = -EINVAL; 5578c2ecf20Sopenharmony_ci if (req->lps != 0 && req->lps != 2) 5588c2ecf20Sopenharmony_ci goto out; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci rc = -ENOMEM; 5618c2ecf20Sopenharmony_ci lpcb = clp_alloc_block(GFP_KERNEL); 5628c2ecf20Sopenharmony_ci if (!lpcb) 5638c2ecf20Sopenharmony_ci goto out; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci rc = -EFAULT; 5668c2ecf20Sopenharmony_ci uptr = (void __force __user *)(unsigned long) req->data_p; 5678c2ecf20Sopenharmony_ci if (copy_from_user(lpcb, uptr, PAGE_SIZE) != 0) 5688c2ecf20Sopenharmony_ci goto out_free; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci rc = -EINVAL; 5718c2ecf20Sopenharmony_ci if (lpcb->fmt != 0 || lpcb->reserved1 != 0 || lpcb->reserved2 != 0) 5728c2ecf20Sopenharmony_ci goto out_free; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci switch (req->lps) { 5758c2ecf20Sopenharmony_ci case 0: 5768c2ecf20Sopenharmony_ci rc = clp_base_command(req, lpcb); 5778c2ecf20Sopenharmony_ci break; 5788c2ecf20Sopenharmony_ci case 2: 5798c2ecf20Sopenharmony_ci rc = clp_pci_command(req, lpcb); 5808c2ecf20Sopenharmony_ci break; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci if (rc) 5838c2ecf20Sopenharmony_ci goto out_free; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci rc = -EFAULT; 5868c2ecf20Sopenharmony_ci if (copy_to_user(uptr, lpcb, PAGE_SIZE) != 0) 5878c2ecf20Sopenharmony_ci goto out_free; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci rc = 0; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ciout_free: 5928c2ecf20Sopenharmony_ci clp_free_block(lpcb); 5938c2ecf20Sopenharmony_ciout: 5948c2ecf20Sopenharmony_ci return rc; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic int clp_immediate_command(struct clp_req *req) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci void __user *uptr; 6008c2ecf20Sopenharmony_ci unsigned long ilp; 6018c2ecf20Sopenharmony_ci int exists; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (req->cmd > 1 || clp_get_ilp(&ilp) != 0) 6048c2ecf20Sopenharmony_ci return -EINVAL; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci uptr = (void __force __user *)(unsigned long) req->data_p; 6078c2ecf20Sopenharmony_ci if (req->cmd == 0) { 6088c2ecf20Sopenharmony_ci /* Command code 0: test for a specific processor */ 6098c2ecf20Sopenharmony_ci exists = test_bit_inv(req->lps, &ilp); 6108c2ecf20Sopenharmony_ci return put_user(exists, (int __user *) uptr); 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci /* Command code 1: return bit mask of installed processors */ 6138c2ecf20Sopenharmony_ci return put_user(ilp, (unsigned long __user *) uptr); 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic long clp_misc_ioctl(struct file *filp, unsigned int cmd, 6178c2ecf20Sopenharmony_ci unsigned long arg) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct clp_req req; 6208c2ecf20Sopenharmony_ci void __user *argp; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci if (cmd != CLP_SYNC) 6238c2ecf20Sopenharmony_ci return -EINVAL; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci argp = is_compat_task() ? compat_ptr(arg) : (void __user *) arg; 6268c2ecf20Sopenharmony_ci if (copy_from_user(&req, argp, sizeof(req))) 6278c2ecf20Sopenharmony_ci return -EFAULT; 6288c2ecf20Sopenharmony_ci if (req.r != 0) 6298c2ecf20Sopenharmony_ci return -EINVAL; 6308c2ecf20Sopenharmony_ci return req.c ? clp_immediate_command(&req) : clp_normal_command(&req); 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic int clp_misc_release(struct inode *inode, struct file *filp) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci return 0; 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_cistatic const struct file_operations clp_misc_fops = { 6398c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6408c2ecf20Sopenharmony_ci .open = nonseekable_open, 6418c2ecf20Sopenharmony_ci .release = clp_misc_release, 6428c2ecf20Sopenharmony_ci .unlocked_ioctl = clp_misc_ioctl, 6438c2ecf20Sopenharmony_ci .compat_ioctl = clp_misc_ioctl, 6448c2ecf20Sopenharmony_ci .llseek = no_llseek, 6458c2ecf20Sopenharmony_ci}; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic struct miscdevice clp_misc_device = { 6488c2ecf20Sopenharmony_ci .minor = MISC_DYNAMIC_MINOR, 6498c2ecf20Sopenharmony_ci .name = "clp", 6508c2ecf20Sopenharmony_ci .fops = &clp_misc_fops, 6518c2ecf20Sopenharmony_ci}; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic int __init clp_misc_init(void) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci return misc_register(&clp_misc_device); 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cidevice_initcall(clp_misc_init); 659