2 * Acess2 Kernel - AHCI Driver
3 * - By John Hodge (thePowersGang)
16 #define MAX_CONTROLLERS 4
19 int AHCI_Install(char **Arguments);
20 int AHCI_Cleanup(void);
23 MODULE_DEFINE(0, VERSION, AHCI, AHCI_Install, AHCI_Cleanup, "LVM", NULL);
24 tAHCI_Ctrlr gaAHCI_Controllers[MAX_CONTROLLERS];
27 int AHCI_Install(char **Arguments)
30 LOG("offsetof(struct sAHCI_MemSpace, Ports) = %x", offsetof(struct sAHCI_MemSpace, Ports));
31 ASSERT( offsetof(struct sAHCI_MemSpace, Ports) == 0x100 );
33 // 0106XX = Standard, 010400 = RAID
36 while( (id = PCI_GetDeviceByClass(0x010601, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
38 tAHCI_Ctrlr *ctrlr = &gaAHCI_Controllers[i];
39 ctrlr->PMemBase = PCI_GetBAR(id, 5);
41 if( !ctrlr->PMemBase )
43 // ctrlr->PMemBase = PCI_AllocateBAR(id, 5);
44 // TODO: Allocate space
48 if( (ctrlr->PMemBase & 0x1FFF) ) {
49 Log_Warning("AHCI", "Controller %i [PCI %i] is invalid (BAR5=%P)",
50 i, id, ctrlr->PMemBase);
54 ctrlr->IRQ = PCI_GetIRQ(id);
56 // Prepare MMIO (two pages according to the spec)
57 ctrlr->MMIO = (void*)MM_MapHWPages(ctrlr->PMemBase, 2);
59 LOG("%i [%i]: %P/IRQ%i mapped to %p",
60 i, id, ctrlr->PMemBase, ctrlr->IRQ, ctrlr->MMIO);
62 LOG(" CAP = %x, PI = %x, VS = %x",
63 ctrlr->MMIO->CAP, ctrlr->MMIO->PI, ctrlr->MMIO->VS);
68 Log_Notice("AHCI", "Only up to %i controllers are supported", MAX_CONTROLLERS);
74 int AHCI_Cleanup(void)
79 int AHCI_InitSys(tAHCI_Ctrlr *Ctrlr)
82 Ctrlr->MMIO->GHC |= AHCI_GHC_AE;
83 // 2. Enumerate ports using PI
85 for( int i = 0; i < 32; i ++ )
87 if( !(Ctrlr->MMIO->PI & (1 << i)) )
89 volatile struct s_port *port = &Ctrlr->MMIO->Ports[i];
91 // 3. (for each port) Ensure that port is not running
92 // - if PxCMD.(ST|CR|FRE|FR) all are clear, port is idle
93 if( (port->PxCMD & (AHCI_PxCMD_ST|AHCI_PxCMD_CR|AHCI_PxCMD_FRE|AHCI_PxCMD_FR)) == 0 )
95 // - Idle is set by clearing PxCMD.ST and waiting for .CR to clear (timeout 500ms)
96 // - AND .FRE = 0, checking .FR (timeout 500ms again)
99 // > On timeout, port/HBA reset
101 for( int i = 0; i < 32; i ++ )
103 if( !(Ctrlr->MMIO->PI & (1 << i)) )
105 volatile struct s_port *port = &Ctrlr->MMIO->Ports[i];
107 while( (port->PxCMD & (AHCI_PxCMD_CR|AHCI_PxCMD_FR)) && now()-basetime < 500 )
110 if( !(port->PxCMD & (AHCI_PxCMD_CR|AHCI_PxCMD_FR)) )
112 Log_Error("AHCI", "Port %i did not return to idle", i);
115 // 4. Read CAP.NCS to get number of command slots
116 // 5. Allocate PxCLB and PxFB for each port (setting PxCMD.FRE once done)
117 // 6. Clear PxSERR with 1 to each implimented bit
118 // > Clear PxIS then IS.IPS before setting PxIE/GHC.IE
119 // 7. Set PxIE with desired interrupts and set GHC.IE
121 // Detect present ports using:
122 // > PxTFD.STS.BSY = 0, PxTFD.STS.DRQ = 0, and PxSSTS.DET = 3