]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/PL180MciDxe/PL180Mci.c
Sync up ArmPkg with patch from mailing list. Changed name of BdsLib.h to BdsUnixLib...
[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 MciIsPowerOn() {
31 return ((MmioRead32(MCI_POWER_CONTROL_REG) & 0x3) == MCI_POWER_ON);
32 }
33
34 EFI_STATUS MciInitialize() {
35 MCI_TRACE("MciInitialize()");
36 return EFI_SUCCESS;
37 }
38
39 BOOLEAN MciIsCardPresent() {
40 return (MmioRead32(FixedPcdGet32(PcdPL180SysMciRegAddress)) & 1);
41 }
42
43 BOOLEAN MciIsReadOnly() {
44 return (MmioRead32(FixedPcdGet32(PcdPL180SysMciRegAddress)) & 2);
45 }
46
47 // Convert block size to 2^n
48 UINT32 GetPow2BlockLen(UINT32 BlockLen) {
49 UINTN Loop;
50 UINTN Pow2BlockLen;
51
52 Loop = 0x8000;
53 Pow2BlockLen = 15;
54 do {
55 Loop = (Loop >> 1) & 0xFFFF;
56 Pow2BlockLen--;
57 } while (Pow2BlockLen && (!(Loop & BlockLen)));
58
59 return Pow2BlockLen;
60 }
61
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);
66
67 #ifndef USE_STREAM
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));
71 #else
72 MmioWrite32(MCI_DATA_CTL_REG, MCI_DATACTL_ENABLE | TransferDirection | MCI_DATACTL_STREAM_TRANS);
73 #endif
74 }
75
76 EFI_STATUS MciSendCommand(MMC_CMD MmcCmd, UINT32 Argument) {
77 UINT32 Status;
78 UINT32 Timer;
79 UINT32 Cmd;
80
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);
85 }
86
87 // Create Command for PL180
88 Cmd = INDX(MmcCmd);
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;
93
94 MmioWrite32(MCI_CLEAR_STATUS_REG,0x5FFF);
95 MmioWrite32(MCI_ARGUMENT_REG,Argument);
96 MmioWrite32(MCI_COMMAND_REG,Cmd);
97
98 Timer = 1000;
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);
104 Timer--;
105 }
106
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));
109 return EFI_TIMEOUT;
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;
113 } else {
114 return EFI_SUCCESS;
115 }
116 } else {
117 Status = MmioRead32(MCI_STATUS_REG);
118 while (!(Status & MCI_STATUS_CMD_SENT) && Timer) {
119 //NanoSecondDelay(10);
120 Status = MmioRead32(MCI_STATUS_REG);
121 Timer--;
122 }
123
124 if (Timer == 0) {
125 //DEBUG ((EFI_D_ERROR, "MciSendCommand(CmdIndex:%d) TIMEOUT2! 0x%X\n",Cmd & 0x3F,MmioRead32(MCI_RESPONSE0_REG)));
126 return EFI_TIMEOUT;
127 } else {
128 return EFI_SUCCESS;
129 }
130 }
131 }
132
133 EFI_STATUS MciReceiveResponse(MMC_RESPONSE_TYPE Type, UINT32* Buffer) {
134 if (Buffer == NULL) {
135 return EFI_INVALID_PARAMETER;
136 }
137
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);
148 }
149
150 return EFI_SUCCESS;
151 }
152
153 EFI_STATUS MciReadBlockData(EFI_LBA Lba, UINTN Length, UINT32* Buffer) {
154 UINTN Loop;
155 UINTN Finish;
156 UINTN Timer;
157 UINTN Status;
158
159 // Read data from the RX FIFO
160 Loop = 0;
161 Finish = MMCI0_BLOCKLEN / 4;
162 Timer = MMCI0_TIMEOUT * 10;
163 do {
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);
169 Loop++;
170 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
171 Loop++;
172 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
173 Loop++;
174 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
175 Loop++;
176 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
177 Loop++;
178 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
179 Loop++;
180 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
181 Loop++;
182 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
183 Loop++;
184 }
185 else if (!(Status & MCI_STATUS_CMD_RXFIFOEMPTY)) {
186 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);
187 Loop++;
188 } else
189 Timer--;
190 } while ((Loop < Finish) && Timer);
191
192 if (Timer == 0) {
193 DEBUG ((EFI_D_ERROR, "MciReadBlockData: Timeout Status:0x%X Loop:%d // Finish:%d\n",MmioRead32(MCI_STATUS_REG),Loop,Finish));
194 return EFI_TIMEOUT;
195 } else
196 return EFI_SUCCESS;
197 }
198
199 EFI_STATUS MciWriteBlockData(EFI_LBA Lba, UINTN Length, UINT32* Buffer) {
200 UINTN Loop;
201 UINTN Finish;
202 UINTN Timer;
203 UINTN Status;
204
205 // Write the data to the TX FIFO
206 Loop = 0;
207 Finish = MMCI0_BLOCKLEN / 4;
208 Timer = MMCI0_TIMEOUT * 100;
209 do {
210 // Read the Status flags
211 Status = MmioRead32(MCI_STATUS_REG);
212
213 // Do eight writes if possible else a single write
214 if (Status & MCI_STATUS_CMD_TXFIFOHALFEMPTY) {
215 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
216 Loop++;
217 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
218 Loop++;
219 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
220 Loop++;
221 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
222 Loop++;
223 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
224 Loop++;
225 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
226 Loop++;
227 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
228 Loop++;
229 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
230 Loop++;
231 }
232 else if (!(Status & MCI_STATUS_CMD_TXFIFOFULL)) {
233 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);
234 Loop++;
235 }
236 else
237 Timer--;
238 } while ((Loop < Finish) && Timer);
239
240 ASSERT(Timer > 0);
241
242 // Wait for FIFO to drain
243 Timer = MMCI0_TIMEOUT;
244 Status = MmioRead32(MCI_STATUS_REG);
245 /*#ifndef USE_STREAM
246 // Single block
247 while (((Status & MCI_STATUS_CMD_TXDONE) != MCI_STATUS_CMD_TXDONE) && Timer) {
248 #else*/
249 // Stream
250 while (((Status & MCI_STATUS_CMD_DATAEND) != MCI_STATUS_CMD_DATAEND) && Timer) {
251 //#endif
252 NanoSecondDelay(10);
253 Status = MmioRead32(MCI_STATUS_REG);
254 Timer--;
255 }
256
257 ASSERT(Timer > 0);
258
259 if (Timer == 0)
260 return EFI_TIMEOUT;
261 else
262 return EFI_SUCCESS;
263 }
264
265 EFI_STATUS MciNotifyState(MMC_STATE State) {
266 UINT32 Data32;
267
268 switch(State) {
269 case MmcInvalidState:
270 ASSERT(0);
271 break;
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");
277
278 // Turn off
279 MmioWrite32(MCI_CLOCK_CONTROL_REG, 0);
280 MmioWrite32(MCI_POWER_CONTROL_REG, 0);
281 MicroSecondDelay(100);
282 }
283
284 MCI_TRACE("MciNotifyState(MmcHwInitializationState): TurnOn MCI");
285 // Setup clock
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);
289
290 // Set the voltage
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);
296
297 // Set Data Length & Data Timer
298 MmioWrite32(MCI_DATA_TIMER_REG,0xFFFFF);
299 MmioWrite32(MCI_DATA_LENGTH_REG,8);
300
301 ASSERT((MmioRead32(MCI_POWER_CONTROL_REG) & 0x3) == MCI_POWER_ON);
302 break;
303 case MmcIdleState:
304 MCI_TRACE("MciNotifyState(MmcIdleState)");
305 break;
306 case MmcReadyState:
307 MCI_TRACE("MciNotifyState(MmcReadyState)");
308 break;
309 case MmcIdentificationState:
310 MCI_TRACE("MciNotifyState(MmcIdentificationState)");
311 break;
312 case MmcStandByState:
313 MCI_TRACE("MciNotifyState(MmcStandByState)");
314
315 // Enable MCICMD push-pull drive
316 MmioWrite32(MCI_POWER_CONTROL_REG,MCI_POWER_ROD | (15<<2) | MCI_POWER_ON);
317
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);
322 break;
323 case MmcTransferState:
324 //MCI_TRACE("MciNotifyState(MmcTransferState)");
325 break;
326 case MmcSendingDataState:
327 MCI_TRACE("MciNotifyState(MmcSendingDataState)");
328 break;
329 case MmcReceiveDataState:
330 MCI_TRACE("MciNotifyState(MmcReceiveDataState)");
331 break;
332 case MmcProgrammingState:
333 MCI_TRACE("MciNotifyState(MmcProgrammingState)");
334 break;
335 case MmcDisconnectState:
336 MCI_TRACE("MciNotifyState(MmcDisconnectState)");
337 break;
338 default:
339 ASSERT(0);
340 }
341 return EFI_SUCCESS;
342 }
343
344 EFI_GUID mPL180MciDevicePathGuid = { 0x621b6fa5, 0x4dc1, 0x476f, 0xb9, 0xd8, 0x52, 0xc5, 0x57, 0xd8, 0x10, 0x70 };
345
346 EFI_STATUS MciBuildDevicePath(EFI_DEVICE_PATH_PROTOCOL **DevicePath) {
347 EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;
348
349 NewDevicePathNode = CreateDeviceNode(HARDWARE_DEVICE_PATH,HW_VENDOR_DP,sizeof(VENDOR_DEVICE_PATH));
350 CopyGuid(&((VENDOR_DEVICE_PATH*)NewDevicePathNode)->Guid,&mPL180MciDevicePathGuid);
351
352 *DevicePath = NewDevicePathNode;
353 return EFI_SUCCESS;
354 }
355
356 EFI_MMC_HOST_PROTOCOL gMciHost = {
357 MciIsCardPresent,
358 MciIsReadOnly,
359 MciBuildDevicePath,
360 MciNotifyState,
361 MciSendCommand,
362 MciReceiveResponse,
363 MciReadBlockData,
364 MciWriteBlockData
365 };
366
367 EFI_STATUS
368 PL180MciDxeInitialize (
369 IN EFI_HANDLE ImageHandle,
370 IN EFI_SYSTEM_TABLE *SystemTable
371 )
372 {
373 EFI_STATUS Status;
374 EFI_HANDLE Handle = NULL;
375
376 MCI_TRACE("PL180MciDxeInitialize()");
377
378 //Publish Component Name, BlockIO protocol interfaces
379 Status = gBS->InstallMultipleProtocolInterfaces (
380 &Handle,
381 &gEfiMmcHostProtocolGuid, &gMciHost,
382 NULL
383 );
384 ASSERT_EFI_ERROR (Status);
385
386 return EFI_SUCCESS;
387 }