2005-04-17 02:20:36 +04:00
/*
* drivers / s390 / cio / cio . c
* S / 390 common I / O routines - - low level i / o calls
*
2008-07-14 11:58:43 +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 )
*/
# 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>
# 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>
2006-12-04 17:40:26 +03:00
# include <asm/reset.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>
2008-04-17 09:46:23 +04:00
# include <asm/cpu.h>
2008-07-14 11:58:51 +04:00
# include <asm/fcx.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"
2006-12-04 17:40:26 +03:00
# include "../s390mach.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 ;
/*
* 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
{
2008-01-26 16:11:30 +03:00
cio_debug_msg_id = debug_register ( " cio_msg " , 16 , 1 , 16 * 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 ) ;
2008-01-26 16:11:30 +03:00
cio_debug_crw_id = debug_register ( " cio_crw " , 16 , 1 , 16 * 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 :
if ( cio_debug_msg_id )
2008-01-26 16:10:42 +03:00
debug_unregister ( cio_debug_msg_id ) ;
2005-04-17 02:20:36 +04:00
if ( cio_debug_trace_id )
2008-01-26 16:10:42 +03:00
debug_unregister ( cio_debug_trace_id ) ;
2005-04-17 02:20:36 +04:00
if ( cio_debug_crw_id )
2008-01-26 16:10:42 +03:00
debug_unregister ( cio_debug_crw_id ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
arch_initcall ( cio_debug_init ) ;
int
cio_set_options ( struct subchannel * sch , int flags )
{
sch - > options . suspend = ( flags & DOIO_ALLOW_SUSPEND ) ! = 0 ;
sch - > options . prefetch = ( flags & DOIO_DENY_PREFETCH ) ! = 0 ;
sch - > options . inter = ( flags & DOIO_SUPPRESS_INTER ) ! = 0 ;
return 0 ;
}
/* FIXME: who wants to use this? */
int
cio_get_options ( struct subchannel * sch )
{
int flags ;
flags = 0 ;
if ( sch - > options . suspend )
flags | = DOIO_ALLOW_SUSPEND ;
if ( sch - > options . prefetch )
flags | = DOIO_DENY_PREFETCH ;
if ( sch - > options . inter )
flags | = DOIO_SUPPRESS_INTER ;
return flags ;
}
/*
* Use tpi to get a pending interrupt , call the interrupt handler and
* return a pointer to the subchannel structure .
*/
2007-02-05 23:18:53 +03:00
static int
2005-04-17 02:20:36 +04:00
cio_tpi ( void )
{
struct tpi_info * tpi_info ;
struct subchannel * sch ;
struct irb * irb ;
[S390] cio: fix cio_tpi.
In cio_tpi only disable bottom halves when not in interrupt context.
Otherwise a WARN_ON gets triggered. Besides that, when we are in
interrupt context bottom halves are disabled anyway.
Fixes this one:
Badness at kernel/softirq.c:77
Modules linked in:
CPU: 2 Not tainted 2.6.26 #4
Process swapper (pid: 0, task: 000000003fe83db0, ksp: 000000003fea7d28)
Krnl PSW : 0404c00180000000 0000000000053f4e (__local_bh_disable+0xbe/0xcc)
R:0 T:1 IO:0 EX:0 Key:0 M:1 W:0 P:0 AS:3 CC:0 PM:0 EA:3
Krnl GPRS: 0000000000008ee0 00000000005f95e0 0000000000000000 0000000000000001
000000000020be92 0000000000000000 0000000000000210 00000000005d36c0
000000003fb5f4d8 0000000000000000 000000000020bed0 000000003fb5f3c8
00000000009be920 0000000000364898 000000003fb5f408 000000003fb5f3c8
Krnl Code: 0000000000053f42: bf2f1000 icm %r2,15,0(%r1)
0000000000053f46: a774ffc5 brc 7,53ed0
0000000000053f4a: a7f40001 brc 15,53f4c
>0000000000053f4e: a7280001 lhi %r2,1
0000000000053f52: 50201000 st %r2,0(%r1)
0000000000053f56: a7f4ffbd brc 15,53ed0
0000000000053f5a: 0707 bcr 0,%r7
0000000000053f5c: a7f13fc0 tmll %r15,16320
Call Trace:
([<0000000000000210>] 0x210)
[<0000000000053f86>] local_bh_disable+0x2a/0x38
[<000000000020bed0>] wait_cons_dev+0xd4/0x154
[<0000000000247cb2>] raw3215_make_room+0x6a/0x1a8
[<000000000024861a>] raw3215_write+0x86/0x28c
[<00000000002488a0>] con3215_write+0x80/0x110
[<000000000004c3e0>] __call_console_drivers+0xc8/0xe4
[<000000000004c47e>] _call_console_drivers+0x82/0xc4
[<000000000004c744>] release_console_sem+0x218/0x2c0
[<000000000004cf64>] vprintk+0x3c0/0x504
[<0000000000354a4a>] printk+0x52/0x64
[<0000000000088004>] __print_symbol+0x40/0x50
[<0000000000071dbc>] print_stack_trace+0x78/0xac
[<0000000000079e78>] print_lock_dependencies+0x148/0x208
[<000000000007a050>] print_irq_inversion_bug+0x118/0x15c
[<000000000007a106>] check_usage_forwards+0x72/0x84
[<000000000007a36e>] mark_lock+0x1d2/0x594
[<000000000007baca>] __lock_acquire+0x886/0xf48
[<000000000007c234>] lock_acquire+0xa8/0xe0
[<0000000000350316>] _write_lock+0x56/0x98
[<000000000026cd92>] zfcp_erp_adapter_reopen+0x4e/0x8c
[<000000000026f1e8>] zfcp_qdio_int_resp+0x2e4/0x2f4
[<00000000002210f4>] qdio_int_handler+0x274/0x888
[<00000000002177b6>] ccw_device_call_handler+0x6e/0xd8
[<0000000000215336>] ccw_device_irq+0xd6/0x160
[<0000000000212f88>] io_subchannel_irq+0x8c/0x118
[<000000000020c120>] do_IRQ+0x1d0/0x1fc
[<00000000000270b2>] io_return+0x0/0x8
[<000000000001c8a4>] cpu_idle+0x178/0x21c
([<000000000001c884>] cpu_idle+0x158/0x21c)
[<00000000003483a2>] start_secondary+0xb6/0xc8
INFO: lockdep is turned off.
Last Breaking-Event-Address:
[<0000000000053f4a>] __local_bh_disable+0xba/0xcc
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
2008-10-10 23:33:17 +04:00
int irq_context ;
2005-04-17 02:20:36 +04:00
tpi_info = ( struct tpi_info * ) __LC_SUBCHANNEL_ID ;
if ( tpi ( NULL ) ! = 1 )
return 0 ;
irb = ( struct irb * ) __LC_IRB ;
/* Store interrupt response block to lowcore. */
2006-01-06 11:19:21 +03:00
if ( tsch ( tpi_info - > schid , irb ) ! = 0 )
2005-04-17 02:20:36 +04:00
/* Not status pending or not operational. */
return 1 ;
sch = ( struct subchannel * ) ( unsigned long ) tpi_info - > intparm ;
if ( ! sch )
return 1 ;
[S390] cio: fix cio_tpi.
In cio_tpi only disable bottom halves when not in interrupt context.
Otherwise a WARN_ON gets triggered. Besides that, when we are in
interrupt context bottom halves are disabled anyway.
Fixes this one:
Badness at kernel/softirq.c:77
Modules linked in:
CPU: 2 Not tainted 2.6.26 #4
Process swapper (pid: 0, task: 000000003fe83db0, ksp: 000000003fea7d28)
Krnl PSW : 0404c00180000000 0000000000053f4e (__local_bh_disable+0xbe/0xcc)
R:0 T:1 IO:0 EX:0 Key:0 M:1 W:0 P:0 AS:3 CC:0 PM:0 EA:3
Krnl GPRS: 0000000000008ee0 00000000005f95e0 0000000000000000 0000000000000001
000000000020be92 0000000000000000 0000000000000210 00000000005d36c0
000000003fb5f4d8 0000000000000000 000000000020bed0 000000003fb5f3c8
00000000009be920 0000000000364898 000000003fb5f408 000000003fb5f3c8
Krnl Code: 0000000000053f42: bf2f1000 icm %r2,15,0(%r1)
0000000000053f46: a774ffc5 brc 7,53ed0
0000000000053f4a: a7f40001 brc 15,53f4c
>0000000000053f4e: a7280001 lhi %r2,1
0000000000053f52: 50201000 st %r2,0(%r1)
0000000000053f56: a7f4ffbd brc 15,53ed0
0000000000053f5a: 0707 bcr 0,%r7
0000000000053f5c: a7f13fc0 tmll %r15,16320
Call Trace:
([<0000000000000210>] 0x210)
[<0000000000053f86>] local_bh_disable+0x2a/0x38
[<000000000020bed0>] wait_cons_dev+0xd4/0x154
[<0000000000247cb2>] raw3215_make_room+0x6a/0x1a8
[<000000000024861a>] raw3215_write+0x86/0x28c
[<00000000002488a0>] con3215_write+0x80/0x110
[<000000000004c3e0>] __call_console_drivers+0xc8/0xe4
[<000000000004c47e>] _call_console_drivers+0x82/0xc4
[<000000000004c744>] release_console_sem+0x218/0x2c0
[<000000000004cf64>] vprintk+0x3c0/0x504
[<0000000000354a4a>] printk+0x52/0x64
[<0000000000088004>] __print_symbol+0x40/0x50
[<0000000000071dbc>] print_stack_trace+0x78/0xac
[<0000000000079e78>] print_lock_dependencies+0x148/0x208
[<000000000007a050>] print_irq_inversion_bug+0x118/0x15c
[<000000000007a106>] check_usage_forwards+0x72/0x84
[<000000000007a36e>] mark_lock+0x1d2/0x594
[<000000000007baca>] __lock_acquire+0x886/0xf48
[<000000000007c234>] lock_acquire+0xa8/0xe0
[<0000000000350316>] _write_lock+0x56/0x98
[<000000000026cd92>] zfcp_erp_adapter_reopen+0x4e/0x8c
[<000000000026f1e8>] zfcp_qdio_int_resp+0x2e4/0x2f4
[<00000000002210f4>] qdio_int_handler+0x274/0x888
[<00000000002177b6>] ccw_device_call_handler+0x6e/0xd8
[<0000000000215336>] ccw_device_irq+0xd6/0x160
[<0000000000212f88>] io_subchannel_irq+0x8c/0x118
[<000000000020c120>] do_IRQ+0x1d0/0x1fc
[<00000000000270b2>] io_return+0x0/0x8
[<000000000001c8a4>] cpu_idle+0x178/0x21c
([<000000000001c884>] cpu_idle+0x158/0x21c)
[<00000000003483a2>] start_secondary+0xb6/0xc8
INFO: lockdep is turned off.
Last Breaking-Event-Address:
[<0000000000053f4a>] __local_bh_disable+0xba/0xcc
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
2008-10-10 23:33:17 +04:00
irq_context = in_interrupt ( ) ;
if ( ! irq_context )
local_bh_disable ( ) ;
2005-04-17 02:20:36 +04:00
irq_enter ( ) ;
2006-12-08 17:54:26 +03:00
spin_lock ( sch - > lock ) ;
2008-07-14 11:58:50 +04:00
memcpy ( & sch - > schib . scsw , & irb - > scsw , sizeof ( union scsw ) ) ;
2005-04-17 02:20:36 +04:00
if ( sch - > driver & & sch - > driver - > irq )
2008-01-26 16:10:39 +03:00
sch - > driver - > irq ( sch ) ;
2006-12-08 17:54:26 +03:00
spin_unlock ( sch - > lock ) ;
2005-04-17 02:20:36 +04:00
irq_exit ( ) ;
[S390] cio: fix cio_tpi.
In cio_tpi only disable bottom halves when not in interrupt context.
Otherwise a WARN_ON gets triggered. Besides that, when we are in
interrupt context bottom halves are disabled anyway.
Fixes this one:
Badness at kernel/softirq.c:77
Modules linked in:
CPU: 2 Not tainted 2.6.26 #4
Process swapper (pid: 0, task: 000000003fe83db0, ksp: 000000003fea7d28)
Krnl PSW : 0404c00180000000 0000000000053f4e (__local_bh_disable+0xbe/0xcc)
R:0 T:1 IO:0 EX:0 Key:0 M:1 W:0 P:0 AS:3 CC:0 PM:0 EA:3
Krnl GPRS: 0000000000008ee0 00000000005f95e0 0000000000000000 0000000000000001
000000000020be92 0000000000000000 0000000000000210 00000000005d36c0
000000003fb5f4d8 0000000000000000 000000000020bed0 000000003fb5f3c8
00000000009be920 0000000000364898 000000003fb5f408 000000003fb5f3c8
Krnl Code: 0000000000053f42: bf2f1000 icm %r2,15,0(%r1)
0000000000053f46: a774ffc5 brc 7,53ed0
0000000000053f4a: a7f40001 brc 15,53f4c
>0000000000053f4e: a7280001 lhi %r2,1
0000000000053f52: 50201000 st %r2,0(%r1)
0000000000053f56: a7f4ffbd brc 15,53ed0
0000000000053f5a: 0707 bcr 0,%r7
0000000000053f5c: a7f13fc0 tmll %r15,16320
Call Trace:
([<0000000000000210>] 0x210)
[<0000000000053f86>] local_bh_disable+0x2a/0x38
[<000000000020bed0>] wait_cons_dev+0xd4/0x154
[<0000000000247cb2>] raw3215_make_room+0x6a/0x1a8
[<000000000024861a>] raw3215_write+0x86/0x28c
[<00000000002488a0>] con3215_write+0x80/0x110
[<000000000004c3e0>] __call_console_drivers+0xc8/0xe4
[<000000000004c47e>] _call_console_drivers+0x82/0xc4
[<000000000004c744>] release_console_sem+0x218/0x2c0
[<000000000004cf64>] vprintk+0x3c0/0x504
[<0000000000354a4a>] printk+0x52/0x64
[<0000000000088004>] __print_symbol+0x40/0x50
[<0000000000071dbc>] print_stack_trace+0x78/0xac
[<0000000000079e78>] print_lock_dependencies+0x148/0x208
[<000000000007a050>] print_irq_inversion_bug+0x118/0x15c
[<000000000007a106>] check_usage_forwards+0x72/0x84
[<000000000007a36e>] mark_lock+0x1d2/0x594
[<000000000007baca>] __lock_acquire+0x886/0xf48
[<000000000007c234>] lock_acquire+0xa8/0xe0
[<0000000000350316>] _write_lock+0x56/0x98
[<000000000026cd92>] zfcp_erp_adapter_reopen+0x4e/0x8c
[<000000000026f1e8>] zfcp_qdio_int_resp+0x2e4/0x2f4
[<00000000002210f4>] qdio_int_handler+0x274/0x888
[<00000000002177b6>] ccw_device_call_handler+0x6e/0xd8
[<0000000000215336>] ccw_device_irq+0xd6/0x160
[<0000000000212f88>] io_subchannel_irq+0x8c/0x118
[<000000000020c120>] do_IRQ+0x1d0/0x1fc
[<00000000000270b2>] io_return+0x0/0x8
[<000000000001c8a4>] cpu_idle+0x178/0x21c
([<000000000001c884>] cpu_idle+0x158/0x21c)
[<00000000003483a2>] start_secondary+0xb6/0xc8
INFO: lockdep is turned off.
Last Breaking-Event-Address:
[<0000000000053f4a>] __local_bh_disable+0xba/0xcc
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
2008-10-10 23:33:17 +04:00
if ( ! irq_context )
_local_bh_enable ( ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
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 ;
2006-01-06 11:19:21 +03:00
stsch ( sch - > schid , & sch - > schib ) ;
2005-04-17 02:20:36 +04:00
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-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 */
{
char dbf_txt [ 15 ] ;
int ccode ;
2008-07-14 11:58:51 +04:00
union orb * orb ;
2005-04-17 02:20:36 +04:00
2008-01-26 16:10:43 +03:00
CIO_TRACE_EVENT ( 4 , " stIO " ) ;
2008-10-10 23:33:09 +04:00
CIO_TRACE_EVENT ( 4 , dev_name ( & sch - > dev ) ) ;
2005-04-17 02:20:36 +04:00
2008-01-26 16:10:43 +03:00
orb = & to_io_private ( sch ) - > orb ;
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
2008-07-14 11:58:51 +04:00
orb - > cmd . pfch = sch - > options . prefetch = = 0 ;
orb - > cmd . spnd = sch - > options . suspend ;
orb - > cmd . ssic = sch - > options . suspend & & sch - > options . inter ;
orb - > cmd . lpm = ( lpm ! = 0 ) ? lpm : sch - > lpm ;
2006-01-06 11:19:28 +03:00
# ifdef CONFIG_64BIT
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 ;
2005-04-17 02:20:36 +04:00
# endif
2008-07-14 11:58:51 +04:00
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 */
2008-01-26 16:10:43 +03:00
sprintf ( dbf_txt , " ccode:%d " , ccode ) ;
CIO_TRACE_EVENT ( 4 , dbf_txt ) ;
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
}
}
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
}
/*
* resume suspended I / O operation
*/
int
cio_resume ( struct subchannel * sch )
{
char dbf_txt [ 15 ] ;
int ccode ;
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
sprintf ( dbf_txt , " ccode:%d " , ccode ) ;
CIO_TRACE_EVENT ( 4 , dbf_txt ) ;
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 ;
}
}
/*
* halt I / O operation
*/
int
cio_halt ( struct subchannel * sch )
{
char dbf_txt [ 15 ] ;
int ccode ;
if ( ! sch )
return - ENODEV ;
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
sprintf ( dbf_txt , " ccode:%d " , ccode ) ;
CIO_TRACE_EVENT ( 2 , dbf_txt ) ;
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 ;
}
}
/*
* Clear I / O operation
*/
int
cio_clear ( struct subchannel * sch )
{
char dbf_txt [ 15 ] ;
int ccode ;
if ( ! sch )
return - ENODEV ;
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
sprintf ( dbf_txt , " ccode:%d " , ccode ) ;
CIO_TRACE_EVENT ( 2 , dbf_txt ) ;
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 ;
}
}
/*
* 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 )
{
char dbf_txt [ 15 ] ;
int ccode ;
if ( ! sch )
return - ENODEV ;
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
sprintf ( dbf_txt , " ccode:%d " , ccode ) ;
CIO_TRACE_EVENT ( 2 , dbf_txt ) ;
switch ( ccode ) {
case 0 : /* success */
/* Update information in scsw. */
2006-01-06 11:19:21 +03:00
stsch ( sch - > schid , & sch - > schib ) ;
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 ;
}
}
/*
* Function : cio_modify
* Issues a " Modify Subchannel " on the specified subchannel
*/
int
cio_modify ( struct subchannel * sch )
{
int ccode , retry , ret ;
ret = 0 ;
for ( retry = 0 ; retry < 5 ; retry + + ) {
2006-01-06 11:19:21 +03:00
ccode = msch_err ( sch - > schid , & sch - > schib ) ;
2005-04-17 02:20:36 +04:00
if ( ccode < 0 ) /* -EIO if msch gets a program check. */
return ccode ;
switch ( ccode ) {
case 0 : /* successfull */
return 0 ;
case 1 : /* status pending */
return - EBUSY ;
case 2 : /* busy */
udelay ( 100 ) ; /* allow for recovery */
ret = - EBUSY ;
break ;
case 3 : /* not operational */
return - ENODEV ;
}
}
return ret ;
}
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
{
char dbf_txt [ 15 ] ;
int ccode ;
int retry ;
int ret ;
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 ;
2006-01-06 11:19:21 +03:00
ccode = stsch ( sch - > schid , & sch - > schib ) ;
2005-04-17 02:20:36 +04:00
if ( ccode )
return - ENODEV ;
for ( retry = 5 , ret = 0 ; retry > 0 ; retry - - ) {
sch - > schib . pmcw . ena = 1 ;
2008-04-30 15:38:39 +04:00
sch - > schib . pmcw . isc = sch - > isc ;
2008-01-26 16:10:45 +03:00
sch - > schib . pmcw . intparm = intparm ;
2005-04-17 02:20:36 +04:00
ret = cio_modify ( sch ) ;
if ( ret = = - ENODEV )
break ;
if ( ret = = - EIO )
/*
* Got a program check in cio_modify . Try without
* the concurrent sense bit the next time .
*/
sch - > schib . pmcw . csense = 0 ;
if ( ret = = 0 ) {
2006-01-06 11:19:21 +03:00
stsch ( sch - > schid , & sch - > schib ) ;
2005-04-17 02:20:36 +04:00
if ( sch - > schib . pmcw . ena )
break ;
}
if ( ret = = - EBUSY ) {
struct irb irb ;
2006-01-06 11:19:21 +03:00
if ( tsch ( sch - > schid , & irb ) ! = 0 )
2005-04-17 02:20:36 +04:00
break ;
}
}
sprintf ( dbf_txt , " ret:%d " , ret ) ;
CIO_TRACE_EVENT ( 2 , dbf_txt ) ;
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
{
char dbf_txt [ 15 ] ;
int ccode ;
int retry ;
int ret ;
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 ;
2006-01-06 11:19:21 +03:00
ccode = stsch ( sch - > schid , & sch - > schib ) ;
2005-04-17 02:20:36 +04:00
if ( ccode = = 3 ) /* Not operational. */
return - ENODEV ;
2008-07-14 11:58:50 +04:00
if ( scsw_actl ( & sch - > schib . scsw ) ! = 0 )
2005-04-17 02:20:36 +04:00
/*
* the disable function must not be called while there are
* requests pending for completion !
*/
return - EBUSY ;
for ( retry = 5 , ret = 0 ; retry > 0 ; retry - - ) {
sch - > schib . pmcw . ena = 0 ;
ret = cio_modify ( sch ) ;
if ( ret = = - ENODEV )
break ;
if ( ret = = - EBUSY )
/*
* The subchannel is busy or status pending .
* We ' ll disable when the next interrupt was delivered
* via the state machine .
*/
break ;
if ( ret = = 0 ) {
2006-01-06 11:19:21 +03:00
stsch ( sch - > schid , & sch - > schib ) ;
2005-04-17 02:20:36 +04:00
if ( ! sch - > schib . pmcw . ena )
break ;
}
}
sprintf ( dbf_txt , " ret:%d " , ret ) ;
CIO_TRACE_EVENT ( 2 , dbf_txt ) ;
return ret ;
}
2008-07-14 11:58:47 +04:00
EXPORT_SYMBOL_GPL ( cio_disable_subchannel ) ;
2005-04-17 02:20:36 +04:00
2006-12-08 17:54:28 +03:00
int cio_create_sch_lock ( struct subchannel * sch )
2006-12-08 17:54:26 +03:00
{
sch - > lock = kmalloc ( sizeof ( spinlock_t ) , GFP_KERNEL ) ;
if ( ! sch - > lock )
return - ENOMEM ;
spin_lock_init ( sch - > lock ) ;
return 0 ;
}
2008-07-14 11:58:48 +04:00
static int cio_check_devno_blacklisted ( struct subchannel * sch )
2008-07-14 11:58:43 +04:00
{
if ( is_blacklisted ( sch - > schid . ssid , sch - > schib . pmcw . dev ) ) {
/*
* This device must not be known to Linux . So we simply
* say that there is no device and return ENODEV .
*/
CIO_MSG_EVENT ( 6 , " Blacklisted device detected "
" at devno %04X, subchannel set %x \n " ,
sch - > schib . pmcw . dev , sch - > schid . ssid ) ;
return - ENODEV ;
}
return 0 ;
}
2008-07-14 11:58:48 +04:00
static int cio_validate_io_subchannel ( struct subchannel * sch )
{
/* Initialization for io subchannels. */
if ( ! css_sch_is_valid ( & sch - > schib ) )
return - ENODEV ;
/* Devno is valid. */
return cio_check_devno_blacklisted ( sch ) ;
}
static int cio_validate_msg_subchannel ( struct subchannel * sch )
{
/* Initialization for message subchannels. */
if ( ! css_sch_is_valid ( & sch - > schib ) )
return - ENODEV ;
/* Devno is valid. */
return cio_check_devno_blacklisted ( sch ) ;
}
2008-07-14 11:58:43 +04:00
/**
* cio_validate_subchannel - basic validation of subchannel
* @ sch : subchannel structure to be filled out
* @ schid : subchannel id
2005-04-17 02:20:36 +04:00
*
* Find out subchannel type and initialize struct subchannel .
* Return codes :
2008-07-14 11:58:43 +04:00
* 0 on success
2005-04-17 02:20:36 +04:00
* - ENXIO for non - defined subchannels
2008-07-14 11:58:43 +04:00
* - ENODEV for invalid subchannels or blacklisted devices
* - EIO for subchannels in an invalid subchannel set
2005-04-17 02:20:36 +04:00
*/
2008-07-14 11:58:43 +04:00
int cio_validate_subchannel ( struct subchannel * sch , struct subchannel_id schid )
2005-04-17 02:20:36 +04:00
{
char dbf_txt [ 15 ] ;
int ccode ;
2006-12-08 17:54:26 +03:00
int err ;
2005-04-17 02:20:36 +04:00
2008-07-14 11:58:43 +04:00
sprintf ( dbf_txt , " valsch%x " , schid . sch_no ) ;
CIO_TRACE_EVENT ( 4 , dbf_txt ) ;
2005-04-17 02:20:36 +04:00
/* Nuke all fields. */
memset ( sch , 0 , sizeof ( struct subchannel ) ) ;
2006-12-08 17:54:26 +03:00
sch - > schid = schid ;
if ( cio_is_console ( schid ) ) {
sch - > lock = cio_get_console_lock ( ) ;
} else {
err = cio_create_sch_lock ( sch ) ;
if ( err )
goto out ;
}
2006-07-12 18:39:50 +04:00
mutex_init ( & sch - > reg_mutex ) ;
2005-04-17 02:20:36 +04:00
/* Set a name for the subchannel */
2008-10-10 23:33:14 +04:00
if ( cio_is_console ( schid ) )
sch - > dev . init_name = cio_get_console_sch_name ( schid ) ;
else
dev_set_name ( & sch - > dev , " 0.%x.%04x " , schid . ssid , schid . sch_no ) ;
2005-04-17 02:20:36 +04:00
/*
* The first subchannel that is not - operational ( ccode = = 3 )
* indicates that there aren ' t any more devices available .
2006-01-06 11:19:25 +03:00
* If stsch gets an exception , it means the current subchannel set
* is not valid .
2005-04-17 02:20:36 +04:00
*/
2006-01-06 11:19:25 +03:00
ccode = stsch_err ( schid , & sch - > schib ) ;
2006-12-08 17:54:26 +03:00
if ( ccode ) {
err = ( ccode = = 3 ) ? - ENXIO : ccode ;
goto out ;
}
2005-04-17 02:20:36 +04:00
/* Copy subchannel type from path management control word. */
sch - > st = sch - > schib . pmcw . st ;
2008-07-14 11:58:45 +04:00
2008-07-14 11:58:43 +04:00
switch ( sch - > st ) {
case SUBCHANNEL_TYPE_IO :
err = cio_validate_io_subchannel ( sch ) ;
break ;
2008-07-14 11:58:48 +04:00
case SUBCHANNEL_TYPE_MSG :
err = cio_validate_msg_subchannel ( sch ) ;
break ;
2008-07-14 11:58:43 +04:00
default :
err = 0 ;
2005-04-17 02:20:36 +04:00
}
2008-07-14 11:58:43 +04:00
if ( err )
2008-01-26 16:10:51 +03:00
goto out ;
2008-07-14 11:58:43 +04:00
CIO_MSG_EVENT ( 4 , " Subchannel 0.%x.%04x reports subchannel type %04X \n " ,
sch - > schid . ssid , sch - > schid . sch_no , sch - > st ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2006-12-08 17:54:26 +03:00
out :
if ( ! cio_is_console ( schid ) )
kfree ( sch - > lock ) ;
sch - > lock = NULL ;
return err ;
2005-04-17 02:20:36 +04:00
}
/*
* do_IRQ ( ) handles all normal I / O device IRQ ' s ( the special
* SMP cross - CPU interrupts have their own specific
* handlers ) .
*
*/
void
do_IRQ ( struct pt_regs * regs )
{
struct tpi_info * tpi_info ;
struct subchannel * sch ;
struct irb * irb ;
2006-10-06 18:38:35 +04:00
struct pt_regs * old_regs ;
2005-04-17 02:20:36 +04:00
2006-10-06 18:38:35 +04:00
old_regs = set_irq_regs ( regs ) ;
2006-10-11 17:31:26 +04:00
irq_enter ( ) ;
2008-04-17 09:46:23 +04:00
s390_idle_check ( ) ;
2008-04-17 09:46:25 +04:00
if ( S390_lowcore . int_clock > = S390_lowcore . clock_comparator )
/* Serve timer interrupts first. */
clock_comparator_work ( ) ;
2005-04-17 02:20:36 +04:00
/*
* Get interrupt information from lowcore
*/
tpi_info = ( struct tpi_info * ) __LC_SUBCHANNEL_ID ;
irb = ( struct irb * ) __LC_IRB ;
do {
kstat_cpu ( smp_processor_id ( ) ) . irqs [ IO_INTERRUPT ] + + ;
/*
* Non I / O - subchannel thin interrupts are processed differently
*/
if ( tpi_info - > adapter_IO = = 1 & &
tpi_info - > int_type = = IO_INTERRUPT_TYPE ) {
2008-07-14 11:58:59 +04:00
do_adapter_IO ( tpi_info - > isc ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
sch = ( struct subchannel * ) ( unsigned long ) tpi_info - > intparm ;
2008-04-17 09:46:26 +04:00
if ( ! sch ) {
/* Clear pending interrupt condition. */
tsch ( tpi_info - > schid , irb ) ;
continue ;
}
spin_lock ( sch - > lock ) ;
2005-04-17 02:20:36 +04:00
/* Store interrupt response block to lowcore. */
2008-04-17 09:46:26 +04:00
if ( tsch ( tpi_info - > schid , irb ) = = 0 ) {
2005-04-17 02:20:36 +04:00
/* 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 )
2008-01-26 16:10:39 +03:00
sch - > driver - > irq ( sch ) ;
2005-04-17 02:20:36 +04:00
}
2008-04-17 09:46:26 +04:00
spin_unlock ( sch - > lock ) ;
2005-04-17 02:20:36 +04:00
/*
* Are more interrupts pending ?
* If so , the tpi instruction will update the lowcore
* to hold the info for the next interrupt .
* We don ' t do this for VM because a tpi drops the cpu
* out of the sie which costs more cycles than it saves .
*/
} while ( ! MACHINE_IS_VM & & tpi ( NULL ) ! = 0 ) ;
2006-10-11 17:31:26 +04:00
irq_exit ( ) ;
2006-10-06 18:38:35 +04:00
set_irq_regs ( old_regs ) ;
2005-04-17 02:20:36 +04:00
}
# ifdef CONFIG_CCW_CONSOLE
static struct subchannel console_subchannel ;
2008-10-10 23:33:14 +04:00
static char console_sch_name [ 10 ] = " 0.x.xxxx " ;
2008-01-26 16:10:43 +03:00
static struct io_subchannel_private console_priv ;
2005-04-17 02:20:36 +04:00
static int console_subchannel_in_use ;
2008-01-26 16:10:43 +03:00
void * cio_get_console_priv ( void )
{
return & console_priv ;
}
2005-04-17 02:20:36 +04:00
/*
* busy wait for the next interrupt on the console
*/
2008-04-17 09:46:26 +04:00
void wait_cons_dev ( void )
__releases ( console_subchannel . lock )
__acquires ( console_subchannel . lock )
2005-04-17 02:20:36 +04:00
{
unsigned long cr6 __attribute__ ( ( aligned ( 8 ) ) ) ;
unsigned long save_cr6 __attribute__ ( ( aligned ( 8 ) ) ) ;
/*
* before entering the spinlock we may already have
* processed the interrupt on a different CPU . . .
*/
if ( ! console_subchannel_in_use )
return ;
2008-07-14 11:58:58 +04:00
/* disable all but the console isc */
2005-04-17 02:20:36 +04:00
__ctl_store ( save_cr6 , 6 , 6 ) ;
2008-07-14 11:58:58 +04:00
cr6 = 1UL < < ( 31 - CONSOLE_ISC ) ;
2005-04-17 02:20:36 +04:00
__ctl_load ( cr6 , 6 , 6 ) ;
do {
2006-12-08 17:54:26 +03:00
spin_unlock ( console_subchannel . lock ) ;
2005-04-17 02:20:36 +04:00
if ( ! cio_tpi ( ) )
cpu_relax ( ) ;
2006-12-08 17:54:26 +03:00
spin_lock ( console_subchannel . lock ) ;
2008-07-14 11:58:50 +04:00
} while ( console_subchannel . schib . scsw . cmd . actl ! = 0 ) ;
2005-04-17 02:20:36 +04:00
/*
* restore previous isc value
*/
__ctl_load ( save_cr6 , 6 , 6 ) ;
}
static int
2006-01-06 11:19:22 +03:00
cio_test_for_console ( struct subchannel_id schid , void * data )
{
2006-01-06 11:19:25 +03:00
if ( stsch_err ( schid , & console_subchannel . schib ) ! = 0 )
2006-01-06 11:19:22 +03:00
return - ENXIO ;
2008-01-26 16:10:45 +03:00
if ( ( console_subchannel . schib . pmcw . st = = SUBCHANNEL_TYPE_IO ) & &
console_subchannel . schib . pmcw . dnv & &
( console_subchannel . schib . pmcw . dev = = console_devno ) ) {
2006-01-06 11:19:22 +03:00
console_irq = schid . sch_no ;
return 1 ; /* found */
}
return 0 ;
}
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 ;
2005-04-17 02:20:36 +04:00
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 ;
if ( stsch ( schid , & console_subchannel . schib ) ! = 0 | |
2008-01-26 16:10:45 +03:00
( console_subchannel . schib . pmcw . st ! = SUBCHANNEL_TYPE_IO ) | |
2005-04-17 02:20:36 +04:00
! console_subchannel . schib . pmcw . dnv )
return - 1 ;
console_devno = console_subchannel . schib . pmcw . dev ;
} 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
if ( console_irq = = - 1 )
return - 1 ;
} else {
/* unlike in 2.4, we cannot autoprobe here, since
* the channel subsystem is not fully initialized .
* With some luck , the HWC console can take over */
return - 1 ;
}
return console_irq ;
}
struct subchannel *
cio_probe_console ( void )
{
2006-01-06 11:19:22 +03:00
int sch_no , ret ;
2006-01-06 11:19:21 +03:00
struct subchannel_id schid ;
2005-04-17 02:20:36 +04:00
if ( xchg ( & console_subchannel_in_use , 1 ) ! = 0 )
return ERR_PTR ( - EBUSY ) ;
2006-01-06 11:19:22 +03:00
sch_no = cio_get_console_sch_no ( ) ;
if ( sch_no = = - 1 ) {
2005-04-17 02:20:36 +04:00
console_subchannel_in_use = 0 ;
2008-07-14 11:59:22 +04:00
printk ( KERN_WARNING " cio: No ccw console found! \n " ) ;
2005-04-17 02:20:36 +04:00
return ERR_PTR ( - ENODEV ) ;
}
memset ( & console_subchannel , 0 , sizeof ( struct subchannel ) ) ;
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 ;
2006-01-06 11:19:21 +03:00
ret = cio_validate_subchannel ( & console_subchannel , schid ) ;
2005-04-17 02:20:36 +04:00
if ( ret ) {
console_subchannel_in_use = 0 ;
return ERR_PTR ( - ENODEV ) ;
}
/*
2008-07-14 11:58:58 +04:00
* enable console I / O - interrupt subclass
2005-04-17 02:20:36 +04:00
*/
2008-07-14 11:59:01 +04:00
isc_register ( CONSOLE_ISC ) ;
2008-07-14 11:58:58 +04:00
console_subchannel . schib . pmcw . isc = CONSOLE_ISC ;
2005-04-17 02:20:36 +04:00
console_subchannel . schib . pmcw . intparm =
2008-01-26 16:10:43 +03:00
( u32 ) ( addr_t ) & console_subchannel ;
2005-04-17 02:20:36 +04:00
ret = cio_modify ( & console_subchannel ) ;
if ( ret ) {
2008-07-14 11:59:01 +04:00
isc_unregister ( CONSOLE_ISC ) ;
2005-04-17 02:20:36 +04:00
console_subchannel_in_use = 0 ;
return ERR_PTR ( ret ) ;
}
return & console_subchannel ;
}
void
cio_release_console ( void )
{
console_subchannel . schib . pmcw . intparm = 0 ;
cio_modify ( & console_subchannel ) ;
2008-07-14 11:59:01 +04:00
isc_unregister ( CONSOLE_ISC ) ;
2005-04-17 02:20:36 +04:00
console_subchannel_in_use = 0 ;
}
/* Bah... hack to catch console special sausages. */
int
2006-01-06 11:19:21 +03:00
cio_is_console ( struct subchannel_id schid )
2005-04-17 02:20:36 +04:00
{
if ( ! console_subchannel_in_use )
return 0 ;
2006-01-06 11:19:21 +03:00
return schid_equal ( & schid , & console_subchannel . schid ) ;
2005-04-17 02:20:36 +04:00
}
struct subchannel *
cio_get_console_subchannel ( void )
{
if ( ! console_subchannel_in_use )
2006-07-12 18:41:55 +04:00
return NULL ;
2005-04-17 02:20:36 +04:00
return & console_subchannel ;
}
2008-10-10 23:33:14 +04:00
const char * cio_get_console_sch_name ( struct subchannel_id schid )
{
snprintf ( console_sch_name , 10 , " 0.%x.%04x " , schid . ssid , schid . sch_no ) ;
return ( const char * ) console_sch_name ;
}
2005-04-17 02:20:36 +04:00
# endif
2007-02-05 23:18:53 +03:00
static int
2006-01-06 11:19:21 +03:00
__disable_subchannel_easy ( struct subchannel_id schid , struct schib * schib )
2005-04-17 02:20:36 +04:00
{
int retry , cc ;
cc = 0 ;
for ( retry = 0 ; retry < 3 ; retry + + ) {
schib - > pmcw . ena = 0 ;
cc = msch ( schid , schib ) ;
if ( cc )
return ( cc = = 3 ? - ENODEV : - EBUSY ) ;
stsch ( schid , schib ) ;
if ( ! schib - > pmcw . ena )
return 0 ;
}
return - EBUSY ; /* uhm... */
}
2007-02-05 23:18:53 +03:00
static int
2008-07-14 11:58:43 +04:00
__clear_io_subchannel_easy ( struct subchannel_id schid )
2005-04-17 02:20:36 +04:00
{
int retry ;
if ( csch ( schid ) )
return - ENODEV ;
for ( retry = 0 ; retry < 20 ; retry + + ) {
struct tpi_info ti ;
if ( tpi ( & ti ) ) {
2006-01-06 11:19:21 +03:00
tsch ( ti . schid , ( struct irb * ) __LC_IRB ) ;
if ( schid_equal ( & ti . schid , & schid ) )
2005-09-04 02:58:01 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-10-10 23:33:22 +04:00
udelay_simple ( 100 ) ;
2005-04-17 02:20:36 +04:00
}
return - EBUSY ;
}
2008-07-14 11:59:05 +04:00
static void __clear_chsc_subchannel_easy ( void )
{
/* It seems we can only wait for a bit here :/ */
2008-10-10 23:33:22 +04:00
udelay_simple ( 100 ) ;
2008-07-14 11:59:05 +04:00
}
2006-12-15 19:18:22 +03:00
static int pgm_check_occured ;
static void cio_reset_pgm_check_handler ( void )
{
pgm_check_occured = 1 ;
}
2008-10-10 23:33:15 +04:00
static int stsch_reset ( struct subchannel_id schid , struct schib * addr )
2006-12-15 19:18:22 +03:00
{
int rc ;
pgm_check_occured = 0 ;
2007-02-05 23:18:37 +03:00
s390_base_pgm_handler_fn = cio_reset_pgm_check_handler ;
2007-01-09 12:18:41 +03:00
rc = stsch ( schid , addr ) ;
2007-02-05 23:18:37 +03:00
s390_base_pgm_handler_fn = NULL ;
2006-12-28 02:35:36 +03:00
2007-02-05 23:18:37 +03:00
/* The program check handler could have changed pgm_check_occured. */
2007-01-09 12:18:41 +03:00
barrier ( ) ;
2006-12-28 02:35:36 +03:00
2006-12-15 19:18:22 +03:00
if ( pgm_check_occured )
return - EIO ;
else
return rc ;
}
2006-12-04 17:40:26 +03:00
static int __shutdown_subchannel_easy ( struct subchannel_id schid , void * data )
2006-01-06 11:19:22 +03:00
{
struct schib schib ;
2006-12-15 19:18:22 +03:00
if ( stsch_reset ( schid , & schib ) )
2006-01-06 11:19:22 +03:00
return - ENXIO ;
if ( ! schib . pmcw . ena )
return 0 ;
switch ( __disable_subchannel_easy ( schid , & schib ) ) {
case 0 :
case - ENODEV :
break ;
default : /* -EBUSY */
2008-07-14 11:58:43 +04:00
switch ( schib . pmcw . st ) {
case SUBCHANNEL_TYPE_IO :
if ( __clear_io_subchannel_easy ( schid ) )
goto out ; /* give up... */
break ;
2008-07-14 11:59:05 +04:00
case SUBCHANNEL_TYPE_CHSC :
__clear_chsc_subchannel_easy ( ) ;
break ;
2008-07-14 11:58:43 +04:00
default :
/* No default clear strategy */
break ;
}
2006-01-06 11:19:22 +03:00
stsch ( schid , & schib ) ;
__disable_subchannel_easy ( schid , & schib ) ;
}
2008-07-14 11:58:43 +04:00
out :
2006-01-06 11:19:22 +03:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2006-12-04 17:40:26 +03:00
static atomic_t chpid_reset_count ;
static void s390_reset_chpids_mcck_handler ( void )
{
struct crw crw ;
struct mci * mci ;
/* Check for pending channel report word. */
mci = ( struct mci * ) & S390_lowcore . mcck_interruption_code ;
if ( ! mci - > cp )
return ;
/* Process channel report words. */
while ( stcrw ( & crw ) = = 0 ) {
/* Check for responses to RCHP. */
if ( crw . slct & & crw . rsc = = CRW_RSC_CPATH )
atomic_dec ( & chpid_reset_count ) ;
}
}
# define RCHP_TIMEOUT (30 * USEC_PER_SEC)
static void css_reset ( void )
{
int i , ret ;
unsigned long long timeout ;
2007-04-27 18:01:26 +04:00
struct chp_id chpid ;
2006-12-04 17:40:26 +03:00
/* Reset subchannels. */
for_each_subchannel ( __shutdown_subchannel_easy , NULL ) ;
/* Reset channel paths. */
2007-02-05 23:18:37 +03:00
s390_base_mcck_handler_fn = s390_reset_chpids_mcck_handler ;
2006-12-04 17:40:26 +03:00
/* Enable channel report machine checks. */
__ctl_set_bit ( 14 , 28 ) ;
/* Temporarily reenable machine checks. */
local_mcck_enable ( ) ;
2007-04-27 18:01:26 +04:00
chp_id_init ( & chpid ) ;
2006-12-04 17:40:26 +03:00
for ( i = 0 ; i < = __MAX_CHPID ; i + + ) {
2007-04-27 18:01:26 +04:00
chpid . id = i ;
ret = rchp ( chpid ) ;
2006-12-04 17:40:26 +03:00
if ( ( ret = = 0 ) | | ( ret = = 2 ) )
/*
* rchp either succeeded , or another rchp is already
* in progress . In either case , we ' ll get a crw .
*/
atomic_inc ( & chpid_reset_count ) ;
}
/* Wait for machine check for all channel paths. */
timeout = get_clock ( ) + ( RCHP_TIMEOUT < < 12 ) ;
while ( atomic_read ( & chpid_reset_count ) ! = 0 ) {
if ( get_clock ( ) > timeout )
break ;
cpu_relax ( ) ;
}
/* Disable machine checks again. */
local_mcck_disable ( ) ;
/* Disable channel report machine checks. */
__ctl_clear_bit ( 14 , 28 ) ;
2007-02-05 23:18:37 +03:00
s390_base_mcck_handler_fn = NULL ;
2006-12-04 17:40:26 +03:00
}
static struct reset_call css_reset_call = {
. fn = css_reset ,
} ;
static int __init init_css_reset_call ( void )
{
atomic_set ( & chpid_reset_count , 0 ) ;
register_reset_call ( & css_reset_call ) ;
return 0 ;
}
arch_initcall ( init_css_reset_call ) ;
struct sch_match_id {
struct subchannel_id schid ;
struct ccw_dev_id devid ;
int rc ;
} ;
static int __reipl_subchannel_match ( struct subchannel_id schid , void * data )
{
struct schib schib ;
struct sch_match_id * match_id = data ;
2006-12-15 19:18:22 +03:00
if ( stsch_reset ( schid , & schib ) )
2006-12-04 17:40:26 +03:00
return - ENXIO ;
2008-01-26 16:10:45 +03:00
if ( ( schib . pmcw . st = = SUBCHANNEL_TYPE_IO ) & & schib . pmcw . dnv & &
2006-12-04 17:40:26 +03:00
( schib . pmcw . dev = = match_id - > devid . devno ) & &
( schid . ssid = = match_id - > devid . ssid ) ) {
match_id - > schid = schid ;
match_id - > rc = 0 ;
return 1 ;
}
return 0 ;
}
static int reipl_find_schid ( struct ccw_dev_id * devid ,
struct subchannel_id * schid )
2005-04-17 02:20:36 +04:00
{
2006-09-20 17:58:49 +04:00
struct sch_match_id match_id ;
match_id . devid = * devid ;
match_id . rc = - ENODEV ;
2006-12-04 17:40:26 +03:00
for_each_subchannel ( __reipl_subchannel_match , & match_id ) ;
2006-09-20 17:58:49 +04:00
if ( match_id . rc = = 0 )
* schid = match_id . schid ;
return match_id . rc ;
2005-04-17 02:20:36 +04:00
}
2006-09-20 17:58:49 +04:00
extern void do_reipl_asm ( __u32 schid ) ;
2005-04-17 02:20:36 +04:00
/* Make sure all subchannels are quiet before we re-ipl an lpar. */
2006-09-20 17:58:49 +04:00
void reipl_ccw_dev ( struct ccw_dev_id * devid )
2005-04-17 02:20:36 +04:00
{
2006-09-20 17:58:49 +04:00
struct subchannel_id schid ;
2006-12-04 17:40:26 +03:00
s390_reset_system ( ) ;
if ( reipl_find_schid ( devid , & schid ) ! = 0 )
2006-09-20 17:58:49 +04:00
panic ( " IPL Device not found \n " ) ;
do_reipl_asm ( * ( ( __u32 * ) & schid ) ) ;
2005-04-17 02:20:36 +04:00
}
2006-09-20 17:59:15 +04:00
2007-04-27 18:01:25 +04:00
int __init cio_get_iplinfo ( struct cio_iplinfo * iplinfo )
2006-09-20 17:59:15 +04:00
{
struct subchannel_id schid ;
2007-04-27 18:01:25 +04:00
struct schib schib ;
2006-09-20 17:59:15 +04:00
schid = * ( struct subchannel_id * ) __LC_SUBCHANNEL_ID ;
if ( ! schid . one )
2007-04-27 18:01:25 +04:00
return - ENODEV ;
if ( stsch ( schid , & schib ) )
return - ENODEV ;
2008-01-26 16:10:45 +03:00
if ( schib . pmcw . st ! = SUBCHANNEL_TYPE_IO )
return - ENODEV ;
2007-04-27 18:01:25 +04:00
if ( ! schib . pmcw . dnv )
return - ENODEV ;
iplinfo - > devno = schib . pmcw . dev ;
iplinfo - > is_qdio = schib . pmcw . qf ;
return 0 ;
2006-09-20 17:59:15 +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 ) ;
}
}
/**
* cio_tm_intrg - perform interrogate function
* @ sch - subchannel on which to perform the interrogate function
*
* 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 ;
}
}