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