From b89d6cc5e4eb29d54d9b64b9374c4cb3208d0d66 Mon Sep 17 00:00:00 2001 From: Christian Mauderer Date: Mon, 24 Jun 2019 22:16:59 +0200 Subject: bsp/beagle: Partial re-write of I2C driver. 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. Closes #3764. --- 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 * Copyright (c) 2017 Sichen Zhao + * Copyright (c) 2019 Christian Mauderer * * 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 works, currently - * the i2c file is working on the Beaglebone Black board(AM335x) - */ - +#include #include #include #include @@ -27,26 +24,114 @@ #include #include #include +#include -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 & 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 -#include #include +#include #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 -- cgit v1.2.3