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 + -
显示快捷键?