pm_device_action_run behavior when multipile sensors are connected on same bus

I have an application in which there are two high speed sensors consider it as sensors 1 and sensor 2. Both sensors are connected over same SPI lines having different CS pins and power supply pins using different LDOs.

According to my application, in idle case when there is no requirement of reading data from the sensors, I am turning off their powers and suspend the SPI bus using Zephyr's PM APIs. I registred pm_action APIs for both the sensors in the respective drivers and I am using CONFIG_PM_DEVICE to manage device state using PM_DEVICE_ACTION_RESUME and PM_DEVICE_ACTION_SUSPEND.

In actual application, I will call the pm_device_action_run(dev, PM_DEVICE_ACTION_SUSPEND); to a sensor at a time, and this will suspend the SPI bus when it executed. In this case what should happen to the second sensor as the SPI bus is already suspeded.

What I observed is, when I call pm_device_action_run(dev, PM_DEVICE_ACTION_SUSPEND); from sensor 1 it suspend the interface and I can see the success return from pm_device_action_run. Now when application tried to suspend the sensor 2 using the same API then I observed that pm_action API of that sensor 2 is not executing which is registered in the driver. In short, if Sensor 1 is suspended then if I call pm_device_action_run it is not reaching to the driver pm_action API and I am not able to execute the Sensor 2 power suspend operations.

Is it something expected or I am handling such scenario differently. I am curious how the Zephyr is handling such scenarion as using common interface for multiple sensor is quite common and I believe Zephyr is alreayd handling it but I am not aware about it.

If someone can help here to understand me such usecase that would be helpful.

Parents
  • Hi Ankit_chauhan,

    What I observed is, when I call pm_device_action_run(dev, PM_DEVICE_ACTION_SUSPEND); from sensor 1 it suspend the interface and I can see the success return from pm_device_action_run. Now when application tried to suspend the sensor 2 using the same API then I observed that pm_action API of that sensor 2 is not executing which is registered in the driver. In short, if Sensor 1 is suspended then if I call pm_device_action_run it is not reaching to the driver pm_action API and I am not able to execute the Sensor 2 power suspend operations.

    What is the "dev" you are using when you call pm_device_action_run() for these two sensors?

    The behavior might also depends on the sensor's PM support a little. What sensors are you using here?

    Hieu

  • Hi Hieu,

    Thank you for the response. 

    I have two sensors, sensor 1 is the Biopotential and Bioimpedance AFE that measures ECG. And sensor 2 is the IMU sensor. Both sensors are connected on SPI2 that uses "nordic,nrf-spim" interface.

    The dev that I am using in the application is from const struct device *const dev = DEVICE_DT_GET_ANY(AFE); that is defined in the dts file inside SPI2 node.

Reply
  • Hi Hieu,

    Thank you for the response. 

    I have two sensors, sensor 1 is the Biopotential and Bioimpedance AFE that measures ECG. And sensor 2 is the IMU sensor. Both sensors are connected on SPI2 that uses "nordic,nrf-spim" interface.

    The dev that I am using in the application is from const struct device *const dev = DEVICE_DT_GET_ANY(AFE); that is defined in the dts file inside SPI2 node.

