summaryrefslogblamecommitdiffstats
path: root/drivers/s390/cio/qdio.c
blob: 7c93a8798d234c89160138571489562c7ef14bd5 (plain) (tree)
1
2
3
4
5
6
7
8
9
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629








                                                              
                                                                  





















                                                                       






                          
                          

















                          







                                                                     
                                                            







                                                                   
                              





                                                    

                                     





























                                                                            
                    






















                                                                              


                                                  
 









                                               
 








                                                    
 





                                           
      

                                                               



                                                  



























                                                                           
      

                                                               



                                                  
























































                                                                        












                                                   
                                                













                                                        




                                                          
                           


                                            





                                                           

 


















                                                                    
                                                    






























                                                                    
                                              







                                                           
              


























                                                           
                  







                                                       
                   



































































                                                          


                                                             










                                                           



                                                                              





                                                                  





                                                     







































































                                                                             
















































































































                                                                             
 
                 

                                                   



                                            




                                                



                                                                  








































                                                                              
                                                                       
























                                                                     
                 




















                                                        
                 


















                                                                   
                  











































                                                                            


                                                            
                        
                                                   
                                                                     

                                                            
                              














                                                                     
                              

                                                   
                        
                                                                     

                                                            
                              



                                                       
                                               




                                                                     

 
                  

































































































                                                                            
                 

                                                  
                             

                            
                               










                                                



                                                                 



































                                                                              
                                                                              




                                                                           

                                                                        



                                                  
                                                                      























                                                                              
                                                                      


























                                                                       
                                                                           






                                                              
                 































                                                                             
                 


                                          



                                                              




















                                                           
                                            

                                                                              






                                                            





















                                                                               
                 


                                        



                                                              

















                                                                       






                                                            


































                                                                           
                  






































































































































































































































































































































                                                                               
                                                  





                                            
                                                   



                                             
                                           















































                                                                            
                                                               





                                                                 
                                                         








                                                                    
                                                               




                                 





























                                                                    
                                                          


















                                                                 
                                          













































                                                                               




                                                                               




















                                                                 
                                          

































                                                                           




                                                                                


































































                                                                            
                                                               











                                                   
                                                                 



                                     
                                                         













































































































                                                                               

                                                                                



                                                            


                                                                            




                                                                  

                                                                            





























































                                                                                
                                                  









































                                                                                
                   











                                           
                                                        









                                                           
                                         
                                                                



                                                   
                                         
                                                                









                                                        







































                                                                           


                             

                                           


                                 









                                            

                            




                              



                              

                     

                                          
                                                                  

                                                                             
                                                                               






                                                                                    
         
 



                                                   

                                                     
                                              
                                 


                                                                   

                                                                            


                                                                          
                                       




                                                                      


                                                                              


                                                                          
                                       



                                                              
                                                        


                                                                              


                                                                       
                                       

                         
                                    
    

                                                          
                                                    
                                 





























                                                                        
                   





                                                           



































                                                                           
                                           

















                                                                               
                                                                   

                                                                        

                                                                            












                                                                         
                                           








                                                                    

                                  


                                                                                   






















                                                                               
                                                     



























                                                                             
                                                                    

                                                                          

                                                                            










                                                     


                                                                               
                                        

















                                                                       
                                                      













                                              
                                                          

























                                               
                                                          








































































































                                                                                
                                                          


                                         
                                        














































































































































                                                                                   
                                                                  



                                                                         


                                                                           






                                                             
                                                                               
                                                                      

                                                                          







                                                             
                                                                        
                                                                    
                                                                   
                                                                    













                                                                        
                                                          

















                                                                      
                                                                     



















                                                   
                                                                     
















                                                                          
                                                                 








                                                               




                                                                           
                                                   




































                                                                    
                                                                       














                                                               
                                                               


                                                                              

                                                                                    











































                                                                          



                                                               

















































































                                                                                 
                                                          
























                                                                          



                                                                             












                                                                  

                                                                               
                                                                
                                                       








                                                                   
                                           












































                                                                              
                                                           























                                                                       



                                                                            
























































                                                                             






                                                                        
                  
                                                                

                                  
                                                                 
         





                                                                         








                                                                          
                  
                                                                  

                                  
                                                                 
         











































                                                                                   


                                                              































                                                                                   








                                                                                       































                                                                     
                                                           























































































































































                                                                            
                                                                      
                                               

                               





                              
                          
























                                                          
                                                           







                                                                  
                                                         








                                                                 
                                                           








                                                                  
                                                           









                                                                  
                                                                 







                                                                     
                                                               













                                                                    









                                                           




























                                                        



                                                                        














                                                                         
                                            















                                                     
/*
 *
 * 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>
 * 2.6 cio integration by Cornelia Huck <cornelia.huck@de.ibm.com>
 *
 * 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 02139, USA.
 */

#include <linux/module.h>
#include <linux/init.h>

#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/timer.h>
#include <linux/mempool.h>

#include <asm/ccwdev.h>
#include <asm/io.h>
#include <asm/atomic.h>
#include <asm/semaphore.h>
#include <asm/timex.h>

#include <asm/debug.h>
#include <asm/qdio.h>

