#define DEBUG 1
#define VERSION 0x0001
#include <acess.h>
+#include <timers.h>
#include <modules.h>
#include <drv_pci.h>
#include "ahci.h"
// === PROTOTYPES ===
int AHCI_Install(char **Arguments);
int AHCI_Cleanup(void);
+// - Hardware init
+ int AHCI_InitSys(tAHCI_Ctrlr *Ctrlr);
// === GLOABLS ===
MODULE_DEFINE(0, VERSION, AHCI, AHCI_Install, AHCI_Cleanup, "LVM", NULL);
// === CODE ====
int AHCI_Install(char **Arguments)
{
-
+ int rv;
LOG("offsetof(struct sAHCI_MemSpace, Ports) = %x", offsetof(struct sAHCI_MemSpace, Ports));
ASSERT( offsetof(struct sAHCI_MemSpace, Ports) == 0x100 );
LOG(" CAP = %x, PI = %x, VS = %x",
ctrlr->MMIO->CAP, ctrlr->MMIO->PI, ctrlr->MMIO->VS);
-
+
+ if( (rv = AHCI_InitSys(ctrlr)) ) {
+ // Clean up controller's allocations
+ // TODO: Should an init error cause module unload?
+ return rv;
+ }
+
i ++;
}
if( id >= 0 ) {
return 0;
}
+static inline void *AHCI_AllocPage(tAHCI_Ctrlr *Ctrlr, const char *AllocName)
+{
+ void *ret;
+ #if PHYS_BITS > 32
+ if( Ctrlr->Supports64Bit )
+ ret = (void*)MM_AllocDMA(1, 64, NULL);
+ else
+ #endif
+ ret = (void*)MM_AllocDMA(1, 32, NULL);
+ if( !ret ) {
+ Log_Error("AHCI", "Unable to allocate page for '%s'", AllocName);
+ return NULL;
+ }
+ return ret;
+}
+
int AHCI_InitSys(tAHCI_Ctrlr *Ctrlr)
{
// 1. Set GHC.AE
Ctrlr->MMIO->GHC |= AHCI_GHC_AE;
// 2. Enumerate ports using PI
- tTime basetime;
+ tTime basetime = now();
for( int i = 0; i < 32; i ++ )
{
if( !(Ctrlr->MMIO->PI & (1 << i)) )
continue ;
+
+ Ctrlr->PortCount ++;
+
volatile struct s_port *port = &Ctrlr->MMIO->Ports[i];
- nPorts ++;
// 3. (for each port) Ensure that port is not running
// - if PxCMD.(ST|CR|FRE|FR) all are clear, port is idle
if( (port->PxCMD & (AHCI_PxCMD_ST|AHCI_PxCMD_CR|AHCI_PxCMD_FRE|AHCI_PxCMD_FR)) == 0 )
basetime = now();
// > On timeout, port/HBA reset
}
- for( int i = 0; i < 32; i ++ )
+ Ctrlr->Ports = malloc( Ctrlr->PortCount * sizeof(*Ctrlr->Ports) );
+ if( !Ctrlr->Ports ) {
+ return MODULE_ERR_MALLOC;
+ }
+ // - Process timeouts after all ports have been poked, saves time
+ for( int i = 0, idx = 0; i < 32; i ++ )
{
if( !(Ctrlr->MMIO->PI & (1 << i)) )
continue ;
volatile struct s_port *port = &Ctrlr->MMIO->Ports[i];
+ Ctrlr->Ports[idx].Idx = i;
+ Ctrlr->Ports[idx].MMIO = port;
+ idx ++;
while( (port->PxCMD & (AHCI_PxCMD_CR|AHCI_PxCMD_FR)) && now()-basetime < 500 )
Time_Delay(10);
- if( !(port->PxCMD & (AHCI_PxCMD_CR|AHCI_PxCMD_FR)) )
+ if( (port->PxCMD & (AHCI_PxCMD_CR|AHCI_PxCMD_FR)) )
{
Log_Error("AHCI", "Port %i did not return to idle", i);
}
}
// 4. Read CAP.NCS to get number of command slots
+ Ctrlr->NCS = (Ctrlr->MMIO->CAP & AHCI_CAP_NCS) >> AHCI_CAP_NCS_ofs;
// 5. Allocate PxCLB and PxFB for each port (setting PxCMD.FRE once done)
+ struct sAHCI_RcvdFIS *fis_vpage = NULL;
+ struct sAHCI_CmdHdr *cl_vpage = NULL;
+ for( int i = 0; i < Ctrlr->PortCount; i ++ )
+ {
+ // CLB First (1 KB alignemnt)
+ if( ((tVAddr)cl_vpage & 0xFFF) == 0 ) {
+ cl_vpage = AHCI_AllocPage(Ctrlr, "CLB");
+ if( !cl_vpage )
+ return MODULE_ERR_MALLOC;
+ }
+ Ctrlr->Ports[i].CmdList = cl_vpage;
+ cl_vpage += 1024/sizeof(*cl_vpage);
+
+ tPAddr cl_paddr = MM_GetPhysAddr(Ctrlr->Ports[i].CmdList);
+ Ctrlr->Ports[i].MMIO->PxCLB = cl_paddr;
+ #if PHYS_BITS > 32
+ Ctrlr->Ports[i].MMIO->PxCLBU = cl_paddr >> 32;
+ #endif
+
+ // Received FIS Area
+ // - If there is space for the FIS in the end of the 1K block, use it
+ if( Ctrlr->NCS <= (1024-256)/32 )
+ {
+ Ctrlr->Ports[i].RcvdFIS = (void*)(cl_vpage - 256/32);
+ }
+ else
+ {
+ if( ((tVAddr)fis_vpage & 0xFFF) == 0 ) {
+ fis_vpage = AHCI_AllocPage(Ctrlr, "FIS");
+ if( !fis_vpage )
+ return MODULE_ERR_MALLOC;
+ }
+ Ctrlr->Ports[i].RcvdFIS = fis_vpage;
+ fis_vpage ++;
+ }
+ tPAddr fis_paddr = MM_GetPhysAddr(Ctrlr->Ports[i].RcvdFIS);
+ Ctrlr->Ports[i].MMIO->PxFB = fis_paddr;
+ #if PHYS_BITS > 32
+ Ctrlr->Ports[i].MMIO->PxFBU = fis_paddr >> 32;
+ #endif
+
+ LOG("Port #%i: CLB=%p/%P, FB=%p/%P", i,
+ Ctrlr->Ports[i].CmdList, cl_paddr,
+ Ctrlr->Ports[i].RcvdFIS, fis_paddr);
+ }
// 6. Clear PxSERR with 1 to each implimented bit
// > Clear PxIS then IS.IPS before setting PxIE/GHC.IE
// 7. Set PxIE with desired interrupts and set GHC.IE
// Detect present ports using:
// > PxTFD.STS.BSY = 0, PxTFD.STS.DRQ = 0, and PxSSTS.DET = 3
+ for( int i = 0; i < Ctrlr->PortCount; i ++ )
+ {
+ if( Ctrlr->Ports[i].MMIO->PxTFD & (AHCI_PxTFD_STS_BSY|AHCI_PxTFD_STS_DRQ) )
+ continue ;
+ if( (Ctrlr->Ports[i].MMIO->PxSSTS & AHCI_PxSSTS_DET) != 3 << AHCI_PxSSTS_DET_ofs )
+ continue ;
+
+ LOG("Port #%i: Connection detected", i);
+ Ctrlr->Ports[i].bPresent = 1;
+ // TODO: Query volumes connected
+ }
return 0;
}
#define AHCI_CAP_SNCQ (1 << 30) // Supports Native Command Queuing
#define AHCI_CAP_SXS (1 << 5) // Support External SATA
#define AHCI_CAP_NCS (31 << 8) // Number of command slots (mask)
+#define AHCI_CAP_NCS_ofs 8 // (offset)
#define AHCI_GHC_AE (1 << 31) // AHCI Enable
#define AHCI_GHC_MRSM (1 << 2) // MSI Revert to Single Message
#define AHCI_PxCMD_SUD (1 << 1) // Spin-Up Device
#define AHCI_PxCMD_ST (1 << 0) // Start
+#define AHCI_PxTFD_ERR (255 << 8)
+#define AHCI_PxTFD_STS (255 << 0) // Status (latest copy of task file status register)
+#define AHCI_PxTFD_STS_BSY (1 << 7) // Interface is busy
+#define AHCI_PxTFD_STS_DRQ (1 << 3) // Data transfer requested
+#define AHCI_PxTFD_STS_ERR (1 << 0) // Error during transfer
+
+#define AHCI_PxSSTS_IPM (15 << 8) // Interface Power Management (0=NP,1=Active,2=Partial,6=Slumber)
+#define AHCI_PxSSTS_IPM_ofs 8
+#define AHCI_PxSSTS_SPD (15 << 4) // Current Interface Speed (0=NP,Generation n)
+#define AHCI_PxSSTS_SPD_ofs 4
+#define AHCI_PxSSTS_DET (15 << 0) // Device Detection (0: None, 1: Present but no PHY yet, 3: Present and PHY, 4: offline)
+#define AHCI_PxSSTS_DET_ofs 0
+
+typedef volatile struct sAHCI_MemSpace tAHCI_MemSpace;
+
struct sAHCI_MemSpace
{
Uint32 CAP; // Host Capabilities
} Ports[32];
} PACKED;
+struct sAHCI_FIS_DMASetup
+{
+ Uint32 unk[7];
+} PACKED;
+struct sAHCI_FIS_PIOSetup
+{
+ Uint32 unk[5];
+} PACKED;
+struct sAHCI_FIS_D2HRegister
+{
+ Uint32 unk[5];
+} PACKED;
+struct sAHCI_FIS_SDB
+{
+ Uint32 unk[2];
+} PACKED;
+
+struct sAHCI_RcvdFIS
+{
+ struct sAHCI_FIS_DMASetup DSFIS;
+ Uint32 _pad1[1];
+ struct sAHCI_FIS_PIOSetup PSFIS;
+ Uint32 _pad2[3];
+ struct sAHCI_FIS_D2HRegister RFIS;
+ Uint32 _pad3[1];
+ struct sAHCI_FIS_SDB SDBFIS;
+ Uint32 UFIS[64/4];
+ Uint32 _redvd[(0x100 - 0xA0) / 4];
+} PACKED;
+
struct sAHCI_CmdHdr
{
Uint16 Flags;