[PATCH] bsp/beagle: Partial re-write of I2C driver.
Vijay Kumar Banerjee
vijaykumar9597 at gmail.com
Tue Jun 25 06:21:15 UTC 2019
On Tue, Jun 25, 2019 at 1:47 AM <list at c-mauderer.de> wrote:
> 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.
>
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 <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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.rtems.org/pipermail/devel/attachments/20190625/2a6fc513/attachment-0002.html>
More information about the devel
mailing list