GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lx_nand_flash_open.c Lines: 96 129 74.4 %
Date: 2024-03-11 05:20:25 Branches: 46 77 59.7 %

Line Branch Exec Source
1
/***************************************************************************
2
 * Copyright (c) 2024 Microsoft Corporation
3
 *
4
 * This program and the accompanying materials are made available under the
5
 * terms of the MIT License which is available at
6
 * https://opensource.org/licenses/MIT.
7
 *
8
 * SPDX-License-Identifier: MIT
9
 **************************************************************************/
10
11
12
/**************************************************************************/
13
/**************************************************************************/
14
/**                                                                       */
15
/** LevelX Component                                                      */
16
/**                                                                       */
17
/**   NAND Flash                                                          */
18
/**                                                                       */
19
/**************************************************************************/
20
/**************************************************************************/
21
22
#define LX_SOURCE_CODE
23
24
25
/* Disable ThreadX error checking.  */
26
27
#ifndef LX_DISABLE_ERROR_CHECKING
28
#define LX_DISABLE_ERROR_CHECKING
29
#endif
30
31
32
/* Include necessary system files.  */
33
34
#include "lx_api.h"
35
36
37
/**************************************************************************/
38
/*                                                                        */
39
/*  FUNCTION                                               RELEASE        */
40
/*                                                                        */
41
/*    _lx_nand_flash_open                                 PORTABLE C      */
42
/*                                                           6.3.0        */
43
/*  AUTHOR                                                                */
44
/*                                                                        */
45
/*    Xiuwen Cai, Microsoft Corporation                                   */
46
/*                                                                        */
47
/*  DESCRIPTION                                                           */
48
/*                                                                        */
49
/*    This function opens a NAND flash instance and ensures the           */
50
/*    NAND flash is in a coherent state.                                  */
51
/*                                                                        */
52
/*  INPUT                                                                 */
53
/*                                                                        */
54
/*    nand_flash                            NAND flash instance           */
55
/*    name                                  Name of NAND flash instance   */
56
/*    nand_driver_initialize                Driver initialize             */
57
/*    memory_ptr                            Pointer to memory used by the */
58
/*                                            LevelX for this NAND.       */
59
/*    memory_size                           Size of memory - must at least*/
60
/*                                            7 * total block count +     */
61
/*                                            2 * page size               */
62
/*                                                                        */
63
/*  OUTPUT                                                                */
64
/*                                                                        */
65
/*    return status                                                       */
66
/*                                                                        */
67
/*  CALLS                                                                 */
68
/*                                                                        */
69
/*    (nand_driver_initialize)              Driver initialize             */
70
/*    _lx_nand_flash_memory_initialize      Initialize buffer             */
71
/*    _lx_nand_flash_driver_block_status_get                              */
72
/*                                          Get block status              */
73
/*    lx_nand_flash_driver_pages_read       Read pages                    */
74
/*    _lx_nand_flash_free_block_list_add    Add free block to list        */
75
/*    _lx_nand_flash_mapped_block_list_add  Add mapped block to list      */
76
/*    _lx_nand_flash_system_error           System error handler          */
77
/*    tx_mutex_create                       Create thread-safe mutex      */
78
/*                                                                        */
79
/*  CALLED BY                                                             */
80
/*                                                                        */
81
/*    Application Code                                                    */
82
/*                                                                        */
83
/*  RELEASE HISTORY                                                       */
84
/*                                                                        */
85
/*    DATE              NAME                      DESCRIPTION             */
86
/*                                                                        */
87
/*  03-08-2023     Xiuwen Cai               Initial Version 6.2.1         */
88
/*  10-31-2023     Xiuwen Cai               Modified comment(s),          */
89
/*                                            avoided clearing user       */
90
/*                                            extension in flash control  */
91
/*                                            block,                      */
92
/*                                            resulting in version 6.3.0  */
93
/*                                                                        */
94
/**************************************************************************/
95
5
UINT  _lx_nand_flash_open(LX_NAND_FLASH  *nand_flash, CHAR *name, UINT (*nand_driver_initialize)(LX_NAND_FLASH *),
96
                          ULONG* memory_ptr, UINT memory_size)
