[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