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.

  • 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

  • Hieu,

    Thanks for the reply.

    I got your point here, instead of suspedning an entire bus I need to focus of device runtime status and then suspend the device. I will take a look at the link that you shared. 

  • Hi Ankit_chauhan,

    Perfect. I will keep this case on hold then. Please feel free to follow-up or close it as you see fit.

    Hieu

  • Hi Hieu,

    Sorry for the late reply. I went through the dev training page you shared and I have implemented the device runtime power management support in my application.

    In my case its using SPIM driver of nordic spi_nrfx_spim.c. When I check the driver I found that in function "transceive" it first calls the pm_device_runtime_get(dev); and at the end of the operation it calls pm_device_runtime_put_async(dev, K_NO_WAIT); from "finalize_spi_transactio".

    From this implementation, it looks like the SPI bus toggles the interface on every communication we made with the peripheral. I confirm that as well by putting debug logs around these operations and observe the toggling of interfaces.

    With this behavior what I observe is, it causes a delay between the reads that I performed for both the peripherals MAX and LSM. This ultimately results into a data loss if I compare the performance of the device that does not have device runtime configuration.

    Do you have any other alternative for this, as the loss of a data is not something that we can go ahead with?

     
    Ankit.

Reply
  • Hi Hieu,

    Sorry for the late reply. I went through the dev training page you shared and I have implemented the device runtime power management support in my application.

    In my case its using SPIM driver of nordic spi_nrfx_spim.c. When I check the driver I found that in function "transceive" it first calls the pm_device_runtime_get(dev); and at the end of the operation it calls pm_device_runtime_put_async(dev, K_NO_WAIT); from "finalize_spi_transactio".

    From this implementation, it looks like the SPI bus toggles the interface on every communication we made with the peripheral. I confirm that as well by putting debug logs around these operations and observe the toggling of interfaces.

    With this behavior what I observe is, it causes a delay between the reads that I performed for both the peripherals MAX and LSM. This ultimately results into a data loss if I compare the performance of the device that does not have device runtime configuration.

    Do you have any other alternative for this, as the loss of a data is not something that we can go ahead with?

     
    Ankit.

Children
Related