#include "cio.h"
#include "css.h"
#include "device.h"
#include "airq.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 ***********************************/

static const char version[] = "QDIO base support version 2";

#ifdef QDIO_PERFORMANCE_STATS
static int proc_perf_file_registration;
static unsigned long i_p_c, i_p_nc, o_p_c, o_p_nc, ii_p_c, ii_p_nc;
static struct qdio_perf_stats perf_stats;
#endif /* QDIO_PERFORMANCE_STATS */

static int hydra_thinints;
static int is_passthrough = 0;
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;
#define QDIO_MEMPOOL_SCSSC_ELEMENTS 2
static mempool_t *qdio_mempool_scssc;

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);
static int register_thinint_result;
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 **********************/

static inline __u64 
qdio_get_micros(void)
{
        return (get_clock() >> 10); /* time>>12 is microseconds */
}

/* 
 * 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);
}

/*check ccq  */
static inline int
qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
{
	char dbf_text[15];

	if (ccq == 0 || ccq == 32 || ccq == 96)
		return 0;
	if (ccq == 97)
		return 1;
	/*notify devices immediately*/
	sprintf(dbf_text,"%d", ccq);
	QDIO_DBF_TEXT2(1,trace,dbf_text);
	return -EIO;
}
/* EQBS: extract buffer states */
static inline int
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];

	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;
again:
	ccq = do_eqbs(irq->sch_token, state, q_no, start, cnt);
	rc = qdio_check_ccq(q, ccq);
	if (rc == 1) {
		QDIO_DBF_TEXT5(1,trace,"eqAGAIN");
		goto again;
	}
	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 */
static inline int
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;
again:
	ccq = do_sqbs(irq->sch_token, state, q_no, start, cnt);
	rc = qdio_check_ccq(q, ccq);
	if (rc == 1) {
		QDIO_DBF_TEXT5(1,trace,"sqAGAIN");
		goto again;
	}
	if (rc < 0) {
                QDIO_DBF_TEXT3(1,trace,"sqberr");
                sprintf(dbf_text,"%2x,%2x,%d,%d",tmp_cnt,*cnt,ccq,q_no);
                QDIO_DBF_TEXT3(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);
}

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;
}
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*));

#ifdef QDIO_PERFORMANCE_STATS
	perf_stats.siga_syncs++;
#endif /* QDIO_PERFORMANCE_STATS */

	cc = do_siga_sync(q->schid, gpr2, gpr3);
	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);
}

static int
__do_siga_output(struct qdio_q *q, unsigned int *busy_bit)
{
       struct qdio_irq *irq;
       unsigned int fc = 0;
       unsigned long schid;

       irq = (struct qdio_irq *) q->irq_ptr;
       if (!irq->is_qebsm)
	       schid = *((u32 *)&q->schid);
       else {
	       schid = irq->sch_token;
	       fc |= 0x80;
       }
       return do_siga_output(schid, q->mask, busy_bit, fc);
}

/* 
 * returns QDIO_SIGA_ERROR_ACCESS_EXCEPTION as cc, when SIGA returns
 * an access exception 
 */
static inline int 
qdio_siga_output(struct qdio_q *q)
{
	int cc;
	__u32 busy_bit;
	__u64 start_time=0;

#ifdef QDIO_PERFORMANCE_STATS
	perf_stats.siga_outs++;
#endif /* QDIO_PERFORMANCE_STATS */

	QDIO_DBF_TEXT4(0,trace,"sigaout");
	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));

	for (;;) {
		cc = __do_siga_output(q, &busy_bit);
//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;
}

static inline int 
qdio_siga_input(struct qdio_q *q)
{
	int cc;

	QDIO_DBF_TEXT4(0,trace,"sigain");
	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));

#ifdef QDIO_PERFORMANCE_STATS
	perf_stats.siga_ins++;
#endif /* QDIO_PERFORMANCE_STATS */

	cc = do_siga_input(q->schid, q->mask);
	
	if (cc)
		QDIO_DBF_HEX3(0,trace,&cc,sizeof(int*));

	return cc;
}

/* locked by the locks in qdio_activate and qdio_cleanup */
static __u32 *
qdio_get_indicator(void)
{
	int i;

	for (i=1;i<INDICATORS_PER_CACHELINE;i++)
		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);
}

static inline void
tiqdio_clear_summary_bit(__u32 *location)
{
	QDIO_DBF_TEXT5(0,trace,"clrsummb");
	QDIO_DBF_HEX5(0,trace,&location,sizeof(void*));

	xchg(location,0);
}

static inline  void
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);
}

static inline void
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);
}

static inline int
qdio_stop_polling(struct qdio_q *q)
{
#ifdef QDIO_USE_PROCESSING_STATE
       unsigned int tmp, gsf, count = 1;
       unsigned char state = 0;
       struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr;

	if (!atomic_swap(&q->polling,0)) 
		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;

       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);

	/* 
	 * 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;*/
       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)
		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 :-) 
*/
static inline void
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 *******************************/
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;
}

