rockbox/utils/hwstub/tools/lua/jz/nand.lua
Amaury Pouly 4fd9400458 hwstub/tools/shell: add JZ4760B and Fiio X1 code
The jz code can do several useful things like dumping the IPL and SPL.
The Fiio code can play with backlight and has code do dump the IPL
and SPL with the correct parameters (extracted by reverse engineering).

Change-Id: I317b3174f5db8d38c9a56670c1d45565142ec208
2017-01-24 15:17:46 +01:00

451 lines
No EOL
15 KiB
Lua

---
--- 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