25e8a0c12b7edd8825654989ab3b8b0495bbc36b
[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 "nand.h" 
55 #include "nandhwapi.h"
56 #include <string.h>
57 #include <stdlib.h>
60 /**
61  *  @brief  The nand master control block which tracks the current nand boot information 
62  */
63 typedef struct nandmcb_s
64 {
65     nandDevInfo_t  devInfo;             /**< Device information */
67     Int32   fpos;                       /**< The current logical file position */
68     Uint32  currentLogicalBlock;        /**< The logical block number of the page currently stored */
69     Uint32  currentPage;                /**< The page number currently stored */
71     Uint8  *page;                       /**< The current page */
72     Uint16 *logicalToPhysMap;           /**< Maps local block to physical block */
73     Uint16 *physToLogicalMap;           /**< Maps a physical block number to a logical block number */
75     Uint32  numBadBlocks;               /**< Total number of bad blocks */
76     Uint8  *blocks;                     /**< There is one byte per block. A non-zero value indicates
77                                              that the block is bad */
79 } nandmcb_t;
81 nandmcb_t nandmcb;
84 /**
85  *  @b Description
86  *  @n
87  *
88  *  This function sets the current file position, and loads the page corresponding
89  *  to that position into the pre-allocated page memory
90  */
91 Int32 nand_seek (Int32 loc, Int32 from)
92 {
93     Int32  desiredPos;
94     Uint32 desiredBlock;
95     Uint32 desiredPage;
97     /* Can't seek from the end of the file, since the end is not known */
98     if (from == 0)
99         desiredPos = loc;
100     else if (from == 1)
101         desiredPos = nandmcb.fpos + loc;
102     else
103         return (-1);
105     if (desiredPos < 0)
106         return (-1);
109     /* Convert the file position (relative to the file start) into a page number */
110     desiredPage = desiredPos / nandmcb.devInfo.pageSizeBytes;
112     /* Convert the logical page to logical block/page */
113     desiredBlock = desiredPage / nandmcb.devInfo.pagesPerBlock;
114     desiredPage  = desiredPage % nandmcb.devInfo.pagesPerBlock;
116     /* Update the current position */
117     nandmcb.fpos = desiredPos;
119     /* Nothing to do if the current block/page is already loaded */
120     if ((desiredBlock == nandmcb.currentLogicalBlock) && (desiredPage == nandmcb.currentPage))
121         return (0);
123     /* Otherwise load the desired page */
124     if (nandHwDriverReadPage((Uint32)(nandmcb.logicalToPhysMap[desiredBlock]), desiredPage, nandmcb.page) < 0)
125         return (-2);
127     /* Update the currently loaded block/page info */
128     nandmcb.currentLogicalBlock = desiredBlock;
129     nandmcb.currentPage         = desiredPage;
131     return (0);
136 /**
137  *  @b  Description
138  *  @n
139  *      Free any memory allocated by the driver
140  */
141 Int32 nand_free_return (Int32 retcode)
143     if (nandmcb.page != NULL)
144         iblFree (nandmcb.page);
146     if (nandmcb.logicalToPhysMap != NULL)
147         iblFree (nandmcb.logicalToPhysMap);
149     if (nandmcb.physToLogicalMap != NULL)
150         iblFree (nandmcb.physToLogicalMap);
152     if (nandmcb.blocks != NULL)
153         iblFree (nandmcb.blocks);
155     return (retcode);
161 /**
162  *  @b Description
163  *  @n
164  *
165  *  This function initializes the nand control structure and reads the bad block info
166  *  from the nand.
167  */
168 Int32 nand_open (void *ptr_driver, void (*asyncComplete)(void *))
170     iblNand_t *ibln = (iblNand_t *)ptr_driver;
171     
172     Int32 size;
173     Int32 ret;
174     Int32 i, j;
176     /* Initialize the control info */
177     iblMemset (&nandmcb, 0, sizeof(nandmcb));
178     iblMemcpy (&nandmcb.devInfo, &ibln->nandInfo, sizeof(iblNand_t));
180     nandmcb.page             = NULL;
181     nandmcb.logicalToPhysMap = NULL;
182     nandmcb.physToLogicalMap = NULL;
183     nandmcb.blocks           = NULL;
185     ret = nandHwDriverInit (&nandmcb.devInfo);
186     if (ret < 0)
187         nand_free_return (ret);
189     /* allocate memory for the page data and the logical to physical block map */
190     size = nandmcb.devInfo.pageSizeBytes + nandmcb.devInfo.pageEccBytes;
191     nandmcb.page = iblMalloc (size * sizeof(Uint8));
192     if (nandmcb.page == NULL)
193         nand_free_return (NAND_MALLOC_PAGE_FAIL);
196     /* Logical to physical map data */
197     nandmcb.logicalToPhysMap = iblMalloc (nandmcb.devInfo.totalBlocks * sizeof(Uint16));
198     if (nandmcb.logicalToPhysMap == NULL)  
199         nand_free_return (NAND_MALLOC_MAP_LTOP_FAIL);
200     
202     /* Physical to logical map data */
203     nandmcb.physToLogicalMap = iblMalloc (nandmcb.devInfo.totalBlocks * sizeof(Uint16));
204     if (nandmcb.physToLogicalMap == NULL)  
205         nand_free_return (NAND_MALLOC_MAP_PTOL_FAIL);
207     /* Block info */
208     size = nandmcb.devInfo.totalBlocks * sizeof(Uint8);
209     nandmcb.blocks = iblMalloc (size);
210     if (nandmcb.blocks == NULL)  
211         nand_free_return (NAND_MALLOC_BLOCK_INFO_FAIL);
214     /* Bad blocks are identified by reading page 0 and page 1. If the first 
215      * byte in these pages is not 0xff then the block is bad */
216     nandmcb.numBadBlocks = 0;
217     for (i = 0; i < nandmcb.devInfo.totalBlocks; i++)  {
219        ret = nandHwDriverReadBytes (i, 0, nandmcb.devInfo.pageSizeBytes, 1, &nandmcb.page[0]);
220        if (ret < 0)
221           nand_free_return (ret);
223        ret = nandHwDriverReadBytes (i, 1, nandmcb.devInfo.pageSizeBytes, 1, &nandmcb.page[1]);
224        if (ret < 0)
225           nand_free_return (ret);
226         
227        if ((nandmcb.page[0] != 0xff) || (nandmcb.page[1] != 0xff))  {
228           nandmcb.blocks[i] = 0xff;
229           nandmcb.numBadBlocks += 1;
230        } else
231           nandmcb.blocks[i] = 0;
233     }
234     
236     /* Construct the logical to physical block array */  
237     for (i = j = 0; i < nandmcb.devInfo.totalBlocks; i++)  {
238         if (nandmcb.blocks[i] != 0xff)
239             nandmcb.logicalToPhysMap[j++] = i;
240     }
242     /* Construct the physical to logical block array */
243     for (i = j = 0; i < nandmcb.devInfo.totalBlocks; i++)  {
244         if (nandmcb.blocks[i] == 0xff)
245             nandmcb.physToLogicalMap[i] = 0xff;
246         else
247             nandmcb.physToLogicalMap[i] = j++;
248     }
251     /* Seek to the first byte of the file */
252     nandmcb.fpos                = -1;      /* Force a read on the first seek */
253     nandmcb.currentLogicalBlock = 0xffffffff;
254     nandmcb.currentPage         = 0xffffffff;
255     nand_seek (0, 0);
257             
258     return (0);
263 /**
264  *  @b  Description
265  *  @n
266  *      This function performs a reads from the current read point
267  *
268  */
269 Int32 nand_read (Uint8 *ptr_buf, Uint32 num_bytes)
271     Int32  i;
272     Int32  pIdx;
274     /* Convert the global file position to an offset in the currently cached page */
275     pIdx = nandmcb.fpos % nandmcb.devInfo.pageSizeBytes;
277     for (i = 0; i < num_bytes; i++)  {
279         ptr_buf[i]    = nandmcb.page[pIdx++];
280         nandmcb.fpos += 1;
282         if (pIdx >= nandmcb.devInfo.pageSizeBytes)  {
284             pIdx = 0;
285             nandmcb.currentPage += 1;
287             if (nandmcb.currentPage >= nandmcb.devInfo.pagesPerBlock)  {
288                 nandmcb.currentPage          = 0;
289                 nandmcb.currentLogicalBlock += 1;
290             }
292             
293             /* Load the new page */
294             if (nandHwDriverReadPage((Uint32)(nandmcb.logicalToPhysMap[nandmcb.currentLogicalBlock]), nandmcb.currentPage, nandmcb.page) < 0)
295                 return (-2);
297         }
298     }
300   return (0);
304 /**
305  *  @b  Description
306  *  @n
307  *      This function performs a peek from the current read point.
308  */
309 Int32 nand_peek (Uint8 *ptr_buf, Uint32 num_bytes)
311     Int32  ret;
312     Int32  origPos;
313     Uint32 origLogicalBlock;
314     Uint32 origPage;
316     origPos          = nandmcb.fpos;
317     origLogicalBlock = nandmcb.currentLogicalBlock;
318     origPage         = nandmcb.currentPage;
320     ret = nand_read (ptr_buf, num_bytes);
322     if ( (origLogicalBlock != nandmcb.currentLogicalBlock)  ||
323          (origPage         != nandmcb.currentPage)  )   {
325             if (nandHwDriverReadPage((Uint32)(nandmcb.logicalToPhysMap[origLogicalBlock]), origPage, nandmcb.page) < 0)
326                 return (-2);
327     }
328     
329     nandmcb.fpos                = origPos;
330     nandmcb.currentLogicalBlock = origLogicalBlock;
331     nandmcb.currentPage         = origPage;
333     return (ret);
337 /**
338  *  @b  Description
339  *  @n
340  *      This function returns how much data is available for immediate read.
341  *      On nand this always returns the page size.
342  */
343 Int32 nand_query (void)
345     return (nandmcb.devInfo.pageSizeBytes);
347    
349 /**
350  *  @b  Description
351  *  @n
352  *      This function closes the nand driver
353  */
354 Int32 nand_close (void)
356     nandHwDriverClose ();
357     return (nand_free_return (0));
363 /**
364  * @brief  The global nand module function table
365  */
366 BOOT_MODULE_FXN_TABLE nand_boot_module =
368     nand_open,          /* Open  API */
369     nand_close,         /* Close API */
370     nand_read,          /* Read  API */
371     NULL,               /* Write API */
372     nand_peek,          /* Peek  API */
373     nand_seek,          /* Seek  API */
374     nand_query          /* Query API */
376 };