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) */
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);
142 }
145 /**
146 * @b Description
147 * @n
148 * Free any memory allocated by the driver
149 */
150 Int32 nand_free_return (Int32 retcode)
151 {
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);
166 }
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 *))
178 {
179 iblNand_t *ibln = (iblNand_t *)ptr_driver;
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 }
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 }
273 if (nandmcb.page[nandmcb.devInfo.badBlkMarkIdx[j]] != 0xff)
274 {
275 badBlock = TRUE;
276 break;
277 }
278 }
279 }
281 if (badBlock) {
282 nandmcb.blocks[i] = 0xff;
283 nandmcb.numBadBlocks += 1;
284 } else
285 nandmcb.blocks[i] = 0;
287 }
290 /* Construct the logical to physical block array */
291 for (i = startBlock, j = 0; 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 = startBlock, j = 0; 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 (0, 0);
312 return (0);
314 }
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)
324 {
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 }
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);
360 }
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)
368 {
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 }
391 nandmcb.fpos = origPos;
392 nandmcb.currentLogicalBlock = origLogicalBlock;
393 nandmcb.currentPage = origPage;
395 return (ret);
397 }
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)
406 {
407 return (nandmcb.devInfo.pageSizeBytes);
408 }
411 /**
412 * @b Description
413 * @n
414 * This function closes the nand driver
415 */
416 Int32 nand_close (void)
417 {
418 if (nandmcb.nand_if != NULL)
419 (*nandmcb.nand_if->nct_driverClose)();
421 nandmcb.nand_if = NULL;
423 return (nand_free_return (0));
425 }
429 /**
430 * @brief The global nand module function table
431 */
432 BOOT_MODULE_FXN_TABLE nand_boot_module =
433 {
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 };