grml...
[seabios.git] / src / ahci.c
index ee50e8fe88877ac31e6987161a846a03054cb543..9b3ce2f391f56bc4d6e8f282454574e146e01f9b 100644 (file)
@@ -17,7 +17,9 @@
 #include "ahci.h" // CDB_CMD_READ_10
 #include "blockcmd.h" // CDB_CMD_READ_10
 
-#define AHCI_MAX_RETRIES 5
+#define AHCI_REQUEST_TIMEOUT 32000 // 32 seconds max for IDE ops
+#define AHCI_RESET_TIMEOUT     500 // 500 miliseconds
+#define AHCI_LINK_TIMEOUT       10 // 10 miliseconds
 
 /****************************************************************
  * these bits must run in both 16bit and 32bit modes
@@ -105,12 +107,13 @@ static void ahci_port_writel(struct ahci_ctrl_s *ctrl, u32 pnr, u32 reg, u32 val
 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, success, 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);
     struct ahci_list_s *list = GET_GLOBAL(port->list);
     u32 pnr                  = GET_GLOBAL(port->pnr);
+    u64 end;
 
     SET_FLATPTR(cmd->fis.reg,       0x27);
     SET_FLATPTR(cmd->fis.pmp_type,  (1 << 7)); /* cmd fis */
@@ -118,38 +121,96 @@ static int ahci_command(struct ahci_port_s *port, int iswrite, int isatapi,
     SET_FLATPTR(cmd->prdt[0].baseu, 0);
     SET_FLATPTR(cmd->prdt[0].flags, bsize-1);
 
-    val = ahci_port_readl(ctrl, pnr, PORT_CMD);
-    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) */
-    SET_FLATPTR(list[0].flags, flags);
-    SET_FLATPTR(list[0].bytes,  bsize);
+             (5 << 0)); /* fis length (dwords) */
+    SET_FLATPTR(list[0].flags,  flags);
+    SET_FLATPTR(list[0].bytes,  0);
     SET_FLATPTR(list[0].base,   ((u32)(cmd)));
     SET_FLATPTR(list[0].baseu,  0);
 
     dprintf(2, "AHCI/%d: send cmd ...\n", pnr);
-    SET_FLATPTR(fis->rfis[2], 0);
+    intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
+    if (intbits)
+        ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits);
     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) {
-        yield();
-    }
+
+    end = calc_future_tsc(AHCI_REQUEST_TIMEOUT);
+    do {
+        for (;;) {
+            intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
+            if (intbits) {
+                ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits);
+                if (intbits & 0x02) {
+                    status = GET_FLATPTR(fis->psfis[2]);
+                    error  = GET_FLATPTR(fis->psfis[3]);
+                    break;
+                }
+                if (intbits & 0x01) {
+                    status = GET_FLATPTR(fis->rfis[2]);
+                    error  = GET_FLATPTR(fis->rfis[3]);
+                    break;
+                }
+            }
+            if (check_tsc(end)) {
+                warn_timeout();
+                return -1;
+            }
+            yield();
+        }
+        dprintf(2, "AHCI/%d: ... intbits 0x%x, status 0x%x ...\n",
+                pnr, intbits, status);
+    } while (status & ATA_CB_STAT_BSY);
 
     success = (0x00 == (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF |
-                                  ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR)) &&
+                                  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");
+    if (success) {
+        dprintf(2, "AHCI/%d: ... finished, status 0x%x, OK\n", pnr,
+                status);
+    } else {
+        dprintf(2, "AHCI/%d: ... finished, status 0x%x, ERROR 0x%x\n", pnr,
+                status, error);
+
+        // non-queued error recovery (AHCI 1.3 section 6.2.2.1)
+        // Clears PxCMD.ST to 0 to reset the PxCI register
+        val = ahci_port_readl(ctrl, pnr, PORT_CMD);
+        ahci_port_writel(ctrl, pnr, PORT_CMD, val & ~PORT_CMD_START);
+
+        // waits for PxCMD.CR to clear to 0
+        while (1) {
+            val = ahci_port_readl(ctrl, pnr, PORT_CMD);
+            if ((val & PORT_CMD_LIST_ON) == 0)
+                break;
+            yield();
+        }
+
+        // Clears any error bits in PxSERR to enable capturing new errors
+        val = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR);
+        ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, val);
+
+        // Clears status bits in PxIS as appropriate
+        val = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
+        ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, val);
+
+        // If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to 1, issue
+        // a COMRESET to the device to put it in an idle state
+        val = ahci_port_readl(ctrl, pnr, PORT_TFDATA);
+        if (val & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ)) {
+            dprintf(2, "AHCI/%d: issue comreset\n", pnr);
+            val = ahci_port_readl(ctrl, pnr, PORT_SCR_CTL);
+            // set Device Detection Initialization (DET) to 1 for 1 ms for comreset
+            ahci_port_writel(ctrl, pnr, PORT_SCR_CTL, val | 1);
+            mdelay (1);
+            ahci_port_writel(ctrl, pnr, PORT_SCR_CTL, val);
+        }
+
+        // Sets PxCMD.ST to 1 to enable issuing new commands
+        val = ahci_port_readl(ctrl, pnr, PORT_CMD);
+        ahci_port_writel(ctrl, pnr, PORT_CMD, val | PORT_CMD_START);
+    }
     return success ? 0 : -1;
 }
 
