[SeaBIOS] non-emulated AHCI hardware

Jonathan A. Kollasch jakllsch at kollasch.net
Fri Apr 22 12:19:22 CEST 2011


On Fri, Apr 22, 2011 at 02:21:08AM -0500, Scott Duplichan wrote:
> 17 Feb 15:28 2011 Jonathan Kollasch wrote:
>  
> ]I have it (mostly) working now, just have to get it polished up a bit.
> ]
> ]      Jonathan Kollasch
>  
> Hello Jonathan,
>  
> Right now I am trying to debug the same AHCI problem. The Host To Device
> FIS length change also gets me past the first polling loop in ahci_command,
> 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
-------------- next part --------------
diff --git a/src/ahci.c b/src/ahci.c
index b820e28..f514b5a 100644
--- a/src/ahci.c
+++ b/src/ahci.c
@@ -5,19 +5,18 @@
 // This file may be distributed under the terms of the GNU LGPLv3 license.
 
 #include "types.h" // u8
-#include "ioport.h" // inb
 #include "util.h" // dprintf
 #include "biosvar.h" // GET_EBDA
 #include "pci.h" // foreachpci
 #include "pci_ids.h" // PCI_CLASS_STORAGE_OTHER
-#include "pci_regs.h" // PCI_INTERRUPT_LINE
+#include "pci_regs.h" // PCI_BASE_ADDRESS_5
 #include "boot.h" // add_bcv_hd
 #include "disk.h" // struct ata_s
 #include "ata.h" // ATA_CB_STAT
-#include "ahci.h" // CDB_CMD_READ_10
 #include "blockcmd.h" // CDB_CMD_READ_10
+#include "ahci.h" // struct ahci_ctrl_s
 
-#define AHCI_MAX_RETRIES 5
+#define AHCI_MAX_RETRIES 1000000
 
 /****************************************************************
  * these bits must run in both 16bit and 32bit modes
@@ -108,7 +107,7 @@ static int ahci_command(struct ahci_port_s *port, int iswrite, int isatapi,
     u32 val, status, success, flags;
     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_fis_s  *fis  = GET_GLOBAL(port->fis);
     struct ahci_list_s *list = GET_GLOBAL(port->list);
     u32 pnr                  = GET_GLOBAL(port->pnr);
 
@@ -118,33 +117,66 @@ 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);
 
+#if 0
     val = ahci_port_readl(ctrl, pnr, PORT_CMD);
     ahci_port_writel(ctrl, pnr, PORT_CMD, val | PORT_CMD_START);
+#endif
 
     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);
 
+    //dprintf(2, "list %p\n", list);
+    //hexdump(list, 0x40);
+    //dprintf(2, "cmd  %p\n", cmd);
+    //hexdump(cmd, 0xc0);
+    //dprintf(2, "fis %p\n", fis);
+    //hexdump(fis->rfis, 0x10);
+
+    //dprintf(1, "AHCI: P%dTFD %08x\n", pnr, ahci_port_readl(ctrl, pnr, PORT_TFDATA));
+
     dprintf(2, "AHCI/%d: send cmd ...\n", pnr);
-    SET_FLATPTR(fis->rfis[2], 0);
-    ahci_port_writel(ctrl, pnr, PORT_SCR_ACT, 1);
+    //SET_FLATPTR(fis->rfis[2], 0);
+
+#if 0
+    int i;
+    for (i = 0; i < 0x14; i += 4)
+	dprintf(1, "%08x %08x\n", i, ahci_ctrl_readl(ctrl, i));
+
+    for (i = 0; i < 0x3c; i += 4)
+	dprintf(1, "%d %08x %08x\n", pnr, i, ahci_port_readl(ctrl, pnr, i));
+#endif
+
     ahci_port_writel(ctrl, pnr, PORT_CMD_ISSUE, 1);
     while (ahci_port_readl(ctrl, pnr, PORT_CMD_ISSUE)) {
+        val = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT);
+        dprintf(2, "AHCI: P%dIS %08x\n", pnr, val);
+        dprintf(2, "AHCI: P%dTFD %08x\n", pnr, ahci_port_readl(ctrl, pnr, PORT_TFDATA));
+        //hexdump(fis->rfis, 0x10);
+        //hexdump(buffer, 0x20);
+        if (val & 0x7d000000) {
+            if (val & 0x79000000)
+                return -1;
+            val = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR);
+            dprintf(1, "AHCI: P%dSERR %08x\n", pnr, val);
+        }
         yield();
     }
+#if 0
     while ((status = GET_FLATPTR(fis->rfis[2])) == 0) {
         yield();
     }
+#endif
 
+    status = ahci_port_readl(ctrl, pnr, PORT_TFDATA) & 0xff;
     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)));
@@ -253,6 +285,7 @@ static void
 ahci_port_reset(struct ahci_ctrl_s *ctrl, u32 pnr)
 {
     u32 val, count = 0;
+    u8 *fis;
 
     /* disable FIS + CMD */
     val = ahci_port_readl(ctrl, pnr, PORT_CMD);
@@ -266,16 +299,44 @@ ahci_port_reset(struct ahci_ctrl_s *ctrl, u32 pnr)
         count++;
     }
 
