This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM335x i2c bus timeout by External Slave Device Hanging the Bus by Holding SDA Low



Hello,

Our AM335x is connected via i2c (bus 0) to audio codec. Occiasionally a i2c bus hang occurs like in the

Ti i2c tips is mentioned.

http://processors.wiki.ti.com/index.php/I2C_Tips#External_Slave_Device_Hanging_the_Bus_by_Holding_SDA_Low

This problem is only solved after power down / up. Rebooting linux does not resolve the problem.

A bus timout occurs in: drivers/i2c/busses/i2c-omap.c, function omap_i2c_wait_for_bb().

I do not have experience with i2c drivers so I am asking on some advice.

When that timeout is detected I want to toggle SCL via gpio to let the i2c slave device make the SDA line high.

Currently at the place of the timeout I change the i2c pins sda/scl to gpio, then I toggle scl by gpio_direction_output( ).

But then Kernel panic occerred.

My question:  is there some example code or how/where to change the ic2 pins to gpio <--> i2c mode.

I can use gpio on i2c pins according our HW engineer.

Thanks in advance for your info.

Kind Regards

Teun Grinwis

  • Hi Teun,

    I will forward this to the SW experts.

  • Please post the kernel panic to give us some idea of what might be happening.
  • It's maybe worth mentioning that you can also manually control the lines through the SYSTEST register of the I2C peripheral, as alternative to switching them to GPIO (although that should indeed work fine also, but I don't know what the proper procedure is for it under Linux)

  • Hello,
    It is not really a kernel panic. but I explain below what I have done, and see now on the log console.

    Currently I am experimenting with toggling a couple of times the i2c0_scl in board startup sw.
    Before registering the i2c0 modules I set the i2c0 to gpio as follows:

    static struct pinmux_config ads_i2c0_pin_mux_prod_test[] = {
    {"i2c0_sda.gpio3_5", OMAP_MUX_MODE7 | AM33XX_PULL_UP | AM33XX_PULL_ENBL | AM33XX_PIN_INPUT}, // gpio3_5 (101)
    {"i2c0_scl.gpio3_6", OMAP_MUX_MODE7 | AM33XX_PULL_UP | AM33XX_PULL_ENBL | AM33XX_PIN_OUTPUT}, // gpio3_6 (102)
    {NULL, 0},
    };

    void ads_i2c0_sda_in_scl_out( void )
    {
    pr_info("-->%s", __func__ );
    setup_pin_mux( ads_i2c0_pin_mux_prod_test );
    }

    //-----------------------------------------------------------------------------
    After that I toggle the scl line a couple of times with following function:
    //-----------------------------------------------------------------------------

    void check_toggle_i2c0()
    {
    static int sda_gpio_nr = 3*32+5;
    static int scl_gpio_nr = 3*32+6;

    int scl_set = 0;
    ads_i2c0_sda_in_scl_out(); /* _TGR_ */

    pr_info("-->%s !!!! toggle scl testing until sda is low !\n", __func__); // _TGR_
    int ix=0;
    while (++ix < 5)
    {
    int sda = gpio_get_value( sda_gpio_nr );

    if (scl_set == 0) scl_set = 1;
    else scl_set = 0;
    gpio_direction_output(scl_gpio_nr, scl_set);
    //
    pr_info( "%s: set scl=%d sda=%d", __func__, scl_set, sda);
    msleep(1000);
    }
    ads_i2c0_init(); /* _TGR_ */
    }

    //-----------------------------------------------------------------------------
    Then I set scl, sda to i2c mode as follows
    //-----------------------------------------------------------------------------

    static struct pinmux_config ads_i2c0_pin_mux[] = {
    {"i2c0_sda.i2c0_sda", OMAP_MUX_MODE0 | AM33XX_SLEWCTRL_SLOW |
    AM33XX_PULL_UP | AM33XX_INPUT_EN | AM33XX_PULL_ENBL},
    {"i2c0_scl.i2c0_scl", OMAP_MUX_MODE0 | AM33XX_SLEWCTRL_SLOW |
    AM33XX_PULL_UP | AM33XX_INPUT_EN | AM33XX_PULL_ENBL | AM33XX_PIN_OUTPUT},
    {NULL, 0},
    };

    void ads_i2c0_init( void )
    {
    pr_info("-->%s", __func__ );
    setup_pin_mux( ads_i2c0_pin_mux );
    return;
    }
    //-----------------------------------------------------------------------------
    The following is now shown on my debug consolo at startup:

    [ 0.103729] DDR PLL Spread Spectrum enabled with 2 percent
    [ 0.103759] LCD PLL Spread Spectrum enabled with 2 percent
    [ 0.103790] COR PLL Spread Spectrum enabled with 2 percent
    [ 0.103820] PER PLL Spread Spectrum enabled with 2 percent
    [ 0.103820] -->ads_i2c0_sda_in_scl_out
    [ 0.103851] -->check_toggle_i2c0 !!!! toggle scl testing until sda is low !
    [ 0.103881] gpio_direction_output: gpio=102 value=1
    [ 0.103881] ------------[ cut here ]------------
    [ 0.103912] WARNING: at drivers/gpio/gpiolib.c:101 gpio_ensure_requested+0x58/0xd4()
    [ 0.103942] autorequest GPIO-102
    [ 0.103942] Modules linked in:
    [ 0.103942] Backtrace:
    [ 0.103973] [<c0017d78>] (dump_backtrace+0x0/0x10c) from [<c03db260>] (dump_stack+0x18/0x1c)
    [ 0.104003] r6:c01df5f8 r5:00000009 r4:df02deb0 r3:c0594bd4
    [ 0.104034] [<c03db248>] (dump_stack+0x0/0x1c) from [<c003e934>] (warn_slowpath_common+0x54/0x6c)
    [ 0.104064] [<c003e8e0>] (warn_slowpath_common+0x0/0x6c) from [<c003e9f0>] (warn_slowpath_fmt+0x38/0x40)
    [ 0.104064] r8:00000001 r7:c05eb048 r6:00000066 r5:df004620 r4:c05eb048
    [ 0.104095] r3:00000009
    [ 0.104125] [<c003e9b8>] (warn_slowpath_fmt+0x0/0x40) from [<c01df5f8>] (gpio_ensure_requested+0x58/0xd4)
    [ 0.104125] r3:00000066 r2:c04d9148
    [ 0.104156] [<c01df5a0>] (gpio_ensure_requested+0x0/0xd4) from [<c01df94c>] (gpio_direction_output+0x90/0x184)
    [ 0.104187] r6:c05eab80 r5:df004620 r4:00000006 r3:00000020
    [ 0.104217] [<c01df8bc>] (gpio_direction_output+0x0/0x184) from [<c0030e1c>] (check_toggle_i2c0+0x40/0x78)
    [ 0.104248] [<c0030ddc>] (check_toggle_i2c0+0x0/0x78) from [<c05460cc>] (pcm051_init+0x1f8/0x3f8)
    [ 0.104248] r6:c05cae40 r5:c0571bc4 r4:00000000 r3:00000000
    [ 0.104278] [<c0545ed4>] (pcm051_init+0x0/0x3f8) from [<c053c3f4>] (customize_machine+0x24/0x30)
    [ 0.104309] r4:c0571710
    [ 0.104339] [<c053c3d0>] (customize_machine+0x0/0x30) from [<c0008764>] (do_one_initcall+0x128/0x1a8)
    [ 0.104339] [<c000863c>] (do_one_initcall+0x0/0x1a8) from [<c0539888>] (kernel_init+0x84/0x120)
    [ 0.104370] [<c0539804>] (kernel_init+0x0/0x120) from [<c0041714>] (do_exit+0x0/0x654)
    [ 0.104400] r5:c0539804 r4:00000000
    [ 0.104461] ---[ end trace 1b75b31a2719ed1c ]---
    [ 0.104461] check_toggle_i2c0: set scl=1 sda=1
    [ 1.076721] gpio_direction_output: gpio=102 value=0
    [ 1.076721] check_toggle_i2c0: set scl=0 sda=1
    [ 2.057250] gpio_direction_output: gpio=102 value=1
    [ 2.057281] check_toggle_i2c0: set scl=1 sda=1
    [ 3.037963] gpio_direction_output: gpio=102 value=0
    [ 3.037963] check_toggle_i2c0: set scl=0 sda=1
    [ 4.018554] -->ads_i2c0_init

    Thanks in advance for your info.

    Kind Regards

    Teun Grinwis
  • Teun Grinwis said:
    void check_toggle_i2c0()
    {
    static int sda_gpio_nr = 3*32+5;
    static int scl_gpio_nr = 3*32+6;

    int scl_set = 0;
    ads_i2c0_sda_in_scl_out(); /* _TGR_ */

    pr_info("-->%s !!!! toggle scl testing until sda is low !\n", __func__); // _TGR_
    int ix=0;
    while (++ix < 5)
    {
    int sda = gpio_get_value( sda_gpio_nr );

    if (scl_set == 0) scl_set = 1;
    else scl_set = 0;
    gpio_direction_output(scl_gpio_nr, scl_set);
    //
    pr_info( "%s: set scl=%d sda=%d", __func__, scl_set, sda);
    msleep(1000);
    }
    ads_i2c0_init(); /* _TGR_ */
    }

    Comments:

    • You never called the "request" function for the GPIO.
    • Your printout says your testing until sda is low.  I don't understand that.  The goal here should be for SDA to be high.
    • I don't see anything conditional in your code that's actually looking at the state of SDA.
    • In my opinion the code should be conditioned upon both the count and the state of SDA.  In other words, if you've sent out 9 pulses (18 toggles) then you should terminate the loop.  If SDA is high then you should terminate the loop.
    • [Nitpick] You should replace the if/else statement with "scl_set ^= 1" to more simply toggle the bit.
  • Hello Brad,
    Thanks for your info, I see your points but the code is not really finished.
    My first goal is to toggle scl (checking on scope).

    Your 1st point, "never called the "request" function for GPIO:
    how todo this?
    I adapted the BSP board file from our system on module supplier, and adapted the io's for the
    pheripherals on our board. (mostly setting mux pins) and did not something as
    request for GPIO, may be this is done after the board init function?
    Can you give a hint to exec this request?

    Thanks in advance for your info.

    Kind Regards

    Teun Grinwis
  • There's a gpio_request API.  Here are a couple places it is discussed:

  • Thanks Brad, All set now.
  • Hello Biser,
    My post is answered, question how do I mark my post as "Answered" ?
    Kind Regards,
    Teun