18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * CCW device SENSE ID I/O handling. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2002, 2009 68c2ecf20Sopenharmony_ci * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> 78c2ecf20Sopenharmony_ci * Martin Schwidefsky <schwidefsky@de.ibm.com> 88c2ecf20Sopenharmony_ci * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/string.h> 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <asm/ccwdev.h> 168c2ecf20Sopenharmony_ci#include <asm/setup.h> 178c2ecf20Sopenharmony_ci#include <asm/cio.h> 188c2ecf20Sopenharmony_ci#include <asm/diag.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "cio.h" 218c2ecf20Sopenharmony_ci#include "cio_debug.h" 228c2ecf20Sopenharmony_ci#include "device.h" 238c2ecf20Sopenharmony_ci#include "io_sch.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define SENSE_ID_RETRIES 256 268c2ecf20Sopenharmony_ci#define SENSE_ID_TIMEOUT (10 * HZ) 278c2ecf20Sopenharmony_ci#define SENSE_ID_MIN_LEN 4 288c2ecf20Sopenharmony_ci#define SENSE_ID_BASIC_LEN 7 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/** 318c2ecf20Sopenharmony_ci * diag210_to_senseid - convert diag 0x210 data to sense id information 328c2ecf20Sopenharmony_ci * @senseid: sense id 338c2ecf20Sopenharmony_ci * @diag: diag 0x210 data 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Return 0 on success, non-zero otherwise. 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_cistatic int diag210_to_senseid(struct senseid *senseid, struct diag210 *diag) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci static struct { 408c2ecf20Sopenharmony_ci int class, type, cu_type; 418c2ecf20Sopenharmony_ci } vm_devices[] = { 428c2ecf20Sopenharmony_ci { 0x08, 0x01, 0x3480 }, 438c2ecf20Sopenharmony_ci { 0x08, 0x02, 0x3430 }, 448c2ecf20Sopenharmony_ci { 0x08, 0x10, 0x3420 }, 458c2ecf20Sopenharmony_ci { 0x08, 0x42, 0x3424 }, 468c2ecf20Sopenharmony_ci { 0x08, 0x44, 0x9348 }, 478c2ecf20Sopenharmony_ci { 0x08, 0x81, 0x3490 }, 488c2ecf20Sopenharmony_ci { 0x08, 0x82, 0x3422 }, 498c2ecf20Sopenharmony_ci { 0x10, 0x41, 0x1403 }, 508c2ecf20Sopenharmony_ci { 0x10, 0x42, 0x3211 }, 518c2ecf20Sopenharmony_ci { 0x10, 0x43, 0x3203 }, 528c2ecf20Sopenharmony_ci { 0x10, 0x45, 0x3800 }, 538c2ecf20Sopenharmony_ci { 0x10, 0x47, 0x3262 }, 548c2ecf20Sopenharmony_ci { 0x10, 0x48, 0x3820 }, 558c2ecf20Sopenharmony_ci { 0x10, 0x49, 0x3800 }, 568c2ecf20Sopenharmony_ci { 0x10, 0x4a, 0x4245 }, 578c2ecf20Sopenharmony_ci { 0x10, 0x4b, 0x4248 }, 588c2ecf20Sopenharmony_ci { 0x10, 0x4d, 0x3800 }, 598c2ecf20Sopenharmony_ci { 0x10, 0x4e, 0x3820 }, 608c2ecf20Sopenharmony_ci { 0x10, 0x4f, 0x3820 }, 618c2ecf20Sopenharmony_ci { 0x10, 0x82, 0x2540 }, 628c2ecf20Sopenharmony_ci { 0x10, 0x84, 0x3525 }, 638c2ecf20Sopenharmony_ci { 0x20, 0x81, 0x2501 }, 648c2ecf20Sopenharmony_ci { 0x20, 0x82, 0x2540 }, 658c2ecf20Sopenharmony_ci { 0x20, 0x84, 0x3505 }, 668c2ecf20Sopenharmony_ci { 0x40, 0x01, 0x3278 }, 678c2ecf20Sopenharmony_ci { 0x40, 0x04, 0x3277 }, 688c2ecf20Sopenharmony_ci { 0x40, 0x80, 0x2250 }, 698c2ecf20Sopenharmony_ci { 0x40, 0xc0, 0x5080 }, 708c2ecf20Sopenharmony_ci { 0x80, 0x00, 0x3215 }, 718c2ecf20Sopenharmony_ci }; 728c2ecf20Sopenharmony_ci int i; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* Special case for osa devices. */ 758c2ecf20Sopenharmony_ci if (diag->vrdcvcla == 0x02 && diag->vrdcvtyp == 0x20) { 768c2ecf20Sopenharmony_ci senseid->cu_type = 0x3088; 778c2ecf20Sopenharmony_ci senseid->cu_model = 0x60; 788c2ecf20Sopenharmony_ci senseid->reserved = 0xff; 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vm_devices); i++) { 828c2ecf20Sopenharmony_ci if (diag->vrdcvcla == vm_devices[i].class && 838c2ecf20Sopenharmony_ci diag->vrdcvtyp == vm_devices[i].type) { 848c2ecf20Sopenharmony_ci senseid->cu_type = vm_devices[i].cu_type; 858c2ecf20Sopenharmony_ci senseid->reserved = 0xff; 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return -ENODEV; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/** 948c2ecf20Sopenharmony_ci * diag_get_dev_info - retrieve device information via diag 0x210 958c2ecf20Sopenharmony_ci * @cdev: ccw device 968c2ecf20Sopenharmony_ci * 978c2ecf20Sopenharmony_ci * Returns zero on success, non-zero otherwise. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_cistatic int diag210_get_dev_info(struct ccw_device *cdev) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct ccw_dev_id *dev_id = &cdev->private->dev_id; 1028c2ecf20Sopenharmony_ci struct senseid *senseid = &cdev->private->dma_area->senseid; 1038c2ecf20Sopenharmony_ci struct diag210 diag_data; 1048c2ecf20Sopenharmony_ci int rc; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (dev_id->ssid != 0) 1078c2ecf20Sopenharmony_ci return -ENODEV; 1088c2ecf20Sopenharmony_ci memset(&diag_data, 0, sizeof(diag_data)); 1098c2ecf20Sopenharmony_ci diag_data.vrdcdvno = dev_id->devno; 1108c2ecf20Sopenharmony_ci diag_data.vrdclen = sizeof(diag_data); 1118c2ecf20Sopenharmony_ci rc = diag210(&diag_data); 1128c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(4, "diag210"); 1138c2ecf20Sopenharmony_ci CIO_HEX_EVENT(4, &rc, sizeof(rc)); 1148c2ecf20Sopenharmony_ci CIO_HEX_EVENT(4, &diag_data, sizeof(diag_data)); 1158c2ecf20Sopenharmony_ci if (rc != 0 && rc != 2) 1168c2ecf20Sopenharmony_ci goto err_failed; 1178c2ecf20Sopenharmony_ci if (diag210_to_senseid(senseid, &diag_data)) 1188c2ecf20Sopenharmony_ci goto err_unknown; 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cierr_unknown: 1228c2ecf20Sopenharmony_ci CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: unknown diag210 data\n", 1238c2ecf20Sopenharmony_ci dev_id->ssid, dev_id->devno); 1248c2ecf20Sopenharmony_ci return -ENODEV; 1258c2ecf20Sopenharmony_cierr_failed: 1268c2ecf20Sopenharmony_ci CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: diag210 failed (rc=%d)\n", 1278c2ecf20Sopenharmony_ci dev_id->ssid, dev_id->devno, rc); 1288c2ecf20Sopenharmony_ci return -ENODEV; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* 1328c2ecf20Sopenharmony_ci * Initialize SENSE ID data. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_cistatic void snsid_init(struct ccw_device *cdev) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci cdev->private->flags.esid = 0; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci memset(&cdev->private->dma_area->senseid, 0, 1398c2ecf20Sopenharmony_ci sizeof(cdev->private->dma_area->senseid)); 1408c2ecf20Sopenharmony_ci cdev->private->dma_area->senseid.cu_type = 0xffff; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* 1448c2ecf20Sopenharmony_ci * Check for complete SENSE ID data. 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_cistatic int snsid_check(struct ccw_device *cdev, void *data) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct cmd_scsw *scsw = &cdev->private->dma_area->irb.scsw.cmd; 1498c2ecf20Sopenharmony_ci int len = sizeof(struct senseid) - scsw->count; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Check for incomplete SENSE ID data. */ 1528c2ecf20Sopenharmony_ci if (len < SENSE_ID_MIN_LEN) 1538c2ecf20Sopenharmony_ci goto out_restart; 1548c2ecf20Sopenharmony_ci if (cdev->private->dma_area->senseid.cu_type == 0xffff) 1558c2ecf20Sopenharmony_ci goto out_restart; 1568c2ecf20Sopenharmony_ci /* Check for incompatible SENSE ID data. */ 1578c2ecf20Sopenharmony_ci if (cdev->private->dma_area->senseid.reserved != 0xff) 1588c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1598c2ecf20Sopenharmony_ci /* Check for extended-identification information. */ 1608c2ecf20Sopenharmony_ci if (len > SENSE_ID_BASIC_LEN) 1618c2ecf20Sopenharmony_ci cdev->private->flags.esid = 1; 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ciout_restart: 1658c2ecf20Sopenharmony_ci snsid_init(cdev); 1668c2ecf20Sopenharmony_ci return -EAGAIN; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* 1708c2ecf20Sopenharmony_ci * Process SENSE ID request result. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_cistatic void snsid_callback(struct ccw_device *cdev, void *data, int rc) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct ccw_dev_id *id = &cdev->private->dev_id; 1758c2ecf20Sopenharmony_ci struct senseid *senseid = &cdev->private->dma_area->senseid; 1768c2ecf20Sopenharmony_ci int vm = 0; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (rc && MACHINE_IS_VM) { 1798c2ecf20Sopenharmony_ci /* Try diag 0x210 fallback on z/VM. */ 1808c2ecf20Sopenharmony_ci snsid_init(cdev); 1818c2ecf20Sopenharmony_ci if (diag210_get_dev_info(cdev) == 0) { 1828c2ecf20Sopenharmony_ci rc = 0; 1838c2ecf20Sopenharmony_ci vm = 1; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci CIO_MSG_EVENT(2, "snsid: device 0.%x.%04x: rc=%d %04x/%02x " 1878c2ecf20Sopenharmony_ci "%04x/%02x%s\n", id->ssid, id->devno, rc, 1888c2ecf20Sopenharmony_ci senseid->cu_type, senseid->cu_model, senseid->dev_type, 1898c2ecf20Sopenharmony_ci senseid->dev_model, vm ? " (diag210)" : ""); 1908c2ecf20Sopenharmony_ci ccw_device_sense_id_done(cdev, rc); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci/** 1948c2ecf20Sopenharmony_ci * ccw_device_sense_id_start - perform SENSE ID 1958c2ecf20Sopenharmony_ci * @cdev: ccw device 1968c2ecf20Sopenharmony_ci * 1978c2ecf20Sopenharmony_ci * Execute a SENSE ID channel program on @cdev to update its sense id 1988c2ecf20Sopenharmony_ci * information. When finished, call ccw_device_sense_id_done with a 1998c2ecf20Sopenharmony_ci * return code specifying the result. 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_civoid ccw_device_sense_id_start(struct ccw_device *cdev) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 2048c2ecf20Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 2058c2ecf20Sopenharmony_ci struct ccw1 *cp = cdev->private->dma_area->iccws; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci CIO_TRACE_EVENT(4, "snsid"); 2088c2ecf20Sopenharmony_ci CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); 2098c2ecf20Sopenharmony_ci /* Data setup. */ 2108c2ecf20Sopenharmony_ci snsid_init(cdev); 2118c2ecf20Sopenharmony_ci /* Channel program setup. */ 2128c2ecf20Sopenharmony_ci cp->cmd_code = CCW_CMD_SENSE_ID; 2138c2ecf20Sopenharmony_ci cp->cda = (u32) (addr_t) &cdev->private->dma_area->senseid; 2148c2ecf20Sopenharmony_ci cp->count = sizeof(struct senseid); 2158c2ecf20Sopenharmony_ci cp->flags = CCW_FLAG_SLI; 2168c2ecf20Sopenharmony_ci /* Request setup. */ 2178c2ecf20Sopenharmony_ci memset(req, 0, sizeof(*req)); 2188c2ecf20Sopenharmony_ci req->cp = cp; 2198c2ecf20Sopenharmony_ci req->timeout = SENSE_ID_TIMEOUT; 2208c2ecf20Sopenharmony_ci req->maxretries = SENSE_ID_RETRIES; 2218c2ecf20Sopenharmony_ci req->lpm = sch->schib.pmcw.pam & sch->opm; 2228c2ecf20Sopenharmony_ci req->check = snsid_check; 2238c2ecf20Sopenharmony_ci req->callback = snsid_callback; 2248c2ecf20Sopenharmony_ci ccw_request_start(cdev); 2258c2ecf20Sopenharmony_ci} 226