2017-11-14 20:38:02 +03:00
// SPDX-License-Identifier: GPL-2.0
2005-04-17 02:20:36 +04:00
/*
* S / 390 common I / O routines - - low level i / o calls
*
2012-07-20 13:15:04 +04:00
* Copyright IBM Corp . 1999 , 2008
2005-04-17 02:20:36 +04:00
* Author ( s ) : Ingo Adlung ( adlung @ de . ibm . com )
2006-01-15 00:21:04 +03:00
* Cornelia Huck ( cornelia . huck @ de . ibm . com )
2005-04-17 02:20:36 +04:00
* Arnd Bergmann ( arndb @ de . ibm . com )
* Martin Schwidefsky ( schwidefsky @ de . ibm . com )
*/
2008-12-25 15:39:36 +03:00
# define KMSG_COMPONENT "cio"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2009-06-12 12:26:46 +04:00
# include <linux/ftrace.h>
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/device.h>
# include <linux/kernel_stat.h>
# include <linux/interrupt.h>
2014-03-05 02:57:29 +04:00
# include <linux/irq.h>
2005-04-17 02:20:36 +04:00
# include <asm/cio.h>
# include <asm/delay.h>
# include <asm/irq.h>
2006-10-06 18:38:35 +04:00
# include <asm/irq_regs.h>
2006-09-20 17:59:15 +04:00
# include <asm/setup.h>
2007-02-21 12:55:21 +03:00
# include <asm/ipl.h>
2007-04-27 18:01:31 +04:00
# include <asm/chpid.h>
2008-01-26 16:10:44 +03:00
# include <asm/airq.h>
2008-07-14 11:58:58 +04:00
# include <asm/isc.h>
2017-02-05 13:48:36 +03:00
# include <linux/sched/cputime.h>
2008-07-14 11:58:51 +04:00
# include <asm/fcx.h>
2009-03-26 17:24:01 +03:00
# include <asm/nmi.h>
# include <asm/crw.h>
2005-04-17 02:20:36 +04:00
# include "cio.h"
# include "css.h"
# include "chsc.h"
# include "ioasm.h"
2008-01-26 16:10:43 +03:00
# include "io_sch.h"
2005-04-17 02:20:36 +04:00
# include "blacklist.h"
# include "cio_debug.h"
2007-04-27 18:01:28 +04:00
# include "chp.h"
2015-12-18 14:59:36 +03:00
# include "trace.h"
2005-04-17 02:20:36 +04:00
debug_info_t * cio_debug_msg_id ;
debug_info_t * cio_debug_trace_id ;
debug_info_t * cio_debug_crw_id ;
2014-05-27 16:40:39 +04:00
DEFINE_PER_CPU_ALIGNED ( struct irb , cio_irb ) ;
EXPORT_PER_CPU_SYMBOL ( cio_irb ) ;
2005-04-17 02:20:36 +04:00
/*
* Function : cio_debug_init
2008-01-26 16:10:42 +03:00
* Initializes three debug logs for common I / O :
* - cio_msg logs generic cio messages
2005-04-17 02:20:36 +04:00
* - cio_trace logs the calling of different functions
2008-01-26 16:10:42 +03:00
* - cio_crw logs machine check related cio messages
2005-04-17 02:20:36 +04:00
*/
2008-01-26 16:10:42 +03:00
static int __init cio_debug_init ( void )
2005-04-17 02:20:36 +04:00
{
2014-02-17 14:16:10 +04:00
cio_debug_msg_id = debug_register ( " cio_msg " , 16 , 1 , 11 * sizeof ( long ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! cio_debug_msg_id )
goto out_unregister ;
2008-01-26 16:10:42 +03:00
debug_register_view ( cio_debug_msg_id , & debug_sprintf_view ) ;
debug_set_level ( cio_debug_msg_id , 2 ) ;
2008-01-26 16:11:30 +03:00
cio_debug_trace_id = debug_register ( " cio_trace " , 16 , 1 , 16 ) ;
2005-04-17 02:20:36 +04:00
if ( ! cio_debug_trace_id )
goto out_unregister ;
2008-01-26 16:10:42 +03:00
debug_register_view ( cio_debug_trace_id , & debug_hex_ascii_view ) ;
debug_set_level ( cio_debug_trace_id , 2 ) ;
2014-02-17 14:16:10 +04:00
cio_debug_crw_id = debug_register ( " cio_crw " , 8 , 1 , 8 * sizeof ( long ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! cio_debug_crw_id )
goto out_unregister ;
2008-01-26 16:10:42 +03:00
debug_register_view ( cio_debug_crw_id , & debug_sprintf_view ) ;
debug_set_level ( cio_debug_crw_id , 4 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
out_unregister :
2015-11-16 16:45:40 +03:00
debug_unregister ( cio_debug_msg_id ) ;
debug_unregister ( cio_debug_trace_id ) ;
debug_unregister ( cio_debug_crw_id ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
arch_initcall ( cio_debug_init ) ;
2011-03-15 19:08:25 +03:00
int cio_set_options ( struct subchannel * sch , int flags )
2005-04-17 02:20:36 +04:00
{
2011-03-15 19:08:25 +03:00
struct io_subchannel_private * priv = to_io_private ( sch ) ;
2005-04-17 02:20:36 +04:00
2011-03-15 19:08:25 +03:00
priv - > options . suspend = ( flags & DOIO_ALLOW_SUSPEND ) ! = 0 ;
priv - > options . prefetch = ( flags & DOIO_DENY_PREFETCH ) ! = 0 ;
priv - > options . inter = ( flags & DOIO_SUPPRESS_INTER ) ! = 0 ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
cio_start_handle_notoper ( struct subchannel * sch , __u8 lpm )
{
char dbf_text [ 15 ] ;
if ( lpm ! = 0 )
sch - > lpm & = ~ lpm ;
else
sch - > lpm = 0 ;
2008-05-07 11:22:54 +04:00
CIO_MSG_EVENT ( 2 , " cio_start: 'not oper' status for "
2006-01-06 11:19:25 +03:00
" subchannel 0.%x.%04x! \n " , sch - > schid . ssid ,
sch - > schid . sch_no ) ;
2008-12-25 15:39:12 +03:00
if ( cio_update_schib ( sch ) )
return - ENODEV ;
2008-10-10 23:33:09 +04:00
sprintf ( dbf_text , " no%s " , dev_name ( & sch - > dev ) ) ;
2005-04-17 02:20:36 +04:00
CIO_TRACE_EVENT ( 0 , dbf_text ) ;
CIO_HEX_EVENT ( 0 , & sch - > schib , sizeof ( struct schib ) ) ;
return ( sch - > lpm ? - EACCES : - ENODEV ) ;
}
int
cio_start_key ( struct subchannel * sch , /* subchannel structure */
struct ccw1 * cpa , /* logical channel prog addr */
__u8 lpm , /* logical path mask */
__u8 key ) /* storage key */
{
2011-03-15 19:08:25 +03:00
struct io_subchannel_private * priv = to_io_private ( sch ) ;
union orb * orb = & priv - > orb ;
2005-04-17 02:20:36 +04:00
int ccode ;
2009-09-11 12:28:18 +04:00
CIO_TRACE_EVENT ( 5 , " stIO " ) ;
CIO_TRACE_EVENT ( 5 , dev_name ( & sch - > dev ) ) ;
2005-04-17 02:20:36 +04:00
2008-09-16 20:32:19 +04:00
memset ( orb , 0 , sizeof ( union orb ) ) ;
2005-04-17 02:20:36 +04:00
/* sch is always under 2G. */
2008-07-14 11:58:51 +04:00
orb - > cmd . intparm = ( u32 ) ( addr_t ) sch ;
orb - > cmd . fmt = 1 ;
2005-04-17 02:20:36 +04:00
2011-03-15 19:08:25 +03:00
orb - > cmd . pfch = priv - > options . prefetch = = 0 ;
orb - > cmd . spnd = priv - > options . suspend ;
orb - > cmd . ssic = priv - > options . suspend & & priv - > options . inter ;
2008-07-14 11:58:51 +04:00
orb - > cmd . lpm = ( lpm ! = 0 ) ? lpm : sch - > lpm ;
2005-04-17 02:20:36 +04:00
/*
* for 64 bit we always support 64 bit IDAWs with 4 k page size only
*/
2008-07-14 11:58:51 +04:00
orb - > cmd . c64 = 1 ;
orb - > cmd . i2k = 0 ;
orb - > cmd . key = key > > 4 ;
2005-04-17 02:20:36 +04:00
/* issue "Start Subchannel" */
2008-07-14 11:58:51 +04:00
orb - > cmd . cpa = ( __u32 ) __pa ( cpa ) ;
2008-01-26 16:10:43 +03:00
ccode = ssch ( sch - > schid , orb ) ;
2005-04-17 02:20:36 +04:00
/* process condition code */
2009-09-11 12:28:18 +04:00
CIO_HEX_EVENT ( 5 , & ccode , sizeof ( ccode ) ) ;
2005-04-17 02:20:36 +04:00
switch ( ccode ) {
case 0 :
/*
* initialize device status information
*/
2008-07-14 11:58:50 +04:00
sch - > schib . scsw . cmd . actl | = SCSW_ACTL_START_PEND ;
2005-04-17 02:20:36 +04:00
return 0 ;
case 1 : /* status pending */
case 2 : /* busy */
return - EBUSY ;
2008-09-09 14:38:58 +04:00
case 3 : /* device/path not operational */
2005-04-17 02:20:36 +04:00
return cio_start_handle_notoper ( sch , lpm ) ;
2008-09-09 14:38:58 +04:00
default :
return ccode ;
2005-04-17 02:20:36 +04:00
}
}
2017-03-17 06:17:29 +03:00
EXPORT_SYMBOL_GPL ( cio_start_key ) ;
2005-04-17 02:20:36 +04:00
int
cio_start ( struct subchannel * sch , struct ccw1 * cpa , __u8 lpm )
{
2005-05-01 19:58:58 +04:00
return cio_start_key ( sch , cpa , lpm , PAGE_DEFAULT_KEY ) ;
2005-04-17 02:20:36 +04:00
}
2017-03-17 06:17:29 +03:00
EXPORT_SYMBOL_GPL ( cio_start ) ;
2005-04-17 02:20:36 +04:00
/*
* resume suspended I / O operation
*/
int
cio_resume ( struct subchannel * sch )
{
int ccode ;
2009-09-11 12:28:18 +04:00
CIO_TRACE_EVENT ( 4 , " resIO " ) ;
2008-10-10 23:33:09 +04:00
CIO_TRACE_EVENT ( 4 , dev_name ( & sch - > dev ) ) ;
2005-04-17 02:20:36 +04:00
2006-01-06 11:19:21 +03:00
ccode = rsch ( sch - > schid ) ;
2005-04-17 02:20:36 +04:00
2009-09-11 12:28:18 +04:00
CIO_HEX_EVENT ( 4 , & ccode , sizeof ( ccode ) ) ;
2005-04-17 02:20:36 +04:00
switch ( ccode ) {
case 0 :
2008-07-14 11:58:50 +04:00
sch - > schib . scsw . cmd . actl | = SCSW_ACTL_RESUME_PEND ;
2005-04-17 02:20:36 +04:00
return 0 ;
case 1 :
return - EBUSY ;
case 2 :
return - EINVAL ;
default :
/*
* useless to wait for request completion
* as device is no longer operational !
*/
return - ENODEV ;
}
}
2017-03-17 06:17:29 +03:00
EXPORT_SYMBOL_GPL ( cio_resume ) ;
2005-04-17 02:20:36 +04:00
/*
* halt I / O operation
*/
int
cio_halt ( struct subchannel * sch )
{
int ccode ;
if ( ! sch )
return - ENODEV ;
2009-09-11 12:28:18 +04:00
CIO_TRACE_EVENT ( 2 , " haltIO " ) ;
2008-10-10 23:33:09 +04:00
CIO_TRACE_EVENT ( 2 , dev_name ( & sch - > dev ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Issue " Halt subchannel " and process condition code
*/
2006-01-06 11:19:21 +03:00
ccode = hsch ( sch - > schid ) ;
2005-04-17 02:20:36 +04:00
2009-09-11 12:28:18 +04:00
CIO_HEX_EVENT ( 2 , & ccode , sizeof ( ccode ) ) ;
2005-04-17 02:20:36 +04:00
switch ( ccode ) {
case 0 :
2008-07-14 11:58:50 +04:00
sch - > schib . scsw . cmd . actl | = SCSW_ACTL_HALT_PEND ;
2005-04-17 02:20:36 +04:00
return 0 ;
case 1 : /* status pending */
case 2 : /* busy */
return - EBUSY ;
default : /* device not operational */
return - ENODEV ;
}
}
2017-03-17 06:17:29 +03:00
EXPORT_SYMBOL_GPL ( cio_halt ) ;
2005-04-17 02:20:36 +04:00
/*
* Clear I / O operation
*/
int
cio_clear ( struct subchannel * sch )
{
int ccode ;
if ( ! sch )
return - ENODEV ;
2009-09-11 12:28:18 +04:00
CIO_TRACE_EVENT ( 2 , " clearIO " ) ;
2008-10-10 23:33:09 +04:00
CIO_TRACE_EVENT ( 2 , dev_name ( & sch - > dev ) ) ;
2005-04-17 02:20:36 +04:00
/*
* Issue " Clear subchannel " and process condition code
*/
2006-01-06 11:19:21 +03:00
ccode = csch ( sch - > schid ) ;
2005-04-17 02:20:36 +04:00
2009-09-11 12:28:18 +04:00
CIO_HEX_EVENT ( 2 , & ccode , sizeof ( ccode ) ) ;
2005-04-17 02:20:36 +04:00
switch ( ccode ) {
case 0 :
2008-07-14 11:58:50 +04:00
sch - > schib . scsw . cmd . actl | = SCSW_ACTL_CLEAR_PEND ;
2005-04-17 02:20:36 +04:00
return 0 ;
default : /* device not operational */
return - ENODEV ;
}
}
2017-03-17 06:17:29 +03:00
EXPORT_SYMBOL_GPL ( cio_clear ) ;
2005-04-17 02:20:36 +04:00
/*
* Function : cio_cancel
* Issues a " Cancel Subchannel " on the specified subchannel
* Note : We don ' t need any fancy intparms and flags here
* since xsch is executed synchronously .
* Only for common I / O internal use as for now .
*/
int
cio_cancel ( struct subchannel * sch )
{
int ccode ;
if ( ! sch )
return - ENODEV ;
2009-09-11 12:28:18 +04:00
CIO_TRACE_EVENT ( 2 , " cancelIO " ) ;
2008-10-10 23:33:09 +04:00
CIO_TRACE_EVENT ( 2 , dev_name ( & sch - > dev ) ) ;
2005-04-17 02:20:36 +04:00
2006-01-06 11:19:21 +03:00
ccode = xsch ( sch - > schid ) ;
2005-04-17 02:20:36 +04:00
2009-09-11 12:28:18 +04:00
CIO_HEX_EVENT ( 2 , & ccode , sizeof ( ccode ) ) ;
2005-04-17 02:20:36 +04:00
switch ( ccode ) {
case 0 : /* success */
/* Update information in scsw. */
2008-12-25 15:39:12 +03:00
if ( cio_update_schib ( sch ) )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
return 0 ;
case 1 : /* status pending */
return - EBUSY ;
case 2 : /* not applicable */
return - EINVAL ;
default : /* not oper */
return - ENODEV ;
}
}
2017-03-17 06:17:29 +03:00
EXPORT_SYMBOL_GPL ( cio_cancel ) ;
2005-04-17 02:20:36 +04:00
2017-03-17 06:17:28 +03:00
/**
* cio_cancel_halt_clear - Cancel running I / O by performing cancel , halt
* and clear ordinally if subchannel is valid .
* @ sch : subchannel on which to perform the cancel_halt_clear operation
* @ iretry : the number of the times remained to retry the next operation
*
* This should be called repeatedly since halt / clear are asynchronous
* operations . We do one try with cio_cancel , three tries with cio_halt ,
* 255 tries with cio_clear . The caller should initialize @ iretry with
* the value 255 for its first call to this , and keep using the same
* @ iretry in the subsequent calls until it gets a non - EBUSY return .
*
* Returns 0 if device now idle , - ENODEV for device not operational ,
* - EBUSY if an interrupt is expected ( either from halt / clear or from a
* status pending ) , and - EIO if out of retries .
*/
int cio_cancel_halt_clear ( struct subchannel * sch , int * iretry )
{
int ret ;
if ( cio_update_schib ( sch ) )
return - ENODEV ;
if ( ! sch - > schib . pmcw . ena )
/* Not operational -> done. */
return 0 ;
/* Stage 1: cancel io. */
if ( ! ( scsw_actl ( & sch - > schib . scsw ) & SCSW_ACTL_HALT_PEND ) & &
! ( scsw_actl ( & sch - > schib . scsw ) & SCSW_ACTL_CLEAR_PEND ) ) {
if ( ! scsw_is_tm ( & sch - > schib . scsw ) ) {
ret = cio_cancel ( sch ) ;
if ( ret ! = - EINVAL )
return ret ;
}
/*
* Cancel io unsuccessful or not applicable ( transport mode ) .
* Continue with asynchronous instructions .
*/
* iretry = 3 ; /* 3 halt retries. */
}
/* Stage 2: halt io. */
if ( ! ( scsw_actl ( & sch - > schib . scsw ) & SCSW_ACTL_CLEAR_PEND ) ) {
if ( * iretry ) {
* iretry - = 1 ;
ret = cio_halt ( sch ) ;
if ( ret ! = - EBUSY )
return ( ret = = 0 ) ? - EBUSY : ret ;
}
/* Halt io unsuccessful. */
* iretry = 255 ; /* 255 clear retries. */
}
/* Stage 3: clear io. */
if ( * iretry ) {
* iretry - = 1 ;
ret = cio_clear ( sch ) ;
return ( ret = = 0 ) ? - EBUSY : ret ;
}
/* Function was unsuccessful */
return - EIO ;
}
2017-03-17 06:17:29 +03:00
EXPORT_SYMBOL_GPL ( cio_cancel_halt_clear ) ;
2008-12-25 15:39:13 +03:00
static void cio_apply_config ( struct subchannel * sch , struct schib * schib )
{
schib - > pmcw . intparm = sch - > config . intparm ;
schib - > pmcw . mbi = sch - > config . mbi ;
schib - > pmcw . isc = sch - > config . isc ;
schib - > pmcw . ena = sch - > config . ena ;
schib - > pmcw . mme = sch - > config . mme ;
schib - > pmcw . mp = sch - > config . mp ;
schib - > pmcw . csense = sch - > config . csense ;
schib - > pmcw . mbfc = sch - > config . mbfc ;
if ( sch - > config . mbfc )
schib - > mba = sch - > config . mba ;
}
static int cio_check_config ( struct subchannel * sch , struct schib * schib )
{
return ( schib - > pmcw . intparm = = sch - > config . intparm ) & &
( schib - > pmcw . mbi = = sch - > config . mbi ) & &
( schib - > pmcw . isc = = sch - > config . isc ) & &
( schib - > pmcw . ena = = sch - > config . ena ) & &
( schib - > pmcw . mme = = sch - > config . mme ) & &
( schib - > pmcw . mp = = sch - > config . mp ) & &
( schib - > pmcw . csense = = sch - > config . csense ) & &
( schib - > pmcw . mbfc = = sch - > config . mbfc ) & &
( ! sch - > config . mbfc | | ( schib - > mba = = sch - > config . mba ) ) ;
}
2005-04-17 02:20:36 +04:00
/*
2008-12-25 15:39:13 +03:00
* cio_commit_config - apply configuration to the subchannel
2005-04-17 02:20:36 +04:00
*/
2008-12-25 15:39:13 +03:00
int cio_commit_config ( struct subchannel * sch )
2005-04-17 02:20:36 +04:00
{
2008-12-25 15:39:13 +03:00
int ccode , retry , ret = 0 ;
2014-02-05 16:36:05 +04:00
struct schib schib ;
struct irb irb ;
2008-12-25 15:39:13 +03:00
2015-12-18 14:58:47 +03:00
if ( stsch ( sch - > schid , & schib ) | | ! css_sch_is_valid ( & schib ) )
2008-12-25 15:39:13 +03:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
for ( retry = 0 ; retry < 5 ; retry + + ) {
2008-12-25 15:39:13 +03:00
/* copy desired changes to local schib */
cio_apply_config ( sch , & schib ) ;
2015-12-18 14:58:47 +03:00
ccode = msch ( sch - > schid , & schib ) ;
2008-12-25 15:39:13 +03:00
if ( ccode < 0 ) /* -EIO if msch gets a program check. */
2005-04-17 02:20:36 +04:00
return ccode ;
switch ( ccode ) {
2009-01-08 05:09:16 +03:00
case 0 : /* successful */
2015-12-18 14:58:47 +03:00
if ( stsch ( sch - > schid , & schib ) | |
2008-12-25 15:39:13 +03:00
! css_sch_is_valid ( & schib ) )
return - ENODEV ;
if ( cio_check_config ( sch , & schib ) ) {
/* commit changes from local schib */
memcpy ( & sch - > schib , & schib , sizeof ( schib ) ) ;
return 0 ;
}
ret = - EAGAIN ;
break ;
case 1 : /* status pending */
2014-02-05 16:36:05 +04:00
ret = - EBUSY ;
if ( tsch ( sch - > schid , & irb ) )
return ret ;
break ;
2008-12-25 15:39:13 +03:00
case 2 : /* busy */
udelay ( 100 ) ; /* allow for recovery */
2005-04-17 02:20:36 +04:00
ret = - EBUSY ;
break ;
2008-12-25 15:39:13 +03:00
case 3 : /* not operational */
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
}
return ret ;
}
2017-03-17 06:17:29 +03:00
EXPORT_SYMBOL_GPL ( cio_commit_config ) ;
2005-04-17 02:20:36 +04:00
2008-12-25 15:39:12 +03:00
/**
* cio_update_schib - Perform stsch and update schib if subchannel is valid .
* @ sch : subchannel on which to perform stsch
* Return zero on success , - ENODEV otherwise .
*/
int cio_update_schib ( struct subchannel * sch )
{
struct schib schib ;
2015-12-18 14:58:47 +03:00
if ( stsch ( sch - > schid , & schib ) | | ! css_sch_is_valid ( & schib ) )
2008-12-25 15:39:12 +03:00
return - ENODEV ;
memcpy ( & sch - > schib , & schib , sizeof ( schib ) ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( cio_update_schib ) ;
2008-07-14 11:58:47 +04:00
/**
* cio_enable_subchannel - enable a subchannel .
* @ sch : subchannel to be enabled
* @ intparm : interruption parameter to set
2005-04-17 02:20:36 +04:00
*/
2008-04-30 15:38:39 +04:00
int cio_enable_subchannel ( struct subchannel * sch , u32 intparm )
2005-04-17 02:20:36 +04:00
{
int ret ;
2009-09-11 12:28:18 +04:00
CIO_TRACE_EVENT ( 2 , " ensch " ) ;
2008-10-10 23:33:09 +04:00
CIO_TRACE_EVENT ( 2 , dev_name ( & sch - > dev ) ) ;
2005-04-17 02:20:36 +04:00
2006-12-08 17:54:28 +03:00
if ( sch_is_pseudo_sch ( sch ) )
return - EINVAL ;
2008-12-25 15:39:12 +03:00
if ( cio_update_schib ( sch ) )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2008-12-25 15:39:13 +03:00
sch - > config . ena = 1 ;
sch - > config . isc = sch - > isc ;
sch - > config . intparm = intparm ;
2014-02-05 16:36:05 +04:00
ret = cio_commit_config ( sch ) ;
if ( ret = = - EIO ) {
/*
* Got a program check in msch . Try without
* the concurrent sense bit the next time .
*/
sch - > config . csense = 0 ;
2008-12-25 15:39:13 +03:00
ret = cio_commit_config ( sch ) ;
2005-04-17 02:20:36 +04:00
}
2009-09-11 12:28:18 +04:00
CIO_HEX_EVENT ( 2 , & ret , sizeof ( ret ) ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2008-07-14 11:58:47 +04:00
EXPORT_SYMBOL_GPL ( cio_enable_subchannel ) ;
2005-04-17 02:20:36 +04:00
2008-07-14 11:58:47 +04:00
/**
* cio_disable_subchannel - disable a subchannel .
* @ sch : subchannel to disable
2005-04-17 02:20:36 +04:00
*/
2008-07-14 11:58:47 +04:00
int cio_disable_subchannel ( struct subchannel * sch )
2005-04-17 02:20:36 +04:00
{
int ret ;
2009-09-11 12:28:18 +04:00
CIO_TRACE_EVENT ( 2 , " dissch " ) ;
2008-10-10 23:33:09 +04:00
CIO_TRACE_EVENT ( 2 , dev_name ( & sch - > dev ) ) ;
2005-04-17 02:20:36 +04:00
2006-12-08 17:54:28 +03:00
if ( sch_is_pseudo_sch ( sch ) )
return 0 ;
2008-12-25 15:39:12 +03:00
if ( cio_update_schib ( sch ) )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2008-12-25 15:39:13 +03:00
sch - > config . ena = 0 ;
2014-02-05 16:36:05 +04:00
ret = cio_commit_config ( sch ) ;
2008-12-25 15:39:13 +03:00
2009-09-11 12:28:18 +04:00
CIO_HEX_EVENT ( 2 , & ret , sizeof ( ret ) ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2008-07-14 11:58:47 +04:00
EXPORT_SYMBOL_GPL ( cio_disable_subchannel ) ;
2005-04-17 02:20:36 +04:00
/*
2013-06-27 11:01:09 +04:00
* do_cio_interrupt ( ) handles all normal I / O device IRQ ' s
2005-04-17 02:20:36 +04:00
*/
2013-06-27 11:01:09 +04:00
static irqreturn_t do_cio_interrupt ( int irq , void * dummy )
2005-04-17 02:20:36 +04:00
{
2013-06-27 11:01:09 +04:00
struct tpi_info * tpi_info ;
2005-04-17 02:20:36 +04:00
struct subchannel * sch ;
struct irb * irb ;
2014-09-30 19:37:52 +04:00
set_cpu_flag ( CIF_NOHZ_DELAY ) ;
2013-06-27 11:01:09 +04:00
tpi_info = ( struct tpi_info * ) & get_irq_regs ( ) - > int_code ;
2015-12-18 14:59:36 +03:00
trace_s390_cio_interrupt ( tpi_info ) ;
2014-08-17 21:30:46 +04:00
irb = this_cpu_ptr ( & cio_irb ) ;
2013-06-17 16:54:02 +04:00
sch = ( struct subchannel * ) ( unsigned long ) tpi_info - > intparm ;
if ( ! sch ) {
/* Clear pending interrupt condition. */
inc_irq_stat ( IRQIO_CIO ) ;
tsch ( tpi_info - > schid , irb ) ;
2013-06-27 11:01:09 +04:00
return IRQ_HANDLED ;
2013-06-17 16:54:02 +04:00
}
spin_lock ( sch - > lock ) ;
/* Store interrupt response block to lowcore. */
if ( tsch ( tpi_info - > schid , irb ) = = 0 ) {
/* Keep subchannel information word up to date. */
memcpy ( & sch - > schib . scsw , & irb - > scsw , sizeof ( irb - > scsw ) ) ;
/* Call interrupt handler if there is one. */
if ( sch - > driver & & sch - > driver - > irq )
sch - > driver - > irq ( sch ) ;
else
2013-01-02 18:18:18 +04:00
inc_irq_stat ( IRQIO_CIO ) ;
2013-06-17 16:54:02 +04:00
} else
inc_irq_stat ( IRQIO_CIO ) ;
spin_unlock ( sch - > lock ) ;
2013-06-27 11:01:09 +04:00
return IRQ_HANDLED ;
}
void __init init_cio_interrupts ( void )
{
irq_set_chip_and_handler ( IO_INTERRUPT ,
& dummy_irq_chip , handle_percpu_irq ) ;
2020-03-04 03:50:48 +03:00
if ( request_irq ( IO_INTERRUPT , do_cio_interrupt , 0 , " I/O " , NULL ) )
panic ( " Failed to register I/O interrupt \n " ) ;
2005-04-17 02:20:36 +04:00
}
# ifdef CONFIG_CCW_CONSOLE
2013-04-13 15:01:50 +04:00
static struct subchannel * console_sch ;
2014-06-03 17:03:09 +04:00
static struct lock_class_key console_sch_key ;
2005-04-17 02:20:36 +04:00
2008-12-25 15:39:01 +03:00
/*
2012-05-09 18:27:36 +04:00
* Use cio_tsch to update the subchannel status and call the interrupt handler
2013-04-13 14:53:21 +04:00
* if status had been pending . Called with the subchannel ' s lock held .
2008-12-25 15:39:01 +03:00
*/
2013-04-13 14:53:21 +04:00
void cio_tsch ( struct subchannel * sch )
2008-12-25 15:39:01 +03:00
{
struct irb * irb ;
int irq_context ;
2014-08-17 21:30:46 +04:00
irb = this_cpu_ptr ( & cio_irb ) ;
2008-12-25 15:39:01 +03:00
/* Store interrupt response block to lowcore. */
2012-05-09 18:27:36 +04:00
if ( tsch ( sch - > schid , irb ) ! = 0 )
2008-12-25 15:39:01 +03:00
/* Not status pending or not operational. */
2012-05-09 18:27:36 +04:00
return ;
memcpy ( & sch - > schib . scsw , & irb - > scsw , sizeof ( union scsw ) ) ;
/* Call interrupt handler with updated status. */
2008-12-25 15:39:01 +03:00
irq_context = in_interrupt ( ) ;
2012-05-09 18:27:36 +04:00
if ( ! irq_context ) {
2008-12-25 15:39:01 +03:00
local_bh_disable ( ) ;
2012-05-09 18:27:36 +04:00
irq_enter ( ) ;
}
2014-02-24 01:40:17 +04:00
kstat_incr_irq_this_cpu ( IO_INTERRUPT ) ;
2008-12-25 15:39:01 +03:00
if ( sch - > driver & & sch - > driver - > irq )
sch - > driver - > irq ( sch ) ;
2011-10-30 18:16:04 +04:00
else
2013-01-02 18:18:18 +04:00
inc_irq_stat ( IRQIO_CIO ) ;
2012-05-09 18:27:36 +04:00
if ( ! irq_context ) {
irq_exit ( ) ;
2008-12-25 15:39:01 +03:00
_local_bh_enable ( ) ;
2012-05-09 18:27:36 +04:00
}
2008-12-25 15:39:01 +03:00
}
2013-04-13 15:01:50 +04:00
static int cio_test_for_console ( struct subchannel_id schid , void * data )
2008-01-26 16:10:43 +03:00
{
2013-04-13 15:01:50 +04:00
struct schib schib ;
2008-01-26 16:10:43 +03:00
2015-12-18 14:58:47 +03:00
if ( stsch ( schid , & schib ) ! = 0 )
2006-01-06 11:19:22 +03:00
return - ENXIO ;
2013-04-13 15:01:50 +04:00
if ( ( schib . pmcw . st = = SUBCHANNEL_TYPE_IO ) & & schib . pmcw . dnv & &
( schib . pmcw . dev = = console_devno ) ) {
2006-01-06 11:19:22 +03:00
console_irq = schid . sch_no ;
return 1 ; /* found */
}
return 0 ;
}
2013-04-13 15:01:50 +04:00
static int cio_get_console_sch_no ( void )
2005-04-17 02:20:36 +04:00
{
2006-01-06 11:19:21 +03:00
struct subchannel_id schid ;
2013-04-13 15:01:50 +04:00
struct schib schib ;
2006-01-06 11:19:21 +03:00
init_subchannel_id ( & schid ) ;
2005-04-17 02:20:36 +04:00
if ( console_irq ! = - 1 ) {
/* VM provided us with the irq number of the console. */
2006-01-06 11:19:21 +03:00
schid . sch_no = console_irq ;
2015-12-18 14:58:47 +03:00
if ( stsch ( schid , & schib ) ! = 0 | |
2013-04-13 15:01:50 +04:00
( schib . pmcw . st ! = SUBCHANNEL_TYPE_IO ) | | ! schib . pmcw . dnv )
2005-04-17 02:20:36 +04:00
return - 1 ;
2013-04-13 15:01:50 +04:00
console_devno = schib . pmcw . dev ;
2005-04-17 02:20:36 +04:00
} else if ( console_devno ! = - 1 ) {
/* At least the console device number is known. */
2006-01-06 11:19:22 +03:00
for_each_subchannel ( cio_test_for_console , NULL ) ;
2005-04-17 02:20:36 +04:00
}
return console_irq ;
}
2013-04-13 15:01:50 +04:00
struct subchannel * cio_probe_console ( void )
2005-04-17 02:20:36 +04:00
{
2006-01-06 11:19:21 +03:00
struct subchannel_id schid ;
2013-04-13 15:01:50 +04:00
struct subchannel * sch ;
2018-06-26 16:09:32 +03:00
struct schib schib ;
2013-04-13 15:01:50 +04:00
int sch_no , ret ;
2005-04-17 02:20:36 +04:00
2006-01-06 11:19:22 +03:00
sch_no = cio_get_console_sch_no ( ) ;
if ( sch_no = = - 1 ) {
2016-03-04 07:49:57 +03:00
pr_warn ( " No CCW console was found \n " ) ;
2005-04-17 02:20:36 +04:00
return ERR_PTR ( - ENODEV ) ;
}
2006-01-06 11:19:21 +03:00
init_subchannel_id ( & schid ) ;
2006-01-06 11:19:22 +03:00
schid . sch_no = sch_no ;
2018-06-26 16:09:32 +03:00
ret = stsch ( schid , & schib ) ;
if ( ret )
return ERR_PTR ( - ENODEV ) ;
sch = css_alloc_subchannel ( schid , & schib ) ;
2013-04-13 15:01:50 +04:00
if ( IS_ERR ( sch ) )
return sch ;
2005-04-17 02:20:36 +04:00
2014-06-03 17:03:09 +04:00
lockdep_set_class ( sch - > lock , & console_sch_key ) ;
2008-07-14 11:59:01 +04:00
isc_register ( CONSOLE_ISC ) ;
2013-04-13 15:01:50 +04:00
sch - > config . isc = CONSOLE_ISC ;
sch - > config . intparm = ( u32 ) ( addr_t ) sch ;
ret = cio_commit_config ( sch ) ;
2005-04-17 02:20:36 +04:00
if ( ret ) {
2008-07-14 11:59:01 +04:00
isc_unregister ( CONSOLE_ISC ) ;
2013-04-13 15:01:50 +04:00
put_device ( & sch - > dev ) ;
2005-04-17 02:20:36 +04:00
return ERR_PTR ( ret ) ;
}
2013-04-13 15:01:50 +04:00
console_sch = sch ;
return sch ;
2005-04-17 02:20:36 +04:00
}
2013-04-13 15:01:50 +04:00
int cio_is_console ( struct subchannel_id schid )
2005-04-17 02:20:36 +04:00
{
2013-04-13 15:01:50 +04:00
if ( ! console_sch )
2005-04-17 02:20:36 +04:00
return 0 ;
2013-04-13 15:01:50 +04:00
return schid_equal ( & schid , & console_sch - > schid ) ;
2005-04-17 02:20:36 +04:00
}
2013-04-13 15:03:54 +04:00
void cio_register_early_subchannels ( void )
2005-04-17 02:20:36 +04:00
{
2013-04-13 15:03:54 +04:00
int ret ;
if ( ! console_sch )
return ;
ret = css_register_subchannel ( console_sch ) ;
if ( ret )
put_device ( & console_sch - > dev ) ;
2005-04-17 02:20:36 +04:00
}
2013-04-13 15:01:50 +04:00
# endif /* CONFIG_CCW_CONSOLE */
2005-04-17 02:20:36 +04:00
2008-07-14 11:58:51 +04:00
/**
* cio_tm_start_key - perform start function
* @ sch : subchannel on which to perform the start function
* @ tcw : transport - command word to be started
* @ lpm : mask of paths to use
* @ key : storage key to use for storage access
*
* Start the tcw on the given subchannel . Return zero on success , non - zero
* otherwise .
*/
int cio_tm_start_key ( struct subchannel * sch , struct tcw * tcw , u8 lpm , u8 key )
{
int cc ;
union orb * orb = & to_io_private ( sch ) - > orb ;
memset ( orb , 0 , sizeof ( union orb ) ) ;
orb - > tm . intparm = ( u32 ) ( addr_t ) sch ;
orb - > tm . key = key > > 4 ;
orb - > tm . b = 1 ;
orb - > tm . lpm = lpm ? lpm : sch - > lpm ;
orb - > tm . tcw = ( u32 ) ( addr_t ) tcw ;
cc = ssch ( sch - > schid , orb ) ;
switch ( cc ) {
case 0 :
return 0 ;
case 1 :
case 2 :
return - EBUSY ;
default :
return cio_start_handle_notoper ( sch , lpm ) ;
}
}
2017-03-17 06:17:29 +03:00
EXPORT_SYMBOL_GPL ( cio_tm_start_key ) ;
2008-07-14 11:58:51 +04:00
/**
* cio_tm_intrg - perform interrogate function
2018-01-29 14:55:29 +03:00
* @ sch : subchannel on which to perform the interrogate function
2008-07-14 11:58:51 +04:00
*
* If the specified subchannel is running in transport - mode , perform the
* interrogate function . Return zero on success , non - zero otherwie .
*/
int cio_tm_intrg ( struct subchannel * sch )
{
int cc ;
if ( ! to_io_private ( sch ) - > orb . tm . b )
return - EINVAL ;
cc = xsch ( sch - > schid ) ;
switch ( cc ) {
case 0 :
case 2 :
return 0 ;
case 1 :
return - EBUSY ;
default :
return - ENODEV ;
}
}
2017-03-17 06:17:29 +03:00
EXPORT_SYMBOL_GPL ( cio_tm_intrg ) ;