static inline int
qdio_get_outbound_buffer_frontier(struct qdio_q *q)
{
	struct qdio_irq *irq;
        volatile char *slsb;
        unsigned int count = 1;
        int first_not_to_check, f, f_mod_no;
	char dbf_text[15];

	QDIO_DBF_TEXT4(0,trace,"getobfro");
	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));

	irq = (struct qdio_irq *) q->irq_ptr;
	if (irq->is_qebsm)
		return qdio_qebsm_get_outbound_buffer_frontier(q);

	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));

	if ((!q->is_iqdio_q)&&(!q->hydra_gives_outbound_pcis))
		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 */
		set_slsb(q, &f_mod_no, SLSB_P_OUTPUT_NOT_INIT, &count);

		/* 
		 * 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 */
static inline int
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);
}

static inline int
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;
	}
}

static inline void
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);

	switch (result) {
	case 0:
		/* went smooth this time, reset timestamp */
#ifdef CONFIG_QDIO_DEBUG
		QDIO_DBF_TEXT3(0,trace,"cc2reslv");
		sprintf(dbf_text,"%4x%2x%2x",q->schid.sch_no,q->q_no,
			atomic_read(&q->busy_siga_counter));
		QDIO_DBF_TEXT3(0,trace,dbf_text);
#endif /* CONFIG_QDIO_DEBUG */
		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);
			break;
		}
		QDIO_DBF_TEXT2(0,trace,"cc2REPRT");
#ifdef CONFIG_QDIO_DEBUG
		sprintf(dbf_text,"%4x%2x%2x",q->schid.sch_no,q->q_no,
			atomic_read(&q->busy_siga_counter));
		QDIO_DBF_TEXT3(0,trace,dbf_text);
#endif /* CONFIG_QDIO_DEBUG */
		/* else fallthrough and report error */
	default:
		/* for plain cc=1, 2 or 3: */
		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;
	}
}

static inline void
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;
}

static inline void
__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);
#ifdef QDIO_PERFORMANCE_STATS
		o_p_c++;
#endif /* QDIO_PERFORMANCE_STATS */
		/* 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;
	}
#ifdef QDIO_PERFORMANCE_STATS
	o_p_nc++;
	perf_stats.tl_runs++;
#endif /* QDIO_PERFORMANCE_STATS */

	/* 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);

	if (q->is_iqdio_q) {
		/* 
		 * for asynchronous queues, we better check, if the fill
		 * level is too high. for synchronous queues, the fill
		 * level will never be that high. 
		 */
		if (atomic_read(&q->number_of_buffers_used)>
		    IQDIO_FILL_LEVEL_TO_POLL)
			qdio_mark_q(q);

	} else if (!q->hydra_gives_outbound_pcis)
		if (!qdio_is_outbound_q_done(q))
			qdio_mark_q(q);

	qdio_release_q(q);
}

static void
qdio_outbound_processing(struct qdio_q *q)
{
	__qdio_outbound_processing(q);
}

/************************* INBOUND ROUTINES *******************************/


static inline int
qdio_get_inbound_buffer_frontier(struct qdio_q *q)
{
	struct qdio_irq *irq;
	int f,f_mod_no;
	volatile char *slsb;
	unsigned int count = 1;
	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*));

	irq = (struct qdio_irq *) q->irq_ptr;
	if (irq->is_qebsm)
		return qdio_qebsm_get_inbound_buffer_frontier(q);

	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) {
			set_slsb(q, &f_mod_no, SLSB_P_INPUT_NOT_INIT, &count);
		} 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)
				set_slsb(q, &last_position,
					 SLSB_P_INPUT_NOT_INIT, &count);
			atomic_set(&q->polling,1);
			last_position=f_mod_no;
		}
#else /* QDIO_USE_PROCESSING_STATE */
		set_slsb(q, &f_mod_no, SLSB_P_INPUT_NOT_INIT, &count);
#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 */
		set_slsb(q, &f_mod_no, SLSB_P_INPUT_NOT_INIT, &count);

		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)
		set_slsb(q, &last_position, SLSB_P_INPUT_NOT_INIT, &count);
#endif /* QDIO_USE_PROCESSING_STATE */

	QDIO_DBF_HEX4(0,trace,&q->first_to_check,sizeof(int));

	return q->first_to_check;
}

static inline int
qdio_has_inbound_q_moved(struct qdio_q *q)
{
	int i;

#ifdef QDIO_PERFORMANCE_STATS
	static int old_pcis=0;
	static int old_thinints=0;

	if ((old_pcis==perf_stats.pcis)&&(old_thinints==perf_stats.thinints))
		perf_stats.start_time_inbound=NOW;
	else
		old_pcis=perf_stats.pcis;
#endif /* QDIO_PERFORMANCE_STATS */

	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 */
static inline int
tiqdio_is_inbound_q_done(struct qdio_q *q)
{
	int no_used;
	unsigned int start_buf, count;
	unsigned char state = 0;
	struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr;

#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;
	if (!q->siga_sync && !irq->is_qebsm)
		/* we'll check for more primed buffers in qeth_stop_polling */
		return 0;
	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)
		/* 
		 * 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;
}

static inline int
qdio_is_inbound_q_done(struct qdio_q *q)
{
	int no_used;
	unsigned int start_buf, count;
	unsigned char state = 0;
	struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr;

#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*));
		QDIO_DBF_TEXT4(0,trace,dbf_text);
		return 1;
	}
	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) {
		/* 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;
	}
}

static inline void
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;

#ifdef QDIO_PERFORMANCE_STATS
	perf_stats.inbound_time+=NOW-perf_stats.start_time_inbound;
	perf_stats.inbound_cnt++;
#endif /* QDIO_PERFORMANCE_STATS */
}

