diff options
Diffstat (limited to 'drivers/scsi/aic7xxx/aic79xx_core.c')
-rw-r--r-- | drivers/scsi/aic7xxx/aic79xx_core.c | 771 |
1 files changed, 766 insertions, 5 deletions
diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c index ade0fb8fbdb..336f4bea251 100644 --- a/drivers/scsi/aic7xxx/aic79xx_core.c +++ b/drivers/scsi/aic7xxx/aic79xx_core.c | |||
@@ -266,8 +266,752 @@ static int ahd_match_scb(struct ahd_softc *ahd, struct scb *scb, | |||
266 | int target, char channel, int lun, | 266 | int target, char channel, int lun, |
267 | u_int tag, role_t role); | 267 | u_int tag, role_t role); |
268 | 268 | ||
269 | /******************************** Private Inlines *****************************/ | 269 | /************************ Sequencer Execution Control *************************/ |
270 | void | ||
271 | ahd_set_modes(struct ahd_softc *ahd, ahd_mode src, ahd_mode dst) | ||
272 | { | ||
273 | if (ahd->src_mode == src && ahd->dst_mode == dst) | ||
274 | return; | ||
275 | #ifdef AHD_DEBUG | ||
276 | if (ahd->src_mode == AHD_MODE_UNKNOWN | ||
277 | || ahd->dst_mode == AHD_MODE_UNKNOWN) | ||
278 | panic("Setting mode prior to saving it.\n"); | ||
279 | if ((ahd_debug & AHD_SHOW_MODEPTR) != 0) | ||
280 | printf("%s: Setting mode 0x%x\n", ahd_name(ahd), | ||
281 | ahd_build_mode_state(ahd, src, dst)); | ||
282 | #endif | ||
283 | ahd_outb(ahd, MODE_PTR, ahd_build_mode_state(ahd, src, dst)); | ||
284 | ahd->src_mode = src; | ||
285 | ahd->dst_mode = dst; | ||
286 | } | ||
287 | |||
288 | void | ||
289 | ahd_update_modes(struct ahd_softc *ahd) | ||
290 | { | ||
291 | ahd_mode_state mode_ptr; | ||
292 | ahd_mode src; | ||
293 | ahd_mode dst; | ||
294 | |||
295 | mode_ptr = ahd_inb(ahd, MODE_PTR); | ||
296 | #ifdef AHD_DEBUG | ||
297 | if ((ahd_debug & AHD_SHOW_MODEPTR) != 0) | ||
298 | printf("Reading mode 0x%x\n", mode_ptr); | ||
299 | #endif | ||
300 | ahd_extract_mode_state(ahd, mode_ptr, &src, &dst); | ||
301 | ahd_known_modes(ahd, src, dst); | ||
302 | } | ||
303 | |||
304 | void | ||
305 | ahd_assert_modes(struct ahd_softc *ahd, ahd_mode srcmode, | ||
306 | ahd_mode dstmode, const char *file, int line) | ||
307 | { | ||
308 | #ifdef AHD_DEBUG | ||
309 | if ((srcmode & AHD_MK_MSK(ahd->src_mode)) == 0 | ||
310 | || (dstmode & AHD_MK_MSK(ahd->dst_mode)) == 0) { | ||
311 | panic("%s:%s:%d: Mode assertion failed.\n", | ||
312 | ahd_name(ahd), file, line); | ||
313 | } | ||
314 | #endif | ||
315 | } | ||
316 | |||
317 | #define AHD_ASSERT_MODES(ahd, source, dest) \ | ||
318 | ahd_assert_modes(ahd, source, dest, __FILE__, __LINE__); | ||
319 | |||
320 | ahd_mode_state | ||
321 | ahd_save_modes(struct ahd_softc *ahd) | ||
322 | { | ||
323 | if (ahd->src_mode == AHD_MODE_UNKNOWN | ||
324 | || ahd->dst_mode == AHD_MODE_UNKNOWN) | ||
325 | ahd_update_modes(ahd); | ||
326 | |||
327 | return (ahd_build_mode_state(ahd, ahd->src_mode, ahd->dst_mode)); | ||
328 | } | ||
329 | |||
330 | void | ||
331 | ahd_restore_modes(struct ahd_softc *ahd, ahd_mode_state state) | ||
332 | { | ||
333 | ahd_mode src; | ||
334 | ahd_mode dst; | ||
335 | |||
336 | ahd_extract_mode_state(ahd, state, &src, &dst); | ||
337 | ahd_set_modes(ahd, src, dst); | ||
338 | } | ||
339 | |||
340 | /* | ||
341 | * Determine whether the sequencer has halted code execution. | ||
342 | * Returns non-zero status if the sequencer is stopped. | ||
343 | */ | ||
344 | int | ||
345 | ahd_is_paused(struct ahd_softc *ahd) | ||
346 | { | ||
347 | return ((ahd_inb(ahd, HCNTRL) & PAUSE) != 0); | ||
348 | } | ||
349 | |||
350 | /* | ||
351 | * Request that the sequencer stop and wait, indefinitely, for it | ||
352 | * to stop. The sequencer will only acknowledge that it is paused | ||
353 | * once it has reached an instruction boundary and PAUSEDIS is | ||
354 | * cleared in the SEQCTL register. The sequencer may use PAUSEDIS | ||
355 | * for critical sections. | ||
356 | */ | ||
357 | void | ||
358 | ahd_pause(struct ahd_softc *ahd) | ||
359 | { | ||
360 | ahd_outb(ahd, HCNTRL, ahd->pause); | ||
361 | |||
362 | /* | ||
363 | * Since the sequencer can disable pausing in a critical section, we | ||
364 | * must loop until it actually stops. | ||
365 | */ | ||
366 | while (ahd_is_paused(ahd) == 0) | ||
367 | ; | ||
368 | } | ||
369 | |||
370 | /* | ||
371 | * Allow the sequencer to continue program execution. | ||
372 | * We check here to ensure that no additional interrupt | ||
373 | * sources that would cause the sequencer to halt have been | ||
374 | * asserted. If, for example, a SCSI bus reset is detected | ||
375 | * while we are fielding a different, pausing, interrupt type, | ||
376 | * we don't want to release the sequencer before going back | ||
377 | * into our interrupt handler and dealing with this new | ||
378 | * condition. | ||
379 | */ | ||
380 | void | ||
381 | ahd_unpause(struct ahd_softc *ahd) | ||
382 | { | ||
383 | /* | ||
384 | * Automatically restore our modes to those saved | ||
385 | * prior to the first change of the mode. | ||
386 | */ | ||
387 | if (ahd->saved_src_mode != AHD_MODE_UNKNOWN | ||
388 | && ahd->saved_dst_mode != AHD_MODE_UNKNOWN) { | ||
389 | if ((ahd->flags & AHD_UPDATE_PEND_CMDS) != 0) | ||
390 | ahd_reset_cmds_pending(ahd); | ||
391 | ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode); | ||
392 | } | ||
393 | |||
394 | if ((ahd_inb(ahd, INTSTAT) & ~CMDCMPLT) == 0) | ||
395 | ahd_outb(ahd, HCNTRL, ahd->unpause); | ||
396 | |||
397 | ahd_known_modes(ahd, AHD_MODE_UNKNOWN, AHD_MODE_UNKNOWN); | ||
398 | } | ||
399 | |||
400 | /*********************** Scatter Gather List Handling *************************/ | ||
401 | void * | ||
402 | ahd_sg_setup(struct ahd_softc *ahd, struct scb *scb, | ||
403 | void *sgptr, dma_addr_t addr, bus_size_t len, int last) | ||
404 | { | ||
405 | scb->sg_count++; | ||
406 | if (sizeof(dma_addr_t) > 4 | ||
407 | && (ahd->flags & AHD_64BIT_ADDRESSING) != 0) { | ||
408 | struct ahd_dma64_seg *sg; | ||
409 | |||
410 | sg = (struct ahd_dma64_seg *)sgptr; | ||
411 | sg->addr = ahd_htole64(addr); | ||
412 | sg->len = ahd_htole32(len | (last ? AHD_DMA_LAST_SEG : 0)); | ||
413 | return (sg + 1); | ||
414 | } else { | ||
415 | struct ahd_dma_seg *sg; | ||
416 | |||
417 | sg = (struct ahd_dma_seg *)sgptr; | ||
418 | sg->addr = ahd_htole32(addr & 0xFFFFFFFF); | ||
419 | sg->len = ahd_htole32(len | ((addr >> 8) & 0x7F000000) | ||
420 | | (last ? AHD_DMA_LAST_SEG : 0)); | ||
421 | return (sg + 1); | ||
422 | } | ||
423 | } | ||
424 | |||
425 | void | ||
426 | ahd_setup_scb_common(struct ahd_softc *ahd, struct scb *scb) | ||
427 | { | ||
428 | /* XXX Handle target mode SCBs. */ | ||
429 | scb->crc_retry_count = 0; | ||
430 | if ((scb->flags & SCB_PACKETIZED) != 0) { | ||
431 | /* XXX what about ACA?? It is type 4, but TAG_TYPE == 0x3. */ | ||
432 | scb->hscb->task_attribute = scb->hscb->control & SCB_TAG_TYPE; | ||
433 | } else { | ||
434 | if (ahd_get_transfer_length(scb) & 0x01) | ||
435 | scb->hscb->task_attribute = SCB_XFERLEN_ODD; | ||
436 | else | ||
437 | scb->hscb->task_attribute = 0; | ||
438 | } | ||
439 | |||
440 | if (scb->hscb->cdb_len <= MAX_CDB_LEN_WITH_SENSE_ADDR | ||
441 | || (scb->hscb->cdb_len & SCB_CDB_LEN_PTR) != 0) | ||
442 | scb->hscb->shared_data.idata.cdb_plus_saddr.sense_addr = | ||
443 | ahd_htole32(scb->sense_busaddr); | ||
444 | } | ||
445 | |||
446 | void | ||
447 | ahd_setup_data_scb(struct ahd_softc *ahd, struct scb *scb) | ||
448 | { | ||
449 | /* | ||
450 | * Copy the first SG into the "current" data ponter area. | ||
451 | */ | ||
452 | if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) { | ||
453 | struct ahd_dma64_seg *sg; | ||
454 | |||
455 | sg = (struct ahd_dma64_seg *)scb->sg_list; | ||
456 | scb->hscb->dataptr = sg->addr; | ||
457 | scb->hscb->datacnt = sg->len; | ||
458 | } else { | ||
459 | struct ahd_dma_seg *sg; | ||
460 | uint32_t *dataptr_words; | ||
461 | |||
462 | sg = (struct ahd_dma_seg *)scb->sg_list; | ||
463 | dataptr_words = (uint32_t*)&scb->hscb->dataptr; | ||
464 | dataptr_words[0] = sg->addr; | ||
465 | dataptr_words[1] = 0; | ||
466 | if ((ahd->flags & AHD_39BIT_ADDRESSING) != 0) { | ||
467 | uint64_t high_addr; | ||
468 | |||
469 | high_addr = ahd_le32toh(sg->len) & 0x7F000000; | ||
470 | scb->hscb->dataptr |= ahd_htole64(high_addr << 8); | ||
471 | } | ||
472 | scb->hscb->datacnt = sg->len; | ||
473 | } | ||
474 | /* | ||
475 | * Note where to find the SG entries in bus space. | ||
476 | * We also set the full residual flag which the | ||
477 | * sequencer will clear as soon as a data transfer | ||
478 | * occurs. | ||
479 | */ | ||
480 | scb->hscb->sgptr = ahd_htole32(scb->sg_list_busaddr|SG_FULL_RESID); | ||
481 | } | ||
482 | |||
483 | void | ||
484 | ahd_setup_noxfer_scb(struct ahd_softc *ahd, struct scb *scb) | ||
485 | { | ||
486 | scb->hscb->sgptr = ahd_htole32(SG_LIST_NULL); | ||
487 | scb->hscb->dataptr = 0; | ||
488 | scb->hscb->datacnt = 0; | ||
489 | } | ||
490 | |||
491 | /************************** Memory mapping routines ***************************/ | ||
492 | void * | ||
493 | ahd_sg_bus_to_virt(struct ahd_softc *ahd, struct scb *scb, uint32_t sg_busaddr) | ||
494 | { | ||
495 | dma_addr_t sg_offset; | ||
496 | |||
497 | /* sg_list_phys points to entry 1, not 0 */ | ||
498 | sg_offset = sg_busaddr - (scb->sg_list_busaddr - ahd_sg_size(ahd)); | ||
499 | return ((uint8_t *)scb->sg_list + sg_offset); | ||
500 | } | ||
501 | |||
502 | uint32_t | ||
503 | ahd_sg_virt_to_bus(struct ahd_softc *ahd, struct scb *scb, void *sg) | ||
504 | { | ||
505 | dma_addr_t sg_offset; | ||
506 | |||
507 | /* sg_list_phys points to entry 1, not 0 */ | ||
508 | sg_offset = ((uint8_t *)sg - (uint8_t *)scb->sg_list) | ||
509 | - ahd_sg_size(ahd); | ||
510 | |||
511 | return (scb->sg_list_busaddr + sg_offset); | ||
512 | } | ||
513 | |||
514 | void | ||
515 | ahd_sync_scb(struct ahd_softc *ahd, struct scb *scb, int op) | ||
516 | { | ||
517 | ahd_dmamap_sync(ahd, ahd->scb_data.hscb_dmat, | ||
518 | scb->hscb_map->dmamap, | ||
519 | /*offset*/(uint8_t*)scb->hscb - scb->hscb_map->vaddr, | ||
520 | /*len*/sizeof(*scb->hscb), op); | ||
521 | } | ||
522 | |||
523 | void | ||
524 | ahd_sync_sglist(struct ahd_softc *ahd, struct scb *scb, int op) | ||
525 | { | ||
526 | if (scb->sg_count == 0) | ||
527 | return; | ||
528 | |||
529 | ahd_dmamap_sync(ahd, ahd->scb_data.sg_dmat, | ||
530 | scb->sg_map->dmamap, | ||
531 | /*offset*/scb->sg_list_busaddr - ahd_sg_size(ahd), | ||
532 | /*len*/ahd_sg_size(ahd) * scb->sg_count, op); | ||
533 | } | ||
534 | |||
535 | void | ||
536 | ahd_sync_sense(struct ahd_softc *ahd, struct scb *scb, int op) | ||
537 | { | ||
538 | ahd_dmamap_sync(ahd, ahd->scb_data.sense_dmat, | ||
539 | scb->sense_map->dmamap, | ||
540 | /*offset*/scb->sense_busaddr, | ||
541 | /*len*/AHD_SENSE_BUFSIZE, op); | ||
542 | } | ||
543 | |||
544 | uint32_t | ||
545 | ahd_targetcmd_offset(struct ahd_softc *ahd, u_int index) | ||
546 | { | ||
547 | return (((uint8_t *)&ahd->targetcmds[index]) | ||
548 | - (uint8_t *)ahd->qoutfifo); | ||
549 | } | ||
550 | |||
551 | /*********************** Miscelaneous Support Functions ***********************/ | ||
552 | /* | ||
553 | * Return pointers to the transfer negotiation information | ||
554 | * for the specified our_id/remote_id pair. | ||
555 | */ | ||
556 | struct ahd_initiator_tinfo * | ||
557 | ahd_fetch_transinfo(struct ahd_softc *ahd, char channel, u_int our_id, | ||
558 | u_int remote_id, struct ahd_tmode_tstate **tstate) | ||
559 | { | ||
560 | /* | ||
561 | * Transfer data structures are stored from the perspective | ||
562 | * of the target role. Since the parameters for a connection | ||
563 | * in the initiator role to a given target are the same as | ||
564 | * when the roles are reversed, we pretend we are the target. | ||
565 | */ | ||
566 | if (channel == 'B') | ||
567 | our_id += 8; | ||
568 | *tstate = ahd->enabled_targets[our_id]; | ||
569 | return (&(*tstate)->transinfo[remote_id]); | ||
570 | } | ||
571 | |||
572 | uint16_t | ||
573 | ahd_inw(struct ahd_softc *ahd, u_int port) | ||
574 | { | ||
575 | /* | ||
576 | * Read high byte first as some registers increment | ||
577 | * or have other side effects when the low byte is | ||
578 | * read. | ||
579 | */ | ||
580 | uint16_t r = ahd_inb(ahd, port+1) << 8; | ||
581 | return r | ahd_inb(ahd, port); | ||
582 | } | ||
583 | |||
584 | void | ||
585 | ahd_outw(struct ahd_softc *ahd, u_int port, u_int value) | ||
586 | { | ||
587 | /* | ||
588 | * Write low byte first to accomodate registers | ||
589 | * such as PRGMCNT where the order maters. | ||
590 | */ | ||
591 | ahd_outb(ahd, port, value & 0xFF); | ||
592 | ahd_outb(ahd, port+1, (value >> 8) & 0xFF); | ||
593 | } | ||
594 | |||
595 | uint32_t | ||
596 | ahd_inl(struct ahd_softc *ahd, u_int port) | ||
597 | { | ||
598 | return ((ahd_inb(ahd, port)) | ||
599 | | (ahd_inb(ahd, port+1) << 8) | ||
600 | | (ahd_inb(ahd, port+2) << 16) | ||
601 | | (ahd_inb(ahd, port+3) << 24)); | ||
602 | } | ||
603 | |||
604 | void | ||
605 | ahd_outl(struct ahd_softc *ahd, u_int port, uint32_t value) | ||
606 | { | ||
607 | ahd_outb(ahd, port, (value) & 0xFF); | ||
608 | ahd_outb(ahd, port+1, ((value) >> 8) & 0xFF); | ||
609 | ahd_outb(ahd, port+2, ((value) >> 16) & 0xFF); | ||
610 | ahd_outb(ahd, port+3, ((value) >> 24) & 0xFF); | ||
611 | } | ||
612 | |||
613 | uint64_t | ||
614 | ahd_inq(struct ahd_softc *ahd, u_int port) | ||
615 | { | ||
616 | return ((ahd_inb(ahd, port)) | ||
617 | | (ahd_inb(ahd, port+1) << 8) | ||
618 | | (ahd_inb(ahd, port+2) << 16) | ||
619 | | (ahd_inb(ahd, port+3) << 24) | ||
620 | | (((uint64_t)ahd_inb(ahd, port+4)) << 32) | ||
621 | | (((uint64_t)ahd_inb(ahd, port+5)) << 40) | ||
622 | | (((uint64_t)ahd_inb(ahd, port+6)) << 48) | ||
623 | | (((uint64_t)ahd_inb(ahd, port+7)) << 56)); | ||
624 | } | ||
625 | |||
626 | void | ||
627 | ahd_outq(struct ahd_softc *ahd, u_int port, uint64_t value) | ||
628 | { | ||
629 | ahd_outb(ahd, port, value & 0xFF); | ||
630 | ahd_outb(ahd, port+1, (value >> 8) & 0xFF); | ||
631 | ahd_outb(ahd, port+2, (value >> 16) & 0xFF); | ||
632 | ahd_outb(ahd, port+3, (value >> 24) & 0xFF); | ||
633 | ahd_outb(ahd, port+4, (value >> 32) & 0xFF); | ||
634 | ahd_outb(ahd, port+5, (value >> 40) & 0xFF); | ||
635 | ahd_outb(ahd, port+6, (value >> 48) & 0xFF); | ||
636 | ahd_outb(ahd, port+7, (value >> 56) & 0xFF); | ||
637 | } | ||
638 | |||
639 | u_int | ||
640 | ahd_get_scbptr(struct ahd_softc *ahd) | ||
641 | { | ||
642 | AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK), | ||
643 | ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK)); | ||
644 | return (ahd_inb(ahd, SCBPTR) | (ahd_inb(ahd, SCBPTR + 1) << 8)); | ||
645 | } | ||
646 | |||
647 | void | ||
648 | ahd_set_scbptr(struct ahd_softc *ahd, u_int scbptr) | ||
649 | { | ||
650 | AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK), | ||
651 | ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK)); | ||
652 | ahd_outb(ahd, SCBPTR, scbptr & 0xFF); | ||
653 | ahd_outb(ahd, SCBPTR+1, (scbptr >> 8) & 0xFF); | ||
654 | } | ||
655 | |||
656 | u_int | ||
657 | ahd_get_hnscb_qoff(struct ahd_softc *ahd) | ||
658 | { | ||
659 | return (ahd_inw_atomic(ahd, HNSCB_QOFF)); | ||
660 | } | ||
270 | 661 | ||
662 | void | ||
663 | ahd_set_hnscb_qoff(struct ahd_softc *ahd, u_int value) | ||
664 | { | ||
665 | ahd_outw_atomic(ahd, HNSCB_QOFF, value); | ||
666 | } | ||
667 | |||
668 | u_int | ||
669 | ahd_get_hescb_qoff(struct ahd_softc *ahd) | ||
670 | { | ||
671 | return (ahd_inb(ahd, HESCB_QOFF)); | ||
672 | } | ||
673 | |||
674 | void | ||
675 | ahd_set_hescb_qoff(struct ahd_softc *ahd, u_int value) | ||
676 | { | ||
677 | ahd_outb(ahd, HESCB_QOFF, value); | ||
678 | } | ||
679 | |||
680 | u_int | ||
681 | ahd_get_snscb_qoff(struct ahd_softc *ahd) | ||
682 | { | ||
683 | u_int oldvalue; | ||
684 | |||
685 | AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK); | ||
686 | oldvalue = ahd_inw(ahd, SNSCB_QOFF); | ||
687 | ahd_outw(ahd, SNSCB_QOFF, oldvalue); | ||
688 | return (oldvalue); | ||
689 | } | ||
690 | |||
691 | void | ||
692 | ahd_set_snscb_qoff(struct ahd_softc *ahd, u_int value) | ||
693 | { | ||
694 | AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK); | ||
695 | ahd_outw(ahd, SNSCB_QOFF, value); | ||
696 | } | ||
697 | |||
698 | u_int | ||
699 | ahd_get_sescb_qoff(struct ahd_softc *ahd) | ||
700 | { | ||
701 | AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK); | ||
702 | return (ahd_inb(ahd, SESCB_QOFF)); | ||
703 | } | ||
704 | |||
705 | void | ||
706 | ahd_set_sescb_qoff(struct ahd_softc *ahd, u_int value) | ||
707 | { | ||
708 | AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK); | ||
709 | ahd_outb(ahd, SESCB_QOFF, value); | ||
710 | } | ||
711 | |||
712 | u_int | ||
713 | ahd_get_sdscb_qoff(struct ahd_softc *ahd) | ||
714 | { | ||
715 | AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK); | ||
716 | return (ahd_inb(ahd, SDSCB_QOFF) | (ahd_inb(ahd, SDSCB_QOFF + 1) << 8)); | ||
717 | } | ||
718 | |||
719 | void | ||
720 | ahd_set_sdscb_qoff(struct ahd_softc *ahd, u_int value) | ||
721 | { | ||
722 | AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK); | ||
723 | ahd_outb(ahd, SDSCB_QOFF, value & 0xFF); | ||
724 | ahd_outb(ahd, SDSCB_QOFF+1, (value >> 8) & 0xFF); | ||
725 | } | ||
726 | |||
727 | u_int | ||
728 | ahd_inb_scbram(struct ahd_softc *ahd, u_int offset) | ||
729 | { | ||
730 | u_int value; | ||
731 | |||
732 | /* | ||
733 | * Workaround PCI-X Rev A. hardware bug. | ||
734 | * After a host read of SCB memory, the chip | ||
735 | * may become confused into thinking prefetch | ||
736 | * was required. This starts the discard timer | ||
737 | * running and can cause an unexpected discard | ||
738 | * timer interrupt. The work around is to read | ||
739 | * a normal register prior to the exhaustion of | ||
740 | * the discard timer. The mode pointer register | ||
741 | * has no side effects and so serves well for | ||
742 | * this purpose. | ||
743 | * | ||
744 | * Razor #528 | ||
745 | */ | ||
746 | value = ahd_inb(ahd, offset); | ||
747 | if ((ahd->bugs & AHD_PCIX_SCBRAM_RD_BUG) != 0) | ||
748 | ahd_inb(ahd, MODE_PTR); | ||
749 | return (value); | ||
750 | } | ||
751 | |||
752 | u_int | ||
753 | ahd_inw_scbram(struct ahd_softc *ahd, u_int offset) | ||
754 | { | ||
755 | return (ahd_inb_scbram(ahd, offset) | ||
756 | | (ahd_inb_scbram(ahd, offset+1) << 8)); | ||
757 | } | ||
758 | |||
759 | uint32_t | ||
760 | ahd_inl_scbram(struct ahd_softc *ahd, u_int offset) | ||
761 | { | ||
762 | return (ahd_inw_scbram(ahd, offset) | ||
763 | | (ahd_inw_scbram(ahd, offset+2) << 16)); | ||
764 | } | ||
765 | |||
766 | uint64_t | ||
767 | ahd_inq_scbram(struct ahd_softc *ahd, u_int offset) | ||
768 | { | ||
769 | return (ahd_inl_scbram(ahd, offset) | ||
770 | | ((uint64_t)ahd_inl_scbram(ahd, offset+4)) << 32); | ||
771 | } | ||
772 | |||
773 | struct scb * | ||
774 | ahd_lookup_scb(struct ahd_softc *ahd, u_int tag) | ||
775 | { | ||
776 | struct scb* scb; | ||
777 | |||
778 | if (tag >= AHD_SCB_MAX) | ||
779 | return (NULL); | ||
780 | scb = ahd->scb_data.scbindex[tag]; | ||
781 | if (scb != NULL) | ||
782 | ahd_sync_scb(ahd, scb, | ||
783 | BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); | ||
784 | return (scb); | ||
785 | } | ||
786 | |||
787 | void | ||
788 | ahd_swap_with_next_hscb(struct ahd_softc *ahd, struct scb *scb) | ||
789 | { | ||
790 | struct hardware_scb *q_hscb; | ||
791 | struct map_node *q_hscb_map; | ||
792 | uint32_t saved_hscb_busaddr; | ||
793 | |||
794 | /* | ||
795 | * Our queuing method is a bit tricky. The card | ||
796 | * knows in advance which HSCB (by address) to download, | ||
797 | * and we can't disappoint it. To achieve this, the next | ||
798 | * HSCB to download is saved off in ahd->next_queued_hscb. | ||
799 | * When we are called to queue "an arbitrary scb", | ||
800 | * we copy the contents of the incoming HSCB to the one | ||
801 | * the sequencer knows about, swap HSCB pointers and | ||
802 | * finally assign the SCB to the tag indexed location | ||
803 | * in the scb_array. This makes sure that we can still | ||
804 | * locate the correct SCB by SCB_TAG. | ||
805 | */ | ||
806 | q_hscb = ahd->next_queued_hscb; | ||
807 | q_hscb_map = ahd->next_queued_hscb_map; | ||
808 | saved_hscb_busaddr = q_hscb->hscb_busaddr; | ||
809 | memcpy(q_hscb, scb->hscb, sizeof(*scb->hscb)); | ||
810 | q_hscb->hscb_busaddr = saved_hscb_busaddr; | ||
811 | q_hscb->next_hscb_busaddr = scb->hscb->hscb_busaddr; | ||
812 | |||
813 | /* Now swap HSCB pointers. */ | ||
814 | ahd->next_queued_hscb = scb->hscb; | ||
815 | ahd->next_queued_hscb_map = scb->hscb_map; | ||
816 | scb->hscb = q_hscb; | ||
817 | scb->hscb_map = q_hscb_map; | ||
818 | |||
819 | /* Now define the mapping from tag to SCB in the scbindex */ | ||
820 | ahd->scb_data.scbindex[SCB_GET_TAG(scb)] = scb; | ||
821 | } | ||
822 | |||
823 | /* | ||
824 | * Tell the sequencer about a new transaction to execute. | ||
825 | */ | ||
826 | void | ||
827 | ahd_queue_scb(struct ahd_softc *ahd, struct scb *scb) | ||
828 | { | ||
829 | ahd_swap_with_next_hscb(ahd, scb); | ||
830 | |||
831 | if (SCBID_IS_NULL(SCB_GET_TAG(scb))) | ||
832 | panic("Attempt to queue invalid SCB tag %x\n", | ||
833 | SCB_GET_TAG(scb)); | ||
834 | |||
835 | /* | ||
836 | * Keep a history of SCBs we've downloaded in the qinfifo. | ||
837 | */ | ||
838 | ahd->qinfifo[AHD_QIN_WRAP(ahd->qinfifonext)] = SCB_GET_TAG(scb); | ||
839 | ahd->qinfifonext++; | ||
840 | |||
841 | if (scb->sg_count != 0) | ||
842 | ahd_setup_data_scb(ahd, scb); | ||
843 | else | ||
844 | ahd_setup_noxfer_scb(ahd, scb); | ||
845 | ahd_setup_scb_common(ahd, scb); | ||
846 | |||
847 | /* | ||
848 | * Make sure our data is consistent from the | ||
849 | * perspective of the adapter. | ||
850 | */ | ||
851 | ahd_sync_scb(ahd, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); | ||
852 | |||
853 | #ifdef AHD_DEBUG | ||
854 | if ((ahd_debug & AHD_SHOW_QUEUE) != 0) { | ||
855 | uint64_t host_dataptr; | ||
856 | |||
857 | host_dataptr = ahd_le64toh(scb->hscb->dataptr); | ||
858 | printf("%s: Queueing SCB %d:0x%x bus addr 0x%x - 0x%x%x/0x%x\n", | ||
859 | ahd_name(ahd), | ||
860 | SCB_GET_TAG(scb), scb->hscb->scsiid, | ||
861 | ahd_le32toh(scb->hscb->hscb_busaddr), | ||
862 | (u_int)((host_dataptr >> 32) & 0xFFFFFFFF), | ||
863 | (u_int)(host_dataptr & 0xFFFFFFFF), | ||
864 | ahd_le32toh(scb->hscb->datacnt)); | ||
865 | } | ||
866 | #endif | ||
867 | /* Tell the adapter about the newly queued SCB */ | ||
868 | ahd_set_hnscb_qoff(ahd, ahd->qinfifonext); | ||
869 | } | ||
870 | |||
871 | /************************** Interrupt Processing ******************************/ | ||
872 | void | ||
873 | ahd_sync_qoutfifo(struct ahd_softc *ahd, int op) | ||
874 | { | ||
875 | ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_map.dmamap, | ||
876 | /*offset*/0, | ||
877 | /*len*/AHD_SCB_MAX * sizeof(struct ahd_completion), op); | ||
878 | } | ||
879 | |||
880 | void | ||
881 | ahd_sync_tqinfifo(struct ahd_softc *ahd, int op) | ||
882 | { | ||
883 | #ifdef AHD_TARGET_MODE | ||
884 | if ((ahd->flags & AHD_TARGETROLE) != 0) { | ||
885 | ahd_dmamap_sync(ahd, ahd->shared_data_dmat, | ||
886 | ahd->shared_data_map.dmamap, | ||
887 | ahd_targetcmd_offset(ahd, 0), | ||
888 | sizeof(struct target_cmd) * AHD_TMODE_CMDS, | ||
889 | op); | ||
890 | } | ||
891 | #endif | ||
892 | } | ||
893 | |||
894 | /* | ||
895 | * See if the firmware has posted any completed commands | ||
896 | * into our in-core command complete fifos. | ||
897 | */ | ||
898 | #define AHD_RUN_QOUTFIFO 0x1 | ||
899 | #define AHD_RUN_TQINFIFO 0x2 | ||
900 | u_int | ||
901 | ahd_check_cmdcmpltqueues(struct ahd_softc *ahd) | ||
902 | { | ||
903 | u_int retval; | ||
904 | |||
905 | retval = 0; | ||
906 | ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_map.dmamap, | ||
907 | /*offset*/ahd->qoutfifonext * sizeof(*ahd->qoutfifo), | ||
908 | /*len*/sizeof(*ahd->qoutfifo), BUS_DMASYNC_POSTREAD); | ||
909 | if (ahd->qoutfifo[ahd->qoutfifonext].valid_tag | ||
910 | == ahd->qoutfifonext_valid_tag) | ||
911 | retval |= AHD_RUN_QOUTFIFO; | ||
912 | #ifdef AHD_TARGET_MODE | ||
913 | if ((ahd->flags & AHD_TARGETROLE) != 0 | ||
914 | && (ahd->flags & AHD_TQINFIFO_BLOCKED) == 0) { | ||
915 | ahd_dmamap_sync(ahd, ahd->shared_data_dmat, | ||
916 | ahd->shared_data_map.dmamap, | ||
917 | ahd_targetcmd_offset(ahd, ahd->tqinfifofnext), | ||
918 | /*len*/sizeof(struct target_cmd), | ||
919 | BUS_DMASYNC_POSTREAD); | ||
920 | if (ahd->targetcmds[ahd->tqinfifonext].cmd_valid != 0) | ||
921 | retval |= AHD_RUN_TQINFIFO; | ||
922 | } | ||
923 | #endif | ||
924 | return (retval); | ||
925 | } | ||
926 | |||
927 | /* | ||
928 | * Catch an interrupt from the adapter | ||
929 | */ | ||
930 | int | ||
931 | ahd_intr(struct ahd_softc *ahd) | ||
932 | { | ||
933 | u_int intstat; | ||
934 | |||
935 | if ((ahd->pause & INTEN) == 0) { | ||
936 | /* | ||
937 | * Our interrupt is not enabled on the chip | ||
938 | * and may be disabled for re-entrancy reasons, | ||
939 | * so just return. This is likely just a shared | ||
940 | * interrupt. | ||
941 | */ | ||
942 | return (0); | ||
943 | } | ||
944 | |||
945 | /* | ||
946 | * Instead of directly reading the interrupt status register, | ||
947 | * infer the cause of the interrupt by checking our in-core | ||
948 | * completion queues. This avoids a costly PCI bus read in | ||
949 | * most cases. | ||
950 | */ | ||
951 | if ((ahd->flags & AHD_ALL_INTERRUPTS) == 0 | ||
952 | && (ahd_check_cmdcmpltqueues(ahd) != 0)) | ||
953 | intstat = CMDCMPLT; | ||
954 | else | ||
955 | intstat = ahd_inb(ahd, INTSTAT); | ||
956 | |||
957 | if ((intstat & INT_PEND) == 0) | ||
958 | return (0); | ||
959 | |||
960 | if (intstat & CMDCMPLT) { | ||
961 | ahd_outb(ahd, CLRINT, CLRCMDINT); | ||
962 | |||
963 | /* | ||
964 | * Ensure that the chip sees that we've cleared | ||
965 | * this interrupt before we walk the output fifo. | ||
966 | * Otherwise, we may, due to posted bus writes, | ||
967 | * clear the interrupt after we finish the scan, | ||
968 | * and after the sequencer has added new entries | ||
969 | * and asserted the interrupt again. | ||
970 | */ | ||
971 | if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) { | ||
972 | if (ahd_is_paused(ahd)) { | ||
973 | /* | ||
974 | * Potentially lost SEQINT. | ||
975 | * If SEQINTCODE is non-zero, | ||
976 | * simulate the SEQINT. | ||
977 | */ | ||
978 | if (ahd_inb(ahd, SEQINTCODE) != NO_SEQINT) | ||
979 | intstat |= SEQINT; | ||
980 | } | ||
981 | } else { | ||
982 | ahd_flush_device_writes(ahd); | ||
983 | } | ||
984 | ahd_run_qoutfifo(ahd); | ||
985 | ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket]++; | ||
986 | ahd->cmdcmplt_total++; | ||
987 | #ifdef AHD_TARGET_MODE | ||
988 | if ((ahd->flags & AHD_TARGETROLE) != 0) | ||
989 | ahd_run_tqinfifo(ahd, /*paused*/FALSE); | ||
990 | #endif | ||
991 | } | ||
992 | |||
993 | /* | ||
994 | * Handle statuses that may invalidate our cached | ||
995 | * copy of INTSTAT separately. | ||
996 | */ | ||
997 | if (intstat == 0xFF && (ahd->features & AHD_REMOVABLE) != 0) { | ||
998 | /* Hot eject. Do nothing */ | ||
999 | } else if (intstat & HWERRINT) { | ||
1000 | ahd_handle_hwerrint(ahd); | ||
1001 | } else if ((intstat & (PCIINT|SPLTINT)) != 0) { | ||
1002 | ahd->bus_intr(ahd); | ||
1003 | } else { | ||
1004 | |||
1005 | if ((intstat & SEQINT) != 0) | ||
1006 | ahd_handle_seqint(ahd, intstat); | ||
1007 | |||
1008 | if ((intstat & SCSIINT) != 0) | ||
1009 | ahd_handle_scsiint(ahd, intstat); | ||
1010 | } | ||
1011 | return (1); | ||
1012 | } | ||
1013 | |||
1014 | /******************************** Private Inlines *****************************/ | ||
271 | static __inline void | 1015 | static __inline void |
272 | ahd_assert_atn(struct ahd_softc *ahd) | 1016 | ahd_assert_atn(struct ahd_softc *ahd) |
273 | { | 1017 | { |
@@ -280,7 +1024,7 @@ ahd_assert_atn(struct ahd_softc *ahd) | |||
280 | * are currently in a packetized transfer. We could | 1024 | * are currently in a packetized transfer. We could |
281 | * just as easily be sending or receiving a message. | 1025 | * just as easily be sending or receiving a message. |
282 | */ | 1026 | */ |
283 | static __inline int | 1027 | static int |
284 | ahd_currently_packetized(struct ahd_softc *ahd) | 1028 | ahd_currently_packetized(struct ahd_softc *ahd) |
285 | { | 1029 | { |
286 | ahd_mode_state saved_modes; | 1030 | ahd_mode_state saved_modes; |
@@ -3941,7 +4685,7 @@ ahd_clear_msg_state(struct ahd_softc *ahd) | |||
3941 | */ | 4685 | */ |
3942 | static void | 4686 | static void |
3943 | ahd_handle_message_phase(struct ahd_softc *ahd) | 4687 | ahd_handle_message_phase(struct ahd_softc *ahd) |
3944 | { | 4688 | { |
3945 | struct ahd_devinfo devinfo; | 4689 | struct ahd_devinfo devinfo; |
3946 | u_int bus_phase; | 4690 | u_int bus_phase; |
3947 | int end_session; | 4691 | int end_session; |
@@ -5983,8 +6727,7 @@ found: | |||
5983 | */ | 6727 | */ |
5984 | void | 6728 | void |
5985 | ahd_free_scb(struct ahd_softc *ahd, struct scb *scb) | 6729 | ahd_free_scb(struct ahd_softc *ahd, struct scb *scb) |
5986 | { | 6730 | { |
5987 | |||
5988 | /* Clean up for the next user */ | 6731 | /* Clean up for the next user */ |
5989 | scb->flags = SCB_FLAG_NONE; | 6732 | scb->flags = SCB_FLAG_NONE; |
5990 | scb->hscb->control = 0; | 6733 | scb->hscb->control = 0; |
@@ -6272,6 +7015,24 @@ static const char *termstat_strings[] = { | |||
6272 | "Not Configured" | 7015 | "Not Configured" |
6273 | }; | 7016 | }; |
6274 | 7017 | ||
7018 | /***************************** Timer Facilities *******************************/ | ||
7019 | #define ahd_timer_init init_timer | ||
7020 | #define ahd_timer_stop del_timer_sync | ||
7021 | typedef void ahd_linux_callback_t (u_long); | ||
7022 | |||
7023 | static void | ||
7024 | ahd_timer_reset(ahd_timer_t *timer, int usec, ahd_callback_t *func, void *arg) | ||
7025 | { | ||
7026 | struct ahd_softc *ahd; | ||
7027 | |||
7028 | ahd = (struct ahd_softc *)arg; | ||
7029 | del_timer(timer); | ||
7030 | timer->data = (u_long)arg; | ||
7031 | timer->expires = jiffies + (usec * HZ)/1000000; | ||
7032 | timer->function = (ahd_linux_callback_t*)func; | ||
7033 | add_timer(timer); | ||
7034 | } | ||
7035 | |||
6275 | /* | 7036 | /* |
6276 | * Start the board, ready for normal operation | 7037 | * Start the board, ready for normal operation |
6277 | */ | 7038 | */ |