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

📄 ide.c

📁 开源的BIOS启动软件
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Linux EBSA285 BIOS IDE driver * * Bits taken from Linux kernel (C) respective authors. * * This is effectively a miniture IDE driver which is capable * of setting up the IDE interface for UDMA modes 0-2, DMA * multiword modes 0-2 or PIO modes 1 through 4. * * It also is capable of reading sectors off the IDE drive into * memory. * * IDE detection error codes: *   0 - success *   1 - unable to send IDENTIFY command *   2 - bad status from IDENTIFY command *   3 - unable to select drive (no interface?) */#define NEW_PCI#include <bios/boot.h>#include <bios/time.h>#include <bios/timer.h>#include <bios/config.h>#include <bios/bootdev.h>#include <bios/malloc.h>#include <bios/string.h>#include <bios/pci.h>#include <bios/stdio.h>#include "ide.h"#ifndef DEBUG#define debug_printf(x...)#endif#define NR_HW		8/* * Wait 45 seconds after powerup for the hard drive to become * ready. */#define MAX_BUSY_WAIT	45 * 100static struct ide_hw	*ide_hw_list[NR_HW];/* * Send a command to the drive and wait for BUSY status */static inline int ide_send_command(struct ide_drive *drive, u8 cmd, int timeout){	struct ide_hw *hw = drive->hw;	ide_outb(hw, cmd, IDE_COMMAND);	timeout += centisecs;	do {		if (timeout < centisecs)			return 1;		wait_cs(5);	} while (ide_inb(hw, drive->statusreg) & BUSY_STAT);	wait_cs(5);	return 0;}/* * check whether status is ok */static inline int ide_status_ok(struct ide_drive *drive, int ok, int bad){	u8 s;	s = ide_inb(drive->hw, IDE_STATUS);	return (s & (ok | bad)) == ok;}/* * select a drive */static inline int ide_select_drive(struct ide_drive *drive){	ide_outb(drive->hw, drive->select, IDE_SELECT);	wait_cs(5);	return ide_inb(drive->hw, IDE_SELECT) == drive->select;}/* * Turn off the DMA capability status bit for this drive. * We also turn of the DMA enable bit. */static void ide_dma_disable(struct ide_drive *drive){	struct ide_hw *hw = drive->hw;	int c;	if (hw->dma_base) {		c = ide_in_dma(hw, 2);		if (drive->slave)			c &= ~0x40;		else			c &= ~0x20;		ide_out_dma(hw, c, 2);		/*		 * Ensure that the BM transfer bit is off.		 */		ide_out_dma(hw, ide_in_dma(hw, 0) & ~1, 0);	}}/* * Turn on the DMA capability status bit for this drive. */static void ide_dma_enable(struct ide_drive *drive){	struct ide_hw *hw = drive->hw;	int c;	if (hw->dma_base) {		c = ide_in_dma(hw, 2);		if (drive->slave)			c |= 0x40;		else			c |= 0x20;		ide_out_dma(hw, c, 2);	}}/* * read some data from the drive */static void ide_input_data(struct ide_drive *drive, u8 *data, int len){	unsigned short *s = (unsigned short *)data;	while (len > 0) {		len -= 2;		*s++ = ide_inw(drive->hw, IDE_DATA);	}}/* * convert a drive string to our endian-ness */static void ide_fixstring(u8 *s, int size, int bswap){	u8 *p = s, *end = &s[size & ~1];	if (bswap) {		for (p = end; p != s;) {			unsigned short *pp = (unsigned short *)(p -= 2);			*pp = *pp << 8 | *pp >> 8;		}	}	while (s != end && *s == ' ')		++s;	while (s != end && *s) {		if (*s++ != ' ' || (s != end && *s && *s != ' '))			*p++ = *(s - 1);	}	/* wipe out trailing garbage */	while (p != end)		*p++ = '\0';}static int idx(unsigned int val, const unsigned int *vals){	int i;	for (i = 0; vals[i]; i++)		if (val >= vals[i])			break;	return i;}static void get_speed_params(struct ide_drive *drive){	struct hd_driveid *id = &drive->id;	int eide_pio_cyc_time;	if (drive->iordy)		eide_pio_cyc_time = id->eide_pio_iordy;	else		eide_pio_cyc_time = id->eide_pio;	drive->dma_speed = DMA_NONE;	drive->pio_speed = 0;	drive->dma = 0;	/*	 * Try UDMA modes 0 through 2	 */	if (ID_UDMA_VALID(id)) {		if (id->dma_ultra & 4) {	/* UDMA mode 2 */			drive->dma_speed = DMA_UDMA2;			drive->dma = 2;		} else		if (id->dma_ultra & 2) {	/* UDMA mode 1 */			drive->dma_speed = DMA_UDMA1;			drive->dma = 2;		} else		if (id->dma_ultra & 1) {	/* UDMA mode 0 */			drive->dma_speed = DMA_UDMA0;			drive->dma = 2;		}	}	/*	 * Try EIDE DMA/timed PIO	 */	if (ID_PIODMA_VALID(id)) {		/*		 * EIDE DMA		 */		if (drive->dma == 0 && id->eide_dma_time != 0 && id->eide_dma_time <= 180) {			static const unsigned int eide_times[] = { 150, 120, 90, 60, 0 };			drive->dma = 1;			drive->dma_speed = DMA_DMA0 + idx(id->eide_dma_time, eide_times);		}		/*		 * EIDE PIO		 */		if (eide_pio_cyc_time != 0) {			static const unsigned int eide_pio_times[] =				{ 570, 540, 510, 480, 450, 420, 390, 360, 330,				  300, 270, 240, 210, 180, 150, 120, 90, 60, 0 };			drive->pio_speed = idx(eide_pio_cyc_time, eide_pio_times);			return;		}	}	switch (id->eide_pio_modes >> 8) {	default:/* mode 4 - 210ns */		drive->pio_speed = 13;		break;	case 1:	/* mode 3 - 390ns */		drive->pio_speed = 7;		break;	case 0:		drive->pio_speed = 0;		break;	}}static int set_drive_features(struct ide_drive *drive){	int mode = 0;	int failed = 0;	switch (drive->dma) {	case 2:	/* UDMA */		mode = 0x40 | (drive->dma_speed - DMA_UDMA0);		break;	case 1:	/* DMA */		if (drive->dma_speed == DMA_DMA1)			mode = 0x21;		else if (drive->dma_speed > DMA_DMA1)			mode = 0x22;		break;	case 0: /* PIO */		switch (drive->pio_speed) {		case 16 ... 18:			mode = 0x0c;	/* mode4 */			break;		case 14 ... 15:			mode = 0x0b;	/* mode3 */			break;		default:			break;		}		break;	}	if (mode) {		if (!ide_select_drive(drive))			failed = 1;		ide_outb(drive->hw, mode, IDE_NSECTOR);		ide_outb(drive->hw, 0x03, IDE_FEATURE);		wait_cs(1);		if (!failed && ide_send_command(drive, WIN_SETFEATURES, 2 * 100))			failed = 1;		if (!failed && !ide_status_ok(drive, 0, ERR_STAT|DRQ_STAT|BUSY_STAT))			failed = 1;	}	return failed;}/* * Promise UDMA33 (PDC20246/7) interface initialisation. */static void pdc20246_dma_init(struct ide_drive *drive, struct hd_driveid *id){	static const struct { u8 c:4, b:3; } dma_regs[] = {		{ 15, 7 },	/* NONE			*/		{  4, 4 },	/* DMA0		180ns	*/		{  4, 3 },	/* DMA1		150ns	*/		{  3, 3 },	/* UDMA0 / DMA2	120ns	*/		{  3, 3 },	/* UDMA0 / DMA2	120ns	*/		{  2, 2 },	/* UDMA1	 90ns	*/		{  1, 1 }	/* UDMA2	 60ns	*/	};	static const struct { u8 a:4, b:5; } pio_regs[] = {		{ 13, 19 },	/* PIO0		600ns	*/		{ 12, 18 },	/* PIO0		570ns	*/		{ 11, 17 },	/* PIO0		540ns	*/		{ 10, 16 },	/* PIO0		510ns	*/		{  9, 15 },	/* PIO0		480ns	*/		{  8, 14 },	/* PIO0		450ns	*/		{  7, 13 },	/* PIO0		420ns	*/		{  7, 12 },	/* PIO1		390ns	*/		{  6, 11 },	/* PIO1		360ns	*/		{  5, 10 },	/* PIO1		330ns	*/		{  4,  9 },	/* PIO1		300ns	*/		{  3,  8 },	/* PIO1		270ns	*/		{  3,  7 },	/* PIO2		240ns	*/		{  2,  6 },	/* PIO2		210ns	*/		{  2,  5 },	/* PIO3		180ns	*/		{  1,  4 },	/* PIO3		150ns	*/		{  0,  3 },	/* PIO4		120ns	*/		{  0,  2 },	/* PIO4		 90ns	*/		{  0,  1 }	/* PIO4		 60ns	*/	};	struct ide_hw *hw = drive->hw;	unsigned int base;	u8 a, b, c;	base = hw->ifnr ? 0x68 : 0x60;	c = pci_read_config_byte(hw->dev, base + 2);	if (c & 0x40)		drive->iordy = 1;	/*	 * Calculate the UDMA/DMA/PIO capability for this drive	 *  (results in drive->pio_speed, drive->dma_speed)	 */	get_speed_params(drive);	if (!set_drive_features(drive)) {		unsigned int drv_off;		drv_off = base + (drive->slave ? 4 : 0);		a = 0xd0 | pio_regs[drive->pio_speed].a;		b = pio_regs[drive->pio_speed].b;		c = 0;		if (drive->iordy)			a |= 0x20;		if (drive->dma && drive->dma_speed <= DMA_UDMA2) {			b |= dma_regs[drive->dma_speed].b << 5;			c |= dma_regs[drive->dma_speed].c;		} else {			b |= 0xe0;			c |= 0x0f;		}		pci_write_config_byte(hw->dev, drv_off + 0, a);		pci_write_config_byte(hw->dev, drv_off + 1, b);		pci_write_config_byte(hw->dev, drv_off + 2, c);		if (drive->dma)			ide_dma_enable(drive);		switch (drive->dma) {		case 2:	printf(", UDMA mode %d", drive->dma_speed - DMA_UDMA0); break;		case 1: printf(", DMA mode %d", drive->dma_speed); break;		case 0: printf(", PIO mode %d", drive->pio_speed >= 16 ? 4 :						drive->pio_speed >= 14 ? 3 : 2); break;		}	}}#define pdc20246_init NULLstatic int pdc20246_check(struct pci_dev *dev){	unsigned int dma_base = dev->bar[4] & ~3;	if (dma_base)		pci_io_write_byte(0x01, dma_base + 0x1f);	return 1;}static int pdc20262_check(struct pci_dev *dev){	unsigned int dma_base = dev->bar[4] & ~3;	if (dma_base)		pci_io_write_byte(0x01, dma_base + 0x1f);	return 1;}#define pdc20262_init NULL#define pdc20262_dma_init NULL/* * CY82C693 checking.  This has the primary IDE BARs on function 1, and * the secondary IDE BARs on function 2.  We therefore only recognise * function 1, and use dev->next to get at function 2. */static int cy82c693_check(struct pci_dev *dev){	return 0 && FUNC(dev->dev) == 1;}#define cy82c693_init NULL#define cy82c693_dma_init NULL/* * List of cards we know about... */#define ENT(id,name,split,pre) \  { id, name, split, pre##_check, pre##_init, pre##_dma_init }static const struct dma_hw dma_hw[] = {  ENT(0x105a4d33, "PDC20246", 0, pdc20246),  ENT(0x105a4d38, "PDC20262", 0, pdc20262),  ENT(0x1080c693, "CY82C693", 1, cy82c693),  { 0, "unknown",  }};static int ide_lba_ok(struct hd_driveid *id){	u32 lba_sects;	u32 chs_sects;	u32 chs_10_pc;	/*	 * ATA-6:	 * If we have CHS of:	 *  16383/16/63, 16383/15/63, 4092/16/63 or 4092/15/63	 * and the lba capacity > 16383 * 63 * heads, then	 * LBA mode must be supported.	 */	if (id->def_log_sects == 63 &&            (id->def_log_cyls == 16383 ||	     (id->def_log_cyls == 4092 && id->cur_log_cyls == 16383)) &&	    (id->def_log_heads == 15 || id->def_log_heads == 16) &&	    id->lba_capacity >= 16383*63*id->def_log_heads)		return 1;	/*	 * ATA-2 check for LBA mode.  (note that this bit is	 * "reserved" in ATA-6)	 */	if ((id->capability & 2) == 0)		return 0;	lba_sects = id->lba_capacity;	chs_sects = id->def_log_cyls * id->def_log_heads * id->def_log_sects;	chs_10_pc = chs_sects / 10;	if ((lba_sects - chs_sects) < chs_10_pc)		return 1;	lba_sects = (lba_sects << 16) | (lba_sects >> 16);	if ((lba_sects - chs_sects) < chs_10_pc) {		id->lba_capacity = lba_sects;		return 1;	}	return 0;}/* * Parse the IDENTIFY information */static int ide_parse_identify(struct ide_drive *drive, struct hd_driveid *id){	ide_fixstring(id->model, sizeof(id->model), 1);	id->model[sizeof(id->model)-1] = '\0';	if (id->def_log_sects && id->def_log_heads && id->def_log_cyls) {		/*		 * The drive supports CHS mode - get the default		 * logical translation		 */		drive->sect = id->def_log_sects;		drive->head = id->def_log_heads;		drive->cyl  = id->def_log_cyls;		/*		 * Now check to see if the current logical translation		 * is valid.  If it is, we should use this instead.		 */		if (ID_CURLOGGEO_VALID(id) && id->cur_log_cyls &&		    id->cur_log_heads && id->cur_log_heads <= 16 &&		    id->cur_log_sects) {		    	u32 capacity, check;			drive->sect = id->cur_log_sects;			drive->head = id->cur_log_heads;			drive->cyl  = id->cur_log_cyls;			/*			 * fix cur capacity if its wrong. ATA-6 defines			 * this to be the product of the current logical			 * translation CHS.			 */			capacity = drive->cyl * drive->head * drive->sect;			check = id->cur_capacity0 << 16 | id->cur_capacity1;			if (check == capacity) {				id->cur_capacity0 = capacity;				id->cur_capacity1 = capacity >> 16;			}		}		drive->lba = 0;		drive->capacity = drive->cyl * drive->head * drive->sect;	} else {		/*		 * Drive supports only LBA mode		 */		drive->capacity = id->lba_capacity;		drive->sect = 63;		drive->head = 16;		drive->cyl  = id->lba_capacity / (16 * 63);		drive->lba  = 1;	}	if (!drive->lba && ide_lba_ok(id)) {		/*		 * Ok, the drive supports CHS mode and		 * apparantly supports LBA mode?		 */		if (id->lba_capacity >= drive->capacity) {			drive->cyl = id->lba_capacity / (drive->head * drive->sect);			drive->lba = 1;			drive->capacity = id->lba_capacity;		}	}	/*	 * Now set or clear the LBA bit in the head/device register	 */	if (drive->lba)		drive->select |= 0x40;	else		drive->select &= 0xbf;	printf("%s, %dMB, %sCHS=%d/%d/%d",		id->model,		drive->capacity / 2048,		drive->lba ? "LBA, " : "",		drive->cyl, drive->head, drive->sect);	/*	 * Do any chipset-specific DMA-type setup	 */	if (drive->hw->dma->setup_dma)		drive->hw->dma->setup_dma(drive, id);	printf("\n");	return 0;}/* * Figure out which status register to use.  We try to use * the ALTSTATUS register in preference to the normal * STATUS register at all times. */static inline int ide_detect_status_reg(struct ide_hw *hw){	u8 s, a;	a = ide_inb(hw, IDE_ALTSTATUS) & 0xc9;	s = ide_inb(hw, IDE_STATUS)    & 0xc9;	return (a ^ s) ? IDE_STATUS : IDE_ALTSTATUS;}/* * Handle IDENTIFY_DEVICE protocol */static int ide_identify_device(struct ide_drive *drive, u8 cmd, void *buf){	int rc = 1;	if (ide_send_command(drive, cmd, 31 * 100))		goto out;	if (ide_status_ok(drive, DRQ_STAT, BAD_R_STAT)) {		ide_input_data(drive, buf, 512);		rc = 0;	} else		rc = 2;	ide_inb(drive->hw, IDE_STATUS);out:

⌨️ 快捷键说明

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