ide-ep93xx.c

来自「一个2.4.21版本的嵌入式linux内核」· C语言 代码 · 共 2,386 行 · 第 1/5 页

C
2,386
字号
	 * Determine the command to be sent to the device.
	 */
	command = (lba48) ? WIN_WRITEDMA_EXT : WIN_WRITEDMA;
	if (rq->cmd == IDE_DRIVE_TASKFILE) {
		ide_task_t *args = rq->special;
		command = args->tfRegister[IDE_COMMAND_OFFSET];
	}

	/*
	 * Send the write dma command to the device.
	 */
	ide_execute_command(drive, command, &ep93xx_ide_dma_intr, 2*WAIT_CMD,
			    &ep93xx_idedma_timer_expiry);

	/*
	 * initiate the dma transfer.
	 */
	return hwif->ide_dma_begin(drive);
}

/*****************************************************************************
 *
 * ep93xx_ide_dma_begin()
 *
 * This function initiates a dma transfer.
 *
 ****************************************************************************/
static int
ep93xx_ide_dma_begin(ide_drive_t *drive)
{
	ide_hwif_t *hwif = HWIF(drive);

	DPRINTK("%s: ep93xx_ide_dma_begin\n", drive->name);

	/*
	 * Insure that neither device is selected
	 */
	//if (MODE_SWITCH_FIX)
	//    outl(inl(IDECR) | (IDECtrl_CS0n | IDECtrl_CS1n), IDECR);
	
	/*
	 * The transfer is not bad yet.
	 */
	g_bad = 0;

	/*
	 * Configure the ep93xx ide controller for a dma operation.
	 */
	if (HWGROUP(drive)->rq->cmd == READ)
		ep93xx_rwproc(drive, 0);
	else
		ep93xx_rwproc(drive, 1);

	/*
	 * Start the dma transfer.
	 */
	ep93xx_dma_start(hwif->hw.dma, 1, NULL);

	return 0;
}

#if 0
/*****************************************************************************
 *
 * ep93xx_ide_dma_end()
 *
 * This function performs any tasks needed to cleanup after a dma transfer.
 * Returns 1 if an error occured, and 0 otherwise.
 *
 ****************************************************************************/
