Topics

RFC [2/3] - SoC, CPU LPS, Tickless idle and Device power management


Thomas, Ramesh
 

***These are implemented and under review***
https://gerrit.zephyrproject.org/r/#/c/532/
https://gerrit.zephyrproject.org/r/#/c/528/
https://gerrit.zephyrproject.org/r/#/c/525/

Device driver power management hook infrastructure
------------------------------------------------------
When the _sys_soc_suspend() and _sys_soc_resume() hook functions are
called, the PM service app may want to do some device specific
operations. Zephyr kernel provides this infrastructure to enable device
drivers to define suspend and resume hook functions that can be called
by the PM service app.

Following are the goals for providing this infrastructure:
1. A framework for drivers to implement generic suspend/resume hook
functions.
2. Provide means for PM app service to call the suspend and resume
functions of all devices in the system. This is necessary especially to
do suspend and resume operations on system devices like the APIC device
which do not export device binding interface.
3. Since not all devices need to implement suspend and resume hook
functions, provide a no-op function by default for such devices. By
default all devices would be setup with the no-op function for their
hooks. The device drivers who need specific handling would assign the
addresses of the functions they implement to these hooks during
initialization.

Non-goals:
The implementation of the hook functions by the device drivers is up to
each driver, the integrator and the system power management policies.

All device power management infrastructure implementations is enclosed
inside CONFIG_DEVICE_POWER flag.

The hook functions for the PM service app:
a) int device_suspend(struct device *port)
param port:
Device structure of the driver instance

return:
DEV_OK for success, error code otherwise

Details - calls the suspend hook of the device associated with the
device structure parameter.

b) int device_resume(struct device *port)
param port:
Device structure of the driver instance

return:
DEV_OK for success, error code otherwise

Details - calls the resume hook of the device associated with the device
structure parameter.

c) void device_suspend_all(bool system_devices)
param system_devices
Boolean true for only the system devices; else all devices

Details:
Kernel holds a list of all devices in the system. This function will
iterate through that list and call the suspend hook function of each
device in the list. It will do this in the reverse order in which the
devices were initialized.

If the <system_devices> parameter is false then it would call all
devices. If it is true then it would call the suspend/resume of only
system devices. This parameter is provided because it is mandatory for
the PM service app to call this function for system devices. This is
because it cannot bind to them and call their suspend/resume functions
directly. For non-system devices, the PM service app can bind to thm
and has the flexibility to call whichever device it requires to call and
in the order it chooses to. If it chooses to call the suspend/resume
functions of non-system devices individually, then it would call this
function passing true for the <system_devices> parameter.

d) void device_resume_all(bool system_devices)
param system_devices
Boolean true for only the system devices; else all devices

Details:
The description is same as for device_suspend_all() except, this would
call the resume hook functions of devices and they would be called in
the same order as the devices were initialized.

API for device drivers:
Structure storing the hook functions. This structure is stored in the
device's "device" structure.
struct device_power_ops {
device_op_t suspend;
device_op_t resume;
}

The NO-OP function:
int device_pm_noop(struct device *unused)
return - always returns DEV_OK.

Details:
No-op function that does nothing and returns DEV_OK immediately. This
can be used to initialize hooks for which the device has no operation to do.

The hooks inside device_power_ops structure are by default initialized
with this function by the kernel. Device drivers which have specific
operations for the hooks would overwrite with a suspend and resume
functions during driver initialization.

a) void device_power_ops_configure(struct device *port,
device_op_t suspend_fn, device_op_t resume_fn)
param port
- device structure of driver instance
param suspend_fn
- Address of suspend function
param resume_fn
- Address of resume function

Details:
Initializes the hook functions in device_power_ops with the functions
passed as parameters. This function should be called by devices during
the time of initialization if they have a specific suspend or resume
function implementation. Drivers can also pass the no-op function as
parameter for any of the hooks it does not have an operation. If it
does not have any operation for both suspend and resume then it need not
call this function at all. This is because the hooks for the devices are
by default initialized with the no-op function by the kernel.


Dirk Brandewie <dirk.j.brandewie@...>
 

On 02/29/2016 01:59 AM, Thomas, Ramesh wrote:
***These are implemented and under review***
https://gerrit.zephyrproject.org/r/#/c/532/
https://gerrit.zephyrproject.org/r/#/c/528/
https://gerrit.zephyrproject.org/r/#/c/525/

Device driver power management hook infrastructure
------------------------------------------------------
When the _sys_soc_suspend() and _sys_soc_resume() hook functions are
called, the PM service app may want to do some device specific
operations. Zephyr kernel provides this infrastructure to enable device
drivers to define suspend and resume hook functions that can be called
by the PM service app.
There is a basic disconnects between my original RFC and this one.

In mine all drivers are required to provide an implementation of
suspend/resume even if it is null. All the information for the system
to interact with the driver is contained in the device structure.

The new version of DEVICE_INIT() is there to keep everything compiling
while *all* the drivers are updated to satisfy the requirement to
support suspend/resume. Once that is done DEVICE_INIT_PM() turns
into DEVICE_INIT() with a tree wide sed script.

