Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i2c-sam0 sleeps waiting for interrupt #21092

Closed
sslupsky opened this issue Nov 30, 2019 · 4 comments
Closed

i2c-sam0 sleeps waiting for interrupt #21092

sslupsky opened this issue Nov 30, 2019 · 4 comments
Assignees
Labels
area: Drivers area: I2C bug The issue is a bug, or the PR is fixing a bug platform: Microchip SAM Microchip SAM Platform (formerly Atmel SAM) priority: low Low impact/importance bug Stale

Comments

@sslupsky
Copy link
Contributor

Describe the bug
This is likely more a question than a bug report. However, at this point I have not been able to determine what the correct course of action is to address this observation.

When CONFIG_SYS_POWER_MANAGEMENT and CONFIG_SYS_POWER_SLEEP_STATES are enabled, the i2c-sam0 driver can suffer corruption when the device enters a sleep state. This happens because the i2c-sam0 driver waits for a semaphore. During the wait, a sleep state can be entered where the i2c clock is turned off.

Here is where this issue manifests itself in the i2c-sam0 driver:

k_sem_take(&data->sem, K_FOREVER);

I believe this issue is related to issue #10524. In that issue @nashif indicates:

i2c and other drivers might require multithreading. this is documented now in the single thread configuration option.

and subsequently closed the issue. From the comment it is not clear what is referred to. I reviewed the CONFIG_MULTITHREADING documentation but there is no mention of how to address the pm issue with i2c.

To Reproduce
I have been testing this with my own SAMD21 board with an i2c peripheral. I believe any SAMD21 board (Adafruit M0 proto board) will reproduce the problem if the config options are set as described above and the following sleep states are defined:

void sys_set_power_state(enum power_states state)
{
	LOG_DBG("SoC entering power state %d", state);

	/* FIXME: When this function is entered the Kernel has disabled
	 * interrupts using BASEPRI register. This is incorrect as it prevents
	 * waking up from any interrupt which priority is not 0. Work around the
	 * issue and disable interrupts using PRIMASK register as recommended
	 * by ARM.
	 */

	/* Set PRIMASK */
	__disable_irq();
	/* Set BASEPRI to 0 */
	irq_unlock(0);

	switch (state) {
#ifdef CONFIG_SYS_POWER_SLEEP_STATES
#ifdef CONFIG_HAS_SYS_POWER_STATE_SLEEP_1
	case SYS_POWER_STATE_SLEEP_1:
        SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
        PM->SLEEP.reg = 2;
        __DSB();
        __WFI();
		break;
#endif /* CONFIG_HAS_SYS_POWER_STATE_SLEEP_1 */
#ifdef CONFIG_HAS_SYS_POWER_STATE_SLEEP_2
	case SYS_POWER_STATE_SLEEP_2:
        // Disable systick interrupt:  See https://www.avrfreaks.net/forum/samd21-samd21e16b-sporadically-locks-and-does-not-wake-standby-sleep-mode
        // If SysTick is not enabled then no need to mask the interrupt
#ifdef CONFIG_CORTEX_M_SYSTICK
        SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;
#endif
        SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
        __DSB();
        __WFI();
        // Re-enable systick interrupt if SysTick is enabled
#ifdef CONFIG_CORTEX_M_SYSTICK
        SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;	
#endif
		break;
#endif /* CONFIG_HAS_SYS_POWER_STATE_SLEEP_2 */
#ifdef CONFIG_HAS_SYS_POWER_STATE_SLEEP_3
	case SYS_POWER_STATE_SLEEP_3:
		break;

Expected behavior
System power sleep states that affect the i2c peripheral clock (sleep state 2 in my example above) should be avoided when an i2c master is waiting for an i2c bus event or transaction to complete.

Note, if a driver is configured to act as an i2c slave, it is possible to wake the cpu on an i2c address match without an internal clock gated on to the peripheral. So, in this instance, the peripheral clock could be gated off while waiting for the device to be addressed.

Impact
I2C is non functional during sleep.

@sslupsky sslupsky added the bug The issue is a bug, or the PR is fixing a bug label Nov 30, 2019
@jfischer-no jfischer-no added area: Drivers platform: Microchip SAM Microchip SAM Platform (formerly Atmel SAM) labels Dec 2, 2019
@dleach02 dleach02 added the priority: low Low impact/importance bug label Dec 3, 2019
@sslupsky
Copy link
Contributor Author

sslupsky commented Dec 4, 2019

I have another observation regarding the sam0 i2c driver. It appears that the i2c bus can be left in a non idle state. This is a problem because the SCL and/or SDA lines can be held low by the sam0 during and after sleep resulting in significant current (>500uA) due to the pull up resistors on these lines.

I have not isolated what is causing this issue but I have confirmed using an oscilloscope that the SCL and/or SDA lines remain low after an i2c transaction.

It is possible this issue is not related to the topic of this issue and should be a recorded as a separate issue.

sslupsky added a commit to sslupsky/zephyr that referenced this issue Jul 10, 2020
Optimizations:
When using system power management, the bus transactions
can be interrupted by low power modes which causes excessive
power consumption and can cause the transactions to terminate
before completed. Further, other interrupts can cause bus transaction
anomalies.

When using the RESTART flag, the MB and SB bits must be cleared.

This commit prevents interruption of the bus during low power modes and
correctly clears the MB or SB flags when a bus transaction is restarted.
Also fixes an initialization problem with the INACTOUT timer and ensures
the LOWTOUT is properly configured when using i2c_configure().

The GCLK_SERCOM_SLOW clock is used to time the i2c
time-outs and must be configured to use a 32KHz oscillator.
See 28.6.3.1

This commit configures GCLK4 to 32768 Hz

Fixes zephyrproject-rtos#21711, zephyrproject-rtos#21549, zephyrproject-rtos#21233, zephyrproject-rtos#21232, zephyrproject-rtos#21114, zephyrproject-rtos#21092

A problem can arise when back to back transactions occur before
the STOP transaction is complete.

This commit also introduces a busy wait for the STOP to complete
with an timeout in the event there is a bus failure.

Signed-off-by: Steven Slupsky <sslupsky@gmail.com>
sslupsky added a commit to sslupsky/zephyr that referenced this issue Jul 10, 2020
The driver issues a READREQ for every access to the COUNT
register which causes 180us of delay.  This is a large
delay that causes several problems with other systems
including i2c.

This commit uses continuous read requests (RCONT) to
optimize accessing the COUNT register.  This reduces (but does not
eliminate) the time spent synchronizing the COUNT register.
This commit also prevent interrupts from corrupting COUNT and COMP
register access.

The kernel does not like it when the RTC does not tick
after waking from sleep.  RCONT enables continuously syncing the
COUNT register but after sleep, we need to wait for a new read
request to complete.  However, the SYNCBUSY flag is not set
when RCONT is enabled so we cannot use that to do the sync.
So, we wait for the RTC COUNT register to begin ticking again by
reading the COUNT register.

We should wait a minimum of the TICK_THRESHOLD which is the amount
of time it takes to sync the register.

Fixes zephyrproject-rtos#21549, zephyrproject-rtos#21114, zephyrproject-rtos#21092

Fix comment typo

The worst case update time after sleep requires 2 sync busy
synchronizations.

This commit updates the TICK_THRESHOLD to reflect the
additional time required for the additional sync busy.

Fix clock initialisation conflict.
Fix whitespace

mnkp recommended that a separate commit be created for the
system power management.  This commit separates the
power_samd2x.c into a separate commit.

Signed-off-by: Steven Slupsky <sslupsky@gmail.com>
@github-actions
Copy link

This issue has been marked as stale because it has been open (more than) 60 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this issue will automatically be closed in 14 days. Note, that you can always re-open a closed issue at any time.

@carlescufi
Copy link
Member

@sslupsky is this still an issue? Otherwise please close it.

@sslupsky
Copy link
Contributor Author

sslupsky commented Sep 8, 2020

@carlescufi I submitted a PR quite a while ago that addresses this. The PR hasn't been accepted yet so I left this open.

I'll close it.

@sslupsky sslupsky closed this as completed Sep 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Drivers area: I2C bug The issue is a bug, or the PR is fixing a bug platform: Microchip SAM Microchip SAM Platform (formerly Atmel SAM) priority: low Low impact/importance bug Stale
Projects
None yet
7 participants