@@ -177,9 +238,9 @@ int ahci_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
     return DISK_RET_SUCCESS;
 }
 
-// read/write count blocks from a harddrive.
+// read/write count blocks from a harddrive, op->buf_fl must be word aligned
 static int
-ahci_disk_readwrite(struct disk_op_s *op, int iswrite)
+ahci_disk_readwrite_aligned(struct disk_op_s *op, int iswrite)
 {
     struct ahci_port_s *port = container_of(
         op->drive_g, struct ahci_port_s, drive);
@@ -196,6 +257,47 @@ ahci_disk_readwrite(struct disk_op_s *op, int iswrite)
     return DISK_RET_SUCCESS;
 }
 
+// read/write count blocks from a harddrive.
+static int
+ahci_disk_readwrite(struct disk_op_s *op, int iswrite)
+{
+    // if caller's buffer is word aligned, use it directly
+    if (((u32) op->buf_fl & 1) == 0)
+        return ahci_disk_readwrite_aligned(op, iswrite);
+
+    // Use a word aligned buffer for AHCI I/O
+    int rc;
+    struct disk_op_s localop = *op;
+    u8 *alignedbuf_fl = GET_GLOBAL(bounce_buf_fl);
+    u8 *position = op->buf_fl;
+
+    localop.buf_fl = alignedbuf_fl;
+    localop.count = 1;
+
+    if (iswrite) {
+        u16 block;
+        for (block = 0; block < op->count; block++) {
+            memcpy_fl (alignedbuf_fl, position, DISK_SECTOR_SIZE);
+            rc = ahci_disk_readwrite_aligned (&localop, 1);
+            if (rc)
+                return rc;
+            position += DISK_SECTOR_SIZE;
+            localop.lba++;
+        }
+    } else { // read
+        u16 block;
+        for (block = 0; block < op->count; block++) {
+            rc = ahci_disk_readwrite_aligned (&localop, 0);
+            if (rc)
+                return rc;
+            memcpy_fl (position, alignedbuf_fl, DISK_SECTOR_SIZE);
+            position += DISK_SECTOR_SIZE;
+            localop.lba++;
+        }
+    }
+    return DISK_RET_SUCCESS;
+}
+
 // command demuxer
 int process_ahci_op(struct disk_op_s *op)
 {
@@ -252,63 +354,36 @@ int process_ahci_op(struct disk_op_s *op)
 static void
 ahci_port_reset(struct ahci_ctrl_s *ctrl, u32 pnr)
 {
-    u32 val, count = 0;
+    u32 val;
+    u64 end;
 
     /* disable FIS + CMD */
-    val = ahci_port_readl(ctrl, pnr, PORT_CMD);
-    while (val & (PORT_CMD_FIS_RX | PORT_CMD_START |
-                  PORT_CMD_FIS_ON | PORT_CMD_LIST_ON) &&
-           count < AHCI_MAX_RETRIES) {
+    end = calc_future_tsc(AHCI_RESET_TIMEOUT);
+    for (;;) {
+        val = ahci_port_readl(ctrl, pnr, PORT_CMD);
+        if (!(val & (PORT_CMD_FIS_RX | PORT_CMD_START |
+                     PORT_CMD_FIS_ON | PORT_CMD_LIST_ON)))
+            break;
         val &= ~(PORT_CMD_FIS_RX | PORT_CMD_START);
         ahci_port_writel(ctrl, pnr, PORT_CMD, val);
-        ndelay(500);
-        val = ahci_port_readl(ctrl, pnr, PORT_CMD);
-        count++;
+        if (check_tsc(end)) {
+            warn_timeout();
+            break;
+        }
+        yield();
     }
 
-    /* clear status */
-    val = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR);
-    if (val)
-        ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, val);
-
     /* disable + clear IRQs */
-    ahci_port_writel(ctrl, pnr, PORT_IRQ_MASK, val);
+    ahci_port_writel(ctrl, pnr, PORT_IRQ_MASK, 0);
     val = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
     if (val)
         ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, val);
 }
 
