2 This file implement the MMC Host Protocol for the ARM PrimeCell PL180.
4 Copyright (c) 2011, ARM Limited. All rights reserved.
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
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.
18 #include <Library/DevicePathLib.h>
19 #include <Library/BaseMemoryLib.h>
21 EFI_MMC_HOST_PROTOCOL
*gpMmcHost
;
26 #define MMCI0_BLOCKLEN 512
27 #define MMCI0_POW2_BLOCKLEN 9
28 #define MMCI0_TIMEOUT 1000
30 BOOLEAN
MciIsPowerOn() {
31 return ((MmioRead32(MCI_POWER_CONTROL_REG
) & 0x3) == MCI_POWER_ON
);
34 EFI_STATUS
MciInitialize() {
35 MCI_TRACE("MciInitialize()");
39 BOOLEAN
MciIsCardPresent() {
40 return (MmioRead32(FixedPcdGet32(PcdPL180SysMciRegAddress
)) & 1);
43 BOOLEAN
MciIsReadOnly() {
44 return (MmioRead32(FixedPcdGet32(PcdPL180SysMciRegAddress
)) & 2);
47 // Convert block size to 2^n
48 UINT32
GetPow2BlockLen(UINT32 BlockLen
) {
55 Loop
= (Loop
>> 1) & 0xFFFF;
57 } while (Pow2BlockLen
&& (!(Loop
& BlockLen
)));
62 VOID
MciPrepareDataPath(UINTN TransferDirection
) {
63 // Set Data Length & Data Timer
64 MmioWrite32(MCI_DATA_TIMER_REG
,0xFFFFFFF);
65 MmioWrite32(MCI_DATA_LENGTH_REG
,MMCI0_BLOCKLEN
);
68 //Note: we are using a hardcoded BlockLen (=512). If we decide to use a variable size, we could
69 // compute the pow2 of BlockLen with the above function GetPow2BlockLen()
70 MmioWrite32(MCI_DATA_CTL_REG
, MCI_DATACTL_ENABLE
| TransferDirection
| (MMCI0_POW2_BLOCKLEN
<< 4));
72 MmioWrite32(MCI_DATA_CTL_REG
, MCI_DATACTL_ENABLE
| TransferDirection
| MCI_DATACTL_STREAM_TRANS
);
76 EFI_STATUS
MciSendCommand(MMC_CMD MmcCmd
, UINT32 Argument
) {
81 if ((MmcCmd
== MMC_CMD17
) || (MmcCmd
== MMC_CMD11
)) {
82 MciPrepareDataPath(MCI_DATACTL_CARD_TO_CONT
);
83 } else if ((MmcCmd
== MMC_CMD24
) || (MmcCmd
== MMC_CMD20
)) {
84 MciPrepareDataPath(MCI_DATACTL_CONT_TO_CARD
);
87 // Create Command for PL180
89 if (MmcCmd
& MMC_CMD_WAIT_RESPONSE
)
90 Cmd
|= MCI_CPSM_WAIT_RESPONSE
;
91 if (MmcCmd
& MMC_CMD_LONG_RESPONSE
)
92 Cmd
|= MCI_CPSM_LONG_RESPONSE
;
94 MmioWrite32(MCI_CLEAR_STATUS_REG
,0x5FFF);
95 MmioWrite32(MCI_ARGUMENT_REG
,Argument
);
96 MmioWrite32(MCI_COMMAND_REG
,Cmd
);
99 if (Cmd
& MCI_CPSM_WAIT_RESPONSE
) {
100 Status
= MmioRead32(MCI_STATUS_REG
);
101 while (!(Status
& (MCI_STATUS_CMD_RESPEND
| MCI_STATUS_CMD_CMDCRCFAIL
| MCI_STATUS_CMD_CMDTIMEOUT
)) && Timer
) {
102 //NanoSecondDelay(10);
103 Status
= MmioRead32(MCI_STATUS_REG
);
107 if ((Timer
== 0) || (Status
== MCI_STATUS_CMD_CMDTIMEOUT
)) {
108 //DEBUG ((EFI_D_ERROR, "MciSendCommand(CmdIndex:%d) TIMEOUT! Response:0x%X Status:0x%X\n",Cmd & 0x3F,MmioRead32(MCI_RESPONSE0_REG),Status));
110 } else if (!((Cmd
& 0x3F) == INDX(1)) && (Status
& MCI_STATUS_CMD_CMDCRCFAIL
)) {
111 // The CMD1 does not contain CRC. We should ignore the CRC failed Status.
112 return EFI_CRC_ERROR
;
117 Status
= MmioRead32(MCI_STATUS_REG
);
118 while (!(Status
& MCI_STATUS_CMD_SENT
) && Timer
) {
119 //NanoSecondDelay(10);
120 Status
= MmioRead32(MCI_STATUS_REG
);
125 //DEBUG ((EFI_D_ERROR, "MciSendCommand(CmdIndex:%d) TIMEOUT2! 0x%X\n",Cmd & 0x3F,MmioRead32(MCI_RESPONSE0_REG)));
133 EFI_STATUS
MciReceiveResponse(MMC_RESPONSE_TYPE Type
, UINT32
* Buffer
) {
134 if (Buffer
== NULL
) {
135 return EFI_INVALID_PARAMETER
;
138 if ((Type
== MMC_RESPONSE_TYPE_R1
) || (Type
== MMC_RESPONSE_TYPE_R1b
) ||
139 (Type
== MMC_RESPONSE_TYPE_R3
) || (Type
== MMC_RESPONSE_TYPE_R6
) ||
140 (Type
== MMC_RESPONSE_TYPE_R7
)) {
141 Buffer
[0] = MmioRead32(MCI_RESPONSE0_REG
);
142 Buffer
[1] = MmioRead32(MCI_RESPONSE1_REG
);
143 } else if (Type
== MMC_RESPONSE_TYPE_R2
) {
144 Buffer
[0] = MmioRead32(MCI_RESPONSE0_REG
);
145 Buffer
[1] = MmioRead32(MCI_RESPONSE1_REG
);
146 Buffer
[2] = MmioRead32(MCI_RESPONSE2_REG
);
147 Buffer
[3] = MmioRead32(MCI_RESPONSE3_REG
);
153 EFI_STATUS
MciReadBlockData(EFI_LBA Lba
, UINTN Length
, UINT32
* Buffer
) {
159 // Read data from the RX FIFO
161 Finish
= MMCI0_BLOCKLEN
/ 4;
162 Timer
= MMCI0_TIMEOUT
* 10;
164 // Read the Status flags
165 Status
= MmioRead32(MCI_STATUS_REG
);
166 // Do eight reads if possible else a single read
167 if (Status
& MCI_STATUS_CMD_RXFIFOHALFFULL
) {
168 Buffer
[Loop
] = MmioRead32(MCI_FIFO_REG
);
170 Buffer
[Loop
] = MmioRead32(MCI_FIFO_REG
);
172 Buffer
[Loop
] = MmioRead32(MCI_FIFO_REG
);
174 Buffer
[Loop
] = MmioRead32(MCI_FIFO_REG
);
176 Buffer
[Loop
] = MmioRead32(MCI_FIFO_REG
);
178 Buffer
[Loop
] = MmioRead32(MCI_FIFO_REG
);
180 Buffer
[Loop
] = MmioRead32(MCI_FIFO_REG
);
182 Buffer
[Loop
] = MmioRead32(MCI_FIFO_REG
);
185 else if (!(Status
& MCI_STATUS_CMD_RXFIFOEMPTY
)) {
186 Buffer
[Loop
] = MmioRead32(MCI_FIFO_REG
);
190 } while ((Loop
< Finish
) && Timer
);
193 DEBUG ((EFI_D_ERROR
, "MciReadBlockData: Timeout Status:0x%X Loop:%d // Finish:%d\n",MmioRead32(MCI_STATUS_REG
),Loop
,Finish
));
199 EFI_STATUS
MciWriteBlockData(EFI_LBA Lba
, UINTN Length
, UINT32
* Buffer
) {
205 // Write the data to the TX FIFO
207 Finish
= MMCI0_BLOCKLEN
/ 4;
208 Timer
= MMCI0_TIMEOUT
* 100;
210 // Read the Status flags
211 Status
= MmioRead32(MCI_STATUS_REG
);
213 // Do eight writes if possible else a single write
214 if (Status
& MCI_STATUS_CMD_TXFIFOHALFEMPTY
) {
215 MmioWrite32(MCI_FIFO_REG
, Buffer
[Loop
]);
217 MmioWrite32(MCI_FIFO_REG
, Buffer
[Loop
]);
219 MmioWrite32(MCI_FIFO_REG
, Buffer
[Loop
]);
221 MmioWrite32(MCI_FIFO_REG
, Buffer
[Loop
]);
223 MmioWrite32(MCI_FIFO_REG
, Buffer
[Loop
]);
225 MmioWrite32(MCI_FIFO_REG
, Buffer
[Loop
]);
227 MmioWrite32(MCI_FIFO_REG
, Buffer
[Loop
]);
229 MmioWrite32(MCI_FIFO_REG
, Buffer
[Loop
]);
232 else if (!(Status
& MCI_STATUS_CMD_TXFIFOFULL
)) {
233 MmioWrite32(MCI_FIFO_REG
, Buffer
[Loop
]);
238 } while ((Loop
< Finish
) && Timer
);
242 // Wait for FIFO to drain
243 Timer
= MMCI0_TIMEOUT
;
244 Status
= MmioRead32(MCI_STATUS_REG
);
247 while (((Status & MCI_STATUS_CMD_TXDONE) != MCI_STATUS_CMD_TXDONE) && Timer) {
250 while (((Status
& MCI_STATUS_CMD_DATAEND
) != MCI_STATUS_CMD_DATAEND
) && Timer
) {
253 Status
= MmioRead32(MCI_STATUS_REG
);
265 EFI_STATUS
MciNotifyState(MMC_STATE State
) {
269 case MmcInvalidState
:
272 case MmcHwInitializationState
:
273 // If device already turn on then restart it
274 Data32
= MmioRead32(MCI_POWER_CONTROL_REG
);
275 if ((Data32
& 0x2) == MCI_POWER_UP
) {
276 MCI_TRACE("MciNotifyState(MmcHwInitializationState): TurnOff MCI");
279 MmioWrite32(MCI_CLOCK_CONTROL_REG
, 0);
280 MmioWrite32(MCI_POWER_CONTROL_REG
, 0);
281 MicroSecondDelay(100);
284 MCI_TRACE("MciNotifyState(MmcHwInitializationState): TurnOn MCI");
286 // - 0x1D = 29 => should be the clock divider to be less than 400kHz at MCLK = 24Mhz
287 MmioWrite32(MCI_CLOCK_CONTROL_REG
,0x1D | MCI_CLOCK_ENABLE
| MCI_CLOCK_POWERSAVE
);
288 //MmioWrite32(MCI_CLOCK_CONTROL_REG,0x1D | MCI_CLOCK_ENABLE);
291 MmioWrite32(MCI_POWER_CONTROL_REG
,MCI_POWER_OPENDRAIN
| (15<<2));
292 MmioWrite32(MCI_POWER_CONTROL_REG
,MCI_POWER_ROD
| MCI_POWER_OPENDRAIN
| (15<<2) | MCI_POWER_UP
);
293 MicroSecondDelay(10);
294 MmioWrite32(MCI_POWER_CONTROL_REG
,MCI_POWER_ROD
| MCI_POWER_OPENDRAIN
| (15<<2) | MCI_POWER_ON
);
295 MicroSecondDelay(100);
297 // Set Data Length & Data Timer
298 MmioWrite32(MCI_DATA_TIMER_REG
,0xFFFFF);
299 MmioWrite32(MCI_DATA_LENGTH_REG
,8);
301 ASSERT((MmioRead32(MCI_POWER_CONTROL_REG
) & 0x3) == MCI_POWER_ON
);
304 MCI_TRACE("MciNotifyState(MmcIdleState)");
307 MCI_TRACE("MciNotifyState(MmcReadyState)");
309 case MmcIdentificationState
:
310 MCI_TRACE("MciNotifyState(MmcIdentificationState)");
312 case MmcStandByState
:
313 MCI_TRACE("MciNotifyState(MmcStandByState)");
315 // Enable MCICMD push-pull drive
316 MmioWrite32(MCI_POWER_CONTROL_REG
,MCI_POWER_ROD
| (15<<2) | MCI_POWER_ON
);
318 /*// Set MMCI0 clock to 4MHz (24MHz may be possible with cache enabled)
319 MmioWrite32(MCI_CLOCK_CONTROL_REG,0x02 | MCI_CLOCK_ENABLE | MCI_CLOCK_POWERSAVE);*/
320 // Set MMCI0 clock to 24MHz (by bypassing the divider)
321 MmioWrite32(MCI_CLOCK_CONTROL_REG
,MCI_CLOCK_BYPASS
| MCI_CLOCK_ENABLE
);
323 case MmcTransferState
:
324 //MCI_TRACE("MciNotifyState(MmcTransferState)");
326 case MmcSendingDataState
:
327 MCI_TRACE("MciNotifyState(MmcSendingDataState)");
329 case MmcReceiveDataState
:
330 MCI_TRACE("MciNotifyState(MmcReceiveDataState)");
332 case MmcProgrammingState
:
333 MCI_TRACE("MciNotifyState(MmcProgrammingState)");
335 case MmcDisconnectState
:
336 MCI_TRACE("MciNotifyState(MmcDisconnectState)");
344 EFI_GUID mPL180MciDevicePathGuid
= { 0x621b6fa5, 0x4dc1, 0x476f, 0xb9, 0xd8, 0x52, 0xc5, 0x57, 0xd8, 0x10, 0x70 };
346 EFI_STATUS
MciBuildDevicePath(EFI_DEVICE_PATH_PROTOCOL
**DevicePath
) {
347 EFI_DEVICE_PATH_PROTOCOL
*NewDevicePathNode
;
349 NewDevicePathNode
= CreateDeviceNode(HARDWARE_DEVICE_PATH
,HW_VENDOR_DP
,sizeof(VENDOR_DEVICE_PATH
));
350 CopyGuid(&((VENDOR_DEVICE_PATH
*)NewDevicePathNode
)->Guid
,&mPL180MciDevicePathGuid
);
352 *DevicePath
= NewDevicePathNode
;
356 EFI_MMC_HOST_PROTOCOL gMciHost
= {
368 PL180MciDxeInitialize (
369 IN EFI_HANDLE ImageHandle
,
370 IN EFI_SYSTEM_TABLE
*SystemTable
374 EFI_HANDLE Handle
= NULL
;
376 MCI_TRACE("PL180MciDxeInitialize()");
378 //Publish Component Name, BlockIO protocol interfaces
379 Status
= gBS
->InstallMultipleProtocolInterfaces (
381 &gEfiMmcHostProtocolGuid
, &gMciHost
,
384 ASSERT_EFI_ERROR (Status
);