]> git.proxmox.com Git - mirror_edk2.git/blame - Omap35xxPkg/MMCHSDxe/MMCHS.c
Remove probe for removable media from FileLib, it was getting called way too much...
[mirror_edk2.git] / Omap35xxPkg / MMCHSDxe / MMCHS.c
CommitLineData
a3f98646 1/** @file
8c6151f2 2 MMC/SD Card driver for OMAP 35xx (SDIO not supported)
3
4 This driver always produces a BlockIo protocol but it starts off with no Media
5 present. A TimerCallBack detects when media is inserted or removed and after
6 a media change event a call to BlockIo ReadBlocks/WriteBlocks will cause the
7 media to be detected (or removed) and the BlockIo Media structure will get
8 updated. No MMC/SD Card harward registers are updated until the first BlockIo
9 ReadBlocks/WriteBlocks after media has been insterted (booting with a card
10 plugged in counts as an insertion event).
a3f98646 11
3d70643b 12 Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
a3f98646 13
3d70643b 14 This program and the accompanying materials
a3f98646 15 are licensed and made available under the terms and conditions of the BSD License
16 which accompanies this distribution. The full text of the license may be found at
17 http://opensource.org/licenses/bsd-license.php
18
19 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
20 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
21
22**/
23
24#include <Uefi.h>
25
26#include "MMCHS.h"
27
8c6151f2 28EFI_BLOCK_IO_MEDIA gMMCHSMedia = {
a3f98646 29 SIGNATURE_32('s','d','i','o'), // MediaId
30 TRUE, // RemovableMedia
8c6151f2 31 FALSE, // MediaPresent
a3f98646 32 FALSE, // LogicalPartition
33 FALSE, // ReadOnly
34 FALSE, // WriteCaching
35 512, // BlockSize
36 4, // IoAlign
37 0, // Pad
38 0 // LastBlock
39};
40
41typedef struct {
42 VENDOR_DEVICE_PATH Mmc;
43 EFI_DEVICE_PATH End;
44} MMCHS_DEVICE_PATH;
45
8c6151f2 46MMCHS_DEVICE_PATH gMmcHsDevicePath = {
a3f98646 47 {
48 HARDWARE_DEVICE_PATH,
49 HW_VENDOR_DP,
50 (UINT8)(sizeof(VENDOR_DEVICE_PATH)),
51 (UINT8)((sizeof(VENDOR_DEVICE_PATH)) >> 8),
52 0xb615f1f5, 0x5088, 0x43cd, 0x80, 0x9c, 0xa1, 0x6e, 0x52, 0x48, 0x7d, 0x00
53 },
54 {
55 END_DEVICE_PATH_TYPE,
56 END_ENTIRE_DEVICE_PATH_SUBTYPE,
57 sizeof (EFI_DEVICE_PATH_PROTOCOL),
58 0
59 }
60};
61
8c6151f2 62CARD_INFO gCardInfo;
a3f98646 63EMBEDDED_EXTERNAL_DEVICE *gTPS65950;
8c6151f2 64EFI_EVENT gTimerEvent;
65BOOLEAN gMediaChange = FALSE;
a3f98646 66
67//
68// Internal Functions
69//
70
8c6151f2 71
a3f98646 72VOID
73ParseCardCIDData (
74 UINT32 Response0,
75 UINT32 Response1,
76 UINT32 Response2,
77 UINT32 Response3
78 )
79{
8c6151f2 80 gCardInfo.CIDData.MDT = ((Response0 >> 8) & 0xFFF);
81 gCardInfo.CIDData.PSN = (((Response0 >> 24) & 0xFF) | ((Response1 & 0xFFFFFF) << 8));
82 gCardInfo.CIDData.PRV = ((Response1 >> 24) & 0xFF);
83 gCardInfo.CIDData.PNM[4] = ((Response2) & 0xFF);
84 gCardInfo.CIDData.PNM[3] = ((Response2 >> 8) & 0xFF);
85 gCardInfo.CIDData.PNM[2] = ((Response2 >> 16) & 0xFF);
86 gCardInfo.CIDData.PNM[1] = ((Response2 >> 24) & 0xFF);
87 gCardInfo.CIDData.PNM[0] = ((Response3) & 0xFF);
88 gCardInfo.CIDData.OID = ((Response3 >> 8) & 0xFFFF);
89 gCardInfo.CIDData.MID = ((Response3 >> 24) & 0xFF);
a3f98646 90}
91
8c6151f2 92
a3f98646 93VOID
94UpdateMMCHSClkFrequency (
95 UINTN NewCLKD
96 )
97{
98 //Set Clock enable to 0x0 to not provide the clock to the card
8c6151f2 99 MmioAnd32 (MMCHS_SYSCTL, ~CEN);
a3f98646 100
101 //Set new clock frequency.
8c6151f2 102 MmioAndThenOr32 (MMCHS_SYSCTL, ~CLKD_MASK, NewCLKD << 6);
a3f98646 103
104 //Poll till Internal Clock Stable
43263288 105 while ((MmioRead32 (MMCHS_SYSCTL) & ICS_MASK) != ICS);
a3f98646 106
107 //Set Clock enable to 0x1 to provide the clock to the card
43263288 108 MmioOr32 (MMCHS_SYSCTL, CEN);
a3f98646 109}
110
8c6151f2 111
a3f98646 112EFI_STATUS
113SendCmd (
114 UINTN Cmd,
115 UINTN CmdInterruptEnableVal,
116 UINTN CmdArgument
117 )
118{
119 UINTN MmcStatus;
120 UINTN RetryCount = 0;
121
122 //Check if command line is in use or not. Poll till command line is available.
43263288 123 while ((MmioRead32 (MMCHS_PSTATE) & DATI_MASK) == DATI_NOT_ALLOWED);
a3f98646 124
125 //Provide the block size.
026e30c4 126 MmioWrite32 (MMCHS_BLK, BLEN_512BYTES);
a3f98646 127
128 //Setting Data timeout counter value to max value.
8c6151f2 129 MmioAndThenOr32 (MMCHS_SYSCTL, ~DTO_MASK, DTO_VAL);
a3f98646 130
131 //Clear Status register.
026e30c4 132 MmioWrite32 (MMCHS_STAT, 0xFFFFFFFF);
a3f98646 133
134 //Set command argument register
026e30c4 135 MmioWrite32 (MMCHS_ARG, CmdArgument);
a3f98646 136
137 //Enable interrupt enable events to occur
026e30c4 138 MmioWrite32 (MMCHS_IE, CmdInterruptEnableVal);
a3f98646 139
140 //Send a command
026e30c4 141 MmioWrite32 (MMCHS_CMD, Cmd);
a3f98646 142
143 //Check for the command status.
144 while (RetryCount < MAX_RETRY_COUNT) {
145 do {
43263288 146 MmcStatus = MmioRead32 (MMCHS_STAT);
a3f98646 147 } while (MmcStatus == 0);
148
149 //Read status of command response
150 if ((MmcStatus & ERRI) != 0) {
151
152 //Perform soft-reset for mmci_cmd line.
43263288 153 MmioOr32 (MMCHS_SYSCTL, SRC);
154 while ((MmioRead32 (MMCHS_SYSCTL) & SRC));
a3f98646 155
156 DEBUG ((EFI_D_INFO, "MmcStatus: %x\n", MmcStatus));
157 return EFI_DEVICE_ERROR;
158 }
159
160 //Check if command is completed.
161 if ((MmcStatus & CC) == CC) {
026e30c4 162 MmioWrite32 (MMCHS_STAT, CC);
a3f98646 163 break;
164 }
165
166 RetryCount++;
167 }
168
169 if (RetryCount == MAX_RETRY_COUNT) {
170 return EFI_TIMEOUT;
171 }
172
173 return EFI_SUCCESS;
174}
175
8c6151f2 176
a3f98646 177VOID
178GetBlockInformation (
179 UINTN *BlockSize,
180 UINTN *NumBlocks
181 )
182{
183 CSD_SDV2 *CsdSDV2Data;
184 UINTN CardSize;
185
8c6151f2 186 if (gCardInfo.CardType == SD_CARD_2_HIGH) {
187 CsdSDV2Data = (CSD_SDV2 *)&gCardInfo.CSDData;
a3f98646 188
189 //Populate BlockSize.
190 *BlockSize = (0x1UL << CsdSDV2Data->READ_BL_LEN);
191
192 //Calculate Total number of blocks.
193 CardSize = CsdSDV2Data->C_SIZELow16 | (CsdSDV2Data->C_SIZEHigh6 << 2);
194 *NumBlocks = ((CardSize + 1) * 1024);
195 } else {
196 //Populate BlockSize.
8c6151f2 197 *BlockSize = (0x1UL << gCardInfo.CSDData.READ_BL_LEN);
a3f98646 198
199 //Calculate Total number of blocks.
8c6151f2 200 CardSize = gCardInfo.CSDData.C_SIZELow2 | (gCardInfo.CSDData.C_SIZEHigh10 << 2);
201 *NumBlocks = (CardSize + 1) * (1 << (gCardInfo.CSDData.C_SIZE_MULT + 2));
a3f98646 202 }
203
204 //For >=2G card, BlockSize may be 1K, but the transfer size is 512 bytes.
205 if (*BlockSize > 512) {
206 *NumBlocks = MultU64x32(*NumBlocks, *BlockSize/2);
207 *BlockSize = 512;
208 }
209
8c6151f2 210 DEBUG ((EFI_D_INFO, "Card type: %x, BlockSize: %x, NumBlocks: %x\n", gCardInfo.CardType, *BlockSize, *NumBlocks));
a3f98646 211}
212
8c6151f2 213
a3f98646 214VOID
215CalculateCardCLKD (
216 UINTN *ClockFrequencySelect
217 )
218{
219 UINT8 MaxDataTransferRate;
220 UINTN TransferRateValue = 0;
221 UINTN TimeValue = 0 ;
222 UINTN Frequency = 0;
223
8c6151f2 224 MaxDataTransferRate = gCardInfo.CSDData.TRAN_SPEED;
a3f98646 225
226 //Calculate Transfer rate unit (Bits 2:0 of TRAN_SPEED)
227 switch (MaxDataTransferRate & 0x7) {
228 case 0:
229 TransferRateValue = 100 * 1000;
230 break;
231
232 case 1:
233 TransferRateValue = 1 * 1000 * 1000;
234 break;
235
236 case 2:
237 TransferRateValue = 10 * 1000 * 1000;
238 break;
239
240 case 3:
241 TransferRateValue = 100 * 1000 * 1000;
242 break;
243
244 default:
245 DEBUG((EFI_D_ERROR, "Invalid parameter.\n"));
246 ASSERT(FALSE);
247 }
248
249 //Calculate Time value (Bits 6:3 of TRAN_SPEED)
250 switch ((MaxDataTransferRate >> 3) & 0xF) {
251 case 1:
252 TimeValue = 10;
253 break;
254
255 case 2:
256 TimeValue = 12;
257 break;
258
259 case 3:
260 TimeValue = 13;
261 break;
262
263 case 4:
264 TimeValue = 15;
265 break;
266
267 case 5:
268 TimeValue = 20;
269 break;
270
271 case 6:
272 TimeValue = 25;
273 break;
274
275 case 7:
276 TimeValue = 30;
277 break;
278
279 case 8:
280 TimeValue = 35;
281 break;
282
283 case 9:
284 TimeValue = 40;
285 break;
286
287 case 10:
288 TimeValue = 45;
289 break;
290
291 case 11:
292 TimeValue = 50;
293 break;
294
295 case 12:
296 TimeValue = 55;
297 break;
298
299 case 13:
300 TimeValue = 60;
301 break;
302
303 case 14:
304 TimeValue = 70;
305 break;
306
307 case 15:
308 TimeValue = 80;
309 break;
310
311 default:
312 DEBUG((EFI_D_ERROR, "Invalid parameter.\n"));
313 ASSERT(FALSE);
314 }
315
316 Frequency = TransferRateValue * TimeValue/10;
317
318 //Calculate Clock divider value to program in MMCHS_SYSCTL[CLKD] field.
319 *ClockFrequencySelect = ((MMC_REFERENCE_CLK/Frequency) + 1);
320
321 DEBUG ((EFI_D_INFO, "MaxDataTransferRate: 0x%x, Frequency: %d KHz, ClockFrequencySelect: %x\n", MaxDataTransferRate, Frequency/1000, *ClockFrequencySelect));
322}
323
8c6151f2 324
a3f98646 325VOID
326GetCardConfigurationData (
327 VOID
328 )
329{
330 UINTN BlockSize;
331 UINTN NumBlocks;
332 UINTN ClockFrequencySelect;
333
334 //Calculate BlockSize and Total number of blocks in the detected card.
335 GetBlockInformation(&BlockSize, &NumBlocks);
8c6151f2 336 gCardInfo.BlockSize = BlockSize;
337 gCardInfo.NumBlocks = NumBlocks;
a3f98646 338
339 //Calculate Card clock divider value.
340 CalculateCardCLKD(&ClockFrequencySelect);
8c6151f2 341 gCardInfo.ClockFrequencySelect = ClockFrequencySelect;
a3f98646 342}
343
8c6151f2 344
a3f98646 345EFI_STATUS
346InitializeMMCHS (
347 VOID
348 )
349{
350 UINT8 Data = 0;
351 EFI_STATUS Status;
352
353 //Select Device group to belong to P1 device group in Power IC.
354 Data = DEV_GRP_P1;
8c6151f2 355 Status = gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, VMMC1_DEV_GRP), 1, &Data);
a3f98646 356 ASSERT_EFI_ERROR(Status);
357
358 //Configure voltage regulator for MMC1 in Power IC to output 3.0 voltage.
359 Data = VSEL_3_00V;
8c6151f2 360 Status = gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, VMMC1_DEDICATED_REG), 1, &Data);
a3f98646 361 ASSERT_EFI_ERROR(Status);
362
363 //After ramping up voltage, set VDDS stable bit to indicate that voltage level is stable.
43263288 364 MmioOr32 (CONTROL_PBIAS_LITE, (PBIASLITEVMODE0 | PBIASLITEPWRDNZ0 | PBIASSPEEDCTRL0 | PBIASLITEVMODE1 | PBIASLITEWRDNZ1));
a3f98646 365
8c6151f2 366 // Enable WP GPIO
367 MmioAndThenOr32 (GPIO1_BASE + GPIO_OE, ~BIT23, BIT23);
a3f98646 368
8c6151f2 369 // Enable Card Detect
370 Data = CARD_DETECT_ENABLE;
371 gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID2, TPS65950_GPIO_CTRL), 1, &Data);
a3f98646 372
a3f98646 373
374 return Status;
375}
376
8c6151f2 377
a3f98646 378EFI_STATUS
379PerformCardIdenfication (
380 VOID
381 )
382{
383 EFI_STATUS Status;
384 UINTN CmdArgument = 0;
385 UINTN Response = 0;
386 UINTN RetryCount = 0;
387 BOOLEAN SDCmd8Supported = FALSE;
388
389 //Enable interrupts.
8c6151f2 390 MmioWrite32 (MMCHS_IE, (BADA_EN | CERR_EN | DEB_EN | DCRC_EN | DTO_EN | CIE_EN |
a3f98646 391 CEB_EN | CCRC_EN | CTO_EN | BRR_EN | BWR_EN | TC_EN | CC_EN));
392
393 //Controller INIT procedure start.
43263288 394 MmioOr32 (MMCHS_CON, INIT);
026e30c4 395 MmioWrite32 (MMCHS_CMD, 0x00000000);
43263288 396 while (!(MmioRead32 (MMCHS_STAT) & CC));
a3f98646 397
398 //Wait for 1 ms
399 gBS->Stall(1000);
400
401 //Set CC bit to 0x1 to clear the flag
43263288 402 MmioOr32 (MMCHS_STAT, CC);
a3f98646 403
404 //Retry INIT procedure.
026e30c4 405 MmioWrite32 (MMCHS_CMD, 0x00000000);
43263288 406 while (!(MmioRead32 (MMCHS_STAT) & CC));
a3f98646 407
408 //End initialization sequence
8c6151f2 409 MmioAnd32 (MMCHS_CON, ~INIT);
a3f98646 410
43263288 411 MmioOr32 (MMCHS_HCTL, (SDVS_3_0_V | DTW_1_BIT | SDBP_ON));
a3f98646 412
413 //Change clock frequency to 400KHz to fit protocol
414 UpdateMMCHSClkFrequency(CLKD_400KHZ);
415
43263288 416 MmioOr32 (MMCHS_CON, OD);
a3f98646 417
418 //Send CMD0 command.
8c6151f2 419 Status = SendCmd (CMD0, CMD0_INT_EN, CmdArgument);
a3f98646 420 if (EFI_ERROR(Status)) {
421 DEBUG ((EFI_D_ERROR, "Cmd0 fails.\n"));
422 return Status;
423 }
424
43263288 425 DEBUG ((EFI_D_INFO, "CMD0 response: %x\n", MmioRead32 (MMCHS_RSP10)));
a3f98646 426
427 //Send CMD5 command.
8c6151f2 428 Status = SendCmd (CMD5, CMD5_INT_EN, CmdArgument);
a3f98646 429 if (Status == EFI_SUCCESS) {
430 DEBUG ((EFI_D_ERROR, "CMD5 Success. SDIO card. Follow SDIO card specification.\n"));
43263288 431 DEBUG ((EFI_D_INFO, "CMD5 response: %x\n", MmioRead32 (MMCHS_RSP10)));
a3f98646 432 //NOTE: Returning unsupported error for now. Need to implement SDIO specification.
433 return EFI_UNSUPPORTED;
434 } else {
435 DEBUG ((EFI_D_INFO, "CMD5 fails. Not an SDIO card.\n"));
436 }
437
43263288 438 MmioOr32 (MMCHS_SYSCTL, SRC);
a3f98646 439 gBS->Stall(1000);
43263288 440 while ((MmioRead32 (MMCHS_SYSCTL) & SRC));
a3f98646 441
442 //Send CMD8 command. (New v2.00 command for Voltage check)
443 //Only 2.7V - 3.6V is supported for SD2.0, only SD 2.0 card can pass.
444 //MMC & SD1.1 card will fail this command.
445 CmdArgument = CMD8_ARG;
8c6151f2 446 Status = SendCmd (CMD8, CMD8_INT_EN, CmdArgument);
a3f98646 447 if (Status == EFI_SUCCESS) {
43263288 448 Response = MmioRead32 (MMCHS_RSP10);
a3f98646 449 DEBUG ((EFI_D_INFO, "CMD8 success. CMD8 response: %x\n", Response));
450 if (Response != CmdArgument) {
451 return EFI_DEVICE_ERROR;
452 }
453 DEBUG ((EFI_D_INFO, "Card is SD2.0\n"));
454 SDCmd8Supported = TRUE; //Supports high capacity.
455 } else {
456 DEBUG ((EFI_D_INFO, "CMD8 fails. Not an SD2.0 card.\n"));
457 }
458
43263288 459 MmioOr32 (MMCHS_SYSCTL, SRC);
a3f98646 460 gBS->Stall(1000);
43263288 461 while ((MmioRead32 (MMCHS_SYSCTL) & SRC));
a3f98646 462
463 //Poll till card is busy
464 while (RetryCount < MAX_RETRY_COUNT) {
465 //Send CMD55 command.
466 CmdArgument = 0;
8c6151f2 467 Status = SendCmd (CMD55, CMD55_INT_EN, CmdArgument);
a3f98646 468 if (Status == EFI_SUCCESS) {
43263288 469 DEBUG ((EFI_D_INFO, "CMD55 success. CMD55 response: %x\n", MmioRead32 (MMCHS_RSP10)));
8c6151f2 470 gCardInfo.CardType = SD_CARD;
a3f98646 471 } else {
472 DEBUG ((EFI_D_INFO, "CMD55 fails.\n"));
8c6151f2 473 gCardInfo.CardType = MMC_CARD;
a3f98646 474 }
475
476 //Send appropriate command for the card type which got detected.
8c6151f2 477 if (gCardInfo.CardType == SD_CARD) {
478 CmdArgument = ((UINTN *) &(gCardInfo.OCRData))[0];
a3f98646 479
480 //Set HCS bit.
481 if (SDCmd8Supported) {
482 CmdArgument |= HCS;
483 }
484
8c6151f2 485 Status = SendCmd (ACMD41, ACMD41_INT_EN, CmdArgument);
a3f98646 486 if (EFI_ERROR(Status)) {
487 DEBUG ((EFI_D_INFO, "ACMD41 fails.\n"));
488 return Status;
489 }
8c6151f2 490 ((UINT32 *) &(gCardInfo.OCRData))[0] = MmioRead32 (MMCHS_RSP10);
491 DEBUG ((EFI_D_INFO, "SD card detected. ACMD41 OCR: %x\n", ((UINT32 *) &(gCardInfo.OCRData))[0]));
492 } else if (gCardInfo.CardType == MMC_CARD) {
a3f98646 493 CmdArgument = 0;
8c6151f2 494 Status = SendCmd (CMD1, CMD1_INT_EN, CmdArgument);
a3f98646 495 if (EFI_ERROR(Status)) {
496 DEBUG ((EFI_D_INFO, "CMD1 fails.\n"));
497 return Status;
498 }
43263288 499 Response = MmioRead32 (MMCHS_RSP10);
a3f98646 500 DEBUG ((EFI_D_INFO, "MMC card detected.. CMD1 response: %x\n", Response));
501
502 //NOTE: For now, I am skipping this since I only have an SD card.
503 //Compare card OCR and host OCR (Section 22.6.1.3.2.4)
504 return EFI_UNSUPPORTED; //For now, MMC is not supported.
505 }
506
507 //Poll the card until it is out of its power-up sequence.
8c6151f2 508 if (gCardInfo.OCRData.Busy == 1) {
a3f98646 509
510 if (SDCmd8Supported) {
8c6151f2 511 gCardInfo.CardType = SD_CARD_2;
a3f98646 512 }
513
514 //Card is ready. Check CCS (Card capacity status) bit (bit#30).
515 //SD 2.0 standard card will response with CCS 0, SD high capacity card will respond with CCS 1.
8c6151f2 516 if (gCardInfo.OCRData.AccessMode & BIT1) {
517 gCardInfo.CardType = SD_CARD_2_HIGH;
a3f98646 518 DEBUG ((EFI_D_INFO, "High capacity card.\n"));
519 } else {
520 DEBUG ((EFI_D_INFO, "Standard capacity card.\n"));
521 }
522
523 break;
524 }
525
526 gBS->Stall(1000);
527 RetryCount++;
528 }
529
530 if (RetryCount == MAX_RETRY_COUNT) {
531 DEBUG ((EFI_D_ERROR, "Timeout error. RetryCount: %d\n", RetryCount));
532 return EFI_TIMEOUT;
533 }
534
535 //Read CID data.
536 CmdArgument = 0;
8c6151f2 537 Status = SendCmd (CMD2, CMD2_INT_EN, CmdArgument);
a3f98646 538 if (EFI_ERROR(Status)) {
539 DEBUG ((EFI_D_ERROR, "CMD2 fails. Status: %x\n", Status));
540 return Status;
541 }
542
43263288 543 DEBUG ((EFI_D_INFO, "CMD2 response: %x %x %x %x\n", MmioRead32 (MMCHS_RSP10), MmioRead32 (MMCHS_RSP32), MmioRead32 (MMCHS_RSP54), MmioRead32 (MMCHS_RSP76)));
a3f98646 544
545 //Parse CID register data.
43263288 546 ParseCardCIDData(MmioRead32 (MMCHS_RSP10), MmioRead32 (MMCHS_RSP32), MmioRead32 (MMCHS_RSP54), MmioRead32 (MMCHS_RSP76));
a3f98646 547
548 //Read RCA
549 CmdArgument = 0;
8c6151f2 550 Status = SendCmd (CMD3, CMD3_INT_EN, CmdArgument);
a3f98646 551 if (EFI_ERROR(Status)) {
552 DEBUG ((EFI_D_ERROR, "CMD3 fails. Status: %x\n", Status));
553 return Status;
554 }
555
556 //Set RCA for the detected card. RCA is CMD3 response.
8c6151f2 557 gCardInfo.RCA = (MmioRead32 (MMCHS_RSP10) >> 16);
558 DEBUG ((EFI_D_INFO, "CMD3 response: RCA %x\n", gCardInfo.RCA));
a3f98646 559
560 //MMC Bus setting change after card identification.
8c6151f2 561 MmioAnd32 (MMCHS_CON, ~OD);
43263288 562 MmioOr32 (MMCHS_HCTL, SDVS_3_0_V);
a3f98646 563 UpdateMMCHSClkFrequency(CLKD_400KHZ); //Set the clock frequency to 400KHz.
564
565 return EFI_SUCCESS;
566}
567
8c6151f2 568
a3f98646 569EFI_STATUS
570GetCardSpecificData (
571 VOID
572 )
573{
574 EFI_STATUS Status;
575 UINTN CmdArgument;
576
577 //Send CMD9 to retrieve CSD.
8c6151f2 578 CmdArgument = gCardInfo.RCA << 16;
579 Status = SendCmd (CMD9, CMD9_INT_EN, CmdArgument);
a3f98646 580 if (EFI_ERROR(Status)) {
581 DEBUG ((EFI_D_ERROR, "CMD9 fails. Status: %x\n", Status));
582 return Status;
583 }
584
585 //Populate 128-bit CSD register data.
8c6151f2 586 ((UINT32 *)&(gCardInfo.CSDData))[0] = MmioRead32 (MMCHS_RSP10);
587 ((UINT32 *)&(gCardInfo.CSDData))[1] = MmioRead32 (MMCHS_RSP32);
588 ((UINT32 *)&(gCardInfo.CSDData))[2] = MmioRead32 (MMCHS_RSP54);
589 ((UINT32 *)&(gCardInfo.CSDData))[3] = MmioRead32 (MMCHS_RSP76);
a3f98646 590
43263288 591 DEBUG ((EFI_D_INFO, "CMD9 response: %x %x %x %x\n", MmioRead32 (MMCHS_RSP10), MmioRead32 (MMCHS_RSP32), MmioRead32 (MMCHS_RSP54), MmioRead32 (MMCHS_RSP76)));
a3f98646 592
593 //Calculate total number of blocks and max. data transfer rate supported by the detected card.
594 GetCardConfigurationData();
595
596 //Change MMCHS clock frequency to what detected card can support.
8c6151f2 597 UpdateMMCHSClkFrequency(gCardInfo.ClockFrequencySelect);
a3f98646 598
599 return Status;
600}
601
8c6151f2 602
a3f98646 603EFI_STATUS
604PerformCardConfiguration (
605 VOID
606 )
607{
608 UINTN CmdArgument = 0;
609 EFI_STATUS Status;
610
611 //Send CMD7
8c6151f2 612 CmdArgument = gCardInfo.RCA << 16;
613 Status = SendCmd (CMD7, CMD7_INT_EN, CmdArgument);
a3f98646 614 if (EFI_ERROR(Status)) {
615 DEBUG ((EFI_D_ERROR, "CMD7 fails. Status: %x\n", Status));
616 return Status;
617 }
618
619 //Send CMD16 to set the block length
8c6151f2 620 CmdArgument = gCardInfo.BlockSize;
621 Status = SendCmd (CMD16, CMD16_INT_EN, CmdArgument);
a3f98646 622 if (EFI_ERROR(Status)) {
623 DEBUG ((EFI_D_ERROR, "CMD16 fails. Status: %x\n", Status));
624 return Status;
625 }
626
627 return EFI_SUCCESS;
628}
629
8c6151f2 630
a3f98646 631EFI_STATUS
8c6151f2 632ReadBlockData (
a3f98646 633 IN EFI_BLOCK_IO_PROTOCOL *This,
634 OUT VOID *Buffer
635 )
636{
637 UINTN MmcStatus;
638 UINTN *DataBuffer = Buffer;
639 UINTN DataSize = This->Media->BlockSize/4;
640 UINTN Count;
641 UINTN RetryCount = 0;
642
643 //Check controller status to make sure there is no error.
644 while (RetryCount < MAX_RETRY_COUNT) {
645 do {
646 //Read Status.
43263288 647 MmcStatus = MmioRead32 (MMCHS_STAT);
a3f98646 648 } while(MmcStatus == 0);
649
650 //Check if Buffer read ready (BRR) bit is set?
651 if (MmcStatus & BRR) {
652
653 //Clear BRR bit
43263288 654 MmioOr32 (MMCHS_STAT, BRR);
a3f98646 655
656 //Read block worth of data.
657 for (Count = 0; Count < DataSize; Count++) {
43263288 658 *DataBuffer++ = MmioRead32 (MMCHS_DATA);
a3f98646 659 }
660 break;
661 }
662 RetryCount++;
663 }
664
665 if (RetryCount == MAX_RETRY_COUNT) {
666 return EFI_TIMEOUT;
667 }
668
669 return EFI_SUCCESS;
670}
671
8c6151f2 672
a3f98646 673EFI_STATUS
8c6151f2 674WriteBlockData (
a3f98646 675 IN EFI_BLOCK_IO_PROTOCOL *This,
676 OUT VOID *Buffer
677 )
678{
679 UINTN MmcStatus;
680 UINTN *DataBuffer = Buffer;
681 UINTN DataSize = This->Media->BlockSize/4;
682 UINTN Count;
683 UINTN RetryCount = 0;
684
685 //Check controller status to make sure there is no error.
686 while (RetryCount < MAX_RETRY_COUNT) {
687 do {
688 //Read Status.
43263288 689 MmcStatus = MmioRead32 (MMCHS_STAT);
a3f98646 690 } while(MmcStatus == 0);
691
692 //Check if Buffer write ready (BWR) bit is set?
693 if (MmcStatus & BWR) {
694
695 //Clear BWR bit
43263288 696 MmioOr32 (MMCHS_STAT, BWR);
a3f98646 697
698 //Write block worth of data.
699 for (Count = 0; Count < DataSize; Count++) {
026e30c4 700 MmioWrite32 (MMCHS_DATA, *DataBuffer++);
a3f98646 701 }
702
703 break;
704 }
705 RetryCount++;
706 }
707
708 if (RetryCount == MAX_RETRY_COUNT) {
709 return EFI_TIMEOUT;
710 }
711
712 return EFI_SUCCESS;
713}
714
8c6151f2 715
a3f98646 716EFI_STATUS
8c6151f2 717TransferBlockData (
a3f98646 718 IN EFI_BLOCK_IO_PROTOCOL *This,
719 OUT VOID *Buffer,
720 IN OPERATION_TYPE OperationType
721 )
722{
723 EFI_STATUS Status;
724 UINTN MmcStatus;
725 UINTN RetryCount = 0;
726
727 //Read or Write data.
728 if (OperationType == READ) {
729 Status = ReadBlockData(This, Buffer);
730 if (EFI_ERROR(Status)) {
731 DEBUG((EFI_D_ERROR, "ReadBlockData fails.\n"));
732 return Status;
733 }
734 } else if (OperationType == WRITE) {
735 Status = WriteBlockData(This, Buffer);
736 if (EFI_ERROR(Status)) {
737 DEBUG((EFI_D_ERROR, "WriteBlockData fails.\n"));
738 return Status;
739 }
740 }
741
742 //Check for the Transfer completion.
743 while (RetryCount < MAX_RETRY_COUNT) {
744 //Read Status
745 do {
43263288 746 MmcStatus = MmioRead32 (MMCHS_STAT);
a3f98646 747 } while (MmcStatus == 0);
748
749 //Check if Transfer complete (TC) bit is set?
750 if (MmcStatus & TC) {
751 break;
752 } else {
753 DEBUG ((EFI_D_ERROR, "MmcStatus for TC: %x\n", MmcStatus));
754 //Check if DEB, DCRC or DTO interrupt occured.
755 if ((MmcStatus & DEB) | (MmcStatus & DCRC) | (MmcStatus & DTO)) {
756 //There was an error during the data transfer.
757
758 //Set SRD bit to 1 and wait until it return to 0x0.
43263288 759 MmioOr32 (MMCHS_SYSCTL, SRD);
760 while((MmioRead32 (MMCHS_SYSCTL) & SRD) != 0x0);
a3f98646 761
762 return EFI_DEVICE_ERROR;
763 }
764 }
765 RetryCount++;
766 }
767
768 if (RetryCount == MAX_RETRY_COUNT) {
769 DEBUG ((EFI_D_ERROR, "TransferBlockData timed out.\n"));
770 return EFI_TIMEOUT;
771 }
772
773 return EFI_SUCCESS;
774}
775
8c6151f2 776BOOLEAN
777CardPresent (
778 VOID
779 )
780{
781 EFI_STATUS Status;
782 UINT8 Data;
783
784 //
785 // Card detect is a GPIO0 on the TPS65950
786 //
787 Status = gTPS65950->Read (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID2, GPIODATAIN1), 1, &Data);
788 if (EFI_ERROR (Status)) {
789 return FALSE;
790 }
791
792 if ((Data & CARD_DETECT_BIT) == CARD_DETECT_BIT) {
793 // No Card present
794 return FALSE;
795 } else {
796 return TRUE;
797 }
798}
799
800EFI_STATUS
801DetectCard (
802 VOID
803 )
804{
805 EFI_STATUS Status;
806
807 if (!CardPresent ()) {
808 return EFI_NO_MEDIA;
809 }
810
811 //Initialize MMC host controller clocks.
812 Status = InitializeMMCHS ();
813 if (EFI_ERROR(Status)) {
814 DEBUG ((EFI_D_ERROR, "Initialize MMC host controller fails. Status: %x\n", Status));
815 return Status;
816 }
817
818 //Software reset of the MMCHS host controller.
819 MmioWrite32 (MMCHS_SYSCONFIG, SOFTRESET);
820 gBS->Stall(1000);
821 while ((MmioRead32 (MMCHS_SYSSTATUS) & RESETDONE_MASK) != RESETDONE);
822
823 //Soft reset for all.
824 MmioWrite32 (MMCHS_SYSCTL, SRA);
825 gBS->Stall(1000);
826 while ((MmioRead32 (MMCHS_SYSCTL) & SRA) != 0x0);
827
828 //Voltage capabilities initialization. Activate VS18 and VS30.
829 MmioOr32 (MMCHS_CAPA, (VS30 | VS18));
830
831 //Wakeup configuration
832 MmioOr32 (MMCHS_SYSCONFIG, ENAWAKEUP);
833 MmioOr32 (MMCHS_HCTL, IWE);
834
835 //MMCHS Controller default initialization
836 MmioOr32 (MMCHS_CON, (OD | DW8_1_4_BIT | CEATA_OFF));
837
838 MmioWrite32 (MMCHS_HCTL, (SDVS_3_0_V | DTW_1_BIT | SDBP_OFF));
839
840 //Enable internal clock
841 MmioOr32 (MMCHS_SYSCTL, ICE);
842
843 //Set the clock frequency to 80KHz.
844 UpdateMMCHSClkFrequency (CLKD_80KHZ);
845
846 //Enable SD bus power.
847 MmioOr32 (MMCHS_HCTL, (SDBP_ON));
848
849 //Poll till SD bus power bit is set.
850 while ((MmioRead32 (MMCHS_HCTL) & SDBP_MASK) != SDBP_ON);
851
852 //Card idenfication
853 Status = PerformCardIdenfication ();
854 if (EFI_ERROR(Status)) {
855 DEBUG ((EFI_D_ERROR, "No MMC/SD card detected.\n"));
856 return Status;
857 }
858
859 //Get CSD (Card specific data) for the detected card.
860 Status = GetCardSpecificData();
861 if (EFI_ERROR(Status)) {
862 return Status;
863 }
864
865 //Configure the card in data transfer mode.
866 Status = PerformCardConfiguration();
867 if (EFI_ERROR(Status)) {
868 return Status;
869 }
870
871 //Patch the Media structure.
872 gMMCHSMedia.LastBlock = (gCardInfo.NumBlocks - 1);
873 gMMCHSMedia.BlockSize = gCardInfo.BlockSize;
874 gMMCHSMedia.ReadOnly = (MmioRead32 (GPIO1_BASE + GPIO_DATAIN) & BIT23) == BIT23;
875 gMMCHSMedia.MediaPresent = TRUE;
876 gMMCHSMedia.MediaId++;
877 gMediaChange = FALSE;
878
879 return Status;
880}
881
882
a3f98646 883EFI_STATUS
884SdReadWrite (
885 IN EFI_BLOCK_IO_PROTOCOL *This,
886 IN UINTN Lba,
887 OUT VOID *Buffer,
888 IN UINTN BufferSize,
889 IN OPERATION_TYPE OperationType
890 )
891{
8c6151f2 892 EFI_STATUS Status = EFI_SUCCESS;
a3f98646 893 UINTN RetryCount = 0;
894 UINTN NumBlocks;
895 UINTN Cmd = 0;
896 UINTN CmdInterruptEnable = 0;
897 UINTN CmdArgument = 0;
8c6151f2 898 EFI_TPL OldTpl;\r
899 BOOLEAN MediaPresentLastTime;\r
900 BOOLEAN Update;\r
901
902 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
903
904
905 if (Buffer == NULL) {
906 Status = EFI_INVALID_PARAMETER;
907 goto Done;
908 }
909
910 Update = FALSE;
911 MediaPresentLastTime = gMMCHSMedia.MediaPresent;
912
913 if (gMediaChange) {
914 Status = DetectCard ();
915 if (EFI_ERROR (Status)) {
916 // We detected a removal
917 gMMCHSMedia.MediaPresent = FALSE;
918 gMMCHSMedia.LastBlock = 0;
919 gMMCHSMedia.BlockSize = 512; // Should be zero but there is a bug in DiskIo
920 gMMCHSMedia.ReadOnly = FALSE;
921 } else {
922 Update = TRUE;
923 }
924 gMediaChange = FALSE;
925 } else if (!gMMCHSMedia.MediaPresent) {
926 Status = EFI_NO_MEDIA;
927 goto Done;
928 }
929
930 if ((MediaPresentLastTime != gMMCHSMedia.MediaPresent) || Update) {
931 gBS->ReinstallProtocolInterface (\r
932 gImageHandle,\r
933 &gEfiBlockIoProtocolGuid,\r
934 &gBlockIo,\r
935 &gBlockIo\r
936 );\r
937 }
938
939 if (EFI_ERROR (Status)) {
940 goto Done;
941 }
942
943 if (Lba > This->Media->LastBlock) {
944 Status = EFI_INVALID_PARAMETER;
945 goto Done;
946 }
947
948 if ((BufferSize % This->Media->BlockSize) != 0) {
949 Status = EFI_BAD_BUFFER_SIZE;
950 goto Done;
951 }
a3f98646 952
953 //Check if the data lines are not in use.
43263288 954 while ((RetryCount++ < MAX_RETRY_COUNT) && ((MmioRead32 (MMCHS_PSTATE) & DATI_MASK) != DATI_ALLOWED));
a3f98646 955 if (RetryCount == MAX_RETRY_COUNT) {
8c6151f2 956 Status = EFI_TIMEOUT;
957 goto Done;
a3f98646 958 }
959
960 //Populate the command information based on the operation type.
961 if (OperationType == READ) {
962 Cmd = CMD17; //Single block read
963 CmdInterruptEnable = CMD17_INT_EN;
8c6151f2 964 } else if (OperationType == WRITE) {
a3f98646 965 Cmd = CMD24; //Single block write
966 CmdInterruptEnable = CMD24_INT_EN;
967 }
968
969 //Calculate total number of blocks its going to read.
970 NumBlocks = (BufferSize + (This->Media->BlockSize - 1))/This->Media->BlockSize;
971
972 //Set command argument based on the card access mode (Byte mode or Block mode)
8c6151f2 973 if (gCardInfo.OCRData.AccessMode & BIT1) {
a3f98646 974 CmdArgument = (UINTN)Lba;
975 } else {
976 CmdArgument = (UINTN)Lba * This->Media->BlockSize;
977 }
978
979 while(NumBlocks) {
980 //Send Command.
8c6151f2 981 Status = SendCmd (Cmd, CmdInterruptEnable, CmdArgument);
a3f98646 982 if (EFI_ERROR(Status)) {
983 DEBUG ((EFI_D_ERROR, "CMD fails. Status: %x\n", Status));
8c6151f2 984 goto Done;
a3f98646 985 }
986
987 //Transfer a block worth of data.
988 Status = TransferBlockData(This, Buffer, OperationType);
989 if (EFI_ERROR(Status)) {
990 DEBUG ((EFI_D_ERROR, "TransferBlockData fails. %x\n", Status));
8c6151f2 991 goto Done;
a3f98646 992 }
993
994 //Adjust command argument.
8c6151f2 995 if (gCardInfo.OCRData.AccessMode & BIT1) {
a3f98646 996 CmdArgument++; //Increase BlockIndex by one.
997 } else {
998 CmdArgument += This->Media->BlockSize; //Increase BlockIndex by BlockSize
999 }
1000
1001 //Adjust Buffer.
1002 Buffer = (UINT8 *)Buffer + This->Media->BlockSize;
1003 NumBlocks--;
1004 }
1005
8c6151f2 1006Done:\r
1007 gBS->RestoreTPL (OldTpl);\r
1008 return Status;\r
a3f98646 1009}
1010
8c6151f2 1011
1012/**\r
1013 Reset the Block Device.\r
1014\r
1015 @param This Indicates a pointer to the calling context.\r
1016 @param ExtendedVerification Driver may perform diagnostics on reset.\r
1017\r
1018 @retval EFI_SUCCESS The device was reset.\r
1019 @retval EFI_DEVICE_ERROR The device is not functioning properly and could\r
1020 not be reset.\r
1021\r
1022**/
a3f98646 1023EFI_STATUS
1024EFIAPI
1025MMCHSReset (
1026 IN EFI_BLOCK_IO_PROTOCOL *This,
1027 IN BOOLEAN ExtendedVerification
1028 )
1029{
8c6151f2 1030 return EFI_SUCCESS;
a3f98646 1031}
1032
8c6151f2 1033
1034/**\r
1035 Read BufferSize bytes from Lba into Buffer.\r
1036\r
1037 @param This Indicates a pointer to the calling context.\r
1038 @param MediaId Id of the media, changes every time the media is replaced.\r
1039 @param Lba The starting Logical Block Address to read from\r
1040 @param BufferSize Size of Buffer, must be a multiple of device block size.\r
1041 @param Buffer A pointer to the destination buffer for the data. The caller is\r
1042 responsible for either having implicit or explicit ownership of the buffer.\r
1043\r
1044 @retval EFI_SUCCESS The data was read correctly from the device.\r
1045 @retval EFI_DEVICE_ERROR The device reported an error while performing the read.\r
1046 @retval EFI_NO_MEDIA There is no media in the device.\r
1047 @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device.\r
1048 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.\r
1049 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, \r
1050 or the buffer is not on proper alignment.\r
1051EFI_STATUS
1052
1053**/
a3f98646 1054EFI_STATUS
1055EFIAPI
1056MMCHSReadBlocks (
1057 IN EFI_BLOCK_IO_PROTOCOL *This,
1058 IN UINT32 MediaId,
1059 IN EFI_LBA Lba,
1060 IN UINTN BufferSize,
1061 OUT VOID *Buffer
1062 )
1063{
1064 EFI_STATUS Status;
1065
a3f98646 1066 //Perform Read operation.
8c6151f2 1067 Status = SdReadWrite (This, (UINTN)Lba, Buffer, BufferSize, READ);
a3f98646 1068
8c6151f2 1069 return Status;\r
a3f98646 1070}
1071
8c6151f2 1072
1073/**\r
1074 Write BufferSize bytes from Lba into Buffer.\r
1075\r
1076 @param This Indicates a pointer to the calling context.\r
1077 @param MediaId The media ID that the write request is for.\r
1078 @param Lba The starting logical block address to be written. The caller is\r
1079 responsible for writing to only legitimate locations.\r
1080 @param BufferSize Size of Buffer, must be a multiple of device block size.\r
1081 @param Buffer A pointer to the source buffer for the data.\r
1082\r
1083 @retval EFI_SUCCESS The data was written correctly to the device.\r
1084 @retval EFI_WRITE_PROTECTED The device can not be written to.\r
1085 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.\r
1086 @retval EFI_NO_MEDIA There is no media in the device.\r
1087 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.\r
1088 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.\r
1089 @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, \r
1090 or the buffer is not on proper alignment.\r
1091\r
1092**/
a3f98646 1093EFI_STATUS
1094EFIAPI
1095MMCHSWriteBlocks (
1096 IN EFI_BLOCK_IO_PROTOCOL *This,
1097 IN UINT32 MediaId,
1098 IN EFI_LBA Lba,
1099 IN UINTN BufferSize,
1100 IN VOID *Buffer
1101 )
1102{
8c6151f2 1103 EFI_STATUS Status;
a3f98646 1104
1105 //Perform write operation.
8c6151f2 1106 Status = SdReadWrite (This, (UINTN)Lba, Buffer, BufferSize, WRITE);
1107\r
1108 return Status;\r
a3f98646 1109}
1110
8c6151f2 1111
1112/**\r
1113 Flush the Block Device.\r
1114\r
1115 @param This Indicates a pointer to the calling context.\r
1116\r
1117 @retval EFI_SUCCESS All outstanding data was written to the device\r
1118 @retval EFI_DEVICE_ERROR The device reported an error while writting back the data\r
1119 @retval EFI_NO_MEDIA There is no media in the device.\r
1120\r
1121**/
a3f98646 1122EFI_STATUS
1123EFIAPI
1124MMCHSFlushBlocks (
1125 IN EFI_BLOCK_IO_PROTOCOL *This
1126 )
1127{
1128 return EFI_SUCCESS;
1129}
1130
8c6151f2 1131
1132EFI_BLOCK_IO_PROTOCOL gBlockIo = {
a3f98646 1133 EFI_BLOCK_IO_INTERFACE_REVISION, // Revision
8c6151f2 1134 &gMMCHSMedia, // *Media
a3f98646 1135 MMCHSReset, // Reset
1136 MMCHSReadBlocks, // ReadBlocks
1137 MMCHSWriteBlocks, // WriteBlocks
1138 MMCHSFlushBlocks // FlushBlocks
1139};
1140
8c6151f2 1141
1142/**\r
1143 Timer callback to convert card present hardware into a boolean that indicates\r
1144 a media change event has happened. If you just check the GPIO you could see \r
1145 card 1 and then check again after card 1 was removed and card 2 was inserted\r
1146 and you would still see media present. Thus you need the timer tick to catch\r
1147 the toggle event.\r
1148\r
1149 @param Event Event whose notification function is being invoked.\r
1150 @param Context The pointer to the notification function's context,\r
1151 which is implementation-dependent. Not used.\r
1152\r
1153**/
1154VOID
1155EFIAPI
1156TimerCallback (
1157 IN EFI_EVENT Event,
1158 IN VOID *Context
1159 )
1160{
1161 BOOLEAN Present;
1162
1163 Present = CardPresent ();
1164 if (gMMCHSMedia.MediaPresent) {
1165 if (!Present && !gMediaChange) {
1166 gMediaChange = TRUE;
1167 }
1168 } else {
1169 if (Present && !gMediaChange) {
1170 gMediaChange = TRUE;
1171 }
1172 }
1173}
1174
1175
a3f98646 1176EFI_STATUS
8c6151f2 1177EFIAPI
a3f98646 1178MMCHSInitialize (
1179 IN EFI_HANDLE ImageHandle,
1180 IN EFI_SYSTEM_TABLE *SystemTable
1181 )
1182{
1183 EFI_STATUS Status;
1184
8c6151f2 1185 Status = gBS->LocateProtocol (&gEmbeddedExternalDeviceProtocolGuid, NULL, (VOID **)&gTPS65950);
a3f98646 1186 ASSERT_EFI_ERROR(Status);
1187
8c6151f2 1188 ZeroMem (&gCardInfo, sizeof (CARD_INFO));
1189
1190 Status = gBS->CreateEvent (EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, TimerCallback, NULL, &gTimerEvent);
1191 ASSERT_EFI_ERROR (Status);
1192
1193 Status = gBS->SetTimer (gTimerEvent, TimerPeriodic, 1000000); // make me a PCD
1194 ASSERT_EFI_ERROR (Status);
a3f98646 1195
1196 //Publish BlockIO.
8c6151f2 1197 Status = gBS->InstallMultipleProtocolInterfaces (
1198 &ImageHandle,
1199 &gEfiBlockIoProtocolGuid, &gBlockIo,
1200 &gEfiDevicePathProtocolGuid, &gMmcHsDevicePath,
1201 NULL
1202 );
a3f98646 1203 return Status;
1204}