125cf1a30Sjl /* 225cf1a30Sjl * CDDL HEADER START 325cf1a30Sjl * 425cf1a30Sjl * The contents of this file are subject to the terms of the 525cf1a30Sjl * Common Development and Distribution License (the "License"). 625cf1a30Sjl * You may not use this file except in compliance with the License. 725cf1a30Sjl * 825cf1a30Sjl * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 925cf1a30Sjl * or http://www.opensolaris.org/os/licensing. 1025cf1a30Sjl * See the License for the specific language governing permissions 1125cf1a30Sjl * and limitations under the License. 1225cf1a30Sjl * 1325cf1a30Sjl * When distributing Covered Code, include this CDDL HEADER in each 1425cf1a30Sjl * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1525cf1a30Sjl * If applicable, add the following below this CDDL HEADER, with the 1625cf1a30Sjl * fields enclosed by brackets "[]" replaced with your own identifying 1725cf1a30Sjl * information: Portions Copyright [yyyy] [name of copyright owner] 1825cf1a30Sjl * 1925cf1a30Sjl * CDDL HEADER END 2025cf1a30Sjl */ 21*07d06da5SSurya Prakki 2225cf1a30Sjl /* 23*07d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2425cf1a30Sjl * Use is subject to license terms. 2525cf1a30Sjl */ 2625cf1a30Sjl 2725cf1a30Sjl /* 2825cf1a30Sjl * CMU-CH nexus utility routines: 2925cf1a30Sjl * property and config routines for attach() 3025cf1a30Sjl * reg/intr/range/assigned-address property routines for bus_map() 3125cf1a30Sjl * init_child() 3225cf1a30Sjl * fault handling 3325cf1a30Sjl * debug functions 3425cf1a30Sjl */ 3525cf1a30Sjl 3625cf1a30Sjl #include <sys/types.h> 3725cf1a30Sjl #include <sys/kmem.h> 3825cf1a30Sjl #include <sys/async.h> 3925cf1a30Sjl #include <sys/sysmacros.h> 4025cf1a30Sjl #include <sys/sunddi.h> 4125cf1a30Sjl #include <sys/sunndi.h> 4225cf1a30Sjl #include <sys/fm/protocol.h> 4325cf1a30Sjl #include <sys/fm/io/pci.h> 4425cf1a30Sjl #include <sys/fm/util.h> 4525cf1a30Sjl #include <sys/ddi_impldefs.h> 4625cf1a30Sjl #include <sys/pcicmu/pcicmu.h> 4725cf1a30Sjl #include <sys/promif.h> 4825cf1a30Sjl 4925cf1a30Sjl /* 5025cf1a30Sjl * get_pcmu_properties 5125cf1a30Sjl * 5225cf1a30Sjl * This function is called from the attach routine to get the key 5325cf1a30Sjl * properties of the pci nodes. 5425cf1a30Sjl * 5525cf1a30Sjl * used by: pcmu_attach() 5625cf1a30Sjl * 5725cf1a30Sjl * return value: DDI_FAILURE on failure 5825cf1a30Sjl */ 5925cf1a30Sjl int 6025cf1a30Sjl get_pcmu_properties(pcmu_t *pcmu_p, dev_info_t *dip) 6125cf1a30Sjl { 6225cf1a30Sjl int i; 6325cf1a30Sjl 6425cf1a30Sjl /* 6525cf1a30Sjl * Get the device's port id. 6625cf1a30Sjl */ 6725cf1a30Sjl if ((pcmu_p->pcmu_id = (uint32_t)pcmu_get_portid(dip)) == -1u) { 6825cf1a30Sjl cmn_err(CE_WARN, "%s%d: no portid property\n", 6925cf1a30Sjl ddi_driver_name(dip), ddi_get_instance(dip)); 7025cf1a30Sjl return (DDI_FAILURE); 7125cf1a30Sjl } 7225cf1a30Sjl 7325cf1a30Sjl /* 7425cf1a30Sjl * Get the bus-ranges property. 7525cf1a30Sjl */ 7625cf1a30Sjl i = sizeof (pcmu_p->pcmu_bus_range); 7725cf1a30Sjl if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 7825cf1a30Sjl "bus-range", (caddr_t)&pcmu_p->pcmu_bus_range, &i) != DDI_SUCCESS) { 7925cf1a30Sjl cmn_err(CE_WARN, "%s%d: no bus-range property\n", 8025cf1a30Sjl ddi_driver_name(dip), ddi_get_instance(dip)); 8125cf1a30Sjl return (DDI_FAILURE); 8225cf1a30Sjl } 8325cf1a30Sjl PCMU_DBG2(PCMU_DBG_ATTACH, dip, 8425cf1a30Sjl "get_pcmu_properties: bus-range (%x,%x)\n", 8525cf1a30Sjl pcmu_p->pcmu_bus_range.lo, pcmu_p->pcmu_bus_range.hi); 8625cf1a30Sjl 8725cf1a30Sjl /* 8825cf1a30Sjl * Get the ranges property. 8925cf1a30Sjl */ 9025cf1a30Sjl if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges", 9125cf1a30Sjl (caddr_t)&pcmu_p->pcmu_ranges, &pcmu_p->pcmu_ranges_length) != 9225cf1a30Sjl DDI_SUCCESS) { 9325cf1a30Sjl cmn_err(CE_WARN, "%s%d: no ranges property\n", 9425cf1a30Sjl ddi_driver_name(dip), ddi_get_instance(dip)); 9525cf1a30Sjl return (DDI_FAILURE); 9625cf1a30Sjl } 9725cf1a30Sjl pcmu_fix_ranges(pcmu_p->pcmu_ranges, 9825cf1a30Sjl pcmu_p->pcmu_ranges_length / sizeof (pcmu_ranges_t)); 9925cf1a30Sjl 10025cf1a30Sjl /* 10125cf1a30Sjl * Determine the number upa slot interrupts. 10225cf1a30Sjl */ 10325cf1a30Sjl pcmu_p->pcmu_numproxy = pcmu_get_numproxy(pcmu_p->pcmu_dip); 10425cf1a30Sjl PCMU_DBG1(PCMU_DBG_ATTACH, dip, "get_pcmu_properties: numproxy=%d\n", 10525cf1a30Sjl pcmu_p->pcmu_numproxy); 10625cf1a30Sjl return (DDI_SUCCESS); 10725cf1a30Sjl } 10825cf1a30Sjl 10925cf1a30Sjl /* 11025cf1a30Sjl * free_pcmu_properties: 11125cf1a30Sjl * 11225cf1a30Sjl * This routine frees the memory used to cache the 11325cf1a30Sjl * "ranges" properties of the pci bus device node. 11425cf1a30Sjl * 11525cf1a30Sjl * used by: pcmu_detach() 11625cf1a30Sjl * 11725cf1a30Sjl * return value: none 11825cf1a30Sjl */ 11925cf1a30Sjl void 12025cf1a30Sjl free_pcmu_properties(pcmu_t *pcmu_p) 12125cf1a30Sjl { 12225cf1a30Sjl kmem_free(pcmu_p->pcmu_ranges, pcmu_p->pcmu_ranges_length); 12325cf1a30Sjl } 12425cf1a30Sjl 12525cf1a30Sjl /* 12625cf1a30Sjl * pcmu_reloc_reg 12725cf1a30Sjl * 12825cf1a30Sjl * If the "reg" entry (*pcmu_rp) is relocatable, lookup "assigned-addresses" 12925cf1a30Sjl * property to fetch corresponding relocated address. 13025cf1a30Sjl * 13125cf1a30Sjl * used by: pcmu_map() 13225cf1a30Sjl * 13325cf1a30Sjl * return value: 13425cf1a30Sjl * 13525cf1a30Sjl * DDI_SUCCESS - on success 13625cf1a30Sjl * DDI_ME_INVAL - regspec is invalid 13725cf1a30Sjl */ 13825cf1a30Sjl int 13925cf1a30Sjl pcmu_reloc_reg(dev_info_t *dip, dev_info_t *rdip, pcmu_t *pcmu_p, 14025cf1a30Sjl pci_regspec_t *rp) 14125cf1a30Sjl { 14225cf1a30Sjl int assign_len, assign_entries, i; 14325cf1a30Sjl pci_regspec_t *assign_p; 14425cf1a30Sjl register uint32_t phys_hi = rp->pci_phys_hi; 14525cf1a30Sjl register uint32_t mask = PCI_REG_ADDR_M | PCI_CONF_ADDR_MASK; 14625cf1a30Sjl register uint32_t phys_addr = phys_hi & mask; 14725cf1a30Sjl 14825cf1a30Sjl PCMU_DBG5(PCMU_DBG_MAP | PCMU_DBG_CONT, dip, 14925cf1a30Sjl "\tpcmu_reloc_reg fr: %x.%x.%x %x.%x\n", 15025cf1a30Sjl rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low, 15125cf1a30Sjl rp->pci_size_hi, rp->pci_size_low); 15225cf1a30Sjl 15325cf1a30Sjl if ((phys_hi & PCI_RELOCAT_B) || !(phys_hi & PCI_ADDR_MASK)) { 15425cf1a30Sjl return (DDI_SUCCESS); 15525cf1a30Sjl } 15625cf1a30Sjl 15725cf1a30Sjl /* phys_mid must be 0 regardless space type. XXX-64 bit mem space */ 15825cf1a30Sjl if (rp->pci_phys_mid != 0 || rp->pci_size_hi != 0) { 15925cf1a30Sjl PCMU_DBG0(PCMU_DBG_MAP | PCMU_DBG_CONT, pcmu_p->pcmu_dip, 16025cf1a30Sjl "phys_mid or size_hi not 0\n"); 16125cf1a30Sjl return (DDI_ME_INVAL); 16225cf1a30Sjl } 16325cf1a30Sjl 16425cf1a30Sjl if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 16525cf1a30Sjl "assigned-addresses", (caddr_t)&assign_p, &assign_len)) { 16625cf1a30Sjl return (DDI_ME_INVAL); 16725cf1a30Sjl } 16825cf1a30Sjl 16925cf1a30Sjl assign_entries = assign_len / sizeof (pci_regspec_t); 17025cf1a30Sjl for (i = 0; i < assign_entries; i++, assign_p++) { 17125cf1a30Sjl if ((assign_p->pci_phys_hi & mask) == phys_addr) { 17225cf1a30Sjl rp->pci_phys_low += assign_p->pci_phys_low; 17325cf1a30Sjl break; 17425cf1a30Sjl } 17525cf1a30Sjl } 17625cf1a30Sjl kmem_free(assign_p - i, assign_len); 17725cf1a30Sjl PCMU_DBG5(PCMU_DBG_MAP | PCMU_DBG_CONT, dip, 17825cf1a30Sjl "\tpcmu_reloc_reg to: %x.%x.%x %x.%x\n", 17925cf1a30Sjl rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low, 18025cf1a30Sjl rp->pci_size_hi, rp->pci_size_low); 18125cf1a30Sjl return (i < assign_entries ? DDI_SUCCESS : DDI_ME_INVAL); 18225cf1a30Sjl } 18325cf1a30Sjl 18425cf1a30Sjl /* 18525cf1a30Sjl * use "ranges" to translate relocated pci regspec into parent space 18625cf1a30Sjl */ 18725cf1a30Sjl int 18825cf1a30Sjl pcmu_xlate_reg(pcmu_t *pcmu_p, pci_regspec_t *pcmu_rp, struct regspec *new_rp) 18925cf1a30Sjl { 19025cf1a30Sjl int n; 19125cf1a30Sjl pcmu_ranges_t *rng_p = pcmu_p->pcmu_ranges; 19225cf1a30Sjl int rng_n = pcmu_p->pcmu_ranges_length / sizeof (pcmu_ranges_t); 19325cf1a30Sjl 19425cf1a30Sjl uint32_t space_type = PCI_REG_ADDR_G(pcmu_rp->pci_phys_hi); 19525cf1a30Sjl uint32_t reg_end, reg_begin = pcmu_rp->pci_phys_low; 19625cf1a30Sjl uint32_t sz = pcmu_rp->pci_size_low; 19725cf1a30Sjl 19825cf1a30Sjl uint32_t rng_begin, rng_end; 19925cf1a30Sjl 20025cf1a30Sjl if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) { 20125cf1a30Sjl if (reg_begin > PCI_CONF_HDR_SIZE) { 20225cf1a30Sjl return (DDI_ME_INVAL); 20325cf1a30Sjl } 20425cf1a30Sjl sz = sz ? MIN(sz, PCI_CONF_HDR_SIZE) : PCI_CONF_HDR_SIZE; 20525cf1a30Sjl reg_begin += pcmu_rp->pci_phys_hi; 20625cf1a30Sjl } 20725cf1a30Sjl reg_end = reg_begin + sz - 1; 20825cf1a30Sjl 20925cf1a30Sjl for (n = 0; n < rng_n; n++, rng_p++) { 21025cf1a30Sjl if (space_type != PCI_REG_ADDR_G(rng_p->child_high)) { 21125cf1a30Sjl continue; /* not the same space type */ 21225cf1a30Sjl } 21325cf1a30Sjl 21425cf1a30Sjl rng_begin = rng_p->child_low; 21525cf1a30Sjl if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) { 21625cf1a30Sjl rng_begin += rng_p->child_high; 21725cf1a30Sjl } 21825cf1a30Sjl rng_end = rng_begin + rng_p->size_low - 1; 21925cf1a30Sjl if (reg_begin >= rng_begin && reg_end <= rng_end) { 22025cf1a30Sjl break; 22125cf1a30Sjl } 22225cf1a30Sjl } 22325cf1a30Sjl if (n >= rng_n) { 22425cf1a30Sjl return (DDI_ME_REGSPEC_RANGE); 22525cf1a30Sjl } 22625cf1a30Sjl 22725cf1a30Sjl new_rp->regspec_addr = reg_begin - rng_begin + rng_p->parent_low; 22825cf1a30Sjl new_rp->regspec_bustype = rng_p->parent_high; 22925cf1a30Sjl new_rp->regspec_size = sz; 23025cf1a30Sjl PCMU_DBG4(PCMU_DBG_MAP | PCMU_DBG_CONT, pcmu_p->pcmu_dip, 23125cf1a30Sjl "\tpcmu_xlate_reg: entry %d new_rp %x.%x %x\n", 23225cf1a30Sjl n, new_rp->regspec_bustype, new_rp->regspec_addr, sz); 23325cf1a30Sjl return (DDI_SUCCESS); 23425cf1a30Sjl } 23525cf1a30Sjl 23625cf1a30Sjl 23725cf1a30Sjl /* 23825cf1a30Sjl * pcmu_report_dev 23925cf1a30Sjl * 24025cf1a30Sjl * This function is called from our control ops routine on a 24125cf1a30Sjl * DDI_CTLOPS_REPORTDEV request. 24225cf1a30Sjl * 24325cf1a30Sjl * The display format is 24425cf1a30Sjl * 24525cf1a30Sjl * <name><inst> at <pname><pinst> device <dev> function <func> 24625cf1a30Sjl * 24725cf1a30Sjl * where 24825cf1a30Sjl * 24925cf1a30Sjl * <name> this device's name property 25025cf1a30Sjl * <inst> this device's instance number 25125cf1a30Sjl * <name> parent device's name property 25225cf1a30Sjl * <inst> parent device's instance number 25325cf1a30Sjl * <dev> this device's device number 25425cf1a30Sjl * <func> this device's function number 25525cf1a30Sjl */ 25625cf1a30Sjl int 25725cf1a30Sjl pcmu_report_dev(dev_info_t *dip) 25825cf1a30Sjl { 25925cf1a30Sjl if (dip == (dev_info_t *)0) { 26025cf1a30Sjl return (DDI_FAILURE); 26125cf1a30Sjl } 26225cf1a30Sjl cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n", ddi_node_name(dip), 26325cf1a30Sjl ddi_get_name_addr(dip), ddi_driver_name(dip), 26425cf1a30Sjl ddi_get_instance(dip)); 26525cf1a30Sjl return (DDI_SUCCESS); 26625cf1a30Sjl } 26725cf1a30Sjl 26825cf1a30Sjl /* 26925cf1a30Sjl * name_child 27025cf1a30Sjl * 27125cf1a30Sjl * This function is called from pcmu_init_child to name a node. It is 27225cf1a30Sjl * also passed as a callback for node merging functions. 27325cf1a30Sjl * 27425cf1a30Sjl * return value: DDI_SUCCESS, DDI_FAILURE 27525cf1a30Sjl */ 27625cf1a30Sjl static int 27725cf1a30Sjl name_child(dev_info_t *child, char *name, int namelen) 27825cf1a30Sjl { 27925cf1a30Sjl pci_regspec_t *pcmu_rp; 28025cf1a30Sjl int reglen; 28125cf1a30Sjl uint_t func; 28225cf1a30Sjl char **unit_addr; 28325cf1a30Sjl uint_t n; 28425cf1a30Sjl 28525cf1a30Sjl /* 28625cf1a30Sjl * Set the address portion of the node name based on 28725cf1a30Sjl * unit-address property, if it exists. 28825cf1a30Sjl * The interpretation of the unit-address is DD[,F] 28925cf1a30Sjl * where DD is the device id and F is the function. 29025cf1a30Sjl */ 29125cf1a30Sjl if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 29225cf1a30Sjl DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) == 29325cf1a30Sjl DDI_PROP_SUCCESS) { 29425cf1a30Sjl if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 29525cf1a30Sjl cmn_err(CE_WARN, "unit-address property in %s.conf" 29625cf1a30Sjl " not well-formed", ddi_driver_name(child)); 29725cf1a30Sjl ddi_prop_free(unit_addr); 29825cf1a30Sjl return (DDI_FAILURE); 29925cf1a30Sjl } 30025cf1a30Sjl (void) snprintf(name, namelen, "%s", *unit_addr); 30125cf1a30Sjl ddi_prop_free(unit_addr); 30225cf1a30Sjl return (DDI_SUCCESS); 30325cf1a30Sjl } 30425cf1a30Sjl 30525cf1a30Sjl /* 30625cf1a30Sjl * The unit-address property is does not exist. Set the address 30725cf1a30Sjl * portion of the node name based on the function and device number. 30825cf1a30Sjl */ 30925cf1a30Sjl if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 31025cf1a30Sjl "reg", (int **)&pcmu_rp, (uint_t *)®len) == DDI_SUCCESS) { 31125cf1a30Sjl if (((reglen * sizeof (int)) % sizeof (pci_regspec_t)) != 0) { 31225cf1a30Sjl cmn_err(CE_WARN, "reg property not well-formed"); 31325cf1a30Sjl return (DDI_FAILURE); 31425cf1a30Sjl } 31525cf1a30Sjl 31625cf1a30Sjl func = PCI_REG_FUNC_G(pcmu_rp[0].pci_phys_hi); 31725cf1a30Sjl if (func != 0) { 31825cf1a30Sjl (void) snprintf(name, namelen, "%x,%x", 319*07d06da5SSurya Prakki PCI_REG_DEV_G(pcmu_rp[0].pci_phys_hi), func); 32025cf1a30Sjl } else { 32125cf1a30Sjl (void) snprintf(name, namelen, "%x", 322*07d06da5SSurya Prakki PCI_REG_DEV_G(pcmu_rp[0].pci_phys_hi)); 32325cf1a30Sjl } 32425cf1a30Sjl ddi_prop_free(pcmu_rp); 32525cf1a30Sjl return (DDI_SUCCESS); 32625cf1a30Sjl } 32725cf1a30Sjl cmn_err(CE_WARN, "cannot name pci child '%s'", ddi_node_name(child)); 32825cf1a30Sjl return (DDI_FAILURE); 32925cf1a30Sjl } 33025cf1a30Sjl 33125cf1a30Sjl int 33225cf1a30Sjl pcmu_uninit_child(pcmu_t *pcmu_p, dev_info_t *child) 33325cf1a30Sjl { 33425cf1a30Sjl PCMU_DBG2(PCMU_DBG_CTLOPS, pcmu_p->pcmu_dip, 33525cf1a30Sjl "DDI_CTLOPS_UNINITCHILD: arg=%s%d\n", 33625cf1a30Sjl ddi_driver_name(child), ddi_get_instance(child)); 33725cf1a30Sjl 33825cf1a30Sjl ddi_set_name_addr(child, NULL); 33925cf1a30Sjl ddi_remove_minor_node(child, NULL); 34025cf1a30Sjl impl_rem_dev_props(child); 34125cf1a30Sjl 34225cf1a30Sjl PCMU_DBG0(PCMU_DBG_PWR, ddi_get_parent(child), "\n\n"); 34325cf1a30Sjl return (DDI_SUCCESS); 34425cf1a30Sjl } 34525cf1a30Sjl 34625cf1a30Sjl /* 34725cf1a30Sjl * pcmu_init_child 34825cf1a30Sjl * 34925cf1a30Sjl * This function is called from our control ops routine on a 35025cf1a30Sjl * DDI_CTLOPS_INITCHILD request. It builds and sets the device's 35125cf1a30Sjl * parent private data area. 35225cf1a30Sjl * 35325cf1a30Sjl * used by: pcmu_ctlops() 35425cf1a30Sjl * 35525cf1a30Sjl * return value: none 35625cf1a30Sjl */ 35725cf1a30Sjl int 35825cf1a30Sjl pcmu_init_child(pcmu_t *pcmu_p, dev_info_t *child) 35925cf1a30Sjl { 36025cf1a30Sjl char name[10]; 36125cf1a30Sjl ddi_acc_handle_t config_handle; 36225cf1a30Sjl uint8_t bcr; 36325cf1a30Sjl uint8_t header_type; 36425cf1a30Sjl 36525cf1a30Sjl if (name_child(child, name, 10) != DDI_SUCCESS) 36625cf1a30Sjl return (DDI_FAILURE); 36725cf1a30Sjl ddi_set_name_addr(child, name); 36825cf1a30Sjl 36925cf1a30Sjl PCMU_DBG2(PCMU_DBG_PWR, ddi_get_parent(child), 37025cf1a30Sjl "INITCHILD: config regs setup for %s@%s\n", 37125cf1a30Sjl ddi_node_name(child), ddi_get_name_addr(child)); 37225cf1a30Sjl 37325cf1a30Sjl /* 37425cf1a30Sjl * Map the child configuration space to for initialization. 37525cf1a30Sjl * We assume the obp will do the following in the devices 37625cf1a30Sjl * config space: 37725cf1a30Sjl * 37825cf1a30Sjl * Set the latency-timer register to values appropriate 37925cf1a30Sjl * for the devices on the bus (based on other devices 38025cf1a30Sjl * MIN_GNT and MAX_LAT registers. 38125cf1a30Sjl * 38225cf1a30Sjl * Set the fast back-to-back enable bit in the command 38325cf1a30Sjl * register if it's supported and all devices on the bus 38425cf1a30Sjl * have the capability. 38525cf1a30Sjl * 38625cf1a30Sjl */ 38725cf1a30Sjl if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) { 38825cf1a30Sjl ddi_set_name_addr(child, NULL); 38925cf1a30Sjl return (DDI_FAILURE); 39025cf1a30Sjl } 39125cf1a30Sjl 39225cf1a30Sjl /* 39325cf1a30Sjl * Determine the configuration header type. 39425cf1a30Sjl */ 39525cf1a30Sjl header_type = pci_config_get8(config_handle, PCI_CONF_HEADER); 39625cf1a30Sjl PCMU_DBG2(PCMU_DBG_INIT_CLD, pcmu_p->pcmu_dip, "%s: header_type=%x\n", 39725cf1a30Sjl ddi_driver_name(child), header_type); 39825cf1a30Sjl 39925cf1a30Sjl /* 40025cf1a30Sjl * If the device has a bus control register then program it 40125cf1a30Sjl * based on the settings in the command register. 40225cf1a30Sjl */ 40325cf1a30Sjl if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) { 40425cf1a30Sjl bcr = pci_config_get8(config_handle, PCI_BCNF_BCNTRL); 40525cf1a30Sjl if (pcmu_command_default & PCI_COMM_PARITY_DETECT) 40625cf1a30Sjl bcr |= PCI_BCNF_BCNTRL_PARITY_ENABLE; 40725cf1a30Sjl if (pcmu_command_default & PCI_COMM_SERR_ENABLE) 40825cf1a30Sjl bcr |= PCI_BCNF_BCNTRL_SERR_ENABLE; 40925cf1a30Sjl bcr |= PCI_BCNF_BCNTRL_MAST_AB_MODE; 41025cf1a30Sjl pci_config_put8(config_handle, PCI_BCNF_BCNTRL, bcr); 41125cf1a30Sjl } 41225cf1a30Sjl 41325cf1a30Sjl pci_config_teardown(&config_handle); 41425cf1a30Sjl return (DDI_SUCCESS); 41525cf1a30Sjl } 41625cf1a30Sjl 41725cf1a30Sjl /* 41825cf1a30Sjl * pcmu_get_reg_set_size 41925cf1a30Sjl * 42025cf1a30Sjl * Given a dev info pointer to a pci child and a register number, this 42125cf1a30Sjl * routine returns the size element of that reg set property. 42225cf1a30Sjl * 42325cf1a30Sjl * used by: pcmu_ctlops() - DDI_CTLOPS_REGSIZE 42425cf1a30Sjl * 42525cf1a30Sjl * return value: size of reg set on success, zero on error 42625cf1a30Sjl */ 42725cf1a30Sjl off_t 42825cf1a30Sjl pcmu_get_reg_set_size(dev_info_t *child, int rnumber) 42925cf1a30Sjl { 43025cf1a30Sjl pci_regspec_t *pcmu_rp; 43125cf1a30Sjl off_t size; 43225cf1a30Sjl int i; 43325cf1a30Sjl 43425cf1a30Sjl if (rnumber < 0) { 43525cf1a30Sjl return (0); 43625cf1a30Sjl } 43725cf1a30Sjl 43825cf1a30Sjl /* 43925cf1a30Sjl * Get the reg property for the device. 44025cf1a30Sjl */ 44125cf1a30Sjl if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", 44225cf1a30Sjl (caddr_t)&pcmu_rp, &i) != DDI_SUCCESS) { 44325cf1a30Sjl return (0); 44425cf1a30Sjl } 44525cf1a30Sjl 44625cf1a30Sjl if (rnumber >= (i / (int)sizeof (pci_regspec_t))) { 44725cf1a30Sjl kmem_free(pcmu_rp, i); 44825cf1a30Sjl return (0); 44925cf1a30Sjl } 45025cf1a30Sjl 45125cf1a30Sjl size = pcmu_rp[rnumber].pci_size_low | 452*07d06da5SSurya Prakki ((uint64_t)pcmu_rp[rnumber].pci_size_hi << 32); 45325cf1a30Sjl kmem_free(pcmu_rp, i); 45425cf1a30Sjl return (size); 45525cf1a30Sjl } 45625cf1a30Sjl 45725cf1a30Sjl 45825cf1a30Sjl /* 45925cf1a30Sjl * pcmu_get_nreg_set 46025cf1a30Sjl * 46125cf1a30Sjl * Given a dev info pointer to a pci child, this routine returns the 46225cf1a30Sjl * number of sets in its "reg" property. 46325cf1a30Sjl * 46425cf1a30Sjl * used by: pcmu_ctlops() - DDI_CTLOPS_NREGS 46525cf1a30Sjl * 46625cf1a30Sjl * return value: # of reg sets on success, zero on error 46725cf1a30Sjl */ 46825cf1a30Sjl uint_t 46925cf1a30Sjl pcmu_get_nreg_set(dev_info_t *child) 47025cf1a30Sjl { 47125cf1a30Sjl pci_regspec_t *pcmu_rp; 47225cf1a30Sjl int i, n; 47325cf1a30Sjl 47425cf1a30Sjl /* 47525cf1a30Sjl * Get the reg property for the device. 47625cf1a30Sjl */ 47725cf1a30Sjl if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", 47825cf1a30Sjl (caddr_t)&pcmu_rp, &i) != DDI_SUCCESS) { 47925cf1a30Sjl return (0); 48025cf1a30Sjl } 48125cf1a30Sjl n = i / (int)sizeof (pci_regspec_t); 48225cf1a30Sjl kmem_free(pcmu_rp, i); 48325cf1a30Sjl return (n); 48425cf1a30Sjl } 48525cf1a30Sjl 48625cf1a30Sjl int 48725cf1a30Sjl pcmu_cfg_report(dev_info_t *dip, ddi_fm_error_t *derr, 48825cf1a30Sjl pcmu_errstate_t *pcmu_err_p, int caller, uint32_t prierr) 48925cf1a30Sjl { 49025cf1a30Sjl int fatal = 0; 49125cf1a30Sjl int nonfatal = 0; 49225cf1a30Sjl int i; 49325cf1a30Sjl pcmu_t *pcmu_p; 49425cf1a30Sjl int instance = ddi_get_instance(dip); 49525cf1a30Sjl 49625cf1a30Sjl ASSERT(dip); 49725cf1a30Sjl 49825cf1a30Sjl pcmu_p = get_pcmu_soft_state(instance); 49925cf1a30Sjl 50025cf1a30Sjl derr->fme_ena = derr->fme_ena ? derr->fme_ena : 50125cf1a30Sjl fm_ena_generate(0, FM_ENA_FMT1); 50225cf1a30Sjl 50325cf1a30Sjl for (i = 0; pci_err_tbl[i].err_class != NULL; i++) { 50425cf1a30Sjl if (pcmu_err_p->pcmu_cfg_stat & pci_err_tbl[i].reg_bit) { 50525cf1a30Sjl char buf[FM_MAX_CLASS]; 50625cf1a30Sjl char *aux_msg = NULL; 50725cf1a30Sjl 50825cf1a30Sjl switch (pci_err_tbl[i].reg_bit) { 50925cf1a30Sjl case PCI_STAT_R_MAST_AB: 51025cf1a30Sjl aux_msg = "Recieved Master Abort"; 51125cf1a30Sjl /* LINTED fallthrough on case statement */ 51225cf1a30Sjl case PCI_STAT_R_TARG_AB: 51325cf1a30Sjl if (aux_msg != NULL) 51425cf1a30Sjl aux_msg = "Recieved Target Abort"; 51525cf1a30Sjl if (prierr) { 51625cf1a30Sjl /* 51725cf1a30Sjl * piow case are already handled in 51825cf1a30Sjl * pcmu_pbm_afsr_report() 51925cf1a30Sjl */ 52025cf1a30Sjl break; 52125cf1a30Sjl } 52225cf1a30Sjl if (caller != PCI_TRAP_CALL) { 52325cf1a30Sjl /* 52425cf1a30Sjl * if we haven't come from trap handler 52525cf1a30Sjl * we won't have an address 52625cf1a30Sjl */ 52725cf1a30Sjl fatal++; 52825cf1a30Sjl } 52925cf1a30Sjl break; 53025cf1a30Sjl default: 53125cf1a30Sjl /* 53225cf1a30Sjl * dpe on dma write or ta on dma 53325cf1a30Sjl */ 53425cf1a30Sjl nonfatal++; 53525cf1a30Sjl break; 53625cf1a30Sjl } 53725cf1a30Sjl (void) snprintf(buf, FM_MAX_CLASS, "%s %s: %s %s", 53825cf1a30Sjl (pcmu_p->pcmu_pcbm_p)->pcbm_nameinst_str, 53925cf1a30Sjl (pcmu_p->pcmu_pcbm_p)->pcbm_nameaddr_str, 54025cf1a30Sjl "PCI config space:", aux_msg); 541*07d06da5SSurya Prakki cmn_err(CE_WARN, "%s %s=0x%p", buf, "pbm-csr", 542*07d06da5SSurya Prakki (void *)(pcmu_p->pcmu_pcbm_p)->pcbm_ctrl_reg); 54325cf1a30Sjl } 54425cf1a30Sjl } 54525cf1a30Sjl 54625cf1a30Sjl if (fatal) 54725cf1a30Sjl return (DDI_FM_FATAL); 54825cf1a30Sjl else if (nonfatal) 54925cf1a30Sjl return (DDI_FM_NONFATAL); 55025cf1a30Sjl 55125cf1a30Sjl return (DDI_FM_OK); 55225cf1a30Sjl } 55325cf1a30Sjl 55425cf1a30Sjl void 55525cf1a30Sjl pcmu_child_cfg_save(dev_info_t *dip) 55625cf1a30Sjl { 55725cf1a30Sjl dev_info_t *cdip; 55825cf1a30Sjl int ret = DDI_SUCCESS; 55925cf1a30Sjl 56025cf1a30Sjl /* 56125cf1a30Sjl * Save the state of the configuration headers of child 56225cf1a30Sjl * nodes. 56325cf1a30Sjl */ 56425cf1a30Sjl 56525cf1a30Sjl for (cdip = ddi_get_child(dip); cdip != NULL; 56625cf1a30Sjl cdip = ddi_get_next_sibling(cdip)) { 56725cf1a30Sjl 56825cf1a30Sjl /* 56925cf1a30Sjl * Not interested in children who are not already 57025cf1a30Sjl * init'ed. They will be set up in pcmu_init_child(). 57125cf1a30Sjl */ 57225cf1a30Sjl if (i_ddi_node_state(cdip) < DS_INITIALIZED) { 57325cf1a30Sjl PCMU_DBG2(PCMU_DBG_DETACH, dip, "DDI_SUSPEND: skipping " 57425cf1a30Sjl "%s%d not in CF1\n", ddi_driver_name(cdip), 57525cf1a30Sjl ddi_get_instance(cdip)); 57625cf1a30Sjl 57725cf1a30Sjl continue; 57825cf1a30Sjl } 57925cf1a30Sjl 58025cf1a30Sjl /* 58125cf1a30Sjl * Only save config registers if not already saved by child. 58225cf1a30Sjl */ 58325cf1a30Sjl if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, 58425cf1a30Sjl SAVED_CONFIG_REGS) == 1) { 58525cf1a30Sjl 58625cf1a30Sjl continue; 58725cf1a30Sjl } 58825cf1a30Sjl 58925cf1a30Sjl /* 59025cf1a30Sjl * The nexus needs to save config registers. Create a property 59125cf1a30Sjl * so it knows to restore on resume. 59225cf1a30Sjl */ 59325cf1a30Sjl ret = ndi_prop_create_boolean(DDI_DEV_T_NONE, cdip, 59425cf1a30Sjl "nexus-saved-config-regs"); 59525cf1a30Sjl 59625cf1a30Sjl if (ret != DDI_PROP_SUCCESS) { 59725cf1a30Sjl cmn_err(CE_WARN, "%s%d can't update prop %s", 59825cf1a30Sjl ddi_driver_name(cdip), ddi_get_instance(cdip), 59925cf1a30Sjl "nexus-saved-config-regs"); 60025cf1a30Sjl } 60125cf1a30Sjl 60225cf1a30Sjl (void) pci_save_config_regs(cdip); 60325cf1a30Sjl } 60425cf1a30Sjl } 60525cf1a30Sjl 60625cf1a30Sjl void 60725cf1a30Sjl pcmu_child_cfg_restore(dev_info_t *dip) 60825cf1a30Sjl { 60925cf1a30Sjl dev_info_t *cdip; 61025cf1a30Sjl 61125cf1a30Sjl /* 61225cf1a30Sjl * Restore config registers for children that did not save 61325cf1a30Sjl * their own registers. Children pwr states are UNKNOWN after 61425cf1a30Sjl * a resume since it is possible for the PM framework to call 61525cf1a30Sjl * resume without an actual power cycle. (ie if suspend fails). 61625cf1a30Sjl */ 61725cf1a30Sjl for (cdip = ddi_get_child(dip); cdip != NULL; 61825cf1a30Sjl cdip = ddi_get_next_sibling(cdip)) { 61925cf1a30Sjl 62025cf1a30Sjl /* 62125cf1a30Sjl * Not interested in children who are not already 62225cf1a30Sjl * init'ed. They will be set up by pcmu_init_child(). 62325cf1a30Sjl */ 62425cf1a30Sjl if (i_ddi_node_state(cdip) < DS_INITIALIZED) { 62525cf1a30Sjl PCMU_DBG2(PCMU_DBG_DETACH, dip, 62625cf1a30Sjl "DDI_RESUME: skipping %s%d not in CF1\n", 62725cf1a30Sjl ddi_driver_name(cdip), ddi_get_instance(cdip)); 62825cf1a30Sjl continue; 62925cf1a30Sjl } 63025cf1a30Sjl 63125cf1a30Sjl /* 63225cf1a30Sjl * Only restore config registers if saved by nexus. 63325cf1a30Sjl */ 63425cf1a30Sjl if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, 63525cf1a30Sjl "nexus-saved-config-regs") == 1) { 63625cf1a30Sjl (void) pci_restore_config_regs(cdip); 63725cf1a30Sjl 63825cf1a30Sjl PCMU_DBG2(PCMU_DBG_PWR, dip, 63925cf1a30Sjl "DDI_RESUME: nexus restoring %s%d config regs\n", 64025cf1a30Sjl ddi_driver_name(cdip), ddi_get_instance(cdip)); 64125cf1a30Sjl 64225cf1a30Sjl if (ndi_prop_remove(DDI_DEV_T_NONE, cdip, 64325cf1a30Sjl "nexus-saved-config-regs") != DDI_PROP_SUCCESS) { 64425cf1a30Sjl cmn_err(CE_WARN, "%s%d can't remove prop %s", 64525cf1a30Sjl ddi_driver_name(cdip), 64625cf1a30Sjl ddi_get_instance(cdip), 64725cf1a30Sjl "nexus-saved-config-regs"); 64825cf1a30Sjl } 64925cf1a30Sjl } 65025cf1a30Sjl } 65125cf1a30Sjl } 65225cf1a30Sjl 65325cf1a30Sjl #ifdef DEBUG 65425cf1a30Sjl extern uint64_t pcmu_debug_flags; 65525cf1a30Sjl 65625cf1a30Sjl pcmu_dflag_to_str_t pcmu_dflag_strings [] = { 65725cf1a30Sjl {PCMU_DBG_ATTACH, "pcmu_attach"}, 65825cf1a30Sjl {PCMU_DBG_DETACH, "pcmu_detach"}, 65925cf1a30Sjl {PCMU_DBG_MAP, "pcmu_map"}, 66025cf1a30Sjl {PCMU_DBG_A_INTX, "pcmu_add_intx"}, 66125cf1a30Sjl {PCMU_DBG_R_INTX, "pcmu_rem_intx"}, 66225cf1a30Sjl {PCMU_DBG_INIT_CLD, "pcmu_init_child"}, 66325cf1a30Sjl {PCMU_DBG_CTLOPS, "pcmu_ctlops"}, 66425cf1a30Sjl {PCMU_DBG_INTR, "pcmu_intr_wrapper"}, 66525cf1a30Sjl {PCMU_DBG_ERR_INTR, "pcmu_pbm_error_intr"}, 66625cf1a30Sjl {PCMU_DBG_BUS_FAULT, "pcmu_fault"}, 66725cf1a30Sjl {PCMU_DBG_IB, "pcmu_ib"}, 66825cf1a30Sjl {PCMU_DBG_CB, "pcmu_cb"}, 66925cf1a30Sjl {PCMU_DBG_PBM, "pcmu_pbm"}, 67025cf1a30Sjl {PCMU_DBG_OPEN, "pcmu_open"}, 67125cf1a30Sjl {PCMU_DBG_CLOSE, "pcmu_close"}, 67225cf1a30Sjl {PCMU_DBG_IOCTL, "pcmu_ioctl"}, 67325cf1a30Sjl {PCMU_DBG_PWR, "pcmu_pwr"} 67425cf1a30Sjl }; 67525cf1a30Sjl 67625cf1a30Sjl void 67725cf1a30Sjl pcmu_debug(uint64_t flag, dev_info_t *dip, char *fmt, 67825cf1a30Sjl uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5) 67925cf1a30Sjl { 68025cf1a30Sjl char *s = "pcmu unknown"; 68125cf1a30Sjl uint_t cont = 0; 68225cf1a30Sjl int i; 68325cf1a30Sjl int no_rec = (sizeof (pcmu_dflag_strings) / 68425cf1a30Sjl sizeof (pcmu_dflag_to_str_t)); 68525cf1a30Sjl 68625cf1a30Sjl if (flag & PCMU_DBG_CONT) { 68725cf1a30Sjl flag &= ~PCMU_DBG_CONT; 68825cf1a30Sjl cont = 1; 68925cf1a30Sjl } 69025cf1a30Sjl if ((pcmu_debug_flags & flag) == flag) { 69125cf1a30Sjl for (i = 0; i < no_rec; i++) { 69225cf1a30Sjl if (pcmu_dflag_strings[i].flag == flag) { 69325cf1a30Sjl s = pcmu_dflag_strings[i].string; 69425cf1a30Sjl break; 69525cf1a30Sjl } 69625cf1a30Sjl } 69725cf1a30Sjl if (s && cont == 0) { 69825cf1a30Sjl prom_printf("%s(%d): %s: ", ddi_driver_name(dip), 69925cf1a30Sjl ddi_get_instance(dip), s); 70025cf1a30Sjl } 70125cf1a30Sjl prom_printf(fmt, a1, a2, a3, a4, a5); 70225cf1a30Sjl } 70325cf1a30Sjl } 70425cf1a30Sjl #endif 705