summaryrefslogtreecommitdiff
path: root/memories/nandflash/TranslatedNandFlash.c
blob: 8200d427fa9bde40f36e6070eac88a206e843521 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
/* ----------------------------------------------------------------------------
 *         ATMEL Microcontroller Software Support 
 * ----------------------------------------------------------------------------
 * Copyright (c) 2008, Atmel Corporation
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer below.
 *
 * Atmel's name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ----------------------------------------------------------------------------
 */

//------------------------------------------------------------------------------
//         Headers
//------------------------------------------------------------------------------

#include "TranslatedNandFlash.h"
#include <utility/trace.h>
#include <utility/assert.h>
#include <utility/math.h>

#include <string.h>

//------------------------------------------------------------------------------
//         Internal definitions
//------------------------------------------------------------------------------

/// Casts
#define MAPPED(translated)          ((struct MappedNandFlash *) translated)
#define MANAGED(translated)         ((struct ManagedNandFlash *) translated)
#define ECC(translated)             ((struct EccNandFlash *) translated)
#define RAW(translated)             ((struct RawNandFlash *) translated)
#define MODEL(translated)           ((struct NandFlashModel *) translated)

/// Minimum number of blocks that should be kept unallocated
#define MINNUMUNALLOCATEDBLOCKS     32

/// Maximum allowed erase count difference
#define MAXERASEDIFFERENCE          5

//------------------------------------------------------------------------------
//         Internal functions
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
/// Returns 1 if there are enough free blocks to perform a single block
/// allocation; otherwise returns 0.
/// \param translated  Pointer to a TranslatedNandFlash instance.
//------------------------------------------------------------------------------
static unsigned char BlockCanBeAllocated(
    const struct TranslatedNandFlash *translated)
{
    unsigned short count;

    // Count number of free and dirty blocks (unallocated blocks)
    count = ManagedNandFlash_CountBlocks(MANAGED(translated), NandBlockStatus_DIRTY)
            + ManagedNandFlash_CountBlocks(MANAGED(translated), NandBlockStatus_FREE);

    // Check that count is greater than minimum number of unallocated blocks
    if (count > MINNUMUNALLOCATEDBLOCKS) {

        return 1;
    }
    else {

        return 0;
    }
}

//------------------------------------------------------------------------------
/// Returns 1 if the given page inside the currently written block is clean (has
/// not been written yet); otherwise returns 0.
/// \param translated  Pointer to a TranslatedNandFlash instance.
/// \param page  Page number.
//------------------------------------------------------------------------------
static unsigned char PageIsClean(
    const struct TranslatedNandFlash *translated,
    unsigned short page)
{
    ASSERT(page < NandFlashModel_GetBlockSizeInPages(MODEL(translated)),
           "PageIsClean: Page out-of-bounds\n\r");

    return ((translated->currentBlockPageStatuses[page / 8] >> (page % 8)) & 1) == 0;
}

//------------------------------------------------------------------------------
/// Marks the given page as being dirty (i.e. written).
/// \param translated  Pointer to a TranslatedNandFlash instance.
/// \param page  Page number.
//------------------------------------------------------------------------------
static void MarkPageDirty(
    struct TranslatedNandFlash *translated,
    unsigned short page)
{
    ASSERT(page < NandFlashModel_GetBlockSizeInPages(MODEL(translated)),
           "PageIsClean: Page out-of-bounds\n\r");

    translated->currentBlockPageStatuses[page / 8] |= 1 << (page % 8);
}

//------------------------------------------------------------------------------
/// Marks all pages as being clean.
/// \param translated  Pointer to a TranslatedNandFlash instance.
//------------------------------------------------------------------------------
static void MarkAllPagesClean(struct TranslatedNandFlash *translated)
{
    memset(translated->currentBlockPageStatuses, 0,
           sizeof(translated->currentBlockPageStatuses));
}

