]> git.proxmox.com Git - mirror_edk2.git/blame - EmbeddedPkg/Drivers/SataSiI3132Dxe/SiI3132AtaPassThru.c
EmbeddedPkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / EmbeddedPkg / Drivers / SataSiI3132Dxe / SiI3132AtaPassThru.c
CommitLineData
f6342447
OM
1/** @file\r
2*\r
3* Copyright (c) 2011-2015, ARM Limited. All rights reserved.\r
4*\r
878b807a 5* SPDX-License-Identifier: BSD-2-Clause-Patent\r
f6342447
OM
6*\r
7**/\r
8\r
9#include "SataSiI3132.h"\r
10\r
11#include <IndustryStandard/Atapi.h>\r
12#include <Library/DevicePathLib.h>\r
13\r
14SATA_SI3132_DEVICE*\r
15GetSataDevice (\r
16 IN SATA_SI3132_INSTANCE* SataInstance,\r
17 IN UINT16 Port,\r
18 IN UINT16 PortMultiplierPort\r
19) {\r
20 LIST_ENTRY *List;\r
21 SATA_SI3132_PORT *SataPort;\r
22 SATA_SI3132_DEVICE *SataDevice;\r
23\r
24 if (Port >= SATA_SII3132_MAXPORT) {\r
25 return NULL;\r
26 }\r
27\r
28 SataPort = &(SataInstance->Ports[Port]);\r
29 List = SataPort->Devices.ForwardLink;\r
30\r
31 while (List != &SataPort->Devices) {\r
32 SataDevice = (SATA_SI3132_DEVICE*)List;\r
33 if (SataDevice->Index == PortMultiplierPort) {\r
34 return SataDevice;\r
35 }\r
36 List = List->ForwardLink;\r
37 }\r
38 return NULL;\r
39}\r
40\r
41EFI_STATUS\r
42SiI3132AtaPassThruCommand (\r
43 IN SATA_SI3132_INSTANCE *SataSiI3132Instance,\r
44 IN SATA_SI3132_PORT *SataPort,\r
45 IN UINT16 PortMultiplierPort,\r
46 IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet,\r
47 IN EFI_EVENT Event OPTIONAL\r
48 )\r
49{\r
50 SATA_SI3132_DEVICE *SataDevice;\r
51 EFI_PHYSICAL_ADDRESS PhysInDataBuffer;\r
52 UINTN InDataBufferLength = 0;\r
53 EFI_PHYSICAL_ADDRESS PhysOutDataBuffer;\r
54 UINTN OutDataBufferLength;\r
55 CONST UINTN EmptySlot = 0;\r
56 UINTN Control = PRB_CTRL_ATA;\r
57 UINTN Protocol = 0;\r
58 UINT32 Value32, Error, Timeout = 0;\r
59 CONST UINT32 IrqMask = (SII3132_PORT_INT_CMDCOMPL | SII3132_PORT_INT_CMDERR) << 16;\r
60 EFI_STATUS Status;\r
61 VOID* PciAllocMapping = NULL;\r
62 EFI_PCI_IO_PROTOCOL *PciIo;\r
63\r
64 PciIo = SataSiI3132Instance->PciIo;\r
65 ZeroMem (SataPort->HostPRB, sizeof (SATA_SI3132_PRB));\r
66\r
67 // Construct Si3132 PRB\r
68 switch (Packet->Protocol) {\r
69 case EFI_ATA_PASS_THRU_PROTOCOL_ATA_HARDWARE_RESET:\r
70 ASSERT (0); //TODO: Implement me!\r
71 break;\r
72 case EFI_ATA_PASS_THRU_PROTOCOL_ATA_SOFTWARE_RESET:\r
73 SATA_TRACE ("SiI3132AtaPassThru() EFI_ATA_PASS_THRU_PROTOCOL_ATA_SOFTWARE_RESET");\r
74 Control = PRB_CTRL_SRST;\r
75\r
76 if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {\r
77 SataPort->HostPRB->Fis.Control = 0x0F;\r
78 }\r
79 break;\r
80 case EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA:\r
81 ASSERT (0); //TODO: Implement me!\r
82 break;\r
83\r
84 // There is no difference for SiI3132 between PIO and DMA invokation\r
85 case EFI_ATA_PASS_THRU_PROTOCOL_UDMA_DATA_IN:\r
86 case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN:\r
87 // Fixup the size for block transfer. Following UEFI Specification, 'InTransferLength' should\r
88 // be in number of bytes. But for most data transfer commands, the value is in number of blocks\r
89 if (Packet->Acb->AtaCommand == ATA_CMD_IDENTIFY_DRIVE) {\r
90 InDataBufferLength = Packet->InTransferLength;\r
91 } else {\r
92 SataDevice = GetSataDevice (SataSiI3132Instance, SataPort->Index, PortMultiplierPort);\r
93 if (!SataDevice || (SataDevice->BlockSize == 0)) {\r
94 return EFI_INVALID_PARAMETER;\r
95 }\r
96\r
97 InDataBufferLength = Packet->InTransferLength * SataDevice->BlockSize;\r
98 }\r
99\r
100 Status = PciIo->Map (\r
050d513b 101 PciIo, EfiPciIoOperationBusMasterWrite,\r
f6342447
OM
102 Packet->InDataBuffer, &InDataBufferLength, &PhysInDataBuffer, &PciAllocMapping\r
103 );\r
104 if (EFI_ERROR (Status)) {\r
105 return Status;\r
106 }\r
107\r
108 // Construct SGEs (32-bit system)\r
109 SataPort->HostPRB->Sge[0].DataAddressLow = (UINT32)PhysInDataBuffer;\r
110 SataPort->HostPRB->Sge[0].DataAddressHigh = (UINT32)(PhysInDataBuffer >> 32);\r
111 SataPort->HostPRB->Sge[0].Attributes = SGE_TRM; // Only one SGE\r
112 SataPort->HostPRB->Sge[0].DataCount = InDataBufferLength;\r
113\r
114 // Copy the Ata Command Block\r
115 CopyMem (&SataPort->HostPRB->Fis, Packet->Acb, sizeof (EFI_ATA_COMMAND_BLOCK));\r
116\r
117 // Fixup the FIS\r
118 SataPort->HostPRB->Fis.FisType = 0x27; // Register - Host to Device FIS\r
119 SataPort->HostPRB->Fis.Control = 1 << 7; // Is a command\r
120 if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {\r
121 SataPort->HostPRB->Fis.Control |= PortMultiplierPort & 0xFF;\r
122 }\r
123 break;\r
124 case EFI_ATA_PASS_THRU_PROTOCOL_UDMA_DATA_OUT:\r
125 case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT:\r
126 SataDevice = GetSataDevice (SataSiI3132Instance, SataPort->Index, PortMultiplierPort);\r
127 if (!SataDevice || (SataDevice->BlockSize == 0)) {\r
128 return EFI_INVALID_PARAMETER;\r
129 }\r
130\r
131 // Fixup the size for block transfer. Following UEFI Specification, 'InTransferLength' should\r
132 // be in number of bytes. But for most data transfer commands, the value is in number of blocks\r
133 OutDataBufferLength = Packet->OutTransferLength * SataDevice->BlockSize;\r
134\r
135 Status = PciIo->Map (\r
050d513b 136 PciIo, EfiPciIoOperationBusMasterRead,\r
f6342447
OM
137 Packet->OutDataBuffer, &OutDataBufferLength, &PhysOutDataBuffer, &PciAllocMapping\r
138 );\r
139 if (EFI_ERROR (Status)) {\r
140 return Status;\r
141 }\r
142\r
143 // Construct SGEs (32-bit system)\r
144 SataPort->HostPRB->Sge[0].DataAddressLow = (UINT32)PhysOutDataBuffer;\r
145 SataPort->HostPRB->Sge[0].DataAddressHigh = (UINT32)(PhysOutDataBuffer >> 32);\r
146 SataPort->HostPRB->Sge[0].Attributes = SGE_TRM; // Only one SGE\r
147 SataPort->HostPRB->Sge[0].DataCount = OutDataBufferLength;\r
148\r
149 // Copy the Ata Command Block\r
150 CopyMem (&SataPort->HostPRB->Fis, Packet->Acb, sizeof (EFI_ATA_COMMAND_BLOCK));\r
151\r
152 // Fixup the FIS\r
153 SataPort->HostPRB->Fis.FisType = 0x27; // Register - Host to Device FIS\r
154 SataPort->HostPRB->Fis.Control = 1 << 7; // Is a command\r
155 if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {\r
156 SataPort->HostPRB->Fis.Control |= PortMultiplierPort & 0xFF;\r
157 }\r
158 break;\r
159 case EFI_ATA_PASS_THRU_PROTOCOL_DMA:\r
160 ASSERT (0); //TODO: Implement me!\r
161 break;\r
162 case EFI_ATA_PASS_THRU_PROTOCOL_DMA_QUEUED:\r
163 ASSERT (0); //TODO: Implement me!\r
164 break;\r
165 case EFI_ATA_PASS_THRU_PROTOCOL_DEVICE_DIAGNOSTIC:\r
166 ASSERT (0); //TODO: Implement me!\r
167 break;\r
168 case EFI_ATA_PASS_THRU_PROTOCOL_DEVICE_RESET:\r
169 ASSERT (0); //TODO: Implement me!\r
170 break;\r
171 case EFI_ATA_PASS_THRU_PROTOCOL_FPDMA:\r
172 ASSERT (0); //TODO: Implement me!\r
173 break;\r
174 case EFI_ATA_PASS_THRU_PROTOCOL_RETURN_RESPONSE:\r
175 ASSERT (0); //TODO: Implement me!\r
176 break;\r
177 default:\r
178 ASSERT (0);\r
179 break;\r
180 }\r
181\r
182 SataPort->HostPRB->Control = Control;\r
183 SataPort->HostPRB->ProtocolOverride = Protocol;\r
184\r
185 // Clear IRQ\r
186 SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, IrqMask);\r
187\r
188 if (!FeaturePcdGet (PcdSataSiI3132FeatureDirectCommandIssuing)) {\r
189 // Indirect Command Issuance\r
190\r
191 //TODO: Find which slot is free (maybe use the Cmd FIFO)\r
192 //SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_CMDEXECFIFO_REG, &EmptySlot);\r
193\r
194 SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_CMDACTIV_REG + (EmptySlot * 8),\r
195 (UINT32)(SataPort->PhysAddrHostPRB & 0xFFFFFFFF));\r
196 SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_CMDACTIV_REG + (EmptySlot * 8) + 4,\r
197 (UINT32)((SataPort->PhysAddrHostPRB >> 32) & 0xFFFFFFFF));\r
198 } else {\r
199 // Direct Command Issuance\r
200 Status = PciIo->Mem.Write (PciIo, EfiPciIoWidthUint32, 1, // Bar 1\r
201 SataPort->RegBase + (EmptySlot * 0x80),\r
202 sizeof (SATA_SI3132_PRB) / 4,\r
203 SataPort->HostPRB);\r
204 ASSERT_EFI_ERROR (Status);\r
205\r
206 SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_CMDEXECFIFO_REG, EmptySlot);\r
207 }\r
208\r
209#if 0\r
210 // Could need to be implemented if we run multiple command in parallel to know which slot has been completed\r
211 SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_SLOTSTATUS_REG, &Value32);\r
212 Timeout = Packet->Timeout;\r
213 while (!Timeout && !Value32) {\r
214 gBS->Stall (1);\r
215 SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_SLOTSTATUS_REG, &Value32);\r
216 Timeout--;\r
217 }\r
218#else\r
219 SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, &Value32);\r
220 if (!Packet->Timeout) {\r
221 while (!(Value32 & IrqMask)) {\r
222 gBS->Stall (1);\r
223 SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, &Value32);\r
224 }\r
225 } else {\r
226 Timeout = Packet->Timeout;\r
227 while (Timeout && !(Value32 & IrqMask)) {\r
228 gBS->Stall (1);\r
229 SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, &Value32);\r
230 Timeout--;\r
231 }\r
232 }\r
233#endif\r
234 // Fill Packet Ata Status Block\r
235 Status = PciIo->Mem.Read (PciIo, EfiPciIoWidthUint32, 1, // Bar 1\r
236 SataPort->RegBase + 0x08,\r
237 sizeof (EFI_ATA_STATUS_BLOCK) / 4,\r
238 Packet->Asb);\r
239 ASSERT_EFI_ERROR (Status);\r
240\r
241\r
242 if ((Packet->Timeout != 0) && (Timeout == 0)) {\r
243 DEBUG ((EFI_D_ERROR, "SiI3132AtaPassThru() Err:Timeout\n"));\r
244 //ASSERT (0);\r
245 return EFI_TIMEOUT;\r
246 } else if (Value32 & (SII3132_PORT_INT_CMDERR << 16)) {\r
247 SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_CMDERROR_REG, &Error);\r
248 DEBUG ((EFI_D_ERROR, "SiI3132AtaPassThru() CmdErr:0x%X (SiI3132 Err:0x%X)\n", Value32, Error));\r
249 ASSERT (0);\r
250 return EFI_DEVICE_ERROR;\r
251 } else if (Value32 & (SII3132_PORT_INT_CMDCOMPL << 16)) {\r
252 // Clear Command Complete\r
253 SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, SII3132_PORT_INT_CMDCOMPL << 16);\r
254\r
255 if (PciAllocMapping) {\r
256 Status = PciIo->Unmap (PciIo, PciAllocMapping);\r
257 ASSERT (!EFI_ERROR (Status));\r
258 }\r
259\r
260 // If the command was ATA_CMD_IDENTIFY_DRIVE then we need to update the BlockSize\r
261 if (Packet->Acb->AtaCommand == ATA_CMD_IDENTIFY_DRIVE) {\r
262 ATA_IDENTIFY_DATA *IdentifyData = (ATA_IDENTIFY_DATA*)Packet->InDataBuffer;\r
263\r
264 // Get the corresponding Block Device\r
265 SataDevice = GetSataDevice (SataSiI3132Instance, SataPort->Index, PortMultiplierPort);\r
266\r
267 // Check logical block size\r
268 if ((IdentifyData->phy_logic_sector_support & BIT12) != 0) {\r
269 ASSERT (SataDevice != NULL);\r
270 SataDevice->BlockSize = (UINT32) (((IdentifyData->logic_sector_size_hi << 16) |\r
271 IdentifyData->logic_sector_size_lo) * sizeof (UINT16));\r
272 } else {\r
273 SataDevice->BlockSize = 0x200;\r
274 }\r
275 }\r
276 return EFI_SUCCESS;\r
277 } else {\r
278 ASSERT (0);\r
279 return EFI_DEVICE_ERROR;\r
280 }\r
281}\r
282\r
283/**\r
284 Sends an ATA command to an ATA device that is attached to the ATA controller. This function\r
285 supports both blocking I/O and non-blocking I/O. The blocking I/O functionality is required,\r
286 and the non-blocking I/O functionality is optional.\r
287\r
288 @param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.\r
289 @param[in] Port The port number of the ATA device to send the command.\r
290 @param[in] PortMultiplierPort The port multiplier port number of the ATA device to send the command.\r
291 If there is no port multiplier, then specify 0.\r
292 @param[in,out] Packet A pointer to the ATA command to send to the ATA device specified by Port\r
293 and PortMultiplierPort.\r
294 @param[in] Event If non-blocking I/O is not supported then Event is ignored, and blocking\r
295 I/O is performed. If Event is NULL, then blocking I/O is performed. If\r
296 Event is not NULL and non blocking I/O is supported, then non-blocking\r
297 I/O is performed, and Event will be signaled when the ATA command completes.\r
298\r
299 @retval EFI_SUCCESS The ATA command was sent by the host. For bi-directional commands,\r
300 InTransferLength bytes were transferred from InDataBuffer. For write and\r
301 bi-directional commands, OutTransferLength bytes were transferred by OutDataBuffer.\r
302 @retval EFI_BAD_BUFFER_SIZE The ATA command was not executed. The number of bytes that could be transferred\r
303 is returned in InTransferLength. For write and bi-directional commands,\r
304 OutTransferLength bytes were transferred by OutDataBuffer.\r
305 @retval EFI_NOT_READY The ATA command could not be sent because there are too many ATA commands\r
306 already queued. The caller may retry again later.\r
307 @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the ATA command.\r
308 @retval EFI_INVALID_PARAMETER Port, PortMultiplierPort, or the contents of Acb are invalid. The ATA\r
309 command was not sent, so no additional status information is available.\r
310\r
311**/\r
312EFI_STATUS\r
313SiI3132AtaPassThru (\r
314 IN EFI_ATA_PASS_THRU_PROTOCOL *This,\r
315 IN UINT16 Port,\r
316 IN UINT16 PortMultiplierPort,\r
317 IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet,\r
318 IN EFI_EVENT Event OPTIONAL\r
319 )\r
320{\r
321 SATA_SI3132_INSTANCE *SataSiI3132Instance;\r
322 SATA_SI3132_DEVICE *SataDevice;\r
323 SATA_SI3132_PORT *SataPort;\r
324\r
325 SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);\r
326 if (!SataSiI3132Instance) {\r
327 return EFI_INVALID_PARAMETER;\r
328 }\r
329\r
330 SataDevice = GetSataDevice (SataSiI3132Instance, Port, PortMultiplierPort);\r
331 if (!SataDevice) {\r
332 return EFI_INVALID_PARAMETER;\r
333 }\r
334 SataPort = SataDevice->Port;\r
335\r
336 DEBUG ((EFI_D_INFO, "SiI3132AtaPassThru(%d,%d) : AtaCmd:0x%X Prot:%d\n", Port, PortMultiplierPort,\r
337 Packet->Acb->AtaCommand, Packet->Protocol));\r
338\r
339 return SiI3132AtaPassThruCommand (SataSiI3132Instance, SataPort, PortMultiplierPort, Packet, Event);\r
340}\r
341\r
342/**\r
343 Used to retrieve the list of legal port numbers for ATA devices on an ATA controller.\r
344 These can either be the list of ports where ATA devices are actually present or the\r
345 list of legal port numbers for the ATA controller. Regardless, the caller of this\r
346 function must probe the port number returned to see if an ATA device is actually\r
347 present at that location on the ATA controller.\r
348\r
349 The GetNextPort() function retrieves the port number on an ATA controller. If on input\r
350 Port is 0xFFFF, then the port number of the first port on the ATA controller is returned\r
351 in Port and EFI_SUCCESS is returned.\r
352\r
353 If Port is a port number that was returned on a previous call to GetNextPort(), then the\r
354 port number of the next port on the ATA controller is returned in Port, and EFI_SUCCESS\r
355 is returned. If Port is not 0xFFFF and Port was not returned on a previous call to\r
356 GetNextPort(), then EFI_INVALID_PARAMETER is returned.\r
357\r
358 If Port is the port number of the last port on the ATA controller, then EFI_NOT_FOUND is\r
359 returned.\r
360\r
361 @param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.\r
362 @param[in,out] Port On input, a pointer to the port number on the ATA controller.\r
363 On output, a pointer to the next port number on the ATA\r
364 controller. An input value of 0xFFFF retrieves the first port\r
365 number on the ATA controller.\r
366\r
367 @retval EFI_SUCCESS The next port number on the ATA controller was returned in Port.\r
368 @retval EFI_NOT_FOUND There are no more ports on this ATA controller.\r
369 @retval EFI_INVALID_PARAMETER Port is not 0xFFFF and Port was not returned on a previous call\r
370 to GetNextPort().\r
371\r
372**/\r
373EFI_STATUS\r
374SiI3132GetNextPort (\r
375 IN EFI_ATA_PASS_THRU_PROTOCOL *This,\r
376 IN OUT UINT16 *Port\r
377 )\r
378{\r
379 SATA_SI3132_INSTANCE *SataSiI3132Instance;\r
380 UINTN PrevPort;\r
381 EFI_STATUS Status = EFI_SUCCESS;\r
382\r
383 SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);\r
384 if (!SataSiI3132Instance) {\r
385 return EFI_INVALID_PARAMETER;\r
386 }\r
387\r
388 PrevPort = *Port;\r
389\r
390 if (PrevPort == 0xFFFF) {\r
391 *Port = 0;\r
392 } else {\r
393 if (PrevPort < SATA_SII3132_MAXPORT) {\r
394 *Port = PrevPort + 1;\r
395 } else {\r
396 Status = EFI_NOT_FOUND;\r
397 }\r
398 }\r
399 return Status;\r
400}\r
401\r
402/**\r
403 Used to retrieve the list of legal port multiplier port numbers for ATA devices on a port of an ATA\r
404 controller. These can either be the list of port multiplier ports where ATA devices are actually\r
405 present on port or the list of legal port multiplier ports on that port. Regardless, the caller of this\r
406 function must probe the port number and port multiplier port number returned to see if an ATA\r
407 device is actually present.\r
408\r
409 The GetNextDevice() function retrieves the port multiplier port number of an ATA device\r
410 present on a port of an ATA controller.\r
411\r
412 If PortMultiplierPort points to a port multiplier port number value that was returned on a\r
413 previous call to GetNextDevice(), then the port multiplier port number of the next ATA device\r
414 on the port of the ATA controller is returned in PortMultiplierPort, and EFI_SUCCESS is\r
415 returned.\r
416\r
417 If PortMultiplierPort points to 0xFFFF, then the port multiplier port number of the first\r
418 ATA device on port of the ATA controller is returned in PortMultiplierPort and\r
419 EFI_SUCCESS is returned.\r
420\r
421 If PortMultiplierPort is not 0xFFFF and the value pointed to by PortMultiplierPort\r
422 was not returned on a previous call to GetNextDevice(), then EFI_INVALID_PARAMETER\r
423 is returned.\r
424\r
425 If PortMultiplierPort is the port multiplier port number of the last ATA device on the port of\r
426 the ATA controller, then EFI_NOT_FOUND is returned.\r
427\r
428 @param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.\r
429 @param[in] Port The port number present on the ATA controller.\r
430 @param[in,out] PortMultiplierPort On input, a pointer to the port multiplier port number of an\r
431 ATA device present on the ATA controller.\r
432 If on input a PortMultiplierPort of 0xFFFF is specified,\r
433 then the port multiplier port number of the first ATA device\r
434 is returned. On output, a pointer to the port multiplier port\r
435 number of the next ATA device present on an ATA controller.\r
436\r
437 @retval EFI_SUCCESS The port multiplier port number of the next ATA device on the port\r
438 of the ATA controller was returned in PortMultiplierPort.\r
439 @retval EFI_NOT_FOUND There are no more ATA devices on this port of the ATA controller.\r
440 @retval EFI_INVALID_PARAMETER PortMultiplierPort is not 0xFFFF, and PortMultiplierPort was not\r
441 returned on a previous call to GetNextDevice().\r
442\r
443**/\r
444EFI_STATUS\r
445SiI3132GetNextDevice (\r
446 IN EFI_ATA_PASS_THRU_PROTOCOL *This,\r
447 IN UINT16 Port,\r
448 IN OUT UINT16 *PortMultiplierPort\r
449 )\r
450{\r
451 SATA_SI3132_INSTANCE *SataSiI3132Instance;\r
452 SATA_SI3132_PORT *SataPort;\r
453 SATA_SI3132_DEVICE *SataDevice;\r
454 LIST_ENTRY *List;\r
455 EFI_STATUS Status = EFI_SUCCESS;\r
456\r
457 SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);\r
458 if (!SataSiI3132Instance) {\r
459 return EFI_INVALID_PARAMETER;\r
460 }\r
461\r
462 if (Port >= SATA_SII3132_MAXPORT) {\r
463 return EFI_INVALID_PARAMETER;\r
464 }\r
465\r
466 SataPort = &(SataSiI3132Instance->Ports[Port]);\r
467\r
468 if (*PortMultiplierPort == 0xFFFF) {\r
469 List = SataPort->Devices.ForwardLink;\r
470 if (List != &SataPort->Devices) {\r
471 // The list is not empty, return the first device\r
472 *PortMultiplierPort = ((SATA_SI3132_DEVICE*)List)->Index;\r
473 } else {\r
474 Status = EFI_NOT_FOUND;\r
475 }\r
476 } else {\r
477 SataDevice = GetSataDevice (SataSiI3132Instance, Port, *PortMultiplierPort);\r
478 if (SataDevice != NULL) {\r
479 // We have found the previous port multiplier, return the next one\r
480 List = SataDevice->Link.ForwardLink;\r
481 if (List != &SataPort->Devices) {\r
482 *PortMultiplierPort = ((SATA_SI3132_DEVICE*)List)->Index;\r
483 } else {\r
484 Status = EFI_NOT_FOUND;\r
485 }\r
486 } else {\r
487 Status = EFI_NOT_FOUND;\r
488 }\r
489 }\r
490 return Status;\r
491}\r
492\r
493/**\r
494 Used to allocate and build a device path node for an ATA device on an ATA controller.\r
495\r
496 The BuildDevicePath() function allocates and builds a single device node for the ATA\r
497 device specified by Port and PortMultiplierPort. If the ATA device specified by Port and\r
498 PortMultiplierPort is not present on the ATA controller, then EFI_NOT_FOUND is returned.\r
499 If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned. If there are not enough\r
500 resources to allocate the device path node, then EFI_OUT_OF_RESOURCES is returned.\r
501\r
502 Otherwise, DevicePath is allocated with the boot service AllocatePool(), the contents of\r
503 DevicePath are initialized to describe the ATA device specified by Port and PortMultiplierPort,\r
504 and EFI_SUCCESS is returned.\r
505\r
506 @param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.\r
507 @param[in] Port Port specifies the port number of the ATA device for which a\r
508 device path node is to be allocated and built.\r
509 @param[in] PortMultiplierPort The port multiplier port number of the ATA device for which a\r
510 device path node is to be allocated and built. If there is no\r
511 port multiplier, then specify 0.\r
512 @param[in,out] DevicePath A pointer to a single device path node that describes the ATA\r
513 device specified by Port and PortMultiplierPort. This function\r
514 is responsible for allocating the buffer DevicePath with the\r
515 boot service AllocatePool(). It is the caller's responsibility\r
516 to free DevicePath when the caller is finished with DevicePath.\r
517 @retval EFI_SUCCESS The device path node that describes the ATA device specified by\r
518 Port and PortMultiplierPort was allocated and returned in DevicePath.\r
519 @retval EFI_NOT_FOUND The ATA device specified by Port and PortMultiplierPort does not\r
520 exist on the ATA controller.\r
521 @retval EFI_INVALID_PARAMETER DevicePath is NULL.\r
522 @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate DevicePath.\r
523\r
524**/\r
525EFI_STATUS\r
526SiI3132BuildDevicePath (\r
527 IN EFI_ATA_PASS_THRU_PROTOCOL *This,\r
528 IN UINT16 Port,\r
529 IN UINT16 PortMultiplierPort,\r
530 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath\r
531 )\r
532{\r
533 SATA_SI3132_INSTANCE *SataSiI3132Instance;\r
534 SATA_SI3132_DEVICE *SataDevice;\r
535 EFI_DEVICE_PATH_PROTOCOL *SiI3132DevicePath;\r
536\r
537 SATA_TRACE ("SiI3132BuildDevicePath()");\r
538\r
539 SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);\r
540 if (!SataSiI3132Instance) {\r
541 return EFI_INVALID_PARAMETER;\r
542 }\r
543\r
544 SataDevice = GetSataDevice (SataSiI3132Instance, Port, PortMultiplierPort);\r
545 if (SataDevice == NULL) {\r
546 return EFI_NOT_FOUND;\r
547 }\r
548\r
549 SiI3132DevicePath = CreateDeviceNode (MESSAGING_DEVICE_PATH, MSG_SATA_DP, sizeof (SATA_DEVICE_PATH));\r
550 if (SiI3132DevicePath == NULL) {\r
551 return EFI_OUT_OF_RESOURCES;\r
552 }\r
553\r
554 ((SATA_DEVICE_PATH*)SiI3132DevicePath)->HBAPortNumber = Port;\r
555 if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {\r
556 ((SATA_DEVICE_PATH*)SiI3132DevicePath)->PortMultiplierPortNumber = PortMultiplierPort;\r
557 } else {\r
558 //Temp:((SATA_DEVICE_PATH*)SiI3132DevicePath)->PortMultiplierPortNumber = SATA_HBA_DIRECT_CONNECT_FLAG;\r
559 ((SATA_DEVICE_PATH*)SiI3132DevicePath)->PortMultiplierPortNumber = 0;\r
560 }\r
561 ((SATA_DEVICE_PATH*)SiI3132DevicePath)->Lun = Port; //TODO: Search information how to define properly LUN (Logical Unit Number)\r
562\r
563 *DevicePath = SiI3132DevicePath;\r
564 return EFI_SUCCESS;\r
565}\r
566\r
567/**\r
568 Used to translate a device path node to a port number and port multiplier port number.\r
569\r
570 The GetDevice() function determines the port and port multiplier port number associated with\r
571 the ATA device described by DevicePath. If DevicePath is a device path node type that the\r
572 ATA Pass Thru driver supports, then the ATA Pass Thru driver will attempt to translate the contents\r
573 DevicePath into a port number and port multiplier port number.\r
574\r
575 If this translation is successful, then that port number and port multiplier port number are returned\r
576 in Port and PortMultiplierPort, and EFI_SUCCESS is returned.\r
577\r
578 If DevicePath, Port, or PortMultiplierPort are NULL, then EFI_INVALID_PARAMETER is returned.\r
579\r
580 If DevicePath is not a device path node type that the ATA Pass Thru driver supports, then\r
581 EFI_UNSUPPORTED is returned.\r
582\r
583 If DevicePath is a device path node type that the ATA Pass Thru driver supports, but there is not\r
584 a valid translation from DevicePath to a port number and port multiplier port number, then\r
585 EFI_NOT_FOUND is returned.\r
586\r
587 @param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.\r
588 @param[in] DevicePath A pointer to the device path node that describes an ATA device on the\r
589 ATA controller.\r
590 @param[out] Port On return, points to the port number of an ATA device on the ATA controller.\r
591 @param[out] PortMultiplierPort On return, points to the port multiplier port number of an ATA device\r
592 on the ATA controller.\r
593\r
594 @retval EFI_SUCCESS DevicePath was successfully translated to a port number and port multiplier\r
595 port number, and they were returned in Port and PortMultiplierPort.\r
596 @retval EFI_INVALID_PARAMETER DevicePath is NULL.\r
597 @retval EFI_INVALID_PARAMETER Port is NULL.\r
598 @retval EFI_INVALID_PARAMETER PortMultiplierPort is NULL.\r
599 @retval EFI_UNSUPPORTED This driver does not support the device path node type in DevicePath.\r
600 @retval EFI_NOT_FOUND A valid translation from DevicePath to a port number and port multiplier\r
601 port number does not exist.\r
602**/\r
603EFI_STATUS\r
604SiI3132GetDevice (\r
605 IN EFI_ATA_PASS_THRU_PROTOCOL *This,\r
606 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
607 OUT UINT16 *Port,\r
608 OUT UINT16 *PortMultiplierPort\r
609 )\r
610{\r
611 SATA_SI3132_INSTANCE *SataSiI3132Instance;\r
612\r
613 SATA_TRACE ("SiI3132GetDevice()");\r
614\r
615 if (!DevicePath || !Port || !PortMultiplierPort) {\r
616 return EFI_INVALID_PARAMETER;\r
617 }\r
618\r
619 if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || (DevicePath->SubType != MSG_SATA_DP)) {\r
620 return EFI_UNSUPPORTED;\r
621 }\r
622\r
623 SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);\r
624 if (!SataSiI3132Instance) {\r
625 return EFI_INVALID_PARAMETER;\r
626 }\r
627\r
628 if (((SATA_DEVICE_PATH*)DevicePath)->Lun >= SATA_SII3132_MAXPORT) {\r
629 return EFI_NOT_FOUND;\r
630 }\r
631\r
632 if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) {\r
633 ASSERT (0); //TODO: Implement me!\r
634 return EFI_UNSUPPORTED;\r
635 } else {\r
636 *Port = ((SATA_DEVICE_PATH*)DevicePath)->Lun;\r
637 // Return the first Sata Sevice as there should be only one directly connected\r
638 *PortMultiplierPort = ((SATA_SI3132_DEVICE*)SataSiI3132Instance->Ports[*Port].Devices.ForwardLink)->Index;\r
639 return EFI_SUCCESS;\r
640 }\r
641}\r
642\r
643EFI_STATUS\r
644SiI3132HwResetPort (\r
645 IN SATA_SI3132_PORT *SataPort\r
646 )\r
647{\r
648 EFI_PCI_IO_PROTOCOL *PciIo;\r
649 UINT32 Value32;\r
650 UINTN Timeout;\r
651\r
652 SATA_TRACE ("SiI3132HwResetPort()");\r
653\r
654 PciIo = SataPort->Instance->PciIo;\r
655\r
656 // Clear Port Reset\r
657 SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_CONTROLCLEAR_REG, SII3132_PORT_CONTROL_RESET);\r
658\r
659 SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_STATUS_REG, &Value32);\r
660 ASSERT (!(Value32 & SII3132_PORT_CONTROL_RESET));\r
661\r
662 // Initialize error counters\r
663 SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_ERRCOUNTDECODE, 0);\r
664 SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_ERRCOUNTCRC, 0);\r
665 SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_ERRCOUNTHANDSHAKE, 0);\r
666\r
667 // Enable interrupts for command completion and command errors\r
668 SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_ENABLEINT_REG, SII3132_PORT_INT_CMDCOMPL | SII3132_PORT_INT_CMDERR);\r
669\r
670 // Clear IRQ\r
671 SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_ENABLEINT_REG, SII3132_PORT_INT_CMDCOMPL | SII3132_PORT_INT_CMDERR | SII3132_PORT_INT_PORTRDY | (1 << 3));\r
672\r
673 // Wait until Port Ready\r
674 SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, &Value32);\r
675 Timeout = 1000;\r
676 while ((Timeout > 0) && ((Value32 & SII3132_PORT_INT_PORTRDY) == 0)) {\r
677 gBS->Stall (1);\r
678 Timeout--;\r
679 SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, &Value32);\r
680 }\r
681 // Clear IRQ\r
682 SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_INTSTATUS_REG, SII3132_PORT_INT_PORTRDY);\r
683\r
684 if (Timeout == 0) {\r
685 SATA_TRACE ("SiI3132HwResetPort(): Timeout");\r
686 return EFI_TIMEOUT;\r
687 } else if ((Value32 & SII3132_PORT_INT_PORTRDY) == 0) {\r
688 SATA_TRACE ("SiI3132HwResetPort(): Port Not Ready");\r
689 return EFI_DEVICE_ERROR;\r
690 } else {\r
691 return EFI_SUCCESS;\r
692 }\r
693}\r
694\r
695/**\r
696 Resets a specific port on the ATA controller. This operation also resets all the ATA devices\r
697 connected to the port.\r
698\r
699 The ResetChannel() function resets an a specific port on an ATA controller. This operation\r
700 resets all the ATA devices connected to that port. If this ATA controller does not support\r
701 a reset port operation, then EFI_UNSUPPORTED is returned.\r
702\r
703 If a device error occurs while executing that port reset operation, then EFI_DEVICE_ERROR is\r
704 returned.\r
705\r
706 If a timeout occurs during the execution of the port reset operation, then EFI_TIMEOUT is returned.\r
707\r
708 If the port reset operation is completed, then EFI_SUCCESS is returned.\r
709\r
710 @param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.\r
711 @param[in] Port The port number on the ATA controller.\r
712\r
713 @retval EFI_SUCCESS The ATA controller port was reset.\r
714 @retval EFI_UNSUPPORTED The ATA controller does not support a port reset operation.\r
715 @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the ATA port.\r
716 @retval EFI_TIMEOUT A timeout occurred while attempting to reset the ATA port.\r
717\r
718**/\r
719EFI_STATUS\r
720SiI3132ResetPort (\r
721 IN EFI_ATA_PASS_THRU_PROTOCOL *This,\r
722 IN UINT16 Port\r
723 )\r
724{\r
725 SATA_SI3132_INSTANCE *SataSiI3132Instance;\r
726 SATA_SI3132_PORT *SataPort;\r
727\r
728 SATA_TRACE ("SiI3132ResetPort()");\r
729\r
730 SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);\r
731 if (!SataSiI3132Instance) {\r
732 return EFI_INVALID_PARAMETER;\r
733 }\r
734\r
735 if (Port >= SATA_SII3132_MAXPORT) {\r
736 return EFI_UNSUPPORTED;\r
737 }\r
738\r
739 SataPort = &(SataSiI3132Instance->Ports[Port]);\r
740 return SiI3132HwResetPort (SataPort);\r
741}\r
742\r
743/**\r
744 Resets an ATA device that is connected to an ATA controller.\r
745\r
746 The ResetDevice() function resets the ATA device specified by Port and PortMultiplierPort.\r
747 If this ATA controller does not support a device reset operation, then EFI_UNSUPPORTED is\r
748 returned.\r
749\r
750 If Port or PortMultiplierPort are not in a valid range for this ATA controller, then\r
751 EFI_INVALID_PARAMETER is returned.\r
752\r
753 If a device error occurs while executing that device reset operation, then EFI_DEVICE_ERROR\r
754 is returned.\r
755\r
756 If a timeout occurs during the execution of the device reset operation, then EFI_TIMEOUT is\r
757 returned.\r
758\r
759 If the device reset operation is completed, then EFI_SUCCESS is returned.\r
760\r
761 @param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance.\r
762 @param[in] Port Port represents the port number of the ATA device to be reset.\r
763 @param[in] PortMultiplierPort The port multiplier port number of the ATA device to reset.\r
764 If there is no port multiplier, then specify 0.\r
765 @retval EFI_SUCCESS The ATA device specified by Port and PortMultiplierPort was reset.\r
766 @retval EFI_UNSUPPORTED The ATA controller does not support a device reset operation.\r
767 @retval EFI_INVALID_PARAMETER Port or PortMultiplierPort are invalid.\r
768 @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the ATA device\r
769 specified by Port and PortMultiplierPort.\r
770 @retval EFI_TIMEOUT A timeout occurred while attempting to reset the ATA device\r
771 specified by Port and PortMultiplierPort.\r
772\r
773**/\r
774EFI_STATUS\r
775SiI3132ResetDevice (\r
776 IN EFI_ATA_PASS_THRU_PROTOCOL *This,\r
777 IN UINT16 Port,\r
778 IN UINT16 PortMultiplierPort\r
779 )\r
780{\r
781 EFI_PCI_IO_PROTOCOL *PciIo;\r
782 SATA_SI3132_INSTANCE *SataSiI3132Instance;\r
783 SATA_SI3132_PORT *SataPort;\r
784 SATA_SI3132_DEVICE *SataDevice;\r
785 UINTN Timeout;\r
786 UINT32 Value32;\r
787\r
788 SATA_TRACE ("SiI3132ResetDevice()");\r
789\r
790 SataSiI3132Instance = INSTANCE_FROM_ATAPASSTHRU_THIS (This);\r
791 if (!SataSiI3132Instance) {\r
792 return EFI_INVALID_PARAMETER;\r
793 }\r
794\r
795 PciIo = SataSiI3132Instance->PciIo;\r
796\r
797 SataDevice = GetSataDevice (SataSiI3132Instance, Port, PortMultiplierPort);\r
798 if (!SataDevice) {\r
799 return EFI_INVALID_PARAMETER;\r
800 }\r
801 SataPort = SataDevice->Port;\r
802\r
803 SATA_PORT_WRITE32 (SataPort->RegBase + SII3132_PORT_CONTROLSET_REG, SII3132_PORT_DEVICE_RESET);\r
804\r
805 Timeout = 100;\r
806 SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_STATUS_REG, &Value32);\r
807 while ((Timeout > 0) && ((Value32 & SII3132_PORT_DEVICE_RESET) != 0)) {\r
808 gBS->Stall (1);\r
809 SATA_PORT_READ32 (SataPort->RegBase + SII3132_PORT_STATUS_REG, &Value32);\r
810 Timeout--;\r
811 }\r
812\r
813 if (Timeout == 0) {\r
814 SATA_TRACE ("SiI3132ResetDevice(): Timeout");\r
815 return EFI_TIMEOUT;\r
816 } else {\r
817 return EFI_SUCCESS;\r
818 }\r
819}\r