]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Drivers/PL180MciDxe/PL180Mci.c
EmbeddedPkg/MmcDxe: Fix the indexes in the response array
[mirror_edk2.git] / ArmPlatformPkg / Drivers / PL180MciDxe / PL180Mci.c
1 /** @file
2 This file implement the MMC Host Protocol for the ARM PrimeCell PL180.
3
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 "PL180Mci.h"
17
18 #include <Library/DevicePathLib.h>
19 #include <Library/BaseMemoryLib.h>
20
21 EFI_MMC_HOST_PROTOCOL *gpMmcHost;
22
23 // Untested ...
24 //#define USE_STREAM
25
26 #define MMCI0_BLOCKLEN 512
27 #define MMCI0_POW2_BLOCKLEN 9
28 #define MMCI0_TIMEOUT 1000
29
30 BOOLEAN
31 MciIsPowerOn (
32 VOID
33 )
34 {
35 return ((MmioRead32(MCI_POWER_CONTROL_REG) & 0x3) == MCI_POWER_ON);
36 }
37
38 EFI_STATUS
39 MciInitialize (
40 VOID
41 )
42 {
43 MCI_TRACE("MciInitialize()");
44 return EFI_SUCCESS;
45 }
46
47 BOOLEAN
48 MciIsCardPresent (
49 VOID
50 )
51 {
52 return (MmioRead32(FixedPcdGet32(PcdPL180SysMciRegAddress)) & 1);
53 }
54
55 BOOLEAN
56 MciIsReadOnly (
57 VOID
58 )
59 {
60 return (MmioRead32(FixedPcdGet32(PcdPL180SysMciRegAddress)) & 2);
61 }
62
63 #if 0
64 //Note: This function has been commented out because it is not used yet.
65 // This function could be used to remove the hardcoded BlockLen used
66 // in MciPrepareDataPath
67
68 // Convert block size to 2^n
69 STATIC
70 UINT32
71 GetPow2BlockLen (
72 IN UINT32 BlockLen
73 )
74 {
75 UINTN Loop;
76 UINTN Pow2BlockLen;
77
78 Loop = 0x8000;
79 Pow2BlockLen = 15;
80 do {
81 Loop = (Loop >> 1) & 0xFFFF;
82 Pow2BlockLen--;
83 } while (Pow2BlockLen && (!(Loop & BlockLen)));
84
85 return Pow2BlockLen;
86 }
87 #endif
88
89 VOID
90 MciPrepareDataPath (
91 IN UINTN TransferDirection
92 )
93 {
94 // Set Data Length & Data Timer
95 MmioWrite32(MCI_DATA_TIMER_REG,0xFFFFFFF);
96 MmioWrite32(MCI_DATA_LENGTH_REG,MMCI0_BLOCKLEN);
97
98 #ifndef USE_STREAM
99 //Note: we are using a hardcoded BlockLen (=512). If we decide to use a variable size, we could
100 // compute the pow2 of BlockLen with the above function GetPow2BlockLen()
101 MmioWrite32(MCI_DATA_CTL_REG, MCI_DATACTL_ENABLE | MCI_DATACTL_DMA_ENABLE | TransferDirection | (MMCI0_POW2_BLOCKLEN << 4));
102 #else
103 MmioWrite32(MCI_DATA_CTL_REG, MCI_DATACTL_ENABLE | MCI_DATACTL_DMA_ENABLE | TransferDirection | MCI_DATACTL_STREAM_TRANS);
104 #endif
105 }
106
107 EFI_STATUS
108 MciSendCommand (
109 IN MMC_CMD MmcCmd,
110 IN UINT32 Argument
111 )
112 {
113 UINT32 Status;
114 UINT32 Cmd;
115 UINTN RetVal;
116 UINTN CmdCtrlReg;
117
118 RetVal = EFI_SUCCESS;
119
120 if ((MmcCmd == MMC_CMD17) || (MmcCmd == MMC_CMD11)) {
121 MciPrepareDataPath(MCI_DATACTL_CARD_TO_CONT);
122 } else if ((MmcCmd == MMC_CMD24) || (MmcCmd == MMC_CMD20)) {
123 MciPrepareDataPath(MCI_DATACTL_CONT_TO_CARD);
124 }
125
126 // Create Command for PL180
127 Cmd = (MMC_GET_INDX(MmcCmd) & INDX_MASK) | MCI_CPSM_ENABLED;
128 if (MmcCmd & MMC_CMD_WAIT_RESPONSE) {
129 Cmd |= MCI_CPSM_WAIT_RESPONSE;
130 }
131
132 if (MmcCmd & MMC_CMD_LONG_RESPONSE) {
133 Cmd |= MCI_CPSM_LONG_RESPONSE;
134 }
135
136 // Clear Status register static flags
137 MmioWrite32(MCI_CLEAR_STATUS_REG,0x7FF);
138
139 //Write to command argument register
140 MmioWrite32(MCI_ARGUMENT_REG,Argument);
141
142 //Write to command register
143 MmioWrite32(MCI_COMMAND_REG,Cmd);
144
145 if (Cmd & MCI_CPSM_WAIT_RESPONSE) {
146 Status = MmioRead32(MCI_STATUS_REG);
147 while (!(Status & (MCI_STATUS_CMD_RESPEND | MCI_STATUS_CMD_CMDCRCFAIL | MCI_STATUS_CMD_CMDTIMEOUT | MCI_STATUS_CMD_START_BIT_ERROR))) {
148 Status = MmioRead32(MCI_STATUS_REG);
149 }
150
151 if ((Status & MCI_STATUS_CMD_START_BIT_ERROR)) {
152 DEBUG ((EFI_D_ERROR, "MciSendCommand(CmdIndex:%d) Start bit Error! Response:0x%X Status:0x%x\n",(Cmd & 0x3F),MmioRead32(MCI_RESPONSE0_REG),Status));
153 RetVal = EFI_NO_RESPONSE;
154 goto Exit;
155 } else if ((Status & MCI_STATUS_CMD_CMDTIMEOUT)) {
156 //DEBUG ((EFI_D_ERROR, "MciSendCommand(CmdIndex:%d) TIMEOUT! Response:0x%X Status:0x%x\n",(Cmd & 0x3F),MmioRead32(MCI_RESPONSE0_REG),Status));
157 RetVal = EFI_TIMEOUT;
158 goto Exit;
159 } else if ((!(MmcCmd & MMC_CMD_NO_CRC_RESPONSE)) && (Status & MCI_STATUS_CMD_CMDCRCFAIL)) {
160 // The CMD1 and response type R3 do not contain CRC. We should ignore the CRC failed Status.
161 RetVal = EFI_CRC_ERROR;
162 goto Exit;
163 } else {
164 RetVal = EFI_SUCCESS;
165 goto Exit;
166 }
167 } else {
168 Status = MmioRead32(MCI_STATUS_REG);
169 while (!(Status & (MCI_STATUS_CMD_SENT | MCI_STATUS_CMD_CMDCRCFAIL | MCI_STATUS_CMD_CMDTIMEOUT| MCI_STATUS_CMD_START_BIT_ERROR))) {
170 Status = MmioRead32(MCI_STATUS_REG);
171 }
172
173 if ((Status & MCI_STATUS_CMD_START_BIT_ERROR)) {
174 DEBUG ((EFI_D_ERROR, "MciSendCommand(CmdIndex:%d) Start bit Error! Response:0x%X Status:0x%x\n",(Cmd & 0x3F),MmioRead32(MCI_RESPONSE0_REG),Status));
175 RetVal = EFI_NO_RESPONSE;
176 goto Exit;
177 } else if ((Status & MCI_STATUS_CMD_CMDTIMEOUT)) {
178 //DEBUG ((EFI_D_ERROR, "MciSendCommand(CmdIndex:%d) TIMEOUT! Response:0x%X Status:0x%x\n",(Cmd & 0x3F),MmioRead32(MCI_RESPONSE0_REG),Status));
179 RetVal = EFI_TIMEOUT;
180 goto Exit;
181 } else
182 if ((!(MmcCmd & MMC_CMD_NO_CRC_RESPONSE)) && (Status & MCI_STATUS_CMD_CMDCRCFAIL)) {
183 // The CMD1 does not contain CRC. We should ignore the CRC failed Status.
184 RetVal = EFI_CRC_ERROR;
185 goto Exit;
186 } else {
187 RetVal = EFI_SUCCESS;
188 goto Exit;
189 }
190 }
191
192 Exit:
193 //Disable Command Path
194 CmdCtrlReg = MmioRead32(MCI_COMMAND_REG);
195 MmioWrite32(MCI_COMMAND_REG, (CmdCtrlReg & ~MCI_CPSM_ENABLED));
196 return RetVal;
197 }
198
199 EFI_STATUS
200 MciReceiveResponse (
201 IN MMC_RESPONSE_TYPE Type,
202 IN UINT32* Buffer
203 )
204 {
205 if (Buffer == NULL) {
206 return EFI_INVALID_PARAMETER;
207 }
208
209 if ((Type == MMC_RESPONSE_TYPE_R1) || (Type == MMC_RESPONSE_TYPE_R1b) ||
210 (Type == MMC_RESPONSE_TYPE_R3) || (Type == MMC_RESPONSE_TYPE_R6) ||
211 (Type == MMC_RESPONSE_TYPE_R7))
212 {
213 Buffer[0] = MmioRead32(MCI_RESPONSE3_REG);
214 } else if (Type == MMC_RESPONSE_TYPE_R2) {
215 Buffer[0] = MmioRead32(MCI_RESPONSE0_REG);
216 Buffer[1] = MmioRead32(MCI_RESPONSE1_REG);
217 Buffer[2] = MmioRead32(MCI_RESPONSE2_REG);
218 Buffer[3] = MmioRead32(MCI_RESPONSE3_REG);
219 }
220
221 return EFI_SUCCESS;
222 }
223
224 EFI_STATUS
225 MciReadBlockData (
226 IN EFI_LBA Lba,
227 IN UINTN Length,
228 IN UINT32* Buffer
229 )
230 {
231 UINTN Loop;
232 UINTN Finish;
233 UINTN Status;
234 EFI_STATUS RetVal;
235 UINTN DataCtrlReg;
236
237 RetVal = EFI_SUCCESS;
238
239 // Read data from the RX FIFO
240 Loop = 0;
241 Finish = MMCI0_BLOCKLEN / 4;
242 do {
243 // Read the Status flags
244 Status = MmioRead32(MCI_STATUS_REG);
245
246 // Do eight reads if possible else a single read
247 if (Status & MCI_STATUS_CMD_RXFIFOHALFFULL) {
248 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
249 Loop++;
250 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
251 Loop++;
252 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
253 Loop++;
254 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
255 Loop++;
256 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
257 Loop++;
258 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
259 Loop++;
260 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
261 Loop++;
262 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
263 Loop++;
264 } else if (Status & MCI_STATUS_CMD_RXDATAAVAILBL) {
265 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
266 Loop++;
267 } else {
268 //Check for error conditions and timeouts
269 if(Status & MCI_STATUS_CMD_DATATIMEOUT) {
270 DEBUG ((EFI_D_ERROR, "MciReadBlockData(): TIMEOUT! Response:0x%X Status:0x%x\n",MmioRead32(MCI_RESPONSE0_REG),Status));
271 RetVal = EFI_TIMEOUT;
272 break;
273 } else if(Status & MCI_STATUS_CMD_DATACRCFAIL) {
274 DEBUG ((EFI_D_ERROR, "MciReadBlockData(): CRC Error! Response:0x%X Status:0x%x\n",MmioRead32(MCI_RESPONSE0_REG),Status));
275 RetVal = EFI_CRC_ERROR;
276 break;
277 } else if(Status & MCI_STATUS_CMD_START_BIT_ERROR) {
278 DEBUG ((EFI_D_ERROR, "MciReadBlockData(): Start-bit Error! Response:0x%X Status:0x%x\n",MmioRead32(MCI_RESPONSE0_REG),Status));
279 RetVal = EFI_NO_RESPONSE;
280 break;
281 }
282 }
283 //clear RX over run flag
284 if(Status & MCI_STATUS_CMD_RXOVERRUN) {
285 MmioWrite32(MCI_CLEAR_STATUS_REG, MCI_STATUS_CMD_RXOVERRUN);
286 }
287 } while ((Loop < Finish));
288
289 //Clear Status flags
290 MmioWrite32(MCI_CLEAR_STATUS_REG, 0x7FF);
291
292 //Disable Data path
293 DataCtrlReg = MmioRead32(MCI_DATA_CTL_REG);
294 MmioWrite32(MCI_DATA_CTL_REG, (DataCtrlReg & 0xFE));
295
296 return RetVal;
297 }
298
299 EFI_STATUS
300 MciWriteBlockData (
301 IN EFI_LBA Lba,
302 IN UINTN Length,
303 IN UINT32* Buffer
304 )
305 {
306 UINTN Loop;
307 UINTN Finish;
308 UINTN Timer;
309 UINTN Status;
310 EFI_STATUS RetVal;
311 UINTN DataCtrlReg;
312
313 RetVal = EFI_SUCCESS;
314
315 // Write the data to the TX FIFO
316 Loop = 0;
317 Finish = MMCI0_BLOCKLEN / 4;
318 Timer = MMCI0_TIMEOUT * 100;
319 do {
320 // Read the Status flags
321 Status = MmioRead32(MCI_STATUS_REG);
322
323 // Do eight writes if possible else a single write
324 if (Status & MCI_STATUS_CMD_TXFIFOHALFEMPTY) {
325 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
326 Loop++;
327 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
328 Loop++;
329 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
330 Loop++;
331 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
332 Loop++;
333 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
334 Loop++;
335 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
336 Loop++;
337 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
338 Loop++;
339 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
340 Loop++;
341 } else if ((Status & MCI_STATUS_CMD_TXFIFOEMPTY)) {
342 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
343 Loop++;
344 } else {
345 //Check for error conditions and timeouts
346 if(Status & MCI_STATUS_CMD_DATATIMEOUT) {
347 DEBUG ((EFI_D_ERROR, "MciWriteBlockData(): TIMEOUT! Response:0x%X Status:0x%x\n",MmioRead32(MCI_RESPONSE0_REG),Status));
348 RetVal = EFI_TIMEOUT;
349 goto Exit;
350 } else if(Status & MCI_STATUS_CMD_DATACRCFAIL) {
351 DEBUG ((EFI_D_ERROR, "MciWriteBlockData(): CRC Error! Response:0x%X Status:0x%x\n",MmioRead32(MCI_RESPONSE0_REG),Status));
352 RetVal = EFI_CRC_ERROR;
353 goto Exit;
354 } else if(Status & MCI_STATUS_CMD_TX_UNDERRUN) {
355 DEBUG ((EFI_D_ERROR, "MciWriteBlockData(): TX buffer Underrun! Response:0x%X Status:0x%x, Number of bytes written 0x%x\n",MmioRead32(MCI_RESPONSE0_REG),Status, Loop));
356 RetVal = EFI_BUFFER_TOO_SMALL;
357 ASSERT(0);
358 goto Exit;
359 }
360 }
361 } while (Loop < Finish);
362
363 // Wait for FIFO to drain
364 Timer = MMCI0_TIMEOUT * 60;
365 Status = MmioRead32(MCI_STATUS_REG);
366 #ifndef USE_STREAM
367 // Single block
368 while (((Status & MCI_STATUS_CMD_TXDONE) != MCI_STATUS_CMD_TXDONE) && Timer) {
369 #else
370 // Stream
371 while (((Status & MCI_STATUS_CMD_DATAEND) != MCI_STATUS_CMD_DATAEND) && Timer) {
372 #endif
373 NanoSecondDelay(10);
374 Status = MmioRead32(MCI_STATUS_REG);
375 Timer--;
376 }
377
378 if(Timer == 0) {
379 DEBUG ((EFI_D_ERROR, "MciWriteBlockData(): Data End timeout Number of bytes written 0x%x\n",Loop));
380 ASSERT(Timer > 0);
381 return EFI_TIMEOUT;
382 }
383
384 //Clear Status flags
385 MmioWrite32(MCI_CLEAR_STATUS_REG, 0x7FF);
386 if (Timer == 0) {
387 RetVal = EFI_TIMEOUT;
388 }
389
390 Exit:
391 //Disable Data path
392 DataCtrlReg = MmioRead32(MCI_DATA_CTL_REG);
393 MmioWrite32(MCI_DATA_CTL_REG, (DataCtrlReg & 0xFE));
394 return RetVal;
395 }
396
397 EFI_STATUS
398 MciNotifyState (
399 IN MMC_STATE State
400 )
401 {
402 UINT32 Data32;
403
404 switch(State) {
405 case MmcInvalidState:
406 ASSERT(0);
407 break;
408 case MmcHwInitializationState:
409 // If device already turn on then restart it
410 Data32 = MmioRead32(MCI_POWER_CONTROL_REG);
411 if ((Data32 & 0x2) == MCI_POWER_UP) {
412 MCI_TRACE("MciNotifyState(MmcHwInitializationState): TurnOff MCI");
413
414 // Turn off
415 MmioWrite32(MCI_CLOCK_CONTROL_REG, 0);
416 MmioWrite32(MCI_POWER_CONTROL_REG, 0);
417 MicroSecondDelay(100);
418 }
419
420 MCI_TRACE("MciNotifyState(MmcHwInitializationState): TurnOn MCI");
421 // Setup clock
422 // - 0x1D = 29 => should be the clock divider to be less than 400kHz at MCLK = 24Mhz
423 MmioWrite32(MCI_CLOCK_CONTROL_REG,0x1D | MCI_CLOCK_ENABLE | MCI_CLOCK_POWERSAVE);
424 //MmioWrite32(MCI_CLOCK_CONTROL_REG,0x1D | MCI_CLOCK_ENABLE);
425
426 // Set the voltage
427 MmioWrite32(MCI_POWER_CONTROL_REG,MCI_POWER_OPENDRAIN | (15<<2));
428 MmioWrite32(MCI_POWER_CONTROL_REG,MCI_POWER_ROD | MCI_POWER_OPENDRAIN | (15<<2) | MCI_POWER_UP);
429 MicroSecondDelay(10);
430 MmioWrite32(MCI_POWER_CONTROL_REG,MCI_POWER_ROD | MCI_POWER_OPENDRAIN | (15<<2) | MCI_POWER_ON);
431 MicroSecondDelay(100);
432
433 // Set Data Length & Data Timer
434 MmioWrite32(MCI_DATA_TIMER_REG,0xFFFFF);
435 MmioWrite32(MCI_DATA_LENGTH_REG,8);
436
437 ASSERT((MmioRead32(MCI_POWER_CONTROL_REG) & 0x3) == MCI_POWER_ON);
438 break;
439 case MmcIdleState:
440 MCI_TRACE("MciNotifyState(MmcIdleState)");
441 break;
442 case MmcReadyState:
443 MCI_TRACE("MciNotifyState(MmcReadyState)");
444 break;
445 case MmcIdentificationState:
446 MCI_TRACE("MciNotifyState(MmcIdentificationState)");
447 break;
448 case MmcStandByState:{
449 volatile UINT32 PwrCtrlReg;
450 MCI_TRACE("MciNotifyState(MmcStandByState)");
451
452 // Enable MCICMD push-pull drive
453 PwrCtrlReg = MmioRead32(MCI_POWER_CONTROL_REG);
454 //Disable Open Drain output
455 PwrCtrlReg &=~(MCI_POWER_OPENDRAIN);
456 MmioWrite32(MCI_POWER_CONTROL_REG,PwrCtrlReg);
457
458 // Set MMCI0 clock to 4MHz (24MHz may be possible with cache enabled)
459 //
460 // Note: Increasing clock speed causes TX FIFO under-run errors.
461 // So careful when optimising this driver for higher performance.
462 //
463 MmioWrite32(MCI_CLOCK_CONTROL_REG,0x02 | MCI_CLOCK_ENABLE | MCI_CLOCK_POWERSAVE);
464 // Set MMCI0 clock to 24MHz (by bypassing the divider)
465 //MmioWrite32(MCI_CLOCK_CONTROL_REG,MCI_CLOCK_BYPASS | MCI_CLOCK_ENABLE);
466 break;
467 }
468 case MmcTransferState:
469 //MCI_TRACE("MciNotifyState(MmcTransferState)");
470 break;
471 case MmcSendingDataState:
472 MCI_TRACE("MciNotifyState(MmcSendingDataState)");
473 break;
474 case MmcReceiveDataState:
475 MCI_TRACE("MciNotifyState(MmcReceiveDataState)");
476 break;
477 case MmcProgrammingState:
478 MCI_TRACE("MciNotifyState(MmcProgrammingState)");
479 break;
480 case MmcDisconnectState:
481 MCI_TRACE("MciNotifyState(MmcDisconnectState)");
482 break;
483 default:
484 ASSERT(0);
485 }
486 return EFI_SUCCESS;
487 }
488
489 EFI_GUID mPL180MciDevicePathGuid = EFI_CALLER_ID_GUID;
490
491 EFI_STATUS
492 MciBuildDevicePath (
493 IN EFI_DEVICE_PATH_PROTOCOL **DevicePath
494 )
495 {
496 EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;
497
498 NewDevicePathNode = CreateDeviceNode(HARDWARE_DEVICE_PATH,HW_VENDOR_DP,sizeof(VENDOR_DEVICE_PATH));
499 CopyGuid(&((VENDOR_DEVICE_PATH*)NewDevicePathNode)->Guid,&mPL180MciDevicePathGuid);
500
501 *DevicePath = NewDevicePathNode;
502 return EFI_SUCCESS;
503 }
504
505 EFI_MMC_HOST_PROTOCOL gMciHost = {
506 MciIsCardPresent,
507 MciIsReadOnly,
508 MciBuildDevicePath,
509 MciNotifyState,
510 MciSendCommand,
511 MciReceiveResponse,
512 MciReadBlockData,
513 MciWriteBlockData
514 };
515
516 EFI_STATUS
517 PL180MciDxeInitialize (
518 IN EFI_HANDLE ImageHandle,
519 IN EFI_SYSTEM_TABLE *SystemTable
520 )
521 {
522 EFI_STATUS Status;
523 EFI_HANDLE Handle = NULL;
524
525 MCI_TRACE("PL180MciDxeInitialize()");
526
527 //Publish Component Name, BlockIO protocol interfaces
528 Status = gBS->InstallMultipleProtocolInterfaces (
529 &Handle,
530 &gEfiMmcHostProtocolGuid, &gMciHost,
531 NULL
532 );
533 ASSERT_EFI_ERROR (Status);
534
535 return EFI_SUCCESS;
536 }