diff options
Diffstat (limited to 'utils/hwstub/tools/lua/jz/nand.lua')
-rw-r--r-- | utils/hwstub/tools/lua/jz/nand.lua | 451 |
1 files changed, 451 insertions, 0 deletions
diff --git a/utils/hwstub/tools/lua/jz/nand.lua b/utils/hwstub/tools/lua/jz/nand.lua new file mode 100644 index 0000000000..2db8ab050b --- /dev/null +++ b/utils/hwstub/tools/lua/jz/nand.lua @@ -0,0 +1,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
\ No newline at end of file |