MmcDxe: Adding eMMC support
[mirror_edk2.git] / EmbeddedPkg / Universal / MmcDxe / MmcIdentification.c
CommitLineData
b4fdedc2
OM
1/** @file\r
2*\r
3* Copyright (c) 2011-2014, ARM Limited. All rights reserved.\r
4*\r
5* This program and the accompanying materials\r
6* are licensed and made available under the terms and conditions of the BSD License\r
7* which accompanies this distribution. The full text of the license may be found at\r
8* http://opensource.org/licenses/bsd-license.php\r
9*\r
10* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12*\r
13**/\r
14\r
15#include <Library/TimerLib.h>\r
16\r
17#include "Mmc.h"\r
18\r
19typedef union {\r
20 UINT32 Raw;\r
21 OCR Ocr;\r
22} OCR_RESPONSE;\r
23\r
24#define MAX_RETRY_COUNT 1000\r
25#define CMD_RETRY_COUNT 20\r
26#define RCA_SHIFT_OFFSET 16\r
27#define EMMC_CARD_SIZE 512\r
28#define EMMC_ECSD_SIZE_OFFSET 53\r
29\r
30UINT32 mEmmcRcaCount = 0;\r
31\r
32STATIC\r
33EFI_STATUS\r
34EFIAPI\r
35EmmcIdentificationMode (\r
36 IN MMC_HOST_INSTANCE *MmcHostInstance,\r
37 IN OCR_RESPONSE Response\r
38 )\r
39{\r
40 EFI_MMC_HOST_PROTOCOL *Host;\r
41 EFI_BLOCK_IO_MEDIA *Media;\r
42 EFI_STATUS Status;\r
43 UINT32 RCA;\r
44 UINT32 ECSD[128];\r
45\r
46 Host = MmcHostInstance->MmcHost;\r
47 Media = MmcHostInstance->BlockIo.Media;\r
48\r
49 // Fetch card identity register\r
50 Status = Host->SendCommand (Host, MMC_CMD2, 0);\r
51 if (EFI_ERROR (Status)) {\r
52 DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Failed to send CMD2, Status=%r.\n", Status));\r
53 return Status;\r
54 }\r
55\r
56 Status = Host->ReceiveResponse (Host, MMC_RESPONSE_TYPE_R2, (UINT32 *)&(MmcHostInstance->CardInfo.CIDData));\r
57 if (EFI_ERROR (Status)) {\r
58 DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): CID retrieval error, Status=%r.\n", Status));\r
59 return Status;\r
60 }\r
61\r
62 // Assign a relative address value to the card\r
63 MmcHostInstance->CardInfo.RCA = ++mEmmcRcaCount; // TODO: might need a more sophisticated way of doing this\r
64 RCA = MmcHostInstance->CardInfo.RCA << RCA_SHIFT_OFFSET;\r
65 Status = Host->SendCommand (Host, MMC_CMD3, RCA);\r
66 if (EFI_ERROR (Status)) {\r
67 DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): RCA set error, Status=%r.\n", Status));\r
68 return Status;\r
69 }\r
70\r
71 // Fetch card specific data\r
72 Status = Host->SendCommand (Host, MMC_CMD9, RCA);\r
73 if (EFI_ERROR (Status)) {\r
74 DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Failed to send CMD9, Status=%r.\n", Status));\r
75 return Status;\r
76 }\r
77\r
78 Status = Host->ReceiveResponse (Host, MMC_RESPONSE_TYPE_R2, (UINT32 *)&(MmcHostInstance->CardInfo.CSDData));\r
79 if (EFI_ERROR (Status)) {\r
80 DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): CSD retrieval error, Status=%r.\n", Status));\r
81 return Status;\r
82 }\r
83\r
84 // Select the card\r
85 Status = Host->SendCommand (Host, MMC_CMD7, RCA);\r
86 if (EFI_ERROR (Status)) {\r
87 DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): Card selection error, Status=%r.\n", Status));\r
88 }\r
89\r
90 // Fetch ECSD\r
91 Status = Host->SendCommand (Host, MMC_CMD8, RCA);\r
92 if (EFI_ERROR (Status)) {\r
93 DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): ECSD fetch error, Status=%r.\n", Status));\r
94 }\r
95\r
96 Status = Host->ReadBlockData (Host, 0, 512, ECSD);\r
97 if (EFI_ERROR (Status)) {\r
98 DEBUG ((EFI_D_ERROR, "EmmcIdentificationMode(): ECSD read error, Status=%r.\n", Status));\r
99 return Status;\r
100 }\r
101\r
102 // Set up media\r
103 Media->BlockSize = EMMC_CARD_SIZE; // 512-byte support is mandatory for eMMC cards\r
104 Media->MediaId = MmcHostInstance->CardInfo.CIDData.PSN;\r
105 Media->ReadOnly = MmcHostInstance->CardInfo.CSDData.PERM_WRITE_PROTECT;\r
106 Media->LogicalBlocksPerPhysicalBlock = 1;\r
107 Media->IoAlign = 4;\r
108 // Compute last block using bits [215:212] of the ECSD\r
109 Media->LastBlock = ECSD[EMMC_ECSD_SIZE_OFFSET] - 1; // eMMC isn't supposed to report this for\r
110 // Cards <2GB in size, but the model does.\r
111\r
112 // Setup card type\r
113 MmcHostInstance->CardInfo.CardType = EMMC_CARD;\r
114 return EFI_SUCCESS;\r
115}\r
116\r
117STATIC\r
118EFI_STATUS\r
119InitializeSdMmcDevice (\r
120 IN MMC_HOST_INSTANCE *MmcHostInstance\r
121 )\r
122{\r
123 UINT32 CmdArg;\r
124 UINT32 Response[4];\r
125 UINTN BlockSize;\r
126 UINTN CardSize;\r
127 UINTN NumBlocks;\r
128 EFI_STATUS Status;\r
129 EFI_MMC_HOST_PROTOCOL *MmcHost;\r
130\r
131 MmcHost = MmcHostInstance->MmcHost;\r
132\r
133 // Send a command to get Card specific data\r
134 CmdArg = MmcHostInstance->CardInfo.RCA << 16;\r
135 Status = MmcHost->SendCommand (MmcHost, MMC_CMD9, CmdArg);\r
136 if (EFI_ERROR (Status)) {\r
137 DEBUG((EFI_D_ERROR, "InitializeSdMmcDevice(MMC_CMD9): Error, Status=%r\n", Status));\r
138 return Status;\r
139 }\r
140\r
141 // Read Response\r
142 Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_CSD, Response);\r
143 if (EFI_ERROR (Status)) {\r
144 DEBUG((EFI_D_ERROR, "InitializeSdMmcDevice(): Failed to receive CSD, Status=%r\n", Status));\r
145 return Status;\r
146 }\r
147 PrintCSD (Response);\r
148\r
149 if (MmcHostInstance->CardInfo.CardType == SD_CARD_2_HIGH) {\r
150 CardSize = HC_MMC_CSD_GET_DEVICESIZE (Response);\r
151 NumBlocks = ((CardSize + 1) * 1024);\r
152 BlockSize = 1 << MMC_CSD_GET_READBLLEN (Response);\r
153 } else {\r
154 CardSize = MMC_CSD_GET_DEVICESIZE (Response);\r
155 NumBlocks = (CardSize + 1) * (1 << (MMC_CSD_GET_DEVICESIZEMULT (Response) + 2));\r
156 BlockSize = 1 << MMC_CSD_GET_READBLLEN (Response);\r
157 }\r
158\r
159 // For >=2G card, BlockSize may be 1K, but the transfer size is 512 bytes.\r
160 if (BlockSize > 512) {\r
161 NumBlocks = MultU64x32 (NumBlocks, BlockSize / 512);\r
162 BlockSize = 512;\r
163 }\r
164\r
165 MmcHostInstance->BlockIo.Media->LastBlock = (NumBlocks - 1);\r
166 MmcHostInstance->BlockIo.Media->BlockSize = BlockSize;\r
167 MmcHostInstance->BlockIo.Media->ReadOnly = MmcHost->IsReadOnly (MmcHost);\r
168 MmcHostInstance->BlockIo.Media->MediaPresent = TRUE;\r
169 MmcHostInstance->BlockIo.Media->MediaId++;\r
170\r
171 CmdArg = MmcHostInstance->CardInfo.RCA << 16;\r
172 Status = MmcHost->SendCommand (MmcHost, MMC_CMD7, CmdArg);\r
173 if (EFI_ERROR (Status)) {\r
174 DEBUG((EFI_D_ERROR, "InitializeSdMmcDevice(MMC_CMD7): Error and Status = %r\n", Status));\r
175 return Status;\r
176 }\r
177\r
178 return EFI_SUCCESS;\r
179}\r
180\r
181STATIC\r
182EFI_STATUS\r
183EFIAPI\r
184MmcIdentificationMode (\r
185 IN MMC_HOST_INSTANCE *MmcHostInstance\r
186 )\r
187{\r
188 EFI_STATUS Status;\r
189 UINT32 Response[4];\r
190 UINTN Timeout;\r
191 UINTN CmdArg;\r
192 BOOLEAN IsHCS;\r
193 EFI_MMC_HOST_PROTOCOL *MmcHost;\r
194 OCR_RESPONSE OcrResponse;\r
195\r
196 MmcHost = MmcHostInstance->MmcHost;\r
197 CmdArg = 0;\r
198 IsHCS = FALSE;\r
199\r
200 if (MmcHost == NULL) {\r
201 return EFI_INVALID_PARAMETER;\r
202 }\r
203\r
204 // We can get into this function if we restart the identification mode\r
205 if (MmcHostInstance->State == MmcHwInitializationState) {\r
206 // Initialize the MMC Host HW\r
207 Status = MmcNotifyState (MmcHostInstance, MmcHwInitializationState);\r
208 if (EFI_ERROR (Status)) {\r
209 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Error MmcHwInitializationState, Status=%r.\n", Status));\r
210 return Status;\r
211 }\r
212 }\r
213\r
214 Status = MmcHost->SendCommand (MmcHost, MMC_CMD0, 0);\r
215 if (EFI_ERROR (Status)) {\r
216 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD0): Error, Status=%r.\n", Status));\r
217 return Status;\r
218 }\r
219 Status = MmcNotifyState (MmcHostInstance, MmcIdleState);\r
220 if (EFI_ERROR (Status)) {\r
221 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Error MmcIdleState, Status=%r.\n", Status));\r
222 return Status;\r
223 }\r
224\r
225 // Send CMD1 to get OCR (SD / MMC)\r
226 Status = MmcHost->SendCommand (MmcHost, MMC_CMD1, EMMC_CMD1_CAPACITY_GREATER_THAN_2GB);\r
227 if (Status == EFI_SUCCESS) {\r
228 Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_OCR, (UINT32 *)&OcrResponse);\r
229 if (EFI_ERROR (Status)) {\r
230 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Failed to receive OCR, Status=%r.\n", Status));\r
231 return Status;\r
232 }\r
233\r
234 if (!OcrResponse.Ocr.PowerUp) {\r
235 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD1): Card initialisation failure, Status=%r.\n", Status));\r
236 return EFI_DEVICE_ERROR;\r
237 }\r
238 OcrResponse.Ocr.PowerUp = 0;\r
239 if (OcrResponse.Raw == EMMC_CMD1_CAPACITY_GREATER_THAN_2GB) {\r
240 MmcHostInstance->CardInfo.OCRData.AccessMode = BIT1;\r
241 }\r
242 else {\r
243 MmcHostInstance->CardInfo.OCRData.AccessMode = 0x0;\r
244 }\r
245 if (OcrResponse.Raw == EMMC_CMD1_CAPACITY_GREATER_THAN_2GB ||\r
246 OcrResponse.Raw == EMMC_CMD1_CAPACITY_LESS_THAN_2GB) {\r
247 return EmmcIdentificationMode (MmcHostInstance, OcrResponse);\r
248 }\r
249 } else {\r
250 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD1) : Failed to send command, Status=%r.\n", Status));\r
251 return Status;\r
252 }\r
253\r
254 // Are we using SDIO ?\r
255 Status = MmcHost->SendCommand (MmcHost, MMC_CMD5, 0);\r
256 if (Status == EFI_SUCCESS) {\r
257 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD5): Error - SDIO not supported, Status=%r.\n", Status));\r
258 return EFI_UNSUPPORTED;\r
259 }\r
260\r
261 // Check which kind of card we are using. Ver2.00 or later SD Memory Card (PL180 is SD v1.1)\r
262 CmdArg = (0x0UL << 12 | BIT8 | 0xCEUL << 0);\r
263 Status = MmcHost->SendCommand (MmcHost, MMC_CMD8, CmdArg);\r
264 if (Status == EFI_SUCCESS) {\r
265 DEBUG ((EFI_D_ERROR, "Card is SD2.0 => Supports high capacity\n"));\r
266 IsHCS = TRUE;\r
267 Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R7, Response);\r
268 if (EFI_ERROR (Status)) {\r
269 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Failed to receive response to CMD8, Status=%r.\n", Status));\r
270 return Status;\r
271 }\r
272 PrintResponseR1 (Response[0]);\r
273 // Check if it is valid response\r
274 if (Response[0] != CmdArg) {\r
275 DEBUG ((EFI_D_ERROR, "The Card is not usable\n"));\r
276 return EFI_UNSUPPORTED;\r
277 }\r
278 } else {\r
279 DEBUG ((EFI_D_ERROR, "Not a SD2.0 Card\n"));\r
280 }\r
281\r
282 // We need to wait for the MMC or SD card is ready => (gCardInfo.OCRData.PowerUp == 1)\r
283 Timeout = MAX_RETRY_COUNT;\r
284 while (Timeout > 0) {\r
285 // SD Card or MMC Card ? CMD55 indicates to the card that the next command is an application specific command\r
286 Status = MmcHost->SendCommand (MmcHost, MMC_CMD55, 0);\r
287 if (Status == EFI_SUCCESS) {\r
288 DEBUG ((EFI_D_INFO, "Card should be SD\n"));\r
289 if (IsHCS) {\r
290 MmcHostInstance->CardInfo.CardType = SD_CARD_2;\r
291 } else {\r
292 MmcHostInstance->CardInfo.CardType = SD_CARD;\r
293 }\r
294\r
295 // Note: The first time CmdArg will be zero\r
296 CmdArg = ((UINTN *) &(MmcHostInstance->CardInfo.OCRData))[0];\r
297 if (IsHCS) {\r
298 CmdArg |= BIT30;\r
299 }\r
300 Status = MmcHost->SendCommand (MmcHost, MMC_ACMD41, CmdArg);\r
301 if (!EFI_ERROR (Status)) {\r
302 Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_OCR, Response);\r
303 if (EFI_ERROR (Status)) {\r
304 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Failed to receive OCR, Status=%r.\n", Status));\r
305 return Status;\r
306 }\r
307 ((UINT32 *) &(MmcHostInstance->CardInfo.OCRData))[0] = Response[0];\r
308 }\r
309 } else {\r
310 DEBUG ((EFI_D_INFO, "Card should be MMC\n"));\r
311 MmcHostInstance->CardInfo.CardType = MMC_CARD;\r
312\r
313 Status = MmcHost->SendCommand (MmcHost, MMC_CMD1, 0x800000);\r
314 if (!EFI_ERROR (Status)) {\r
315 Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_OCR, Response);\r
316 if (EFI_ERROR (Status)) {\r
317 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Failed to receive OCR, Status=%r.\n", Status));\r
318 return Status;\r
319 }\r
320 ((UINT32 *) &(MmcHostInstance->CardInfo.OCRData))[0] = Response[0];\r
321 }\r
322 }\r
323\r
324 if (!EFI_ERROR (Status)) {\r
325 if (!MmcHostInstance->CardInfo.OCRData.PowerUp) {\r
326 MicroSecondDelay (1);\r
327 Timeout--;\r
328 } else {\r
329 if ((MmcHostInstance->CardInfo.CardType == SD_CARD_2) && (MmcHostInstance->CardInfo.OCRData.AccessMode & BIT1)) {\r
330 MmcHostInstance->CardInfo.CardType = SD_CARD_2_HIGH;\r
331 DEBUG ((EFI_D_ERROR, "High capacity card.\n"));\r
332 }\r
333 break; // The MMC/SD card is ready. Continue the Identification Mode\r
334 }\r
335 } else {\r
336 MicroSecondDelay (1);\r
337 Timeout--;\r
338 }\r
339 }\r
340\r
341 if (Timeout == 0) {\r
342 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode(): No Card\n"));\r
343 return EFI_NO_MEDIA;\r
344 } else {\r
345 PrintOCR (Response[0]);\r
346 }\r
347\r
348 Status = MmcNotifyState (MmcHostInstance, MmcReadyState);\r
349 if (EFI_ERROR (Status)) {\r
350 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Error MmcReadyState\n"));\r
351 return Status;\r
352 }\r
353\r
354 Status = MmcHost->SendCommand (MmcHost, MMC_CMD2, 0);\r
355 if (EFI_ERROR (Status)) {\r
356 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD2): Error\n"));\r
357 return Status;\r
358 }\r
359 Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_CID, Response);\r
360 if (EFI_ERROR (Status)) {\r
361 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Failed to receive CID, Status=%r.\n", Status));\r
362 return Status;\r
363 }\r
364\r
365 PrintCID (Response);\r
366\r
367 Status = MmcHost->NotifyState (MmcHost, MmcIdentificationState);\r
368 if (EFI_ERROR (Status)) {\r
369 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Error MmcIdentificationState\n"));\r
370 return Status;\r
371 }\r
372\r
373 //\r
374 // Note, SD specifications say that "if the command execution causes a state change, it\r
375 // will be visible to the host in the response to the next command"\r
376 // The status returned for this CMD3 will be 2 - identification\r
377 //\r
378 CmdArg = 1;\r
379 Status = MmcHost->SendCommand (MmcHost, MMC_CMD3, CmdArg);\r
380 if (EFI_ERROR (Status)) {\r
381 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode(MMC_CMD3): Error\n"));\r
382 return Status;\r
383 }\r
384\r
385 Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_RCA, Response);\r
386 if (EFI_ERROR (Status)) {\r
387 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Failed to receive RCA, Status=%r.\n", Status));\r
388 return Status;\r
389 }\r
390 PrintRCA (Response[0]);\r
391\r
392 // For MMC card, RCA is assigned by CMD3 while CMD3 dumps the RCA for SD card\r
393 if (MmcHostInstance->CardInfo.CardType != MMC_CARD) {\r
394 MmcHostInstance->CardInfo.RCA = Response[0] >> 16;\r
395 } else {\r
396 MmcHostInstance->CardInfo.RCA = CmdArg;\r
397 }\r
398 Status = MmcNotifyState (MmcHostInstance, MmcStandByState);\r
399 if (EFI_ERROR (Status)) {\r
400 DEBUG ((EFI_D_ERROR, "MmcIdentificationMode() : Error MmcStandByState\n"));\r
401 return Status;\r
402 }\r
403\r
404 return EFI_SUCCESS;\r
405}\r
406\r
407EFI_STATUS\r
408InitializeMmcDevice (\r
409 IN MMC_HOST_INSTANCE *MmcHostInstance\r
410 )\r
411{\r
412 EFI_STATUS Status;\r
413 EFI_MMC_HOST_PROTOCOL *MmcHost;\r
414 UINTN BlockCount;\r
415\r
416 BlockCount = 1;\r
417 MmcHost = MmcHostInstance->MmcHost;\r
418\r
419 Status = MmcIdentificationMode (MmcHostInstance);\r
420 if (EFI_ERROR (Status)) {\r
421 DEBUG((EFI_D_ERROR, "InitializeMmcDevice(): Error in Identification Mode, Status=%r\n", Status));\r
422 return Status;\r
423 }\r
424\r
425 Status = MmcNotifyState (MmcHostInstance, MmcTransferState);\r
426 if (EFI_ERROR (Status)) {\r
427 DEBUG((EFI_D_ERROR, "InitializeMmcDevice(): Error MmcTransferState, Status=%r\n", Status));\r
428 return Status;\r
429 }\r
430\r
431 if (MmcHostInstance->CardInfo.CardType != EMMC_CARD) {\r
432 Status = InitializeSdMmcDevice (MmcHostInstance);\r
433 if (EFI_ERROR (Status)) {\r
434 return Status;\r
435 }\r
436 }\r
437\r
438 // Set Block Length\r
439 Status = MmcHost->SendCommand (MmcHost, MMC_CMD16, MmcHostInstance->BlockIo.Media->BlockSize);\r
440 if (EFI_ERROR (Status)) {\r
441 DEBUG((EFI_D_ERROR, "InitializeMmcDevice(MMC_CMD16): Error MmcHostInstance->BlockIo.Media->BlockSize: %d and Error = %r\n",\r
442 MmcHostInstance->BlockIo.Media->BlockSize, Status));\r
443 return Status;\r
444 }\r
445\r
446 // Block Count (not used). Could return an error for SD card\r
447 if (MmcHostInstance->CardInfo.CardType == MMC_CARD) {\r
448 Status = MmcHost->SendCommand (MmcHost, MMC_CMD23, BlockCount);\r
449 if (EFI_ERROR (Status)) {\r
450 DEBUG((EFI_D_ERROR, "InitializeMmcDevice(MMC_CMD23): Error, Status=%r\n", Status));\r
451 return Status;\r
452 }\r
453 }\r
454\r
455 return EFI_SUCCESS;\r
456}\r