forked from Minki/linux
a53c8fab3f
Remove the file name from the comment at top of many files. In most cases the file name was wrong anyway, so it's rather pointless. Also unify the IBM copyright statement. We did have a lot of sightly different statements and wanted to change them one after another whenever a file gets touched. However that never happened. Instead people start to take the old/"wrong" statements to use as a template for new files. So unify all of them in one go. Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
223 lines
5.7 KiB
C
223 lines
5.7 KiB
C
/*
|
|
* CCW device SENSE ID I/O handling.
|
|
*
|
|
* Copyright IBM Corp. 2002, 2009
|
|
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
|
|
* Martin Schwidefsky <schwidefsky@de.ibm.com>
|
|
* Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/string.h>
|
|
#include <linux/types.h>
|
|
#include <linux/errno.h>
|
|
#include <asm/ccwdev.h>
|
|
#include <asm/setup.h>
|
|
#include <asm/cio.h>
|
|
#include <asm/diag.h>
|
|
|
|
#include "cio.h"
|
|
#include "cio_debug.h"
|
|
#include "device.h"
|
|
#include "io_sch.h"
|
|
|
|
#define SENSE_ID_RETRIES 256
|
|
#define SENSE_ID_TIMEOUT (10 * HZ)
|
|
#define SENSE_ID_MIN_LEN 4
|
|
#define SENSE_ID_BASIC_LEN 7
|
|
|
|
/**
|
|
* diag210_to_senseid - convert diag 0x210 data to sense id information
|
|
* @senseid: sense id
|
|
* @diag: diag 0x210 data
|
|
*
|
|
* Return 0 on success, non-zero otherwise.
|
|
*/
|
|
static int diag210_to_senseid(struct senseid *senseid, struct diag210 *diag)
|
|
{
|
|
static struct {
|
|
int class, type, cu_type;
|
|
} vm_devices[] = {
|
|
{ 0x08, 0x01, 0x3480 },
|
|
{ 0x08, 0x02, 0x3430 },
|
|
{ 0x08, 0x10, 0x3420 },
|
|
{ 0x08, 0x42, 0x3424 },
|
|
{ 0x08, 0x44, 0x9348 },
|
|
{ 0x08, 0x81, 0x3490 },
|
|
{ 0x08, 0x82, 0x3422 },
|
|
{ 0x10, 0x41, 0x1403 },
|
|
{ 0x10, 0x42, 0x3211 },
|
|
{ 0x10, 0x43, 0x3203 },
|
|
{ 0x10, 0x45, 0x3800 },
|
|
{ 0x10, 0x47, 0x3262 },
|
|
{ 0x10, 0x48, 0x3820 },
|
|
{ 0x10, 0x49, 0x3800 },
|
|
{ 0x10, 0x4a, 0x4245 },
|
|
{ 0x10, 0x4b, 0x4248 },
|
|
{ 0x10, 0x4d, 0x3800 },
|
|
{ 0x10, 0x4e, 0x3820 },
|
|
{ 0x10, 0x4f, 0x3820 },
|
|
{ 0x10, 0x82, 0x2540 },
|
|
{ 0x10, 0x84, 0x3525 },
|
|
{ 0x20, 0x81, 0x2501 },
|
|
{ 0x20, 0x82, 0x2540 },
|
|
{ 0x20, 0x84, 0x3505 },
|
|
{ 0x40, 0x01, 0x3278 },
|
|
{ 0x40, 0x04, 0x3277 },
|
|
{ 0x40, 0x80, 0x2250 },
|
|
{ 0x40, 0xc0, 0x5080 },
|
|
{ 0x80, 0x00, 0x3215 },
|
|
};
|
|
int i;
|
|
|
|
/* Special case for osa devices. */
|
|
if (diag->vrdcvcla == 0x02 && diag->vrdcvtyp == 0x20) {
|
|
senseid->cu_type = 0x3088;
|
|
senseid->cu_model = 0x60;
|
|
senseid->reserved = 0xff;
|
|
return 0;
|
|
}
|
|
for (i = 0; i < ARRAY_SIZE(vm_devices); i++) {
|
|
if (diag->vrdcvcla == vm_devices[i].class &&
|
|
diag->vrdcvtyp == vm_devices[i].type) {
|
|
senseid->cu_type = vm_devices[i].cu_type;
|
|
senseid->reserved = 0xff;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -ENODEV;
|
|
}
|
|
|
|
/**
|
|
* diag_get_dev_info - retrieve device information via diag 0x210
|
|
* @cdev: ccw device
|
|
*
|
|
* Returns zero on success, non-zero otherwise.
|
|
*/
|
|
static int diag210_get_dev_info(struct ccw_device *cdev)
|
|
{
|
|
struct ccw_dev_id *dev_id = &cdev->private->dev_id;
|
|
struct senseid *senseid = &cdev->private->senseid;
|
|
struct diag210 diag_data;
|
|
int rc;
|
|
|
|
if (dev_id->ssid != 0)
|
|
return -ENODEV;
|
|
memset(&diag_data, 0, sizeof(diag_data));
|
|
diag_data.vrdcdvno = dev_id->devno;
|
|
diag_data.vrdclen = sizeof(diag_data);
|
|
rc = diag210(&diag_data);
|
|
CIO_TRACE_EVENT(4, "diag210");
|
|
CIO_HEX_EVENT(4, &rc, sizeof(rc));
|
|
CIO_HEX_EVENT(4, &diag_data, sizeof(diag_data));
|
|
if (rc != 0 && rc != 2)
|
|
goto err_failed;
|
|
if (diag210_to_senseid(senseid, &diag_data))
|
|
goto err_unknown;
|
|
return 0;
|
|
|
|
err_unknown:
|
|
CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: unknown diag210 data\n",
|
|
dev_id->ssid, dev_id->devno);
|
|
return -ENODEV;
|
|
err_failed:
|
|
CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: diag210 failed (rc=%d)\n",
|
|
dev_id->ssid, dev_id->devno, rc);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/*
|
|
* Initialize SENSE ID data.
|
|
*/
|
|
static void snsid_init(struct ccw_device *cdev)
|
|
{
|
|
cdev->private->flags.esid = 0;
|
|
memset(&cdev->private->senseid, 0, sizeof(cdev->private->senseid));
|
|
cdev->private->senseid.cu_type = 0xffff;
|
|
}
|
|
|
|
/*
|
|
* Check for complete SENSE ID data.
|
|
*/
|
|
static int snsid_check(struct ccw_device *cdev, void *data)
|
|
{
|
|
struct cmd_scsw *scsw = &cdev->private->irb.scsw.cmd;
|
|
int len = sizeof(struct senseid) - scsw->count;
|
|
|
|
/* Check for incomplete SENSE ID data. */
|
|
if (len < SENSE_ID_MIN_LEN)
|
|
goto out_restart;
|
|
if (cdev->private->senseid.cu_type == 0xffff)
|
|
goto out_restart;
|
|
/* Check for incompatible SENSE ID data. */
|
|
if (cdev->private->senseid.reserved != 0xff)
|
|
return -EOPNOTSUPP;
|
|
/* Check for extended-identification information. */
|
|
if (len > SENSE_ID_BASIC_LEN)
|
|
cdev->private->flags.esid = 1;
|
|
return 0;
|
|
|
|
out_restart:
|
|
snsid_init(cdev);
|
|
return -EAGAIN;
|
|
}
|
|
|
|
/*
|
|
* Process SENSE ID request result.
|
|
*/
|
|
static void snsid_callback(struct ccw_device *cdev, void *data, int rc)
|
|
{
|
|
struct ccw_dev_id *id = &cdev->private->dev_id;
|
|
struct senseid *senseid = &cdev->private->senseid;
|
|
int vm = 0;
|
|
|
|
if (rc && MACHINE_IS_VM) {
|
|
/* Try diag 0x210 fallback on z/VM. */
|
|
snsid_init(cdev);
|
|
if (diag210_get_dev_info(cdev) == 0) {
|
|
rc = 0;
|
|
vm = 1;
|
|
}
|
|
}
|
|
CIO_MSG_EVENT(2, "snsid: device 0.%x.%04x: rc=%d %04x/%02x "
|
|
"%04x/%02x%s\n", id->ssid, id->devno, rc,
|
|
senseid->cu_type, senseid->cu_model, senseid->dev_type,
|
|
senseid->dev_model, vm ? " (diag210)" : "");
|
|
ccw_device_sense_id_done(cdev, rc);
|
|
}
|
|
|
|
/**
|
|
* ccw_device_sense_id_start - perform SENSE ID
|
|
* @cdev: ccw device
|
|
*
|
|
* Execute a SENSE ID channel program on @cdev to update its sense id
|
|
* information. When finished, call ccw_device_sense_id_done with a
|
|
* return code specifying the result.
|
|
*/
|
|
void ccw_device_sense_id_start(struct ccw_device *cdev)
|
|
{
|
|
struct subchannel *sch = to_subchannel(cdev->dev.parent);
|
|
struct ccw_request *req = &cdev->private->req;
|
|
struct ccw1 *cp = cdev->private->iccws;
|
|
|
|
CIO_TRACE_EVENT(4, "snsid");
|
|
CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
|
|
/* Data setup. */
|
|
snsid_init(cdev);
|
|
/* Channel program setup. */
|
|
cp->cmd_code = CCW_CMD_SENSE_ID;
|
|
cp->cda = (u32) (addr_t) &cdev->private->senseid;
|
|
cp->count = sizeof(struct senseid);
|
|
cp->flags = CCW_FLAG_SLI;
|
|
/* Request setup. */
|
|
memset(req, 0, sizeof(*req));
|
|
req->cp = cp;
|
|
req->timeout = SENSE_ID_TIMEOUT;
|
|
req->maxretries = SENSE_ID_RETRIES;
|
|
req->lpm = sch->schib.pmcw.pam & sch->opm;
|
|
req->check = snsid_check;
|
|
req->callback = snsid_callback;
|
|
ccw_request_start(cdev);
|
|
}
|