97
{
98
99
ULONG                       block;
100
ULONG                       page;
101
UCHAR                       block_status;
102
ULONG                       block_count;
103
UINT                        status;
104
LX_NAND_FLASH               *tail_ptr;
105
LX_NAND_DEVICE_INFO         *nand_device_info_page;
106
UCHAR                       *spare_buffer_ptr;
107
UCHAR                       *page_buffer_ptr;
108
ULONG                       page_type;
109
UCHAR                       page_index;
110
LX_INTERRUPT_SAVE_AREA
111
112
    LX_PARAMETER_NOT_USED(name);
113
114
    /* Clear the NAND flash control block. User extension is not cleared.  */
115
1505
    LX_MEMSET(nand_flash, 0, (ULONG)((UCHAR*)&(nand_flash -> lx_nand_flash_open_previous) - (UCHAR*)nand_flash) + sizeof(nand_flash -> lx_nand_flash_open_previous));
116
117
    /* Call the flash driver's initialization function.  */
118
5
    (nand_driver_initialize)(nand_flash);
119
120
    /* Determine if we can support this NAND flash size.  */
121

5
    if (nand_flash -> lx_nand_flash_pages_per_block > LX_NAND_MAX_PAGE_PER_BLOCK || nand_flash -> lx_nand_flash_total_blocks > LX_NAND_MAX_BLOCK_COUNT)
122
    {
123
124
        /* Return an error.  */
125
        return(LX_ERROR);
126
    }
127
128
    /* Check if it is new LevelX NAND driver.  */
129

5
    if (nand_flash -> lx_nand_flash_driver_pages_read == LX_NULL || nand_flash -> lx_nand_flash_driver_pages_write == LX_NULL || nand_flash -> lx_nand_flash_driver_pages_copy == LX_NULL)
130
    {
131
132
        /* Return an error.  */
133
        return(LX_ERROR);
134
    }
135
136
    /* Check the spare data length.  */
137
5
    if (nand_flash -> lx_nand_flash_spare_data1_length < sizeof(ULONG))
138
    {
139
140
        /* Return an error.  */
141
        return(LX_ERROR);
142
    }
143
144
    /* Calculate the number of words per block and per page.  */
145
5
    nand_flash -> lx_nand_flash_words_per_page =   (nand_flash -> lx_nand_flash_bytes_per_page / sizeof(ULONG));
146
5
    nand_flash -> lx_nand_flash_words_per_block =  (nand_flash -> lx_nand_flash_words_per_page * nand_flash -> lx_nand_flash_pages_per_block);
147
148
    /* Calculate the total pages.  */
149
5
    nand_flash -> lx_nand_flash_total_pages =   nand_flash -> lx_nand_flash_total_blocks * nand_flash -> lx_nand_flash_pages_per_block;
150
151
    /* Initialize memory buffer.  */
152
5
    status = _lx_nand_flash_memory_initialize(nand_flash, memory_ptr, memory_size);
153
5
    if (status != LX_SUCCESS)
154
    {
155
        return(status);
156
    }
157
158
    /* Initialize block numbers.  */
159
5
    nand_flash -> lx_nand_flash_metadata_block_number = LX_NAND_BLOCK_UNMAPPED;
160
5
    nand_flash -> lx_nand_flash_backup_metadata_block_number = LX_NAND_BLOCK_UNMAPPED;
161
162
    /* Setup page buffer and spare buffer pointers.  */
163
5
    page_buffer_ptr = nand_flash -> lx_nand_flash_page_buffer;
164
5
    spare_buffer_ptr = page_buffer_ptr + nand_flash -> lx_nand_flash_bytes_per_page;
165
166
    /* Loop through the blocks to check for bad blocks and determine the minimum and maximum erase count for each good block.  */
167
5
    for (block = 0; block < nand_flash -> lx_nand_flash_total_blocks; block++)
168
    {
169
170
        /* First, check to make sure this block is good.  */
171
5
        status =  _lx_nand_flash_driver_block_status_get(nand_flash, block, &block_status);
172
173
        /* Check for an error from flash driver.   */
174
5
        if (status)
175
        {
176
177
            /* Call system error handler.  */
178
            _lx_nand_flash_system_error(nand_flash, status, block, 0);
179
180
            /* Return an error.  */
181
            return(LX_ERROR);
182
        }
183
184
        /* Is this block bad?  */
185
5
        if (block_status != LX_NAND_GOOD_BLOCK)
186
        {
187
188
            /* Yes, this block is bad.  */
189
190
            /* Increment the number of bad blocks.  */
191
            nand_flash -> lx_nand_flash_bad_blocks++;
192
193
            /* Save the block status.  */
194
            nand_flash -> lx_nand_flash_block_status_table[block] = LX_NAND_BLOCK_STATUS_BAD;
195
196
            /* Continue to the next block.  */
197
            continue;
198
        }
199
200
        /* Call driver read function to read page 0.  */
201
#ifdef LX_NAND_ENABLE_CONTROL_BLOCK_FOR_DRIVER_INTERFACE
202
        status = (nand_flash -> lx_nand_flash_driver_pages_read)(nand_flash, block, 0, page_buffer_ptr, spare_buffer_ptr, 1);
203
#else
204
5
        status = (nand_flash -> lx_nand_flash_driver_pages_read)(block, 0, page_buffer_ptr, spare_buffer_ptr, 1);
205
#endif
206
207
        /* Check for an error from flash driver.   */
208
5
        if (status)
209
        {
210
211
            /* Call system error handler.  */
212
            _lx_nand_flash_system_error(nand_flash, status, block, 0);
213
214
            /* Determine if the error is fatal.  */
215
            if (status != LX_NAND_ERROR_CORRECTED)
216
            {
217
218
                /* Return an error.  */
219
                return(LX_ERROR);
220
            }
221
        }
222
223
        /* Get the page type.  */
224
5
        page_type = LX_UTILITY_LONG_GET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data1_offset]);
