4fd9400458
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
451 lines
No EOL
15 KiB
Lua
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 |