--- c/src/lib/libbsp/arm/beagle/Makefile.am | 4 +- c/src/lib/libbsp/arm/beagle/i2c/bbb-i2c.c | 788 ++++++++++++++++++++++++++++++ c/src/lib/libbsp/arm/beagle/include/i2c.h | 153 +++++- 3 files changed, 926 insertions(+), 19 deletions(-) create mode 100644 c/src/lib/libbsp/arm/beagle/i2c/bbb-i2c.c
diff --git a/c/src/lib/libbsp/arm/beagle/Makefile.am b/c/src/lib/libbsp/arm/beagle/Makefile.am index 8bb8478..4da72bc 100644 --- a/c/src/lib/libbsp/arm/beagle/Makefile.am +++ b/c/src/lib/libbsp/arm/beagle/Makefile.am @@ -88,7 +88,6 @@ libbsp_a_SOURCES += ../../shared/timerstub.c libbsp_a_SOURCES += ../../shared/cpucounterread.c libbsp_a_SOURCES += ../shared/startup/bsp-start-memcpy.S libbsp_a_SOURCES += ../shared/arm-cp15-set-exception-handler.c -libbsp_a_SOURCES += ../shared/arm-cp15-set-ttb-entries.c # Startup libbsp_a_SOURCES += startup/bspreset.c @@ -116,6 +115,9 @@ libbsp_a_SOURCES += ../../shared/console.c \ # I2C libbsp_a_SOURCES += misc/i2c.c +# i2c +libbsp_a_SOURCES += i2c/bbb-i2c.c + # GPIO libbsp_a_SOURCES += gpio/bbb-gpio.c diff --git a/c/src/lib/libbsp/arm/beagle/i2c/bbb-i2c.c b/c/src/lib/libbsp/arm/beagle/i2c/bbb-i2c.c new file mode 100644 index 0000000..cd1e9c1 --- /dev/null +++ b/c/src/lib/libbsp/arm/beagle/i2c/bbb-i2c.c @@ -0,0 +1,788 @@ +/** + * @file + * + * @ingroup arm_beagle + * + * @brief BeagleBoard I2C bus initialization and API Support. + */ + +/* + * Copyright (c) 2016 Punit Vara <punitvara at gmail.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <stdio.h> +#include <bsp/i2c.h> +#include <libcpu/am335x.h> +#include <rtems/irq-extension.h> +#include <bsp/bbb-gpio.h> +#include <rtems/score/assert.h> + +/* +static bool am335x_i2c_pinmux(bbb_i2c_bus *bus) +{ + bool status =true; + // We will check i2c_bus_id in am335x_i2c_bus_register + // Apart from mode and pull_up register what about SCREWCTRL & RXACTIVE ?? + if (bus->i2c_bus_id == I2C1) { + REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_CS0) = (BBB_MUXMODE(MODE2) | BBB_PU_EN); + REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_D1) = (BBB_MUXMODE(MODE2) | BBB_PU_EN); + REG(AM335X_PADCONF_BASE + AM335X_CONF_UART1_TXD) = (BBB_MUXMODE(MODE3) | BBB_PU_EN); + REG(AM335X_PADCONF_BASE + AM335X_CONF_UART1_RXD) = (BBB_MUXMODE(MODE3) | BBB_PU_EN); + } else if (bus->i2c_bus_id == I2C2) { + REG(AM335X_PADCONF_BASE + AM335X_CONF_UART1_RTSN) = (BBB_MUXMODE(MODE3) | BBB_PU_EN); + REG(AM335X_PADCONF_BASE + AM335X_CONF_UART1_CTSN) = (BBB_MUXMODE(MODE3) | BBB_PU_EN); + REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_D0) = (BBB_MUXMODE(MODE3) | BBB_PU_EN); + REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_SCLK) = (BBB_MUXMODE(MODE3) | BBB_PU_EN); + } else { + status = false; + } + return status; +} +*/ + +/* ref. Table 21-4 I2C Clock Signals */ +/* + For I2C1/2 + Interface clock - 100MHz - CORE_LKOUTM4 / 2 - pd_per_l4ls_gclk + Functional clock - 48MHz - PER_CLKOUTM2 / 4 - pd_per_ic2_fclk +*/ + +/* +static void am335x_i2c1_i2c2_module_clk_config(bbb_i2c_bus *bus) +{ +*/ +/*0x2 = SW_WKUP : SW_WKUP: Start a software forced wake-up +transition on the domain. */ +/* + REG(AM335X_CM_PER_ADDR + AM335X_CM_PER_L4LS_CLKSTCTRL) |= + AM335X_CM_PER_L4LS_CLKSTCTRL_CLKTRCTRL_SW_WKUP; + while((REG(AM335X_CM_PER_ADDR + AM335X_CM_PER_L4LS_CLKSTCTRL) & + AM335X_CM_PER_L4LS_CLKSTCTRL_CLKTRCTRL) != + AM335X_CM_PER_L4LS_CLKSTCTRL_CLKTRCTRL_SW_WKUP); +*/ + +/* 0x2 = ENABLE : Module is explicitly enabled. Interface clock (if not +used for functions) may be gated according to the clock domain +state. Functional clocks are guarantied to stay present. As long as in +this configuration, power domain sleep transition cannot happen.*/ + /* REG(AM335X_CM_PER_ADDR + AM335X_CM_PER_L4LS_CLKCTRL) |= + AM335X_CM_PER_L4LS_CLKCTRL_MODULEMODE_ENABLE; + while((REG(AM335X_CM_PER_ADDR + AM335X_CM_PER_L4LS_CLKCTRL) & + AM335X_CM_PER_L4LS_CLKCTRL_MODULEMODE) != AM335X_CM_PER_L4LS_CLKCTRL_MODULEMODE_ENABLE); +*/ +/*0x2 = ENABLE : Module is explicitly enabled. Interface clock (if not +used for functions) may be gated according to the clock domain +state. Functional clocks are guarantied to stay present. As long as in +this configuration, power domain sleep transition cannot happen.*/ +/* + if (bus->i2c_bus_id == I2C1) { + REG(AM335X_CM_PER_ADDR + AM335X_CM_PER_I2C1_CLKCTRL) |= + AM335X_CM_PER_I2C1_CLKCTRL_MODULEMODE_ENABLE; + while(REG((AM335X_CM_PER_ADDR + AM335X_CM_PER_I2C1_CLKCTRL) & + AM335X_CM_PER_I2C1_CLKCTRL_MODULEMODE) != AM335X_CM_PER_I2C1_CLKCTRL_MODULEMODE_ENABLE); + } else if (bus->i2c_bus_id == I2C2) { + REG(AM335X_CM_PER_ADDR + AM335X_CM_PER_I2C2_CLKCTRL) |= + AM335X_CM_PER_I2C2_CLKCTRL_MODULEMODE_ENABLE; + while(REG((AM335X_CM_PER_ADDR + AM335X_CM_PER_I2C2_CLKCTRL) & + AM335X_CM_PER_I2C2_CLKCTRL_MODULEMODE) != AM335X_CM_PER_I2C2_CLKCTRL_MODULEMODE_ENABLE); + while(!(REG(AM335X_CM_PER_ADDR + AM335X_CM_PER_L4LS_CLKSTCTRL) & + (AM335X_CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_L4LS_GCLK | + AM335X_CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_I2C_FCLK))); + } +} +*/ + +static void am335x_i2c0_pinmux(bbb_i2c_bus *bus) +{ + REG(bus->regs + AM335X_CONF_I2C0_SDA) = + (BBB_RXACTIVE | BBB_SLEWCTRL | BBB_PU_EN); + + REG(bus->regs + AM335X_CONF_I2C0_SCL) = + (BBB_RXACTIVE | BBB_SLEWCTRL | BBB_PU_EN); +} + +static void I2C0ModuleClkConfig(void) +{ + /* Configuring L3 Interface Clocks. */ + + /* Writing to MODULEMODE field of CM_PER_L3_CLKCTRL register. */ + REG(AM335X_CM_PER_ADDR + CM_PER_L3_CLKCTRL) |= + CM_PER_L3_CLKCTRL_MODULEMODE_ENABLE; + + /* Waiting for MODULEMODE field to reflect the written value. */ + while(CM_PER_L3_CLKCTRL_MODULEMODE_ENABLE != + (REG(AM335X_CM_PER_ADDR + CM_PER_L3_CLKCTRL) & + CM_PER_L3_CLKCTRL_MODULEMODE)); + + /* Writing to MODULEMODE field of CM_PER_L3_INSTR_CLKCTRL register. */ + REG(AM335X_CM_PER_ADDR + CM_PER_L3_INSTR_CLKCTRL) |= + CM_PER_L3_INSTR_CLKCTRL_MODULEMODE_ENABLE; + + /* Waiting for MODULEMODE field to reflect the written value. */ + while(CM_PER_L3_INSTR_CLKCTRL_MODULEMODE_ENABLE != + (REG(AM335X_CM_PER_ADDR + CM_PER_L3_INSTR_CLKCTRL) & + CM_PER_L3_INSTR_CLKCTRL_MODULEMODE)); + + /* Writing to CLKTRCTRL field of CM_PER_L3_CLKSTCTRL register. */ + REG(AM335X_CM_PER_ADDR + CM_PER_L3_CLKSTCTRL) |= + CM_PER_L3_CLKSTCTRL_CLKTRCTRL_SW_WKUP; + + /* Waiting for CLKTRCTRL field to reflect the written value. */ + while(CM_PER_L3_CLKSTCTRL_CLKTRCTRL_SW_WKUP != + (REG(AM335X_CM_PER_ADDR + CM_PER_L3_CLKSTCTRL) & + CM_PER_L3_CLKSTCTRL_CLKTRCTRL)); + + /* Writing to CLKTRCTRL field of CM_PER_OCPWP_L3_CLKSTCTRL register. */ + REG(AM335X_CM_PER_ADDR + CM_PER_OCPWP_L3_CLKSTCTRL) |= + CM_PER_OCPWP_L3_CLKSTCTRL_CLKTRCTRL_SW_WKUP; + + /*Waiting for CLKTRCTRL field to reflect the written value. */ + while(CM_PER_OCPWP_L3_CLKSTCTRL_CLKTRCTRL_SW_WKUP != + (REG(AM335X_CM_PER_ADDR + CM_PER_OCPWP_L3_CLKSTCTRL) & + CM_PER_OCPWP_L3_CLKSTCTRL_CLKTRCTRL)); + + /* Writing to CLKTRCTRL field of CM_PER_L3S_CLKSTCTRL register. */ + REG(AM335X_CM_PER_ADDR + CM_PER_L3S_CLKSTCTRL) |= + CM_PER_L3S_CLKSTCTRL_CLKTRCTRL_SW_WKUP; + + /*Waiting for CLKTRCTRL field to reflect the written value. */ + while(CM_PER_L3S_CLKSTCTRL_CLKTRCTRL_SW_WKUP != + (REG(AM335X_CM_PER_ADDR + CM_PER_L3S_CLKSTCTRL) & + CM_PER_L3S_CLKSTCTRL_CLKTRCTRL)); + + /* Checking fields for necessary values. */ + + /* Waiting for IDLEST field in CM_PER_L3_CLKCTRL register to be set to 0x0. */ + while((CM_PER_L3_CLKCTRL_IDLEST_FUNC << CM_PER_L3_CLKCTRL_IDLEST_SHIFT)!= + (REG(AM335X_CM_PER_ADDR + CM_PER_L3_CLKCTRL) & + CM_PER_L3_CLKCTRL_IDLEST)); + + /* + ** Waiting for IDLEST field in CM_PER_L3_INSTR_CLKCTRL register to attain the + ** desired value. + */ + while((CM_PER_L3_INSTR_CLKCTRL_IDLEST_FUNC << + CM_PER_L3_INSTR_CLKCTRL_IDLEST_SHIFT)!= + (REG(AM335X_CM_PER_ADDR + CM_PER_L3_INSTR_CLKCTRL) & + CM_PER_L3_INSTR_CLKCTRL_IDLEST)); + + /* + ** Waiting for CLKACTIVITY_L3_GCLK field in CM_PER_L3_CLKSTCTRL register to + ** attain the desired value. + */ + while(CM_PER_L3_CLKSTCTRL_CLKACTIVITY_L3_GCLK != + (REG(AM335X_CM_PER_ADDR + CM_PER_L3_CLKSTCTRL) & + CM_PER_L3_CLKSTCTRL_CLKACTIVITY_L3_GCLK)); + + /* + ** Waiting for CLKACTIVITY_OCPWP_L3_GCLK field in CM_PER_OCPWP_L3_CLKSTCTRL + ** register to attain the desired value. + */ + while(CM_PER_OCPWP_L3_CLKSTCTRL_CLKACTIVITY_OCPWP_L3_GCLK != + (REG(AM335X_CM_PER_ADDR + CM_PER_OCPWP_L3_CLKSTCTRL) & + CM_PER_OCPWP_L3_CLKSTCTRL_CLKACTIVITY_OCPWP_L3_GCLK)); + + /* + ** Waiting for CLKACTIVITY_L3S_GCLK field in CM_PER_L3S_CLKSTCTRL register + ** to attain the desired value. + */ + while(CM_PER_L3S_CLKSTCTRL_CLKACTIVITY_L3S_GCLK != + (REG(AM335X_CM_PER_ADDR + CM_PER_L3S_CLKSTCTRL) & + CM_PER_L3S_CLKSTCTRL_CLKACTIVITY_L3S_GCLK)); + + + /* Configuring registers related to Wake-Up region. */ + + /* Writing to MODULEMODE field of CM_WKUP_CONTROL_CLKCTRL register. */ + REG(SOC_CM_WKUP_REGS + CM_WKUP_CONTROL_CLKCTRL) |= + CM_WKUP_CONTROL_CLKCTRL_MODULEMODE_ENABLE; + + /* Waiting for MODULEMODE field to reflect the written value. */ + while(CM_WKUP_CONTROL_CLKCTRL_MODULEMODE_ENABLE != + (REG(SOC_CM_WKUP_REGS + CM_WKUP_CONTROL_CLKCTRL) & + CM_WKUP_CONTROL_CLKCTRL_MODULEMODE)); + + /* Writing to CLKTRCTRL field of CM_PER_L3S_CLKSTCTRL register. */ + REG(SOC_CM_WKUP_REGS + CM_WKUP_CLKSTCTRL) |= + CM_WKUP_CLKSTCTRL_CLKTRCTRL_SW_WKUP; + + /*Waiting for CLKTRCTRL field to reflect the written value. */ + while(CM_WKUP_CLKSTCTRL_CLKTRCTRL_SW_WKUP != + (REG(SOC_CM_WKUP_REGS + CM_WKUP_CLKSTCTRL) & + CM_WKUP_CLKSTCTRL_CLKTRCTRL)); + + /* Writing to CLKTRCTRL field of CM_L3_AON_CLKSTCTRL register. */ + REG(SOC_CM_WKUP_REGS + CM_WKUP_CM_L3_AON_CLKSTCTRL) |= + CM_WKUP_CM_L3_AON_CLKSTCTRL_CLKTRCTRL_SW_WKUP; + + /*Waiting for CLKTRCTRL field to reflect the written value. */ + while(CM_WKUP_CM_L3_AON_CLKSTCTRL_CLKTRCTRL_SW_WKUP != + (REG(SOC_CM_WKUP_REGS + CM_WKUP_CM_L3_AON_CLKSTCTRL) & + CM_WKUP_CM_L3_AON_CLKSTCTRL_CLKTRCTRL)); + + /* Writing to MODULEMODE field of CM_WKUP_I2C0_CLKCTRL register. */ + REG(SOC_CM_WKUP_REGS + CM_WKUP_I2C0_CLKCTRL) |= + CM_WKUP_I2C0_CLKCTRL_MODULEMODE_ENABLE; + + /* Waiting for MODULEMODE field to reflect the written value. */ + while(CM_WKUP_I2C0_CLKCTRL_MODULEMODE_ENABLE != + (REG(SOC_CM_WKUP_REGS + CM_WKUP_I2C0_CLKCTRL) & + CM_WKUP_I2C0_CLKCTRL_MODULEMODE)); + + /* Verifying if the other bits are set to required settings. */ + + /* + ** Waiting for IDLEST field in CM_WKUP_CONTROL_CLKCTRL register to attain + ** desired value. + */ + while((CM_WKUP_CONTROL_CLKCTRL_IDLEST_FUNC << + CM_WKUP_CONTROL_CLKCTRL_IDLEST_SHIFT) != + (REG(SOC_CM_WKUP_REGS + CM_WKUP_CONTROL_CLKCTRL) & + CM_WKUP_CONTROL_CLKCTRL_IDLEST)); + + /* + ** Waiting for CLKACTIVITY_L3_AON_GCLK field in CM_L3_AON_CLKSTCTRL + ** register to attain desired value. + */ + while(CM_WKUP_CM_L3_AON_CLKSTCTRL_CLKACTIVITY_L3_AON_GCLK != + (REG(SOC_CM_WKUP_REGS + CM_WKUP_CM_L3_AON_CLKSTCTRL) & + CM_WKUP_CM_L3_AON_CLKSTCTRL_CLKACTIVITY_L3_AON_GCLK)); + + /* + ** Waiting for IDLEST field in CM_WKUP_L4WKUP_CLKCTRL register to attain + ** desired value. + */ + while((CM_WKUP_L4WKUP_CLKCTRL_IDLEST_FUNC << + CM_WKUP_L4WKUP_CLKCTRL_IDLEST_SHIFT) != + (REG(SOC_CM_WKUP_REGS + CM_WKUP_L4WKUP_CLKCTRL) & + CM_WKUP_L4WKUP_CLKCTRL_IDLEST)); + + /* + ** Waiting for CLKACTIVITY_L4_WKUP_GCLK field in CM_WKUP_CLKSTCTRL register + ** to attain desired value. + */ + while(CM_WKUP_CLKSTCTRL_CLKACTIVITY_L4_WKUP_GCLK != + (REG(SOC_CM_WKUP_REGS + CM_WKUP_CLKSTCTRL) & + CM_WKUP_CLKSTCTRL_CLKACTIVITY_L4_WKUP_GCLK)); + + /* + ** Waiting for CLKACTIVITY_L4_WKUP_AON_GCLK field in CM_L4_WKUP_AON_CLKSTCTRL + ** register to attain desired value. + */ + while(CM_WKUP_CM_L4_WKUP_AON_CLKSTCTRL_CLKACTIVITY_L4_WKUP_AON_GCLK != + (REG(SOC_CM_WKUP_REGS + CM_WKUP_CM_L4_WKUP_AON_CLKSTCTRL) & + CM_WKUP_CM_L4_WKUP_AON_CLKSTCTRL_CLKACTIVITY_L4_WKUP_AON_GCLK)); + + /* + ** Waiting for CLKACTIVITY_I2C0_GFCLK field in CM_WKUP_CLKSTCTRL + ** register to attain desired value. + */ + while(CM_WKUP_CLKSTCTRL_CLKACTIVITY_I2C0_GFCLK != + (REG(SOC_CM_WKUP_REGS + CM_WKUP_CLKSTCTRL) & + CM_WKUP_CLKSTCTRL_CLKACTIVITY_I2C0_GFCLK)); + + /* + ** Waiting for IDLEST field in CM_WKUP_I2C0_CLKCTRL register to attain + ** desired value. + */ + while((CM_WKUP_I2C0_CLKCTRL_IDLEST_FUNC << + CM_WKUP_I2C0_CLKCTRL_IDLEST_SHIFT) != + (REG(SOC_CM_WKUP_REGS + CM_WKUP_I2C0_CLKCTRL) & + CM_WKUP_I2C0_CLKCTRL_IDLEST)); +} + +/* +void am335x_i2c_init(bbb_i2c_bus *bus, uint32_t input_clock) +{ + // am335x_i2c_pinmux() + // am335x_i2c1_i2c2_module_clk_config +} +*/ + +static bool am335x_i2c_busbusy(volatile bbb_i2c_regs *regs) +{ + bool status; + + if (REG(®s->BBB_I2C_IRQSTATUS_RAW) & AM335X_I2C_IRQSTATUS_RAW_BB) + { + status = true; + } else { + status = false; + } + return status; +} + +static void am335x_i2c_reset(bbb_i2c_bus *bus) +{ + volatile bbb_i2c_regs *regs = bus->regs; + printk("reset bus->reg is %x \n",bus->regs); + /* Disable I2C module at the time of initialization*/ + /*Should I use write32 ?? I guess mmio_clear is correct choice here*/ + printk("inside BBB_I2C_CON value is %x \n",®s->BBB_I2C_CON); + mmio_clear((®s->BBB_I2C_CON),AM335X_I2C_CON_I2C_EN); + mmio_clear((®s->BBB_I2C_SYSC),AM335X_I2C_SYSC_AUTOIDLE); + //REG(bus->regs + AM335X_I2C_CON) &= ~(AM335X_I2C_CON_I2C_EN); + //REG(bus->regs + AM335X_I2C_SYSC) &= ~(AM335X_I2C_SYSC_AUTOIDLE); + + /* + can I clear all the interrupt here ? + mmio_write(get_reg_addr(bbb_i2c_bus->reg->AM335X_I2C_IRQ_ENABLE_CLR), ??) + */ +} + +/* +Possible values for msg->flag + * - @ref I2C_M_TEN, + * - @ref I2C_M_RD, + * - @ref I2C_M_STOP, + * - @ref I2C_M_NOSTART, + * - @ref I2C_M_REV_DIR_ADDR, + * - @ref I2C_M_IGNORE_NAK, + * - @ref I2C_M_NO_RD_ACK, and + * - @ref I2C_M_RECV_LEN. +*/ + +static void am335x_i2c_set_address_size(const i2c_msg *msgs,volatile bbb_i2c_regs *regs) +{ + /*can be configured multiple modes here. Need to think about own address modes*/ + if ((msgs->flags & I2C_M_TEN) == 0) {/* 7-bit mode slave address mode*/ + mmio_write(®s->BBB_I2C_CON,(AM335X_I2C_CFG_7BIT_SLAVE_ADDR | AM335X_I2C_CON_I2C_EN)); + } else { /* 10-bit slave address mode*/ + mmio_write(®s->BBB_I2C_CON,(AM335X_I2C_CFG_10BIT_SLAVE_ADDR | AM335X_I2C_CON_I2C_EN)); + } + } + +static void am335x_i2c_next_byte(bbb_i2c_bus *bus) +{ + i2c_msg *msg; + + printk("Enter next_byte\n"); + ++bus->msgs; + --bus->msg_todo; + + msg = &bus->msgs[0]; + + bus->current_msg_todo = msg->len; + bus->current_msg_byte = msg->buf; +} + +static unsigned int am335x_i2c_intrawstatus(volatile bbb_i2c_regs *regs) +{ + return (REG(®s->BBB_I2C_IRQSTATUS_RAW)); +} + +static void am335x_i2c_masterint_enable(volatile bbb_i2c_regs *regs, unsigned int flag) +{ + REG(®s->BBB_I2C_IRQENABLE_SET) |= flag; +} + +static void am335x_i2c_masterint_disable(volatile bbb_i2c_regs *regs, unsigned int flag) +{ + REG(®s->BBB_I2C_IRQENABLE_CLR) = flag; +} + +static void am335x_int_clear(volatile bbb_i2c_regs *regs, unsigned int flag) +{ + REG(®s->BBB_I2C_IRQSTATUS) = flag; +} + + +static void am335x_clean_interrupts(volatile bbb_i2c_regs *regs) +{ + am335x_i2c_masterint_enable(regs,0x7FFF); + am335x_int_clear(regs,0x7FFF); + am335x_i2c_masterint_disable(regs,0x7FFF); +} + + +static void am335x_i2c_setup_read_transfer(bbb_i2c_bus *bus, volatile bbb_i2c_regs *regs, const i2c_msg *msgs, bool send_stop) +{ + volatile unsigned int no_bytes; + //am335x_i2c_masterint_enable(regs, AM335X_I2C_INT_RECV_READY); + // No of data to be transmitted at a time + REG(®s->BBB_I2C_CNT) = 0x02; + no_bytes = REG(®s->BBB_I2C_CNT); + + // I2C Controller in Master Mode + REG(®s->BBB_I2C_CON) = AM335X_I2C_CFG_MST_TX | AM335X_I2C_CON_I2C_EN | AM335X_I2C_CON_START | AM335X_I2C_CON_MST; + printk("set master in transmission mode %x \n",REG(®s->BBB_I2C_CON)); + + // Set Slave address & Master enable, bring out of reset + REG(®s->BBB_I2C_SA) = msgs->addr; + printf("slave address : %x\n",REG(®s->BBB_I2C_SA)); + + // clear status of all interrupts + am335x_clean_interrupts(regs); + printk("\n set memory address to read\n"); + + // transmit interrupt is enabled + am335x_i2c_masterint_enable(regs,AM335X_I2C_IRQSTATUS_XRDY); + printk("Enable transmit interrupt \n"); + //start condition + REG(®s->BBB_I2C_CON) |= AM335X_I2C_CON_START; + printk("start transmission \n"); + while(am335x_i2c_busbusy(regs) == 0); + printk("bus is free \n"); + printk("CNT : %x\n", no_bytes); + while(0 != no_bytes); + printk("total msg count for tranmission is zero \n"); + while( !(am335x_i2c_intrawstatus(regs) & (AM335X_I2C_IRQSTATUS_ARDY))); + + printk("Enter read transfer \n"); + // No of data to be received at a time(msg_count!!) + printk("msg_todo for read is %d \n",bus->msg_todo); + REG(®s->BBB_I2C_CNT) = bus->msg_todo; + + // clear status of all interrupts + //am335x_clean_interrupts(regs); + + // I2C Controller in Master Mode + REG(®s->BBB_I2C_CON) = AM335X_I2C_CFG_MST_RX | AM335X_I2C_CON_I2C_EN | AM335X_I2C_CON_MST; + printk("Set master to receiver mode %x \n", REG(®s->BBB_I2C_CON)); + // receive interrupt is enabled + am335x_i2c_masterint_enable(regs, AM335X_I2C_INT_RECV_READY | AM335X_I2C_INT_STOP_CONDITION); + + if (send_stop) { + // stop condition + printk("stop to read\n"); + REG(®s->BBB_I2C_CON) |= AM335X_I2C_CON_STOP; + } else { + // start condition + printk("start to read\n"); + REG(®s->BBB_I2C_CON) |= AM335X_I2C_CON_START; + } + while(am335x_i2c_busbusy(regs) == 0); + printk("Exit read transfer\n"); +} + + +static void am335x_i2c_continue_read_transfer( + bbb_i2c_bus *bus, + volatile bbb_i2c_regs *regs +) +{ + printk("enter continue read transfer \n"); + bus->current_msg_byte[bus->already_transferred] = REG(®s->BBB_I2C_DATA); + bus->already_transferred++; + am335x_int_clear(regs,AM335X_I2C_INT_RECV_READY); + printk("clear RRDY in continue read transfer\n"); + + if (bus->already_transferred == REG(®s->BBB_I2C_CNT)) { + printk("continue read transfer finished \n"); + //am335x_i2c_setup_read_transfer(bus,regs,false); + am335x_i2c_masterint_disable(regs, AM335X_I2C_INT_RECV_READY); + printk("disable RRDY in continue read transfer\n"); + REG(®s->BBB_I2C_CON) |= AM335X_I2C_CON_STOP; + printk("stop condition in continue read transfer %x\n",REG(®s->BBB_I2C_CON)); + } +} + +static void am335x_i2c_continue_write(bbb_i2c_bus *bus, volatile bbb_i2c_regs *regs) +{ + REG(®s->BBB_I2C_DATA) = 0x00; + am335x_int_clear(regs, AM335X_I2C_IRQSTATUS_XRDY); + printk("clear XRDY continue write\n"); + /* + if (bus->already_transferred == REG(®s->BBB_I2C_CNT)) { + printk("\n finished transfer \n"); + am335x_i2c_masterint_disable(regs, AM335X_I2C_IRQSTATUS_XRDY); + printk("disable XRDY continue write \n"); + REG(®s->BBB_I2C_CON) |= AM335X_I2C_CON_STOP; + } else { + printk("write memory address \n"); + REG(®s->BBB_I2C_DATA) = *bus->current_msg_byte; + } + */ + + /* + if (bus->already_transferred == bus->msg_todo) { + printk("finished transfer \n"); + am335x_int_clear(regs, AM335X_I2C_IRQSTATUS_XRDY); + REG(®s->BBB_I2C_CON) |= AM335X_I2C_CON_STOP; + } else { + printk("remaining byte \n"); + REG(®s->BBB_I2C_DATA) = bus->current_msg_byte[bus->already_transferred]; + printk("%s",REG(®s->BBB_I2C_DATA)); + bus->already_transferred++; + } + */ +} + +static void am335x_i2c_setup_write_transfer(bbb_i2c_bus *bus,volatile bbb_i2c_regs *regs) +{ + volatile unsigned int no_bytes; + printk(" \n Enter write transfer \n"); + + // Following data count specify bytes to be transmitted + REG(®s->BBB_I2C_CNT) = bus->msg_todo; + no_bytes = REG(®s->BBB_I2C_CNT); + // clear status of all interrupts + // Already cleaned during reset + am335x_clean_interrupts(regs); + + // I2C Controller in Master transmitter Mode + REG(®s->BBB_I2C_CON) = AM335X_I2C_CFG_MST_TX | AM335X_I2C_CON_I2C_EN; + printk("enable master in transmiter mode setup write %x\n",REG(®s->BBB_I2C_CON)); + + // transmit interrupt is enabled + am335x_i2c_masterint_enable(regs,AM335X_I2C_IRQSTATUS_XRDY); + printk("enable XRDY setup write\n"); + + //start condition + REG(®s->BBB_I2C_CON) |= AM335X_I2C_CON_START; + printk("set start condition in setup write %x \n",REG(®s->BBB_I2C_CON)); + + while(am335x_i2c_busbusy(regs) == 0); + printk("CNT in setup write : %x \n",REG(®s->BBB_I2C_CNT)); + printk("setup write msg_todo %x \n",bus->current_todo); + while(0 != no_bytes); + printk("check whether ???\n"); + printk("RAW = %x",REG(®s->BBB_I2C_IRQSTATUS_RAW)); + while( !((am335x_i2c_intrawstatus(regs)) & (AM335X_I2C_IRQSTATUS_ARDY))); + printk("exit setup write \n"); +} + + +static void am335x_i2c_setup_transfer(bbb_i2c_bus *bus, volatile bbb_i2c_regs *regs) +{ + const i2c_msg *msgs = bus->msgs; + uint32_t msg_todo = bus->msg_todo; + bool send_stop = false; + uint32_t i; + + printk("Enter setup transfer\n"); + bus->current_todo = msgs[0].len; + + for (i = 1; i < msg_todo && (msgs[i].flags & I2C_M_NOSTART) != 0; ++i) { + bus->current_todo += msgs[i].len; + } + + regs = bus->regs; + + REG(&bus->regs->BBB_I2C_BUF) |= AM335X_I2C_BUF_TXFIFO_CLR; + REG(&bus->regs->BBB_I2C_BUF) |= AM335X_I2C_BUF_RXFIFO_CLR; + am335x_i2c_set_address_size(msgs,regs); + bus->read = ((bus->read == true) ? 0:1); + bus->already_transferred = (bus->read == true) ? 0 : 1; + + if (bus->read) { + if (REG(®s->BBB_I2C_CNT) == 1) { + send_stop = true; + } + printk("configure to read bus\n"); + am335x_i2c_setup_read_transfer(bus,regs,msgs,send_stop); + } else { + printk("configure to write bus\n"); + am335x_i2c_setup_write_transfer(bus,regs); + } + +} + +static void am335x_i2c_interrupt(void *arg) +{ + bbb_i2c_bus *bus = arg; + volatile bbb_i2c_regs *regs = bus->regs; + /* get status of enabled interrupts */ + uint32_t irqstatus = REG(®s->BBB_I2C_IRQSTATUS); + bool done = false; + printk("\n inside interrupt function \n"); + /* Clear all enabled interrupt except receive ready and transmit ready interrupt in status register */ + REG(®s->BBB_I2C_IRQSTATUS) = (irqstatus & ~(AM335X_I2C_IRQSTATUS_RRDY | AM335X_I2C_IRQSTATUS_XRDY)); + printk("\n irqstatus = %x \n",REG(®s->BBB_I2C_IRQSTATUS)); + + if (irqstatus & AM335X_I2C_INT_RECV_READY) { + printk("\nInside receive interrupt\n"); + am335x_i2c_continue_read_transfer(bus, regs); + } + + if (irqstatus & AM335X_I2C_IRQSTATUS_XRDY) { + printk("\ninside transmit interrupt \n"); + am335x_i2c_continue_write(bus,regs); + } + + if (irqstatus & AM335X_I2C_IRQSTATUS_NACK) { + done = true; + printk("inside NACK\n"); + am335x_i2c_masterint_disable(regs,AM335X_I2C_IRQSTATUS_NACK); + } + + if (irqstatus & AM335X_I2C_IRQSTATUS_BF) { + done = true; + printk("inside BF \n "); + } + + if (done) { + uint32_t err = irqstatus & BBB_I2C_IRQ_ERROR; + printk("interrupt done \n"); + + am335x_i2c_next_byte(bus); + + if (bus->msg_todo == 0 || err != 0) { + rtems_status_code sc; + + // am335x_i2c_disable_interrupts(regs); + am335x_i2c_masterint_disable(regs, (AM335X_I2C_IRQSTATUS_RRDY | AM335X_I2C_IRQSTATUS_XRDY | AM335X_I2C_IRQSTATUS_BF)); + + REG(®s->BBB_I2C_IRQSTATUS) = err; + + sc = rtems_event_transient_send(bus->task_id); + _Assert(sc == RTEMS_SUCCESSFUL); + (void) sc; + } else { + am335x_i2c_setup_transfer(bus, regs); + } + } +} + +static int am335x_i2c_transfer(i2c_bus *base, i2c_msg *msgs, uint32_t msg_count) +{ + rtems_status_code sc; + bbb_i2c_bus *bus = (bbb_i2c_bus *)base; + volatile bbb_i2c_regs *regs; + uint32_t i; + printk("\n enter transfer "); + rtems_task_wake_after(1); + + + if (msg_count < 1){ + return 1; + } + + for (i=0; i<msg_count;++i) { + if ((msgs[i].flags & I2C_M_RECV_LEN) != 0) { + return -EINVAL; + } + } + + bus->msgs = &msgs[0]; + bus->msg_todo = msg_count; + printk("total msg = msg_count : %x \n",bus->msg_todo); + bus->current_msg_todo = msgs[0].len;// current data size + bus->current_msg_byte = msgs[0].buf;// current data + printk("\n current_msg_todo %x \n ",msgs[0].len); + printk("\n current_msg_byte %x \n ",msgs[0].buf); + bus->task_id = rtems_task_self(); + + regs = bus->regs; + am335x_i2c_setup_transfer(bus,regs); + REG(®s->BBB_I2C_IRQENABLE_SET) = BBB_I2C_IRQ_USED; + + sc = rtems_event_transient_receive(RTEMS_WAIT, bus->base.timeout); + // If timeout then return timeout error + if (sc != RTEMS_SUCCESSFUL) { + am335x_i2c_reset(bus); + + rtems_event_transient_clear(); + + return -ETIMEDOUT; + } + printk("exit transfer\n"); + // return bus->regs->BBB_I2C_IRQSTATUS == 0 ? 0 : -EIO; + return 0; +} + +static int am335x_i2c_set_clock(i2c_bus *base, unsigned long clock) +{ + bbb_i2c_bus *bus = (bbb_i2c_bus *) base; + volatile bbb_i2c_regs *regs = bus->regs; + uint32_t prescaler,divider; + + printk("set clock start\n"); + prescaler = (BBB_I2C_SYSCLK / BBB_I2C_INTERNAL_CLK) -1; + printk("PSC offset %x \n ",®s->BBB_I2C_PSC); + printk("PSC offset %x \n", &bus->regs->BBB_I2C_PSC); + //mmio_write((®s->BBB_I2C_PSC), prescaler); + REG(&bus->regs->BBB_I2C_PSC) = prescaler; + + divider = BBB_I2C_INTERNAL_CLK/(2*clock); + printk("SCLL offset %x \n",&bus->regs->BBB_I2C_SCLL); + //mmio_write((®s->BBB_I2C_SCLL), (divider - 7)); + REG(&bus->regs->BBB_I2C_SCLL) = (divider - 7); + //mmio_write((®s->BBB_I2C_SCLH), (divider - 5)); + printk("SCHL offset %x\n",&bus->regs->BBB_I2C_SCLH); + REG(&bus->regs->BBB_I2C_SCLH) = (divider - 5); + printk("set clock end \n"); + return 0; +} + +static void am335x_i2c_destroy(i2c_bus *base) +{ + bbb_i2c_bus *bus = (bbb_i2c_bus *) base; + rtems_status_code sc; + printk(" starting destroy\n"); + sc = rtems_interrupt_handler_remove(bus->irq, am335x_i2c_interrupt, bus); + _Assert(sc == RTEMS_SUCCESSFUL); + (void)sc; + printk("end destroy\n"); + i2c_bus_destroy_and_free(&bus->base); +} + +int am335x_i2c_bus_register( + const char *bus_path, + uintptr_t register_base, + uint32_t input_clock, + rtems_vector_number irq +) +{ + + bbb_i2c_bus *bus; + rtems_status_code sc; + int err; + /*check bus number is >0 & <MAX*/ + + bus = (bbb_i2c_bus *) i2c_bus_alloc_and_init(sizeof(*bus)); + + if (bus == NULL) { + return -1; + } + + bus->regs = (volatile bbb_i2c_regs *) register_base; + +// 1. Enable clock for I2CX + I2C0ModuleClkConfig(); +// 2. pinmux setup + am335x_i2c0_pinmux(bus); +// 3. RESET : Disable Master, autoideal + am335x_i2c_reset(bus); +// 4. configure bus speed + bus->input_clock = input_clock; // By default 100KHz. Normally pass 100KHz as argument + + printk("Before set clock \n"); + err = am335x_i2c_set_clock(&bus->base, I2C_BUS_CLOCK_DEFAULT); + + if (err != 0) { + (*bus->base.destroy)(&bus->base); + + rtems_set_errno_and_return_minus_one(-err); + } + bus->irq = irq; + + //bring I2C out of reset + + REG(&bus->regs->BBB_I2C_CON) |= AM335X_I2C_CON_I2C_EN; + + // 5. Start interrupt service routine & one interrupt at a time + sc = rtems_interrupt_handler_install( + irq, + "BBB I2C", + RTEMS_INTERRUPT_UNIQUE, + am335x_i2c_interrupt, + bus + ); + + if (sc != RTEMS_SUCCESSFUL) { + (*bus->base.destroy)(&bus->base); + + rtems_set_errno_and_return_minus_one(EIO); + } + // 6. start transfer for reading and writing + bus->base.transfer = am335x_i2c_transfer; + bus->base.set_clock = am335x_i2c_set_clock; + bus->base.destroy = am335x_i2c_destroy; + printk("exit register\n"); + return i2c_bus_register(&bus->base,bus_path); +} diff --git a/c/src/lib/libbsp/arm/beagle/include/i2c.h b/c/src/lib/libbsp/arm/beagle/include/i2c.h index e7d1716..d4a9e32 100644 --- a/c/src/lib/libbsp/arm/beagle/include/i2c.h +++ b/c/src/lib/libbsp/arm/beagle/include/i2c.h @@ -1,5 +1,5 @@ /** - * @file + * * @ingroup arm_beagle * @@ -24,7 +24,7 @@ #define LIBBSP_ARM_BEAGLE_I2C_H #include <rtems.h> - +#include <dev/i2c/i2c.h> #include <bsp.h> #ifdef __cplusplus @@ -180,20 +180,8 @@ struct i2c { unsigned short res15; }; -static unsigned short wait_for_pin( void ); - -static void wait_for_bb( void ); - -static void flush_fifo( void ); - void i2c_init( int speed, int slaveadd ); -static int i2c_read_byte( - unsigned char devaddr, - unsigned char regoffset, - unsigned char *value -); - int i2c_write( unsigned char chip, unsigned int addr, @@ -210,10 +198,6 @@ int i2c_read( int len ); -static int imw ( unsigned char chip, unsigned long addr, unsigned char byte ); - -static int imd( unsigned char chip, unsigned int addr, unsigned int length ); - /** * @brief Initializes the I2C module @a i2c. * @@ -361,6 +345,139 @@ static inline rtems_status_code beagle_i2c_read( return beagle_i2c_write_and_read(i2c, addr, NULL, 0, in, in_size); } +#define BBB_I2C_SYSCLK 48000000 +#define BBB_I2C_INTERNAL_CLK 12000000 +#define BBB_I2C_SPEED_CLK 100000 + +#define BBB_I2C_IRQ_ERROR \ + (AM335X_I2C_IRQSTATUS_NACK \ + | AM335X_I2C_IRQSTATUS_ROVR \ + | AM335X_I2C_IRQSTATUS_AL \ + | AM335X_I2C_IRQSTATUS_ARDY \ + | AM335X_I2C_IRQSTATUS_RRDY \ + | AM335X_I2C_IRQSTATUS_XRDY \ + | AM335X_I2C_IRQSTATUS_XUDF) + +#define BBB_I2C_IRQ_USED \ + ( BBB_I2C_IRQ_ERROR \ + | AM335X_I2C_IRQSTATUS_AAS \ + | AM335X_I2C_IRQSTATUS_BF \ + | AM335X_I2C_IRQSTATUS_STC \ + | AM335X_I2C_IRQSTATUS_GC \ + | AM335X_I2C_IRQSTATUS_XDR \ + | AM335X_I2C_IRQSTATUS_RDR) + +#define BBB_I2C_0_BUS_PATH "/dev/i2c-0" +#define BBB_I2C_1_BUS_PATH "/dev/i2c-1" +#define BBB_I2C_2_BUS_PATH "/dev/i2c-2" + +#define BBB_I2C0_IRQ 70 +#define BBB_I2C1_IRQ 71 +#define BBB_I2C2_IRQ 30 + +#define MODE2 2 +#define MODE3 3 + +typedef enum { + I2C0, + I2C1, + I2C2, + I2C_COUNT +}bbb_i2c_id_t; + +typedef struct i2c_regs +{ + uint32_t BBB_I2C_REVNB_LO; // 0h + uint32_t BBB_I2C_REVNB_HI; //4h + uint32_t dummy1[2]; + uint32_t BBB_I2C_SYSC; // 10h =16 + uint32_t dummy2[4]; + uint32_t BBB_I2C_IRQSTATUS_RAW; //24h =36 + uint32_t BBB_I2C_IRQSTATUS; //28h =40 + uint32_t BBB_I2C_IRQENABLE_SET; //2Ch =44 + uint32_t BBB_I2C_IRQENABLE_CLR; //30h =48 + uint32_t BBB_I2C_WE; // 34h = 52 + uint32_t BBB_I2C_DMARXENABLE_SET; //38h = 56 + uint32_t BBB_I2C_DMATXENABLE_SET; //3Ch = 60 + uint32_t BBB_I2C_DMARXENABLE_CLR; //40h = 64 + uint32_t BBB_I2C_DMATXENABLE_CLR; //44h = 68 + uint32_t BBB_I2C_DMARXWAKE_EN; //48h = 72 + uint32_t BBB_I2C_DMATXWAKE_EN; //4Ch =76 + uint32_t dummy3[16]; + uint32_t BBB_I2C_SYSS; // 90h =144 + uint32_t BBB_I2C_BUF; // 94h =148 + uint32_t BBB_I2C_CNT; // 98h =152 + uint32_t BBB_I2C_DATA; //9Ch =156 + uint32_t dummy4; + uint32_t BBB_I2C_CON; // A4h = 164 + uint32_t BBB_I2C_OA; //A8h = 168 + uint32_t BBB_I2C_SA; //ACh = 172 + uint32_t BBB_I2C_PSC; //B0h = 176 + uint32_t BBB_I2C_SCLL; //B4h = 180 + uint32_t BBB_I2C_SCLH; //B8h = 184 + uint32_t BBB_I2C_SYSTEST; //BCh = 188 + uint32_t BBB_I2C_BUFSTAT; //C0h 192 + uint32_t BBB_I2C_OA1; //C4h 196 + uint32_t BBB_I2C_OA2; //C8h 200 + uint32_t BBB_I2C_OA3; //CCh 204 + uint32_t BBB_I2C_ACTOA; //D0h 208 + uint32_t BBB_I2C_SBLOCK; //D4h 212 +}bbb_i2c_regs; + +typedef struct bbb_i2c_bus{ + i2c_bus base; + volatile bbb_i2c_regs *regs; + i2c_msg *msgs; + uint32_t msg_todo; + uint32_t current_msg_todo; // current_data_size + uint8_t *current_msg_byte; // current_data + uint32_t current_todo; + bool read; + bool hold; + rtems_id task_id; + rtems_vector_number irq; + uint32_t input_clock; + uint32_t already_transferred; +} bbb_i2c_bus; + +int am335x_i2c_bus_register( + const char *bus_path, + uintptr_t register_base, + uint32_t input_clock, + rtems_vector_number irq +); + +static inline int bbb_register_i2c_0(void) +{ + return am335x_i2c_bus_register( + BBB_I2C_0_BUS_PATH, + AM335X_I2C0_BASE, + I2C_BUS_CLOCK_DEFAULT, + BBB_I2C0_IRQ + ); +} + +static inline int bbb_register_i2c_1(void) +{ + return am335x_i2c_bus_register( + BBB_I2C_1_BUS_PATH, + AM335X_I2C1_BASE, + I2C_BUS_CLOCK_DEFAULT, + BBB_I2C1_IRQ + ); +} + +static inline int bbb_register_i2c_2(void) +{ + return am335x_i2c_bus_register( + BBB_I2C_2_BUS_PATH, + AM335X_I2C2_BASE, + I2C_BUS_CLOCK_DEFAULT, + BBB_I2C2_IRQ + ); +} + + #ifdef __cplusplus } #endif /* __cplusplus */ -- 2.7.4 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel