226 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * CCW device SENSE ID I/O handling.4 *5 * Copyright IBM Corp. 2002, 20096 * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>7 * Martin Schwidefsky <schwidefsky@de.ibm.com>8 * Peter Oberparleiter <peter.oberparleiter@de.ibm.com>9 */10 11#include <linux/kernel.h>12#include <linux/string.h>13#include <linux/types.h>14#include <linux/errno.h>15#include <asm/ccwdev.h>16#include <asm/setup.h>17#include <asm/cio.h>18#include <asm/diag.h>19 20#include "cio.h"21#include "cio_debug.h"22#include "device.h"23#include "io_sch.h"24 25#define SENSE_ID_RETRIES 25626#define SENSE_ID_TIMEOUT (10 * HZ)27#define SENSE_ID_MIN_LEN 428#define SENSE_ID_BASIC_LEN 729 30/**31 * diag210_to_senseid - convert diag 0x210 data to sense id information32 * @senseid: sense id33 * @diag: diag 0x210 data34 *35 * Return 0 on success, non-zero otherwise.36 */37static int diag210_to_senseid(struct senseid *senseid, struct diag210 *diag)38{39 static struct {40 int class, type, cu_type;41 } vm_devices[] = {42 { 0x08, 0x01, 0x3480 },43 { 0x08, 0x02, 0x3430 },44 { 0x08, 0x10, 0x3420 },45 { 0x08, 0x42, 0x3424 },46 { 0x08, 0x44, 0x9348 },47 { 0x08, 0x81, 0x3490 },48 { 0x08, 0x82, 0x3422 },49 { 0x10, 0x41, 0x1403 },50 { 0x10, 0x42, 0x3211 },51 { 0x10, 0x43, 0x3203 },52 { 0x10, 0x45, 0x3800 },53 { 0x10, 0x47, 0x3262 },54 { 0x10, 0x48, 0x3820 },55 { 0x10, 0x49, 0x3800 },56 { 0x10, 0x4a, 0x4245 },57 { 0x10, 0x4b, 0x4248 },58 { 0x10, 0x4d, 0x3800 },59 { 0x10, 0x4e, 0x3820 },60 { 0x10, 0x4f, 0x3820 },61 { 0x10, 0x82, 0x2540 },62 { 0x10, 0x84, 0x3525 },63 { 0x20, 0x81, 0x2501 },64 { 0x20, 0x82, 0x2540 },65 { 0x20, 0x84, 0x3505 },66 { 0x40, 0x01, 0x3278 },67 { 0x40, 0x04, 0x3277 },68 { 0x40, 0x80, 0x2250 },69 { 0x40, 0xc0, 0x5080 },70 { 0x80, 0x00, 0x3215 },71 };72 int i;73 74 /* Special case for osa devices. */75 if (diag->vrdcvcla == 0x02 && diag->vrdcvtyp == 0x20) {76 senseid->cu_type = 0x3088;77 senseid->cu_model = 0x60;78 senseid->reserved = 0xff;79 return 0;80 }81 for (i = 0; i < ARRAY_SIZE(vm_devices); i++) {82 if (diag->vrdcvcla == vm_devices[i].class &&83 diag->vrdcvtyp == vm_devices[i].type) {84 senseid->cu_type = vm_devices[i].cu_type;85 senseid->reserved = 0xff;86 return 0;87 }88 }89 90 return -ENODEV;91}92 93/**94 * diag210_get_dev_info - retrieve device information via diag 0x21095 * @cdev: ccw device96 *97 * Returns zero on success, non-zero otherwise.98 */99static int diag210_get_dev_info(struct ccw_device *cdev)100{101 struct ccw_dev_id *dev_id = &cdev->private->dev_id;102 struct senseid *senseid = &cdev->private->dma_area->senseid;103 struct diag210 diag_data;104 int rc;105 106 if (dev_id->ssid != 0)107 return -ENODEV;108 memset(&diag_data, 0, sizeof(diag_data));109 diag_data.vrdcdvno = dev_id->devno;110 diag_data.vrdclen = sizeof(diag_data);111 rc = diag210(&diag_data);112 CIO_TRACE_EVENT(4, "diag210");113 CIO_HEX_EVENT(4, &rc, sizeof(rc));114 CIO_HEX_EVENT(4, &diag_data, sizeof(diag_data));115 if (rc != 0 && rc != 2)116 goto err_failed;117 if (diag210_to_senseid(senseid, &diag_data))118 goto err_unknown;119 return 0;120 121err_unknown:122 CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: unknown diag210 data\n",123 dev_id->ssid, dev_id->devno);124 return -ENODEV;125err_failed:126 CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: diag210 failed (rc=%d)\n",127 dev_id->ssid, dev_id->devno, rc);128 return -ENODEV;129}130 131/*132 * Initialize SENSE ID data.133 */134static void snsid_init(struct ccw_device *cdev)135{136 cdev->private->flags.esid = 0;137 138 memset(&cdev->private->dma_area->senseid, 0,139 sizeof(cdev->private->dma_area->senseid));140 cdev->private->dma_area->senseid.cu_type = 0xffff;141}142 143/*144 * Check for complete SENSE ID data.145 */146static int snsid_check(struct ccw_device *cdev, void *data)147{148 struct cmd_scsw *scsw = &cdev->private->dma_area->irb.scsw.cmd;149 int len = sizeof(struct senseid) - scsw->count;150 151 /* Check for incomplete SENSE ID data. */152 if (len < SENSE_ID_MIN_LEN)153 goto out_restart;154 if (cdev->private->dma_area->senseid.cu_type == 0xffff)155 goto out_restart;156 /* Check for incompatible SENSE ID data. */157 if (cdev->private->dma_area->senseid.reserved != 0xff)158 return -EOPNOTSUPP;159 /* Check for extended-identification information. */160 if (len > SENSE_ID_BASIC_LEN)161 cdev->private->flags.esid = 1;162 return 0;163 164out_restart:165 snsid_init(cdev);166 return -EAGAIN;167}168 169/*170 * Process SENSE ID request result.171 */172static void snsid_callback(struct ccw_device *cdev, void *data, int rc)173{174 struct ccw_dev_id *id = &cdev->private->dev_id;175 struct senseid *senseid = &cdev->private->dma_area->senseid;176 int vm = 0;177 178 if (rc && MACHINE_IS_VM) {179 /* Try diag 0x210 fallback on z/VM. */180 snsid_init(cdev);181 if (diag210_get_dev_info(cdev) == 0) {182 rc = 0;183 vm = 1;184 }185 }186 CIO_MSG_EVENT(2, "snsid: device 0.%x.%04x: rc=%d %04x/%02x "187 "%04x/%02x%s\n", id->ssid, id->devno, rc,188 senseid->cu_type, senseid->cu_model, senseid->dev_type,189 senseid->dev_model, vm ? " (diag210)" : "");190 ccw_device_sense_id_done(cdev, rc);191}192 193/**194 * ccw_device_sense_id_start - perform SENSE ID195 * @cdev: ccw device196 *197 * Execute a SENSE ID channel program on @cdev to update its sense id198 * information. When finished, call ccw_device_sense_id_done with a199 * return code specifying the result.200 */201void ccw_device_sense_id_start(struct ccw_device *cdev)202{203 struct subchannel *sch = to_subchannel(cdev->dev.parent);204 struct ccw_request *req = &cdev->private->req;205 struct ccw1 *cp = cdev->private->dma_area->iccws;206 207 CIO_TRACE_EVENT(4, "snsid");208 CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));209 /* Data setup. */210 snsid_init(cdev);211 /* Channel program setup. */212 cp->cmd_code = CCW_CMD_SENSE_ID;213 cp->cda = virt_to_dma32(&cdev->private->dma_area->senseid);214 cp->count = sizeof(struct senseid);215 cp->flags = CCW_FLAG_SLI;216 /* Request setup. */217 memset(req, 0, sizeof(*req));218 req->cp = cp;219 req->timeout = SENSE_ID_TIMEOUT;220 req->maxretries = SENSE_ID_RETRIES;221 req->lpm = sch->schib.pmcw.pam & sch->opm;222 req->check = snsid_check;223 req->callback = snsid_callback;224 ccw_request_start(cdev);225}226