225
226
        /* Check if the type is device info.  */
227
5
        if (page_type == LX_NAND_PAGE_TYPE_DEVICE_INFO)
228
        {
229
230
            /* Get the device info page.  */
231
5
            nand_device_info_page = (LX_NAND_DEVICE_INFO*)page_buffer_ptr;
232
233
            /* Check signature.  */
234
5
            if (nand_device_info_page -> lx_nand_device_info_signature1 == LX_NAND_DEVICE_INFO_SIGNATURE1 &&
235
5
                nand_device_info_page -> lx_nand_device_info_signature2 == LX_NAND_DEVICE_INFO_SIGNATURE2)
236
            {
237
238
                /* Save the block numbers.  */
239
5
                nand_flash -> lx_nand_flash_metadata_block_number = nand_device_info_page -> lx_nand_device_info_metadata_block_number;
240
5
                nand_flash -> lx_nand_flash_backup_metadata_block_number = nand_device_info_page -> lx_nand_device_info_backup_metadata_block_number;
241
5
                break;
242
            }
243
244
        }
245
246
    }
247
248
    /* Check if we have found the metadata block.  */
249
5
    if (nand_flash -> lx_nand_flash_metadata_block_number == LX_NAND_BLOCK_UNMAPPED)
250
    {
251
252
        /* Not found, return an error.  */
253
        return (LX_ERROR);
254
    }
255
256
    /* Initialize metadata block numbers and lists.  */
257
5
    nand_flash -> lx_nand_flash_metadata_block_number_current = nand_flash -> lx_nand_flash_metadata_block_number;
258
5
    nand_flash -> lx_nand_flash_backup_metadata_block_number_current = nand_flash -> lx_nand_flash_backup_metadata_block_number;
259
5
    nand_flash -> lx_nand_flash_metadata_block[0] = (USHORT)nand_flash -> lx_nand_flash_metadata_block_number;
260
5
    nand_flash -> lx_nand_flash_backup_metadata_block[0] = (USHORT)nand_flash -> lx_nand_flash_backup_metadata_block_number;
261
262
    /* Found one metadata block.  */
263
5
    nand_flash -> lx_nand_flash_metadata_block_count = 1;
264
265
    /* Clear searched block count.  */
266
5
    block_count = 0;
267
268
    do