//------------------------------------------------------------------------------
/// Allocates the best-fitting physical block for the given logical block.
/// Returns 0 if successful; otherwise returns NandCommon_ERROR_NOBLOCKFOUND if
/// there are no more free blocks, or a NandCommon_ERROR code.
/// \param translated  Pointer to a TranslatedNandFlash instance.
/// \param block  Logical block number.
//------------------------------------------------------------------------------
static unsigned char AllocateBlock(
    struct TranslatedNandFlash *translated,
    unsigned short block)
{
    unsigned short freeBlock, liveBlock;
    unsigned char error;
    signed int eraseDifference;

    TRACE_DEBUG("Allocating a new block\n\r");

    // Find youngest free block and youngest live block
    if (ManagedNandFlash_FindYoungestBlock(MANAGED(translated),
                                           NandBlockStatus_FREE,
                                           &freeBlock)) {

        TRACE_ERROR("AllocateBlock: Could not find a free block\n\r");
        return NandCommon_ERROR_NOBLOCKFOUND;
    }

    // If this is the last free block, save the logical mapping in it and
    // clean dirty blocks
    TRACE_DEBUG("Number of FREE blocks: %d\n\r",
              ManagedNandFlash_CountBlocks(MANAGED(translated), NandBlockStatus_FREE));
    if (ManagedNandFlash_CountBlocks(MANAGED(translated),
                                     NandBlockStatus_FREE) == 1) {

        // Save mapping and clean dirty blocks
        TRACE_DEBUG("Last FREE block, cleaning up ...\n\r");

        error = MappedNandFlash_SaveLogicalMapping(MAPPED(translated), freeBlock);
        if (error) {

            TRACE_ERROR("AllocateBlock: Failed to save mapping\n\r");
            return error;
        }
        error = ManagedNandFlash_EraseDirtyBlocks(MANAGED(translated));
        if (error) {

            TRACE_ERROR("AllocatedBlock: Failed to erase dirty blocks\n\r");
            return error;
        }

        // Allocate new block
        return AllocateBlock(translated, block);
    }

    // Find youngest LIVE block to check the erase count difference
    if (!ManagedNandFlash_FindYoungestBlock(MANAGED(translated),
                                            NandBlockStatus_LIVE,
                                            &liveBlock)) {

        // Calculate erase count difference
        TRACE_DEBUG("Free block erase count = %d\n\r", MANAGED(translated)->blockStatuses[freeBlock].eraseCount);
        TRACE_DEBUG("Live block erase count = %d\n\r", MANAGED(translated)->blockStatuses[liveBlock].eraseCount);
        eraseDifference = absv(MANAGED(translated)->blockStatuses[freeBlock].eraseCount
                              - MANAGED(translated)->blockStatuses[liveBlock].eraseCount);

        // Check if it is too big
        if (eraseDifference > MAXERASEDIFFERENCE) {

            TRACE_WARNING("Erase difference too big, switching blocks\n\r");
            MappedNandFlash_Map(
                MAPPED(translated),
                MappedNandFlash_PhysicalToLogical(
                    MAPPED(translated),
                    liveBlock),
                freeBlock);
            ManagedNandFlash_CopyBlock(MANAGED(translated),
                                       liveBlock,
                                       freeBlock);

            // Allocate a new block
            return AllocateBlock(translated, block);
        }
    }

    // Map block
    TRACE_DEBUG("Allocating PB#%d for LB#%d\n\r", freeBlock, block);
    MappedNandFlash_Map(MAPPED(translated), block, freeBlock);

    return 0;
}

