- Added all HwStor Initialization Required Functions
[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 StorPortDebugPrint(0, "AhciPortInitialize()\n");
40
41 adapterExtension = portExtension->AdapterExtension;
42 abar = adapterExtension->ABAR_Address;
43 portExtension->Port = &abar->PortList[portExtension->PortNumber];
44
45 commandListPhysical = StorPortGetPhysicalAddress(adapterExtension, NULL, portExtension->CommandList, &mappedLength);
46 if (mappedLength == 0 || (commandListPhysical.LowPart % 1024) != 0){
47 StorPortDebugPrint(0, "\tcommandListPhysical mappedLength:%d\n", mappedLength);
48 return FALSE;
49 }
50
51 receivedFISPhysical = StorPortGetPhysicalAddress(adapterExtension, NULL, portExtension->ReceivedFIS, &mappedLength);
52 if (mappedLength == 0 || (receivedFISPhysical.LowPart % 256) != 0){
53 StorPortDebugPrint(0, "\treceivedFISPhysical mappedLength:%d\n", mappedLength);
54 return FALSE;
55 }
56
57 // 10.1.2 For each implemented port, system software shall allocate memory for and program:
58 //  PxCLB and PxCLBU (if CAP.S64A is set to ‘1’)
59 //  PxFB and PxFBU (if CAP.S64A is set to ‘1’)
60 //Note: Assuming 32bit support only
61 StorPortWriteRegisterUlong(adapterExtension, &portExtension->Port->CLB, commandListPhysical.LowPart);
62 StorPortWriteRegisterUlong(adapterExtension, &portExtension->Port->FB, receivedFISPhysical.LowPart);
63
64 return TRUE;
65 }// -- AhciPortInitialize();
66
67 /**
68 * @name AhciAllocateResourceForAdapter
69 * @implemented
70 *
71 * Allocate memory from poll for required pointers
72 *
73 * @param adapterExtension
74 * @param ConfigInfo
75 *
76 * @return
77 * return TRUE if allocation was successful
78 */
79 BOOLEAN AhciAllocateResourceForAdapter(
80 __in PAHCI_ADAPTER_EXTENSION adapterExtension,
81 __in PPORT_CONFIGURATION_INFORMATION ConfigInfo
82 )
83 {
84 PVOID portsExtension = NULL;
85 PCHAR nonCachedExtension;
86 ULONG portCount, portImplemented, status, index, NCS, AlignedNCS, nonCachedExtensionSize, currentCount;
87
88 StorPortDebugPrint(0, "AhciAllocateResourceForAdapter()\n");
89
90 // 3.1.1 NCS = CAP[12:08] -> Align
91 NCS = (adapterExtension->CAP & 0xF00) >> 8;
92 AlignedNCS = ((NCS/8) + 1) * 8;
93
94 // get port count -- Number of set bits in `adapterExtension->PortImplemented`
95 portCount = 0;
96 portImplemented = adapterExtension->PortImplemented;
97 while(portImplemented > 0)
98 {
99 portCount++;
100 portImplemented &= (portImplemented-1);
101 }
102
103 nonCachedExtensionSize = sizeof(AHCI_COMMAND_HEADER) * AlignedNCS + //should be 1K aligned
104 sizeof(AHCI_RECEIVED_FIS);
105 //align nonCachedExtensionSize to 1K
106 nonCachedExtensionSize = (((nonCachedExtensionSize - 1) / 0x400) + 1) * 0x400;
107 nonCachedExtensionSize *= portCount;
108
109 adapterExtension->NonCachedExtension = StorPortGetUncachedExtension(adapterExtension, ConfigInfo, nonCachedExtensionSize);
110 if (adapterExtension->NonCachedExtension == NULL) {
111 StorPortDebugPrint(0, "\tadapterExtension->NonCachedExtension == NULL\n");
112 return FALSE;
113 }
114
115 nonCachedExtension = (PCHAR)adapterExtension->NonCachedExtension;
116
117 AhciZeroMemory(nonCachedExtension, nonCachedExtensionSize);
118
119
120 // allocate memory for port extension
121 status = StorPortAllocatePool(
122 adapterExtension,
123 portCount * sizeof(AHCI_PORT_EXTENSION),
124 AHCI_POOL_TAG,
125 (PVOID*)&portsExtension);
126
127 if (status != STOR_STATUS_SUCCESS){
128 StorPortDebugPrint(0, "\tstatus != STOR_STATUS_SUCCESS\n");
129 return FALSE;
130 }
131
132 AhciZeroMemory((PCHAR)portsExtension, portCount * sizeof(AHCI_PORT_EXTENSION));
133
134 nonCachedExtensionSize /= portCount;
135 currentCount = 0;
136 for (index = 0; index < 32; index++)
137 {
138 if ((adapterExtension->PortImplemented & (1<<index)) != 0)
139 {
140 adapterExtension->PortExtension[index] = (PAHCI_PORT_EXTENSION)((PCHAR)portsExtension + sizeof(AHCI_PORT_EXTENSION) * currentCount);
141
142 adapterExtension->PortExtension[index]->PortNumber = index;
143 adapterExtension->PortExtension[index]->AdapterExtension = adapterExtension;
144 adapterExtension->PortExtension[index]->CommandList = (PAHCI_COMMAND_HEADER)(nonCachedExtension + (currentCount*nonCachedExtensionSize));
145 adapterExtension->PortExtension[index]->ReceivedFIS = (PAHCI_RECEIVED_FIS)((PCHAR)adapterExtension->PortExtension[index]->CommandList + sizeof(AHCI_COMMAND_HEADER) * AlignedNCS);
146 currentCount++;
147 }
148 }
149
150 return TRUE;
151 }// -- AhciAllocateResourceForAdapter();
152
153 /**
154 * @name AhciHwInitialize
155 * @implemented
156 *
157 * initializes the HBA and finds all devices that are of interest to the miniport driver.
158 *
159 * @param adapterExtension
160 *
161 * @return
162 * return TRUE if intialization was successful
163 */
164 BOOLEAN AhciHwInitialize(
165 __in PVOID AdapterExtension
166 )
167 {
168 PAHCI_ADAPTER_EXTENSION adapterExtension;
169
170 StorPortDebugPrint(0, "AhciHwInitialize()\n");
171
172 adapterExtension = (PAHCI_ADAPTER_EXTENSION)AdapterExtension;
173
174 return TRUE;
175 }// -- AhciHwInitialize();
176
177 /**
178 * @name AhciHwInterrupt
179 * @implemented
180 *
181 * The Storport driver calls the HwStorInterrupt routine after the HBA generates an interrupt request.
182 *
183 * @param adapterExtension
184 *
185 * @return
186 * return TRUE Indicates that an interrupt was pending on adapter.
187 * return FALSE Indicates the interrupt was not ours.
188 */
189 BOOLEAN AhciHwInterrupt(
190 __in PVOID AdapterExtension
191 )
192 {
193 PAHCI_ADAPTER_EXTENSION adapterExtension;
194
195 StorPortDebugPrint(0, "AhciHwInterrupt()\n");
196
197 adapterExtension = (PAHCI_ADAPTER_EXTENSION)AdapterExtension;
198
199 return TRUE;
200 }// -- AhciHwInterrupt();
201
202 /**
203 * @name AhciHwStartIo
204 * @implemented
205 *
206 * The Storport driver calls the HwStorStartIo routine one time for each incoming I/O request.
207 *
208 * @param adapterExtension
209 * @param Srb
210 *
211 * @return
212 * return TRUE if the request was accepted
213 * return FALSE if the request must be submitted later
214 */
215 BOOLEAN AhciHwStartIo(
216 __in PVOID AdapterExtension,
217 __in PSCSI_REQUEST_BLOCK Srb
218
219 )
220 {
221 UCHAR function;
222 PAHCI_ADAPTER_EXTENSION adapterExtension;
223
224 StorPortDebugPrint(0, "AhciHwStartIo()\n");
225
226 adapterExtension = (PAHCI_ADAPTER_EXTENSION)AdapterExtension;
227 function = Srb->Function;
228
229 return TRUE;
230 }// -- AhciHwStartIo();
231
232 /**
233 * @name AhciHwResetBus
234 * @implemented
235 *
236 * The HwStorResetBus routine is called by the port driver to clear error conditions.
237 *
238 * @param adapterExtension
239 * @param PathId
240 *
241 * @return
242 * return TRUE if bus was successfully reset
243 */
244 BOOLEAN AhciHwResetBus(
245 __in PVOID AdapterExtension,
246 __in ULONG PathId
247
248 )
249 {
250 PAHCI_ADAPTER_EXTENSION adapterExtension;
251
252 StorPortDebugPrint(0, "AhciHwResetBus()\n");
253
254 adapterExtension = (PAHCI_ADAPTER_EXTENSION)AdapterExtension;
255
256 return TRUE;
257 }// -- AhciHwResetBus();
258
259 /**
260 * @name AhciHwFindAdapter
261 * @implemented
262 *
263 * The HwStorFindAdapter routine uses the supplied configuration to determine whether a specific
264 * HBA is supported and, if it is, to return configuration information about that adapter.
265 *
266 * 10.1 Platform Communication
267 * http://www.intel.in/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_2.pdf
268
269 * @param DeviceExtension
270 * @param HwContext
271 * @param BusInformation
272 * @param ArgumentString
273 * @param ConfigInfo
274 * @param Reserved3
275 *
276 * @return
277 * SP_RETURN_FOUND
278 * 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.
279 *
280 * SP_RETURN_ERROR
281 * 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.
282 *
283 * SP_RETURN_BAD_CONFIG
284 * Indicates that the supplied configuration information was invalid for the adapter.
285 *
286 * SP_RETURN_NOT_FOUND
287 * Indicates that no supported HBA was found for the supplied configuration information.
288 *
289 * @remarks Called by Storport.
290 */
291 ULONG AhciHwFindAdapter(
292 __in PVOID AdapterExtension,
293 __in PVOID HwContext,
294 __in PVOID BusInformation,
295 __in PVOID ArgumentString,
296 __inout PPORT_CONFIGURATION_INFORMATION ConfigInfo,
297 __in PBOOLEAN Reserved3
298 )
299 {
300 ULONG ghc;
301 ULONG index;
302 ULONG portCount, portImplemented;
303 ULONG pci_cfg_len;
304 UCHAR pci_cfg_buf[0x30];
305
306 PAHCI_MEMORY_REGISTERS abar;
307 PPCI_COMMON_CONFIG pciConfigData;
308 PAHCI_ADAPTER_EXTENSION adapterExtension;
309
310 StorPortDebugPrint(0, "AhciHwFindAdapter()\n");
311
312 adapterExtension = (PAHCI_ADAPTER_EXTENSION)AdapterExtension;
313 adapterExtension->SlotNumber = ConfigInfo->SlotNumber;
314 adapterExtension->SystemIoBusNumber = ConfigInfo->SystemIoBusNumber;
315
316 // get PCI configuration header
317 pci_cfg_len = StorPortGetBusData(
318 adapterExtension,
319 PCIConfiguration,
320 adapterExtension->SystemIoBusNumber,
321 adapterExtension->SlotNumber,
322 (PVOID)pci_cfg_buf,
323 (ULONG)0x30);
324
325 if (pci_cfg_len != 0x30){
326 StorPortDebugPrint(0, "\tpci_cfg_len != 0x30 :: %d", pci_cfg_len);
327 return SP_RETURN_ERROR;//Not a valid device at the given bus number
328 }
329
330 pciConfigData = (PPCI_COMMON_CONFIG)pci_cfg_buf;
331 adapterExtension->VendorID = pciConfigData->VendorID;
332 adapterExtension->DeviceID = pciConfigData->DeviceID;
333 adapterExtension->RevisionID = pciConfigData->RevisionID;
334 // 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).
335 adapterExtension->AhciBaseAddress = pciConfigData->u.type0.BaseAddresses[5] & (0xFFFFFFF0);
336
337 StorPortDebugPrint(0, "\tVendorID:%d DeviceID:%d RevisionID:%d\n", adapterExtension->VendorID, adapterExtension->DeviceID, adapterExtension->RevisionID);
338
339 // 2.1.11
340 abar = NULL;
341 if (ConfigInfo->NumberOfAccessRanges > 0)
342 {
343 for (index = 0; index < ConfigInfo->NumberOfAccessRanges; index++)
344 {
345 if ((*(ConfigInfo->AccessRanges))[index].RangeStart.QuadPart == adapterExtension->AhciBaseAddress)
346 {
347 abar = (PAHCI_MEMORY_REGISTERS)StorPortGetDeviceBase(
348 adapterExtension,
349 ConfigInfo->AdapterInterfaceType,
350 ConfigInfo->SystemIoBusNumber,
351 (*(ConfigInfo->AccessRanges))[index].RangeStart,
352 (*(ConfigInfo->AccessRanges))[index].RangeLength,
353 (BOOLEAN)!(*(ConfigInfo->AccessRanges))[index].RangeInMemory);
354 break;
355 }
356 }
357 }
358
359 if (abar == NULL){
360 StorPortDebugPrint(0, "\tabar == NULL\n");
361 return SP_RETURN_ERROR; // corrupted information supplied
362 }
363
364 adapterExtension->ABAR_Address = abar;
365 adapterExtension->CAP = StorPortReadRegisterUlong(adapterExtension, &abar->CAP);
366 adapterExtension->CAP2 = StorPortReadRegisterUlong(adapterExtension, &abar->CAP2);
367 adapterExtension->Version = StorPortReadRegisterUlong(adapterExtension, &abar->VS);
368
369 // 10.1.2
370 // 1. Indicate that system software is AHCI aware by setting GHC.AE to ‘1’.
371 // 3.1.2 -- AE bit is read-write only if CAP.SAM is '0'
372 ghc = StorPortReadRegisterUlong(adapterExtension, &abar->GHC);
373 // AE := Highest Significant bit of GHC
374 if ((ghc & (0x1<<31)) == 1)//Hmm, controller was already in power state
375 {
376 // reset controller to have it in know state
377 StorPortDebugPrint(0, "\tAE Already set, Reset()\n");
378 if (!AhciAdapterReset(adapterExtension)){
379 StorPortDebugPrint(0, "\tReset Failed!\n");
380 return SP_RETURN_ERROR;// reset failed
381 }
382 }
383
384 ghc = 0x1<<31;// only AE=1
385 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc);
386
387 adapterExtension->IS = abar->IS;
388 adapterExtension->PortImplemented = StorPortReadRegisterUlong(adapterExtension, &abar->PI);
389
390 if (adapterExtension->PortImplemented == 0){
391 StorPortDebugPrint(0, "\tadapterExtension->PortImplemented == 0\n");
392 return SP_RETURN_ERROR;
393 }
394
395 ConfigInfo->MaximumTransferLength = 128 * 1024;//128 KB
396 ConfigInfo->NumberOfPhysicalBreaks = 0x21;
397 ConfigInfo->MaximumNumberOfTargets = 1;
398 ConfigInfo->MaximumNumberOfLogicalUnits = 1;
399 ConfigInfo->ResetTargetSupported = TRUE;
400 ConfigInfo->NumberOfBuses = 32;
401 ConfigInfo->SynchronizationModel = StorSynchronizeFullDuplex;
402 ConfigInfo->ScatterGather = TRUE;
403
404 // allocate necessary resource for each port
405 if (!AhciAllocateResourceForAdapter(adapterExtension, ConfigInfo)){
406 StorPortDebugPrint(0, "\tAhciAllocateResourceForAdapter() == FALSE\n");
407 return SP_RETURN_ERROR;
408 }
409
410 for (index = 0; index < 32; index++)
411 {
412 if ((adapterExtension->PortImplemented & (1<<index)) != 0)
413 AhciPortInitialize(adapterExtension->PortExtension[index]);
414 }
415
416 // Turn IE -- Interrupt Enabled
417 ghc |= 0x2;
418 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc);
419
420 return SP_RETURN_FOUND;
421 }// -- AhciHwFindAdapter();
422
423 /**
424 * @name DriverEntry
425 * @implemented
426 *
427 * Initial Entrypoint for storahci miniport driver
428 *
429 * @param DriverObject
430 * @param RegistryPath
431 *
432 * @return
433 * NT_STATUS in case of driver loaded successfully.
434 */
435 ULONG DriverEntry(
436 __in PVOID DriverObject,
437 __in PVOID RegistryPath
438 )
439 {
440 HW_INITIALIZATION_DATA hwInitializationData;
441 ULONG i, status;
442
443 StorPortDebugPrint(0, "Storahci Loaded 10023\n");
444
445 // initialize the hardware data structure
446 AhciZeroMemory((PCHAR)&hwInitializationData, sizeof(HW_INITIALIZATION_DATA));
447
448 // set size of hardware initialization structure
449 hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
450
451 // identity required miniport entry point routines
452 hwInitializationData.HwStartIo = AhciHwStartIo;
453 hwInitializationData.HwResetBus = AhciHwResetBus;
454 hwInitializationData.HwInterrupt = AhciHwInterrupt;
455 hwInitializationData.HwInitialize = AhciHwInitialize;
456 hwInitializationData.HwFindAdapter = AhciHwFindAdapter;
457
458 // adapter specific information
459 hwInitializationData.NeedPhysicalAddresses = TRUE;
460 hwInitializationData.TaggedQueuing = TRUE;
461 hwInitializationData.AutoRequestSense = TRUE;
462 hwInitializationData.MultipleRequestPerLu = TRUE;
463
464 hwInitializationData.NumberOfAccessRanges = 6;
465 hwInitializationData.AdapterInterfaceType = PCIBus;
466 hwInitializationData.MapBuffers = STOR_MAP_NON_READ_WRITE_BUFFERS;
467
468 // set required extension sizes
469 hwInitializationData.SrbExtensionSize = sizeof(AHCI_SRB_EXTENSION);
470 hwInitializationData.DeviceExtensionSize = sizeof(AHCI_ADAPTER_EXTENSION);
471
472 // register our hw init data
473 status = StorPortInitialize(
474 DriverObject,
475 RegistryPath,
476 &hwInitializationData,
477 NULL);
478 StorPortDebugPrint(0, "\tstatus:%d\n", status);
479 return status;
480 }// -- DriverEntry();
481
482 /**
483 * @name AhciAdapterReset
484 * @implemented
485 *
486 * 10.4.3 HBA Reset
487 * If the HBA becomes unusable for multiple ports, and a software reset or port reset does not correct the
488 * problem, software may reset the entire HBA by setting GHC.HR to ‘1’. When software sets the GHC.HR
489 * bit to ‘1’, the HBA shall perform an internal reset action. The bit shall be cleared to ‘0’ by the HBA when
490 * the reset is complete. A software write of ‘0’ to GHC.HR shall have no effect. To perform the HBA reset,
491 * software sets GHC.HR to ‘1’ and may poll until this bit is read to be ‘0’, at which point software knows that
492 * the HBA reset has completed.
493 * If the HBA has not cleared GHC.HR to ‘0’ within 1 second of software setting GHC.HR to ‘1’, the HBA is in
494 * a hung or locked state.
495 *
496 * @param adapterExtension
497 *
498 * @return
499 * TRUE in case AHCI Controller RESTARTED successfully. i.e GHC.HR == 0
500 */
501 BOOLEAN AhciAdapterReset(
502 __in PAHCI_ADAPTER_EXTENSION adapterExtension
503 )
504 {
505 ULONG ghc, ticks;
506 PAHCI_MEMORY_REGISTERS abar = NULL;
507
508 StorPortDebugPrint(0, "AhciAdapterReset()\n");
509
510 abar = adapterExtension->ABAR_Address;
511 if (abar == NULL) // basic sanity
512 return FALSE;
513
514 // HR -- Very first bit (lowest significant)
515 ghc = 1;
516 StorPortWriteRegisterUlong(adapterExtension, &abar->GHC, ghc);
517
518 for (ticks = 0; (ticks < 50) &&
519 (StorPortReadRegisterUlong(adapterExtension, &abar->GHC) == 1);
520 StorPortStallExecution(20000), ticks++);
521
522 if (ticks == 50) { //1 second
523 StorPortDebugPrint(0, "\tDevice Timeout\n");
524 return FALSE;
525 }
526
527 return TRUE;
528 }// -- AhciAdapterReset();
529
530 /**
531 * @name AhciZeroMemory
532 * @implemented
533 *
534 * Clear buffer by filling zeros
535 *
536 * @param buffer
537 */
538 VOID AhciZeroMemory(
539 __in PCHAR buffer,
540 __in ULONG bufferSize
541 )
542 {
543 ULONG i;
544 for (i = 0; i < bufferSize; i++)
545 buffer[i] = 0;
546 }// -- AhciZeroMemory();