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