//------------------------------------------------------------------------------
//         Exported functions
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
/// Initializes a TranslatedNandFlash instance.
/// Returns 0 if successful; otherwise returns a NandCommon_ERROR_xxx code.
/// \param translated  Pointer to a TranslatedNandFlash instance.
/// \param model  Pointer to the underlying nand chip model. Can be 0.
/// \param commandAddress  Address at which commands are sent.
/// \param addressAddress  Address at which addresses are sent.
/// \param dataAddress  Address at which data is sent.
/// \param pinChipEnable  Pin controlling the CE signal of the NandFlash.
/// \param pinReadyBusy  Pin used to monitor the ready/busy signal of the Nand.
/// \param baseBlock Basic physical block address of mapped area.
/// \param sizeInBlocks Number of blocks that is mapped.
//------------------------------------------------------------------------------
unsigned char TranslatedNandFlash_Initialize(
    struct TranslatedNandFlash *translated,
    const struct NandFlashModel *model,
    unsigned int commandAddress,
    unsigned int addressAddress,
    unsigned int dataAddress,
    const Pin pinChipEnable,
    const Pin pinReadyBusy,
    unsigned short baseBlock,
    unsigned short sizeInBlocks)
{
    translated->currentLogicalBlock = -1;
    translated->previousPhysicalBlock = -1;
    MarkAllPagesClean(translated);

    // Initialize MappedNandFlash
    return MappedNandFlash_Initialize(MAPPED(translated),
                                      model,
                                      commandAddress,
                                      addressAddress,
                                      dataAddress,
                                      pinChipEnable,
                                      pinReadyBusy,
                                      baseBlock,
                                      sizeInBlocks);
}

//------------------------------------------------------------------------------
/// Reads the data and/or the spare area of a page on a translated nandflash.
/// If the block is not currently mapped but could be (i.e. there are available
/// physical blocks), then the data/spare is filled with 0xFF.
/// Returns 0 if successful; otherwise returns NandCommon_ERROR_NOMOREBLOCKS
/// if no more block can be allocated, or a NandCommon_ERROR code.
/// \param translated  Pointer to a TranslatedNandFlash instance.
/// \param block  Logical block number.
/// \param page  Number of page to read inside logical block.
/// \param data  Data area buffer, can be 0.
/// \param spare  Spare area buffer, can be 0.
//------------------------------------------------------------------------------
unsigned char TranslatedNandFlash_ReadPage(
    const struct TranslatedNandFlash *translated,
    unsigned short block,
    unsigned short page,
    void *data,
    void *spare)
{
    unsigned char error;

    TRACE_INFO("TranslatedNandFlash_ReadPage(B#%d:P#%d)\n\r", block, page);

    // If the page to read is in the current block, there is a previous physical
    // block and the page is clean -> read the page in the old block since the
    // new one does not contain meaningful data
    if ((block == translated->currentLogicalBlock)
        && (translated->previousPhysicalBlock != -1)
        && (PageIsClean(translated, page))) {

        TRACE_DEBUG("Reading page from current block\n\r");
        return ManagedNandFlash_ReadPage(MANAGED(translated),
                                         translated->previousPhysicalBlock,
                                         page,
                                         data,
                                         spare);
    }
    else {

        // Try to read the page from the logical block
        error = MappedNandFlash_ReadPage(MAPPED(translated), block, page, data, spare);
    
        // Block was not mapped
        if (error == NandCommon_ERROR_BLOCKNOTMAPPED) {
    
            ASSERT(!spare, "Cannot read the spare information of an unmapped block\n\r");
    
            // Check if a block can be allocated
            if (BlockCanBeAllocated(translated)) {
    
                // Return 0xFF in buffers with no error
                TRACE_DEBUG("Block #%d is not mapped but can be allocated, filling buffer with 0xFF\n\r", block);
                if (data) {
    
                    memset(data, 0xFF, NandFlashModel_GetPageDataSize(MODEL(translated)));
                }
                if (spare) {
    
                    memset(spare, 0xFF, NandFlashModel_GetPageSpareSize(MODEL(translated)));
                }
            }
            else {

                TRACE_ERROR("Block #%d is not mapped and there are no more blocks available\n\r", block);
                return NandCommon_ERROR_NOMOREBLOCKS;
            }
        }
        // Error
        else if (error) {
    
            return error;
        }
    }

    return 0;
}

