diff --git a/sys/dev/ahci.c b/sys/dev/ahci.c index 33fb999..6207fa2 100644 --- a/sys/dev/ahci.c +++ b/sys/dev/ahci.c @@ -3,6 +3,41 @@ #include #include +#include + +/* + * SATA Definitions + */ + +typedef struct SATAFIS_REG_H2D { + uint8_t type; // 0x27 + uint8_t flag; + uint8_t command; + uint8_t feature0; + uint8_t lba0; + uint8_t lba1; + uint8_t lba2; + uint8_t device; + uint8_t lba3; + uint8_t lba4; + uint8_t lba5; + uint8_t feature1; + uint8_t count0; + uint8_t count1; + uint8_t icc; + uint8_t control; + uint8_t _rsvd[4]; +} SATAFIS_REG_H2D; + +#define SATAFIS_REG_H2D_FLAG_COMMAND 0x80 /* Command Flag */ + +#define SATAFIS_TYPE_REG_H2D 0x27 + +#define SATAFIS_CMD_IDENTIFY 0xEC + +/* + * AHCI Definitions + */ typedef struct AHCIDevice { @@ -71,10 +106,20 @@ typedef struct AHCIPort #define AHCIPORT_CMD_SUD 0x00000002 /* Spin-Up Device */ #define AHCIPORT_CMD_ST 0x00000001 /* Start */ +#define AHCIPORT_TFD_BSY 0x00000080 /* Port Busy */ +#define AHCIPORT_TFD_DRQ 0x00000004 /* Data Transfer Requested */ +#define AHCIPORT_TFD_ERR 0x00000001 /* Error during Transfer */ + +#define AHCIPORT_SSTS_DETMASK 0x0000000F /* Device Detection (DET) Mask */ +#define AHCIPORT_SSTS_DETNP 0x00000000 /* DET: Not Present */ +#define AHCIPORT_SSTS_DETNOTEST 0x00000001 /* DET: Phy not established */ +#define AHCIPORT_SSTS_DETPE 0x00000003 /* DET: Present and Established */ +#define AHCIPORT_SSTS_DETNE 0x00000004 /* DET: Not Enabled or in BIST mode */ + #define AHCI_ABAR 5 #define AHCI_PORT_OFFSET 0x100 #define AHCI_PORT_LENGTH 0x80 -#define AHCI_MAX_PORTS 32 +#define AHCI_MAX_PORTS 8 #define AHCI_MAX_CMDS 32 /* @@ -142,8 +187,10 @@ typedef struct AHCIRecvFIS typedef struct AHCI { PCIDevice dev; + // Device Space AHCIHostControl *hc; AHCIPort *port[AHCI_MAX_PORTS]; + // Driver Memory AHCICommandList *clst[AHCI_MAX_PORTS]; AHCICommandTable *ctbl[AHCI_MAX_PORTS][AHCI_MAX_CMDS]; AHCIRecvFIS *rfis[AHCI_MAX_PORTS]; @@ -171,6 +218,12 @@ AHCI_Init(uint32_t bus, uint32_t slot, uint32_t func) return; } + // XXX: Temporary until we have a slab allocator +#define PGSIZE 4096 + ASSERT(sizeof(AHCI) <= PGSIZE); + ASSERT(sizeof(AHCICommandList) <= PGSIZE); + ASSERT(sizeof(AHCIRecvFIS) <= PGSIZE); + int deviceIdx = 0; while (deviceList[deviceIdx].device != 0x0) { if (deviceList[deviceIdx].device == device) { @@ -183,6 +236,78 @@ AHCI_Init(uint32_t bus, uint32_t slot, uint32_t func) } } +uint64_t +AHCI_IssueCommand(AHCI *ahci, int port, SGArray *sga, void *cfis, int len) +{ + // XXX: support multiple commands + volatile AHCICommandList *cl = ahci->clst[port]; + volatile AHCICommandTable *ct = ahci->ctbl[port][0]; + volatile AHCIPort *p = ahci->port[port]; + + // Copy Command FIS + memcpy(&ct->cfis, cfis, len); + + // Convert SGArray into PRDT + int i; + for (i = 0; i < sga->len; i++) + { + ct->prdt[i].dba = sga->entries[i].offset; + ct->prdt[i].descInfo = sga->entries[i].length - 1; + // Must be a multiple of word size + ASSERT(sga->entries[i].length % 2 == 0); + } + + // Specify cfis length and prdt entries; + // XXX: support multiple commands + cl->cmds[0].descInfo = (sga->len << 16) | (len / 4); + + p->ci = 1; +} + +void +AHCI_WaitPort(AHCI *ahci, int port) +{ + volatile AHCIPort *p = ahci->port[port]; + while (1) { + if (((p->tfd & AHCIPORT_TFD_BSY) == 0) && (p->ci != 0)) { + return; + } + + // implement timeouts + } +} + +void +AHCI_IdentifyPort(AHCI *ahci, int port) +{ + volatile AHCIPort *p = ahci->port[port]; + SGArray sga; + SATAFIS_REG_H2D fis; + uint8_t buf[512]; + + kprintf("AHCI: Signature %08x\n", p->sig); + + memset(&fis, 0, sizeof(fis)); + fis.type = SATAFIS_TYPE_REG_H2D; + fis.flag = SATAFIS_REG_H2D_FLAG_COMMAND; + fis.command = SATAFIS_CMD_IDENTIFY; + fis.count0 = 1; + fis.count1 = 0; + + sga.len = 1; + // VA2PA + sga.entries[0].offset = &buf; + sga.entries[0].length = 512; + + AHCI_IssueCommand(ahci, port, &sga, &fis, sizeof(fis)); + kprintf("AHCI: Identify Issued Port %d\n", port); + AHCI_WaitPort(ahci, port); + + kprintf("AHCI: Identify Succeeded Port %d\n", port); + + return; +} + void AHCI_ResetPort(AHCI *ahci, int port) { @@ -192,14 +317,14 @@ AHCI_ResetPort(AHCI *ahci, int port) AHCIPORT_CMD_FRE | AHCIPORT_CMD_FR; // Wait for controller to be idle - if (cmd & cmd_mask != 0) { + if ((cmd & cmd_mask) != 0) { int tries; for (tries = 0; tries < 2; tries++) { cmd = cmd & ~(AHCIPORT_CMD_ST | AHCIPORT_CMD_FRE); p->cmd = cmd; // sleep 500ms cmd = p->cmd; - if (cmd & cmd_mask != 0) { + if ((cmd & cmd_mask) != 0) { kprintf("AHCI: failed to reset port %d\n", port); } } @@ -207,8 +332,22 @@ AHCI_ResetPort(AHCI *ahci, int port) // Reset error p->serr = 0xFFFFFFFF; + p->serr = 0x00000000; - // + // Check port + uint32_t ssts = p->ssts; + if ((ssts & AHCIPORT_SSTS_DETMASK) == AHCIPORT_SSTS_DETNP) { + kprintf("AHCI: Device not present on port %d\n", port); + return; + } + if ((ssts & AHCIPORT_SSTS_DETMASK) == AHCIPORT_SSTS_DETNOTEST) { + kprintf("AHCI: Phys communication not established on port %d\n", port); + return; + } + if ((ssts & AHCIPORT_SSTS_DETNE) == AHCIPORT_SSTS_DETNOTEST) { + kprintf("AHCI: Port %d not enabled\n", port); + return; + } } void @@ -222,15 +361,18 @@ AHCI_Reset(AHCI *ahci) // Reset ports for (port = 0; port < AHCI_MAX_PORTS; port++) { - if (ahci->port[port] != 0) + if (ahci->port[port] != 0) { AHCI_ResetPort(ahci, port); + // XXX: Skipme if nothing connected + AHCI_IdentifyPort(ahci, port); + } } } void AHCI_Configure(PCIDevice dev) { - AHCI ahci; + AHCI *ahci = (AHCI *)PAlloc_AllocPage(); PCI_Configure(&dev); @@ -247,27 +389,63 @@ AHCI_Configure(PCIDevice dev) dev.bars[bar].type == PCIBAR_TYPE_IO ? "IO" : "Mem"); } - // memcpy dev to ahci.dev + // Copy PCIDevice structure + memcpy(&ahci->dev, &dev, sizeof(dev)); + + // XXX: Register IRQ // Setup - ahci.hc = dev.bars[AHCI_ABAR].base; + ahci->hc = dev.bars[AHCI_ABAR].base; - uint32_t ports = ahci.hc->pi; - uint32_t vers = ahci.hc->vs; + uint32_t ports = ahci->hc->pi; + uint32_t vers = ahci->hc->vs; kprintf("AHCI: Version %d.%d, Ports: 0x%08x\n", vers >> 16, vers & 0xFFFF, ports); + if (ports > ((1 << AHCI_MAX_PORTS) - 1)) + { + kprintf("AHCI: Currently only supports %d ports\n", AHCI_MAX_PORTS); + } int p; for (p = 0; p < AHCI_MAX_PORTS; p++) { if (ports & (1 << p)) { - ahci.port[p] = dev.bars[AHCI_ABAR].base + + ahci->port[p] = dev.bars[AHCI_ABAR].base + AHCI_PORT_OFFSET + AHCI_PORT_LENGTH * p; } else { - ahci.port[p] = 0; + ahci->port[p] = 0; } } + + // Allocate memory and setup pointers + for (p = 0; p < AHCI_MAX_PORTS; p++) + { + volatile AHCIPort *port = ahci->port[p]; + if (port != 0) { + int c; + for (c = 0; c < AHCI_MAX_CMDS; c++) + { + ahci->ctbl[p][c] = (AHCICommandTable *)PAlloc_AllocPage(); + memset(ahci->ctbl[p][c], 0, sizeof(AHCICommandTable)); + // XXX: VA2PA + ahci->clst[p]->cmds[c].ctba = ahci->ctbl[p][c]; + } + + ahci->clst[p] = (AHCICommandList *)PAlloc_AllocPage(); + memset(ahci->clst[p], 0, sizeof(AHCICommandList)); + // XXX: VA2PA + port->clba = ahci->clst[p]; + + ahci->rfis[p] = (AHCIRecvFIS *)PAlloc_AllocPage(); + memset(ahci->rfis[p], 0, sizeof(AHCIRecvFIS)); + // XXX: VA2PA + port->fb = ahci->rfis[p]; + } + } + + // Reset controller and ports + AHCI_Reset(ahci); } diff --git a/sys/include/sga.h b/sys/include/sga.h new file mode 100644 index 0000000..b4c3287 --- /dev/null +++ b/sys/include/sga.h @@ -0,0 +1,20 @@ + +#ifndef __SGA_H__ +#define __SGA_H__ + +#define SGARRAY_MAX_ENTRIES 32 + +typedef struct SGEntry +{ + uint64_t offset; + uint64_t length; +} SGEntry; + +typedef struct SGArray +{ + uint32_t len; + SGEntry entries[SGARRAY_MAX_ENTRIES]; +} SGArray; + +#endif /* __SGA_H__ */ +