UART Rx interrupt not enabled after PM_DEVICE_ACTION_RESUME

Hello,

I'm adding power management to a custom driver that talks to a sensor via uart2 of the nRF9151DK. But from my tests it seems that uart_irq_rx_enable()  has no effect after the driver resumes the uart device with pm_device_runtime_get().

The driver has three functions:

turn_on(): Runs pm_device_runtime_get() to resume uart operation and sets a gpio high to enable the sensor.

turn_off(): Runs pm_device_runtime_put() to suspend uart operation and set a gpio low to disable the sensor.

sample_fetch(): Sends a command to the sensor and waits for the rx buffer to have the expected response size in bytes, using a semaphore with a 1s timeout.

The pm_action callback function is:


static int sensor_pm_action(const struct device *dev,
                             enum pm_device_action action)
{
    const struct sensor_cfg *cfg = dev->config;
    struct gpio_dt_spec en_gpio = cfg->en_gpio;
    int ret = 0;

    switch (action)
    {
    case PM_DEVICE_ACTION_RESUME:
        LOG_INF("Resuming sensor");
        
        /* Re-initialize the sensor */
        ret = sensor_init(dev);
        break;

    case PM_DEVICE_ACTION_SUSPEND:
        LOG_INF("Suspending sensor");
        
        /* Disconnect GPIO */
        gpio_pin_configure((&en_gpio)->port, (&en_gpio)->pin, GPIO_INPUT | GPIO_DISCONNECTED);

        break;
    default:
        return -ENOTSUP;
    }

    return ret;
}

 The driver init function sets up the uart paramenters:

static int sensor_init(const struct device *dev)
{
    const struct sensor_cfg *cfg = dev->config;
    struct sensor_data *data = dev->data;
    struct gpio_dt_spec en_gpio = cfg->en_gpio;
    int ret = 0;

    LOG_DBG("Initializing sensor driver");

    if (!gpio_is_ready_dt(&en_gpio))
    {
        LOG_ERR("Sensor EN pin not ready");
        return -ENOSYS;
    }

    gpio_pin_configure((&en_gpio)->port, (&en_gpio)->pin, GPIO_OUTPUT);
    gpio_pin_set_dt(&en_gpio, 0);

    if (!device_is_ready(cfg->uart_dev))
    {
        LOG_ERR("UART not ready");
        return -ENOSYS;
    }

    uart_irq_rx_disable(cfg->uart_dev);
    uart_irq_tx_disable(cfg->uart_dev);

    ret = uart_configure(cfg->uart_dev, &uart_cfg_sensor);

    if (ret == -ENOSYS)
    {
        LOG_ERR("Unable to configure UART port");
        return -ENOSYS;
    }

    ret = uart_irq_callback_user_data_set(cfg->uart_dev, cfg->cb, (void *)dev);

    if (ret < 0)
    {
        if (ret == -ENOTSUP)
        {
            LOG_ERR("Interrupt-driven UART API support not enabled");
        }
        else if (ret == -ENOSYS)
        {
            LOG_ERR("UART device does not support interrupt-driven API");
        }
        else
        {
            LOG_ERR("Error setting UART callback: %d", ret);
        }
        return ret;
    }

    if (ret == -ENOSYS)
    {
        LOG_ERR("Unable to configure UART");
        return -ENOSYS;
    }

    data->rx_index = 0;
    data->tx_index = 0;

    k_sem_init(&data->tx_sem, 1, 1);
    k_sem_init(&data->rx_sem, 0, 1);

    return ret;
}

App overlay file:

&uart2 {
    status = "okay";
    current-speed = <4800>;
    pinctrl-0 = <&uart2_default>;
    pinctrl-1 = <&uart2_sleep>;
    pinctrl-names = "default", "sleep";
    zephyr,pm-device-runtime-auto;

    sensor_test: sensor {
        compatible = "sensor";
        en-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
        status = "okay";
        zephyr,pm-device-runtime-auto;
    };
};

