11772Sjl139090 /*
21772Sjl139090 * CDDL HEADER START
31772Sjl139090 *
41772Sjl139090 * The contents of this file are subject to the terms of the
51772Sjl139090 * Common Development and Distribution License (the "License").
61772Sjl139090 * You may not use this file except in compliance with the License.
71772Sjl139090 *
81772Sjl139090 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91772Sjl139090 * or http://www.opensolaris.org/os/licensing.
101772Sjl139090 * See the License for the specific language governing permissions
111772Sjl139090 * and limitations under the License.
121772Sjl139090 *
131772Sjl139090 * When distributing Covered Code, include this CDDL HEADER in each
141772Sjl139090 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151772Sjl139090 * If applicable, add the following below this CDDL HEADER, with the
161772Sjl139090 * fields enclosed by brackets "[]" replaced with your own identifying
171772Sjl139090 * information: Portions Copyright [yyyy] [name of copyright owner]
181772Sjl139090 *
191772Sjl139090 * CDDL HEADER END
201772Sjl139090 */
21*11311SSurya.Prakki@Sun.COM
221772Sjl139090 /*
23*11311SSurya.Prakki@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
241772Sjl139090 * Use is subject to license terms.
251772Sjl139090 */
261772Sjl139090
271772Sjl139090 /*
281772Sjl139090 * CMU-CH nexus utility routines:
291772Sjl139090 * property and config routines for attach()
301772Sjl139090 * reg/intr/range/assigned-address property routines for bus_map()
311772Sjl139090 * init_child()
321772Sjl139090 * fault handling
331772Sjl139090 * debug functions
341772Sjl139090 */
351772Sjl139090
361772Sjl139090 #include <sys/types.h>
371772Sjl139090 #include <sys/kmem.h>
381772Sjl139090 #include <sys/async.h>
391772Sjl139090 #include <sys/sysmacros.h>
401772Sjl139090 #include <sys/sunddi.h>
411772Sjl139090 #include <sys/sunndi.h>
421772Sjl139090 #include <sys/fm/protocol.h>
431772Sjl139090 #include <sys/fm/io/pci.h>
441772Sjl139090 #include <sys/fm/util.h>
451772Sjl139090 #include <sys/ddi_impldefs.h>
461772Sjl139090 #include <sys/pcicmu/pcicmu.h>
471772Sjl139090 #include <sys/promif.h>
481772Sjl139090
491772Sjl139090 /*
501772Sjl139090 * get_pcmu_properties
511772Sjl139090 *
521772Sjl139090 * This function is called from the attach routine to get the key
531772Sjl139090 * properties of the pci nodes.
541772Sjl139090 *
551772Sjl139090 * used by: pcmu_attach()
561772Sjl139090 *
571772Sjl139090 * return value: DDI_FAILURE on failure
581772Sjl139090 */
591772Sjl139090 int
get_pcmu_properties(pcmu_t * pcmu_p,dev_info_t * dip)601772Sjl139090 get_pcmu_properties(pcmu_t *pcmu_p, dev_info_t *dip)
611772Sjl139090 {
621772Sjl139090 int i;
631772Sjl139090
641772Sjl139090 /*
651772Sjl139090 * Get the device's port id.
661772Sjl139090 */
671772Sjl139090 if ((pcmu_p->pcmu_id = (uint32_t)pcmu_get_portid(dip)) == -1u) {
681772Sjl139090 cmn_err(CE_WARN, "%s%d: no portid property\n",
691772Sjl139090 ddi_driver_name(dip), ddi_get_instance(dip));
701772Sjl139090 return (DDI_FAILURE);
711772Sjl139090 }
721772Sjl139090
731772Sjl139090 /*
741772Sjl139090 * Get the bus-ranges property.
751772Sjl139090 */
761772Sjl139090 i = sizeof (pcmu_p->pcmu_bus_range);
771772Sjl139090 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
781772Sjl139090 "bus-range", (caddr_t)&pcmu_p->pcmu_bus_range, &i) != DDI_SUCCESS) {
791772Sjl139090 cmn_err(CE_WARN, "%s%d: no bus-range property\n",
801772Sjl139090 ddi_driver_name(dip), ddi_get_instance(dip));
811772Sjl139090 return (DDI_FAILURE);
821772Sjl139090 }
831772Sjl139090 PCMU_DBG2(PCMU_DBG_ATTACH, dip,
841772Sjl139090 "get_pcmu_properties: bus-range (%x,%x)\n",
851772Sjl139090 pcmu_p->pcmu_bus_range.lo, pcmu_p->pcmu_bus_range.hi);
861772Sjl139090
871772Sjl139090 /*
881772Sjl139090 * Get the ranges property.
891772Sjl139090 */
901772Sjl139090 if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges",
911772Sjl139090 (caddr_t)&pcmu_p->pcmu_ranges, &pcmu_p->pcmu_ranges_length) !=
921772Sjl139090 DDI_SUCCESS) {
931772Sjl139090 cmn_err(CE_WARN, "%s%d: no ranges property\n",
941772Sjl139090 ddi_driver_name(dip), ddi_get_instance(dip));
951772Sjl139090 return (DDI_FAILURE);
961772Sjl139090 }
971772Sjl139090 pcmu_fix_ranges(pcmu_p->pcmu_ranges,
981772Sjl139090 pcmu_p->pcmu_ranges_length / sizeof (pcmu_ranges_t));
991772Sjl139090
1001772Sjl139090 /*
1011772Sjl139090 * Determine the number upa slot interrupts.
1021772Sjl139090 */
1031772Sjl139090 pcmu_p->pcmu_numproxy = pcmu_get_numproxy(pcmu_p->pcmu_dip);
1041772Sjl139090 PCMU_DBG1(PCMU_DBG_ATTACH, dip, "get_pcmu_properties: numproxy=%d\n",
1051772Sjl139090 pcmu_p->pcmu_numproxy);
1061772Sjl139090 return (DDI_SUCCESS);
1071772Sjl139090 }
1081772Sjl139090
1091772Sjl139090 /*
1101772Sjl139090 * free_pcmu_properties:
1111772Sjl139090 *
1121772Sjl139090 * This routine frees the memory used to cache the
1131772Sjl139090 * "ranges" properties of the pci bus device node.
1141772Sjl139090 *
1151772Sjl139090 * used by: pcmu_detach()
1161772Sjl139090 *
1171772Sjl139090 * return value: none
1181772Sjl139090 */
1191772Sjl139090 void
free_pcmu_properties(pcmu_t * pcmu_p)1201772Sjl139090 free_pcmu_properties(pcmu_t *pcmu_p)
1211772Sjl139090 {
1221772Sjl139090 kmem_free(pcmu_p->pcmu_ranges, pcmu_p->pcmu_ranges_length);
1231772Sjl139090 }
1241772Sjl139090
1251772Sjl139090 /*
1261772Sjl139090 * pcmu_reloc_reg
1271772Sjl139090 *
1281772Sjl139090 * If the "reg" entry (*pcmu_rp) is relocatable, lookup "assigned-addresses"
1291772Sjl139090 * property to fetch corresponding relocated address.
1301772Sjl139090 *
1311772Sjl139090 * used by: pcmu_map()
1321772Sjl139090 *
1331772Sjl139090 * return value:
1341772Sjl139090 *
1351772Sjl139090 * DDI_SUCCESS - on success
1361772Sjl139090 * DDI_ME_INVAL - regspec is invalid
1371772Sjl139090 */
1381772Sjl139090 int
pcmu_reloc_reg(dev_info_t * dip,dev_info_t * rdip,pcmu_t * pcmu_p,pci_regspec_t * rp)1391772Sjl139090 pcmu_reloc_reg(dev_info_t *dip, dev_info_t *rdip, pcmu_t *pcmu_p,
1401772Sjl139090 pci_regspec_t *rp)
1411772Sjl139090 {
1421772Sjl139090 int assign_len, assign_entries, i;
1431772Sjl139090 pci_regspec_t *assign_p;
1441772Sjl139090 register uint32_t phys_hi = rp->pci_phys_hi;
1451772Sjl139090 register uint32_t mask = PCI_REG_ADDR_M | PCI_CONF_ADDR_MASK;
1461772Sjl139090 register uint32_t phys_addr = phys_hi & mask;
1471772Sjl139090
1481772Sjl139090 PCMU_DBG5(PCMU_DBG_MAP | PCMU_DBG_CONT, dip,
1491772Sjl139090 "\tpcmu_reloc_reg fr: %x.%x.%x %x.%x\n",
1501772Sjl139090 rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low,
1511772Sjl139090 rp->pci_size_hi, rp->pci_size_low);
1521772Sjl139090
1531772Sjl139090 if ((phys_hi & PCI_RELOCAT_B) || !(phys_hi & PCI_ADDR_MASK)) {
1541772Sjl139090 return (DDI_SUCCESS);
1551772Sjl139090 }
1561772Sjl139090
1571772Sjl139090 /* phys_mid must be 0 regardless space type. XXX-64 bit mem space */
1581772Sjl139090 if (rp->pci_phys_mid != 0 || rp->pci_size_hi != 0) {
1591772Sjl139090 PCMU_DBG0(PCMU_DBG_MAP | PCMU_DBG_CONT, pcmu_p->pcmu_dip,
1601772Sjl139090 "phys_mid or size_hi not 0\n");
1611772Sjl139090 return (DDI_ME_INVAL);
1621772Sjl139090 }
1631772Sjl139090
1641772Sjl139090 if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
1651772Sjl139090 "assigned-addresses", (caddr_t)&assign_p, &assign_len)) {
1661772Sjl139090 return (DDI_ME_INVAL);
1671772Sjl139090 }
1681772Sjl139090
1691772Sjl139090 assign_entries = assign_len / sizeof (pci_regspec_t);
1701772Sjl139090 for (i = 0; i < assign_entries; i++, assign_p++) {
1711772Sjl139090 if ((assign_p->pci_phys_hi & mask) == phys_addr) {
1721772Sjl139090 rp->pci_phys_low += assign_p->pci_phys_low;
1731772Sjl139090 break;
1741772Sjl139090 }
1751772Sjl139090 }
1761772Sjl139090 kmem_free(assign_p - i, assign_len);
1771772Sjl139090 PCMU_DBG5(PCMU_DBG_MAP | PCMU_DBG_CONT, dip,
1781772Sjl139090 "\tpcmu_reloc_reg to: %x.%x.%x %x.%x\n",
1791772Sjl139090 rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low,
1801772Sjl139090 rp->pci_size_hi, rp->pci_size_low);
1811772Sjl139090 return (i < assign_entries ? DDI_SUCCESS : DDI_ME_INVAL);
1821772Sjl139090 }
1831772Sjl139090
1841772Sjl139090 /*
1851772Sjl139090 * use "ranges" to translate relocated pci regspec into parent space
1861772Sjl139090 */
1871772Sjl139090 int
pcmu_xlate_reg(pcmu_t * pcmu_p,pci_regspec_t * pcmu_rp,struct regspec * new_rp)1881772Sjl139090 pcmu_xlate_reg(pcmu_t *pcmu_p, pci_regspec_t *pcmu_rp, struct regspec *new_rp)
1891772Sjl139090 {
1901772Sjl139090 int n;
1911772Sjl139090 pcmu_ranges_t *rng_p = pcmu_p->pcmu_ranges;
1921772Sjl139090 int rng_n = pcmu_p->pcmu_ranges_length / sizeof (pcmu_ranges_t);
1931772Sjl139090
1941772Sjl139090 uint32_t space_type = PCI_REG_ADDR_G(pcmu_rp->pci_phys_hi);
1951772Sjl139090 uint32_t reg_end, reg_begin = pcmu_rp->pci_phys_low;
1961772Sjl139090 uint32_t sz = pcmu_rp->pci_size_low;
1971772Sjl139090
1981772Sjl139090 uint32_t rng_begin, rng_end;
1991772Sjl139090
2001772Sjl139090 if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) {
2011772Sjl139090 if (reg_begin > PCI_CONF_HDR_SIZE) {
2021772Sjl139090 return (DDI_ME_INVAL);
2031772Sjl139090 }
2041772Sjl139090 sz = sz ? MIN(sz, PCI_CONF_HDR_SIZE) : PCI_CONF_HDR_SIZE;
2051772Sjl139090 reg_begin += pcmu_rp->pci_phys_hi;
2061772Sjl139090 }
2071772Sjl139090 reg_end = reg_begin + sz - 1;
2081772Sjl139090
2091772Sjl139090 for (n = 0; n < rng_n; n++, rng_p++) {
2101772Sjl139090 if (space_type != PCI_REG_ADDR_G(rng_p->child_high)) {
2111772Sjl139090 continue; /* not the same space type */
2121772Sjl139090 }
2131772Sjl139090
2141772Sjl139090 rng_begin = rng_p->child_low;
2151772Sjl139090 if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) {
2161772Sjl139090 rng_begin += rng_p->child_high;
2171772Sjl139090 }
2181772Sjl139090 rng_end = rng_begin + rng_p->size_low - 1;
2191772Sjl139090 if (reg_begin >= rng_begin && reg_end <= rng_end) {
2201772Sjl139090 break;
2211772Sjl139090 }
2221772Sjl139090 }
2231772Sjl139090 if (n >= rng_n) {
2241772Sjl139090 return (DDI_ME_REGSPEC_RANGE);
2251772Sjl139090 }
2261772Sjl139090
2271772Sjl139090 new_rp->regspec_addr = reg_begin - rng_begin + rng_p->parent_low;
2281772Sjl139090 new_rp->regspec_bustype = rng_p->parent_high;
2291772Sjl139090 new_rp->regspec_size = sz;
2301772Sjl139090 PCMU_DBG4(PCMU_DBG_MAP | PCMU_DBG_CONT, pcmu_p->pcmu_dip,
2311772Sjl139090 "\tpcmu_xlate_reg: entry %d new_rp %x.%x %x\n",
2321772Sjl139090 n, new_rp->regspec_bustype, new_rp->regspec_addr, sz);
2331772Sjl139090 return (DDI_SUCCESS);
2341772Sjl139090 }
2351772Sjl139090
2361772Sjl139090
2371772Sjl139090 /*
2381772Sjl139090 * pcmu_report_dev
2391772Sjl139090 *
2401772Sjl139090 * This function is called from our control ops routine on a
2411772Sjl139090 * DDI_CTLOPS_REPORTDEV request.
2421772Sjl139090 *
2431772Sjl139090 * The display format is
2441772Sjl139090 *
2451772Sjl139090 * <name><inst> at <pname><pinst> device <dev> function <func>
2461772Sjl139090 *
2471772Sjl139090 * where
2481772Sjl139090 *
2491772Sjl139090 * <name> this device's name property
2501772Sjl139090 * <inst> this device's instance number
2511772Sjl139090 * <name> parent device's name property
2521772Sjl139090 * <inst> parent device's instance number
2531772Sjl139090 * <dev> this device's device number
2541772Sjl139090 * <func> this device's function number
2551772Sjl139090 */
2561772Sjl139090 int
pcmu_report_dev(dev_info_t * dip)2571772Sjl139090 pcmu_report_dev(dev_info_t *dip)
2581772Sjl139090 {
2591772Sjl139090 if (dip == (dev_info_t *)0) {
2601772Sjl139090 return (DDI_FAILURE);
2611772Sjl139090 }
2621772Sjl139090 cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n", ddi_node_name(dip),
2631772Sjl139090 ddi_get_name_addr(dip), ddi_driver_name(dip),
2641772Sjl139090 ddi_get_instance(dip));
2651772Sjl139090 return (DDI_SUCCESS);
2661772Sjl139090 }
2671772Sjl139090
2681772Sjl139090 /*
2691772Sjl139090 * name_child
2701772Sjl139090 *
2711772Sjl139090 * This function is called from pcmu_init_child to name a node. It is
2721772Sjl139090 * also passed as a callback for node merging functions.
2731772Sjl139090 *
2741772Sjl139090 * return value: DDI_SUCCESS, DDI_FAILURE
2751772Sjl139090 */
2761772Sjl139090 static int
name_child(dev_info_t * child,char * name,int namelen)2771772Sjl139090 name_child(dev_info_t *child, char *name, int namelen)
2781772Sjl139090 {
2791772Sjl139090 pci_regspec_t *pcmu_rp;
2801772Sjl139090 int reglen;
2811772Sjl139090 uint_t func;
2821772Sjl139090 char **unit_addr;
2831772Sjl139090 uint_t n;
2841772Sjl139090
2851772Sjl139090 /*
2861772Sjl139090 * Set the address portion of the node name based on
2871772Sjl139090 * unit-address property, if it exists.
2881772Sjl139090 * The interpretation of the unit-address is DD[,F]
2891772Sjl139090 * where DD is the device id and F is the function.
2901772Sjl139090 */
2911772Sjl139090 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
2921772Sjl139090 DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) ==
2931772Sjl139090 DDI_PROP_SUCCESS) {
2941772Sjl139090 if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
2951772Sjl139090 cmn_err(CE_WARN, "unit-address property in %s.conf"
2961772Sjl139090 " not well-formed", ddi_driver_name(child));
2971772Sjl139090 ddi_prop_free(unit_addr);
2981772Sjl139090 return (DDI_FAILURE);
2991772Sjl139090 }
3001772Sjl139090 (void) snprintf(name, namelen, "%s", *unit_addr);
3011772Sjl139090 ddi_prop_free(unit_addr);
3021772Sjl139090 return (DDI_SUCCESS);
3031772Sjl139090 }
3041772Sjl139090
3051772Sjl139090 /*
3061772Sjl139090 * The unit-address property is does not exist. Set the address
3071772Sjl139090 * portion of the node name based on the function and device number.
3081772Sjl139090 */
3091772Sjl139090 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
3101772Sjl139090 "reg", (int **)&pcmu_rp, (uint_t *)®len) == DDI_SUCCESS) {
3111772Sjl139090 if (((reglen * sizeof (int)) % sizeof (pci_regspec_t)) != 0) {
3121772Sjl139090 cmn_err(CE_WARN, "reg property not well-formed");
3131772Sjl139090 return (DDI_FAILURE);
3141772Sjl139090 }
3151772Sjl139090
3161772Sjl139090 func = PCI_REG_FUNC_G(pcmu_rp[0].pci_phys_hi);
3171772Sjl139090 if (func != 0) {
3181772Sjl139090 (void) snprintf(name, namelen, "%x,%x",
319*11311SSurya.Prakki@Sun.COM PCI_REG_DEV_G(pcmu_rp[0].pci_phys_hi), func);
3201772Sjl139090 } else {
3211772Sjl139090 (void) snprintf(name, namelen, "%x",
322*11311SSurya.Prakki@Sun.COM PCI_REG_DEV_G(pcmu_rp[0].pci_phys_hi));
3231772Sjl139090 }
3241772Sjl139090 ddi_prop_free(pcmu_rp);
3251772Sjl139090 return (DDI_SUCCESS);
3261772Sjl139090 }
3271772Sjl139090 cmn_err(CE_WARN, "cannot name pci child '%s'", ddi_node_name(child));
3281772Sjl139090 return (DDI_FAILURE);
3291772Sjl139090 }
3301772Sjl139090
3311772Sjl139090 int
pcmu_uninit_child(pcmu_t * pcmu_p,dev_info_t * child)3321772Sjl139090 pcmu_uninit_child(pcmu_t *pcmu_p, dev_info_t *child)
3331772Sjl139090 {
3341772Sjl139090 PCMU_DBG2(PCMU_DBG_CTLOPS, pcmu_p->pcmu_dip,
3351772Sjl139090 "DDI_CTLOPS_UNINITCHILD: arg=%s%d\n",
3361772Sjl139090 ddi_driver_name(child), ddi_get_instance(child));
3371772Sjl139090
3381772Sjl139090 ddi_set_name_addr(child, NULL);
3391772Sjl139090 ddi_remove_minor_node(child, NULL);
3401772Sjl139090 impl_rem_dev_props(child);
3411772Sjl139090
3421772Sjl139090 PCMU_DBG0(PCMU_DBG_PWR, ddi_get_parent(child), "\n\n");
3431772Sjl139090 return (DDI_SUCCESS);
3441772Sjl139090 }
3451772Sjl139090
3461772Sjl139090 /*
3471772Sjl139090 * pcmu_init_child
3481772Sjl139090 *
3491772Sjl139090 * This function is called from our control ops routine on a
3501772Sjl139090 * DDI_CTLOPS_INITCHILD request. It builds and sets the device's
3511772Sjl139090 * parent private data area.
3521772Sjl139090 *
3531772Sjl139090 * used by: pcmu_ctlops()
3541772Sjl139090 *
3551772Sjl139090 * return value: none
3561772Sjl139090 */
3571772Sjl139090 int
pcmu_init_child(pcmu_t * pcmu_p,dev_info_t * child)3581772Sjl139090 pcmu_init_child(pcmu_t *pcmu_p, dev_info_t *child)
3591772Sjl139090 {
3601772Sjl139090 char name[10];
3611772Sjl139090 ddi_acc_handle_t config_handle;
3621772Sjl139090 uint8_t bcr;
3631772Sjl139090 uint8_t header_type;
3641772Sjl139090
3651772Sjl139090 if (name_child(child, name, 10) != DDI_SUCCESS)
3661772Sjl139090 return (DDI_FAILURE);
3671772Sjl139090 ddi_set_name_addr(child, name);
3681772Sjl139090
3691772Sjl139090 PCMU_DBG2(PCMU_DBG_PWR, ddi_get_parent(child),
3701772Sjl139090 "INITCHILD: config regs setup for %s@%s\n",
3711772Sjl139090 ddi_node_name(child), ddi_get_name_addr(child));
3721772Sjl139090
3731772Sjl139090 /*
3741772Sjl139090 * Map the child configuration space to for initialization.
3751772Sjl139090 * We assume the obp will do the following in the devices
3761772Sjl139090 * config space:
3771772Sjl139090 *
3781772Sjl139090 * Set the latency-timer register to values appropriate
3791772Sjl139090 * for the devices on the bus (based on other devices
3801772Sjl139090 * MIN_GNT and MAX_LAT registers.
3811772Sjl139090 *
3821772Sjl139090 * Set the fast back-to-back enable bit in the command
3831772Sjl139090 * register if it's supported and all devices on the bus
3841772Sjl139090 * have the capability.
3851772Sjl139090 *
3861772Sjl139090 */
3871772Sjl139090 if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) {
3881772Sjl139090 ddi_set_name_addr(child, NULL);
3891772Sjl139090 return (DDI_FAILURE);
3901772Sjl139090 }
3911772Sjl139090
3921772Sjl139090 /*
3931772Sjl139090 * Determine the configuration header type.
3941772Sjl139090 */
3951772Sjl139090 header_type = pci_config_get8(config_handle, PCI_CONF_HEADER);
3961772Sjl139090 PCMU_DBG2(PCMU_DBG_INIT_CLD, pcmu_p->pcmu_dip, "%s: header_type=%x\n",
3971772Sjl139090 ddi_driver_name(child), header_type);
3981772Sjl139090
3991772Sjl139090 /*
4001772Sjl139090 * If the device has a bus control register then program it
4011772Sjl139090 * based on the settings in the command register.
4021772Sjl139090 */
4031772Sjl139090 if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) {
4041772Sjl139090 bcr = pci_config_get8(config_handle, PCI_BCNF_BCNTRL);
4051772Sjl139090 if (pcmu_command_default & PCI_COMM_PARITY_DETECT)
4061772Sjl139090 bcr |= PCI_BCNF_BCNTRL_PARITY_ENABLE;
4071772Sjl139090 if (pcmu_command_default & PCI_COMM_SERR_ENABLE)
4081772Sjl139090 bcr |= PCI_BCNF_BCNTRL_SERR_ENABLE;
4091772Sjl139090 bcr |= PCI_BCNF_BCNTRL_MAST_AB_MODE;
4101772Sjl139090 pci_config_put8(config_handle, PCI_BCNF_BCNTRL, bcr);
4111772Sjl139090 }
4121772Sjl139090
4131772Sjl139090 pci_config_teardown(&config_handle);
4141772Sjl139090 return (DDI_SUCCESS);
4151772Sjl139090 }
4161772Sjl139090
4171772Sjl139090 /*
4181772Sjl139090 * pcmu_get_reg_set_size
4191772Sjl139090 *
4201772Sjl139090 * Given a dev info pointer to a pci child and a register number, this
4211772Sjl139090 * routine returns the size element of that reg set property.
4221772Sjl139090 *
4231772Sjl139090 * used by: pcmu_ctlops() - DDI_CTLOPS_REGSIZE
4241772Sjl139090 *
4251772Sjl139090 * return value: size of reg set on success, zero on error
4261772Sjl139090 */
4271772Sjl139090 off_t
pcmu_get_reg_set_size(dev_info_t * child,int rnumber)4281772Sjl139090 pcmu_get_reg_set_size(dev_info_t *child, int rnumber)
4291772Sjl139090 {
4301772Sjl139090 pci_regspec_t *pcmu_rp;
4311772Sjl139090 off_t size;
4321772Sjl139090 int i;
4331772Sjl139090
4341772Sjl139090 if (rnumber < 0) {
4351772Sjl139090 return (0);
4361772Sjl139090 }
4371772Sjl139090
4381772Sjl139090 /*
4391772Sjl139090 * Get the reg property for the device.
4401772Sjl139090 */
4411772Sjl139090 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
4421772Sjl139090 (caddr_t)&pcmu_rp, &i) != DDI_SUCCESS) {
4431772Sjl139090 return (0);
4441772Sjl139090 }
4451772Sjl139090
4461772Sjl139090 if (rnumber >= (i / (int)sizeof (pci_regspec_t))) {
4471772Sjl139090 kmem_free(pcmu_rp, i);
4481772Sjl139090 return (0);
4491772Sjl139090 }
4501772Sjl139090
4511772Sjl139090 size = pcmu_rp[rnumber].pci_size_low |
452*11311SSurya.Prakki@Sun.COM ((uint64_t)pcmu_rp[rnumber].pci_size_hi << 32);
4531772Sjl139090 kmem_free(pcmu_rp, i);
4541772Sjl139090 return (size);
4551772Sjl139090 }
4561772Sjl139090
4571772Sjl139090
4581772Sjl139090 /*
4591772Sjl139090 * pcmu_get_nreg_set
4601772Sjl139090 *
4611772Sjl139090 * Given a dev info pointer to a pci child, this routine returns the
4621772Sjl139090 * number of sets in its "reg" property.
4631772Sjl139090 *
4641772Sjl139090 * used by: pcmu_ctlops() - DDI_CTLOPS_NREGS
4651772Sjl139090 *
4661772Sjl139090 * return value: # of reg sets on success, zero on error
4671772Sjl139090 */
4681772Sjl139090 uint_t
pcmu_get_nreg_set(dev_info_t * child)4691772Sjl139090 pcmu_get_nreg_set(dev_info_t *child)
4701772Sjl139090 {
4711772Sjl139090 pci_regspec_t *pcmu_rp;
4721772Sjl139090 int i, n;
4731772Sjl139090
4741772Sjl139090 /*
4751772Sjl139090 * Get the reg property for the device.
4761772Sjl139090 */
4771772Sjl139090 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg",
4781772Sjl139090 (caddr_t)&pcmu_rp, &i) != DDI_SUCCESS) {
4791772Sjl139090 return (0);
4801772Sjl139090 }
4811772Sjl139090 n = i / (int)sizeof (pci_regspec_t);
4821772Sjl139090 kmem_free(pcmu_rp, i);
4831772Sjl139090 return (n);
4841772Sjl139090 }
4851772Sjl139090
4861772Sjl139090 int
pcmu_cfg_report(dev_info_t * dip,ddi_fm_error_t * derr,pcmu_errstate_t * pcmu_err_p,int caller,uint32_t prierr)4871772Sjl139090 pcmu_cfg_report(dev_info_t *dip, ddi_fm_error_t *derr,
4881772Sjl139090 pcmu_errstate_t *pcmu_err_p, int caller, uint32_t prierr)
4891772Sjl139090 {
4901772Sjl139090 int fatal = 0;
4911772Sjl139090 int nonfatal = 0;
4921772Sjl139090 int i;
4931772Sjl139090 pcmu_t *pcmu_p;
4941772Sjl139090 int instance = ddi_get_instance(dip);
4951772Sjl139090
4961772Sjl139090 ASSERT(dip);
4971772Sjl139090
4981772Sjl139090 pcmu_p = get_pcmu_soft_state(instance);
4991772Sjl139090
5001772Sjl139090 derr->fme_ena = derr->fme_ena ? derr->fme_ena :
5011772Sjl139090 fm_ena_generate(0, FM_ENA_FMT1);
5021772Sjl139090
5031772Sjl139090 for (i = 0; pci_err_tbl[i].err_class != NULL; i++) {
5041772Sjl139090 if (pcmu_err_p->pcmu_cfg_stat & pci_err_tbl[i].reg_bit) {
5051772Sjl139090 char buf[FM_MAX_CLASS];
5061772Sjl139090 char *aux_msg = NULL;
5071772Sjl139090
5081772Sjl139090 switch (pci_err_tbl[i].reg_bit) {
5091772Sjl139090 case PCI_STAT_R_MAST_AB:
5101772Sjl139090 aux_msg = "Recieved Master Abort";
5111772Sjl139090 /* LINTED fallthrough on case statement */
5121772Sjl139090 case PCI_STAT_R_TARG_AB:
5131772Sjl139090 if (aux_msg != NULL)
5141772Sjl139090 aux_msg = "Recieved Target Abort";
5151772Sjl139090 if (prierr) {
5161772Sjl139090 /*
5171772Sjl139090 * piow case are already handled in
5181772Sjl139090 * pcmu_pbm_afsr_report()
5191772Sjl139090 */
5201772Sjl139090 break;
5211772Sjl139090 }
5221772Sjl139090 if (caller != PCI_TRAP_CALL) {
5231772Sjl139090 /*
5241772Sjl139090 * if we haven't come from trap handler
5251772Sjl139090 * we won't have an address
5261772Sjl139090 */
5271772Sjl139090 fatal++;
5281772Sjl139090 }
5291772Sjl139090 break;
5301772Sjl139090 default:
5311772Sjl139090 /*
5321772Sjl139090 * dpe on dma write or ta on dma
5331772Sjl139090 */
5341772Sjl139090 nonfatal++;
5351772Sjl139090 break;
5361772Sjl139090 }
5371772Sjl139090 (void) snprintf(buf, FM_MAX_CLASS, "%s %s: %s %s",
5381772Sjl139090 (pcmu_p->pcmu_pcbm_p)->pcbm_nameinst_str,
5391772Sjl139090 (pcmu_p->pcmu_pcbm_p)->pcbm_nameaddr_str,
5401772Sjl139090 "PCI config space:", aux_msg);
541*11311SSurya.Prakki@Sun.COM cmn_err(CE_WARN, "%s %s=0x%p", buf, "pbm-csr",
542*11311SSurya.Prakki@Sun.COM (void *)(pcmu_p->pcmu_pcbm_p)->pcbm_ctrl_reg);
5431772Sjl139090 }
5441772Sjl139090 }
5451772Sjl139090
5461772Sjl139090 if (fatal)
5471772Sjl139090 return (DDI_FM_FATAL);
5481772Sjl139090 else if (nonfatal)
5491772Sjl139090 return (DDI_FM_NONFATAL);
5501772Sjl139090
5511772Sjl139090 return (DDI_FM_OK);
5521772Sjl139090 }
5531772Sjl139090
5541772Sjl139090 void
pcmu_child_cfg_save(dev_info_t * dip)5551772Sjl139090 pcmu_child_cfg_save(dev_info_t *dip)
5561772Sjl139090 {
5571772Sjl139090 dev_info_t *cdip;
5581772Sjl139090 int ret = DDI_SUCCESS;
5591772Sjl139090
5601772Sjl139090 /*
5611772Sjl139090 * Save the state of the configuration headers of child
5621772Sjl139090 * nodes.
5631772Sjl139090 */
5641772Sjl139090
5651772Sjl139090 for (cdip = ddi_get_child(dip); cdip != NULL;
5661772Sjl139090 cdip = ddi_get_next_sibling(cdip)) {
5671772Sjl139090
5681772Sjl139090 /*
5691772Sjl139090 * Not interested in children who are not already
5701772Sjl139090 * init'ed. They will be set up in pcmu_init_child().
5711772Sjl139090 */
5721772Sjl139090 if (i_ddi_node_state(cdip) < DS_INITIALIZED) {
5731772Sjl139090 PCMU_DBG2(PCMU_DBG_DETACH, dip, "DDI_SUSPEND: skipping "
5741772Sjl139090 "%s%d not in CF1\n", ddi_driver_name(cdip),
5751772Sjl139090 ddi_get_instance(cdip));
5761772Sjl139090
5771772Sjl139090 continue;
5781772Sjl139090 }
5791772Sjl139090
5801772Sjl139090 /*
5811772Sjl139090 * Only save config registers if not already saved by child.
5821772Sjl139090 */
5831772Sjl139090 if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
5841772Sjl139090 SAVED_CONFIG_REGS) == 1) {
5851772Sjl139090
5861772Sjl139090 continue;
5871772Sjl139090 }
5881772Sjl139090
5891772Sjl139090 /*
5901772Sjl139090 * The nexus needs to save config registers. Create a property
5911772Sjl139090 * so it knows to restore on resume.
5921772Sjl139090 */
5931772Sjl139090 ret = ndi_prop_create_boolean(DDI_DEV_T_NONE, cdip,
5941772Sjl139090 "nexus-saved-config-regs");
5951772Sjl139090
5961772Sjl139090 if (ret != DDI_PROP_SUCCESS) {
5971772Sjl139090 cmn_err(CE_WARN, "%s%d can't update prop %s",
5981772Sjl139090 ddi_driver_name(cdip), ddi_get_instance(cdip),
5991772Sjl139090 "nexus-saved-config-regs");
6001772Sjl139090 }
6011772Sjl139090
6021772Sjl139090 (void) pci_save_config_regs(cdip);
6031772Sjl139090 }
6041772Sjl139090 }
6051772Sjl139090
6061772Sjl139090 void
pcmu_child_cfg_restore(dev_info_t * dip)6071772Sjl139090 pcmu_child_cfg_restore(dev_info_t *dip)
6081772Sjl139090 {
6091772Sjl139090 dev_info_t *cdip;
6101772Sjl139090
6111772Sjl139090 /*
6121772Sjl139090 * Restore config registers for children that did not save
6131772Sjl139090 * their own registers. Children pwr states are UNKNOWN after
6141772Sjl139090 * a resume since it is possible for the PM framework to call
6151772Sjl139090 * resume without an actual power cycle. (ie if suspend fails).
6161772Sjl139090 */
6171772Sjl139090 for (cdip = ddi_get_child(dip); cdip != NULL;
6181772Sjl139090 cdip = ddi_get_next_sibling(cdip)) {
6191772Sjl139090
6201772Sjl139090 /*
6211772Sjl139090 * Not interested in children who are not already
6221772Sjl139090 * init'ed. They will be set up by pcmu_init_child().
6231772Sjl139090 */
6241772Sjl139090 if (i_ddi_node_state(cdip) < DS_INITIALIZED) {
6251772Sjl139090 PCMU_DBG2(PCMU_DBG_DETACH, dip,
6261772Sjl139090 "DDI_RESUME: skipping %s%d not in CF1\n",
6271772Sjl139090 ddi_driver_name(cdip), ddi_get_instance(cdip));
6281772Sjl139090 continue;
6291772Sjl139090 }
6301772Sjl139090
6311772Sjl139090 /*
6321772Sjl139090 * Only restore config registers if saved by nexus.
6331772Sjl139090 */
6341772Sjl139090 if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
6351772Sjl139090 "nexus-saved-config-regs") == 1) {
6361772Sjl139090 (void) pci_restore_config_regs(cdip);
6371772Sjl139090
6381772Sjl139090 PCMU_DBG2(PCMU_DBG_PWR, dip,
6391772Sjl139090 "DDI_RESUME: nexus restoring %s%d config regs\n",
6401772Sjl139090 ddi_driver_name(cdip), ddi_get_instance(cdip));
6411772Sjl139090
6421772Sjl139090 if (ndi_prop_remove(DDI_DEV_T_NONE, cdip,
6431772Sjl139090 "nexus-saved-config-regs") != DDI_PROP_SUCCESS) {
6441772Sjl139090 cmn_err(CE_WARN, "%s%d can't remove prop %s",
6451772Sjl139090 ddi_driver_name(cdip),
6461772Sjl139090 ddi_get_instance(cdip),
6471772Sjl139090 "nexus-saved-config-regs");
6481772Sjl139090 }
6491772Sjl139090 }
6501772Sjl139090 }
6511772Sjl139090 }
6521772Sjl139090
6531772Sjl139090 #ifdef DEBUG
6541772Sjl139090 extern uint64_t pcmu_debug_flags;
6551772Sjl139090
6561772Sjl139090 pcmu_dflag_to_str_t pcmu_dflag_strings [] = {
6571772Sjl139090 {PCMU_DBG_ATTACH, "pcmu_attach"},
6581772Sjl139090 {PCMU_DBG_DETACH, "pcmu_detach"},
6591772Sjl139090 {PCMU_DBG_MAP, "pcmu_map"},
6601772Sjl139090 {PCMU_DBG_A_INTX, "pcmu_add_intx"},
6611772Sjl139090 {PCMU_DBG_R_INTX, "pcmu_rem_intx"},
6621772Sjl139090 {PCMU_DBG_INIT_CLD, "pcmu_init_child"},
6631772Sjl139090 {PCMU_DBG_CTLOPS, "pcmu_ctlops"},
6641772Sjl139090 {PCMU_DBG_INTR, "pcmu_intr_wrapper"},
6651772Sjl139090 {PCMU_DBG_ERR_INTR, "pcmu_pbm_error_intr"},
6661772Sjl139090 {PCMU_DBG_BUS_FAULT, "pcmu_fault"},
6671772Sjl139090 {PCMU_DBG_IB, "pcmu_ib"},
6681772Sjl139090 {PCMU_DBG_CB, "pcmu_cb"},
6691772Sjl139090 {PCMU_DBG_PBM, "pcmu_pbm"},
6701772Sjl139090 {PCMU_DBG_OPEN, "pcmu_open"},
6711772Sjl139090 {PCMU_DBG_CLOSE, "pcmu_close"},
6721772Sjl139090 {PCMU_DBG_IOCTL, "pcmu_ioctl"},
6731772Sjl139090 {PCMU_DBG_PWR, "pcmu_pwr"}
6741772Sjl139090 };
6751772Sjl139090
6761772Sjl139090 void
pcmu_debug(uint64_t flag,dev_info_t * dip,char * fmt,uintptr_t a1,uintptr_t a2,uintptr_t a3,uintptr_t a4,uintptr_t a5)6771772Sjl139090 pcmu_debug(uint64_t flag, dev_info_t *dip, char *fmt,
6781772Sjl139090 uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
6791772Sjl139090 {
6801772Sjl139090 char *s = "pcmu unknown";
6811772Sjl139090 uint_t cont = 0;
6821772Sjl139090 int i;
6831772Sjl139090 int no_rec = (sizeof (pcmu_dflag_strings) /
6841772Sjl139090 sizeof (pcmu_dflag_to_str_t));
6851772Sjl139090
6861772Sjl139090 if (flag & PCMU_DBG_CONT) {
6871772Sjl139090 flag &= ~PCMU_DBG_CONT;
6881772Sjl139090 cont = 1;
6891772Sjl139090 }
6901772Sjl139090 if ((pcmu_debug_flags & flag) == flag) {
6911772Sjl139090 for (i = 0; i < no_rec; i++) {
6921772Sjl139090 if (pcmu_dflag_strings[i].flag == flag) {
6931772Sjl139090 s = pcmu_dflag_strings[i].string;
6941772Sjl139090 break;
6951772Sjl139090 }
6961772Sjl139090 }
6971772Sjl139090 if (s && cont == 0) {
6981772Sjl139090 prom_printf("%s(%d): %s: ", ddi_driver_name(dip),
6991772Sjl139090 ddi_get_instance(dip), s);
7001772Sjl139090 }
7011772Sjl139090 prom_printf(fmt, a1, a2, a3, a4, a5);
7021772Sjl139090 }
7031772Sjl139090 }
7041772Sjl139090 #endif
705