1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 268dd13d6SSebastian Ott /* 368dd13d6SSebastian Ott * PCI I/O adapter configuration related functions. 468dd13d6SSebastian Ott * 568dd13d6SSebastian Ott * Copyright IBM Corp. 2016 668dd13d6SSebastian Ott */ 768dd13d6SSebastian Ott #define KMSG_COMPONENT "sclp_cmd" 868dd13d6SSebastian Ott #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 968dd13d6SSebastian Ott 1068dd13d6SSebastian Ott #include <linux/completion.h> 1168dd13d6SSebastian Ott #include <linux/export.h> 1212283a40SSebastian Ott #include <linux/mutex.h> 1368dd13d6SSebastian Ott #include <linux/errno.h> 1468dd13d6SSebastian Ott #include <linux/slab.h> 1568dd13d6SSebastian Ott #include <linux/init.h> 1668dd13d6SSebastian Ott #include <linux/err.h> 1768dd13d6SSebastian Ott 1868dd13d6SSebastian Ott #include <asm/sclp.h> 1968dd13d6SSebastian Ott 2068dd13d6SSebastian Ott #include "sclp.h" 2168dd13d6SSebastian Ott 2268dd13d6SSebastian Ott #define SCLP_CMDW_CONFIGURE_PCI 0x001a0001 2368dd13d6SSebastian Ott #define SCLP_CMDW_DECONFIGURE_PCI 0x001b0001 2468dd13d6SSebastian Ott 2512283a40SSebastian Ott #define SCLP_ATYPE_PCI 2 2612283a40SSebastian Ott 276779df40SJan Höppner #define SCLP_ERRNOTIFY_AQ_RESET 0 2812283a40SSebastian Ott #define SCLP_ERRNOTIFY_AQ_REPAIR 1 2912283a40SSebastian Ott #define SCLP_ERRNOTIFY_AQ_INFO_LOG 2 3012283a40SSebastian Ott 3112283a40SSebastian Ott static DEFINE_MUTEX(sclp_pci_mutex); 3212283a40SSebastian Ott static struct sclp_register sclp_pci_event = { 3312283a40SSebastian Ott .send_mask = EVTYP_ERRNOTIFY_MASK, 3412283a40SSebastian Ott }; 3512283a40SSebastian Ott 3612283a40SSebastian Ott struct err_notify_evbuf { 3712283a40SSebastian Ott struct evbuf_header header; 3812283a40SSebastian Ott u8 action; 3912283a40SSebastian Ott u8 atype; 4012283a40SSebastian Ott u32 fh; 4112283a40SSebastian Ott u32 fid; 42*fa226f1dSGustavo A. R. Silva u8 data[]; 4312283a40SSebastian Ott } __packed; 4412283a40SSebastian Ott 4512283a40SSebastian Ott struct err_notify_sccb { 4612283a40SSebastian Ott struct sccb_header header; 4712283a40SSebastian Ott struct err_notify_evbuf evbuf; 4812283a40SSebastian Ott } __packed; 4968dd13d6SSebastian Ott 5068dd13d6SSebastian Ott struct pci_cfg_sccb { 5168dd13d6SSebastian Ott struct sccb_header header; 5268dd13d6SSebastian Ott u8 atype; /* adapter type */ 5368dd13d6SSebastian Ott u8 reserved1; 5468dd13d6SSebastian Ott u16 reserved2; 5568dd13d6SSebastian Ott u32 aid; /* adapter identifier */ 5668dd13d6SSebastian Ott } __packed; 5768dd13d6SSebastian Ott 5868dd13d6SSebastian Ott static int do_pci_configure(sclp_cmdw_t cmd, u32 fid) 5968dd13d6SSebastian Ott { 6068dd13d6SSebastian Ott struct pci_cfg_sccb *sccb; 6168dd13d6SSebastian Ott int rc; 6268dd13d6SSebastian Ott 6368dd13d6SSebastian Ott if (!SCLP_HAS_PCI_RECONFIG) 6468dd13d6SSebastian Ott return -EOPNOTSUPP; 6568dd13d6SSebastian Ott 6668dd13d6SSebastian Ott sccb = (struct pci_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 6768dd13d6SSebastian Ott if (!sccb) 6868dd13d6SSebastian Ott return -ENOMEM; 6968dd13d6SSebastian Ott 7068dd13d6SSebastian Ott sccb->header.length = PAGE_SIZE; 7112283a40SSebastian Ott sccb->atype = SCLP_ATYPE_PCI; 7268dd13d6SSebastian Ott sccb->aid = fid; 7368dd13d6SSebastian Ott rc = sclp_sync_request(cmd, sccb); 7468dd13d6SSebastian Ott if (rc) 7568dd13d6SSebastian Ott goto out; 7668dd13d6SSebastian Ott switch (sccb->header.response_code) { 7768dd13d6SSebastian Ott case 0x0020: 7868dd13d6SSebastian Ott case 0x0120: 7968dd13d6SSebastian Ott break; 8068dd13d6SSebastian Ott default: 8168dd13d6SSebastian Ott pr_warn("configure PCI I/O adapter failed: cmd=0x%08x response=0x%04x\n", 8268dd13d6SSebastian Ott cmd, sccb->header.response_code); 8368dd13d6SSebastian Ott rc = -EIO; 8468dd13d6SSebastian Ott break; 8568dd13d6SSebastian Ott } 8668dd13d6SSebastian Ott out: 8768dd13d6SSebastian Ott free_page((unsigned long) sccb); 8868dd13d6SSebastian Ott return rc; 8968dd13d6SSebastian Ott } 9068dd13d6SSebastian Ott 9168dd13d6SSebastian Ott int sclp_pci_configure(u32 fid) 9268dd13d6SSebastian Ott { 9368dd13d6SSebastian Ott return do_pci_configure(SCLP_CMDW_CONFIGURE_PCI, fid); 9468dd13d6SSebastian Ott } 9568dd13d6SSebastian Ott EXPORT_SYMBOL(sclp_pci_configure); 9668dd13d6SSebastian Ott 9768dd13d6SSebastian Ott int sclp_pci_deconfigure(u32 fid) 9868dd13d6SSebastian Ott { 9968dd13d6SSebastian Ott return do_pci_configure(SCLP_CMDW_DECONFIGURE_PCI, fid); 10068dd13d6SSebastian Ott } 10168dd13d6SSebastian Ott EXPORT_SYMBOL(sclp_pci_deconfigure); 10212283a40SSebastian Ott 10312283a40SSebastian Ott static void sclp_pci_callback(struct sclp_req *req, void *data) 10412283a40SSebastian Ott { 10512283a40SSebastian Ott struct completion *completion = data; 10612283a40SSebastian Ott 10712283a40SSebastian Ott complete(completion); 10812283a40SSebastian Ott } 10912283a40SSebastian Ott 11012283a40SSebastian Ott static int sclp_pci_check_report(struct zpci_report_error_header *report) 11112283a40SSebastian Ott { 11212283a40SSebastian Ott if (report->version != 1) 11312283a40SSebastian Ott return -EINVAL; 11412283a40SSebastian Ott 1156779df40SJan Höppner switch (report->action) { 1166779df40SJan Höppner case SCLP_ERRNOTIFY_AQ_RESET: 1176779df40SJan Höppner case SCLP_ERRNOTIFY_AQ_REPAIR: 1186779df40SJan Höppner case SCLP_ERRNOTIFY_AQ_INFO_LOG: 1196779df40SJan Höppner break; 1206779df40SJan Höppner default: 12112283a40SSebastian Ott return -EINVAL; 1226779df40SJan Höppner } 12312283a40SSebastian Ott 12412283a40SSebastian Ott if (report->length > (PAGE_SIZE - sizeof(struct err_notify_sccb))) 12512283a40SSebastian Ott return -EINVAL; 12612283a40SSebastian Ott 12712283a40SSebastian Ott return 0; 12812283a40SSebastian Ott } 12912283a40SSebastian Ott 13012283a40SSebastian Ott int sclp_pci_report(struct zpci_report_error_header *report, u32 fh, u32 fid) 13112283a40SSebastian Ott { 13212283a40SSebastian Ott DECLARE_COMPLETION_ONSTACK(completion); 13312283a40SSebastian Ott struct err_notify_sccb *sccb; 13499d00a2bSMartin Schwidefsky struct sclp_req req; 13512283a40SSebastian Ott int ret; 13612283a40SSebastian Ott 13712283a40SSebastian Ott ret = sclp_pci_check_report(report); 13812283a40SSebastian Ott if (ret) 13912283a40SSebastian Ott return ret; 14012283a40SSebastian Ott 14112283a40SSebastian Ott mutex_lock(&sclp_pci_mutex); 14212283a40SSebastian Ott ret = sclp_register(&sclp_pci_event); 14312283a40SSebastian Ott if (ret) 14412283a40SSebastian Ott goto out_unlock; 14512283a40SSebastian Ott 14612283a40SSebastian Ott if (!(sclp_pci_event.sclp_receive_mask & EVTYP_ERRNOTIFY_MASK)) { 14712283a40SSebastian Ott ret = -EOPNOTSUPP; 14812283a40SSebastian Ott goto out_unregister; 14912283a40SSebastian Ott } 15012283a40SSebastian Ott 15112283a40SSebastian Ott sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 15212283a40SSebastian Ott if (!sccb) { 15312283a40SSebastian Ott ret = -ENOMEM; 15412283a40SSebastian Ott goto out_unregister; 15512283a40SSebastian Ott } 15612283a40SSebastian Ott 15799d00a2bSMartin Schwidefsky memset(&req, 0, sizeof(req)); 15812283a40SSebastian Ott req.callback_data = &completion; 15912283a40SSebastian Ott req.callback = sclp_pci_callback; 16012283a40SSebastian Ott req.command = SCLP_CMDW_WRITE_EVENT_DATA; 16112283a40SSebastian Ott req.status = SCLP_REQ_FILLED; 16212283a40SSebastian Ott req.sccb = sccb; 16312283a40SSebastian Ott 16412283a40SSebastian Ott sccb->evbuf.header.length = sizeof(sccb->evbuf) + report->length; 16512283a40SSebastian Ott sccb->evbuf.header.type = EVTYP_ERRNOTIFY; 16612283a40SSebastian Ott sccb->header.length = sizeof(sccb->header) + sccb->evbuf.header.length; 16712283a40SSebastian Ott 16812283a40SSebastian Ott sccb->evbuf.action = report->action; 16912283a40SSebastian Ott sccb->evbuf.atype = SCLP_ATYPE_PCI; 17012283a40SSebastian Ott sccb->evbuf.fh = fh; 17112283a40SSebastian Ott sccb->evbuf.fid = fid; 17212283a40SSebastian Ott 17312283a40SSebastian Ott memcpy(sccb->evbuf.data, report->data, report->length); 17412283a40SSebastian Ott 17512283a40SSebastian Ott ret = sclp_add_request(&req); 17612283a40SSebastian Ott if (ret) 17712283a40SSebastian Ott goto out_free_req; 17812283a40SSebastian Ott 17912283a40SSebastian Ott wait_for_completion(&completion); 18012283a40SSebastian Ott if (req.status != SCLP_REQ_DONE) { 18112283a40SSebastian Ott pr_warn("request failed (status=0x%02x)\n", 18212283a40SSebastian Ott req.status); 18312283a40SSebastian Ott ret = -EIO; 18412283a40SSebastian Ott goto out_free_req; 18512283a40SSebastian Ott } 18612283a40SSebastian Ott 18712283a40SSebastian Ott if (sccb->header.response_code != 0x0020) { 18812283a40SSebastian Ott pr_warn("request failed with response code 0x%x\n", 18912283a40SSebastian Ott sccb->header.response_code); 19012283a40SSebastian Ott ret = -EIO; 19112283a40SSebastian Ott } 19212283a40SSebastian Ott 19312283a40SSebastian Ott out_free_req: 19412283a40SSebastian Ott free_page((unsigned long) sccb); 19512283a40SSebastian Ott out_unregister: 19612283a40SSebastian Ott sclp_unregister(&sclp_pci_event); 19712283a40SSebastian Ott out_unlock: 19812283a40SSebastian Ott mutex_unlock(&sclp_pci_mutex); 19912283a40SSebastian Ott return ret; 20012283a40SSebastian Ott } 201