]> git.proxmox.com Git - mirror_edk2.git/blame - 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
CommitLineData
1bfda055 1/** @file\r
2 This file implement the MMC Host Protocol for the ARM PrimeCell PL180.\r
3\r
4 Copyright (c) 2011, ARM Limited. All rights reserved.\r
5 \r
6 This program and the accompanying materials \r
7 are licensed and made available under the terms and conditions of the BSD License \r
8 which accompanies this distribution. The full text of the license may be found at \r
9 http://opensource.org/licenses/bsd-license.php \r
10\r
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
13\r
14**/\r
15\r
16#include "PL180Mci.h"\r
17\r
18#include <Library/DevicePathLib.h>\r
19#include <Library/BaseMemoryLib.h>\r
20\r
21EFI_MMC_HOST_PROTOCOL *gpMmcHost;\r
22\r
23// Untested ...\r
24//#define USE_STREAM\r
25\r
26#define MMCI0_BLOCKLEN 512\r
27#define MMCI0_POW2_BLOCKLEN 9\r
28#define MMCI0_TIMEOUT 1000\r
29\r
30BOOLEAN MciIsPowerOn() {\r
31 return ((MmioRead32(MCI_POWER_CONTROL_REG) & 0x3) == MCI_POWER_ON);\r
32}\r
33\r
34EFI_STATUS MciInitialize() {\r
35 MCI_TRACE("MciInitialize()");\r
36 return EFI_SUCCESS;\r
37}\r
38\r
39BOOLEAN MciIsCardPresent() {\r
40 return (MmioRead32(FixedPcdGet32(PcdPL180SysMciRegAddress)) & 1);\r
41}\r
42\r
43BOOLEAN MciIsReadOnly() {\r
44 return (MmioRead32(FixedPcdGet32(PcdPL180SysMciRegAddress)) & 2);\r
45}\r
46\r
47// Convert block size to 2^n\r
48UINT32 GetPow2BlockLen(UINT32 BlockLen) {\r
49 UINTN Loop;\r
50 UINTN Pow2BlockLen;\r
51 \r
52 Loop = 0x8000;\r
53 Pow2BlockLen = 15;\r
54 do {\r
55 Loop = (Loop >> 1) & 0xFFFF;\r
56 Pow2BlockLen--;\r
57 } while (Pow2BlockLen && (!(Loop & BlockLen)));\r
58\r
59 return Pow2BlockLen;\r
60}\r
61\r
62VOID MciPrepareDataPath(UINTN TransferDirection) {\r
63 // Set Data Length & Data Timer\r
64 MmioWrite32(MCI_DATA_TIMER_REG,0xFFFFFFF);\r
65 MmioWrite32(MCI_DATA_LENGTH_REG,MMCI0_BLOCKLEN);\r
66\r
67#ifndef USE_STREAM\r
68 //Note: we are using a hardcoded BlockLen (=512). If we decide to use a variable size, we could\r
69 // compute the pow2 of BlockLen with the above function GetPow2BlockLen()\r
70 MmioWrite32(MCI_DATA_CTL_REG, MCI_DATACTL_ENABLE | TransferDirection | (MMCI0_POW2_BLOCKLEN << 4));\r
71#else\r
72 MmioWrite32(MCI_DATA_CTL_REG, MCI_DATACTL_ENABLE | TransferDirection | MCI_DATACTL_STREAM_TRANS);\r
73#endif\r
74}\r
75\r
76EFI_STATUS MciSendCommand(MMC_CMD MmcCmd, UINT32 Argument) {\r
77 UINT32 Status;\r
78 UINT32 Timer;\r
79 UINT32 Cmd;\r
80\r
81 if ((MmcCmd == MMC_CMD17) || (MmcCmd == MMC_CMD11)) {\r
82 MciPrepareDataPath(MCI_DATACTL_CARD_TO_CONT);\r
83 } else if ((MmcCmd == MMC_CMD24) || (MmcCmd == MMC_CMD20)) {\r
84 MciPrepareDataPath(MCI_DATACTL_CONT_TO_CARD);\r
85 }\r
86\r
87 // Create Command for PL180\r
88 Cmd = INDX(MmcCmd);\r
89 if (MmcCmd & MMC_CMD_WAIT_RESPONSE)\r
90 Cmd |= MCI_CPSM_WAIT_RESPONSE;\r
91 if (MmcCmd & MMC_CMD_LONG_RESPONSE)\r
92 Cmd |= MCI_CPSM_LONG_RESPONSE;\r
93\r
94 MmioWrite32(MCI_CLEAR_STATUS_REG,0x5FFF);\r
95 MmioWrite32(MCI_ARGUMENT_REG,Argument);\r
96 MmioWrite32(MCI_COMMAND_REG,Cmd);\r
97\r
98 Timer = 1000;\r
99 if (Cmd & MCI_CPSM_WAIT_RESPONSE) {\r
100 Status = MmioRead32(MCI_STATUS_REG);\r
101 while (!(Status & (MCI_STATUS_CMD_RESPEND | MCI_STATUS_CMD_CMDCRCFAIL | MCI_STATUS_CMD_CMDTIMEOUT)) && Timer) {\r
102 //NanoSecondDelay(10);\r
103 Status = MmioRead32(MCI_STATUS_REG);\r
104 Timer--;\r
105 }\r
106\r
107 if ((Timer == 0) || (Status == MCI_STATUS_CMD_CMDTIMEOUT)) {\r
108 //DEBUG ((EFI_D_ERROR, "MciSendCommand(CmdIndex:%d) TIMEOUT! Response:0x%X Status:0x%X\n",Cmd & 0x3F,MmioRead32(MCI_RESPONSE0_REG),Status));\r
109 return EFI_TIMEOUT;\r
110 } else if (!((Cmd & 0x3F) == INDX(1)) && (Status & MCI_STATUS_CMD_CMDCRCFAIL)) {\r
111 // The CMD1 does not contain CRC. We should ignore the CRC failed Status.\r
112 return EFI_CRC_ERROR;\r
113 } else {\r
114 return EFI_SUCCESS;\r
115 }\r
116 } else {\r
117 Status = MmioRead32(MCI_STATUS_REG);\r
118 while (!(Status & MCI_STATUS_CMD_SENT) && Timer) {\r
119 //NanoSecondDelay(10);\r
120 Status = MmioRead32(MCI_STATUS_REG);\r
121 Timer--;\r
122 }\r
123\r
124 if (Timer == 0) {\r
125 //DEBUG ((EFI_D_ERROR, "MciSendCommand(CmdIndex:%d) TIMEOUT2! 0x%X\n",Cmd & 0x3F,MmioRead32(MCI_RESPONSE0_REG)));\r
126 return EFI_TIMEOUT;\r
127 } else {\r
128 return EFI_SUCCESS;\r
129 }\r
130 }\r
131}\r
132\r
133EFI_STATUS MciReceiveResponse(MMC_RESPONSE_TYPE Type, UINT32* Buffer) {\r
134 if (Buffer == NULL) {\r
135 return EFI_INVALID_PARAMETER;\r
136 }\r
137\r
138 if ((Type == MMC_RESPONSE_TYPE_R1) || (Type == MMC_RESPONSE_TYPE_R1b) ||\r
139 (Type == MMC_RESPONSE_TYPE_R3) || (Type == MMC_RESPONSE_TYPE_R6) ||\r
140 (Type == MMC_RESPONSE_TYPE_R7)) {\r
141 Buffer[0] = MmioRead32(MCI_RESPONSE0_REG);\r
142 Buffer[1] = MmioRead32(MCI_RESPONSE1_REG);\r
143 } else if (Type == MMC_RESPONSE_TYPE_R2) {\r
144 Buffer[0] = MmioRead32(MCI_RESPONSE0_REG);\r
145 Buffer[1] = MmioRead32(MCI_RESPONSE1_REG);\r
146 Buffer[2] = MmioRead32(MCI_RESPONSE2_REG);\r
147 Buffer[3] = MmioRead32(MCI_RESPONSE3_REG);\r
148 }\r
149\r
150 return EFI_SUCCESS;\r
151}\r
152\r
153EFI_STATUS MciReadBlockData(EFI_LBA Lba, UINTN Length, UINT32* Buffer) {\r
154 UINTN Loop;\r
155 UINTN Finish;\r
156 UINTN Timer;\r
157 UINTN Status;\r
158\r
159 // Read data from the RX FIFO\r
160 Loop = 0;\r
161 Finish = MMCI0_BLOCKLEN / 4;\r
162 Timer = MMCI0_TIMEOUT * 10;\r
163 do {\r
164 // Read the Status flags\r
165 Status = MmioRead32(MCI_STATUS_REG);\r
166 // Do eight reads if possible else a single read\r
167 if (Status & MCI_STATUS_CMD_RXFIFOHALFFULL) {\r
168 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);\r
169 Loop++;\r
170 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);\r
171 Loop++;\r
172 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);\r
173 Loop++;\r
174 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);\r
175 Loop++;\r
176 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);\r
177 Loop++;\r
178 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);\r
179 Loop++;\r
180 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);\r
181 Loop++;\r
182 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);\r
183 Loop++;\r
184 }\r
185 else if (!(Status & MCI_STATUS_CMD_RXFIFOEMPTY)) {\r
186 Buffer[Loop] = MmioRead32(MCI_FIFO_REG);\r
187 Loop++;\r
188 } else\r
189 Timer--;\r
190 } while ((Loop < Finish) && Timer);\r
191\r
192 if (Timer == 0) {\r
193 DEBUG ((EFI_D_ERROR, "MciReadBlockData: Timeout Status:0x%X Loop:%d // Finish:%d\n",MmioRead32(MCI_STATUS_REG),Loop,Finish));\r
194 return EFI_TIMEOUT;\r
195 } else\r
196 return EFI_SUCCESS;\r
197}\r
198\r
199EFI_STATUS MciWriteBlockData(EFI_LBA Lba, UINTN Length, UINT32* Buffer) {\r
200 UINTN Loop;\r
201 UINTN Finish;\r
202 UINTN Timer;\r
203 UINTN Status;\r
204\r
205 // Write the data to the TX FIFO\r
206 Loop = 0;\r
207 Finish = MMCI0_BLOCKLEN / 4;\r
208 Timer = MMCI0_TIMEOUT * 100;\r
209 do {\r
210 // Read the Status flags\r
211 Status = MmioRead32(MCI_STATUS_REG);\r
212\r
213 // Do eight writes if possible else a single write\r
214 if (Status & MCI_STATUS_CMD_TXFIFOHALFEMPTY) {\r
215 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);\r
216 Loop++;\r
217 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);\r
218 Loop++;\r
219 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);\r
220 Loop++;\r
221 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);\r
222 Loop++;\r
223 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);\r
224 Loop++;\r
225 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);\r
226 Loop++;\r
227 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);\r
228 Loop++;\r
229 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);\r
230 Loop++;\r
231 }\r
232 else if (!(Status & MCI_STATUS_CMD_TXFIFOFULL)) {\r
233 MmioWrite32(MCI_FIFO_REG, Buffer[Loop]);\r
234 Loop++;\r
235 }\r
236 else\r
237 Timer--;\r
238 } while ((Loop < Finish) && Timer);\r
239\r
240 ASSERT(Timer > 0);\r
241\r
242 // Wait for FIFO to drain\r
243 Timer = MMCI0_TIMEOUT;\r
244 Status = MmioRead32(MCI_STATUS_REG);\r
245/*#ifndef USE_STREAM\r
246 // Single block\r
247 while (((Status & MCI_STATUS_CMD_TXDONE) != MCI_STATUS_CMD_TXDONE) && Timer) {\r
248#else*/\r
249 // Stream\r
250 while (((Status & MCI_STATUS_CMD_DATAEND) != MCI_STATUS_CMD_DATAEND) && Timer) {\r
251//#endif\r
252 NanoSecondDelay(10);\r
253 Status = MmioRead32(MCI_STATUS_REG);\r
254 Timer--;\r
255 }\r
256\r
257 ASSERT(Timer > 0);\r
258\r
259 if (Timer == 0)\r
260 return EFI_TIMEOUT;\r
261 else\r
262 return EFI_SUCCESS;\r
263}\r
264\r
265EFI_STATUS MciNotifyState(MMC_STATE State) {\r
266 UINT32 Data32;\r
267\r
268 switch(State) {\r
269 case MmcInvalidState:\r
270 ASSERT(0);\r
271 break;\r
272 case MmcHwInitializationState:\r
273 // If device already turn on then restart it\r
274 Data32 = MmioRead32(MCI_POWER_CONTROL_REG);\r
275 if ((Data32 & 0x2) == MCI_POWER_UP) {\r
276 MCI_TRACE("MciNotifyState(MmcHwInitializationState): TurnOff MCI");\r
277\r
278 // Turn off\r
279 MmioWrite32(MCI_CLOCK_CONTROL_REG, 0);\r
280 MmioWrite32(MCI_POWER_CONTROL_REG, 0);\r
281 MicroSecondDelay(100);\r
282 }\r
283 \r
284 MCI_TRACE("MciNotifyState(MmcHwInitializationState): TurnOn MCI");\r
285 // Setup clock\r
286 // - 0x1D = 29 => should be the clock divider to be less than 400kHz at MCLK = 24Mhz\r
287 MmioWrite32(MCI_CLOCK_CONTROL_REG,0x1D | MCI_CLOCK_ENABLE | MCI_CLOCK_POWERSAVE);\r
288 //MmioWrite32(MCI_CLOCK_CONTROL_REG,0x1D | MCI_CLOCK_ENABLE);\r
289\r
290 // Set the voltage\r
291 MmioWrite32(MCI_POWER_CONTROL_REG,MCI_POWER_OPENDRAIN | (15<<2));\r
292 MmioWrite32(MCI_POWER_CONTROL_REG,MCI_POWER_ROD | MCI_POWER_OPENDRAIN | (15<<2) | MCI_POWER_UP);\r
293 MicroSecondDelay(10);\r
294 MmioWrite32(MCI_POWER_CONTROL_REG,MCI_POWER_ROD | MCI_POWER_OPENDRAIN | (15<<2) | MCI_POWER_ON);\r
295 MicroSecondDelay(100);\r
296\r
297 // Set Data Length & Data Timer\r
298 MmioWrite32(MCI_DATA_TIMER_REG,0xFFFFF);\r
299 MmioWrite32(MCI_DATA_LENGTH_REG,8);\r
300\r
301 ASSERT((MmioRead32(MCI_POWER_CONTROL_REG) & 0x3) == MCI_POWER_ON);\r
302 break;\r
303 case MmcIdleState:\r
304 MCI_TRACE("MciNotifyState(MmcIdleState)");\r
305 break;\r
306 case MmcReadyState:\r
307 MCI_TRACE("MciNotifyState(MmcReadyState)");\r
308 break;\r
309 case MmcIdentificationState:\r
310 MCI_TRACE("MciNotifyState(MmcIdentificationState)");\r
311 break;\r
312 case MmcStandByState:\r
313 MCI_TRACE("MciNotifyState(MmcStandByState)");\r
314 \r
315 // Enable MCICMD push-pull drive\r
316 MmioWrite32(MCI_POWER_CONTROL_REG,MCI_POWER_ROD | (15<<2) | MCI_POWER_ON);\r
317\r
318 /*// Set MMCI0 clock to 4MHz (24MHz may be possible with cache enabled)\r
319 MmioWrite32(MCI_CLOCK_CONTROL_REG,0x02 | MCI_CLOCK_ENABLE | MCI_CLOCK_POWERSAVE);*/\r
320 // Set MMCI0 clock to 24MHz (by bypassing the divider)\r
321 MmioWrite32(MCI_CLOCK_CONTROL_REG,MCI_CLOCK_BYPASS | MCI_CLOCK_ENABLE);\r
322 break;\r
323 case MmcTransferState:\r
324 //MCI_TRACE("MciNotifyState(MmcTransferState)");\r
325 break;\r
326 case MmcSendingDataState:\r
327 MCI_TRACE("MciNotifyState(MmcSendingDataState)");\r
328 break;\r
329 case MmcReceiveDataState:\r
330 MCI_TRACE("MciNotifyState(MmcReceiveDataState)");\r
331 break;\r
332 case MmcProgrammingState:\r
333 MCI_TRACE("MciNotifyState(MmcProgrammingState)");\r
334 break;\r
335 case MmcDisconnectState:\r
336 MCI_TRACE("MciNotifyState(MmcDisconnectState)");\r
337 break;\r
338 default:\r
339 ASSERT(0);\r
340 }\r
341 return EFI_SUCCESS;\r
342}\r
343\r
344EFI_GUID mPL180MciDevicePathGuid = { 0x621b6fa5, 0x4dc1, 0x476f, 0xb9, 0xd8, 0x52, 0xc5, 0x57, 0xd8, 0x10, 0x70 };\r
345\r
346EFI_STATUS MciBuildDevicePath(EFI_DEVICE_PATH_PROTOCOL **DevicePath) {\r
347 EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;\r
348\r
349 NewDevicePathNode = CreateDeviceNode(HARDWARE_DEVICE_PATH,HW_VENDOR_DP,sizeof(VENDOR_DEVICE_PATH));\r
350 CopyGuid(&((VENDOR_DEVICE_PATH*)NewDevicePathNode)->Guid,&mPL180MciDevicePathGuid);\r
351 \r
352 *DevicePath = NewDevicePathNode;\r
353 return EFI_SUCCESS;\r
354}\r
355\r
356EFI_MMC_HOST_PROTOCOL gMciHost = {\r
357 MciIsCardPresent,\r
358 MciIsReadOnly,\r
359 MciBuildDevicePath,\r
360 MciNotifyState,\r
361 MciSendCommand,\r
362 MciReceiveResponse,\r
363 MciReadBlockData,\r
364 MciWriteBlockData\r
365};\r
366\r
367EFI_STATUS\r
368PL180MciDxeInitialize (\r
369 IN EFI_HANDLE ImageHandle,\r
370 IN EFI_SYSTEM_TABLE *SystemTable\r
371 )\r
372{\r
373 EFI_STATUS Status;\r
374 EFI_HANDLE Handle = NULL;\r
375\r
376 MCI_TRACE("PL180MciDxeInitialize()");\r
377\r
378 //Publish Component Name, BlockIO protocol interfaces\r
379 Status = gBS->InstallMultipleProtocolInterfaces (\r
380 &Handle, \r
381 &gEfiMmcHostProtocolGuid, &gMciHost,\r
382 NULL\r
383 );\r
384 ASSERT_EFI_ERROR (Status);\r
385\r
386 return EFI_SUCCESS;\r
387}\r