static int
ep93xx_ide_dma_end(ide_drive_t *drive)
{
	ide_hwif_t * hwif = HWIF(drive);
	int i, stat;

	DPRINTK("%s: ep93xx_ide_dma_end\n", drive->name);

	/*
	 * See if there is any data left in UDMA FIFOs.  For a read, first wait
	 * until either the DMA is done or the FIFO is empty.  If there is any
	 * residual data in the FIFO, there was an error in the transfer.
	 */
	if (HWGROUP(drive)->rq->cmd == READ) {
		do {
			i = inl(IDEUDMARFST);
		} while (!ep93xx_dma_is_done(hwif->hw.dma) &&
			 ((i & 15) != ((i >> 4) & 15)));
		udelay(1);
	}
	else
		i = inl(IDEUDMAWFST);
	if ((i & 15) != ((i >> 4) & 15))
		g_bad = 1;
            
	/*
	 * See if all of the buffers were returned by the DMA driver.  If not,
	 * then a transfer error has occurred.
	 */
	if (!ep93xx_dma_is_done(hwif->hw.dma))
		g_bad = 1;

	/*
	 * Put the dma interface into pause mode.
	 */
	ep93xx_dma_pause(hwif->hw.dma, 1, 0);
	ep93xx_dma_flush(hwif->hw.dma);

	/*
	 * Enable PIO mode on the IDE interface.
	 */
	ep93xx_set_pio();

	/*
	 * Indicate there's no dma transfer currently in progress.
	 */
	hwif->sg_dma_active = 0;
	drive->waiting_for_dma = 0;

	/*
	 * Get status from the ide device.
	 */
	stat = HWIF(drive)->INB(IDE_STATUS_REG);

	/*
	 * If this is an ATAPI device and it reported an error, then simply
	 * return a DMA success.
	 */
	if ((drive->media != ide_disk) && (stat & ERR_STAT))
		return 0;

	/*
	 * See if a CRC error occurred.  If so, then a transfer error has
	 * occurred.
	 */
	if ((stat & ERR_STAT) && (HWIF(drive)->INB(IDE_ERROR_REG) & ICRC_ERR))
		g_bad = 1;

	/*
	 * Compute the array index used for this request.
	 */
	i = HWGROUP(drive)->rq->cmd == READ ? 0 : 1;
	i += drive->name[2] == 'a' ? 0 : 2;

	/*
	 * Fail if an error occurred during the DMA.
	 */
	if (g_bad) {
		/*
		 * Increment the number of retries for this request, along with
		 * the number of consecutive errors for this drive.
		 */
		g_retries[i]++;
		g_errors[i]++;

		/*
		 * See if the maximum number of consective errors has occurred.
		 */
		if (g_errors[i] == FAILURES_BEFORE_BACKOFF) {
			/*
			 * Drop down to the next lowest transfer rate, or PIO
			 * if already at the bottom.
			 */
			switch (drive->current_speed) {
			case XFER_UDMA_4:
				ide_config_drive_speed(drive, XFER_UDMA_3);
				break;
			case XFER_UDMA_3:
				ide_config_drive_speed(drive, XFER_UDMA_2);
				break;
			case XFER_UDMA_2:
				ide_config_drive_speed(drive, XFER_UDMA_1);
				break;
			case XFER_UDMA_1:
				ide_config_drive_speed(drive, XFER_UDMA_0);
				break;
			case XFER_UDMA_0:
				HWIF(drive)->ide_dma_off(drive);
				break;
			case XFER_MW_DMA_2:
				ide_config_drive_speed(drive, XFER_MW_DMA_1);
				break;
			case XFER_MW_DMA_1:
				ide_config_drive_speed(drive, XFER_MW_DMA_0);
				break;
			case XFER_MW_DMA_0:
				HWIF(drive)->ide_dma_off(drive);
				break;
			}

			/*
			 * Reset the consecutive error counters for this drive.
			 */
			g_errors[i] = 0;
			g_errors[i ^ 1] = 0;
		}

		/*
		 * Fail the transfer.
		 */
		return 1;
	} else
		/*
		 * Success, so reset the consecutive error counter.
		 */
		g_errors[i] = 0;

	/*
	 * Success.
	 */
	return 0;
}

#else

#define DMA_END_SUCCESS	0
#define DMA_END_FAIL	1

#define DMA_FAIL_CODE_CRC_WRITE			2
#define DMA_FAIL_CODE_CRC_READ			3
#define DMA_FAIL_CODE_BUFFER_BUSY_READ		4
#define DMA_FAIL_CODE_BUFFER_BUSY_WRITE		5
#define DMA_FAIL_CODE_FIFO_STATUS_WRITE		6
#define DMA_FAIL_CODE_FIFO_STATUS_READ		7
            

