Mercurial > illumos > illumos-gate
changeset 3144:9f0744dba6f9
6468070 PCIE hotplug failed on IOBox on OPL system - ndi_ra_alloc() may fail.
6492640 Fatal PCIe Fabric Error has occurred during hotplug operation of unconfiguring the PCIe I/O boat
author | jj156685 |
---|---|
date | Mon, 20 Nov 2006 14:41:16 -0800 |
parents | 9f5d43f9dae5 |
children | 83654d8242d3 |
files | usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c usr/src/uts/common/sys/hotplug/pci/pcishpc_regs.h usr/src/uts/sun4/io/pcicfg.e.c usr/src/uts/sun4/io/px/px_pci.c |
diffstat | 4 files changed, 285 insertions(+), 92 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c Mon Nov 20 11:43:41 2006 -0800 +++ b/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c Mon Nov 20 14:41:16 2006 -0800 @@ -49,25 +49,9 @@ #include <sys/sunndi.h> #include <sys/ndi_impldefs.h> #include <sys/hotplug/pci/pcishpc.h> +#include <sys/hotplug/pci/pcishpc_regs.h> #include <sys/hotplug/hpcsvc.h> -/* - * SHPC controller registers accessed via the SHPC DWORD select and DATA - * registers in PCI configuration space relative to the SHPC capibility - * pointer. - */ -#define SHPC_BASE_OFFSET_REG 0x00 -#define SHPC_SLOTS_AVAIL_I_REG 0x01 -#define SHPC_SLOTS_AVAIL_II_REG 0x02 -#define SHPC_SLOT_CONFIGURATION_REG 0x03 -#define SHPC_PROF_IF_SBCR_REG 0x04 -#define SHPC_COMMAND_STATUS_REG 0x05 -#define SHPC_IRQ_LOCATOR_REG 0x06 -#define SHPC_SERR_LOCATOR_REG 0x07 -#define SHPC_CTRL_SERR_INT_REG 0x08 -#define SHPC_LOGICAL_SLOT_REGS 0x09 -#define SHPC_VENDOR_SPECIFIC 0x28 - /* General Register bit weights for the 32-bit SHPC registers */ #define REG_BIT0 0x00000001
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/sys/hotplug/pci/pcishpc_regs.h Mon Nov 20 14:41:16 2006 -0800 @@ -0,0 +1,60 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_HOTPLUG_PCI_PCISHPC_REGS_H +#define _SYS_HOTPLUG_PCI_PCISHPC_REGS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * SHPC controller registers accessed via the SHPC DWORD select and DATA + * registers in PCI configuration space relative to the SHPC capibility + * pointer. + */ +#define SHPC_DWORD_SELECT_OFF 0x2 +#define SHPC_DWORD_DATA_OFF 0x4 + +#define SHPC_BASE_OFFSET_REG 0x00 +#define SHPC_SLOTS_AVAIL_I_REG 0x01 +#define SHPC_SLOTS_AVAIL_II_REG 0x02 +#define SHPC_SLOT_CONFIGURATION_REG 0x03 +#define SHPC_PROF_IF_SBCR_REG 0x04 +#define SHPC_COMMAND_STATUS_REG 0x05 +#define SHPC_IRQ_LOCATOR_REG 0x06 +#define SHPC_SERR_LOCATOR_REG 0x07 +#define SHPC_CTRL_SERR_INT_REG 0x08 +#define SHPC_LOGICAL_SLOT_REGS 0x09 +#define SHPC_VENDOR_SPECIFIC 0x28 + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_HOTPLUG_PCI_PCISHPC_REGS_H */
--- a/usr/src/uts/sun4/io/pcicfg.e.c Mon Nov 20 11:43:41 2006 -0800 +++ b/usr/src/uts/sun4/io/pcicfg.e.c Mon Nov 20 14:41:16 2006 -0800 @@ -44,7 +44,9 @@ #include <sys/ddi.h> #include <sys/sunddi.h> #include <sys/sunndi.h> +#include <sys/pci_cap.h> #include <sys/hotplug/pci/pcicfg.h> +#include <sys/hotplug/pci/pcishpc_regs.h> #include <sys/ndi_impldefs.h> #define PCICFG_DEVICE_TYPE_PCI 1 @@ -100,7 +102,7 @@ static int pcicfg_slot_busnums = 8; static int pcicfg_slot_memsize = 32 * PCICFG_MEMGRAN; /* 32MB per slot */ -static int pcicfg_slot_iosize = 64 * PCICFG_IOGRAN; /* 64K per slot */ +static int pcicfg_slot_iosize = 16 * PCICFG_IOGRAN; /* 64K per slot */ static int pcicfg_chassis_per_tree = 1; static int pcicfg_sec_reset_delay = 1000000; @@ -319,6 +321,7 @@ static int pcicfg_ntbridge_unconfigure_child(dev_info_t *, uint_t); static void pcicfg_free_hole(hole_t *); static uint64_t pcicfg_alloc_hole(hole_t *, uint64_t *, uint32_t); +static int pcicfg_update_available_prop(dev_info_t *, pci_regspec_t *); #ifdef DEBUG static void pcicfg_dump_common_config(ddi_acc_handle_t config_handle); @@ -596,24 +599,38 @@ static uint8_t pcicfg_get_nslots(dev_info_t *dip, ddi_acc_handle_t handle) { - int cap_id_loc; uint8_t num_slots = 0; - - /* just depend on the pcie_cap for now. */ - if ((cap_id_loc = pcicfg_get_cap(handle, PCI_CAP_ID_PCI_E)) - > 0) { - if (pci_config_get16(handle, cap_id_loc + - PCIE_PCIECAP) & - PCIE_PCIECAP_SLOT_IMPL) - num_slots = 1; - } else /* not a PCIe switch/bridge. Must be a PCI-PCI[-X] bridge */ - if ((cap_id_loc = pcicfg_get_cap(handle, PCI_CAP_ID_SLOT_ID)) - > 0) { - uint8_t esr_reg = pci_config_get8(handle, cap_id_loc + - PCI_CAP_ID_REGS_OFF); + uint16_t cap_ptr; + + if ((PCI_CAP_LOCATE(handle, PCI_CAP_ID_PCI_HOTPLUG, + &cap_ptr)) == DDI_SUCCESS) { + uint32_t config; + + PCI_CAP_PUT8(handle, NULL, cap_ptr, SHPC_DWORD_SELECT_OFF, + SHPC_SLOT_CONFIGURATION_REG); + config = PCI_CAP_GET32(handle, NULL, cap_ptr, + SHPC_DWORD_DATA_OFF); + num_slots = config & 0x1F; + } else if ((PCI_CAP_LOCATE(handle, PCI_CAP_ID_SLOT_ID, &cap_ptr)) + == DDI_SUCCESS) { + uint8_t esr_reg = PCI_CAP_GET8(handle, NULL, + cap_ptr, PCI_CAP_ID_REGS_OFF); + num_slots = PCI_CAPSLOT_NSLOTS(esr_reg); - } - /* XXX - need to cover PCI-PCIe bridge with n slots */ + } else if ((PCI_CAP_LOCATE(handle, PCI_CAP_ID_PCI_E, &cap_ptr)) + == DDI_SUCCESS) { + int port_type = PCI_CAP_GET16(handle, NULL, cap_ptr, + PCIE_PCIECAP) & PCIE_PCIECAP_DEV_TYPE_MASK; + + if ((port_type == PCIE_PCIECAP_DEV_TYPE_DOWN) && + (PCI_CAP_GET16(handle, NULL, cap_ptr, PCIE_PCIECAP) + & PCIE_PCIECAP_SLOT_IMPL)) + num_slots = 1; + } + + DEBUG3("%s#%d has %d slots", + ddi_get_name(dip), ddi_get_instance(dip), num_slots); + return (num_slots); } @@ -2439,73 +2456,110 @@ static int pcicfg_find_resource_end(dev_info_t *dip, void *hdl) { - pcicfg_phdl_t *entry = (pcicfg_phdl_t *)hdl; + pcicfg_phdl_t *entry_p = (pcicfg_phdl_t *)hdl; pci_regspec_t *pci_ap; + pcicfg_range_t *ranges; int length; int rcount; int i; - entry->error = PCICFG_SUCCESS; - - if (dip == entry->dip) { + entry_p->error = PCICFG_SUCCESS; + + if (dip == entry_p->dip) { DEBUG0("Don't include parent bridge node\n"); return (DDI_WALK_CONTINUE); - } else { - if (ddi_getlongprop(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, "assigned-addresses", - (caddr_t)&pci_ap, &length) != DDI_PROP_SUCCESS) { - DEBUG0("Node doesn't have assigned-addresses\n"); - return (DDI_WALK_CONTINUE); + } + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "ranges", + (caddr_t)&ranges, &length) != DDI_PROP_SUCCESS) { + DEBUG0("Node doesn't have ranges\n"); + goto ap; + } + + rcount = length / sizeof (pcicfg_range_t); + + for (i = 0; i < rcount; i++) { + uint64_t base; + uint64_t mid = ranges[i].child_mid; + uint64_t lo = ranges[i].child_lo; + uint64_t size = ranges[i].size_lo; + + switch (PCI_REG_ADDR_G(ranges[i].child_hi)) { + + case PCI_REG_ADDR_G(PCI_ADDR_MEM32): + base = entry_p->memory_base; + entry_p->memory_base = MAX(base, lo + size); + break; + case PCI_REG_ADDR_G(PCI_ADDR_MEM64): + base = entry_p->memory_base; + entry_p->memory_base = MAX(base, + PCICFG_LADDR(lo, mid) + size); + break; + case PCI_REG_ADDR_G(PCI_ADDR_IO): + base = entry_p->io_base; + entry_p->io_base = MAX(base, lo + size); + break; } - - rcount = length / sizeof (pci_regspec_t); - - for (i = 0; i < rcount; i++) { - - switch (PCI_REG_ADDR_G(pci_ap[i].pci_phys_hi)) { - - case PCI_REG_ADDR_G(PCI_ADDR_MEM32): - if ((pci_ap[i].pci_phys_low + - pci_ap[i].pci_size_low) > - entry->memory_base) { - entry->memory_base = - pci_ap[i].pci_phys_low + - pci_ap[i].pci_size_low; - } - break; - case PCI_REG_ADDR_G(PCI_ADDR_MEM64): - if ((PCICFG_LADDR(pci_ap[i].pci_phys_low, + } + + kmem_free(ranges, length); + return (DDI_WALK_CONTINUE); + +ap: if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "assigned-addresses", + (caddr_t)&pci_ap, &length) != DDI_PROP_SUCCESS) { + DEBUG0("Node doesn't have assigned-addresses\n"); + return (DDI_WALK_CONTINUE); + } + + rcount = length / sizeof (pci_regspec_t); + + for (i = 0; i < rcount; i++) { + + switch (PCI_REG_ADDR_G(pci_ap[i].pci_phys_hi)) { + + case PCI_REG_ADDR_G(PCI_ADDR_MEM32): + if ((pci_ap[i].pci_phys_low + + pci_ap[i].pci_size_low) > + entry_p->memory_base) { + entry_p->memory_base = + pci_ap[i].pci_phys_low + + pci_ap[i].pci_size_low; + } + break; + case PCI_REG_ADDR_G(PCI_ADDR_MEM64): + if ((PCICFG_LADDR(pci_ap[i].pci_phys_low, + pci_ap[i].pci_phys_mid) + + pci_ap[i].pci_size_low) > + entry_p->memory_base) { + entry_p->memory_base = PCICFG_LADDR( + pci_ap[i].pci_phys_low, pci_ap[i].pci_phys_mid) + - pci_ap[i].pci_size_low) > - entry->memory_base) { - entry->memory_base = PCICFG_LADDR( - pci_ap[i].pci_phys_low, - pci_ap[i].pci_phys_mid) + - pci_ap[i].pci_size_low; - } - break; - case PCI_REG_ADDR_G(PCI_ADDR_IO): - if ((pci_ap[i].pci_phys_low + - pci_ap[i].pci_size_low) > - entry->io_base) { - entry->io_base = - pci_ap[i].pci_phys_low + - pci_ap[i].pci_size_low; - } - break; + pci_ap[i].pci_size_low; + } + break; + case PCI_REG_ADDR_G(PCI_ADDR_IO): + if ((pci_ap[i].pci_phys_low + + pci_ap[i].pci_size_low) > + entry_p->io_base) { + entry_p->io_base = + pci_ap[i].pci_phys_low + + pci_ap[i].pci_size_low; } + break; } - - /* - * free the memory allocated by ddi_getlongprop - */ - kmem_free(pci_ap, length); - - /* - * continue the walk to the next sibling to sum memory - */ - return (DDI_WALK_CONTINUE); - } + } + + /* + * free the memory allocated by ddi_getlongprop + */ + kmem_free(pci_ap, length); + + /* + * continue the walk to the next sibling to sum memory + */ + return (DDI_WALK_CONTINUE); } static int @@ -2947,6 +3001,53 @@ return (PCICFG_SUCCESS); } +static int +pcicfg_update_available_prop(dev_info_t *dip, pci_regspec_t *newone) +{ + int alen; + pci_regspec_t *avail_p; + caddr_t new_avail; + uint_t status; + + DEBUG2("pcicfg_update_available_prop() - Address %lx Size %x\n", + newone->pci_phys_low, newone->pci_size_low); + status = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "available", (caddr_t)&avail_p, &alen); + switch (status) { + case DDI_PROP_SUCCESS: + break; + case DDI_PROP_NO_MEMORY: + DEBUG0("no memory for available property\n"); + return (PCICFG_FAILURE); + default: + (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, + "available", (int *)newone, + sizeof (*newone)/sizeof (int)); + + return (PCICFG_SUCCESS); + } + + /* + * Allocate memory for the existing available plus one and then + * build it. + */ + new_avail = kmem_zalloc(alen+sizeof (*newone), KM_SLEEP); + + bcopy(avail_p, new_avail, alen); + bcopy(newone, new_avail + alen, sizeof (*newone)); + + /* Write out the new "available" spec */ + (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, + "available", (int *)new_avail, + (alen + sizeof (*newone))/sizeof (int)); + + kmem_free((caddr_t)new_avail, alen+sizeof (*newone)); + + /* Don't forget to free up memory from ddi_getlongprop */ + kmem_free((caddr_t)avail_p, alen); + + return (PCICFG_SUCCESS); +} static void pcicfg_device_on(ddi_acc_handle_t config_handle) @@ -4542,6 +4643,9 @@ * warning messages appropriately (perhaps some can be in debug mode). */ if (num_slots) { + pci_regspec_t reg; + uint64_t mem_assigned = mem_end; + uint64_t io_assigned = io_end; uint64_t mem_reqd = mem_answer + (num_slots * pcicfg_slot_memsize); uint64_t io_reqd = io_answer + (num_slots * @@ -4583,6 +4687,31 @@ *highest_bus); DEBUG3("mem_end %lx, io_end %lx, highest_bus %x\n", mem_end, io_end, *highest_bus); + + mem_size = mem_end - mem_assigned; + io_size = io_end - io_assigned; + + reg.pci_phys_mid = reg.pci_size_hi = 0; + if (io_size > 0) { + reg.pci_phys_hi = (PCI_REG_REL_M | PCI_ADDR_IO); + reg.pci_phys_low = io_assigned; + reg.pci_size_low = io_size; + if (pcicfg_update_available_prop(new_child, ®)) { + DEBUG0("Failed to update available prop " + "(io)\n"); + return (PCICFG_FAILURE); + } + } + if (mem_size > 0) { + reg.pci_phys_hi = (PCI_REG_REL_M | PCI_ADDR_MEM32); + reg.pci_phys_low = mem_assigned; + reg.pci_size_low = mem_size; + if (pcicfg_update_available_prop(new_child, ®)) { + DEBUG0("Failed to update available prop " + "(memory)\n"); + return (PCICFG_FAILURE); + } + } } /*
--- a/usr/src/uts/sun4/io/px/px_pci.c Mon Nov 20 11:43:41 2006 -0800 +++ b/usr/src/uts/sun4/io/px/px_pci.c Mon Nov 20 14:41:16 2006 -0800 @@ -620,15 +620,31 @@ } return (DDI_SUCCESS); - case DDI_POST: + case DDI_POST: { + ddi_acc_handle_t config_handle; DBG(DBG_PWR, dip, "POST_ATTACH for %s@%d\n", ddi_driver_name(rdip), ddi_get_instance(rdip)); if (as->cmd == DDI_ATTACH && as->result != DDI_SUCCESS) pcie_pm_release(dip); - (void) pcie_postattach_child(rdip); + /* + * For hotplug-capable slots, we should explicitly + * disable the errors, so that we won't panic upon + * unsupported hotplug messages. + */ + if (!ddi_prop_exists(DDI_DEV_T_ANY, rdip, + DDI_PROP_DONTPASS, "hotplug-capable")) { + (void) pcie_postattach_child(rdip); + return (DDI_SUCCESS); + } + if (pci_config_setup(rdip, &config_handle) == + DDI_SUCCESS) { + pcie_disable_errors(rdip, config_handle); + pci_config_teardown(&config_handle); + } return (DDI_SUCCESS); + } default: break; } @@ -1251,6 +1267,10 @@ } pxb->pxb_hotplug_capable = (pxb->pxb_hpc_type != HPC_NONE); + + if (pxb->pxb_hotplug_capable == B_TRUE) + (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, pxb->pxb_dip, + "hotplug-capable"); } static void