17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 500d0963fSdilpreet * Common Development and Distribution License (the "License"). 600d0963fSdilpreet * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 2200d0963fSdilpreet * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate /* 297c478bd9Sstevel@tonic-gate * PCI nexus utility routines: 307c478bd9Sstevel@tonic-gate * property and config routines for attach() 317c478bd9Sstevel@tonic-gate * reg/intr/range/assigned-address property routines for bus_map() 327c478bd9Sstevel@tonic-gate * init_child() 337c478bd9Sstevel@tonic-gate * fault handling 347c478bd9Sstevel@tonic-gate */ 357c478bd9Sstevel@tonic-gate 367c478bd9Sstevel@tonic-gate #include <sys/types.h> 377c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 387c478bd9Sstevel@tonic-gate #include <sys/async.h> 397c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 407c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 417c478bd9Sstevel@tonic-gate #include <sys/sunndi.h> 427c478bd9Sstevel@tonic-gate #include <sys/fm/protocol.h> 437c478bd9Sstevel@tonic-gate #include <sys/fm/io/pci.h> 447c478bd9Sstevel@tonic-gate #include <sys/fm/util.h> 457c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 467c478bd9Sstevel@tonic-gate #include <sys/pci/pci_obj.h> 477c478bd9Sstevel@tonic-gate 487c478bd9Sstevel@tonic-gate /*LINTLIBRARY*/ 497c478bd9Sstevel@tonic-gate 507c478bd9Sstevel@tonic-gate /* 517c478bd9Sstevel@tonic-gate * get_pci_properties 527c478bd9Sstevel@tonic-gate * 537c478bd9Sstevel@tonic-gate * This function is called from the attach routine to get the key 547c478bd9Sstevel@tonic-gate * properties of the pci nodes. 557c478bd9Sstevel@tonic-gate * 567c478bd9Sstevel@tonic-gate * used by: pci_attach() 577c478bd9Sstevel@tonic-gate * 587c478bd9Sstevel@tonic-gate * return value: DDI_FAILURE on failure 597c478bd9Sstevel@tonic-gate */ 607c478bd9Sstevel@tonic-gate int 617c478bd9Sstevel@tonic-gate get_pci_properties(pci_t *pci_p, dev_info_t *dip) 627c478bd9Sstevel@tonic-gate { 637c478bd9Sstevel@tonic-gate int i; 647c478bd9Sstevel@tonic-gate 657c478bd9Sstevel@tonic-gate /* 667c478bd9Sstevel@tonic-gate * Get the device's port id. 677c478bd9Sstevel@tonic-gate */ 687c478bd9Sstevel@tonic-gate if ((pci_p->pci_id = (uint32_t)pci_get_portid(dip)) == -1u) { 697c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d: no portid property\n", 707c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip)); 717c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 727c478bd9Sstevel@tonic-gate } 737c478bd9Sstevel@tonic-gate 747c478bd9Sstevel@tonic-gate /* 757c478bd9Sstevel@tonic-gate * Get the bus-ranges property. 767c478bd9Sstevel@tonic-gate */ 777c478bd9Sstevel@tonic-gate i = sizeof (pci_p->pci_bus_range); 787c478bd9Sstevel@tonic-gate if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 797c478bd9Sstevel@tonic-gate "bus-range", (caddr_t)&pci_p->pci_bus_range, &i) != DDI_SUCCESS) { 807c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d: no bus-range property\n", 817c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip)); 827c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 837c478bd9Sstevel@tonic-gate } 847c478bd9Sstevel@tonic-gate DEBUG2(DBG_ATTACH, dip, "get_pci_properties: bus-range (%x,%x)\n", 857c478bd9Sstevel@tonic-gate pci_p->pci_bus_range.lo, pci_p->pci_bus_range.hi); 867c478bd9Sstevel@tonic-gate 877c478bd9Sstevel@tonic-gate /* 887c478bd9Sstevel@tonic-gate * disable streaming cache if necessary, this must be done 897c478bd9Sstevel@tonic-gate * before PBM is configured. 907c478bd9Sstevel@tonic-gate */ 917c478bd9Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 927c478bd9Sstevel@tonic-gate "no-streaming-cache")) { 937c478bd9Sstevel@tonic-gate pci_stream_buf_enable = 0; 947c478bd9Sstevel@tonic-gate pci_stream_buf_exists = 0; 957c478bd9Sstevel@tonic-gate } 967c478bd9Sstevel@tonic-gate 977c478bd9Sstevel@tonic-gate /* 987c478bd9Sstevel@tonic-gate * Get the ranges property. 997c478bd9Sstevel@tonic-gate */ 1007c478bd9Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges", 1017c478bd9Sstevel@tonic-gate (caddr_t)&pci_p->pci_ranges, &pci_p->pci_ranges_length) != 1027c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 1037c478bd9Sstevel@tonic-gate 1047c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d: no ranges property\n", 1057c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip)); 1067c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 1077c478bd9Sstevel@tonic-gate } 1087c478bd9Sstevel@tonic-gate pci_fix_ranges(pci_p->pci_ranges, 1097c478bd9Sstevel@tonic-gate pci_p->pci_ranges_length / sizeof (pci_ranges_t)); 1107c478bd9Sstevel@tonic-gate 1117c478bd9Sstevel@tonic-gate /* 1127c478bd9Sstevel@tonic-gate * Determine the number upa slot interrupts. 1137c478bd9Sstevel@tonic-gate */ 1147c478bd9Sstevel@tonic-gate pci_p->pci_numproxy = pci_get_numproxy(pci_p->pci_dip); 1157c478bd9Sstevel@tonic-gate DEBUG1(DBG_ATTACH, dip, "get_pci_properties: numproxy=%d\n", 1167c478bd9Sstevel@tonic-gate pci_p->pci_numproxy); 1177c478bd9Sstevel@tonic-gate 1187c478bd9Sstevel@tonic-gate pci_p->pci_thermal_interrupt = 1197c478bd9Sstevel@tonic-gate ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1207c478bd9Sstevel@tonic-gate "thermal-interrupt", -1); 1217c478bd9Sstevel@tonic-gate DEBUG1(DBG_ATTACH, dip, "get_pci_properties: thermal_interrupt=%d\n", 1227c478bd9Sstevel@tonic-gate pci_p->pci_thermal_interrupt); 1237c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 1247c478bd9Sstevel@tonic-gate } 1257c478bd9Sstevel@tonic-gate 1267c478bd9Sstevel@tonic-gate /* 1277c478bd9Sstevel@tonic-gate * free_pci_properties: 1287c478bd9Sstevel@tonic-gate * 1297c478bd9Sstevel@tonic-gate * This routine frees the memory used to cache the 1307c478bd9Sstevel@tonic-gate * "ranges" properties of the pci bus device node. 1317c478bd9Sstevel@tonic-gate * 1327c478bd9Sstevel@tonic-gate * used by: pci_detach() 1337c478bd9Sstevel@tonic-gate * 1347c478bd9Sstevel@tonic-gate * return value: none 1357c478bd9Sstevel@tonic-gate */ 1367c478bd9Sstevel@tonic-gate void 1377c478bd9Sstevel@tonic-gate free_pci_properties(pci_t *pci_p) 1387c478bd9Sstevel@tonic-gate { 1397c478bd9Sstevel@tonic-gate kmem_free(pci_p->pci_ranges, pci_p->pci_ranges_length); 1407c478bd9Sstevel@tonic-gate } 1417c478bd9Sstevel@tonic-gate 1427c478bd9Sstevel@tonic-gate /* 1437c478bd9Sstevel@tonic-gate * pci_reloc_reg 1447c478bd9Sstevel@tonic-gate * 1457c478bd9Sstevel@tonic-gate * If the "reg" entry (*pci_rp) is relocatable, lookup "assigned-addresses" 1467c478bd9Sstevel@tonic-gate * property to fetch corresponding relocated address. 1477c478bd9Sstevel@tonic-gate * 1487c478bd9Sstevel@tonic-gate * used by: pci_map() 1497c478bd9Sstevel@tonic-gate * 1507c478bd9Sstevel@tonic-gate * return value: 1517c478bd9Sstevel@tonic-gate * 1527c478bd9Sstevel@tonic-gate * DDI_SUCCESS - on success 1537c478bd9Sstevel@tonic-gate * DDI_ME_INVAL - regspec is invalid 1547c478bd9Sstevel@tonic-gate */ 1557c478bd9Sstevel@tonic-gate int 1567c478bd9Sstevel@tonic-gate pci_reloc_reg(dev_info_t *dip, dev_info_t *rdip, pci_t *pci_p, 1577c478bd9Sstevel@tonic-gate pci_regspec_t *rp) 1587c478bd9Sstevel@tonic-gate { 1597c478bd9Sstevel@tonic-gate int assign_len, assign_entries, i; 1607c478bd9Sstevel@tonic-gate pci_regspec_t *assign_p; 1617c478bd9Sstevel@tonic-gate register uint32_t phys_hi = rp->pci_phys_hi; 1627c478bd9Sstevel@tonic-gate 1637c478bd9Sstevel@tonic-gate DEBUG5(DBG_MAP | DBG_CONT, dip, "\tpci_reloc_reg fr: %x.%x.%x %x.%x\n", 1647c478bd9Sstevel@tonic-gate rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low, 1657c478bd9Sstevel@tonic-gate rp->pci_size_hi, rp->pci_size_low); 1667c478bd9Sstevel@tonic-gate 1677c478bd9Sstevel@tonic-gate if ((phys_hi & PCI_RELOCAT_B) || !(phys_hi & PCI_ADDR_MASK)) 1687c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 1697c478bd9Sstevel@tonic-gate 1707c478bd9Sstevel@tonic-gate /* phys_mid must be 0 regardless space type. */ 1717c478bd9Sstevel@tonic-gate if (rp->pci_phys_mid != 0 || rp->pci_size_hi != 0) { 1727c478bd9Sstevel@tonic-gate DEBUG0(DBG_MAP | DBG_CONT, pci_p->pci_dip, 1737c478bd9Sstevel@tonic-gate "phys_mid or size_hi not 0\n"); 1747c478bd9Sstevel@tonic-gate return (DDI_ME_INVAL); 1757c478bd9Sstevel@tonic-gate } 1767c478bd9Sstevel@tonic-gate 1777c478bd9Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 1787c478bd9Sstevel@tonic-gate "assigned-addresses", (caddr_t)&assign_p, &assign_len)) 1797c478bd9Sstevel@tonic-gate return (DDI_ME_INVAL); 1807c478bd9Sstevel@tonic-gate 1817c478bd9Sstevel@tonic-gate assign_entries = assign_len / sizeof (pci_regspec_t); 1827c478bd9Sstevel@tonic-gate for (i = 0; i < assign_entries; i++, assign_p++) { 183*5371fdd6Sjj156685 uint32_t space_type = phys_hi & PCI_REG_ADDR_M; 184*5371fdd6Sjj156685 uint32_t assign_type = assign_p->pci_phys_hi & PCI_REG_ADDR_M; 185*5371fdd6Sjj156685 uint32_t assign_addr = PCI_REG_BDFR_G(assign_p->pci_phys_hi); 186*5371fdd6Sjj156685 187*5371fdd6Sjj156685 if (PCI_REG_BDFR_G(phys_hi) != assign_addr) 188*5371fdd6Sjj156685 continue; 189*5371fdd6Sjj156685 if (space_type == assign_type) { /* exact match */ 1907c478bd9Sstevel@tonic-gate rp->pci_phys_low += assign_p->pci_phys_low; 1917c478bd9Sstevel@tonic-gate break; 1927c478bd9Sstevel@tonic-gate } 193*5371fdd6Sjj156685 if (space_type == PCI_ADDR_MEM64 && 194*5371fdd6Sjj156685 assign_type == PCI_ADDR_MEM32) { 195*5371fdd6Sjj156685 rp->pci_phys_low += assign_p->pci_phys_low; 196*5371fdd6Sjj156685 rp->pci_phys_hi ^= PCI_ADDR_MEM64 ^ PCI_ADDR_MEM32; 197*5371fdd6Sjj156685 break; 198*5371fdd6Sjj156685 } 1997c478bd9Sstevel@tonic-gate } 2007c478bd9Sstevel@tonic-gate kmem_free(assign_p - i, assign_len); 2017c478bd9Sstevel@tonic-gate DEBUG5(DBG_MAP | DBG_CONT, dip, "\tpci_reloc_reg to: %x.%x.%x %x.%x\n", 2027c478bd9Sstevel@tonic-gate rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low, 2037c478bd9Sstevel@tonic-gate rp->pci_size_hi, rp->pci_size_low); 2047c478bd9Sstevel@tonic-gate return (i < assign_entries ? DDI_SUCCESS : DDI_ME_INVAL); 2057c478bd9Sstevel@tonic-gate } 2067c478bd9Sstevel@tonic-gate 2077c478bd9Sstevel@tonic-gate /* 2087c478bd9Sstevel@tonic-gate * use "ranges" to translate relocated pci regspec into parent space 2097c478bd9Sstevel@tonic-gate */ 2107c478bd9Sstevel@tonic-gate int 2117c478bd9Sstevel@tonic-gate pci_xlate_reg(pci_t *pci_p, pci_regspec_t *pci_rp, struct regspec *new_rp) 2127c478bd9Sstevel@tonic-gate { 2137c478bd9Sstevel@tonic-gate int n; 2147c478bd9Sstevel@tonic-gate pci_ranges_t *rng_p = pci_p->pci_ranges; 2157c478bd9Sstevel@tonic-gate int rng_n = pci_p->pci_ranges_length / sizeof (pci_ranges_t); 2167c478bd9Sstevel@tonic-gate 2177c478bd9Sstevel@tonic-gate uint32_t space_type = PCI_REG_ADDR_G(pci_rp->pci_phys_hi); 2187c478bd9Sstevel@tonic-gate uint32_t reg_end, reg_begin = pci_rp->pci_phys_low; 2197c478bd9Sstevel@tonic-gate uint32_t sz = pci_rp->pci_size_low; 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate uint32_t rng_begin, rng_end; 2227c478bd9Sstevel@tonic-gate 2237c478bd9Sstevel@tonic-gate if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) { 2247c478bd9Sstevel@tonic-gate if (reg_begin > PCI_CONF_HDR_SIZE) 2257c478bd9Sstevel@tonic-gate return (DDI_ME_INVAL); 2267c478bd9Sstevel@tonic-gate sz = sz ? MIN(sz, PCI_CONF_HDR_SIZE) : PCI_CONF_HDR_SIZE; 2277c478bd9Sstevel@tonic-gate reg_begin += pci_rp->pci_phys_hi; 2287c478bd9Sstevel@tonic-gate } 2297c478bd9Sstevel@tonic-gate reg_end = reg_begin + sz - 1; 2307c478bd9Sstevel@tonic-gate 2317c478bd9Sstevel@tonic-gate for (n = 0; n < rng_n; n++, rng_p++) { 2327c478bd9Sstevel@tonic-gate if (space_type != PCI_REG_ADDR_G(rng_p->child_high)) 2337c478bd9Sstevel@tonic-gate continue; /* not the same space type */ 2347c478bd9Sstevel@tonic-gate 2357c478bd9Sstevel@tonic-gate rng_begin = rng_p->child_low; 2367c478bd9Sstevel@tonic-gate if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) 2377c478bd9Sstevel@tonic-gate rng_begin += rng_p->child_high; 2387c478bd9Sstevel@tonic-gate 2397c478bd9Sstevel@tonic-gate rng_end = rng_begin + rng_p->size_low - 1; 2407c478bd9Sstevel@tonic-gate if (reg_begin >= rng_begin && reg_end <= rng_end) 2417c478bd9Sstevel@tonic-gate break; 2427c478bd9Sstevel@tonic-gate } 2437c478bd9Sstevel@tonic-gate if (n >= rng_n) 2447c478bd9Sstevel@tonic-gate return (DDI_ME_REGSPEC_RANGE); 2457c478bd9Sstevel@tonic-gate 2467c478bd9Sstevel@tonic-gate new_rp->regspec_addr = reg_begin - rng_begin + rng_p->parent_low; 2477c478bd9Sstevel@tonic-gate new_rp->regspec_bustype = rng_p->parent_high; 2487c478bd9Sstevel@tonic-gate new_rp->regspec_size = sz; 2497c478bd9Sstevel@tonic-gate DEBUG4(DBG_MAP | DBG_CONT, pci_p->pci_dip, 2507c478bd9Sstevel@tonic-gate "\tpci_xlate_reg: entry %d new_rp %x.%x %x\n", 2517c478bd9Sstevel@tonic-gate n, new_rp->regspec_bustype, new_rp->regspec_addr, sz); 2527c478bd9Sstevel@tonic-gate 2537c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2547c478bd9Sstevel@tonic-gate } 2557c478bd9Sstevel@tonic-gate 2567c478bd9Sstevel@tonic-gate 2577c478bd9Sstevel@tonic-gate /* 2587c478bd9Sstevel@tonic-gate * report_dev 2597c478bd9Sstevel@tonic-gate * 2607c478bd9Sstevel@tonic-gate * This function is called from our control ops routine on a 2617c478bd9Sstevel@tonic-gate * DDI_CTLOPS_REPORTDEV request. 2627c478bd9Sstevel@tonic-gate * 2637c478bd9Sstevel@tonic-gate * The display format is 2647c478bd9Sstevel@tonic-gate * 2657c478bd9Sstevel@tonic-gate * <name><inst> at <pname><pinst> device <dev> function <func> 2667c478bd9Sstevel@tonic-gate * 2677c478bd9Sstevel@tonic-gate * where 2687c478bd9Sstevel@tonic-gate * 2697c478bd9Sstevel@tonic-gate * <name> this device's name property 2707c478bd9Sstevel@tonic-gate * <inst> this device's instance number 2717c478bd9Sstevel@tonic-gate * <name> parent device's name property 2727c478bd9Sstevel@tonic-gate * <inst> parent device's instance number 2737c478bd9Sstevel@tonic-gate * <dev> this device's device number 2747c478bd9Sstevel@tonic-gate * <func> this device's function number 2757c478bd9Sstevel@tonic-gate */ 2767c478bd9Sstevel@tonic-gate int 2777c478bd9Sstevel@tonic-gate report_dev(dev_info_t *dip) 2787c478bd9Sstevel@tonic-gate { 2797c478bd9Sstevel@tonic-gate if (dip == (dev_info_t *)0) 2807c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2817c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n", 2827c478bd9Sstevel@tonic-gate ddi_node_name(dip), ddi_get_name_addr(dip), 2837c478bd9Sstevel@tonic-gate ddi_driver_name(dip), 2847c478bd9Sstevel@tonic-gate ddi_get_instance(dip)); 2857c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 2867c478bd9Sstevel@tonic-gate } 2877c478bd9Sstevel@tonic-gate 2887c478bd9Sstevel@tonic-gate 2897c478bd9Sstevel@tonic-gate /* 2907c478bd9Sstevel@tonic-gate * reg property for pcimem nodes that covers the entire address 2917c478bd9Sstevel@tonic-gate * space for the node: config, io, or memory. 2927c478bd9Sstevel@tonic-gate */ 2937c478bd9Sstevel@tonic-gate pci_regspec_t pci_pcimem_reg[3] = 2947c478bd9Sstevel@tonic-gate { 2957c478bd9Sstevel@tonic-gate {PCI_ADDR_CONFIG, 0, 0, 0, 0x800000 }, 2967c478bd9Sstevel@tonic-gate {(uint_t)(PCI_ADDR_IO|PCI_RELOCAT_B), 0, 0, 0, PCI_IO_SIZE }, 2977c478bd9Sstevel@tonic-gate {(uint_t)(PCI_ADDR_MEM32|PCI_RELOCAT_B), 0, 0, 0, PCI_MEM_SIZE } 2987c478bd9Sstevel@tonic-gate }; 2997c478bd9Sstevel@tonic-gate 3007c478bd9Sstevel@tonic-gate /* 3017c478bd9Sstevel@tonic-gate * name_child 3027c478bd9Sstevel@tonic-gate * 3037c478bd9Sstevel@tonic-gate * This function is called from init_child to name a node. It is 3047c478bd9Sstevel@tonic-gate * also passed as a callback for node merging functions. 3057c478bd9Sstevel@tonic-gate * 3067c478bd9Sstevel@tonic-gate * return value: DDI_SUCCESS, DDI_FAILURE 3077c478bd9Sstevel@tonic-gate */ 3087c478bd9Sstevel@tonic-gate static int 3097c478bd9Sstevel@tonic-gate name_child(dev_info_t *child, char *name, int namelen) 3107c478bd9Sstevel@tonic-gate { 3117c478bd9Sstevel@tonic-gate pci_regspec_t *pci_rp; 3127c478bd9Sstevel@tonic-gate int reglen; 3137c478bd9Sstevel@tonic-gate uint_t func; 3147c478bd9Sstevel@tonic-gate char **unit_addr; 3157c478bd9Sstevel@tonic-gate uint_t n; 3167c478bd9Sstevel@tonic-gate 3177c478bd9Sstevel@tonic-gate /* 3187c478bd9Sstevel@tonic-gate * Set the address portion of the node name based on 3197c478bd9Sstevel@tonic-gate * unit-address property, if it exists. 3207c478bd9Sstevel@tonic-gate * The interpretation of the unit-address is DD[,F] 3217c478bd9Sstevel@tonic-gate * where DD is the device id and F is the function. 3227c478bd9Sstevel@tonic-gate */ 3237c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 3247c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) == 3257c478bd9Sstevel@tonic-gate DDI_PROP_SUCCESS) { 3267c478bd9Sstevel@tonic-gate if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 3277c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "unit-address property in %s.conf" 3287c478bd9Sstevel@tonic-gate " not well-formed", ddi_driver_name(child)); 3297c478bd9Sstevel@tonic-gate ddi_prop_free(unit_addr); 3307c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3317c478bd9Sstevel@tonic-gate } 3327c478bd9Sstevel@tonic-gate (void) snprintf(name, namelen, "%s", *unit_addr); 3337c478bd9Sstevel@tonic-gate ddi_prop_free(unit_addr); 3347c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 3357c478bd9Sstevel@tonic-gate } 3367c478bd9Sstevel@tonic-gate 3377c478bd9Sstevel@tonic-gate /* 3387c478bd9Sstevel@tonic-gate * The unit-address property is does not exist. Set the address 3397c478bd9Sstevel@tonic-gate * portion of the node name based on the function and device number. 3407c478bd9Sstevel@tonic-gate */ 3417c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 3427c478bd9Sstevel@tonic-gate "reg", (int **)&pci_rp, (uint_t *)®len) == DDI_SUCCESS) { 3437c478bd9Sstevel@tonic-gate if (((reglen * sizeof (int)) % sizeof (pci_regspec_t)) != 0) { 3447c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "reg property not well-formed"); 3457c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3467c478bd9Sstevel@tonic-gate } 3477c478bd9Sstevel@tonic-gate 3487c478bd9Sstevel@tonic-gate func = PCI_REG_FUNC_G(pci_rp[0].pci_phys_hi); 3497c478bd9Sstevel@tonic-gate if (func != 0) 3507c478bd9Sstevel@tonic-gate (void) snprintf(name, namelen, "%x,%x", 3517c478bd9Sstevel@tonic-gate PCI_REG_DEV_G(pci_rp[0].pci_phys_hi), func); 3527c478bd9Sstevel@tonic-gate else 3537c478bd9Sstevel@tonic-gate (void) snprintf(name, namelen, "%x", 3547c478bd9Sstevel@tonic-gate PCI_REG_DEV_G(pci_rp[0].pci_phys_hi)); 3557c478bd9Sstevel@tonic-gate ddi_prop_free(pci_rp); 3567c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 3577c478bd9Sstevel@tonic-gate } 3587c478bd9Sstevel@tonic-gate 3597c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "cannot name pci child '%s'", ddi_node_name(child)); 3607c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3617c478bd9Sstevel@tonic-gate } 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate int 3647c478bd9Sstevel@tonic-gate uninit_child(pci_t *pci_p, dev_info_t *child) 3657c478bd9Sstevel@tonic-gate { 3667c478bd9Sstevel@tonic-gate DEBUG2(DBG_CTLOPS, pci_p->pci_dip, 3677c478bd9Sstevel@tonic-gate "DDI_CTLOPS_UNINITCHILD: arg=%s%d\n", 3687c478bd9Sstevel@tonic-gate ddi_driver_name(child), ddi_get_instance(child)); 3697c478bd9Sstevel@tonic-gate 3707c478bd9Sstevel@tonic-gate 3717c478bd9Sstevel@tonic-gate (void) pm_uninit_child(child); 3727c478bd9Sstevel@tonic-gate 3737c478bd9Sstevel@tonic-gate ddi_set_name_addr(child, NULL); 3747c478bd9Sstevel@tonic-gate ddi_remove_minor_node(child, NULL); 3757c478bd9Sstevel@tonic-gate impl_rem_dev_props(child); 3767c478bd9Sstevel@tonic-gate 3777c478bd9Sstevel@tonic-gate DEBUG0(DBG_PWR, ddi_get_parent(child), "\n\n"); 3787c478bd9Sstevel@tonic-gate 3797c478bd9Sstevel@tonic-gate /* 3807c478bd9Sstevel@tonic-gate * Handle chip specific post-uninit-child tasks. 3817c478bd9Sstevel@tonic-gate */ 3827c478bd9Sstevel@tonic-gate pci_post_uninit_child(pci_p); 3837c478bd9Sstevel@tonic-gate 3847c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 3857c478bd9Sstevel@tonic-gate } 3867c478bd9Sstevel@tonic-gate 3877c478bd9Sstevel@tonic-gate /* 3887c478bd9Sstevel@tonic-gate * init_child 3897c478bd9Sstevel@tonic-gate * 3907c478bd9Sstevel@tonic-gate * This function is called from our control ops routine on a 3917c478bd9Sstevel@tonic-gate * DDI_CTLOPS_INITCHILD request. It builds and sets the device's 3927c478bd9Sstevel@tonic-gate * parent private data area. 3937c478bd9Sstevel@tonic-gate * 3947c478bd9Sstevel@tonic-gate * used by: pci_ctlops() 3957c478bd9Sstevel@tonic-gate * 3967c478bd9Sstevel@tonic-gate * return value: none 3977c478bd9Sstevel@tonic-gate */ 3987c478bd9Sstevel@tonic-gate int 3997c478bd9Sstevel@tonic-gate init_child(pci_t *pci_p, dev_info_t *child) 4007c478bd9Sstevel@tonic-gate { 4017c478bd9Sstevel@tonic-gate pci_regspec_t *pci_rp; 4027c478bd9Sstevel@tonic-gate char name[10]; 4037c478bd9Sstevel@tonic-gate ddi_acc_handle_t config_handle; 4047c478bd9Sstevel@tonic-gate uint16_t command_preserve, command; 4057c478bd9Sstevel@tonic-gate uint8_t bcr; 4067c478bd9Sstevel@tonic-gate uint8_t header_type, min_gnt; 4077c478bd9Sstevel@tonic-gate uint16_t latency_timer; 4087c478bd9Sstevel@tonic-gate uint_t n; 4097c478bd9Sstevel@tonic-gate int i, no_config; 4107c478bd9Sstevel@tonic-gate 4117c478bd9Sstevel@tonic-gate /* 4127c478bd9Sstevel@tonic-gate * The following is a special case for pcimem nodes. 4137c478bd9Sstevel@tonic-gate * For these nodes we create a reg property with a 4147c478bd9Sstevel@tonic-gate * single entry that covers the entire address space 4157c478bd9Sstevel@tonic-gate * for the node (config, io or memory). 4167c478bd9Sstevel@tonic-gate */ 4177c478bd9Sstevel@tonic-gate if (strcmp(ddi_driver_name(child), "pcimem") == 0) { 4187c478bd9Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_NONE, child, 4197c478bd9Sstevel@tonic-gate DDI_PROP_CANSLEEP, "reg", (caddr_t)pci_pcimem_reg, 4207c478bd9Sstevel@tonic-gate sizeof (pci_pcimem_reg)); 4217c478bd9Sstevel@tonic-gate ddi_set_name_addr(child, "0"); 4227c478bd9Sstevel@tonic-gate ddi_set_parent_data(child, NULL); 4237c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4247c478bd9Sstevel@tonic-gate } 4257c478bd9Sstevel@tonic-gate 4267c478bd9Sstevel@tonic-gate /* 4277c478bd9Sstevel@tonic-gate * Check whether the node has config space or is a hard decode 4287c478bd9Sstevel@tonic-gate * node (possibly created by a driver.conf file). 4297c478bd9Sstevel@tonic-gate */ 4307c478bd9Sstevel@tonic-gate no_config = ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 4317c478bd9Sstevel@tonic-gate "no-config", 0); 4327c478bd9Sstevel@tonic-gate 4337c478bd9Sstevel@tonic-gate /* 4347c478bd9Sstevel@tonic-gate * Pseudo nodes indicate a prototype node with per-instance 4357c478bd9Sstevel@tonic-gate * properties to be merged into the real h/w device node. 4367c478bd9Sstevel@tonic-gate * However, do not merge if the no-config property is set 4377c478bd9Sstevel@tonic-gate * (see PSARC 2000/088). 4387c478bd9Sstevel@tonic-gate */ 4397c478bd9Sstevel@tonic-gate if ((ndi_dev_is_persistent_node(child) == 0) && (no_config == 0)) { 4407c478bd9Sstevel@tonic-gate extern int pci_allow_pseudo_children; 4417c478bd9Sstevel@tonic-gate 4427c478bd9Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, child, 4437c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, "reg", (caddr_t)&pci_rp, &i) == 4447c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 4457c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "cannot merge prototype from %s.conf", 4467c478bd9Sstevel@tonic-gate ddi_driver_name(child)); 4477c478bd9Sstevel@tonic-gate kmem_free(pci_rp, i); 4487c478bd9Sstevel@tonic-gate return (DDI_NOT_WELL_FORMED); 4497c478bd9Sstevel@tonic-gate } 4507c478bd9Sstevel@tonic-gate /* 4517c478bd9Sstevel@tonic-gate * Name the child 4527c478bd9Sstevel@tonic-gate */ 4537c478bd9Sstevel@tonic-gate if (name_child(child, name, 10) != DDI_SUCCESS) 4547c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4557c478bd9Sstevel@tonic-gate 4567c478bd9Sstevel@tonic-gate ddi_set_name_addr(child, name); 4577c478bd9Sstevel@tonic-gate ddi_set_parent_data(child, NULL); 4587c478bd9Sstevel@tonic-gate 4597c478bd9Sstevel@tonic-gate /* 4607c478bd9Sstevel@tonic-gate * Try to merge the properties from this prototype 4617c478bd9Sstevel@tonic-gate * node into real h/w nodes. 4627c478bd9Sstevel@tonic-gate */ 4637c478bd9Sstevel@tonic-gate if (ndi_merge_node(child, name_child) == DDI_SUCCESS) { 4647c478bd9Sstevel@tonic-gate /* 4657c478bd9Sstevel@tonic-gate * Merged ok - return failure to remove the node. 4667c478bd9Sstevel@tonic-gate */ 4677c478bd9Sstevel@tonic-gate ddi_set_name_addr(child, NULL); 4687c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4697c478bd9Sstevel@tonic-gate } 4707c478bd9Sstevel@tonic-gate 4717c478bd9Sstevel@tonic-gate /* workaround for ddivs to run under PCI */ 4727c478bd9Sstevel@tonic-gate if (pci_allow_pseudo_children) 4737c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4747c478bd9Sstevel@tonic-gate 4757c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", 4767c478bd9Sstevel@tonic-gate ddi_driver_name(child), ddi_get_name_addr(child), 4777c478bd9Sstevel@tonic-gate ddi_driver_name(child)); 4787c478bd9Sstevel@tonic-gate ddi_set_name_addr(child, NULL); 4797c478bd9Sstevel@tonic-gate return (DDI_NOT_WELL_FORMED); 4807c478bd9Sstevel@tonic-gate } 4817c478bd9Sstevel@tonic-gate 4827c478bd9Sstevel@tonic-gate if (name_child(child, name, 10) != DDI_SUCCESS) 4837c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4847c478bd9Sstevel@tonic-gate ddi_set_name_addr(child, name); 4857c478bd9Sstevel@tonic-gate 4867c478bd9Sstevel@tonic-gate if (no_config != 0) { 4877c478bd9Sstevel@tonic-gate /* 4887c478bd9Sstevel@tonic-gate * There is no config space so there's nothing more to do. 4897c478bd9Sstevel@tonic-gate */ 4907c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4917c478bd9Sstevel@tonic-gate } 4927c478bd9Sstevel@tonic-gate 4937c478bd9Sstevel@tonic-gate if (pm_init_child(child) != DDI_SUCCESS) 4947c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4957c478bd9Sstevel@tonic-gate 4967c478bd9Sstevel@tonic-gate 4977c478bd9Sstevel@tonic-gate /* 4987c478bd9Sstevel@tonic-gate * If configuration registers were previously saved by 4997c478bd9Sstevel@tonic-gate * child (before it went to D3), then let the child do the 5007c478bd9Sstevel@tonic-gate * restore to set up the config regs as it'll first need to 5017c478bd9Sstevel@tonic-gate * power the device out of D3. 5027c478bd9Sstevel@tonic-gate */ 5037c478bd9Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 5047c478bd9Sstevel@tonic-gate "config-regs-saved-by-child") == 1) { 5057c478bd9Sstevel@tonic-gate DEBUG0(DBG_PWR, child, 5067c478bd9Sstevel@tonic-gate "INITCHILD: config regs to be restored by child\n"); 5077c478bd9Sstevel@tonic-gate 5087c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 5097c478bd9Sstevel@tonic-gate } 5107c478bd9Sstevel@tonic-gate 5117c478bd9Sstevel@tonic-gate DEBUG2(DBG_PWR, ddi_get_parent(child), 5127c478bd9Sstevel@tonic-gate "INITCHILD: config regs setup for %s@%s\n", 5137c478bd9Sstevel@tonic-gate ddi_node_name(child), ddi_get_name_addr(child)); 5147c478bd9Sstevel@tonic-gate 5157c478bd9Sstevel@tonic-gate /* 5167c478bd9Sstevel@tonic-gate * Map the child configuration space to for initialization. 5177c478bd9Sstevel@tonic-gate * We assume the obp will do the following in the devices 5187c478bd9Sstevel@tonic-gate * config space: 5197c478bd9Sstevel@tonic-gate * 5207c478bd9Sstevel@tonic-gate * Set the latency-timer register to values appropriate 5217c478bd9Sstevel@tonic-gate * for the devices on the bus (based on other devices 5227c478bd9Sstevel@tonic-gate * MIN_GNT and MAX_LAT registers. 5237c478bd9Sstevel@tonic-gate * 5247c478bd9Sstevel@tonic-gate * Set the fast back-to-back enable bit in the command 5257c478bd9Sstevel@tonic-gate * register if it's supported and all devices on the bus 5267c478bd9Sstevel@tonic-gate * have the capability. 5277c478bd9Sstevel@tonic-gate * 5287c478bd9Sstevel@tonic-gate */ 5297c478bd9Sstevel@tonic-gate if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) { 5307c478bd9Sstevel@tonic-gate (void) pm_uninit_child(child); 5317c478bd9Sstevel@tonic-gate ddi_set_name_addr(child, NULL); 5327c478bd9Sstevel@tonic-gate 5337c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 5347c478bd9Sstevel@tonic-gate } 5357c478bd9Sstevel@tonic-gate 5367c478bd9Sstevel@tonic-gate /* 5377c478bd9Sstevel@tonic-gate * Determine the configuration header type. 5387c478bd9Sstevel@tonic-gate */ 5397c478bd9Sstevel@tonic-gate header_type = pci_config_get8(config_handle, PCI_CONF_HEADER); 5407c478bd9Sstevel@tonic-gate DEBUG2(DBG_INIT_CLD, pci_p->pci_dip, "%s: header_type=%x\n", 5417c478bd9Sstevel@tonic-gate ddi_driver_name(child), header_type); 5427c478bd9Sstevel@tonic-gate 5437c478bd9Sstevel@tonic-gate /* 5447c478bd9Sstevel@tonic-gate * Support for "command-preserve" property. Note that we 5457c478bd9Sstevel@tonic-gate * add PCI_COMM_BACK2BACK_ENAB to the bits to be preserved 5467c478bd9Sstevel@tonic-gate * since the obp will set this if the device supports and 5477c478bd9Sstevel@tonic-gate * all targets on the same bus support it. Since psycho 5487c478bd9Sstevel@tonic-gate * doesn't support PCI_COMM_BACK2BACK_ENAB, it will never 5497c478bd9Sstevel@tonic-gate * be set. This is just here in case future revs do support 5507c478bd9Sstevel@tonic-gate * PCI_COMM_BACK2BACK_ENAB. 5517c478bd9Sstevel@tonic-gate */ 5527c478bd9Sstevel@tonic-gate command_preserve = 5537c478bd9Sstevel@tonic-gate ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 5547c478bd9Sstevel@tonic-gate "command-preserve", 0); 5557c478bd9Sstevel@tonic-gate DEBUG2(DBG_INIT_CLD, pci_p->pci_dip, "%s: command-preserve=%x\n", 5567c478bd9Sstevel@tonic-gate ddi_driver_name(child), command_preserve); 5577c478bd9Sstevel@tonic-gate command = pci_config_get16(config_handle, PCI_CONF_COMM); 5587c478bd9Sstevel@tonic-gate command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB); 5597c478bd9Sstevel@tonic-gate command |= (pci_command_default & ~command_preserve); 5607c478bd9Sstevel@tonic-gate pci_config_put16(config_handle, PCI_CONF_COMM, command); 5617c478bd9Sstevel@tonic-gate command = pci_config_get16(config_handle, PCI_CONF_COMM); 5627c478bd9Sstevel@tonic-gate DEBUG2(DBG_INIT_CLD, pci_p->pci_dip, "%s: command=%x\n", 5637c478bd9Sstevel@tonic-gate ddi_driver_name(child), 5647c478bd9Sstevel@tonic-gate pci_config_get16(config_handle, PCI_CONF_COMM)); 5657c478bd9Sstevel@tonic-gate 5667c478bd9Sstevel@tonic-gate /* 5677c478bd9Sstevel@tonic-gate * If the device has a bus control register then program it 5687c478bd9Sstevel@tonic-gate * based on the settings in the command register. 5697c478bd9Sstevel@tonic-gate */ 5707c478bd9Sstevel@tonic-gate if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) { 5717c478bd9Sstevel@tonic-gate bcr = pci_config_get8(config_handle, PCI_BCNF_BCNTRL); 5727c478bd9Sstevel@tonic-gate if (pci_command_default & PCI_COMM_PARITY_DETECT) 5737c478bd9Sstevel@tonic-gate bcr |= PCI_BCNF_BCNTRL_PARITY_ENABLE; 5747c478bd9Sstevel@tonic-gate if (pci_command_default & PCI_COMM_SERR_ENABLE) 5757c478bd9Sstevel@tonic-gate bcr |= PCI_BCNF_BCNTRL_SERR_ENABLE; 5767c478bd9Sstevel@tonic-gate bcr |= PCI_BCNF_BCNTRL_MAST_AB_MODE; 5777c478bd9Sstevel@tonic-gate pci_config_put8(config_handle, PCI_BCNF_BCNTRL, bcr); 5787c478bd9Sstevel@tonic-gate } 5797c478bd9Sstevel@tonic-gate 5807c478bd9Sstevel@tonic-gate /* 5817c478bd9Sstevel@tonic-gate * Initialize cache-line-size configuration register if needed. 5827c478bd9Sstevel@tonic-gate */ 5837c478bd9Sstevel@tonic-gate if (pci_set_cache_line_size_register && 5847c478bd9Sstevel@tonic-gate ddi_getprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 5857c478bd9Sstevel@tonic-gate "cache-line-size", 0) == 0) { 5867c478bd9Sstevel@tonic-gate 5877c478bd9Sstevel@tonic-gate pci_config_put8(config_handle, PCI_CONF_CACHE_LINESZ, 5887c478bd9Sstevel@tonic-gate PCI_CACHE_LINE_SIZE); 5897c478bd9Sstevel@tonic-gate n = pci_config_get8(config_handle, PCI_CONF_CACHE_LINESZ); 5907c478bd9Sstevel@tonic-gate if (n != 0) 5917c478bd9Sstevel@tonic-gate (void) ndi_prop_update_int(DDI_DEV_T_NONE, child, 5927c478bd9Sstevel@tonic-gate "cache-line-size", n); 5937c478bd9Sstevel@tonic-gate } 5947c478bd9Sstevel@tonic-gate 5957c478bd9Sstevel@tonic-gate /* 5967c478bd9Sstevel@tonic-gate * Initialize latency timer registers if needed. 5977c478bd9Sstevel@tonic-gate */ 5987c478bd9Sstevel@tonic-gate if (pci_set_latency_timer_register && 5997c478bd9Sstevel@tonic-gate ddi_getprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 6007c478bd9Sstevel@tonic-gate "latency-timer", 0) == 0) { 6017c478bd9Sstevel@tonic-gate 6027c478bd9Sstevel@tonic-gate latency_timer = pci_latency_timer; 6037c478bd9Sstevel@tonic-gate if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) { 6047c478bd9Sstevel@tonic-gate pci_config_put8(config_handle, PCI_BCNF_LATENCY_TIMER, 6057c478bd9Sstevel@tonic-gate latency_timer); 6067c478bd9Sstevel@tonic-gate } else { 6077c478bd9Sstevel@tonic-gate min_gnt = pci_config_get8(config_handle, 6087c478bd9Sstevel@tonic-gate PCI_CONF_MIN_G); 6097c478bd9Sstevel@tonic-gate DEBUG2(DBG_INIT_CLD, pci_p->pci_dip, "%s: min_gnt=%x\n", 6107c478bd9Sstevel@tonic-gate ddi_driver_name(child), min_gnt); 6117c478bd9Sstevel@tonic-gate if (min_gnt != 0) { 6127c478bd9Sstevel@tonic-gate switch (pci_p->pci_pbm_p->pbm_speed) { 6137c478bd9Sstevel@tonic-gate case PBM_SPEED_33MHZ: 6147c478bd9Sstevel@tonic-gate latency_timer = min_gnt * 8; 6157c478bd9Sstevel@tonic-gate break; 6167c478bd9Sstevel@tonic-gate case PBM_SPEED_66MHZ: 6177c478bd9Sstevel@tonic-gate latency_timer = min_gnt * 4; 6187c478bd9Sstevel@tonic-gate break; 6197c478bd9Sstevel@tonic-gate } 6207c478bd9Sstevel@tonic-gate } 6217c478bd9Sstevel@tonic-gate } 6227c478bd9Sstevel@tonic-gate latency_timer = MIN(latency_timer, 0xff); 6237c478bd9Sstevel@tonic-gate pci_config_put8(config_handle, PCI_CONF_LATENCY_TIMER, 6247c478bd9Sstevel@tonic-gate latency_timer); 6257c478bd9Sstevel@tonic-gate n = pci_config_get8(config_handle, PCI_CONF_LATENCY_TIMER); 6267c478bd9Sstevel@tonic-gate if (n != 0) 6277c478bd9Sstevel@tonic-gate (void) ndi_prop_update_int(DDI_DEV_T_NONE, child, 6287c478bd9Sstevel@tonic-gate "latency-timer", n); 6297c478bd9Sstevel@tonic-gate } 6307c478bd9Sstevel@tonic-gate 6317c478bd9Sstevel@tonic-gate pci_config_teardown(&config_handle); 6327c478bd9Sstevel@tonic-gate 6337c478bd9Sstevel@tonic-gate /* 6347c478bd9Sstevel@tonic-gate * Handle chip specific init-child tasks. 6357c478bd9Sstevel@tonic-gate */ 6367c478bd9Sstevel@tonic-gate pci_post_init_child(pci_p, child); 6377c478bd9Sstevel@tonic-gate 6387c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 6397c478bd9Sstevel@tonic-gate } 6407c478bd9Sstevel@tonic-gate 6417c478bd9Sstevel@tonic-gate /* 6427c478bd9Sstevel@tonic-gate * get_nreg_set 6437c478bd9Sstevel@tonic-gate * 6447c478bd9Sstevel@tonic-gate * Given a dev info pointer to a pci child, this routine returns the 6457c478bd9Sstevel@tonic-gate * number of sets in its "reg" property. 6467c478bd9Sstevel@tonic-gate * 6477c478bd9Sstevel@tonic-gate * used by: pci_ctlops() - DDI_CTLOPS_NREGS 6487c478bd9Sstevel@tonic-gate * 6497c478bd9Sstevel@tonic-gate * return value: # of reg sets on success, zero on error 6507c478bd9Sstevel@tonic-gate */ 6517c478bd9Sstevel@tonic-gate uint_t 6527c478bd9Sstevel@tonic-gate get_nreg_set(dev_info_t *child) 6537c478bd9Sstevel@tonic-gate { 6547c478bd9Sstevel@tonic-gate pci_regspec_t *pci_rp; 6557c478bd9Sstevel@tonic-gate int i, n; 6567c478bd9Sstevel@tonic-gate 6577c478bd9Sstevel@tonic-gate /* 6587c478bd9Sstevel@tonic-gate * Get the reg property for the device. 6597c478bd9Sstevel@tonic-gate */ 6607c478bd9Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", 6617c478bd9Sstevel@tonic-gate (caddr_t)&pci_rp, &i) != DDI_SUCCESS) 6627c478bd9Sstevel@tonic-gate return (0); 6637c478bd9Sstevel@tonic-gate 6647c478bd9Sstevel@tonic-gate n = i / (int)sizeof (pci_regspec_t); 6657c478bd9Sstevel@tonic-gate kmem_free(pci_rp, i); 6667c478bd9Sstevel@tonic-gate return (n); 6677c478bd9Sstevel@tonic-gate } 6687c478bd9Sstevel@tonic-gate 6697c478bd9Sstevel@tonic-gate 6707c478bd9Sstevel@tonic-gate /* 6717c478bd9Sstevel@tonic-gate * get_nintr 6727c478bd9Sstevel@tonic-gate * 6737c478bd9Sstevel@tonic-gate * Given a dev info pointer to a pci child, this routine returns the 6747c478bd9Sstevel@tonic-gate * number of items in its "interrupts" property. 6757c478bd9Sstevel@tonic-gate * 6767c478bd9Sstevel@tonic-gate * used by: pci_ctlops() - DDI_CTLOPS_NREGS 6777c478bd9Sstevel@tonic-gate * 6787c478bd9Sstevel@tonic-gate * return value: # of interrupts on success, zero on error 6797c478bd9Sstevel@tonic-gate */ 6807c478bd9Sstevel@tonic-gate uint_t 6817c478bd9Sstevel@tonic-gate get_nintr(dev_info_t *child) 6827c478bd9Sstevel@tonic-gate { 6837c478bd9Sstevel@tonic-gate int *pci_ip; 6847c478bd9Sstevel@tonic-gate int i, n; 6857c478bd9Sstevel@tonic-gate 6867c478bd9Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 6877c478bd9Sstevel@tonic-gate "interrupts", (caddr_t)&pci_ip, &i) != DDI_SUCCESS) 6887c478bd9Sstevel@tonic-gate return (0); 6897c478bd9Sstevel@tonic-gate 6907c478bd9Sstevel@tonic-gate n = i / (int)sizeof (uint_t); 6917c478bd9Sstevel@tonic-gate kmem_free(pci_ip, i); 6927c478bd9Sstevel@tonic-gate return (n); 6937c478bd9Sstevel@tonic-gate } 6947c478bd9Sstevel@tonic-gate 6957c478bd9Sstevel@tonic-gate uint64_t 6967c478bd9Sstevel@tonic-gate pci_get_cfg_pabase(pci_t *pci_p) 6977c478bd9Sstevel@tonic-gate { 6987c478bd9Sstevel@tonic-gate int i; 6997c478bd9Sstevel@tonic-gate pci_ranges_t *rangep = pci_p->pci_ranges; 7007c478bd9Sstevel@tonic-gate int nrange = pci_p->pci_ranges_length / sizeof (pci_ranges_t); 7017c478bd9Sstevel@tonic-gate uint32_t cfg_space_type = PCI_REG_ADDR_G(PCI_ADDR_CONFIG); 7027c478bd9Sstevel@tonic-gate 7037c478bd9Sstevel@tonic-gate ASSERT(cfg_space_type == 0); 7047c478bd9Sstevel@tonic-gate 7057c478bd9Sstevel@tonic-gate for (i = 0; i < nrange; i++, rangep++) { 7067c478bd9Sstevel@tonic-gate if (PCI_REG_ADDR_G(rangep->child_high) == cfg_space_type) 7077c478bd9Sstevel@tonic-gate break; 7087c478bd9Sstevel@tonic-gate } 7097c478bd9Sstevel@tonic-gate 7107c478bd9Sstevel@tonic-gate if (i >= nrange) 711f47a9c50Smathue cmn_err(CE_PANIC, "no cfg space in pci(%p) ranges prop.\n", 7127c478bd9Sstevel@tonic-gate (void *)pci_p); 7137c478bd9Sstevel@tonic-gate 7147c478bd9Sstevel@tonic-gate return (((uint64_t)rangep->parent_high << 32) | rangep->parent_low); 7157c478bd9Sstevel@tonic-gate } 7167c478bd9Sstevel@tonic-gate 7177c478bd9Sstevel@tonic-gate int 7187c478bd9Sstevel@tonic-gate pci_cfg_report(dev_info_t *dip, ddi_fm_error_t *derr, pci_errstate_t *pci_err_p, 7197c478bd9Sstevel@tonic-gate int caller, uint32_t prierr) 7207c478bd9Sstevel@tonic-gate { 7217c478bd9Sstevel@tonic-gate int fatal = 0; 7227c478bd9Sstevel@tonic-gate int nonfatal = 0; 7237c478bd9Sstevel@tonic-gate int i; 7247c478bd9Sstevel@tonic-gate 7257c478bd9Sstevel@tonic-gate ASSERT(dip); 7267c478bd9Sstevel@tonic-gate 7277c478bd9Sstevel@tonic-gate derr->fme_ena = derr->fme_ena ? derr->fme_ena : 7287c478bd9Sstevel@tonic-gate fm_ena_generate(0, FM_ENA_FMT1); 7297c478bd9Sstevel@tonic-gate 7307c478bd9Sstevel@tonic-gate for (i = 0; pci_err_tbl[i].err_class != NULL; i++) { 7317c478bd9Sstevel@tonic-gate if (pci_err_p->pci_cfg_stat & pci_err_tbl[i].reg_bit) { 7327c478bd9Sstevel@tonic-gate char buf[FM_MAX_CLASS]; 7337c478bd9Sstevel@tonic-gate 7347c478bd9Sstevel@tonic-gate (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", 7357c478bd9Sstevel@tonic-gate PCI_ERROR_SUBCLASS, 7367c478bd9Sstevel@tonic-gate pci_err_tbl[i].err_class); 7377c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(dip, buf, derr->fme_ena, 7387c478bd9Sstevel@tonic-gate DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, 7397c478bd9Sstevel@tonic-gate PCI_CONFIG_STATUS, DATA_TYPE_UINT16, 7407c478bd9Sstevel@tonic-gate pci_err_p->pci_cfg_stat, 7417c478bd9Sstevel@tonic-gate PCI_CONFIG_COMMAND, DATA_TYPE_UINT16, 7427c478bd9Sstevel@tonic-gate pci_err_p->pci_cfg_comm, 7437c478bd9Sstevel@tonic-gate PCI_PA, DATA_TYPE_UINT64, 7447c478bd9Sstevel@tonic-gate pci_err_p->pci_pa, 7457c478bd9Sstevel@tonic-gate NULL); 7467c478bd9Sstevel@tonic-gate 7477c478bd9Sstevel@tonic-gate switch (pci_err_tbl[i].reg_bit) { 7487c478bd9Sstevel@tonic-gate case PCI_STAT_S_SYSERR: 7497c478bd9Sstevel@tonic-gate /* 7507c478bd9Sstevel@tonic-gate * address parity error on dma - treat as fatal 7517c478bd9Sstevel@tonic-gate */ 7527c478bd9Sstevel@tonic-gate fatal++; 7537c478bd9Sstevel@tonic-gate break; 7547c478bd9Sstevel@tonic-gate case PCI_STAT_R_MAST_AB: 7557c478bd9Sstevel@tonic-gate case PCI_STAT_R_TARG_AB: 7567c478bd9Sstevel@tonic-gate case PCI_STAT_S_PERROR: 7577c478bd9Sstevel@tonic-gate if (prierr) { 7587c478bd9Sstevel@tonic-gate /* 7597c478bd9Sstevel@tonic-gate * piow case are already handled in 7607c478bd9Sstevel@tonic-gate * pbm_afsr_report() 7617c478bd9Sstevel@tonic-gate */ 7627c478bd9Sstevel@tonic-gate break; 7637c478bd9Sstevel@tonic-gate } 7647c478bd9Sstevel@tonic-gate if (caller != PCI_TRAP_CALL) { 7657c478bd9Sstevel@tonic-gate /* 7667c478bd9Sstevel@tonic-gate * if we haven't come from trap handler 7677c478bd9Sstevel@tonic-gate * we won't have an address 7687c478bd9Sstevel@tonic-gate */ 7697c478bd9Sstevel@tonic-gate fatal++; 7707c478bd9Sstevel@tonic-gate break; 7717c478bd9Sstevel@tonic-gate } 7727c478bd9Sstevel@tonic-gate 7737c478bd9Sstevel@tonic-gate /* 7747c478bd9Sstevel@tonic-gate * queue target ereport - use return from 7757c478bd9Sstevel@tonic-gate * pci_lookup_handle() to determine if sync 7767c478bd9Sstevel@tonic-gate * or async 7777c478bd9Sstevel@tonic-gate */ 7787c478bd9Sstevel@tonic-gate nonfatal++; 77900d0963fSdilpreet pci_target_enqueue(derr->fme_ena, 78000d0963fSdilpreet pci_err_tbl[i].terr_class, 78100d0963fSdilpreet PCI_ERROR_SUBCLASS, 78200d0963fSdilpreet (uint64_t)derr->fme_bus_specific); 7837c478bd9Sstevel@tonic-gate break; 7847c478bd9Sstevel@tonic-gate default: 7857c478bd9Sstevel@tonic-gate /* 7867c478bd9Sstevel@tonic-gate * dpe on dma write or ta on dma 7877c478bd9Sstevel@tonic-gate */ 7887c478bd9Sstevel@tonic-gate nonfatal++; 7897c478bd9Sstevel@tonic-gate break; 7907c478bd9Sstevel@tonic-gate } 7917c478bd9Sstevel@tonic-gate } 7927c478bd9Sstevel@tonic-gate } 7937c478bd9Sstevel@tonic-gate 7947c478bd9Sstevel@tonic-gate if (fatal) 7957c478bd9Sstevel@tonic-gate return (DDI_FM_FATAL); 7967c478bd9Sstevel@tonic-gate else if (nonfatal) 7977c478bd9Sstevel@tonic-gate return (DDI_FM_NONFATAL); 7987c478bd9Sstevel@tonic-gate 7997c478bd9Sstevel@tonic-gate return (DDI_FM_OK); 8007c478bd9Sstevel@tonic-gate } 8017c478bd9Sstevel@tonic-gate 8027c478bd9Sstevel@tonic-gate void 8037c478bd9Sstevel@tonic-gate pci_child_cfg_save(dev_info_t *dip) 8047c478bd9Sstevel@tonic-gate { 8057c478bd9Sstevel@tonic-gate dev_info_t *cdip; 8067c478bd9Sstevel@tonic-gate int ret = DDI_SUCCESS; 8077c478bd9Sstevel@tonic-gate 8087c478bd9Sstevel@tonic-gate /* 8097c478bd9Sstevel@tonic-gate * Save the state of the configuration headers of child 8107c478bd9Sstevel@tonic-gate * nodes. 8117c478bd9Sstevel@tonic-gate */ 8127c478bd9Sstevel@tonic-gate 8137c478bd9Sstevel@tonic-gate for (cdip = ddi_get_child(dip); cdip != NULL; 8147c478bd9Sstevel@tonic-gate cdip = ddi_get_next_sibling(cdip)) { 8157c478bd9Sstevel@tonic-gate 8167c478bd9Sstevel@tonic-gate /* 8177c478bd9Sstevel@tonic-gate * Not interested in children who are not already 8187c478bd9Sstevel@tonic-gate * init'ed. They will be set up in init_child(). 8197c478bd9Sstevel@tonic-gate */ 8207c478bd9Sstevel@tonic-gate if (i_ddi_node_state(cdip) < DS_INITIALIZED) { 8217c478bd9Sstevel@tonic-gate DEBUG2(DBG_DETACH, dip, "DDI_SUSPEND: skipping " 8227c478bd9Sstevel@tonic-gate "%s%d not in CF1\n", ddi_driver_name(cdip), 8237c478bd9Sstevel@tonic-gate ddi_get_instance(cdip)); 8247c478bd9Sstevel@tonic-gate 8257c478bd9Sstevel@tonic-gate continue; 8267c478bd9Sstevel@tonic-gate } 8277c478bd9Sstevel@tonic-gate 8287c478bd9Sstevel@tonic-gate /* 8297c478bd9Sstevel@tonic-gate * Only save config registers if not already saved by child. 8307c478bd9Sstevel@tonic-gate */ 8317c478bd9Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, 8327c478bd9Sstevel@tonic-gate SAVED_CONFIG_REGS) == 1) { 8337c478bd9Sstevel@tonic-gate 8347c478bd9Sstevel@tonic-gate continue; 8357c478bd9Sstevel@tonic-gate } 8367c478bd9Sstevel@tonic-gate 8377c478bd9Sstevel@tonic-gate /* 8387c478bd9Sstevel@tonic-gate * The nexus needs to save config registers. Create a property 8397c478bd9Sstevel@tonic-gate * so it knows to restore on resume. 8407c478bd9Sstevel@tonic-gate */ 8417c478bd9Sstevel@tonic-gate ret = ndi_prop_create_boolean(DDI_DEV_T_NONE, cdip, 8427c478bd9Sstevel@tonic-gate "nexus-saved-config-regs"); 8437c478bd9Sstevel@tonic-gate 8447c478bd9Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) { 8457c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d can't update prop %s", 8467c478bd9Sstevel@tonic-gate ddi_driver_name(cdip), ddi_get_instance(cdip), 8477c478bd9Sstevel@tonic-gate "nexus-saved-config-regs"); 8487c478bd9Sstevel@tonic-gate } 8497c478bd9Sstevel@tonic-gate 8507c478bd9Sstevel@tonic-gate (void) pci_save_config_regs(cdip); 8517c478bd9Sstevel@tonic-gate } 8527c478bd9Sstevel@tonic-gate } 8537c478bd9Sstevel@tonic-gate 8547c478bd9Sstevel@tonic-gate void 8557c478bd9Sstevel@tonic-gate pci_child_cfg_restore(dev_info_t *dip) 8567c478bd9Sstevel@tonic-gate { 8577c478bd9Sstevel@tonic-gate dev_info_t *cdip; 8587c478bd9Sstevel@tonic-gate 8597c478bd9Sstevel@tonic-gate /* 8607c478bd9Sstevel@tonic-gate * Restore config registers for children that did not save 8617c478bd9Sstevel@tonic-gate * their own registers. Children pwr states are UNKNOWN after 8627c478bd9Sstevel@tonic-gate * a resume since it is possible for the PM framework to call 8637c478bd9Sstevel@tonic-gate * resume without an actual power cycle. (ie if suspend fails). 8647c478bd9Sstevel@tonic-gate */ 8657c478bd9Sstevel@tonic-gate for (cdip = ddi_get_child(dip); cdip != NULL; 8667c478bd9Sstevel@tonic-gate cdip = ddi_get_next_sibling(cdip)) { 8677c478bd9Sstevel@tonic-gate 8687c478bd9Sstevel@tonic-gate /* 8697c478bd9Sstevel@tonic-gate * Not interested in children who are not already 8707c478bd9Sstevel@tonic-gate * init'ed. They will be set up by init_child(). 8717c478bd9Sstevel@tonic-gate */ 8727c478bd9Sstevel@tonic-gate if (i_ddi_node_state(cdip) < DS_INITIALIZED) { 8737c478bd9Sstevel@tonic-gate DEBUG2(DBG_DETACH, dip, 8747c478bd9Sstevel@tonic-gate "DDI_RESUME: skipping %s%d not in CF1\n", 8757c478bd9Sstevel@tonic-gate ddi_driver_name(cdip), ddi_get_instance(cdip)); 8767c478bd9Sstevel@tonic-gate continue; 8777c478bd9Sstevel@tonic-gate } 8787c478bd9Sstevel@tonic-gate 8797c478bd9Sstevel@tonic-gate /* 8807c478bd9Sstevel@tonic-gate * Only restore config registers if saved by nexus. 8817c478bd9Sstevel@tonic-gate */ 8827c478bd9Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS, 8837c478bd9Sstevel@tonic-gate "nexus-saved-config-regs") == 1) { 8847c478bd9Sstevel@tonic-gate (void) pci_restore_config_regs(cdip); 8857c478bd9Sstevel@tonic-gate 8867c478bd9Sstevel@tonic-gate DEBUG2(DBG_PWR, dip, 8877c478bd9Sstevel@tonic-gate "DDI_RESUME: nexus restoring %s%d config regs\n", 8887c478bd9Sstevel@tonic-gate ddi_driver_name(cdip), ddi_get_instance(cdip)); 8897c478bd9Sstevel@tonic-gate 8907c478bd9Sstevel@tonic-gate if (ndi_prop_remove(DDI_DEV_T_NONE, cdip, 8917c478bd9Sstevel@tonic-gate "nexus-saved-config-regs") != DDI_PROP_SUCCESS) { 8927c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d can't remove prop %s", 8937c478bd9Sstevel@tonic-gate ddi_driver_name(cdip), 8947c478bd9Sstevel@tonic-gate ddi_get_instance(cdip), 8957c478bd9Sstevel@tonic-gate "nexus-saved-config-regs"); 8967c478bd9Sstevel@tonic-gate } 8977c478bd9Sstevel@tonic-gate } 8987c478bd9Sstevel@tonic-gate } 8997c478bd9Sstevel@tonic-gate } 900