📄 ide.c
字号:
/* * 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 + -