Moving *all* the drivers will be moderately painful but now is the
right time to do it before we get a bunch more drivers added to
the tree.

To be honest if I had thought about it more when I was defining
the device model we would no be here :-(

--Dirk


Following are the goals for providing this infrastructure:
1. A framework for drivers to implement generic suspend/resume hook
functions.
2. Provide means for PM app service to call the suspend and resume
functions of all devices in the system. This is necessary especially to
do suspend and resume operations on system devices like the APIC device
which do not export device binding interface.
3. Since not all devices need to implement suspend and resume hook
functions, provide a no-op function by default for such devices. By
default all devices would be setup with the no-op function for their
hooks. The device drivers who need specific handling would assign the
addresses of the functions they implement to these hooks during
initialization.

Non-goals:
The implementation of the hook functions by the device drivers is up to
each driver, the integrator and the system power management policies.

All device power management infrastructure implementations is enclosed
inside CONFIG_DEVICE_POWER flag.

The hook functions for the PM service app:
a) int device_suspend(struct device *port)
param port:
Device structure of the driver instance

return:
DEV_OK for success, error code otherwise

Details - calls the suspend hook of the device associated with the
device structure parameter.

b) int device_resume(struct device *port)
param port:
Device structure of the driver instance

return:
DEV_OK for success, error code otherwise

Details - calls the resume hook of the device associated with the device
structure parameter.

c) void device_suspend_all(bool system_devices)
param system_devices
Boolean true for only the system devices; else all devices

Details:
Kernel holds a list of all devices in the system. This function will
iterate through that list and call the suspend hook function of each
device in the list. It will do this in the reverse order in which the
devices were initialized.

If the <system_devices> parameter is false then it would call all
devices. If it is true then it would call the suspend/resume of only
system devices. This parameter is provided because it is mandatory for
the PM service app to call this function for system devices. This is
because it cannot bind to them and call their suspend/resume functions
directly. For non-system devices, the PM service app can bind to thm
and has the flexibility to call whichever device it requires to call and
in the order it chooses to. If it chooses to call the suspend/resume
functions of non-system devices individually, then it would call this
function passing true for the <system_devices> parameter.

d) void device_resume_all(bool system_devices)
param system_devices
Boolean true for only the system devices; else all devices

Details:
The description is same as for device_suspend_all() except, this would
call the resume hook functions of devices and they would be called in
the same order as the devices were initialized.

API for device drivers:
Structure storing the hook functions. This structure is stored in the
device's "device" structure.
struct device_power_ops {
device_op_t suspend;
device_op_t resume;
}

The NO-OP function:
int device_pm_noop(struct device *unused)
return - always returns DEV_OK.

Details:
No-op function that does nothing and returns DEV_OK immediately. This
can be used to initialize hooks for which the device has no operation to do.

The hooks inside device_power_ops structure are by default initialized
with this function by the kernel. Device drivers which have specific
operations for the hooks would overwrite with a suspend and resume
functions during driver initialization.

a) void device_power_ops_configure(struct device *port,
device_op_t suspend_fn, device_op_t resume_fn)
param port
- device structure of driver instance
param suspend_fn
- Address of suspend function
param resume_fn
- Address of resume function

Details:
Initializes the hook functions in device_power_ops with the functions
passed as parameters. This function should be called by devices during
the time of initialization if they have a specific suspend or resume
function implementation. Drivers can also pass the no-op function as
parameter for any of the hooks it does not have an operation. If it
does not have any operation for both suspend and resume then it need not
call this function at all. This is because the hooks for the devices are
by default initialized with the no-op function by the kernel.


Thomas, Ramesh
 

On Tue, 2016-03-01 at 11:03 -0800, Dirk Brandewie wrote:

On 02/29/2016 01:59 AM, Thomas, Ramesh wrote:
***These are implemented and under review***
https://gerrit.zephyrproject.org/r/#/c/532/
https://gerrit.zephyrproject.org/r/#/c/528/
https://gerrit.zephyrproject.org/r/#/c/525/

Device driver power management hook infrastructure
------------------------------------------------------
When the _sys_soc_suspend() and _sys_soc_resume() hook functions are
called, the PM service app may want to do some device specific
operations. Zephyr kernel provides this infrastructure to enable
device
drivers to define suspend and resume hook functions that can be
called
by the PM service app.
There is a basic disconnects between my original RFC and this one.

In mine all drivers are required to provide an implementation of
suspend/resume even if it is null. All the information for the system
to interact with the driver is contained in the device structure.

The new version of DEVICE_INIT() is there to keep everything compiling
while *all* the drivers are updated to satisfy the requirement to
support suspend/resume. Once that is done DEVICE_INIT_PM() turns
into DEVICE_INIT() with a tree wide sed script.

Moving *all* the drivers will be moderately painful but now is the
right time to do it before we get a bunch more drivers added to
the tree.
In this RFC I tried to keep the main ideas and feedbacks from the
original RFCs but made some modifications as I encountered issues during
implementation. These should not impact the general goals of the
changes. Listed below are the differences which can be reviewed again if
necessary.

