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