&pinctrl {
    uart2_default: uart2_default {
        group1 {
            psels = <NRF_PSEL(UART_TX, 0, 24)>,
                    <NRF_PSEL(UART_RTS, 0, 23)>;
        };
        group2 {
            psels = <NRF_PSEL(UART_RX, 0, 25)>,
                    <NRF_PSEL(UART_CTS, 0, 17)>;
            bias-pull-up;
        };
    };

    uart2_sleep: uart2_sleep {
        group1 {
            psels = <NRF_PSEL(UART_TX, 0, 24)>,
                    <NRF_PSEL(UART_RX, 0, 25)>,
                    <NRF_PSEL(UART_RTS, 0, 23)>,
                    <NRF_PSEL(UART_CTS, 0, 17)>;
            low-power-enable;
        };
    };
};

And prj.conf file:

CONFIG_GPIO=y
CONFIG_LOG=y
CONFIG_SERIAL=y
CONFIG_CONSOLE=y

CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_SENSOR=y
CONFIG_CUSTOM_SENSOR_DRIVER=y
CONFIG_CUSTOM_SENSOR=y

CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y

CONFIG_TFM_SECURE_UART=n
CONFIG_TFM_LOG_LEVEL_SILENCE=y

If the  zephyr,pm-device-runtime-auto is removed from the uart node in devicetree, the driver gets the response form the sensor.

Any ideas of what is causing this? Thanks!

Best regards,

Lalo

Parents
  • Hi,

     

    If the  zephyr,pm-device-runtime-auto is removed from the uart node in devicetree, the driver gets the response form the sensor.

    If you remove this, then it works without any issues?

    I would recommend that you check the state, as shown in the docs if my understanding of the issue is correct:

    https://docs.zephyrproject.org/latest/services/pm/device_runtime.html

     

    Q1: Have you scoped the TXD and RXD lines to confirm the behavior?

    Q2: do you have any debug logs showing the issue?

    It seems that you are always running sensor_init(), meaning that the uart will be re-initialized each time. That will cause a ret != 0 for that function.

     

    Kind regards,

    Håkon

  • Hi Håkon, 

    If you remove this, then it works without any issues?

    Yes, sorry if I didn't give more details, I followed your sugestion and removed sensor_init() from the sensor_pm_action() callback.

    I'm still getting the same results, with this overlay I get an Rx timeout:

    &uart2 {
        status = "okay";
        current-speed = <4800>;
        pinctrl-0 = <&uart2_default>;
        pinctrl-1 = <&uart2_sleep>;
        pinctrl-names = "default", "sleep";
        zephyr,pm-device-runtime-auto;
    
        sensor_test: sensor {
            compatible = "sensor";
            en-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
            status = "okay";
            zephyr,pm-device-runtime-auto;
        };
    };

    -----------------------------------------------------------------------

    *** Booting nRF Connect SDK v3.0.2-89ba1294ac9b ***
    *** Using Zephyr OS v4.0.99-f791c49f492c ***
    [00:00:00.390,655] <inf> sensor_pm_test: Sensor Test Started
    [00:00:00.390,686] <inf> test_sensor: Resuming sensor
    [00:00:02.391,052] <err> test_sensor: Rx Timeout!
    [00:00:02.391,082] <err> sensor_pm_test: Could not fetch sample (-62)
    [00:00:02.391,113] <inf> test_sensor: Suspending sensor

    -----------------------------------------------------------------------

    If I remove the zephyr,pm-device-runtime-auto:

    &uart2 {
        status = "okay";
        current-speed = <4800>;
        pinctrl-0 = <&uart2_default>;
        pinctrl-1 = <&uart2_sleep>;
        pinctrl-names = "default", "sleep";
        
        sensor_test: sensor {
            compatible = "sensor";
            en-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
            status = "okay";
            zephyr,pm-device-runtime-auto;
        };
    };
     

    I get a valid response from the sensor:
    -----------------------------------------------------------------------
    *** Booting nRF Connect SDK v3.0.2-89ba1294ac9b ***
    *** Using Zephyr OS v4.0.99-f791c49f492c ***
    [00:00:00.414,825] <inf> sensor_pm_test: Sensor Test Started
    [00:00:00.414,855] <inf> test_sensor: Resuming sensor
    [00:00:01.488,067] <dbg> test_sensor: sensor_uart_isr: Sensor Resp>>
                                                                   01 03 0e 00 00 01 00 00 00 00 1e 00 00 00 00 00 |........ ........
                                                                   00 ec d7 00 00 00 00 00 00 00 00 00 00 00 00 00 |........ ........
    [00:00:01.488,128] <inf> test_sensor: Suspending sensor
    -----------------------------------------------------------------------
    For reference, this is the uart ISR handler function:
    static void sensor_uart_isr(const struct device *uart_dev, void *user_data)
    {
        const struct device *dev = user_data;
        struct sensor_data *data = dev->data;
    
        ARG_UNUSED(user_data);
    
        if (uart_dev == NULL)
        {
            return;
        }
    
        if (!uart_irq_update(uart_dev))
        {
            LOG_DBG("Unable to start processing interrupts");
            return;
        }
    
        if (uart_irq_rx_ready(uart_dev))
        {
            data->rx_index += uart_fifo_read(uart_dev, &data->rx_buffer[data->rx_index], RESP_SIZE - data->rx_index);
    
            if (data->rx_index == RESP_SIZE)
            {
                LOG_HEXDUMP_DBG(data->rx_buffer, sizeof(data->rx_buffer), "Sensor Resp>>");
                data->rx_index = 0;
                uart_irq_rx_disable(uart_dev);
                k_sem_give(&data->rx_sem);
            }
        }
    
        if (uart_irq_tx_ready(uart_dev))
        {
            data->tx_index +=
                uart_fifo_fill(uart_dev, &data->tx_buffer[data->tx_index],
                               CMD_SIZE - data->tx_index);
    
            if (data->tx_index == CMD_SIZE)
            {
                data->tx_index = 0;
                uart_irq_tx_disable(uart_dev);
                k_sem_give(&data->tx_sem);
            }
        }
    }
    And the sample_fetch function:

    static int sample_fetch(const struct device *dev)
    {
        struct sensor_data *data = dev->data;
        struct sensor_cfg *cfg = dev->config;
        int ret;
        const uint8_t cmd[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x07, 0x04, 0x08};
    
        memset(data->tx_buffer, '\0', sizeof(data->tx_buffer));
    
        for (size_t i = 0; i < sizeof(cmd); i++)
        {
            data->tx_buffer[i] = cmd[i];
        }
    
        data->rx_index = 0;
        data->tx_index = 0;
    
        ret = k_sem_take(&data->tx_sem, SERIAL_TIMEOUT);
        if (ret < 0)
        {
            LOG_ERR("Tx Timeout!");
            return -ETIME;
        }
    
        k_sem_reset(&data->rx_sem);
        uart_irq_tx_enable(cfg->uart_dev);
        uart_irq_rx_enable(cfg->uart_dev);
    
        ret = k_sem_take(&data->rx_sem, SERIAL_TIMEOUT);
    
        if (ret < 0)
        {
            LOG_ERR("Rx Timeout!");
            return -ETIME;
        }
    
        .....
        Process data...
        .....
    
        return 0;
    }
    I checked the Tx/Rx lines with an oscilloscope and found a strange behavior in the Rx timeout case (Tx-yellow, Rx-blue):
     
    This is the trace for the valid response:
     
     
