Writing Device Drivers

System Power Management Model

This section describes the details of the System Power Management model. The model includes the following components:

Autoshutdown Threshold

The system may be shut down (powered off) automatically after a configurable period of idleness. This period is known as the autoshutdown threshold. This behavior is enabled by default for SPARC desktop systems first shipped after October 1, 1995 and before July 1, 1999. It may be overridden using dtpower(1M) or power.conf(4).

Busy State

There are several ways to measure the busy state of the system. The currently supported built-in metrics are keyboard characters, mouse activity, tty characters, load average, disk reads, and NFS requests. Any one of these metrics may make the system busy. In addition to the built-in metrics, an interface is defined for running a user-specified process that may indicate that the system is busy.

Hardware State

Devices that export a reg property are considered to have hardware state that must be saved prior to shutting down the system. If a device does not have a reg property, then it is considered to be stateless. However, this consideration can be overridden by the device driver.

A device that has hardware state but no reg property (such as a SCSI target driver, which has hardware at the other end of the SCSI bus), is called to save and restore its state if it exports a pm-hardware-state property with the value needs-suspend-resume. Otherwise, the lack of a reg property is taken to mean that the device has no hardware state. .

A device that has a reg property but no hardware state may export a pm-hardware-state property with the value no-suspend-resume to keep the framework from calling into the driver to save and restore that state. For more information on power management properties, see the pm(9) man page.

Policy

The system will be shut down if the following conditions apply:

Entry Points Used by System Power Management

System power management passes the command DDI_SUSPEND to the detach(9E) driver entry point to request the driver to save the device hardware state. It passes the command DDI_RESUME to the attach(9E) driver entry point to request the driver to restore the device hardware state.

detach(9E)

    int detach(dev_info_t *dip, ddi_detach_cmd_t cmd);

If a device has a reg property or a pm-hardware-state property with a value of needs-suspend-resume, then the framework calls into the driver's detach(9E) entry point to allow the driver to save the hardware state of the device to memory so that it can be restored after the system power returns. To process the DDI_SUSPEND command, detach(9E) must do the following:

If, for some reason, the driver is not able to suspend the device and save its state to memory, then it must return DDI_FAILURE, and the framework aborts the system power management operation.

Dump requests must be honored. The framework uses the dump(9E) entry point to write out the state file containing the contents of memory. See dump(9E) for restrictions imposed on the device driver when using this entry point.

If the device implements power-manageable components, the device may have had its state saved and powered off when its detach(9E) entry point is called with the DDI_SUSPEND command. In this case the driver should cancel pending timeouts and suppress the call to pm_raise_power(9F) (except for dump(9E) requests) until the device is resumed by a call to attach(9E) with a command of DDI_RESUME. The driver must keep sufficient track of its state to be able to deal appropriately with this possibility. Example 9-6 shows an example of a detach(9E) routine with the DDI_SUSPEND command implemented.


Example 9-6 detach(9E) Routine Implementing DDI_SUSPEND

int
xxdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
        struct xxstate *xsp;
        int instance;

        instance = ddi_get_instance(dip);
        xsp = ddi_get_soft_state(statep, instance);

        switch (cmd) {
        case DDI_DETACH:
          ...

        case DDI_SUSPEND:
                mutex_enter(&xsp->mu);
                xsp->xx_suspended = 1;  /* stop new operations */

                /*
                 * Sleep waiting for all the commands to be completed
                 */
                ...

                /*
                 * If a callback is outstanding which cannot be cancelled
                 * then either wait for the callback to complete or fail the
                 * suspend request
                 */
                ...

                /*
                 * This section is only needed if the driver maintains a
                 * running timeout
                 */
                if (xsp->xx_timeout_id) {
                        timeout_id_t temp_timeout_id = xsp->xx_timeout_id;

                        xsp->xx_timeout_id = 0;
                        mutex_exit(&xsp->mu);
                        untimeout(temp_timeout_id);
                        mutex_enter(&xsp->mu);
                }

                if (!xsp->xx_state_saved) {
                        /*
                         * Save device register contents into
                         * xsp->xx_device_state
                         */
                        ...
                }
                mutex_exit(&xsp->mu);
                return (DDI_SUCCESS);

        default:
                return (DDI_FAILURE);
}

attach(9E)

    int attach(dev_info_t *dip, ddi_attach_cmd_t cmd);

When power is restored to the system, each device with a reg property or with a pm-hardware-state property of value needs-suspend-resume has its attach(9E) entry point called with a command value of DDI_RESUME. If the system shutdown was aborted for some reason, each driver that was suspended is called to resume, even though the power has not been shut off. Consequently, the resume code in attach(9E) must make no assumptions about the state of the hardware; it may or may not have lost power.

The power management framework will consider the power level of the components to be unknown at DDI_RESUME time. Depending on the nature of the device, the driver writer has two choices:

Example 9-7 shows an example of an attach(9E) routine with the DDI_RESUME command.


Example 9-7 attach(9E) Routine Implementing DDI_RESUME

int
xxattach(devinfo_t *dip, ddi_attach_cmd_t cmd)
{
        struct xxstate *xsp;
        int    instance;

        instance = ddi_get_instance(dip);
        xsp = ddi_get_soft_state(statep, instance);

        switch (cmd) {
        case DDI_ATTACH:
        ...

        case DDI_RESUME:
                mutex_enter(&xsp->mu);
                if (xsp->xx_pm_state_saved) {
                        /*
                         * Restore device register contents from
                         * xsp->xx_device_state
                         */
                        ...
                }
                /*
                 * This section is optional and only needed if the
                 * driver maintains a running timeout
                 */
                xsp->xx_timeout_id = timeout(...);

                xsp->xx_suspended = 0;          /* allow new operations */
                cv_broadcast(&xsp->xx_suspend_cv);

                /* If it is possible to determine in a device-specific way what
                 * the power levels of components are without powering the components up,
                 * then the following code is recommended
                 */
                for (i = 0; i < num_components; i++) {
                        xsp->xx_power_level[i] = xx_get_power_level(dip, i);
                        if (xsp->xx_power_level[i] != XX_LEVEL_UNKNOWN)
                                (void) pm_power_has_changed(dip, i, 
                                    xsp->xx_power_level[i]);
                }
                mutex_exit(&xsp->mu);
                return(DDI_SUCCESS);
        default:
                return(DDI_FAILURE);
        }
}


Note -

The detach(9E) and attach(9E) interfaces may also be used to resume a system that has been quiesced.