summaryrefslogtreecommitdiff
path: root/memories/nandflash/NandFlashModel.c
blob: 759a4cac05cba2422bcd8a2cf4a1609da080bd44 (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
/* ----------------------------------------------------------------------------
 *         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 "NandFlashModel.h"
#include "NandCommon.h"
#if defined(CHIP_NAND_CTRL)
#include <hsmc4/hsmc4.h>
#endif
#include <utility/trace.h>

#include <string.h>

//------------------------------------------------------------------------------
//         Internal definitions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Get the power of input, given a certain result, i.e. input^(power) = result.
/// returns the value of "power" if succesfully find the power.
/// \param result  a certain output we want to calculate.
/// \param input  the input of the power.
//------------------------------------------------------------------------------
#if defined(OP_BOOTSTRAP_on)
unsigned int CALPOW(unsigned int result, unsigned int input)
{
	unsigned int i=0;

	while(i<32)
	{
		if(result == (input << i))
			return i;
		i++;
	}

	return 0;
}
#endif

//------------------------------------------------------------------------------
/// Get the interger part of input, given a certain result, i.e.  return = result / input.
/// returns the value of interger part of the result/input.
/// \param result  a certain output we want to calculate.
/// \param input  the input of the division.
//------------------------------------------------------------------------------
#if defined(OP_BOOTSTRAP_on)
unsigned int CALINT(unsigned int result, unsigned int input)
{
	unsigned int i=0;
	unsigned int tmpInput=0;
        
    while(1)
    {
    	tmpInput +=input;
  		i++;
	    if(tmpInput == result)
	      return i;
	    else if (tmpInput > result)
	      return (i-1);
    }

}
#endif


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

//------------------------------------------------------------------------------
/// Looks for a NandFlashModel corresponding to the given ID inside a list of
/// model. If found, the model variable is filled with the correct values.
/// This function returns 0 if a matching model has been found; otherwise it
/// returns NandCommon_ERROR_UNKNOWNMODEL.
/// \param modelList  List of NandFlashModel instances.
/// \param size  Number of models in list.
/// \param chipId  Identifier returned by the Nand(id1|(id2<<8)|(id3<<16)|(id4<<24)).
/// \param model  NandFlashModel instance to update with the model parameters.
//------------------------------------------------------------------------------
unsigned char NandFlashModel_Find(
    const struct NandFlashModel *modelList,
    unsigned int size,
    unsigned int chipId,
    struct NandFlashModel *model)
{
    unsigned char found = 0, id2, id4;
    unsigned int i;
    #if defined(CHIP_NAND_CTRL)
    unsigned char pageSize = 0;
    #endif
    id2 = (unsigned char)(chipId>>8);
    id4 = (unsigned char)(chipId>>24);

    TRACE_INFO("Nandflash ID is 0x%08X\n\r", chipId);

    for(i=0; i<size; i++) {
        if(modelList[i].deviceId == id2) {
            found = 1;

            if(model) {

                memcpy(model, &modelList[i], sizeof(struct NandFlashModel));

                if(model->blockSizeInKBytes == 0 || model->pageSizeInBytes == 0) {
                    TRACE_DEBUG("Fetch from ID4(0x%.2x):\r\n", id4);
                    /// Fetch from the extended ID4
                    /// ID4 D5  D4 BlockSize || D1  D0  PageSize
                    ///     0   0   64K      || 0   0   1K
                    ///     0   1   128K     || 0   1   2K
                    ///     1   0   256K     || 1   0   4K
                    ///     1   1   512K     || 1   1   8k
                    #if !defined(OP_BOOTSTRAP_on)
                    switch(id4 & 0x03) {
                        case 0x00: model->pageSizeInBytes = 1024; break;
                        case 0x01: model->pageSizeInBytes = 2048; break;
                        case 0x02: model->pageSizeInBytes = 4096; break;
                        case 0x03: model->pageSizeInBytes = 8192; break;
                    }
                    switch(id4 & 0x30) {
                        case 0x00: model->blockSizeInKBytes = 64;  break;
                        case 0x10: model->blockSizeInKBytes = 128; break;
                        case 0x20: model->blockSizeInKBytes = 256; break;
                        case 0x30: model->blockSizeInKBytes = 512; break;
                    }
					#else
					model->pageSizeInBytes = 1024 << (id4 & 0x03);
                    model->blockSizeInKBytes = (64) << ((id4 & 0x30) >>4);
					#endif
                }
                #if defined(CHIP_NAND_CTRL)
                    switch(model->pageSizeInBytes) {
                        case 1024: pageSize = AT91C_HSMC4_PAGESIZE_1056_Bytes; break;
                        case 2048: pageSize = AT91C_HSMC4_PAGESIZE_2112_Bytes; break;
                        case 4096: pageSize = AT91C_HSMC4_PAGESIZE_4224_Bytes; break;
                        default: TRACE_ERROR("Unsupportted page size for NAND Flash Controller\n\r");
                    }
                    HSMC4_SetMode(pageSize | AT91C_HSMC4_DTOMUL_1048576 | AT91C_HSMC4_EDGECTRL |AT91C_HSMC4_DTOCYC | AT91C_HSMC4_RSPARE);
                #endif
            }
            TRACE_DEBUG("NAND Model found:\r\n");
            TRACE_DEBUG(" * deviceId = 0x%02X\r\n", model->deviceId);
            TRACE_DEBUG(" * deviceSizeInMegaBytes = %d\r\n", model->deviceSizeInMegaBytes);
            TRACE_DEBUG(" * blockSizeInkBytes = %d\r\n", model->blockSizeInKBytes);
            TRACE_DEBUG(" * pageSizeInBytes = %d\r\n", model->pageSizeInBytes);
            TRACE_DEBUG(" * options = 0x%02X\r\n", model->options);
            break;
        }
    }

    // Check if chip has been detected
    if (found) {

        return 0;
    }
    else {

        return NandCommon_ERROR_UNKNOWNMODEL;
    }
}

//------------------------------------------------------------------------------
/// Translates address/size access of a NandFlashModel to block, page and
/// offset values. The values are stored in the provided variables if their
/// pointer is not 0.
/// Returns 0 if the access is correct; otherwise returns
/// NandCommon_ERROR_OUTOFBOUNDS.
/// \param model  Pointer to a NandFlashModel instance.
/// \param address  Access address.
/// \param size  Access size in bytes.
/// \param block  Stores the first accessed block number.
/// \param page  Stores the first accessed page number inside the first block.
/// \param offset  Stores the byte offset inside the first accessed page.
//------------------------------------------------------------------------------
unsigned char NandFlashModel_TranslateAccess(
    const struct NandFlashModel *model,
    unsigned int address,
    unsigned int size,
    unsigned short *block,
    unsigned short *page,
    unsigned short *offset)
{
     // Check that access is not too big
    #if !defined(OP_BOOTSTRAP_on)
    if ((address + size) > NandFlashModel_GetDeviceSizeInBytes(model)) {

        TRACE_DEBUG("NandFlashModel_TranslateAccess: out-of-bounds access.\n\r");
        return NandCommon_ERROR_OUTOFBOUNDS;
    }
	#endif

    // Get Nand info
    unsigned int blockSize = NandFlashModel_GetBlockSizeInBytes(model);
    unsigned int pageSize = NandFlashModel_GetPageDataSize(model);

    // Translate address
    #if !defined(OP_BOOTSTRAP_on)
    unsigned short tmpBlock = address / blockSize;
    address -= tmpBlock * blockSize;
    unsigned short tmpPage = address / pageSize;
    address -= tmpPage * pageSize;
    unsigned short tmpOffset = address;
	#else
	unsigned short tmpBlock = CALINT(address, blockSize);
	address -= tmpBlock * blockSize;
	unsigned short tmpPage = CALINT(address, pageSize);
	address -= tmpPage * pageSize;
	unsigned short tmpOffset= address;
	#endif

    // Save results
    if (block) {

        *block = tmpBlock;
    }
    if (page) {

        *page = tmpPage;
    }
    if (offset) {

        *offset = tmpOffset;
    }

    return 0;
}

//------------------------------------------------------------------------------
/// Returns the spare area placement scheme used by a particular nandflash
/// model.
/// \param model  Pointer to a NandFlashModel instance.
//------------------------------------------------------------------------------
const struct NandSpareScheme * NandFlashModel_GetScheme(
    const struct NandFlashModel *model)
{
    return model->scheme;
}

//------------------------------------------------------------------------------
/// Returns the device ID of a particular NandFlash model.
/// \param model  Pointer to a NandFlashModel instance.
//------------------------------------------------------------------------------
unsigned char NandFlashModel_GetDeviceId(
   const struct NandFlashModel *model)
{
    return model->deviceId;
}

//------------------------------------------------------------------------------
/// Returns the number of blocks in the entire device.
/// \param model  Pointer to a NandFlashModel instance.
//------------------------------------------------------------------------------
unsigned short NandFlashModel_GetDeviceSizeInBlocks(
   const struct NandFlashModel *model)
{
	#if !defined(OP_BOOTSTRAP_on)
    return ((1024) / model->blockSizeInKBytes) * model->deviceSizeInMegaBytes;
	#else
	unsigned int pow;
	pow = CALPOW((1024 * model->deviceSizeInMegaBytes), model->blockSizeInKBytes);
	return (0x1 << pow);
	#endif
}

//------------------------------------------------------------------------------
/// Returns the number of pages in the entire device.
/// \param model  Pointer to a NandFlashModel instance.
//------------------------------------------------------------------------------
unsigned int NandFlashModel_GetDeviceSizeInPages(
   const struct NandFlashModel *model)
{
    return (unsigned int) NandFlashModel_GetDeviceSizeInBlocks(model) //* 8 // HACK
           * NandFlashModel_GetBlockSizeInPages(model);
}

//------------------------------------------------------------------------------
/// Returns the size of the whole device in bytes (this does not include the
/// size of the spare zones).
/// \param model  Pointer to a NandFlashModel instance.
//------------------------------------------------------------------------------
unsigned long long NandFlashModel_GetDeviceSizeInBytes(
   const struct NandFlashModel *model)
{
    return ((unsigned long long) model->deviceSizeInMegaBytes) << 20;
}

//------------------------------------------------------------------------------
/// Returns the size of the whole device in Mega bytes (this does not include the
/// size of the spare zones).
/// \param model  Pointer to a NandFlashModel instance.
//------------------------------------------------------------------------------
unsigned int NandFlashModel_GetDeviceSizeInMBytes(
   const struct NandFlashModel *model)
{
    return ((unsigned int) model->deviceSizeInMegaBytes);
}


//------------------------------------------------------------------------------
/// Returns the number of pages in one single block of a device.
/// \param model  Pointer to a NandFlashModel instance.
//------------------------------------------------------------------------------
unsigned short NandFlashModel_GetBlockSizeInPages(
   const struct NandFlashModel *model)
{
    #if !defined(OP_BOOTSTRAP_on)
    return model->blockSizeInKBytes * 1024 / model->pageSizeInBytes;
	#else
	unsigned int pow;
	pow = CALPOW((model->blockSizeInKBytes * 1024), model->pageSizeInBytes);
	return (0x1 << pow);
	#endif
}

//------------------------------------------------------------------------------
/// Returns the size in bytes of one single block of a device. This does not
/// take into account the spare zones size.
/// \param model  Pointer to a NandFlashModel instance.
//------------------------------------------------------------------------------
unsigned int NandFlashModel_GetBlockSizeInBytes(
   const struct NandFlashModel *model)
{
    return (model->blockSizeInKBytes *1024);
}

//------------------------------------------------------------------------------
/// Returns the size of the data area of a page in bytes.
/// \param model  Pointer to a NandFlashModel instance.
//------------------------------------------------------------------------------
unsigned short NandFlashModel_GetPageDataSize(
   const struct NandFlashModel *model)
{
    return model->pageSizeInBytes;
}

//------------------------------------------------------------------------------
/// Returns the size of the spare area of a page in bytes.
/// \param model  Pointer to a NandFlashModel instance.
//------------------------------------------------------------------------------
unsigned char NandFlashModel_GetPageSpareSize(
   const struct NandFlashModel *model)
{
    return (model->pageSizeInBytes>>5); /// Spare size is 16/512 of data size
}

//------------------------------------------------------------------------------
/// Returns the number of bits used by the data bus of a NandFlash device.
/// \param model  Pointer to a NandFlashModel instance.
//------------------------------------------------------------------------------
unsigned char NandFlashModel_GetDataBusWidth(
   const struct NandFlashModel *model)
{
    return (model->options&NandFlashModel_DATABUS16)? 16: 8;
}

//------------------------------------------------------------------------------
/// Returns 1 if the given NandFlash model uses the "small blocks/pages"
/// command set; otherwise returns 0.
/// \param model  Pointer to a NandFlashModel instance.
//------------------------------------------------------------------------------
unsigned char NandFlashModel_HasSmallBlocks(
   const struct NandFlashModel *model)
{
    return (model->pageSizeInBytes <= 512 )? 1: 0;
}

//------------------------------------------------------------------------------
/// Returns 1 if the device supports the copy-back operation. Otherwise returns
/// 0.
/// \param model  Pointer to a NandFlashModel instance.
//------------------------------------------------------------------------------
unsigned char NandFlashModel_SupportsCopyBack(
    const struct NandFlashModel *model)
{
    return ((model->options & NandFlashModel_COPYBACK) != 0);
}
personal git repositories of Harald Welte. Your mileage may vary