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 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)130 MmcTransferBlock (
131   IN EFI_BLOCK_IO_PROTOCOL    *This,
132   IN UINTN                    Cmd,
133   IN UINTN                    Transfer,
134   IN UINT32                   MediaId,
135   IN EFI_LBA                  Lba,
136   IN UINTN                    BufferSize,
137   OUT VOID                    *Buffer
138   )
139 {
140   EFI_STATUS              Status;
141   UINTN                   CmdArg;
142   INTN                    Timeout;
143   UINT32                  Response[4];
144   MMC_HOST_INSTANCE       *MmcHostInstance;
145   EFI_MMC_HOST_PROTOCOL   *MmcHost;
146 
147   MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
148   MmcHost = MmcHostInstance->MmcHost;
149 
150   //Set command argument based on the card access mode (Byte mode or Block mode)
151   if (MmcHostInstance->CardInfo.OCRData.AccessMode & BIT1) {
152     CmdArg = Lba;
153   } else {
154     CmdArg = Lba * This->Media->BlockSize;
155   }
156 
157   Status = MmcHost->SendCommand (MmcHost, Cmd, CmdArg);
158   if (EFI_ERROR (Status)) {
159     DEBUG ((EFI_D_ERROR, "%a(MMC_CMD%d): Error %r\n", __func__, Cmd, Status));
160     return Status;
161   }
162 
163   if (Transfer == MMC_IOBLOCKS_READ) {
164     // Read Data
165     Status = MmcHost->ReadBlockData (MmcHost, Lba, BufferSize, Buffer);
166     if (EFI_ERROR (Status)) {
167       DEBUG ((EFI_D_BLKIO, "%a(): Error Read Block Data and Status = %r\n", __func__, Status));
168       MmcStopTransmission (MmcHost);
169       return Status;
170     }
171     Status = MmcNotifyState (MmcHostInstance, MmcProgrammingState);
172     if (EFI_ERROR (Status)) {
173       DEBUG ((EFI_D_ERROR, "%a() : Error MmcProgrammingState\n", __func__));
174       return Status;
175     }
176   } else {
177     // Write Data
178     Status = MmcHost->WriteBlockData (MmcHost, Lba, BufferSize, Buffer);
179     if (EFI_ERROR (Status)) {
180       DEBUG ((EFI_D_BLKIO, "%a(): Error Write Block Data and Status = %r\n", __func__, Status));
181       MmcStopTransmission (MmcHost);
182       return Status;
183     }
184   }
185 
186   // Command 13 - Read status and wait for programming to complete (return to tran)
187   Timeout = MMCI0_TIMEOUT;
188   CmdArg = MmcHostInstance->CardInfo.RCA << 16;
189   Response[0] = 0;
190   while(   (!(Response[0] & MMC_R0_READY_FOR_DATA))
191         && (MMC_R0_CURRENTSTATE (Response) != MMC_R0_STATE_TRAN)
192         && Timeout--) {
193     Status = MmcHost->SendCommand (MmcHost, MMC_CMD13, CmdArg);
194     if (!EFI_ERROR (Status)) {
195       MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
196       if ((Response[0] & MMC_R0_READY_FOR_DATA)) {
197         break;  // Prevents delay once finished
198       }
199     }
200     gBS->Stall (1);
201   }
202 
203   if (BufferSize > This->Media->BlockSize) {
204     Status = MmcHost->SendCommand (MmcHost, MMC_CMD12, 0);
205     if (EFI_ERROR (Status)) {
206       DEBUG ((EFI_D_BLKIO, "%a(): Error and Status:%r\n", __func__, Status));
207     }
208   }
209 
210   Status = MmcNotifyState (MmcHostInstance, MmcTransferState);
211   if (EFI_ERROR (Status)) {
212     DEBUG ((EFI_D_ERROR, "MmcIoBlocks() : Error MmcTransferState\n"));
213     return Status;
214   }
215   return Status;
216 }
217 
218 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)219 MmcIoBlocks (
220   IN EFI_BLOCK_IO_PROTOCOL    *This,
221   IN UINTN                    Transfer,
222   IN UINT32                   MediaId,
223   IN EFI_LBA                  Lba,
224   IN UINTN                    BufferSize,
225   OUT VOID                    *Buffer
226   )
227 {
228   UINT32                  Response[4];
229   EFI_STATUS              Status;
230   UINTN                   CmdArg;
231   INTN                    Timeout;
232   UINTN                   Cmd;
233   MMC_HOST_INSTANCE       *MmcHostInstance;
234   EFI_MMC_HOST_PROTOCOL   *MmcHost;
235   UINTN                   BytesRemainingToBeTransfered;
236   UINTN                   BlockCount;
237   UINTN                   ConsumeSize;
238 
239   BlockCount = 1;
240   MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
241   ASSERT (MmcHostInstance != NULL);
242   MmcHost = MmcHostInstance->MmcHost;
243   ASSERT (MmcHost);
244 
245   if (This->Media->MediaId != MediaId) {
246     return EFI_MEDIA_CHANGED;
247   }
248 
249   if ((MmcHost == NULL) || (Buffer == NULL)) {
250     return EFI_INVALID_PARAMETER;
251   }
252 
253   // Check if a Card is Present
254   if (!MmcHostInstance->BlockIo.Media->MediaPresent) {
255     return EFI_NO_MEDIA;
256   }
257 
258   if (MmcHost->IsMultiBlock && MmcHost->IsMultiBlock(MmcHost)) {
259     BlockCount = (BufferSize + This->Media->BlockSize - 1) / This->Media->BlockSize;
260   }
261 
262   // All blocks must be within the device
263   if ((Lba + (BufferSize / This->Media->BlockSize)) > (This->Media->LastBlock + 1)) {
264     return EFI_INVALID_PARAMETER;
265   }
266 
267   if ((Transfer == MMC_IOBLOCKS_WRITE) && (This->Media->ReadOnly == TRUE)) {
268     return EFI_WRITE_PROTECTED;
269   }
270 
271   // Reading 0 Byte is valid
272   if (BufferSize == 0) {
273     return EFI_SUCCESS;
274   }
275 
276   // The buffer size must be an exact multiple of the block size
277   if ((BufferSize % This->Media->BlockSize) != 0) {
278     return EFI_BAD_BUFFER_SIZE;
279   }
280 
281   // Check the alignment
282   if ((This->Media->IoAlign > 2) && (((UINTN)Buffer & (This->Media->IoAlign - 1)) != 0)) {
283     return EFI_INVALID_PARAMETER;
284   }
285 
286   BytesRemainingToBeTransfered = BufferSize;
287   while (BytesRemainingToBeTransfered > 0) {
288 
289     // Check if the Card is in Ready status
290     CmdArg = MmcHostInstance->CardInfo.RCA << 16;
291     Response[0] = 0;
292     Timeout = 20;
293     while(   (!(Response[0] & MMC_R0_READY_FOR_DATA))
294           && (MMC_R0_CURRENTSTATE (Response) != MMC_R0_STATE_TRAN)
295           && Timeout--) {
296       Status = MmcHost->SendCommand (MmcHost, MMC_CMD13, CmdArg);
297       if (!EFI_ERROR (Status)) {
298         MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
299       }
300     }
301 
302     if (0 == Timeout) {
303       DEBUG ((EFI_D_ERROR, "The Card is busy\n"));
304       return EFI_NOT_READY;
305     }
306 
307     if (Transfer == MMC_IOBLOCKS_READ) {
308       if (BlockCount == 1) {
309         // Read a single block
310         Cmd = MMC_CMD17;
311       } else {
312 	// Read multiple blocks
313 	Cmd = MMC_CMD18;
314       }
315     } else {
316       if (BlockCount == 1) {
317         // Write a single block
318         Cmd = MMC_CMD24;
319       } else {
320 	// Write multiple blocks
321 	Cmd = MMC_CMD25;
322       }
323     }
324 
325     ConsumeSize = BlockCount * This->Media->BlockSize;
326     if (BytesRemainingToBeTransfered < ConsumeSize) {
327       ConsumeSize = BytesRemainingToBeTransfered;
328     }
329     Status = MmcTransferBlock (This, Cmd, Transfer, MediaId, Lba, ConsumeSize, Buffer);
330     if (EFI_ERROR (Status)) {
331       DEBUG ((EFI_D_ERROR, "%a(): Failed to transfer block and Status:%r\n", __func__, Status));
332     }
333 
334     BytesRemainingToBeTransfered -= ConsumeSize;
335     if (BytesRemainingToBeTransfered > 0) {
336       Lba    += BlockCount;
337       Buffer = (UINT8 *)Buffer + ConsumeSize;
338     }
339   }
340 
341   return EFI_SUCCESS;
342 }
343 
344 EFI_STATUS
345 EFIAPI
MmcReadBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,OUT VOID * Buffer)346 MmcReadBlocks (
347   IN EFI_BLOCK_IO_PROTOCOL    *This,
348   IN UINT32                   MediaId,
349   IN EFI_LBA                  Lba,
350   IN UINTN                    BufferSize,
351   OUT VOID                    *Buffer
352   )
353 {
354   return MmcIoBlocks (This, MMC_IOBLOCKS_READ, MediaId, Lba, BufferSize, Buffer);
355 }
356 
357 EFI_STATUS
358 EFIAPI
MmcWriteBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,IN VOID * Buffer)359 MmcWriteBlocks (
360   IN EFI_BLOCK_IO_PROTOCOL    *This,
361   IN UINT32                   MediaId,
362   IN EFI_LBA                  Lba,
363   IN UINTN                    BufferSize,
364   IN VOID                     *Buffer
365   )
366 {
367   return MmcIoBlocks (This, MMC_IOBLOCKS_WRITE, MediaId, Lba, BufferSize, Buffer);
368 }
369 
370 EFI_STATUS
371 EFIAPI
MmcFlushBlocks(IN EFI_BLOCK_IO_PROTOCOL * This)372 MmcFlushBlocks (
373   IN EFI_BLOCK_IO_PROTOCOL  *This
374   )
375 {
376   return EFI_SUCCESS;
377 }
378 
379 EFI_STATUS
380 EFIAPI
MmcEraseBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN OUT EFI_ERASE_BLOCK_TOKEN * Token,IN UINTN Size)381 MmcEraseBlocks (
382   IN EFI_BLOCK_IO_PROTOCOL          *This,
383   IN UINT32                         MediaId,
384   IN EFI_LBA                        Lba,
385   IN OUT EFI_ERASE_BLOCK_TOKEN      *Token,
386   IN UINTN                          Size
387   )
388 {
389   EFI_STATUS             Status;
390   MMC_HOST_INSTANCE      *MmcHostInstance;
391   EFI_MMC_HOST_PROTOCOL  *MmcHost;
392   UINT32                 Response[4];
393   UINTN                  CmdArg;
394 
395   MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
396   ASSERT (MmcHostInstance != NULL);
397   MmcHost = MmcHostInstance->MmcHost;
398   ASSERT (MmcHost);
399 
400   if (This->Media->ReadOnly == TRUE)
401      return EFI_WRITE_PROTECTED;
402 
403 // EFI_DEVICE_ERROR, EFI_INVALID_PARAMETER
404 
405   if (!MmcHostInstance->BlockIo.Media->MediaPresent)
406      return EFI_NO_MEDIA;
407 
408   if (This->Media->MediaId != MediaId)
409      return EFI_MEDIA_CHANGED;
410 
411   CmdArg = Lba;
412   Status = MmcHost->SendCommand (MmcHost, MMC_CMD35, CmdArg);
413   if (!EFI_ERROR (Status)) {
414      MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
415   }
416 
417   CmdArg = Lba + Size;
418   Status = MmcHost->SendCommand (MmcHost, MMC_CMD36, CmdArg);
419   if (!EFI_ERROR (Status)) {
420      MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
421   }
422 
423   CmdArg = 0;
424   Status = MmcHost->SendCommand (MmcHost, MMC_CMD38, CmdArg);
425   if (!EFI_ERROR (Status)) {
426      MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1b, Response);
427   }
428 
429   return EFI_SUCCESS;
430 }
431