static void ppb_uninitchild(dev_info_t *child) { ppb_devstate_t *ppb; ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state, ddi_get_instance(ddi_get_parent(child))); /* * SG OPL FMA specific */ if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) { pcie_fini_dom(child); pcie_fini_cfghdl(child); } ppb_removechild(child); }
static int ppb_initchild(dev_info_t *child) { char name[MAXNAMELEN]; ddi_acc_handle_t config_handle; ushort_t command_preserve, command; uint_t n; ushort_t bcr; uchar_t header_type; uchar_t min_gnt, latency_timer; ppb_devstate_t *ppb; /* * Name the child */ if (ppb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS) return (DDI_FAILURE); ddi_set_name_addr(child, name); ddi_set_parent_data(child, NULL); /* * Pseudo nodes indicate a prototype node with per-instance * properties to be merged into the real h/w device node. * The interpretation of the unit-address is DD[,F] * where DD is the device id and F is the function. */ if (ndi_dev_is_persistent_node(child) == 0) { extern int pci_allow_pseudo_children; /* * Try to merge the properties from this prototype * node into real h/w nodes. */ if (ndi_merge_node(child, ppb_name_child) == DDI_SUCCESS) { /* * Merged ok - return failure to remove the node. */ ppb_removechild(child); return (DDI_FAILURE); } /* workaround for ddivs to run under PCI */ if (pci_allow_pseudo_children) return (DDI_SUCCESS); /* * The child was not merged into a h/w node, * but there's not much we can do with it other * than return failure to cause the node to be removed. */ cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", ddi_driver_name(child), ddi_get_name_addr(child), ddi_driver_name(child)); ppb_removechild(child); return (DDI_NOT_WELL_FORMED); } ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state, ddi_get_instance(ddi_get_parent(child))); ddi_set_parent_data(child, NULL); /* * If hardware is PM capable, set up the power info structure. * This also ensures the the bus will not be off (0MHz) otherwise * system panics during a bus access. */ if (PM_CAPABLE(ppb->ppb_pwr_p)) { /* * Create a pwr_info struct for child. Bus will be * at full speed after creating info. */ pci_pwr_create_info(ppb->ppb_pwr_p, child); #ifdef DEBUG ASSERT(ppb->ppb_pwr_p->current_lvl == PM_LEVEL_B0); #endif } /* * If configuration registers were previously saved by * child (before it entered D3), then let the child do the * restore to set up the config regs as it'll first need to * power the device out of D3. */ if (ddi_prop_exists(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "config-regs-saved-by-child") == 1) { DEBUG2(DBG_PWR, ddi_get_parent(child), "INITCHILD: config regs to be restored by child" " for %s@%s\n", ddi_node_name(child), ddi_get_name_addr(child)); return (DDI_SUCCESS); } DEBUG2(DBG_PWR, ddi_get_parent(child), "INITCHILD: config regs setup for %s@%s\n", ddi_node_name(child), ddi_get_name_addr(child)); if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) { if (PM_CAPABLE(ppb->ppb_pwr_p)) { pci_pwr_rm_info(ppb->ppb_pwr_p, child); } return (DDI_FAILURE); } /* * Determine the configuration header type. */ header_type = pci_config_get8(config_handle, PCI_CONF_HEADER); /* * Support for the "command-preserve" property. */ command_preserve = ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "command-preserve", 0); command = pci_config_get16(config_handle, PCI_CONF_COMM); command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB); command |= (ppb_command_default & ~command_preserve); pci_config_put16(config_handle, PCI_CONF_COMM, command); /* * If the device has a bus control register then program it * based on the settings in the command register. */ if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) { bcr = pci_config_get8(config_handle, PCI_BCNF_BCNTRL); if (ppb_command_default & PCI_COMM_PARITY_DETECT) bcr |= PCI_BCNF_BCNTRL_PARITY_ENABLE; if (ppb_command_default & PCI_COMM_SERR_ENABLE) bcr |= PCI_BCNF_BCNTRL_SERR_ENABLE; bcr |= PCI_BCNF_BCNTRL_MAST_AB_MODE; pci_config_put8(config_handle, PCI_BCNF_BCNTRL, bcr); } /* * Initialize cache-line-size configuration register if needed. */ if (ppb_set_cache_line_size_register && ddi_getprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "cache-line-size", 0) == 0) { pci_config_put8(config_handle, PCI_CONF_CACHE_LINESZ, ppb->ppb_cache_line_size); n = pci_config_get8(config_handle, PCI_CONF_CACHE_LINESZ); if (n != 0) { (void) ndi_prop_update_int(DDI_DEV_T_NONE, child, "cache-line-size", n); } } /* * Initialize latency timer configuration registers if needed. */ if (ppb_set_latency_timer_register && ddi_getprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "latency-timer", 0) == 0) { if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) { latency_timer = ppb->ppb_latency_timer; pci_config_put8(config_handle, PCI_BCNF_LATENCY_TIMER, ppb->ppb_latency_timer); } else { min_gnt = pci_config_get8(config_handle, PCI_CONF_MIN_G); latency_timer = min_gnt * 8; } pci_config_put8(config_handle, PCI_CONF_LATENCY_TIMER, latency_timer); n = pci_config_get8(config_handle, PCI_CONF_LATENCY_TIMER); if (n != 0) { (void) ndi_prop_update_int(DDI_DEV_T_NONE, child, "latency-timer", n); } } /* * SPARC PCIe FMA specific * * Note: parent_data for parent is created only if this is sparc PCI-E * platform, for which, SG take a different route to handle device * errors. */ if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) { if (pcie_init_cfghdl(child) != DDI_SUCCESS) { pci_config_teardown(&config_handle); return (DDI_FAILURE); } pcie_init_dom(child); } /* * Check to see if the XMITS/PCI-X workaround applies. */ n = ddi_getprop(DDI_DEV_T_ANY, child, DDI_PROP_NOTPROM, "pcix-update-cmd-reg", -1); if (n != -1) { extern void pcix_set_cmd_reg(dev_info_t *child, uint16_t value); DEBUG1(DBG_INIT_CLD, child, "Turning on XMITS NCPQ " "Workaround: value = %x\n", n); pcix_set_cmd_reg(child, n); } pci_config_teardown(&config_handle); return (DDI_SUCCESS); }
/*ARGSUSED*/ static int ppb_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, void *arg, void *result) { pci_regspec_t *drv_regp; int reglen; int rn; int totreg; ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(dip)); struct detachspec *dsp; struct attachspec *asp; switch (ctlop) { case DDI_CTLOPS_REPORTDEV: if (rdip == (dev_info_t *)0) return (DDI_FAILURE); cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n", ddi_node_name(rdip), ddi_get_name_addr(rdip), ddi_driver_name(rdip), ddi_get_instance(rdip)); return (DDI_SUCCESS); case DDI_CTLOPS_INITCHILD: return (ppb_initchild((dev_info_t *)arg)); case DDI_CTLOPS_UNINITCHILD: ppb_removechild((dev_info_t *)arg); return (DDI_SUCCESS); case DDI_CTLOPS_SIDDEV: return (DDI_SUCCESS); case DDI_CTLOPS_REGSIZE: case DDI_CTLOPS_NREGS: if (rdip == (dev_info_t *)0) return (DDI_FAILURE); break; /* X86 systems support PME wakeup from suspend */ case DDI_CTLOPS_ATTACH: if (!pcie_is_child(dip, rdip)) return (DDI_SUCCESS); asp = (struct attachspec *)arg; if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) && (asp->when == DDI_POST) && (asp->result == DDI_SUCCESS)) pf_init(rdip, (void *)ppb->ppb_fm_ibc, asp->cmd); if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE) if (pci_pre_resume(rdip) != DDI_SUCCESS) return (DDI_FAILURE); return (DDI_SUCCESS); case DDI_CTLOPS_DETACH: if (!pcie_is_child(dip, rdip)) return (DDI_SUCCESS); dsp = (struct detachspec *)arg; if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) && (dsp->when == DDI_PRE)) pf_fini(rdip, dsp->cmd); if (dsp->cmd == DDI_SUSPEND && dsp->when == DDI_POST) if (pci_post_suspend(rdip) != DDI_SUCCESS) return (DDI_FAILURE); return (DDI_SUCCESS); case DDI_CTLOPS_PEEK: case DDI_CTLOPS_POKE: if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") != 0) return (ddi_ctlops(dip, rdip, ctlop, arg, result)); return (pci_peekpoke_check(dip, rdip, ctlop, arg, result, ddi_ctlops, &ppb->ppb_err_mutex, &ppb->ppb_peek_poke_mutex, ppb_peekpoke_cb)); default: return (ddi_ctlops(dip, rdip, ctlop, arg, result)); } *(int *)result = 0; if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg", (caddr_t)&drv_regp, ®len) != DDI_SUCCESS) return (DDI_FAILURE); totreg = reglen / sizeof (pci_regspec_t); if (ctlop == DDI_CTLOPS_NREGS) *(int *)result = totreg; else if (ctlop == DDI_CTLOPS_REGSIZE) { rn = *(int *)arg; if (rn >= totreg) { kmem_free(drv_regp, reglen); return (DDI_FAILURE); } *(off_t *)result = drv_regp[rn].pci_size_low; } kmem_free(drv_regp, reglen); return (DDI_SUCCESS); }