]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Drivers/PL180MciDxe/PL180Mci.c
697e33f0d0841709dc6025c7569b985349d42c23
[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_RESPONSE0_REG);
214 Buffer[1] = MmioRead32(MCI_RESPONSE1_REG);
215 } else if (Type == MMC_RESPONSE_TYPE_R2) {
216 Buffer[0] = MmioRead32(MCI_RESPONSE0_REG);
217 Buffer[1] = MmioRead32(MCI_RESPONSE1_REG);
218 Buffer[2] = MmioRead32(MCI_RESPONSE2_REG);
219 Buffer[3] = MmioRead32(MCI_RESPONSE3_REG);
220 }
221
222 return EFI_SUCCESS;
223 }
224
225 EFI_STATUS
226 MciReadBlockData (
227 IN EFI_LBA Lba,
228 IN UINTN Length,
229 IN UINT32* Buffer
230 )
231 {
232 UINTN Loop;
233 UINTN Finish;
234 UINTN Status;
235 EFI_STATUS RetVal;
236 UINTN DataCtrlReg;
237
238 RetVal = EFI_SUCCESS;
239
240 // Read data from the RX FIFO
241 Loop = 0;
242 Finish = MMCI0_BLOCKLEN / 4;
243 do {
244 // Read the Status flags
245 Status = MmioRead32(MCI_STATUS_REG);
246
247 // Do eight reads if possible else a single read
248 if (Status & MCI_STATUS_CMD_RXFIFOHALFFULL) {
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 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
260 Loop++;
261 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
262 Loop++;
263 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
264 Loop++;
265 } else if (Status & MCI_STATUS_CMD_RXDATAAVAILBL) {
266 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
267 Loop++;
268 } else {
269 //Check for error conditions and timeouts
270 if(Status & MCI_STATUS_CMD_DATATIMEOUT) {
271 DEBUG ((EFI_D_ERROR, "MciReadBlockData(): TIMEOUT! Response:0x%X Status:0x%x\n",MmioRead32(MCI_RESPONSE0_REG),Status));
272 RetVal = EFI_TIMEOUT;
273 break;
274 } else if(Status & MCI_STATUS_CMD_DATACRCFAIL) {
275 DEBUG ((EFI_D_ERROR, "MciReadBlockData(): CRC Error! Response:0x%X Status:0x%x\n",MmioRead32(MCI_RESPONSE0_REG),Status));
276 RetVal = EFI_CRC_ERROR;
277 break;
278 } else if(Status & MCI_STATUS_CMD_START_BIT_ERROR) {
279 DEBUG ((EFI_D_ERROR, "MciReadBlockData(): Start-bit Error! Response:0x%X Status:0x%x\n",MmioRead32(MCI_RESPONSE0_REG),Status));
280 RetVal = EFI_NO_RESPONSE;
281 break;
282 }
283 }
284 //clear RX over run flag
285 if(Status & MCI_STATUS_CMD_RXOVERRUN) {
286 MmioWrite32(MCI_CLEAR_STATUS_REG, MCI_STATUS_CMD_RXOVERRUN);
287 }
288 } while ((Loop < Finish));
289
290 //Clear Status flags
291 MmioWrite32(MCI_CLEAR_STATUS_REG, 0x7FF);
292
293 //Disable Data path
294 DataCtrlReg = MmioRead32(MCI_DATA_CTL_REG);
295 MmioWrite32(MCI_DATA_CTL_REG, (DataCtrlReg & 0xFE));
296
297 return RetVal;
298 }
299
300 EFI_STATUS
301 MciWriteBlockData (
302 IN EFI_LBA Lba,
303 IN UINTN Length,
304 IN UINT32* Buffer
305 )
306 {
307 UINTN Loop;
308 UINTN Finish;
309 UINTN Timer;
310 UINTN Status;
311 EFI_STATUS RetVal;
312 UINTN DataCtrlReg;
313
314 RetVal = EFI_SUCCESS;
315
316 // Write the data to the TX FIFO
317 Loop = 0;
318 Finish = MMCI0_BLOCKLEN / 4;
319 Timer = MMCI0_TIMEOUT * 100;
320 do {
321 // Read the Status flags
322 Status = MmioRead32(MCI_STATUS_REG);
323
324 // Do eight writes if possible else a single write
325 if (Status & MCI_STATUS_CMD_TXFIFOHALFEMPTY) {
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 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
337 Loop++;
338 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
339 Loop++;
340 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
341 Loop++;
342 } else if ((Status & MCI_STATUS_CMD_TXFIFOEMPTY)) {
343 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
344 Loop++;
345 } else {
346 //Check for error conditions and timeouts
347 if(Status & MCI_STATUS_CMD_DATATIMEOUT) {
348 DEBUG ((EFI_D_ERROR, "MciWriteBlockData(): TIMEOUT! Response:0x%X Status:0x%x\n",MmioRead32(MCI_RESPONSE0_REG),Status));
349 RetVal = EFI_TIMEOUT;
350 goto Exit;
351 } else if(Status & MCI_STATUS_CMD_DATACRCFAIL) {
352 DEBUG ((EFI_D_ERROR, "MciWriteBlockData(): CRC Error! Response:0x%X Status:0x%x\n",MmioRead32(MCI_RESPONSE0_REG),Status));
353 RetVal = EFI_CRC_ERROR;
354 goto Exit;
355 } else if(Status & MCI_STATUS_CMD_TX_UNDERRUN) {
356 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));
357 RetVal = EFI_BUFFER_TOO_SMALL;
358 ASSERT(0);
359 goto Exit;
360 }
361 }
362 } while (Loop < Finish);
363
364 // Wait for FIFO to drain
365 Timer = MMCI0_TIMEOUT * 60;
366 Status = MmioRead32(MCI_STATUS_REG);
367 #ifndef USE_STREAM
368 // Single block
369 while (((Status & MCI_STATUS_CMD_TXDONE) != MCI_STATUS_CMD_TXDONE) && Timer) {
370 #else
371 // Stream
372 while (((Status & MCI_STATUS_CMD_DATAEND) != MCI_STATUS_CMD_DATAEND) && Timer) {
373 #endif
374 NanoSecondDelay(10);
375 Status = MmioRead32(MCI_STATUS_REG);
376 Timer--;
377 }
378
379 if(Timer == 0) {
380 DEBUG ((EFI_D_ERROR, "MciWriteBlockData(): Data End timeout Number of bytes written 0x%x\n",Loop));
381 ASSERT(Timer > 0);
382 return EFI_TIMEOUT;
383 }
384
385 //Clear Status flags
386 MmioWrite32(MCI_CLEAR_STATUS_REG, 0x7FF);
387 if (Timer == 0) {
388 RetVal = EFI_TIMEOUT;
389 }
390
391 Exit:
392 //Disable Data path
393 DataCtrlReg = MmioRead32(MCI_DATA_CTL_REG);
394 MmioWrite32(MCI_DATA_CTL_REG, (DataCtrlReg & 0xFE));
395 return RetVal;
396 }
397
398 EFI_STATUS
399 MciNotifyState (
400 IN MMC_STATE State
401 )
402 {
403 UINT32 Data32;
404
405 switch(State) {
406 case MmcInvalidState:
407 ASSERT(0);
408 break;
409 case MmcHwInitializationState:
410 // If device already turn on then restart it
411 Data32 = MmioRead32(MCI_POWER_CONTROL_REG);
412 if ((Data32 & 0x2) == MCI_POWER_UP) {
413 MCI_TRACE("MciNotifyState(MmcHwInitializationState): TurnOff MCI");
414
415 // Turn off
416 MmioWrite32(MCI_CLOCK_CONTROL_REG, 0);
417 MmioWrite32(MCI_POWER_CONTROL_REG, 0);
418 MicroSecondDelay(100);
419 }
420
421 MCI_TRACE("MciNotifyState(MmcHwInitializationState): TurnOn MCI");
422 // Setup clock
423 // - 0x1D = 29 => should be the clock divider to be less than 400kHz at MCLK = 24Mhz
424 MmioWrite32(MCI_CLOCK_CONTROL_REG,0x1D | MCI_CLOCK_ENABLE | MCI_CLOCK_POWERSAVE);
425 //MmioWrite32(MCI_CLOCK_CONTROL_REG,0x1D | MCI_CLOCK_ENABLE);
426
427 // Set the voltage
428 MmioWrite32(MCI_POWER_CONTROL_REG,MCI_POWER_OPENDRAIN | (15<<2));
429 MmioWrite32(MCI_POWER_CONTROL_REG,MCI_POWER_ROD | MCI_POWER_OPENDRAIN | (15<<2) | MCI_POWER_UP);
430 MicroSecondDelay(10);
431 MmioWrite32(MCI_POWER_CONTROL_REG,MCI_POWER_ROD | MCI_POWER_OPENDRAIN | (15<<2) | MCI_POWER_ON);
432 MicroSecondDelay(100);
433
434 // Set Data Length & Data Timer
435 MmioWrite32(MCI_DATA_TIMER_REG,0xFFFFF);
436 MmioWrite32(MCI_DATA_LENGTH_REG,8);
437
438 ASSERT((MmioRead32(MCI_POWER_CONTROL_REG) & 0x3) == MCI_POWER_ON);
439 break;
440 case MmcIdleState:
441 MCI_TRACE("MciNotifyState(MmcIdleState)");
442 break;
443 case MmcReadyState:
444 MCI_TRACE("MciNotifyState(MmcReadyState)");
445 break;
446 case MmcIdentificationState:
447 MCI_TRACE("MciNotifyState(MmcIdentificationState)");
448 break;
449 case MmcStandByState:{
450 volatile UINT32 PwrCtrlReg;
451 MCI_TRACE("MciNotifyState(MmcStandByState)");
452
453 // Enable MCICMD push-pull drive
454 PwrCtrlReg = MmioRead32(MCI_POWER_CONTROL_REG);
455 //Disable Open Drain output
456 PwrCtrlReg &=~(MCI_POWER_OPENDRAIN);
457 MmioWrite32(MCI_POWER_CONTROL_REG,PwrCtrlReg);
458
459 // Set MMCI0 clock to 4MHz (24MHz may be possible with cache enabled)
460 //
461 // Note: Increasing clock speed causes TX FIFO under-run errors.
462 // So careful when optimising this driver for higher performance.
463 //
464 MmioWrite32(MCI_CLOCK_CONTROL_REG,0x02 | MCI_CLOCK_ENABLE | MCI_CLOCK_POWERSAVE);
465 // Set MMCI0 clock to 24MHz (by bypassing the divider)
466 //MmioWrite32(MCI_CLOCK_CONTROL_REG,MCI_CLOCK_BYPASS | MCI_CLOCK_ENABLE);
467 break;
468 }
469 case MmcTransferState:
470 //MCI_TRACE("MciNotifyState(MmcTransferState)");
471 break;
472 case MmcSendingDataState:
473 MCI_TRACE("MciNotifyState(MmcSendingDataState)");
474 break;
475 case MmcReceiveDataState:
476 MCI_TRACE("MciNotifyState(MmcReceiveDataState)");
477 break;
478 case MmcProgrammingState:
479 MCI_TRACE("MciNotifyState(MmcProgrammingState)");
480 break;
481 case MmcDisconnectState:
482 MCI_TRACE("MciNotifyState(MmcDisconnectState)");
483 break;
484 default:
485 ASSERT(0);
486 }
487 return EFI_SUCCESS;
488 }
489
490 EFI_GUID mPL180MciDevicePathGuid = EFI_CALLER_ID_GUID;
491
492 EFI_STATUS
493 MciBuildDevicePath (
494 IN EFI_DEVICE_PATH_PROTOCOL **DevicePath
495 )
496 {
497 EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;
498
499 NewDevicePathNode = CreateDeviceNode(HARDWARE_DEVICE_PATH,HW_VENDOR_DP,sizeof(VENDOR_DEVICE_PATH));
500 CopyGuid(&((VENDOR_DEVICE_PATH*)NewDevicePathNode)->Guid,&mPL180MciDevicePathGuid);
501
502 *DevicePath = NewDevicePathNode;
503 return EFI_SUCCESS;
504 }
505
506 EFI_MMC_HOST_PROTOCOL gMciHost = {
507 MciIsCardPresent,
508 MciIsReadOnly,
509 MciBuildDevicePath,
510 MciNotifyState,
511 MciSendCommand,
512 MciReceiveResponse,
513 MciReadBlockData,
514 MciWriteBlockData
515 };
516
517 EFI_STATUS
518 PL180MciDxeInitialize (
519 IN EFI_HANDLE ImageHandle,
520 IN EFI_SYSTEM_TABLE *SystemTable
521 )
522 {
523 EFI_STATUS Status;
524 EFI_HANDLE Handle = NULL;
525
526 MCI_TRACE("PL180MciDxeInitialize()");
527
528 //Publish Component Name, BlockIO protocol interfaces
529 Status = gBS->InstallMultipleProtocolInterfaces (
530 &Handle,
531 &gEfiMmcHostProtocolGuid, &gMciHost,
532 NULL
533 );
534 ASSERT_EFI_ERROR (Status);
535
536 return EFI_SUCCESS;
537 }