mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-15 23:14:31 +08:00
37db8985b2
As virtio-ccw devices are channel devices, we need to use the dma area within the common I/O layer for any communication with the hypervisor. Note that we do not need to use that area for control blocks directly referenced by instructions, e.g. the orb. It handles neither QDIO in the common code, nor any device type specific stuff (like channel programs constructed by the DASD driver). An interesting side effect is that virtio structures are now going to get allocated in 31 bit addressable storage. Signed-off-by: Halil Pasic <pasic@linux.ibm.com> Reviewed-by: Sebastian Ott <sebott@linux.ibm.com> Reviewed-by: Cornelia Huck <cohuck@redhat.com> Reviewed-by: Michael Mueller <mimu@linux.ibm.com> Tested-by: Michael Mueller <mimu@linux.ibm.com> Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
226 lines
5.9 KiB
C
226 lines
5.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* 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->dma_area->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->dma_area->senseid, 0,
|
|
sizeof(cdev->private->dma_area->senseid));
|
|
cdev->private->dma_area->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->dma_area->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->dma_area->senseid.cu_type == 0xffff)
|
|
goto out_restart;
|
|
/* Check for incompatible SENSE ID data. */
|
|
if (cdev->private->dma_area->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->dma_area->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->dma_area->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->dma_area->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);
|
|
}
|