-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;
-}
-
-#define MAXMODEL 40
-
 static struct ahci_port_s*
-ahci_port_init(struct ahci_ctrl_s *ctrl, u32 pnr)
+ahci_port_alloc(struct ahci_ctrl_s *ctrl, u32 pnr)
 {
-    struct ahci_port_s *port = malloc_fseg(sizeof(*port));
-    char model[MAXMODEL+1];
-    u16 buffer[256];
-    u32 val;
-    int rc;
+    struct ahci_port_s *port = malloc_tmp(sizeof(*port));
 
     if (!port) {
         warn_noalloc();
@@ -316,9 +391,9 @@ ahci_port_init(struct ahci_ctrl_s *ctrl, u32 pnr)
     }
     port->pnr = pnr;
     port->ctrl = ctrl;
-    port->list = memalign_low(1024, 1024);
-    port->fis = memalign_low(256, 256);
-    port->cmd = memalign_low(256, 256);
+    port->list = memalign_tmp(1024, 1024);
+    port->fis = memalign_tmp(256, 256);
+    port->cmd = memalign_tmp(256, 256);
     if (port->list == NULL || port->fis == NULL || port->cmd == NULL) {
         warn_noalloc();
         return NULL;
@@ -329,8 +404,105 @@ ahci_port_init(struct ahci_ctrl_s *ctrl, u32 pnr)
 
     ahci_port_writel(ctrl, pnr, PORT_LST_ADDR, (u32)port->list);
     ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR, (u32)port->fis);
-    val = ahci_port_readl(ctrl, pnr, PORT_CMD);
-    ahci_port_writel(ctrl, pnr, PORT_CMD, val | PORT_CMD_FIS_RX);
+    return port;
+}
+
+static struct ahci_port_s* ahci_port_realloc(struct ahci_port_s *port)
+{
+    struct ahci_port_s *tmp;
+    u32 cmd;
+
+    tmp = malloc_fseg(sizeof(*port));
+    *tmp = *port;
+    free(port);
+    port = tmp;
+
+    ahci_port_reset(port->ctrl, port->pnr);
+
+    free(port->list);
+    free(port->fis);
+    free(port->cmd);
+    port->list = memalign_low(1024, 1024);
+    port->fis = memalign_low(256, 256);
+    port->cmd = memalign_low(256, 256);
+
+    ahci_port_writel(port->ctrl, port->pnr, PORT_LST_ADDR, (u32)port->list);
+    ahci_port_writel(port->ctrl, port->pnr, PORT_FIS_ADDR, (u32)port->fis);
+
+    cmd = ahci_port_readl(port->ctrl, port->pnr, PORT_CMD);
+    cmd |= (PORT_CMD_FIS_RX|PORT_CMD_START);
+    ahci_port_writel(port->ctrl, port->pnr, PORT_CMD, cmd);
+
+    return port;
+}
+
+static void ahci_port_release(struct ahci_port_s *port)
+{
+    ahci_port_reset(port->ctrl, port->pnr);
+    free(port->list);
+    free(port->fis);
+    free(port->cmd);
+    free(port);
+}
+
+#define MAXMODEL 40
+
+/* See ahci spec chapter 10.1 "Software Initialization of HBA" */
+static int ahci_port_init(struct ahci_port_s *port)
+{
+    struct ahci_ctrl_s *ctrl = port->ctrl;
+    u32 pnr = port->pnr;
+    char model[MAXMODEL+1];
+    u16 buffer[256];
+    u32 cmd, stat, err, tf;
+    u64 end;
+    int rc;
+
+    /* enable FIS recv */
+    cmd = ahci_port_readl(ctrl, pnr, PORT_CMD);
+    cmd |= PORT_CMD_FIS_RX;
+    ahci_port_writel(ctrl, pnr, PORT_CMD, cmd);
+
+    /* spin up */
+    cmd |= PORT_CMD_SPIN_UP;
+    ahci_port_writel(ctrl, pnr, PORT_CMD, cmd);
+    end = calc_future_tsc(AHCI_LINK_TIMEOUT);
+    for (;;) {
+        stat = ahci_port_readl(ctrl, pnr, PORT_SCR_STAT);
+        if ((stat & 0x07) == 0x03) {
+            dprintf(1, "AHCI/%d: link up\n", port->pnr);
+            break;
+        }
+        if (check_tsc(end)) {
+            dprintf(1, "AHCI/%d: link down\n", port->pnr);
+            return -1;
+        }
+        yield();
+    }
+
+    /* clear error status */
+    err = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR);
+    if (err)
+        ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, err);
+
+    /* wait for device becoming ready */
+    end = calc_future_tsc(AHCI_REQUEST_TIMEOUT);
+    for (;;) {
+        tf = ahci_port_readl(ctrl, pnr, PORT_TFDATA);
+        if (!(tf & (ATA_CB_STAT_BSY |
+                    ATA_CB_STAT_DRQ)))
+            break;
+        if (check_tsc(end)) {
+            warn_timeout();
+            dprintf(1, "AHCI/%d: device not ready (tf 0x%x)\n", port->pnr, tf);
+            return -1;
+        }
+        yield();
+    }
+
+    /* start device */
+    cmd |= PORT_CMD_START;
+    ahci_port_writel(ctrl, pnr, PORT_CMD, cmd);
 
     sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_PACKET_DEVICE);
     rc = ahci_command(port, 0, 0, buffer, sizeof(buffer));
