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
|
---
--- GPIO
---
JZ.nand = {}
JZ.nand.rom = {}
function JZ.nand.init_pins(buswidth)
-- PA[21,19,18]: cs1, fre, fwe
JZ.gpio.pinmask(0, 0x2c0000).std_function(0)
JZ.gpio.pinmask(0, 0x2c0000).pull(false)
if buswidth == 16 then
-- PA[15:0]: d{15-0}
JZ.gpio.pinmask(0, 0xffff).std_function(0)
else
-- PA[7:0]: d{7-0}
JZ.gpio.pinmask(0, 0xff).std_function(0)
end
-- PB[1:0]: ale, cle
JZ.gpio.pinmask(1, 3).std_function(0)
JZ.gpio.pinmask(1, 3).pull(false)
-- PA20: rb#
JZ.gpio.pin(0, 20).gpio_in()
end
function JZ.nand.send_cmd(cmd)
DEV.write8(0xba400000, cmd)
end
function JZ.nand.send_addr(addr)
DEV.write8(0xba800000, addr)
end
function JZ.nand.read_data8()
return DEV.read8(0xba000000)
end
function JZ.nand.read_data32()
-- Boot ROM cannot do 16-bit read/write (those end up being 2x8-bit)
return DEV.read32(0xba000000)
end
function JZ.nand.wait_ready()
local pin = JZ.gpio.pin(0, 20)
-- wait ready
while pin.read() == 0 do end
end
function JZ.nand.set_buswidth(buswidth)
if buswidth == 8 then
HW.NEMC.SMC[1].BW.write("8BIT")
elseif buswidth == 16 then
HW.NEMC.SMC[1].BW.write("16BIT")
else
error("invalid buswidth")
end
end
-- {row,col}cycle must be 2 or 3
-- buswidth must be 8 or 16
-- count is the number of bytes to read (must be multiple of 2 is buswidth is 16)
-- function returns a table of bytes
function JZ.nand.read_page(col,colcycle,row,rowcycle,buswidth,count)
JZ.nand.set_buswidth(buswidth)
-- read page first cycle
JZ.nand.send_cmd(0)
-- column
JZ.nand.send_addr(col)
JZ.nand.send_addr(bit32.rshift(col, 8))
if colcycle == 3 then
JZ.nand.send_addr(bit32.rshift(col, 16))
end
-- row
JZ.nand.send_addr(row)
JZ.nand.send_addr(bit32.rshift(row, 8))
if rowcycle == 3 then
JZ.nand.send_addr(bit32.rshift(row, 16))
end
-- read page second cycle
JZ.nand.send_cmd(0x30)
-- wait ready
JZ.nand.wait_ready()
-- read
return JZ.nand.read_page_data(buswidth,count)
end
function JZ.nand.read_page2(params,col,row,count)
return JZ.nand.read_page(col,params.col_cycle,row,params.row_cycle,params.bus_width,count)
end
-- read data, assuming read page command was sent
function JZ.nand.read_page_data(buswidth,count)
-- read
data = {}
if buswidth == 8 then
for i=0, count-1 do
data[i] = JZ.nand.read_data8()
end
else
for i=0, count-1, 4 do
local hw = JZ.nand.read_data32()
data[i] = bit32.band(hw, 0xff)
data[i + 1] = bit32.band(bit32.rshift(hw, 8), 0xff)
data[i + 2] = bit32.band(bit32.rshift(hw, 16), 0xff)
data[i + 3] = bit32.band(bit32.rshift(hw, 24), 0xff)
end
end
return data
end
function JZ.nand.read_oob(params,page)
--[[
NAND flash are magic, every setup is different, so basically:
- if the page size is 512, that's a special case and we need to use a special
command to read OOB, also note that 512-byte NAND only have one cycle column address
- otherwise, assume OOB is after the data (so at offset page_size in bytes),
but beware that for 16-bit bus, column address is in words, not bytes
For simplicity, we do not support 512-byte NAND at the moment
]]
-- compute column address of OOB data
local col_addr = params.page_size
if params.bus_width == 16 then
col_addr = params.page_size / 2
end
-- read page
return JZ.nand.read_page2(params,col_addr,page,params.oob_size)
end
--[[
setup NAND parameters, the table should contain:
- bus_width: 8 or 16,
- addr_setup_time: in cycles
- addr_hold_time: in cycles
- write_strobe_time: in cycles
- read_strobe_time: in cycles
- recovery_time: in cycles
]]
function JZ.nand.setup(params)
JZ.nand.init_pins(params.bus_width)
HW.NEMC.SMC[1].BL.write(params.bus_width == 8 and "8" or "16")
HW.NEMC.SMC[1].TAS.write(params.addr_setup_time)
HW.NEMC.SMC[1].TAH.write(params.addr_hold_time)
HW.NEMC.SMC[1].TBP.write(params.write_strobe_time)
HW.NEMC.SMC[1].TAW.write(params.read_strobe_time)
HW.NEMC.SMC[1].STRV.write(params.recovery_time)
end
function JZ.nand.reset()
print("NAND: reset")
JZ.nand.send_cmd(0xff)
JZ.nand.wait_ready()
end
-- init nand like ROM
function JZ.nand.rom.init()
-- init pins to 16-bit in doubt
JZ.nand.init_pins(16)
-- take safest setting: 8-bit, max TAS, max TAH, max read/write strobe wait
HW.NEMC.SMC[1].write(0xfff7700)
-- enable flash on CS1 with CS# always asserted
HW.NEMC.NFC.write(3)
-- reset
JZ.nand.reset()
end
function JZ.nand.rom.parse_flag(data, offset)
local cnt_55 = 0
local cnt_aa = 0
for i = offset,offset + 31 do
if data[i] == 0x55 then
cnt_55 = cnt_55 + 1
elseif data[i] == 0xaa then
cnt_aa = cnt_aa + 1
end
end
if cnt_55 >= 7 then
return 0x55
elseif cnt_aa >= 7 then
return 0xaa
else
return 0xff
end
end
function JZ.nand.rom.read_page(col,row,count)
return JZ.nand.read_page(col, JZ.nand.rom.colcycle, row, JZ.nand.rom.rowcycle,
JZ.nand.rom.buswidth, count)
end
function JZ.nand.rom.read_page_data(count)
return JZ.nand.read_page_data(JZ.nand.rom.buswidth, count)
end
-- read flash parameters
function JZ.nand.rom.read_flags()
local flags = nil
-- read first page
for colcycle = 2,3 do
flags = JZ.nand.read_page(0,colcycle,0,3,8,160)
local buswidth_flag = JZ.nand.rom.parse_flag(flags, 0)
if buswidth_flag == 0x55 then
-- set to 8-bit
JZ.nand.rom.colcycle = colcycle
JZ.nand.rom.buswidth = 8
break
elseif buswidth_flag == 0xaa then
-- set to 16-bit
JZ.nand.rom.colcycle = colcycle
JZ.nand.rom.buswidth = 16
break
end
end
if JZ.nand.rom.buswidth == nil then
error("Cannot read flags")
end
print("NAND: colcycle = " .. JZ.nand.rom.colcycle)
print("NAND: buswidth = " .. JZ.nand.rom.buswidth)
-- reread flags correctly now
flags = JZ.nand.read_page(0,JZ.nand.rom.colcycle,0,3,JZ.nand.rom.buswidth,160)
-- rowcycle
local rowcycle_flag = JZ.nand.rom.parse_flag(flags, 64)
if rowcycle_flag == 0x55 then
JZ.nand.rom.rowcycle = 2
elseif rowcycle_flag == 0xaa then
JZ.nand.rom.rowcycle = 3
else
error("invalid rowcycle flag")
end
print("NAND: rowcycle = " .. JZ.nand.rom.rowcycle)
-- pagesize
local pagesize1_flag = JZ.nand.rom.parse_flag(flags, 96)
local pagesize0_flag = JZ.nand.rom.parse_flag(flags, 128)
if pagesize1_flag == 0x55 and pagesize0_flag == 0x55 then
JZ.nand.rom.pagesize = 512
elseif pagesize1_flag == 0x55 and pagesize0_flag == 0xaa then
JZ.nand.rom.pagesize = 2048
elseif pagesize1_flag == 0xaa and pagesize0_flag == 0x55 then
JZ.nand.rom.pagesize = 4096
elseif pagesize1_flag == 0xaa and pagesize0_flag == 0xaa then
JZ.nand.rom.pagesize = 8192
else
error(string.format("invalid pagesize flag: %#x,%#x", pagesize1_flag, pagesize0_flag))
end
print("NAND: pagesize = " .. JZ.nand.rom.pagesize)
end
-- read bootloader
function JZ.nand.rom.read_bootloader()
-- computer number of blocks per page
local bl_size = 256
local bl_per_page = JZ.nand.rom.pagesize / bl_size
local ecc_per_bl = 39
local bootloader_size = 8 * 1024
local bootloader = {}
local page_offset = 0
while true do
local all_ok = true
print("NAND: try at page offset " .. page_offset)
for page = 0, bootloader_size / JZ.nand.rom.pagesize - 1 do
print("NAND: page " .. page)
-- enable randomizer
HW.NEMC.PNC.write(3)
-- read ECC
local ecc = JZ.nand.rom.read_page(0, page_offset + 2 * page + 1, ecc_per_bl * bl_per_page)
-- disable randomizer
HW.NEMC.PNC.write(0)
HW.NEMC.NFC.write(0)
HW.NEMC.NFC.write(3)
-- send read page commannd, but don't read the data just yet
JZ.nand.rom.read_page(0, page_offset + 2 * page, 0)
-- for each block
for bl = 0, bl_per_page - 1 do
print("NAND: block " .. bl)
-- enable randomizer (except for first block of first page)
if page ~=0 or bl ~= 0 then
HW.NEMC.PNC.write(3)
end
-- read data
local data = JZ.nand.rom.read_page_data(bl_size)
-- disable randomizer
HW.NEMC.PNC.write(0)
-- setup bch
HW.BCH.INTS.write(0xffffffff)
HW.BCH.CTRL.SET.write(0x2b)
HW.BCH.CTRL.CLR.write(0x4)
HW.BCH.COUNT.DEC.write((bl_size + ecc_per_bl) * 2)
for i = 0, bl_size - 1 do
HW.BCH.DATA.write(data[i])
end
for i = 0, ecc_per_bl - 1 do
HW.BCH.DATA.write(ecc[bl * ecc_per_bl + i])
end
while HW.BCH.INTS.DECF.read() == 0 do
end
HW.BCH.CTRL.CLR.write(1)
print(string.format("NAND: ecc = 0x%x", HW.BCH.INTS.read()))
-- now fix the errors
if HW.BCH.INTS.UNCOR.read() == 1 then
print("NAND: uncorrectable errors !")
all_ok = false
end
print(string.format("NAND: correcting %d errors", HW.BCH.INTS.ERRC.read()))
if HW.BCH.INTS.ERRC.read() > 0 then
error("Error correction is not implemented for now")
end
for i = 0, bl_size - 1 do
bootloader[(page * bl_per_page + bl) * bl_size + i] = data[i]
end
end
end
if all_ok then
break
end
page_offset = page_offset + 16 * 1024 / JZ.nand.rom.pagesize
end
return bootloader
end
--[[
read SPL: offset and size in pages, the param table should contain:
- page_size: page size in bytes (exclusing spare)
- oob_size: spare data size in bytes
- page_per_block: number of pages per block
- ecc_pos: offset within spare of the ecc data
- badblock_pos: offset within spare of the badblock marker (only one page per block is marked)
- badblock_page: page number within block of the page containing badblock marker in spare
- col_cycle: number of cycles for column address
- row_cycle: number of cycles for row address
- ecc_size: ECC size in bytes
- ecc_level: level of error correction in bits: 4, 8, 12, ...
]]
function JZ.nand.rom.read_spl(params, offset, size)
--[[
On-flash format: each block contains page_per_block pages,
where each page contains page_size bytes, follows by oob_size spare bytes.
The spare area contains a bad block marker at offset badblock_pos and
the ECC data at offset ecc_pos. Note that only one page within each block
actually contains the bad block marker: this page is badblock_page. The marker
is 0xff is the block is valid. Any invalid block is skipped.
The ECC is computed on a per-512-block basis. Since a page contains several such
blocks, the ECC data contains consecutive ecc blocks, one for each 512-byte data
block.
+---------------------+
|page0|page1|...|pageN| <--- block
+---------------------+
+-------------------------------+
|data(page_size)|spare(oob_size)| <--- page
+-------------------------------+
+---------------------------------------------+
|xxxx|badblock marker(1)|xxxxx|ECC data()|xxxx| <-- spare
+---------------------------------------------+
]]
local bootloader = {}
if (offset % params.page_per_block) ~= 0 then
print("Warning: SPL is not block-aligned")
end
-- setup parameters
JZ.nand.setup(params)
-- enable NAND
HW.NEMC.NFC.write(3)
-- reset
JZ.nand.reset()
-- read SPL !
local checked_block = false
local cur_page = offset
local loaded_pages = 0
while loaded_pages < size do
::load_loop::
-- if we just crossed a page boundary, reset the block check flag
if (cur_page % params.page_per_block) == 0 then
checked_block = false
end
-- check block for bad block marker if needed
if not checked_block then
print("Reading bad block marker for block " .. (cur_page / params.page_per_block) .. "...")
-- read OOB data
local oob_data = JZ.nand.read_oob(params,cur_page + params.badblock_page)
if oob_data[params.badblock_pos] ~= 0xff then
print("Bad block at " .. (cur_page / page_per_block))
-- skip block
cur_page = ((cur_page + params.page_per_block) / params.page_per_block) * params.page_per_block
-- lua has no continue...
goto load_loop
end
checked_block = true
end
print("Reading page " .. cur_page .. "...")
-- send read page command
JZ.nand.read_page2(params,0,cur_page, 0)
local page_data = JZ.nand.read_page_data(params.bus_width,params.page_size)
local oob_data = JZ.nand.read_page_data(params.bus_width,params.oob_size)
-- handle each 512-byte block for ECC
local bl_size = 512
for bl = 0,params.page_size/bl_size-1 do
print("Checking subblock " .. bl .. "...")
-- setup bch
HW.BCH.INTS.write(0xffffffff)
HW.BCH.CTRL.CLR.BSEL.write() -- clear level
HW.BCH.CTRL.SET.BSEL.write(params.ecc_level / 4 - 1) -- set level
HW.BCH.CTRL.SET.write(3) -- enable and reset
HW.BCH.CTRL.CLR.ENCE.write(0x4) -- decode
-- write ecc data count
HW.BCH.COUNT.DEC.write((bl_size + params.ecc_size) * 2)
-- write data
for j = 0, bl_size - 1 do
HW.BCH.DATA.write(page_data[bl_size * bl + j])
end
-- write ecc data
for j = 0, params.ecc_size - 1 do
HW.BCH.DATA.write(oob_data[params.ecc_pos + bl * params.ecc_size + j])
end
-- wait until bch is done
while HW.BCH.INTS.DECF.read() == 0 do
end
-- disable bch
HW.BCH.CTRL.CLR.write(1)
print(string.format("NAND: ecc = 0x%x", HW.BCH.INTS.read()))
-- now fix the errors
if HW.BCH.INTS.UNCOR.read() == 1 then
error("NAND: uncorrectable errors !")
end
print(string.format("NAND: correcting %d errors", HW.BCH.INTS.ERRC.read()))
if HW.BCH.INTS.ERRC.read() > 0 then
error("Error correction is not implemented for now")
end
for i = 0, bl_size - 1 do
bootloader[loaded_pages * params.page_size + bl * bl_size + i] = page_data[bl_size * bl + i]
end
end
cur_page = cur_page + 1
loaded_pages = loaded_pages + 1
end
-- disable NAND
HW.NEMC.NFC.write(0)
return bootloader
end
-- dump data
function JZ.nand.rom.write_to_file(file, data)
local f = io.open(file, "w")
if f == nil then error("Cannot open file or write to nil") end
local i = 0
while type(data[i]) == "number" do
f:write(string.char(data[i]))
i = i + 1
end
io.close(f)
end
|