//------------------------------------------------------------------------------
/// Writes the data and/or spare area of a page on a translated nandflash.
/// Allocates block has needed to keep the wear even between all blocks.
/// \param translated  Pointer to a TranslatedNandFlash instance.
/// \param block  Logical block number.
/// \param page  Number of page to write inside logical block.
/// \param data  Data area buffer, can be 0.
/// \param spare  Spare area buffer, can be 0.
//------------------------------------------------------------------------------
unsigned char TranslatedNandFlash_WritePage(
    struct TranslatedNandFlash *translated,
    unsigned short block,
    unsigned short page,
    void *data,
    void *spare)
{
    unsigned char allocate = 1;
    unsigned char error;

    TRACE_INFO("TranslatedNandFlash_WritePage(B#%d:P#%d)\n\r", block, page);

    // A new block must be allocated unless:
    // 1. the block is not mapped and there are no more blocks to allocate
    if (MappedNandFlash_LogicalToPhysical(MAPPED(translated), block) == -1) {

        // Block is not mapped, check if it can be
        if (!BlockCanBeAllocated(translated)) {

            TRACE_ERROR("TranslatedNandFlash_WritePage: Not enough free blocks\n\r");
            return NandCommon_ERROR_NOMOREBLOCKS;
        }
        TRACE_DEBUG("Allocate because block not mapped\n\r");
    }
    // or 2. the block to write is the current one and the page to write is
    // clean
    else if (translated->currentLogicalBlock == block) {

        if (PageIsClean(translated, page)) {

            TRACE_DEBUG("NO allocate because write in current block\n\r");
            allocate = 0;
        }
        else {

            TRACE_DEBUG("Allocate because page DIRTY in current block\n\r");
        }
    }
    else {

        TRACE_DEBUG("Allocate because block is mapped and different from current block\n\r");
    }

    // Allocate block if needed
    if (allocate) {

        // Flush current block write (if any) and then allocate block
        error = TranslatedNandFlash_Flush(translated);
        if (error) {

            return error;
        }
        translated->previousPhysicalBlock = MappedNandFlash_LogicalToPhysical(
                                                MAPPED(translated),
                                                block);
        TRACE_DEBUG("Previous physical block is now #%d\n\r",
                  translated->previousPhysicalBlock);
        error = AllocateBlock(translated, block);
        if (error) {

            return error;
        }

        // Block becomes the current block with all pages clean
        translated->currentLogicalBlock = block;
        MarkAllPagesClean(translated);
    }

    // Start writing page
    error = MappedNandFlash_WritePage(MAPPED(translated),
                                      block,
                                      page,
                                      data,
                                      spare);
    if (error) {

        return error;
    }

    // If write went through, mark page as written
    MarkPageDirty(translated, page);
    return 0;
}

//------------------------------------------------------------------------------
/// Terminates the current write operation by copying all the missing pages from
/// the previous physical block.
/// \param translated  Pointer to a TranslatedNandFlash instance.
//------------------------------------------------------------------------------
unsigned char TranslatedNandFlash_Flush(struct TranslatedNandFlash *translated)
{
    unsigned int i;
    unsigned char error;
    unsigned int currentPhysicalBlock;

    // Check if there is a current block and a previous block
    if ((translated->currentLogicalBlock == -1)
        || (translated->previousPhysicalBlock == -1)) {

        return 0;
    }

    TRACE_INFO("TranslatedNandFlash_Flush(PB#%d -> LB#%d)\n\r",
              translated->previousPhysicalBlock, translated->currentLogicalBlock);

    // Copy missing pages in the current block
    currentPhysicalBlock = MappedNandFlash_LogicalToPhysical(
                                MAPPED(translated),
                                translated->currentLogicalBlock);

    for (i=0; i < NandFlashModel_GetBlockSizeInPages(MODEL(translated)); i++) {

        if (PageIsClean(translated, i)) {

            TRACE_DEBUG("Copying back page #%d of block #%d\n\r", i,
                      translated->previousPhysicalBlock);

            // Copy page
            error = ManagedNandFlash_CopyPage(MANAGED(translated),
                                              translated->previousPhysicalBlock,
                                              i,
                                              currentPhysicalBlock,
                                              i);
            if (error) {

                TRACE_ERROR("FinishCurrentWrite: copy page #%d\n\r", i);
                return error;
            }
        }
    }

    translated->currentLogicalBlock = -1;
    translated->previousPhysicalBlock = -1;
    return 0;
}

