18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PCI I/O adapter configuration related functions. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2016 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "sclp_cmd" 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/completion.h> 118c2ecf20Sopenharmony_ci#include <linux/export.h> 128c2ecf20Sopenharmony_ci#include <linux/mutex.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <asm/sclp.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "sclp.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define SCLP_CMDW_CONFIGURE_PCI 0x001a0001 238c2ecf20Sopenharmony_ci#define SCLP_CMDW_DECONFIGURE_PCI 0x001b0001 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define SCLP_ATYPE_PCI 2 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define SCLP_ERRNOTIFY_AQ_RESET 0 288c2ecf20Sopenharmony_ci#define SCLP_ERRNOTIFY_AQ_REPAIR 1 298c2ecf20Sopenharmony_ci#define SCLP_ERRNOTIFY_AQ_INFO_LOG 2 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(sclp_pci_mutex); 328c2ecf20Sopenharmony_cistatic struct sclp_register sclp_pci_event = { 338c2ecf20Sopenharmony_ci .send_mask = EVTYP_ERRNOTIFY_MASK, 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct err_notify_evbuf { 378c2ecf20Sopenharmony_ci struct evbuf_header header; 388c2ecf20Sopenharmony_ci u8 action; 398c2ecf20Sopenharmony_ci u8 atype; 408c2ecf20Sopenharmony_ci u32 fh; 418c2ecf20Sopenharmony_ci u32 fid; 428c2ecf20Sopenharmony_ci u8 data[]; 438c2ecf20Sopenharmony_ci} __packed; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistruct err_notify_sccb { 468c2ecf20Sopenharmony_ci struct sccb_header header; 478c2ecf20Sopenharmony_ci struct err_notify_evbuf evbuf; 488c2ecf20Sopenharmony_ci} __packed; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct pci_cfg_sccb { 518c2ecf20Sopenharmony_ci struct sccb_header header; 528c2ecf20Sopenharmony_ci u8 atype; /* adapter type */ 538c2ecf20Sopenharmony_ci u8 reserved1; 548c2ecf20Sopenharmony_ci u16 reserved2; 558c2ecf20Sopenharmony_ci u32 aid; /* adapter identifier */ 568c2ecf20Sopenharmony_ci} __packed; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int do_pci_configure(sclp_cmdw_t cmd, u32 fid) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct pci_cfg_sccb *sccb; 618c2ecf20Sopenharmony_ci int rc; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (!SCLP_HAS_PCI_RECONFIG) 648c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci sccb = (struct pci_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 678c2ecf20Sopenharmony_ci if (!sccb) 688c2ecf20Sopenharmony_ci return -ENOMEM; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci sccb->header.length = PAGE_SIZE; 718c2ecf20Sopenharmony_ci sccb->atype = SCLP_ATYPE_PCI; 728c2ecf20Sopenharmony_ci sccb->aid = fid; 738c2ecf20Sopenharmony_ci rc = sclp_sync_request(cmd, sccb); 748c2ecf20Sopenharmony_ci if (rc) 758c2ecf20Sopenharmony_ci goto out; 768c2ecf20Sopenharmony_ci switch (sccb->header.response_code) { 778c2ecf20Sopenharmony_ci case 0x0020: 788c2ecf20Sopenharmony_ci case 0x0120: 798c2ecf20Sopenharmony_ci break; 808c2ecf20Sopenharmony_ci default: 818c2ecf20Sopenharmony_ci pr_warn("configure PCI I/O adapter failed: cmd=0x%08x response=0x%04x\n", 828c2ecf20Sopenharmony_ci cmd, sccb->header.response_code); 838c2ecf20Sopenharmony_ci rc = -EIO; 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ciout: 878c2ecf20Sopenharmony_ci free_page((unsigned long) sccb); 888c2ecf20Sopenharmony_ci return rc; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ciint sclp_pci_configure(u32 fid) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci return do_pci_configure(SCLP_CMDW_CONFIGURE_PCI, fid); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sclp_pci_configure); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ciint sclp_pci_deconfigure(u32 fid) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci return do_pci_configure(SCLP_CMDW_DECONFIGURE_PCI, fid); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sclp_pci_deconfigure); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic void sclp_pci_callback(struct sclp_req *req, void *data) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct completion *completion = data; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci complete(completion); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int sclp_pci_check_report(struct zpci_report_error_header *report) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci if (report->version != 1) 1138c2ecf20Sopenharmony_ci return -EINVAL; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci switch (report->action) { 1168c2ecf20Sopenharmony_ci case SCLP_ERRNOTIFY_AQ_RESET: 1178c2ecf20Sopenharmony_ci case SCLP_ERRNOTIFY_AQ_REPAIR: 1188c2ecf20Sopenharmony_ci case SCLP_ERRNOTIFY_AQ_INFO_LOG: 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci default: 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (report->length > (PAGE_SIZE - sizeof(struct err_notify_sccb))) 1258c2ecf20Sopenharmony_ci return -EINVAL; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ciint sclp_pci_report(struct zpci_report_error_header *report, u32 fh, u32 fid) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(completion); 1338c2ecf20Sopenharmony_ci struct err_notify_sccb *sccb; 1348c2ecf20Sopenharmony_ci struct sclp_req req; 1358c2ecf20Sopenharmony_ci int ret; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ret = sclp_pci_check_report(report); 1388c2ecf20Sopenharmony_ci if (ret) 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci mutex_lock(&sclp_pci_mutex); 1428c2ecf20Sopenharmony_ci ret = sclp_register(&sclp_pci_event); 1438c2ecf20Sopenharmony_ci if (ret) 1448c2ecf20Sopenharmony_ci goto out_unlock; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (!(sclp_pci_event.sclp_receive_mask & EVTYP_ERRNOTIFY_MASK)) { 1478c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 1488c2ecf20Sopenharmony_ci goto out_unregister; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 1528c2ecf20Sopenharmony_ci if (!sccb) { 1538c2ecf20Sopenharmony_ci ret = -ENOMEM; 1548c2ecf20Sopenharmony_ci goto out_unregister; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci memset(&req, 0, sizeof(req)); 1588c2ecf20Sopenharmony_ci req.callback_data = &completion; 1598c2ecf20Sopenharmony_ci req.callback = sclp_pci_callback; 1608c2ecf20Sopenharmony_ci req.command = SCLP_CMDW_WRITE_EVENT_DATA; 1618c2ecf20Sopenharmony_ci req.status = SCLP_REQ_FILLED; 1628c2ecf20Sopenharmony_ci req.sccb = sccb; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci sccb->evbuf.header.length = sizeof(sccb->evbuf) + report->length; 1658c2ecf20Sopenharmony_ci sccb->evbuf.header.type = EVTYP_ERRNOTIFY; 1668c2ecf20Sopenharmony_ci sccb->header.length = sizeof(sccb->header) + sccb->evbuf.header.length; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci sccb->evbuf.action = report->action; 1698c2ecf20Sopenharmony_ci sccb->evbuf.atype = SCLP_ATYPE_PCI; 1708c2ecf20Sopenharmony_ci sccb->evbuf.fh = fh; 1718c2ecf20Sopenharmony_ci sccb->evbuf.fid = fid; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci memcpy(sccb->evbuf.data, report->data, report->length); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci ret = sclp_add_request(&req); 1768c2ecf20Sopenharmony_ci if (ret) 1778c2ecf20Sopenharmony_ci goto out_free_req; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci wait_for_completion(&completion); 1808c2ecf20Sopenharmony_ci if (req.status != SCLP_REQ_DONE) { 1818c2ecf20Sopenharmony_ci pr_warn("request failed (status=0x%02x)\n", 1828c2ecf20Sopenharmony_ci req.status); 1838c2ecf20Sopenharmony_ci ret = -EIO; 1848c2ecf20Sopenharmony_ci goto out_free_req; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (sccb->header.response_code != 0x0020) { 1888c2ecf20Sopenharmony_ci pr_warn("request failed with response code 0x%x\n", 1898c2ecf20Sopenharmony_ci sccb->header.response_code); 1908c2ecf20Sopenharmony_ci ret = -EIO; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciout_free_req: 1948c2ecf20Sopenharmony_ci free_page((unsigned long) sccb); 1958c2ecf20Sopenharmony_ciout_unregister: 1968c2ecf20Sopenharmony_ci sclp_unregister(&sclp_pci_event); 1978c2ecf20Sopenharmony_ciout_unlock: 1988c2ecf20Sopenharmony_ci mutex_unlock(&sclp_pci_mutex); 1998c2ecf20Sopenharmony_ci return ret; 2008c2ecf20Sopenharmony_ci} 201