On Friday, April 22, 2011 05:19 AM Jonathan A. Kollasch wrote:
]> thanks for the tip. Do you have any fixes beyond that one I should try? ] ]Yeah, I just didn't like them well enough to post a patch. ] ]Attached is what I have. Last I tried it a few months ago it mostly ]worked for ATA disks but not ATAPI devices. IIRC the key changes are ]in the port probe and reset functions. ] ] Jonathan Kollasch
Hello Jonathan,
Thanks. I made some progress. Though there are some quirks, the attached patch gets AHCI working well enough on my AMD Fusion board to install Windows 7 from DVD. I installed it on both a regular SATA drive and on a SATA SSD drive. There are at least 2 remaining problems:
1) When booting a DOS drive, a disk read error occurs at some point after autoexec executes. 2) DOS booting is slower by 100-200 ms. While that isn't much in absolute terms, it is a big percentage increase from the 642 ms I can get using the IDE interface.
Windows 7 setup loads from DVD noticeable faster when AHCI is used. Windows 7 boot with AHCI and an SSD disk is impressive. Using AHCI and completely hiding the IDE device keeps Windows from wasting time with uninstalled drives while booting. Starting from power off with the Windows 7 SSD drive, the desktop background appears after 15 seconds and the desktop is complete after around 20 seconds.
Thanks, Scott
--- seabios-0.6.2-original\src\ahci.c Mon Feb 28 21:10:57 2011 +++ seabios-0.6.2\src\ahci.c Sun Apr 24 01:12:20 2011 @@ -105,7 +105,7 @@ static int ahci_command(struct ahci_port_s *port, int iswrite, int isatapi, void *buffer, u32 bsize) { - u32 val, status, success, flags; + u32 val, status, flags, intbits, error; struct ahci_ctrl_s *ctrl = GET_GLOBAL(port->ctrl); struct ahci_cmd_s *cmd = GET_GLOBAL(port->cmd); struct ahci_fis_s *fis = GET_GLOBAL(port->fis); @@ -118,19 +118,24 @@ SET_FLATPTR(cmd->prdt[0].baseu, 0); SET_FLATPTR(cmd->prdt[0].flags, bsize-1);
+ // clear interrupt status + val = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); + ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, val); + + // toggle the start control val = ahci_port_readl(ctrl, pnr, PORT_CMD); + ahci_port_writel(ctrl, pnr, PORT_CMD, val & ~(PORT_CMD_START | PORT_CMD_FIS_RX)); ahci_port_writel(ctrl, pnr, PORT_CMD, val | PORT_CMD_START);
if (ahci_port_readl(ctrl, pnr, PORT_CMD_ISSUE)) return -1;
flags = ((1 << 16) | /* one prd entry */ - (1 << 10) | /* clear busy on ok */ (iswrite ? (1 << 6) : 0) | (isatapi ? (1 << 5) : 0) | - (4 << 0)); /* fis length (dwords) */ + (5 << 0)); /* fis length (dwords) */ SET_FLATPTR(list[0].flags, flags); - SET_FLATPTR(list[0].bytes, bsize); + SET_FLATPTR(list[0].bytes, 0); SET_FLATPTR(list[0].base, ((u32)(cmd))); SET_FLATPTR(list[0].baseu, 0);
@@ -138,19 +143,39 @@ SET_FLATPTR(fis->rfis[2], 0); ahci_port_writel(ctrl, pnr, PORT_SCR_ACT, 1); ahci_port_writel(ctrl, pnr, PORT_CMD_ISSUE, 1); - while (ahci_port_readl(ctrl, pnr, PORT_CMD_ISSUE)) { - yield(); - } - while ((status = GET_FLATPTR(fis->rfis[2])) == 0) { + while (1) { + intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT) & 0x3f; + if (intbits) break; yield(); } - - success = (0x00 == (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | - ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR)) && - ATA_CB_STAT_RDY == (status & (ATA_CB_STAT_RDY))); - dprintf(2, "AHCI/%d: ... finished, status 0x%x, %s\n", pnr, - status, success ? "OK" : "ERROR"); - return success ? 0 : -1; + status = 0xFFFF; + error = -2; + if (intbits == 0x01) { // Device to Host Register FIS Interrupt (DHRS) + status = GET_FLATPTR(fis->rfis[2]); + error = GET_FLATPTR(fis->rfis[3]); + } + if (intbits == 0x02) { // PIO Setup FIS Interrupt (PSS) + status = GET_FLATPTR(fis->psfis[2]); + error = GET_FLATPTR(fis->psfis[3]); + } + if (intbits == 0x04) { // DMA Setup FIS Interrupt (DSS) + status = GET_FLATPTR(fis->dsfis[2]); + error = GET_FLATPTR(fis->dsfis[3]); + } + if (intbits == 0x08) { // Set Device Bits Interrupt (SDBS) + status = GET_FLATPTR(fis->sdbfis[2]); + error = GET_FLATPTR(fis->sdbfis[3]); + } + if (intbits == 0x10) { // Unknown FIS Interrupt (UFS) + status = GET_FLATPTR(fis->ufis[2]); + error = GET_FLATPTR(fis->ufis[3]); + } + + if (!error) + dprintf(2, "AHCI/%d: ... finished, status 0x%02x OK\n", pnr, status); + else + dprintf(2, "AHCI/%d: ... finished, status 0x%02x ERROR %02x\n", pnr, status, error); + return error; }
#define CDROM_CDB_SIZE 12 @@ -172,7 +197,7 @@ } rc = ahci_command(port, 0, 1, op->buf_fl, op->count * blocksize); - if (rc < 0) + if (rc) return DISK_RET_EBADTRACK; return DISK_RET_SUCCESS; } @@ -191,7 +216,7 @@ op->count * DISK_SECTOR_SIZE); dprintf(2, "ahci disk %s, lba %6x, count %3x, buf %p, rc %d\n", iswrite ? "write" : "read", (u32)op->lba, op->count, op->buf_fl, rc); - if (rc < 0) + if (rc) return DISK_RET_EBADTRACK; return DISK_RET_SUCCESS; } @@ -281,22 +306,10 @@ static int ahci_port_probe(struct ahci_ctrl_s *ctrl, u32 pnr) { - u32 val, count = 0; - - val = ahci_port_readl(ctrl, pnr, PORT_TFDATA); - while (val & ((1 << 7) /* BSY */ | - (1 << 3) /* DRQ */)) { - ndelay(500); - val = ahci_port_readl(ctrl, pnr, PORT_TFDATA); - count++; - if (count >= AHCI_MAX_RETRIES) - return -1; - } - - val = ahci_port_readl(ctrl, pnr, PORT_SCR_STAT); - if ((val & 0x07) != 0x03) - return -1; - return 0; + u32 val = ahci_port_readl(ctrl, pnr, PORT_SCR_STAT) & 7; + if (val == 3) // Device presence detected and Phy communication established + return 0; + return -1; }
#define MAXMODEL 40 @@ -340,7 +353,7 @@ port->atapi = 0; sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_DEVICE); rc = ahci_command(port, 0, 0, buffer, sizeof(buffer)); - if (rc < 0) + if (rc) goto err; }