Repurposing I2C Pins without Device PM

I'm working to write a Zephyr-compatible driver for the LP5814 on a board with a nRF5340. The LP5814 is an LED controller that receives commands over an I2C interface. There's a feature of the LP5814 that is proving to be difficult to implement because of how Zephyr controls access to the I2C bus.

The feature in question is the LP5814's low power mode. Entering it requires a simple I2C transaction, but leaving it involves a special sequence of edges on the SDA and SCL pins that belong to the I2C bus. See the timing diagram:

There's an alternate way to enter and exit low power mode, but it looks like it could be vulnerable to being activated with I2C traffic meant for other devices.

Multiple threads in my application access I2C devices. They go through the usual Zephyr-nrfx glue code, which involves a synchronization object so that only one thread can do a transaction at a time. Unfortunately, there isn't a way to acquire this lock without also enabling the I2C peripheral. It would be desired to acquire the lock while keeping the I2C peripheral disabled so that no other I2C-driven device driver could interrupt the wakeup procedure of the LP5814.

We're currently on Zephyr 3.4, so I looked ahead to newer releases to see if such functionality was added. I see that an "exclusive access" pair of functions was added in Zephyr's PR 86721, but it still uses the Device Power Manager get/put functions to drive pinctrl. If I'm not mistaken, I can't finesse pinctrl into respecting the GPIO configuration required for the LP5814 wakeup procedure.

github.com/.../86721

Currently, my work-around is to access the nrfx driver directly so I can shut off the I2C peripheral and reconfigure the pins to my desires, restoring the configurations and reactivating the I2C peripheral when finished. This workaround has to be wrapped in something like a scheduler disable, which feels awful.

I plan to make my application request all I2C operations through a new layer of firmware with a lock that doesn't interact with the Device Power Manager. In-between my main application logic and device drivers are HALs. I will make those HALs use the new layer so the lock is respected and so that I don't have to edit any of the drivers that comes with Zephyr or NCS. I'm worried that System Work Queue work from those drivers will not be able to respect the new design, though.

Is there something I seem to be missing? Is Zephyr/NCS just not really equipped to handle an I2C device that needs the SDA and SCL pins to be repurposed on occasion?

Parents
  • Hi,

    The best way (in my opinion) is to use PM_DEVICE to quickly suspend the I2C, use the pins as GPIOs, and resume the I2C device.

    I quickly made a minimal application to show how to implement it : test364177.zip

    This example application does not contain any error handling, please be sure to add proper error handling to it. I just wanted to go straight to the point, so I did not put any.

    I used a nRF54LM20DK and built the app with NCS v3.4.1

    And here is the result that I got:

    Best regards,

    Simon

Reply
  • Hi,

    The best way (in my opinion) is to use PM_DEVICE to quickly suspend the I2C, use the pins as GPIOs, and resume the I2C device.

    I quickly made a minimal application to show how to implement it : test364177.zip

    This example application does not contain any error handling, please be sure to add proper error handling to it. I just wanted to go straight to the point, so I did not put any.

    I used a nRF54LM20DK and built the app with NCS v3.4.1

    And here is the result that I got:

    Best regards,

    Simon

Children
  • Thanks for that sample application, Simon.

    This closely models my workaround, but is this reliable when I2C transactions can be initiated from more than one thread? Looking at the source for Device Power Manager, I don't see anything that allows a caller to wait for the get/put usage counter to become empty, then block access so that I could do the suspend action and subsequent GPIO operations.

    Consider this example.

    Thread A communicates with the LP5814. Thread B communicates with another device on the same I2C bus. The Device PM currently has the I2C bus in the suspended state. Both threads are not doing anything.

    - Thread A begins to wake the LP5814.
    - Halfway through the LP5814 wakeup procedure, Thread B is scheduled to run. It begins an I2C transaction. The Device PM enables the I2C bus, which overrides the GPIO configurations. The wakeup procedure stops.
    - The I2C transaction finishes, and control returns to Thread A. It resumes the GPIO operations, but the GPIO configurations are likely not correct, and since the procedure was interrupted with I2C traffic, the wakeup will likely not work anyway.

    This is why I mentioned that I probably have to wrap the wakeup procedure with a scheduler lock or something similar.

Related