Up "I2C bus lock up" 作成: 2021-04-10
更新: 2021-04-10


    TomE : Having the I2C bus lock up is a 35 year old "well known secret" を引用:
    2017-02-28
    TomE
    Specialist

    Having the I2C bus lock up is a 35 year old "well known secret".

    The easiest way to cause this problem is to reset the main micro part way through a read transfer.
    The Slave is legitimately driving data onto the bus and won't let go until it is clocked out.
    So all I2C master chips should start by sending a bunch of clocks.
    None do.
    This big fat failure mode was always part of the I2C bus design, and never really addressed or fixed.

    There should be a whole section on this problem in the Wikipedia I2C page.
    There isn't.
    What there is on that page is a link in the "External links" section to the "Official I2C Specs", which is here:

    https://en.wikipedia.org/wiki/I%C2%B2C#Limitations

    http://www.nxp.com/documents/user_manual/UM10204.pdf

    If you read section "3.1.16 Bus Clear", it tells you to assert the "Reset Pins" of all the I2C devices.
    The whole point of I2C is to minimise the number of bus pins, so almost no devices have Reset pins.
    But then it gives the advice that "everybody knows", which is:

    If the data line (SDA) is stuck LOW, the master should send nine clock pulses.
    The device that held the bus LOW should release it sometime within those nine clocks.

    Here's the big problem.
    There's no PORTABLE or GENERIC way to do that.
    It is really simple to add this to a "bare-metal" system, but trying to add it to Linux requires all of the I2C drivers to have code added and all "platform layers" changed to support this as well.
    To do that would take a decade or more.
    Luckily this problem has been known for at least THIRTY FIVE YEARS, so there have been plenty of decades for this to be fixed.

    Which has been done (at last).
    It only took THIRTY ONE YEARS to get this fixed, in Kernel 3.10 in 2013.
    Here's the code in the current/latest drivers to reset the I2C bus.
    Look for the functions starting at line 733 under the comment "i2c bus recovery routines", especially "i2c_generic_recovery()".

    http://lxr.free-electrons.com/source/drivers/i2c/i2c-core.c

    But you're using 3.14, which is quite old.
    Good news, this feature has been in there since 3.10.
    As long as it is supported by the "platform layer", meaning that someone at NXP or elsewhere has written the support functions for this some time in the last four years.

    Which is unlikely.
    Google for "i2c_bus_recovery_info" finds this patch adding support for i.MX, but it only happened in August 2016:

    https://patchwork.kernel.org/patch/9291261/

    So this facility was added in Linux 3.10, but NXP didn't add it to their driver until 4.4.
    It came in between these two:

    http://lxr.free-electrons.com/source/drivers/i2c/busses/i2c-imx.c?v=4.3

    http://lxr.free-electrons.com/source/drivers/i2c/busses/i2c-imx.c?v=4.4

    So one solution is to try to run 4.4 or later.
    Which might work for you or it might not.

    The proper "workaround" is to add "i2c_bus_recovery_info" to the Platform.
    That's the patch/workaround you really need for this.

    You can either wait for a fix from your supplier or you can fix it yourself.
    I've been adding these fixes manually to products for as long as I can remember.
    If I'd have waited for the supplier to fix it, I'd have been waiting at least 15 years, maybe more.

    Basically, forget messing around with the I2C controller.
    Just reprogram the I2C Clock pins as a GPIO, and toggle it on and off (at 400kHz or so) nine times.
    Just add that to your low-level Linux startup code before the ports have been reprogrammed to be I2C,
    or even better,
    add some simple code to your Bootstrap (RedBoot, U-Boot or whatever) to do that for you.

    I find I've answered this one before back in 2012;

    https://community.nxp.com/thread/116562

    Searching these forums for "i2c lockup" would have found you four other posts on this subject.