From b65731f1f612238279eb4d997f43589b535c5646 Mon Sep 17 00:00:00 2001 From: kini Date: Thu, 2 Mar 2006 17:22:20 -0800 Subject: PSARC/2005/375 PCI Hotplug Extensions for PCIe PSARC/2006/037 PCI Express Hotplug Framework Interrupt Interfaces 6379464 Integrate PSARC/2005/375 for SPARC PCI Express Hotplug Support 5049969 Make efcode' PCI configurator as the default configurator for SPARC platforms --HG-- rename : usr/src/uts/sun4u/fcode/Makefile => usr/src/uts/sparc/fcode/Makefile rename : usr/src/uts/sun4u/fcodem/Makefile => usr/src/uts/sparc/fcodem/Makefile rename : usr/src/uts/sun4u/fcpci/Makefile => usr/src/uts/sparc/fcpci/Makefile rename : usr/src/uts/sun4u/pcicfg.e/Makefile => usr/src/uts/sparc/pcicfg.e/Makefile rename : usr/src/uts/sun4u/io/pcicfg.e.c => usr/src/uts/sun4/io/pcicfg.e.c rename : usr/src/uts/sun4u/sys/fc_plat.h => usr/src/uts/sun4/sys/fc_plat.h --- .../pcidr/etc/SUNW,EC_dr,ESC_dr_req,sysevent.conf | 3 +- usr/src/lib/efcode/pci/Makefile.com | 5 +- usr/src/pkgdefs/SUNWckr/prototype_sparc | 3 +- usr/src/pkgdefs/SUNWefc.u/prototype_com | 54 +- usr/src/pkgdefs/SUNWefcl/prototype_sparc | 5 +- usr/src/uts/common/Makefile.files | 2 + usr/src/uts/common/Makefile.rules | 7 + usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c | 78 + usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c | 2379 ++++++++ usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h | 11 +- usr/src/uts/common/sys/hotplug/pci/pcishpc.h | 50 + usr/src/uts/sparc/Makefile.files | 6 +- usr/src/uts/sparc/Makefile.sparc.shared | 4 +- usr/src/uts/sparc/fcode/Makefile | 103 + usr/src/uts/sparc/fcodem/Makefile | 102 + usr/src/uts/sparc/fcpci/Makefile | 101 + usr/src/uts/sparc/ml/modstubs.s | 36 +- usr/src/uts/sparc/pcicfg.e/Makefile | 107 + usr/src/uts/sparc/pciehpc/Makefile | 87 + usr/src/uts/sparc/pcihp/Makefile | 4 +- usr/src/uts/sparc/pcishpc/Makefile | 87 + usr/src/uts/sparc/px_pci/Makefile | 5 +- usr/src/uts/sun4/Makefile.files | 3 - usr/src/uts/sun4/io/pcicfg.e.c | 5946 ++++++++++++++++++++ usr/src/uts/sun4/io/px/px.c | 105 +- usr/src/uts/sun4/io/px/px_devctl.c | 26 +- usr/src/uts/sun4/io/px/px_dma.c | 4 +- usr/src/uts/sun4/io/px/px_fm.c | 6 +- usr/src/uts/sun4/io/px/px_lib.h | 8 +- usr/src/uts/sun4/io/px/px_pci.c | 529 +- usr/src/uts/sun4/io/px/px_pci.h | 18 +- usr/src/uts/sun4/io/px/px_var.h | 9 +- usr/src/uts/sun4/sys/fc_plat.h | 102 + usr/src/uts/sun4u/Makefile.files | 1 - usr/src/uts/sun4u/Makefile.sun4u.shared | 2 - usr/src/uts/sun4u/daktari/Makefile | 11 +- usr/src/uts/sun4u/fcode/Makefile | 98 - usr/src/uts/sun4u/fcodem/Makefile | 96 - usr/src/uts/sun4u/fcpci/Makefile | 96 - usr/src/uts/sun4u/io/pcicfg.e.c | 5580 ------------------ usr/src/uts/sun4u/io/px/px_lib4u.c | 16 +- usr/src/uts/sun4u/montecarlo/Makefile | 10 +- usr/src/uts/sun4u/pcicfg.e/Makefile | 103 - usr/src/uts/sun4u/px/Makefile | 4 +- usr/src/uts/sun4u/serengeti/Makefile | 11 +- usr/src/uts/sun4u/starcat/Makefile | 10 +- usr/src/uts/sun4u/sys/Makefile | 2 +- usr/src/uts/sun4u/sys/fc_plat.h | 102 - usr/src/uts/sun4v/io/px/px_lib4v.c | 28 +- usr/src/uts/sun4v/px/Makefile | 4 +- 50 files changed, 9901 insertions(+), 6268 deletions(-) create mode 100644 usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c create mode 100644 usr/src/uts/common/sys/hotplug/pci/pcishpc.h create mode 100644 usr/src/uts/sparc/fcode/Makefile create mode 100644 usr/src/uts/sparc/fcodem/Makefile create mode 100644 usr/src/uts/sparc/fcpci/Makefile create mode 100644 usr/src/uts/sparc/pcicfg.e/Makefile create mode 100644 usr/src/uts/sparc/pciehpc/Makefile create mode 100644 usr/src/uts/sparc/pcishpc/Makefile create mode 100644 usr/src/uts/sun4/io/pcicfg.e.c create mode 100644 usr/src/uts/sun4/sys/fc_plat.h delete mode 100644 usr/src/uts/sun4u/fcode/Makefile delete mode 100644 usr/src/uts/sun4u/fcodem/Makefile delete mode 100644 usr/src/uts/sun4u/fcpci/Makefile delete mode 100644 usr/src/uts/sun4u/io/pcicfg.e.c delete mode 100644 usr/src/uts/sun4u/pcicfg.e/Makefile delete mode 100644 usr/src/uts/sun4u/sys/fc_plat.h (limited to 'usr/src') diff --git a/usr/src/cmd/pcidr/etc/SUNW,EC_dr,ESC_dr_req,sysevent.conf b/usr/src/cmd/pcidr/etc/SUNW,EC_dr,ESC_dr_req,sysevent.conf index cb151ebf2a..15fb798ce5 100644 --- a/usr/src/cmd/pcidr/etc/SUNW,EC_dr,ESC_dr_req,sysevent.conf +++ b/usr/src/cmd/pcidr/etc/SUNW,EC_dr,ESC_dr_req,sysevent.conf @@ -22,8 +22,9 @@ # # ident "%Z%%M% %I% %E% SMI" # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # EC_dr ESC_dr_req SUNW pcie_pci - - - /usr/lib/pci/pcidr class=$class subclass=$subclass publisher=$publisher dr_request_type=$dr_request_type dr_ap_id=$dr_ap_id +EC_dr ESC_dr_req SUNW px_pci - - - /usr/lib/pci/pcidr class=$class subclass=$subclass publisher=$publisher dr_request_type=$dr_request_type dr_ap_id=$dr_ap_id diff --git a/usr/src/lib/efcode/pci/Makefile.com b/usr/src/lib/efcode/pci/Makefile.com index 759b03e7e9..9d9db0a6c0 100644 --- a/usr/src/lib/efcode/pci/Makefile.com +++ b/usr/src/lib/efcode/pci/Makefile.com @@ -20,7 +20,7 @@ # CDDL HEADER END # # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -29,6 +29,7 @@ OBJECTS = pci.o LIBRARY = lfc_upa_pci.a -SYMLINKS = lfc_pci_pci.so lfc_gptwo_pci.so +SYMLINKS = lfc_pci_pci.so lfc_gptwo_pci.so lfc_pciex_pciex.so \ + lfc_pciex_pci.so lfc_pci_pciex.so include ../../Makefile.efcode diff --git a/usr/src/pkgdefs/SUNWckr/prototype_sparc b/usr/src/pkgdefs/SUNWckr/prototype_sparc index 352dd57adc..731701cf21 100644 --- a/usr/src/pkgdefs/SUNWckr/prototype_sparc +++ b/usr/src/pkgdefs/SUNWckr/prototype_sparc @@ -167,9 +167,10 @@ f none kernel/misc/sparcv9/kcf 755 root sys f none kernel/misc/sparcv9/krtld 755 root sys f none kernel/misc/sparcv9/mac 755 root sys l none kernel/misc/sparcv9/md5=../../../kernel/crypto/sparcv9/md5 -f none kernel/misc/sparcv9/pcicfg 755 root sys f none kernel/misc/sparcv9/pcie 755 root sys f none kernel/misc/sparcv9/pcihp 755 root sys +f none kernel/misc/sparcv9/pciehpc 755 root sys +f none kernel/misc/sparcv9/pcishpc 755 root sys f none kernel/misc/sparcv9/pcmcia 755 root sys f none kernel/misc/sparcv9/rpcsec 755 root sys f none kernel/misc/sparcv9/scsi 755 root sys diff --git a/usr/src/pkgdefs/SUNWefc.u/prototype_com b/usr/src/pkgdefs/SUNWefc.u/prototype_com index 6a7e03e59c..b7fef18ce7 100644 --- a/usr/src/pkgdefs/SUNWefc.u/prototype_com +++ b/usr/src/pkgdefs/SUNWefc.u/prototype_com @@ -22,7 +22,7 @@ # #pragma ident "%Z%%M% %I% %E% SMI" # -# Copyright 2000-2001,2003 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # This required package information file contains a list of package contents. @@ -46,52 +46,20 @@ i depend # # SUNWefc.u # +d none kernel 755 root sys +d none kernel/drv 755 root sys +f none kernel/drv/fcode.conf 644 root sys +d none kernel/drv/sparcv9 755 root sys +f none kernel/drv/sparcv9/fcode 755 root sys +d none kernel/misc 755 root sys +d none kernel/misc/sparcv9 755 root sys +f none kernel/misc/sparcv9/fcodem 755 root sys +f none kernel/misc/sparcv9/fcpci 755 root sys +f none kernel/misc/sparcv9/pcicfg.e 755 root sys d none platform 755 root sys d none platform/sun4u 755 root sys d none platform/sun4u/kernel 755 root sys -d none platform/sun4u/kernel/drv 755 root sys -f none platform/sun4u/kernel/drv/fcode.conf 644 root sys -d none platform/sun4u/kernel/drv/sparcv9 755 root sys -f none platform/sun4u/kernel/drv/sparcv9/fcode 755 root sys d none platform/sun4u/kernel/misc 755 root sys d none platform/sun4u/kernel/misc/sparcv9 755 root sys -f none platform/sun4u/kernel/misc/sparcv9/fcodem 755 root sys -f none platform/sun4u/kernel/misc/sparcv9/fcpci 755 root sys -f none platform/sun4u/kernel/misc/sparcv9/pcicfg.e 755 root sys f none platform/sun4u/kernel/misc/sparcv9/gptwocfg 755 root sys f none platform/sun4u/kernel/misc/sparcv9/gptwo_cpu 755 root sys -d none platform/SUNW,Sun-Fire 755 root sys -d none platform/SUNW,Sun-Fire/kernel 755 root sys -d none platform/SUNW,Sun-Fire/kernel/misc 755 root sys -d none platform/SUNW,Sun-Fire/kernel/misc/sparcv9 755 root sys -s none platform/SUNW,Sun-Fire/kernel/misc/sparcv9/pcicfg=../../../../sun4u/kernel/misc/sparcv9/pcicfg.e -d none platform/SUNW,Sun-Fire-880 755 root sys -d none platform/SUNW,Sun-Fire-880/kernel 755 root sys -d none platform/SUNW,Sun-Fire-880/kernel/misc 755 root sys -d none platform/SUNW,Sun-Fire-880/kernel/misc/sparcv9 755 root sys -s none platform/SUNW,Sun-Fire-880/kernel/misc/sparcv9/pcicfg=../../../../sun4u/kernel/misc/sparcv9/pcicfg.e -d none platform/SUNW,Sun-Fire-15000 755 root sys -d none platform/SUNW,Sun-Fire-15000/kernel 755 root sys -d none platform/SUNW,Sun-Fire-15000/kernel/misc 755 root sys -d none platform/SUNW,Sun-Fire-15000/kernel/misc/sparcv9 755 root sys -s none platform/SUNW,Sun-Fire-15000/kernel/misc/sparcv9/pcicfg=../../../../sun4u/kernel/misc/sparcv9/pcicfg.e -d none platform/SUNW,UltraSPARC-IIi-Netract 755 root sys -d none platform/SUNW,UltraSPARC-IIi-Netract/kernel 755 root sys -d none platform/SUNW,UltraSPARC-IIi-Netract/kernel/misc 755 root sys -d none platform/SUNW,UltraSPARC-IIi-Netract/kernel/misc/sparcv9 755 root sys -s none platform/SUNW,UltraSPARC-IIi-Netract/kernel/misc/sparcv9/pcicfg=../../../../sun4u/kernel/misc/sparcv9/pcicfg.e -d none platform/SUNW,Netra-T12 755 root sys -d none platform/SUNW,Netra-T12/kernel 755 root sys -d none platform/SUNW,Netra-T12/kernel/misc 755 root sys -d none platform/SUNW,Netra-T12/kernel/misc/sparcv9 755 root sys -s none platform/SUNW,Netra-T12/kernel/misc/sparcv9/pcicfg=../../../../sun4u/kernel/misc/sparcv9/pcicfg.e -d none platform/SUNW,UltraSPARC-IIe-NetraCT-40 755 root sys -d none platform/SUNW,UltraSPARC-IIe-NetraCT-40/kernel 755 root sys -d none platform/SUNW,UltraSPARC-IIe-NetraCT-40/kernel/misc 755 root sys -d none platform/SUNW,UltraSPARC-IIe-NetraCT-40/kernel/misc/sparcv9 755 root sys -s none platform/SUNW,UltraSPARC-IIe-NetraCT-40/kernel/misc/sparcv9/pcicfg=../../../../sun4u/kernel/misc/sparcv9/pcicfg.e -d none platform/SUNW,UltraSPARC-IIe-NetraCT-60 755 root sys -d none platform/SUNW,UltraSPARC-IIe-NetraCT-60/kernel 755 root sys -d none platform/SUNW,UltraSPARC-IIe-NetraCT-60/kernel/misc 755 root sys -d none platform/SUNW,UltraSPARC-IIe-NetraCT-60/kernel/misc/sparcv9 755 root sys -s none platform/SUNW,UltraSPARC-IIe-NetraCT-60/kernel/misc/sparcv9/pcicfg=../../../../sun4u/kernel/misc/sparcv9/pcicfg.e diff --git a/usr/src/pkgdefs/SUNWefcl/prototype_sparc b/usr/src/pkgdefs/SUNWefcl/prototype_sparc index 0dca26522c..a20f5ac105 100644 --- a/usr/src/pkgdefs/SUNWefcl/prototype_sparc +++ b/usr/src/pkgdefs/SUNWefcl/prototype_sparc @@ -22,7 +22,7 @@ # # ident "%Z%%M% %I% %E% SMI" # -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # This required package information file contains a list of package contents. @@ -64,3 +64,6 @@ f none usr/lib/efcode/sparcv9/lfc_upa_pci.so 755 root bin f none usr/lib/efcode/sparcv9/lfc_gptwo.so 755 root bin s none usr/lib/efcode/sparcv9/lfc_gptwo_gptwo.so=lfc_gptwo.so s none usr/lib/efcode/sparcv9/lfc_gptwo_pci.so=lfc_upa_pci.so +s none usr/lib/efcode/sparcv9/lfc_pciex_pciex.so=lfc_upa_pci.so +s none usr/lib/efcode/sparcv9/lfc_pciex_pci.so=lfc_upa_pci.so +s none usr/lib/efcode/sparcv9/lfc_pci_pciex.so=lfc_upa_pci.so diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index f347dc71a7..f2d155fd25 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -684,6 +684,8 @@ PCIHPNEXUS_OBJS += pcihp.o PCIEHPCNEXUS_OBJS += pciehpc.o +PCISHPC_OBJS += pcishpc.o + PCICFG_OBJS += pcicfg.o OPENEEPR_OBJS += openprom.o diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules index 2e6586e4e2..3a214afab7 100644 --- a/usr/src/uts/common/Makefile.rules +++ b/usr/src/uts/common/Makefile.rules @@ -476,6 +476,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/hotplug/pciehpc/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/hotplug/pcishpc/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/hotplug/pcihp/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -1196,6 +1200,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/pcicfg/%.c $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/pciehpc/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/pcishpc/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/pcihp/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) diff --git a/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c b/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c index 329f053017..41435779fa 100644 --- a/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c +++ b/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c @@ -47,6 +47,9 @@ #include #include #include +#if defined(__sparc) +#include +#endif #include /* @@ -70,6 +73,12 @@ static char *pciehpc_led_state_text(hpc_led_state_t state); static void pciehpc_attn_btn_handler(pciehpc_t *ctrl_p); static void pciehpc_dev_info(pciehpc_t *ctrl_p); +static int pciehpc_pcie_dev(dev_info_t *dip, ddi_acc_handle_t handle); +#if defined(__sparc) +static void pciehpc_disable_errors(pciehpc_t *ctrl_p); +static void pciehpc_enable_errors(pciehpc_t *ctrl_p); +#endif + #ifdef DEBUG int pciehpc_debug = 0; static void pciehpc_dump_hpregs(pciehpc_t *ctrl_p); @@ -186,6 +195,8 @@ pciehpc_init(dev_info_t *dip, pciehpc_regops_t *regops) goto cleanup; } + PCIEHPC_DISABLE_ERRORS(ctrl_p); + /* * Set the platform specific hot plug mode. */ @@ -245,6 +256,7 @@ cleanup2: (void) (ctrl_p->ops.uninit_hpc_hw)(ctrl_p); cleanup1: + PCIEHPC_ENABLE_ERRORS(ctrl_p); pciehpc_regs_teardown(&ctrl_p->cfghdl); cleanup: @@ -285,6 +297,8 @@ pciehpc_uninit(dev_info_t *dip) /* uninitialize hpc, remove interrupt handler, etc. */ (void) (ctrl_p->ops.uninit_hpc_hw)(ctrl_p); + PCIEHPC_ENABLE_ERRORS(ctrl_p); + /* free up the HPC register mapping */ pciehpc_regs_teardown(&ctrl_p->cfghdl); @@ -1257,6 +1271,7 @@ pciehpc_slot_control(caddr_t ops_arg, hpc_slot_t slot_hdl, /* if power to the slot is still on then set Power led to ON */ if (ctrl_p->slot.slot_state == HPC_SLOT_CONNECTED) pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_ON); + PCIEHPC_ENABLE_ERRORS(ctrl_p); break; case HPC_CTRL_ENABLE_AUTOCFG: case HPC_CTRL_DISABLE_AUTOCFG: @@ -1270,11 +1285,16 @@ pciehpc_slot_control(caddr_t ops_arg, hpc_slot_t slot_hdl, case HPC_CTRL_DEV_CONFIG_START: case HPC_CTRL_DEV_UNCONFIG_START: + PCIEHPC_DISABLE_ERRORS(ctrl_p); /* no action is needed here */ break; case HPC_CTRL_DEV_CONFIGURED: case HPC_CTRL_DEV_UNCONFIGURED: /* no action is needed here */ + if (request == HPC_CTRL_DEV_CONFIGURED) { + /*EMPTY*/ + PCIEHPC_ENABLE_ERRORS(ctrl_p); + } break; default: PCIEHPC_DEBUG((CE_WARN, @@ -1683,6 +1703,15 @@ pciehpc_dev_info(pciehpc_t *ctrl_p) int reglen; dev_info_t *dip = ctrl_p->dip; + /* + * Check if it is a PCIe fabric hotplug nexus. This is specially + * not so for Rootcomplex nodes supporting PCIe hotplug. + * We save this information so as to implement hardening for + * fabric nodes only via pcie services. + */ + if (pciehpc_pcie_dev(dip, ctrl_p->cfghdl) == DDI_SUCCESS) + ctrl_p->soft_state |= PCIEHPC_SOFT_STATE_PCIE_DEV; + /* Get the device number. */ if (ddi_getlongprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS, "reg", (caddr_t)®spec, ®len) != DDI_SUCCESS) { @@ -1757,3 +1786,52 @@ pciehpc_set_slot_name(pciehpc_t *ctrl_p) p->slotNum); } } + +/*ARGSUSED*/ +static int +pciehpc_pcie_dev(dev_info_t *dip, ddi_acc_handle_t handle) +{ + /* get parent device's device_type property */ + char *device_type; + int rc; + dev_info_t *pdip = ddi_get_parent(dip); + + if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, + DDI_PROP_DONTPASS, "device_type", &device_type) + != DDI_PROP_SUCCESS) { + PCIEHPC_DEBUG2((CE_NOTE, "device_type property missing for " + "%s#%d", ddi_get_name(pdip), ddi_get_instance(pdip))); + return (DDI_FAILURE); + } + + PCIEHPC_DEBUG((CE_NOTE, "device_type=<%s>\n", device_type)); + rc = DDI_FAILURE; + if (strcmp(device_type, "pciex") == 0) + rc = DDI_SUCCESS; + ddi_prop_free(device_type); + return (rc); +} + +#if defined(__sparc) +static void +pciehpc_disable_errors(pciehpc_t *ctrl_p) +{ + if (ctrl_p->soft_state & PCIEHPC_SOFT_STATE_PCIE_DEV) { + pcie_disable_errors(ctrl_p->dip, ctrl_p->cfghdl); + PCIEHPC_DEBUG3((CE_NOTE, "%s%d: pciehpc_disable_errors\n", + ddi_driver_name(ctrl_p->dip), + ddi_get_instance(ctrl_p->dip))); + } +} + +static void +pciehpc_enable_errors(pciehpc_t *ctrl_p) +{ + if (ctrl_p->soft_state & PCIEHPC_SOFT_STATE_PCIE_DEV) { + pcie_enable_errors(ctrl_p->dip, ctrl_p->cfghdl); + PCIEHPC_DEBUG3((CE_NOTE, "%s%d: pciehpc_enable_errors\n", + ddi_driver_name(ctrl_p->dip), + ddi_get_instance(ctrl_p->dip))); + } +} +#endif diff --git a/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c b/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c new file mode 100644 index 0000000000..e5a19c8cf4 --- /dev/null +++ b/usr/src/uts/common/io/hotplug/pcishpc/pcishpc.c @@ -0,0 +1,2379 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * PCISHPC - The Standard PCI HotPlug Controller driver module. This driver + * can be used with PCI HotPlug controllers that are compatible + * with the PCI SHPC specification 1.x. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 +#define REG_BIT1 0x00000002 +#define REG_BIT2 0x00000004 +#define REG_BIT3 0x00000008 +#define REG_BIT4 0x00000010 +#define REG_BIT5 0x00000020 +#define REG_BIT6 0x00000040 +#define REG_BIT7 0x00000080 +#define REG_BIT8 0x00000100 +#define REG_BIT9 0x00000200 +#define REG_BIT10 0x00000400 +#define REG_BIT11 0x00000800 +#define REG_BIT12 0x00001000 +#define REG_BIT13 0x00002000 +#define REG_BIT14 0x00004000 +#define REG_BIT15 0x00008000 +#define REG_BIT16 0x00010000 +#define REG_BIT17 0x00020000 +#define REG_BIT18 0x00040000 +#define REG_BIT19 0x00080000 +#define REG_BIT20 0x00100000 +#define REG_BIT21 0x00200000 +#define REG_BIT22 0x00400000 +#define REG_BIT23 0x00800000 +#define REG_BIT24 0x01000000 +#define REG_BIT25 0x02000000 +#define REG_BIT26 0x04000000 +#define REG_BIT27 0x08000000 +#define REG_BIT28 0x10000000 +#define REG_BIT29 0x20000000 +#define REG_BIT30 0x40000000 +#define REG_BIT31 0x80000000 + + +/* Register bits used with the SHPC SHPC_CTRL_SERR_INT_REG register */ +#define SHPC_SERR_INT_GLOBAL_IRQ_MASK REG_BIT0 +#define SHPC_SERR_INT_GLOBAL_SERR_MASK REG_BIT1 +#define SHPC_SERR_INT_CMD_COMPLETE_MASK REG_BIT2 +#define SHPC_SERR_INT_ARBITER_SERR_MASK REG_BIT3 +#define SHPC_SERR_INT_CMD_COMPLETE_IRQ REG_BIT16 +#define SHPC_SERR_INT_ARBITER_IRQ REG_BIT17 +#define SHPC_SERR_INT_MASK_ALL (REG_BIT0|REG_BIT1|REG_BIT2|REG_BIT3) + +/* Register bits used with the SHPC SHPC_LOGICAL_SLOT_REGS register */ +#define SHPC_SLOT_MRL_STATE_MASK REG_BIT8 +#define SHPC_SLOT_CARD_EMPTY_MASK (REG_BIT10 | REG_BIT11) +#define SHPC_SLOT_PRESENCE_DETECTED REG_BIT16 +#define SHPC_SLOT_ISO_PWR_DETECTED REG_BIT17 +#define SHPC_SLOT_ATTN_DETECTED REG_BIT18 +#define SHPC_SLOT_MRL_DETECTED REG_BIT19 +#define SHPC_SLOT_POWER_DETECTED REG_BIT20 +#define SHPC_SLOT_PRESENCE_MASK REG_BIT24 +#define SHPC_SLOT_ISO_PWR_MASK REG_BIT25 +#define SHPC_SLOT_ATTN_MASK REG_BIT26 +#define SHPC_SLOT_MRL_MASK REG_BIT27 +#define SHPC_SLOT_POWER_MASK REG_BIT28 +#define SHPC_SLOT_MRL_SERR_MASK REG_BIT29 +#define SHPC_SLOT_POWER_SERR_MASK REG_BIT30 +#define SHPC_SLOT_MASK_ALL (REG_BIT24|REG_BIT25|REG_BIT26|\ + REG_BIT27|REG_BIT28|REG_BIT29|REG_BIT30) + +/* Register bits used with the SHPC SHPC_IRQ_LOCATOR_REG register. */ +#define SHPC_IRQ_CMD_COMPLETE REG_BIT0 +#define SHPC_IRQ_SLOT_N_PENDING REG_BIT1 + +/* Register bits used with the SHPC SHPC_SERR_LOCATOR_REG register. */ +#define SHPC_IRQ_SERR_ARBITER_PENDING REG_BIT0 +#define SHPC_IRQ_SERR_SLOT_N_PENDING REG_BIT1 + +/* Register bits used with the SHPC SHPC_SLOT_CONFIGURATION_REG register */ +#define SHPC_SLOT_CONFIG_MRL_SENSOR REG_BIT30 +#define SHPC_SLOT_CONFIG_ATTN_BUTTON REG_BIT31 +#define SHPC_SLOT_CONFIG_PHY_SLOT_NUM_SHIFT 16 +#define SHPC_SLOT_CONFIG_PHY_SLOT_NUM_MASK 0x3FF +#define SHPC_SLOT_CONFIG_PHY_SLOT_NUM(reg) (((reg) >> 16) & 0x3FF) + +/* Max PCISHPC controller slots */ +#define MAX_SHPC_SLOTS 31 + +/* PCISHPC controller command complete delay in microseconds. */ +#define SHPC_COMMAND_WAIT_TIME 10000 + +/* reset delay is 2^25 clock cycles as per PCI spec @33MHz. */ +static int pcishpc_reset_delay = 0x1000000; + +/* PCISHPC controller softstate structure */ +typedef struct pcishpc_ctrl { + dev_info_t *shpc_dip; /* DIP for SHPC Nexus */ + ddi_acc_handle_t shpc_config_hdl; /* SHPC DDI cfg handle */ + kmutex_t shpc_intr_mutex; /* Interrupt mutex lock */ + boolean_t interrupt_installed; /* Interrupt installed */ + boolean_t command_complete; /* Got a cmd complete IRQ */ + kcondvar_t cmd_comp_cv; + boolean_t arbiter_timeout; /* Got a Arb timeout IRQ */ + kmutex_t shpc_mutex; /* Mutex for this SHPC */ + char nexus_path[MAXNAMELEN]; /* Pathname of Nexus */ + uint32_t shpc_bus; /* SHPC bus */ + uint32_t shpc_dev; /* SHPC device */ + uint32_t shpc_func; /* SHPC function */ + uint8_t shpc_dword_select; /* SHPC register offset */ + uint8_t shpc_dword_data_reg; /* SHPC data register */ + uint32_t numSlots; /* # of HotPlug Slots */ + uint32_t deviceStart; /* 1st PCI Device # */ + uint32_t physStart; /* 1st Phys Device # */ + uint32_t deviceIncreases; /* Device # Increases */ + struct pcishpc *slots[MAX_SHPC_SLOTS]; /* Slot pointers */ + boolean_t has_attn; /* Do we have attn btn? */ + boolean_t has_mrl; /* Do we have MRL? */ + struct pcishpc_ctrl *nextp; /* Linked list pointer */ +} pcishpc_ctrl_t; + +/* PCISHPC slot softstate structure */ +typedef struct pcishpc { + pcishpc_ctrl_t *ctrl; /* SHPC ctrl for this slot */ + hpc_slot_info_t slot_info; /* HPS framework slot info */ + hpc_slot_t slot_handle; /* HPS framework handle */ + hpc_slot_ops_t *slot_ops; /* HPS framework callbacks */ + uint32_t fault_led_state; /* Fault LED state */ + uint32_t power_led_state; /* Power LED state */ + uint32_t attn_led_state; /* Attn LED state */ + uint32_t active_led_state; /* Active LED state */ + hpc_slot_state_t slot_state; /* Slot State */ + uint32_t deviceNum; /* PCI device num for slot */ + uint32_t slotNum; /* SHPC slot number */ + uint32_t phy_slot_num; /* physical slot number */ + uint32_t slot_events; /* Slot event(s) IRQ */ + kcondvar_t attn_btn_cv; /* ATTN button pressed intr */ + boolean_t attn_btn_pending; + kthread_t *attn_btn_threadp; /* ATTN button event thread */ + boolean_t attn_btn_thread_exit; + struct pcishpc *nextp; /* Linked list pointer */ +} pcishpc_t; +/* mutex to protect the shpc_head and shpc_ctrl_head linked lists */ +static kmutex_t pcishpc_list_mutex; + +/* Pointer to a linked list of shpc slot softstate structures */ +static pcishpc_t *pcishpc_head = NULL; + +/* Pointer to a linked list of shpc controller softstate structures */ +static pcishpc_ctrl_t *pcishpc_ctrl_head = NULL; + +/* mutex to protect access to the controller */ +static kmutex_t pcishpc_control_mutex; + +/* SHPC static function prototypes */ +static pcishpc_ctrl_t *pcishpc_create_controller(dev_info_t *dip); +static int pcishpc_destroy_controller(dev_info_t *dip); +static pcishpc_ctrl_t *pcishpc_get_controller(dev_info_t *dip); +static pcishpc_t *pcishpc_create_slot(pcishpc_ctrl_t *ctrl_p); +static int pcishpc_destroy_slots(pcishpc_ctrl_t *ctrl_p); +static pcishpc_t *pcishpc_hpc_get_slot_state(hpc_slot_t slot); +static int pcishpc_setup_controller(pcishpc_ctrl_t *ctrl_p); +static int pcishpc_register_slot(pcishpc_ctrl_t *ctrl_p, int slot); +static int pcishpc_connect(caddr_t ops_arg, + hpc_slot_t slot_hdl, void *data, + uint_t flags); +static int pcishpc_disconnect(caddr_t ops_arg, + hpc_slot_t slot_hdl, void *data, + uint_t flags); +static int pcishpc_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl, + int request, caddr_t arg); +static int pcishpc_setled(pcishpc_t *pcishpc_p, hpc_led_t led, + hpc_led_state_t state); +static int pcishpc_set_power_state(pcishpc_t *pcishpc_p, + hpc_slot_state_t state); +static int pcishpc_probe_controller(pcishpc_ctrl_t *pcishpc_p); +static int pcishpc_get_pci_info(pcishpc_ctrl_t *pcishpc_p); +static void pcishpc_get_slot_state(pcishpc_t *pcishpc_p); +static int pcishpc_process_intr(pcishpc_ctrl_t *ctrl_p); +static int pcishpc_enable_irqs(pcishpc_ctrl_t *ctrl_p); +static int pcishpc_disable_irqs(pcishpc_ctrl_t *ctrl_p); +static void pcishpc_set_soft_int(pcishpc_ctrl_t *ctrl_p); +static int pcishpc_wait_busy(pcishpc_t *pcishpc_p); +static int pcishpc_issue_command(pcishpc_t *pcishpc_p, uint8_t cmd_code); +static int pcishpc_led_shpc_to_hpc(int state); +static int pcishpc_led_hpc_to_shpc(int state); +static int pcishpc_slot_shpc_to_hpc(int state); +static int pcishpc_slot_hpc_to_shpc(int state); +static char *pcishpc_textledstate(hpc_led_state_t state); +static char *pcishpc_textslotstate(hpc_slot_state_t state); +static char *pcishpc_textrequest(int request); +static int pcishpc_set_slot_state(pcishpc_t *pcishpc_p); +static void pcishpc_dump_regs(pcishpc_ctrl_t *ctrl_p); +static void pcishpc_write_reg(pcishpc_ctrl_t *ctrl_p, int reg, + uint32_t data); +static uint32_t pcishpc_read_reg(pcishpc_ctrl_t *ctrl_p, int reg); +static void pcishpc_debug(char *fmt, ...); + +static void pcishpc_attn_btn_handler(pcishpc_t *pcishpc_p); +static void pcishpc_set_slot_name(pcishpc_ctrl_t *ctrl_p, int slot); + +static int pcishpc_debug_enabled = 0; + +/* Module operations information for the kernel */ +extern struct mod_ops mod_miscops; +static struct modlmisc modlmisc = { + &mod_miscops, + "PCI SHPC hotplug module v1.12", +}; + +/* Module linkage information for the kernel */ +static struct modlinkage modlinkage = { + MODREV_1, + &modlmisc, + NULL +}; + +int +_init(void) +{ + int rc; + + if ((rc = mod_install(&modlinkage)) != 0) { + pcishpc_debug("pcishpc: install error=%d", rc); + return (rc); + } + + /* Init the shpc driver list mutex. */ + mutex_init(&pcishpc_list_mutex, NULL, MUTEX_DRIVER, NULL); + /* Init the shpc control mutex. */ + mutex_init(&pcishpc_control_mutex, NULL, MUTEX_DRIVER, NULL); + + pcishpc_debug("pcishpc: installed"); + return (rc); +} + +int +_fini(void) +{ + pcishpc_debug("pcishpc: _fini called()"); + /* XXX - to be fixed later */ + return (EBUSY); +} + +int +_info(struct modinfo *modinfop) +{ + pcishpc_debug("pcishpc: _info called()"); + return (mod_info(&modlinkage, modinfop)); +} + + +/* + * pcishpc_create_controller() + * + * This function allocates and creates an SHPC controller state structure + * and adds it to the linked list of controllers. + */ +static pcishpc_ctrl_t * +pcishpc_create_controller(dev_info_t *dip) +{ + pcishpc_ctrl_t *ctrl_p; + + pcishpc_debug("pcishpc: create controller for %s#%d", + ddi_driver_name(dip), ddi_get_instance(dip)); + + ctrl_p = kmem_zalloc(sizeof (pcishpc_ctrl_t), KM_SLEEP); + + ctrl_p->interrupt_installed = B_FALSE; + ctrl_p->shpc_dip = dip; + + (void) ddi_pathname(dip, ctrl_p->nexus_path); + + /* Get the PCI BUS,DEVICE,FUNCTION for this SHPC controller. */ + if (pcishpc_get_pci_info(ctrl_p) != DDI_SUCCESS) { + + pcishpc_debug("pcishpc_create_controller() " + "Error: pcishpc_get_pci_info() failed"); + kmem_free(ctrl_p, sizeof (pcishpc_ctrl_t)); + return (NULL); + } + + if (pci_config_setup(dip, &ctrl_p->shpc_config_hdl) != DDI_SUCCESS) { + pcishpc_debug("pcishpc_create_controller() " + "Error: Unable to map SHPC PCI Config registers"); + kmem_free(ctrl_p, sizeof (pcishpc_ctrl_t)); + return (NULL); + } + + /* Make sure the SHPC is listed in the PCI capibilities list. */ + if (pcishpc_probe_controller(ctrl_p) != DDI_SUCCESS) { + pcishpc_debug("pcishpc_create_controller() " + "Error: Unable to find SHPC controller"); + pci_config_teardown(&ctrl_p->shpc_config_hdl); + kmem_free(ctrl_p, sizeof (pcishpc_ctrl_t)); + return (NULL); + } + + /* Init the interrupt mutex */ + mutex_init(&ctrl_p->shpc_intr_mutex, NULL, MUTEX_DRIVER, + (void *)PCISHPC_INTR_PRI); + + /* Interrupts are now enabled. */ + ctrl_p->interrupt_installed = B_TRUE; + + /* Init the shpc controller's mutex. */ + mutex_init(&ctrl_p->shpc_mutex, NULL, MUTEX_DRIVER, NULL); + + mutex_enter(&pcishpc_list_mutex); + + /* Insert new softstate into linked list of current soft states. */ + ctrl_p->nextp = pcishpc_ctrl_head; + pcishpc_ctrl_head = ctrl_p; + + mutex_exit(&pcishpc_list_mutex); + + pcishpc_debug("pcishpc_create_controller() success"); + + return (ctrl_p); +} + + +/* + * pcishpc_probe_controller() + * + * This function probes to make sure there is indeed an SHPC controller. + */ +static int +pcishpc_probe_controller(pcishpc_ctrl_t *ctrl_p) +{ + uint8_t cap_ptr; + uint8_t cap_id; + uint16_t status; + + status = pci_config_get16(ctrl_p->shpc_config_hdl, PCI_CONF_STAT); + if (!(status & PCI_STAT_CAP)) { + return (DDI_FAILURE); + } + + /* Get a pointer to the PCI capabilities list. */ + cap_ptr = pci_config_get8(ctrl_p->shpc_config_hdl, PCI_BCNF_CAP_PTR); + + cap_ptr &= 0xFC; + + /* Walk PCI capabilities list searching for the SHPC capability. */ + while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) { + cap_id = pci_config_get8(ctrl_p->shpc_config_hdl, cap_ptr); + + pcishpc_debug("pcishpc_probe_controller() capability @ " + "pointer=%02x (id=%02x)", cap_ptr, cap_id); + + if (cap_id == PCI_CAP_ID_PCI_HOTPLUG) { + /* Save the SHPC register offset. */ + ctrl_p->shpc_dword_select = cap_ptr+2; + /* Save the SHPC data register. */ + ctrl_p->shpc_dword_data_reg = cap_ptr+4; + break; + } + + /* Get the pointer to the next capability. */ + cap_ptr = pci_config_get8(ctrl_p->shpc_config_hdl, + cap_ptr+1); + + cap_ptr &= 0xFC; + } + + if (cap_ptr == PCI_CAP_NEXT_PTR_NULL) { + return (DDI_FAILURE); + } + + pcishpc_debug("pcishpc_probe_controller() Found SHPC capibility"); + + return (DDI_SUCCESS); +} + + +/* + * pcishpc_destroy_controller() + * + * This function deallocates all of the SHPC controller resources. + */ +static int +pcishpc_destroy_controller(dev_info_t *dip) +{ + pcishpc_ctrl_t *ctrl_p; + pcishpc_ctrl_t **ctrl_pp; + + pcishpc_debug("pcishpc_destroy_controller() called(dip=%p)", dip); + + mutex_enter(&pcishpc_list_mutex); + + ctrl_pp = &pcishpc_ctrl_head; + + /* Walk the linked list of softstates. */ + while ((ctrl_p = *ctrl_pp) != NULL) { + if (ctrl_p->shpc_dip == dip) { + /* + * Deallocate the slot state structures for + * this controller. + */ + (void) pcishpc_destroy_slots(ctrl_p); + + *ctrl_pp = ctrl_p->nextp; + + pci_config_teardown(&ctrl_p->shpc_config_hdl); + + cv_destroy(&ctrl_p->cmd_comp_cv); + + mutex_destroy(&ctrl_p->shpc_mutex); + mutex_destroy(&ctrl_p->shpc_intr_mutex); + kmem_free(ctrl_p, sizeof (pcishpc_ctrl_t)); + mutex_exit(&pcishpc_list_mutex); + + pcishpc_debug("pcishpc_destroy_controller() success"); + return (DDI_SUCCESS); + } + ctrl_pp = &(ctrl_p->nextp); + } + + mutex_exit(&pcishpc_list_mutex); + + pcishpc_debug("pcishpc_destroy_controller() not found"); + + return (DDI_FAILURE); +} + + +/* + * pcishpc_intr() + * + * This is the SHPC controller interrupt handler. + */ +int +pcishpc_intr(dev_info_t *dip) +{ + pcishpc_ctrl_t *ctrl_p = pcishpc_get_controller(dip); + int slot; + uint32_t irq_locator, irq_serr_locator, reg; + boolean_t slot_event = B_FALSE; + + pcishpc_debug("pcishpc_intr() called"); + + if (ctrl_p->interrupt_installed == B_TRUE) { + mutex_enter(&ctrl_p->shpc_intr_mutex); + + pcishpc_debug("pcishpc_intr() interrupt received"); + + reg = pcishpc_read_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG); + + if (reg & SHPC_SERR_INT_CMD_COMPLETE_IRQ) { + pcishpc_debug("pcishpc_intr() " + "SHPC_SERR_INT_CMD_COMPLETE_IRQ detected"); + ctrl_p->command_complete = B_TRUE; + cv_signal(&ctrl_p->cmd_comp_cv); + } + + if (reg & SHPC_SERR_INT_ARBITER_IRQ) { + pcishpc_debug("pcishpc_intr() SHPC_SERR_INT_ARBITER_IRQ" + " detected"); + ctrl_p->arbiter_timeout = B_TRUE; + } + + /* Write back the SERR INT register to acknowledge the IRQs. */ + pcishpc_write_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG, reg); + + irq_locator = pcishpc_read_reg(ctrl_p, SHPC_IRQ_LOCATOR_REG); + + irq_serr_locator = pcishpc_read_reg(ctrl_p, + SHPC_SERR_LOCATOR_REG); + + /* Check for slot events that might have occured. */ + for (slot = 0; slot < ctrl_p->numSlots; slot++) { + if ((irq_locator & (SHPC_IRQ_SLOT_N_PENDING<slots[slot]->slot_events |= reg; + + /* Acknoledge any slot interrupts */ + pcishpc_write_reg(ctrl_p, + SHPC_LOGICAL_SLOT_REGS+slot, reg); + } + } + + if (slot_event == B_TRUE) { + pcishpc_debug("pcishpc_intr() slot(s) have event(s)"); + (void) pcishpc_process_intr(ctrl_p); + } else { + pcishpc_debug("pcishpc_intr() No slot event(s)"); + } + + mutex_exit(&ctrl_p->shpc_intr_mutex); + + pcishpc_debug("pcishpc_intr() claimed"); + + return (DDI_INTR_CLAIMED); + } + + pcishpc_debug("pcishpc_intr() unclaimed"); + + return (DDI_INTR_UNCLAIMED); +} + +/* + * pcishpc_process_intr() + * + * This is the SHPC soft interrupt handler. + */ +static int +pcishpc_process_intr(pcishpc_ctrl_t *ctrl_p) +{ + int slot; + + mutex_enter(&ctrl_p->shpc_mutex); + + pcishpc_debug("pcishpc_process_intr() called"); + + /* XXX - add event handling code here */ + for (slot = 0; slot < ctrl_p->numSlots; slot++) { + if (ctrl_p->slots[slot]->slot_events & + SHPC_SLOT_PRESENCE_DETECTED) + pcishpc_debug("slot %d: SHPC_SLOT_PRESENCE_DETECTED", + slot+1); + + if (ctrl_p->slots[slot]->slot_events & + SHPC_SLOT_ISO_PWR_DETECTED) + pcishpc_debug("slot %d: SHPC_SLOT_ISO_PWR_DETECTED", + slot+1); + + if (ctrl_p->slots[slot]->slot_events & + SHPC_SLOT_ATTN_DETECTED) { + pcishpc_debug("slot %d: SHPC_SLOT_ATTN_DETECTED", + slot+1); + /* + * if ATTN button event is still pending + * then cancel it + */ + if (ctrl_p->slots[slot]->attn_btn_pending == B_TRUE) + ctrl_p->slots[slot]->attn_btn_pending = B_FALSE; + + /* wake up the ATTN event handler */ + cv_signal(&ctrl_p->slots[slot]->attn_btn_cv); + } + + if (ctrl_p->slots[slot]->slot_events & SHPC_SLOT_MRL_DETECTED) + pcishpc_debug("slot %d: SHPC_SLOT_MRL_DETECTED", + slot+1); + + if (ctrl_p->slots[slot]->slot_events & SHPC_SLOT_POWER_DETECTED) + pcishpc_debug("slot %d: SHPC_SLOT_POWER_DETECTED", + slot+1); + + /* Clear the events now that we've processed all of them. */ + ctrl_p->slots[slot]->slot_events = 0; + } + + mutex_exit(&ctrl_p->shpc_mutex); + + return (DDI_INTR_CLAIMED); +} + + +/* + * pcishpc_get_controller() + * + * This function retrieves the hot plug SHPC controller soft state. + */ +static pcishpc_ctrl_t * +pcishpc_get_controller(dev_info_t *dip) +{ + pcishpc_ctrl_t *ctrl_p; + + pcishpc_debug("pcishpc_get_controller() called (dip=%p)", dip); + + mutex_enter(&pcishpc_list_mutex); + + ctrl_p = pcishpc_ctrl_head; + + while (ctrl_p) { + if (ctrl_p->shpc_dip == dip) + break; + ctrl_p = ctrl_p->nextp; + } + + mutex_exit(&pcishpc_list_mutex); + + pcishpc_debug("pcishpc_get_controller() (ctrl_p=%llx)", ctrl_p); + + return (ctrl_p); +} + + +/* + * pcishpc_hpc_get_slot_state() + * + * This function retrieves the hot plug SHPC soft state from the + * the HPS framework slot handle. + */ +static pcishpc_t * +pcishpc_hpc_get_slot_state(hpc_slot_t slot) +{ + pcishpc_t *pcishpc_p; + + pcishpc_debug("pcishpc_hpc_get_slot_state() called (hpc_slot=%x)", + slot); + + mutex_enter(&pcishpc_list_mutex); + + pcishpc_p = pcishpc_head; + + while (pcishpc_p) { + if (pcishpc_p->slot_handle == slot) { + pcishpc_debug("pcishpc_hpc_get_slot_state() found " + "(pcishpc=%x)", pcishpc_p); + mutex_exit(&pcishpc_list_mutex); + return (pcishpc_p); + } + pcishpc_p = pcishpc_p->nextp; + } + + mutex_exit(&pcishpc_list_mutex); + + pcishpc_debug("pcishpc_hpc_get_slot_state() failed (slot=%x)", slot); + + return (NULL); +} + + +/* + * pcishpc_get_pci_info() + * + * Read the PCI Bus, PCI Device, and PCI function for the SHPC controller. + */ +static int +pcishpc_get_pci_info(pcishpc_ctrl_t *pcishpc_p) +{ + pci_regspec_t *regspec; + int reglen; + + pcishpc_debug("pcishpc_get_pci_info() called"); + + if (ddi_getlongprop(DDI_DEV_T_NONE, pcishpc_p->shpc_dip, + DDI_PROP_DONTPASS, "reg", (caddr_t)®spec, ®len) + != DDI_SUCCESS) { + pcishpc_debug("pcishpc_get_pci_info() failed to get regspec."); + return (DDI_FAILURE); + } + + pcishpc_p->shpc_bus = PCI_REG_BUS_G(regspec[0].pci_phys_hi); + pcishpc_p->shpc_dev = PCI_REG_DEV_G(regspec[0].pci_phys_hi); + pcishpc_p->shpc_func = PCI_REG_FUNC_G(regspec[0].pci_phys_hi); + + kmem_free(regspec, reglen); + + pcishpc_debug("pcishpc_get_pci_info() %s%d: bus=%d, dev=%d, func=%d", + ddi_driver_name(pcishpc_p->shpc_dip), + ddi_get_instance(pcishpc_p->shpc_dip), + pcishpc_p->shpc_bus, pcishpc_p->shpc_dev, + pcishpc_p->shpc_func); + + return (DDI_SUCCESS); +} + + +/* + * pcishpc_init() + * + * Install and configure an SHPC controller and register the HotPlug slots + * with the Solaris HotPlug framework. This function is usually called by + * a PCI bridge Nexus driver that has a built in SHPC controller. + */ +int +pcishpc_init(dev_info_t *dip) +{ + pcishpc_ctrl_t *ctrl_p; + int i; + + pcishpc_debug("pcishpc_init() called from %s#%d", + ddi_driver_name(dip), ddi_get_instance(dip)); + + mutex_enter(&pcishpc_control_mutex); + + if (pcishpc_get_controller(dip) != NULL) { + pcishpc_debug("pcishpc_init() shpc instance already " + "initialized!"); + mutex_exit(&pcishpc_control_mutex); + return (DDI_SUCCESS); + } + + /* Initialize soft state structure for the SHPC instance. */ + ctrl_p = pcishpc_create_controller(dip); + + if (ctrl_p == NULL) { + pcishpc_debug("pcishpc_init() failed to create shpc softstate"); + mutex_exit(&pcishpc_control_mutex); + return (DDI_FAILURE); + } + + if (pcishpc_setup_controller(ctrl_p) != DDI_SUCCESS) { + pcishpc_debug("pcishpc_init() failed to setup controller"); + (void) pcishpc_destroy_controller(dip); + mutex_exit(&pcishpc_control_mutex); + return (DDI_FAILURE); + } + +#if 0 + pcishpc_debug("%s%d: P2P bridge register dump:", + ddi_driver_name(dip), ddi_get_instance(dip)); + + for (i = 0; i < 0x100; i += 4) { + pcishpc_debug("SHPC Cfg reg 0x%02x: %08x", i, + pci_config_get32(ctrl_p->shpc_config_hdl, i)); + } +#endif + + /* Setup each HotPlug slot on this SHPC controller. */ + for (i = 0; i < ctrl_p->numSlots; i++) { + if (pcishpc_register_slot(ctrl_p, i) != DDI_SUCCESS) { + pcishpc_debug("pcishpc_init() failed to register " + "slot %d", i); + (void) pcishpc_destroy_controller(dip); + mutex_exit(&pcishpc_control_mutex); + return (DDI_FAILURE); + } + } + + (void) pcishpc_enable_irqs(ctrl_p); + + if (pcishpc_debug_enabled) { + /* Dump out the SHPC registers. */ + pcishpc_dump_regs(ctrl_p); + } + + mutex_exit(&pcishpc_control_mutex); + + pcishpc_debug("pcishpc_init() success(dip=%p)", dip); + return (DDI_SUCCESS); +} + + +/* + * pcishpc_enable_irqs() + * + * Enable/unmask the different IRQ's we support from the SHPC controller. + */ +static int +pcishpc_enable_irqs(pcishpc_ctrl_t *ctrl_p) +{ + uint32_t reg; + int slot; + + reg = pcishpc_read_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG); + + /* Enable all interrupts. */ + reg &= ~SHPC_SERR_INT_MASK_ALL; + + pcishpc_write_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG, reg); + + /* Unmask the interrupts for each slot. */ + for (slot = 0; slot < ctrl_p->numSlots; slot++) { + ctrl_p->slots[slot]->slot_events = 0; + + reg = pcishpc_read_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot); + + /* Enable/Unmask all slot interrupts. */ + reg &= (~SHPC_SLOT_MASK_ALL); + + pcishpc_write_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot, reg); + } + + return (DDI_SUCCESS); +} + + +/* + * pcishpc_disable_irqs() + * + * Disable/Mask the different IRQ's we support from the SHPC controller. + */ +static int +pcishpc_disable_irqs(pcishpc_ctrl_t *ctrl_p) +{ + uint32_t reg; + int slot; + + reg = pcishpc_read_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG); + + /* Mask all interrupts. */ + reg |= SHPC_SERR_INT_MASK_ALL; + + pcishpc_write_reg(ctrl_p, SHPC_CTRL_SERR_INT_REG, reg); + + /* Unmask the interrupts for each slot. */ + for (slot = 0; slot < ctrl_p->numSlots; slot++) { + reg = pcishpc_read_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot); + + /* Disable/Mask all slot interrupts. */ + reg |= SHPC_SLOT_MASK_ALL; + + pcishpc_write_reg(ctrl_p, SHPC_LOGICAL_SLOT_REGS+slot, reg); + } + + return (DDI_SUCCESS); +} + + +/* + * pcishpc_register_slot() + * + * Create and register a slot with the Solaris HotPlug framework. + */ +static int +pcishpc_register_slot(pcishpc_ctrl_t *ctrl_p, int slot) +{ + pcishpc_t *pcishpc_p; + + pcishpc_p = pcishpc_create_slot(ctrl_p); + + ctrl_p->slots[slot] = pcishpc_p; + + pcishpc_p->slot_ops = hpc_alloc_slot_ops(KM_SLEEP); + + pcishpc_p->slot_ops->hpc_version = HPC_SLOT_OPS_VERSION; + + pcishpc_p->slotNum = slot; + + /* Setup the PCI device # for this SHPC slot. */ + if (ctrl_p->deviceIncreases) + pcishpc_p->deviceNum = ctrl_p->deviceStart + pcishpc_p->slotNum; + else + pcishpc_p->deviceNum = ctrl_p->deviceStart - pcishpc_p->slotNum; + + /* Setup the HPS framework slot ops callbacks for the SHPC driver. */ + pcishpc_p->slot_ops->hpc_op_connect = pcishpc_connect; + pcishpc_p->slot_ops->hpc_op_disconnect = pcishpc_disconnect; + pcishpc_p->slot_ops->hpc_op_control = pcishpc_pci_control; + /* PCI HPC drivers do not support the insert/remove callbacks. */ + pcishpc_p->slot_ops->hpc_op_insert = NULL; + pcishpc_p->slot_ops->hpc_op_remove = NULL; + + /* Setup the HPS framework slot information. */ + pcishpc_p->slot_info.version = HPC_SLOT_OPS_VERSION; + pcishpc_p->slot_info.slot_type = HPC_SLOT_TYPE_PCI; + /* Do not auto enable the deivce in this slot. */ + pcishpc_p->slot_info.slot_flags = HPC_SLOT_NO_AUTO_ENABLE | + HPC_SLOT_CREATE_DEVLINK; + + pcishpc_p->slot_info.slot.pci.device_number = pcishpc_p->deviceNum; + pcishpc_p->slot_info.slot.pci.slot_capabilities = HPC_SLOT_64BITS; + + /* setup thread for handling ATTN button events */ + if (ctrl_p->has_attn) { + pcishpc_debug("pcishpc_register_slot: " + "setting up ATTN button event " + "handler thread for slot %d\n", slot); + cv_init(&pcishpc_p->attn_btn_cv, NULL, CV_DRIVER, NULL); + pcishpc_p->attn_btn_pending = B_FALSE; + pcishpc_p->attn_btn_threadp = thread_create(NULL, 0, + pcishpc_attn_btn_handler, + (void *)pcishpc_p, 0, &p0, TS_RUN, minclsyspri); + pcishpc_p->attn_btn_thread_exit = B_FALSE; + } + + /* setup the slot name (used for ap-id) */ + pcishpc_set_slot_name(ctrl_p, slot); + + pcishpc_get_slot_state(pcishpc_p); + + /* Register this SHPC slot with the HPS framework. */ + if (hpc_slot_register(ctrl_p->shpc_dip, ctrl_p->nexus_path, + &pcishpc_p->slot_info, &pcishpc_p->slot_handle, + pcishpc_p->slot_ops, (caddr_t)pcishpc_p, 0) != 0) { + + pcishpc_debug("pcishpc_register_slot() faled to Register " + "slot"); + return (DDI_FAILURE); + } + + pcishpc_debug("pcishpc_register_slot() success for slot %d", slot); + + return (DDI_SUCCESS); +} + + +/* + * pcishpc_create_slot() + * + * Allocate and add a new HotPlug slot state structure to the linked list. + */ +static pcishpc_t * +pcishpc_create_slot(pcishpc_ctrl_t *ctrl_p) +{ + pcishpc_t *pcishpc_p; + + pcishpc_debug("pcishpc_create_slot() called(ctrl_p=%x)", ctrl_p); + + /* Allocate a new slot structure. */ + pcishpc_p = kmem_zalloc(sizeof (pcishpc_t), KM_SLEEP); + + pcishpc_p->ctrl = ctrl_p; + + mutex_enter(&pcishpc_list_mutex); + + /* Insert new slot into linked list of current slots. */ + pcishpc_p->nextp = pcishpc_head; + pcishpc_head = pcishpc_p; + + mutex_exit(&pcishpc_list_mutex); + + pcishpc_debug("pcishpc_create_slot() success"); + return (pcishpc_p); +} + +/* + * pcishpc_setup_controller() + * + * Get the number of HotPlug Slots, and the PCI device information + * for this HotPlug controller. + */ +static int +pcishpc_setup_controller(pcishpc_ctrl_t *ctrl_p) +{ + uint32_t config; + dev_info_t *ppdip; + + config = pcishpc_read_reg(ctrl_p, SHPC_SLOT_CONFIGURATION_REG); + + /* Get the number of HotPlug slots. */ + ctrl_p->numSlots = ((config)&31); + + /* Get the first PCI device Number used. */ + /* + * PCI-X I/O boat workaround. + * The register doesn't set up the correct value. + */ + ppdip = ddi_get_parent(ddi_get_parent(ctrl_p->shpc_dip)); + if ((ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS, + "vendor-id", -1) == 0x108e) && + (ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS, + "device-id", -1) == 0x9010)) + ctrl_p->deviceStart = 4; + else + ctrl_p->deviceStart = ((config>>8)&31); + + /* Get the first Physical device number. */ + ctrl_p->physStart = ((config>>16)&0x7ff); + /* Check if the device numbers increase or decrease. */ + ctrl_p->deviceIncreases = ((config>>29)&0x1); + + ctrl_p->has_attn = + (config & SHPC_SLOT_CONFIG_ATTN_BUTTON) ? B_TRUE : B_FALSE; + ctrl_p->has_mrl = + (config & SHPC_SLOT_CONFIG_MRL_SENSOR) ? B_TRUE : B_FALSE; + + cv_init(&ctrl_p->cmd_comp_cv, NULL, CV_DRIVER, NULL); + ctrl_p->command_complete = B_FALSE; + ctrl_p->arbiter_timeout = B_FALSE; + + if (ctrl_p->numSlots > MAX_SHPC_SLOTS) { + pcishpc_debug("pcishpc_setup_controller() too many SHPC " + "slots error"); + return (DDI_FAILURE); + } + + return (DDI_SUCCESS); +} + + +/* + * pcishpc_uninit() + * Unload the HogPlug controller driver and deallocate all resources. + */ +int +pcishpc_uninit(dev_info_t *dip) +{ + pcishpc_ctrl_t *ctrl_p; + + pcishpc_debug("pcishpc_uninit() called(dip=%p)", dip); + + mutex_enter(&pcishpc_control_mutex); + + ctrl_p = pcishpc_get_controller(dip); + + if (!ctrl_p) { + pcishpc_debug("pcishpc_uninit() Unable to find softstate"); + mutex_exit(&pcishpc_control_mutex); + return (DDI_FAILURE); + } + + (void) pcishpc_destroy_controller(dip); + + mutex_exit(&pcishpc_control_mutex); + + pcishpc_debug("pcishpc_uninit() success(dip=%p)", dip); + + return (DDI_SUCCESS); +} + +/* + * pcishpc_destroy_slots() + * + * Free up all of the slot resources for this controller. + */ +static int +pcishpc_destroy_slots(pcishpc_ctrl_t *ctrl_p) +{ + pcishpc_t *pcishpc_p, *prev_pcishpc_p; + pcishpc_t **pcishpc_pp; + + pcishpc_debug("pcishpc_destroy_slots() called(ctrl_p=%p)", ctrl_p); + + pcishpc_pp = &pcishpc_head; + prev_pcishpc_p = NULL; + + while ((pcishpc_p = *pcishpc_pp) != NULL) { + + pcishpc_pp = &(pcishpc_p->nextp); + + if (pcishpc_p->ctrl == ctrl_p) { + if (pcishpc_p->attn_btn_threadp != NULL) { + mutex_enter(&ctrl_p->shpc_mutex); + pcishpc_p->attn_btn_thread_exit = B_TRUE; + cv_signal(&pcishpc_p->attn_btn_cv); + pcishpc_debug("pcishpc_destroy_slots: " + "waiting for ATTN thread exit\n"); + cv_wait(&pcishpc_p->attn_btn_cv, + &ctrl_p->shpc_mutex); + pcishpc_debug("pcishpc_destroy_slots: " + "ATTN thread exit\n"); + cv_destroy(&pcishpc_p->attn_btn_cv); + pcishpc_p->attn_btn_threadp = NULL; + mutex_exit(&ctrl_p->shpc_mutex); + } + + if (prev_pcishpc_p == NULL) + pcishpc_head = pcishpc_p->nextp; + else + prev_pcishpc_p->nextp = pcishpc_p->nextp; + + pcishpc_debug("pcishpc_destroy_slots() (shpc_p=%p) " + "destroyed", pcishpc_p); + kmem_free(pcishpc_p, sizeof (pcishpc_t)); + } else + prev_pcishpc_p = pcishpc_p; + } + + return (DDI_SUCCESS); +} + + +/* + * pcishpc_connect() + * + * Called by Hot Plug Services to connect a slot on the bus. + */ +/*ARGSUSED*/ +static int +pcishpc_connect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, uint_t flags) +{ + pcishpc_t *pcishpc_p; + uint32_t status; + + pcishpc_debug("pcishpc_connect called()"); + + pcishpc_p = pcishpc_hpc_get_slot_state(slot_hdl); + + if (!pcishpc_p) { + pcishpc_debug("pcishpc_connect() " + "Failed to find soft state for slot_hdl %x", slot_hdl); + return (HPC_ERR_FAILED); + } + + mutex_enter(&pcishpc_p->ctrl->shpc_mutex); + + /* make sure the MRL sensor is closed */ + status = pcishpc_read_reg(pcishpc_p->ctrl, + SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum); + + if (status & SHPC_SLOT_MRL_STATE_MASK) { + pcishpc_debug("pcishpc_connect() failed: MRL open"); + goto cleanup; + } + + if (pcishpc_set_power_state(pcishpc_p, HPC_SLOT_CONNECTED) != + DDI_SUCCESS) { + pcishpc_debug("pcishpc_connect() failed: set power state"); + goto cleanup; + } + + mutex_exit(&pcishpc_p->ctrl->shpc_mutex); + + pcishpc_debug("pcishpc_connect() success!"); + + return (HPC_SUCCESS); + +cleanup: + mutex_exit(&pcishpc_p->ctrl->shpc_mutex); + return (HPC_ERR_FAILED); +} + + +/* + * pcishpc_set_power_state() + * + * Changed a slot's power state. + */ +static int +pcishpc_set_power_state(pcishpc_t *pcishpc_p, hpc_slot_state_t state) +{ + pcishpc_get_slot_state(pcishpc_p); + + /* Check to see if the slot is already in this state. */ + if (pcishpc_p->slot_state == state) { + pcishpc_debug("pcishpc_set_power_state() slot already in " + "this state"); + return (DDI_SUCCESS); + } + + if ((pcishpc_p->slot_state == HPC_SLOT_EMPTY) && + ((state == HPC_SLOT_CONNECTED) || + (state == HPC_SLOT_DISCONNECTED))) { + pcishpc_debug("pcishpc_set_power_state() slot in " + "empty state"); + return (DDI_FAILURE); + } + + /* Set the Power LED to blink. */ + (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_BLINK); + + /* Turn all other LEDS off. */ + (void) pcishpc_setled(pcishpc_p, HPC_FAULT_LED, HPC_LED_OFF); + (void) pcishpc_setled(pcishpc_p, HPC_ATTN_LED, HPC_LED_OFF); + (void) pcishpc_setled(pcishpc_p, HPC_ACTIVE_LED, HPC_LED_OFF); + + /* Set the slot state to the new slot state. */ + pcishpc_p->slot_state = state; + + /* Update the hardweare slot state. */ + if (pcishpc_set_slot_state(pcishpc_p) != DDI_SUCCESS) { + pcishpc_debug("pcishpc_set_power_state() failed"); + (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_OFF); + pcishpc_get_slot_state(pcishpc_p); + return (DDI_FAILURE); + } + + /* Turn the Power LED ON for a connected slot. */ + if (state == HPC_SLOT_CONNECTED) { + (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_ON); + } + + /* Turn the Power LED OFF for a disconnected slot. */ + if (state == HPC_SLOT_DISCONNECTED) { + (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, HPC_LED_OFF); + } + + /* Turn all other LEDS off. */ + (void) pcishpc_setled(pcishpc_p, HPC_FAULT_LED, HPC_LED_OFF); + (void) pcishpc_setled(pcishpc_p, HPC_ATTN_LED, HPC_LED_OFF); + (void) pcishpc_setled(pcishpc_p, HPC_ACTIVE_LED, HPC_LED_OFF); + + pcishpc_debug("pcishpc_set_power_state() success!"); + + pcishpc_get_slot_state(pcishpc_p); + + /* delay after powerON to let the device initialize itself */ + drv_usecwait(pcishpc_reset_delay); + + return (DDI_SUCCESS); +} + +/* + * pcishpc_disconnect() + * + * Called by Hot Plug Services to disconnect a slot on the bus. + */ +/*ARGSUSED*/ +static int +pcishpc_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl, void *data, + uint_t flags) +{ + pcishpc_t *pcishpc_p; + + pcishpc_debug("pcishpc_disconnect called()"); + + pcishpc_p = pcishpc_hpc_get_slot_state(slot_hdl); + + if (!pcishpc_p) { + pcishpc_debug("pcishpc_disconnect() " + "Failed to find soft state for slot_hdl %x", slot_hdl); + return (HPC_ERR_FAILED); + } + + mutex_enter(&pcishpc_p->ctrl->shpc_mutex); + + if (pcishpc_set_power_state(pcishpc_p, HPC_SLOT_DISCONNECTED) + != DDI_SUCCESS) { + pcishpc_debug("pcishpc_disconnect() failed"); + goto cleanup; + } + + mutex_exit(&pcishpc_p->ctrl->shpc_mutex); + + pcishpc_debug("pcishpc_disconnect() success!"); + + return (HPC_SUCCESS); + +cleanup: + mutex_exit(&pcishpc_p->ctrl->shpc_mutex); + return (HPC_ERR_FAILED); +} + + +/* + * pcishpc_pci_control() + * + * Called by Hot Plug Services to perform a attachment point specific + * operation on a Hot Pluggable Standard PCI Slot. + */ +/*ARGSUSED*/ +static int +pcishpc_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl, int request, + caddr_t arg) +{ + hpc_slot_state_t *hpc_slot_state; + hpc_board_type_t *hpc_board_type; + hpc_led_info_t *hpc_led_info; + pcishpc_t *pcishpc_p; + int ret = HPC_SUCCESS; + + pcishpc_debug("pcishpc_pci_control called(Request %s)", + pcishpc_textrequest(request)); + + pcishpc_p = pcishpc_hpc_get_slot_state(slot_hdl); + + if (!pcishpc_p) { + pcishpc_debug("pcishpc_pci_control() Error: " + "Failed to find soft state for slot_hdl %x", slot_hdl); + return (HPC_ERR_FAILED); + } + + mutex_enter(&pcishpc_p->ctrl->shpc_mutex); + + switch (request) { + case HPC_CTRL_GET_SLOT_STATE: + hpc_slot_state = (hpc_slot_state_t *)arg; + pcishpc_get_slot_state(pcishpc_p); + *hpc_slot_state = pcishpc_p->slot_state; + pcishpc_debug("pcishpc_pci_control() - " + "HPC_CTRL_GET_SLOT_STATE (state=%s)", + pcishpc_textslotstate(pcishpc_p->slot_state)); + break; + + case HPC_CTRL_GET_BOARD_TYPE: + hpc_board_type = (hpc_board_type_t *)arg; + pcishpc_debug("pcishpc_pci_control() - " + "HPC_CTRL_GET_BOARD_TYPE"); + pcishpc_get_slot_state(pcishpc_p); + /* + * The HPS framework does not know what board + * type is plugged in. + */ + if (pcishpc_p->slot_state == HPC_SLOT_EMPTY) + *hpc_board_type = HPC_BOARD_UNKNOWN; + else + *hpc_board_type = HPC_BOARD_PCI_HOTPLUG; + break; + + case HPC_CTRL_GET_LED_STATE: + hpc_led_info = (hpc_led_info_t *)arg; + + pcishpc_get_slot_state(pcishpc_p); + + switch (hpc_led_info->led) { + case HPC_FAULT_LED: + hpc_led_info->state = + pcishpc_p->fault_led_state; + pcishpc_debug("pcishpc_pci_control() - " + "GET_LED FAULT (state=%s)", + pcishpc_textledstate( + hpc_led_info->state)); + break; + + case HPC_POWER_LED: + hpc_led_info->state = + pcishpc_p->power_led_state; + pcishpc_debug("pcishpc_pci_control() - " + "GET_LED POWER (state=%s)", + pcishpc_textledstate( + hpc_led_info->state)); + break; + + case HPC_ATTN_LED: + hpc_led_info->state = + pcishpc_p->attn_led_state; + pcishpc_debug("pcishpc_pci_control() - " + "GET_LED ATTN(state = %s)", + pcishpc_textledstate( + hpc_led_info->state)); + break; + + case HPC_ACTIVE_LED: + hpc_led_info->state = + pcishpc_p->active_led_state; + pcishpc_debug("pcishpc_pci_control() - " + "GET_LED ACTIVE(state = %s)", + pcishpc_textledstate( + hpc_led_info->state)); + break; + + default: + pcishpc_debug("pcishpc_pci_control() " + "Error: GET_LED - " + "Invalid LED %x", + hpc_led_info->led); + ret = HPC_ERR_NOTSUPPORTED; + break; + } + break; + + case HPC_CTRL_SET_LED_STATE: + hpc_led_info = (hpc_led_info_t *)arg; + (void) pcishpc_setled(pcishpc_p, hpc_led_info->led, + hpc_led_info->state); + break; + + case HPC_CTRL_DEV_UNCONFIG_FAILURE: + case HPC_CTRL_DEV_CONFIG_FAILURE: + pcishpc_debug("pcishpc_pci_control() Config/Unconfig " + "failed."); + (void) pcishpc_setled(pcishpc_p, HPC_ATTN_LED, + HPC_LED_BLINK); + break; + + case HPC_CTRL_ENABLE_AUTOCFG: + case HPC_CTRL_DISABLE_AUTOCFG: + case HPC_CTRL_DISABLE_SLOT: + case HPC_CTRL_DEV_UNCONFIGURED: + case HPC_CTRL_ENABLE_SLOT: + case HPC_CTRL_DISABLE_ENUM: + case HPC_CTRL_DEV_UNCONFIG_START: + case HPC_CTRL_DEV_CONFIG_START: + case HPC_CTRL_DEV_CONFIGURED: + pcishpc_debug("pcishpc_pci_control() - %s", + pcishpc_textrequest(request)); + break; + + case HPC_CTRL_ENABLE_ENUM: + default: + pcishpc_debug("pcishpc_pci_control() - Error: " + "request (%d) NOT SUPPORTED", request); + ret = HPC_ERR_NOTSUPPORTED; + break; + } + + mutex_exit(&pcishpc_p->ctrl->shpc_mutex); + return (ret); +} + + +/* + * pcishpc_setled() + * + * Change the state of a slot's LED. + */ +static int +pcishpc_setled(pcishpc_t *pcishpc_p, hpc_led_t led, hpc_led_state_t state) +{ + switch (led) { + case HPC_FAULT_LED: + pcishpc_debug("pcishpc_setled() - HPC_FAULT_LED " + "(set %s)", pcishpc_textledstate(state)); + pcishpc_p->fault_led_state = state; + break; + + case HPC_POWER_LED: + pcishpc_debug("pcishpc_setled() - HPC_POWER_LED " + "(set %s)", pcishpc_textledstate(state)); + pcishpc_p->power_led_state = state; + break; + + case HPC_ATTN_LED: + pcishpc_debug("pcishpc_setled() - HPC_ATTN_LED " + "(set %s)", pcishpc_textledstate(state)); + pcishpc_p->attn_led_state = state; + break; + + case HPC_ACTIVE_LED: + pcishpc_debug("pcishpc_setled() - HPC_ACTIVE_LED " + "(set %s)", pcishpc_textledstate(state)); + pcishpc_p->active_led_state = state; + break; + } + + return (pcishpc_set_slot_state(pcishpc_p)); +} + + +/* + * pcishpc_set_slot_state() + * + * Updates the slot's state and leds. + */ +static int +pcishpc_set_slot_state(pcishpc_t *pcishpc_p) +{ + uint32_t reg; + uint8_t cmd_code; + hpc_slot_state_t slot_state; + + reg = pcishpc_read_reg(pcishpc_p->ctrl, + SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum); + + /* Default all states to unchanged. */ + cmd_code = 0; + + /* Has the slot state changed? */ + if ((reg & SHPC_SLOT_CARD_EMPTY_MASK) == SHPC_SLOT_CARD_EMPTY_MASK) + slot_state = HPC_SLOT_EMPTY; + else + slot_state = pcishpc_slot_shpc_to_hpc(reg & 3); + if (pcishpc_p->slot_state != slot_state) { + pcishpc_debug("pcishpc_set_slot_state() Slot State changed"); + /* Set the new slot state in the Slot operation command. */ + cmd_code |= pcishpc_slot_hpc_to_shpc(pcishpc_p->slot_state); + } + + /* Has the Power LED state changed? */ + if (pcishpc_p->power_led_state != pcishpc_led_shpc_to_hpc((reg>>2)&3)) { + pcishpc_debug("pcishpc_set_slot_state() Power LED State " + "changed"); + /* Set the new power led state in the Slot operation command. */ + cmd_code |= + (pcishpc_led_hpc_to_shpc(pcishpc_p->power_led_state) + << 2); + } + + /* Has the Attn LED state changed? */ + if (pcishpc_p->attn_led_state != pcishpc_led_shpc_to_hpc((reg>>4)&3)) { + pcishpc_debug("pcishpc_set_slot_state() Attn LED State " + "changed"); + /* Set the new attn led state in the Slot operation command. */ + cmd_code |= (pcishpc_led_hpc_to_shpc(pcishpc_p->attn_led_state) + << 4); + } + + return (pcishpc_issue_command(pcishpc_p, cmd_code)); +} + + +/* + * pcishpc_wait_busy() + * + * Wait until the SHPC controller is not busy. + */ +static int +pcishpc_wait_busy(pcishpc_t *pcishpc_p) +{ + uint32_t status; + + /* Wait until SHPC controller is NOT busy. */ + /*CONSTCOND*/ + while (1) { + status = pcishpc_read_reg(pcishpc_p->ctrl, + SHPC_COMMAND_STATUS_REG); + + /* Is there an MRL Sensor error? */ + if (((status>>17) & 1)) { + pcishpc_debug("pcishpc_wait_busy() ERROR: MRL Sensor " + "error"); + break; + } + + /* Is there an Invalid command error? */ + if (((status>>18) & 1)) { + pcishpc_debug("pcishpc_wait_busy() ERROR: Invalid " + "command error"); + break; + } + + /* Is there an Invalid Speed/Mode error? */ + if (((status>>19) & 1)) { + pcishpc_debug("pcishpc_wait_busy() ERROR: Invalid " + "Speed/Mode error"); + break; + } + + /* Is the SHPC controller not BUSY? */ + if (((status>>16)&1) == 0) { + /* Return Success. */ + return (DDI_SUCCESS); + } + + pcishpc_debug("pcishpc_wait_busy() SHPC controller busy. " + "Waiting"); + + /* Wait before polling the status register again. */ + delay(drv_usectohz(SHPC_COMMAND_WAIT_TIME)); + } + + return (DDI_FAILURE); +} + + +/* + * pcishpc_issue_command() + * + * Sends a command to the SHPC controller. + */ +static int +pcishpc_issue_command(pcishpc_t *pcishpc_p, uint8_t cmd_code) +{ + uint32_t command; + int retCode; + + pcishpc_debug("pcishpc_issue_command() cmd_code=%02x", cmd_code); + + /* Setup the slot for this command. */ + command = ((1+pcishpc_p->slotNum)<<8) | (uint32_t)cmd_code; + + mutex_enter(&pcishpc_p->ctrl->shpc_intr_mutex); + + pcishpc_p->ctrl->command_complete = B_FALSE; + + /* Write the command to the SHPC controller. */ + pcishpc_write_reg(pcishpc_p->ctrl, SHPC_COMMAND_STATUS_REG, command); + + while (pcishpc_p->ctrl->command_complete == B_FALSE) + cv_wait(&pcishpc_p->ctrl->cmd_comp_cv, + &pcishpc_p->ctrl->shpc_intr_mutex); + + /* Wait until the SHPC controller processes the command. */ + retCode = pcishpc_wait_busy(pcishpc_p); + + /* Make sure the command completed. */ + if (retCode == DDI_SUCCESS) { + + /* Did the command fail to generate the command complete IRQ? */ + if (pcishpc_p->ctrl->command_complete != B_TRUE) { + pcishpc_debug("pcishpc_issue_command() Failed on " + "generate cmd complete IRQ"); + retCode = DDI_FAILURE; + } + + } + + mutex_exit(&pcishpc_p->ctrl->shpc_intr_mutex); + + if (retCode == DDI_FAILURE) + pcishpc_debug("pcishpc_issue_command() Failed on cmd_code=%02x", + cmd_code); + else + pcishpc_debug("pcishpc_issue_command() Success on " + "cmd_code=%02x", cmd_code); + + return (retCode); +} + +/* + * pcishpc_led_shpc_to_hpc() + * + * Convert from SHPC indicator status to HPC indicator status. + */ +static int +pcishpc_led_shpc_to_hpc(int state) +{ + switch (state) { + case 1: /* SHPC On bits b01 */ + return (HPC_LED_ON); + case 2: /* SHPC Blink bits b10 */ + return (HPC_LED_BLINK); + case 3: /* SHPC Off bits b11 */ + return (HPC_LED_OFF); + } + + return (HPC_LED_OFF); +} + + +/* + * pcishpc_led_hpc_to_shpc() + * + * Convert from HPC indicator status to SHPC indicator status. + */ +static int +pcishpc_led_hpc_to_shpc(int state) +{ + switch (state) { + case HPC_LED_ON: + return (1); /* SHPC On bits b01 */ + case HPC_LED_BLINK: + return (2); /* SHPC Blink bits b10 */ + case HPC_LED_OFF: + return (3); /* SHPC Off bits b11 */ + } + + return (3); /* SHPC Off bits b11 */ +} + +/* + * pcishpc_slot_shpc_to_hpc() + * + * Convert from SHPC slot state to HPC slot state. + */ +static int +pcishpc_slot_shpc_to_hpc(int state) +{ + switch (state) { + case 0: /* SHPC Reserved */ + return (HPC_SLOT_EMPTY); + + case 1: /* SHPC Powered Only */ + return (HPC_SLOT_UNKNOWN); + + case 2: /* SHPC Enabled */ + return (HPC_SLOT_CONNECTED); + + case 3: /* SHPC Disabled */ + return (HPC_SLOT_DISCONNECTED); + } + + /* Unknown slot state. */ + return (HPC_SLOT_UNKNOWN); +} + + +/* + * pcishpc_slot_hpc_to_shpc() + * + * Convert from HPC slot state to SHPC slot state. + */ +static int +pcishpc_slot_hpc_to_shpc(int state) +{ + switch (state) { + case HPC_SLOT_EMPTY: + return (0); /* SHPC Reserved */ + + case HPC_SLOT_UNKNOWN: + return (1); /* SHPC Powered Only */ + + case HPC_SLOT_CONNECTED: + return (2); /* SHPC Enabled */ + + case HPC_SLOT_DISCONNECTED: + return (3); /* SHPC Disabled */ + } + + /* Known slot state is reserved. */ + return (0); +} + + +/* + * pcishpc_get_slot_state() + * + * Get the state of the slot. + */ +static void +pcishpc_get_slot_state(pcishpc_t *pcishpc_p) +{ + uint32_t reg; + + /* Read the logical slot register for this Slot. */ + reg = pcishpc_read_reg(pcishpc_p->ctrl, + SHPC_LOGICAL_SLOT_REGS+pcishpc_p->slotNum); + + /* Convert from the SHPC slot state to the HPC slot state. */ + if ((reg & SHPC_SLOT_CARD_EMPTY_MASK) == SHPC_SLOT_CARD_EMPTY_MASK) + pcishpc_p->slot_state = HPC_SLOT_EMPTY; + else + pcishpc_p->slot_state = pcishpc_slot_shpc_to_hpc(reg & 3); + + /* Convert from the SHPC Power LED state to the HPC Power LED state. */ + pcishpc_p->power_led_state = pcishpc_led_shpc_to_hpc((reg>>2)&3); + + /* Convert from the SHPC Attn LED state to the HPC Attn LED state. */ + pcishpc_p->attn_led_state = pcishpc_led_shpc_to_hpc((reg>>4)&3); + + /* We don't have a fault LED so just default it to OFF. */ + pcishpc_p->fault_led_state = HPC_LED_OFF; + + /* We don't have an active LED so just default it to OFF. */ + pcishpc_p->active_led_state = HPC_LED_OFF; +} + +/* + * pcishpc_textledstate() + * + * Convert the led state into a text message. + */ +static char * +pcishpc_textledstate(hpc_led_state_t state) +{ + /* Convert an HPC led state into a textual string. */ + switch (state) { + case HPC_LED_OFF: + return ("off"); + + case HPC_LED_ON: + return ("on"); + + case HPC_LED_BLINK: + return ("blink"); + } + return ("unknown"); +} + +/* + * pcishpc_textrequest() + * + * Convert the request into a text message. + */ +static char * +pcishpc_textrequest(int request) +{ + /* Convert an HPC request into a textual string. */ + switch (request) { + case HPC_CTRL_GET_LED_STATE: + return ("HPC_CTRL_GET_LED_STATE"); + case HPC_CTRL_SET_LED_STATE: + return ("HPC_CTRL_SET_LED_STATE"); + case HPC_CTRL_GET_SLOT_STATE: + return ("HPC_CTRL_GET_SLOT_STATE"); + case HPC_CTRL_DEV_CONFIGURED: + return ("HPC_CTRL_DEV_CONFIGURED"); + case HPC_CTRL_DEV_UNCONFIGURED: + return ("HPC_CTRL_DEV_UNCONFIGURED"); + case HPC_CTRL_GET_BOARD_TYPE: + return ("HPC_CTRL_GET_BOARD_TYPE"); + case HPC_CTRL_DISABLE_AUTOCFG: + return ("HPC_CTRL_DISABLE_AUTOCFG"); + case HPC_CTRL_ENABLE_AUTOCFG: + return ("HPC_CTRL_ENABLE_AUTOCFG"); + case HPC_CTRL_DISABLE_SLOT: + return ("HPC_CTRL_DISABLE_SLOT"); + case HPC_CTRL_ENABLE_SLOT: + return ("HPC_CTRL_ENABLE_SLOT"); + case HPC_CTRL_DISABLE_ENUM: + return ("HPC_CTRL_DISABLE_ENUM"); + case HPC_CTRL_ENABLE_ENUM: + return ("HPC_CTRL_ENABLE_ENUM"); + case HPC_CTRL_DEV_CONFIG_FAILURE: + return ("HPC_CTRL_DEV_CONFIG_FAILURE"); + case HPC_CTRL_DEV_UNCONFIG_FAILURE: + return ("HPC_CTRL_DEV_UNCONFIG_FAILURE"); + case HPC_CTRL_DEV_CONFIG_START: + return ("HPC_CTRL_DEV_CONFIG_START"); + case HPC_CTRL_DEV_UNCONFIG_START: + return ("HPC_CTRL_DEV_UNCONFIG_START"); + } + return ("Unknown"); +} + +/* + * pcishpc_textslotstate() + * + * Convert the request into a text message. + */ +static char * +pcishpc_textslotstate(hpc_slot_state_t state) +{ + /* Convert an HPC slot state into a textual string. */ + switch (state) { + case HPC_SLOT_EMPTY: + return ("HPC_SLOT_EMPTY"); + case HPC_SLOT_DISCONNECTED: + return ("HPC_SLOT_DISCONNECTED"); + case HPC_SLOT_CONNECTED: + return ("HPC_SLOT_CONNECTED"); + case HPC_SLOT_UNKNOWN: + return ("HPC_SLOT_UNKNOWN"); + } + return ("Unknown"); +} + + +/* + * pcishpc_write_reg() + * + * Write to a SHPC controller register. + */ +static void +pcishpc_write_reg(pcishpc_ctrl_t *ctrl_p, int reg, uint32_t data) +{ + /* Setup the SHPC dword select register. */ + pci_config_put8(ctrl_p->shpc_config_hdl, + ctrl_p->shpc_dword_select, (uint8_t)reg); + + /* Read back the SHPC dword select register and verify. */ + if (pci_config_get8(ctrl_p->shpc_config_hdl, + ctrl_p->shpc_dword_select) != (uint8_t)reg) { + pcishpc_debug("pcishpc_write_reg() - Failed writing " + "DWORD select reg"); + return; + } + + /* Write to the SHPC dword data register. */ + pci_config_put32(ctrl_p->shpc_config_hdl, + ctrl_p->shpc_dword_data_reg, data); + + /* + * Issue a read of the VendorID/DeviceID just to force the previous + * write to complete. This is probably not necessary, but it does + * help enforce ordering if there is an issue. + */ + (void) pci_config_get16(ctrl_p->shpc_config_hdl, PCI_CONF_VENID); +} + + +/* + * pcishpc_read_reg() + * + * Read from a SHPC controller register. + */ +static uint32_t +pcishpc_read_reg(pcishpc_ctrl_t *ctrl_p, int reg) +{ + /* Setup the SHPC dword select register. */ + pci_config_put8(ctrl_p->shpc_config_hdl, + ctrl_p->shpc_dword_select, (uint8_t)reg); + + /* Read back the SHPC dword select register and verify. */ + if (pci_config_get8(ctrl_p->shpc_config_hdl, + ctrl_p->shpc_dword_select) != (uint8_t)reg) { + pcishpc_debug("pcishpc_read_reg() - Failed writing DWORD " + "select reg"); + return (0xFFFFFFFF); + } + + /* Read from the SHPC dword data register. */ + return (pci_config_get32(ctrl_p->shpc_config_hdl, + ctrl_p->shpc_dword_data_reg)); +} + + +/* + * pcishpc_debug() + * + * Controls debug output if enabled. + */ +static void +pcishpc_debug(char *fmt, ...) +{ + va_list ap; + + if (pcishpc_debug_enabled) { + va_start(ap, fmt); + vcmn_err(CE_WARN, fmt, ap); + va_end(ap); + } +} + + +/* + * pcishpc_dump_regs() + * + * Dumps all of the SHPC controller registers. + */ +static void +pcishpc_dump_regs(pcishpc_ctrl_t *ctrl_p) +{ + int slot, numSlots; + uint32_t reg; + char *state; + + cmn_err(CE_WARN, "pcishpc_dump_regs() called:"); + cmn_err(CE_WARN, "================================================" + "=========="); + + cmn_err(CE_WARN, "SHPC Base Offset " + ": 0x%08x", pcishpc_read_reg(ctrl_p, SHPC_BASE_OFFSET_REG)); + + reg = pcishpc_read_reg(ctrl_p, SHPC_SLOTS_AVAIL_I_REG); + + cmn_err(CE_WARN, "Number of PCIX slots avail (33 Mhz) : %d", + (reg & 31)); + + cmn_err(CE_WARN, "Number of PCIX slots avail (66 Mhz) : %d", + ((reg>>8) & 31)); + + cmn_err(CE_WARN, "Number of PCIX slots avail (100 Mhz) : %d", + ((reg>>16) & 31)); + + cmn_err(CE_WARN, "Number of PCIX slots avail (133 Mhz) : %d", + ((reg>>24) & 31)); + + reg = pcishpc_read_reg(ctrl_p, SHPC_SLOTS_AVAIL_II_REG); + + cmn_err(CE_WARN, "Number of conventional PCI slots (66 Mhz) : %d", + (reg & 31)); + + reg = pcishpc_read_reg(ctrl_p, SHPC_SLOT_CONFIGURATION_REG); + + numSlots = (reg & 31); + + cmn_err(CE_WARN, "Number of Slots connected to this port : %d", + numSlots); + + cmn_err(CE_WARN, "PCI Device # for First HotPlug Slot : %d", + ((reg>>8) & 31)); + + cmn_err(CE_WARN, "Physical Slot # for First PCI Device # : %d", + ((reg>>16) & 0x7ff)); + + cmn_err(CE_WARN, "Physical Slot Number Up/Down " + ": %d", ((reg>>29) & 0x1)); + + cmn_err(CE_WARN, "MRL Sensor Implemented " + ": %s", (reg & SHPC_SLOT_CONFIG_MRL_SENSOR) ? "Yes" : + "No"); + + cmn_err(CE_WARN, "Attention Button Implemented " + ": %s", (reg & SHPC_SLOT_CONFIG_ATTN_BUTTON) ? "Yes" : + "No"); + + reg = pcishpc_read_reg(ctrl_p, SHPC_PROF_IF_SBCR_REG); + + switch (reg & 7) { + case 0: + state = "33Mhz Conventional PCI"; + break; + case 1: + state = "66Mhz Conventional PCI"; + break; + case 2: + state = "66Mhz PCI-X"; + break; + case 3: + state = "100Mhz PCI-X"; + break; + case 4: + state = "133Mhz PCI-X"; + break; + default: + state = "Reserved (Error)"; + break; + } + + cmn_err(CE_WARN, "Current Port Operation Mode " + ": %s", state); + + cmn_err(CE_WARN, "SHPC Interrupt Message Number " + ": %d", ((reg>>16) &31)); + + cmn_err(CE_WARN, "SHPC Programming Interface " + ": %d", ((reg>>24) & 0xff)); + + reg = pcishpc_read_reg(ctrl_p, SHPC_COMMAND_STATUS_REG); + + cmn_err(CE_WARN, "SHPC Command Code " + ": %d", (reg & 0xff)); + + cmn_err(CE_WARN, "SHPC Target Slot " + ": %d", ((reg>>8) & 31)); + + cmn_err(CE_WARN, "SHPC Controller Busy " + ": %s", ((reg>>16) & 1) ? "Yes" : "No"); + + cmn_err(CE_WARN, "SHPC Controller Err: MRL Sensor " + ": %s", ((reg>>17) & 1) ? "Yes" : "No"); + + cmn_err(CE_WARN, "SHPC Controller Err: Invalid Command : %s", + ((reg>>18) & 1) ? "Yes" : "No"); + + cmn_err(CE_WARN, "SHPC Controller Err: Invalid Speed/Mode : %s", + ((reg>>19) & 1) ? "Yes" : "No"); + + reg = pcishpc_read_reg(ctrl_p, SHPC_IRQ_LOCATOR_REG); + + cmn_err(CE_WARN, "Command Completion Interrupt Pending : %s", + (reg & SHPC_IRQ_CMD_COMPLETE) ? "Yes" : "No"); + + for (slot = 0; slot < numSlots; slot++) { + cmn_err(CE_WARN, "Slot %d Interrupt Pending " + ": %s", slot+1, + (reg & (SHPC_IRQ_SLOT_N_PENDING<>2) &3))); + + cmn_err(CE_WARN, "Slot %d Attention Indicator State " + ": %s", slot+1, + pcishpc_textledstate(pcishpc_led_shpc_to_hpc( + (reg>>4)&3))); + + cmn_err(CE_WARN, "Slot %d Power Fault " + ": %s", slot+1, ((reg>>6)&1) ? "Fault Detected" : + "No Fault"); + cmn_err(CE_WARN, "Slot %d Attention Button " + ": %s", slot+1, ((reg>>7)&1) ? "Depressed" : + "Not Depressed"); + cmn_err(CE_WARN, "Slot %d MRL Sensor " + ": %s", slot+1, ((reg>>8)&1) ? "Not Closed" : + "Closed"); + cmn_err(CE_WARN, "Slot %d 66mhz Capable " + ": %s", slot+1, ((reg>>9)&1) ? "66mhz" : "33mgz"); + + switch ((reg>>10)&3) { + case 0: + state = "Card Present 7.5W"; + break; + case 1: + state = "Card Present 15W"; + break; + case 2: + state = "Card Present 25W"; + break; + case 3: + state = "Slot Empty"; + break; + } + + cmn_err(CE_WARN, "Slot %d PRSNT1#/PRSNT2# " + ": %s", slot+1, state); + + switch ((reg>>12)&3) { + case 0: + state = "Non PCI-X"; + break; + case 1: + state = "66mhz PCI-X"; + break; + case 2: + state = "Reserved"; + break; + case 3: + state = "133mhz PCI-X"; + break; + } + + cmn_err(CE_WARN, "Slot %d Card Presence Change Detected : %s", + slot+1, (reg & SHPC_SLOT_PRESENCE_DETECTED) ? "Yes" : + "No"); + cmn_err(CE_WARN, "Slot %d Isolated Power Fault Detected : %s", + slot+1, (reg & SHPC_SLOT_ISO_PWR_DETECTED) ? "Yes" : + "No"); + cmn_err(CE_WARN, "Slot %d Attention Button Press Detected" + ": %s", slot+1, + (reg & SHPC_SLOT_ATTN_DETECTED) ? "Yes" : "No"); + cmn_err(CE_WARN, "Slot %d MRL Sensor Change Detected " + ": %s", slot+1, + (reg & SHPC_SLOT_MRL_DETECTED) ? "Yes" : "No"); + cmn_err(CE_WARN, "Slot %d Connected Power Fault Detected" + ": %s", slot+1, + (reg & SHPC_SLOT_POWER_DETECTED) ? "Yes" : "No"); + + cmn_err(CE_WARN, "Slot %d Card Presence IRQ Masked " + ": %s", slot+1, + (reg & SHPC_SLOT_PRESENCE_MASK) ? "Yes" : "No"); + cmn_err(CE_WARN, "Slot %d Isolated Power Fault IRQ Masked" + ": %s", slot+1, + (reg & SHPC_SLOT_ISO_PWR_MASK) ? "Yes" : "No"); + cmn_err(CE_WARN, "Slot %d Attention Button IRQ Masked " + ": %s", slot+1, (reg & SHPC_SLOT_ATTN_MASK) ? "Yes" : + "No"); + cmn_err(CE_WARN, "Slot %d MRL Sensor IRQ Masked " + ": %s", slot+1, + (reg & SHPC_SLOT_MRL_MASK) ? "Yes" : "No"); + cmn_err(CE_WARN, "Slot %d Connected Power Fault IRQ Masked" + " : %s", slot+1, + (reg & SHPC_SLOT_POWER_MASK) ? "Yes" : "No"); + cmn_err(CE_WARN, "Slot %d MRL Sensor SERR Masked " + ": %s", slot+1, + (reg & SHPC_SLOT_MRL_SERR_MASK) ? "Yes" : "No"); + cmn_err(CE_WARN, "Slot %d Connected Power Fault SERR Masked :" + "%s", slot+1, + (reg & SHPC_SLOT_POWER_SERR_MASK) ? "Yes" : "No"); + } +} + +static void +pcishpc_attn_btn_handler(pcishpc_t *pcishpc_p) +{ + hpc_led_state_t power_led_state; + callb_cpr_t cprinfo; + + pcishpc_debug("pcishpc_attn_btn_handler: thread started\n"); + + CALLB_CPR_INIT(&cprinfo, &pcishpc_p->ctrl->shpc_mutex, + callb_generic_cpr, "pcishpc_attn_btn_handler"); + + mutex_enter(&pcishpc_p->ctrl->shpc_mutex); + + /* wait for ATTN button event */ + cv_wait(&pcishpc_p->attn_btn_cv, &pcishpc_p->ctrl->shpc_mutex); + + while (pcishpc_p->attn_btn_thread_exit == B_FALSE) { + if (pcishpc_p->attn_btn_pending == B_TRUE) { + /* get the current state of power LED */ + power_led_state = pcishpc_p->power_led_state; + + /* Blink the Power LED while we wait for 5 seconds */ + (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, + HPC_LED_BLINK); + + /* wait for 5 seconds before taking any action */ + if (cv_timedwait(&pcishpc_p->attn_btn_cv, + &pcishpc_p->ctrl->shpc_mutex, + ddi_get_lbolt() + SEC_TO_TICK(5)) == -1) { + /* + * It is a time out; + * make sure the ATTN pending flag is + * still ON before sending the event + * to HPS framework. + */ + if (pcishpc_p->attn_btn_pending == B_TRUE) { + /* + * send the ATTN button event + * to HPS framework + */ + pcishpc_p->attn_btn_pending = B_FALSE; + (void) hpc_slot_event_notify( + pcishpc_p->slot_handle, + HPC_EVENT_SLOT_ATTN, + HPC_EVENT_NORMAL); + } + } + + /* restore the power LED state ??? XXX */ + (void) pcishpc_setled(pcishpc_p, HPC_POWER_LED, + power_led_state); + continue; + } + + /* wait for another ATTN button event */ + cv_wait(&pcishpc_p->attn_btn_cv, &pcishpc_p->ctrl->shpc_mutex); + } + + pcishpc_debug("pcishpc_attn_btn_handler: thread exit\n"); + cv_signal(&pcishpc_p->attn_btn_cv); + CALLB_CPR_EXIT(&cprinfo); + thread_exit(); +} + +/* + * setup slot name/slot-number info. + */ +static void +pcishpc_set_slot_name(pcishpc_ctrl_t *ctrl_p, int slot) +{ + pcishpc_t *p = ctrl_p->slots[slot]; + uchar_t *slotname_data; + int *slotnum; + uint_t count; + int len; + uchar_t *s; + uint32_t bit_mask; + int pci_id_cnt, pci_id_bit; + int slots_before, found; + int invalid_slotnum = 0; + uint32_t config; + + if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->shpc_dip, + DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) == + DDI_PROP_SUCCESS) { + p->phy_slot_num = slotnum[0]; + ddi_prop_free(slotnum); + } else { + config = pcishpc_read_reg(ctrl_p, SHPC_SLOT_CONFIGURATION_REG); + p->phy_slot_num = SHPC_SLOT_CONFIG_PHY_SLOT_NUM(config); + } + + if (!p->phy_slot_num) { /* platform may not have initialized it */ + cmn_err(CE_WARN, "%s#%d: Invalid slot number! ", + ddi_driver_name(ctrl_p->shpc_dip), + ddi_get_instance(ctrl_p->shpc_dip)); + p->phy_slot_num = pci_config_get8(ctrl_p->shpc_config_hdl, + PCI_BCNF_SECBUS); + invalid_slotnum = 1; + } + + /* + * construct the slot_name: + * if "slot-names" property exists then use that name + * else if valid slot number exists then it is "pci". + * else it will be "pcidev" + */ + if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->shpc_dip, DDI_PROP_DONTPASS, + "slot-names", (caddr_t)&slotname_data, + &len) == DDI_PROP_SUCCESS) { + + bit_mask = slotname_data[3] | (slotname_data[2] << 8) | + (slotname_data[1] << 16) | (slotname_data[0] << 24); + + pci_id_bit = 1; + pci_id_cnt = slots_before = found = 0; + + /* + * Walk the bit mask until we find the bit that corresponds + * to our slots device number. We count how many bits + * we find before we find our slot's bit. + */ + while (!found && (pci_id_cnt < 32)) { + + while (p->deviceNum != pci_id_cnt) { + + /* + * Find the next bit set. + */ + while (!(bit_mask & pci_id_bit) && + (pci_id_cnt < 32)) { + pci_id_bit = pci_id_bit << 1; + pci_id_cnt++; + } + + if (p->deviceNum != pci_id_cnt) + slots_before++; + else + found = 1; + } + } + + if (pci_id_cnt < 32) { + + /* + * Set ptr to first string. + */ + s = slotname_data + 4; + + /* + * Increment past all the strings for the slots + * before ours. + */ + while (slots_before) { + while (*s != NULL) + s++; + s++; + slots_before--; + } + + (void) sprintf(p->slot_info.pci_slot_name, (char *)s); + + kmem_free(slotname_data, len); + return; + } + + /* slot-names entry not found */ + pcishpc_debug("pcishpc_set_slot_name(): " + "No slot-names entry found for slot #%d", + p->phy_slot_num); + kmem_free(slotname_data, len); + } + + if (invalid_slotnum) + (void) sprintf(p->slot_info.pci_slot_name, "pci%ddev%d", + p->phy_slot_num, p->deviceNum); + else + (void) sprintf(p->slot_info.pci_slot_name, "pci%d", + p->phy_slot_num); +} diff --git a/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h b/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h index 6afbf1e432..e11e57109e 100644 --- a/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h +++ b/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h @@ -21,7 +21,7 @@ */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -80,6 +80,7 @@ typedef uint32_t pciehpc_soft_state_t; #define PCIEHPC_SOFT_STATE_INIT_ENABLE 0x20 #define PCIEHPC_SOFT_STATE_INIT_BLOCK 0x40 #define PCIEHPC_SOFT_STATE_INIT_FM 0x80 +#define PCIEHPC_SOFT_STATE_PCIE_DEV 0x10000 /* * PCI Express Hotplug controller soft state structure @@ -210,6 +211,14 @@ extern int pciehpc_debug; /* default interrupt priority for Hot Plug interrupts */ #define PCIEHPC_INTR_PRI 1 +#if defined(__sparc) +#define PCIEHPC_ENABLE_ERRORS(arg) pciehpc_enable_errors(arg) +#define PCIEHPC_DISABLE_ERRORS(arg) pciehpc_disable_errors(arg) +#else +#define PCIEHPC_ENABLE_ERRORS(arg) +#define PCIEHPC_DISABLE_ERRORS(arg) +#endif + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/common/sys/hotplug/pci/pcishpc.h b/usr/src/uts/common/sys/hotplug/pci/pcishpc.h new file mode 100644 index 0000000000..f8c1fab1c0 --- /dev/null +++ b/usr/src/uts/common/sys/hotplug/pci/pcishpc.h @@ -0,0 +1,50 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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_PCISHPC_H +#define _SYS_PCISHPC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Interfaces exported by SHPC Nexus extension module + */ + +int pcishpc_init(dev_info_t *); +int pcishpc_uninit(dev_info_t *); +int pcishpc_intr(dev_info_t *); + +#define PCISHPC_INTR_PRI (LOCK_LEVEL - 1) +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_PCISHPC_H */ diff --git a/usr/src/uts/sparc/Makefile.files b/usr/src/uts/sparc/Makefile.files index c582f2a42b..a831f5a4ed 100644 --- a/usr/src/uts/sparc/Makefile.files +++ b/usr/src/uts/sparc/Makefile.files @@ -20,7 +20,7 @@ # CDDL HEADER END # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -68,6 +68,7 @@ FD_OBJS += fd_asm.o CPR_SPARC_OBJS += cpr_sparc.o PCI_PCI_OBJS += pci_pci.o pci_debug.o pci_pwr.o pcix.o PX_PCI_OBJS += px_pci.o px_debug.o pcie_pwr.o +FCODE_OBJS += fcode.o # # file system modules @@ -90,6 +91,9 @@ KRTLD_OBJS += \ kobj_reloc.o SWAPGENERIC_OBJS += swapgeneric.o +PCICFG_E_OBJS += pcicfg.e.o +FCPCI_OBJS += fcpci.o +FCODEM_OBJS += fc_ddi.o fc_physio.o fc_ops.o fc_subr.o # # special files diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared index 5bdb8df6b2..7933df65ed 100644 --- a/usr/src/uts/sparc/Makefile.sparc.shared +++ b/usr/src/uts/sparc/Makefile.sparc.shared @@ -264,6 +264,7 @@ DRV_KMODS += sbp2 DRV_KMODS += ib ibd DRV_KMODS += pci_pci px_pci pcie DRV_KMODS += i8042 kb8042 mouse8042 +DRV_KMODS += fcode $(CLOSED_BUILD)CLOSED_DRV_KMODS += audioens $(CLOSED_BUILD)CLOSED_DRV_KMODS += audiovia823x @@ -352,7 +353,7 @@ MISC_KMODS += kmech_krb5 MISC_KMODS += fssnap_if MISC_KMODS += hidparser kbtrans usba usba10 usbs49_fw MISC_KMODS += s1394 -MISC_KMODS += hpcsvc pcicfg pcihp +MISC_KMODS += hpcsvc pcihp pciehpc pcishpc MISC_KMODS += rsmops MISC_KMODS += kcf MISC_KMODS += ibcm @@ -364,6 +365,7 @@ MISC_KMODS += zmod MISC_KMODS += mac dls MISC_KMODS += cmlb MISC_KMODS += tem +MISC_KMODS += pcicfg.e fcodem fcpci $(CLOSED_BUILD)CLOSED_MISC_KMODS += amsrc1 $(CLOSED_BUILD)CLOSED_MISC_KMODS += klmmod klmops diff --git a/usr/src/uts/sparc/fcode/Makefile b/usr/src/uts/sparc/fcode/Makefile new file mode 100644 index 0000000000..5f5eac7595 --- /dev/null +++ b/usr/src/uts/sparc/fcode/Makefile @@ -0,0 +1,103 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +# uts/sparc/fcode/Makefile +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the fcode driver kernel module +# +# sparc implementation architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = fcode +OBJECTS = $(FCODE_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(FCODE_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) +CONF_SRCDIR = $(UTSBASE)/sun4/io/efcode + +# +# Include common rules. +# +include $(UTSBASE)/sparc/Makefile.sparc + +# +# Define targets +# +ALL_TARGET = $(BINARY) $(SRC_CONFFILE) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) + +# +# Include sun4 specific headers files +# +INC_PATH += -I$(UTSBASE)/sun4 + +# Turn this on once compiler understands v9 in it's backend +#INLINES += $(UTSBASE)/sun4/io/fcode.il + +# +# lint pass one enforcement +# +CFLAGS += $(CCVERBOSE) + +# +# Turn on doubleword alignment for 64 bit registers +# +CFLAGS += -dalign + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sparc/fcodem/Makefile b/usr/src/uts/sparc/fcodem/Makefile new file mode 100644 index 0000000000..94141b4ba7 --- /dev/null +++ b/usr/src/uts/sparc/fcodem/Makefile @@ -0,0 +1,102 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +# uts/sparc/fcodem/Makefile +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the fcodem misc kernel module +# +# sparc implementation architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = fcodem +OBJECTS = $(FCODEM_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(FCODEM_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/sparc/Makefile.sparc + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Include sun4 specific headers files +# +INC_PATH += -I$(UTSBASE)/sun4 + +# Turn this on once compiler understands v9 in it's backend +#INLINES += $(UTSBASE)/sun4/io/fcode.il + +# +# lint pass one enforcement +# +CFLAGS += $(CCVERBOSE) + +# +# Turn on doubleword alignment for 64 bit registers +# +CFLAGS += -dalign + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sparc/fcpci/Makefile b/usr/src/uts/sparc/fcpci/Makefile new file mode 100644 index 0000000000..c11db6ad71 --- /dev/null +++ b/usr/src/uts/sparc/fcpci/Makefile @@ -0,0 +1,101 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +# uts/sparc/fcpci/Makefile +# +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the fcpci kernel misc module +# +# sparc implementation architecture dependent +# +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = fcpci +OBJECTS = $(FCPCI_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(FCPCI_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/sparc/Makefile.sparc + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Include sun4 specific headers files +# +INC_PATH += -I$(UTSBASE)/sun4 + +# Turn this on once compiler understands v9 in it's backend +#INLINES += $(UTSBASE)/sun4/io/fcode.il + +# +# lint pass one enforcement +# +CFLAGS += $(CCVERBOSE) + +# +# Turn on doubleword alignment for 64 bit registers +# +CFLAGS += -dalign + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sparc/ml/modstubs.s b/usr/src/uts/sparc/ml/modstubs.s index 2f2bdb9508..97bff00380 100644 --- a/usr/src/uts/sparc/ml/modstubs.s +++ b/usr/src/uts/sparc/ml/modstubs.s @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -982,13 +982,35 @@ stubs_base: #endif /* - * Stubs for PCI configurator module (misc/pcicfg). + * Stubs for PCI configurator module (misc/pcicfg.e). */ -#ifndef PCICFG_MODULE - MODULE(pcicfg,misc); - STUB(pcicfg, pcicfg_configure, 0); - STUB(pcicfg, pcicfg_unconfigure, 0); - END_MODULE(pcicfg); +#ifndef PCICFG_E_MODULE + MODULE(pcicfg.e,misc); + STUB(pcicfg.e, pcicfg_configure, 0); + STUB(pcicfg.e, pcicfg_unconfigure, 0); + END_MODULE(pcicfg.e); +#endif + +/* + * Stubs for PCIEHPC (pci-ex hot plug support) module (misc/pciehpc). + */ +#ifndef PCIEHPC_MODULE + MODULE(pciehpc,misc); + STUB(pciehpc, pciehpc_init, 0); + STUB(pciehpc, pciehpc_uninit, 0); + WSTUB(pciehpc, pciehpc_intr, 0); + END_MODULE(pciehpc); +#endif + +/* + * Stubs for PCISHPC (pci/pci-x shpc hot plug support) module (misc/pcishpc). + */ +#ifndef PCISHPC_MODULE + MODULE(pcishpc,misc); + STUB(pcishpc, pcishpc_init, 0); + STUB(pcishpc, pcishpc_uninit, 0); + WSTUB(pcishpc, pcishpc_intr, 0); + END_MODULE(pcishpc); #endif #ifndef PCIHP_MODULE diff --git a/usr/src/uts/sparc/pcicfg.e/Makefile b/usr/src/uts/sparc/pcicfg.e/Makefile new file mode 100644 index 0000000000..9c48775d98 --- /dev/null +++ b/usr/src/uts/sparc/pcicfg.e/Makefile @@ -0,0 +1,107 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +# uts/sparc/pcicfg.e/Makefile +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the EFCode Enabled +# PCI Configurator. +# +# sun4 implementation architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = pcicfg.e +OBJECTS = $(PCICFG_E_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(PCICFG_E_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/sparc/Makefile.sparc + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Include sun4 specific headers files +# +INC_PATH += -I$(UTSBASE)/sun4 + + +# Turn this on once compiler understands v9 in it's backend +#INLINES += $(UTSBASE)/sun4u/io/pcicfg.il + +# +# lint pass one enforcement +# +CFLAGS += $(CCVERBOSE) + +# +# Turn on doubleword alignment for 64 bit registers +# +CFLAGS += -dalign -DPCICFG_INTERPRET_FCODE + +# +# Dependency +LDFLAGS += -dy -Nmisc/busra -Nmisc/fcpci -Nmisc/fcodem -Nmisc/pcie + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sparc/pciehpc/Makefile b/usr/src/uts/sparc/pciehpc/Makefile new file mode 100644 index 0000000000..1f9d29c436 --- /dev/null +++ b/usr/src/uts/sparc/pciehpc/Makefile @@ -0,0 +1,87 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +# uts/sparc/pciehpc/Makefile +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the kernel/misc/pciehpc module +# (PCIe hotplug controller module) for PCIe hotplug support in PCIe nexus +# drivers. +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = pciehpc +OBJECTS = $(PCIEHPCNEXUS_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(PCIEHPCNEXUS_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE) + +# +# Include common rules. + +include $(UTSBASE)/sparc/Makefile.sparc + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Dependency +LDFLAGS += -dy -Nmisc/hpcsvc -Nmisc/pcie + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sparc/pcihp/Makefile b/usr/src/uts/sparc/pcihp/Makefile index b410885379..8e7076c31e 100644 --- a/usr/src/uts/sparc/pcihp/Makefile +++ b/usr/src/uts/sparc/pcihp/Makefile @@ -21,7 +21,7 @@ # # # uts/sparc/pcihp/Makefile -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #pragma ident "%Z%%M% %I% %E% SMI" @@ -71,7 +71,7 @@ CLEANFILES += $(MODSTUBS_O) # # Dependency -LDFLAGS += -dy -Nmisc/busra -Nmisc/hpcsvc -Nmisc/pcicfg +LDFLAGS += -dy -Nmisc/busra -Nmisc/hpcsvc -Nmisc/pcicfg.e # # Default build targets. diff --git a/usr/src/uts/sparc/pcishpc/Makefile b/usr/src/uts/sparc/pcishpc/Makefile new file mode 100644 index 0000000000..4052e0338f --- /dev/null +++ b/usr/src/uts/sparc/pcishpc/Makefile @@ -0,0 +1,87 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +# uts/sparc/pcishpc/Makefile +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of the kernel/misc/pcishpc module +# (PCI SHPC hotplug controller module) for PCI hotplug support in PCI +# nexus drivers. +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = pcishpc +OBJECTS = $(PCISHPC_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(PCISHPC_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE) + +# +# Include common rules. + +include $(UTSBASE)/sparc/Makefile.sparc + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Dependency +LDFLAGS += -dy -Nmisc/hpcsvc + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sparc/px_pci/Makefile b/usr/src/uts/sparc/px_pci/Makefile index fd148993ee..3d6b919e2f 100644 --- a/usr/src/uts/sparc/px_pci/Makefile +++ b/usr/src/uts/sparc/px_pci/Makefile @@ -22,7 +22,7 @@ # # uts/sparc/px_pci/Makefile # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -75,7 +75,7 @@ CFLAGS += -dalign # # Dependency # -LDFLAGS += -dy -Nmisc/pcie +LDFLAGS += -dy -Nmisc/pcie -Nmisc/pcishpc -Nmisc/pcihp -Nmisc/pciehpc # # Default build targets. @@ -102,4 +102,3 @@ install: $(INSTALL_DEPS) # Include common targets. # include $(UTSBASE)/sparc/Makefile.targ -include $(UTSBASE)/sun4/Makefile.rules diff --git a/usr/src/uts/sun4/Makefile.files b/usr/src/uts/sun4/Makefile.files index 8ad679100e..2be0e9093f 100644 --- a/usr/src/uts/sun4/Makefile.files +++ b/usr/src/uts/sun4/Makefile.files @@ -80,9 +80,6 @@ PX_OBJS += px.o px_cb.o px_debug.o px_devctl.o px_dma.o \ FPC_OBJS += fpc.o fpc-impl.o fpc-kstats.o VIS_OBJS += visinstr.o TOD_OBJS += tod.o -FCODE_OBJS += fcode.o -FCODEM_OBJS += fc_ddi.o fc_physio.o fc_ops.o fc_subr.o -FCPCI_OBJS += fcpci.o EBUS_OBJS += ebus.o SU_OBJS += su_driver.o diff --git a/usr/src/uts/sun4/io/pcicfg.e.c b/usr/src/uts/sun4/io/pcicfg.e.c new file mode 100644 index 0000000000..aa5fef244e --- /dev/null +++ b/usr/src/uts/sun4/io/pcicfg.e.c @@ -0,0 +1,5946 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * PCI configurator (pcicfg) + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCICFG_DEVICE_TYPE_PCI 1 +#define PCICFG_DEVICE_TYPE_PCIE 2 + +#define EFCODE21554 /* changes for supporting 21554 */ + +static int pcicfg_alloc_resource(dev_info_t *, pci_regspec_t); +static int pcicfg_free_resource(dev_info_t *, pci_regspec_t); +static int pcicfg_remove_assigned_prop(dev_info_t *, pci_regspec_t *); + +#ifdef PCICFG_INTERPRET_FCODE +static int pcicfg_fcode_assign_bars(ddi_acc_handle_t, dev_info_t *, + uint_t, uint_t, uint_t, int32_t, pci_regspec_t *); +#endif /* PCICFG_INTERPRET_FCODE */ + +/* + * ************************************************************************ + * *** Implementation specific local data structures/definitions. *** + * ************************************************************************ + */ + +static int pcicfg_start_devno = 0; /* for Debug only */ + +#define PCICFG_MAX_DEVICE 32 +#define PCICFG_MAX_FUNCTION 8 +#define PCICFG_MAX_REGISTER 64 +#define PCICFG_MAX_BUS_DEPTH 255 + +#define PCICFG_NODEVICE 42 +#define PCICFG_NOMEMORY 43 +#define PCICFG_NOMULTI 44 + +#define PCICFG_HIADDR(n) ((uint32_t)(((uint64_t)(n) & 0xFFFFFFFF00000000)>> 32)) +#define PCICFG_LOADDR(n) ((uint32_t)((uint64_t)(n) & 0x00000000FFFFFFFF)) +#define PCICFG_LADDR(lo, hi) (((uint64_t)(hi) << 32) | (uint32_t)(lo)) + +#define PCICFG_HIWORD(n) ((uint16_t)(((uint32_t)(n) & 0xFFFF0000)>> 16)) +#define PCICFG_LOWORD(n) ((uint16_t)((uint32_t)(n) & 0x0000FFFF)) +#define PCICFG_HIBYTE(n) ((uint8_t)(((uint16_t)(n) & 0xFF00)>> 8)) +#define PCICFG_LOBYTE(n) ((uint8_t)((uint16_t)(n) & 0x00FF)) + +#define PCICFG_ROUND_UP(addr, gran) ((uintptr_t)((gran+addr-1)&(~(gran-1)))) +#define PCICFG_ROUND_DOWN(addr, gran) ((uintptr_t)((addr) & ~(gran-1))) + +#define PCICFG_MEMGRAN 0x100000 +#define PCICFG_IOGRAN 0x1000 +#define PCICFG_4GIG_LIMIT 0xFFFFFFFFUL + +#define PCICFG_MEM_MULT 4 +#define PCICFG_IO_MULT 4 +#define PCICFG_RANGE_LEN 2 /* Number of range entries */ + +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_chassis_per_tree = 1; +static int pcicfg_sec_reset_delay = 1000000; + +/* + * The following typedef is used to represent a + * 1275 "bus-range" property of a PCI Bus node. + * DAF - should be in generic include file... + */ + +typedef struct pcicfg_bus_range { + uint32_t lo; + uint32_t hi; +} pcicfg_bus_range_t; + +typedef struct pcicfg_range { + + uint32_t child_hi; + uint32_t child_mid; + uint32_t child_lo; + uint32_t parent_hi; + uint32_t parent_mid; + uint32_t parent_lo; + uint32_t size_hi; + uint32_t size_lo; + +} pcicfg_range_t; + +typedef struct hole hole_t; + +struct hole { + uint64_t start; + uint64_t len; + hole_t *next; +}; + +typedef struct pcicfg_phdl pcicfg_phdl_t; + +struct pcicfg_phdl { + + dev_info_t *dip; /* Associated with the attach point */ + pcicfg_phdl_t *next; + + uint64_t memory_base; /* Memory base for this attach point */ + uint64_t memory_last; + uint64_t memory_len; + uint32_t io_base; /* I/O base for this attach point */ + uint32_t io_last; + uint32_t io_len; + + int error; + uint_t highest_bus; /* Highest bus seen on the probe */ + + hole_t mem_hole; /* Memory hole linked list. */ + hole_t io_hole; /* IO hole linked list */ + + ndi_ra_request_t mem_req; /* allocator request for memory */ + ndi_ra_request_t io_req; /* allocator request for I/O */ +}; + +struct pcicfg_standard_prop_entry { + uchar_t *name; + uint_t config_offset; + uint_t size; +}; + + +struct pcicfg_name_entry { + uint32_t class_code; + char *name; +}; + +struct pcicfg_find_ctrl { + uint_t device; + uint_t function; + dev_info_t *dip; +}; + +typedef struct pcicfg_err_regs { + uint16_t cmd; + uint16_t bcntl; + uint16_t pcie_dev; + uint16_t devctl; + int pcie_cap_off; +} pcicfg_err_regs_t; + +/* + * List of Indirect Config Map Devices. At least the intent of the + * design is to look for a device in this list during the configure + * operation, and if the device is listed here, then it is a nontransparent + * bridge, hence load the driver and avail the config map services from + * the driver. Class and Subclass should be as defined in the PCI specs + * ie. class is 0x6, and subclass is 0x9. + */ +static struct { + uint8_t mem_range_bar_offset; + uint8_t io_range_bar_offset; + uint8_t prefetch_mem_range_bar_offset; +} pcicfg_indirect_map_devs[] = { + PCI_CONF_BASE3, PCI_CONF_BASE2, PCI_CONF_BASE3, + 0, 0, 0, +}; + +#define PCICFG_MAKE_REG_HIGH(busnum, devnum, funcnum, register)\ + (\ + ((ulong_t)(busnum & 0xff) << 16) |\ + ((ulong_t)(devnum & 0x1f) << 11) |\ + ((ulong_t)(funcnum & 0x7) << 8) |\ + ((ulong_t)(register & 0x3f))) + +/* + * debug macros: + */ +#if defined(DEBUG) +extern void prom_printf(const char *, ...); + +/* + * Following values are defined for this debug flag. + * + * 1 = dump configuration header only. + * 2 = dump generic debug data only (no config header dumped) + * 3 = dump everything (both 1 and 2) + */ +int pcicfg_debug = 0; +int pcicfg_dump_fcode = 0; + +static void debug(char *, uintptr_t, uintptr_t, + uintptr_t, uintptr_t, uintptr_t); + +#define DEBUG0(fmt)\ + debug(fmt, 0, 0, 0, 0, 0); +#define DEBUG1(fmt, a1)\ + debug(fmt, (uintptr_t)(a1), 0, 0, 0, 0); +#define DEBUG2(fmt, a1, a2)\ + debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), 0, 0, 0); +#define DEBUG3(fmt, a1, a2, a3)\ + debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2),\ + (uintptr_t)(a3), 0, 0); +#define DEBUG4(fmt, a1, a2, a3, a4)\ + debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2),\ + (uintptr_t)(a3), (uintptr_t)(a4), 0); +#else +#define DEBUG0(fmt) +#define DEBUG1(fmt, a1) +#define DEBUG2(fmt, a1, a2) +#define DEBUG3(fmt, a1, a2, a3) +#define DEBUG4(fmt, a1, a2, a3, a4) +#endif + +#ifdef PCICFG_INTERPRET_FCODE +int pcicfg_dont_interpret = 0; +#else +int pcicfg_dont_interpret = 1; +#endif + +/* + * forward declarations for routines defined in this module (called here) + */ + +static int pcicfg_add_config_reg(dev_info_t *, + uint_t, uint_t, uint_t); +static int pcicfg_probe_children(dev_info_t *, uint_t, uint_t, uint_t, + uint_t *); + +#ifdef PCICFG_INTERPRET_FCODE +static int pcicfg_load_fcode(dev_info_t *, uint_t, uint_t, uint_t, + uint16_t, uint16_t, uchar_t **, int *, int, int); +#endif + +static int pcicfg_fcode_probe(dev_info_t *, uint_t, uint_t, uint_t, uint_t *); +static int pcicfg_probe_bridge(dev_info_t *, ddi_acc_handle_t, uint_t, + uint_t *); +static int pcicfg_free_all_resources(dev_info_t *); +static int pcicfg_alloc_new_resources(dev_info_t *); +static int pcicfg_match_dev(dev_info_t *, void *); +static dev_info_t *pcicfg_devi_find(dev_info_t *, uint_t, uint_t); +static pcicfg_phdl_t *pcicfg_find_phdl(dev_info_t *); +static pcicfg_phdl_t *pcicfg_create_phdl(dev_info_t *); +static int pcicfg_destroy_phdl(dev_info_t *); +static int pcicfg_sum_resources(dev_info_t *, void *); +static int pcicfg_find_resource_end(dev_info_t *, void *); +static int pcicfg_allocate_chunk(dev_info_t *); +static int pcicfg_program_ap(dev_info_t *); +static int pcicfg_device_assign(dev_info_t *); +static int pcicfg_bridge_assign(dev_info_t *, void *); +static int pcicfg_free_resources(dev_info_t *); +static void pcicfg_setup_bridge(pcicfg_phdl_t *, ddi_acc_handle_t); +static void pcicfg_update_bridge(pcicfg_phdl_t *, ddi_acc_handle_t); +static void pcicfg_enable_bridge_probe_err(dev_info_t *dip, + ddi_acc_handle_t h, pcicfg_err_regs_t *regs); +static void pcicfg_disable_bridge_probe_err(dev_info_t *dip, + ddi_acc_handle_t h, pcicfg_err_regs_t *regs); +static int pcicfg_update_assigned_prop(dev_info_t *, pci_regspec_t *); +static void pcicfg_device_on(ddi_acc_handle_t); +static void pcicfg_device_off(ddi_acc_handle_t); +static int pcicfg_set_busnode_props(dev_info_t *, uint8_t); +static int pcicfg_free_bridge_resources(dev_info_t *); +static int pcicfg_free_device_resources(dev_info_t *); +static int pcicfg_teardown_device(dev_info_t *); +static int pcicfg_config_setup(dev_info_t *, ddi_acc_handle_t *); +static void pcicfg_config_teardown(ddi_acc_handle_t *); +static void pcicfg_get_mem(pcicfg_phdl_t *, uint32_t, uint64_t *); +static void pcicfg_get_io(pcicfg_phdl_t *, uint32_t, uint32_t *); +static int pcicfg_update_ranges_prop(dev_info_t *, pcicfg_range_t *); +static int pcicfg_map_phys(dev_info_t *, pci_regspec_t *, caddr_t *, + ddi_device_acc_attr_t *, ddi_acc_handle_t *); +static void pcicfg_unmap_phys(ddi_acc_handle_t *, pci_regspec_t *); +static int pcicfg_dump_assigned(dev_info_t *); +static uint_t pcicfg_configure_ntbridge(dev_info_t *, uint_t, uint_t); +static int pcicfg_indirect_map(dev_info_t *dip); +static uint_t pcicfg_get_ntbridge_child_range(dev_info_t *, uint64_t *, + uint64_t *, uint_t); +static int pcicfg_is_ntbridge(dev_info_t *); +static int pcicfg_ntbridge_allocate_resources(dev_info_t *); +static int pcicfg_ntbridge_configure_done(dev_info_t *); +static int pcicfg_ntbridge_unconfigure(dev_info_t *); +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); + +#ifdef DEBUG +static void pcicfg_dump_common_config(ddi_acc_handle_t config_handle); +static void pcicfg_dump_device_config(ddi_acc_handle_t); + +static void pcicfg_dump_bridge_config(ddi_acc_handle_t config_handle); +static uint64_t pcicfg_unused_space(hole_t *, uint32_t *); + +#define PCICFG_DUMP_COMMON_CONFIG(hdl) (void)pcicfg_dump_common_config(hdl) +#define PCICFG_DUMP_DEVICE_CONFIG(hdl) (void)pcicfg_dump_device_config(hdl) +#define PCICFG_DUMP_BRIDGE_CONFIG(hdl) (void)pcicfg_dump_bridge_config(hdl) +#else +#define PCICFG_DUMP_COMMON_CONFIG(handle) +#define PCICFG_DUMP_DEVICE_CONFIG(handle) +#define PCICFG_DUMP_BRIDGE_CONFIG(handle) +#endif + +static kmutex_t pcicfg_list_mutex; /* Protects the probe handle list */ +static pcicfg_phdl_t *pcicfg_phdl_list = NULL; + +#ifndef _DONT_USE_1275_GENERIC_NAMES +/* + * Class code table + */ +static struct pcicfg_name_entry pcicfg_class_lookup [] = { + + { 0x001, "display" }, + { 0x100, "scsi" }, + { 0x101, "ide" }, + { 0x102, "fdc" }, + { 0x103, "ipi" }, + { 0x104, "raid" }, + { 0x200, "ethernet" }, + { 0x201, "token-ring" }, + { 0x202, "fddi" }, + { 0x203, "atm" }, + { 0x300, "display" }, + { 0x400, "video" }, + { 0x401, "sound" }, + { 0x500, "memory" }, + { 0x501, "flash" }, + { 0x600, "host" }, + { 0x601, "isa" }, + { 0x602, "eisa" }, + { 0x603, "mca" }, + { 0x604, "pci" }, + { 0x605, "pcmcia" }, + { 0x606, "nubus" }, + { 0x607, "cardbus" }, + { 0x609, "pci" }, + { 0x700, "serial" }, + { 0x701, "parallel" }, + { 0x800, "interrupt-controller" }, + { 0x801, "dma-controller" }, + { 0x802, "timer" }, + { 0x803, "rtc" }, + { 0x900, "keyboard" }, + { 0x901, "pen" }, + { 0x902, "mouse" }, + { 0xa00, "dock" }, + { 0xb00, "cpu" }, + { 0xc00, "firewire" }, + { 0xc01, "access-bus" }, + { 0xc02, "ssa" }, + { 0xc03, "usb" }, + { 0xc04, "fibre-channel" }, + { 0, 0 } +}; +#endif /* _DONT_USE_1275_GENERIC_NAMES */ + +/* + * Module control operations + */ + +extern struct mod_ops mod_miscops; + +static struct modlmisc modlmisc = { + &mod_miscops, /* Type of module */ + "PCIe/PCI Config (EFCode Enabled) v%I%" +}; + +static struct modlinkage modlinkage = { + MODREV_1, (void *)&modlmisc, NULL +}; + +#ifdef DEBUG + +static void +pcicfg_dump_common_config(ddi_acc_handle_t config_handle) +{ + if ((pcicfg_debug & 1) == 0) + return; + cmn_err(CE_CONT, " Vendor ID = [0x%x]\n", + pci_config_get16(config_handle, PCI_CONF_VENID)); + cmn_err(CE_CONT, " Device ID = [0x%x]\n", + pci_config_get16(config_handle, PCI_CONF_DEVID)); + cmn_err(CE_CONT, " Command REG = [0x%x]\n", + pci_config_get16(config_handle, PCI_CONF_COMM)); + cmn_err(CE_CONT, " Status REG = [0x%x]\n", + pci_config_get16(config_handle, PCI_CONF_STAT)); + cmn_err(CE_CONT, " Revision ID = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_REVID)); + cmn_err(CE_CONT, " Prog Class = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_PROGCLASS)); + cmn_err(CE_CONT, " Dev Class = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_SUBCLASS)); + cmn_err(CE_CONT, " Base Class = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_BASCLASS)); + cmn_err(CE_CONT, " Device ID = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_CACHE_LINESZ)); + cmn_err(CE_CONT, " Header Type = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_HEADER)); + cmn_err(CE_CONT, " BIST = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_BIST)); + cmn_err(CE_CONT, " BASE 0 = [0x%x]\n", + pci_config_get32(config_handle, PCI_CONF_BASE0)); + cmn_err(CE_CONT, " BASE 1 = [0x%x]\n", + pci_config_get32(config_handle, PCI_CONF_BASE1)); + +} + +static void +pcicfg_dump_device_config(ddi_acc_handle_t config_handle) +{ + if ((pcicfg_debug & 1) == 0) + return; + pcicfg_dump_common_config(config_handle); + + cmn_err(CE_CONT, " BASE 2 = [0x%x]\n", + pci_config_get32(config_handle, PCI_CONF_BASE2)); + cmn_err(CE_CONT, " BASE 3 = [0x%x]\n", + pci_config_get32(config_handle, PCI_CONF_BASE3)); + cmn_err(CE_CONT, " BASE 4 = [0x%x]\n", + pci_config_get32(config_handle, PCI_CONF_BASE4)); + cmn_err(CE_CONT, " BASE 5 = [0x%x]\n", + pci_config_get32(config_handle, PCI_CONF_BASE5)); + cmn_err(CE_CONT, " Cardbus CIS = [0x%x]\n", + pci_config_get32(config_handle, PCI_CONF_CIS)); + cmn_err(CE_CONT, " Sub VID = [0x%x]\n", + pci_config_get16(config_handle, PCI_CONF_SUBVENID)); + cmn_err(CE_CONT, " Sub SID = [0x%x]\n", + pci_config_get16(config_handle, PCI_CONF_SUBSYSID)); + cmn_err(CE_CONT, " ROM = [0x%x]\n", + pci_config_get32(config_handle, PCI_CONF_ROM)); + cmn_err(CE_CONT, " I Line = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_ILINE)); + cmn_err(CE_CONT, " I Pin = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_IPIN)); + cmn_err(CE_CONT, " Max Grant = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_MIN_G)); + cmn_err(CE_CONT, " Max Latent = [0x%x]\n", + pci_config_get8(config_handle, PCI_CONF_MAX_L)); +} + +static void +pcicfg_dump_bridge_config(ddi_acc_handle_t config_handle) +{ + if ((pcicfg_debug & 1) == 0) + return; + + pcicfg_dump_common_config(config_handle); + + cmn_err(CE_CONT, "........................................\n"); + + cmn_err(CE_CONT, " Pri Bus = [0x%x]\n", + pci_config_get8(config_handle, PCI_BCNF_PRIBUS)); + cmn_err(CE_CONT, " Sec Bus = [0x%x]\n", + pci_config_get8(config_handle, PCI_BCNF_SECBUS)); + cmn_err(CE_CONT, " Sub Bus = [0x%x]\n", + pci_config_get8(config_handle, PCI_BCNF_SUBBUS)); + cmn_err(CE_CONT, " Latency = [0x%x]\n", + pci_config_get8(config_handle, PCI_BCNF_LATENCY_TIMER)); + cmn_err(CE_CONT, " I/O Base LO = [0x%x]\n", + pci_config_get8(config_handle, PCI_BCNF_IO_BASE_LOW)); + cmn_err(CE_CONT, " I/O Lim LO = [0x%x]\n", + pci_config_get8(config_handle, PCI_BCNF_IO_LIMIT_LOW)); + cmn_err(CE_CONT, " Sec. Status = [0x%x]\n", + pci_config_get16(config_handle, PCI_BCNF_SEC_STATUS)); + cmn_err(CE_CONT, " Mem Base = [0x%x]\n", + pci_config_get16(config_handle, PCI_BCNF_MEM_BASE)); + cmn_err(CE_CONT, " Mem Limit = [0x%x]\n", + pci_config_get16(config_handle, PCI_BCNF_MEM_LIMIT)); + cmn_err(CE_CONT, " PF Mem Base = [0x%x]\n", + pci_config_get16(config_handle, PCI_BCNF_PF_BASE_LOW)); + cmn_err(CE_CONT, " PF Mem Lim = [0x%x]\n", + pci_config_get16(config_handle, PCI_BCNF_PF_LIMIT_LOW)); + cmn_err(CE_CONT, " PF Base HI = [0x%x]\n", + pci_config_get32(config_handle, PCI_BCNF_PF_BASE_HIGH)); + cmn_err(CE_CONT, " PF Lim HI = [0x%x]\n", + pci_config_get32(config_handle, PCI_BCNF_PF_LIMIT_HIGH)); + cmn_err(CE_CONT, " I/O Base HI = [0x%x]\n", + pci_config_get16(config_handle, PCI_BCNF_IO_BASE_HI)); + cmn_err(CE_CONT, " I/O Lim HI = [0x%x]\n", + pci_config_get16(config_handle, PCI_BCNF_IO_LIMIT_HI)); + cmn_err(CE_CONT, " ROM addr = [0x%x]\n", + pci_config_get32(config_handle, PCI_BCNF_ROM)); + cmn_err(CE_CONT, " Intr Line = [0x%x]\n", + pci_config_get8(config_handle, PCI_BCNF_ILINE)); + cmn_err(CE_CONT, " Intr Pin = [0x%x]\n", + pci_config_get8(config_handle, PCI_BCNF_IPIN)); + cmn_err(CE_CONT, " Bridge Ctrl = [0x%x]\n", + pci_config_get16(config_handle, PCI_BCNF_BCNTRL)); +} + +#endif + + +int +_init() +{ + DEBUG0("PCI configurator installed - Fcode Interpretation/21554\n"); + + mutex_init(&pcicfg_list_mutex, NULL, MUTEX_DRIVER, NULL); + return (mod_install(&modlinkage)); +} + +int +_fini(void) +{ + int error; + + error = mod_remove(&modlinkage); + if (error != 0) { + return (error); + } + mutex_destroy(&pcicfg_list_mutex); + return (0); +} + +int +_info(modinfop) +struct modinfo *modinfop; +{ + return (mod_info(&modlinkage, modinfop)); +} + +/* + * given a cap_id, return its cap_id location in config space + */ +static int +pcicfg_get_cap(ddi_acc_handle_t config_handle, uint8_t cap_id) +{ + uint8_t curcap; + uint_t cap_id_loc; + uint16_t status; + int location = -1; + + /* + * Need to check the Status register for ECP support first. + * Also please note that for type 1 devices, the + * offset could change. Should support type 1 next. + */ + status = pci_config_get16(config_handle, PCI_CONF_STAT); + if (!(status & PCI_STAT_CAP)) { + return (-1); + } + cap_id_loc = pci_config_get8(config_handle, PCI_CONF_CAP_PTR); + + /* Walk the list of capabilities */ + while (cap_id_loc) { + + curcap = pci_config_get8(config_handle, cap_id_loc); + + if (curcap == cap_id) { + location = cap_id_loc; + break; + } + cap_id_loc = pci_config_get8(config_handle, + cap_id_loc + 1); + } + return (location); +} + +/*ARGSUSED*/ +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); + num_slots = PCI_CAPSLOT_NSLOTS(esr_reg); + } + /* XXX - need to cover PCI-PCIe bridge with n slots */ + return (num_slots); +} + +/*ARGSUSED*/ +static uint8_t +pcicfg_is_chassis(dev_info_t *dip, ddi_acc_handle_t handle) +{ + int cap_id_loc; + + 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 + 2); + if (PCI_CAPSLOT_FIC(esr_reg)) + return (B_TRUE); + } + return (B_FALSE); +} + +/*ARGSUSED*/ +static int +pcicfg_pcie_dev(dev_info_t *dip, int bus_type, pcicfg_err_regs_t *regs) +{ + /* get parent device's device_type property */ + char *device_type; + int rc = DDI_FAILURE; + dev_info_t *pdip = ddi_get_parent(dip); + + regs->pcie_dev = 0; + if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, + DDI_PROP_DONTPASS, "device_type", &device_type) + != DDI_PROP_SUCCESS) { + DEBUG2("device_type property missing for %s#%d", + ddi_get_name(pdip), ddi_get_instance(pdip)); + return (DDI_FAILURE); + } + switch (bus_type) { + case PCICFG_DEVICE_TYPE_PCIE: + if (strcmp(device_type, "pciex") == 0) { + rc = DDI_SUCCESS; + regs->pcie_dev = 1; + } + break; + case PCICFG_DEVICE_TYPE_PCI: + if (strcmp(device_type, "pci") == 0) + rc = DDI_SUCCESS; + break; + default: + break; + } + ddi_prop_free(device_type); + return (rc); +} + +/*ARGSUSED*/ +static int +pcicfg_pcie_port_type(dev_info_t *dip, ddi_acc_handle_t handle) +{ + int port_type = -1; + int cap_loc; + + if ((cap_loc = pcicfg_get_cap(handle, PCI_CAP_ID_PCI_E)) > 0) + port_type = pci_config_get16(handle, + cap_loc + PCIE_PCIECAP) & PCIE_PCIECAP_DEV_TYPE_MASK; + + return (port_type); +} + +static int +pcicfg_pcie_device_type(dev_info_t *dip, ddi_acc_handle_t handle) +{ + int port_type = pcicfg_pcie_port_type(dip, handle); + + DEBUG1("device port_type = %x\n", port_type); + /* No PCIe CAP regs, we are not PCIe device_type */ + if (port_type < 0) + return (DDI_FAILURE); + + /* check for all PCIe device_types */ + if ((port_type == PCIE_PCIECAP_DEV_TYPE_UP) || + (port_type == PCIE_PCIECAP_DEV_TYPE_DOWN) || + (port_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE)) + return (DDI_SUCCESS); + + return (DDI_FAILURE); + +} + +/* + * In the following functions ndi_devi_enter() without holding the + * parent dip is sufficient. This is because pci dr is driven through + * opens on the nexus which is in the device tree path above the node + * being operated on, and implicitly held due to the open. + */ + +/* + * This entry point is called to configure a device (and + * all its children) on the given bus. It is called when + * a new device is added to the PCI domain. This routine + * will create the device tree and program the devices + * registers. + */ + +int +pcicfg_configure(dev_info_t *devi, uint_t device) +{ + uint_t bus; + int len; + int func; + dev_info_t *new_device; + pcicfg_bus_range_t pci_bus_range; + int rv; + int circ; + uint_t highest_bus = 0; + + /* + * Start probing at the device specified in "device" on the + * "bus" specified. + */ + len = sizeof (pcicfg_bus_range_t); + if (ddi_getlongprop_buf(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, + "bus-range", (caddr_t)&pci_bus_range, &len) != DDI_SUCCESS) { + DEBUG0("no bus-range property\n"); + return (PCICFG_FAILURE); + } + + bus = pci_bus_range.lo; /* primary bus number of this bus node */ + + ndi_devi_enter(devi, &circ); + for (func = 0; func < PCICFG_MAX_FUNCTION; func++) { + + DEBUG3("Configuring [0x%x][0x%x][0x%x]\n", bus, device, func); + + /* + * Try executing fcode if available. + */ + switch (rv = pcicfg_fcode_probe(devi, bus, device, func, + &highest_bus)) { + case PCICFG_FAILURE: + DEBUG2("configure failed: " + "bus [0x%x] device [0x%x]\n", + bus, device); + break; + case PCICFG_NODEVICE: + DEBUG3("no device : bus " + "[0x%x] slot [0x%x] func [0x%x]\n", + bus, device, func); + if (func) + continue; + break; + default: + DEBUG3("configure: bus => [%d] " + "slot => [%d] func => [%d]\n", + bus, device, func); + break; + } + + if (rv != PCICFG_SUCCESS) + break; + + if ((new_device = pcicfg_devi_find(devi, + device, func)) == NULL) { + DEBUG0("Did'nt find device node just created\n"); + goto cleanup; + } + } + ndi_devi_exit(devi, circ); + + if (func == 0) + return (PCICFG_FAILURE); /* probe failed */ + else + return (PCICFG_SUCCESS); + +cleanup: + /* + * Clean up a partially created "probe state" tree. + * There are no resources allocated to the in the + * probe state. + */ + + for (func = 0; func < PCICFG_MAX_FUNCTION; func++) { + if ((new_device = pcicfg_devi_find(devi, + device, func)) == NULL) { + DEBUG0("No more devices to clean up\n"); + continue; + } + + DEBUG2("Cleaning up device [0x%x] function [0x%x]\n", + device, func); + /* + * If this was a bridge device it will have a + * probe handle - if not, no harm in calling this. + */ + (void) pcicfg_destroy_phdl(new_device); + /* + * This will free up the node + */ + (void) ndi_devi_offline(new_device, NDI_DEVI_REMOVE); + } + ndi_devi_exit(devi, circ); + + return (PCICFG_FAILURE); +} + +/* + * configure the child nodes of ntbridge. new_device points to ntbridge itself + */ +/*ARGSUSED*/ +static uint_t +pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device) +{ + int bus_range[2], rc = PCICFG_FAILURE, rc1, max_devs = 0; + int devno; + dev_info_t *new_ntbridgechild; + ddi_acc_handle_t config_handle; + uint16_t vid; + uint64_t next_bus; + uint64_t blen; + ndi_ra_request_t req; + uint8_t pcie_device_type = 0; + + /* + * If we need to do indirect config, lets create a property here + * to let the child conf map routine know that it has to + * go through the DDI calls, and not assume the devices are + * mapped directly under the host. + */ + if ((rc = ndi_prop_update_int(DDI_DEV_T_NONE, new_device, + PCI_DEV_CONF_MAP_PROP, (int)DDI_SUCCESS)) + != DDI_SUCCESS) { + + DEBUG0("Cannot create indirect conf map property.\n"); + return ((int)PCICFG_FAILURE); + } + if (pci_config_setup(new_device, &config_handle) != DDI_SUCCESS) + return (PCICFG_FAILURE); + /* check if we are PCIe device */ + if (pcicfg_pcie_device_type(new_device, config_handle) == DDI_SUCCESS) + pcie_device_type = 1; + pci_config_teardown(&config_handle); + + /* create Bus node properties for ntbridge. */ + if (pcicfg_set_busnode_props(new_device, pcie_device_type) != + PCICFG_SUCCESS) { + DEBUG0("Failed to set busnode props\n"); + return (rc); + } + + /* For now: Lets only support one layer of child */ + bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); + req.ra_len = 1; + if (ndi_ra_alloc(ddi_get_parent(new_device), &req, + &next_bus, &blen, NDI_RA_TYPE_PCI_BUSNUM, + NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("ntbridge: Failed to get a bus number\n"); + return (rc); + } + + DEBUG1("ntbridge bus range start ->[%d]\n", next_bus); + + /* + * Following will change, as we detect more bridges + * on the way. + */ + bus_range[0] = (int)next_bus; + bus_range[1] = (int)next_bus; + + if (ndi_prop_update_int_array(DDI_DEV_T_NONE, new_device, + "bus-range", bus_range, 2) != DDI_SUCCESS) { + DEBUG0("Cannot set ntbridge bus-range property"); + return (rc); + } + + /* + * The other interface (away from the host) will be + * initialized by the nexus driver when it loads. + * We just have to set the registers and the nexus driver + * figures out the rest. + */ + + /* + * finally, lets load and attach the driver + * before configuring children of ntbridge. + */ + rc = ndi_devi_online(new_device, NDI_NO_EVENT|NDI_CONFIG); + if (rc != NDI_SUCCESS) { + cmn_err(CE_WARN, + "pcicfg: Fail:cant load nontransparent bridgd driver..\n"); + rc = PCICFG_FAILURE; + return (rc); + } + DEBUG0("pcicfg: Success loading nontransparent bridge nexus driver.."); + + /* Now set aside pci resources for our children. */ + if (pcicfg_ntbridge_allocate_resources(new_device) != + PCICFG_SUCCESS) { + max_devs = 0; + rc = PCICFG_FAILURE; + } else + max_devs = PCICFG_MAX_DEVICE; + + /* Probe devices on 2nd bus */ + for (devno = pcicfg_start_devno; devno < max_devs; devno++) { + + if (ndi_devi_alloc(new_device, DEVI_PSEUDO_NEXNAME, + (pnode_t)DEVI_SID_NODEID, &new_ntbridgechild) + != NDI_SUCCESS) { + + DEBUG0("pcicfg: Failed to alloc test node\n"); + rc = PCICFG_FAILURE; + break; + } + + if (pcicfg_add_config_reg(new_ntbridgechild, next_bus, devno, 0) + != DDI_PROP_SUCCESS) { + cmn_err(CE_WARN, + "Failed to add conf reg for ntbridge child.\n"); + (void) ndi_devi_free(new_ntbridgechild); + rc = PCICFG_FAILURE; + break; + } + + if ((rc = pci_config_setup(new_ntbridgechild, + &config_handle)) != PCICFG_SUCCESS) { + cmn_err(CE_WARN, + "Cannot map ntbridge child %x\n", devno); + (void) ndi_devi_free(new_ntbridgechild); + rc = PCICFG_FAILURE; + break; + } + + /* + * See if there is any PCI HW at this location + * by reading the Vendor ID. If it returns with 0xffff + * then there is no hardware at this location. + */ + vid = pci_config_get16(config_handle, PCI_CONF_VENID); + + pci_config_teardown(&config_handle); + (void) ndi_devi_free(new_ntbridgechild); + if (vid == 0xffff) + continue; + + /* Lets fake attachments points for each child, */ + if (pcicfg_configure(new_device, devno) != PCICFG_SUCCESS) { + int old_dev = pcicfg_start_devno; + + cmn_err(CE_WARN, + "Error configuring ntbridge child dev=%d\n", devno); + + rc = PCICFG_FAILURE; + while (old_dev != devno) { + if (pcicfg_ntbridge_unconfigure_child( + new_device, old_dev) == PCICFG_FAILURE) + + cmn_err(CE_WARN, + "Unconfig Error ntbridge child " + "dev=%d\n", old_dev); + old_dev++; + } + break; + } + } /* devno loop */ + DEBUG1("ntbridge: finish probing 2nd bus, rc=%d\n", rc); + + if (rc != PCICFG_FAILURE) + rc = pcicfg_ntbridge_configure_done(new_device); + else { + pcicfg_phdl_t *entry = pcicfg_find_phdl(new_device); + uint_t *bus; + int k; + + if (ddi_getlongprop(DDI_DEV_T_ANY, new_device, + DDI_PROP_DONTPASS, "bus-range", (caddr_t)&bus, + &k) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read bus-range property\n"); + rc = PCICFG_FAILURE; + return (rc); + } + + DEBUG2("Need to free bus [%d] range [%d]\n", + bus[0], bus[1] - bus[0] + 1); + + if (ndi_ra_free(ddi_get_parent(new_device), + (uint64_t)bus[0], (uint64_t)(bus[1] - bus[0] + 1), + NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("Failed to free a bus number\n"); + rc = PCICFG_FAILURE; + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)bus, k); + + return (rc); + } + + /* + * Since no memory allocations are done for non transparent + * bridges (but instead we just set the handle with the + * already allocated memory, we just need to reset the + * following values before calling the destroy_phdl() + * function next, otherwise the it will try to free + * memory allocated as in case of a transparent bridge. + */ + entry->memory_len = 0; + entry->io_len = 0; + /* the following will free hole data. */ + (void) pcicfg_destroy_phdl(new_device); + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)bus, k); + } + + /* + * Unload driver just in case child configure failed! + */ + rc1 = ndi_devi_offline(new_device, NDI_NO_EVENT); + DEBUG1("pcicfg: now unloading the ntbridge driver. rc1=%d\n", rc1); + if (rc1 != NDI_SUCCESS) { + cmn_err(CE_WARN, + "pcicfg: cant unload ntbridge driver..children.\n"); + rc = PCICFG_FAILURE; + } + + return (rc); +} + +static int +pcicfg_ntbridge_allocate_resources(dev_info_t *dip) +{ + pcicfg_phdl_t *phdl; + ndi_ra_request_t *mem_request; + ndi_ra_request_t *io_request; + uint64_t boundbase, boundlen; + + phdl = pcicfg_find_phdl(dip); + ASSERT(phdl); + + mem_request = &phdl->mem_req; + io_request = &phdl->io_req; + + phdl->error = PCICFG_SUCCESS; + + /* Set Memory space handle for ntbridge */ + if (pcicfg_get_ntbridge_child_range(dip, &boundbase, &boundlen, + PCI_BASE_SPACE_MEM) != DDI_SUCCESS) { + cmn_err(CE_WARN, + "ntbridge: Mem resource information failure\n"); + phdl->memory_len = 0; + return (PCICFG_FAILURE); + } + mem_request->ra_boundbase = boundbase; + mem_request->ra_boundlen = boundbase + boundlen; + mem_request->ra_len = boundlen; + mem_request->ra_align_mask = + PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */ + mem_request->ra_flags |= NDI_RA_ALLOC_BOUNDED; + + /* + * mem_request->ra_len = + * PCICFG_ROUND_UP(mem_request->ra_len, PCICFG_MEMGRAN); + */ + + phdl->memory_base = phdl->memory_last = boundbase; + phdl->memory_len = boundlen; + phdl->mem_hole.start = phdl->memory_base; + phdl->mem_hole.len = mem_request->ra_len; + phdl->mem_hole.next = (hole_t *)NULL; + + DEBUG2("AP requested [0x%llx], needs [0x%llx] bytes of memory\n", + boundlen, mem_request->ra_len); + + /* set up a memory resource map for NT bridge */ + if (ndi_ra_map_setup(dip, NDI_RA_TYPE_MEM) == NDI_FAILURE) { + DEBUG0("Can not setup ntbridge memory resource map\n"); + return (PCICFG_FAILURE); + } + /* initialize the memory map */ + if (ndi_ra_free(dip, boundbase, boundlen, NDI_RA_TYPE_MEM, + NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("Can not initalize ntbridge memory resource map\n"); + return (PCICFG_FAILURE); + } + /* Set IO space handle for ntbridge */ + if (pcicfg_get_ntbridge_child_range(dip, &boundbase, &boundlen, + PCI_BASE_SPACE_IO) != DDI_SUCCESS) { + cmn_err(CE_WARN, "ntbridge: IO resource information failure\n"); + phdl->io_len = 0; + return (PCICFG_FAILURE); + } + io_request->ra_len = boundlen; + io_request->ra_align_mask = + PCICFG_IOGRAN - 1; /* 4K alignment on I/O space */ + io_request->ra_boundbase = boundbase; + io_request->ra_boundlen = boundbase + boundlen; + io_request->ra_flags |= NDI_RA_ALLOC_BOUNDED; + + /* + * io_request->ra_len = + * PCICFG_ROUND_UP(io_request->ra_len, PCICFG_IOGRAN); + */ + + phdl->io_base = phdl->io_last = (uint32_t)boundbase; + phdl->io_len = (uint32_t)boundlen; + phdl->io_hole.start = phdl->io_base; + phdl->io_hole.len = io_request->ra_len; + phdl->io_hole.next = (hole_t *)NULL; + + DEBUG2("AP requested [0x%llx], needs [0x%llx] bytes of IO\n", + boundlen, io_request->ra_len); + + DEBUG2("MEMORY BASE = [0x%x] length [0x%x]\n", + phdl->memory_base, phdl->memory_len); + DEBUG2("IO BASE = [0x%x] length [0x%x]\n", + phdl->io_base, phdl->io_len); + + /* set up a IO resource map for NT bridge */ + if (ndi_ra_map_setup(dip, NDI_RA_TYPE_IO) == NDI_FAILURE) { + DEBUG0("Can not setup ntbridge memory resource map\n"); + return (PCICFG_FAILURE); + } + /* initialize the IO map */ + if (ndi_ra_free(dip, boundbase, boundlen, NDI_RA_TYPE_IO, + NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("Can not initalize ntbridge memory resource map\n"); + return (PCICFG_FAILURE); + } + + return (PCICFG_SUCCESS); +} + +static int +pcicfg_ntbridge_configure_done(dev_info_t *dip) +{ + pcicfg_range_t range[PCICFG_RANGE_LEN]; + pcicfg_phdl_t *entry; + uint_t len; + pcicfg_bus_range_t bus_range; + int new_bus_range[2]; + + DEBUG1("Configuring children for %llx\n", dip); + + entry = pcicfg_find_phdl(dip); + ASSERT(entry); + + bzero((caddr_t)range, + sizeof (pcicfg_range_t) * PCICFG_RANGE_LEN); + range[1].child_hi = range[1].parent_hi |= + (PCI_REG_REL_M | PCI_ADDR_MEM32); + range[1].child_lo = range[1].parent_lo = (uint32_t)entry->memory_base; + + range[0].child_hi = range[0].parent_hi |= + (PCI_REG_REL_M | PCI_ADDR_IO); + range[0].child_lo = range[0].parent_lo = (uint32_t)entry->io_base; + + len = sizeof (pcicfg_bus_range_t); + if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "bus-range", (caddr_t)&bus_range, (int *)&len) != DDI_SUCCESS) { + DEBUG0("no bus-range property\n"); + return (PCICFG_FAILURE); + } + + new_bus_range[0] = bus_range.lo; /* primary bus number */ + if (entry->highest_bus) { /* secondary bus number */ + if (entry->highest_bus < bus_range.lo) { + cmn_err(CE_WARN, + "ntbridge bus range invalid !(%d,%d)\n", + bus_range.lo, entry->highest_bus); + new_bus_range[1] = bus_range.lo + entry->highest_bus; + } + else + new_bus_range[1] = entry->highest_bus; + } + else + new_bus_range[1] = bus_range.hi; + + DEBUG2("ntbridge: bus range lo=%x, hi=%x\n", + new_bus_range[0], new_bus_range[1]); + + if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, + "bus-range", new_bus_range, 2) != DDI_SUCCESS) { + DEBUG0("Failed to set bus-range property"); + entry->error = PCICFG_FAILURE; + return (PCICFG_FAILURE); + } + +#ifdef DEBUG + { + uint64_t unused; + unused = pcicfg_unused_space(&entry->io_hole, &len); + DEBUG2("ntbridge: Unused IO space %llx bytes over %d holes\n", + unused, len); + } +#endif + + range[0].size_lo = entry->io_len; + if (pcicfg_update_ranges_prop(dip, &range[0])) { + DEBUG0("Failed to update ranges (i/o)\n"); + entry->error = PCICFG_FAILURE; + return (PCICFG_FAILURE); + } + +#ifdef DEBUG + { + uint64_t unused; + unused = pcicfg_unused_space(&entry->mem_hole, &len); + DEBUG2("ntbridge: Unused Mem space %llx bytes over %d holes\n", + unused, len); + } +#endif + + range[1].size_lo = entry->memory_len; + if (pcicfg_update_ranges_prop(dip, &range[1])) { + DEBUG0("Failed to update ranges (memory)\n"); + entry->error = PCICFG_FAILURE; + return (PCICFG_FAILURE); + } + + return (PCICFG_SUCCESS); +} + +static int +pcicfg_ntbridge_unconfigure_child(dev_info_t *new_device, uint_t devno) +{ + + dev_info_t *new_ntbridgechild; + int len, bus; + uint16_t vid; + ddi_acc_handle_t config_handle; + pcicfg_bus_range_t pci_bus_range; + + len = sizeof (pcicfg_bus_range_t); + if (ddi_getlongprop_buf(DDI_DEV_T_ANY, new_device, DDI_PROP_DONTPASS, + "bus-range", (caddr_t)&pci_bus_range, &len) != DDI_SUCCESS) { + DEBUG0("no bus-range property\n"); + return (PCICFG_FAILURE); + } + + bus = pci_bus_range.lo; /* primary bus number of this bus node */ + + if (ndi_devi_alloc(new_device, DEVI_PSEUDO_NEXNAME, + (pnode_t)DEVI_SID_NODEID, &new_ntbridgechild) != NDI_SUCCESS) { + + DEBUG0("pcicfg: Failed to alloc test node\n"); + return (PCICFG_FAILURE); + } + + if (pcicfg_add_config_reg(new_ntbridgechild, bus, devno, 0) + != DDI_PROP_SUCCESS) { + cmn_err(CE_WARN, + "Unconfigure: Failed to add conf reg prop for ntbridge " + "child.\n"); + (void) ndi_devi_free(new_ntbridgechild); + return (PCICFG_FAILURE); + } + + if (pcicfg_config_setup(new_ntbridgechild, &config_handle) + != DDI_SUCCESS) { + cmn_err(CE_WARN, + "pcicfg: Cannot map ntbridge child %x\n", devno); + (void) ndi_devi_free(new_ntbridgechild); + return (PCICFG_FAILURE); + } + + /* + * See if there is any PCI HW at this location + * by reading the Vendor ID. If it returns with 0xffff + * then there is no hardware at this location. + */ + vid = pci_config_get16(config_handle, PCI_CONF_VENID); + + pci_config_teardown(&config_handle); + (void) ndi_devi_free(new_ntbridgechild); + if (vid == 0xffff) + return (PCICFG_NODEVICE); + + return (pcicfg_unconfigure(new_device, devno)); +} + +static int +pcicfg_ntbridge_unconfigure(dev_info_t *dip) +{ + pcicfg_phdl_t *entry = pcicfg_find_phdl(dip); + uint_t *bus; + int k, rc = PCICFG_FAILURE; + + if (entry->memory_len) + if (ndi_ra_map_destroy(dip, NDI_RA_TYPE_MEM) == NDI_FAILURE) { + DEBUG1("cannot destroy ntbridge memory map size=%x\n", + entry->memory_len); + return (PCICFG_FAILURE); + } + if (entry->io_len) + if (ndi_ra_map_destroy(dip, NDI_RA_TYPE_IO) == NDI_FAILURE) { + DEBUG1("cannot destroy ntbridge io map size=%x\n", + entry->io_len); + return (PCICFG_FAILURE); + } + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "bus-range", (caddr_t)&bus, + &k) != DDI_PROP_SUCCESS) { + DEBUG0("ntbridge: Failed to read bus-range property\n"); + return (rc); + } + + DEBUG2("ntbridge: Need to free bus [%d] range [%d]\n", + bus[0], bus[1] - bus[0] + 1); + + if (ndi_ra_free(ddi_get_parent(dip), + (uint64_t)bus[0], (uint64_t)(bus[1] - bus[0] + 1), + NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("ntbridge: Failed to free a bus number\n"); + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)bus, k); + + return (rc); + } + + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)bus, k); + + /* + * Since our resources will be freed at the parent level, + * just reset these values. + */ + entry->memory_len = 0; + entry->io_len = 0; + /* the following will also free hole data. */ + return (pcicfg_destroy_phdl(dip)); + +} + +static int +pcicfg_is_ntbridge(dev_info_t *dip) +{ + ddi_acc_handle_t config_handle; + uint8_t class, subclass; + int rc = DDI_SUCCESS; + + if (pcicfg_config_setup(dip, &config_handle) != DDI_SUCCESS) { + cmn_err(CE_WARN, + "pcicfg: cannot map config space, to get map type\n"); + return (DDI_FAILURE); + } + class = pci_config_get8(config_handle, PCI_CONF_BASCLASS); + subclass = pci_config_get8(config_handle, PCI_CONF_SUBCLASS); + + /* check for class=6, subclass=9, for non transparent bridges. */ + if ((class != PCI_CLASS_BRIDGE) || (subclass != PCI_BRIDGE_STBRIDGE)) + rc = DDI_FAILURE; + + DEBUG3("pcicfg: checking device %x,%x for indirect map. rc=%d\n", + pci_config_get16(config_handle, PCI_CONF_VENID), + pci_config_get16(config_handle, PCI_CONF_DEVID), + rc); + pci_config_teardown(&config_handle); + return (rc); +} + +/* + * this function is called only for SPARC platforms, where we may have + * a mix n' match of direct vs indirectly mapped configuration space. + * On x86, this function does not get called. We always return TRUE + * via a macro for x86. + */ +/*ARGSUSED*/ +static int +pcicfg_indirect_map(dev_info_t *dip) +{ +#if defined(__sparc) + int rc = DDI_FAILURE; + + if (ddi_prop_get_int(DDI_DEV_T_ANY, ddi_get_parent(dip), 0, + PCI_DEV_CONF_MAP_PROP, DDI_FAILURE) != DDI_FAILURE) + rc = DDI_SUCCESS; + else + if (ddi_prop_get_int(DDI_DEV_T_ANY, ddi_get_parent(dip), + 0, PCI_BUS_CONF_MAP_PROP, + DDI_FAILURE) != DDI_FAILURE) + rc = DDI_SUCCESS; + DEBUG1("pci conf map = %d", rc); + return (rc); +#else + return (DDI_SUCCESS); +#endif +} + +static uint_t +pcicfg_get_ntbridge_child_range(dev_info_t *dip, uint64_t *boundbase, + uint64_t *boundlen, uint_t space_type) +{ + int length, found = DDI_FAILURE, acount, i, ibridge; + pci_regspec_t *assigned; + + if ((ibridge = pcicfg_is_ntbridge(dip)) == DDI_FAILURE) + return (found); + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned, + &length) != DDI_PROP_SUCCESS) { + DEBUG1("Failed to get assigned-addresses property %llx\n", dip); + return (found); + } + DEBUG1("pcicfg: ntbridge child range: dip = %s\n", + ddi_driver_name(dip)); + + acount = length / sizeof (pci_regspec_t); + + for (i = 0; i < acount; i++) { + if ((PCI_REG_REG_G(assigned[i].pci_phys_hi) + == pcicfg_indirect_map_devs[ibridge].mem_range_bar_offset) && + (space_type == PCI_BASE_SPACE_MEM)) { + found = DDI_SUCCESS; + break; + } else { + if ((PCI_REG_REG_G(assigned[i].pci_phys_hi) + == pcicfg_indirect_map_devs[ibridge].io_range_bar_offset) && + (space_type == PCI_BASE_SPACE_IO)) { + found = DDI_SUCCESS; + break; + } + } + } + DEBUG3("pcicfg: ntbridge child range: space=%x, base=%lx, len=%lx\n", + space_type, assigned[i].pci_phys_low, assigned[i].pci_size_low); + + if (found == DDI_SUCCESS) { + *boundbase = assigned[i].pci_phys_low; + *boundlen = assigned[i].pci_size_low; + } + + kmem_free(assigned, length); + return (found); +} + +/* + * This will turn resources allocated by pcicfg_configure() + * and remove the device tree from the attachment point + * and below. The routine assumes the devices have their + * drivers detached. + */ +int +pcicfg_unconfigure(dev_info_t *devi, uint_t device) +{ + dev_info_t *child_dip; + int func; + int i; + + /* + * Cycle through devices to make sure none are busy. + * If a single device is busy fail the whole unconfigure. + */ + for (func = 0; func < PCICFG_MAX_FUNCTION; func++) { + if ((child_dip = pcicfg_devi_find(devi, device, func)) == NULL) + continue; + + if (ndi_devi_offline(child_dip, NDI_UNCONFIG) == NDI_SUCCESS) + continue; + /* + * Device function is busy. Before returning we have to + * put all functions back online which were taken + * offline during the process. + */ + DEBUG2("Device [0x%x] function [%x] is busy\n", device, func); + for (i = 0; i < func; i++) { + if ((child_dip = pcicfg_devi_find(devi, device, i)) + == NULL) { + DEBUG0("No more devices to put back on line!!\n"); + /* + * Made it through all functions + */ + continue; + } + if (ndi_devi_online(child_dip, NDI_CONFIG) != NDI_SUCCESS) { + DEBUG0("Failed to put back devices state\n"); + return (PCICFG_FAILURE); + } + } + return (PCICFG_FAILURE); + } + + /* + * Now, tear down all devinfo nodes for this AP. + */ + for (func = 0; func < PCICFG_MAX_FUNCTION; func++) { + if ((child_dip = pcicfg_devi_find(devi, + device, func)) == NULL) { + DEBUG0("No more devices to tear down!\n"); + continue; + } + + DEBUG2("Tearing down device [0x%x] function [0x%x]\n", + device, func); + + if (pcicfg_is_ntbridge(child_dip) != DDI_FAILURE) + if (pcicfg_ntbridge_unconfigure(child_dip) != + PCICFG_SUCCESS) { + cmn_err(CE_WARN, + "ntbridge: unconfigure failed\n"); + return (PCICFG_FAILURE); + } + + if (pcicfg_teardown_device(child_dip) != PCICFG_SUCCESS) { + DEBUG2("Failed to tear down device [0x%x]" + "function [0x%x]\n", + device, func); + return (PCICFG_FAILURE); + } + } + + return (PCICFG_SUCCESS); +} + +static int +pcicfg_teardown_device(dev_info_t *dip) +{ + ddi_acc_handle_t config_handle; + + /* + * Free up resources associated with 'dip' + */ + + if (pcicfg_free_resources(dip) != PCICFG_SUCCESS) { + DEBUG0("Failed to free resources\n"); + return (PCICFG_FAILURE); + } + + /* + * This will disable the device + */ + if (pci_config_setup(dip, &config_handle) != PCICFG_SUCCESS) { + return (PCICFG_FAILURE); + } + + pcicfg_device_off(config_handle); + pci_config_teardown(&config_handle); + + /* + * The framework provides this routine which can + * tear down a sub-tree. + */ + if (ndi_devi_offline(dip, NDI_DEVI_REMOVE) != NDI_SUCCESS) { + DEBUG0("Failed to offline and remove node\n"); + return (PCICFG_FAILURE); + } + + return (PCICFG_SUCCESS); +} + +/* + * BEGIN GENERIC SUPPORT ROUTINES + */ +static pcicfg_phdl_t * +pcicfg_find_phdl(dev_info_t *dip) +{ + pcicfg_phdl_t *entry; + mutex_enter(&pcicfg_list_mutex); + for (entry = pcicfg_phdl_list; entry != NULL; entry = entry->next) { + if (entry->dip == dip) { + mutex_exit(&pcicfg_list_mutex); + return (entry); + } + } + mutex_exit(&pcicfg_list_mutex); + + /* + * Did'nt find entry - create one + */ + return (pcicfg_create_phdl(dip)); +} + +static pcicfg_phdl_t * +pcicfg_create_phdl(dev_info_t *dip) +{ + pcicfg_phdl_t *new; + + new = (pcicfg_phdl_t *)kmem_zalloc(sizeof (pcicfg_phdl_t), + KM_SLEEP); + + new->dip = dip; + mutex_enter(&pcicfg_list_mutex); + new->next = pcicfg_phdl_list; + pcicfg_phdl_list = new; + mutex_exit(&pcicfg_list_mutex); + + return (new); +} + +static int +pcicfg_destroy_phdl(dev_info_t *dip) +{ + pcicfg_phdl_t *entry; + pcicfg_phdl_t *follow = NULL; + + mutex_enter(&pcicfg_list_mutex); + for (entry = pcicfg_phdl_list; entry != NULL; follow = entry, + entry = entry->next) { + if (entry->dip == dip) { + if (entry == pcicfg_phdl_list) { + pcicfg_phdl_list = entry->next; + } else { + follow->next = entry->next; + } + /* + * If this entry has any allocated memory + * or IO space associated with it, that + * must be freed up. + */ + if (entry->memory_len > 0) { + (void) ndi_ra_free(ddi_get_parent(dip), + entry->memory_base, + entry->memory_len, + NDI_RA_TYPE_MEM, NDI_RA_PASS); + } + pcicfg_free_hole(&entry->mem_hole); + + if (entry->io_len > 0) { + (void) ndi_ra_free(ddi_get_parent(dip), + entry->io_base, + entry->io_len, + NDI_RA_TYPE_IO, NDI_RA_PASS); + } + pcicfg_free_hole(&entry->io_hole); + + /* + * Destroy this entry + */ + kmem_free((caddr_t)entry, sizeof (pcicfg_phdl_t)); + mutex_exit(&pcicfg_list_mutex); + return (PCICFG_SUCCESS); + } + } + mutex_exit(&pcicfg_list_mutex); + /* + * Did'nt find the entry + */ + return (PCICFG_FAILURE); +} + +static int +pcicfg_program_ap(dev_info_t *dip) +{ + pcicfg_phdl_t *phdl; + uint8_t header_type; + ddi_acc_handle_t handle; + pcicfg_phdl_t *entry; + + if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) { + DEBUG0("Failed to map config space!\n"); + return (PCICFG_FAILURE); + + } + + header_type = pci_config_get8(handle, PCI_CONF_HEADER); + + (void) pcicfg_config_teardown(&handle); + + if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { + + if (pcicfg_allocate_chunk(dip) != PCICFG_SUCCESS) { + DEBUG0("Not enough memory to hotplug\n"); + (void) pcicfg_destroy_phdl(dip); + return (PCICFG_FAILURE); + } + + phdl = pcicfg_find_phdl(dip); + ASSERT(phdl); + + (void) pcicfg_bridge_assign(dip, (void *)phdl); + + if (phdl->error != PCICFG_SUCCESS) { + DEBUG0("Problem assigning bridge\n"); + (void) pcicfg_destroy_phdl(dip); + return (phdl->error); + } + + /* + * Successfully allocated and assigned + * memory. Set the memory and IO length + * to zero so when the handle is freed up + * it will not de-allocate assigned resources. + */ + entry = (pcicfg_phdl_t *)phdl; + + entry->memory_len = entry->io_len = 0; + + /* + * Free up the "entry" structure. + */ + (void) pcicfg_destroy_phdl(dip); + } else { + if (pcicfg_device_assign(dip) != PCICFG_SUCCESS) { + return (PCICFG_FAILURE); + } + } + return (PCICFG_SUCCESS); +} + +static int +pcicfg_bridge_assign(dev_info_t *dip, void *hdl) +{ + ddi_acc_handle_t handle; + pci_regspec_t *reg; + int length; + int rcount; + int i; + int offset; + uint64_t mem_answer; + uint32_t io_answer; + int count; + uint8_t header_type; + pcicfg_range_t range[PCICFG_RANGE_LEN]; + int bus_range[2]; + + pcicfg_phdl_t *entry = (pcicfg_phdl_t *)hdl; + + DEBUG1("bridge assign: assigning addresses to %s\n", ddi_get_name(dip)); + + if (entry == NULL) { + DEBUG0("Failed to get entry\n"); + return (DDI_WALK_TERMINATE); + } + + entry->error = PCICFG_SUCCESS; + + if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) { + DEBUG0("Failed to map config space!\n"); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } + + header_type = pci_config_get8(handle, PCI_CONF_HEADER); + + if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { + + bzero((caddr_t)range, + sizeof (pcicfg_range_t) * PCICFG_RANGE_LEN); + + (void) pcicfg_setup_bridge(entry, handle); + + range[0].child_hi = range[0].parent_hi |= + (PCI_REG_REL_M | PCI_ADDR_IO); + range[0].child_lo = range[0].parent_lo = + entry->io_last; + range[1].child_hi = range[1].parent_hi |= + (PCI_REG_REL_M | PCI_ADDR_MEM32); + range[1].child_lo = range[1].parent_lo = + entry->memory_last; + + ndi_devi_enter(dip, &count); + ddi_walk_devs(ddi_get_child(dip), + pcicfg_bridge_assign, (void *)entry); + ndi_devi_exit(dip, count); + + (void) pcicfg_update_bridge(entry, handle); + + bus_range[0] = pci_config_get8(handle, PCI_BCNF_SECBUS); + bus_range[1] = pci_config_get8(handle, PCI_BCNF_SUBBUS); + + if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, + "bus-range", bus_range, 2) != DDI_SUCCESS) { + DEBUG0("Failed to set bus-range property"); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } + + if (entry->io_len > 0) { + range[0].size_lo = entry->io_last - entry->io_base; + if (pcicfg_update_ranges_prop(dip, &range[0])) { + DEBUG0("Failed to update ranges (i/o)\n"); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } + } + if (entry->memory_len > 0) { + range[1].size_lo = + entry->memory_last - entry->memory_base; + if (pcicfg_update_ranges_prop(dip, &range[1])) { + DEBUG0("Failed to update ranges (memory)\n"); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } + } + + (void) pcicfg_device_on(handle); + + PCICFG_DUMP_BRIDGE_CONFIG(handle); + + return (DDI_WALK_PRUNECHILD); + } + + /* + * If there is an interrupt pin set program + * interrupt line with default values. + */ + if (pci_config_get8(handle, PCI_CONF_IPIN)) { + pci_config_put8(handle, PCI_CONF_ILINE, 0xf); + } + + /* + * A single device (under a bridge). + * For each "reg" property with a length, allocate memory + * and program the base registers. + */ + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "reg", (caddr_t)®, + &length) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read reg property\n"); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } + + rcount = length / sizeof (pci_regspec_t); + offset = PCI_CONF_BASE0; + for (i = 0; i < rcount; i++) { + if ((reg[i].pci_size_low != 0)|| + (reg[i].pci_size_hi != 0)) { + + offset = PCI_REG_REG_G(reg[i].pci_phys_hi); + + switch (PCI_REG_ADDR_G(reg[i].pci_phys_hi)) { + case PCI_REG_ADDR_G(PCI_ADDR_MEM64): + + (void) pcicfg_get_mem(entry, + reg[i].pci_size_low, &mem_answer); + pci_config_put64(handle, offset, mem_answer); + DEBUG2("REGISTER off %x (64)LO ----> [0x%x]\n", + offset, + pci_config_get32(handle, offset)); + DEBUG2("REGISTER off %x (64)HI ----> [0x%x]\n", + offset + 4, + pci_config_get32(handle, offset + 4)); + + reg[i].pci_phys_low = PCICFG_HIADDR(mem_answer); + reg[i].pci_phys_mid = + PCICFG_LOADDR(mem_answer); + + break; + + case PCI_REG_ADDR_G(PCI_ADDR_MEM32): + /* allocate memory space from the allocator */ + + (void) pcicfg_get_mem(entry, + reg[i].pci_size_low, &mem_answer); + pci_config_put32(handle, + offset, (uint32_t)mem_answer); + + DEBUG2("REGISTER off %x(32)LO ----> [0x%x]\n", + offset, + pci_config_get32(handle, offset)); + + reg[i].pci_phys_low = (uint32_t)mem_answer; + + break; + case PCI_REG_ADDR_G(PCI_ADDR_IO): + /* allocate I/O space from the allocator */ + + (void) pcicfg_get_io(entry, + reg[i].pci_size_low, &io_answer); + pci_config_put32(handle, offset, io_answer); + + DEBUG2("REGISTER off %x (I/O)LO ----> [0x%x]\n", + offset, + pci_config_get32(handle, offset)); + + reg[i].pci_phys_low = io_answer; + + break; + default: + DEBUG0("Unknown register type\n"); + kmem_free(reg, length); + (void) pcicfg_config_teardown(&handle); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } /* switch */ + + /* + * Now that memory locations are assigned, + * update the assigned address property. + */ + if (pcicfg_update_assigned_prop(dip, + ®[i]) != PCICFG_SUCCESS) { + kmem_free(reg, length); + (void) pcicfg_config_teardown(&handle); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } + } + } + (void) pcicfg_device_on(handle); + + PCICFG_DUMP_DEVICE_CONFIG(handle); + + (void) pcicfg_config_teardown(&handle); + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)reg, length); + + return (DDI_WALK_CONTINUE); +} +static int +pcicfg_device_assign(dev_info_t *dip) +{ + ddi_acc_handle_t handle; + pci_regspec_t *reg; + int length; + int rcount; + int i; + int offset; + ndi_ra_request_t request; + uint64_t answer; + uint64_t alen; + + + DEBUG1("%llx now under configuration\n", dip); + + /* + * XXX Failure here should be noted + */ + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "reg", (caddr_t)®, + &length) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read reg property\n"); + return (PCICFG_FAILURE); + } + + if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) { + DEBUG0("Failed to map config space!\n"); + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)reg, length); + + return (PCICFG_FAILURE); + } + + /* + * A single device + * + * For each "reg" property with a length, allocate memory + * and program the base registers. + */ + + /* + * If there is an interrupt pin set program + * interrupt line with default values. + */ + if (pci_config_get8(handle, PCI_CONF_IPIN)) { + pci_config_put8(handle, PCI_CONF_ILINE, 0xf); + } + + bzero((caddr_t)&request, sizeof (ndi_ra_request_t)); + + request.ra_flags |= NDI_RA_ALIGN_SIZE; + request.ra_boundbase = 0; + request.ra_boundlen = PCICFG_4GIG_LIMIT; + + rcount = length / sizeof (pci_regspec_t); + offset = PCI_CONF_BASE0; + for (i = 0; i < rcount; i++) { + if ((reg[i].pci_size_low != 0)|| + (reg[i].pci_size_hi != 0)) { + + offset = PCI_REG_REG_G(reg[i].pci_phys_hi); + request.ra_len = reg[i].pci_size_low; + + switch (PCI_REG_ADDR_G(reg[i].pci_phys_hi)) { + case PCI_REG_ADDR_G(PCI_ADDR_MEM64): + request.ra_flags ^= NDI_RA_ALLOC_BOUNDED; + /* allocate memory space from the allocator */ + if (ndi_ra_alloc(ddi_get_parent(dip), + &request, &answer, &alen, + NDI_RA_TYPE_MEM, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("Failed to allocate 64b mem\n"); + kmem_free(reg, length); + (void) pcicfg_config_teardown(&handle); + return (PCICFG_FAILURE); + } + DEBUG3("64 addr = [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(answer), + PCICFG_LOADDR(answer), + alen); + /* program the low word */ + pci_config_put32(handle, + offset, PCICFG_LOADDR(answer)); + + /* program the high word with value zero */ + pci_config_put32(handle, offset + 4, + PCICFG_HIADDR(answer)); + + reg[i].pci_phys_low = PCICFG_LOADDR(answer); + reg[i].pci_phys_mid = PCICFG_HIADDR(answer); + /* + * currently support 32b address space + * assignments only. + */ + reg[i].pci_phys_hi ^= PCI_ADDR_MEM64 ^ + PCI_ADDR_MEM32; + + offset += 8; + break; + + case PCI_REG_ADDR_G(PCI_ADDR_MEM32): + request.ra_flags |= NDI_RA_ALLOC_BOUNDED; + /* allocate memory space from the allocator */ + if (ndi_ra_alloc(ddi_get_parent(dip), + &request, &answer, &alen, + NDI_RA_TYPE_MEM, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("Failed to allocate 32b mem\n"); + kmem_free(reg, length); + (void) pcicfg_config_teardown(&handle); + return (PCICFG_FAILURE); + } + DEBUG3("32 addr = [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(answer), + PCICFG_LOADDR(answer), + alen); + /* program the low word */ + pci_config_put32(handle, + offset, PCICFG_LOADDR(answer)); + + reg[i].pci_phys_low = PCICFG_LOADDR(answer); + + offset += 4; + break; + case PCI_REG_ADDR_G(PCI_ADDR_IO): + /* allocate I/O space from the allocator */ + request.ra_flags |= NDI_RA_ALLOC_BOUNDED; + if (ndi_ra_alloc(ddi_get_parent(dip), + &request, &answer, &alen, + NDI_RA_TYPE_IO, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("Failed to allocate I/O\n"); + kmem_free(reg, length); + (void) pcicfg_config_teardown(&handle); + return (PCICFG_FAILURE); + } + DEBUG3("I/O addr = [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(answer), + PCICFG_LOADDR(answer), + alen); + pci_config_put32(handle, + offset, PCICFG_LOADDR(answer)); + + reg[i].pci_phys_low = PCICFG_LOADDR(answer); + + offset += 4; + break; + default: + DEBUG0("Unknown register type\n"); + kmem_free(reg, length); + (void) pcicfg_config_teardown(&handle); + return (PCICFG_FAILURE); + } /* switch */ + + /* + * Now that memory locations are assigned, + * update the assigned address property. + */ + + if (pcicfg_update_assigned_prop(dip, + ®[i]) != PCICFG_SUCCESS) { + kmem_free(reg, length); + (void) pcicfg_config_teardown(&handle); + return (PCICFG_FAILURE); + } + } + } + + (void) pcicfg_device_on(handle); + kmem_free(reg, length); + + PCICFG_DUMP_DEVICE_CONFIG(handle); + + (void) pcicfg_config_teardown(&handle); + return (PCICFG_SUCCESS); +} + +/* + * The "dip" passed to this routine is assumed to be + * the device at the attachment point. Currently it is + * assumed to be a bridge. + */ +static int +pcicfg_allocate_chunk(dev_info_t *dip) +{ + pcicfg_phdl_t *phdl; + ndi_ra_request_t *mem_request; + ndi_ra_request_t *io_request; + uint64_t mem_answer; + uint64_t io_answer; + int count; + uint64_t alen; + + /* + * This should not find an existing entry - so + * it will create a new one. + */ + phdl = pcicfg_find_phdl(dip); + ASSERT(phdl); + + mem_request = &phdl->mem_req; + io_request = &phdl->io_req; + + /* + * From this point in the tree - walk the devices, + * The function passed in will read and "sum" up + * the memory and I/O requirements and put them in + * structure "phdl". + */ + ndi_devi_enter(ddi_get_parent(dip), &count); + ddi_walk_devs(dip, pcicfg_sum_resources, (void *)phdl); + ndi_devi_exit(ddi_get_parent(dip), count); + + if (phdl->error != PCICFG_SUCCESS) { + DEBUG0("Failure summing resources\n"); + return (phdl->error); + } + + /* + * Call into the memory allocator with the request. + * Record the addresses returned in the phdl + */ + DEBUG1("AP requires [0x%x] bytes of memory space\n", + mem_request->ra_len); + DEBUG1("AP requires [0x%x] bytes of I/O space\n", + io_request->ra_len); + + mem_request->ra_align_mask = + PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */ + io_request->ra_align_mask = + PCICFG_IOGRAN - 1; /* 4K alignment on I/O space */ + io_request->ra_boundbase = 0; + io_request->ra_boundlen = PCICFG_4GIG_LIMIT; + io_request->ra_flags |= NDI_RA_ALLOC_BOUNDED; + + mem_request->ra_len = + PCICFG_ROUND_UP(mem_request->ra_len, PCICFG_MEMGRAN); + + io_request->ra_len = + PCICFG_ROUND_UP(io_request->ra_len, PCICFG_IOGRAN); + + if (ndi_ra_alloc(ddi_get_parent(dip), + mem_request, &mem_answer, &alen, + NDI_RA_TYPE_MEM, NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("Failed to allocate memory\n"); + return (PCICFG_FAILURE); + } + + phdl->memory_base = phdl->memory_last = mem_answer; + phdl->memory_len = alen; + + phdl->mem_hole.start = phdl->memory_base; + phdl->mem_hole.len = phdl->memory_len; + phdl->mem_hole.next = (hole_t *)NULL; + + if (ndi_ra_alloc(ddi_get_parent(dip), io_request, &io_answer, + &alen, NDI_RA_TYPE_IO, NDI_RA_PASS) != NDI_SUCCESS) { + + DEBUG0("Failed to allocate I/O space\n"); + (void) ndi_ra_free(ddi_get_parent(dip), mem_answer, + alen, NDI_RA_TYPE_MEM, NDI_RA_PASS); + phdl->memory_len = phdl->io_len = 0; + return (PCICFG_FAILURE); + } + + phdl->io_base = phdl->io_last = (uint32_t)io_answer; + phdl->io_len = (uint32_t)alen; + + phdl->io_hole.start = phdl->io_base; + phdl->io_hole.len = phdl->io_len; + phdl->io_hole.next = (hole_t *)NULL; + + DEBUG2("MEMORY BASE = [0x%x] length [0x%x]\n", + phdl->memory_base, phdl->memory_len); + DEBUG2("IO BASE = [0x%x] length [0x%x]\n", + phdl->io_base, phdl->io_len); + + return (PCICFG_SUCCESS); +} + +#ifdef DEBUG +/* + * This function is useful in debug mode, where we can measure how + * much memory was wasted/unallocated in bridge device's domain. + */ +static uint64_t +pcicfg_unused_space(hole_t *hole, uint32_t *hole_count) +{ + uint64_t len = 0; + uint32_t count = 0; + + do { + len += hole->len; + hole = hole->next; + count++; + } while (hole); + *hole_count = count; + return (len); +} +#endif + +/* + * This function frees data structures that hold the hole information + * which are allocated in pcicfg_alloc_hole(). This is not freeing + * any memory allocated through NDI calls. + */ +static void +pcicfg_free_hole(hole_t *addr_hole) +{ + hole_t *nhole, *hole = addr_hole->next; + + while (hole) { + nhole = hole->next; + kmem_free(hole, sizeof (hole_t)); + hole = nhole; + } +} + +static uint64_t +pcicfg_alloc_hole(hole_t *addr_hole, uint64_t *alast, uint32_t length) +{ + uint64_t actual_hole_start, ostart, olen; + hole_t *hole = addr_hole, *thole, *nhole; + + do { + actual_hole_start = PCICFG_ROUND_UP(hole->start, length); + if (((actual_hole_start - hole->start) + length) <= hole->len) { + DEBUG3("hole found. start %llx, len %llx, req=%x\n", + hole->start, hole->len, length); + ostart = hole->start; + olen = hole->len; + /* current hole parameters adjust */ + if ((actual_hole_start - hole->start) == 0) { + hole->start += length; + hole->len -= length; + if (hole->start > *alast) + *alast = hole->start; + } else { + hole->len = actual_hole_start - hole->start; + nhole = (hole_t *)kmem_zalloc(sizeof (hole_t), + KM_SLEEP); + nhole->start = actual_hole_start + length; + nhole->len = (ostart + olen) - nhole->start; + nhole->next = NULL; + thole = hole->next; + hole->next = nhole; + nhole->next = thole; + if (nhole->start > *alast) + *alast = nhole->start; + DEBUG2("put new hole to %llx, %llx\n", + nhole->start, nhole->len); + } + DEBUG2("adjust current hole to %llx, %llx\n", + hole->start, hole->len); + break; + } + actual_hole_start = 0; + hole = hole->next; + } while (hole); + + DEBUG1("return hole at %llx\n", actual_hole_start); + return (actual_hole_start); +} + +static void +pcicfg_get_mem(pcicfg_phdl_t *entry, + uint32_t length, uint64_t *ans) +{ + uint64_t new_mem; + + /* See if there is a hole, that can hold this request. */ + new_mem = pcicfg_alloc_hole(&entry->mem_hole, &entry->memory_last, + length); + if (new_mem) { /* if non-zero, found a hole. */ + if (ans != NULL) + *ans = new_mem; + } else + cmn_err(CE_WARN, "No %u bytes memory window for %s\n", + length, ddi_get_name(entry->dip)); +} + +static void +pcicfg_get_io(pcicfg_phdl_t *entry, + uint32_t length, uint32_t *ans) +{ + uint32_t new_io; + uint64_t io_last; + + /* + * See if there is a hole, that can hold this request. + * Pass 64 bit parameters and then truncate to 32 bit. + */ + io_last = entry->io_last; + new_io = (uint32_t)pcicfg_alloc_hole(&entry->io_hole, &io_last, length); + if (new_io) { /* if non-zero, found a hole. */ + entry->io_last = (uint32_t)io_last; + if (ans != NULL) + *ans = new_io; + } else + cmn_err(CE_WARN, "No %u bytes IO space window for %s\n", + length, ddi_get_name(entry->dip)); +} + +static int +pcicfg_sum_resources(dev_info_t *dip, void *hdl) +{ + pcicfg_phdl_t *entry = (pcicfg_phdl_t *)hdl; + pci_regspec_t *pci_rp; + int length; + int rcount; + int i; + ndi_ra_request_t *mem_request; + ndi_ra_request_t *io_request; + uint8_t header_type; + ddi_acc_handle_t handle; + + entry->error = PCICFG_SUCCESS; + + mem_request = &entry->mem_req; + io_request = &entry->io_req; + + if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) { + DEBUG0("Failed to map config space!\n"); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } + + header_type = pci_config_get8(handle, PCI_CONF_HEADER); + + /* + * If its a bridge - just record the highest bus seen + */ + if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { + + if (entry->highest_bus < pci_config_get8(handle, + PCI_BCNF_SECBUS)) { + entry->highest_bus = + pci_config_get8(handle, PCI_BCNF_SECBUS); + } + + (void) pcicfg_config_teardown(&handle); + entry->error = PCICFG_FAILURE; + return (DDI_WALK_CONTINUE); + } else { + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "reg", (caddr_t)&pci_rp, + &length) != DDI_PROP_SUCCESS) { + /* + * If one node in (the subtree of nodes) + * does'nt have a "reg" property fail the + * allocation. + */ + entry->memory_len = 0; + entry->io_len = 0; + entry->error = PCICFG_FAILURE; + return (DDI_WALK_TERMINATE); + } + /* + * For each "reg" property with a length, add that to the + * total memory (or I/O) to allocate. + */ + rcount = length / sizeof (pci_regspec_t); + + for (i = 0; i < rcount; i++) { + + switch (PCI_REG_ADDR_G(pci_rp[i].pci_phys_hi)) { + + case PCI_REG_ADDR_G(PCI_ADDR_MEM32): + mem_request->ra_len = + pci_rp[i].pci_size_low + + PCICFG_ROUND_UP(mem_request->ra_len, + pci_rp[i].pci_size_low); + DEBUG1("ADDING 32 --->0x%x\n", + pci_rp[i].pci_size_low); + + break; + case PCI_REG_ADDR_G(PCI_ADDR_MEM64): + mem_request->ra_len = + pci_rp[i].pci_size_low + + PCICFG_ROUND_UP(mem_request->ra_len, + pci_rp[i].pci_size_low); + DEBUG1("ADDING 64 --->0x%x\n", + pci_rp[i].pci_size_low); + + break; + case PCI_REG_ADDR_G(PCI_ADDR_IO): + io_request->ra_len = + pci_rp[i].pci_size_low + + PCICFG_ROUND_UP(io_request->ra_len, + pci_rp[i].pci_size_low); + DEBUG1("ADDING I/O --->0x%x\n", + pci_rp[i].pci_size_low); + break; + default: + /* Config space register - not included */ + break; + } + } + + /* + * free the memory allocated by ddi_getlongprop + */ + kmem_free(pci_rp, length); + + /* + * continue the walk to the next sibling to sum memory + */ + + (void) pcicfg_config_teardown(&handle); + + return (DDI_WALK_CONTINUE); + } +} + +static int +pcicfg_find_resource_end(dev_info_t *dip, void *hdl) +{ + pcicfg_phdl_t *entry = (pcicfg_phdl_t *)hdl; + pci_regspec_t *pci_ap; + int length; + int rcount; + int i; + + entry->error = PCICFG_SUCCESS; + + if (dip == entry->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); + } + + 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, + 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; + } + } + + /* + * 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 +pcicfg_free_bridge_resources(dev_info_t *dip) +{ + pcicfg_range_t *ranges; + uint_t *bus; + int k; + int length; + int i; + + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "ranges", (caddr_t)&ranges, + &length) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read ranges property\n"); + if (ddi_get_child(dip)) { + cmn_err(CE_WARN, "No ranges property found for %s", + ddi_get_name(dip)); + /* + * strictly speaking, we can check for children with + * assigned-addresses but for now it is better to + * be conservative and assume that if there are child + * nodes, then they do consume PCI memory or IO + * resources, Hence return failure. + */ + return (PCICFG_FAILURE); + } + length = 0; + + } + + for (i = 0; i < length / sizeof (pcicfg_range_t); i++) { + if (ranges[i].size_lo != 0 || + ranges[i].size_hi != 0) { + switch (ranges[i].parent_hi & PCI_REG_ADDR_M) { + case PCI_ADDR_IO: + DEBUG2("Free I/O " + "base/length = [0x%x]/[0x%x]\n", + ranges[i].child_lo, + ranges[i].size_lo); + if (ndi_ra_free(ddi_get_parent(dip), + (uint64_t)ranges[i].child_lo, + (uint64_t)ranges[i].size_lo, + NDI_RA_TYPE_IO, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("Trouble freeing " + "PCI i/o space\n"); + kmem_free(ranges, length); + return (PCICFG_FAILURE); + } + break; + case PCI_ADDR_MEM32: + case PCI_ADDR_MEM64: + DEBUG3("Free Memory base/length = " + "[0x%x.%x]/[0x%x]\n", + ranges[i].child_mid, + ranges[i].child_lo, + ranges[i].size_lo) + if (ndi_ra_free(ddi_get_parent(dip), + PCICFG_LADDR(ranges[i].child_lo, + ranges[i].child_mid), + (uint64_t)ranges[i].size_lo, + NDI_RA_TYPE_MEM, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("Trouble freeing " + "PCI memory space\n"); + kmem_free(ranges, length); + return (PCICFG_FAILURE); + } + break; + default: + DEBUG0("Unknown memory space\n"); + break; + } + } + } + + if (length) + kmem_free(ranges, length); + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "bus-range", (caddr_t)&bus, + &k) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read bus-range property\n"); + return (PCICFG_FAILURE); + } + + DEBUG2("Need to free bus [%d] range [%d]\n", + bus[0], bus[1] - bus[0] + 1); + + if (ndi_ra_free(ddi_get_parent(dip), + (uint64_t)bus[0], (uint64_t)(bus[1] - bus[0] + 1), + NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) { + /*EMPTY*/ + DEBUG0("Failed to free a bus number\n"); + } + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)bus, k); + + return (PCICFG_SUCCESS); +} + +static int +pcicfg_free_device_resources(dev_info_t *dip) +{ + pci_regspec_t *assigned; + + int length; + int acount; + int i; + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned, + &length) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read assigned-addresses property\n"); + return (PCICFG_FAILURE); + } + + /* + * For each "assigned-addresses" property entry with a length, + * call the memory allocation routines to return the + * resource. + */ + acount = length / sizeof (pci_regspec_t); + for (i = 0; i < acount; i++) { + /* + * Workaround for Devconf (x86) bug to skip extra entries + * beyond the PCI_CONF_BASE5 offset. But we want to free up + * any memory for expansion roms if allocated. + */ + if ((PCI_REG_REG_G(assigned[i].pci_phys_hi) > PCI_CONF_BASE5) && + (PCI_REG_REG_G(assigned[i].pci_phys_hi) != PCI_CONF_ROM)) + break; + + if (pcicfg_free_resource(dip, assigned[i])) { + DEBUG1("pcicfg_free_device_resources - Trouble freeing " + "%x\n", assigned[i].pci_phys_hi); + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)assigned, length); + + return (PCICFG_FAILURE); + } + } + kmem_free(assigned, length); + return (PCICFG_SUCCESS); +} + +static int +pcicfg_free_resources(dev_info_t *dip) +{ + ddi_acc_handle_t handle; + uint8_t header_type; + + if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) { + DEBUG0("Failed to map config space!\n"); + return (PCICFG_FAILURE); + } + + header_type = pci_config_get8(handle, PCI_CONF_HEADER); + + (void) pci_config_teardown(&handle); + + /* + * A different algorithm is used for bridges and leaf devices. + */ + if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { + if (pcicfg_free_bridge_resources(dip) != PCICFG_SUCCESS) { + DEBUG0("Failed freeing up bridge resources\n"); + return (PCICFG_FAILURE); + } + } else { + if (pcicfg_free_device_resources(dip) != PCICFG_SUCCESS) { + DEBUG0("Failed freeing up device resources\n"); + return (PCICFG_FAILURE); + } + } + return (PCICFG_SUCCESS); +} + +#ifndef _DONT_USE_1275_GENERIC_NAMES +static char * +pcicfg_get_class_name(uint32_t classcode) +{ + struct pcicfg_name_entry *ptr; + + for (ptr = &pcicfg_class_lookup[0]; ptr->name != NULL; ptr++) { + if (ptr->class_code == classcode) { + return (ptr->name); + } + } + return (NULL); +} +#endif /* _DONT_USE_1275_GENERIC_NAMES */ + +static dev_info_t * +pcicfg_devi_find(dev_info_t *dip, uint_t device, uint_t function) +{ + struct pcicfg_find_ctrl ctrl; + int count; + + ctrl.device = device; + ctrl.function = function; + ctrl.dip = NULL; + + ndi_devi_enter(dip, &count); + ddi_walk_devs(ddi_get_child(dip), pcicfg_match_dev, (void *)&ctrl); + ndi_devi_exit(dip, count); + + return (ctrl.dip); +} + +static int +pcicfg_match_dev(dev_info_t *dip, void *hdl) +{ + struct pcicfg_find_ctrl *ctrl = (struct pcicfg_find_ctrl *)hdl; + pci_regspec_t *pci_rp; + int length; + int pci_dev; + int pci_func; + + if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, + (uint_t *)&length) != DDI_PROP_SUCCESS) { + ctrl->dip = NULL; + return (DDI_WALK_TERMINATE); + } + + /* get the PCI device address info */ + pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); + pci_func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); + + /* + * free the memory allocated by ddi_prop_lookup_int_array + */ + ddi_prop_free(pci_rp); + + + if ((pci_dev == ctrl->device) && (pci_func == ctrl->function)) { + /* found the match for the specified device address */ + ctrl->dip = dip; + return (DDI_WALK_TERMINATE); + } + + /* + * continue the walk to the next sibling to look for a match. + */ + return (DDI_WALK_PRUNECHILD); +} + +static int +pcicfg_update_assigned_prop(dev_info_t *dip, pci_regspec_t *newone) +{ + int alen; + pci_regspec_t *assigned; + caddr_t newreg; + uint_t status; + + DEBUG0("pcicfg_update_assigned_prop()\n"); + status = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "assigned-addresses", (caddr_t)&assigned, &alen); + switch (status) { + case DDI_PROP_SUCCESS: + break; + case DDI_PROP_NO_MEMORY: + DEBUG0("no memory for assigned-addresses property\n"); + return (PCICFG_FAILURE); + default: + (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, + "assigned-addresses", (int *)newone, + sizeof (*newone)/sizeof (int)); + + (void) pcicfg_dump_assigned(dip); + + return (PCICFG_SUCCESS); + } + + /* + * Allocate memory for the existing + * assigned-addresses(s) plus one and then + * build it. + */ + + newreg = kmem_zalloc(alen+sizeof (*newone), KM_SLEEP); + + bcopy(assigned, newreg, alen); + bcopy(newone, newreg + alen, sizeof (*newone)); + + /* + * Write out the new "assigned-addresses" spec + */ + (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, + "assigned-addresses", (int *)newreg, + (alen + sizeof (*newone))/sizeof (int)); + + kmem_free((caddr_t)newreg, alen+sizeof (*newone)); + + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)assigned, alen); + + (void) pcicfg_dump_assigned(dip); + + return (PCICFG_SUCCESS); +} +static int +pcicfg_update_ranges_prop(dev_info_t *dip, pcicfg_range_t *addition) +{ + int rlen; + pcicfg_range_t *ranges; + caddr_t newreg; + uint_t status; + + status = ddi_getlongprop(DDI_DEV_T_ANY, + dip, DDI_PROP_DONTPASS, "ranges", (caddr_t)&ranges, &rlen); + + + switch (status) { + case DDI_PROP_SUCCESS: + break; + case DDI_PROP_NO_MEMORY: + DEBUG0("ranges present, but unable to get memory\n"); + return (PCICFG_FAILURE); + default: + DEBUG0("no ranges property - creating one\n"); + if (ndi_prop_update_int_array(DDI_DEV_T_NONE, + dip, "ranges", (int *)addition, + sizeof (pcicfg_range_t)/sizeof (int)) + != DDI_SUCCESS) { + DEBUG0("Did'nt create ranges property\n"); + return (PCICFG_FAILURE); + } + return (PCICFG_SUCCESS); + } + + /* + * Allocate memory for the existing reg(s) plus one and then + * build it. + */ + newreg = kmem_zalloc(rlen+sizeof (pcicfg_range_t), KM_SLEEP); + + bcopy(ranges, newreg, rlen); + bcopy(addition, newreg + rlen, sizeof (pcicfg_range_t)); + + /* + * Write out the new "ranges" property + */ + (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, + dip, "ranges", (int *)newreg, + (rlen + sizeof (pcicfg_range_t))/sizeof (int)); + + kmem_free((caddr_t)newreg, rlen+sizeof (pcicfg_range_t)); + + kmem_free((caddr_t)ranges, rlen); + + return (PCICFG_SUCCESS); +} + +static int +pcicfg_update_reg_prop(dev_info_t *dip, uint32_t regvalue, uint_t reg_offset) +{ + int rlen; + pci_regspec_t *reg; + caddr_t newreg; + uint32_t hiword; + pci_regspec_t addition; + uint32_t size; + uint_t status; + + status = ddi_getlongprop(DDI_DEV_T_ANY, + dip, DDI_PROP_DONTPASS, "reg", (caddr_t)®, &rlen); + + switch (status) { + case DDI_PROP_SUCCESS: + break; + case DDI_PROP_NO_MEMORY: + DEBUG0("reg present, but unable to get memory\n"); + return (PCICFG_FAILURE); + default: + DEBUG0("no reg property\n"); + return (PCICFG_FAILURE); + } + + /* + * Allocate memory for the existing reg(s) plus one and then + * build it. + */ + newreg = kmem_zalloc(rlen+sizeof (pci_regspec_t), KM_SLEEP); + + /* + * Build the regspec, then add it to the existing one(s) + */ + + hiword = PCICFG_MAKE_REG_HIGH(PCI_REG_BUS_G(reg->pci_phys_hi), + PCI_REG_DEV_G(reg->pci_phys_hi), + PCI_REG_FUNC_G(reg->pci_phys_hi), reg_offset); + + if (reg_offset == PCI_CONF_ROM) { + size = (~(PCI_BASE_ROM_ADDR_M & regvalue))+1; + hiword |= PCI_ADDR_MEM32; + } else { + size = (~(PCI_BASE_M_ADDR_M & regvalue))+1; + + if ((PCI_BASE_SPACE_M & regvalue) == PCI_BASE_SPACE_MEM) { + if ((PCI_BASE_TYPE_M & regvalue) == PCI_BASE_TYPE_MEM) { + hiword |= PCI_ADDR_MEM32; + } else if ((PCI_BASE_TYPE_M & regvalue) + == PCI_BASE_TYPE_ALL) { + hiword |= PCI_ADDR_MEM64; + } + } else { + hiword |= PCI_ADDR_IO; + } + } + + addition.pci_phys_hi = hiword; + addition.pci_phys_mid = 0; + addition.pci_phys_low = 0; + addition.pci_size_hi = 0; + addition.pci_size_low = size; + + bcopy(reg, newreg, rlen); + bcopy(&addition, newreg + rlen, sizeof (pci_regspec_t)); + + /* + * Write out the new "reg" property + */ + (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, + dip, "reg", (int *)newreg, + (rlen + sizeof (pci_regspec_t))/sizeof (int)); + + kmem_free((caddr_t)newreg, rlen+sizeof (pci_regspec_t)); + kmem_free((caddr_t)reg, rlen); + + return (PCICFG_SUCCESS); +} + +static void +pcicfg_device_on(ddi_acc_handle_t config_handle) +{ + /* + * Enable memory, IO, and bus mastership + * XXX should we enable parity, SERR#, + * fast back-to-back, and addr. stepping? + */ + pci_config_put16(config_handle, PCI_CONF_COMM, + pci_config_get16(config_handle, PCI_CONF_COMM) | 0x7); +} + +static void +pcicfg_device_off(ddi_acc_handle_t config_handle) +{ + /* + * Disable I/O and memory traffic through the bridge + */ + pci_config_put16(config_handle, PCI_CONF_COMM, 0x0); +} + +/* + * Setup the basic 1275 properties based on information found in the config + * header of the PCI device + */ +static int +pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle, + uint8_t pcie_dev) +{ + int ret, cap_id_loc; + uint16_t val; + uint32_t wordval; + uint8_t byteval; + + /* These two exists only for non-bridges */ + if (((pci_config_get8(config_handle, PCI_CONF_HEADER) + & PCI_HEADER_TYPE_M) == PCI_HEADER_ZERO) && !pcie_dev) { + byteval = pci_config_get8(config_handle, PCI_CONF_MIN_G); + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "min-grant", byteval)) != DDI_SUCCESS) { + return (ret); + } + + byteval = pci_config_get8(config_handle, PCI_CONF_MAX_L); + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "max-latency", byteval)) != DDI_SUCCESS) { + return (ret); + } + } + + /* + * These should always exist and have the value of the + * corresponding register value + */ + val = pci_config_get16(config_handle, PCI_CONF_VENID); + + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "vendor-id", val)) != DDI_SUCCESS) { + return (ret); + } + val = pci_config_get16(config_handle, PCI_CONF_DEVID); + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "device-id", val)) != DDI_SUCCESS) { + return (ret); + } + byteval = pci_config_get8(config_handle, PCI_CONF_REVID); + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "revision-id", byteval)) != DDI_SUCCESS) { + return (ret); + } + + wordval = (pci_config_get16(config_handle, PCI_CONF_SUBCLASS)<< 8) | + (pci_config_get8(config_handle, PCI_CONF_PROGCLASS)); + + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "class-code", wordval)) != DDI_SUCCESS) { + return (ret); + } + val = (pci_config_get16(config_handle, + PCI_CONF_STAT) & PCI_STAT_DEVSELT); + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "devsel-speed", val)) != DDI_SUCCESS) { + return (ret); + } + + /* + * The next three are bits set in the status register. The property is + * present (but with no value other than its own existence) if the bit + * is set, non-existent otherwise + */ + if ((!pcie_dev) && + (pci_config_get16(config_handle, PCI_CONF_STAT) & + PCI_STAT_FBBC)) { + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "fast-back-to-back", 0)) != DDI_SUCCESS) { + return (ret); + } + } + if ((!pcie_dev) && + (pci_config_get16(config_handle, PCI_CONF_STAT) & + PCI_STAT_66MHZ)) { + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "66mhz-capable", 0)) != DDI_SUCCESS) { + return (ret); + } + } + if (pci_config_get16(config_handle, PCI_CONF_STAT) & PCI_STAT_UDF) { + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "udf-supported", 0)) != DDI_SUCCESS) { + return (ret); + } + } + + /* + * These next three are optional and are not present + * if the corresponding register is zero. If the value + * is non-zero then the property exists with the value + * of the register. + */ + if ((val = pci_config_get16(config_handle, + PCI_CONF_SUBVENID)) != 0) { + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "subsystem-vendor-id", val)) != DDI_SUCCESS) { + return (ret); + } + } + if ((val = pci_config_get16(config_handle, + PCI_CONF_SUBSYSID)) != 0) { + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "subsystem-id", val)) != DDI_SUCCESS) { + return (ret); + } + } + if ((val = pci_config_get16(config_handle, + PCI_CONF_CACHE_LINESZ)) != 0) { + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "cache-line-size", val)) != DDI_SUCCESS) { + return (ret); + } + } + + /* + * If the Interrupt Pin register is non-zero then the + * interrupts property exists + */ + if ((byteval = pci_config_get8(config_handle, PCI_CONF_IPIN)) != 0) { + /* + * If interrupt pin is non-zero, + * record the interrupt line used + */ + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "interrupts", byteval)) != DDI_SUCCESS) { + return (ret); + } + } + if (pcie_dev && (cap_id_loc = pcicfg_get_cap(config_handle, + PCI_CAP_ID_PCI_E)) > 0) { + val = pci_config_get16(config_handle, cap_id_loc + + PCIE_PCIECAP) & PCIE_PCIECAP_SLOT_IMPL; + /* if slot implemented, get physical slot number */ + if (val) { + wordval = (pci_config_get32(config_handle, cap_id_loc + + PCIE_SLOTCAP) >> + PCIE_SLOTCAP_PHY_SLOT_NUM_SHIFT) & + PCIE_SLOTCAP_PHY_SLOT_NUM_MASK; + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, + dip, "physical-slot#", wordval)) + != DDI_SUCCESS) { + return (ret); + } + } + } + return (PCICFG_SUCCESS); +} +static int +pcicfg_set_busnode_props(dev_info_t *dip, uint8_t pcie_device_type) +{ + int ret; + char device_type[8]; + + if (pcie_device_type) + (void) strcpy(device_type, "pciex"); + else + (void) strcpy(device_type, "pci"); + + if ((ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip, + "device_type", device_type)) != DDI_SUCCESS) { + return (ret); + } + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "#address-cells", 3)) != DDI_SUCCESS) { + return (ret); + } + if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, + "#size-cells", 2)) != DDI_SUCCESS) { + return (ret); + } + return (PCICFG_SUCCESS); +} + +static int +pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle, + uint8_t pcie_dev) +{ + + int ret; + char *name; + char buffer[64], pprefix[8]; + uint16_t classcode; + uint8_t revid, pif, pclass, psubclass; + char *compat[24]; + int i; + int n; + uint16_t sub_vid, sub_sid, vid, did; + + /* set the property prefix based on the device type */ + if (pcie_dev) + (void) sprintf(pprefix, "pciex"); + else + (void) sprintf(pprefix, "pci"); + sub_vid = pci_config_get16(config_handle, PCI_CONF_SUBVENID), + sub_sid = pci_config_get16(config_handle, PCI_CONF_SUBSYSID); + vid = pci_config_get16(config_handle, PCI_CONF_VENID), + did = pci_config_get16(config_handle, PCI_CONF_DEVID); + revid = pci_config_get8(config_handle, PCI_CONF_REVID); + pif = pci_config_get8(config_handle, PCI_CONF_PROGCLASS); + classcode = pci_config_get16(config_handle, PCI_CONF_SUBCLASS); + pclass = pci_config_get8(config_handle, PCI_CONF_BASCLASS); + psubclass = pci_config_get8(config_handle, PCI_CONF_SUBCLASS); + + /* + * NOTE: These are for both a child and PCI-PCI bridge node + */ + + /* + * "name" property rule + * -------------------- + * + * + * | \svid | + * | \ | + * | \ | + * | ssid \ | =0 | != 0 | + * |------------|-----------------------|-----------------------| + * | | | | + * | =0 | vid,did | svid,ssid | + * | | | | + * |------------|-----------------------|-----------------------| + * | | | | + * | !=0 | svid,ssid | svid,ssid | + * | | | | + * |------------|-----------------------|-----------------------| + * + * where: + * vid = vendor id + * did = device id + * svid = subsystem vendor id + * ssid = subsystem id + */ + + if ((sub_sid != 0) || (sub_vid != 0)) { + (void) sprintf(buffer, "%s%x,%x", pprefix, sub_vid, sub_sid); + } else { + (void) sprintf(buffer, "%s%x,%x", pprefix, vid, did); + } + + /* + * In some environments, trying to use "generic" 1275 names is + * not the convention. In those cases use the name as created + * above. In all the rest of the cases, check to see if there + * is a generic name first. + */ +#ifdef _DONT_USE_1275_GENERIC_NAMES + name = buffer; +#else + if ((name = pcicfg_get_class_name(classcode)) == NULL) { + /* + * Set name to the above fabricated name + */ + name = buffer; + } +#endif + + /* + * The node name field needs to be filled in with the name + */ + if (ndi_devi_set_nodename(dip, name, 0) != NDI_SUCCESS) { + DEBUG0("Failed to set nodename for node\n"); + return (PCICFG_FAILURE); + } + + /* + * Create the compatible property as an array of pointers + * to strings. Start with the buffer created above. + */ + n = 0; + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + /* + * Setup 'compatible' as per the PCI2.1 bindings document. + * pci[ex]VVVV,DDDD.SSSS.ssss.RR + * pci[ex]VVVV,DDDD.SSSS.ssss + * pciSSSS.ssss -> not created for PCIe as per PCIe bindings + * pci[ex]VVVV,DDDD.RR + * pci[ex]VVVV,DDDD + * pci[ex]class,CCSSPP + * pci[ex]class,CCSS + */ + + /* pci[ex]VVVV,DDDD.SSSS.ssss.RR */ + (void) sprintf(buffer, "%s%x,%x.%x.%x.%x", pprefix, vid, did, + sub_vid, sub_sid, revid); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + /* pci[ex]VVVV,DDDD.SSSS.ssss */ + (void) sprintf(buffer, "%s%x,%x.%x.%x", pprefix, vid, did, + sub_vid, sub_sid); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + /* pciSSSS.ssss -> not created for PCIe as per PCIe bindings */ + if (!pcie_dev) { + (void) sprintf(buffer, "pci%x,%x", sub_vid, sub_sid); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + } + + /* pci[ex]VVVV,DDDD.RR */ + (void) sprintf(buffer, "%s%x,%x.%x", pprefix, vid, did, revid); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + /* pci[ex]VVVV,DDDD */ + (void) sprintf(buffer, "%s%x,%x", pprefix, vid, did); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + /* pci[ex]class,CCSSPP */ + (void) sprintf(buffer, "%sclass,%02x%02x%02x", pprefix, + pclass, psubclass, pif); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + /* pci[ex]class,CCSS */ + (void) sprintf(buffer, "%sclass,%04x", pprefix, classcode); + compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); + (void) strcpy(compat[n++], buffer); + + if ((ret = ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, + "compatible", (char **)compat, n)) != DDI_SUCCESS) { + return (ret); + } + + for (i = 0; i < n; i++) { + kmem_free(compat[i], strlen(compat[i]) + 1); + } + + DEBUG1("pcicfg_set_childnode_props - creating name=%s\n", name); + if ((ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip, + "name", name)) != DDI_SUCCESS) { + + DEBUG0("pcicfg_set_childnode_props - Unable to create name " + "property\n"); + + return (ret); + } + + return (PCICFG_SUCCESS); +} + +/* + * Program the bus numbers into the bridge + */ + +static void +pcicfg_set_bus_numbers(ddi_acc_handle_t config_handle, +uint_t primary, uint_t secondary, uint_t subordinate) +{ + DEBUG3("Setting bridge bus-range %d,%d,%d\n", primary, secondary, + subordinate); + /* + * Primary bus# + */ + pci_config_put8(config_handle, PCI_BCNF_PRIBUS, primary); + + /* + * Secondary bus# + */ + pci_config_put8(config_handle, PCI_BCNF_SECBUS, secondary); + + /* + * Subordinate bus# + */ + pci_config_put8(config_handle, PCI_BCNF_SUBBUS, subordinate); +} + +/* + * Put bridge registers into initial state + */ +static void +pcicfg_setup_bridge(pcicfg_phdl_t *entry, + ddi_acc_handle_t handle) +{ + /* + * The highest bus seen during probing is the max-subordinate bus + */ + pci_config_put8(handle, PCI_BCNF_SUBBUS, entry->highest_bus); + + /* + * Reset the secondary bus + */ + pci_config_put16(handle, PCI_BCNF_BCNTRL, + pci_config_get16(handle, PCI_BCNF_BCNTRL) | 0x40); + + drv_usecwait(100); + + pci_config_put16(handle, PCI_BCNF_BCNTRL, + pci_config_get16(handle, PCI_BCNF_BCNTRL) & ~0x40); + + /* + * Program the memory base register with the + * start of the memory range + */ + pci_config_put16(handle, PCI_BCNF_MEM_BASE, + PCICFG_HIWORD(PCICFG_LOADDR(entry->memory_last))); + + /* + * Program the I/O base register with the start of the I/O range + */ + pci_config_put8(handle, PCI_BCNF_IO_BASE_LOW, + PCICFG_HIBYTE(PCICFG_LOWORD(PCICFG_LOADDR(entry->io_last)))); + pci_config_put16(handle, PCI_BCNF_IO_BASE_HI, + PCICFG_HIWORD(PCICFG_LOADDR(entry->io_last))); + + /* + * Clear status bits + */ + pci_config_put16(handle, PCI_BCNF_SEC_STATUS, 0xffff); + + /* + * Turn off prefetchable range + */ + pci_config_put32(handle, PCI_BCNF_PF_BASE_LOW, 0x0000ffff); + pci_config_put32(handle, PCI_BCNF_PF_BASE_HIGH, 0xffffffff); + pci_config_put32(handle, PCI_BCNF_PF_LIMIT_HIGH, 0x0); + + /* + * Needs to be set to this value + */ + pci_config_put8(handle, PCI_CONF_ILINE, 0xf); + + /* + * After a Reset, we need to wait 2^25 clock cycles before the + * first Configuration access. The worst case is 33MHz, which + * is a 1 second wait. + */ + drv_usecwait(pcicfg_sec_reset_delay); + +} + +static void +pcicfg_update_bridge(pcicfg_phdl_t *entry, + ddi_acc_handle_t handle) +{ + uint_t length; + + /* + * Program the memory limit register with the end of the memory range + */ + + DEBUG1("DOWN ROUNDED ===>[0x%x]\n", + PCICFG_ROUND_DOWN(entry->memory_last, + PCICFG_MEMGRAN)); + + pci_config_put16(handle, PCI_BCNF_MEM_LIMIT, + PCICFG_HIWORD(PCICFG_LOADDR( + PCICFG_ROUND_DOWN(entry->memory_last, + PCICFG_MEMGRAN)))); + /* + * Since this is a bridge, the rest of this range will + * be responded to by the bridge. We have to round up + * so no other device claims it. + */ + if ((length = (PCICFG_ROUND_UP(entry->memory_last, + PCICFG_MEMGRAN) - entry->memory_last)) > 0) { + (void) pcicfg_get_mem(entry, length, NULL); + DEBUG1("Added [0x%x]at the top of " + "the bridge (mem)\n", length); + } + + /* + * Program the I/O limit register with the end of the I/O range + */ + pci_config_put8(handle, PCI_BCNF_IO_LIMIT_LOW, + PCICFG_HIBYTE(PCICFG_LOWORD( + PCICFG_LOADDR(PCICFG_ROUND_DOWN(entry->io_last, + PCICFG_IOGRAN))))); + + pci_config_put16(handle, PCI_BCNF_IO_LIMIT_HI, + PCICFG_HIWORD(PCICFG_LOADDR(PCICFG_ROUND_DOWN(entry->io_last, + PCICFG_IOGRAN)))); + + /* + * Same as above for I/O space. Since this is a + * bridge, the rest of this range will be responded + * to by the bridge. We have to round up so no + * other device claims it. + */ + if ((length = (PCICFG_ROUND_UP(entry->io_last, + PCICFG_IOGRAN) - entry->io_last)) > 0) { + (void) pcicfg_get_io(entry, length, NULL); + DEBUG1("Added [0x%x]at the top of " + "the bridge (I/O)\n", length); + } +} + +/*ARGSUSED*/ +static void +pcicfg_disable_bridge_probe_err(dev_info_t *dip, ddi_acc_handle_t h, + pcicfg_err_regs_t *regs) +{ + uint16_t val; + + /* disable SERR generated in the context of Master Aborts. */ + regs->cmd = val = pci_config_get16(h, PCI_CONF_COMM); + val &= ~PCI_COMM_SERR_ENABLE; + pci_config_put16(h, PCI_CONF_COMM, val); + regs->bcntl = val = pci_config_get16(h, PCI_BCNF_BCNTRL); + val &= ~PCI_BCNF_BCNTRL_SERR_ENABLE; + pci_config_put16(h, PCI_BCNF_BCNTRL, val); + /* clear any current pending errors */ + pci_config_put16(h, PCI_CONF_STAT, PCI_STAT_S_TARG_AB| + PCI_STAT_R_TARG_AB | PCI_STAT_R_MAST_AB | PCI_STAT_S_SYSERR); + pci_config_put16(h, PCI_BCNF_SEC_STATUS, PCI_STAT_S_TARG_AB| + PCI_STAT_R_TARG_AB | PCI_STAT_R_MAST_AB | PCI_STAT_S_SYSERR); + /* if we are a PCIe device, disable the generation of UR, CE and NFE */ + if (regs->pcie_dev) { + uint16_t devctl; + int off = pcicfg_get_cap(h, PCI_CAP_ID_PCI_E); + + regs->pcie_cap_off = off; + regs->devctl = devctl = pci_config_get16(h, off + PCIE_DEVCTL); + devctl &= ~(PCIE_DEVCTL_UR_REPORTING_EN | + PCIE_DEVCTL_CE_REPORTING_EN | + PCIE_DEVCTL_NFE_REPORTING_EN | + PCIE_DEVCTL_FE_REPORTING_EN); + pci_config_put16(h, off + PCIE_DEVCTL, devctl); + } +} + +/*ARGSUSED*/ +static void +pcicfg_enable_bridge_probe_err(dev_info_t *dip, ddi_acc_handle_t h, + pcicfg_err_regs_t *regs) +{ + /* clear any pending errors */ + pci_config_put16(h, PCI_CONF_STAT, PCI_STAT_S_TARG_AB| + PCI_STAT_R_TARG_AB | PCI_STAT_R_MAST_AB | PCI_STAT_S_SYSERR); + pci_config_put16(h, PCI_BCNF_SEC_STATUS, PCI_STAT_S_TARG_AB| + PCI_STAT_R_TARG_AB | PCI_STAT_R_MAST_AB | PCI_STAT_S_SYSERR); + + /* restore original settings */ + if (regs->pcie_dev) { + pcie_clear_errors(dip, h); + pci_config_put16(h, regs->pcie_cap_off + PCIE_DEVCTL, + regs->devctl); + } + + pci_config_put16(h, PCI_BCNF_BCNTRL, regs->bcntl); + pci_config_put16(h, PCI_CONF_COMM, regs->cmd); + +} + +static int +pcicfg_probe_children(dev_info_t *parent, uint_t bus, + uint_t device, uint_t func, uint_t *highest_bus) +{ + dev_info_t *new_child; + ddi_acc_handle_t config_handle; + uint8_t header_type, pcie_dev = 0; + + int i; + uint32_t request; + int ret; + pcicfg_err_regs_t regs; + /* + * This node will be put immediately below + * "parent". Allocate a blank device node. It will either + * be filled in or freed up based on further probing. + */ + /* + * Note: in usr/src/uts/common/io/hotplug/pcicfg/pcicfg.c + * ndi_devi_alloc() is called as ndi_devi_alloc_sleep() + */ + if (ndi_devi_alloc(parent, DEVI_PSEUDO_NEXNAME, + (pnode_t)DEVI_SID_NODEID, &new_child) + != NDI_SUCCESS) { + DEBUG0("pcicfg_probe_children(): Failed to alloc child node\n"); + return (PCICFG_FAILURE); + } + + if (pcicfg_add_config_reg(new_child, bus, + device, func) != DDI_SUCCESS) { + DEBUG0("pcicfg_probe_children():" + "Failed to add candidate REG\n"); + goto failedconfig; + } + + if ((ret = pcicfg_config_setup(new_child, &config_handle)) + != PCICFG_SUCCESS) { + if (ret == PCICFG_NODEVICE) { + (void) ndi_devi_free(new_child); + return (ret); + } + DEBUG0("pcicfg_probe_children():" + "Failed to setup config space\n"); + goto failedconfig; + } + + /* + * As soon as we have access to config space, + * turn off device. It will get turned on + * later (after memory is assigned). + */ + (void) pcicfg_device_off(config_handle); + + /* check if we are PCIe device */ + if (pcicfg_pcie_dev(new_child, PCICFG_DEVICE_TYPE_PCIE, ®s) + == DDI_SUCCESS) { + DEBUG0("PCIe device detected\n"); + pcie_dev = 1; + } + + /* + * Set 1275 properties common to all devices + */ + if (pcicfg_set_standard_props(new_child, config_handle, + pcie_dev) != PCICFG_SUCCESS) { + DEBUG0("Failed to set standard properties\n"); + goto failedchild; + } + + /* + * Child node properties NOTE: Both for PCI-PCI bridge and child node + */ + if (pcicfg_set_childnode_props(new_child, config_handle, + pcie_dev) != PCICFG_SUCCESS) { + goto failedchild; + } + + header_type = pci_config_get8(config_handle, PCI_CONF_HEADER); + + /* + * If this is not a multi-function card only probe function zero. + */ + if ((!(header_type & PCI_HEADER_MULTI)) && (func != 0)) { + + (void) pcicfg_config_teardown(&config_handle); + (void) ndi_devi_free(new_child); + return (PCICFG_NODEVICE); + } + + DEBUG1("---Vendor ID = [0x%x]\n", + pci_config_get16(config_handle, PCI_CONF_VENID)); + DEBUG1("---Device ID = [0x%x]\n", + pci_config_get16(config_handle, PCI_CONF_DEVID)); + + /* + * Attach the child to its parent + */ + (void) i_ndi_config_node(new_child, DS_LINKED, 0); + + if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { + + DEBUG3("--Bridge found bus [0x%x] device" + "[0x%x] func [0x%x]\n", bus, device, func); + + if (pcicfg_probe_bridge(new_child, config_handle, + bus, highest_bus) != PCICFG_SUCCESS) { + (void) pcicfg_free_bridge_resources(new_child); + goto failedchild; + } + + } else { + + DEBUG3("--Leaf device found bus [0x%x] device" + "[0x%x] func [0x%x]\n", + bus, device, func); + + i = PCI_CONF_BASE0; + + while (i <= PCI_CONF_BASE5) { + + pci_config_put32(config_handle, i, 0xffffffff); + + request = pci_config_get32(config_handle, i); + /* + * If its a zero length, don't do + * any programming. + */ + if (request != 0) { + /* + * Add to the "reg" property + */ + if (pcicfg_update_reg_prop(new_child, + request, i) != PCICFG_SUCCESS) { + goto failedchild; + } + } else { + DEBUG1("BASE register [0x%x] asks for " + "[0x0]=[0x0](32)\n", i); + i += 4; + continue; + } + + /* + * Increment by eight if it is 64 bit address space + */ + if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) { + DEBUG3("BASE register [0x%x] asks for " + "[0x%x]=[0x%x] (64)\n", + i, request, + (~(PCI_BASE_M_ADDR_M & request))+1) + i += 8; + } else { + DEBUG3("BASE register [0x%x] asks for " + "[0x%x]=[0x%x](32)\n", + i, request, + (~(PCI_BASE_M_ADDR_M & request))+1) + i += 4; + } + } + + /* + * Get the ROM size and create register for it + */ + pci_config_put32(config_handle, PCI_CONF_ROM, 0xfffffffe); + + request = pci_config_get32(config_handle, PCI_CONF_ROM); + /* + * If its a zero length, don't do + * any programming. + */ + + if (request != 0) { + DEBUG3("BASE register [0x%x] asks for [0x%x]=[0x%x]\n", + PCI_CONF_ROM, request, + (~(PCI_BASE_ROM_ADDR_M & request))+1); + /* + * Add to the "reg" property + */ + if (pcicfg_update_reg_prop(new_child, + request, PCI_CONF_ROM) != PCICFG_SUCCESS) { + goto failedchild; + } + } + + /* now allocate & program the resources */ + if (pcicfg_device_assign(new_child) != PCICFG_SUCCESS) { + (void) pcicfg_free_device_resources(new_child); + goto failedchild; + } + (void) ndi_devi_bind_driver(new_child, 0); + } + + (void) pcicfg_config_teardown(&config_handle); + return (PCICFG_SUCCESS); + +failedchild: + + (void) pcicfg_config_teardown(&config_handle); + +failedconfig: + + (void) ndi_devi_free(new_child); + return (PCICFG_FAILURE); +} + +static int +pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, + uint_t device, uint_t func, uint_t *highest_bus) +{ + dev_info_t *new_child; + int8_t header_type; + int ret; + ddi_acc_handle_t h, ph; + int error = 0; + extern int pcicfg_dont_interpret; + pcicfg_err_regs_t parent_regs, regs; +#ifdef PCICFG_INTERPRET_FCODE + struct pci_ops_bus_args po; + fco_handle_t c; + char unit_address[64]; + int fcode_size = 0; + uchar_t *fcode_addr; + uint64_t mem_answer, mem_alen; + pci_regspec_t p; + int32_t request; + ndi_ra_request_t req; + int16_t vendor_id, device_id; +#endif + + /* + * check if our parent is of type pciex. + * if so, program config space to disable error msgs during probe. + */ + if (pcicfg_pcie_dev(parent, PCICFG_DEVICE_TYPE_PCIE, &parent_regs) + == DDI_SUCCESS) { + DEBUG0("PCI/PCIe parent detected. Disable errors.\n"); + /* + * disable parent generating URs or SERR#s during probing + * alone. + */ + if (pci_config_setup(parent, &ph) != DDI_SUCCESS) + return (DDI_FAILURE); + pcicfg_disable_bridge_probe_err(parent, ph, &parent_regs); + } + + /* + * This node will be put immediately below + * "parent". Allocate a blank device node. It will either + * be filled in or freed up based on further probing. + */ + + if (ndi_devi_alloc(parent, DEVI_PSEUDO_NEXNAME, + (pnode_t)DEVI_SID_NODEID, &new_child) + != NDI_SUCCESS) { + DEBUG0("pcicfg_fcode_probe(): Failed to alloc child node\n"); + /* return (PCICFG_FAILURE); */ + ret = PCICFG_FAILURE; + goto failed2; + } + + /* + * Create a dummy reg property. This will be replaced with + * a real reg property when fcode completes or if we need to + * produce one by hand. + */ + if (pcicfg_add_config_reg(new_child, bus, + device, func) != DDI_SUCCESS) { + ret = PCICFG_FAILURE; + goto failed3; + } +#ifdef EFCODE21554 + if ((ret = pcicfg_config_setup(new_child, &h)) + != PCICFG_SUCCESS) { + DEBUG0("pcicfg_fcode_probe():" + "Failed to setup config space\n"); + ret = PCICFG_NODEVICE; + goto failed3; + } + +#else + p.pci_phys_hi = PCICFG_MAKE_REG_HIGH(bus, device, func, 0); + p.pci_phys_mid = p.pci_phys_low = 0; + p.pci_size_hi = p.pci_size_low = 0; + + /* + * Map in configuration space (temporarily) + */ + acc.devacc_attr_version = DDI_DEVICE_ATTR_V0; + acc.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; + acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + + if (pcicfg_map_phys(new_child, &p, &virt, &acc, &h)) { + DEBUG0("pcicfg_fcode_probe():" + "Failed to setup config space\n"); + ret = PCICFG_NODEVICE; + goto failed3; + } + + /* + * First use ddi_peek16 so that if there is not a device there, + * a bus error will not cause a panic. + */ + v = virt + PCI_CONF_VENID; + if (ddi_peek16(new_child, (int16_t *)v, &vendor_id)) { + DEBUG0("Can not read Vendor ID"); + pcicfg_unmap_phys(&h, &p); + ret = PCICFG_NODEVICE; + goto failed3; + } +#endif + DEBUG0("fcode_probe: conf space mapped.\n"); + /* + * As soon as we have access to config space, + * turn off device. It will get turned on + * later (after memory is assigned). + */ + (void) pcicfg_device_off(h); + + /* check if we are PCIe device */ + if (pcicfg_pcie_dev(new_child, PCICFG_DEVICE_TYPE_PCIE, ®s) + == DDI_SUCCESS) { + /*EMPTY*/ + DEBUG0("PCI/PCIe device detected\n"); + } + + /* + * Set 1275 properties common to all devices + */ + if (pcicfg_set_standard_props(new_child, + h, regs.pcie_dev) != PCICFG_SUCCESS) { + DEBUG0("Failed to set standard properties\n"); + goto failed; + } + + /* + * Child node properties NOTE: Both for PCI-PCI bridge and child node + */ + if (pcicfg_set_childnode_props(new_child, + h, regs.pcie_dev) != PCICFG_SUCCESS) { + ret = PCICFG_FAILURE; + goto failed; + } + + header_type = pci_config_get8(h, PCI_CONF_HEADER); + + /* + * If this is not a multi-function card only probe function zero. + */ + if (!(header_type & PCI_HEADER_MULTI) && (func > 0)) { + + ret = PCICFG_NODEVICE; + goto failed; + } + + if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { + + /* + * XXX - Transparent bridges are handled differently + * than other devices with regards to fcode. Since + * no transparent bridge currently ships with fcode, + * there is no reason to try to extract it from its rom + * or call the fcode interpreter to try to load a drop-in. + * If fcode is developed to handle transparent bridges, + * this code will have to change. + */ + + DEBUG3("--Bridge found bus [0x%x] device" + "[0x%x] func [0x%x]\n", bus, device, func); + + if ((ret = pcicfg_probe_bridge(new_child, h, + bus, highest_bus)) != PCICFG_SUCCESS) + (void) pcicfg_free_bridge_resources(new_child); + goto done; + } else { + + DEBUG3("--Leaf device found bus [0x%x] device" + "[0x%x] func [0x%x]\n", + bus, device, func); + + /* + * link in tree, but don't bind driver + * We don't have compatible property yet + */ + (void) i_ndi_config_node(new_child, DS_LINKED, 0); + + if (pci_config_get8(h, PCI_CONF_IPIN)) { + pci_config_put8(h, PCI_CONF_ILINE, 0xf); + } + +#ifdef PCICFG_INTERPRET_FCODE + /* + * Some platforms (x86) don't run fcode, so don't interpret + * fcode that might be in the ROM. + */ + if (pcicfg_dont_interpret == 0) { + + /* This platform supports fcode */ + + vendor_id = pci_config_get16(h, PCI_CONF_VENID); + device_id = pci_config_get16(h, PCI_CONF_DEVID); + + /* + * Get the ROM size and create register for it + */ + pci_config_put32(h, PCI_CONF_ROM, 0xfffffffe); + + request = pci_config_get32(h, PCI_CONF_ROM); + + /* + * If its a zero length, don't do + * any programming. + */ + + if (request != 0) { + /* + * Add resource to assigned-addresses. + */ + if (pcicfg_fcode_assign_bars(h, new_child, + bus, device, func, request, &p) + != PCICFG_SUCCESS) { + DEBUG0("Failed to assign addresses to " + "implemented BARs"); + ret = PCICFG_FAILURE; + goto failed; + } + + /* Turn device on */ + (void) pcicfg_device_on(h); + + /* + * Attempt to load fcode. + */ + (void) pcicfg_load_fcode(new_child, bus, device, + func, vendor_id, device_id, &fcode_addr, + &fcode_size, PCICFG_LOADDR(mem_answer), + (~(PCI_BASE_ROM_ADDR_M & request)) + 1); + + /* Turn device off */ + (void) pcicfg_device_off(h); + + /* + * Free the ROM resources. + */ + (void) pcicfg_free_resource(new_child, p); + + DEBUG2("configure: fcode addr %lx size %x\n", + fcode_addr, fcode_size); + + /* + * Create the fcode-rom-offset property. The + * buffer containing the fcode always starts + * with 0xF1, so the fcode offset is zero. + */ + if (ndi_prop_update_int(DDI_DEV_T_NONE, + new_child, "fcode-rom-offset", 0) + != DDI_SUCCESS) { + DEBUG0("Failed to create " + "fcode-rom-offset property\n"); + ret = PCICFG_FAILURE; + goto failed; + } + } else { + DEBUG0("There is no Expansion ROM\n"); + fcode_addr = NULL; + fcode_size = 0; + } + + /* + * Fill in the bus specific arguments. For + * PCI, it is the config address. + */ + po.config_address = + PCICFG_MAKE_REG_HIGH(bus, device, func, 0); + + DEBUG1("config_address=%x\n", po.config_address); + + /* + * Build unit address. + */ + (void) sprintf(unit_address, "%x,%x", device, func); + + DEBUG3("pci_fc_ops_alloc_handle ap=%lx " + "new device=%lx unit address=%s\n", + parent, new_child, unit_address); + + c = pci_fc_ops_alloc_handle(parent, new_child, + fcode_addr, fcode_size, unit_address, &po); + + DEBUG0("calling fcode_interpreter()\n"); + + DEBUG3("Before int DIP=%lx binding name %s major %d\n", + new_child, ddi_binding_name(new_child), + ddi_name_to_major(ddi_binding_name(new_child))); + + error = fcode_interpreter(parent, &pci_fc_ops, c); + + DEBUG1("returned from fcode_interpreter() - " + "returned %x\n", error); + + pci_fc_ops_free_handle(c); + + DEBUG1("fcode size = %x\n", fcode_size); + /* + * We don't need the fcode anymore. While allocating + * we had rounded up to a page size. + */ + if (fcode_size) { + kmem_free(fcode_addr, ptob(btopr(fcode_size))); + } + } else { + /* This platform does not support fcode */ + + DEBUG0("NOT calling fcode_interpreter()\n"); + } + +#endif /* PCICFG_INTERPRET_FCODE */ + + if ((error == 0) && (pcicfg_dont_interpret == 0)) { + /* + * The interpreter completed successfully. + * We need to redo the resources based on the new reg + * property. + */ + DEBUG3("DIP=%lx binding name %s major %d\n", new_child, + ddi_binding_name(new_child), + ddi_name_to_major(ddi_binding_name(new_child))); + + /* + * Readjust resources specified by reg property. + */ + if (pcicfg_alloc_new_resources(new_child) == + PCICFG_FAILURE) { + ret = PCICFG_FAILURE; + goto failed; + } + + ret = PCICFG_SUCCESS; + /* no fcode, bind driver now */ + (void) ndi_devi_bind_driver(new_child, 0); + + goto done; + } else if ((error != FC_NO_FCODE) && + (pcicfg_dont_interpret == 0)) { + /* + * The interpreter located fcode, but had an error in + * processing. Cleanup and fail the operation. + */ + DEBUG0("Interpreter detected fcode failure\n"); + (void) pcicfg_free_resources(new_child); + ret = PCICFG_FAILURE; + goto failed; + } else { + /* + * Either the interpreter failed with FC_NO_FCODE or we + * chose not to run the interpreter + * (pcicfg_dont_interpret). + * + * If the interpreter failed because there was no + * dropin, then we need to probe the device ourself. + */ + + /* + * Free any resources that may have been assigned + * during fcode loading/execution since we need + * to start over. + */ + (void) pcicfg_free_resources(new_child); + +#ifdef EFCODE21554 + pcicfg_config_teardown(&h); +#else + pcicfg_unmap_phys(&h, &p); +#endif + (void) ndi_devi_free(new_child); + + DEBUG0("No Drop-in Probe device ourself\n"); + + ret = pcicfg_probe_children(parent, bus, device, func, + highest_bus); + + if (ret != PCICFG_SUCCESS) { + DEBUG0("Could not self probe child\n"); + goto failed2; + } + + /* + * We successfully self probed the device. + */ + if ((new_child = pcicfg_devi_find( + parent, device, func)) == NULL) { + DEBUG0("Did'nt find device node " + "just created\n"); + ret = PCICFG_FAILURE; + goto failed2; + } +#ifdef EFCODE21554 + /* + * Till now, we have detected a non transparent bridge + * (ntbridge) as a part of the generic probe code and + * configured only one configuration + * header which is the side facing the host bus. + * Now, configure the other side and create children. + * + * To make the process simpler, lets load the device + * driver for the non transparent bridge as this is a + * Solaris bundled driver, and use its configuration map + * services rather than programming it here. + * If the driver is not bundled into Solaris, it must be + * first loaded and configured before performing any + * hotplug operations. + * + * This not only makes the code simpler but also more + * generic. + * + * So here we go. + */ + if (pcicfg_is_ntbridge(new_child) != DDI_FAILURE) { + + DEBUG0("Found nontransparent bridge.\n"); + + ret = pcicfg_configure_ntbridge(new_child, + bus, device); + } + if (ret != PCICFG_SUCCESS) { + /* + * Bridge configure failed. Free up the self + * probed entry. The bus resource allocation + * maps need to be cleaned up to prevent + * warnings on retries of the failed configure. + */ + (void) pcicfg_ntbridge_unconfigure(new_child); + (void) pcicfg_teardown_device(new_child); + } +#endif + goto done2; + } + } +done: +failed: +#ifdef EFCODE21554 + pcicfg_config_teardown(&h); +#else + pcicfg_unmap_phys(&h, &p); +#endif +failed3: + if (ret != PCICFG_SUCCESS) + (void) ndi_devi_free(new_child); +done2: +failed2: + if (parent_regs.pcie_dev) { + pcicfg_enable_bridge_probe_err(parent, ph, &parent_regs); + pci_config_teardown(&ph); + } + return (ret); +} + +static int +pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, + uint_t *highest_bus) +{ + uint64_t next_bus; + uint_t new_bus, num_slots; + ndi_ra_request_t req; + int rval, i, j; + uint64_t mem_answer, mem_base, mem_alen, mem_size, mem_end; + uint64_t io_answer, io_base, io_alen, io_size, io_end; + uint64_t round_answer, round_len; + pcicfg_range_t range[PCICFG_RANGE_LEN]; + int bus_range[2]; + pcicfg_phdl_t phdl; + int count; + uint64_t pcibus_base, pcibus_alen; + uint64_t max_bus; + uint8_t pcie_device_type = 0; + + bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); + req.ra_flags = (NDI_RA_ALLOC_BOUNDED | NDI_RA_ALLOC_PARTIAL_OK); + req.ra_boundbase = 0; + req.ra_boundlen = PCICFG_MAX_BUS_DEPTH; + req.ra_len = PCICFG_MAX_BUS_DEPTH; + req.ra_align_mask = 0; /* no alignment needed */ + + rval = ndi_ra_alloc(ddi_get_parent(new_child), &req, + &pcibus_base, &pcibus_alen, NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS); + + if (rval != NDI_SUCCESS) { + if (rval == NDI_RA_PARTIAL_REQ) { + /*EMPTY*/ + DEBUG0("NDI_RA_PARTIAL_REQ returned for bus range\n"); + } else { + DEBUG0( + "Failed to allocate bus range for bridge\n"); + return (PCICFG_FAILURE); + } + } + + DEBUG2("Bus Range Allocated [base=%d] [len=%d]\n", + pcibus_base, pcibus_alen); + + if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_PCI_BUSNUM) + == NDI_FAILURE) { + DEBUG0("Can not setup resource map - NDI_RA_TYPE_PCI_BUSNUM\n"); + return (PCICFG_FAILURE); + } + + /* + * Put available bus range into the pool. + * Take the first one for this bridge to use and don't give + * to child. + */ + (void) ndi_ra_free(new_child, pcibus_base+1, pcibus_alen-1, + NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS); + + next_bus = pcibus_base; + max_bus = pcibus_base + pcibus_alen - 1; + + new_bus = next_bus; + + DEBUG1("NEW bus found ->[%d]\n", new_bus); + + /* Keep track of highest bus for subordinate bus programming */ + *highest_bus = new_bus; + + /* + * Allocate Memory Space for Bridge + */ + bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); + req.ra_flags = (NDI_RA_ALLOC_BOUNDED | NDI_RA_ALLOC_PARTIAL_OK); + req.ra_boundbase = 0; + /* + * Note: To support a 32b system, boundlen and len need to be + * 32b quantities + */ + req.ra_boundlen = PCICFG_4GIG_LIMIT + 1; + req.ra_len = PCICFG_4GIG_LIMIT + 1; /* Get as big as possible */ + req.ra_align_mask = + PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */ + + rval = ndi_ra_alloc(ddi_get_parent(new_child), &req, + &mem_answer, &mem_alen, NDI_RA_TYPE_MEM, NDI_RA_PASS); + + if (rval != NDI_SUCCESS) { + if (rval == NDI_RA_PARTIAL_REQ) { + /*EMPTY*/ + DEBUG0("NDI_RA_PARTIAL_REQ returned\n"); + } else { + DEBUG0( + "Failed to allocate memory for bridge\n"); + return (PCICFG_FAILURE); + } + } + + DEBUG3("Bridge Memory Allocated [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(mem_answer), + PCICFG_LOADDR(mem_answer), + mem_alen); + + if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_MEM) == NDI_FAILURE) { + DEBUG0("Can not setup resource map - NDI_RA_TYPE_MEM\n"); + return (PCICFG_FAILURE); + } + + /* + * Put available memory into the pool. + */ + (void) ndi_ra_free(new_child, mem_answer, mem_alen, NDI_RA_TYPE_MEM, + NDI_RA_PASS); + + mem_base = mem_answer; + + /* + * Allocate I/O Space for Bridge + */ + bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); + req.ra_align_mask = PCICFG_IOGRAN - 1; /* 4k alignment */ + req.ra_boundbase = 0; + req.ra_boundlen = PCICFG_4GIG_LIMIT; + req.ra_flags = (NDI_RA_ALLOC_BOUNDED | NDI_RA_ALLOC_PARTIAL_OK); + req.ra_len = PCICFG_4GIG_LIMIT; /* Get as big as possible */ + + rval = ndi_ra_alloc(ddi_get_parent(new_child), &req, &io_answer, + &io_alen, NDI_RA_TYPE_IO, NDI_RA_PASS); + + if (rval != NDI_SUCCESS) { + if (rval == NDI_RA_PARTIAL_REQ) { + /*EMPTY*/ + DEBUG0("NDI_RA_PARTIAL_REQ returned\n"); + } else { + DEBUG0("Failed to allocate io space for bridge\n"); + io_base = io_answer = io_alen = 0; + /* return (PCICFG_FAILURE); */ + } + } + + if (io_alen) { + DEBUG3("Bridge IO Space Allocated [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(io_answer), PCICFG_LOADDR(io_answer), + io_alen); + + if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_IO) == + NDI_FAILURE) { + DEBUG0("Can not setup resource map - NDI_RA_TYPE_IO\n"); + return (PCICFG_FAILURE); + } + + /* + * Put available I/O into the pool. + */ + (void) ndi_ra_free(new_child, io_answer, io_alen, + NDI_RA_TYPE_IO, NDI_RA_PASS); + io_base = io_answer; + } + + (void) pcicfg_set_bus_numbers(h, bus, new_bus, max_bus); + + /* + * Reset the secondary bus + */ + pci_config_put16(h, PCI_BCNF_BCNTRL, + pci_config_get16(h, PCI_BCNF_BCNTRL) | 0x40); + + drv_usecwait(100); + + pci_config_put16(h, PCI_BCNF_BCNTRL, + pci_config_get16(h, PCI_BCNF_BCNTRL) & ~0x40); + + /* + * Program the memory base register with the + * start of the memory range + */ + pci_config_put16(h, PCI_BCNF_MEM_BASE, + PCICFG_HIWORD(PCICFG_LOADDR(mem_answer))); + + /* + * Program the memory limit register with the + * end of the memory range. + */ + + pci_config_put16(h, PCI_BCNF_MEM_LIMIT, + PCICFG_HIWORD(PCICFG_LOADDR( + PCICFG_ROUND_DOWN((mem_answer + mem_alen), PCICFG_MEMGRAN) - 1))); + + /* + * Allocate the chunk of memory (if any) not programmed into the + * bridge because of the round down. + */ + if (PCICFG_ROUND_DOWN((mem_answer + mem_alen), PCICFG_MEMGRAN) + != (mem_answer + mem_alen)) { + DEBUG0("Need to allocate Memory round off chunk\n"); + bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); + req.ra_flags = NDI_RA_ALLOC_SPECIFIED; + req.ra_addr = PCICFG_ROUND_DOWN((mem_answer + mem_alen), + PCICFG_MEMGRAN); + req.ra_len = (mem_answer + mem_alen) - + (PCICFG_ROUND_DOWN((mem_answer + mem_alen), + PCICFG_MEMGRAN)); + + (void) ndi_ra_alloc(new_child, &req, + &round_answer, &round_len, NDI_RA_TYPE_MEM, NDI_RA_PASS); + } + + /* + * Program the I/O Space Base + */ + pci_config_put8(h, PCI_BCNF_IO_BASE_LOW, + PCICFG_HIBYTE(PCICFG_LOWORD( + PCICFG_LOADDR(io_answer)))); + + pci_config_put16(h, PCI_BCNF_IO_BASE_HI, + PCICFG_HIWORD(PCICFG_LOADDR(io_answer))); + + /* + * Program the I/O Space Limit + */ + pci_config_put8(h, PCI_BCNF_IO_LIMIT_LOW, + PCICFG_HIBYTE(PCICFG_LOWORD( + PCICFG_LOADDR(PCICFG_ROUND_DOWN(io_answer + io_alen, + PCICFG_IOGRAN)))) - 1); + + pci_config_put16(h, PCI_BCNF_IO_LIMIT_HI, + PCICFG_HIWORD(PCICFG_LOADDR( + PCICFG_ROUND_DOWN(io_answer + io_alen, PCICFG_IOGRAN))) + - 1); + + /* + * Allocate the chunk of I/O (if any) not programmed into the + * bridge because of the round down. + */ + if (PCICFG_ROUND_DOWN((io_answer + io_alen), PCICFG_IOGRAN) + != (io_answer + io_alen)) { + DEBUG0("Need to allocate I/O round off chunk\n"); + bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); + req.ra_flags = NDI_RA_ALLOC_SPECIFIED; + req.ra_addr = PCICFG_ROUND_DOWN((io_answer + io_alen), + PCICFG_IOGRAN); + req.ra_len = (io_answer + io_alen) - + (PCICFG_ROUND_DOWN((io_answer + io_alen), + PCICFG_IOGRAN)); + + (void) ndi_ra_alloc(new_child, &req, + &round_answer, &round_len, NDI_RA_TYPE_IO, NDI_RA_PASS); + } + + /* + * Clear status bits + */ + pci_config_put16(h, PCI_BCNF_SEC_STATUS, 0xffff); + + /* + * Turn off prefetchable range + */ + pci_config_put32(h, PCI_BCNF_PF_BASE_LOW, 0x0000ffff); + pci_config_put32(h, PCI_BCNF_PF_BASE_HIGH, 0xffffffff); + pci_config_put32(h, PCI_BCNF_PF_LIMIT_HIGH, 0x0); + + /* + * Needs to be set to this value + */ + pci_config_put8(h, PCI_CONF_ILINE, 0xf); + + /* check our device_type as defined by Open Firmware */ + if (pcicfg_pcie_device_type(new_child, h) == DDI_SUCCESS) + pcie_device_type = 1; + + /* + * Set bus properties + */ + if (pcicfg_set_busnode_props(new_child, pcie_device_type) + != PCICFG_SUCCESS) { + DEBUG0("Failed to set busnode props\n"); + return (PCICFG_FAILURE); + } + + (void) pcicfg_device_on(h); + + if (ndi_devi_online(new_child, NDI_NO_EVENT|NDI_CONFIG) + != NDI_SUCCESS) { + DEBUG0("Unable to online bridge\n"); + return (PCICFG_FAILURE); + } + + DEBUG0("Bridge is ONLINE\n"); + + /* + * After a Reset, we need to wait 2^25 clock cycles before the + * first Configuration access. The worst case is 33MHz, which + * is a 1 second wait. + */ + drv_usecwait(pcicfg_sec_reset_delay); + + /* + * Probe all children devices + */ + DEBUG0("Bridge Programming Complete - probe children\n"); + ndi_devi_enter(new_child, &count); + for (i = 0; i < PCICFG_MAX_DEVICE; i++) { + for (j = 0; j < PCICFG_MAX_FUNCTION; j++) { + if ((rval = pcicfg_fcode_probe(new_child, + new_bus, i, j, highest_bus)) + != PCICFG_SUCCESS) { + if (rval == PCICFG_NODEVICE) { + DEBUG3("No Device at bus [0x%x]" + "device [0x%x] " + "func [0x%x]\n", new_bus, i, j); + if (j) + continue; + } else { + DEBUG3("Failed to configure bus " + "[0x%x] device [0x%x] " + "func [0x%x]\n", new_bus, i, j); + rval = PCICFG_FAILURE; + } + + break; + } + } + /* if any function fails to be configured, no need to proceed */ + if (rval != PCICFG_NODEVICE) { + break; + } + } + ndi_devi_exit(new_child, count); + + /* if empty topology underneath, it is still a success. */ + if (rval != PCICFG_FAILURE) + rval = PCICFG_SUCCESS; + + /* + * Offline the bridge to allow reprogramming of resources. + */ + (void) ndi_devi_offline(new_child, NDI_NO_EVENT|NDI_UNCONFIG); + + phdl.dip = new_child; + phdl.memory_base = mem_answer; + phdl.io_base = (uint32_t)io_answer; + phdl.error = PCICFG_SUCCESS; /* in case of empty child tree */ + + ndi_devi_enter(ddi_get_parent(new_child), &count); + ddi_walk_devs(new_child, pcicfg_find_resource_end, (void *)&phdl); + ndi_devi_exit(ddi_get_parent(new_child), count); + + if (phdl.error != PCICFG_SUCCESS) { + DEBUG0("Failure summing resources\n"); + return (PCICFG_FAILURE); + } + + num_slots = pcicfg_get_nslots(new_child, h); + mem_end = PCICFG_ROUND_UP(phdl.memory_base, PCICFG_MEMGRAN); + io_end = PCICFG_ROUND_UP(phdl.io_base, PCICFG_IOGRAN); + + DEBUG3("Start of Unallocated Bridge(%d slots) Resources " + "Mem=0x%lx I/O=0x%lx\n", num_slots, mem_end, io_end); + + /* + * if the bridge a slots, then preallocate. If not, assume static + * configuration. Also check for preallocation limits and spit + * warning messages appropriately (perhaps some can be in debug mode). + */ + if (num_slots) { + uint64_t mem_reqd = mem_answer + (num_slots * + pcicfg_slot_memsize); + uint64_t io_reqd = io_answer + (num_slots * + pcicfg_slot_iosize); + uint8_t highest_bus_reqd = new_bus + (num_slots * + pcicfg_slot_busnums); + + if (mem_end > mem_reqd) + cmn_err(CE_WARN, "Memory space consumed by bridge" + " more than planned for %d slot(s)(%lx, %lx)", + num_slots, mem_answer, mem_end); + if (io_end > io_reqd) + cmn_err(CE_WARN, "IO space consumed by bridge" + " more than planned for %d slot(s)(%lx, %lx)", + num_slots, io_answer, io_end); + if (*highest_bus > highest_bus_reqd) + cmn_err(CE_WARN, "Buses consumed by bridge" + " more than planned for %d slot(s)(%x, %x)", + num_slots, new_bus, *highest_bus); + + if (mem_reqd > (mem_answer + mem_alen)) + cmn_err(CE_WARN, "Memory space required by bridge" + " more than available for %d slot(s)(%lx, %lx)", + num_slots, mem_answer, mem_end); + + if (io_reqd > (io_answer + io_alen)) + cmn_err(CE_WARN, "IO space required by bridge" + " more than available for %d slot(s)(%lx, %lx)", + num_slots, io_answer, io_end); + if (highest_bus_reqd > max_bus) + cmn_err(CE_WARN, "Bus numbers required by bridge" + " more than available for %d slot(s)(%x, %x)", + num_slots, new_bus, *highest_bus); + + mem_end = MAX((MIN(mem_reqd, (mem_answer + mem_alen))), + mem_end); + io_end = MAX((MIN(io_reqd, (io_answer + io_alen))), io_end); + *highest_bus = MAX((MIN(highest_bus_reqd, max_bus)), + *highest_bus); + DEBUG3("mem_end %lx, io_end %lx, highest_bus %x\n", + mem_end, io_end, *highest_bus); + } + + /* + * Give back unused memory space to parent. + */ + (void) ndi_ra_free(ddi_get_parent(new_child), + mem_end, (mem_answer + mem_alen) - mem_end, NDI_RA_TYPE_MEM, + NDI_RA_PASS); + + if (mem_end == mem_answer) { + DEBUG0("No memory resources used\n"); + /* + * To prevent the bridge from forwarding any Memory + * transactions, the Memory Limit will be programmed + * with a smaller value than the Memory Base. + */ + pci_config_put16(h, PCI_BCNF_MEM_BASE, 0xffff); + pci_config_put16(h, PCI_BCNF_MEM_LIMIT, 0); + + mem_size = 0; + } else { + /* + * Reprogram the end of the memory. + */ + pci_config_put16(h, PCI_BCNF_MEM_LIMIT, + PCICFG_HIWORD(mem_end) - 1); + mem_size = mem_end - mem_base; + } + + /* + * Give back unused io space to parent. + */ + (void) ndi_ra_free(ddi_get_parent(new_child), + io_end, (io_answer + io_alen) - io_end, + NDI_RA_TYPE_IO, NDI_RA_PASS); + + if (io_end == io_answer) { + DEBUG0("No IO Space resources used\n"); + + /* + * To prevent the bridge from forwarding any I/O + * transactions, the I/O Limit will be programmed + * with a smaller value than the I/O Base. + */ + pci_config_put8(h, PCI_BCNF_IO_LIMIT_LOW, 0); + pci_config_put16(h, PCI_BCNF_IO_LIMIT_HI, 0); + pci_config_put8(h, PCI_BCNF_IO_BASE_LOW, 0xff); + pci_config_put16(h, PCI_BCNF_IO_BASE_HI, 0); + + io_size = 0; + } else { + /* + * Reprogram the end of the io space. + */ + pci_config_put8(h, PCI_BCNF_IO_LIMIT_LOW, + PCICFG_HIBYTE(PCICFG_LOWORD( + PCICFG_LOADDR(io_end) - 1))); + + pci_config_put16(h, PCI_BCNF_IO_LIMIT_HI, + PCICFG_HIWORD(PCICFG_LOADDR(io_end - 1))); + + io_size = io_end - io_base; + } + + if ((max_bus - *highest_bus) > 0) { + /* + * Give back unused bus numbers + */ + (void) ndi_ra_free(ddi_get_parent(new_child), + *highest_bus+1, max_bus - *highest_bus, + NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS); + } + + /* + * Set bus numbers to ranges encountered during scan + */ + (void) pcicfg_set_bus_numbers(h, bus, new_bus, *highest_bus); + + /* + * Remove the ranges property if it exists since we will create + * a new one. + */ + (void) ndi_prop_remove(DDI_DEV_T_NONE, new_child, "ranges"); + + DEBUG2("Creating Ranges property - Mem Address %lx Mem Size %x\n", + mem_base, mem_size); + DEBUG2(" - I/O Address %lx I/O Size %x\n", + io_base, io_size); + + bzero((caddr_t)range, sizeof (pcicfg_range_t) * PCICFG_RANGE_LEN); + + range[0].child_hi = range[0].parent_hi |= (PCI_REG_REL_M | PCI_ADDR_IO); + range[0].child_lo = range[0].parent_lo = io_base; + range[1].child_hi = range[1].parent_hi |= + (PCI_REG_REL_M | PCI_ADDR_MEM32); + range[1].child_lo = range[1].parent_lo = mem_base; + + if (io_size > 0) { + range[0].size_lo = io_size; + if (pcicfg_update_ranges_prop(new_child, &range[0])) { + DEBUG0("Failed to update ranges (io)\n"); + return (PCICFG_FAILURE); + } + } + if (mem_size > 0) { + range[1].size_lo = mem_size; + if (pcicfg_update_ranges_prop(new_child, &range[1])) { + DEBUG0("Failed to update ranges (memory)\n"); + return (PCICFG_FAILURE); + } + } + + bus_range[0] = pci_config_get8(h, PCI_BCNF_SECBUS); + bus_range[1] = pci_config_get8(h, PCI_BCNF_SUBBUS); + DEBUG1("End of bridge probe: bus_range[0] = %d\n", bus_range[0]); + DEBUG1("End of bridge probe: bus_range[1] = %d\n", bus_range[1]); + + if (ndi_prop_update_int_array(DDI_DEV_T_NONE, new_child, + "bus-range", bus_range, 2) != DDI_SUCCESS) { + DEBUG0("Failed to set bus-range property"); + return (PCICFG_FAILURE); + } + /* + * Remove the resource maps for the bridge since we no longer + * need them. Note that the failure is ignored since the + * ndi_devi_offline above may have already taken care of it via + * driver detach. + * It has been checked that there are no other reasons for + * failure other than map itself being non-existent. So we are Ok. + */ + if (ndi_ra_map_destroy(new_child, NDI_RA_TYPE_MEM) == NDI_FAILURE) { + /*EMPTY*/ + DEBUG0("Can not destroy resource map - NDI_RA_TYPE_MEM\n"); + } + + if (ndi_ra_map_destroy(new_child, NDI_RA_TYPE_IO) == NDI_FAILURE) { + /*EMPTY*/ + DEBUG0("Can not destroy resource map - NDI_RA_TYPE_IO\n"); + } + + if (ndi_ra_map_destroy(new_child, NDI_RA_TYPE_PCI_BUSNUM) + == NDI_FAILURE) { + /*EMPTY*/ + DEBUG0("Can't destroy resource map - NDI_RA_TYPE_PCI_BUSNUM\n"); + } + + return (rval); +} + +/* + * Return PCICFG_SUCCESS if device exists at the specified address. + * Return PCICFG_NODEVICE is no device exists at the specified address. + * + */ +int +pcicfg_config_setup(dev_info_t *dip, ddi_acc_handle_t *handle) +{ + caddr_t virt; + ddi_device_acc_attr_t attr; + int status; + int rlen; + pci_regspec_t *reg; + int ret = DDI_SUCCESS; + int16_t tmp; + /* + * flags = PCICFG_CONF_INDIRECT_MAP if configuration space is indirectly + * mapped, otherwise it is 0. "flags" is introduced in support of any + * non transparent bridges, where configuration space is indirectly + * mapped. + * Indirect mapping is always true on sun4v systems. + */ + int flags = 0; + + + /* + * Get the pci register spec from the node + */ + status = ddi_getlongprop(DDI_DEV_T_ANY, + dip, DDI_PROP_DONTPASS, "reg", (caddr_t)®, &rlen); + + switch (status) { + case DDI_PROP_SUCCESS: + break; + case DDI_PROP_NO_MEMORY: + DEBUG0("reg present, but unable to get memory\n"); + return (PCICFG_FAILURE); + default: + DEBUG0("no reg property\n"); + return (PCICFG_FAILURE); + } + + if (pcicfg_indirect_map(dip) == DDI_SUCCESS) + flags |= PCICFG_CONF_INDIRECT_MAP; + + /* + * Map in configuration space (temporarily) + */ + attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; + attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; + attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + attr.devacc_attr_access = DDI_CAUTIOUS_ACC; + +#ifdef EFCODE21554 + if (ddi_regs_map_setup(dip, 0, &virt, + 0, 0, &attr, handle) != DDI_SUCCESS) +#else + if (pcicfg_map_phys(dip, reg, &virt, &attr, handle) + != DDI_SUCCESS) +#endif + { + DEBUG0("pcicfg_config_setup():" + "Failed to setup config space\n"); + + kmem_free((caddr_t)reg, rlen); + return (PCICFG_FAILURE); + } + + if (flags & PCICFG_CONF_INDIRECT_MAP) { + /* + * need to use DDI interfaces as the conf space is + * cannot be directly accessed by the host. + */ + tmp = (int16_t)ddi_get16(*handle, (uint16_t *)virt); + } else { + ret = ddi_peek16(dip, (int16_t *)virt, &tmp); + } + + if (ret == DDI_SUCCESS) { + if (tmp == -1) { + DEBUG1("NO DEVICEFOUND, read %x\n", tmp); + ret = PCICFG_NODEVICE; + } else { + /* XXX - Need to check why HV is returning 0 */ + if (tmp == 0) { + DEBUG0("Device Not Ready yet ?"); + ret = PCICFG_NODEVICE; + } else { + DEBUG1("DEVICEFOUND, read %x\n", tmp); + ret = PCICFG_SUCCESS; + } + } + } else { + DEBUG0("ddi_peek failed, must be NODEVICE\n"); + ret = PCICFG_NODEVICE; + } + + /* + * A bug in XMITS 3.0 causes us to miss the Master Abort Split + * Completion message. The result is the error message being + * sent back as part of the config data. If the first two words + * of the config space happen to be the same as the Master Abort + * message, then report back that there is no device there. + */ + if ((ret == PCICFG_SUCCESS) && !(flags & PCICFG_CONF_INDIRECT_MAP)) { + int32_t pcix_scm; + +#define PCICFG_PCIX_SCM 0x10000004 + + pcix_scm = 0; + (void) ddi_peek32(dip, (int32_t *)virt, &pcix_scm); + if (pcix_scm == PCICFG_PCIX_SCM) { + pcix_scm = 0; + (void) ddi_peek32(dip, + (int32_t *)(virt + 4), &pcix_scm); + if (pcix_scm == PCICFG_PCIX_SCM) + ret = PCICFG_NODEVICE; + } + } + + if (ret == PCICFG_NODEVICE) +#ifdef EFCODE21554 + ddi_regs_map_free(handle); +#else + pcicfg_unmap_phys(handle, reg); +#endif + + kmem_free((caddr_t)reg, rlen); + + return (ret); + +} + +static void +pcicfg_config_teardown(ddi_acc_handle_t *handle) +{ + (void) ddi_regs_map_free(handle); +} + +static int +pcicfg_add_config_reg(dev_info_t *dip, + uint_t bus, uint_t device, uint_t func) +{ + int reg[10] = { PCI_ADDR_CONFIG, 0, 0, 0, 0}; + + reg[0] = PCICFG_MAKE_REG_HIGH(bus, device, func, 0); + + return (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, + "reg", reg, 5)); +} + +static int +pcicfg_dump_assigned(dev_info_t *dip) +{ + pci_regspec_t *reg; + int length; + int rcount; + int i; + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)®, + &length) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read assigned-addresses property\n"); + return (PCICFG_FAILURE); + } + + rcount = length / sizeof (pci_regspec_t); + for (i = 0; i < rcount; i++) { + DEBUG4("pcicfg_dump_assigned - size=%x low=%x mid=%x high=%x\n", + reg[i].pci_size_low, reg[i].pci_phys_low, + reg[i].pci_phys_mid, reg[i].pci_phys_hi); + } + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)reg, length); + + return (PCICFG_SUCCESS); +} + +#ifdef PCICFG_INTERPRET_FCODE +static int +pcicfg_load_fcode(dev_info_t *dip, uint_t bus, uint_t device, uint_t func, + uint16_t vendor_id, uint16_t device_id, uchar_t **fcode_addr, + int *fcode_size, int rom_paddr, int rom_size) +{ + pci_regspec_t p; + int pci_data; + int start_of_fcode; + int image_length; + int code_type; + ddi_acc_handle_t h; + ddi_device_acc_attr_t acc; + uint8_t *addr; + int8_t image_not_found, indicator; + uint16_t vendor_id_img, device_id_img; + int16_t rom_sig; +#ifdef DEBUG + int i; +#endif + + DEBUG4("pcicfg_load_fcode() - " + "bus %x device =%x func=%x rom_paddr=%lx\n", + bus, device, func, rom_paddr); + DEBUG2("pcicfg_load_fcode() - vendor_id=%x device_id=%x\n", + vendor_id, device_id); + + *fcode_size = 0; + *fcode_addr = NULL; + + acc.devacc_attr_version = DDI_DEVICE_ATTR_V0; + acc.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; + acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + + p.pci_phys_hi = PCI_ADDR_MEM32 | PCICFG_MAKE_REG_HIGH(bus, device, + func, PCI_CONF_ROM); + + p.pci_phys_mid = 0; + p.pci_phys_low = 0; + + p.pci_size_low = rom_size; + p.pci_size_hi = 0; + + if (pcicfg_map_phys(dip, &p, (caddr_t *)&addr, &acc, &h)) { + DEBUG1("Can Not map in ROM %x\n", p.pci_phys_low); + return (PCICFG_FAILURE); + } + + /* + * Walk the ROM to find the proper image for this device. + */ + image_not_found = 1; + while (image_not_found) { + DEBUG1("Expansion ROM maps to %lx\n", addr); + +#ifdef DEBUG + if (pcicfg_dump_fcode) { + for (i = 0; i < 100; i++) + DEBUG2("ROM 0x%x --> 0x%x\n", i, + ddi_get8(h, (uint8_t *)(addr + i))); + } +#endif + + /* + * Some device say they have an Expansion ROM, but do not, so + * for non-21554 devices use peek so we don't panic due to + * accessing non existent memory. + */ + if (pcicfg_indirect_map(dip) == DDI_SUCCESS) { + rom_sig = ddi_get16(h, + (uint16_t *)(addr + PCI_ROM_SIGNATURE)); + } else { + if (ddi_peek16(dip, + (int16_t *)(addr + PCI_ROM_SIGNATURE), &rom_sig)) { + cmn_err(CE_WARN, + "PCI Expansion ROM is not accessible"); + pcicfg_unmap_phys(&h, &p); + return (PCICFG_FAILURE); + } + } + + /* + * Validate the ROM Signature. + */ + if ((uint16_t)rom_sig != 0xaa55) { + DEBUG1("Invalid ROM Signature %x\n", (uint16_t)rom_sig); + pcicfg_unmap_phys(&h, &p); + return (PCICFG_FAILURE); + } + + DEBUG0("Valid ROM Signature Found\n"); + + start_of_fcode = ddi_get16(h, (uint16_t *)(addr + 2)); + + pci_data = ddi_get16(h, + (uint16_t *)(addr + PCI_ROM_PCI_DATA_STRUCT_PTR)); + + DEBUG2("Pointer To PCI Data Structure %x %x\n", pci_data, + addr); + + /* + * Validate the PCI Data Structure Signature. + * 0x52494350 = "PCIR" + */ + + if (ddi_get8(h, (uint8_t *)(addr + pci_data)) != 0x50) { + DEBUG0("Invalid PCI Data Structure Signature\n"); + pcicfg_unmap_phys(&h, &p); + return (PCICFG_FAILURE); + } + + if (ddi_get8(h, (uint8_t *)(addr + pci_data + 1)) != 0x43) { + DEBUG0("Invalid PCI Data Structure Signature\n"); + pcicfg_unmap_phys(&h, &p); + return (PCICFG_FAILURE); + } + if (ddi_get8(h, (uint8_t *)(addr + pci_data + 2)) != 0x49) { + DEBUG0("Invalid PCI Data Structure Signature\n"); + pcicfg_unmap_phys(&h, &p); + return (PCICFG_FAILURE); + } + if (ddi_get8(h, (uint8_t *)(addr + pci_data + 3)) != 0x52) { + DEBUG0("Invalid PCI Data Structure Signature\n"); + pcicfg_unmap_phys(&h, &p); + return (PCICFG_FAILURE); + } + + /* + * Is this image for this device? + */ + vendor_id_img = ddi_get16(h, + (uint16_t *)(addr + pci_data + PCI_PDS_VENDOR_ID)); + device_id_img = ddi_get16(h, + (uint16_t *)(addr + pci_data + PCI_PDS_DEVICE_ID)); + + DEBUG2("This image is for vendor_id=%x device_id=%x\n", + vendor_id_img, device_id_img); + + code_type = ddi_get8(h, addr + pci_data + PCI_PDS_CODE_TYPE); + + switch (code_type) { + case PCI_PDS_CODE_TYPE_PCAT: + DEBUG0("ROM is of x86/PC-AT Type\n"); + break; + case PCI_PDS_CODE_TYPE_OPEN_FW: + DEBUG0("ROM is of Open Firmware Type\n"); + break; + default: + DEBUG1("ROM is of Unknown Type 0x%x\n", code_type); + break; + } + + if ((vendor_id_img != vendor_id) || + (device_id_img != device_id) || + (code_type != PCI_PDS_CODE_TYPE_OPEN_FW)) { + DEBUG0("Firmware Image is not for this device..." + "goto next image\n"); + /* + * Read indicator byte to see if there is another + * image in the ROM + */ + indicator = ddi_get8(h, + (uint8_t *)(addr + pci_data + PCI_PDS_INDICATOR)); + + if (indicator != 1) { + /* + * There is another image in the ROM. + */ + image_length = ddi_get16(h, (uint16_t *)(addr + + pci_data + PCI_PDS_IMAGE_LENGTH)) * 512; + + addr += image_length; + } else { + /* + * There are no more images. + */ + DEBUG0("There are no more images in the ROM\n"); + pcicfg_unmap_phys(&h, &p); + + return (PCICFG_FAILURE); + } + } else { + DEBUG0("Correct image was found\n"); + image_not_found = 0; /* Image was found */ + } + } + + *fcode_size = (ddi_get8(h, addr + start_of_fcode + 4) << 24) | + (ddi_get8(h, addr + start_of_fcode + 5) << 16) | + (ddi_get8(h, addr + start_of_fcode + 6) << 8) | + (ddi_get8(h, addr + start_of_fcode + 7)); + + DEBUG1("Fcode Size %x\n", *fcode_size); + + /* + * Allocate page aligned buffer space + */ + *fcode_addr = kmem_zalloc(ptob(btopr(*fcode_size)), KM_SLEEP); + + if (*fcode_addr == NULL) { + DEBUG0("kmem_zalloc returned NULL\n"); + pcicfg_unmap_phys(&h, &p); + return (PCICFG_FAILURE); + } + + DEBUG1("Fcode Addr %lx\n", *fcode_addr); + + ddi_rep_get8(h, *fcode_addr, addr + start_of_fcode, *fcode_size, + DDI_DEV_AUTOINCR); + + pcicfg_unmap_phys(&h, &p); + + return (PCICFG_SUCCESS); +} + +static int +pcicfg_fcode_assign_bars(ddi_acc_handle_t h, dev_info_t *dip, uint_t bus, + uint_t device, uint_t func, int32_t fc_request, pci_regspec_t *rom_regspec) +{ + /* + * Assign values to all BARs so that it is safe to turn on the + * device for accessing the fcode on the PROM. On successful + * exit from this function, "assigned-addresses" are created + * for all BARs and ROM BAR is enabled. Also, rom_regspec is + * filled with the values that can be used to free up this + * resource later. + */ + uint32_t request, hiword, size; + pci_regspec_t phys_spec; + ndi_ra_request_t req; + uint64_t mem_answer, mem_alen; + int i; + + DEBUG1("pcicfg_fcode_assign_bars :%s\n", DEVI(dip)->devi_name); + + /* + * Process the BARs. + */ + for (i = PCI_CONF_BASE0; i <= PCI_CONF_BASE5; ) { + pci_config_put32(h, i, 0xffffffff); + request = pci_config_get32(h, i); + /* + * Check if implemented + */ + if (request == 0) { + DEBUG1("pcicfg_fcode_assign_bars :" + "BASE register [0x%x] asks for 0(32)\n", i); + i += 4; + continue; + } + /* + * Build the phys_spec for this BAR + */ + hiword = PCICFG_MAKE_REG_HIGH(bus, device, func, i); + size = (~(PCI_BASE_M_ADDR_M & request)) + 1; + + DEBUG3("pcicfg_fcode_assign_bars :" + "BASE register [0x%x] asks for [0x%x]=[0x%x]\n", + i, request, size); + + if ((PCI_BASE_SPACE_M & request) == PCI_BASE_SPACE_MEM) { + if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_MEM) { + hiword |= PCI_ADDR_MEM32; + } else if ((PCI_BASE_TYPE_M & request) + == PCI_BASE_TYPE_ALL) { + hiword |= PCI_ADDR_MEM64; + } + if (request & PCI_BASE_PREF_M) + hiword |= PCI_REG_PF_M; + } else { + hiword |= PCI_ADDR_IO; + } + phys_spec.pci_phys_hi = hiword; + phys_spec.pci_phys_mid = 0; + phys_spec.pci_phys_low = 0; + phys_spec.pci_size_hi = 0; + phys_spec.pci_size_low = size; + + /* + * The following function + * - allocates address space + * - programs the BAR + * - adds an "assigned-addresses" property + */ + if (pcicfg_alloc_resource(dip, phys_spec)) { + cmn_err(CE_WARN, "failed to allocate %d bytes" + " for dev %s BASE register [0x%x]\n", + size, DEVI(dip)->devi_name, i); + goto failure; + } + if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) { + /* + * 64 bit, should be in memory space. + */ + i += 8; + } else { + /* + * 32 bit, either memory or I/O space. + */ + i += 4; + } + } + + /* + * Handle ROM BAR. We do not use the common + * resource allocator function because we need to + * return reg spec to the caller. + */ + size = (~(PCI_BASE_ROM_ADDR_M & fc_request)) + 1; + + DEBUG3("BASE register [0x%x] asks for " + "[0x%x]=[0x%x]\n", PCI_CONF_ROM, fc_request, size); + + bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); + + req.ra_boundbase = 0; + req.ra_boundlen = PCICFG_4GIG_LIMIT; + req.ra_len = size; + req.ra_flags |= NDI_RA_ALIGN_SIZE; + req.ra_flags ^= NDI_RA_ALLOC_BOUNDED; + + if (ndi_ra_alloc(ddi_get_parent(dip), + &req, &mem_answer, &mem_alen, + NDI_RA_TYPE_MEM, NDI_RA_PASS)) { + cmn_err(CE_WARN, "failed to allocate %d bytes" + " for dev %s ROM BASE register\n", + size, DEVI(dip)->devi_name); + goto failure; + } + + DEBUG3("ROM addr = [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(mem_answer), + PCICFG_LOADDR(mem_answer), mem_alen); + + /* + * Assign address space and enable ROM. + */ + pci_config_put32(h, PCI_CONF_ROM, + PCICFG_LOADDR(mem_answer) | PCI_BASE_ROM_ENABLE); + + /* + * Add resource to assigned-addresses. + */ + phys_spec.pci_phys_hi = PCICFG_MAKE_REG_HIGH(bus, device, func, \ + PCI_CONF_ROM) | PCI_ADDR_MEM32; + if (fc_request & PCI_BASE_PREF_M) + phys_spec.pci_phys_hi |= PCI_REG_PF_M; + phys_spec.pci_phys_mid = 0; + phys_spec.pci_phys_low = PCICFG_LOADDR(mem_answer); + phys_spec.pci_size_hi = 0; + phys_spec.pci_size_low = size; + + if (pcicfg_update_assigned_prop(dip, &phys_spec) + != PCICFG_SUCCESS) { + cmn_err(CE_WARN, "failed to update" + " assigned-address property for dev %s\n", + DEVI(dip)->devi_name); + goto failure; + } + /* + * Copy out the reg spec. + */ + *rom_regspec = phys_spec; + + return (PCICFG_SUCCESS); + +failure: + /* + * We came in with no "assigned-addresses". + * Free up the resources we may have allocated. + */ + (void) pcicfg_free_device_resources(dip); + + return (PCICFG_FAILURE); +} + +#endif /* PCICFG_INTERPRET_FCODE */ + +static int +pcicfg_free_all_resources(dev_info_t *dip) +{ + pci_regspec_t *assigned; + int assigned_len; + int acount; + int i; + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned, + &assigned_len) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read assigned-addresses property\n"); + return (PCICFG_FAILURE); + } + + acount = assigned_len / sizeof (pci_regspec_t); + + for (i = 0; i < acount; i++) { + if (pcicfg_free_resource(dip, assigned[i])) { + /* + * Dont forget to free mem from ddi_getlongprop + */ + kmem_free((caddr_t)assigned, assigned_len); + return (PCICFG_FAILURE); + } + } + + /* + * Don't forget to free up memory from ddi_getlongprop + */ + if (assigned_len) + kmem_free((caddr_t)assigned, assigned_len); + + return (PCICFG_SUCCESS); +} +static int +pcicfg_alloc_new_resources(dev_info_t *dip) +{ + pci_regspec_t *assigned, *reg; + int assigned_len, reg_len; + int acount, rcount; + int i, j, alloc_size; + boolean_t alloc; + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "reg", (caddr_t)®, + ®_len) != DDI_PROP_SUCCESS) { + DEBUG0("Failed to read reg property\n"); + return (PCICFG_FAILURE); + } + rcount = reg_len / sizeof (pci_regspec_t); + + DEBUG2("pcicfg_alloc_new_resources() reg size=%x entries=%x\n", + reg_len, rcount); + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned, + &assigned_len) != DDI_PROP_SUCCESS) { + acount = 0; + } else { + acount = assigned_len / sizeof (pci_regspec_t); + } + + DEBUG1("assigned-addresses property len=%x\n", acount); + + /* + * For each address described by reg, search for it in the + * assigned-addresses property. If it does not exist, allocate + * resources for it. If it does exist, check the size in both. + * The size needs to be bigger of the two. + */ + for (i = 1; i < rcount; i++) { + alloc = B_TRUE; + alloc_size = reg[i].pci_size_low; + for (j = 0; j < acount; j++) { + if (assigned[j].pci_phys_hi == reg[i].pci_phys_hi) { + /* + * There is an exact match. Check size. + */ + DEBUG1("pcicfg_alloc_new_resources " + "- %x - MATCH\n", + reg[i].pci_phys_hi); + + if (reg[i].pci_size_low > + assigned[j].pci_size_low) { + /* + * Fcode wants more. + */ + DEBUG3("pcicfg_alloc_new_resources" + " - %x - RESIZE" + " assigned 0x%x reg 0x%x\n", + assigned[j].pci_phys_hi, + assigned[j].pci_size_low, + reg[i].pci_size_low); + + /* + * Free the old resource. + */ + (void) pcicfg_free_resource(dip, + assigned[j]); + } else { + DEBUG3("pcicfg_alloc_new_resources" + " - %x - ENOUGH" + " assigned 0x%x reg 0x%x\n", + assigned[j].pci_phys_hi, + assigned[j].pci_size_low, + reg[i].pci_size_low); + + alloc = B_FALSE; + } + break; + } + /* + * Fcode may have set one or more of the + * NPT bits in phys.hi. + */ + if (PCI_REG_BDFR_G(assigned[j].pci_phys_hi) == + PCI_REG_BDFR_G(reg[i].pci_phys_hi)) { + + DEBUG2("pcicfg_alloc_new_resources " + "- PARTIAL MATCH assigned 0x%x " + "reg 0x%x\n", assigned[j].pci_phys_hi, + reg[i].pci_phys_hi); + /* + * Changing the SS bits is an error + */ + if (PCI_REG_ADDR_G( + assigned[j].pci_phys_hi) != + PCI_REG_ADDR_G(reg[i].pci_phys_hi)) { + + cmn_err(CE_WARN, "Fcode changing" + " SS bits of - 0x%x -" + " on %s\n", reg[i].pci_phys_hi, + DEVI(dip)->devi_name); + + /* + * Dont forget to free mem from + * ddi_getlongprop + */ + if (acount != 0) + kmem_free((caddr_t)assigned, + assigned_len); + kmem_free((caddr_t)reg, reg_len); + return (PCICFG_FAILURE); + } + + + /* + * We are going to allocate new resource. + * Free the old resource. Again, adjust + * the size to be safe. + */ + (void) pcicfg_free_resource(dip, assigned[j]); + + alloc_size = MAX(reg[i].pci_size_low, + assigned[j].pci_size_low); + + break; + } + } + /* + * We are allocating resources for one of three reasons - + * - Fcode wants a larger address space + * - Fcode has set changed/set n, p, t bits. + * - It is a new "reg", it should be only ROM bar, but + * we don't do the checking. + */ + if (alloc == B_TRUE) { + DEBUG1("pcicfg_alloc_new_resources : creating 0x%x\n", + reg[i].pci_phys_hi); + + reg[i].pci_size_low = alloc_size; + if (pcicfg_alloc_resource(dip, reg[i])) { + /* + * Dont forget to free mem from + * ddi_getlongprop + */ + if (acount != 0) + kmem_free((caddr_t)assigned, assigned_len); + kmem_free((caddr_t)reg, reg_len); + return (PCICFG_FAILURE); + } + } + } + + /* + * Don't forget to free up memory from ddi_getlongprop + */ + if (acount != 0) + kmem_free((caddr_t)assigned, assigned_len); + kmem_free((caddr_t)reg, reg_len); + + return (PCICFG_SUCCESS); +} +static int +pcicfg_alloc_resource(dev_info_t *dip, pci_regspec_t phys_spec) +{ + uint64_t answer; + uint64_t alen; + int offset; + pci_regspec_t config; + caddr_t virt, v; + ddi_device_acc_attr_t acc; + ddi_acc_handle_t h; + ndi_ra_request_t request; + pci_regspec_t *assigned; + int assigned_len, entries, i; + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned, + &assigned_len) == DDI_PROP_SUCCESS) { + DEBUG0("pcicfg_alloc_resource - " + "searching assigned-addresses\n"); + + entries = assigned_len / (sizeof (pci_regspec_t)); + + /* + * Walk through the assigned-addresses entries. If there is + * a match, there is no need to allocate the resource. + */ + for (i = 0; i < entries; i++) { + if (assigned[i].pci_phys_hi == phys_spec.pci_phys_hi) { + DEBUG1("pcicfg_alloc_resource - MATCH %x\n", + assigned[i].pci_phys_hi); + kmem_free(assigned, assigned_len); + return (0); + } + } + kmem_free(assigned, assigned_len); + } + + bzero((caddr_t)&request, sizeof (ndi_ra_request_t)); + + config.pci_phys_hi = PCI_CONF_ADDR_MASK & phys_spec.pci_phys_hi; + config.pci_phys_hi &= ~PCI_REG_REG_M; + config.pci_phys_mid = config.pci_phys_low = 0; + config.pci_size_hi = config.pci_size_low = 0; + + /* + * Map in configuration space (temporarily) + */ + acc.devacc_attr_version = DDI_DEVICE_ATTR_V0; + acc.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; + acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + + if (pcicfg_map_phys(dip, &config, &virt, &acc, &h)) { + DEBUG0("Can not map in config space\n"); + return (1); + } + + request.ra_flags |= NDI_RA_ALIGN_SIZE; + request.ra_boundbase = 0; + request.ra_boundlen = PCICFG_4GIG_LIMIT; + /* + * Use size stored in phys_spec parameter. + */ + request.ra_len = phys_spec.pci_size_low; + + offset = PCI_REG_REG_G(phys_spec.pci_phys_hi); + + v = virt + offset; + + if (PCI_REG_REG_G(phys_spec.pci_phys_hi) == PCI_CONF_ROM) { + + request.ra_flags ^= NDI_RA_ALLOC_BOUNDED; + + /* allocate memory space from the allocator */ + + if (ndi_ra_alloc(ddi_get_parent(dip), + &request, &answer, &alen, + NDI_RA_TYPE_MEM, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("(ROM)Failed to allocate 32b mem"); + pcicfg_unmap_phys(&h, &config); + return (1); + } + DEBUG3("ROM addr = [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(answer), + PCICFG_LOADDR(answer), + alen); + + /* program the low word */ + + ddi_put32(h, (uint32_t *)v, (uint32_t)PCICFG_LOADDR(answer)); + + phys_spec.pci_phys_low = PCICFG_LOADDR(answer); + phys_spec.pci_phys_mid = PCICFG_HIADDR(answer); + } else { + + switch (PCI_REG_ADDR_G(phys_spec.pci_phys_hi)) { + case PCI_REG_ADDR_G(PCI_ADDR_MEM64): + request.ra_flags ^= NDI_RA_ALLOC_BOUNDED; + /* allocate memory space from the allocator */ + if (ndi_ra_alloc(ddi_get_parent(dip), + &request, &answer, &alen, + NDI_RA_TYPE_MEM, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("Failed to allocate 64b mem\n"); + pcicfg_unmap_phys(&h, &config); + return (1); + } + DEBUG3("64 addr = [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(answer), + PCICFG_LOADDR(answer), + alen); + + /* program the low word */ + + ddi_put32(h, (uint32_t *)v, + (uint32_t)PCICFG_LOADDR(answer)); + + /* program the high word with value zero */ + v += 4; + ddi_put32(h, (uint32_t *)v, + (uint32_t)PCICFG_HIADDR(answer)); + + phys_spec.pci_phys_low = PCICFG_LOADDR(answer); + phys_spec.pci_phys_mid = PCICFG_HIADDR(answer); + + break; + + case PCI_REG_ADDR_G(PCI_ADDR_MEM32): + request.ra_flags |= NDI_RA_ALLOC_BOUNDED; + /* allocate memory space from the allocator */ + if (ndi_ra_alloc(ddi_get_parent(dip), + &request, &answer, &alen, + NDI_RA_TYPE_MEM, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("Failed to allocate 32b mem\n"); + pcicfg_unmap_phys(&h, &config); + return (1); + } + + DEBUG3("32 addr = [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(answer), + PCICFG_LOADDR(answer), + alen); + + /* program the low word */ + + ddi_put32(h, (uint32_t *)v, + (uint32_t)PCICFG_LOADDR(answer)); + + phys_spec.pci_phys_low = PCICFG_LOADDR(answer); + + break; + case PCI_REG_ADDR_G(PCI_ADDR_IO): + /* allocate I/O space from the allocator */ + request.ra_flags |= NDI_RA_ALLOC_BOUNDED; + if (ndi_ra_alloc(ddi_get_parent(dip), + &request, &answer, &alen, + NDI_RA_TYPE_IO, NDI_RA_PASS) + != NDI_SUCCESS) { + DEBUG0("Failed to allocate I/O\n"); + pcicfg_unmap_phys(&h, &config); + return (1); + } + DEBUG3("I/O addr = [0x%x.%x] len [0x%x]\n", + PCICFG_HIADDR(answer), + PCICFG_LOADDR(answer), + alen); + + ddi_put32(h, (uint32_t *)v, + (uint32_t)PCICFG_LOADDR(answer)); + + phys_spec.pci_phys_low = PCICFG_LOADDR(answer); + + break; + default: + DEBUG0("Unknown register type\n"); + pcicfg_unmap_phys(&h, &config); + return (1); + } /* switch */ + } + + /* + * Now that memory locations are assigned, + * update the assigned address property. + */ + + DEBUG1("updating assigned-addresss for %x\n", phys_spec.pci_phys_hi); + + if (pcicfg_update_assigned_prop(dip, &phys_spec)) { + pcicfg_unmap_phys(&h, &config); + return (1); + } + + pcicfg_unmap_phys(&h, &config); + + return (0); +} + +static int +pcicfg_free_resource(dev_info_t *dip, pci_regspec_t phys_spec) +{ + int offset; + pci_regspec_t config; + caddr_t virt, v; + ddi_device_acc_attr_t acc; + ddi_acc_handle_t h; + ndi_ra_request_t request; + int l; + + bzero((caddr_t)&request, sizeof (ndi_ra_request_t)); + + config.pci_phys_hi = PCI_CONF_ADDR_MASK & phys_spec.pci_phys_hi; + config.pci_phys_hi &= ~PCI_REG_REG_M; + config.pci_phys_mid = config.pci_phys_low = 0; + config.pci_size_hi = config.pci_size_low = 0; + + /* + * Map in configuration space (temporarily) + */ + acc.devacc_attr_version = DDI_DEVICE_ATTR_V0; + acc.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; + acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC; + + if (pcicfg_map_phys(dip, &config, &virt, &acc, &h)) { + DEBUG0("Can not map in config space\n"); + return (1); + } + + offset = PCI_REG_REG_G(phys_spec.pci_phys_hi); + + v = virt + offset; + + /* + * Use size stored in phys_spec parameter. + */ + l = phys_spec.pci_size_low; + + if (PCI_REG_REG_G(phys_spec.pci_phys_hi) == PCI_CONF_ROM) { + + /* free memory back to the allocator */ + if (ndi_ra_free(ddi_get_parent(dip), phys_spec.pci_phys_low, + l, NDI_RA_TYPE_MEM, NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("(ROM)Can not free 32b mem"); + pcicfg_unmap_phys(&h, &config); + return (1); + } + + /* Unmap the BAR by writing a zero */ + + ddi_put32(h, (uint32_t *)v, (uint32_t)0); + } else { + + switch (PCI_REG_ADDR_G(phys_spec.pci_phys_hi)) { + case PCI_REG_ADDR_G(PCI_ADDR_MEM64): + /* free memory back to the allocator */ + if (ndi_ra_free(ddi_get_parent(dip), + PCICFG_LADDR(phys_spec.pci_phys_low, + phys_spec.pci_phys_mid), + l, NDI_RA_TYPE_MEM, + NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("Can not free 64b mem"); + pcicfg_unmap_phys(&h, &config); + return (1); + } + + break; + + case PCI_REG_ADDR_G(PCI_ADDR_MEM32): + /* free memory back to the allocator */ + if (ndi_ra_free(ddi_get_parent(dip), + phys_spec.pci_phys_low, + l, NDI_RA_TYPE_MEM, + NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("Can not free 32b mem"); + pcicfg_unmap_phys(&h, &config); + return (1); + } + + break; + case PCI_REG_ADDR_G(PCI_ADDR_IO): + /* free I/O space back to the allocator */ + if (ndi_ra_free(ddi_get_parent(dip), + phys_spec.pci_phys_low, + l, NDI_RA_TYPE_IO, + NDI_RA_PASS) != NDI_SUCCESS) { + DEBUG0("Can not free I/O space"); + pcicfg_unmap_phys(&h, &config); + return (1); + } + + break; + default: + DEBUG0("Unknown register type\n"); + pcicfg_unmap_phys(&h, &config); + return (1); + } /* switch */ + } + + /* + * Now that memory locations are assigned, + * update the assigned address property. + */ + + DEBUG1("updating assigned-addresss for %x\n", phys_spec.pci_phys_hi); + + if (pcicfg_remove_assigned_prop(dip, &phys_spec)) { + pcicfg_unmap_phys(&h, &config); + return (1); + } + + pcicfg_unmap_phys(&h, &config); + + return (0); +} + +static int +pcicfg_remove_assigned_prop(dev_info_t *dip, pci_regspec_t *oldone) +{ + int alen, num_entries, i; + pci_regspec_t *assigned, *assigned_copy; + uint_t status; + + status = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "assigned-addresses", (caddr_t)&assigned, &alen); + switch (status) { + case DDI_PROP_SUCCESS: + break; + case DDI_PROP_NO_MEMORY: + DEBUG0("no memory for assigned-addresses property\n"); + return (1); + default: + DEBUG0("assigned-addresses property does not exist\n"); + return (0); + } + + /* + * Make a copy of old assigned-addresses property. + */ + assigned_copy = kmem_alloc(alen, KM_SLEEP); + bcopy(assigned, assigned_copy, alen); + + status = ndi_prop_remove(DDI_DEV_T_NONE, dip, "assigned-addresses"); + + if (status != DDI_PROP_SUCCESS) { + /* + * If "assigned-addresses" is retrieved from PROM, the + * ndi_prop_remove() will fail. + */ + DEBUG1("pcicfg_remove_assigned_prop: 0x%x not removed\n", + oldone->pci_phys_hi); + + /* + * Free up allocated memory + */ + kmem_free(assigned_copy, alen); + kmem_free((caddr_t)assigned, alen); + + return (0); + } + + num_entries = alen / sizeof (pci_regspec_t); + + /* + * Rebuild the assigned-addresses property. + */ + for (i = 0; i < num_entries; i++) { + if (assigned_copy[i].pci_phys_hi != oldone->pci_phys_hi) { + (void) pcicfg_update_assigned_prop(dip, + &assigned_copy[i]); + } + } + + /* + * Free the copy of the original assigned-addresses. + */ + kmem_free(assigned_copy, alen); + + /* + * Don't forget to free up memory from ddi_getlongprop + */ + kmem_free((caddr_t)assigned, alen); + + return (0); +} + +static int +pcicfg_map_phys(dev_info_t *dip, pci_regspec_t *phys_spec, + caddr_t *addrp, ddi_device_acc_attr_t *accattrp, + ddi_acc_handle_t *handlep) +{ + ddi_map_req_t mr; + ddi_acc_hdl_t *hp; + int result; + + *handlep = impl_acc_hdl_alloc(KM_SLEEP, NULL); + hp = impl_acc_hdl_get(*handlep); + hp->ah_vers = VERS_ACCHDL; + hp->ah_dip = dip; + hp->ah_rnumber = 0; + hp->ah_offset = 0; + hp->ah_len = 0; + hp->ah_acc = *accattrp; + + mr.map_op = DDI_MO_MAP_LOCKED; + mr.map_type = DDI_MT_REGSPEC; + mr.map_obj.rp = (struct regspec *)phys_spec; + mr.map_prot = PROT_READ | PROT_WRITE; + mr.map_flags = DDI_MF_KERNEL_MAPPING; + mr.map_handlep = hp; + mr.map_vers = DDI_MAP_VERSION; + + result = ddi_map(dip, &mr, 0, 0, addrp); + + if (result != DDI_SUCCESS) { + impl_acc_hdl_free(*handlep); + *handlep = (ddi_acc_handle_t)NULL; + } else { + hp->ah_addr = *addrp; + } + + return (result); +} + +void +pcicfg_unmap_phys(ddi_acc_handle_t *handlep, pci_regspec_t *ph) +{ + ddi_map_req_t mr; + ddi_acc_hdl_t *hp; + + hp = impl_acc_hdl_get(*handlep); + ASSERT(hp); + + mr.map_op = DDI_MO_UNMAP; + mr.map_type = DDI_MT_REGSPEC; + mr.map_obj.rp = (struct regspec *)ph; + mr.map_prot = PROT_READ | PROT_WRITE; + mr.map_flags = DDI_MF_KERNEL_MAPPING; + mr.map_handlep = hp; + mr.map_vers = DDI_MAP_VERSION; + + (void) ddi_map(hp->ah_dip, &mr, hp->ah_offset, + hp->ah_len, &hp->ah_addr); + + impl_acc_hdl_free(*handlep); + *handlep = (ddi_acc_handle_t)NULL; +} +#ifdef DEBUG +static void +debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3, + uintptr_t a4, uintptr_t a5) +{ + if (pcicfg_debug == 1) { + prom_printf("pcicfg: "); + prom_printf(fmt, a1, a2, a3, a4, a5); + } else + if (pcicfg_debug) + cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5); +} +#endif diff --git a/usr/src/uts/sun4/io/px/px.c b/usr/src/uts/sun4/io/px/px.c index d8337a931d..4ce07e4620 100644 --- a/usr/src/uts/sun4/io/px/px.c +++ b/usr/src/uts/sun4/io/px/px.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include "px_obj.h" #include #include "px_tools_ext.h" @@ -59,6 +61,12 @@ static int px_info(dev_info_t *dip, ddi_info_cmd_t infocmd, static int px_pwr_setup(dev_info_t *dip); static void px_pwr_teardown(dev_info_t *dip); +/* + * function prototypes for hotplug routines: + */ +static uint_t px_init_hotplug(px_t *px_p); +static uint_t px_uninit_hotplug(dev_info_t *dip); + /* * bus ops and dev ops structures: */ @@ -181,14 +189,12 @@ px_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) int instance = getminor((dev_t)arg); px_t *px_p = INST_TO_STATE(instance); -#ifdef HOTPLUG /* * Allow hotplug to deal with ones it manages * Hot Plug will be done later. */ - if (px_p && (px_p->hotplug_capable == B_TRUE)) + if (px_p && (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE)) return (pcihp_info(dip, infocmd, arg, result)); -#endif /* HOTPLUG */ /* non-hotplug or not attached */ switch (infocmd) { @@ -239,6 +245,8 @@ px_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) px_p->px_soft_state = PX_SOFT_STATE_CLOSED; px_p->px_open_count = 0; + (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, + "device_type", "pciex"); /* * Get key properties of the pci bridge node and * determine it's type (psycho, schizo, etc ...). @@ -293,6 +301,8 @@ px_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) if ((ret = px_err_add_intr(&px_p->px_fault)) != DDI_SUCCESS) goto err_bad_pec_add_intr; + (void) px_init_hotplug(px_p); + /* * Create the "devctl" node for hotplug and pcitool support. * For non-hotplug bus, we still need ":devctl" to @@ -420,17 +430,11 @@ px_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) */ px_cpr_rem_callb(px_p); -#ifdef HOTPLUG - /* - * Hot plug will be done later. - */ - if (px_p->hotplug_capable == B_TRUE) { - if (pxhp_uninit(dip) == DDI_FAILURE) { + if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE) + if (px_uninit_hotplug(dip) != DDI_SUCCESS) { mutex_exit(&px_p->px_mutex); return (DDI_FAILURE); } - } -#endif /* HOTPLUG */ /* * things which used to be done in obj_destroy @@ -530,6 +534,8 @@ px_pwr_setup(dev_info_t *dip) DDI_INTR_PRI(px_pwr_pil)); cv_init(&px_p->px_l23ready_cv, NULL, CV_DRIVER, NULL); + + /* Initilize handle */ hdl.ih_cb_arg1 = px_p; hdl.ih_cb_arg2 = NULL; @@ -1304,3 +1310,78 @@ px_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, return (ret); } + +static uint_t +px_init_hotplug(px_t *px_p) +{ + px_bus_range_t bus_range; + dev_info_t *dip; + pciehpc_regops_t regops; + + dip = px_p->px_dip; + + if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "hotplug-capable") == 0) + return (DDI_FAILURE); + + /* + * Before initializing hotplug - open up bus range. The busra + * module will initialize its pool of bus numbers from this. + * "busra" will be the agent that keeps track of them during + * hotplug. Also, note, that busra will remove any bus numbers + * already in use from boot time. + */ + if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "bus-range") == 0) { + cmn_err(CE_WARN, "%s%d: bus-range not found\n", + ddi_driver_name(dip), ddi_get_instance(dip)); +#ifdef DEBUG + bus_range.lo = 0x0; + bus_range.hi = 0xff; + + if (ndi_prop_update_int_array(DDI_DEV_T_NONE, + dip, "bus-range", (int *)&bus_range, 2) + != DDI_PROP_SUCCESS) { + return (DDI_FAILURE); + } +#else + return (DDI_FAILURE); +#endif + } + + if (px_lib_hotplug_init(dip, (void *)®ops) != DDI_SUCCESS) + return (DDI_FAILURE); + + if (pciehpc_init(dip, ®ops) != DDI_SUCCESS) { + px_lib_hotplug_uninit(dip); + return (DDI_FAILURE); + } + + if (pcihp_init(dip) != DDI_SUCCESS) { + (void) pciehpc_uninit(dip); + px_lib_hotplug_uninit(dip); + return (DDI_FAILURE); + } + + if (pcihp_get_cb_ops() != NULL) { + DBG(DBG_ATTACH, dip, "%s%d hotplug enabled", + ddi_driver_name(dip), ddi_get_instance(dip)); + px_p->px_dev_caps |= PX_HOTPLUG_CAPABLE; + } + + return (DDI_SUCCESS); +} + +static uint_t +px_uninit_hotplug(dev_info_t *dip) +{ + if (pcihp_uninit(dip) != DDI_SUCCESS) + return (DDI_FAILURE); + + if (pciehpc_uninit(dip) != DDI_SUCCESS) + return (DDI_FAILURE); + + px_lib_hotplug_uninit(dip); + + return (DDI_SUCCESS); +} diff --git a/usr/src/uts/sun4/io/px/px_devctl.c b/usr/src/uts/sun4/io/px/px_devctl.c index 1dbc054c8a..196d8d6e3a 100644 --- a/usr/src/uts/sun4/io/px/px_devctl.c +++ b/usr/src/uts/sun4/io/px/px_devctl.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -82,6 +82,8 @@ static int px_open(dev_t *devp, int flags, int otyp, cred_t *credp) { px_t *px_p; + int rval; + uint_t orig_px_soft_state; /* * Make sure the open is for the right file type. @@ -101,6 +103,7 @@ px_open(dev_t *devp, int flags, int otyp, cred_t *credp) */ DBG(DBG_OPEN, px_p->px_dip, "devp=%x: flags=%x\n", devp, flags); mutex_enter(&px_p->px_mutex); + orig_px_soft_state = px_p->px_soft_state; if (flags & FEXCL) { if (px_p->px_soft_state != PX_SOFT_STATE_CLOSED) { mutex_exit(&px_p->px_mutex); @@ -116,6 +119,15 @@ px_open(dev_t *devp, int flags, int otyp, cred_t *credp) } px_p->px_soft_state = PX_SOFT_STATE_OPEN; } + + if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE) + if (rval = (pcihp_get_cb_ops())->cb_open(devp, flags, + otyp, credp)) { + px_p->px_soft_state = orig_px_soft_state; + mutex_exit(&px_p->px_mutex); + return (rval); + } + px_p->px_open_count++; mutex_exit(&px_p->px_mutex); return (0); @@ -127,6 +139,7 @@ static int px_close(dev_t dev, int flags, int otyp, cred_t *credp) { px_t *px_p; + int rval; if (otyp != OTYP_CHR) return (EINVAL); @@ -137,6 +150,14 @@ px_close(dev_t dev, int flags, int otyp, cred_t *credp) DBG(DBG_CLOSE, px_p->px_dip, "dev=%x: flags=%x\n", dev, flags); mutex_enter(&px_p->px_mutex); + + if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE) + if (rval = (pcihp_get_cb_ops())->cb_close(dev, flags, + otyp, credp)) { + mutex_exit(&px_p->px_mutex); + return (rval); + } + px_p->px_soft_state = PX_SOFT_STATE_CLOSED; px_p->px_open_count = 0; mutex_exit(&px_p->px_mutex); @@ -230,6 +251,9 @@ px_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) return (rv); default: + if (px_p->px_dev_caps & PX_HOTPLUG_CAPABLE) + return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, + arg, mode, credp, rvalp)); break; } diff --git a/usr/src/uts/sun4/io/px/px_dma.c b/usr/src/uts/sun4/io/px/px_dma.c index 743b71c41c..58731d3277 100644 --- a/usr/src/uts/sun4/io/px/px_dma.c +++ b/usr/src/uts/sun4/io/px/px_dma.c @@ -228,7 +228,7 @@ px_dma_attach(px_t *px_p) PCI_MAP_ATTR_WRITE|PCI_MAP_ATTR_READ, &baddr) != DDI_ENOTSUP) /* ignore all other errors */ - px_p->px_soft_state |= PX_BYPASS_DMA_ALLOWED; + px_p->px_dev_caps |= PX_BYPASS_DMA_ALLOWED; return (DDI_SUCCESS); } @@ -276,7 +276,7 @@ px_dma_attr2hdl(px_t *px_p, ddi_dma_impl_t *mp) * If Bypass DMA is not supported, return error so that * target driver can fall back to dvma mode of operation */ - if (!(px_p->px_soft_state & PX_BYPASS_DMA_ALLOWED)) + if (!(px_p->px_dev_caps & PX_BYPASS_DMA_ALLOWED)) return (DDI_DMA_BADATTR); mp->dmai_flags |= PX_DMAI_FLAGS_BYPASSREQ; if (nocross != UINT64_MAX) diff --git a/usr/src/uts/sun4/io/px/px_fm.c b/usr/src/uts/sun4/io/px/px_fm.c index 987649b781..3270dfc2d9 100644 --- a/usr/src/uts/sun4/io/px/px_fm.c +++ b/usr/src/uts/sun4/io/px/px_fm.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -804,7 +804,9 @@ px_err_fabric_intr(px_t *px_p, msgcode_t msg_code, */ if ((err & (PX_FATAL_GOS | PX_FATAL_SW)) || (ret == DDI_FM_FATAL) || (fab_err == DDI_FM_FATAL)) - PX_FM_PANIC("Fatal PCIe Fabric Error has occurred\n"); + PX_FM_PANIC("%s#%d: Fatal PCIe Fabric Error has occurred" + "(%x,%x,%x)\n", ddi_driver_name(rpdip), + ddi_get_instance(rpdip), err, fab_err, ret); return (DDI_INTR_CLAIMED); } diff --git a/usr/src/uts/sun4/io/px/px_lib.h b/usr/src/uts/sun4/io/px/px_lib.h index d4edf2702f..a32b084c63 100644 --- a/usr/src/uts/sun4/io/px/px_lib.h +++ b/usr/src/uts/sun4/io/px/px_lib.h @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -210,6 +210,12 @@ extern void px_fab_set(px_t *px_p, pcie_req_id_t bdf, uint16_t offset, extern void px_cpr_add_callb(px_t *); extern void px_cpr_rem_callb(px_t *); +/* + * Hotplug functions + */ +extern int px_lib_hotplug_init(dev_info_t *dip, void *regops); +extern void px_lib_hotplug_uninit(dev_info_t *dip); + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/sun4/io/px/px_pci.c b/usr/src/uts/sun4/io/px/px_pci.c index d0761e6950..90da208323 100644 --- a/usr/src/uts/sun4/io/px/px_pci.c +++ b/usr/src/uts/sun4/io/px/px_pci.c @@ -37,11 +37,19 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include #include #include #include +#include +#include #include #include #include @@ -49,12 +57,13 @@ #include "px_pci.h" #include "px_debug.h" +/* Tunables. Beware: Some are for debug purpose only. */ /* * PXB MSI tunable: * * By default MSI is enabled on all supported platforms. */ -boolean_t pxb_enable_msi = B_TRUE; +boolean_t pxb_enable_msi = B_TRUE; /* MSI enabled by default, otherwise INTX */ static int pxb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *, off_t, off_t, caddr_t *); @@ -73,6 +82,11 @@ static int pxb_fm_init_child(dev_info_t *dip, dev_info_t *cdip, int cap, static int pxb_fm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data); +static int ppb_pcie_device_type(pxb_devstate_t *pxb_p); +#ifdef PRINT_PLX_SEEPROM_CRC +static void pxb_print_plx_seeprom_crc_data(pxb_devstate_t *pxb_p); +#endif + struct bus_ops pxb_bus_ops = { BUSO_REV, pxb_bus_map, @@ -142,6 +156,17 @@ static int pxb_pwr_setup(dev_info_t *dip); static int pxb_pwr_init_and_raise(dev_info_t *dip, pcie_pwr_t *pwr_p); static void pxb_pwr_teardown(dev_info_t *dip); +static int pxb_get_cap(ddi_acc_handle_t config_handle, uint8_t cap_id); +static uint16_t pxb_find_ext_cap_reg(ddi_acc_handle_t config_handle, + uint16_t cap_id); +static int pxb_bad_func(pxb_devstate_t *pxb, int func); +static int pxb_check_bad_devs(pxb_devstate_t *pxb, int vend); + +/* Hotplug related functions */ +static int pxb_pciehpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle); +static int pxb_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle); +static void pxb_init_hotplug(pxb_devstate_t *pxb); + struct dev_ops pxb_ops = { DEVO_REV, /* devo_rev */ 0, /* refcnt */ @@ -162,7 +187,7 @@ struct dev_ops pxb_ops = { static struct modldrv modldrv = { &mod_driverops, /* Type of module */ - "PCIe/PCI nexus driver %I%", + "PCIe/PCI nexus driver 1.29", &pxb_ops, /* driver ops */ }; @@ -188,11 +213,12 @@ int pxb_tlp_count = 64; static int pxb_intr_init(pxb_devstate_t *pxb, int intr_type); static void pxb_intr_fini(pxb_devstate_t *pxb); static uint_t pxb_intr(caddr_t arg1, caddr_t arg2); +static int pxb_intr_attach(pxb_devstate_t *pxb); + static int pxb_get_port_type(dev_info_t *dip, ddi_acc_handle_t config_handle); static void pxb_removechild(dev_info_t *); static int pxb_initchild(dev_info_t *child); static dev_info_t *get_my_childs_dip(dev_info_t *dip, dev_info_t *rdip); -static void pxb_init_hotplug(pxb_devstate_t *pxb); static void pxb_create_ranges_prop(dev_info_t *, ddi_acc_handle_t); int @@ -262,7 +288,7 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) int instance; pxb_devstate_t *pxb; ddi_acc_handle_t config_handle; - int intr_types; + char device_type[8]; instance = ddi_get_instance(devi); @@ -326,6 +352,7 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) /* Save the vnedor id and device id */ pxb->pxb_vendor_id = pci_config_get16(config_handle, PCI_CONF_VENID); pxb->pxb_device_id = pci_config_get16(config_handle, PCI_CONF_DEVID); + pxb->pxb_rev_id = pci_config_get8(config_handle, PCI_CONF_REVID); /* * This is a software workaround to fix the Broadcom PCIe-PCI bridge @@ -356,13 +383,8 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) goto fail; } - if ((pxb_fm_init(pxb)) != DDI_SUCCESS) { - DBG(DBG_ATTACH, devi, "Failed in px_pci_fm_attach\n"); - goto fail; - } - pxb->pxb_init_flags |= PXB_INIT_FM; - pxb->pxb_port_type = pxb_get_port_type(devi, config_handle); + if ((pxb->pxb_port_type != PX_CAP_REG_DEV_TYPE_UP) && (pxb->pxb_port_type != PX_CAP_REG_DEV_TYPE_DOWN) && (pxb->pxb_port_type != PX_CAP_REG_DEV_TYPE_PCIE2PCI) && @@ -374,8 +396,12 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) /* * Make sure the "device_type" property exists. */ + if (ppb_pcie_device_type(pxb) == DDI_SUCCESS) + (void) strcpy(device_type, "pciex"); + else + (void) strcpy(device_type, "pci"); (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi, - "device_type", "pciex"); + "device_type", device_type); /* * Check whether the "ranges" property is present. @@ -387,32 +413,6 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) pxb_create_ranges_prop(devi, config_handle); } - /* - * Initialize interrupt handlers. - * If both MSI and FIXED are supported, try to attach MSI first. - * If MSI fails for any reason, then try FIXED, but only allow one - * type to be attached. - */ - if (ddi_intr_get_supported_types(devi, &intr_types) != DDI_SUCCESS) { - DBG(DBG_ATTACH, devi, "ddi_intr_get_supported_types failed\n"); - goto fail; - } - - if ((intr_types & DDI_INTR_TYPE_MSI) && pxb_enable_msi) { - if (pxb_intr_init(pxb, DDI_INTR_TYPE_MSI) == DDI_SUCCESS) - intr_types = DDI_INTR_TYPE_MSI; - else - DBG(DBG_ATTACH, devi, "Unable to attach MSI handler\n"); - } - - if (intr_types & DDI_INTR_TYPE_FIXED) { - if (pxb_intr_init(pxb, DDI_INTR_TYPE_FIXED) != DDI_SUCCESS) { - DBG(DBG_ATTACH, devi, - "Unable to attach INTx handler\n"); - goto fail; - } - } - /* * Initialize hotplug support on this bus. At minimum * (for non hotplug bus) this would create ":devctl" minor @@ -421,7 +421,23 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) * slots and successfully initializes Hot Plug Framework. */ pxb->pxb_hotplug_capable = B_FALSE; - pxb_init_hotplug(pxb); + + if ((pxb->pxb_port_type == PX_CAP_REG_DEV_TYPE_DOWN) || + (pxb->pxb_port_type == PX_CAP_REG_DEV_TYPE_PCIE2PCI) || + (pxb->pxb_port_type == PX_CAP_REG_DEV_TYPE_PCI2PCIE)) { + pxb_init_hotplug(pxb); + } + + /* attach interrupt only if hotplug functionality is required */ + if (pxb->pxb_hotplug_capable != B_FALSE) { + if (pxb_intr_attach(pxb) != DDI_SUCCESS) + goto fail; + } +#ifdef PRINT_PLX_SEEPROM_CRC + /* check seeprom CRC to ensure the platform config is right */ + (void) pxb_print_plx_seeprom_crc_data(pxb); +#endif + if (pxb->pxb_hotplug_capable == B_FALSE) { /* * create minor node for devctl interfaces @@ -436,6 +452,12 @@ pxb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) "pxb_attach(): this nexus %s hotplug slots\n", pxb->pxb_hotplug_capable == B_TRUE ? "has":"has no"); + if ((pxb_fm_init(pxb)) != DDI_SUCCESS) { + DBG(DBG_ATTACH, devi, "Failed in px_pci_fm_attach\n"); + goto fail; + } + pxb->pxb_init_flags |= PXB_INIT_FM; + ddi_report_dev(devi); return (DDI_SUCCESS); @@ -463,16 +485,22 @@ pxb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) pxb = (pxb_devstate_t *) ddi_get_soft_state(pxb_state, ddi_get_instance(devi)); - if (pxb->pxb_hotplug_capable == B_TRUE) + if (pxb->pxb_hotplug_capable == B_TRUE) { if (pcihp_uninit(devi) == DDI_FAILURE) error = DDI_FAILURE; + + if (pxb->pxb_hpc_type == HPC_PCIE) + (void) pciehpc_uninit(devi); + else if (pxb->pxb_hpc_type == HPC_SHPC) + (void) pcishpc_uninit(devi); + } else ddi_remove_minor_node(devi, "devctl"); (void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type"); - pxb_intr_fini(pxb); - + if (pxb->pxb_hotplug_capable != B_FALSE) + pxb_intr_fini(pxb); if (pxb->pxb_init_flags & PXB_INIT_FM) pxb_fm_fini(pxb); @@ -881,8 +909,13 @@ pxb_initchild(dev_info_t *child) * optimized out. */ if ((!pxb_tlp_count) || - (pxb->pxb_vendor_id != PXB_VENDOR_PLX) || - ((pxb->pxb_device_id != PXB_DEVICE_PLX_8532) && + ((pxb->pxb_vendor_id != PXB_VENDOR_SUN) && + (pxb->pxb_vendor_id != PXB_VENDOR_PLX)) || + ((pxb->pxb_vendor_id == PXB_VENDOR_SUN) && + (pxb->pxb_device_id != PXB_DEVICE_PLX_PCIX) && + (pxb->pxb_device_id != PXB_DEVICE_PLX_PCIE)) || + ((pxb->pxb_vendor_id == PXB_VENDOR_PLX) && + (pxb->pxb_device_id != PXB_DEVICE_PLX_8532) && (pxb->pxb_device_id != PXB_DEVICE_PLX_8516))) { /* Workaround not needed return success */ result = DDI_SUCCESS; @@ -906,6 +939,46 @@ done: return (result); } +static int +pxb_intr_attach(pxb_devstate_t *pxb) +{ + int intr_types; + dev_info_t *devi; + uint8_t bad_msi_dev = 0; + + devi = pxb->pxb_dip; + /* + * Initialize interrupt handlers. + * If both MSI and FIXED are supported, try to attach MSI first. + * If MSI fails for any reason, then try FIXED, but only allow one + * type to be attached. + */ + if (ddi_intr_get_supported_types(devi, &intr_types) != DDI_SUCCESS) { + DBG(DBG_ATTACH, devi, "ddi_intr_get_supported_types failed\n"); + return (DDI_FAILURE); + } + + if (pxb_bad_func(pxb, PXB_MSI)) + bad_msi_dev = 1; + + if ((intr_types & DDI_INTR_TYPE_MSI) && pxb_enable_msi && + !bad_msi_dev) { + if (pxb_intr_init(pxb, DDI_INTR_TYPE_MSI) == DDI_SUCCESS) + intr_types = DDI_INTR_TYPE_MSI; + else + DBG(DBG_ATTACH, devi, "Unable to attach MSI handler\n"); + } + + if (intr_types & DDI_INTR_TYPE_FIXED) { + if (pxb_intr_init(pxb, DDI_INTR_TYPE_FIXED) != DDI_SUCCESS) { + DBG(DBG_ATTACH, devi, + "Unable to attach INTx handler\n"); + return (DDI_FAILURE); + } + } + return (DDI_SUCCESS); +} + /* * This function initializes internally generated interrupts only. * It does not affect any interrupts generated by downstream devices @@ -917,7 +990,8 @@ done: * device might not ask for any interrupts. */ static int -pxb_intr_init(pxb_devstate_t *pxb, int intr_type) { +pxb_intr_init(pxb_devstate_t *pxb, int intr_type) +{ dev_info_t *dip = pxb->pxb_dip; int request, count, x; int ret; @@ -1055,8 +1129,9 @@ fail: return (DDI_FAILURE); } -static -void pxb_intr_fini(pxb_devstate_t *pxb) { +static void +pxb_intr_fini(pxb_devstate_t *pxb) +{ int x; int count = pxb->pxb_intr_count; int flags = pxb->pxb_init_flags; @@ -1093,21 +1168,30 @@ void pxb_intr_fini(pxb_devstate_t *pxb) { */ /*ARGSUSED*/ static uint_t -pxb_intr(caddr_t arg1, caddr_t arg2) { +pxb_intr(caddr_t arg1, caddr_t arg2) +{ pxb_devstate_t *pxb = (pxb_devstate_t *)arg1; dev_info_t *dip = pxb->pxb_dip; + int rval = DDI_INTR_UNCLAIMED; - cmn_err(CE_WARN, - "%s%d: Received %s Interrupt\n", - ddi_driver_name(dip), - ddi_get_instance(dip), - (pxb->pxb_intr_type == DDI_INTR_TYPE_MSI) ? "MSI" : "INTx"); + if (pxb->pxb_hotplug_capable == B_TRUE) { + if (pxb->pxb_hpc_type == HPC_PCIE) + rval = pciehpc_intr(pxb->pxb_dip); + else + if (pxb->pxb_hpc_type == HPC_SHPC) + rval = pcishpc_intr(pxb->pxb_dip); + } + if ((rval == DDI_INTR_UNCLAIMED) && (pxb->pxb_intr_type == + DDI_INTR_TYPE_MSI)) + cmn_err(CE_WARN, "%s%d: Cannot handle interrupt", + ddi_driver_name(dip), ddi_get_instance(dip)); - return (DDI_INTR_UNCLAIMED); + return (rval); } -static -int pxb_get_port_type(dev_info_t *dip, ddi_acc_handle_t config_handle) { +static int +pxb_get_port_type(dev_info_t *dip, ddi_acc_handle_t config_handle) +{ ushort_t caps_ptr, cap; int port_type = PX_CAP_REG_DEV_TYPE_PCIE_DEV; @@ -1168,10 +1252,41 @@ pxb_removechild(dev_info_t *dip) static void pxb_init_hotplug(pxb_devstate_t *pxb) { - /* - * Hot plug support to be decided. - */ + int rv; + + pxb->pxb_hpc_type = HPC_NONE; + + if (((pxb->pxb_port_type == PX_CAP_REG_DEV_TYPE_DOWN) || + (pxb->pxb_port_type == PX_CAP_REG_DEV_TYPE_PCI2PCIE)) && + (pxb_pciehpc_probe(pxb->pxb_dip, + pxb->pxb_config_handle) == DDI_SUCCESS)) { + rv = pciehpc_init(pxb->pxb_dip, NULL); + if (rv == DDI_SUCCESS) + pxb->pxb_hpc_type = HPC_PCIE; + } else if ((pxb->pxb_port_type == PX_CAP_REG_DEV_TYPE_PCIE2PCI) && + (pxb_pcishpc_probe(pxb->pxb_dip, + pxb->pxb_config_handle) == DDI_SUCCESS)) { + rv = pcishpc_init(pxb->pxb_dip); + if (rv == DDI_SUCCESS) + pxb->pxb_hpc_type = HPC_SHPC; + } + if (pxb->pxb_hpc_type != HPC_NONE) { + if (pcihp_init(pxb->pxb_dip) != DDI_SUCCESS) { + if (pxb->pxb_hpc_type == HPC_PCIE) + (void) pciehpc_uninit(pxb->pxb_dip); + else if (pxb->pxb_hpc_type == HPC_SHPC) + (void) pcishpc_uninit(pxb->pxb_dip); + + pxb->pxb_hpc_type = HPC_NONE; + cmn_err(CE_WARN, + "%s%d: Failed setting hotplug framework", + ddi_driver_name(pxb->pxb_dip), + ddi_get_instance(pxb->pxb_dip)); + } + } + + pxb->pxb_hotplug_capable = (pxb->pxb_hpc_type != HPC_NONE); } static void @@ -1442,9 +1557,12 @@ pxb_pwr_setup(dev_info_t *dip) * Due to PLX erratum #34, we can't allow the downstream device * go to non-D0 state. */ - if ((pxb->pxb_vendor_id == PXB_VENDOR_PLX) && - ((pxb->pxb_device_id == PXB_DEVICE_PLX_8516) || - (pxb->pxb_device_id == PXB_DEVICE_PLX_8532))) { + if (((pxb->pxb_vendor_id == PXB_VENDOR_SUN) && + ((pxb->pxb_device_id == PXB_DEVICE_PLX_PCIX) || + (pxb->pxb_device_id == PXB_DEVICE_PLX_PCIE))) || + ((pxb->pxb_vendor_id == PXB_VENDOR_PLX) && + ((pxb->pxb_device_id == PXB_DEVICE_PLX_8516) || + (pxb->pxb_device_id == PXB_DEVICE_PLX_8532)))) { DBG(DBG_PWR, dip, "pxb_pwr_setup: PLX8532/PLX8516 found " "disabling PM\n"); pwr_p->pwr_func_lvl = PM_LEVEL_D0; @@ -1601,7 +1719,8 @@ pxb_fm_init(pxb_devstate_t *pxb_p) /* * Register error callback with our parent. */ - ddi_fm_handler_register(pxb_p->pxb_dip, pxb_fm_err_callback, NULL); + ddi_fm_handler_register(pxb_p->pxb_dip, pxb_fm_err_callback, + (void *)&pxb_p->pxb_config_handle); return (DDI_SUCCESS); } @@ -1636,20 +1755,24 @@ pxb_fm_init_child(dev_info_t *dip, dev_info_t *cdip, int cap, } /* - * Error callback handler. + * FMA Error callback handler. + * Need to revisit when pcie fm is supported. */ /*ARGSUSED*/ static int pxb_fm_err_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data) { - /* Need to revisit when pcie fm is supported */ uint16_t pci_cfg_stat, pci_cfg_sec_stat; + int ret; + ddi_acc_handle_t hdl = *(ddi_acc_handle_t *)impl_data; pci_ereport_post(dip, derr, &pci_cfg_stat); pci_bdg_ereport_post(dip, derr, &pci_cfg_sec_stat); - return (pci_bdg_check_status(dip, derr, pci_cfg_stat, - pci_cfg_sec_stat)); + ret = pci_bdg_check_status(dip, derr, pci_cfg_stat, pci_cfg_sec_stat); + /* do this till Fabric FMA is in place. */ + pcie_clear_errors(dip, hdl); + return (ret); } /* @@ -1667,3 +1790,277 @@ pxb_pwr_teardown(dev_info_t *dip) if (pwr_p->pwr_conf_hdl) pci_config_teardown(&pwr_p->pwr_conf_hdl); } + +/*ARGSUSED*/ +static int pxb_pciehpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle) +{ + uint16_t status; + uint8_t cap_ptr, cap_id; + + status = pci_config_get16(config_handle, PCI_CONF_STAT); + + if (!(status & PCI_STAT_CAP)) + cap_ptr = PCI_CAP_NEXT_PTR_NULL; + else { + cap_ptr = pci_config_get8(config_handle, PCI_CONF_CAP_PTR); + cap_ptr &= 0xFC; + } + + while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) { + cap_id = pci_config_get8(config_handle, cap_ptr); + + if (cap_id == PCI_CAP_ID_PCI_E) { + uint16_t slotimpl; + + slotimpl = pci_config_get16(config_handle, cap_ptr + + PCIE_PCIECAP) & PCIE_PCIECAP_SLOT_IMPL; + if (slotimpl) + if (pci_config_get32(config_handle, cap_ptr + + PCIE_SLOTCAP) & + PCIE_SLOTCAP_HP_CAPABLE) + break; + } + + cap_ptr = pci_config_get8(config_handle, cap_ptr + + PCI_CAP_NEXT_PTR); + cap_ptr &= 0xFC; + } + + return ((cap_ptr != PCI_CAP_NEXT_PTR_NULL) ? DDI_SUCCESS : DDI_FAILURE); +} + +/*ARGSUSED*/ +static int pxb_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle) +{ + uint16_t status; + uint8_t cap_ptr, cap_id; + + status = pci_config_get16(config_handle, PCI_CONF_STAT); + + if (!(status & PCI_STAT_CAP)) + cap_ptr = PCI_CAP_NEXT_PTR_NULL; + else { + cap_ptr = pci_config_get8(config_handle, PCI_CONF_CAP_PTR); + cap_ptr &= 0xFC; + } + + while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) { + cap_id = pci_config_get8(config_handle, cap_ptr); + + if (cap_id == PCI_CAP_ID_PCI_HOTPLUG) + break; + + cap_ptr = pci_config_get8(config_handle, cap_ptr + + PCI_CAP_NEXT_PTR); + cap_ptr &= 0xFC; + } + + return ((cap_ptr != PCI_CAP_NEXT_PTR_NULL) ? DDI_SUCCESS : DDI_FAILURE); +} + +/* check if this device has PCIe link underneath. */ +static int +ppb_pcie_device_type(pxb_devstate_t *pxb_p) +{ + int port_type = pxb_p->pxb_port_type; + + /* No PCIe CAP regs, we are not PCIe device_type */ + if (port_type < 0) + return (DDI_FAILURE); + + /* check for all PCIe device_types */ + if ((port_type == PCIE_PCIECAP_DEV_TYPE_UP) || + (port_type == PCIE_PCIECAP_DEV_TYPE_DOWN) || + (port_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE)) + return (DDI_SUCCESS); + + return (DDI_FAILURE); +} + +#ifdef PRINT_PLX_SEEPROM_CRC +static void +pxb_print_plx_seeprom_crc_data(pxb_devstate_t *pxb_p) +{ + ddi_acc_handle_t h; + dev_info_t *dip = pxb_p->pxb_dip; + int nregs; + caddr_t mp; + off_t bar_size; + ddi_device_acc_attr_t mattr = { + DDI_DEVICE_ATTR_V0, + DDI_STRUCTURE_LE_ACC, + DDI_STRICTORDER_ACC + }; + uint32_t addr_reg_off = 0x260, data_reg_off = 0x264, data = 0x6BE4; + + if (pxb_p->pxb_vendor_id != PXB_VENDOR_PLX) + return; + if (ddi_dev_nregs(dip, &nregs) != DDI_SUCCESS) + return; + if (nregs < 2) /* check for CONF entry only, no BARs */ + return; + if (ddi_dev_regsize(dip, 1, &bar_size) != DDI_SUCCESS) + return; + if (ddi_regs_map_setup(dip, 1, (caddr_t *)&mp, 0, bar_size, + &mattr, &h) != DDI_SUCCESS) + return; + ddi_put32(h, (uint32_t *)((uchar_t *)mp + addr_reg_off), data); + delay(drv_usectohz(1000000)); + printf("%s#%d: EEPROM StatusReg = %x, CRC = %x\n", + ddi_driver_name(dip), ddi_get_instance(dip), + ddi_get32(h, (uint32_t *)((uchar_t *)mp + addr_reg_off)), + ddi_get32(h, (uint32_t *)((uchar_t *)mp + data_reg_off))); +#ifdef PLX_HOT_RESET_DISABLE + /* prevent hot reset from propogating downstream. */ + data = ddi_get32(h, (uint32_t *)((uchar_t *)mp + 0x1DC)); + ddi_put32(h, (uint32_t *)((uchar_t *)mp + 0x1DC), data | 0x80000); + delay(drv_usectohz(1000000)); + printf("%s#%d: EEPROM 0x1DC prewrite=%x postwrite=%x\n", + ddi_driver_name(dip), ddi_get_instance(dip), data, + ddi_get32(h, (uint32_t *)((uchar_t *)mp + 0x1DC))); +#endif + ddi_regs_map_free(&h); +} +#endif + +/* + * given a cap_id, return its cap_id location in config space + */ +static int +pxb_get_cap(ddi_acc_handle_t config_handle, uint8_t cap_id) +{ + uint8_t curcap; + uint_t cap_id_loc; + uint16_t status; + int location = -1; + + /* + * Need to check the Status register for ECP support first. + * Also please note that for type 1 devices, the + * offset could change. Should support type 1 next. + */ + status = pci_config_get16(config_handle, PCI_CONF_STAT); + if (!(status & PCI_STAT_CAP)) { + return (-1); + } + cap_id_loc = pci_config_get8(config_handle, PCI_CONF_CAP_PTR); + + /* Walk the list of capabilities */ + while (cap_id_loc) { + + curcap = pci_config_get8(config_handle, cap_id_loc); + + if (curcap == cap_id) { + location = cap_id_loc; + break; + } + cap_id_loc = pci_config_get8(config_handle, + cap_id_loc + 1); + } + return (location); +} + +static uint16_t +pxb_find_ext_cap_reg(ddi_acc_handle_t config_handle, uint16_t cap_id) +{ + uint32_t hdr, hdr_next_ptr, hdr_cap_id; + uint16_t offset = P2ALIGN(PCIE_EXT_CAP, 4); + + hdr = pci_config_get32(config_handle, offset); + hdr_next_ptr = (hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT) & + PCIE_EXT_CAP_NEXT_PTR_MASK; + hdr_cap_id = (hdr >> PCIE_EXT_CAP_ID_SHIFT) & + PCIE_EXT_CAP_ID_MASK; + + while ((hdr_next_ptr != PCIE_EXT_CAP_NEXT_PTR_NULL) && + (hdr_cap_id != cap_id)) { + offset = P2ALIGN(hdr_next_ptr, 4); + hdr = pci_config_get32(config_handle, offset); + hdr_next_ptr = (hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT) & + PCIE_EXT_CAP_NEXT_PTR_MASK; + hdr_cap_id = (hdr >> PCIE_EXT_CAP_ID_SHIFT) & + PCIE_EXT_CAP_ID_MASK; + } + + if (hdr_cap_id == cap_id) + return (P2ALIGN(offset, 4)); + + return (PCIE_EXT_CAP_NEXT_PTR_NULL); +} + +/* check for known bad functionality in devices */ +static int +pxb_bad_func(pxb_devstate_t *pxb, int func) +{ + int ret = B_FALSE; + + switch (func) { + /* some devices do not render MSI well. */ + case PXB_MSI: + if (pxb_check_bad_devs(pxb, PXB_VENDOR_SUN) || + pxb_check_bad_devs(pxb, PXB_VENDOR_PLX)) { + + if (pxb->pxb_rev_id <= + PXB_DEVICE_PLX_BAD_MSI_REV) + ret = B_TRUE; + } + break; + + case PXB_LINK_INIT: + /* + * some devices require certain initialization sequence so + * as to not get wedged. + */ + + case PXB_HOTPLUG_MSGS: + /* + * disable UR for 1.0a implementations where + * hotplug messages sent downstream cause URs to be + * detected causing error messages to be sent to the fabric. + */ + if (pxb_check_bad_devs(pxb, PXB_VENDOR_SUN) || + pxb_check_bad_devs(pxb, PXB_VENDOR_PLX)) { + + ret = B_TRUE; + } + if (func == PXB_HOTPLUG_MSGS) + ret = B_TRUE; + break; + + + default: + break; + } + return (ret); +} + +/* check for known bad devices from a vendor */ +static int +pxb_check_bad_devs(pxb_devstate_t *pxb, int vend) +{ + int ret = B_FALSE; + + switch (vend) { + case PXB_VENDOR_SUN: + if ((pxb->pxb_vendor_id == PXB_VENDOR_SUN) && + ((pxb->pxb_device_id == + PXB_DEVICE_PLX_PCIX) || + (pxb->pxb_device_id == + PXB_DEVICE_PLX_PCIE))) { + ret = B_TRUE; + } + break; + case PXB_VENDOR_PLX: + if ((pxb->pxb_vendor_id == PXB_VENDOR_PLX) && + ((pxb->pxb_device_id == + PXB_DEVICE_PLX_8532) || + (pxb->pxb_device_id == + PXB_DEVICE_PLX_8516))) { + ret = B_TRUE; + } + break; + default: + break; + } + return (ret); +} diff --git a/usr/src/uts/sun4/io/px/px_pci.h b/usr/src/uts/sun4/io/px/px_pci.h index 283c635b39..d3fbe16c7e 100644 --- a/usr/src/uts/sun4/io/px/px_pci.h +++ b/usr/src/uts/sun4/io/px/px_pci.h @@ -114,6 +114,8 @@ typedef struct { uint32_t size_low; } pxb_ranges_t; +typedef enum { HPC_NONE, HPC_PCIE, HPC_SHPC, HPC_OUTBAND } pxb_hpc_type_t; + typedef struct { dev_info_t *pxb_dip; @@ -133,6 +135,7 @@ typedef struct { * HP support */ boolean_t pxb_hotplug_capable; + pxb_hpc_type_t pxb_hpc_type; kmutex_t pxb_mutex; uint_t pxb_soft_state; @@ -147,7 +150,7 @@ typedef struct { /* Vendor Device Id */ uint16_t pxb_vendor_id; uint16_t pxb_device_id; - + uint8_t pxb_rev_id; } pxb_devstate_t; /* @@ -184,6 +187,19 @@ extern void *pxb_state; ((pxb->pxb_vendor_id == PXB_VENDOR_BCM) && \ (pxb->pxb_device_id == PXB_DEVICE_BCM5714)) +#define PXB_DEVICE_PLX_BAD_MSI_REV 0xAA /* last known bad rev for MSI */ + +#define PXB_VENDOR_SUN 0x108E +#define PXB_DEVICE_PLX_PCIX 0x9010 +#define PXB_DEVICE_PLX_PCIE 0x9020 + +#define PXB_HOTPLUG_INTR_PRI (LOCK_LEVEL - 1) + +/* functionality checks */ +#define PXB_MSI 1 +#define PXB_LINK_INIT 2 +#define PXB_HOTPLUG_MSGS 3 + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/sun4/io/px/px_var.h b/usr/src/uts/sun4/io/px/px_var.h index 23d3355530..5b1fd8d898 100644 --- a/usr/src/uts/sun4/io/px/px_var.h +++ b/usr/src/uts/sun4/io/px/px_var.h @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -132,6 +132,8 @@ struct px { int px_fm_cap; ddi_iblock_cookie_t px_fm_ibc; + uint32_t px_dev_caps; + /* Platform specific information */ void *px_plat_p; @@ -152,7 +154,10 @@ struct px { #define PX_SOFT_STATE_OPEN 0x01 #define PX_SOFT_STATE_OPEN_EXCL 0x02 #define PX_SOFT_STATE_CLOSED 0x04 -#define PX_BYPASS_DMA_ALLOWED 0x10 + +/* px_dev_caps definition */ +#define PX_BYPASS_DMA_ALLOWED 0x00000001 +#define PX_HOTPLUG_CAPABLE 0x00000002 /* px_pm_flags definitions used with interrupts and FMA code */ #define PX_PMETOACK_RECVD 0x01 /* With PME_To_ACK interrupt */ diff --git a/usr/src/uts/sun4/sys/fc_plat.h b/usr/src/uts/sun4/sys/fc_plat.h new file mode 100644 index 0000000000..77211e30f3 --- /dev/null +++ b/usr/src/uts/sun4/sys/fc_plat.h @@ -0,0 +1,102 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_FC_PLAT_H +#define _SYS_FC_PLAT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Platform specific definitions for the fcode interpreter and driver. + * Define the cell size for the implementation. + * + * These definitions are appropriate for SPARC V9. + */ + +/* + * The cell size is based on the cell size of the underlying "firmware" + * implementation. NB: FCode is really a 32-bit language, but we still + * define our interfaces in terms of the underlying cell size. + */ + +typedef unsigned long long fc_cell_t; + +/* + * common typedef for phandles accross the interface. + */ +typedef uint32_t fc_phandle_t; + +/* + * Handy macros for converting from an fc_cell_t to an integral type + * These are useful because arguments and results are always passed + * in an array of fc_cell_t's. + */ + +#define fc_ptr2cell(p) ((fc_cell_t)((uintptr_t)((void *)(p)))) +#define fc_int2cell(i) ((fc_cell_t)((int)(i))) +#define fc_uint2cell(u) ((fc_cell_t)((unsigned int)(u))) +#define fc_uint32_t2cell(u) ((fc_cell_t)((unsigned int)((uint32_t)(u)))) +#define fc_uint16_t2cell(w) ((fc_cell_t)((unsigned int)((uint16_t)(w)))) +#define fc_uint8_t2cell(b) ((fc_cell_t)((unsigned int)((uint8_t)(b)))) +#define fc_size2cell(u) ((fc_cell_t)((size_t)(u))) +#define fc_ssize2cell(i) ((fc_cell_t)((ssize_t)(i))) +#define fc_phandle2cell(ph) ((fc_cell_t)((unsigned int)((phandle_t)(ph)))) +#define fc_dnode2cell(d) ((fc_cell_t)((unsigned int)((pnode_t)(d)))) +#define fc_ull2cell_high(ll) (0LL) +#define fc_ull2cell_low(ll) ((fc_cell_t)(ll)) +#define fc_uintptr2cell(i) ((fc_cell_t)((uintptr_t)(i))) +#define fc_uchar2cell(c) ((fc_cell_t)((unsigned char)(c))) +#define fc_ushort2cell(w) ((fc_cell_t)((unsigned short)(w))) +#define fc_ihandle2cell(h) ((fc_cell_t)((fc_ihandle_t)(h))) + +#define fc_cell2ptr(p) ((void *)((fc_cell_t)(p))) +#define fc_cell2int(i) ((int)((fc_cell_t)(i))) +#define fc_cell2uint(u) ((unsigned int)((fc_cell_t)(u))) +#define fc_cell2uint32_t(u) ((uint32_t)((fc_cell_t)(u))) +#define fc_cell2uint16_t(w) ((uint16_t)((fc_cell_t)(w))) +#define fc_cell2uint8_t(b) ((uint8_t)((fc_cell_t)(b))) +#define fc_cell2size(u) ((size_t)((fc_cell_t)(u))) +#define fc_cell2ssize(i) ((ssize_t)((fc_cell_t)(i))) +#define fc_cell2phandle(ph) ((phandle_t)((fc_cell_t)(ph))) +#define fc_cell2dnode(d) ((pnode_t)((fc_cell_t)(d))) +#define fc_cells2ull(h, l) ((unsigned long long)(fc_cell_t)(l)) +#define fc_cell2uintptr(i) ((uintptr_t)((fc_cell_t)(i))) +#define fc_cell2uchar(c) ((unsigned char)(fc_cell_t)(c)) +#define fc_cell2ushort(w) ((unsigned short)(fc_cell_t)(w)) +#define fc_cell2ihandle(h) ((fc_ihandle_t)(fc_cell_t)(h)) + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_FC_PLAT_H */ diff --git a/usr/src/uts/sun4u/Makefile.files b/usr/src/uts/sun4u/Makefile.files index a35156f264..88169fb6b1 100644 --- a/usr/src/uts/sun4u/Makefile.files +++ b/usr/src/uts/sun4u/Makefile.files @@ -103,7 +103,6 @@ GRBEEP_OBJS += grbeep.o ADM1031_OBJS += adm1031.o ICS951601_OBJS += ics951601.o PPM_OBJS += ppm_subr.o ppm.o -PCICFG_E_OBJS += pcicfg.e.o PCF8584_OBJS += pcf8584.o PCA9556_OBJS += pca9556.o ADM1026_OBJS += adm1026.o diff --git a/usr/src/uts/sun4u/Makefile.sun4u.shared b/usr/src/uts/sun4u/Makefile.sun4u.shared index 196e611b4b..b2b4b80914 100644 --- a/usr/src/uts/sun4u/Makefile.sun4u.shared +++ b/usr/src/uts/sun4u/Makefile.sun4u.shared @@ -367,7 +367,6 @@ DRV_KMODS += su DRV_KMODS += tod DRV_KMODS += power DRV_KMODS += epic -DRV_KMODS += fcode DRV_KMODS += grbeep DRV_KMODS += pcf8584 max1617 seeprom tda8444 pca9556 DRV_KMODS += ics951601 adm1031 @@ -434,7 +433,6 @@ SYS_KMODS += MISC_KMODS += obpsym bootdev vis cpr platmod md5 sha1 i2c_svc MISC_KMODS += sbd -MISC_KMODS += fcodem fcpci pcicfg.e MISC_KMODS += kmech_krb5 MISC_KMODS += zuluvm diff --git a/usr/src/uts/sun4u/daktari/Makefile b/usr/src/uts/sun4u/daktari/Makefile index 73c5ce61b3..0febbbe020 100644 --- a/usr/src/uts/sun4u/daktari/Makefile +++ b/usr/src/uts/sun4u/daktari/Makefile @@ -21,9 +21,10 @@ # # # uts/sun4u/daktari/Makefile -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# #ident "%Z%%M% %I% %E% SMI" # # This makefile drives the production of all Daktari system @@ -85,13 +86,7 @@ install: $(ROOT_DAKTARI_DIR) $(USR_DAKTARI_DIR) \ $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%/include) \ $(LINKED_PLATFORMS:%=$(USR_PLAT_DIR)/%/sbin) \ $(DAKTARI_CRYPTO_LINKS) \ - .WAIT $(DAKTARI_KMODS) \ - pcicfg - -pcicfg: $(ROOT_PSM_MISC_DIR_64)/pcicfg.e - -@$(RM) $(ROOT_DAKTARI_MISC_DIR_64)/$@ - $(SYMLINK) ../../../../sun4u/kernel/misc/sparcv9/pcicfg.e \ - $(ROOT_DAKTARI_MISC_DIR_64)/$@ + .WAIT $(DAKTARI_KMODS) $(DAKTARI_CRYPTO_LINKS): $(ROOT_DAKTARI_CRYPTO_DIR_64) -$(RM) $(ROOT_DAKTARI_CRYPTO_DIR_64)/$@; diff --git a/usr/src/uts/sun4u/fcode/Makefile b/usr/src/uts/sun4u/fcode/Makefile deleted file mode 100644 index 3e980a4cae..0000000000 --- a/usr/src/uts/sun4u/fcode/Makefile +++ /dev/null @@ -1,98 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (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 -# -# -# uts/sun4u/fcode/Makefile -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -#ident "%Z%%M% %I% %E% SMI" -# -# This makefile drives the production of the ebus driver kernel module -# -# sun4u implementation architecture dependent -# - -# -# Path to the base of the uts directory tree (usually /usr/src/uts). -# -UTSBASE = ../.. - -# -# Define the module and object file sets. -# -MODULE = fcode -OBJECTS = $(FCODE_OBJS:%=$(OBJS_DIR)/%) -LINTS = $(FCODE_OBJS:%.o=$(LINTS_DIR)/%.ln) -ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/sun4/io/efcode - - -# -# Include common rules. -# -include $(UTSBASE)/sun4u/Makefile.sun4u - -# -# Define targets -# -ALL_TARGET = $(BINARY) $(SRC_CONFFILE) -LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) - -# Turn this on once compiler understands v9 in it's backend -#INLINES += $(UTSBASE)/sun4/io/fcode.il - -# -# lint pass one enforcement -# -CFLAGS += $(CCVERBOSE) - -# -# Turn on doubleword alignment for 64 bit registers -# -CFLAGS += -dalign - -# -# Default build targets. -# -.KEEP_STATE: - -def: $(DEF_DEPS) - -all: $(ALL_DEPS) - -clean: $(CLEAN_DEPS) - -clobber: $(CLOBBER_DEPS) - -lint: $(LINT_DEPS) - -modlintlib: $(MODLINTLIB_DEPS) - -clean.lint: $(CLEAN_LINT_DEPS) - -install: $(INSTALL_DEPS) - -# -# Include common targets. -# -include $(UTSBASE)/sun4u/Makefile.targ diff --git a/usr/src/uts/sun4u/fcodem/Makefile b/usr/src/uts/sun4u/fcodem/Makefile deleted file mode 100644 index adcdd91b6c..0000000000 --- a/usr/src/uts/sun4u/fcodem/Makefile +++ /dev/null @@ -1,96 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (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 -# -# -# uts/sun4u/fcodem/Makefile -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -#ident "%Z%%M% %I% %E% SMI" -# -# This makefile drives the production of the ebus driver kernel module -# -# sun4u implementation architecture dependent -# - -# -# Path to the base of the uts directory tree (usually /usr/src/uts). -# -UTSBASE = ../.. - -# -# Define the module and object file sets. -# -MODULE = fcodem -OBJECTS = $(FCODEM_OBJS:%=$(OBJS_DIR)/%) -LINTS = $(FCODEM_OBJS:%.o=$(LINTS_DIR)/%.ln) -ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE) - -# -# Include common rules. -# -include $(UTSBASE)/sun4u/Makefile.sun4u - -# -# Define targets -# -ALL_TARGET = $(BINARY) -LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) - -# Turn this on once compiler understands v9 in it's backend -#INLINES += $(UTSBASE)/sun4/io/fcode.il - -# -# lint pass one enforcement -# -CFLAGS += $(CCVERBOSE) - -# -# Turn on doubleword alignment for 64 bit registers -# -CFLAGS += -dalign - -# -# Default build targets. -# -.KEEP_STATE: - -def: $(DEF_DEPS) - -all: $(ALL_DEPS) - -clean: $(CLEAN_DEPS) - -clobber: $(CLOBBER_DEPS) - -lint: $(LINT_DEPS) - -modlintlib: $(MODLINTLIB_DEPS) - -clean.lint: $(CLEAN_LINT_DEPS) - -install: $(INSTALL_DEPS) - -# -# Include common targets. -# -include $(UTSBASE)/sun4u/Makefile.targ diff --git a/usr/src/uts/sun4u/fcpci/Makefile b/usr/src/uts/sun4u/fcpci/Makefile deleted file mode 100644 index 101284b7bd..0000000000 --- a/usr/src/uts/sun4u/fcpci/Makefile +++ /dev/null @@ -1,96 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (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 -# -# -# uts/sun4u/fcpci/Makefile -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -#ident "%Z%%M% %I% %E% SMI" -# -# This makefile drives the production of the ebus driver kernel module -# -# sun4u implementation architecture dependent -# - -# -# Path to the base of the uts directory tree (usually /usr/src/uts). -# -UTSBASE = ../.. - -# -# Define the module and object file sets. -# -MODULE = fcpci -OBJECTS = $(FCPCI_OBJS:%=$(OBJS_DIR)/%) -LINTS = $(FCPCI_OBJS:%.o=$(LINTS_DIR)/%.ln) -ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE) - -# -# Include common rules. -# -include $(UTSBASE)/sun4u/Makefile.sun4u - -# -# Define targets -# -ALL_TARGET = $(BINARY) -LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) - -# Turn this on once compiler understands v9 in it's backend -#INLINES += $(UTSBASE)/sun4/io/fcode.il - -# -# lint pass one enforcement -# -CFLAGS += $(CCVERBOSE) - -# -# Turn on doubleword alignment for 64 bit registers -# -CFLAGS += -dalign - -# -# Default build targets. -# -.KEEP_STATE: - -def: $(DEF_DEPS) - -all: $(ALL_DEPS) - -clean: $(CLEAN_DEPS) - -clobber: $(CLOBBER_DEPS) - -lint: $(LINT_DEPS) - -modlintlib: $(MODLINTLIB_DEPS) - -clean.lint: $(CLEAN_LINT_DEPS) - -install: $(INSTALL_DEPS) - -# -# Include common targets. -# -include $(UTSBASE)/sun4u/Makefile.targ diff --git a/usr/src/uts/sun4u/io/pcicfg.e.c b/usr/src/uts/sun4u/io/pcicfg.e.c deleted file mode 100644 index 3fb3f38717..0000000000 --- a/usr/src/uts/sun4u/io/pcicfg.e.c +++ /dev/null @@ -1,5580 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (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 2005 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#pragma ident "%Z%%M% %I% %E% SMI" - -/* - * PCI configurator (pcicfg) - */ - -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#include -#include -#include -#include - -#include - -#define EFCODE21554 /* changes for supporting 21554 */ - -static int pcicfg_alloc_resource(dev_info_t *, pci_regspec_t); -static int pcicfg_free_resource(dev_info_t *, pci_regspec_t); -static int pcicfg_remove_assigned_prop(dev_info_t *, pci_regspec_t *); - -#ifdef PCICFG_INTERPRET_FCODE -static int pcicfg_fcode_assign_bars(ddi_acc_handle_t, dev_info_t *, - uint_t, uint_t, uint_t, int32_t, pci_regspec_t *); -#endif /* PCICFG_INTERPRET_FCODE */ - -/* - * ************************************************************************ - * *** Implementation specific local data structures/definitions. *** - * ************************************************************************ - */ - -static int pcicfg_start_devno = 0; /* for Debug only */ - -#define PCICFG_MAX_DEVICE 32 -#define PCICFG_MAX_FUNCTION 8 -#define PCICFG_MAX_REGISTER 64 -#define PCICFG_MAX_BUS_DEPTH 255 - -#define PCICFG_NODEVICE 42 -#define PCICFG_NOMEMORY 43 -#define PCICFG_NOMULTI 44 - -#define PCICFG_HIADDR(n) ((uint32_t)(((uint64_t)(n) & 0xFFFFFFFF00000000)>> 32)) -#define PCICFG_LOADDR(n) ((uint32_t)((uint64_t)(n) & 0x00000000FFFFFFFF)) -#define PCICFG_LADDR(lo, hi) (((uint64_t)(hi) << 32) | (uint32_t)(lo)) - -#define PCICFG_HIWORD(n) ((uint16_t)(((uint32_t)(n) & 0xFFFF0000)>> 16)) -#define PCICFG_LOWORD(n) ((uint16_t)((uint32_t)(n) & 0x0000FFFF)) -#define PCICFG_HIBYTE(n) ((uint8_t)(((uint16_t)(n) & 0xFF00)>> 8)) -#define PCICFG_LOBYTE(n) ((uint8_t)((uint16_t)(n) & 0x00FF)) - -#define PCICFG_ROUND_UP(addr, gran) ((uintptr_t)((gran+addr-1)&(~(gran-1)))) -#define PCICFG_ROUND_DOWN(addr, gran) ((uintptr_t)((addr) & ~(gran-1))) - -#define PCICFG_MEMGRAN 0x100000 -#define PCICFG_IOGRAN 0x1000 -#define PCICFG_4GIG_LIMIT 0xFFFFFFFFUL - -#define PCICFG_MEM_MULT 4 -#define PCICFG_IO_MULT 4 -#define PCICFG_RANGE_LEN 2 /* Number of range entries */ - -/* - * The following typedef is used to represent a - * 1275 "bus-range" property of a PCI Bus node. - * DAF - should be in generic include file... - */ - -typedef struct pcicfg_bus_range { - uint32_t lo; - uint32_t hi; -} pcicfg_bus_range_t; - -typedef struct pcicfg_range { - - uint32_t child_hi; - uint32_t child_mid; - uint32_t child_lo; - uint32_t parent_hi; - uint32_t parent_mid; - uint32_t parent_lo; - uint32_t size_hi; - uint32_t size_lo; - -} pcicfg_range_t; - -typedef struct hole hole_t; - -struct hole { - uint64_t start; - uint64_t len; - hole_t *next; -}; - -typedef struct pcicfg_phdl pcicfg_phdl_t; - -struct pcicfg_phdl { - - dev_info_t *dip; /* Associated with the attach point */ - pcicfg_phdl_t *next; - - uint64_t memory_base; /* Memory base for this attach point */ - uint64_t memory_last; - uint64_t memory_len; - uint32_t io_base; /* I/O base for this attach point */ - uint32_t io_last; - uint32_t io_len; - - int error; - uint_t highest_bus; /* Highest bus seen on the probe */ - - hole_t mem_hole; /* Memory hole linked list. */ - hole_t io_hole; /* IO hole linked list */ - - ndi_ra_request_t mem_req; /* allocator request for memory */ - ndi_ra_request_t io_req; /* allocator request for I/O */ -}; - -struct pcicfg_standard_prop_entry { - uchar_t *name; - uint_t config_offset; - uint_t size; -}; - - -struct pcicfg_name_entry { - uint32_t class_code; - char *name; -}; - -struct pcicfg_find_ctrl { - uint_t device; - uint_t function; - dev_info_t *dip; -}; - -/* - * List of Indirect Config Map Devices. At least the intent of the - * design is to look for a device in this list during the configure - * operation, and if the device is listed here, then it is a nontransparent - * bridge, hence load the driver and avail the config map services from - * the driver. Class and Subclass should be as defined in the PCI specs - * ie. class is 0x6, and subclass is 0x9. - */ -static struct { - uint8_t mem_range_bar_offset; - uint8_t io_range_bar_offset; - uint8_t prefetch_mem_range_bar_offset; -} pcicfg_indirect_map_devs[] = { - PCI_CONF_BASE3, PCI_CONF_BASE2, PCI_CONF_BASE3, - 0, 0, 0, -}; - -#define PCICFG_MAKE_REG_HIGH(busnum, devnum, funcnum, register)\ - (\ - ((ulong_t)(busnum & 0xff) << 16) |\ - ((ulong_t)(devnum & 0x1f) << 11) |\ - ((ulong_t)(funcnum & 0x7) << 8) |\ - ((ulong_t)(register & 0x3f))) - -/* - * debug macros: - */ -#if defined(DEBUG) -extern void prom_printf(const char *, ...); - -/* - * Following values are defined for this debug flag. - * - * 1 = dump configuration header only. - * 2 = dump generic debug data only (no config header dumped) - * 3 = dump everything (both 1 and 2) - */ -int pcicfg_debug = 0; -int pcicfg_dump_fcode = 0; - -static void debug(char *, uintptr_t, uintptr_t, - uintptr_t, uintptr_t, uintptr_t); - -#define DEBUG0(fmt)\ - debug(fmt, 0, 0, 0, 0, 0); -#define DEBUG1(fmt, a1)\ - debug(fmt, (uintptr_t)(a1), 0, 0, 0, 0); -#define DEBUG2(fmt, a1, a2)\ - debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), 0, 0, 0); -#define DEBUG3(fmt, a1, a2, a3)\ - debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2),\ - (uintptr_t)(a3), 0, 0); -#define DEBUG4(fmt, a1, a2, a3, a4)\ - debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2),\ - (uintptr_t)(a3), (uintptr_t)(a4), 0); -#else -#define DEBUG0(fmt) -#define DEBUG1(fmt, a1) -#define DEBUG2(fmt, a1, a2) -#define DEBUG3(fmt, a1, a2, a3) -#define DEBUG4(fmt, a1, a2, a3, a4) -#endif - -#ifdef PCICFG_INTERPRET_FCODE -int pcicfg_dont_interpret = 0; -#else -int pcicfg_dont_interpret = 1; -#endif - -/* - * forward declarations for routines defined in this module (called here) - */ - -static int pcicfg_add_config_reg(dev_info_t *, - uint_t, uint_t, uint_t); -static int pcicfg_probe_children(dev_info_t *, uint_t, uint_t, uint_t); - -#ifdef PCICFG_INTERPRET_FCODE -static int pcicfg_load_fcode(dev_info_t *, uint_t, uint_t, uint_t, - uint16_t, uint16_t, uchar_t **, int *, int, int); -#endif - -static int pcicfg_fcode_probe(dev_info_t *, uint_t, uint_t, uint_t, uint_t *); -static int pcicfg_probe_bridge(dev_info_t *, ddi_acc_handle_t, uint_t, - uint_t *); -static int pcicfg_free_all_resources(dev_info_t *); -static int pcicfg_alloc_new_resources(dev_info_t *); -static int pcicfg_match_dev(dev_info_t *, void *); -static dev_info_t *pcicfg_devi_find(dev_info_t *, uint_t, uint_t); -static pcicfg_phdl_t *pcicfg_find_phdl(dev_info_t *); -static pcicfg_phdl_t *pcicfg_create_phdl(dev_info_t *); -static int pcicfg_destroy_phdl(dev_info_t *); -static int pcicfg_sum_resources(dev_info_t *, void *); -static int pcicfg_find_resource_end(dev_info_t *, void *); -static int pcicfg_allocate_chunk(dev_info_t *); -static int pcicfg_program_ap(dev_info_t *); -static int pcicfg_device_assign(dev_info_t *); -static int pcicfg_bridge_assign(dev_info_t *, void *); -static int pcicfg_free_resources(dev_info_t *); -static void pcicfg_setup_bridge(pcicfg_phdl_t *, ddi_acc_handle_t); -static void pcicfg_update_bridge(pcicfg_phdl_t *, ddi_acc_handle_t); -static int pcicfg_update_assigned_prop(dev_info_t *, pci_regspec_t *); -static void pcicfg_device_on(ddi_acc_handle_t); -static void pcicfg_device_off(ddi_acc_handle_t); -static int pcicfg_set_busnode_props(dev_info_t *); -static int pcicfg_free_bridge_resources(dev_info_t *); -static int pcicfg_free_device_resources(dev_info_t *); -static int pcicfg_teardown_device(dev_info_t *); -static int pcicfg_config_setup(dev_info_t *, ddi_acc_handle_t *); -static void pcicfg_config_teardown(ddi_acc_handle_t *); -static void pcicfg_get_mem(pcicfg_phdl_t *, uint32_t, uint64_t *); -static void pcicfg_get_io(pcicfg_phdl_t *, uint32_t, uint32_t *); -static int pcicfg_update_ranges_prop(dev_info_t *, pcicfg_range_t *); -static int pcicfg_map_phys(dev_info_t *, pci_regspec_t *, caddr_t *, - ddi_device_acc_attr_t *, ddi_acc_handle_t *); -static void pcicfg_unmap_phys(ddi_acc_handle_t *, pci_regspec_t *); -static int pcicfg_dump_assigned(dev_info_t *); -static uint_t pcicfg_configure_ntbridge(dev_info_t *, uint_t, uint_t); -static int pcicfg_indirect_map(dev_info_t *dip); -static uint_t pcicfg_get_ntbridge_child_range(dev_info_t *, uint64_t *, - uint64_t *, uint_t); -static int pcicfg_is_ntbridge(dev_info_t *); -static int pcicfg_ntbridge_allocate_resources(dev_info_t *); -static int pcicfg_ntbridge_configure_done(dev_info_t *); -static int pcicfg_ntbridge_unconfigure(dev_info_t *); -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); - -#ifdef DEBUG -static void pcicfg_dump_common_config(ddi_acc_handle_t config_handle); -static void pcicfg_dump_device_config(ddi_acc_handle_t); - -static void pcicfg_dump_bridge_config(ddi_acc_handle_t config_handle); -static uint64_t pcicfg_unused_space(hole_t *, uint32_t *); - -#define PCICFG_DUMP_COMMON_CONFIG(hdl) (void)pcicfg_dump_common_config(hdl) -#define PCICFG_DUMP_DEVICE_CONFIG(hdl) (void)pcicfg_dump_device_config(hdl) -#define PCICFG_DUMP_BRIDGE_CONFIG(hdl) (void)pcicfg_dump_bridge_config(hdl) -#else -#define PCICFG_DUMP_COMMON_CONFIG(handle) -#define PCICFG_DUMP_DEVICE_CONFIG(handle) -#define PCICFG_DUMP_BRIDGE_CONFIG(handle) -#endif - -static kmutex_t pcicfg_list_mutex; /* Protects the probe handle list */ -static pcicfg_phdl_t *pcicfg_phdl_list = NULL; - -#ifndef _DONT_USE_1275_GENERIC_NAMES -/* - * Class code table - */ -static struct pcicfg_name_entry pcicfg_class_lookup [] = { - - { 0x001, "display" }, - { 0x100, "scsi" }, - { 0x101, "ide" }, - { 0x102, "fdc" }, - { 0x103, "ipi" }, - { 0x104, "raid" }, - { 0x200, "ethernet" }, - { 0x201, "token-ring" }, - { 0x202, "fddi" }, - { 0x203, "atm" }, - { 0x300, "display" }, - { 0x400, "video" }, - { 0x401, "sound" }, - { 0x500, "memory" }, - { 0x501, "flash" }, - { 0x600, "host" }, - { 0x601, "isa" }, - { 0x602, "eisa" }, - { 0x603, "mca" }, - { 0x604, "pci" }, - { 0x605, "pcmcia" }, - { 0x606, "nubus" }, - { 0x607, "cardbus" }, - { 0x609, "pci" }, - { 0x700, "serial" }, - { 0x701, "parallel" }, - { 0x800, "interrupt-controller" }, - { 0x801, "dma-controller" }, - { 0x802, "timer" }, - { 0x803, "rtc" }, - { 0x900, "keyboard" }, - { 0x901, "pen" }, - { 0x902, "mouse" }, - { 0xa00, "dock" }, - { 0xb00, "cpu" }, - { 0xc00, "firewire" }, - { 0xc01, "access-bus" }, - { 0xc02, "ssa" }, - { 0xc03, "usb" }, - { 0xc04, "fibre-channel" }, - { 0, 0 } -}; -#endif /* _DONT_USE_1275_GENERIC_NAMES */ - -/* - * Module control operations - */ - -extern struct mod_ops mod_miscops; - -static struct modlmisc modlmisc = { - &mod_miscops, /* Type of module */ - "PCI Config (EFCode Enabled) v%I%" -}; - -static struct modlinkage modlinkage = { - MODREV_1, (void *)&modlmisc, NULL -}; - -#ifdef DEBUG - -static void -pcicfg_dump_common_config(ddi_acc_handle_t config_handle) -{ - if ((pcicfg_debug & 1) == 0) - return; - cmn_err(CE_CONT, " Vendor ID = [0x%x]\n", - pci_config_get16(config_handle, PCI_CONF_VENID)); - cmn_err(CE_CONT, " Device ID = [0x%x]\n", - pci_config_get16(config_handle, PCI_CONF_DEVID)); - cmn_err(CE_CONT, " Command REG = [0x%x]\n", - pci_config_get16(config_handle, PCI_CONF_COMM)); - cmn_err(CE_CONT, " Status REG = [0x%x]\n", - pci_config_get16(config_handle, PCI_CONF_STAT)); - cmn_err(CE_CONT, " Revision ID = [0x%x]\n", - pci_config_get8(config_handle, PCI_CONF_REVID)); - cmn_err(CE_CONT, " Prog Class = [0x%x]\n", - pci_config_get8(config_handle, PCI_CONF_PROGCLASS)); - cmn_err(CE_CONT, " Dev Class = [0x%x]\n", - pci_config_get8(config_handle, PCI_CONF_SUBCLASS)); - cmn_err(CE_CONT, " Base Class = [0x%x]\n", - pci_config_get8(config_handle, PCI_CONF_BASCLASS)); - cmn_err(CE_CONT, " Device ID = [0x%x]\n", - pci_config_get8(config_handle, PCI_CONF_CACHE_LINESZ)); - cmn_err(CE_CONT, " Header Type = [0x%x]\n", - pci_config_get8(config_handle, PCI_CONF_HEADER)); - cmn_err(CE_CONT, " BIST = [0x%x]\n", - pci_config_get8(config_handle, PCI_CONF_BIST)); - cmn_err(CE_CONT, " BASE 0 = [0x%x]\n", - pci_config_get32(config_handle, PCI_CONF_BASE0)); - cmn_err(CE_CONT, " BASE 1 = [0x%x]\n", - pci_config_get32(config_handle, PCI_CONF_BASE1)); - -} - -static void -pcicfg_dump_device_config(ddi_acc_handle_t config_handle) -{ - if ((pcicfg_debug & 1) == 0) - return; - pcicfg_dump_common_config(config_handle); - - cmn_err(CE_CONT, " BASE 2 = [0x%x]\n", - pci_config_get32(config_handle, PCI_CONF_BASE2)); - cmn_err(CE_CONT, " BASE 3 = [0x%x]\n", - pci_config_get32(config_handle, PCI_CONF_BASE3)); - cmn_err(CE_CONT, " BASE 4 = [0x%x]\n", - pci_config_get32(config_handle, PCI_CONF_BASE4)); - cmn_err(CE_CONT, " BASE 5 = [0x%x]\n", - pci_config_get32(config_handle, PCI_CONF_BASE5)); - cmn_err(CE_CONT, " Cardbus CIS = [0x%x]\n", - pci_config_get32(config_handle, PCI_CONF_CIS)); - cmn_err(CE_CONT, " Sub VID = [0x%x]\n", - pci_config_get16(config_handle, PCI_CONF_SUBVENID)); - cmn_err(CE_CONT, " Sub SID = [0x%x]\n", - pci_config_get16(config_handle, PCI_CONF_SUBSYSID)); - cmn_err(CE_CONT, " ROM = [0x%x]\n", - pci_config_get32(config_handle, PCI_CONF_ROM)); - cmn_err(CE_CONT, " I Line = [0x%x]\n", - pci_config_get8(config_handle, PCI_CONF_ILINE)); - cmn_err(CE_CONT, " I Pin = [0x%x]\n", - pci_config_get8(config_handle, PCI_CONF_IPIN)); - cmn_err(CE_CONT, " Max Grant = [0x%x]\n", - pci_config_get8(config_handle, PCI_CONF_MIN_G)); - cmn_err(CE_CONT, " Max Latent = [0x%x]\n", - pci_config_get8(config_handle, PCI_CONF_MAX_L)); -} - -static void -pcicfg_dump_bridge_config(ddi_acc_handle_t config_handle) -{ - if ((pcicfg_debug & 1) == 0) - return; - - pcicfg_dump_common_config(config_handle); - - cmn_err(CE_CONT, "........................................\n"); - - cmn_err(CE_CONT, " Pri Bus = [0x%x]\n", - pci_config_get8(config_handle, PCI_BCNF_PRIBUS)); - cmn_err(CE_CONT, " Sec Bus = [0x%x]\n", - pci_config_get8(config_handle, PCI_BCNF_SECBUS)); - cmn_err(CE_CONT, " Sub Bus = [0x%x]\n", - pci_config_get8(config_handle, PCI_BCNF_SUBBUS)); - cmn_err(CE_CONT, " Latency = [0x%x]\n", - pci_config_get8(config_handle, PCI_BCNF_LATENCY_TIMER)); - cmn_err(CE_CONT, " I/O Base LO = [0x%x]\n", - pci_config_get8(config_handle, PCI_BCNF_IO_BASE_LOW)); - cmn_err(CE_CONT, " I/O Lim LO = [0x%x]\n", - pci_config_get8(config_handle, PCI_BCNF_IO_LIMIT_LOW)); - cmn_err(CE_CONT, " Sec. Status = [0x%x]\n", - pci_config_get16(config_handle, PCI_BCNF_SEC_STATUS)); - cmn_err(CE_CONT, " Mem Base = [0x%x]\n", - pci_config_get16(config_handle, PCI_BCNF_MEM_BASE)); - cmn_err(CE_CONT, " Mem Limit = [0x%x]\n", - pci_config_get16(config_handle, PCI_BCNF_MEM_LIMIT)); - cmn_err(CE_CONT, " PF Mem Base = [0x%x]\n", - pci_config_get16(config_handle, PCI_BCNF_PF_BASE_LOW)); - cmn_err(CE_CONT, " PF Mem Lim = [0x%x]\n", - pci_config_get16(config_handle, PCI_BCNF_PF_LIMIT_LOW)); - cmn_err(CE_CONT, " PF Base HI = [0x%x]\n", - pci_config_get32(config_handle, PCI_BCNF_PF_BASE_HIGH)); - cmn_err(CE_CONT, " PF Lim HI = [0x%x]\n", - pci_config_get32(config_handle, PCI_BCNF_PF_LIMIT_HIGH)); - cmn_err(CE_CONT, " I/O Base HI = [0x%x]\n", - pci_config_get16(config_handle, PCI_BCNF_IO_BASE_HI)); - cmn_err(CE_CONT, " I/O Lim HI = [0x%x]\n", - pci_config_get16(config_handle, PCI_BCNF_IO_LIMIT_HI)); - cmn_err(CE_CONT, " ROM addr = [0x%x]\n", - pci_config_get32(config_handle, PCI_BCNF_ROM)); - cmn_err(CE_CONT, " Intr Line = [0x%x]\n", - pci_config_get8(config_handle, PCI_BCNF_ILINE)); - cmn_err(CE_CONT, " Intr Pin = [0x%x]\n", - pci_config_get8(config_handle, PCI_BCNF_IPIN)); - cmn_err(CE_CONT, " Bridge Ctrl = [0x%x]\n", - pci_config_get16(config_handle, PCI_BCNF_BCNTRL)); -} - -#endif - - -int -_init() -{ - DEBUG0("PCI configurator installed - Fcode Interpretation/21554\n"); - - mutex_init(&pcicfg_list_mutex, NULL, MUTEX_DRIVER, NULL); - return (mod_install(&modlinkage)); -} - -int -_fini(void) -{ - int error; - - error = mod_remove(&modlinkage); - if (error != 0) { - return (error); - } - mutex_destroy(&pcicfg_list_mutex); - return (0); -} - -int -_info(modinfop) -struct modinfo *modinfop; -{ - return (mod_info(&modlinkage, modinfop)); -} - -/* - * In the following functions ndi_devi_enter() without holding the - * parent dip is sufficient. This is because pci dr is driven through - * opens on the nexus which is in the device tree path above the node - * being operated on, and implicitly held due to the open. - */ - -/* - * This entry point is called to configure a device (and - * all its children) on the given bus. It is called when - * a new device is added to the PCI domain. This routine - * will create the device tree and program the devices - * registers. - */ - -int -pcicfg_configure(dev_info_t *devi, uint_t device) -{ - uint_t bus; - int len; - int func; - dev_info_t *new_device; - pcicfg_bus_range_t pci_bus_range; - int rv; - int circ; - uint_t highest_bus = 0; - - /* - * Start probing at the device specified in "device" on the - * "bus" specified. - */ - len = sizeof (pcicfg_bus_range_t); - if (ddi_getlongprop_buf(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, - "bus-range", (caddr_t)&pci_bus_range, &len) != DDI_SUCCESS) { - DEBUG0("no bus-range property\n"); - return (PCICFG_FAILURE); - } - - bus = pci_bus_range.lo; /* primary bus number of this bus node */ - - ndi_devi_enter(devi, &circ); - for (func = 0; func < PCICFG_MAX_FUNCTION; func++) { - - DEBUG3("Configuring [0x%x][0x%x][0x%x]\n", bus, device, func); - - /* - * Try executing fcode if available. - */ - switch (rv = pcicfg_fcode_probe(devi, bus, device, func, - &highest_bus)) { - case PCICFG_FAILURE: - DEBUG2("configure failed: " - "bus [0x%x] device [0x%x]\n", - bus, device); - break; - case PCICFG_NODEVICE: - DEBUG3("no device : bus " - "[0x%x] slot [0x%x] func [0x%x]\n", - bus, device, func); - break; - default: - DEBUG3("configure: bus => [%d] " - "slot => [%d] func => [%d]\n", - bus, device, func); - break; - } - - if (rv != PCICFG_SUCCESS) - break; - - if ((new_device = pcicfg_devi_find(devi, - device, func)) == NULL) { - DEBUG0("Did'nt find device node just created\n"); - goto cleanup; - } - } - ndi_devi_exit(devi, circ); - - if (func == 0) - return (PCICFG_FAILURE); /* probe failed */ - else - return (PCICFG_SUCCESS); - -cleanup: - /* - * Clean up a partially created "probe state" tree. - * There are no resources allocated to the in the - * probe state. - */ - - for (func = 0; func < PCICFG_MAX_FUNCTION; func++) { - if ((new_device = pcicfg_devi_find(devi, - device, func)) == NULL) { - DEBUG0("No more devices to clean up\n"); - break; - } - - DEBUG2("Cleaning up device [0x%x] function [0x%x]\n", - device, func); - /* - * If this was a bridge device it will have a - * probe handle - if not, no harm in calling this. - */ - (void) pcicfg_destroy_phdl(new_device); - /* - * This will free up the node - */ - (void) ndi_devi_offline(new_device, NDI_DEVI_REMOVE); - } - ndi_devi_exit(devi, circ); - - return (PCICFG_FAILURE); -} - -/* - * configure the child nodes of ntbridge. new_device points to ntbridge itself - */ -/*ARGSUSED*/ -static uint_t -pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device) -{ - int bus_range[2], rc = PCICFG_FAILURE, rc1, max_devs = 0; - int devno; - dev_info_t *new_ntbridgechild; - ddi_acc_handle_t config_handle; - uint16_t vid; - uint64_t next_bus; - uint64_t blen; - ndi_ra_request_t req; - - /* - * If we need to do indirect config, lets create a property here - * to let the child conf map routine know that it has to - * go through the DDI calls, and not assume the devices are - * mapped directly under the host. - */ - if ((rc = ndi_prop_update_int(DDI_DEV_T_NONE, new_device, - PCICFG_DEV_CONF_MAP_PROP, (int)DDI_SUCCESS)) - != DDI_SUCCESS) { - - DEBUG0("Cannot create indirect conf map property.\n"); - return ((int)PCICFG_FAILURE); - } - - /* create Bus node properties for ntbridge. */ - if (pcicfg_set_busnode_props(new_device) != PCICFG_SUCCESS) { - DEBUG0("Failed to set busnode props\n"); - return (rc); - } - - /* For now: Lets only support one layer of child */ - bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); - req.ra_len = 1; - if (ndi_ra_alloc(ddi_get_parent(new_device), &req, - &next_bus, &blen, NDI_RA_TYPE_PCI_BUSNUM, - NDI_RA_PASS) != NDI_SUCCESS) { - DEBUG0("ntbridge: Failed to get a bus number\n"); - return (rc); - } - - DEBUG1("ntbridge bus range start ->[%d]\n", next_bus); - - /* - * Following will change, as we detect more bridges - * on the way. - */ - bus_range[0] = (int)next_bus; - bus_range[1] = (int)next_bus; - - if (ndi_prop_update_int_array(DDI_DEV_T_NONE, new_device, - "bus-range", bus_range, 2) != DDI_SUCCESS) { - DEBUG0("Cannot set ntbridge bus-range property"); - return (rc); - } - - /* - * The other interface (away from the host) will be - * initialized by the nexus driver when it loads. - * We just have to set the registers and the nexus driver - * figures out the rest. - */ - - /* - * finally, lets load and attach the driver - * before configuring children of ntbridge. - */ - rc = ndi_devi_online(new_device, NDI_NO_EVENT|NDI_CONFIG); - if (rc != NDI_SUCCESS) { - cmn_err(CE_WARN, - "pcicfg: Fail:cant load nontransparent bridgd driver..\n"); - rc = PCICFG_FAILURE; - return (rc); - } - DEBUG0("pcicfg: Success loading nontransparent bridge nexus driver.."); - - /* Now set aside pci resources for our children. */ - if (pcicfg_ntbridge_allocate_resources(new_device) != - PCICFG_SUCCESS) { - max_devs = 0; - rc = PCICFG_FAILURE; - } else - max_devs = PCICFG_MAX_DEVICE; - - /* Probe devices on 2nd bus */ - for (devno = pcicfg_start_devno; devno < max_devs; devno++) { - - if (ndi_devi_alloc(new_device, DEVI_PSEUDO_NEXNAME, - (pnode_t)DEVI_SID_NODEID, &new_ntbridgechild) - != NDI_SUCCESS) { - - DEBUG0("pcicfg: Failed to alloc test node\n"); - rc = PCICFG_FAILURE; - break; - } - - if (pcicfg_add_config_reg(new_ntbridgechild, next_bus, devno, 0) - != DDI_PROP_SUCCESS) { - cmn_err(CE_WARN, - "Failed to add conf reg for ntbridge child.\n"); - (void) ndi_devi_free(new_ntbridgechild); - rc = PCICFG_FAILURE; - break; - } - - if ((rc = pci_config_setup(new_ntbridgechild, - &config_handle)) != PCICFG_SUCCESS) { - cmn_err(CE_WARN, - "Cannot map ntbridge child %x\n", devno); - (void) ndi_devi_free(new_ntbridgechild); - rc = PCICFG_FAILURE; - break; - } - - /* - * See if there is any PCI HW at this location - * by reading the Vendor ID. If it returns with 0xffff - * then there is no hardware at this location. - */ - vid = pci_config_get16(config_handle, PCI_CONF_VENID); - - pci_config_teardown(&config_handle); - (void) ndi_devi_free(new_ntbridgechild); - if (vid == 0xffff) - continue; - - /* Lets fake attachments points for each child, */ - if (pcicfg_configure(new_device, devno) != PCICFG_SUCCESS) { - int old_dev = pcicfg_start_devno; - - cmn_err(CE_WARN, - "Error configuring ntbridge child dev=%d\n", devno); - - rc = PCICFG_FAILURE; - while (old_dev != devno) { - if (pcicfg_ntbridge_unconfigure_child( - new_device, old_dev) == PCICFG_FAILURE) - - cmn_err(CE_WARN, - "Unconfig Error ntbridge child " - "dev=%d\n", old_dev); - old_dev++; - } - break; - } - } /* devno loop */ - DEBUG1("ntbridge: finish probing 2nd bus, rc=%d\n", rc); - - if (rc != PCICFG_FAILURE) - rc = pcicfg_ntbridge_configure_done(new_device); - else { - pcicfg_phdl_t *entry = pcicfg_find_phdl(new_device); - uint_t *bus; - int k; - - if (ddi_getlongprop(DDI_DEV_T_ANY, new_device, - DDI_PROP_DONTPASS, "bus-range", (caddr_t)&bus, - &k) != DDI_PROP_SUCCESS) { - DEBUG0("Failed to read bus-range property\n"); - rc = PCICFG_FAILURE; - return (rc); - } - - DEBUG2("Need to free bus [%d] range [%d]\n", - bus[0], bus[1] - bus[0] + 1); - - if (ndi_ra_free(ddi_get_parent(new_device), - (uint64_t)bus[0], (uint64_t)(bus[1] - bus[0] + 1), - NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) { - DEBUG0("Failed to free a bus number\n"); - rc = PCICFG_FAILURE; - /* - * Don't forget to free up memory from ddi_getlongprop - */ - kmem_free((caddr_t)bus, k); - - return (rc); - } - - /* - * Since no memory allocations are done for non transparent - * bridges (but instead we just set the handle with the - * already allocated memory, we just need to reset the - * following values before calling the destroy_phdl() - * function next, otherwise the it will try to free - * memory allocated as in case of a transparent bridge. - */ - entry->memory_len = 0; - entry->io_len = 0; - /* the following will free hole data. */ - (void) pcicfg_destroy_phdl(new_device); - /* - * Don't forget to free up memory from ddi_getlongprop - */ - kmem_free((caddr_t)bus, k); - } - - /* - * Unload driver just in case child configure failed! - */ - rc1 = ndi_devi_offline(new_device, NDI_NO_EVENT); - DEBUG1("pcicfg: now unloading the ntbridge driver. rc1=%d\n", rc1); - if (rc1 != NDI_SUCCESS) { - cmn_err(CE_WARN, - "pcicfg: cant unload ntbridge driver..children.\n"); - rc = PCICFG_FAILURE; - } - - return (rc); -} - -static int -pcicfg_ntbridge_allocate_resources(dev_info_t *dip) -{ - pcicfg_phdl_t *phdl; - ndi_ra_request_t *mem_request; - ndi_ra_request_t *io_request; - uint64_t boundbase, boundlen; - - phdl = pcicfg_find_phdl(dip); - ASSERT(phdl); - - mem_request = &phdl->mem_req; - io_request = &phdl->io_req; - - phdl->error = PCICFG_SUCCESS; - - /* Set Memory space handle for ntbridge */ - if (pcicfg_get_ntbridge_child_range(dip, &boundbase, &boundlen, - PCI_BASE_SPACE_MEM) != DDI_SUCCESS) { - cmn_err(CE_WARN, - "ntbridge: Mem resource information failure\n"); - phdl->memory_len = 0; - return (PCICFG_FAILURE); - } - mem_request->ra_boundbase = boundbase; - mem_request->ra_boundlen = boundbase + boundlen; - mem_request->ra_len = boundlen; - mem_request->ra_align_mask = - PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */ - mem_request->ra_flags |= NDI_RA_ALLOC_BOUNDED; - - /* - * mem_request->ra_len = - * PCICFG_ROUND_UP(mem_request->ra_len, PCICFG_MEMGRAN); - */ - - phdl->memory_base = phdl->memory_last = boundbase; - phdl->memory_len = boundlen; - phdl->mem_hole.start = phdl->memory_base; - phdl->mem_hole.len = mem_request->ra_len; - phdl->mem_hole.next = (hole_t *)NULL; - - DEBUG2("AP requested [0x%llx], needs [0x%llx] bytes of memory\n", - boundlen, mem_request->ra_len); - - /* set up a memory resource map for NT bridge */ - if (ndi_ra_map_setup(dip, NDI_RA_TYPE_MEM) == NDI_FAILURE) { - DEBUG0("Can not setup ntbridge memory resource map\n"); - return (PCICFG_FAILURE); - } - /* initialize the memory map */ - if (ndi_ra_free(dip, boundbase, boundlen, NDI_RA_TYPE_MEM, - NDI_RA_PASS) != NDI_SUCCESS) { - DEBUG0("Can not initalize ntbridge memory resource map\n"); - return (PCICFG_FAILURE); - } - /* Set IO space handle for ntbridge */ - if (pcicfg_get_ntbridge_child_range(dip, &boundbase, &boundlen, - PCI_BASE_SPACE_IO) != DDI_SUCCESS) { - cmn_err(CE_WARN, "ntbridge: IO resource information failure\n"); - phdl->io_len = 0; - return (PCICFG_FAILURE); - } - io_request->ra_len = boundlen; - io_request->ra_align_mask = - PCICFG_IOGRAN - 1; /* 4K alignment on I/O space */ - io_request->ra_boundbase = boundbase; - io_request->ra_boundlen = boundbase + boundlen; - io_request->ra_flags |= NDI_RA_ALLOC_BOUNDED; - - /* - * io_request->ra_len = - * PCICFG_ROUND_UP(io_request->ra_len, PCICFG_IOGRAN); - */ - - phdl->io_base = phdl->io_last = (uint32_t)boundbase; - phdl->io_len = (uint32_t)boundlen; - phdl->io_hole.start = phdl->io_base; - phdl->io_hole.len = io_request->ra_len; - phdl->io_hole.next = (hole_t *)NULL; - - DEBUG2("AP requested [0x%llx], needs [0x%llx] bytes of IO\n", - boundlen, io_request->ra_len); - - DEBUG2("MEMORY BASE = [0x%x] length [0x%x]\n", - phdl->memory_base, phdl->memory_len); - DEBUG2("IO BASE = [0x%x] length [0x%x]\n", - phdl->io_base, phdl->io_len); - - /* set up a IO resource map for NT bridge */ - if (ndi_ra_map_setup(dip, NDI_RA_TYPE_IO) == NDI_FAILURE) { - DEBUG0("Can not setup ntbridge memory resource map\n"); - return (PCICFG_FAILURE); - } - /* initialize the IO map */ - if (ndi_ra_free(dip, boundbase, boundlen, NDI_RA_TYPE_IO, - NDI_RA_PASS) != NDI_SUCCESS) { - DEBUG0("Can not initalize ntbridge memory resource map\n"); - return (PCICFG_FAILURE); - } - - return (PCICFG_SUCCESS); -} - -static int -pcicfg_ntbridge_configure_done(dev_info_t *dip) -{ - pcicfg_range_t range[PCICFG_RANGE_LEN]; - pcicfg_phdl_t *entry; - uint_t len; - pcicfg_bus_range_t bus_range; - int new_bus_range[2]; - - DEBUG1("Configuring children for %llx\n", dip); - - entry = pcicfg_find_phdl(dip); - ASSERT(entry); - - bzero((caddr_t)range, - sizeof (pcicfg_range_t) * PCICFG_RANGE_LEN); - range[1].child_hi = range[1].parent_hi |= - (PCI_REG_REL_M | PCI_ADDR_MEM32); - range[1].child_lo = range[1].parent_lo = (uint32_t)entry->memory_base; - - range[0].child_hi = range[0].parent_hi |= - (PCI_REG_REL_M | PCI_ADDR_IO); - range[0].child_lo = range[0].parent_lo = (uint32_t)entry->io_base; - - len = sizeof (pcicfg_bus_range_t); - if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, - "bus-range", (caddr_t)&bus_range, (int *)&len) != DDI_SUCCESS) { - DEBUG0("no bus-range property\n"); - return (PCICFG_FAILURE); - } - - new_bus_range[0] = bus_range.lo; /* primary bus number */ - if (entry->highest_bus) { /* secondary bus number */ - if (entry->highest_bus < bus_range.lo) { - cmn_err(CE_WARN, - "ntbridge bus range invalid !(%d,%d)\n", - bus_range.lo, entry->highest_bus); - new_bus_range[1] = bus_range.lo + entry->highest_bus; - } - else - new_bus_range[1] = entry->highest_bus; - } - else - new_bus_range[1] = bus_range.hi; - - DEBUG2("ntbridge: bus range lo=%x, hi=%x\n", - new_bus_range[0], new_bus_range[1]); - - if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, - "bus-range", new_bus_range, 2) != DDI_SUCCESS) { - DEBUG0("Failed to set bus-range property"); - entry->error = PCICFG_FAILURE; - return (PCICFG_FAILURE); - } - -#ifdef DEBUG - { - uint64_t unused; - unused = pcicfg_unused_space(&entry->io_hole, &len); - DEBUG2("ntbridge: Unused IO space %llx bytes over %d holes\n", - unused, len); - } -#endif - - range[0].size_lo = entry->io_len; - if (pcicfg_update_ranges_prop(dip, &range[0])) { - DEBUG0("Failed to update ranges (i/o)\n"); - entry->error = PCICFG_FAILURE; - return (PCICFG_FAILURE); - } - -#ifdef DEBUG - { - uint64_t unused; - unused = pcicfg_unused_space(&entry->mem_hole, &len); - DEBUG2("ntbridge: Unused Mem space %llx bytes over %d holes\n", - unused, len); - } -#endif - - range[1].size_lo = entry->memory_len; - if (pcicfg_update_ranges_prop(dip, &range[1])) { - DEBUG0("Failed to update ranges (memory)\n"); - entry->error = PCICFG_FAILURE; - return (PCICFG_FAILURE); - } - - return (PCICFG_SUCCESS); -} - -static int -pcicfg_ntbridge_unconfigure_child(dev_info_t *new_device, uint_t devno) -{ - - dev_info_t *new_ntbridgechild; - int len, bus; - uint16_t vid; - ddi_acc_handle_t config_handle; - pcicfg_bus_range_t pci_bus_range; - - len = sizeof (pcicfg_bus_range_t); - if (ddi_getlongprop_buf(DDI_DEV_T_ANY, new_device, DDI_PROP_DONTPASS, - "bus-range", (caddr_t)&pci_bus_range, &len) != DDI_SUCCESS) { - DEBUG0("no bus-range property\n"); - return (PCICFG_FAILURE); - } - - bus = pci_bus_range.lo; /* primary bus number of this bus node */ - - if (ndi_devi_alloc(new_device, DEVI_PSEUDO_NEXNAME, - (pnode_t)DEVI_SID_NODEID, &new_ntbridgechild) != NDI_SUCCESS) { - - DEBUG0("pcicfg: Failed to alloc test node\n"); - return (PCICFG_FAILURE); - } - - if (pcicfg_add_config_reg(new_ntbridgechild, bus, devno, 0) - != DDI_PROP_SUCCESS) { - cmn_err(CE_WARN, - "Unconfigure: Failed to add conf reg prop for ntbridge " - "child.\n"); - (void) ndi_devi_free(new_ntbridgechild); - return (PCICFG_FAILURE); - } - - if (pcicfg_config_setup(new_ntbridgechild, &config_handle) - != DDI_SUCCESS) { - cmn_err(CE_WARN, - "pcicfg: Cannot map ntbridge child %x\n", devno); - (void) ndi_devi_free(new_ntbridgechild); - return (PCICFG_FAILURE); - } - - /* - * See if there is any PCI HW at this location - * by reading the Vendor ID. If it returns with 0xffff - * then there is no hardware at this location. - */ - vid = pci_config_get16(config_handle, PCI_CONF_VENID); - - pci_config_teardown(&config_handle); - (void) ndi_devi_free(new_ntbridgechild); - if (vid == 0xffff) - return (PCICFG_NODEVICE); - - return (pcicfg_unconfigure(new_device, devno)); -} - -static int -pcicfg_ntbridge_unconfigure(dev_info_t *dip) -{ - pcicfg_phdl_t *entry = pcicfg_find_phdl(dip); - uint_t *bus; - int k, rc = PCICFG_FAILURE; - - if (entry->memory_len) - if (ndi_ra_map_destroy(dip, NDI_RA_TYPE_MEM) == NDI_FAILURE) { - DEBUG1("cannot destroy ntbridge memory map size=%x\n", - entry->memory_len); - return (PCICFG_FAILURE); - } - if (entry->io_len) - if (ndi_ra_map_destroy(dip, NDI_RA_TYPE_IO) == NDI_FAILURE) { - DEBUG1("cannot destroy ntbridge io map size=%x\n", - entry->io_len); - return (PCICFG_FAILURE); - } - if (ddi_getlongprop(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, "bus-range", (caddr_t)&bus, - &k) != DDI_PROP_SUCCESS) { - DEBUG0("ntbridge: Failed to read bus-range property\n"); - return (rc); - } - - DEBUG2("ntbridge: Need to free bus [%d] range [%d]\n", - bus[0], bus[1] - bus[0] + 1); - - if (ndi_ra_free(ddi_get_parent(dip), - (uint64_t)bus[0], (uint64_t)(bus[1] - bus[0] + 1), - NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) { - DEBUG0("ntbridge: Failed to free a bus number\n"); - /* - * Don't forget to free up memory from ddi_getlongprop - */ - kmem_free((caddr_t)bus, k); - - return (rc); - } - - /* - * Don't forget to free up memory from ddi_getlongprop - */ - kmem_free((caddr_t)bus, k); - - /* - * Since our resources will be freed at the parent level, - * just reset these values. - */ - entry->memory_len = 0; - entry->io_len = 0; - /* the following will also free hole data. */ - return (pcicfg_destroy_phdl(dip)); - -} - -static int -pcicfg_is_ntbridge(dev_info_t *dip) -{ - ddi_acc_handle_t config_handle; - uint8_t class, subclass; - int rc = DDI_SUCCESS; - - if (pcicfg_config_setup(dip, &config_handle) != DDI_SUCCESS) { - cmn_err(CE_WARN, - "pcicfg: cannot map config space, to get map type\n"); - return (DDI_FAILURE); - } - class = pci_config_get8(config_handle, PCI_CONF_BASCLASS); - subclass = pci_config_get8(config_handle, PCI_CONF_SUBCLASS); - - /* check for class=6, subclass=9, for non transparent bridges. */ - if ((class != PCI_CLASS_BRIDGE) || (subclass != PCI_BRIDGE_STBRIDGE)) - rc = DDI_FAILURE; - - DEBUG3("pcicfg: checking device %x,%x for indirect map. rc=%d\n", - pci_config_get16(config_handle, PCI_CONF_VENID), - pci_config_get16(config_handle, PCI_CONF_DEVID), - rc); - pci_config_teardown(&config_handle); - return (rc); -} - -/* - * this function is called only for SPARC platforms, where we may have - * a mix n' match of direct vs indirectly mapped configuration space. - * On x86, this function does not get called. We always return TRUE - * via a macro for x86. - */ -/*ARGSUSED*/ -static int -pcicfg_indirect_map(dev_info_t *dip) -{ -#if defined(__sparc) - int rc = DDI_FAILURE; - - if (ddi_getprop(DDI_DEV_T_ANY, ddi_get_parent(dip), DDI_PROP_DONTPASS, - PCICFG_DEV_CONF_MAP_PROP, DDI_FAILURE) != DDI_FAILURE) - rc = DDI_SUCCESS; - else - if (ddi_getprop(DDI_DEV_T_ANY, ddi_get_parent(dip), - DDI_PROP_DONTPASS, PCICFG_BUS_CONF_MAP_PROP, - DDI_FAILURE) != DDI_FAILURE) - rc = DDI_SUCCESS; - - return (rc); -#else - return (DDI_SUCCESS); -#endif -} - -static uint_t -pcicfg_get_ntbridge_child_range(dev_info_t *dip, uint64_t *boundbase, - uint64_t *boundlen, uint_t space_type) -{ - int length, found = DDI_FAILURE, acount, i, ibridge; - pci_regspec_t *assigned; - - if ((ibridge = pcicfg_is_ntbridge(dip)) == DDI_FAILURE) - return (found); - - if (ddi_getlongprop(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned, - &length) != DDI_PROP_SUCCESS) { - DEBUG1("Failed to get assigned-addresses property %llx\n", dip); - return (found); - } - DEBUG1("pcicfg: ntbridge child range: dip = %s\n", - ddi_driver_name(dip)); - - acount = length / sizeof (pci_regspec_t); - - for (i = 0; i < acount; i++) { - if ((PCI_REG_REG_G(assigned[i].pci_phys_hi) - == pcicfg_indirect_map_devs[ibridge].mem_range_bar_offset) && - (space_type == PCI_BASE_SPACE_MEM)) { - found = DDI_SUCCESS; - break; - } else { - if ((PCI_REG_REG_G(assigned[i].pci_phys_hi) - == pcicfg_indirect_map_devs[ibridge].io_range_bar_offset) && - (space_type == PCI_BASE_SPACE_IO)) { - found = DDI_SUCCESS; - break; - } - } - } - DEBUG3("pcicfg: ntbridge child range: space=%x, base=%lx, len=%lx\n", - space_type, assigned[i].pci_phys_low, assigned[i].pci_size_low); - - if (found == DDI_SUCCESS) { - *boundbase = assigned[i].pci_phys_low; - *boundlen = assigned[i].pci_size_low; - } - - kmem_free(assigned, length); - return (found); -} - -/* - * This will turn resources allocated by pcicfg_configure() - * and remove the device tree from the attachment point - * and below. The routine assumes the devices have their - * drivers detached. - */ -int -pcicfg_unconfigure(dev_info_t *devi, uint_t device) -{ - dev_info_t *child_dip; - int func; - int i; - - /* - * Cycle through devices to make sure none are busy. - * If a single device is busy fail the whole unconfigure. - */ - for (func = 0; func < PCICFG_MAX_FUNCTION; func++) { - if ((child_dip = pcicfg_devi_find(devi, device, func)) == NULL) - break; - - if (ndi_devi_offline(child_dip, NDI_UNCONFIG) == NDI_SUCCESS) - continue; - /* - * Device function is busy. Before returning we have to - * put all functions back online which were taken - * offline during the process. - */ - DEBUG2("Device [0x%x] function [%x] is busy\n", device, func); - for (i = 0; i < func; i++) { - if ((child_dip = pcicfg_devi_find(devi, device, i)) - == NULL) { - DEBUG0("No more devices to put back on line!!\n"); - /* - * Made it through all functions - */ - break; - } - if (ndi_devi_online(child_dip, NDI_CONFIG) != NDI_SUCCESS) { - DEBUG0("Failed to put back devices state\n"); - return (PCICFG_FAILURE); - } - } - return (PCICFG_FAILURE); - } - - /* - * Now, tear down all devinfo nodes for this AP. - */ - for (func = 0; func < PCICFG_MAX_FUNCTION; func++) { - if ((child_dip = pcicfg_devi_find(devi, - device, func)) == NULL) { - DEBUG0("No more devices to tear down!\n"); - break; - } - - DEBUG2("Tearing down device [0x%x] function [0x%x]\n", - device, func); - - if (pcicfg_is_ntbridge(child_dip) != DDI_FAILURE) - if (pcicfg_ntbridge_unconfigure(child_dip) != - PCICFG_SUCCESS) { - cmn_err(CE_WARN, - "ntbridge: unconfigure failed\n"); - return (PCICFG_FAILURE); - } - - if (pcicfg_teardown_device(child_dip) != PCICFG_SUCCESS) { - DEBUG2("Failed to tear down device [0x%x]" - "function [0x%x]\n", - device, func); - return (PCICFG_FAILURE); - } - } - - return (PCICFG_SUCCESS); -} - -static int -pcicfg_teardown_device(dev_info_t *dip) -{ - ddi_acc_handle_t config_handle; - - /* - * Free up resources associated with 'dip' - */ - - if (pcicfg_free_resources(dip) != PCICFG_SUCCESS) { - DEBUG0("Failed to free resources\n"); - return (PCICFG_FAILURE); - } - - /* - * This will disable the device - */ - if (pci_config_setup(dip, &config_handle) != PCICFG_SUCCESS) { - return (PCICFG_FAILURE); - } - - pcicfg_device_off(config_handle); - pci_config_teardown(&config_handle); - - /* - * The framework provides this routine which can - * tear down a sub-tree. - */ - if (ndi_devi_offline(dip, NDI_DEVI_REMOVE) != NDI_SUCCESS) { - DEBUG0("Failed to offline and remove node\n"); - return (PCICFG_FAILURE); - } - - return (PCICFG_SUCCESS); -} - -/* - * BEGIN GENERIC SUPPORT ROUTINES - */ -static pcicfg_phdl_t * -pcicfg_find_phdl(dev_info_t *dip) -{ - pcicfg_phdl_t *entry; - mutex_enter(&pcicfg_list_mutex); - for (entry = pcicfg_phdl_list; entry != NULL; entry = entry->next) { - if (entry->dip == dip) { - mutex_exit(&pcicfg_list_mutex); - return (entry); - } - } - mutex_exit(&pcicfg_list_mutex); - - /* - * Did'nt find entry - create one - */ - return (pcicfg_create_phdl(dip)); -} - -static pcicfg_phdl_t * -pcicfg_create_phdl(dev_info_t *dip) -{ - pcicfg_phdl_t *new; - - new = (pcicfg_phdl_t *)kmem_zalloc(sizeof (pcicfg_phdl_t), - KM_SLEEP); - - new->dip = dip; - mutex_enter(&pcicfg_list_mutex); - new->next = pcicfg_phdl_list; - pcicfg_phdl_list = new; - mutex_exit(&pcicfg_list_mutex); - - return (new); -} - -static int -pcicfg_destroy_phdl(dev_info_t *dip) -{ - pcicfg_phdl_t *entry; - pcicfg_phdl_t *follow = NULL; - - mutex_enter(&pcicfg_list_mutex); - for (entry = pcicfg_phdl_list; entry != NULL; follow = entry, - entry = entry->next) { - if (entry->dip == dip) { - if (entry == pcicfg_phdl_list) { - pcicfg_phdl_list = entry->next; - } else { - follow->next = entry->next; - } - /* - * If this entry has any allocated memory - * or IO space associated with it, that - * must be freed up. - */ - if (entry->memory_len > 0) { - (void) ndi_ra_free(ddi_get_parent(dip), - entry->memory_base, - entry->memory_len, - NDI_RA_TYPE_MEM, NDI_RA_PASS); - } - pcicfg_free_hole(&entry->mem_hole); - - if (entry->io_len > 0) { - (void) ndi_ra_free(ddi_get_parent(dip), - entry->io_base, - entry->io_len, - NDI_RA_TYPE_IO, NDI_RA_PASS); - } - pcicfg_free_hole(&entry->io_hole); - - /* - * Destroy this entry - */ - kmem_free((caddr_t)entry, sizeof (pcicfg_phdl_t)); - mutex_exit(&pcicfg_list_mutex); - return (PCICFG_SUCCESS); - } - } - mutex_exit(&pcicfg_list_mutex); - /* - * Did'nt find the entry - */ - return (PCICFG_FAILURE); -} - -static int -pcicfg_program_ap(dev_info_t *dip) -{ - pcicfg_phdl_t *phdl; - uint8_t header_type; - ddi_acc_handle_t handle; - pcicfg_phdl_t *entry; - - if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) { - DEBUG0("Failed to map config space!\n"); - return (PCICFG_FAILURE); - - } - - header_type = pci_config_get8(handle, PCI_CONF_HEADER); - - (void) pcicfg_config_teardown(&handle); - - if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { - - if (pcicfg_allocate_chunk(dip) != PCICFG_SUCCESS) { - DEBUG0("Not enough memory to hotplug\n"); - (void) pcicfg_destroy_phdl(dip); - return (PCICFG_FAILURE); - } - - phdl = pcicfg_find_phdl(dip); - ASSERT(phdl); - - (void) pcicfg_bridge_assign(dip, (void *)phdl); - - if (phdl->error != PCICFG_SUCCESS) { - DEBUG0("Problem assigning bridge\n"); - (void) pcicfg_destroy_phdl(dip); - return (phdl->error); - } - - /* - * Successfully allocated and assigned - * memory. Set the memory and IO length - * to zero so when the handle is freed up - * it will not de-allocate assigned resources. - */ - entry = (pcicfg_phdl_t *)phdl; - - entry->memory_len = entry->io_len = 0; - - /* - * Free up the "entry" structure. - */ - (void) pcicfg_destroy_phdl(dip); - } else { - if (pcicfg_device_assign(dip) != PCICFG_SUCCESS) { - return (PCICFG_FAILURE); - } - } - return (PCICFG_SUCCESS); -} - -static int -pcicfg_bridge_assign(dev_info_t *dip, void *hdl) -{ - ddi_acc_handle_t handle; - pci_regspec_t *reg; - int length; - int rcount; - int i; - int offset; - uint64_t mem_answer; - uint32_t io_answer; - int count; - uint8_t header_type; - pcicfg_range_t range[PCICFG_RANGE_LEN]; - int bus_range[2]; - - pcicfg_phdl_t *entry = (pcicfg_phdl_t *)hdl; - - DEBUG1("bridge assign: assigning addresses to %s\n", - ddi_get_name(dip)); - - entry->error = PCICFG_SUCCESS; - - if (entry == NULL) { - DEBUG0("Failed to get entry\n"); - entry->error = PCICFG_FAILURE; - return (DDI_WALK_TERMINATE); - } - - if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) { - DEBUG0("Failed to map config space!\n"); - entry->error = PCICFG_FAILURE; - return (DDI_WALK_TERMINATE); - } - - header_type = pci_config_get8(handle, PCI_CONF_HEADER); - - if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { - - bzero((caddr_t)range, - sizeof (pcicfg_range_t) * PCICFG_RANGE_LEN); - - (void) pcicfg_setup_bridge(entry, handle); - - range[0].child_hi = range[0].parent_hi |= - (PCI_REG_REL_M | PCI_ADDR_IO); - range[0].child_lo = range[0].parent_lo = - entry->io_last; - range[1].child_hi = range[1].parent_hi |= - (PCI_REG_REL_M | PCI_ADDR_MEM32); - range[1].child_lo = range[1].parent_lo = - entry->memory_last; - - ndi_devi_enter(dip, &count); - ddi_walk_devs(ddi_get_child(dip), - pcicfg_bridge_assign, (void *)entry); - ndi_devi_exit(dip, count); - - (void) pcicfg_update_bridge(entry, handle); - - bus_range[0] = pci_config_get8(handle, PCI_BCNF_SECBUS); - bus_range[1] = pci_config_get8(handle, PCI_BCNF_SUBBUS); - - if (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, - "bus-range", bus_range, 2) != DDI_SUCCESS) { - DEBUG0("Failed to set bus-range property"); - entry->error = PCICFG_FAILURE; - return (DDI_WALK_TERMINATE); - } - - if (entry->io_len > 0) { - range[0].size_lo = entry->io_last - entry->io_base; - if (pcicfg_update_ranges_prop(dip, &range[0])) { - DEBUG0("Failed to update ranges (i/o)\n"); - entry->error = PCICFG_FAILURE; - return (DDI_WALK_TERMINATE); - } - } - if (entry->memory_len > 0) { - range[1].size_lo = - entry->memory_last - entry->memory_base; - if (pcicfg_update_ranges_prop(dip, &range[1])) { - DEBUG0("Failed to update ranges (memory)\n"); - entry->error = PCICFG_FAILURE; - return (DDI_WALK_TERMINATE); - } - } - - (void) pcicfg_device_on(handle); - - PCICFG_DUMP_BRIDGE_CONFIG(handle); - - return (DDI_WALK_PRUNECHILD); - } - - /* - * If there is an interrupt pin set program - * interrupt line with default values. - */ - if (pci_config_get8(handle, PCI_CONF_IPIN)) { - pci_config_put8(handle, PCI_CONF_ILINE, 0xf); - } - - /* - * A single device (under a bridge). - * For each "reg" property with a length, allocate memory - * and program the base registers. - */ - if (ddi_getlongprop(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, "reg", (caddr_t)®, - &length) != DDI_PROP_SUCCESS) { - DEBUG0("Failed to read reg property\n"); - entry->error = PCICFG_FAILURE; - return (DDI_WALK_TERMINATE); - } - - rcount = length / sizeof (pci_regspec_t); - offset = PCI_CONF_BASE0; - for (i = 0; i < rcount; i++) { - if ((reg[i].pci_size_low != 0)|| - (reg[i].pci_size_hi != 0)) { - - offset = PCI_REG_REG_G(reg[i].pci_phys_hi); - - switch (PCI_REG_ADDR_G(reg[i].pci_phys_hi)) { - case PCI_REG_ADDR_G(PCI_ADDR_MEM64): - - (void) pcicfg_get_mem(entry, - reg[i].pci_size_low, &mem_answer); - pci_config_put64(handle, offset, mem_answer); - DEBUG2("REGISTER off %x (64)LO ----> [0x%x]\n", - offset, - pci_config_get32(handle, offset)); - DEBUG2("REGISTER off %x (64)HI ----> [0x%x]\n", - offset + 4, - pci_config_get32(handle, offset + 4)); - - reg[i].pci_phys_low = PCICFG_HIADDR(mem_answer); - reg[i].pci_phys_mid = - PCICFG_LOADDR(mem_answer); - - break; - - case PCI_REG_ADDR_G(PCI_ADDR_MEM32): - /* allocate memory space from the allocator */ - - (void) pcicfg_get_mem(entry, - reg[i].pci_size_low, &mem_answer); - pci_config_put32(handle, - offset, (uint32_t)mem_answer); - - DEBUG2("REGISTER off %x(32)LO ----> [0x%x]\n", - offset, - pci_config_get32(handle, offset)); - - reg[i].pci_phys_low = (uint32_t)mem_answer; - - break; - case PCI_REG_ADDR_G(PCI_ADDR_IO): - /* allocate I/O space from the allocator */ - - (void) pcicfg_get_io(entry, - reg[i].pci_size_low, &io_answer); - pci_config_put32(handle, offset, io_answer); - - DEBUG2("REGISTER off %x (I/O)LO ----> [0x%x]\n", - offset, - pci_config_get32(handle, offset)); - - reg[i].pci_phys_low = io_answer; - - break; - default: - DEBUG0("Unknown register type\n"); - kmem_free(reg, length); - (void) pcicfg_config_teardown(&handle); - entry->error = PCICFG_FAILURE; - return (DDI_WALK_TERMINATE); - } /* switch */ - - /* - * Now that memory locations are assigned, - * update the assigned address property. - */ - if (pcicfg_update_assigned_prop(dip, - ®[i]) != PCICFG_SUCCESS) { - kmem_free(reg, length); - (void) pcicfg_config_teardown(&handle); - entry->error = PCICFG_FAILURE; - return (DDI_WALK_TERMINATE); - } - } - } - (void) pcicfg_device_on(handle); - - PCICFG_DUMP_DEVICE_CONFIG(handle); - - (void) pcicfg_config_teardown(&handle); - /* - * Don't forget to free up memory from ddi_getlongprop - */ - kmem_free((caddr_t)reg, length); - - return (DDI_WALK_CONTINUE); -} -static int -pcicfg_device_assign(dev_info_t *dip) -{ - ddi_acc_handle_t handle; - pci_regspec_t *reg; - int length; - int rcount; - int i; - int offset; - ndi_ra_request_t request; - uint64_t answer; - uint64_t alen; - - - DEBUG1("%llx now under configuration\n", dip); - - /* - * XXX Failure here should be noted - */ - if (ddi_getlongprop(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, "reg", (caddr_t)®, - &length) != DDI_PROP_SUCCESS) { - DEBUG0("Failed to read reg property\n"); - return (PCICFG_FAILURE); - } - - if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) { - DEBUG0("Failed to map config space!\n"); - /* - * Don't forget to free up memory from ddi_getlongprop - */ - kmem_free((caddr_t)reg, length); - - return (PCICFG_FAILURE); - } - - /* - * A single device - * - * For each "reg" property with a length, allocate memory - * and program the base registers. - */ - - /* - * If there is an interrupt pin set program - * interrupt line with default values. - */ - if (pci_config_get8(handle, PCI_CONF_IPIN)) { - pci_config_put8(handle, PCI_CONF_ILINE, 0xf); - } - - bzero((caddr_t)&request, sizeof (ndi_ra_request_t)); - - request.ra_flags |= NDI_RA_ALIGN_SIZE; - request.ra_boundbase = 0; - request.ra_boundlen = PCICFG_4GIG_LIMIT; - - rcount = length / sizeof (pci_regspec_t); - offset = PCI_CONF_BASE0; - for (i = 0; i < rcount; i++) { - if ((reg[i].pci_size_low != 0)|| - (reg[i].pci_size_hi != 0)) { - - offset = PCI_REG_REG_G(reg[i].pci_phys_hi); - request.ra_len = reg[i].pci_size_low; - - switch (PCI_REG_ADDR_G(reg[i].pci_phys_hi)) { - case PCI_REG_ADDR_G(PCI_ADDR_MEM64): - request.ra_flags ^= NDI_RA_ALLOC_BOUNDED; - /* allocate memory space from the allocator */ - if (ndi_ra_alloc(ddi_get_parent(dip), - &request, &answer, &alen, - NDI_RA_TYPE_MEM, NDI_RA_PASS) - != NDI_SUCCESS) { - DEBUG0("Failed to allocate 64b mem\n"); - kmem_free(reg, length); - (void) pcicfg_config_teardown(&handle); - return (PCICFG_FAILURE); - } - DEBUG3("64 addr = [0x%x.%x] len [0x%x]\n", - PCICFG_HIADDR(answer), - PCICFG_LOADDR(answer), - alen); - /* program the low word */ - pci_config_put32(handle, - offset, PCICFG_LOADDR(answer)); - - /* program the high word with value zero */ - pci_config_put32(handle, offset + 4, - PCICFG_HIADDR(answer)); - - reg[i].pci_phys_low = PCICFG_LOADDR(answer); - reg[i].pci_phys_mid = PCICFG_HIADDR(answer); - - offset += 8; - break; - - case PCI_REG_ADDR_G(PCI_ADDR_MEM32): - request.ra_flags |= NDI_RA_ALLOC_BOUNDED; - /* allocate memory space from the allocator */ - if (ndi_ra_alloc(ddi_get_parent(dip), - &request, &answer, &alen, - NDI_RA_TYPE_MEM, NDI_RA_PASS) - != NDI_SUCCESS) { - DEBUG0("Failed to allocate 32b mem\n"); - kmem_free(reg, length); - (void) pcicfg_config_teardown(&handle); - return (PCICFG_FAILURE); - } - DEBUG3("32 addr = [0x%x.%x] len [0x%x]\n", - PCICFG_HIADDR(answer), - PCICFG_LOADDR(answer), - alen); - /* program the low word */ - pci_config_put32(handle, - offset, PCICFG_LOADDR(answer)); - - reg[i].pci_phys_low = PCICFG_LOADDR(answer); - - offset += 4; - break; - case PCI_REG_ADDR_G(PCI_ADDR_IO): - /* allocate I/O space from the allocator */ - request.ra_flags |= NDI_RA_ALLOC_BOUNDED; - if (ndi_ra_alloc(ddi_get_parent(dip), - &request, &answer, &alen, - NDI_RA_TYPE_IO, NDI_RA_PASS) - != NDI_SUCCESS) { - DEBUG0("Failed to allocate I/O\n"); - kmem_free(reg, length); - (void) pcicfg_config_teardown(&handle); - return (PCICFG_FAILURE); - } - DEBUG3("I/O addr = [0x%x.%x] len [0x%x]\n", - PCICFG_HIADDR(answer), - PCICFG_LOADDR(answer), - alen); - pci_config_put32(handle, - offset, PCICFG_LOADDR(answer)); - - reg[i].pci_phys_low = PCICFG_LOADDR(answer); - - offset += 4; - break; - default: - DEBUG0("Unknown register type\n"); - kmem_free(reg, length); - (void) pcicfg_config_teardown(&handle); - return (PCICFG_FAILURE); - } /* switch */ - - /* - * Now that memory locations are assigned, - * update the assigned address property. - */ - - if (pcicfg_update_assigned_prop(dip, - ®[i]) != PCICFG_SUCCESS) { - kmem_free(reg, length); - (void) pcicfg_config_teardown(&handle); - return (PCICFG_FAILURE); - } - } - } - - (void) pcicfg_device_on(handle); - kmem_free(reg, length); - - PCICFG_DUMP_DEVICE_CONFIG(handle); - - (void) pcicfg_config_teardown(&handle); - return (PCICFG_SUCCESS); -} - -/* - * The "dip" passed to this routine is assumed to be - * the device at the attachment point. Currently it is - * assumed to be a bridge. - */ -static int -pcicfg_allocate_chunk(dev_info_t *dip) -{ - pcicfg_phdl_t *phdl; - ndi_ra_request_t *mem_request; - ndi_ra_request_t *io_request; - uint64_t mem_answer; - uint64_t io_answer; - int count; - uint64_t alen; - - /* - * This should not find an existing entry - so - * it will create a new one. - */ - phdl = pcicfg_find_phdl(dip); - ASSERT(phdl); - - mem_request = &phdl->mem_req; - io_request = &phdl->io_req; - - /* - * From this point in the tree - walk the devices, - * The function passed in will read and "sum" up - * the memory and I/O requirements and put them in - * structure "phdl". - */ - ndi_devi_enter(ddi_get_parent(dip), &count); - ddi_walk_devs(dip, pcicfg_sum_resources, (void *)phdl); - ndi_devi_exit(ddi_get_parent(dip), count); - - if (phdl->error != PCICFG_SUCCESS) { - DEBUG0("Failure summing resources\n"); - return (phdl->error); - } - - /* - * Call into the memory allocator with the request. - * Record the addresses returned in the phdl - */ - DEBUG1("AP requires [0x%x] bytes of memory space\n", - mem_request->ra_len); - DEBUG1("AP requires [0x%x] bytes of I/O space\n", - io_request->ra_len); - - mem_request->ra_align_mask = - PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */ - io_request->ra_align_mask = - PCICFG_IOGRAN - 1; /* 4K alignment on I/O space */ - io_request->ra_boundbase = 0; - io_request->ra_boundlen = PCICFG_4GIG_LIMIT; - io_request->ra_flags |= NDI_RA_ALLOC_BOUNDED; - - mem_request->ra_len = - PCICFG_ROUND_UP(mem_request->ra_len, PCICFG_MEMGRAN); - - io_request->ra_len = - PCICFG_ROUND_UP(io_request->ra_len, PCICFG_IOGRAN); - - if (ndi_ra_alloc(ddi_get_parent(dip), - mem_request, &mem_answer, &alen, - NDI_RA_TYPE_MEM, NDI_RA_PASS) != NDI_SUCCESS) { - DEBUG0("Failed to allocate memory\n"); - return (PCICFG_FAILURE); - } - - phdl->memory_base = phdl->memory_last = mem_answer; - phdl->memory_len = alen; - - phdl->mem_hole.start = phdl->memory_base; - phdl->mem_hole.len = phdl->memory_len; - phdl->mem_hole.next = (hole_t *)NULL; - - if (ndi_ra_alloc(ddi_get_parent(dip), io_request, &io_answer, - &alen, NDI_RA_TYPE_IO, NDI_RA_PASS) != NDI_SUCCESS) { - - DEBUG0("Failed to allocate I/O space\n"); - (void) ndi_ra_free(ddi_get_parent(dip), mem_answer, - alen, NDI_RA_TYPE_MEM, NDI_RA_PASS); - phdl->memory_len = phdl->io_len = 0; - return (PCICFG_FAILURE); - } - - phdl->io_base = phdl->io_last = (uint32_t)io_answer; - phdl->io_len = (uint32_t)alen; - - phdl->io_hole.start = phdl->io_base; - phdl->io_hole.len = phdl->io_len; - phdl->io_hole.next = (hole_t *)NULL; - - DEBUG2("MEMORY BASE = [0x%x] length [0x%x]\n", - phdl->memory_base, phdl->memory_len); - DEBUG2("IO BASE = [0x%x] length [0x%x]\n", - phdl->io_base, phdl->io_len); - - return (PCICFG_SUCCESS); -} - -#ifdef DEBUG -/* - * This function is useful in debug mode, where we can measure how - * much memory was wasted/unallocated in bridge device's domain. - */ -static uint64_t -pcicfg_unused_space(hole_t *hole, uint32_t *hole_count) -{ - uint64_t len = 0; - uint32_t count = 0; - - do { - len += hole->len; - hole = hole->next; - count++; - } while (hole); - *hole_count = count; - return (len); -} -#endif - -/* - * This function frees data structures that hold the hole information - * which are allocated in pcicfg_alloc_hole(). This is not freeing - * any memory allocated through NDI calls. - */ -static void -pcicfg_free_hole(hole_t *addr_hole) -{ - hole_t *nhole, *hole = addr_hole->next; - - while (hole) { - nhole = hole->next; - kmem_free(hole, sizeof (hole_t)); - hole = nhole; - } -} - -static uint64_t -pcicfg_alloc_hole(hole_t *addr_hole, uint64_t *alast, uint32_t length) -{ - uint64_t actual_hole_start, ostart, olen; - hole_t *hole = addr_hole, *thole, *nhole; - - do { - actual_hole_start = PCICFG_ROUND_UP(hole->start, length); - if (((actual_hole_start - hole->start) + length) <= hole->len) { - DEBUG3("hole found. start %llx, len %llx, req=%x\n", - hole->start, hole->len, length); - ostart = hole->start; - olen = hole->len; - /* current hole parameters adjust */ - if ((actual_hole_start - hole->start) == 0) { - hole->start += length; - hole->len -= length; - if (hole->start > *alast) - *alast = hole->start; - } else { - hole->len = actual_hole_start - hole->start; - nhole = (hole_t *)kmem_zalloc(sizeof (hole_t), - KM_SLEEP); - nhole->start = actual_hole_start + length; - nhole->len = (ostart + olen) - nhole->start; - nhole->next = NULL; - thole = hole->next; - hole->next = nhole; - nhole->next = thole; - if (nhole->start > *alast) - *alast = nhole->start; - DEBUG2("put new hole to %llx, %llx\n", - nhole->start, nhole->len); - } - DEBUG2("adjust current hole to %llx, %llx\n", - hole->start, hole->len); - break; - } - actual_hole_start = 0; - hole = hole->next; - } while (hole); - - DEBUG1("return hole at %llx\n", actual_hole_start); - return (actual_hole_start); -} - -static void -pcicfg_get_mem(pcicfg_phdl_t *entry, - uint32_t length, uint64_t *ans) -{ - uint64_t new_mem; - - /* See if there is a hole, that can hold this request. */ - new_mem = pcicfg_alloc_hole(&entry->mem_hole, &entry->memory_last, - length); - if (new_mem) { /* if non-zero, found a hole. */ - if (ans != NULL) - *ans = new_mem; - } else - cmn_err(CE_WARN, "No %u bytes memory window for %s\n", - length, ddi_get_name(entry->dip)); -} - -static void -pcicfg_get_io(pcicfg_phdl_t *entry, - uint32_t length, uint32_t *ans) -{ - uint32_t new_io; - uint64_t io_last; - - /* - * See if there is a hole, that can hold this request. - * Pass 64 bit parameters and then truncate to 32 bit. - */ - io_last = entry->io_last; - new_io = (uint32_t)pcicfg_alloc_hole(&entry->io_hole, &io_last, length); - if (new_io) { /* if non-zero, found a hole. */ - entry->io_last = (uint32_t)io_last; - if (ans != NULL) - *ans = new_io; - } else - cmn_err(CE_WARN, "No %u bytes IO space window for %s\n", - length, ddi_get_name(entry->dip)); -} - -static int -pcicfg_sum_resources(dev_info_t *dip, void *hdl) -{ - pcicfg_phdl_t *entry = (pcicfg_phdl_t *)hdl; - pci_regspec_t *pci_rp; - int length; - int rcount; - int i; - ndi_ra_request_t *mem_request; - ndi_ra_request_t *io_request; - uint8_t header_type; - ddi_acc_handle_t handle; - - entry->error = PCICFG_SUCCESS; - - mem_request = &entry->mem_req; - io_request = &entry->io_req; - - if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) { - DEBUG0("Failed to map config space!\n"); - entry->error = PCICFG_FAILURE; - return (DDI_WALK_TERMINATE); - } - - header_type = pci_config_get8(handle, PCI_CONF_HEADER); - - /* - * If its a bridge - just record the highest bus seen - */ - if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { - - if (entry->highest_bus < pci_config_get8(handle, - PCI_BCNF_SECBUS)) { - entry->highest_bus = - pci_config_get8(handle, PCI_BCNF_SECBUS); - } - - (void) pcicfg_config_teardown(&handle); - entry->error = PCICFG_FAILURE; - return (DDI_WALK_CONTINUE); - } else { - if (ddi_getlongprop(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, "reg", (caddr_t)&pci_rp, - &length) != DDI_PROP_SUCCESS) { - /* - * If one node in (the subtree of nodes) - * does'nt have a "reg" property fail the - * allocation. - */ - entry->memory_len = 0; - entry->io_len = 0; - entry->error = PCICFG_FAILURE; - return (DDI_WALK_TERMINATE); - } - /* - * For each "reg" property with a length, add that to the - * total memory (or I/O) to allocate. - */ - rcount = length / sizeof (pci_regspec_t); - - for (i = 0; i < rcount; i++) { - - switch (PCI_REG_ADDR_G(pci_rp[i].pci_phys_hi)) { - - case PCI_REG_ADDR_G(PCI_ADDR_MEM32): - mem_request->ra_len = - pci_rp[i].pci_size_low + - PCICFG_ROUND_UP(mem_request->ra_len, - pci_rp[i].pci_size_low); - DEBUG1("ADDING 32 --->0x%x\n", - pci_rp[i].pci_size_low); - - break; - case PCI_REG_ADDR_G(PCI_ADDR_MEM64): - mem_request->ra_len = - pci_rp[i].pci_size_low + - PCICFG_ROUND_UP(mem_request->ra_len, - pci_rp[i].pci_size_low); - DEBUG1("ADDING 64 --->0x%x\n", - pci_rp[i].pci_size_low); - - break; - case PCI_REG_ADDR_G(PCI_ADDR_IO): - io_request->ra_len = - pci_rp[i].pci_size_low + - PCICFG_ROUND_UP(io_request->ra_len, - pci_rp[i].pci_size_low); - DEBUG1("ADDING I/O --->0x%x\n", - pci_rp[i].pci_size_low); - break; - default: - /* Config space register - not included */ - break; - } - } - - /* - * free the memory allocated by ddi_getlongprop - */ - kmem_free(pci_rp, length); - - /* - * continue the walk to the next sibling to sum memory - */ - - (void) pcicfg_config_teardown(&handle); - - return (DDI_WALK_CONTINUE); - } -} - -static int -pcicfg_find_resource_end(dev_info_t *dip, void *hdl) -{ - pcicfg_phdl_t *entry = (pcicfg_phdl_t *)hdl; - pci_regspec_t *pci_ap; - int length; - int rcount; - int i; - - entry->error = PCICFG_SUCCESS; - - if (dip == entry->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); - } - - 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, - 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; - } - } - - /* - * 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 -pcicfg_free_bridge_resources(dev_info_t *dip) -{ - pcicfg_range_t *ranges; - uint_t *bus; - int k; - int length; - int i; - - - if (ddi_getlongprop(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, "ranges", (caddr_t)&ranges, - &length) != DDI_PROP_SUCCESS) { - DEBUG0("Failed to read ranges property\n"); - -#if 0 - return (PCICFG_FAILURE); -#endif - } else { - for (i = 0; i < length / sizeof (pcicfg_range_t); i++) { - if (ranges[i].size_lo != 0 || - ranges[i].size_hi != 0) { - switch (ranges[i].parent_hi & PCI_REG_ADDR_M) { - case PCI_ADDR_IO: - DEBUG2("Free I/O " - "base/length = [0x%x]/[0x%x]\n", - ranges[i].child_lo, - ranges[i].size_lo); - if (ndi_ra_free(ddi_get_parent(dip), - (uint64_t)ranges[i].child_lo, - (uint64_t)ranges[i].size_lo, - NDI_RA_TYPE_IO, NDI_RA_PASS) - != NDI_SUCCESS) { - DEBUG0("Trouble freeing " - "PCI i/o space\n"); - kmem_free(ranges, length); - return (PCICFG_FAILURE); - } - break; - case PCI_ADDR_MEM32: - case PCI_ADDR_MEM64: - DEBUG3("Free Memory base/length = " - "[0x%x.%x]/[0x%x]\n", - ranges[i].child_mid, - ranges[i].child_lo, - ranges[i].size_lo) - if (ndi_ra_free(ddi_get_parent(dip), - PCICFG_LADDR(ranges[i].child_lo, - ranges[i].child_mid), - (uint64_t)ranges[i].size_lo, - NDI_RA_TYPE_MEM, NDI_RA_PASS) - != NDI_SUCCESS) { - DEBUG0("Trouble freeing " - "PCI memory space\n"); - kmem_free(ranges, length); - return (PCICFG_FAILURE); - } - break; - default: - DEBUG0("Unknown memory space\n"); - break; - } - } - } - - kmem_free(ranges, length); - } - - if (ddi_getlongprop(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, "bus-range", (caddr_t)&bus, - &k) != DDI_PROP_SUCCESS) { - DEBUG0("Failed to read bus-range property\n"); - return (PCICFG_FAILURE); - } - - DEBUG2("Need to free bus [%d] range [%d]\n", - bus[0], bus[1] - bus[0] + 1); - - if (ndi_ra_free(ddi_get_parent(dip), - (uint64_t)bus[0], (uint64_t)(bus[1] - bus[0] + 1), - NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS) != NDI_SUCCESS) { - DEBUG0("Failed to free a bus number\n"); - } - /* - * Don't forget to free up memory from ddi_getlongprop - */ - kmem_free((caddr_t)bus, k); - - return (PCICFG_SUCCESS); -} - -static int -pcicfg_free_device_resources(dev_info_t *dip) -{ - pci_regspec_t *assigned; - - int length; - int acount; - int i; - - if (ddi_getlongprop(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned, - &length) != DDI_PROP_SUCCESS) { - DEBUG0("Failed to read assigned-addresses property\n"); - return (PCICFG_FAILURE); - } - - /* - * For each "assigned-addresses" property entry with a length, - * call the memory allocation routines to return the - * resource. - */ - acount = length / sizeof (pci_regspec_t); - for (i = 0; i < acount; i++) { - /* - * Workaround for Devconf (x86) bug to skip extra entries - * beyond the PCI_CONF_BASE5 offset. But we want to free up - * any memory for expansion roms if allocated. - */ - if ((PCI_REG_REG_G(assigned[i].pci_phys_hi) > PCI_CONF_BASE5) && - (PCI_REG_REG_G(assigned[i].pci_phys_hi) != PCI_CONF_ROM)) - break; - - if (pcicfg_free_resource(dip, assigned[i])) { - DEBUG1("pcicfg_free_device_resources - Trouble freeing " - "%x\n", assigned[i].pci_phys_hi); - /* - * Don't forget to free up memory from ddi_getlongprop - */ - kmem_free((caddr_t)assigned, length); - - return (PCICFG_FAILURE); - } - } - kmem_free(assigned, length); - return (PCICFG_SUCCESS); -} - -static int -pcicfg_free_resources(dev_info_t *dip) -{ - ddi_acc_handle_t handle; - uint8_t header_type; - - if (pcicfg_config_setup(dip, &handle) != DDI_SUCCESS) { - DEBUG0("Failed to map config space!\n"); - return (PCICFG_FAILURE); - } - - header_type = pci_config_get8(handle, PCI_CONF_HEADER); - - (void) pci_config_teardown(&handle); - - /* - * A different algorithm is used for bridges and leaf devices. - */ - if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { - if (pcicfg_free_bridge_resources(dip) != PCICFG_SUCCESS) { - DEBUG0("Failed freeing up bridge resources\n"); - return (PCICFG_FAILURE); - } - } else { - if (pcicfg_free_device_resources(dip) != PCICFG_SUCCESS) { - DEBUG0("Failed freeing up device resources\n"); - return (PCICFG_FAILURE); - } - } - return (PCICFG_SUCCESS); -} - -#ifndef _DONT_USE_1275_GENERIC_NAMES -static char * -pcicfg_get_class_name(uint32_t classcode) -{ - struct pcicfg_name_entry *ptr; - - for (ptr = &pcicfg_class_lookup[0]; ptr->name != NULL; ptr++) { - if (ptr->class_code == classcode) { - return (ptr->name); - } - } - return (NULL); -} -#endif /* _DONT_USE_1275_GENERIC_NAMES */ - -static dev_info_t * -pcicfg_devi_find(dev_info_t *dip, uint_t device, uint_t function) -{ - struct pcicfg_find_ctrl ctrl; - int count; - - ctrl.device = device; - ctrl.function = function; - ctrl.dip = NULL; - - ndi_devi_enter(dip, &count); - ddi_walk_devs(ddi_get_child(dip), pcicfg_match_dev, (void *)&ctrl); - ndi_devi_exit(dip, count); - - return (ctrl.dip); -} - -static int -pcicfg_match_dev(dev_info_t *dip, void *hdl) -{ - struct pcicfg_find_ctrl *ctrl = (struct pcicfg_find_ctrl *)hdl; - pci_regspec_t *pci_rp; - int length; - int pci_dev; - int pci_func; - - if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, - (uint_t *)&length) != DDI_PROP_SUCCESS) { - ctrl->dip = NULL; - return (DDI_WALK_TERMINATE); - } - - /* get the PCI device address info */ - pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); - pci_func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); - - /* - * free the memory allocated by ddi_prop_lookup_int_array - */ - ddi_prop_free(pci_rp); - - - if ((pci_dev == ctrl->device) && (pci_func == ctrl->function)) { - /* found the match for the specified device address */ - ctrl->dip = dip; - return (DDI_WALK_TERMINATE); - } - - /* - * continue the walk to the next sibling to look for a match. - */ - return (DDI_WALK_PRUNECHILD); -} - -static int -pcicfg_update_assigned_prop(dev_info_t *dip, pci_regspec_t *newone) -{ - int alen; - pci_regspec_t *assigned; - caddr_t newreg; - uint_t status; - - DEBUG0("pcicfg_update_assigned_prop()\n"); - status = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, - "assigned-addresses", (caddr_t)&assigned, &alen); - switch (status) { - case DDI_PROP_SUCCESS: - break; - case DDI_PROP_NO_MEMORY: - DEBUG0("no memory for assigned-addresses property\n"); - return (PCICFG_FAILURE); - default: - (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, - "assigned-addresses", (int *)newone, - sizeof (*newone)/sizeof (int)); - - (void) pcicfg_dump_assigned(dip); - - return (PCICFG_SUCCESS); - } - - /* - * Allocate memory for the existing - * assigned-addresses(s) plus one and then - * build it. - */ - - newreg = kmem_zalloc(alen+sizeof (*newone), KM_SLEEP); - - bcopy(assigned, newreg, alen); - bcopy(newone, newreg + alen, sizeof (*newone)); - - /* - * Write out the new "assigned-addresses" spec - */ - (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, - "assigned-addresses", (int *)newreg, - (alen + sizeof (*newone))/sizeof (int)); - - kmem_free((caddr_t)newreg, alen+sizeof (*newone)); - - /* - * Don't forget to free up memory from ddi_getlongprop - */ - kmem_free((caddr_t)assigned, alen); - - (void) pcicfg_dump_assigned(dip); - - return (PCICFG_SUCCESS); -} -static int -pcicfg_update_ranges_prop(dev_info_t *dip, pcicfg_range_t *addition) -{ - int rlen; - pcicfg_range_t *ranges; - caddr_t newreg; - uint_t status; - - status = ddi_getlongprop(DDI_DEV_T_ANY, - dip, DDI_PROP_DONTPASS, "ranges", (caddr_t)&ranges, &rlen); - - - switch (status) { - case DDI_PROP_SUCCESS: - break; - case DDI_PROP_NO_MEMORY: - DEBUG0("ranges present, but unable to get memory\n"); - return (PCICFG_FAILURE); - default: - DEBUG0("no ranges property - creating one\n"); - if (ndi_prop_update_int_array(DDI_DEV_T_NONE, - dip, "ranges", (int *)addition, - sizeof (pcicfg_range_t)/sizeof (int)) - != DDI_SUCCESS) { - DEBUG0("Did'nt create ranges property\n"); - return (PCICFG_FAILURE); - } - return (PCICFG_SUCCESS); - } - - /* - * Allocate memory for the existing reg(s) plus one and then - * build it. - */ - newreg = kmem_zalloc(rlen+sizeof (pcicfg_range_t), KM_SLEEP); - - bcopy(ranges, newreg, rlen); - bcopy(addition, newreg + rlen, sizeof (pcicfg_range_t)); - - /* - * Write out the new "ranges" property - */ - (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, - dip, "ranges", (int *)newreg, - (rlen + sizeof (pcicfg_range_t))/sizeof (int)); - - kmem_free((caddr_t)newreg, rlen+sizeof (pcicfg_range_t)); - - kmem_free((caddr_t)ranges, rlen); - - return (PCICFG_SUCCESS); -} - -static int -pcicfg_update_reg_prop(dev_info_t *dip, uint32_t regvalue, uint_t reg_offset) -{ - int rlen; - pci_regspec_t *reg; - caddr_t newreg; - uint32_t hiword; - pci_regspec_t addition; - uint32_t size; - uint_t status; - - status = ddi_getlongprop(DDI_DEV_T_ANY, - dip, DDI_PROP_DONTPASS, "reg", (caddr_t)®, &rlen); - - switch (status) { - case DDI_PROP_SUCCESS: - break; - case DDI_PROP_NO_MEMORY: - DEBUG0("reg present, but unable to get memory\n"); - return (PCICFG_FAILURE); - default: - DEBUG0("no reg property\n"); - return (PCICFG_FAILURE); - } - - /* - * Allocate memory for the existing reg(s) plus one and then - * build it. - */ - newreg = kmem_zalloc(rlen+sizeof (pci_regspec_t), KM_SLEEP); - - /* - * Build the regspec, then add it to the existing one(s) - */ - - hiword = PCICFG_MAKE_REG_HIGH(PCI_REG_BUS_G(reg->pci_phys_hi), - PCI_REG_DEV_G(reg->pci_phys_hi), - PCI_REG_FUNC_G(reg->pci_phys_hi), reg_offset); - - if (reg_offset == PCI_CONF_ROM) { - size = (~(PCI_BASE_ROM_ADDR_M & regvalue))+1; - hiword |= PCI_ADDR_MEM32; - } else { - size = (~(PCI_BASE_M_ADDR_M & regvalue))+1; - - if ((PCI_BASE_SPACE_M & regvalue) == PCI_BASE_SPACE_MEM) { - if ((PCI_BASE_TYPE_M & regvalue) == PCI_BASE_TYPE_MEM) { - hiword |= PCI_ADDR_MEM32; - } else if ((PCI_BASE_TYPE_M & regvalue) - == PCI_BASE_TYPE_ALL) { - hiword |= PCI_ADDR_MEM64; - } - } else { - hiword |= PCI_ADDR_IO; - } - } - - addition.pci_phys_hi = hiword; - addition.pci_phys_mid = 0; - addition.pci_phys_low = 0; - addition.pci_size_hi = 0; - addition.pci_size_low = size; - - bcopy(reg, newreg, rlen); - bcopy(&addition, newreg + rlen, sizeof (pci_regspec_t)); - - /* - * Write out the new "reg" property - */ - (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, - dip, "reg", (int *)newreg, - (rlen + sizeof (pci_regspec_t))/sizeof (int)); - - kmem_free((caddr_t)newreg, rlen+sizeof (pci_regspec_t)); - kmem_free((caddr_t)reg, rlen); - - return (PCICFG_SUCCESS); -} - -static void -pcicfg_device_on(ddi_acc_handle_t config_handle) -{ - /* - * Enable memory, IO, and bus mastership - * XXX should we enable parity, SERR#, - * fast back-to-back, and addr. stepping? - */ - pci_config_put16(config_handle, PCI_CONF_COMM, - pci_config_get16(config_handle, PCI_CONF_COMM) | 0x7); -} - -static void -pcicfg_device_off(ddi_acc_handle_t config_handle) -{ - /* - * Disable I/O and memory traffic through the bridge - */ - pci_config_put16(config_handle, PCI_CONF_COMM, 0x0); -} - -/* - * Setup the basic 1275 properties based on information found in the config - * header of the PCI device - */ -static int -pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle) -{ - int ret; - uint16_t val; - uint32_t wordval; - uint8_t byteval; - - /* These two exists only for non-bridges */ - if ((pci_config_get8(config_handle, - PCI_CONF_HEADER) & PCI_HEADER_TYPE_M) == PCI_HEADER_ZERO) { - byteval = pci_config_get8(config_handle, PCI_CONF_MIN_G); - if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "min-grant", byteval)) != DDI_SUCCESS) { - return (ret); - } - - byteval = pci_config_get8(config_handle, PCI_CONF_MAX_L); - if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "max-latency", byteval)) != DDI_SUCCESS) { - return (ret); - } - } - - /* - * These should always exist and have the value of the - * corresponding register value - */ - val = pci_config_get16(config_handle, PCI_CONF_VENID); - - if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "vendor-id", val)) != DDI_SUCCESS) { - return (ret); - } - val = pci_config_get16(config_handle, PCI_CONF_DEVID); - if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "device-id", val)) != DDI_SUCCESS) { - return (ret); - } - byteval = pci_config_get8(config_handle, PCI_CONF_REVID); - if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "revision-id", byteval)) != DDI_SUCCESS) { - return (ret); - } - - wordval = (pci_config_get16(config_handle, PCI_CONF_SUBCLASS)<< 8) | - (pci_config_get8(config_handle, PCI_CONF_PROGCLASS)); - - if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "class-code", wordval)) != DDI_SUCCESS) { - return (ret); - } - val = (pci_config_get16(config_handle, - PCI_CONF_STAT) & PCI_STAT_DEVSELT); - if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "devsel-speed", val)) != DDI_SUCCESS) { - return (ret); - } - - /* - * The next three are bits set in the status register. The property is - * present (but with no value other than its own existence) if the bit - * is set, non-existent otherwise - */ - if (pci_config_get16(config_handle, PCI_CONF_STAT) & PCI_STAT_FBBC) { - if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "fast-back-to-back", 0)) != DDI_SUCCESS) { - return (ret); - } - } - if (pci_config_get16(config_handle, PCI_CONF_STAT) & PCI_STAT_66MHZ) { - if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "66mhz-capable", 0)) != DDI_SUCCESS) { - return (ret); - } - } - if (pci_config_get16(config_handle, PCI_CONF_STAT) & PCI_STAT_UDF) { - if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "udf-supported", 0)) != DDI_SUCCESS) { - return (ret); - } - } - - /* - * These next three are optional and are not present - * if the corresponding register is zero. If the value - * is non-zero then the property exists with the value - * of the register. - */ - if ((val = pci_config_get16(config_handle, - PCI_CONF_SUBVENID)) != 0) { - if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "subsystem-vendor-id", val)) != DDI_SUCCESS) { - return (ret); - } - } - if ((val = pci_config_get16(config_handle, - PCI_CONF_SUBSYSID)) != 0) { - if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "subsystem-id", val)) != DDI_SUCCESS) { - return (ret); - } - } - if ((val = pci_config_get16(config_handle, - PCI_CONF_CACHE_LINESZ)) != 0) { - if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "cache-line-size", val)) != DDI_SUCCESS) { - return (ret); - } - } - - /* - * If the Interrupt Pin register is non-zero then the - * interrupts property exists - */ - if ((byteval = pci_config_get8(config_handle, PCI_CONF_IPIN)) != 0) { - /* - * If interrupt pin is non-zero, - * record the interrupt line used - */ - if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "interrupts", byteval)) != DDI_SUCCESS) { - return (ret); - } - } - return (PCICFG_SUCCESS); -} -static int -pcicfg_set_busnode_props(dev_info_t *dip) -{ - int ret; - - if ((ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip, - "device_type", "pci")) != DDI_SUCCESS) { - return (ret); - } - if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "#address-cells", 3)) != DDI_SUCCESS) { - return (ret); - } - if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip, - "#size-cells", 2)) != DDI_SUCCESS) { - return (ret); - } - return (PCICFG_SUCCESS); -} - -static int -pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle) -{ - - int ret; -#ifndef _DONT_USE_1275_GENERIC_NAMES - uint32_t wordval; -#endif - char *name; - char buffer[64]; - uint32_t classcode; - char *compat[8]; - int i; - int n; - uint16_t sub_vid, sub_sid; - /* - * NOTE: These are for both a child and PCI-PCI bridge node - */ -#ifndef _DONT_USE_1275_GENERIC_NAMES - wordval = (pci_config_get16(config_handle, PCI_CONF_SUBCLASS)<< 8) | - (pci_config_get8(config_handle, PCI_CONF_PROGCLASS)); -#endif - - /* - * "name" property rule - * -------------------- - * - * - * | \svid | - * | \ | - * | \ | - * | ssid \ | =0 | != 0 | - * |------------|-----------------------|-----------------------| - * | | | | - * | =0 | vid,did | svid,ssid | - * | | | | - * |------------|-----------------------|-----------------------| - * | | | | - * | !=0 | svid,ssid | svid,ssid | - * | | | | - * |------------|-----------------------|-----------------------| - * - * where: - * vid = vendor id - * did = device id - * svid = subsystem vendor id - * ssid = subsystem id - */ - - sub_vid = pci_config_get16(config_handle, PCI_CONF_SUBVENID), - sub_sid = pci_config_get16(config_handle, PCI_CONF_SUBSYSID); - if ((pci_config_get16(config_handle, PCI_CONF_SUBSYSID) != 0) || - (pci_config_get16(config_handle, PCI_CONF_SUBVENID) != 0)) { - (void) sprintf(buffer, "pci%x,%x", sub_vid, sub_sid); - } else { - (void) sprintf(buffer, "pci%x,%x", - pci_config_get16(config_handle, PCI_CONF_VENID), - pci_config_get16(config_handle, PCI_CONF_DEVID)); - } - - /* - * In some environments, trying to use "generic" 1275 names is - * not the convention. In those cases use the name as created - * above. In all the rest of the cases, check to see if there - * is a generic name first. - */ -#ifdef _DONT_USE_1275_GENERIC_NAMES - name = buffer; -#else - if ((name = pcicfg_get_class_name(wordval>>8)) == NULL) { - /* - * Set name to the above fabricated name - */ - name = buffer; - } -#endif - - /* - * The node name field needs to be filled in with the name - */ - if (ndi_devi_set_nodename(dip, name, 0) != NDI_SUCCESS) { - DEBUG0("Failed to set nodename for node\n"); - return (PCICFG_FAILURE); - } - - /* - * Create the compatible property as an array of pointers - * to strings. Start with the buffer created above. - */ - n = 0; - compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); - (void) strcpy(compat[n++], buffer); - - /* - * Add in the VendorID/DeviceID compatible name. - */ - (void) sprintf(buffer, "pci%x,%x", - pci_config_get16(config_handle, PCI_CONF_VENID), - pci_config_get16(config_handle, PCI_CONF_DEVID)); - - compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); - (void) strcpy(compat[n++], buffer); - - classcode = (pci_config_get16(config_handle, PCI_CONF_SUBCLASS)<< 8) | - (pci_config_get8(config_handle, PCI_CONF_PROGCLASS)); - - /* - * Add in the Classcode - */ - (void) sprintf(buffer, "pciclass,%06x", classcode); - - compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP); - (void) strcpy(compat[n++], buffer); - - if ((ret = ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, - "compatible", (char **)compat, n)) != DDI_SUCCESS) { - return (ret); - } - - for (i = 0; i < n; i++) { - kmem_free(compat[i], strlen(compat[i]) + 1); - } - - DEBUG1("pcicfg_set_childnode_props - creating name=%s\n", name); - if ((ret = ndi_prop_update_string(DDI_DEV_T_NONE, dip, - "name", name)) != DDI_SUCCESS) { - - DEBUG0("pcicfg_set_childnode_props - Unable to create name " - "property\n"); - - return (ret); - } - - return (PCICFG_SUCCESS); -} - -/* - * Program the bus numbers into the bridge - */ - -static void -pcicfg_set_bus_numbers(ddi_acc_handle_t config_handle, -uint_t primary, uint_t secondary, uint_t subordinate) -{ - /* - * Primary bus# - */ - pci_config_put8(config_handle, PCI_BCNF_PRIBUS, primary); - - /* - * Secondary bus# - */ - pci_config_put8(config_handle, PCI_BCNF_SECBUS, secondary); - - /* - * Subordinate bus# - */ - pci_config_put8(config_handle, PCI_BCNF_SUBBUS, subordinate); -} - -/* - * Put bridge registers into initial state - */ -static void -pcicfg_setup_bridge(pcicfg_phdl_t *entry, - ddi_acc_handle_t handle) -{ - /* - * The highest bus seen during probing is the max-subordinate bus - */ - pci_config_put8(handle, PCI_BCNF_SUBBUS, entry->highest_bus); - - /* - * Reset the secondary bus - */ - pci_config_put16(handle, PCI_BCNF_BCNTRL, - pci_config_get16(handle, PCI_BCNF_BCNTRL) | 0x40); - - drv_usecwait(100); - - pci_config_put16(handle, PCI_BCNF_BCNTRL, - pci_config_get16(handle, PCI_BCNF_BCNTRL) & ~0x40); - - /* - * Program the memory base register with the - * start of the memory range - */ - pci_config_put16(handle, PCI_BCNF_MEM_BASE, - PCICFG_HIWORD(PCICFG_LOADDR(entry->memory_last))); - - /* - * Program the I/O base register with the start of the I/O range - */ - pci_config_put8(handle, PCI_BCNF_IO_BASE_LOW, - PCICFG_HIBYTE(PCICFG_LOWORD(PCICFG_LOADDR(entry->io_last)))); - pci_config_put16(handle, PCI_BCNF_IO_BASE_HI, - PCICFG_HIWORD(PCICFG_LOADDR(entry->io_last))); - - /* - * Clear status bits - */ - pci_config_put16(handle, PCI_BCNF_SEC_STATUS, 0xffff); - - /* - * Turn off prefetchable range - */ - pci_config_put32(handle, PCI_BCNF_PF_BASE_LOW, 0x0000ffff); - pci_config_put32(handle, PCI_BCNF_PF_BASE_HIGH, 0xffffffff); - pci_config_put32(handle, PCI_BCNF_PF_LIMIT_HIGH, 0x0); - - /* - * Needs to be set to this value - */ - pci_config_put8(handle, PCI_CONF_ILINE, 0xf); - - /* - * After a Reset, we need to wait 2^25 clock cycles before the - * first Configuration access. The worst case is 33MHz, which - * is a 1 second wait. - */ - drv_usecwait(1000000); - -} - -static void -pcicfg_update_bridge(pcicfg_phdl_t *entry, - ddi_acc_handle_t handle) -{ - uint_t length; - - /* - * Program the memory limit register with the end of the memory range - */ - - DEBUG1("DOWN ROUNDED ===>[0x%x]\n", - PCICFG_ROUND_DOWN(entry->memory_last, - PCICFG_MEMGRAN)); - - pci_config_put16(handle, PCI_BCNF_MEM_LIMIT, - PCICFG_HIWORD(PCICFG_LOADDR( - PCICFG_ROUND_DOWN(entry->memory_last, - PCICFG_MEMGRAN)))); - /* - * Since this is a bridge, the rest of this range will - * be responded to by the bridge. We have to round up - * so no other device claims it. - */ - if ((length = (PCICFG_ROUND_UP(entry->memory_last, - PCICFG_MEMGRAN) - entry->memory_last)) > 0) { - (void) pcicfg_get_mem(entry, length, NULL); - DEBUG1("Added [0x%x]at the top of " - "the bridge (mem)\n", length); - } - - /* - * Program the I/O limit register with the end of the I/O range - */ - pci_config_put8(handle, PCI_BCNF_IO_LIMIT_LOW, - PCICFG_HIBYTE(PCICFG_LOWORD( - PCICFG_LOADDR(PCICFG_ROUND_DOWN(entry->io_last, - PCICFG_IOGRAN))))); - - pci_config_put16(handle, PCI_BCNF_IO_LIMIT_HI, - PCICFG_HIWORD(PCICFG_LOADDR(PCICFG_ROUND_DOWN(entry->io_last, - PCICFG_IOGRAN)))); - - /* - * Same as above for I/O space. Since this is a - * bridge, the rest of this range will be responded - * to by the bridge. We have to round up so no - * other device claims it. - */ - if ((length = (PCICFG_ROUND_UP(entry->io_last, - PCICFG_IOGRAN) - entry->io_last)) > 0) { - (void) pcicfg_get_io(entry, length, NULL); - DEBUG1("Added [0x%x]at the top of " - "the bridge (I/O)\n", length); - } -} - -static int -pcicfg_probe_children(dev_info_t *parent, uint_t bus, - uint_t device, uint_t func) -{ - dev_info_t *new_child; - ddi_acc_handle_t config_handle; - uint8_t header_type; - - int i, j; - ndi_ra_request_t req; - uint64_t next_bus; - uint64_t blen; - uint32_t request; - uint_t new_bus; - int ret; - int circ; - - /* - * This node will be put immediately below - * "parent". Allocate a blank device node. It will either - * be filled in or freed up based on further probing. - */ - - ndi_devi_enter(parent, &circ); - /* - * Note: in usr/src/uts/common/io/hotplug/pcicfg/pcicfg.c - * ndi_devi_alloc() is called as ndi_devi_alloc_sleep() - */ - if (ndi_devi_alloc(parent, DEVI_PSEUDO_NEXNAME, - (pnode_t)DEVI_SID_NODEID, &new_child) - != NDI_SUCCESS) { - DEBUG0("pcicfg_probe_children(): Failed to alloc child node\n"); - ndi_devi_exit(parent, circ); - return (PCICFG_FAILURE); - } - - if (pcicfg_add_config_reg(new_child, bus, - device, func) != DDI_SUCCESS) { - DEBUG0("pcicfg_probe_children():" - "Failed to add candidate REG\n"); - goto failedconfig; - } - - if ((ret = pcicfg_config_setup(new_child, &config_handle)) - != PCICFG_SUCCESS) { - if (ret == PCICFG_NODEVICE) { - (void) ndi_devi_free(new_child); - ndi_devi_exit(parent, circ); - return (ret); - } - DEBUG0("pcicfg_probe_children():" - "Failed to setup config space\n"); - goto failedconfig; - } - - /* - * As soon as we have access to config space, - * turn off device. It will get turned on - * later (after memory is assigned). - */ - (void) pcicfg_device_off(config_handle); - - - /* - * Set 1275 properties common to all devices - */ - if (pcicfg_set_standard_props(new_child, - config_handle) != PCICFG_SUCCESS) { - DEBUG0("Failed to set standard properties\n"); - goto failedchild; - } - - /* - * Child node properties NOTE: Both for PCI-PCI bridge and child node - */ - if (pcicfg_set_childnode_props(new_child, - config_handle) != PCICFG_SUCCESS) { - goto failedchild; - } - - header_type = pci_config_get8(config_handle, PCI_CONF_HEADER); - - /* - * If this is not a multi-function card only probe function zero. - */ - if (!(header_type & PCI_HEADER_MULTI) && (func != 0)) { - - (void) pcicfg_config_teardown(&config_handle); - (void) ndi_devi_free(new_child); - ndi_devi_exit(parent, circ); - return (PCICFG_NODEVICE); - } - - DEBUG1("---Vendor ID = [0x%x]\n", - pci_config_get16(config_handle, PCI_CONF_VENID)); - DEBUG1("---Device ID = [0x%x]\n", - pci_config_get16(config_handle, PCI_CONF_DEVID)); - - if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { - - DEBUG3("--Bridge found bus [0x%x] device" - "[0x%x] func [0x%x]\n", bus, device, func); - - /* - * Get next bus in sequence and program device. - * XXX There might have to be slot specific - * ranges taken care of here. - */ - bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); - req.ra_len = 1; - if (ndi_ra_alloc(ddi_get_parent(new_child), &req, - &next_bus, &blen, NDI_RA_TYPE_PCI_BUSNUM, - NDI_RA_PASS) != NDI_SUCCESS) { - DEBUG0("Failed to get a bus number\n"); - goto failedchild; - } - new_bus = next_bus; - - DEBUG1("NEW bus found ->[%d]\n", new_bus); - - (void) pcicfg_set_bus_numbers(config_handle, - bus, new_bus, new_bus); - /* - * Set bus properties - */ - if (pcicfg_set_busnode_props(new_child) != PCICFG_SUCCESS) { - DEBUG0("Failed to set busnode props\n"); - goto failedchild; - } - - /* - * Probe all children devices - */ - for (i = 0; i < PCICFG_MAX_DEVICE; i++) { - for (j = 0; j < PCICFG_MAX_FUNCTION; j++) { - if (pcicfg_probe_children(new_child, - new_bus, i, j) == - PCICFG_FAILURE) { - DEBUG3("Failed to configure bus " - "[0x%x] device [0x%x] func [0x%x]\n", - new_bus, i, j); - goto failedchild; - } - } - } - - } else { - - DEBUG3("--Leaf device found bus [0x%x] device" - "[0x%x] func [0x%x]\n", - bus, device, func); - - i = PCI_CONF_BASE0; - - while (i <= PCI_CONF_BASE5) { - - pci_config_put32(config_handle, i, 0xffffffff); - - request = pci_config_get32(config_handle, i); - /* - * If its a zero length, don't do - * any programming. - */ - if (request != 0) { - /* - * Add to the "reg" property - */ - if (pcicfg_update_reg_prop(new_child, - request, i) != PCICFG_SUCCESS) { - goto failedchild; - } - } else { - DEBUG1("BASE register [0x%x] asks for " - "[0x0]=[0x0](32)\n", i); - i += 4; - continue; - } - - /* - * Increment by eight if it is 64 bit address space - */ - if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) { - DEBUG3("BASE register [0x%x] asks for " - "[0x%x]=[0x%x] (64)\n", - i, request, - (~(PCI_BASE_M_ADDR_M & request))+1) - i += 8; - } else { - DEBUG3("BASE register [0x%x] asks for " - "[0x%x]=[0x%x](32)\n", - i, request, - (~(PCI_BASE_M_ADDR_M & request))+1) - i += 4; - } - } - - /* - * Get the ROM size and create register for it - */ - pci_config_put32(config_handle, PCI_CONF_ROM, 0xfffffffe); - - request = pci_config_get32(config_handle, PCI_CONF_ROM); - /* - * If its a zero length, don't do - * any programming. - */ - - if (request != 0) { - DEBUG3("BASE register [0x%x] asks for [0x%x]=[0x%x]\n", - PCI_CONF_ROM, request, - (~(PCI_BASE_ROM_ADDR_M & request))+1); - /* - * Add to the "reg" property - */ - if (pcicfg_update_reg_prop(new_child, - request, PCI_CONF_ROM) != PCICFG_SUCCESS) { - goto failedchild; - } - } - } - - /* - * Attach the child to its parent - */ - (void) ndi_devi_bind_driver(new_child, 0); - - (void) pcicfg_config_teardown(&config_handle); - ndi_devi_exit(parent, circ); - - return (PCICFG_SUCCESS); - -failedchild: - - (void) pcicfg_config_teardown(&config_handle); - -failedconfig: - - (void) ndi_devi_free(new_child); - ndi_devi_exit(parent, circ); - - return (PCICFG_FAILURE); -} - -static int -pcicfg_fcode_probe(dev_info_t *parent, uint_t bus, - uint_t device, uint_t func, uint_t *highest_bus) -{ - dev_info_t *new_child; - int8_t header_type; - int ret; - ddi_acc_handle_t h; - int error = 0; - int rval; - extern int pcicfg_dont_interpret; -#ifdef PCICFG_INTERPRET_FCODE - struct pci_ops_bus_args po; - fco_handle_t c; - char unit_address[64]; - int fcode_size = 0; - uchar_t *fcode_addr; - uint64_t mem_answer, mem_alen; - pci_regspec_t p; - int32_t request; - ndi_ra_request_t req; - int16_t vendor_id, device_id; -#endif - - /* - * This node will be put immediately below - * "parent". Allocate a blank device node. It will either - * be filled in or freed up based on further probing. - */ - - if (ndi_devi_alloc(parent, DEVI_PSEUDO_NEXNAME, - (pnode_t)DEVI_SID_NODEID, &new_child) - != NDI_SUCCESS) { - DEBUG0("pcicfg_fcode_probe(): Failed to alloc child node\n"); - return (PCICFG_FAILURE); - } - - /* - * Create a dummy reg property. This will be replaced with - * a real reg property when fcode completes or if we need to - * produce one by hand. - */ - if (pcicfg_add_config_reg(new_child, bus, - device, func) != DDI_SUCCESS) { - - (void) ndi_devi_free(new_child); - return (PCICFG_FAILURE); - } -#ifdef EFCODE21554 - if ((ret = pcicfg_config_setup(new_child, &h)) - != PCICFG_SUCCESS) { - DEBUG0("pcicfg_fcode_probe():" - "Failed to setup config space\n"); - (void) ndi_devi_free(new_child); - return (ret); - } - - DEBUG0("fcode_probe: conf space mapped.\n"); -#else - p.pci_phys_hi = PCICFG_MAKE_REG_HIGH(bus, device, func, 0); - p.pci_phys_mid = p.pci_phys_low = 0; - p.pci_size_hi = p.pci_size_low = 0; - - /* - * Map in configuration space (temporarily) - */ - acc.devacc_attr_version = DDI_DEVICE_ATTR_V0; - acc.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; - acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC; - - if (pcicfg_map_phys(new_child, &p, &virt, &acc, &h)) { - DEBUG0("pcicfg_fcode_probe():" - "Failed to setup config space\n"); - - (void) ndi_devi_free(new_child); - - return (PCICFG_FAILURE); - } - - /* - * First use ddi_peek16 so that if there is not a device there, - * a bus error will not cause a panic. - */ - v = virt + PCI_CONF_VENID; - if (ddi_peek16(new_child, (int16_t *)v, &vendor_id)) { - DEBUG0("Can not read Vendor ID"); - pcicfg_unmap_phys(&h, &p); - (void) ndi_devi_free(new_child); - return (PCICFG_NODEVICE); - } -#endif - /* - * As soon as we have access to config space, - * turn off device. It will get turned on - * later (after memory is assigned). - */ - (void) pcicfg_device_off(h); - - /* - * Set 1275 properties common to all devices - */ - if (pcicfg_set_standard_props(new_child, - h) != PCICFG_SUCCESS) { - DEBUG0("Failed to set standard properties\n"); - goto failed; - } - - /* - * Child node properties NOTE: Both for PCI-PCI bridge and child node - */ - if (pcicfg_set_childnode_props(new_child, - h) != PCICFG_SUCCESS) { - goto failed; - } - - header_type = pci_config_get8(h, PCI_CONF_HEADER); - - /* - * If this is not a multi-function card only probe function zero. - */ - if (!(header_type & PCI_HEADER_MULTI) && (func > 0)) { - -#ifdef EFCODE21554 - pcicfg_config_teardown(&h); -#else - pcicfg_unmap_phys(&h, &p); -#endif - (void) ndi_devi_free(new_child); - return (PCICFG_NODEVICE); - } - - if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { - - /* - * XXX - Transparent bridges are handled differently - * than other devices with regards to fcode. Since - * no transparent bridge currently ships with fcode, - * there is no reason to try to extract it from its rom - * or call the fcode interpreter to try to load a drop-in. - * If fcode is developed to handle transparent bridges, - * this code will have to change. - */ - - DEBUG3("--Bridge found bus [0x%x] device" - "[0x%x] func [0x%x]\n", bus, device, func); - - rval = pcicfg_probe_bridge(new_child, h, bus, highest_bus); - -#ifdef EFCODE21554 - pcicfg_config_teardown(&h); -#else - pcicfg_unmap_phys(&h, &p); -#endif - if (rval != PCICFG_SUCCESS) - (void) ndi_devi_free(new_child); - - return (rval); - } else { - - DEBUG3("--Leaf device found bus [0x%x] device" - "[0x%x] func [0x%x]\n", - bus, device, func); - - /* - * link in tree, but don't bind driver - * We don't have compatible property yet - */ - (void) i_ndi_config_node(new_child, DS_LINKED, 0); - - if (pci_config_get8(h, PCI_CONF_IPIN)) { - pci_config_put8(h, PCI_CONF_ILINE, 0xf); - } - -#ifdef PCICFG_INTERPRET_FCODE - /* - * Some platforms (x86) don't run fcode, so don't interpret - * fcode that might be in the ROM. - */ - if (pcicfg_dont_interpret == 0) { - - /* This platform supports fcode */ - - vendor_id = pci_config_get16(h, PCI_CONF_VENID); - device_id = pci_config_get16(h, PCI_CONF_DEVID); - - /* - * Get the ROM size and create register for it - */ - pci_config_put32(h, PCI_CONF_ROM, 0xfffffffe); - - request = pci_config_get32(h, PCI_CONF_ROM); - - /* - * If its a zero length, don't do - * any programming. - */ - - if (request != 0) { - /* - * Add resource to assigned-addresses. - */ - if (pcicfg_fcode_assign_bars(h, new_child, - bus, device, func, request, &p) - != PCICFG_SUCCESS) { - DEBUG0("Failed to assign addresses to " - "implemented BARs"); - goto failed; - } - - /* Turn device on */ - (void) pcicfg_device_on(h); - - /* - * Attempt to load fcode. - */ - (void) pcicfg_load_fcode(new_child, bus, device, - func, vendor_id, device_id, &fcode_addr, - &fcode_size, PCICFG_LOADDR(mem_answer), - (~(PCI_BASE_ROM_ADDR_M & request)) + 1); - - /* Turn device off */ - (void) pcicfg_device_off(h); - - /* - * Free the ROM resources. - */ - (void) pcicfg_free_resource(new_child, p); - - DEBUG2("configure: fcode addr %lx size %x\n", - fcode_addr, fcode_size); - - /* - * Create the fcode-rom-offset property. The - * buffer containing the fcode always starts - * with 0xF1, so the fcode offset is zero. - */ - if (ndi_prop_update_int(DDI_DEV_T_NONE, - new_child, "fcode-rom-offset", 0) - != DDI_SUCCESS) { - DEBUG0("Failed to create " - "fcode-rom-offset property\n"); - (void) ndi_devi_free(new_child); - return (PCICFG_FAILURE); - } - } else { - DEBUG0("There is no Expansion ROM\n"); - fcode_addr = NULL; - fcode_size = 0; - } - - /* - * Fill in the bus specific arguments. For - * PCI, it is the config address. - */ - po.config_address = - PCICFG_MAKE_REG_HIGH(bus, device, func, 0); - - DEBUG1("config_address=%x\n", po.config_address); - - /* - * Build unit address. - */ - (void) sprintf(unit_address, "%x,%x", device, func); - - DEBUG3("pci_fc_ops_alloc_handle ap=%lx " - "new device=%lx unit address=%s\n", - parent, new_child, unit_address); - - c = pci_fc_ops_alloc_handle(parent, new_child, - fcode_addr, fcode_size, unit_address, &po); - - DEBUG0("calling fcode_interpreter()\n"); - - DEBUG3("Before int DIP=%lx binding name %s major %d\n", - new_child, ddi_binding_name(new_child), - ddi_name_to_major(ddi_binding_name(new_child))); - - error = fcode_interpreter(parent, &pci_fc_ops, c); - - DEBUG1("returned from fcode_interpreter() - " - "returned %x\n", error); - - pci_fc_ops_free_handle(c); - - DEBUG1("fcode size = %x\n", fcode_size); - /* - * We don't need the fcode anymore. While allocating - * we had rounded up to a page size. - */ - if (fcode_size) { - kmem_free(fcode_addr, ptob(btopr(fcode_size))); - } - } else { - /* This platform does not support fcode */ - - DEBUG0("NOT calling fcode_interpreter()\n"); - } - -#endif /* PCICFG_INTERPRET_FCODE */ - - if ((error == 0) && (pcicfg_dont_interpret == 0)) { - /* - * The interpreter completed successfully. - * We need to redo the resources based on the new reg - * property. - */ - DEBUG3("DIP=%lx binding name %s major %d\n", new_child, - ddi_binding_name(new_child), - ddi_name_to_major(ddi_binding_name(new_child))); - - /* - * Readjust resources specified by reg property. - */ - if (pcicfg_alloc_new_resources(new_child) == - PCICFG_FAILURE) { -#ifdef EFCODE21554 - pcicfg_config_teardown(&h); -#else - pcicfg_unmap_phys(&h, &p); -#endif - (void) ndi_devi_free(new_child); - return (PCICFG_FAILURE); - } - -#ifdef EFCODE21554 - pcicfg_config_teardown(&h); -#else - pcicfg_unmap_phys(&h, &p); -#endif - /* no fcode, bind driver now */ - (void) ndi_devi_bind_driver(new_child, 0); - - return (PCICFG_SUCCESS); - } else if ((error != FC_NO_FCODE) && - (pcicfg_dont_interpret == 0)) { - /* - * The interpreter located fcode, but had an error in - * processing. Cleanup and fail the operation. - */ - DEBUG0("Interpreter detected fcode failure\n"); - (void) pcicfg_free_resources(new_child); - goto failed; - } else { - /* - * Either the interpreter failed with FC_NO_FCODE or we - * chose not to run the interpreter - * (pcicfg_dont_interpret). - * - * If the interpreter failed because there was no - * dropin, then we need to probe the device ourself. - */ - - /* - * Free any resources that may have been assigned - * during fcode loading/execution since we need - * to start over. - */ - (void) pcicfg_free_resources(new_child); - -#ifdef EFCODE21554 - pcicfg_config_teardown(&h); -#else - pcicfg_unmap_phys(&h, &p); -#endif - (void) ndi_devi_free(new_child); - - DEBUG0("No Drop-in Probe device ourself\n"); - - ret = pcicfg_probe_children(parent, bus, device, func); - - if (ret != PCICFG_SUCCESS) { - DEBUG0("Could not self probe child\n"); - return (ret); - } - - /* - * We successfully self probed the device. - */ - if ((new_child = pcicfg_devi_find( - parent, device, func)) == NULL) { - DEBUG0("Did'nt find device node " - "just created\n"); - return (PCICFG_FAILURE); - } - if (pcicfg_program_ap(new_child) - == PCICFG_FAILURE) { - DEBUG0("Failed to program devices\n"); - (void) ndi_devi_free(new_child); - return (PCICFG_FAILURE); - } -#ifdef EFCODE21554 - /* - * Till now, we have detected a non transparent bridge - * (ntbridge) as a part of the generic probe code and - * configured only one configuration - * header which is the side facing the host bus. - * Now, configure the other side and create children. - * - * To make the process simpler, lets load the device - * driver for the non transparent bridge as this is a - * Solaris bundled driver, and use its configuration map - * services rather than programming it here. - * If the driver is not bundled into Solaris, it must be - * first loaded and configured before performing any - * hotplug operations. - * - * This not only makes the code simpler but also more - * generic. - * - * So here we go. - */ - if (pcicfg_is_ntbridge(new_child) != DDI_FAILURE) { - - DEBUG0("Found nontransparent bridge.\n"); - - ret = pcicfg_configure_ntbridge(new_child, - bus, device); - } - if (ret != PCICFG_SUCCESS) { - /* - * Bridge configure failed. Free up the self - * probed entry. The bus resource allocation - * maps need to be cleaned up to prevent - * warnings on retries of the failed configure. - */ - (void) pcicfg_ntbridge_unconfigure(new_child); - (void) pcicfg_teardown_device(new_child); - } -#endif - - return (ret); - } - } -failed: -#ifdef EFCODE21554 - pcicfg_config_teardown(&h); -#else - pcicfg_unmap_phys(&h, &p); -#endif - (void) ndi_devi_free(new_child); - - return (PCICFG_FAILURE); -} - -static int -pcicfg_probe_bridge(dev_info_t *new_child, ddi_acc_handle_t h, uint_t bus, - uint_t *highest_bus) -{ - uint64_t next_bus; - uint_t new_bus; - ndi_ra_request_t req; - int rval, i, j; - uint64_t mem_answer, io_answer, mem_base, io_base, mem_alen, io_alen; - uint64_t mem_size, io_size; - uint64_t mem_end, io_end; - uint64_t round_answer, round_len; - pcicfg_range_t range[PCICFG_RANGE_LEN]; - int bus_range[2]; - pcicfg_phdl_t phdl; - int count; - uint64_t pcibus_base, pcibus_alen; - uint64_t max_bus; - - bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); - req.ra_flags = (NDI_RA_ALLOC_BOUNDED | NDI_RA_ALLOC_PARTIAL_OK); - req.ra_boundbase = 0; - req.ra_boundlen = PCICFG_MAX_BUS_DEPTH; - req.ra_len = PCICFG_MAX_BUS_DEPTH; - req.ra_align_mask = 0; /* no alignment needed */ - - rval = ndi_ra_alloc(ddi_get_parent(new_child), &req, - &pcibus_base, &pcibus_alen, NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS); - - if (rval != NDI_SUCCESS) { - if (rval == NDI_RA_PARTIAL_REQ) { - DEBUG0("NDI_RA_PARTIAL_REQ returned for bus range\n"); - } else { - DEBUG0( - "Failed to allocate bus range for bridge\n"); - return (PCICFG_FAILURE); - } - } - - DEBUG2("Bus Range Allocated [base=%d] [len=%d]\n", - pcibus_base, pcibus_alen); - - if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_PCI_BUSNUM) - == NDI_FAILURE) { - DEBUG0("Can not setup resource map - NDI_RA_TYPE_PCI_BUSNUM\n"); - return (PCICFG_FAILURE); - } - - /* - * Put available bus range into the pool. - * Take the first one for this bridge to use and don't give - * to child. - */ - (void) ndi_ra_free(new_child, pcibus_base+1, pcibus_alen-1, - NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS); - - next_bus = pcibus_base; - max_bus = pcibus_base + pcibus_alen - 1; - - new_bus = next_bus; - - DEBUG1("NEW bus found ->[%d]\n", new_bus); - - /* Keep track of highest bus for subordinate bus programming */ - *highest_bus = new_bus; - - /* - * Allocate Memory Space for Bridge - */ - bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); - req.ra_flags = (NDI_RA_ALLOC_BOUNDED | NDI_RA_ALLOC_PARTIAL_OK); - req.ra_boundbase = 0; - req.ra_boundlen = PCICFG_4GIG_LIMIT + 1; - req.ra_len = PCICFG_4GIG_LIMIT + 1; /* Get as big as possible */ - req.ra_align_mask = - PCICFG_MEMGRAN - 1; /* 1M alignment on memory space */ - - rval = ndi_ra_alloc(ddi_get_parent(new_child), &req, - &mem_answer, &mem_alen, NDI_RA_TYPE_MEM, NDI_RA_PASS); - - if (rval != NDI_SUCCESS) { - if (rval == NDI_RA_PARTIAL_REQ) { - DEBUG0("NDI_RA_PARTIAL_REQ returned\n"); - } else { - DEBUG0( - "Failed to allocate memory for bridge\n"); - return (PCICFG_FAILURE); - } - } - - DEBUG3("Bridge Memory Allocated [0x%x.%x] len [0x%x]\n", - PCICFG_HIADDR(mem_answer), - PCICFG_LOADDR(mem_answer), - mem_alen); - - if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_MEM) == NDI_FAILURE) { - DEBUG0("Can not setup resource map - NDI_RA_TYPE_MEM\n"); - return (PCICFG_FAILURE); - } - - /* - * Put available memory into the pool. - */ - (void) ndi_ra_free(new_child, mem_answer, mem_alen, NDI_RA_TYPE_MEM, - NDI_RA_PASS); - - mem_base = mem_answer; - - /* - * Allocate I/O Space for Bridge - */ - bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); - req.ra_align_mask = PCICFG_IOGRAN - 1; /* 4k alignment */ - req.ra_boundbase = 0; - req.ra_boundlen = PCICFG_4GIG_LIMIT; - req.ra_flags = (NDI_RA_ALLOC_BOUNDED | NDI_RA_ALLOC_PARTIAL_OK); - req.ra_len = PCICFG_4GIG_LIMIT; /* Get as big as possible */ - - rval = ndi_ra_alloc(ddi_get_parent(new_child), &req, &io_answer, - &io_alen, NDI_RA_TYPE_IO, NDI_RA_PASS); - - if (rval != NDI_SUCCESS) { - if (rval == NDI_RA_PARTIAL_REQ) { - DEBUG0("NDI_RA_PARTIAL_REQ returned\n"); - } else { - DEBUG0("Failed to allocate io space for bridge\n"); - return (PCICFG_FAILURE); - } - } - - DEBUG3("Bridge IO Space Allocated [0x%x.%x] len [0x%x]\n", - PCICFG_HIADDR(io_answer), PCICFG_LOADDR(io_answer), io_alen); - - if (ndi_ra_map_setup(new_child, NDI_RA_TYPE_IO) == NDI_FAILURE) { - DEBUG0("Can not setup resource map - NDI_RA_TYPE_IO\n"); - return (PCICFG_FAILURE); - } - - /* - * Put available I/O into the pool. - */ - (void) ndi_ra_free(new_child, io_answer, io_alen, NDI_RA_TYPE_IO, - NDI_RA_PASS); - - io_base = io_answer; - - (void) pcicfg_set_bus_numbers(h, bus, new_bus, max_bus); - - /* - * Reset the secondary bus - */ - pci_config_put16(h, PCI_BCNF_BCNTRL, - pci_config_get16(h, PCI_BCNF_BCNTRL) | 0x40); - - drv_usecwait(100); - - pci_config_put16(h, PCI_BCNF_BCNTRL, - pci_config_get16(h, PCI_BCNF_BCNTRL) & ~0x40); - - /* - * Program the memory base register with the - * start of the memory range - */ - pci_config_put16(h, PCI_BCNF_MEM_BASE, - PCICFG_HIWORD(PCICFG_LOADDR(mem_answer))); - - /* - * Program the memory limit register with the - * end of the memory range. - */ - - pci_config_put16(h, PCI_BCNF_MEM_LIMIT, - PCICFG_HIWORD(PCICFG_LOADDR( - PCICFG_ROUND_DOWN((mem_answer + mem_alen), PCICFG_MEMGRAN) - 1))); - - /* - * Allocate the chunk of memory (if any) not programmed into the - * bridge because of the round down. - */ - if (PCICFG_ROUND_DOWN((mem_answer + mem_alen), PCICFG_MEMGRAN) - != (mem_answer + mem_alen)) { - DEBUG0("Need to allocate Memory round off chunk\n"); - bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); - req.ra_flags = NDI_RA_ALLOC_SPECIFIED; - req.ra_addr = PCICFG_ROUND_DOWN((mem_answer + mem_alen), - PCICFG_MEMGRAN); - req.ra_len = (mem_answer + mem_alen) - - (PCICFG_ROUND_DOWN((mem_answer + mem_alen), - PCICFG_MEMGRAN)); - - (void) ndi_ra_alloc(new_child, &req, - &round_answer, &round_len, NDI_RA_TYPE_MEM, NDI_RA_PASS); - } - - /* - * Program the I/O Space Base - */ - pci_config_put8(h, PCI_BCNF_IO_BASE_LOW, - PCICFG_HIBYTE(PCICFG_LOWORD( - PCICFG_LOADDR(io_answer)))); - - pci_config_put16(h, PCI_BCNF_IO_BASE_HI, - PCICFG_HIWORD(PCICFG_LOADDR(io_answer))); - - /* - * Program the I/O Space Limit - */ - pci_config_put8(h, PCI_BCNF_IO_LIMIT_LOW, - PCICFG_HIBYTE(PCICFG_LOWORD( - PCICFG_LOADDR(PCICFG_ROUND_DOWN(io_answer + io_alen, - PCICFG_IOGRAN)))) - 1); - - pci_config_put16(h, PCI_BCNF_IO_LIMIT_HI, - PCICFG_HIWORD(PCICFG_LOADDR( - PCICFG_ROUND_DOWN(io_answer + io_alen, PCICFG_IOGRAN))) - - 1); - - /* - * Allocate the chunk of I/O (if any) not programmed into the - * bridge because of the round down. - */ - if (PCICFG_ROUND_DOWN((io_answer + io_alen), PCICFG_IOGRAN) - != (io_answer + io_alen)) { - DEBUG0("Need to allocate I/O round off chunk\n"); - bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); - req.ra_flags = NDI_RA_ALLOC_SPECIFIED; - req.ra_addr = PCICFG_ROUND_DOWN((io_answer + io_alen), - PCICFG_IOGRAN); - req.ra_len = (io_answer + io_alen) - - (PCICFG_ROUND_DOWN((io_answer + io_alen), - PCICFG_IOGRAN)); - - (void) ndi_ra_alloc(new_child, &req, - &round_answer, &round_len, NDI_RA_TYPE_IO, NDI_RA_PASS); - } - - /* - * Clear status bits - */ - pci_config_put16(h, PCI_BCNF_SEC_STATUS, 0xffff); - - /* - * Turn off prefetchable range - */ - pci_config_put32(h, PCI_BCNF_PF_BASE_LOW, 0x0000ffff); - pci_config_put32(h, PCI_BCNF_PF_BASE_HIGH, 0xffffffff); - pci_config_put32(h, PCI_BCNF_PF_LIMIT_HIGH, 0x0); - - /* - * Needs to be set to this value - */ - pci_config_put8(h, PCI_CONF_ILINE, 0xf); - - /* - * Set bus properties - */ - if (pcicfg_set_busnode_props(new_child) != PCICFG_SUCCESS) { - DEBUG0("Failed to set busnode props\n"); - return (PCICFG_FAILURE); - } - - (void) pcicfg_device_on(h); - - if (ndi_devi_online(new_child, NDI_NO_EVENT|NDI_CONFIG) - != NDI_SUCCESS) { - DEBUG0("Unable to online bridge\n"); - return (PCICFG_FAILURE); - } - - DEBUG0("Bridge is ONLINE\n"); - - /* - * After a Reset, we need to wait 2^25 clock cycles before the - * first Configuration access. The worst case is 33MHz, which - * is a 1 second wait. - */ - drv_usecwait(1000000); - - /* - * Probe all children devices - */ - DEBUG0("Bridge Programming Complete - probe children\n"); - ndi_devi_enter(new_child, &count); - for (i = 0; i < PCICFG_MAX_DEVICE; i++) { - for (j = 0; j < PCICFG_MAX_FUNCTION; j++) { - if (pcicfg_fcode_probe(new_child, - new_bus, i, j, highest_bus) != PCICFG_SUCCESS) { - DEBUG3("Failed to configure bus " - "[0x%x] device [0x%x] func [0x%x]\n", - new_bus, i, j); - break; - } - } - } - ndi_devi_exit(new_child, count); - - /* - * Offline the bridge to allow reprogramming of resources. - */ - (void) ndi_devi_offline(new_child, NDI_NO_EVENT|NDI_UNCONFIG); - - phdl.dip = new_child; - phdl.memory_base = 0; - phdl.io_base = 0; - - ndi_devi_enter(ddi_get_parent(new_child), &count); - ddi_walk_devs(new_child, pcicfg_find_resource_end, (void *)&phdl); - ndi_devi_exit(ddi_get_parent(new_child), count); - - if (phdl.error != PCICFG_SUCCESS) { - DEBUG0("Failure summing resources\n"); - return (PCICFG_FAILURE); - } - - mem_end = PCICFG_ROUND_UP(phdl.memory_base, PCICFG_MEMGRAN); - io_end = PCICFG_ROUND_UP(phdl.io_base, PCICFG_IOGRAN); - - DEBUG2("Start of Unallocated Bridge Resources Mem=0x%lx I/O=0x%lx\n", - mem_end, io_end); - - - if (mem_end == 0) { - /* - * Give back all memory space back to parent. - */ - (void) ndi_ra_free(ddi_get_parent(new_child), - mem_answer, mem_alen, NDI_RA_TYPE_MEM, NDI_RA_PASS); - - DEBUG0("No memory resources used\n"); - - /* - * To prevent the bridge from forwarding any Memory - * transactions, the Memory Limit will be programmed - * with a smaller value than the Memory Base. - */ - pci_config_put16(h, PCI_BCNF_MEM_BASE, 0xffff); - pci_config_put16(h, PCI_BCNF_MEM_LIMIT, 0); - - mem_size = 0; - } else { - /* - * Give back the unused memory space to the parent. - */ - (void) ndi_ra_free(ddi_get_parent(new_child), - mem_end, (mem_answer + mem_alen) - mem_end, NDI_RA_TYPE_MEM, - NDI_RA_PASS); - /* - * Reprogram the end of the memory. - */ - pci_config_put16(h, PCI_BCNF_MEM_LIMIT, - PCICFG_HIWORD(mem_end) - 1); - mem_size = mem_end - mem_base; - } - - - if (io_end == 0) { - /* - * Give back all io space back to parent. - */ - (void) ndi_ra_free(ddi_get_parent(new_child), - io_answer, io_alen, NDI_RA_TYPE_IO, NDI_RA_PASS); - - DEBUG0("No IO Space resources used\n"); - - /* - * To prevent the bridge from forwarding any I/O - * transactions, the I/O Limit will be programmed - * with a smaller value than the I/O Base. - */ - pci_config_put8(h, PCI_BCNF_IO_LIMIT_LOW, 0); - pci_config_put16(h, PCI_BCNF_IO_LIMIT_HI, 0); - pci_config_put8(h, PCI_BCNF_IO_BASE_LOW, 0xff); - pci_config_put16(h, PCI_BCNF_IO_BASE_HI, 0); - - io_size = 0; - } else { - /* - * Give back the unused io space to the parent. - */ - (void) ndi_ra_free(ddi_get_parent(new_child), - io_end, (io_answer + io_alen) - io_end, - NDI_RA_TYPE_IO, NDI_RA_PASS); - - /* - * Reprogram the end of the io space. - */ - pci_config_put8(h, PCI_BCNF_IO_LIMIT_LOW, - PCICFG_HIBYTE(PCICFG_LOWORD( - PCICFG_LOADDR(io_end) - 1))); - - pci_config_put16(h, PCI_BCNF_IO_LIMIT_HI, - PCICFG_HIWORD(PCICFG_LOADDR(io_end - 1))); - - io_size = io_end - io_base; - } - - if ((max_bus - *highest_bus) > 0) { - /* - * Give back unused bus numbers - */ - (void) ndi_ra_free(ddi_get_parent(new_child), - *highest_bus+1, max_bus - *highest_bus, - NDI_RA_TYPE_PCI_BUSNUM, NDI_RA_PASS); - } - - /* - * Set bus numbers to ranges encountered during scan - */ - (void) pcicfg_set_bus_numbers(h, bus, new_bus, *highest_bus); - - /* - * Remove the ranges property if it exists since we will create - * a new one. - */ - (void) ndi_prop_remove(DDI_DEV_T_NONE, new_child, "ranges"); - - DEBUG2("Creating Ranges property - Mem Address %lx Mem Size %x\n", - mem_base, mem_size); - DEBUG2(" - I/O Address %lx I/O Size %x\n", - io_base, io_size); - - bzero((caddr_t)range, sizeof (pcicfg_range_t) * PCICFG_RANGE_LEN); - - range[0].child_hi = range[0].parent_hi |= (PCI_REG_REL_M | PCI_ADDR_IO); - range[0].child_lo = range[0].parent_lo = io_base; - range[1].child_hi = range[1].parent_hi |= - (PCI_REG_REL_M | PCI_ADDR_MEM32); - range[1].child_lo = range[1].parent_lo = mem_base; - - if (io_size > 0) { - range[0].size_lo = io_size; - if (pcicfg_update_ranges_prop(new_child, &range[0])) { - DEBUG0("Failed to update ranges (io)\n"); - return (PCICFG_FAILURE); - } - } - if (mem_size > 0) { - range[1].size_lo = mem_size; - if (pcicfg_update_ranges_prop(new_child, &range[1])) { - DEBUG0("Failed to update ranges (memory)\n"); - return (PCICFG_FAILURE); - } - } - - bus_range[0] = pci_config_get8(h, PCI_BCNF_SECBUS); - bus_range[1] = pci_config_get8(h, PCI_BCNF_SUBBUS); - DEBUG1("End of bridge probe: bus_range[0] = %d\n", bus_range[0]); - DEBUG1("End of bridge probe: bus_range[1] = %d\n", bus_range[1]); - - if (ndi_prop_update_int_array(DDI_DEV_T_NONE, new_child, - "bus-range", bus_range, 2) != DDI_SUCCESS) { - DEBUG0("Failed to set bus-range property"); - return (PCICFG_FAILURE); - } - /* - * Remove the resource maps for the bridge since we no longer - * need them. - */ - if (ndi_ra_map_destroy(new_child, NDI_RA_TYPE_MEM) == NDI_FAILURE) { - DEBUG0("Can not destroy resource map - NDI_RA_TYPE_MEM\n"); - return (PCICFG_FAILURE); - } - - if (ndi_ra_map_destroy(new_child, NDI_RA_TYPE_IO) == NDI_FAILURE) { - DEBUG0("Can not destroy resource map - NDI_RA_TYPE_IO\n"); - return (PCICFG_FAILURE); - } - - if (ndi_ra_map_destroy(new_child, NDI_RA_TYPE_PCI_BUSNUM) - == NDI_FAILURE) { - DEBUG0("Can't destroy resource map - NDI_RA_TYPE_PCI_BUSNUM\n"); - return (PCICFG_FAILURE); - } - - return (PCICFG_SUCCESS); -} - -/* - * Return PCICFG_SUCCESS if device exists at the specified address. - * Return PCICFG_NODEVICE is no device exists at the specified address. - * - */ -int -pcicfg_config_setup(dev_info_t *dip, ddi_acc_handle_t *handle) -{ - caddr_t virt; - ddi_device_acc_attr_t attr; - int status; - int rlen; - pci_regspec_t *reg; - int ret = DDI_SUCCESS; - int16_t tmp; - /* - * flags = PCICFG_CONF_INDIRECT_MAP if configuration space is indirectly - * mapped, otherwise it is 0. "flags" is introduced in support of any - * non transparent bridges, where configuration space is indirectly - * mapped. - */ - int flags = 0; - - - /* - * Get the pci register spec from the node - */ - status = ddi_getlongprop(DDI_DEV_T_ANY, - dip, DDI_PROP_DONTPASS, "reg", (caddr_t)®, &rlen); - - switch (status) { - case DDI_PROP_SUCCESS: - break; - case DDI_PROP_NO_MEMORY: - DEBUG0("reg present, but unable to get memory\n"); - return (PCICFG_FAILURE); - default: - DEBUG0("no reg property\n"); - return (PCICFG_FAILURE); - } - - if (pcicfg_indirect_map(dip) == DDI_SUCCESS) - flags |= PCICFG_CONF_INDIRECT_MAP; - - /* - * Map in configuration space (temporarily) - */ - attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; - attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; - attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; - -#ifdef EFCODE21554 - if (ddi_regs_map_setup(dip, 0, &virt, - 0, 0, &attr, handle) != DDI_SUCCESS) -#else - if (pcicfg_map_phys(dip, reg, &virt, &attr, handle) - != DDI_SUCCESS) -#endif - { - DEBUG0("pcicfg_config_setup():" - "Failed to setup config space\n"); - - kmem_free((caddr_t)reg, rlen); - return (PCICFG_FAILURE); - } - - if (flags & PCICFG_CONF_INDIRECT_MAP) { - /* - * need to use DDI interfaces as the conf space is - * cannot be directly accessed by the host. - */ - tmp = (int16_t)ddi_get16(*handle, (uint16_t *)virt); - } else { - ret = ddi_peek16(dip, (int16_t *)virt, &tmp); - } - - if (ret == DDI_SUCCESS) { - if ((tmp == (int16_t)0xffff) || (tmp == -1)) { - DEBUG1("NO DEVICEFOUND, read %x\n", tmp); - ret = PCICFG_NODEVICE; - } else { - DEBUG1("DEVICEFOUND, read %x\n", tmp); - ret = PCICFG_SUCCESS; - } - } else { - DEBUG0("ddi_peek failed, must be NODEVICE\n"); - ret = PCICFG_NODEVICE; - } - - /* - * A bug in XMITS 3.0 causes us to miss the Master Abort Split - * Completion message. The result is the error message being - * sent back as part of the config data. If the first two words - * of the config space happen to be the same as the Master Abort - * message, then report back that there is no device there. - */ - if ((ret == PCICFG_SUCCESS) && !(flags & PCICFG_CONF_INDIRECT_MAP)) { - int32_t pcix_scm; - -#define PCICFG_PCIX_SCM 0x10000004 - - pcix_scm = 0; - (void) ddi_peek32(dip, (int32_t *)virt, &pcix_scm); - if (pcix_scm == PCICFG_PCIX_SCM) { - pcix_scm = 0; - (void) ddi_peek32(dip, - (int32_t *)(virt + 4), &pcix_scm); - if (pcix_scm == PCICFG_PCIX_SCM) - ret = PCICFG_NODEVICE; - } - } - - if (ret == PCICFG_NODEVICE) -#ifdef EFCODE21554 - ddi_regs_map_free(handle); -#else - pcicfg_unmap_phys(handle, reg); -#endif - - kmem_free((caddr_t)reg, rlen); - - return (ret); - -} - -static void -pcicfg_config_teardown(ddi_acc_handle_t *handle) -{ - (void) ddi_regs_map_free(handle); -} - -static int -pcicfg_add_config_reg(dev_info_t *dip, - uint_t bus, uint_t device, uint_t func) -{ - int reg[10] = { PCI_ADDR_CONFIG, 0, 0, 0, 0}; - - reg[0] = PCICFG_MAKE_REG_HIGH(bus, device, func, 0); - - return (ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, - "reg", reg, 5)); -} - -static int -pcicfg_dump_assigned(dev_info_t *dip) -{ - pci_regspec_t *reg; - int length; - int rcount; - int i; - - if (ddi_getlongprop(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)®, - &length) != DDI_PROP_SUCCESS) { - DEBUG0("Failed to read assigned-addresses property\n"); - return (PCICFG_FAILURE); - } - - rcount = length / sizeof (pci_regspec_t); - for (i = 0; i < rcount; i++) { - DEBUG4("pcicfg_dump_assigned - size=%x low=%x mid=%x high=%x\n", - reg[i].pci_size_low, reg[i].pci_phys_low, - reg[i].pci_phys_mid, reg[i].pci_phys_hi); - } - /* - * Don't forget to free up memory from ddi_getlongprop - */ - kmem_free((caddr_t)reg, length); - - return (PCICFG_SUCCESS); -} - -#ifdef PCICFG_INTERPRET_FCODE -static int -pcicfg_load_fcode(dev_info_t *dip, uint_t bus, uint_t device, uint_t func, - uint16_t vendor_id, uint16_t device_id, uchar_t **fcode_addr, - int *fcode_size, int rom_paddr, int rom_size) -{ - pci_regspec_t p; - int pci_data; - int start_of_fcode; - int image_length; - int code_type; - ddi_acc_handle_t h; - ddi_device_acc_attr_t acc; - uint8_t *addr; - int8_t image_not_found, indicator; - uint16_t vendor_id_img, device_id_img; - int16_t rom_sig; -#ifdef DEBUG - int i; -#endif - - DEBUG4("pcicfg_load_fcode() - " - "bus %x device =%x func=%x rom_paddr=%lx\n", - bus, device, func, rom_paddr); - DEBUG2("pcicfg_load_fcode() - vendor_id=%x device_id=%x\n", - vendor_id, device_id); - - *fcode_size = 0; - *fcode_addr = NULL; - - acc.devacc_attr_version = DDI_DEVICE_ATTR_V0; - acc.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; - acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC; - - p.pci_phys_hi = PCI_ADDR_MEM32 | PCICFG_MAKE_REG_HIGH(bus, device, - func, PCI_CONF_ROM); - - p.pci_phys_mid = 0; - p.pci_phys_low = 0; - - p.pci_size_low = rom_size; - p.pci_size_hi = 0; - - if (pcicfg_map_phys(dip, &p, (caddr_t *)&addr, &acc, &h)) { - DEBUG1("Can Not map in ROM %x\n", p.pci_phys_low); - return (PCICFG_FAILURE); - } - - /* - * Walk the ROM to find the proper image for this device. - */ - image_not_found = 1; - while (image_not_found) { - DEBUG1("Expansion ROM maps to %lx\n", addr); - -#ifdef DEBUG - if (pcicfg_dump_fcode) { - for (i = 0; i < 100; i++) - DEBUG2("ROM 0x%x --> 0x%x\n", i, - ddi_get8(h, (uint8_t *)(addr + i))); - } -#endif - - /* - * Some device say they have an Expansion ROM, but do not, so - * for non-21554 devices use peek so we don't panic due to - * accessing non existent memory. - */ - if (pcicfg_indirect_map(dip) == DDI_SUCCESS) { - rom_sig = ddi_get16(h, - (uint16_t *)(addr + PCI_ROM_SIGNATURE)); - } else { - if (ddi_peek16(dip, - (int16_t *)(addr + PCI_ROM_SIGNATURE), &rom_sig)) { - cmn_err(CE_WARN, - "PCI Expansion ROM is not accessible"); - pcicfg_unmap_phys(&h, &p); - return (PCICFG_FAILURE); - } - } - - /* - * Validate the ROM Signature. - */ - if ((uint16_t)rom_sig != 0xaa55) { - DEBUG1("Invalid ROM Signature %x\n", (uint16_t)rom_sig); - pcicfg_unmap_phys(&h, &p); - return (PCICFG_FAILURE); - } - - DEBUG0("Valid ROM Signature Found\n"); - - start_of_fcode = ddi_get16(h, (uint16_t *)(addr + 2)); - - pci_data = ddi_get16(h, - (uint16_t *)(addr + PCI_ROM_PCI_DATA_STRUCT_PTR)); - - DEBUG2("Pointer To PCI Data Structure %x %x\n", pci_data, - addr); - - /* - * Validate the PCI Data Structure Signature. - * 0x52494350 = "PCIR" - */ - - if (ddi_get8(h, (uint8_t *)(addr + pci_data)) != 0x50) { - DEBUG0("Invalid PCI Data Structure Signature\n"); - pcicfg_unmap_phys(&h, &p); - return (PCICFG_FAILURE); - } - - if (ddi_get8(h, (uint8_t *)(addr + pci_data + 1)) != 0x43) { - DEBUG0("Invalid PCI Data Structure Signature\n"); - pcicfg_unmap_phys(&h, &p); - return (PCICFG_FAILURE); - } - if (ddi_get8(h, (uint8_t *)(addr + pci_data + 2)) != 0x49) { - DEBUG0("Invalid PCI Data Structure Signature\n"); - pcicfg_unmap_phys(&h, &p); - return (PCICFG_FAILURE); - } - if (ddi_get8(h, (uint8_t *)(addr + pci_data + 3)) != 0x52) { - DEBUG0("Invalid PCI Data Structure Signature\n"); - pcicfg_unmap_phys(&h, &p); - return (PCICFG_FAILURE); - } - - /* - * Is this image for this device? - */ - vendor_id_img = ddi_get16(h, - (uint16_t *)(addr + pci_data + PCI_PDS_VENDOR_ID)); - device_id_img = ddi_get16(h, - (uint16_t *)(addr + pci_data + PCI_PDS_DEVICE_ID)); - - DEBUG2("This image is for vendor_id=%x device_id=%x\n", - vendor_id_img, device_id_img); - - code_type = ddi_get8(h, addr + pci_data + PCI_PDS_CODE_TYPE); - - switch (code_type) { - case PCI_PDS_CODE_TYPE_PCAT: - DEBUG0("ROM is of x86/PC-AT Type\n"); - break; - case PCI_PDS_CODE_TYPE_OPEN_FW: - DEBUG0("ROM is of Open Firmware Type\n"); - break; - default: - DEBUG1("ROM is of Unknown Type 0x%x\n", code_type); - break; - } - - if ((vendor_id_img != vendor_id) || - (device_id_img != device_id) || - (code_type != PCI_PDS_CODE_TYPE_OPEN_FW)) { - DEBUG0("Firmware Image is not for this device..." - "goto next image\n"); - /* - * Read indicator byte to see if there is another - * image in the ROM - */ - indicator = ddi_get8(h, - (uint8_t *)(addr + pci_data + PCI_PDS_INDICATOR)); - - if (indicator != 1) { - /* - * There is another image in the ROM. - */ - image_length = ddi_get16(h, (uint16_t *)(addr + - pci_data + PCI_PDS_IMAGE_LENGTH)) * 512; - - addr += image_length; - } else { - /* - * There are no more images. - */ - DEBUG0("There are no more images in the ROM\n"); - pcicfg_unmap_phys(&h, &p); - - return (PCICFG_FAILURE); - } - } else { - DEBUG0("Correct image was found\n"); - image_not_found = 0; /* Image was found */ - } - } - - *fcode_size = (ddi_get8(h, addr + start_of_fcode + 4) << 24) | - (ddi_get8(h, addr + start_of_fcode + 5) << 16) | - (ddi_get8(h, addr + start_of_fcode + 6) << 8) | - (ddi_get8(h, addr + start_of_fcode + 7)); - - DEBUG1("Fcode Size %x\n", *fcode_size); - - /* - * Allocate page aligned buffer space - */ - *fcode_addr = kmem_zalloc(ptob(btopr(*fcode_size)), KM_SLEEP); - - if (*fcode_addr == NULL) { - DEBUG0("kmem_zalloc returned NULL\n"); - pcicfg_unmap_phys(&h, &p); - return (PCICFG_FAILURE); - } - - DEBUG1("Fcode Addr %lx\n", *fcode_addr); - - ddi_rep_get8(h, *fcode_addr, addr + start_of_fcode, *fcode_size, - DDI_DEV_AUTOINCR); - - pcicfg_unmap_phys(&h, &p); - - return (PCICFG_SUCCESS); -} - -static int -pcicfg_fcode_assign_bars(ddi_acc_handle_t h, dev_info_t *dip, uint_t bus, - uint_t device, uint_t func, int32_t fc_request, pci_regspec_t *rom_regspec) -{ - /* - * Assign values to all BARs so that it is safe to turn on the - * device for accessing the fcode on the PROM. On successful - * exit from this function, "assigned-addresses" are created - * for all BARs and ROM BAR is enabled. Also, rom_regspec is - * filled with the values that can be used to free up this - * resource later. - */ - uint32_t request, hiword, size; - pci_regspec_t phys_spec; - ndi_ra_request_t req; - uint64_t mem_answer, mem_alen; - int i; - - DEBUG1("pcicfg_fcode_assign_bars :%s\n", DEVI(dip)->devi_name); - - /* - * Process the BARs. - */ - for (i = PCI_CONF_BASE0; i <= PCI_CONF_BASE5; ) { - pci_config_put32(h, i, 0xffffffff); - request = pci_config_get32(h, i); - /* - * Check if implemented - */ - if (request == 0) { - DEBUG1("pcicfg_fcode_assign_bars :" - "BASE register [0x%x] asks for 0(32)\n", i); - i += 4; - continue; - } - /* - * Build the phys_spec for this BAR - */ - hiword = PCICFG_MAKE_REG_HIGH(bus, device, func, i); - size = (~(PCI_BASE_M_ADDR_M & request)) + 1; - - DEBUG3("pcicfg_fcode_assign_bars :" - "BASE register [0x%x] asks for [0x%x]=[0x%x]\n", - i, request, size); - - if ((PCI_BASE_SPACE_M & request) == PCI_BASE_SPACE_MEM) { - if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_MEM) { - hiword |= PCI_ADDR_MEM32; - } else if ((PCI_BASE_TYPE_M & request) - == PCI_BASE_TYPE_ALL) { - hiword |= PCI_ADDR_MEM64; - } - if (request & PCI_BASE_PREF_M) - hiword |= PCI_REG_PF_M; - } else { - hiword |= PCI_ADDR_IO; - } - phys_spec.pci_phys_hi = hiword; - phys_spec.pci_phys_mid = 0; - phys_spec.pci_phys_low = 0; - phys_spec.pci_size_hi = 0; - phys_spec.pci_size_low = size; - - /* - * The following function - * - allocates address space - * - programs the BAR - * - adds an "assigned-addresses" property - */ - if (pcicfg_alloc_resource(dip, phys_spec)) { - cmn_err(CE_WARN, "failed to allocate %d bytes" - " for dev %s BASE register [0x%x]\n", - size, DEVI(dip)->devi_name, i); - goto failure; - } - if ((PCI_BASE_TYPE_M & request) == PCI_BASE_TYPE_ALL) { - /* - * 64 bit, should be in memory space. - */ - i += 8; - } else { - /* - * 32 bit, either memory or I/O space. - */ - i += 4; - } - } - - /* - * Handle ROM BAR. We do not use the common - * resource allocator function because we need to - * return reg spec to the caller. - */ - size = (~(PCI_BASE_ROM_ADDR_M & fc_request)) + 1; - - DEBUG3("BASE register [0x%x] asks for " - "[0x%x]=[0x%x]\n", PCI_CONF_ROM, fc_request, size); - - bzero((caddr_t)&req, sizeof (ndi_ra_request_t)); - - req.ra_boundbase = 0; - req.ra_boundlen = PCICFG_4GIG_LIMIT; - req.ra_len = size; - req.ra_flags |= NDI_RA_ALIGN_SIZE; - req.ra_flags ^= NDI_RA_ALLOC_BOUNDED; - - if (ndi_ra_alloc(ddi_get_parent(dip), - &req, &mem_answer, &mem_alen, - NDI_RA_TYPE_MEM, NDI_RA_PASS)) { - cmn_err(CE_WARN, "failed to allocate %d bytes" - " for dev %s ROM BASE register\n", - size, DEVI(dip)->devi_name); - goto failure; - } - - DEBUG3("ROM addr = [0x%x.%x] len [0x%x]\n", - PCICFG_HIADDR(mem_answer), - PCICFG_LOADDR(mem_answer), mem_alen); - - /* - * Assign address space and enable ROM. - */ - pci_config_put32(h, PCI_CONF_ROM, - PCICFG_LOADDR(mem_answer) | PCI_BASE_ROM_ENABLE); - - /* - * Add resource to assigned-addresses. - */ - phys_spec.pci_phys_hi = PCICFG_MAKE_REG_HIGH(bus, device, func, \ - PCI_CONF_ROM) | PCI_ADDR_MEM32; - if (fc_request & PCI_BASE_PREF_M) - phys_spec.pci_phys_hi |= PCI_REG_PF_M; - phys_spec.pci_phys_mid = 0; - phys_spec.pci_phys_low = PCICFG_LOADDR(mem_answer); - phys_spec.pci_size_hi = 0; - phys_spec.pci_size_low = size; - - if (pcicfg_update_assigned_prop(dip, &phys_spec) - != PCICFG_SUCCESS) { - cmn_err(CE_WARN, "failed to update" - " assigned-address property for dev %s\n", - DEVI(dip)->devi_name); - goto failure; - } - /* - * Copy out the reg spec. - */ - *rom_regspec = phys_spec; - - return (PCICFG_SUCCESS); - -failure: - /* - * We came in with no "assigned-addresses". - * Free up the resources we may have allocated. - */ - (void) pcicfg_free_device_resources(dip); - - return (PCICFG_FAILURE); -} - -#endif /* PCICFG_INTERPRET_FCODE */ - -static int -pcicfg_free_all_resources(dev_info_t *dip) -{ - pci_regspec_t *assigned; - int assigned_len; - int acount; - int i; - - if (ddi_getlongprop(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned, - &assigned_len) != DDI_PROP_SUCCESS) { - DEBUG0("Failed to read assigned-addresses property\n"); - return (PCICFG_FAILURE); - } - - acount = assigned_len / sizeof (pci_regspec_t); - - for (i = 0; i < acount; i++) { - if (pcicfg_free_resource(dip, assigned[i])) { - /* - * Dont forget to free mem from ddi_getlongprop - */ - kmem_free((caddr_t)assigned, assigned_len); - return (PCICFG_FAILURE); - } - } - - /* - * Don't forget to free up memory from ddi_getlongprop - */ - if (assigned_len) - kmem_free((caddr_t)assigned, assigned_len); - - return (PCICFG_SUCCESS); -} -static int -pcicfg_alloc_new_resources(dev_info_t *dip) -{ - pci_regspec_t *assigned, *reg; - int assigned_len, reg_len; - int acount, rcount; - int i, j, alloc_size; - boolean_t alloc; - - if (ddi_getlongprop(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, "reg", (caddr_t)®, - ®_len) != DDI_PROP_SUCCESS) { - DEBUG0("Failed to read reg property\n"); - return (PCICFG_FAILURE); - } - rcount = reg_len / sizeof (pci_regspec_t); - - DEBUG2("pcicfg_alloc_new_resources() reg size=%x entries=%x\n", - reg_len, rcount); - - if (ddi_getlongprop(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned, - &assigned_len) != DDI_PROP_SUCCESS) { - acount = 0; - } else { - acount = assigned_len / sizeof (pci_regspec_t); - } - - DEBUG1("assigned-addresses property len=%x\n", acount); - - /* - * For each address described by reg, search for it in the - * assigned-addresses property. If it does not exist, allocate - * resources for it. If it does exist, check the size in both. - * The size needs to be bigger of the two. - */ - for (i = 1; i < rcount; i++) { - alloc = B_TRUE; - alloc_size = reg[i].pci_size_low; - for (j = 0; j < acount; j++) { - if (assigned[j].pci_phys_hi == reg[i].pci_phys_hi) { - /* - * There is an exact match. Check size. - */ - DEBUG1("pcicfg_alloc_new_resources " - "- %x - MATCH\n", - reg[i].pci_phys_hi); - - if (reg[i].pci_size_low > - assigned[j].pci_size_low) { - /* - * Fcode wants more. - */ - DEBUG3("pcicfg_alloc_new_resources" - " - %x - RESIZE" - " assigned 0x%x reg 0x%x\n", - assigned[j].pci_phys_hi, - assigned[j].pci_size_low, - reg[i].pci_size_low); - - /* - * Free the old resource. - */ - (void) pcicfg_free_resource(dip, - assigned[j]); - } else { - DEBUG3("pcicfg_alloc_new_resources" - " - %x - ENOUGH" - " assigned 0x%x reg 0x%x\n", - assigned[j].pci_phys_hi, - assigned[j].pci_size_low, - reg[i].pci_size_low); - - alloc = B_FALSE; - } - break; - } - /* - * Fcode may have set one or more of the - * NPT bits in phys.hi. - */ - if (PCI_REG_BDFR_G(assigned[j].pci_phys_hi) == - PCI_REG_BDFR_G(reg[i].pci_phys_hi)) { - - DEBUG2("pcicfg_alloc_new_resources " - "- PARTIAL MATCH assigned 0x%x " - "reg 0x%x\n", assigned[j].pci_phys_hi, - reg[i].pci_phys_hi); - /* - * Changing the SS bits is an error - */ - if (PCI_REG_ADDR_G( - assigned[j].pci_phys_hi) != - PCI_REG_ADDR_G(reg[i].pci_phys_hi)) { - - cmn_err(CE_WARN, "Fcode changing" - " SS bits of - 0x%x -" - " on %s\n", reg[i].pci_phys_hi, - DEVI(dip)->devi_name); - - /* - * Dont forget to free mem from - * ddi_getlongprop - */ - if (acount != 0) - kmem_free((caddr_t)assigned, - assigned_len); - kmem_free((caddr_t)reg, reg_len); - return (PCICFG_FAILURE); - } - - - /* - * We are going to allocate new resource. - * Free the old resource. Again, adjust - * the size to be safe. - */ - (void) pcicfg_free_resource(dip, assigned[j]); - - alloc_size = MAX(reg[i].pci_size_low, - assigned[j].pci_size_low); - - break; - } - } - /* - * We are allocating resources for one of three reasons - - * - Fcode wants a larger address space - * - Fcode has set changed/set n, p, t bits. - * - It is a new "reg", it should be only ROM bar, but - * we don't do the checking. - */ - if (alloc == B_TRUE) { - DEBUG1("pcicfg_alloc_new_resources : creating 0x%x\n", - reg[i].pci_phys_hi); - - reg[i].pci_size_low = alloc_size; - if (pcicfg_alloc_resource(dip, reg[i])) { - /* - * Dont forget to free mem from - * ddi_getlongprop - */ - if (acount != 0) - kmem_free((caddr_t)assigned, assigned_len); - kmem_free((caddr_t)reg, reg_len); - return (PCICFG_FAILURE); - } - } - } - - /* - * Don't forget to free up memory from ddi_getlongprop - */ - if (acount != 0) - kmem_free((caddr_t)assigned, assigned_len); - kmem_free((caddr_t)reg, reg_len); - - return (PCICFG_SUCCESS); -} -static int -pcicfg_alloc_resource(dev_info_t *dip, pci_regspec_t phys_spec) -{ - uint64_t answer; - uint64_t alen; - int offset; - pci_regspec_t config; - caddr_t virt, v; - ddi_device_acc_attr_t acc; - ddi_acc_handle_t h; - ndi_ra_request_t request; - pci_regspec_t *assigned; - int assigned_len, entries, i; - - if (ddi_getlongprop(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&assigned, - &assigned_len) == DDI_PROP_SUCCESS) { - DEBUG0("pcicfg_alloc_resource - " - "searching assigned-addresses\n"); - - entries = assigned_len / (sizeof (pci_regspec_t)); - - /* - * Walk through the assigned-addresses entries. If there is - * a match, there is no need to allocate the resource. - */ - for (i = 0; i < entries; i++) { - if (assigned[i].pci_phys_hi == phys_spec.pci_phys_hi) { - DEBUG1("pcicfg_alloc_resource - MATCH %x\n", - assigned[i].pci_phys_hi); - kmem_free(assigned, assigned_len); - return (0); - } - } - kmem_free(assigned, assigned_len); - } - - bzero((caddr_t)&request, sizeof (ndi_ra_request_t)); - - config.pci_phys_hi = PCI_CONF_ADDR_MASK & phys_spec.pci_phys_hi; - config.pci_phys_hi &= ~PCI_REG_REG_M; - config.pci_phys_mid = config.pci_phys_low = 0; - config.pci_size_hi = config.pci_size_low = 0; - - /* - * Map in configuration space (temporarily) - */ - acc.devacc_attr_version = DDI_DEVICE_ATTR_V0; - acc.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; - acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC; - - if (pcicfg_map_phys(dip, &config, &virt, &acc, &h)) { - DEBUG0("Can not map in config space\n"); - return (1); - } - - request.ra_flags |= NDI_RA_ALIGN_SIZE; - request.ra_boundbase = 0; - request.ra_boundlen = PCICFG_4GIG_LIMIT; - /* - * Use size stored in phys_spec parameter. - */ - request.ra_len = phys_spec.pci_size_low; - - offset = PCI_REG_REG_G(phys_spec.pci_phys_hi); - - v = virt + offset; - - if (PCI_REG_REG_G(phys_spec.pci_phys_hi) == PCI_CONF_ROM) { - - request.ra_flags ^= NDI_RA_ALLOC_BOUNDED; - - /* allocate memory space from the allocator */ - - if (ndi_ra_alloc(ddi_get_parent(dip), - &request, &answer, &alen, - NDI_RA_TYPE_MEM, NDI_RA_PASS) - != NDI_SUCCESS) { - DEBUG0("(ROM)Failed to allocate 32b mem"); - pcicfg_unmap_phys(&h, &config); - return (1); - } - DEBUG3("ROM addr = [0x%x.%x] len [0x%x]\n", - PCICFG_HIADDR(answer), - PCICFG_LOADDR(answer), - alen); - - /* program the low word */ - - ddi_put32(h, (uint32_t *)v, (uint32_t)PCICFG_LOADDR(answer)); - - phys_spec.pci_phys_low = PCICFG_LOADDR(answer); - phys_spec.pci_phys_mid = PCICFG_HIADDR(answer); - } else { - - switch (PCI_REG_ADDR_G(phys_spec.pci_phys_hi)) { - case PCI_REG_ADDR_G(PCI_ADDR_MEM64): - request.ra_flags ^= NDI_RA_ALLOC_BOUNDED; - /* allocate memory space from the allocator */ - if (ndi_ra_alloc(ddi_get_parent(dip), - &request, &answer, &alen, - NDI_RA_TYPE_MEM, NDI_RA_PASS) - != NDI_SUCCESS) { - DEBUG0("Failed to allocate 64b mem\n"); - pcicfg_unmap_phys(&h, &config); - return (1); - } - DEBUG3("64 addr = [0x%x.%x] len [0x%x]\n", - PCICFG_HIADDR(answer), - PCICFG_LOADDR(answer), - alen); - - /* program the low word */ - - ddi_put32(h, (uint32_t *)v, - (uint32_t)PCICFG_LOADDR(answer)); - - /* program the high word with value zero */ - v += 4; - ddi_put32(h, (uint32_t *)v, - (uint32_t)PCICFG_HIADDR(answer)); - - phys_spec.pci_phys_low = PCICFG_LOADDR(answer); - phys_spec.pci_phys_mid = PCICFG_HIADDR(answer); - - break; - - case PCI_REG_ADDR_G(PCI_ADDR_MEM32): - request.ra_flags |= NDI_RA_ALLOC_BOUNDED; - /* allocate memory space from the allocator */ - if (ndi_ra_alloc(ddi_get_parent(dip), - &request, &answer, &alen, - NDI_RA_TYPE_MEM, NDI_RA_PASS) - != NDI_SUCCESS) { - DEBUG0("Failed to allocate 32b mem\n"); - pcicfg_unmap_phys(&h, &config); - return (1); - } - - DEBUG3("32 addr = [0x%x.%x] len [0x%x]\n", - PCICFG_HIADDR(answer), - PCICFG_LOADDR(answer), - alen); - - /* program the low word */ - - ddi_put32(h, (uint32_t *)v, - (uint32_t)PCICFG_LOADDR(answer)); - - phys_spec.pci_phys_low = PCICFG_LOADDR(answer); - - break; - case PCI_REG_ADDR_G(PCI_ADDR_IO): - /* allocate I/O space from the allocator */ - request.ra_flags |= NDI_RA_ALLOC_BOUNDED; - if (ndi_ra_alloc(ddi_get_parent(dip), - &request, &answer, &alen, - NDI_RA_TYPE_IO, NDI_RA_PASS) - != NDI_SUCCESS) { - DEBUG0("Failed to allocate I/O\n"); - pcicfg_unmap_phys(&h, &config); - return (1); - } - DEBUG3("I/O addr = [0x%x.%x] len [0x%x]\n", - PCICFG_HIADDR(answer), - PCICFG_LOADDR(answer), - alen); - - ddi_put32(h, (uint32_t *)v, - (uint32_t)PCICFG_LOADDR(answer)); - - phys_spec.pci_phys_low = PCICFG_LOADDR(answer); - - break; - default: - DEBUG0("Unknown register type\n"); - pcicfg_unmap_phys(&h, &config); - return (1); - } /* switch */ - } - - /* - * Now that memory locations are assigned, - * update the assigned address property. - */ - - DEBUG1("updating assigned-addresss for %x\n", phys_spec.pci_phys_hi); - - if (pcicfg_update_assigned_prop(dip, &phys_spec)) { - pcicfg_unmap_phys(&h, &config); - return (1); - } - - pcicfg_unmap_phys(&h, &config); - - return (0); -} - -static int -pcicfg_free_resource(dev_info_t *dip, pci_regspec_t phys_spec) -{ - int offset; - pci_regspec_t config; - caddr_t virt, v; - ddi_device_acc_attr_t acc; - ddi_acc_handle_t h; - ndi_ra_request_t request; - int l; - - bzero((caddr_t)&request, sizeof (ndi_ra_request_t)); - - config.pci_phys_hi = PCI_CONF_ADDR_MASK & phys_spec.pci_phys_hi; - config.pci_phys_hi &= ~PCI_REG_REG_M; - config.pci_phys_mid = config.pci_phys_low = 0; - config.pci_size_hi = config.pci_size_low = 0; - - /* - * Map in configuration space (temporarily) - */ - acc.devacc_attr_version = DDI_DEVICE_ATTR_V0; - acc.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; - acc.devacc_attr_dataorder = DDI_STRICTORDER_ACC; - - if (pcicfg_map_phys(dip, &config, &virt, &acc, &h)) { - DEBUG0("Can not map in config space\n"); - return (1); - } - - offset = PCI_REG_REG_G(phys_spec.pci_phys_hi); - - v = virt + offset; - - /* - * Use size stored in phys_spec parameter. - */ - l = phys_spec.pci_size_low; - - if (PCI_REG_REG_G(phys_spec.pci_phys_hi) == PCI_CONF_ROM) { - - /* free memory back to the allocator */ - if (ndi_ra_free(ddi_get_parent(dip), phys_spec.pci_phys_low, - l, NDI_RA_TYPE_MEM, NDI_RA_PASS) != NDI_SUCCESS) { - DEBUG0("(ROM)Can not free 32b mem"); - pcicfg_unmap_phys(&h, &config); - return (1); - } - - /* Unmap the BAR by writing a zero */ - - ddi_put32(h, (uint32_t *)v, (uint32_t)0); - } else { - - switch (PCI_REG_ADDR_G(phys_spec.pci_phys_hi)) { - case PCI_REG_ADDR_G(PCI_ADDR_MEM64): - /* free memory back to the allocator */ - if (ndi_ra_free(ddi_get_parent(dip), - PCICFG_LADDR(phys_spec.pci_phys_low, - phys_spec.pci_phys_mid), - l, NDI_RA_TYPE_MEM, - NDI_RA_PASS) != NDI_SUCCESS) { - DEBUG0("Can not free 64b mem"); - pcicfg_unmap_phys(&h, &config); - return (1); - } - - break; - - case PCI_REG_ADDR_G(PCI_ADDR_MEM32): - /* free memory back to the allocator */ - if (ndi_ra_free(ddi_get_parent(dip), - phys_spec.pci_phys_low, - l, NDI_RA_TYPE_MEM, - NDI_RA_PASS) != NDI_SUCCESS) { - DEBUG0("Can not free 32b mem"); - pcicfg_unmap_phys(&h, &config); - return (1); - } - - break; - case PCI_REG_ADDR_G(PCI_ADDR_IO): - /* free I/O space back to the allocator */ - if (ndi_ra_free(ddi_get_parent(dip), - phys_spec.pci_phys_low, - l, NDI_RA_TYPE_IO, - NDI_RA_PASS) != NDI_SUCCESS) { - DEBUG0("Can not free I/O space"); - pcicfg_unmap_phys(&h, &config); - return (1); - } - - break; - default: - DEBUG0("Unknown register type\n"); - pcicfg_unmap_phys(&h, &config); - return (1); - } /* switch */ - } - - /* - * Now that memory locations are assigned, - * update the assigned address property. - */ - - DEBUG1("updating assigned-addresss for %x\n", phys_spec.pci_phys_hi); - - if (pcicfg_remove_assigned_prop(dip, &phys_spec)) { - pcicfg_unmap_phys(&h, &config); - return (1); - } - - pcicfg_unmap_phys(&h, &config); - - return (0); -} - -static int -pcicfg_remove_assigned_prop(dev_info_t *dip, pci_regspec_t *oldone) -{ - int alen, num_entries, i; - pci_regspec_t *assigned, *assigned_copy; - uint_t status; - - status = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, - "assigned-addresses", (caddr_t)&assigned, &alen); - switch (status) { - case DDI_PROP_SUCCESS: - break; - case DDI_PROP_NO_MEMORY: - DEBUG0("no memory for assigned-addresses property\n"); - return (1); - default: - DEBUG0("assigned-addresses property does not exist\n"); - return (0); - } - - /* - * Make a copy of old assigned-addresses property. - */ - assigned_copy = kmem_alloc(alen, KM_SLEEP); - bcopy(assigned, assigned_copy, alen); - - status = ndi_prop_remove(DDI_DEV_T_NONE, dip, "assigned-addresses"); - - if (status != DDI_PROP_SUCCESS) { - /* - * If "assigned-addresses" is retrieved from PROM, the - * ndi_prop_remove() will fail. - */ - DEBUG1("pcicfg_remove_assigned_prop: 0x%x not removed\n", - oldone->pci_phys_hi); - - /* - * Free up allocated memory - */ - kmem_free(assigned_copy, alen); - kmem_free((caddr_t)assigned, alen); - - return (0); - } - - num_entries = alen / sizeof (pci_regspec_t); - - /* - * Rebuild the assigned-addresses property. - */ - for (i = 0; i < num_entries; i++) { - if (assigned_copy[i].pci_phys_hi != oldone->pci_phys_hi) { - (void) pcicfg_update_assigned_prop(dip, - &assigned_copy[i]); - } - } - - /* - * Free the copy of the original assigned-addresses. - */ - kmem_free(assigned_copy, alen); - - /* - * Don't forget to free up memory from ddi_getlongprop - */ - kmem_free((caddr_t)assigned, alen); - - return (0); -} - -static int -pcicfg_map_phys(dev_info_t *dip, pci_regspec_t *phys_spec, - caddr_t *addrp, ddi_device_acc_attr_t *accattrp, - ddi_acc_handle_t *handlep) -{ - ddi_map_req_t mr; - ddi_acc_hdl_t *hp; - int result; - - *handlep = impl_acc_hdl_alloc(KM_SLEEP, NULL); - hp = impl_acc_hdl_get(*handlep); - hp->ah_vers = VERS_ACCHDL; - hp->ah_dip = dip; - hp->ah_rnumber = 0; - hp->ah_offset = 0; - hp->ah_len = 0; - hp->ah_acc = *accattrp; - - mr.map_op = DDI_MO_MAP_LOCKED; - mr.map_type = DDI_MT_REGSPEC; - mr.map_obj.rp = (struct regspec *)phys_spec; - mr.map_prot = PROT_READ | PROT_WRITE; - mr.map_flags = DDI_MF_KERNEL_MAPPING; - mr.map_handlep = hp; - mr.map_vers = DDI_MAP_VERSION; - - result = ddi_map(dip, &mr, 0, 0, addrp); - - if (result != DDI_SUCCESS) { - impl_acc_hdl_free(*handlep); - *handlep = (ddi_acc_handle_t)NULL; - } else { - hp->ah_addr = *addrp; - } - - return (result); -} - -void -pcicfg_unmap_phys(ddi_acc_handle_t *handlep, pci_regspec_t *ph) -{ - ddi_map_req_t mr; - ddi_acc_hdl_t *hp; - - hp = impl_acc_hdl_get(*handlep); - ASSERT(hp); - - mr.map_op = DDI_MO_UNMAP; - mr.map_type = DDI_MT_REGSPEC; - mr.map_obj.rp = (struct regspec *)ph; - mr.map_prot = PROT_READ | PROT_WRITE; - mr.map_flags = DDI_MF_KERNEL_MAPPING; - mr.map_handlep = hp; - mr.map_vers = DDI_MAP_VERSION; - - (void) ddi_map(hp->ah_dip, &mr, hp->ah_offset, - hp->ah_len, &hp->ah_addr); - - impl_acc_hdl_free(*handlep); - *handlep = (ddi_acc_handle_t)NULL; -} -#ifdef DEBUG -static void -debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3, - uintptr_t a4, uintptr_t a5) -{ - if (pcicfg_debug == 1) { - prom_printf("pcicfg: "); - prom_printf(fmt, a1, a2, a3, a4, a5); - } else - if (pcicfg_debug) - cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5); -} -#endif diff --git a/usr/src/uts/sun4u/io/px/px_lib4u.c b/usr/src/uts/sun4u/io/px/px_lib4u.c index e4d84fc9df..2ee179b796 100644 --- a/usr/src/uts/sun4u/io/px/px_lib4u.c +++ b/usr/src/uts/sun4u/io/px/px_lib4u.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -2025,3 +2026,16 @@ px_cpr_rem_callb(px_t *px_p) { (void) callb_delete(px_p->px_cprcb_id); } + +/*ARGSUSED*/ +int +px_lib_hotplug_init(dev_info_t *dip, void *arg) +{ + return (DDI_ENOTSUP); +} + +/*ARGSUSED*/ +void +px_lib_hotplug_uninit(dev_info_t *dip) +{ +} diff --git a/usr/src/uts/sun4u/montecarlo/Makefile b/usr/src/uts/sun4u/montecarlo/Makefile index fca5358f84..cf62a50216 100644 --- a/usr/src/uts/sun4u/montecarlo/Makefile +++ b/usr/src/uts/sun4u/montecarlo/Makefile @@ -21,9 +21,10 @@ # # # uts/sun4u/montecarlo/Makefile -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# #ident "%Z%%M% %I% %E% SMI" # # This makefile drives the production of all MonteCarlo system @@ -71,12 +72,7 @@ install: $(ROOT_MONTECARLO_DIR) $(USR_MONTECARLO_DIR) \ $(USR_MONTECARLO_SBIN_DIR) \ $(USR_MONTECARLO_LIB_DIR) \ .WAIT $(MONTECARLO_KMODS) $(CLOSED_MONTECARLO_KMODS) \ - pcicfg se.conf ttymux.conf - -pcicfg: $(ROOT_MONTECARLO_MISC_DIR_64) - -@$(RM) $(ROOT_MONTECARLO_MISC_DIR_64)/$@ - $(SYMLINK) ../../../../sun4u/kernel/misc/sparcv9/pcicfg.e \ - $(ROOT_MONTECARLO_MISC_DIR_64)/$@ + se.conf ttymux.conf se.conf: $(ROOT_MONTECARLO_DRV_DIR) -@$(RM) $(ROOT_MONTECARLO_DRV_DIR)/$@ diff --git a/usr/src/uts/sun4u/pcicfg.e/Makefile b/usr/src/uts/sun4u/pcicfg.e/Makefile deleted file mode 100644 index c57597056a..0000000000 --- a/usr/src/uts/sun4u/pcicfg.e/Makefile +++ /dev/null @@ -1,103 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (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 -# -# -# uts/sun4u/pcicfg.e/Makefile -# Copyright 2004 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -#ident "%Z%%M% %I% %E% SMI" -# -# This makefile drives the production of the EFCode Enabled -# PCI Configurator. -# -# sun4u implementation architecture dependent -# - -# -# Path to the base of the uts directory tree (usually /usr/src/uts). -# -UTSBASE = ../.. - -# -# Define the module and object file sets. -# -MODULE = pcicfg.e -OBJECTS = $(PCICFG_E_OBJS:%=$(OBJS_DIR)/%) -LINTS = $(PCICFG_E_OBJS:%.o=$(LINTS_DIR)/%.ln) -ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE) - -# -# Include common rules. -# -include $(UTSBASE)/sun4u/Makefile.sun4u - -# -# Define targets -# -ALL_TARGET = $(BINARY) -LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) - - -# Turn this on once compiler understands v9 in it's backend -#INLINES += $(UTSBASE)/sun4u/io/pcicfg.il - -# -# lint pass one enforcement -# -CFLAGS += $(CCVERBOSE) - -# -# Turn on doubleword alignment for 64 bit registers -# -CFLAGS += -dalign -DPCICFG_INTERPRET_FCODE - -# -# Dependency -LDFLAGS += -dy -Nmisc/busra -Nmisc/hpcsvc -Nmisc/fcpci -Nmisc/fcodem - - -# -# Default build targets. -# -.KEEP_STATE: - -def: $(DEF_DEPS) - -all: $(ALL_DEPS) - -clean: $(CLEAN_DEPS) - -clobber: $(CLOBBER_DEPS) - -lint: $(LINT_DEPS) - -modlintlib: $(MODLINTLIB_DEPS) - -clean.lint: $(CLEAN_LINT_DEPS) - -install: $(INSTALL_DEPS) - -# -# Include common targets. -# -include $(UTSBASE)/sun4u/Makefile.targ diff --git a/usr/src/uts/sun4u/px/Makefile b/usr/src/uts/sun4u/px/Makefile index 972b5a41fb..8364572ae6 100644 --- a/usr/src/uts/sun4u/px/Makefile +++ b/usr/src/uts/sun4u/px/Makefile @@ -22,7 +22,7 @@ # # uts/sun4u/px/Makefile # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -76,7 +76,7 @@ CFLAGS += -dalign # # Dependency # -LDFLAGS += -dy -Nmisc/busra -Nmisc/pcie +LDFLAGS += -dy -Nmisc/busra -Nmisc/pcie -Nmisc/pcihp -Nmisc/pciehpc # # Default build targets. diff --git a/usr/src/uts/sun4u/serengeti/Makefile b/usr/src/uts/sun4u/serengeti/Makefile index a6ccb23ab5..7f532317bc 100644 --- a/usr/src/uts/sun4u/serengeti/Makefile +++ b/usr/src/uts/sun4u/serengeti/Makefile @@ -21,9 +21,10 @@ # # # uts/sun4u/serengeti/Makefile -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# #ident "%Z%%M% %I% %E% SMI" # # This makefile drives the production of the sun4u serengeti platform @@ -86,13 +87,7 @@ install: $(ROOT_SERENGETI_DIR) $(USR_SERENGETI_DIR) \ $(USR_SERENGETI_LIB_DIR) \ $(SERENGETI_CRYPTO_LINKS) \ genassym unix .WAIT \ - $(SERENGETI_KMODS) $(CLOSED_SERENGETI_KMODS) \ - pcicfg - -pcicfg: $(ROOT_PSM_MISC_DIR_64)/pcicfg.e $(ROOT_SERENGETI_MISC_DIR_64) - -@$(RM) $(ROOT_SERENGETI_MISC_DIR_64)/$@ - $(SYMLINK) ../../../../sun4u/kernel/misc/sparcv9/pcicfg.e \ - $(ROOT_SERENGETI_MISC_DIR_64)/$@ + $(SERENGETI_KMODS) $(CLOSED_SERENGETI_KMODS) genassym unix $(SERENGETI_KMODS): FRC @cd $@; pwd; $(MAKE) $(NO_STATE) $(TARGET) diff --git a/usr/src/uts/sun4u/starcat/Makefile b/usr/src/uts/sun4u/starcat/Makefile index a22852614f..afdb3d7244 100644 --- a/usr/src/uts/sun4u/starcat/Makefile +++ b/usr/src/uts/sun4u/starcat/Makefile @@ -22,9 +22,10 @@ # #pragma ident "%Z%%M% %I% %E% SMI" # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# # This makefile drives the production of the sun4u starcat platform # module. # @@ -86,12 +87,7 @@ install: $(ROOT_STARCAT_DIR) $(USR_STARCAT_DIR) \ $(USR_STARCAT_LIB_DIR) \ $(STARCAT_CRYPTO_LINKS) \ genassym unix .WAIT $(STARCAT_CPU_KMODS) \ - $(STARCAT_KMODS) $(CLOSED_STARCAT_KMODS) pcicfg - -pcicfg: $(ROOT_PSM_MISC_DIR_64)/pcicfg.e $(ROOT_STARCAT_MISC_DIR_64) - -@$(RM) $(ROOT_STARCAT_MISC_DIR_64)/$@ - $(SYMLINK) ../../../../sun4u/kernel/misc/sparcv9/pcicfg.e \ - $(ROOT_STARCAT_MISC_DIR_64)/$@ + $(STARCAT_KMODS) $(CLOSED_STARCAT_KMODS) genassym unix $(STARCAT_KMODS) $(STARCAT_CPU_KMODS): FRC @cd $@; pwd; $(MAKE) $(NO_STATE) $(TARGET) diff --git a/usr/src/uts/sun4u/sys/Makefile b/usr/src/uts/sun4u/sys/Makefile index 21c7f3eca7..0de14065cc 100644 --- a/usr/src/uts/sun4u/sys/Makefile +++ b/usr/src/uts/sun4u/sys/Makefile @@ -50,6 +50,7 @@ SUN4_HDRS= \ eeprom.h \ errclassify.h \ fcode.h \ + fc_plat.h \ idprom.h \ intr.h \ intreg.h \ @@ -80,7 +81,6 @@ HDRS= \ envctrl_ue250.h \ envctrl_ue450.h \ gpio_87317.h \ - fc_plat.h \ iocache.h \ iommu.h \ machasi.h \ diff --git a/usr/src/uts/sun4u/sys/fc_plat.h b/usr/src/uts/sun4u/sys/fc_plat.h deleted file mode 100644 index 77211e30f3..0000000000 --- a/usr/src/uts/sun4u/sys/fc_plat.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (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 2005 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#ifndef _SYS_FC_PLAT_H -#define _SYS_FC_PLAT_H - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Platform specific definitions for the fcode interpreter and driver. - * Define the cell size for the implementation. - * - * These definitions are appropriate for SPARC V9. - */ - -/* - * The cell size is based on the cell size of the underlying "firmware" - * implementation. NB: FCode is really a 32-bit language, but we still - * define our interfaces in terms of the underlying cell size. - */ - -typedef unsigned long long fc_cell_t; - -/* - * common typedef for phandles accross the interface. - */ -typedef uint32_t fc_phandle_t; - -/* - * Handy macros for converting from an fc_cell_t to an integral type - * These are useful because arguments and results are always passed - * in an array of fc_cell_t's. - */ - -#define fc_ptr2cell(p) ((fc_cell_t)((uintptr_t)((void *)(p)))) -#define fc_int2cell(i) ((fc_cell_t)((int)(i))) -#define fc_uint2cell(u) ((fc_cell_t)((unsigned int)(u))) -#define fc_uint32_t2cell(u) ((fc_cell_t)((unsigned int)((uint32_t)(u)))) -#define fc_uint16_t2cell(w) ((fc_cell_t)((unsigned int)((uint16_t)(w)))) -#define fc_uint8_t2cell(b) ((fc_cell_t)((unsigned int)((uint8_t)(b)))) -#define fc_size2cell(u) ((fc_cell_t)((size_t)(u))) -#define fc_ssize2cell(i) ((fc_cell_t)((ssize_t)(i))) -#define fc_phandle2cell(ph) ((fc_cell_t)((unsigned int)((phandle_t)(ph)))) -#define fc_dnode2cell(d) ((fc_cell_t)((unsigned int)((pnode_t)(d)))) -#define fc_ull2cell_high(ll) (0LL) -#define fc_ull2cell_low(ll) ((fc_cell_t)(ll)) -#define fc_uintptr2cell(i) ((fc_cell_t)((uintptr_t)(i))) -#define fc_uchar2cell(c) ((fc_cell_t)((unsigned char)(c))) -#define fc_ushort2cell(w) ((fc_cell_t)((unsigned short)(w))) -#define fc_ihandle2cell(h) ((fc_cell_t)((fc_ihandle_t)(h))) - -#define fc_cell2ptr(p) ((void *)((fc_cell_t)(p))) -#define fc_cell2int(i) ((int)((fc_cell_t)(i))) -#define fc_cell2uint(u) ((unsigned int)((fc_cell_t)(u))) -#define fc_cell2uint32_t(u) ((uint32_t)((fc_cell_t)(u))) -#define fc_cell2uint16_t(w) ((uint16_t)((fc_cell_t)(w))) -#define fc_cell2uint8_t(b) ((uint8_t)((fc_cell_t)(b))) -#define fc_cell2size(u) ((size_t)((fc_cell_t)(u))) -#define fc_cell2ssize(i) ((ssize_t)((fc_cell_t)(i))) -#define fc_cell2phandle(ph) ((phandle_t)((fc_cell_t)(ph))) -#define fc_cell2dnode(d) ((pnode_t)((fc_cell_t)(d))) -#define fc_cells2ull(h, l) ((unsigned long long)(fc_cell_t)(l)) -#define fc_cell2uintptr(i) ((uintptr_t)((fc_cell_t)(i))) -#define fc_cell2uchar(c) ((unsigned char)(fc_cell_t)(c)) -#define fc_cell2ushort(w) ((unsigned short)(fc_cell_t)(w)) -#define fc_cell2ihandle(h) ((fc_ihandle_t)(fc_cell_t)(h)) - -#ifdef __cplusplus -} -#endif - -#endif /* _SYS_FC_PLAT_H */ diff --git a/usr/src/uts/sun4v/io/px/px_lib4v.c b/usr/src/uts/sun4v/io/px/px_lib4v.c index 298e832aed..ed5b463c84 100644 --- a/usr/src/uts/sun4v/io/px/px_lib4v.c +++ b/usr/src/uts/sun4v/io/px/px_lib4v.c @@ -20,7 +20,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -40,6 +40,7 @@ #include #include #include +#include #include "px_lib4v.h" #include "px_err.h" @@ -72,6 +73,14 @@ px_lib_dev_init(dev_info_t *dip, devhandle_t *dev_hdl) ddi_prop_free(rp); + /* + * hotplug implementation requires this property to be associated with + * any indirect PCI config access services + */ + (void) ddi_prop_update_int(makedevice(ddi_driver_major(dip), + PCIHP_AP_MINOR_NUM(ddi_get_instance(dip), PCIHP_DEVCTL_MINOR)), dip, + PCI_BUS_CONF_MAP_PROP, 1); + DBG(DBG_ATTACH, dip, "px_lib_dev_init: dev_hdl 0x%llx\n", *dev_hdl); return (DDI_SUCCESS); @@ -83,6 +92,10 @@ px_lib_dev_fini(dev_info_t *dip) { DBG(DBG_DETACH, dip, "px_lib_dev_fini: dip 0x%p\n", dip); + (void) ddi_prop_remove(makedevice(ddi_driver_major(dip), + PCIHP_AP_MINOR_NUM(ddi_get_instance(dip), PCIHP_DEVCTL_MINOR)), dip, + PCI_BUS_CONF_MAP_PROP); + return (DDI_SUCCESS); } @@ -1824,6 +1837,19 @@ px_fab_set(px_t *px_p, pcie_req_id_t bdf, uint16_t offset, (bdf << PX_RA_BDF_SHIFT), offset, 4, wdata); } +/*ARGSUSED*/ +int +px_lib_hotplug_init(dev_info_t *dip, void *arg) +{ + return (DDI_ENOTSUP); +} + +/*ARGSUSED*/ +void +px_lib_hotplug_uninit(dev_info_t *dip) +{ +} + /* Dummy cpr add callback */ /*ARGSUSED*/ void diff --git a/usr/src/uts/sun4v/px/Makefile b/usr/src/uts/sun4v/px/Makefile index ee1eb57e5d..0ae8987576 100644 --- a/usr/src/uts/sun4v/px/Makefile +++ b/usr/src/uts/sun4v/px/Makefile @@ -22,7 +22,7 @@ # # uts/sun4v/px/Makefile # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -78,7 +78,7 @@ CFLAGS += -dalign # Dependency # -LDFLAGS += -dy -Nmisc/busra -Nmisc/pcie +LDFLAGS += -dy -Nmisc/busra -Nmisc/pcie -Nmisc/pciehpc -Nmisc/pcihp # Default build targets. # -- cgit v1.2.3