ide-ep93xx.c
来自「一个2.4.21版本的嵌入式linux内核」· C语言 代码 · 共 2,386 行 · 第 1/5 页
C
2,386 行
sg[nents].address = virt_addr;
sg[nents].length = size;
nents++;
} while (buf_head != NULL);
/*
* This call to map_sg will return the number of entries
* for which DMAable memory could be mapped.
*/
return ep93xx_map_sg(sg, nents, hwif->sg_dma_direction);
}
/*****************************************************************************
*
* ide_build_dmatable() prepares a dma request.
* Returns 0 if all went okay, returns 1 otherwise.
*
****************************************************************************/
static int
ide_build_dmatable(ide_drive_t *drive)
{
unsigned int *table = HWIF(drive)->dmatable_cpu = g_dma_table_base;
unsigned int count = 0;
int i;
struct scatterlist *sg;
DPRINTK("%s: ide_build_dmatable\n", drive->name);
HWIF(drive)->sg_nents = i = ide_build_sglist(HWIF(drive),
HWGROUP(drive)->rq);
if (!i)
return 0;
sg = HWIF(drive)->sg_table;
while (i && sg_dma_len(sg)) {
u32 cur_addr;
u32 cur_len;
cur_addr = sg_dma_address(sg);
cur_len = sg_dma_len(sg);
/*
* Fill in the dma table, without crossing any 64kB boundaries.
* Most hardware requires 16-bit alignment of all blocks,
* but the trm290 requires 32-bit alignment.
*/
while (cur_len) {
if (count++ >= PRD_ENTRIES) {
printk("%s: DMA table too small\n", drive->name);
goto use_pio_instead;
} else {
u32 xcount, bcount = 0x10000 - (cur_addr & 0xffff);
if (bcount > cur_len)
bcount = cur_len;
*table++ = cur_addr;
xcount = bcount & 0xffff;
if (xcount == 0x0000) {
/*
* Most chipsets correctly interpret a
* length of 0x0000 as 64KB, but at
* least one (e.g. CS5530)
* misinterprets it as zero (!). So
* here we break the 64KB entry into
* two 32KB entries instead.
*/
if (count++ >= PRD_ENTRIES) {
printk("%s: DMA table too small\n",
drive->name);
goto use_pio_instead;
}
*table++ = 0x8000;
*table++ = cur_addr + 0x8000;
xcount = 0x8000;
}
*table++ = xcount;
cur_addr += bcount;
cur_len -= bcount;
}
}
sg++;
i--;
}
if (count) {
*--table |= 0x80000000;
return count;
}
printk("%s: empty DMA table?\n", drive->name);
use_pio_instead:
/*
* Revert to PIO for this request.
*/
HWIF(drive)->sg_dma_active = 0;
return 0;
}
/*****************************************************************************
*
* ide_release_dma()
*
* This function releases the memory allocated for the scatter gather list
* and for the dma table, and frees the dma channel.
*
****************************************************************************/
static void
ep93xx_ide_release_dma(ide_hwif_t *hwif)
{
DPRINTK("%s: ep93xx_ide_release_dma\n", hwif->name);
/*
* Check if we have a valid dma handle.
*/
if (hwif->hw.dma != 0) {
/*
* Pause the DMA channel.
*/
ep93xx_dma_pause(hwif->hw.dma, 1, 0);
/*
* Flush the DMA channel
*/
ep93xx_dma_flush(hwif->hw.dma);
}
}
/*****************************************************************************
*
* ep93xx_ide_callback()
*
* Registered with the ep93xx dma driver and called at the end of the dma
* interrupt handler, this function should process the dma buffers.
*
****************************************************************************/
static void
ep93xx_ide_callback(ep93xx_dma_int_t dma_int, ep93xx_dma_dev_t device,
unsigned int user_data)
{
ide_drive_t *drive = (ide_drive_t *)user_data;
ide_hwif_t *hwif = HWIF(drive);
unsigned int temp;
DPRINTK("ep93xx_ide_callback %d\n", dma_int);
/*
* Retrieve from the dma interface as many used buffers as are
* available.
*/
while (ep93xx_dma_remove_buffer(hwif->hw.dma, &temp) == 0)
g_prd_returned++;
/*
* Add new buffers if we have any available.
*/
while (g_prd_count) {
/*
* Check if this is a read or write operation.
*/
if (hwif->sg_dma_direction == PCI_DMA_FROMDEVICE)
/*
* Set up buffers for a read op.
*/
temp = ep93xx_dma_add_buffer(hwif->hw.dma,
hwif->dma_base,
hwif->dmatable_cpu[0],
hwif->dmatable_cpu[1], 0,
g_prd_count);
else
/*
* Set up buffers for a write op.
*/
temp = ep93xx_dma_add_buffer(hwif->hw.dma,
hwif->dmatable_cpu[0],
hwif->dma_base,
hwif->dmatable_cpu[1], 0,
g_prd_count);
/*
* Add a buffer to the dma interface.
*/
if (temp != 0)
break;
hwif->dmatable_cpu += 2;
/*
* Decrement the count of dmatable entries
*/
g_prd_count--;
}
/*
* See if all of the buffers have been returned.
*/
if (g_prd_returned >= g_prd_total) {
/*
* See if the IDE device is still requesting data.
*/
if (inl(IDECR) & IDECtrl_DMARQ) {
/*
* Indicate that an error has occurred during this
* transfer.
*/
g_bad = 1;
/*
* Add another word to the DMA to try and satisfy the
* device's request.
*/
if (hwif->sg_dma_direction == PCI_DMA_FROMDEVICE)
ep93xx_dma_add_buffer(hwif->hw.dma,
hwif->dma_base,
virt_to_bus(&g_garbage),
4, 0, 0);
else
ep93xx_dma_add_buffer(hwif->hw.dma,
virt_to_bus(&g_garbage),
hwif->dma_base,
4, 0, 0);
}
}
}
/*****************************************************************************
*
* ep93xx_dma_timer_expiry()
*
*
* dma_timer_expiry - handle a DMA timeout
* @drive: Drive that timed out
*
* An IDE DMA transfer timed out. In the event of an error we ask
* the driver to resolve the problem, if a DMA transfer is still
* in progress we continue to wait (arguably we need to add a
* secondary 'I dont care what the drive thinks' timeout here)
* Finally if we have an interrupt we let it complete the I/O.
* But only one time - we clear expiry and if it's still not
* completed after WAIT_CMD, we error and retry in PIO.
* This can occur if an interrupt is lost or due to hang or bugs.
*
*
****************************************************************************/
static int
ep93xx_idedma_timer_expiry(ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
u8 dev_stat = hwif->INB(IDE_ALTSTATUS_REG);
u8 irq_stat = inl(IDECR) & IDECtrl_INTRQ;
DPRINTK(KERN_WARNING "%s: dma_timer_expiry: dev status == 0x%02x,irq= %d\n",
drive->name, dev_stat, irq_stat);
/*
* Clear the expiry handler in case we decide to wait more,
* next time timer expires it is an error
*/
HWGROUP(drive)->expiry = NULL;
/*
* If the interrupt is asserted, call the handler.
*/
if (irq_stat)
HWGROUP(drive)->handler(drive);
/*
* Check if the busy bit or the drq bit is set, indicating that
* a dma transfer is still active, or the IDE interrupt is asserted.
*/
if ( (dev_stat & 0x80) || (dev_stat & 0x08) || irq_stat)
return WAIT_CMD;
/*
* the device is not busy and the interrupt is not asserted, so check
* if there's an error.
*/
if (dev_stat & 0x01) /* ERROR */
return -1;
return 0; /* Unknown status -- reset the bus */
}
/*****************************************************************************
*
* ep93xx_config_ide_device()
*
* This function sets up the ep93xx ide device for a dma transfer by first
* probing to find the best dma mode supported by the device.
*
* Returns a 0 for success, and a 1 otherwise.
*
****************************************************************************/
static unsigned int
ep93xx_config_ide_device(ide_drive_t *drive)
{
unsigned int ulChipID;
byte transfer = 0;
DPRINTK("%s: ep93xx_config_ide_device\n", drive->name);
/*
* Determine the best transfer speed supported. On Rev D1/E0
* the maximum DMA mode is 2. On Rev E1 the maximum UDMA mode is 3.
*/
transfer = ide_dma_speed(drive, 2);
/*
* Do nothing if a DMA mode is not supported or if the drive supports
* MDMA.
*/
if(transfer == XFER_MW_DMA_2 || transfer == XFER_MW_DMA_1 ||
transfer == XFER_MW_DMA_0 || transfer == 0) {
return HWIF(drive)->ide_dma_off(drive);
}
ulChipID = inl(SYSCON_CHIPID);
if(transfer == XFER_UDMA_3 && ((ulChipID & SYSCON_CHIPID_REV_MASK)>>SYSCON_CHIPID_REV_SHIFT) != 0x6)
{
transfer = XFER_UDMA_2;
}
/*
* Configure the drive.
*/
if (ide_config_drive_speed(drive, transfer) == 0) {
/*
* Hold on to this value for use later.
*/
drive->current_speed = transfer;
/*
* Success, so turn on DMA.
*/
return HWIF(drive)->ide_dma_on(drive);
}
else
return HWIF(drive)->ide_dma_off(drive);
}
/*****************************************************************************
*
* ep93xx_set_pio()
*
* Configures the ep93xx controller for a PIO mode transfer.
*
****************************************************************************/
static void
ep93xx_set_pio(void)
{
DPRINTK("ep93xx_set_pio\n");
/*
* Insure that neither device is selected
*/
// if (MODE_SWITCH_FIX)
outl(inl(IDECR) | (IDECtrl_CS0n | IDECtrl_CS1n), IDECR);
/*
* Clear the MDMA and UDMA operation registers.
*/
outl(0, IDEMDMAOP);
outl(0, IDEUDMAOP);
/*
* Reset the UDMA state machine.
*/
outl(IDEUDMADebug_RWOE | IDEUDMADebug_RWPTR | IDEUDMADebug_RWDR |
IDEUDMADebug_RROE | IDEUDMADebug_RRPTR | IDEUDMADebug_RRDR,
IDEUDMADEBUG);
outl(0, IDEUDMADEBUG);
/*
* Enable PIO mode of operation.
*/
outl(IDECfg_PIO | IDECfg_IDEEN | (4 << IDECfg_MODE_SHIFT) |
(1 << IDECfg_WST_SHIFT), IDECFG);
}
/*****************************************************************************
*
* ep93xx_rwproc()
*
* Initializes the ep93xx IDE controller interface with the transfer type,
* transfer mode, and transfer direction.
*
****************************************************************************/
static void
ep93xx_rwproc(ide_drive_t *drive, int action)
{
int speed;
DPRINTK("%s: ep93xx_rwproc\n", drive->name);
/*
* Insure that neither device is selected.
*/
//if (MODE_SWITCH_FIX)
outl(inl(IDECR) | (IDECtrl_CS0n | IDECtrl_CS1n), IDECR);
/*
* Configure the IDE controller for the specified transfer mode.
*/
switch (drive->current_speed)
{
/*
* Configure for an MDMA operation.
*/
case XFER_MW_DMA_0:
case XFER_MW_DMA_1:
case XFER_MW_DMA_2:
printk("BAAAAADDD: someone tries to use MDMA: not supported !!\n");
outl(((drive->current_speed & 3) << IDECfg_MODE_SHIFT) |
IDECfg_MDMA | IDECfg_IDEEN, IDECFG);
outl(action ? IDEMDMAOp_RWOP : 0, IDEMDMAOP);
outl(inl(IDEMDMAOP) | IDEMDMAOp_MEN, IDEMDMAOP);
break;
/*
* Configure for a UDMA operation.
*/
case XFER_UDMA_0:
case XFER_UDMA_1:
case XFER_UDMA_2:
case XFER_UDMA_3:
case XFER_UDMA_4:
speed = drive->current_speed;
speed -=64; //udma 0
if(speed >= 4)
speed = 3; //max udma 2
outl(((speed & 7) << IDECfg_MODE_SHIFT) |
IDECfg_UDMA | IDECfg_IDEEN, IDECFG);
outl(action ? IDEUDMAOp_RWOP : 0, IDEUDMAOP);
outl(inl(IDEUDMAOP) | IDEUDMAOp_UEN, IDEUDMAOP);
break;
default:
break;
}
}
/*****************************************************************************
*
* ep93xx_ide_dma_check()
*
* This function determines if the device supports dma transfers, and if it
* does, then enables dma.
*
****************************************************************************/
static int
ep93xx_ide_dma_check(ide_drive_t *drive)
{
ide_hwif_t * hwif = HWIF(drive);
struct hd_driveid *id = drive->id;
DPRINTK("%s: ep93xx_ide_dma_check\n", drive->name);
if (!id || !(id->capability & 1) || !hwif->autodma) {
/*
* Disable dma for this drive.
*/
hwif->ide_dma_off_quietly(drive);
return 1;
}
/*
* Consult the list of known "bad" drives
*/
if (in_drive_list(id, drive_blacklist)) {
printk("%s: Disabling DMA for %s (blacklisted)\n",
drive->name, id->model);
/*
* Disable dma for this drive.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?