static inline void
__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);
#ifdef QDIO_PERFORMANCE_STATS
		ii_p_c++;
#endif /* QDIO_PERFORMANCE_STATS */
		/* 
		 * as we might just be about to stop polling, we make
		 * sure that we check again at least once more 
		 */
		tiqdio_sched_tl();
		return;
	}
#ifdef QDIO_PERFORMANCE_STATS
	ii_p_nc++;
#endif /* QDIO_PERFORMANCE_STATS */
	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
	 */
	if (!spare_ind_was_set)
		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;
		} else if ((!q->siga_sync_done_on_outb_tis)&&
			 (q->hydra_gives_outbound_pcis)) {
			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];
#ifdef QDIO_PERFORMANCE_STATS
			perf_stats.tl_runs--;
#endif /* QDIO_PERFORMANCE_STATS */
			if (!qdio_is_outbound_q_done(oq))
				__qdio_outbound_processing(oq);
		}
	}

	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
tiqdio_inbound_processing(struct qdio_q *q)
{
	__tiqdio_inbound_processing(q, atomic_read(&spare_indicator_usecount));
}

static inline void
__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);
#ifdef QDIO_PERFORMANCE_STATS
		i_p_c++;
#endif /* QDIO_PERFORMANCE_STATS */
		/* 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;
	}
#ifdef QDIO_PERFORMANCE_STATS
	i_p_nc++;
	perf_stats.tl_runs++;
#endif /* QDIO_PERFORMANCE_STATS */

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
qdio_inbound_processing(struct qdio_q *q)
{
	__qdio_inbound_processing(q);
}

/************************* MAIN ROUTINES *******************************/

#ifdef QDIO_USE_PROCESSING_STATE
static inline int
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);
#ifdef QDIO_PERFORMANCE_STATS
		ii_p_c++;
#endif /* QDIO_PERFORMANCE_STATS */
		/* 
		 * 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 */

static inline void
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");

#ifdef QDIO_PERFORMANCE_STATS
	perf_stats.tl_runs++;
#endif /* QDIO_PERFORMANCE_STATS */

	tiqdio_inbound_checks();
}

/********************* GENERAL HELPER_ROUTINES ***********************/

static void
qdio_release_irq_memory(struct qdio_irq *irq_ptr)
{
	int i;

	for (i=0;i<QDIO_MAX_QUEUES_PER_IRQ;i++) {
		if (!irq_ptr->input_qs[i])
			goto next;

		kfree(irq_ptr->input_qs[i]->slib);
		kfree(irq_ptr->input_qs[i]);

next:
		if (!irq_ptr->output_qs[i])
			continue;

		kfree(irq_ptr->output_qs[i]->slib);
		kfree(irq_ptr->output_qs[i]);

	}
	kfree(irq_ptr->qdr);
	free_page((unsigned long) irq_ptr);
}

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;
	int result=-ENOMEM;

	for (i=0;i<no_input_qs;i++) {
		q = kzalloc(sizeof(struct qdio_q), GFP_KERNEL);

		if (!q) {
			QDIO_PRINT_ERR("kmalloc of q failed!\n");
			goto out;
		}

		q->slib = kmalloc(PAGE_SIZE, GFP_KERNEL);
		if (!q->slib) {
			QDIO_PRINT_ERR("kmalloc of slib failed!\n");
			goto out;
		}

		irq_ptr->input_qs[i]=q;
	}

	for (i=0;i<no_output_qs;i++) {
		q = kzalloc(sizeof(struct qdio_q), GFP_KERNEL);

		if (!q) {
			goto out;
		}

		q->slib=kmalloc(PAGE_SIZE,GFP_KERNEL);
		if (!q->slib) {
			QDIO_PRINT_ERR("kmalloc of slib failed!\n");
			goto out;
		}

		irq_ptr->output_qs[i]=q;
	}

	result=0;
out:
	return result;
}

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;

	sprintf(dbf_text,"qfqs%4x",cdev->private->sch_no);
	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;
		q->schid = irq_ptr->schid;
		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->tasklet.data=(unsigned long)q;
		/* q->is_thinint_q isn't valid at this time, but
		 * irq_ptr->is_thinint_irq is */
		q->tasklet.func=(void(*)(unsigned long))
			((irq_ptr->is_thinint_irq)?&tiqdio_inbound_processing:
			 &qdio_inbound_processing);

		/* 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 */
		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);
                }
	}

	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;
		q->int_parm=int_parm;
		q->is_input_q=0;
		q->schid = irq_ptr->schid;
		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;

		q->tasklet.data=(unsigned long)q;
		q->tasklet.func=(void(*)(unsigned long))
			&qdio_outbound_processing;

		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 */
                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);
                }
	}
}

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);*/
	}
}

