/* * Create properties of various data types for testing devfs events. */ static int gen_create_properties(dev_info_t *devi) { int int_val = 3023; int int_array[] = { 3, 10, 304, 230, 4}; int64_t int64_val = 20; int64_t int64_array[] = { 12, 24, 36, 48}; char *string_val = "Dev_node_prop"; char *string_array[] = {"Dev_node_prop:0", "Dev_node_prop:1", "Dev_node_prop:2", "Dev_node_prop:3"}; uchar_t byte_array[] = { (uchar_t)0xaa, (uchar_t)0x55, (uchar_t)0x12, (uchar_t)0xcd }; char bytes[] = { (char)0x00, (char)0xef, (char)0xff }; if (ddi_prop_update_int(DDI_DEV_T_NONE, devi, "int", int_val) != DDI_PROP_SUCCESS) return (DDI_FAILURE); if (ddi_prop_update_int_array(DDI_DEV_T_NONE, devi, "int-array", int_array, sizeof (int_array) / sizeof (int)) != DDI_PROP_SUCCESS) return (DDI_FAILURE); if (ddi_prop_update_int64(DDI_DEV_T_NONE, devi, "int64", int64_val) != DDI_PROP_SUCCESS) return (DDI_FAILURE); if (ddi_prop_update_int64_array(DDI_DEV_T_NONE, devi, "int64-array", int64_array, sizeof (int64_array) / sizeof (int64_t)) != DDI_PROP_SUCCESS) return (DDI_FAILURE); if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "string", string_val) != DDI_PROP_SUCCESS) return (DDI_FAILURE); if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi, "string-array", string_array, sizeof (string_array) / sizeof (char *)) != DDI_PROP_SUCCESS) return (DDI_FAILURE); if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP, "boolean", NULL, 0) != DDI_PROP_SUCCESS) return (DDI_FAILURE); if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, devi, "byte-array", byte_array, sizeof (byte_array)) != DDI_PROP_SUCCESS) return (DDI_FAILURE); /* untyped property */ if (ddi_prop_create(DDI_DEV_T_NONE, devi, DDI_PROP_CANSLEEP, "untyped", (caddr_t)bytes, sizeof (bytes)) != DDI_PROP_SUCCESS) return (DDI_FAILURE); return (DDI_SUCCESS); }
/* * If bridge is PM capable, set up PM state for nexus. */ static void ppb_pwr_setup(ppb_devstate_t *ppb, dev_info_t *pdip) { char *comp_array[5]; int i; ddi_acc_handle_t conf_hdl; uint8_t pmcsr_bse; uint16_t pmcap; /* * Determine if bridge is PM capable. If not, leave ppb_pwr_p NULL * and return. */ if (pci_config_setup(pdip, &ppb->ppb_conf_hdl) != DDI_SUCCESS) { return; } conf_hdl = ppb->ppb_conf_hdl; /* * Locate and store the power management cap_ptr for future references. */ if ((PCI_CAP_LOCATE(conf_hdl, PCI_CAP_ID_PM, &ppb->ppb_pm_cap_ptr)) == DDI_FAILURE) { DEBUG0(DBG_PWR, pdip, "bridge does not support PM. PCI" " PM data structure not found in config header\n"); pci_config_teardown(&conf_hdl); return; } /* * Allocate PM state structure for ppb. */ ppb->ppb_pwr_p = (pci_pwr_t *) kmem_zalloc(sizeof (pci_pwr_t), KM_SLEEP); ppb->ppb_pwr_p->pwr_fp = 0; pmcsr_bse = PCI_CAP_GET8(conf_hdl, NULL, ppb->ppb_pm_cap_ptr, PCI_PMCSR_BSE); pmcap = PCI_CAP_GET16(conf_hdl, NULL, ppb->ppb_pm_cap_ptr, PCI_PMCAP); if (pmcap == PCI_CAP_EINVAL16 || pmcsr_bse == PCI_CAP_EINVAL8) { pci_config_teardown(&conf_hdl); return; } if (pmcap & PCI_PMCAP_D1) { DEBUG0(DBG_PWR, pdip, "setup: B1 state supported\n"); ppb->ppb_pwr_p->pwr_flags |= PCI_PWR_B1_CAPABLE; } else { DEBUG0(DBG_PWR, pdip, "setup: B1 state NOT supported\n"); } if (pmcap & PCI_PMCAP_D2) { DEBUG0(DBG_PWR, pdip, "setup: B2 state supported\n"); ppb->ppb_pwr_p->pwr_flags |= PCI_PWR_B2_CAPABLE; } else { DEBUG0(DBG_PWR, pdip, "setup: B2 via D2 NOT supported\n"); } if (pmcsr_bse & PCI_PMCSR_BSE_BPCC_EN) { DEBUG0(DBG_PWR, pdip, "setup: bridge power/clock control enable\n"); } else { DEBUG0(DBG_PWR, pdip, "setup: bridge power/clock control disabled\n"); kmem_free(ppb->ppb_pwr_p, sizeof (pci_pwr_t)); ppb->ppb_pwr_p = NULL; pci_config_teardown(&conf_hdl); return; } /* * PCI states D0 and D3 always are supported for normal PCI * devices. D1 and D2 are optional which are checked for above. * Bridge function states D0-D3 correspond to secondary bus states * B0-B3, EXCEPT if PCI_PMCSR_BSE_B2_B3 is set. In this case, setting * the bridge function to D3 will set the bridge bus to state B2 instead * of B3. D2 will not correspond to B2 (and in fact, probably * won't be D2 capable). Implicitly, this means that if * PCI_PMCSR_BSE_B2_B3 is set, the bus will not be B3 capable. */ if (pmcsr_bse & PCI_PMCSR_BSE_B2_B3) { ppb->ppb_pwr_p->pwr_flags |= PCI_PWR_B2_CAPABLE; DEBUG0(DBG_PWR, pdip, "B2 supported via D3\n"); } else { ppb->ppb_pwr_p->pwr_flags |= PCI_PWR_B3_CAPABLE; DEBUG0(DBG_PWR, pdip, "B3 supported via D3\n"); } ppb->ppb_pwr_p->pwr_dip = pdip; mutex_init(&ppb->ppb_pwr_p->pwr_mutex, NULL, MUTEX_DRIVER, NULL); i = 0; comp_array[i++] = "NAME=PCI bridge PM"; if (ppb->ppb_pwr_p->pwr_flags & PCI_PWR_B3_CAPABLE) { comp_array[i++] = "0=Clock/Power Off (B3)"; } if (ppb->ppb_pwr_p->pwr_flags & PCI_PWR_B2_CAPABLE) { comp_array[i++] = "1=Clock Off (B2)"; } if (ppb->ppb_pwr_p->pwr_flags & PCI_PWR_B1_CAPABLE) { comp_array[i++] = "2=Bus Inactive (B1)"; } comp_array[i++] = "3=Full Power (B0)"; /* * Create pm-components property. It does not already exist. */ if (ddi_prop_update_string_array(DDI_DEV_T_NONE, pdip, "pm-components", comp_array, i) != DDI_PROP_SUCCESS) { cmn_err(CE_WARN, "%s%d pm-components prop update failed", ddi_driver_name(pdip), ddi_get_instance(pdip)); pci_config_teardown(&conf_hdl); mutex_destroy(&ppb->ppb_pwr_p->pwr_mutex); kmem_free(ppb->ppb_pwr_p, sizeof (pci_pwr_t)); ppb->ppb_pwr_p = NULL; return; } if (ddi_prop_create(DDI_DEV_T_NONE, pdip, DDI_PROP_CANSLEEP, "pm-want-child-notification?", NULL, NULL) != DDI_PROP_SUCCESS) { cmn_err(CE_WARN, "%s%d fail to create pm-want-child-notification? prop", ddi_driver_name(pdip), ddi_get_instance(pdip)); (void) ddi_prop_remove(DDI_DEV_T_NONE, pdip, "pm-components"); pci_config_teardown(&conf_hdl); mutex_destroy(&ppb->ppb_pwr_p->pwr_mutex); kmem_free(ppb->ppb_pwr_p, sizeof (pci_pwr_t)); ppb->ppb_pwr_p = NULL; return; } ppb->ppb_pwr_p->current_lvl = pci_pwr_current_lvl(ppb->ppb_pwr_p); }
/* * usb_create_pm_components: * map descriptor into pm properties */ int usb_create_pm_components(dev_info_t *dip, uint_t *pwr_states) { uchar_t *usb_cfg; /* buf for config descriptor */ usb_cfg_descr_t cfg_descr; size_t cfg_length; usba_cfg_pwr_descr_t confpwr_descr; usba_if_pwr_descr_t ifpwr_descr; uint8_t cfg_attrib; int i, lvl, rval; int n_prop = 0; uint8_t *ptr; char *drvname; char str[USBA_POWER_STR_SIZE]; char *pm_comp[USBA_N_PMCOMP]; USBA_CHECK_CONTEXT(); if (usb_is_pm_enabled(dip) != USB_SUCCESS) { return (USB_FAILURE); } /* Obtain the raw configuration descriptor */ usb_cfg = usb_get_raw_cfg_data(dip, &cfg_length); /* get configuration descriptor, must succceed */ rval = usb_parse_cfg_descr(usb_cfg, cfg_length, &cfg_descr, USB_CFG_DESCR_SIZE); ASSERT(rval == USB_CFG_DESCR_SIZE); cfg_attrib = cfg_descr.bmAttributes; *pwr_states = 0; /* * Now start creating the pm-components strings */ drvname = (char *)ddi_driver_name(dip); (void) snprintf(str, USBA_POWER_STR_SIZE, "NAME= %s%d Power", drvname, ddi_get_instance(dip)); pm_comp[n_prop] = kmem_zalloc(strlen(str) + 1, KM_SLEEP); (void) strcpy(pm_comp[n_prop++], str); /* * if the device is bus powered we look at the bBusPowerSavingDx * fields else we look at bSelfPowerSavingDx fields. * OS and USB power states are numerically reversed, * * Here is the mapping :- * OS State USB State * 0 D3 (minimal or no power) * 1 D2 * 2 D1 * 3 D0 (Full power) * * if we own the whole device, we look at the config pwr descr * else at the interface pwr descr. */ if (usb_owns_device(dip)) { /* Parse the configuration power descriptor */ rval = usba_parse_cfg_pwr_descr(usb_cfg, cfg_length, &confpwr_descr, USBA_CFG_PWR_DESCR_SIZE); if (rval != USBA_CFG_PWR_DESCR_SIZE) { USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle, "usb_create_pm_components: " "usb_parse_cfg_pwr_descr returns length of %d, " "expecting %d", rval, USBA_CFG_PWR_DESCR_SIZE); return (USB_FAILURE); } if (cfg_attrib & USB_CFG_ATTR_SELFPWR) { ptr = &confpwr_descr.bSelfPowerSavingD3; } else { ptr = &confpwr_descr.bBusPowerSavingD3; } } else { /* Parse the interface power descriptor */ rval = usba_parse_if_pwr_descr(usb_cfg, cfg_length, usba_get_ifno(dip), /* interface index */ 0, /* XXXX alt interface index */ &ifpwr_descr, USBA_IF_PWR_DESCR_SIZE); if (rval != USBA_IF_PWR_DESCR_SIZE) { USB_DPRINTF_L2(DPRINT_MASK_USBAI, usbai_log_handle, "usb_create_pm_components: " "usb_parse_if_pwr_descr " "returns length of %d, " "expecting %d", rval, USBA_CFG_PWR_DESCR_SIZE); return (USB_FAILURE); } if (cfg_attrib & USB_CFG_ATTR_SELFPWR) { ptr = &ifpwr_descr.bSelfPowerSavingD3; } else { ptr = &ifpwr_descr.bBusPowerSavingD3; } } /* walk thru levels and create prop level=name strings */ for (lvl = USB_DEV_OS_PWR_0; lvl <= USB_DEV_OS_PWR_3; lvl++) { if (*ptr || (lvl == USB_DEV_OS_PWR_3)) { (void) snprintf(str, USBA_POWER_STR_SIZE, "%d=USB D%d State", lvl, USB_DEV_OS_PWR2USB_PWR(lvl)); pm_comp[n_prop] = kmem_zalloc(strlen(str) + 1, KM_SLEEP); (void) strcpy(pm_comp[n_prop++], str); *pwr_states |= USB_DEV_PWRMASK(lvl); } ptr -= 2; /* skip to the next power state */ } USB_DPRINTF_L3(DPRINT_MASK_USBAI, usbai_log_handle, "usb_create_pm_components: pwr_states: %x", *pwr_states); /* now create the actual components */ rval = ddi_prop_update_string_array(DDI_DEV_T_NONE, dip, "pm-components", pm_comp, n_prop); if (rval == DDI_PROP_SUCCESS) { rval = USB_SUCCESS; } else { rval = USB_FAILURE; } /* display & delete properties */ USB_DPRINTF_L3(DPRINT_MASK_USBAI, usbai_log_handle, "usb_create_pm_components: The properties are:"); for (i = 0; i < n_prop; i++) { USB_DPRINTF_L3(DPRINT_MASK_USBAI, usbai_log_handle, "\t%s", pm_comp[i]); kmem_free(pm_comp[i], strlen(pm_comp[i]) + 1); } 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); } }