1261694f9eb4eb007bdb647305f834b5a061c16f
[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;
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     for (i = 0; i < nandmcb.devInfo.totalBlocks; i++)  {
256         badBlock = FALSE;
257         for (j = 0; j < ibl_N_BAD_BLOCK_PAGE; j++)
258         {
259             if (nandmcb.devInfo.badBlkMarkIdx[j] < nandmcb.devInfo.pageEccBytes)
260             {
261                 ret = (*nandmcb.nand_if->nct_driverReadBytes)(i, 
262                     j, 
263                     nandmcb.devInfo.pageSizeBytes, 
264                     nandmcb.devInfo.pageEccBytes, 
265                     nandmcb.page);
266                 if (ret < 0)
267                 {
268                     nand_free_return (ret);
269                     return (-1);
270                 }
271                 
272                 if (nandmcb.page[nandmcb.devInfo.badBlkMarkIdx[j]] != 0xff)
273                 {
274                     badBlock = TRUE;
275                     break;
276                 }
277             }
278         }
279         
280         if (badBlock)  {
281             nandmcb.blocks[i] = 0xff;
282             nandmcb.numBadBlocks += 1;
283         } else
284             nandmcb.blocks[i] = 0;
286     }
287     
289     /* Construct the logical to physical block array */  
290     for (i = j = 0; i < nandmcb.devInfo.totalBlocks; i++)  {
291         if (nandmcb.blocks[i] != 0xff)
292             nandmcb.logicalToPhysMap[j++] = i;
293     }
295     /* Construct the physical to logical block array */
296     for (i = j = 0; i < nandmcb.devInfo.totalBlocks; i++)  {
297         if (nandmcb.blocks[i] == 0xff)
298             nandmcb.physToLogicalMap[i] = 0xff;
299         else
300             nandmcb.physToLogicalMap[i] = j++;
301     }
304     /* Seek to the first byte of the file */
305     nandmcb.fpos                = -1;      /* Force a read on the first seek */
306     nandmcb.currentLogicalBlock = 0xffffffff;
307     nandmcb.currentPage         = 0xffffffff;
309     nand_seek (ibln->bootAddress[iblEndianIdx][iblImageIdx], 0);
310             
311     return (0);
316 /**
317  *  @b  Description
318  *  @n
319  *      This function performs a reads from the current read point
320  *
321  */
322 Int32 nand_read (Uint8 *ptr_buf, Uint32 num_bytes)
324     Int32  i;
325     Int32  pIdx;
328     if (nandmcb.nand_if == NULL)
329         return (-1);
331     /* Convert the global file position to an offset in the currently cached page */
332     pIdx = nandmcb.fpos % nandmcb.devInfo.pageSizeBytes;
334     for (i = 0; i < num_bytes; i++)  {
336         ptr_buf[i]    = nandmcb.page[pIdx++];
337         nandmcb.fpos += 1;
339         if (pIdx >= nandmcb.devInfo.pageSizeBytes)  {
341             pIdx = 0;
342             nandmcb.currentPage += 1;
344             if (nandmcb.currentPage >= nandmcb.devInfo.pagesPerBlock)  {
345                 nandmcb.currentPage          = 0;
346                 nandmcb.currentLogicalBlock += 1;
347             }
349             
350             /* Load the new page */
351             if ((*nandmcb.nand_if->nct_driverReadPage)((Uint32)(nandmcb.logicalToPhysMap[nandmcb.currentLogicalBlock]), nandmcb.currentPage, nandmcb.page) < 0)
352                 return (-2);
354         }
355     }
357   return (0);
361 /**
362  *  @b  Description
363  *  @n
364  *      This function performs a peek from the current read point.
365  */
366 Int32 nand_peek (Uint8 *ptr_buf, Uint32 num_bytes)
368     Int32  ret;
369     Int32  origPos;
370     Uint32 origLogicalBlock;
371     Uint32 origPage;
374     if (nandmcb.nand_if == NULL)
375         return (-1);
377     origPos          = nandmcb.fpos;
378     origLogicalBlock = nandmcb.currentLogicalBlock;
379     origPage         = nandmcb.currentPage;
381     ret = nand_read (ptr_buf, num_bytes);
383     if ( (origLogicalBlock != nandmcb.currentLogicalBlock)  ||
384          (origPage         != nandmcb.currentPage)  )   {
386             if ((*nandmcb.nand_if->nct_driverReadPage)((Uint32)(nandmcb.logicalToPhysMap[origLogicalBlock]), origPage, nandmcb.page) < 0)
387                 return (-2);
388     }
389     
390     nandmcb.fpos                = origPos;
391     nandmcb.currentLogicalBlock = origLogicalBlock;
392     nandmcb.currentPage         = origPage;
394     return (ret);
398 /**
399  *  @b  Description
400  *  @n
401  *      This function returns how much data is available for immediate read.
402  *      On nand this always returns the page size.
403  */
404 Int32 nand_query (void)
406     return (nandmcb.devInfo.pageSizeBytes);
408    
410 /**
411  *  @b  Description
412  *  @n
413  *      This function closes the nand driver
414  */
415 Int32 nand_close (void)
417     if (nandmcb.nand_if != NULL)
418         (*nandmcb.nand_if->nct_driverClose)();
420     nandmcb.nand_if = NULL;
422     return (nand_free_return (0));
428 /**
429  * @brief  The global nand module function table
430  */
431 BOOT_MODULE_FXN_TABLE nand_boot_module =
433     nand_open,          /* Open  API */
434     nand_close,         /* Close API */
435     nand_read,          /* Read  API */
436     NULL,               /* Write API */
437     nand_peek,          /* Peek  API */
438     nand_seek,          /* Seek  API */
439     nand_query          /* Query API */
441 };