Kyösti Mälkki (kyosti.malkki@gmail.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/1649
-gerrit
commit 05f592394eff03e6ec0ca51c9fd0ce39334cd2eb Author: Kyösti Mälkki kyosti.malkki@gmail.com Date: Sun Oct 28 12:19:03 2012 +0200
Add SMBus decoder
Decodes a series of IO accesses to the SMBus controller device as a series of bytes transmitted on the SMBus.
Tested mostly on Intel ICH4, some VIA seemed to be compatible too.
Change-Id: Id5248f4d9a2d550e8fd06d4252a67a404f719b77 Signed-off-by: Kyösti Mälkki kyosti.malkki@gmail.com --- SerialICE/simba/intel_smbus.lua | 259 +++++++++++++++++++ SerialICE/simba/serialice.lua | 1 + SerialICE/simba/smbus_host.lua | 559 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 819 insertions(+)
diff --git a/SerialICE/simba/intel_smbus.lua b/SerialICE/simba/intel_smbus.lua new file mode 100644 index 0000000..daa0b9d --- /dev/null +++ b/SerialICE/simba/intel_smbus.lua @@ -0,0 +1,259 @@ + + +dofile("smbus_host.lua") + +-- ******************************************** +-- Intel 82801 SMBus Host controller + +I801_SMB_HSTSTS = 0 +I801_SMB_HSTCNT = 2 +I801_SMB_HSTCMD = 3 +I801_SMB_HSTADD = 4 +I801_SMB_HSTDAT0 = 5 +I801_SMB_HSTDAT1 = 6 +I801_SMB_BLKDAT = 7 +I801_SMB_PEC = 8 +I801_SMB_AUXSTS = 12 +I801_SMB_AUXCTL = 13 + +I801_QUICK = 0x00 +I801_BYTE = 0x04 +I801_BYTE_DATA = 0x08 +I801_WORD_DATA = 0x0C +I801_PROC_CALL = 0x10 +I801_BLOCK_DATA = 0x14 +I801_I2C_BLOCK_DATA = 0x18 +I801_BLOCK_PROCESS = 0x1C +I801_BLOCK_LAST = 0x34 -- 0x14 | 0x20 +I801_I2C_BLOCK_LAST = 0x38 -- 0x18 | 0x20 +I801_START = 0x40 +I801_PEC_EN = 0x80 + + +local function intel_smbus_get_protocol(f) + local proto = bit32.band(f.reg.control, 0x1c) + + if proto == I801_QUICK then + return SMBUS_QUICK + + elseif proto == I801_BYTE then + return SMBUS_BYTE + + elseif proto == I801_BYTE_DATA then + return SMBUS_BYTE_DATA + + elseif proto == I801_WORD_DATA then + return SMBUS_WORD_DATA + + elseif proto == I801_PROC_CALL then + return SMBUS_PROC_CALL + + elseif proto == I801_BLOCK_DATA then + return SMBUS_BLOCK_DATA + + elseif proto == I801_I2C_BLOCK_DATA then + return SMBUS_I2C_BLOCK_DATA + + elseif proto == I801_BLOCK_PROCESS then + return SMBUS_BLOCK_PROCESS + + else + printk(f, action, "Unknown protocol\n") + return SMBUS_NOOP + end +end + +local function intel_smbus_host_status(f, action) + if not action.write then + if smbus.passive(f) then + f.reg.status = action.data; + end + + if smbus.state(f, HOST_IDLE) then + if not smbus.passive(f) then + f.reg.status = 0x0 + end + if bit32.band(f.reg.status, 0x40) ~= 0 then + printk(f, action, "Host may be busy, ignoring.\n") + end + smbus.get_resource(f) + + elseif smbus.state(f, HOST_ACTIVE) then + f.reg.busy_count = 3 + + elseif smbus.state(f, HOST_STARTED) then + if not smbus.passive(f) then + f.reg.status = bit32.band(f.reg.status, 0xFE) + if f.reg.busy_count > 0 then + f.reg.busy_count = f.reg.busy_count - 1 + f.reg.status = bit32.bor(f.reg.status, 0x01) + end + if bit32.band(f.reg.status, 0x02) == 0 then + smbus_transaction(host) + end + end + + local irq = bit32.band(f.reg.status, 0x02) ~= 0 + local failures = bit32.band(f.reg.status, 0x1c) ~= 0 + local host_busy = bit32.band(f.reg.status, 0x01) ~= 0 + + if irq and not host_busy then + smbus.done(f) + end + if failures then + smbus.timeout(f) + end + end + + if not smbus.passive(f) then + action.data = f.reg.status; + f.reg.status = bit32.bor(f.reg.status, 0x40) + end + else + + if not smbus.passive(f) then + f.reg.status = bit32.band(f.reg.status, bit32.bnot(action.data)) + end + + local ack_irq = bit32.band(action.data, 0x02) ~= 0 + local release_host = bit32.band(action.data, 0x40) ~= 0 + local failures = bit32.band(action.data, 0x1c) ~= 0 + if release_host then + smbus.put_resource(f) + end + if failures then + smbus.timeout_ack(f) + end + if ack_irq then + smbus.ack(f) + end + end + +end + + +local function intel_smbus_host_control(f, action) + + if not action.write then + f.reg.block_ptr=0; + if not smbus.passive(f) then + action.data = bit32.band(f.reg.control, bit32.bnot(0x40)) + end + + else + + f.reg.control = action.data; + if bit32.band(f.reg.control, 0x80) ~= 0 then + printk(f, action, "No PEC simulation\n") + end + + local abort = bit32.band(f.reg.control, 0x02) ~= 0 + local start = bit32.band(f.reg.control, 0x40) ~= 0 + if abort then + smbus.abort(f) + end + if start then + f.reg.block_ptr=0; + smbus.update_register(f, action, SMB_REG_PROTO) + smbus.start(f, intel_smbus_get_protocol(f)) + end + end +end + + + +local function intel_smbus_block_data(f, action) + if f.reg.block_ptr < MAX_BLOCK_SRAM then + smbus.block_data(f, action, f.reg.block_ptr) + end + f.reg.block_ptr = f.reg.block_ptr + 1 + smbus.update_register(f, action, SMB_REG_BLOCK) +end + +local function intel_smbus_host_access(f, action) + local reg = bit32.band(action.addr, (f.size-1)) + + -- mirror hw register both ways + local data_write = 0 + + -- Store this to display CS:IP etc. + f.host.action = action + + if reg == I801_SMB_HSTSTS then + intel_smbus_host_status(f, action); + + elseif reg == I801_SMB_HSTCNT then + intel_smbus_host_control(f, action); + + elseif reg == I801_SMB_HSTCMD then + smbus.update_register(f, action, SMB_REG_CMD) + + elseif reg == I801_SMB_HSTADD then + smbus.update_register(f, action, SMB_REG_SLAVE) + + elseif reg == I801_SMB_HSTDAT0 then + smbus.update_register(f, action, SMB_REG_DATA0) + + elseif reg == I801_SMB_HSTDAT1 then + smbus.update_register(f, action, SMB_REG_DATA1) + + elseif reg == I801_SMB_BLKDAT then + intel_smbus_block_data(f, action); + + elseif reg == I801_SMB_AUXSTS then + if data_write then + f.reg.aux_sts = action.data; + else + action.data = f.reg.aux_sts; + end + + elseif reg == I801_SMB_AUXCTL then + if data_write then + f.reg.aux_ctl = action.data; + else + action.data = f.reg.aux_ctl; + end + + else + printk(f, action, "Unknown register 0x%02x\n", reg); + end +end + + +function intel_smbus_host_pre(f, action) + if action.write then + intel_smbus_host_access(f, action) + end + return handle_action(f, action) +end + +function intel_smbus_host_post(f, action) + if not action.write then + intel_smbus_host_access(f, action) + end + return true +end + + +local intel_smbus_host = { + id = -1, + name = "i801-smbus", + pre = intel_smbus_host_pre, + post = intel_smbus_host_post, + hide = hide_smbus_io, + base = 0x0, + size = 0x0, +} + +function intel_smbus_setup(base, size) + local f = intel_smbus_host + f.base = bit32.band(base, bit32.bnot(size-1)) + f.size = size + if not f.reg then + f.reg = { control = 0, status = 0, busy_count = 0, block_ptr = 0, aux_ctl = 0, aux_sts = 0 } + end + smbus.init(f) + + enable_hook(io_hooks, f) +end + diff --git a/SerialICE/simba/serialice.lua b/SerialICE/simba/serialice.lua index 08625d6..9d2f672 100644 --- a/SerialICE/simba/serialice.lua +++ b/SerialICE/simba/serialice.lua @@ -35,6 +35,7 @@ hide_i8237_io = true hide_i8254_io = true hide_i8259_io = true hide_superio_cfg = true +hide_smbus_io = true
-- Set to "true" to log every memory and IO access log_everything = false diff --git a/SerialICE/simba/smbus_host.lua b/SerialICE/simba/smbus_host.lua new file mode 100644 index 0000000..a407843 --- /dev/null +++ b/SerialICE/simba/smbus_host.lua @@ -0,0 +1,559 @@ + + +local debug_smbus = false +smbus = {} + +smbus.host = { + state = { current = 0, jump_to = 0 }, + proto = 0, + start_proto = 0, + + passive = true, + signals = 0, + + slave = 0, + cmd = 0, + data0 = 0, + data1 = 0, + data0_valid = false, + data1_valid = false, + wr_data0 = 0, + wr_data1 = 0, + block_register = 0, + + block = {}, + max_index = 0, +} + +MAX_BLOCK_SRAM = 32 + +if true then + for i = 0, MAX_BLOCK_SRAM-1, 1 do + smbus.host.block[i] = { hw = 0, tmp = 0, hw_valid = false, tmp_valid = false } + end +end + +-- ******************* + +SIG_INUSE = 0 +SIG_RELEASE = 1 +SIG_START = 2 +SIG_ABORT = 3 +SIG_DONE = 4 +SIG_ACK = 5 +SIG_TIMEOUT = 6 +SIG_TIMEOUT_ACK = 7 +SIG_DATA_WRITE = 8 + +local function signal_reset(f) + f.host.signals = 0 +end + +local function signal_set(f, flag) + local mask = bit32.lshift(1, flag) + f.host.signals = bit32.bor(f.host.signals, mask) +end + +local function signal_clr(f, flag) + local mask = bit32.bnot(bit32.lshift(1, flag)) + f.host.signals = bit32.band(f.host.signals, mask) + +end + +local function signal_in(f, flag) + local mask = bit32.lshift(1, flag) + return bit32.band(f.host.signals, mask) ~= 0 +end + +-- ******************* + +-- SMBus Protocol Message Types +SMBUS_NOOP = 0 +SMBUS_QUICK = 1 +SMBUS_BYTE = 2 +SMBUS_BYTE_DATA = 3 +SMBUS_WORD_DATA = 4 +SMBUS_PROC_CALL = 5 +SMBUS_BLOCK_DATA = 6 +SMBUS_I2C_BLOCK_DATA = 7 +SMBUS_BLOCK_PROCESS = 8 + +local proto_name = {} +proto_name[SMBUS_NOOP] = "(no-op)" +proto_name[SMBUS_QUICK] = "quick" +proto_name[SMBUS_BYTE] = "byte" +proto_name[SMBUS_BYTE_DATA] = "byte_data" +proto_name[SMBUS_WORD_DATA] = "word_data" +proto_name[SMBUS_PROC_CALL] = "proc_call" +proto_name[SMBUS_BLOCK_DATA] = "block" +proto_name[SMBUS_I2C_BLOCK_DATA] = "i2c_block" +proto_name[SMBUS_BLOCK_PROCESS] = "block_process" + +-- SMBus Host +SMB_REG_CMD = 1 +SMB_REG_SLAVE = 2 +SMB_REG_DATA0 = 3 +SMB_REG_DATA1 = 4 +SMB_REG_BLOCK = 5 +SMB_REG_PROTO = 6 + + +local function dump_block(f, length) + local block = "" + local max_length = math.min(length, MAX_BLOCK_SRAM) + + for i=0, max_length-1, 1 do + if f.host.block[i].hw_valid then + block = block .. string.format(" %02x", f.host.block[i].hw) + else + block = block .. " xx" + end + end + return block +end + + +-- FIXME: Probably wrong for process calls and hw-dependent. +local function host_proto(f, proto) + return f.host.proto == proto +end + +local function host_reading(f) + return bit32.band(f.host.slave, 0x01) == 0x01 +end + + + +local function dump_transaction(f, action) + + local data0, data1, length, dir + local invalid_data = "xx" + local iodir = {} + iodir[0] = "<=" + iodir[1] = "=>" + + if host_reading(f) then + dir = iodir[1] + data0 = invalid_data + length = f.host.max_index + if f.host.data0_valid then + length = f.host.data0 + data0 = string.format("%02x", f.host.data0) + end + data1 = invalid_data + if f.host.data1_valid then + data1 = string.format("%02x", f.host.data1) + end + else + dir = iodir[0] + length = f.host.wr_data0 + data0 = string.format("%02x", f.host.wr_data0) + data1 = string.format("%02x", f.host.wr_data1) + end + + + local dump = string.format("%02x %s ", f.host.slave / 2, proto_name[f.host.proto]) + + if host_proto(f, SMBUS_QUICK) then + + elseif host_proto(f, SMBUS_BYTE) then + dump = dump .. string.format("%02x %s %s", f.host.cmd, dir, data0) + + elseif host_proto(f, SMBUS_BYTE_DATA) then + dump = dump .. string.format("%02x %s %s", f.host.cmd, dir, data0) + + elseif host_proto(f, SMBUS_WORD_DATA) then + dump = dump .. string.format("%02x %s %s%s", f.host.cmd, dir, data0, data1) + + elseif host_proto(f, SMBUS_PROC_CALL) then + dump = dump .. string.format("%02x %02x %02x %s %s %s", f.host.cmd, + f.host.wr_data0, f.host.wr_data1, iodir[1], data0, data1) + + elseif host_proto(f, SMBUS_BLOCK_DATA) then + dump = dump .. string.format("%02x len=%02d %s", f.host.cmd, length, dir) + dump = dump .. dump_block(f, length) .. "" + + elseif host_proto(f, SMBUS_I2C_BLOCK_DATA) then + dump = dump .. string.format("%02x %02x %02x len=%02d %s", + f.host.cmd, f.host.data0, f.host.data1, f.host.max_index, dir) + dump = dump .. dump_block(f, length) .. "" + + elseif host_proto(f, SMBUS_BLOCK_PROCESS) then + dump = dump .. string.format("%02x len=%02d %s", f.host.cmd, length, iodir[1]) + dump = dump .. dump_block(f, length) .. "" + else + dump = dump .. "Cannot parse command" + end + + if signal_in(f, SIG_TIMEOUT) then + action.undefined = true + dump = dump .. " (TIMEOUT) " + end + + printk(f, action, "%s\n", dump) +end + + +-- ******************* + +HOST_NOOP = 0 +HOST_IDLE = 1 +HOST_ACTIVE = 2 +HOST_STARTED = 3 +HOST_WAIT = 4 +HOST_COMPLETE = 5 +HOST_FAIL = 6 + +local ctrl_state = {} +ctrl_state[HOST_NOOP] = "noop" +ctrl_state[HOST_IDLE] = "idle" +ctrl_state[HOST_ACTIVE] = "active" +ctrl_state[HOST_STARTED] = "started" +ctrl_state[HOST_WAIT] = "wait" +ctrl_state[HOST_COMPLETE] = "complete" +ctrl_state[HOST_FAIL] = "failed" + +function dprintk(...) + if debug_smbus then + printk(...) + end +end + +local function host_jump(f, state) + f.host.state.jump_to = state +end + +local function host_change_state(f, prev_state, new_state) + + if new_state == HOST_NOOP then + --printk(f, f.host.action, "state switch to HOST_NOOP\n") + new_state = HOST_IDLE + end + + dprintk(f, f.host.action, "%s -> %s\n", + ctrl_state[prev_state], ctrl_state[new_state]) + + -- Jumping to current is no jump. + f.host.state.current = new_state + f.host.state.jump_to = new_state + + if smbus.state(f, HOST_IDLE) then + signal_reset(f) + + elseif smbus.state(f, HOST_ACTIVE) then + signal_reset(f) + signal_set(f, SIG_INUSE) + + elseif smbus.state(f, HOST_STARTED) then + local i + f.host.proto = f.host.start_proto + f.host.wr_data0 = f.host.data0 + f.host.wr_data1 = f.host.data1 + + -- Invalidation used with reads + f.host.data0_valid = false + f.host.data1_valid = false + + for i = 0, MAX_BLOCK_SRAM-1, 1 do + f.host.max_index = 0 + -- On block writes, previously read data in buffer is also valid. + if f.host.block[i].tmp_valid then + f.host.block[i].hw = f.host.block[i].tmp + f.host.block[i].tmp_valid = false + f.host.block[i].hw_valid = true + end + -- On block reads, no data in buffer is yet valid. + if host_reading(f) and host_proto(f, SMBUS_BLOCK_DATA) then + f.host.block[i].hw_valid = false; + end + end + + elseif smbus.state(f, HOST_COMPLETE) then + dump_transaction(f, f.host.action) + if signal_in(f, SIG_RELEASE) then + host_jump(f, HOST_IDLE) + else + host_jump(f, HOST_ACTIVE) + end + +-- elseif smbus.state(f, HOST_FAIL) then +-- dump_transaction(f, f.host.action) +-- host_jump(f, HOST_ACTIVE) + end + +end + +local function host_switch(f, new_state) + local prev_state = f.host.state.current + while prev_state ~= new_state do + host_change_state(f, prev_state, new_state) + prev_state = f.host.state.current + new_state = f.host.state.jump_to + end +end + +local function host_read_completed(f) + + if not host_reading(f) then + return true + end + + local complete = false + + if host_proto(f, SMBUS_QUICK) then + complete = true + + elseif host_proto(f, SMBUS_BYTE) then + complete = f.host.data0_valid + + elseif host_proto(f, SMBUS_BYTE_DATA) then + complete = f.host.data0_valid + + elseif host_proto(f, SMBUS_WORD_DATA) then + complete = f.host.data0_valid and f.host.data1_valid + + elseif host_proto(f, SMBUS_BLOCK_DATA) then + complete = f.host.data0_valid and f.host.max_index >= f.host.data0 + + elseif host_proto(f, SMBUS_PROC_CALL) or host_proto(f, SMBUS_I2C_BLOCK_DATA) + or host_proto(f, SMBUS_BLOCK_PROCESS) then + printk(f, f.host.action, "Unimplemented completion (proto %d)\n", f.host.proto) + end + return complete +end + + +-- Syncronize state machine according to input signals. +local function host_sync(f) + +-- if release and not smbus.state(f, HOST_ACTIVE) then +-- printk(f, f.host.action, "Premature reset of bit INUSE_STS\n") +-- end + if signal_in(f, SIG_ABORT) then + -- FIXME Killing on-going transaction. + host_switch(f, HOST_COMPLETE) + end + + if smbus.state(f, HOST_IDLE) then + if signal_in(f, SIG_INUSE) then + host_switch(f, HOST_ACTIVE) + end + if signal_in(f, SIG_START) then + host_switch(f, HOST_STARTED) + end + + elseif smbus.state(f, HOST_ACTIVE) then + if signal_in(f, SIG_START) then + host_switch(f, HOST_STARTED) + end + + elseif smbus.state(f, HOST_STARTED) then + if signal_in(f, SIG_TIMEOUT) then + host_switch(f, HOST_FAIL) + elseif signal_in(f, SIG_DONE) then + host_switch(f, HOST_WAIT) + end + + elseif smbus.state(f, HOST_WAIT) then + if signal_in(f, SIG_START) then + -- Restarting previous transaction. + host_switch(f, HOST_COMPLETE) + host_switch(f, HOST_STARTED) + elseif signal_in(f, SIG_ACK) and not host_reading(f) then + -- Complete after sw ack. + host_switch(f, HOST_COMPLETE) + elseif signal_in(f, SIG_DATA_WRITE) or host_read_completed(f) then + -- Complete after all data read or new data written + -- remain active + signal_clr(f, SIG_RELEASE) + host_switch(f, HOST_COMPLETE) + end + elseif smbus.state(f, HOST_FAIL) then + if signal_in(f, SIG_TIMEOUT_ACK) then + host_switch(f, HOST_COMPLETE) + end + end + + if signal_in(f, SIG_START) and not smbus.state(f, HOST_STARTED) then + printk(f, f.host.action, "Starting from illegal state\n"); + end + + signal_clr(f, SIG_DONE) + signal_clr(f, SIG_START) + signal_clr(f, SIG_DATA_WRITE) +end + + + +-- ******************************* + +-- Mutual exclusion. +function smbus.get_resource(f) + signal_set(f, SIG_INUSE) + host_sync(f) +end + +function smbus.put_resource(f) + signal_set(f, SIG_RELEASE) + host_sync(f) +end + +-- status +function smbus.state(f, state) + return f.host.state.current == state +end + +function smbus.passive(f) + return f.host.passive +end + +-- control +function smbus.start(f, proto) + signal_set(f, SIG_START) + f.host.start_proto = proto + host_sync(f) +end + +function smbus.timeout(f) + signal_set(f, SIG_TIMEOUT) + host_sync(f) +end + +function smbus.timeout_ack(f) + signal_set(f, SIG_TIMEOUT_ACK) + host_sync(f) +end + +function smbus.done(f) + signal_set(f, SIG_DONE) + host_sync(f) +end + +function smbus.ack(f) + signal_set(f, SIG_ACK) + host_sync(f) +end + +function smbus.abort(f) + signal_set(f, SIG_ABORT) + host_sync(f) +end + +-- A data read may complete and close an active transaction. +function smbus.data_read(f, action) + if not action.write then + signal_clr(f, SIG_DATA_WRITE) + host_sync(f) + end +end + +-- A data write will close active transaction. +function smbus.data_write(f, action) + if action.write then + signal_set(f, SIG_DATA_WRITE) + host_sync(f) + end +end + + + +function smbus.update_register(f, action, smb_reg) + + local data_write = action.write or smbus.passive(f) + + -- A write to data registers completes previous transaction. + smbus.data_write(f, action) + + if smb_reg == SMB_REG_SLAVE then + if data_write then + f.host.slave = action.data + else + action.data = f.host.slave + end + + elseif smb_reg == SMB_REG_CMD then + if data_write then + f.host.cmd = action.data + else + action.data = f.host.cmd + end + + elseif smb_reg == SMB_REG_DATA0 then + if data_write then + f.host.data0 = action.data + else + action.data = f.host.data0 + end + f.host.data0_valid = true + + elseif smb_reg == SMB_REG_DATA1 then + if data_write then + f.host.data1 = action.data + else + action.data = f.host.data1 + end + f.host.data1_valid = true + + elseif smb_reg == SMB_REG_BLOCK then + if data_write then + f.host.block_register = action.data + else + action.data = f.host.block_register + end + -- Nothing here, smbus.host_block_data updates datas. + -- This exist to check completion below for blocks. + elseif smb_reg == SMB_REG_PROTO then + -- Nothing here. Protocol updates when signalling start. + else + printk(f, f.host.action, "Undefined host register\n") + end + + -- New read data may complete a waiting transaction. + smbus.data_read(f, action) +end + +function smbus.block_data(f, action, index) + + if smbus.passive(f) then + if not action.write then + f.host.block[index].hw = action.data + f.host.block[index].hw_valid = true + end + f.host.block[index].tmp = action.data + f.host.block[index].tmp_valid = true + else + if action.write then + f.host.block[index].tmp = action.data + f.host.block[index].tmp_valid = true + else + action.data = 0xff + if f.host.block[index].tmp_valid then + action.data = f.host.block[index].tmp + elseif f.host.block[index].hw_valid then + action.data = f.host.block[index].hw + end + end + end + + -- Detect for block read completion via maximum indexed item. + if not action.write then + f.host.max_index = math.max(f.host.max_index, index+1) + end +end + +local init_action = { + name = "init", + cs = 0, + eip = 0, + my_id = 0, + parent_id = 0, +} +function smbus.init(f) + if not f.host then + f.host = smbus.host + f.host.action = init_action + end + host_switch(f, HOST_IDLE) +end + +