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