static int
tiqdio_thinint_handler(void)
{
	QDIO_DBF_TEXT4(0,trace,"thin_int");

#ifdef QDIO_PERFORMANCE_STATS
	perf_stats.thinints++;
	perf_stats.start_time_inbound=NOW;
#endif /* QDIO_PERFORMANCE_STATS */

	/* 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();
	return 0;
}

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");
	sprintf(dbf_text,"%4x%4x",irq_ptr->schid.sch_no,state);
	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();
}

static inline void
qdio_irq_check_sense(struct subchannel_id schid, struct irb *irb)
{
	char dbf_text[15];

	if (irb->esw.esw0.erw.cons) {
		sprintf(dbf_text,"sens%4x",schid.sch_no);
		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");
		HEXDUMP16(WARN,"irb: ",irb);
		HEXDUMP16(WARN,"sense data: ",irb->ecw);
	}
		
}

static inline void
qdio_handle_pci(struct qdio_irq *irq_ptr)
{
	int i;
	struct qdio_q *q;

#ifdef QDIO_PERFORMANCE_STATS
	perf_stats.pcis++;
	perf_stats.start_time_inbound=NOW;
#endif /* QDIO_PERFORMANCE_STATS */
	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 {
#ifdef QDIO_PERFORMANCE_STATS
			perf_stats.tl_runs--;
#endif /* QDIO_PERFORMANCE_STATS */
			__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];
#ifdef QDIO_PERFORMANCE_STATS
		perf_stats.tl_runs--;
#endif /* QDIO_PERFORMANCE_STATS */
		if (qdio_is_outbound_q_done(q))
			continue;
		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);

static inline void
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
qdio_call_shutdown(void *data)
{
	struct ccw_device *cdev;

	cdev = (struct ccw_device *)data;
	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:
		QDIO_PRINT_ERR("establish queues on irq 0.%x.%04x: timed out\n",
			       irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
		QDIO_DBF_TEXT2(1,setup,"eq:timeo");
		qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
		break;
	case QDIO_IRQ_STATE_CLEANUP:
		QDIO_PRINT_INFO("Did not get interrupt on cleanup, "
				"irq=0.%x.%x.\n",
				irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
		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. */
		QDIO_PRINT_INFO("Queues on irq 0.%x.%04x killed by cio.\n",
				irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
		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,
				     qdio_call_shutdown, (void *)cdev);
			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;
		}
	}

	qdio_irq_check_sense(irq_ptr->schid, irb);

#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)
{
	int cc = 0;
	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
	*((int*)(&dbf_text[4])) = irq_ptr->schid.sch_no;
	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;
		if (!(irq_ptr->is_qebsm))
			cc = do_siga_sync(q->schid, 0, q->mask);
	} else if (flags&QDIO_FLAG_SYNC_OUTPUT) {
		q=irq_ptr->output_qs[queue_number];
		if (!q)
			return -EINVAL;
		if (!(irq_ptr->is_qebsm))
			cc = do_siga_sync(q->schid, q->mask, 0);
	} else 
		return -EINVAL;

	ptr=&cc;
	if (cc)
		QDIO_DBF_HEX3(0,trace,&ptr,sizeof(int));

	return cc;
}

