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
