High Current Consumption when UART is configured with disable-rx flag and zephyr,pm-device-runtime-auto

Hello,

We are currently using the nrf52840 for development using Zephyr v3.7.99-ncs1.

We want to use the UART in low power mode and therefore, we have used the zephyr,pm-device-runtime-auto flag in the DTS uart configuration. Also, we want to have the RX pin of the UART disabled. However, we see increased current consumption in the following scenario.

- When the UART is configured in the .dts file without the disable-rx flag, the current consumption of the device is 0.6mA.

- When UART is configured including the disable-rx flag, the current consumption is 1.5mA.

Therefore, it seems that using the disable-rx flag results in the UART not going into low power mode. 

- Our UART RX pin has an external pull up resistor as you can see in the image attached.

I provide the pinctrl.dtsi UART configuration and the .dts file UART configuration.

.dtsi

&pinctrl {

	uart0_default: uart0_default {
		group1 {
			psels = <NRF_PSEL(UART_TX, 1, 0)>;
		};
		group2 {
			psels = <NRF_PSEL(UART_RX, 0, 24)>;
			bias-pull-up;
		};
	};

	uart0_sleep: uart0_sleep {
		group1 {
			psels = <NRF_PSEL(UART_TX, 1, 0)>,
				    <NRF_PSEL(UART_RX, 0, 24)>;
			low-power-enable;
		};
	};
};

.dts configuration

&uart0 {
	compatible = "nordic,nrf-uarte";
	status = "okay";
	current-speed = <115200>;
	pinctrl-0 = <&uart0_default>;
	pinctrl-1 = <&uart0_sleep>;
	pinctrl-names = "default", "sleep";
	zephyr,pm-device-runtime-auto;
	disable-rx;
};

Is there a specific reasoning or configuration we need to follow to be in low power mode and have the UART RX disabled at the same time?

Parents
  • It seems like the disable-rx is not doing what we expect it to do.

    Without disable-rx, the driver seems to leave the channel running 
    With disable-rx, the driver never queues RX so I think it never calls the power management plugs to shut down the hardware.

    I think the fix is here is to let the driver power off itself and then explicity turn off RX in your firmware. A template code can be like this.

    static const struct device *uart0 = DEVICE_DT_GET(DT_NODELABEL(uart0));
    
    void main(void)
    {
        ...
        ...
        
        pm_device_runtime_enable(uart0);
    
        /* 
         * when you want to disable rx path
         */
        uart_rx_disable(uart0);
        pm_device_action_run(uart0, PM_DEVICE_ACTION_SUSPEND);
    
        ...
        ...
    }
    

    leave zephyr,pm-device-runtime-auto; as is in your devicetree

  • The main problem we are dealing with is that without the use of the disable-rx flag the software crashes at some point, waiting indefinitely in the while loop in the uart_nrfx_uarte.c file, in the uarte_pm_suspend() function.

    If we use the disable-rx flag, the code is not stuck anymore in the following while loop but the current consumption is really high. 

    while (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXTO)) {
    	/* Busy wait for event to register */
    	Z_SPIN_DELAY(2);
    }


    static void uarte_pm_suspend(const struct device *dev)
    {
    	NRF_UARTE_Type *uarte = get_uarte_instance(dev);
    	const struct uarte_nrfx_config *cfg = dev->config;
    	struct uarte_nrfx_data *data = dev->data;
    
    	(void)data;
    #ifdef UARTE_ANY_ASYNC
    	if (data->async) {
    		/* Entering inactive state requires device to be no
    		 * active asynchronous calls.
    		 */
    		__ASSERT_NO_MSG(!data->async->rx.enabled);
    		__ASSERT_NO_MSG(!data->async->tx.len);
    		if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
    			/* If runtime PM is enabled then reference counting ensures that
    			 * suspend will not occur when TX is active.
    			 */
    			__ASSERT_NO_MSG(nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED));
    		} else {
    			wait_for_tx_stopped(dev);
    		}
    
    #if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX)
    		if (data->async && HW_RX_COUNTING_ENABLED(cfg)) {
    			nrfx_timer_disable(&cfg->timer);
    			/* Timer/counter value is reset when disabled. */
    			data->async->rx.total_byte_cnt = 0;
    			data->async->rx.total_user_byte_cnt = 0;
    		}
    #endif
    	} else if (IS_ENABLED(UARTE_ANY_NONE_ASYNC))
    #endif
    	{
    		if (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXSTARTED)) {
    #if defined(UARTE_INTERRUPT_DRIVEN) && defined(CONFIG_PM_DEVICE)
    			if (data->int_driven) {
    				data->int_driven->rx_irq_enabled =
    					nrf_uarte_int_enable_check(uarte, NRF_UARTE_INT_ENDRX_MASK);
    				if (data->int_driven->rx_irq_enabled) {
    					nrf_uarte_int_disable(uarte, NRF_UARTE_INT_ENDRX_MASK);
    				}
    			}
    #endif
    			nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPRX);
    			while (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXTO)) {
    				/* Busy wait for event to register */
    				Z_SPIN_DELAY(2);
    			}
    			nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED);
    			nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXTO);
    			nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX);
    			nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ERROR);
    		}
    
    		wait_for_tx_stopped(dev);
    	}
    
    #ifdef CONFIG_SOC_NRF54H20_GPD
    	nrf_gpd_retain_pins_set(cfg->pcfg, true);
    #endif
    
    	nrf_uarte_disable(uarte);
    
    	(void)pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_SLEEP);
    }