@@ -341,16 +513,12 @@ ahci_port_init(struct ahci_ctrl_s *ctrl, u32 pnr)
         sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_DEVICE);
         rc = ahci_command(port, 0, 0, buffer, sizeof(buffer));
         if (rc < 0)
-            goto err;
+            return -1;
     }
 
     port->drive.type = DTYPE_AHCI;
+    port->drive.cntl_id = pnr;
     port->drive.removable = (buffer[0] & 0x80) ? 1 : 0;
-    port->drive.desc = malloc_tmp(MAXDESCSIZE);
-    if (!port->drive.desc) {
-        warn_noalloc();
-        return NULL;
-    }
 
     if (!port->atapi) {
         // found disk (ata)
@@ -371,82 +539,87 @@ ahci_port_init(struct ahci_ctrl_s *ctrl, u32 pnr)
             adjsize >>= 10;
             adjprefix = 'G';
         }
-        snprintf(port->drive.desc, MAXDESCSIZE
-                 , "AHCI/%d: %s ATA-%d Hard-Disk (%u %ciBytes)"
-                 , port->pnr
-                 , ata_extract_model(model, MAXMODEL, buffer)
-                 , ata_extract_version(buffer)
-                 , (u32)adjsize, adjprefix);
-
-        // Setup disk geometry translation.
-        setup_translation(&port->drive);
-        // Register with bcv system.
-        add_bcv_internal(&port->drive);
+        port->desc = znprintf(MAXDESCSIZE
+                              , "AHCI/%d: %s ATA-%d Hard-Disk (%u %ciBytes)"
+                              , port->pnr
+                              , ata_extract_model(model, MAXMODEL, buffer)
+                              , ata_extract_version(buffer)
+                              , (u32)adjsize, adjprefix);
+        port->prio = bootprio_find_ata_device(ctrl->pci_tmp, pnr, 0);
     } else {
         // found cdrom (atapi)
         port->drive.blksize = CDROM_SECTOR_SIZE;
         port->drive.sectors = (u64)-1;
         u8 iscd = ((buffer[0] >> 8) & 0x1f) == 0x05;
-        snprintf(port->drive.desc, MAXDESCSIZE, "AHCI/%d: %s ATAPI-%d %s"
-                 , port->pnr
-                 , ata_extract_model(model, MAXMODEL, buffer)
-                 , ata_extract_version(buffer)
-                 , (iscd ? "DVD/CD" : "Device"));
-
-        // fill cdidmap
-        if (iscd)
-            map_cd_drive(&port->drive);
+        if (!iscd) {
+            dprintf(1, "AHCI/%d: atapi device is'nt a cdrom\n", port->pnr);
+            return -1;
+        }
+        port->desc = znprintf(MAXDESCSIZE
+                              , "DVD/CD [AHCI/%d: %s ATAPI-%d DVD/CD]"
+                              , port->pnr
+                              , ata_extract_model(model, MAXMODEL, buffer)
+                              , ata_extract_version(buffer));
+        port->prio = bootprio_find_ata_device(ctrl->pci_tmp, pnr, 0);
     }