Children
  • Hi Ankit_chauhan,

    Could you please share the part number of the sensor, or, preferably, which driver files are being compiled?

    If you are unsure what driver files are used, please just share the "compatible" properties of the node in DeviceTree, and all relevant Kconfigs.

  • Hi Hieu,

    The ECG sensor that I am using is MAX30001 and the IMU sensor is LSM6DSL. Both are custom drives written by our own by taking reference from the sample drivers of Zephyr. Below is the device tree configuration that we are using in our dts. And I have also enabled "low-power-enable;" in the "spi2_sleep" dtsi configuration

    &spi2 {
    status = "okay";
    pinctrl-0 = <&spi2_default>;
    pinctrl-1 = <&spi2_sleep>;
    pinctrl-names = "default", "sleep";
    cs-gpios = <&gpio0 19 GPIO_ACTIVE_LOW>,
    <&gpio0 15 GPIO_ACTIVE_LOW>;


    max30001: max30001@0 {
    compatible = "maxim,max30001";
    irq-gpios = <&gpio0 23 GPIO_ACTIVE_LOW>;
    status = "okay";
    reg = <0>;
    spi-max-frequency = <DT_FREQ_M(8)>;
    };


    lsm6dsl: lsm6dsl@1 {
    irq-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
    compatible = "st,lsm6dsl";
    status = "okay";
    reg = <1>;
    spi-max-frequency = <DT_FREQ_M(8)>;
    };
    };

    We are not using any other configuration for these drivers except the trigger config. 

    CONFIG_LSM6DSL_TRIGGER=y
    CONFIG_MAX30001_TRIGGER=y
    We are not using device runtime power configuration for suspend and resume. Below are the configuration related to the power
    CONFIG_PM=y
    CONFIG_PM_DEVICE=y
  • Hi Ankit_chauhan,

    There should be a LSM6DSL available in the SDK. Why do you need a custom one?

    Could you please give me the implementation related to Power Management in the drivers?

  • Hieu,

    Yes, earlier we were using the Zephyr sample driver for LSM, but as per our application architecture requirement we need to read a FIFO using a single API and we do not have a sensor API to read the FIFO. We did not get appropreate results what we are looking for with the available Zephyr LSM driver. So. to full fill this requirement we creates a custom sensor API so that we can utilize the FIFO read directly instead of using conventional APIs.

    Below is the implementation of my PM action API

    #ifdef CONFIG_PM_DEVICE
    static int lsm6dsl_pm_action(const struct device *dev,
    								enum pm_device_action action)
    {
    	int ret = 0;
    	const struct lsm6dsl_config * const config = dev->config;
    	enum pm_device_state cur_state = PM_DEVICE_STATE_OFF;
    
    	switch(action) {
    		case PM_DEVICE_ACTION_SUSPEND:
    		case PM_DEVICE_ACTION_TURN_OFF:
    			/// Check the current status of bus 
                ret = pm_device_state_get(config->bus_cfg.spi.bus, &cur_state);
                if(ret<0) {
                    LOG_ERR("lsm6dsl pm_device_state_get failed : %d ", ret);
                } else {
                    LOG_DBG("lsm6dsl pm_device_state_get : %s. : %d", pm_device_state_str(cur_state), cur_state);
                }
    
    			LOG_INF("lsm6dsl Power OFF :: PM_DEVICE_ACTION_SUSPEND :: Bus :: %s", config->bus_cfg.spi.bus->name);
    
    			if(PM_DEVICE_STATE_ACTIVE == cur_state) {
    				ret = pm_device_action_run(config->bus_cfg.spi.bus, PM_DEVICE_ACTION_SUSPEND);
    				if(ret<0) {
    					LOG_ERR("lsm6dsl Driver Power Action failed : %d : PM_DEVICE_ACTION_SUSPEND.", ret);
    				} else {
    					LOG_INF("lsm6dsl Driver Power Action : PM_DEVICE_ACTION_SUSPEND.");
    				}
    			}
    
    			break;
    		case PM_DEVICE_ACTION_RESUME:
    		case PM_DEVICE_ACTION_TURN_ON:
    			ret = pm_device_state_get(config->bus_cfg.spi.bus, &cur_state);
                if(ret<0) {
                    LOG_ERR("lsm6dsl pm_device_state_get failed : %d ", ret);
                } else {
                    LOG_DBG("lsm6dsl pm_device_state_get : %s. : %d", pm_device_state_str(cur_state), cur_state);
                }
    
    			LOG_INF("lsm6dsl Power ON :: current status  :: %s", pm_device_state_str(cur_state));
    
    			if(PM_DEVICE_STATE_ACTIVE != cur_state) {
    				ret = pm_device_action_run(config->bus_cfg.spi.bus, PM_DEVICE_ACTION_RESUME);
    				if(ret<0) {
    					LOG_ERR("lsm6dsl Driver Power Action failed : %d : PM_DEVICE_ACTION_RESUME.", ret);
    				} else {
    					LOG_INF("lsm6dsl Driver Power Action : PM_DEVICE_ACTION_RESUME.");
    				}
    			}
    			
    			break;
    		default:
    			return -ENOTSUP;
    	}
    
    	return ret;
    }
    #endif /* CONFIG_PM_DEVICE */

    And I am using below APIs to execute above actions 

    #ifdef CONFIG_PM_DEVICE
        ret = pm_device_action_run(dev, PM_DEVICE_ACTION_SUSPEND);
    #endif /* CONFIG_PM_DEVICE */

    #ifdef CONFIG_PM_DEVICE
        ret = pm_device_action_run(dev, PM_DEVICE_ACTION_RESUME);
    #endif /* CONFIG_PM_DEVICE */
    Where the dev is 
    const struct device *const dev = DEVICE_DT_GET_ANY(st_lsm6dsl);
  • Hi Ankit_chauhan,

    Yes, this is what I suspected was happening, that the sensor driver handling of PM API also affects the SPI bus as a whole. I think that you see that this gives you the problem of suspending one sensor affecting the other as well.

    It is much better that the suspending of one sensor only affects itself and nothing else. Maybe it can signal that it is done/not done using the SPI bus with pm_runtime_device_get() and pm_device_runtime_put(), but it shouldn't suspend the whole bus.

    Please refer to our online course on Device Driver Development for more information: Lesson 7 - Device driver development - Nordic Developer Academy.

    Hieu

Related