269
    {
270
271
        /* Initialize next block to unmapped.  */
272
7
        nand_flash -> lx_nand_flash_metadata_block_number_next = LX_NAND_BLOCK_UNMAPPED;
273
7
        nand_flash -> lx_nand_flash_backup_metadata_block_number_next = LX_NAND_BLOCK_UNMAPPED;
274
275
        /* Loop to read pages in the metadata block.  */
276
601
        for (page = 0; page < nand_flash -> lx_nand_flash_pages_per_block ; page++)
277
        {
278
279
            /* Call driver read function to read page.  */
280
#ifdef LX_NAND_ENABLE_CONTROL_BLOCK_FOR_DRIVER_INTERFACE
281
            status = (nand_flash -> lx_nand_flash_driver_pages_read)(nand_flash, block, page, page_buffer_ptr, spare_buffer_ptr, 1);
282
#else
283
594
            status = (nand_flash -> lx_nand_flash_driver_pages_read)(block, page, page_buffer_ptr, spare_buffer_ptr, 1);
284
#endif
285
286
            /* Check for an error from flash driver.   */
287
594
            if (status)
288
            {
289
290
                /* Call system error handler.  */
291
                _lx_nand_flash_system_error(nand_flash, status, block, 0);
292
293
                /* Determine if the error is fatal.  */
294
                if (status != LX_NAND_ERROR_CORRECTED)
295
                {
296
297
                    /* Return an error.  */
298
                    return(LX_ERROR);
299
                }
300
            }
301
302
            /* Get page type and page index.  */
303
594
            page_type = LX_UTILITY_LONG_GET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data1_offset]) & (~LX_NAND_PAGE_TYPE_PAGE_NUMBER_MASK);
304
594
            page_index = LX_UTILITY_LONG_GET(&spare_buffer_ptr[nand_flash -> lx_nand_flash_spare_data1_offset]) & LX_NAND_PAGE_TYPE_PAGE_NUMBER_MASK;
305
306
            /* Process metadata page by type.  */
307

594
            switch (page_type)
308
            {
309
5
            case LX_NAND_PAGE_TYPE_DEVICE_INFO:
310
311
                /* This page is for device info.  */
312
5
                nand_device_info_page = (LX_NAND_DEVICE_INFO*)page_buffer_ptr;
313
314
                /* Get the base erase count.  */
315
5
                nand_flash -> lx_nand_flash_base_erase_count = nand_device_info_page -> lx_nand_device_info_base_erase_count;
316
5
                break;
317
318
10
            case LX_NAND_PAGE_TYPE_ERASE_COUNT_TABLE:
319
320
                /* Check if page index is valid.  */
321
10
                if (((ULONG)page_index + 1) * nand_flash -> lx_nand_flash_bytes_per_page > nand_flash -> lx_nand_flash_erase_count_table_size)
322
                {
323
324
                    /* Invalid page index. Return an error.  */
325
                    status = LX_ERROR;
326
                    break;
327
                }
328
329
                /* Copy page data to erase count table.  */
330
10
                LX_MEMCPY(nand_flash -> lx_nand_flash_erase_count_table + page_index * nand_flash -> lx_nand_flash_bytes_per_page, /* Use case of memcpy is verified. */
331
                            page_buffer_ptr, nand_flash -> lx_nand_flash_bytes_per_page);
332
10
                break;
333
334
23
            case LX_NAND_PAGE_TYPE_BLOCK_MAPPING_TABLE:
335
336
                /* Check if page index is valid.  */
337
23
                if (((ULONG)page_index + 1) * nand_flash -> lx_nand_flash_bytes_per_page > nand_flash -> lx_nand_flash_block_mapping_table_size)
338
                {
339
340
                    /* Invalid page index. Return an error.  */
341
                    status = LX_ERROR;
342
                    break;
343
                }
344
345
                /* Copy page data to block mapping table.  */
346
23
                LX_MEMCPY(nand_flash -> lx_nand_flash_block_mapping_table + page_index * nand_flash -> lx_nand_flash_bytes_per_page / sizeof(*nand_flash -> lx_nand_flash_block_mapping_table), /* Use case of memcpy is verified. */
347
                    page_buffer_ptr, nand_flash -> lx_nand_flash_bytes_per_page);
348
23
                break;
349
350
544
            case LX_NAND_PAGE_TYPE_BLOCK_STATUS_TABLE:
351
352
                /* Check if page index is valid.  */
353
544
                if (((ULONG)page_index + 1) * nand_flash -> lx_nand_flash_bytes_per_page > nand_flash -> lx_nand_flash_block_status_table_size)
354
                {
355
356
                    /* Invalid page index. Return an error.  */
357
                    status = LX_ERROR;
358
                    break;
359
                }
360
361
                /* Copy page data to block status table.  */
362
544
                LX_MEMCPY(nand_flash -> lx_nand_flash_block_status_table + page_index * nand_flash -> lx_nand_flash_bytes_per_page / sizeof(*nand_flash -> lx_nand_flash_block_status_table), /* Use case of memcpy is verified. */
363
                    page_buffer_ptr, nand_flash -> lx_nand_flash_bytes_per_page);
364
544
                break;
365
366
5
            case LX_NAND_PAGE_TYPE_FREE_PAGE:
367
368
                /* Found a free page. Update current page.  */
369
5
                nand_flash -> lx_nand_flash_metadata_block_current_page = page;
370
5
                nand_flash -> lx_nand_flash_backup_metadata_block_current_page = page;
371
372
                /* Skip all the remaining pages.  */
373
5
                page = nand_flash -> lx_nand_flash_pages_per_block;
374
5
                break;
375
376
7
            case LX_NAND_PAGE_TYPE_BLOCK_LINK:
377
378
                /* Found next blocks. Update next block numbers.  */
379
7
                nand_flash -> lx_nand_flash_metadata_block_number_next = LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_MAIN_METADATA_OFFSET]);
