[PATCH] bsp/beagle: Partial re-write of I2C driver.
list at c-mauderer.de
list at c-mauderer.de
Mon Jun 24 20:16:59 UTC 2019
From: Christian Mauderer <christian.mauderer at 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.
---
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 <punitvara at gmail.com>
* Copyright (c) 2017 Sichen Zhao <zsc19940506 at gmail.com>
+ * Copyright (c) 2019 Christian Mauderer <christian.mauderer at 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<punitvara at 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
More information about the devel
mailing list