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