/* usb function to perform async pm_request_power_change */ int usb_req_lower_power(dev_info_t *dip, int comp, int level, void (*callback)(void *, int), void *arg, usb_flags_t flags) { usba_pm_req_t *pmrq; if (flags & USB_FLAGS_SLEEP) { return (pm_lower_power(dip, comp, level)); } if ((pmrq = kmem_alloc(sizeof (usba_pm_req_t), KM_NOSLEEP)) == NULL) { return (USB_FAILURE); } pmrq->dip = dip; pmrq->comp = comp; pmrq->level = level; pmrq->cb = callback; pmrq->arg = arg; pmrq->flags = flags; if (usb_async_req(dip, usba_async_req_lower_power, (void *)pmrq, USB_FLAGS_NOSLEEP | USB_FLAGS_NOQUEUE) != USB_SUCCESS) { kmem_free(pmrq, sizeof (usba_pm_req_t)); return (USB_FAILURE); } return (USB_SUCCESS); }
/* * destroy PM components */ static void uftdi_destroy_pm_components(uftdi_state_t *uf) { uftdi_pm_t *pm = uf->uf_pm; dev_info_t *dip = uf->uf_dip; int rval; if (!pm) return; if (uf->uf_dev_state != USB_DEV_DISCONNECTED) { if (pm->pm_wakeup_enabled) { rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); if (rval != DDI_SUCCESS) { USB_DPRINTF_L2(DPRINT_PM, uf->uf_lh, "uftdi_destroy_pm_components: " "raising power failed, rval=%d", rval); } rval = usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_DISABLE); if (rval != USB_SUCCESS) { USB_DPRINTF_L2(DPRINT_PM, uf->uf_lh, "uftdi_destroy_pm_components: disable " "remote wakeup failed, rval=%d", rval); } } (void) pm_lower_power(dip, 0, USB_DEV_OS_PWR_OFF); } kmem_free(pm, sizeof (*pm)); uf->uf_pm = NULL; }
/* * wusb_df_destroy_power_mgmt: * Shut down and destroy power management and remote wakeup functionality. */ static void wusb_df_destroy_power_mgmt(wusb_df_state_t *wusb_dfp) { USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "destroy_power_mgmt enter"); ASSERT(!mutex_owned(&wusb_dfp->wusb_df_mutex)); mutex_enter(&wusb_dfp->wusb_df_mutex); if (!wusb_dfp->wusb_df_pm) { mutex_exit(&wusb_dfp->wusb_df_mutex); return; } mutex_exit(&wusb_dfp->wusb_df_mutex); (void) wusb_df_pm_busy_component(wusb_dfp); mutex_enter(&wusb_dfp->wusb_df_mutex); if (wusb_dfp->wusb_df_dev_state != USB_DEV_DISCONNECTED) { if (wusb_dfp->wusb_df_pm->wusb_df_wakeup_enabled) { mutex_exit(&wusb_dfp->wusb_df_mutex); (void) pm_raise_power(wusb_dfp->wusb_df_dip, 0, USB_DEV_OS_FULL_PWR); if (usb_handle_remote_wakeup(wusb_dfp->wusb_df_dip, USB_REMOTE_WAKEUP_DISABLE) != USB_SUCCESS) { USB_DPRINTF_L2(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_destroy_power_mgmt: " "Error disabling rmt wakeup"); } mutex_enter(&wusb_dfp->wusb_df_mutex); } } mutex_exit(&wusb_dfp->wusb_df_mutex); /* * Since remote wakeup is disabled now, * no one can raise power * and get to device once power is lowered here. */ (void) pm_lower_power(wusb_dfp->wusb_df_dip, 0, USB_DEV_OS_PWR_OFF); wusb_df_pm_idle_component(wusb_dfp); mutex_enter(&wusb_dfp->wusb_df_mutex); kmem_free(wusb_dfp->wusb_df_pm, sizeof (wusb_df_power_t)); wusb_dfp->wusb_df_pm = NULL; mutex_exit(&wusb_dfp->wusb_df_mutex); }
static void usba_async_req_lower_power(void *arg) { usba_pm_req_t *pmrq = (usba_pm_req_t *)arg; int rval; /* * To eliminate race condition between the call to power entry * point and our call to lower power level, we call idle component * to push ahead the PM timestamp */ (void) pm_idle_component(pmrq->dip, pmrq->comp); rval = pm_lower_power(pmrq->dip, pmrq->comp, pmrq->level); pmrq->cb(pmrq->arg, rval); }
/* * Remove PM state for nexus. */ static void ppb_pwr_teardown(ppb_devstate_t *ppb, dev_info_t *dip) { int low_lvl; /* * Determine the lowest power level supported. */ if (ppb->ppb_pwr_p->pwr_flags & PCI_PWR_B3_CAPABLE) { low_lvl = PM_LEVEL_B3; } else { low_lvl = PM_LEVEL_B2; } if (pm_lower_power(dip, PCI_PM_COMP_0, low_lvl) != DDI_SUCCESS) { cmn_err(CE_WARN, "%s%d failed to lower power", ddi_driver_name(dip), ddi_get_instance(dip)); } pci_config_teardown(&ppb->ppb_conf_hdl); mutex_destroy(&ppb->ppb_pwr_p->pwr_mutex); kmem_free(ppb->ppb_pwr_p, sizeof (pci_pwr_t)); if (ddi_prop_remove(DDI_DEV_T_NONE, dip, "pm-components") != DDI_PROP_SUCCESS) { cmn_err(CE_WARN, "%s%d unable to remove prop pm-components", ddi_driver_name(dip), ddi_get_instance(dip)); } if (ddi_prop_remove(DDI_DEV_T_NONE, dip, "pm-want-child-notification?") != DDI_PROP_SUCCESS) { cmn_err(CE_WARN, "%s%d unable to remove prop pm-want_child_notification?", ddi_driver_name(dip), ddi_get_instance(dip)); } }
static int gen_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) { struct dstate *dstatep; int instance; int i; int rv; int rm_power; int level_tmp; #ifdef DEBUG int n_devs; int n_minorcomps; int isclone; #endif switch (cmd) { case DDI_DETACH: GEN_DEBUG((CE_CONT, "%s%d: DDI_DETACH", ddi_node_name(devi), ddi_get_instance(devi))); instance = ddi_get_instance(devi); dstatep = ddi_get_soft_state(dstates, instance); if (dstatep == NULL) { return (DDI_FAILURE); } #ifdef DEBUG n_devs = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0, "ndevs", 1); isclone = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0, "isclone", 0); n_minorcomps = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 0, "ncomps", 1); #endif /* DEBUG */ /* * power off component 1. */ if (dstatep->flag & PM_SUPPORTED_FLAG) { GEN_DEBUG((CE_CONT, "%s%d: DDI_DETACH: pm_lower_power comp 1 level %d", ddi_node_name(devi), ddi_get_instance(devi), MINPWR)); if (pm_lower_power(dstatep->dip, 1, MINPWR) != DDI_SUCCESS) { cmn_err(CE_WARN, "%s%d: DDI_DETACH:\n\t" "pm_lower_power failed for comp 1 to" " level %d\n", ddi_node_name(devi), ddi_get_instance(devi), MINPWR); return (DDI_FAILURE); } /* * check power level. Issue pm_power_has_changed * if not at MINPWR. */ mutex_enter(&dstatep->lock); level_tmp = dstatep->level[1]; dstatep->level[1] = MINPWR; if (dstatep->level[1] != MINPWR) { GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:" " power off via pm_power_has_changed" " instead", ddi_node_name(devi), ddi_get_instance(devi))); if (pm_power_has_changed(dstatep->dip, 1, MINPWR) != DDI_SUCCESS) { GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:" " pm_power_has_changed failed for" " comp 1 to level %d", ddi_node_name(devi), ddi_get_instance(devi), MINPWR)); dstatep->level[1] = level_tmp; mutex_exit(&dstatep->lock); return (DDI_FAILURE); } } mutex_exit(&dstatep->lock); } /* * If the LOWER_POWER_FLAG flag is not set, * don't call pm_lowr_power() for comp 0. * This should be used only for the XXXXX@XX,no_invol * devices that export the * no-involuntary-power-cycles property */ if (!(dstatep->flag & LOWER_POWER_FLAG) && dstatep->flag & PM_SUPPORTED_FLAG) { cmn_err(CE_NOTE, "%s%d: DDI_DETACH:\n\t" " NOT CALLING PM_LOWER_POWER():" " LOWER_POWER_FLAG NOT SET\n", ddi_node_name(devi), ddi_get_instance(devi)); } else if (dstatep->flag & PM_SUPPORTED_FLAG) { GEN_DEBUG((CE_CONT, "%s%d: DDI_DETACH: pm_lower_power comp 0 level %d", ddi_node_name(devi), ddi_get_instance(devi), MINPWR)); if (pm_lower_power(dstatep->dip, 0, MINPWR) != DDI_SUCCESS) { cmn_err(CE_WARN, "%s%d: DDI_DETACH:\n\t" "pm_lower_power failed for comp 0 to" " level %d\n", ddi_node_name(devi), ddi_get_instance(devi), MINPWR); return (DDI_FAILURE); } /* * check power level. Issue pm_power_has_changed * if not at MINPWR. */ mutex_enter(&dstatep->lock); level_tmp = dstatep->level[0]; dstatep->level[0] = MINPWR; if (dstatep->level[0] != MINPWR) { GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:" " power off via pm_power_has_changed" " instead", ddi_node_name(devi), ddi_get_instance(devi))); if (pm_power_has_changed(dstatep->dip, 0, MINPWR) != DDI_SUCCESS) { GEN_DEBUG((CE_NOTE, "%s%d: DDI_DETACH:" " pm_power_has_changed failed for" " comp 0 to level %d", ddi_node_name(devi), ddi_get_instance(devi), MINPWR)); dstatep->level[0] = level_tmp; mutex_exit(&dstatep->lock); return (DDI_FAILURE); } } mutex_exit(&dstatep->lock); } GEN_DEBUG((CE_CONT, "%s%d detaching: n_devs=%d n_minorcomps=%d isclone=%d", ddi_node_name(devi), ddi_get_instance(devi), n_devs, n_minorcomps, isclone)); for (i = 0; i < NUMEVENTS; i++) { if (dstatep->gen_cb_ids[i]) { (void) ddi_remove_event_handler(dstatep->gen_cb_ids[i]); dstatep->gen_cb_ids[i] = NULL; } } ddi_prop_remove_all(devi); ddi_remove_minor_node(devi, NULL); if (dstatep->node_type) kmem_free(dstatep->node_type, strlen(dstatep->node_type) + 1); ddi_soft_state_free(dstates, instance); return (DDI_SUCCESS); case DDI_SUSPEND: GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND", ddi_node_name(devi), ddi_get_instance(devi))); instance = ddi_get_instance(devi); dstatep = ddi_get_soft_state(dstates, instance); if (dstatep == NULL) { return (DDI_FAILURE); } /* * fail the suspend if FAIL_SUSPEND_FLAG is set. * clear the FAIL_SUSPEND_FLAG flag */ mutex_enter(&dstatep->lock); if (dstatep->flag & FAIL_SUSPEND_FLAG) { GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:" " FAIL_SUSPEND_FLAG is set," " fail suspend", ddi_node_name(devi), ddi_get_instance(devi))); dstatep->flag &= ~FAIL_SUSPEND_FLAG; rv = DDI_FAILURE; } else { rv = DDI_SUCCESS; } mutex_exit(&dstatep->lock); /* * Issue ddi_removing_power() to determine if the suspend * was initiated by either CPR or DR. If CPR, the system * will be powered OFF; if this driver has set the * NO_INVOL_FLAG, then refuse to suspend. If DR, power * will not be removed, thus allow the suspend. */ if (dstatep->flag & NO_INVOL_FLAG && dstatep->flag & PM_SUPPORTED_FLAG) { GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:" " check via ddi_removing_power()", ddi_node_name(devi), ddi_get_instance(devi))); rm_power = ddi_removing_power(dstatep->dip); if (rm_power < 0) { cmn_err(CE_WARN, "%s%d: DDI_SUSPEND:" " ddi_removing_power() failed\n", ddi_node_name(devi), ddi_get_instance(devi)); } else if (rm_power == 1) { /* * CPR: power will be removed */ GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:\n\t" " CPR: POWER WILL BE REMOVED, THEREFORE" " REFUSE TO SUSPEND", ddi_node_name(devi), ddi_get_instance(devi))); rv = DDI_FAILURE; } else if (rm_power == 0) { /* * DR: power will not be removed */ GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND:\n\t" " DR: POWER WILL NOT BE REMOVED, THEREFORE" " ALLOW THE SUSPEND", ddi_node_name(devi), ddi_get_instance(devi))); rv = DDI_SUCCESS; } } /* * power OFF via pm_power_has_changed() */ mutex_enter(&dstatep->lock); if (dstatep->flag & PM_SUPPORTED_FLAG && !(dstatep->flag & NO_INVOL_FLAG)) { level_tmp = dstatep->level[0]; dstatep->level[0] = MINPWR; GEN_DEBUG((CE_CONT, "%s%d: DDI_SUSPEND: pm_power_has_changed comp 0" " level %d", ddi_node_name(devi), ddi_get_instance(devi), MINPWR)); if (pm_power_has_changed(dstatep->dip, 0, MINPWR) != DDI_SUCCESS) { cmn_err(CE_WARN, "%s%d: DDI_SUSPEND:\n\t" "pm_power_has_changed failed for comp 0 to" " level %d\n", ddi_node_name(devi), ddi_get_instance(devi), MINPWR); dstatep->level[0] = level_tmp; mutex_exit(&dstatep->lock); return (DDI_FAILURE); } } mutex_exit(&dstatep->lock); return (rv); default: return (DDI_FAILURE); } }