//------------------------------------------------------------------------------
/// Erase all blocks in the tranalated area of nand flash.
/// \param managed  Pointer to a TranslatedNandFlash instance.
/// \param level    Erase level.
//------------------------------------------------------------------------------
unsigned char TranslatedNandFlash_EraseAll(
    struct TranslatedNandFlash *translated,
    unsigned char level)
{
    MappedNandFlash_EraseAll(MAPPED(translated), level);
    if (level > NandEraseDIRTY) {
        translated->currentLogicalBlock = -1;
        translated->previousPhysicalBlock = -1;
        MarkAllPagesClean(translated);
    }
    return 0;
}

//------------------------------------------------------------------------------
/// Allocates a free block to save the current logical mapping on it.
/// Returns 0 if successful; otherwise returns a NandCommon_ERROR code.
/// \param translated  Pointer to a TranslatedNandFlash instance.
//------------------------------------------------------------------------------
unsigned char TranslatedNandFlash_SaveLogicalMapping(
    struct TranslatedNandFlash *translated)
{
    unsigned char error;
    unsigned short freeBlock;

    TRACE_INFO("TranslatedNandFlash_SaveLogicalMapping()\n\r");

    // Save logical mapping in the youngest free block
    // Find the youngest block
    error = ManagedNandFlash_FindYoungestBlock(MANAGED(translated),
                                               NandBlockStatus_FREE,
                                               &freeBlock);
    if (error) {

        TRACE_ERROR("TranNF_SaveLogicalMapping: No free block\n\r");
        return error;
    }

    // Check if this is the last free block, in which case dirty blocks are wiped
    // prior to saving the mapping
    if (ManagedNandFlash_CountBlocks(MANAGED(translated),
                                     NandBlockStatus_FREE) == 1) {

        TranslatedNandFlash_Flush(translated);
        error = ManagedNandFlash_EraseDirtyBlocks(MANAGED(translated));
        if (error) {
        
            TRACE_ERROR("TranNF_Flush: Could not erase dirty blocks\n\r");
            return error;
        }
    }

    // Save the mapping
    error = MappedNandFlash_SaveLogicalMapping(MAPPED(translated), freeBlock);
    if (error) {

        TRACE_ERROR("TranNF_Flush: Failed to save mapping in #%d\n\r",
                    freeBlock);
        return error;
    }

    return 0;
}

//------------------------------------------------------------------------------
/// Returns the number of available blocks in a translated nandflash.
/// \param translated  Pointer to a TranslatedNandFlash instance.
//------------------------------------------------------------------------------
unsigned short TranslatedNandFlash_GetDeviceSizeInBlocks(
   const struct TranslatedNandFlash *translated)
{
    return ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(translated))
           - MINNUMUNALLOCATEDBLOCKS
           - ManagedNandFlash_CountBlocks(MANAGED(translated),
                                          NandBlockStatus_BAD)
           - 1; // Logical mapping block
}

//------------------------------------------------------------------------------
/// Returns the number of available pages in a translated nandflash.
/// \param translated  Pointer to a TranslatedNandFlash instance.
//------------------------------------------------------------------------------
unsigned int TranslatedNandFlash_GetDeviceSizeInPages(
   const struct TranslatedNandFlash *translated)
{
    return TranslatedNandFlash_GetDeviceSizeInBlocks(translated)
           * NandFlashModel_GetBlockSizeInPages(MODEL(translated));
}

//------------------------------------------------------------------------------
/// Returns the number of available data bytes in a translated nandflash.
/// \param translated  Pointer to a TranslatedNandFlash instance.
//------------------------------------------------------------------------------
unsigned long long TranslatedNandFlash_GetDeviceSizeInBytes(
   const struct TranslatedNandFlash *translated)
{
    return TranslatedNandFlash_GetDeviceSizeInPages(translated)
           * NandFlashModel_GetPageDataSize(MODEL(translated));
}

personal git repositories of Harald Welte. Your mileage may vary