void dma_end_debug(int failcode, ide_drive_t *drive)
{
    printk("dma error with device %s\n", drive->name);

    printk("DMAMM_1_CONTROL  	:0x%08x \n", inl(DMAMM_1_CONTROL) );           
    printk("DMAMM_1_STATUS  	:0x%08x \n", inl(DMAMM_1_STATUS) );           
    printk("DMAMM_1_BCR0  	:0x%08x \n", inl(DMAMM_1_BCR0) );
    printk("DMAMM_1_SAR_BASE0  	:0x%08x \n", inl(DMAMM_1_SAR_BASE0) );      
    printk("DMAMM_1_SAR_CUR0  	:0x%08x \n", inl(DMAMM_1_SAR_CURRENT0) );
    printk("DMAMM_1_DAR_BASE0  	:0x%08x \n", inl(DMAMM_1_DAR_BASE0) );
    printk("DMAMM_1_DAR_CUR0  	:0x%08x \n", inl(DMAMM_1_DAR_CURRENT0) );

    switch(failcode)
    {
    case DMA_FAIL_CODE_FIFO_STATUS_READ:
        printk("READ ERROR detected - bad UDMA fifo status \n");
	printk("udma read fifo status   :0x%08x \n", inl(IDEUDMARFST) );
    break;    
            

    case DMA_FAIL_CODE_FIFO_STATUS_WRITE:
        printk("WRITE ERROR detected - bad UDMA fifo status \n");
        printk("UDMA write fifo status  :0x%08x \n", inl(IDEUDMAWFST) );
    break;

    case DMA_FAIL_CODE_BUFFER_BUSY_READ:
        printk("READ ERROR detected - DMA is not done \n");                        
        printk("udma read fifo status   :0x%08x \n", inl(IDEUDMARFST) );	    
    break;
    
    case DMA_FAIL_CODE_BUFFER_BUSY_WRITE:
        printk("WRITE ERROR detected - DMA is not done \n");
    	printk("udma write fifo status  :0x%08x\n", inl(IDEUDMAWFST) );
    break;

    case DMA_FAIL_CODE_CRC_WRITE:
	udma_crc_write_errors++;
	printk("udma_crc_write_errors 	:%d\n"     , udma_crc_write_errors);
	printk("udma write fifo status  :0x%08x\n" , inl(IDEUDMAWFST) );
    break;

    case DMA_FAIL_CODE_CRC_READ:
	udma_crc_read_errors++;
	printk("udma_crc_read_errors    :%d\n"     , udma_crc_read_errors);
	printk("udma read fifo status   :0x%08x \n", inl(IDEUDMARFST) );
    break;
    
    default:
	printk("unknown udma error code %d\n", failcode);
    break;
    }

}


