10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51501Sgovinda * Common Development and Distribution License (the "License"). 61501Sgovinda * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*10187SKrishna.Elango@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate /* 270Sstevel@tonic-gate * PCI nexus utility routines: 280Sstevel@tonic-gate * property and config routines for attach() 290Sstevel@tonic-gate * reg/intr/range/assigned-address property routines for bus_map() 300Sstevel@tonic-gate * init_child() 310Sstevel@tonic-gate * fault handling 320Sstevel@tonic-gate */ 330Sstevel@tonic-gate 340Sstevel@tonic-gate #include <sys/types.h> 350Sstevel@tonic-gate #include <sys/kmem.h> 360Sstevel@tonic-gate #include <sys/async.h> 370Sstevel@tonic-gate #include <sys/sysmacros.h> 380Sstevel@tonic-gate #include <sys/sunddi.h> 390Sstevel@tonic-gate #include <sys/sunndi.h> 400Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 410Sstevel@tonic-gate #include "px_obj.h" 42*10187SKrishna.Elango@Sun.COM #include <sys/pcie_pwr.h> 430Sstevel@tonic-gate 440Sstevel@tonic-gate /*LINTLIBRARY*/ 450Sstevel@tonic-gate 460Sstevel@tonic-gate /* 470Sstevel@tonic-gate * px_get_props 480Sstevel@tonic-gate * 490Sstevel@tonic-gate * This function is called from the attach routine to get the key 500Sstevel@tonic-gate * properties of the pci nodes. 510Sstevel@tonic-gate * 520Sstevel@tonic-gate * used by: px_attach() 530Sstevel@tonic-gate * 540Sstevel@tonic-gate * return value: DDI_FAILURE on failure 550Sstevel@tonic-gate */ 560Sstevel@tonic-gate int 570Sstevel@tonic-gate px_get_props(px_t *px_p, dev_info_t *dip) 580Sstevel@tonic-gate { 590Sstevel@tonic-gate int i, no_of_intrs; 600Sstevel@tonic-gate 610Sstevel@tonic-gate /* 620Sstevel@tonic-gate * Get the bus-ranges property. 630Sstevel@tonic-gate */ 640Sstevel@tonic-gate i = sizeof (px_p->px_bus_range); 650Sstevel@tonic-gate if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 660Sstevel@tonic-gate "bus-range", (caddr_t)&px_p->px_bus_range, &i) != DDI_SUCCESS) { 670Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d: no bus-range property\n", 680Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip)); 690Sstevel@tonic-gate return (DDI_FAILURE); 700Sstevel@tonic-gate } 710Sstevel@tonic-gate DBG(DBG_ATTACH, dip, "get_px_properties: bus-range (%x,%x)\n", 726313Skrishnae px_p->px_bus_range.lo, px_p->px_bus_range.hi); 730Sstevel@tonic-gate 740Sstevel@tonic-gate /* 750Sstevel@tonic-gate * Get the interrupts property. 760Sstevel@tonic-gate */ 770Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 786313Skrishnae "interrupts", (caddr_t)&px_p->px_inos, 796313Skrishnae &px_p->px_inos_len) != DDI_SUCCESS) { 800Sstevel@tonic-gate 810Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d: no interrupts property\n", 826313Skrishnae ddi_driver_name(dip), ddi_get_instance(dip)); 830Sstevel@tonic-gate return (DDI_FAILURE); 840Sstevel@tonic-gate } 850Sstevel@tonic-gate 860Sstevel@tonic-gate /* 870Sstevel@tonic-gate * figure out number of interrupts in the "interrupts" property 880Sstevel@tonic-gate * and convert them all into ino. 890Sstevel@tonic-gate */ 900Sstevel@tonic-gate i = ddi_getprop(DDI_DEV_T_ANY, dip, 0, "#interrupt-cells", 1); 910Sstevel@tonic-gate i = CELLS_1275_TO_BYTES(i); 920Sstevel@tonic-gate no_of_intrs = px_p->px_inos_len / i; 930Sstevel@tonic-gate for (i = 0; i < no_of_intrs; i++) 940Sstevel@tonic-gate px_p->px_inos[i] = px_p->px_inos[i] & 0x3F; 950Sstevel@tonic-gate 960Sstevel@tonic-gate /* 970Sstevel@tonic-gate * Get the ranges property. 980Sstevel@tonic-gate */ 990Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges", 1006313Skrishnae (caddr_t)&px_p->px_ranges_p, &px_p->px_ranges_length) != 1016313Skrishnae DDI_SUCCESS) { 1020Sstevel@tonic-gate 1030Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d: no ranges property\n", 1046313Skrishnae ddi_driver_name(dip), ddi_get_instance(dip)); 1050Sstevel@tonic-gate kmem_free(px_p->px_inos, px_p->px_inos_len); 1060Sstevel@tonic-gate return (DDI_FAILURE); 1070Sstevel@tonic-gate } 1080Sstevel@tonic-gate 1090Sstevel@tonic-gate return (DDI_SUCCESS); 1100Sstevel@tonic-gate } 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate /* 1130Sstevel@tonic-gate * px_free_props: 1140Sstevel@tonic-gate * 1150Sstevel@tonic-gate * This routine frees the memory used to cache the "interrupts" 1160Sstevel@tonic-gate * and "ranges" properties of the pci bus device node. 1170Sstevel@tonic-gate * 1180Sstevel@tonic-gate * used by: px_detach() 1190Sstevel@tonic-gate * 1200Sstevel@tonic-gate * return value: none 1210Sstevel@tonic-gate */ 1220Sstevel@tonic-gate void 1230Sstevel@tonic-gate px_free_props(px_t *px_p) 1240Sstevel@tonic-gate { 1250Sstevel@tonic-gate kmem_free(px_p->px_inos, px_p->px_inos_len); 1260Sstevel@tonic-gate kmem_free(px_p->px_ranges_p, px_p->px_ranges_length); 1270Sstevel@tonic-gate } 1280Sstevel@tonic-gate 1290Sstevel@tonic-gate /* 1300Sstevel@tonic-gate * px_reloc_reg 1310Sstevel@tonic-gate * 1320Sstevel@tonic-gate * If the "reg" entry (*px_rp) is relocatable, lookup "assigned-addresses" 1330Sstevel@tonic-gate * property to fetch corresponding relocated address. 1340Sstevel@tonic-gate * 1350Sstevel@tonic-gate * used by: px_map() 1360Sstevel@tonic-gate * 1370Sstevel@tonic-gate * return value: 1380Sstevel@tonic-gate * 1390Sstevel@tonic-gate * DDI_SUCCESS - on success 1400Sstevel@tonic-gate * DDI_ME_INVAL - regspec is invalid 1410Sstevel@tonic-gate */ 1420Sstevel@tonic-gate int 1430Sstevel@tonic-gate px_reloc_reg(dev_info_t *dip, dev_info_t *rdip, px_t *px_p, 1440Sstevel@tonic-gate pci_regspec_t *rp) 1450Sstevel@tonic-gate { 1460Sstevel@tonic-gate int assign_len, assign_entries, i; 1470Sstevel@tonic-gate pci_regspec_t *assign_p; 1480Sstevel@tonic-gate uint32_t phys_hi = rp->pci_phys_hi; 1490Sstevel@tonic-gate uint32_t space_type = phys_hi & PCI_REG_ADDR_M; /* 28-bit */ 1500Sstevel@tonic-gate 1510Sstevel@tonic-gate DBG(DBG_MAP | DBG_CONT, dip, "\tpx_reloc_reg fr: %x.%x.%x %x.%x\n", 1526313Skrishnae rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low, 1536313Skrishnae rp->pci_size_hi, rp->pci_size_low); 1540Sstevel@tonic-gate 1550Sstevel@tonic-gate if (space_type == PCI_ADDR_CONFIG || phys_hi & PCI_RELOCAT_B) 1560Sstevel@tonic-gate return (DDI_SUCCESS); 1570Sstevel@tonic-gate 1580Sstevel@tonic-gate /* 1590Sstevel@tonic-gate * Hot plug will be taken care of later 1600Sstevel@tonic-gate * if (px_p->hotplug_capable == B_FALSE) 1610Sstevel@tonic-gate */ 1620Sstevel@tonic-gate { 1630Sstevel@tonic-gate uint32_t bus = PCI_REG_BUS_G(phys_hi); 1640Sstevel@tonic-gate if (bus < px_p->px_bus_range.lo || 1650Sstevel@tonic-gate bus > px_p->px_bus_range.hi) { 1660Sstevel@tonic-gate DBG(DBG_MAP | DBG_CONT, dip, "bad bus# (%x)\n", bus); 1670Sstevel@tonic-gate return (DDI_ME_INVAL); 1680Sstevel@tonic-gate } 1690Sstevel@tonic-gate } 1700Sstevel@tonic-gate 1710Sstevel@tonic-gate i = ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 1728413SDaniel.Ice@Sun.COM "assigned-addresses", (caddr_t)&assign_p, &assign_len); 1730Sstevel@tonic-gate if (i) { 1740Sstevel@tonic-gate DBG(DBG_MAP | DBG_CONT, dip, "%s%d: assigned-addresses %d\n", 1758413SDaniel.Ice@Sun.COM ddi_driver_name(rdip), ddi_get_instance(rdip), i); 1760Sstevel@tonic-gate return (DDI_ME_INVAL); 1770Sstevel@tonic-gate } 1780Sstevel@tonic-gate 1790Sstevel@tonic-gate assign_entries = assign_len / sizeof (pci_regspec_t); 1800Sstevel@tonic-gate for (i = 0; i < assign_entries; i++, assign_p++) { 1810Sstevel@tonic-gate uint32_t assign_type = assign_p->pci_phys_hi & PCI_REG_ADDR_M; 1820Sstevel@tonic-gate uint32_t assign_addr = PCI_REG_BDFR_G(assign_p->pci_phys_hi); 1830Sstevel@tonic-gate 1840Sstevel@tonic-gate if (PCI_REG_BDFR_G(phys_hi) != assign_addr) 1850Sstevel@tonic-gate continue; 1860Sstevel@tonic-gate if (space_type == assign_type) { /* exact match */ 1870Sstevel@tonic-gate rp->pci_phys_low += assign_p->pci_phys_low; 1883549Sgovinda if (space_type == PCI_ADDR_MEM64) 1893549Sgovinda rp->pci_phys_mid += assign_p->pci_phys_mid; 1900Sstevel@tonic-gate break; 1910Sstevel@tonic-gate } 1920Sstevel@tonic-gate if (space_type == PCI_ADDR_MEM64 && 1938413SDaniel.Ice@Sun.COM assign_type == PCI_ADDR_MEM32) { 1940Sstevel@tonic-gate rp->pci_phys_low += assign_p->pci_phys_low; 1950Sstevel@tonic-gate rp->pci_phys_hi ^= PCI_ADDR_MEM64 ^ PCI_ADDR_MEM32; 1960Sstevel@tonic-gate break; 1970Sstevel@tonic-gate } 1980Sstevel@tonic-gate } 1990Sstevel@tonic-gate kmem_free(assign_p - i, assign_len); 2000Sstevel@tonic-gate DBG(DBG_MAP | DBG_CONT, dip, "\tpx_reloc_reg to: %x.%x.%x %x.%x <%d>\n", 2018413SDaniel.Ice@Sun.COM rp->pci_phys_hi, rp->pci_phys_mid, rp->pci_phys_low, 2028413SDaniel.Ice@Sun.COM rp->pci_size_hi, rp->pci_size_low, i); 2030Sstevel@tonic-gate return (i < assign_entries ? DDI_SUCCESS : DDI_ME_INVAL); 2040Sstevel@tonic-gate } 2050Sstevel@tonic-gate 2062053Sschwartz /* 2072053Sschwartz * use "ranges" to translate relocated pci regspec into parent space 2082053Sschwartz */ 2090Sstevel@tonic-gate int 2102053Sschwartz px_xlate_reg(px_t *px_p, pci_regspec_t *px_rp, struct regspec *new_rp) 2110Sstevel@tonic-gate { 2122053Sschwartz int n; 2130Sstevel@tonic-gate px_ranges_t *rng_p = px_p->px_ranges_p; 2140Sstevel@tonic-gate int rng_n = px_p->px_ranges_length / sizeof (px_ranges_t); 2153549Sgovinda uint32_t space_type = PCI_REG_ADDR_G(px_rp->pci_phys_hi); 2163549Sgovinda uint64_t reg_begin, reg_end, reg_sz; 2173549Sgovinda uint64_t rng_begin, rng_end, rng_sz; 2183549Sgovinda uint64_t addr; 2192053Sschwartz 2203549Sgovinda reg_begin = (uint64_t)px_rp->pci_phys_mid << 32 | px_rp->pci_phys_low; 2213549Sgovinda reg_sz = (uint64_t)px_rp->pci_size_hi << 32 | px_rp->pci_size_low; 2222053Sschwartz if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) { 2232053Sschwartz if (reg_begin > PCI_CONF_HDR_SIZE) 2242053Sschwartz return (DDI_ME_INVAL); 2253549Sgovinda reg_sz = reg_sz ? MIN(reg_sz, PCI_CONF_HDR_SIZE) : 2263549Sgovinda PCI_CONF_HDR_SIZE; 2272053Sschwartz reg_begin += px_rp->pci_phys_hi << 4; 2282053Sschwartz } 2293549Sgovinda reg_end = reg_begin + reg_sz - 1; 2302053Sschwartz 2310Sstevel@tonic-gate for (n = 0; n < rng_n; n++, rng_p++) { 2320Sstevel@tonic-gate if (space_type != PCI_REG_ADDR_G(rng_p->child_high)) 2330Sstevel@tonic-gate continue; /* not the same space type */ 2340Sstevel@tonic-gate 2353549Sgovinda rng_begin = (uint64_t)rng_p->child_mid << 32 | rng_p->child_low; 2363549Sgovinda rng_sz = (uint64_t)rng_p->size_high << 32 | rng_p->size_low; 2370Sstevel@tonic-gate if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) 2380Sstevel@tonic-gate rng_begin += rng_p->child_high; 2390Sstevel@tonic-gate 2403549Sgovinda rng_end = rng_begin + rng_sz - 1; 2410Sstevel@tonic-gate if (reg_begin >= rng_begin && reg_end <= rng_end) 2420Sstevel@tonic-gate break; 2430Sstevel@tonic-gate } 2440Sstevel@tonic-gate if (n >= rng_n) 2450Sstevel@tonic-gate return (DDI_ME_REGSPEC_RANGE); 2460Sstevel@tonic-gate 2473549Sgovinda addr = reg_begin - rng_begin + ((uint64_t)rng_p->parent_high << 32 | 2483549Sgovinda rng_p->parent_low); 2493549Sgovinda new_rp->regspec_addr = (uint32_t)addr; 2503549Sgovinda new_rp->regspec_bustype = (uint32_t)(addr >> 32); 2513549Sgovinda new_rp->regspec_size = (uint32_t)reg_sz; 2522053Sschwartz DBG(DBG_MAP | DBG_CONT, px_p->px_dip, 2538413SDaniel.Ice@Sun.COM "\tpx_xlate_reg: entry %d new_rp %x.%x %x\n", 2548413SDaniel.Ice@Sun.COM n, new_rp->regspec_bustype, new_rp->regspec_addr, reg_sz); 2550Sstevel@tonic-gate 2560Sstevel@tonic-gate return (DDI_SUCCESS); 2570Sstevel@tonic-gate } 2580Sstevel@tonic-gate 2590Sstevel@tonic-gate /* 2600Sstevel@tonic-gate * px_report_dev 2610Sstevel@tonic-gate * 2620Sstevel@tonic-gate * This function is called from our control ops routine on a 2630Sstevel@tonic-gate * DDI_CTLOPS_REPORTDEV request. 2640Sstevel@tonic-gate * 2650Sstevel@tonic-gate * The display format is 2660Sstevel@tonic-gate * 2670Sstevel@tonic-gate * <name><inst> at <pname><pinst> device <dev> function <func> 2680Sstevel@tonic-gate * 2690Sstevel@tonic-gate * where 2700Sstevel@tonic-gate * 2710Sstevel@tonic-gate * <name> this device's name property 2720Sstevel@tonic-gate * <inst> this device's instance number 2730Sstevel@tonic-gate * <name> parent device's name property 2740Sstevel@tonic-gate * <inst> parent device's instance number 2750Sstevel@tonic-gate * <dev> this device's device number 2760Sstevel@tonic-gate * <func> this device's function number 2770Sstevel@tonic-gate */ 2780Sstevel@tonic-gate int 2790Sstevel@tonic-gate px_report_dev(dev_info_t *dip) 2800Sstevel@tonic-gate { 2810Sstevel@tonic-gate if (dip == (dev_info_t *)0) 2820Sstevel@tonic-gate return (DDI_FAILURE); 2830Sstevel@tonic-gate cmn_err(CE_CONT, "?PCI Express-device: %s@%s, %s%d\n", 2840Sstevel@tonic-gate ddi_node_name(dip), ddi_get_name_addr(dip), 2850Sstevel@tonic-gate ddi_driver_name(dip), 2860Sstevel@tonic-gate ddi_get_instance(dip)); 2870Sstevel@tonic-gate return (DDI_SUCCESS); 2880Sstevel@tonic-gate } 2890Sstevel@tonic-gate 2900Sstevel@tonic-gate 2910Sstevel@tonic-gate /* 2920Sstevel@tonic-gate * reg property for pcimem nodes that covers the entire address 2930Sstevel@tonic-gate * space for the node: config, io, or memory. 2940Sstevel@tonic-gate */ 2950Sstevel@tonic-gate pci_regspec_t pci_pcimem_reg[3] = 2960Sstevel@tonic-gate { 2970Sstevel@tonic-gate {PCI_ADDR_CONFIG, 0, 0, 0, 0x800000 }, 2980Sstevel@tonic-gate {(uint_t)(PCI_ADDR_IO|PCI_RELOCAT_B), 0, 0, 0, PX_IO_SIZE }, 2990Sstevel@tonic-gate {(uint_t)(PCI_ADDR_MEM32|PCI_RELOCAT_B), 0, 0, 0, PX_MEM_SIZE } 3000Sstevel@tonic-gate }; 3010Sstevel@tonic-gate 3020Sstevel@tonic-gate /* 3030Sstevel@tonic-gate * px_name_child 3040Sstevel@tonic-gate * 3050Sstevel@tonic-gate * This function is called from init_child to name a node. It is 3060Sstevel@tonic-gate * also passed as a callback for node merging functions. 3070Sstevel@tonic-gate * 3080Sstevel@tonic-gate * return value: DDI_SUCCESS, DDI_FAILURE 3090Sstevel@tonic-gate */ 3100Sstevel@tonic-gate static int 3110Sstevel@tonic-gate px_name_child(dev_info_t *child, char *name, int namelen) 3120Sstevel@tonic-gate { 3130Sstevel@tonic-gate pci_regspec_t *pci_rp; 3140Sstevel@tonic-gate int reglen; 3150Sstevel@tonic-gate uint_t func; 3160Sstevel@tonic-gate char **unit_addr; 3170Sstevel@tonic-gate uint_t n; 3180Sstevel@tonic-gate 3190Sstevel@tonic-gate /* 3200Sstevel@tonic-gate * Set the address portion of the node name based on 3210Sstevel@tonic-gate * unit-address property, if it exists. 3220Sstevel@tonic-gate * The interpretation of the unit-address is DD[,F] 3230Sstevel@tonic-gate * where DD is the device id and F is the function. 3240Sstevel@tonic-gate */ 3250Sstevel@tonic-gate if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 3260Sstevel@tonic-gate DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) == 3270Sstevel@tonic-gate DDI_PROP_SUCCESS) { 3280Sstevel@tonic-gate if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 3290Sstevel@tonic-gate cmn_err(CE_WARN, "unit-address property in %s.conf" 3300Sstevel@tonic-gate " not well-formed", ddi_driver_name(child)); 3310Sstevel@tonic-gate ddi_prop_free(unit_addr); 3320Sstevel@tonic-gate return (DDI_FAILURE); 3330Sstevel@tonic-gate } 3340Sstevel@tonic-gate (void) snprintf(name, namelen, "%s", *unit_addr); 3350Sstevel@tonic-gate ddi_prop_free(unit_addr); 3360Sstevel@tonic-gate return (DDI_SUCCESS); 3370Sstevel@tonic-gate } 3380Sstevel@tonic-gate 3390Sstevel@tonic-gate /* 3400Sstevel@tonic-gate * The unit-address property is does not exist. Set the address 3410Sstevel@tonic-gate * portion of the node name based on the function and device number. 3420Sstevel@tonic-gate */ 3430Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 3440Sstevel@tonic-gate "reg", (int **)&pci_rp, (uint_t *)®len) == DDI_SUCCESS) { 3450Sstevel@tonic-gate if (((reglen * sizeof (int)) % sizeof (pci_regspec_t)) != 0) { 3460Sstevel@tonic-gate cmn_err(CE_WARN, "reg property not well-formed"); 3470Sstevel@tonic-gate return (DDI_FAILURE); 3480Sstevel@tonic-gate } 3490Sstevel@tonic-gate 3500Sstevel@tonic-gate func = PCI_REG_FUNC_G(pci_rp[0].pci_phys_hi); 3510Sstevel@tonic-gate if (func != 0) 3520Sstevel@tonic-gate (void) snprintf(name, namelen, "%x,%x", 3538413SDaniel.Ice@Sun.COM PCI_REG_DEV_G(pci_rp[0].pci_phys_hi), func); 3540Sstevel@tonic-gate else 3550Sstevel@tonic-gate (void) snprintf(name, namelen, "%x", 3568413SDaniel.Ice@Sun.COM PCI_REG_DEV_G(pci_rp[0].pci_phys_hi)); 3570Sstevel@tonic-gate ddi_prop_free(pci_rp); 3580Sstevel@tonic-gate return (DDI_SUCCESS); 3590Sstevel@tonic-gate } 3600Sstevel@tonic-gate 3610Sstevel@tonic-gate cmn_err(CE_WARN, "cannot name pci child '%s'", ddi_node_name(child)); 3620Sstevel@tonic-gate return (DDI_FAILURE); 3630Sstevel@tonic-gate } 3640Sstevel@tonic-gate 3650Sstevel@tonic-gate int 3660Sstevel@tonic-gate px_uninit_child(px_t *px_p, dev_info_t *child) 3670Sstevel@tonic-gate { 3680Sstevel@tonic-gate DBG(DBG_INIT_CLD, px_p->px_dip, 3690Sstevel@tonic-gate "DDI_CTLOPS_UNINITCHILD: arg=%s%d\n", 3700Sstevel@tonic-gate ddi_driver_name(child), ddi_get_instance(child)); 3710Sstevel@tonic-gate 3720Sstevel@tonic-gate ddi_set_name_addr(child, NULL); 3730Sstevel@tonic-gate ddi_remove_minor_node(child, NULL); 3748413SDaniel.Ice@Sun.COM 3758413SDaniel.Ice@Sun.COM /* 3768413SDaniel.Ice@Sun.COM * XXX Clear parent private data used as a flag to disable 3778413SDaniel.Ice@Sun.COM * iommu BDF protection 3788413SDaniel.Ice@Sun.COM */ 3798413SDaniel.Ice@Sun.COM if ((intptr_t)ddi_get_parent_data(child) == 1) 3808413SDaniel.Ice@Sun.COM ddi_set_parent_data(child, NULL); 3818413SDaniel.Ice@Sun.COM 3820Sstevel@tonic-gate impl_rem_dev_props(child); 38327Sjchu 38427Sjchu DBG(DBG_PWR, ddi_get_parent(child), "\n\n"); 3850Sstevel@tonic-gate 38627Sjchu pcie_uninitchild(child); 38727Sjchu 38827Sjchu return (DDI_SUCCESS); 3890Sstevel@tonic-gate } 3900Sstevel@tonic-gate 3910Sstevel@tonic-gate /* 3920Sstevel@tonic-gate * px_init_child 3930Sstevel@tonic-gate * 3940Sstevel@tonic-gate * This function is called from our control ops routine on a 3950Sstevel@tonic-gate * DDI_CTLOPS_INITCHILD request. It builds and sets the device's 3960Sstevel@tonic-gate * parent private data area. 3970Sstevel@tonic-gate * 3980Sstevel@tonic-gate * used by: pci_ctlops() 3990Sstevel@tonic-gate * 4000Sstevel@tonic-gate * return value: none 4010Sstevel@tonic-gate */ 4020Sstevel@tonic-gate int 4030Sstevel@tonic-gate px_init_child(px_t *px_p, dev_info_t *child) 4040Sstevel@tonic-gate { 40527Sjchu dev_info_t *parent_dip = px_p->px_dip; 40627Sjchu pci_regspec_t *pci_rp; 40727Sjchu char name[10]; 40827Sjchu int i, no_config; 4098413SDaniel.Ice@Sun.COM intptr_t ppd = NULL; 4100Sstevel@tonic-gate 4110Sstevel@tonic-gate /* 4120Sstevel@tonic-gate * The following is a special case for pcimem nodes. 4130Sstevel@tonic-gate * For these nodes we create a reg property with a 4140Sstevel@tonic-gate * single entry that covers the entire address space 4150Sstevel@tonic-gate * for the node (config, io or memory). 4160Sstevel@tonic-gate */ 4170Sstevel@tonic-gate if (strcmp(ddi_driver_name(child), "pcimem") == 0) { 4180Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_NONE, child, 4190Sstevel@tonic-gate DDI_PROP_CANSLEEP, "reg", (caddr_t)pci_pcimem_reg, 4200Sstevel@tonic-gate sizeof (pci_pcimem_reg)); 4210Sstevel@tonic-gate ddi_set_name_addr(child, "0"); 4220Sstevel@tonic-gate ddi_set_parent_data(child, NULL); 4230Sstevel@tonic-gate return (DDI_SUCCESS); 4240Sstevel@tonic-gate } 4250Sstevel@tonic-gate 4260Sstevel@tonic-gate /* 4270Sstevel@tonic-gate * Check whether the node has config space or is a hard decode 4280Sstevel@tonic-gate * node (possibly created by a driver.conf file). 4290Sstevel@tonic-gate */ 4300Sstevel@tonic-gate no_config = ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 4310Sstevel@tonic-gate "no-config", 0); 4320Sstevel@tonic-gate 4330Sstevel@tonic-gate /* 4348413SDaniel.Ice@Sun.COM * XXX set ppd to 1 to disable iommu BDF protection 4358413SDaniel.Ice@Sun.COM * It relies on unused parent private data for PCI devices. 4368413SDaniel.Ice@Sun.COM */ 4378413SDaniel.Ice@Sun.COM if (ddi_prop_exists(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS, 4388413SDaniel.Ice@Sun.COM "dvma-share")) 4398413SDaniel.Ice@Sun.COM ppd = 1; 4408413SDaniel.Ice@Sun.COM 4418413SDaniel.Ice@Sun.COM /* 4420Sstevel@tonic-gate * Pseudo nodes indicate a prototype node with per-instance 4430Sstevel@tonic-gate * properties to be merged into the real h/w device node. 4440Sstevel@tonic-gate * However, do not merge if the no-config property is set 4450Sstevel@tonic-gate * (see PSARC 2000/088). 4460Sstevel@tonic-gate */ 4470Sstevel@tonic-gate if ((ndi_dev_is_persistent_node(child) == 0) && (no_config == 0)) { 4480Sstevel@tonic-gate extern int pci_allow_pseudo_children; 4490Sstevel@tonic-gate 4500Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, child, 4510Sstevel@tonic-gate DDI_PROP_DONTPASS, "reg", (caddr_t)&pci_rp, &i) == 4520Sstevel@tonic-gate DDI_SUCCESS) { 4530Sstevel@tonic-gate cmn_err(CE_WARN, "cannot merge prototype from %s.conf", 4540Sstevel@tonic-gate ddi_driver_name(child)); 4550Sstevel@tonic-gate kmem_free(pci_rp, i); 4560Sstevel@tonic-gate return (DDI_NOT_WELL_FORMED); 4570Sstevel@tonic-gate } 4580Sstevel@tonic-gate /* 4590Sstevel@tonic-gate * Name the child 4600Sstevel@tonic-gate */ 4610Sstevel@tonic-gate if (px_name_child(child, name, 10) != DDI_SUCCESS) 4620Sstevel@tonic-gate return (DDI_FAILURE); 4630Sstevel@tonic-gate 4640Sstevel@tonic-gate ddi_set_name_addr(child, name); 4658413SDaniel.Ice@Sun.COM ddi_set_parent_data(child, (void *)ppd); 4660Sstevel@tonic-gate 4670Sstevel@tonic-gate /* 4680Sstevel@tonic-gate * Try to merge the properties from this prototype 4690Sstevel@tonic-gate * node into real h/w nodes. 4700Sstevel@tonic-gate */ 4710Sstevel@tonic-gate if (ndi_merge_node(child, px_name_child) == DDI_SUCCESS) { 4720Sstevel@tonic-gate /* 4730Sstevel@tonic-gate * Merged ok - return failure to remove the node. 4740Sstevel@tonic-gate */ 4750Sstevel@tonic-gate ddi_set_name_addr(child, NULL); 4760Sstevel@tonic-gate return (DDI_FAILURE); 4770Sstevel@tonic-gate } 4780Sstevel@tonic-gate 4790Sstevel@tonic-gate /* workaround for ddivs to run under PCI */ 4800Sstevel@tonic-gate if (pci_allow_pseudo_children) 4810Sstevel@tonic-gate return (DDI_SUCCESS); 4820Sstevel@tonic-gate 4830Sstevel@tonic-gate cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", 4840Sstevel@tonic-gate ddi_driver_name(child), ddi_get_name_addr(child), 4850Sstevel@tonic-gate ddi_driver_name(child)); 4860Sstevel@tonic-gate ddi_set_name_addr(child, NULL); 4870Sstevel@tonic-gate return (DDI_NOT_WELL_FORMED); 4880Sstevel@tonic-gate } 4890Sstevel@tonic-gate 4900Sstevel@tonic-gate if (px_name_child(child, name, 10) != DDI_SUCCESS) 4910Sstevel@tonic-gate return (DDI_FAILURE); 4920Sstevel@tonic-gate ddi_set_name_addr(child, name); 4930Sstevel@tonic-gate 4940Sstevel@tonic-gate if (no_config != 0) { 4950Sstevel@tonic-gate /* 4960Sstevel@tonic-gate * There is no config space so there's nothing more to do. 4970Sstevel@tonic-gate */ 4980Sstevel@tonic-gate return (DDI_SUCCESS); 4990Sstevel@tonic-gate } 5000Sstevel@tonic-gate 50127Sjchu if (pcie_pm_hold(parent_dip) != DDI_SUCCESS) { 50227Sjchu DBG(DBG_PWR, parent_dip, 5030Sstevel@tonic-gate "INITCHILD: px_pm_hold failed\n"); 5040Sstevel@tonic-gate return (DDI_FAILURE); 5050Sstevel@tonic-gate } 5060Sstevel@tonic-gate /* Any return of DDI_FAILURE after this must call px_pm_release */ 5070Sstevel@tonic-gate 5080Sstevel@tonic-gate /* 5090Sstevel@tonic-gate * If configuration registers were previously saved by 5100Sstevel@tonic-gate * child (before it went to D3), then let the child do the 5110Sstevel@tonic-gate * restore to set up the config regs as it'll first need to 5120Sstevel@tonic-gate * power the device out of D3. 5130Sstevel@tonic-gate */ 5140Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 5150Sstevel@tonic-gate "config-regs-saved-by-child") == 1) { 5160Sstevel@tonic-gate DBG(DBG_PWR, child, 5170Sstevel@tonic-gate "INITCHILD: config regs to be restored by child\n"); 5180Sstevel@tonic-gate 5190Sstevel@tonic-gate return (DDI_SUCCESS); 5200Sstevel@tonic-gate } 5210Sstevel@tonic-gate 52227Sjchu DBG(DBG_PWR, parent_dip, 5230Sstevel@tonic-gate "INITCHILD: config regs setup for %s@%s\n", 5240Sstevel@tonic-gate ddi_node_name(child), ddi_get_name_addr(child)); 5250Sstevel@tonic-gate 5268413SDaniel.Ice@Sun.COM ddi_set_parent_data(child, (void *)ppd); 5276313Skrishnae if (pcie_init_bus(child)) 5286313Skrishnae (void) pcie_initchild(child); 5290Sstevel@tonic-gate 5300Sstevel@tonic-gate /* 5310Sstevel@tonic-gate * Handle chip specific init-child tasks. 5320Sstevel@tonic-gate */ 53327Sjchu pcie_pm_release(parent_dip); 5340Sstevel@tonic-gate 5350Sstevel@tonic-gate return (DDI_SUCCESS); 5360Sstevel@tonic-gate } 5370Sstevel@tonic-gate 5380Sstevel@tonic-gate /* 5390Sstevel@tonic-gate * px_get_reg_set_size 5400Sstevel@tonic-gate * 5410Sstevel@tonic-gate * Given a dev info pointer to a pci child and a register number, this 5420Sstevel@tonic-gate * routine returns the size element of that reg set property. 5430Sstevel@tonic-gate * 5440Sstevel@tonic-gate * used by: pci_ctlops() - DDI_CTLOPS_REGSIZE 5450Sstevel@tonic-gate * 54627Sjchu * return value: size of reg set on success, 0 on error 5470Sstevel@tonic-gate */ 5480Sstevel@tonic-gate off_t 5490Sstevel@tonic-gate px_get_reg_set_size(dev_info_t *child, int rnumber) 5500Sstevel@tonic-gate { 5510Sstevel@tonic-gate pci_regspec_t *pci_rp; 55227Sjchu off_t size = 0; 5530Sstevel@tonic-gate int i; 5540Sstevel@tonic-gate 5550Sstevel@tonic-gate if (rnumber < 0) 55627Sjchu return (0); 5570Sstevel@tonic-gate 5580Sstevel@tonic-gate /* 5590Sstevel@tonic-gate * Get the reg property for the device. 5600Sstevel@tonic-gate */ 5610Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", 5620Sstevel@tonic-gate (caddr_t)&pci_rp, &i) != DDI_SUCCESS) 56327Sjchu return (0); 5640Sstevel@tonic-gate 5650Sstevel@tonic-gate if (rnumber >= (i / (int)sizeof (pci_regspec_t))) 5660Sstevel@tonic-gate goto done; 5670Sstevel@tonic-gate 5680Sstevel@tonic-gate size = pci_rp[rnumber].pci_size_low | 5696313Skrishnae ((uint64_t)pci_rp[rnumber].pci_size_hi << 32); 5700Sstevel@tonic-gate done: 5710Sstevel@tonic-gate kmem_free(pci_rp, i); 5720Sstevel@tonic-gate return (size); 5730Sstevel@tonic-gate } 5740Sstevel@tonic-gate 5750Sstevel@tonic-gate 5760Sstevel@tonic-gate /* 5770Sstevel@tonic-gate * px_get_nreg_set 5780Sstevel@tonic-gate * 5790Sstevel@tonic-gate * Given a dev info pointer to a pci child, this routine returns the 5800Sstevel@tonic-gate * number of sets in its "reg" property. 5810Sstevel@tonic-gate * 5820Sstevel@tonic-gate * used by: pci_ctlops() - DDI_CTLOPS_NREGS 5830Sstevel@tonic-gate * 5840Sstevel@tonic-gate * return value: # of reg sets on success, zero on error 5850Sstevel@tonic-gate */ 5860Sstevel@tonic-gate uint_t 5870Sstevel@tonic-gate px_get_nreg_set(dev_info_t *child) 5880Sstevel@tonic-gate { 5890Sstevel@tonic-gate pci_regspec_t *pci_rp; 5900Sstevel@tonic-gate int i, n; 5910Sstevel@tonic-gate 5920Sstevel@tonic-gate /* 5930Sstevel@tonic-gate * Get the reg property for the device. 5940Sstevel@tonic-gate */ 5950Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", 5960Sstevel@tonic-gate (caddr_t)&pci_rp, &i) != DDI_SUCCESS) 5970Sstevel@tonic-gate return (0); 5980Sstevel@tonic-gate 5990Sstevel@tonic-gate n = i / (int)sizeof (pci_regspec_t); 6000Sstevel@tonic-gate kmem_free(pci_rp, i); 6010Sstevel@tonic-gate return (n); 6020Sstevel@tonic-gate } 6030Sstevel@tonic-gate 6040Sstevel@tonic-gate 6050Sstevel@tonic-gate /* 6060Sstevel@tonic-gate * px_get_nintr 6070Sstevel@tonic-gate * 6080Sstevel@tonic-gate * Given a dev info pointer to a pci child, this routine returns the 6090Sstevel@tonic-gate * number of items in its "interrupts" property. 6100Sstevel@tonic-gate * 6110Sstevel@tonic-gate * used by: pci_ctlops() - DDI_CTLOPS_NREGS 6120Sstevel@tonic-gate * 6130Sstevel@tonic-gate * return value: # of interrupts on success, zero on error 6140Sstevel@tonic-gate */ 6150Sstevel@tonic-gate uint_t 6160Sstevel@tonic-gate px_get_nintr(dev_info_t *child) 6170Sstevel@tonic-gate { 6180Sstevel@tonic-gate int *pci_ip; 6190Sstevel@tonic-gate int i, n; 6200Sstevel@tonic-gate 6210Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 6220Sstevel@tonic-gate "interrupts", (caddr_t)&pci_ip, &i) != DDI_SUCCESS) 6230Sstevel@tonic-gate return (0); 6240Sstevel@tonic-gate 6250Sstevel@tonic-gate n = i / (int)sizeof (uint_t); 6260Sstevel@tonic-gate kmem_free(pci_ip, i); 6270Sstevel@tonic-gate return (n); 6280Sstevel@tonic-gate } 6290Sstevel@tonic-gate 6300Sstevel@tonic-gate uint64_t 6310Sstevel@tonic-gate px_get_cfg_pabase(px_t *px_p) 6320Sstevel@tonic-gate { 6330Sstevel@tonic-gate int i; 6340Sstevel@tonic-gate px_ranges_t *rangep = px_p->px_ranges_p; 6350Sstevel@tonic-gate int nrange = px_p->px_ranges_length / sizeof (px_ranges_t); 6360Sstevel@tonic-gate uint32_t cfg_space_type = PCI_REG_ADDR_G(PCI_ADDR_CONFIG); 6370Sstevel@tonic-gate 6380Sstevel@tonic-gate ASSERT(cfg_space_type == 0); 6390Sstevel@tonic-gate 6400Sstevel@tonic-gate for (i = 0; i < nrange; i++, rangep++) { 6410Sstevel@tonic-gate if (PCI_REG_ADDR_G(rangep->child_high) == cfg_space_type) 6420Sstevel@tonic-gate break; 6430Sstevel@tonic-gate } 6440Sstevel@tonic-gate 6450Sstevel@tonic-gate if (i >= nrange) 646671Skrishnae cmn_err(CE_PANIC, "no cfg space in px(%p) ranges prop.\n", 6478413SDaniel.Ice@Sun.COM px_p); 6480Sstevel@tonic-gate 6490Sstevel@tonic-gate return (((uint64_t)rangep->parent_high << 32) | rangep->parent_low); 6500Sstevel@tonic-gate } 6510Sstevel@tonic-gate 6520Sstevel@tonic-gate /* 6530Sstevel@tonic-gate * decodes standard PCI config space 16bit error status reg 6540Sstevel@tonic-gate */ 6550Sstevel@tonic-gate int 6560Sstevel@tonic-gate px_log_cfg_err(dev_info_t *dip, ushort_t status_reg, char *err_msg) 6570Sstevel@tonic-gate { 6580Sstevel@tonic-gate int nerr = ddi_get_instance(dip); /* temp for instance */ 6590Sstevel@tonic-gate uint64_t perr_fatal = px_perr_fatal & (1 << nerr); 6600Sstevel@tonic-gate uint64_t serr_fatal = px_serr_fatal & (1 << nerr); 6610Sstevel@tonic-gate nerr = 0; 6620Sstevel@tonic-gate 6630Sstevel@tonic-gate if ((status_reg & PCI_STAT_PERROR) && perr_fatal) 6640Sstevel@tonic-gate nerr++; 6650Sstevel@tonic-gate if ((status_reg & PCI_STAT_S_SYSERR) && serr_fatal) 6660Sstevel@tonic-gate nerr++; 6670Sstevel@tonic-gate if (status_reg & PCI_STAT_R_MAST_AB) 6680Sstevel@tonic-gate nerr++; 6690Sstevel@tonic-gate if ((status_reg & PCI_STAT_S_PERROR) && perr_fatal) 6700Sstevel@tonic-gate nerr++; 6710Sstevel@tonic-gate 6720Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d: %sPCI Express config space CSR=0x%b", 6730Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), err_msg, 6740Sstevel@tonic-gate (uint32_t)status_reg, PX_STATUS_BITS); 6750Sstevel@tonic-gate 6760Sstevel@tonic-gate return (nerr); 6770Sstevel@tonic-gate } 678