Reply
  • The main problem we are dealing with is that without the use of the disable-rx flag the software crashes at some point, waiting indefinitely in the while loop in the uart_nrfx_uarte.c file, in the uarte_pm_suspend() function.

    If we use the disable-rx flag, the code is not stuck anymore in the following while loop but the current consumption is really high. 

    while (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXTO)) {
    	/* Busy wait for event to register */
    	Z_SPIN_DELAY(2);
    }


    static void uarte_pm_suspend(const struct device *dev)
    {
    	NRF_UARTE_Type *uarte = get_uarte_instance(dev);
    	const struct uarte_nrfx_config *cfg = dev->config;
    	struct uarte_nrfx_data *data = dev->data;
    
    	(void)data;
    #ifdef UARTE_ANY_ASYNC
    	if (data->async) {
    		/* Entering inactive state requires device to be no
    		 * active asynchronous calls.
    		 */
    		__ASSERT_NO_MSG(!data->async->rx.enabled);
    		__ASSERT_NO_MSG(!data->async->tx.len);
    		if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
    			/* If runtime PM is enabled then reference counting ensures that
    			 * suspend will not occur when TX is active.
    			 */
    			__ASSERT_NO_MSG(nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED));
    		} else {
    			wait_for_tx_stopped(dev);
    		}
    
    #if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX)
    		if (data->async && HW_RX_COUNTING_ENABLED(cfg)) {
    			nrfx_timer_disable(&cfg->timer);
    			/* Timer/counter value is reset when disabled. */
    			data->async->rx.total_byte_cnt = 0;
    			data->async->rx.total_user_byte_cnt = 0;
    		}
    #endif
    	} else if (IS_ENABLED(UARTE_ANY_NONE_ASYNC))
    #endif
    	{
    		if (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXSTARTED)) {
    #if defined(UARTE_INTERRUPT_DRIVEN) && defined(CONFIG_PM_DEVICE)
    			if (data->int_driven) {
    				data->int_driven->rx_irq_enabled =
    					nrf_uarte_int_enable_check(uarte, NRF_UARTE_INT_ENDRX_MASK);
    				if (data->int_driven->rx_irq_enabled) {
    					nrf_uarte_int_disable(uarte, NRF_UARTE_INT_ENDRX_MASK);
    				}
    			}
    #endif
    			nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPRX);
    			while (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXTO)) {
    				/* Busy wait for event to register */
    				Z_SPIN_DELAY(2);
    			}
    			nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED);
    			nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXTO);
    			nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX);
    			nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ERROR);
    		}
    
    		wait_for_tx_stopped(dev);
    	}
    
    #ifdef CONFIG_SOC_NRF54H20_GPD
    	nrf_gpd_retain_pins_set(cfg->pcfg, true);
    #endif
    
    	nrf_uarte_disable(uarte);
    
    	(void)pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_SLEEP);
    }

Children
No Data
Related