• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2 *
3 *  Copyright (c) 2011-2015, ARM Limited. All rights reserved.
4 *
5 *  This program and the accompanying materials
6 *  are licensed and made available under the terms and conditions of the BSD License
7 *  which accompanies this distribution.  The full text of the license may be found at
8 *  http://opensource.org/licenses/bsd-license.php
9 *
10 *  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 *  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 *
13 **/
14 
15 #include <Library/BaseMemoryLib.h>
16 
17 #include "Mmc.h"
18 
19 EFI_STATUS
MmcNotifyState(IN MMC_HOST_INSTANCE * MmcHostInstance,IN MMC_STATE State)20 MmcNotifyState (
21   IN MMC_HOST_INSTANCE *MmcHostInstance,
22   IN MMC_STATE State
23   )
24 {
25   MmcHostInstance->State = State;
26   return MmcHostInstance->MmcHost->NotifyState (MmcHostInstance->MmcHost, State);
27 }
28 
29 EFI_STATUS
30 EFIAPI
MmcGetCardStatus(IN MMC_HOST_INSTANCE * MmcHostInstance)31 MmcGetCardStatus (
32   IN MMC_HOST_INSTANCE     *MmcHostInstance
33   )
34 {
35   EFI_STATUS              Status;
36   UINT32                  Response[4];
37   UINTN                   CmdArg;
38   EFI_MMC_HOST_PROTOCOL   *MmcHost;
39 
40   Status = EFI_SUCCESS;
41   MmcHost = MmcHostInstance->MmcHost;
42   CmdArg = 0;
43 
44   if (MmcHost == NULL) {
45     return EFI_INVALID_PARAMETER;
46   }
47   if (MmcHostInstance->State != MmcHwInitializationState) {
48     //Get the Status of the card.
49     CmdArg = MmcHostInstance->CardInfo.RCA << 16;
50     Status = MmcHost->SendCommand (MmcHost, MMC_CMD13, CmdArg);
51     if (EFI_ERROR (Status)) {
52       DEBUG ((EFI_D_ERROR, "MmcGetCardStatus(MMC_CMD13): Error and Status = %r\n", Status));
53       return Status;
54     }
55 
56     //Read Response
57     MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
58     PrintResponseR1 (Response[0]);
59   }
60 
61   return Status;
62 }
63 
64 EFI_STATUS
65 EFIAPI
MmcReset(IN EFI_BLOCK_IO_PROTOCOL * This,IN BOOLEAN ExtendedVerification)66 MmcReset (
67   IN EFI_BLOCK_IO_PROTOCOL    *This,
68   IN BOOLEAN                  ExtendedVerification
69   )
70 {
71   MMC_HOST_INSTANCE       *MmcHostInstance;
72 
73   MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
74 
75   if (MmcHostInstance->MmcHost == NULL) {
76     // Nothing to do
77     return EFI_SUCCESS;
78   }
79 
80   // If a card is not present then clear all media settings
81   if (!MmcHostInstance->MmcHost->IsCardPresent (MmcHostInstance->MmcHost)) {
82     MmcHostInstance->BlockIo.Media->MediaPresent = FALSE;
83     MmcHostInstance->BlockIo.Media->LastBlock    = 0;
84     MmcHostInstance->BlockIo.Media->BlockSize    = 512;  // Should be zero but there is a bug in DiskIo
85     MmcHostInstance->BlockIo.Media->ReadOnly     = FALSE;
86 
87     // Indicate that the driver requires initialization
88     MmcHostInstance->State = MmcHwInitializationState;
89 
90     return EFI_SUCCESS;
91   }
92 
93   // Implement me. Either send a CMD0 (could not work for some MMC host) or just turn off/turn
94   //      on power and restart Identification mode
95   return EFI_SUCCESS;
96 }
97 
98 EFI_STATUS
MmcDetectCard(EFI_MMC_HOST_PROTOCOL * MmcHost)99 MmcDetectCard (
100   EFI_MMC_HOST_PROTOCOL     *MmcHost
101   )
102 {
103   if (!MmcHost->IsCardPresent (MmcHost)) {
104     return EFI_NO_MEDIA;
105   } else {
106     return EFI_SUCCESS;
107   }
108 }
109 
110 EFI_STATUS
MmcStopTransmission(EFI_MMC_HOST_PROTOCOL * MmcHost)111 MmcStopTransmission (
112   EFI_MMC_HOST_PROTOCOL     *MmcHost
113   )
114 {
115   EFI_STATUS              Status;
116   UINT32                  Response[4];
117   // Command 12 - Stop transmission (ends read or write)
118   // Normally only needed for streaming transfers or after error.
119   Status = MmcHost->SendCommand (MmcHost, MMC_CMD12, 0);
120   if (!EFI_ERROR (Status)) {
121     MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1b, Response);
122   }
123   return Status;
124 }
125 
126 #define MMCI0_BLOCKLEN 512
127 #define MMCI0_TIMEOUT  10000
128 
129 STATIC
130 EFI_STATUS
MmcTransferBlock(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINTN Cmd,IN UINTN Transfer,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,OUT VOID * Buffer)131 MmcTransferBlock (
132   IN EFI_BLOCK_IO_PROTOCOL    *This,
133   IN UINTN                    Cmd,
134   IN UINTN                    Transfer,
135   IN UINT32                   MediaId,
136   IN EFI_LBA                  Lba,
137   IN UINTN                    BufferSize,
138   OUT VOID                    *Buffer
139   )
140 {
141   EFI_STATUS              Status;
142   UINTN                   CmdArg;
143   INTN                    Timeout;
144   UINT32                  Response[4];
145   MMC_HOST_INSTANCE       *MmcHostInstance;
146   EFI_MMC_HOST_PROTOCOL   *MmcHost;
147 
148   MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
149   MmcHost = MmcHostInstance->MmcHost;
150 
151   //Set command argument based on the card access mode (Byte mode or Block mode)
152   if ((MmcHostInstance->CardInfo.OCRData.AccessMode & MMC_OCR_ACCESS_MASK) ==
153       MMC_OCR_ACCESS_SECTOR) {
154     CmdArg = Lba;
155   } else {
156     CmdArg = Lba * This->Media->BlockSize;
157   }
158 
159   Status = MmcHost->SendCommand (MmcHost, Cmd, CmdArg);
160   if (EFI_ERROR (Status)) {
161     DEBUG ((EFI_D_ERROR, "%a(MMC_CMD%d): Error %r\n", __func__, Cmd, Status));
162     return Status;
163   }
164 
165   if (Transfer == MMC_IOBLOCKS_READ) {
166     // Read Data
167     Status = MmcHost->ReadBlockData (MmcHost, Lba, BufferSize, Buffer);
168     if (EFI_ERROR (Status)) {
169       DEBUG ((EFI_D_BLKIO, "%a(): Error Read Block Data and Status = %r\n", __func__, Status));
170       MmcStopTransmission (MmcHost);
171       return Status;
172     }
173     Status = MmcNotifyState (MmcHostInstance, MmcProgrammingState);
174     if (EFI_ERROR (Status)) {
175       DEBUG ((EFI_D_ERROR, "%a() : Error MmcProgrammingState\n", __func__));
176       return Status;
177     }
178   } else {
179     // Write Data
180     Status = MmcHost->WriteBlockData (MmcHost, Lba, BufferSize, Buffer);
181     if (EFI_ERROR (Status)) {
182       DEBUG ((EFI_D_BLKIO, "%a(): Error Write Block Data and Status = %r\n", __func__, Status));
183       MmcStopTransmission (MmcHost);
184       return Status;
185     }
186   }
187 
188   // Command 13 - Read status and wait for programming to complete (return to tran)
189   Timeout = MMCI0_TIMEOUT;
190   CmdArg = MmcHostInstance->CardInfo.RCA << 16;
191   Response[0] = 0;
192   while(!(Response[0] & MMC_R0_READY_FOR_DATA)
193         && (MMC_R0_CURRENTSTATE (Response) != MMC_R0_STATE_TRAN)
194         && Timeout--) {
195     Status = MmcHost->SendCommand (MmcHost, MMC_CMD13, CmdArg);
196     if (!EFI_ERROR (Status)) {
197       MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
198       if (Response[0] & MMC_R0_READY_FOR_DATA) {
199         break;  // Prevents delay once finished
200       }
201     }
202   }
203 
204   if (BufferSize > This->Media->BlockSize) {
205     Status = MmcHost->SendCommand (MmcHost, MMC_CMD12, 0);
206     if (EFI_ERROR (Status)) {
207       DEBUG ((EFI_D_BLKIO, "%a(): Error and Status:%r\n", __func__, Status));
208     }
209   }
210 
211   Status = MmcNotifyState (MmcHostInstance, MmcTransferState);
212   if (EFI_ERROR (Status)) {
213     DEBUG ((EFI_D_ERROR, "MmcIoBlocks() : Error MmcTransferState\n"));
214     return Status;
215   }
216   return Status;
217 }
218 
219 EFI_STATUS
MmcIoBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINTN Transfer,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,OUT VOID * Buffer)220 MmcIoBlocks (
221   IN EFI_BLOCK_IO_PROTOCOL    *This,
222   IN UINTN                    Transfer,
223   IN UINT32                   MediaId,
224   IN EFI_LBA                  Lba,
225   IN UINTN                    BufferSize,
226   OUT VOID                    *Buffer
227   )
228 {
229   UINT32                  Response[4];
230   EFI_STATUS              Status;
231   UINTN                   CmdArg;
232   INTN                    Timeout;
233   UINTN                   Cmd;
234   MMC_HOST_INSTANCE       *MmcHostInstance;
235   EFI_MMC_HOST_PROTOCOL   *MmcHost;
236   UINTN                   BytesRemainingToBeTransfered;
237   UINTN                   BlockCount;
238   UINTN                   ConsumeSize;
239 
240   BlockCount = 1;
241   MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
242   ASSERT (MmcHostInstance != NULL);
243   MmcHost = MmcHostInstance->MmcHost;
244   ASSERT (MmcHost);
245 
246   if (This->Media->MediaId != MediaId) {
247     return EFI_MEDIA_CHANGED;
248   }
249 
250   if ((MmcHost == NULL) || (Buffer == NULL)) {
251     return EFI_INVALID_PARAMETER;
252   }
253 
254   // Check if a Card is Present
255   if (!MmcHostInstance->BlockIo.Media->MediaPresent) {
256     return EFI_NO_MEDIA;
257   }
258 
259   if (MMC_HOST_HAS_ISMULTIBLOCK(MmcHost) && MmcHost->IsMultiBlock(MmcHost)) {
260     BlockCount = (BufferSize + This->Media->BlockSize - 1) / This->Media->BlockSize;
261   }
262 
263   // All blocks must be within the device
264   if ((Lba + (BufferSize / This->Media->BlockSize)) > (This->Media->LastBlock + 1)) {
265     return EFI_INVALID_PARAMETER;
266   }
267 
268   if ((Transfer == MMC_IOBLOCKS_WRITE) && (This->Media->ReadOnly == TRUE)) {
269     return EFI_WRITE_PROTECTED;
270   }
271 
272   // Reading 0 Byte is valid
273   if (BufferSize == 0) {
274     return EFI_SUCCESS;
275   }
276 
277   // The buffer size must be an exact multiple of the block size
278   if ((BufferSize % This->Media->BlockSize) != 0) {
279     return EFI_BAD_BUFFER_SIZE;
280   }
281 
282   // Check the alignment
283   if ((This->Media->IoAlign > 2) && (((UINTN)Buffer & (This->Media->IoAlign - 1)) != 0)) {
284     return EFI_INVALID_PARAMETER;
285   }
286 
287   BytesRemainingToBeTransfered = BufferSize;
288   while (BytesRemainingToBeTransfered > 0) {
289 
290     // Check if the Card is in Ready status
291     CmdArg = MmcHostInstance->CardInfo.RCA << 16;
292     Response[0] = 0;
293     Timeout = 20;
294     while(   (!(Response[0] & MMC_R0_READY_FOR_DATA))
295           && (MMC_R0_CURRENTSTATE (Response) != MMC_R0_STATE_TRAN)
296           && Timeout--) {
297       Status = MmcHost->SendCommand (MmcHost, MMC_CMD13, CmdArg);
298       if (!EFI_ERROR (Status)) {
299         MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
300       }
301     }
302 
303     if (0 == Timeout) {
304       DEBUG ((EFI_D_ERROR, "The Card is busy\n"));
305       return EFI_NOT_READY;
306     }
307 
308     if (Transfer == MMC_IOBLOCKS_READ) {
309       if (BlockCount == 1) {
310         // Read a single block
311         Cmd = MMC_CMD17;
312       } else {
313 	// Read multiple blocks
314 	Cmd = MMC_CMD18;
315       }
316     } else {
317       if (BlockCount == 1) {
318         // Write a single block
319         Cmd = MMC_CMD24;
320       } else {
321 	// Write multiple blocks
322 	Cmd = MMC_CMD25;
323       }
324     }
325 
326     ConsumeSize = BlockCount * This->Media->BlockSize;
327     if (BytesRemainingToBeTransfered < ConsumeSize) {
328       ConsumeSize = BytesRemainingToBeTransfered;
329     }
330     Status = MmcTransferBlock (This, Cmd, Transfer, MediaId, Lba, ConsumeSize, Buffer);
331     if (EFI_ERROR (Status)) {
332       DEBUG ((EFI_D_ERROR, "%a(): Failed to transfer block and Status:%r\n", __func__, Status));
333     }
334 
335     BytesRemainingToBeTransfered -= ConsumeSize;
336     if (BytesRemainingToBeTransfered > 0) {
337       Lba    += BlockCount;
338       Buffer = (UINT8 *)Buffer + ConsumeSize;
339     }
340   }
341 
342   return EFI_SUCCESS;
343 }
344 
345 EFI_STATUS
346 EFIAPI
MmcReadBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,OUT VOID * Buffer)347 MmcReadBlocks (
348   IN EFI_BLOCK_IO_PROTOCOL    *This,
349   IN UINT32                   MediaId,
350   IN EFI_LBA                  Lba,
351   IN UINTN                    BufferSize,
352   OUT VOID                    *Buffer
353   )
354 {
355   return MmcIoBlocks (This, MMC_IOBLOCKS_READ, MediaId, Lba, BufferSize, Buffer);
356 }
357 
358 EFI_STATUS
359 EFIAPI
MmcWriteBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,IN VOID * Buffer)360 MmcWriteBlocks (
361   IN EFI_BLOCK_IO_PROTOCOL    *This,
362   IN UINT32                   MediaId,
363   IN EFI_LBA                  Lba,
364   IN UINTN                    BufferSize,
365   IN VOID                     *Buffer
366   )
367 {
368   return MmcIoBlocks (This, MMC_IOBLOCKS_WRITE, MediaId, Lba, BufferSize, Buffer);
369 }
370 
371 EFI_STATUS
372 EFIAPI
MmcFlushBlocks(IN EFI_BLOCK_IO_PROTOCOL * This)373 MmcFlushBlocks (
374   IN EFI_BLOCK_IO_PROTOCOL  *This
375   )
376 {
377   return EFI_SUCCESS;
378 }
379 
380 EFI_STATUS
381 EFIAPI
MmcEraseBlocks(IN EFI_ERASE_BLOCK_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN OUT EFI_ERASE_BLOCK_TOKEN * Token,IN UINTN Size)382 MmcEraseBlocks (
383   IN EFI_ERASE_BLOCK_PROTOCOL       *This,
384   IN UINT32                         MediaId,
385   IN EFI_LBA                        Lba,
386   IN OUT EFI_ERASE_BLOCK_TOKEN      *Token,
387   IN UINTN                          Size
388   )
389 {
390   EFI_STATUS             Status;
391   MMC_HOST_INSTANCE      *MmcHostInstance;
392   EFI_MMC_HOST_PROTOCOL  *MmcHost;
393   UINT32                 Response[4];
394   UINTN                  CmdArg;
395 
396   MmcHostInstance = MMC_HOST_INSTANCE_FROM_ERASEBLK (This);
397   ASSERT (MmcHostInstance != NULL);
398   MmcHost = MmcHostInstance->MmcHost;
399   ASSERT (MmcHost);
400 
401   if (MmcHostInstance->BlockIo.Media->ReadOnly == TRUE)
402      return EFI_WRITE_PROTECTED;
403 
404 // EFI_DEVICE_ERROR, EFI_INVALID_PARAMETER
405 
406   if (!MmcHostInstance->BlockIo.Media->MediaPresent)
407      return EFI_NO_MEDIA;
408 
409   if (MmcHostInstance->BlockIo.Media->MediaId != MediaId)
410      return EFI_MEDIA_CHANGED;
411 
412   CmdArg = Lba;
413   Status = MmcHost->SendCommand (MmcHost, MMC_CMD35, CmdArg);
414   if (!EFI_ERROR (Status)) {
415      MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
416   }
417 
418   CmdArg = Lba + Size;
419   Status = MmcHost->SendCommand (MmcHost, MMC_CMD36, CmdArg);
420   if (!EFI_ERROR (Status)) {
421      MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
422   }
423 
424   CmdArg = 0;
425   Status = MmcHost->SendCommand (MmcHost, MMC_CMD38, CmdArg);
426   if (!EFI_ERROR (Status)) {
427      MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1b, Response);
428   }
429 
430   return EFI_SUCCESS;
431 }
432