380
7
                nand_flash -> lx_nand_flash_backup_metadata_block_number_next = LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_BACKUP_METADATA_OFFSET]);
381
382
                /* Save block numbers in metadata block lists.  */
383
7
                nand_flash -> lx_nand_flash_metadata_block[nand_flash -> lx_nand_flash_metadata_block_count] = (USHORT)LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_MAIN_METADATA_OFFSET]);
384
7
                nand_flash -> lx_nand_flash_backup_metadata_block[nand_flash -> lx_nand_flash_metadata_block_count] = (USHORT)LX_UTILITY_LONG_GET(&page_buffer_ptr[LX_NAND_BLOCK_LINK_BACKUP_METADATA_OFFSET]);
385
386
                /* Increase metadata block count.  */
387
7
                nand_flash -> lx_nand_flash_metadata_block_count++;
388
389
7
                break;
390
391
            default:
392
393
                /* Unknown type, return error.  */
394
                status = LX_ERROR;
395
            }
396
397
            /* Check status.  */
398
594
            if (status == LX_ERROR)
399
            {
400
401
                /* Error, break the loop.  */
402
                break;
403
            }
404
405
        }
406
407
        /* Check if we have reached the last page.  */
408
7
        if (page == nand_flash -> lx_nand_flash_pages_per_block)
409
        {
410
411
            /* Move to next block.  */
412
2
            nand_flash -> lx_nand_flash_metadata_block_number_current = nand_flash -> lx_nand_flash_metadata_block_number_next;
413
2
            nand_flash -> lx_nand_flash_backup_metadata_block_number_current = nand_flash -> lx_nand_flash_backup_metadata_block_number_next;
414
415
            /* Make sure the block is valid.  */
416
2
            if (nand_flash -> lx_nand_flash_metadata_block_number_current == LX_NAND_BLOCK_UNMAPPED)
417
            {
418
419
                /* Error, break the loop.  */
420
                break;
421
            }
422
423
            /* Get the block to process.  */
424
2
            block = nand_flash -> lx_nand_flash_metadata_block_number_current;
425
        }
426
427
        /* Increase the processed block number.  */
428
7
        block_count++;
429
430
        /* If block count is larger than total blocks, there is an error.  */
431
7
        if (block_count >= nand_flash -> lx_nand_flash_total_blocks)
432
        {
433
434
            /* Break the loop.  */
435
            break;
436
        }
