Built code to support issuing control commands.
This commit is contained in:
parent
f0b4968b62
commit
568d7ee600
202
sys/dev/ahci.c
202
sys/dev/ahci.c
@ -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
20
sys/include/sga.h
Normal 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__ */
|
||||||
|
|
Loading…
Reference in New Issue
Block a user