+    fis = memalign_tmp(256, 256);
+
+    ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR, (u32)fis);
+    ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR_HI, 0);
+    ahci_port_writel(ctrl, pnr, PORT_CMD,
+        PORT_CMD_ICC_ACTIVE|PORT_CMD_POWER_ON|PORT_CMD_SPIN_UP|PORT_CMD_FIS_RX);
+
+    ahci_port_writel(ctrl, pnr, PORT_SCR_CTL, 0x0301);
+    mdelay(5);
+    ahci_port_writel(ctrl, pnr, PORT_SCR_CTL, 0x0300);
+    mdelay(500);
+    val = ahci_port_readl(ctrl, pnr, PORT_SCR_STAT);
+    dprintf(1, "AHCI: P%dSStatus %08x\n", pnr, val);
+
     /* 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);
+
+    /* disable FIS*/
+    val = ahci_port_readl(ctrl, pnr, PORT_CMD);
+    while (val & (PORT_CMD_FIS_RX | PORT_CMD_FIS_ON) &&
+           count < AHCI_MAX_RETRIES) {
+        val &= ~(PORT_CMD_FIS_RX);
+        ahci_port_writel(ctrl, pnr, PORT_CMD, val);
+        ndelay(500);
+        val = ahci_port_readl(ctrl, pnr, PORT_CMD);
+        count++;
+    }
+    ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR, 0);
+    ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR_HI, 0);
+    free(fis);
 }
 
 static int
@@ -290,7 +351,8 @@ ahci_port_probe(struct ahci_ctrl_s *ctrl, u32 pnr)
         val = ahci_port_readl(ctrl, pnr, PORT_TFDATA);
         count++;
         if (count >= AHCI_MAX_RETRIES)
-            return -1;
+            break;
+            //return -1;
     }
 
     val = ahci_port_readl(ctrl, pnr, PORT_SCR_STAT);
@@ -327,23 +389,40 @@ ahci_port_init(struct ahci_ctrl_s *ctrl, u32 pnr)
     memset(port->fis, 0, 256);
     memset(port->cmd, 0, 256);
 
-    ahci_port_writel(ctrl, pnr, PORT_LST_ADDR, (u32)port->list);
+    /* clear status */
+    val = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR);
+    if (val)
+        ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, val);
+
+    ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, 0xffffffff);
+
     ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR, (u32)port->fis);
+    ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR_HI, 0);
     val = ahci_port_readl(ctrl, pnr, PORT_CMD);
     ahci_port_writel(ctrl, pnr, PORT_CMD, val | PORT_CMD_FIS_RX);
 
-    sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_PACKET_DEVICE);
-    rc = ahci_command(port, 0, 0, buffer, sizeof(buffer));
-    if (rc == 0) {
+    ahci_port_writel(ctrl, pnr, PORT_LST_ADDR, (u32)port->list);
+    ahci_port_writel(ctrl, pnr, PORT_LST_ADDR_HI, 0);
+    val = ahci_port_readl(ctrl, pnr, PORT_CMD);
+    ahci_port_writel(ctrl, pnr, PORT_CMD, val | PORT_CMD_START);
+
+    val = ahci_port_readl(ctrl, pnr, PORT_SIG);
+    dprintf(1, "AHCI: P%dSIG %08x\n", pnr, val);
+    dprintf(1, "AHCI: P%d buffer %p\n", pnr, buffer);
+
+    if ((ahci_port_readl(ctrl, pnr, PORT_SIG) >> 16) == 0xeb14) {
         port->atapi = 1;
+        sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_PACKET_DEVICE);
+        rc = ahci_command(port, 0, 0, buffer, sizeof(buffer));
     } else {
         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)
-            goto err;
     }
 
+    if (rc < 0)
+        goto err;
+
     port->drive.type = DTYPE_AHCI;
     port->drive.cntl_id = pnr;
     port->drive.removable = (buffer[0] & 0x80) ? 1 : 0;
@@ -426,12 +505,12 @@ ahci_detect(void *data)
     }
 }
 
-// Initialize an ata controller and detect its drives.
+// Initialize an AHCI controller and detect its drives.
 static void
 ahci_init_controller(int bdf)
 {
     struct ahci_ctrl_s *ctrl = malloc_fseg(sizeof(*ctrl));
-    u32 val;
+    u32 count = 0;
 
     if (!ctrl) {
         warn_noalloc();
@@ -439,15 +518,24 @@ ahci_init_controller(int bdf)
     }
     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);
+    dprintf(1, "AHCI controller at %02x.%x, iobase %x\n",
+            bdf >> 3, bdf & 7, ctrl->iobase);
 
     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);
+    ahci_ctrl_writel(ctrl, HOST_CTL, HOST_CTL_AHCI_EN | HOST_CTL_RESET);
+    while ((ahci_ctrl_readl(ctrl, HOST_CTL) & HOST_CTL_RESET) != 0) {
+        ndelay(500);
+        count++;
+        if (count >= AHCI_MAX_RETRIES) {
+            dprintf(1, "Reset of AHCI controller at %02x.%x timed out.\n",
+                    bdf >> 3, bdf & 7);
+            break;
+        }
+    }
+
+    ahci_ctrl_writel(ctrl, HOST_CTL, HOST_CTL_AHCI_EN);
 
     ctrl->caps = ahci_ctrl_readl(ctrl, HOST_CAP);
     ctrl->ports = ahci_ctrl_readl(ctrl, HOST_PORTS_IMPL);
diff --git a/src/ahci.h b/src/ahci.h
index 0e13e00..100a981 100644
--- a/src/ahci.h
+++ b/src/ahci.h
@@ -27,7 +27,6 @@ struct sata_cmd_fis {
 
 struct ahci_ctrl_s {
     int pci_bdf;
-    u8  irq;
     u32 iobase;
     u32 caps;
     u32 ports;


More information about the SeaBIOS mailing list