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;