-    dprintf(1, "%s\n", port->drive.desc);
-
-    return port;
-
-err:
-    dprintf(1, "AHCI/%d: init failure, reset\n", port->pnr);
-    ahci_port_reset(ctrl, pnr);
-    return NULL;
+    return 0;
 }
 
 // Detect any drives attached to a given controller.
 static void
-ahci_detect(void *data)
+ahci_port_detect(void *data)
 {
-    struct ahci_ctrl_s *ctrl = data;
-    struct ahci_port_s *port;
-    u32 pnr, max;
+    struct ahci_port_s *port = data;
     int rc;
 
-    max = ctrl->caps & 0x1f;
-    for (pnr = 0; pnr < max; pnr++) {
-        if (!(ctrl->ports & (1 << pnr)))
-            continue;
-        dprintf(2, "AHCI/%d: probing\n", pnr);
-        ahci_port_reset(ctrl, pnr);
-        rc = ahci_port_probe(ctrl, pnr);
-        dprintf(1, "AHCI/%d: link %s\n", pnr, rc == 0 ? "up" : "down");
-        if (rc != 0)
-            continue;
-        port = ahci_port_init(ctrl, pnr);
+    dprintf(2, "AHCI/%d: probing\n", port->pnr);
+    ahci_port_reset(port->ctrl, port->pnr);
+    rc = ahci_port_init(port);
+    if (rc < 0)
+        ahci_port_release(port);
+    else {
+        port = ahci_port_realloc(port);
+        dprintf(1, "AHCI/%d: registering: \"%s\"\n", port->pnr, port->desc);
+        if (!port->atapi) {
+            // Register with bcv system.
+            boot_add_hd(&port->drive, port->desc, port->prio);
+        } else {
+            // fill cdidmap
+            boot_add_cd(&port->drive, port->desc, port->prio);
+        }
     }
 }
 
 // Initialize an ata controller and detect its drives.
 static void
-ahci_init_controller(int bdf)
+ahci_init_controller(struct pci_device *pci)
 {
     struct ahci_ctrl_s *ctrl = malloc_fseg(sizeof(*ctrl));
-    u32 val;
+    struct ahci_port_s *port;
+    u16 bdf = pci->bdf;
+    u32 val, pnr, max;
 
     if (!ctrl) {
         warn_noalloc();
         return;
     }
+
+    if (bounce_buf_init() < 0) {
+        warn_noalloc();
+        free(ctrl);
+        return;
+    }
+
+    ctrl->pci_tmp = pci;
     ctrl->pci_bdf = bdf;
     ctrl->iobase = pci_config_readl(bdf, PCI_BASE_ADDRESS_5);
     ctrl->irq = pci_config_readb(bdf, PCI_INTERRUPT_LINE);
     dprintf(1, "AHCI controller at %02x.%x, iobase %x, irq %d\n",
             bdf >> 3, bdf & 7, ctrl->iobase, ctrl->irq);
 
+    pci_config_maskw(bdf, PCI_COMMAND, 0,
+                     PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+
     val = ahci_ctrl_readl(ctrl, HOST_CTL);
     ahci_ctrl_writel(ctrl, HOST_CTL, val | HOST_CTL_AHCI_EN);
 
@@ -455,7 +628,15 @@ ahci_init_controller(int bdf)
     dprintf(2, "AHCI: cap 0x%x, ports_impl 0x%x\n",
             ctrl->caps, ctrl->ports);
 
-    run_thread(ahci_detect, ctrl);
+    max = ctrl->caps & 0x1f;
+    for (pnr = 0; pnr <= max; pnr++) {
+        if (!(ctrl->ports & (1 << pnr)))
+            continue;
+        port = ahci_port_alloc(ctrl, pnr);
+        if (port == NULL)
+            continue;
+        run_thread(ahci_port_detect, port);
+    }
 }
 
 // Locate and init ahci controllers.
@@ -463,13 +644,13 @@ static void
 ahci_init(void)
 {
     // Scan PCI bus for ATA adapters
-    int bdf, max;
-    foreachpci(bdf, max) {
-        if (pci_config_readw(bdf, PCI_CLASS_DEVICE) != PCI_CLASS_STORAGE_SATA)
+    struct pci_device *pci;
+    foreachpci(pci) {
+        if (pci->class != PCI_CLASS_STORAGE_SATA)
             continue;
-        if (pci_config_readb(bdf, PCI_CLASS_PROG) != 1 /* AHCI rev 1 */)
+        if (pci->prog_if != 1 /* AHCI rev 1 */)
             continue;
-        ahci_init_controller(bdf);
+        ahci_init_controller(pci);
     }
 }