437

7
    } while (nand_flash -> lx_nand_flash_metadata_block_current_page == 0 && status != LX_ERROR);
438
439
    /* Check if metadata page is processed correctly.  */
440

5
    if (nand_flash -> lx_nand_flash_metadata_block_current_page == 0 || status == LX_ERROR)
441
    {
442
443
        /* Return an error.  */
444
        return(LX_ERROR);
445
    }
446
447
    /* Loop to build free and mapped block lists.  */
448
5125
    for (block = 0; block < nand_flash -> lx_nand_flash_total_blocks; block++)
449
    {
450
451
        /* Check for free blocks.  */
452
5120
        if (nand_flash -> lx_nand_flash_block_status_table[block] == LX_NAND_BLOCK_STATUS_FREE)
453
        {
454
455
            /* Add the block to free block list.  */
456
5093
            _lx_nand_flash_free_block_list_add(nand_flash, block);
457
        }
458
459
        /* Check for mapped blocks.  */
460
5120
        if (nand_flash -> lx_nand_flash_block_mapping_table[block] != LX_NAND_BLOCK_UNMAPPED)
461
        {
462
463
            /* Add the block to free block list.  */
464
3
            _lx_nand_flash_mapped_block_list_add(nand_flash, block);
465
        }
466
    }
467
468
469
#ifdef LX_THREAD_SAFE_ENABLE
470
471
    /* If the thread safe option is enabled, create a ThreadX mutex that will be used in all external APIs
472
       in order to provide thread-safe operation.  */
473
    status =  tx_mutex_create(&nand_flash -> lx_nand_flash_mutex, "NAND Flash Mutex", TX_NO_INHERIT);
474
475
    /* Determine if the mutex creation encountered an error.  */
476
    if (status != LX_SUCCESS)
477
    {
478
479
        /* Call system error handler, since this should not happen.  */
480
        _lx_nand_flash_system_error(nand_flash, LX_SYSTEM_MUTEX_CREATE_FAILED, 0, 0);
481
482
        /* Return error to caller.  */
483
        return(LX_ERROR);
484
    }
485
#endif
486
487
    /* Lockout interrupts.  */
488
5
    LX_DISABLE
489
490
    /* At this point, the NAND flash has been opened successfully.  Place the
491
       NAND flash control block on the linked list of currently opened NAND flashes.  */
492
493
    /* Set the NAND flash state to open.  */
494
5
    nand_flash -> lx_nand_flash_state =  LX_NAND_FLASH_OPENED;
495
496
    /* Place the NAND flash control block on the list of opened NAND flashes.  First,
497
       check for an empty list.  */
498
5
    if (_lx_nand_flash_opened_count)
499
    {
500
501
        /* List is not empty - other NAND flashes are open.  */
502
503
        /* Pickup tail pointer.  */
504
        tail_ptr =  _lx_nand_flash_opened_ptr -> lx_nand_flash_open_previous;
505
506
        /* Place the new NAND flash control block in the list.  */
507
        _lx_nand_flash_opened_ptr -> lx_nand_flash_open_previous =  nand_flash;
508
        tail_ptr -> lx_nand_flash_open_next =                       nand_flash;
509
510
        /* Setup this NAND flash's opened links.  */
511
        nand_flash -> lx_nand_flash_open_previous =  tail_ptr;
512
        nand_flash -> lx_nand_flash_open_next =      _lx_nand_flash_opened_ptr;
513
    }
514
    else
515
    {
516
517
        /* The opened NAND flash list is empty.  Add the NAND flash to empty list.  */
518
5
        _lx_nand_flash_opened_ptr =                 nand_flash;
519
5
        nand_flash -> lx_nand_flash_open_next =      nand_flash;
520
5
        nand_flash -> lx_nand_flash_open_previous =  nand_flash;
521
    }
522
523
    /* Increment the opened NAND flash counter.  */
524
5
    _lx_nand_flash_opened_count++;
525
526
    /* Restore interrupts.  */
527
5
    LX_RESTORE
528
529
    /* Return a successful completion.  */
530
5
    return(LX_SUCCESS);
531
}
532