Built code to support issuing control commands.

This commit is contained in:
Ali Mashtizadeh 2014-07-02 16:41:56 -07:00
parent f0b4968b62
commit 568d7ee600
2 changed files with 210 additions and 12 deletions

View File

@ -3,6 +3,41 @@
#include <kassert.h> #include <kassert.h>
#include <pci.h> #include <pci.h>
#include <sga.h>
/*
* 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 typedef struct AHCIDevice
{ {
@ -71,10 +106,20 @@ typedef struct AHCIPort
#define AHCIPORT_CMD_SUD 0x00000002 /* Spin-Up Device */ #define AHCIPORT_CMD_SUD 0x00000002 /* Spin-Up Device */
#define AHCIPORT_CMD_ST 0x00000001 /* Start */ #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_ABAR 5
#define AHCI_PORT_OFFSET 0x100 #define AHCI_PORT_OFFSET 0x100
#define AHCI_PORT_LENGTH 0x80 #define AHCI_PORT_LENGTH 0x80
#define AHCI_MAX_PORTS 32 #define AHCI_MAX_PORTS 8
#define AHCI_MAX_CMDS 32 #define AHCI_MAX_CMDS 32
/* /*
@ -142,8 +187,10 @@ typedef struct AHCIRecvFIS
typedef struct AHCI typedef struct AHCI
{ {
PCIDevice dev; PCIDevice dev;
// Device Space
AHCIHostControl *hc; AHCIHostControl *hc;
AHCIPort *port[AHCI_MAX_PORTS]; AHCIPort *port[AHCI_MAX_PORTS];
// Driver Memory
AHCICommandList *clst[AHCI_MAX_PORTS]; AHCICommandList *clst[AHCI_MAX_PORTS];
AHCICommandTable *ctbl[AHCI_MAX_PORTS][AHCI_MAX_CMDS]; AHCICommandTable *ctbl[AHCI_MAX_PORTS][AHCI_MAX_CMDS];
AHCIRecvFIS *rfis[AHCI_MAX_PORTS]; AHCIRecvFIS *rfis[AHCI_MAX_PORTS];
@ -171,6 +218,12 @@ AHCI_Init(uint32_t bus, uint32_t slot, uint32_t func)
return; 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; int deviceIdx = 0;
while (deviceList[deviceIdx].device != 0x0) { while (deviceList[deviceIdx].device != 0x0) {
if (deviceList[deviceIdx].device == device) { 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 void
AHCI_ResetPort(AHCI *ahci, int port) AHCI_ResetPort(AHCI *ahci, int port)
{ {
@ -192,14 +317,14 @@ AHCI_ResetPort(AHCI *ahci, int port)
AHCIPORT_CMD_FRE | AHCIPORT_CMD_FR; AHCIPORT_CMD_FRE | AHCIPORT_CMD_FR;
// Wait for controller to be idle // Wait for controller to be idle
if (cmd & cmd_mask != 0) { if ((cmd & cmd_mask) != 0) {
int tries; int tries;
for (tries = 0; tries < 2; tries++) { for (tries = 0; tries < 2; tries++) {
cmd = cmd & ~(AHCIPORT_CMD_ST | AHCIPORT_CMD_FRE); cmd = cmd & ~(AHCIPORT_CMD_ST | AHCIPORT_CMD_FRE);
p->cmd = cmd; p->cmd = cmd;
// sleep 500ms // sleep 500ms
cmd = p->cmd; cmd = p->cmd;
if (cmd & cmd_mask != 0) { if ((cmd & cmd_mask) != 0) {
kprintf("AHCI: failed to reset port %d\n", port); kprintf("AHCI: failed to reset port %d\n", port);
} }
} }
@ -207,8 +332,22 @@ AHCI_ResetPort(AHCI *ahci, int port)
// Reset error // Reset error
p->serr = 0xFFFFFFFF; 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 void
@ -222,15 +361,18 @@ AHCI_Reset(AHCI *ahci)
// Reset ports // Reset ports
for (port = 0; port < AHCI_MAX_PORTS; port++) for (port = 0; port < AHCI_MAX_PORTS; port++)
{ {
if (ahci->port[port] != 0) if (ahci->port[port] != 0) {
AHCI_ResetPort(ahci, port); AHCI_ResetPort(ahci, port);
// XXX: Skipme if nothing connected
AHCI_IdentifyPort(ahci, port);
}
} }
} }
void void
AHCI_Configure(PCIDevice dev) AHCI_Configure(PCIDevice dev)
{ {
AHCI ahci; AHCI *ahci = (AHCI *)PAlloc_AllocPage();
PCI_Configure(&dev); PCI_Configure(&dev);
@ -247,27 +389,63 @@ AHCI_Configure(PCIDevice dev)
dev.bars[bar].type == PCIBAR_TYPE_IO ? "IO" : "Mem"); 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 // Setup
ahci.hc = dev.bars[AHCI_ABAR].base; ahci->hc = dev.bars[AHCI_ABAR].base;
uint32_t ports = ahci.hc->pi; uint32_t ports = ahci->hc->pi;
uint32_t vers = ahci.hc->vs; uint32_t vers = ahci->hc->vs;
kprintf("AHCI: Version %d.%d, Ports: 0x%08x\n", kprintf("AHCI: Version %d.%d, Ports: 0x%08x\n",
vers >> 16, vers & 0xFFFF, ports); 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; int p;
for (p = 0; p < AHCI_MAX_PORTS; p++) for (p = 0; p < AHCI_MAX_PORTS; p++)
{ {
if (ports & (1 << 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; AHCI_PORT_OFFSET + AHCI_PORT_LENGTH * p;
} else { } 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);
} }

20
sys/include/sga.h Normal file
View File

@ -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__ */