Omap35xxPkg/MmcHostDxe: Clean code to respect EDK2 coding convention
[mirror_edk2.git] / Omap35xxPkg / MmcHostDxe / MmcHostDxe.c
CommitLineData
5ec65e96 1/** @file
2*
3* Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
4* Copyright (c) 2011, ARM Limited. All rights reserved.
5*
6* This program and the accompanying materials
7* are licensed and made available under the terms and conditions of the BSD License
8* which accompanies this distribution. The full text of the license may be found at
9* http://opensource.org/licenses/bsd-license.php
10*
11* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13*
14**/
15
16#include "MmcHostDxe.h"
17
18EMBEDDED_EXTERNAL_DEVICE *gTPS65950;
66f47f9f 19UINT8 mMaxDataTransferRate = 0;
20UINT32 mRca = 0;
21BOOLEAN mBitModeSet = FALSE;
5ec65e96 22
23
24typedef struct {
25 VENDOR_DEVICE_PATH Mmc;
26 EFI_DEVICE_PATH End;
27} MMCHS_DEVICE_PATH;
28
29MMCHS_DEVICE_PATH gMMCDevicePath = {
30 {
31 HARDWARE_DEVICE_PATH,
32 HW_VENDOR_DP,
33 (UINT8)(sizeof(VENDOR_DEVICE_PATH)),
34 (UINT8)((sizeof(VENDOR_DEVICE_PATH)) >> 8),
35 0xb615f1f5, 0x5088, 0x43cd, 0x80, 0x9c, 0xa1, 0x6e, 0x52, 0x48, 0x7d, 0x00
36 },
37 {
38 END_DEVICE_PATH_TYPE,
39 END_ENTIRE_DEVICE_PATH_SUBTYPE,
40 sizeof (EFI_DEVICE_PATH_PROTOCOL),
41 0
42 }
43};
44
45BOOLEAN
46IgnoreCommand (
47 UINT32 Command
48 )
49{
50 switch(Command) {
51 case MMC_CMD12:
52 return TRUE;
53 case MMC_CMD13:
54 return TRUE;
55 default:
56 return FALSE;
57 }
58}
59
60UINT32
61TranslateCommand (
62 UINT32 Command
63 )
64{
65 UINT32 Translation;
66
67 switch(Command) {
68 case MMC_CMD2:
69 Translation = CMD2;
70 break;
71 case MMC_CMD3:
72 Translation = CMD3;
73 break;
74 /*case MMC_CMD6:
75 Translation = CMD6;
76 break;*/
77 case MMC_CMD7:
78 Translation = CMD7;
79 break;
80 case MMC_CMD8:
81 Translation = CMD8;
82 break;
83 case MMC_CMD9:
84 Translation = CMD9;
85 break;
86 /*case MMC_CMD12:
87 Translation = CMD12;
88 break;
89 case MMC_CMD13:
90 Translation = CMD13;
91 break;*/
92 case MMC_CMD16:
93 Translation = CMD16;
94 break;
95 case MMC_CMD17:
96 Translation = 0x113A0014;//CMD17;
97 break;
98 case MMC_CMD24:
99 Translation = CMD24 | 4;
100 break;
101 case MMC_CMD55:
102 Translation = CMD55;
103 break;
104 case MMC_ACMD41:
105 Translation = ACMD41;
106 break;
107 default:
108 Translation = Command;
109 }
110
111 return Translation;
112}
113
114VOID
115CalculateCardCLKD (
116 UINTN *ClockFrequencySelect
117 )
118{
5ec65e96 119 UINTN TransferRateValue = 0;
120 UINTN TimeValue = 0 ;
121 UINTN Frequency = 0;
122
66f47f9f 123 DEBUG ((DEBUG_BLKIO, "CalculateCardCLKD()\n"));
124
5ec65e96 125 // For SD Cards we would need to send CMD6 to set
126 // speeds abouve 25MHz. High Speed mode 50 MHz and up
127
66f47f9f 128 // Calculate Transfer rate unit (Bits 2:0 of TRAN_SPEED)
129 switch (mMaxDataTransferRate & 0x7) { // 2
5ec65e96 130 case 0:
131 TransferRateValue = 100 * 1000;
132 break;
133
134 case 1:
135 TransferRateValue = 1 * 1000 * 1000;
136 break;
137
138 case 2:
139 TransferRateValue = 10 * 1000 * 1000;
140 break;
141
142 case 3:
143 TransferRateValue = 100 * 1000 * 1000;
144 break;
145
146 default:
66f47f9f 147 DEBUG ((DEBUG_BLKIO, "Invalid parameter.\n"));
5ec65e96 148 ASSERT(FALSE);
66f47f9f 149 return;
5ec65e96 150 }
151
152 //Calculate Time value (Bits 6:3 of TRAN_SPEED)
66f47f9f 153 switch ((mMaxDataTransferRate >> 3) & 0xF) { // 6
5ec65e96 154 case 1:
155 TimeValue = 10;
156 break;
157
158 case 2:
159 TimeValue = 12;
160 break;
161
162 case 3:
163 TimeValue = 13;
164 break;
165
166 case 4:
167 TimeValue = 15;
168 break;
169
170 case 5:
171 TimeValue = 20;
172 break;
173
174 case 6:
175 TimeValue = 25;
176 break;
177
178 case 7:
179 TimeValue = 30;
180 break;
181
182 case 8:
183 TimeValue = 35;
184 break;
185
186 case 9:
187 TimeValue = 40;
188 break;
189
190 case 10:
191 TimeValue = 45;
192 break;
193
194 case 11:
195 TimeValue = 50;
196 break;
197
198 case 12:
199 TimeValue = 55;
200 break;
201
202 case 13:
203 TimeValue = 60;
204 break;
205
206 case 14:
207 TimeValue = 70;
208 break;
209
210 case 15:
211 TimeValue = 80;
212 break;
213
214 default:
66f47f9f 215 DEBUG ((DEBUG_BLKIO, "Invalid parameter.\n"));
5ec65e96 216 ASSERT(FALSE);
66f47f9f 217 return;
5ec65e96 218 }
219
220 Frequency = TransferRateValue * TimeValue/10;
221
66f47f9f 222 // Calculate Clock divider value to program in MMCHS_SYSCTL[CLKD] field.
5ec65e96 223 *ClockFrequencySelect = ((MMC_REFERENCE_CLK/Frequency) + 1);
224
66f47f9f 225 DEBUG ((DEBUG_BLKIO, "mMaxDataTransferRate: 0x%x, Frequency: %d KHz, ClockFrequencySelect: %x\n", mMaxDataTransferRate, Frequency/1000, *ClockFrequencySelect));
5ec65e96 226}
227
228VOID
229UpdateMMCHSClkFrequency (
230 UINTN NewCLKD
231 )
232{
66f47f9f 233 DEBUG ((DEBUG_BLKIO, "UpdateMMCHSClkFrequency()\n"));
234
235 // Set Clock enable to 0x0 to not provide the clock to the card
5ec65e96 236 MmioAnd32 (MMCHS_SYSCTL, ~CEN);
237
66f47f9f 238 // Set new clock frequency.
5ec65e96 239 MmioAndThenOr32 (MMCHS_SYSCTL, ~CLKD_MASK, NewCLKD << 6);
240
66f47f9f 241 // Poll till Internal Clock Stable
5ec65e96 242 while ((MmioRead32 (MMCHS_SYSCTL) & ICS_MASK) != ICS);
243
66f47f9f 244 // Set Clock enable to 0x1 to provide the clock to the card
5ec65e96 245 MmioOr32 (MMCHS_SYSCTL, CEN);
246}
247
248EFI_STATUS
249InitializeMMCHS (
250 VOID
251 )
252{
66f47f9f 253 UINT8 Data;
5ec65e96 254 EFI_STATUS Status;
255
66f47f9f 256 DEBUG ((DEBUG_BLKIO, "InitializeMMCHS()\n"));
257
258 // Select Device group to belong to P1 device group in Power IC.
5ec65e96 259 Data = DEV_GRP_P1;
260 Status = gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, VMMC1_DEV_GRP), 1, &Data);
261 ASSERT_EFI_ERROR(Status);
262
66f47f9f 263 // Configure voltage regulator for MMC1 in Power IC to output 3.0 voltage.
5ec65e96 264 Data = VSEL_3_00V;
265 Status = gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, VMMC1_DEDICATED_REG), 1, &Data);
266 ASSERT_EFI_ERROR(Status);
267
66f47f9f 268 // After ramping up voltage, set VDDS stable bit to indicate that voltage level is stable.
5ec65e96 269 MmioOr32 (CONTROL_PBIAS_LITE, (PBIASLITEVMODE0 | PBIASLITEPWRDNZ0 | PBIASSPEEDCTRL0 | PBIASLITEVMODE1 | PBIASLITEWRDNZ1));
270
271 // Enable WP GPIO
272 MmioAndThenOr32 (GPIO1_BASE + GPIO_OE, ~BIT23, BIT23);
273
274 // Enable Card Detect
275 Data = CARD_DETECT_ENABLE;
276 gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID2, TPS65950_GPIO_CTRL), 1, &Data);
277
5ec65e96 278 return Status;
279}
280
281BOOLEAN
282MMCIsCardPresent (
283 VOID
284 )
285{
5ec65e96 286 EFI_STATUS Status;
287 UINT8 Data;
288
289 //
290 // Card detect is a GPIO0 on the TPS65950
291 //
292 Status = gTPS65950->Read (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID2, GPIODATAIN1), 1, &Data);
293 if (EFI_ERROR (Status)) {
294 return FALSE;
295 }
296
297 return !(Data & CARD_DETECT_BIT);
298}
299
300BOOLEAN
301MMCIsReadOnly (
302 VOID
303 )
304{
305 /* Note:
306 * On our BeagleBoard the SD card WP pin is always read as TRUE.
307 * Probably something wrong with GPIO configuration.
308 * BeagleBoard-xM uses microSD cards so there is no write protect at all.
309 * Hence commenting out SD card WP pin read status.
310 */
311 //return (MmioRead32 (GPIO1_BASE + GPIO_DATAIN) & BIT23) == BIT23;
312 return 0;
313
314}
315
316// TODO
317EFI_GUID mPL180MciDevicePathGuid = EFI_CALLER_ID_GUID;
318
319EFI_STATUS
320MMCBuildDevicePath (
321 IN EFI_DEVICE_PATH_PROTOCOL **DevicePath
322 )
323{
324 EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;
325
326 NewDevicePathNode = CreateDeviceNode(HARDWARE_DEVICE_PATH,HW_VENDOR_DP,sizeof(VENDOR_DEVICE_PATH));
327 CopyGuid(&((VENDOR_DEVICE_PATH*)NewDevicePathNode)->Guid,&mPL180MciDevicePathGuid);
328 *DevicePath = NewDevicePathNode;
329 return EFI_SUCCESS;
330}
331
332EFI_STATUS
333MMCSendCommand (
334 IN MMC_CMD MmcCmd,
335 IN UINT32 Argument
336 )
337{
66f47f9f 338 UINTN MmcStatus;
339 UINTN RetryCount = 0;
340
5ec65e96 341 if (IgnoreCommand(MmcCmd))
342 return EFI_SUCCESS;
343
344 MmcCmd = TranslateCommand(MmcCmd);
345
66f47f9f 346 //DEBUG ((EFI_D_ERROR, "MMCSendCommand(%d)\n", MmcCmd));
5ec65e96 347
66f47f9f 348 // Check if command line is in use or not. Poll till command line is available.
5ec65e96 349 while ((MmioRead32 (MMCHS_PSTATE) & DATI_MASK) == DATI_NOT_ALLOWED);
350
66f47f9f 351 // Provide the block size.
5ec65e96 352 MmioWrite32 (MMCHS_BLK, BLEN_512BYTES);
353
66f47f9f 354 // Setting Data timeout counter value to max value.
5ec65e96 355 MmioAndThenOr32 (MMCHS_SYSCTL, ~DTO_MASK, DTO_VAL);
356
66f47f9f 357 // Clear Status register.
5ec65e96 358 MmioWrite32 (MMCHS_STAT, 0xFFFFFFFF);
359
66f47f9f 360 // Set command argument register
5ec65e96 361 MmioWrite32 (MMCHS_ARG, Argument);
362
363 //TODO: fix this
364 //Enable interrupt enable events to occur
365 //MmioWrite32 (MMCHS_IE, CmdInterruptEnableVal);
366
66f47f9f 367 // Send a command
5ec65e96 368 MmioWrite32 (MMCHS_CMD, MmcCmd);
369
66f47f9f 370 // Check for the command status.
5ec65e96 371 while (RetryCount < MAX_RETRY_COUNT) {
372 do {
373 MmcStatus = MmioRead32 (MMCHS_STAT);
374 } while (MmcStatus == 0);
375
66f47f9f 376 // Read status of command response
5ec65e96 377 if ((MmcStatus & ERRI) != 0) {
378
66f47f9f 379 // Perform soft-reset for mmci_cmd line.
5ec65e96 380 MmioOr32 (MMCHS_SYSCTL, SRC);
381 while ((MmioRead32 (MMCHS_SYSCTL) & SRC));
382
383 //DEBUG ((EFI_D_INFO, "MmcStatus: 0x%x\n", MmcStatus));
384 return EFI_DEVICE_ERROR;
385 }
386
66f47f9f 387 // Check if command is completed.
5ec65e96 388 if ((MmcStatus & CC) == CC) {
389 MmioWrite32 (MMCHS_STAT, CC);
390 break;
391 }
392
393 RetryCount++;
394 }
395
396 if (RetryCount == MAX_RETRY_COUNT) {
66f47f9f 397 DEBUG ((DEBUG_BLKIO, "MMCSendCommand: Timeout\n"));
5ec65e96 398 return EFI_TIMEOUT;
399 }
400
401 return EFI_SUCCESS;
402}
403
404EFI_STATUS
405MMCNotifyState (
406 IN MMC_STATE State
407 )
408{
409 EFI_STATUS Status;
66f47f9f 410 UINTN FreqSel;
411
5ec65e96 412 switch(State) {
413 case MmcInvalidState:
414 ASSERT(0);
415 break;
416 case MmcHwInitializationState:
66f47f9f 417 mBitModeSet = FALSE;
5ec65e96 418
66f47f9f 419 DEBUG ((DEBUG_BLKIO, "MMCHwInitializationState()\n"));
5ec65e96 420 Status = InitializeMMCHS ();
421 if (EFI_ERROR(Status)) {
66f47f9f 422 DEBUG ((DEBUG_BLKIO, "Initialize MMC host controller fails. Status: %x\n", Status));
5ec65e96 423 return Status;
424 }
425
66f47f9f 426 // Software reset of the MMCHS host controller.
5ec65e96 427 MmioWrite32 (MMCHS_SYSCONFIG, SOFTRESET);
428 gBS->Stall(1000);
429 while ((MmioRead32 (MMCHS_SYSSTATUS) & RESETDONE_MASK) != RESETDONE);
430
66f47f9f 431 // Soft reset for all.
5ec65e96 432 MmioWrite32 (MMCHS_SYSCTL, SRA);
433 gBS->Stall(1000);
434 while ((MmioRead32 (MMCHS_SYSCTL) & SRA) != 0x0);
435
436 //Voltage capabilities initialization. Activate VS18 and VS30.
437 MmioOr32 (MMCHS_CAPA, (VS30 | VS18));
438
66f47f9f 439 // Wakeup configuration
5ec65e96 440 MmioOr32 (MMCHS_SYSCONFIG, ENAWAKEUP);
441 MmioOr32 (MMCHS_HCTL, IWE);
442
66f47f9f 443 // MMCHS Controller default initialization
5ec65e96 444 MmioOr32 (MMCHS_CON, (OD | DW8_1_4_BIT | CEATA_OFF));
445
446 MmioWrite32 (MMCHS_HCTL, (SDVS_3_0_V | DTW_1_BIT | SDBP_OFF));
447
66f47f9f 448 // Enable internal clock
5ec65e96 449 MmioOr32 (MMCHS_SYSCTL, ICE);
450
66f47f9f 451 // Set the clock frequency to 80KHz.
5ec65e96 452 UpdateMMCHSClkFrequency (CLKD_80KHZ);
453
66f47f9f 454 // Enable SD bus power.
5ec65e96 455 MmioOr32 (MMCHS_HCTL, (SDBP_ON));
456
66f47f9f 457 // Poll till SD bus power bit is set.
5ec65e96 458 while ((MmioRead32 (MMCHS_HCTL) & SDBP_MASK) != SDBP_ON);
459
66f47f9f 460 // Enable interrupts.
5ec65e96 461 MmioWrite32 (MMCHS_IE, (BADA_EN | CERR_EN | DEB_EN | DCRC_EN | DTO_EN | CIE_EN |
462 CEB_EN | CCRC_EN | CTO_EN | BRR_EN | BWR_EN | TC_EN | CC_EN));
463
66f47f9f 464 // Controller INIT procedure start.
5ec65e96 465 MmioOr32 (MMCHS_CON, INIT);
466 MmioWrite32 (MMCHS_CMD, 0x00000000);
467 while (!(MmioRead32 (MMCHS_STAT) & CC));
468
66f47f9f 469 // Wait for 1 ms
470 gBS->Stall (1000);
5ec65e96 471
66f47f9f 472 // Set CC bit to 0x1 to clear the flag
5ec65e96 473 MmioOr32 (MMCHS_STAT, CC);
474
66f47f9f 475 // Retry INIT procedure.
5ec65e96 476 MmioWrite32 (MMCHS_CMD, 0x00000000);
477 while (!(MmioRead32 (MMCHS_STAT) & CC));
478
66f47f9f 479 // End initialization sequence
5ec65e96 480 MmioAnd32 (MMCHS_CON, ~INIT);
481
482 MmioOr32 (MMCHS_HCTL, (SDVS_3_0_V | DTW_1_BIT | SDBP_ON));
483
66f47f9f 484 // Change clock frequency to 400KHz to fit protocol
5ec65e96 485 UpdateMMCHSClkFrequency(CLKD_400KHZ);
486
487 MmioOr32 (MMCHS_CON, OD);
488 break;
489 case MmcIdleState:
490 break;
491 case MmcReadyState:
492 break;
493 case MmcIdentificationState:
494 break;
495 case MmcStandByState:
66f47f9f 496 CalculateCardCLKD (&FreqSel);
497 UpdateMMCHSClkFrequency (FreqSel);
5ec65e96 498 break;
499 case MmcTransferState:
66f47f9f 500 if (!mBitModeSet) {
501 Status = MMCSendCommand (This, CMD55, mRca << 16);
5ec65e96 502 if (!EFI_ERROR (Status)) {
66f47f9f 503 // Set device into 4-bit data bus mode
504 Status = MMCSendCommand (This, ACMD6, 0x2);
5ec65e96 505 if (!EFI_ERROR (Status)) {
66f47f9f 506 // Set host controler into 4-bit mode
5ec65e96 507 MmioOr32 (MMCHS_HCTL, DTW_4_BIT);
66f47f9f 508 DEBUG ((DEBUG_BLKIO, "SD Memory Card set to 4-bit mode\n"));
509 mBitModeSet = TRUE;
5ec65e96 510 }
511 }
512 }
513 break;
514 case MmcSendingDataState:
515 break;
516 case MmcReceiveDataState:
517 break;
518 case MmcProgrammingState:
519 break;
520 case MmcDisconnectState:
521 default:
522 ASSERT(0);
523 }
524 return EFI_SUCCESS;
525}
526
527EFI_STATUS
528MMCReceiveResponse (
529 IN MMC_RESPONSE_TYPE Type,
530 IN UINT32* Buffer
531 )
532{
5ec65e96 533 if (Buffer == NULL) {
5ec65e96 534 return EFI_INVALID_PARAMETER;
535 }
536
537 if (Type == MMC_RESPONSE_TYPE_R2) {
538 Buffer[0] = MmioRead32 (MMCHS_RSP10);
539 Buffer[1] = MmioRead32 (MMCHS_RSP32);
540 Buffer[2] = MmioRead32 (MMCHS_RSP54);
541 Buffer[3] = MmioRead32 (MMCHS_RSP76);
542 } else {
543 Buffer[0] = MmioRead32 (MMCHS_RSP10);
544 }
545
546 if (Type == MMC_RESPONSE_TYPE_CSD) {
66f47f9f 547 mMaxDataTransferRate = Buffer[3] & 0xFF;
5ec65e96 548 } else if (Type == MMC_RESPONSE_TYPE_RCA) {
66f47f9f 549 mRca = Buffer[0] >> 16;
5ec65e96 550 }
551
552 return EFI_SUCCESS;
553}
554
555EFI_STATUS
556MMCReadBlockData (
557 IN EFI_LBA Lba,
558 IN UINTN Length,
559 IN UINT32* Buffer
560 )
561{
5ec65e96 562 UINTN MmcStatus;
563 UINTN Count;
564 UINTN RetryCount = 0;
565
66f47f9f 566 DEBUG ((DEBUG_BLKIO, "MMCReadBlockData(LBA: 0x%x, Length: 0x%x, Buffer: 0x%x)\n", Lba, Length, Buffer));
567
568 // Check controller status to make sure there is no error.
5ec65e96 569 while (RetryCount < MAX_RETRY_COUNT) {
570 do {
66f47f9f 571 // Read Status.
5ec65e96 572 MmcStatus = MmioRead32 (MMCHS_STAT);
573 } while(MmcStatus == 0);
574
66f47f9f 575 // Check if Buffer read ready (BRR) bit is set?
5ec65e96 576 if (MmcStatus & BRR) {
577
66f47f9f 578 // Clear BRR bit
5ec65e96 579 MmioOr32 (MMCHS_STAT, BRR);
580
581 for (Count = 0; Count < Length / 4; Count++) {
582 *Buffer++ = MmioRead32(MMCHS_DATA);
583 }
584 break;
585 }
586 RetryCount++;
587 }
588
589 if (RetryCount == MAX_RETRY_COUNT) {
590 return EFI_TIMEOUT;
591 }
592
593 return EFI_SUCCESS;
594}
595
596EFI_STATUS
597MMCWriteBlockData (
598 IN EFI_LBA Lba,
599 IN UINTN Length,
600 IN UINT32* Buffer
601 )
602{
603 UINTN MmcStatus;
604 UINTN Count;
605 UINTN RetryCount = 0;
606
66f47f9f 607 // Check controller status to make sure there is no error.
5ec65e96 608 while (RetryCount < MAX_RETRY_COUNT) {
609 do {
66f47f9f 610 // Read Status.
5ec65e96 611 MmcStatus = MmioRead32 (MMCHS_STAT);
612 } while(MmcStatus == 0);
613
66f47f9f 614 // Check if Buffer write ready (BWR) bit is set?
5ec65e96 615 if (MmcStatus & BWR) {
616
66f47f9f 617 // Clear BWR bit
5ec65e96 618 MmioOr32 (MMCHS_STAT, BWR);
619
66f47f9f 620 // Write block worth of data.
5ec65e96 621 for (Count = 0; Count < Length / 4; Count++) {
622 MmioWrite32 (MMCHS_DATA, *Buffer++);
623 }
624
625 break;
626 }
627 RetryCount++;
628 }
629
630 if (RetryCount == MAX_RETRY_COUNT) {
631 return EFI_TIMEOUT;
632 }
633
634 return EFI_SUCCESS;
635}
636
637EFI_MMC_HOST_PROTOCOL gMMCHost = {
638 MMCIsCardPresent,
639 MMCIsReadOnly,
640 MMCBuildDevicePath,
641 MMCNotifyState,
642 MMCSendCommand,
643 MMCReceiveResponse,
644 MMCReadBlockData,
645 MMCWriteBlockData
646};
647
648EFI_STATUS
649MMCInitialize (
650 IN EFI_HANDLE ImageHandle,
651 IN EFI_SYSTEM_TABLE *SystemTable
652 )
653{
5ec65e96 654 EFI_STATUS Status;
655 EFI_HANDLE Handle = NULL;
656
66f47f9f 657 DEBUG ((DEBUG_BLKIO, "MMCInitialize()\n"));
658
5ec65e96 659 Status = gBS->LocateProtocol (&gEmbeddedExternalDeviceProtocolGuid, NULL, (VOID **)&gTPS65950);
660 ASSERT_EFI_ERROR(Status);
661
662 Status = gBS->InstallMultipleProtocolInterfaces (
663 &Handle,
664 &gEfiMmcHostProtocolGuid, &gMMCHost,
665 NULL
666 );
667 ASSERT_EFI_ERROR (Status);
668
669 return Status;
670}