Topics

RFC: 2/5 System Device Driver Modifications (Revised)


Thomas, Ramesh
 

[Revised incorporating feedbacks. The changes are shown at the end]

Problem Statement:
Not all Zephyr kernel drivers provide the same interfaces.

Why this is a problem:
-----------------------------
The Zephyr kernel currently has devices that are essential for core OS
functions (such as APICs and timers), but provide no public API means
for accessing them. Without any consistent method to access device
drivers on the platform, it becomes very difficult to achieve power
targets.

What should be done:
-----------------------------
1) Each native Zephyr kernel driver and shim driver (such as QMSI) shall
support:

a) uint32_t suspend() - routine that will move any required device state
data
to RAM* with the following valid return values:
- DEV_OK
- DEV_BUSY
- DEV_FAIL

b) uint32_t resume() - routine that will retrieve any device state data
from
RAM* with the following valid return values:

- DEV_OK
- DEV_FAIL

2) Provide a name recognized by the device_get_binding() function, this
includes what are currently thought to be drivers such as timers,
IOAPIC, and LOAPIC.

*The power management process is not expected to power down system RAM
(it will most likely stay in selective suspend).

The size of the device data is dependent upon an individual device, and
therefore the system integrator must be wary of the number of devices
being utilized.

Device suspend flow:
Device Drivers at suspend shall:
- Device state shall be saved to memory and maintained across a PM event
- Turning off the device clock
- Return appropriate error code

Device resume flow:
Device Drivers at resume shall:
- Restore to state saved in memory
- Turn on the device clock
- Return appropriate error code

Device domain experts will be needed to implement the proper methods for
suspending and resuming each device.

----New additions-------
Following 2 macros would be added to allow device drivers to implement
the .suspend() and .resume() hooks described in #1 above.

A device ops structure holds the .suspend() and .resume() which is
included inside each device's device structure:

struct device_ops example_dev_ops = {
.suspend = example_suspend,
.resume = example_resume
};

Two new macros would be created that drivers use instead of DEVICE_INIT
and SYS_INIT if they implement the .suspend and .resume functions. For
devices using non-PM macros, a default dev_ops will be assigned that
does nothing (so app need not check if dev_ops != NULL).

a) DEVICE_INIT_PM(dev_name, drv_name, init_fn, &example_dev_ops, data,
cfg_info, level, prio);

b) SYS_INIT_PM(name, init_fn, &example_dev_ops, level, prio);

SYS_INIT_PM also added the "name" argument so PMA can call
the .suspend/.resume for devices like APICs that need to do
suspend/resume operations. The methods that retrieve the pointer to the
device structure of any device requires the device to be assigned a name
unlike the SYS_INIT macro which assigns a "" as the name for devices.

All this will be inside DEVICE_POWER_MANAGEMENT Kconfig flag.