Way device_ops structure gets initialized changed.

1. (Original RFC) Modify DEVICE_INIT call of all drivers in the system
and add a suspend_fn and resume_fn parameter to it. The macro would
statically create the device_ops structure and initialize it with the
passed suspend_fn and resume_fn. Caller of DEVICE_INIT can pass the
address of a "null" function if they do not have any operation for
either suspend or resume.

2. (This RFC) DEVICE_INIT() macro would create the device_ops structure
same as option #1 but would by default initialize it with no-op
functions for suspend and resume. So this DEVICE_INIT macro API params
need not be changed. Drivers which need any operation for
suspend/resume, would at run time, update the device_ops with their
suspend/resume function using device_power_ops_configure(suspend_fn,
resume_fn).

The "null" function referred to in #1 is same as the "no-op" function in
#2 except for the name change.

I picked #2 for the implementation because of the following reasons.
1. No need to modify DEVICE_INIT macros of all drivers (I greped 75+)
2. Scalable because if device_ops needs to be modified later to include
more ops, remove ops or add other fields, then we do not need to modify
all drivers calling DEVICE_INIT()

(I figured this only when I was about to modify the drivers and missed
it during original RFC discussion)

Let us review the above options and do whatever is appropriate. I have
no problem switching to any of them.

Also please review the other change from the original RFC -
device_suspend_all/device_resume_all now takes a parameter
system_devices. This gives an option to use this function only for
system devices if the PM app choses to call other devices suspend/resume
directly.

Let us review that also and if the parameter is not required then we can
remove it.


To be honest if I had thought about it more when I was defining
the device model we would no be here :-(

--Dirk


Following are the goals for providing this infrastructure:
1. A framework for drivers to implement generic suspend/resume hook
functions.
2. Provide means for PM app service to call the suspend and resume
functions of all devices in the system. This is necessary especially
to
do suspend and resume operations on system devices like the APIC
device
which do not export device binding interface.
3. Since not all devices need to implement suspend and resume hook
functions, provide a no-op function by default for such devices. By
default all devices would be setup with the no-op function for their
hooks. The device drivers who need specific handling would assign
the
addresses of the functions they implement to these hooks during
initialization.

Non-goals:
The implementation of the hook functions by the device drivers is up
to
each driver, the integrator and the system power management
policies.

All device power management infrastructure implementations is
enclosed
inside CONFIG_DEVICE_POWER flag.

The hook functions for the PM service app:
a) int device_suspend(struct device *port)
param port:
Device structure of the driver instance

return:
DEV_OK for success, error code otherwise

Details - calls the suspend hook of the device associated with the
device structure parameter.

b) int device_resume(struct device *port)
param port:
Device structure of the driver instance

return:
DEV_OK for success, error code otherwise

Details - calls the resume hook of the device associated with the
device
structure parameter.

c) void device_suspend_all(bool system_devices)
param system_devices
Boolean true for only the system devices; else all devices

Details:
Kernel holds a list of all devices in the system. This function
will
iterate through that list and call the suspend hook function of each
device in the list. It will do this in the reverse order in which
the
devices were initialized.

If the <system_devices> parameter is false then it would call all
devices. If it is true then it would call the suspend/resume of only
system devices. This parameter is provided because it is mandatory
for
the PM service app to call this function for system devices. This is
because it cannot bind to them and call their suspend/resume
functions
directly. For non-system devices, the PM service app can bind to
thm
and has the flexibility to call whichever device it requires to call
and
in the order it chooses to. If it chooses to call the suspend/resume
functions of non-system devices individually, then it would call
this
function passing true for the <system_devices> parameter.

d) void device_resume_all(bool system_devices)
param system_devices
Boolean true for only the system devices; else all devices

Details:
The description is same as for device_suspend_all() except, this
would
call the resume hook functions of devices and they would be called
in
the same order as the devices were initialized.

API for device drivers:
Structure storing the hook functions. This structure is stored in
the
device's "device" structure.
struct device_power_ops {
device_op_t suspend;
device_op_t resume;
}

The NO-OP function:
int device_pm_noop(struct device *unused)
return - always returns DEV_OK.

Details:
No-op function that does nothing and returns DEV_OK immediately.
This
can be used to initialize hooks for which the device has no
operation to do.

The hooks inside device_power_ops structure are by default
initialized
with this function by the kernel. Device drivers which have
specific
operations for the hooks would overwrite with a suspend and resume
functions during driver initialization.

a) void device_power_ops_configure(struct device *port,
device_op_t suspend_fn, device_op_t resume_fn)
param port
- device structure of driver instance
param suspend_fn
- Address of suspend function
param resume_fn
- Address of resume function

Details:
Initializes the hook functions in device_power_ops with the
functions
passed as parameters. This function should be called by devices
during
the time of initialization if they have a specific suspend or resume
function implementation. Drivers can also pass the no-op function
as
parameter for any of the hooks it does not have an operation. If it
does not have any operation for both suspend and resume then it need
not
call this function at all. This is because the hooks for the devices
are
by default initialized with the no-op function by the kernel.