On Tue, Jun 25, 2019 at 1:47 AM <l...@c-mauderer.de> wrote: > From: Christian Mauderer <christian.maude...@embedded-brains.de> > > The old driver worked well for EEPROMS with the RTEMS EEPROM driver. But > it had problems with a lot of other situations. Although it's not a > direct port, the new driver is heavily modeled after the FreeBSD ti_i2c > driver. > This is great! I have tried this patch with the FreeBSD ported IIC bus through the rtems_i2c adaptation layer is FreeBSD and the output MATCHES the original FreeBSD i2c scan with the ti_i2c driver. Would also like to add that the i2c probe and i2c md in u-boot also shows the same results. ``` SHLL [/] # i2c -a 0x50 -w16 -mtr -c10 aa 55 33 ee 41 33 33 35 42 4e SHLL [/] # i2c -s Scanning I2C devices on /dev/iic0: Hardware may not support START/STOP scanning; trying less-reliable read method. 24 34 50 ```
> --- > bsps/arm/beagle/i2c/bbb-i2c.c | 646 ++++++++++++++++-------------- > bsps/arm/beagle/include/bsp/i2c.h | 84 +--- > bsps/arm/include/libcpu/am335x.h | 35 +- > 3 files changed, 370 insertions(+), 395 deletions(-) > > diff --git a/bsps/arm/beagle/i2c/bbb-i2c.c b/bsps/arm/beagle/i2c/bbb-i2c.c > index 3a8637d457..37b88864b9 100644 > --- a/bsps/arm/beagle/i2c/bbb-i2c.c > +++ b/bsps/arm/beagle/i2c/bbb-i2c.c > @@ -9,17 +9,14 @@ > /* > * Copyright (c) 2016 Punit Vara <punitv...@gmail.com> > * Copyright (c) 2017 Sichen Zhao <zsc19940...@gmail.com> > + * Copyright (c) 2019 Christian Mauderer < > christian.maude...@embedded-brains.de> > * > * 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. > */ > > -/* > - * Modified on Punit Vara<punitv...@gmail.com> works, currently > - * the i2c file is working on the Beaglebone Black board(AM335x) > - */ > - > +#include <rtems/bspIo.h> > #include <stdio.h> > #include <bsp/i2c.h> > #include <libcpu/am335x.h> > @@ -27,26 +24,114 @@ > #include <rtems/counter.h> > #include <bsp/bbb-gpio.h> > #include <rtems/score/assert.h> > +#include <dev/i2c/i2c.h> > > -static void am335x_i2c0_pinmux( bbb_i2c_bus *bus ) > +typedef struct bbb_i2c_bus { > + i2c_bus base; > + volatile bbb_i2c_regs *regs; > + struct { > + volatile uint32_t *ctrl_clkctrl; > + volatile uint32_t *i2c_clkctrl; > + volatile uint32_t *clkstctrl; > + } clkregs; > + struct { > + volatile uint32_t *conf_sda; > + uint32_t mmode_sda; > + volatile uint32_t *conf_scl; > + uint32_t mmode_scl; > + } pinregs; > + rtems_id task_id; > + rtems_vector_number irq; > + i2c_msg *buffer; > + size_t buffer_pos; > + int error; > + uint32_t con_reg; > +} bbb_i2c_bus; > + > +#define TRANSFER_TIMEOUT_COUNT 100 > +#define FIFO_THRESHOLD 5 > +#define min(l,r) ((l) < (r) ? (l) : (r)) > +#if 0 > +#define debug_print(fmt, args...) printk("bbb-i2c: " fmt, ## args) > +#else > +#define debug_print(fmt, args...) > +#endif > + > +static int am335x_i2c_fill_registers( > + bbb_i2c_bus *bus, > + uintptr_t register_base > +) > { > - REG( bus->regs + AM335X_CONF_I2C0_SDA ) = > - ( BBB_RXACTIVE | BBB_SLEWCTRL | BBB_PU_EN ); > + /* FIXME: The pin handling should be replaced by a proper pin handling > during > + * initialization. This one is heavily board specific. */ > +#if ! IS_AM335X > + printk ("The I2C driver currently only works on Beagle Bone. Please add > your pin configs.") > + return EINVAL; > +#endif > + bus->regs = (volatile bbb_i2c_regs *) register_base; > + switch ((intptr_t) bus->regs) { > + case AM335X_I2C0_BASE: > + bus->clkregs.ctrl_clkctrl = ®(AM335X_SOC_CM_WKUP_REGS + > + AM335X_CM_WKUP_CONTROL_CLKCTRL); > + bus->clkregs.i2c_clkctrl = ®(AM335X_SOC_CM_WKUP_REGS + > + AM335X_CM_WKUP_I2C0_CLKCTRL); > + bus->clkregs.clkstctrl = ®(AM335X_SOC_CM_WKUP_REGS + > + AM335X_CM_WKUP_CLKSTCTRL); > + bus->pinregs.conf_sda = ®(AM335X_PADCONF_BASE + > AM335X_CONF_I2C0_SDA); > + bus->pinregs.mmode_sda = 0; > + bus->pinregs.conf_scl = ®(AM335X_PADCONF_BASE + > AM335X_CONF_I2C0_SCL); > + bus->pinregs.mmode_scl = 0; > + break; > + case AM335X_I2C1_BASE: > + bus->clkregs.ctrl_clkctrl = ®(AM335X_SOC_CM_WKUP_REGS + > + AM335X_CM_WKUP_CONTROL_CLKCTRL); > + bus->clkregs.i2c_clkctrl = ®(AM335X_CM_PER_ADDR + > + AM335X_CM_PER_I2C1_CLKCTRL); > + bus->clkregs.clkstctrl = NULL; > + bus->pinregs.conf_sda = ®(AM335X_PADCONF_BASE + > AM335X_CONF_SPI0_D1); > + bus->pinregs.mmode_sda = 2; > + bus->pinregs.conf_scl = ®(AM335X_PADCONF_BASE + > AM335X_CONF_SPI0_CS0); > + bus->pinregs.mmode_scl = 2; > + break; > + case AM335X_I2C2_BASE: > + bus->clkregs.ctrl_clkctrl = ®(AM335X_SOC_CM_WKUP_REGS + > + AM335X_CM_WKUP_CONTROL_CLKCTRL); > + bus->clkregs.i2c_clkctrl = ®(AM335X_CM_PER_ADDR + > + AM335X_CM_PER_I2C2_CLKCTRL); > + bus->clkregs.clkstctrl = NULL; > + bus->pinregs.conf_sda = ®(AM335X_PADCONF_BASE + > AM335X_CONF_UART1_CTSN); > + bus->pinregs.mmode_sda = 3; > + bus->pinregs.conf_scl = ®(AM335X_PADCONF_BASE + > AM335X_CONF_UART1_RTSN); > + bus->pinregs.mmode_scl = 3; > + break; > + default: > + return EINVAL; > + } > + return 0; > +} > > - REG( bus->regs + AM335X_CONF_I2C0_SCL ) = > - ( BBB_RXACTIVE | BBB_SLEWCTRL | BBB_PU_EN ); > +static void am335x_i2c_pinmux( bbb_i2c_bus *bus ) > +{ > + *bus->pinregs.conf_sda = > + ( BBB_RXACTIVE | BBB_SLEWCTRL | bus->pinregs.mmode_sda); > + > + *bus->pinregs.conf_scl = > + ( BBB_RXACTIVE | BBB_SLEWCTRL | bus->pinregs.mmode_scl); > } > > -static void I2C0ModuleClkConfig( void ) > +static void am335x_i2c_module_clk_enable( bbb_i2c_bus *bus ) > { > + volatile uint32_t *ctrl_clkctrl = bus->clkregs.ctrl_clkctrl; > + volatile uint32_t *i2c_clkctrl = bus->clkregs.i2c_clkctrl; > + volatile uint32_t *clkstctrl = bus->clkregs.clkstctrl; > + > /* Writing to MODULEMODE field of AM335X_CM_WKUP_I2C0_CLKCTRL register. > */ > - REG( AM335X_SOC_CM_WKUP_REGS + AM335X_CM_WKUP_I2C0_CLKCTRL ) |= > - AM335X_CM_WKUP_I2C0_CLKCTRL_MODULEMODE_ENABLE; > + *i2c_clkctrl |= AM335X_CM_WKUP_I2C0_CLKCTRL_MODULEMODE_ENABLE; > > /* Waiting for MODULEMODE field to reflect the written value. */ > while ( AM335X_CM_WKUP_I2C0_CLKCTRL_MODULEMODE_ENABLE != > - ( REG( AM335X_SOC_CM_WKUP_REGS + AM335X_CM_WKUP_I2C0_CLKCTRL ) & > - AM335X_CM_WKUP_I2C0_CLKCTRL_MODULEMODE ) ) ; > + ( *i2c_clkctrl & AM335X_CM_WKUP_I2C0_CLKCTRL_MODULEMODE ) ) > + { /* busy wait */ } > > /* > * Waiting for IDLEST field in AM335X_CM_WKUP_CONTROL_CLKCTRL > @@ -54,16 +139,18 @@ static void I2C0ModuleClkConfig( void ) > */ > while ( ( AM335X_CM_WKUP_CONTROL_CLKCTRL_IDLEST_FUNC << > AM335X_CM_WKUP_CONTROL_CLKCTRL_IDLEST_SHIFT ) != > - ( REG( AM335X_SOC_CM_WKUP_REGS + AM335X_CM_WKUP_CONTROL_CLKCTRL > ) & > - AM335X_CM_WKUP_CONTROL_CLKCTRL_IDLEST ) ) ; > - > - /* > - * Waiting for CLKACTIVITY_I2C0_GFCLK field in AM335X_CM_WKUP_CLKSTCTRL > - * register to attain desired value. > - */ > - while ( AM335X_CM_WKUP_CLKSTCTRL_CLKACTIVITY_I2C0_GFCLK != > - ( REG( AM335X_SOC_CM_WKUP_REGS + AM335X_CM_WKUP_CLKSTCTRL ) & > - AM335X_CM_WKUP_CLKSTCTRL_CLKACTIVITY_I2C0_GFCLK ) ) ; > + ( *ctrl_clkctrl & AM335X_CM_WKUP_I2C0_CLKCTRL_IDLEST ) ) > + { /* busy wait */ } > + > + if ( clkstctrl != NULL ) { > + /* > + * Waiting for CLKACTIVITY_I2C0_GFCLK field in > AM335X_CM_WKUP_CLKSTCTRL > + * register to attain desired value. > + */ > + while ( AM335X_CM_WKUP_CLKSTCTRL_CLKACTIVITY_I2C0_GFCLK != > + ( *clkstctrl & > AM335X_CM_WKUP_CLKSTCTRL_CLKACTIVITY_I2C0_GFCLK ) ) > + { /* busy wait */ } > + } > > /* > * Waiting for IDLEST field in AM335X_CM_WKUP_I2C0_CLKCTRL register to > attain > @@ -71,333 +158,286 @@ static void I2C0ModuleClkConfig( void ) > */ > while ( ( AM335X_CM_WKUP_I2C0_CLKCTRL_IDLEST_FUNC << > AM335X_CM_WKUP_I2C0_CLKCTRL_IDLEST_SHIFT ) != > - ( REG( AM335X_SOC_CM_WKUP_REGS + AM335X_CM_WKUP_I2C0_CLKCTRL ) & > - AM335X_CM_WKUP_I2C0_CLKCTRL_IDLEST ) ) ; > -} > - > -static void am335x_i2c_reset( bbb_i2c_bus *bus ) > -{ > - volatile bbb_i2c_regs *regs = bus->regs; > - int timeout = I2C_TIMEOUT; > - > - if ( REG( ®s->BBB_I2C_CON ) & BBB_I2C_CON_EN ) { > - REG( ®s->BBB_I2C_CON ) = BBB_I2C_CON_CLR; > - udelay( 50000 ); > - } > - > - REG( ®s->BBB_I2C_SYSC ) = BBB_I2C_SYSC_SRST; /* for ES2 after soft > reset */ > - udelay( 1000 ); > - REG( ®s->BBB_I2C_CON ) = BBB_I2C_CON_EN; > - > - while ( !( REG( ®s->BBB_I2C_SYSS ) & BBB_I2C_SYSS_RDONE ) && > timeout-- ) { > - if ( timeout <= 0 ) { > - puts( "ERROR: Timeout in soft-reset\n" ); > - > - return; > - } > - > - udelay( 1000 ); > - } > + ( *i2c_clkctrl & AM335X_CM_WKUP_I2C0_CLKCTRL_IDLEST ) ) ; > } > -/* > - * 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 > +static int am335x_i2c_set_clock( > + i2c_bus *base, > + unsigned long clock > ) > { > - /* > - * 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 */ > - REG( ®s->BBB_I2C_CON ) = AM335X_I2C_CFG_7BIT_SLAVE_ADDR; > - } else { > - /* 10-bit slave address mode */ > - REG( ®s->BBB_I2C_CON ) = AM335X_I2C_CFG_10BIT_SLAVE_ADDR; > - } > -} > - > -static void am335x_i2c_next_byte( bbb_i2c_bus *bus ) > -{ > - i2c_msg *msg; > + bbb_i2c_bus *bus = (bbb_i2c_bus *) base; > + uint32_t prescaler, divider; > > - ++bus->msgs; > - --bus->msg_todo; > - msg = &bus->msgs[ 0 ]; > - bus->current_msg_todo = msg->len; > - bus->current_msg_byte = msg->buf; > -} > + prescaler = ( BBB_I2C_SYSCLK / BBB_I2C_INTERNAL_CLK ) - 1; > + bus->regs->BBB_I2C_PSC = prescaler; > + divider = BBB_I2C_INTERNAL_CLK / ( 2 * clock ); > + bus->regs->BBB_I2C_SCLL = ( divider - 7 ); > + bus->regs->BBB_I2C_SCLH = ( divider - 5 ); > > -static void am335x_i2c_masterint_enable( > - volatile bbb_i2c_regs *regs, > - unsigned int flag > -) > -{ > - REG( ®s->BBB_I2C_IRQENABLE_SET ) |= flag; > + return 0; > } > > -static void am335x_i2c_masterint_disable( > - volatile bbb_i2c_regs *regs, > - unsigned int flag > -) > +static int am335x_i2c_reset( bbb_i2c_bus *bus ) > { > - REG( ®s->BBB_I2C_IRQENABLE_CLR ) = flag; > -} > + volatile bbb_i2c_regs *regs = bus->regs; > + int timeout = 100; > + int err; > > -static void am335x_int_clear( > - volatile bbb_i2c_regs *regs, > - unsigned int flag > -) > -{ > - REG( ®s->BBB_I2C_IRQSTATUS ) = flag; > -} > + bus->con_reg = 0; > + regs->BBB_I2C_CON = bus->con_reg; > + udelay( 50000 ); > > -static void am335x_clean_interrupts( volatile bbb_i2c_regs *regs ) > -{ > - am335x_i2c_masterint_enable( regs, BBB_I2C_ALL_FLAGS ); > - am335x_int_clear( regs, BBB_I2C_ALL_FLAGS ); > - am335x_i2c_masterint_disable( regs, BBB_I2C_ALL_FLAGS ); > -} > - > -static void am335x_i2c_setup_read_transfer( > - bbb_i2c_bus *bus, > - volatile bbb_i2c_regs *regs, > - const i2c_msg *msgs, > - bool send_stop > -) > -{ > - REG( ®s->BBB_I2C_CNT ) = bus->current_msg_todo; > + regs->BBB_I2C_SYSC = AM335X_I2C_SYSC_SRST; > + udelay( 1000 ); > + regs->BBB_I2C_CON = AM335X_I2C_CON_I2C_EN; > > - REG( ®s->BBB_I2C_CON ) = AM335X_I2C_CFG_MST_RX | > AM335X_I2C_CON_I2C_EN; > + while ( !( regs->BBB_I2C_SYSS & AM335X_I2C_SYSS_RDONE ) > + && timeout >= 0 ) { > + --timeout; > + udelay( 100 ); > + } > > - if ( send_stop ) { > - REG( ®s->BBB_I2C_CON ) |= AM335X_I2C_CON_START | > AM335X_I2C_CON_STOP; > - } else { > - REG( ®s->BBB_I2C_CON ) |= AM335X_I2C_CON_START; > + if ( timeout <= 0 ) { > + puts( "ERROR: Timeout in soft-reset\n" ); > + return ETIMEDOUT; > } > > - am335x_i2c_masterint_enable( regs, AM335X_I2C_INT_RECV_READY | > - AM335X_I2C_IRQSTATUS_ARDY ); > -} > + /* Disable again after reset */ > + regs->BBB_I2C_CON = bus->con_reg; > > -static void am335x_i2c_continue_read_transfer( > - bbb_i2c_bus *bus, > - volatile bbb_i2c_regs *regs > -) > -{ > - bus->current_msg_byte[ bus->already_transferred ] = > - REG( ®s->BBB_I2C_DATA ); > + err = am335x_i2c_set_clock( &bus->base, I2C_BUS_CLOCK_DEFAULT ); > + if (err) { > + return err; > + } > > - bus->already_transferred++; > + regs->BBB_I2C_BUF = AM335X_I2C_BUF_TXTRSH(FIFO_THRESHOLD) | > + AM335X_I2C_BUF_RXTRSH(FIFO_THRESHOLD); > > - REG( ®s->BBB_I2C_IRQSTATUS ) = AM335X_I2C_INT_RECV_READY; > + /* Enable the I2C controller in master mode. */ > + bus->con_reg |= AM335X_I2C_CON_I2C_EN | AM335X_I2C_CON_MST; > + regs->BBB_I2C_CON = bus->con_reg; > > - if ( bus->already_transferred == bus->current_msg_todo - 1 ) { > - REG( ®s->BBB_I2C_CON ) |= AM335X_I2C_CON_STOP; > - } > -} > + regs->BBB_I2C_IRQENABLE_SET = > + AM335X_I2C_IRQSTATUS_XDR | AM335X_I2C_IRQSTATUS_XRDY | > + AM335X_I2C_IRQSTATUS_RDR | AM335X_I2C_IRQSTATUS_RRDY | > + AM335X_I2C_IRQSTATUS_ARDY | AM335X_I2C_IRQSTATUS_NACK | > + AM335X_I2C_IRQSTATUS_AL; > > -static void am335x_i2c_continue_write( > - bbb_i2c_bus *bus, > - volatile bbb_i2c_regs *regs > -) > -{ > - if ( bus->already_transferred == bus->msg_todo ) { > - REG( ®s->BBB_I2C_DATA ) = > - bus->current_msg_byte[ bus->already_transferred ]; > - REG( ®s->BBB_I2C_IRQSTATUS ) = AM335X_I2C_IRQSTATUS_XRDY; > - am335x_i2c_masterint_disable( regs, AM335X_I2C_IRQSTATUS_XRDY ); > - REG( ®s->BBB_I2C_CON ) |= AM335X_I2C_CON_STOP; > - } else { > - writeb( bus->current_msg_byte[ bus->already_transferred ], > - ®s->BBB_I2C_DATA ); > - REG( ®s->BBB_I2C_IRQSTATUS ) = AM335X_I2C_IRQSTATUS_XRDY; > - bus->already_transferred++; > - } > -} > - > -static void am335x_i2c_setup_write_transfer( > - bbb_i2c_bus *bus, > - volatile bbb_i2c_regs *regs, > - const i2c_msg *msgs > -) > -{ > - volatile unsigned int no_bytes; > - > - REG( ®s->BBB_I2C_CNT ) = bus->current_msg_todo; > - no_bytes = REG( ®s->BBB_I2C_CNT ); > - (void) no_bytes; /* indicate we know that no_bytes is not referenced > again */ > - REG( ®s->BBB_I2C_SA ) = msgs->addr; > - REG( ®s->BBB_I2C_CON ) = AM335X_I2C_CFG_MST_TX | > AM335X_I2C_CON_I2C_EN; > - am335x_clean_interrupts( regs ); > - am335x_i2c_masterint_enable( regs, AM335X_I2C_IRQSTATUS_XRDY ); > - REG( ®s->BBB_I2C_CON ) |= AM335X_I2C_CON_START | AM335X_I2C_CON_STOP; > + return 0; > } > > -static void am335x_i2c_setup_transfer( > - bbb_i2c_bus *bus, > - volatile bbb_i2c_regs *regs > -) > +/* Return true if done. */ > +static bool am335x_i2c_transfer_intr(bbb_i2c_bus *bus, uint32_t status) > { > - const i2c_msg *msgs = bus->msgs; > - uint32_t msg_todo = bus->msg_todo; > - bool send_stop = false; > - uint32_t i; > + size_t i; > + size_t amount = 0; > + volatile bbb_i2c_regs *regs = bus->regs; > > - bus->current_todo = msgs[ 0 ].len; > + /* Handle errors */ > + if ((status & AM335X_I2C_IRQSTATUS_NACK) != 0) { > + debug_print("NACK\n"); > + regs->BBB_I2C_IRQSTATUS = AM335X_I2C_IRQSTATUS_NACK; > + bus->error = ENXIO; > + } else if ((status & AM335X_I2C_IRQSTATUS_AL) != 0) { > + debug_print("Arbitration lost\n"); > + regs->BBB_I2C_IRQSTATUS = AM335X_I2C_IRQSTATUS_AL; > + bus->error = ENXIO; > + } > > - for ( i = 1; i < msg_todo && ( msgs[ i ].flags & I2C_M_NOSTART ) != 0; > - ++i ) { > - bus->current_todo += msgs[ i ].len; > + /* Transfer finished? */ > + if ((status & AM335X_I2C_IRQSTATUS_ARDY) != 0) { > + debug_print("ARDY transaction complete\n"); > + if (bus->error != 0 && (bus->buffer->flags & I2C_M_STOP) == 0) { > + regs->BBB_I2C_CON = bus->con_reg | AM335X_I2C_CON_STOP; > + } > + regs->BBB_I2C_IRQSTATUS = AM335X_I2C_IRQSTATUS_ARDY | > + AM335X_I2C_IRQSTATUS_RDR | > + AM335X_I2C_IRQSTATUS_RRDY | > + AM335X_I2C_IRQSTATUS_XDR | > + AM335X_I2C_IRQSTATUS_XRDY; > + return true; > } > > - 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 = ( msgs->flags & I2C_M_RD ) != 0; > - bus->already_transferred = ( bus->read == true ) ? 0 : 1; > + if (bus->buffer->flags & I2C_M_RD) { > + if (status & AM335X_I2C_IRQSTATUS_RDR) { > + debug_print("RDR\n"); > + /* last data received */ > + amount = bus->buffer->len - bus->buffer_pos; > + } else if (status & AM335X_I2C_IRQSTATUS_RRDY) { > + debug_print("RRDY\n"); > + /* FIFO threshold reached */ > + amount = min(FIFO_THRESHOLD, bus->buffer->len - bus->buffer_pos); > + } > > - if ( bus->read ) { > - if ( bus->current_msg_todo == 1 ) { > - send_stop = true; > + debug_print("Read %d bytes\n", amount); > + for (i = 0; i < amount; i++) { > + bus->buffer->buf[bus->buffer_pos] = (uint8_t)(regs->BBB_I2C_DATA); > + ++bus->buffer_pos; > } > > - am335x_i2c_setup_read_transfer( bus, regs, msgs, send_stop ); > + if (status & AM335X_I2C_IRQSTATUS_RDR) { > + regs->BBB_I2C_IRQSTATUS =AM335X_I2C_IRQSTATUS_RDR; > + } > + if (status & AM335X_I2C_IRQSTATUS_RRDY) { > + regs->BBB_I2C_IRQSTATUS =AM335X_I2C_IRQSTATUS_RRDY; > + } > } else { > - am335x_i2c_setup_write_transfer( bus, regs, msgs ); > + if (status & AM335X_I2C_IRQSTATUS_XDR) { > + debug_print("XDR\n"); > + /* Remaining TX data won't reach the FIFO threshold. */ > + amount = bus->buffer->len - bus->buffer_pos; > + } else if (status & AM335X_I2C_IRQSTATUS_XRDY) { > + debug_print("XRDY\n"); > + /* FIFO threshold reached */ > + amount = min(FIFO_THRESHOLD, bus->buffer->len - bus->buffer_pos); > + } > + > + debug_print("Write %d bytes\n", amount); > + for (i = 0; i < amount; i++) { > + regs->BBB_I2C_DATA = bus->buffer->buf[bus->buffer_pos]; > + ++bus->buffer_pos; > + } > + > + if (status & AM335X_I2C_IRQSTATUS_XDR) { > + regs->BBB_I2C_IRQSTATUS = AM335X_I2C_IRQSTATUS_XDR; > + } > + if (status & AM335X_I2C_IRQSTATUS_XRDY) { > + regs->BBB_I2C_IRQSTATUS = AM335X_I2C_IRQSTATUS_XRDY; > + } > } > + > + return false; > } > > static void am335x_i2c_interrupt( void *arg ) > { > - bbb_i2c_bus *bus = 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; > + uint32_t status; > > - /* > - * 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 ) ); > + status = regs->BBB_I2C_IRQSTATUS; > > - if ( irqstatus & AM335X_I2C_INT_RECV_READY ) { > - am335x_i2c_continue_read_transfer( bus, regs ); > - } > - > - if ( irqstatus & AM335X_I2C_IRQSTATUS_XRDY ) { > - am335x_i2c_continue_write( bus, regs ); > - } > + debug_print("interrupt: %08x\n", status); > > - if ( irqstatus & AM335X_I2C_IRQSTATUS_NACK ) { > - done = true; > - am335x_i2c_masterint_disable( regs, AM335X_I2C_IRQSTATUS_NACK ); > + if (status == 0) { > + /* Why can this even happen? */ > + return; > } > > - if ( irqstatus & AM335X_I2C_IRQSTATUS_ARDY ) { > - done = true; > - REG( ®s->BBB_I2C_IRQSTATUS ) = BBB_I2C_STAT_ARDY; > + if (bus->buffer == NULL) { > + debug_print("Buffer is NULL\n"); > + bus->error = EINVAL; > } > > - if ( irqstatus & AM335X_I2C_IRQSTATUS_BF ) { > - REG( ®s->BBB_I2C_IRQSTATUS ) = AM335X_I2C_IRQSTATUS_BF; > - } > - > - if ( done ) { > - uint32_t err = irqstatus & BBB_I2C_IRQ_ERROR; > - am335x_i2c_next_byte( bus ); > - > - if ( bus->msg_todo == 0 ) { > - rtems_status_code sc; > - 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 ); > - } > + if (bus->buffer == NULL || am335x_i2c_transfer_intr(bus, status)) { > + rtems_status_code sc; > + sc = rtems_event_transient_send( bus->task_id ); > + _Assert( sc == RTEMS_SUCCESSFUL ); > + (void) sc; /* suppress warning in case of no assert */ > } > } > > static int am335x_i2c_transfer( > i2c_bus *base, > i2c_msg *msgs, > - uint32_t msg_count > + uint32_t nmsgs > ) > { > - rtems_status_code sc; > - bbb_i2c_bus *bus = (bbb_i2c_bus *) base; > - volatile bbb_i2c_regs *regs; > - uint32_t i; > + size_t i; > + int err = 0; > + bool repstart = false; > + int timeout = 0; > + bbb_i2c_bus *bus = (bbb_i2c_bus *) base; > + volatile bbb_i2c_regs *regs = bus->regs; > + uint32_t reg; > + rtems_status_code sc; > + > + bus->task_id = rtems_task_self(); > > - rtems_task_wake_after( 1 ); > + for (i = 0; i < nmsgs; i++) { > + bus->buffer = &msgs[i]; > + bus->buffer_pos = 0; > + bus->error = 0; > > - if ( msg_count < 1 ) { > - return 1; > - } > + debug_print("processing %2d/%d: addr: 0x%04x, flags: 0x%04x, len: %d, > buf: %p\n", > + i, nmsgs, msgs[i].addr, msgs[i].flags, msgs[i].len, msgs[i].buf); > > - for ( i = 0; i < msg_count; ++i ) { > - if ( ( msgs[ i ].flags & I2C_M_RECV_LEN ) != 0 ) { > - return -EINVAL; > + if (bus->buffer == NULL || bus->buffer->buf == NULL || > + bus->buffer->len == 0) { > + err = EINVAL; > + break; > } > - } > > - bus->msgs = &msgs[ 0 ]; > - bus->msg_todo = msg_count; > - bus->current_msg_todo = msgs[ 0 ].len; > - bus->current_msg_byte = 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; > + /* > + * Send START when bus is busy on repeated starts. > + * Otherwise wait some time. > + */ > + if (!repstart) { > + timeout = 0; > + while ((regs->BBB_I2C_IRQSTATUS_RAW & AM335X_I2C_IRQSTATUS_BB) != 0 > + && timeout <= TRANSFER_TIMEOUT_COUNT) { > + ++timeout; > + rtems_task_wake_after(RTEMS_MICROSECONDS_TO_TICKS(1000)); > + } > + if (timeout > TRANSFER_TIMEOUT_COUNT) { > + err = EBUSY; > + break; > + } > + timeout = 0; > + } else { > + repstart = false; > + } > + > + if ((bus->buffer->flags & I2C_M_STOP) == 0) { > + repstart = true; > + } > > - sc = rtems_event_transient_receive( RTEMS_WAIT, bus->base.timeout ); > + regs->BBB_I2C_SA = bus->buffer->addr; > + regs->BBB_I2C_CNT = bus->buffer->len; > > - if ( sc != RTEMS_SUCCESSFUL ) { > - am335x_i2c_reset( bus ); > - rtems_event_transient_clear(); > + regs->BBB_I2C_BUF |= AM335X_I2C_BUF_RXFIFO_CLR | > AM335X_I2C_BUF_TXFIFO_CLR; > > - return -ETIMEDOUT; > + reg = bus->con_reg | AM335X_I2C_CON_START; > + if (!repstart) { > + reg |= AM335X_I2C_CON_STOP; > + } > + if ((bus->buffer->flags & I2C_M_RD) == 0) { > + reg |= AM335X_I2C_CON_TRX; > + } > + /* Implicit stop on last message. */ > + if (i == nmsgs - 1) { > + reg |= AM335X_I2C_CON_STOP; > + } > + regs->BBB_I2C_CON = reg; > + > + sc = rtems_event_transient_receive( RTEMS_WAIT, bus->base.timeout ); > + if ( sc != RTEMS_SUCCESSFUL ) { > + rtems_event_transient_clear(); > + err = ETIMEDOUT; > + break; > + } > + if (bus->error) { > + err = bus->error; > + break; > + } > } > > - return 0; > -} > + if (timeout == 0) { > + while ((regs->BBB_I2C_IRQSTATUS_RAW & AM335X_I2C_IRQSTATUS_BB) != 0 > + && timeout <= TRANSFER_TIMEOUT_COUNT) { > + ++timeout; > + rtems_task_wake_after(RTEMS_MICROSECONDS_TO_TICKS(1000)); > + } > + } > > -static int am335x_i2c_set_clock( > - i2c_bus *base, > - unsigned long clock > -) > -{ > - bbb_i2c_bus *bus = (bbb_i2c_bus *) base; > - uint32_t prescaler, divider; > + if ((regs->BBB_I2C_CON & AM335X_I2C_CON_MST) == 0) { > + regs->BBB_I2C_CON = bus->con_reg; > + } > > - prescaler = ( BBB_I2C_SYSCLK / BBB_I2C_INTERNAL_CLK ) - 1; > - REG( &bus->regs->BBB_I2C_PSC ) = prescaler; > - divider = BBB_I2C_INTERNAL_CLK / ( 2 * clock ); > - REG( &bus->regs->BBB_I2C_SCLL ) = ( divider - 7 ); > - REG( &bus->regs->BBB_I2C_SCLH ) = ( divider - 5 ); > + bus->buffer = NULL; > > - return 0; > + return -err; > } > > static void am335x_i2c_destroy( i2c_bus *base ) > @@ -405,6 +445,8 @@ static void am335x_i2c_destroy( i2c_bus *base ) > bbb_i2c_bus *bus = (bbb_i2c_bus *) base; > rtems_status_code sc; > > + bus->regs->BBB_I2C_IRQENABLE_CLR = 0xFFFF; > + bus->regs->BBB_I2C_CON = 0; > sc = rtems_interrupt_handler_remove( bus->irq, am335x_i2c_interrupt, > bus ); > _Assert( sc == RTEMS_SUCCESSFUL ); > (void) sc; > @@ -422,36 +464,36 @@ int am335x_i2c_bus_register( > rtems_status_code sc; > int err; > > - /* Check bus number is >0 & <MAX */ > + (void) input_clock; /* FIXME: Unused. Left for compatibility. */ > + > bus = (bbb_i2c_bus *) i2c_bus_alloc_and_init( sizeof( *bus ) ); > > if ( bus == NULL ) { > return -1; > } > > - bus->regs = (volatile bbb_i2c_regs *) register_base; > - > - I2C0ModuleClkConfig(); > - am335x_i2c0_pinmux( bus ); > - am335x_i2c_reset( bus ); > - bus->input_clock = input_clock; > - err = am335x_i2c_set_clock( &bus->base, I2C_BUS_CLOCK_DEFAULT ); > + bus->irq = irq; > > - if ( err != 0 ) { > + err = am335x_i2c_fill_registers(bus, register_base); > + if (err != 0) { > ( *bus->base.destroy )( &bus->base ); > - rtems_set_errno_and_return_minus_one( -err ); > + rtems_set_errno_and_return_minus_one( err ); > + } > + am335x_i2c_module_clk_enable(bus); > + am335x_i2c_pinmux( bus ); > + err = am335x_i2c_reset( bus ); > + if (err != 0) { > + ( *bus->base.destroy )( &bus->base ); > + rtems_set_errno_and_return_minus_one( err ); > } > - > - bus->irq = irq; > - REG( &bus->regs->BBB_I2C_IRQSTATUS ) = BBB_I2C_ALL_IRQ_FLAGS; > > sc = rtems_interrupt_handler_install( > - irq, > + bus->irq, > "BBB_I2C", > RTEMS_INTERRUPT_UNIQUE, > (rtems_interrupt_handler) am335x_i2c_interrupt, > bus > - ); > + ); > > if ( sc != RTEMS_SUCCESSFUL ) { > ( *bus->base.destroy )( &bus->base ); > diff --git a/bsps/arm/beagle/include/bsp/i2c.h > b/bsps/arm/beagle/include/bsp/i2c.h > index 3ada3c4b0d..9d253406bf 100644 > --- a/bsps/arm/beagle/include/bsp/i2c.h > +++ b/bsps/arm/beagle/include/bsp/i2c.h > @@ -24,76 +24,15 @@ > #define LIBBSP_ARM_BEAGLE_I2C_H > > #include <rtems.h> > -#include <dev/i2c/i2c.h> > #include <bsp.h> > +#include <dev/i2c/i2c.h> > > #ifdef __cplusplus > extern "C" { > #endif /* __cplusplus */ > > - > -/* I2C Configuration Register (I2C_CON): */ > - > -#define BBB_I2C_CON_EN (1 << 15) /* I2C module enable */ > -#define BBB_I2C_CON_BE (1 << 14) /* Big endian mode */ > -#define BBB_I2C_CON_STB (1 << 11) /* Start byte mode (master mode only) > */ > -#define BBB_I2C_CON_MST (1 << 10) /* Master/slave mode */ > -#define BBB_I2C_CON_TRX (1 << 9) /* Transmitter/receiver mode */ > - /* (master mode only) */ > -#define BBB_I2C_CON_XA (1 << 8) /* Expand address */ > -#define BBB_I2C_CON_STP (1 << 1) /* Stop condition (master mode only) */ > -#define BBB_I2C_CON_STT (1 << 0) /* Start condition (master mode only) > */ > -#define BBB_I2C_CON_CLR 0x0 /* Clear configuration register */ > -/* I2C Status Register (I2C_STAT): */ > - > -#define BBB_I2C_STAT_SBD (1 << 15) /* Single byte data */ > -#define BBB_I2C_STAT_BB (1 << 12) /* Bus busy */ > -#define BBB_I2C_STAT_ROVR (1 << 11) /* Receive overrun */ > -#define BBB_I2C_STAT_XUDF (1 << 10) /* Transmit underflow */ > -#define BBB_I2C_STAT_AAS (1 << 9) /* Address as slave */ > -#define BBB_I2C_STAT_GC (1 << 5) > -#define BBB_I2C_STAT_XRDY (1 << 4) /* Transmit data ready */ > -#define BBB_I2C_STAT_RRDY (1 << 3) /* Receive data ready */ > -#define BBB_I2C_STAT_ARDY (1 << 2) /* Register access ready */ > -#define BBB_I2C_STAT_NACK (1 << 1) /* No acknowledgment interrupt enable > */ > -#define BBB_I2C_STAT_AL (1 << 0) /* Arbitration lost interrupt enable */ > - > -/* I2C Interrupt Enable Register (I2C_IE): */ > -#define BBB_I2C_IE_GC_IE (1 << 5) > -#define BBB_I2C_IE_XRDY_IE (1 << 4) /* Transmit data ready interrupt > enable */ > -#define BBB_I2C_IE_RRDY_IE (1 << 3) /* Receive data ready interrupt > enable */ > -#define BBB_I2C_IE_ARDY_IE (1 << 2) /* Register access ready interrupt > enable */ > -#define BBB_I2C_IE_NACK_IE (1 << 1) /* No acknowledgment interrupt > enable */ > -#define BBB_I2C_IE_AL_IE (1 << 0) /* Arbitration lost interrupt enable */ > - > -/* I2C SYSC Register (I2C_SYSC): */ > -#define BBB_I2C_SYSC_SRST (1 << 1) > - > -#define BBB_I2C_TIMEOUT 1000 > - > -#define BBB_I2C_SYSS_RDONE (1 << 0) /* Internel reset > monitoring */ > - > -#define BBB_CONFIG_SYS_I2C_SPEED 100000 > -#define BBB_CONFIG_SYS_I2C_SLAVE 1 > -#define BBB_I2C_ALL_FLAGS 0x7FFF > -#define BBB_I2C_ALL_IRQ_FLAGS 0xFFFF > - > #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 \ > - ( AM335X_I2C_IRQSTATUS_ARDY \ > - | AM335X_I2C_IRQSTATUS_XRDY ) > > #define BBB_I2C_0_BUS_PATH "/dev/i2c-0" > #define BBB_I2C_1_BUS_PATH "/dev/i2c-1" > @@ -103,9 +42,6 @@ extern "C" { > #define BBB_I2C1_IRQ 71 > #define BBB_I2C2_IRQ 30 > > -#define BBB_MODE2 2 > -#define BBB_MODE3 3 > - > typedef enum { > I2C0, > I2C1, > @@ -151,26 +87,10 @@ typedef struct i2c_regs { > uint32_t BBB_I2C_SBLOCK; > } 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; > - uint8_t *current_msg_byte; > - 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, > + uint32_t input_clock, /* FIXME: Unused. Left for > compatibility. */ > rtems_vector_number irq > ); > > diff --git a/bsps/arm/include/libcpu/am335x.h > b/bsps/arm/include/libcpu/am335x.h > index a78cbd028d..b69c822d62 100644 > --- a/bsps/arm/include/libcpu/am335x.h > +++ b/bsps/arm/include/libcpu/am335x.h > @@ -664,6 +664,9 @@ > #define AM335X_I2C_CON_MST (0x00000400u) > #define AM335X_I2C_CON_STB (0x00000800u) > #define AM335X_I2C_SYSC_AUTOIDLE (0x00000001u) > +#define AM335X_I2C_SYSC_SRST (0x00000002u) > +#define AM335X_I2C_SYSC_ENAWAKEUP (0x00000004u) > +#define AM335X_I2C_SYSS_RDONE (0x00000001u) > > /*I2C0 module clock registers*/ > #define AM335X_CM_WKUP_CONTROL_CLKCTRL (0x4) > @@ -686,29 +689,39 @@ > #define AM335X_CM_PER_CONTROL_CLKCTRL_IDLEST (0x00030000u) > > > +#define AM335X_I2C_BUF_TXTRSH_SHIFT (0) > +#define AM335X_I2C_BUF_TXTRSH_MASK (0x0000003Fu) > +#define AM335X_I2C_BUF_TXTRSH(X) (((X) << AM335X_I2C_BUF_TXTRSH_SHIFT) > \ > + & AM335X_I2C_BUF_TXTRSH_MASK) > +#define AM335X_I2C_BUF_TXFIFO_CLR (0x00000040u) > +#define AM335X_I2C_BUF_RXTRSH_SHIFT (8) > +#define AM335X_I2C_BUF_RXTRSH_MASK (0x00003F00u) > +#define AM335X_I2C_BUF_RXTRSH(X) (((X) << AM335X_I2C_BUF_RXTRSH_SHIFT) > \ > + & AM335X_I2C_BUF_RXTRSH_MASK) > +#define AM335X_I2C_BUF_RXFIFO_CLR (0x00004000u) > + > /* I2C status Register */ > +#define AM335X_I2C_IRQSTATUS_AL (1 << 0) > #define AM335X_I2C_IRQSTATUS_NACK (1 << 1) > -#define AM335X_I2C_IRQSTATUS_ROVR (1 << 11) > -#define AM335X_I2C_IRQSTATUS_AL (1<<0) > #define AM335X_I2C_IRQSTATUS_ARDY (1 << 2) > #define AM335X_I2C_IRQSTATUS_RRDY (1 << 3) > #define AM335X_I2C_IRQSTATUS_XRDY (1 << 4) > -#define AM335X_I2C_IRQSTATUS_XUDF (1 << 10) > -#define AM335X_I2C_BUF_TXFIFO_CLR (0x00000040u) > -#define AM335X_I2C_BUF_RXFIFO_CLR (0x00004000u) > -#define AM335X_I2C_IRQSTATUS_AAS (1 << 9) > -#define AM335X_I2C_IRQSTATUS_BF (1 << 8) > +#define AM335X_I2C_IRQSTATUS_GC (1 << 5) > #define AM335X_I2C_IRQSTATUS_STC (1 << 6) > -#define AM335X_I2C_IRQSTATUS_GC (1 << 5) > -#define AM335X_I2C_IRQSTATUS_XDR (1 << 14) > -#define AM335X_I2C_IRQSTATUS_RDR (1 << 13) > +#define AM335X_I2C_IRQSTATUS_AERR (1 << 7) > +#define AM335X_I2C_IRQSTATUS_BF (1 << 8) > +#define AM335X_I2C_IRQSTATUS_AAS (1 << 9) > +#define AM335X_I2C_IRQSTATUS_XUDF (1 << 10) > +#define AM335X_I2C_IRQSTATUS_ROVR (1 << 11) > +#define AM335X_I2C_IRQSTATUS_BB (1 << 12) > +#define AM335X_I2C_IRQSTATUS_RDR (1 << 13) > +#define AM335X_I2C_IRQSTATUS_XDR (1 << 14) > > #define AM335X_I2C_INT_RECV_READY AM335X_I2C_IRQSTATUS_RRDY > #define AM335X_I2C_CON_STOP (0x00000002u) > #define AM335X_I2C_CON_START (0x00000001u) > #define AM335X_I2C_CFG_MST_RX AM335X_I2C_CON_MST > #define AM335X_I2C_CFG_MST_TX (AM335X_I2C_CON_TRX | AM335X_I2C_CON_MST) > -#define AM335X_I2C_IRQSTATUS_RAW_BB (0x00001000u) > #define AM335X_CM_PER_OCPWP_L3_CLKSTCTRL_CLKACTIVITY_OCPWP_L4_GCLK > (0x00000020u) > #define AM335X_I2C_INT_STOP_CONDITION AM335X_I2C_IRQSTATUS_BF > > -- > 2.21.0 > >
_______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel