Changes for C6670 support from Hao
[keystone-rtos/ibl.git] / src / driver / nand / nand.c
1 /*
2  *
3  * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ 
4  * 
5  * 
6  *  Redistribution and use in source and binary forms, with or without 
7  *  modification, are permitted provided that the following conditions 
8  *  are met:
9  *
10  *    Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  *    Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the 
15  *    documentation and/or other materials provided with the   
16  *    distribution.
17  *
18  *    Neither the name of Texas Instruments Incorporated nor the names of
19  *    its contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
23  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
24  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
26  *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
27  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
28  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
31  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
32  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34 */
38 /************************************************************************************************
39  * FILE PURPOSE: The top level NAND driver
40  ************************************************************************************************
41  * FILE NAME: nand.c
42  *
43  * DESCRIPTION: Provides the required driver functions which populate the BOOT_MODULE_FXN_TABLE
44  *
45  * @file nand.c
46  *
47  * @brief
48  *              The top level nand driver
49  *
50  *************************************************************************************************/
51 #include "types.h"
52 #include "ibl.h"
53 #include "iblloc.h"
54 #include "device.h"
55 #include "nand.h" 
56 #include "nandhwapi.h"
57 #include <string.h>
58 #include <stdlib.h>
60 extern void *iblMalloc (Uint32 size);
61 extern void iblFree (void *mem);
63 /**
64  *  @brief  The nand master control block which tracks the current nand boot information 
65  */
66 typedef struct nandmcb_s
67 {
68     nandDevInfo_t  devInfo;             /**< Device information */
70     Int32   fpos;                       /**< The current logical file position */
71     Uint32  currentLogicalBlock;        /**< The logical block number of the page currently stored */
72     Uint32  currentPage;                /**< The page number currently stored */
74     Uint8  *page;                       /**< The current page */
75     Uint16 *logicalToPhysMap;           /**< Maps local block to physical block */
76     Uint16 *physToLogicalMap;           /**< Maps a physical block number to a logical block number */
78     Uint32  numBadBlocks;               /**< Total number of bad blocks */
79     Uint8  *blocks;                     /**< There is one byte per block. A non-zero value indicates
80                                              that the block is bad */
82     nandCtbl_t *nand_if;                /**< Current low level interface (GPIO, EMIF or SPI) */
83     
85 } nandmcb_t;
87 nandmcb_t nandmcb;
91 /**
92  *  @b Description
93  *  @n
94  *
95  *  This function sets the current file position, and loads the page corresponding
96  *  to that position into the pre-allocated page memory
97  */
98 Int32 nand_seek (Int32 loc, Int32 from)
99 {
100     Int32  desiredPos;
101     Uint32 desiredBlock;
102     Uint32 desiredPage;
104     /* Can't seek from the end of the file, since the end is not known */
105     if (from == 0)
106         desiredPos = loc;
107     else if (from == 1)
108         desiredPos = nandmcb.fpos + loc;
109     else
110         return (-1);
112     if (desiredPos < 0)
113         return (-1);
116     /* Convert the file position (relative to the file start) into a page number */
117     desiredPage = desiredPos / nandmcb.devInfo.pageSizeBytes;
119     /* Convert the logical page to logical block/page */
120     desiredBlock = desiredPage / nandmcb.devInfo.pagesPerBlock;
121     desiredPage  = desiredPage % nandmcb.devInfo.pagesPerBlock;
123     /* Update the current position */
124     nandmcb.fpos = desiredPos;
126     /* Nothing to do if the current block/page is already loaded */
127     if ((desiredBlock == nandmcb.currentLogicalBlock) && (desiredPage == nandmcb.currentPage))
128         return (0);
130     /* Otherwise load the desired page */
131     if (nandmcb.nand_if->nct_driverReadPage != NULL)  {
132         if ((*nandmcb.nand_if->nct_driverReadPage)((Uint32)(nandmcb.logicalToPhysMap[desiredBlock]), desiredPage, nandmcb.page) < 0)
133             return (-2);
134     }
136     /* Update the currently loaded block/page info */
137     nandmcb.currentLogicalBlock = desiredBlock;
138     nandmcb.currentPage         = desiredPage;
140     return (0);
145 /**
146  *  @b  Description
147  *  @n
148  *      Free any memory allocated by the driver
149  */
150 Int32 nand_free_return (Int32 retcode)
152     if (nandmcb.page != NULL)
153         iblFree (nandmcb.page);
155     if (nandmcb.logicalToPhysMap != NULL)
156         iblFree (nandmcb.logicalToPhysMap);
158     if (nandmcb.physToLogicalMap != NULL)
159         iblFree (nandmcb.physToLogicalMap);
161     if (nandmcb.blocks != NULL)
162         iblFree (nandmcb.blocks);
164     return (retcode);
170 /**
171  *  @b Description
172  *  @n
173  *
174  *  This function initializes the nand control structure and reads the bad block info
175  *  from the nand.
176  */
177 Int32 nand_open (void *ptr_driver, void (*asyncComplete)(void *))
179     iblNand_t *ibln = (iblNand_t *)ptr_driver;
180     
181     Int32 size;
182     Int32 ret;
183     Int32 i, j, startBlock;
184     Bool  badBlock;
186     /* Initialize the control info */
187     iblMemset (&nandmcb, 0, sizeof(nandmcb));
188     iblMemcpy (&nandmcb.devInfo, &ibln->nandInfo, sizeof(nandDevInfo_t));
190     nandmcb.page             = NULL;
191     nandmcb.logicalToPhysMap = NULL;
192     nandmcb.physToLogicalMap = NULL;
193     nandmcb.blocks           = NULL;
195     nandmcb.nand_if = deviceGetNandCtbl (ibln->interface);
197     if (nandmcb.nand_if != NULL)  {
199         ret = (*nandmcb.nand_if->nct_driverInit)(ibln->interface, (void *)&nandmcb.devInfo);
200         if (ret < 0)
201         {
202             nand_free_return (ret);
203             return (-1);
204         }
205     }  
206     else  
207     {
208         return (-1);
209     }
211     /* allocate memory for the page data and the logical to physical block map */
212     size = nandmcb.devInfo.pageSizeBytes + nandmcb.devInfo.pageEccBytes;
213     nandmcb.page = iblMalloc (size * sizeof(Uint8));
214     if (nandmcb.page == NULL)
215     {
216         nand_free_return (NAND_MALLOC_PAGE_FAIL);
217         return (-1);
218     }
221     /* Logical to physical map data */
222     nandmcb.logicalToPhysMap = iblMalloc (nandmcb.devInfo.totalBlocks * sizeof(Uint16));
223     if (nandmcb.logicalToPhysMap == NULL)  
224     {
225         nand_free_return (NAND_MALLOC_MAP_LTOP_FAIL);
226         return (-1);
227     }
228     
230     /* Physical to logical map data */
231     nandmcb.physToLogicalMap = iblMalloc (nandmcb.devInfo.totalBlocks * sizeof(Uint16));
232     if (nandmcb.physToLogicalMap == NULL)  
233     {
234         nand_free_return (NAND_MALLOC_MAP_PTOL_FAIL);
235         return (-1);
236     }
238     /* Block info */
239     size = nandmcb.devInfo.totalBlocks * sizeof(Uint8);
240     nandmcb.blocks = iblMalloc (size);
241     if (nandmcb.blocks == NULL)  
242     {
243         nand_free_return (NAND_MALLOC_BLOCK_INFO_FAIL);
244         return (-1);
245     }
248     /* Bad blocks are identified by reading page 0 and page 1. If the first 
249      * byte in these pages is not 0xff then the block is bad */
250     if (nandmcb.nand_if == NULL)
251         return (-1);
253     nandmcb.numBadBlocks = 0;
254     startBlock = ibln->bootAddress[iblEndianIdx][iblImageIdx]/(nandmcb.devInfo.pageSizeBytes*nandmcb.devInfo.pagesPerBlock);
255     for (i = startBlock; i < nandmcb.devInfo.totalBlocks; i++)  {
257         badBlock = FALSE;
258         for (j = 0; j < ibl_N_BAD_BLOCK_PAGE; j++)
259         {
260             if (nandmcb.devInfo.badBlkMarkIdx[j] < nandmcb.devInfo.pageEccBytes)
261             {
262                 ret = (*nandmcb.nand_if->nct_driverReadBytes)(i, 
263                     j, 
264                     nandmcb.devInfo.pageSizeBytes, 
265                     nandmcb.devInfo.pageEccBytes, 
266                     nandmcb.page);
267                 if (ret < 0)
268                 {
269                     nand_free_return (ret);
270                     return (-1);
271                 }
272                 
273                 if (nandmcb.page[nandmcb.devInfo.badBlkMarkIdx[j]] != 0xff)
274                 {
275                     badBlock = TRUE;
276                     break;
277                 }
278             }
279         }
280         
281         if (badBlock)  {
282             nandmcb.blocks[i] = 0xff;
283             nandmcb.numBadBlocks += 1;
284         } else
285             nandmcb.blocks[i] = 0;
287     }
288     
290     /* Construct the logical to physical block array */  
291     for (i = j = startBlock; i < nandmcb.devInfo.totalBlocks; i++)  {
292         if (nandmcb.blocks[i] != 0xff)
293             nandmcb.logicalToPhysMap[j++] = i;
294     }
296     /* Construct the physical to logical block array */
297     for (i = j = startBlock; i < nandmcb.devInfo.totalBlocks; i++)  {
298         if (nandmcb.blocks[i] == 0xff)
299             nandmcb.physToLogicalMap[i] = 0xff;
300         else
301             nandmcb.physToLogicalMap[i] = j++;
302     }
305     /* Seek to the first byte of the file */
306     nandmcb.fpos                = -1;      /* Force a read on the first seek */
307     nandmcb.currentLogicalBlock = 0xffffffff;
308     nandmcb.currentPage         = 0xffffffff;
310     nand_seek (ibln->bootAddress[iblEndianIdx][iblImageIdx], 0);
311             
312     return (0);
317 /**
318  *  @b  Description
319  *  @n
320  *      This function performs a reads from the current read point
321  *
322  */
323 Int32 nand_read (Uint8 *ptr_buf, Uint32 num_bytes)
325     Int32  i;
326     Int32  pIdx;
329     if (nandmcb.nand_if == NULL)
330         return (-1);
332     /* Convert the global file position to an offset in the currently cached page */
333     pIdx = nandmcb.fpos % nandmcb.devInfo.pageSizeBytes;
335     for (i = 0; i < num_bytes; i++)  {
337         ptr_buf[i]    = nandmcb.page[pIdx++];
338         nandmcb.fpos += 1;
340         if (pIdx >= nandmcb.devInfo.pageSizeBytes)  {
342             pIdx = 0;
343             nandmcb.currentPage += 1;
345             if (nandmcb.currentPage >= nandmcb.devInfo.pagesPerBlock)  {
346                 nandmcb.currentPage          = 0;
347                 nandmcb.currentLogicalBlock += 1;
348             }
350             
351             /* Load the new page */
352             if ((*nandmcb.nand_if->nct_driverReadPage)((Uint32)(nandmcb.logicalToPhysMap[nandmcb.currentLogicalBlock]), nandmcb.currentPage, nandmcb.page) < 0)
353                 return (-2);
355         }
356     }
358   return (0);
362 /**
363  *  @b  Description
364  *  @n
365  *      This function performs a peek from the current read point.
366  */
367 Int32 nand_peek (Uint8 *ptr_buf, Uint32 num_bytes)
369     Int32  ret;
370     Int32  origPos;
371     Uint32 origLogicalBlock;
372     Uint32 origPage;
375     if (nandmcb.nand_if == NULL)
376         return (-1);
378     origPos          = nandmcb.fpos;
379     origLogicalBlock = nandmcb.currentLogicalBlock;
380     origPage         = nandmcb.currentPage;
382     ret = nand_read (ptr_buf, num_bytes);
384     if ( (origLogicalBlock != nandmcb.currentLogicalBlock)  ||
385          (origPage         != nandmcb.currentPage)  )   {
387             if ((*nandmcb.nand_if->nct_driverReadPage)((Uint32)(nandmcb.logicalToPhysMap[origLogicalBlock]), origPage, nandmcb.page) < 0)
388                 return (-2);
389     }
390     
391     nandmcb.fpos                = origPos;
392     nandmcb.currentLogicalBlock = origLogicalBlock;
393     nandmcb.currentPage         = origPage;
395     return (ret);
399 /**
400  *  @b  Description
401  *  @n
402  *      This function returns how much data is available for immediate read.
403  *      On nand this always returns the page size.
404  */
405 Int32 nand_query (void)
407     return (nandmcb.devInfo.pageSizeBytes);
409    
411 /**
412  *  @b  Description
413  *  @n
414  *      This function closes the nand driver
415  */
416 Int32 nand_close (void)
418     if (nandmcb.nand_if != NULL)
419         (*nandmcb.nand_if->nct_driverClose)();
421     nandmcb.nand_if = NULL;
423     return (nand_free_return (0));
429 /**
430  * @brief  The global nand module function table
431  */
432 BOOT_MODULE_FXN_TABLE nand_boot_module =
434     nand_open,          /* Open  API */
435     nand_close,         /* Close API */
436     nand_read,          /* Read  API */
437     NULL,               /* Write API */
438     nand_peek,          /* Peek  API */
439     nand_seek,          /* Seek  API */
440     nand_query          /* Query API */
442 };