static inline void
qdio_check_subchannel_qebsm(struct qdio_irq *irq_ptr, unsigned char qdioac,
			    unsigned long token)
{
	struct qdio_q *q;
	int i;
	unsigned int count, start_buf;
	char dbf_text[15];

	/*check if QEBSM is disabled */
	if (!(irq_ptr->is_qebsm) || !(qdioac & 0x01)) {
		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
qdio_get_ssqd_information(struct qdio_irq *irq_ptr)
{
	int result;
	unsigned char qdioac;
	struct {
		struct chsc_header request;
		u16 reserved1:10;
		u16 ssid:2;
		u16 fmt:4;
		u16 first_sch;
		u16 reserved2;
		u16 last_sch;
		u32 reserved3;
		struct chsc_header response;
		u32 reserved4;
		u8  flags;
		u8  reserved5;
		u16 sch;
		u8  qfmt;
		u8  parm;
		u8  qdioac1;
		u8  sch_class;
		u8  reserved7;
		u8  icnt;
		u8  reserved8;
		u8  ocnt;
		u8 reserved9;
		u8 mbccnt;
		u16 qdioac2;
		u64 sch_token;
	} *ssqd_area;

	QDIO_DBF_TEXT0(0,setup,"getssqd");
	qdioac = 0;
	ssqd_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC);
	if (!ssqd_area) {
	        QDIO_PRINT_WARN("Could not get memory for chsc. Using all " \
				"SIGAs for sch x%x.\n", irq_ptr->schid.sch_no);
		irq_ptr->qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY ||
				  CHSC_FLAG_SIGA_OUTPUT_NECESSARY ||
				  CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */
		irq_ptr->is_qebsm = 0;
		irq_ptr->sch_token = 0;
		irq_ptr->qib.rflags &= ~QIB_RFLAGS_ENABLE_QEBSM;
		return;
	}

	ssqd_area->request = (struct chsc_header) {
		.length = 0x0010,
		.code   = 0x0024,
	};
	ssqd_area->first_sch = irq_ptr->schid.sch_no;
	ssqd_area->last_sch = irq_ptr->schid.sch_no;
	ssqd_area->ssid = irq_ptr->schid.ssid;
	result = chsc(ssqd_area);

	if (result) {
		QDIO_PRINT_WARN("CHSC returned cc %i. Using all " \
				"SIGAs for sch 0.%x.%x.\n", result,
				irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
		qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY ||
			CHSC_FLAG_SIGA_OUTPUT_NECESSARY ||
			CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */
		irq_ptr->is_qebsm  = 0;
		goto out;
	}

	if (ssqd_area->response.code != QDIO_CHSC_RESPONSE_CODE_OK) {
		QDIO_PRINT_WARN("response upon checking SIGA needs " \
				"is 0x%x. Using all SIGAs for sch 0.%x.%x.\n",
				ssqd_area->response.code,
				irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
		qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY ||
			CHSC_FLAG_SIGA_OUTPUT_NECESSARY ||
			CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */
		irq_ptr->is_qebsm  = 0;
		goto out;
	}
	if (!(ssqd_area->flags & CHSC_FLAG_QDIO_CAPABILITY) ||
	    !(ssqd_area->flags & CHSC_FLAG_VALIDITY) ||
	    (ssqd_area->sch != irq_ptr->schid.sch_no)) {
		QDIO_PRINT_WARN("huh? problems checking out sch 0.%x.%x... " \
				"using all SIGAs.\n",
				irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
		qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY |
			CHSC_FLAG_SIGA_OUTPUT_NECESSARY |
			CHSC_FLAG_SIGA_SYNC_NECESSARY; /* worst case */
		irq_ptr->is_qebsm  = 0;
		goto out;
	}
	qdioac = ssqd_area->qdioac1;
out:
	qdio_check_subchannel_qebsm(irq_ptr, qdioac,
				    ssqd_area->sch_token);
	mempool_free(ssqd_area, qdio_mempool_scssc);
	irq_ptr->qdioac = qdioac;
}

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);

#ifdef CONFIG_64BIT
	/* 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);

	/* 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;
		struct subchannel_id schid;
		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=
			virt_to_phys((volatile void *)indicators);
		real_addr_dev_st_chg_ind=
			virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind);
	}

	scssc_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC);
	if (!scssc_area) {
		QDIO_PRINT_WARN("No memory for setting indicators on " \
				"subchannel 0.%x.%x.\n",
				irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
		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;
	scssc_area->schid = irq_ptr->schid;
	/* 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) {
		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);
		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:
	mempool_free(scssc_area, qdio_mempool_scssc);
	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;

	scsscf_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC);
	if (!scsscf_area) {
		QDIO_PRINT_WARN("No memory for setting delay target on " \
				"subchannel 0.%x.%x.\n",
				irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
		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) {
		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,
				result);
		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:
	mempool_free(scsscf_area, qdio_mempool_scssc);
	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;

	sprintf(dbf_text,"qcln%4x",irq_ptr->schid.sch_no);
	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);

	sprintf(dbf_text,"qsqs%4x",irq_ptr->schid.sch_no);
	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);
		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);

	sprintf(dbf_text,"qfqs%4x",irq_ptr->schid.sch_no);
	QDIO_DBF_TEXT1(0,trace,dbf_text);
	QDIO_DBF_TEXT0(0,setup,dbf_text);

	cdev->private->qdio_data = NULL;

	up(&irq_ptr->setting_up_sema);

	qdio_release_irq_memory(irq_ptr);
	module_put(THIS_MODULE);
	return 0;
}

static inline void
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*));
}

static inline void
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;
}

static inline void
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;
}


static inline void
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));

	}
}

static inline void
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));

	}
}

static inline int
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))) {
		sprintf(dbf_text,"ick1%4x",irq_ptr->schid.sch_no);
		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 " \
			       "queues on irq 0.%x.%x (cs=x%x, ds=x%x).\n",
			       irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
			       cstat,dstat);
		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));
		QDIO_PRINT_ERR("establish queues on irq 0.%x.%04x: didn't get "
			       "device end: dstat=%02x, cstat=%02x\n",
			       irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
			       dstat, cstat);
		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));
		QDIO_PRINT_ERR("establish queues on irq 0.%x.%04x: got "
			       "the following devstat: dstat=%02x, "
			       "cstat=%02x\n", irq_ptr->schid.ssid,
			       irq_ptr->schid.sch_no, dstat, cstat);
		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;

	sprintf(dbf_text,"qehi%4x",cdev->private->sch_no);
	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];

	sprintf(dbf_text,"qini%4x",init_data->cdev->private->sch_no);
	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];

	sprintf(dbf_text,"qalc%4x",init_data->cdev->private->sch_no);
	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 */
	irq_ptr = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);

	QDIO_DBF_TEXT0(0,setup,"irq_ptr:");
	QDIO_DBF_HEX0(0,setup,&irq_ptr,sizeof(void*));

	if (!irq_ptr) {
		QDIO_PRINT_ERR("kmalloc of irq_ptr failed!\n");
		return -ENOMEM;
	}

	init_MUTEX(&irq_ptr->setting_up_sema);

	/* QDR must be in DMA area since CCW data address is only 32 bit */
	irq_ptr->qdr=kmalloc(sizeof(struct qdr), GFP_KERNEL | GFP_DMA);
  	if (!(irq_ptr->qdr)) {
   		free_page((unsigned long) irq_ptr);
    		QDIO_PRINT_ERR("kmalloc of irq_ptr->qdr failed!\n");
		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)) {
		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;
}

int qdio_fill_irq(struct qdio_initialize *init_data)
{
	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;

	irq_ptr->schid = ccw_device_get_subchannel_id(init_data->cdev);
	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) {
		irq_ptr->dev_st_chg_ind = qdio_get_indicator();
		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 " \
					"for irq 0.%x.%x\n",
					irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
			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 */
	irq_ptr->is_qebsm = is_passthrough;
	if (irq_ptr->is_qebsm)
		irq_ptr->qib.rflags |= QIB_RFLAGS_ENABLE_QEBSM;

	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);
	}

	sprintf(dbf_text,"qest%4x",cdev->private->sch_no);
	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);

	ccw_device_set_options(cdev, 0);
	result=ccw_device_start_timeout(cdev,&irq_ptr->ccw,
					QDIO_DOING_ESTABLISH,0, 0,
					QDIO_ESTABLISH_TIMEOUT);
	if (result) {
		result2=ccw_device_start_timeout(cdev,&irq_ptr->ccw,
						 QDIO_DOING_ESTABLISH,0,0,
						 QDIO_ESTABLISH_TIMEOUT);
		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);
		}
		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);
		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;
	}
	
	/* Timeout is cared for already by using ccw_device_start_timeout(). */
	wait_event_interruptible(cdev->private->wait_q,
		 irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED ||
		 irq_ptr->state == QDIO_IRQ_STATE_ERR);

	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;
	}

	qdio_get_ssqd_information(irq_ptr);
	/* 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;
	}

	sprintf(dbf_text,"qact%4x", irq_ptr->schid.sch_no);
	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);
		}
		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);
		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;
		}
	}

	wait_event_interruptible_timeout(cdev->private->wait_q,
					 ((irq_ptr->state ==
					  QDIO_IRQ_STATE_STOPPED) ||
					  (irq_ptr->state ==
					   QDIO_IRQ_STATE_ERR)),
					 QDIO_ACTIVATE_TIMEOUT);

	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 */
