162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PCI I/O adapter configuration related functions. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2016 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#define KMSG_COMPONENT "sclp_cmd" 862306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/completion.h> 1162306a36Sopenharmony_ci#include <linux/export.h> 1262306a36Sopenharmony_ci#include <linux/mutex.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/err.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <asm/sclp.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "sclp.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define SCLP_CMDW_CONFIGURE_PCI 0x001a0001 2362306a36Sopenharmony_ci#define SCLP_CMDW_DECONFIGURE_PCI 0x001b0001 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define SCLP_ATYPE_PCI 2 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define SCLP_ERRNOTIFY_AQ_RESET 0 2862306a36Sopenharmony_ci#define SCLP_ERRNOTIFY_AQ_REPAIR 1 2962306a36Sopenharmony_ci#define SCLP_ERRNOTIFY_AQ_INFO_LOG 2 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic DEFINE_MUTEX(sclp_pci_mutex); 3262306a36Sopenharmony_cistatic struct sclp_register sclp_pci_event = { 3362306a36Sopenharmony_ci .send_mask = EVTYP_ERRNOTIFY_MASK, 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct err_notify_evbuf { 3762306a36Sopenharmony_ci struct evbuf_header header; 3862306a36Sopenharmony_ci u8 action; 3962306a36Sopenharmony_ci u8 atype; 4062306a36Sopenharmony_ci u32 fh; 4162306a36Sopenharmony_ci u32 fid; 4262306a36Sopenharmony_ci u8 data[]; 4362306a36Sopenharmony_ci} __packed; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct err_notify_sccb { 4662306a36Sopenharmony_ci struct sccb_header header; 4762306a36Sopenharmony_ci struct err_notify_evbuf evbuf; 4862306a36Sopenharmony_ci} __packed; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct pci_cfg_sccb { 5162306a36Sopenharmony_ci struct sccb_header header; 5262306a36Sopenharmony_ci u8 atype; /* adapter type */ 5362306a36Sopenharmony_ci u8 reserved1; 5462306a36Sopenharmony_ci u16 reserved2; 5562306a36Sopenharmony_ci u32 aid; /* adapter identifier */ 5662306a36Sopenharmony_ci} __packed; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int do_pci_configure(sclp_cmdw_t cmd, u32 fid) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct pci_cfg_sccb *sccb; 6162306a36Sopenharmony_ci int rc; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (!SCLP_HAS_PCI_RECONFIG) 6462306a36Sopenharmony_ci return -EOPNOTSUPP; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci sccb = (struct pci_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 6762306a36Sopenharmony_ci if (!sccb) 6862306a36Sopenharmony_ci return -ENOMEM; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci sccb->header.length = PAGE_SIZE; 7162306a36Sopenharmony_ci sccb->atype = SCLP_ATYPE_PCI; 7262306a36Sopenharmony_ci sccb->aid = fid; 7362306a36Sopenharmony_ci rc = sclp_sync_request(cmd, sccb); 7462306a36Sopenharmony_ci if (rc) 7562306a36Sopenharmony_ci goto out; 7662306a36Sopenharmony_ci switch (sccb->header.response_code) { 7762306a36Sopenharmony_ci case 0x0020: 7862306a36Sopenharmony_ci case 0x0120: 7962306a36Sopenharmony_ci break; 8062306a36Sopenharmony_ci default: 8162306a36Sopenharmony_ci pr_warn("configure PCI I/O adapter failed: cmd=0x%08x response=0x%04x\n", 8262306a36Sopenharmony_ci cmd, sccb->header.response_code); 8362306a36Sopenharmony_ci rc = -EIO; 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ciout: 8762306a36Sopenharmony_ci free_page((unsigned long) sccb); 8862306a36Sopenharmony_ci return rc; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ciint sclp_pci_configure(u32 fid) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci return do_pci_configure(SCLP_CMDW_CONFIGURE_PCI, fid); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ciEXPORT_SYMBOL(sclp_pci_configure); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ciint sclp_pci_deconfigure(u32 fid) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci return do_pci_configure(SCLP_CMDW_DECONFIGURE_PCI, fid); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ciEXPORT_SYMBOL(sclp_pci_deconfigure); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void sclp_pci_callback(struct sclp_req *req, void *data) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct completion *completion = data; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci complete(completion); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int sclp_pci_check_report(struct zpci_report_error_header *report) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci if (report->version != 1) 11362306a36Sopenharmony_ci return -EINVAL; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci switch (report->action) { 11662306a36Sopenharmony_ci case SCLP_ERRNOTIFY_AQ_RESET: 11762306a36Sopenharmony_ci case SCLP_ERRNOTIFY_AQ_REPAIR: 11862306a36Sopenharmony_ci case SCLP_ERRNOTIFY_AQ_INFO_LOG: 11962306a36Sopenharmony_ci break; 12062306a36Sopenharmony_ci default: 12162306a36Sopenharmony_ci return -EINVAL; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (report->length > (PAGE_SIZE - sizeof(struct err_notify_sccb))) 12562306a36Sopenharmony_ci return -EINVAL; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciint sclp_pci_report(struct zpci_report_error_header *report, u32 fh, u32 fid) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(completion); 13362306a36Sopenharmony_ci struct err_notify_sccb *sccb; 13462306a36Sopenharmony_ci struct sclp_req req; 13562306a36Sopenharmony_ci int ret; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci ret = sclp_pci_check_report(report); 13862306a36Sopenharmony_ci if (ret) 13962306a36Sopenharmony_ci return ret; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci mutex_lock(&sclp_pci_mutex); 14262306a36Sopenharmony_ci ret = sclp_register(&sclp_pci_event); 14362306a36Sopenharmony_ci if (ret) 14462306a36Sopenharmony_ci goto out_unlock; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (!(sclp_pci_event.sclp_receive_mask & EVTYP_ERRNOTIFY_MASK)) { 14762306a36Sopenharmony_ci ret = -EOPNOTSUPP; 14862306a36Sopenharmony_ci goto out_unregister; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 15262306a36Sopenharmony_ci if (!sccb) { 15362306a36Sopenharmony_ci ret = -ENOMEM; 15462306a36Sopenharmony_ci goto out_unregister; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci memset(&req, 0, sizeof(req)); 15862306a36Sopenharmony_ci req.callback_data = &completion; 15962306a36Sopenharmony_ci req.callback = sclp_pci_callback; 16062306a36Sopenharmony_ci req.command = SCLP_CMDW_WRITE_EVENT_DATA; 16162306a36Sopenharmony_ci req.status = SCLP_REQ_FILLED; 16262306a36Sopenharmony_ci req.sccb = sccb; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci sccb->evbuf.header.length = sizeof(sccb->evbuf) + report->length; 16562306a36Sopenharmony_ci sccb->evbuf.header.type = EVTYP_ERRNOTIFY; 16662306a36Sopenharmony_ci sccb->header.length = sizeof(sccb->header) + sccb->evbuf.header.length; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci sccb->evbuf.action = report->action; 16962306a36Sopenharmony_ci sccb->evbuf.atype = SCLP_ATYPE_PCI; 17062306a36Sopenharmony_ci sccb->evbuf.fh = fh; 17162306a36Sopenharmony_ci sccb->evbuf.fid = fid; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci memcpy(sccb->evbuf.data, report->data, report->length); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci ret = sclp_add_request(&req); 17662306a36Sopenharmony_ci if (ret) 17762306a36Sopenharmony_ci goto out_free_req; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci wait_for_completion(&completion); 18062306a36Sopenharmony_ci if (req.status != SCLP_REQ_DONE) { 18162306a36Sopenharmony_ci pr_warn("request failed (status=0x%02x)\n", 18262306a36Sopenharmony_ci req.status); 18362306a36Sopenharmony_ci ret = -EIO; 18462306a36Sopenharmony_ci goto out_free_req; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (sccb->header.response_code != 0x0020) { 18862306a36Sopenharmony_ci pr_warn("request failed with response code 0x%x\n", 18962306a36Sopenharmony_ci sccb->header.response_code); 19062306a36Sopenharmony_ci ret = -EIO; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ciout_free_req: 19462306a36Sopenharmony_ci free_page((unsigned long) sccb); 19562306a36Sopenharmony_ciout_unregister: 19662306a36Sopenharmony_ci sclp_unregister(&sclp_pci_event); 19762306a36Sopenharmony_ciout_unlock: 19862306a36Sopenharmony_ci mutex_unlock(&sclp_pci_mutex); 19962306a36Sopenharmony_ci return ret; 20062306a36Sopenharmony_ci} 201