diff options
author | johnny <none@none> | 2005-11-10 12:33:07 -0800 |
---|---|---|
committer | johnny <none@none> | 2005-11-10 12:33:07 -0800 |
commit | 70025d765b044c6d8594bb965a2247a61e991a99 (patch) | |
tree | ecf0115912221f8be4400ca9bf4d04802b8e0355 /usr/src | |
parent | 4610e4a00999c6d2291b3fc263926b890ec500a5 (diff) | |
download | illumos-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')
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)®s, &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)®s, &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)®spec, ®len) != 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 = ® + 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 = ® + 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, + ®len) != 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, + ®len) != 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 */ |