1*b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 21da177e4SLinus Torvalds /* 339f5360bSPeter Oberparleiter * CCW device SENSE ID I/O handling. 41da177e4SLinus Torvalds * 539f5360bSPeter Oberparleiter * Copyright IBM Corp. 2002, 2009 639f5360bSPeter Oberparleiter * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> 739f5360bSPeter Oberparleiter * Martin Schwidefsky <schwidefsky@de.ibm.com> 839f5360bSPeter Oberparleiter * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> 91da177e4SLinus Torvalds */ 101da177e4SLinus Torvalds 11045236abSAhmed S. Darwish #include <linux/kernel.h> 1239f5360bSPeter Oberparleiter #include <linux/string.h> 1339f5360bSPeter Oberparleiter #include <linux/types.h> 1439f5360bSPeter Oberparleiter #include <linux/errno.h> 151da177e4SLinus Torvalds #include <asm/ccwdev.h> 1639f5360bSPeter Oberparleiter #include <asm/setup.h> 171da177e4SLinus Torvalds #include <asm/cio.h> 180a87c5cfSMichael Holzheu #include <asm/diag.h> 191da177e4SLinus Torvalds 201da177e4SLinus Torvalds #include "cio.h" 211da177e4SLinus Torvalds #include "cio_debug.h" 221da177e4SLinus Torvalds #include "device.h" 23cd6b4f27SCornelia Huck #include "io_sch.h" 241da177e4SLinus Torvalds 25de1b0438SPeter Oberparleiter #define SENSE_ID_RETRIES 256 2639f5360bSPeter Oberparleiter #define SENSE_ID_TIMEOUT (10 * HZ) 2739f5360bSPeter Oberparleiter #define SENSE_ID_MIN_LEN 4 2839f5360bSPeter Oberparleiter #define SENSE_ID_BASIC_LEN 7 2939f5360bSPeter Oberparleiter 306f52ac29SPeter Oberparleiter /** 3139f5360bSPeter Oberparleiter * diag210_to_senseid - convert diag 0x210 data to sense id information 3239f5360bSPeter Oberparleiter * @senseid: sense id 3339f5360bSPeter Oberparleiter * @diag: diag 0x210 data 346f52ac29SPeter Oberparleiter * 3539f5360bSPeter Oberparleiter * Return 0 on success, non-zero otherwise. 361da177e4SLinus Torvalds */ 3739f5360bSPeter Oberparleiter static int diag210_to_senseid(struct senseid *senseid, struct diag210 *diag) 381da177e4SLinus Torvalds { 391da177e4SLinus Torvalds static struct { 406f52ac29SPeter Oberparleiter int class, type, cu_type; 411da177e4SLinus Torvalds } vm_devices[] = { 421da177e4SLinus Torvalds { 0x08, 0x01, 0x3480 }, 431da177e4SLinus Torvalds { 0x08, 0x02, 0x3430 }, 441da177e4SLinus Torvalds { 0x08, 0x10, 0x3420 }, 451da177e4SLinus Torvalds { 0x08, 0x42, 0x3424 }, 461da177e4SLinus Torvalds { 0x08, 0x44, 0x9348 }, 471da177e4SLinus Torvalds { 0x08, 0x81, 0x3490 }, 481da177e4SLinus Torvalds { 0x08, 0x82, 0x3422 }, 491da177e4SLinus Torvalds { 0x10, 0x41, 0x1403 }, 501da177e4SLinus Torvalds { 0x10, 0x42, 0x3211 }, 511da177e4SLinus Torvalds { 0x10, 0x43, 0x3203 }, 521da177e4SLinus Torvalds { 0x10, 0x45, 0x3800 }, 531da177e4SLinus Torvalds { 0x10, 0x47, 0x3262 }, 541da177e4SLinus Torvalds { 0x10, 0x48, 0x3820 }, 551da177e4SLinus Torvalds { 0x10, 0x49, 0x3800 }, 561da177e4SLinus Torvalds { 0x10, 0x4a, 0x4245 }, 571da177e4SLinus Torvalds { 0x10, 0x4b, 0x4248 }, 581da177e4SLinus Torvalds { 0x10, 0x4d, 0x3800 }, 591da177e4SLinus Torvalds { 0x10, 0x4e, 0x3820 }, 601da177e4SLinus Torvalds { 0x10, 0x4f, 0x3820 }, 611da177e4SLinus Torvalds { 0x10, 0x82, 0x2540 }, 621da177e4SLinus Torvalds { 0x10, 0x84, 0x3525 }, 631da177e4SLinus Torvalds { 0x20, 0x81, 0x2501 }, 641da177e4SLinus Torvalds { 0x20, 0x82, 0x2540 }, 651da177e4SLinus Torvalds { 0x20, 0x84, 0x3505 }, 661da177e4SLinus Torvalds { 0x40, 0x01, 0x3278 }, 671da177e4SLinus Torvalds { 0x40, 0x04, 0x3277 }, 681da177e4SLinus Torvalds { 0x40, 0x80, 0x2250 }, 691da177e4SLinus Torvalds { 0x40, 0xc0, 0x5080 }, 701da177e4SLinus Torvalds { 0x80, 0x00, 0x3215 }, 711da177e4SLinus Torvalds }; 726f52ac29SPeter Oberparleiter int i; 736f52ac29SPeter Oberparleiter 746f52ac29SPeter Oberparleiter /* Special case for osa devices. */ 7539f5360bSPeter Oberparleiter if (diag->vrdcvcla == 0x02 && diag->vrdcvtyp == 0x20) { 7639f5360bSPeter Oberparleiter senseid->cu_type = 0x3088; 7739f5360bSPeter Oberparleiter senseid->cu_model = 0x60; 7839f5360bSPeter Oberparleiter senseid->reserved = 0xff; 796f52ac29SPeter Oberparleiter return 0; 801da177e4SLinus Torvalds } 8139f5360bSPeter Oberparleiter for (i = 0; i < ARRAY_SIZE(vm_devices); i++) { 8239f5360bSPeter Oberparleiter if (diag->vrdcvcla == vm_devices[i].class && 8339f5360bSPeter Oberparleiter diag->vrdcvtyp == vm_devices[i].type) { 8439f5360bSPeter Oberparleiter senseid->cu_type = vm_devices[i].cu_type; 8539f5360bSPeter Oberparleiter senseid->reserved = 0xff; 866f52ac29SPeter Oberparleiter return 0; 871da177e4SLinus Torvalds } 8839f5360bSPeter Oberparleiter } 896f52ac29SPeter Oberparleiter 906f52ac29SPeter Oberparleiter return -ENODEV; 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 9339f5360bSPeter Oberparleiter /** 9439f5360bSPeter Oberparleiter * diag_get_dev_info - retrieve device information via diag 0x210 9539f5360bSPeter Oberparleiter * @cdev: ccw device 9639f5360bSPeter Oberparleiter * 9739f5360bSPeter Oberparleiter * Returns zero on success, non-zero otherwise. 981da177e4SLinus Torvalds */ 9939f5360bSPeter Oberparleiter static int diag210_get_dev_info(struct ccw_device *cdev) 1001da177e4SLinus Torvalds { 10139f5360bSPeter Oberparleiter struct ccw_dev_id *dev_id = &cdev->private->dev_id; 10239f5360bSPeter Oberparleiter struct senseid *senseid = &cdev->private->senseid; 10339f5360bSPeter Oberparleiter struct diag210 diag_data; 10439f5360bSPeter Oberparleiter int rc; 1051da177e4SLinus Torvalds 10639f5360bSPeter Oberparleiter if (dev_id->ssid != 0) 10739f5360bSPeter Oberparleiter return -ENODEV; 10839f5360bSPeter Oberparleiter memset(&diag_data, 0, sizeof(diag_data)); 10939f5360bSPeter Oberparleiter diag_data.vrdcdvno = dev_id->devno; 11039f5360bSPeter Oberparleiter diag_data.vrdclen = sizeof(diag_data); 11139f5360bSPeter Oberparleiter rc = diag210(&diag_data); 11239f5360bSPeter Oberparleiter CIO_TRACE_EVENT(4, "diag210"); 11339f5360bSPeter Oberparleiter CIO_HEX_EVENT(4, &rc, sizeof(rc)); 11439f5360bSPeter Oberparleiter CIO_HEX_EVENT(4, &diag_data, sizeof(diag_data)); 11539f5360bSPeter Oberparleiter if (rc != 0 && rc != 2) 11639f5360bSPeter Oberparleiter goto err_failed; 11739f5360bSPeter Oberparleiter if (diag210_to_senseid(senseid, &diag_data)) 11839f5360bSPeter Oberparleiter goto err_unknown; 11939f5360bSPeter Oberparleiter return 0; 1201da177e4SLinus Torvalds 12139f5360bSPeter Oberparleiter err_unknown: 12239f5360bSPeter Oberparleiter CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: unknown diag210 data\n", 12339f5360bSPeter Oberparleiter dev_id->ssid, dev_id->devno); 12439f5360bSPeter Oberparleiter return -ENODEV; 12539f5360bSPeter Oberparleiter err_failed: 12639f5360bSPeter Oberparleiter CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: diag210 failed (rc=%d)\n", 12739f5360bSPeter Oberparleiter dev_id->ssid, dev_id->devno, rc); 12839f5360bSPeter Oberparleiter return -ENODEV; 1291da177e4SLinus Torvalds } 1301da177e4SLinus Torvalds 1311da177e4SLinus Torvalds /* 13239f5360bSPeter Oberparleiter * Initialize SENSE ID data. 1331da177e4SLinus Torvalds */ 13439f5360bSPeter Oberparleiter static void snsid_init(struct ccw_device *cdev) 1351da177e4SLinus Torvalds { 13639f5360bSPeter Oberparleiter cdev->private->flags.esid = 0; 13739f5360bSPeter Oberparleiter memset(&cdev->private->senseid, 0, sizeof(cdev->private->senseid)); 13839f5360bSPeter Oberparleiter cdev->private->senseid.cu_type = 0xffff; 139d23861ffSCornelia Huck } 14039f5360bSPeter Oberparleiter 1411da177e4SLinus Torvalds /* 14239f5360bSPeter Oberparleiter * Check for complete SENSE ID data. 1431da177e4SLinus Torvalds */ 14439f5360bSPeter Oberparleiter static int snsid_check(struct ccw_device *cdev, void *data) 14539f5360bSPeter Oberparleiter { 14639f5360bSPeter Oberparleiter struct cmd_scsw *scsw = &cdev->private->irb.scsw.cmd; 14739f5360bSPeter Oberparleiter int len = sizeof(struct senseid) - scsw->count; 14839f5360bSPeter Oberparleiter 14939f5360bSPeter Oberparleiter /* Check for incomplete SENSE ID data. */ 15039f5360bSPeter Oberparleiter if (len < SENSE_ID_MIN_LEN) 15139f5360bSPeter Oberparleiter goto out_restart; 15239f5360bSPeter Oberparleiter if (cdev->private->senseid.cu_type == 0xffff) 15339f5360bSPeter Oberparleiter goto out_restart; 15439f5360bSPeter Oberparleiter /* Check for incompatible SENSE ID data. */ 15539f5360bSPeter Oberparleiter if (cdev->private->senseid.reserved != 0xff) 1561da177e4SLinus Torvalds return -EOPNOTSUPP; 15739f5360bSPeter Oberparleiter /* Check for extended-identification information. */ 15839f5360bSPeter Oberparleiter if (len > SENSE_ID_BASIC_LEN) 1596f52ac29SPeter Oberparleiter cdev->private->flags.esid = 1; 16039f5360bSPeter Oberparleiter return 0; 1616f52ac29SPeter Oberparleiter 16239f5360bSPeter Oberparleiter out_restart: 16339f5360bSPeter Oberparleiter snsid_init(cdev); 1641da177e4SLinus Torvalds return -EAGAIN; 1651da177e4SLinus Torvalds } 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds /* 16839f5360bSPeter Oberparleiter * Process SENSE ID request result. 1691da177e4SLinus Torvalds */ 17039f5360bSPeter Oberparleiter static void snsid_callback(struct ccw_device *cdev, void *data, int rc) 1711da177e4SLinus Torvalds { 17239f5360bSPeter Oberparleiter struct ccw_dev_id *id = &cdev->private->dev_id; 17339f5360bSPeter Oberparleiter struct senseid *senseid = &cdev->private->senseid; 17439f5360bSPeter Oberparleiter int vm = 0; 1751da177e4SLinus Torvalds 17639f5360bSPeter Oberparleiter if (rc && MACHINE_IS_VM) { 17739f5360bSPeter Oberparleiter /* Try diag 0x210 fallback on z/VM. */ 17839f5360bSPeter Oberparleiter snsid_init(cdev); 17939f5360bSPeter Oberparleiter if (diag210_get_dev_info(cdev) == 0) { 18039f5360bSPeter Oberparleiter rc = 0; 18139f5360bSPeter Oberparleiter vm = 1; 1821da177e4SLinus Torvalds } 1831da177e4SLinus Torvalds } 18439f5360bSPeter Oberparleiter CIO_MSG_EVENT(2, "snsid: device 0.%x.%04x: rc=%d %04x/%02x " 18539f5360bSPeter Oberparleiter "%04x/%02x%s\n", id->ssid, id->devno, rc, 18639f5360bSPeter Oberparleiter senseid->cu_type, senseid->cu_model, senseid->dev_type, 18739f5360bSPeter Oberparleiter senseid->dev_model, vm ? " (diag210)" : ""); 18839f5360bSPeter Oberparleiter ccw_device_sense_id_done(cdev, rc); 18939f5360bSPeter Oberparleiter } 19039f5360bSPeter Oberparleiter 19139f5360bSPeter Oberparleiter /** 19239f5360bSPeter Oberparleiter * ccw_device_sense_id_start - perform SENSE ID 19339f5360bSPeter Oberparleiter * @cdev: ccw device 19439f5360bSPeter Oberparleiter * 19539f5360bSPeter Oberparleiter * Execute a SENSE ID channel program on @cdev to update its sense id 19639f5360bSPeter Oberparleiter * information. When finished, call ccw_device_sense_id_done with a 19739f5360bSPeter Oberparleiter * return code specifying the result. 1981da177e4SLinus Torvalds */ 19939f5360bSPeter Oberparleiter void ccw_device_sense_id_start(struct ccw_device *cdev) 20039f5360bSPeter Oberparleiter { 20139f5360bSPeter Oberparleiter struct subchannel *sch = to_subchannel(cdev->dev.parent); 20239f5360bSPeter Oberparleiter struct ccw_request *req = &cdev->private->req; 20339f5360bSPeter Oberparleiter struct ccw1 *cp = cdev->private->iccws; 2046f52ac29SPeter Oberparleiter 20539f5360bSPeter Oberparleiter CIO_TRACE_EVENT(4, "snsid"); 20639f5360bSPeter Oberparleiter CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); 20739f5360bSPeter Oberparleiter /* Data setup. */ 20839f5360bSPeter Oberparleiter snsid_init(cdev); 20939f5360bSPeter Oberparleiter /* Channel program setup. */ 21039f5360bSPeter Oberparleiter cp->cmd_code = CCW_CMD_SENSE_ID; 21139f5360bSPeter Oberparleiter cp->cda = (u32) (addr_t) &cdev->private->senseid; 21239f5360bSPeter Oberparleiter cp->count = sizeof(struct senseid); 21339f5360bSPeter Oberparleiter cp->flags = CCW_FLAG_SLI; 21439f5360bSPeter Oberparleiter /* Request setup. */ 21539f5360bSPeter Oberparleiter memset(req, 0, sizeof(*req)); 21639f5360bSPeter Oberparleiter req->cp = cp; 21739f5360bSPeter Oberparleiter req->timeout = SENSE_ID_TIMEOUT; 21839f5360bSPeter Oberparleiter req->maxretries = SENSE_ID_RETRIES; 21939f5360bSPeter Oberparleiter req->lpm = sch->schib.pmcw.pam & sch->opm; 22039f5360bSPeter Oberparleiter req->check = snsid_check; 22139f5360bSPeter Oberparleiter req->callback = snsid_callback; 22239f5360bSPeter Oberparleiter ccw_request_start(cdev); 2231da177e4SLinus Torvalds } 224