/* usb function to perform async pm_request_power_change */ int usb_req_raise_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_raise_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_raise_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); }
/* * wusb_df_init_power_mgmt: * Initialize power management and remote wakeup functionality. * No mutex is necessary in this function as it's called only by attach. */ static void wusb_df_init_power_mgmt(wusb_df_state_t *wusb_dfp) { wusb_df_power_t *wusb_dfpm; uint_t pwr_states; USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "init_power_mgmt enter"); /* * If remote wakeup is not available you may not want to do * power management. */ /* Allocate the state structure */ wusb_dfpm = kmem_zalloc(sizeof (wusb_df_power_t), KM_SLEEP); wusb_dfp->wusb_df_pm = wusb_dfpm; wusb_dfpm->wusb_df_state = wusb_dfp; wusb_dfpm->wusb_df_pm_capabilities = 0; wusb_dfpm->wusb_df_current_power = USB_DEV_OS_FULL_PWR; if (usb_create_pm_components(wusb_dfp->wusb_df_dip, &pwr_states) == USB_SUCCESS) { USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_init_power_mgmt: created PM components"); wusb_dfpm->wusb_df_pwr_states = (uint8_t)pwr_states; (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_ENABLE) == USB_SUCCESS) { wusb_dfpm->wusb_df_wakeup_enabled = 1; } else { USB_DPRINTF_L2(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_init_power_mgmt:" "fail to enable remote wakeup"); } } else { USB_DPRINTF_L2(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_init_power_mgmt: create_pm_compts failed"); } USB_DPRINTF_L4(PRINT_MASK_PM, wusb_dfp->wusb_df_log_hdl, "wusb_df_init_power_mgmt: end"); }
static void usba_async_req_raise_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 raise power level, we first mark the * component busy and later idle */ (void) pm_busy_component(pmrq->dip, pmrq->comp); rval = pm_raise_power(pmrq->dip, pmrq->comp, pmrq->level); (void) pm_idle_component(pmrq->dip, pmrq->comp); pmrq->cb(pmrq->arg, rval); /* We are done with pmrq. Free it now */ kmem_free(pmrq, sizeof (usba_pm_req_t)); }
/* * mark device busy and raise power */ static int uftdi_pm_set_busy(uftdi_state_t *uf) { uftdi_pm_t *pm = uf->uf_pm; dev_info_t *dip = uf->uf_dip; int rval; USB_DPRINTF_L4(DPRINT_PM, uf->uf_lh, "uftdi_pm_set_busy"); if (!pm) return (USB_SUCCESS); mutex_enter(&uf->uf_lock); /* if already marked busy, just increment the counter */ if (pm->pm_busy_cnt++ > 0) { mutex_exit(&uf->uf_lock); return (USB_SUCCESS); } rval = pm_busy_component(dip, 0); ASSERT(rval == DDI_SUCCESS); if (pm->pm_cur_power == USB_DEV_OS_FULL_PWR) { mutex_exit(&uf->uf_lock); return (USB_SUCCESS); } /* need to raise power */ pm->pm_raise_power = B_TRUE; mutex_exit(&uf->uf_lock); rval = pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); if (rval != DDI_SUCCESS) { USB_DPRINTF_L2(DPRINT_PM, uf->uf_lh, "raising power failed"); } mutex_enter(&uf->uf_lock); pm->pm_raise_power = B_FALSE; mutex_exit(&uf->uf_lock); return (USB_SUCCESS); }
/* * wusb_df_cpr_resume: * * wusb_df_restore_device_state marks success by putting device back online */ static void wusb_df_cpr_resume(dev_info_t *dip) { int instance = ddi_get_instance(dip); wusb_df_state_t *wusb_dfp = ddi_get_soft_state(wusb_df_statep, instance); USB_DPRINTF_L4(PRINT_MASK_CPR, wusb_dfp->wusb_df_log_hdl, "resume: enter"); /* * NOTE: A pm_raise_power in wusb_df_restore_device_state will bring * the power-up state of device into synch with the system. */ wusb_df_pm_busy_component(wusb_dfp); (void) pm_raise_power(wusb_dfp->wusb_df_dip, 0, USB_DEV_OS_FULL_PWR); mutex_enter(&wusb_dfp->wusb_df_mutex); wusb_df_restore_device_state(dip, wusb_dfp); mutex_exit(&wusb_dfp->wusb_df_mutex); wusb_df_pm_idle_component(wusb_dfp); }
/* * wusb_df_reconnect_callback: * Called with device hotplug-inserted * Restore state */ static int wusb_df_reconnect_callback(dev_info_t *dip) { int instance = ddi_get_instance(dip); wusb_df_state_t *wusb_dfp = ddi_get_soft_state(wusb_df_statep, instance); USB_DPRINTF_L4(PRINT_MASK_ATTA, wusb_dfp->wusb_df_log_hdl, "reconnect: enter"); wusb_df_pm_busy_component(wusb_dfp); (void) pm_raise_power(wusb_dfp->wusb_df_dip, 0, USB_DEV_OS_FULL_PWR); mutex_enter(&wusb_dfp->wusb_df_mutex); (void) wusb_df_serialize_access(wusb_dfp, WUSB_DF_SER_NOSIG); wusb_df_restore_device_state(dip, wusb_dfp); wusb_df_release_access(wusb_dfp); mutex_exit(&wusb_dfp->wusb_df_mutex); wusb_df_pm_idle_component(wusb_dfp); return (USB_SUCCESS); }
/* * create PM components */ static int uftdi_create_pm_components(uftdi_state_t *uf) { dev_info_t *dip = uf->uf_dip; uftdi_pm_t *pm; uint_t pwr_states; if (usb_create_pm_components(dip, &pwr_states) != USB_SUCCESS) { USB_DPRINTF_L2(DPRINT_PM, uf->uf_lh, "uftdi_create_pm_components: failed"); return (USB_SUCCESS); } pm = uf->uf_pm = kmem_zalloc(sizeof (*pm), KM_SLEEP); pm->pm_pwr_states = (uint8_t)pwr_states; pm->pm_cur_power = USB_DEV_OS_FULL_PWR; pm->pm_wakeup_enabled = usb_handle_remote_wakeup(dip, USB_REMOTE_WAKEUP_ENABLE) == USB_SUCCESS; (void) pm_raise_power(dip, 0, USB_DEV_OS_FULL_PWR); return (USB_SUCCESS); }
/*ARGSUSED*/ static int gen_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) { struct dstate *dstatep; ddi_eventcookie_t cookie; int instance; int rval = 0; char *nodename; int i; struct devctl_iocdata *dcp; uint_t state; int ret; int level_tmp; instance = MINOR_TO_INST(getminor(dev)); dstatep = ddi_get_soft_state(dstates, instance); nodename = dstatep->nodename; if (dstatep == NULL) return (ENXIO); /* * read devctl ioctl data */ if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) return (EFAULT); switch (cmd) { case GENDRV_IOFAULT_SIMULATE: if (ddi_get_eventcookie(dstatep->dip, DDI_DEVI_FAULT_EVENT, &(cookie)) != NDI_SUCCESS) return (DDI_FAILURE); return (ndi_post_event(dstatep->dip, dstatep->dip, cookie, NULL)); case GENDRV_NDI_EVENT_TEST: if (ddi_get_eventcookie(dstatep->dip, "pshot_dev_offline", &cookie) == NDI_SUCCESS) { (void) ndi_post_event(dstatep->dip, dstatep->dip, cookie, NULL); } if (ddi_get_eventcookie(dstatep->dip, "pshot_dev_reset", &cookie) == NDI_SUCCESS) { (void) ndi_post_event(dstatep->dip, dstatep->dip, cookie, NULL); } if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_reset", &cookie) == NDI_SUCCESS) { (void) ndi_post_event(dstatep->dip, dstatep->dip, cookie, NULL); } if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_quiesce", &cookie) == NDI_SUCCESS) { (void) ndi_post_event(dstatep->dip, dstatep->dip, cookie, NULL); } if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_unquiesce", &cookie) == NDI_SUCCESS) { (void) ndi_post_event(dstatep->dip, dstatep->dip, cookie, NULL); } if (ddi_get_eventcookie(dstatep->dip, "pshot_bus_test_post", &cookie) == NDI_SUCCESS) { (void) ndi_post_event(dstatep->dip, dstatep->dip, cookie, NULL); } break; case DEVCTL_PM_PWR_HAS_CHANGED_ON_RESUME: /* * Issue pm_power_has_changed() call on DDI_RESUME */ mutex_enter(&dstatep->lock); dstatep->flag |= PWR_HAS_CHANGED_ON_RESUME_FLAG; mutex_exit(&dstatep->lock); GEN_DEBUG((CE_CONT, "%s%d:" " DEVCTL_PM_PWR_HAS_CHANGED_ON_RESUME", nodename, instance)); break; case DEVCTL_PM_FAIL_SUSPEND: /* * Fail the suspend attempt in DDI_SUSPEND */ mutex_enter(&dstatep->lock); dstatep->flag |= FAIL_SUSPEND_FLAG; mutex_exit(&dstatep->lock); GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_FAIL_SUSPEND", nodename, instance)); break; case DEVCTL_PM_PUP_WITH_PWR_HAS_CHANGED: /* * Use pm_power_has_changed() to power up comp 0 when * enforcing the comp 0 vs comp-not 0 dependency: * Power up comp 0 first, if request for comp-not-0 * comes in. * Else, default to pm_raise_power(). */ mutex_enter(&dstatep->lock); dstatep->flag |= PUP_WITH_PWR_HAS_CHANGED_FLAG; mutex_exit(&dstatep->lock); GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_PUP_WITH_PWR_HAS_CHANGED", nodename, instance)); break; case DEVCTL_PM_BUSY_COMP: /* * mark component 0 busy via a pm_busy_component() call. * update the busy[] array. */ mutex_enter(&dstatep->lock); ++dstatep->busy[0]; GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_BUSY_COMP: comp 0:" " busy=%d", nodename, instance, dstatep->busy[0])); mutex_exit(&dstatep->lock); ret = pm_busy_component(dstatep->dip, 0); ASSERT(ret == DDI_SUCCESS); break; case DEVCTL_PM_BUSY_COMP_TEST: /* * test busy state on component 0 */ mutex_enter(&dstatep->lock); state = dstatep->busy[0]; if (copyout(&state, dcp->cpyout_buf, sizeof (uint_t)) != 0) { cmn_err(CE_WARN, "%s%d:" " DEVCTL_PM_BUSY_COMP_TEST: copyout failed\n", nodename, instance); rval = EINVAL; } GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_BUSY_COMP_TEST:" " comp 0 busy %d", nodename, instance, state)); mutex_exit(&dstatep->lock); break; case DEVCTL_PM_IDLE_COMP: /* * mark component 0 idle via a pm_idle_component() call. * NOP if dstatep->busy[0] == 0. */ mutex_enter(&dstatep->lock); if (dstatep->busy[0] > 0) { --dstatep->busy[0]; GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_IDLE_COMP:" " comp 0: busy=%d", nodename, instance, dstatep->busy[0])); mutex_exit(&dstatep->lock); ret = pm_idle_component(dstatep->dip, 0); ASSERT(ret == DDI_SUCCESS); } else { mutex_exit(&dstatep->lock); } break; case DEVCTL_PM_PROM_PRINTF: (void) prom_printf("%s%d: PROM_PRINTF FROM GEN_DRV\n", nodename, instance); break; case DEVCTL_PM_RAISE_PWR: /* * power up both components to MAXPWR via * pm_raise_power() calls. this ioctl() cmd * assumes that the current level is 0 */ for (i = 0; i < COMPONENTS; i++) { GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_RAISE_PWR:" " comp %d old 0 new %d", nodename, instance, i, maxpwr[i])); if (pm_raise_power(dstatep->dip, 0, maxpwr[i]) != DDI_SUCCESS) { rval = EINVAL; } } break; case DEVCTL_PM_CHANGE_PWR_LOW: /* * power off both components via pm_power_has_changed() calls */ for (i = (COMPONENTS - 1); i >= 0; --i) { GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_CHANGE_PWR_LOW:" " comp %d new 0", nodename, instance, i)); mutex_enter(&dstatep->lock); level_tmp = dstatep->level[i]; dstatep->level[i] = 0; if (pm_power_has_changed(dstatep->dip, i, 0) != DDI_SUCCESS) { dstatep->level[i] = level_tmp; rval = EINVAL; } mutex_exit(&dstatep->lock); } break; case DEVCTL_PM_CHANGE_PWR_HIGH: /* * power up both components to MAXPWR via * pm_power_has_changed() calls */ for (i = 0; i < COMPONENTS; i++) { GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_CHANGE_PWR_HIGH:" " comp %d new %d", nodename, instance, i, maxpwr[i])); mutex_enter(&dstatep->lock); level_tmp = dstatep->level[i]; dstatep->level[i] = maxpwr[i]; if (pm_power_has_changed(dstatep->dip, i, maxpwr[i]) != DDI_SUCCESS) { dstatep->level[i] = level_tmp; rval = EINVAL; } mutex_exit(&dstatep->lock); } break; case DEVCTL_PM_POWER: /* * test if the gen_drv_power() routine has been called, * then clear */ mutex_enter(&dstatep->lock); state = (dstatep->flag & POWER_FLAG) ? 1 : 0; if (copyout(&state, dcp->cpyout_buf, sizeof (uint_t)) != 0) { cmn_err(CE_WARN, "%s%d: DEVCTL_PM_POWER:" " copyout failed\n", nodename, instance); rval = EINVAL; } GEN_DEBUG((CE_CONT, "%s%d: %s POWER_FLAG: %d", nodename, instance, "DEVCTL_PM_POWER", state)); dstatep->flag &= ~POWER_FLAG; mutex_exit(&dstatep->lock); break; case DEVCTL_PM_NO_LOWER_POWER: /* * issue to not invoke pm_lower_power() on detach */ mutex_enter(&dstatep->lock); dstatep->flag &= ~LOWER_POWER_FLAG; mutex_exit(&dstatep->lock); GEN_DEBUG((CE_CONT, "%s%d: DEVCTL_PM_NO_LOWER_POWER", nodename, instance)); break; default: return (ENOTTY); } return (rval); }
static int gen_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) { int instance = ddi_get_instance(devi); struct dstate *dstatep; int rval; int n_devs; int n_minorcomps; int isclone; ddi_eventcookie_t dev_offline_cookie, dev_reset_cookie; ddi_eventcookie_t bus_reset_cookie, bus_quiesce_cookie; ddi_eventcookie_t bus_unquiesce_cookie, bus_test_post_cookie; int i_init = 0; int level_tmp; int i; char *pm_comp[] = { "NAME=leaf0", "0=D0", "1=D1", "2=D2", "3=D3", "NAME=leaf1", "0=off", "1=blank", "2=on"}; char *pm_hw_state = {"needs-suspend-resume"}; switch (cmd) { case DDI_ATTACH: if (ddi_soft_state_zalloc(dstates, instance) != DDI_SUCCESS) { cmn_err(CE_CONT, "%s%d: can't allocate state\n", ddi_get_name(devi), instance); return (DDI_FAILURE); } dstatep = ddi_get_soft_state(dstates, instance); dstatep->dip = devi; mutex_init(&dstatep->lock, NULL, MUTEX_DRIVER, NULL); 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); GEN_DEBUG((CE_CONT, "%s%d attaching: n_devs=%d n_minorcomps=%d isclone=%d", ddi_get_name(devi), ddi_get_instance(devi), n_devs, n_minorcomps, isclone)); if (isclone) { if (ddi_create_minor_node(devi, "gen", S_IFCHR, INST_TO_MINOR(instance), mnodetypes[0], isclone) != DDI_SUCCESS) { ddi_remove_minor_node(devi, NULL); ddi_soft_state_free(dstates, instance); cmn_err(CE_WARN, "%s%d: can't create minor " "node", ddi_get_name(devi), instance); return (DDI_FAILURE); } rval = DDI_SUCCESS; } else { rval = gen_create_minor_nodes(devi, dstatep); if (rval != DDI_SUCCESS) { ddi_prop_remove_all(devi); ddi_remove_minor_node(devi, NULL); ddi_soft_state_free(dstates, instance); cmn_err(CE_WARN, "%s%d: can't create minor " "nodes", ddi_get_name(devi), instance); return (DDI_FAILURE); } } if (ddi_get_eventcookie(devi, "pshot_dev_offline", &dev_offline_cookie) == DDI_SUCCESS) { (void) ddi_add_event_handler(devi, dev_offline_cookie, gen_event_cb, NULL, &(dstatep->gen_cb_ids[0])); } if (ddi_get_eventcookie(devi, "pshot_dev_reset", &dev_reset_cookie) == DDI_SUCCESS) { (void) ddi_add_event_handler(devi, dev_reset_cookie, gen_event_cb, NULL, &(dstatep->gen_cb_ids[1])); } if (ddi_get_eventcookie(devi, "pshot_bus_reset", &bus_reset_cookie) == DDI_SUCCESS) { (void) ddi_add_event_handler(devi, bus_reset_cookie, gen_event_cb, NULL, &(dstatep->gen_cb_ids[2])); } if (ddi_get_eventcookie(devi, "pshot_bus_quiesce", &bus_quiesce_cookie) == DDI_SUCCESS) { (void) ddi_add_event_handler(devi, bus_quiesce_cookie, gen_event_cb, NULL, &(dstatep->gen_cb_ids[3])); } if (ddi_get_eventcookie(devi, "pshot_bus_unquiesce", &bus_unquiesce_cookie) == DDI_SUCCESS) { (void) ddi_add_event_handler(devi, bus_unquiesce_cookie, gen_event_cb, NULL, &(dstatep->gen_cb_ids[4])); } if (ddi_get_eventcookie(devi, "pshot_bus_test_post", &bus_test_post_cookie) == DDI_SUCCESS) { (void) ddi_add_event_handler(devi, bus_test_post_cookie, gen_event_cb, NULL, &(dstatep->gen_cb_ids[5])); } /* * initialize the devices' pm state */ mutex_enter(&dstatep->lock); dstatep->flag &= ~OPEN_FLAG; dstatep->flag &= ~PWR_HAS_CHANGED_ON_RESUME_FLAG; dstatep->flag &= ~FAIL_SUSPEND_FLAG; dstatep->flag &= ~PUP_WITH_PWR_HAS_CHANGED_FLAG; dstatep->flag |= LOWER_POWER_FLAG; dstatep->flag &= ~NO_INVOL_FLAG; dstatep->flag |= PM_SUPPORTED_FLAG; dstatep->busy[0] = 0; dstatep->busy[1] = 0; dstatep->level[0] = -1; dstatep->level[1] = -1; mutex_exit(&dstatep->lock); /* * stash the nodename */ dstatep->nodename = ddi_node_name(devi); /* * Check if the no-involuntary-power-cycles property * was created. Set NO_INVOL_FLAG if so. */ if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip, (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), "no-involuntary-power-cycles") == 1) { GEN_DEBUG((CE_CONT, "%s%d: DDI_ATTACH:\n\tno-involuntary-power-cycles" " property was created", ddi_node_name(devi), ddi_get_instance(devi))); mutex_enter(&dstatep->lock); dstatep->flag |= NO_INVOL_FLAG; mutex_exit(&dstatep->lock); } /* * Check if the dependency-property property * was created. */ if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip, (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), "dependency-property") == 1) { GEN_DEBUG((CE_CONT, "%s%d: DDI_ATTACH:\n\tdependency-property" " property was created", ddi_node_name(devi), ddi_get_instance(devi))); } /* * create the pm-components property. two comps: * 4 levels on comp0, 3 on comp 1. * - skip for a "tape" device, clear PM_SUPPORTED_FLAG */ if (strcmp(ddi_node_name(devi), "tape") != 0) { if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi, "pm-components", pm_comp, 9) != DDI_PROP_SUCCESS) { cmn_err(CE_WARN, "%s%d: %s\n", ddi_node_name(devi), ddi_get_instance(devi), "unable to create \"pm-components\" " " property."); return (DDI_FAILURE); } } else { mutex_enter(&dstatep->lock); dstatep->flag &= ~PM_SUPPORTED_FLAG; mutex_exit(&dstatep->lock); } /* * Check if the pm-components property was created */ if (dstatep->flag & PM_SUPPORTED_FLAG) { if (ddi_prop_exists(DDI_DEV_T_ANY, dstatep->dip, (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM), "pm-components") != 1) { cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t%s", ddi_node_name(devi), ddi_get_instance(devi), "\"pm-components\" property does" " not exist"); return (DDI_FAILURE); } else { GEN_DEBUG((CE_CONT, "%s%d: DDI_ATTACH:" " created pm-components property", ddi_node_name(devi), ddi_get_instance(devi))); } } /* * create the pm-hardware-state property. * needed to get DDI_SUSPEND and DDI_RESUME calls */ if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "pm-hardware-state", pm_hw_state) != DDI_PROP_SUCCESS) { cmn_err(CE_WARN, "%s%d: DDI_ATTACH:\n\t%s\n", ddi_node_name(devi), ddi_get_instance(devi), "unable to create \"pm-hardware-state\" " " property."); return (DDI_FAILURE); } /* * set power levels to max via pm_raise_power(), */ mutex_enter(&dstatep->lock); i_init = (dstatep->flag & PM_SUPPORTED_FLAG) ? 0 : COMPONENTS; mutex_exit(&dstatep->lock); for (i = i_init; i < COMPONENTS; i++) { GEN_DEBUG((CE_CONT, "%s%d: DDI_ATTACH: pm_raise_power comp %d " "to level %d", ddi_node_name(devi), ddi_get_instance(devi), i, maxpwr[i])); if (pm_raise_power(dstatep->dip, i, maxpwr[i]) != DDI_SUCCESS) { cmn_err(CE_WARN, "%s%d: DDI_ATTACH: pm_raise_power failed\n", ddi_node_name(devi), ddi_get_instance(devi)); dstatep->level[i] = -1; return (DDI_FAILURE); } } if (rval == DDI_SUCCESS) { ddi_report_dev(devi); } return (rval); case DDI_RESUME: GEN_DEBUG((CE_CONT, "%s%d: DDI_RESUME", ddi_node_name(devi), ddi_get_instance(devi))); dstatep = ddi_get_soft_state(dstates, ddi_get_instance(devi)); if (dstatep == NULL) { return (DDI_FAILURE); } /* * Call pm_power_has_changed() if flag * PWR_HAS_CHANGED_ON_RESUME_FLAG is set, * then clear the flag */ mutex_enter(&dstatep->lock); i_init = (dstatep->flag & PM_SUPPORTED_FLAG) ? 0 : COMPONENTS; mutex_exit(&dstatep->lock); if (dstatep->flag & PWR_HAS_CHANGED_ON_RESUME_FLAG) { for (i = i_init; i < COMPONENTS; i++) { GEN_DEBUG((CE_CONT, "%s%d: DDI_RESUME: pm_power_has_changed " "comp %d to level %d", ddi_node_name(devi), ddi_get_instance(devi), i, maxpwr[i])); mutex_enter(&dstatep->lock); level_tmp = dstatep->level[i]; dstatep->level[i] = maxpwr[i]; if (pm_power_has_changed(dstatep->dip, i, maxpwr[i]) != DDI_SUCCESS) { cmn_err(CE_WARN, "%s%d: DDI_RESUME:\n\t" " pm_power_has_changed" " failed: comp %d to level %d\n", ddi_node_name(devi), ddi_get_instance(devi), i, maxpwr[i]); dstatep->level[i] = level_tmp; } mutex_exit(&dstatep->lock); } } else { /* * Call pm_raise_power() instead */ for (i = i_init; i < COMPONENTS; i++) { GEN_DEBUG((CE_CONT, "%s%d: DDI_RESUME: pm_raise_power" " comp %d to level %d", ddi_node_name(devi), ddi_get_instance(devi), i, maxpwr[i])); if (pm_raise_power(dstatep->dip, i, maxpwr[i]) != DDI_SUCCESS) { cmn_err(CE_WARN, "%s%d: DDI_RESUME:" "\n\tpm_raise_power" "failed: comp %d to level %d\n", ddi_node_name(devi), ddi_get_instance(devi), i, maxpwr[i]); } } } return (DDI_SUCCESS); default: GEN_DEBUG((CE_WARN, "attach: default")); return (DDI_FAILURE); } }
/*ARGSUSED0*/ static int gen_power(dev_info_t *dip, int cmpt, int level) { struct dstate *dstatep; int instance = ddi_get_instance(dip); char *nodename = ddi_node_name(dip); int level_tmp; GEN_DEBUG((CE_CONT, "%s%d: power: cmpt %d to level %d", nodename, instance, cmpt, level)); dstatep = ddi_get_soft_state(dstates, instance); if (dstatep == NULL) { return (DDI_FAILURE); } /* * Keep track of the power levels for both components * in the dstatep->comp[] array. * Set comp 0 to full level if non-zero comps * are being set to a higher, non-zero level. */ if (cmpt == 0) { mutex_enter(&dstatep->lock); dstatep->level[cmpt] = level; mutex_exit(&dstatep->lock); } else if (level > dstatep->level[cmpt] && level != 0 && dstatep->level[0] != COMP_0_MAXPWR) { /* * If component 0 is not at COMP_0_MAXPWR, and component 1 * is being powered ON, invoke pm_raise_power() or * pm_power_has_changed() based on the * PUP_WITH_PWR_HAS_CHANGED_FLAG flag. * PUP_WITH_PWR_HAS_CHANGED_FLAG = FALSE by default, invoking * pm_raise_power(). */ if (!(dstatep->flag & PUP_WITH_PWR_HAS_CHANGED_FLAG)) { /* * first set comp 0 to level COMP_0_MAXPWR */ GEN_DEBUG((CE_CONT, "%s%d: power: " "pm_raise_power: comp 0 to level %d", nodename, instance, COMP_0_MAXPWR)); if (pm_raise_power(dip, 0, COMP_0_MAXPWR) != DDI_SUCCESS) { cmn_err(CE_WARN, "%s%d: power: pm_raise_power() " "failed: comp 0 to level %d\n", nodename, instance, COMP_0_MAXPWR); return (DDI_FAILURE); } else { mutex_enter(&dstatep->lock); dstatep->level[0] = COMP_0_MAXPWR; /* * now set the level on the non-zero comp */ dstatep->level[cmpt] = level; mutex_exit(&dstatep->lock); GEN_DEBUG((CE_CONT, "%s%d: power: " "comp %d to level %d", nodename, instance, cmpt, level)); } } else { GEN_DEBUG((CE_CONT, "%s%d: power: " "pm_power_has_changed: comp 0 to level %d", nodename, instance, COMP_0_MAXPWR)); mutex_enter(&dstatep->lock); level_tmp = dstatep->level[0]; dstatep->level[0] = COMP_0_MAXPWR; if (pm_power_has_changed(dip, 0, COMP_0_MAXPWR) != DDI_SUCCESS) { cmn_err(CE_WARN, "%s%d: power: pm_power_has_changed() " "failed: comp 0 to level %d\n", nodename, instance, COMP_0_MAXPWR); dstatep->level[0] = level_tmp; } else { /* * now set the level on the non-zero comp */ GEN_DEBUG((CE_CONT, "%s%d: power:" " pm_power_has_changed: comp %d" " to level %d", nodename, instance, cmpt, level)); dstatep->level[cmpt] = level; } mutex_exit(&dstatep->lock); } } else { mutex_enter(&dstatep->lock); dstatep->level[cmpt] = level; mutex_exit(&dstatep->lock); } return (DDI_SUCCESS); }