static int
ep93xx_ide_dma_end(ide_drive_t *drive)
{
	ide_hwif_t * hwif = HWIF(drive);
	int i, stat;
	int rv;
	int failcode;
	
	rv = DMA_END_SUCCESS;
	failcode = DMA_END_SUCCESS;

	DPRINTK("%s: ep93xx_ide_dma_end\n", drive->name);

	//printk("drive->ep93xx_if_test=0x%08x\n", (int) drive->ep93xx_if_test );
//	if( drive->ep93xx_if_test & 0xffff0000)
//	    ide_ep93xx_check_control(drive);

/* do all read checks we can do here: udma read fifo rptr and write ptr must be equal and the dma engine should be done, too */

    if (HWGROUP(drive)->rq->cmd == READ) 
    {
	
	while(1)
	{
	/*
	 * See if there is any data left in UDMA FIFOs.  For a read, first wait
	 * until either the DMA is done or the FIFO is empty.  If there is any
	 * residual data in the FIFO, there was an error in the transfer.
	 */
	    i = inl(IDEUDMARFST);
	    if( ep93xx_dma_is_done(hwif->hw.dma) )
		break;
	    if ( (i & 15) == ((i >> 4) & 15) )
		break;
	    udelay(1);
	}
	udelay(1);
	i = inl(IDEUDMARFST);
	
	
	
	if ((i & 15) != ((i >> 4) & 15))
	{
    	    g_bad = 1;
	    if(drive->ep93xx_if_test & TEST_FOR_READ)
	    {
                failcode = DMA_FAIL_CODE_FIFO_STATUS_READ;
		dma_end_debug(failcode, drive);
    	    }
	}
	
	/*
	 * See if all of the buffers were returned by the DMA driver.  If not,
	 * then a transfer error has occurred.                                             
	 */                                                                                
	if (!ep93xx_dma_is_done(hwif->hw.dma))                                             
	{                                                                                  
	    g_bad = 1; 
    	    if(drive->ep93xx_if_test & TEST_FOR_READ)
    	    {
        	failcode = DMA_FAIL_CODE_BUFFER_BUSY_READ;
		dma_end_debug(failcode, drive);
	    }
	}

    }

    /* check for udma write bugs */

    if (HWGROUP(drive)->rq->cmd == WRITE)
    {

	while(1)
	{
	/*
	 * See if there is any data left in UDMA FIFOs.  For a read, first wait
	 * until either the DMA is done or the FIFO is empty.  If there is any
	 * residual data in the FIFO, there was an error in the transfer.
	 */
	    i = inl(IDEUDMAWFST);
	    if( ep93xx_dma_is_done(hwif->hw.dma) )
		break;
	    if ( (i & 15) == ((i >> 4) & 15) )
		break;
	    udelay(1);
	}
	udelay(1);
	i = inl(IDEUDMAWFST);


	if (!ep93xx_dma_is_done(hwif->hw.dma))                                             
	{                                                                                  
	    g_bad = 1;                                                                     
    	    if(drive->ep93xx_if_test & TEST_FOR_WRITE)
    	    {
                failcode = DMA_FAIL_CODE_BUFFER_BUSY_WRITE;
		dma_end_debug(failcode, drive);            
    	    }                                                                               
	}
	
	if ( (i & 15) != ((i >> 4) & 15) )
	{
	    g_bad = 1;
    	    if(drive->ep93xx_if_test & TEST_FOR_WRITE)
    	    {
                failcode = DMA_FAIL_CODE_FIFO_STATUS_WRITE;
		dma_end_debug(failcode, drive);            
	    }
	}

    }        

	/*
	 * Put the dma interface into pause mode.
	 */
	ep93xx_dma_pause(hwif->hw.dma, 1, 0);
	ep93xx_dma_flush(hwif->hw.dma);

	/*
	 * Enable PIO mode on the IDE interface.
	 */
	ep93xx_set_pio();

	/*
	 * Indicate there's no dma transfer currently in progress.
	 */
	hwif->sg_dma_active = 0;
	drive->waiting_for_dma = 0;

	/*
	 * Get status from the ide device.
	 */
	stat = HWIF(drive)->INB(IDE_STATUS_REG);

	/*
	 * If this is an ATAPI device and it reported an error, then simply
	 * return a DMA success.
	 */
	if ((drive->media != ide_disk) && (stat & ERR_STAT) && !failcode)
	{
		rv = DMA_END_SUCCESS;
		goto end;
	}
	/*
	 * See if a CRC error occurred.  If so, then a transfer error has
	 * occurred.
	 */
    if ((stat & ERR_STAT) && (HWIF(drive)->INB(IDE_ERROR_REG) & ICRC_ERR))
    {
		g_bad = 1;
	printk("ep93xx dma: IDE device error flag set!\n");	
        
        if(drive->ep93xx_if_test & TEST_FOR_READ)
        {    
            if (HWGROUP(drive)->rq->cmd == READ)
            {
                failcode = DMA_FAIL_CODE_CRC_READ;
		dma_end_debug(failcode, drive);
            }
        }

        if(drive->ep93xx_if_test & TEST_FOR_WRITE)
        {
            if (HWGROUP(drive)->rq->cmd == WRITE)
            {
                failcode = DMA_FAIL_CODE_CRC_WRITE;
		dma_end_debug(failcode, drive);
            }
        }        
    }
    
    
	/*
	 * Compute the array index used for this request.
	 */
	i = HWGROUP(drive)->rq->cmd == READ ? 0 : 1;
	i += drive->name[2] == 'a' ? 0 : 2;

	/*
	 * Fail if an error occurred during the DMA.
	 */
	if (g_bad) {
		/*
		 * Increment the number of retries for this request, along with
		 * the number of consecutive errors for this drive.
		 */
		g_retries[i]++;
		g_errors[i]++;
		printk("g_retries[%d]=%d\n", i, (int) g_retries[i] );
		printk("g_errors[%d]=%d\n", i, (int) g_errors[i] );
		printk("%s: ep93xx_ide_dma_end\n", drive->name);
		/*
		 * See if the maximum number of consective errors has occurred.
		 */
		if (g_errors[i] == FAILURES_BEFORE_BACKOFF) {
			/*
			 * Drop down to the next lowest transfer rate, or PIO

⌨️ 快捷键说明

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