Reply
  • Hi Håkon, 

    If you remove this, then it works without any issues?

    Yes, sorry if I didn't give more details, I followed your sugestion and removed sensor_init() from the sensor_pm_action() callback.

    I'm still getting the same results, with this overlay I get an Rx timeout:

    &uart2 {
        status = "okay";
        current-speed = <4800>;
        pinctrl-0 = <&uart2_default>;
        pinctrl-1 = <&uart2_sleep>;
        pinctrl-names = "default", "sleep";
        zephyr,pm-device-runtime-auto;
    
        sensor_test: sensor {
            compatible = "sensor";
            en-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
            status = "okay";
            zephyr,pm-device-runtime-auto;
        };
    };

    -----------------------------------------------------------------------

    *** Booting nRF Connect SDK v3.0.2-89ba1294ac9b ***
    *** Using Zephyr OS v4.0.99-f791c49f492c ***
    [00:00:00.390,655] <inf> sensor_pm_test: Sensor Test Started
    [00:00:00.390,686] <inf> test_sensor: Resuming sensor
    [00:00:02.391,052] <err> test_sensor: Rx Timeout!
    [00:00:02.391,082] <err> sensor_pm_test: Could not fetch sample (-62)
    [00:00:02.391,113] <inf> test_sensor: Suspending sensor

    -----------------------------------------------------------------------

    If I remove the zephyr,pm-device-runtime-auto:

    &uart2 {
        status = "okay";
        current-speed = <4800>;
        pinctrl-0 = <&uart2_default>;
        pinctrl-1 = <&uart2_sleep>;
        pinctrl-names = "default", "sleep";
        
        sensor_test: sensor {
            compatible = "sensor";
            en-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
            status = "okay";
            zephyr,pm-device-runtime-auto;
        };
    };
     

    I get a valid response from the sensor:
    -----------------------------------------------------------------------
    *** Booting nRF Connect SDK v3.0.2-89ba1294ac9b ***
    *** Using Zephyr OS v4.0.99-f791c49f492c ***
    [00:00:00.414,825] <inf> sensor_pm_test: Sensor Test Started
    [00:00:00.414,855] <inf> test_sensor: Resuming sensor
    [00:00:01.488,067] <dbg> test_sensor: sensor_uart_isr: Sensor Resp>>
                                                                   01 03 0e 00 00 01 00 00 00 00 1e 00 00 00 00 00 |........ ........
                                                                   00 ec d7 00 00 00 00 00 00 00 00 00 00 00 00 00 |........ ........
    [00:00:01.488,128] <inf> test_sensor: Suspending sensor
    -----------------------------------------------------------------------
    For reference, this is the uart ISR handler function:
    static void sensor_uart_isr(const struct device *uart_dev, void *user_data)
    {
        const struct device *dev = user_data;
        struct sensor_data *data = dev->data;
    
        ARG_UNUSED(user_data);
    
        if (uart_dev == NULL)
        {
            return;
        }
    
        if (!uart_irq_update(uart_dev))
        {
            LOG_DBG("Unable to start processing interrupts");
            return;
        }
    
        if (uart_irq_rx_ready(uart_dev))
        {
            data->rx_index += uart_fifo_read(uart_dev, &data->rx_buffer[data->rx_index], RESP_SIZE - data->rx_index);
    
            if (data->rx_index == RESP_SIZE)
            {
                LOG_HEXDUMP_DBG(data->rx_buffer, sizeof(data->rx_buffer), "Sensor Resp>>");
                data->rx_index = 0;
                uart_irq_rx_disable(uart_dev);
                k_sem_give(&data->rx_sem);
            }
        }
    
        if (uart_irq_tx_ready(uart_dev))
        {
            data->tx_index +=
                uart_fifo_fill(uart_dev, &data->tx_buffer[data->tx_index],
                               CMD_SIZE - data->tx_index);
    
            if (data->tx_index == CMD_SIZE)
            {
                data->tx_index = 0;
                uart_irq_tx_disable(uart_dev);
                k_sem_give(&data->tx_sem);
            }
        }
    }
    And the sample_fetch function:

    static int sample_fetch(const struct device *dev)
    {
        struct sensor_data *data = dev->data;
        struct sensor_cfg *cfg = dev->config;
        int ret;
        const uint8_t cmd[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x07, 0x04, 0x08};
    
        memset(data->tx_buffer, '\0', sizeof(data->tx_buffer));
    
        for (size_t i = 0; i < sizeof(cmd); i++)
        {
            data->tx_buffer[i] = cmd[i];
        }
    
        data->rx_index = 0;
        data->tx_index = 0;
    
        ret = k_sem_take(&data->tx_sem, SERIAL_TIMEOUT);
        if (ret < 0)
        {
            LOG_ERR("Tx Timeout!");
            return -ETIME;
        }
    
        k_sem_reset(&data->rx_sem);
        uart_irq_tx_enable(cfg->uart_dev);
        uart_irq_rx_enable(cfg->uart_dev);
    
        ret = k_sem_take(&data->rx_sem, SERIAL_TIMEOUT);
    
        if (ret < 0)
        {
            LOG_ERR("Rx Timeout!");
            return -ETIME;
        }
    
        .....
        Process data...
        .....
    
        return 0;
    }
    I checked the Tx/Rx lines with an oscilloscope and found a strange behavior in the Rx timeout case (Tx-yellow, Rx-blue):
     
    This is the trace for the valid response:
     
     
Children
Related