| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * | 
|  | 3 | * linux/drivers/s390/cio/qdio.c | 
|  | 4 | * | 
|  | 5 | * Linux for S/390 QDIO base support, Hipersocket base support | 
|  | 6 | * version 2 | 
|  | 7 | * | 
|  | 8 | * Copyright 2000,2002 IBM Corporation | 
|  | 9 | * Author(s):             Utz Bacher <utz.bacher@de.ibm.com> | 
|  | 10 | * 2.6 cio integration by Cornelia Huck <cohuck@de.ibm.com> | 
|  | 11 | * | 
|  | 12 | * Restriction: only 63 iqdio subchannels would have its own indicator, | 
|  | 13 | * after that, subsequent subchannels share one indicator | 
|  | 14 | * | 
|  | 15 | * | 
|  | 16 | * | 
|  | 17 | * | 
|  | 18 | * This program is free software; you can redistribute it and/or modify | 
|  | 19 | * it under the terms of the GNU General Public License as published by | 
|  | 20 | * the Free Software Foundation; either version 2, or (at your option) | 
|  | 21 | * any later version. | 
|  | 22 | * | 
|  | 23 | * This program is distributed in the hope that it will be useful, | 
|  | 24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 26 | * GNU General Public License for more details. | 
|  | 27 | * | 
|  | 28 | * You should have received a copy of the GNU General Public License | 
|  | 29 | * along with this program; if not, write to the Free Software | 
|  | 30 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
|  | 31 | */ | 
|  | 32 |  | 
|  | 33 | #include <linux/config.h> | 
|  | 34 | #include <linux/module.h> | 
|  | 35 | #include <linux/init.h> | 
|  | 36 |  | 
|  | 37 | #include <linux/slab.h> | 
|  | 38 | #include <linux/kernel.h> | 
|  | 39 | #include <linux/proc_fs.h> | 
|  | 40 | #include <linux/timer.h> | 
|  | 41 |  | 
|  | 42 | #include <asm/ccwdev.h> | 
|  | 43 | #include <asm/io.h> | 
|  | 44 | #include <asm/atomic.h> | 
|  | 45 | #include <asm/semaphore.h> | 
|  | 46 | #include <asm/timex.h> | 
|  | 47 |  | 
|  | 48 | #include <asm/debug.h> | 
|  | 49 | #include <asm/qdio.h> | 
|  | 50 |  | 
|  | 51 | #include "cio.h" | 
|  | 52 | #include "css.h" | 
|  | 53 | #include "device.h" | 
|  | 54 | #include "airq.h" | 
|  | 55 | #include "qdio.h" | 
|  | 56 | #include "ioasm.h" | 
|  | 57 | #include "chsc.h" | 
|  | 58 |  | 
| Ursula Braun-Krahl | cdb32dc | 2005-11-07 00:59:10 -0800 | [diff] [blame] | 59 | #define VERSION_QDIO_C "$Revision: 1.108 $" | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 60 |  | 
|  | 61 | /****************** MODULE PARAMETER VARIABLES ********************/ | 
|  | 62 | MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>"); | 
|  | 63 | MODULE_DESCRIPTION("QDIO base support version 2, " \ | 
|  | 64 | "Copyright 2000 IBM Corporation"); | 
|  | 65 | MODULE_LICENSE("GPL"); | 
|  | 66 |  | 
|  | 67 | /******************** HERE WE GO ***********************************/ | 
|  | 68 |  | 
|  | 69 | static const char version[] = "QDIO base support version 2 (" | 
|  | 70 | VERSION_QDIO_C "/" VERSION_QDIO_H  "/" VERSION_CIO_QDIO_H ")"; | 
|  | 71 |  | 
|  | 72 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 73 | static int proc_perf_file_registration; | 
|  | 74 | static unsigned long i_p_c, i_p_nc, o_p_c, o_p_nc, ii_p_c, ii_p_nc; | 
|  | 75 | static struct qdio_perf_stats perf_stats; | 
|  | 76 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 77 |  | 
|  | 78 | static int hydra_thinints; | 
|  | 79 | static int omit_svs; | 
|  | 80 |  | 
|  | 81 | static int indicator_used[INDICATORS_PER_CACHELINE]; | 
|  | 82 | static __u32 * volatile indicators; | 
|  | 83 | static __u32 volatile spare_indicator; | 
|  | 84 | static atomic_t spare_indicator_usecount; | 
|  | 85 |  | 
|  | 86 | static debug_info_t *qdio_dbf_setup; | 
|  | 87 | static debug_info_t *qdio_dbf_sbal; | 
|  | 88 | static debug_info_t *qdio_dbf_trace; | 
|  | 89 | static debug_info_t *qdio_dbf_sense; | 
|  | 90 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 91 | static debug_info_t *qdio_dbf_slsb_out; | 
|  | 92 | static debug_info_t *qdio_dbf_slsb_in; | 
|  | 93 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 94 |  | 
|  | 95 | /* iQDIO stuff: */ | 
|  | 96 | static volatile struct qdio_q *tiq_list=NULL; /* volatile as it could change | 
|  | 97 | during a while loop */ | 
|  | 98 | static DEFINE_SPINLOCK(ttiq_list_lock); | 
|  | 99 | static int register_thinint_result; | 
|  | 100 | static void tiqdio_tl(unsigned long); | 
|  | 101 | static DECLARE_TASKLET(tiqdio_tasklet,tiqdio_tl,0); | 
|  | 102 |  | 
|  | 103 | /* not a macro, as one of the arguments is atomic_read */ | 
|  | 104 | static inline int | 
|  | 105 | qdio_min(int a,int b) | 
|  | 106 | { | 
|  | 107 | if (a<b) | 
|  | 108 | return a; | 
|  | 109 | else | 
|  | 110 | return b; | 
|  | 111 | } | 
|  | 112 |  | 
|  | 113 | /***************** SCRUBBER HELPER ROUTINES **********************/ | 
|  | 114 |  | 
| Al Viro | a46206e | 2005-08-25 23:03:35 +0100 | [diff] [blame] | 115 | static inline __u64 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 116 | qdio_get_micros(void) | 
|  | 117 | { | 
|  | 118 | return (get_clock() >> 10); /* time>>12 is microseconds */ | 
|  | 119 | } | 
|  | 120 |  | 
|  | 121 | /* | 
|  | 122 | * unfortunately, we can't just xchg the values; in do_QDIO we want to reserve | 
|  | 123 | * the q in any case, so that we'll not be interrupted when we are in | 
|  | 124 | * qdio_mark_tiq... shouldn't have a really bad impact, as reserving almost | 
|  | 125 | * ever works (last famous words) | 
|  | 126 | */ | 
|  | 127 | static inline int | 
|  | 128 | qdio_reserve_q(struct qdio_q *q) | 
|  | 129 | { | 
|  | 130 | return atomic_add_return(1,&q->use_count) - 1; | 
|  | 131 | } | 
|  | 132 |  | 
|  | 133 | static inline void | 
|  | 134 | qdio_release_q(struct qdio_q *q) | 
|  | 135 | { | 
|  | 136 | atomic_dec(&q->use_count); | 
|  | 137 | } | 
|  | 138 |  | 
|  | 139 | static volatile inline void | 
|  | 140 | qdio_set_slsb(volatile char *slsb, unsigned char value) | 
|  | 141 | { | 
|  | 142 | xchg((char*)slsb,value); | 
|  | 143 | } | 
|  | 144 |  | 
|  | 145 | static inline int | 
|  | 146 | qdio_siga_sync(struct qdio_q *q, unsigned int gpr2, | 
|  | 147 | unsigned int gpr3) | 
|  | 148 | { | 
|  | 149 | int cc; | 
|  | 150 |  | 
|  | 151 | QDIO_DBF_TEXT4(0,trace,"sigasync"); | 
|  | 152 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 153 |  | 
|  | 154 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 155 | perf_stats.siga_syncs++; | 
|  | 156 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 157 |  | 
|  | 158 | cc = do_siga_sync(q->irq, gpr2, gpr3); | 
|  | 159 | if (cc) | 
|  | 160 | QDIO_DBF_HEX3(0,trace,&cc,sizeof(int*)); | 
|  | 161 |  | 
|  | 162 | return cc; | 
|  | 163 | } | 
|  | 164 |  | 
|  | 165 | static inline int | 
|  | 166 | qdio_siga_sync_q(struct qdio_q *q) | 
|  | 167 | { | 
|  | 168 | if (q->is_input_q) | 
|  | 169 | return qdio_siga_sync(q, 0, q->mask); | 
|  | 170 | return qdio_siga_sync(q, q->mask, 0); | 
|  | 171 | } | 
|  | 172 |  | 
|  | 173 | /* | 
|  | 174 | * returns QDIO_SIGA_ERROR_ACCESS_EXCEPTION as cc, when SIGA returns | 
|  | 175 | * an access exception | 
|  | 176 | */ | 
|  | 177 | static inline int | 
|  | 178 | qdio_siga_output(struct qdio_q *q) | 
|  | 179 | { | 
|  | 180 | int cc; | 
|  | 181 | __u32 busy_bit; | 
|  | 182 | __u64 start_time=0; | 
|  | 183 |  | 
|  | 184 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 185 | perf_stats.siga_outs++; | 
|  | 186 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 187 |  | 
|  | 188 | QDIO_DBF_TEXT4(0,trace,"sigaout"); | 
|  | 189 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 190 |  | 
|  | 191 | for (;;) { | 
|  | 192 | cc = do_siga_output(q->irq, q->mask, &busy_bit); | 
|  | 193 | //QDIO_PRINT_ERR("cc=%x, busy=%x\n",cc,busy_bit); | 
|  | 194 | if ((cc==2) && (busy_bit) && (q->is_iqdio_q)) { | 
|  | 195 | if (!start_time) | 
|  | 196 | start_time=NOW; | 
|  | 197 | if ((NOW-start_time)>QDIO_BUSY_BIT_PATIENCE) | 
|  | 198 | break; | 
|  | 199 | } else | 
|  | 200 | break; | 
|  | 201 | } | 
|  | 202 |  | 
|  | 203 | if ((cc==2) && (busy_bit)) | 
|  | 204 | cc |= QDIO_SIGA_ERROR_B_BIT_SET; | 
|  | 205 |  | 
|  | 206 | if (cc) | 
|  | 207 | QDIO_DBF_HEX3(0,trace,&cc,sizeof(int*)); | 
|  | 208 |  | 
|  | 209 | return cc; | 
|  | 210 | } | 
|  | 211 |  | 
|  | 212 | static inline int | 
|  | 213 | qdio_siga_input(struct qdio_q *q) | 
|  | 214 | { | 
|  | 215 | int cc; | 
|  | 216 |  | 
|  | 217 | QDIO_DBF_TEXT4(0,trace,"sigain"); | 
|  | 218 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 219 |  | 
|  | 220 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 221 | perf_stats.siga_ins++; | 
|  | 222 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 223 |  | 
|  | 224 | cc = do_siga_input(q->irq, q->mask); | 
|  | 225 |  | 
|  | 226 | if (cc) | 
|  | 227 | QDIO_DBF_HEX3(0,trace,&cc,sizeof(int*)); | 
|  | 228 |  | 
|  | 229 | return cc; | 
|  | 230 | } | 
|  | 231 |  | 
|  | 232 | /* locked by the locks in qdio_activate and qdio_cleanup */ | 
| Al Viro | ade31f3 | 2005-08-23 22:47:47 +0100 | [diff] [blame] | 233 | static __u32 volatile * | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 234 | qdio_get_indicator(void) | 
|  | 235 | { | 
|  | 236 | int i; | 
|  | 237 |  | 
|  | 238 | for (i=1;i<INDICATORS_PER_CACHELINE;i++) | 
|  | 239 | if (!indicator_used[i]) { | 
|  | 240 | indicator_used[i]=1; | 
|  | 241 | return indicators+i; | 
|  | 242 | } | 
|  | 243 | atomic_inc(&spare_indicator_usecount); | 
|  | 244 | return (__u32 * volatile) &spare_indicator; | 
|  | 245 | } | 
|  | 246 |  | 
|  | 247 | /* locked by the locks in qdio_activate and qdio_cleanup */ | 
|  | 248 | static void | 
|  | 249 | qdio_put_indicator(__u32 *addr) | 
|  | 250 | { | 
|  | 251 | int i; | 
|  | 252 |  | 
|  | 253 | if ( (addr) && (addr!=&spare_indicator) ) { | 
|  | 254 | i=addr-indicators; | 
|  | 255 | indicator_used[i]=0; | 
|  | 256 | } | 
|  | 257 | if (addr == &spare_indicator) | 
|  | 258 | atomic_dec(&spare_indicator_usecount); | 
|  | 259 | } | 
|  | 260 |  | 
|  | 261 | static inline volatile void | 
|  | 262 | tiqdio_clear_summary_bit(__u32 *location) | 
|  | 263 | { | 
|  | 264 | QDIO_DBF_TEXT5(0,trace,"clrsummb"); | 
|  | 265 | QDIO_DBF_HEX5(0,trace,&location,sizeof(void*)); | 
|  | 266 |  | 
|  | 267 | xchg(location,0); | 
|  | 268 | } | 
|  | 269 |  | 
|  | 270 | static inline volatile void | 
|  | 271 | tiqdio_set_summary_bit(__u32 *location) | 
|  | 272 | { | 
|  | 273 | QDIO_DBF_TEXT5(0,trace,"setsummb"); | 
|  | 274 | QDIO_DBF_HEX5(0,trace,&location,sizeof(void*)); | 
|  | 275 |  | 
|  | 276 | xchg(location,-1); | 
|  | 277 | } | 
|  | 278 |  | 
|  | 279 | static inline void | 
|  | 280 | tiqdio_sched_tl(void) | 
|  | 281 | { | 
|  | 282 | tasklet_hi_schedule(&tiqdio_tasklet); | 
|  | 283 | } | 
|  | 284 |  | 
|  | 285 | static inline void | 
|  | 286 | qdio_mark_tiq(struct qdio_q *q) | 
|  | 287 | { | 
|  | 288 | unsigned long flags; | 
|  | 289 |  | 
|  | 290 | QDIO_DBF_TEXT4(0,trace,"mark iq"); | 
|  | 291 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 292 |  | 
|  | 293 | spin_lock_irqsave(&ttiq_list_lock,flags); | 
|  | 294 | if (unlikely(atomic_read(&q->is_in_shutdown))) | 
|  | 295 | goto out_unlock; | 
|  | 296 |  | 
|  | 297 | if (!q->is_input_q) | 
|  | 298 | goto out_unlock; | 
|  | 299 |  | 
|  | 300 | if ((q->list_prev) || (q->list_next)) | 
|  | 301 | goto out_unlock; | 
|  | 302 |  | 
|  | 303 | if (!tiq_list) { | 
|  | 304 | tiq_list=q; | 
|  | 305 | q->list_prev=q; | 
|  | 306 | q->list_next=q; | 
|  | 307 | } else { | 
|  | 308 | q->list_next=tiq_list; | 
|  | 309 | q->list_prev=tiq_list->list_prev; | 
|  | 310 | tiq_list->list_prev->list_next=q; | 
|  | 311 | tiq_list->list_prev=q; | 
|  | 312 | } | 
|  | 313 | spin_unlock_irqrestore(&ttiq_list_lock,flags); | 
|  | 314 |  | 
|  | 315 | tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind); | 
|  | 316 | tiqdio_sched_tl(); | 
|  | 317 | return; | 
|  | 318 | out_unlock: | 
|  | 319 | spin_unlock_irqrestore(&ttiq_list_lock,flags); | 
|  | 320 | return; | 
|  | 321 | } | 
|  | 322 |  | 
|  | 323 | static inline void | 
|  | 324 | qdio_mark_q(struct qdio_q *q) | 
|  | 325 | { | 
|  | 326 | QDIO_DBF_TEXT4(0,trace,"mark q"); | 
|  | 327 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 328 |  | 
|  | 329 | if (unlikely(atomic_read(&q->is_in_shutdown))) | 
|  | 330 | return; | 
|  | 331 |  | 
|  | 332 | tasklet_schedule(&q->tasklet); | 
|  | 333 | } | 
|  | 334 |  | 
|  | 335 | static inline int | 
|  | 336 | qdio_stop_polling(struct qdio_q *q) | 
|  | 337 | { | 
|  | 338 | #ifdef QDIO_USE_PROCESSING_STATE | 
|  | 339 | int gsf; | 
|  | 340 |  | 
|  | 341 | if (!atomic_swap(&q->polling,0)) | 
|  | 342 | return 1; | 
|  | 343 |  | 
|  | 344 | QDIO_DBF_TEXT4(0,trace,"stoppoll"); | 
|  | 345 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 346 |  | 
|  | 347 | /* show the card that we are not polling anymore */ | 
|  | 348 | if (!q->is_input_q) | 
|  | 349 | return 1; | 
|  | 350 |  | 
|  | 351 | gsf=GET_SAVED_FRONTIER(q); | 
|  | 352 | set_slsb(&q->slsb.acc.val[(gsf+QDIO_MAX_BUFFERS_PER_Q-1)& | 
|  | 353 | (QDIO_MAX_BUFFERS_PER_Q-1)], | 
|  | 354 | SLSB_P_INPUT_NOT_INIT); | 
|  | 355 | /* | 
|  | 356 | * we don't issue this SYNC_MEMORY, as we trust Rick T and | 
|  | 357 | * moreover will not use the PROCESSING state under VM, so | 
|  | 358 | * q->polling was 0 anyway | 
|  | 359 | */ | 
|  | 360 | /*SYNC_MEMORY;*/ | 
|  | 361 | if (q->slsb.acc.val[gsf]!=SLSB_P_INPUT_PRIMED) | 
|  | 362 | return 1; | 
|  | 363 | /* | 
|  | 364 | * set our summary bit again, as otherwise there is a | 
|  | 365 | * small window we can miss between resetting it and | 
|  | 366 | * checking for PRIMED state | 
|  | 367 | */ | 
|  | 368 | if (q->is_thinint_q) | 
|  | 369 | tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind); | 
|  | 370 | return 0; | 
|  | 371 |  | 
|  | 372 | #else /* QDIO_USE_PROCESSING_STATE */ | 
|  | 373 | return 1; | 
|  | 374 | #endif /* QDIO_USE_PROCESSING_STATE */ | 
|  | 375 | } | 
|  | 376 |  | 
|  | 377 | /* | 
|  | 378 | * see the comment in do_QDIO and before qdio_reserve_q about the | 
|  | 379 | * sophisticated locking outside of unmark_q, so that we don't need to | 
|  | 380 | * disable the interrupts :-) | 
|  | 381 | */ | 
|  | 382 | static inline void | 
|  | 383 | qdio_unmark_q(struct qdio_q *q) | 
|  | 384 | { | 
|  | 385 | unsigned long flags; | 
|  | 386 |  | 
|  | 387 | QDIO_DBF_TEXT4(0,trace,"unmark q"); | 
|  | 388 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 389 |  | 
|  | 390 | if ((!q->list_prev)||(!q->list_next)) | 
|  | 391 | return; | 
|  | 392 |  | 
|  | 393 | if ((q->is_thinint_q)&&(q->is_input_q)) { | 
|  | 394 | /* iQDIO */ | 
|  | 395 | spin_lock_irqsave(&ttiq_list_lock,flags); | 
|  | 396 | /* in case cleanup has done this already and simultanously | 
|  | 397 | * qdio_unmark_q is called from the interrupt handler, we've | 
|  | 398 | * got to check this in this specific case again */ | 
|  | 399 | if ((!q->list_prev)||(!q->list_next)) | 
|  | 400 | goto out; | 
|  | 401 | if (q->list_next==q) { | 
|  | 402 | /* q was the only interesting q */ | 
|  | 403 | tiq_list=NULL; | 
|  | 404 | q->list_next=NULL; | 
|  | 405 | q->list_prev=NULL; | 
|  | 406 | } else { | 
|  | 407 | q->list_next->list_prev=q->list_prev; | 
|  | 408 | q->list_prev->list_next=q->list_next; | 
|  | 409 | tiq_list=q->list_next; | 
|  | 410 | q->list_next=NULL; | 
|  | 411 | q->list_prev=NULL; | 
|  | 412 | } | 
|  | 413 | out: | 
|  | 414 | spin_unlock_irqrestore(&ttiq_list_lock,flags); | 
|  | 415 | } | 
|  | 416 | } | 
|  | 417 |  | 
|  | 418 | static inline unsigned long | 
|  | 419 | tiqdio_clear_global_summary(void) | 
|  | 420 | { | 
|  | 421 | unsigned long time; | 
|  | 422 |  | 
|  | 423 | QDIO_DBF_TEXT5(0,trace,"clrglobl"); | 
|  | 424 |  | 
|  | 425 | time = do_clear_global_summary(); | 
|  | 426 |  | 
|  | 427 | QDIO_DBF_HEX5(0,trace,&time,sizeof(unsigned long)); | 
|  | 428 |  | 
|  | 429 | return time; | 
|  | 430 | } | 
|  | 431 |  | 
|  | 432 |  | 
|  | 433 | /************************* OUTBOUND ROUTINES *******************************/ | 
|  | 434 |  | 
| Jesper Juhl | 77933d7 | 2005-07-27 11:46:09 -0700 | [diff] [blame] | 435 | static inline int | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 436 | qdio_get_outbound_buffer_frontier(struct qdio_q *q) | 
|  | 437 | { | 
|  | 438 | int f,f_mod_no; | 
|  | 439 | volatile char *slsb; | 
|  | 440 | int first_not_to_check; | 
|  | 441 | char dbf_text[15]; | 
|  | 442 |  | 
|  | 443 | QDIO_DBF_TEXT4(0,trace,"getobfro"); | 
|  | 444 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 445 |  | 
|  | 446 | slsb=&q->slsb.acc.val[0]; | 
|  | 447 | f_mod_no=f=q->first_to_check; | 
|  | 448 | /* | 
|  | 449 | * f points to already processed elements, so f+no_used is correct... | 
|  | 450 | * ... but: we don't check 128 buffers, as otherwise | 
|  | 451 | * qdio_has_outbound_q_moved would return 0 | 
|  | 452 | */ | 
|  | 453 | first_not_to_check=f+qdio_min(atomic_read(&q->number_of_buffers_used), | 
|  | 454 | (QDIO_MAX_BUFFERS_PER_Q-1)); | 
|  | 455 |  | 
|  | 456 | if ((!q->is_iqdio_q)&&(!q->hydra_gives_outbound_pcis)) | 
|  | 457 | SYNC_MEMORY; | 
|  | 458 |  | 
|  | 459 | check_next: | 
|  | 460 | if (f==first_not_to_check) | 
|  | 461 | goto out; | 
|  | 462 |  | 
|  | 463 | switch(slsb[f_mod_no]) { | 
|  | 464 |  | 
|  | 465 | /* the adapter has not fetched the output yet */ | 
|  | 466 | case SLSB_CU_OUTPUT_PRIMED: | 
|  | 467 | QDIO_DBF_TEXT5(0,trace,"outpprim"); | 
|  | 468 | break; | 
|  | 469 |  | 
|  | 470 | /* the adapter got it */ | 
|  | 471 | case SLSB_P_OUTPUT_EMPTY: | 
|  | 472 | atomic_dec(&q->number_of_buffers_used); | 
|  | 473 | f++; | 
|  | 474 | f_mod_no=f&(QDIO_MAX_BUFFERS_PER_Q-1); | 
|  | 475 | QDIO_DBF_TEXT5(0,trace,"outpempt"); | 
|  | 476 | goto check_next; | 
|  | 477 |  | 
|  | 478 | case SLSB_P_OUTPUT_ERROR: | 
|  | 479 | QDIO_DBF_TEXT3(0,trace,"outperr"); | 
|  | 480 | sprintf(dbf_text,"%x-%x-%x",f_mod_no, | 
|  | 481 | q->sbal[f_mod_no]->element[14].sbalf.value, | 
|  | 482 | q->sbal[f_mod_no]->element[15].sbalf.value); | 
|  | 483 | QDIO_DBF_TEXT3(1,trace,dbf_text); | 
|  | 484 | QDIO_DBF_HEX2(1,sbal,q->sbal[f_mod_no],256); | 
|  | 485 |  | 
|  | 486 | /* kind of process the buffer */ | 
|  | 487 | set_slsb(&q->slsb.acc.val[f_mod_no], SLSB_P_OUTPUT_NOT_INIT); | 
|  | 488 |  | 
|  | 489 | /* | 
|  | 490 | * we increment the frontier, as this buffer | 
|  | 491 | * was processed obviously | 
|  | 492 | */ | 
|  | 493 | atomic_dec(&q->number_of_buffers_used); | 
|  | 494 | f_mod_no=(f_mod_no+1)&(QDIO_MAX_BUFFERS_PER_Q-1); | 
|  | 495 |  | 
|  | 496 | if (q->qdio_error) | 
|  | 497 | q->error_status_flags|= | 
|  | 498 | QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR; | 
|  | 499 | q->qdio_error=SLSB_P_OUTPUT_ERROR; | 
|  | 500 | q->error_status_flags|=QDIO_STATUS_LOOK_FOR_ERROR; | 
|  | 501 |  | 
|  | 502 | break; | 
|  | 503 |  | 
|  | 504 | /* no new buffers */ | 
|  | 505 | default: | 
|  | 506 | QDIO_DBF_TEXT5(0,trace,"outpni"); | 
|  | 507 | } | 
|  | 508 | out: | 
|  | 509 | return (q->first_to_check=f_mod_no); | 
|  | 510 | } | 
|  | 511 |  | 
|  | 512 | /* all buffers are processed */ | 
| Jesper Juhl | 77933d7 | 2005-07-27 11:46:09 -0700 | [diff] [blame] | 513 | static inline int | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 514 | qdio_is_outbound_q_done(struct qdio_q *q) | 
|  | 515 | { | 
|  | 516 | int no_used; | 
|  | 517 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 518 | char dbf_text[15]; | 
|  | 519 | #endif | 
|  | 520 |  | 
|  | 521 | no_used=atomic_read(&q->number_of_buffers_used); | 
|  | 522 |  | 
|  | 523 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 524 | if (no_used) { | 
|  | 525 | sprintf(dbf_text,"oqisnt%02x",no_used); | 
|  | 526 | QDIO_DBF_TEXT4(0,trace,dbf_text); | 
|  | 527 | } else { | 
|  | 528 | QDIO_DBF_TEXT4(0,trace,"oqisdone"); | 
|  | 529 | } | 
|  | 530 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 531 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 532 | return (no_used==0); | 
|  | 533 | } | 
|  | 534 |  | 
| Jesper Juhl | 77933d7 | 2005-07-27 11:46:09 -0700 | [diff] [blame] | 535 | static inline int | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 536 | qdio_has_outbound_q_moved(struct qdio_q *q) | 
|  | 537 | { | 
|  | 538 | int i; | 
|  | 539 |  | 
|  | 540 | i=qdio_get_outbound_buffer_frontier(q); | 
|  | 541 |  | 
|  | 542 | if ( (i!=GET_SAVED_FRONTIER(q)) || | 
|  | 543 | (q->error_status_flags&QDIO_STATUS_LOOK_FOR_ERROR) ) { | 
|  | 544 | SAVE_FRONTIER(q,i); | 
|  | 545 | QDIO_DBF_TEXT4(0,trace,"oqhasmvd"); | 
|  | 546 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 547 | return 1; | 
|  | 548 | } else { | 
|  | 549 | QDIO_DBF_TEXT4(0,trace,"oqhsntmv"); | 
|  | 550 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 551 | return 0; | 
|  | 552 | } | 
|  | 553 | } | 
|  | 554 |  | 
| Jesper Juhl | 77933d7 | 2005-07-27 11:46:09 -0700 | [diff] [blame] | 555 | static inline void | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 556 | qdio_kick_outbound_q(struct qdio_q *q) | 
|  | 557 | { | 
|  | 558 | int result; | 
|  | 559 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 560 | char dbf_text[15]; | 
|  | 561 |  | 
|  | 562 | QDIO_DBF_TEXT4(0,trace,"kickoutq"); | 
|  | 563 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 564 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 565 |  | 
|  | 566 | if (!q->siga_out) | 
|  | 567 | return; | 
|  | 568 |  | 
|  | 569 | /* here's the story with cc=2 and busy bit set (thanks, Rick): | 
|  | 570 | * VM's CP could present us cc=2 and busy bit set on SIGA-write | 
|  | 571 | * during reconfiguration of their Guest LAN (only in HIPERS mode, | 
|  | 572 | * QDIO mode is asynchronous -- cc=2 and busy bit there will take | 
|  | 573 | * the queues down immediately; and not being under VM we have a | 
|  | 574 | * problem on cc=2 and busy bit set right away). | 
|  | 575 | * | 
|  | 576 | * Therefore qdio_siga_output will try for a short time constantly, | 
|  | 577 | * if such a condition occurs. If it doesn't change, it will | 
|  | 578 | * increase the busy_siga_counter and save the timestamp, and | 
|  | 579 | * schedule the queue for later processing (via mark_q, using the | 
|  | 580 | * queue tasklet). __qdio_outbound_processing will check out the | 
|  | 581 | * counter. If non-zero, it will call qdio_kick_outbound_q as often | 
|  | 582 | * as the value of the counter. This will attempt further SIGA | 
|  | 583 | * instructions. For each successful SIGA, the counter is | 
|  | 584 | * decreased, for failing SIGAs the counter remains the same, after | 
|  | 585 | * all. | 
|  | 586 | * After some time of no movement, qdio_kick_outbound_q will | 
|  | 587 | * finally fail and reflect corresponding error codes to call | 
|  | 588 | * the upper layer module and have it take the queues down. | 
|  | 589 | * | 
|  | 590 | * Note that this is a change from the original HiperSockets design | 
|  | 591 | * (saying cc=2 and busy bit means take the queues down), but in | 
|  | 592 | * these days Guest LAN didn't exist... excessive cc=2 with busy bit | 
|  | 593 | * conditions will still take the queues down, but the threshold is | 
|  | 594 | * higher due to the Guest LAN environment. | 
|  | 595 | */ | 
|  | 596 |  | 
|  | 597 |  | 
|  | 598 | result=qdio_siga_output(q); | 
|  | 599 |  | 
|  | 600 | switch (result) { | 
|  | 601 | case 0: | 
|  | 602 | /* went smooth this time, reset timestamp */ | 
|  | 603 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 604 | QDIO_DBF_TEXT3(0,trace,"cc2reslv"); | 
|  | 605 | sprintf(dbf_text,"%4x%2x%2x",q->irq,q->q_no, | 
|  | 606 | atomic_read(&q->busy_siga_counter)); | 
|  | 607 | QDIO_DBF_TEXT3(0,trace,dbf_text); | 
|  | 608 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 609 | q->timing.busy_start=0; | 
|  | 610 | break; | 
|  | 611 | case (2|QDIO_SIGA_ERROR_B_BIT_SET): | 
|  | 612 | /* cc=2 and busy bit: */ | 
|  | 613 | atomic_inc(&q->busy_siga_counter); | 
|  | 614 |  | 
|  | 615 | /* if the last siga was successful, save | 
|  | 616 | * timestamp here */ | 
|  | 617 | if (!q->timing.busy_start) | 
|  | 618 | q->timing.busy_start=NOW; | 
|  | 619 |  | 
|  | 620 | /* if we're in time, don't touch error_status_flags | 
|  | 621 | * and siga_error */ | 
|  | 622 | if (NOW-q->timing.busy_start<QDIO_BUSY_BIT_GIVE_UP) { | 
|  | 623 | qdio_mark_q(q); | 
|  | 624 | break; | 
|  | 625 | } | 
|  | 626 | QDIO_DBF_TEXT2(0,trace,"cc2REPRT"); | 
|  | 627 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 628 | sprintf(dbf_text,"%4x%2x%2x",q->irq,q->q_no, | 
|  | 629 | atomic_read(&q->busy_siga_counter)); | 
|  | 630 | QDIO_DBF_TEXT3(0,trace,dbf_text); | 
|  | 631 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 632 | /* else fallthrough and report error */ | 
|  | 633 | default: | 
|  | 634 | /* for plain cc=1, 2 or 3: */ | 
|  | 635 | if (q->siga_error) | 
|  | 636 | q->error_status_flags|= | 
|  | 637 | QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR; | 
|  | 638 | q->error_status_flags|= | 
|  | 639 | QDIO_STATUS_LOOK_FOR_ERROR; | 
|  | 640 | q->siga_error=result; | 
|  | 641 | } | 
|  | 642 | } | 
|  | 643 |  | 
| Jesper Juhl | 77933d7 | 2005-07-27 11:46:09 -0700 | [diff] [blame] | 644 | static inline void | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 645 | qdio_kick_outbound_handler(struct qdio_q *q) | 
|  | 646 | { | 
|  | 647 | int start, end, real_end, count; | 
|  | 648 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 649 | char dbf_text[15]; | 
|  | 650 | #endif | 
|  | 651 |  | 
|  | 652 | start = q->first_element_to_kick; | 
|  | 653 | /* last_move_ftc was just updated */ | 
|  | 654 | real_end = GET_SAVED_FRONTIER(q); | 
|  | 655 | end = (real_end+QDIO_MAX_BUFFERS_PER_Q-1)& | 
|  | 656 | (QDIO_MAX_BUFFERS_PER_Q-1); | 
|  | 657 | count = (end+QDIO_MAX_BUFFERS_PER_Q+1-start)& | 
|  | 658 | (QDIO_MAX_BUFFERS_PER_Q-1); | 
|  | 659 |  | 
|  | 660 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 661 | QDIO_DBF_TEXT4(0,trace,"kickouth"); | 
|  | 662 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 663 |  | 
|  | 664 | sprintf(dbf_text,"s=%2xc=%2x",start,count); | 
|  | 665 | QDIO_DBF_TEXT4(0,trace,dbf_text); | 
|  | 666 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 667 |  | 
|  | 668 | if (q->state==QDIO_IRQ_STATE_ACTIVE) | 
|  | 669 | q->handler(q->cdev,QDIO_STATUS_OUTBOUND_INT| | 
|  | 670 | q->error_status_flags, | 
|  | 671 | q->qdio_error,q->siga_error,q->q_no,start,count, | 
|  | 672 | q->int_parm); | 
|  | 673 |  | 
|  | 674 | /* for the next time: */ | 
|  | 675 | q->first_element_to_kick=real_end; | 
|  | 676 | q->qdio_error=0; | 
|  | 677 | q->siga_error=0; | 
|  | 678 | q->error_status_flags=0; | 
|  | 679 | } | 
|  | 680 |  | 
|  | 681 | static inline void | 
|  | 682 | __qdio_outbound_processing(struct qdio_q *q) | 
|  | 683 | { | 
|  | 684 | int siga_attempts; | 
|  | 685 |  | 
|  | 686 | QDIO_DBF_TEXT4(0,trace,"qoutproc"); | 
|  | 687 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 688 |  | 
|  | 689 | if (unlikely(qdio_reserve_q(q))) { | 
|  | 690 | qdio_release_q(q); | 
|  | 691 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 692 | o_p_c++; | 
|  | 693 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 694 | /* as we're sissies, we'll check next time */ | 
|  | 695 | if (likely(!atomic_read(&q->is_in_shutdown))) { | 
|  | 696 | qdio_mark_q(q); | 
|  | 697 | QDIO_DBF_TEXT4(0,trace,"busy,agn"); | 
|  | 698 | } | 
|  | 699 | return; | 
|  | 700 | } | 
|  | 701 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 702 | o_p_nc++; | 
|  | 703 | perf_stats.tl_runs++; | 
|  | 704 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 705 |  | 
|  | 706 | /* see comment in qdio_kick_outbound_q */ | 
|  | 707 | siga_attempts=atomic_read(&q->busy_siga_counter); | 
|  | 708 | while (siga_attempts) { | 
|  | 709 | atomic_dec(&q->busy_siga_counter); | 
|  | 710 | qdio_kick_outbound_q(q); | 
|  | 711 | siga_attempts--; | 
|  | 712 | } | 
|  | 713 |  | 
|  | 714 | if (qdio_has_outbound_q_moved(q)) | 
|  | 715 | qdio_kick_outbound_handler(q); | 
|  | 716 |  | 
|  | 717 | if (q->is_iqdio_q) { | 
|  | 718 | /* | 
|  | 719 | * for asynchronous queues, we better check, if the fill | 
|  | 720 | * level is too high. for synchronous queues, the fill | 
|  | 721 | * level will never be that high. | 
|  | 722 | */ | 
|  | 723 | if (atomic_read(&q->number_of_buffers_used)> | 
|  | 724 | IQDIO_FILL_LEVEL_TO_POLL) | 
|  | 725 | qdio_mark_q(q); | 
|  | 726 |  | 
|  | 727 | } else if (!q->hydra_gives_outbound_pcis) | 
|  | 728 | if (!qdio_is_outbound_q_done(q)) | 
|  | 729 | qdio_mark_q(q); | 
|  | 730 |  | 
|  | 731 | qdio_release_q(q); | 
|  | 732 | } | 
|  | 733 |  | 
|  | 734 | static void | 
|  | 735 | qdio_outbound_processing(struct qdio_q *q) | 
|  | 736 | { | 
|  | 737 | __qdio_outbound_processing(q); | 
|  | 738 | } | 
|  | 739 |  | 
|  | 740 | /************************* INBOUND ROUTINES *******************************/ | 
|  | 741 |  | 
|  | 742 |  | 
| Jesper Juhl | 77933d7 | 2005-07-27 11:46:09 -0700 | [diff] [blame] | 743 | static inline int | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 744 | qdio_get_inbound_buffer_frontier(struct qdio_q *q) | 
|  | 745 | { | 
|  | 746 | int f,f_mod_no; | 
|  | 747 | volatile char *slsb; | 
|  | 748 | int first_not_to_check; | 
|  | 749 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 750 | char dbf_text[15]; | 
|  | 751 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 752 | #ifdef QDIO_USE_PROCESSING_STATE | 
|  | 753 | int last_position=-1; | 
|  | 754 | #endif /* QDIO_USE_PROCESSING_STATE */ | 
|  | 755 |  | 
|  | 756 | QDIO_DBF_TEXT4(0,trace,"getibfro"); | 
|  | 757 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 758 |  | 
|  | 759 | slsb=&q->slsb.acc.val[0]; | 
|  | 760 | f_mod_no=f=q->first_to_check; | 
|  | 761 | /* | 
|  | 762 | * we don't check 128 buffers, as otherwise qdio_has_inbound_q_moved | 
|  | 763 | * would return 0 | 
|  | 764 | */ | 
|  | 765 | first_not_to_check=f+qdio_min(atomic_read(&q->number_of_buffers_used), | 
|  | 766 | (QDIO_MAX_BUFFERS_PER_Q-1)); | 
|  | 767 |  | 
|  | 768 | /* | 
|  | 769 | * we don't use this one, as a PCI or we after a thin interrupt | 
|  | 770 | * will sync the queues | 
|  | 771 | */ | 
|  | 772 | /* SYNC_MEMORY;*/ | 
|  | 773 |  | 
|  | 774 | check_next: | 
|  | 775 | f_mod_no=f&(QDIO_MAX_BUFFERS_PER_Q-1); | 
|  | 776 | if (f==first_not_to_check) | 
|  | 777 | goto out; | 
|  | 778 | switch (slsb[f_mod_no]) { | 
|  | 779 |  | 
|  | 780 | /* CU_EMPTY means frontier is reached */ | 
|  | 781 | case SLSB_CU_INPUT_EMPTY: | 
|  | 782 | QDIO_DBF_TEXT5(0,trace,"inptempt"); | 
|  | 783 | break; | 
|  | 784 |  | 
|  | 785 | /* P_PRIMED means set slsb to P_PROCESSING and move on */ | 
|  | 786 | case SLSB_P_INPUT_PRIMED: | 
|  | 787 | QDIO_DBF_TEXT5(0,trace,"inptprim"); | 
|  | 788 |  | 
|  | 789 | #ifdef QDIO_USE_PROCESSING_STATE | 
|  | 790 | /* | 
|  | 791 | * as soon as running under VM, polling the input queues will | 
|  | 792 | * kill VM in terms of CP overhead | 
|  | 793 | */ | 
|  | 794 | if (q->siga_sync) { | 
|  | 795 | set_slsb(&slsb[f_mod_no],SLSB_P_INPUT_NOT_INIT); | 
|  | 796 | } else { | 
|  | 797 | /* set the previous buffer to NOT_INIT. The current | 
|  | 798 | * buffer will be set to PROCESSING at the end of | 
|  | 799 | * this function to avoid further interrupts. */ | 
|  | 800 | if (last_position>=0) | 
|  | 801 | set_slsb(&slsb[last_position], | 
|  | 802 | SLSB_P_INPUT_NOT_INIT); | 
|  | 803 | atomic_set(&q->polling,1); | 
|  | 804 | last_position=f_mod_no; | 
|  | 805 | } | 
|  | 806 | #else /* QDIO_USE_PROCESSING_STATE */ | 
|  | 807 | set_slsb(&slsb[f_mod_no],SLSB_P_INPUT_NOT_INIT); | 
|  | 808 | #endif /* QDIO_USE_PROCESSING_STATE */ | 
|  | 809 | /* | 
|  | 810 | * not needed, as the inbound queue will be synced on the next | 
|  | 811 | * siga-r, resp. tiqdio_is_inbound_q_done will do the siga-s | 
|  | 812 | */ | 
|  | 813 | /*SYNC_MEMORY;*/ | 
|  | 814 | f++; | 
|  | 815 | atomic_dec(&q->number_of_buffers_used); | 
|  | 816 | goto check_next; | 
|  | 817 |  | 
|  | 818 | case SLSB_P_INPUT_NOT_INIT: | 
|  | 819 | case SLSB_P_INPUT_PROCESSING: | 
|  | 820 | QDIO_DBF_TEXT5(0,trace,"inpnipro"); | 
|  | 821 | break; | 
|  | 822 |  | 
|  | 823 | /* P_ERROR means frontier is reached, break and report error */ | 
|  | 824 | case SLSB_P_INPUT_ERROR: | 
|  | 825 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 826 | sprintf(dbf_text,"inperr%2x",f_mod_no); | 
|  | 827 | QDIO_DBF_TEXT3(1,trace,dbf_text); | 
|  | 828 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 829 | QDIO_DBF_HEX2(1,sbal,q->sbal[f_mod_no],256); | 
|  | 830 |  | 
|  | 831 | /* kind of process the buffer */ | 
|  | 832 | set_slsb(&slsb[f_mod_no],SLSB_P_INPUT_NOT_INIT); | 
|  | 833 |  | 
|  | 834 | if (q->qdio_error) | 
|  | 835 | q->error_status_flags|= | 
|  | 836 | QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR; | 
|  | 837 | q->qdio_error=SLSB_P_INPUT_ERROR; | 
|  | 838 | q->error_status_flags|=QDIO_STATUS_LOOK_FOR_ERROR; | 
|  | 839 |  | 
|  | 840 | /* we increment the frontier, as this buffer | 
|  | 841 | * was processed obviously */ | 
|  | 842 | f_mod_no=(f_mod_no+1)&(QDIO_MAX_BUFFERS_PER_Q-1); | 
|  | 843 | atomic_dec(&q->number_of_buffers_used); | 
|  | 844 |  | 
|  | 845 | #ifdef QDIO_USE_PROCESSING_STATE | 
|  | 846 | last_position=-1; | 
|  | 847 | #endif /* QDIO_USE_PROCESSING_STATE */ | 
|  | 848 |  | 
|  | 849 | break; | 
|  | 850 |  | 
|  | 851 | /* everything else means frontier not changed (HALTED or so) */ | 
|  | 852 | default: | 
|  | 853 | break; | 
|  | 854 | } | 
|  | 855 | out: | 
|  | 856 | q->first_to_check=f_mod_no; | 
|  | 857 |  | 
|  | 858 | #ifdef QDIO_USE_PROCESSING_STATE | 
|  | 859 | if (last_position>=0) | 
|  | 860 | set_slsb(&slsb[last_position],SLSB_P_INPUT_PROCESSING); | 
|  | 861 | #endif /* QDIO_USE_PROCESSING_STATE */ | 
|  | 862 |  | 
|  | 863 | QDIO_DBF_HEX4(0,trace,&q->first_to_check,sizeof(int)); | 
|  | 864 |  | 
|  | 865 | return q->first_to_check; | 
|  | 866 | } | 
|  | 867 |  | 
| Jesper Juhl | 77933d7 | 2005-07-27 11:46:09 -0700 | [diff] [blame] | 868 | static inline int | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 869 | qdio_has_inbound_q_moved(struct qdio_q *q) | 
|  | 870 | { | 
|  | 871 | int i; | 
|  | 872 |  | 
|  | 873 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 874 | static int old_pcis=0; | 
|  | 875 | static int old_thinints=0; | 
|  | 876 |  | 
|  | 877 | if ((old_pcis==perf_stats.pcis)&&(old_thinints==perf_stats.thinints)) | 
|  | 878 | perf_stats.start_time_inbound=NOW; | 
|  | 879 | else | 
|  | 880 | old_pcis=perf_stats.pcis; | 
|  | 881 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 882 |  | 
|  | 883 | i=qdio_get_inbound_buffer_frontier(q); | 
|  | 884 | if ( (i!=GET_SAVED_FRONTIER(q)) || | 
|  | 885 | (q->error_status_flags&QDIO_STATUS_LOOK_FOR_ERROR) ) { | 
|  | 886 | SAVE_FRONTIER(q,i); | 
|  | 887 | if ((!q->siga_sync)&&(!q->hydra_gives_outbound_pcis)) | 
|  | 888 | SAVE_TIMESTAMP(q); | 
|  | 889 |  | 
|  | 890 | QDIO_DBF_TEXT4(0,trace,"inhasmvd"); | 
|  | 891 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 892 | return 1; | 
|  | 893 | } else { | 
|  | 894 | QDIO_DBF_TEXT4(0,trace,"inhsntmv"); | 
|  | 895 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 896 | return 0; | 
|  | 897 | } | 
|  | 898 | } | 
|  | 899 |  | 
|  | 900 | /* means, no more buffers to be filled */ | 
| Jesper Juhl | 77933d7 | 2005-07-27 11:46:09 -0700 | [diff] [blame] | 901 | static inline int | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 902 | tiqdio_is_inbound_q_done(struct qdio_q *q) | 
|  | 903 | { | 
|  | 904 | int no_used; | 
|  | 905 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 906 | char dbf_text[15]; | 
|  | 907 | #endif | 
|  | 908 |  | 
|  | 909 | no_used=atomic_read(&q->number_of_buffers_used); | 
|  | 910 |  | 
|  | 911 | /* propagate the change from 82 to 80 through VM */ | 
|  | 912 | SYNC_MEMORY; | 
|  | 913 |  | 
|  | 914 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 915 | if (no_used) { | 
|  | 916 | sprintf(dbf_text,"iqisnt%02x",no_used); | 
|  | 917 | QDIO_DBF_TEXT4(0,trace,dbf_text); | 
|  | 918 | } else { | 
|  | 919 | QDIO_DBF_TEXT4(0,trace,"iniqisdo"); | 
|  | 920 | } | 
|  | 921 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 922 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 923 |  | 
|  | 924 | if (!no_used) | 
|  | 925 | return 1; | 
|  | 926 |  | 
|  | 927 | if (!q->siga_sync) | 
|  | 928 | /* we'll check for more primed buffers in qeth_stop_polling */ | 
|  | 929 | return 0; | 
|  | 930 |  | 
|  | 931 | if (q->slsb.acc.val[q->first_to_check]!=SLSB_P_INPUT_PRIMED) | 
|  | 932 | /* | 
|  | 933 | * nothing more to do, if next buffer is not PRIMED. | 
|  | 934 | * note that we did a SYNC_MEMORY before, that there | 
|  | 935 | * has been a sychnronization. | 
|  | 936 | * we will return 0 below, as there is nothing to do | 
|  | 937 | * (stop_polling not necessary, as we have not been | 
|  | 938 | * using the PROCESSING state | 
|  | 939 | */ | 
|  | 940 | return 0; | 
|  | 941 |  | 
|  | 942 | /* | 
|  | 943 | * ok, the next input buffer is primed. that means, that device state | 
|  | 944 | * change indicator and adapter local summary are set, so we will find | 
|  | 945 | * it next time. | 
|  | 946 | * we will return 0 below, as there is nothing to do, except scheduling | 
|  | 947 | * ourselves for the next time. | 
|  | 948 | */ | 
|  | 949 | tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind); | 
|  | 950 | tiqdio_sched_tl(); | 
|  | 951 | return 0; | 
|  | 952 | } | 
|  | 953 |  | 
| Jesper Juhl | 77933d7 | 2005-07-27 11:46:09 -0700 | [diff] [blame] | 954 | static inline int | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 955 | qdio_is_inbound_q_done(struct qdio_q *q) | 
|  | 956 | { | 
|  | 957 | int no_used; | 
|  | 958 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 959 | char dbf_text[15]; | 
|  | 960 | #endif | 
|  | 961 |  | 
|  | 962 | no_used=atomic_read(&q->number_of_buffers_used); | 
|  | 963 |  | 
|  | 964 | /* | 
|  | 965 | * we need that one for synchronization with the adapter, as it | 
|  | 966 | * does a kind of PCI avoidance | 
|  | 967 | */ | 
|  | 968 | SYNC_MEMORY; | 
|  | 969 |  | 
|  | 970 | if (!no_used) { | 
|  | 971 | QDIO_DBF_TEXT4(0,trace,"inqisdnA"); | 
|  | 972 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 973 | QDIO_DBF_TEXT4(0,trace,dbf_text); | 
|  | 974 | return 1; | 
|  | 975 | } | 
|  | 976 |  | 
|  | 977 | if (q->slsb.acc.val[q->first_to_check]==SLSB_P_INPUT_PRIMED) { | 
|  | 978 | /* we got something to do */ | 
|  | 979 | QDIO_DBF_TEXT4(0,trace,"inqisntA"); | 
|  | 980 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 981 | return 0; | 
|  | 982 | } | 
|  | 983 |  | 
|  | 984 | /* on VM, we don't poll, so the q is always done here */ | 
|  | 985 | if (q->siga_sync) | 
|  | 986 | return 1; | 
|  | 987 | if (q->hydra_gives_outbound_pcis) | 
|  | 988 | return 1; | 
|  | 989 |  | 
|  | 990 | /* | 
|  | 991 | * at this point we know, that inbound first_to_check | 
|  | 992 | * has (probably) not moved (see qdio_inbound_processing) | 
|  | 993 | */ | 
|  | 994 | if (NOW>GET_SAVED_TIMESTAMP(q)+q->timing.threshold) { | 
|  | 995 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 996 | QDIO_DBF_TEXT4(0,trace,"inqisdon"); | 
|  | 997 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 998 | sprintf(dbf_text,"pf%02xcn%02x",q->first_to_check,no_used); | 
|  | 999 | QDIO_DBF_TEXT4(0,trace,dbf_text); | 
|  | 1000 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 1001 | return 1; | 
|  | 1002 | } else { | 
|  | 1003 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 1004 | QDIO_DBF_TEXT4(0,trace,"inqisntd"); | 
|  | 1005 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 1006 | sprintf(dbf_text,"pf%02xcn%02x",q->first_to_check,no_used); | 
|  | 1007 | QDIO_DBF_TEXT4(0,trace,dbf_text); | 
|  | 1008 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 1009 | return 0; | 
|  | 1010 | } | 
|  | 1011 | } | 
|  | 1012 |  | 
| Jesper Juhl | 77933d7 | 2005-07-27 11:46:09 -0700 | [diff] [blame] | 1013 | static inline void | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1014 | qdio_kick_inbound_handler(struct qdio_q *q) | 
|  | 1015 | { | 
|  | 1016 | int count, start, end, real_end, i; | 
|  | 1017 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 1018 | char dbf_text[15]; | 
|  | 1019 | #endif | 
|  | 1020 |  | 
|  | 1021 | QDIO_DBF_TEXT4(0,trace,"kickinh"); | 
|  | 1022 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 1023 |  | 
|  | 1024 | start=q->first_element_to_kick; | 
|  | 1025 | real_end=q->first_to_check; | 
|  | 1026 | end=(real_end+QDIO_MAX_BUFFERS_PER_Q-1)&(QDIO_MAX_BUFFERS_PER_Q-1); | 
|  | 1027 |  | 
|  | 1028 | i=start; | 
|  | 1029 | count=0; | 
|  | 1030 | while (1) { | 
|  | 1031 | count++; | 
|  | 1032 | if (i==end) | 
|  | 1033 | break; | 
|  | 1034 | i=(i+1)&(QDIO_MAX_BUFFERS_PER_Q-1); | 
|  | 1035 | } | 
|  | 1036 |  | 
|  | 1037 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 1038 | sprintf(dbf_text,"s=%2xc=%2x",start,count); | 
|  | 1039 | QDIO_DBF_TEXT4(0,trace,dbf_text); | 
|  | 1040 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 1041 |  | 
|  | 1042 | if (likely(q->state==QDIO_IRQ_STATE_ACTIVE)) | 
|  | 1043 | q->handler(q->cdev, | 
|  | 1044 | QDIO_STATUS_INBOUND_INT|q->error_status_flags, | 
|  | 1045 | q->qdio_error,q->siga_error,q->q_no,start,count, | 
|  | 1046 | q->int_parm); | 
|  | 1047 |  | 
|  | 1048 | /* for the next time: */ | 
|  | 1049 | q->first_element_to_kick=real_end; | 
|  | 1050 | q->qdio_error=0; | 
|  | 1051 | q->siga_error=0; | 
|  | 1052 | q->error_status_flags=0; | 
|  | 1053 |  | 
|  | 1054 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 1055 | perf_stats.inbound_time+=NOW-perf_stats.start_time_inbound; | 
|  | 1056 | perf_stats.inbound_cnt++; | 
|  | 1057 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 1058 | } | 
|  | 1059 |  | 
|  | 1060 | static inline void | 
|  | 1061 | __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set) | 
|  | 1062 | { | 
|  | 1063 | struct qdio_irq *irq_ptr; | 
|  | 1064 | struct qdio_q *oq; | 
|  | 1065 | int i; | 
|  | 1066 |  | 
|  | 1067 | QDIO_DBF_TEXT4(0,trace,"iqinproc"); | 
|  | 1068 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 1069 |  | 
|  | 1070 | /* | 
|  | 1071 | * we first want to reserve the q, so that we know, that we don't | 
|  | 1072 | * interrupt ourselves and call qdio_unmark_q, as is_in_shutdown might | 
|  | 1073 | * be set | 
|  | 1074 | */ | 
|  | 1075 | if (unlikely(qdio_reserve_q(q))) { | 
|  | 1076 | qdio_release_q(q); | 
|  | 1077 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 1078 | ii_p_c++; | 
|  | 1079 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 1080 | /* | 
|  | 1081 | * as we might just be about to stop polling, we make | 
|  | 1082 | * sure that we check again at least once more | 
|  | 1083 | */ | 
|  | 1084 | tiqdio_sched_tl(); | 
|  | 1085 | return; | 
|  | 1086 | } | 
|  | 1087 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 1088 | ii_p_nc++; | 
|  | 1089 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 1090 | if (unlikely(atomic_read(&q->is_in_shutdown))) { | 
|  | 1091 | qdio_unmark_q(q); | 
|  | 1092 | goto out; | 
|  | 1093 | } | 
|  | 1094 |  | 
|  | 1095 | /* | 
|  | 1096 | * we reset spare_ind_was_set, when the queue does not use the | 
|  | 1097 | * spare indicator | 
|  | 1098 | */ | 
|  | 1099 | if (spare_ind_was_set) | 
|  | 1100 | spare_ind_was_set = (q->dev_st_chg_ind == &spare_indicator); | 
|  | 1101 |  | 
|  | 1102 | if (!(*(q->dev_st_chg_ind)) && !spare_ind_was_set) | 
|  | 1103 | goto out; | 
|  | 1104 | /* | 
|  | 1105 | * q->dev_st_chg_ind is the indicator, be it shared or not. | 
|  | 1106 | * only clear it, if indicator is non-shared | 
|  | 1107 | */ | 
|  | 1108 | if (!spare_ind_was_set) | 
|  | 1109 | tiqdio_clear_summary_bit((__u32*)q->dev_st_chg_ind); | 
|  | 1110 |  | 
|  | 1111 | if (q->hydra_gives_outbound_pcis) { | 
|  | 1112 | if (!q->siga_sync_done_on_thinints) { | 
|  | 1113 | SYNC_MEMORY_ALL; | 
|  | 1114 | } else if ((!q->siga_sync_done_on_outb_tis)&& | 
|  | 1115 | (q->hydra_gives_outbound_pcis)) { | 
|  | 1116 | SYNC_MEMORY_ALL_OUTB; | 
|  | 1117 | } | 
|  | 1118 | } else { | 
|  | 1119 | SYNC_MEMORY; | 
|  | 1120 | } | 
|  | 1121 | /* | 
|  | 1122 | * maybe we have to do work on our outbound queues... at least | 
|  | 1123 | * we have to check the outbound-int-capable thinint-capable | 
|  | 1124 | * queues | 
|  | 1125 | */ | 
|  | 1126 | if (q->hydra_gives_outbound_pcis) { | 
|  | 1127 | irq_ptr = (struct qdio_irq*)q->irq_ptr; | 
|  | 1128 | for (i=0;i<irq_ptr->no_output_qs;i++) { | 
|  | 1129 | oq = irq_ptr->output_qs[i]; | 
|  | 1130 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 1131 | perf_stats.tl_runs--; | 
|  | 1132 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 1133 | if (!qdio_is_outbound_q_done(oq)) | 
|  | 1134 | __qdio_outbound_processing(oq); | 
|  | 1135 | } | 
|  | 1136 | } | 
|  | 1137 |  | 
|  | 1138 | if (!qdio_has_inbound_q_moved(q)) | 
|  | 1139 | goto out; | 
|  | 1140 |  | 
|  | 1141 | qdio_kick_inbound_handler(q); | 
|  | 1142 | if (tiqdio_is_inbound_q_done(q)) | 
|  | 1143 | if (!qdio_stop_polling(q)) { | 
|  | 1144 | /* | 
|  | 1145 | * we set the flags to get into the stuff next time, | 
|  | 1146 | * see also comment in qdio_stop_polling | 
|  | 1147 | */ | 
|  | 1148 | tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind); | 
|  | 1149 | tiqdio_sched_tl(); | 
|  | 1150 | } | 
|  | 1151 | out: | 
|  | 1152 | qdio_release_q(q); | 
|  | 1153 | } | 
|  | 1154 |  | 
|  | 1155 | static void | 
|  | 1156 | tiqdio_inbound_processing(struct qdio_q *q) | 
|  | 1157 | { | 
|  | 1158 | __tiqdio_inbound_processing(q, atomic_read(&spare_indicator_usecount)); | 
|  | 1159 | } | 
|  | 1160 |  | 
|  | 1161 | static inline void | 
|  | 1162 | __qdio_inbound_processing(struct qdio_q *q) | 
|  | 1163 | { | 
|  | 1164 | int q_laps=0; | 
|  | 1165 |  | 
|  | 1166 | QDIO_DBF_TEXT4(0,trace,"qinproc"); | 
|  | 1167 | QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); | 
|  | 1168 |  | 
|  | 1169 | if (unlikely(qdio_reserve_q(q))) { | 
|  | 1170 | qdio_release_q(q); | 
|  | 1171 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 1172 | i_p_c++; | 
|  | 1173 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 1174 | /* as we're sissies, we'll check next time */ | 
|  | 1175 | if (likely(!atomic_read(&q->is_in_shutdown))) { | 
|  | 1176 | qdio_mark_q(q); | 
|  | 1177 | QDIO_DBF_TEXT4(0,trace,"busy,agn"); | 
|  | 1178 | } | 
|  | 1179 | return; | 
|  | 1180 | } | 
|  | 1181 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 1182 | i_p_nc++; | 
|  | 1183 | perf_stats.tl_runs++; | 
|  | 1184 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 1185 |  | 
|  | 1186 | again: | 
|  | 1187 | if (qdio_has_inbound_q_moved(q)) { | 
|  | 1188 | qdio_kick_inbound_handler(q); | 
|  | 1189 | if (!qdio_stop_polling(q)) { | 
|  | 1190 | q_laps++; | 
|  | 1191 | if (q_laps<QDIO_Q_LAPS) | 
|  | 1192 | goto again; | 
|  | 1193 | } | 
|  | 1194 | qdio_mark_q(q); | 
|  | 1195 | } else { | 
|  | 1196 | if (!qdio_is_inbound_q_done(q)) | 
|  | 1197 | /* means poll time is not yet over */ | 
|  | 1198 | qdio_mark_q(q); | 
|  | 1199 | } | 
|  | 1200 |  | 
|  | 1201 | qdio_release_q(q); | 
|  | 1202 | } | 
|  | 1203 |  | 
|  | 1204 | static void | 
|  | 1205 | qdio_inbound_processing(struct qdio_q *q) | 
|  | 1206 | { | 
|  | 1207 | __qdio_inbound_processing(q); | 
|  | 1208 | } | 
|  | 1209 |  | 
|  | 1210 | /************************* MAIN ROUTINES *******************************/ | 
|  | 1211 |  | 
|  | 1212 | #ifdef QDIO_USE_PROCESSING_STATE | 
|  | 1213 | static inline int | 
|  | 1214 | tiqdio_reset_processing_state(struct qdio_q *q, int q_laps) | 
|  | 1215 | { | 
|  | 1216 | if (!q) { | 
|  | 1217 | tiqdio_sched_tl(); | 
|  | 1218 | return 0; | 
|  | 1219 | } | 
|  | 1220 |  | 
|  | 1221 | /* | 
|  | 1222 | * under VM, we have not used the PROCESSING state, so no | 
|  | 1223 | * need to stop polling | 
|  | 1224 | */ | 
|  | 1225 | if (q->siga_sync) | 
|  | 1226 | return 2; | 
|  | 1227 |  | 
|  | 1228 | if (unlikely(qdio_reserve_q(q))) { | 
|  | 1229 | qdio_release_q(q); | 
|  | 1230 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 1231 | ii_p_c++; | 
|  | 1232 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 1233 | /* | 
|  | 1234 | * as we might just be about to stop polling, we make | 
|  | 1235 | * sure that we check again at least once more | 
|  | 1236 | */ | 
|  | 1237 |  | 
|  | 1238 | /* | 
|  | 1239 | * sanity -- we'd get here without setting the | 
|  | 1240 | * dev st chg ind | 
|  | 1241 | */ | 
|  | 1242 | tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind); | 
|  | 1243 | tiqdio_sched_tl(); | 
|  | 1244 | return 0; | 
|  | 1245 | } | 
|  | 1246 | if (qdio_stop_polling(q)) { | 
|  | 1247 | qdio_release_q(q); | 
|  | 1248 | return 2; | 
|  | 1249 | } | 
|  | 1250 | if (q_laps<QDIO_Q_LAPS-1) { | 
|  | 1251 | qdio_release_q(q); | 
|  | 1252 | return 3; | 
|  | 1253 | } | 
|  | 1254 | /* | 
|  | 1255 | * we set the flags to get into the stuff | 
|  | 1256 | * next time, see also comment in qdio_stop_polling | 
|  | 1257 | */ | 
|  | 1258 | tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind); | 
|  | 1259 | tiqdio_sched_tl(); | 
|  | 1260 | qdio_release_q(q); | 
|  | 1261 | return 1; | 
|  | 1262 |  | 
|  | 1263 | } | 
|  | 1264 | #endif /* QDIO_USE_PROCESSING_STATE */ | 
|  | 1265 |  | 
|  | 1266 | static inline void | 
|  | 1267 | tiqdio_inbound_checks(void) | 
|  | 1268 | { | 
|  | 1269 | struct qdio_q *q; | 
|  | 1270 | int spare_ind_was_set=0; | 
|  | 1271 | #ifdef QDIO_USE_PROCESSING_STATE | 
|  | 1272 | int q_laps=0; | 
|  | 1273 | #endif /* QDIO_USE_PROCESSING_STATE */ | 
|  | 1274 |  | 
|  | 1275 | QDIO_DBF_TEXT4(0,trace,"iqdinbck"); | 
|  | 1276 | QDIO_DBF_TEXT5(0,trace,"iqlocsum"); | 
|  | 1277 |  | 
|  | 1278 | #ifdef QDIO_USE_PROCESSING_STATE | 
|  | 1279 | again: | 
|  | 1280 | #endif /* QDIO_USE_PROCESSING_STATE */ | 
|  | 1281 |  | 
|  | 1282 | /* when the spare indicator is used and set, save that and clear it */ | 
|  | 1283 | if ((atomic_read(&spare_indicator_usecount)) && spare_indicator) { | 
|  | 1284 | spare_ind_was_set = 1; | 
|  | 1285 | tiqdio_clear_summary_bit((__u32*)&spare_indicator); | 
|  | 1286 | } | 
|  | 1287 |  | 
|  | 1288 | q=(struct qdio_q*)tiq_list; | 
|  | 1289 | do { | 
|  | 1290 | if (!q) | 
|  | 1291 | break; | 
|  | 1292 | __tiqdio_inbound_processing(q, spare_ind_was_set); | 
|  | 1293 | q=(struct qdio_q*)q->list_next; | 
|  | 1294 | } while (q!=(struct qdio_q*)tiq_list); | 
|  | 1295 |  | 
|  | 1296 | #ifdef QDIO_USE_PROCESSING_STATE | 
|  | 1297 | q=(struct qdio_q*)tiq_list; | 
|  | 1298 | do { | 
|  | 1299 | int ret; | 
|  | 1300 |  | 
|  | 1301 | ret = tiqdio_reset_processing_state(q, q_laps); | 
|  | 1302 | switch (ret) { | 
|  | 1303 | case 0: | 
|  | 1304 | return; | 
|  | 1305 | case 1: | 
|  | 1306 | q_laps++; | 
|  | 1307 | case 2: | 
|  | 1308 | q = (struct qdio_q*)q->list_next; | 
|  | 1309 | break; | 
|  | 1310 | default: | 
|  | 1311 | q_laps++; | 
|  | 1312 | goto again; | 
|  | 1313 | } | 
|  | 1314 | } while (q!=(struct qdio_q*)tiq_list); | 
|  | 1315 | #endif /* QDIO_USE_PROCESSING_STATE */ | 
|  | 1316 | } | 
|  | 1317 |  | 
|  | 1318 | static void | 
|  | 1319 | tiqdio_tl(unsigned long data) | 
|  | 1320 | { | 
|  | 1321 | QDIO_DBF_TEXT4(0,trace,"iqdio_tl"); | 
|  | 1322 |  | 
|  | 1323 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 1324 | perf_stats.tl_runs++; | 
|  | 1325 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 1326 |  | 
|  | 1327 | tiqdio_inbound_checks(); | 
|  | 1328 | } | 
|  | 1329 |  | 
|  | 1330 | /********************* GENERAL HELPER_ROUTINES ***********************/ | 
|  | 1331 |  | 
|  | 1332 | static void | 
|  | 1333 | qdio_release_irq_memory(struct qdio_irq *irq_ptr) | 
|  | 1334 | { | 
|  | 1335 | int i; | 
|  | 1336 |  | 
|  | 1337 | for (i=0;i<QDIO_MAX_QUEUES_PER_IRQ;i++) { | 
|  | 1338 | if (!irq_ptr->input_qs[i]) | 
|  | 1339 | goto next; | 
|  | 1340 |  | 
| Jesper Juhl | 17fd682 | 2005-11-07 01:01:30 -0800 | [diff] [blame] | 1341 | kfree(irq_ptr->input_qs[i]->slib); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1342 | kfree(irq_ptr->input_qs[i]); | 
|  | 1343 |  | 
|  | 1344 | next: | 
|  | 1345 | if (!irq_ptr->output_qs[i]) | 
|  | 1346 | continue; | 
|  | 1347 |  | 
| Jesper Juhl | 17fd682 | 2005-11-07 01:01:30 -0800 | [diff] [blame] | 1348 | kfree(irq_ptr->output_qs[i]->slib); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1349 | kfree(irq_ptr->output_qs[i]); | 
|  | 1350 |  | 
|  | 1351 | } | 
|  | 1352 | kfree(irq_ptr->qdr); | 
|  | 1353 | kfree(irq_ptr); | 
|  | 1354 | } | 
|  | 1355 |  | 
|  | 1356 | static void | 
|  | 1357 | qdio_set_impl_params(struct qdio_irq *irq_ptr, | 
|  | 1358 | unsigned int qib_param_field_format, | 
|  | 1359 | /* pointer to 128 bytes or NULL, if no param field */ | 
|  | 1360 | unsigned char *qib_param_field, | 
|  | 1361 | /* pointer to no_queues*128 words of data or NULL */ | 
|  | 1362 | unsigned int no_input_qs, | 
|  | 1363 | unsigned int no_output_qs, | 
|  | 1364 | unsigned long *input_slib_elements, | 
|  | 1365 | unsigned long *output_slib_elements) | 
|  | 1366 | { | 
|  | 1367 | int i,j; | 
|  | 1368 |  | 
|  | 1369 | if (!irq_ptr) | 
|  | 1370 | return; | 
|  | 1371 |  | 
|  | 1372 | irq_ptr->qib.pfmt=qib_param_field_format; | 
|  | 1373 | if (qib_param_field) | 
|  | 1374 | memcpy(irq_ptr->qib.parm,qib_param_field, | 
|  | 1375 | QDIO_MAX_BUFFERS_PER_Q); | 
|  | 1376 |  | 
|  | 1377 | if (input_slib_elements) | 
|  | 1378 | for (i=0;i<no_input_qs;i++) { | 
|  | 1379 | for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) | 
|  | 1380 | irq_ptr->input_qs[i]->slib->slibe[j].parms= | 
|  | 1381 | input_slib_elements[ | 
|  | 1382 | i*QDIO_MAX_BUFFERS_PER_Q+j]; | 
|  | 1383 | } | 
|  | 1384 | if (output_slib_elements) | 
|  | 1385 | for (i=0;i<no_output_qs;i++) { | 
|  | 1386 | for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) | 
|  | 1387 | irq_ptr->output_qs[i]->slib->slibe[j].parms= | 
|  | 1388 | output_slib_elements[ | 
|  | 1389 | i*QDIO_MAX_BUFFERS_PER_Q+j]; | 
|  | 1390 | } | 
|  | 1391 | } | 
|  | 1392 |  | 
|  | 1393 | static int | 
|  | 1394 | qdio_alloc_qs(struct qdio_irq *irq_ptr, | 
|  | 1395 | int no_input_qs, int no_output_qs) | 
|  | 1396 | { | 
|  | 1397 | int i; | 
|  | 1398 | struct qdio_q *q; | 
|  | 1399 | int result=-ENOMEM; | 
|  | 1400 |  | 
|  | 1401 | for (i=0;i<no_input_qs;i++) { | 
|  | 1402 | q=kmalloc(sizeof(struct qdio_q),GFP_KERNEL); | 
|  | 1403 |  | 
|  | 1404 | if (!q) { | 
|  | 1405 | QDIO_PRINT_ERR("kmalloc of q failed!\n"); | 
|  | 1406 | goto out; | 
|  | 1407 | } | 
|  | 1408 |  | 
|  | 1409 | memset(q,0,sizeof(struct qdio_q)); | 
|  | 1410 |  | 
|  | 1411 | q->slib=kmalloc(PAGE_SIZE,GFP_KERNEL); | 
|  | 1412 | if (!q->slib) { | 
|  | 1413 | QDIO_PRINT_ERR("kmalloc of slib failed!\n"); | 
|  | 1414 | goto out; | 
|  | 1415 | } | 
|  | 1416 |  | 
|  | 1417 | irq_ptr->input_qs[i]=q; | 
|  | 1418 | } | 
|  | 1419 |  | 
|  | 1420 | for (i=0;i<no_output_qs;i++) { | 
|  | 1421 | q=kmalloc(sizeof(struct qdio_q),GFP_KERNEL); | 
|  | 1422 |  | 
|  | 1423 | if (!q) { | 
|  | 1424 | goto out; | 
|  | 1425 | } | 
|  | 1426 |  | 
|  | 1427 | memset(q,0,sizeof(struct qdio_q)); | 
|  | 1428 |  | 
|  | 1429 | q->slib=kmalloc(PAGE_SIZE,GFP_KERNEL); | 
|  | 1430 | if (!q->slib) { | 
|  | 1431 | QDIO_PRINT_ERR("kmalloc of slib failed!\n"); | 
|  | 1432 | goto out; | 
|  | 1433 | } | 
|  | 1434 |  | 
|  | 1435 | irq_ptr->output_qs[i]=q; | 
|  | 1436 | } | 
|  | 1437 |  | 
|  | 1438 | result=0; | 
|  | 1439 | out: | 
|  | 1440 | return result; | 
|  | 1441 | } | 
|  | 1442 |  | 
|  | 1443 | static void | 
|  | 1444 | qdio_fill_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev, | 
|  | 1445 | int no_input_qs, int no_output_qs, | 
|  | 1446 | qdio_handler_t *input_handler, | 
|  | 1447 | qdio_handler_t *output_handler, | 
|  | 1448 | unsigned long int_parm,int q_format, | 
|  | 1449 | unsigned long flags, | 
|  | 1450 | void **inbound_sbals_array, | 
|  | 1451 | void **outbound_sbals_array) | 
|  | 1452 | { | 
|  | 1453 | struct qdio_q *q; | 
|  | 1454 | int i,j; | 
|  | 1455 | char dbf_text[20]; /* see qdio_initialize */ | 
|  | 1456 | void *ptr; | 
|  | 1457 | int available; | 
|  | 1458 |  | 
|  | 1459 | sprintf(dbf_text,"qfqs%4x",cdev->private->irq); | 
|  | 1460 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 1461 | for (i=0;i<no_input_qs;i++) { | 
|  | 1462 | q=irq_ptr->input_qs[i]; | 
|  | 1463 |  | 
|  | 1464 | memset(q,0,((char*)&q->slib)-((char*)q)); | 
|  | 1465 | sprintf(dbf_text,"in-q%4x",i); | 
|  | 1466 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 1467 | QDIO_DBF_HEX0(0,setup,&q,sizeof(void*)); | 
|  | 1468 |  | 
|  | 1469 | memset(q->slib,0,PAGE_SIZE); | 
|  | 1470 | q->sl=(struct sl*)(((char*)q->slib)+PAGE_SIZE/2); | 
|  | 1471 |  | 
|  | 1472 | available=0; | 
|  | 1473 |  | 
|  | 1474 | for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) | 
|  | 1475 | q->sbal[j]=*(inbound_sbals_array++); | 
|  | 1476 |  | 
|  | 1477 | q->queue_type=q_format; | 
|  | 1478 | q->int_parm=int_parm; | 
|  | 1479 | q->irq=irq_ptr->irq; | 
|  | 1480 | q->irq_ptr = irq_ptr; | 
|  | 1481 | q->cdev = cdev; | 
|  | 1482 | q->mask=1<<(31-i); | 
|  | 1483 | q->q_no=i; | 
|  | 1484 | q->is_input_q=1; | 
|  | 1485 | q->first_to_check=0; | 
|  | 1486 | q->last_move_ftc=0; | 
|  | 1487 | q->handler=input_handler; | 
|  | 1488 | q->dev_st_chg_ind=irq_ptr->dev_st_chg_ind; | 
|  | 1489 |  | 
|  | 1490 | q->tasklet.data=(unsigned long)q; | 
|  | 1491 | /* q->is_thinint_q isn't valid at this time, but | 
|  | 1492 | * irq_ptr->is_thinint_irq is */ | 
|  | 1493 | q->tasklet.func=(void(*)(unsigned long)) | 
|  | 1494 | ((irq_ptr->is_thinint_irq)?&tiqdio_inbound_processing: | 
|  | 1495 | &qdio_inbound_processing); | 
|  | 1496 |  | 
|  | 1497 | /* actually this is not used for inbound queues. yet. */ | 
|  | 1498 | atomic_set(&q->busy_siga_counter,0); | 
|  | 1499 | q->timing.busy_start=0; | 
|  | 1500 |  | 
|  | 1501 | /*		for (j=0;j<QDIO_STATS_NUMBER;j++) | 
|  | 1502 | q->timing.last_transfer_times[j]=(qdio_get_micros()/ | 
|  | 1503 | QDIO_STATS_NUMBER)*j; | 
|  | 1504 | q->timing.last_transfer_index=QDIO_STATS_NUMBER-1; | 
|  | 1505 | */ | 
|  | 1506 |  | 
|  | 1507 | /* fill in slib */ | 
|  | 1508 | if (i>0) irq_ptr->input_qs[i-1]->slib->nsliba= | 
|  | 1509 | (unsigned long)(q->slib); | 
|  | 1510 | q->slib->sla=(unsigned long)(q->sl); | 
|  | 1511 | q->slib->slsba=(unsigned long)(&q->slsb.acc.val[0]); | 
|  | 1512 |  | 
|  | 1513 | /* fill in sl */ | 
|  | 1514 | for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) | 
|  | 1515 | q->sl->element[j].sbal=(unsigned long)(q->sbal[j]); | 
|  | 1516 |  | 
|  | 1517 | QDIO_DBF_TEXT2(0,setup,"sl-sb-b0"); | 
|  | 1518 | ptr=(void*)q->sl; | 
|  | 1519 | QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*)); | 
|  | 1520 | ptr=(void*)&q->slsb; | 
|  | 1521 | QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*)); | 
|  | 1522 | ptr=(void*)q->sbal[0]; | 
|  | 1523 | QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*)); | 
|  | 1524 |  | 
|  | 1525 | /* fill in slsb */ | 
|  | 1526 | for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) { | 
|  | 1527 | set_slsb(&q->slsb.acc.val[j], | 
|  | 1528 | SLSB_P_INPUT_NOT_INIT); | 
|  | 1529 | /*			q->sbal[j]->element[1].sbalf.i1.key=QDIO_STORAGE_KEY;*/ | 
|  | 1530 | } | 
|  | 1531 | } | 
|  | 1532 |  | 
|  | 1533 | for (i=0;i<no_output_qs;i++) { | 
|  | 1534 | q=irq_ptr->output_qs[i]; | 
|  | 1535 | memset(q,0,((char*)&q->slib)-((char*)q)); | 
|  | 1536 |  | 
|  | 1537 | sprintf(dbf_text,"outq%4x",i); | 
|  | 1538 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 1539 | QDIO_DBF_HEX0(0,setup,&q,sizeof(void*)); | 
|  | 1540 |  | 
|  | 1541 | memset(q->slib,0,PAGE_SIZE); | 
|  | 1542 | q->sl=(struct sl*)(((char*)q->slib)+PAGE_SIZE/2); | 
|  | 1543 |  | 
|  | 1544 | available=0; | 
|  | 1545 |  | 
|  | 1546 | for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) | 
|  | 1547 | q->sbal[j]=*(outbound_sbals_array++); | 
|  | 1548 |  | 
|  | 1549 | q->queue_type=q_format; | 
|  | 1550 | q->int_parm=int_parm; | 
|  | 1551 | q->is_input_q=0; | 
|  | 1552 | q->irq=irq_ptr->irq; | 
|  | 1553 | q->cdev = cdev; | 
|  | 1554 | q->irq_ptr = irq_ptr; | 
|  | 1555 | q->mask=1<<(31-i); | 
|  | 1556 | q->q_no=i; | 
|  | 1557 | q->first_to_check=0; | 
|  | 1558 | q->last_move_ftc=0; | 
|  | 1559 | q->handler=output_handler; | 
|  | 1560 |  | 
|  | 1561 | q->tasklet.data=(unsigned long)q; | 
|  | 1562 | q->tasklet.func=(void(*)(unsigned long)) | 
|  | 1563 | &qdio_outbound_processing; | 
|  | 1564 |  | 
|  | 1565 | atomic_set(&q->busy_siga_counter,0); | 
|  | 1566 | q->timing.busy_start=0; | 
|  | 1567 |  | 
|  | 1568 | /* fill in slib */ | 
|  | 1569 | if (i>0) irq_ptr->output_qs[i-1]->slib->nsliba= | 
|  | 1570 | (unsigned long)(q->slib); | 
|  | 1571 | q->slib->sla=(unsigned long)(q->sl); | 
|  | 1572 | q->slib->slsba=(unsigned long)(&q->slsb.acc.val[0]); | 
|  | 1573 |  | 
|  | 1574 | /* fill in sl */ | 
|  | 1575 | for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) | 
|  | 1576 | q->sl->element[j].sbal=(unsigned long)(q->sbal[j]); | 
|  | 1577 |  | 
|  | 1578 | QDIO_DBF_TEXT2(0,setup,"sl-sb-b0"); | 
|  | 1579 | ptr=(void*)q->sl; | 
|  | 1580 | QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*)); | 
|  | 1581 | ptr=(void*)&q->slsb; | 
|  | 1582 | QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*)); | 
|  | 1583 | ptr=(void*)q->sbal[0]; | 
|  | 1584 | QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*)); | 
|  | 1585 |  | 
|  | 1586 | /* fill in slsb */ | 
|  | 1587 | for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) { | 
|  | 1588 | set_slsb(&q->slsb.acc.val[j], | 
|  | 1589 | SLSB_P_OUTPUT_NOT_INIT); | 
|  | 1590 | /*			q->sbal[j]->element[1].sbalf.i1.key=QDIO_STORAGE_KEY;*/ | 
|  | 1591 | } | 
|  | 1592 | } | 
|  | 1593 | } | 
|  | 1594 |  | 
|  | 1595 | static void | 
|  | 1596 | qdio_fill_thresholds(struct qdio_irq *irq_ptr, | 
|  | 1597 | unsigned int no_input_qs, | 
|  | 1598 | unsigned int no_output_qs, | 
|  | 1599 | unsigned int min_input_threshold, | 
|  | 1600 | unsigned int max_input_threshold, | 
|  | 1601 | unsigned int min_output_threshold, | 
|  | 1602 | unsigned int max_output_threshold) | 
|  | 1603 | { | 
|  | 1604 | int i; | 
|  | 1605 | struct qdio_q *q; | 
|  | 1606 |  | 
|  | 1607 | for (i=0;i<no_input_qs;i++) { | 
|  | 1608 | q=irq_ptr->input_qs[i]; | 
|  | 1609 | q->timing.threshold=max_input_threshold; | 
|  | 1610 | /*		for (j=0;j<QDIO_STATS_CLASSES;j++) { | 
|  | 1611 | q->threshold_classes[j].threshold= | 
|  | 1612 | min_input_threshold+ | 
|  | 1613 | (max_input_threshold-min_input_threshold)/ | 
|  | 1614 | QDIO_STATS_CLASSES; | 
|  | 1615 | } | 
|  | 1616 | qdio_use_thresholds(q,QDIO_STATS_CLASSES/2);*/ | 
|  | 1617 | } | 
|  | 1618 | for (i=0;i<no_output_qs;i++) { | 
|  | 1619 | q=irq_ptr->output_qs[i]; | 
|  | 1620 | q->timing.threshold=max_output_threshold; | 
|  | 1621 | /*		for (j=0;j<QDIO_STATS_CLASSES;j++) { | 
|  | 1622 | q->threshold_classes[j].threshold= | 
|  | 1623 | min_output_threshold+ | 
|  | 1624 | (max_output_threshold-min_output_threshold)/ | 
|  | 1625 | QDIO_STATS_CLASSES; | 
|  | 1626 | } | 
|  | 1627 | qdio_use_thresholds(q,QDIO_STATS_CLASSES/2);*/ | 
|  | 1628 | } | 
|  | 1629 | } | 
|  | 1630 |  | 
|  | 1631 | static int | 
|  | 1632 | tiqdio_thinint_handler(void) | 
|  | 1633 | { | 
|  | 1634 | QDIO_DBF_TEXT4(0,trace,"thin_int"); | 
|  | 1635 |  | 
|  | 1636 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 1637 | perf_stats.thinints++; | 
|  | 1638 | perf_stats.start_time_inbound=NOW; | 
|  | 1639 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 1640 |  | 
|  | 1641 | /* SVS only when needed: | 
|  | 1642 | * issue SVS to benefit from iqdio interrupt avoidance | 
|  | 1643 | * (SVS clears AISOI)*/ | 
|  | 1644 | if (!omit_svs) | 
|  | 1645 | tiqdio_clear_global_summary(); | 
|  | 1646 |  | 
|  | 1647 | tiqdio_inbound_checks(); | 
|  | 1648 | return 0; | 
|  | 1649 | } | 
|  | 1650 |  | 
|  | 1651 | static void | 
|  | 1652 | qdio_set_state(struct qdio_irq *irq_ptr, enum qdio_irq_states state) | 
|  | 1653 | { | 
|  | 1654 | int i; | 
|  | 1655 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 1656 | char dbf_text[15]; | 
|  | 1657 |  | 
|  | 1658 | QDIO_DBF_TEXT5(0,trace,"newstate"); | 
|  | 1659 | sprintf(dbf_text,"%4x%4x",irq_ptr->irq,state); | 
|  | 1660 | QDIO_DBF_TEXT5(0,trace,dbf_text); | 
|  | 1661 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 1662 |  | 
|  | 1663 | irq_ptr->state=state; | 
|  | 1664 | for (i=0;i<irq_ptr->no_input_qs;i++) | 
|  | 1665 | irq_ptr->input_qs[i]->state=state; | 
|  | 1666 | for (i=0;i<irq_ptr->no_output_qs;i++) | 
|  | 1667 | irq_ptr->output_qs[i]->state=state; | 
|  | 1668 | mb(); | 
|  | 1669 | } | 
|  | 1670 |  | 
|  | 1671 | static inline void | 
|  | 1672 | qdio_irq_check_sense(int irq, struct irb *irb) | 
|  | 1673 | { | 
|  | 1674 | char dbf_text[15]; | 
|  | 1675 |  | 
|  | 1676 | if (irb->esw.esw0.erw.cons) { | 
|  | 1677 | sprintf(dbf_text,"sens%4x",irq); | 
|  | 1678 | QDIO_DBF_TEXT2(1,trace,dbf_text); | 
|  | 1679 | QDIO_DBF_HEX0(0,sense,irb,QDIO_DBF_SENSE_LEN); | 
|  | 1680 |  | 
|  | 1681 | QDIO_PRINT_WARN("sense data available on qdio channel.\n"); | 
|  | 1682 | HEXDUMP16(WARN,"irb: ",irb); | 
|  | 1683 | HEXDUMP16(WARN,"sense data: ",irb->ecw); | 
|  | 1684 | } | 
|  | 1685 |  | 
|  | 1686 | } | 
|  | 1687 |  | 
|  | 1688 | static inline void | 
|  | 1689 | qdio_handle_pci(struct qdio_irq *irq_ptr) | 
|  | 1690 | { | 
|  | 1691 | int i; | 
|  | 1692 | struct qdio_q *q; | 
|  | 1693 |  | 
|  | 1694 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 1695 | perf_stats.pcis++; | 
|  | 1696 | perf_stats.start_time_inbound=NOW; | 
|  | 1697 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 1698 | for (i=0;i<irq_ptr->no_input_qs;i++) { | 
|  | 1699 | q=irq_ptr->input_qs[i]; | 
|  | 1700 | if (q->is_input_q&QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT) | 
|  | 1701 | qdio_mark_q(q); | 
|  | 1702 | else { | 
|  | 1703 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 1704 | perf_stats.tl_runs--; | 
|  | 1705 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 1706 | __qdio_inbound_processing(q); | 
|  | 1707 | } | 
|  | 1708 | } | 
|  | 1709 | if (!irq_ptr->hydra_gives_outbound_pcis) | 
|  | 1710 | return; | 
|  | 1711 | for (i=0;i<irq_ptr->no_output_qs;i++) { | 
|  | 1712 | q=irq_ptr->output_qs[i]; | 
|  | 1713 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 1714 | perf_stats.tl_runs--; | 
|  | 1715 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 1716 | if (qdio_is_outbound_q_done(q)) | 
|  | 1717 | continue; | 
|  | 1718 | if (!irq_ptr->sync_done_on_outb_pcis) | 
|  | 1719 | SYNC_MEMORY; | 
|  | 1720 | __qdio_outbound_processing(q); | 
|  | 1721 | } | 
|  | 1722 | } | 
|  | 1723 |  | 
|  | 1724 | static void qdio_establish_handle_irq(struct ccw_device*, int, int); | 
|  | 1725 |  | 
|  | 1726 | static inline void | 
|  | 1727 | qdio_handle_activate_check(struct ccw_device *cdev, unsigned long intparm, | 
|  | 1728 | int cstat, int dstat) | 
|  | 1729 | { | 
|  | 1730 | struct qdio_irq *irq_ptr; | 
|  | 1731 | struct qdio_q *q; | 
|  | 1732 | char dbf_text[15]; | 
|  | 1733 |  | 
|  | 1734 | irq_ptr = cdev->private->qdio_data; | 
|  | 1735 |  | 
|  | 1736 | QDIO_DBF_TEXT2(1, trace, "ick2"); | 
|  | 1737 | sprintf(dbf_text,"%s", cdev->dev.bus_id); | 
|  | 1738 | QDIO_DBF_TEXT2(1,trace,dbf_text); | 
|  | 1739 | QDIO_DBF_HEX2(0,trace,&intparm,sizeof(int)); | 
|  | 1740 | QDIO_DBF_HEX2(0,trace,&dstat,sizeof(int)); | 
|  | 1741 | QDIO_DBF_HEX2(0,trace,&cstat,sizeof(int)); | 
|  | 1742 | QDIO_PRINT_ERR("received check condition on activate " \ | 
|  | 1743 | "queues on device %s (cs=x%x, ds=x%x).\n", | 
|  | 1744 | cdev->dev.bus_id, cstat, dstat); | 
|  | 1745 | if (irq_ptr->no_input_qs) { | 
|  | 1746 | q=irq_ptr->input_qs[0]; | 
|  | 1747 | } else if (irq_ptr->no_output_qs) { | 
|  | 1748 | q=irq_ptr->output_qs[0]; | 
|  | 1749 | } else { | 
|  | 1750 | QDIO_PRINT_ERR("oops... no queue registered for device %s!?\n", | 
|  | 1751 | cdev->dev.bus_id); | 
|  | 1752 | goto omit_handler_call; | 
|  | 1753 | } | 
|  | 1754 | q->handler(q->cdev,QDIO_STATUS_ACTIVATE_CHECK_CONDITION| | 
|  | 1755 | QDIO_STATUS_LOOK_FOR_ERROR, | 
|  | 1756 | 0,0,0,-1,-1,q->int_parm); | 
|  | 1757 | omit_handler_call: | 
|  | 1758 | qdio_set_state(irq_ptr,QDIO_IRQ_STATE_STOPPED); | 
|  | 1759 |  | 
|  | 1760 | } | 
|  | 1761 |  | 
|  | 1762 | static void | 
|  | 1763 | qdio_call_shutdown(void *data) | 
|  | 1764 | { | 
|  | 1765 | struct ccw_device *cdev; | 
|  | 1766 |  | 
|  | 1767 | cdev = (struct ccw_device *)data; | 
|  | 1768 | qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR); | 
|  | 1769 | put_device(&cdev->dev); | 
|  | 1770 | } | 
|  | 1771 |  | 
|  | 1772 | static void | 
|  | 1773 | qdio_timeout_handler(struct ccw_device *cdev) | 
|  | 1774 | { | 
|  | 1775 | struct qdio_irq *irq_ptr; | 
|  | 1776 | char dbf_text[15]; | 
|  | 1777 |  | 
|  | 1778 | QDIO_DBF_TEXT2(0, trace, "qtoh"); | 
|  | 1779 | sprintf(dbf_text, "%s", cdev->dev.bus_id); | 
|  | 1780 | QDIO_DBF_TEXT2(0, trace, dbf_text); | 
|  | 1781 |  | 
|  | 1782 | irq_ptr = cdev->private->qdio_data; | 
|  | 1783 | sprintf(dbf_text, "state:%d", irq_ptr->state); | 
|  | 1784 | QDIO_DBF_TEXT2(0, trace, dbf_text); | 
|  | 1785 |  | 
|  | 1786 | switch (irq_ptr->state) { | 
|  | 1787 | case QDIO_IRQ_STATE_INACTIVE: | 
|  | 1788 | QDIO_PRINT_ERR("establish queues on irq %04x: timed out\n", | 
|  | 1789 | irq_ptr->irq); | 
|  | 1790 | QDIO_DBF_TEXT2(1,setup,"eq:timeo"); | 
|  | 1791 | qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); | 
|  | 1792 | break; | 
|  | 1793 | case QDIO_IRQ_STATE_CLEANUP: | 
|  | 1794 | QDIO_PRINT_INFO("Did not get interrupt on cleanup, irq=0x%x.\n", | 
|  | 1795 | irq_ptr->irq); | 
|  | 1796 | qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); | 
|  | 1797 | break; | 
|  | 1798 | case QDIO_IRQ_STATE_ESTABLISHED: | 
|  | 1799 | case QDIO_IRQ_STATE_ACTIVE: | 
|  | 1800 | /* I/O has been terminated by common I/O layer. */ | 
|  | 1801 | QDIO_PRINT_INFO("Queues on irq %04x killed by cio.\n", | 
|  | 1802 | irq_ptr->irq); | 
|  | 1803 | QDIO_DBF_TEXT2(1, trace, "cio:term"); | 
|  | 1804 | qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED); | 
|  | 1805 | if (get_device(&cdev->dev)) { | 
|  | 1806 | /* Can't call shutdown from interrupt context. */ | 
|  | 1807 | PREPARE_WORK(&cdev->private->kick_work, | 
|  | 1808 | qdio_call_shutdown, (void *)cdev); | 
|  | 1809 | queue_work(ccw_device_work, &cdev->private->kick_work); | 
|  | 1810 | } | 
|  | 1811 | break; | 
|  | 1812 | default: | 
|  | 1813 | BUG(); | 
|  | 1814 | } | 
|  | 1815 | ccw_device_set_timeout(cdev, 0); | 
|  | 1816 | wake_up(&cdev->private->wait_q); | 
|  | 1817 | } | 
|  | 1818 |  | 
|  | 1819 | static void | 
|  | 1820 | qdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) | 
|  | 1821 | { | 
|  | 1822 | struct qdio_irq *irq_ptr; | 
|  | 1823 | int cstat,dstat; | 
|  | 1824 | char dbf_text[15]; | 
|  | 1825 |  | 
|  | 1826 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 1827 | QDIO_DBF_TEXT4(0, trace, "qint"); | 
|  | 1828 | sprintf(dbf_text, "%s", cdev->dev.bus_id); | 
|  | 1829 | QDIO_DBF_TEXT4(0, trace, dbf_text); | 
|  | 1830 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 1831 |  | 
|  | 1832 | if (!intparm) { | 
|  | 1833 | QDIO_PRINT_ERR("got unsolicited interrupt in qdio " \ | 
|  | 1834 | "handler, device %s\n", cdev->dev.bus_id); | 
|  | 1835 | return; | 
|  | 1836 | } | 
|  | 1837 |  | 
|  | 1838 | irq_ptr = cdev->private->qdio_data; | 
|  | 1839 | if (!irq_ptr) { | 
|  | 1840 | QDIO_DBF_TEXT2(1, trace, "uint"); | 
|  | 1841 | sprintf(dbf_text,"%s", cdev->dev.bus_id); | 
|  | 1842 | QDIO_DBF_TEXT2(1,trace,dbf_text); | 
|  | 1843 | QDIO_PRINT_ERR("received interrupt on unused device %s!\n", | 
|  | 1844 | cdev->dev.bus_id); | 
|  | 1845 | return; | 
|  | 1846 | } | 
|  | 1847 |  | 
|  | 1848 | if (IS_ERR(irb)) { | 
|  | 1849 | /* Currently running i/o is in error. */ | 
|  | 1850 | switch (PTR_ERR(irb)) { | 
|  | 1851 | case -EIO: | 
|  | 1852 | QDIO_PRINT_ERR("i/o error on device %s\n", | 
|  | 1853 | cdev->dev.bus_id); | 
|  | 1854 | return; | 
|  | 1855 | case -ETIMEDOUT: | 
|  | 1856 | qdio_timeout_handler(cdev); | 
|  | 1857 | return; | 
|  | 1858 | default: | 
|  | 1859 | QDIO_PRINT_ERR("unknown error state %ld on device %s\n", | 
|  | 1860 | PTR_ERR(irb), cdev->dev.bus_id); | 
|  | 1861 | return; | 
|  | 1862 | } | 
|  | 1863 | } | 
|  | 1864 |  | 
|  | 1865 | qdio_irq_check_sense(irq_ptr->irq, irb); | 
|  | 1866 |  | 
|  | 1867 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 1868 | sprintf(dbf_text, "state:%d", irq_ptr->state); | 
|  | 1869 | QDIO_DBF_TEXT4(0, trace, dbf_text); | 
|  | 1870 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 1871 |  | 
|  | 1872 | cstat = irb->scsw.cstat; | 
|  | 1873 | dstat = irb->scsw.dstat; | 
|  | 1874 |  | 
|  | 1875 | switch (irq_ptr->state) { | 
|  | 1876 | case QDIO_IRQ_STATE_INACTIVE: | 
|  | 1877 | qdio_establish_handle_irq(cdev, cstat, dstat); | 
|  | 1878 | break; | 
|  | 1879 |  | 
|  | 1880 | case QDIO_IRQ_STATE_CLEANUP: | 
|  | 1881 | qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); | 
|  | 1882 | break; | 
|  | 1883 |  | 
|  | 1884 | case QDIO_IRQ_STATE_ESTABLISHED: | 
|  | 1885 | case QDIO_IRQ_STATE_ACTIVE: | 
|  | 1886 | if (cstat & SCHN_STAT_PCI) { | 
|  | 1887 | qdio_handle_pci(irq_ptr); | 
|  | 1888 | break; | 
|  | 1889 | } | 
|  | 1890 |  | 
|  | 1891 | if ((cstat&~SCHN_STAT_PCI)||dstat) { | 
|  | 1892 | qdio_handle_activate_check(cdev, intparm, cstat, dstat); | 
|  | 1893 | break; | 
|  | 1894 | } | 
|  | 1895 | default: | 
|  | 1896 | QDIO_PRINT_ERR("got interrupt for queues in state %d on " \ | 
|  | 1897 | "device %s?!\n", | 
|  | 1898 | irq_ptr->state, cdev->dev.bus_id); | 
|  | 1899 | } | 
|  | 1900 | wake_up(&cdev->private->wait_q); | 
|  | 1901 |  | 
|  | 1902 | } | 
|  | 1903 |  | 
|  | 1904 | int | 
|  | 1905 | qdio_synchronize(struct ccw_device *cdev, unsigned int flags, | 
|  | 1906 | unsigned int queue_number) | 
|  | 1907 | { | 
|  | 1908 | int cc; | 
|  | 1909 | struct qdio_q *q; | 
|  | 1910 | struct qdio_irq *irq_ptr; | 
|  | 1911 | void *ptr; | 
|  | 1912 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 1913 | char dbf_text[15]="SyncXXXX"; | 
|  | 1914 | #endif | 
|  | 1915 |  | 
|  | 1916 | irq_ptr = cdev->private->qdio_data; | 
|  | 1917 | if (!irq_ptr) | 
|  | 1918 | return -ENODEV; | 
|  | 1919 |  | 
|  | 1920 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 1921 | *((int*)(&dbf_text[4])) = irq_ptr->irq; | 
|  | 1922 | QDIO_DBF_HEX4(0,trace,dbf_text,QDIO_DBF_TRACE_LEN); | 
|  | 1923 | *((int*)(&dbf_text[0]))=flags; | 
|  | 1924 | *((int*)(&dbf_text[4]))=queue_number; | 
|  | 1925 | QDIO_DBF_HEX4(0,trace,dbf_text,QDIO_DBF_TRACE_LEN); | 
|  | 1926 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 1927 |  | 
|  | 1928 | if (flags&QDIO_FLAG_SYNC_INPUT) { | 
|  | 1929 | q=irq_ptr->input_qs[queue_number]; | 
|  | 1930 | if (!q) | 
|  | 1931 | return -EINVAL; | 
|  | 1932 | cc = do_siga_sync(q->irq, 0, q->mask); | 
|  | 1933 | } else if (flags&QDIO_FLAG_SYNC_OUTPUT) { | 
|  | 1934 | q=irq_ptr->output_qs[queue_number]; | 
|  | 1935 | if (!q) | 
|  | 1936 | return -EINVAL; | 
|  | 1937 | cc = do_siga_sync(q->irq, q->mask, 0); | 
|  | 1938 | } else | 
|  | 1939 | return -EINVAL; | 
|  | 1940 |  | 
|  | 1941 | ptr=&cc; | 
|  | 1942 | if (cc) | 
|  | 1943 | QDIO_DBF_HEX3(0,trace,&ptr,sizeof(int)); | 
|  | 1944 |  | 
|  | 1945 | return cc; | 
|  | 1946 | } | 
|  | 1947 |  | 
|  | 1948 | static unsigned char | 
|  | 1949 | qdio_check_siga_needs(int sch) | 
|  | 1950 | { | 
|  | 1951 | int result; | 
|  | 1952 | unsigned char qdioac; | 
|  | 1953 |  | 
|  | 1954 | struct { | 
|  | 1955 | struct chsc_header request; | 
|  | 1956 | u16 reserved1; | 
|  | 1957 | u16 first_sch; | 
|  | 1958 | u16 reserved2; | 
|  | 1959 | u16 last_sch; | 
|  | 1960 | u32 reserved3; | 
|  | 1961 | struct chsc_header response; | 
|  | 1962 | u32 reserved4; | 
|  | 1963 | u8  flags; | 
|  | 1964 | u8  reserved5; | 
|  | 1965 | u16 sch; | 
|  | 1966 | u8  qfmt; | 
|  | 1967 | u8  reserved6; | 
|  | 1968 | u8  qdioac; | 
|  | 1969 | u8  sch_class; | 
|  | 1970 | u8  reserved7; | 
|  | 1971 | u8  icnt; | 
|  | 1972 | u8  reserved8; | 
|  | 1973 | u8  ocnt; | 
|  | 1974 | } *ssqd_area; | 
|  | 1975 |  | 
|  | 1976 | ssqd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 
|  | 1977 | if (!ssqd_area) { | 
|  | 1978 | QDIO_PRINT_WARN("Could not get memory for chsc. Using all " \ | 
|  | 1979 | "SIGAs for sch x%x.\n", sch); | 
|  | 1980 | return CHSC_FLAG_SIGA_INPUT_NECESSARY || | 
|  | 1981 | CHSC_FLAG_SIGA_OUTPUT_NECESSARY || | 
|  | 1982 | CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */ | 
|  | 1983 | } | 
|  | 1984 | ssqd_area->request = (struct chsc_header) { | 
|  | 1985 | .length = 0x0010, | 
|  | 1986 | .code   = 0x0024, | 
|  | 1987 | }; | 
|  | 1988 |  | 
|  | 1989 | ssqd_area->first_sch = sch; | 
|  | 1990 | ssqd_area->last_sch = sch; | 
|  | 1991 |  | 
|  | 1992 | result=chsc(ssqd_area); | 
|  | 1993 |  | 
|  | 1994 | if (result) { | 
|  | 1995 | QDIO_PRINT_WARN("CHSC returned cc %i. Using all " \ | 
|  | 1996 | "SIGAs for sch x%x.\n", | 
|  | 1997 | result,sch); | 
|  | 1998 | qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY || | 
|  | 1999 | CHSC_FLAG_SIGA_OUTPUT_NECESSARY || | 
|  | 2000 | CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */ | 
|  | 2001 | goto out; | 
|  | 2002 | } | 
|  | 2003 |  | 
|  | 2004 | if (ssqd_area->response.code != QDIO_CHSC_RESPONSE_CODE_OK) { | 
|  | 2005 | QDIO_PRINT_WARN("response upon checking SIGA needs " \ | 
|  | 2006 | "is 0x%x. Using all SIGAs for sch x%x.\n", | 
|  | 2007 | ssqd_area->response.code, sch); | 
|  | 2008 | qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY || | 
|  | 2009 | CHSC_FLAG_SIGA_OUTPUT_NECESSARY || | 
|  | 2010 | CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */ | 
|  | 2011 | goto out; | 
|  | 2012 | } | 
|  | 2013 | if (!(ssqd_area->flags & CHSC_FLAG_QDIO_CAPABILITY) || | 
|  | 2014 | !(ssqd_area->flags & CHSC_FLAG_VALIDITY) || | 
|  | 2015 | (ssqd_area->sch != sch)) { | 
|  | 2016 | QDIO_PRINT_WARN("huh? problems checking out sch x%x... " \ | 
|  | 2017 | "using all SIGAs.\n",sch); | 
|  | 2018 | qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY | | 
|  | 2019 | CHSC_FLAG_SIGA_OUTPUT_NECESSARY | | 
|  | 2020 | CHSC_FLAG_SIGA_SYNC_NECESSARY; /* worst case */ | 
|  | 2021 | goto out; | 
|  | 2022 | } | 
|  | 2023 |  | 
|  | 2024 | qdioac = ssqd_area->qdioac; | 
|  | 2025 | out: | 
|  | 2026 | free_page ((unsigned long) ssqd_area); | 
|  | 2027 | return qdioac; | 
|  | 2028 | } | 
|  | 2029 |  | 
|  | 2030 | static unsigned int | 
|  | 2031 | tiqdio_check_chsc_availability(void) | 
|  | 2032 | { | 
|  | 2033 | char dbf_text[15]; | 
|  | 2034 |  | 
|  | 2035 | if (!css_characteristics_avail) | 
|  | 2036 | return -EIO; | 
|  | 2037 |  | 
|  | 2038 | /* Check for bit 41. */ | 
|  | 2039 | if (!css_general_characteristics.aif) { | 
|  | 2040 | QDIO_PRINT_WARN("Adapter interruption facility not " \ | 
|  | 2041 | "installed.\n"); | 
|  | 2042 | return -ENOENT; | 
|  | 2043 | } | 
|  | 2044 |  | 
|  | 2045 | /* Check for bits 107 and 108. */ | 
|  | 2046 | if (!css_chsc_characteristics.scssc || | 
|  | 2047 | !css_chsc_characteristics.scsscf) { | 
|  | 2048 | QDIO_PRINT_WARN("Set Chan Subsys. Char. & Fast-CHSCs " \ | 
|  | 2049 | "not available.\n"); | 
|  | 2050 | return -ENOENT; | 
|  | 2051 | } | 
|  | 2052 |  | 
|  | 2053 | /* Check for OSA/FCP thin interrupts (bit 67). */ | 
|  | 2054 | hydra_thinints = css_general_characteristics.aif_osa; | 
|  | 2055 | sprintf(dbf_text,"hydrati%1x", hydra_thinints); | 
|  | 2056 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2057 |  | 
|  | 2058 | /* Check for aif time delay disablement fac (bit 56). If installed, | 
|  | 2059 | * omit svs even under lpar (good point by rick again) */ | 
|  | 2060 | omit_svs = css_general_characteristics.aif_tdd; | 
|  | 2061 | sprintf(dbf_text,"omitsvs%1x", omit_svs); | 
|  | 2062 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2063 | return 0; | 
|  | 2064 | } | 
|  | 2065 |  | 
|  | 2066 |  | 
|  | 2067 | static unsigned int | 
|  | 2068 | tiqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero) | 
|  | 2069 | { | 
|  | 2070 | unsigned long real_addr_local_summary_bit; | 
|  | 2071 | unsigned long real_addr_dev_st_chg_ind; | 
|  | 2072 | void *ptr; | 
|  | 2073 | char dbf_text[15]; | 
|  | 2074 |  | 
|  | 2075 | unsigned int resp_code; | 
|  | 2076 | int result; | 
|  | 2077 |  | 
|  | 2078 | struct { | 
|  | 2079 | struct chsc_header request; | 
|  | 2080 | u16 operation_code; | 
|  | 2081 | u16 reserved1; | 
|  | 2082 | u32 reserved2; | 
|  | 2083 | u32 reserved3; | 
|  | 2084 | u64 summary_indicator_addr; | 
|  | 2085 | u64 subchannel_indicator_addr; | 
|  | 2086 | u32 ks:4; | 
|  | 2087 | u32 kc:4; | 
|  | 2088 | u32 reserved4:21; | 
|  | 2089 | u32 isc:3; | 
|  | 2090 | u32 word_with_d_bit; | 
|  | 2091 | /* set to 0x10000000 to enable | 
|  | 2092 | * time delay disablement facility */ | 
|  | 2093 | u32 reserved5; | 
|  | 2094 | u32 subsystem_id; | 
|  | 2095 | u32 reserved6[1004]; | 
|  | 2096 | struct chsc_header response; | 
|  | 2097 | u32 reserved7; | 
|  | 2098 | } *scssc_area; | 
|  | 2099 |  | 
|  | 2100 | if (!irq_ptr->is_thinint_irq) | 
|  | 2101 | return -ENODEV; | 
|  | 2102 |  | 
|  | 2103 | if (reset_to_zero) { | 
|  | 2104 | real_addr_local_summary_bit=0; | 
|  | 2105 | real_addr_dev_st_chg_ind=0; | 
|  | 2106 | } else { | 
|  | 2107 | real_addr_local_summary_bit= | 
|  | 2108 | virt_to_phys((volatile void *)indicators); | 
|  | 2109 | real_addr_dev_st_chg_ind= | 
|  | 2110 | virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind); | 
|  | 2111 | } | 
|  | 2112 |  | 
|  | 2113 | scssc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 
|  | 2114 | if (!scssc_area) { | 
|  | 2115 | QDIO_PRINT_WARN("No memory for setting indicators on " \ | 
|  | 2116 | "subchannel x%x.\n", irq_ptr->irq); | 
|  | 2117 | return -ENOMEM; | 
|  | 2118 | } | 
|  | 2119 | scssc_area->request = (struct chsc_header) { | 
|  | 2120 | .length = 0x0fe0, | 
|  | 2121 | .code   = 0x0021, | 
|  | 2122 | }; | 
|  | 2123 | scssc_area->operation_code = 0; | 
|  | 2124 |  | 
|  | 2125 | scssc_area->summary_indicator_addr = real_addr_local_summary_bit; | 
|  | 2126 | scssc_area->subchannel_indicator_addr = real_addr_dev_st_chg_ind; | 
|  | 2127 | scssc_area->ks = QDIO_STORAGE_KEY; | 
|  | 2128 | scssc_area->kc = QDIO_STORAGE_KEY; | 
|  | 2129 | scssc_area->isc = TIQDIO_THININT_ISC; | 
|  | 2130 | scssc_area->subsystem_id = (1<<16) + irq_ptr->irq; | 
|  | 2131 | /* enables the time delay disablement facility. Don't care | 
|  | 2132 | * whether it is really there (i.e. we haven't checked for | 
|  | 2133 | * it) */ | 
|  | 2134 | if (css_general_characteristics.aif_tdd) | 
|  | 2135 | scssc_area->word_with_d_bit = 0x10000000; | 
|  | 2136 | else | 
|  | 2137 | QDIO_PRINT_WARN("Time delay disablement facility " \ | 
|  | 2138 | "not available\n"); | 
|  | 2139 |  | 
|  | 2140 |  | 
|  | 2141 |  | 
|  | 2142 | result = chsc(scssc_area); | 
|  | 2143 | if (result) { | 
|  | 2144 | QDIO_PRINT_WARN("could not set indicators on irq x%x, " \ | 
|  | 2145 | "cc=%i.\n",irq_ptr->irq,result); | 
|  | 2146 | result = -EIO; | 
|  | 2147 | goto out; | 
|  | 2148 | } | 
|  | 2149 |  | 
|  | 2150 | resp_code = scssc_area->response.code; | 
|  | 2151 | if (resp_code!=QDIO_CHSC_RESPONSE_CODE_OK) { | 
|  | 2152 | QDIO_PRINT_WARN("response upon setting indicators " \ | 
|  | 2153 | "is 0x%x.\n",resp_code); | 
|  | 2154 | sprintf(dbf_text,"sidR%4x",resp_code); | 
|  | 2155 | QDIO_DBF_TEXT1(0,trace,dbf_text); | 
|  | 2156 | QDIO_DBF_TEXT1(0,setup,dbf_text); | 
|  | 2157 | ptr=&scssc_area->response; | 
|  | 2158 | QDIO_DBF_HEX2(1,setup,&ptr,QDIO_DBF_SETUP_LEN); | 
|  | 2159 | result = -EIO; | 
|  | 2160 | goto out; | 
|  | 2161 | } | 
|  | 2162 |  | 
|  | 2163 | QDIO_DBF_TEXT2(0,setup,"setscind"); | 
|  | 2164 | QDIO_DBF_HEX2(0,setup,&real_addr_local_summary_bit, | 
|  | 2165 | sizeof(unsigned long)); | 
|  | 2166 | QDIO_DBF_HEX2(0,setup,&real_addr_dev_st_chg_ind,sizeof(unsigned long)); | 
|  | 2167 | result = 0; | 
|  | 2168 | out: | 
|  | 2169 | free_page ((unsigned long) scssc_area); | 
|  | 2170 | return result; | 
|  | 2171 |  | 
|  | 2172 | } | 
|  | 2173 |  | 
|  | 2174 | static unsigned int | 
|  | 2175 | tiqdio_set_delay_target(struct qdio_irq *irq_ptr, unsigned long delay_target) | 
|  | 2176 | { | 
|  | 2177 | unsigned int resp_code; | 
|  | 2178 | int result; | 
|  | 2179 | void *ptr; | 
|  | 2180 | char dbf_text[15]; | 
|  | 2181 |  | 
|  | 2182 | struct { | 
|  | 2183 | struct chsc_header request; | 
|  | 2184 | u16 operation_code; | 
|  | 2185 | u16 reserved1; | 
|  | 2186 | u32 reserved2; | 
|  | 2187 | u32 reserved3; | 
|  | 2188 | u32 reserved4[2]; | 
|  | 2189 | u32 delay_target; | 
|  | 2190 | u32 reserved5[1009]; | 
|  | 2191 | struct chsc_header response; | 
|  | 2192 | u32 reserved6; | 
|  | 2193 | } *scsscf_area; | 
|  | 2194 |  | 
|  | 2195 | if (!irq_ptr->is_thinint_irq) | 
|  | 2196 | return -ENODEV; | 
|  | 2197 |  | 
|  | 2198 | scsscf_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); | 
|  | 2199 | if (!scsscf_area) { | 
|  | 2200 | QDIO_PRINT_WARN("No memory for setting delay target on " \ | 
|  | 2201 | "subchannel x%x.\n", irq_ptr->irq); | 
|  | 2202 | return -ENOMEM; | 
|  | 2203 | } | 
|  | 2204 | scsscf_area->request = (struct chsc_header) { | 
|  | 2205 | .length = 0x0fe0, | 
|  | 2206 | .code   = 0x1027, | 
|  | 2207 | }; | 
|  | 2208 |  | 
|  | 2209 | scsscf_area->delay_target = delay_target<<16; | 
|  | 2210 |  | 
|  | 2211 | result=chsc(scsscf_area); | 
|  | 2212 | if (result) { | 
|  | 2213 | QDIO_PRINT_WARN("could not set delay target on irq x%x, " \ | 
|  | 2214 | "cc=%i. Continuing.\n",irq_ptr->irq,result); | 
|  | 2215 | result = -EIO; | 
|  | 2216 | goto out; | 
|  | 2217 | } | 
|  | 2218 |  | 
|  | 2219 | resp_code = scsscf_area->response.code; | 
|  | 2220 | if (resp_code!=QDIO_CHSC_RESPONSE_CODE_OK) { | 
|  | 2221 | QDIO_PRINT_WARN("response upon setting delay target " \ | 
|  | 2222 | "is 0x%x. Continuing.\n",resp_code); | 
|  | 2223 | sprintf(dbf_text,"sdtR%4x",resp_code); | 
|  | 2224 | QDIO_DBF_TEXT1(0,trace,dbf_text); | 
|  | 2225 | QDIO_DBF_TEXT1(0,setup,dbf_text); | 
|  | 2226 | ptr=&scsscf_area->response; | 
|  | 2227 | QDIO_DBF_HEX2(1,trace,&ptr,QDIO_DBF_TRACE_LEN); | 
|  | 2228 | } | 
|  | 2229 | QDIO_DBF_TEXT2(0,trace,"delytrgt"); | 
|  | 2230 | QDIO_DBF_HEX2(0,trace,&delay_target,sizeof(unsigned long)); | 
|  | 2231 | result = 0; /* not critical */ | 
|  | 2232 | out: | 
|  | 2233 | free_page ((unsigned long) scsscf_area); | 
|  | 2234 | return result; | 
|  | 2235 | } | 
|  | 2236 |  | 
|  | 2237 | int | 
|  | 2238 | qdio_cleanup(struct ccw_device *cdev, int how) | 
|  | 2239 | { | 
|  | 2240 | struct qdio_irq *irq_ptr; | 
|  | 2241 | char dbf_text[15]; | 
|  | 2242 | int rc; | 
|  | 2243 |  | 
|  | 2244 | irq_ptr = cdev->private->qdio_data; | 
|  | 2245 | if (!irq_ptr) | 
|  | 2246 | return -ENODEV; | 
|  | 2247 |  | 
|  | 2248 | sprintf(dbf_text,"qcln%4x",irq_ptr->irq); | 
|  | 2249 | QDIO_DBF_TEXT1(0,trace,dbf_text); | 
|  | 2250 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2251 |  | 
|  | 2252 | rc = qdio_shutdown(cdev, how); | 
|  | 2253 | if ((rc == 0) || (rc == -EINPROGRESS)) | 
|  | 2254 | rc = qdio_free(cdev); | 
|  | 2255 | return rc; | 
|  | 2256 | } | 
|  | 2257 |  | 
|  | 2258 | int | 
|  | 2259 | qdio_shutdown(struct ccw_device *cdev, int how) | 
|  | 2260 | { | 
|  | 2261 | struct qdio_irq *irq_ptr; | 
|  | 2262 | int i; | 
|  | 2263 | int result = 0; | 
|  | 2264 | int rc; | 
|  | 2265 | unsigned long flags; | 
|  | 2266 | int timeout; | 
|  | 2267 | char dbf_text[15]; | 
|  | 2268 |  | 
|  | 2269 | irq_ptr = cdev->private->qdio_data; | 
|  | 2270 | if (!irq_ptr) | 
|  | 2271 | return -ENODEV; | 
|  | 2272 |  | 
|  | 2273 | down(&irq_ptr->setting_up_sema); | 
|  | 2274 |  | 
|  | 2275 | sprintf(dbf_text,"qsqs%4x",irq_ptr->irq); | 
|  | 2276 | QDIO_DBF_TEXT1(0,trace,dbf_text); | 
|  | 2277 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2278 |  | 
|  | 2279 | /* mark all qs as uninteresting */ | 
|  | 2280 | for (i=0;i<irq_ptr->no_input_qs;i++) | 
|  | 2281 | atomic_set(&irq_ptr->input_qs[i]->is_in_shutdown,1); | 
|  | 2282 |  | 
|  | 2283 | for (i=0;i<irq_ptr->no_output_qs;i++) | 
|  | 2284 | atomic_set(&irq_ptr->output_qs[i]->is_in_shutdown,1); | 
|  | 2285 |  | 
|  | 2286 | tasklet_kill(&tiqdio_tasklet); | 
|  | 2287 |  | 
|  | 2288 | for (i=0;i<irq_ptr->no_input_qs;i++) { | 
|  | 2289 | qdio_unmark_q(irq_ptr->input_qs[i]); | 
|  | 2290 | tasklet_kill(&irq_ptr->input_qs[i]->tasklet); | 
|  | 2291 | wait_event_interruptible_timeout(cdev->private->wait_q, | 
|  | 2292 | !atomic_read(&irq_ptr-> | 
|  | 2293 | input_qs[i]-> | 
|  | 2294 | use_count), | 
|  | 2295 | QDIO_NO_USE_COUNT_TIMEOUT); | 
|  | 2296 | if (atomic_read(&irq_ptr->input_qs[i]->use_count)) | 
|  | 2297 | result=-EINPROGRESS; | 
|  | 2298 | } | 
|  | 2299 |  | 
|  | 2300 | for (i=0;i<irq_ptr->no_output_qs;i++) { | 
|  | 2301 | tasklet_kill(&irq_ptr->output_qs[i]->tasklet); | 
|  | 2302 | wait_event_interruptible_timeout(cdev->private->wait_q, | 
|  | 2303 | !atomic_read(&irq_ptr-> | 
|  | 2304 | output_qs[i]-> | 
|  | 2305 | use_count), | 
|  | 2306 | QDIO_NO_USE_COUNT_TIMEOUT); | 
|  | 2307 | if (atomic_read(&irq_ptr->output_qs[i]->use_count)) | 
|  | 2308 | result=-EINPROGRESS; | 
|  | 2309 | } | 
|  | 2310 |  | 
|  | 2311 | /* cleanup subchannel */ | 
|  | 2312 | spin_lock_irqsave(get_ccwdev_lock(cdev),flags); | 
|  | 2313 | if (how&QDIO_FLAG_CLEANUP_USING_CLEAR) { | 
|  | 2314 | rc = ccw_device_clear(cdev, QDIO_DOING_CLEANUP); | 
|  | 2315 | timeout=QDIO_CLEANUP_CLEAR_TIMEOUT; | 
|  | 2316 | } else if (how&QDIO_FLAG_CLEANUP_USING_HALT) { | 
|  | 2317 | rc = ccw_device_halt(cdev, QDIO_DOING_CLEANUP); | 
|  | 2318 | timeout=QDIO_CLEANUP_HALT_TIMEOUT; | 
|  | 2319 | } else { /* default behaviour */ | 
|  | 2320 | rc = ccw_device_halt(cdev, QDIO_DOING_CLEANUP); | 
|  | 2321 | timeout=QDIO_CLEANUP_HALT_TIMEOUT; | 
|  | 2322 | } | 
|  | 2323 | if (rc == -ENODEV) { | 
|  | 2324 | /* No need to wait for device no longer present. */ | 
|  | 2325 | qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); | 
|  | 2326 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | 
|  | 2327 | } else if (((void *)cdev->handler != (void *)qdio_handler) && rc == 0) { | 
|  | 2328 | /* | 
|  | 2329 | * Whoever put another handler there, has to cope with the | 
|  | 2330 | * interrupt theirself. Might happen if qdio_shutdown was | 
|  | 2331 | * called on already shutdown queues, but this shouldn't have | 
|  | 2332 | * bad side effects. | 
|  | 2333 | */ | 
|  | 2334 | qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); | 
|  | 2335 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | 
|  | 2336 | } else if (rc == 0) { | 
|  | 2337 | qdio_set_state(irq_ptr, QDIO_IRQ_STATE_CLEANUP); | 
|  | 2338 | ccw_device_set_timeout(cdev, timeout); | 
|  | 2339 | spin_unlock_irqrestore(get_ccwdev_lock(cdev),flags); | 
|  | 2340 |  | 
|  | 2341 | wait_event(cdev->private->wait_q, | 
|  | 2342 | irq_ptr->state == QDIO_IRQ_STATE_INACTIVE || | 
|  | 2343 | irq_ptr->state == QDIO_IRQ_STATE_ERR); | 
|  | 2344 | } else { | 
|  | 2345 | QDIO_PRINT_INFO("ccw_device_{halt,clear} returned %d for " | 
|  | 2346 | "device %s\n", result, cdev->dev.bus_id); | 
|  | 2347 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | 
|  | 2348 | result = rc; | 
|  | 2349 | goto out; | 
|  | 2350 | } | 
|  | 2351 | if (irq_ptr->is_thinint_irq) { | 
|  | 2352 | qdio_put_indicator((__u32*)irq_ptr->dev_st_chg_ind); | 
|  | 2353 | tiqdio_set_subchannel_ind(irq_ptr,1); | 
|  | 2354 | /* reset adapter interrupt indicators */ | 
|  | 2355 | } | 
|  | 2356 |  | 
|  | 2357 | /* exchange int handlers, if necessary */ | 
|  | 2358 | if ((void*)cdev->handler == (void*)qdio_handler) | 
|  | 2359 | cdev->handler=irq_ptr->original_int_handler; | 
|  | 2360 |  | 
|  | 2361 | /* Ignore errors. */ | 
|  | 2362 | qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); | 
|  | 2363 | ccw_device_set_timeout(cdev, 0); | 
|  | 2364 | out: | 
|  | 2365 | up(&irq_ptr->setting_up_sema); | 
|  | 2366 | return result; | 
|  | 2367 | } | 
|  | 2368 |  | 
|  | 2369 | int | 
|  | 2370 | qdio_free(struct ccw_device *cdev) | 
|  | 2371 | { | 
|  | 2372 | struct qdio_irq *irq_ptr; | 
|  | 2373 | char dbf_text[15]; | 
|  | 2374 |  | 
|  | 2375 | irq_ptr = cdev->private->qdio_data; | 
|  | 2376 | if (!irq_ptr) | 
|  | 2377 | return -ENODEV; | 
|  | 2378 |  | 
|  | 2379 | down(&irq_ptr->setting_up_sema); | 
|  | 2380 |  | 
|  | 2381 | sprintf(dbf_text,"qfqs%4x",irq_ptr->irq); | 
|  | 2382 | QDIO_DBF_TEXT1(0,trace,dbf_text); | 
|  | 2383 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2384 |  | 
|  | 2385 | cdev->private->qdio_data = 0; | 
|  | 2386 |  | 
|  | 2387 | up(&irq_ptr->setting_up_sema); | 
|  | 2388 |  | 
|  | 2389 | qdio_release_irq_memory(irq_ptr); | 
|  | 2390 | module_put(THIS_MODULE); | 
|  | 2391 | return 0; | 
|  | 2392 | } | 
|  | 2393 |  | 
|  | 2394 | static inline void | 
|  | 2395 | qdio_allocate_do_dbf(struct qdio_initialize *init_data) | 
|  | 2396 | { | 
|  | 2397 | char dbf_text[20]; /* if a printf printed out more than 8 chars */ | 
|  | 2398 |  | 
|  | 2399 | sprintf(dbf_text,"qfmt:%x",init_data->q_format); | 
|  | 2400 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2401 | QDIO_DBF_HEX0(0,setup,init_data->adapter_name,8); | 
|  | 2402 | sprintf(dbf_text,"qpff%4x",init_data->qib_param_field_format); | 
|  | 2403 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2404 | QDIO_DBF_HEX0(0,setup,&init_data->qib_param_field,sizeof(char*)); | 
|  | 2405 | QDIO_DBF_HEX0(0,setup,&init_data->input_slib_elements,sizeof(long*)); | 
|  | 2406 | QDIO_DBF_HEX0(0,setup,&init_data->output_slib_elements,sizeof(long*)); | 
|  | 2407 | sprintf(dbf_text,"miit%4x",init_data->min_input_threshold); | 
|  | 2408 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2409 | sprintf(dbf_text,"mait%4x",init_data->max_input_threshold); | 
|  | 2410 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2411 | sprintf(dbf_text,"miot%4x",init_data->min_output_threshold); | 
|  | 2412 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2413 | sprintf(dbf_text,"maot%4x",init_data->max_output_threshold); | 
|  | 2414 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2415 | sprintf(dbf_text,"niq:%4x",init_data->no_input_qs); | 
|  | 2416 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2417 | sprintf(dbf_text,"noq:%4x",init_data->no_output_qs); | 
|  | 2418 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2419 | QDIO_DBF_HEX0(0,setup,&init_data->input_handler,sizeof(void*)); | 
|  | 2420 | QDIO_DBF_HEX0(0,setup,&init_data->output_handler,sizeof(void*)); | 
|  | 2421 | QDIO_DBF_HEX0(0,setup,&init_data->int_parm,sizeof(long)); | 
|  | 2422 | QDIO_DBF_HEX0(0,setup,&init_data->flags,sizeof(long)); | 
|  | 2423 | QDIO_DBF_HEX0(0,setup,&init_data->input_sbal_addr_array,sizeof(void*)); | 
|  | 2424 | QDIO_DBF_HEX0(0,setup,&init_data->output_sbal_addr_array,sizeof(void*)); | 
|  | 2425 | } | 
|  | 2426 |  | 
|  | 2427 | static inline void | 
|  | 2428 | qdio_allocate_fill_input_desc(struct qdio_irq *irq_ptr, int i, int iqfmt) | 
|  | 2429 | { | 
|  | 2430 | irq_ptr->input_qs[i]->is_iqdio_q = iqfmt; | 
|  | 2431 | irq_ptr->input_qs[i]->is_thinint_q = irq_ptr->is_thinint_irq; | 
|  | 2432 |  | 
|  | 2433 | irq_ptr->qdr->qdf0[i].sliba=(unsigned long)(irq_ptr->input_qs[i]->slib); | 
|  | 2434 |  | 
|  | 2435 | irq_ptr->qdr->qdf0[i].sla=(unsigned long)(irq_ptr->input_qs[i]->sl); | 
|  | 2436 |  | 
|  | 2437 | irq_ptr->qdr->qdf0[i].slsba= | 
|  | 2438 | (unsigned long)(&irq_ptr->input_qs[i]->slsb.acc.val[0]); | 
|  | 2439 |  | 
|  | 2440 | irq_ptr->qdr->qdf0[i].akey=QDIO_STORAGE_KEY; | 
|  | 2441 | irq_ptr->qdr->qdf0[i].bkey=QDIO_STORAGE_KEY; | 
|  | 2442 | irq_ptr->qdr->qdf0[i].ckey=QDIO_STORAGE_KEY; | 
|  | 2443 | irq_ptr->qdr->qdf0[i].dkey=QDIO_STORAGE_KEY; | 
|  | 2444 | } | 
|  | 2445 |  | 
|  | 2446 | static inline void | 
|  | 2447 | qdio_allocate_fill_output_desc(struct qdio_irq *irq_ptr, int i, | 
|  | 2448 | int j, int iqfmt) | 
|  | 2449 | { | 
|  | 2450 | irq_ptr->output_qs[i]->is_iqdio_q = iqfmt; | 
|  | 2451 | irq_ptr->output_qs[i]->is_thinint_q = irq_ptr->is_thinint_irq; | 
|  | 2452 |  | 
|  | 2453 | irq_ptr->qdr->qdf0[i+j].sliba=(unsigned long)(irq_ptr->output_qs[i]->slib); | 
|  | 2454 |  | 
|  | 2455 | irq_ptr->qdr->qdf0[i+j].sla=(unsigned long)(irq_ptr->output_qs[i]->sl); | 
|  | 2456 |  | 
|  | 2457 | irq_ptr->qdr->qdf0[i+j].slsba= | 
|  | 2458 | (unsigned long)(&irq_ptr->output_qs[i]->slsb.acc.val[0]); | 
|  | 2459 |  | 
|  | 2460 | irq_ptr->qdr->qdf0[i+j].akey=QDIO_STORAGE_KEY; | 
|  | 2461 | irq_ptr->qdr->qdf0[i+j].bkey=QDIO_STORAGE_KEY; | 
|  | 2462 | irq_ptr->qdr->qdf0[i+j].ckey=QDIO_STORAGE_KEY; | 
|  | 2463 | irq_ptr->qdr->qdf0[i+j].dkey=QDIO_STORAGE_KEY; | 
|  | 2464 | } | 
|  | 2465 |  | 
|  | 2466 |  | 
|  | 2467 | static inline void | 
|  | 2468 | qdio_initialize_set_siga_flags_input(struct qdio_irq *irq_ptr) | 
|  | 2469 | { | 
|  | 2470 | int i; | 
|  | 2471 |  | 
|  | 2472 | for (i=0;i<irq_ptr->no_input_qs;i++) { | 
|  | 2473 | irq_ptr->input_qs[i]->siga_sync= | 
|  | 2474 | irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_NECESSARY; | 
|  | 2475 | irq_ptr->input_qs[i]->siga_in= | 
|  | 2476 | irq_ptr->qdioac&CHSC_FLAG_SIGA_INPUT_NECESSARY; | 
|  | 2477 | irq_ptr->input_qs[i]->siga_out= | 
|  | 2478 | irq_ptr->qdioac&CHSC_FLAG_SIGA_OUTPUT_NECESSARY; | 
|  | 2479 | irq_ptr->input_qs[i]->siga_sync_done_on_thinints= | 
|  | 2480 | irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS; | 
|  | 2481 | irq_ptr->input_qs[i]->hydra_gives_outbound_pcis= | 
|  | 2482 | irq_ptr->hydra_gives_outbound_pcis; | 
|  | 2483 | irq_ptr->input_qs[i]->siga_sync_done_on_outb_tis= | 
|  | 2484 | ((irq_ptr->qdioac& | 
|  | 2485 | (CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS| | 
|  | 2486 | CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS))== | 
|  | 2487 | (CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS| | 
|  | 2488 | CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS)); | 
|  | 2489 |  | 
|  | 2490 | } | 
|  | 2491 | } | 
|  | 2492 |  | 
|  | 2493 | static inline void | 
|  | 2494 | qdio_initialize_set_siga_flags_output(struct qdio_irq *irq_ptr) | 
|  | 2495 | { | 
|  | 2496 | int i; | 
|  | 2497 |  | 
|  | 2498 | for (i=0;i<irq_ptr->no_output_qs;i++) { | 
|  | 2499 | irq_ptr->output_qs[i]->siga_sync= | 
|  | 2500 | irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_NECESSARY; | 
|  | 2501 | irq_ptr->output_qs[i]->siga_in= | 
|  | 2502 | irq_ptr->qdioac&CHSC_FLAG_SIGA_INPUT_NECESSARY; | 
|  | 2503 | irq_ptr->output_qs[i]->siga_out= | 
|  | 2504 | irq_ptr->qdioac&CHSC_FLAG_SIGA_OUTPUT_NECESSARY; | 
|  | 2505 | irq_ptr->output_qs[i]->siga_sync_done_on_thinints= | 
|  | 2506 | irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS; | 
|  | 2507 | irq_ptr->output_qs[i]->hydra_gives_outbound_pcis= | 
|  | 2508 | irq_ptr->hydra_gives_outbound_pcis; | 
|  | 2509 | irq_ptr->output_qs[i]->siga_sync_done_on_outb_tis= | 
|  | 2510 | ((irq_ptr->qdioac& | 
|  | 2511 | (CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS| | 
|  | 2512 | CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS))== | 
|  | 2513 | (CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS| | 
|  | 2514 | CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS)); | 
|  | 2515 |  | 
|  | 2516 | } | 
|  | 2517 | } | 
|  | 2518 |  | 
|  | 2519 | static inline int | 
|  | 2520 | qdio_establish_irq_check_for_errors(struct ccw_device *cdev, int cstat, | 
|  | 2521 | int dstat) | 
|  | 2522 | { | 
|  | 2523 | char dbf_text[15]; | 
|  | 2524 | struct qdio_irq *irq_ptr; | 
|  | 2525 |  | 
|  | 2526 | irq_ptr = cdev->private->qdio_data; | 
|  | 2527 |  | 
|  | 2528 | if (cstat || (dstat & ~(DEV_STAT_CHN_END|DEV_STAT_DEV_END))) { | 
|  | 2529 | sprintf(dbf_text,"ick1%4x",irq_ptr->irq); | 
|  | 2530 | QDIO_DBF_TEXT2(1,trace,dbf_text); | 
|  | 2531 | QDIO_DBF_HEX2(0,trace,&dstat,sizeof(int)); | 
|  | 2532 | QDIO_DBF_HEX2(0,trace,&cstat,sizeof(int)); | 
|  | 2533 | QDIO_PRINT_ERR("received check condition on establish " \ | 
|  | 2534 | "queues on irq 0x%x (cs=x%x, ds=x%x).\n", | 
|  | 2535 | irq_ptr->irq,cstat,dstat); | 
|  | 2536 | qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ERR); | 
|  | 2537 | } | 
|  | 2538 |  | 
|  | 2539 | if (!(dstat & DEV_STAT_DEV_END)) { | 
|  | 2540 | QDIO_DBF_TEXT2(1,setup,"eq:no de"); | 
|  | 2541 | QDIO_DBF_HEX2(0,setup,&dstat, sizeof(dstat)); | 
|  | 2542 | QDIO_DBF_HEX2(0,setup,&cstat, sizeof(cstat)); | 
|  | 2543 | QDIO_PRINT_ERR("establish queues on irq %04x: didn't get " | 
|  | 2544 | "device end: dstat=%02x, cstat=%02x\n", | 
|  | 2545 | irq_ptr->irq, dstat, cstat); | 
|  | 2546 | qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); | 
|  | 2547 | return 1; | 
|  | 2548 | } | 
|  | 2549 |  | 
|  | 2550 | if (dstat & ~(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) { | 
|  | 2551 | QDIO_DBF_TEXT2(1,setup,"eq:badio"); | 
|  | 2552 | QDIO_DBF_HEX2(0,setup,&dstat, sizeof(dstat)); | 
|  | 2553 | QDIO_DBF_HEX2(0,setup,&cstat, sizeof(cstat)); | 
|  | 2554 | QDIO_PRINT_ERR("establish queues on irq %04x: got " | 
|  | 2555 | "the following devstat: dstat=%02x, " | 
|  | 2556 | "cstat=%02x\n", | 
|  | 2557 | irq_ptr->irq, dstat, cstat); | 
|  | 2558 | qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); | 
|  | 2559 | return 1; | 
|  | 2560 | } | 
|  | 2561 | return 0; | 
|  | 2562 | } | 
|  | 2563 |  | 
|  | 2564 | static void | 
|  | 2565 | qdio_establish_handle_irq(struct ccw_device *cdev, int cstat, int dstat) | 
|  | 2566 | { | 
|  | 2567 | struct qdio_irq *irq_ptr; | 
|  | 2568 | char dbf_text[15]; | 
|  | 2569 |  | 
|  | 2570 | irq_ptr = cdev->private->qdio_data; | 
|  | 2571 |  | 
|  | 2572 | sprintf(dbf_text,"qehi%4x",cdev->private->irq); | 
|  | 2573 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2574 | QDIO_DBF_TEXT0(0,trace,dbf_text); | 
|  | 2575 |  | 
|  | 2576 | if (qdio_establish_irq_check_for_errors(cdev, cstat, dstat)) { | 
|  | 2577 | ccw_device_set_timeout(cdev, 0); | 
|  | 2578 | return; | 
|  | 2579 | } | 
|  | 2580 |  | 
|  | 2581 | qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ESTABLISHED); | 
|  | 2582 | ccw_device_set_timeout(cdev, 0); | 
|  | 2583 | } | 
|  | 2584 |  | 
|  | 2585 | int | 
|  | 2586 | qdio_initialize(struct qdio_initialize *init_data) | 
|  | 2587 | { | 
|  | 2588 | int rc; | 
|  | 2589 | char dbf_text[15]; | 
|  | 2590 |  | 
|  | 2591 | sprintf(dbf_text,"qini%4x",init_data->cdev->private->irq); | 
|  | 2592 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2593 | QDIO_DBF_TEXT0(0,trace,dbf_text); | 
|  | 2594 |  | 
|  | 2595 | rc = qdio_allocate(init_data); | 
|  | 2596 | if (rc == 0) { | 
|  | 2597 | rc = qdio_establish(init_data); | 
|  | 2598 | if (rc != 0) | 
|  | 2599 | qdio_free(init_data->cdev); | 
|  | 2600 | } | 
|  | 2601 |  | 
|  | 2602 | return rc; | 
|  | 2603 | } | 
|  | 2604 |  | 
|  | 2605 |  | 
|  | 2606 | int | 
|  | 2607 | qdio_allocate(struct qdio_initialize *init_data) | 
|  | 2608 | { | 
|  | 2609 | struct qdio_irq *irq_ptr; | 
|  | 2610 | char dbf_text[15]; | 
|  | 2611 |  | 
|  | 2612 | sprintf(dbf_text,"qalc%4x",init_data->cdev->private->irq); | 
|  | 2613 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2614 | QDIO_DBF_TEXT0(0,trace,dbf_text); | 
|  | 2615 | if ( (init_data->no_input_qs>QDIO_MAX_QUEUES_PER_IRQ) || | 
|  | 2616 | (init_data->no_output_qs>QDIO_MAX_QUEUES_PER_IRQ) || | 
|  | 2617 | ((init_data->no_input_qs) && (!init_data->input_handler)) || | 
|  | 2618 | ((init_data->no_output_qs) && (!init_data->output_handler)) ) | 
|  | 2619 | return -EINVAL; | 
|  | 2620 |  | 
|  | 2621 | if (!init_data->input_sbal_addr_array) | 
|  | 2622 | return -EINVAL; | 
|  | 2623 |  | 
|  | 2624 | if (!init_data->output_sbal_addr_array) | 
|  | 2625 | return -EINVAL; | 
|  | 2626 |  | 
|  | 2627 | qdio_allocate_do_dbf(init_data); | 
|  | 2628 |  | 
|  | 2629 | /* create irq */ | 
|  | 2630 | irq_ptr=kmalloc(sizeof(struct qdio_irq), GFP_KERNEL | GFP_DMA); | 
|  | 2631 |  | 
|  | 2632 | QDIO_DBF_TEXT0(0,setup,"irq_ptr:"); | 
|  | 2633 | QDIO_DBF_HEX0(0,setup,&irq_ptr,sizeof(void*)); | 
|  | 2634 |  | 
|  | 2635 | if (!irq_ptr) { | 
|  | 2636 | QDIO_PRINT_ERR("kmalloc of irq_ptr failed!\n"); | 
|  | 2637 | return -ENOMEM; | 
|  | 2638 | } | 
|  | 2639 |  | 
|  | 2640 | memset(irq_ptr,0,sizeof(struct qdio_irq)); | 
|  | 2641 |  | 
|  | 2642 | init_MUTEX(&irq_ptr->setting_up_sema); | 
|  | 2643 |  | 
|  | 2644 | /* QDR must be in DMA area since CCW data address is only 32 bit */ | 
|  | 2645 | irq_ptr->qdr=kmalloc(sizeof(struct qdr), GFP_KERNEL | GFP_DMA); | 
|  | 2646 | if (!(irq_ptr->qdr)) { | 
|  | 2647 | kfree(irq_ptr); | 
|  | 2648 | QDIO_PRINT_ERR("kmalloc of irq_ptr->qdr failed!\n"); | 
|  | 2649 | return -ENOMEM; | 
|  | 2650 | } | 
|  | 2651 | QDIO_DBF_TEXT0(0,setup,"qdr:"); | 
|  | 2652 | QDIO_DBF_HEX0(0,setup,&irq_ptr->qdr,sizeof(void*)); | 
|  | 2653 |  | 
|  | 2654 | if (qdio_alloc_qs(irq_ptr, | 
|  | 2655 | init_data->no_input_qs, | 
|  | 2656 | init_data->no_output_qs)) { | 
|  | 2657 | qdio_release_irq_memory(irq_ptr); | 
|  | 2658 | return -ENOMEM; | 
|  | 2659 | } | 
|  | 2660 |  | 
|  | 2661 | init_data->cdev->private->qdio_data = irq_ptr; | 
|  | 2662 |  | 
|  | 2663 | qdio_set_state(irq_ptr,QDIO_IRQ_STATE_INACTIVE); | 
|  | 2664 |  | 
|  | 2665 | return 0; | 
|  | 2666 | } | 
|  | 2667 |  | 
|  | 2668 | int qdio_fill_irq(struct qdio_initialize *init_data) | 
|  | 2669 | { | 
|  | 2670 | int i; | 
|  | 2671 | char dbf_text[15]; | 
|  | 2672 | struct ciw *ciw; | 
|  | 2673 | int is_iqdio; | 
|  | 2674 | struct qdio_irq *irq_ptr; | 
|  | 2675 |  | 
|  | 2676 | irq_ptr = init_data->cdev->private->qdio_data; | 
|  | 2677 |  | 
|  | 2678 | memset(irq_ptr,0,((char*)&irq_ptr->qdr)-((char*)irq_ptr)); | 
|  | 2679 |  | 
|  | 2680 | /* wipes qib.ac, required by ar7063 */ | 
|  | 2681 | memset(irq_ptr->qdr,0,sizeof(struct qdr)); | 
|  | 2682 |  | 
|  | 2683 | irq_ptr->int_parm=init_data->int_parm; | 
|  | 2684 |  | 
|  | 2685 | irq_ptr->irq = init_data->cdev->private->irq; | 
|  | 2686 | irq_ptr->no_input_qs=init_data->no_input_qs; | 
|  | 2687 | irq_ptr->no_output_qs=init_data->no_output_qs; | 
|  | 2688 |  | 
|  | 2689 | if (init_data->q_format==QDIO_IQDIO_QFMT) { | 
|  | 2690 | irq_ptr->is_iqdio_irq=1; | 
|  | 2691 | irq_ptr->is_thinint_irq=1; | 
|  | 2692 | } else { | 
|  | 2693 | irq_ptr->is_iqdio_irq=0; | 
|  | 2694 | irq_ptr->is_thinint_irq=hydra_thinints; | 
|  | 2695 | } | 
|  | 2696 | sprintf(dbf_text,"is_i_t%1x%1x", | 
|  | 2697 | irq_ptr->is_iqdio_irq,irq_ptr->is_thinint_irq); | 
|  | 2698 | QDIO_DBF_TEXT2(0,setup,dbf_text); | 
|  | 2699 |  | 
|  | 2700 | if (irq_ptr->is_thinint_irq) { | 
|  | 2701 | irq_ptr->dev_st_chg_ind=qdio_get_indicator(); | 
|  | 2702 | QDIO_DBF_HEX1(0,setup,&irq_ptr->dev_st_chg_ind,sizeof(void*)); | 
|  | 2703 | if (!irq_ptr->dev_st_chg_ind) { | 
|  | 2704 | QDIO_PRINT_WARN("no indicator location available " \ | 
|  | 2705 | "for irq 0x%x\n",irq_ptr->irq); | 
|  | 2706 | qdio_release_irq_memory(irq_ptr); | 
|  | 2707 | return -ENOBUFS; | 
|  | 2708 | } | 
|  | 2709 | } | 
|  | 2710 |  | 
|  | 2711 | /* defaults */ | 
|  | 2712 | irq_ptr->equeue.cmd=DEFAULT_ESTABLISH_QS_CMD; | 
|  | 2713 | irq_ptr->equeue.count=DEFAULT_ESTABLISH_QS_COUNT; | 
|  | 2714 | irq_ptr->aqueue.cmd=DEFAULT_ACTIVATE_QS_CMD; | 
|  | 2715 | irq_ptr->aqueue.count=DEFAULT_ACTIVATE_QS_COUNT; | 
|  | 2716 |  | 
|  | 2717 | qdio_fill_qs(irq_ptr, init_data->cdev, | 
|  | 2718 | init_data->no_input_qs, | 
|  | 2719 | init_data->no_output_qs, | 
|  | 2720 | init_data->input_handler, | 
|  | 2721 | init_data->output_handler,init_data->int_parm, | 
|  | 2722 | init_data->q_format,init_data->flags, | 
|  | 2723 | init_data->input_sbal_addr_array, | 
|  | 2724 | init_data->output_sbal_addr_array); | 
|  | 2725 |  | 
|  | 2726 | if (!try_module_get(THIS_MODULE)) { | 
|  | 2727 | QDIO_PRINT_CRIT("try_module_get() failed!\n"); | 
|  | 2728 | qdio_release_irq_memory(irq_ptr); | 
|  | 2729 | return -EINVAL; | 
|  | 2730 | } | 
|  | 2731 |  | 
|  | 2732 | qdio_fill_thresholds(irq_ptr,init_data->no_input_qs, | 
|  | 2733 | init_data->no_output_qs, | 
|  | 2734 | init_data->min_input_threshold, | 
|  | 2735 | init_data->max_input_threshold, | 
|  | 2736 | init_data->min_output_threshold, | 
|  | 2737 | init_data->max_output_threshold); | 
|  | 2738 |  | 
|  | 2739 | /* fill in qdr */ | 
|  | 2740 | irq_ptr->qdr->qfmt=init_data->q_format; | 
|  | 2741 | irq_ptr->qdr->iqdcnt=init_data->no_input_qs; | 
|  | 2742 | irq_ptr->qdr->oqdcnt=init_data->no_output_qs; | 
|  | 2743 | irq_ptr->qdr->iqdsz=sizeof(struct qdesfmt0)/4; /* size in words */ | 
|  | 2744 | irq_ptr->qdr->oqdsz=sizeof(struct qdesfmt0)/4; | 
|  | 2745 |  | 
|  | 2746 | irq_ptr->qdr->qiba=(unsigned long)&irq_ptr->qib; | 
|  | 2747 | irq_ptr->qdr->qkey=QDIO_STORAGE_KEY; | 
|  | 2748 |  | 
|  | 2749 | /* fill in qib */ | 
|  | 2750 | irq_ptr->qib.qfmt=init_data->q_format; | 
|  | 2751 | if (init_data->no_input_qs) | 
|  | 2752 | irq_ptr->qib.isliba=(unsigned long)(irq_ptr->input_qs[0]->slib); | 
|  | 2753 | if (init_data->no_output_qs) | 
|  | 2754 | irq_ptr->qib.osliba=(unsigned long)(irq_ptr->output_qs[0]->slib); | 
|  | 2755 | memcpy(irq_ptr->qib.ebcnam,init_data->adapter_name,8); | 
|  | 2756 |  | 
|  | 2757 | qdio_set_impl_params(irq_ptr,init_data->qib_param_field_format, | 
|  | 2758 | init_data->qib_param_field, | 
|  | 2759 | init_data->no_input_qs, | 
|  | 2760 | init_data->no_output_qs, | 
|  | 2761 | init_data->input_slib_elements, | 
|  | 2762 | init_data->output_slib_elements); | 
|  | 2763 |  | 
|  | 2764 | /* first input descriptors, then output descriptors */ | 
|  | 2765 | is_iqdio = (init_data->q_format == QDIO_IQDIO_QFMT) ? 1 : 0; | 
|  | 2766 | for (i=0;i<init_data->no_input_qs;i++) | 
|  | 2767 | qdio_allocate_fill_input_desc(irq_ptr, i, is_iqdio); | 
|  | 2768 |  | 
|  | 2769 | for (i=0;i<init_data->no_output_qs;i++) | 
|  | 2770 | qdio_allocate_fill_output_desc(irq_ptr, i, | 
|  | 2771 | init_data->no_input_qs, | 
|  | 2772 | is_iqdio); | 
|  | 2773 |  | 
|  | 2774 | /* qdr, qib, sls, slsbs, slibs, sbales filled. */ | 
|  | 2775 |  | 
|  | 2776 | /* get qdio commands */ | 
|  | 2777 | ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_EQUEUE); | 
|  | 2778 | if (!ciw) { | 
|  | 2779 | QDIO_DBF_TEXT2(1,setup,"no eq"); | 
|  | 2780 | QDIO_PRINT_INFO("No equeue CIW found for QDIO commands. " | 
|  | 2781 | "Trying to use default.\n"); | 
|  | 2782 | } else | 
|  | 2783 | irq_ptr->equeue = *ciw; | 
|  | 2784 | ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_AQUEUE); | 
|  | 2785 | if (!ciw) { | 
|  | 2786 | QDIO_DBF_TEXT2(1,setup,"no aq"); | 
|  | 2787 | QDIO_PRINT_INFO("No aqueue CIW found for QDIO commands. " | 
|  | 2788 | "Trying to use default.\n"); | 
|  | 2789 | } else | 
|  | 2790 | irq_ptr->aqueue = *ciw; | 
|  | 2791 |  | 
|  | 2792 | /* Set new interrupt handler. */ | 
|  | 2793 | irq_ptr->original_int_handler = init_data->cdev->handler; | 
|  | 2794 | init_data->cdev->handler = qdio_handler; | 
|  | 2795 |  | 
|  | 2796 | return 0; | 
|  | 2797 | } | 
|  | 2798 |  | 
|  | 2799 | int | 
|  | 2800 | qdio_establish(struct qdio_initialize *init_data) | 
|  | 2801 | { | 
|  | 2802 | struct qdio_irq *irq_ptr; | 
|  | 2803 | unsigned long saveflags; | 
|  | 2804 | int result, result2; | 
|  | 2805 | struct ccw_device *cdev; | 
|  | 2806 | char dbf_text[20]; | 
|  | 2807 |  | 
|  | 2808 | cdev=init_data->cdev; | 
|  | 2809 | irq_ptr = cdev->private->qdio_data; | 
|  | 2810 | if (!irq_ptr) | 
|  | 2811 | return -EINVAL; | 
|  | 2812 |  | 
|  | 2813 | if (cdev->private->state != DEV_STATE_ONLINE) | 
|  | 2814 | return -EINVAL; | 
|  | 2815 |  | 
|  | 2816 | down(&irq_ptr->setting_up_sema); | 
|  | 2817 |  | 
|  | 2818 | qdio_fill_irq(init_data); | 
|  | 2819 |  | 
|  | 2820 | /* the thinint CHSC stuff */ | 
|  | 2821 | if (irq_ptr->is_thinint_irq) { | 
|  | 2822 |  | 
|  | 2823 | result = tiqdio_set_subchannel_ind(irq_ptr,0); | 
|  | 2824 | if (result) { | 
|  | 2825 | up(&irq_ptr->setting_up_sema); | 
|  | 2826 | qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR); | 
|  | 2827 | return result; | 
|  | 2828 | } | 
|  | 2829 | tiqdio_set_delay_target(irq_ptr,TIQDIO_DELAY_TARGET); | 
|  | 2830 | } | 
|  | 2831 |  | 
|  | 2832 | sprintf(dbf_text,"qest%4x",cdev->private->irq); | 
|  | 2833 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 2834 | QDIO_DBF_TEXT0(0,trace,dbf_text); | 
|  | 2835 |  | 
|  | 2836 | /* establish q */ | 
|  | 2837 | irq_ptr->ccw.cmd_code=irq_ptr->equeue.cmd; | 
|  | 2838 | irq_ptr->ccw.flags=CCW_FLAG_SLI; | 
|  | 2839 | irq_ptr->ccw.count=irq_ptr->equeue.count; | 
|  | 2840 | irq_ptr->ccw.cda=QDIO_GET_ADDR(irq_ptr->qdr); | 
|  | 2841 |  | 
|  | 2842 | spin_lock_irqsave(get_ccwdev_lock(cdev),saveflags); | 
|  | 2843 |  | 
|  | 2844 | ccw_device_set_options(cdev, 0); | 
|  | 2845 | result=ccw_device_start_timeout(cdev,&irq_ptr->ccw, | 
|  | 2846 | QDIO_DOING_ESTABLISH,0, 0, | 
|  | 2847 | QDIO_ESTABLISH_TIMEOUT); | 
|  | 2848 | if (result) { | 
|  | 2849 | result2=ccw_device_start_timeout(cdev,&irq_ptr->ccw, | 
|  | 2850 | QDIO_DOING_ESTABLISH,0,0, | 
|  | 2851 | QDIO_ESTABLISH_TIMEOUT); | 
|  | 2852 | sprintf(dbf_text,"eq:io%4x",result); | 
|  | 2853 | QDIO_DBF_TEXT2(1,setup,dbf_text); | 
|  | 2854 | if (result2) { | 
|  | 2855 | sprintf(dbf_text,"eq:io%4x",result); | 
|  | 2856 | QDIO_DBF_TEXT2(1,setup,dbf_text); | 
|  | 2857 | } | 
|  | 2858 | QDIO_PRINT_WARN("establish queues on irq %04x: do_IO " \ | 
|  | 2859 | "returned %i, next try returned %i\n", | 
|  | 2860 | irq_ptr->irq,result,result2); | 
|  | 2861 | result=result2; | 
|  | 2862 | if (result) | 
|  | 2863 | ccw_device_set_timeout(cdev, 0); | 
|  | 2864 | } | 
|  | 2865 |  | 
|  | 2866 | spin_unlock_irqrestore(get_ccwdev_lock(cdev),saveflags); | 
|  | 2867 |  | 
|  | 2868 | if (result) { | 
|  | 2869 | up(&irq_ptr->setting_up_sema); | 
|  | 2870 | qdio_shutdown(cdev,QDIO_FLAG_CLEANUP_USING_CLEAR); | 
|  | 2871 | return result; | 
|  | 2872 | } | 
|  | 2873 |  | 
| Ursula Braun-Krahl | cdb32dc | 2005-11-07 00:59:10 -0800 | [diff] [blame] | 2874 | /* Timeout is cared for already by using ccw_device_start_timeout(). */ | 
|  | 2875 | wait_event_interruptible(cdev->private->wait_q, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2876 | irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED || | 
| Ursula Braun-Krahl | cdb32dc | 2005-11-07 00:59:10 -0800 | [diff] [blame] | 2877 | irq_ptr->state == QDIO_IRQ_STATE_ERR); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2878 |  | 
|  | 2879 | if (irq_ptr->state == QDIO_IRQ_STATE_ESTABLISHED) | 
|  | 2880 | result = 0; | 
|  | 2881 | else { | 
|  | 2882 | up(&irq_ptr->setting_up_sema); | 
|  | 2883 | qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR); | 
|  | 2884 | return -EIO; | 
|  | 2885 | } | 
|  | 2886 |  | 
|  | 2887 | irq_ptr->qdioac=qdio_check_siga_needs(irq_ptr->irq); | 
|  | 2888 | /* if this gets set once, we're running under VM and can omit SVSes */ | 
|  | 2889 | if (irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_NECESSARY) | 
|  | 2890 | omit_svs=1; | 
|  | 2891 |  | 
|  | 2892 | sprintf(dbf_text,"qdioac%2x",irq_ptr->qdioac); | 
|  | 2893 | QDIO_DBF_TEXT2(0,setup,dbf_text); | 
|  | 2894 |  | 
|  | 2895 | sprintf(dbf_text,"qib ac%2x",irq_ptr->qib.ac); | 
|  | 2896 | QDIO_DBF_TEXT2(0,setup,dbf_text); | 
|  | 2897 |  | 
|  | 2898 | irq_ptr->hydra_gives_outbound_pcis= | 
|  | 2899 | irq_ptr->qib.ac&QIB_AC_OUTBOUND_PCI_SUPPORTED; | 
|  | 2900 | irq_ptr->sync_done_on_outb_pcis= | 
|  | 2901 | irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS; | 
|  | 2902 |  | 
|  | 2903 | qdio_initialize_set_siga_flags_input(irq_ptr); | 
|  | 2904 | qdio_initialize_set_siga_flags_output(irq_ptr); | 
|  | 2905 |  | 
|  | 2906 | up(&irq_ptr->setting_up_sema); | 
|  | 2907 |  | 
|  | 2908 | return result; | 
|  | 2909 |  | 
|  | 2910 | } | 
|  | 2911 |  | 
|  | 2912 | int | 
|  | 2913 | qdio_activate(struct ccw_device *cdev, int flags) | 
|  | 2914 | { | 
|  | 2915 | struct qdio_irq *irq_ptr; | 
|  | 2916 | int i,result=0,result2; | 
|  | 2917 | unsigned long saveflags; | 
|  | 2918 | char dbf_text[20]; /* see qdio_initialize */ | 
|  | 2919 |  | 
|  | 2920 | irq_ptr = cdev->private->qdio_data; | 
|  | 2921 | if (!irq_ptr) | 
|  | 2922 | return -ENODEV; | 
|  | 2923 |  | 
|  | 2924 | if (cdev->private->state != DEV_STATE_ONLINE) | 
|  | 2925 | return -EINVAL; | 
|  | 2926 |  | 
|  | 2927 | down(&irq_ptr->setting_up_sema); | 
|  | 2928 | if (irq_ptr->state==QDIO_IRQ_STATE_INACTIVE) { | 
|  | 2929 | result=-EBUSY; | 
|  | 2930 | goto out; | 
|  | 2931 | } | 
|  | 2932 |  | 
|  | 2933 | sprintf(dbf_text,"qact%4x", irq_ptr->irq); | 
|  | 2934 | QDIO_DBF_TEXT2(0,setup,dbf_text); | 
|  | 2935 | QDIO_DBF_TEXT2(0,trace,dbf_text); | 
|  | 2936 |  | 
|  | 2937 | /* activate q */ | 
|  | 2938 | irq_ptr->ccw.cmd_code=irq_ptr->aqueue.cmd; | 
|  | 2939 | irq_ptr->ccw.flags=CCW_FLAG_SLI; | 
|  | 2940 | irq_ptr->ccw.count=irq_ptr->aqueue.count; | 
|  | 2941 | irq_ptr->ccw.cda=QDIO_GET_ADDR(0); | 
|  | 2942 |  | 
|  | 2943 | spin_lock_irqsave(get_ccwdev_lock(cdev),saveflags); | 
|  | 2944 |  | 
|  | 2945 | ccw_device_set_timeout(cdev, 0); | 
|  | 2946 | ccw_device_set_options(cdev, CCWDEV_REPORT_ALL); | 
|  | 2947 | result=ccw_device_start(cdev,&irq_ptr->ccw,QDIO_DOING_ACTIVATE, | 
|  | 2948 | 0, DOIO_DENY_PREFETCH); | 
|  | 2949 | if (result) { | 
|  | 2950 | result2=ccw_device_start(cdev,&irq_ptr->ccw, | 
|  | 2951 | QDIO_DOING_ACTIVATE,0,0); | 
|  | 2952 | sprintf(dbf_text,"aq:io%4x",result); | 
|  | 2953 | QDIO_DBF_TEXT2(1,setup,dbf_text); | 
|  | 2954 | if (result2) { | 
|  | 2955 | sprintf(dbf_text,"aq:io%4x",result); | 
|  | 2956 | QDIO_DBF_TEXT2(1,setup,dbf_text); | 
|  | 2957 | } | 
|  | 2958 | QDIO_PRINT_WARN("activate queues on irq %04x: do_IO " \ | 
|  | 2959 | "returned %i, next try returned %i\n", | 
|  | 2960 | irq_ptr->irq,result,result2); | 
|  | 2961 | result=result2; | 
|  | 2962 | } | 
|  | 2963 |  | 
|  | 2964 | spin_unlock_irqrestore(get_ccwdev_lock(cdev),saveflags); | 
|  | 2965 | if (result) | 
|  | 2966 | goto out; | 
|  | 2967 |  | 
|  | 2968 | for (i=0;i<irq_ptr->no_input_qs;i++) { | 
|  | 2969 | if (irq_ptr->is_thinint_irq) { | 
|  | 2970 | /* | 
|  | 2971 | * that way we know, that, if we will get interrupted | 
|  | 2972 | * by tiqdio_inbound_processing, qdio_unmark_q will | 
|  | 2973 | * not be called | 
|  | 2974 | */ | 
|  | 2975 | qdio_reserve_q(irq_ptr->input_qs[i]); | 
|  | 2976 | qdio_mark_tiq(irq_ptr->input_qs[i]); | 
|  | 2977 | qdio_release_q(irq_ptr->input_qs[i]); | 
|  | 2978 | } | 
|  | 2979 | } | 
|  | 2980 |  | 
|  | 2981 | if (flags&QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT) { | 
|  | 2982 | for (i=0;i<irq_ptr->no_input_qs;i++) { | 
|  | 2983 | irq_ptr->input_qs[i]->is_input_q|= | 
|  | 2984 | QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT; | 
|  | 2985 | } | 
|  | 2986 | } | 
|  | 2987 |  | 
|  | 2988 | wait_event_interruptible_timeout(cdev->private->wait_q, | 
|  | 2989 | ((irq_ptr->state == | 
|  | 2990 | QDIO_IRQ_STATE_STOPPED) || | 
|  | 2991 | (irq_ptr->state == | 
|  | 2992 | QDIO_IRQ_STATE_ERR)), | 
|  | 2993 | QDIO_ACTIVATE_TIMEOUT); | 
|  | 2994 |  | 
|  | 2995 | switch (irq_ptr->state) { | 
|  | 2996 | case QDIO_IRQ_STATE_STOPPED: | 
|  | 2997 | case QDIO_IRQ_STATE_ERR: | 
|  | 2998 | up(&irq_ptr->setting_up_sema); | 
|  | 2999 | qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR); | 
|  | 3000 | down(&irq_ptr->setting_up_sema); | 
|  | 3001 | result = -EIO; | 
|  | 3002 | break; | 
|  | 3003 | default: | 
|  | 3004 | qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ACTIVE); | 
|  | 3005 | result = 0; | 
|  | 3006 | } | 
|  | 3007 | out: | 
|  | 3008 | up(&irq_ptr->setting_up_sema); | 
|  | 3009 |  | 
|  | 3010 | return result; | 
|  | 3011 | } | 
|  | 3012 |  | 
|  | 3013 | /* buffers filled forwards again to make Rick happy */ | 
|  | 3014 | static inline void | 
|  | 3015 | qdio_do_qdio_fill_input(struct qdio_q *q, unsigned int qidx, | 
|  | 3016 | unsigned int count, struct qdio_buffer *buffers) | 
|  | 3017 | { | 
|  | 3018 | for (;;) { | 
|  | 3019 | set_slsb(&q->slsb.acc.val[qidx],SLSB_CU_INPUT_EMPTY); | 
|  | 3020 | count--; | 
|  | 3021 | if (!count) break; | 
|  | 3022 | qidx=(qidx+1)&(QDIO_MAX_BUFFERS_PER_Q-1); | 
|  | 3023 | } | 
|  | 3024 |  | 
|  | 3025 | /* not necessary, as the queues are synced during the SIGA read */ | 
|  | 3026 | /*SYNC_MEMORY;*/ | 
|  | 3027 | } | 
|  | 3028 |  | 
|  | 3029 | static inline void | 
|  | 3030 | qdio_do_qdio_fill_output(struct qdio_q *q, unsigned int qidx, | 
|  | 3031 | unsigned int count, struct qdio_buffer *buffers) | 
|  | 3032 | { | 
|  | 3033 | for (;;) { | 
|  | 3034 | set_slsb(&q->slsb.acc.val[qidx],SLSB_CU_OUTPUT_PRIMED); | 
|  | 3035 | count--; | 
|  | 3036 | if (!count) break; | 
|  | 3037 | qidx=(qidx+1)&(QDIO_MAX_BUFFERS_PER_Q-1); | 
|  | 3038 | } | 
|  | 3039 |  | 
|  | 3040 | /* SIGA write will sync the queues */ | 
|  | 3041 | /*SYNC_MEMORY;*/ | 
|  | 3042 | } | 
|  | 3043 |  | 
|  | 3044 | static inline void | 
|  | 3045 | do_qdio_handle_inbound(struct qdio_q *q, unsigned int callflags, | 
|  | 3046 | unsigned int qidx, unsigned int count, | 
|  | 3047 | struct qdio_buffer *buffers) | 
|  | 3048 | { | 
|  | 3049 | int used_elements; | 
|  | 3050 |  | 
|  | 3051 | /* This is the inbound handling of queues */ | 
|  | 3052 | used_elements=atomic_add_return(count, &q->number_of_buffers_used) - count; | 
|  | 3053 |  | 
|  | 3054 | qdio_do_qdio_fill_input(q,qidx,count,buffers); | 
|  | 3055 |  | 
|  | 3056 | if ((used_elements+count==QDIO_MAX_BUFFERS_PER_Q)&& | 
|  | 3057 | (callflags&QDIO_FLAG_UNDER_INTERRUPT)) | 
|  | 3058 | atomic_swap(&q->polling,0); | 
|  | 3059 |  | 
|  | 3060 | if (used_elements) | 
|  | 3061 | return; | 
|  | 3062 | if (callflags&QDIO_FLAG_DONT_SIGA) | 
|  | 3063 | return; | 
|  | 3064 | if (q->siga_in) { | 
|  | 3065 | int result; | 
|  | 3066 |  | 
|  | 3067 | result=qdio_siga_input(q); | 
|  | 3068 | if (result) { | 
|  | 3069 | if (q->siga_error) | 
|  | 3070 | q->error_status_flags|= | 
|  | 3071 | QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR; | 
|  | 3072 | q->error_status_flags|=QDIO_STATUS_LOOK_FOR_ERROR; | 
|  | 3073 | q->siga_error=result; | 
|  | 3074 | } | 
|  | 3075 | } | 
|  | 3076 |  | 
|  | 3077 | qdio_mark_q(q); | 
|  | 3078 | } | 
|  | 3079 |  | 
|  | 3080 | static inline void | 
|  | 3081 | do_qdio_handle_outbound(struct qdio_q *q, unsigned int callflags, | 
|  | 3082 | unsigned int qidx, unsigned int count, | 
|  | 3083 | struct qdio_buffer *buffers) | 
|  | 3084 | { | 
|  | 3085 | int used_elements; | 
|  | 3086 |  | 
|  | 3087 | /* This is the outbound handling of queues */ | 
|  | 3088 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 3089 | perf_stats.start_time_outbound=NOW; | 
|  | 3090 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 3091 |  | 
|  | 3092 | qdio_do_qdio_fill_output(q,qidx,count,buffers); | 
|  | 3093 |  | 
|  | 3094 | used_elements=atomic_add_return(count, &q->number_of_buffers_used) - count; | 
|  | 3095 |  | 
|  | 3096 | if (callflags&QDIO_FLAG_DONT_SIGA) { | 
|  | 3097 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 3098 | perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound; | 
|  | 3099 | perf_stats.outbound_cnt++; | 
|  | 3100 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 3101 | return; | 
|  | 3102 | } | 
|  | 3103 | if (q->is_iqdio_q) { | 
|  | 3104 | /* one siga for every sbal */ | 
|  | 3105 | while (count--) | 
|  | 3106 | qdio_kick_outbound_q(q); | 
|  | 3107 |  | 
|  | 3108 | __qdio_outbound_processing(q); | 
|  | 3109 | } else { | 
|  | 3110 | /* under VM, we do a SIGA sync unconditionally */ | 
|  | 3111 | SYNC_MEMORY; | 
|  | 3112 | else { | 
|  | 3113 | /* | 
|  | 3114 | * w/o shadow queues (else branch of | 
|  | 3115 | * SYNC_MEMORY :-/ ), we try to | 
|  | 3116 | * fast-requeue buffers | 
|  | 3117 | */ | 
|  | 3118 | if (q->slsb.acc.val[(qidx+QDIO_MAX_BUFFERS_PER_Q-1) | 
|  | 3119 | &(QDIO_MAX_BUFFERS_PER_Q-1)]!= | 
|  | 3120 | SLSB_CU_OUTPUT_PRIMED) { | 
|  | 3121 | qdio_kick_outbound_q(q); | 
|  | 3122 | } else { | 
|  | 3123 | QDIO_DBF_TEXT3(0,trace, "fast-req"); | 
|  | 3124 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 3125 | perf_stats.fast_reqs++; | 
|  | 3126 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 3127 | } | 
|  | 3128 | } | 
|  | 3129 | /* | 
|  | 3130 | * only marking the q could take too long, | 
|  | 3131 | * the upper layer module could do a lot of | 
|  | 3132 | * traffic in that time | 
|  | 3133 | */ | 
|  | 3134 | __qdio_outbound_processing(q); | 
|  | 3135 | } | 
|  | 3136 |  | 
|  | 3137 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 3138 | perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound; | 
|  | 3139 | perf_stats.outbound_cnt++; | 
|  | 3140 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 3141 | } | 
|  | 3142 |  | 
|  | 3143 | /* count must be 1 in iqdio */ | 
|  | 3144 | int | 
|  | 3145 | do_QDIO(struct ccw_device *cdev,unsigned int callflags, | 
|  | 3146 | unsigned int queue_number, unsigned int qidx, | 
|  | 3147 | unsigned int count,struct qdio_buffer *buffers) | 
|  | 3148 | { | 
|  | 3149 | struct qdio_irq *irq_ptr; | 
|  | 3150 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 3151 | char dbf_text[20]; | 
|  | 3152 |  | 
|  | 3153 | sprintf(dbf_text,"doQD%04x",cdev->private->irq); | 
|  | 3154 | QDIO_DBF_TEXT3(0,trace,dbf_text); | 
|  | 3155 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 3156 |  | 
|  | 3157 | if ( (qidx>QDIO_MAX_BUFFERS_PER_Q) || | 
|  | 3158 | (count>QDIO_MAX_BUFFERS_PER_Q) || | 
|  | 3159 | (queue_number>QDIO_MAX_QUEUES_PER_IRQ) ) | 
|  | 3160 | return -EINVAL; | 
|  | 3161 |  | 
|  | 3162 | if (count==0) | 
|  | 3163 | return 0; | 
|  | 3164 |  | 
|  | 3165 | irq_ptr = cdev->private->qdio_data; | 
|  | 3166 | if (!irq_ptr) | 
|  | 3167 | return -ENODEV; | 
|  | 3168 |  | 
|  | 3169 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 3170 | if (callflags&QDIO_FLAG_SYNC_INPUT) | 
|  | 3171 | QDIO_DBF_HEX3(0,trace,&irq_ptr->input_qs[queue_number], | 
|  | 3172 | sizeof(void*)); | 
|  | 3173 | else | 
|  | 3174 | QDIO_DBF_HEX3(0,trace,&irq_ptr->output_qs[queue_number], | 
|  | 3175 | sizeof(void*)); | 
|  | 3176 | sprintf(dbf_text,"flag%04x",callflags); | 
|  | 3177 | QDIO_DBF_TEXT3(0,trace,dbf_text); | 
|  | 3178 | sprintf(dbf_text,"qi%02xct%02x",qidx,count); | 
|  | 3179 | QDIO_DBF_TEXT3(0,trace,dbf_text); | 
|  | 3180 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 3181 |  | 
|  | 3182 | if (irq_ptr->state!=QDIO_IRQ_STATE_ACTIVE) | 
|  | 3183 | return -EBUSY; | 
|  | 3184 |  | 
|  | 3185 | if (callflags&QDIO_FLAG_SYNC_INPUT) | 
|  | 3186 | do_qdio_handle_inbound(irq_ptr->input_qs[queue_number], | 
|  | 3187 | callflags, qidx, count, buffers); | 
|  | 3188 | else if (callflags&QDIO_FLAG_SYNC_OUTPUT) | 
|  | 3189 | do_qdio_handle_outbound(irq_ptr->output_qs[queue_number], | 
|  | 3190 | callflags, qidx, count, buffers); | 
|  | 3191 | else { | 
|  | 3192 | QDIO_DBF_TEXT3(1,trace,"doQD:inv"); | 
|  | 3193 | return -EINVAL; | 
|  | 3194 | } | 
|  | 3195 | return 0; | 
|  | 3196 | } | 
|  | 3197 |  | 
|  | 3198 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 3199 | static int | 
|  | 3200 | qdio_perf_procfile_read(char *buffer, char **buffer_location, off_t offset, | 
|  | 3201 | int buffer_length, int *eof, void *data) | 
|  | 3202 | { | 
|  | 3203 | int c=0; | 
|  | 3204 |  | 
|  | 3205 | /* we are always called with buffer_length=4k, so we all | 
|  | 3206 | deliver on the first read */ | 
|  | 3207 | if (offset>0) | 
|  | 3208 | return 0; | 
|  | 3209 |  | 
|  | 3210 | #define _OUTP_IT(x...) c+=sprintf(buffer+c,x) | 
|  | 3211 | _OUTP_IT("i_p_nc/c=%lu/%lu\n",i_p_nc,i_p_c); | 
|  | 3212 | _OUTP_IT("ii_p_nc/c=%lu/%lu\n",ii_p_nc,ii_p_c); | 
|  | 3213 | _OUTP_IT("o_p_nc/c=%lu/%lu\n",o_p_nc,o_p_c); | 
|  | 3214 | _OUTP_IT("Number of tasklet runs (total)                  : %u\n", | 
|  | 3215 | perf_stats.tl_runs); | 
|  | 3216 | _OUTP_IT("\n"); | 
|  | 3217 | _OUTP_IT("Number of SIGA sync's issued                    : %u\n", | 
|  | 3218 | perf_stats.siga_syncs); | 
|  | 3219 | _OUTP_IT("Number of SIGA in's issued                      : %u\n", | 
|  | 3220 | perf_stats.siga_ins); | 
|  | 3221 | _OUTP_IT("Number of SIGA out's issued                     : %u\n", | 
|  | 3222 | perf_stats.siga_outs); | 
|  | 3223 | _OUTP_IT("Number of PCIs caught                           : %u\n", | 
|  | 3224 | perf_stats.pcis); | 
|  | 3225 | _OUTP_IT("Number of adapter interrupts caught             : %u\n", | 
|  | 3226 | perf_stats.thinints); | 
|  | 3227 | _OUTP_IT("Number of fast requeues (outg. SBALs w/o SIGA)  : %u\n", | 
|  | 3228 | perf_stats.fast_reqs); | 
|  | 3229 | _OUTP_IT("\n"); | 
|  | 3230 | _OUTP_IT("Total time of all inbound actions (us) incl. UL : %u\n", | 
|  | 3231 | perf_stats.inbound_time); | 
|  | 3232 | _OUTP_IT("Number of inbound transfers                     : %u\n", | 
|  | 3233 | perf_stats.inbound_cnt); | 
|  | 3234 | _OUTP_IT("Total time of all outbound do_QDIOs (us)        : %u\n", | 
|  | 3235 | perf_stats.outbound_time); | 
|  | 3236 | _OUTP_IT("Number of do_QDIOs outbound                     : %u\n", | 
|  | 3237 | perf_stats.outbound_cnt); | 
|  | 3238 | _OUTP_IT("\n"); | 
|  | 3239 |  | 
|  | 3240 | return c; | 
|  | 3241 | } | 
|  | 3242 |  | 
|  | 3243 | static struct proc_dir_entry *qdio_perf_proc_file; | 
|  | 3244 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 3245 |  | 
|  | 3246 | static void | 
|  | 3247 | qdio_add_procfs_entry(void) | 
|  | 3248 | { | 
|  | 3249 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 3250 | proc_perf_file_registration=0; | 
|  | 3251 | qdio_perf_proc_file=create_proc_entry(QDIO_PERF, | 
|  | 3252 | S_IFREG|0444,&proc_root); | 
|  | 3253 | if (qdio_perf_proc_file) { | 
|  | 3254 | qdio_perf_proc_file->read_proc=&qdio_perf_procfile_read; | 
|  | 3255 | } else proc_perf_file_registration=-1; | 
|  | 3256 |  | 
|  | 3257 | if (proc_perf_file_registration) | 
|  | 3258 | QDIO_PRINT_WARN("was not able to register perf. " \ | 
|  | 3259 | "proc-file (%i).\n", | 
|  | 3260 | proc_perf_file_registration); | 
|  | 3261 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 3262 | } | 
|  | 3263 |  | 
|  | 3264 | static void | 
|  | 3265 | qdio_remove_procfs_entry(void) | 
|  | 3266 | { | 
|  | 3267 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 3268 | perf_stats.tl_runs=0; | 
|  | 3269 |  | 
|  | 3270 | if (!proc_perf_file_registration) /* means if it went ok earlier */ | 
|  | 3271 | remove_proc_entry(QDIO_PERF,&proc_root); | 
|  | 3272 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 3273 | } | 
|  | 3274 |  | 
|  | 3275 | static void | 
|  | 3276 | tiqdio_register_thinints(void) | 
|  | 3277 | { | 
|  | 3278 | char dbf_text[20]; | 
|  | 3279 | register_thinint_result= | 
|  | 3280 | s390_register_adapter_interrupt(&tiqdio_thinint_handler); | 
|  | 3281 | if (register_thinint_result) { | 
|  | 3282 | sprintf(dbf_text,"regthn%x",(register_thinint_result&0xff)); | 
|  | 3283 | QDIO_DBF_TEXT0(0,setup,dbf_text); | 
|  | 3284 | QDIO_PRINT_ERR("failed to register adapter handler " \ | 
|  | 3285 | "(rc=%i).\nAdapter interrupts might " \ | 
|  | 3286 | "not work. Continuing.\n", | 
|  | 3287 | register_thinint_result); | 
|  | 3288 | } | 
|  | 3289 | } | 
|  | 3290 |  | 
|  | 3291 | static void | 
|  | 3292 | tiqdio_unregister_thinints(void) | 
|  | 3293 | { | 
|  | 3294 | if (!register_thinint_result) | 
|  | 3295 | s390_unregister_adapter_interrupt(&tiqdio_thinint_handler); | 
|  | 3296 | } | 
|  | 3297 |  | 
|  | 3298 | static int | 
|  | 3299 | qdio_get_qdio_memory(void) | 
|  | 3300 | { | 
|  | 3301 | int i; | 
|  | 3302 | indicator_used[0]=1; | 
|  | 3303 |  | 
|  | 3304 | for (i=1;i<INDICATORS_PER_CACHELINE;i++) | 
|  | 3305 | indicator_used[i]=0; | 
|  | 3306 | indicators=(__u32*)kmalloc(sizeof(__u32)*(INDICATORS_PER_CACHELINE), | 
|  | 3307 | GFP_KERNEL); | 
|  | 3308 | if (!indicators) return -ENOMEM; | 
|  | 3309 | memset(indicators,0,sizeof(__u32)*(INDICATORS_PER_CACHELINE)); | 
|  | 3310 | return 0; | 
|  | 3311 | } | 
|  | 3312 |  | 
|  | 3313 | static void | 
|  | 3314 | qdio_release_qdio_memory(void) | 
|  | 3315 | { | 
| Jesper Juhl | 17fd682 | 2005-11-07 01:01:30 -0800 | [diff] [blame] | 3316 | kfree(indicators); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3317 | } | 
|  | 3318 |  | 
|  | 3319 | static void | 
|  | 3320 | qdio_unregister_dbf_views(void) | 
|  | 3321 | { | 
|  | 3322 | if (qdio_dbf_setup) | 
|  | 3323 | debug_unregister(qdio_dbf_setup); | 
|  | 3324 | if (qdio_dbf_sbal) | 
|  | 3325 | debug_unregister(qdio_dbf_sbal); | 
|  | 3326 | if (qdio_dbf_sense) | 
|  | 3327 | debug_unregister(qdio_dbf_sense); | 
|  | 3328 | if (qdio_dbf_trace) | 
|  | 3329 | debug_unregister(qdio_dbf_trace); | 
|  | 3330 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 3331 | if (qdio_dbf_slsb_out) | 
|  | 3332 | debug_unregister(qdio_dbf_slsb_out); | 
|  | 3333 | if (qdio_dbf_slsb_in) | 
|  | 3334 | debug_unregister(qdio_dbf_slsb_in); | 
|  | 3335 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 3336 | } | 
|  | 3337 |  | 
|  | 3338 | static int | 
|  | 3339 | qdio_register_dbf_views(void) | 
|  | 3340 | { | 
|  | 3341 | qdio_dbf_setup=debug_register(QDIO_DBF_SETUP_NAME, | 
| Michael Holzheu | 66a464d | 2005-06-25 14:55:33 -0700 | [diff] [blame] | 3342 | QDIO_DBF_SETUP_PAGES, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3343 | QDIO_DBF_SETUP_NR_AREAS, | 
|  | 3344 | QDIO_DBF_SETUP_LEN); | 
|  | 3345 | if (!qdio_dbf_setup) | 
|  | 3346 | goto oom; | 
|  | 3347 | debug_register_view(qdio_dbf_setup,&debug_hex_ascii_view); | 
|  | 3348 | debug_set_level(qdio_dbf_setup,QDIO_DBF_SETUP_LEVEL); | 
|  | 3349 |  | 
|  | 3350 | qdio_dbf_sbal=debug_register(QDIO_DBF_SBAL_NAME, | 
| Michael Holzheu | 66a464d | 2005-06-25 14:55:33 -0700 | [diff] [blame] | 3351 | QDIO_DBF_SBAL_PAGES, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3352 | QDIO_DBF_SBAL_NR_AREAS, | 
|  | 3353 | QDIO_DBF_SBAL_LEN); | 
|  | 3354 | if (!qdio_dbf_sbal) | 
|  | 3355 | goto oom; | 
|  | 3356 |  | 
|  | 3357 | debug_register_view(qdio_dbf_sbal,&debug_hex_ascii_view); | 
|  | 3358 | debug_set_level(qdio_dbf_sbal,QDIO_DBF_SBAL_LEVEL); | 
|  | 3359 |  | 
|  | 3360 | qdio_dbf_sense=debug_register(QDIO_DBF_SENSE_NAME, | 
| Michael Holzheu | 66a464d | 2005-06-25 14:55:33 -0700 | [diff] [blame] | 3361 | QDIO_DBF_SENSE_PAGES, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3362 | QDIO_DBF_SENSE_NR_AREAS, | 
|  | 3363 | QDIO_DBF_SENSE_LEN); | 
|  | 3364 | if (!qdio_dbf_sense) | 
|  | 3365 | goto oom; | 
|  | 3366 |  | 
|  | 3367 | debug_register_view(qdio_dbf_sense,&debug_hex_ascii_view); | 
|  | 3368 | debug_set_level(qdio_dbf_sense,QDIO_DBF_SENSE_LEVEL); | 
|  | 3369 |  | 
|  | 3370 | qdio_dbf_trace=debug_register(QDIO_DBF_TRACE_NAME, | 
| Michael Holzheu | 66a464d | 2005-06-25 14:55:33 -0700 | [diff] [blame] | 3371 | QDIO_DBF_TRACE_PAGES, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3372 | QDIO_DBF_TRACE_NR_AREAS, | 
|  | 3373 | QDIO_DBF_TRACE_LEN); | 
|  | 3374 | if (!qdio_dbf_trace) | 
|  | 3375 | goto oom; | 
|  | 3376 |  | 
|  | 3377 | debug_register_view(qdio_dbf_trace,&debug_hex_ascii_view); | 
|  | 3378 | debug_set_level(qdio_dbf_trace,QDIO_DBF_TRACE_LEVEL); | 
|  | 3379 |  | 
|  | 3380 | #ifdef CONFIG_QDIO_DEBUG | 
|  | 3381 | qdio_dbf_slsb_out=debug_register(QDIO_DBF_SLSB_OUT_NAME, | 
| Michael Holzheu | 66a464d | 2005-06-25 14:55:33 -0700 | [diff] [blame] | 3382 | QDIO_DBF_SLSB_OUT_PAGES, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3383 | QDIO_DBF_SLSB_OUT_NR_AREAS, | 
|  | 3384 | QDIO_DBF_SLSB_OUT_LEN); | 
|  | 3385 | if (!qdio_dbf_slsb_out) | 
|  | 3386 | goto oom; | 
|  | 3387 | debug_register_view(qdio_dbf_slsb_out,&debug_hex_ascii_view); | 
|  | 3388 | debug_set_level(qdio_dbf_slsb_out,QDIO_DBF_SLSB_OUT_LEVEL); | 
|  | 3389 |  | 
|  | 3390 | qdio_dbf_slsb_in=debug_register(QDIO_DBF_SLSB_IN_NAME, | 
| Michael Holzheu | 66a464d | 2005-06-25 14:55:33 -0700 | [diff] [blame] | 3391 | QDIO_DBF_SLSB_IN_PAGES, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3392 | QDIO_DBF_SLSB_IN_NR_AREAS, | 
|  | 3393 | QDIO_DBF_SLSB_IN_LEN); | 
|  | 3394 | if (!qdio_dbf_slsb_in) | 
|  | 3395 | goto oom; | 
|  | 3396 | debug_register_view(qdio_dbf_slsb_in,&debug_hex_ascii_view); | 
|  | 3397 | debug_set_level(qdio_dbf_slsb_in,QDIO_DBF_SLSB_IN_LEVEL); | 
|  | 3398 | #endif /* CONFIG_QDIO_DEBUG */ | 
|  | 3399 | return 0; | 
|  | 3400 | oom: | 
|  | 3401 | QDIO_PRINT_ERR("not enough memory for dbf.\n"); | 
|  | 3402 | qdio_unregister_dbf_views(); | 
|  | 3403 | return -ENOMEM; | 
|  | 3404 | } | 
|  | 3405 |  | 
|  | 3406 | static int __init | 
|  | 3407 | init_QDIO(void) | 
|  | 3408 | { | 
|  | 3409 | int res; | 
|  | 3410 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 3411 | void *ptr; | 
|  | 3412 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 3413 |  | 
|  | 3414 | printk("qdio: loading %s\n",version); | 
|  | 3415 |  | 
|  | 3416 | res=qdio_get_qdio_memory(); | 
|  | 3417 | if (res) | 
|  | 3418 | return res; | 
|  | 3419 |  | 
|  | 3420 | res = qdio_register_dbf_views(); | 
|  | 3421 | if (res) | 
|  | 3422 | return res; | 
|  | 3423 |  | 
|  | 3424 | QDIO_DBF_TEXT0(0,setup,"initQDIO"); | 
|  | 3425 |  | 
|  | 3426 | #ifdef QDIO_PERFORMANCE_STATS | 
|  | 3427 | memset((void*)&perf_stats,0,sizeof(perf_stats)); | 
|  | 3428 | QDIO_DBF_TEXT0(0,setup,"perfstat"); | 
|  | 3429 | ptr=&perf_stats; | 
|  | 3430 | QDIO_DBF_HEX0(0,setup,&ptr,sizeof(void*)); | 
|  | 3431 | #endif /* QDIO_PERFORMANCE_STATS */ | 
|  | 3432 |  | 
|  | 3433 | qdio_add_procfs_entry(); | 
|  | 3434 |  | 
|  | 3435 | if (tiqdio_check_chsc_availability()) | 
|  | 3436 | QDIO_PRINT_ERR("Not all CHSCs supported. Continuing.\n"); | 
|  | 3437 |  | 
|  | 3438 | tiqdio_register_thinints(); | 
|  | 3439 |  | 
|  | 3440 | return 0; | 
|  | 3441 | } | 
|  | 3442 |  | 
|  | 3443 | static void __exit | 
|  | 3444 | cleanup_QDIO(void) | 
|  | 3445 | { | 
|  | 3446 | tiqdio_unregister_thinints(); | 
|  | 3447 | qdio_remove_procfs_entry(); | 
|  | 3448 | qdio_release_qdio_memory(); | 
|  | 3449 | qdio_unregister_dbf_views(); | 
|  | 3450 |  | 
|  | 3451 | printk("qdio: %s: module removed\n",version); | 
|  | 3452 | } | 
|  | 3453 |  | 
|  | 3454 | module_init(init_QDIO); | 
|  | 3455 | module_exit(cleanup_QDIO); | 
|  | 3456 |  | 
|  | 3457 | EXPORT_SYMBOL(qdio_allocate); | 
|  | 3458 | EXPORT_SYMBOL(qdio_establish); | 
|  | 3459 | EXPORT_SYMBOL(qdio_initialize); | 
|  | 3460 | EXPORT_SYMBOL(qdio_activate); | 
|  | 3461 | EXPORT_SYMBOL(do_QDIO); | 
|  | 3462 | EXPORT_SYMBOL(qdio_shutdown); | 
|  | 3463 | EXPORT_SYMBOL(qdio_free); | 
|  | 3464 | EXPORT_SYMBOL(qdio_cleanup); | 
|  | 3465 | EXPORT_SYMBOL(qdio_synchronize); |