static inline void
qdio_do_qdio_fill_input(struct qdio_q *q, unsigned int qidx,
			unsigned int count, struct qdio_buffer *buffers)
{
	struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr;
	qidx &= (QDIO_MAX_BUFFERS_PER_Q - 1);
	if (irq->is_qebsm) {
		while (count)
			set_slsb(q, &qidx, SLSB_CU_INPUT_EMPTY, &count);
		return;
	}
	for (;;) {
		set_slsb(q, &qidx, SLSB_CU_INPUT_EMPTY, &count);
		count--;
		if (!count) break;
		qidx = (qidx + 1) & (QDIO_MAX_BUFFERS_PER_Q - 1);
	}
}

static inline void
qdio_do_qdio_fill_output(struct qdio_q *q, unsigned int qidx,
			 unsigned int count, struct qdio_buffer *buffers)
{
	struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr;

	qidx &= (QDIO_MAX_BUFFERS_PER_Q - 1);
	if (irq->is_qebsm) {
		while (count)
			set_slsb(q, &qidx, SLSB_CU_OUTPUT_PRIMED, &count);
		return;
	}

	for (;;) {
		set_slsb(q, &qidx, SLSB_CU_OUTPUT_PRIMED, &count);
		count--;
		if (!count) break;
		qidx = (qidx + 1) & (QDIO_MAX_BUFFERS_PER_Q - 1);
	}
}

static inline void
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))
		atomic_swap(&q->polling,0);
	
	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);
}

static inline void
do_qdio_handle_outbound(struct qdio_q *q, unsigned int callflags,
			unsigned int qidx, unsigned int count,
			struct qdio_buffer *buffers)
{
	int used_elements;
	unsigned int cnt, start_buf;
	unsigned char state = 0;
	struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr;

	/* This is the outbound handling of queues */
#ifdef QDIO_PERFORMANCE_STATS
	perf_stats.start_time_outbound=NOW;
#endif /* QDIO_PERFORMANCE_STATS */

	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) {
#ifdef QDIO_PERFORMANCE_STATS
		perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound;
		perf_stats.outbound_cnt++;
#endif /* QDIO_PERFORMANCE_STATS */
		return;
	}
	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 
			 */
			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) {
				qdio_kick_outbound_q(q);
			} else {
				QDIO_DBF_TEXT3(0,trace, "fast-req");
#ifdef QDIO_PERFORMANCE_STATS
				perf_stats.fast_reqs++;
#endif /* QDIO_PERFORMANCE_STATS */
			}
		}
		/* 
		 * 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);
	}

#ifdef QDIO_PERFORMANCE_STATS
	perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound;
	perf_stats.outbound_cnt++;
#endif /* QDIO_PERFORMANCE_STATS */
}

