Пример #1
0
/* 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);
}
Пример #2
0
/*
 * 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;
}
Пример #3
0
/*
 * 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);
}
Пример #4
0
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);
}
Пример #5
0
/*
 * 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));
	}
}
Пример #6
0
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);
	}
}