1 /************************************************************************************************
2 * FILE PURPOSE: The top level NAND driver
3 ************************************************************************************************
4 * FILE NAME: nand.c
5 *
6 * DESCRIPTION: Provides the required driver functions which populate the BOOT_MODULE_FXN_TABLE
7 *
8 * @file nand.c
9 *
10 * @brief
11 * The top level nand driver
12 *
13 *************************************************************************************************/
14 #include "types.h"
15 #include "ibl.h"
16 #include "iblloc.h"
17 #include "nand.h"
18 #include "nandhwapi.h"
19 #include <string.h>
20 #include <stdlib.h>
23 /**
24 * @brief The nand master control block which tracks the current nand boot information
25 */
26 typedef struct nandmcb_s
27 {
28 nandDevInfo_t devInfo; /**< Device information */
30 Int32 fpos; /**< The current logical file position */
31 Uint32 currentLogicalBlock; /**< The logical block number of the page currently stored */
32 Uint32 currentPage; /**< The page number currently stored */
34 Uint8 *page; /**< The current page */
35 Uint16 *logicalToPhysMap; /**< Maps local block to physical block */
36 Uint16 *physToLogicalMap; /**< Maps a physical block number to a logical block number */
38 Uint32 numBadBlocks; /**< Total number of bad blocks */
39 Uint8 *blocks; /**< There is one byte per block. A non-zero value indicates
40 that the block is bad */
42 } nandmcb_t;
44 nandmcb_t nandmcb;
47 /**
48 * @b Description
49 * @n
50 *
51 * This function sets the current file position, and loads the page corresponding
52 * to that position into the pre-allocated page memory
53 */
54 Int32 nand_seek (Int32 loc, Int32 from)
55 {
56 Int32 desiredPos;
57 Uint32 desiredBlock;
58 Uint32 desiredPage;
60 /* Can't seek from the end of the file, since the end is not known */
61 if (from == 0)
62 desiredPos = loc;
63 else if (from == 1)
64 desiredPos = nandmcb.fpos + loc;
65 else
66 return (-1);
68 if (desiredPos < 0)
69 return (-1);
72 /* Convert the file position (relative to the file start) into a page number */
73 desiredPage = desiredPos / nandmcb.devInfo.pageSizeBytes;
75 /* Convert the logical page to logical block/page */
76 desiredBlock = desiredPage / nandmcb.devInfo.pagesPerBlock;
77 desiredPage = desiredPage % nandmcb.devInfo.pagesPerBlock;
79 /* Update the current position */
80 nandmcb.fpos = desiredPos;
82 /* Nothing to do if the current block/page is already loaded */
83 if ((desiredBlock == nandmcb.currentLogicalBlock) && (desiredPage == nandmcb.currentPage))
84 return (0);
86 /* Otherwise load the desired page */
87 if (nandHwDriverReadPage((Uint32)(nandmcb.logicalToPhysMap[desiredBlock]), desiredPage, nandmcb.page) < 0)
88 return (-2);
90 /* Update the currently loaded block/page info */
91 nandmcb.currentLogicalBlock = desiredBlock;
92 nandmcb.currentPage = desiredPage;
94 return (0);
96 }
99 /**
100 * @b Description
101 * @n
102 * Free any memory allocated by the driver
103 */
104 Int32 nand_free_return (Int32 retcode)
105 {
106 if (nandmcb.page != NULL)
107 iblFree (nandmcb.page);
109 if (nandmcb.logicalToPhysMap != NULL)
110 iblFree (nandmcb.logicalToPhysMap);
112 if (nandmcb.physToLogicalMap != NULL)
113 iblFree (nandmcb.physToLogicalMap);
115 if (nandmcb.blocks != NULL)
116 iblFree (nandmcb.blocks);
118 return (retcode);
120 }
124 /**
125 * @b Description
126 * @n
127 *
128 * This function initializes the nand control structure and reads the bad block info
129 * from the nand.
130 */
131 Int32 nand_open (void *ptr_driver, void (*asyncComplete)(void *))
132 {
133 iblNand_t *ibln = (iblNand_t *)ptr_driver;
135 Int32 size;
136 Int32 ret;
137 Int32 i, j;
139 /* Initialize the control info */
140 iblMemset (&nandmcb, 0, sizeof(nandmcb));
141 iblMemcpy (&nandmcb.devInfo, &ibln->nandInfo, sizeof(iblNand_t));
143 nandmcb.page = NULL;
144 nandmcb.logicalToPhysMap = NULL;
145 nandmcb.physToLogicalMap = NULL;
146 nandmcb.blocks = NULL;
148 ret = nandHwDriverInit (&nandmcb.devInfo);
149 if (ret < 0)
150 nand_free_return (ret);
152 /* allocate memory for the page data and the logical to physical block map */
153 size = nandmcb.devInfo.pageSizeBytes + nandmcb.devInfo.pageEccBytes;
154 nandmcb.page = iblMalloc (size * sizeof(Uint8));
155 if (nandmcb.page == NULL)
156 nand_free_return (NAND_MALLOC_PAGE_FAIL);
159 /* Logical to physical map data */
160 nandmcb.logicalToPhysMap = iblMalloc (nandmcb.devInfo.totalBlocks * sizeof(Uint16));
161 if (nandmcb.logicalToPhysMap == NULL)
162 nand_free_return (NAND_MALLOC_MAP_LTOP_FAIL);
165 /* Physical to logical map data */
166 nandmcb.physToLogicalMap = iblMalloc (nandmcb.devInfo.totalBlocks * sizeof(Uint16));
167 if (nandmcb.physToLogicalMap == NULL)
168 nand_free_return (NAND_MALLOC_MAP_PTOL_FAIL);
170 /* Block info */
171 size = nandmcb.devInfo.totalBlocks * sizeof(Uint8);
172 nandmcb.blocks = iblMalloc (size);
173 if (nandmcb.blocks == NULL)
174 nand_free_return (NAND_MALLOC_BLOCK_INFO_FAIL);
177 /* Bad blocks are identified by reading page 0 and page 1. If the first
178 * byte in these pages is not 0xff then the block is bad */
179 nandmcb.numBadBlocks = 0;
180 for (i = 0; i < nandmcb.devInfo.totalBlocks; i++) {
182 ret = nandHwDriverReadBytes (i, 0, nandmcb.devInfo.pageSizeBytes, 1, &nandmcb.page[0]);
183 if (ret < 0)
184 nand_free_return (ret);
186 ret = nandHwDriverReadBytes (i, 1, nandmcb.devInfo.pageSizeBytes, 1, &nandmcb.page[1]);
187 if (ret < 0)
188 nand_free_return (ret);
190 if ((nandmcb.page[0] != 0xff) || (nandmcb.page[1] != 0xff)) {
191 nandmcb.blocks[i] = 0xff;
192 nandmcb.numBadBlocks += 1;
193 } else
194 nandmcb.blocks[i] = 0;
196 }
199 /* Construct the logical to physical block array */
200 for (i = j = 0; i < nandmcb.devInfo.totalBlocks; i++) {
201 if (nandmcb.blocks[i] != 0xff)
202 nandmcb.logicalToPhysMap[j++] = i;
203 }
205 /* Construct the physical to logical block array */
206 for (i = j = 0; i < nandmcb.devInfo.totalBlocks; i++) {
207 if (nandmcb.blocks[i] == 0xff)
208 nandmcb.physToLogicalMap[i] = 0xff;
209 else
210 nandmcb.physToLogicalMap[i] = j++;
211 }
214 /* Seek to the first byte of the file */
215 nandmcb.fpos = -1; /* Force a read on the first seek */
216 nandmcb.currentLogicalBlock = 0xffffffff;
217 nandmcb.currentPage = 0xffffffff;
218 nand_seek (0, 0);
221 return (0);
223 }
226 /**
227 * @b Description
228 * @n
229 * This function performs a reads from the current read point
230 *
231 */
232 Int32 nand_read (Uint8 *ptr_buf, Uint32 num_bytes)
233 {
234 Int32 i;
235 Int32 pIdx;
237 /* Convert the global file position to an offset in the currently cached page */
238 pIdx = nandmcb.fpos % nandmcb.devInfo.pageSizeBytes;
240 for (i = 0; i < num_bytes; i++) {
242 ptr_buf[i] = nandmcb.page[pIdx++];
243 nandmcb.fpos += 1;
245 if (pIdx >= nandmcb.devInfo.pageSizeBytes) {
247 pIdx = 0;
248 nandmcb.currentPage += 1;
250 if (nandmcb.currentPage >= nandmcb.devInfo.pagesPerBlock) {
251 nandmcb.currentPage = 0;
252 nandmcb.currentLogicalBlock += 1;
253 }
256 /* Load the new page */
257 if (nandHwDriverReadPage((Uint32)(nandmcb.logicalToPhysMap[nandmcb.currentLogicalBlock]), nandmcb.currentPage, nandmcb.page) < 0)
258 return (-2);
260 }
261 }
263 return (0);
265 }
267 /**
268 * @b Description
269 * @n
270 * This function performs a peek from the current read point.
271 */
272 Int32 nand_peek (Uint8 *ptr_buf, Uint32 num_bytes)
273 {
274 Int32 ret;
275 Int32 origPos;
276 Uint32 origLogicalBlock;
277 Uint32 origPage;
279 origPos = nandmcb.fpos;
280 origLogicalBlock = nandmcb.currentLogicalBlock;
281 origPage = nandmcb.currentPage;
283 ret = nand_read (ptr_buf, num_bytes);
285 if ( (origLogicalBlock != nandmcb.currentLogicalBlock) ||
286 (origPage != nandmcb.currentPage) ) {
288 if (nandHwDriverReadPage((Uint32)(nandmcb.logicalToPhysMap[origLogicalBlock]), origPage, nandmcb.page) < 0)
289 return (-2);
290 }
292 nandmcb.fpos = origPos;
293 nandmcb.currentLogicalBlock = origLogicalBlock;
294 nandmcb.currentPage = origPage;
296 return (ret);
298 }
300 /**
301 * @b Description
302 * @n
303 * This function returns how much data is available for immediate read.
304 * On nand this always returns the page size.
305 */
306 Int32 nand_query (void)
307 {
308 return (nandmcb.devInfo.pageSizeBytes);
309 }
312 /**
313 * @b Description
314 * @n
315 * This function closes the nand driver
316 */
317 Int32 nand_close (void)
318 {
319 nandHwDriverClose ();
320 return (nand_free_return (0));
322 }
326 /**
327 * @brief The global nand module function table
328 */
329 BOOT_MODULE_FXN_TABLE nand_boot_module =
330 {
331 nand_open, /* Open API */
332 nand_close, /* Close API */
333 nand_read, /* Read API */
334 NULL, /* Write API */
335 nand_peek, /* Peek API */
336 nand_seek, /* Seek API */
337 nand_query /* Query API */
339 };