diff --git a/litex/soc/integration/builder.py b/litex/soc/integration/builder.py index b2b802711..fd78d4a76 100644 --- a/litex/soc/integration/builder.py +++ b/litex/soc/integration/builder.py @@ -203,6 +203,10 @@ class Builder: csr_base = self.soc.mem_regions['csr'].origin) write_to_file(os.path.join(self.generated_dir, "csr.h"), csr_contents) + # Generate I2C command/value table + i2c_contents = export.get_i2c_header(self.soc.i2c_init) + write_to_file(os.path.join(self.generated_dir, "i2c.h"), i2c_contents) + # Generate Git SHA1 of tools to git.h git_contents = export.get_git_header() write_to_file(os.path.join(self.generated_dir, "git.h"), git_contents) diff --git a/litex/soc/integration/export.py b/litex/soc/integration/export.py index c01ac191e..e6d1ceeb0 100644 --- a/litex/soc/integration/export.py +++ b/litex/soc/integration/export.py @@ -272,6 +272,47 @@ def get_csr_header(regions, constants, csr_base=None, with_access_functions=True r += "\n#endif\n" return r +def get_i2c_header(i2c_init_values): + r = generated_banner("//") + r += "#ifndef __GENERATED_I2C_H\n#define __GENERATED_I2C_H\n\n" + + if i2c_init_values: + r += "#include \n\n" + + r += "struct i2c_cmds {\n" + r += "\tstruct i2c_ops ops;\n" + r += "\tuint32_t *init_table;\n" + r += "\tint nb_cmds;\n" + r += "\tint addr_len;\n" + r += "\tint i2c_addr;\n" + r += "};\n" + + r += "\n#define I2C_INIT\n" + r += "#define I2C_INIT_DEVS {}\n\n".format(len(i2c_init_values)) + + for i, (dev, i2c_addr, table, _) in enumerate(i2c_init_values): + r += "uint32_t {}_{}_{}_init_table[{}] = {{\n".format(dev, hex(i2c_addr), i, len(table) * 2) + for addr, data in table: + r += "\t0x{:04X}, 0x{:02X},\n".format(addr, data) + r += "};\n" + + r += "static struct i2c_cmds i2c_init[I2C_INIT_DEVS] = {\n" + for i, (dev, i2c_addr, table, addr_len) in enumerate(i2c_init_values): + r += "\t{\n" + r += "\t\t.ops.write = {}_w_write,\n".format(dev) + r += "\t\t.ops.read = {}_r_read,\n".format(dev) + r += "\t\t.init_table = {}_{}_{}_init_table,\n".format(dev, hex(i2c_addr), i) + r += "\t\t.nb_cmds = {},\n".format(len(table)) + r += "\t\t.i2c_addr = {},\n".format(hex(i2c_addr)) + r += "\t\t.addr_len = {},\n".format(addr_len) + r += "\t},\n" + r += "};\n" + else: + r += "\n// No initialization values\n" + + r += "\n#endif\n" + return r + # JSON Export -------------------------------------------------------------------------------------- def get_csr_json(csr_regions={}, constants={}, mem_regions={}): diff --git a/litex/soc/integration/soc_core.py b/litex/soc/integration/soc_core.py index 6aaae1e0f..630d3dfa8 100644 --- a/litex/soc/integration/soc_core.py +++ b/litex/soc/integration/soc_core.py @@ -174,6 +174,9 @@ class SoCCore(LiteXSoC): # Wishbone Slaves. self.wb_slaves = {} + # I2C initialisation tables + self.i2c_init = [] + # Modules instances ------------------------------------------------------------------------ # Add SoCController @@ -269,6 +272,9 @@ class SoCCore(LiteXSoC): def add_csr_region(self, name, origin, busword, obj): self.csr_regions[name] = SoCCSRRegion(origin, busword, obj) + def add_i2c_init_table(self, dev, i2c_addr, table, addr_len=1): + self.i2c_init.append((dev, i2c_addr, table, addr_len)) + # Finalization --------------------------------------------------------------------------------- def do_finalize(self): diff --git a/litex/soc/software/bios/main.c b/litex/soc/software/bios/main.c index 13ae2d6c2..a31be759e 100644 --- a/litex/soc/software/bios/main.c +++ b/litex/soc/software/bios/main.c @@ -36,6 +36,7 @@ #include #include +#include #include @@ -91,6 +92,10 @@ __attribute__((__used__)) int main(int i, char **c) uart_init(); #endif +#ifdef CSR_I2C_BASE + i2c_send_init_cmds(); +#endif + #ifndef CONFIG_SIM_DISABLE_BIOS_PROMPT printf("\n"); printf("\e[1m __ _ __ _ __\e[0m\n"); diff --git a/litex/soc/software/libbase/i2c.c b/litex/soc/software/libbase/i2c.c index c6af33765..8e8c36864 100644 --- a/litex/soc/software/libbase/i2c.c +++ b/litex/soc/software/libbase/i2c.c @@ -1,13 +1,20 @@ // This file is Copyright (c) 2020 Antmicro +// This file is Copyright (c) 2022 Franck Jullien #include "i2c.h" +#include + #include +#include #ifdef CSR_I2C_BASE #define I2C_PERIOD_CYCLES (CONFIG_CLOCK_FREQUENCY / I2C_FREQ_HZ) #define I2C_DELAY(n) cdelay((n)*I2C_PERIOD_CYCLES/4) +i2c_write_t current_i2c_write = i2c_w_write; +i2c_read_t current_i2c_read = i2c_r_read; + static inline void cdelay(int i) { while(i > 0) { @@ -16,9 +23,47 @@ static inline void cdelay(int i) } } +int i2c_send_init_cmds(void) +{ +#ifdef I2C_INIT + struct i2c_cmds *i2c_cmd; + int dev, i, len; + uint8_t data[2]; + uint8_t addr; + + for (dev = 0; dev < I2C_INIT_DEVS; dev++) { + i2c_cmd = &i2c_init[dev]; + current_i2c_write = i2c_cmd->ops.write; + current_i2c_read = i2c_cmd->ops.read; + + for (i = 0; i < i2c_cmd->nb_cmds; i++) { + + if (i2c_cmd->addr_len == 2) { + len = 2; + addr = (i2c_cmd->init_table[i*2] >> 8) & 0xff; + data[0] = i2c_cmd->init_table[i*2] & 0xff; + data[1] = i2c_cmd->init_table[(i*2) + 1] & 0xff; + } else { + len = 1; + addr = i2c_cmd->init_table[i*2] & 0xff; + data[0] = i2c_cmd->init_table[(i*2) + 1] & 0xff; + } + + if (!i2c_write(i2c_cmd->i2c_addr, addr, data, len)) + printf("Error during i2c write at address 0x%04x\n", addr); + } + } + + current_i2c_write = i2c_w_write; + current_i2c_read = i2c_r_read; +#endif + + return 0; +} + static inline void i2c_oe_scl_sda(bool oe, bool scl, bool sda) { - i2c_w_write( + current_i2c_write( ((oe & 1) << CSR_I2C_W_OE_OFFSET) | ((scl & 1) << CSR_I2C_W_SCL_OFFSET) | ((sda & 1) << CSR_I2C_W_SDA_OFFSET) @@ -69,7 +114,7 @@ static int i2c_receive_bit(void) i2c_oe_scl_sda(0, 1, 0); I2C_DELAY(1); // read in the middle of SCL high - value = i2c_r_read() & 1; + value = current_i2c_read() & 1; I2C_DELAY(1); i2c_oe_scl_sda(0, 0, 0); I2C_DELAY(1); diff --git a/litex/soc/software/libbase/i2c.h b/litex/soc/software/libbase/i2c.h index 8e149145b..135542700 100644 --- a/litex/soc/software/libbase/i2c.h +++ b/litex/soc/software/libbase/i2c.h @@ -6,6 +6,15 @@ extern "C" { #endif #include +#include + +typedef void (*i2c_write_t)(uint32_t v); +typedef uint32_t (*i2c_read_t)(void); + +struct i2c_ops { + i2c_write_t write; + i2c_read_t read; +}; /* I2C frequency defaults to a safe value in range 10-100 kHz to be compatible with SMBus */ #ifndef I2C_FREQ_HZ @@ -19,6 +28,7 @@ void i2c_reset(void); bool i2c_write(unsigned char slave_addr, unsigned char addr, const unsigned char *data, unsigned int len); bool i2c_read(unsigned char slave_addr, unsigned char addr, unsigned char *data, unsigned int len, bool send_stop); bool i2c_poll(unsigned char slave_addr); +int i2c_send_init_cmds(void); #ifdef __cplusplus }