162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * CCW device SENSE ID I/O handling. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2002, 2009 662306a36Sopenharmony_ci * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> 762306a36Sopenharmony_ci * Martin Schwidefsky <schwidefsky@de.ibm.com> 862306a36Sopenharmony_ci * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/string.h> 1362306a36Sopenharmony_ci#include <linux/types.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <asm/ccwdev.h> 1662306a36Sopenharmony_ci#include <asm/setup.h> 1762306a36Sopenharmony_ci#include <asm/cio.h> 1862306a36Sopenharmony_ci#include <asm/diag.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "cio.h" 2162306a36Sopenharmony_ci#include "cio_debug.h" 2262306a36Sopenharmony_ci#include "device.h" 2362306a36Sopenharmony_ci#include "io_sch.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define SENSE_ID_RETRIES 256 2662306a36Sopenharmony_ci#define SENSE_ID_TIMEOUT (10 * HZ) 2762306a36Sopenharmony_ci#define SENSE_ID_MIN_LEN 4 2862306a36Sopenharmony_ci#define SENSE_ID_BASIC_LEN 7 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/** 3162306a36Sopenharmony_ci * diag210_to_senseid - convert diag 0x210 data to sense id information 3262306a36Sopenharmony_ci * @senseid: sense id 3362306a36Sopenharmony_ci * @diag: diag 0x210 data 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * Return 0 on success, non-zero otherwise. 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_cistatic int diag210_to_senseid(struct senseid *senseid, struct diag210 *diag) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci static struct { 4062306a36Sopenharmony_ci int class, type, cu_type; 4162306a36Sopenharmony_ci } vm_devices[] = { 4262306a36Sopenharmony_ci { 0x08, 0x01, 0x3480 }, 4362306a36Sopenharmony_ci { 0x08, 0x02, 0x3430 }, 4462306a36Sopenharmony_ci { 0x08, 0x10, 0x3420 }, 4562306a36Sopenharmony_ci { 0x08, 0x42, 0x3424 }, 4662306a36Sopenharmony_ci { 0x08, 0x44, 0x9348 }, 4762306a36Sopenharmony_ci { 0x08, 0x81, 0x3490 }, 4862306a36Sopenharmony_ci { 0x08, 0x82, 0x3422 }, 4962306a36Sopenharmony_ci { 0x10, 0x41, 0x1403 }, 5062306a36Sopenharmony_ci { 0x10, 0x42, 0x3211 }, 5162306a36Sopenharmony_ci { 0x10, 0x43, 0x3203 }, 5262306a36Sopenharmony_ci { 0x10, 0x45, 0x3800 }, 5362306a36Sopenharmony_ci { 0x10, 0x47, 0x3262 }, 5462306a36Sopenharmony_ci { 0x10, 0x48, 0x3820 }, 5562306a36Sopenharmony_ci { 0x10, 0x49, 0x3800 }, 5662306a36Sopenharmony_ci { 0x10, 0x4a, 0x4245 }, 5762306a36Sopenharmony_ci { 0x10, 0x4b, 0x4248 }, 5862306a36Sopenharmony_ci { 0x10, 0x4d, 0x3800 }, 5962306a36Sopenharmony_ci { 0x10, 0x4e, 0x3820 }, 6062306a36Sopenharmony_ci { 0x10, 0x4f, 0x3820 }, 6162306a36Sopenharmony_ci { 0x10, 0x82, 0x2540 }, 6262306a36Sopenharmony_ci { 0x10, 0x84, 0x3525 }, 6362306a36Sopenharmony_ci { 0x20, 0x81, 0x2501 }, 6462306a36Sopenharmony_ci { 0x20, 0x82, 0x2540 }, 6562306a36Sopenharmony_ci { 0x20, 0x84, 0x3505 }, 6662306a36Sopenharmony_ci { 0x40, 0x01, 0x3278 }, 6762306a36Sopenharmony_ci { 0x40, 0x04, 0x3277 }, 6862306a36Sopenharmony_ci { 0x40, 0x80, 0x2250 }, 6962306a36Sopenharmony_ci { 0x40, 0xc0, 0x5080 }, 7062306a36Sopenharmony_ci { 0x80, 0x00, 0x3215 }, 7162306a36Sopenharmony_ci }; 7262306a36Sopenharmony_ci int i; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* Special case for osa devices. */ 7562306a36Sopenharmony_ci if (diag->vrdcvcla == 0x02 && diag->vrdcvtyp == 0x20) { 7662306a36Sopenharmony_ci senseid->cu_type = 0x3088; 7762306a36Sopenharmony_ci senseid->cu_model = 0x60; 7862306a36Sopenharmony_ci senseid->reserved = 0xff; 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vm_devices); i++) { 8262306a36Sopenharmony_ci if (diag->vrdcvcla == vm_devices[i].class && 8362306a36Sopenharmony_ci diag->vrdcvtyp == vm_devices[i].type) { 8462306a36Sopenharmony_ci senseid->cu_type = vm_devices[i].cu_type; 8562306a36Sopenharmony_ci senseid->reserved = 0xff; 8662306a36Sopenharmony_ci return 0; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return -ENODEV; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/** 9462306a36Sopenharmony_ci * diag210_get_dev_info - retrieve device information via diag 0x210 9562306a36Sopenharmony_ci * @cdev: ccw device 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * Returns zero on success, non-zero otherwise. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_cistatic int diag210_get_dev_info(struct ccw_device *cdev) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct ccw_dev_id *dev_id = &cdev->private->dev_id; 10262306a36Sopenharmony_ci struct senseid *senseid = &cdev->private->dma_area->senseid; 10362306a36Sopenharmony_ci struct diag210 diag_data; 10462306a36Sopenharmony_ci int rc; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (dev_id->ssid != 0) 10762306a36Sopenharmony_ci return -ENODEV; 10862306a36Sopenharmony_ci memset(&diag_data, 0, sizeof(diag_data)); 10962306a36Sopenharmony_ci diag_data.vrdcdvno = dev_id->devno; 11062306a36Sopenharmony_ci diag_data.vrdclen = sizeof(diag_data); 11162306a36Sopenharmony_ci rc = diag210(&diag_data); 11262306a36Sopenharmony_ci CIO_TRACE_EVENT(4, "diag210"); 11362306a36Sopenharmony_ci CIO_HEX_EVENT(4, &rc, sizeof(rc)); 11462306a36Sopenharmony_ci CIO_HEX_EVENT(4, &diag_data, sizeof(diag_data)); 11562306a36Sopenharmony_ci if (rc != 0 && rc != 2) 11662306a36Sopenharmony_ci goto err_failed; 11762306a36Sopenharmony_ci if (diag210_to_senseid(senseid, &diag_data)) 11862306a36Sopenharmony_ci goto err_unknown; 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cierr_unknown: 12262306a36Sopenharmony_ci CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: unknown diag210 data\n", 12362306a36Sopenharmony_ci dev_id->ssid, dev_id->devno); 12462306a36Sopenharmony_ci return -ENODEV; 12562306a36Sopenharmony_cierr_failed: 12662306a36Sopenharmony_ci CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: diag210 failed (rc=%d)\n", 12762306a36Sopenharmony_ci dev_id->ssid, dev_id->devno, rc); 12862306a36Sopenharmony_ci return -ENODEV; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* 13262306a36Sopenharmony_ci * Initialize SENSE ID data. 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_cistatic void snsid_init(struct ccw_device *cdev) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci cdev->private->flags.esid = 0; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci memset(&cdev->private->dma_area->senseid, 0, 13962306a36Sopenharmony_ci sizeof(cdev->private->dma_area->senseid)); 14062306a36Sopenharmony_ci cdev->private->dma_area->senseid.cu_type = 0xffff; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* 14462306a36Sopenharmony_ci * Check for complete SENSE ID data. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_cistatic int snsid_check(struct ccw_device *cdev, void *data) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct cmd_scsw *scsw = &cdev->private->dma_area->irb.scsw.cmd; 14962306a36Sopenharmony_ci int len = sizeof(struct senseid) - scsw->count; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Check for incomplete SENSE ID data. */ 15262306a36Sopenharmony_ci if (len < SENSE_ID_MIN_LEN) 15362306a36Sopenharmony_ci goto out_restart; 15462306a36Sopenharmony_ci if (cdev->private->dma_area->senseid.cu_type == 0xffff) 15562306a36Sopenharmony_ci goto out_restart; 15662306a36Sopenharmony_ci /* Check for incompatible SENSE ID data. */ 15762306a36Sopenharmony_ci if (cdev->private->dma_area->senseid.reserved != 0xff) 15862306a36Sopenharmony_ci return -EOPNOTSUPP; 15962306a36Sopenharmony_ci /* Check for extended-identification information. */ 16062306a36Sopenharmony_ci if (len > SENSE_ID_BASIC_LEN) 16162306a36Sopenharmony_ci cdev->private->flags.esid = 1; 16262306a36Sopenharmony_ci return 0; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ciout_restart: 16562306a36Sopenharmony_ci snsid_init(cdev); 16662306a36Sopenharmony_ci return -EAGAIN; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* 17062306a36Sopenharmony_ci * Process SENSE ID request result. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_cistatic void snsid_callback(struct ccw_device *cdev, void *data, int rc) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct ccw_dev_id *id = &cdev->private->dev_id; 17562306a36Sopenharmony_ci struct senseid *senseid = &cdev->private->dma_area->senseid; 17662306a36Sopenharmony_ci int vm = 0; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (rc && MACHINE_IS_VM) { 17962306a36Sopenharmony_ci /* Try diag 0x210 fallback on z/VM. */ 18062306a36Sopenharmony_ci snsid_init(cdev); 18162306a36Sopenharmony_ci if (diag210_get_dev_info(cdev) == 0) { 18262306a36Sopenharmony_ci rc = 0; 18362306a36Sopenharmony_ci vm = 1; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci CIO_MSG_EVENT(2, "snsid: device 0.%x.%04x: rc=%d %04x/%02x " 18762306a36Sopenharmony_ci "%04x/%02x%s\n", id->ssid, id->devno, rc, 18862306a36Sopenharmony_ci senseid->cu_type, senseid->cu_model, senseid->dev_type, 18962306a36Sopenharmony_ci senseid->dev_model, vm ? " (diag210)" : ""); 19062306a36Sopenharmony_ci ccw_device_sense_id_done(cdev, rc); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/** 19462306a36Sopenharmony_ci * ccw_device_sense_id_start - perform SENSE ID 19562306a36Sopenharmony_ci * @cdev: ccw device 19662306a36Sopenharmony_ci * 19762306a36Sopenharmony_ci * Execute a SENSE ID channel program on @cdev to update its sense id 19862306a36Sopenharmony_ci * information. When finished, call ccw_device_sense_id_done with a 19962306a36Sopenharmony_ci * return code specifying the result. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_civoid ccw_device_sense_id_start(struct ccw_device *cdev) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 20462306a36Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 20562306a36Sopenharmony_ci struct ccw1 *cp = cdev->private->dma_area->iccws; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci CIO_TRACE_EVENT(4, "snsid"); 20862306a36Sopenharmony_ci CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); 20962306a36Sopenharmony_ci /* Data setup. */ 21062306a36Sopenharmony_ci snsid_init(cdev); 21162306a36Sopenharmony_ci /* Channel program setup. */ 21262306a36Sopenharmony_ci cp->cmd_code = CCW_CMD_SENSE_ID; 21362306a36Sopenharmony_ci cp->cda = (u32)virt_to_phys(&cdev->private->dma_area->senseid); 21462306a36Sopenharmony_ci cp->count = sizeof(struct senseid); 21562306a36Sopenharmony_ci cp->flags = CCW_FLAG_SLI; 21662306a36Sopenharmony_ci /* Request setup. */ 21762306a36Sopenharmony_ci memset(req, 0, sizeof(*req)); 21862306a36Sopenharmony_ci req->cp = cp; 21962306a36Sopenharmony_ci req->timeout = SENSE_ID_TIMEOUT; 22062306a36Sopenharmony_ci req->maxretries = SENSE_ID_RETRIES; 22162306a36Sopenharmony_ci req->lpm = sch->schib.pmcw.pam & sch->opm; 22262306a36Sopenharmony_ci req->check = snsid_check; 22362306a36Sopenharmony_ci req->callback = snsid_callback; 22462306a36Sopenharmony_ci ccw_request_start(cdev); 22562306a36Sopenharmony_ci} 226