2005-04-16 15:20:36 -07:00
/*
2009-12-07 12:51:26 +01:00
* CCW device SENSE ID I / O handling .
2005-04-16 15:20:36 -07:00
*
2012-07-20 11:15:04 +02:00
* Copyright IBM Corp . 2002 , 2009
2009-12-07 12:51:26 +01:00
* Author ( s ) : Cornelia Huck < cornelia . huck @ de . ibm . com >
* Martin Schwidefsky < schwidefsky @ de . ibm . com >
* Peter Oberparleiter < peter . oberparleiter @ de . ibm . com >
2005-04-16 15:20:36 -07:00
*/
2007-02-12 15:49:51 +01:00
# include <linux/kernel.h>
2009-12-07 12:51:26 +01:00
# include <linux/string.h>
# include <linux/types.h>
# include <linux/errno.h>
2005-04-16 15:20:36 -07:00
# include <asm/ccwdev.h>
2009-12-07 12:51:26 +01:00
# include <asm/setup.h>
2005-04-16 15:20:36 -07:00
# include <asm/cio.h>
2007-08-22 13:51:40 +02:00
# include <asm/diag.h>
2005-04-16 15:20:36 -07:00
# include "cio.h"
# include "cio_debug.h"
# include "device.h"
2008-01-26 14:10:43 +01:00
# include "io_sch.h"
2005-04-16 15:20:36 -07:00
2009-12-07 12:51:40 +01:00
# define SENSE_ID_RETRIES 256
2009-12-07 12:51:26 +01:00
# define SENSE_ID_TIMEOUT (10 * HZ)
# define SENSE_ID_MIN_LEN 4
# define SENSE_ID_BASIC_LEN 7
2008-02-05 16:50:33 +01:00
/**
2009-12-07 12:51:26 +01:00
* diag210_to_senseid - convert diag 0x210 data to sense id information
* @ senseid : sense id
* @ diag : diag 0x210 data
2008-02-05 16:50:33 +01:00
*
2009-12-07 12:51:26 +01:00
* Return 0 on success , non - zero otherwise .
2005-04-16 15:20:36 -07:00
*/
2009-12-07 12:51:26 +01:00
static int diag210_to_senseid ( struct senseid * senseid , struct diag210 * diag )
2005-04-16 15:20:36 -07:00
{
static struct {
2008-02-05 16:50:33 +01:00
int class , type , cu_type ;
2005-04-16 15:20:36 -07:00
} 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 } ,
} ;
2008-02-05 16:50:33 +01:00
int i ;
2009-12-07 12:51:26 +01:00
/* 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 ;
}
}
2008-02-05 16:50:33 +01:00
2009-12-07 12:51:26 +01:00
return - ENODEV ;
2008-02-05 16:50:33 +01:00
}
/**
2009-12-07 12:51:26 +01:00
* diag_get_dev_info - retrieve device information via diag 0x210
* @ cdev : ccw device
2008-02-05 16:50:33 +01:00
*
* Returns zero on success , non - zero otherwise .
*/
2009-12-07 12:51:26 +01:00
static int diag210_get_dev_info ( struct ccw_device * cdev )
2008-02-05 16:50:33 +01:00
{
2009-12-07 12:51:26 +01:00
struct ccw_dev_id * dev_id = & cdev - > private - > dev_id ;
struct senseid * senseid = & cdev - > private - > senseid ;
2005-04-16 15:20:36 -07:00
struct diag210 diag_data ;
2009-12-07 12:51:26 +01:00
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 ) ;
2008-02-05 16:50:33 +01:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
}
/*
2009-12-07 12:51:26 +01:00
* Initialize SENSE ID data .
2005-04-16 15:20:36 -07:00
*/
2009-12-07 12:51:26 +01:00
static void snsid_init ( struct ccw_device * cdev )
2005-04-16 15:20:36 -07:00
{
2009-12-07 12:51:26 +01:00
cdev - > private - > flags . esid = 0 ;
memset ( & cdev - > private - > senseid , 0 , sizeof ( cdev - > private - > senseid ) ) ;
cdev - > private - > senseid . cu_type = 0xffff ;
2005-04-16 15:20:36 -07:00
}
/*
2009-12-07 12:51:26 +01:00
* Check for complete SENSE ID data .
2005-04-16 15:20:36 -07:00
*/
2009-12-07 12:51:26 +01:00
static int snsid_check ( struct ccw_device * cdev , void * data )
2005-04-16 15:20:36 -07:00
{
2009-12-07 12:51:26 +01:00
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 )
2005-04-16 15:20:36 -07:00
return - EOPNOTSUPP ;
2009-12-07 12:51:26 +01:00
/* Check for extended-identification information. */
if ( len > SENSE_ID_BASIC_LEN )
cdev - > private - > flags . esid = 1 ;
return 0 ;
2008-01-26 14:10:43 +01:00
2009-12-07 12:51:26 +01:00
out_restart :
snsid_init ( cdev ) ;
2005-04-16 15:20:36 -07:00
return - EAGAIN ;
}
/*
2009-12-07 12:51:26 +01:00
* Process SENSE ID request result .
2005-04-16 15:20:36 -07:00
*/
2009-12-07 12:51:26 +01:00
static void snsid_callback ( struct ccw_device * cdev , void * data , int rc )
2005-04-16 15:20:36 -07:00
{
2009-12-07 12:51:26 +01:00
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 ;
2005-04-16 15:20:36 -07:00
}
}
2009-12-07 12:51:26 +01:00
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 ) ;
}
2008-02-05 16:50:33 +01:00
2009-12-07 12:51:26 +01:00
/**
* 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 ) ;
2005-04-16 15:20:36 -07:00
}