2005-04-16 15:20:36 -07:00
/*
*
* linux / drivers / s390 / cio / qdio . c
*
* Linux for S / 390 QDIO base support , Hipersocket base support
* version 2
*
* Copyright 2000 , 2002 IBM Corporation
* Author ( s ) : Utz Bacher < utz . bacher @ de . ibm . com >
2006-01-14 13:21:04 -08:00
* 2.6 cio integration by Cornelia Huck < cornelia . huck @ de . ibm . com >
2005-04-16 15:20:36 -07:00
*
* Restriction : only 63 iqdio subchannels would have its own indicator ,
* after that , subsequent subchannels share one indicator
*
*
*
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 , or ( at your option )
* any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/module.h>
# include <linux/init.h>
2008-02-19 15:29:30 +01:00
# include <linux/delay.h>
2005-04-16 15:20:36 -07:00
# include <linux/slab.h>
# include <linux/kernel.h>
# include <linux/proc_fs.h>
# include <linux/timer.h>
2006-04-27 18:40:09 -07:00
# include <linux/mempool.h>
2005-04-16 15:20:36 -07:00
# include <asm/ccwdev.h>
# include <asm/io.h>
# include <asm/atomic.h>
# include <asm/semaphore.h>
# include <asm/timex.h>
# include <asm/debug.h>
2006-12-08 15:54:18 +01:00
# include <asm/s390_rdev.h>
2005-04-16 15:20:36 -07:00
# include <asm/qdio.h>
2008-01-26 14:10:44 +01:00
# include <asm/airq.h>
2005-04-16 15:20:36 -07:00
# include "cio.h"
# include "css.h"
# include "device.h"
# include "qdio.h"
# include "ioasm.h"
# include "chsc.h"
/****************** MODULE PARAMETER VARIABLES ********************/
MODULE_AUTHOR ( " Utz Bacher <utz.bacher@de.ibm.com> " ) ;
MODULE_DESCRIPTION ( " QDIO base support version 2, " \
" Copyright 2000 IBM Corporation " ) ;
MODULE_LICENSE ( " GPL " ) ;
/******************** HERE WE GO ***********************************/
2006-02-01 03:06:31 -08:00
static const char version [ ] = " QDIO base support version 2 " ;
2005-04-16 15:20:36 -07:00
2006-12-08 15:54:18 +01:00
static int qdio_performance_stats = 0 ;
2005-04-16 15:20:36 -07:00
static int proc_perf_file_registration ;
static struct qdio_perf_stats perf_stats ;
static int hydra_thinints ;
2006-01-06 00:19:20 -08:00
static int is_passthrough = 0 ;
2005-04-16 15:20:36 -07:00
static int omit_svs ;
static int indicator_used [ INDICATORS_PER_CACHELINE ] ;
static __u32 * volatile indicators ;
static __u32 volatile spare_indicator ;
static atomic_t spare_indicator_usecount ;
2006-04-27 18:40:09 -07:00
# define QDIO_MEMPOOL_SCSSC_ELEMENTS 2
static mempool_t * qdio_mempool_scssc ;
2007-08-10 14:32:28 +02:00
static struct kmem_cache * qdio_q_cache ;
2005-04-16 15:20:36 -07:00
static debug_info_t * qdio_dbf_setup ;
static debug_info_t * qdio_dbf_sbal ;
static debug_info_t * qdio_dbf_trace ;
static debug_info_t * qdio_dbf_sense ;
# ifdef CONFIG_QDIO_DEBUG
static debug_info_t * qdio_dbf_slsb_out ;
static debug_info_t * qdio_dbf_slsb_in ;
# endif /* CONFIG_QDIO_DEBUG */
/* iQDIO stuff: */
static volatile struct qdio_q * tiq_list = NULL ; /* volatile as it could change
during a while loop */
static DEFINE_SPINLOCK ( ttiq_list_lock ) ;
2008-01-26 14:10:44 +01:00
static void * tiqdio_ind ;
2005-04-16 15:20:36 -07:00
static void tiqdio_tl ( unsigned long ) ;
static DECLARE_TASKLET ( tiqdio_tasklet , tiqdio_tl , 0 ) ;
/* not a macro, as one of the arguments is atomic_read */
static inline int
qdio_min ( int a , int b )
{
if ( a < b )
return a ;
else
return b ;
}
/***************** SCRUBBER HELPER ROUTINES **********************/
2007-05-04 18:47:49 +02:00
# ifdef CONFIG_64BIT
static inline void qdio_perf_stat_inc ( atomic64_t * count )
{
if ( qdio_performance_stats )
atomic64_inc ( count ) ;
}
static inline void qdio_perf_stat_dec ( atomic64_t * count )
{
if ( qdio_performance_stats )
atomic64_dec ( count ) ;
}
# else /* CONFIG_64BIT */
static inline void qdio_perf_stat_inc ( atomic_t * count )
{
if ( qdio_performance_stats )
atomic_inc ( count ) ;
}
static inline void qdio_perf_stat_dec ( atomic_t * count )
{
if ( qdio_performance_stats )
atomic_dec ( count ) ;
}
# endif /* CONFIG_64BIT */
2005-04-16 15:20:36 -07:00
2005-08-25 23:03:35 +01:00
static inline __u64
2005-04-16 15:20:36 -07:00
qdio_get_micros ( void )
{
2006-09-20 15:59:03 +02:00
return ( get_clock ( ) > > 12 ) ; /* time>>12 is microseconds */
2005-04-16 15:20:36 -07:00
}
/*
* unfortunately , we can ' t just xchg the values ; in do_QDIO we want to reserve
* the q in any case , so that we ' ll not be interrupted when we are in
* qdio_mark_tiq . . . shouldn ' t have a really bad impact , as reserving almost
* ever works ( last famous words )
*/
static inline int
qdio_reserve_q ( struct qdio_q * q )
{
return atomic_add_return ( 1 , & q - > use_count ) - 1 ;
}
static inline void
qdio_release_q ( struct qdio_q * q )
{
atomic_dec ( & q - > use_count ) ;
}
2006-01-06 00:19:20 -08:00
/*check ccq */
2007-02-05 21:18:53 +01:00
static int
2006-01-06 00:19:20 -08:00
qdio_check_ccq ( struct qdio_q * q , unsigned int ccq )
2005-04-16 15:20:36 -07:00
{
2006-01-06 00:19:20 -08:00
char dbf_text [ 15 ] ;
2007-07-17 13:36:04 +02:00
if ( ccq = = 0 | | ccq = = 32 )
2006-01-06 00:19:20 -08:00
return 0 ;
2007-07-17 13:36:04 +02:00
if ( ccq = = 96 | | ccq = = 97 )
2006-01-06 00:19:20 -08:00
return 1 ;
/*notify devices immediately*/
sprintf ( dbf_text , " %d " , ccq ) ;
QDIO_DBF_TEXT2 ( 1 , trace , dbf_text ) ;
return - EIO ;
2005-04-16 15:20:36 -07:00
}
2006-01-06 00:19:20 -08:00
/* EQBS: extract buffer states */
2007-02-05 21:18:53 +01:00
static int
2006-01-06 00:19:20 -08:00
qdio_do_eqbs ( struct qdio_q * q , unsigned char * state ,
unsigned int * start , unsigned int * cnt )
{
struct qdio_irq * irq ;
unsigned int tmp_cnt , q_no , ccq ;
int rc ;
char dbf_text [ 15 ] ;
2005-04-16 15:20:36 -07:00
2006-01-06 00:19:20 -08:00
ccq = 0 ;
tmp_cnt = * cnt ;
irq = ( struct qdio_irq * ) q - > irq_ptr ;
q_no = q - > q_no ;
if ( ! q - > is_input_q )
q_no + = irq - > no_input_qs ;
2006-02-20 18:28:12 -08:00
again :
2006-01-06 00:19:20 -08:00
ccq = do_eqbs ( irq - > sch_token , state , q_no , start , cnt ) ;
rc = qdio_check_ccq ( q , ccq ) ;
2007-08-22 13:51:38 +02:00
if ( ( ccq = = 96 ) & & ( tmp_cnt ! = * cnt ) )
rc = 0 ;
2006-02-20 18:28:12 -08:00
if ( rc = = 1 ) {
QDIO_DBF_TEXT5 ( 1 , trace , " eqAGAIN " ) ;
goto again ;
}
2006-01-06 00:19:20 -08:00
if ( rc < 0 ) {
QDIO_DBF_TEXT2 ( 1 , trace , " eqberr " ) ;
sprintf ( dbf_text , " %2x,%2x,%d,%d " , tmp_cnt , * cnt , ccq , q_no ) ;
QDIO_DBF_TEXT2 ( 1 , trace , dbf_text ) ;
q - > handler ( q - > cdev , QDIO_STATUS_ACTIVATE_CHECK_CONDITION |
QDIO_STATUS_LOOK_FOR_ERROR ,
0 , 0 , 0 , - 1 , - 1 , q - > int_parm ) ;
return 0 ;
}
return ( tmp_cnt - * cnt ) ;
}
/* SQBS: set buffer states */
2007-02-05 21:18:53 +01:00
static int
2006-01-06 00:19:20 -08:00
qdio_do_sqbs ( struct qdio_q * q , unsigned char state ,
unsigned int * start , unsigned int * cnt )
{
struct qdio_irq * irq ;
unsigned int tmp_cnt , q_no , ccq ;
int rc ;
char dbf_text [ 15 ] ;
ccq = 0 ;
tmp_cnt = * cnt ;
irq = ( struct qdio_irq * ) q - > irq_ptr ;
q_no = q - > q_no ;
if ( ! q - > is_input_q )
q_no + = irq - > no_input_qs ;
2006-02-20 18:28:12 -08:00
again :
2006-01-06 00:19:20 -08:00
ccq = do_sqbs ( irq - > sch_token , state , q_no , start , cnt ) ;
rc = qdio_check_ccq ( q , ccq ) ;
2006-02-20 18:28:12 -08:00
if ( rc = = 1 ) {
QDIO_DBF_TEXT5 ( 1 , trace , " sqAGAIN " ) ;
goto again ;
}
2006-01-06 00:19:20 -08:00
if ( rc < 0 ) {
2007-03-19 13:18:12 +01:00
QDIO_DBF_TEXT3 ( 1 , trace , " sqberr " ) ;
sprintf ( dbf_text , " %2x,%2x " , tmp_cnt , * cnt ) ;
QDIO_DBF_TEXT3 ( 1 , trace , dbf_text ) ;
sprintf ( dbf_text , " %d,%d " , ccq , q_no ) ;
QDIO_DBF_TEXT3 ( 1 , trace , dbf_text ) ;
2006-01-06 00:19:20 -08:00
q - > handler ( q - > cdev , QDIO_STATUS_ACTIVATE_CHECK_CONDITION |
QDIO_STATUS_LOOK_FOR_ERROR ,
0 , 0 , 0 , - 1 , - 1 , q - > int_parm ) ;
return 0 ;
}
return ( tmp_cnt - * cnt ) ;
}
static inline int
qdio_set_slsb ( struct qdio_q * q , unsigned int * bufno ,
unsigned char state , unsigned int * count )
{
volatile char * slsb ;
struct qdio_irq * irq ;
irq = ( struct qdio_irq * ) q - > irq_ptr ;
if ( ! irq - > is_qebsm ) {
slsb = ( char * ) & q - > slsb . acc . val [ ( * bufno ) ] ;
xchg ( slsb , state ) ;
return 1 ;
}
return qdio_do_sqbs ( q , state , bufno , count ) ;
}
# ifdef CONFIG_QDIO_DEBUG
static inline void
qdio_trace_slsb ( struct qdio_q * q )
{
if ( q - > queue_type = = QDIO_TRACE_QTYPE ) {
if ( q - > is_input_q )
QDIO_DBF_HEX2 ( 0 , slsb_in , & q - > slsb ,
QDIO_MAX_BUFFERS_PER_Q ) ;
else
QDIO_DBF_HEX2 ( 0 , slsb_out , & q - > slsb ,
QDIO_MAX_BUFFERS_PER_Q ) ;
}
}
# endif
static inline int
set_slsb ( struct qdio_q * q , unsigned int * bufno ,
unsigned char state , unsigned int * count )
{
int rc ;
# ifdef CONFIG_QDIO_DEBUG
qdio_trace_slsb ( q ) ;
# endif
rc = qdio_set_slsb ( q , bufno , state , count ) ;
# ifdef CONFIG_QDIO_DEBUG
qdio_trace_slsb ( q ) ;
# endif
return rc ;
}
2005-04-16 15:20:36 -07:00
static inline int
qdio_siga_sync ( struct qdio_q * q , unsigned int gpr2 ,
unsigned int gpr3 )
{
int cc ;
QDIO_DBF_TEXT4 ( 0 , trace , " sigasync " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . siga_syncs ) ;
2005-04-16 15:20:36 -07:00
2006-01-06 00:19:21 -08:00
cc = do_siga_sync ( q - > schid , gpr2 , gpr3 ) ;
2005-04-16 15:20:36 -07:00
if ( cc )
QDIO_DBF_HEX3 ( 0 , trace , & cc , sizeof ( int * ) ) ;
return cc ;
}
static inline int
qdio_siga_sync_q ( struct qdio_q * q )
{
if ( q - > is_input_q )
return qdio_siga_sync ( q , 0 , q - > mask ) ;
return qdio_siga_sync ( q , q - > mask , 0 ) ;
}
2006-01-06 00:19:20 -08:00
static int
__do_siga_output ( struct qdio_q * q , unsigned int * busy_bit )
{
struct qdio_irq * irq ;
unsigned int fc = 0 ;
2006-01-06 00:19:21 -08:00
unsigned long schid ;
2006-01-06 00:19:20 -08:00
irq = ( struct qdio_irq * ) q - > irq_ptr ;
if ( ! irq - > is_qebsm )
2006-01-06 00:19:21 -08:00
schid = * ( ( u32 * ) & q - > schid ) ;
else {
schid = irq - > sch_token ;
fc | = 0x80 ;
}
return do_siga_output ( schid , q - > mask , busy_bit , fc ) ;
2006-01-06 00:19:20 -08:00
}
2005-04-16 15:20:36 -07:00
/*
* returns QDIO_SIGA_ERROR_ACCESS_EXCEPTION as cc , when SIGA returns
* an access exception
*/
2007-02-05 21:18:53 +01:00
static int
2005-04-16 15:20:36 -07:00
qdio_siga_output ( struct qdio_q * q )
{
int cc ;
__u32 busy_bit ;
__u64 start_time = 0 ;
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . siga_outs ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT4 ( 0 , trace , " sigaout " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
for ( ; ; ) {
2006-01-06 00:19:20 -08:00
cc = __do_siga_output ( q , & busy_bit ) ;
2005-04-16 15:20:36 -07:00
//QDIO_PRINT_ERR("cc=%x, busy=%x\n",cc,busy_bit);
if ( ( cc = = 2 ) & & ( busy_bit ) & & ( q - > is_iqdio_q ) ) {
if ( ! start_time )
start_time = NOW ;
if ( ( NOW - start_time ) > QDIO_BUSY_BIT_PATIENCE )
break ;
} else
break ;
}
if ( ( cc = = 2 ) & & ( busy_bit ) )
cc | = QDIO_SIGA_ERROR_B_BIT_SET ;
if ( cc )
QDIO_DBF_HEX3 ( 0 , trace , & cc , sizeof ( int * ) ) ;
return cc ;
}
2007-02-05 21:18:53 +01:00
static int
2005-04-16 15:20:36 -07:00
qdio_siga_input ( struct qdio_q * q )
{
int cc ;
QDIO_DBF_TEXT4 ( 0 , trace , " sigain " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . siga_ins ) ;
2005-04-16 15:20:36 -07:00
2006-01-06 00:19:21 -08:00
cc = do_siga_input ( q - > schid , q - > mask ) ;
2005-04-16 15:20:36 -07:00
if ( cc )
QDIO_DBF_HEX3 ( 0 , trace , & cc , sizeof ( int * ) ) ;
return cc ;
}
/* locked by the locks in qdio_activate and qdio_cleanup */
2006-01-06 00:19:20 -08:00
static __u32 *
2005-04-16 15:20:36 -07:00
qdio_get_indicator ( void )
{
int i ;
2008-01-26 14:10:44 +01:00
for ( i = 0 ; i < INDICATORS_PER_CACHELINE ; i + + )
2005-04-16 15:20:36 -07:00
if ( ! indicator_used [ i ] ) {
indicator_used [ i ] = 1 ;
return indicators + i ;
}
atomic_inc ( & spare_indicator_usecount ) ;
return ( __u32 * volatile ) & spare_indicator ;
}
/* locked by the locks in qdio_activate and qdio_cleanup */
static void
qdio_put_indicator ( __u32 * addr )
{
int i ;
if ( ( addr ) & & ( addr ! = & spare_indicator ) ) {
i = addr - indicators ;
indicator_used [ i ] = 0 ;
}
if ( addr = = & spare_indicator )
atomic_dec ( & spare_indicator_usecount ) ;
}
2006-01-06 00:19:20 -08:00
static inline void
2005-04-16 15:20:36 -07:00
tiqdio_clear_summary_bit ( __u32 * location )
{
QDIO_DBF_TEXT5 ( 0 , trace , " clrsummb " ) ;
QDIO_DBF_HEX5 ( 0 , trace , & location , sizeof ( void * ) ) ;
xchg ( location , 0 ) ;
}
2006-01-06 00:19:20 -08:00
static inline void
2005-04-16 15:20:36 -07:00
tiqdio_set_summary_bit ( __u32 * location )
{
QDIO_DBF_TEXT5 ( 0 , trace , " setsummb " ) ;
QDIO_DBF_HEX5 ( 0 , trace , & location , sizeof ( void * ) ) ;
xchg ( location , - 1 ) ;
}
static inline void
tiqdio_sched_tl ( void )
{
tasklet_hi_schedule ( & tiqdio_tasklet ) ;
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
qdio_mark_tiq ( struct qdio_q * q )
{
unsigned long flags ;
QDIO_DBF_TEXT4 ( 0 , trace , " mark iq " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
spin_lock_irqsave ( & ttiq_list_lock , flags ) ;
if ( unlikely ( atomic_read ( & q - > is_in_shutdown ) ) )
goto out_unlock ;
if ( ! q - > is_input_q )
goto out_unlock ;
if ( ( q - > list_prev ) | | ( q - > list_next ) )
goto out_unlock ;
if ( ! tiq_list ) {
tiq_list = q ;
q - > list_prev = q ;
q - > list_next = q ;
} else {
q - > list_next = tiq_list ;
q - > list_prev = tiq_list - > list_prev ;
tiq_list - > list_prev - > list_next = q ;
tiq_list - > list_prev = q ;
}
spin_unlock_irqrestore ( & ttiq_list_lock , flags ) ;
tiqdio_set_summary_bit ( ( __u32 * ) q - > dev_st_chg_ind ) ;
tiqdio_sched_tl ( ) ;
return ;
out_unlock :
spin_unlock_irqrestore ( & ttiq_list_lock , flags ) ;
return ;
}
static inline void
qdio_mark_q ( struct qdio_q * q )
{
QDIO_DBF_TEXT4 ( 0 , trace , " mark q " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
if ( unlikely ( atomic_read ( & q - > is_in_shutdown ) ) )
return ;
tasklet_schedule ( & q - > tasklet ) ;
}
2007-02-05 21:18:53 +01:00
static int
2005-04-16 15:20:36 -07:00
qdio_stop_polling ( struct qdio_q * q )
{
# ifdef QDIO_USE_PROCESSING_STATE
2006-01-06 00:19:20 -08:00
unsigned int tmp , gsf , count = 1 ;
unsigned char state = 0 ;
struct qdio_irq * irq = ( struct qdio_irq * ) q - > irq_ptr ;
2005-04-16 15:20:36 -07:00
2006-12-04 15:40:59 +01:00
if ( ! atomic_xchg ( & q - > polling , 0 ) )
2005-04-16 15:20:36 -07:00
return 1 ;
QDIO_DBF_TEXT4 ( 0 , trace , " stoppoll " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
/* show the card that we are not polling anymore */
if ( ! q - > is_input_q )
return 1 ;
2006-01-06 00:19:20 -08:00
tmp = gsf = GET_SAVED_FRONTIER ( q ) ;
tmp = ( ( tmp + QDIO_MAX_BUFFERS_PER_Q - 1 ) & ( QDIO_MAX_BUFFERS_PER_Q - 1 ) ) ;
set_slsb ( q , & tmp , SLSB_P_INPUT_NOT_INIT , & count ) ;
2005-04-16 15:20:36 -07:00
/*
* we don ' t issue this SYNC_MEMORY , as we trust Rick T and
* moreover will not use the PROCESSING state under VM , so
* q - > polling was 0 anyway
*/
/*SYNC_MEMORY;*/
2006-01-06 00:19:20 -08:00
if ( irq - > is_qebsm ) {
count = 1 ;
qdio_do_eqbs ( q , & state , & gsf , & count ) ;
} else
state = q - > slsb . acc . val [ gsf ] ;
if ( state ! = SLSB_P_INPUT_PRIMED )
2005-04-16 15:20:36 -07:00
return 1 ;
/*
* set our summary bit again , as otherwise there is a
* small window we can miss between resetting it and
* checking for PRIMED state
*/
if ( q - > is_thinint_q )
tiqdio_set_summary_bit ( ( __u32 * ) q - > dev_st_chg_ind ) ;
return 0 ;
# else /* QDIO_USE_PROCESSING_STATE */
return 1 ;
# endif /* QDIO_USE_PROCESSING_STATE */
}
/*
* see the comment in do_QDIO and before qdio_reserve_q about the
* sophisticated locking outside of unmark_q , so that we don ' t need to
* disable the interrupts : - )
*/
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
qdio_unmark_q ( struct qdio_q * q )
{
unsigned long flags ;
QDIO_DBF_TEXT4 ( 0 , trace , " unmark q " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
if ( ( ! q - > list_prev ) | | ( ! q - > list_next ) )
return ;
if ( ( q - > is_thinint_q ) & & ( q - > is_input_q ) ) {
/* iQDIO */
spin_lock_irqsave ( & ttiq_list_lock , flags ) ;
/* in case cleanup has done this already and simultanously
* qdio_unmark_q is called from the interrupt handler , we ' ve
* got to check this in this specific case again */
if ( ( ! q - > list_prev ) | | ( ! q - > list_next ) )
goto out ;
if ( q - > list_next = = q ) {
/* q was the only interesting q */
tiq_list = NULL ;
q - > list_next = NULL ;
q - > list_prev = NULL ;
} else {
q - > list_next - > list_prev = q - > list_prev ;
q - > list_prev - > list_next = q - > list_next ;
tiq_list = q - > list_next ;
q - > list_next = NULL ;
q - > list_prev = NULL ;
}
out :
spin_unlock_irqrestore ( & ttiq_list_lock , flags ) ;
}
}
static inline unsigned long
tiqdio_clear_global_summary ( void )
{
unsigned long time ;
QDIO_DBF_TEXT5 ( 0 , trace , " clrglobl " ) ;
time = do_clear_global_summary ( ) ;
QDIO_DBF_HEX5 ( 0 , trace , & time , sizeof ( unsigned long ) ) ;
return time ;
}
/************************* OUTBOUND ROUTINES *******************************/
2006-01-06 00:19:20 -08:00
static int
qdio_qebsm_get_outbound_buffer_frontier ( struct qdio_q * q )
{
struct qdio_irq * irq ;
unsigned char state ;
unsigned int cnt , count , ftc ;
irq = ( struct qdio_irq * ) q - > irq_ptr ;
if ( ( ! q - > is_iqdio_q ) & & ( ! q - > hydra_gives_outbound_pcis ) )
SYNC_MEMORY ;
ftc = q - > first_to_check ;
count = qdio_min ( atomic_read ( & q - > number_of_buffers_used ) ,
( QDIO_MAX_BUFFERS_PER_Q - 1 ) ) ;
if ( count = = 0 )
return q - > first_to_check ;
cnt = qdio_do_eqbs ( q , & state , & ftc , & count ) ;
if ( cnt = = 0 )
return q - > first_to_check ;
switch ( state ) {
case SLSB_P_OUTPUT_ERROR :
QDIO_DBF_TEXT3 ( 0 , trace , " outperr " ) ;
atomic_sub ( cnt , & q - > number_of_buffers_used ) ;
if ( q - > qdio_error )
q - > error_status_flags | =
QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR ;
q - > qdio_error = SLSB_P_OUTPUT_ERROR ;
q - > error_status_flags | = QDIO_STATUS_LOOK_FOR_ERROR ;
q - > first_to_check = ftc ;
break ;
case SLSB_P_OUTPUT_EMPTY :
QDIO_DBF_TEXT5 ( 0 , trace , " outpempt " ) ;
atomic_sub ( cnt , & q - > number_of_buffers_used ) ;
q - > first_to_check = ftc ;
break ;
case SLSB_CU_OUTPUT_PRIMED :
/* all buffers primed */
QDIO_DBF_TEXT5 ( 0 , trace , " outpprim " ) ;
break ;
default :
break ;
}
QDIO_DBF_HEX4 ( 0 , trace , & q - > first_to_check , sizeof ( int ) ) ;
return q - > first_to_check ;
}
static int
qdio_qebsm_get_inbound_buffer_frontier ( struct qdio_q * q )
{
struct qdio_irq * irq ;
unsigned char state ;
int tmp , ftc , count , cnt ;
char dbf_text [ 15 ] ;
irq = ( struct qdio_irq * ) q - > irq_ptr ;
ftc = q - > first_to_check ;
count = qdio_min ( atomic_read ( & q - > number_of_buffers_used ) ,
( QDIO_MAX_BUFFERS_PER_Q - 1 ) ) ;
if ( count = = 0 )
return q - > first_to_check ;
cnt = qdio_do_eqbs ( q , & state , & ftc , & count ) ;
if ( cnt = = 0 )
return q - > first_to_check ;
switch ( state ) {
case SLSB_P_INPUT_ERROR :
# ifdef CONFIG_QDIO_DEBUG
QDIO_DBF_TEXT3 ( 1 , trace , " inperr " ) ;
sprintf ( dbf_text , " %2x,%2x " , ftc , count ) ;
QDIO_DBF_TEXT3 ( 1 , trace , dbf_text ) ;
# endif /* CONFIG_QDIO_DEBUG */
if ( q - > qdio_error )
q - > error_status_flags | =
QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR ;
q - > qdio_error = SLSB_P_INPUT_ERROR ;
q - > error_status_flags | = QDIO_STATUS_LOOK_FOR_ERROR ;
atomic_sub ( cnt , & q - > number_of_buffers_used ) ;
q - > first_to_check = ftc ;
break ;
case SLSB_P_INPUT_PRIMED :
QDIO_DBF_TEXT3 ( 0 , trace , " inptprim " ) ;
sprintf ( dbf_text , " %2x,%2x " , ftc , count ) ;
QDIO_DBF_TEXT3 ( 1 , trace , dbf_text ) ;
tmp = 0 ;
ftc = q - > first_to_check ;
# ifdef QDIO_USE_PROCESSING_STATE
if ( cnt > 1 ) {
cnt - = 1 ;
tmp = set_slsb ( q , & ftc , SLSB_P_INPUT_NOT_INIT , & cnt ) ;
if ( ! tmp )
break ;
}
cnt = 1 ;
tmp + = set_slsb ( q , & ftc ,
SLSB_P_INPUT_PROCESSING , & cnt ) ;
atomic_set ( & q - > polling , 1 ) ;
# else
tmp = set_slsb ( q , & ftc , SLSB_P_INPUT_NOT_INIT , & cnt ) ;
# endif
atomic_sub ( tmp , & q - > number_of_buffers_used ) ;
q - > first_to_check = ftc ;
break ;
case SLSB_CU_INPUT_EMPTY :
case SLSB_P_INPUT_NOT_INIT :
case SLSB_P_INPUT_PROCESSING :
QDIO_DBF_TEXT5 ( 0 , trace , " inpnipro " ) ;
break ;
default :
break ;
}
QDIO_DBF_HEX4 ( 0 , trace , & q - > first_to_check , sizeof ( int ) ) ;
return q - > first_to_check ;
}
2005-04-16 15:20:36 -07:00
2007-02-05 21:18:53 +01:00
static int
2005-04-16 15:20:36 -07:00
qdio_get_outbound_buffer_frontier ( struct qdio_q * q )
{
2006-01-06 00:19:20 -08:00
struct qdio_irq * irq ;
volatile char * slsb ;
unsigned int count = 1 ;
int first_not_to_check , f , f_mod_no ;
2005-04-16 15:20:36 -07:00
char dbf_text [ 15 ] ;
QDIO_DBF_TEXT4 ( 0 , trace , " getobfro " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
2006-01-06 00:19:20 -08:00
irq = ( struct qdio_irq * ) q - > irq_ptr ;
if ( irq - > is_qebsm )
return qdio_qebsm_get_outbound_buffer_frontier ( q ) ;
2005-04-16 15:20:36 -07:00
slsb = & q - > slsb . acc . val [ 0 ] ;
f_mod_no = f = q - > first_to_check ;
/*
* f points to already processed elements , so f + no_used is correct . . .
* . . . but : we don ' t check 128 buffers , as otherwise
* qdio_has_outbound_q_moved would return 0
*/
first_not_to_check = f + qdio_min ( atomic_read ( & q - > number_of_buffers_used ) ,
( QDIO_MAX_BUFFERS_PER_Q - 1 ) ) ;
2007-08-22 13:51:39 +02:00
if ( ( ( ! q - > is_iqdio_q ) & & ( ! q - > hydra_gives_outbound_pcis ) ) | |
( q - > queue_type = = QDIO_IQDIO_QFMT_ASYNCH ) )
2005-04-16 15:20:36 -07:00
SYNC_MEMORY ;
check_next :
if ( f = = first_not_to_check )
goto out ;
switch ( slsb [ f_mod_no ] ) {
/* the adapter has not fetched the output yet */
case SLSB_CU_OUTPUT_PRIMED :
QDIO_DBF_TEXT5 ( 0 , trace , " outpprim " ) ;
break ;
/* the adapter got it */
case SLSB_P_OUTPUT_EMPTY :
atomic_dec ( & q - > number_of_buffers_used ) ;
f + + ;
f_mod_no = f & ( QDIO_MAX_BUFFERS_PER_Q - 1 ) ;
QDIO_DBF_TEXT5 ( 0 , trace , " outpempt " ) ;
goto check_next ;
case SLSB_P_OUTPUT_ERROR :
QDIO_DBF_TEXT3 ( 0 , trace , " outperr " ) ;
sprintf ( dbf_text , " %x-%x-%x " , f_mod_no ,
q - > sbal [ f_mod_no ] - > element [ 14 ] . sbalf . value ,
q - > sbal [ f_mod_no ] - > element [ 15 ] . sbalf . value ) ;
QDIO_DBF_TEXT3 ( 1 , trace , dbf_text ) ;
QDIO_DBF_HEX2 ( 1 , sbal , q - > sbal [ f_mod_no ] , 256 ) ;
/* kind of process the buffer */
2006-01-06 00:19:20 -08:00
set_slsb ( q , & f_mod_no , SLSB_P_OUTPUT_NOT_INIT , & count ) ;
2005-04-16 15:20:36 -07:00
/*
* we increment the frontier , as this buffer
* was processed obviously
*/
atomic_dec ( & q - > number_of_buffers_used ) ;
f_mod_no = ( f_mod_no + 1 ) & ( QDIO_MAX_BUFFERS_PER_Q - 1 ) ;
if ( q - > qdio_error )
q - > error_status_flags | =
QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR ;
q - > qdio_error = SLSB_P_OUTPUT_ERROR ;
q - > error_status_flags | = QDIO_STATUS_LOOK_FOR_ERROR ;
break ;
/* no new buffers */
default :
QDIO_DBF_TEXT5 ( 0 , trace , " outpni " ) ;
}
out :
return ( q - > first_to_check = f_mod_no ) ;
}
/* all buffers are processed */
2007-02-05 21:18:53 +01:00
static int
2005-04-16 15:20:36 -07:00
qdio_is_outbound_q_done ( struct qdio_q * q )
{
int no_used ;
# ifdef CONFIG_QDIO_DEBUG
char dbf_text [ 15 ] ;
# endif
no_used = atomic_read ( & q - > number_of_buffers_used ) ;
# ifdef CONFIG_QDIO_DEBUG
if ( no_used ) {
sprintf ( dbf_text , " oqisnt%02x " , no_used ) ;
QDIO_DBF_TEXT4 ( 0 , trace , dbf_text ) ;
} else {
QDIO_DBF_TEXT4 ( 0 , trace , " oqisdone " ) ;
}
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
# endif /* CONFIG_QDIO_DEBUG */
return ( no_used = = 0 ) ;
}
2007-02-05 21:18:53 +01:00
static int
2005-04-16 15:20:36 -07:00
qdio_has_outbound_q_moved ( struct qdio_q * q )
{
int i ;
i = qdio_get_outbound_buffer_frontier ( q ) ;
if ( ( i ! = GET_SAVED_FRONTIER ( q ) ) | |
( q - > error_status_flags & QDIO_STATUS_LOOK_FOR_ERROR ) ) {
SAVE_FRONTIER ( q , i ) ;
QDIO_DBF_TEXT4 ( 0 , trace , " oqhasmvd " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
return 1 ;
} else {
QDIO_DBF_TEXT4 ( 0 , trace , " oqhsntmv " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
return 0 ;
}
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
qdio_kick_outbound_q ( struct qdio_q * q )
{
int result ;
# ifdef CONFIG_QDIO_DEBUG
char dbf_text [ 15 ] ;
QDIO_DBF_TEXT4 ( 0 , trace , " kickoutq " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
# endif /* CONFIG_QDIO_DEBUG */
if ( ! q - > siga_out )
return ;
/* here's the story with cc=2 and busy bit set (thanks, Rick):
* VM ' s CP could present us cc = 2 and busy bit set on SIGA - write
* during reconfiguration of their Guest LAN ( only in HIPERS mode ,
* QDIO mode is asynchronous - - cc = 2 and busy bit there will take
* the queues down immediately ; and not being under VM we have a
* problem on cc = 2 and busy bit set right away ) .
*
* Therefore qdio_siga_output will try for a short time constantly ,
* if such a condition occurs . If it doesn ' t change , it will
* increase the busy_siga_counter and save the timestamp , and
* schedule the queue for later processing ( via mark_q , using the
* queue tasklet ) . __qdio_outbound_processing will check out the
* counter . If non - zero , it will call qdio_kick_outbound_q as often
* as the value of the counter . This will attempt further SIGA
* instructions . For each successful SIGA , the counter is
* decreased , for failing SIGAs the counter remains the same , after
* all .
* After some time of no movement , qdio_kick_outbound_q will
* finally fail and reflect corresponding error codes to call
* the upper layer module and have it take the queues down .
*
* Note that this is a change from the original HiperSockets design
* ( saying cc = 2 and busy bit means take the queues down ) , but in
* these days Guest LAN didn ' t exist . . . excessive cc = 2 with busy bit
* conditions will still take the queues down , but the threshold is
* higher due to the Guest LAN environment .
*/
result = qdio_siga_output ( q ) ;
2006-01-06 00:19:20 -08:00
switch ( result ) {
case 0 :
/* went smooth this time, reset timestamp */
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_QDIO_DEBUG
2006-01-06 00:19:20 -08:00
QDIO_DBF_TEXT3 ( 0 , trace , " cc2reslv " ) ;
2006-01-06 00:19:21 -08:00
sprintf ( dbf_text , " %4x%2x%2x " , q - > schid . sch_no , q - > q_no ,
2006-01-06 00:19:20 -08:00
atomic_read ( & q - > busy_siga_counter ) ) ;
QDIO_DBF_TEXT3 ( 0 , trace , dbf_text ) ;
2005-04-16 15:20:36 -07:00
# endif /* CONFIG_QDIO_DEBUG */
2006-01-06 00:19:20 -08:00
q - > timing . busy_start = 0 ;
break ;
case ( 2 | QDIO_SIGA_ERROR_B_BIT_SET ) :
/* cc=2 and busy bit: */
atomic_inc ( & q - > busy_siga_counter ) ;
/* if the last siga was successful, save
* timestamp here */
if ( ! q - > timing . busy_start )
q - > timing . busy_start = NOW ;
/* if we're in time, don't touch error_status_flags
* and siga_error */
if ( NOW - q - > timing . busy_start < QDIO_BUSY_BIT_GIVE_UP ) {
qdio_mark_q ( q ) ;
2005-04-16 15:20:36 -07:00
break ;
2006-01-06 00:19:20 -08:00
}
QDIO_DBF_TEXT2 ( 0 , trace , " cc2REPRT " ) ;
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_QDIO_DEBUG
2006-01-06 00:19:21 -08:00
sprintf ( dbf_text , " %4x%2x%2x " , q - > schid . sch_no , q - > q_no ,
2006-01-06 00:19:20 -08:00
atomic_read ( & q - > busy_siga_counter ) ) ;
QDIO_DBF_TEXT3 ( 0 , trace , dbf_text ) ;
2005-04-16 15:20:36 -07:00
# endif /* CONFIG_QDIO_DEBUG */
2006-01-06 00:19:20 -08:00
/* else fallthrough and report error */
default :
/* for plain cc=1, 2 or 3: */
if ( q - > siga_error )
2005-04-16 15:20:36 -07:00
q - > error_status_flags | =
2006-01-06 00:19:20 -08:00
QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR ;
q - > error_status_flags | =
QDIO_STATUS_LOOK_FOR_ERROR ;
q - > siga_error = result ;
}
2005-04-16 15:20:36 -07:00
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
qdio_kick_outbound_handler ( struct qdio_q * q )
{
int start , end , real_end , count ;
# ifdef CONFIG_QDIO_DEBUG
char dbf_text [ 15 ] ;
# endif
start = q - > first_element_to_kick ;
/* last_move_ftc was just updated */
real_end = GET_SAVED_FRONTIER ( q ) ;
end = ( real_end + QDIO_MAX_BUFFERS_PER_Q - 1 ) &
( QDIO_MAX_BUFFERS_PER_Q - 1 ) ;
count = ( end + QDIO_MAX_BUFFERS_PER_Q + 1 - start ) &
( QDIO_MAX_BUFFERS_PER_Q - 1 ) ;
# ifdef CONFIG_QDIO_DEBUG
QDIO_DBF_TEXT4 ( 0 , trace , " kickouth " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
sprintf ( dbf_text , " s=%2xc=%2x " , start , count ) ;
QDIO_DBF_TEXT4 ( 0 , trace , dbf_text ) ;
# endif /* CONFIG_QDIO_DEBUG */
if ( q - > state = = QDIO_IRQ_STATE_ACTIVE )
q - > handler ( q - > cdev , QDIO_STATUS_OUTBOUND_INT |
q - > error_status_flags ,
q - > qdio_error , q - > siga_error , q - > q_no , start , count ,
q - > int_parm ) ;
/* for the next time: */
q - > first_element_to_kick = real_end ;
q - > qdio_error = 0 ;
q - > siga_error = 0 ;
q - > error_status_flags = 0 ;
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
__qdio_outbound_processing ( struct qdio_q * q )
{
int siga_attempts ;
QDIO_DBF_TEXT4 ( 0 , trace , " qoutproc " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
if ( unlikely ( qdio_reserve_q ( q ) ) ) {
qdio_release_q ( q ) ;
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . outbound_tl_runs_resched ) ;
2005-04-16 15:20:36 -07:00
/* as we're sissies, we'll check next time */
if ( likely ( ! atomic_read ( & q - > is_in_shutdown ) ) ) {
qdio_mark_q ( q ) ;
QDIO_DBF_TEXT4 ( 0 , trace , " busy,agn " ) ;
}
return ;
}
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . outbound_tl_runs ) ;
qdio_perf_stat_inc ( & perf_stats . tl_runs ) ;
2005-04-16 15:20:36 -07:00
/* see comment in qdio_kick_outbound_q */
siga_attempts = atomic_read ( & q - > busy_siga_counter ) ;
while ( siga_attempts ) {
atomic_dec ( & q - > busy_siga_counter ) ;
qdio_kick_outbound_q ( q ) ;
siga_attempts - - ;
}
if ( qdio_has_outbound_q_moved ( q ) )
qdio_kick_outbound_handler ( q ) ;
2007-05-02 15:18:07 +02:00
if ( q - > queue_type = = QDIO_ZFCP_QFMT ) {
if ( ( ! q - > hydra_gives_outbound_pcis ) & &
( ! qdio_is_outbound_q_done ( q ) ) )
qdio_mark_q ( q ) ;
}
else if ( ( ( ! q - > is_iqdio_q ) & & ( ! q - > is_pci_out ) ) | |
( q - > queue_type = = QDIO_IQDIO_QFMT_ASYNCH ) ) {
2005-04-16 15:20:36 -07:00
/*
2007-05-02 15:18:07 +02:00
* make sure buffer switch from PRIMED to EMPTY is noticed
* and outbound_handler is called
2005-04-16 15:20:36 -07:00
*/
2007-05-02 15:18:07 +02:00
if ( qdio_is_outbound_q_done ( q ) ) {
del_timer ( & q - > timer ) ;
} else {
if ( ! timer_pending ( & q - > timer ) )
mod_timer ( & q - > timer , jiffies +
QDIO_FORCE_CHECK_TIMEOUT ) ;
}
}
2005-04-16 15:20:36 -07:00
qdio_release_q ( q ) ;
}
static void
2007-10-12 16:11:38 +02:00
qdio_outbound_processing ( unsigned long q )
2005-04-16 15:20:36 -07:00
{
2007-10-12 16:11:38 +02:00
__qdio_outbound_processing ( ( struct qdio_q * ) q ) ;
2005-04-16 15:20:36 -07:00
}
/************************* INBOUND ROUTINES *******************************/
2007-02-05 21:18:53 +01:00
static int
2005-04-16 15:20:36 -07:00
qdio_get_inbound_buffer_frontier ( struct qdio_q * q )
{
2006-01-06 00:19:20 -08:00
struct qdio_irq * irq ;
2005-04-16 15:20:36 -07:00
int f , f_mod_no ;
volatile char * slsb ;
2006-01-06 00:19:20 -08:00
unsigned int count = 1 ;
2005-04-16 15:20:36 -07:00
int first_not_to_check ;
# ifdef CONFIG_QDIO_DEBUG
char dbf_text [ 15 ] ;
# endif /* CONFIG_QDIO_DEBUG */
# ifdef QDIO_USE_PROCESSING_STATE
int last_position = - 1 ;
# endif /* QDIO_USE_PROCESSING_STATE */
QDIO_DBF_TEXT4 ( 0 , trace , " getibfro " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
2006-01-06 00:19:20 -08:00
irq = ( struct qdio_irq * ) q - > irq_ptr ;
if ( irq - > is_qebsm )
return qdio_qebsm_get_inbound_buffer_frontier ( q ) ;
2005-04-16 15:20:36 -07:00
slsb = & q - > slsb . acc . val [ 0 ] ;
f_mod_no = f = q - > first_to_check ;
/*
* we don ' t check 128 buffers , as otherwise qdio_has_inbound_q_moved
* would return 0
*/
first_not_to_check = f + qdio_min ( atomic_read ( & q - > number_of_buffers_used ) ,
( QDIO_MAX_BUFFERS_PER_Q - 1 ) ) ;
/*
* we don ' t use this one , as a PCI or we after a thin interrupt
* will sync the queues
*/
/* SYNC_MEMORY;*/
check_next :
f_mod_no = f & ( QDIO_MAX_BUFFERS_PER_Q - 1 ) ;
if ( f = = first_not_to_check )
goto out ;
switch ( slsb [ f_mod_no ] ) {
/* CU_EMPTY means frontier is reached */
case SLSB_CU_INPUT_EMPTY :
QDIO_DBF_TEXT5 ( 0 , trace , " inptempt " ) ;
break ;
/* P_PRIMED means set slsb to P_PROCESSING and move on */
case SLSB_P_INPUT_PRIMED :
QDIO_DBF_TEXT5 ( 0 , trace , " inptprim " ) ;
# ifdef QDIO_USE_PROCESSING_STATE
/*
* as soon as running under VM , polling the input queues will
* kill VM in terms of CP overhead
*/
if ( q - > siga_sync ) {
2006-01-06 00:19:20 -08:00
set_slsb ( q , & f_mod_no , SLSB_P_INPUT_NOT_INIT , & count ) ;
2005-04-16 15:20:36 -07:00
} else {
/* set the previous buffer to NOT_INIT. The current
* buffer will be set to PROCESSING at the end of
* this function to avoid further interrupts . */
if ( last_position > = 0 )
2006-01-06 00:19:20 -08:00
set_slsb ( q , & last_position ,
SLSB_P_INPUT_NOT_INIT , & count ) ;
2005-04-16 15:20:36 -07:00
atomic_set ( & q - > polling , 1 ) ;
last_position = f_mod_no ;
}
# else /* QDIO_USE_PROCESSING_STATE */
2006-01-06 00:19:20 -08:00
set_slsb ( q , & f_mod_no , SLSB_P_INPUT_NOT_INIT , & count ) ;
2005-04-16 15:20:36 -07:00
# endif /* QDIO_USE_PROCESSING_STATE */
/*
* not needed , as the inbound queue will be synced on the next
* siga - r , resp . tiqdio_is_inbound_q_done will do the siga - s
*/
/*SYNC_MEMORY;*/
f + + ;
atomic_dec ( & q - > number_of_buffers_used ) ;
goto check_next ;
case SLSB_P_INPUT_NOT_INIT :
case SLSB_P_INPUT_PROCESSING :
QDIO_DBF_TEXT5 ( 0 , trace , " inpnipro " ) ;
break ;
/* P_ERROR means frontier is reached, break and report error */
case SLSB_P_INPUT_ERROR :
# ifdef CONFIG_QDIO_DEBUG
sprintf ( dbf_text , " inperr%2x " , f_mod_no ) ;
QDIO_DBF_TEXT3 ( 1 , trace , dbf_text ) ;
# endif /* CONFIG_QDIO_DEBUG */
QDIO_DBF_HEX2 ( 1 , sbal , q - > sbal [ f_mod_no ] , 256 ) ;
/* kind of process the buffer */
2006-01-06 00:19:20 -08:00
set_slsb ( q , & f_mod_no , SLSB_P_INPUT_NOT_INIT , & count ) ;
2005-04-16 15:20:36 -07:00
if ( q - > qdio_error )
q - > error_status_flags | =
QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR ;
q - > qdio_error = SLSB_P_INPUT_ERROR ;
q - > error_status_flags | = QDIO_STATUS_LOOK_FOR_ERROR ;
/* we increment the frontier, as this buffer
* was processed obviously */
f_mod_no = ( f_mod_no + 1 ) & ( QDIO_MAX_BUFFERS_PER_Q - 1 ) ;
atomic_dec ( & q - > number_of_buffers_used ) ;
# ifdef QDIO_USE_PROCESSING_STATE
last_position = - 1 ;
# endif /* QDIO_USE_PROCESSING_STATE */
break ;
/* everything else means frontier not changed (HALTED or so) */
default :
break ;
}
out :
q - > first_to_check = f_mod_no ;
# ifdef QDIO_USE_PROCESSING_STATE
if ( last_position > = 0 )
2006-09-20 15:59:19 +02:00
set_slsb ( q , & last_position , SLSB_P_INPUT_PROCESSING , & count ) ;
2005-04-16 15:20:36 -07:00
# endif /* QDIO_USE_PROCESSING_STATE */
QDIO_DBF_HEX4 ( 0 , trace , & q - > first_to_check , sizeof ( int ) ) ;
return q - > first_to_check ;
}
2007-02-05 21:18:53 +01:00
static int
2005-04-16 15:20:36 -07:00
qdio_has_inbound_q_moved ( struct qdio_q * q )
{
int i ;
i = qdio_get_inbound_buffer_frontier ( q ) ;
if ( ( i ! = GET_SAVED_FRONTIER ( q ) ) | |
( q - > error_status_flags & QDIO_STATUS_LOOK_FOR_ERROR ) ) {
SAVE_FRONTIER ( q , i ) ;
if ( ( ! q - > siga_sync ) & & ( ! q - > hydra_gives_outbound_pcis ) )
SAVE_TIMESTAMP ( q ) ;
QDIO_DBF_TEXT4 ( 0 , trace , " inhasmvd " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
return 1 ;
} else {
QDIO_DBF_TEXT4 ( 0 , trace , " inhsntmv " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
return 0 ;
}
}
/* means, no more buffers to be filled */
2007-02-05 21:18:53 +01:00
static int
2005-04-16 15:20:36 -07:00
tiqdio_is_inbound_q_done ( struct qdio_q * q )
{
int no_used ;
2006-01-06 00:19:20 -08:00
unsigned int start_buf , count ;
unsigned char state = 0 ;
struct qdio_irq * irq = ( struct qdio_irq * ) q - > irq_ptr ;
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_QDIO_DEBUG
char dbf_text [ 15 ] ;
# endif
no_used = atomic_read ( & q - > number_of_buffers_used ) ;
/* propagate the change from 82 to 80 through VM */
SYNC_MEMORY ;
# ifdef CONFIG_QDIO_DEBUG
if ( no_used ) {
sprintf ( dbf_text , " iqisnt%02x " , no_used ) ;
QDIO_DBF_TEXT4 ( 0 , trace , dbf_text ) ;
} else {
QDIO_DBF_TEXT4 ( 0 , trace , " iniqisdo " ) ;
}
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
# endif /* CONFIG_QDIO_DEBUG */
if ( ! no_used )
return 1 ;
2006-01-06 00:19:20 -08:00
if ( irq - > is_qebsm ) {
count = 1 ;
start_buf = q - > first_to_check ;
qdio_do_eqbs ( q , & state , & start_buf , & count ) ;
} else
state = q - > slsb . acc . val [ q - > first_to_check ] ;
if ( state ! = SLSB_P_INPUT_PRIMED )
2005-04-16 15:20:36 -07:00
/*
* nothing more to do , if next buffer is not PRIMED .
* note that we did a SYNC_MEMORY before , that there
* has been a sychnronization .
* we will return 0 below , as there is nothing to do
* ( stop_polling not necessary , as we have not been
* using the PROCESSING state
*/
return 0 ;
/*
* ok , the next input buffer is primed . that means , that device state
* change indicator and adapter local summary are set , so we will find
* it next time .
* we will return 0 below , as there is nothing to do , except scheduling
* ourselves for the next time .
*/
tiqdio_set_summary_bit ( ( __u32 * ) q - > dev_st_chg_ind ) ;
tiqdio_sched_tl ( ) ;
return 0 ;
}
2007-02-05 21:18:53 +01:00
static int
2005-04-16 15:20:36 -07:00
qdio_is_inbound_q_done ( struct qdio_q * q )
{
int no_used ;
2006-01-06 00:19:20 -08:00
unsigned int start_buf , count ;
unsigned char state = 0 ;
struct qdio_irq * irq = ( struct qdio_irq * ) q - > irq_ptr ;
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_QDIO_DEBUG
char dbf_text [ 15 ] ;
# endif
no_used = atomic_read ( & q - > number_of_buffers_used ) ;
/*
* we need that one for synchronization with the adapter , as it
* does a kind of PCI avoidance
*/
SYNC_MEMORY ;
if ( ! no_used ) {
QDIO_DBF_TEXT4 ( 0 , trace , " inqisdnA " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
return 1 ;
}
2006-01-06 00:19:20 -08:00
if ( irq - > is_qebsm ) {
count = 1 ;
start_buf = q - > first_to_check ;
qdio_do_eqbs ( q , & state , & start_buf , & count ) ;
} else
state = q - > slsb . acc . val [ q - > first_to_check ] ;
if ( state = = SLSB_P_INPUT_PRIMED ) {
2005-04-16 15:20:36 -07:00
/* we got something to do */
QDIO_DBF_TEXT4 ( 0 , trace , " inqisntA " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
return 0 ;
}
/* on VM, we don't poll, so the q is always done here */
if ( q - > siga_sync )
return 1 ;
if ( q - > hydra_gives_outbound_pcis )
return 1 ;
/*
* at this point we know , that inbound first_to_check
* has ( probably ) not moved ( see qdio_inbound_processing )
*/
if ( NOW > GET_SAVED_TIMESTAMP ( q ) + q - > timing . threshold ) {
# ifdef CONFIG_QDIO_DEBUG
QDIO_DBF_TEXT4 ( 0 , trace , " inqisdon " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
sprintf ( dbf_text , " pf%02xcn%02x " , q - > first_to_check , no_used ) ;
QDIO_DBF_TEXT4 ( 0 , trace , dbf_text ) ;
# endif /* CONFIG_QDIO_DEBUG */
return 1 ;
} else {
# ifdef CONFIG_QDIO_DEBUG
QDIO_DBF_TEXT4 ( 0 , trace , " inqisntd " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
sprintf ( dbf_text , " pf%02xcn%02x " , q - > first_to_check , no_used ) ;
QDIO_DBF_TEXT4 ( 0 , trace , dbf_text ) ;
# endif /* CONFIG_QDIO_DEBUG */
return 0 ;
}
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
qdio_kick_inbound_handler ( struct qdio_q * q )
{
int count , start , end , real_end , i ;
# ifdef CONFIG_QDIO_DEBUG
char dbf_text [ 15 ] ;
# endif
QDIO_DBF_TEXT4 ( 0 , trace , " kickinh " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
start = q - > first_element_to_kick ;
real_end = q - > first_to_check ;
end = ( real_end + QDIO_MAX_BUFFERS_PER_Q - 1 ) & ( QDIO_MAX_BUFFERS_PER_Q - 1 ) ;
i = start ;
count = 0 ;
while ( 1 ) {
count + + ;
if ( i = = end )
break ;
i = ( i + 1 ) & ( QDIO_MAX_BUFFERS_PER_Q - 1 ) ;
}
# ifdef CONFIG_QDIO_DEBUG
sprintf ( dbf_text , " s=%2xc=%2x " , start , count ) ;
QDIO_DBF_TEXT4 ( 0 , trace , dbf_text ) ;
# endif /* CONFIG_QDIO_DEBUG */
if ( likely ( q - > state = = QDIO_IRQ_STATE_ACTIVE ) )
q - > handler ( q - > cdev ,
QDIO_STATUS_INBOUND_INT | q - > error_status_flags ,
q - > qdio_error , q - > siga_error , q - > q_no , start , count ,
q - > int_parm ) ;
/* for the next time: */
q - > first_element_to_kick = real_end ;
q - > qdio_error = 0 ;
q - > siga_error = 0 ;
q - > error_status_flags = 0 ;
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . inbound_cnt ) ;
2005-04-16 15:20:36 -07:00
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
__tiqdio_inbound_processing ( struct qdio_q * q , int spare_ind_was_set )
{
struct qdio_irq * irq_ptr ;
struct qdio_q * oq ;
int i ;
QDIO_DBF_TEXT4 ( 0 , trace , " iqinproc " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
/*
* we first want to reserve the q , so that we know , that we don ' t
* interrupt ourselves and call qdio_unmark_q , as is_in_shutdown might
* be set
*/
if ( unlikely ( qdio_reserve_q ( q ) ) ) {
qdio_release_q ( q ) ;
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . inbound_thin_tl_runs_resched ) ;
2005-04-16 15:20:36 -07:00
/*
* as we might just be about to stop polling , we make
* sure that we check again at least once more
*/
tiqdio_sched_tl ( ) ;
return ;
}
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . inbound_thin_tl_runs ) ;
2005-04-16 15:20:36 -07:00
if ( unlikely ( atomic_read ( & q - > is_in_shutdown ) ) ) {
qdio_unmark_q ( q ) ;
goto out ;
}
/*
* we reset spare_ind_was_set , when the queue does not use the
* spare indicator
*/
if ( spare_ind_was_set )
spare_ind_was_set = ( q - > dev_st_chg_ind = = & spare_indicator ) ;
if ( ! ( * ( q - > dev_st_chg_ind ) ) & & ! spare_ind_was_set )
goto out ;
/*
* q - > dev_st_chg_ind is the indicator , be it shared or not .
* only clear it , if indicator is non - shared
*/
2008-04-17 07:46:03 +02:00
if ( q - > dev_st_chg_ind ! = & spare_indicator )
2005-04-16 15:20:36 -07:00
tiqdio_clear_summary_bit ( ( __u32 * ) q - > dev_st_chg_ind ) ;
if ( q - > hydra_gives_outbound_pcis ) {
if ( ! q - > siga_sync_done_on_thinints ) {
SYNC_MEMORY_ALL ;
2008-01-26 14:10:53 +01:00
} else if ( ! q - > siga_sync_done_on_outb_tis ) {
2005-04-16 15:20:36 -07:00
SYNC_MEMORY_ALL_OUTB ;
}
} else {
SYNC_MEMORY ;
}
/*
* maybe we have to do work on our outbound queues . . . at least
* we have to check the outbound - int - capable thinint - capable
* queues
*/
if ( q - > hydra_gives_outbound_pcis ) {
irq_ptr = ( struct qdio_irq * ) q - > irq_ptr ;
for ( i = 0 ; i < irq_ptr - > no_output_qs ; i + + ) {
oq = irq_ptr - > output_qs [ i ] ;
2006-12-08 15:54:18 +01:00
if ( ! qdio_is_outbound_q_done ( oq ) ) {
2007-05-04 18:47:49 +02:00
qdio_perf_stat_dec ( & perf_stats . tl_runs ) ;
2005-04-16 15:20:36 -07:00
__qdio_outbound_processing ( oq ) ;
2006-12-08 15:54:18 +01:00
}
2005-04-16 15:20:36 -07:00
}
}
if ( ! qdio_has_inbound_q_moved ( q ) )
goto out ;
qdio_kick_inbound_handler ( q ) ;
if ( tiqdio_is_inbound_q_done ( q ) )
if ( ! qdio_stop_polling ( q ) ) {
/*
* we set the flags to get into the stuff next time ,
* see also comment in qdio_stop_polling
*/
tiqdio_set_summary_bit ( ( __u32 * ) q - > dev_st_chg_ind ) ;
tiqdio_sched_tl ( ) ;
}
out :
qdio_release_q ( q ) ;
}
static void
2007-10-12 16:11:38 +02:00
tiqdio_inbound_processing ( unsigned long q )
2005-04-16 15:20:36 -07:00
{
2007-10-12 16:11:38 +02:00
__tiqdio_inbound_processing ( ( struct qdio_q * ) q ,
atomic_read ( & spare_indicator_usecount ) ) ;
2005-04-16 15:20:36 -07:00
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
__qdio_inbound_processing ( struct qdio_q * q )
{
int q_laps = 0 ;
QDIO_DBF_TEXT4 ( 0 , trace , " qinproc " ) ;
QDIO_DBF_HEX4 ( 0 , trace , & q , sizeof ( void * ) ) ;
if ( unlikely ( qdio_reserve_q ( q ) ) ) {
qdio_release_q ( q ) ;
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . inbound_tl_runs_resched ) ;
2005-04-16 15:20:36 -07:00
/* as we're sissies, we'll check next time */
if ( likely ( ! atomic_read ( & q - > is_in_shutdown ) ) ) {
qdio_mark_q ( q ) ;
QDIO_DBF_TEXT4 ( 0 , trace , " busy,agn " ) ;
}
return ;
}
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . inbound_tl_runs ) ;
qdio_perf_stat_inc ( & perf_stats . tl_runs ) ;
2005-04-16 15:20:36 -07:00
again :
if ( qdio_has_inbound_q_moved ( q ) ) {
qdio_kick_inbound_handler ( q ) ;
if ( ! qdio_stop_polling ( q ) ) {
q_laps + + ;
if ( q_laps < QDIO_Q_LAPS )
goto again ;
}
qdio_mark_q ( q ) ;
} else {
if ( ! qdio_is_inbound_q_done ( q ) )
/* means poll time is not yet over */
qdio_mark_q ( q ) ;
}
qdio_release_q ( q ) ;
}
static void
2007-10-12 16:11:38 +02:00
qdio_inbound_processing ( unsigned long q )
2005-04-16 15:20:36 -07:00
{
2007-10-12 16:11:38 +02:00
__qdio_inbound_processing ( ( struct qdio_q * ) q ) ;
2005-04-16 15:20:36 -07:00
}
/************************* MAIN ROUTINES *******************************/
# ifdef QDIO_USE_PROCESSING_STATE
2007-02-05 21:18:53 +01:00
static int
2005-04-16 15:20:36 -07:00
tiqdio_reset_processing_state ( struct qdio_q * q , int q_laps )
{
if ( ! q ) {
tiqdio_sched_tl ( ) ;
return 0 ;
}
/*
* under VM , we have not used the PROCESSING state , so no
* need to stop polling
*/
if ( q - > siga_sync )
return 2 ;
if ( unlikely ( qdio_reserve_q ( q ) ) ) {
qdio_release_q ( q ) ;
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . inbound_thin_tl_runs_resched ) ;
2005-04-16 15:20:36 -07:00
/*
* as we might just be about to stop polling , we make
* sure that we check again at least once more
*/
/*
* sanity - - we ' d get here without setting the
* dev st chg ind
*/
tiqdio_set_summary_bit ( ( __u32 * ) q - > dev_st_chg_ind ) ;
tiqdio_sched_tl ( ) ;
return 0 ;
}
if ( qdio_stop_polling ( q ) ) {
qdio_release_q ( q ) ;
return 2 ;
}
if ( q_laps < QDIO_Q_LAPS - 1 ) {
qdio_release_q ( q ) ;
return 3 ;
}
/*
* we set the flags to get into the stuff
* next time , see also comment in qdio_stop_polling
*/
tiqdio_set_summary_bit ( ( __u32 * ) q - > dev_st_chg_ind ) ;
tiqdio_sched_tl ( ) ;
qdio_release_q ( q ) ;
return 1 ;
}
# endif /* QDIO_USE_PROCESSING_STATE */
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
tiqdio_inbound_checks ( void )
{
struct qdio_q * q ;
int spare_ind_was_set = 0 ;
# ifdef QDIO_USE_PROCESSING_STATE
int q_laps = 0 ;
# endif /* QDIO_USE_PROCESSING_STATE */
QDIO_DBF_TEXT4 ( 0 , trace , " iqdinbck " ) ;
QDIO_DBF_TEXT5 ( 0 , trace , " iqlocsum " ) ;
# ifdef QDIO_USE_PROCESSING_STATE
again :
# endif /* QDIO_USE_PROCESSING_STATE */
/* when the spare indicator is used and set, save that and clear it */
if ( ( atomic_read ( & spare_indicator_usecount ) ) & & spare_indicator ) {
spare_ind_was_set = 1 ;
tiqdio_clear_summary_bit ( ( __u32 * ) & spare_indicator ) ;
}
q = ( struct qdio_q * ) tiq_list ;
do {
if ( ! q )
break ;
__tiqdio_inbound_processing ( q , spare_ind_was_set ) ;
q = ( struct qdio_q * ) q - > list_next ;
} while ( q ! = ( struct qdio_q * ) tiq_list ) ;
# ifdef QDIO_USE_PROCESSING_STATE
q = ( struct qdio_q * ) tiq_list ;
do {
int ret ;
ret = tiqdio_reset_processing_state ( q , q_laps ) ;
switch ( ret ) {
case 0 :
return ;
case 1 :
q_laps + + ;
case 2 :
q = ( struct qdio_q * ) q - > list_next ;
break ;
default :
q_laps + + ;
goto again ;
}
} while ( q ! = ( struct qdio_q * ) tiq_list ) ;
# endif /* QDIO_USE_PROCESSING_STATE */
}
static void
tiqdio_tl ( unsigned long data )
{
QDIO_DBF_TEXT4 ( 0 , trace , " iqdio_tl " ) ;
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . tl_runs ) ;
2005-04-16 15:20:36 -07:00
tiqdio_inbound_checks ( ) ;
}
/********************* GENERAL HELPER_ROUTINES ***********************/
static void
qdio_release_irq_memory ( struct qdio_irq * irq_ptr )
{
int i ;
2007-08-10 14:32:28 +02:00
struct qdio_q * q ;
2005-04-16 15:20:36 -07:00
2007-08-10 14:32:28 +02:00
for ( i = 0 ; i < QDIO_MAX_QUEUES_PER_IRQ ; i + + ) {
q = irq_ptr - > input_qs [ i ] ;
if ( q ) {
free_page ( ( unsigned long ) q - > slib ) ;
kmem_cache_free ( qdio_q_cache , q ) ;
}
q = irq_ptr - > output_qs [ i ] ;
if ( q ) {
free_page ( ( unsigned long ) q - > slib ) ;
kmem_cache_free ( qdio_q_cache , q ) ;
}
2005-04-16 15:20:36 -07:00
}
2007-08-10 14:32:28 +02:00
free_page ( ( unsigned long ) irq_ptr - > qdr ) ;
2006-04-27 18:40:11 -07:00
free_page ( ( unsigned long ) irq_ptr ) ;
2005-04-16 15:20:36 -07:00
}
static void
qdio_set_impl_params ( struct qdio_irq * irq_ptr ,
unsigned int qib_param_field_format ,
/* pointer to 128 bytes or NULL, if no param field */
unsigned char * qib_param_field ,
/* pointer to no_queues*128 words of data or NULL */
unsigned int no_input_qs ,
unsigned int no_output_qs ,
unsigned long * input_slib_elements ,
unsigned long * output_slib_elements )
{
int i , j ;
if ( ! irq_ptr )
return ;
irq_ptr - > qib . pfmt = qib_param_field_format ;
if ( qib_param_field )
memcpy ( irq_ptr - > qib . parm , qib_param_field ,
QDIO_MAX_BUFFERS_PER_Q ) ;
if ( input_slib_elements )
for ( i = 0 ; i < no_input_qs ; i + + ) {
for ( j = 0 ; j < QDIO_MAX_BUFFERS_PER_Q ; j + + )
irq_ptr - > input_qs [ i ] - > slib - > slibe [ j ] . parms =
input_slib_elements [
i * QDIO_MAX_BUFFERS_PER_Q + j ] ;
}
if ( output_slib_elements )
for ( i = 0 ; i < no_output_qs ; i + + ) {
for ( j = 0 ; j < QDIO_MAX_BUFFERS_PER_Q ; j + + )
irq_ptr - > output_qs [ i ] - > slib - > slibe [ j ] . parms =
output_slib_elements [
i * QDIO_MAX_BUFFERS_PER_Q + j ] ;
}
}
static int
qdio_alloc_qs ( struct qdio_irq * irq_ptr ,
int no_input_qs , int no_output_qs )
{
int i ;
struct qdio_q * q ;
2007-08-10 14:32:28 +02:00
for ( i = 0 ; i < no_input_qs ; i + + ) {
q = kmem_cache_alloc ( qdio_q_cache , GFP_KERNEL ) ;
if ( ! q )
return - ENOMEM ;
memset ( q , 0 , sizeof ( * q ) ) ;
2005-04-16 15:20:36 -07:00
2007-08-10 14:32:28 +02:00
q - > slib = ( struct slib * ) __get_free_page ( GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! q - > slib ) {
2007-08-10 14:32:28 +02:00
kmem_cache_free ( qdio_q_cache , q ) ;
return - ENOMEM ;
2005-04-16 15:20:36 -07:00
}
irq_ptr - > input_qs [ i ] = q ;
}
2007-08-10 14:32:28 +02:00
for ( i = 0 ; i < no_output_qs ; i + + ) {
q = kmem_cache_alloc ( qdio_q_cache , GFP_KERNEL ) ;
if ( ! q )
return - ENOMEM ;
memset ( q , 0 , sizeof ( * q ) ) ;
2005-04-16 15:20:36 -07:00
2007-08-10 14:32:28 +02:00
q - > slib = ( struct slib * ) __get_free_page ( GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! q - > slib ) {
2007-08-10 14:32:28 +02:00
kmem_cache_free ( qdio_q_cache , q ) ;
return - ENOMEM ;
2005-04-16 15:20:36 -07:00
}
irq_ptr - > output_qs [ i ] = q ;
}
2007-08-10 14:32:28 +02:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
static void
qdio_fill_qs ( struct qdio_irq * irq_ptr , struct ccw_device * cdev ,
int no_input_qs , int no_output_qs ,
qdio_handler_t * input_handler ,
qdio_handler_t * output_handler ,
unsigned long int_parm , int q_format ,
unsigned long flags ,
void * * inbound_sbals_array ,
void * * outbound_sbals_array )
{
struct qdio_q * q ;
int i , j ;
char dbf_text [ 20 ] ; /* see qdio_initialize */
void * ptr ;
int available ;
2006-10-11 15:31:38 +02:00
sprintf ( dbf_text , " qfqs%4x " , cdev - > private - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
for ( i = 0 ; i < no_input_qs ; i + + ) {
q = irq_ptr - > input_qs [ i ] ;
memset ( q , 0 , ( ( char * ) & q - > slib ) - ( ( char * ) q ) ) ;
sprintf ( dbf_text , " in-q%4x " , i ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
QDIO_DBF_HEX0 ( 0 , setup , & q , sizeof ( void * ) ) ;
memset ( q - > slib , 0 , PAGE_SIZE ) ;
q - > sl = ( struct sl * ) ( ( ( char * ) q - > slib ) + PAGE_SIZE / 2 ) ;
available = 0 ;
for ( j = 0 ; j < QDIO_MAX_BUFFERS_PER_Q ; j + + )
q - > sbal [ j ] = * ( inbound_sbals_array + + ) ;
q - > queue_type = q_format ;
q - > int_parm = int_parm ;
2006-01-06 00:19:21 -08:00
q - > schid = irq_ptr - > schid ;
2005-04-16 15:20:36 -07:00
q - > irq_ptr = irq_ptr ;
q - > cdev = cdev ;
q - > mask = 1 < < ( 31 - i ) ;
q - > q_no = i ;
q - > is_input_q = 1 ;
q - > first_to_check = 0 ;
q - > last_move_ftc = 0 ;
q - > handler = input_handler ;
q - > dev_st_chg_ind = irq_ptr - > dev_st_chg_ind ;
/* q->is_thinint_q isn't valid at this time, but
2007-10-12 16:11:38 +02:00
* irq_ptr - > is_thinint_irq is
*/
if ( irq_ptr - > is_thinint_irq )
tasklet_init ( & q - > tasklet , tiqdio_inbound_processing ,
( unsigned long ) q ) ;
else
tasklet_init ( & q - > tasklet , qdio_inbound_processing ,
( unsigned long ) q ) ;
2005-04-16 15:20:36 -07:00
/* actually this is not used for inbound queues. yet. */
atomic_set ( & q - > busy_siga_counter , 0 ) ;
q - > timing . busy_start = 0 ;
/* for (j=0;j<QDIO_STATS_NUMBER;j++)
q - > timing . last_transfer_times [ j ] = ( qdio_get_micros ( ) /
QDIO_STATS_NUMBER ) * j ;
q - > timing . last_transfer_index = QDIO_STATS_NUMBER - 1 ;
*/
/* fill in slib */
if ( i > 0 ) irq_ptr - > input_qs [ i - 1 ] - > slib - > nsliba =
( unsigned long ) ( q - > slib ) ;
q - > slib - > sla = ( unsigned long ) ( q - > sl ) ;
q - > slib - > slsba = ( unsigned long ) ( & q - > slsb . acc . val [ 0 ] ) ;
/* fill in sl */
for ( j = 0 ; j < QDIO_MAX_BUFFERS_PER_Q ; j + + )
q - > sl - > element [ j ] . sbal = ( unsigned long ) ( q - > sbal [ j ] ) ;
QDIO_DBF_TEXT2 ( 0 , setup , " sl-sb-b0 " ) ;
ptr = ( void * ) q - > sl ;
QDIO_DBF_HEX2 ( 0 , setup , & ptr , sizeof ( void * ) ) ;
ptr = ( void * ) & q - > slsb ;
QDIO_DBF_HEX2 ( 0 , setup , & ptr , sizeof ( void * ) ) ;
ptr = ( void * ) q - > sbal [ 0 ] ;
QDIO_DBF_HEX2 ( 0 , setup , & ptr , sizeof ( void * ) ) ;
/* fill in slsb */
2006-01-06 00:19:20 -08:00
if ( ! irq_ptr - > is_qebsm ) {
unsigned int count = 1 ;
for ( j = 0 ; j < QDIO_MAX_BUFFERS_PER_Q ; j + + )
set_slsb ( q , & j , SLSB_P_INPUT_NOT_INIT , & count ) ;
}
2005-04-16 15:20:36 -07:00
}
for ( i = 0 ; i < no_output_qs ; i + + ) {
q = irq_ptr - > output_qs [ i ] ;
memset ( q , 0 , ( ( char * ) & q - > slib ) - ( ( char * ) q ) ) ;
sprintf ( dbf_text , " outq%4x " , i ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
QDIO_DBF_HEX0 ( 0 , setup , & q , sizeof ( void * ) ) ;
memset ( q - > slib , 0 , PAGE_SIZE ) ;
q - > sl = ( struct sl * ) ( ( ( char * ) q - > slib ) + PAGE_SIZE / 2 ) ;
available = 0 ;
for ( j = 0 ; j < QDIO_MAX_BUFFERS_PER_Q ; j + + )
q - > sbal [ j ] = * ( outbound_sbals_array + + ) ;
q - > queue_type = q_format ;
2006-12-15 17:18:14 +01:00
if ( ( q - > queue_type = = QDIO_IQDIO_QFMT ) & &
( no_output_qs > 1 ) & &
( i = = no_output_qs - 1 ) )
q - > queue_type = QDIO_IQDIO_QFMT_ASYNCH ;
2005-04-16 15:20:36 -07:00
q - > int_parm = int_parm ;
q - > is_input_q = 0 ;
2007-05-02 15:18:07 +02:00
q - > is_pci_out = 0 ;
2006-01-06 00:19:21 -08:00
q - > schid = irq_ptr - > schid ;
2005-04-16 15:20:36 -07:00
q - > cdev = cdev ;
q - > irq_ptr = irq_ptr ;
q - > mask = 1 < < ( 31 - i ) ;
q - > q_no = i ;
q - > first_to_check = 0 ;
q - > last_move_ftc = 0 ;
q - > handler = output_handler ;
2007-10-12 16:11:38 +02:00
tasklet_init ( & q - > tasklet , qdio_outbound_processing ,
( unsigned long ) q ) ;
setup_timer ( & q - > timer , qdio_outbound_processing ,
( unsigned long ) q ) ;
2005-04-16 15:20:36 -07:00
atomic_set ( & q - > busy_siga_counter , 0 ) ;
q - > timing . busy_start = 0 ;
/* fill in slib */
if ( i > 0 ) irq_ptr - > output_qs [ i - 1 ] - > slib - > nsliba =
( unsigned long ) ( q - > slib ) ;
q - > slib - > sla = ( unsigned long ) ( q - > sl ) ;
q - > slib - > slsba = ( unsigned long ) ( & q - > slsb . acc . val [ 0 ] ) ;
/* fill in sl */
for ( j = 0 ; j < QDIO_MAX_BUFFERS_PER_Q ; j + + )
q - > sl - > element [ j ] . sbal = ( unsigned long ) ( q - > sbal [ j ] ) ;
QDIO_DBF_TEXT2 ( 0 , setup , " sl-sb-b0 " ) ;
ptr = ( void * ) q - > sl ;
QDIO_DBF_HEX2 ( 0 , setup , & ptr , sizeof ( void * ) ) ;
ptr = ( void * ) & q - > slsb ;
QDIO_DBF_HEX2 ( 0 , setup , & ptr , sizeof ( void * ) ) ;
ptr = ( void * ) q - > sbal [ 0 ] ;
QDIO_DBF_HEX2 ( 0 , setup , & ptr , sizeof ( void * ) ) ;
/* fill in slsb */
2006-01-06 00:19:20 -08:00
if ( ! irq_ptr - > is_qebsm ) {
unsigned int count = 1 ;
for ( j = 0 ; j < QDIO_MAX_BUFFERS_PER_Q ; j + + )
set_slsb ( q , & j , SLSB_P_OUTPUT_NOT_INIT , & count ) ;
}
2005-04-16 15:20:36 -07:00
}
}
static void
qdio_fill_thresholds ( struct qdio_irq * irq_ptr ,
unsigned int no_input_qs ,
unsigned int no_output_qs ,
unsigned int min_input_threshold ,
unsigned int max_input_threshold ,
unsigned int min_output_threshold ,
unsigned int max_output_threshold )
{
int i ;
struct qdio_q * q ;
for ( i = 0 ; i < no_input_qs ; i + + ) {
q = irq_ptr - > input_qs [ i ] ;
q - > timing . threshold = max_input_threshold ;
/* for (j=0;j<QDIO_STATS_CLASSES;j++) {
q - > threshold_classes [ j ] . threshold =
min_input_threshold +
( max_input_threshold - min_input_threshold ) /
QDIO_STATS_CLASSES ;
}
qdio_use_thresholds ( q , QDIO_STATS_CLASSES / 2 ) ; */
}
for ( i = 0 ; i < no_output_qs ; i + + ) {
q = irq_ptr - > output_qs [ i ] ;
q - > timing . threshold = max_output_threshold ;
/* for (j=0;j<QDIO_STATS_CLASSES;j++) {
q - > threshold_classes [ j ] . threshold =
min_output_threshold +
( max_output_threshold - min_output_threshold ) /
QDIO_STATS_CLASSES ;
}
qdio_use_thresholds ( q , QDIO_STATS_CLASSES / 2 ) ; */
}
}
2008-01-26 14:10:44 +01:00
static void tiqdio_thinint_handler ( void * ind , void * drv_data )
2005-04-16 15:20:36 -07:00
{
QDIO_DBF_TEXT4 ( 0 , trace , " thin_int " ) ;
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . thinints ) ;
2005-04-16 15:20:36 -07:00
/* SVS only when needed:
* issue SVS to benefit from iqdio interrupt avoidance
* ( SVS clears AISOI ) */
if ( ! omit_svs )
tiqdio_clear_global_summary ( ) ;
tiqdio_inbound_checks ( ) ;
}
static void
qdio_set_state ( struct qdio_irq * irq_ptr , enum qdio_irq_states state )
{
int i ;
# ifdef CONFIG_QDIO_DEBUG
char dbf_text [ 15 ] ;
QDIO_DBF_TEXT5 ( 0 , trace , " newstate " ) ;
2006-01-06 00:19:21 -08:00
sprintf ( dbf_text , " %4x%4x " , irq_ptr - > schid . sch_no , state ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT5 ( 0 , trace , dbf_text ) ;
# endif /* CONFIG_QDIO_DEBUG */
irq_ptr - > state = state ;
for ( i = 0 ; i < irq_ptr - > no_input_qs ; i + + )
irq_ptr - > input_qs [ i ] - > state = state ;
for ( i = 0 ; i < irq_ptr - > no_output_qs ; i + + )
irq_ptr - > output_qs [ i ] - > state = state ;
mb ( ) ;
}
2007-02-05 21:18:53 +01:00
static void
2006-01-06 00:19:21 -08:00
qdio_irq_check_sense ( struct subchannel_id schid , struct irb * irb )
2005-04-16 15:20:36 -07:00
{
char dbf_text [ 15 ] ;
if ( irb - > esw . esw0 . erw . cons ) {
2006-01-06 00:19:21 -08:00
sprintf ( dbf_text , " sens%4x " , schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT2 ( 1 , trace , dbf_text ) ;
QDIO_DBF_HEX0 ( 0 , sense , irb , QDIO_DBF_SENSE_LEN ) ;
QDIO_PRINT_WARN ( " sense data available on qdio channel. \n " ) ;
2006-12-04 15:40:59 +01:00
QDIO_HEXDUMP16 ( WARN , " irb: " , irb ) ;
QDIO_HEXDUMP16 ( WARN , " sense data: " , irb - > ecw ) ;
2005-04-16 15:20:36 -07:00
}
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
qdio_handle_pci ( struct qdio_irq * irq_ptr )
{
int i ;
struct qdio_q * q ;
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . pcis ) ;
2005-04-16 15:20:36 -07:00
for ( i = 0 ; i < irq_ptr - > no_input_qs ; i + + ) {
q = irq_ptr - > input_qs [ i ] ;
if ( q - > is_input_q & QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT )
qdio_mark_q ( q ) ;
else {
2007-05-10 15:45:47 +02:00
qdio_perf_stat_dec ( & perf_stats . tl_runs ) ;
2005-04-16 15:20:36 -07:00
__qdio_inbound_processing ( q ) ;
}
}
if ( ! irq_ptr - > hydra_gives_outbound_pcis )
return ;
for ( i = 0 ; i < irq_ptr - > no_output_qs ; i + + ) {
q = irq_ptr - > output_qs [ i ] ;
if ( qdio_is_outbound_q_done ( q ) )
continue ;
2007-05-04 18:47:49 +02:00
qdio_perf_stat_dec ( & perf_stats . tl_runs ) ;
2005-04-16 15:20:36 -07:00
if ( ! irq_ptr - > sync_done_on_outb_pcis )
SYNC_MEMORY ;
__qdio_outbound_processing ( q ) ;
}
}
static void qdio_establish_handle_irq ( struct ccw_device * , int , int ) ;
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
qdio_handle_activate_check ( struct ccw_device * cdev , unsigned long intparm ,
int cstat , int dstat )
{
struct qdio_irq * irq_ptr ;
struct qdio_q * q ;
char dbf_text [ 15 ] ;
irq_ptr = cdev - > private - > qdio_data ;
QDIO_DBF_TEXT2 ( 1 , trace , " ick2 " ) ;
sprintf ( dbf_text , " %s " , cdev - > dev . bus_id ) ;
QDIO_DBF_TEXT2 ( 1 , trace , dbf_text ) ;
QDIO_DBF_HEX2 ( 0 , trace , & intparm , sizeof ( int ) ) ;
QDIO_DBF_HEX2 ( 0 , trace , & dstat , sizeof ( int ) ) ;
QDIO_DBF_HEX2 ( 0 , trace , & cstat , sizeof ( int ) ) ;
QDIO_PRINT_ERR ( " received check condition on activate " \
" queues on device %s (cs=x%x, ds=x%x). \n " ,
cdev - > dev . bus_id , cstat , dstat ) ;
if ( irq_ptr - > no_input_qs ) {
q = irq_ptr - > input_qs [ 0 ] ;
} else if ( irq_ptr - > no_output_qs ) {
q = irq_ptr - > output_qs [ 0 ] ;
} else {
QDIO_PRINT_ERR ( " oops... no queue registered for device %s!? \n " ,
cdev - > dev . bus_id ) ;
goto omit_handler_call ;
}
q - > handler ( q - > cdev , QDIO_STATUS_ACTIVATE_CHECK_CONDITION |
QDIO_STATUS_LOOK_FOR_ERROR ,
0 , 0 , 0 , - 1 , - 1 , q - > int_parm ) ;
omit_handler_call :
qdio_set_state ( irq_ptr , QDIO_IRQ_STATE_STOPPED ) ;
}
static void
2006-12-08 15:53:57 +01:00
qdio_call_shutdown ( struct work_struct * work )
2005-04-16 15:20:36 -07:00
{
2006-12-08 15:53:57 +01:00
struct ccw_device_private * priv ;
2005-04-16 15:20:36 -07:00
struct ccw_device * cdev ;
2006-12-08 15:53:57 +01:00
priv = container_of ( work , struct ccw_device_private , kick_work ) ;
cdev = priv - > cdev ;
2005-04-16 15:20:36 -07:00
qdio_shutdown ( cdev , QDIO_FLAG_CLEANUP_USING_CLEAR ) ;
put_device ( & cdev - > dev ) ;
}
static void
qdio_timeout_handler ( struct ccw_device * cdev )
{
struct qdio_irq * irq_ptr ;
char dbf_text [ 15 ] ;
QDIO_DBF_TEXT2 ( 0 , trace , " qtoh " ) ;
sprintf ( dbf_text , " %s " , cdev - > dev . bus_id ) ;
QDIO_DBF_TEXT2 ( 0 , trace , dbf_text ) ;
irq_ptr = cdev - > private - > qdio_data ;
sprintf ( dbf_text , " state:%d " , irq_ptr - > state ) ;
QDIO_DBF_TEXT2 ( 0 , trace , dbf_text ) ;
switch ( irq_ptr - > state ) {
case QDIO_IRQ_STATE_INACTIVE :
2006-01-06 00:19:25 -08:00
QDIO_PRINT_ERR ( " establish queues on irq 0.%x.%04x: timed out \n " ,
irq_ptr - > schid . ssid , irq_ptr - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT2 ( 1 , setup , " eq:timeo " ) ;
qdio_set_state ( irq_ptr , QDIO_IRQ_STATE_ERR ) ;
break ;
case QDIO_IRQ_STATE_CLEANUP :
2006-01-06 00:19:25 -08:00
QDIO_PRINT_INFO ( " Did not get interrupt on cleanup, "
" irq=0.%x.%x. \n " ,
irq_ptr - > schid . ssid , irq_ptr - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
qdio_set_state ( irq_ptr , QDIO_IRQ_STATE_ERR ) ;
break ;
case QDIO_IRQ_STATE_ESTABLISHED :
case QDIO_IRQ_STATE_ACTIVE :
/* I/O has been terminated by common I/O layer. */
2006-01-06 00:19:25 -08:00
QDIO_PRINT_INFO ( " Queues on irq 0.%x.%04x killed by cio. \n " ,
irq_ptr - > schid . ssid , irq_ptr - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT2 ( 1 , trace , " cio:term " ) ;
qdio_set_state ( irq_ptr , QDIO_IRQ_STATE_STOPPED ) ;
if ( get_device ( & cdev - > dev ) ) {
/* Can't call shutdown from interrupt context. */
PREPARE_WORK ( & cdev - > private - > kick_work ,
2006-12-08 15:53:57 +01:00
qdio_call_shutdown ) ;
2005-04-16 15:20:36 -07:00
queue_work ( ccw_device_work , & cdev - > private - > kick_work ) ;
}
break ;
default :
BUG ( ) ;
}
ccw_device_set_timeout ( cdev , 0 ) ;
wake_up ( & cdev - > private - > wait_q ) ;
}
static void
qdio_handler ( struct ccw_device * cdev , unsigned long intparm , struct irb * irb )
{
struct qdio_irq * irq_ptr ;
int cstat , dstat ;
char dbf_text [ 15 ] ;
# ifdef CONFIG_QDIO_DEBUG
QDIO_DBF_TEXT4 ( 0 , trace , " qint " ) ;
sprintf ( dbf_text , " %s " , cdev - > dev . bus_id ) ;
QDIO_DBF_TEXT4 ( 0 , trace , dbf_text ) ;
# endif /* CONFIG_QDIO_DEBUG */
if ( ! intparm ) {
QDIO_PRINT_ERR ( " got unsolicited interrupt in qdio " \
" handler, device %s \n " , cdev - > dev . bus_id ) ;
return ;
}
irq_ptr = cdev - > private - > qdio_data ;
if ( ! irq_ptr ) {
QDIO_DBF_TEXT2 ( 1 , trace , " uint " ) ;
sprintf ( dbf_text , " %s " , cdev - > dev . bus_id ) ;
QDIO_DBF_TEXT2 ( 1 , trace , dbf_text ) ;
QDIO_PRINT_ERR ( " received interrupt on unused device %s! \n " ,
cdev - > dev . bus_id ) ;
return ;
}
if ( IS_ERR ( irb ) ) {
/* Currently running i/o is in error. */
switch ( PTR_ERR ( irb ) ) {
case - EIO :
QDIO_PRINT_ERR ( " i/o error on device %s \n " ,
cdev - > dev . bus_id ) ;
return ;
case - ETIMEDOUT :
qdio_timeout_handler ( cdev ) ;
return ;
default :
QDIO_PRINT_ERR ( " unknown error state %ld on device %s \n " ,
PTR_ERR ( irb ) , cdev - > dev . bus_id ) ;
return ;
}
}
2006-01-06 00:19:21 -08:00
qdio_irq_check_sense ( irq_ptr - > schid , irb ) ;
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_QDIO_DEBUG
sprintf ( dbf_text , " state:%d " , irq_ptr - > state ) ;
QDIO_DBF_TEXT4 ( 0 , trace , dbf_text ) ;
# endif /* CONFIG_QDIO_DEBUG */
cstat = irb - > scsw . cstat ;
dstat = irb - > scsw . dstat ;
switch ( irq_ptr - > state ) {
case QDIO_IRQ_STATE_INACTIVE :
qdio_establish_handle_irq ( cdev , cstat , dstat ) ;
break ;
case QDIO_IRQ_STATE_CLEANUP :
qdio_set_state ( irq_ptr , QDIO_IRQ_STATE_INACTIVE ) ;
break ;
case QDIO_IRQ_STATE_ESTABLISHED :
case QDIO_IRQ_STATE_ACTIVE :
if ( cstat & SCHN_STAT_PCI ) {
qdio_handle_pci ( irq_ptr ) ;
break ;
}
if ( ( cstat & ~ SCHN_STAT_PCI ) | | dstat ) {
qdio_handle_activate_check ( cdev , intparm , cstat , dstat ) ;
break ;
}
default :
QDIO_PRINT_ERR ( " got interrupt for queues in state %d on " \
" device %s?! \n " ,
irq_ptr - > state , cdev - > dev . bus_id ) ;
}
wake_up ( & cdev - > private - > wait_q ) ;
}
int
qdio_synchronize ( struct ccw_device * cdev , unsigned int flags ,
unsigned int queue_number )
{
2006-01-06 00:19:20 -08:00
int cc = 0 ;
2005-04-16 15:20:36 -07:00
struct qdio_q * q ;
struct qdio_irq * irq_ptr ;
void * ptr ;
# ifdef CONFIG_QDIO_DEBUG
char dbf_text [ 15 ] = " SyncXXXX " ;
# endif
irq_ptr = cdev - > private - > qdio_data ;
if ( ! irq_ptr )
return - ENODEV ;
# ifdef CONFIG_QDIO_DEBUG
2006-01-06 00:19:21 -08:00
* ( ( int * ) ( & dbf_text [ 4 ] ) ) = irq_ptr - > schid . sch_no ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_HEX4 ( 0 , trace , dbf_text , QDIO_DBF_TRACE_LEN ) ;
* ( ( int * ) ( & dbf_text [ 0 ] ) ) = flags ;
* ( ( int * ) ( & dbf_text [ 4 ] ) ) = queue_number ;
QDIO_DBF_HEX4 ( 0 , trace , dbf_text , QDIO_DBF_TRACE_LEN ) ;
# endif /* CONFIG_QDIO_DEBUG */
if ( flags & QDIO_FLAG_SYNC_INPUT ) {
q = irq_ptr - > input_qs [ queue_number ] ;
if ( ! q )
return - EINVAL ;
2006-01-06 00:19:20 -08:00
if ( ! ( irq_ptr - > is_qebsm ) )
2006-01-06 00:19:21 -08:00
cc = do_siga_sync ( q - > schid , 0 , q - > mask ) ;
2005-04-16 15:20:36 -07:00
} else if ( flags & QDIO_FLAG_SYNC_OUTPUT ) {
q = irq_ptr - > output_qs [ queue_number ] ;
if ( ! q )
return - EINVAL ;
2006-01-06 00:19:20 -08:00
if ( ! ( irq_ptr - > is_qebsm ) )
2006-01-06 00:19:21 -08:00
cc = do_siga_sync ( q - > schid , q - > mask , 0 ) ;
2005-04-16 15:20:36 -07:00
} else
return - EINVAL ;
ptr = & cc ;
if ( cc )
QDIO_DBF_HEX3 ( 0 , trace , & ptr , sizeof ( int ) ) ;
return cc ;
}
2008-04-17 07:46:22 +02:00
static int
qdio_get_ssqd_information ( struct subchannel_id * schid ,
struct qdio_chsc_ssqd * * ssqd_area )
{
int result ;
QDIO_DBF_TEXT0 ( 0 , setup , " getssqd " ) ;
* ssqd_area = mempool_alloc ( qdio_mempool_scssc , GFP_ATOMIC ) ;
if ( ! ssqd_area ) {
QDIO_PRINT_WARN ( " Could not get memory for chsc on sch x%x. \n " ,
schid - > sch_no ) ;
return - ENOMEM ;
}
( * ssqd_area ) - > request = ( struct chsc_header ) {
. length = 0x0010 ,
. code = 0x0024 ,
} ;
( * ssqd_area ) - > first_sch = schid - > sch_no ;
( * ssqd_area ) - > last_sch = schid - > sch_no ;
( * ssqd_area ) - > ssid = schid - > ssid ;
result = chsc ( * ssqd_area ) ;
if ( result ) {
QDIO_PRINT_WARN ( " CHSC returned cc %i on sch 0.%x.%x. \n " ,
result , schid - > ssid , schid - > sch_no ) ;
goto out ;
}
if ( ( * ssqd_area ) - > response . code ! = QDIO_CHSC_RESPONSE_CODE_OK ) {
QDIO_PRINT_WARN ( " CHSC response is 0x%x on sch 0.%x.%x. \n " ,
( * ssqd_area ) - > response . code ,
schid - > ssid , schid - > sch_no ) ;
goto out ;
}
if ( ! ( ( * ssqd_area ) - > flags & CHSC_FLAG_QDIO_CAPABILITY ) | |
! ( ( * ssqd_area ) - > flags & CHSC_FLAG_VALIDITY ) | |
( ( * ssqd_area ) - > sch ! = schid - > sch_no ) ) {
QDIO_PRINT_WARN ( " huh? problems checking out sch 0.%x.%x... " \
" using all SIGAs. \n " ,
schid - > ssid , schid - > sch_no ) ;
goto out ;
}
return 0 ;
out :
return - EINVAL ;
}
int
qdio_get_ssqd_pct ( struct ccw_device * cdev )
{
struct qdio_chsc_ssqd * ssqd_area ;
struct subchannel_id schid ;
char dbf_text [ 15 ] ;
int rc ;
int pct = 0 ;
QDIO_DBF_TEXT0 ( 0 , setup , " getpct " ) ;
schid = ccw_device_get_subchannel_id ( cdev ) ;
rc = qdio_get_ssqd_information ( & schid , & ssqd_area ) ;
if ( ! rc )
pct = ( int ) ssqd_area - > pct ;
if ( rc ! = - ENOMEM )
mempool_free ( ssqd_area , qdio_mempool_scssc ) ;
sprintf ( dbf_text , " pct: %d " , pct ) ;
QDIO_DBF_TEXT2 ( 0 , setup , dbf_text ) ;
return pct ;
}
EXPORT_SYMBOL ( qdio_get_ssqd_pct ) ;
2007-02-05 21:18:53 +01:00
static void
2008-04-17 07:46:22 +02:00
qdio_check_subchannel_qebsm ( struct qdio_irq * irq_ptr , unsigned long token )
2006-01-06 00:19:20 -08:00
{
struct qdio_q * q ;
int i ;
unsigned int count , start_buf ;
char dbf_text [ 15 ] ;
/*check if QEBSM is disabled */
2008-04-17 07:46:22 +02:00
if ( ! ( irq_ptr - > is_qebsm ) | | ! ( irq_ptr - > qdioac & 0x01 ) ) {
2006-01-06 00:19:20 -08:00
irq_ptr - > is_qebsm = 0 ;
irq_ptr - > sch_token = 0 ;
irq_ptr - > qib . rflags & = ~ QIB_RFLAGS_ENABLE_QEBSM ;
QDIO_DBF_TEXT0 ( 0 , setup , " noV=V " ) ;
return ;
}
irq_ptr - > sch_token = token ;
/*input queue*/
for ( i = 0 ; i < irq_ptr - > no_input_qs ; i + + ) {
q = irq_ptr - > input_qs [ i ] ;
count = QDIO_MAX_BUFFERS_PER_Q ;
start_buf = 0 ;
set_slsb ( q , & start_buf , SLSB_P_INPUT_NOT_INIT , & count ) ;
}
sprintf ( dbf_text , " V=V:%2x " , irq_ptr - > is_qebsm ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
sprintf ( dbf_text , " %8lx " , irq_ptr - > sch_token ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
/*output queue*/
for ( i = 0 ; i < irq_ptr - > no_output_qs ; i + + ) {
q = irq_ptr - > output_qs [ i ] ;
count = QDIO_MAX_BUFFERS_PER_Q ;
start_buf = 0 ;
set_slsb ( q , & start_buf , SLSB_P_OUTPUT_NOT_INIT , & count ) ;
}
}
static void
2008-04-17 07:46:22 +02:00
qdio_get_ssqd_siga ( struct qdio_irq * irq_ptr )
2005-04-16 15:20:36 -07:00
{
2008-04-17 07:46:22 +02:00
int rc ;
struct qdio_chsc_ssqd * ssqd_area ;
2005-04-16 15:20:36 -07:00
2006-01-06 00:19:20 -08:00
QDIO_DBF_TEXT0 ( 0 , setup , " getssqd " ) ;
2008-04-17 07:46:22 +02:00
irq_ptr - > qdioac = 0 ;
rc = qdio_get_ssqd_information ( & irq_ptr - > schid , & ssqd_area ) ;
if ( rc ) {
QDIO_PRINT_WARN ( " using all SIGAs for sch x%x.n " ,
irq_ptr - > schid . sch_no ) ;
2007-07-17 13:36:09 +02:00
irq_ptr - > qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY |
CHSC_FLAG_SIGA_OUTPUT_NECESSARY |
2006-01-06 00:19:20 -08:00
CHSC_FLAG_SIGA_SYNC_NECESSARY ; /* all flags set */
irq_ptr - > is_qebsm = 0 ;
2008-04-17 07:46:22 +02:00
} else
irq_ptr - > qdioac = ssqd_area - > qdioac1 ;
2005-04-16 15:20:36 -07:00
2008-04-17 07:46:22 +02:00
qdio_check_subchannel_qebsm ( irq_ptr , ssqd_area - > sch_token ) ;
if ( rc ! = - ENOMEM )
mempool_free ( ssqd_area , qdio_mempool_scssc ) ;
2005-04-16 15:20:36 -07:00
}
static unsigned int
tiqdio_check_chsc_availability ( void )
{
char dbf_text [ 15 ] ;
if ( ! css_characteristics_avail )
return - EIO ;
/* Check for bit 41. */
if ( ! css_general_characteristics . aif ) {
QDIO_PRINT_WARN ( " Adapter interruption facility not " \
" installed. \n " ) ;
return - ENOENT ;
}
/* Check for bits 107 and 108. */
if ( ! css_chsc_characteristics . scssc | |
! css_chsc_characteristics . scsscf ) {
QDIO_PRINT_WARN ( " Set Chan Subsys. Char. & Fast-CHSCs " \
" not available. \n " ) ;
return - ENOENT ;
}
/* Check for OSA/FCP thin interrupts (bit 67). */
hydra_thinints = css_general_characteristics . aif_osa ;
sprintf ( dbf_text , " hydrati%1x " , hydra_thinints ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
2006-01-06 00:19:28 -08:00
# ifdef CONFIG_64BIT
2006-01-06 00:19:20 -08:00
/* Check for QEBSM support in general (bit 58). */
is_passthrough = css_general_characteristics . qebsm ;
# endif
sprintf ( dbf_text , " cssQBS:%1x " , is_passthrough ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
2005-04-16 15:20:36 -07:00
/* Check for aif time delay disablement fac (bit 56). If installed,
* omit svs even under lpar ( good point by rick again ) */
omit_svs = css_general_characteristics . aif_tdd ;
sprintf ( dbf_text , " omitsvs%1x " , omit_svs ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
return 0 ;
}
static unsigned int
tiqdio_set_subchannel_ind ( struct qdio_irq * irq_ptr , int reset_to_zero )
{
unsigned long real_addr_local_summary_bit ;
unsigned long real_addr_dev_st_chg_ind ;
void * ptr ;
char dbf_text [ 15 ] ;
unsigned int resp_code ;
int result ;
struct {
struct chsc_header request ;
u16 operation_code ;
u16 reserved1 ;
u32 reserved2 ;
u32 reserved3 ;
u64 summary_indicator_addr ;
u64 subchannel_indicator_addr ;
u32 ks : 4 ;
u32 kc : 4 ;
u32 reserved4 : 21 ;
u32 isc : 3 ;
u32 word_with_d_bit ;
/* set to 0x10000000 to enable
* time delay disablement facility */
u32 reserved5 ;
2006-01-06 00:19:21 -08:00
struct subchannel_id schid ;
2005-04-16 15:20:36 -07:00
u32 reserved6 [ 1004 ] ;
struct chsc_header response ;
u32 reserved7 ;
} * scssc_area ;
if ( ! irq_ptr - > is_thinint_irq )
return - ENODEV ;
if ( reset_to_zero ) {
real_addr_local_summary_bit = 0 ;
real_addr_dev_st_chg_ind = 0 ;
} else {
real_addr_local_summary_bit =
2008-01-26 14:10:44 +01:00
virt_to_phys ( ( volatile void * ) tiqdio_ind ) ;
2005-04-16 15:20:36 -07:00
real_addr_dev_st_chg_ind =
virt_to_phys ( ( volatile void * ) irq_ptr - > dev_st_chg_ind ) ;
}
2006-04-27 18:40:09 -07:00
scssc_area = mempool_alloc ( qdio_mempool_scssc , GFP_ATOMIC ) ;
2005-04-16 15:20:36 -07:00
if ( ! scssc_area ) {
QDIO_PRINT_WARN ( " No memory for setting indicators on " \
2006-01-06 00:19:25 -08:00
" subchannel 0.%x.%x. \n " ,
irq_ptr - > schid . ssid , irq_ptr - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
}
scssc_area - > request = ( struct chsc_header ) {
. length = 0x0fe0 ,
. code = 0x0021 ,
} ;
scssc_area - > operation_code = 0 ;
scssc_area - > summary_indicator_addr = real_addr_local_summary_bit ;
scssc_area - > subchannel_indicator_addr = real_addr_dev_st_chg_ind ;
scssc_area - > ks = QDIO_STORAGE_KEY ;
scssc_area - > kc = QDIO_STORAGE_KEY ;
scssc_area - > isc = TIQDIO_THININT_ISC ;
2006-01-06 00:19:21 -08:00
scssc_area - > schid = irq_ptr - > schid ;
2005-04-16 15:20:36 -07:00
/* enables the time delay disablement facility. Don't care
* whether it is really there ( i . e . we haven ' t checked for
* it ) */
if ( css_general_characteristics . aif_tdd )
scssc_area - > word_with_d_bit = 0x10000000 ;
else
QDIO_PRINT_WARN ( " Time delay disablement facility " \
" not available \n " ) ;
result = chsc ( scssc_area ) ;
if ( result ) {
2006-01-06 00:19:25 -08:00
QDIO_PRINT_WARN ( " could not set indicators on irq 0.%x.%x, " \
" cc=%i. \n " ,
irq_ptr - > schid . ssid , irq_ptr - > schid . sch_no , result ) ;
2005-04-16 15:20:36 -07:00
result = - EIO ;
goto out ;
}
resp_code = scssc_area - > response . code ;
if ( resp_code ! = QDIO_CHSC_RESPONSE_CODE_OK ) {
QDIO_PRINT_WARN ( " response upon setting indicators " \
" is 0x%x. \n " , resp_code ) ;
sprintf ( dbf_text , " sidR%4x " , resp_code ) ;
QDIO_DBF_TEXT1 ( 0 , trace , dbf_text ) ;
QDIO_DBF_TEXT1 ( 0 , setup , dbf_text ) ;
ptr = & scssc_area - > response ;
QDIO_DBF_HEX2 ( 1 , setup , & ptr , QDIO_DBF_SETUP_LEN ) ;
result = - EIO ;
goto out ;
}
QDIO_DBF_TEXT2 ( 0 , setup , " setscind " ) ;
QDIO_DBF_HEX2 ( 0 , setup , & real_addr_local_summary_bit ,
sizeof ( unsigned long ) ) ;
QDIO_DBF_HEX2 ( 0 , setup , & real_addr_dev_st_chg_ind , sizeof ( unsigned long ) ) ;
result = 0 ;
out :
2006-04-27 18:40:09 -07:00
mempool_free ( scssc_area , qdio_mempool_scssc ) ;
2005-04-16 15:20:36 -07:00
return result ;
}
static unsigned int
tiqdio_set_delay_target ( struct qdio_irq * irq_ptr , unsigned long delay_target )
{
unsigned int resp_code ;
int result ;
void * ptr ;
char dbf_text [ 15 ] ;
struct {
struct chsc_header request ;
u16 operation_code ;
u16 reserved1 ;
u32 reserved2 ;
u32 reserved3 ;
u32 reserved4 [ 2 ] ;
u32 delay_target ;
u32 reserved5 [ 1009 ] ;
struct chsc_header response ;
u32 reserved6 ;
} * scsscf_area ;
if ( ! irq_ptr - > is_thinint_irq )
return - ENODEV ;
2006-04-27 18:40:09 -07:00
scsscf_area = mempool_alloc ( qdio_mempool_scssc , GFP_ATOMIC ) ;
2005-04-16 15:20:36 -07:00
if ( ! scsscf_area ) {
QDIO_PRINT_WARN ( " No memory for setting delay target on " \
2006-01-06 00:19:25 -08:00
" subchannel 0.%x.%x. \n " ,
irq_ptr - > schid . ssid , irq_ptr - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
}
scsscf_area - > request = ( struct chsc_header ) {
. length = 0x0fe0 ,
. code = 0x1027 ,
} ;
scsscf_area - > delay_target = delay_target < < 16 ;
result = chsc ( scsscf_area ) ;
if ( result ) {
2006-01-06 00:19:25 -08:00
QDIO_PRINT_WARN ( " could not set delay target on irq 0.%x.%x, " \
" cc=%i. Continuing. \n " ,
irq_ptr - > schid . ssid , irq_ptr - > schid . sch_no ,
2006-01-06 00:19:21 -08:00
result ) ;
2005-04-16 15:20:36 -07:00
result = - EIO ;
goto out ;
}
resp_code = scsscf_area - > response . code ;
if ( resp_code ! = QDIO_CHSC_RESPONSE_CODE_OK ) {
QDIO_PRINT_WARN ( " response upon setting delay target " \
" is 0x%x. Continuing. \n " , resp_code ) ;
sprintf ( dbf_text , " sdtR%4x " , resp_code ) ;
QDIO_DBF_TEXT1 ( 0 , trace , dbf_text ) ;
QDIO_DBF_TEXT1 ( 0 , setup , dbf_text ) ;
ptr = & scsscf_area - > response ;
QDIO_DBF_HEX2 ( 1 , trace , & ptr , QDIO_DBF_TRACE_LEN ) ;
}
QDIO_DBF_TEXT2 ( 0 , trace , " delytrgt " ) ;
QDIO_DBF_HEX2 ( 0 , trace , & delay_target , sizeof ( unsigned long ) ) ;
result = 0 ; /* not critical */
out :
2006-04-27 18:40:09 -07:00
mempool_free ( scsscf_area , qdio_mempool_scssc ) ;
2005-04-16 15:20:36 -07:00
return result ;
}
int
qdio_cleanup ( struct ccw_device * cdev , int how )
{
struct qdio_irq * irq_ptr ;
char dbf_text [ 15 ] ;
int rc ;
irq_ptr = cdev - > private - > qdio_data ;
if ( ! irq_ptr )
return - ENODEV ;
2006-01-06 00:19:21 -08:00
sprintf ( dbf_text , " qcln%4x " , irq_ptr - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT1 ( 0 , trace , dbf_text ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
rc = qdio_shutdown ( cdev , how ) ;
if ( ( rc = = 0 ) | | ( rc = = - EINPROGRESS ) )
rc = qdio_free ( cdev ) ;
return rc ;
}
int
qdio_shutdown ( struct ccw_device * cdev , int how )
{
struct qdio_irq * irq_ptr ;
int i ;
int result = 0 ;
int rc ;
unsigned long flags ;
int timeout ;
char dbf_text [ 15 ] ;
irq_ptr = cdev - > private - > qdio_data ;
if ( ! irq_ptr )
return - ENODEV ;
down ( & irq_ptr - > setting_up_sema ) ;
2006-01-06 00:19:21 -08:00
sprintf ( dbf_text , " qsqs%4x " , irq_ptr - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT1 ( 0 , trace , dbf_text ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
/* mark all qs as uninteresting */
for ( i = 0 ; i < irq_ptr - > no_input_qs ; i + + )
atomic_set ( & irq_ptr - > input_qs [ i ] - > is_in_shutdown , 1 ) ;
for ( i = 0 ; i < irq_ptr - > no_output_qs ; i + + )
atomic_set ( & irq_ptr - > output_qs [ i ] - > is_in_shutdown , 1 ) ;
tasklet_kill ( & tiqdio_tasklet ) ;
for ( i = 0 ; i < irq_ptr - > no_input_qs ; i + + ) {
qdio_unmark_q ( irq_ptr - > input_qs [ i ] ) ;
tasklet_kill ( & irq_ptr - > input_qs [ i ] - > tasklet ) ;
wait_event_interruptible_timeout ( cdev - > private - > wait_q ,
! atomic_read ( & irq_ptr - >
input_qs [ i ] - >
use_count ) ,
QDIO_NO_USE_COUNT_TIMEOUT ) ;
if ( atomic_read ( & irq_ptr - > input_qs [ i ] - > use_count ) )
result = - EINPROGRESS ;
}
for ( i = 0 ; i < irq_ptr - > no_output_qs ; i + + ) {
tasklet_kill ( & irq_ptr - > output_qs [ i ] - > tasklet ) ;
2007-05-02 15:18:07 +02:00
del_timer ( & irq_ptr - > output_qs [ i ] - > timer ) ;
2005-04-16 15:20:36 -07:00
wait_event_interruptible_timeout ( cdev - > private - > wait_q ,
! atomic_read ( & irq_ptr - >
output_qs [ i ] - >
use_count ) ,
QDIO_NO_USE_COUNT_TIMEOUT ) ;
if ( atomic_read ( & irq_ptr - > output_qs [ i ] - > use_count ) )
result = - EINPROGRESS ;
}
/* cleanup subchannel */
spin_lock_irqsave ( get_ccwdev_lock ( cdev ) , flags ) ;
if ( how & QDIO_FLAG_CLEANUP_USING_CLEAR ) {
rc = ccw_device_clear ( cdev , QDIO_DOING_CLEANUP ) ;
timeout = QDIO_CLEANUP_CLEAR_TIMEOUT ;
} else if ( how & QDIO_FLAG_CLEANUP_USING_HALT ) {
rc = ccw_device_halt ( cdev , QDIO_DOING_CLEANUP ) ;
timeout = QDIO_CLEANUP_HALT_TIMEOUT ;
} else { /* default behaviour */
rc = ccw_device_halt ( cdev , QDIO_DOING_CLEANUP ) ;
timeout = QDIO_CLEANUP_HALT_TIMEOUT ;
}
if ( rc = = - ENODEV ) {
/* No need to wait for device no longer present. */
qdio_set_state ( irq_ptr , QDIO_IRQ_STATE_INACTIVE ) ;
spin_unlock_irqrestore ( get_ccwdev_lock ( cdev ) , flags ) ;
} else if ( ( ( void * ) cdev - > handler ! = ( void * ) qdio_handler ) & & rc = = 0 ) {
/*
* Whoever put another handler there , has to cope with the
* interrupt theirself . Might happen if qdio_shutdown was
* called on already shutdown queues , but this shouldn ' t have
* bad side effects .
*/
qdio_set_state ( irq_ptr , QDIO_IRQ_STATE_INACTIVE ) ;
spin_unlock_irqrestore ( get_ccwdev_lock ( cdev ) , flags ) ;
} else if ( rc = = 0 ) {
qdio_set_state ( irq_ptr , QDIO_IRQ_STATE_CLEANUP ) ;
ccw_device_set_timeout ( cdev , timeout ) ;
spin_unlock_irqrestore ( get_ccwdev_lock ( cdev ) , flags ) ;
wait_event ( cdev - > private - > wait_q ,
irq_ptr - > state = = QDIO_IRQ_STATE_INACTIVE | |
irq_ptr - > state = = QDIO_IRQ_STATE_ERR ) ;
} else {
QDIO_PRINT_INFO ( " ccw_device_{halt,clear} returned %d for "
" device %s \n " , result , cdev - > dev . bus_id ) ;
spin_unlock_irqrestore ( get_ccwdev_lock ( cdev ) , flags ) ;
result = rc ;
goto out ;
}
if ( irq_ptr - > is_thinint_irq ) {
qdio_put_indicator ( ( __u32 * ) irq_ptr - > dev_st_chg_ind ) ;
tiqdio_set_subchannel_ind ( irq_ptr , 1 ) ;
/* reset adapter interrupt indicators */
}
/* exchange int handlers, if necessary */
if ( ( void * ) cdev - > handler = = ( void * ) qdio_handler )
cdev - > handler = irq_ptr - > original_int_handler ;
/* Ignore errors. */
qdio_set_state ( irq_ptr , QDIO_IRQ_STATE_INACTIVE ) ;
ccw_device_set_timeout ( cdev , 0 ) ;
out :
up ( & irq_ptr - > setting_up_sema ) ;
return result ;
}
int
qdio_free ( struct ccw_device * cdev )
{
struct qdio_irq * irq_ptr ;
char dbf_text [ 15 ] ;
irq_ptr = cdev - > private - > qdio_data ;
if ( ! irq_ptr )
return - ENODEV ;
down ( & irq_ptr - > setting_up_sema ) ;
2006-01-06 00:19:21 -08:00
sprintf ( dbf_text , " qfqs%4x " , irq_ptr - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT1 ( 0 , trace , dbf_text ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
2006-07-12 16:41:55 +02:00
cdev - > private - > qdio_data = NULL ;
2005-04-16 15:20:36 -07:00
up ( & irq_ptr - > setting_up_sema ) ;
qdio_release_irq_memory ( irq_ptr ) ;
module_put ( THIS_MODULE ) ;
return 0 ;
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
qdio_allocate_do_dbf ( struct qdio_initialize * init_data )
{
char dbf_text [ 20 ] ; /* if a printf printed out more than 8 chars */
sprintf ( dbf_text , " qfmt:%x " , init_data - > q_format ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
QDIO_DBF_HEX0 ( 0 , setup , init_data - > adapter_name , 8 ) ;
sprintf ( dbf_text , " qpff%4x " , init_data - > qib_param_field_format ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
QDIO_DBF_HEX0 ( 0 , setup , & init_data - > qib_param_field , sizeof ( char * ) ) ;
QDIO_DBF_HEX0 ( 0 , setup , & init_data - > input_slib_elements , sizeof ( long * ) ) ;
QDIO_DBF_HEX0 ( 0 , setup , & init_data - > output_slib_elements , sizeof ( long * ) ) ;
sprintf ( dbf_text , " miit%4x " , init_data - > min_input_threshold ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
sprintf ( dbf_text , " mait%4x " , init_data - > max_input_threshold ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
sprintf ( dbf_text , " miot%4x " , init_data - > min_output_threshold ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
sprintf ( dbf_text , " maot%4x " , init_data - > max_output_threshold ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
sprintf ( dbf_text , " niq:%4x " , init_data - > no_input_qs ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
sprintf ( dbf_text , " noq:%4x " , init_data - > no_output_qs ) ;
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
QDIO_DBF_HEX0 ( 0 , setup , & init_data - > input_handler , sizeof ( void * ) ) ;
QDIO_DBF_HEX0 ( 0 , setup , & init_data - > output_handler , sizeof ( void * ) ) ;
QDIO_DBF_HEX0 ( 0 , setup , & init_data - > int_parm , sizeof ( long ) ) ;
QDIO_DBF_HEX0 ( 0 , setup , & init_data - > flags , sizeof ( long ) ) ;
QDIO_DBF_HEX0 ( 0 , setup , & init_data - > input_sbal_addr_array , sizeof ( void * ) ) ;
QDIO_DBF_HEX0 ( 0 , setup , & init_data - > output_sbal_addr_array , sizeof ( void * ) ) ;
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
qdio_allocate_fill_input_desc ( struct qdio_irq * irq_ptr , int i , int iqfmt )
{
irq_ptr - > input_qs [ i ] - > is_iqdio_q = iqfmt ;
irq_ptr - > input_qs [ i ] - > is_thinint_q = irq_ptr - > is_thinint_irq ;
irq_ptr - > qdr - > qdf0 [ i ] . sliba = ( unsigned long ) ( irq_ptr - > input_qs [ i ] - > slib ) ;
irq_ptr - > qdr - > qdf0 [ i ] . sla = ( unsigned long ) ( irq_ptr - > input_qs [ i ] - > sl ) ;
irq_ptr - > qdr - > qdf0 [ i ] . slsba =
( unsigned long ) ( & irq_ptr - > input_qs [ i ] - > slsb . acc . val [ 0 ] ) ;
irq_ptr - > qdr - > qdf0 [ i ] . akey = QDIO_STORAGE_KEY ;
irq_ptr - > qdr - > qdf0 [ i ] . bkey = QDIO_STORAGE_KEY ;
irq_ptr - > qdr - > qdf0 [ i ] . ckey = QDIO_STORAGE_KEY ;
irq_ptr - > qdr - > qdf0 [ i ] . dkey = QDIO_STORAGE_KEY ;
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
qdio_allocate_fill_output_desc ( struct qdio_irq * irq_ptr , int i ,
int j , int iqfmt )
{
irq_ptr - > output_qs [ i ] - > is_iqdio_q = iqfmt ;
irq_ptr - > output_qs [ i ] - > is_thinint_q = irq_ptr - > is_thinint_irq ;
irq_ptr - > qdr - > qdf0 [ i + j ] . sliba = ( unsigned long ) ( irq_ptr - > output_qs [ i ] - > slib ) ;
irq_ptr - > qdr - > qdf0 [ i + j ] . sla = ( unsigned long ) ( irq_ptr - > output_qs [ i ] - > sl ) ;
irq_ptr - > qdr - > qdf0 [ i + j ] . slsba =
( unsigned long ) ( & irq_ptr - > output_qs [ i ] - > slsb . acc . val [ 0 ] ) ;
irq_ptr - > qdr - > qdf0 [ i + j ] . akey = QDIO_STORAGE_KEY ;
irq_ptr - > qdr - > qdf0 [ i + j ] . bkey = QDIO_STORAGE_KEY ;
irq_ptr - > qdr - > qdf0 [ i + j ] . ckey = QDIO_STORAGE_KEY ;
irq_ptr - > qdr - > qdf0 [ i + j ] . dkey = QDIO_STORAGE_KEY ;
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
qdio_initialize_set_siga_flags_input ( struct qdio_irq * irq_ptr )
{
int i ;
for ( i = 0 ; i < irq_ptr - > no_input_qs ; i + + ) {
irq_ptr - > input_qs [ i ] - > siga_sync =
irq_ptr - > qdioac & CHSC_FLAG_SIGA_SYNC_NECESSARY ;
irq_ptr - > input_qs [ i ] - > siga_in =
irq_ptr - > qdioac & CHSC_FLAG_SIGA_INPUT_NECESSARY ;
irq_ptr - > input_qs [ i ] - > siga_out =
irq_ptr - > qdioac & CHSC_FLAG_SIGA_OUTPUT_NECESSARY ;
irq_ptr - > input_qs [ i ] - > siga_sync_done_on_thinints =
irq_ptr - > qdioac & CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS ;
irq_ptr - > input_qs [ i ] - > hydra_gives_outbound_pcis =
irq_ptr - > hydra_gives_outbound_pcis ;
irq_ptr - > input_qs [ i ] - > siga_sync_done_on_outb_tis =
( ( irq_ptr - > qdioac &
( CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS |
CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS ) ) = =
( CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS |
CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS ) ) ;
}
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
qdio_initialize_set_siga_flags_output ( struct qdio_irq * irq_ptr )
{
int i ;
for ( i = 0 ; i < irq_ptr - > no_output_qs ; i + + ) {
irq_ptr - > output_qs [ i ] - > siga_sync =
irq_ptr - > qdioac & CHSC_FLAG_SIGA_SYNC_NECESSARY ;
irq_ptr - > output_qs [ i ] - > siga_in =
irq_ptr - > qdioac & CHSC_FLAG_SIGA_INPUT_NECESSARY ;
irq_ptr - > output_qs [ i ] - > siga_out =
irq_ptr - > qdioac & CHSC_FLAG_SIGA_OUTPUT_NECESSARY ;
irq_ptr - > output_qs [ i ] - > siga_sync_done_on_thinints =
irq_ptr - > qdioac & CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS ;
irq_ptr - > output_qs [ i ] - > hydra_gives_outbound_pcis =
irq_ptr - > hydra_gives_outbound_pcis ;
irq_ptr - > output_qs [ i ] - > siga_sync_done_on_outb_tis =
( ( irq_ptr - > qdioac &
( CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS |
CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS ) ) = =
( CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS |
CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS ) ) ;
}
}
2007-02-05 21:18:53 +01:00
static int
2005-04-16 15:20:36 -07:00
qdio_establish_irq_check_for_errors ( struct ccw_device * cdev , int cstat ,
int dstat )
{
char dbf_text [ 15 ] ;
struct qdio_irq * irq_ptr ;
irq_ptr = cdev - > private - > qdio_data ;
if ( cstat | | ( dstat & ~ ( DEV_STAT_CHN_END | DEV_STAT_DEV_END ) ) ) {
2006-01-06 00:19:21 -08:00
sprintf ( dbf_text , " ick1%4x " , irq_ptr - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT2 ( 1 , trace , dbf_text ) ;
QDIO_DBF_HEX2 ( 0 , trace , & dstat , sizeof ( int ) ) ;
QDIO_DBF_HEX2 ( 0 , trace , & cstat , sizeof ( int ) ) ;
QDIO_PRINT_ERR ( " received check condition on establish " \
2006-01-06 00:19:25 -08:00
" queues on irq 0.%x.%x (cs=x%x, ds=x%x). \n " ,
irq_ptr - > schid . ssid , irq_ptr - > schid . sch_no ,
cstat , dstat ) ;
2005-04-16 15:20:36 -07:00
qdio_set_state ( irq_ptr , QDIO_IRQ_STATE_ERR ) ;
}
if ( ! ( dstat & DEV_STAT_DEV_END ) ) {
QDIO_DBF_TEXT2 ( 1 , setup , " eq:no de " ) ;
QDIO_DBF_HEX2 ( 0 , setup , & dstat , sizeof ( dstat ) ) ;
QDIO_DBF_HEX2 ( 0 , setup , & cstat , sizeof ( cstat ) ) ;
2006-01-06 00:19:25 -08:00
QDIO_PRINT_ERR ( " establish queues on irq 0.%x.%04x: didn't get "
2005-04-16 15:20:36 -07:00
" device end: dstat=%02x, cstat=%02x \n " ,
2006-01-06 00:19:25 -08:00
irq_ptr - > schid . ssid , irq_ptr - > schid . sch_no ,
dstat , cstat ) ;
2005-04-16 15:20:36 -07:00
qdio_set_state ( irq_ptr , QDIO_IRQ_STATE_ERR ) ;
return 1 ;
}
if ( dstat & ~ ( DEV_STAT_CHN_END | DEV_STAT_DEV_END ) ) {
QDIO_DBF_TEXT2 ( 1 , setup , " eq:badio " ) ;
QDIO_DBF_HEX2 ( 0 , setup , & dstat , sizeof ( dstat ) ) ;
QDIO_DBF_HEX2 ( 0 , setup , & cstat , sizeof ( cstat ) ) ;
2006-01-06 00:19:25 -08:00
QDIO_PRINT_ERR ( " establish queues on irq 0.%x.%04x: got "
2005-04-16 15:20:36 -07:00
" the following devstat: dstat=%02x, "
2006-01-06 00:19:25 -08:00
" cstat=%02x \n " , irq_ptr - > schid . ssid ,
2006-01-06 00:19:21 -08:00
irq_ptr - > schid . sch_no , dstat , cstat ) ;
2005-04-16 15:20:36 -07:00
qdio_set_state ( irq_ptr , QDIO_IRQ_STATE_ERR ) ;
return 1 ;
}
return 0 ;
}
static void
qdio_establish_handle_irq ( struct ccw_device * cdev , int cstat , int dstat )
{
struct qdio_irq * irq_ptr ;
char dbf_text [ 15 ] ;
irq_ptr = cdev - > private - > qdio_data ;
2006-10-11 15:31:38 +02:00
sprintf ( dbf_text , " qehi%4x " , cdev - > private - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
QDIO_DBF_TEXT0 ( 0 , trace , dbf_text ) ;
if ( qdio_establish_irq_check_for_errors ( cdev , cstat , dstat ) ) {
ccw_device_set_timeout ( cdev , 0 ) ;
return ;
}
qdio_set_state ( irq_ptr , QDIO_IRQ_STATE_ESTABLISHED ) ;
ccw_device_set_timeout ( cdev , 0 ) ;
}
int
qdio_initialize ( struct qdio_initialize * init_data )
{
int rc ;
char dbf_text [ 15 ] ;
2006-10-11 15:31:38 +02:00
sprintf ( dbf_text , " qini%4x " , init_data - > cdev - > private - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
QDIO_DBF_TEXT0 ( 0 , trace , dbf_text ) ;
rc = qdio_allocate ( init_data ) ;
if ( rc = = 0 ) {
rc = qdio_establish ( init_data ) ;
if ( rc ! = 0 )
qdio_free ( init_data - > cdev ) ;
}
return rc ;
}
int
qdio_allocate ( struct qdio_initialize * init_data )
{
struct qdio_irq * irq_ptr ;
char dbf_text [ 15 ] ;
2006-10-11 15:31:38 +02:00
sprintf ( dbf_text , " qalc%4x " , init_data - > cdev - > private - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
QDIO_DBF_TEXT0 ( 0 , trace , dbf_text ) ;
if ( ( init_data - > no_input_qs > QDIO_MAX_QUEUES_PER_IRQ ) | |
( init_data - > no_output_qs > QDIO_MAX_QUEUES_PER_IRQ ) | |
( ( init_data - > no_input_qs ) & & ( ! init_data - > input_handler ) ) | |
( ( init_data - > no_output_qs ) & & ( ! init_data - > output_handler ) ) )
return - EINVAL ;
if ( ! init_data - > input_sbal_addr_array )
return - EINVAL ;
if ( ! init_data - > output_sbal_addr_array )
return - EINVAL ;
qdio_allocate_do_dbf ( init_data ) ;
/* create irq */
2006-04-27 18:40:11 -07:00
irq_ptr = ( void * ) get_zeroed_page ( GFP_KERNEL | GFP_DMA ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT0 ( 0 , setup , " irq_ptr: " ) ;
QDIO_DBF_HEX0 ( 0 , setup , & irq_ptr , sizeof ( void * ) ) ;
if ( ! irq_ptr ) {
2007-08-10 14:32:28 +02:00
QDIO_PRINT_ERR ( " allocation of irq_ptr failed! \n " ) ;
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
}
init_MUTEX ( & irq_ptr - > setting_up_sema ) ;
/* QDR must be in DMA area since CCW data address is only 32 bit */
2007-08-10 14:32:28 +02:00
irq_ptr - > qdr = ( struct qdr * ) __get_free_page ( GFP_KERNEL | GFP_DMA ) ;
2005-04-16 15:20:36 -07:00
if ( ! ( irq_ptr - > qdr ) ) {
2006-04-27 18:40:11 -07:00
free_page ( ( unsigned long ) irq_ptr ) ;
2007-08-10 14:32:28 +02:00
QDIO_PRINT_ERR ( " allocation of irq_ptr->qdr failed! \n " ) ;
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
}
QDIO_DBF_TEXT0 ( 0 , setup , " qdr: " ) ;
QDIO_DBF_HEX0 ( 0 , setup , & irq_ptr - > qdr , sizeof ( void * ) ) ;
if ( qdio_alloc_qs ( irq_ptr ,
init_data - > no_input_qs ,
init_data - > no_output_qs ) ) {
2007-08-10 14:32:28 +02:00
QDIO_PRINT_ERR ( " queue allocation failed! \n " ) ;
2005-04-16 15:20:36 -07:00
qdio_release_irq_memory ( irq_ptr ) ;
return - ENOMEM ;
}
init_data - > cdev - > private - > qdio_data = irq_ptr ;
qdio_set_state ( irq_ptr , QDIO_IRQ_STATE_INACTIVE ) ;
return 0 ;
}
2007-02-05 21:16:47 +01:00
static int qdio_fill_irq ( struct qdio_initialize * init_data )
2005-04-16 15:20:36 -07:00
{
int i ;
char dbf_text [ 15 ] ;
struct ciw * ciw ;
int is_iqdio ;
struct qdio_irq * irq_ptr ;
irq_ptr = init_data - > cdev - > private - > qdio_data ;
memset ( irq_ptr , 0 , ( ( char * ) & irq_ptr - > qdr ) - ( ( char * ) irq_ptr ) ) ;
/* wipes qib.ac, required by ar7063 */
memset ( irq_ptr - > qdr , 0 , sizeof ( struct qdr ) ) ;
irq_ptr - > int_parm = init_data - > int_parm ;
2006-01-06 00:19:21 -08:00
irq_ptr - > schid = ccw_device_get_subchannel_id ( init_data - > cdev ) ;
2005-04-16 15:20:36 -07:00
irq_ptr - > no_input_qs = init_data - > no_input_qs ;
irq_ptr - > no_output_qs = init_data - > no_output_qs ;
if ( init_data - > q_format = = QDIO_IQDIO_QFMT ) {
irq_ptr - > is_iqdio_irq = 1 ;
irq_ptr - > is_thinint_irq = 1 ;
} else {
irq_ptr - > is_iqdio_irq = 0 ;
irq_ptr - > is_thinint_irq = hydra_thinints ;
}
sprintf ( dbf_text , " is_i_t%1x%1x " ,
irq_ptr - > is_iqdio_irq , irq_ptr - > is_thinint_irq ) ;
QDIO_DBF_TEXT2 ( 0 , setup , dbf_text ) ;
if ( irq_ptr - > is_thinint_irq ) {
2006-01-06 00:19:20 -08:00
irq_ptr - > dev_st_chg_ind = qdio_get_indicator ( ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_HEX1 ( 0 , setup , & irq_ptr - > dev_st_chg_ind , sizeof ( void * ) ) ;
if ( ! irq_ptr - > dev_st_chg_ind ) {
QDIO_PRINT_WARN ( " no indicator location available " \
2006-01-06 00:19:25 -08:00
" for irq 0.%x.%x \n " ,
irq_ptr - > schid . ssid , irq_ptr - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
qdio_release_irq_memory ( irq_ptr ) ;
return - ENOBUFS ;
}
}
/* defaults */
irq_ptr - > equeue . cmd = DEFAULT_ESTABLISH_QS_CMD ;
irq_ptr - > equeue . count = DEFAULT_ESTABLISH_QS_COUNT ;
irq_ptr - > aqueue . cmd = DEFAULT_ACTIVATE_QS_CMD ;
irq_ptr - > aqueue . count = DEFAULT_ACTIVATE_QS_COUNT ;
qdio_fill_qs ( irq_ptr , init_data - > cdev ,
init_data - > no_input_qs ,
init_data - > no_output_qs ,
init_data - > input_handler ,
init_data - > output_handler , init_data - > int_parm ,
init_data - > q_format , init_data - > flags ,
init_data - > input_sbal_addr_array ,
init_data - > output_sbal_addr_array ) ;
if ( ! try_module_get ( THIS_MODULE ) ) {
QDIO_PRINT_CRIT ( " try_module_get() failed! \n " ) ;
qdio_release_irq_memory ( irq_ptr ) ;
return - EINVAL ;
}
qdio_fill_thresholds ( irq_ptr , init_data - > no_input_qs ,
init_data - > no_output_qs ,
init_data - > min_input_threshold ,
init_data - > max_input_threshold ,
init_data - > min_output_threshold ,
init_data - > max_output_threshold ) ;
/* fill in qdr */
irq_ptr - > qdr - > qfmt = init_data - > q_format ;
irq_ptr - > qdr - > iqdcnt = init_data - > no_input_qs ;
irq_ptr - > qdr - > oqdcnt = init_data - > no_output_qs ;
irq_ptr - > qdr - > iqdsz = sizeof ( struct qdesfmt0 ) / 4 ; /* size in words */
irq_ptr - > qdr - > oqdsz = sizeof ( struct qdesfmt0 ) / 4 ;
irq_ptr - > qdr - > qiba = ( unsigned long ) & irq_ptr - > qib ;
irq_ptr - > qdr - > qkey = QDIO_STORAGE_KEY ;
/* fill in qib */
2006-01-06 00:19:20 -08:00
irq_ptr - > is_qebsm = is_passthrough ;
if ( irq_ptr - > is_qebsm )
irq_ptr - > qib . rflags | = QIB_RFLAGS_ENABLE_QEBSM ;
2005-04-16 15:20:36 -07:00
irq_ptr - > qib . qfmt = init_data - > q_format ;
if ( init_data - > no_input_qs )
irq_ptr - > qib . isliba = ( unsigned long ) ( irq_ptr - > input_qs [ 0 ] - > slib ) ;
if ( init_data - > no_output_qs )
irq_ptr - > qib . osliba = ( unsigned long ) ( irq_ptr - > output_qs [ 0 ] - > slib ) ;
memcpy ( irq_ptr - > qib . ebcnam , init_data - > adapter_name , 8 ) ;
qdio_set_impl_params ( irq_ptr , init_data - > qib_param_field_format ,
init_data - > qib_param_field ,
init_data - > no_input_qs ,
init_data - > no_output_qs ,
init_data - > input_slib_elements ,
init_data - > output_slib_elements ) ;
/* first input descriptors, then output descriptors */
is_iqdio = ( init_data - > q_format = = QDIO_IQDIO_QFMT ) ? 1 : 0 ;
for ( i = 0 ; i < init_data - > no_input_qs ; i + + )
qdio_allocate_fill_input_desc ( irq_ptr , i , is_iqdio ) ;
for ( i = 0 ; i < init_data - > no_output_qs ; i + + )
qdio_allocate_fill_output_desc ( irq_ptr , i ,
init_data - > no_input_qs ,
is_iqdio ) ;
/* qdr, qib, sls, slsbs, slibs, sbales filled. */
/* get qdio commands */
ciw = ccw_device_get_ciw ( init_data - > cdev , CIW_TYPE_EQUEUE ) ;
if ( ! ciw ) {
QDIO_DBF_TEXT2 ( 1 , setup , " no eq " ) ;
QDIO_PRINT_INFO ( " No equeue CIW found for QDIO commands. "
" Trying to use default. \n " ) ;
} else
irq_ptr - > equeue = * ciw ;
ciw = ccw_device_get_ciw ( init_data - > cdev , CIW_TYPE_AQUEUE ) ;
if ( ! ciw ) {
QDIO_DBF_TEXT2 ( 1 , setup , " no aq " ) ;
QDIO_PRINT_INFO ( " No aqueue CIW found for QDIO commands. "
" Trying to use default. \n " ) ;
} else
irq_ptr - > aqueue = * ciw ;
/* Set new interrupt handler. */
irq_ptr - > original_int_handler = init_data - > cdev - > handler ;
init_data - > cdev - > handler = qdio_handler ;
return 0 ;
}
int
qdio_establish ( struct qdio_initialize * init_data )
{
struct qdio_irq * irq_ptr ;
unsigned long saveflags ;
int result , result2 ;
struct ccw_device * cdev ;
char dbf_text [ 20 ] ;
cdev = init_data - > cdev ;
irq_ptr = cdev - > private - > qdio_data ;
if ( ! irq_ptr )
return - EINVAL ;
if ( cdev - > private - > state ! = DEV_STATE_ONLINE )
return - EINVAL ;
down ( & irq_ptr - > setting_up_sema ) ;
qdio_fill_irq ( init_data ) ;
/* the thinint CHSC stuff */
if ( irq_ptr - > is_thinint_irq ) {
result = tiqdio_set_subchannel_ind ( irq_ptr , 0 ) ;
if ( result ) {
up ( & irq_ptr - > setting_up_sema ) ;
qdio_shutdown ( cdev , QDIO_FLAG_CLEANUP_USING_CLEAR ) ;
return result ;
}
tiqdio_set_delay_target ( irq_ptr , TIQDIO_DELAY_TARGET ) ;
}
2006-10-11 15:31:38 +02:00
sprintf ( dbf_text , " qest%4x " , cdev - > private - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
QDIO_DBF_TEXT0 ( 0 , trace , dbf_text ) ;
/* establish q */
irq_ptr - > ccw . cmd_code = irq_ptr - > equeue . cmd ;
irq_ptr - > ccw . flags = CCW_FLAG_SLI ;
irq_ptr - > ccw . count = irq_ptr - > equeue . count ;
irq_ptr - > ccw . cda = QDIO_GET_ADDR ( irq_ptr - > qdr ) ;
spin_lock_irqsave ( get_ccwdev_lock ( cdev ) , saveflags ) ;
2007-02-12 15:47:18 +01:00
ccw_device_set_options_mask ( cdev , 0 ) ;
2008-02-09 18:24:32 +01:00
result = ccw_device_start ( cdev , & irq_ptr - > ccw ,
QDIO_DOING_ESTABLISH , 0 , 0 ) ;
2005-04-16 15:20:36 -07:00
if ( result ) {
2008-02-09 18:24:32 +01:00
result2 = ccw_device_start ( cdev , & irq_ptr - > ccw ,
QDIO_DOING_ESTABLISH , 0 , 0 ) ;
2005-04-16 15:20:36 -07:00
sprintf ( dbf_text , " eq:io%4x " , result ) ;
QDIO_DBF_TEXT2 ( 1 , setup , dbf_text ) ;
if ( result2 ) {
sprintf ( dbf_text , " eq:io%4x " , result ) ;
QDIO_DBF_TEXT2 ( 1 , setup , dbf_text ) ;
}
2006-01-06 00:19:25 -08:00
QDIO_PRINT_WARN ( " establish queues on irq 0.%x.%04x: do_IO " \
" returned %i, next try returned %i \n " ,
irq_ptr - > schid . ssid , irq_ptr - > schid . sch_no ,
result , result2 ) ;
2005-04-16 15:20:36 -07:00
result = result2 ;
if ( result )
ccw_device_set_timeout ( cdev , 0 ) ;
}
spin_unlock_irqrestore ( get_ccwdev_lock ( cdev ) , saveflags ) ;
if ( result ) {
up ( & irq_ptr - > setting_up_sema ) ;
qdio_shutdown ( cdev , QDIO_FLAG_CLEANUP_USING_CLEAR ) ;
return result ;
}
2008-02-09 18:24:32 +01:00
wait_event_interruptible_timeout ( cdev - > private - > wait_q ,
irq_ptr - > state = = QDIO_IRQ_STATE_ESTABLISHED | |
irq_ptr - > state = = QDIO_IRQ_STATE_ERR ,
QDIO_ESTABLISH_TIMEOUT ) ;
2005-04-16 15:20:36 -07:00
if ( irq_ptr - > state = = QDIO_IRQ_STATE_ESTABLISHED )
result = 0 ;
else {
up ( & irq_ptr - > setting_up_sema ) ;
qdio_shutdown ( cdev , QDIO_FLAG_CLEANUP_USING_CLEAR ) ;
return - EIO ;
}
2008-04-17 07:46:22 +02:00
qdio_get_ssqd_siga ( irq_ptr ) ;
2005-04-16 15:20:36 -07:00
/* if this gets set once, we're running under VM and can omit SVSes */
if ( irq_ptr - > qdioac & CHSC_FLAG_SIGA_SYNC_NECESSARY )
omit_svs = 1 ;
sprintf ( dbf_text , " qdioac%2x " , irq_ptr - > qdioac ) ;
QDIO_DBF_TEXT2 ( 0 , setup , dbf_text ) ;
sprintf ( dbf_text , " qib ac%2x " , irq_ptr - > qib . ac ) ;
QDIO_DBF_TEXT2 ( 0 , setup , dbf_text ) ;
irq_ptr - > hydra_gives_outbound_pcis =
irq_ptr - > qib . ac & QIB_AC_OUTBOUND_PCI_SUPPORTED ;
irq_ptr - > sync_done_on_outb_pcis =
irq_ptr - > qdioac & CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS ;
qdio_initialize_set_siga_flags_input ( irq_ptr ) ;
qdio_initialize_set_siga_flags_output ( irq_ptr ) ;
up ( & irq_ptr - > setting_up_sema ) ;
return result ;
}
int
qdio_activate ( struct ccw_device * cdev , int flags )
{
struct qdio_irq * irq_ptr ;
int i , result = 0 , result2 ;
unsigned long saveflags ;
char dbf_text [ 20 ] ; /* see qdio_initialize */
irq_ptr = cdev - > private - > qdio_data ;
if ( ! irq_ptr )
return - ENODEV ;
if ( cdev - > private - > state ! = DEV_STATE_ONLINE )
return - EINVAL ;
down ( & irq_ptr - > setting_up_sema ) ;
if ( irq_ptr - > state = = QDIO_IRQ_STATE_INACTIVE ) {
result = - EBUSY ;
goto out ;
}
2006-01-06 00:19:21 -08:00
sprintf ( dbf_text , " qact%4x " , irq_ptr - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT2 ( 0 , setup , dbf_text ) ;
QDIO_DBF_TEXT2 ( 0 , trace , dbf_text ) ;
/* activate q */
irq_ptr - > ccw . cmd_code = irq_ptr - > aqueue . cmd ;
irq_ptr - > ccw . flags = CCW_FLAG_SLI ;
irq_ptr - > ccw . count = irq_ptr - > aqueue . count ;
irq_ptr - > ccw . cda = QDIO_GET_ADDR ( 0 ) ;
spin_lock_irqsave ( get_ccwdev_lock ( cdev ) , saveflags ) ;
ccw_device_set_timeout ( cdev , 0 ) ;
ccw_device_set_options ( cdev , CCWDEV_REPORT_ALL ) ;
result = ccw_device_start ( cdev , & irq_ptr - > ccw , QDIO_DOING_ACTIVATE ,
0 , DOIO_DENY_PREFETCH ) ;
if ( result ) {
result2 = ccw_device_start ( cdev , & irq_ptr - > ccw ,
QDIO_DOING_ACTIVATE , 0 , 0 ) ;
sprintf ( dbf_text , " aq:io%4x " , result ) ;
QDIO_DBF_TEXT2 ( 1 , setup , dbf_text ) ;
if ( result2 ) {
sprintf ( dbf_text , " aq:io%4x " , result ) ;
QDIO_DBF_TEXT2 ( 1 , setup , dbf_text ) ;
}
2006-01-06 00:19:25 -08:00
QDIO_PRINT_WARN ( " activate queues on irq 0.%x.%04x: do_IO " \
" returned %i, next try returned %i \n " ,
irq_ptr - > schid . ssid , irq_ptr - > schid . sch_no ,
result , result2 ) ;
2005-04-16 15:20:36 -07:00
result = result2 ;
}
spin_unlock_irqrestore ( get_ccwdev_lock ( cdev ) , saveflags ) ;
if ( result )
goto out ;
for ( i = 0 ; i < irq_ptr - > no_input_qs ; i + + ) {
if ( irq_ptr - > is_thinint_irq ) {
/*
* that way we know , that , if we will get interrupted
* by tiqdio_inbound_processing , qdio_unmark_q will
* not be called
*/
qdio_reserve_q ( irq_ptr - > input_qs [ i ] ) ;
qdio_mark_tiq ( irq_ptr - > input_qs [ i ] ) ;
qdio_release_q ( irq_ptr - > input_qs [ i ] ) ;
}
}
if ( flags & QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT ) {
for ( i = 0 ; i < irq_ptr - > no_input_qs ; i + + ) {
irq_ptr - > input_qs [ i ] - > is_input_q | =
QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT ;
}
}
2008-02-19 15:29:30 +01:00
msleep ( QDIO_ACTIVATE_TIMEOUT ) ;
2005-04-16 15:20:36 -07:00
switch ( irq_ptr - > state ) {
case QDIO_IRQ_STATE_STOPPED :
case QDIO_IRQ_STATE_ERR :
up ( & irq_ptr - > setting_up_sema ) ;
qdio_shutdown ( cdev , QDIO_FLAG_CLEANUP_USING_CLEAR ) ;
down ( & irq_ptr - > setting_up_sema ) ;
result = - EIO ;
break ;
default :
qdio_set_state ( irq_ptr , QDIO_IRQ_STATE_ACTIVE ) ;
result = 0 ;
}
out :
up ( & irq_ptr - > setting_up_sema ) ;
return result ;
}
/* buffers filled forwards again to make Rick happy */
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
qdio_do_qdio_fill_input ( struct qdio_q * q , unsigned int qidx ,
unsigned int count , struct qdio_buffer * buffers )
{
2006-01-06 00:19:20 -08:00
struct qdio_irq * irq = ( struct qdio_irq * ) q - > irq_ptr ;
2007-03-19 13:18:12 +01:00
int tmp = 0 ;
2006-01-06 00:19:20 -08:00
qidx & = ( QDIO_MAX_BUFFERS_PER_Q - 1 ) ;
if ( irq - > is_qebsm ) {
2007-03-19 13:18:12 +01:00
while ( count ) {
tmp = set_slsb ( q , & qidx , SLSB_CU_INPUT_EMPTY , & count ) ;
if ( ! tmp )
return ;
}
2006-01-06 00:19:20 -08:00
return ;
}
2005-04-16 15:20:36 -07:00
for ( ; ; ) {
2006-01-06 00:19:20 -08:00
set_slsb ( q , & qidx , SLSB_CU_INPUT_EMPTY , & count ) ;
2005-04-16 15:20:36 -07:00
count - - ;
if ( ! count ) break ;
2006-01-06 00:19:20 -08:00
qidx = ( qidx + 1 ) & ( QDIO_MAX_BUFFERS_PER_Q - 1 ) ;
2005-04-16 15:20:36 -07:00
}
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
qdio_do_qdio_fill_output ( struct qdio_q * q , unsigned int qidx ,
unsigned int count , struct qdio_buffer * buffers )
{
2006-01-06 00:19:20 -08:00
struct qdio_irq * irq = ( struct qdio_irq * ) q - > irq_ptr ;
2007-03-19 13:18:12 +01:00
int tmp = 0 ;
2006-01-06 00:19:20 -08:00
qidx & = ( QDIO_MAX_BUFFERS_PER_Q - 1 ) ;
if ( irq - > is_qebsm ) {
2007-03-19 13:18:12 +01:00
while ( count ) {
tmp = set_slsb ( q , & qidx , SLSB_CU_OUTPUT_PRIMED , & count ) ;
if ( ! tmp )
return ;
}
2006-01-06 00:19:20 -08:00
return ;
}
2005-04-16 15:20:36 -07:00
for ( ; ; ) {
2006-01-06 00:19:20 -08:00
set_slsb ( q , & qidx , SLSB_CU_OUTPUT_PRIMED , & count ) ;
2005-04-16 15:20:36 -07:00
count - - ;
if ( ! count ) break ;
2006-01-06 00:19:20 -08:00
qidx = ( qidx + 1 ) & ( QDIO_MAX_BUFFERS_PER_Q - 1 ) ;
2005-04-16 15:20:36 -07:00
}
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
do_qdio_handle_inbound ( struct qdio_q * q , unsigned int callflags ,
unsigned int qidx , unsigned int count ,
struct qdio_buffer * buffers )
{
int used_elements ;
/* This is the inbound handling of queues */
used_elements = atomic_add_return ( count , & q - > number_of_buffers_used ) - count ;
qdio_do_qdio_fill_input ( q , qidx , count , buffers ) ;
if ( ( used_elements + count = = QDIO_MAX_BUFFERS_PER_Q ) & &
( callflags & QDIO_FLAG_UNDER_INTERRUPT ) )
2006-12-04 15:40:59 +01:00
atomic_xchg ( & q - > polling , 0 ) ;
2005-04-16 15:20:36 -07:00
if ( used_elements )
return ;
if ( callflags & QDIO_FLAG_DONT_SIGA )
return ;
if ( q - > siga_in ) {
int result ;
result = qdio_siga_input ( q ) ;
if ( result ) {
if ( q - > siga_error )
q - > error_status_flags | =
QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR ;
q - > error_status_flags | = QDIO_STATUS_LOOK_FOR_ERROR ;
q - > siga_error = result ;
}
}
qdio_mark_q ( q ) ;
}
2007-02-05 21:18:53 +01:00
static void
2005-04-16 15:20:36 -07:00
do_qdio_handle_outbound ( struct qdio_q * q , unsigned int callflags ,
unsigned int qidx , unsigned int count ,
struct qdio_buffer * buffers )
{
int used_elements ;
2006-01-06 00:19:20 -08:00
unsigned int cnt , start_buf ;
unsigned char state = 0 ;
struct qdio_irq * irq = ( struct qdio_irq * ) q - > irq_ptr ;
2005-04-16 15:20:36 -07:00
/* This is the outbound handling of queues */
qdio_do_qdio_fill_output ( q , qidx , count , buffers ) ;
used_elements = atomic_add_return ( count , & q - > number_of_buffers_used ) - count ;
if ( callflags & QDIO_FLAG_DONT_SIGA ) {
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . outbound_cnt ) ;
2005-04-16 15:20:36 -07:00
return ;
}
2007-05-02 15:18:07 +02:00
if ( callflags & QDIO_FLAG_PCI_OUT )
q - > is_pci_out = 1 ;
else
q - > is_pci_out = 0 ;
2005-04-16 15:20:36 -07:00
if ( q - > is_iqdio_q ) {
/* one siga for every sbal */
while ( count - - )
qdio_kick_outbound_q ( q ) ;
__qdio_outbound_processing ( q ) ;
} else {
/* under VM, we do a SIGA sync unconditionally */
SYNC_MEMORY ;
else {
/*
* w / o shadow queues ( else branch of
* SYNC_MEMORY : - / ) , we try to
* fast - requeue buffers
*/
2006-01-06 00:19:20 -08:00
if ( irq - > is_qebsm ) {
cnt = 1 ;
start_buf = ( ( qidx + QDIO_MAX_BUFFERS_PER_Q - 1 ) &
( QDIO_MAX_BUFFERS_PER_Q - 1 ) ) ;
qdio_do_eqbs ( q , & state , & start_buf , & cnt ) ;
} else
state = q - > slsb . acc . val [ ( qidx + QDIO_MAX_BUFFERS_PER_Q - 1 )
& ( QDIO_MAX_BUFFERS_PER_Q - 1 ) ] ;
if ( state ! = SLSB_CU_OUTPUT_PRIMED ) {
2005-04-16 15:20:36 -07:00
qdio_kick_outbound_q ( q ) ;
} else {
QDIO_DBF_TEXT3 ( 0 , trace , " fast-req " ) ;
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . fast_reqs ) ;
2005-04-16 15:20:36 -07:00
}
}
/*
* only marking the q could take too long ,
* the upper layer module could do a lot of
* traffic in that time
*/
__qdio_outbound_processing ( q ) ;
}
2007-05-04 18:47:49 +02:00
qdio_perf_stat_inc ( & perf_stats . outbound_cnt ) ;
2005-04-16 15:20:36 -07:00
}
/* count must be 1 in iqdio */
int
do_QDIO ( struct ccw_device * cdev , unsigned int callflags ,
unsigned int queue_number , unsigned int qidx ,
unsigned int count , struct qdio_buffer * buffers )
{
struct qdio_irq * irq_ptr ;
# ifdef CONFIG_QDIO_DEBUG
char dbf_text [ 20 ] ;
2006-10-18 18:30:47 +02:00
sprintf ( dbf_text , " doQD%04x " , cdev - > private - > schid . sch_no ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT3 ( 0 , trace , dbf_text ) ;
# endif /* CONFIG_QDIO_DEBUG */
if ( ( qidx > QDIO_MAX_BUFFERS_PER_Q ) | |
( count > QDIO_MAX_BUFFERS_PER_Q ) | |
( queue_number > QDIO_MAX_QUEUES_PER_IRQ ) )
return - EINVAL ;
if ( count = = 0 )
return 0 ;
irq_ptr = cdev - > private - > qdio_data ;
if ( ! irq_ptr )
return - ENODEV ;
# ifdef CONFIG_QDIO_DEBUG
if ( callflags & QDIO_FLAG_SYNC_INPUT )
QDIO_DBF_HEX3 ( 0 , trace , & irq_ptr - > input_qs [ queue_number ] ,
sizeof ( void * ) ) ;
else
QDIO_DBF_HEX3 ( 0 , trace , & irq_ptr - > output_qs [ queue_number ] ,
sizeof ( void * ) ) ;
sprintf ( dbf_text , " flag%04x " , callflags ) ;
QDIO_DBF_TEXT3 ( 0 , trace , dbf_text ) ;
sprintf ( dbf_text , " qi%02xct%02x " , qidx , count ) ;
QDIO_DBF_TEXT3 ( 0 , trace , dbf_text ) ;
# endif /* CONFIG_QDIO_DEBUG */
if ( irq_ptr - > state ! = QDIO_IRQ_STATE_ACTIVE )
return - EBUSY ;
if ( callflags & QDIO_FLAG_SYNC_INPUT )
do_qdio_handle_inbound ( irq_ptr - > input_qs [ queue_number ] ,
callflags , qidx , count , buffers ) ;
else if ( callflags & QDIO_FLAG_SYNC_OUTPUT )
do_qdio_handle_outbound ( irq_ptr - > output_qs [ queue_number ] ,
callflags , qidx , count , buffers ) ;
else {
QDIO_DBF_TEXT3 ( 1 , trace , " doQD:inv " ) ;
return - EINVAL ;
}
return 0 ;
}
static int
qdio_perf_procfile_read ( char * buffer , char * * buffer_location , off_t offset ,
int buffer_length , int * eof , void * data )
{
int c = 0 ;
/* we are always called with buffer_length=4k, so we all
deliver on the first read */
if ( offset > 0 )
return 0 ;
# define _OUTP_IT(x...) c+=sprintf(buffer+c,x)
2007-05-04 18:47:49 +02:00
# ifdef CONFIG_64BIT
_OUTP_IT ( " Number of tasklet runs (total) : %li \n " ,
( long ) atomic64_read ( & perf_stats . tl_runs ) ) ;
_OUTP_IT ( " Inbound tasklet runs tried/retried : %li/%li \n " ,
( long ) atomic64_read ( & perf_stats . inbound_tl_runs ) ,
( long ) atomic64_read ( & perf_stats . inbound_tl_runs_resched ) ) ;
_OUTP_IT ( " Inbound-thin tasklet runs tried/retried : %li/%li \n " ,
( long ) atomic64_read ( & perf_stats . inbound_thin_tl_runs ) ,
( long ) atomic64_read ( & perf_stats . inbound_thin_tl_runs_resched ) ) ;
_OUTP_IT ( " Outbound tasklet runs tried/retried : %li/%li \n " ,
( long ) atomic64_read ( & perf_stats . outbound_tl_runs ) ,
( long ) atomic64_read ( & perf_stats . outbound_tl_runs_resched ) ) ;
2005-04-16 15:20:36 -07:00
_OUTP_IT ( " \n " ) ;
2007-05-04 18:47:49 +02:00
_OUTP_IT ( " Number of SIGA sync's issued : %li \n " ,
( long ) atomic64_read ( & perf_stats . siga_syncs ) ) ;
_OUTP_IT ( " Number of SIGA in's issued : %li \n " ,
( long ) atomic64_read ( & perf_stats . siga_ins ) ) ;
_OUTP_IT ( " Number of SIGA out's issued : %li \n " ,
( long ) atomic64_read ( & perf_stats . siga_outs ) ) ;
_OUTP_IT ( " Number of PCIs caught : %li \n " ,
( long ) atomic64_read ( & perf_stats . pcis ) ) ;
_OUTP_IT ( " Number of adapter interrupts caught : %li \n " ,
( long ) atomic64_read ( & perf_stats . thinints ) ) ;
_OUTP_IT ( " Number of fast requeues (outg. SBALs w/o SIGA) : %li \n " ,
( long ) atomic64_read ( & perf_stats . fast_reqs ) ) ;
2005-04-16 15:20:36 -07:00
_OUTP_IT ( " \n " ) ;
2007-05-04 18:47:49 +02:00
_OUTP_IT ( " Number of inbound transfers : %li \n " ,
( long ) atomic64_read ( & perf_stats . inbound_cnt ) ) ;
_OUTP_IT ( " Number of do_QDIOs outbound : %li \n " ,
( long ) atomic64_read ( & perf_stats . outbound_cnt ) ) ;
# else /* CONFIG_64BIT */
_OUTP_IT ( " Number of tasklet runs (total) : %i \n " ,
atomic_read ( & perf_stats . tl_runs ) ) ;
_OUTP_IT ( " Inbound tasklet runs tried/retried : %i/%i \n " ,
atomic_read ( & perf_stats . inbound_tl_runs ) ,
atomic_read ( & perf_stats . inbound_tl_runs_resched ) ) ;
_OUTP_IT ( " Inbound-thin tasklet runs tried/retried : %i/%i \n " ,
atomic_read ( & perf_stats . inbound_thin_tl_runs ) ,
atomic_read ( & perf_stats . inbound_thin_tl_runs_resched ) ) ;
_OUTP_IT ( " Outbound tasklet runs tried/retried : %i/%i \n " ,
atomic_read ( & perf_stats . outbound_tl_runs ) ,
atomic_read ( & perf_stats . outbound_tl_runs_resched ) ) ;
_OUTP_IT ( " \n " ) ;
_OUTP_IT ( " Number of SIGA sync's issued : %i \n " ,
atomic_read ( & perf_stats . siga_syncs ) ) ;
_OUTP_IT ( " Number of SIGA in's issued : %i \n " ,
atomic_read ( & perf_stats . siga_ins ) ) ;
_OUTP_IT ( " Number of SIGA out's issued : %i \n " ,
atomic_read ( & perf_stats . siga_outs ) ) ;
_OUTP_IT ( " Number of PCIs caught : %i \n " ,
atomic_read ( & perf_stats . pcis ) ) ;
_OUTP_IT ( " Number of adapter interrupts caught : %i \n " ,
atomic_read ( & perf_stats . thinints ) ) ;
_OUTP_IT ( " Number of fast requeues (outg. SBALs w/o SIGA) : %i \n " ,
atomic_read ( & perf_stats . fast_reqs ) ) ;
_OUTP_IT ( " \n " ) ;
_OUTP_IT ( " Number of inbound transfers : %i \n " ,
atomic_read ( & perf_stats . inbound_cnt ) ) ;
_OUTP_IT ( " Number of do_QDIOs outbound : %i \n " ,
atomic_read ( & perf_stats . outbound_cnt ) ) ;
# endif /* CONFIG_64BIT */
2005-04-16 15:20:36 -07:00
_OUTP_IT ( " \n " ) ;
return c ;
}
static struct proc_dir_entry * qdio_perf_proc_file ;
static void
qdio_add_procfs_entry ( void )
{
proc_perf_file_registration = 0 ;
qdio_perf_proc_file = create_proc_entry ( QDIO_PERF ,
S_IFREG | 0444 , & proc_root ) ;
if ( qdio_perf_proc_file ) {
qdio_perf_proc_file - > read_proc = & qdio_perf_procfile_read ;
} else proc_perf_file_registration = - 1 ;
if ( proc_perf_file_registration )
QDIO_PRINT_WARN ( " was not able to register perf. " \
" proc-file (%i). \n " ,
proc_perf_file_registration ) ;
}
static void
qdio_remove_procfs_entry ( void )
{
if ( ! proc_perf_file_registration ) /* means if it went ok earlier */
remove_proc_entry ( QDIO_PERF , & proc_root ) ;
}
2006-12-08 15:54:18 +01:00
/**
* attributes in sysfs
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static ssize_t
qdio_performance_stats_show ( struct bus_type * bus , char * buf )
{
return sprintf ( buf , " %i \n " , qdio_performance_stats ? 1 : 0 ) ;
}
static ssize_t
qdio_performance_stats_store ( struct bus_type * bus , const char * buf , size_t count )
{
char * tmp ;
int i ;
i = simple_strtoul ( buf , & tmp , 16 ) ;
if ( ( i = = 0 ) | | ( i = = 1 ) ) {
if ( i = = qdio_performance_stats )
return count ;
qdio_performance_stats = i ;
if ( i = = 0 ) {
/* reset perf. stat. info */
2007-05-04 18:47:49 +02:00
# ifdef CONFIG_64BIT
atomic64_set ( & perf_stats . tl_runs , 0 ) ;
atomic64_set ( & perf_stats . outbound_tl_runs , 0 ) ;
atomic64_set ( & perf_stats . inbound_tl_runs , 0 ) ;
atomic64_set ( & perf_stats . inbound_tl_runs_resched , 0 ) ;
atomic64_set ( & perf_stats . inbound_thin_tl_runs , 0 ) ;
atomic64_set ( & perf_stats . inbound_thin_tl_runs_resched ,
0 ) ;
atomic64_set ( & perf_stats . siga_outs , 0 ) ;
atomic64_set ( & perf_stats . siga_ins , 0 ) ;
atomic64_set ( & perf_stats . siga_syncs , 0 ) ;
atomic64_set ( & perf_stats . pcis , 0 ) ;
atomic64_set ( & perf_stats . thinints , 0 ) ;
atomic64_set ( & perf_stats . fast_reqs , 0 ) ;
atomic64_set ( & perf_stats . outbound_cnt , 0 ) ;
atomic64_set ( & perf_stats . inbound_cnt , 0 ) ;
# else /* CONFIG_64BIT */
atomic_set ( & perf_stats . tl_runs , 0 ) ;
atomic_set ( & perf_stats . outbound_tl_runs , 0 ) ;
atomic_set ( & perf_stats . inbound_tl_runs , 0 ) ;
atomic_set ( & perf_stats . inbound_tl_runs_resched , 0 ) ;
atomic_set ( & perf_stats . inbound_thin_tl_runs , 0 ) ;
atomic_set ( & perf_stats . inbound_thin_tl_runs_resched , 0 ) ;
atomic_set ( & perf_stats . siga_outs , 0 ) ;
atomic_set ( & perf_stats . siga_ins , 0 ) ;
atomic_set ( & perf_stats . siga_syncs , 0 ) ;
atomic_set ( & perf_stats . pcis , 0 ) ;
atomic_set ( & perf_stats . thinints , 0 ) ;
atomic_set ( & perf_stats . fast_reqs , 0 ) ;
atomic_set ( & perf_stats . outbound_cnt , 0 ) ;
atomic_set ( & perf_stats . inbound_cnt , 0 ) ;
# endif /* CONFIG_64BIT */
2006-12-08 15:54:18 +01:00
}
} else {
2007-10-12 16:11:40 +02:00
QDIO_PRINT_ERR ( " QDIO performance_stats: write 0 or 1 to this file! \n " ) ;
2006-12-08 15:54:18 +01:00
return - EINVAL ;
}
return count ;
}
static BUS_ATTR ( qdio_performance_stats , 0644 , qdio_performance_stats_show ,
qdio_performance_stats_store ) ;
2005-04-16 15:20:36 -07:00
static void
tiqdio_register_thinints ( void )
{
char dbf_text [ 20 ] ;
2008-01-26 14:10:44 +01:00
tiqdio_ind =
s390_register_adapter_interrupt ( & tiqdio_thinint_handler , NULL ) ;
if ( IS_ERR ( tiqdio_ind ) ) {
sprintf ( dbf_text , " regthn%lx " , PTR_ERR ( tiqdio_ind ) ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT0 ( 0 , setup , dbf_text ) ;
QDIO_PRINT_ERR ( " failed to register adapter handler " \
2008-01-26 14:10:44 +01:00
" (rc=%li). \n Adapter interrupts might " \
2005-04-16 15:20:36 -07:00
" not work. Continuing. \n " ,
2008-01-26 14:10:44 +01:00
PTR_ERR ( tiqdio_ind ) ) ;
tiqdio_ind = NULL ;
2005-04-16 15:20:36 -07:00
}
}
static void
tiqdio_unregister_thinints ( void )
{
2008-01-26 14:10:44 +01:00
if ( tiqdio_ind )
s390_unregister_adapter_interrupt ( tiqdio_ind ) ;
2005-04-16 15:20:36 -07:00
}
static int
qdio_get_qdio_memory ( void )
{
int i ;
indicator_used [ 0 ] = 1 ;
for ( i = 1 ; i < INDICATORS_PER_CACHELINE ; i + + )
indicator_used [ i ] = 0 ;
2006-03-24 03:15:31 -08:00
indicators = kzalloc ( sizeof ( __u32 ) * ( INDICATORS_PER_CACHELINE ) ,
2008-01-26 14:10:44 +01:00
GFP_KERNEL ) ;
if ( ! indicators )
2006-03-24 03:15:31 -08:00
return - ENOMEM ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static void
qdio_release_qdio_memory ( void )
{
2005-11-07 01:01:30 -08:00
kfree ( indicators ) ;
2005-04-16 15:20:36 -07:00
}
static void
qdio_unregister_dbf_views ( void )
{
if ( qdio_dbf_setup )
debug_unregister ( qdio_dbf_setup ) ;
if ( qdio_dbf_sbal )
debug_unregister ( qdio_dbf_sbal ) ;
if ( qdio_dbf_sense )
debug_unregister ( qdio_dbf_sense ) ;
if ( qdio_dbf_trace )
debug_unregister ( qdio_dbf_trace ) ;
# ifdef CONFIG_QDIO_DEBUG
if ( qdio_dbf_slsb_out )
debug_unregister ( qdio_dbf_slsb_out ) ;
if ( qdio_dbf_slsb_in )
debug_unregister ( qdio_dbf_slsb_in ) ;
# endif /* CONFIG_QDIO_DEBUG */
}
static int
qdio_register_dbf_views ( void )
{
qdio_dbf_setup = debug_register ( QDIO_DBF_SETUP_NAME ,
2005-06-25 14:55:33 -07:00
QDIO_DBF_SETUP_PAGES ,
2005-04-16 15:20:36 -07:00
QDIO_DBF_SETUP_NR_AREAS ,
QDIO_DBF_SETUP_LEN ) ;
if ( ! qdio_dbf_setup )
goto oom ;
debug_register_view ( qdio_dbf_setup , & debug_hex_ascii_view ) ;
debug_set_level ( qdio_dbf_setup , QDIO_DBF_SETUP_LEVEL ) ;
qdio_dbf_sbal = debug_register ( QDIO_DBF_SBAL_NAME ,
2005-06-25 14:55:33 -07:00
QDIO_DBF_SBAL_PAGES ,
2005-04-16 15:20:36 -07:00
QDIO_DBF_SBAL_NR_AREAS ,
QDIO_DBF_SBAL_LEN ) ;
if ( ! qdio_dbf_sbal )
goto oom ;
debug_register_view ( qdio_dbf_sbal , & debug_hex_ascii_view ) ;
debug_set_level ( qdio_dbf_sbal , QDIO_DBF_SBAL_LEVEL ) ;
qdio_dbf_sense = debug_register ( QDIO_DBF_SENSE_NAME ,
2005-06-25 14:55:33 -07:00
QDIO_DBF_SENSE_PAGES ,
2005-04-16 15:20:36 -07:00
QDIO_DBF_SENSE_NR_AREAS ,
QDIO_DBF_SENSE_LEN ) ;
if ( ! qdio_dbf_sense )
goto oom ;
debug_register_view ( qdio_dbf_sense , & debug_hex_ascii_view ) ;
debug_set_level ( qdio_dbf_sense , QDIO_DBF_SENSE_LEVEL ) ;
qdio_dbf_trace = debug_register ( QDIO_DBF_TRACE_NAME ,
2005-06-25 14:55:33 -07:00
QDIO_DBF_TRACE_PAGES ,
2005-04-16 15:20:36 -07:00
QDIO_DBF_TRACE_NR_AREAS ,
QDIO_DBF_TRACE_LEN ) ;
if ( ! qdio_dbf_trace )
goto oom ;
debug_register_view ( qdio_dbf_trace , & debug_hex_ascii_view ) ;
debug_set_level ( qdio_dbf_trace , QDIO_DBF_TRACE_LEVEL ) ;
# ifdef CONFIG_QDIO_DEBUG
qdio_dbf_slsb_out = debug_register ( QDIO_DBF_SLSB_OUT_NAME ,
2005-06-25 14:55:33 -07:00
QDIO_DBF_SLSB_OUT_PAGES ,
2005-04-16 15:20:36 -07:00
QDIO_DBF_SLSB_OUT_NR_AREAS ,
QDIO_DBF_SLSB_OUT_LEN ) ;
if ( ! qdio_dbf_slsb_out )
goto oom ;
debug_register_view ( qdio_dbf_slsb_out , & debug_hex_ascii_view ) ;
debug_set_level ( qdio_dbf_slsb_out , QDIO_DBF_SLSB_OUT_LEVEL ) ;
qdio_dbf_slsb_in = debug_register ( QDIO_DBF_SLSB_IN_NAME ,
2005-06-25 14:55:33 -07:00
QDIO_DBF_SLSB_IN_PAGES ,
2005-04-16 15:20:36 -07:00
QDIO_DBF_SLSB_IN_NR_AREAS ,
QDIO_DBF_SLSB_IN_LEN ) ;
if ( ! qdio_dbf_slsb_in )
goto oom ;
debug_register_view ( qdio_dbf_slsb_in , & debug_hex_ascii_view ) ;
debug_set_level ( qdio_dbf_slsb_in , QDIO_DBF_SLSB_IN_LEVEL ) ;
# endif /* CONFIG_QDIO_DEBUG */
return 0 ;
oom :
QDIO_PRINT_ERR ( " not enough memory for dbf. \n " ) ;
qdio_unregister_dbf_views ( ) ;
return - ENOMEM ;
}
2006-04-27 18:40:09 -07:00
static void * qdio_mempool_alloc ( gfp_t gfp_mask , void * size )
{
return ( void * ) get_zeroed_page ( gfp_mask | GFP_DMA ) ;
}
static void qdio_mempool_free ( void * element , void * size )
{
free_page ( ( unsigned long ) element ) ;
}
2005-04-16 15:20:36 -07:00
static int __init
init_QDIO ( void )
{
int res ;
void * ptr ;
printk ( " qdio: loading %s \n " , version ) ;
res = qdio_get_qdio_memory ( ) ;
if ( res )
return res ;
2007-08-10 14:32:28 +02:00
qdio_q_cache = kmem_cache_create ( " qdio_q " , sizeof ( struct qdio_q ) ,
256 , 0 , NULL ) ;
if ( ! qdio_q_cache ) {
qdio_release_qdio_memory ( ) ;
return - ENOMEM ;
}
2005-04-16 15:20:36 -07:00
res = qdio_register_dbf_views ( ) ;
2007-08-10 14:32:28 +02:00
if ( res ) {
kmem_cache_destroy ( qdio_q_cache ) ;
qdio_release_qdio_memory ( ) ;
2005-04-16 15:20:36 -07:00
return res ;
2007-08-10 14:32:28 +02:00
}
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT0 ( 0 , setup , " initQDIO " ) ;
2006-12-08 15:54:18 +01:00
res = bus_create_file ( & ccw_bus_type , & bus_attr_qdio_performance_stats ) ;
2005-04-16 15:20:36 -07:00
2006-12-08 15:54:18 +01:00
memset ( ( void * ) & perf_stats , 0 , sizeof ( perf_stats ) ) ;
2005-04-16 15:20:36 -07:00
QDIO_DBF_TEXT0 ( 0 , setup , " perfstat " ) ;
ptr = & perf_stats ;
QDIO_DBF_HEX0 ( 0 , setup , & ptr , sizeof ( void * ) ) ;
qdio_add_procfs_entry ( ) ;
2006-04-27 18:40:09 -07:00
qdio_mempool_scssc = mempool_create ( QDIO_MEMPOOL_SCSSC_ELEMENTS ,
qdio_mempool_alloc ,
qdio_mempool_free , NULL ) ;
2005-04-16 15:20:36 -07:00
if ( tiqdio_check_chsc_availability ( ) )
QDIO_PRINT_ERR ( " Not all CHSCs supported. Continuing. \n " ) ;
tiqdio_register_thinints ( ) ;
return 0 ;
}
static void __exit
cleanup_QDIO ( void )
{
tiqdio_unregister_thinints ( ) ;
qdio_remove_procfs_entry ( ) ;
qdio_release_qdio_memory ( ) ;
qdio_unregister_dbf_views ( ) ;
2006-04-27 18:40:09 -07:00
mempool_destroy ( qdio_mempool_scssc ) ;
2007-08-10 14:32:28 +02:00
kmem_cache_destroy ( qdio_q_cache ) ;
2006-12-08 15:54:18 +01:00
bus_remove_file ( & ccw_bus_type , & bus_attr_qdio_performance_stats ) ;
2005-04-16 15:20:36 -07:00
printk ( " qdio: %s: module removed \n " , version ) ;
}
module_init ( init_QDIO ) ;
module_exit ( cleanup_QDIO ) ;
EXPORT_SYMBOL ( qdio_allocate ) ;
EXPORT_SYMBOL ( qdio_establish ) ;
EXPORT_SYMBOL ( qdio_initialize ) ;
EXPORT_SYMBOL ( qdio_activate ) ;
EXPORT_SYMBOL ( do_QDIO ) ;
EXPORT_SYMBOL ( qdio_shutdown ) ;
EXPORT_SYMBOL ( qdio_free ) ;
EXPORT_SYMBOL ( qdio_cleanup ) ;
EXPORT_SYMBOL ( qdio_synchronize ) ;