]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - QuarkSocPkg/QuarkNorthCluster/Spi/Common/SpiCommon.c
QuarkSocPkg: Add new package for Quark SoC X1000
[mirror_edk2.git] / QuarkSocPkg / QuarkNorthCluster / Spi / Common / SpiCommon.c
... / ...
CommitLineData
1/** @file\r
2PCH SPI Common Driver implements the SPI Host Controller Compatibility Interface.\r
3\r
4Copyright (c) 2013-2015 Intel Corporation.\r
5\r
6This program and the accompanying materials\r
7are licensed and made available under the terms and conditions of the BSD License\r
8which accompanies this distribution. The full text of the license may be found at\r
9http://opensource.org/licenses/bsd-license.php\r
10\r
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include "PchSpi.h"\r
17\r
18VOID\r
19FillOutPublicInfoStruct (\r
20 SPI_INSTANCE *SpiInstance\r
21 )\r
22/*++\r
23\r
24Routine Description:\r
25\r
26 Fillout SpiInstance->InitInfo;\r
27\r
28Arguments:\r
29\r
30 SpiInstance - Pointer to SpiInstance to initialize\r
31\r
32Returns:\r
33\r
34 NONE\r
35\r
36--*/\r
37{\r
38 UINT8 Index;\r
39\r
40 SpiInstance->InitInfo.InitTable = &SpiInstance->SpiInitTable;\r
41\r
42 //\r
43 // Give invalid index in case operation not supported.\r
44 //\r
45 SpiInstance->InitInfo.JedecIdOpcodeIndex = 0xff;\r
46 SpiInstance->InitInfo.OtherOpcodeIndex = 0xff;\r
47 SpiInstance->InitInfo.WriteStatusOpcodeIndex = 0xff;\r
48 SpiInstance->InitInfo.ProgramOpcodeIndex = 0xff;\r
49 SpiInstance->InitInfo.ReadOpcodeIndex = 0xff;\r
50 SpiInstance->InitInfo.EraseOpcodeIndex = 0xff;\r
51 SpiInstance->InitInfo.ReadStatusOpcodeIndex = 0xff;\r
52 SpiInstance->InitInfo.FullChipEraseOpcodeIndex = 0xff;\r
53 for (Index = 0; Index < SPI_NUM_OPCODE; Index++) {\r
54 if (SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationJedecId) {\r
55 SpiInstance->InitInfo.JedecIdOpcodeIndex = Index;\r
56 }\r
57 if (SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationOther) {\r
58 SpiInstance->InitInfo.OtherOpcodeIndex = Index;\r
59 }\r
60 if (SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationWriteStatus) {\r
61 SpiInstance->InitInfo.WriteStatusOpcodeIndex = Index;\r
62 }\r
63 if (SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationProgramData_1_Byte ||\r
64 SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationProgramData_64_Byte) {\r
65 SpiInstance->InitInfo.ProgramOpcodeIndex = Index;\r
66 }\r
67 if (SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationReadData ||\r
68 SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationFastRead ||\r
69 SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationDualOutputFastRead) {\r
70 SpiInstance->InitInfo.ReadOpcodeIndex = Index;\r
71 }\r
72 if (SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationErase_256_Byte ||\r
73 SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationErase_4K_Byte ||\r
74 SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationErase_8K_Byte ||\r
75 SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationErase_64K_Byte) {\r
76 SpiInstance->InitInfo.EraseOpcodeIndex = Index;\r
77 }\r
78 if (SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationReadStatus) {\r
79 SpiInstance->InitInfo.ReadStatusOpcodeIndex = Index;\r
80 }\r
81 if (SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationFullChipErase) {\r
82 SpiInstance->InitInfo.FullChipEraseOpcodeIndex = Index;\r
83 }\r
84 }\r
85}\r
86\r
87EFI_STATUS\r
88SpiProtocolConstructor (\r
89 SPI_INSTANCE *SpiInstance\r
90 )\r
91/*++\r
92\r
93Routine Description:\r
94\r
95 Initialize an SPI protocol instance.\r
96 The function will assert in debug if PCH RCBA has not been initialized\r
97\r
98Arguments:\r
99\r
100 SpiInstance - Pointer to SpiInstance to initialize\r
101\r
102Returns:\r
103\r
104 EFI_SUCCESS The protocol instance was properly initialized\r
105 EFI_UNSUPPORTED The PCH is not supported by this module\r
106\r
107--*/\r
108{\r
109 SpiInstance->InitDone = FALSE; // Indicate NOT READY.\r
110\r
111 //\r
112 // Check if the current PCH is known and supported by this code\r
113 //\r
114 if (!IsQncSupported ()) {\r
115 DEBUG ((DEBUG_ERROR, "PCH SPI Protocol not supported due to no proper QNC LPC found!\n"));\r
116 return EFI_UNSUPPORTED;\r
117 }\r
118 //\r
119 // Initialize the SPI protocol instance\r
120 //\r
121 SpiInstance->Signature = PCH_SPI_PRIVATE_DATA_SIGNATURE;\r
122 SpiInstance->Handle = NULL;\r
123 SpiInstance->SpiProtocol.Init = SpiProtocolInit;\r
124 SpiInstance->SpiProtocol.Lock = SpiProtocolLock;\r
125 SpiInstance->SpiProtocol.Execute = SpiProtocolExecute;\r
126 SpiInstance->SpiProtocol.Info = SpiProtocolInfo;\r
127\r
128 //\r
129 // Sanity check to ensure PCH RCBA initialization has occurred previously.\r
130 //\r
131 SpiInstance->PchRootComplexBar = MmioRead32 (\r
132 PciDeviceMmBase (PCI_BUS_NUMBER_QNC,\r
133 PCI_DEVICE_NUMBER_QNC_LPC,\r
134 PCI_FUNCTION_NUMBER_QNC_LPC) + R_QNC_LPC_RCBA\r
135 ) & B_QNC_LPC_RCBA_MASK;\r
136 ASSERT (SpiInstance->PchRootComplexBar != 0);\r
137\r
138 return EFI_SUCCESS;\r
139}\r
140\r
141EFI_STATUS\r
142EFIAPI\r
143UnlockFlashComponents (\r
144 IN EFI_SPI_PROTOCOL *This,\r
145 IN UINT8 UnlockCmdOpcodeIndex\r
146 )\r
147/*++\r
148\r
149Routine Description:\r
150\r
151 Issue unlock command to disable block protection, this only needs to be done once per SPI power on\r
152\r
153Arguments:\r
154\r
155 This A pointer to "EFI_SPI_PROTOCOL" for issuing commands\r
156 UnlockCmdOpcodeIndex The index of the Unlock command\r
157\r
158Returns:\r
159\r
160 EFI_SUCCESS UnLock operation succeed.\r
161 EFI_DEVICE_ERROR Device error, operation failed.\r
162\r
163--*/\r
164{\r
165 EFI_STATUS Status;\r
166 SPI_INSTANCE *SpiInstance;\r
167 UINT8 SpiStatus;\r
168 UINTN PchRootComplexBar;\r
169\r
170 if (UnlockCmdOpcodeIndex >= SPI_NUM_OPCODE) {\r
171 return EFI_UNSUPPORTED;\r
172 }\r
173\r
174 SpiInstance = SPI_INSTANCE_FROM_SPIPROTOCOL (This);\r
175 PchRootComplexBar = SpiInstance->PchRootComplexBar;\r
176\r
177 //\r
178 // Issue unlock command to disable block protection, this only needs to be done once per SPI power on\r
179 //\r
180 SpiStatus = 0;\r
181 //\r
182 // Issue unlock command to the flash component 1 at first\r
183 //\r
184 Status = SpiProtocolExecute (\r
185 This,\r
186 UnlockCmdOpcodeIndex,\r
187 SpiInstance->SpiInitTable.PrefixOpcode[0] == PCH_SPI_COMMAND_WRITE_ENABLE ? 0 : 1,\r
188 TRUE,\r
189 TRUE,\r
190 TRUE,\r
191 (UINTN) 0,\r
192 sizeof (SpiStatus),\r
193 &SpiStatus,\r
194 EnumSpiRegionAll\r
195 );\r
196 if (EFI_ERROR (Status)) {\r
197 DEBUG ((EFI_D_ERROR, "Unlock flash component 1 fail!\n"));\r
198 return Status;\r
199 }\r
200\r
201 return EFI_SUCCESS;\r
202}\r
203\r
204EFI_STATUS\r
205EFIAPI\r
206SpiProtocolInit (\r
207 IN EFI_SPI_PROTOCOL *This,\r
208 IN SPI_INIT_TABLE *InitTable\r
209 )\r
210/*++\r
211\r
212Routine Description:\r
213\r
214 Initialize the host controller to execute SPI command.\r
215\r
216Arguments:\r
217\r
218 This Pointer to the EFI_SPI_PROTOCOL instance.\r
219 InitTable Initialization data to be programmed into the SPI host controller.\r
220\r
221Returns:\r
222\r
223 EFI_SUCCESS Initialization completed.\r
224 EFI_ACCESS_DENIED The SPI static configuration interface has been locked-down.\r
225 EFI_INVALID_PARAMETER Bad input parameters.\r
226 EFI_UNSUPPORTED Can't get Descriptor mode VSCC values\r
227--*/\r
228{\r
229 EFI_STATUS Status;\r
230 UINT8 Index;\r
231 UINT16 OpcodeType;\r
232 SPI_INSTANCE *SpiInstance;\r
233 BOOLEAN MultiPartitionIsSupported;\r
234 UINTN PchRootComplexBar;\r
235 UINT8 SFDPCmdOpcodeIndex;\r
236 UINT8 UnlockCmdOpcodeIndex;\r
237 UINT8 ReadDataCmdOpcodeIndex;\r
238 UINT8 FlashPartId[3];\r
239\r
240 SpiInstance = SPI_INSTANCE_FROM_SPIPROTOCOL (This);\r
241 PchRootComplexBar = SpiInstance->PchRootComplexBar;\r
242\r
243 if (InitTable != NULL) {\r
244 //\r
245 // Copy table into SPI driver Private data structure\r
246 //\r
247 CopyMem (\r
248 &SpiInstance->SpiInitTable,\r
249 InitTable,\r
250 sizeof (SPI_INIT_TABLE)\r
251 );\r
252 } else {\r
253 return EFI_INVALID_PARAMETER;\r
254 }\r
255 //\r
256 // Check if the SPI interface has been locked-down.\r
257 //\r
258 if ((MmioRead16 (PchRootComplexBar + R_QNC_RCRB_SPIS) & B_QNC_RCRB_SPIS_SCL) != 0) {\r
259 ASSERT_EFI_ERROR (EFI_ACCESS_DENIED);\r
260 return EFI_ACCESS_DENIED;\r
261 }\r
262 //\r
263 // Clear all the status bits for status regs.\r
264 //\r
265 MmioOr16 (\r
266 (UINTN) (PchRootComplexBar + R_QNC_RCRB_SPIS),\r
267 (UINT16) ((B_QNC_RCRB_SPIS_CDS | B_QNC_RCRB_SPIS_BAS))\r
268 );\r
269 MmioRead16 (PchRootComplexBar + R_QNC_RCRB_SPIS);\r
270\r
271 //\r
272 // Set the Prefix Opcode registers.\r
273 //\r
274 MmioWrite16 (\r
275 PchRootComplexBar + R_QNC_RCRB_SPIPREOP,\r
276 (SpiInstance->SpiInitTable.PrefixOpcode[1] << 8) | InitTable->PrefixOpcode[0]\r
277 );\r
278 MmioRead16 (PchRootComplexBar + R_QNC_RCRB_SPIPREOP);\r
279\r
280 //\r
281 // Set Opcode Type Configuration registers.\r
282 //\r
283 for (Index = 0, OpcodeType = 0; Index < SPI_NUM_OPCODE; Index++) {\r
284 switch (SpiInstance->SpiInitTable.OpcodeMenu[Index].Type) {\r
285 case EnumSpiOpcodeRead:\r
286 OpcodeType |= (UINT16) (B_QNC_RCRB_SPIOPTYPE_ADD_READ << (Index * 2));\r
287 break;\r
288 case EnumSpiOpcodeWrite:\r
289 OpcodeType |= (UINT16) (B_QNC_RCRB_SPIOPTYPE_ADD_WRITE << (Index * 2));\r
290 break;\r
291 case EnumSpiOpcodeWriteNoAddr:\r
292 OpcodeType |= (UINT16) (B_QNC_RCRB_SPIOPTYPE_NOADD_WRITE << (Index * 2));\r
293 break;\r
294 default:\r
295 OpcodeType |= (UINT16) (B_QNC_RCRB_SPIOPTYPE_NOADD_READ << (Index * 2));\r
296 break;\r
297 }\r
298 }\r
299 MmioWrite16 (PchRootComplexBar + R_QNC_RCRB_SPIOPTYPE, OpcodeType);\r
300 MmioRead16 (PchRootComplexBar + R_QNC_RCRB_SPIOPTYPE);\r
301\r
302 //\r
303 // Setup the Opcode Menu registers.\r
304 //\r
305 ReadDataCmdOpcodeIndex = SPI_NUM_OPCODE;\r
306 SFDPCmdOpcodeIndex = SPI_NUM_OPCODE;\r
307 UnlockCmdOpcodeIndex = SPI_NUM_OPCODE;\r
308 for (Index = 0; Index < SPI_NUM_OPCODE; Index++) {\r
309 MmioWrite8 (\r
310 PchRootComplexBar + R_QNC_RCRB_SPIOPMENU + Index,\r
311 SpiInstance->SpiInitTable.OpcodeMenu[Index].Code\r
312 );\r
313 MmioRead8 (PchRootComplexBar + R_QNC_RCRB_SPIOPMENU + Index);\r
314 if (SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationJedecId) {\r
315 Status = SpiProtocolExecute (\r
316 This,\r
317 Index,\r
318 0,\r
319 TRUE,\r
320 TRUE,\r
321 FALSE,\r
322 (UINTN) 0,\r
323 3,\r
324 FlashPartId,\r
325 EnumSpiRegionDescriptor\r
326 );\r
327 if (EFI_ERROR (Status)) {\r
328 return Status;\r
329 }\r
330 if (FlashPartId[0] != SpiInstance->SpiInitTable.VendorId ||\r
331 FlashPartId[1] != SpiInstance->SpiInitTable.DeviceId0 ||\r
332 FlashPartId[2] != SpiInstance->SpiInitTable.DeviceId1) {\r
333 return EFI_INVALID_PARAMETER;\r
334 }\r
335 }\r
336\r
337 if (SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationReadData ||\r
338 SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationFastRead ||\r
339 SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationDualOutputFastRead) {\r
340 ReadDataCmdOpcodeIndex = Index;\r
341 }\r
342\r
343 if (SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationDiscoveryParameters) {\r
344 SFDPCmdOpcodeIndex = Index;\r
345 }\r
346\r
347 if (SpiInstance->SpiInitTable.OpcodeMenu[Index].Operation == EnumSpiOperationWriteStatus) {\r
348 UnlockCmdOpcodeIndex = Index;\r
349 }\r
350 }\r
351\r
352 MultiPartitionIsSupported = FALSE;\r
353\r
354 Status = UnlockFlashComponents (\r
355 This,\r
356 UnlockCmdOpcodeIndex\r
357 );\r
358 if (EFI_ERROR (Status)) {\r
359 DEBUG ((EFI_D_ERROR, "Unlock flash components fail!\n"));\r
360 }\r
361\r
362 SpiPhaseInit ();\r
363 FillOutPublicInfoStruct (SpiInstance);\r
364 SpiInstance->InitDone = TRUE;\r
365 return EFI_SUCCESS;\r
366}\r
367\r
368EFI_STATUS\r
369EFIAPI\r
370SpiProtocolLock (\r
371 IN EFI_SPI_PROTOCOL *This\r
372 )\r
373/*++\r
374\r
375Routine Description:\r
376\r
377 Lock the SPI Static Configuration Interface.\r
378 Once locked, the interface can not be changed and can only be clear by system reset.\r
379\r
380Arguments:\r
381\r
382 This Pointer to the EFI_SPI_PROTOCOL instance.\r
383\r
384Returns:\r
385\r
386 EFI_SUCCESS Lock operation succeed.\r
387 EFI_DEVICE_ERROR Device error, operation failed.\r
388 EFI_ACCESS_DENIED The interface has already been locked.\r
389\r
390--*/\r
391{\r
392 SPI_INSTANCE *SpiInstance;\r
393 UINTN PchRootComplexBar;\r
394\r
395 SpiInstance = SPI_INSTANCE_FROM_SPIPROTOCOL (This);\r
396 PchRootComplexBar = SpiInstance->PchRootComplexBar;\r
397\r
398 //\r
399 // Check if the SPI interface has been locked-down.\r
400 //\r
401 if ((MmioRead16 (PchRootComplexBar + R_QNC_RCRB_SPIS) & B_QNC_RCRB_SPIS_SCL) != 0) {\r
402 return EFI_ACCESS_DENIED;\r
403 }\r
404\r
405 //\r
406 // Lock-down the configuration interface.\r
407 //\r
408 MmioOr16 ((UINTN) (PchRootComplexBar + R_QNC_RCRB_SPIS), (UINT16) (B_QNC_RCRB_SPIS_SCL));\r
409\r
410 //\r
411 // Verify if it's really locked.\r
412 //\r
413 if ((MmioRead16 (PchRootComplexBar + R_QNC_RCRB_SPIS) & B_QNC_RCRB_SPIS_SCL) == 0) {\r
414 return EFI_DEVICE_ERROR;\r
415 } else {\r
416 //\r
417 // Save updated register in S3 Boot script.\r
418 //\r
419 S3BootScriptSaveMemWrite (\r
420 S3BootScriptWidthUint16,\r
421 (UINTN) (PchRootComplexBar + R_QNC_RCRB_SPIS),\r
422 1,\r
423 (VOID *) (UINTN) (PchRootComplexBar + R_QNC_RCRB_SPIS)\r
424 );\r
425 }\r
426\r
427 return EFI_SUCCESS;\r
428}\r
429\r
430EFI_STATUS\r
431EFIAPI\r
432SpiProtocolExecute (\r
433 IN EFI_SPI_PROTOCOL *This,\r
434 IN UINT8 OpcodeIndex,\r
435 IN UINT8 PrefixOpcodeIndex,\r
436 IN BOOLEAN DataCycle,\r
437 IN BOOLEAN Atomic,\r
438 IN BOOLEAN ShiftOut,\r
439 IN UINTN Address,\r
440 IN UINT32 DataByteCount,\r
441 IN OUT UINT8 *Buffer,\r
442 IN SPI_REGION_TYPE SpiRegionType\r
443 )\r
444/*++\r
445\r
446Routine Description:\r
447\r
448 Execute SPI commands from the host controller.\r
449 This function would be called by runtime driver, please do not use any MMIO marco here\r
450\r
451Arguments:\r
452\r
453 This Pointer to the EFI_SPI_PROTOCOL instance.\r
454 OpcodeIndex Index of the command in the OpCode Menu.\r
455 PrefixOpcodeIndex Index of the first command to run when in an atomic cycle sequence.\r
456 DataCycle TRUE if the SPI cycle contains data\r
457 Atomic TRUE if the SPI cycle is atomic and interleave cycles are not allowed.\r
458 ShiftOut If DataByteCount is not zero, TRUE to shift data out and FALSE to shift data in.\r
459 Address In Descriptor Mode, for Descriptor Region, GbE Region, ME Region and Platform\r
460 Region, this value specifies the offset from the Region Base; for BIOS Region,\r
461 this value specifies the offset from the start of the BIOS Image. In Non\r
462 Descriptor Mode, this value specifies the offset from the start of the BIOS Image.\r
463 Please note BIOS Image size may be smaller than BIOS Region size (in Descriptor\r
464 Mode) or the flash size (in Non Descriptor Mode), and in this case, BIOS Image is\r
465 supposed to be placed at the top end of the BIOS Region (in Descriptor Mode) or\r
466 the flash (in Non Descriptor Mode)\r
467 DataByteCount Number of bytes in the data portion of the SPI cycle. This function may break the\r
468 data transfer into multiple operations. This function ensures each operation does\r
469 not cross 256 byte flash address boundary.\r
470 *NOTE: if there is some SPI chip that has a stricter address boundary requirement\r
471 (e.g., its write page size is < 256 byte), then the caller cannot rely on this\r
472 function to cut the data transfer at proper address boundaries, and it's the\r
473 caller's reponsibility to pass in a properly cut DataByteCount parameter.\r
474 Buffer Pointer to caller-allocated buffer containing the dada received or sent during the\r
475 SPI cycle.\r
476 SpiRegionType SPI Region type. Values EnumSpiRegionBios, EnumSpiRegionGbE, EnumSpiRegionMe,\r
477 EnumSpiRegionDescriptor, and EnumSpiRegionPlatformData are only applicable in\r
478 Descriptor mode. Value EnumSpiRegionAll is applicable to both Descriptor Mode\r
479 and Non Descriptor Mode, which indicates "SpiRegionOffset" is actually relative\r
480 to base of the 1st flash device (i.e., it is a Flash Linear Address).\r
481\r
482Returns:\r
483\r
484 EFI_SUCCESS Command succeed.\r
485 EFI_INVALID_PARAMETER The parameters specified are not valid.\r
486 EFI_UNSUPPORTED Command not supported.\r
487 EFI_DEVICE_ERROR Device error, command aborts abnormally.\r
488\r
489--*/\r
490{\r
491 EFI_STATUS Status;\r
492 UINT16 BiosCtlSave;\r
493 UINT32 SmiEnSave;\r
494\r
495 BiosCtlSave = 0;\r
496 SmiEnSave = 0;\r
497\r
498 //\r
499 // Check if the parameters are valid.\r
500 //\r
501 if ((OpcodeIndex >= SPI_NUM_OPCODE) || (PrefixOpcodeIndex >= SPI_NUM_PREFIX_OPCODE)) {\r
502 return EFI_INVALID_PARAMETER;\r
503 }\r
504 //\r
505 // Make sure it's safe to program the command.\r
506 //\r
507 if (!WaitForSpiCycleComplete (This, FALSE)) {\r
508 return EFI_DEVICE_ERROR;\r
509 }\r
510\r
511 //\r
512 // Acquire access to the SPI interface is not required any more.\r
513 //\r
514 //\r
515 // Disable SMIs to make sure normal mode flash access is not interrupted by an SMI\r
516 // whose SMI handler accesses flash (e.g. for error logging)\r
517 //\r
518 SmiEnSave = QNCPortRead (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC);\r
519 QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC, (SmiEnSave & ~SMI_EN));\r
520\r
521 //\r
522 // Save BIOS Ctrl register\r
523 //\r
524 BiosCtlSave = PciRead16 (\r
525 PCI_LIB_ADDRESS (PCI_BUS_NUMBER_QNC,\r
526 PCI_DEVICE_NUMBER_QNC_LPC,\r
527 PCI_FUNCTION_NUMBER_QNC_LPC,\r
528 R_QNC_LPC_BIOS_CNTL)\r
529 ) & (B_QNC_LPC_BIOS_CNTL_BCD | B_QNC_LPC_BIOS_CNTL_PFE | B_QNC_LPC_BIOS_CNTL_BIOSWE | B_QNC_LPC_BIOS_CNTL_SMM_BWP);\r
530\r
531 //\r
532 // Enable flash writing\r
533 //\r
534 PciOr16 (\r
535 PCI_LIB_ADDRESS (PCI_BUS_NUMBER_QNC,\r
536 PCI_DEVICE_NUMBER_QNC_LPC,\r
537 PCI_FUNCTION_NUMBER_QNC_LPC,\r
538 R_QNC_LPC_BIOS_CNTL),\r
539 (UINT16) (B_QNC_LPC_BIOS_CNTL_BIOSWE | B_QNC_LPC_BIOS_CNTL_SMM_BWP)\r
540 );\r
541\r
542 //\r
543 // If shifts the data out, disable Prefetching and Caching.\r
544 //\r
545 if (ShiftOut) {\r
546 PciAndThenOr16 (\r
547 PCI_LIB_ADDRESS (PCI_BUS_NUMBER_QNC,\r
548 PCI_DEVICE_NUMBER_QNC_LPC,\r
549 PCI_FUNCTION_NUMBER_QNC_LPC,\r
550 R_QNC_LPC_BIOS_CNTL),\r
551 (UINT16) (~(B_QNC_LPC_BIOS_CNTL_BCD | B_QNC_LPC_BIOS_CNTL_PFE)),\r
552 (UINT16) ((B_QNC_LPC_BIOS_CNTL_BCD))\r
553 );\r
554 }\r
555 //\r
556 // Sends the command to the SPI interface to execute.\r
557 //\r
558 Status = SendSpiCmd (\r
559 This,\r
560 OpcodeIndex,\r
561 PrefixOpcodeIndex,\r
562 DataCycle,\r
563 Atomic,\r
564 ShiftOut,\r
565 Address,\r
566 DataByteCount,\r
567 Buffer,\r
568 SpiRegionType\r
569 );\r
570\r
571 //\r
572 // Restore BIOS Ctrl register\r
573 //\r
574 PciAndThenOr16 (\r
575 PCI_LIB_ADDRESS (PCI_BUS_NUMBER_QNC,\r
576 PCI_DEVICE_NUMBER_QNC_LPC,\r
577 PCI_FUNCTION_NUMBER_QNC_LPC,\r
578 R_QNC_LPC_BIOS_CNTL),\r
579 (UINT16) (~(B_QNC_LPC_BIOS_CNTL_BCD | B_QNC_LPC_BIOS_CNTL_PFE | B_QNC_LPC_BIOS_CNTL_BIOSWE | B_QNC_LPC_BIOS_CNTL_SMM_BWP)),\r
580 (UINT16) (BiosCtlSave)\r
581 );\r
582 //\r
583 // Restore SMIs.\r
584 //\r
585 QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC, SmiEnSave);\r
586\r
587 return Status;\r
588}\r
589\r
590VOID\r
591SpiOffset2Physical (\r
592 IN EFI_SPI_PROTOCOL *This,\r
593 IN UINTN SpiRegionOffset,\r
594 IN SPI_REGION_TYPE SpiRegionType,\r
595 OUT UINTN *HardwareSpiAddress,\r
596 OUT UINTN *BaseAddress,\r
597 OUT UINTN *LimitAddress\r
598 )\r
599/*++\r
600\r
601Routine Description:\r
602\r
603 Convert SPI offset to Physical address of SPI hardware\r
604\r
605Arguments:\r
606\r
607 This Pointer to the EFI_SPI_PROTOCOL instance.\r
608 SpiRegionOffset In Descriptor Mode, for Descriptor Region, GbE Region, ME Region and Platform\r
609 Region, this value specifies the offset from the Region Base; for BIOS Region,\r
610 this value specifies the offset from the start of the BIOS Image. In Non\r
611 Descriptor Mode, this value specifies the offset from the start of the BIOS Image.\r
612 Please note BIOS Image size may be smaller than BIOS Region size (in Descriptor\r
613 Mode) or the flash size (in Non Descriptor Mode), and in this case, BIOS Image is\r
614 supposed to be placed at the top end of the BIOS Region (in Descriptor Mode) or\r
615 the flash (in Non Descriptor Mode)\r
616 BaseAddress Base Address of the region.\r
617 SpiRegionType SPI Region type. Values EnumSpiRegionBios, EnumSpiRegionGbE, EnumSpiRegionMe,\r
618 EnumSpiRegionDescriptor, and EnumSpiRegionPlatformData are only applicable in\r
619 Descriptor mode. Value EnumSpiRegionAll is applicable to both Descriptor Mode\r
620 and Non Descriptor Mode, which indicates "SpiRegionOffset" is actually relative\r
621 to base of the 1st flash device (i.e., it is a Flash Linear Address).\r
622 HardwareSpiAddress Return absolution SPI address (i.e., Flash Linear Address)\r
623 BaseAddress Return base address of the region type\r
624 LimitAddress Return limit address of the region type\r
625\r
626Returns:\r
627\r
628 EFI_SUCCESS Command succeed.\r
629\r
630--*/\r
631{\r
632 SPI_INSTANCE *SpiInstance;\r
633 UINTN PchRootComplexBar;\r
634\r
635 SpiInstance = SPI_INSTANCE_FROM_SPIPROTOCOL (This);\r
636 PchRootComplexBar = SpiInstance->PchRootComplexBar;\r
637\r
638 if (SpiRegionType == EnumSpiRegionAll) {\r
639 //\r
640 // EnumSpiRegionAll indicates address is relative to flash device (i.e., address is Flash\r
641 // Linear Address)\r
642 //\r
643 *HardwareSpiAddress = SpiRegionOffset;\r
644 } else {\r
645 //\r
646 // Otherwise address is relative to BIOS image\r
647 //\r
648 *HardwareSpiAddress = SpiRegionOffset + SpiInstance->SpiInitTable.BiosStartOffset;\r
649 }\r
650}\r
651\r
652EFI_STATUS\r
653SendSpiCmd (\r
654 IN EFI_SPI_PROTOCOL *This,\r
655 IN UINT8 OpcodeIndex,\r
656 IN UINT8 PrefixOpcodeIndex,\r
657 IN BOOLEAN DataCycle,\r
658 IN BOOLEAN Atomic,\r
659 IN BOOLEAN ShiftOut,\r
660 IN UINTN Address,\r
661 IN UINT32 DataByteCount,\r
662 IN OUT UINT8 *Buffer,\r
663 IN SPI_REGION_TYPE SpiRegionType\r
664 )\r
665/*++\r
666\r
667Routine Description:\r
668\r
669 This function sends the programmed SPI command to the slave device.\r
670\r
671Arguments:\r
672\r
673 OpcodeIndex Index of the command in the OpCode Menu.\r
674 PrefixOpcodeIndex Index of the first command to run when in an atomic cycle sequence.\r
675 DataCycle TRUE if the SPI cycle contains data\r
676 Atomic TRUE if the SPI cycle is atomic and interleave cycles are not allowed.\r
677 ShiftOut If DataByteCount is not zero, TRUE to shift data out and FALSE to shift data in.\r
678 Address In Descriptor Mode, for Descriptor Region, GbE Region, ME Region and Platform\r
679 Region, this value specifies the offset from the Region Base; for BIOS Region,\r
680 this value specifies the offset from the start of the BIOS Image. In Non\r
681 Descriptor Mode, this value specifies the offset from the start of the BIOS Image.\r
682 Please note BIOS Image size may be smaller than BIOS Region size (in Descriptor\r
683 Mode) or the flash size (in Non Descriptor Mode), and in this case, BIOS Image is\r
684 supposed to be placed at the top end of the BIOS Region (in Descriptor Mode) or\r
685 the flash (in Non Descriptor Mode)\r
686 DataByteCount Number of bytes in the data portion of the SPI cycle. This function may break the\r
687 data transfer into multiple operations. This function ensures each operation does\r
688 not cross 256 byte flash address boundary.\r
689 *NOTE: if there is some SPI chip that has a stricter address boundary requirement\r
690 (e.g., its write page size is < 256 byte), then the caller cannot rely on this\r
691 function to cut the data transfer at proper address boundaries, and it's the\r
692 caller's reponsibility to pass in a properly cut DataByteCount parameter.\r
693 Buffer Data received or sent during the SPI cycle.\r
694 SpiRegionType SPI Region type. Values EnumSpiRegionBios, EnumSpiRegionGbE, EnumSpiRegionMe,\r
695 EnumSpiRegionDescriptor, and EnumSpiRegionPlatformData are only applicable in\r
696 Descriptor mode. Value EnumSpiRegionAll is applicable to both Descriptor Mode\r
697 and Non Descriptor Mode, which indicates "SpiRegionOffset" is actually relative\r
698 to base of the 1st flash device (i.e., it is a Flash Linear Address).\r
699\r
700Returns:\r
701\r
702 EFI_SUCCESS SPI command completes successfully.\r
703 EFI_DEVICE_ERROR Device error, the command aborts abnormally.\r
704 EFI_ACCESS_DENIED Some unrecognized command encountered in hardware sequencing mode\r
705 EFI_INVALID_PARAMETER The parameters specified are not valid.\r
706\r
707--*/\r
708{\r
709 UINT32 Index;\r
710 SPI_INSTANCE *SpiInstance;\r
711 UINTN HardwareSpiAddr;\r
712 UINTN SpiBiosSize;\r
713 UINTN BaseAddress;\r
714 UINTN LimitAddress;\r
715 UINT32 SpiDataCount;\r
716 UINT8 OpCode;\r
717 SPI_OPERATION Operation;\r
718 UINTN PchRootComplexBar;\r
719\r
720 SpiInstance = SPI_INSTANCE_FROM_SPIPROTOCOL (This);\r
721 PchRootComplexBar = SpiInstance->PchRootComplexBar;\r
722 SpiBiosSize = SpiInstance->SpiInitTable.BiosSize;\r
723 Operation = SpiInstance->SpiInitTable.OpcodeMenu[OpcodeIndex].Operation;\r
724 OpCode = MmioRead8 (PchRootComplexBar + R_QNC_RCRB_SPIOPMENU + OpcodeIndex);\r
725\r
726 //\r
727 // Check if the value of opcode register is 0 or the BIOS Size of SpiInitTable is 0\r
728 //\r
729 if (OpCode == 0 || SpiBiosSize == 0) {\r
730 ASSERT (FALSE);\r
731 return EFI_INVALID_PARAMETER;\r
732 }\r
733\r
734 SpiOffset2Physical (This, Address, SpiRegionType, &HardwareSpiAddr, &BaseAddress, &LimitAddress);\r
735 //\r
736 // Have direct access to BIOS region in Descriptor mode,\r
737 //\r
738 if (SpiInstance->SpiInitTable.OpcodeMenu[OpcodeIndex].Type == EnumSpiOpcodeRead &&\r
739 SpiRegionType == EnumSpiRegionBios) {\r
740 CopyMem (\r
741 Buffer,\r
742 (UINT8 *) ((HardwareSpiAddr - BaseAddress) + (UINT32) (~(SpiBiosSize - 1))),\r
743 DataByteCount\r
744 );\r
745 return EFI_SUCCESS;\r
746 }\r
747 //\r
748 // DEBUG((EFI_D_ERROR, "SPIADDR %x, %x, %x, %x\n", Address, HardwareSpiAddr, BaseAddress,\r
749 // LimitAddress));\r
750 //\r
751 if ((DataCycle == FALSE) && (DataByteCount > 0)) {\r
752 DataByteCount = 0;\r
753 }\r
754\r
755 do {\r
756 //\r
757 // Trim at 256 byte boundary per operation,\r
758 // - PCH SPI controller requires trimming at 4KB boundary\r
759 // - Some SPI chips require trimming at 256 byte boundary for write operation\r
760 // - Trimming has limited performance impact as we can read / write atmost 64 byte\r
761 // per operation\r
762 //\r
763 if (HardwareSpiAddr + DataByteCount > ((HardwareSpiAddr + BIT8) &~(BIT8 - 1))) {\r
764 SpiDataCount = (((UINT32) (HardwareSpiAddr) + BIT8) &~(BIT8 - 1)) - (UINT32) (HardwareSpiAddr);\r
765 } else {\r
766 SpiDataCount = DataByteCount;\r
767 }\r
768 //\r
769 // Calculate the number of bytes to shift in/out during the SPI data cycle.\r
770 // Valid settings for the number of bytes duing each data portion of the\r
771 // PCH SPI cycles are: 0, 1, 2, 3, 4, 5, 6, 7, 8, 16, 24, 32, 40, 48, 56, 64\r
772 //\r
773 if (SpiDataCount >= 64) {\r
774 SpiDataCount = 64;\r
775 } else if ((SpiDataCount &~0x07) != 0) {\r
776 SpiDataCount = SpiDataCount &~0x07;\r
777 }\r
778 //\r
779 // If shifts data out, load data into the SPI data buffer.\r
780 //\r
781 if (ShiftOut) {\r
782 for (Index = 0; Index < SpiDataCount; Index++) {\r
783 MmioWrite8 (PchRootComplexBar + R_QNC_RCRB_SPID0 + Index, Buffer[Index]);\r
784 MmioRead8 (PchRootComplexBar + R_QNC_RCRB_SPID0 + Index);\r
785 }\r
786 }\r
787\r
788 MmioWrite32 (\r
789 (PchRootComplexBar + R_QNC_RCRB_SPIA),\r
790 (UINT32) (HardwareSpiAddr & B_QNC_RCRB_SPIA_MASK)\r
791 );\r
792 MmioRead32 (PchRootComplexBar + R_QNC_RCRB_SPIA);\r
793\r
794 //\r
795 // Execute the command on the SPI compatible mode\r
796 //\r
797\r
798 //\r
799 // Clear error flags\r
800 //\r
801 MmioOr16 ((PchRootComplexBar + R_QNC_RCRB_SPIS), B_QNC_RCRB_SPIS_BAS);\r
802\r
803 //\r
804 // Initialte the SPI cycle\r
805 //\r
806 if (DataCycle) {\r
807 MmioWrite16 (\r
808 (PchRootComplexBar + R_QNC_RCRB_SPIC),\r
809 ( (UINT16) (B_QNC_RCRB_SPIC_DC) | (UINT16) (((SpiDataCount - 1) << 8) & B_QNC_RCRB_SPIC_DBC) |\r
810 (UINT16) ((OpcodeIndex << 4) & B_QNC_RCRB_SPIC_COP) |\r
811 (UINT16) ((PrefixOpcodeIndex << 3) & B_QNC_RCRB_SPIC_SPOP) |\r
812 (UINT16) (Atomic ? B_QNC_RCRB_SPIC_ACS : 0) |\r
813 (UINT16) (B_QNC_RCRB_SPIC_SCGO)));\r
814 } else {\r
815 MmioWrite16 (\r
816 (PchRootComplexBar + R_QNC_RCRB_SPIC),\r
817 ( (UINT16) ((OpcodeIndex << 4) & B_QNC_RCRB_SPIC_COP) |\r
818 (UINT16) ((PrefixOpcodeIndex << 3) & B_QNC_RCRB_SPIC_SPOP) |\r
819 (UINT16) (Atomic ? B_QNC_RCRB_SPIC_ACS : 0) |\r
820 (UINT16) (B_QNC_RCRB_SPIC_SCGO)));\r
821 }\r
822\r
823 MmioRead16 (PchRootComplexBar + R_QNC_RCRB_SPIC);\r
824\r
825 //\r
826 // end of command execution\r
827 //\r
828 // Wait the SPI cycle to complete.\r
829 //\r
830 if (!WaitForSpiCycleComplete (This, TRUE)) {\r
831 return EFI_DEVICE_ERROR;\r
832 }\r
833 //\r
834 // If shifts data in, get data from the SPI data buffer.\r
835 //\r
836 if (!ShiftOut) {\r
837 for (Index = 0; Index < SpiDataCount; Index++) {\r
838 Buffer[Index] = MmioRead8 (PchRootComplexBar + R_QNC_RCRB_SPID0 + Index);\r
839 }\r
840 }\r
841\r
842 HardwareSpiAddr += SpiDataCount;\r
843 Buffer += SpiDataCount;\r
844 DataByteCount -= SpiDataCount;\r
845 } while (DataByteCount > 0);\r
846\r
847 return EFI_SUCCESS;\r
848}\r
849\r
850BOOLEAN\r
851WaitForSpiCycleComplete (\r
852 IN EFI_SPI_PROTOCOL *This,\r
853 IN BOOLEAN ErrorCheck\r
854 )\r
855/*++\r
856\r
857Routine Description:\r
858\r
859 Wait execution cycle to complete on the SPI interface. Check both Hardware\r
860 and Software Sequencing status registers\r
861\r
862Arguments:\r
863\r
864 This - The SPI protocol instance\r
865 UseSoftwareSequence - TRUE if this is a Hardware Sequencing operation\r
866 ErrorCheck - TRUE if the SpiCycle needs to do the error check\r
867\r
868Returns:\r
869\r
870 TRUE SPI cycle completed on the interface.\r
871 FALSE Time out while waiting the SPI cycle to complete.\r
872 It's not safe to program the next command on the SPI interface.\r
873\r
874--*/\r
875{\r
876 UINT64 WaitTicks;\r
877 UINT64 WaitCount;\r
878 UINT16 Data16;\r
879 SPI_INSTANCE *SpiInstance;\r
880 UINTN PchRootComplexBar;\r
881\r
882 SpiInstance = SPI_INSTANCE_FROM_SPIPROTOCOL (This);\r
883 PchRootComplexBar = SpiInstance->PchRootComplexBar;\r
884\r
885 //\r
886 // Convert the wait period allowed into to tick count\r
887 //\r
888 WaitCount = WAIT_TIME / WAIT_PERIOD;\r
889\r
890 //\r
891 // Wait for the SPI cycle to complete.\r
892 //\r
893 for (WaitTicks = 0; WaitTicks < WaitCount; WaitTicks++) {\r
894 Data16 = MmioRead16 (PchRootComplexBar + R_QNC_RCRB_SPIS);\r
895 if ((Data16 & B_QNC_RCRB_SPIS_SCIP) == 0) {\r
896 MmioWrite16 (PchRootComplexBar + R_QNC_RCRB_SPIS, (B_QNC_RCRB_SPIS_BAS | B_QNC_RCRB_SPIS_CDS));\r
897 if ((Data16 & B_QNC_RCRB_SPIS_BAS) && (ErrorCheck == TRUE)) {\r
898 return FALSE;\r
899 } else {\r
900 return TRUE;\r
901 }\r
902 }\r
903\r
904 MicroSecondDelay (WAIT_PERIOD);\r
905 }\r
906\r
907 return FALSE;\r
908}\r
909\r
910EFI_STATUS\r
911EFIAPI\r
912SpiProtocolInfo (\r
913 IN EFI_SPI_PROTOCOL *This,\r
914 OUT SPI_INIT_INFO **InitInfoPtr\r
915 )\r
916/*++\r
917\r
918Routine Description:\r
919\r
920 Return info about SPI host controller, to help callers usage of Execute\r
921 service.\r
922\r
923 If 0xff is returned as an opcode index in init info struct\r
924 then device does not support the operation.\r
925\r
926Arguments:\r
927\r
928 This Pointer to the EFI_SPI_PROTOCOL instance.\r
929 InitInfoPtr Pointer to init info written to this memory location.\r
930\r
931Returns:\r
932\r
933 EFI_SUCCESS Information returned.\r
934 EFI_INVALID_PARAMETER Invalid parameter.\r
935 EFI_NOT_READY Required resources not setup.\r
936 Others Unexpected error happened.\r
937\r
938--*/\r
939{\r
940 SPI_INSTANCE *SpiInstance;\r
941\r
942 if (This == NULL || InitInfoPtr == NULL) {\r
943 return EFI_INVALID_PARAMETER;\r
944 }\r
945 SpiInstance = SPI_INSTANCE_FROM_SPIPROTOCOL (This);\r
946 if (SpiInstance->Signature != PCH_SPI_PRIVATE_DATA_SIGNATURE) {\r
947 return EFI_INVALID_PARAMETER;\r
948 }\r
949\r
950 if (!SpiInstance->InitDone) {\r
951 *InitInfoPtr = NULL;\r
952 return EFI_NOT_READY;\r
953 }\r
954 *InitInfoPtr = &SpiInstance->InitInfo;\r
955 return EFI_SUCCESS;\r
956}\r