diff options
Diffstat (limited to 'usr/src/uts/i86pc/io/pciex/npe.c')
-rw-r--r-- | usr/src/uts/i86pc/io/pciex/npe.c | 139 |
1 files changed, 108 insertions, 31 deletions
diff --git a/usr/src/uts/i86pc/io/pciex/npe.c b/usr/src/uts/i86pc/io/pciex/npe.c index fde4276cc7..3ca09deb31 100644 --- a/usr/src/uts/i86pc/io/pciex/npe.c +++ b/usr/src/uts/i86pc/io/pciex/npe.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -32,8 +32,8 @@ #include <sys/conf.h> #include <sys/modctl.h> -#include <sys/pcie.h> #include <sys/pci_impl.h> +#include <sys/pcie_impl.h> #include <sys/sysmacros.h> #include <sys/ddi_intr.h> #include <sys/sunndi.h> @@ -42,9 +42,9 @@ #include <sys/ndifm.h> #include <sys/fm/util.h> #include <sys/hotplug/pci/pcihp.h> -#include <io/pci/pci_common.h> #include <io/pci/pci_tools_ext.h> -#include <io/pciex/pcie_error.h> +#include <io/pci/pci_common.h> +#include <io/pciex/pcie_nvidia.h> /* * Bus Operation functions @@ -60,6 +60,16 @@ static int npe_fm_init(dev_info_t *, dev_info_t *, int, static int npe_fm_callback(dev_info_t *, ddi_fm_error_t *, const void *); +/* + * Disable URs and Received MA for all PCIe devices. Until x86 SW is changed so + * that random drivers do not do PIO accesses on devices that it does not own, + * these error bits must be disabled. SERR must also be disabled if URs have + * been masked. + */ +uint32_t npe_aer_uce_mask = PCIE_AER_UCE_UR; +uint32_t npe_aer_ce_mask = 0; +uint32_t npe_aer_suce_mask = PCIE_AER_SUCE_RCVD_MA; + struct bus_ops npe_bus_ops = { BUSO_REV, npe_bus_map, @@ -158,6 +168,7 @@ static int npe_initchild(dev_info_t *child); extern void npe_query_acpi_mcfg(dev_info_t *dip); extern void npe_ck804_fix_aer_ptr(ddi_acc_handle_t cfg_hdl); extern int npe_disable_empty_bridges_workaround(dev_info_t *child); +extern void npe_nvidia_error_mask(ddi_acc_handle_t cfg_hdl); /* * Module linkage information for the kernel. @@ -177,7 +188,6 @@ static struct modlinkage modlinkage = { /* Save minimal state. */ void *npe_statep; - int _init(void) { @@ -244,6 +254,8 @@ npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) pcip->pci_dip = devi; + pcie_rc_init_bus(devi); + /* * Initialize hotplug support on this bus. At minimum * (for non hotplug bus) this would create ":devctl" minor @@ -262,12 +274,17 @@ npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) ddi_soft_state_free(npe_statep, instance); return (DDI_FAILURE); } + pcip->pci_fmcap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE | DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE; ddi_fm_init(devi, &pcip->pci_fmcap, &pcip->pci_fm_ibc); - if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE) + if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE) { ddi_fm_handler_register(devi, npe_fm_callback, NULL); + } + + PCIE_DIP2PFD(devi) = kmem_zalloc(sizeof (pf_data_t), KM_SLEEP); + pcie_rc_init_pfd(devi, PCIE_DIP2PFD(devi)); npe_query_acpi_mcfg(devi); ddi_report_dev(devi); @@ -275,7 +292,6 @@ npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) } - /*ARGSUSED*/ static int npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) @@ -299,6 +315,10 @@ npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE) ddi_fm_handler_unregister(devi); + pcie_rc_fini_bus(devi); + pcie_rc_fini_pfd(PCIE_DIP2PFD(devi)); + kmem_free(PCIE_DIP2PFD(devi), sizeof (pf_data_t)); + ddi_fm_fini(devi); ddi_soft_state_free(npe_statep, instance); return (DDI_SUCCESS); @@ -610,7 +630,10 @@ npe_ctlops(dev_info_t *dip, dev_info_t *rdip, int totreg; uint_t reglen; pci_regspec_t *drv_regp; - struct attachspec *asp; + struct attachspec *asp; + struct detachspec *dsp; + pci_state_t *pci_p = ddi_get_soft_state(npe_statep, + ddi_get_instance(dip)); switch (ctlop) { case DDI_CTLOPS_REPORTDEV: @@ -677,7 +700,15 @@ npe_ctlops(dev_info_t *dip, dev_info_t *rdip, /* X86 systems support PME wakeup from suspended state */ case DDI_CTLOPS_ATTACH: + if (!pcie_is_child(dip, rdip)) + return (DDI_SUCCESS); + asp = (struct attachspec *)arg; + if ((asp->when == DDI_POST) && (asp->result == DDI_SUCCESS)) { + pf_init(rdip, (void *)pci_p->pci_fm_ibc, asp->cmd); + (void) pcie_postattach_child(rdip); + } + /* only do this for immediate children */ if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE && ddi_get_parent(rdip) == dip) @@ -688,16 +719,25 @@ npe_ctlops(dev_info_t *dip, dev_info_t *rdip, (void *) dip); /* NOTREACHED */ } - return (ddi_ctlops(dip, rdip, ctlop, arg, result)); + + return (DDI_SUCCESS); case DDI_CTLOPS_DETACH: - asp = (struct attachspec *)arg; + if (!pcie_is_child(dip, rdip)) + return (DDI_SUCCESS); + + dsp = (struct detachspec *)arg; + + if (dsp->when == DDI_PRE) + pf_fini(rdip, dsp->cmd); + /* only do this for immediate children */ - if (asp->cmd == DDI_SUSPEND && asp->when == DDI_POST && + if (dsp->cmd == DDI_SUSPEND && dsp->when == DDI_POST && ddi_get_parent(rdip) == dip) if (pci_post_suspend(rdip) != DDI_SUCCESS) return (DDI_FAILURE); - return (ddi_ctlops(dip, rdip, ctlop, arg, result)); + + return (DDI_SUCCESS); default: break; @@ -722,7 +762,9 @@ npe_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, static int npe_initchild(dev_info_t *child) { - char name[80]; + char name[80]; + pcie_bus_t *bus_p; + uint32_t regs; ddi_acc_handle_t cfg_hdl; /* @@ -797,15 +839,55 @@ npe_initchild(dev_info_t *child) else ddi_set_parent_data(child, NULL); + /* Disable certain errors on PCIe drivers for x86 platforms */ + regs = pcie_get_aer_uce_mask() | npe_aer_uce_mask; + pcie_set_aer_uce_mask(regs); + regs = pcie_get_aer_ce_mask() | npe_aer_ce_mask; + pcie_set_aer_ce_mask(regs); + regs = pcie_get_aer_suce_mask() | npe_aer_suce_mask; + pcie_set_aer_suce_mask(regs); + /* - * Enable AER next pointer being displayed and PCIe Error initilization + * If URs are disabled, mask SERRs as well, otherwise the system will + * still be notified of URs */ + if (npe_aer_uce_mask & PCIE_AER_UCE_UR) + pcie_set_serr_mask(1); + if (pci_config_setup(child, &cfg_hdl) == DDI_SUCCESS) { npe_ck804_fix_aer_ptr(cfg_hdl); - (void) pcie_error_enable(child, cfg_hdl); + npe_nvidia_error_mask(cfg_hdl); pci_config_teardown(&cfg_hdl); } + bus_p = pcie_init_bus(child); + if (bus_p) { + uint16_t device_id = (uint16_t)(bus_p->bus_dev_ven_id >> 16); + uint16_t vendor_id = (uint16_t)(bus_p->bus_dev_ven_id & 0xFFFF); + uint16_t rev_id = bus_p->bus_rev_id; + + /* Disable AER for certain NVIDIA Chipsets */ + if ((vendor_id == NVIDIA_VENDOR_ID) && + (device_id == NVIDIA_CK804_DEVICE_ID) && + (rev_id < NVIDIA_CK804_AER_VALID_REVID)) + bus_p->bus_aer_off = 0; + + (void) pcie_initchild(child); + + /* If device is an NVIDIA RC do device specific error setup */ + if ((vendor_id == NVIDIA_VENDOR_ID) && + NVIDIA_PCIE_RC_DEV_ID(device_id)) { + ddi_acc_handle_t cfg_hdl = bus_p->bus_cfg_hdl; + uint16_t rc_ctl; + + rc_ctl = pci_config_get16(cfg_hdl, NVIDIA_INTR_BCR_OFF + + 0x2); + pci_config_put16(cfg_hdl, NVIDIA_INTR_BCR_OFF + 0x2, + rc_ctl | NVIDIA_INTR_BCR_SERR_FORWARD_BIT); + } + + } + return (DDI_SUCCESS); } @@ -813,22 +895,8 @@ npe_initchild(dev_info_t *child) static int npe_removechild(dev_info_t *dip) { - ddi_acc_handle_t cfg_hdl; - struct ddi_parent_private_data *pdptr; + pcie_uninitchild(dip); - /* - * Do it way early. - * Otherwise ddi_map() call form pcie_error_fini crashes - */ - if (pci_config_setup(dip, &cfg_hdl) == DDI_SUCCESS) { - pcie_error_disable(dip, cfg_hdl); - pci_config_teardown(&cfg_hdl); - } - - 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); /* @@ -872,6 +940,7 @@ npe_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) return (ENXIO); dip = pci_p->pci_dip; + return (pci_common_ioctl(dip, dev, cmd, arg, mode, credp, rvalp)); } @@ -907,5 +976,13 @@ npe_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap, static int npe_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used) { - return (ndi_fm_handler_dispatch(dip, NULL, derr)); + /* + * On current x86 systems, npe's callback does not get called for failed + * loads. If in the future this feature is used, the fault PA should be + * logged in the derr->fme_bus_specific field. The appropriate PCIe + * error handling code should be called and needs to be coordinated with + * safe access handling. + */ + + return (DDI_FM_OK); } |