fc12377624dfb183347eb77909ad931f46dfdb5f
[tpg/acess2.git] / KernelLand / Modules / Storage / AHCI / ahci.c
1 /*
2  * Acess2 Kernel - AHCI Driver
3  * - By John Hodge (thePowersGang)
4  *
5  * ahci.c
6  * - Driver core
7  */
8 #define DEBUG   1
9 #define VERSION 0x0001
10 #include <acess.h>
11 #include <modules.h>
12 #include <drv_pci.h>
13 #include "ahci.h"
14
15 // === CONSTANTS ===
16 #define MAX_CONTROLLERS 4
17
18 // === PROTOTYPES ===
19  int    AHCI_Install(char **Arguments);
20  int    AHCI_Cleanup(void);
21
22 // === GLOABLS ===
23 MODULE_DEFINE(0, VERSION, AHCI, AHCI_Install, AHCI_Cleanup, "LVM", NULL);
24 tAHCI_Ctrlr     gaAHCI_Controllers[MAX_CONTROLLERS];
25
26 // === CODE ====
27 int AHCI_Install(char **Arguments)
28 {
29
30         LOG("offsetof(struct sAHCI_MemSpace, Ports) = %x", offsetof(struct sAHCI_MemSpace, Ports));
31         ASSERT( offsetof(struct sAHCI_MemSpace, Ports) == 0x100 );
32
33         // 0106XX = Standard, 010400 = RAID
34          int    i = 0;
35          int    id = -1;
36         while( (id = PCI_GetDeviceByClass(0x010601, 0xFFFFFF, id)) >= 0 && i < MAX_CONTROLLERS )
37         {
38                 tAHCI_Ctrlr *ctrlr = &gaAHCI_Controllers[i];
39                 ctrlr->PMemBase = PCI_GetBAR(id, 5);
40                 // 
41                 if( !ctrlr->PMemBase )
42                 {
43                         // ctrlr->PMemBase = PCI_AllocateBAR(id, 5);
44                         // TODO: Allocate space
45                         continue ;
46                 }
47                 // - IO Address?
48                 if( (ctrlr->PMemBase & 0x1FFF) ) {
49                         Log_Warning("AHCI", "Controller %i [PCI %i] is invalid (BAR5=%P)",
50                                 i, id, ctrlr->PMemBase);
51                         continue;
52                 }
53                 
54                 ctrlr->IRQ = PCI_GetIRQ(id);
55
56                 // Prepare MMIO (two pages according to the spec)
57                 ctrlr->MMIO = (void*)MM_MapHWPages(ctrlr->PMemBase, 2);
58
59                 LOG("%i [%i]: %P/IRQ%i mapped to %p",
60                         i, id, ctrlr->PMemBase, ctrlr->IRQ, ctrlr->MMIO);
61
62                 LOG(" CAP = %x, PI = %x, VS = %x",
63                         ctrlr->MMIO->CAP, ctrlr->MMIO->PI, ctrlr->MMIO->VS);
64         
65                 i ++;
66         }
67         if( id >= 0 ) {
68                 Log_Notice("AHCI", "Only up to %i controllers are supported", MAX_CONTROLLERS);
69         }
70         
71         return 0;
72 }
73
74 int AHCI_Cleanup(void)
75 {
76         return 0;
77 }
78
79 int AHCI_InitSys(tAHCI_Ctrlr *Ctrlr)
80 {
81         // 1. Set GHC.AE
82         Ctrlr->MMIO->GHC |= AHCI_GHC_AE;
83         // 2. Enumerate ports using PI
84         tTime   basetime;
85         for( int i = 0; i < 32; i ++ )
86         {
87                 if( !(Ctrlr->MMIO->PI & (1 << i)) )
88                         continue ;
89                 volatile struct s_port  *port = &Ctrlr->MMIO->Ports[i];
90                 nPorts ++;
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 )
94                         continue ;
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)
97                 port->PxCMD = 0;
98                 basetime = now();
99                 //  > On timeout, port/HBA reset
100         }
101         for( int i = 0; i < 32; i ++ )
102         {
103                 if( !(Ctrlr->MMIO->PI & (1 << i)) )
104                         continue ;
105                 volatile struct s_port  *port = &Ctrlr->MMIO->Ports[i];
106         
107                 while( (port->PxCMD & (AHCI_PxCMD_CR|AHCI_PxCMD_FR)) && now()-basetime < 500 )
108                         Time_Delay(10);
109                 
110                 if( !(port->PxCMD & (AHCI_PxCMD_CR|AHCI_PxCMD_FR)) )
111                 {
112                         Log_Error("AHCI", "Port %i did not return to idle", i);
113                 }
114         }
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
120         
121         // Detect present ports using:
122         // > PxTFD.STS.BSY = 0, PxTFD.STS.DRQ = 0, and PxSSTS.DET = 3
123         return 0;
124 }
125

UCC git Repository :: git.ucc.asn.au