summaryrefslogtreecommitdiff
path: root/usr/src/uts/intel/io/pci
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/intel/io/pci')
-rw-r--r--usr/src/uts/intel/io/pci/pci_pci.c85
1 files changed, 76 insertions, 9 deletions
diff --git a/usr/src/uts/intel/io/pci/pci_pci.c b/usr/src/uts/intel/io/pci/pci_pci.c
index 753c04d8f8..48e49703b4 100644
--- a/usr/src/uts/intel/io/pci/pci_pci.c
+++ b/usr/src/uts/intel/io/pci/pci_pci.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -36,6 +36,7 @@
#include <sys/autoconf.h>
#include <sys/ddi_impldefs.h>
#include <sys/pci.h>
+#include <sys/pcie_impl.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
@@ -135,6 +136,7 @@ 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 **);
+static void ppb_peekpoke_cb(dev_info_t *, ddi_fm_error_t *);
struct cb_ops ppb_cb_ops = {
ppb_open, /* open */
@@ -216,6 +218,8 @@ typedef struct {
uchar_t sec_latency_timer;
ushort_t bridge_control;
} config_state[PCI_MAX_CHILDREN];
+
+ uint8_t parent_bus;
} ppb_devstate_t;
@@ -276,9 +280,12 @@ ppb_probe(dev_info_t *devi)
static int
ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
+ dev_info_t *root = ddi_root_node();
int instance;
ppb_devstate_t *ppb;
+ dev_info_t *pdip;
ddi_acc_handle_t config_handle;
+ char *bus;
switch (cmd) {
case DDI_ATTACH:
@@ -332,6 +339,22 @@ ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
return (DDI_FAILURE);
}
+ ppb->parent_bus = PCIE_PCIECAP_DEV_TYPE_PCI_DEV;
+ for (pdip = ddi_get_parent(devi); pdip && (pdip != root) &&
+ (ppb->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV);
+ pdip = ddi_get_parent(pdip)) {
+ if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
+ DDI_PROP_DONTPASS, "device_type", &bus) !=
+ DDI_PROP_SUCCESS)
+ break;
+
+ if (strcmp(bus, "pciex") == 0)
+ ppb->parent_bus =
+ PCIE_PCIECAP_DEV_TYPE_PCIE_DEV;
+
+ ddi_prop_free(bus);
+ }
+
if (ppb_support_ht_msimap == 1)
(void) ppb_ht_msimap_set(config_handle,
HT_MSIMAP_ENABLE);
@@ -432,7 +455,9 @@ ppb_ctlops(dev_info_t *dip, dev_info_t *rdip,
int reglen;
int rn;
int totreg;
- ppb_devstate_t *ppb;
+ ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state,
+ ddi_get_instance(dip));
+ struct detachspec *dsp;
struct attachspec *asp;
switch (ctlop) {
@@ -463,28 +488,42 @@ ppb_ctlops(dev_info_t *dip, dev_info_t *rdip,
/* X86 systems support PME wakeup from suspend */
case DDI_CTLOPS_ATTACH:
+ if (!pcie_is_child(dip, rdip))
+ return (DDI_SUCCESS);
+
asp = (struct attachspec *)arg;
+ if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
+ (asp->when == DDI_POST) && (asp->result == DDI_SUCCESS))
+ pf_init(rdip, (void *)ppb->ppb_fm_ibc, asp->cmd);
+
if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE)
if (pci_pre_resume(rdip) != DDI_SUCCESS)
return (DDI_FAILURE);
- return (ddi_ctlops(dip, rdip, ctlop, arg, result));
+ return (DDI_SUCCESS);
case DDI_CTLOPS_DETACH:
- asp = (struct attachspec *)arg;
- if (asp->cmd == DDI_SUSPEND && asp->when == DDI_POST)
+ if (!pcie_is_child(dip, rdip))
+ return (DDI_SUCCESS);
+
+ dsp = (struct detachspec *)arg;
+ if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
+ (dsp->when == DDI_PRE))
+ pf_fini(rdip, dsp->cmd);
+
+ if (dsp->cmd == DDI_SUSPEND && dsp->when == DDI_POST)
if (pci_post_suspend(rdip) != DDI_SUCCESS)
return (DDI_FAILURE);
- return (ddi_ctlops(dip, rdip, ctlop, arg, result));
+
+ return (DDI_SUCCESS);
case DDI_CTLOPS_PEEK:
case DDI_CTLOPS_POKE:
- ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(dip));
if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") != 0)
return (ddi_ctlops(dip, rdip, ctlop, arg, result));
return (pci_peekpoke_check(dip, rdip, ctlop, arg, result,
ddi_ctlops, &ppb->ppb_err_mutex,
- &ppb->ppb_peek_poke_mutex));
+ &ppb->ppb_peek_poke_mutex, ppb_peekpoke_cb));
default:
return (ddi_ctlops(dip, rdip, ctlop, arg, result));
@@ -566,10 +605,14 @@ static int
ppb_initchild(dev_info_t *child)
{
struct ddi_parent_private_data *pdptr;
+ ppb_devstate_t *ppb;
char name[MAXNAMELEN];
ddi_acc_handle_t config_handle;
ushort_t command_preserve, command;
+ ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
+ ddi_get_instance(ddi_get_parent(child)));
+
if (ppb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS)
return (DDI_FAILURE);
ddi_set_name_addr(child, name);
@@ -613,6 +656,20 @@ ppb_initchild(dev_info_t *child)
return (DDI_NOT_WELL_FORMED);
}
+ ddi_set_parent_data(child, NULL);
+
+ /*
+ * PCIe FMA specific
+ *
+ * Note: parent_data for parent is created only if this is PCI-E
+ * platform, for which, SG take a different route to handle device
+ * errors.
+ */
+ if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
+ if (pcie_init_bus(child) == NULL)
+ return (DDI_FAILURE);
+ }
+
/* transfer select properties from PROM to kernel */
if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS,
"interrupts", -1) != -1) {
@@ -645,8 +702,14 @@ static void
ppb_removechild(dev_info_t *dip)
{
struct ddi_parent_private_data *pdptr;
+ ppb_devstate_t *ppb;
+
+ ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
+ ddi_get_instance(ddi_get_parent(dip)));
- if ((pdptr = ddi_get_parent_data(dip)) != NULL) {
+ if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
+ pcie_fini_bus(dip);
+ else if ((pdptr = ddi_get_parent_data(dip)) != NULL) {
kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec)));
ddi_set_parent_data(dip, NULL);
}
@@ -905,6 +968,10 @@ ppb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
return (pcihp_info(dip, cmd, arg, result));
}
+void ppb_peekpoke_cb(dev_info_t *dip, ddi_fm_error_t *derr) {
+ (void) pci_ereport_post(dip, derr, NULL);
+}
+
/*ARGSUSED*/
static int
ppb_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap,