6e21584b24a9bcebd42d87dead135f177849033b
[reactos.git] / drivers / storage / storahci / storahci.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GNU GPLv2 only as published by the Free Software Foundation
4 * PURPOSE: To Implement AHCI Miniport driver targeting storport NT 5.2
5 * PROGRAMMERS: Aman Priyadarshi (aman.eureka@gmail.com)
6 */
7
8 #include "storahci.h"
9
10 BOOLEAN AhciAdapterReset(
11 __in PAHCI_ADAPTER_EXTENSION adapterExtension
12 );
13
14 VOID AhciZeroMemory(
15 __in PCHAR buffer,
16 __in ULONG bufferSize
17 );
18
19 /**
20 * @name AhciPortInitialize
21 * @implemented
22 *
23 * Initialize port by setting up PxCLB & PxFB Registers
24 *
25 * @param portExtension
26 *
27 * @return
28 * Return true if intialization was successful
29 */
30 BOOLEAN AhciPortInitialize(
31 __in PAHCI_PORT_EXTENSION portExtension
32 )
33 {
34 ULONG mappedLength;
35 PAHCI_MEMORY_REGISTERS abar;
36 PAHCI_ADAPTER_EXTENSION adapterExtension;
37 STOR_PHYSICAL_ADDRESS commandListPhysical, receivedFISPhysical;
38
39 adapterExtension = portExtension->AdapterExtension;
40 abar = adapterExtension->ABAR_Address;
41 portExtension->Port = &abar->PortList[portExtension->PortNumber];
42
43 commandListPhysical = StorPortGetPhysicalAddress(adapterExtension, NULL, portExtension->CommandList, &mappedLength);
44 if (mappedLength == 0 || (commandListPhysical.LowPart % 1024) != 0)
45 return FALSE;
46
47 receivedFISPhysical = StorPortGetPhysicalAddress(adapterExtension, NULL, portExtension->ReceivedFIS, &mappedLength);
48 if (mappedLength == 0 || (commandListPhysical.LowPart % 256) != 0)
49 return FALSE;
50
51 // 10.1.2 For each implemented port, system software shall allocate memory for and program:
52 //  PxCLB and PxCLBU (if CAP.S64A is set to ‘1’)
53 //  PxFB and PxFBU (if CAP.S64A is set to ‘1’)
54 //Note: Assuming 32bit support only
55 StorPortWriteRegisterUlong(adapterExtension, &portExtension->Port->CLB, commandListPhysical.LowPart);
56 StorPortWriteRegisterUlong(adapterExtension, &portExtension->Port->FB, receivedFISPhysical.LowPart);
57
58 return TRUE;
59 }// -- AhciPortInitialize();
60
61 /**
62 * @name AhciAllocateResourceForAdapter
63 * @implemented
64 *
65 * Allocate memory from poll for required pointers
66 *
67 * @param adapterExtension
68 * @param ConfigInfo
69 *
70 * @return
71 * return TRUE if allocation was successful
72 */
73 BOOLEAN AhciAllocateResourceForAdapter(
74 __in PAHCI_ADAPTER_EXTENSION adapterExtension,
75 __in PPORT_CONFIGURATION_INFORMATION ConfigInfo
76 )
77 {
78 PVOID portsExtension = NULL;
79 PCHAR nonCachedExtension;
80 ULONG portCount, portImplemented, status, index, NCS, AlignedNCS, nonCachedExtensionSize, currentCount;
81
82 // 3.1.1 NCS = CAP[12:08] -> Align
83 NCS = (adapterExtension->CAP & 0xF00) >> 8;
84 AlignedNCS = ((NCS/8) + 1) * 8;
85
86 // get port count -- Number of set bits in `adapterExtension->PortImplemented`
87 portCount = 0;
88 portImplemented = adapterExtension->PortImplemented;
89 while(portImplemented > 0)
90 {
91 portCount++;
92 portImplemented &= (portImplemented-1);
93 }
94
95 nonCachedExtensionSize = sizeof(AHCI_COMMAND_HEADER) * AlignedNCS + //should be 1K aligned
96 sizeof(AHCI_RECEIVED_FIS);
97 //align nonCachedExtensionSize to 1K
98 nonCachedExtensionSize = (((nonCachedExtensionSize - 1) / 0x400) + 1) * 0x400;
99 nonCachedExtensionSize *= portCount;
100
101 adapterExtension->NonCachedExtension = StorPortGetUncachedExtension(adapterExtension, ConfigInfo, nonCachedExtensionSize);
102 if (adapterExtension->NonCachedExtension == NULL)
103 return FALSE;
104
105 nonCachedExtension = (PCHAR)adapterExtension->NonCachedExtension;
106
107 AhciZeroMemory(nonCachedExtension, nonCachedExtensionSize);
108
109
110 // allocate memory for port extension
111 status = StorPortAllocatePool(
112 adapterExtension,
113 portCount * sizeof(AHCI_PORT_EXTENSION),
114 AHCI_POOL_TAG,
115 (PVOID*)&portsExtension);
116
117 if (status != STOR_STATUS_SUCCESS)
118 return FALSE;
119
120 AhciZeroMemory((PCHAR)portsExtension, portCount * sizeof(AHCI_PORT_EXTENSION));
121
122 nonCachedExtensionSize /= portCount;
123 currentCount = 0;
124 for (index = 0; index < 32; index++)
125 {
126 if ((adapterExtension->PortImplemented & (1<<index)) != 0)
127 {
128 adapterExtension->PortExtension[index] = (PAHCI_PORT_EXTENSION)((PCHAR)portsExtension + sizeof(AHCI_PORT_EXTENSION) * currentCount);
129
130 adapterExtension->PortExtension[index]->PortNumber = index;
131 adapterExtension->PortExtension[index]->AdapterExtension = adapterExtension;
132 adapterExtension->PortExtension[index]->CommandList = (PAHCI_COMMAND_HEADER)(nonCachedExtension + (currentCount*nonCachedExtensionSize));
133 adapterExtension->PortExtension[index]->ReceivedFIS = (PAHCI_RECEIVED_FIS)((PCHAR)adapterExtension->PortExtension[index]->CommandList + sizeof(AHCI_COMMAND_HEADER) * AlignedNCS);
134 currentCount++;
135 }
136 }
137
138 return TRUE;
139 }// -- AhciAllocateResourceForAdapter();
140
141 /**
142 * @name AhciFindAdapter
143 * @implemented
144 *
145 * The HwStorFindAdapter routine uses the supplied configuration to determine whether a specific
146 * HBA is supported and, if it is, to return configuration information about that adapter.
147 *
148 * 10.1 Platform Communication
149 * http://www.intel.in/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_2.pdf
150
151 * @param DeviceExtension
152 * @param HwContext
153 * @param BusInformation
154 * @param ArgumentString
155 * @param ConfigInfo
156 * @param Reserved3
157 *
158 * @return
159 * SP_RETURN_FOUND
160 * Indicates that a supported HBA was found and that the HBA-relevant configuration information was successfully determined and set in the PORT_CONFIGURATION_INFORMATION structure.
161 *
162 * SP_RETURN_ERROR
163 * Indicates that an HBA was found but there was an error obtaining the configuration information. If possible, such an error should be logged with StorPortLogError.
164 *
165 * SP_RETURN_BAD_CONFIG
166 * Indicates that the supplied configuration information was invalid for the adapter.
167 *
168 * SP_RETURN_NOT_FOUND
169 * Indicates that no supported HBA was found for the supplied configuration information.
170 *
171 * @remarks Called by Storport.
172 */
173 ULONG AhciFindAdapter(
174 IN PVOID DeviceExtension,
175 __in PVOID HwContext,
176 __in PVOID BusInformation,
177 __in IN PVOID ArgumentString,
178 __inout PPORT_CONFIGURATION_INFORMATION ConfigInfo,
179 __in PBOOLEAN Reserved3
180 )
181 {
182 ULONG ghc;
183 ULONG index;
184 ULONG portCount, portImplemented;
185 ULONG pci_cfg_len;
186 UCHAR pci_cfg_buf[0x30];
187
188 PAHCI_MEMORY_REGISTERS abar;
189 PPCI_COMMON_CONFIG pciConfigData;
190 PAHCI_ADAPTER_EXTENSION adapterExtension;
191
192 adapterExtension = (PAHCI_ADAPTER_EXTENSION)DeviceExtension;
193 adapterExtension->SlotNumber = ConfigInfo->SlotNumber;
194 adapterExtension->SystemIoBusNumber = ConfigInfo->SystemIoBusNumber;
195
196 // get PCI configuration header
197 pci_cfg_len = StorPortGetBusData(
198 adapterExtension,
199 PCIConfiguration,
200 adapterExtension->SystemIoBusNumber,
201 adapterExtension->SlotNumber,
202 (PVOID)pci_cfg_buf,
203 (ULONG)0x30);
204
205 if (pci_cfg_len != 0x30)
206 return SP_RETURN_ERROR;//Not a valid device at the given bus number
207
208 pciConfigData = (PPCI_COMMON_CONFIG)pci_cfg_buf;
209 adapterExtension->VendorID = pciConfigData->VendorID;
210 adapterExtension->DeviceID = pciConfigData->DeviceID;
211 adapterExtension->RevisionID = pciConfigData->RevisionID;
212 // The last PCI base address register (BAR[5], header offset 0x24) points to the AHCI base memory, it’s called ABAR (AHCI Base Memory Register).
213 adapterExtension->AhciBaseAddress = pciConfigData->u.type0.BaseAddresses[5] & (0xFFFFFFF0);
214
215 // 2.1.11
216 abar = NULL;
217 if (ConfigInfo->NumberOfAccessRanges > 0)
218 {
219 for (index = 0; index < ConfigInfo->NumberOfAccessRanges; index++)
220 {
221 if ((*(ConfigInfo->AccessRanges))[index].RangeStart.QuadPart == adapterExtension->AhciBaseAddress)
222 {
223 abar = (PAHCI_MEMORY_REGISTERS)StorPortGetDeviceBase(
224 adapterExtension,
225 ConfigInfo->AdapterInterfaceType,
226 ConfigInfo->SystemIoBusNumber,
227 (*(ConfigInfo->AccessRanges))[index].RangeStart,
228 (*(ConfigInfo->AccessRanges))[index].RangeLength,
229 (BOOLEAN)!(*(ConfigInfo->AccessRanges))[index].RangeInMemory);
230 break;
231 }
232 }
233 }
234
235 if (abar == NULL)
236 return SP_RETURN_ERROR; // corrupted information supplied
237
238 adapterExtension->ABAR_Address = abar;
239 adapterExtension->CAP = StorPortReadRegisterUlong(adapterExtension, &abar->CAP);
240 adapterExtension->CAP2 = StorPortReadRegisterUlong(adapterExtension, &abar->CAP2);
241 adapterExtension->Version = StorPortReadRegisterUlong(adapterExtension, &abar->VS);
242
243 // 10.1.2
244 // 1. Indicate that system software is AHCI aware by setting GHC.AE to ‘1’.
245 // 3.1.2 -- AE bit is read-write only if CAP.SAM is '0'
246 ghc = StorPortReadRegisterUlong(adapterExtension, &abar->GHC);
247 // AE := Highest Significant bit of GHC
248 if ((ghc & (0x1<<31)) == 1)//Hmm, controller was already in power state
249 {
250 // reset controller to have it in know state
251 DebugPrint("AhciFindAdapter -> AE Already set, Reset()\n");
252 if (!AhciAdapterReset(adapterExtension))
253 return SP_RETURN_ERROR;// reset failed
254 }
255
256 ghc = 0x1<<31;// only AE=1
257 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc);
258
259 adapterExtension->IS = abar->IS;
260 adapterExtension->PortImplemented = StorPortReadRegisterUlong(adapterExtension, &abar->PI);
261
262 if (adapterExtension->PortImplemented == 0)
263 return SP_RETURN_ERROR;
264
265 ConfigInfo->MaximumTransferLength = 128 * 1024;//128 KB
266 ConfigInfo->NumberOfPhysicalBreaks = 0x21;
267 ConfigInfo->MaximumNumberOfTargets = 1;
268 ConfigInfo->MaximumNumberOfLogicalUnits = 1;
269 ConfigInfo->ResetTargetSupported = TRUE;
270 ConfigInfo->NumberOfBuses = 32;
271 ConfigInfo->SynchronizationModel = StorSynchronizeFullDuplex;
272 ConfigInfo->ScatterGather = TRUE;
273
274 // allocate necessary resource for each port
275 if (!AhciAllocateResourceForAdapter(adapterExtension, ConfigInfo))
276 return SP_RETURN_ERROR;
277
278 for (index = 0; index < 32; index++)
279 {
280 if ((adapterExtension->PortImplemented & (1<<index)) != 0)
281 AhciPortInitialize(adapterExtension->PortExtension[index]);
282 }
283
284 // Turn IE -- Interrupt Enabled
285 ghc |= 0x2;
286 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc);
287
288 return SP_RETURN_FOUND;
289 }// -- AhciFindAdapter();
290
291 /**
292 * @name DriverEntry
293 * @implemented
294 *
295 * Initial Entrypoint for storahci miniport driver
296 *
297 * @param DriverObject
298 * @param RegistryPath
299 *
300 * @return
301 * NT_STATUS in case of driver loaded successfully.
302 */
303 ULONG DriverEntry(
304 IN PVOID DriverObject,
305 IN PVOID RegistryPath
306 )
307 {
308 HW_INITIALIZATION_DATA hwInitializationData;
309 ULONG i, status;
310
311 DebugPrint("Storahci -> DriverEntry()\n");
312
313 // initialize the hardware data structure
314 AhciZeroMemory((PCHAR)&hwInitializationData, sizeof(HW_INITIALIZATION_DATA));
315
316 // set size of hardware initialization structure
317 hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
318
319 // identity required miniport entry point routines
320 hwInitializationData.HwFindAdapter = AhciFindAdapter;
321
322 // adapter specific information
323 hwInitializationData.NeedPhysicalAddresses = TRUE;
324 hwInitializationData.TaggedQueuing = TRUE;
325 hwInitializationData.AutoRequestSense = TRUE;
326 hwInitializationData.MultipleRequestPerLu = TRUE;
327
328 hwInitializationData.NumberOfAccessRanges = 6;
329 hwInitializationData.AdapterInterfaceType = PCIBus;
330 hwInitializationData.MapBuffers = STOR_MAP_NON_READ_WRITE_BUFFERS;
331
332 // set required extension sizes
333 hwInitializationData.SrbExtensionSize = sizeof(AHCI_SRB_EXTENSION);
334 hwInitializationData.DeviceExtensionSize = sizeof(AHCI_ADAPTER_EXTENSION);
335
336 // register our hw init data
337 status = StorPortInitialize(
338 DriverObject,
339 RegistryPath,
340 &hwInitializationData,
341 NULL);
342
343 return status;
344 }// -- DriverEntry();
345
346 /**
347 * @name AhciAdapterReset
348 * @implemented
349 *
350 * 10.4.3 HBA Reset
351 * If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the
352 * problem, software may reset the entire HBA by setting GHC.HR to ‘1’. When software sets the GHC.HR
353 * bit to ‘1’, the HBA shall perform an internal reset action. The bit shall be cleared to ‘0’ by the HBA when
354 * the reset is complete. A software write of ‘0’ to GHC.HR shall have no effect. To perform the HBA reset,
355 * software sets GHC.HR to ‘1’ and may poll until this bit is read to be ‘0’, at which point software knows that
356 * the HBA reset has completed.
357 * If the HBA has not cleared GHC.HR to ‘0’ within 1 second of software setting GHC.HR to ‘1’, the HBA is in
358 * a hung or locked state.
359 *
360 * @param adapterExtension
361 *
362 * @return
363 * TRUE in case AHCI Controller RESTARTED successfully. i.e GHC.HR == 0
364 */
365 BOOLEAN AhciAdapterReset(
366 PAHCI_ADAPTER_EXTENSION adapterExtension
367 )
368 {
369 ULONG ghc, ticks;
370 PAHCI_MEMORY_REGISTERS abar = NULL;
371
372 abar = adapterExtension->ABAR_Address;
373
374 if (abar == NULL) // basic sanity
375 return FALSE;
376
377 // HR -- Very first bit (lowest significant)
378 ghc = 1;
379 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc);
380
381 for (ticks = 0; (ticks < 50) &&
382 (StorPortReadRegisterUlong(adapterExtension, &abar->GHC) == 1);
383 StorPortStallExecution(20000), ticks++);
384
385 if (ticks == 50)//1 second
386 return FALSE;
387
388 return TRUE;
389 }// -- AhciAdapterReset();
390
391 /**
392 * @name AhciZeroMemory
393 * @implemented
394 *
395 * Clear buffer by filling zeros
396 *
397 * @param buffer
398 */
399 VOID AhciZeroMemory(
400 __in PCHAR buffer,
401 __in ULONG bufferSize
402 )
403 {
404 ULONG i;
405 for (i = 0; i < bufferSize; i++)
406 buffer[i] = 0;
407 }// -- AhciZeroMemory();