]>
Commit | Line | Data |
---|---|---|
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 | 28 | EFI_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 | ||
41 | typedef struct { | |
42 | VENDOR_DEVICE_PATH Mmc; | |
43 | EFI_DEVICE_PATH End; | |
44 | } MMCHS_DEVICE_PATH; | |
45 | ||
8c6151f2 | 46 | MMCHS_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 | 62 | CARD_INFO gCardInfo; |
a3f98646 | 63 | EMBEDDED_EXTERNAL_DEVICE *gTPS65950; |
8c6151f2 | 64 | EFI_EVENT gTimerEvent; |
65 | BOOLEAN gMediaChange = FALSE; | |
a3f98646 | 66 | |
67 | // | |
68 | // Internal Functions | |
69 | // | |
70 | ||
8c6151f2 | 71 | |
a3f98646 | 72 | VOID |
73 | ParseCardCIDData ( | |
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 | 93 | VOID |
94 | UpdateMMCHSClkFrequency ( | |
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 | 112 | EFI_STATUS |
113 | SendCmd ( | |
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 | 177 | VOID |
178 | GetBlockInformation ( | |
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 | 214 | VOID |
215 | CalculateCardCLKD ( | |
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 | 325 | VOID |
326 | GetCardConfigurationData ( | |
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 | 345 | EFI_STATUS |
346 | InitializeMMCHS ( | |
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 | 378 | EFI_STATUS |
379 | PerformCardIdenfication ( | |
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 | 569 | EFI_STATUS |
570 | GetCardSpecificData ( | |
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 | 603 | EFI_STATUS |
604 | PerformCardConfiguration ( | |
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 | 631 | EFI_STATUS |
8c6151f2 | 632 | ReadBlockData ( |
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 | 673 | EFI_STATUS |
8c6151f2 | 674 | WriteBlockData ( |
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 | 716 | EFI_STATUS |
8c6151f2 | 717 | TransferBlockData ( |
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 | 776 | BOOLEAN |
777 | CardPresent ( | |
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 | ||
800 | EFI_STATUS | |
801 | DetectCard ( | |
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 | 883 | EFI_STATUS |
884 | SdReadWrite ( | |
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 | 1006 | Done:\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 | 1023 | EFI_STATUS |
1024 | EFIAPI | |
1025 | MMCHSReset ( | |
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 | |
1051 | EFI_STATUS | |
1052 | ||
1053 | **/ | |
a3f98646 | 1054 | EFI_STATUS |
1055 | EFIAPI | |
1056 | MMCHSReadBlocks ( | |
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 | 1093 | EFI_STATUS |
1094 | EFIAPI | |
1095 | MMCHSWriteBlocks ( | |
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 | 1122 | EFI_STATUS |
1123 | EFIAPI | |
1124 | MMCHSFlushBlocks ( | |
1125 | IN EFI_BLOCK_IO_PROTOCOL *This | |
1126 | ) | |
1127 | { | |
1128 | return EFI_SUCCESS; | |
1129 | } | |
1130 | ||
8c6151f2 | 1131 | |
1132 | EFI_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 | **/ | |
1154 | VOID | |
1155 | EFIAPI | |
1156 | TimerCallback ( | |
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 | 1176 | EFI_STATUS |
8c6151f2 | 1177 | EFIAPI |
a3f98646 | 1178 | MMCHSInitialize ( |
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 | } |