⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 aic79xx_core.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
			ahd_outw(ahd, SCB_NEXT_COMPLETE, comp_head);			if (SCBID_IS_NULL(comp_head))				ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD,					 SCB_GET_TAG(scb));		} else			ahd_complete_scb(ahd, scb);	}	ahd_set_scbptr(ahd, saved_scbptr);	/*	 * Setup for command channel portion of flush.	 */	ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);	/*	 * Wait for any inprogress DMA to complete and clear DMA state	 * if this if for an SCB in the qinfifo.	 */	while (((ccscbctl = ahd_inb(ahd, CCSCBCTL)) & (CCARREN|CCSCBEN)) != 0) {		if ((ccscbctl & (CCSCBDIR|CCARREN)) == (CCSCBDIR|CCARREN)) {			if ((ccscbctl & ARRDONE) != 0)				break;		} else if ((ccscbctl & CCSCBDONE) != 0)			break;		ahd_delay(200);	}	if ((ccscbctl & CCSCBDIR) != 0)		ahd_outb(ahd, CCSCBCTL, ccscbctl & ~(CCARREN|CCSCBEN));	saved_scbptr = ahd_get_scbptr(ahd);	/*	 * Manually update/complete any completed SCBs that are waiting to be	 * DMA'ed back up to the host.	 */	scbid = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD);	while (!SCBID_IS_NULL(scbid)) {		uint8_t *hscb_ptr;		u_int	 i;				ahd_set_scbptr(ahd, scbid);		next_scbid = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);		scb = ahd_lookup_scb(ahd, scbid);		if (scb == NULL) {			printf("%s: Warning - DMA-up and complete "			       "SCB %d invalid\n", ahd_name(ahd), scbid);			continue;		}		hscb_ptr = (uint8_t *)scb->hscb;		for (i = 0; i < sizeof(struct hardware_scb); i++)			*hscb_ptr++ = ahd_inb_scbram(ahd, SCB_BASE + i);		ahd_complete_scb(ahd, scb);		scbid = next_scbid;	}	ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD, SCB_LIST_NULL);	scbid = ahd_inw(ahd, COMPLETE_SCB_HEAD);	while (!SCBID_IS_NULL(scbid)) {		ahd_set_scbptr(ahd, scbid);		next_scbid = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);		scb = ahd_lookup_scb(ahd, scbid);		if (scb == NULL) {			printf("%s: Warning - Complete SCB %d invalid\n",			       ahd_name(ahd), scbid);			continue;		}		ahd_complete_scb(ahd, scb);		scbid = next_scbid;	}	ahd_outw(ahd, COMPLETE_SCB_HEAD, SCB_LIST_NULL);	/*	 * Restore state.	 */	ahd_set_scbptr(ahd, saved_scbptr);	ahd_restore_modes(ahd, saved_modes);	ahd->flags |= AHD_UPDATE_PEND_CMDS;}/* * Determine if an SCB for a packetized transaction * is active in a FIFO. */static intahd_scb_active_in_fifo(struct ahd_softc *ahd, struct scb *scb){	/*	 * The FIFO is only active for our transaction if	 * the SCBPTR matches the SCB's ID and the firmware	 * has installed a handler for the FIFO or we have	 * a pending SAVEPTRS or CFG4DATA interrupt.	 */	if (ahd_get_scbptr(ahd) != SCB_GET_TAG(scb)	 || ((ahd_inb(ahd, LONGJMP_ADDR+1) & INVALID_ADDR) != 0	  && (ahd_inb(ahd, SEQINTSRC) & (CFG4DATA|SAVEPTRS)) == 0))		return (0);	return (1);}/* * Run a data fifo to completion for a transaction we know * has completed across the SCSI bus (good status has been * received).  We are already set to the correct FIFO mode * on entry to this routine. * * This function attempts to operate exactly as the firmware * would when running this FIFO.  Care must be taken to update * this routine any time the firmware's FIFO algorithm is * changed. */static voidahd_run_data_fifo(struct ahd_softc *ahd, struct scb *scb){	u_int seqintsrc;	while (1) {		seqintsrc = ahd_inb(ahd, SEQINTSRC);		if ((seqintsrc & CFG4DATA) != 0) {			uint32_t datacnt;			uint32_t sgptr;			/*			 * Clear full residual flag.			 */			sgptr = ahd_inl_scbram(ahd, SCB_SGPTR) & ~SG_FULL_RESID;			ahd_outb(ahd, SCB_SGPTR, sgptr);			/*			 * Load datacnt and address.			 */			datacnt = ahd_inl_scbram(ahd, SCB_DATACNT);			if ((datacnt & AHD_DMA_LAST_SEG) != 0) {				sgptr |= LAST_SEG;				ahd_outb(ahd, SG_STATE, 0);			} else				ahd_outb(ahd, SG_STATE, LOADING_NEEDED);			ahd_outq(ahd, HADDR, ahd_inq_scbram(ahd, SCB_DATAPTR));			ahd_outl(ahd, HCNT, datacnt & AHD_SG_LEN_MASK);			ahd_outb(ahd, SG_CACHE_PRE, sgptr);			ahd_outb(ahd, DFCNTRL, PRELOADEN|SCSIEN|HDMAEN);			/*			 * Initialize Residual Fields.			 */			ahd_outb(ahd, SCB_RESIDUAL_DATACNT+3, datacnt >> 24);			ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr & SG_PTR_MASK);			/*			 * Mark the SCB as having a FIFO in use.			 */			ahd_outb(ahd, SCB_FIFO_USE_COUNT,				 ahd_inb_scbram(ahd, SCB_FIFO_USE_COUNT) + 1);			/*			 * Install a "fake" handler for this FIFO.			 */			ahd_outw(ahd, LONGJMP_ADDR, 0);			/*			 * Notify the hardware that we have satisfied			 * this sequencer interrupt.			 */			ahd_outb(ahd, CLRSEQINTSRC, CLRCFG4DATA);		} else if ((seqintsrc & SAVEPTRS) != 0) {			uint32_t sgptr;			uint32_t resid;			if ((ahd_inb(ahd, LONGJMP_ADDR+1)&INVALID_ADDR) != 0) {				/*				 * Snapshot Save Pointers.  Clear				 * the snapshot and continue.				 */				ahd_outb(ahd, DFFSXFRCTL, CLRCHN);				continue;			}			/*			 * Disable S/G fetch so the DMA engine			 * is available to future users.			 */			if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0)				ahd_outb(ahd, CCSGCTL, 0);			ahd_outb(ahd, SG_STATE, 0);			/*			 * Flush the data FIFO.  Strickly only			 * necessary for Rev A parts.			 */			ahd_outb(ahd, DFCNTRL,				 ahd_inb(ahd, DFCNTRL) | FIFOFLUSH);			/*			 * Calculate residual.			 */			sgptr = ahd_inl_scbram(ahd, SCB_RESIDUAL_SGPTR);			resid = ahd_inl(ahd, SHCNT);			resid |=			    ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT+3) << 24;			ahd_outl(ahd, SCB_RESIDUAL_DATACNT, resid);			if ((ahd_inb(ahd, SG_CACHE_SHADOW) & LAST_SEG) == 0) {				/*				 * Must back up to the correct S/G element.				 * Typically this just means resetting our				 * low byte to the offset in the SG_CACHE,				 * but if we wrapped, we have to correct				 * the other bytes of the sgptr too.				 */				if ((ahd_inb(ahd, SG_CACHE_SHADOW) & 0x80) != 0				 && (sgptr & 0x80) == 0)					sgptr -= 0x100;				sgptr &= ~0xFF;				sgptr |= ahd_inb(ahd, SG_CACHE_SHADOW)				       & SG_ADDR_MASK;				ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr);				ahd_outb(ahd, SCB_RESIDUAL_DATACNT + 3, 0);			} else if ((resid & AHD_SG_LEN_MASK) == 0) {				ahd_outb(ahd, SCB_RESIDUAL_SGPTR,					 sgptr | SG_LIST_NULL);			}			/*			 * Save Pointers.			 */			ahd_outq(ahd, SCB_DATAPTR, ahd_inq(ahd, SHADDR));			ahd_outl(ahd, SCB_DATACNT, resid);			ahd_outl(ahd, SCB_SGPTR, sgptr);			ahd_outb(ahd, CLRSEQINTSRC, CLRSAVEPTRS);			ahd_outb(ahd, SEQIMODE,				 ahd_inb(ahd, SEQIMODE) | ENSAVEPTRS);			/*			 * If the data is to the SCSI bus, we are			 * done, otherwise wait for FIFOEMP.			 */			if ((ahd_inb(ahd, DFCNTRL) & DIRECTION) != 0)				break;		} else if ((ahd_inb(ahd, SG_STATE) & LOADING_NEEDED) != 0) {			uint32_t sgptr;			uint64_t data_addr;			uint32_t data_len;			u_int	 dfcntrl;			/*			 * Disable S/G fetch so the DMA engine			 * is available to future users.			 */			if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0) {				ahd_outb(ahd, CCSGCTL, 0);				ahd_outb(ahd, SG_STATE, LOADING_NEEDED);			}			/*			 * Wait for the DMA engine to notice that the			 * host transfer is enabled and that there is			 * space in the S/G FIFO for new segments before			 * loading more segments.			 */			if ((ahd_inb(ahd, DFSTATUS) & PRELOAD_AVAIL) == 0)				continue;			if ((ahd_inb(ahd, DFCNTRL) & HDMAENACK) == 0)				continue;			/*			 * Determine the offset of the next S/G			 * element to load.			 */			sgptr = ahd_inl_scbram(ahd, SCB_RESIDUAL_SGPTR);			sgptr &= SG_PTR_MASK;			if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {				struct ahd_dma64_seg *sg;				sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);				data_addr = sg->addr;				data_len = sg->len;				sgptr += sizeof(*sg);			} else {				struct	ahd_dma_seg *sg;				sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);				data_addr = sg->len & AHD_SG_HIGH_ADDR_MASK;				data_addr <<= 8;				data_addr |= sg->addr;				data_len = sg->len;				sgptr += sizeof(*sg);			}			/*			 * Update residual information.			 */			ahd_outb(ahd, SCB_RESIDUAL_DATACNT+3, data_len >> 24);			ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr);			/*			 * Load the S/G.			 */			if (data_len & AHD_DMA_LAST_SEG) {				sgptr |= LAST_SEG;				ahd_outb(ahd, SG_STATE, 0);			}			ahd_outq(ahd, HADDR, data_addr);			ahd_outl(ahd, HCNT, data_len & AHD_SG_LEN_MASK);			ahd_outb(ahd, SG_CACHE_PRE, sgptr & 0xFF);			/*			 * Advertise the segment to the hardware.			 */			dfcntrl = ahd_inb(ahd, DFCNTRL)|PRELOADEN|HDMAEN;			if ((ahd->features & AHD_NEW_DFCNTRL_OPTS)!=0) {				/*				 * Use SCSIENWRDIS so that SCSIEN				 * is never modified by this				 * operation.				 */				dfcntrl |= SCSIENWRDIS;			}			ahd_outb(ahd, DFCNTRL, dfcntrl);		} else if ((ahd_inb(ahd, SG_CACHE_SHADOW)			 & LAST_SEG_DONE) != 0) {			/*			 * Transfer completed to the end of SG list			 * and has flushed to the host.			 */			ahd_outb(ahd, SCB_SGPTR,				 ahd_inb_scbram(ahd, SCB_SGPTR) | SG_LIST_NULL);			break;		} else if ((ahd_inb(ahd, DFSTATUS) & FIFOEMP) != 0) {			break;		}		ahd_delay(200);	}	/*	 * Clear any handler for this FIFO, decrement	 * the FIFO use count for the SCB, and release	 * the FIFO.	 */	ahd_outb(ahd, LONGJMP_ADDR + 1, INVALID_ADDR);	ahd_outb(ahd, SCB_FIFO_USE_COUNT,		 ahd_inb_scbram(ahd, SCB_FIFO_USE_COUNT) - 1);	ahd_outb(ahd, DFFSXFRCTL, CLRCHN);}voidahd_run_qoutfifo(struct ahd_softc *ahd){	struct scb *scb;	u_int  scb_index;	if ((ahd->flags & AHD_RUNNING_QOUTFIFO) != 0)		panic("ahd_run_qoutfifo recursion");	ahd->flags |= AHD_RUNNING_QOUTFIFO;	ahd_sync_qoutfifo(ahd, BUS_DMASYNC_POSTREAD);	while ((ahd->qoutfifo[ahd->qoutfifonext]	     & QOUTFIFO_ENTRY_VALID_LE) == ahd->qoutfifonext_valid_tag) {		scb_index = ahd_le16toh(ahd->qoutfifo[ahd->qoutfifonext]				      & ~QOUTFIFO_ENTRY_VALID_LE);		scb = ahd_lookup_scb(ahd, scb_index);		if (scb == NULL) {			printf("%s: WARNING no command for scb %d "			       "(cmdcmplt)\nQOUTPOS = %d\n",			       ahd_name(ahd), scb_index,			       ahd->qoutfifonext);			ahd_dump_card_state(ahd);		} else			ahd_complete_scb(ahd, scb);		ahd->qoutfifonext = (ahd->qoutfifonext+1) & (AHD_QOUT_SIZE-1);		if (ahd->qoutfifonext == 0)			ahd->qoutfifonext_valid_tag ^= QOUTFIFO_ENTRY_VALID_LE;	}	ahd->flags &= ~AHD_RUNNING_QOUTFIFO;}/************************* Interrupt Handling *********************************/voidahd_handle_hwerrint(struct ahd_softc *ahd){	/*	 * Some catastrophic hardware error has occurred.	 * Print it for the user and disable the controller.	 */	int i;	int error;	error = ahd_inb(ahd, ERROR);	for (i = 0; i < num_errors; i++) {		if ((error & ahd_hard_errors[i].errno) != 0)			printf("%s: hwerrint, %s\n",			       ahd_name(ahd), ahd_hard_errors[i].errmesg);	}	ahd_dump_card_state(ahd);	panic("BRKADRINT");	/* Tell everyone that this HBA is no longer available */	ahd_abort_scbs(ahd, CAM_TARGET_WILDCARD, ALL_CHANNELS,		       CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,		       CAM_NO_HBA);	/* Tell the system that this controller has gone away. */	ahd_free(ahd);}voidahd_handle_seqint(struct ahd_softc *ahd, u_int intstat){	u_int seqintcode;	/*	 * Save the sequencer interrupt code and clear the SEQINT	 * bit. We will unpause the sequencer, if appropriate,	 * after servicing the request.	 */	seqintcode = ahd_inb(ahd, SEQINTCODE);	ahd_outb(ahd, CLRINT, CLRSEQINT);	if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {		/*		 * Unpause the sequencer and let it clear		 * SEQINT by writing NO_SEQINT to it.  This		 * will cause the sequencer to be paused again,		 * which is the expected state of this routine.		 */		ahd_unpause(ahd);		while (!ahd_is_paused(ahd))			;		ahd_outb(ahd, CLRINT, CLRSEQINT);	}	ahd_update_modes(ahd);#ifdef AHD_DEBUG	if ((ahd_debug & AHD_SHOW_MISC) != 0)		printf("%s: Handle Seqint Called for code %d\n",		       ahd_name(ahd), seqintcode);#endif	switch (seqintcode) {	case BAD_SCB_STATUS:	{

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -