170025d76Sjohnny /* 270025d76Sjohnny * CDDL HEADER START 370025d76Sjohnny * 470025d76Sjohnny * The contents of this file are subject to the terms of the 500d0963fSdilpreet * Common Development and Distribution License (the "License"). 600d0963fSdilpreet * You may not use this file except in compliance with the License. 770025d76Sjohnny * 870025d76Sjohnny * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 970025d76Sjohnny * or http://www.opensolaris.org/os/licensing. 1070025d76Sjohnny * See the License for the specific language governing permissions 1170025d76Sjohnny * and limitations under the License. 1270025d76Sjohnny * 1370025d76Sjohnny * When distributing Covered Code, include this CDDL HEADER in each 1470025d76Sjohnny * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1570025d76Sjohnny * If applicable, add the following below this CDDL HEADER, with the 1670025d76Sjohnny * fields enclosed by brackets "[]" replaced with your own identifying 1770025d76Sjohnny * information: Portions Copyright [yyyy] [name of copyright owner] 1870025d76Sjohnny * 1970025d76Sjohnny * CDDL HEADER END 2070025d76Sjohnny */ 2170025d76Sjohnny 2270025d76Sjohnny /* 23fc256490SJason Beloro * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 2470025d76Sjohnny * Use is subject to license terms. 2570025d76Sjohnny */ 2670025d76Sjohnny 2770025d76Sjohnny /* 28cd21e7c5SGarrett D'Amore * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved. 29*abe68f2cSRobert Mustacchi * Copyright 2016 Joyent, Inc. 30cd21e7c5SGarrett D'Amore */ 31cd21e7c5SGarrett D'Amore 32cd21e7c5SGarrett D'Amore /* 3370025d76Sjohnny * Host to PCI-Express local bus driver 3470025d76Sjohnny */ 3570025d76Sjohnny 3670025d76Sjohnny #include <sys/conf.h> 3770025d76Sjohnny #include <sys/modctl.h> 3826947304SEvan Yan #include <sys/file.h> 3970025d76Sjohnny #include <sys/pci_impl.h> 40eae2e508Skrishnae #include <sys/pcie_impl.h> 4170025d76Sjohnny #include <sys/sysmacros.h> 4270025d76Sjohnny #include <sys/ddi_intr.h> 4370025d76Sjohnny #include <sys/sunndi.h> 4400d0963fSdilpreet #include <sys/sunddi.h> 4500d0963fSdilpreet #include <sys/ddifm.h> 4600d0963fSdilpreet #include <sys/ndifm.h> 4700d0963fSdilpreet #include <sys/fm/util.h> 4826947304SEvan Yan #include <sys/hotplug/pci/pcie_hp.h> 49c333dd99Sdm120769 #include <io/pci/pci_tools_ext.h> 50eae2e508Skrishnae #include <io/pci/pci_common.h> 51eae2e508Skrishnae #include <io/pciex/pcie_nvidia.h> 5270025d76Sjohnny 5370025d76Sjohnny /* 5414f1dfe8SSeth Goldberg * Helper Macros 5514f1dfe8SSeth Goldberg */ 5614f1dfe8SSeth Goldberg #define NPE_IS_HANDLE_FOR_STDCFG_ACC(hp) \ 5714f1dfe8SSeth Goldberg ((hp) != NULL && \ 5814f1dfe8SSeth Goldberg ((ddi_acc_hdl_t *)(hp))->ah_platform_private != NULL && \ 5914f1dfe8SSeth Goldberg (((ddi_acc_impl_t *)((ddi_acc_hdl_t *)(hp))-> \ 6014f1dfe8SSeth Goldberg ah_platform_private)-> \ 6114f1dfe8SSeth Goldberg ahi_acc_attr &(DDI_ACCATTR_CPU_VADDR|DDI_ACCATTR_CONFIG_SPACE)) \ 6214f1dfe8SSeth Goldberg == DDI_ACCATTR_CONFIG_SPACE) 6314f1dfe8SSeth Goldberg 6414f1dfe8SSeth Goldberg /* 6570025d76Sjohnny * Bus Operation functions 6670025d76Sjohnny */ 6770025d76Sjohnny static int npe_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *, 6870025d76Sjohnny off_t, off_t, caddr_t *); 6970025d76Sjohnny static int npe_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, 7070025d76Sjohnny void *, void *); 7170025d76Sjohnny static int npe_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t, 7270025d76Sjohnny ddi_intr_handle_impl_t *, void *); 7300d0963fSdilpreet static int npe_fm_init(dev_info_t *, dev_info_t *, int, 7400d0963fSdilpreet ddi_iblock_cookie_t *); 7500d0963fSdilpreet 7600d0963fSdilpreet static int npe_fm_callback(dev_info_t *, ddi_fm_error_t *, const void *); 7770025d76Sjohnny 78eae2e508Skrishnae /* 79eae2e508Skrishnae * Disable URs and Received MA for all PCIe devices. Until x86 SW is changed so 80eae2e508Skrishnae * that random drivers do not do PIO accesses on devices that it does not own, 81eae2e508Skrishnae * these error bits must be disabled. SERR must also be disabled if URs have 82eae2e508Skrishnae * been masked. 83eae2e508Skrishnae */ 84eae2e508Skrishnae uint32_t npe_aer_uce_mask = PCIE_AER_UCE_UR; 85eae2e508Skrishnae uint32_t npe_aer_ce_mask = 0; 86eae2e508Skrishnae uint32_t npe_aer_suce_mask = PCIE_AER_SUCE_RCVD_MA; 87eae2e508Skrishnae 8870025d76Sjohnny struct bus_ops npe_bus_ops = { 8970025d76Sjohnny BUSO_REV, 9070025d76Sjohnny npe_bus_map, 9170025d76Sjohnny NULL, 9270025d76Sjohnny NULL, 9370025d76Sjohnny NULL, 9470025d76Sjohnny i_ddi_map_fault, 95cd21e7c5SGarrett D'Amore NULL, 9670025d76Sjohnny ddi_dma_allochdl, 9770025d76Sjohnny ddi_dma_freehdl, 9870025d76Sjohnny ddi_dma_bindhdl, 9970025d76Sjohnny ddi_dma_unbindhdl, 10070025d76Sjohnny ddi_dma_flush, 10170025d76Sjohnny ddi_dma_win, 10270025d76Sjohnny ddi_dma_mctl, 10370025d76Sjohnny npe_ctlops, 10470025d76Sjohnny ddi_bus_prop_op, 10570025d76Sjohnny 0, /* (*bus_get_eventcookie)(); */ 10670025d76Sjohnny 0, /* (*bus_add_eventcall)(); */ 10770025d76Sjohnny 0, /* (*bus_remove_eventcall)(); */ 10870025d76Sjohnny 0, /* (*bus_post_event)(); */ 10970025d76Sjohnny 0, /* (*bus_intr_ctl)(); */ 11070025d76Sjohnny 0, /* (*bus_config)(); */ 11170025d76Sjohnny 0, /* (*bus_unconfig)(); */ 11200d0963fSdilpreet npe_fm_init, /* (*bus_fm_init)(); */ 11370025d76Sjohnny NULL, /* (*bus_fm_fini)(); */ 11470025d76Sjohnny NULL, /* (*bus_fm_access_enter)(); */ 11570025d76Sjohnny NULL, /* (*bus_fm_access_exit)(); */ 11670025d76Sjohnny NULL, /* (*bus_power)(); */ 11726947304SEvan Yan npe_intr_ops, /* (*bus_intr_op)(); */ 11826947304SEvan Yan pcie_hp_common_ops /* (*bus_hp_op)(); */ 11970025d76Sjohnny }; 12070025d76Sjohnny 12170025d76Sjohnny static int npe_open(dev_t *, int, int, cred_t *); 12270025d76Sjohnny static int npe_close(dev_t, int, int, cred_t *); 12370025d76Sjohnny static int npe_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 12470025d76Sjohnny 12570025d76Sjohnny struct cb_ops npe_cb_ops = { 12670025d76Sjohnny npe_open, /* open */ 12770025d76Sjohnny npe_close, /* close */ 12870025d76Sjohnny nodev, /* strategy */ 12970025d76Sjohnny nodev, /* print */ 13070025d76Sjohnny nodev, /* dump */ 13170025d76Sjohnny nodev, /* read */ 13270025d76Sjohnny nodev, /* write */ 13370025d76Sjohnny npe_ioctl, /* ioctl */ 13470025d76Sjohnny nodev, /* devmap */ 13570025d76Sjohnny nodev, /* mmap */ 13670025d76Sjohnny nodev, /* segmap */ 13770025d76Sjohnny nochpoll, /* poll */ 13826947304SEvan Yan pcie_prop_op, /* cb_prop_op */ 13970025d76Sjohnny NULL, /* streamtab */ 14070025d76Sjohnny D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 14170025d76Sjohnny CB_REV, /* rev */ 14270025d76Sjohnny nodev, /* int (*cb_aread)() */ 14370025d76Sjohnny nodev /* int (*cb_awrite)() */ 14470025d76Sjohnny }; 14570025d76Sjohnny 14670025d76Sjohnny 14770025d76Sjohnny /* 14870025d76Sjohnny * Device Node Operation functions 14970025d76Sjohnny */ 15070025d76Sjohnny static int npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); 15170025d76Sjohnny static int npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd); 15226947304SEvan Yan static int npe_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 15370025d76Sjohnny 15470025d76Sjohnny struct dev_ops npe_ops = { 15570025d76Sjohnny DEVO_REV, /* devo_rev */ 15670025d76Sjohnny 0, /* refcnt */ 157247980beSanish npe_info, /* info */ 15870025d76Sjohnny nulldev, /* identify */ 15970025d76Sjohnny nulldev, /* probe */ 16070025d76Sjohnny npe_attach, /* attach */ 16170025d76Sjohnny npe_detach, /* detach */ 16270025d76Sjohnny nulldev, /* reset */ 16370025d76Sjohnny &npe_cb_ops, /* driver operations */ 16419397407SSherry Moore &npe_bus_ops, /* bus operations */ 16519397407SSherry Moore NULL, /* power */ 16619397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 16770025d76Sjohnny }; 16870025d76Sjohnny 16970025d76Sjohnny /* 17070025d76Sjohnny * Internal routines in support of particular npe_ctlops. 17170025d76Sjohnny */ 17270025d76Sjohnny static int npe_removechild(dev_info_t *child); 17370025d76Sjohnny static int npe_initchild(dev_info_t *child); 17470025d76Sjohnny 17570025d76Sjohnny /* 17670025d76Sjohnny * External support routine 17770025d76Sjohnny */ 17870025d76Sjohnny extern void npe_query_acpi_mcfg(dev_info_t *dip); 179337fc9e2Sanish extern void npe_ck804_fix_aer_ptr(ddi_acc_handle_t cfg_hdl); 1807a23d100Sanish extern int npe_disable_empty_bridges_workaround(dev_info_t *child); 181a2de976fSPavel Potoplyak extern void npe_nvidia_error_workaround(ddi_acc_handle_t cfg_hdl); 182a2de976fSPavel Potoplyak extern void npe_intel_error_workaround(ddi_acc_handle_t cfg_hdl); 18314f1dfe8SSeth Goldberg extern boolean_t npe_is_mmcfg_supported(dev_info_t *dip); 184cb7ea99dSJimmy Vetayases extern void npe_enable_htmsi_children(dev_info_t *dip); 185cb7ea99dSJimmy Vetayases extern int npe_save_htconfig_children(dev_info_t *dip); 186cb7ea99dSJimmy Vetayases extern int npe_restore_htconfig_children(dev_info_t *dip); 18770025d76Sjohnny 18870025d76Sjohnny /* 18970025d76Sjohnny * Module linkage information for the kernel. 19070025d76Sjohnny */ 19170025d76Sjohnny static struct modldrv modldrv = { 19270025d76Sjohnny &mod_driverops, /* Type of module */ 19326947304SEvan Yan "Host to PCIe nexus driver", /* Name of module */ 19470025d76Sjohnny &npe_ops, /* driver ops */ 19570025d76Sjohnny }; 19670025d76Sjohnny 19770025d76Sjohnny static struct modlinkage modlinkage = { 19870025d76Sjohnny MODREV_1, 19970025d76Sjohnny (void *)&modldrv, 20070025d76Sjohnny NULL 20170025d76Sjohnny }; 20270025d76Sjohnny 20370025d76Sjohnny /* Save minimal state. */ 20470025d76Sjohnny void *npe_statep; 20570025d76Sjohnny 20670025d76Sjohnny int 20770025d76Sjohnny _init(void) 20870025d76Sjohnny { 20970025d76Sjohnny int e; 21070025d76Sjohnny 21170025d76Sjohnny /* 21270025d76Sjohnny * Initialize per-pci bus soft state pointer. 21370025d76Sjohnny */ 21470025d76Sjohnny e = ddi_soft_state_init(&npe_statep, sizeof (pci_state_t), 1); 21570025d76Sjohnny if (e != 0) 21670025d76Sjohnny return (e); 21770025d76Sjohnny 21870025d76Sjohnny if ((e = mod_install(&modlinkage)) != 0) 21970025d76Sjohnny ddi_soft_state_fini(&npe_statep); 22070025d76Sjohnny 22170025d76Sjohnny return (e); 22270025d76Sjohnny } 22370025d76Sjohnny 22470025d76Sjohnny 22570025d76Sjohnny int 22670025d76Sjohnny _fini(void) 22770025d76Sjohnny { 22870025d76Sjohnny int rc; 22970025d76Sjohnny 23070025d76Sjohnny rc = mod_remove(&modlinkage); 23170025d76Sjohnny if (rc != 0) 23270025d76Sjohnny return (rc); 23370025d76Sjohnny 23470025d76Sjohnny ddi_soft_state_fini(&npe_statep); 23570025d76Sjohnny return (rc); 23670025d76Sjohnny } 23770025d76Sjohnny 23870025d76Sjohnny 23970025d76Sjohnny int 24070025d76Sjohnny _info(struct modinfo *modinfop) 24170025d76Sjohnny { 24270025d76Sjohnny return (mod_info(&modlinkage, modinfop)); 24370025d76Sjohnny } 24470025d76Sjohnny 24570025d76Sjohnny /*ARGSUSED*/ 24670025d76Sjohnny static int 24726947304SEvan Yan npe_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 24826947304SEvan Yan { 24926947304SEvan Yan minor_t minor = getminor((dev_t)arg); 25026947304SEvan Yan int instance = PCI_MINOR_NUM_TO_INSTANCE(minor); 25126947304SEvan Yan pci_state_t *pcip = ddi_get_soft_state(npe_statep, instance); 25226947304SEvan Yan int ret = DDI_SUCCESS; 25326947304SEvan Yan 25426947304SEvan Yan switch (cmd) { 25526947304SEvan Yan case DDI_INFO_DEVT2INSTANCE: 25626947304SEvan Yan *result = (void *)(intptr_t)instance; 25726947304SEvan Yan break; 25826947304SEvan Yan case DDI_INFO_DEVT2DEVINFO: 25926947304SEvan Yan if (pcip == NULL) { 26026947304SEvan Yan ret = DDI_FAILURE; 26126947304SEvan Yan break; 26226947304SEvan Yan } 26326947304SEvan Yan 26426947304SEvan Yan *result = (void *)pcip->pci_dip; 26526947304SEvan Yan break; 26626947304SEvan Yan default: 26726947304SEvan Yan ret = DDI_FAILURE; 26826947304SEvan Yan break; 26926947304SEvan Yan } 27026947304SEvan Yan 27126947304SEvan Yan return (ret); 27226947304SEvan Yan } 27326947304SEvan Yan 27426947304SEvan Yan /*ARGSUSED*/ 27526947304SEvan Yan static int 27670025d76Sjohnny npe_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 27770025d76Sjohnny { 27870025d76Sjohnny int instance = ddi_get_instance(devi); 27970025d76Sjohnny pci_state_t *pcip = NULL; 28070025d76Sjohnny 281cb7ea99dSJimmy Vetayases if (cmd == DDI_RESUME) { 282cb7ea99dSJimmy Vetayases /* 283cb7ea99dSJimmy Vetayases * the system might still be able to resume even if this fails 284cb7ea99dSJimmy Vetayases */ 285cb7ea99dSJimmy Vetayases (void) npe_restore_htconfig_children(devi); 2862df1fe9cSrandyf return (DDI_SUCCESS); 287cb7ea99dSJimmy Vetayases } 288cb7ea99dSJimmy Vetayases 289cb7ea99dSJimmy Vetayases /* 290cb7ea99dSJimmy Vetayases * We must do this here in order to ensure that all top level devices 291cb7ea99dSJimmy Vetayases * get their HyperTransport MSI mapping regs programmed first. 292cb7ea99dSJimmy Vetayases * "Memory controller" and "hostbridge" class devices are leaf devices 293cb7ea99dSJimmy Vetayases * that may affect MSI translation functionality for devices 294cb7ea99dSJimmy Vetayases * connected to the same link/bus. 295cb7ea99dSJimmy Vetayases * 296cb7ea99dSJimmy Vetayases * This will also program HT MSI mapping registers on root buses 297cb7ea99dSJimmy Vetayases * devices (basically sitting on an HT bus) that are not dependent 298cb7ea99dSJimmy Vetayases * on the aforementioned HT devices for MSI translation. 299cb7ea99dSJimmy Vetayases */ 300cb7ea99dSJimmy Vetayases npe_enable_htmsi_children(devi); 3012df1fe9cSrandyf 30270025d76Sjohnny if (ddi_prop_update_string(DDI_DEV_T_NONE, devi, "device_type", 30370025d76Sjohnny "pciex") != DDI_PROP_SUCCESS) { 30470025d76Sjohnny cmn_err(CE_WARN, "npe: 'device_type' prop create failed"); 30570025d76Sjohnny } 30670025d76Sjohnny 30770025d76Sjohnny if (ddi_soft_state_zalloc(npe_statep, instance) == DDI_SUCCESS) 30870025d76Sjohnny pcip = ddi_get_soft_state(npe_statep, instance); 30970025d76Sjohnny 31070025d76Sjohnny if (pcip == NULL) 31170025d76Sjohnny return (DDI_FAILURE); 31270025d76Sjohnny 31370025d76Sjohnny pcip->pci_dip = devi; 31426947304SEvan Yan pcip->pci_soft_state = PCI_SOFT_STATE_CLOSED; 31570025d76Sjohnny 31626947304SEvan Yan if (pcie_init(devi, NULL) != DDI_SUCCESS) 31726947304SEvan Yan goto fail1; 31870025d76Sjohnny 3197a364d25Sschwartz /* Second arg: initialize for pci_express root nexus */ 32026947304SEvan Yan if (pcitool_init(devi, B_TRUE) != DDI_SUCCESS) 32126947304SEvan Yan goto fail2; 322eae2e508Skrishnae 32300d0963fSdilpreet pcip->pci_fmcap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE | 32400d0963fSdilpreet DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE; 32500d0963fSdilpreet ddi_fm_init(devi, &pcip->pci_fmcap, &pcip->pci_fm_ibc); 32600d0963fSdilpreet 327eae2e508Skrishnae if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE) { 32800d0963fSdilpreet ddi_fm_handler_register(devi, npe_fm_callback, NULL); 329eae2e508Skrishnae } 330eae2e508Skrishnae 331eae2e508Skrishnae PCIE_DIP2PFD(devi) = kmem_zalloc(sizeof (pf_data_t), KM_SLEEP); 332eae2e508Skrishnae pcie_rc_init_pfd(devi, PCIE_DIP2PFD(devi)); 33370025d76Sjohnny 33470025d76Sjohnny npe_query_acpi_mcfg(devi); 33570025d76Sjohnny ddi_report_dev(devi); 336c0da6274SZhi-Jun Robin Fu pcie_fab_init_bus(devi, PCIE_BUS_FINAL); 33726947304SEvan Yan 33870025d76Sjohnny return (DDI_SUCCESS); 33970025d76Sjohnny 34026947304SEvan Yan fail2: 34126947304SEvan Yan (void) pcie_uninit(devi); 34226947304SEvan Yan fail1: 34326947304SEvan Yan pcie_rc_fini_bus(devi); 34426947304SEvan Yan ddi_soft_state_free(npe_statep, instance); 34526947304SEvan Yan 34626947304SEvan Yan return (DDI_FAILURE); 34770025d76Sjohnny } 34870025d76Sjohnny 34970025d76Sjohnny /*ARGSUSED*/ 35070025d76Sjohnny static int 35170025d76Sjohnny npe_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 35270025d76Sjohnny { 35370025d76Sjohnny int instance = ddi_get_instance(devi); 35400d0963fSdilpreet pci_state_t *pcip; 35500d0963fSdilpreet 35600d0963fSdilpreet pcip = ddi_get_soft_state(npe_statep, ddi_get_instance(devi)); 35770025d76Sjohnny 3582df1fe9cSrandyf switch (cmd) { 3592df1fe9cSrandyf case DDI_DETACH: 360c0da6274SZhi-Jun Robin Fu pcie_fab_fini_bus(devi, PCIE_BUS_INITIAL); 3612df1fe9cSrandyf 36270025d76Sjohnny /* Uninitialize pcitool support. */ 36370025d76Sjohnny pcitool_uninit(devi); 36470025d76Sjohnny 36526947304SEvan Yan if (pcie_uninit(devi) != DDI_SUCCESS) 36626947304SEvan Yan return (DDI_FAILURE); 36700d0963fSdilpreet 36800d0963fSdilpreet if (pcip->pci_fmcap & DDI_FM_ERRCB_CAPABLE) 36900d0963fSdilpreet ddi_fm_handler_unregister(devi); 37000d0963fSdilpreet 371eae2e508Skrishnae pcie_rc_fini_pfd(PCIE_DIP2PFD(devi)); 372eae2e508Skrishnae kmem_free(PCIE_DIP2PFD(devi), sizeof (pf_data_t)); 373eae2e508Skrishnae 37400d0963fSdilpreet ddi_fm_fini(devi); 37570025d76Sjohnny ddi_soft_state_free(npe_statep, instance); 37670025d76Sjohnny return (DDI_SUCCESS); 3772df1fe9cSrandyf 3782df1fe9cSrandyf case DDI_SUSPEND: 379cb7ea99dSJimmy Vetayases /* 380cb7ea99dSJimmy Vetayases * the system might still be able to suspend/resume even if 381cb7ea99dSJimmy Vetayases * this fails 382cb7ea99dSJimmy Vetayases */ 383cb7ea99dSJimmy Vetayases (void) npe_save_htconfig_children(devi); 3842df1fe9cSrandyf return (DDI_SUCCESS); 3852df1fe9cSrandyf default: 3862df1fe9cSrandyf return (DDI_FAILURE); 3872df1fe9cSrandyf } 38870025d76Sjohnny } 38970025d76Sjohnny 39014f1dfe8SSeth Goldberg /* 39114f1dfe8SSeth Goldberg * Configure the access handle for standard configuration space 39214f1dfe8SSeth Goldberg * access (see pci_fm_acc_setup for code that initializes the 39314f1dfe8SSeth Goldberg * access-function pointers). 39414f1dfe8SSeth Goldberg */ 39514f1dfe8SSeth Goldberg static int 39614f1dfe8SSeth Goldberg npe_setup_std_pcicfg_acc(dev_info_t *rdip, ddi_map_req_t *mp, 39714f1dfe8SSeth Goldberg ddi_acc_hdl_t *hp, off_t offset, off_t len) 39814f1dfe8SSeth Goldberg { 39914f1dfe8SSeth Goldberg int ret; 40014f1dfe8SSeth Goldberg 40114f1dfe8SSeth Goldberg if ((ret = pci_fm_acc_setup(hp, offset, len)) == 40214f1dfe8SSeth Goldberg DDI_SUCCESS) { 40314f1dfe8SSeth Goldberg if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(rdip)) && 40414f1dfe8SSeth Goldberg mp->map_handlep->ah_acc.devacc_attr_access 40514f1dfe8SSeth Goldberg != DDI_DEFAULT_ACC) { 40614f1dfe8SSeth Goldberg ndi_fmc_insert(rdip, ACC_HANDLE, 40714f1dfe8SSeth Goldberg (void *)mp->map_handlep, NULL); 40814f1dfe8SSeth Goldberg } 40914f1dfe8SSeth Goldberg } 41014f1dfe8SSeth Goldberg return (ret); 41114f1dfe8SSeth Goldberg } 41214f1dfe8SSeth Goldberg 41370025d76Sjohnny static int 41470025d76Sjohnny npe_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 41570025d76Sjohnny off_t offset, off_t len, caddr_t *vaddrp) 41670025d76Sjohnny { 41770025d76Sjohnny int rnumber; 41870025d76Sjohnny int space; 41900d0963fSdilpreet ddi_acc_impl_t *ap; 42070025d76Sjohnny ddi_acc_hdl_t *hp; 42170025d76Sjohnny ddi_map_req_t mr; 42270025d76Sjohnny pci_regspec_t pci_reg; 42370025d76Sjohnny pci_regspec_t *pci_rp; 424*abe68f2cSRobert Mustacchi struct regspec64 reg; 42570025d76Sjohnny pci_acc_cfblk_t *cfp; 42600d0963fSdilpreet int retval; 4275c59319bSDan Mick int64_t *ecfginfo; 4285c59319bSDan Mick uint_t nelem; 429*abe68f2cSRobert Mustacchi uint64_t pci_rlength; 43070025d76Sjohnny 43170025d76Sjohnny mr = *mp; /* Get private copy of request */ 43270025d76Sjohnny mp = &mr; 43370025d76Sjohnny 43470025d76Sjohnny /* 43570025d76Sjohnny * check for register number 43670025d76Sjohnny */ 43770025d76Sjohnny switch (mp->map_type) { 43870025d76Sjohnny case DDI_MT_REGSPEC: 43970025d76Sjohnny pci_reg = *(pci_regspec_t *)(mp->map_obj.rp); 44070025d76Sjohnny pci_rp = &pci_reg; 44170025d76Sjohnny if (pci_common_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS) 44270025d76Sjohnny return (DDI_FAILURE); 44370025d76Sjohnny break; 44470025d76Sjohnny case DDI_MT_RNUMBER: 44570025d76Sjohnny rnumber = mp->map_obj.rnumber; 44670025d76Sjohnny /* 44770025d76Sjohnny * get ALL "reg" properties for dip, select the one of 44870025d76Sjohnny * of interest. In x86, "assigned-addresses" property 44970025d76Sjohnny * is identical to the "reg" property, so there is no 45070025d76Sjohnny * need to cross check the two to determine the physical 45170025d76Sjohnny * address of the registers. 45270025d76Sjohnny * This routine still performs some validity checks to 45370025d76Sjohnny * make sure that everything is okay. 45470025d76Sjohnny */ 45570025d76Sjohnny if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 456*abe68f2cSRobert Mustacchi DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &nelem) != 457*abe68f2cSRobert Mustacchi DDI_PROP_SUCCESS) 45870025d76Sjohnny return (DDI_FAILURE); 45970025d76Sjohnny 46070025d76Sjohnny /* 46170025d76Sjohnny * validate the register number. 46270025d76Sjohnny */ 463*abe68f2cSRobert Mustacchi nelem /= (sizeof (pci_regspec_t) / sizeof (int)); 464*abe68f2cSRobert Mustacchi if (rnumber >= nelem) { 46570025d76Sjohnny ddi_prop_free(pci_rp); 46670025d76Sjohnny return (DDI_FAILURE); 46770025d76Sjohnny } 46870025d76Sjohnny 46970025d76Sjohnny /* 47070025d76Sjohnny * copy the required entry. 47170025d76Sjohnny */ 47270025d76Sjohnny pci_reg = pci_rp[rnumber]; 47370025d76Sjohnny 47470025d76Sjohnny /* 47570025d76Sjohnny * free the memory allocated by ddi_prop_lookup_int_array 47670025d76Sjohnny */ 47770025d76Sjohnny ddi_prop_free(pci_rp); 47870025d76Sjohnny 47970025d76Sjohnny pci_rp = &pci_reg; 48070025d76Sjohnny if (pci_common_get_reg_prop(rdip, pci_rp) != DDI_SUCCESS) 48170025d76Sjohnny return (DDI_FAILURE); 48270025d76Sjohnny mp->map_type = DDI_MT_REGSPEC; 48370025d76Sjohnny break; 48470025d76Sjohnny default: 48570025d76Sjohnny return (DDI_ME_INVAL); 48670025d76Sjohnny } 48770025d76Sjohnny 48870025d76Sjohnny space = pci_rp->pci_phys_hi & PCI_REG_ADDR_M; 48970025d76Sjohnny 49070025d76Sjohnny /* 49170025d76Sjohnny * check for unmap and unlock of address space 49270025d76Sjohnny */ 49370025d76Sjohnny if ((mp->map_op == DDI_MO_UNMAP) || (mp->map_op == DDI_MO_UNLOCK)) { 49470025d76Sjohnny switch (space) { 49570025d76Sjohnny case PCI_ADDR_IO: 49670025d76Sjohnny reg.regspec_bustype = 1; 49770025d76Sjohnny break; 49870025d76Sjohnny 49970025d76Sjohnny case PCI_ADDR_CONFIG: 50014f1dfe8SSeth Goldberg /* 50114f1dfe8SSeth Goldberg * If this is an unmap/unlock of a standard config 50214f1dfe8SSeth Goldberg * space mapping (memory-mapped config space mappings 50314f1dfe8SSeth Goldberg * would have the DDI_ACCATTR_CPU_VADDR bit set in the 50414f1dfe8SSeth Goldberg * acc_attr), undo that setup here. 50514f1dfe8SSeth Goldberg */ 50614f1dfe8SSeth Goldberg if (NPE_IS_HANDLE_FOR_STDCFG_ACC(mp->map_handlep)) { 50714f1dfe8SSeth Goldberg 50800d0963fSdilpreet if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(rdip)) && 50900d0963fSdilpreet mp->map_handlep->ah_acc.devacc_attr_access 51000d0963fSdilpreet != DDI_DEFAULT_ACC) { 51100d0963fSdilpreet ndi_fmc_remove(rdip, ACC_HANDLE, 51200d0963fSdilpreet (void *)mp->map_handlep); 51300d0963fSdilpreet } 514247980beSanish return (DDI_SUCCESS); 51500d0963fSdilpreet } 51600d0963fSdilpreet 51749fbdd30SErwin T Tsaur pci_rp->pci_size_low = PCIE_CONF_HDR_SIZE; 518247980beSanish 519649d4cceSanish /* FALLTHROUGH */ 52070025d76Sjohnny case PCI_ADDR_MEM64: 52170025d76Sjohnny case PCI_ADDR_MEM32: 52270025d76Sjohnny reg.regspec_bustype = 0; 52370025d76Sjohnny break; 52470025d76Sjohnny 52570025d76Sjohnny default: 52670025d76Sjohnny return (DDI_FAILURE); 52770025d76Sjohnny } 52800d0963fSdilpreet 529*abe68f2cSRobert Mustacchi reg.regspec_addr = (uint64_t)pci_rp->pci_phys_mid << 32 | 530*abe68f2cSRobert Mustacchi (uint64_t)pci_rp->pci_phys_low; 531*abe68f2cSRobert Mustacchi reg.regspec_size = (uint64_t)pci_rp->pci_size_hi << 32 | 532*abe68f2cSRobert Mustacchi (uint64_t)pci_rp->pci_size_low; 533*abe68f2cSRobert Mustacchi 53400d0963fSdilpreet /* 53500d0963fSdilpreet * Adjust offset and length 53600d0963fSdilpreet * A non-zero length means override the one in the regspec. 53700d0963fSdilpreet */ 538*abe68f2cSRobert Mustacchi if (reg.regspec_addr + offset < MAX(reg.regspec_addr, offset)) 539*abe68f2cSRobert Mustacchi return (DDI_FAILURE); 540*abe68f2cSRobert Mustacchi reg.regspec_addr += offset; 54100d0963fSdilpreet if (len != 0) 542*abe68f2cSRobert Mustacchi reg.regspec_size = len; 54300d0963fSdilpreet 544*abe68f2cSRobert Mustacchi mp->map_obj.rp = (struct regspec *)® 545*abe68f2cSRobert Mustacchi mp->map_flags |= DDI_MF_EXT_REGSPEC; 54600d0963fSdilpreet retval = ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp); 54700d0963fSdilpreet if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(rdip)) && 54800d0963fSdilpreet mp->map_handlep->ah_acc.devacc_attr_access != 54900d0963fSdilpreet DDI_DEFAULT_ACC) { 55000d0963fSdilpreet ndi_fmc_remove(rdip, ACC_HANDLE, 55100d0963fSdilpreet (void *)mp->map_handlep); 55200d0963fSdilpreet } 55300d0963fSdilpreet return (retval); 55470025d76Sjohnny 55570025d76Sjohnny } 55670025d76Sjohnny 55770025d76Sjohnny /* check for user mapping request - not legal for Config */ 55870025d76Sjohnny if (mp->map_op == DDI_MO_MAP_HANDLE && space == PCI_ADDR_CONFIG) { 55970025d76Sjohnny cmn_err(CE_NOTE, "npe: Config mapping request from user\n"); 56070025d76Sjohnny return (DDI_FAILURE); 56170025d76Sjohnny } 56270025d76Sjohnny 56370025d76Sjohnny 56400d0963fSdilpreet /* 56500d0963fSdilpreet * Note that pci_fm_acc_setup() is called to serve two purposes 56600d0963fSdilpreet * i) enable legacy PCI I/O style config space access 56700d0963fSdilpreet * ii) register with FMA 56800d0963fSdilpreet */ 56970025d76Sjohnny if (space == PCI_ADDR_CONFIG) { 57014f1dfe8SSeth Goldberg 57170025d76Sjohnny /* Can't map config space without a handle */ 57270025d76Sjohnny hp = (ddi_acc_hdl_t *)mp->map_handlep; 57370025d76Sjohnny if (hp == NULL) 57470025d76Sjohnny return (DDI_FAILURE); 57570025d76Sjohnny 57670025d76Sjohnny /* record the device address for future reference */ 57770025d76Sjohnny cfp = (pci_acc_cfblk_t *)&hp->ah_bus_private; 57870025d76Sjohnny cfp->c_busnum = PCI_REG_BUS_G(pci_rp->pci_phys_hi); 57970025d76Sjohnny cfp->c_devnum = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 58070025d76Sjohnny cfp->c_funcnum = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 58170025d76Sjohnny 58200d0963fSdilpreet *vaddrp = (caddr_t)offset; 583649d4cceSanish 58449fbdd30SErwin T Tsaur /* Check if MMCFG is supported */ 58514f1dfe8SSeth Goldberg if (!npe_is_mmcfg_supported(rdip)) { 58614f1dfe8SSeth Goldberg return (npe_setup_std_pcicfg_acc(rdip, mp, hp, 58714f1dfe8SSeth Goldberg offset, len)); 58814f1dfe8SSeth Goldberg } 589247980beSanish 590247980beSanish 5915c59319bSDan Mick if (ddi_prop_lookup_int64_array(DDI_DEV_T_ANY, rdip, 0, 5925c59319bSDan Mick "ecfg", &ecfginfo, &nelem) == DDI_PROP_SUCCESS) { 5935c59319bSDan Mick 59414f1dfe8SSeth Goldberg if (nelem != 4 || 59514f1dfe8SSeth Goldberg cfp->c_busnum < ecfginfo[2] || 5965c59319bSDan Mick cfp->c_busnum > ecfginfo[3]) { 59714f1dfe8SSeth Goldberg /* 59814f1dfe8SSeth Goldberg * Invalid property or Doesn't contain the 59914f1dfe8SSeth Goldberg * requested bus; fall back to standard 60014f1dfe8SSeth Goldberg * (I/O-based) config access. 60114f1dfe8SSeth Goldberg */ 6025c59319bSDan Mick ddi_prop_free(ecfginfo); 60314f1dfe8SSeth Goldberg return (npe_setup_std_pcicfg_acc(rdip, mp, hp, 60414f1dfe8SSeth Goldberg offset, len)); 60514f1dfe8SSeth Goldberg } else { 6065c59319bSDan Mick pci_rp->pci_phys_low = ecfginfo[0]; 6075c59319bSDan Mick 6085c59319bSDan Mick ddi_prop_free(ecfginfo); 609649d4cceSanish 61070025d76Sjohnny pci_rp->pci_phys_low += ((cfp->c_busnum << 20) | 61114f1dfe8SSeth Goldberg (cfp->c_devnum) << 15 | 61214f1dfe8SSeth Goldberg (cfp->c_funcnum << 12)); 61370025d76Sjohnny 61470025d76Sjohnny pci_rp->pci_size_low = PCIE_CONF_HDR_SIZE; 61514f1dfe8SSeth Goldberg } 6165c59319bSDan Mick } else { 61714f1dfe8SSeth Goldberg /* 61814f1dfe8SSeth Goldberg * Couldn't find the MMCFG property -- fall back to 61914f1dfe8SSeth Goldberg * standard config access 62014f1dfe8SSeth Goldberg */ 62114f1dfe8SSeth Goldberg return (npe_setup_std_pcicfg_acc(rdip, mp, hp, 62214f1dfe8SSeth Goldberg offset, len)); 6235c59319bSDan Mick } 62470025d76Sjohnny } 62570025d76Sjohnny 62670025d76Sjohnny /* 62770025d76Sjohnny * range check 62870025d76Sjohnny */ 629*abe68f2cSRobert Mustacchi pci_rlength = (uint64_t)pci_rp->pci_size_low | 630*abe68f2cSRobert Mustacchi (uint64_t)pci_rp->pci_size_hi << 32; 631*abe68f2cSRobert Mustacchi if ((offset >= pci_rlength) || (len > pci_rlength) || 632*abe68f2cSRobert Mustacchi (offset + len > pci_rlength) || (offset + len < MAX(offset, len))) { 63370025d76Sjohnny return (DDI_FAILURE); 634*abe68f2cSRobert Mustacchi } 63570025d76Sjohnny 63670025d76Sjohnny /* 63770025d76Sjohnny * convert the pci regsec into the generic regspec used by the 63870025d76Sjohnny * parent root nexus driver. 63970025d76Sjohnny */ 64070025d76Sjohnny switch (space) { 64170025d76Sjohnny case PCI_ADDR_IO: 64270025d76Sjohnny reg.regspec_bustype = 1; 64370025d76Sjohnny break; 64470025d76Sjohnny case PCI_ADDR_CONFIG: 64570025d76Sjohnny case PCI_ADDR_MEM64: 64670025d76Sjohnny case PCI_ADDR_MEM32: 64770025d76Sjohnny reg.regspec_bustype = 0; 64870025d76Sjohnny break; 64970025d76Sjohnny default: 65070025d76Sjohnny return (DDI_FAILURE); 65170025d76Sjohnny } 65270025d76Sjohnny 653*abe68f2cSRobert Mustacchi reg.regspec_addr = (uint64_t)pci_rp->pci_phys_mid << 32 | 654*abe68f2cSRobert Mustacchi (uint64_t)pci_rp->pci_phys_low; 655*abe68f2cSRobert Mustacchi reg.regspec_size = pci_rlength; 65670025d76Sjohnny 657*abe68f2cSRobert Mustacchi /* 658*abe68f2cSRobert Mustacchi * Adjust offset and length 659*abe68f2cSRobert Mustacchi * A non-zero length means override the one in the regspec. 660*abe68f2cSRobert Mustacchi */ 661*abe68f2cSRobert Mustacchi if (reg.regspec_addr + offset < MAX(reg.regspec_addr, offset)) 662*abe68f2cSRobert Mustacchi return (DDI_FAILURE); 663*abe68f2cSRobert Mustacchi reg.regspec_addr += offset; 664*abe68f2cSRobert Mustacchi if (len != 0) 665*abe68f2cSRobert Mustacchi reg.regspec_size = len; 666*abe68f2cSRobert Mustacchi 667*abe68f2cSRobert Mustacchi 668*abe68f2cSRobert Mustacchi mp->map_obj.rp = (struct regspec *)® 669*abe68f2cSRobert Mustacchi mp->map_flags |= DDI_MF_EXT_REGSPEC; 67000d0963fSdilpreet retval = ddi_map(dip, mp, (off_t)0, (off_t)0, vaddrp); 67100d0963fSdilpreet if (retval == DDI_SUCCESS) { 672649d4cceSanish /* 67300d0963fSdilpreet * For config space gets force use of cautious access routines. 67400d0963fSdilpreet * These will handle default and protected mode accesses too. 675649d4cceSanish */ 67600d0963fSdilpreet if (space == PCI_ADDR_CONFIG) { 67700d0963fSdilpreet ap = (ddi_acc_impl_t *)mp->map_handlep; 67800d0963fSdilpreet ap->ahi_acc_attr &= ~DDI_ACCATTR_DIRECT; 679649d4cceSanish ap->ahi_acc_attr |= DDI_ACCATTR_CONFIG_SPACE; 68000d0963fSdilpreet ap->ahi_get8 = i_ddi_caut_get8; 68100d0963fSdilpreet ap->ahi_get16 = i_ddi_caut_get16; 68200d0963fSdilpreet ap->ahi_get32 = i_ddi_caut_get32; 68300d0963fSdilpreet ap->ahi_get64 = i_ddi_caut_get64; 68400d0963fSdilpreet ap->ahi_rep_get8 = i_ddi_caut_rep_get8; 68500d0963fSdilpreet ap->ahi_rep_get16 = i_ddi_caut_rep_get16; 68600d0963fSdilpreet ap->ahi_rep_get32 = i_ddi_caut_rep_get32; 68700d0963fSdilpreet ap->ahi_rep_get64 = i_ddi_caut_rep_get64; 688649d4cceSanish } 68900d0963fSdilpreet if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(rdip)) && 69000d0963fSdilpreet mp->map_handlep->ah_acc.devacc_attr_access != 69100d0963fSdilpreet DDI_DEFAULT_ACC) { 69200d0963fSdilpreet ndi_fmc_insert(rdip, ACC_HANDLE, 69300d0963fSdilpreet (void *)mp->map_handlep, NULL); 69400d0963fSdilpreet } 69500d0963fSdilpreet } 69600d0963fSdilpreet return (retval); 69700d0963fSdilpreet } 69800d0963fSdilpreet 699649d4cceSanish 700649d4cceSanish 70170025d76Sjohnny /*ARGSUSED*/ 70270025d76Sjohnny static int 70370025d76Sjohnny npe_ctlops(dev_info_t *dip, dev_info_t *rdip, 70470025d76Sjohnny ddi_ctl_enum_t ctlop, void *arg, void *result) 70570025d76Sjohnny { 70670025d76Sjohnny int totreg; 70770025d76Sjohnny uint_t reglen; 70870025d76Sjohnny pci_regspec_t *drv_regp; 7092df1fe9cSrandyf struct attachspec *asp; 710eae2e508Skrishnae struct detachspec *dsp; 711eae2e508Skrishnae pci_state_t *pci_p = ddi_get_soft_state(npe_statep, 712eae2e508Skrishnae ddi_get_instance(dip)); 71370025d76Sjohnny 71470025d76Sjohnny switch (ctlop) { 71570025d76Sjohnny case DDI_CTLOPS_REPORTDEV: 71670025d76Sjohnny if (rdip == (dev_info_t *)0) 71770025d76Sjohnny return (DDI_FAILURE); 71870025d76Sjohnny cmn_err(CE_CONT, "?PCI Express-device: %s@%s, %s%d\n", 71970025d76Sjohnny ddi_node_name(rdip), ddi_get_name_addr(rdip), 72070025d76Sjohnny ddi_driver_name(rdip), ddi_get_instance(rdip)); 72170025d76Sjohnny return (DDI_SUCCESS); 72270025d76Sjohnny 72370025d76Sjohnny case DDI_CTLOPS_INITCHILD: 72470025d76Sjohnny return (npe_initchild((dev_info_t *)arg)); 72570025d76Sjohnny 72670025d76Sjohnny case DDI_CTLOPS_UNINITCHILD: 72770025d76Sjohnny return (npe_removechild((dev_info_t *)arg)); 72870025d76Sjohnny 72970025d76Sjohnny case DDI_CTLOPS_SIDDEV: 73070025d76Sjohnny return (DDI_SUCCESS); 73170025d76Sjohnny 73270025d76Sjohnny case DDI_CTLOPS_REGSIZE: 73370025d76Sjohnny case DDI_CTLOPS_NREGS: 73470025d76Sjohnny if (rdip == (dev_info_t *)0) 73570025d76Sjohnny return (DDI_FAILURE); 73670025d76Sjohnny 73770025d76Sjohnny *(int *)result = 0; 73870025d76Sjohnny if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip, 73970025d76Sjohnny DDI_PROP_DONTPASS, "reg", (int **)&drv_regp, 74070025d76Sjohnny ®len) != DDI_PROP_SUCCESS) { 74170025d76Sjohnny return (DDI_FAILURE); 74270025d76Sjohnny } 74370025d76Sjohnny 74470025d76Sjohnny totreg = (reglen * sizeof (int)) / sizeof (pci_regspec_t); 74570025d76Sjohnny if (ctlop == DDI_CTLOPS_NREGS) 74670025d76Sjohnny *(int *)result = totreg; 74770025d76Sjohnny else if (ctlop == DDI_CTLOPS_REGSIZE) { 748*abe68f2cSRobert Mustacchi uint64_t val; 749*abe68f2cSRobert Mustacchi int rn; 750*abe68f2cSRobert Mustacchi 75170025d76Sjohnny rn = *(int *)arg; 75270025d76Sjohnny if (rn >= totreg) { 75370025d76Sjohnny ddi_prop_free(drv_regp); 75470025d76Sjohnny return (DDI_FAILURE); 75570025d76Sjohnny } 756*abe68f2cSRobert Mustacchi val = drv_regp[rn].pci_size_low | 757*abe68f2cSRobert Mustacchi (uint64_t)drv_regp[rn].pci_size_hi << 32; 758*abe68f2cSRobert Mustacchi if (val > OFF_MAX) { 759*abe68f2cSRobert Mustacchi int ce = CE_NOTE; 760*abe68f2cSRobert Mustacchi #ifdef DEBUG 761*abe68f2cSRobert Mustacchi ce = CE_WARN; 762*abe68f2cSRobert Mustacchi #endif 763*abe68f2cSRobert Mustacchi dev_err(rdip, ce, "failed to get register " 764*abe68f2cSRobert Mustacchi "size, value larger than OFF_MAX: 0x%" 765*abe68f2cSRobert Mustacchi PRIx64 "\n", val); 766*abe68f2cSRobert Mustacchi return (DDI_FAILURE); 767*abe68f2cSRobert Mustacchi } 768*abe68f2cSRobert Mustacchi *(off_t *)result = (off_t)val; 76970025d76Sjohnny } 77070025d76Sjohnny ddi_prop_free(drv_regp); 77170025d76Sjohnny 77270025d76Sjohnny return (DDI_SUCCESS); 77370025d76Sjohnny 77470025d76Sjohnny case DDI_CTLOPS_POWER: 77570025d76Sjohnny { 77670025d76Sjohnny power_req_t *reqp = (power_req_t *)arg; 77770025d76Sjohnny /* 77870025d76Sjohnny * We currently understand reporting of PCI_PM_IDLESPEED 77970025d76Sjohnny * capability. Everything else is passed up. 78070025d76Sjohnny */ 78170025d76Sjohnny if ((reqp->request_type == PMR_REPORT_PMCAP) && 78270025d76Sjohnny (reqp->req.report_pmcap_req.cap == PCI_PM_IDLESPEED)) 78370025d76Sjohnny return (DDI_SUCCESS); 78470025d76Sjohnny 78570025d76Sjohnny break; 78670025d76Sjohnny } 78770025d76Sjohnny 78800d0963fSdilpreet case DDI_CTLOPS_PEEK: 78900d0963fSdilpreet case DDI_CTLOPS_POKE: 79000d0963fSdilpreet return (pci_common_peekpoke(dip, rdip, ctlop, arg, result)); 79100d0963fSdilpreet 7922df1fe9cSrandyf /* X86 systems support PME wakeup from suspended state */ 7932df1fe9cSrandyf case DDI_CTLOPS_ATTACH: 794eae2e508Skrishnae if (!pcie_is_child(dip, rdip)) 795eae2e508Skrishnae return (DDI_SUCCESS); 796eae2e508Skrishnae 7972df1fe9cSrandyf asp = (struct attachspec *)arg; 798eae2e508Skrishnae if ((asp->when == DDI_POST) && (asp->result == DDI_SUCCESS)) { 799eae2e508Skrishnae pf_init(rdip, (void *)pci_p->pci_fm_ibc, asp->cmd); 800eae2e508Skrishnae (void) pcie_postattach_child(rdip); 801eae2e508Skrishnae } 802eae2e508Skrishnae 8032df1fe9cSrandyf /* only do this for immediate children */ 8042df1fe9cSrandyf if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE && 8052df1fe9cSrandyf ddi_get_parent(rdip) == dip) 8062df1fe9cSrandyf if (pci_pre_resume(rdip) != DDI_SUCCESS) { 8072df1fe9cSrandyf /* Not good, better stop now. */ 8082df1fe9cSrandyf cmn_err(CE_PANIC, 8092df1fe9cSrandyf "Couldn't pre-resume device %p", 8102df1fe9cSrandyf (void *) dip); 8112df1fe9cSrandyf /* NOTREACHED */ 8122df1fe9cSrandyf } 813eae2e508Skrishnae 814eae2e508Skrishnae return (DDI_SUCCESS); 8152df1fe9cSrandyf 8162df1fe9cSrandyf case DDI_CTLOPS_DETACH: 817eae2e508Skrishnae if (!pcie_is_child(dip, rdip)) 818eae2e508Skrishnae return (DDI_SUCCESS); 819eae2e508Skrishnae 820eae2e508Skrishnae dsp = (struct detachspec *)arg; 821eae2e508Skrishnae 822eae2e508Skrishnae if (dsp->when == DDI_PRE) 823eae2e508Skrishnae pf_fini(rdip, dsp->cmd); 824eae2e508Skrishnae 8252df1fe9cSrandyf /* only do this for immediate children */ 826eae2e508Skrishnae if (dsp->cmd == DDI_SUSPEND && dsp->when == DDI_POST && 8272df1fe9cSrandyf ddi_get_parent(rdip) == dip) 8282df1fe9cSrandyf if (pci_post_suspend(rdip) != DDI_SUCCESS) 8292df1fe9cSrandyf return (DDI_FAILURE); 830eae2e508Skrishnae 831eae2e508Skrishnae return (DDI_SUCCESS); 8322df1fe9cSrandyf 83370025d76Sjohnny default: 83470025d76Sjohnny break; 83570025d76Sjohnny } 83670025d76Sjohnny 83770025d76Sjohnny return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 83870025d76Sjohnny 83970025d76Sjohnny } 84070025d76Sjohnny 84170025d76Sjohnny 84270025d76Sjohnny /* 84370025d76Sjohnny * npe_intr_ops 84470025d76Sjohnny */ 84570025d76Sjohnny static int 84670025d76Sjohnny npe_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 84770025d76Sjohnny ddi_intr_handle_impl_t *hdlp, void *result) 84870025d76Sjohnny { 84970025d76Sjohnny return (pci_common_intr_ops(pdip, rdip, intr_op, hdlp, result)); 85070025d76Sjohnny } 85170025d76Sjohnny 85270025d76Sjohnny 85370025d76Sjohnny static int 85470025d76Sjohnny npe_initchild(dev_info_t *child) 85570025d76Sjohnny { 85670025d76Sjohnny char name[80]; 857eae2e508Skrishnae pcie_bus_t *bus_p; 858eae2e508Skrishnae uint32_t regs; 859337fc9e2Sanish ddi_acc_handle_t cfg_hdl; 86070025d76Sjohnny 8617a23d100Sanish /* 8627a23d100Sanish * Do not bind drivers to empty bridges. 8637a23d100Sanish * Fail above, if the bridge is found to be hotplug capable 8647a23d100Sanish */ 8657a23d100Sanish if (npe_disable_empty_bridges_workaround(child) == 1) 8667a23d100Sanish return (DDI_FAILURE); 8677a23d100Sanish 86870025d76Sjohnny if (pci_common_name_child(child, name, 80) != DDI_SUCCESS) 86970025d76Sjohnny return (DDI_FAILURE); 87070025d76Sjohnny 87170025d76Sjohnny ddi_set_name_addr(child, name); 87270025d76Sjohnny 87370025d76Sjohnny /* 87470025d76Sjohnny * Pseudo nodes indicate a prototype node with per-instance 87570025d76Sjohnny * properties to be merged into the real h/w device node. 87670025d76Sjohnny * The interpretation of the unit-address is DD[,F] 87770025d76Sjohnny * where DD is the device id and F is the function. 87870025d76Sjohnny */ 87970025d76Sjohnny if (ndi_dev_is_persistent_node(child) == 0) { 88070025d76Sjohnny extern int pci_allow_pseudo_children; 88170025d76Sjohnny 88270025d76Sjohnny ddi_set_parent_data(child, NULL); 88370025d76Sjohnny 88470025d76Sjohnny /* 88570025d76Sjohnny * Try to merge the properties from this prototype 88670025d76Sjohnny * node into real h/w nodes. 88770025d76Sjohnny */ 88870025d76Sjohnny if (ndi_merge_node(child, pci_common_name_child) == 88970025d76Sjohnny DDI_SUCCESS) { 89070025d76Sjohnny /* 89170025d76Sjohnny * Merged ok - return failure to remove the node. 89270025d76Sjohnny */ 89370025d76Sjohnny ddi_set_name_addr(child, NULL); 89470025d76Sjohnny return (DDI_FAILURE); 89570025d76Sjohnny } 89670025d76Sjohnny 89770025d76Sjohnny /* workaround for DDIVS to run under PCI Express */ 89870025d76Sjohnny if (pci_allow_pseudo_children) { 89970025d76Sjohnny /* 90070025d76Sjohnny * If the "interrupts" property doesn't exist, 90170025d76Sjohnny * this must be the ddivs no-intr case, and it returns 90270025d76Sjohnny * DDI_SUCCESS instead of DDI_FAILURE. 90370025d76Sjohnny */ 90470025d76Sjohnny if (ddi_prop_get_int(DDI_DEV_T_ANY, child, 90570025d76Sjohnny DDI_PROP_DONTPASS, "interrupts", -1) == -1) 90670025d76Sjohnny return (DDI_SUCCESS); 90770025d76Sjohnny /* 90870025d76Sjohnny * Create the ddi_parent_private_data for a pseudo 90970025d76Sjohnny * child. 91070025d76Sjohnny */ 91170025d76Sjohnny pci_common_set_parent_private_data(child); 91270025d76Sjohnny return (DDI_SUCCESS); 91370025d76Sjohnny } 91470025d76Sjohnny 91570025d76Sjohnny /* 91670025d76Sjohnny * The child was not merged into a h/w node, 91770025d76Sjohnny * but there's not much we can do with it other 91870025d76Sjohnny * than return failure to cause the node to be removed. 91970025d76Sjohnny */ 92070025d76Sjohnny cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", 92170025d76Sjohnny ddi_get_name(child), ddi_get_name_addr(child), 92270025d76Sjohnny ddi_get_name(child)); 92370025d76Sjohnny ddi_set_name_addr(child, NULL); 92470025d76Sjohnny return (DDI_NOT_WELL_FORMED); 92570025d76Sjohnny } 92670025d76Sjohnny 92770025d76Sjohnny if (ddi_prop_get_int(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 92870025d76Sjohnny "interrupts", -1) != -1) 92970025d76Sjohnny pci_common_set_parent_private_data(child); 93070025d76Sjohnny else 93170025d76Sjohnny ddi_set_parent_data(child, NULL); 93270025d76Sjohnny 933eae2e508Skrishnae /* Disable certain errors on PCIe drivers for x86 platforms */ 934eae2e508Skrishnae regs = pcie_get_aer_uce_mask() | npe_aer_uce_mask; 935eae2e508Skrishnae pcie_set_aer_uce_mask(regs); 936eae2e508Skrishnae regs = pcie_get_aer_ce_mask() | npe_aer_ce_mask; 937eae2e508Skrishnae pcie_set_aer_ce_mask(regs); 938eae2e508Skrishnae regs = pcie_get_aer_suce_mask() | npe_aer_suce_mask; 939eae2e508Skrishnae pcie_set_aer_suce_mask(regs); 940eae2e508Skrishnae 94170025d76Sjohnny /* 942eae2e508Skrishnae * If URs are disabled, mask SERRs as well, otherwise the system will 943eae2e508Skrishnae * still be notified of URs 94470025d76Sjohnny */ 945eae2e508Skrishnae if (npe_aer_uce_mask & PCIE_AER_UCE_UR) 946eae2e508Skrishnae pcie_set_serr_mask(1); 947eae2e508Skrishnae 948337fc9e2Sanish if (pci_config_setup(child, &cfg_hdl) == DDI_SUCCESS) { 949337fc9e2Sanish npe_ck804_fix_aer_ptr(cfg_hdl); 950a2de976fSPavel Potoplyak npe_nvidia_error_workaround(cfg_hdl); 951a2de976fSPavel Potoplyak npe_intel_error_workaround(cfg_hdl); 952337fc9e2Sanish pci_config_teardown(&cfg_hdl); 953337fc9e2Sanish } 95470025d76Sjohnny 955c0da6274SZhi-Jun Robin Fu bus_p = PCIE_DIP2BUS(child); 956eae2e508Skrishnae if (bus_p) { 957eae2e508Skrishnae uint16_t device_id = (uint16_t)(bus_p->bus_dev_ven_id >> 16); 958eae2e508Skrishnae uint16_t vendor_id = (uint16_t)(bus_p->bus_dev_ven_id & 0xFFFF); 959eae2e508Skrishnae uint16_t rev_id = bus_p->bus_rev_id; 960eae2e508Skrishnae 961eae2e508Skrishnae /* Disable AER for certain NVIDIA Chipsets */ 962eae2e508Skrishnae if ((vendor_id == NVIDIA_VENDOR_ID) && 963eae2e508Skrishnae (device_id == NVIDIA_CK804_DEVICE_ID) && 964eae2e508Skrishnae (rev_id < NVIDIA_CK804_AER_VALID_REVID)) 965eae2e508Skrishnae bus_p->bus_aer_off = 0; 966eae2e508Skrishnae 967fc256490SJason Beloro pcie_init_dom(child); 968eae2e508Skrishnae (void) pcie_initchild(child); 969eae2e508Skrishnae } 970eae2e508Skrishnae 97170025d76Sjohnny return (DDI_SUCCESS); 97270025d76Sjohnny } 97370025d76Sjohnny 97470025d76Sjohnny 97570025d76Sjohnny static int 97670025d76Sjohnny npe_removechild(dev_info_t *dip) 97770025d76Sjohnny { 978eae2e508Skrishnae pcie_uninitchild(dip); 97970025d76Sjohnny 98070025d76Sjohnny ddi_set_name_addr(dip, NULL); 98170025d76Sjohnny 98270025d76Sjohnny /* 98370025d76Sjohnny * Strip the node to properly convert it back to prototype form 98470025d76Sjohnny */ 98570025d76Sjohnny ddi_remove_minor_node(dip, NULL); 98670025d76Sjohnny 98770025d76Sjohnny ddi_prop_remove_all(dip); 98870025d76Sjohnny 98970025d76Sjohnny return (DDI_SUCCESS); 99070025d76Sjohnny } 99170025d76Sjohnny 99270025d76Sjohnny static int 99370025d76Sjohnny npe_open(dev_t *devp, int flags, int otyp, cred_t *credp) 99470025d76Sjohnny { 99526947304SEvan Yan minor_t minor = getminor(*devp); 99626947304SEvan Yan int instance = PCI_MINOR_NUM_TO_INSTANCE(minor); 99726947304SEvan Yan pci_state_t *pci_p = ddi_get_soft_state(npe_statep, instance); 99826947304SEvan Yan int rv; 99926947304SEvan Yan 100026947304SEvan Yan /* 100126947304SEvan Yan * Make sure the open is for the right file type. 100226947304SEvan Yan */ 100326947304SEvan Yan if (otyp != OTYP_CHR) 100426947304SEvan Yan return (EINVAL); 100526947304SEvan Yan 100626947304SEvan Yan if (pci_p == NULL) 100726947304SEvan Yan return (ENXIO); 100826947304SEvan Yan 100926947304SEvan Yan mutex_enter(&pci_p->pci_mutex); 101026947304SEvan Yan switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) { 101126947304SEvan Yan case PCI_TOOL_REG_MINOR_NUM: 101226947304SEvan Yan case PCI_TOOL_INTR_MINOR_NUM: 101326947304SEvan Yan break; 101426947304SEvan Yan default: 101526947304SEvan Yan /* Handle devctl ioctls */ 101626947304SEvan Yan rv = pcie_open(pci_p->pci_dip, devp, flags, otyp, credp); 101726947304SEvan Yan mutex_exit(&pci_p->pci_mutex); 101826947304SEvan Yan return (rv); 101926947304SEvan Yan } 102026947304SEvan Yan 102126947304SEvan Yan /* Handle pcitool ioctls */ 102226947304SEvan Yan if (flags & FEXCL) { 102326947304SEvan Yan if (pci_p->pci_soft_state != PCI_SOFT_STATE_CLOSED) { 102426947304SEvan Yan mutex_exit(&pci_p->pci_mutex); 102526947304SEvan Yan cmn_err(CE_NOTE, "npe_open: busy"); 102626947304SEvan Yan return (EBUSY); 102726947304SEvan Yan } 102826947304SEvan Yan pci_p->pci_soft_state = PCI_SOFT_STATE_OPEN_EXCL; 102926947304SEvan Yan } else { 103026947304SEvan Yan if (pci_p->pci_soft_state == PCI_SOFT_STATE_OPEN_EXCL) { 103126947304SEvan Yan mutex_exit(&pci_p->pci_mutex); 103226947304SEvan Yan cmn_err(CE_NOTE, "npe_open: busy"); 103326947304SEvan Yan return (EBUSY); 103426947304SEvan Yan } 103526947304SEvan Yan pci_p->pci_soft_state = PCI_SOFT_STATE_OPEN; 103626947304SEvan Yan } 103726947304SEvan Yan mutex_exit(&pci_p->pci_mutex); 103826947304SEvan Yan 103926947304SEvan Yan return (0); 104070025d76Sjohnny } 104170025d76Sjohnny 104270025d76Sjohnny static int 104370025d76Sjohnny npe_close(dev_t dev, int flags, int otyp, cred_t *credp) 104470025d76Sjohnny { 104526947304SEvan Yan minor_t minor = getminor(dev); 104626947304SEvan Yan int instance = PCI_MINOR_NUM_TO_INSTANCE(minor); 104726947304SEvan Yan pci_state_t *pci_p = ddi_get_soft_state(npe_statep, instance); 104826947304SEvan Yan int rv; 104926947304SEvan Yan 105026947304SEvan Yan if (pci_p == NULL) 105126947304SEvan Yan return (ENXIO); 105226947304SEvan Yan 105326947304SEvan Yan mutex_enter(&pci_p->pci_mutex); 105426947304SEvan Yan 105526947304SEvan Yan switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) { 105626947304SEvan Yan case PCI_TOOL_REG_MINOR_NUM: 105726947304SEvan Yan case PCI_TOOL_INTR_MINOR_NUM: 105826947304SEvan Yan break; 105926947304SEvan Yan default: 106026947304SEvan Yan /* Handle devctl ioctls */ 106126947304SEvan Yan rv = pcie_close(pci_p->pci_dip, dev, flags, otyp, credp); 106226947304SEvan Yan mutex_exit(&pci_p->pci_mutex); 106326947304SEvan Yan return (rv); 106426947304SEvan Yan } 106526947304SEvan Yan 106626947304SEvan Yan /* Handle pcitool ioctls */ 106726947304SEvan Yan pci_p->pci_soft_state = PCI_SOFT_STATE_CLOSED; 106826947304SEvan Yan mutex_exit(&pci_p->pci_mutex); 106926947304SEvan Yan return (0); 107070025d76Sjohnny } 107170025d76Sjohnny 107270025d76Sjohnny static int 107370025d76Sjohnny npe_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) 107470025d76Sjohnny { 107570025d76Sjohnny minor_t minor = getminor(dev); 107626947304SEvan Yan int instance = PCI_MINOR_NUM_TO_INSTANCE(minor); 107770025d76Sjohnny pci_state_t *pci_p = ddi_get_soft_state(npe_statep, instance); 107826947304SEvan Yan int ret = ENOTTY; 107970025d76Sjohnny 108070025d76Sjohnny if (pci_p == NULL) 108170025d76Sjohnny return (ENXIO); 108270025d76Sjohnny 108326947304SEvan Yan switch (PCI_MINOR_NUM_TO_PCI_DEVNUM(minor)) { 108426947304SEvan Yan case PCI_TOOL_REG_MINOR_NUM: 108526947304SEvan Yan case PCI_TOOL_INTR_MINOR_NUM: 108626947304SEvan Yan /* To handle pcitool related ioctls */ 108726947304SEvan Yan ret = pci_common_ioctl(pci_p->pci_dip, dev, cmd, arg, mode, 108826947304SEvan Yan credp, rvalp); 108926947304SEvan Yan break; 109026947304SEvan Yan default: 109126947304SEvan Yan /* To handle devctl and hotplug related ioctls */ 109226947304SEvan Yan ret = pcie_ioctl(pci_p->pci_dip, dev, cmd, arg, mode, credp, 109326947304SEvan Yan rvalp); 109426947304SEvan Yan break; 109570025d76Sjohnny } 109670025d76Sjohnny 109726947304SEvan Yan return (ret); 109870025d76Sjohnny } 1099247980beSanish 110000d0963fSdilpreet /*ARGSUSED*/ 1101247980beSanish static int 110200d0963fSdilpreet npe_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap, 110300d0963fSdilpreet ddi_iblock_cookie_t *ibc) 1104247980beSanish { 110500d0963fSdilpreet pci_state_t *pcip = ddi_get_soft_state(npe_statep, 110600d0963fSdilpreet ddi_get_instance(dip)); 1107247980beSanish 110800d0963fSdilpreet ASSERT(ibc != NULL); 110900d0963fSdilpreet *ibc = pcip->pci_fm_ibc; 111000d0963fSdilpreet 111100d0963fSdilpreet return (pcip->pci_fmcap); 1112247980beSanish } 1113247980beSanish 111400d0963fSdilpreet /*ARGSUSED*/ 111500d0963fSdilpreet static int 111600d0963fSdilpreet npe_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used) 111700d0963fSdilpreet { 1118eae2e508Skrishnae /* 1119eae2e508Skrishnae * On current x86 systems, npe's callback does not get called for failed 1120eae2e508Skrishnae * loads. If in the future this feature is used, the fault PA should be 1121eae2e508Skrishnae * logged in the derr->fme_bus_specific field. The appropriate PCIe 1122eae2e508Skrishnae * error handling code should be called and needs to be coordinated with 1123eae2e508Skrishnae * safe access handling. 1124eae2e508Skrishnae */ 1125eae2e508Skrishnae 1126eae2e508Skrishnae return (DDI_FM_OK); 1127247980beSanish } 1128