Writing Device Drivers
Previous Next

System Power Management Model

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

  • Autoshutdown threshold

  • Busy state

  • Hardware state

  • Policy

  • Power management entry points

Autoshutdown Threshold

The system can be shut down, that is, 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. See the power.conf(4)man page for more information. Autoshutdown can be overridden using dtpower(1M) or power.conf(4).

Busy State

The busy state of the system can be measured in several ways. The currently supported built-in metric items are keyboard characters, mouse activity, tty characters, load average, disk reads, and NFS requests. Any one of these items can make the system busy. In addition to the built-in metrics, an interface is defined for running a user-specified process that can 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. A device without the reg property is considered to be stateless. However, this consideration can be overridden by the device driver.

A device with hardware state but no reg property, such as a SCSI driver, must be called to save and restore the state if the driver 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. For information on device properties, see Chapter 4, Properties.

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

Automatic Power Management for Systems

The system is shut down if the following conditions apply:

  • Autoshutdown is enabled by dtpower(1M) or power.conf(4).

  • The system has been idle for autoshutdown threshold minutes.

  • All of the metrics that are specified in power.conf have been satisfied.

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. System power management passes the command DDI_RESUME to the attach(9E) driver entry point to request the driver to restore the device hardware state.

detach() Entry Point

The syntax for detach(9E) is as follows:

int detach(dev_info_t *dip, ddi_detach_cmd_t cmd);

A device with a reg property or a pm-hardware-state property set to needs-suspend-resume must be able to save the hardware state of the device. The framework calls into the driver's detach(9E) entry point to enable the driver to save the state for restoration after the system power returns. To process the DDI_SUSPEND command, detach(9E) must perform the following tasks:

  • Block further operations from being initiated until the device is resumed, except for dump(9E) requests.

  • Wait until outstanding operations have completed. If an outstanding operation can be restarted, you can abort that operation.

  • Cancel any timeouts and callbacks that are pending.

  • Save any volatile hardware state to memory. The state includes the contents of device registers, and can also include downloaded firmware.

If the driver is unable to suspend the device and save its state to memory, then the driver must return DDI_FAILURE. The framework then aborts the system power management operation.

In some cases, powering down a device involves certain risks. For example, if a tape drive is powered off with a tape inside, the tape can be damaged. In such a case, attach(9E) should do the following:

  • Call ddi_removing_power(9F) to determine whether a DDI_SUSPEND command can cause power to be removed from the device.

  • Determine whether power removal can cause problems.

If both cases are true, the DDI_SUSPEND request should be rejected. Example 12-6 shows an attach(9E) routine using ddi_removing_power(9F) to check whether the DDI_SUSPEND command causes problems.

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

Calling the detach(9E) entry point of a power-manageable component with the DDI_SUSPEND command should save the state when the device is powered off. The driver should cancel pending timeouts. The driver should also suppress any calls to pm_raise_power(9F) except for dump(9E) requests. When the device is resumed by a call to attach(9E) with a command of DDI_RESUME, timeouts and calls to pm_raise_power() can be resumed. The driver must keep sufficient track of its state to be able to deal appropriately with this possibility. The following example shows a detach(9E) routine with the DDI_SUSPEND command implemented.

Example 12-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:
        /*
         * We do not allow DDI_SUSPEND if power will be removed and
         * we have a device that damages tape when power is removed
         * We do support DDI_SUSPEND for Device Reconfiguration.
         */
        if (ddi_removing_power(dip) && xxdamages_tape(dip))
            return (DDI_FAILURE);
        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() Entry Point

The syntax for attach(9E) is as follows:

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 is aborted, each suspended driver 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 whether the system actually lost power.

The power management framework considers 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:

  • If the driver can determine the actual power level of the components of the device without powering the components up, such as by reading a register, then the driver should notify the framework of the power level of each component by calling pm_power_has_changed(9F).

  • If the driver cannot determine the power levels of the components, then the driver should mark each component internally as unknown and call pm_raise_power(9F) before the first access to each component.

The following example shows an attach(9E) routine with the DDI_RESUME command.

Example 12-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 can also be used to resume a system that has been quiesced.


Previous Next