/* 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];

	sprintf(dbf_text,"doQD%04x",cdev->private->sch_no);
 	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;
}

#ifdef QDIO_PERFORMANCE_STATS
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)
	_OUTP_IT("i_p_nc/c=%lu/%lu\n",i_p_nc,i_p_c);
	_OUTP_IT("ii_p_nc/c=%lu/%lu\n",ii_p_nc,ii_p_c);
	_OUTP_IT("o_p_nc/c=%lu/%lu\n",o_p_nc,o_p_c);
	_OUTP_IT("Number of tasklet runs (total)                  : %u\n",
		 perf_stats.tl_runs);
	_OUTP_IT("\n");
	_OUTP_IT("Number of SIGA sync's issued                    : %u\n",
		 perf_stats.siga_syncs);
	_OUTP_IT("Number of SIGA in's issued                      : %u\n",
		 perf_stats.siga_ins);
	_OUTP_IT("Number of SIGA out's issued                     : %u\n",
		 perf_stats.siga_outs);
	_OUTP_IT("Number of PCIs caught                           : %u\n",
		 perf_stats.pcis);
	_OUTP_IT("Number of adapter interrupts caught             : %u\n",
		 perf_stats.thinints);
	_OUTP_IT("Number of fast requeues (outg. SBALs w/o SIGA)  : %u\n",
		 perf_stats.fast_reqs);
	_OUTP_IT("\n");
	_OUTP_IT("Total time of all inbound actions (us) incl. UL : %u\n",
		 perf_stats.inbound_time);
	_OUTP_IT("Number of inbound transfers                     : %u\n",
		 perf_stats.inbound_cnt);
	_OUTP_IT("Total time of all outbound do_QDIOs (us)        : %u\n",
		 perf_stats.outbound_time);
	_OUTP_IT("Number of do_QDIOs outbound                     : %u\n",
		 perf_stats.outbound_cnt);
	_OUTP_IT("\n");

        return c;
}

static struct proc_dir_entry *qdio_perf_proc_file;
#endif /* QDIO_PERFORMANCE_STATS */

static void
qdio_add_procfs_entry(void)
{
#ifdef QDIO_PERFORMANCE_STATS
        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);
#endif /* QDIO_PERFORMANCE_STATS */
}

static void
qdio_remove_procfs_entry(void)
{
#ifdef QDIO_PERFORMANCE_STATS
	perf_stats.tl_runs=0;

        if (!proc_perf_file_registration) /* means if it went ok earlier */
		remove_proc_entry(QDIO_PERF,&proc_root);
#endif /* QDIO_PERFORMANCE_STATS */
}

static void
tiqdio_register_thinints(void)
{
	char dbf_text[20];
	register_thinint_result=
		s390_register_adapter_interrupt(&tiqdio_thinint_handler);
	if (register_thinint_result) {
		sprintf(dbf_text,"regthn%x",(register_thinint_result&0xff));
		QDIO_DBF_TEXT0(0,setup,dbf_text);
		QDIO_PRINT_ERR("failed to register adapter handler " \
			       "(rc=%i).\nAdapter interrupts might " \
			       "not work. Continuing.\n",
			       register_thinint_result);
	}
}

static void
tiqdio_unregister_thinints(void)
{
	if (!register_thinint_result)
		s390_unregister_adapter_interrupt(&tiqdio_thinint_handler);
}

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;
	indicators = kzalloc(sizeof(__u32)*(INDICATORS_PER_CACHELINE),
				   GFP_KERNEL);
       	if (!indicators)
		return -ENOMEM;
	return 0;
}

static void
qdio_release_qdio_memory(void)
{
	kfree(indicators);
}

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,
				      QDIO_DBF_SETUP_PAGES,
				      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,
				     QDIO_DBF_SBAL_PAGES,
				     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,
				      QDIO_DBF_SENSE_PAGES,
				      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,
				      QDIO_DBF_TRACE_PAGES,
				      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,
                                         QDIO_DBF_SLSB_OUT_PAGES,
                                         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,
                                        QDIO_DBF_SLSB_IN_PAGES,
                                        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;
}

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);
}

static int __init
init_QDIO(void)
{
	int res;
#ifdef QDIO_PERFORMANCE_STATS
	void *ptr;
#endif /* QDIO_PERFORMANCE_STATS */

	printk("qdio: loading %s\n",version);

	res=qdio_get_qdio_memory();
	if (res)
		return res;

	res = qdio_register_dbf_views();
	if (res)
		return res;

	QDIO_DBF_TEXT0(0,setup,"initQDIO");

#ifdef QDIO_PERFORMANCE_STATS
       	memset((void*)&perf_stats,0,sizeof(perf_stats));
	QDIO_DBF_TEXT0(0,setup,"perfstat");
	ptr=&perf_stats;
	QDIO_DBF_HEX0(0,setup,&ptr,sizeof(void*));
#endif /* QDIO_PERFORMANCE_STATS */

	qdio_add_procfs_entry();

	qdio_mempool_scssc = mempool_create(QDIO_MEMPOOL_SCSSC_ELEMENTS,
					    qdio_mempool_alloc,
					    qdio_mempool_free, NULL);

	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();
	mempool_destroy(qdio_mempool_scssc);

  	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);