summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorjohnny <none@none>2005-11-10 12:33:07 -0800
committerjohnny <none@none>2005-11-10 12:33:07 -0800
commit70025d765b044c6d8594bb965a2247a61e991a99 (patch)
treeecf0115912221f8be4400ca9bf4d04802b8e0355 /usr/src
parent4610e4a00999c6d2291b3fc263926b890ec500a5 (diff)
downloadillumos-joyent-70025d765b044c6d8594bb965a2247a61e991a99.tar.gz
PSARC 2005/375 PCI Hotplug Extensions for PCIe
PSARC 2002/315 cPCI Autoconfiguration Support 6331880 PCI Hot-Plug Framework Extension for PCIe 6331883 cPCI autoconfiguration support 6326583 PCIE support for X86 6339777 cleanup of references to obsoleted interface, pcihp_cb_ops, in pci/pci_pci nexus (x86) 6341189 assertion panic on Dell PowerEdge 6850 in apic.c: apic_irq_table[irqno]->airq_intin_no != ipin
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/Makefile.lint1
-rw-r--r--usr/src/cmd/Makefile1
-rw-r--r--usr/src/cmd/biosdev/biosdev.c3
-rwxr-xr-xusr/src/cmd/pcidr/Makefile79
-rwxr-xr-xusr/src/cmd/pcidr/Makefile.com52
-rw-r--r--usr/src/cmd/pcidr/etc/Makefile50
-rw-r--r--usr/src/cmd/pcidr/etc/SUNW,EC_dr,ESC_dr_req,sysevent.conf29
-rwxr-xr-xusr/src/cmd/pcidr/pcidr.c656
-rw-r--r--usr/src/cmd/pcidr/pcidr.h109
-rw-r--r--usr/src/cmd/pcidr/pcidr_common.c351
-rwxr-xr-xusr/src/cmd/pcidr/plugins/Makefile52
-rwxr-xr-xusr/src/cmd/pcidr/plugins/Makefile.targ37
-rwxr-xr-xusr/src/cmd/pcidr/plugins/default/Makefile93
-rw-r--r--usr/src/cmd/pcidr/plugins/default/pcidr_cfga.c330
-rw-r--r--usr/src/cmd/pcidr/plugins/default/pcidr_cfga.h57
-rw-r--r--usr/src/cmd/pcidr/plugins/default/pcidr_plugin.c144
-rw-r--r--usr/src/lib/cfgadm_plugins/pci/common/cfga.c4
-rw-r--r--usr/src/pkgdefs/SUNWcakr.i/prototype_com7
-rw-r--r--usr/src/pkgdefs/SUNWcsr/prototype_com1
-rw-r--r--usr/src/pkgdefs/SUNWcsu/prototype_com3
-rw-r--r--usr/src/uts/common/Makefile.files2
-rw-r--r--usr/src/uts/common/Makefile.rules7
-rw-r--r--usr/src/uts/common/io/busra.c52
-rw-r--r--usr/src/uts/common/io/hotplug/pcicfg/pcicfg.c1220
-rw-r--r--usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c1764
-rw-r--r--usr/src/uts/common/io/hotplug/pcihp/pcihp.c148
-rw-r--r--usr/src/uts/common/io/pci_pci/pci_pci.c70
-rw-r--r--usr/src/uts/common/sys/hotplug/hpctrl.h7
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pcicfg.h22
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pciehpc.h56
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h217
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pcihp.h10
-rw-r--r--usr/src/uts/common/sys/hotplug/pci/pcihp_impl.h56
-rw-r--r--usr/src/uts/common/sys/pci.h19
-rw-r--r--usr/src/uts/common/sys/pcie.h80
-rw-r--r--usr/src/uts/i86pc/Makefile.files10
-rw-r--r--usr/src/uts/i86pc/Makefile.i86pc4
-rw-r--r--usr/src/uts/i86pc/Makefile.rules14
-rw-r--r--usr/src/uts/i86pc/io/acpica/osl.c3
-rw-r--r--usr/src/uts/i86pc/io/pci/pci.c729
-rw-r--r--usr/src/uts/i86pc/io/pci/pci_boot.c134
-rw-r--r--usr/src/uts/i86pc/io/pci/pci_common.c761
-rw-r--r--usr/src/uts/i86pc/io/pci/pci_common.h66
-rw-r--r--usr/src/uts/i86pc/io/pci/pci_tools.c6
-rw-r--r--usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_acpi.c865
-rw-r--r--usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_acpi.h107
-rw-r--r--usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_ck804.c854
-rw-r--r--usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_ck804.h100
-rw-r--r--usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_x86.c64
-rw-r--r--usr/src/uts/i86pc/io/pciex/inc.flg120
-rw-r--r--usr/src/uts/i86pc/io/pciex/npe.c719
-rw-r--r--usr/src/uts/i86pc/io/pciex/npe_misc.c131
-rw-r--r--usr/src/uts/i86pc/io/pciex/pcie_ck804_boot.c224
-rw-r--r--usr/src/uts/i86pc/io/pciex/pcie_ck804_boot.h75
-rw-r--r--usr/src/uts/i86pc/io/pciex/pcie_error.c550
-rw-r--r--usr/src/uts/i86pc/io/pciex/pcie_error.h54
-rw-r--r--usr/src/uts/i86pc/io/pciex/pcie_pci.c1147
-rw-r--r--usr/src/uts/i86pc/io/pciex/pcie_pci.conf36
-rw-r--r--usr/src/uts/i86pc/io/pcplusmp/apic.c16
-rw-r--r--usr/src/uts/i86pc/io/pcplusmp/apic_introp.c33
-rw-r--r--usr/src/uts/i86pc/io/psm/uppc.c3
-rw-r--r--usr/src/uts/i86pc/npe/Makefile93
-rw-r--r--usr/src/uts/i86pc/pcie_pci/Makefile96
-rw-r--r--usr/src/uts/i86pc/pciehpc/Makefile88
-rw-r--r--usr/src/uts/intel/ia32/ml/modstubs.s11
-rw-r--r--usr/src/uts/intel/io/vgatext/vgatext.c2
-rw-r--r--usr/src/uts/intel/os/driver_aliases9
-rw-r--r--usr/src/uts/intel/os/name_to_major2
-rw-r--r--usr/src/uts/intel/sys/acpi/acpi_pci.h77
69 files changed, 11950 insertions, 1042 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint
index 99f32c89ea..31a289cbda 100644
--- a/usr/src/Makefile.lint
+++ b/usr/src/Makefile.lint
@@ -194,6 +194,7 @@ COMMON_SUBDIRS = \
cmd/pathchk \
cmd/pax \
cmd/pbind \
+ cmd/pcidr \
cmd/pfexec \
cmd/pgrep \
cmd/picl/picld \
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index c3f7131069..2c246deaa9 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -278,6 +278,7 @@ COMMON_SUBDIRS= \
pathchk \
pax \
pbind \
+ pcidr \
pcitool \
pcmciad \
pfexec \
diff --git a/usr/src/cmd/biosdev/biosdev.c b/usr/src/cmd/biosdev/biosdev.c
index 42c943c31b..0f77213c4f 100644
--- a/usr/src/cmd/biosdev/biosdev.c
+++ b/usr/src/cmd/biosdev/biosdev.c
@@ -215,7 +215,8 @@ matchpcibdf(di_node_t node, void *arg)
len = di_prop_lookup_strings(DDI_DEV_T_ANY, parentnode,
"device_type", (char **)&devtype);
- if ((len <= 0) || (strcmp(devtype, "pci") != 0))
+ if ((len <= 0) ||
+ ((strcmp(devtype, "pci") != 0) && (strcmp(devtype, "pciex") != 0)))
return (DI_WALK_CONTINUE);
len = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg",
diff --git a/usr/src/cmd/pcidr/Makefile b/usr/src/cmd/pcidr/Makefile
new file mode 100755
index 0000000000..756f10afe6
--- /dev/null
+++ b/usr/src/cmd/pcidr/Makefile
@@ -0,0 +1,79 @@
+#
+# 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.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/pcidr/Makefile
+#
+
+.PARALLEL:
+#############################################################################
+
+PROGOBJECTS = pcidr.o pcidr_common.o
+PROG = pcidr
+
+SUBDIRS = plugins etc
+
+include $(SRC)/cmd/Makefile.cmd
+include Makefile.com
+#############################################################################
+
+# used by the lint_SRCS rule
+#
+SRCS = $(PROGOBJECTS:%.o=%.c)
+CLOBBERFILES += $(PROGOBJECTS)
+
+LDLIBS += -lnvpair
+LINTFLAGS += -erroff=E_NAME_DEF_NOT_USED2
+
+all := TARGET = all
+install := TARGET = install
+clean := TARGET = clean
+clobber := TARGET = clobber
+lint := TARGET = lint
+
+.KEEP_STATE:
+#############################################################################
+
+all: $(PROG)
+clean:
+clobber:
+install: $(ROOTCMD)
+lint: lint_SRCS
+
+$(ROOTCMD): all
+
+$(PROG): $(PROGOBJECTS)
+ $(LINK.c) -o $(PROG) $(PROGOBJECTS) $(LDLIBS)
+ $(POST_PROCESS)
+
+all clean clobber lint: $(SUBDIRS)
+install: .WAIT $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+FRC:
+
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/cmd/pcidr/Makefile.com b/usr/src/cmd/pcidr/Makefile.com
new file mode 100755
index 0000000000..257987bb10
--- /dev/null
+++ b/usr/src/cmd/pcidr/Makefile.com
@@ -0,0 +1,52 @@
+#
+# 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.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/pcidr/Makefile.com
+#
+# to be included AFTER cmd/Makefile.cmd or lib/Makefile.lib
+
+TOP = $(SRC)/cmd/pcidr
+INSTALLDIR = /usr/lib/pci
+
+#############################################################################
+### used by macros in Makefile.cmd/lib
+
+HDRDIR = $(TOP)
+HDRS_SH = cd $(HDRDIR); ls *.h
+HDRS = $(HDRS_SH:sh)
+
+ROOTLIBDIR = $(ROOT)/$(INSTALLDIR)
+ROOTCMDDIR = $(ROOTLIBDIR)
+#############################################################################
+
+CPPFLAGS += -D_REENTRANT -I$(HDRDIR)
+
+# Note that LDFLAGS is NOT used in the build rules for shared objects!
+# LDLIBS is limited to -L and -l options; all other options must be added to
+# DYNFLAGS for shared objects
+
+LDLIBS += -lc
diff --git a/usr/src/cmd/pcidr/etc/Makefile b/usr/src/cmd/pcidr/etc/Makefile
new file mode 100644
index 0000000000..fee5fec036
--- /dev/null
+++ b/usr/src/cmd/pcidr/etc/Makefile
@@ -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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/pcidr/etc/Makefile
+#
+
+.PARALLEL:
+
+include $(SRC)/cmd/Makefile.cmd
+
+CONF = SUNW,EC_dr,ESC_dr_req,sysevent.conf
+CONFDIR = $(ROOTETC)/sysevent/config
+
+# utilize the predefined install target patterns in cmd/Makefile.targ by
+# overriding ROOTCMDDIR
+#
+ROOTCMDDIR = $(CONFDIR)
+FILEMODE = 0644
+
+.KEEP_STATE:
+
+all clean clobber lint:
+
+install: $(CONF:%=$(CONFDIR)/%)
+
+include $(SRC)/cmd/Makefile.targ
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
new file mode 100644
index 0000000000..cb151ebf2a
--- /dev/null
+++ b/usr/src/cmd/pcidr/etc/SUNW,EC_dr,ESC_dr_req,sysevent.conf
@@ -0,0 +1,29 @@
+#
+# 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
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 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
diff --git a/usr/src/cmd/pcidr/pcidr.c b/usr/src/cmd/pcidr/pcidr.c
new file mode 100755
index 0000000000..1940f604d2
--- /dev/null
+++ b/usr/src/cmd/pcidr/pcidr.c
@@ -0,0 +1,656 @@
+/*
+ * 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"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <sys/systeminfo.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/sysevent/dr.h>
+#include <syslog.h>
+#include <libnvpair.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <pcidr.h>
+
+/*
+ * pcidr takes in arguments of the form specified in the help() routine
+ * including a set of name=value pairs, then looks up a plugin (shared object)
+ * based on <plugin_paths> and however find_plugin() operates. The entry
+ * point of the plugin is <PCIDR_PLUGIN_SYM> and has the type
+ * <pcidr_plugin_t>. Plugins must use the <PCIDR_PLUGIN_PROTO> macro to
+ * define their entry point.
+ *
+ * The name=value arguments are intended to be used as a mechanism to pass
+ * arbitrary sysevent attributes using the macro expansion capability provided
+ * by the syseventd SLM processing sysevent.conf files (i.e. specifying
+ * "$attribute" arguments for the handler in a .conf file entry). They are
+ * converted into an nvlist_t (see libnvpair(3LIB)) by converting the values
+ * of recognized names into appropriate types using pcidr_name2type() and
+ * leaving all others as string types. Because pcidr is used as a sysevent.conf
+ * handler, the format of the value string for non-string attributes in each
+ * name=value argument must match that used by the syseventd macro capability
+ *
+ * The plugin will be passed this (nvlist_t *) along with a (pcidr_opt_t *) arg
+ * for other options. While pcidr does some basic checking of arguments, it
+ * leaves any name=value check (after conversion) up to each plugin. Note
+ * that pcidr_check_attrs() is used by the default plugin and can be used by
+ * any plugin that support the same or a superset of its attributes. If the
+ * default plugin supports additional publishers, it should be updated in
+ * pcidr_check_attrs().
+ *
+ * See help() for an example of how pcidr can be specified in a sysevent.conf
+ * file.
+ */
+
+/*
+ * plugin search paths (searched in order specified);
+ * macros begin MACRO_BEGTOK and end with MACRO_ENDTOK;
+ *
+ * be sure to update parse_path() and its support functions whenever macros
+ * are updated e.g. si_name2cmd(), as well as substring tokens (prefix or
+ * suffix) used to recognize different types of macros e.g. SI_MACRO
+ *
+ * NOTE: if plugin search algorithm is changed starting with find_plugin(),
+ * please update documentation here.
+ *
+ * macros:
+ * SI_PLATFORM = cmd of same name in sysinfo(2)
+ * SI_MACHINE = cmd of same name in sysinfo(2)
+ */
+#define MACRO_BEGTOK "${"
+#define MACRO_ENDTOK "}"
+#define SI_MACRO "SI_"
+
+static char *plugin_paths[] = {
+ "/usr/platform/${SI_PLATFORM}/lib/pci/" PCIDR_PLUGIN_NAME,
+ "/usr/platform/${SI_MACHINE}/lib/pci/" PCIDR_PLUGIN_NAME,
+ "/usr/lib/pci/" PCIDR_PLUGIN_NAME,
+};
+static int plugin_paths_len = sizeof (plugin_paths) / sizeof (plugin_paths[0]);
+
+
+static nvlist_t *nvlistp = NULL; /* attribute list */
+
+typedef struct {
+ char *name;
+ char *beg;
+ char *end;
+} macro_list_t;
+static macro_list_t *parse_macros(char *const, int *);
+static void free_macros(macro_list_t *, int);
+static char *parse_path(char *const);
+static void help();
+static void exiter();
+static char *find_plugin(nvlist_t *);
+static int do_plugin(char *, nvlist_t *, pcidr_opt_t *);
+static int nvadd(nvlist_t *, char *, char *, data_type_t);
+static nvlist_t *parse_argv_attr(int, char **, int *);
+static int si_name2cmd(char *);
+
+
+static void
+help()
+{
+/* since the handler is not public, we don't expose its usage normally */
+#ifdef DEBUG
+ (void) printf(
+"%s [-h] [-s] [-v <level>] [-l <log_file>] <attributes>\n"
+" -h help\n"
+"\n"
+" -s turn OFF messages to the syslog (use syslog by default)\n"
+"\n"
+" -v verbose mode; <level> range is %d..%d; default is %d\n"
+"\n"
+" -l also log messages to <log_file> (in addition to using\n"
+" the syslog if that option is not disabled);\n"
+" if <log_file> is '-', stdout is used\n"
+"\n"
+" <attributes>\n"
+" whitespace seperated strings of <name>=<value> pairs\n"
+"\n"
+"Example 1 (command line):\n"
+" %s -s -v%d -l- \\\n"
+" class=EC_dr subclass=ESC_dr_req publisher=pcie_pci \\\n"
+" dr_request_type=dr_request_outgoing_resource \\\n"
+" dr_ap_id=/devices/foo/bar\n"
+"\n"
+"Example 2 (/etc/sysevent/config/SUNW,sysevent.conf entry):\n"
+" EC_dr ESC_dr_req SUNW pcie_pci - - - %s -v%d -l/tmp/log \\\n"
+" class=$class subclass=$subclass publisher=$publisher \\\n"
+" dr_request_type=$dr_request_type\\\n"
+" dr_ap_id=$dr_ap_id\n"
+"\n",
+ prg, MIN_DLVL, MAX_DLVL, dlvl,
+ prg, MAX_DLVL, /* Example 1 */
+ prg, DWARN); /* Example 2 */
+#endif
+}
+
+
+/*
+ * will convert <value> from a string to the type indicated by <type>
+ * and will add it with <name> to nvlist_t <listp>; function returns the same
+ * value as nvlist_add_*()
+ */
+static int
+nvadd(nvlist_t *listp, char *name, char *value, data_type_t type)
+{
+ char *fn = "nvadd";
+ int rv = 0;
+
+ switch (type) {
+ case DATA_TYPE_STRING:
+ rv = nvlist_add_string(listp, name, value);
+ if (rv != 0) {
+ dprint(DDEBUG, "%s: nvlist_add_string() failed: "
+ "name = %s, value = %s, rv = %d\n",
+ fn, name, value, rv);
+ }
+ break;
+ /*
+ * Conversion must support whatever string format syseventd uses for
+ * its .conf macros; in addition, minimum types supported must match
+ * those for pcidr_name2type()
+ */
+ default:
+ dprint(DDEBUG, "%s: unsupported type: name = %s, value = %s, "
+ "type = 0x%x\n", fn, name, value, (int)type);
+ rv = EINVAL;
+ }
+
+ return (rv);
+}
+
+
+/*
+ * argc: length of argv
+ * argv: each string starting from index <argip> has the format "name=value"
+ * argip: starting index in <argv>; also used to return ending index
+ *
+ * return: allocated nvlist on success, exits otherwise
+ *
+ * recognized names will have predetermined types, while all others will have
+ * values of type string
+ */
+static nvlist_t *
+parse_argv_attr(int argc, char **argv, int *argip)
+{
+ char *fn = "parse_argv_attr";
+ int rv, i;
+ nvlist_t *attrlistp = NULL;
+ char *eqp, *name, *value;
+ data_type_t type;
+
+ assert(*argip < argc);
+
+ rv = nvlist_alloc(&attrlistp, NV_UNIQUE_NAME_TYPE, 0);
+ if (rv != 0) {
+ dprint(DDEBUG, "%s: nvlist_alloc() failed: rv = %d\n", fn, rv);
+ goto ERR;
+ }
+
+ for (i = *argip; i < argc; i++) {
+ eqp = strchr(argv[i], '=');
+ if (eqp == NULL)
+ goto ERR_ARG;
+ *eqp = '\0';
+ name = argv[i];
+ value = eqp;
+ value++;
+ if (*name == '\0' || *value == '\0')
+ goto ERR_ARG;
+
+ if (pcidr_name2type(name, &type) != 0)
+ type = DATA_TYPE_STRING;
+
+ rv = nvadd(attrlistp, name, value, type);
+ if (rv != 0) {
+ dprint(DDEBUG, "%s: nvadd() failed: attribute \"%s\", "
+ "value = %s, type = %d, rv = %d\n",
+ fn, name, value, (int)type, rv);
+ goto ERR;
+ }
+ *eqp = '=';
+ }
+
+ *argip = i;
+ return (attrlistp);
+
+ /*NOTREACHED*/
+ERR_ARG:
+ if (eqp != NULL)
+ *eqp = '=';
+ dprint(DDEBUG, "%s: bad attribute argv[%d]: \"%s\"\n", fn, i, argv[i]);
+ERR:
+ if (attrlistp != NULL)
+ nvlist_free(attrlistp);
+ return (NULL);
+}
+
+
+static struct {
+ int cmd;
+ char *name;
+} si_cmd_nametab[] = {
+ SI_PLATFORM, "SI_PLATFORM",
+ SI_MACHINE, "SI_MACHINE",
+};
+static int si_cmd_nametab_len =
+ sizeof (si_cmd_nametab) / sizeof (si_cmd_nametab[0]);
+
+static int
+si_name2cmd(char *name)
+{
+ int i;
+
+ for (i = 0; i < si_cmd_nametab_len; i++) {
+ if (strcmp(name, si_cmd_nametab[i].name) == 0)
+ return (si_cmd_nametab[i].cmd);
+ }
+ return (-1);
+}
+
+
+/*
+ * finds occurences of substrings surrounded (delimited) by MACRO_BEGTOK and
+ * MACRO_ENDTOK in <str>;
+ * returns an allocated array of macro_list_t whose length is
+ * returned through <lenp>; array entries will be in order of the occurrence;
+ * else returns NULL if none are found
+ *
+ * macro_list_t members:
+ * char *name = allocated string containing name without macro delimiters
+ * char *beg = location in <str> at _first char_ of MACRO_BEGTOK
+ * char *end = location in <str> at _last char_ of MACRO_ENDTOK
+ */
+static macro_list_t *
+parse_macros(char *const str, int *lenp)
+{
+ char *beg, *end;
+ macro_list_t *lp;
+ size_t size;
+ int i, begtok_len, endtok_len;
+
+ begtok_len = strlen(MACRO_BEGTOK);
+ endtok_len = strlen(MACRO_ENDTOK);
+
+ /* count all occurrences */
+ for (beg = str, i = 0; beg != NULL; i++) {
+ beg = strstr(beg, MACRO_BEGTOK);
+ if (beg == NULL)
+ break;
+ end = strstr(beg + begtok_len, MACRO_ENDTOK);
+ if (end == NULL)
+ break;
+ beg = end + endtok_len;
+ }
+ if (i <= 0)
+ return (NULL);
+
+ *lenp = i;
+ lp = pcidr_malloc(sizeof (macro_list_t) * i);
+
+ for (beg = str, i = 0; i < *lenp; i++) {
+ beg = strstr(beg, MACRO_BEGTOK);
+ assert(beg != NULL);
+ end = strstr(beg + begtok_len, MACRO_ENDTOK);
+ assert(end != NULL);
+
+ size = (end - (beg + begtok_len)) + 1;
+ lp[i].name = pcidr_malloc(size * sizeof (char));
+ (void) strlcpy(lp[i].name, beg + begtok_len, size);
+
+ lp[i].beg = beg;
+ lp[i].end = (end + endtok_len) - 1;
+
+ beg = end + endtok_len;
+ }
+
+ return (lp);
+}
+
+static void
+free_macros(macro_list_t *lp, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ free(lp[i].name);
+ free(lp);
+}
+
+
+/*
+ * evaluates any macros in <opath> and returns allocated string on success;
+ * else NULL
+ */
+static char *
+parse_path(char *const opath)
+{
+ char *fn = "parse_path";
+ char buf[MAXPATHLEN + 1];
+ int bufsize = sizeof (buf) / sizeof (buf[0]);
+ char sibuf[257];
+ int sibufsize = sizeof (sibuf) / sizeof (sibuf[0]);
+ macro_list_t *lp;
+ char *path, *pathp, *pathend;
+ int rv, i, lplen, si_cmd, pathlen, okmacro, si_macro_len;
+ size_t sz;
+
+ /*
+ * make a copy so we can modify it for easier parsing;
+ * lp members will refer to the copy
+ */
+ path = strdup(opath);
+ lp = parse_macros(path, &lplen);
+ if (lp == NULL)
+ return (path);
+
+ rv = 0;
+ si_macro_len = strlen(SI_MACRO);
+ pathlen = strlen(path);
+ pathend = &path[pathlen - 1];
+ pathp = path;
+ buf[0] = '\0';
+ for (i = 0; i < lplen; i++) {
+ lp[i].beg[0] = '\0';
+ sz = strlcat(buf, pathp, bufsize);
+ assert(sz < bufsize);
+
+ okmacro = 0;
+ if (strncmp(lp[i].name, SI_MACRO, si_macro_len) == 0) {
+ si_cmd = si_name2cmd(lp[i].name);
+ assert(si_cmd >= 0);
+
+ rv = sysinfo(si_cmd, sibuf, sibufsize);
+ if (rv < 0) {
+ dprint(DDEBUG, "%s: sysinfo cmd %d failed: "
+ "errno = %d\n", fn, si_cmd, errno);
+ goto OUT;
+ }
+
+ sz = strlcat(buf, sibuf, bufsize);
+ assert(sz < bufsize);
+ okmacro = 1;
+ }
+ /* check for unrecognized macros */
+ assert(okmacro);
+ pathp = lp[i].end + 1;
+ }
+
+ rv = 0;
+ if (pathp < pathend) {
+ sz = strlcat(buf, pathp, bufsize);
+ assert(sz < bufsize);
+ }
+OUT:
+ free_macros(lp, lplen);
+ free(path);
+ if (rv == 0)
+ return (strdup(buf));
+ return (NULL);
+}
+
+
+/*
+ * returns allocated string containing plugin path which caller must free;
+ * else NULL; <attrlistp> is for future use if attributes can be used to
+ * determin plugin
+ */
+/*ARGSUSED*/
+static char *
+find_plugin(nvlist_t *attrlistp)
+{
+ char *fn = "find_plugin";
+ char *path = NULL;
+ int i, rv;
+ struct stat statbuf;
+
+ for (i = 0; i < plugin_paths_len; i++) {
+ path = parse_path(plugin_paths[i]);
+ if (path == NULL) {
+ dprint(DDEBUG, "%s: error parsing path %s\n", fn,
+ path);
+ return (NULL);
+ }
+
+ rv = stat(path, &statbuf);
+ if (rv < 0)
+ dprint(DDEBUG, "%s: stat on %s failed: "
+ "errno = %d\n", fn, path, errno);
+ else if ((statbuf.st_mode & S_IFMT) != S_IFREG)
+ dprint(DDEBUG, "%s: %s is not a regular "
+ "file\n", fn, path);
+ else
+ return (path);
+
+ free(path);
+ }
+ return (NULL);
+}
+
+
+/*
+ * load plugin specified by <path> and pass the proceeding arguments
+ * to the plugin interface; returns 0 on success (likewise for
+ * the plugin function)
+ */
+static int
+do_plugin(char *path, nvlist_t *attrlistp, pcidr_opt_t *optp)
+{
+ char *fn = "do_plugin";
+ int rv;
+ void *dlh;
+ sigset_t set, oset;
+ pcidr_plugin_t fp;
+
+ dlh = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
+ if (dlh == NULL) {
+ dprint(DDEBUG, "%s: dlopen() failed: %s\n", fn, dlerror());
+ rv = EINVAL;
+ goto OUT;
+ }
+
+ if (sigfillset(&set) != 0) {
+ dprint(DDEBUG, "%s: sigfillset() failed: errno = %d\n", fn,
+ errno);
+ rv = errno;
+ goto OUT;
+ }
+ if (sigprocmask(SIG_BLOCK, &set, &oset) != 0) {
+ dprint(DDEBUG, "%s: blocking signals with sigprocmask() "
+ "failed: errno = %d\n", fn, errno);
+ rv = errno;
+ goto OUT;
+ }
+
+ fp = (pcidr_plugin_t)dlsym(dlh, PCIDR_PLUGIN_SYMSTR);
+ if (fp == NULL) {
+ dprint(DDEBUG, "%s: dlsym() failed: %s\n", fn, dlerror());
+ rv = EINVAL;
+ goto OUT;
+ }
+ rv = fp(attrlistp, optp);
+ if (rv != 0)
+ dprint(DDEBUG, "%s: %s() failed: rv = %d\n", fn,
+ PCIDR_PLUGIN_SYMSTR, rv);
+
+ if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0) {
+ dprint(DDEBUG, "%s: unblocking signals with sigprocmask() "
+ "failed: errno = %d\n", fn, errno);
+ rv = errno;
+ goto OUT;
+ }
+OUT:
+ if (dlh != NULL)
+ (void) dlclose(dlh);
+ return (rv);
+}
+
+
+static void
+exiter()
+{
+ extern FILE *dfp;
+
+ if (nvlistp != NULL)
+ nvlist_free(nvlistp);
+ if (dfp != NULL)
+ (void) fclose(dfp);
+#ifdef DEBUG
+ closelog();
+#endif
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int rv, argi;
+ char *dfile = NULL, *plugin_path = NULL;
+ struct stat statbuf;
+ pcidr_opt_t plugin_opt;
+ char *optstr = NULL;
+
+ extern char *optarg;
+ extern int optind, optopt;
+ int c;
+
+ /*CONSTCOND*/
+ assert(MIN_DLVL == 0);
+ /*CONSTCOND*/
+ assert(MIN_DLVL == DNONE);
+ assert(MAX_DLVL == dpritab_len - 1);
+
+ (void) atexit(exiter);
+ prg = argv[0];
+ dfp = NULL;
+
+#ifdef DEBUG
+ openlog(prg, LOG_PID | LOG_CONS, LOG_DAEMON);
+ dlvl = DWARN;
+ dsys = 1;
+ optstr = "hsv:l:";
+#else
+ dlvl = DNONE;
+ dsys = 0;
+ optstr = "sv:l:";
+#endif
+
+ while ((c = getopt(argc, argv, optstr)) != -1) {
+ switch (c) {
+ case 'h':
+ help();
+ exit(0);
+ break;
+ case 's':
+ dsys = 0;
+ break;
+ case 'v':
+ dlvl = atoi(optarg);
+ break;
+ case 'l':
+ dfile = optarg;
+ break;
+ default:
+ dprint(DWARN, "bad option: %c\n", optopt);
+ return (EINVAL);
+ }
+ }
+
+ /*
+ * [ -l ] do file option first so we can still get msgs if -s is used
+ */
+ if (dfile != NULL) {
+ if (strcmp(dfile, "-") == 0) {
+ /* ignore if stdout is not open/valid */
+ dfp = NULL;
+ if (stdout != NULL &&
+ fstat(fileno(stdout), &statbuf) == 0)
+ dfp = stdout;
+ } else {
+ dfp = fopen(dfile, "a");
+ if (dfp == NULL) {
+ dprint(DWARN, "cannot open %s: %s\n",
+ dfile, strerror(errno));
+ return (EINVAL);
+ }
+ }
+ }
+
+ /* [ -v ] */
+ if (dlvl < MIN_DLVL || dlvl > MAX_DLVL) {
+ dprint(DWARN, "bad arg for -v: %d\n", dlvl);
+ return (EINVAL);
+ }
+
+ argi = optind;
+ if (argi >= argc) {
+ dprint(DWARN, "missing attribute arguments\n");
+ return (EINVAL);
+ }
+
+ nvlistp = parse_argv_attr(argc, argv, &argi);
+ if (nvlistp == NULL) {
+ dprint(DWARN, "attribute parsing error\n");
+ return (EINVAL);
+ }
+
+ (void) memset(&plugin_opt, 0, sizeof (plugin_opt));
+ plugin_opt.logopt.dlvl = dlvl;
+ plugin_opt.logopt.prg = prg;
+ plugin_opt.logopt.dfp = dfp;
+ plugin_opt.logopt.dsys = dsys;
+
+ dprint(DINFO, "=== sysevent attributes ========================\n");
+ pcidr_print_attrlist(DINFO, nvlistp, NULL);
+ dprint(DINFO, "================================================\n");
+
+ plugin_path = find_plugin(nvlistp);
+ if (plugin_path == NULL) {
+ dprint(DWARN, "cannot find plugin\n");
+ return (EINVAL);
+ }
+ dprint(DINFO, "using plugin: %s\n\n", plugin_path);
+
+ rv = do_plugin(plugin_path, nvlistp, &plugin_opt);
+ if (rv != 0) {
+ dprint(DWARN, "plugin %s failed\n", plugin_path);
+ }
+ if (plugin_path != NULL)
+ free(plugin_path);
+ return (rv);
+}
diff --git a/usr/src/cmd/pcidr/pcidr.h b/usr/src/cmd/pcidr/pcidr.h
new file mode 100644
index 0000000000..f23a7971b3
--- /dev/null
+++ b/usr/src/cmd/pcidr/pcidr.h
@@ -0,0 +1,109 @@
+/*
+ * 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 _PCIDR_H
+#define _PCIDR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <libnvpair.h>
+#include <config_admin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PCIDR_MALLOC_CNT 5
+#define PCIDR_MALLOC_TIME 1000000
+
+/* .._SYM and .._SYMSTR must match */
+#define PCIDR_PLUGIN_SYM pcidr_event_handler
+#define PCIDR_PLUGIN_SYMSTR "pcidr_event_handler"
+#define PCIDR_PLUGIN_NAME "pcidr_plugin.so"
+
+
+/*
+ * these ATTRNM_* correspond to the built-in sysevent.conf macros
+ * Note that the "publisher" macro used by syseventd is only a subset (third
+ * colon-delimited field) of the full publisher-id string specified in an
+ * event buffer/message.
+ */
+#define ATTRNM_CLASS "class"
+#define ATTRNM_SUBCLASS "subclass"
+#define ATTRNM_PUB_NAME "publisher"
+
+/* be sure to match with dpritab! */
+typedef enum {DNONE = 0, DWARN, DINFO, DDEBUG} dlvl_t;
+#define MIN_DLVL DNONE
+#define MAX_DLVL DDEBUG
+
+/* default set of DR attributes */
+typedef struct {
+ char *class;
+ char *subclass;
+ char *pub_name;
+ char *dr_req_type;
+ char *dr_ap_id;
+} pcidr_attrs_t;
+
+
+typedef struct {
+ dlvl_t dlvl;
+ char *prg;
+ FILE *dfp;
+ int dsys;
+} pcidr_logopt_t;
+
+typedef struct {
+ pcidr_logopt_t logopt;
+} pcidr_opt_t;
+
+typedef int(*pcidr_plugin_t)(nvlist_t *, pcidr_opt_t *);
+#define PCIDR_PLUGIN_PROTO(a, b) \
+ int PCIDR_PLUGIN_SYM(nvlist_t *a, pcidr_opt_t *b)
+
+
+void *pcidr_malloc(size_t);
+void dprint(dlvl_t, char *, ...);
+int pcidr_name2type(char *, data_type_t *);
+void pcidr_print_attrlist(dlvl_t, nvlist_t *, char *);
+int pcidr_check_string(char *, ...);
+int pcidr_get_attrs(nvlist_t *, pcidr_attrs_t *);
+int pcidr_check_attrs(pcidr_attrs_t *);
+void pcidr_set_logopt(pcidr_logopt_t *);
+
+extern dlvl_t dlvl;
+extern char *prg;
+extern FILE *dfp;
+extern int dsys;
+extern char *prg;
+extern int dpritab_len;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PCIDR_H */
diff --git a/usr/src/cmd/pcidr/pcidr_common.c b/usr/src/cmd/pcidr/pcidr_common.c
new file mode 100644
index 0000000000..31f198520c
--- /dev/null
+++ b/usr/src/cmd/pcidr/pcidr_common.c
@@ -0,0 +1,351 @@
+/*
+ * 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"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/sysevent/dr.h>
+#include <syslog.h>
+#include <libnvpair.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <dlfcn.h>
+#include <signal.h>
+#include <pcidr.h>
+
+
+/*
+ * How dpritab is used:
+ * dpritab[dlvl_t value] = corresponding syslog priority
+ *
+ * Be careful of some priorities (facility + severity) that get "lost" by
+ * default since they have no syslog.conf entries such as daemon.info and
+ * daemon.debug; see syslog(3C) and syslog.conf(4) for more info
+ */
+int dpritab[] = {LOG_INFO, LOG_WARNING, LOG_NOTICE, LOG_NOTICE};
+int dpritab_len = sizeof (dpritab) / sizeof (dpritab[0]);
+
+/*
+ * the following affects pcidr_set_logopt() which plugins should use to set
+ * these logging options received from the handler
+ */
+dlvl_t dlvl = MIN_DLVL; /* verbosity */
+char *prg = ""; /* program name */
+FILE *dfp = NULL; /* file to output messages to */
+int dsys = 1; /* flag controlling output to syslog */
+
+
+void *
+pcidr_malloc(size_t size)
+{
+ int i = 0;
+ void *buf;
+
+ errno = 0;
+ buf = malloc(size);
+ if (buf != NULL)
+ return (buf);
+
+ for (i = 0; i < PCIDR_MALLOC_CNT; i++) {
+ assert(errno == EAGAIN);
+ if (errno != EAGAIN)
+ exit(errno);
+ (void) usleep(PCIDR_MALLOC_TIME);
+
+ errno = 0;
+ buf = malloc(size);
+ if (buf != NULL)
+ return (buf);
+ }
+
+ assert(buf != NULL);
+ /* exit() in case assertions are disabled (NDEBUG defined) */
+ exit(errno);
+ return (NULL);
+}
+
+
+void
+dprint(dlvl_t lvl, char *fmt, ...)
+{
+ int buflen, rv;
+ char *buf;
+ va_list ap;
+
+ if (dlvl < lvl || (dsys == 0 && dfp == NULL))
+ return;
+
+ va_start(ap, fmt);
+ /*LINTED*/
+ buflen = vsnprintf(NULL, 0, fmt, ap);
+ va_end(ap);
+ if (buflen <= 0)
+ return;
+ buflen++;
+ buf = (char *)pcidr_malloc(sizeof (char) * buflen);
+
+ va_start(ap, fmt);
+ /*LINTED*/
+ rv = vsnprintf(buf, buflen, fmt, ap);
+ va_end(ap);
+ if (rv <= 0) {
+ free(buf);
+ return;
+ }
+
+#ifdef DEBUG
+ if (dsys != 0)
+ syslog(dpritab[lvl], "%s", buf);
+#endif
+ if (dfp != NULL)
+ (void) fprintf(dfp, "%s", buf);
+
+ free(buf);
+}
+
+
+void
+pcidr_set_logopt(pcidr_logopt_t *logopt)
+{
+ dlvl = logopt->dlvl;
+ prg = logopt->prg;
+ dfp = logopt->dfp;
+ dsys = logopt->dsys;
+}
+
+
+/*
+ * if <name> is recognized, function will return its type through <typep> and
+ * return 0; else function will return non-zero
+ */
+int
+pcidr_name2type(char *name, data_type_t *typep)
+{
+ /* string type */
+ if (strcmp(name, ATTRNM_CLASS) == 0 ||
+ strcmp(name, ATTRNM_SUBCLASS) == 0 ||
+ strcmp(name, ATTRNM_PUB_NAME) == 0 ||
+ strcmp(name, DR_REQ_TYPE) == 0 ||
+ strcmp(name, DR_AP_ID) == 0) {
+ *typep = DATA_TYPE_STRING;
+ return (0);
+ }
+
+ return (1);
+}
+
+
+void
+pcidr_print_attrlist(dlvl_t lvl, nvlist_t *attrlistp, char *prestr)
+{
+ char *fn = "pcidr_print_attrlist";
+ nvpair_t *nvpairp;
+ char *valstr, *name;
+ data_type_t type;
+ int rv;
+
+ if (prestr == NULL)
+ prestr = "";
+
+ nvpairp = NULL;
+ while ((nvpairp = nvlist_next_nvpair(attrlistp, nvpairp)) != NULL) {
+ type = nvpair_type(nvpairp);
+ name = nvpair_name(nvpairp);
+
+ switch (type) {
+ case DATA_TYPE_STRING:
+ rv = nvpair_value_string(nvpairp, &valstr);
+ if (rv != 0) {
+ dprint(lvl, "%s: nvpair_value_string() "
+ "failed: name = %s, rv = %d\n",
+ fn, name, rv);
+ continue;
+ }
+ break;
+ default:
+ dprint(lvl, "%s: unsupported type: name = %s, "
+ "type = 0x%x\n", fn, name, (int)type);
+ continue;
+ }
+ dprint(lvl, "%s%s = %s\n", prestr, name, valstr);
+ }
+}
+
+
+/*
+ * if one of the args matches <valstr>, return 0; else return non-zero
+ * args list must be NULL terminated;
+ * if args list is empty, this will return 0 if <valstr> is NOT empty
+ */
+int
+pcidr_check_string(char *valstr, ...)
+{
+ va_list ap;
+ int rv;
+ char *argstr;
+
+ assert(valstr != NULL);
+ rv = 1;
+ va_start(ap, valstr);
+ if (va_arg(ap, char *) == NULL) {
+ if (valstr[0] != '\0')
+ rv = 0;
+ goto OUT;
+ }
+
+ va_start(ap, valstr);
+ while ((argstr = va_arg(ap, char *)) != NULL) {
+ if (strcmp(argstr, valstr) == 0) {
+ rv = 0;
+ break;
+ }
+ }
+OUT:
+ va_end(ap);
+ return (rv);
+}
+
+
+/*
+ * dr attribute values that the default plugin checks for;
+ * other plugins may also use this if they support a superset of these
+ * values.
+ * returns 0 if valid, else non-zero
+ */
+int
+pcidr_check_attrs(pcidr_attrs_t *drp)
+{
+ char *fn = "pcidr_check_attrs";
+ int rv = 0;
+ char *val, *name;
+
+ name = ATTRNM_CLASS;
+ val = drp->class;
+ if (pcidr_check_string(val, EC_DR, NULL) != 0) {
+ dprint(DDEBUG, "%s: attribute \"%s\" has invalid value = %s\n",
+ fn, name, val);
+ rv = 1;
+ }
+
+ name = ATTRNM_SUBCLASS;
+ val = drp->subclass;
+ if (pcidr_check_string(val, ESC_DR_REQ, NULL) != 0) {
+ dprint(DDEBUG, "%s: attribute \"%s\" has invalid value = %s\n",
+ fn, name, val);
+ rv = 1;
+ }
+
+ name = ATTRNM_PUB_NAME;
+ val = drp->pub_name;
+ if (pcidr_check_string(val, NULL) != 0) {
+ dprint(DDEBUG, "%s: attribute \"%s\" is empty\n",
+ fn, name, val);
+ rv = 1;
+ }
+
+ name = DR_REQ_TYPE;
+ val = drp->dr_req_type;
+ if (pcidr_check_string(val, DR_REQ_INCOMING_RES, DR_REQ_OUTGOING_RES,
+ NULL) != 0) {
+ dprint(DDEBUG, "%s: attribute \"%s\" has invalid value = %s\n",
+ fn, name, val);
+ rv = 1;
+ }
+
+ name = DR_AP_ID;
+ val = drp->dr_ap_id;
+ if (pcidr_check_string(drp->dr_ap_id, NULL) != 0) {
+ dprint(DDEBUG, "%s: attribute \"%s\" is empty\n",
+ fn, name, val);
+ rv = 1;
+ }
+
+ return (rv);
+}
+
+
+/*
+ * get dr attributes from <listp> for the default plugin and returns
+ * them through <drp>;
+ * returns 0 on success
+ */
+int
+pcidr_get_attrs(nvlist_t *attrlistp, pcidr_attrs_t *drp)
+{
+ char *fn = "pcidr_get_attrs";
+ char *name;
+ int r, rv = 0;
+
+ name = ATTRNM_CLASS;
+ r = nvlist_lookup_string(attrlistp, name, &drp->class);
+ if (r != 0) {
+ dprint(DDEBUG, "%s: nvlist_lookup_string() failed for "
+ "attribute \"%s\": rv = %d\n", fn, name, r);
+ rv = r;
+ }
+
+ name = ATTRNM_SUBCLASS;
+ r = nvlist_lookup_string(attrlistp, name, &drp->subclass);
+ if (r != 0) {
+ dprint(DDEBUG, "%s: nvlist_lookup_string() failed for "
+ "attribute \"%s\": rv = %d\n", fn, name, r);
+ rv = r;
+ }
+
+ name = ATTRNM_PUB_NAME;
+ r = nvlist_lookup_string(attrlistp, name, &drp->pub_name);
+ if (r != 0) {
+ dprint(DDEBUG, "%s: nvlist_lookup_string() failed for "
+ "attribute \"%s\": rv = %d\n", fn, name, r);
+ rv = r;
+ }
+
+ name = DR_REQ_TYPE;
+ r = nvlist_lookup_string(attrlistp, name, &drp->dr_req_type);
+ if (r != 0) {
+ dprint(DDEBUG, "%s: nvlist_lookup_string() failed for "
+ "attribute \"%s\": rv = %d\n", fn, name, r);
+ rv = r;
+ }
+
+ name = DR_AP_ID;
+ r = nvlist_lookup_string(attrlistp, name, &drp->dr_ap_id);
+ if (r != 0) {
+ dprint(DDEBUG, "%s: nvlist_lookup_string() failed for "
+ "attribute \"%s\": rv = %d\n", fn, name, r);
+ rv = r;
+ }
+
+ return (rv);
+}
diff --git a/usr/src/cmd/pcidr/plugins/Makefile b/usr/src/cmd/pcidr/plugins/Makefile
new file mode 100755
index 0000000000..98a61d7d3a
--- /dev/null
+++ b/usr/src/cmd/pcidr/plugins/Makefile
@@ -0,0 +1,52 @@
+#
+# 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.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/pcidr/plugins/Makefile
+#
+
+.PARALLEL:
+
+SUBDIRS = default
+
+include $(SRC)/cmd/Makefile.cmd
+
+all := TARGET = all
+install := TARGET = install
+clean := TARGET = clean
+clobber := TARGET = clobber
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber lint install: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/cmd/pcidr/plugins/Makefile.targ b/usr/src/cmd/pcidr/plugins/Makefile.targ
new file mode 100755
index 0000000000..5530fee78f
--- /dev/null
+++ b/usr/src/cmd/pcidr/plugins/Makefile.targ
@@ -0,0 +1,37 @@
+#
+# 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.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/pcidr/plugins/Makefile.targ
+#
+
+# unlike cmd/Makefile.targ, lib/Makefile.targ install targets do not depend
+# on or create parent directories
+#
+$(ROOTLIBS): $(ROOTLIBDIR)
+
+$(ROOTLIBDIR):
+ $(INS.dir)
diff --git a/usr/src/cmd/pcidr/plugins/default/Makefile b/usr/src/cmd/pcidr/plugins/default/Makefile
new file mode 100755
index 0000000000..2a8ced542c
--- /dev/null
+++ b/usr/src/cmd/pcidr/plugins/default/Makefile
@@ -0,0 +1,93 @@
+#
+# 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.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# cmd/pcidr/plugins/default/Makefile
+#
+
+.PARALLEL:
+
+#############################################################################
+
+NAME = pcidr_plugin
+LIBRARY = $(NAME).a
+VERS =
+
+TOPOBJECTS = pcidr_common.o
+LOCOBJECTS = $(NAME).o pcidr_cfga.o
+OBJECTS = $(LOCOBJECTS) $(TOPOBJECTS)
+
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/cmd/pcidr/Makefile.com
+#############################################################################
+
+THISDIR = $(TOP)/plugins/default
+
+# SRCS is used by the lintcheck rule and is defined as
+# $(OBJECTS:%.o=$(SRCDIR)/%.c) where SRCDIR is this directory; set SRCS to a
+# list of source paths if it differ from the default
+#
+SRCS = $(TOPOBJECTS:%.o=$(TOP)/%.c) $(LOCOBJECTS:%.o=$(SRCDIR)/%.c)
+
+LIBS = $(DYNLIB)
+
+LDLIBS += -lcfgadm -lnvpair
+
+HDRSRCS_SH = ls -1 $(THISDIR)/*.h
+HDRSRCS = $(HDRSRCS_SH:sh)
+CPPFLAGS += -I$(THISDIR)
+
+# override LIBLINKS so that ROOTLIBS or anything else won't match the install
+# target "$(ROOTLIBDIR)/$(LIBLINKS)"
+#
+LIBLINKS = __no_liblinks__
+
+.KEEP_STATE:
+#############################################################################
+
+all: $(LIBS)
+install: $(ROOTLIBS)
+clean:
+clobber:
+lint: lintcheck
+
+$(ROOTLIBS): all
+
+# Note that we can't do:
+# $(TOPOBJECTS:%=objs/%) := SRCDIR = $(TOP)
+# and let the make do the rest because the man page states:
+# "Notice that if a conditional macro is referred to in a dependency list,
+# the $ must be delayed (use $$ instead)."
+#
+# So we must add new targets for our TOPOBJECTS items but follow
+# the existing pic/*.o and objs/*.o rules in lib/Makefile.targ
+#
+objs/%.o pics/%.o: $(TOP)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+include $(SRC)/lib/Makefile.targ
+include ../Makefile.targ
diff --git a/usr/src/cmd/pcidr/plugins/default/pcidr_cfga.c b/usr/src/cmd/pcidr/plugins/default/pcidr_cfga.c
new file mode 100644
index 0000000000..631c602a8f
--- /dev/null
+++ b/usr/src/cmd/pcidr/plugins/default/pcidr_cfga.c
@@ -0,0 +1,330 @@
+/*
+ * 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"
+
+#include <string.h>
+#include <sys/param.h>
+#include <assert.h>
+#include <pcidr.h>
+#include <pcidr_cfga.h>
+
+
+/*
+ * misc config_admin(3cfgadm) related routines
+ */
+
+static struct {
+ cfga_stat_t stat;
+ char *name;
+} pcidr_cfga_stat_nametab[] = {
+ {CFGA_STAT_NONE, "CFGA_STAT_NONE"},
+ {CFGA_STAT_EMPTY, "CFGA_STAT_EMPTY"},
+ {CFGA_STAT_DISCONNECTED, "CFGA_STAT_DISCONNECTED"},
+ {CFGA_STAT_CONNECTED, "CFGA_STAT_CONNECTED"},
+ {CFGA_STAT_UNCONFIGURED, "CFGA_STAT_UNCONFIGURED"},
+ {CFGA_STAT_CONFIGURED, "CFGA_STAT_CONFIGURED"},
+};
+static int pcidr_cfga_stat_nametab_len =
+ sizeof (pcidr_cfga_stat_nametab) / sizeof (pcidr_cfga_stat_nametab[0]);
+
+char *
+pcidr_cfga_stat_name(cfga_stat_t val)
+{
+ int i;
+
+ for (i = 0; i < pcidr_cfga_stat_nametab_len; i++) {
+ if (pcidr_cfga_stat_nametab[i].stat == val)
+ return (pcidr_cfga_stat_nametab[i].name);
+ }
+ return (NULL);
+}
+
+
+static struct {
+ cfga_stat_t cmd;
+ char *name;
+} pcidr_cfga_cmd_nametab[] = {
+ {CFGA_CMD_NONE, "CFGA_CMD_NONE"},
+ {CFGA_CMD_LOAD, "CFGA_CMD_LOAD"},
+ {CFGA_CMD_UNLOAD, "CFGA_CMD_UNLOAD"},
+ {CFGA_CMD_CONNECT, "CFGA_CMD_CONNECT"},
+ {CFGA_CMD_DISCONNECT, "CFGA_CMD_DISCONNECT"},
+ {CFGA_CMD_CONFIGURE, "CFGA_CMD_CONFIGURE"},
+ {CFGA_CMD_UNCONFIGURE, "CFGA_CMD_UNCONFIGURE"},
+};
+static int pcidr_cfga_cmd_nametab_len =
+ sizeof (pcidr_cfga_cmd_nametab) / sizeof (pcidr_cfga_cmd_nametab[0]);
+
+char *
+pcidr_cfga_cmd_name(cfga_cmd_t val)
+{
+ int i;
+
+ for (i = 0; i < pcidr_cfga_cmd_nametab_len; i++) {
+ if (pcidr_cfga_cmd_nametab[i].cmd == val)
+ return (pcidr_cfga_cmd_nametab[i].name);
+ }
+ return (NULL);
+}
+
+
+static struct {
+ cfga_cond_t cond;
+ char *name;
+} pcidr_cfga_cond_nametab[] = {
+ {CFGA_COND_UNKNOWN, "CFGA_COND_UNKNOWN"},
+ {CFGA_COND_OK, "CFGA_COND_OK"},
+ {CFGA_COND_FAILING, "CFGA_COND_FAILING"},
+ {CFGA_COND_FAILED, "CFGA_COND_FAILED"},
+ {CFGA_COND_UNUSABLE, "CFGA_COND_UNUSABLE"},
+};
+static int pcidr_cfga_cond_nametab_len =
+ sizeof (pcidr_cfga_cond_nametab) / sizeof (pcidr_cfga_cond_nametab[0]);
+
+char *
+pcidr_cfga_cond_name(cfga_cond_t val)
+{
+ int i;
+
+ for (i = 0; i < pcidr_cfga_cond_nametab_len; i++) {
+ if (pcidr_cfga_cond_nametab[i].cond == val)
+ return (pcidr_cfga_cond_nametab[i].name);
+ }
+ return (NULL);
+}
+
+
+static struct {
+ cfga_err_t err;
+ char *name;
+} pcidr_cfga_err_nametab[] = {
+ {CFGA_OK, "CFGA_OK"},
+ {CFGA_NACK, "CFGA_NACK"},
+ {CFGA_NOTSUPP, "CFGA_NOTSUPP"},
+ {CFGA_OPNOTSUPP, "CFGA_OPNOTSUPP"},
+ {CFGA_PRIV, "CFGA_PRIV"},
+ {CFGA_BUSY, "CFGA_BUSY"},
+ {CFGA_SYSTEM_BUSY, "CFGA_SYSTEM_BUSY"},
+ {CFGA_DATA_ERROR, "CFGA_DATA_ERROR"},
+ {CFGA_LIB_ERROR, "CFGA_LIB_ERROR"},
+ {CFGA_NO_LIB, "CFGA_NO_LIB"},
+ {CFGA_INSUFFICENT_CONDITION, "CFGA_INSUFFICENT_CONDITION"},
+ {CFGA_INVAL, "CFGA_INVAL"},
+ {CFGA_ERROR, "CFGA_ERROR"},
+ {CFGA_APID_NOEXIST, "CFGA_APID_NOEXIST"},
+ {CFGA_ATTR_INVAL, "CFGA_ATTR_INVAL"},
+};
+static int pcidr_cfga_err_nametab_len =
+ sizeof (pcidr_cfga_err_nametab) / sizeof (pcidr_cfga_err_nametab[0]);
+
+char *
+pcidr_cfga_err_name(cfga_err_t val)
+{
+ int i;
+
+ for (i = 0; i < pcidr_cfga_err_nametab_len; i++) {
+ if (pcidr_cfga_err_nametab[i].err == val)
+ return (pcidr_cfga_err_nametab[i].name);
+ }
+ return (NULL);
+}
+
+
+void
+pcidr_print_cfga(dlvl_t lvl, cfga_list_data_t *datap, char *prestr)
+{
+ char *str;
+
+ if (prestr == NULL)
+ prestr = "";
+
+ dprint(lvl, "%slogical APID = %s\n", prestr, datap->ap_log_id);
+ dprint(lvl, "%sphyiscal APID = %s\n", prestr, datap->ap_phys_id);
+ dprint(lvl, "%sAP class = %s\n", prestr, datap->ap_class);
+
+ str = pcidr_cfga_stat_name(datap->ap_r_state);
+ if (str == NULL)
+ str = "(unrecognized cfga_stat_t value!)";
+ dprint(lvl, "%sAP receptacle state = %s\n", prestr, str);
+
+ str = pcidr_cfga_stat_name(datap->ap_o_state);
+ if (str == NULL)
+ str = "(unrecognized cfga_stat_t value!)";
+ dprint(lvl, "%sAP occupant state = %s\n", prestr, str);
+
+ str = pcidr_cfga_cond_name(datap->ap_cond);
+ if (str == NULL)
+ str = "(unrecognized cfga_cond_t value!)";
+ dprint(lvl, "%sAP condition = %s\n", prestr, str);
+
+ dprint(lvl, "%sAP busy indicator = %d\n", prestr, datap->ap_busy);
+
+ str = ctime(&datap->ap_status_time);
+ str[strlen(str) - 1] = '\0'; /* get rid of newline */
+ dprint(lvl, "%sAP last change time = %ld (%s)\n", prestr,
+ datap->ap_status_time, str);
+
+ dprint(lvl, "%sAP info = %s\n", prestr, datap->ap_info);
+ dprint(lvl, "%sAP type = %s\n", prestr, datap->ap_type);
+}
+
+
+/*
+ * for use with config_admin(3cfgadm) functions in their
+ * <struct cfga_msg *msgp> parameter
+ */
+int
+pcidr_cfga_msg_func(void *datap, const char *msg)
+{
+ pcidr_cfga_msg_data_t *dp = (pcidr_cfga_msg_data_t *)datap;
+ char *prestr = dp->prestr;
+
+ if (prestr == NULL)
+ prestr = "";
+
+ dprint(dp->dlvl, "%s%s", prestr, msg);
+ return (0);
+}
+
+
+/*
+ * for use with config_admin(3cfgadm) functions in their
+ * <struct cfga_confirm *confp> parameter
+ */
+/*ARGSUSED*/
+int
+pcidr_cfga_confirm_func(void *datap, const char *msg)
+{
+ return (1);
+}
+
+
+/*
+ * returns 0 if successful, -1 if unusuccesful, 1 if the AP already had
+ * <cmd> performed on it
+ */
+int
+pcidr_cfga_do_cmd(cfga_cmd_t cmd, cfga_list_data_t *cfga_listp)
+{
+ char *fn = "pcidr_cfga_do_cmd";
+ int rv, i, j;
+ char *cmdnm, *cfga_errstr, *apid, *str;
+ int cmdarr[2];
+ int cmdarr_len = sizeof (cmdarr) / sizeof (cmdarr[0]);
+
+ struct cfga_msg cfga_msg;
+ pcidr_cfga_msg_data_t cfga_msg_data;
+ struct cfga_confirm cfga_confirm;
+ cfga_flags_t cfga_flags;
+
+ cmdnm = pcidr_cfga_cmd_name(cmd);
+ assert(cmdnm != NULL);
+
+ apid = cfga_listp->ap_phys_id;
+ cfga_msg_data.dlvl = DDEBUG;
+ cfga_msg_data.prestr = "pcidr_cfga_do_cmd(msg): ";
+ cfga_msg.message_routine = pcidr_cfga_msg_func;
+ cfga_msg.appdata_ptr = (void *)&cfga_msg_data;
+ cfga_confirm.confirm = pcidr_cfga_confirm_func;
+ cfga_confirm.appdata_ptr = NULL;
+ cfga_flags = CFGA_FLAG_VERBOSE;
+
+ if (cfga_listp->ap_busy != 0) {
+ dprint(DDEBUG, "%s: apid = %s is busy\n",
+ fn, cfga_listp->ap_phys_id);
+ return (-1);
+ }
+
+ /*
+ * explicitly perform each step that would otherwise be done
+ * implicitly by cfgadm to isolate errors
+ */
+ j = 0;
+ switch (cmd) {
+ case CFGA_CMD_CONFIGURE:
+ if (cfga_listp->ap_o_state < CFGA_STAT_CONNECTED) {
+ cmdarr[j] = CFGA_CMD_CONNECT;
+ j++;
+ }
+ if (cfga_listp->ap_o_state < CFGA_STAT_CONFIGURED) {
+ cmdarr[j] = CFGA_CMD_CONFIGURE;
+ j++;
+ }
+ if (cfga_listp->ap_o_state >= CFGA_STAT_CONFIGURED)
+ goto ALREADY;
+ break;
+ case CFGA_CMD_DISCONNECT:
+ if (cfga_listp->ap_o_state >= CFGA_STAT_CONFIGURED) {
+ cmdarr[j] = CFGA_CMD_UNCONFIGURE;
+ j++;
+ }
+ if (cfga_listp->ap_o_state >= CFGA_STAT_CONNECTED) {
+ cmdarr[j] = CFGA_CMD_DISCONNECT;
+ j++;
+ }
+ if (cfga_listp->ap_r_state <= CFGA_STAT_DISCONNECTED)
+ goto ALREADY;
+ break;
+ default:
+ dprint(DDEBUG, "%s: unsupported cmd %d\n", cmd);
+ return (-1);
+ }
+ assert(j <= cmdarr_len);
+
+ for (i = 0; i < j; i++) {
+ cmd = cmdarr[i];
+ cmdnm = pcidr_cfga_cmd_name(cmd);
+ assert(cmdnm != NULL);
+
+ rv = config_change_state(cmd, 1, &apid, NULL, &cfga_confirm,
+ &cfga_msg, &cfga_errstr, cfga_flags);
+ if (rv != CFGA_OK) {
+ dprint(DDEBUG, "%s: command %s failed on apid %s",
+ fn, cmdnm, apid);
+
+ str = pcidr_cfga_err_name(rv);
+ if (str == NULL)
+ str = "unrecognized rv!";
+ dprint(DDEBUG, ": rv = %d (%s)", rv, str);
+
+ if (cfga_errstr != NULL) {
+ dprint(DDEBUG, ", error string = "
+ "\"%s\"", cfga_errstr);
+ free(cfga_errstr);
+ }
+ dprint(DDEBUG, "\n");
+ return (-1);
+ }
+ }
+
+ return (0);
+ /*NOTREACHED*/
+ALREADY:
+ dprint(DDEBUG, "%s: command %s already done on apid %s\n",
+ fn, cmdnm, apid);
+ return (1);
+}
diff --git a/usr/src/cmd/pcidr/plugins/default/pcidr_cfga.h b/usr/src/cmd/pcidr/plugins/default/pcidr_cfga.h
new file mode 100644
index 0000000000..4465a5c3bc
--- /dev/null
+++ b/usr/src/cmd/pcidr/plugins/default/pcidr_cfga.h
@@ -0,0 +1,57 @@
+/*
+ * 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 _PCIDR_CFGA_H
+#define _PCIDR_CFGA_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <config_admin.h>
+#include <pcidr.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void pcidr_print_cfga(dlvl_t, cfga_list_data_t *, char *);
+char *pcidr_cfga_stat_name(cfga_stat_t);
+char *pcidr_cfga_cmd_name(cfga_cmd_t);
+char *pcidr_cfga_cond_name(cfga_cond_t);
+char *pcidr_cfga_err_name(cfga_err_t);
+int pcidr_cfga_do_cmd(cfga_cmd_t, cfga_list_data_t *);
+
+typedef struct {
+ dlvl_t dlvl;
+ char *prestr;
+} pcidr_cfga_msg_data_t;
+int pcidr_cfga_msg_func(void *, const char *);
+int pcidr_cfga_confirm_func(void *, const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PCIDR_CFGA_H */
diff --git a/usr/src/cmd/pcidr/plugins/default/pcidr_plugin.c b/usr/src/cmd/pcidr/plugins/default/pcidr_plugin.c
new file mode 100644
index 0000000000..c321cfe01c
--- /dev/null
+++ b/usr/src/cmd/pcidr/plugins/default/pcidr_plugin.c
@@ -0,0 +1,144 @@
+/*
+ * 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"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <sys/systeminfo.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/sysevent/dr.h>
+#include <syslog.h>
+#include <libnvpair.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <dlfcn.h>
+#include <pcidr.h>
+#include <pcidr_cfga.h>
+
+
+PCIDR_PLUGIN_PROTO(attrlistp, optp)
+{
+ char *fn = PCIDR_PLUGIN_SYMSTR;
+ int rv = 0;
+ char *cfga_errstr = NULL;
+ char *str, *apid;
+ cfga_list_data_t *cfga_listp = NULL;
+ cfga_cmd_t cmd;
+ int cfga_list_len;
+ pcidr_attrs_t dr;
+
+ pcidr_set_logopt(&optp->logopt);
+
+ if (pcidr_get_attrs(attrlistp, &dr) != 0 ||
+ pcidr_check_attrs(&dr) != 0) {
+ dprint(DWARN, "%s: invalid or missing attributes\n", fn);
+ return (EINVAL);
+ }
+
+ /*
+ * get state of APID; enforce the cfgadm pci plugin implementation of
+ * returning one matching AP per supplied apid string
+ */
+ rv = config_list_ext(1, &dr.dr_ap_id, &cfga_listp, &cfga_list_len,
+ NULL, NULL, &cfga_errstr, CFGA_FLAG_LIST_ALL);
+ if (rv != CFGA_OK) {
+ str = pcidr_cfga_err_name(rv);
+ if (str == NULL)
+ str = "unrecognized rv!";
+ dprint(DDEBUG, "%s: config_list_ext() on apid = \"%s\" "
+ "failed: rv = %d (%s)", fn, dr.dr_ap_id, rv, str);
+
+ if (cfga_errstr != NULL) {
+ dprint(DDEBUG, ", error string = \"%s\"",
+ cfga_errstr);
+ free(cfga_errstr);
+ }
+ dprint(DDEBUG, "\n");
+ rv = EINVAL;
+ goto OUT;
+ }
+ if (cfga_list_len != 1) {
+ dprint(DWARN, "%s: invalid condition - more than one AP was "
+ "found for the APID \"%s\"\n", fn, dr.dr_ap_id);
+ rv = EINVAL;
+ goto OUT;
+ }
+
+ /*
+ * perform DR
+ */
+ dprint(DINFO, "%s: showing info and performing DR on APID(s) "
+ "matching \"%s\"\n", fn, dr.dr_ap_id);
+
+ cmd = CFGA_CMD_NONE;
+ dprint(DINFO, "===========================================\n", fn);
+ pcidr_print_cfga(DINFO, &cfga_listp[0], " .. ");
+ apid = cfga_listp[0].ap_phys_id;
+
+ if (strcmp(dr.dr_req_type, DR_REQ_OUTGOING_RES) == 0) {
+ cmd = CFGA_CMD_DISCONNECT;
+ dprint(DINFO, "%s: disconnecting ...\n", fn, apid);
+
+ rv = pcidr_cfga_do_cmd(cmd, &cfga_listp[0]);
+ if (rv < 0) {
+ dprint(DINFO, "%s: disconnect FAILED\n", fn);
+ rv = EIO;
+ }
+ else
+ dprint(DINFO, "%s: disconnect OK\n", fn);
+
+ goto OUT;
+ }
+ if (strcmp(dr.dr_req_type, DR_REQ_INCOMING_RES) == 0) {
+ cmd = CFGA_CMD_CONFIGURE;
+ dprint(DINFO, "%s: configuring ...\n", fn, apid);
+
+ rv = pcidr_cfga_do_cmd(cmd, &cfga_listp[0]);
+ if (rv < 0) {
+ dprint(DINFO, "%s: configure FAILED\n", fn);
+ rv = EIO;
+ } else
+ dprint(DINFO, "%s: configure OK\n", fn);
+
+ goto OUT;
+ }
+
+ /* we should not get here if pcidr_check_attrs() is correct */
+ dprint(DWARN, "%s: invalid dr_req_type = %s\n", fn, dr.dr_req_type);
+ assert(cmd != CFGA_CMD_NONE);
+ return (EINVAL);
+ /*NOTREACHED*/
+OUT:
+ if (cfga_listp != NULL)
+ free(cfga_listp);
+ return (rv);
+}
diff --git a/usr/src/lib/cfgadm_plugins/pci/common/cfga.c b/usr/src/lib/cfgadm_plugins/pci/common/cfga.c
index 9f7752fa29..fcb4c66caa 100644
--- a/usr/src/lib/cfgadm_plugins/pci/common/cfga.c
+++ b/usr/src/lib/cfgadm_plugins/pci/common/cfga.c
@@ -704,7 +704,9 @@ cfga_change_state(cfga_cmd_t state_change_cmd, const char *ap_id,
if (devctl_ap_configure(dcp, NULL) == -1) {
rv = CFGA_ERROR;
cfga_err(errstring, CMD_SLOT_CONFIGURE, 0);
- if (devctl_ap_disconnect(dcp, NULL) == -1) {
+ if ((rs == AP_RSTATE_DISCONNECTED) &&
+ (devctl_ap_disconnect(dcp, NULL)
+ == -1)) {
rv = CFGA_ERROR;
cfga_err(errstring,
CMD_SLOT_CONFIGURE, 0);
diff --git a/usr/src/pkgdefs/SUNWcakr.i/prototype_com b/usr/src/pkgdefs/SUNWcakr.i/prototype_com
index 674eda12f9..e22548f268 100644
--- a/usr/src/pkgdefs/SUNWcakr.i/prototype_com
+++ b/usr/src/pkgdefs/SUNWcakr.i/prototype_com
@@ -67,7 +67,10 @@ f none platform/i86pc/kernel/drv/bscv 755 root sys
f none platform/i86pc/kernel/drv/bscv.conf 644 root sys
f none platform/i86pc/kernel/drv/isa 755 root sys
f none platform/i86pc/kernel/drv/kb8042 755 root sys
+f none platform/i86pc/kernel/drv/npe 755 root sys
f none platform/i86pc/kernel/drv/pci 755 root sys
+f none platform/i86pc/kernel/drv/pcie_pci 755 root sys
+f none platform/i86pc/kernel/drv/pcie_pci.conf 644 root sys
f none platform/i86pc/kernel/drv/power 755 root sys
f none platform/i86pc/kernel/drv/power.conf 644 root sys
f none platform/i86pc/kernel/drv/rootnex 755 root sys
@@ -76,12 +79,15 @@ f none platform/i86pc/kernel/mach/uppc 755 root sys
d none platform/i86pc/kernel/misc 755 root sys
f none platform/i86pc/kernel/misc/bootdev 755 root sys
f none platform/i86pc/kernel/misc/pci_autoconfig 755 root sys
+f none platform/i86pc/kernel/misc/pciehpc 755 root sys
f none platform/i86pc/kernel/unix 755 root sys
d none platform/i86pc/kernel/drv/amd64 755 root sys
f none platform/i86pc/kernel/drv/amd64/bmc 755 root sys
f none platform/i86pc/kernel/drv/amd64/isa 755 root sys
f none platform/i86pc/kernel/drv/amd64/kb8042 755 root sys
+f none platform/i86pc/kernel/drv/amd64/npe 755 root sys
f none platform/i86pc/kernel/drv/amd64/pci 755 root sys
+f none platform/i86pc/kernel/drv/amd64/pcie_pci 755 root sys
f none platform/i86pc/kernel/drv/amd64/power 755 root sys
f none platform/i86pc/kernel/drv/amd64/rootnex 755 root sys
d none platform/i86pc/kernel/mach/amd64 755 root sys
@@ -89,5 +95,6 @@ f none platform/i86pc/kernel/mach/amd64/uppc 755 root sys
d none platform/i86pc/kernel/misc/amd64 755 root sys
f none platform/i86pc/kernel/misc/amd64/bootdev 755 root sys
f none platform/i86pc/kernel/misc/amd64/pci_autoconfig 755 root sys
+f none platform/i86pc/kernel/misc/amd64/pciehpc 755 root sys
d none platform/i86pc/kernel/amd64 755 root sys
f none platform/i86pc/kernel/amd64/unix 755 root sys
diff --git a/usr/src/pkgdefs/SUNWcsr/prototype_com b/usr/src/pkgdefs/SUNWcsr/prototype_com
index 154aef85a3..83724af857 100644
--- a/usr/src/pkgdefs/SUNWcsr/prototype_com
+++ b/usr/src/pkgdefs/SUNWcsr/prototype_com
@@ -284,6 +284,7 @@ e renameold etc/security/priv_names 644 root sys
d none etc/sysevent 755 root sys
d none etc/sysevent/config 755 root sys
f none etc/sysevent/config/README 444 root sys
+f none etc/sysevent/config/SUNW,EC_dr,ESC_dr_req,sysevent.conf 644 root sys
e pamconf etc/pam.conf 644 root sys
s none etc/services=./inet/services
s none etc/setmnt=../usr/sbin/setmnt
diff --git a/usr/src/pkgdefs/SUNWcsu/prototype_com b/usr/src/pkgdefs/SUNWcsu/prototype_com
index dc37682cb9..5a2fcf7e23 100644
--- a/usr/src/pkgdefs/SUNWcsu/prototype_com
+++ b/usr/src/pkgdefs/SUNWcsu/prototype_com
@@ -569,6 +569,9 @@ f none usr/lib/more.help 644 root bin
d none usr/lib/netsvc 755 root sys
f none usr/lib/newsyslog 555 root sys
f none usr/lib/nscd_nischeck 555 root bin
+d none usr/lib/pci 755 root bin
+f none usr/lib/pci/pcidr 555 root bin
+f none usr/lib/pci/pcidr_plugin.so 755 root bin
f none usr/lib/platexec 555 root bin
f none usr/lib/pt_chmod 4511 root bin
d none usr/lib/rcm 755 root bin
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index a99cc15380..2b952a3b5a 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -685,6 +685,8 @@ HPCSVC_OBJS += hpcsvc.o
PCIHPNEXUS_OBJS += pcihp.o
+PCIEHPCNEXUS_OBJS += pciehpc.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 0f5c43f1d1..b7f5ea902d 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/pcicfg/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/hotplug/pciehpc/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/hotplug/pcihp/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1220,6 +1224,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/hpcsvc/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/pcicfg/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/hotplug/pciehpc/%.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/busra.c b/usr/src/uts/common/io/busra.c
index c339a8b6ed..9d0062f48e 100644
--- a/usr/src/uts/common/io/busra.c
+++ b/usr/src/uts/common/io/busra.c
@@ -141,7 +141,7 @@ static int ra_map_exist(dev_info_t *dip, char *type);
static struct modlmisc modlmisc = {
&mod_miscops, /* Type of module. This one is a module */
- "Bus Resource Allocator (BUSRA) %I%", /* Name of the module. */
+ "Bus Resource Allocator (BUSRA) 1.36", /* Name of the module. */
};
static struct modlinkage modlinkage = {
@@ -931,16 +931,10 @@ pci_resource_setup(dev_info_t *dip)
(caddr_t)&bus_type, &len) != DDI_SUCCESS)
return (NDI_FAILURE);
- /* it is not a pci bus type */
+ /* it is not a pci/pci-ex bus type */
if ((strcmp(bus_type, "pci") != 0) && (strcmp(bus_type, "pciex") != 0))
return (NDI_FAILURE);
- /* read the "available" property if it is available */
- if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
- "available", (caddr_t)&regs, &rlen) != DDI_SUCCESS)
- return (NDI_FAILURE);
-
-
/*
* The pci-hotplug project addresses adding the call
* to pci_resource_setup from pci nexus driver.
@@ -958,6 +952,15 @@ pci_resource_setup(dev_info_t *dip)
}
+ /*
+ * Create empty resource maps first.
+ *
+ * NOTE: If all the allocated resources are already assigned to
+ * device(s) in the hot plug slot then "available" property may not
+ * be present. But, subsequent hot plug operation may unconfigure
+ * the device in the slot and try to free up it's resources. So,
+ * at the minimum we should create empty maps here.
+ */
if (ndi_ra_map_setup(dip, NDI_RA_TYPE_MEM) == NDI_FAILURE) {
return (NDI_FAILURE);
}
@@ -975,12 +978,17 @@ pci_resource_setup(dev_info_t *dip)
return (NDI_FAILURE);
}
-
- /* create the available resource list for both memory and io space */
- rcount = rlen / sizeof (pci_regspec_t);
- for (i = 0; i < rcount; i++) {
- switch (PCI_REG_ADDR_G(regs[i].pci_phys_hi)) {
- case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
+ /* read the "available" property if it is available */
+ if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "available", (caddr_t)&regs, &rlen) == DDI_SUCCESS) {
+ /*
+ * create the available resource list for both memory and
+ * io space
+ */
+ rcount = rlen / sizeof (pci_regspec_t);
+ for (i = 0; i < rcount; i++) {
+ switch (PCI_REG_ADDR_G(regs[i].pci_phys_hi)) {
+ case PCI_REG_ADDR_G(PCI_ADDR_MEM32):
(void) ndi_ra_free(dip,
(uint64_t)regs[i].pci_phys_low,
(uint64_t)regs[i].pci_size_low,
@@ -988,7 +996,7 @@ pci_resource_setup(dev_info_t *dip)
NDI_RA_TYPE_PCI_PREFETCH_MEM : NDI_RA_TYPE_MEM,
0);
break;
- case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
+ case PCI_REG_ADDR_G(PCI_ADDR_MEM64):
(void) ndi_ra_free(dip,
((uint64_t)(regs[i].pci_phys_mid) << 32) |
((uint64_t)(regs[i].pci_phys_low)),
@@ -998,27 +1006,27 @@ pci_resource_setup(dev_info_t *dip)
NDI_RA_TYPE_PCI_PREFETCH_MEM : NDI_RA_TYPE_MEM,
0);
break;
- case PCI_REG_ADDR_G(PCI_ADDR_IO):
+ case PCI_REG_ADDR_G(PCI_ADDR_IO):
(void) ndi_ra_free(dip,
(uint64_t)regs[i].pci_phys_low,
(uint64_t)regs[i].pci_size_low,
NDI_RA_TYPE_IO,
0);
break;
- case PCI_REG_ADDR_G(PCI_ADDR_CONFIG):
+ case PCI_REG_ADDR_G(PCI_ADDR_CONFIG):
break;
- default:
+ default:
cmn_err(CE_WARN,
"pci_resource_setup: bad addr type: %x\n",
PCI_REG_ADDR_G(regs[i].pci_phys_hi));
break;
+ }
}
+ kmem_free((caddr_t)regs, rlen);
}
- kmem_free((caddr_t)regs, rlen);
-
/*
- * Create resource map for available bus numbers if the node
+ * update resource map for available bus numbers if the node
* has available-bus-range or bus-range property.
*/
len = sizeof (struct bus_range);
@@ -1107,7 +1115,7 @@ claim_pci_busnum(dev_info_t *dip, void *arg)
(caddr_t)&bus_type, &len) != DDI_SUCCESS)
return (DDI_WALK_PRUNECHILD);
- /* it is not a pci bus type */
+ /* it is not a pci/pci-ex bus type */
if ((strcmp(bus_type, "pci") != 0) && (strcmp(bus_type, "pciex") != 0))
return (DDI_WALK_PRUNECHILD);
diff --git a/usr/src/uts/common/io/hotplug/pcicfg/pcicfg.c b/usr/src/uts/common/io/hotplug/pcicfg/pcicfg.c
index 90979eca9e..3e55b0c174 100644
--- a/usr/src/uts/common/io/hotplug/pcicfg/pcicfg.c
+++ b/usr/src/uts/common/io/hotplug/pcicfg/pcicfg.c
@@ -30,6 +30,7 @@
* PCI configurator (pcicfg)
*/
+#include <sys/sysmacros.h>
#include <sys/isa_defs.h>
#include <sys/conf.h>
@@ -41,7 +42,7 @@
#include <sys/hwconf.h>
#include <sys/ddi_impldefs.h>
-#include <sys/pci.h>
+#include <sys/pcie.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
@@ -70,6 +71,7 @@ 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
@@ -95,6 +97,14 @@ static int pcicfg_start_devno = 0; /* for Debug only */
#define PCICFG_IO_MULT 4
#define PCICFG_RANGE_LEN 2 /* Number of range entries */
+#define PCI_STAT_ECP_SUPP 0x10
+
+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_sec_reset_delay = 3000000;
+static int pcicfg_do_legacy_props = 1; /* create legacy compatible prop */
+
/*
* The following typedef is used to represent a
* 1275 "bus-range" property of a PCI Bus node.
@@ -131,7 +141,8 @@ typedef struct pcicfg_phdl pcicfg_phdl_t;
struct pcicfg_phdl {
- dev_info_t *dip; /* Associated with the attach point */
+ dev_info_t *dip; /* Associated with the bridge */
+ dev_info_t *top_dip; /* top node of the attach point */
pcicfg_phdl_t *next;
uint64_t memory_base; /* Memory base for this attach point */
@@ -237,7 +248,8 @@ static void debug(char *, uintptr_t, uintptr_t,
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);
+static int pcicfg_probe_children(dev_info_t *, uint_t, uint_t, uint_t,
+ uint_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 *);
@@ -254,7 +266,7 @@ 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_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 *);
@@ -264,7 +276,7 @@ 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 uint_t pcicfg_configure_ntbridge(dev_info_t *, uint_t, uint_t);
+static int pcicfg_configure_ntbridge(dev_info_t *, uint_t, uint_t);
static uint_t pcicfg_ntbridge_child(dev_info_t *);
static int pcicfg_indirect_map(dev_info_t *dip);
static uint_t pcicfg_get_ntbridge_child_range(dev_info_t *, uint64_t *,
@@ -277,6 +289,16 @@ static uint_t 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);
+static int pcicfg_device_type(dev_info_t *, ddi_acc_handle_t *);
+static void pcicfg_update_phdl(dev_info_t *, uint8_t, uint8_t);
+static int pcicfg_get_cap(ddi_acc_handle_t, uint8_t);
+static uint8_t pcicfg_get_nslots(dev_info_t *, ddi_acc_handle_t);
+static int pcicfg_pcie_dev(dev_info_t *, ddi_acc_handle_t);
+static int pcicfg_pcie_device_type(dev_info_t *, ddi_acc_handle_t);
+static int pcicfg_pcie_port_type(dev_info_t *, ddi_acc_handle_t);
+static int pcicfg_probe_bridge(dev_info_t *, ddi_acc_handle_t, uint_t,
+ uint_t *);
+static int pcicfg_find_resource_end(dev_info_t *, void *);
#ifdef DEBUG
static void pcicfg_dump_common_config(ddi_acc_handle_t config_handle);
@@ -317,10 +339,14 @@ static struct pcicfg_name_entry pcicfg_class_lookup [] = {
{ 0x102, "fdc" },
{ 0x103, "ipi" },
{ 0x104, "raid" },
+ { 0x105, "ata" },
+ { 0x106, "sata" },
{ 0x200, "ethernet" },
{ 0x201, "token-ring" },
{ 0x202, "fddi" },
{ 0x203, "atm" },
+ { 0x204, "isdn" },
+ { 0x206, "mcd" },
{ 0x300, "display" },
{ 0x400, "video" },
{ 0x401, "sound" },
@@ -335,6 +361,7 @@ static struct pcicfg_name_entry pcicfg_class_lookup [] = {
{ 0x606, "nubus" },
{ 0x607, "cardbus" },
{ 0x609, "pci" },
+ { 0x60a, "ib-pci" },
{ 0x700, "serial" },
{ 0x701, "parallel" },
{ 0x800, "interrupt-controller" },
@@ -346,11 +373,31 @@ static struct pcicfg_name_entry pcicfg_class_lookup [] = {
{ 0x902, "mouse" },
{ 0xa00, "dock" },
{ 0xb00, "cpu" },
+ { 0xb01, "cpu" },
+ { 0xb02, "cpu" },
+ { 0xb10, "cpu" },
+ { 0xb20, "cpu" },
+ { 0xb30, "cpu" },
+ { 0xb40, "coproc" },
{ 0xc00, "firewire" },
{ 0xc01, "access-bus" },
{ 0xc02, "ssa" },
{ 0xc03, "usb" },
{ 0xc04, "fibre-channel" },
+ { 0xc05, "smbus" },
+ { 0xc06, "ib" },
+ { 0xd00, "irda" },
+ { 0xd01, "ir" },
+ { 0xd10, "rf" },
+ { 0xd11, "btooth" },
+ { 0xd12, "brdband" },
+ { 0xd20, "802.11a" },
+ { 0xd21, "802.11b" },
+ { 0xe00, "i2o" },
+ { 0xf01, "tv" },
+ { 0xf02, "audio" },
+ { 0xf03, "voice" },
+ { 0xf04, "data" },
{ 0, 0 }
};
#endif /* _DONT_USE_1275_GENERIC_NAMES */
@@ -495,6 +542,9 @@ _init()
{
DEBUG0(" PCI configurator installed\n");
mutex_init(&pcicfg_list_mutex, NULL, MUTEX_DRIVER, NULL);
+#if defined(__sparc)
+ pcicfg_do_legacy_props = 0;
+#endif
return (mod_install(&modlinkage));
}
@@ -543,6 +593,7 @@ pcicfg_configure(dev_info_t *devi, uint_t device)
pcicfg_bus_range_t pci_bus_range;
int rv;
int circ;
+ uint_t highest_bus;
/*
* Start probing at the device specified in "device" on the
@@ -557,8 +608,7 @@ pcicfg_configure(dev_info_t *devi, uint_t device)
bus = pci_bus_range.lo; /* primary bus number of this bus node */
- ndi_devi_alloc_sleep(devi, "hp_attachment",
- (pnode_t)DEVI_SID_NODEID, &attach_point);
+ attach_point = devi;
ndi_devi_enter(devi, &circ);
for (func = 0; func < PCICFG_MAX_FUNCTION; func++) {
@@ -566,7 +616,7 @@ pcicfg_configure(dev_info_t *devi, uint_t device)
DEBUG3("Configuring [0x%x][0x%x][0x%x]\n", bus, device, func);
switch (rv = pcicfg_probe_children(attach_point,
- bus, device, func)) {
+ bus, device, func, &highest_bus)) {
case PCICFG_FAILURE:
DEBUG2("configure failed: "
"bus [0x%x] device [0x%x]\n",
@@ -576,7 +626,7 @@ pcicfg_configure(dev_info_t *devi, uint_t device)
DEBUG3("no device : bus "
"[0x%x] slot [0x%x] func [0x%x]\n",
bus, device, func);
- break;
+ continue;
default:
DEBUG3("configure: bus => [%d] "
"slot => [%d] func => [%d]\n",
@@ -593,16 +643,6 @@ pcicfg_configure(dev_info_t *devi, uint_t device)
goto cleanup;
}
- if (pcicfg_program_ap(new_device) == PCICFG_FAILURE) {
- DEBUG0("Failed to program devices\n");
- goto cleanup;
- }
-
- /*
- * Reparent the subtree from pcicfg_probe_children
- */
- (void) pcicfg_reparent_node(new_device, devi);
-
/*
* Up until now, we have detected a non transparent bridge
* (ntbridge) as a part of the generic probe code and
@@ -642,7 +682,6 @@ pcicfg_configure(dev_info_t *devi, uint_t device)
#endif
}
- (void) ndi_devi_free(attach_point);
ndi_devi_exit(devi, circ);
if (func == 0)
@@ -661,7 +700,7 @@ cleanup:
if ((new_device = pcicfg_devi_find(devi,
device, func)) == NULL) {
DEBUG0("No more devices to clean up\n");
- break;
+ continue;
}
DEBUG2("Cleaning up device [0x%x] function [0x%x]\n",
@@ -676,8 +715,6 @@ cleanup:
*/
(void) ndi_devi_offline(new_device, NDI_DEVI_REMOVE);
}
-
- (void) ndi_devi_free(attach_point);
ndi_devi_exit(devi, circ);
return (PCICFG_FAILURE);
@@ -687,7 +724,7 @@ cleanup:
* configure the child nodes of ntbridge. new_device points to ntbridge itself
*/
/*ARGSUSED*/
-static uint_t
+static int
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;
@@ -698,6 +735,7 @@ pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device)
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
@@ -706,15 +744,24 @@ pcicfg_configure_ntbridge(dev_info_t *new_device, uint_t bus, uint_t device)
* 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))
+ 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) {
+ DEBUG0("PCIe device detected\n");
+ pcie_device_type = 1;
+ }
+ pci_config_teardown(&config_handle);
/* create Bus node properties for ntbridge. */
- if (pcicfg_set_busnode_props(new_device) != PCICFG_SUCCESS) {
+ if (pcicfg_set_busnode_props(new_device, pcie_device_type)
+ != PCICFG_SUCCESS) {
DEBUG0("Failed to set busnode props\n");
return (rc);
}
@@ -1224,11 +1271,11 @@ pcicfg_ntbridge_child(dev_info_t *dip)
}
len = sizeof (int);
if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ddi_get_parent(anode),
- DDI_PROP_DONTPASS, PCICFG_DEV_CONF_MAP_PROP, (caddr_t)&val,
+ DDI_PROP_DONTPASS, PCI_DEV_CONF_MAP_PROP, (caddr_t)&val,
&len) != DDI_SUCCESS) {
DEBUG1("ntbridge child: no \"%s\" property\n",
- PCICFG_DEV_CONF_MAP_PROP);
+ PCI_DEV_CONF_MAP_PROP);
return (rc);
}
DEBUG0("ntbridge child: success\n");
@@ -1248,12 +1295,12 @@ 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)
+ 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_getprop(DDI_DEV_T_ANY, ddi_get_parent(dip),
- DDI_PROP_DONTPASS, PCICFG_BUS_CONF_MAP_PROP,
+ 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;
@@ -1330,7 +1377,7 @@ pcicfg_unconfigure(dev_info_t *devi, uint_t device)
*/
for (func = 0; func < PCICFG_MAX_FUNCTION; func++) {
if ((child_dip = pcicfg_devi_find(devi, device, func)) == NULL)
- break;
+ continue;
if (ndi_devi_offline(child_dip, NDI_UNCONFIG) == NDI_SUCCESS)
continue;
@@ -1347,7 +1394,7 @@ pcicfg_unconfigure(dev_info_t *devi, uint_t device)
/*
* Made it through all functions
*/
- break;
+ continue;
}
if (ndi_devi_online(child_dip, NDI_CONFIG) != NDI_SUCCESS) {
DEBUG0("Failed to put back devices state\n");
@@ -1363,8 +1410,8 @@ pcicfg_unconfigure(dev_info_t *devi, uint_t device)
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("No device at %x,%x\n", device, func);
+ continue;
}
DEBUG2("Tearing down device [0x%x] function [0x%x]\n",
@@ -1882,6 +1929,12 @@ pcicfg_device_assign(dev_info_t *dip)
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;
@@ -1981,8 +2034,7 @@ pcicfg_allocate_chunk(dev_info_t *dip)
int circular;
/*
- * This should not find an existing entry - so
- * it will create a new one.
+ * the handle so far has the slots information, add memory/IO reqts.
*/
phdl = pcicfg_find_phdl(dip);
ASSERT(phdl);
@@ -2024,12 +2076,6 @@ pcicfg_allocate_chunk(dev_info_t *dip)
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);
-
/*
* Check if the Bridge is a child of
* ntbridge, If yes, then allocate IO space from the hole allocated
@@ -2049,6 +2095,8 @@ pcicfg_allocate_chunk(dev_info_t *dip)
}
alen = mem_request->ra_len;
} else
+ mem_request->ra_len =
+ PCICFG_ROUND_UP(mem_request->ra_len, PCICFG_MEMGRAN);
if (ndi_ra_alloc(ddi_get_parent(dip),
mem_request, &mem_answer, &alen,
NDI_RA_TYPE_MEM, NDI_RA_PASS) != NDI_SUCCESS) {
@@ -2085,6 +2133,9 @@ pcicfg_allocate_chunk(dev_info_t *dip)
pphdl->io_last = io_last;
alen = io_request->ra_len;
} else
+ io_request->ra_len =
+ PCICFG_ROUND_UP(io_request->ra_len, PCICFG_IOGRAN);
+
if (ndi_ra_alloc(ddi_get_parent(dip), io_request, &io_answer,
&alen, NDI_RA_TYPE_IO, NDI_RA_PASS) != NDI_SUCCESS) {
@@ -2106,7 +2157,6 @@ pcicfg_allocate_chunk(dev_info_t *dip)
phdl->memory_base, phdl->memory_len);
DEBUG2("IO BASE = [0x%x] length [0x%x]\n",
phdl->io_base, phdl->io_len);
-
return (PCICFG_SUCCESS);
}
@@ -2269,7 +2319,6 @@ pcicfg_sum_resources(dev_info_t *dip, void *hdl)
entry->highest_bus =
pci_config_get8(handle, PCI_BCNF_SECBUS);
}
-
(void) pcicfg_config_teardown(&handle);
entry->error = PCICFG_FAILURE;
return (DDI_WALK_CONTINUE);
@@ -2354,11 +2403,23 @@ pcicfg_free_bridge_resources(dev_info_t *dip)
int i;
- if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
+ if ((i = ddi_getlongprop(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "ranges", (caddr_t)&ranges,
- &length) != DDI_PROP_SUCCESS) {
+ &length)) != DDI_PROP_SUCCESS) {
DEBUG0("Failed to read ranges property\n");
- return (PCICFG_FAILURE);
+ 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++) {
@@ -2407,7 +2468,8 @@ pcicfg_free_bridge_resources(dev_info_t *dip)
}
}
- kmem_free(ranges, length);
+ if (length)
+ kmem_free(ranges, length);
if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "bus-range", (caddr_t)&bus,
@@ -2799,6 +2861,7 @@ pcicfg_update_reg_prop(dev_info_t *dip, uint32_t regvalue, uint_t reg_offset)
bcopy(reg, newreg, rlen);
bcopy(&addition, newreg + rlen, sizeof (pci_regspec_t));
+ DEBUG3("updating BAR@off %x with %x,%x\n", reg_offset, hiword, size);
/*
* Write out the new "reg" property
*/
@@ -2838,16 +2901,17 @@ pcicfg_device_off(ddi_acc_handle_t config_handle)
* header of the PCI device
*/
static int
-pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle)
+pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle,
+ uint8_t pcie_dev)
{
- int ret;
+ 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) {
+ 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) {
@@ -2901,13 +2965,17 @@ pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle)
* 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 ((!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 (pci_config_get16(config_handle, PCI_CONF_STAT) & PCI_STAT_66MHZ) {
+ 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);
@@ -2962,15 +3030,40 @@ pcicfg_set_standard_props(dev_info_t *dip, ddi_acc_handle_t config_handle)
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;
+ /* create the property only if slotnum set correctly? */
+ 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)
+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", "pci")) != DDI_SUCCESS) {
+ "device_type", device_type)) != DDI_SUCCESS) {
return (ret);
}
if ((ret = ndi_prop_update_int(DDI_DEV_T_NONE, dip,
@@ -2985,40 +3078,52 @@ pcicfg_set_busnode_props(dev_info_t *dip)
}
static int
-pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle)
+pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle,
+ uint8_t pcie_dev)
{
int ret;
-#ifndef _DONT_USE_1275_GENERIC_NAMES
- uint32_t wordval;
-#endif
char *name;
- char buffer[64];
- uint32_t classcode;
- char *compat[8];
+ char buffer[64], pprefix[8], nprefix[8];
+ uint16_t classcode;
+ uint8_t revid, pif, pclass, psubclass;
+ char *compat[24];
int i;
int n;
- uint16_t sub_vid, sub_sid;
+ uint16_t sub_vid, sub_sid, vid, did;
#ifdef _EFCODE_WORKAROUND
char nmbuffer[32];
#endif
+ /* set the property prefix based on the device type */
+ if (pcie_dev) {
+ (void) sprintf(pprefix, "pciex");
+ } else
+ (void) sprintf(pprefix, "pci");
+
+ /* set the prefix right for name property */
+#if defined(__sparc)
+ (void) sprintf(nprefix, pprefix);
+#else
+ /* x86 platforms need to go with pci for upgrade purposes */
+ (void) sprintf(nprefix, "pci");
+#endif
/*
* 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
-
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) {
- (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));
- }
+ 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);
+
+ if (!sub_sid) /* XXX - different from pcicfg.e */
+ (void) sprintf(buffer, "%s%x,%x", nprefix, vid, did);
+ else
+ (void) sprintf(buffer, "%s%x,%x", nprefix, sub_vid, sub_sid);
/*
* In some environments, trying to use "generic" 1275 names is
@@ -3029,7 +3134,7 @@ pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle)
#ifdef _DONT_USE_1275_GENERIC_NAMES
name = buffer;
#else
- if ((name = pcicfg_get_class_name(wordval>>8)) == NULL) {
+ if ((name = pcicfg_get_class_name(classcode)) == NULL) {
/*
* Set name to the above fabricated name
*/
@@ -3062,28 +3167,76 @@ pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle)
(void) strcpy(compat[n++], nmbuffer);
}
#endif
- compat[n] = kmem_alloc(strlen(buffer) + 1, KM_SLEEP);
- (void) strcpy(compat[n++], buffer);
/*
- * Add in the VendorID/DeviceID compatible name.
+ * 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
+ * Add legacy entries for compatibility with legacy devices and OS
+ * for x86.
+ * pciVVVV,DDDD.SSSS.ssss.RR
+ * pciVVVV,DDDD.SSSS.ssss
+ * pciSSSS.ssss
+ * pciVVVV,DDDD.RR
+ * pciVVVV,DDDD
+ * pciclass,CCSSPP
+ * pciclass,CCSS
*/
- (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);
+ do {
+ /* 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 && pcicfg_do_legacy_props) {
+ (void) sprintf(buffer, "pci%x,%x", sub_vid, sub_sid);
+ 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));
+ /* 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);
- /*
- * Add in the Classcode
- */
- (void) sprintf(buffer, "pciclass,%06x", classcode);
- 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 (!pcie_dev)
+ break;
+
+ /* also add compatible names using "pci" prefix */
+ (void) sprintf(pprefix, "pci");
+ pcie_dev = 0;
+
+ } while (pcicfg_do_legacy_props);
if ((ret = ndi_prop_update_string_array(DDI_DEV_T_NONE, dip,
"compatible", (char **)compat, n)) != DDI_SUCCESS) {
@@ -3102,8 +3255,10 @@ pcicfg_set_childnode_props(dev_info_t *dip, ddi_acc_handle_t config_handle)
*/
static void
pcicfg_set_bus_numbers(ddi_acc_handle_t config_handle,
-uint_t primary, uint_t secondary)
+uint_t primary, uint_t secondary, uint_t subordinate)
{
+ DEBUG3("Setting bridge bus-range %d,%d,%d\n", primary, secondary,
+ subordinate);
/*
* Primary bus#
*/
@@ -3118,7 +3273,7 @@ uint_t primary, uint_t secondary)
* Set the subordinate bus number to ff in order to pass through any
* type 1 cycle with a bus number higher than the secondary bus#
*/
- pci_config_put8(config_handle, PCI_BCNF_SUBBUS, 0xFF);
+ pci_config_put8(config_handle, PCI_BCNF_SUBBUS, subordinate);
}
/*
@@ -3138,8 +3293,10 @@ pcicfg_setup_bridge(pcicfg_phdl_t *entry,
*/
pci_config_put16(handle, PCI_BCNF_BCNTRL,
pci_config_get16(handle, PCI_BCNF_BCNTRL) | 0x40);
+ drv_usecwait(1000);
pci_config_put16(handle, PCI_BCNF_BCNTRL,
pci_config_get16(handle, PCI_BCNF_BCNTRL) & ~0x40);
+ drv_usecwait(1000);
/*
* Program the memory base register with the
@@ -3172,6 +3329,12 @@ pcicfg_setup_bridge(pcicfg_phdl_t *entry,
* Needs to be set to this value
*/
pci_config_put8(handle, PCI_CONF_ILINE, 0xf);
+
+ /*
+ * XXX - may be delay should be used since noone configures
+ * devices in the interrupt context
+ */
+ drv_usecwait(pcicfg_sec_reset_delay); /* 1 sec wait */
}
static void
@@ -3232,20 +3395,14 @@ pcicfg_update_bridge(pcicfg_phdl_t *entry,
static int
pcicfg_probe_children(dev_info_t *parent, uint_t bus,
- uint_t device, uint_t func)
+ 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;
-
- int i, j;
- ndi_ra_request_t req;
- uint64_t next_bus;
- uint64_t blen;
+ uint8_t header_type, pcie_dev = 0;
+ int i;
uint32_t request;
- uint_t new_bus;
int ret;
- int circ;
/*
* This node will be put immediately below
@@ -3253,7 +3410,6 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus,
* be filled in or freed up based on further probing.
*/
- ndi_devi_enter(parent, &circ);
ndi_devi_alloc_sleep(parent, DEVI_PSEUDO_NEXNAME,
(pnode_t)DEVI_SID_NODEID, &new_child);
@@ -3261,14 +3417,13 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus,
device, func) != DDI_SUCCESS) {
DEBUG0("pcicfg_probe_children():"
"Failed to add candidate REG\n");
- goto failedchild;
+ 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():"
@@ -3283,11 +3438,17 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus,
*/
(void) pcicfg_device_off(config_handle);
+ /* check if we are PCIe device */
+ if (pcicfg_pcie_dev(new_child, config_handle) == 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) != PCICFG_SUCCESS) {
+ if (pcicfg_set_standard_props(new_child, config_handle,
+ pcie_dev) != PCICFG_SUCCESS) {
DEBUG0("Failed to set standard properties\n");
goto failedchild;
}
@@ -3295,8 +3456,8 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus,
/*
* Child node properties NOTE: Both for PCI-PCI bridge and child node
*/
- if (pcicfg_set_childnode_props(new_child,
- config_handle) != PCICFG_SUCCESS) {
+ if (pcicfg_set_childnode_props(new_child, config_handle,
+ pcie_dev) != PCICFG_SUCCESS) {
goto failedchild;
}
@@ -3305,61 +3466,28 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus,
/*
* If this is not a multi-function card only probe function zero.
*/
- if (!(header_type & PCI_HEADER_MULTI) && (func != 0)) {
+ 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);
}
+ /*
+ * 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);
- /*
- * 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");
+ if (pcicfg_probe_bridge(new_child, config_handle,
+ bus, highest_bus) != PCICFG_SUCCESS) {
+ (void) pcicfg_free_bridge_resources(new_child);
goto failedchild;
}
- new_bus = next_bus;
-
- DEBUG1("NEW bus found ->[%d]\n", new_bus);
-
- (void) pcicfg_set_bus_numbers(config_handle,
- 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 {
@@ -3434,16 +3562,17 @@ pcicfg_probe_children(dev_info_t *parent, uint_t bus,
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);
- /*
- * Attach the child to its parent
- */
- (void) i_ndi_config_node(new_child, DS_LINKED, 0);
- ndi_devi_exit(parent, circ);
-
return (PCICFG_SUCCESS);
failedchild:
@@ -3455,11 +3584,622 @@ failedchild:
failedconfig:
(void) ndi_devi_free(new_child);
- ndi_devi_exit(parent, circ);
-
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, num_slots;
+ 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;
+ 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;
+ /*
+ * limit the boundlen,len to a 32b quantity. It should be Ok to
+ * lose alignment-based-size of resource due to this.
+ */
+ req.ra_boundlen = PCICFG_4GIG_LIMIT;
+ req.ra_len = PCICFG_4GIG_LIMIT; /* 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");
+ 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);
+
+ /* 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_probe_children(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
+ /*EMPTY*/
+ DEBUG3("Failed to configure bus "
+ "[0x%x] device [0x%x] "
+ "func [0x%x]\n", new_bus, i, j);
+ break;
+ }
+ }
+ /* if any function fails to be configured, no need to proceed */
+ if (rval != PCICFG_NODEVICE)
+ 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 = mem_answer;
+ phdl.io_base = 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)(%" PRIx64 ",%"
+ PRIx64 ")", 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)(%" PRIx64 ",%" PRIx64 ")",
+ 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)(%" PRIx64 ",%"
+ PRIx64 ")", 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)(%" PRIx64 ",%" PRIx64 ")",
+ 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 may have already taken care of it. It has been
+ * checked that there are no other reasons for failure other than
+ * map itself being non-existent.
+ */
+ 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 (PCICFG_SUCCESS);
+}
+
+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);
+ }
+}
+
/*
* Make "parent" be the parent of the "child" dip
*/
@@ -3526,27 +4266,6 @@ pcicfg_config_setup(dev_info_t *dip, ddi_acc_handle_t *handle)
anode = dip;
- /*
- * Find the attachment point node
- */
- while ((anode != NULL) && (strcmp(ddi_binding_name(anode),
- "hp_attachment") != 0)) {
- anode = ddi_get_parent(anode);
- }
-
- if (anode == NULL) {
- DEBUG0("Tree not in PROBE state\n");
- kmem_free((caddr_t)reg, rlen);
- return (PCICFG_FAILURE);
- }
-
- if (ndi_prop_update_int_array(DDI_DEV_T_NONE, anode,
- "reg", (int *)reg, 5)) {
- DEBUG0("Failed to update reg property...\n");
- kmem_free((caddr_t)reg, rlen);
- return (PCICFG_FAILURE);
- }
-
if (pcicfg_indirect_map(anode) == DDI_SUCCESS)
flags |= PCICFG_CONF_INDIRECT_MAP;
@@ -3578,9 +4297,13 @@ pcicfg_config_setup(dev_info_t *dip, ddi_acc_handle_t *handle)
DEBUG1("NO DEVICEFOUND, read %x\n", tmp);
ret = PCICFG_NODEVICE;
} else {
- DEBUG1("DEVICEFOUND, read %x\n", tmp);
- ret = PCICFG_SUCCESS;
-
+ 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");
@@ -3826,6 +4549,7 @@ pcicfg_fcode_name(dev_info_t *dip, ddi_acc_handle_t config_handle, char *buffer)
}
return (rc);
}
+
/*
* Called from pcicfg_set_childnode_props(), this functions returns
* the value of FCode "compatible" property for a list of FCode devices
@@ -3923,3 +4647,125 @@ pcicfg_create_ac_child(dev_info_t *dip)
return (DDI_SUCCESS);
}
#endif /* _EFCODE_WORKAROUND */
+
+/*
+ * 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_ECP_SUPP)) {
+ 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_get8(handle, cap_id_loc +
+ PCI_CAP_ID_REGS_OFF) &
+ 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 + 2);
+ num_slots = PCI_CAPSLOT_NSLOTS(esr_reg);
+ }
+ /* XXX - need to cover PCI-PCIe bridge with n slots */
+ return (num_slots);
+}
+
+/*ARGSUSED*/
+static int
+pcicfg_pcie_dev(dev_info_t *dip, ddi_acc_handle_t handle)
+{
+ /* get parent device's device_type property */
+ char *device_type;
+ int val;
+ 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) {
+ DEBUG2("device_type property missing for %s#%d",
+ ddi_get_name(pdip), ddi_get_instance(pdip));
+ return (DDI_FAILURE);
+ }
+ DEBUG1("device_type=<%s>\n", device_type);
+
+ val = DDI_FAILURE;
+ if (strcmp(device_type, "pciex") == 0)
+ val = DDI_SUCCESS;
+ ddi_prop_free(device_type);
+ return (val);
+}
+
+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_ROOT) ||
+ (port_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE))
+ return (DDI_SUCCESS);
+
+ return (DDI_FAILURE);
+
+}
+
+/*ARGSUSED*/
+static int
+pcicfg_pcie_port_type(dev_info_t *dip, ddi_acc_handle_t handle)
+{
+ int port_type = -1;
+ uint_t cap_loc;
+
+ /* Note: need to look at the port type information here */
+ 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);
+}
diff --git a/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c b/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c
new file mode 100644
index 0000000000..17cd0f9018
--- /dev/null
+++ b/usr/src/uts/common/io/hotplug/pciehpc/pciehpc.c
@@ -0,0 +1,1764 @@
+/*
+ * 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"
+
+/*
+ * PCIEHPC - The Standard PCI Express HotPlug Controller driver module. This
+ * driver can be used with PCI Express HotPlug controllers that
+ * are compatible with the PCI Express ver 1.0a specification.
+ */
+
+#include <sys/types.h>
+#include <sys/note.h>
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/debug.h>
+#include <sys/vtrace.h>
+#include <sys/modctl.h>
+#include <sys/autoconf.h>
+#include <sys/varargs.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/pci.h>
+#include <sys/time.h>
+#include <sys/callb.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/hotplug/pci/pciehpc_impl.h>
+
+/*
+ * Local data/functions
+ */
+
+/* mutex to protect pciehpc_head list */
+static kmutex_t pciehpc_list_mutex;
+
+/* pointer to linked list of pciehpc structures */
+static pciehpc_t *pciehpc_head = NULL;
+
+/* mutex to protect init/uninit controllers */
+static kmutex_t pciehpc_init_mutex;
+static int pciehpc_init_count = 0; /* count of pciehpc instances in use */
+
+static pciehpc_t *pciehpc_create_soft_state(dev_info_t *dip);
+static pciehpc_t *pciehpc_get_soft_state(dev_info_t *dip);
+static void pciehpc_destroy_soft_state(dev_info_t *dip);
+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);
+
+#ifdef DEBUG
+int pciehpc_debug = 0;
+static void pciehpc_dump_hpregs(pciehpc_t *ctrl_p);
+#endif
+
+/*
+ * Module linkage information for the kernel.
+ */
+extern struct mod_ops mod_miscops;
+static struct modlmisc modlmisc =
+{
+ &mod_miscops,
+ "PCIe hotplug driver v%I%",
+};
+
+static struct modlinkage modlinkage =
+{
+ MODREV_1,
+ &modlmisc,
+ NULL
+};
+
+
+int
+_init(void)
+{
+ int error;
+
+ PCIEHPC_DEBUG3((CE_NOTE, "pciehpc: _init() called\n"));
+ mutex_init(&pciehpc_list_mutex, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&pciehpc_init_mutex, NULL, MUTEX_DRIVER, NULL);
+ if ((error = mod_install(&modlinkage)) != 0) {
+ mutex_destroy(&pciehpc_init_mutex);
+ mutex_destroy(&pciehpc_list_mutex);
+ }
+
+ return (error);
+}
+
+int
+_fini(void)
+{
+ int error;
+
+ PCIEHPC_DEBUG3((CE_NOTE, "pciehpc: _fini() called\n"));
+
+ mutex_enter(&pciehpc_init_mutex);
+ if (pciehpc_init_count != 0) {
+ mutex_exit(&pciehpc_init_mutex);
+ return (EBUSY);
+ }
+ error = mod_remove(&modlinkage);
+ if (error != 0) {
+ mutex_exit(&pciehpc_init_mutex);
+ return (error);
+ }
+ mutex_destroy(&pciehpc_list_mutex);
+ mutex_destroy(&pciehpc_init_mutex);
+ return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ PCIEHPC_DEBUG3((CE_NOTE, "pciehpc: _info() called\n"));
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*
+ * pciehpc_init()
+ *
+ * Initialize Hot Plug Controller if present. The arguments are:
+ * dip - Devinfo node pointer to the hot plug bus node
+ * regops - register ops to access HPC registers for non-standard
+ * HPC hw implementations (e.g: HPC in host PCI-E brdiges)
+ * This is NULL for standard HPC in PCIe bridges.
+ * Returns:
+ * DDI_SUCCESS for successful HPC initialization
+ * DDI_FAILURE for errors or if HPC hw not found
+ */
+int
+pciehpc_init(dev_info_t *dip, pciehpc_regops_t *regops)
+{
+ pciehpc_t *ctrl_p;
+
+ PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_init() called (dip=%p)",
+ (void *)dip));
+
+ mutex_enter(&pciehpc_init_mutex);
+
+ /* Make sure that it is not already initialized */
+ if (pciehpc_get_soft_state(dip) != NULL) {
+ PCIEHPC_DEBUG((CE_WARN,
+ "%s%d: pciehpc instance already initialized!",
+ ddi_driver_name(dip), ddi_get_instance(dip)));
+ mutex_exit(&pciehpc_init_mutex);
+ return (DDI_SUCCESS);
+ }
+
+ /* allocate a new soft state structure */
+ ctrl_p = pciehpc_create_soft_state(dip);
+
+ /* get PCI device info */
+ pciehpc_dev_info(ctrl_p);
+
+ /* setup access handle for HPC regs */
+ if (regops != NULL) {
+ /* HPC access is non-standard; use the supplied reg ops */
+ ctrl_p->regops = *regops;
+ } else {
+ /* standard HPC in a PCIe bridge */
+ if (pciehpc_regs_setup(dip, 0, 0, &ctrl_p->regs_base,
+ &ctrl_p->cfghdl) != DDI_SUCCESS)
+ goto cleanup;
+ }
+
+ /*
+ * Set the platform specific hot plug mode.
+ */
+ ctrl_p->hp_mode = PCIEHPC_NATIVE_HP_MODE; /* default is Native mode */
+ ctrl_p->ops.init_hpc_hw = pciehpc_hpc_init;
+ ctrl_p->ops.init_hpc_slotinfo = pciehpc_slotinfo_init;
+ ctrl_p->ops.disable_hpc_intr = pciehpc_disable_intr;
+ ctrl_p->ops.enable_hpc_intr = pciehpc_enable_intr;
+ ctrl_p->ops.uninit_hpc_hw = pciehpc_hpc_uninit;
+ ctrl_p->ops.uninit_hpc_slotinfo = pciehpc_slotinfo_uninit;
+ ctrl_p->ops.probe_hpc = pciehpc_probe_hpc;
+
+#if defined(__i386) || defined(__amd64)
+ pciehpc_update_ops(ctrl_p);
+#endif
+ if (regops == NULL) { /* it is a standard HPC in a PCIe bridge */
+ /* make sure we really have a hot plug controller */
+ if ((ctrl_p->ops.probe_hpc)(ctrl_p) != DDI_SUCCESS)
+ goto cleanup1;
+ }
+
+ /* initialize hot plug controller hw */
+ if ((ctrl_p->ops.init_hpc_hw)(ctrl_p) != DDI_SUCCESS)
+ goto cleanup1;
+
+ /* initialize slot information soft state structure */
+ if ((ctrl_p->ops.init_hpc_slotinfo)(ctrl_p) != DDI_SUCCESS)
+ goto cleanup2;
+
+ /* register the hot plug slot with HPS framework */
+ if (pciehpc_register_slot(ctrl_p) != DDI_SUCCESS)
+ goto cleanup3;
+
+ /* HPC initialization is complete now */
+ ctrl_p->soft_state |= PCIEHPC_SOFT_STATE_INITIALIZED;
+ ctrl_p->soft_state &= ~PCIEHPC_SOFT_STATE_UNINITIALIZED;
+
+#ifdef DEBUG
+ /* For debug, dump the HPC registers */
+ if (pciehpc_debug > 2)
+ pciehpc_dump_hpregs(ctrl_p);
+#endif
+
+ /* enable hot plug interrupts/event */
+ (void) (ctrl_p->ops.enable_hpc_intr)(ctrl_p);
+
+ pciehpc_init_count++;
+
+ mutex_exit(&pciehpc_init_mutex);
+
+ return (DDI_SUCCESS);
+
+cleanup3:
+ (void) (ctrl_p->ops.uninit_hpc_slotinfo)(ctrl_p);
+
+cleanup2:
+ (void) (ctrl_p->ops.uninit_hpc_hw)(ctrl_p);
+
+cleanup1:
+ pciehpc_regs_teardown(&ctrl_p->cfghdl);
+
+cleanup:
+ pciehpc_destroy_soft_state(dip);
+ mutex_exit(&pciehpc_init_mutex);
+ return (DDI_FAILURE);
+}
+
+/*
+ * Uninitialize HPC soft state structure and free up any resources
+ * used for the HPC instance.
+ */
+int
+pciehpc_uninit(dev_info_t *dip)
+{
+ pciehpc_t *ctrl_p;
+
+ PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_uninit() called (dip=%p)\n",
+ (void *)dip));
+
+ mutex_enter(&pciehpc_init_mutex);
+
+ /* get the soft state structure for this dip */
+ if ((ctrl_p = pciehpc_get_soft_state(dip)) == NULL) {
+ mutex_exit(&pciehpc_init_mutex);
+ return (DDI_FAILURE);
+ }
+
+ /* disable interrupts */
+ (void) (ctrl_p->ops.disable_hpc_intr)(ctrl_p);
+
+ /* unregister the slot */
+ (void) pciehpc_unregister_slot(ctrl_p);
+
+ /* uninit any slot info data structures */
+ (void) (ctrl_p->ops.uninit_hpc_slotinfo)(ctrl_p);
+
+ /* uninitialize hpc, remove interrupt handler, etc. */
+ (void) (ctrl_p->ops.uninit_hpc_hw)(ctrl_p);
+
+ /* free up the HPC register mapping */
+ pciehpc_regs_teardown(&ctrl_p->cfghdl);
+
+ /* destroy the soft state structure */
+ pciehpc_destroy_soft_state(dip);
+
+ ASSERT(pciehpc_init_count != 0);
+
+ pciehpc_init_count--;
+
+ mutex_exit(&pciehpc_init_mutex);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Probe for the inband PCI-E hot plug controller. Returns DDI_SUCCESS
+ * if found. This function works only for the standard PCI-E bridge
+ * that has inband hot plug controller.
+ *
+ * NOTE: This won't work for Host-PCIE bridges.
+ */
+int
+pciehpc_probe_hpc(pciehpc_t *ctrl_p)
+{
+ uint8_t cap_ptr;
+ uint8_t cap_id;
+ uint16_t status;
+
+ /* Read the PCI configuration status register. */
+ status = pciehpc_reg_get16(ctrl_p, PCI_CONF_STAT);
+
+ /* check for capabilities list */
+ if (!(status & PCI_STAT_CAP)) {
+ /* no capabilities list */
+ return (DDI_FAILURE);
+ }
+
+ /* Get a pointer to the PCI capabilities list. */
+ cap_ptr = pciehpc_reg_get8(ctrl_p, PCI_BCNF_CAP_PTR);
+ cap_ptr &= 0xFC; /* mask off reserved bits */
+
+ /*
+ * Walk thru the capabilities list looking for PCI Express capability
+ * structure.
+ */
+ while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
+ cap_id = pciehpc_reg_get8(ctrl_p, (uint_t)cap_ptr);
+
+ PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_probe_hpc() capability @"
+ " pointer=%02x (id=%02x)\n", cap_ptr, cap_id));
+
+ if (cap_id == PCI_CAP_ID_PCI_E) {
+ uint32_t slot_cap;
+
+ /* Read the PCI Express Slot Capabilities Register */
+ slot_cap = pciehpc_reg_get32(ctrl_p,
+ (uint_t)cap_ptr + PCIE_SLOTCAP);
+
+ /* Does it have PCI Express HotPlug capability? */
+ if (slot_cap & PCIE_SLOTCAP_HP_CAPABLE) {
+ /* Save the offset to PCI Express Capabilities structure */
+ ctrl_p->pcie_caps_reg_offset = cap_ptr;
+ return (DDI_SUCCESS);
+ }
+ }
+
+ /* Get the pointer to the next capability */
+ cap_ptr = pciehpc_reg_get8(ctrl_p, (uint_t)cap_ptr + 1);
+ cap_ptr &= 0xFC;
+ }
+
+ return (DDI_FAILURE);
+}
+
+/*
+ * Setup slot information for use with HPS framework.
+ */
+int
+pciehpc_slotinfo_init(pciehpc_t *ctrl_p)
+{
+ uint32_t slot_capabilities;
+ pciehpc_slot_t *p = &ctrl_p->slot;
+
+ /*
+ * setup HPS framework slot ops structure
+ */
+ p->slot_ops.hpc_version = HPC_SLOT_OPS_VERSION;
+ p->slot_ops.hpc_op_connect = pciehpc_slot_connect;
+ p->slot_ops.hpc_op_disconnect = pciehpc_slot_disconnect;
+ p->slot_ops.hpc_op_insert = NULL;
+ p->slot_ops.hpc_op_remove = NULL;
+ p->slot_ops.hpc_op_control = pciehpc_slot_control;
+
+ /*
+ * setup HPS framework slot information structure
+ */
+ p->slot_info.version = HPC_SLOT_OPS_VERSION;
+ p->slot_info.slot_type = HPC_SLOT_TYPE_PCIE;
+ p->slot_info.slot_flags =
+ HPC_SLOT_CREATE_DEVLINK | HPC_SLOT_NO_AUTO_ENABLE;
+ p->slot_info.pci_slot_capabilities = HPC_SLOT_64BITS;
+ /* the device number is fixed as 0 as per the spec */
+ p->slot_info.pci_dev_num = 0;
+
+ /* read Slot Capabilities Register */
+ slot_capabilities = pciehpc_reg_get32(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCAP);
+
+ /* set slot-name/slot-number info */
+ pciehpc_set_slot_name(ctrl_p);
+
+ /* check if Attn Button present */
+ ctrl_p->has_attn = (slot_capabilities & PCIE_SLOTCAP_ATTN_BUTTON) ?
+ B_TRUE : B_FALSE;
+
+ /* check if Manual Retention Latch sensor present */
+ ctrl_p->has_mrl = (slot_capabilities & PCIE_SLOTCAP_MRL_SENSOR) ?
+ B_TRUE : B_FALSE;
+
+ /*
+ * PCI-E (draft) version 1.1 defines EMI Lock Present bit
+ * in Slot Capabilities register. Check for it.
+ */
+ ctrl_p->has_emi_lock = (slot_capabilities &
+ PCIE_SLOTCAP_EMI_LOCK_PRESENT) ? B_TRUE : B_FALSE;
+
+ /* initialize synchronization conditional variable */
+ cv_init(&ctrl_p->slot.cmd_comp_cv, NULL, CV_DRIVER, NULL);
+ ctrl_p->slot.command_pending = B_FALSE;
+
+ /* setup thread for handling ATTN button events */
+ if (ctrl_p->has_attn) {
+ PCIEHPC_DEBUG3((CE_NOTE,
+ "pciehpc_slotinfo_init: setting up ATTN button event "
+ "handler thread for slot %d\n", ctrl_p->slot.slotNum));
+ cv_init(&ctrl_p->slot.attn_btn_cv, NULL, CV_DRIVER, NULL);
+ ctrl_p->slot.attn_btn_pending = B_FALSE;
+ ctrl_p->slot.attn_btn_threadp = thread_create(NULL, 0,
+ pciehpc_attn_btn_handler,
+ (void *)ctrl_p, 0, &p0, TS_RUN, minclsyspri);
+ ctrl_p->slot.attn_btn_thread_exit = B_FALSE;
+ }
+
+ /* get current slot state from the hw */
+ pciehpc_get_slot_state(ctrl_p);
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+int
+pciehpc_slotinfo_uninit(pciehpc_t *ctrl_p)
+{
+ cv_destroy(&ctrl_p->slot.cmd_comp_cv);
+
+ if (ctrl_p->slot.attn_btn_threadp != NULL) {
+ mutex_enter(&ctrl_p->pciehpc_mutex);
+ ctrl_p->slot.attn_btn_thread_exit = B_TRUE;
+ cv_signal(&ctrl_p->slot.attn_btn_cv);
+ PCIEHPC_DEBUG3((CE_NOTE,
+ "pciehpc_slotinfo_uninit: waiting for ATTN thread exit\n"));
+ cv_wait(&ctrl_p->slot.attn_btn_cv, &ctrl_p->pciehpc_mutex);
+ PCIEHPC_DEBUG3((CE_NOTE,
+ "pciehpc_slotinfo_uninit: ATTN thread exit\n"));
+ cv_destroy(&ctrl_p->slot.attn_btn_cv);
+ ctrl_p->slot.attn_btn_threadp = NULL;
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Get the current state of the slot from the hw.
+ */
+void
+pciehpc_get_slot_state(pciehpc_t *ctrl_p)
+{
+ pciehpc_slot_t *p = &ctrl_p->slot;
+ uint16_t control, status;
+
+ /* read the Slot Control Register */
+ control = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+
+ p->fault_led_state = HPC_LED_OFF; /* no fault led */
+ p->active_led_state = HPC_LED_OFF; /* no active led */
+
+ /* read the current Slot Status Register */
+ status = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
+
+ /* get POWER led state */
+ p->power_led_state =
+ pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control));
+
+ /* get ATTN led state */
+ p->attn_led_state =
+ pciehpc_led_state_to_hpc(pcie_slotctl_attn_indicator_get(control));
+
+ if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED))
+ /* no device present; slot is empty */
+ p->slot_state = HPC_SLOT_EMPTY;
+
+ else if (!(control & PCIE_SLOTCTL_PWR_CONTROL))
+ /* device is present and powered up */
+ p->slot_state = HPC_SLOT_CONNECTED;
+ else
+ /* device is present and powered down */
+ p->slot_state = HPC_SLOT_DISCONNECTED;
+}
+
+
+/*
+ * pciehpc_regs_setup()
+ *
+ * Setup PCI-E config registers for DDI access functions.
+ *
+ * Note: This is same as pci_config_setup() except that this may be
+ * used to map specific reg set with an offset in the case of host
+ * PCI-E bridges.
+ */
+int
+pciehpc_regs_setup(dev_info_t *dip, uint_t rnum, offset_t off,
+ caddr_t *addrp, ddi_acc_handle_t *handle)
+{
+ ddi_device_acc_attr_t attr;
+
+ attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
+ attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
+ attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
+
+ /* Check for fault management capabilities */
+ if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(dip)))
+ attr.devacc_attr_access = DDI_FLAGERR_ACC;
+
+ return (ddi_regs_map_setup(dip, rnum, addrp, off, 0, &attr, handle));
+}
+
+/*
+ * pciehpc_regs_teardown()
+ *
+ * Unmap config register set.
+ *
+ * Note: This is same as pci_config_teardown() function.
+ */
+void
+pciehpc_regs_teardown(ddi_acc_handle_t *handle)
+{
+ ddi_regs_map_free(handle);
+}
+
+/*
+ * Find the soft state structure for the HPC associated with the dip.
+ */
+static pciehpc_t *
+pciehpc_get_soft_state(dev_info_t *dip)
+{
+ pciehpc_t *ctrl_p;
+
+ mutex_enter(&pciehpc_list_mutex);
+
+ ctrl_p = pciehpc_head;
+
+ while (ctrl_p) {
+ if (ctrl_p->dip == dip) {
+ mutex_exit(&pciehpc_list_mutex);
+ return (ctrl_p);
+ }
+ ctrl_p = ctrl_p->nextp;
+ }
+
+ mutex_exit(&pciehpc_list_mutex);
+
+ return (NULL);
+}
+
+/*
+ * Allocate a soft state structure for the HPC associated with this dip.
+ */
+static pciehpc_t *
+pciehpc_create_soft_state(dev_info_t *dip)
+{
+ pciehpc_t *ctrl_p;
+
+ ctrl_p = kmem_zalloc(sizeof (pciehpc_t), KM_SLEEP);
+
+ ctrl_p->dip = dip;
+
+ mutex_enter(&pciehpc_list_mutex);
+ ctrl_p->nextp = pciehpc_head;
+ pciehpc_head = ctrl_p;
+ ctrl_p->soft_state = PCIEHPC_SOFT_STATE_UNINITIALIZED;
+ mutex_exit(&pciehpc_list_mutex);
+
+ return (ctrl_p);
+}
+
+/*
+ * Remove the HPC soft state structure from the linked list.
+ */
+static void
+pciehpc_destroy_soft_state(dev_info_t *dip)
+{
+ pciehpc_t **pp;
+ pciehpc_t *p;
+
+ mutex_enter(&pciehpc_list_mutex);
+ pp = &pciehpc_head;
+ while ((p = *pp) != NULL) {
+ if (p->dip == dip) {
+ *pp = p->nextp;
+ kmem_free(p, sizeof (pciehpc_t));
+ break;
+ }
+ pp = &(p->nextp);
+ }
+ mutex_exit(&pciehpc_list_mutex);
+}
+
+/*
+ * convert LED state from PCIE HPC definition to hpc_led_state_t
+ * definition.
+ */
+hpc_led_state_t
+pciehpc_led_state_to_hpc(uint16_t state)
+{
+ switch (state) {
+ case PCIE_SLOTCTL_INDICATOR_STATE_ON:
+ return (HPC_LED_ON);
+ case PCIE_SLOTCTL_INDICATOR_STATE_BLINK:
+ return (HPC_LED_BLINK);
+ case PCIE_SLOTCTL_INDICATOR_STATE_OFF:
+ default:
+ return (HPC_LED_OFF);
+ }
+}
+
+/*
+ * convert LED state from hpc_led_state_t definition to PCIE HPC
+ * definition.
+ */
+uint16_t
+pciehpc_led_state_to_pciehpc(hpc_led_state_t state)
+{
+ switch (state) {
+ case HPC_LED_ON:
+ return (PCIE_SLOTCTL_INDICATOR_STATE_ON);
+ case HPC_LED_BLINK:
+ return (PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
+ case HPC_LED_OFF:
+ default:
+ return (PCIE_SLOTCTL_INDICATOR_STATE_OFF);
+ }
+}
+
+/*
+ * Initialize HPC hardware, install interrupt handler, etc. It doesn't
+ * enable hot plug interrupts.
+ *
+ * (Note: It is called only from pciehpc_init().)
+ */
+int
+pciehpc_hpc_init(pciehpc_t *ctrl_p)
+{
+ uint16_t reg;
+
+ /* read the Slot Control Register */
+ reg = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+
+ /* disable all interrupts */
+ reg &= ~(SLOTCTL_SUPPORTED_INTRS_MASK);
+ pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset +
+ PCIE_SLOTCTL, reg);
+
+ /* initialize the interrupt mutex */
+ mutex_init(&ctrl_p->pciehpc_mutex, NULL, MUTEX_DRIVER,
+ (void *)PCIEHPC_INTR_PRI);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Uninitialize HPC hardware, uninstall interrupt handler, etc.
+ *
+ * (Note: It is called only from pciehpc_uninit().)
+ */
+int
+pciehpc_hpc_uninit(pciehpc_t *ctrl_p)
+{
+ /* disable interrupts */
+ (void) pciehpc_disable_intr(ctrl_p);
+
+ /* destroy the mutex */
+ mutex_destroy(&ctrl_p->pciehpc_mutex);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Disable hot plug interrupts.
+ * Note: this is only for Native hot plug mode.
+ */
+int
+pciehpc_disable_intr(pciehpc_t *ctrl_p)
+{
+ uint16_t reg;
+
+ /* read the Slot Control Register */
+ reg = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+
+ /* disable all interrupts */
+ reg &= ~(SLOTCTL_SUPPORTED_INTRS_MASK);
+ pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset +
+ PCIE_SLOTCTL, reg);
+
+ /* clear any interrupt status bits */
+ reg = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
+ pciehpc_reg_put16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS, reg);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Enable hot plug interrupts.
+ * Note: this is only for Native hot plug mode.
+ */
+int
+pciehpc_enable_intr(pciehpc_t *ctrl_p)
+{
+ uint16_t reg;
+
+ /* clear any interrupt status bits */
+ reg = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
+ pciehpc_reg_put16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS, reg);
+
+ /* read the Slot Control Register */
+ reg = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+
+ /* enable all interrupts */
+ pciehpc_reg_put16(ctrl_p, ctrl_p->pcie_caps_reg_offset +
+ PCIE_SLOTCTL, reg | SLOTCTL_SUPPORTED_INTRS_MASK);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Register the PCI-E hot plug slot with HPS framework.
+ */
+int
+pciehpc_register_slot(pciehpc_t *ctrl_p)
+{
+ char nexus_path[MAXNAMELEN];
+ pciehpc_slot_t *p = &ctrl_p->slot;
+
+ /* get nexus path name */
+ (void) ddi_pathname(ctrl_p->dip, nexus_path);
+
+ /* register the slot with HPS framework */
+ if (hpc_slot_register(ctrl_p->dip, nexus_path,
+ &p->slot_info, &p->slot_handle,
+ &p->slot_ops, (caddr_t)ctrl_p, 0) != 0) {
+ PCIEHPC_DEBUG((CE_WARN,
+ "pciehpc_register_slot() failed to register slot %d\n",
+ p->slotNum));
+ return (DDI_FAILURE);
+ }
+
+ PCIEHPC_DEBUG3((CE_NOTE,
+ "pciehpc_register_slot(): registered slot %d\n", p->slotNum));
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Unregister the PCI-E hot plug slot from the HPS framework.
+ */
+int
+pciehpc_unregister_slot(pciehpc_t *ctrl_p)
+{
+ pciehpc_slot_t *p = &ctrl_p->slot;
+
+ if (hpc_slot_unregister(&p->slot_handle) != 0) {
+ PCIEHPC_DEBUG((CE_WARN,
+ "pciehpc_unregister_slot() failed to unregister slot %d\n",
+ p->slotNum));
+ return (DDI_FAILURE);
+ }
+ PCIEHPC_DEBUG3((CE_NOTE,
+ "pciehpc_unregister_slot(): unregistered slot %d\n", p->slotNum));
+ return (DDI_SUCCESS);
+}
+
+/*
+ * pciehpc_intr()
+ *
+ * Interrupt handler for PCI-E Hot plug controller interrupts.
+ *
+ * Note: This is only for native mode hot plug. This is called
+ * by the nexus driver at interrupt context. Interrupt Service Routine
+ * registration is done by the nexus driver for both hot plug and
+ * non-hot plug interrupts. This function is called from the ISR
+ * of the nexus driver to handle hot-plug interrupts.
+ */
+int
+pciehpc_intr(dev_info_t *dip)
+{
+ pciehpc_t *ctrl_p;
+ uint16_t status;
+
+ /* get the soft state structure for this dip */
+ if ((ctrl_p = pciehpc_get_soft_state(dip)) == NULL)
+ return (DDI_INTR_UNCLAIMED);
+
+ mutex_enter(&ctrl_p->pciehpc_mutex);
+
+ /* make sure the controller soft state is initialized */
+ if (ctrl_p->soft_state & PCIEHPC_SOFT_STATE_UNINITIALIZED) {
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ /* if it is not NATIVE hot plug mode then return */
+ if (ctrl_p->hp_mode != PCIEHPC_NATIVE_HP_MODE) {
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ /* read the current slot status register */
+ status = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
+
+ /* check if there are any hot plug interrupts occurred */
+ if (!(status & SLOT_STATUS_EVENTS)) {
+ /* no hot plug events occurred */
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ /* clear the interrupt status bits */
+ pciehpc_reg_put16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS, status);
+
+ /* check for CMD COMPLETE interrupt */
+ if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
+ PCIEHPC_DEBUG3((CE_NOTE,
+ "pciehpc_intr(): CMD COMPLETED interrupt received\n"));
+ /* wake up any one waiting for Command Completion event */
+ cv_signal(&ctrl_p->slot.cmd_comp_cv);
+ }
+
+ /* check for ATTN button interrupt */
+ if (status & PCIE_SLOTSTS_ATTN_BTN_PRESSED) {
+ PCIEHPC_DEBUG3((CE_NOTE,
+ "pciehpc_intr(): ATTN BUTTON interrupt received\n"));
+ /* if ATTN button event is still pending then cancel it */
+ if (ctrl_p->slot.attn_btn_pending == B_TRUE)
+ ctrl_p->slot.attn_btn_pending = B_FALSE;
+ else
+ ctrl_p->slot.attn_btn_pending = B_TRUE;
+ /* wake up the ATTN event handler */
+ cv_signal(&ctrl_p->slot.attn_btn_cv);
+ }
+
+ /* check for power fault interrupt */
+ if (status & PCIE_SLOTSTS_PWR_FAULT_DETECTED) {
+ /* send the event to HPS framework */
+ (void) hpc_slot_event_notify(ctrl_p->slot.slot_handle,
+ HPC_EVENT_SLOT_POWER_FAULT, HPC_EVENT_NORMAL);
+ }
+
+ /* check for MRL SENSOR CHANGED interrupt */
+ if (status & PCIE_SLOTSTS_MRL_SENSOR_CHANGED) {
+ PCIEHPC_DEBUG3((CE_NOTE,
+ "pciehpc_intr(): MRL SENSOR CHANGED interrupt received"
+ " on slot %d\n", ctrl_p->slot.slotNum));
+ /* For now (phase-I), no action is taken on this event */
+ }
+
+ /* check for PRESENCE CHANGED interrupt */
+ if (status & PCIE_SLOTSTS_PRESENCE_CHANGED) {
+ PCIEHPC_DEBUG3((CE_NOTE,
+ "pciehpc_intr(): PRESENCE CHANGED interrupt received"
+ " on slot %d\n", ctrl_p->slot.slotNum));
+ /* For now (phase-I), no action is taken on this event */
+ }
+
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+
+ return (DDI_INTR_CLAIMED);
+}
+
+#ifdef DEBUG
+/*
+ * Dump PCI-E Hot Plug registers.
+ */
+static void
+pciehpc_dump_hpregs(pciehpc_t *ctrl_p)
+{
+ uint16_t control;
+ uint32_t capabilities;
+
+ capabilities = pciehpc_reg_get32(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCAP);
+
+ control = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+
+ cmn_err(CE_NOTE, "pciehpc_dump_hpregs: Found PCI-E hot plug slot %d\n",
+ ctrl_p->slot.slotNum);
+ cmn_err(CE_NOTE, "Attention Button Present = %s",
+ capabilities & PCIE_SLOTCAP_ATTN_BUTTON ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "Power controller Present = %s",
+ capabilities & PCIE_SLOTCAP_POWER_CONTROLLER ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "MRL Sensor Present = %s",
+ capabilities & PCIE_SLOTCAP_MRL_SENSOR ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "Attn Indicator Present = %s",
+ capabilities & PCIE_SLOTCAP_ATTN_INDICATOR ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "Power Indicator Present = %s",
+ capabilities & PCIE_SLOTCAP_PWR_INDICATOR ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "HotPlug Surprise = %s",
+ capabilities & PCIE_SLOTCAP_HP_SURPRISE ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "HotPlug Capable = %s",
+ capabilities & PCIE_SLOTCAP_HP_CAPABLE ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "Physical Slot Number = %d",
+ PCIE_SLOTCAP_PHY_SLOT_NUM(capabilities));
+
+ cmn_err(CE_NOTE, "Attn Button interrupt Enabled = %s",
+ control & PCIE_SLOTCTL_ATTN_BTN_EN ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "Power Fault interrupt Enabled = %s",
+ control & PCIE_SLOTCTL_PWR_FAULT_EN ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "MRL Sensor INTR Enabled = %s",
+ control & PCIE_SLOTCTL_MRL_SENSOR_EN ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "Presence interrupt Enabled = %s",
+ control & PCIE_SLOTCTL_PRESENCE_CHANGE_EN ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "Cmd Complete interrupt Enabled = %s",
+ control & PCIE_SLOTCTL_CMD_INTR_EN ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "HotPlug interrupt Enabled = %s",
+ control & PCIE_SLOTCTL_HP_INTR_EN ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "Power Indicator LED = %s", pciehpc_led_state_text(
+ pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control))));
+
+ cmn_err(CE_NOTE, "Attn Indicator LED = %s",
+ pciehpc_led_state_text(pciehpc_led_state_to_hpc(
+ pcie_slotctl_attn_indicator_get(control))));
+}
+
+static char *
+pciehpc_led_state_text(hpc_led_state_t state)
+{
+ switch (state) {
+ case HPC_LED_ON:
+ return ("on");
+ case HPC_LED_OFF:
+ return ("off");
+ case HPC_LED_BLINK:
+ default:
+ return ("blink");
+ }
+}
+#endif /* DEBUG */
+
+/*
+ * pciehpc_slot_connect()
+ *
+ * Connect power to the PCI-E slot.
+ *
+ * Returns: HPC_SUCCESS if the slot is powered up and enabled.
+ * HPC_ERR_FAILED if the slot can't be enabled.
+ *
+ * (Note: This function is called by HPS framework at kernel context only.)
+ */
+/*ARGSUSED*/
+int
+pciehpc_slot_connect(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ void *data, uint_t flags)
+{
+ uint16_t status;
+ uint16_t control;
+
+ pciehpc_t *ctrl_p = (pciehpc_t *)ops_arg;
+
+ ASSERT(slot_hdl == ctrl_p->slot.slot_handle);
+
+ mutex_enter(&ctrl_p->pciehpc_mutex);
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(ctrl_p);
+
+ /* check if the slot is already in the 'connected' state */
+ if (ctrl_p->slot.slot_state == HPC_SLOT_CONNECTED) {
+ /* slot is already in the 'connected' state */
+ PCIEHPC_DEBUG3((CE_NOTE,
+ "pciehpc_slot_connect() slot %d already connected\n",
+ ctrl_p->slot.slotNum));
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_SUCCESS);
+ }
+
+ /* read the Slot Status Register */
+ status = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
+
+ /* make sure the MRL switch is closed if present */
+ if ((ctrl_p->has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) {
+ /* MRL switch is open */
+ cmn_err(CE_WARN, "MRL switch is open on slot %d\n",
+ ctrl_p->slot.slotNum);
+ goto cleanup;
+ }
+
+ /* make sure the slot has a device present */
+ if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
+ /* slot is empty */
+ PCIEHPC_DEBUG((CE_NOTE,
+ "slot %d is empty\n", ctrl_p->slot.slotNum));
+ goto cleanup;
+ }
+
+ /* get the current state of Slot Control Register */
+ control = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+
+ /* check if the slot's power state is ON */
+ if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
+ /* slot is already powered up */
+ PCIEHPC_DEBUG3((CE_NOTE,
+ "pciehpc_slot_connect() slot %d already connected\n",
+ ctrl_p->slot.slotNum));
+ ctrl_p->slot.slot_state = HPC_SLOT_CONNECTED;
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_SUCCESS);
+ }
+
+ /*
+ * Enable power to the slot involves:
+ * 1. Set power LED to blink and ATTN led to OFF.
+ * 2. Set power control ON in Slot Control Reigster and
+ * wait for Command Completed Interrupt or 1 sec timeout.
+ * 3. Set power LED to be ON.
+ * 4. If Data Link Layer State Changed events are supported
+ * then wait for the event to indicate Data Layer Link
+ * is active. The time out value for this event is 1 second.
+ * This is specified in PCI-E (draft) version 1.1.
+ */
+
+ /* 1. set power LED to blink & ATTN led to OFF */
+ pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_BLINK);
+ pciehpc_set_led_state(ctrl_p, HPC_ATTN_LED, HPC_LED_OFF);
+
+ /* 2. set power control to ON */
+ control = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+ control &= ~PCIE_SLOTCTL_PWR_CONTROL;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+ /* NOTE - any check to make sure power is really turned ON? */
+
+ /* NOTE - what about power-fault on the slot? */
+
+ /* 3. Set power LED to be ON */
+ pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_ON);
+
+ /*
+ * NOTE - needs work for pci-e 1.1 features
+ * - if EMI Lock is present then it should be turned ON
+ * - if DLL State Change events are supported then we need
+ * to wait for DLL Active event.
+ */
+
+ ctrl_p->slot.slot_state = HPC_SLOT_CONNECTED;
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_SUCCESS);
+
+cleanup:
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_ERR_FAILED);
+}
+
+/*
+ * pciehpc_slot_disconnect()
+ *
+ * Disconnect power to the slot.
+ *
+ * Returns: HPC_SUCCESS if the slot is powered up and enabled.
+ * HPC_ERR_FAILED if the slot can't be enabled.
+ *
+ * (Note: This function is called by HPS framework at kernel context only.)
+ */
+/*ARGSUSED*/
+int
+pciehpc_slot_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ void *data, uint_t flags)
+{
+ uint16_t status;
+ uint16_t control;
+
+ pciehpc_t *ctrl_p = (pciehpc_t *)ops_arg;
+
+ ASSERT(slot_hdl == ctrl_p->slot.slot_handle);
+
+ mutex_enter(&ctrl_p->pciehpc_mutex);
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(ctrl_p);
+
+ /* check if the slot is already in the 'disconnected' state */
+ if (ctrl_p->slot.slot_state == HPC_SLOT_DISCONNECTED) {
+ /* slot is in the 'disconnected' state */
+ PCIEHPC_DEBUG3((CE_NOTE,
+ "pciehpc_slot_disconnect(): slot %d already disconnected\n",
+ ctrl_p->slot.slotNum));
+ ASSERT(ctrl_p->slot.power_led_state == HPC_LED_OFF);
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_SUCCESS);
+ }
+
+ /* read the Slot Status Register */
+ status = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
+
+ /* make sure the slot has a device present */
+ if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
+ /* slot is empty */
+ PCIEHPC_DEBUG((CE_NOTE,
+ "pciehpc_slot_disconnect(): slot %d is empty\n",
+ ctrl_p->slot.slotNum));
+ goto cleanup;
+ }
+
+ /*
+ * Disable power to the slot involves:
+ * 1. Set power LED to blink.
+ * 2. Set power control OFF in Slot Control Reigster and
+ * wait for Command Completed Interrupt or 1 sec timeout.
+ * 3. Set POWER led and ATTN led to be OFF.
+ */
+
+ /* 1. set power LED to blink */
+ pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_BLINK);
+
+ /* 2. set power control to OFF */
+ control = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+ control |= PCIE_SLOTCTL_PWR_CONTROL;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+#ifdef DEBUG
+ /* check for power control bit to be OFF */
+ control = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+ ASSERT(control & PCIE_SLOTCTL_PWR_CONTROL);
+#endif
+
+ /* 3. Set power LED to be OFF */
+ pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_OFF);
+ pciehpc_set_led_state(ctrl_p, HPC_ATTN_LED, HPC_LED_OFF);
+
+ ctrl_p->slot.slot_state = HPC_SLOT_DISCONNECTED;
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_SUCCESS);
+
+cleanup:
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_ERR_FAILED);
+}
+
+/*ARGSUSED*/
+int
+pciehpc_slot_control(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ int request, caddr_t arg)
+{
+ pciehpc_t *ctrl_p;
+ hpc_led_info_t *led_info;
+ int ret = HPC_SUCCESS;
+
+ ctrl_p = (pciehpc_t *)ops_arg;
+
+ ASSERT(ctrl_p != NULL);
+
+ mutex_enter(&ctrl_p->pciehpc_mutex);
+
+ /* get the current slot state */
+ pciehpc_get_slot_state(ctrl_p);
+
+ switch (request) {
+
+ case HPC_CTRL_GET_SLOT_STATE:
+ *(hpc_slot_state_t *)arg = ctrl_p->slot.slot_state;
+ break;
+
+ case HPC_CTRL_GET_BOARD_TYPE:
+ if (ctrl_p->slot.slot_state == HPC_SLOT_EMPTY)
+ *(hpc_board_type_t *)arg = HPC_BOARD_UNKNOWN;
+ else
+ *(hpc_board_type_t *)arg = HPC_BOARD_PCI_HOTPLUG;
+ break;
+
+ case HPC_CTRL_GET_LED_STATE:
+ led_info = (hpc_led_info_t *)arg;
+ switch (led_info->led) {
+ case HPC_ATTN_LED:
+ led_info->state = ctrl_p->slot.attn_led_state;
+ break;
+ case HPC_POWER_LED:
+ led_info->state = ctrl_p->slot.power_led_state;
+ break;
+ case HPC_FAULT_LED:
+ case HPC_ACTIVE_LED:
+ led_info->state = HPC_LED_OFF;
+ break;
+ default:
+ PCIEHPC_DEBUG((CE_WARN, "pciehpc_slot_control:"
+ " unknown led state\n"));
+ ret = HPC_ERR_NOTSUPPORTED;
+ break;
+ }
+ break;
+ case HPC_CTRL_SET_LED_STATE:
+ led_info = (hpc_led_info_t *)arg;
+ switch (led_info->led) {
+ case HPC_ATTN_LED:
+ pciehpc_set_led_state(ctrl_p, led_info->led,
+ led_info->state);
+ break;
+ case HPC_POWER_LED:
+ PCIEHPC_DEBUG((CE_WARN, "pciehpc_slot_control: power"
+ " LED control is not allowed on slot #%d\n",
+ ctrl_p->slot.slotNum));
+ ret = HPC_ERR_NOTSUPPORTED;
+ break;
+ case HPC_FAULT_LED:
+ case HPC_ACTIVE_LED:
+ break;
+ default:
+ PCIEHPC_DEBUG((CE_WARN, "pciehpc_slot_control:"
+ " unknown led type %d\n", led_info->led));
+ ret = HPC_ERR_NOTSUPPORTED;
+ break;
+ }
+ break;
+ case HPC_CTRL_DEV_CONFIG_FAILURE:
+ /* turn the ATTN led ON for configure failure */
+ pciehpc_set_led_state(ctrl_p, HPC_ATTN_LED, HPC_LED_ON);
+ /* 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);
+ break;
+ case HPC_CTRL_DEV_UNCONFIG_FAILURE:
+ /* 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);
+ break;
+ case HPC_CTRL_ENABLE_AUTOCFG:
+ case HPC_CTRL_DISABLE_AUTOCFG:
+ /* no action is needed here */
+ break;
+
+ case HPC_CTRL_DISABLE_SLOT:
+ case HPC_CTRL_ENABLE_SLOT:
+ /* no action is needed here */
+ break;
+
+ case HPC_CTRL_DEV_CONFIG_START:
+ case HPC_CTRL_DEV_UNCONFIG_START:
+ /* make sure POWER LED is blinking */
+ pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_BLINK);
+ break;
+ case HPC_CTRL_DEV_CONFIGURED:
+ case HPC_CTRL_DEV_UNCONFIGURED:
+ /* make sure PWR LED is ON */
+ pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_ON);
+ /* make sure ATTN LED is OFF */
+ pciehpc_set_led_state(ctrl_p, HPC_ATTN_LED, HPC_LED_OFF);
+ break;
+ default:
+ PCIEHPC_DEBUG((CE_WARN,
+ "pciehpc_slot_control: unsupported operation\n"));
+ ret = HPC_ERR_NOTSUPPORTED;
+ }
+
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+
+ return (ret);
+}
+
+/*
+ * Get the state of an LED.
+ */
+hpc_led_state_t
+pciehpc_get_led_state(pciehpc_t *ctrl_p, hpc_led_t led)
+{
+ uint16_t control;
+ uint16_t state;
+
+ /* get the current state of Slot Control register */
+ control = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+
+ switch (led) {
+ case HPC_POWER_LED:
+ state = pcie_slotctl_pwr_indicator_get(control);
+ break;
+ case HPC_ATTN_LED:
+ state = pcie_slotctl_attn_indicator_get(control);
+ break;
+ default:
+ PCIEHPC_DEBUG((CE_WARN,
+ "pciehpc_get_led_state() invalid LED %d\n", led));
+ return (HPC_LED_OFF);
+ }
+
+ switch (state) {
+ case PCIE_SLOTCTL_INDICATOR_STATE_ON:
+ return (HPC_LED_ON);
+
+ case PCIE_SLOTCTL_INDICATOR_STATE_BLINK:
+ return (HPC_LED_BLINK);
+
+ case PCIE_SLOTCTL_INDICATOR_STATE_OFF:
+ default:
+ return (HPC_LED_OFF);
+ }
+}
+
+/*
+ * Set the state of an LED. It updates both hw and sw state.
+ */
+void
+pciehpc_set_led_state(pciehpc_t *ctrl_p, hpc_led_t led, hpc_led_state_t state)
+{
+ uint16_t control;
+
+ /* get the current state of Slot Control register */
+ control = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+
+ switch (led) {
+ case HPC_POWER_LED:
+ /* clear led mask */
+ control &= ~PCIE_SLOTCTL_PWR_INDICATOR_MASK;
+ ctrl_p->slot.power_led_state = state;
+ break;
+ case HPC_ATTN_LED:
+ /* clear led mask */
+ control &= ~PCIE_SLOTCTL_ATTN_INDICATOR_MASK;
+ ctrl_p->slot.attn_led_state = state;
+ break;
+ default:
+ PCIEHPC_DEBUG((CE_WARN,
+ "pciehpc_set_led_state() invalid LED %d\n", led));
+ return;
+ }
+
+ switch (state) {
+ case HPC_LED_ON:
+ if (led == HPC_POWER_LED)
+ control = pcie_slotctl_pwr_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_ON);
+ else if (led == HPC_ATTN_LED)
+ control = pcie_slotctl_attn_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_ON);
+ break;
+ case HPC_LED_OFF:
+ if (led == HPC_POWER_LED)
+ control = pcie_slotctl_pwr_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_OFF);
+ else if (led == HPC_ATTN_LED)
+ control = pcie_slotctl_attn_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_OFF);
+ break;
+ case HPC_LED_BLINK:
+ if (led == HPC_POWER_LED)
+ control = pcie_slotctl_pwr_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
+ else if (led == HPC_ATTN_LED)
+ control = pcie_slotctl_attn_indicator_set(control,
+ PCIE_SLOTCTL_INDICATOR_STATE_BLINK);
+ break;
+
+ default:
+ PCIEHPC_DEBUG((CE_WARN,
+ "pciehpc_set_led_state() invalid LED state %d\n", state));
+ return;
+ }
+
+ /* update the Slot Control Register */
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+#ifdef DEBUG
+ /* get the current state of Slot Control register */
+ control = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+
+ PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_set_led_state: "
+ "slot %d power-led %s attn-led %s\n",
+ ctrl_p->slot.slotNum,
+ pciehpc_led_state_text(
+ pciehpc_led_state_to_hpc(
+ pcie_slotctl_pwr_indicator_get(control))),
+ pciehpc_led_state_text(
+ pciehpc_led_state_to_hpc(
+ pcie_slotctl_attn_indicator_get(control)))));
+#endif
+}
+
+/*
+ * Send a command to the PCI-E Hot Plug Controller.
+ *
+ * NOTES: The PCI-E spec defines the following semantics for issuing hot plug
+ * commands.
+ * 1) If Command Complete events/interrupts are supported then software
+ * waits for Command Complete event after issuing a command (i.e writing
+ * to the Slot Control register). The command completion could take as
+ * long as 1 second so software should be prepared to wait for 1 second
+ * before issuing another command.
+ *
+ * 2) If Command Complete events/interrupts are not supported then
+ * software could issue multiple Slot Control writes without any delay
+ * between writes.
+ */
+void
+pciehpc_issue_hpc_command(pciehpc_t *ctrl_p, uint16_t control)
+{
+
+ uint16_t status;
+ uint32_t slot_cap;
+
+ /*
+ * PCI-E version 1.1 spec defines No Command Completed
+ * Support bit (bit#18) in Slot Capabilities register. If this
+ * bit is set then slot doesn't support notification of command
+ * completion events.
+ */
+ slot_cap = pciehpc_reg_get32(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCAP);
+ /*
+ * If no Command Completion event is supported or it is ACPI
+ * hot plug mode then just issue the command and return.
+ */
+ if ((slot_cap & PCIE_SLOTCAP_NO_CMD_COMP_SUPP) ||
+ (ctrl_p->hp_mode == PCIEHPC_ACPI_HP_MODE)) {
+ pciehpc_reg_put16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL, control);
+ return;
+ }
+
+ /*
+ * **************************************
+ * Command Complete events are supported.
+ * **************************************
+ */
+
+ /*
+ * If HPC is not yet initialized then just poll for the Command
+ * Completion interrupt.
+ */
+ if (!(ctrl_p->soft_state & PCIEHPC_SOFT_STATE_INITIALIZED)) {
+ int retry = PCIEHPC_CMD_WAIT_RETRY;
+
+ /* write the command to the HPC */
+ pciehpc_reg_put16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL,
+ control);
+
+ /* poll for status completion */
+ while (retry--) {
+ /* wait for 10 msec before checking the status */
+ delay(drv_usectohz(PCIEHPC_CMD_WAIT_TIME));
+
+ status = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
+
+ if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
+ /* clear the status bits */
+ pciehpc_reg_put16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS,
+ status);
+ break;
+ }
+ }
+ return;
+ }
+
+ /* HPC is already initialized */
+
+ ASSERT(MUTEX_HELD(&ctrl_p->pciehpc_mutex));
+
+ /*
+ * If previous command is still pending then wait for its
+ * completion. i.e cv_wait()
+ */
+
+ while (ctrl_p->slot.command_pending == B_TRUE)
+ cv_wait(&ctrl_p->slot.cmd_comp_cv, &ctrl_p->pciehpc_mutex);
+
+ /*
+ * Issue the command and wait for Command Completion or
+ * the 1 sec timeout.
+ */
+ pciehpc_reg_put16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL, control);
+
+ ctrl_p->slot.command_pending = B_TRUE;
+
+ if (cv_timedwait(&ctrl_p->slot.cmd_comp_cv, &ctrl_p->pciehpc_mutex,
+ ddi_get_lbolt() + SEC_TO_TICK(1)) == -1) {
+ /* it is a timeout */
+ PCIEHPC_DEBUG2((CE_NOTE,
+ "pciehpc_issue_hpc_command: Command Complete"
+ " interrupt is not received for slot %d\n",
+ ctrl_p->slot.slotNum));
+
+ /* clear the status info in case interrupts are disabled? */
+ status = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
+
+ if (status & PCIE_SLOTSTS_COMMAND_COMPLETED) {
+ /* clear the status bits */
+ pciehpc_reg_put16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS,
+ status);
+ }
+ }
+
+ ctrl_p->slot.command_pending = B_FALSE;
+
+ /* wake up any one waiting for issuing another command to HPC */
+ cv_signal(&ctrl_p->slot.cmd_comp_cv);
+}
+
+/*
+ * pciehcp_attn_btn_handler()
+ *
+ * This handles ATTN button pressed event as per the PCI-E 1.1 spec.
+ */
+static void
+pciehpc_attn_btn_handler(pciehpc_t *ctrl_p)
+{
+ hpc_led_state_t power_led_state;
+ callb_cpr_t cprinfo;
+
+ PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_attn_btn_handler: thread started\n"));
+
+ CALLB_CPR_INIT(&cprinfo, &ctrl_p->pciehpc_mutex, callb_generic_cpr,
+ "pciehpc_attn_btn_handler");
+
+ mutex_enter(&ctrl_p->pciehpc_mutex);
+
+ /* wait for ATTN button event */
+ cv_wait(&ctrl_p->slot.attn_btn_cv, &ctrl_p->pciehpc_mutex);
+
+ while (ctrl_p->slot.attn_btn_thread_exit == B_FALSE) {
+
+ if (ctrl_p->slot.attn_btn_pending == B_TRUE) {
+
+ /* get the current state of power LED */
+ power_led_state = pciehpc_get_led_state(ctrl_p, HPC_POWER_LED);
+
+ /* Blink the Power LED while we wait for 5 seconds */
+ pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_BLINK);
+
+ /* wait for 5 seconds before taking any action */
+ if (cv_timedwait(&ctrl_p->slot.attn_btn_cv,
+ &ctrl_p->pciehpc_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 (ctrl_p->slot.attn_btn_pending == B_TRUE) {
+ /* send the ATTN button event to HPS framework */
+ ctrl_p->slot.attn_btn_pending = B_FALSE;
+ (void) hpc_slot_event_notify(
+ ctrl_p->slot.slot_handle,
+ HPC_EVENT_SLOT_ATTN, HPC_EVENT_NORMAL);
+ }
+ }
+ /* restore the power LED state */
+ pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, power_led_state);
+ continue;
+ }
+
+ /* wait for another ATTN button event */
+ cv_wait(&ctrl_p->slot.attn_btn_cv, &ctrl_p->pciehpc_mutex);
+ }
+
+ PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_attn_btn_handler: thread exit\n"));
+ cv_signal(&ctrl_p->slot.attn_btn_cv);
+ CALLB_CPR_EXIT(&cprinfo);
+ thread_exit();
+}
+
+/*
+ * Read/Write access to HPC registers. If platform nexus has non-standard
+ * HPC access mechanism then regops functions are used to do reads/writes.
+ */
+uint8_t
+pciehpc_reg_get8(pciehpc_t *ctrl_p, uint_t off)
+{
+ PCIEHPC_DEBUG3((CE_NOTE, "read reg8 (offset %x)", off));
+
+ if (ctrl_p->regops.get != NULL)
+ return ((uint8_t)ctrl_p->regops.get(ctrl_p->regops.cookie,
+ (off_t)off));
+ else
+ return (ddi_mem_get8(ctrl_p->cfghdl,
+ (uint8_t *)(ctrl_p->regs_base + off)));
+}
+
+uint16_t
+pciehpc_reg_get16(pciehpc_t *ctrl_p, uint_t off)
+{
+ PCIEHPC_DEBUG3((CE_NOTE, "read reg16 (offset %x)", off));
+
+ if (ctrl_p->regops.get != NULL)
+ return ((uint16_t)ctrl_p->regops.get(ctrl_p->regops.cookie,
+ (off_t)off));
+ else
+ return (ddi_mem_get16(ctrl_p->cfghdl,
+ (uint16_t *)(ctrl_p->regs_base + off)));
+}
+
+uint32_t
+pciehpc_reg_get32(pciehpc_t *ctrl_p, uint_t off)
+{
+ PCIEHPC_DEBUG3((CE_NOTE, "read reg32 (offset %x)", off));
+
+ if (ctrl_p->regops.get != NULL)
+ return ((uint32_t)ctrl_p->regops.get(ctrl_p->regops.cookie,
+ (off_t)off));
+ else
+ return (ddi_mem_get32(ctrl_p->cfghdl,
+ (uint32_t *)(ctrl_p->regs_base + off)));
+}
+
+void
+pciehpc_reg_put8(pciehpc_t *ctrl_p, uint_t off, uint8_t val)
+{
+ PCIEHPC_DEBUG3((CE_NOTE, "write reg8 (offset %x, val %x)",
+ off, val));
+
+ if (ctrl_p->regops.put != NULL)
+ ctrl_p->regops.put(ctrl_p->regops.cookie, (off_t)off, (uint_t)val);
+ else
+ ddi_mem_put8(ctrl_p->cfghdl,
+ (uint8_t *)(ctrl_p->regs_base + off), val);
+}
+
+void
+pciehpc_reg_put16(pciehpc_t *ctrl_p, uint_t off, uint16_t val)
+{
+ PCIEHPC_DEBUG3((CE_NOTE, "write reg16 (offset %x, val %x)",
+ off, val));
+
+ if (ctrl_p->regops.put != NULL)
+ ctrl_p->regops.put(ctrl_p->regops.cookie, (off_t)off, (uint_t)val);
+ else
+ ddi_mem_put16(ctrl_p->cfghdl,
+ (uint16_t *)(ctrl_p->regs_base + off), val);
+}
+
+void
+pciehpc_reg_put32(pciehpc_t *ctrl_p, uint_t off, uint32_t val)
+{
+ PCIEHPC_DEBUG3((CE_NOTE, "write reg32 (offset %x, val %x)",
+ off, val));
+
+ if (ctrl_p->regops.put != NULL)
+ ctrl_p->regops.put(ctrl_p->regops.cookie, (off_t)off, (uint_t)val);
+ else
+ ddi_mem_put32(ctrl_p->cfghdl,
+ (uint32_t *)(ctrl_p->regs_base + off), val);
+}
+
+static void
+pciehpc_dev_info(pciehpc_t *ctrl_p)
+{
+ pci_regspec_t *regspec;
+ int reglen;
+ dev_info_t *dip = ctrl_p->dip;
+
+ /* Get the device number. */
+ if (ddi_getlongprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
+ "reg", (caddr_t)&regspec, &reglen) != DDI_SUCCESS) {
+ return;
+ }
+
+ ctrl_p->bus = PCI_REG_BUS_G(regspec[0].pci_phys_hi);
+ ctrl_p->dev = PCI_REG_DEV_G(regspec[0].pci_phys_hi);
+ ctrl_p->func = PCI_REG_FUNC_G(regspec[0].pci_phys_hi);
+
+ kmem_free(regspec, reglen);
+
+ PCIEHPC_DEBUG3((CE_NOTE, "pciehpc_dev_info: bus=%x, dev=%x, func=%x",
+ ctrl_p->bus, ctrl_p->dev, ctrl_p->func));
+}
+
+/*
+ * setup slot name/slot-number info.
+ */
+void
+pciehpc_set_slot_name(pciehpc_t *ctrl_p)
+{
+ pciehpc_slot_t *p = &ctrl_p->slot;
+ uchar_t *slotname_data;
+ int *slotnum;
+ uint_t count;
+ int len;
+ int invalid_slotnum = 0;
+ uint32_t slot_capabilities;
+
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->dip,
+ DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) ==
+ DDI_PROP_SUCCESS) {
+ p->slotNum = slotnum[0];
+ ddi_prop_free(slotnum);
+ } else {
+ slot_capabilities = pciehpc_reg_get32(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCAP);
+ p->slotNum = PCIE_SLOTCAP_PHY_SLOT_NUM(slot_capabilities);
+ }
+
+ if (!p->slotNum) { /* platform may not have initialized it */
+ PCIEHPC_DEBUG((CE_WARN, "%s#%d: Invalid slot number! ",
+ ddi_driver_name(ctrl_p->dip),
+ ddi_get_instance(ctrl_p->dip)));
+ p->slotNum = pciehpc_reg_get8(ctrl_p, 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 "pcie<slot-num>".
+ * else it will be "pcie<sec-bus-number>dev0"
+ */
+ if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->dip, DDI_PROP_DONTPASS,
+ "slot-names", (caddr_t)&slotname_data,
+ &len) == DDI_PROP_SUCCESS) {
+ /*
+ * Note: for PCI-E slots, the device number is always 0 so the
+ * first (and only) string is the slot name for this slot.
+ */
+ (void) sprintf(p->slot_info.pci_slot_name,
+ (char *)slotname_data + 4);
+ kmem_free(slotname_data, len);
+ } else {
+ if (invalid_slotnum)
+ (void) sprintf(p->slot_info.pci_slot_name, "pcie%ddev0",
+ p->slotNum);
+ else
+ (void) sprintf(p->slot_info.pci_slot_name, "pcie%d",
+ p->slotNum);
+ }
+}
diff --git a/usr/src/uts/common/io/hotplug/pcihp/pcihp.c b/usr/src/uts/common/io/hotplug/pcihp/pcihp.c
index 46dbcd10a2..f56ada1d63 100644
--- a/usr/src/uts/common/io/hotplug/pcihp/pcihp.c
+++ b/usr/src/uts/common/io/hotplug/pcihp/pcihp.c
@@ -967,15 +967,18 @@ pcihp_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
* CONFIGURE the occupant in the slot.
* **************************************
*/
+ slotinfop = &pcihp_p->slotinfo[pci_dev];
+
+ mutex_enter(&slotinfop->slot_mutex);
rv = pcihp_configure_ap(pcihp_p, pci_dev);
if (rv == HPC_SUCCESS) {
- slotinfop = &pcihp_p->slotinfo[pci_dev];
pcihp_gen_sysevent(slotinfop->name,
PCIHP_DR_AP_STATE_CHANGE,
SE_NO_HINT, pcihp_p->dip, KM_SLEEP);
pcihp_create_occupant_props(self, dev, pci_dev);
}
+ mutex_exit(&slotinfop->slot_mutex);
break;
@@ -985,17 +988,19 @@ pcihp_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
* UNCONFIGURE the occupant in the slot.
* **************************************
*/
+ slotinfop = &pcihp_p->slotinfo[pci_dev];
+
+ mutex_enter(&slotinfop->slot_mutex);
rv = pcihp_unconfigure_ap(pcihp_p, pci_dev);
if (rv == HPC_SUCCESS) {
-
- slotinfop = &pcihp_p->slotinfo[pci_dev];
pcihp_gen_sysevent(slotinfop->name,
PCIHP_DR_AP_STATE_CHANGE,
SE_NO_HINT, pcihp_p->dip, KM_SLEEP);
pcihp_delete_occupant_props(pcihp_p->dip, dev);
}
+ mutex_exit(&slotinfop->slot_mutex);
break;
@@ -1461,11 +1466,11 @@ pcihp_configure_ap(pcihp_t *pcihp_p, int pci_dev)
*/
slotinfop = &pcihp_p->slotinfo[pci_dev];
- mutex_enter(&slotinfop->slot_mutex);
+
if ((pci_dev >= PCI_MAX_DEVS) || (slotinfop->slot_hdl == NULL) ||
(slotinfop->slot_flags & PCIHP_SLOT_DISABLED)) {
- mutex_exit(&slotinfop->slot_mutex);
+
return (ENXIO);
}
@@ -1508,7 +1513,7 @@ pcihp_configure_ap(pcihp_t *pcihp_p, int pci_dev)
else
slotinfop->last_change = (time32_t)time;
- mutex_exit(&slotinfop->slot_mutex);
+
return (rv);
}
@@ -1519,19 +1524,19 @@ pcihp_configure_ap(pcihp_t *pcihp_p, int pci_dev)
/* Check if the receptacle is in the CONNECTED state. */
if (hpc_nexus_control(slotinfop->slot_hdl,
HPC_CTRL_GET_SLOT_STATE, (caddr_t)&rstate) != 0) {
- mutex_exit(&slotinfop->slot_mutex);
+
return (ENXIO);
}
if (rstate == HPC_SLOT_EMPTY) {
/* error. slot is empty */
- mutex_exit(&slotinfop->slot_mutex);
+
return (ENXIO);
}
if (rstate != HPC_SLOT_CONNECTED) {
/* error. either the slot is empty or connect failed */
- mutex_exit(&slotinfop->slot_mutex);
+
return (ENXIO);
}
@@ -1564,7 +1569,7 @@ pcihp_configure_ap(pcihp_t *pcihp_p, int pci_dev)
/* tell HPC driver occupant configure Error */
(void) hpc_nexus_control(slotinfop->slot_hdl,
HPC_CTRL_DEV_CONFIG_FAILURE, NULL);
- mutex_exit(&slotinfop->slot_mutex);
+
return (EIO);
}
@@ -1607,7 +1612,7 @@ pcihp_configure_ap(pcihp_t *pcihp_p, int pci_dev)
(void) hpc_nexus_control(slotinfop->slot_hdl,
HPC_CTRL_DEV_CONFIGURED, NULL);
- mutex_exit(&slotinfop->slot_mutex);
+
return (rv);
}
@@ -1633,11 +1638,11 @@ pcihp_unconfigure_ap(pcihp_t *pcihp_p, int pci_dev)
*/
slotinfop = &pcihp_p->slotinfo[pci_dev];
- mutex_enter(&slotinfop->slot_mutex);
+
if ((pci_dev >= PCI_MAX_DEVS) || (slotinfop->slot_hdl == NULL) ||
(slotinfop->slot_flags & PCIHP_SLOT_DISABLED)) {
- mutex_exit(&slotinfop->slot_mutex);
+
return (ENXIO);
}
/*
@@ -1657,7 +1662,7 @@ pcihp_unconfigure_ap(pcihp_t *pcihp_p, int pci_dev)
if (slotinfop->slot_flags &
PCIHP_SLOT_DEV_NON_HOTPLUG) {
/* Operation unsupported if no HS board/slot */
- mutex_exit(&slotinfop->slot_mutex);
+
return (ENOTSUP);
}
}
@@ -1759,7 +1764,7 @@ pcihp_unconfigure_ap(pcihp_t *pcihp_p, int pci_dev)
else
slotinfop->last_change = (time32_t)time;
- mutex_exit(&slotinfop->slot_mutex);
+
/* unmask enum events again */
if ((slotinfop->slot_flags & PCIHP_SLOT_AUTO_CFG_EN) == 0) {
@@ -2297,6 +2302,8 @@ pcihp_event_handler(caddr_t slot_arg, uint_t event_mask)
struct pcihp_config_ctrl ctrl;
int circular_count;
int rval;
+ int hint;
+ hpc_slot_state_t rstate;
/*
* Get the soft state structure.
@@ -2353,6 +2360,12 @@ pcihp_event_handler(caddr_t slot_arg, uint_t event_mask)
mutex_enter(&slotinfop->slot_mutex);
+ if (hpc_nexus_control(slotinfop->slot_hdl,
+ HPC_CTRL_GET_SLOT_STATE, (caddr_t)&rstate) != 0)
+ rv = HPC_ERR_FAILED;
+
+ slotinfop->rstate = (ap_rstate_t)rstate;
+
switch (event_mask) {
case HPC_EVENT_SLOT_INSERTION:
@@ -2641,19 +2654,21 @@ pcihp_event_handler(caddr_t slot_arg, uint_t event_mask)
* unconfigured before this event.
*/
if (slotinfop->ostate != AP_OSTATE_UNCONFIGURED) {
- panic("pcihp (%s%d): card is removed from"
- " the slot %s before doing unconfigure!!",
+ slotinfop->condition = AP_COND_FAILED;
+ cmn_err(CE_WARN, "pcihp (%s%d): card is removed"
+ " from the slot %s",
ddi_driver_name(pcihp_p->dip),
ddi_get_instance(pcihp_p->dip),
slotinfop->name);
- /*NOTREACHED*/
}
+ else
+ cmn_err(CE_NOTE, "pcihp (%s%d): card is removed"
+ " from the slot %s",
+ ddi_driver_name(pcihp_p->dip),
+ ddi_get_instance(pcihp_p->dip),
+ slotinfop->name);
- cmn_err(CE_NOTE, "pcihp (%s%d): card is removed"
- " from the slot %s",
- ddi_driver_name(pcihp_p->dip),
- ddi_get_instance(pcihp_p->dip),
- slotinfop->name);
+ slotinfop->rstate = AP_RSTATE_EMPTY;
/* +++ HOOK for RCM to report this hotplug event? +++ */
@@ -2865,6 +2880,78 @@ pcihp_event_handler(caddr_t slot_arg, uint_t event_mask)
break;
+ case HPC_EVENT_SLOT_ATTN:
+ /*
+ * Attention button is pressed.
+ */
+ if (((slotinfop->slot_flags & PCIHP_SLOT_AUTO_CFG_EN) == 0) ||
+ (slotinfop->slot_flags & PCIHP_SLOT_DISABLED)) {
+ /*
+ * either auto-conifiguration or the slot is disabled,
+ * ignore this event.
+ */
+ break;
+ }
+
+ if (slotinfop->ostate == AP_OSTATE_UNCONFIGURED)
+ hint = SE_INCOMING_RES;
+ else
+ hint = SE_OUTGOING_RES;
+
+ if (ddi_getprop(DDI_DEV_T_ANY, pcihp_p->dip, DDI_PROP_DONTPASS,
+ "inkernel-autoconfig", 0) == 0) {
+ pcihp_gen_sysevent(slotinfop->name, PCIHP_DR_REQ, hint,
+ pcihp_p->dip, KM_SLEEP);
+ break;
+ }
+
+ if ((slotinfop->ostate == AP_OSTATE_UNCONFIGURED) &&
+ (slotinfop->rstate != AP_RSTATE_EMPTY) &&
+ (slotinfop->condition != AP_COND_FAILED)) {
+ if (slotinfop->rstate == AP_RSTATE_DISCONNECTED) {
+ rv = hpc_nexus_connect(slotinfop->slot_hdl,
+ NULL, 0);
+ if (rv == HPC_SUCCESS)
+ slotinfop->rstate = AP_RSTATE_CONNECTED;
+ else
+ break;
+ }
+
+ rv = pcihp_configure_ap(pcihp_p, pci_dev);
+
+ } else if ((slotinfop->ostate == AP_OSTATE_CONFIGURED) &&
+ (slotinfop->rstate == AP_RSTATE_CONNECTED) &&
+ (slotinfop->condition != AP_COND_FAILED)) {
+ rv = pcihp_unconfigure_ap(pcihp_p, pci_dev);
+
+ if (rv != HPC_SUCCESS)
+ break;
+
+ rv = hpc_nexus_disconnect(slotinfop->slot_hdl,
+ NULL, 0);
+ if (rv == HPC_SUCCESS)
+ slotinfop->rstate = AP_RSTATE_DISCONNECTED;
+ }
+
+ break;
+
+ case HPC_EVENT_SLOT_POWER_FAULT:
+ /*
+ * Power fault is detected.
+ */
+ cmn_err(CE_NOTE, "pcihp (%s%d): power-fault"
+ " for this slot %s",
+ ddi_driver_name(pcihp_p->dip),
+ ddi_get_instance(pcihp_p->dip),
+ slotinfop->name);
+
+ slotinfop->condition = AP_COND_FAILED;
+
+ pcihp_gen_sysevent(slotinfop->name, PCIHP_DR_AP_STATE_CHANGE,
+ SE_NO_HINT, pcihp_p->dip, KM_SLEEP);
+
+ break;
+
default:
cmn_err(CE_NOTE, "pcihp (%s%d): unknown event %x"
" for this slot %s",
@@ -3247,7 +3334,9 @@ pcihp_handle_enum_extraction(pcihp_t *pcihp_p, int pci_dev, int opcode,
return (PCIHP_SUCCESS);
}
+ mutex_enter(&slotinfop->slot_mutex);
rv = pcihp_unconfigure_ap(pcihp_p, pci_dev);
+ mutex_exit(&slotinfop->slot_mutex);
if (rv != HPC_SUCCESS && rv != EBUSY) {
cmn_err(CE_NOTE, "%s%d: PCI device %x Failed on Unconfigure",
ddi_driver_name(pcihp_p->dip),
@@ -3320,7 +3409,9 @@ pcihp_handle_enum_insertion(pcihp_t *pcihp_p, int pci_dev, int opcode,
if ((slotinfop->slot_flags & PCIHP_SLOT_AUTO_CFG_EN) ==
PCIHP_SLOT_AUTO_CFG_EN) {
+ mutex_enter(&slotinfop->slot_mutex);
rv = pcihp_configure_ap(pcihp_p, pci_dev);
+ mutex_exit(&slotinfop->slot_mutex);
if (rv != HPC_SUCCESS) { /* configure failed */
cmn_err(CE_NOTE, "%s%d: PCI device %x Failed on"
" Configure", ddi_driver_name(pcihp_p->dip),
@@ -3518,7 +3609,6 @@ pcihp_config_setup(dev_info_t **dip, ddi_acc_handle_t *handle,
uint32_t lo;
uint32_t hi;
} pci_bus_range;
- int flags = 0;
slotinfop = &pcihp_p->slotinfo[pci_dev];
@@ -3588,16 +3678,12 @@ pcihp_config_setup(dev_info_t **dip, ddi_acc_handle_t *handle,
return (PCIHP_FAILURE);
}
- if (pcihp_indirect_map(*dip) == DDI_SUCCESS)
- flags |= PCICFG_CONF_INDIRECT_MAP;
-
/*
* 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.
*/
- if (flags & PCICFG_CONF_INDIRECT_MAP) {
-
+ if (pcihp_indirect_map(*dip) == DDI_SUCCESS) {
if (pci_config_get16(*handle, 0) == 0xffff) {
pci_config_teardown(handle);
(void) ndi_devi_free(*dip);
@@ -3886,11 +3972,11 @@ pcihp_indirect_map(dev_info_t *dip)
int rc = DDI_FAILURE;
if (ddi_prop_get_int(DDI_DEV_T_ANY, ddi_get_parent(dip), 0,
- PCICFG_DEV_CONF_MAP_PROP, DDI_FAILURE) != DDI_FAILURE)
+ 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, PCICFG_BUS_CONF_MAP_PROP,
+ 0, PCI_BUS_CONF_MAP_PROP,
DDI_FAILURE) != DDI_FAILURE)
rc = DDI_SUCCESS;
return (rc);
diff --git a/usr/src/uts/common/io/pci_pci/pci_pci.c b/usr/src/uts/common/io/pci_pci/pci_pci.c
index 76512618ee..3bcaf25925 100644
--- a/usr/src/uts/common/io/pci_pci/pci_pci.c
+++ b/usr/src/uts/common/io/pci_pci/pci_pci.c
@@ -103,6 +103,39 @@ struct bus_ops ppb_bus_ops = {
i_ddi_intr_ops /* (*bus_intr_op)(); */
};
+/*
+ * The goal here is to leverage off of the pcihp.c source without making
+ * changes to it. Call into it's cb_ops directly if needed.
+ */
+static int ppb_open(dev_t *, int, int, cred_t *);
+static int ppb_close(dev_t, int, int, cred_t *);
+static int ppb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+static int ppb_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
+ caddr_t, int *);
+static int ppb_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
+
+struct cb_ops ppb_cb_ops = {
+ ppb_open, /* open */
+ ppb_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ ppb_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ppb_prop_op, /* cb_prop_op */
+ NULL, /* streamtab */
+ D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
+ CB_REV, /* rev */
+ nodev, /* int (*cb_aread)() */
+ nodev /* int (*cb_awrite)() */
+};
+
+
static int ppb_probe(dev_info_t *);
static int ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
static int ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
@@ -110,13 +143,13 @@ static int ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
struct dev_ops ppb_ops = {
DEVO_REV, /* devo_rev */
0, /* refcnt */
- pcihp_info, /* info */
+ ppb_info, /* info */
nulldev, /* identify */
ppb_probe, /* probe */
ppb_attach, /* attach */
ppb_detach, /* detach */
nulldev, /* reset */
- &pcihp_cb_ops, /* driver operations */
+ &ppb_cb_ops, /* driver operations */
&ppb_bus_ops /* bus operations */
};
@@ -814,3 +847,36 @@ ppb_restore_config_regs(ppb_devstate_t *ppb_p)
pci_config_teardown(&config_handle);
}
}
+
+static int
+ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
+{
+ return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp));
+}
+
+static int
+ppb_close(dev_t dev, int flags, int otyp, cred_t *credp)
+{
+ return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp));
+}
+
+static int
+ppb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
+{
+ return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, credp,
+ rvalp));
+}
+
+static int
+ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
+ int flags, char *name, caddr_t valuep, int *lengthp)
+{
+ return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags,
+ name, valuep, lengthp));
+}
+
+static int
+ppb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
+{
+ return (pcihp_info(dip, cmd, arg, result));
+}
diff --git a/usr/src/uts/common/sys/hotplug/hpctrl.h b/usr/src/uts/common/sys/hotplug/hpctrl.h
index ad0ede5eff..1d1c83a8af 100644
--- a/usr/src/uts/common/sys/hotplug/hpctrl.h
+++ b/usr/src/uts/common/sys/hotplug/hpctrl.h
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -112,6 +112,7 @@ typedef struct hpc_slot_info {
#define HPC_SLOT_TYPE_PCI 0x1 /* PCI bus slot */
#define HPC_SLOT_TYPE_CPCI 0x2 /* Compact PCI bus slot */
#define HPC_SLOT_TYPE_SBD 0x3 /* System bus slot */
+#define HPC_SLOT_TYPE_PCIE 0x4 /* PCI Express slot */
/* bit definitions in slot_capabilities field for PCI or cPCI bus slots */
#define HPC_SLOT_64BITS 0x0001 /* slot is a 64bit slot */
@@ -195,7 +196,7 @@ typedef enum { HPC_SLOT_EMPTY, HPC_SLOT_DISCONNECTED,
* type definition for board type.
*
* HPC_BOARD_UNKNOWN Board is either not present or unknown.
- * HPC_BOARD_PCI_HOTPLUG PCI bus adapter.
+ * HPC_BOARD_PCI_HOTPLUG PCI or PCIe adapter.
* HPC_BOARD_CPCI_NON_HS Non Hot Swap cPCI board.
* HPC_BOARD_CPCI_BASIC_HS Basic Hot Swap cPCI board.
* HPC_BOARD_CPCI_FULL_HS Full Hot Swap cPCI board.
@@ -242,6 +243,8 @@ typedef enum { HPC_BOARD_UNKNOWN, HPC_BOARD_PCI_HOTPLUG,
#define HPC_EVENT_ENABLE_ENUM 0x00008000
#define HPC_EVENT_DISABLE_ENUM 0x00010000
#define HPC_EVENT_BUS_ENUM HPC_EVENT_SLOT_ENUM
+#define HPC_EVENT_SLOT_ATTN 0x00020000
+#define HPC_EVENT_SLOT_POWER_FAULT 0x00040000
/*
* return values for errors from HPS framework interfaces.
diff --git a/usr/src/uts/common/sys/hotplug/pci/pcicfg.h b/usr/src/uts/common/sys/hotplug/pci/pcicfg.h
index 481a176f76..6bd5f55922 100644
--- a/usr/src/uts/common/sys/hotplug/pci/pcicfg.h
+++ b/usr/src/uts/common/sys/hotplug/pci/pcicfg.h
@@ -19,9 +19,10 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
- * All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
*/
#ifndef _SYS_HOTPLUG_PCI_PCICFG_H
@@ -33,6 +34,8 @@
extern "C" {
#endif
+#include <sys/hotplug/pci/pcihp_impl.h>
+
/*
* Interfaces exported by PCI configurator module, kernel/misc/pcicfg.
*/
@@ -51,19 +54,8 @@ int pcicfg_unconfigure(dev_info_t *, uint_t);
#define PCICFG_CONF_INDIRECT_MAP 1
#define PCICFG_CONF_DIRECT_MAP 0
-/*
- * we recognize the non transparent bridge child nodes with the
- * following property. This is specific to an implementation only.
- * This property is specific to AP nodes only.
- */
-#define PCICFG_DEV_CONF_MAP_PROP "pci-parent-indirect"
-
-/*
- * If a non transparent bridge drives a hotplug/hotswap bus, then
- * the following property must be defined for the node either by
- * the driver or the OBP.
- */
-#define PCICFG_BUS_CONF_MAP_PROP "pci-conf-indirect"
+#define PCICFG_DEV_CONF_MAP_PROP PCI_DEV_CONF_MAP_PROP
+#define PCICFG_BUS_CONF_MAP_PROP PCI_BUS_CONF_MAP_PROP
#ifdef __cplusplus
}
diff --git a/usr/src/uts/common/sys/hotplug/pci/pciehpc.h b/usr/src/uts/common/sys/hotplug/pci/pciehpc.h
new file mode 100644
index 0000000000..dd1ad7c416
--- /dev/null
+++ b/usr/src/uts/common/sys/hotplug/pci/pciehpc.h
@@ -0,0 +1,56 @@
+/*
+ * 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_HOTPLUG_PCI_PCIEHPC_H
+#define _SYS_HOTPLUG_PCI_PCIEHPC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Interfaces exported by PCI-E nexus Hot Plug Controller extension module
+ */
+
+/* register ops for read/write of non-standard HPC (e.g: OPL platform) */
+typedef struct pciehpc_regops {
+ uint_t (*get)(void *cookie, off_t offset);
+ uint_t (*put)(void *cookie, off_t offset, uint_t val);
+ void *cookie;
+} pciehpc_regops_t;
+
+int pciehpc_init(dev_info_t *, pciehpc_regops_t *);
+int pciehpc_uninit(dev_info_t *);
+int pciehpc_intr(dev_info_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_HOTPLUG_PCI_PCIEHPC_H */
diff --git a/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h b/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h
new file mode 100644
index 0000000000..6afbf1e432
--- /dev/null
+++ b/usr/src/uts/common/sys/hotplug/pci/pciehpc_impl.h
@@ -0,0 +1,217 @@
+/*
+ * 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_HOTPLUG_PCI_PCIEHPC_IMPL_H
+#define _SYS_HOTPLUG_PCI_PCIEHPC_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/disp.h>
+#include <sys/stat.h>
+#include <sys/condvar.h>
+#include <sys/pcie.h>
+#include <sys/hotplug/hpcsvc.h>
+#include <sys/hotplug/pci/pciehpc.h>
+
+/*
+ * PCI Express Hot Plug slot softstate structure
+ *
+ */
+typedef struct pciehpc_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 slotNum; /* slot number */
+ /* synchronization variable(s) for hot plug events */
+ kcondvar_t cmd_comp_cv; /* Command Completion intr. */
+ boolean_t command_pending;
+ 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;
+} pciehpc_slot_t;
+
+typedef enum {
+ PCIEHPC_NATIVE_HP_MODE, PCIEHPC_ACPI_HP_MODE
+} pciehpc_hp_mode_t;
+
+typedef uint32_t pciehpc_soft_state_t;
+
+/* init_flags */
+#define PCIEHPC_SOFT_STATE_UNINITIALIZED 0x01
+#define PCIEHPC_SOFT_STATE_INITIALIZED 0x02
+#define PCIEHPC_SOFT_STATE_INIT_HTABLE 0x04
+#define PCIEHPC_SOFT_STATE_INIT_ALLOC 0x08
+#define PCIEHPC_SOFT_STATE_INIT_HANDLER 0x10
+#define PCIEHPC_SOFT_STATE_INIT_ENABLE 0x20
+#define PCIEHPC_SOFT_STATE_INIT_BLOCK 0x40
+#define PCIEHPC_SOFT_STATE_INIT_FM 0x80
+
+/*
+ * PCI Express Hotplug controller soft state structure
+ */
+typedef struct pciehpc
+{
+ dev_info_t *dip; /* DIP for the Nexus */
+ uint8_t bus; /* primary bus number */
+ uint8_t dev; /* device number */
+ uint8_t func; /* function number */
+ kmutex_t pciehpc_mutex; /* Mutex for this ctrl */
+ pciehpc_soft_state_t soft_state; /* soft state flags */
+ pciehpc_hp_mode_t hp_mode; /* HP mode (Native, ACPI) */
+ struct pciehpc *nextp; /* Linked list pointer */
+
+ /* PCIE Hot Plug Controller register access */
+ ddi_acc_handle_t cfghdl; /* PCI cfg access handle */
+ caddr_t regs_base; /* config regs base */
+ uint_t pcie_caps_reg_offset; /* offset to PCIE Cap regs */
+
+ /* slot information */
+ pciehpc_slot_t slot; /* Slot info */
+ boolean_t has_attn; /* Do we have attn btn? */
+ boolean_t has_mrl; /* Do we have MRL? */
+ boolean_t has_emi_lock; /* Do we have EMI Lock? */
+
+ /* register read/write ops for non-standard HPC (e.g: OPL) */
+ pciehpc_regops_t regops;
+
+ /* platform specific ops (Native HP, ACPI, etc.) */
+ struct pciehpc_ops {
+ /* initialize/setup hot plug controller hw */
+ int (*init_hpc_hw)(struct pciehpc *ctrl_p);
+ /* initialize slot information structure */
+ int (*init_hpc_slotinfo)(struct pciehpc *ctrl_p);
+ /* disable hot plug interrupts/events */
+ int (*disable_hpc_intr)(struct pciehpc *ctrl_p);
+ /* enable hot plug interrupts/events */
+ int (*enable_hpc_intr)(struct pciehpc *ctrl_p);
+ /* uninitialize hot plug controller hw */
+ int (*uninit_hpc_hw)(struct pciehpc *ctrl_p);
+ /* uninitialize slot information structure */
+ int (*uninit_hpc_slotinfo)(struct pciehpc *ctrl_p);
+ /* probe for HPC */
+ int (*probe_hpc)(struct pciehpc *ctrl_p);
+ } ops;
+
+ /* platform implementation specific data if any: ACPI, CK804,... */
+ void *misc_data;
+} pciehpc_t;
+
+typedef struct pciehpc_ops pciehpc_ops_t;
+
+/*
+ * PCI-E HPC Command Completion delay in microseconds and the max retry
+ * count.
+ */
+#define PCIEHPC_CMD_WAIT_TIME 10000
+#define PCIEHPC_CMD_WAIT_RETRY 100
+
+#define SLOTCTL_SUPPORTED_INTRS_MASK \
+ (PCIE_SLOTCTL_ATTN_BTN_EN \
+ | PCIE_SLOTCTL_PWR_FAULT_EN \
+ | PCIE_SLOTCTL_MRL_SENSOR_EN \
+ | PCIE_SLOTCTL_PRESENCE_CHANGE_EN \
+ | PCIE_SLOTCTL_CMD_INTR_EN \
+ | PCIE_SLOTCTL_HP_INTR_EN)
+
+#define SLOT_STATUS_EVENTS \
+ (PCIE_SLOTSTS_ATTN_BTN_PRESSED \
+ | PCIE_SLOTSTS_PWR_FAULT_DETECTED \
+ | PCIE_SLOTSTS_MRL_SENSOR_CHANGED \
+ | PCIE_SLOTSTS_COMMAND_COMPLETED \
+ | PCIE_SLOTSTS_PRESENCE_CHANGED)
+
+/*
+ * function prototype defintions for common native mode functions in
+ * PCIEHPC module.
+ */
+int pciehpc_hpc_init(pciehpc_t *ctrl_p);
+int pciehpc_hpc_uninit(pciehpc_t *ctrl_p);
+int pciehpc_slotinfo_init(pciehpc_t *ctrl_p);
+int pciehpc_enable_intr(pciehpc_t *ctrl_p);
+int pciehpc_disable_intr(pciehpc_t *ctrl_p);
+int pciehpc_slotinfo_uninit(pciehpc_t *ctrl_p);
+int pciehpc_probe_hpc(pciehpc_t *ctrl_p);
+hpc_led_state_t pciehpc_led_state_to_hpc(uint16_t state);
+uint16_t pciehpc_led_state_to_pciehpc(hpc_led_state_t state);
+hpc_led_state_t pciehpc_get_led_state(pciehpc_t *ctrl_p, hpc_led_t led);
+void pciehpc_set_led_state(pciehpc_t *ctrl_p, hpc_led_t led,
+ hpc_led_state_t state);
+int pciehpc_slot_connect(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ void *data, uint_t flags);
+int pciehpc_slot_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ void *data, uint_t flags);
+int pciehpc_slot_control(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ int request, caddr_t arg);
+void pciehpc_get_slot_state(pciehpc_t *ctrl_p);
+void pciehpc_issue_hpc_command(pciehpc_t *ctrl_p, uint16_t control);
+int pciehpc_regs_setup(dev_info_t *dip, uint_t rnum, offset_t off,
+ caddr_t *addrp, ddi_acc_handle_t *handle);
+void pciehpc_regs_teardown(ddi_acc_handle_t *handle);
+int pciehpc_register_slot(pciehpc_t *ctrl_p);
+int pciehpc_unregister_slot(pciehpc_t *ctrl_p);
+uint8_t pciehpc_reg_get8(pciehpc_t *ctrl_p, uint_t off);
+uint16_t pciehpc_reg_get16(pciehpc_t *ctrl_p, uint_t off);
+uint32_t pciehpc_reg_get32(pciehpc_t *ctrl_p, uint_t off);
+void pciehpc_reg_put8(pciehpc_t *ctrl_p, uint_t off, uint8_t val);
+void pciehpc_reg_put16(pciehpc_t *ctrl_p, uint_t off, uint16_t val);
+void pciehpc_reg_put32(pciehpc_t *ctrl_p, uint_t off, uint32_t val);
+void pciehpc_set_slot_name(pciehpc_t *ctrl_p);
+
+#if defined(__i386) || defined(__amd64)
+void pciehpc_update_ops(pciehpc_t *ctrl_p);
+#endif /* defined(__i386) || defined(__amd64) */
+
+#ifdef DEBUG
+extern int pciehpc_debug;
+#define PCIEHPC_DEBUG(args) if (pciehpc_debug >= 1) cmn_err args
+#define PCIEHPC_DEBUG2(args) if (pciehpc_debug >= 2) cmn_err args
+#define PCIEHPC_DEBUG3(args) if (pciehpc_debug >= 3) cmn_err args
+#else
+#define PCIEHPC_DEBUG(args)
+#define PCIEHPC_DEBUG2(args)
+#define PCIEHPC_DEBUG3(args)
+#endif
+
+/* default interrupt priority for Hot Plug interrupts */
+#define PCIEHPC_INTR_PRI 1
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_HOTPLUG_PCI_PCIEHPC_IMPL_H */
diff --git a/usr/src/uts/common/sys/hotplug/pci/pcihp.h b/usr/src/uts/common/sys/hotplug/pci/pcihp.h
index 48a6db128a..cd8f57a7e7 100644
--- a/usr/src/uts/common/sys/hotplug/pci/pcihp.h
+++ b/usr/src/uts/common/sys/hotplug/pci/pcihp.h
@@ -20,8 +20,8 @@
* CDDL HEADER END
*/
/*
- * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
- * All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
*/
#ifndef _SYS_HOTPLUG_PCI_PCIHP_H
@@ -33,18 +33,16 @@
extern "C" {
#endif
+#include <sys/hotplug/pci/pcihp_impl.h>
+
/*
* Interfaces exported by PCI Nexus extension module, kernel/misc/pcihp.
*/
int pcihp_init(dev_info_t *);
int pcihp_uninit(dev_info_t *);
int pcihp_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
-
struct cb_ops *pcihp_get_cb_ops(void);
-/* exported data definitions */
-extern struct cb_ops pcihp_cb_ops;
-
/* definitions for minor numbers */
#define PCIHP_AP_MINOR_NUM(x, y) (((uint_t)(x) << 8) | \
((y) & 0xFF))
diff --git a/usr/src/uts/common/sys/hotplug/pci/pcihp_impl.h b/usr/src/uts/common/sys/hotplug/pci/pcihp_impl.h
new file mode 100644
index 0000000000..8a7d616ee8
--- /dev/null
+++ b/usr/src/uts/common/sys/hotplug/pci/pcihp_impl.h
@@ -0,0 +1,56 @@
+/*
+ * 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_HOTPLUG_PCI_PCIHP_IMPL_H
+#define _SYS_HOTPLUG_PCI_PCIHP_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * we recognize the non transparent bridge child nodes with the
+ * following property. This is specific to an implementation only.
+ * This property is specific to AP nodes only.
+ */
+#define PCI_DEV_CONF_MAP_PROP "pci-parent-indirect"
+
+/*
+ * If a bridge device provides its own config space access services,
+ * and supports a hotplug/hotswap bus below at any level, then
+ * the following property must be defined for the node either by
+ * the driver or the OBP.
+ */
+#define PCI_BUS_CONF_MAP_PROP "pci-conf-indirect"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_HOTPLUG_PCI_PCIHP_IMPL_H */
diff --git a/usr/src/uts/common/sys/pci.h b/usr/src/uts/common/sys/pci.h
index 55a924fc54..bd0bb7cd52 100644
--- a/usr/src/uts/common/sys/pci.h
+++ b/usr/src/uts/common/sys/pci.h
@@ -541,6 +541,7 @@ extern "C" {
*/
#define PCI_CAP_ID 0x0 /* capability identifier, 1 byte */
#define PCI_CAP_NEXT_PTR 0x1 /* next entry pointer, 1 byte */
+#define PCI_CAP_ID_REGS_OFF 0x2 /* cap id register offset */
#define PCI_CAP_MAX_PTR 0x30 /* maximum number of cap pointers */
#define PCI_CAP_PTR_OFF 0x40 /* minimum cap pointer offset */
#define PCI_CAP_PTR_MASK 0xFC /* mask for capability pointer */
@@ -699,6 +700,18 @@ extern "C" {
#define PCI_MSIX_MAX_INTRS 2048 /* maximum MSI-X interrupts supported */
/*
+ * PCI Slot Id Capabilities, 2 bytes
+ */
+/* Byte 1: Expansion Slot Register (ESR), Byte 2: Chassis Number Register */
+#define PCI_CAPSLOT_ESR_NSLOTS_MASK 0x1F /* Number of slots mask */
+#define PCI_CAPSLOT_ESR_FIC 0x20 /* First In Chassis bit */
+#define PCI_CAPSLOT_ESR_FIC_MASK 0x01 /* First In Chassis mask */
+#define PCI_CAPSLOT_ESR_FIC_SHIFT 5 /* First In Chassis shift */
+#define PCI_CAPSLOT_FIC(esr_reg) ((esr_reg) & PCI_CAPSLOT_ESR_FIC)
+#define PCI_CAPSLOT_NSLOTS(esr_reg) ((esr_reg) & \
+ PCI_CAPSLOT_ESR_NSLOTS_MASK)
+
+/*
* other interesting PCI constants
*/
#define PCI_BASE_NUM 6 /* num of base regs in configuration header */
@@ -706,16 +719,12 @@ extern "C" {
#define PCI_BAR_SZ_64 8 /* size of 64 bit base addr reg in bytes */
#define PCI_BASE_SIZE 4 /* size of base reg in bytes */
#define PCI_CONF_HDR_SIZE 256 /* configuration header size */
+#define PCI_MAX_BUS_NUM 256 /* Maximum PCI buses allowed */
#define PCI_CLK_33MHZ (33 * 1000 * 1000) /* 33MHz clock speed */
#define PCI_CLK_66MHZ (66 * 1000 * 1000) /* 66MHz clock speed */
#define PCI_CLK_133MHZ (133 * 1000 * 1000) /* 133MHz clock speed */
/*
- * PCI-Express definitions
- */
-#define PCIE_CONF_HDR_SIZE 4096 /* PCI-Ex config header size */
-
-/*
* This structure represents one entry of the 1275 "reg" property and
* "assigned-addresses" property for a PCI node. For the "reg" property, it
* may be one of an arbitrary length array for devices with multiple address
diff --git a/usr/src/uts/common/sys/pcie.h b/usr/src/uts/common/sys/pcie.h
index d7ae4ccc19..61812bab0f 100644
--- a/usr/src/uts/common/sys/pcie.h
+++ b/usr/src/uts/common/sys/pcie.h
@@ -36,7 +36,8 @@ extern "C" {
#include <sys/pci.h>
/*
- * PCI-Express Capability Registers Offsets
+ * PCI Express capability registers in PCI configuration space relative to
+ * the PCI Express Capability structure.
*/
#define PCIE_CAP_ID PCI_CAP_ID
#define PCIE_CAP_NEXT_PTR PCI_CAP_NEXT_PTR
@@ -54,7 +55,12 @@ extern "C" {
#define PCIE_ROOTSTS 0x20 /* Root Status */
/*
- * PCI-Express Capabilities Register
+ * PCI-Express Config Space size
+ */
+#define PCIE_CONF_HDR_SIZE 4096 /* PCIe configuration header size */
+
+/*
+ * PCI-Express Capabilities Register (2 bytes)
*/
#define PCIE_PCIECAP_VER_1_0 0x1 /* PCI-E spec 1.0 */
#define PCIE_PCIECAP_VER_MASK 0xF /* Version Mask */
@@ -70,7 +76,7 @@ extern "C" {
#define PCIE_PCIECAP_INT_MSG_NUM 0x3700 /* Interrupt Message Number */
/*
- * Device Capabilities Register
+ * Device Capabilities Register (4 bytes)
*/
#define PCIE_DEVCAP_MAX_PAYLOAD_128 0x0
#define PCIE_DEVCAP_MAX_PAYLOAD_256 0x1
@@ -124,7 +130,7 @@ extern "C" {
#define PCIE_DEVCAP_PLMT_SCL_MASK 0xC000000 /* Power Limit Scale */
/*
- * Device Control Register
+ * Device Control Register (2 bytes)
*/
#define PCIE_DEVCTL_CE_REPORTING_EN 0x1 /* Correctable Error Enable */
#define PCIE_DEVCTL_NFE_REPORTING_EN 0x2 /* Non-Fatal Error Enable */
@@ -154,7 +160,7 @@ extern "C" {
#define PCIE_DEVCTL_MAX_READ_REQ_MASK 0x7000 /* Max_Read_Request_Size */
/*
- * Device Status Register
+ * Device Status Register (2 bytes)
*/
#define PCIE_DEVSTS_CE_DETECTED 0x1 /* Correctable Error Detected */
#define PCIE_DEVSTS_NFE_DETECTED 0x2 /* Non Fatal Error Detected */
@@ -164,7 +170,7 @@ extern "C" {
#define PCIE_DEVSTS_TRANS_PENDING 0x20 /* Transactions Pending */
/*
- * Link Capability Register
+ * Link Capability Register (4 bytes)
*/
#define PCIE_LINKCAP_MAX_SPEED_2_5 0x1 /* 2.5 Gb/s Speed */
#define PCIE_LINKCAP_MAX_SPEED_MASK 0xF /* Maximum Link Speed */
@@ -201,10 +207,14 @@ extern "C" {
#define PCIE_LINKCAP_L1_EXIT_LAT_MAX 0x38000 /* > 64 us */
#define PCIE_LINKCAP_L1_EXIT_LAT_MASK 0x38000 /* L1 Exit Latency */
+/* PCIe v1.1 spec based */
+#define PCIE_LINKCAP_DLL_ACTIVE_REP_CAPABLE 0x100000 /* DLL Active */
+ /* Capable bit */
+
#define PCIE_LINKCAP_PORT_NUMBER 0xF0000000 /* Port Number */
/*
- * Link Control Register
+ * Link Control Register (2 bytes)
*/
#define PCIE_LINKCTL_ASPM_CTL_DIS 0x0 /* ASPM Disable */
#define PCIE_LINKCTL_ASPM_CTL_L0S 0x1 /* ASPM L0s only */
@@ -222,7 +232,7 @@ extern "C" {
#define PCIE_LINKCTL_EXT_SYNCH 0x80 /* Extended Synch */
/*
- * Link Status Register
+ * Link Status Register (2 bytes)
*/
#define PCIE_LINKSTS_SPEED_2_5 0x1 /* Link Speed */
#define PCIE_LINKSTS_SPEED_MASK 0xF /* Link Speed */
@@ -241,7 +251,7 @@ extern "C" {
#define PCIE_LINKSTS_SLOT_CLK_CFG 0x1000 /* Slot Clock Configuration */
/*
- * Slot Capability Register
+ * Slot Capability Register (4 bytes)
*/
#define PCIE_SLOTCAP_ATTN_BUTTON 0x1 /* Attention Button Present */
#define PCIE_SLOTCAP_POWER_CONTROLLER 0x2 /* Power Controller Present */
@@ -259,12 +269,18 @@ extern "C" {
#define PCIE_SLOTCAP_PLMT_SCL_1_BY_100 0x10000 /* 0.01x Scale */
#define PCIE_SLOTCAP_PLMT_SCL_1_BY_1000 0x18000 /* 0.001x Scale */
#define PCIE_SLOTCAP_PLMT_SCL_MASK 0x18000 /* Slot Power Limit Scale */
+#define PCIE_SLOTCAP_EMI_LOCK_PRESENT 0x20000 /* EMI Lock Present */
+#define PCIE_SLOTCAP_NO_CMD_COMP_SUPP 0x40000 /* No Command Comp. Supported */
#define PCIE_SLOTCAP_PHY_SLOT_NUM_SHIFT 19 /* Physical Slot Num Shift */
#define PCIE_SLOTCAP_PHY_SLOT_NUM_MASK 0x1FFF /* Physical Slot Num Mask */
+#define PCIE_SLOTCAP_PHY_SLOT_NUM(reg) \
+ (((reg) >> PCIE_SLOTCAP_PHY_SLOT_NUM_SHIFT) & \
+ PCIE_SLOTCAP_PHY_SLOT_NUM_MASK)
+
/*
- * Slot Control Register
+ * Slot Control Register (2 bytes)
*/
#define PCIE_SLOTCTL_ATTN_BTN_EN 0x1 /* Attn Button Pressed Enable */
#define PCIE_SLOTCTL_PWR_FAULT_EN 0x2 /* Pwr Fault Detected Enable */
@@ -272,40 +288,52 @@ extern "C" {
#define PCIE_SLOTCTL_PRESENCE_CHANGE_EN 0x8 /* Presence Detect Changed En */
#define PCIE_SLOTCTL_CMD_INTR_EN 0x10 /* CMD Completed Interrupt En */
#define PCIE_SLOTCTL_HP_INTR_EN 0x20 /* Hot-Plug Interrupt Enable */
+#define PCIE_SLOTCTL_PWR_CONTROL 0x0400 /* Power controller Control */
+#define PCIE_SLOTCTL_EMI_LOCK_CONTROL 0x0800 /* EMI Lock control */
+#define PCIE_SLOTCTL_ATTN_INDICATOR_MASK 0x00C0 /* Attn Indicator mask */
+#define PCIE_SLOTCTL_PWR_INDICATOR_MASK 0x0300 /* Power Indicator mask */
-#define PCIE_SLOTCTL_ATTN_CTL_ON 0x40 /* On */
-#define PCIE_SLOTCTL_ATTN_CTL_BLINK 0x80 /* Blink */
-#define PCIE_SLOTCTL_ATTN_CTL_OFF 0xC0 /* Off */
-#define PCIE_SLOTCTL_ATTN_CTL_MASK 0xC0 /* Attn Indicator Control */
-
-#define PCIE_SLOTCTL_PWR_CTL_ON 0x100 /* On */
-#define PCIE_SLOTCTL_PWR_CTL_BLINK 0x200 /* Blink */
-#define PCIE_SLOTCTL_PWR_CTL_OFF 0x300 /* Off */
-#define PCIE_SLOTCTL_PWR_CTL_MASK 0x300 /* Power Indicator Control */
+/* State values for the Power and Attention Indicators */
+#define PCIE_SLOTCTL_INDICATOR_STATE_ON 0x1 /* indicator ON */
+#define PCIE_SLOTCTL_INDICATOR_STATE_BLINK 0x2 /* indicator BLINK */
+#define PCIE_SLOTCTL_INDICATOR_STATE_OFF 0x3 /* indicator OFF */
-#define PCIE_SLOTCTL_PWR_CONTROLLER_CTL 0x400 /* Power Controller Control */
+/*
+ * Macros to set/get the state of Power and Attention Indicators
+ * in the PCI Express Slot Control Register.
+ */
+#define pcie_slotctl_pwr_indicator_get(reg) \
+ (((reg) & PCIE_SLOTCTL_PWR_INDICATOR_MASK) >> 8)
+#define pcie_slotctl_attn_indicator_get(ctrl) \
+ (((ctrl) & PCIE_SLOTCTL_ATTN_INDICATOR_MASK) >> 6)
+#define pcie_slotctl_attn_indicator_set(ctrl, v)\
+ (((ctrl) & ~PCIE_SLOTCTL_ATTN_INDICATOR_MASK) | ((v) << 6))
+#define pcie_slotctl_pwr_indicator_set(ctrl, v)\
+ (((ctrl) & ~PCIE_SLOTCTL_PWR_INDICATOR_MASK) | ((v) << 8))
/*
- * Slot Status Register
+ * Slot Status register (2 bytes)
*/
#define PCIE_SLOTSTS_ATTN_BTN_PRESSED 0x1 /* Attention Button Pressed */
#define PCIE_SLOTSTS_PWR_FAULT_DETECTED 0x2 /* Power Fault Detected */
#define PCIE_SLOTSTS_MRL_SENSOR_CHANGED 0x4 /* MRL Sensor Changed */
#define PCIE_SLOTSTS_PRESENCE_CHANGED 0x8 /* Presence Detect Changed */
#define PCIE_SLOTSTS_COMMAND_COMPLETED 0x10 /* Command Completed */
-#define PCIE_SLOTSTS_MRL_SENSOR_OPEN 0x20 /* MRL Open */
+#define PCIE_SLOTSTS_MRL_SENSOR_OPEN 0x20 /* MRL Sensor Open */
#define PCIE_SLOTSTS_PRESENCE_DETECTED 0x40 /* Card Present in slot */
+#define PCIE_SLOTSTS_EMI_LOCK_SET 0x0080 /* EMI Lock set */
+#define PCIE_SLOTSTS_DLL_STATE_CHANGED 0x0100 /* DLL State Changed */
/*
- * Root Control Register
+ * Root Control Register (2 bytes)
*/
#define PCIE_ROOTCTL_SYS_ERR_ON_CE_EN 0x1 /* Sys Err on Cor Err Enable */
#define PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN 0x2 /* Sys Err on NF Err Enable */
-#define PCIE_ROOTCTL_SYS_ERR_ON_FE_EN 0x3 /* Sys Err on Fatal Err En */
-#define PCIE_ROOTCTL_PME_INTERRUPT_EN 0x4 /* PME Interrupt Enable */
+#define PCIE_ROOTCTL_SYS_ERR_ON_FE_EN 0x4 /* Sys Err on Fatal Err En */
+#define PCIE_ROOTCTL_PME_INTERRUPT_EN 0x8 /* PME Interrupt Enable */
/*
- * Root Status Register
+ * Root Status Register (4 bytes)
*/
#define PCIE_ROOTSTS_PME_REQ_ID_SHIFT 0 /* PME Requestor ID */
#define PCIE_ROOTSTS_PME_REQ_ID_MASK 0xFFFF /* PME Requestor ID */
diff --git a/usr/src/uts/i86pc/Makefile.files b/usr/src/uts/i86pc/Makefile.files
index a3a6bcd5d0..9d67f854e9 100644
--- a/usr/src/uts/i86pc/Makefile.files
+++ b/usr/src/uts/i86pc/Makefile.files
@@ -108,7 +108,7 @@ SPECIAL_OBJS += $(SPECIAL_OBJS_$(CLASS))
#
ROOTNEX_OBJS += rootnex.o
ISANEXUS_OBJS += isa.o dma_engine.o i8237A.o
-PCINEXUS_OBJS += pci.o pci_tools.o
+PCINEXUS_OBJS += pci.o pci_common.o pci_tools.o
TCIC_OBJS += tcic.o
@@ -146,12 +146,20 @@ BSCV_OBJS += bscv.o
BSCBUS_OBJS += bscbus.o
#
+# PCI-Express driver modules
+#
+PCI_E_NEXUS_OBJS += npe.o npe_misc.o pci_common.o pci_tools.o pcie_error.o
+PCI_E_PCINEXUS_OBJS += pcie_pci.o pcie_error.o
+PCIEHPCNEXUS_OBJS += pciehpc_x86.o pciehpc_acpi.o pciehpc_ck804.o
+
+#
# platform specific modules
#
UPPC_OBJS += uppc.o psm_common.o
PCI_AUTOCONFIG_OBJS += \
pci_autoconfig.o \
pci_boot.o \
+ pcie_ck804_boot.o \
pci_memlist.o \
pci_resource.o
diff --git a/usr/src/uts/i86pc/Makefile.i86pc b/usr/src/uts/i86pc/Makefile.i86pc
index 8c66b144e1..adb6395127 100644
--- a/usr/src/uts/i86pc/Makefile.i86pc
+++ b/usr/src/uts/i86pc/Makefile.i86pc
@@ -243,6 +243,8 @@ DRV_KMODS += rootnex
DRV_KMODS += isa
DRV_KMODS += pci
DRV_KMODS += pcic
+DRV_KMODS += pcie_pci
+DRV_KMODS += npe
DRV_KMODS_32 += bscv
DRV_KMODS_32 += bscbus
@@ -308,7 +310,7 @@ SYS_KMODS +=
#
# 'Misc' Modules (/kernel/misc):
#
-MISC_KMODS += pci_autoconfig bootdev acpica
+MISC_KMODS += pci_autoconfig bootdev acpica pciehpc
#
# 'Mach' Modules (/kernel/mach):
diff --git a/usr/src/uts/i86pc/Makefile.rules b/usr/src/uts/i86pc/Makefile.rules
index 8dafce36c3..b026324518 100644
--- a/usr/src/uts/i86pc/Makefile.rules
+++ b/usr/src/uts/i86pc/Makefile.rules
@@ -69,6 +69,14 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/pci/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/pciex/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/pciex/hotplug/pciehpc/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/i86pc/io/pcplusmp/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -221,6 +229,12 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/bmc/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/pci/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/pciex/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/pciex/hotplug/pciehpc/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/i86pc/io/pcplusmp/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/i86pc/io/acpica/osl.c b/usr/src/uts/i86pc/io/acpica/osl.c
index 84d75338e0..0128cc551b 100644
--- a/usr/src/uts/i86pc/io/acpica/osl.c
+++ b/usr/src/uts/i86pc/io/acpica/osl.c
@@ -1288,7 +1288,8 @@ create_d2a_map(void)
"device_type", &device_type_prop) != DDI_PROP_SUCCESS)
continue;
- if (strcmp("pci", device_type_prop) != 0) {
+ if ((strcmp("pci", device_type_prop) != 0) &&
+ (strcmp("pciex", device_type_prop) != 0)) {
ddi_prop_free(device_type_prop);
continue;
}
diff --git a/usr/src/uts/i86pc/io/pci/pci.c b/usr/src/uts/i86pc/io/pci/pci.c
index 3ad2d3832a..14bf8a4d5e 100644
--- a/usr/src/uts/i86pc/io/pci/pci.c
+++ b/usr/src/uts/i86pc/io/pci/pci.c
@@ -31,26 +31,16 @@
*/
#include <sys/conf.h>
-#include <sys/kmem.h>
-#include <sys/debug.h>
#include <sys/modctl.h>
-#include <sys/autoconf.h>
-#include <sys/ddi_impldefs.h>
-#include <sys/ddi_subrdefs.h>
#include <sys/pci.h>
#include <sys/pci_impl.h>
-#include <sys/ddi.h>
-#include <sys/sunddi.h>
+#include <sys/sysmacros.h>
#include <sys/sunndi.h>
#include <sys/hotplug/pci/pcihp.h>
#include <sys/pci_cfgspace.h>
-#include <sys/avintr.h>
-#include <sys/psm.h>
-#include <sys/pci_intr_lib.h>
-#include <sys/policy.h>
-#include <sys/pci_tools.h>
-#include "pci_tools_ext.h"
-#include "pci_var.h"
+#include <io/pci/pci_common.h>
+#include <io/pci/pci_tools_ext.h>
+#include <io/pci/pci_var.h>
/* Save minimal state. */
void *pci_statep;
@@ -64,16 +54,6 @@ static int pci_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
void *, void *);
static int pci_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
ddi_intr_handle_impl_t *, void *);
-static int pci_get_priority(dev_info_t *, int, int *);
-static int pci_get_nintrs(dev_info_t *, int, int *);
-static int pci_enable_intr(dev_info_t *, dev_info_t *,
- ddi_intr_handle_impl_t *, uint32_t);
-static void pci_disable_intr(dev_info_t *, dev_info_t *,
- ddi_intr_handle_impl_t *, uint32_t);
-
-/* Extern decalrations */
-extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *,
- psm_intr_op_t, int *);
struct bus_ops pci_bus_ops = {
BUSO_REV,
@@ -107,19 +87,18 @@ struct bus_ops pci_bus_ops = {
pci_intr_ops /* (*bus_intr_op)(); */
};
-static int pci_open(dev_t *devp, int flags, int otyp, cred_t *credp);
-static int pci_close(dev_t dev, int flags, int otyp, cred_t *credp);
-static int pci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
- int *rvalp);
-static int pci_prop_op(dev_t dev, dev_info_t *devi, ddi_prop_op_t prop_op,
- int flags, char *name, caddr_t valuep, int *lengthp);
-
/*
* One goal here is to leverage off of the pcihp.c source without making
* changes to it. Call into it's cb_ops directly if needed, piggybacking
* anything else needed by the pci_tools.c module. Only pci_tools and pcihp
* will be opening PCI nexus driver file descriptors.
*/
+static int pci_open(dev_t *, int, int, cred_t *);
+static int pci_close(dev_t, int, int, cred_t *);
+static int pci_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+static int pci_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
+ caddr_t, int *);
+static int pci_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
struct cb_ops pci_cb_ops = {
pci_open, /* open */
@@ -145,8 +124,6 @@ struct cb_ops pci_cb_ops = {
/*
* Device Node Operation functions
*/
-static int pci_info(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg,
- void **result);
static int pci_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
static int pci_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
@@ -178,11 +155,6 @@ static int pci_removechild(dev_info_t *child);
static int pci_initchild(dev_info_t *child);
/*
- * Miscellaneous internal functions
- */
-static int pci_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp);
-
-/*
* These are the access routines. The pci_bus_map sets the handle
* to point to these.
*/
@@ -342,51 +314,6 @@ pci_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
}
static int
-pci_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp)
-{
- pci_regspec_t *assigned_addr;
- int assigned_addr_len;
- uint_t phys_hi;
- int i;
- int rc;
- int number;
-
- phys_hi = pci_rp->pci_phys_hi;
- if (((phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_CONFIG) ||
- (phys_hi & PCI_RELOCAT_B))
- return (DDI_SUCCESS);
-
- /*
- * the "reg" property specifies relocatable, get and interpret the
- * "assigned-addresses" property.
- */
- rc = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
- DDI_PROP_DONTPASS, "assigned-addresses",
- (int **)&assigned_addr, (uint_t *)&assigned_addr_len);
- if (rc != DDI_PROP_SUCCESS)
- return (DDI_FAILURE);
-
- /*
- * Scan the "assigned-addresses" for one that matches the specified
- * "reg" property entry.
- */
- phys_hi &= PCI_CONF_ADDR_MASK;
- number = assigned_addr_len / (sizeof (pci_regspec_t) / sizeof (int));
- for (i = 0; i < number; i++) {
- if ((assigned_addr[i].pci_phys_hi & PCI_CONF_ADDR_MASK) ==
- phys_hi) {
- pci_rp->pci_phys_mid = assigned_addr[i].pci_phys_mid;
- pci_rp->pci_phys_low = assigned_addr[i].pci_phys_low;
- ddi_prop_free(assigned_addr);
- return (DDI_SUCCESS);
- }
- }
-
- ddi_prop_free(assigned_addr);
- return (DDI_FAILURE);
-}
-
-static int
pci_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
off_t offset, off_t len, caddr_t *vaddrp)
{
@@ -398,7 +325,6 @@ pci_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
pci_regspec_t *pci_rp;
int rnumber;
int length;
- int rc;
pci_acc_cfblk_t *cfp;
int space;
@@ -413,7 +339,7 @@ pci_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
case DDI_MT_REGSPEC:
pci_reg = *(pci_regspec_t *)(mp->map_obj.rp);
pci_rp = &pci_reg;
- if (pci_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS)
+ if (pci_common_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS)
return (DDI_FAILURE);
break;
case DDI_MT_RNUMBER:
@@ -427,12 +353,10 @@ pci_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
* This routine still performs some validity checks to
* make sure that everything is okay.
*/
- rc = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
- DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
- (uint_t *)&length);
- if (rc != DDI_PROP_SUCCESS) {
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
+ DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
+ (uint_t *)&length) != DDI_PROP_SUCCESS)
return (DDI_FAILURE);
- }
/*
* validate the register number.
@@ -454,7 +378,7 @@ pci_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
ddi_prop_free(pci_rp);
pci_rp = &pci_reg;
- if (pci_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS)
+ if (pci_common_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS)
return (DDI_FAILURE);
mp->map_type = DDI_MT_REGSPEC;
break;
@@ -522,10 +446,9 @@ pci_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
if (space == PCI_ADDR_CONFIG) {
hp = (ddi_acc_hdl_t *)mp->map_handlep;
- if (hp == NULL) {
- /* Can't map config space without a handle */
+ /* Can't map config space without a handle */
+ if (hp == NULL)
return (DDI_FAILURE);
- }
ap = (ddi_acc_impl_t *)hp->ah_platform_private;
@@ -624,464 +547,6 @@ pci_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
}
-/*
- * pci_get_priority:
- * Figure out the priority of the device
- */
-static int
-pci_get_priority(dev_info_t *dip, int inum, int *pri)
-{
- struct intrspec *ispec;
-
- DDI_INTR_NEXDBG((CE_CONT, "pci_get_priority: dip = 0x%p\n",
- (void *)dip));
-
- if ((ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip, inum)) ==
- NULL)
- return (DDI_FAILURE);
-
- *pri = ispec->intrspec_pri;
-
- return (DDI_SUCCESS);
-}
-
-
-/*
- * pci_get_nintrs:
- * Figure out how many interrupts the device supports
- */
-static int
-pci_get_nintrs(dev_info_t *dip, int type, int *nintrs)
-{
- int ret;
-
- *nintrs = 0;
-
- if (DDI_INTR_IS_MSI_OR_MSIX(type))
- ret = pci_msi_get_nintrs(dip, type, nintrs);
- else {
- ret = DDI_FAILURE;
- if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
- "interrupts", -1) != -1) {
- *nintrs = 1;
- ret = DDI_SUCCESS;
- }
- }
-
- return (ret);
-}
-
-
-/*
- * pci_intr_ops: bus_intr_op() function for interrupt support
- */
-/* ARGSUSED */
-static int
-pci_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
- ddi_intr_handle_impl_t *hdlp, void *result)
-{
- int priority = 0;
- int psm_status = 0;
- int pci_status = 0;
- int pci_rval, psm_rval = PSM_FAILURE;
- int types = 0;
- int i, j;
- int behavior;
- ddi_intrspec_t isp;
- struct intrspec *ispec;
- ddi_intr_handle_impl_t tmp_hdl;
- ddi_intr_msix_t *msix_p;
-
- DDI_INTR_NEXDBG((CE_CONT,
- "pci_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
- (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
-
- /* Process the request */
- switch (intr_op) {
- case DDI_INTROP_SUPPORTED_TYPES:
- /* Fixed supported by default */
- *(int *)result = DDI_INTR_TYPE_FIXED;
-
- /* Figure out if MSI or MSI-X is supported? */
- if (pci_msi_get_supported_type(rdip, &types) != DDI_SUCCESS)
- return (DDI_SUCCESS);
-
- if (psm_intr_ops != NULL) {
- /* MSI or MSI-X is supported, OR it in */
- *(int *)result |= types;
-
- tmp_hdl.ih_type = *(int *)result;
- (void) (*psm_intr_ops)(rdip, &tmp_hdl,
- PSM_INTR_OP_CHECK_MSI, result);
- DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: rdip: 0x%p "
- "supported types: 0x%x\n", (void *)rdip,
- *(int *)result));
- }
- break;
- case DDI_INTROP_NINTRS:
- if (pci_get_nintrs(rdip, hdlp->ih_type, result) != DDI_SUCCESS)
- return (DDI_FAILURE);
- break;
- case DDI_INTROP_ALLOC:
- /*
- * MSI or MSIX (figure out number of vectors available)
- * FIXED interrupts: just return available interrupts
- */
- if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) &&
- (psm_intr_ops != NULL) &&
- (pci_get_priority(rdip, hdlp->ih_inum,
- &priority) == DDI_SUCCESS)) {
- hdlp->ih_pri = priority;
- behavior = hdlp->ih_scratch2;
- (void) (*psm_intr_ops)(rdip, hdlp,
- PSM_INTR_OP_ALLOC_VECTORS, result);
-
- /* verify behavior flag and take appropriate action */
- if ((behavior == DDI_INTR_ALLOC_STRICT) &&
- (*(int *)result < hdlp->ih_scratch1)) {
- DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: "
- "behavior %x, couldn't get enough intrs\n",
- behavior));
- hdlp->ih_scratch1 = *(int *)result;
- (void) (*psm_intr_ops)(rdip, hdlp,
- PSM_INTR_OP_FREE_VECTORS, NULL);
- return (DDI_EAGAIN);
- }
-
- if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
- if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) {
- msix_p = pci_msix_init(hdlp->ih_dip);
- if (msix_p)
- i_ddi_set_msix(hdlp->ih_dip,
- msix_p);
- }
- msix_p->msix_intrs_in_use += *(int *)result;
- }
-
- } else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) {
- /* Figure out if this device supports MASKING */
- pci_rval = pci_intx_get_cap(rdip, &pci_status);
- if (pci_rval == DDI_SUCCESS && pci_status)
- hdlp->ih_cap |= pci_status;
- *(int *)result = 1; /* DDI_INTR_TYPE_FIXED */
- } else
- return (DDI_FAILURE);
- break;
- case DDI_INTROP_FREE:
- if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) &&
- (psm_intr_ops != NULL)) {
- (void) (*psm_intr_ops)(rdip, hdlp,
- PSM_INTR_OP_FREE_VECTORS, NULL);
-
- if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
- msix_p = i_ddi_get_msix(hdlp->ih_dip);
- if (msix_p &&
- --msix_p->msix_intrs_in_use == 0) {
- pci_msix_fini(msix_p);
- i_ddi_set_msix(hdlp->ih_dip, NULL);
- }
- }
- }
- break;
- case DDI_INTROP_GETPRI:
- if (pci_get_priority(rdip, hdlp->ih_inum, &priority) !=
- DDI_SUCCESS) /* Get the priority */
- return (DDI_FAILURE);
- DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: priority = 0x%x\n",
- priority));
- *(int *)result = priority;
- break;
- case DDI_INTROP_SETPRI:
- /* Validate the interrupt priority passed */
- if (*(int *)result > LOCK_LEVEL)
- return (DDI_FAILURE);
-
- /* Ensure that PSM is all initialized */
- if (psm_intr_ops == NULL)
- return (DDI_FAILURE);
-
- /* Change the priority */
- if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) ==
- PSM_FAILURE)
- return (DDI_FAILURE);
-
- /* update ispec */
- isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
- ispec = (struct intrspec *)isp;
- ispec->intrspec_pri = *(int *)result;
- break;
- case DDI_INTROP_ADDISR:
- /* update ispec */
- isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
- ispec = (struct intrspec *)isp;
- ispec->intrspec_func = hdlp->ih_cb_func;
- break;
- case DDI_INTROP_REMISR:
- /* Get the interrupt structure pointer */
- isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
- ispec = (struct intrspec *)isp;
- ispec->intrspec_func = (uint_t (*)()) 0;
- break;
- case DDI_INTROP_GETCAP:
- /*
- * First check the config space and/or
- * MSI capability register(s)
- */
- if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
- pci_rval = pci_msi_get_cap(rdip, hdlp->ih_type,
- &pci_status);
- else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
- pci_rval = pci_intx_get_cap(rdip, &pci_status);
-
- /* next check with pcplusmp */
- if (psm_intr_ops != NULL)
- psm_rval = (*psm_intr_ops)(rdip, hdlp,
- PSM_INTR_OP_GET_CAP, &psm_status);
-
- DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned psm_rval = %x, "
- "psm_status = %x, pci_rval = %x, pci_status = %x\n",
- psm_rval, psm_status, pci_rval, pci_status));
-
- if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) {
- *(int *)result = 0;
- return (DDI_FAILURE);
- }
-
- if (psm_rval == PSM_SUCCESS)
- *(int *)result = psm_status;
-
- if (pci_rval == DDI_SUCCESS)
- *(int *)result |= pci_status;
-
- DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned = %x\n",
- *(int *)result));
- break;
- case DDI_INTROP_SETCAP:
- DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: SETCAP cap=0x%x\n",
- *(int *)result));
- if (psm_intr_ops == NULL)
- return (DDI_FAILURE);
-
- if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) {
- DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops"
- " returned failure\n"));
- return (DDI_FAILURE);
- }
- break;
- case DDI_INTROP_ENABLE:
- DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: ENABLE\n"));
- if (psm_intr_ops == NULL)
- return (DDI_FAILURE);
-
- if (pci_enable_intr(pdip, rdip, hdlp, hdlp->ih_inum) !=
- DDI_SUCCESS)
- return (DDI_FAILURE);
-
- DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: ENABLE vector=0x%x\n",
- hdlp->ih_vector));
- break;
- case DDI_INTROP_DISABLE:
- DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: DISABLE\n"));
- if (psm_intr_ops == NULL)
- return (DDI_FAILURE);
-
- pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum);
- DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: DISABLE vector = %x\n",
- hdlp->ih_vector));
- break;
- case DDI_INTROP_BLOCKENABLE:
- DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: BLOCKENABLE\n"));
- if (hdlp->ih_type != DDI_INTR_TYPE_MSI) {
- DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: not MSI\n"));
- return (DDI_FAILURE);
- }
-
- /* Check if psm_intr_ops is NULL? */
- if (psm_intr_ops == NULL)
- return (DDI_FAILURE);
-
- for (i = 0; i < hdlp->ih_scratch1; i++) {
- if (pci_enable_intr(pdip, rdip, hdlp,
- hdlp->ih_inum + i) != DDI_SUCCESS) {
- DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: "
- "pci_enable_intr failed for %d\n", i));
- for (j = 0; j < i; j++)
- pci_disable_intr(pdip, rdip, hdlp,
- hdlp->ih_inum + j);
- return (DDI_FAILURE);
- }
- DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: BLOCKENABLE "
- "inum %x done\n", hdlp->ih_inum + i));
- }
- break;
- case DDI_INTROP_BLOCKDISABLE:
- DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: BLOCKDISABLE\n"));
- if (hdlp->ih_type != DDI_INTR_TYPE_MSI) {
- DDI_INTR_NEXDBG((CE_CONT, "BLOCKDISABLE: not MSI\n"));
- return (DDI_FAILURE);
- }
-
- /* Check if psm_intr_ops is present */
- if (psm_intr_ops == NULL)
- return (DDI_FAILURE);
-
- for (i = 0; i < hdlp->ih_scratch1; i++) {
- pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum + i);
- DDI_INTR_NEXDBG((CE_CONT, "pci_intr_ops: BLOCKDISABLE "
- "inum %x done\n", hdlp->ih_inum + i));
- }
- break;
- case DDI_INTROP_SETMASK:
- case DDI_INTROP_CLRMASK:
- /*
- * First handle in the config space
- */
- if (intr_op == DDI_INTROP_SETMASK) {
- if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
- pci_status = pci_msi_set_mask(rdip,
- hdlp->ih_type, hdlp->ih_inum);
- else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
- pci_status = pci_intx_set_mask(rdip);
- } else {
- if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
- pci_status = pci_msi_clr_mask(rdip,
- hdlp->ih_type, hdlp->ih_inum);
- else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
- pci_status = pci_intx_clr_mask(rdip);
- }
-
- /* For MSI/X; no need to check with pcplusmp */
- if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
- return (pci_status);
-
- /* For fixed interrupts only: handle config space first */
- if (hdlp->ih_type == DDI_INTR_TYPE_FIXED &&
- pci_status == DDI_SUCCESS)
- break;
-
- /* For fixed interrupts only: confer with pcplusmp next */
- if (psm_intr_ops != NULL) {
- /* If interrupt is shared; do nothing */
- psm_rval = (*psm_intr_ops)(rdip, hdlp,
- PSM_INTR_OP_GET_SHARED, &psm_status);
-
- if (psm_rval == PSM_FAILURE || psm_status == 1)
- return (pci_status);
-
- /* Now, pcplusmp should try to set/clear the mask */
- if (intr_op == DDI_INTROP_SETMASK)
- psm_rval = (*psm_intr_ops)(rdip, hdlp,
- PSM_INTR_OP_SET_MASK, NULL);
- else
- psm_rval = (*psm_intr_ops)(rdip, hdlp,
- PSM_INTR_OP_CLEAR_MASK, NULL);
- }
- return ((psm_rval == PSM_FAILURE) ? DDI_FAILURE : DDI_SUCCESS);
- case DDI_INTROP_GETPENDING:
- /*
- * First check the config space and/or
- * MSI capability register(s)
- */
- if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
- pci_rval = pci_msi_get_pending(rdip, hdlp->ih_type,
- hdlp->ih_inum, &pci_status);
- else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
- pci_rval = pci_intx_get_pending(rdip, &pci_status);
-
- /* On failure; next try with pcplusmp */
- if (pci_rval != DDI_SUCCESS && psm_intr_ops != NULL)
- psm_rval = (*psm_intr_ops)(rdip, hdlp,
- PSM_INTR_OP_GET_PENDING, &psm_status);
-
- DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned "
- "psm_rval = %x, psm_status = %x, pci_rval = %x, "
- "pci_status = %x\n", psm_rval, psm_status, pci_rval,
- pci_status));
- if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) {
- *(int *)result = 0;
- return (DDI_FAILURE);
- }
-
- if (psm_rval != PSM_FAILURE)
- *(int *)result = psm_status;
- else if (pci_rval != DDI_FAILURE)
- *(int *)result = pci_status;
- DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned = %x\n",
- *(int *)result));
- break;
- case DDI_INTROP_NAVAIL:
- if ((psm_intr_ops != NULL) && (pci_get_priority(rdip,
- hdlp->ih_inum, &priority) == DDI_SUCCESS)) {
- /* Priority in the handle not initialized yet */
- hdlp->ih_pri = priority;
- (void) (*psm_intr_ops)(rdip, hdlp,
- PSM_INTR_OP_NAVAIL_VECTORS, result);
- } else {
- *(int *)result = 1;
- }
- DDI_INTR_NEXDBG((CE_CONT, "pci: NAVAIL returned = %x\n",
- *(int *)result));
- break;
- default:
- return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
- }
-
- return (DDI_SUCCESS);
-}
-
-
-static int
-pci_enable_intr(dev_info_t *pdip, dev_info_t *rdip,
- ddi_intr_handle_impl_t *hdlp, uint32_t inum)
-{
- int vector;
- struct intrspec *ispec;
-
- DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: hdlp %p inum %x\n",
- (void *)hdlp, inum));
-
- /* Translate the interrupt if needed */
- ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum);
- if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
- ispec->intrspec_vec = inum;
- hdlp->ih_private = (void *)ispec;
-
- /* translate the interrupt if needed */
- (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &vector);
- DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: priority=%x vector=%x\n",
- hdlp->ih_pri, vector));
-
- /* Add the interrupt handler */
- if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func,
- DEVI(rdip)->devi_name, vector, hdlp->ih_cb_arg1,
- hdlp->ih_cb_arg2, rdip))
- return (DDI_FAILURE);
-
- return (DDI_SUCCESS);
-}
-
-
-static void
-pci_disable_intr(dev_info_t *pdip, dev_info_t *rdip,
- ddi_intr_handle_impl_t *hdlp, uint32_t inum)
-{
- int vector;
- struct intrspec *ispec;
-
- DDI_INTR_NEXDBG((CE_CONT, "pci_disable_intr: \n"));
- ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum);
- if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
- ispec->intrspec_vec = inum;
- hdlp->ih_private = (void *)ispec;
-
- /* translate the interrupt if needed */
- (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &vector);
-
- /* Disable the interrupt handler */
- rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, vector);
-}
-
-
/*ARGSUSED*/
static int
pci_ctlops(dev_info_t *dip, dev_info_t *rdip,
@@ -1161,74 +626,24 @@ pci_ctlops(dev_info_t *dip, dev_info_t *rdip,
}
/*
- * Assign the address portion of the node name
+ * pci_intr_ops
*/
static int
-pci_name_child(dev_info_t *child, char *name, int namelen)
+pci_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
+ ddi_intr_handle_impl_t *hdlp, void *result)
{
- pci_regspec_t *pci_rp;
- char **unit_addr;
- int dev, func, length;
- uint_t n;
-
- if (ndi_dev_is_persistent_node(child) == 0) {
- /*
- * For .conf node, use "unit-address" property
- */
- if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
- DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
- DDI_PROP_SUCCESS) {
- cmn_err(CE_WARN,
- "cannot find unit-address in %s.conf",
- ddi_get_name(child));
- return (DDI_FAILURE);
- }
- if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
- cmn_err(CE_WARN, "unit-address property in %s.conf"
- " not well-formed", ddi_get_name(child));
- ddi_prop_free(unit_addr);
- return (DDI_FAILURE);
- }
- (void) snprintf(name, namelen, "%s", *unit_addr);
- ddi_prop_free(unit_addr);
- return (DDI_SUCCESS);
- }
-
- if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
- DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
- (uint_t *)&length) != DDI_PROP_SUCCESS) {
- cmn_err(CE_WARN, "cannot find reg property in %s",
- ddi_get_name(child));
- return (DDI_FAILURE);
- }
-
- /* copy the device identifications */
- dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
- 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 (func != 0) {
- (void) snprintf(name, namelen, "%x,%x", dev, func);
- } else {
- (void) snprintf(name, namelen, "%x", dev);
- }
-
- return (DDI_SUCCESS);
+ return (pci_common_intr_ops(pdip, rdip, intr_op, hdlp, result));
}
+
static int
pci_initchild(dev_info_t *child)
{
- struct ddi_parent_private_data *pdptr;
char name[80];
ddi_acc_handle_t config_handle;
ushort_t command_preserve, command;
- if (pci_name_child(child, name, 80) != DDI_SUCCESS) {
+ if (pci_common_name_child(child, name, 80) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
ddi_set_name_addr(child, name);
@@ -1248,7 +663,8 @@ pci_initchild(dev_info_t *child)
* Try to merge the properties from this prototype
* node into real h/w nodes.
*/
- if (ndi_merge_node(child, pci_name_child) == DDI_SUCCESS) {
+ if (ndi_merge_node(child, pci_common_name_child) ==
+ DDI_SUCCESS) {
/*
* Merged ok - return failure to remove the node.
*/
@@ -1270,12 +686,7 @@ pci_initchild(dev_info_t *child)
* Create the ddi_parent_private_data for a pseudo
* child.
*/
- pdptr = (struct ddi_parent_private_data *)kmem_zalloc(
- (sizeof (struct ddi_parent_private_data) +
- sizeof (struct intrspec)), KM_SLEEP);
- pdptr->par_intr = (struct intrspec *)(pdptr + 1);
- pdptr->par_nintr = 1;
- ddi_set_parent_data(child, pdptr);
+ pci_common_set_parent_private_data(child);
return (DDI_SUCCESS);
}
@@ -1292,14 +703,9 @@ pci_initchild(dev_info_t *child)
}
if (ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
- "interrupts", -1) != -1) {
- pdptr = (struct ddi_parent_private_data *)
- kmem_zalloc((sizeof (struct ddi_parent_private_data) +
- sizeof (struct intrspec)), KM_SLEEP);
- pdptr->par_intr = (struct intrspec *)(pdptr + 1);
- pdptr->par_nintr = 1;
- ddi_set_parent_data(child, pdptr);
- } else
+ "interrupts", -1) != -1)
+ pci_common_set_parent_private_data(child);
+ else
ddi_set_parent_data(child, NULL);
/*
@@ -1624,6 +1030,7 @@ pci_config_rep_wr64(ddi_acc_impl_t *hdlp, uint64_t *host_addr,
}
}
+
/*
* When retrofitting this module for pci_tools, functions such as open, close,
* and ioctl are now pulled into this module. Before this, the functions in
@@ -1634,93 +1041,35 @@ pci_config_rep_wr64(ddi_acc_impl_t *hdlp, uint64_t *host_addr,
static int
pci_open(dev_t *devp, int flags, int otyp, cred_t *credp)
{
- return ((pcihp_cb_ops.cb_open)(devp, flags, otyp, credp));
+ return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp));
}
static int
pci_close(dev_t dev, int flags, int otyp, cred_t *credp)
{
- return ((pcihp_cb_ops.cb_close)(dev, flags, otyp, credp));
+ return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp));
}
static int
-pci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
- int *rvalp)
+pci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
{
minor_t minor = getminor(dev);
int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
pci_state_t *pci_p = ddi_get_soft_state(pci_statep, instance);
- dev_info_t *dip = pci_p->pci_dip;
- int rv = ENOTTY;
-
- switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
- case PCI_TOOL_REG_MINOR_NUM:
-
- switch (cmd) {
- case PCITOOL_DEVICE_SET_REG:
- case PCITOOL_DEVICE_GET_REG:
-
- /* Require full privileges. */
- if (secpolicy_kmdb(credp))
- rv = EPERM;
- else
- rv = pcitool_dev_reg_ops(
- dip, (void *)arg, cmd, mode);
- break;
- case PCITOOL_NEXUS_SET_REG:
- case PCITOOL_NEXUS_GET_REG:
+ if (pci_p == NULL)
+ return (ENXIO);
- /* Require full privileges. */
- if (secpolicy_kmdb(credp))
- rv = EPERM;
- else
- rv = pcitool_bus_reg_ops(
- dip, (void *)arg, cmd, mode);
- break;
- }
- break;
-
- case PCI_TOOL_INTR_MINOR_NUM:
-
- switch (cmd) {
- case PCITOOL_DEVICE_SET_INTR:
-
- /* Require PRIV_SYS_RES_CONFIG, same as psradm */
- if (secpolicy_ponline(credp)) {
- rv = EPERM;
- break;
- }
-
- /*FALLTHRU*/
- /* These require no special privileges. */
- case PCITOOL_DEVICE_GET_INTR:
- case PCITOOL_DEVICE_NUM_INTR:
- rv = pcitool_intr_admn(dip, (void *)arg, cmd, mode);
- break;
- }
- break;
-
- /*
- * All non-PCItool ioctls go through here, including:
- * devctl ioctls with minor number PCIHP_DEVCTL_MINOR and
- * those for attachment points with where minor number is the
- * device number.
- */
- default:
- rv = (pcihp_cb_ops.cb_ioctl)(dev, cmd, arg, mode,
- credp, rvalp);
- break;
- }
-
- return (rv);
+ return (pci_common_ioctl(pci_p->pci_dip,
+ dev, cmd, arg, mode, credp, rvalp));
}
+
static int
pci_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
int flags, char *name, caddr_t valuep, int *lengthp)
{
- return ((pcihp_cb_ops.cb_prop_op)(dev, dip, prop_op, flags,
+ return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags,
name, valuep, lengthp));
}
diff --git a/usr/src/uts/i86pc/io/pci/pci_boot.c b/usr/src/uts/i86pc/io/pci/pci_boot.c
index 71ea0f76f1..2b65a9078e 100644
--- a/usr/src/uts/i86pc/io/pci/pci_boot.c
+++ b/usr/src/uts/i86pc/io/pci/pci_boot.c
@@ -34,11 +34,12 @@
#include <sys/pci_cfgspace.h>
#include <sys/memlist.h>
#include <sys/bootconf.h>
-#include "mps_table.h"
+#include <io/pci/mps_table.h>
#include <sys/pci_cfgspace.h>
#include <sys/pci_cfgspace_impl.h>
#include <sys/psw.h>
#include "../../../../common/pci/pci_strings.h"
+#include <io/pciex/pcie_ck804_boot.h>
#define pci_getb (*pci_getb_func)
#define pci_getw (*pci_getw_func)
@@ -51,7 +52,7 @@
#define CONFIG_INFO 0
#define CONFIG_UPDATE 1
#define CONFIG_NEW 2
-#define COMPAT_BUFSIZE 256
+#define COMPAT_BUFSIZE 512
extern int pci_bios_nbus;
static uchar_t max_dev_pci = 32; /* PCI standard */
@@ -66,9 +67,9 @@ static void create_root_bus_dip(uchar_t bus);
static dev_info_t *new_func_pci(uchar_t, uchar_t, uchar_t, uchar_t,
ushort_t, int);
static void add_compatible(dev_info_t *, ushort_t, ushort_t,
- ushort_t, ushort_t, uchar_t, uint_t);
+ ushort_t, ushort_t, uchar_t, uint_t, int);
static int add_reg_props(dev_info_t *, uchar_t, uchar_t, uchar_t, int, int);
-static void add_ppb_props(dev_info_t *, uchar_t, uchar_t, uchar_t);
+static void add_ppb_props(dev_info_t *, uchar_t, uchar_t, uchar_t, int);
static void add_model_prop(dev_info_t *, uint_t);
static void add_bus_range_prop(int);
static int pci_slot_names_prop(int, char *, int);
@@ -164,8 +165,6 @@ create_root_bus_dip(uchar_t bus)
ndi_devi_alloc_sleep(ddi_root_node(), "pci",
(pnode_t)DEVI_SID_NODEID, &dip);
- (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
- "device_type", "pci");
(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
"#address-cells", 3);
(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
@@ -174,6 +173,13 @@ create_root_bus_dip(uchar_t bus)
(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
"reg", (int *)pci_regs, 3);
+ /*
+ * If system has PCIe bus, then create different properties
+ */
+ if (create_pcie_root_bus(bus, dip) == B_FALSE)
+ (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
+ "device_type", "pci");
+
(void) ndi_devi_bind_driver(dip, 0);
pci_bus_res[bus].dip = dip;
pci_bus_res[bus].pmem_space = find_bus_res(bus, PREFETCH_TYPE);
@@ -403,9 +409,12 @@ new_func_pci(uchar_t bus, uchar_t dev, uchar_t func, uchar_t header,
dev_info_t *dip;
uchar_t basecl, subcl, intr, revid;
ushort_t subvenid, subdevid, status;
+ ushort_t slot_num;
uint_t classcode, revclass;
int reprogram = 0, pciide;
int power[2] = {1, 1};
+ int pciex = 0;
+ ushort_t is_pci_bridge = 0;
ushort_t deviceid = pci_getw(bus, dev, func, PCI_CONF_DEVID);
@@ -434,6 +443,9 @@ new_func_pci(uchar_t bus, uchar_t dev, uchar_t func, uchar_t header,
subcl = (classcode >> 8) & 0xff;
pciide = is_pciide(basecl, subcl, revid, vendorid, deviceid,
subvenid, subdevid);
+ if (check_if_device_is_pciex(bus, dev, func, &slot_num,
+ &is_pci_bridge) == B_TRUE)
+ pciex = 1;
if (pciide)
(void) snprintf(nodename, sizeof (nodename), "pci-ide");
@@ -484,10 +496,12 @@ new_func_pci(uchar_t bus, uchar_t dev, uchar_t func, uchar_t header,
(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
"subsystem-vendor-id", subvenid);
}
- (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "min-grant", mingrant);
- (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
- "max-latency", maxlatency);
+ if (!pciex)
+ (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
+ "min-grant", mingrant);
+ if (!pciex)
+ (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
+ "max-latency", maxlatency);
}
/* interrupt, record if not 0 */
@@ -503,26 +517,38 @@ new_func_pci(uchar_t bus, uchar_t dev, uchar_t func, uchar_t header,
(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
"devsel-speed", (status & PCI_STAT_DEVSELT) >> 9);
- if (status & PCI_STAT_FBBC)
+ if (!pciex && (status & PCI_STAT_FBBC))
(void) ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
"fast-back-to-back");
- if (status & PCI_STAT_66MHZ)
+ if (!pciex && (status & PCI_STAT_66MHZ))
(void) ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
"66mhz-capable");
if (status & PCI_STAT_UDF)
(void) ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
"udf-supported");
+ if (pciex & slot_num)
+ (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
+ "physical-slot#", slot_num);
(void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
"power-consumption", power, 2);
- if ((basecl == PCI_CLASS_BRIDGE) && (subcl == PCI_BRIDGE_PCI)) {
- add_ppb_props(dip, bus, dev, func);
- }
+ if ((basecl == PCI_CLASS_BRIDGE) && (subcl == PCI_BRIDGE_PCI))
+ add_ppb_props(dip, bus, dev, func, pciex);
+
+ /* check for ck8-04 based PCI ISA bridge only */
+ if (NVIDIA_IS_LPC_BRIDGE(vendorid, deviceid) && (dev == 1) &&
+ (func == 0))
+ add_ck804_isa_bridge_props(dip, bus, dev, func);
+
+ if (pciex && is_pci_bridge)
+ (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip, "model",
+ (char *)"PCIe-PCI bridge");
+ else
+ add_model_prop(dip, classcode);
- add_model_prop(dip, classcode);
add_compatible(dip, subvenid, subdevid, vendorid, deviceid,
- revid, classcode);
+ revid, classcode, pciex);
reprogram = add_reg_props(dip, bus, dev, func, config_op, pciide);
(void) ndi_devi_bind_driver(dip, 0);
@@ -556,6 +582,7 @@ new_func_pci(uchar_t bus, uchar_t dev, uchar_t func, uchar_t header,
reprogram = 0; /* don't reprogram pci-ide bridge */
}
+
if (reprogram)
return (dip);
return (NULL);
@@ -564,6 +591,7 @@ new_func_pci(uchar_t bus, uchar_t dev, uchar_t func, uchar_t header,
/*
* Set the compatible property to a value compliant with
* rev 2.1 of the IEEE1275 PCI binding.
+ * (Also used for PCI-Express devices).
*
* pciVVVV,DDDD.SSSS.ssss.RR (0)
* pciVVVV,DDDD.SSSS.ssss (1)
@@ -576,22 +604,62 @@ new_func_pci(uchar_t bus, uchar_t dev, uchar_t func, uchar_t header,
* The Subsystem (SSSS) forms are not inserted if
* subsystem-vendor-id is 0.
*
+ * NOTE: For PCI-Express devices "pci" is replaced with "pciex" in 0-6 above
+ * property 2 is not created as per "1275 bindings for PCI Express Interconnect"
+ *
* Set with setprop and \x00 between each
* to generate the encoded string array form.
*/
void
add_compatible(dev_info_t *dip, ushort_t subvenid, ushort_t subdevid,
- ushort_t vendorid, ushort_t deviceid, uchar_t revid, uint_t classcode)
+ ushort_t vendorid, ushort_t deviceid, uchar_t revid, uint_t classcode,
+ int pciex)
{
- int i, size;
- char *compat[7];
+ int i = 0;
+ int size = COMPAT_BUFSIZE;
+ char *compat[13];
char *buf, *curr;
-#define COMPAT_BUFSIZE 256
- i = 0;
- size = COMPAT_BUFSIZE;
curr = buf = kmem_alloc(size, KM_SLEEP);
+ if (pciex) {
+ if (subvenid) {
+ compat[i++] = curr; /* form 0 */
+ (void) snprintf(curr, size, "pciex%x,%x.%x.%x.%x",
+ vendorid, deviceid, subvenid, subdevid, revid);
+ size -= strlen(curr) + 1;
+ curr += strlen(curr) + 1;
+
+ compat[i++] = curr; /* form 1 */
+ (void) snprintf(curr, size, "pciex%x,%x.%x.%x",
+ vendorid, deviceid, subvenid, subdevid);
+ size -= strlen(curr) + 1;
+ curr += strlen(curr) + 1;
+
+ }
+ compat[i++] = curr; /* form 3 */
+ (void) snprintf(curr, size, "pciex%x,%x.%x",
+ vendorid, deviceid, revid);
+ size -= strlen(curr) + 1;
+ curr += strlen(curr) + 1;
+
+ compat[i++] = curr; /* form 4 */
+ (void) snprintf(curr, size, "pciex%x,%x", vendorid, deviceid);
+ size -= strlen(curr) + 1;
+ curr += strlen(curr) + 1;
+
+ compat[i++] = curr; /* form 5 */
+ (void) snprintf(curr, size, "pciexclass,%06x", classcode);
+ size -= strlen(curr) + 1;
+ curr += strlen(curr) + 1;
+
+ compat[i++] = curr; /* form 6 */
+ (void) snprintf(curr, size, "pciexclass,%04x",
+ (classcode >> 8));
+ size -= strlen(curr) + 1;
+ curr += strlen(curr) + 1;
+ }
+
if (subvenid) {
compat[i++] = curr; /* form 0 */
(void) snprintf(curr, size, "pci%x,%x.%x.%x.%x",
@@ -606,8 +674,7 @@ add_compatible(dev_info_t *dip, ushort_t subvenid, ushort_t subdevid,
curr += strlen(curr) + 1;
compat[i++] = curr; /* form 2 */
- (void) snprintf(curr, size, "pci%x,%x",
- subvenid, subdevid);
+ (void) snprintf(curr, size, "pci%x,%x", subvenid, subdevid);
size -= strlen(curr) + 1;
curr += strlen(curr) + 1;
}
@@ -628,6 +695,8 @@ add_compatible(dev_info_t *dip, ushort_t subvenid, ushort_t subdevid,
compat[i++] = curr; /* form 6 */
(void) snprintf(curr, size, "pciclass,%04x", (classcode >> 8));
+ size -= strlen(curr) + 1;
+ curr += strlen(curr) + 1;
(void) ndi_prop_update_string_array(DDI_DEV_T_NONE, dip,
"compatible", compat, i);
@@ -923,7 +992,10 @@ add_reg_props(dev_info_t *dip, uchar_t bus, uchar_t dev, uchar_t func,
pci_putl(bus, dev, func, offset, PCI_BASE_ROM_ADDR_M);
value = pci_getl(bus, dev, func, offset);
pci_putl(bus, dev, func, offset, base);
- value &= PCI_BASE_ROM_ADDR_M;
+ if (value & PCI_BASE_ROM_ENABLE)
+ value &= PCI_BASE_ROM_ADDR_M;
+ else
+ value = 0;
if (value != 0) {
regs[nreg].pci_phys_hi = (PCI_ADDR_MEM32 | devloc) + offset;
@@ -1011,8 +1083,10 @@ done:
}
static void
-add_ppb_props(dev_info_t *dip, uchar_t bus, uchar_t dev, uchar_t func)
+add_ppb_props(dev_info_t *dip, uchar_t bus, uchar_t dev, uchar_t func,
+ int pciex)
{
+ char *dev_type;
int i;
uint_t val, io_range[2], mem_range[2], pmem_range[2];
uchar_t secbus = pci_getb(bus, dev, func, PCI_BCNF_SECBUS);
@@ -1032,6 +1106,8 @@ add_ppb_props(dev_info_t *dip, uchar_t bus, uchar_t dev, uchar_t func)
pci_bus_res[secbus].dip = dip;
pci_bus_res[secbus].par_bus = bus;
+ dev_type = pciex ? "pciex" : "pci";
+
/* setup bus number hierarchy */
pci_bus_res[secbus].sub_bus = subbus;
if (subbus > pci_bus_res[bus].sub_bus)
@@ -1040,7 +1116,7 @@ add_ppb_props(dev_info_t *dip, uchar_t bus, uchar_t dev, uchar_t func)
pci_bus_res[i].par_bus = bus;
(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
- "device_type", "pci");
+ "device_type", dev_type);
(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
"#address-cells", 3);
(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
@@ -1321,7 +1397,7 @@ pci_slot_names_prop(int bus, char *buf, int len)
for (i = 0; i < pci_irq_nroutes; i++) {
if (slot[i] == 0xff)
continue;
- (void) sprintf(buf + plen, "Slot %d", slot[i]);
+ (void) sprintf(buf + plen, "Slot%d", slot[i]);
plen += strlen(buf+plen) + 1;
*(buf + plen) = 0;
}
diff --git a/usr/src/uts/i86pc/io/pci/pci_common.c b/usr/src/uts/i86pc/io/pci/pci_common.c
new file mode 100644
index 0000000000..de7d84eea6
--- /dev/null
+++ b/usr/src/uts/i86pc/io/pci/pci_common.c
@@ -0,0 +1,761 @@
+/*
+ * 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"
+
+/*
+ * File that has code which is common between pci(7d) and npe(7d)
+ * It shares the following:
+ * - interrupt code
+ * - pci_tools ioctl code
+ * - name_child code
+ * - set_parent_private_data code
+ */
+
+#include <sys/conf.h>
+#include <sys/pci.h>
+#include <sys/sunndi.h>
+#include <sys/hotplug/pci/pcihp.h>
+#include <sys/pci_intr_lib.h>
+#include <sys/psm.h>
+#include <sys/policy.h>
+#include <sys/sysmacros.h>
+#include <sys/pci_tools.h>
+#include <io/pci/pci_var.h>
+#include <io/pci/pci_tools_ext.h>
+#include <io/pci/pci_common.h>
+
+/*
+ * Function prototypes
+ */
+static int pci_get_priority(dev_info_t *, ddi_intr_handle_impl_t *, int *);
+static int pci_get_nintrs(dev_info_t *, int, int *);
+static int pci_enable_intr(dev_info_t *, dev_info_t *,
+ ddi_intr_handle_impl_t *, uint32_t);
+static void pci_disable_intr(dev_info_t *, dev_info_t *,
+ ddi_intr_handle_impl_t *, uint32_t);
+
+/* Extern decalration for pcplusmp module */
+extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *,
+ psm_intr_op_t, int *);
+
+
+/*
+ * pci_name_child:
+ *
+ * Assign the address portion of the node name
+ */
+int
+pci_common_name_child(dev_info_t *child, char *name, int namelen)
+{
+ int dev, func, length;
+ char **unit_addr;
+ uint_t n;
+ pci_regspec_t *pci_rp;
+
+ if (ndi_dev_is_persistent_node(child) == 0) {
+ /*
+ * For .conf node, use "unit-address" property
+ */
+ if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
+ DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
+ DDI_PROP_SUCCESS) {
+ cmn_err(CE_WARN, "cannot find unit-address in %s.conf",
+ ddi_get_name(child));
+ return (DDI_FAILURE);
+ }
+ if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
+ cmn_err(CE_WARN, "unit-address property in %s.conf"
+ " not well-formed", ddi_get_name(child));
+ ddi_prop_free(unit_addr);
+ return (DDI_FAILURE);
+ }
+ (void) snprintf(name, namelen, "%s", *unit_addr);
+ ddi_prop_free(unit_addr);
+ return (DDI_SUCCESS);
+ }
+
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
+ "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
+ cmn_err(CE_WARN, "cannot find reg property in %s",
+ ddi_get_name(child));
+ return (DDI_FAILURE);
+ }
+
+ /* copy the device identifications */
+ dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
+ 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 (func != 0) {
+ (void) snprintf(name, namelen, "%x,%x", dev, func);
+ } else {
+ (void) snprintf(name, namelen, "%x", dev);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Interrupt related code:
+ *
+ * The following busop is common to npe and pci drivers
+ * bus_introp
+ */
+
+/*
+ * Create the ddi_parent_private_data for a pseudo child.
+ */
+void
+pci_common_set_parent_private_data(dev_info_t *dip)
+{
+ struct ddi_parent_private_data *pdptr;
+
+ pdptr = (struct ddi_parent_private_data *)kmem_zalloc(
+ (sizeof (struct ddi_parent_private_data) +
+ sizeof (struct intrspec)), KM_SLEEP);
+ pdptr->par_intr = (struct intrspec *)(pdptr + 1);
+ pdptr->par_nintr = 1;
+ ddi_set_parent_data(dip, pdptr);
+}
+
+/*
+ * pci_get_priority:
+ * Figure out the priority of the device
+ */
+static int
+pci_get_priority(dev_info_t *dip, ddi_intr_handle_impl_t *hdlp, int *pri)
+{
+ struct intrspec *ispec;
+
+ DDI_INTR_NEXDBG((CE_CONT, "pci_get_priority: dip = 0x%p, hdlp = %p\n",
+ (void *)dip, (void *)hdlp));
+
+ if ((ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip,
+ hdlp->ih_inum)) == NULL) {
+ if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type)) {
+ int class = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+ DDI_PROP_DONTPASS, "class-code", -1);
+
+ *pri = (class == -1) ? 1 : pci_devclass_to_ipl(class);
+ pci_common_set_parent_private_data(hdlp->ih_dip);
+ ispec = (struct intrspec *)pci_intx_get_ispec(dip, dip,
+ hdlp->ih_inum);
+ return (DDI_SUCCESS);
+ }
+ return (DDI_FAILURE);
+ }
+
+ *pri = ispec->intrspec_pri;
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * pci_get_nintrs:
+ * Figure out how many interrupts the device supports
+ */
+static int
+pci_get_nintrs(dev_info_t *dip, int type, int *nintrs)
+{
+ int ret;
+
+ *nintrs = 0;
+
+ if (DDI_INTR_IS_MSI_OR_MSIX(type))
+ ret = pci_msi_get_nintrs(dip, type, nintrs);
+ else {
+ ret = DDI_FAILURE;
+ if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "interrupts", -1) != -1) {
+ *nintrs = 1;
+ ret = DDI_SUCCESS;
+ }
+ }
+
+ return (ret);
+}
+
+static int pcie_pci_intr_pri_counter = 0;
+
+/*
+ * pci_common_intr_ops: bus_intr_op() function for interrupt support
+ */
+int
+pci_common_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
+ ddi_intr_handle_impl_t *hdlp, void *result)
+{
+ int priority = 0;
+ int psm_status = 0;
+ int pci_status = 0;
+ int pci_rval, psm_rval = PSM_FAILURE;
+ int types = 0;
+ int pciepci = 0;
+ int i, j;
+ int behavior;
+ ddi_intrspec_t isp;
+ struct intrspec *ispec;
+ ddi_intr_handle_impl_t tmp_hdl;
+ ddi_intr_msix_t *msix_p;
+
+ DDI_INTR_NEXDBG((CE_CONT,
+ "pci_common_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
+ (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
+
+ /* Process the request */
+ switch (intr_op) {
+ case DDI_INTROP_SUPPORTED_TYPES:
+ /* Fixed supported by default */
+ *(int *)result = DDI_INTR_TYPE_FIXED;
+
+ /* Figure out if MSI or MSI-X is supported? */
+ if (pci_msi_get_supported_type(rdip, &types) != DDI_SUCCESS)
+ return (DDI_SUCCESS);
+
+ if (psm_intr_ops != NULL) {
+ /* MSI or MSI-X is supported, OR it in */
+ *(int *)result |= types;
+
+ tmp_hdl.ih_type = *(int *)result;
+ (void) (*psm_intr_ops)(rdip, &tmp_hdl,
+ PSM_INTR_OP_CHECK_MSI, result);
+ DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
+ "rdip: 0x%p supported types: 0x%x\n", (void *)rdip,
+ *(int *)result));
+ }
+ break;
+ case DDI_INTROP_NINTRS:
+ if (pci_get_nintrs(rdip, hdlp->ih_type, result) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ break;
+ case DDI_INTROP_ALLOC:
+ /*
+ * MSI or MSIX (figure out number of vectors available)
+ * FIXED interrupts: just return available interrupts
+ */
+ if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) &&
+ (psm_intr_ops != NULL) &&
+ (pci_get_priority(rdip, hdlp, &priority) == DDI_SUCCESS)) {
+ /*
+ * Following check is a special case for 'pcie_pci'.
+ * This makes sure vectors with the right priority
+ * are allocated for pcie_pci during ALLOC time.
+ */
+ if (strcmp(ddi_driver_name(rdip), "pcie_pci") == 0) {
+ hdlp->ih_pri =
+ (pcie_pci_intr_pri_counter % 2) ? 4 : 7;
+ pciepci = 1;
+ } else
+ hdlp->ih_pri = priority;
+ behavior = hdlp->ih_scratch2;
+ (void) (*psm_intr_ops)(rdip, hdlp,
+ PSM_INTR_OP_ALLOC_VECTORS, result);
+
+ /* verify behavior flag and take appropriate action */
+ if ((behavior == DDI_INTR_ALLOC_STRICT) &&
+ (*(int *)result < hdlp->ih_scratch1)) {
+ DDI_INTR_NEXDBG((CE_CONT,
+ "pci_common_intr_ops: behavior %x, "
+ "couldn't get enough intrs\n", behavior));
+ hdlp->ih_scratch1 = *(int *)result;
+ (void) (*psm_intr_ops)(rdip, hdlp,
+ PSM_INTR_OP_FREE_VECTORS, NULL);
+ return (DDI_EAGAIN);
+ }
+
+ if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
+ if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) {
+ msix_p = pci_msix_init(hdlp->ih_dip);
+ if (msix_p)
+ i_ddi_set_msix(hdlp->ih_dip,
+ msix_p);
+ }
+ msix_p->msix_intrs_in_use += *(int *)result;
+ }
+
+ if (pciepci) {
+ /* update priority in ispec */
+ isp = pci_intx_get_ispec(pdip, rdip,
+ (int)hdlp->ih_inum);
+ ispec = (struct intrspec *)isp;
+ if (ispec)
+ ispec->intrspec_pri = hdlp->ih_pri;
+ ++pcie_pci_intr_pri_counter;
+ }
+
+ } else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED) {
+ /* Figure out if this device supports MASKING */
+ pci_rval = pci_intx_get_cap(rdip, &pci_status);
+ if (pci_rval == DDI_SUCCESS && pci_status)
+ hdlp->ih_cap |= pci_status;
+ *(int *)result = 1; /* DDI_INTR_TYPE_FIXED */
+ } else
+ return (DDI_FAILURE);
+ break;
+ case DDI_INTROP_FREE:
+ if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) &&
+ (psm_intr_ops != NULL)) {
+ (void) (*psm_intr_ops)(rdip, hdlp,
+ PSM_INTR_OP_FREE_VECTORS, NULL);
+
+ if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
+ msix_p = i_ddi_get_msix(hdlp->ih_dip);
+ if (msix_p &&
+ --msix_p->msix_intrs_in_use == 0) {
+ pci_msix_fini(msix_p);
+ i_ddi_set_msix(hdlp->ih_dip, NULL);
+ }
+ }
+ }
+ break;
+ case DDI_INTROP_GETPRI:
+ /* Get the priority */
+ if (pci_get_priority(rdip, hdlp, &priority) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
+ "priority = 0x%x\n", priority));
+ *(int *)result = priority;
+ break;
+ case DDI_INTROP_SETPRI:
+ /* Validate the interrupt priority passed */
+ if (*(int *)result > LOCK_LEVEL)
+ return (DDI_FAILURE);
+
+ /* Ensure that PSM is all initialized */
+ if (psm_intr_ops == NULL)
+ return (DDI_FAILURE);
+
+ /* Change the priority */
+ if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_PRI, result) ==
+ PSM_FAILURE)
+ return (DDI_FAILURE);
+
+ /* update ispec */
+ isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
+ ispec = (struct intrspec *)isp;
+ if (ispec)
+ ispec->intrspec_pri = *(int *)result;
+ break;
+ case DDI_INTROP_ADDISR:
+ /* update ispec */
+ isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
+ ispec = (struct intrspec *)isp;
+ if (ispec)
+ ispec->intrspec_func = hdlp->ih_cb_func;
+ break;
+ case DDI_INTROP_REMISR:
+ /* Get the interrupt structure pointer */
+ isp = pci_intx_get_ispec(pdip, rdip, (int)hdlp->ih_inum);
+ ispec = (struct intrspec *)isp;
+ if (ispec)
+ ispec->intrspec_func = (uint_t (*)()) 0;
+ break;
+ case DDI_INTROP_GETCAP:
+ /*
+ * First check the config space and/or
+ * MSI capability register(s)
+ */
+ if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
+ pci_rval = pci_msi_get_cap(rdip, hdlp->ih_type,
+ &pci_status);
+ else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
+ pci_rval = pci_intx_get_cap(rdip, &pci_status);
+
+ /* next check with pcplusmp */
+ if (psm_intr_ops != NULL)
+ psm_rval = (*psm_intr_ops)(rdip, hdlp,
+ PSM_INTR_OP_GET_CAP, &psm_status);
+
+ DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned psm_rval = %x, "
+ "psm_status = %x, pci_rval = %x, pci_status = %x\n",
+ psm_rval, psm_status, pci_rval, pci_status));
+
+ if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) {
+ *(int *)result = 0;
+ return (DDI_FAILURE);
+ }
+
+ if (psm_rval == PSM_SUCCESS)
+ *(int *)result = psm_status;
+
+ if (pci_rval == DDI_SUCCESS)
+ *(int *)result |= pci_status;
+
+ DDI_INTR_NEXDBG((CE_CONT, "pci: GETCAP returned = %x\n",
+ *(int *)result));
+ break;
+ case DDI_INTROP_SETCAP:
+ DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
+ "SETCAP cap=0x%x\n", *(int *)result));
+ if (psm_intr_ops == NULL)
+ return (DDI_FAILURE);
+
+ if ((*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_SET_CAP, result)) {
+ DDI_INTR_NEXDBG((CE_CONT, "GETCAP: psm_intr_ops"
+ " returned failure\n"));
+ return (DDI_FAILURE);
+ }
+ break;
+ case DDI_INTROP_ENABLE:
+ DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE\n"));
+ if (psm_intr_ops == NULL)
+ return (DDI_FAILURE);
+
+ if (pci_enable_intr(pdip, rdip, hdlp, hdlp->ih_inum) !=
+ DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: ENABLE "
+ "vector=0x%x\n", hdlp->ih_vector));
+ break;
+ case DDI_INTROP_DISABLE:
+ DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE\n"));
+ if (psm_intr_ops == NULL)
+ return (DDI_FAILURE);
+
+ pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum);
+ DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: DISABLE "
+ "vector = %x\n", hdlp->ih_vector));
+ break;
+ case DDI_INTROP_BLOCKENABLE:
+ DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
+ "BLOCKENABLE\n"));
+ if (hdlp->ih_type != DDI_INTR_TYPE_MSI) {
+ DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: not MSI\n"));
+ return (DDI_FAILURE);
+ }
+
+ /* Check if psm_intr_ops is NULL? */
+ if (psm_intr_ops == NULL)
+ return (DDI_FAILURE);
+
+ for (i = 0; i < hdlp->ih_scratch1; i++) {
+ if (pci_enable_intr(pdip, rdip, hdlp,
+ hdlp->ih_inum + i) != DDI_SUCCESS) {
+ DDI_INTR_NEXDBG((CE_CONT, "BLOCKENABLE: "
+ "pci_enable_intr failed for %d\n", i));
+ for (j = 0; j < i; j++)
+ pci_disable_intr(pdip, rdip, hdlp,
+ hdlp->ih_inum + j);
+ return (DDI_FAILURE);
+ }
+ DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
+ "BLOCKENABLE inum %x done\n", hdlp->ih_inum + i));
+ }
+ break;
+ case DDI_INTROP_BLOCKDISABLE:
+ DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
+ "BLOCKDISABLE\n"));
+ if (hdlp->ih_type != DDI_INTR_TYPE_MSI) {
+ DDI_INTR_NEXDBG((CE_CONT, "BLOCKDISABLE: not MSI\n"));
+ return (DDI_FAILURE);
+ }
+
+ /* Check if psm_intr_ops is present */
+ if (psm_intr_ops == NULL)
+ return (DDI_FAILURE);
+
+ for (i = 0; i < hdlp->ih_scratch1; i++) {
+ pci_disable_intr(pdip, rdip, hdlp, hdlp->ih_inum + i);
+ DDI_INTR_NEXDBG((CE_CONT, "pci_common_intr_ops: "
+ "BLOCKDISABLE inum %x done\n", hdlp->ih_inum + i));
+ }
+ break;
+ case DDI_INTROP_SETMASK:
+ case DDI_INTROP_CLRMASK:
+ /*
+ * First handle in the config space
+ */
+ if (intr_op == DDI_INTROP_SETMASK) {
+ if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
+ pci_status = pci_msi_set_mask(rdip,
+ hdlp->ih_type, hdlp->ih_inum);
+ else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
+ pci_status = pci_intx_set_mask(rdip);
+ } else {
+ if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
+ pci_status = pci_msi_clr_mask(rdip,
+ hdlp->ih_type, hdlp->ih_inum);
+ else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
+ pci_status = pci_intx_clr_mask(rdip);
+ }
+
+ /* For MSI/X; no need to check with pcplusmp */
+ if (hdlp->ih_type != DDI_INTR_TYPE_FIXED)
+ return (pci_status);
+
+ /* For fixed interrupts only: handle config space first */
+ if (hdlp->ih_type == DDI_INTR_TYPE_FIXED &&
+ pci_status == DDI_SUCCESS)
+ break;
+
+ /* For fixed interrupts only: confer with pcplusmp next */
+ if (psm_intr_ops != NULL) {
+ /* If interrupt is shared; do nothing */
+ psm_rval = (*psm_intr_ops)(rdip, hdlp,
+ PSM_INTR_OP_GET_SHARED, &psm_status);
+
+ if (psm_rval == PSM_FAILURE || psm_status == 1)
+ return (pci_status);
+
+ /* Now, pcplusmp should try to set/clear the mask */
+ if (intr_op == DDI_INTROP_SETMASK)
+ psm_rval = (*psm_intr_ops)(rdip, hdlp,
+ PSM_INTR_OP_SET_MASK, NULL);
+ else
+ psm_rval = (*psm_intr_ops)(rdip, hdlp,
+ PSM_INTR_OP_CLEAR_MASK, NULL);
+ }
+ return ((psm_rval == PSM_FAILURE) ? DDI_FAILURE : DDI_SUCCESS);
+ case DDI_INTROP_GETPENDING:
+ /*
+ * First check the config space and/or
+ * MSI capability register(s)
+ */
+ if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type))
+ pci_rval = pci_msi_get_pending(rdip, hdlp->ih_type,
+ hdlp->ih_inum, &pci_status);
+ else if (hdlp->ih_type == DDI_INTR_TYPE_FIXED)
+ pci_rval = pci_intx_get_pending(rdip, &pci_status);
+
+ /* On failure; next try with pcplusmp */
+ if (pci_rval != DDI_SUCCESS && psm_intr_ops != NULL)
+ psm_rval = (*psm_intr_ops)(rdip, hdlp,
+ PSM_INTR_OP_GET_PENDING, &psm_status);
+
+ DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned "
+ "psm_rval = %x, psm_status = %x, pci_rval = %x, "
+ "pci_status = %x\n", psm_rval, psm_status, pci_rval,
+ pci_status));
+ if (psm_rval == PSM_FAILURE && pci_rval == DDI_FAILURE) {
+ *(int *)result = 0;
+ return (DDI_FAILURE);
+ }
+
+ if (psm_rval != PSM_FAILURE)
+ *(int *)result = psm_status;
+ else if (pci_rval != DDI_FAILURE)
+ *(int *)result = pci_status;
+ DDI_INTR_NEXDBG((CE_CONT, "pci: GETPENDING returned = %x\n",
+ *(int *)result));
+ break;
+ case DDI_INTROP_NAVAIL:
+ if ((psm_intr_ops != NULL) && (pci_get_priority(rdip,
+ hdlp, &priority) == DDI_SUCCESS)) {
+ /* Priority in the handle not initialized yet */
+ hdlp->ih_pri = priority;
+ (void) (*psm_intr_ops)(rdip, hdlp,
+ PSM_INTR_OP_NAVAIL_VECTORS, result);
+ } else {
+ *(int *)result = 1;
+ }
+ DDI_INTR_NEXDBG((CE_CONT, "pci: NAVAIL returned = %x\n",
+ *(int *)result));
+ break;
+ default:
+ return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
+ }
+
+ return (DDI_SUCCESS);
+}
+
+
+static int
+pci_enable_intr(dev_info_t *pdip, dev_info_t *rdip,
+ ddi_intr_handle_impl_t *hdlp, uint32_t inum)
+{
+ int vector;
+ struct intrspec *ispec;
+
+ DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: hdlp %p inum %x\n",
+ (void *)hdlp, inum));
+
+ /* Translate the interrupt if needed */
+ ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum);
+ if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec)
+ ispec->intrspec_vec = inum;
+ hdlp->ih_private = (void *)ispec;
+
+ /* translate the interrupt if needed */
+ (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &vector);
+ DDI_INTR_NEXDBG((CE_CONT, "pci_enable_intr: priority=%x vector=%x\n",
+ hdlp->ih_pri, vector));
+
+ /* Add the interrupt handler */
+ if (!add_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func,
+ DEVI(rdip)->devi_name, vector, hdlp->ih_cb_arg1,
+ hdlp->ih_cb_arg2, rdip))
+ return (DDI_FAILURE);
+
+ return (DDI_SUCCESS);
+}
+
+
+static void
+pci_disable_intr(dev_info_t *pdip, dev_info_t *rdip,
+ ddi_intr_handle_impl_t *hdlp, uint32_t inum)
+{
+ int vector;
+ struct intrspec *ispec;
+
+ DDI_INTR_NEXDBG((CE_CONT, "pci_disable_intr: \n"));
+ ispec = (struct intrspec *)pci_intx_get_ispec(pdip, rdip, (int)inum);
+ if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) && ispec)
+ ispec->intrspec_vec = inum;
+ hdlp->ih_private = (void *)ispec;
+
+ /* translate the interrupt if needed */
+ (void) (*psm_intr_ops)(rdip, hdlp, PSM_INTR_OP_XLATE_VECTOR, &vector);
+
+ /* Disable the interrupt handler */
+ rem_avintr((void *)hdlp, hdlp->ih_pri, hdlp->ih_cb_func, vector);
+}
+
+/*
+ * Miscellaneous library function
+ */
+int
+pci_common_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp)
+{
+ int i;
+ int number;
+ int assigned_addr_len;
+ uint_t phys_hi = pci_rp->pci_phys_hi;
+ pci_regspec_t *assigned_addr;
+
+ if (((phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_CONFIG) ||
+ (phys_hi & PCI_RELOCAT_B))
+ return (DDI_SUCCESS);
+
+ /*
+ * the "reg" property specifies relocatable, get and interpret the
+ * "assigned-addresses" property.
+ */
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "assigned-addresses", (int **)&assigned_addr,
+ (uint_t *)&assigned_addr_len) != DDI_PROP_SUCCESS)
+ return (DDI_FAILURE);
+
+ /*
+ * Scan the "assigned-addresses" for one that matches the specified
+ * "reg" property entry.
+ */
+ phys_hi &= PCI_CONF_ADDR_MASK;
+ number = assigned_addr_len / (sizeof (pci_regspec_t) / sizeof (int));
+ for (i = 0; i < number; i++) {
+ if ((assigned_addr[i].pci_phys_hi & PCI_CONF_ADDR_MASK) ==
+ phys_hi) {
+ pci_rp->pci_phys_mid = assigned_addr[i].pci_phys_mid;
+ pci_rp->pci_phys_low = assigned_addr[i].pci_phys_low;
+ ddi_prop_free(assigned_addr);
+ return (DDI_SUCCESS);
+ }
+ }
+
+ ddi_prop_free(assigned_addr);
+ return (DDI_FAILURE);
+}
+
+
+/*
+ * For pci_tools
+ */
+
+int
+pci_common_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg,
+ int mode, cred_t *credp, int *rvalp)
+{
+ int rv = ENOTTY;
+
+ minor_t minor = getminor(dev);
+
+ switch (PCIHP_AP_MINOR_NUM_TO_PCI_DEVNUM(minor)) {
+ case PCI_TOOL_REG_MINOR_NUM:
+
+ switch (cmd) {
+ case PCITOOL_DEVICE_SET_REG:
+ case PCITOOL_DEVICE_GET_REG:
+
+ /* Require full privileges. */
+ if (secpolicy_kmdb(credp))
+ rv = EPERM;
+ else
+ rv = pcitool_dev_reg_ops(dip, (void *)arg,
+ cmd, mode);
+ break;
+
+ case PCITOOL_NEXUS_SET_REG:
+ case PCITOOL_NEXUS_GET_REG:
+
+ /* Require full privileges. */
+ if (secpolicy_kmdb(credp))
+ rv = EPERM;
+ else
+ rv = pcitool_bus_reg_ops(dip, (void *)arg,
+ cmd, mode);
+ break;
+ }
+ break;
+
+ case PCI_TOOL_INTR_MINOR_NUM:
+
+ switch (cmd) {
+ case PCITOOL_DEVICE_SET_INTR:
+
+ /* Require PRIV_SYS_RES_CONFIG, same as psradm */
+ if (secpolicy_ponline(credp)) {
+ rv = EPERM;
+ break;
+ }
+
+ /*FALLTHRU*/
+ /* These require no special privileges. */
+ case PCITOOL_DEVICE_GET_INTR:
+ case PCITOOL_DEVICE_NUM_INTR:
+ rv = pcitool_intr_admn(dip, (void *)arg, cmd, mode);
+ break;
+ }
+ break;
+
+ /*
+ * All non-PCItool ioctls go through here, including:
+ * devctl ioctls with minor number PCIHP_DEVCTL_MINOR and
+ * those for attachment points with where minor number is the
+ * device number.
+ */
+ default:
+ rv = (pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode,
+ credp, rvalp);
+ break;
+ }
+
+ return (rv);
+}
diff --git a/usr/src/uts/i86pc/io/pci/pci_common.h b/usr/src/uts/i86pc/io/pci/pci_common.h
new file mode 100644
index 0000000000..c2f737eaa4
--- /dev/null
+++ b/usr/src/uts/i86pc/io/pci/pci_common.h
@@ -0,0 +1,66 @@
+/*
+ * 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 _PCI_PCI_COMMON_H
+#define _PCI_PCI_COMMON_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * Common header file with definitions shared between
+ * pci(7d) and npe(7d)
+ */
+
+/*
+ * PCI tool related declarations
+ */
+int pci_common_ioctl(dev_info_t *dip, dev_t dev, int cmd,
+ intptr_t arg, int mode, cred_t *credp, int *rvalp);
+
+/*
+ * Interrupt related declaration
+ */
+int pci_common_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
+ ddi_intr_handle_impl_t *, void *);
+void pci_common_set_parent_private_data(dev_info_t *);
+
+/*
+ * Miscellaneous library functions
+ */
+int pci_common_get_reg_prop(dev_info_t *dip, pci_regspec_t *pci_rp);
+int pci_common_name_child(dev_info_t *child, char *name, int namelen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PCI_PCI_COMMON_H */
diff --git a/usr/src/uts/i86pc/io/pci/pci_tools.c b/usr/src/uts/i86pc/io/pci/pci_tools.c
index 1b928610e0..9585720d6c 100644
--- a/usr/src/uts/i86pc/io/pci/pci_tools.c
+++ b/usr/src/uts/i86pc/io/pci/pci_tools.c
@@ -29,12 +29,11 @@
#include <sys/types.h>
#include <sys/mkdev.h>
#include <sys/stat.h>
-#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <vm/seg_kmem.h>
#include <sys/machparam.h>
#include <sys/ontrap.h>
-#include <sys/pci.h>
+#include <sys/pcie.h>
#include <sys/hotplug/pci/pcihp.h>
#include <sys/pci_cfgspace.h>
#include <sys/pci_tools.h>
@@ -189,9 +188,8 @@ pcitool_pciex_cfg_access(dev_info_t *dip, pcitool_reg_t *prg,
prg->status = PCITOOL_SUCCESS;
- /* XXX replace e0000000 value below with 0 once FW changes are made */
prg->phys_addr = ddi_prop_get_int64(DDI_DEV_T_ANY, dip, 0,
- "ecfga-base-address", 0xe00000000);
+ "ecfga-base-address", 0);
if (prg->phys_addr == 0) {
prg->status = PCITOOL_IO_ERROR;
return (EIO);
diff --git a/usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_acpi.c b/usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_acpi.c
new file mode 100644
index 0000000000..abb848083c
--- /dev/null
+++ b/usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_acpi.c
@@ -0,0 +1,865 @@
+/*
+ * 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"
+
+/*
+ * ACPI interface related functions used in PCIEHPC driver module.
+ */
+
+#include <sys/note.h>
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/debug.h>
+#include <sys/vtrace.h>
+#include <sys/varargs.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/pci.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include "pciehpc_acpi.h"
+
+/* local static functions */
+static int pciehpc_acpi_hpc_init(pciehpc_t *ctrl_p);
+static int pciehpc_acpi_hpc_uninit(pciehpc_t *ctrl_p);
+static int pciehpc_acpi_slotinfo_init(pciehpc_t *ctrl_p);
+static int pciehpc_acpi_slotinfo_uninit(pciehpc_t *ctrl_p);
+static int pciehpc_acpi_enable_intr(pciehpc_t *ctrl_p);
+static int pciehpc_acpi_disable_intr(pciehpc_t *ctrl_p);
+static int pciehpc_acpi_slot_connect(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ void *data, uint_t flags);
+static int pciehpc_acpi_slot_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ void *data, uint_t flags);
+
+static ACPI_STATUS pciehpc_acpi_install_event_handler(pciehpc_t *ctrl_p);
+static void pciehpc_acpi_uninstall_event_handler(pciehpc_t *ctrl_p);
+static ACPI_STATUS pciehpc_acpi_power_on_slot(pciehpc_t *ctrl_p);
+static ACPI_STATUS pciehpc_acpi_power_off_slot(pciehpc_t *ctrl_p);
+static void pciehpc_acpi_notify_handler(ACPI_HANDLE device, uint32_t val,
+ void *context);
+static ACPI_STATUS pciehpc_acpi_ej0_present(ACPI_HANDLE pcibus_obj);
+static ACPI_STATUS pciehpc_acpi_get_dev_state(ACPI_HANDLE obj, int *statusp);
+static ACPI_STATUS pciehpc_acpi_query_osc(dev_info_t *dip, ACPI_HANDLE osc_hdl,
+ uint32_t *hp_mode);
+static ACPI_STATUS pciehpc_acpi_set_native_hp(dev_info_t *dip,
+ ACPI_HANDLE osc_hdl);
+
+#ifdef DEBUG
+static void pciehpc_dump_acpi_obj(ACPI_HANDLE pcibus_obj);
+static ACPI_STATUS pciehpc_walk_obj_namespace(ACPI_HANDLE hdl, uint32_t nl,
+ void *context, void **ret);
+static ACPI_STATUS pciehpc_print_acpi_name(ACPI_HANDLE hdl, uint32_t nl,
+ void *context, void **ret);
+static void print_acpi_pathname(ACPI_HANDLE hdl);
+#endif
+
+#define ACPI_HP_MODE 1
+#define NATIVE_HP_MODE 2
+
+/* UUID for for PCI/PCI-X/PCI-Exp hierarchy as defined in PCI fw ver 3.0 */
+static uint8_t pcie_uuid[16] =
+ {0x5b, 0x4d, 0xdb, 0x33, 0xf7, 0x1f, 0x1c, 0x40,
+ 0x96, 0x57, 0x74, 0x41, 0xc0, 0x3d, 0xd7, 0x66};
+/*
+ * Function to check if ACPI hot plug is enabled on this bridge device.
+ * Since there is no absolute way to figure out if ACPI BIOS has hot plug
+ * control, the following approach is necessary:
+ *
+ * 1) If _OSC method is present then it is straight forward.
+ * 2) Otherwise, we need platform specific way to determine
+ * whether ACPI BIOS has hot plug control or not.
+ * (e.g: using .conf file property)
+ *
+ * NOTE: As per the PCI FW 3.0 spec, in the absence of _OSC method OS
+ * should not try to do native hot plug control.
+ *
+ * Returns 0 if the current mode is not ACPI hot plug.
+ */
+int
+pciehpc_acpi_hotplug_enabled(dev_info_t *dip)
+{
+ int *hotplug_mode;
+ uint_t count;
+ ACPI_HANDLE pcibus_obj;
+ int status = AE_ERROR;
+ ACPI_HANDLE osc_hdl;
+ int use_native_hotplug = 0;
+ uint32_t hp_mode;
+
+ /*
+ * (1) Find the ACPI device node for this bus node.
+ */
+ status = acpica_find_pciobj(dip, &pcibus_obj);
+ if (status != AE_OK) {
+ /*
+ * No ACPI device for this bus node. Assume there is
+ * no ACPI hot plug support on this bus.
+ */
+ PCIEHPC_DEBUG((CE_CONT, "No ACPI device found (dip %p)",
+ (void *)dip));
+ return (0);
+ }
+
+ /*
+ * If "use-native-hotplug-mode" property exists then it will tell us
+ * if we need to switch to or assume native hot plug mode on the
+ * platform.
+ */
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "use-native-hotplug-mode", &hotplug_mode, &count) ==
+ DDI_PROP_SUCCESS) {
+ if (hotplug_mode[0] == 1)
+ use_native_hotplug = 1;
+ ddi_prop_free(hotplug_mode);
+ }
+
+ /*
+ * NOTE: Without _OSC method there is no reliable way to know if the
+ * platform implements ACPI hot plug or native hot plug. But, if it is
+ * present then it may be possible to use either mode (i.e ACPI or
+ * native-hotplug) if the platform supports.
+ */
+
+ /*
+ * (2) Check if the node has _OSC method present.
+ */
+ if (AcpiGetHandle(pcibus_obj, "_OSC", &osc_hdl) != AE_OK) {
+ /* no _OSC method present; we need to guess here! */
+ if (use_native_hotplug)
+ return (0); /* assume native hot plug mode */
+ else
+ return (1); /* assume ACPI hot plug mode */
+ }
+
+ /*
+ * (3) _OSC method exists; check if the current mode is ACPI hot plug.
+ */
+ if (pciehpc_acpi_query_osc(dip, osc_hdl, &hp_mode) != AE_OK) {
+ /* failed to query _OSC method; assume ACPI hot plug */
+ return (1);
+ }
+ if (hp_mode == NATIVE_HP_MODE)
+ return (0); /* it is in native hp mode! */
+
+ if (use_native_hotplug) {
+ /* try to switch to native hot plug mode */
+ if (pciehpc_acpi_set_native_hp(dip, osc_hdl) == AE_OK) {
+ PCIEHPC_DEBUG((CE_NOTE, "using native hot plug mode\n"));
+ return (0); /* switched to native hot plug mode */
+ }
+ /* failed to switch to native hp mode */
+ PCIEHPC_DEBUG((CE_NOTE, "failed to switch to native"
+ " hot plug mode; using ACPI mode\n"));
+ }
+
+#ifdef DEBUG
+ if (pciehpc_debug > 1)
+ pciehpc_dump_acpi_obj(pcibus_obj);
+#endif
+ return (1); /* use ACPI mode */
+}
+
+void
+pciehpc_acpi_setup_ops(pciehpc_t *ctrl_p)
+{
+ ctrl_p->ops.init_hpc_hw = pciehpc_acpi_hpc_init;
+ ctrl_p->ops.init_hpc_slotinfo = pciehpc_acpi_slotinfo_init;
+ ctrl_p->ops.disable_hpc_intr = pciehpc_acpi_disable_intr;
+ ctrl_p->ops.enable_hpc_intr = pciehpc_acpi_enable_intr;
+ ctrl_p->ops.uninit_hpc_hw = pciehpc_acpi_hpc_uninit;
+ ctrl_p->ops.uninit_hpc_slotinfo = pciehpc_acpi_slotinfo_uninit;
+}
+
+/*
+ * Intialize hot plug control for ACPI mode.
+ */
+static int
+pciehpc_acpi_hpc_init(pciehpc_t *ctrl_p)
+{
+ ACPI_HANDLE pcibus_obj;
+ int status = AE_ERROR;
+ ACPI_HANDLE slot_dev_obj;
+ ACPI_HANDLE hdl;
+ pciehpc_acpi_t *acpi_p;
+ uint16_t bus_methods = 0;
+ uint16_t slot_methods = 0;
+
+ /* get the ACPI object for the bus node */
+ status = acpica_find_pciobj(ctrl_p->dip, &pcibus_obj);
+ if (status != AE_OK)
+ return (DDI_FAILURE);
+
+ /* get the ACPI object handle for the child node */
+ status = AcpiGetNextObject(ACPI_TYPE_DEVICE, pcibus_obj,
+ NULL, &slot_dev_obj);
+ if (status != AE_OK)
+ return (DDI_FAILURE);
+
+ /*
+ * gather the info about the ACPI methods present on the bus node
+ * and the child nodes.
+ */
+ if (AcpiGetHandle(pcibus_obj, "_OSC", &hdl) == AE_OK)
+ bus_methods |= PCIEHPC_ACPI_OSC_PRESENT;
+ if (AcpiGetHandle(pcibus_obj, "_OSHP", &hdl) == AE_OK)
+ bus_methods |= PCIEHPC_ACPI_OSHP_PRESENT;
+ if (AcpiGetHandle(pcibus_obj, "_HPX", &hdl) == AE_OK)
+ bus_methods |= PCIEHPC_ACPI_HPX_PRESENT;
+ if (AcpiGetHandle(pcibus_obj, "_HPP", &hdl) == AE_OK)
+ bus_methods |= PCIEHPC_ACPI_HPP_PRESENT;
+ if (AcpiGetHandle(pcibus_obj, "_DSM", &hdl) == AE_OK)
+ bus_methods |= PCIEHPC_ACPI_DSM_PRESENT;
+ if (AcpiGetHandle(pcibus_obj, "_SUN", &hdl) == AE_OK)
+ bus_methods |= PCIEHPC_ACPI_SUN_PRESENT;
+ if (AcpiGetHandle(slot_dev_obj, "_PS0", &hdl) == AE_OK)
+ slot_methods |= PCIEHPC_ACPI_PS0_PRESENT;
+ if (AcpiGetHandle(slot_dev_obj, "_EJ0", &hdl) == AE_OK)
+ slot_methods |= PCIEHPC_ACPI_EJ0_PRESENT;
+ if (AcpiGetHandle(slot_dev_obj, "_STA", &hdl) == AE_OK)
+ slot_methods |= PCIEHPC_ACPI_STA_PRESENT;
+
+ /* save ACPI object handles, etc. */
+ acpi_p = kmem_zalloc(sizeof (pciehpc_acpi_t), KM_SLEEP);
+ acpi_p->bus_obj = pcibus_obj;
+ acpi_p->slot_dev_obj = slot_dev_obj;
+ acpi_p->bus_methods = bus_methods;
+ acpi_p->slot_methods = slot_methods;
+ ctrl_p->misc_data = acpi_p;
+ ctrl_p->hp_mode = PCIEHPC_ACPI_HP_MODE;
+
+ /* initialize the slot mutex */
+ mutex_init(&ctrl_p->pciehpc_mutex, NULL, MUTEX_DRIVER,
+ (void *)PCIEHPC_INTR_PRI);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Uninitialize HPC.
+ */
+static int
+pciehpc_acpi_hpc_uninit(pciehpc_t *ctrl_p)
+{
+ /* free up buffer used for misc_data */
+ if (ctrl_p->misc_data) {
+ kmem_free(ctrl_p->misc_data, sizeof (pciehpc_acpi_t));
+ ctrl_p->misc_data = NULL;
+ }
+
+ /* destroy the mutex */
+ mutex_destroy(&ctrl_p->pciehpc_mutex);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Enable interrupts. For ACPI hot plug this is a NOP.
+ * Just return DDI_SUCCESS.
+ */
+/*ARGSUSED*/
+static int
+pciehpc_acpi_enable_intr(pciehpc_t *ctrl_p)
+{
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Disable interrupts. For ACPI hot plug this is a NOP.
+ * Just return DDI_SUCCESS.
+ */
+/*ARGSUSED*/
+static int
+pciehpc_acpi_disable_intr(pciehpc_t *ctrl_p)
+{
+ return (DDI_SUCCESS);
+}
+
+/*
+ * This function is similar to pciehpc_slotinfo_init() with some
+ * changes:
+ * - no need for kernel thread to handle ATTN button events
+ * - function ops for connect/disconnect are different
+ *
+ * ASSUMPTION: No conflict in doing reads to HP registers directly.
+ * Otherwise, there are no ACPI interfaces to do LED control or to get
+ * the hot plug capabilities (ATTN button, MRL, etc.).
+ */
+static int
+pciehpc_acpi_slotinfo_init(pciehpc_t *ctrl_p)
+{
+ uint32_t slot_capabilities;
+ pciehpc_slot_t *p = &ctrl_p->slot;
+
+ /*
+ * setup HPS framework slot ops structure
+ */
+ p->slot_ops.hpc_version = HPC_SLOT_OPS_VERSION;
+ p->slot_ops.hpc_op_connect = pciehpc_acpi_slot_connect;
+ p->slot_ops.hpc_op_disconnect = pciehpc_acpi_slot_disconnect;
+ p->slot_ops.hpc_op_insert = NULL;
+ p->slot_ops.hpc_op_remove = NULL;
+ p->slot_ops.hpc_op_control = pciehpc_slot_control;
+
+ /*
+ * setup HPS framework slot information structure
+ */
+ p->slot_info.version = HPC_SLOT_OPS_VERSION;
+ p->slot_info.slot_type = HPC_SLOT_TYPE_PCIE;
+ p->slot_info.slot_flags =
+ HPC_SLOT_CREATE_DEVLINK | HPC_SLOT_NO_AUTO_ENABLE;
+ p->slot_info.pci_slot_capabilities = HPC_SLOT_64BITS;
+ /* the device number is fixed as 0 as per the spec */
+ p->slot_info.pci_dev_num = 0;
+
+ /* read Slot Capabilities Register */
+ slot_capabilities = pciehpc_reg_get32(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCAP);
+
+ /* setup slot number/name */
+ pciehpc_set_slot_name(ctrl_p);
+
+ /* check if Attn Button present */
+ ctrl_p->has_attn = (slot_capabilities & PCIE_SLOTCAP_ATTN_BUTTON) ?
+ B_TRUE : B_FALSE;
+
+ /* check if Manual Retention Latch sensor present */
+ ctrl_p->has_mrl = (slot_capabilities & PCIE_SLOTCAP_MRL_SENSOR) ?
+ B_TRUE : B_FALSE;
+
+ /*
+ * PCI-E (draft) version 1.1 defines EMI Lock Present bit
+ * in Slot Capabilities register. Check for it.
+ */
+ ctrl_p->has_emi_lock = (slot_capabilities &
+ PCIE_SLOTCAP_EMI_LOCK_PRESENT) ? B_TRUE : B_FALSE;
+
+ /* initialize synchronization conditional variable */
+ cv_init(&ctrl_p->slot.cmd_comp_cv, NULL, CV_DRIVER, NULL);
+ ctrl_p->slot.command_pending = B_FALSE;
+
+ /* get current slot state from the hw */
+ pciehpc_get_slot_state(ctrl_p);
+
+ /* setup Notify() handler for hot plug events from ACPI BIOS */
+ if (pciehpc_acpi_install_event_handler(ctrl_p) != AE_OK)
+ return (DDI_FAILURE);
+
+ PCIEHPC_DEBUG((CE_NOTE, "ACPI hot plug is enabled for slot #%d",
+ ctrl_p->slot.slotNum));
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * This function is similar to pciehcp_slotinfo_uninit() but has ACPI
+ * specific cleanup.
+ */
+static int
+pciehpc_acpi_slotinfo_uninit(pciehpc_t *ctrl_p)
+{
+ /* uninstall Notify() event handler */
+ pciehpc_acpi_uninstall_event_handler(ctrl_p);
+
+ cv_destroy(&ctrl_p->slot.cmd_comp_cv);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * This function is same as pciehpc_slot_connect() except that it
+ * uses ACPI method PS0 to enable power to the slot. If no PS0 method
+ * is present then it returns HPC_ERR_FAILED.
+ */
+/*ARGSUSED*/
+static int
+pciehpc_acpi_slot_connect(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ void *data, uint_t flags)
+{
+ uint16_t status;
+ uint16_t control;
+
+ pciehpc_t *ctrl_p = (pciehpc_t *)ops_arg;
+
+ ASSERT(slot_hdl == ctrl_p->slot.slot_handle);
+
+ mutex_enter(&ctrl_p->pciehpc_mutex);
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(ctrl_p);
+
+ /* check if the slot is already in the 'connected' state */
+ if (ctrl_p->slot.slot_state == HPC_SLOT_CONNECTED) {
+ /* slot is already in the 'connected' state */
+ PCIEHPC_DEBUG((CE_NOTE, "slot %d already connected\n",
+ ctrl_p->slot.slotNum));
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_SUCCESS);
+ }
+
+ /* read the Slot Status Register */
+ status = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
+
+ /* make sure the MRL switch is closed if present */
+ if ((ctrl_p->has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) {
+ /* MRL switch is open */
+ cmn_err(CE_WARN, "MRL switch is open on slot %d\n",
+ ctrl_p->slot.slotNum);
+ goto cleanup;
+ }
+
+ /* make sure the slot has a device present */
+ if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
+ /* slot is empty */
+ PCIEHPC_DEBUG((CE_NOTE, "slot %d is empty\n",
+ ctrl_p->slot.slotNum));
+ goto cleanup;
+ }
+
+ /* get the current state of Slot Control Register */
+ control = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+
+ /* check if the slot's power state is ON */
+ if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
+ /* slot is already powered up */
+ PCIEHPC_DEBUG((CE_NOTE, "slot %d already connected\n",
+ ctrl_p->slot.slotNum));
+ ctrl_p->slot.slot_state = HPC_SLOT_CONNECTED;
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_SUCCESS);
+ }
+
+ /* turn on power to the slot using ACPI method (PS0) */
+ if (pciehpc_acpi_power_on_slot(ctrl_p) != AE_OK)
+ goto cleanup;
+
+ ctrl_p->slot.slot_state = HPC_SLOT_CONNECTED;
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_SUCCESS);
+
+cleanup:
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_ERR_FAILED);
+}
+
+/*
+ * This function is same as pciehpc_slot_disconnect() except that it
+ * uses ACPI method EJ0 to disable power to the slot. If no EJ0 method
+ * is present then it returns HPC_ERR_FAILED.
+ */
+/*ARGSUSED*/
+static int
+pciehpc_acpi_slot_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ void *data, uint_t flags)
+{
+ uint16_t status;
+
+ pciehpc_t *ctrl_p = (pciehpc_t *)ops_arg;
+
+ ASSERT(slot_hdl == ctrl_p->slot.slot_handle);
+
+ mutex_enter(&ctrl_p->pciehpc_mutex);
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(ctrl_p);
+
+ /* check if the slot is already in the 'disconnected' state */
+ if (ctrl_p->slot.slot_state == HPC_SLOT_DISCONNECTED) {
+ /* slot is in the 'disconnected' state */
+ PCIEHPC_DEBUG((CE_NOTE, "slot %d already disconnected\n",
+ ctrl_p->slot.slotNum));
+ ASSERT(ctrl_p->slot.power_led_state == HPC_LED_OFF);
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_SUCCESS);
+ }
+
+ /* read the Slot Status Register */
+ status = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
+
+ /* make sure the slot has a device present */
+ if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
+ /* slot is empty */
+ PCIEHPC_DEBUG((CE_WARN, "slot %d is empty\n",
+ ctrl_p->slot.slotNum));
+ goto cleanup;
+ }
+
+ /* turn off power to the slot using ACPI method (EJ0) */
+ if (pciehpc_acpi_power_off_slot(ctrl_p) != AE_OK)
+ goto cleanup;
+
+ ctrl_p->slot.slot_state = HPC_SLOT_DISCONNECTED;
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_SUCCESS);
+
+cleanup:
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_ERR_FAILED);
+}
+
+/*
+ * Install event handler for the hot plug events on the bus node as well
+ * as device function (dev=0,func=0).
+ */
+static ACPI_STATUS
+pciehpc_acpi_install_event_handler(pciehpc_t *ctrl_p)
+{
+ int status = AE_OK;
+ pciehpc_acpi_t *acpi_p;
+
+ PCIEHPC_DEBUG3((CE_CONT, "install event handler for slot %d\n",
+ ctrl_p->slot.slotNum));
+ acpi_p = ctrl_p->misc_data;
+ if (acpi_p->slot_dev_obj == NULL)
+ return (AE_NOT_FOUND);
+
+ /*
+ * Install event hanlder for events on the bus object.
+ * (Note: Insert event (hot-insert) is delivered on this object)
+ */
+ status = AcpiInstallNotifyHandler(acpi_p->slot_dev_obj,
+ ACPI_SYSTEM_NOTIFY,
+ pciehpc_acpi_notify_handler, (void *)ctrl_p);
+ if (status != AE_OK)
+ goto cleanup;
+
+ /*
+ * Install event hanlder for events on the device function object.
+ * (Note: Eject device event (hot-remove) is delivered on this object)
+ *
+ * NOTE: Here the assumption is that Notify events are delivered
+ * on all of the 8 possible device functions so, subscribing to
+ * one of them is sufficient.
+ */
+ status = AcpiInstallNotifyHandler(acpi_p->bus_obj,
+ ACPI_SYSTEM_NOTIFY,
+ pciehpc_acpi_notify_handler, (void *)ctrl_p);
+ return (status);
+
+cleanup:
+ (void) AcpiRemoveNotifyHandler(acpi_p->slot_dev_obj,
+ ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler);
+ return (status);
+}
+
+/*ARGSUSED*/
+static void
+pciehpc_acpi_notify_handler(ACPI_HANDLE device, uint32_t val, void *context)
+{
+ pciehpc_t *ctrl_p = context;
+ pciehpc_acpi_t *acpi_p;
+ int dev_state = 0;
+
+ PCIEHPC_DEBUG((CE_CONT, "received Notify(%d) event on slot #%d\n",
+ val, ctrl_p->slot.slotNum));
+
+ mutex_enter(&ctrl_p->pciehpc_mutex);
+
+ /*
+ * get the state of the device (from _STA method)
+ */
+ acpi_p = ctrl_p->misc_data;
+ if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj,
+ &dev_state) != AE_OK) {
+ cmn_err(CE_WARN, "failed to get device status on slot %d\n",
+ ctrl_p->slot.slotNum);
+ }
+ PCIEHPC_DEBUG((CE_CONT, "(1)device state on slot #%d: 0x%x\n",
+ ctrl_p->slot.slotNum, dev_state));
+
+ pciehpc_get_slot_state(ctrl_p);
+
+ switch (val) {
+ case 0: /* (re)enumerate the device */
+ case 3: /* Request Eject */
+ if (ctrl_p->slot.slot_state != HPC_SLOT_CONNECTED) {
+ /* unexpected slot state; suprise removal? */
+ cmn_err(CE_WARN, "Unexpteced event on slot #%d"
+ "(state 0x%x)\n", ctrl_p->slot.slotNum, dev_state);
+ }
+ /* send the ATTN button event to HPS framework */
+ hpc_slot_event_notify(ctrl_p->slot.slot_handle,
+ HPC_EVENT_SLOT_ATTN, HPC_EVENT_NORMAL);
+ break;
+ default:
+ cmn_err(CE_NOTE, "Unknown Notify() event %d on slot #%d\n",
+ val, ctrl_p->slot.slotNum);
+ break;
+ }
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+}
+
+static void
+pciehpc_acpi_uninstall_event_handler(pciehpc_t *ctrl_p)
+{
+ pciehpc_acpi_t *acpi_p = ctrl_p->misc_data;
+
+ PCIEHPC_DEBUG((CE_CONT, "Uninstall event handler for slot #%d\n",
+ ctrl_p->slot.slotNum));
+ (void) AcpiRemoveNotifyHandler(acpi_p->slot_dev_obj,
+ ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler);
+ (void) AcpiRemoveNotifyHandler(acpi_p->bus_obj,
+ ACPI_SYSTEM_NOTIFY, pciehpc_acpi_notify_handler);
+}
+
+/*
+ * Run _PS0 method to turn on power to the slot.
+ */
+static ACPI_STATUS
+pciehpc_acpi_power_on_slot(pciehpc_t *ctrl_p)
+{
+ int status = AE_OK;
+ pciehpc_acpi_t *acpi_p = ctrl_p->misc_data;
+ int dev_state = 0;
+
+ PCIEHPC_DEBUG((CE_CONT, "turn ON power to the slot #%d\n",
+ ctrl_p->slot.slotNum));
+
+ status = AcpiEvaluateObject(acpi_p->slot_dev_obj, "_PS0", NULL, NULL);
+
+ /* get the state of the device (from _STA method) */
+ if (status == AE_OK) {
+ if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj,
+ &dev_state) != AE_OK)
+ cmn_err(CE_WARN, "failed to get device status on slot #%d\n",
+ ctrl_p->slot.slotNum);
+ }
+ PCIEHPC_DEBUG((CE_CONT, "(3)device state on slot #%d: 0x%x\n",
+ ctrl_p->slot.slotNum, dev_state));
+
+ pciehpc_get_slot_state(ctrl_p);
+
+ if (ctrl_p->slot.slot_state != HPC_SLOT_CONNECTED)
+ cmn_err(CE_WARN, "failed to power on the slot #%d"
+ "(dev_state 0x%x, ACPI_STATUS 0x%x)\n",
+ ctrl_p->slot.slotNum, dev_state, status);
+
+ return (status);
+}
+
+/*
+ * Run _EJ0 method to turn off power to the slot.
+ */
+static ACPI_STATUS
+pciehpc_acpi_power_off_slot(pciehpc_t *ctrl_p)
+{
+ int status = AE_OK;
+ pciehpc_acpi_t *acpi_p = ctrl_p->misc_data;
+ int dev_state = 0;
+
+ PCIEHPC_DEBUG((CE_CONT, "turn OFF power to the slot #%d\n",
+ ctrl_p->slot.slotNum));
+
+ status = AcpiEvaluateObject(acpi_p->slot_dev_obj, "_EJ0", NULL, NULL);
+
+ /* get the state of the device (from _STA method) */
+ if (status == AE_OK) {
+ if (pciehpc_acpi_get_dev_state(acpi_p->slot_dev_obj,
+ &dev_state) != AE_OK)
+ cmn_err(CE_WARN, "failed to get device status on slot #%d\n",
+ ctrl_p->slot.slotNum);
+ }
+ PCIEHPC_DEBUG((CE_CONT, "(2)device state on slot #%d: 0x%x\n",
+ ctrl_p->slot.slotNum, dev_state));
+
+ pciehpc_get_slot_state(ctrl_p);
+
+ if (ctrl_p->slot.slot_state == HPC_SLOT_CONNECTED)
+ cmn_err(CE_WARN, "failed to power OFF the slot #%d"
+ "(dev_state 0x%x, ACPI_STATUS 0x%x)\n",
+ ctrl_p->slot.slotNum, dev_state, status);
+
+ return (status);
+}
+
+/*
+ * Check if the child device node of the bus object has _EJ0 method
+ * present.
+ */
+static ACPI_STATUS
+pciehpc_acpi_ej0_present(ACPI_HANDLE pcibus_obj)
+{
+ int status = AE_OK;
+ ACPI_HANDLE d0f0_obj;
+ ACPI_HANDLE ej0_hdl;
+
+ if ((status = AcpiGetNextObject(ACPI_TYPE_DEVICE, pcibus_obj,
+ NULL, &d0f0_obj)) == AE_OK) {
+ /* child device node(s) are present; check for _EJ0 method */
+ status = AcpiGetHandle(d0f0_obj, "_EJ0", &ej0_hdl);
+ }
+
+ return (status);
+}
+
+
+/*
+ * Get the status info (as returned by _STA method) for the device.
+ */
+static ACPI_STATUS
+pciehpc_acpi_get_dev_state(ACPI_HANDLE obj, int *statusp)
+{
+ ACPI_BUFFER rb;
+ ACPI_DEVICE_INFO *info = NULL;
+ int ret = AE_OK;
+
+ /*
+ * Get device info object
+ */
+ rb.Length = ACPI_ALLOCATE_BUFFER;
+ rb.Pointer = NULL;
+ if ((ret = AcpiGetObjectInfo(obj, &rb)) != AE_OK)
+ return (ret);
+ info = (ACPI_DEVICE_INFO *)rb.Pointer;
+
+ if (info->Valid & ACPI_VALID_STA) {
+ *statusp = info->CurrentStatus;
+ } else {
+ /*
+ * no _STA present; assume the device status is normal
+ * (i.e present, enabled, shown in UI and functioning).
+ * See section 6.3.7 of ACPI 3.0 spec.
+ */
+ *statusp = STATUS_NORMAL;
+ }
+
+ AcpiOsFree(info);
+
+ return (ret);
+}
+
+/*
+ * Query _OSC method to find out the current hot plug mode.
+ */
+/*ARGSUSED*/
+static ACPI_STATUS
+pciehpc_acpi_query_osc(dev_info_t *dip, ACPI_HANDLE osc_hdl, uint32_t *hp_mode)
+{
+ /* NOTE: This is currently not implemented */
+ return (AE_ERROR);
+}
+
+/*
+ * Switch to native hot plug mode if possible.
+ */
+/*ARGSUSED*/
+static ACPI_STATUS
+pciehpc_acpi_set_native_hp(dev_info_t *dip, ACPI_HANDLE osc_hdl)
+{
+ /* NOTE: This is currently not implemented */
+ return (AE_ERROR);
+}
+
+#ifdef DEBUG
+static void
+pciehpc_dump_acpi_obj(ACPI_HANDLE pcibus_obj)
+{
+ int status;
+ ACPI_BUFFER retbuf;
+
+ if (pcibus_obj == NULL)
+ return;
+
+ /* print the full path name */
+ retbuf.Pointer = NULL;
+ retbuf.Length = ACPI_ALLOCATE_BUFFER;
+ status = AcpiGetName(pcibus_obj, ACPI_FULL_PATHNAME, &retbuf);
+ if (status != AE_OK)
+ return;
+ cmn_err(CE_CONT, "PCIE BUS PATHNAME: %s\n", (char *)retbuf.Pointer);
+ AcpiOsFree(retbuf.Pointer);
+
+ /* dump all the methods for this bus node */
+ cmn_err(CE_CONT, " METHODS: \n");
+ status = AcpiWalkNamespace(ACPI_TYPE_METHOD, pcibus_obj, 1,
+ pciehpc_print_acpi_name, " ", NULL);
+ /* dump all the child devices */
+ status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, pcibus_obj, 1,
+ pciehpc_walk_obj_namespace, NULL, NULL);
+}
+
+/*ARGSUSED*/
+static ACPI_STATUS
+pciehpc_walk_obj_namespace(ACPI_HANDLE hdl, uint32_t nl, void *context,
+ void **ret)
+{
+ int status;
+ ACPI_BUFFER retbuf;
+ char buf[32];
+
+ /* print the full path name */
+ retbuf.Pointer = NULL;
+ retbuf.Length = ACPI_ALLOCATE_BUFFER;
+ status = AcpiGetName(hdl, ACPI_FULL_PATHNAME, &retbuf);
+ if (status != AE_OK)
+ return (status);
+ buf[0] = 0;
+ while (nl--)
+ (void) strcat(buf, " ");
+ cmn_err(CE_CONT, "%sDEVICE: %s\n", buf, (char *)retbuf.Pointer);
+ AcpiOsFree(retbuf.Pointer);
+
+ /* dump all the methods for this device */
+ cmn_err(CE_CONT, "%s METHODS: \n", buf);
+ status = AcpiWalkNamespace(ACPI_TYPE_METHOD, hdl, 1,
+ pciehpc_print_acpi_name, (void *)buf, NULL);
+ return (status);
+}
+
+/*ARGSUSED*/
+static ACPI_STATUS
+pciehpc_print_acpi_name(ACPI_HANDLE hdl, uint32_t nl, void *context, void **ret)
+{
+ int status;
+ ACPI_BUFFER retbuf;
+ char name[16];
+
+ retbuf.Pointer = name;
+ retbuf.Length = 16;
+ status = AcpiGetName(hdl, ACPI_SINGLE_NAME, &retbuf);
+ if (status == AE_OK)
+ cmn_err(CE_CONT, "%s %s \n", (char *)context, name);
+ return (AE_OK);
+}
+
+static void
+print_acpi_pathname(ACPI_HANDLE hdl)
+{
+ int status;
+ ACPI_BUFFER retbuf;
+
+ retbuf.Pointer = NULL;
+ retbuf.Length = ACPI_ALLOCATE_BUFFER;
+ status = AcpiGetName(hdl, ACPI_FULL_PATHNAME, &retbuf);
+ if (status == AE_OK) {
+ cmn_err(CE_CONT, "%s \n", (char *)retbuf.Pointer);
+ AcpiOsFree(retbuf.Pointer);
+ }
+}
+#endif
diff --git a/usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_acpi.h b/usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_acpi.h
new file mode 100644
index 0000000000..96e78556e2
--- /dev/null
+++ b/usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_acpi.h
@@ -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
+ */
+
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PCIEHPC_ACPI_H
+#define _PCIEHPC_ACPI_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/acpi/acpi.h>
+#include <sys/acpica.h>
+#include <sys/hotplug/pci/pciehpc_impl.h>
+
+/* soft state data structure for ACPI hot plug mode */
+typedef struct pciehpc_acpi {
+ /* handle for the ACPI device for the bus node with HPC */
+ ACPI_HANDLE bus_obj;
+
+ /* handle for the ACPI device for the slot (dev#0,func#0) */
+ ACPI_HANDLE slot_dev_obj;
+
+ /* ACPI control methods present on the bus node */
+ uint16_t bus_methods;
+
+ /* ACPI control methods on the slot device functions */
+ uint16_t slot_methods;
+} pciehpc_acpi_t;
+
+/* bit definitions in acpi_bus_methods */
+#define PCIEHPC_ACPI_OSC_PRESENT 0x0001
+#define PCIEHPC_ACPI_OSHP_PRESENT 0x0002
+#define PCIEHPC_ACPI_SUN_PRESENT 0x0004
+#define PCIEHPC_ACPI_STA_PRESENT 0x0008
+#define PCIEHPC_ACPI_EJ0_PRESENT 0x0010
+#define PCIEHPC_ACPI_HPP_PRESENT 0x0020
+#define PCIEHPC_ACPI_HPX_PRESENT 0x0040
+#define PCIEHPC_ACPI_PS0_PRESENT 0x0080
+#define PCIEHPC_ACPI_DSM_PRESENT 0x0080
+#define PCIEHPC_ACPI_STR_PRESENT 0x0100
+
+/* revision id of _OSC for PCI/PCI-X/PCI-Exp hierarchy */
+#define PCIE_OSC_REVISION_ID 1 /* defined in PCI fw ver 3.0 */
+
+/*
+ * _OSC method Capabilities buffer bit definitions (from PCI FW 3.0)
+ */
+/* first DWORD: errors from _OSC invocation (except bit 0) */
+#define OSC_DWORD1_QUERY_ENABLE 0x1 /* Query Support Flag */
+#define OSC_DWORD1_FAILED 0x2 /* _OSC failure */
+#define OSC_DWORD1_INV_UUID 0x4 /* invalid UUID */
+#define OSC_DWORD1_INV_REVID 0x8 /* invalid revision ID */
+#define OSC_CAPS_MASKED 0x10 /* capabilities masked */
+
+/* second DWORD: Support Field (set by OS) */
+#define OSC_DWORD2_EXT_PCI_CFG 0x1 /* Extended PCI Config Ops supported */
+#define OSC_DWORD2_ACT_PM 0x2 /* Active State PM supported */
+#define OSC_DWORD2_CLK_PM_CAP 0x4 /* Clock PM Capability supported */
+#define OSC_DWORD2_PCI_SEGS 0x8 /* PCI Segment Groups supported */
+#define OSC_DWORD2_MSI 0x10 /* MSI supported */
+
+/* third DWORD: Control Field (set by OS/BIOS) */
+#define OSC_DWORD3_PCIE_NAT_HP 0x1 /* PCI Exp Native Hot Plug control */
+#define OSC_DWORD3_SHPC_NAT_HP 0x2 /* SHPC Native Hot Plug control */
+#define OSC_DWORD3_PCIE_NAT_PM 0x4 /* PCI Exp Native Power Mgmt. control */
+#define OSC_DWORD3_PCIE_ADV_ERR 0x8 /* PCIE Advanced Err. rep. control */
+#define OSC_DWORD3_PCE_CAPS 0x10 /* PCIE Caps Structure control */
+
+/* Device status bit as returned by _STA method (see 6.3.7 of ACPI 3.0) */
+#define DEV_STS_PRESENT 0x1 /* device is present */
+#define DEV_STS_ENABLED 0x2 /* device is enabled */
+#define DEV_STS_SHOWN_UI 0x4 /* device should be shown in UI */
+#define DEV_STS_FUNC_OK 0x8 /* device functioning normally */
+#define STATUS_NORMAL \
+ (DEV_STS_PRESENT | DEV_STS_ENABLED | DEV_STS_SHOWN_UI | DEV_STS_FUNC_OK)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PCIEHPC_ACPI_H */
diff --git a/usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_ck804.c b/usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_ck804.c
new file mode 100644
index 0000000000..62794c00ee
--- /dev/null
+++ b/usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_ck804.c
@@ -0,0 +1,854 @@
+/*
+ * 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"
+
+/*
+ * CK8-04 specific interfaces used in PCIEHPC driver module (X86 only).
+ */
+
+#include <sys/types.h>
+#include <sys/note.h>
+#include <sys/kmem.h>
+#include <sys/debug.h>
+#include <sys/vtrace.h>
+#include <sys/autoconf.h>
+#include <sys/varargs.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/pci.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/time.h>
+#include <sys/callb.h>
+#include "pciehpc_ck804.h"
+
+static int ck804_slot_connect(caddr_t, hpc_slot_t, void *, uint_t);
+static int ck804_slot_disconnect(caddr_t, hpc_slot_t, void *, uint_t);
+static int ck804_slotinfo_init(pciehpc_t *);
+static int ck804_slotinfo_uninit(pciehpc_t *);
+static void ck804_slot_refclk(pciehpc_t *, int);
+static int ck804_map_regs(pciehpc_t *);
+static void ck804_unmap_regs(pciehpc_t *);
+static dev_info_t *ck804_find_lpc_bridge(pciehpc_t *);
+static int match_lpc_dev(dev_info_t *, void *);
+static uint16_t ck804_reg_get16(pciehpc_t *, uint_t);
+static void ck804_reg_put16(pciehpc_t *, uint_t, uint16_t);
+
+#ifdef DEBUG
+static void ck804_dump_pci_common_config(pciehpc_t *);
+static void ck804_dump_pci_device_config(pciehpc_t *);
+static void ck804_dump_pci_bridge_config(pciehpc_t *);
+static void ck804_dump_hpregs(pciehpc_t *);
+static char *pciehpc_led_state_text(hpc_led_state_t);
+#endif
+
+/*
+ * setup CK8-04 specific functions if necessary for this platform.
+ */
+void
+pciehpc_ck804_update_ops(pciehpc_t *ctrl_p)
+{
+ int *vendor_id = 0;
+ int *device_id = 0;
+ int *revision_id = 0;
+ uint_t count;
+ dev_info_t *dip = ctrl_p->dip;
+
+ /*
+ * check if this is a ck8-04 platform and revision A3 bridge
+ * controller
+ */
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "vendor-id", &vendor_id, &count) != DDI_PROP_SUCCESS) {
+ return;
+ }
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "device-id", &device_id, &count) != DDI_PROP_SUCCESS) {
+ ddi_prop_free(vendor_id);
+ return;
+ }
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "revision-id", &revision_id, &count) != DDI_PROP_SUCCESS) {
+ ddi_prop_free(vendor_id);
+ ddi_prop_free(device_id);
+ return;
+ }
+
+ if ((*vendor_id == NVIDIA_VENDOR_ID) &&
+ (*device_id == CK804_DEVICE_ID) &&
+ (*revision_id == 0xa3)) {
+ /*
+ * Need special handling for CK8-04 native mode hot plug
+ * operations. Update ops vector.
+ */
+ ctrl_p->ops.init_hpc_slotinfo = ck804_slotinfo_init;
+ ctrl_p->ops.uninit_hpc_slotinfo = ck804_slotinfo_uninit;
+ }
+
+ ddi_prop_free(vendor_id);
+ ddi_prop_free(device_id);
+ ddi_prop_free(revision_id);
+}
+
+/*
+ * ck804_slot_connect()
+ *
+ * Connect power to the PCI-E slot on CK8-04 based platform.
+ * The current CK8-04 implementations require special sequence
+ * which is slightly different from the standard sequence.
+ * This function is equivalent to pciehpc_slot_connect() with
+ * specific changes for CK8-04 native hot plug implementation.
+ *
+ * NOTE: This code may be applicable only on the specific
+ * implementations of CK8-04. The future revisions of CK8-04
+ * may be different. For now, this code is used on all CK8-04
+ * based platforms.
+ *
+ * Returns: HPC_SUCCESS if the slot is powered up and enabled.
+ * HPC_ERR_FAILED if the slot can't be enabled.
+ *
+ * (Note: This function is called by HPS framework at kernel context only.)
+ */
+/*ARGSUSED*/
+static int
+ck804_slot_connect(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ void *data, uint_t flags)
+{
+ uint16_t status;
+ uint16_t control;
+ uint16_t x_control;
+ uint16_t x_status;
+ int wait_time;
+ uint32_t vend_xp;
+
+ pciehpc_t *ctrl_p = (pciehpc_t *)ops_arg;
+
+ ASSERT(slot_hdl == ctrl_p->slot.slot_handle);
+
+ mutex_enter(&ctrl_p->pciehpc_mutex);
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(ctrl_p);
+
+ /* check if the slot is already in the 'connected' state */
+ if (ctrl_p->slot.slot_state == HPC_SLOT_CONNECTED) {
+ /* slot is already in the 'connected' state */
+ PCIEHPC_DEBUG3((CE_NOTE,
+ "ck804_slot_connect() slot %d already connected\n",
+ ctrl_p->slot.slotNum));
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_SUCCESS);
+ }
+
+ /* read the Slot Status Register */
+ status = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
+
+ /* make sure the MRL switch is closed if present */
+ if ((ctrl_p->has_mrl) && (status & PCIE_SLOTSTS_MRL_SENSOR_OPEN)) {
+ /* MRL switch is open */
+ cmn_err(CE_WARN, "MRL switch is open on slot #%d\n",
+ ctrl_p->slot.slotNum);
+ goto cleanup;
+ }
+
+ /* make sure the slot has a device present */
+ if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
+ /* slot is empty */
+ PCIEHPC_DEBUG((CE_NOTE,
+ "slot #%d is empty\n", ctrl_p->slot.slotNum));
+ goto cleanup;
+ }
+
+ /* get the current state of Slot Control Register */
+ control = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+
+ /* check if the slot's power state is ON */
+ if (!(control & PCIE_SLOTCTL_PWR_CONTROL)) {
+ /* slot is already powered up */
+ PCIEHPC_DEBUG((CE_NOTE,
+ "ck804_slot_connect() slot %d already connected\n",
+ ctrl_p->slot.slotNum));
+ ctrl_p->slot.slot_state = HPC_SLOT_CONNECTED;
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_SUCCESS);
+ }
+
+ /*
+ * Enable power to the slot involves:
+ * 1. Set power LED to blink and ATTN led to OFF.
+ * 2. Set power control ON in Slot Control Reigster.
+ * 3. Poll NV_XVR_SLOT_STS_PWROK bit in ext status reg to be up.
+ * 4. Set power LED to be ON.
+ * 5. Enable REFCLK for the slot in MCP_NVA_TGIO_CTRL register.
+ * 6. Set NV_XVR_SLOT_CTRL_SAFE bit in the extended control reg.
+ * 7. Poll link-up status bit NV_XVR_VEND_XP_DL_UP.
+ */
+
+ x_control = pciehpc_reg_get16(ctrl_p, NV_SVR_SLOT_CONTROL_REG);
+ x_status = pciehpc_reg_get16(ctrl_p, NV_SVR_SLOT_STATUS_REG);
+ PCIEHPC_DEBUG3((CE_NOTE,
+ "ck804_slot_connect() x_control %x, x_status %x"
+ " for slot %d\n", x_control, x_status, ctrl_p->slot.slotNum));
+
+ /* make sure REFCLK is off for this slot? */
+ ck804_slot_refclk(ctrl_p, DISABLE_REFCLK);
+
+ /* 1. set power LED to blink & ATTN led to OFF */
+ pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_BLINK);
+ pciehpc_set_led_state(ctrl_p, HPC_ATTN_LED, HPC_LED_OFF);
+
+ /* 2. set power control to ON */
+ control = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+ control &= ~PCIE_SLOTCTL_PWR_CONTROL;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+ /*
+ * 3. Poll NV_XVR_SLOT_STS_PWROK bit in Extended Slot Status
+ * register to be up.
+ */
+ wait_time = 200; /* 2 seconds of clock ticks OK? */
+ for (;;) {
+
+ x_status = pciehpc_reg_get16(ctrl_p, NV_SVR_SLOT_STATUS_REG);
+
+ if (x_status & NV_SVR_SLOT_STS_PWROK)
+ break;
+
+ if (wait_time <= 0) {
+ cmn_err(CE_WARN, "ck804_slot_connect: time out in waiting"
+ " for PWROK on slot %d\n", ctrl_p->slot.slotNum);
+ /* should we turn off power? */
+ goto cleanup;
+ }
+
+ wait_time -= 10;
+ delay(10); /* wait for 10 ticks */
+ }
+
+ /* 4. Set power LED to be ON */
+ pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_ON);
+
+ /* 5. Enable REFCLK for the slot in MCP_NVA_TGIO_CTRL register */
+ ck804_slot_refclk(ctrl_p, ENABLE_REFCLK);
+
+ /* 6. Set NV_XVR_SLOT_CTRL_SAFE bit in the extended control reg */
+ pciehpc_reg_put16(ctrl_p, NV_SVR_SLOT_CONTROL_REG,
+ x_control | NV_SVR_SLOT_CTRL_SAFE);
+
+ /* 7. Poll on link-up status bit NV_XVR_VEND_XP_DL_UP */
+ wait_time = 200; /* 2 seconds of clock ticks OK? */
+ for (;;) {
+
+ vend_xp = pciehpc_reg_get32(ctrl_p, NV_XVR_VEND_XP);
+
+ if (vend_xp & NV_XVR_VEND_XP_DL_UP)
+ break; /* link is UP */
+
+ if (wait_time <= 0) {
+ cmn_err(CE_WARN, "ck804_slot_connect: time out in waiting"
+ " for DL_UP on slot %d\n", ctrl_p->slot.slotNum);
+ /* should we turn off power? */
+ goto cleanup;
+ }
+
+ wait_time -= 10;
+ delay(10); /* wait for 10 ticks */
+ }
+
+ ctrl_p->slot.slot_state = HPC_SLOT_CONNECTED;
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_SUCCESS);
+
+cleanup:
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_ERR_FAILED);
+}
+
+/*
+ * This function is same as pciehpc_slotinfo_init() except
+ * that it changes the slot_connect/slot_disconnect functions
+ * to ck804 specific functions and also maps CK8-04 specific
+ * control registers.
+ */
+static int
+ck804_slotinfo_init(pciehpc_t *ctrl_p)
+{
+ pciehpc_slot_t *p = &ctrl_p->slot;
+
+ (void) pciehpc_slotinfo_init(ctrl_p);
+ p->slot_ops.hpc_op_connect = ck804_slot_connect;
+ p->slot_ops.hpc_op_disconnect = ck804_slot_disconnect;
+
+ /* map CK8-04 specific control registers */
+ return (ck804_map_regs(ctrl_p));
+}
+
+/*
+ * This function is same as pciehpc_slotinfo_uninit() except
+ * that it cleans up ck8-04 specific data for the slot.
+ */
+static int
+ck804_slotinfo_uninit(pciehpc_t *ctrl_p)
+{
+ (void) pciehpc_slotinfo_uninit(ctrl_p);
+
+ ck804_unmap_regs(ctrl_p);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Manage REFCLK (disable/enable) for the slot. Only device
+ * numbers B, C, D and E are valid. This works only for CK8-04.
+ * Needs work for slots under IO-4.
+ */
+static void
+ck804_slot_refclk(pciehpc_t *ctrl_p, int cmd)
+{
+ uint16_t tgio_ctrl;
+ uint16_t mask;
+
+ tgio_ctrl = ck804_reg_get16(ctrl_p, MCP_NVA_TGIO_CTRL);
+
+ switch (ctrl_p->dev) {
+ case 0xE:
+ mask = DISABLE_PEx_REFCLK_DEV_E;
+ break;
+ case 0xD:
+ mask = DISABLE_PEx_REFCLK_DEV_D;
+ break;
+ case 0xC:
+ mask = DISABLE_PEx_REFCLK_DEV_C;
+ break;
+ case 0xB:
+ mask = DISABLE_PEx_REFCLK_DEV_B;
+ break;
+ default:
+ cmn_err(CE_WARN,
+ "ck804_slot_refclk: invalid device number %x\n",
+ ctrl_p->dev);
+ return;
+ };
+
+ switch (cmd) {
+ case DISABLE_REFCLK:
+ tgio_ctrl |= mask;
+ ck804_reg_put16(ctrl_p, MCP_NVA_TGIO_CTRL, tgio_ctrl);
+ return;
+ case ENABLE_REFCLK:
+ tgio_ctrl &= ~mask;
+ ck804_reg_put16(ctrl_p, MCP_NVA_TGIO_CTRL, tgio_ctrl);
+ return;
+ default:
+ cmn_err(CE_WARN, "ck804_slot_refclk: invalid command %x\n",
+ cmd);
+ return;
+ }
+}
+
+#ifdef DEBUG
+static void
+ck804_dump_pci_common_config(pciehpc_t *ctrl_p)
+{
+ if (pciehpc_debug <= 2)
+ return;
+ cmn_err(CE_CONT, " Vendor ID = [0x%x]\n",
+ pciehpc_reg_get16(ctrl_p, PCI_CONF_VENID));
+ cmn_err(CE_CONT, " Device ID = [0x%x]\n",
+ pciehpc_reg_get16(ctrl_p, PCI_CONF_DEVID));
+ cmn_err(CE_CONT, " Command REG = [0x%x]\n",
+ pciehpc_reg_get16(ctrl_p, PCI_CONF_COMM));
+ cmn_err(CE_CONT, " Status REG = [0x%x]\n",
+ pciehpc_reg_get16(ctrl_p, PCI_CONF_STAT));
+ cmn_err(CE_CONT, " Revision ID = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_CONF_REVID));
+ cmn_err(CE_CONT, " Prog Class = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_CONF_PROGCLASS));
+ cmn_err(CE_CONT, " Dev Class = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_CONF_SUBCLASS));
+ cmn_err(CE_CONT, " Base Class = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_CONF_BASCLASS));
+ cmn_err(CE_CONT, " Device ID = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_CONF_CACHE_LINESZ));
+ cmn_err(CE_CONT, " Header Type = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_CONF_HEADER));
+ cmn_err(CE_CONT, " BIST = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_CONF_BIST));
+ cmn_err(CE_CONT, " BASE 0 = [0x%x]\n",
+ pciehpc_reg_get32(ctrl_p, PCI_CONF_BASE0));
+ cmn_err(CE_CONT, " BASE 1 = [0x%x]\n",
+ pciehpc_reg_get32(ctrl_p, PCI_CONF_BASE1));
+
+}
+
+static void
+ck804_dump_pci_device_config(pciehpc_t *ctrl_p)
+{
+ if (pciehpc_debug <= 2)
+ return;
+ ck804_dump_pci_common_config(ctrl_p);
+
+ cmn_err(CE_CONT, " BASE 2 = [0x%x]\n",
+ pciehpc_reg_get32(ctrl_p, PCI_CONF_BASE2));
+ cmn_err(CE_CONT, " BASE 3 = [0x%x]\n",
+ pciehpc_reg_get32(ctrl_p, PCI_CONF_BASE3));
+ cmn_err(CE_CONT, " BASE 4 = [0x%x]\n",
+ pciehpc_reg_get32(ctrl_p, PCI_CONF_BASE4));
+ cmn_err(CE_CONT, " BASE 5 = [0x%x]\n",
+ pciehpc_reg_get32(ctrl_p, PCI_CONF_BASE5));
+ cmn_err(CE_CONT, " Cardbus CIS = [0x%x]\n",
+ pciehpc_reg_get32(ctrl_p, PCI_CONF_CIS));
+ cmn_err(CE_CONT, " Sub VID = [0x%x]\n",
+ pciehpc_reg_get16(ctrl_p, PCI_CONF_SUBVENID));
+ cmn_err(CE_CONT, " Sub SID = [0x%x]\n",
+ pciehpc_reg_get16(ctrl_p, PCI_CONF_SUBSYSID));
+ cmn_err(CE_CONT, " ROM = [0x%x]\n",
+ pciehpc_reg_get32(ctrl_p, PCI_CONF_ROM));
+ cmn_err(CE_CONT, " I Line = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_CONF_ILINE));
+ cmn_err(CE_CONT, " I Pin = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_CONF_IPIN));
+ cmn_err(CE_CONT, " Max Grant = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_CONF_MIN_G));
+ cmn_err(CE_CONT, " Max Latent = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_CONF_MAX_L));
+}
+
+static void
+ck804_dump_pci_bridge_config(pciehpc_t *ctrl_p)
+{
+ if (pciehpc_debug <= 2)
+ return;
+
+ ck804_dump_pci_common_config(ctrl_p);
+
+ cmn_err(CE_CONT, "........................................\n");
+
+ cmn_err(CE_CONT, " Pri Bus = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_BCNF_PRIBUS));
+ cmn_err(CE_CONT, " Sec Bus = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_BCNF_SECBUS));
+ cmn_err(CE_CONT, " Sub Bus = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_BCNF_SUBBUS));
+ cmn_err(CE_CONT, " Latency = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_BCNF_LATENCY_TIMER));
+ cmn_err(CE_CONT, " I/O Base LO = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_BCNF_IO_BASE_LOW));
+ cmn_err(CE_CONT, " I/O Lim LO = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_BCNF_IO_LIMIT_LOW));
+ cmn_err(CE_CONT, " Sec. Status = [0x%x]\n",
+ pciehpc_reg_get16(ctrl_p, PCI_BCNF_SEC_STATUS));
+ cmn_err(CE_CONT, " Mem Base = [0x%x]\n",
+ pciehpc_reg_get16(ctrl_p, PCI_BCNF_MEM_BASE));
+ cmn_err(CE_CONT, " Mem Limit = [0x%x]\n",
+ pciehpc_reg_get16(ctrl_p, PCI_BCNF_MEM_LIMIT));
+ cmn_err(CE_CONT, " PF Mem Base = [0x%x]\n",
+ pciehpc_reg_get16(ctrl_p, PCI_BCNF_PF_BASE_LOW));
+ cmn_err(CE_CONT, " PF Mem Lim = [0x%x]\n",
+ pciehpc_reg_get16(ctrl_p, PCI_BCNF_PF_LIMIT_LOW));
+ cmn_err(CE_CONT, " PF Base HI = [0x%x]\n",
+ pciehpc_reg_get32(ctrl_p, PCI_BCNF_PF_BASE_HIGH));
+ cmn_err(CE_CONT, " PF Lim HI = [0x%x]\n",
+ pciehpc_reg_get32(ctrl_p, PCI_BCNF_PF_LIMIT_HIGH));
+ cmn_err(CE_CONT, " I/O Base HI = [0x%x]\n",
+ pciehpc_reg_get16(ctrl_p, PCI_BCNF_IO_BASE_HI));
+ cmn_err(CE_CONT, " I/O Lim HI = [0x%x]\n",
+ pciehpc_reg_get16(ctrl_p, PCI_BCNF_IO_LIMIT_HI));
+ cmn_err(CE_CONT, " ROM addr = [0x%x]\n",
+ pciehpc_reg_get32(ctrl_p, PCI_BCNF_ROM));
+ cmn_err(CE_CONT, " Intr Line = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_BCNF_ILINE));
+ cmn_err(CE_CONT, " Intr Pin = [0x%x]\n",
+ pciehpc_reg_get8(ctrl_p, PCI_BCNF_IPIN));
+ cmn_err(CE_CONT, " Bridge Ctrl = [0x%x]\n",
+ pciehpc_reg_get16(ctrl_p, PCI_BCNF_BCNTRL));
+}
+
+/* dump hot plug registers */
+static void
+ck804_dump_hpregs(pciehpc_t *ctrl_p)
+{
+ uint16_t control;
+ uint32_t capabilities;
+
+ capabilities = pciehpc_reg_get32(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCAP);
+
+ control = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+
+ cmn_err(CE_NOTE, "ck804_dump_hpregs: hot plug info for slot %d\n",
+ ctrl_p->slot.slotNum);
+ cmn_err(CE_NOTE, "Attention Button Present = %s",
+ capabilities & PCIE_SLOTCAP_ATTN_BUTTON ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "Power controller Present = %s",
+ capabilities & PCIE_SLOTCAP_POWER_CONTROLLER ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "MRL Sensor Present = %s",
+ capabilities & PCIE_SLOTCAP_MRL_SENSOR ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "Attn Indicator Present = %s",
+ capabilities & PCIE_SLOTCAP_ATTN_INDICATOR ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "Power Indicator Present = %s",
+ capabilities & PCIE_SLOTCAP_PWR_INDICATOR ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "HotPlug Surprise = %s",
+ capabilities & PCIE_SLOTCAP_HP_SURPRISE ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "HotPlug Capable = %s",
+ capabilities & PCIE_SLOTCAP_HP_CAPABLE ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "Physical Slot Number = %d",
+ PCIE_SLOTCAP_PHY_SLOT_NUM(capabilities));
+
+ cmn_err(CE_NOTE, "Attn Button interrupt Enabled = %s",
+ control & PCIE_SLOTCTL_ATTN_BTN_EN ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "Power Fault interrupt Enabled = %s",
+ control & PCIE_SLOTCTL_PWR_FAULT_EN ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "MRL Sensor INTR Enabled = %s",
+ control & PCIE_SLOTCTL_MRL_SENSOR_EN ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "Presence interrupt Enabled = %s",
+ control & PCIE_SLOTCTL_PRESENCE_CHANGE_EN ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "Cmd Complete interrupt Enabled = %s",
+ control & PCIE_SLOTCTL_CMD_INTR_EN ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "HotPlug interrupt Enabled = %s",
+ control & PCIE_SLOTCTL_HP_INTR_EN ? "Yes":"No");
+
+ cmn_err(CE_NOTE, "Power Indicator LED = %s", pciehpc_led_state_text(
+ pciehpc_led_state_to_hpc(pcie_slotctl_pwr_indicator_get(control))));
+
+ cmn_err(CE_NOTE, "Attn Indicator LED = %s",
+ pciehpc_led_state_text(pciehpc_led_state_to_hpc(
+ pcie_slotctl_attn_indicator_get(control))));
+
+ cmn_err(CE_NOTE, "Extended Slot Status Register: %x",
+ pciehpc_reg_get16(ctrl_p, NV_SVR_SLOT_STATUS_REG));
+
+ cmn_err(CE_NOTE, "Extended Slot Control Register: %x",
+ pciehpc_reg_get16(ctrl_p, NV_SVR_SLOT_CONTROL_REG));
+
+ cmn_err(CE_NOTE, "Strapping Register for Slot Capability: %x",
+ pciehpc_reg_get32(ctrl_p, NV_XVR_VEND_SLOT_STRAP));
+
+ cmn_err(CE_NOTE, "TGIO Control Register: %x",
+ ck804_reg_get16(ctrl_p, MCP_NVA_TGIO_CTRL));
+}
+
+static char *
+pciehpc_led_state_text(hpc_led_state_t state)
+{
+ switch (state) {
+ case HPC_LED_ON:
+ return ("on");
+ case HPC_LED_OFF:
+ return ("off");
+ case HPC_LED_BLINK:
+ default:
+ return ("blink");
+ }
+}
+#endif
+
+/*
+ * ck804_slot_disconnect()
+ *
+ * Disconnect power to the slot. This function is equivalent of
+ * pciehpc_disconnect() with necessary changes specific to CK8-04 native
+ * hot plug implementation.
+ *
+ * Returns: HPC_SUCCESS if the slot is powered up and enabled.
+ * HPC_ERR_FAILED if the slot can't be enabled.
+ *
+ * (Note: This function is called by HPS framework at kernel context only.)
+ */
+/*ARGSUSED*/
+static int
+ck804_slot_disconnect(caddr_t ops_arg, hpc_slot_t slot_hdl,
+ void *data, uint_t flags)
+{
+ uint16_t status;
+ uint16_t control;
+ uint16_t x_control;
+ uint16_t x_status;
+ int wait_time;
+
+ pciehpc_t *ctrl_p = (pciehpc_t *)ops_arg;
+
+ ASSERT(slot_hdl == ctrl_p->slot.slot_handle);
+
+ mutex_enter(&ctrl_p->pciehpc_mutex);
+
+ /* get the current state of the slot */
+ pciehpc_get_slot_state(ctrl_p);
+
+ /* check if the slot is already in the 'disconnected' state */
+ if (ctrl_p->slot.slot_state == HPC_SLOT_DISCONNECTED) {
+ /* slot is in the 'disconnected' state */
+ PCIEHPC_DEBUG((CE_NOTE,
+ "ck804_slot_disconnect(): slot %d already disconnected\n",
+ ctrl_p->slot.slotNum));
+ ASSERT(ctrl_p->slot.power_led_state == HPC_LED_OFF);
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_SUCCESS);
+ }
+
+ /* read the Slot Status Register */
+ status = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTSTS);
+
+ /* make sure the slot has a device present */
+ if (!(status & PCIE_SLOTSTS_PRESENCE_DETECTED)) {
+ /* slot is empty */
+ PCIEHPC_DEBUG((CE_WARN,
+ "ck804_slot_disconnect(): slot %d is empty\n",
+ ctrl_p->slot.slotNum));
+ goto cleanup1;
+ }
+
+ /*
+ * Disable power to the slot involves:
+ * 1. Set power LED to blink.
+ * 2. Disable REFCLK for the slot.
+ * 3. Set power control OFF in Slot Control Reigster and
+ * wait for Command Completed Interrupt or 1 sec timeout.
+ * 4. Clear NV_SVR_SLOT_CTRL_SAFE bit in the Ext. Control reg.
+ * 5. Poll NV_XVR_SLOT_STS_PWROK bit in ext status reg to be down.
+ * 6. Set POWER led and ATTN led to be OFF.
+ */
+
+ /* 1. set power LED to blink */
+ pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_BLINK);
+
+ /* 2. Disable REFCLK for the slot */
+ ck804_slot_refclk(ctrl_p, DISABLE_REFCLK);
+
+ /* 3. set power control to OFF */
+ control = pciehpc_reg_get16(ctrl_p,
+ ctrl_p->pcie_caps_reg_offset + PCIE_SLOTCTL);
+ control |= PCIE_SLOTCTL_PWR_CONTROL;
+ pciehpc_issue_hpc_command(ctrl_p, control);
+
+ /* 4. Clear NV_SVR_SLOT_CTRL_SAFE bit in NV_SVR_SLOT_CTRL reg */
+ x_control = pciehpc_reg_get16(ctrl_p, NV_SVR_SLOT_CONTROL_REG);
+ pciehpc_reg_put16(ctrl_p, NV_SVR_SLOT_CONTROL_REG,
+ x_control & ~NV_SVR_SLOT_CTRL_SAFE);
+
+ /* 5. Poll NV_SVR_SLOT_STS_PWROK bit in ext status reg to be off */
+ wait_time = 200; /* 2 seconds of clock ticks OK? */
+ for (;;) {
+
+ x_status = pciehpc_reg_get16(ctrl_p, NV_SVR_SLOT_STATUS_REG);
+
+ if (!(x_status & NV_SVR_SLOT_STS_PWROK))
+ break; /* PWROK is cleared */
+
+ if (wait_time <= 0) {
+ cmn_err(CE_WARN, "ck804_slot_disconnect: time out in waiting"
+ " for PWROK to go off on slot %d\n",
+ ctrl_p->slot.slotNum);
+ goto cleanup2;
+ }
+
+ wait_time -= 10;
+ delay(10); /* wait for 10 ticks */
+ }
+
+ /* 6. Set power LED to be OFF */
+ pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_OFF);
+ pciehpc_set_led_state(ctrl_p, HPC_ATTN_LED, HPC_LED_OFF);
+
+ ctrl_p->slot.slot_state = HPC_SLOT_DISCONNECTED;
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_SUCCESS);
+
+cleanup2:
+ pciehpc_set_led_state(ctrl_p, HPC_POWER_LED, HPC_LED_ON);
+cleanup1:
+ mutex_exit(&ctrl_p->pciehpc_mutex);
+ return (HPC_ERR_FAILED);
+}
+
+/*
+ * Background:
+ *
+ * CK8-04 defines two sets of system control registers (in I/O space)
+ * that are mapped in and exposed from PCI config space in the ISA
+ * bridge. One of the I/O BARs (offset 0x68 in PIC-ISA config space)
+ * defines MCP_NVA_TGIO_CTRL register (offset 0xCC from the I/O BAR)
+ * that has bits for managing reference clock for all the hot plug slots
+ * (max of 4 depending on the CK8-04 configuration) during hot plug
+ * operations.
+ *
+ * NOTE: The NVA I/O BAR is located in the PCI-ISA bridge device
+ * on both CK8-04 and IO-04. Please note that these I/O BARs are
+ * not regular BARs (BAR0-BAR4) in PCI config space.
+ *
+ * Description:
+ *
+ * This function finds regspec for NVA I/O BAR in the PCI-ISA
+ * device node and maps it in. PCI-ISA node appears as a child
+ * of parent nexus node (i.e child of npe nexus) with a name
+ * pci10de,51. It assumes the regspec number as 1. So, this
+ * kind of mapping registers from another device function is
+ * not a common thing so it is not clean w.r.t DDI but
+ * there is nothing in DDI that disallows it.
+ */
+static int
+ck804_map_regs(pciehpc_t *ctrl_p)
+{
+ dev_info_t *lpcdev;
+ ddi_device_acc_attr_t attr;
+ ddi_acc_handle_t handle;
+ pciehpc_ck804_t *ck804_p;
+ caddr_t addrp;
+
+ /* find the LPC bridge node for this pci-e hierarchy */
+ if ((lpcdev = ck804_find_lpc_bridge(ctrl_p)) == NULL)
+ return (DDI_FAILURE);
+
+ /*
+ * Map in Analog Control I/O Bar.
+ *
+ * NOTE: reg set #0 corresponds to System Control I/O Bar and
+ * reg set #1 corresponds to Analog Control I/O Bar.
+ */
+ attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
+ attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
+ attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
+ if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(lpcdev)))
+ attr.devacc_attr_access = DDI_FLAGERR_ACC;
+ if ((ddi_regs_map_setup(lpcdev, 1, &addrp, 0, 0,
+ &attr, &handle)) == DDI_SUCCESS) {
+ ck804_p = kmem_zalloc(sizeof (pciehpc_ck804_t), KM_SLEEP);
+ ck804_p->analog_bar_hdl = handle;
+ ck804_p->analog_bar_base = addrp;
+ ck804_p->lpcdev = lpcdev;
+ ctrl_p->misc_data = (void *)ck804_p;
+ return (DDI_SUCCESS);
+ }
+
+ return (DDI_FAILURE);
+}
+
+/*
+ * Unmap CK8-04 specific data/regs.
+ */
+static void
+ck804_unmap_regs(pciehpc_t *ctrl_p)
+{
+ pciehpc_ck804_t *ck804_p = (pciehpc_ck804_t *)ctrl_p->misc_data;
+
+ if (ck804_p == NULL)
+ return;
+
+ ddi_regs_map_free(&ck804_p->analog_bar_hdl);
+ kmem_free(ck804_p, sizeof (pciehpc_ck804_t));
+ ctrl_p->misc_data = NULL;
+}
+
+static dev_info_t *
+ck804_find_lpc_bridge(pciehpc_t *ctrl_p)
+{
+ dev_info_t *pdip = ddi_get_parent(ctrl_p->dip);
+ dev_info_t *lpc_dip = NULL;
+ int count;
+
+ ndi_devi_enter(pdip, &count);
+ ddi_walk_devs(ddi_get_child(pdip), match_lpc_dev, &lpc_dip);
+ ndi_devi_exit(pdip, count);
+
+ return (lpc_dip);
+}
+
+/*
+ * check if the dip matches LPC Bridge node
+ */
+static int
+match_lpc_dev(dev_info_t *dip, void *hdl)
+{
+ dev_info_t **lpcdev = (dev_info_t **)hdl;
+ int *vendor_id = 0;
+ int *device_id = 0;
+ uint_t count;
+
+ *lpcdev = NULL;
+
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "vendor-id", &vendor_id, &count) != DDI_PROP_SUCCESS) {
+ return (DDI_WALK_TERMINATE);
+ }
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "device-id", &device_id, &count) != DDI_PROP_SUCCESS) {
+ ddi_prop_free(vendor_id);
+ return (DDI_WALK_TERMINATE);
+ }
+
+ if ((*vendor_id == NVIDIA_VENDOR_ID) &&
+ (*device_id == CK804_DEVICE_ID)) {
+ *lpcdev = dip;
+ ddi_prop_free(vendor_id);
+ ddi_prop_free(device_id);
+ return (DDI_WALK_TERMINATE);
+ }
+
+ ddi_prop_free(vendor_id);
+ ddi_prop_free(device_id);
+ return (DDI_WALK_PRUNECHILD);
+}
+
+static uint16_t
+ck804_reg_get16(pciehpc_t *ctrl_p, uint_t off)
+{
+ pciehpc_ck804_t *ck804_p = (pciehpc_ck804_t *)ctrl_p->misc_data;
+
+ ASSERT(ck804_p != NULL);
+
+ return (ddi_mem_get16(ck804_p->analog_bar_hdl,
+ (uint16_t *)(ck804_p->analog_bar_base + off)));
+}
+
+static void
+ck804_reg_put16(pciehpc_t *ctrl_p, uint_t off, uint16_t val)
+{
+ pciehpc_ck804_t *ck804_p = (pciehpc_ck804_t *)ctrl_p->misc_data;
+
+ ASSERT(ck804_p != NULL);
+
+ ddi_mem_put16(ck804_p->analog_bar_hdl,
+ (uint16_t *)(ck804_p->analog_bar_base + off), val);
+}
diff --git a/usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_ck804.h b/usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_ck804.h
new file mode 100644
index 0000000000..6ea64359ef
--- /dev/null
+++ b/usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_ck804.h
@@ -0,0 +1,100 @@
+/*
+ * 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 _PCIEHPC_CK804_H
+#define _PCIEHPC_CK804_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/acpi/acpi.h>
+#include <sys/acpica.h>
+#include <sys/hotplug/pci/pciehpc_impl.h>
+
+/* soft state data structure specific to CK8-04 */
+typedef struct pciehpc_ck804 {
+ dev_info_t *lpcdev; /* dip for LPC Bridge device */
+ ddi_acc_handle_t analog_bar_hdl; /* acc handle for Analog BAR */
+ caddr_t analog_bar_base; /* Analog BAR base */
+} pciehpc_ck804_t;
+
+/* vendor/device ids for CK8-04 */
+#define NVIDIA_VENDOR_ID 0x10de
+#define CK804_DEVICE_ID 0x005d
+#define CK804_LPC_BRIDGE_DEVID 0x0051
+
+/* register offsets in Analog Control BAR */
+#define MCP_NVA_TGIO_CTRL 0xCC
+
+/* extended slot control/status registers in PCI config space */
+#define NV_SVR_SLOT_STATUS_REG 0xA4 /* 2 byte ext. status register */
+#define NV_SVR_SLOT_CONTROL_REG 0xA6 /* 2 byte ext. status register */
+
+/* (Backdoor Strapping) Slot Capability Register (read/write) */
+#define NV_XVR_VEND_SLOT_STRAP 0xF20 /* 4 byte */
+
+/* vendor specific register NV_XVR_VEND_XP (4 bytes) in PCI config space */
+#define NV_XVR_VEND_XP 0xF00
+
+/* bit definitions in NV_XVR_VEND_XP regiser */
+#define NV_XVR_VEND_XP_DL_UP 0x40000000
+
+/* bit definitions in NV_XVR_VEND_XP register */
+#define NV_XVR_VEND_SLOT_STRAP_HP_CAPABLE 0x40 /* hot plug capable slot */
+#define SLOT_STRAP_CAPS 0x5B /* capabilities/features available */
+
+/* bit definitions in Extended Slot Status register */
+#define NV_SVR_SLOT_STS_PWROK 0x0020 /* POWER OK */
+
+/* bit definitions in Extended Slot Control register */
+#define NV_SVR_SLOT_CTRL_SAFE 0x0001 /* safe to assert/release PERST */
+
+/*
+ * bit definitions for Reference Clock Control (MCP_NVA_TGIO_CTRL)
+ *
+ * PE0_REFCLK is for device #E
+ * PE1_REFCLK is for device #D
+ * PE2_REFCLK is for device #C
+ * PE3_REFCLK is for device #B
+ */
+#define DISABLE_PEx_REFCLK_DEV_E 0x01 /* disable REFCLK for dev#E */
+#define DISABLE_PEx_REFCLK_DEV_D 0x02 /* disable REFCLK for dev#D */
+#define DISABLE_PEx_REFCLK_DEV_C 0x04 /* disable REFCLK for dev#C */
+#define DISABLE_PEx_REFCLK_DEV_B 0x08 /* disable REFCLK for dev#B */
+
+#define ENABLE_REFCLK 0
+#define DISABLE_REFCLK 1
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PCIEHPC_CK804_H */
diff --git a/usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_x86.c b/usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_x86.c
new file mode 100644
index 0000000000..f9659bbd9e
--- /dev/null
+++ b/usr/src/uts/i86pc/io/pciex/hotplug/pciehpc/pciehpc_x86.c
@@ -0,0 +1,64 @@
+/*
+ * 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"
+
+#include <sys/types.h>
+#include <sys/note.h>
+#include <sys/kmem.h>
+#include <sys/debug.h>
+#include <sys/vtrace.h>
+#include <sys/autoconf.h>
+#include <sys/varargs.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/pci.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/time.h>
+#include <sys/callb.h>
+#include <sys/hotplug/pci/pciehpc_impl.h>
+
+extern int pciehpc_acpi_hotplug_enabled(dev_info_t *dip);
+extern void pciehpc_acpi_setup_ops(pciehpc_t *ctrl_p);
+extern void pciehpc_ck804_update_ops(pciehpc_t *ctrl_p);
+
+/*
+ * Update ops vector with platform specific (ACPI, CK8-04,...) functions.
+ */
+void
+pciehpc_update_ops(pciehpc_t *ctrl_p)
+{
+ /* update platform specific (ACPI, CK8-04,...) impl. ops */
+ if (pciehpc_acpi_hotplug_enabled(ctrl_p->dip)) {
+ /* update ops vector for ACPI mode */
+ pciehpc_acpi_setup_ops(ctrl_p);
+ ctrl_p->hp_mode = PCIEHPC_ACPI_HP_MODE;
+ } else {
+ /* check for CK8-04 and update the ops if necessary */
+ (void) pciehpc_ck804_update_ops(ctrl_p);
+ }
+}
diff --git a/usr/src/uts/i86pc/io/pciex/inc.flg b/usr/src/uts/i86pc/io/pciex/inc.flg
new file mode 100644
index 0000000000..4379616c55
--- /dev/null
+++ b/usr/src/uts/i86pc/io/pciex/inc.flg
@@ -0,0 +1,120 @@
+#!/bin/sh
+#
+# 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.
+#
+
+#ident "%Z%%M% %I% %E% SMI"
+
+# This file brings down all that is needed to build the
+# x86 PCI Express code.
+#
+
+# header files
+find_files "s.*.h" \
+ usr/src/uts/common/sys \
+ usr/src/uts/sparc/sys \
+ usr/src/uts/sparc/v7/sys \
+ usr/src/uts/sparc/v9/sys \
+ usr/src/uts/sun/sys \
+ usr/src/uts/sun4/sys \
+ usr/src/uts/intel/sys \
+ usr/src/uts/intel/ia32/sys \
+ usr/src/uts/common/rpc \
+ usr/src/uts/common/netinet \
+ usr/src/uts/common/inet \
+ usr/src/uts/common/net \
+ usr/src/uts/common/vm \
+ usr/src/uts/common/gssapi
+
+# cfgadm plugin directory
+find_files "s.*" \
+ usr/src/lib/cfgadm_plugins/pci \
+ usr/src/cmd/pcidr
+
+# to compile the drivers/modules
+find_files "s.*" \
+ usr/src/uts/i86pc/npe \
+ usr/src/uts/i86pc/pcie_pci \
+ usr/src/uts/i86pc/pciehpc \
+ usr/src/uts/i86pc/pci_autoconfig \
+ usr/src/uts/intel/pcicfg
+
+# packaging files
+find_files "s.*" \
+ usr/src/pkgdefs/common_files \
+ usr/src/pkgdefs/SUNWckr \
+ usr/src/pkgdefs/SUNWcakr.i \
+ usr/src/pkgdefs/SUNWcsu
+
+# extra files needed
+find_files "s.*" \
+ usr/src/common/smbios \
+ usr/src/uts/common/os \
+ usr/src/uts/common/rpc \
+ usr/src/uts/intel/asm \
+ usr/src/uts/intel/amd64 \
+ usr/src/uts/i86pc/io/pci \
+ usr/src/uts/i86pc/io/pcplusmp \
+ usr/src/uts/i86pc/io/acpica
+
+# makefiles
+echo_file usr/src/Makefile.master
+echo_file usr/src/Makefile.master.64
+echo_file usr/src/req.flg
+echo_file usr/src/Makefile.psm
+echo_file usr/src/Makefile.psm.targ
+echo_file usr/src/uts/Makefile
+echo_file usr/src/uts/Makefile.targ
+echo_file usr/src/uts/Makefile.uts
+echo_file usr/src/uts/sun/Makefile.files
+echo_file usr/src/uts/sun/Makefile.rules
+echo_file usr/src/uts/common/Makefile.files
+echo_file usr/src/uts/common/Makefile.rules
+echo_file usr/src/uts/common/sys/Makefile
+echo_file usr/src/uts/i86pc/Makefile
+echo_file usr/src/uts/i86pc/Makefile.files
+echo_file usr/src/uts/i86pc/Makefile.rules
+echo_file usr/src/uts/i86pc/Makefile.i86pc
+echo_file usr/src/uts/i86pc/Makefile.targ
+echo_file usr/src/uts/intel/Makefile
+echo_file usr/src/uts/intel/Makefile.files
+echo_file usr/src/uts/intel/Makefile.rules
+echo_file usr/src/uts/intel/Makefile.intel
+echo_file usr/src/uts/intel/Makefile.targ
+echo_file usr/src/uts/intel/ia32/ml/ia32.il
+echo_file usr/src/cmd/Makefile
+echo_file usr/src/cmd/Makefile.cmd
+echo_file usr/src/cmd/Makefile.targ
+echo_file usr/src/pkgdefs/awk_procedure
+echo_file usr/src/pkgdefs/bld_awk_pkginfo.ksh
+echo_file usr/src/pkgdefs/req.flg
+echo_file usr/src/pkgdefs/Makefile
+echo_file usr/src/pkgdefs/Makefile.com
+echo_file usr/src/pkgdefs/Makefile.prtarg
+echo_file usr/src/pkgdefs/Makefile.targ
+echo_file usr/src/pkgdefs/etc/exception_list_sparc
+echo_file usr/src/pkgdefs/etc/exception_list_i386
+echo_file usr/src/cmd/mapfile_noexstk
+echo_file usr/src/cmd/sgs/mapfiles/sparc/map.pagealign
diff --git a/usr/src/uts/i86pc/io/pciex/npe.c b/usr/src/uts/i86pc/io/pciex/npe.c
new file mode 100644
index 0000000000..54aed71fd2
--- /dev/null
+++ b/usr/src/uts/i86pc/io/pciex/npe.c
@@ -0,0 +1,719 @@
+/*
+ * 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"
+
+/*
+ * Host to PCI-Express local bus driver
+ */
+
+#include <sys/conf.h>
+#include <sys/modctl.h>
+#include <sys/pcie.h>
+#include <sys/pci_impl.h>
+#include <sys/sysmacros.h>
+#include <sys/ddi_intr.h>
+#include <sys/sunndi.h>
+#include <sys/hotplug/pci/pcihp.h>
+#include <io/pci/pci_common.h>
+#include <io/pci/pci_tools_ext.h>
+#include <io/pci/pci_var.h>
+#include <io/pciex/pcie_error.h>
+
+/*
+ * Bus Operation functions
+ */
+static int npe_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *,
+ off_t, off_t, caddr_t *);
+static int npe_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
+ void *, void *);
+static int npe_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t,
+ ddi_intr_handle_impl_t *, void *);
+
+struct bus_ops npe_bus_ops = {
+ BUSO_REV,
+ npe_bus_map,
+ NULL,
+ NULL,
+ NULL,
+ i_ddi_map_fault,
+ ddi_dma_map,
+ ddi_dma_allochdl,
+ ddi_dma_freehdl,
+ ddi_dma_bindhdl,
+ ddi_dma_unbindhdl,
+ ddi_dma_flush,
+ ddi_dma_win,
+ ddi_dma_mctl,
+ npe_ctlops,
+ ddi_bus_prop_op,
+ 0, /* (*bus_get_eventcookie)(); */
+ 0, /* (*bus_add_eventcall)(); */
+ 0, /* (*bus_remove_eventcall)(); */
+ 0, /* (*bus_post_event)(); */
+ 0, /* (*bus_intr_ctl)(); */
+ 0, /* (*bus_config)(); */
+ 0, /* (*bus_unconfig)(); */
+ NULL, /* (*bus_fm_init)(); */
+ NULL, /* (*bus_fm_fini)(); */
+ NULL, /* (*bus_fm_access_enter)(); */
+ NULL, /* (*bus_fm_access_exit)(); */
+ NULL, /* (*bus_power)(); */
+ npe_intr_ops /* (*bus_intr_op)(); */
+};
+
+/*
+ * One goal here is to leverage off of the pcihp.c source without making
+ * changes to it. Call into it's cb_ops directly if needed, piggybacking
+ * anything else needed by the pci_tools.c module. Only pci_tools and pcihp
+ * will be using the PCI devctl node.
+ */
+static int npe_open(dev_t *, int, int, cred_t *);
+static int npe_close(dev_t, int, int, cred_t *);
+static int npe_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+static int npe_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
+ caddr_t, int *);
+static int npe_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
+
+struct cb_ops npe_cb_ops = {
+ npe_open, /* open */
+ npe_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ npe_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ npe_prop_op, /* cb_prop_op */
+ NULL, /* streamtab */
+ D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
+ CB_REV, /* rev */
+ nodev, /* int (*cb_aread)() */
+ nodev /* int (*cb_awrite)() */
+};
+
+
+/*
+ * Device Node Operation functions
+ */
+static int npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
+static int npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
+
+struct dev_ops npe_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* refcnt */
+ pcihp_info, /* info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ npe_attach, /* attach */
+ npe_detach, /* detach */
+ nulldev, /* reset */
+ &npe_cb_ops, /* driver operations */
+ &npe_bus_ops /* bus operations */
+};
+
+/*
+ * Internal routines in support of particular npe_ctlops.
+ */
+static int npe_removechild(dev_info_t *child);
+static int npe_initchild(dev_info_t *child);
+
+/*
+ * External support routine
+ */
+extern void npe_query_acpi_mcfg(dev_info_t *dip);
+extern void npe_ck804_fix_aer_ptr(dev_info_t *child);
+
+/*
+ * Module linkage information for the kernel.
+ */
+static struct modldrv modldrv = {
+ &mod_driverops, /* Type of module */
+ "Host to PCIe nexus driver %I%",
+ &npe_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modldrv,
+ NULL
+};
+
+/* Save minimal state. */
+void *npe_statep;
+
+
+int
+_init(void)
+{
+ int e;
+
+ /*
+ * Initialize per-pci bus soft state pointer.
+ */
+ e = ddi_soft_state_init(&npe_statep, sizeof (pci_state_t), 1);
+ if (e != 0)
+ return (e);
+
+ if ((e = mod_install(&modlinkage)) != 0)
+ ddi_soft_state_fini(&npe_statep);
+
+ return (e);
+}
+
+
+int
+_fini(void)
+{
+ int rc;
+
+ rc = mod_remove(&modlinkage);
+ if (rc != 0)
+ return (rc);
+
+ ddi_soft_state_fini(&npe_statep);
+ return (rc);
+}
+
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+
+/*ARGSUSED*/
+static int
+npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+ /*
+ * Use the minor number as constructed by pcihp, as the index value to
+ * ddi_soft_state_zalloc.
+ */
+ int instance = ddi_get_instance(devi);
+ pci_state_t *pcip = NULL;
+
+ if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "device_type",
+ "pciex") != DDI_PROP_SUCCESS) {
+ cmn_err(CE_WARN, "npe: 'device_type' prop create failed");
+ }
+
+ if (ddi_soft_state_zalloc(npe_statep, instance) == DDI_SUCCESS)
+ pcip = ddi_get_soft_state(npe_statep, instance);
+
+ if (pcip == NULL)
+ return (DDI_FAILURE);
+
+ pcip->pci_dip = devi;
+
+ /*
+ * Initialize hotplug support on this bus. At minimum
+ * (for non hotplug bus) this would create ":devctl" minor
+ * node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls
+ * to this bus.
+ */
+ if (pcihp_init(devi) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "npe: Failed to setup hotplug framework");
+ ddi_soft_state_free(npe_statep, instance);
+ return (DDI_FAILURE);
+ }
+
+ if (pcitool_init(devi, B_TRUE) != DDI_SUCCESS) {
+ (void) pcihp_uninit(devi);
+ ddi_soft_state_free(npe_statep, instance);
+ return (DDI_FAILURE);
+ }
+
+ npe_query_acpi_mcfg(devi);
+ ddi_report_dev(devi);
+ return (DDI_SUCCESS);
+
+}
+
+
+/*ARGSUSED*/
+static int
+npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
+{
+ int instance = ddi_get_instance(devi);
+
+ /* Uninitialize pcitool support. */
+ pcitool_uninit(devi);
+
+ /*
+ * Uninitialize hotplug support on this bus.
+ */
+ (void) pcihp_uninit(devi);
+ ddi_soft_state_free(npe_statep, instance);
+ return (DDI_SUCCESS);
+}
+
+
+static int
+npe_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
+ off_t offset, off_t len, caddr_t *vaddrp)
+{
+ int rnumber;
+ int length;
+ int space;
+ ddi_acc_hdl_t *hp;
+ ddi_map_req_t mr;
+ pci_regspec_t pci_reg;
+ pci_regspec_t *pci_rp;
+ struct regspec reg;
+ pci_acc_cfblk_t *cfp;
+
+
+ mr = *mp; /* Get private copy of request */
+ mp = &mr;
+
+ /*
+ * check for register number
+ */
+ switch (mp->map_type) {
+ case DDI_MT_REGSPEC:
+ pci_reg = *(pci_regspec_t *)(mp->map_obj.rp);
+ pci_rp = &pci_reg;
+ if (pci_common_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ break;
+ case DDI_MT_RNUMBER:
+ rnumber = mp->map_obj.rnumber;
+ /*
+ * get ALL "reg" properties for dip, select the one of
+ * of interest. In x86, "assigned-addresses" property
+ * is identical to the "reg" property, so there is no
+ * need to cross check the two to determine the physical
+ * address of the registers.
+ * This routine still performs some validity checks to
+ * make sure that everything is okay.
+ */
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
+ DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
+ (uint_t *)&length) != DDI_PROP_SUCCESS)
+ return (DDI_FAILURE);
+
+ /*
+ * validate the register number.
+ */
+ length /= (sizeof (pci_regspec_t) / sizeof (int));
+ if (rnumber >= length) {
+ ddi_prop_free(pci_rp);
+ return (DDI_FAILURE);
+ }
+
+ /*
+ * copy the required entry.
+ */
+ pci_reg = pci_rp[rnumber];
+
+ /*
+ * free the memory allocated by ddi_prop_lookup_int_array
+ */
+ ddi_prop_free(pci_rp);
+
+ pci_rp = &pci_reg;
+ if (pci_common_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ mp->map_type = DDI_MT_REGSPEC;
+ break;
+ default:
+ return (DDI_ME_INVAL);
+ }
+
+ space = pci_rp->pci_phys_hi & PCI_REG_ADDR_M;
+
+ /*
+ * check for unmap and unlock of address space
+ */
+ if ((mp->map_op == DDI_MO_UNMAP) || (mp->map_op == DDI_MO_UNLOCK)) {
+ /*
+ * Adjust offset and length
+ * A non-zero length means override the one in the regspec.
+ */
+ pci_rp->pci_phys_low += (uint_t)offset;
+ if (len != 0)
+ pci_rp->pci_size_low = len;
+
+ switch (space) {
+ case PCI_ADDR_IO:
+ reg.regspec_bustype = 1;
+ break;
+
+ case PCI_ADDR_CONFIG:
+ /* Just fall through */
+ case PCI_ADDR_MEM64:
+ /*
+ * MEM64 requires special treatment on map, to check
+ * that the device is below 4G. On unmap, however,
+ * we can assume that everything is OK... the map
+ * must have succeeded.
+ */
+ /* FALLTHROUGH */
+ case PCI_ADDR_MEM32:
+ reg.regspec_bustype = 0;
+ break;
+
+ default:
+ return (DDI_FAILURE);
+ }
+ reg.regspec_addr = pci_rp->pci_phys_low;
+ reg.regspec_size = pci_rp->pci_size_low;
+
+ mp->map_obj.rp = &reg;
+ return (ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp));
+
+ }
+
+ /* check for user mapping request - not legal for Config */
+ if (mp->map_op == DDI_MO_MAP_HANDLE && space == PCI_ADDR_CONFIG) {
+ cmn_err(CE_NOTE, "npe: Config mapping request from user\n");
+ return (DDI_FAILURE);
+ }
+
+
+ if (space == PCI_ADDR_CONFIG) {
+ /* Can't map config space without a handle */
+ hp = (ddi_acc_hdl_t *)mp->map_handlep;
+ if (hp == NULL)
+ return (DDI_FAILURE);
+
+ pci_rp->pci_phys_low = ddi_prop_get_int64(DDI_DEV_T_ANY,
+ rdip, 0, "ecfga-base-address", 0);
+
+ /* record the device address for future reference */
+ cfp = (pci_acc_cfblk_t *)&hp->ah_bus_private;
+ cfp->c_busnum = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
+ cfp->c_devnum = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
+ cfp->c_funcnum = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
+
+ pci_rp->pci_phys_low += ((cfp->c_busnum << 20) |
+ (cfp->c_devnum) << 15 | (cfp->c_funcnum << 12));
+
+ pci_rp->pci_size_low = PCIE_CONF_HDR_SIZE;
+ }
+
+ length = pci_rp->pci_size_low;
+
+ /*
+ * range check
+ */
+ if ((offset >= length) || (len > length) || (offset + len > length))
+ return (DDI_FAILURE);
+
+ /*
+ * Adjust offset and length
+ * A non-zero length means override the one in the regspec.
+ */
+ pci_rp->pci_phys_low += (uint_t)offset;
+ if (len != 0)
+ pci_rp->pci_size_low = len;
+
+ /*
+ * convert the pci regsec into the generic regspec used by the
+ * parent root nexus driver.
+ */
+ switch (space) {
+ case PCI_ADDR_IO:
+ reg.regspec_bustype = 1;
+ break;
+ case PCI_ADDR_CONFIG:
+ case PCI_ADDR_MEM64:
+ /*
+ * We can't handle 64-bit devices that are mapped above
+ * 4G or that are larger than 4G.
+ */
+ if (pci_rp->pci_phys_mid != 0 || pci_rp->pci_size_hi != 0)
+ return (DDI_FAILURE);
+ /*
+ * Other than that, we can treat them as 32-bit mappings
+ */
+ /* FALLTHROUGH */
+ case PCI_ADDR_MEM32:
+ reg.regspec_bustype = 0;
+ break;
+ default:
+ return (DDI_FAILURE);
+ }
+
+ reg.regspec_addr = pci_rp->pci_phys_low;
+ reg.regspec_size = pci_rp->pci_size_low;
+
+ mp->map_obj.rp = &reg;
+ return (ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp));
+}
+
+
+/*ARGSUSED*/
+static int
+npe_ctlops(dev_info_t *dip, dev_info_t *rdip,
+ ddi_ctl_enum_t ctlop, void *arg, void *result)
+{
+ int rn;
+ int totreg;
+ uint_t reglen;
+ pci_regspec_t *drv_regp;
+
+ switch (ctlop) {
+ case DDI_CTLOPS_REPORTDEV:
+ if (rdip == (dev_info_t *)0)
+ return (DDI_FAILURE);
+ cmn_err(CE_CONT, "?PCI Express-device: %s@%s, %s%d\n",
+ ddi_node_name(rdip), ddi_get_name_addr(rdip),
+ ddi_driver_name(rdip), ddi_get_instance(rdip));
+ return (DDI_SUCCESS);
+
+ case DDI_CTLOPS_INITCHILD:
+ return (npe_initchild((dev_info_t *)arg));
+
+ case DDI_CTLOPS_UNINITCHILD:
+ return (npe_removechild((dev_info_t *)arg));
+
+ case DDI_CTLOPS_SIDDEV:
+ return (DDI_SUCCESS);
+
+ case DDI_CTLOPS_REGSIZE:
+ case DDI_CTLOPS_NREGS:
+ if (rdip == (dev_info_t *)0)
+ return (DDI_FAILURE);
+
+ *(int *)result = 0;
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
+ DDI_PROP_DONTPASS, "reg", (int **)&drv_regp,
+ &reglen) != DDI_PROP_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+
+ totreg = (reglen * sizeof (int)) / sizeof (pci_regspec_t);
+ if (ctlop == DDI_CTLOPS_NREGS)
+ *(int *)result = totreg;
+ else if (ctlop == DDI_CTLOPS_REGSIZE) {
+ rn = *(int *)arg;
+ if (rn >= totreg) {
+ ddi_prop_free(drv_regp);
+ return (DDI_FAILURE);
+ }
+ *(off_t *)result = drv_regp[rn].pci_size_low;
+ }
+ ddi_prop_free(drv_regp);
+
+ return (DDI_SUCCESS);
+
+ case DDI_CTLOPS_POWER:
+ {
+ power_req_t *reqp = (power_req_t *)arg;
+ /*
+ * We currently understand reporting of PCI_PM_IDLESPEED
+ * capability. Everything else is passed up.
+ */
+ if ((reqp->request_type == PMR_REPORT_PMCAP) &&
+ (reqp->req.report_pmcap_req.cap == PCI_PM_IDLESPEED))
+ return (DDI_SUCCESS);
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return (ddi_ctlops(dip, rdip, ctlop, arg, result));
+
+}
+
+
+/*
+ * npe_intr_ops
+ */
+static int
+npe_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
+ ddi_intr_handle_impl_t *hdlp, void *result)
+{
+ return (pci_common_intr_ops(pdip, rdip, intr_op, hdlp, result));
+}
+
+
+static int
+npe_initchild(dev_info_t *child)
+{
+ char name[80];
+
+ if (pci_common_name_child(child, name, 80) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ ddi_set_name_addr(child, name);
+
+ /*
+ * Pseudo nodes indicate a prototype node with per-instance
+ * properties to be merged into the real h/w device node.
+ * The interpretation of the unit-address is DD[,F]
+ * where DD is the device id and F is the function.
+ */
+ if (ndi_dev_is_persistent_node(child) == 0) {
+ extern int pci_allow_pseudo_children;
+
+ ddi_set_parent_data(child, NULL);
+
+ /*
+ * Try to merge the properties from this prototype
+ * node into real h/w nodes.
+ */
+ if (ndi_merge_node(child, pci_common_name_child) ==
+ DDI_SUCCESS) {
+ /*
+ * Merged ok - return failure to remove the node.
+ */
+ ddi_set_name_addr(child, NULL);
+ return (DDI_FAILURE);
+ }
+
+ /* workaround for DDIVS to run under PCI Express */
+ if (pci_allow_pseudo_children) {
+ /*
+ * If the "interrupts" property doesn't exist,
+ * this must be the ddivs no-intr case, and it returns
+ * DDI_SUCCESS instead of DDI_FAILURE.
+ */
+ if (ddi_prop_get_int(DDI_DEV_T_ANY, child,
+ DDI_PROP_DONTPASS, "interrupts", -1) == -1)
+ return (DDI_SUCCESS);
+ /*
+ * Create the ddi_parent_private_data for a pseudo
+ * child.
+ */
+ pci_common_set_parent_private_data(child);
+ return (DDI_SUCCESS);
+ }
+
+ /*
+ * The child was not merged into a h/w node,
+ * but there's not much we can do with it other
+ * than return failure to cause the node to be removed.
+ */
+ cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
+ ddi_get_name(child), ddi_get_name_addr(child),
+ ddi_get_name(child));
+ ddi_set_name_addr(child, NULL);
+ return (DDI_NOT_WELL_FORMED);
+ }
+
+ if (ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS,
+ "interrupts", -1) != -1)
+ pci_common_set_parent_private_data(child);
+ else
+ ddi_set_parent_data(child, NULL);
+
+ /*
+ * Enable AER next pointer being displayed
+ */
+ npe_ck804_fix_aer_ptr(child);
+
+ (void) pcie_error_init(child);
+
+ return (DDI_SUCCESS);
+}
+
+
+static int
+npe_removechild(dev_info_t *dip)
+{
+ struct ddi_parent_private_data *pdptr;
+
+ /*
+ * Do it way early.
+ * Otherwise ddi_map() call form pcie_error_fini crashes
+ */
+ pcie_error_fini(dip);
+
+ if ((pdptr = ddi_get_parent_data(dip)) != NULL) {
+ kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec)));
+ ddi_set_parent_data(dip, NULL);
+ }
+ ddi_set_name_addr(dip, NULL);
+
+ /*
+ * Strip the node to properly convert it back to prototype form
+ */
+ ddi_remove_minor_node(dip, NULL);
+
+ ddi_prop_remove_all(dip);
+
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * When retrofitting this module for pci_tools, functions such as open, close,
+ * and ioctl are now pulled into this module. Before this, the functions in
+ * the pcihp module were referenced directly. Now they are called or
+ * referenced through the pcihp cb_ops structure from functions in this module.
+ */
+static int
+npe_open(dev_t *devp, int flags, int otyp, cred_t *credp)
+{
+ return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp));
+}
+
+static int
+npe_close(dev_t dev, int flags, int otyp, cred_t *credp)
+{
+ return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp));
+}
+
+static int
+npe_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
+{
+ minor_t minor = getminor(dev);
+ int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor);
+ pci_state_t *pci_p = ddi_get_soft_state(npe_statep, instance);
+ dev_info_t *dip;
+
+ if (pci_p == NULL)
+ return (ENXIO);
+
+ dip = pci_p->pci_dip;
+ return (pci_common_ioctl(dip, dev, cmd, arg, mode, credp, rvalp));
+}
+
+static int
+npe_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
+ int flags, char *name, caddr_t valuep, int *lengthp)
+{
+ return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags,
+ name, valuep, lengthp));
+}
+
+static int
+npe_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
+{
+ return (pcihp_info(dip, cmd, arg, result));
+}
diff --git a/usr/src/uts/i86pc/io/pciex/npe_misc.c b/usr/src/uts/i86pc/io/pciex/npe_misc.c
new file mode 100644
index 0000000000..c84e50478e
--- /dev/null
+++ b/usr/src/uts/i86pc/io/pciex/npe_misc.c
@@ -0,0 +1,131 @@
+/*
+ * 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"
+
+/*
+ * Library file that has miscellaneous support for npe(7d)
+ */
+
+#include <sys/conf.h>
+#include <sys/pci.h>
+#include <sys/sunndi.h>
+#include <sys/acpi/acpi.h>
+#include <sys/acpi/acpi_pci.h>
+#include <sys/acpica.h>
+#include <io/pciex/pcie_ck804_boot.h>
+
+/*
+ * Prototype declaration
+ */
+void npe_query_acpi_mcfg(dev_info_t *dip);
+void npe_ck804_fix_aer_ptr(dev_info_t *child);
+
+/*
+ * Default ecfga base address
+ */
+int64_t npe_default_ecfga_base = 0xE0000000;
+
+/*
+ * Query the MCFG table using ACPI. If MCFG is found, setup the
+ * 'ecfga-base-address' (Enhanced Configuration Access base address)
+ * property accordingly. Otherwise, set the value of the property
+ * to the default value.
+ */
+void
+npe_query_acpi_mcfg(dev_info_t *dip)
+{
+ MCFG_TABLE *mcfgp;
+ CFG_BASE_ADDR_ALLOC *cfg_baap;
+ char *cfg_baa_endp;
+ uint64_t ecfga_base;
+
+ /* Query the MCFG table using ACPI */
+ if (AcpiGetFirmwareTable(MCFG_SIG, 1, ACPI_LOGICAL_ADDRESSING,
+ (ACPI_TABLE_HEADER **)&mcfgp) == AE_OK) {
+
+ cfg_baap = (CFG_BASE_ADDR_ALLOC *)mcfgp->CfgBaseAddrAllocList;
+ cfg_baa_endp = ((char *)mcfgp) + mcfgp->Length;
+
+ while ((char *)cfg_baap < cfg_baa_endp) {
+ ecfga_base = ACPI_GET_ADDRESS(cfg_baap->base_addr);
+ if (ecfga_base != (uint64_t)0) {
+ /*
+ * Setup the 'ecfga-base-address' property to
+ * the base_addr found in the MCFG and return.
+ */
+ (void) ndi_prop_update_int64(DDI_DEV_T_NONE,
+ dip, "ecfga-base-address", ecfga_base);
+ return;
+ }
+ cfg_baap++;
+ }
+ }
+ /*
+ * If MCFG is not found or ecfga_base is not found in MCFG table,
+ * set the 'ecfga-base-address' property to the default value.
+ */
+ (void) ndi_prop_update_int64(DDI_DEV_T_NONE, dip,
+ "ecfga-base-address", npe_default_ecfga_base);
+}
+
+
+/*
+ * Enable reporting of AER capability next pointer.
+ * This needs to be done only for CK8-04 devices
+ * by setting NV_XVR_VEND_CYA1 (offset 0xf40) bit 13
+ * NOTE: BIOS is disabling this, it needs to be enabled temporarily
+ */
+void
+npe_ck804_fix_aer_ptr(dev_info_t *child)
+{
+ ushort_t vid, did, cya1;
+ ddi_acc_handle_t config_handle;
+
+ if (pci_config_setup(child, &config_handle) != DDI_SUCCESS)
+ return;
+
+ vid = pci_config_get16(config_handle, PCI_CONF_VENID);
+ if (vid != NVIDIA_CK804_VENDOR_ID) {
+ pci_config_teardown(&config_handle);
+ return;
+ }
+
+ did = pci_config_get16(config_handle, PCI_CONF_DEVID);
+ if (did != NVIDIA_CK804_DEVICE_ID) {
+ pci_config_teardown(&config_handle);
+ return;
+ }
+
+ cya1 = pci_config_get16(config_handle, NVIDIA_CK804_VEND_CYA1_OFF);
+ if (!(cya1 & ~NVIDIA_CK804_VEND_CYA1_ERPT_MASK))
+ (void) pci_config_put16(config_handle,
+ NVIDIA_CK804_VEND_CYA1_OFF,
+ cya1 | NVIDIA_CK804_VEND_CYA1_ERPT_VAL);
+
+ pci_config_teardown(&config_handle);
+}
diff --git a/usr/src/uts/i86pc/io/pciex/pcie_ck804_boot.c b/usr/src/uts/i86pc/io/pciex/pcie_ck804_boot.c
new file mode 100644
index 0000000000..32b9c89dfe
--- /dev/null
+++ b/usr/src/uts/i86pc/io/pciex/pcie_ck804_boot.c
@@ -0,0 +1,224 @@
+/*
+ * 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"
+
+/*
+ * Library file that has code for PCIe booting
+ */
+
+#include <sys/conf.h>
+#include <sys/pci.h>
+#include <sys/sunndi.h>
+#include <sys/pcie.h>
+#include <sys/pci_cfgspace.h>
+#include <io/pciex/pcie_ck804_boot.h>
+
+/*
+ * PCI Configuration (ck804, PCIe) related library functions
+ */
+static boolean_t look_for_any_pciex_device(uchar_t);
+
+/* Globals */
+extern int pci_boot_debug;
+
+boolean_t
+check_if_device_is_pciex(uchar_t bus, uchar_t dev, uchar_t func,
+ ushort_t *slot_number, ushort_t *is_pci_bridge)
+{
+ ushort_t cap;
+ ushort_t capsp;
+ ushort_t cap_count = PCI_CAP_MAX_PTR;
+ ushort_t status;
+ uint32_t slot_cap;
+
+ *slot_number = 0;
+
+ status = (*pci_getw_func)(bus, dev, func, PCI_CONF_STAT);
+ if (!(status & PCI_STAT_CAP))
+ return (B_FALSE);
+
+ capsp = (*pci_getb_func)(bus, dev, func, PCI_CONF_CAP_PTR);
+ while (cap_count-- && capsp >= PCI_CAP_PTR_OFF) {
+ capsp &= PCI_CAP_PTR_MASK;
+ cap = (*pci_getb_func)(bus, dev, func, capsp);
+
+ if (cap == PCI_CAP_ID_PCI_E) {
+#ifdef DEBUG
+ if (pci_boot_debug)
+ cmn_err(CE_CONT, "PCI-Express (%x,%x,%x) "
+ "capability found\n", bus, dev, func);
+#endif /* DEBUG */
+
+ status = (*pci_getw_func)(bus, dev, func, capsp + 2);
+ /*
+ * See section 7.8.2 of PCI-Express Base Spec v1.0a
+ * for Device/Port Type.
+ * PCIE_PCIECAP_DEV_TYPE_PCIE2PCI implies that the
+ * device is a PCIe2PCI bridge
+ */
+ *is_pci_bridge =
+ (status & PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) ? 1 : 0;
+
+ /*
+ * Check for "Slot Implemented" bit
+ * PCIE_PCIECAP_SLOT_IMPL implies that
+ */
+ if (status & PCIE_PCIECAP_SLOT_IMPL) {
+ /* offset 14h is Slot Cap Register */
+ slot_cap = (*pci_getl_func)(bus, dev, func,
+ capsp + PCIE_SLOTCAP);
+ *slot_number =
+ PCIE_SLOTCAP_PHY_SLOT_NUM(slot_cap);
+ }
+ return (B_TRUE);
+ }
+ capsp = (*pci_getb_func)(bus, dev, func,
+ capsp + PCI_CAP_NEXT_PTR);
+ }
+ return (B_FALSE);
+}
+
+
+/*
+ * scan all buses, devices, functions to look for any
+ * PCI-Express device in the system.
+ * If found, return B_TRUE else B_FALSE
+ */
+static boolean_t
+look_for_any_pciex_device(uchar_t bus)
+{
+ uchar_t dev, func;
+ uchar_t nfunc, header;
+ ushort_t venid, slot_num, is_pci_bridge = 0;
+
+ for (dev = 0; dev < 32; dev++) {
+ nfunc = 1;
+ for (func = 0; func < nfunc; func++) {
+#ifdef DEBUG
+ if (pci_boot_debug)
+ cmn_err(CE_NOTE, "pciex dev 0x%x, func 0x%x",
+ dev, func);
+#endif /* DEBUG */
+
+ venid = (*pci_getw_func)(bus, dev, func,
+ PCI_CONF_VENID);
+ /* no function at this address */
+ if ((venid == 0xffff) || (venid == 0))
+ continue;
+
+ header = (*pci_getb_func)(bus, dev, func,
+ PCI_CONF_HEADER);
+ if (header == 0xff)
+ continue; /* illegal value */
+
+ /*
+ * according to some mail from Microsoft posted to
+ * the pci-drivers alias, their only requirement for
+ * a multifunction device is for the 1st function to
+ * have to PCI_HEADER_MULTI bit set.
+ */
+ if ((func == 0) && (header & PCI_HEADER_MULTI))
+ nfunc = 8;
+
+ if (check_if_device_is_pciex(bus, dev, func,
+ &slot_num, &is_pci_bridge) == B_TRUE)
+ return (B_TRUE);
+ } /* end of func */
+ } /* end of dev */
+
+ return (B_FALSE);
+}
+
+
+boolean_t
+create_pcie_root_bus(uchar_t bus, dev_info_t *dip)
+{
+ /*
+ * Currently this is being hard-coded.
+ * We need to figure out if the root bus does indeed
+ * have PCI-Ex in the path by looking for MCFG in
+ * the ACPI tables
+ */
+ if (look_for_any_pciex_device(bus) == B_FALSE)
+ return (B_FALSE);
+
+#ifdef DEBUG
+ if (pci_boot_debug)
+ cmn_err(CE_CONT, "Found PCI-Ex in the system\n");
+#endif /* DEBUG */
+ (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
+ "device_type", "pciex");
+ (void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
+ "compatible", "pciex_root_complex");
+
+ return (B_TRUE);
+}
+
+
+/*
+ * Only for Nvidia's CrushK 8-04 chipsets:
+ * To enable hotplug; we need to map in two I/O BARs
+ * from ISA bridge's config space
+ */
+void
+add_ck804_isa_bridge_props(dev_info_t *dip, uchar_t bus, uchar_t dev,
+ uchar_t func)
+{
+ uint_t devloc, base;
+ pci_regspec_t regs[2] = {{0}};
+ pci_regspec_t assigned[2] = {{0}};
+
+ devloc = (uint_t)bus << PCI_REG_BUS_SHIFT |
+ (uint_t)dev << PCI_REG_DEV_SHIFT |
+ (uint_t)func << PCI_REG_FUNC_SHIFT;
+ regs[0].pci_phys_hi = devloc;
+
+ /* System Control BAR i/o space */
+ base = (*pci_getl_func)(bus, dev, func,
+ NVIDIA_CK804_ISA_SYSCTRL_BAR_OFF);
+ regs[0].pci_size_low = assigned[0].pci_size_low = PCI_CONF_HDR_SIZE;
+ assigned[0].pci_phys_hi = regs[0].pci_phys_hi = (PCI_RELOCAT_B |
+ PCI_ADDR_IO | devloc | NVIDIA_CK804_ISA_SYSCTRL_BAR_OFF);
+ assigned[0].pci_phys_low = regs[0].pci_phys_low =
+ base & PCI_BASE_IO_ADDR_M;
+
+ /* Analog BAR i/o space */
+ base = (*pci_getl_func)(bus, dev, func,
+ NVIDIA_CK804_ISA_ANALOG_BAR_OFF);
+ regs[1].pci_size_low = assigned[1].pci_size_low = PCI_CONF_HDR_SIZE;
+ assigned[1].pci_phys_hi = regs[1].pci_phys_hi = (PCI_RELOCAT_B |
+ PCI_ADDR_IO | devloc | NVIDIA_CK804_ISA_ANALOG_BAR_OFF);
+ assigned[1].pci_phys_low = regs[1].pci_phys_low =
+ base & PCI_BASE_IO_ADDR_M;
+
+ (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "reg",
+ (int *)regs, 2 * sizeof (pci_regspec_t) / sizeof (int));
+ (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip,
+ "assigned-addresses",
+ (int *)assigned, 2 * sizeof (pci_regspec_t) / sizeof (int));
+}
diff --git a/usr/src/uts/i86pc/io/pciex/pcie_ck804_boot.h b/usr/src/uts/i86pc/io/pciex/pcie_ck804_boot.h
new file mode 100644
index 0000000000..c2b9bf8632
--- /dev/null
+++ b/usr/src/uts/i86pc/io/pciex/pcie_ck804_boot.h
@@ -0,0 +1,75 @@
+/*
+ * 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 _PCIEX_PCI_CK804_H
+#define _PCIEX_PCI_CK804_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * PCI Configuration (ck804, PCIe) related library functions
+ */
+boolean_t check_if_device_is_pciex(uchar_t, uchar_t, uchar_t,
+ ushort_t *, ushort_t *);
+boolean_t create_pcie_root_bus(uchar_t, dev_info_t *);
+void add_ck804_isa_bridge_props(dev_info_t *, uchar_t, uchar_t,
+ uchar_t);
+
+/*
+ * Only for Nvidia's CrushK 8-04 chipsets:
+ * To enable hotplug; we need to map in two I/O BARs
+ * from ISA bridge's config space
+ */
+#define NVIDIA_CK804_VENDOR_ID 0x10de /* Nvidia ck8-04 vid */
+#define NVIDIA_CK804_DEVICE_ID 0x5d /* ck8-04 dev id */
+#define NVIDIA_CK804_PRO_ISA_BRIDGE_DEVID 0x51 /* LPC Bridge */
+#define NVIDIA_CK804_SLAVE_ISA_BRIDGE_DEVID 0xd3 /* Slave LPC Bridge */
+#define NVIDIA_CK804_ISA_SYSCTRL_BAR_OFF 0x64 /* System Control BAR */
+#define NVIDIA_CK804_ISA_ANALOG_BAR_OFF 0x68 /* Analog BAR */
+
+/* NV_XVR_VEND_CYA1 related defines */
+#define NVIDIA_CK804_VEND_CYA1_OFF 0xf40 /* NV_XVR_VEND_CYA1 */
+#define NVIDIA_CK804_VEND_CYA1_ERPT_VAL 0x2000 /* enable CYA1 ERPT */
+#define NVIDIA_CK804_VEND_CYA1_ERPT_MASK 0xdfff /* CYA1 ERPT mask */
+
+/*
+ * Check if the given device is a Nvidia's LPC bridge
+ */
+#define NVIDIA_IS_LPC_BRIDGE(vid, did) \
+ (((vid) == NVIDIA_CK804_VENDOR_ID) && \
+ (((did) == NVIDIA_CK804_PRO_ISA_BRIDGE_DEVID) || \
+ ((did) == NVIDIA_CK804_SLAVE_ISA_BRIDGE_DEVID)))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PCIEX_PCI_CK804_H */
diff --git a/usr/src/uts/i86pc/io/pciex/pcie_error.c b/usr/src/uts/i86pc/io/pciex/pcie_error.c
new file mode 100644
index 0000000000..256f9b365b
--- /dev/null
+++ b/usr/src/uts/i86pc/io/pciex/pcie_error.c
@@ -0,0 +1,550 @@
+/*
+ * 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"
+
+/*
+ * Library file that has code for PCIe error handling
+ */
+
+#include <sys/conf.h>
+#include <sys/sysmacros.h>
+#include <sys/kmem.h>
+#include <sys/modctl.h>
+#include <sys/pci.h>
+#include <sys/pci_impl.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/sysmacros.h>
+#include <sys/pcie.h>
+#include <sys/pcie_impl.h>
+#include <sys/promif.h>
+#include <io/pciex/pcie_error.h>
+#include <io/pciex/pcie_ck804_boot.h>
+
+
+#ifdef DEBUG
+uint_t pcie_error_debug_flags = 0;
+#define PCIE_ERROR_DBG pcie_error_dbg
+
+static void pcie_error_dbg(char *fmt, ...);
+#else /* DEBUG */
+#define PCIE_ERROR_DBG 0 &&
+#endif /* DEBUG */
+
+/* Variables to control error settings */
+
+
+/* Device Command Register */
+ushort_t pcie_command_default = \
+ PCI_COMM_SERR_ENABLE | \
+ PCI_COMM_WAIT_CYC_ENAB | \
+ PCI_COMM_PARITY_DETECT | \
+ PCI_COMM_ME | \
+ PCI_COMM_MAE | \
+ PCI_COMM_IO;
+
+/* PCI-Express Device Control Register */
+ushort_t pcie_device_ctrl_default = \
+ PCIE_DEVCTL_CE_REPORTING_EN | \
+ PCIE_DEVCTL_NFE_REPORTING_EN | \
+ PCIE_DEVCTL_FE_REPORTING_EN | \
+ PCIE_DEVCTL_RO_EN;
+
+/* PCI-Express AER Root Control Register */
+ushort_t pcie_root_ctrl_default = \
+ PCIE_ROOTCTL_SYS_ERR_ON_CE_EN | \
+ PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN | \
+ PCIE_ROOTCTL_SYS_ERR_ON_FE_EN;
+
+/* PCI-Express Root Error Command Register */
+ushort_t pcie_root_error_cmd_default = \
+ PCIE_AER_RE_CMD_CE_REP_EN | \
+ PCIE_AER_RE_CMD_NFE_REP_EN | \
+ PCIE_AER_RE_CMD_FE_REP_EN;
+
+/*
+ * PCI-Express related masks (AER only)
+ * Can be defined to mask off certain types of AER errors
+ * By default all are set to 0; as no errors are masked
+ */
+uint32_t pcie_aer_uce_mask = 0; /* Uncorrectable errors mask */
+uint32_t pcie_aer_ce_mask = 0; /* Correctable errors mask */
+uint32_t pcie_aer_suce_mask = 0; /* Secondary UNCOR error mask */
+
+/*
+ * By default, error handling is enabled
+ * Enable error handling flags. There are two flags
+ * pcie_error_disable_flag : disable AER, Baseline error handling, SERR
+ * default value = 0 (do not disable error handling)
+ * 1 (disable all error handling)
+ *
+ * pcie_serr_disable_flag : disable SERR only (in RCR and command reg)
+ * default value = 1 (disable SERR bits)
+ * 0 (enable SERR handling)
+ *
+ * pcie_aer_disable_flag : disable AER only
+ * default value = 1 (disable AER bits)
+ * 0 (enable AER handling)
+ *
+ * NOTE: pci_serr_disable_flag is a subset of pcie_error_disable_flag
+ * If pcie_error_disable_flag is set; then pcie_serr_disable_flag is ignored
+ * Above is also true for pcie_aer_disable_flag
+ */
+uint32_t pcie_error_disable_flag = 0;
+uint32_t pcie_serr_disable_flag = 1;
+uint32_t pcie_aer_disable_flag = 1;
+
+/*
+ * Function prototypes
+ */
+static void pcie_error_clear_errors(ddi_acc_handle_t, uint16_t,
+ uint16_t, uint16_t);
+static uint16_t pcie_error_find_cap_reg(ddi_acc_handle_t, uint8_t);
+static uint16_t pcie_error_find_ext_cap_reg(ddi_acc_handle_t, uint16_t);
+static void pcie_ck804_error_init(dev_info_t *, ddi_acc_handle_t,
+ uint16_t, uint16_t);
+static void pcie_ck804_error_fini(ddi_acc_handle_t, uint16_t, uint16_t);
+
+/*
+ * PCI-Express error initialization.
+ */
+int
+pcie_error_init(dev_info_t *cdip)
+{
+ ddi_acc_handle_t cfg_hdl;
+ uint8_t header_type;
+ uint8_t bcr;
+ uint16_t command_reg, status_reg;
+ uint16_t cap_ptr, aer_ptr;
+ uint16_t device_ctl;
+ uint16_t dev_type;
+ uint32_t aer_reg;
+
+ /*
+ * flag to turn this off
+ */
+ if (pcie_error_disable_flag)
+ return (DDI_SUCCESS);
+
+ if (pci_config_setup(cdip, &cfg_hdl) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ /* Determine the configuration header type */
+ header_type = pci_config_get8(cfg_hdl, PCI_CONF_HEADER);
+ PCIE_ERROR_DBG("%s: header_type=%x\n",
+ ddi_driver_name(cdip), header_type);
+
+ /* Setup the device's command register */
+ status_reg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
+ pci_config_put16(cfg_hdl, PCI_CONF_STAT, status_reg);
+ command_reg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
+ if (pcie_serr_disable_flag) {
+ pcie_command_default &= ~PCI_COMM_SERR_ENABLE;
+ /* shouldn't happen; just in case */
+ if (command_reg & PCI_COMM_SERR_ENABLE)
+ command_reg &= ~PCI_COMM_SERR_ENABLE;
+ }
+ command_reg |= pcie_command_default;
+ pci_config_put16(cfg_hdl, PCI_CONF_COMM, command_reg);
+
+ PCIE_ERROR_DBG("%s: command=%x\n", ddi_driver_name(cdip),
+ pci_config_get16(cfg_hdl, PCI_CONF_COMM));
+
+ /*
+ * If the device has a bus control register then program it
+ * based on the settings in the command register.
+ */
+ if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) {
+ status_reg = pci_config_get16(cfg_hdl, PCI_BCNF_SEC_STATUS);
+ pci_config_put16(cfg_hdl, PCI_BCNF_SEC_STATUS, status_reg);
+ bcr = pci_config_get8(cfg_hdl, PCI_BCNF_BCNTRL);
+ if (pcie_command_default & PCI_COMM_PARITY_DETECT)
+ bcr |= PCI_BCNF_BCNTRL_PARITY_ENABLE;
+ if (pcie_command_default & PCI_COMM_SERR_ENABLE) {
+ if (pcie_serr_disable_flag)
+ bcr &= ~PCI_BCNF_BCNTRL_SERR_ENABLE;
+ else
+ bcr |= PCI_BCNF_BCNTRL_SERR_ENABLE;
+ }
+ bcr |= PCI_BCNF_BCNTRL_MAST_AB_MODE;
+ pci_config_put8(cfg_hdl, PCI_BCNF_BCNTRL, bcr);
+ }
+
+ /* Look for PCIe capability */
+ cap_ptr = pcie_error_find_cap_reg(cfg_hdl, PCI_CAP_ID_PCI_E);
+ if (cap_ptr != PCI_CAP_NEXT_PTR_NULL) { /* PCIe found */
+ aer_ptr = pcie_error_find_ext_cap_reg(cfg_hdl,
+ PCIE_EXT_CAP_ID_AER);
+ dev_type = pci_config_get16(cfg_hdl, cap_ptr +
+ PCIE_PCIECAP) & PCIE_PCIECAP_DEV_TYPE_MASK;
+ }
+
+ /*
+ * Clear any pending errors
+ */
+ pcie_error_clear_errors(cfg_hdl, cap_ptr, aer_ptr, dev_type);
+
+ /* No PCIe; just return */
+ if (cap_ptr == PCI_CAP_NEXT_PTR_NULL)
+ goto cleanup;
+
+ /*
+ * Only enable these set of errors for CK8-04/IO-4 devices
+ */
+ if ((pci_config_get16(cfg_hdl, PCI_CONF_VENID) ==
+ NVIDIA_CK804_VENDOR_ID) &&
+ (pci_config_get16(cfg_hdl, PCI_CONF_DEVID) ==
+ NVIDIA_CK804_DEVICE_ID))
+ pcie_ck804_error_init(cdip, cfg_hdl, cap_ptr, aer_ptr);
+
+ /*
+ * Enable PCI-Express Baseline Error Handling
+ *
+ * NOTE: Unsupported Request related errors are not enabled
+ * If these are enabled; then as SERR is already set the
+ * ck8-04/io-4 based machines end up with an MCE
+ * Programs that scan PCI configuration space can easily
+ * generate URs as they scan entire BDFs..
+ */
+ device_ctl = pci_config_get16(cfg_hdl, cap_ptr + PCIE_DEVCTL);
+ pci_config_put16(cfg_hdl, cap_ptr + PCIE_DEVCTL,
+ pcie_device_ctrl_default | device_ctl);
+
+ PCIE_ERROR_DBG("%s: device control=0x%x->0x%x\n",
+ ddi_driver_name(cdip), device_ctl,
+ pci_config_get16(cfg_hdl, cap_ptr + PCIE_DEVCTL));
+
+ /*
+ * Enable PCI-Express Advanced Error Handling if Exists
+ */
+ if (aer_ptr == PCIE_EXT_CAP_NEXT_PTR_NULL)
+ goto cleanup;
+
+
+ if (pcie_aer_disable_flag)
+ goto cleanup;
+
+ /* Enable Uncorrectable errors */
+ aer_reg = pci_config_get32(cfg_hdl, aer_ptr + PCIE_AER_UCE_MASK);
+ pci_config_put32(cfg_hdl, aer_ptr + PCIE_AER_UCE_MASK,
+ aer_reg | pcie_aer_uce_mask);
+ PCIE_ERROR_DBG("%s: AER UCE=0x%x->0x%x\n", ddi_driver_name(cdip),
+ aer_reg, pci_config_get32(cfg_hdl, aer_ptr + PCIE_AER_UCE_MASK));
+
+ /* Enable Correctable errors */
+ aer_reg = pci_config_get32(cfg_hdl, aer_ptr + PCIE_AER_CE_MASK);
+ pci_config_put32(cfg_hdl, aer_ptr + PCIE_AER_CE_MASK,
+ aer_reg | pcie_aer_ce_mask);
+ PCIE_ERROR_DBG("%s: AER CE=0x%x->0x%x\n", ddi_driver_name(cdip),
+ aer_reg, pci_config_get32(cfg_hdl, aer_ptr + PCIE_AER_CE_MASK));
+
+ /*
+ * Enable Secondary Uncorrectable errors if this is a bridge
+ */
+ if (!(dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI))
+ goto cleanup;
+
+ /*
+ * Enable secondary bus errors
+ */
+ aer_reg = pci_config_get32(cfg_hdl, aer_ptr + PCIE_AER_SUCE_MASK);
+ pci_config_put32(cfg_hdl, aer_ptr + PCIE_AER_SUCE_MASK,
+ aer_reg | pcie_aer_suce_mask);
+ PCIE_ERROR_DBG("%s: AER SUCE=0x%x->0x%x\n",
+ ddi_driver_name(cdip), aer_reg,
+ pci_config_get32(cfg_hdl, aer_ptr + PCIE_AER_SUCE_MASK));
+
+cleanup:
+ pci_config_teardown(&cfg_hdl);
+ return (DDI_SUCCESS);
+}
+
+
+static void
+pcie_ck804_error_init(dev_info_t *child, ddi_acc_handle_t cfg_hdl,
+ uint16_t cap_ptr, uint16_t aer_ptr)
+{
+ uint16_t rc_ctl;
+
+ if (!pcie_serr_disable_flag) {
+ rc_ctl = pci_config_get16(cfg_hdl, cap_ptr + PCIE_ROOTCTL);
+ pci_config_put16(cfg_hdl, cap_ptr + PCIE_ROOTCTL,
+ rc_ctl | pcie_root_ctrl_default);
+ PCIE_ERROR_DBG("%s: PCIe Root Control Register=0x%x->0x%x\n",
+ ddi_driver_name(child), rc_ctl,
+ pci_config_get16(cfg_hdl, cap_ptr + PCIE_ROOTCTL));
+ }
+
+ /* Root Error Command Register */
+ rc_ctl = pci_config_get16(cfg_hdl, aer_ptr + PCIE_AER_RE_CMD);
+ if (!pcie_aer_disable_flag)
+ pci_config_put16(cfg_hdl, aer_ptr + PCIE_AER_RE_CMD,
+ rc_ctl | pcie_root_error_cmd_default);
+ PCIE_ERROR_DBG("%s: PCIe AER RootError Command Register=0x%x->0x%x\n",
+ ddi_driver_name(child), rc_ctl,
+ pci_config_get16(cfg_hdl, aer_ptr + PCIE_AER_RE_CMD));
+}
+
+
+/*
+ * PCI-Express CK8-04 child device de-initialization.
+ * This function disables generic pci-express interrupts and error handling.
+ */
+void
+pcie_error_fini(dev_info_t *cdip)
+{
+ ddi_acc_handle_t cfg_hdl;
+ uint16_t cap_ptr, aer_ptr;
+ uint16_t dev_type;
+ uint8_t header_type;
+ uint8_t bcr;
+ uint16_t command_reg, status_reg;
+
+ if (pcie_error_disable_flag)
+ return;
+
+ if (pci_config_setup(cdip, &cfg_hdl) != DDI_SUCCESS)
+ return;
+
+ /* Determine the configuration header type */
+ header_type = pci_config_get8(cfg_hdl, PCI_CONF_HEADER);
+ status_reg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
+ pci_config_put16(cfg_hdl, PCI_CONF_STAT, status_reg);
+
+ /* Clear the device's command register (SERR and PARITY detect) */
+ command_reg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
+ if (pcie_serr_disable_flag || (command_reg & PCI_COMM_SERR_ENABLE))
+ command_reg &= ~PCI_COMM_SERR_ENABLE;
+ command_reg &= ~PCI_COMM_PARITY_DETECT;
+ pci_config_put16(cfg_hdl, PCI_CONF_COMM, command_reg);
+
+ /*
+ * If the device has a bus control register then clear
+ * SERR, Master Abort and Parity detect
+ */
+ if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) {
+ status_reg = pci_config_get16(cfg_hdl, PCI_BCNF_SEC_STATUS);
+ pci_config_put16(cfg_hdl, PCI_BCNF_SEC_STATUS, status_reg);
+ bcr = pci_config_get8(cfg_hdl, PCI_BCNF_BCNTRL);
+ if (pcie_command_default & PCI_COMM_PARITY_DETECT)
+ bcr &= ~PCI_BCNF_BCNTRL_PARITY_ENABLE;
+ if ((pcie_command_default & PCI_COMM_SERR_ENABLE) ||
+ (pcie_serr_disable_flag))
+ bcr &= ~PCI_BCNF_BCNTRL_SERR_ENABLE;
+ bcr &= ~PCI_BCNF_BCNTRL_MAST_AB_MODE;
+ pci_config_put8(cfg_hdl, PCI_BCNF_BCNTRL, bcr);
+ }
+
+ cap_ptr = pcie_error_find_cap_reg(cfg_hdl, PCI_CAP_ID_PCI_E);
+ if (cap_ptr == PCI_CAP_NEXT_PTR_NULL) {
+ pci_config_teardown(&cfg_hdl);
+ return;
+ }
+
+ /* Disable PCI-Express Baseline Error Handling */
+ pci_config_put16(cfg_hdl, cap_ptr + PCIE_DEVCTL, 0x0);
+
+ aer_ptr = pcie_error_find_ext_cap_reg(cfg_hdl, PCIE_EXT_CAP_ID_AER);
+
+ /*
+ * Only disable these set of errors for CK8-04/IO-4 devices
+ */
+ if ((pci_config_get16(cfg_hdl, PCI_CONF_VENID) ==
+ NVIDIA_CK804_VENDOR_ID) &&
+ (pci_config_get16(cfg_hdl, PCI_CONF_DEVID) ==
+ NVIDIA_CK804_DEVICE_ID))
+ pcie_ck804_error_fini(cfg_hdl, cap_ptr, aer_ptr);
+
+ if (aer_ptr == PCIE_EXT_CAP_NEXT_PTR_NULL) {
+ pci_config_teardown(&cfg_hdl);
+ return;
+ }
+
+ /* Disable AER bits */
+ if (!pcie_aer_disable_flag)
+ dev_type = pci_config_get16(cfg_hdl, cap_ptr + PCIE_PCIECAP) &
+ PCIE_PCIECAP_DEV_TYPE_MASK;
+
+ /* Disable Uncorrectable errors */
+ if (!pcie_aer_disable_flag)
+ pci_config_put32(cfg_hdl, aer_ptr + PCIE_AER_UCE_MASK,
+ PCIE_AER_UCE_BITS);
+
+ /* Disable Correctable errors */
+ if (!pcie_aer_disable_flag)
+ pci_config_put32(cfg_hdl,
+ aer_ptr + PCIE_AER_CE_MASK, PCIE_AER_CE_BITS);
+
+ /* Disable Secondary Uncorrectable errors if this is a bridge */
+ if (!pcie_aer_disable_flag) {
+ if (!(dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI)) {
+ pci_config_teardown(&cfg_hdl);
+ return;
+ }
+
+ /* Disable secondary bus errors */
+ pci_config_put32(cfg_hdl, aer_ptr + PCIE_AER_SUCE_MASK,
+ PCIE_AER_SUCE_BITS);
+ }
+ pci_config_teardown(&cfg_hdl);
+}
+
+
+static void
+pcie_ck804_error_fini(ddi_acc_handle_t cfg_hdl, uint16_t cap_ptr,
+ uint16_t aer_ptr)
+{
+ uint16_t rc_ctl;
+
+ if (!pcie_serr_disable_flag) {
+ rc_ctl = pci_config_get16(cfg_hdl, cap_ptr + PCIE_ROOTCTL);
+ rc_ctl &= ~pcie_root_ctrl_default;
+ pci_config_put16(cfg_hdl, cap_ptr + PCIE_ROOTCTL, rc_ctl);
+ }
+
+ /* Root Error Command Register */
+ rc_ctl = pci_config_get16(cfg_hdl, aer_ptr + PCIE_AER_RE_CMD);
+ rc_ctl &= ~pcie_root_error_cmd_default;
+ if (!pcie_aer_disable_flag)
+ pci_config_put16(cfg_hdl, aer_ptr + PCIE_AER_RE_CMD, rc_ctl);
+}
+
+/*
+ * Clear any pending errors
+ */
+static void
+pcie_error_clear_errors(ddi_acc_handle_t cfg_hdl, uint16_t cap_ptr,
+ uint16_t aer_ptr, uint16_t dev_type)
+{
+ uint16_t device_sts;
+
+ /* 1. clear the Advanced PCIe Errors */
+ if (aer_ptr != PCIE_EXT_CAP_NEXT_PTR_NULL) {
+ pci_config_put32(cfg_hdl, aer_ptr + PCIE_AER_CE_STS, -1);
+ pci_config_put32(cfg_hdl, aer_ptr + PCIE_AER_UCE_STS, -1);
+ if (dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) {
+ pci_config_put32(cfg_hdl,
+ aer_ptr + PCIE_AER_SUCE_STS, -1);
+ }
+ }
+
+ /* 2. clear the PCIe Errors */
+ device_sts = pci_config_get16(cfg_hdl, cap_ptr + PCIE_DEVSTS);
+ pci_config_put16(cfg_hdl, cap_ptr + PCIE_DEVSTS, device_sts);
+
+ /* 3. clear the Legacy PCI Errors */
+ device_sts = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
+ pci_config_put16(cfg_hdl, PCI_CONF_STAT, device_sts);
+ if (dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) {
+ device_sts = pci_config_get16(cfg_hdl, PCI_BCNF_SEC_STATUS);
+ pci_config_put16(cfg_hdl, PCI_BCNF_SEC_STATUS, device_sts);
+ }
+}
+
+
+/*
+ * Helper Function to traverse the pci-express config space looking
+ * for the pci-express capability id pointer.
+ */
+static uint16_t
+pcie_error_find_cap_reg(ddi_acc_handle_t cfg_hdl, uint8_t cap_id)
+{
+ uint16_t caps_ptr, cap;
+ ushort_t status;
+
+ /*
+ * Check if capabilities list is supported. If not then it is a PCI
+ * device.
+ */
+ status = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
+ if (status == 0xff || !((status & PCI_STAT_CAP)))
+ return (PCI_CAP_NEXT_PTR_NULL);
+
+ caps_ptr = P2ALIGN(pci_config_get8(cfg_hdl, PCI_CONF_CAP_PTR), 4);
+ while (caps_ptr != PCI_CAP_NEXT_PTR_NULL) {
+ if (caps_ptr < PCI_CAP_PTR_OFF)
+ return (PCI_CAP_NEXT_PTR_NULL);
+
+ cap = pci_config_get8(cfg_hdl, caps_ptr);
+ if (cap == cap_id) {
+ break;
+ } else if (cap == 0xff)
+ return (PCI_CAP_NEXT_PTR_NULL);
+
+ caps_ptr = P2ALIGN(pci_config_get8(cfg_hdl,
+ (caps_ptr + PCI_CAP_NEXT_PTR)), 4);
+ }
+
+ return (caps_ptr);
+}
+
+/*
+ * Helper Function to traverse the pci-express extended config space looking
+ * for the pci-express capability id pointer.
+ */
+static uint16_t
+pcie_error_find_ext_cap_reg(ddi_acc_handle_t cfg_hdl, 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(cfg_hdl, 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(cfg_hdl, 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);
+}
+
+#ifdef DEBUG
+static void
+pcie_error_dbg(char *fmt, ...)
+{
+ va_list ap;
+
+ if (!pcie_error_debug_flags)
+ return;
+
+ va_start(ap, fmt);
+ prom_vprintf(fmt, ap);
+ va_end(ap);
+}
+#endif /* DEBUG */
diff --git a/usr/src/uts/i86pc/io/pciex/pcie_error.h b/usr/src/uts/i86pc/io/pciex/pcie_error.h
new file mode 100644
index 0000000000..408cb903a2
--- /dev/null
+++ b/usr/src/uts/i86pc/io/pciex/pcie_error.h
@@ -0,0 +1,54 @@
+/*
+ * 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 _PCIEX_PCIE_ERROR_H
+#define _PCIEX_PCIE_ERROR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * PCI Error related library header file
+ */
+
+
+/*
+ * Error related library functions
+ */
+
+int pcie_error_init(dev_info_t *cdip);
+void pcie_error_fini(dev_info_t *cdip);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PCIEX_PCIE_ERROR_H */
diff --git a/usr/src/uts/i86pc/io/pciex/pcie_pci.c b/usr/src/uts/i86pc/io/pciex/pcie_pci.c
new file mode 100644
index 0000000000..2e05880740
--- /dev/null
+++ b/usr/src/uts/i86pc/io/pciex/pcie_pci.c
@@ -0,0 +1,1147 @@
+/*
+ * 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-E to PCI bus bridge nexus driver
+ */
+
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/debug.h>
+#include <sys/modctl.h>
+#include <sys/autoconf.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/pci.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/pcie.h>
+#include <sys/pcie_impl.h>
+#include <sys/hotplug/pci/pcihp.h>
+#include <sys/hotplug/pci/pciehpc.h>
+#include <io/pciex/pcie_error.h>
+#include <io/pciex/pcie_ck804_boot.h>
+
+#ifdef DEBUG
+static int pepb_debug = 0;
+#define PEPB_DEBUG(args) if (pepb_debug) cmn_err args
+#else
+#define PEPB_DEBUG(args)
+#endif
+
+/*
+ * interfaces from misc/pcie
+ */
+static int pepb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *, off_t,
+ off_t, caddr_t *);
+static int pepb_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
+ void *);
+
+struct bus_ops pepb_bus_ops = {
+ BUSO_REV,
+ pepb_bus_map,
+ 0,
+ 0,
+ 0,
+ i_ddi_map_fault,
+ ddi_dma_map,
+ ddi_dma_allochdl,
+ ddi_dma_freehdl,
+ ddi_dma_bindhdl,
+ ddi_dma_unbindhdl,
+ ddi_dma_flush,
+ ddi_dma_win,
+ ddi_dma_mctl,
+ pepb_ctlops,
+ ddi_bus_prop_op,
+ 0, /* (*bus_get_eventcookie)(); */
+ 0, /* (*bus_add_eventcall)(); */
+ 0, /* (*bus_remove_eventcall)(); */
+ 0, /* (*bus_post_event)(); */
+ 0, /* (*bus_intr_ctl)(); */
+ 0, /* (*bus_config)(); */
+ 0, /* (*bus_unconfig)(); */
+ NULL, /* (*bus_fm_init)(); */
+ NULL, /* (*bus_fm_fini)(); */
+ NULL, /* (*bus_fm_access_enter)(); */
+ NULL, /* (*bus_fm_access_exit)(); */
+ NULL, /* (*bus_power)(); */
+ i_ddi_intr_ops /* (*bus_intr_op)(); */
+};
+
+/*
+ * The goal here is to leverage off of the pcihp.c source without making
+ * changes to it. Call into it's cb_ops directly if needed.
+ */
+static int pepb_open(dev_t *, int, int, cred_t *);
+static int pepb_close(dev_t, int, int, cred_t *);
+static int pepb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+static int pepb_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *,
+ caddr_t, int *);
+static int pepb_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
+
+struct cb_ops pepb_cb_ops = {
+ pepb_open, /* open */
+ pepb_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ pepb_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ pepb_prop_op, /* cb_prop_op */
+ NULL, /* streamtab */
+ D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */
+ CB_REV, /* rev */
+ nodev, /* int (*cb_aread)() */
+ nodev /* int (*cb_awrite)() */
+};
+
+static int pepb_probe(dev_info_t *);
+static int pepb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
+static int pepb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
+
+struct dev_ops pepb_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* refcnt */
+ pepb_info, /* info */
+ nulldev, /* identify */
+ pepb_probe, /* probe */
+ pepb_attach, /* attach */
+ pepb_detach, /* detach */
+ nulldev, /* reset */
+ &pepb_cb_ops, /* driver operations */
+ &pepb_bus_ops /* bus operations */
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* Type of module */
+ "PCIe to PCI nexus driver %I%",
+ &pepb_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modldrv,
+ NULL
+};
+
+typedef enum {
+ INBAND_HPC_NONE,
+ INBAND_HPC_PCIE,
+ INBAND_HPC_SHPC
+} pepb_inband_hpc_t;
+
+static pepb_inband_hpc_t pepb_probe_inband_hpc(dev_info_t *);
+
+/*
+ * soft state pointer and structure template:
+ */
+static void *pepb_state;
+
+typedef struct {
+ dev_info_t *dip;
+
+ /*
+ * cpr support:
+ */
+#define PCI_MAX_DEVICES 32
+#define PCI_MAX_FUNCTIONS 8
+#define PCI_MAX_CHILDREN PCI_MAX_DEVICES * PCI_MAX_FUNCTIONS
+ uint_t config_state_index;
+ struct {
+ dev_info_t *dip;
+ ushort_t command;
+ uchar_t cache_line_size;
+ uchar_t latency_timer;
+ uchar_t header_type;
+ uchar_t sec_latency_timer;
+ ushort_t bridge_control;
+ } config_state[PCI_MAX_CHILDREN];
+
+ /*
+ * hot plug support
+ */
+ pepb_inband_hpc_t inband_hpc; /* inband HPC type */
+
+ /*
+ * interrupt support
+ */
+ ddi_intr_handle_t *htable; /* Intr Handlers */
+ int htable_size; /* htable size */
+ int intr_count; /* Num of Intr */
+ uint_t intr_priority; /* Intr Priority */
+ int intr_type; /* (MSI | FIXED) */
+ uint32_t soft_state; /* soft state flags */
+ kmutex_t pepb_mutex; /* Mutex for this ctrl */
+} pepb_devstate_t;
+
+/* soft state flags */
+#define PEPB_SOFT_STATE_INIT_HTABLE 0x01 /* htable kmem_alloced */
+#define PEPB_SOFT_STATE_INIT_ALLOC 0x02 /* ddi_intr_alloc called */
+#define PEPB_SOFT_STATE_INIT_HANDLER 0x04 /* ddi_intr_add_handler done */
+#define PEPB_SOFT_STATE_INIT_ENABLE 0x08 /* ddi_intr_enable called */
+#define PEPB_SOFT_STATE_INIT_BLOCK 0x10 /* ddi_intr_block_enable done */
+#define PEPB_SOFT_STATE_INIT_MUTEX 0x20 /* mutex initialized */
+
+/* default interrupt priority for all interrupts (hotplug or non-hotplug */
+#define PEPB_INTR_PRI 1
+
+/*
+ * forward function declarations:
+ */
+static void pepb_uninitchild(dev_info_t *);
+static int pepb_initchild(dev_info_t *child);
+static int pepb_create_pci_prop(dev_info_t *);
+static void pepb_save_config_regs(pepb_devstate_t *pepb_p);
+static void pepb_restore_config_regs(pepb_devstate_t *pepb_p);
+static int pepb_pcie_device_type(dev_info_t *dip, int *port_type);
+static int pepb_pcie_port_type(dev_info_t *dip,
+ ddi_acc_handle_t config_handle);
+static int pepb_get_cap(ddi_acc_handle_t config_handle, uint8_t cap_id);
+
+/* interrupt related declarations */
+static uint_t pepb_intr(caddr_t arg, caddr_t arg2);
+static int pepb_intr_init(pepb_devstate_t *pepb_p, int intr_type);
+static void pepb_intr_fini(pepb_devstate_t *pepb_p);
+
+int
+_init(void)
+{
+ int e;
+
+ if ((e = ddi_soft_state_init(&pepb_state, sizeof (pepb_devstate_t),
+ 1)) == 0 && (e = mod_install(&modlinkage)) != 0)
+ ddi_soft_state_fini(&pepb_state);
+ return (e);
+}
+
+int
+_fini(void)
+{
+ int e;
+
+ if ((e = mod_remove(&modlinkage)) == 0)
+ ddi_soft_state_fini(&pepb_state);
+ return (e);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*ARGSUSED*/
+static int
+pepb_probe(dev_info_t *devi)
+{
+ return (DDI_PROBE_SUCCESS);
+}
+
+static int
+pepb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+ int instance;
+ int port_type;
+ int intr_types;
+ char device_type[8];
+ pepb_devstate_t *pepb;
+ ddi_acc_handle_t config_handle;
+
+ switch (cmd) {
+ case DDI_RESUME:
+
+ /*
+ * Get the soft state structure for the bridge.
+ */
+ pepb = ddi_get_soft_state(pepb_state, ddi_get_instance(devi));
+ pepb_restore_config_regs(pepb);
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+
+ case DDI_ATTACH:
+ break;
+ }
+
+ /*
+ * Allocate and get soft state structure.
+ */
+ instance = ddi_get_instance(devi);
+ if (ddi_soft_state_zalloc(pepb_state, instance) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ pepb = ddi_get_soft_state(pepb_state, instance);
+ pepb->dip = devi;
+
+ /*
+ * Make sure the "device_type" property exists.
+ */
+ if (pepb_pcie_device_type(devi, &port_type) == 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", device_type);
+
+ /* probe for inband HPC */
+ pepb->inband_hpc = pepb_probe_inband_hpc(devi);
+
+ /*
+ * Initialize interrupt handlers.
+ */
+ if (ddi_intr_get_supported_types(devi, &intr_types) != DDI_SUCCESS)
+ goto next_step;
+
+ /*
+ * Only register hotplug interrupts for now.
+ * Check if device supports PCIe hotplug or not?
+ * If yes, register fixed interrupts if ILINE is valid.
+ */
+ if (pepb->inband_hpc == INBAND_HPC_PCIE) {
+ uint8_t iline;
+
+ (void) pci_config_setup(devi, &config_handle);
+ iline = pci_config_get8(config_handle, PCI_CONF_ILINE);
+ pci_config_teardown(&config_handle);
+
+ if (iline == 0 || iline > 15)
+ goto next_step;
+
+ if (pepb_intr_init(pepb, DDI_INTR_TYPE_FIXED) != DDI_SUCCESS)
+ PEPB_DEBUG((CE_WARN,
+ "%s#%d: Unable to attach INTx handler",
+ ddi_driver_name(devi), ddi_get_instance(devi)));
+ }
+
+next_step:
+ /*
+ * Initialize hotplug support on this bus. At minimum
+ * (for non hotplug bus) this would create ":devctl" minor
+ * node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls
+ * to this bus.
+ */
+ if (pcihp_init(devi) != DDI_SUCCESS)
+ cmn_err(CE_WARN, "Failed to setup hotplug framework");
+ else {
+ /*
+ * If there is an inband PCI-E HPC then initialize it.
+ * The failure is not considered fatal for the system
+ * so log the message and ignore the failure.
+ */
+ if (pepb->inband_hpc == INBAND_HPC_PCIE &&
+ pciehpc_init(devi, NULL) != DDI_SUCCESS)
+ cmn_err(CE_NOTE, "Failed to initialize inband hotplug "
+ "controller");
+ }
+
+ ddi_report_dev(devi);
+ return (DDI_SUCCESS);
+}
+
+static int
+pepb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
+{
+ pepb_devstate_t *pepb;
+
+ switch (cmd) {
+ case DDI_SUSPEND:
+ pepb = ddi_get_soft_state(pepb_state, ddi_get_instance(devi));
+ pepb_save_config_regs(pepb);
+ return (DDI_SUCCESS);
+
+ case DDI_DETACH:
+ break;
+
+ default:
+ return (DDI_FAILURE);
+ }
+
+ (void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type");
+ pepb = ddi_get_soft_state(pepb_state, ddi_get_instance(devi));
+
+ /* remove interrupt handlers */
+ pepb_intr_fini(pepb);
+
+ /* uninitialize inband PCI-E HPC if present */
+ if (pepb->inband_hpc == INBAND_HPC_PCIE)
+ (void) pciehpc_uninit(devi);
+ /*
+ * And finally free the per-pci soft state.
+ */
+ ddi_soft_state_free(pepb_state, ddi_get_instance(devi));
+
+ /*
+ * Uninitialize hotplug support on this bus.
+ */
+ (void) pcihp_uninit(devi);
+ return (DDI_SUCCESS);
+}
+
+static int
+pepb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
+ off_t offset, off_t len, caddr_t *vaddrp)
+{
+ dev_info_t *pdip;
+
+ pdip = (dev_info_t *)DEVI(dip)->devi_parent;
+ return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip, rdip, mp,
+ offset, len, vaddrp));
+}
+
+static int
+pepb_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
+ void *arg, void *result)
+{
+ pci_regspec_t *drv_regp;
+ int reglen;
+ int rn;
+ int totreg;
+
+ switch (ctlop) {
+ case DDI_CTLOPS_REPORTDEV:
+ if (rdip == (dev_info_t *)0)
+ return (DDI_FAILURE);
+ cmn_err(CE_CONT, "?PCIE-device: %s@%s, %s%d\n",
+ ddi_node_name(rdip), ddi_get_name_addr(rdip),
+ ddi_driver_name(rdip),
+ ddi_get_instance(rdip));
+ return (DDI_SUCCESS);
+
+ case DDI_CTLOPS_INITCHILD:
+ return (pepb_initchild((dev_info_t *)arg));
+
+ case DDI_CTLOPS_UNINITCHILD:
+ pepb_uninitchild((dev_info_t *)arg);
+ return (DDI_SUCCESS);
+
+ case DDI_CTLOPS_SIDDEV:
+ return (DDI_SUCCESS);
+
+ case DDI_CTLOPS_REGSIZE:
+ case DDI_CTLOPS_NREGS:
+ if (rdip == (dev_info_t *)0)
+ return (DDI_FAILURE);
+ break;
+
+ default:
+ return (ddi_ctlops(dip, rdip, ctlop, arg, result));
+ }
+
+ *(int *)result = 0;
+ if (ddi_getlongprop(DDI_DEV_T_NONE, rdip,
+ DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg", (caddr_t)&drv_regp,
+ &reglen) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ totreg = reglen / sizeof (pci_regspec_t);
+ if (ctlop == DDI_CTLOPS_NREGS)
+ *(int *)result = totreg;
+ else if (ctlop == DDI_CTLOPS_REGSIZE) {
+ rn = *(int *)arg;
+ if (rn >= totreg) {
+ kmem_free(drv_regp, reglen);
+ return (DDI_FAILURE);
+ }
+ *(off_t *)result = drv_regp[rn].pci_size_low;
+ }
+
+ kmem_free(drv_regp, reglen);
+ return (DDI_SUCCESS);
+}
+
+static int
+pepb_name_child(dev_info_t *child, char *name, int namelen)
+{
+ pci_regspec_t *pci_rp;
+ uint_t slot, func;
+ char **unit_addr;
+ uint_t n;
+
+ /*
+ * For .conf nodes, use unit-address property as name
+ */
+ if (ndi_dev_is_persistent_node(child) == 0) {
+ if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
+ DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
+ DDI_PROP_SUCCESS) {
+ cmn_err(CE_WARN,
+ "cannot find unit-address in %s.conf",
+ ddi_driver_name(child));
+ return (DDI_FAILURE);
+ }
+ if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
+ cmn_err(CE_WARN, "unit-address property in %s.conf"
+ " not well-formed", ddi_driver_name(child));
+ ddi_prop_free(unit_addr);
+ return (DDI_SUCCESS);
+ }
+ (void) snprintf(name, namelen, "%s", *unit_addr);
+ ddi_prop_free(unit_addr);
+ return (DDI_SUCCESS);
+ }
+
+ /* get child "reg" property */
+ if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
+ DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) {
+ return (DDI_FAILURE);
+ }
+
+ /* copy the device identifications */
+ slot = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
+ func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
+
+ if (func != 0)
+ (void) snprintf(name, namelen, "%x,%x", slot, func);
+ else
+ (void) snprintf(name, namelen, "%x", slot);
+
+ ddi_prop_free(pci_rp);
+ return (DDI_SUCCESS);
+}
+
+static int
+pepb_initchild(dev_info_t *child)
+{
+ struct ddi_parent_private_data *pdptr;
+ char name[MAXNAMELEN];
+ int ret;
+
+ if (pepb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ ddi_set_name_addr(child, name);
+
+ /*
+ * Pseudo nodes indicate a prototype node with per-instance
+ * properties to be merged into the real h/w device node.
+ * The interpretation of the unit-address is DD[,F]
+ * where DD is the device id and F is the function.
+ */
+ if (ndi_dev_is_persistent_node(child) == 0) {
+ extern int pci_allow_pseudo_children;
+
+ ddi_set_parent_data(child, NULL);
+
+ /*
+ * Try to merge the properties from this prototype
+ * node into real h/w nodes.
+ */
+ if (ndi_merge_node(child, pepb_name_child) != DDI_SUCCESS) {
+ /*
+ * Merged ok - return failure to remove the node.
+ */
+ ddi_set_name_addr(child, NULL);
+ return (DDI_FAILURE);
+ }
+
+ /* workaround for ddivs to run under PCI-E */
+ if (pci_allow_pseudo_children)
+ return (DDI_SUCCESS);
+
+ /*
+ * The child was not merged into a h/w node,
+ * but there's not much we can do with it other
+ * than return failure to cause the node to be removed.
+ */
+ cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
+ ddi_driver_name(child), ddi_get_name_addr(child),
+ ddi_driver_name(child));
+ ddi_set_name_addr(child, NULL);
+ return (DDI_NOT_WELL_FORMED);
+ }
+
+ if ((ret = pepb_create_pci_prop(child)) != DDI_SUCCESS)
+ return (ret);
+
+ if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS, "interrupts",
+ -1) != -1) {
+ pdptr = kmem_zalloc((sizeof (struct ddi_parent_private_data) +
+ sizeof (struct intrspec)), KM_SLEEP);
+ pdptr->par_intr = (struct intrspec *)(pdptr + 1);
+ pdptr->par_nintr = 1;
+ ddi_set_parent_data(child, pdptr);
+ } else
+ ddi_set_parent_data(child, NULL);
+
+ if (pcie_error_init(child) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ return (DDI_SUCCESS);
+}
+
+static void
+pepb_uninitchild(dev_info_t *dip)
+{
+ struct ddi_parent_private_data *pdptr;
+
+ /*
+ * Do it way early.
+ * Otherwise ddi_map() call form pcie_error_fini crashes
+ */
+ pcie_error_fini(dip);
+
+ if ((pdptr = ddi_get_parent_data(dip)) != NULL) {
+ kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec)));
+ ddi_set_parent_data(dip, NULL);
+ }
+ ddi_set_name_addr(dip, NULL);
+
+ /*
+ * Strip the node to properly convert it back to prototype form
+ */
+ ddi_remove_minor_node(dip, NULL);
+
+ ddi_prop_remove_all(dip);
+}
+
+static int
+pepb_create_pci_prop(dev_info_t *child)
+{
+ pci_regspec_t *pci_rp;
+ int length;
+ int value;
+
+ /* get child "reg" property */
+ value = ddi_getlongprop(DDI_DEV_T_NONE, child, DDI_PROP_CANSLEEP,
+ "reg", (caddr_t)&pci_rp, &length);
+ if (value != DDI_SUCCESS)
+ return (value);
+
+ (void) ndi_prop_update_byte_array(DDI_DEV_T_NONE, child, "reg",
+ (uchar_t *)pci_rp, length);
+
+ /*
+ * free the memory allocated by ddi_getlongprop ().
+ */
+ kmem_free(pci_rp, length);
+
+ /* assign the basic PCI Properties */
+
+ value = ddi_getprop(DDI_DEV_T_ANY, child, DDI_PROP_CANSLEEP,
+ "vendor-id", -1);
+ if (value != -1)
+ (void) ndi_prop_update_int(DDI_DEV_T_NONE, child, "vendor-id",
+ value);
+
+ value = ddi_getprop(DDI_DEV_T_ANY, child, DDI_PROP_CANSLEEP,
+ "device-id", -1);
+ if (value != -1)
+ (void) ndi_prop_update_int(DDI_DEV_T_NONE, child, "device-id",
+ value);
+
+ value = ddi_getprop(DDI_DEV_T_ANY, child, DDI_PROP_CANSLEEP,
+ "interrupts", -1);
+ if (value != -1)
+ (void) ndi_prop_update_int(DDI_DEV_T_NONE, child, "interrupts",
+ value);
+ return (DDI_SUCCESS);
+}
+
+
+/*
+ * pepb_save_config_regs
+ *
+ * This routine saves the state of the configuration registers of all
+ * the child nodes of each PBM.
+ *
+ * used by: pepb_detach() on suspends
+ *
+ * return value: none
+ *
+ * XXX: Need to save PCI-E config registers including MSI
+ */
+static void
+pepb_save_config_regs(pepb_devstate_t *pepb_p)
+{
+ int i;
+ dev_info_t *dip;
+ ddi_acc_handle_t config_handle;
+
+ for (i = 0, dip = ddi_get_child(pepb_p->dip); dip != NULL;
+ i++, dip = ddi_get_next_sibling(dip)) {
+
+ if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
+ ddi_driver_name(pepb_p->dip),
+ ddi_get_instance(pepb_p->dip),
+ ddi_driver_name(dip),
+ ddi_get_instance(dip));
+ continue;
+ }
+
+ pepb_p->config_state[i].dip = dip;
+ pepb_p->config_state[i].command =
+ pci_config_get16(config_handle, PCI_CONF_COMM);
+ pepb_p->config_state[i].header_type =
+ pci_config_get8(config_handle, PCI_CONF_HEADER);
+
+ if ((pepb_p->config_state[i].header_type & PCI_HEADER_TYPE_M) ==
+ PCI_HEADER_ONE)
+ pepb_p->config_state[i].bridge_control =
+ pci_config_get16(config_handle, PCI_BCNF_BCNTRL);
+
+ pepb_p->config_state[i].cache_line_size =
+ pci_config_get8(config_handle, PCI_CONF_CACHE_LINESZ);
+ pepb_p->config_state[i].latency_timer =
+ pci_config_get8(config_handle, PCI_CONF_LATENCY_TIMER);
+
+ if ((pepb_p->config_state[i].header_type &
+ PCI_HEADER_TYPE_M) == PCI_HEADER_ONE)
+ pepb_p->config_state[i].sec_latency_timer =
+ pci_config_get8(config_handle,
+ PCI_BCNF_LATENCY_TIMER);
+
+ pci_config_teardown(&config_handle);
+ }
+ pepb_p->config_state_index = i;
+}
+
+
+/*
+ * pepb_restore_config_regs
+ *
+ * This routine restores the state of the configuration registers of all
+ * the child nodes of each PBM.
+ *
+ * used by: pepb_attach() on resume
+ *
+ * return value: none
+ *
+ * XXX: Need to restore PCI-E config registers including MSI
+ */
+static void
+pepb_restore_config_regs(pepb_devstate_t *pepb_p)
+{
+ int i;
+ dev_info_t *dip;
+ ddi_acc_handle_t config_handle;
+
+ for (i = 0; i < pepb_p->config_state_index; i++) {
+ dip = pepb_p->config_state[i].dip;
+ if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
+ ddi_driver_name(pepb_p->dip),
+ ddi_get_instance(pepb_p->dip),
+ ddi_driver_name(dip),
+ ddi_get_instance(dip));
+ continue;
+ }
+ pci_config_put16(config_handle, PCI_CONF_COMM,
+ pepb_p->config_state[i].command);
+ if ((pepb_p->config_state[i].header_type & PCI_HEADER_TYPE_M) ==
+ PCI_HEADER_ONE)
+ pci_config_put16(config_handle, PCI_BCNF_BCNTRL,
+ pepb_p->config_state[i].bridge_control);
+
+ pci_config_put8(config_handle, PCI_CONF_CACHE_LINESZ,
+ pepb_p->config_state[i].cache_line_size);
+ pci_config_put8(config_handle, PCI_CONF_LATENCY_TIMER,
+ pepb_p->config_state[i].latency_timer);
+
+ if ((pepb_p->config_state[i].header_type &
+ PCI_HEADER_TYPE_M) == PCI_HEADER_ONE)
+ pci_config_put8(config_handle, PCI_BCNF_LATENCY_TIMER,
+ pepb_p->config_state[i].sec_latency_timer);
+
+ pci_config_teardown(&config_handle);
+ }
+}
+
+/*
+ * Probe for the inband PCI(E) hot plug controller. Returns the type
+ * of HPC found. This function works only for the standard PCI(E) bridge
+ * that has inband hot plug controller.
+ *
+ * NOTE: This won't work for Host-PCI(E) bridges.
+ */
+static pepb_inband_hpc_t
+pepb_probe_inband_hpc(dev_info_t *dip)
+{
+ uint8_t cap_ptr;
+ uint8_t cap_id;
+ uint16_t status;
+ ddi_acc_handle_t cfg_handle;
+
+ if (pci_config_setup(dip, &cfg_handle) != DDI_SUCCESS)
+ return (INBAND_HPC_NONE);
+
+ /* Read the PCI configuration status register. */
+ status = pci_config_get16(cfg_handle, PCI_CONF_STAT);
+
+ /* check for capabilities list */
+ if (!(status & PCI_STAT_CAP)) {
+ /* no capabilities list */
+ pci_config_teardown(&cfg_handle);
+ return (INBAND_HPC_NONE);
+ }
+
+ /* Get a pointer to the PCI capabilities list. */
+ cap_ptr = pci_config_get8(cfg_handle, PCI_BCNF_CAP_PTR);
+ cap_ptr &= 0xFC; /* mask off reserved bits */
+
+ /*
+ * Walk thru the capabilities list looking for PCI Express capability
+ * structure.
+ */
+ while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) {
+ cap_id = pci_config_get8(cfg_handle, cap_ptr);
+
+ if (cap_id == PCI_CAP_ID_PCI_E) {
+ uint32_t slot_cap;
+
+ /* Read the PCI Express Slot Capabilities Register */
+ slot_cap = pci_config_get32(cfg_handle,
+ cap_ptr + PCIE_SLOTCAP);
+
+ /* Does it have PCI Express HotPlug capability? */
+ if (slot_cap & PCIE_SLOTCAP_HP_CAPABLE) {
+ pci_config_teardown(&cfg_handle);
+ return (INBAND_HPC_PCIE);
+ }
+ }
+
+ if (cap_id == PCI_CAP_ID_PCI_HOTPLUG) {
+ pci_config_teardown(&cfg_handle);
+ return (INBAND_HPC_SHPC); /* inband SHPC present */
+ }
+
+ /* Get the pointer to the next capability */
+ cap_ptr = pci_config_get8(cfg_handle, cap_ptr + 1);
+ cap_ptr &= 0xFC;
+ }
+
+ pci_config_teardown(&cfg_handle);
+
+ return (INBAND_HPC_NONE);
+}
+
+static int
+pepb_pcie_device_type(dev_info_t *dip, int *port_type)
+{
+ ddi_acc_handle_t handle;
+
+ if (pci_config_setup(dip, &handle) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+ *port_type = pepb_pcie_port_type(dip, handle);
+ pci_config_teardown(&handle);
+
+ /* 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_ROOT) ||
+ (*port_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE))
+ return (DDI_SUCCESS);
+
+ return (DDI_FAILURE);
+
+}
+
+/*
+ * This function initializes internally generated interrupts only.
+ * It does not affect any interrupts generated by downstream devices
+ * or the forwarding of them.
+ *
+ * Enable Device Specific Interrupts or Hotplug features here.
+ * Enabling features may change how many interrupts are requested
+ * by the device. If features are not enabled first, the
+ * device might not ask for any interrupts.
+ */
+static int
+pepb_intr_init(pepb_devstate_t *pepb_p, int intr_type)
+{
+ dev_info_t *dip = pepb_p->dip;
+ int request = 1, count, x;
+ int ret;
+ int intr_cap = 0;
+
+ PEPB_DEBUG((CE_NOTE, "pepb_intr_init: Attaching %s handler\n",
+ (intr_type == DDI_INTR_TYPE_MSI) ? "MSI" : "INTx"));
+
+ /*
+ * Get number of requested interrupts. If none requested or DDI_FAILURE
+ * just return DDI_SUCCESS.
+ *
+ * Several Bridges/Switches will not have this property set, resulting
+ * in a FAILURE, if the device is not configured in a way that
+ * interrupts are needed. (eg. hotplugging)
+ */
+ ret = ddi_intr_get_nintrs(dip, intr_type, &request);
+ if ((ret != DDI_SUCCESS) || (request == 0)) {
+ PEPB_DEBUG((CE_NOTE, "ddi_intr_get_nintrs ret:%d req:%d\n",
+ ret, request));
+ return (DDI_FAILURE);
+ }
+
+ /* Allocate an array of interrupt handlers */
+ pepb_p->htable_size = sizeof (ddi_intr_handle_t) * request;
+ pepb_p->htable = kmem_zalloc(pepb_p->htable_size, KM_SLEEP);
+ pepb_p->soft_state |= PEPB_SOFT_STATE_INIT_HTABLE;
+
+ ret = ddi_intr_alloc(dip, pepb_p->htable, intr_type, 0, request,
+ &count, 0);
+ if ((ret != DDI_SUCCESS) || (count == 0)) {
+ PEPB_DEBUG((CE_NOTE, "ddi_intr_alloc() ret: %d ask: %d"
+ " actual: %d\n", ret, request, count));
+ goto fail;
+ }
+
+ /* Save the actual number of interrupts allocated */
+ pepb_p->intr_count = count;
+#ifdef DEBUG
+ if (count < request)
+ PEPB_DEBUG((CE_WARN, "Requested Intr: %d Received: %d\n",
+ request, count));
+#endif /* DEBUG */
+ pepb_p->soft_state |= PEPB_SOFT_STATE_INIT_ALLOC;
+
+ /* Get interrupt priority */
+ ret = ddi_intr_get_pri(pepb_p->htable[0], &pepb_p->intr_priority);
+ if (ret != DDI_SUCCESS) {
+ PEPB_DEBUG((CE_WARN, "ddi_intr_get_pri() ret: %d\n", ret));
+ goto fail;
+ }
+
+ /* initialize the interrupt mutex */
+ mutex_init(&pepb_p->pepb_mutex, NULL, MUTEX_DRIVER,
+ DDI_INTR_PRI(PEPB_INTR_PRI));
+ pepb_p->soft_state |= PEPB_SOFT_STATE_INIT_MUTEX;
+
+ for (count = 0; count < pepb_p->intr_count; count++) {
+ ret = ddi_intr_add_handler(pepb_p->htable[count],
+ pepb_intr, (caddr_t)pepb_p, NULL);
+
+ if (ret != DDI_SUCCESS) {
+ PEPB_DEBUG((CE_WARN, "Cannot add interrupt(%d)\n",
+ ret));
+ break;
+ }
+ }
+
+ /* If unsucessful, remove the added handlers */
+ if (ret != DDI_SUCCESS) {
+ for (x = 0; x < count; x++) {
+ (void) ddi_intr_remove_handler(pepb_p->htable[x]);
+ }
+ goto fail;
+ }
+
+ pepb_p->soft_state |= PEPB_SOFT_STATE_INIT_HANDLER;
+
+ (void) ddi_intr_get_cap(pepb_p->htable[0], &intr_cap);
+
+ if (intr_cap & DDI_INTR_FLAG_BLOCK) {
+ (void) ddi_intr_block_enable(pepb_p->htable,
+ pepb_p->intr_count);
+ pepb_p->soft_state |= PEPB_SOFT_STATE_INIT_BLOCK;
+ } else {
+ for (count = 0; count < pepb_p->intr_count; count++) {
+ (void) ddi_intr_enable(pepb_p->htable[count]);
+ }
+ }
+ pepb_p->soft_state |= PEPB_SOFT_STATE_INIT_ENABLE;
+
+ /* Save the interrupt type */
+ pepb_p->intr_type = intr_type;
+
+ return (DDI_SUCCESS);
+
+fail:
+ pepb_intr_fini(pepb_p);
+
+ return (DDI_FAILURE);
+}
+
+static void
+pepb_intr_fini(pepb_devstate_t *pepb_p)
+{
+ int x;
+ int count = pepb_p->intr_count;
+ int flags = pepb_p->soft_state;
+
+ if ((flags & PEPB_SOFT_STATE_INIT_ENABLE) &&
+ (flags & PEPB_SOFT_STATE_INIT_BLOCK)) {
+ (void) ddi_intr_block_disable(pepb_p->htable, count);
+ flags &= ~(PEPB_SOFT_STATE_INIT_ENABLE |
+ PEPB_SOFT_STATE_INIT_BLOCK);
+ }
+
+ if (flags & PEPB_SOFT_STATE_INIT_MUTEX) {
+ /* destroy the mutex */
+ mutex_destroy(&pepb_p->pepb_mutex);
+ }
+
+ for (x = 0; x < count; x++) {
+ if (flags & PEPB_SOFT_STATE_INIT_ENABLE)
+ (void) ddi_intr_disable(pepb_p->htable[x]);
+
+ if (flags & PEPB_SOFT_STATE_INIT_HANDLER)
+ (void) ddi_intr_remove_handler(pepb_p->htable[x]);
+
+ if (flags & PEPB_SOFT_STATE_INIT_ALLOC)
+ (void) ddi_intr_free(pepb_p->htable[x]);
+ }
+
+ flags &= ~(PEPB_SOFT_STATE_INIT_ENABLE |
+ PEPB_SOFT_STATE_INIT_HANDLER |
+ PEPB_SOFT_STATE_INIT_ALLOC | PEPB_SOFT_STATE_INIT_MUTEX);
+
+ if (flags & PEPB_SOFT_STATE_INIT_HTABLE)
+ kmem_free(pepb_p->htable, pepb_p->htable_size);
+
+ flags &= ~PEPB_SOFT_STATE_INIT_HTABLE;
+
+ pepb_p->soft_state &= flags;
+}
+
+/*
+ * pepb_intr()
+ *
+ * This is the common interrupt handler for both hotplug and non-hotplug
+ * interrupts. For handling hot plug interrupts it calls pciehpc_intr().
+ *
+ * NOTE: Currently only hot plug interrupts are enabled so it simply
+ * calls pciehpc_intr().
+ */
+/*ARGSUSED*/
+static uint_t
+pepb_intr(caddr_t arg, caddr_t arg2)
+{
+ pepb_devstate_t *pepb_p = (pepb_devstate_t *)arg;
+ int ret = DDI_INTR_UNCLAIMED;
+
+ if (!(pepb_p->soft_state & PEPB_SOFT_STATE_INIT_ENABLE))
+ return (DDI_INTR_UNCLAIMED);
+
+ mutex_enter(&pepb_p->pepb_mutex);
+
+ /* if HPC is initialized then call the interrupt handler */
+ if (pepb_p->inband_hpc == INBAND_HPC_PCIE)
+ ret = pciehpc_intr(pepb_p->dip);
+
+ mutex_exit(&pepb_p->pepb_mutex);
+
+ return (ret);
+}
+
+/*
+ * given a cap_id, return its cap_id location in config space
+ */
+static int
+pepb_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 int
+pepb_pcie_port_type(dev_info_t *dip, ddi_acc_handle_t handle)
+{
+ int port_type = -1;
+ uint_t cap_loc;
+
+ /* Need to look at the port type information here XXX KINI */
+ if ((cap_loc = pepb_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
+pepb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
+{
+ return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp));
+}
+
+static int
+pepb_close(dev_t dev, int flags, int otyp, cred_t *credp)
+{
+ return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp));
+}
+
+static int
+pepb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, credp,
+ rvalp));
+}
+
+static int
+pepb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
+ int flags, char *name, caddr_t valuep, int *lengthp)
+{
+ return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags,
+ name, valuep, lengthp));
+}
+
+static int
+pepb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
+{
+ return (pcihp_info(dip, cmd, arg, result));
+}
diff --git a/usr/src/uts/i86pc/io/pciex/pcie_pci.conf b/usr/src/uts/i86pc/io/pciex/pcie_pci.conf
new file mode 100644
index 0000000000..b8d0d8b148
--- /dev/null
+++ b/usr/src/uts/i86pc/io/pciex/pcie_pci.conf
@@ -0,0 +1,36 @@
+#
+# 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.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Force load driver to support hotplug activity
+#
+ddi-forceattach=1;
+
+#
+# force interrupt priorities to be one
+# otherwise this driver binds as bridge device with priority 12
+#
+interrupt-priorities=1;
diff --git a/usr/src/uts/i86pc/io/pcplusmp/apic.c b/usr/src/uts/i86pc/io/pcplusmp/apic.c
index de03805535..c3ccd67721 100644
--- a/usr/src/uts/i86pc/io/pcplusmp/apic.c
+++ b/usr/src/uts/i86pc/io/pcplusmp/apic.c
@@ -488,6 +488,13 @@ static uchar_t apic_io_ver[MAX_IO_APIC];
static uchar_t apic_io_vectbase[MAX_IO_APIC];
static uchar_t apic_io_vectend[MAX_IO_APIC];
volatile int32_t *apicioadr[MAX_IO_APIC];
+
+/*
+ * First available slot to be used as IRQ index into the apic_irq_table
+ * for those interrupts (like MSI/X) that don't have a physical IRQ.
+ */
+int apic_first_avail_irq = APIC_FIRST_FREE_IRQ;
+
/*
* apic_ioapic_lock protects the ioapics (reg select), the status, temp_bound
* and bound elements of cpus_info and the temp_cpu element of irq_struct
@@ -1130,6 +1137,8 @@ acpi_probe(void)
apic_io_ver[i] = (uchar_t)(ver & 0xff);
intmax = (ver >> 16) & 0xff;
apic_io_vectend[i] = apic_io_vectbase[i] + intmax;
+ if (apic_first_avail_irq <= apic_io_vectend[i])
+ apic_first_avail_irq = apic_io_vectend[i] + 1;
}
@@ -3133,7 +3142,8 @@ apic_introp_xlate(dev_info_t *dip, struct intrspec *ispec, int type)
goto nonpci;
}
- if (strcmp(dev_type, "pci") == 0) {
+ if ((strcmp(dev_type, "pci") == 0) ||
+ (strcmp(dev_type, "pciex") == 0)) {
/* pci device */
if (acpica_get_bdf(dip, &busid, &devid, NULL) != 0)
goto nonpci;
@@ -3482,7 +3492,7 @@ apic_setup_irq_table(dev_info_t *dip, int irqno, struct apic_io_intr *intrp,
intr_index = (type == DDI_INTR_TYPE_MSI) ? MSI_INDEX :
MSIX_INDEX;
mutex_enter(&airq_mutex);
- if ((irqno = apic_allocate_irq(APIC_FIRST_FREE_IRQ)) == -1) {
+ if ((irqno = apic_allocate_irq(apic_first_avail_irq)) == -1) {
mutex_exit(&airq_mutex);
/* need an irq for MSI/X to index into autovect[] */
cmn_err(CE_WARN, "No interrupt irq: %s instance %d",
@@ -3574,7 +3584,7 @@ apic_setup_irq_table(dev_info_t *dip, int irqno, struct apic_io_intr *intrp,
* The slot is used by another irqno, so allocate
* a free irqno for this interrupt
*/
- newirq = apic_allocate_irq(APIC_FIRST_FREE_IRQ);
+ newirq = apic_allocate_irq(apic_first_avail_irq);
if (newirq == -1) {
mutex_exit(&airq_mutex);
return (-1);
diff --git a/usr/src/uts/i86pc/io/pcplusmp/apic_introp.c b/usr/src/uts/i86pc/io/pcplusmp/apic_introp.c
index 20b6b5610b..ec1493331f 100644
--- a/usr/src/uts/i86pc/io/pcplusmp/apic_introp.c
+++ b/usr/src/uts/i86pc/io/pcplusmp/apic_introp.c
@@ -88,6 +88,7 @@ extern volatile int32_t *apicioadr[MAX_IO_APIC];
extern lock_t apic_ioapic_lock;
extern kmutex_t airq_mutex;
extern apic_cpus_info_t *apic_cpus;
+extern int apic_first_avail_irq;
/*
@@ -372,7 +373,7 @@ apic_alloc_vectors(dev_info_t *dip, int inum, int count, int pri, int type)
idx = (short)((type == DDI_INTR_TYPE_MSI) ? MSI_INDEX : MSIX_INDEX);
major = (dip != NULL) ? ddi_name_to_major(ddi_get_name(dip)) : 0;
for (i = 0; i < rcount; i++) {
- if ((irqno = apic_allocate_irq(APIC_FIRST_FREE_IRQ)) ==
+ if ((irqno = apic_allocate_irq(apic_first_avail_irq)) ==
(uchar_t)-1) {
mutex_exit(&airq_mutex);
DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_vectors: "
@@ -458,26 +459,32 @@ apic_check_msi_support(dev_info_t *dip)
{
dev_info_t *rootdip;
+ char dev_type[16];
+ int dev_len;
DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: dip: 0x%p\n",
(void *)dip));
/* check whether the device or its ancestors have PCI-E capability */
- for (rootdip = ddi_root_node(); dip != rootdip &&
- pci_check_pciex(dip) != DDI_SUCCESS; dip = ddi_get_parent(dip));
-
- /* PCI-E capability found */
- if (dip != rootdip) {
- DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: "
- "PCI-E capability found @ nodename %s driver %s%d\n",
- ddi_node_name(dip), ddi_driver_name(dip),
- ddi_get_instance(dip)));
- return (PSM_SUCCESS);
+ for (rootdip = ddi_root_node(); dip != rootdip;
+ dip = ddi_get_parent(dip)) {
+
+ DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: dip: 0x%p,"
+ " driver: %s, binding: %s, nodename: %s\n", (void *)dip,
+ ddi_driver_name(dip), ddi_binding_name(dip),
+ ddi_node_name(dip)));
+ dev_len = sizeof (dev_type);
+ if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "device_type", (caddr_t)dev_type, &dev_len)
+ != DDI_PROP_SUCCESS)
+ continue;
+ if (strcmp(dev_type, "pciex") == 0)
+ return (PSM_SUCCESS);
}
/* MSI is not supported on this system */
- DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: "
- "no PCI-E capability found\n"));
+ DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: no 'pciex' "
+ "device_type found\n"));
return (PSM_FAILURE);
}
diff --git a/usr/src/uts/i86pc/io/psm/uppc.c b/usr/src/uts/i86pc/io/psm/uppc.c
index 8d040928fa..4951fbe560 100644
--- a/usr/src/uts/i86pc/io/psm/uppc.c
+++ b/usr/src/uts/i86pc/io/psm/uppc.c
@@ -751,7 +751,8 @@ uppc_translate_irq(dev_info_t *dip, int irqno)
return (irqno);
}
- if (strcmp(dev_type, "pci") == 0) {
+ if ((strcmp(dev_type, "pci") == 0) ||
+ (strcmp(dev_type, "pciex") == 0)) {
/* pci device */
if (acpica_get_bdf(dip, &busid, &devid, NULL) != 0)
diff --git a/usr/src/uts/i86pc/npe/Makefile b/usr/src/uts/i86pc/npe/Makefile
new file mode 100644
index 0000000000..1bcbf5ae29
--- /dev/null
+++ b/usr/src/uts/i86pc/npe/Makefile
@@ -0,0 +1,93 @@
+#
+# 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/i86pc/npe/Makefile
+# Copyright 2005 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 PCI-E nexus driver
+#
+# i86pc 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 = npe
+OBJECTS = $(PCI_E_NEXUS_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(PCI_E_NEXUS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/i86pc/Makefile.i86pc
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# depends on misc/pcihp misc/acpica
+#
+# For PCI Hotplug support, the misc/pcihp module provides devctl control
+# device and cb_ops functions to support hotplug operations.
+#
+# acpica supplies ACPI access routines
+#
+LDFLAGS += -dy -Nmisc/pcihp -Nmisc/acpica
+
+#
+# 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)/i86pc/Makefile.targ
diff --git a/usr/src/uts/i86pc/pcie_pci/Makefile b/usr/src/uts/i86pc/pcie_pci/Makefile
new file mode 100644
index 0000000000..2b98a7c8fe
--- /dev/null
+++ b/usr/src/uts/i86pc/pcie_pci/Makefile
@@ -0,0 +1,96 @@
+#
+# 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/i86pc/pcie_pci/Makefile
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the pcie_pci driver kernel
+# module.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = pcie_pci
+OBJECTS = $(PCI_E_PCINEXUS_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(PCI_E_PCINEXUS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/i86pc/io/pciex
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/i86pc/Makefile.i86pc
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Dependency
+#
+LDFLAGS += -dy -Nmisc/pcihp
+
+#
+# Override defaults to build a unique, local modstubs.o.
+#
+MODSTUBS_DIR = $(OBJS_DIR)
+CLEANFILES += $(MODSTUBS_O)
+
+#
+# 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)/i86pc/Makefile.targ
+
diff --git a/usr/src/uts/i86pc/pciehpc/Makefile b/usr/src/uts/i86pc/pciehpc/Makefile
new file mode 100644
index 0000000000..1fcfc387a3
--- /dev/null
+++ b/usr/src/uts/i86pc/pciehpc/Makefile
@@ -0,0 +1,88 @@
+#
+# 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.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of the kernel/misc/pciehpc module
+# for PCI-E hotplug controller support in PCI-E nexus drivers.
+#
+# i86pc 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 = pciehpc
+OBJECTS = $(PCIEHPCNEXUS_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(PCIEHPCNEXUS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_PSM_MISC_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/i86pc/Makefile.i86pc
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# Dependency
+#
+LDFLAGS += -dy -Nmisc/hpcsvc -Nmisc/acpica
+
+#
+# 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)/i86pc/Makefile.targ
diff --git a/usr/src/uts/intel/ia32/ml/modstubs.s b/usr/src/uts/intel/ia32/ml/modstubs.s
index 658dc6dd89..d77ddd912b 100644
--- a/usr/src/uts/intel/ia32/ml/modstubs.s
+++ b/usr/src/uts/intel/ia32/ml/modstubs.s
@@ -975,6 +975,17 @@ fcnname/**/_info: \
END_MODULE(pcicfg);
#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, nomod_zero);
+ END_MODULE(pciehpc);
+#endif
+
#ifndef WC_MODULE
MODULE(wc,drv);
STUB(wc, wcvnget, 0);
diff --git a/usr/src/uts/intel/io/vgatext/vgatext.c b/usr/src/uts/intel/io/vgatext/vgatext.c
index b99fc01c68..0edff05b41 100644
--- a/usr/src/uts/intel/io/vgatext/vgatext.c
+++ b/usr/src/uts/intel/io/vgatext/vgatext.c
@@ -405,7 +405,7 @@ vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
MYNAME ": can't find reg entry for memory");
goto fail;
}
- } else if (STREQ(parent_type, "pci")) {
+ } else if (STREQ(parent_type, "pci") || STREQ(parent_type, "pciex")) {
reg_rnumber = vgatext_get_pci_reg_index(devi,
PCI_REG_ADDR_M|PCI_REG_REL_M,
PCI_ADDR_IO|PCI_RELOCAT_B, VGA_REG_ADDR,
diff --git a/usr/src/uts/intel/os/driver_aliases b/usr/src/uts/intel/os/driver_aliases
index 9d8b7b4c59..b8f85bc97c 100644
--- a/usr/src/uts/intel/os/driver_aliases
+++ b/usr/src/uts/intel/os/driver_aliases
@@ -1,3 +1,9 @@
+npe "pciex_root_complex"
+pcie_pci "pciexclass,060400"
+pcie_pci "pciexclass,060401"
+pcie_pci "pciex1011,1"
+pcie_pci "pciex1011,21"
+pcie_pci "pciex1014,22"
pci_pci "pciclass,060400"
pci_pci "pciclass,060401"
pci_pci "pci1011,1"
@@ -10,6 +16,9 @@ vgatext "pnpPNP,900"
vgatext "pciclass,000100"
vgatext "pciclass,030000"
vgatext "pciclass,030001"
+vgatext "pciexclass,000100"
+vgatext "pciexclass,030000"
+vgatext "pciexclass,030001"
bscbus "SVI0101"
pseudo zconsnex
st "scsiclass,01"
diff --git a/usr/src/uts/intel/os/name_to_major b/usr/src/uts/intel/os/name_to_major
index ce24d9f92c..77b634d1af 100644
--- a/usr/src/uts/intel/os/name_to_major
+++ b/usr/src/uts/intel/os/name_to_major
@@ -116,4 +116,6 @@ aggr 179
smbios 180
power 181
zfs 182
+npe 183
+pcie_pci 184
did 239
diff --git a/usr/src/uts/intel/sys/acpi/acpi_pci.h b/usr/src/uts/intel/sys/acpi/acpi_pci.h
new file mode 100644
index 0000000000..c2486d35ab
--- /dev/null
+++ b/usr/src/uts/intel/sys/acpi/acpi_pci.h
@@ -0,0 +1,77 @@
+/*
+ * 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 _ACPI_PCI_H
+#define _ACPI_PCI_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Memory mapped configuration space address description table documented
+ * in ACPI 3.0. These definitions are currently not included in the
+ * actbl.h from Intel. This file will need to be removed once Intel has
+ * updated their include file with these information.
+ */
+#define MCFG_SIG "MCFG" /* Memory mapped config space address */
+ /* description table's signature */
+
+#pragma pack(1)
+
+typedef struct cfg_base_addr_alloc {
+ UINT64 base_addr;
+ UINT16 group_bno;
+ UINT8 start_bno;
+ UINT8 end_bno;
+ UINT32 reserved;
+} CFG_BASE_ADDR_ALLOC;
+
+typedef struct mcfg_table {
+ char Signature[4]; /* MCFG signature */
+ UINT32 Length; /* Length of table, in bytes */
+ UINT8 Revision; /* ACPI Specification minor version # */
+ UINT8 Checksum; /* To make sum of entire table == 0 */
+ char OemId[6]; /* OEM identification */
+ char OemTableId[8]; /* OEM table identification */
+ UINT32 OemRevision; /* OEM revision number */
+ char CreatorId[4]; /* Table creator vendor Id */
+ UINT32 CreatorRevision; /* Table creator utility revision no */
+ UINT8 Reserved[8]; /* Reserved */
+ /* List of memory mapped cfg base address allocation structures */
+ CFG_BASE_ADDR_ALLOC CfgBaseAddrAllocList[1];
+} MCFG_TABLE;
+
+#pragma pack()
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ACPI_PCI_H */