110187SKrishna.Elango@Sun.COM /* 210187SKrishna.Elango@Sun.COM * CDDL HEADER START 310187SKrishna.Elango@Sun.COM * 410187SKrishna.Elango@Sun.COM * The contents of this file are subject to the terms of the 510187SKrishna.Elango@Sun.COM * Common Development and Distribution License (the "License"). 610187SKrishna.Elango@Sun.COM * You may not use this file except in compliance with the License. 710187SKrishna.Elango@Sun.COM * 810187SKrishna.Elango@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 910187SKrishna.Elango@Sun.COM * or http://www.opensolaris.org/os/licensing. 1010187SKrishna.Elango@Sun.COM * See the License for the specific language governing permissions 1110187SKrishna.Elango@Sun.COM * and limitations under the License. 1210187SKrishna.Elango@Sun.COM * 1310187SKrishna.Elango@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 1410187SKrishna.Elango@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1510187SKrishna.Elango@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 1610187SKrishna.Elango@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 1710187SKrishna.Elango@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 1810187SKrishna.Elango@Sun.COM * 1910187SKrishna.Elango@Sun.COM * CDDL HEADER END 2010187SKrishna.Elango@Sun.COM */ 2110187SKrishna.Elango@Sun.COM /* 2211412SStephen.Hanson@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 2310187SKrishna.Elango@Sun.COM * Use is subject to license terms. 2410187SKrishna.Elango@Sun.COM */ 2510187SKrishna.Elango@Sun.COM 2610187SKrishna.Elango@Sun.COM /* 2710187SKrishna.Elango@Sun.COM * Common x86 and SPARC PCI-E to PCI bus bridge nexus driver 2810187SKrishna.Elango@Sun.COM */ 2910187SKrishna.Elango@Sun.COM 3010187SKrishna.Elango@Sun.COM #include <sys/sysmacros.h> 3110187SKrishna.Elango@Sun.COM #include <sys/conf.h> 3210187SKrishna.Elango@Sun.COM #include <sys/kmem.h> 3310187SKrishna.Elango@Sun.COM #include <sys/debug.h> 3410187SKrishna.Elango@Sun.COM #include <sys/modctl.h> 3510187SKrishna.Elango@Sun.COM #include <sys/autoconf.h> 3610187SKrishna.Elango@Sun.COM #include <sys/ddi_impldefs.h> 3710187SKrishna.Elango@Sun.COM #include <sys/pci.h> 3810187SKrishna.Elango@Sun.COM #include <sys/ddi.h> 3910187SKrishna.Elango@Sun.COM #include <sys/sunddi.h> 4010187SKrishna.Elango@Sun.COM #include <sys/sunndi.h> 4110187SKrishna.Elango@Sun.COM #include <sys/fm/util.h> 4210187SKrishna.Elango@Sun.COM #include <sys/pci_cap.h> 4310923SEvan.Yan@Sun.COM #include <sys/pci_impl.h> 4410187SKrishna.Elango@Sun.COM #include <sys/pcie_impl.h> 4510187SKrishna.Elango@Sun.COM #include <sys/open.h> 4610187SKrishna.Elango@Sun.COM #include <sys/stat.h> 4710187SKrishna.Elango@Sun.COM #include <sys/file.h> 4810187SKrishna.Elango@Sun.COM #include <sys/promif.h> /* prom_printf */ 4910187SKrishna.Elango@Sun.COM #include <sys/disp.h> 5010187SKrishna.Elango@Sun.COM #include <sys/pcie_pwr.h> 5110923SEvan.Yan@Sun.COM #include <sys/hotplug/pci/pcie_hp.h> 5210187SKrishna.Elango@Sun.COM #include "pcieb.h" 5310187SKrishna.Elango@Sun.COM #ifdef PX_PLX 5410187SKrishna.Elango@Sun.COM #include <io/pciex/pcieb_plx.h> 5510187SKrishna.Elango@Sun.COM #endif /* PX_PLX */ 5610187SKrishna.Elango@Sun.COM 5710187SKrishna.Elango@Sun.COM /*LINTLIBRARY*/ 5810187SKrishna.Elango@Sun.COM 5910187SKrishna.Elango@Sun.COM /* panic flag */ 6010187SKrishna.Elango@Sun.COM int pcieb_die = PF_ERR_FATAL_FLAGS; 6110187SKrishna.Elango@Sun.COM 6210187SKrishna.Elango@Sun.COM /* flag to turn on MSI support */ 6311465SKerry.Shu@Sun.COM int pcieb_enable_msi = 1; 6410187SKrishna.Elango@Sun.COM 6510187SKrishna.Elango@Sun.COM #if defined(DEBUG) 6610187SKrishna.Elango@Sun.COM uint_t pcieb_dbg_print = 0; 6710187SKrishna.Elango@Sun.COM 6810187SKrishna.Elango@Sun.COM static char *pcieb_debug_sym [] = { /* same sequence as pcieb_debug_bit */ 6910187SKrishna.Elango@Sun.COM /* 0 */ "attach", 7010187SKrishna.Elango@Sun.COM /* 1 */ "pwr", 7110187SKrishna.Elango@Sun.COM /* 2 */ "intr" 7210187SKrishna.Elango@Sun.COM }; 7310187SKrishna.Elango@Sun.COM #endif /* DEBUG */ 7410187SKrishna.Elango@Sun.COM 7510187SKrishna.Elango@Sun.COM static int pcieb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *, off_t, 7610187SKrishna.Elango@Sun.COM off_t, caddr_t *); 7710187SKrishna.Elango@Sun.COM static int pcieb_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, 7810187SKrishna.Elango@Sun.COM void *); 7910187SKrishna.Elango@Sun.COM static int pcieb_fm_init(pcieb_devstate_t *pcieb_p); 8010187SKrishna.Elango@Sun.COM static void pcieb_fm_fini(pcieb_devstate_t *pcieb_p); 8110187SKrishna.Elango@Sun.COM static int pcieb_fm_init_child(dev_info_t *dip, dev_info_t *cdip, int cap, 8210187SKrishna.Elango@Sun.COM ddi_iblock_cookie_t *ibc_p); 8310187SKrishna.Elango@Sun.COM static int pcieb_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, 8410187SKrishna.Elango@Sun.COM ddi_dma_attr_t *attr_p, int (*waitfp)(caddr_t), caddr_t arg, 8510187SKrishna.Elango@Sun.COM ddi_dma_handle_t *handlep); 8610187SKrishna.Elango@Sun.COM static int pcieb_dma_mctl(dev_info_t *dip, dev_info_t *rdip, 8710187SKrishna.Elango@Sun.COM ddi_dma_handle_t handle, enum ddi_dma_ctlops cmd, off_t *offp, 8810187SKrishna.Elango@Sun.COM size_t *lenp, caddr_t *objp, uint_t cache_flags); 8910187SKrishna.Elango@Sun.COM static int pcieb_intr_ops(dev_info_t *dip, dev_info_t *rdip, 9010187SKrishna.Elango@Sun.COM ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); 9110187SKrishna.Elango@Sun.COM 9210187SKrishna.Elango@Sun.COM static struct bus_ops pcieb_bus_ops = { 9310187SKrishna.Elango@Sun.COM BUSO_REV, 9410187SKrishna.Elango@Sun.COM pcieb_bus_map, 9510187SKrishna.Elango@Sun.COM 0, 9610187SKrishna.Elango@Sun.COM 0, 9710187SKrishna.Elango@Sun.COM 0, 9810187SKrishna.Elango@Sun.COM i_ddi_map_fault, 9910187SKrishna.Elango@Sun.COM ddi_dma_map, 10010187SKrishna.Elango@Sun.COM pcieb_dma_allochdl, 10110187SKrishna.Elango@Sun.COM ddi_dma_freehdl, 10210187SKrishna.Elango@Sun.COM ddi_dma_bindhdl, 10310187SKrishna.Elango@Sun.COM ddi_dma_unbindhdl, 10410187SKrishna.Elango@Sun.COM ddi_dma_flush, 10510187SKrishna.Elango@Sun.COM ddi_dma_win, 10610187SKrishna.Elango@Sun.COM pcieb_dma_mctl, 10710187SKrishna.Elango@Sun.COM pcieb_ctlops, 10810187SKrishna.Elango@Sun.COM ddi_bus_prop_op, 10910187SKrishna.Elango@Sun.COM ndi_busop_get_eventcookie, /* (*bus_get_eventcookie)(); */ 11010187SKrishna.Elango@Sun.COM ndi_busop_add_eventcall, /* (*bus_add_eventcall)(); */ 11110187SKrishna.Elango@Sun.COM ndi_busop_remove_eventcall, /* (*bus_remove_eventcall)(); */ 11210187SKrishna.Elango@Sun.COM ndi_post_event, /* (*bus_post_event)(); */ 11310187SKrishna.Elango@Sun.COM NULL, /* (*bus_intr_ctl)(); */ 11410187SKrishna.Elango@Sun.COM NULL, /* (*bus_config)(); */ 11510187SKrishna.Elango@Sun.COM NULL, /* (*bus_unconfig)(); */ 11610187SKrishna.Elango@Sun.COM pcieb_fm_init_child, /* (*bus_fm_init)(); */ 11710187SKrishna.Elango@Sun.COM NULL, /* (*bus_fm_fini)(); */ 11810187SKrishna.Elango@Sun.COM i_ndi_busop_access_enter, /* (*bus_fm_access_enter)(); */ 11910187SKrishna.Elango@Sun.COM i_ndi_busop_access_exit, /* (*bus_fm_access_exit)(); */ 12010187SKrishna.Elango@Sun.COM pcie_bus_power, /* (*bus_power)(); */ 12110923SEvan.Yan@Sun.COM pcieb_intr_ops, /* (*bus_intr_op)(); */ 12210923SEvan.Yan@Sun.COM pcie_hp_common_ops /* (*bus_hp_op)(); */ 12310187SKrishna.Elango@Sun.COM }; 12410187SKrishna.Elango@Sun.COM 12510187SKrishna.Elango@Sun.COM static int pcieb_open(dev_t *, int, int, cred_t *); 12610187SKrishna.Elango@Sun.COM static int pcieb_close(dev_t, int, int, cred_t *); 12710187SKrishna.Elango@Sun.COM static int pcieb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 12810187SKrishna.Elango@Sun.COM static int pcieb_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 12910187SKrishna.Elango@Sun.COM static uint_t pcieb_intr_handler(caddr_t arg1, caddr_t arg2); 13010187SKrishna.Elango@Sun.COM 13110187SKrishna.Elango@Sun.COM /* PM related functions */ 13210187SKrishna.Elango@Sun.COM static int pcieb_pwr_setup(dev_info_t *dip); 13310187SKrishna.Elango@Sun.COM static int pcieb_pwr_init_and_raise(dev_info_t *dip, pcie_pwr_t *pwr_p); 13410187SKrishna.Elango@Sun.COM static void pcieb_pwr_teardown(dev_info_t *dip); 13510187SKrishna.Elango@Sun.COM static int pcieb_pwr_disable(dev_info_t *dip); 13610187SKrishna.Elango@Sun.COM 13710187SKrishna.Elango@Sun.COM /* Hotplug related functions */ 13810187SKrishna.Elango@Sun.COM static void pcieb_id_props(pcieb_devstate_t *pcieb); 13910187SKrishna.Elango@Sun.COM 14010187SKrishna.Elango@Sun.COM /* 14110187SKrishna.Elango@Sun.COM * soft state pointer 14210187SKrishna.Elango@Sun.COM */ 14310187SKrishna.Elango@Sun.COM void *pcieb_state; 14410187SKrishna.Elango@Sun.COM 14510187SKrishna.Elango@Sun.COM static struct cb_ops pcieb_cb_ops = { 14610187SKrishna.Elango@Sun.COM pcieb_open, /* open */ 14710187SKrishna.Elango@Sun.COM pcieb_close, /* close */ 14810187SKrishna.Elango@Sun.COM nodev, /* strategy */ 14910187SKrishna.Elango@Sun.COM nodev, /* print */ 15010187SKrishna.Elango@Sun.COM nodev, /* dump */ 15110187SKrishna.Elango@Sun.COM nodev, /* read */ 15210187SKrishna.Elango@Sun.COM nodev, /* write */ 15310187SKrishna.Elango@Sun.COM pcieb_ioctl, /* ioctl */ 15410187SKrishna.Elango@Sun.COM nodev, /* devmap */ 15510187SKrishna.Elango@Sun.COM nodev, /* mmap */ 15610187SKrishna.Elango@Sun.COM nodev, /* segmap */ 15710187SKrishna.Elango@Sun.COM nochpoll, /* poll */ 15810923SEvan.Yan@Sun.COM pcie_prop_op, /* cb_prop_op */ 15910187SKrishna.Elango@Sun.COM NULL, /* streamtab */ 16010187SKrishna.Elango@Sun.COM D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 16110187SKrishna.Elango@Sun.COM CB_REV, /* rev */ 16210187SKrishna.Elango@Sun.COM nodev, /* int (*cb_aread)() */ 16310187SKrishna.Elango@Sun.COM nodev /* int (*cb_awrite)() */ 16410187SKrishna.Elango@Sun.COM }; 16510187SKrishna.Elango@Sun.COM 16610187SKrishna.Elango@Sun.COM static int pcieb_probe(dev_info_t *); 16710187SKrishna.Elango@Sun.COM static int pcieb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); 16810187SKrishna.Elango@Sun.COM static int pcieb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd); 16910187SKrishna.Elango@Sun.COM 17010187SKrishna.Elango@Sun.COM static struct dev_ops pcieb_ops = { 17110187SKrishna.Elango@Sun.COM DEVO_REV, /* devo_rev */ 17210187SKrishna.Elango@Sun.COM 0, /* refcnt */ 17310187SKrishna.Elango@Sun.COM pcieb_info, /* info */ 17410187SKrishna.Elango@Sun.COM nulldev, /* identify */ 17510187SKrishna.Elango@Sun.COM pcieb_probe, /* probe */ 17610187SKrishna.Elango@Sun.COM pcieb_attach, /* attach */ 17710187SKrishna.Elango@Sun.COM pcieb_detach, /* detach */ 17810187SKrishna.Elango@Sun.COM nulldev, /* reset */ 17910187SKrishna.Elango@Sun.COM &pcieb_cb_ops, /* driver operations */ 18010187SKrishna.Elango@Sun.COM &pcieb_bus_ops, /* bus operations */ 18110187SKrishna.Elango@Sun.COM pcie_power, /* power */ 18210187SKrishna.Elango@Sun.COM ddi_quiesce_not_needed, /* quiesce */ 18310187SKrishna.Elango@Sun.COM }; 18410187SKrishna.Elango@Sun.COM 18510187SKrishna.Elango@Sun.COM /* 18610187SKrishna.Elango@Sun.COM * Module linkage information for the kernel. 18710187SKrishna.Elango@Sun.COM */ 18810187SKrishna.Elango@Sun.COM 18910187SKrishna.Elango@Sun.COM static struct modldrv modldrv = { 19010187SKrishna.Elango@Sun.COM &mod_driverops, /* Type of module */ 19110923SEvan.Yan@Sun.COM "PCIe bridge/switch driver", 19210187SKrishna.Elango@Sun.COM &pcieb_ops, /* driver ops */ 19310187SKrishna.Elango@Sun.COM }; 19410187SKrishna.Elango@Sun.COM 19510187SKrishna.Elango@Sun.COM static struct modlinkage modlinkage = { 19610187SKrishna.Elango@Sun.COM MODREV_1, 19710187SKrishna.Elango@Sun.COM (void *)&modldrv, 19810187SKrishna.Elango@Sun.COM NULL 19910187SKrishna.Elango@Sun.COM }; 20010187SKrishna.Elango@Sun.COM 20110187SKrishna.Elango@Sun.COM /* 20210187SKrishna.Elango@Sun.COM * forward function declarations: 20310187SKrishna.Elango@Sun.COM */ 20410187SKrishna.Elango@Sun.COM static void pcieb_uninitchild(dev_info_t *); 20510187SKrishna.Elango@Sun.COM static int pcieb_initchild(dev_info_t *child); 20610187SKrishna.Elango@Sun.COM static void pcieb_create_ranges_prop(dev_info_t *, ddi_acc_handle_t); 20710187SKrishna.Elango@Sun.COM static boolean_t pcieb_is_pcie_device_type(dev_info_t *dip); 20810187SKrishna.Elango@Sun.COM 20910187SKrishna.Elango@Sun.COM /* interrupt related declarations */ 21010187SKrishna.Elango@Sun.COM static int pcieb_msi_supported(dev_info_t *); 21110187SKrishna.Elango@Sun.COM static int pcieb_intr_attach(pcieb_devstate_t *pcieb); 21210187SKrishna.Elango@Sun.COM static int pcieb_intr_init(pcieb_devstate_t *pcieb_p, int intr_type); 21310187SKrishna.Elango@Sun.COM static void pcieb_intr_fini(pcieb_devstate_t *pcieb_p); 21410187SKrishna.Elango@Sun.COM 21510187SKrishna.Elango@Sun.COM int 21610187SKrishna.Elango@Sun.COM _init(void) 21710187SKrishna.Elango@Sun.COM { 21810187SKrishna.Elango@Sun.COM int e; 21910187SKrishna.Elango@Sun.COM 22010187SKrishna.Elango@Sun.COM if ((e = ddi_soft_state_init(&pcieb_state, sizeof (pcieb_devstate_t), 22110187SKrishna.Elango@Sun.COM 1)) == 0 && (e = mod_install(&modlinkage)) != 0) 22210187SKrishna.Elango@Sun.COM ddi_soft_state_fini(&pcieb_state); 22310187SKrishna.Elango@Sun.COM return (e); 22410187SKrishna.Elango@Sun.COM } 22510187SKrishna.Elango@Sun.COM 22610187SKrishna.Elango@Sun.COM int 22710187SKrishna.Elango@Sun.COM _fini(void) 22810187SKrishna.Elango@Sun.COM { 22910187SKrishna.Elango@Sun.COM int e; 23010187SKrishna.Elango@Sun.COM 23110187SKrishna.Elango@Sun.COM if ((e = mod_remove(&modlinkage)) == 0) { 23210187SKrishna.Elango@Sun.COM ddi_soft_state_fini(&pcieb_state); 23310187SKrishna.Elango@Sun.COM } 23410187SKrishna.Elango@Sun.COM return (e); 23510187SKrishna.Elango@Sun.COM } 23610187SKrishna.Elango@Sun.COM 23710187SKrishna.Elango@Sun.COM int 23810187SKrishna.Elango@Sun.COM _info(struct modinfo *modinfop) 23910187SKrishna.Elango@Sun.COM { 24010187SKrishna.Elango@Sun.COM return (mod_info(&modlinkage, modinfop)); 24110187SKrishna.Elango@Sun.COM } 24210187SKrishna.Elango@Sun.COM 24310923SEvan.Yan@Sun.COM /* ARGSUSED */ 24410923SEvan.Yan@Sun.COM static int 24510923SEvan.Yan@Sun.COM pcieb_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 24610923SEvan.Yan@Sun.COM { 24710923SEvan.Yan@Sun.COM minor_t minor = getminor((dev_t)arg); 24810923SEvan.Yan@Sun.COM int instance = PCI_MINOR_NUM_TO_INSTANCE(minor); 24910923SEvan.Yan@Sun.COM pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, instance); 25010923SEvan.Yan@Sun.COM int ret = DDI_SUCCESS; 25110923SEvan.Yan@Sun.COM 25210923SEvan.Yan@Sun.COM switch (infocmd) { 25310923SEvan.Yan@Sun.COM case DDI_INFO_DEVT2INSTANCE: 25410923SEvan.Yan@Sun.COM *result = (void *)(intptr_t)instance; 25510923SEvan.Yan@Sun.COM break; 25610923SEvan.Yan@Sun.COM case DDI_INFO_DEVT2DEVINFO: 25710923SEvan.Yan@Sun.COM if (pcieb == NULL) { 25810923SEvan.Yan@Sun.COM ret = DDI_FAILURE; 25910923SEvan.Yan@Sun.COM break; 26010923SEvan.Yan@Sun.COM } 26110923SEvan.Yan@Sun.COM 26210923SEvan.Yan@Sun.COM *result = (void *)pcieb->pcieb_dip; 26310923SEvan.Yan@Sun.COM break; 26410923SEvan.Yan@Sun.COM default: 26510923SEvan.Yan@Sun.COM ret = DDI_FAILURE; 26610923SEvan.Yan@Sun.COM break; 26710923SEvan.Yan@Sun.COM } 26810923SEvan.Yan@Sun.COM 26910923SEvan.Yan@Sun.COM return (ret); 27010923SEvan.Yan@Sun.COM } 27110923SEvan.Yan@Sun.COM 27210923SEvan.Yan@Sun.COM 27310187SKrishna.Elango@Sun.COM /*ARGSUSED*/ 27410187SKrishna.Elango@Sun.COM static int 27510187SKrishna.Elango@Sun.COM pcieb_probe(dev_info_t *devi) 27610187SKrishna.Elango@Sun.COM { 27710187SKrishna.Elango@Sun.COM return (DDI_PROBE_SUCCESS); 27810187SKrishna.Elango@Sun.COM } 27910187SKrishna.Elango@Sun.COM 28010187SKrishna.Elango@Sun.COM static int 28110187SKrishna.Elango@Sun.COM pcieb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 28210187SKrishna.Elango@Sun.COM { 28310187SKrishna.Elango@Sun.COM int instance; 28410187SKrishna.Elango@Sun.COM char device_type[8]; 28510187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb; 28610187SKrishna.Elango@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2UPBUS(devi); 28710187SKrishna.Elango@Sun.COM ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl; 28810187SKrishna.Elango@Sun.COM 28910187SKrishna.Elango@Sun.COM switch (cmd) { 29010187SKrishna.Elango@Sun.COM case DDI_RESUME: 29110187SKrishna.Elango@Sun.COM (void) pcie_pwr_resume(devi); 29210187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 29310187SKrishna.Elango@Sun.COM 29410187SKrishna.Elango@Sun.COM default: 29510187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 29610187SKrishna.Elango@Sun.COM 29710187SKrishna.Elango@Sun.COM case DDI_ATTACH: 29810187SKrishna.Elango@Sun.COM break; 29910187SKrishna.Elango@Sun.COM } 30010187SKrishna.Elango@Sun.COM 30110187SKrishna.Elango@Sun.COM if (!(PCIE_IS_BDG(bus_p))) { 30210187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, devi, "This is not a switch or" 30310187SKrishna.Elango@Sun.COM " bridge\n"); 30410187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 30510187SKrishna.Elango@Sun.COM } 30610187SKrishna.Elango@Sun.COM 30710187SKrishna.Elango@Sun.COM /* 30810187SKrishna.Elango@Sun.COM * If PCIE_LINKCTL_LINK_DISABLE bit in the PCIe Config 30910187SKrishna.Elango@Sun.COM * Space (PCIe Capability Link Control Register) is set, 31010187SKrishna.Elango@Sun.COM * then do not bind the driver. 31110187SKrishna.Elango@Sun.COM */ 31210187SKrishna.Elango@Sun.COM if (PCIE_CAP_GET(16, bus_p, PCIE_LINKCTL) & PCIE_LINKCTL_LINK_DISABLE) 31310187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 31410187SKrishna.Elango@Sun.COM 31510187SKrishna.Elango@Sun.COM /* 31610187SKrishna.Elango@Sun.COM * Allocate and get soft state structure. 31710187SKrishna.Elango@Sun.COM */ 31810187SKrishna.Elango@Sun.COM instance = ddi_get_instance(devi); 31910187SKrishna.Elango@Sun.COM if (ddi_soft_state_zalloc(pcieb_state, instance) != DDI_SUCCESS) 32010187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 32110187SKrishna.Elango@Sun.COM pcieb = ddi_get_soft_state(pcieb_state, instance); 32210187SKrishna.Elango@Sun.COM pcieb->pcieb_dip = devi; 32310187SKrishna.Elango@Sun.COM 32410187SKrishna.Elango@Sun.COM if ((pcieb_fm_init(pcieb)) != DDI_SUCCESS) { 32510187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, devi, "Failed in pcieb_fm_init\n"); 32610187SKrishna.Elango@Sun.COM goto fail; 32710187SKrishna.Elango@Sun.COM } 32810187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_FM; 32910187SKrishna.Elango@Sun.COM 33010187SKrishna.Elango@Sun.COM mutex_init(&pcieb->pcieb_mutex, NULL, MUTEX_DRIVER, NULL); 33110187SKrishna.Elango@Sun.COM mutex_init(&pcieb->pcieb_err_mutex, NULL, MUTEX_DRIVER, 33210187SKrishna.Elango@Sun.COM (void *)pcieb->pcieb_fm_ibc); 33310187SKrishna.Elango@Sun.COM mutex_init(&pcieb->pcieb_peek_poke_mutex, NULL, MUTEX_DRIVER, 33410187SKrishna.Elango@Sun.COM (void *)pcieb->pcieb_fm_ibc); 33510187SKrishna.Elango@Sun.COM 33610187SKrishna.Elango@Sun.COM /* create special properties for device identification */ 33710187SKrishna.Elango@Sun.COM pcieb_id_props(pcieb); 33810187SKrishna.Elango@Sun.COM 33910187SKrishna.Elango@Sun.COM /* 34010187SKrishna.Elango@Sun.COM * Power management setup. This also makes sure that switch/bridge 34110187SKrishna.Elango@Sun.COM * is at D0 during attach. 34210187SKrishna.Elango@Sun.COM */ 34310187SKrishna.Elango@Sun.COM if (pwr_common_setup(devi) != DDI_SUCCESS) { 34410187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, devi, "pwr_common_setup failed\n"); 34510187SKrishna.Elango@Sun.COM goto fail; 34610187SKrishna.Elango@Sun.COM } 34710187SKrishna.Elango@Sun.COM 34810187SKrishna.Elango@Sun.COM if (pcieb_pwr_setup(devi) != DDI_SUCCESS) { 34910187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, devi, "pxb_pwr_setup failed \n"); 35010187SKrishna.Elango@Sun.COM goto fail; 35110187SKrishna.Elango@Sun.COM } 35210187SKrishna.Elango@Sun.COM 35310187SKrishna.Elango@Sun.COM /* 35410187SKrishna.Elango@Sun.COM * Make sure the "device_type" property exists. 35510187SKrishna.Elango@Sun.COM */ 35610187SKrishna.Elango@Sun.COM if (pcieb_is_pcie_device_type(devi)) 35710187SKrishna.Elango@Sun.COM (void) strcpy(device_type, "pciex"); 35810187SKrishna.Elango@Sun.COM else 35910187SKrishna.Elango@Sun.COM (void) strcpy(device_type, "pci"); 36010187SKrishna.Elango@Sun.COM 36110187SKrishna.Elango@Sun.COM (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi, 36210187SKrishna.Elango@Sun.COM "device_type", device_type); 36310187SKrishna.Elango@Sun.COM 36410187SKrishna.Elango@Sun.COM /* 36510187SKrishna.Elango@Sun.COM * Check whether the "ranges" property is present. 36610187SKrishna.Elango@Sun.COM * Otherwise create the ranges property by reading 36710187SKrishna.Elango@Sun.COM * the configuration registers 36810187SKrishna.Elango@Sun.COM */ 36910187SKrishna.Elango@Sun.COM if (ddi_prop_exists(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 37010187SKrishna.Elango@Sun.COM "ranges") == 0) { 37110187SKrishna.Elango@Sun.COM pcieb_create_ranges_prop(devi, config_handle); 37210187SKrishna.Elango@Sun.COM } 37310187SKrishna.Elango@Sun.COM 37410187SKrishna.Elango@Sun.COM if (PCIE_IS_PCI_BDG(bus_p)) 37510187SKrishna.Elango@Sun.COM pcieb_set_pci_perf_parameters(devi, config_handle); 37610187SKrishna.Elango@Sun.COM 37710187SKrishna.Elango@Sun.COM #ifdef PX_PLX 37810187SKrishna.Elango@Sun.COM pcieb_attach_plx_workarounds(pcieb); 37910187SKrishna.Elango@Sun.COM #endif /* PX_PLX */ 38010187SKrishna.Elango@Sun.COM 38110923SEvan.Yan@Sun.COM if (pcie_init(devi, NULL) != DDI_SUCCESS) 38210923SEvan.Yan@Sun.COM goto fail; 38310187SKrishna.Elango@Sun.COM 38410187SKrishna.Elango@Sun.COM /* 38510187SKrishna.Elango@Sun.COM * Initialize interrupt handlers. Ignore return value. 38610187SKrishna.Elango@Sun.COM */ 38710187SKrishna.Elango@Sun.COM (void) pcieb_intr_attach(pcieb); 38810187SKrishna.Elango@Sun.COM 38911445SEvan.Yan@Sun.COM (void) pcie_hpintr_enable(devi); 39011445SEvan.Yan@Sun.COM 39110187SKrishna.Elango@Sun.COM /* Do any platform specific workarounds needed at this time */ 39210187SKrishna.Elango@Sun.COM pcieb_plat_attach_workaround(devi); 39310187SKrishna.Elango@Sun.COM 39410187SKrishna.Elango@Sun.COM /* 39510187SKrishna.Elango@Sun.COM * If this is a root port, determine and set the max payload size. 39610187SKrishna.Elango@Sun.COM * Since this will involve scanning the fabric, all error enabling 39710187SKrishna.Elango@Sun.COM * and sw workarounds should be in place before doing this. 39810187SKrishna.Elango@Sun.COM */ 39910187SKrishna.Elango@Sun.COM if (PCIE_IS_RP(bus_p)) 40010187SKrishna.Elango@Sun.COM pcie_init_root_port_mps(devi); 40110187SKrishna.Elango@Sun.COM 40210187SKrishna.Elango@Sun.COM ddi_report_dev(devi); 40310187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 40410187SKrishna.Elango@Sun.COM 40510187SKrishna.Elango@Sun.COM fail: 40610187SKrishna.Elango@Sun.COM (void) pcieb_detach(devi, DDI_DETACH); 40710187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 40810187SKrishna.Elango@Sun.COM } 40910187SKrishna.Elango@Sun.COM 41010187SKrishna.Elango@Sun.COM static int 41110187SKrishna.Elango@Sun.COM pcieb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 41210187SKrishna.Elango@Sun.COM { 41310187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb; 41410187SKrishna.Elango@Sun.COM int error = DDI_SUCCESS; 41510187SKrishna.Elango@Sun.COM 41610187SKrishna.Elango@Sun.COM switch (cmd) { 41710187SKrishna.Elango@Sun.COM case DDI_SUSPEND: 41810187SKrishna.Elango@Sun.COM error = pcie_pwr_suspend(devi); 41910187SKrishna.Elango@Sun.COM return (error); 42010187SKrishna.Elango@Sun.COM 42110187SKrishna.Elango@Sun.COM case DDI_DETACH: 42210187SKrishna.Elango@Sun.COM break; 42310187SKrishna.Elango@Sun.COM 42410187SKrishna.Elango@Sun.COM default: 42510187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 42610187SKrishna.Elango@Sun.COM } 42710187SKrishna.Elango@Sun.COM 42810187SKrishna.Elango@Sun.COM pcieb = ddi_get_soft_state(pcieb_state, ddi_get_instance(devi)); 42910187SKrishna.Elango@Sun.COM 43011445SEvan.Yan@Sun.COM /* disable hotplug interrupt */ 43111445SEvan.Yan@Sun.COM (void) pcie_hpintr_disable(devi); 43211445SEvan.Yan@Sun.COM 43310187SKrishna.Elango@Sun.COM /* remove interrupt handlers */ 43410187SKrishna.Elango@Sun.COM pcieb_intr_fini(pcieb); 43510187SKrishna.Elango@Sun.COM 43610923SEvan.Yan@Sun.COM /* uninitialize inband PCI-E HPC if present */ 43710923SEvan.Yan@Sun.COM (void) pcie_uninit(devi); 43810187SKrishna.Elango@Sun.COM 43910187SKrishna.Elango@Sun.COM (void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type"); 44010187SKrishna.Elango@Sun.COM 44110187SKrishna.Elango@Sun.COM (void) ndi_prop_remove(DDI_DEV_T_NONE, pcieb->pcieb_dip, 44210187SKrishna.Elango@Sun.COM "pcie_ce_mask"); 44310187SKrishna.Elango@Sun.COM 44410187SKrishna.Elango@Sun.COM if (pcieb->pcieb_init_flags & PCIEB_INIT_FM) 44510187SKrishna.Elango@Sun.COM pcieb_fm_fini(pcieb); 44610187SKrishna.Elango@Sun.COM 44710187SKrishna.Elango@Sun.COM pcieb_pwr_teardown(devi); 44810187SKrishna.Elango@Sun.COM pwr_common_teardown(devi); 44910187SKrishna.Elango@Sun.COM 45010187SKrishna.Elango@Sun.COM mutex_destroy(&pcieb->pcieb_peek_poke_mutex); 45110187SKrishna.Elango@Sun.COM mutex_destroy(&pcieb->pcieb_err_mutex); 45210187SKrishna.Elango@Sun.COM mutex_destroy(&pcieb->pcieb_mutex); 45310187SKrishna.Elango@Sun.COM 45410187SKrishna.Elango@Sun.COM /* 45510187SKrishna.Elango@Sun.COM * And finally free the per-pci soft state. 45610187SKrishna.Elango@Sun.COM */ 45710187SKrishna.Elango@Sun.COM ddi_soft_state_free(pcieb_state, ddi_get_instance(devi)); 45810187SKrishna.Elango@Sun.COM 45910187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 46010187SKrishna.Elango@Sun.COM } 46110187SKrishna.Elango@Sun.COM 46210187SKrishna.Elango@Sun.COM static int 46310187SKrishna.Elango@Sun.COM pcieb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 46410187SKrishna.Elango@Sun.COM off_t offset, off_t len, caddr_t *vaddrp) 46510187SKrishna.Elango@Sun.COM { 46610187SKrishna.Elango@Sun.COM dev_info_t *pdip; 46710187SKrishna.Elango@Sun.COM 46811412SStephen.Hanson@Sun.COM if (PCIE_IS_RP(PCIE_DIP2BUS(dip)) && mp->map_handlep != NULL) { 46911236SStephen.Hanson@Sun.COM ddi_acc_impl_t *hdlp = 47011236SStephen.Hanson@Sun.COM (ddi_acc_impl_t *)(mp->map_handlep)->ah_platform_private; 47111236SStephen.Hanson@Sun.COM 47211236SStephen.Hanson@Sun.COM pcieb_set_prot_scan(dip, hdlp); 47311236SStephen.Hanson@Sun.COM } 47410187SKrishna.Elango@Sun.COM pdip = (dev_info_t *)DEVI(dip)->devi_parent; 47510187SKrishna.Elango@Sun.COM return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip, rdip, mp, 47610187SKrishna.Elango@Sun.COM offset, len, vaddrp)); 47710187SKrishna.Elango@Sun.COM } 47810187SKrishna.Elango@Sun.COM 47910187SKrishna.Elango@Sun.COM static int 48010187SKrishna.Elango@Sun.COM pcieb_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, 48110187SKrishna.Elango@Sun.COM void *arg, void *result) 48210187SKrishna.Elango@Sun.COM { 48310187SKrishna.Elango@Sun.COM pci_regspec_t *drv_regp; 48410187SKrishna.Elango@Sun.COM int reglen; 48510187SKrishna.Elango@Sun.COM int rn; 48610187SKrishna.Elango@Sun.COM int totreg; 48710187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, 48810187SKrishna.Elango@Sun.COM ddi_get_instance(dip)); 48910187SKrishna.Elango@Sun.COM struct detachspec *ds; 49010187SKrishna.Elango@Sun.COM struct attachspec *as; 49110187SKrishna.Elango@Sun.COM 49210187SKrishna.Elango@Sun.COM switch (ctlop) { 49310187SKrishna.Elango@Sun.COM case DDI_CTLOPS_REPORTDEV: 49410187SKrishna.Elango@Sun.COM if (rdip == (dev_info_t *)0) 49510187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 496*11596SJason.Beloro@Sun.COM 497*11596SJason.Beloro@Sun.COM if (ddi_get_parent(rdip) == dip) { 498*11596SJason.Beloro@Sun.COM cmn_err(CE_CONT, "?PCIE-device: %s@%s, %s%d\n", 499*11596SJason.Beloro@Sun.COM ddi_node_name(rdip), ddi_get_name_addr(rdip), 500*11596SJason.Beloro@Sun.COM ddi_driver_name(rdip), ddi_get_instance(rdip)); 501*11596SJason.Beloro@Sun.COM } 502*11596SJason.Beloro@Sun.COM 503*11596SJason.Beloro@Sun.COM /* Pass it up for fabric sync */ 504*11596SJason.Beloro@Sun.COM (void) ddi_ctlops(dip, rdip, ctlop, arg, result); 50510187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 50610187SKrishna.Elango@Sun.COM 50710187SKrishna.Elango@Sun.COM case DDI_CTLOPS_INITCHILD: 50810187SKrishna.Elango@Sun.COM return (pcieb_initchild((dev_info_t *)arg)); 50910187SKrishna.Elango@Sun.COM 51010187SKrishna.Elango@Sun.COM case DDI_CTLOPS_UNINITCHILD: 51110187SKrishna.Elango@Sun.COM pcieb_uninitchild((dev_info_t *)arg); 51210187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 51310187SKrishna.Elango@Sun.COM 51410187SKrishna.Elango@Sun.COM case DDI_CTLOPS_SIDDEV: 51510187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 51610187SKrishna.Elango@Sun.COM 51710187SKrishna.Elango@Sun.COM case DDI_CTLOPS_REGSIZE: 51810187SKrishna.Elango@Sun.COM case DDI_CTLOPS_NREGS: 51910187SKrishna.Elango@Sun.COM if (rdip == (dev_info_t *)0) 52010187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 52110187SKrishna.Elango@Sun.COM break; 52210187SKrishna.Elango@Sun.COM 52310187SKrishna.Elango@Sun.COM case DDI_CTLOPS_PEEK: 52410187SKrishna.Elango@Sun.COM case DDI_CTLOPS_POKE: 52510187SKrishna.Elango@Sun.COM return (pcieb_plat_peekpoke(dip, rdip, ctlop, arg, result)); 52610187SKrishna.Elango@Sun.COM case DDI_CTLOPS_ATTACH: 52710187SKrishna.Elango@Sun.COM if (!pcie_is_child(dip, rdip)) 52810187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 52910187SKrishna.Elango@Sun.COM 53010187SKrishna.Elango@Sun.COM as = (struct attachspec *)arg; 53110187SKrishna.Elango@Sun.COM switch (as->when) { 53210187SKrishna.Elango@Sun.COM case DDI_PRE: 53310187SKrishna.Elango@Sun.COM if (as->cmd == DDI_RESUME) { 53410187SKrishna.Elango@Sun.COM pcie_clear_errors(rdip); 53510187SKrishna.Elango@Sun.COM if (pcieb_plat_ctlops(rdip, ctlop, arg) != 53610187SKrishna.Elango@Sun.COM DDI_SUCCESS) 53710187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 53810187SKrishna.Elango@Sun.COM } 53910187SKrishna.Elango@Sun.COM 54010187SKrishna.Elango@Sun.COM if (as->cmd == DDI_ATTACH) 54110187SKrishna.Elango@Sun.COM return (pcie_pm_hold(dip)); 54210187SKrishna.Elango@Sun.COM 54310187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 54410187SKrishna.Elango@Sun.COM 54510187SKrishna.Elango@Sun.COM case DDI_POST: 54610768SRamesh.Chitrothu@Sun.COM if (as->cmd == DDI_ATTACH && 54710768SRamesh.Chitrothu@Sun.COM as->result != DDI_SUCCESS) { 54810768SRamesh.Chitrothu@Sun.COM /* 54910768SRamesh.Chitrothu@Sun.COM * Attach failed for the child device. The child 55010768SRamesh.Chitrothu@Sun.COM * driver may have made PM calls before the 55110768SRamesh.Chitrothu@Sun.COM * attach failed. pcie_pm_remove_child() should 55210768SRamesh.Chitrothu@Sun.COM * cleanup PM state and holds (if any) 55310768SRamesh.Chitrothu@Sun.COM * associated with the child device. 55410768SRamesh.Chitrothu@Sun.COM */ 55510768SRamesh.Chitrothu@Sun.COM return (pcie_pm_remove_child(dip, rdip)); 55610768SRamesh.Chitrothu@Sun.COM } 55710187SKrishna.Elango@Sun.COM 55810187SKrishna.Elango@Sun.COM if (as->result == DDI_SUCCESS) { 55910187SKrishna.Elango@Sun.COM pf_init(rdip, (void *)pcieb->pcieb_fm_ibc, 56010187SKrishna.Elango@Sun.COM as->cmd); 56110187SKrishna.Elango@Sun.COM 56210187SKrishna.Elango@Sun.COM (void) pcieb_plat_ctlops(rdip, ctlop, arg); 56310187SKrishna.Elango@Sun.COM } 56410187SKrishna.Elango@Sun.COM 56510187SKrishna.Elango@Sun.COM /* 56610187SKrishna.Elango@Sun.COM * For empty hotplug-capable slots, we should explicitly 56710187SKrishna.Elango@Sun.COM * disable the errors, so that we won't panic upon 56810187SKrishna.Elango@Sun.COM * unsupported hotplug messages. 56910187SKrishna.Elango@Sun.COM */ 57010187SKrishna.Elango@Sun.COM if ((!ddi_prop_exists(DDI_DEV_T_ANY, rdip, 57110187SKrishna.Elango@Sun.COM DDI_PROP_DONTPASS, "hotplug-capable")) || 57210187SKrishna.Elango@Sun.COM ddi_get_child(rdip)) { 57310187SKrishna.Elango@Sun.COM (void) pcie_postattach_child(rdip); 57410187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 57510187SKrishna.Elango@Sun.COM } 57610187SKrishna.Elango@Sun.COM 57710187SKrishna.Elango@Sun.COM pcie_disable_errors(rdip); 57810187SKrishna.Elango@Sun.COM 57910187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 58010187SKrishna.Elango@Sun.COM default: 58110187SKrishna.Elango@Sun.COM break; 58210187SKrishna.Elango@Sun.COM } 58310187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 58410187SKrishna.Elango@Sun.COM 58510187SKrishna.Elango@Sun.COM case DDI_CTLOPS_DETACH: 58610187SKrishna.Elango@Sun.COM if (!pcie_is_child(dip, rdip)) 58710187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 58810187SKrishna.Elango@Sun.COM 58910187SKrishna.Elango@Sun.COM ds = (struct detachspec *)arg; 59010187SKrishna.Elango@Sun.COM switch (ds->when) { 59110187SKrishna.Elango@Sun.COM case DDI_PRE: 59210187SKrishna.Elango@Sun.COM pf_fini(rdip, ds->cmd); 59310187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 59410187SKrishna.Elango@Sun.COM 59510187SKrishna.Elango@Sun.COM case DDI_POST: 59610187SKrishna.Elango@Sun.COM if (pcieb_plat_ctlops(rdip, ctlop, arg) != DDI_SUCCESS) 59710187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 59810187SKrishna.Elango@Sun.COM if (ds->cmd == DDI_DETACH && 59910187SKrishna.Elango@Sun.COM ds->result == DDI_SUCCESS) { 60010187SKrishna.Elango@Sun.COM return (pcie_pm_remove_child(dip, rdip)); 60110187SKrishna.Elango@Sun.COM } 60210187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 60310187SKrishna.Elango@Sun.COM default: 60410187SKrishna.Elango@Sun.COM break; 60510187SKrishna.Elango@Sun.COM } 60610187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 60710187SKrishna.Elango@Sun.COM default: 60810187SKrishna.Elango@Sun.COM return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 60910187SKrishna.Elango@Sun.COM } 61010187SKrishna.Elango@Sun.COM 61110187SKrishna.Elango@Sun.COM *(int *)result = 0; 61210187SKrishna.Elango@Sun.COM if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, 61310187SKrishna.Elango@Sun.COM DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg", (caddr_t)&drv_regp, 61410187SKrishna.Elango@Sun.COM ®len) != DDI_SUCCESS) 61510187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 61610187SKrishna.Elango@Sun.COM 61710187SKrishna.Elango@Sun.COM totreg = reglen / sizeof (pci_regspec_t); 61810187SKrishna.Elango@Sun.COM if (ctlop == DDI_CTLOPS_NREGS) 61910187SKrishna.Elango@Sun.COM *(int *)result = totreg; 62010187SKrishna.Elango@Sun.COM else if (ctlop == DDI_CTLOPS_REGSIZE) { 62110187SKrishna.Elango@Sun.COM rn = *(int *)arg; 62210187SKrishna.Elango@Sun.COM if (rn >= totreg) { 62310187SKrishna.Elango@Sun.COM kmem_free(drv_regp, reglen); 62410187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 62510187SKrishna.Elango@Sun.COM } 62610187SKrishna.Elango@Sun.COM 62710187SKrishna.Elango@Sun.COM *(off_t *)result = drv_regp[rn].pci_size_low | 62810187SKrishna.Elango@Sun.COM ((uint64_t)drv_regp[rn].pci_size_hi << 32); 62910187SKrishna.Elango@Sun.COM } 63010187SKrishna.Elango@Sun.COM 63110187SKrishna.Elango@Sun.COM kmem_free(drv_regp, reglen); 63210187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 63310187SKrishna.Elango@Sun.COM } 63410187SKrishna.Elango@Sun.COM 63510187SKrishna.Elango@Sun.COM /* 63610187SKrishna.Elango@Sun.COM * name_child 63710187SKrishna.Elango@Sun.COM * 63810187SKrishna.Elango@Sun.COM * This function is called from init_child to name a node. It is 63910187SKrishna.Elango@Sun.COM * also passed as a callback for node merging functions. 64010187SKrishna.Elango@Sun.COM * 64110187SKrishna.Elango@Sun.COM * return value: DDI_SUCCESS, DDI_FAILURE 64210187SKrishna.Elango@Sun.COM */ 64310187SKrishna.Elango@Sun.COM static int 64410187SKrishna.Elango@Sun.COM pcieb_name_child(dev_info_t *child, char *name, int namelen) 64510187SKrishna.Elango@Sun.COM { 64610187SKrishna.Elango@Sun.COM pci_regspec_t *pci_rp; 64710923SEvan.Yan@Sun.COM uint_t device, func; 64810187SKrishna.Elango@Sun.COM char **unit_addr; 64910187SKrishna.Elango@Sun.COM uint_t n; 65010187SKrishna.Elango@Sun.COM 65110187SKrishna.Elango@Sun.COM /* 65210187SKrishna.Elango@Sun.COM * For .conf nodes, use unit-address property as name 65310187SKrishna.Elango@Sun.COM */ 65410187SKrishna.Elango@Sun.COM if (ndi_dev_is_persistent_node(child) == 0) { 65510187SKrishna.Elango@Sun.COM if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 65610187SKrishna.Elango@Sun.COM DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != 65710187SKrishna.Elango@Sun.COM DDI_PROP_SUCCESS) { 65810187SKrishna.Elango@Sun.COM cmn_err(CE_WARN, 65910187SKrishna.Elango@Sun.COM "cannot find unit-address in %s.conf", 66010187SKrishna.Elango@Sun.COM ddi_driver_name(child)); 66110187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 66210187SKrishna.Elango@Sun.COM } 66310187SKrishna.Elango@Sun.COM if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 66410187SKrishna.Elango@Sun.COM cmn_err(CE_WARN, "unit-address property in %s.conf" 66510187SKrishna.Elango@Sun.COM " not well-formed", ddi_driver_name(child)); 66610187SKrishna.Elango@Sun.COM ddi_prop_free(unit_addr); 66710187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 66810187SKrishna.Elango@Sun.COM } 66910187SKrishna.Elango@Sun.COM (void) snprintf(name, namelen, "%s", *unit_addr); 67010187SKrishna.Elango@Sun.COM ddi_prop_free(unit_addr); 67110187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 67210187SKrishna.Elango@Sun.COM } 67310187SKrishna.Elango@Sun.COM 67410187SKrishna.Elango@Sun.COM /* 67510187SKrishna.Elango@Sun.COM * Get the address portion of the node name based on 67610187SKrishna.Elango@Sun.COM * the function and device number. 67710187SKrishna.Elango@Sun.COM */ 67810187SKrishna.Elango@Sun.COM if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 67910187SKrishna.Elango@Sun.COM DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) { 68010187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 68110187SKrishna.Elango@Sun.COM } 68210187SKrishna.Elango@Sun.COM 68310187SKrishna.Elango@Sun.COM /* copy the device identifications */ 68410923SEvan.Yan@Sun.COM device = PCI_REG_DEV_G(pci_rp[0].pci_phys_hi); 68510187SKrishna.Elango@Sun.COM func = PCI_REG_FUNC_G(pci_rp[0].pci_phys_hi); 68610187SKrishna.Elango@Sun.COM 68710923SEvan.Yan@Sun.COM if (pcie_ari_is_enabled(ddi_get_parent(child)) 68810923SEvan.Yan@Sun.COM == PCIE_ARI_FORW_ENABLED) { 68910923SEvan.Yan@Sun.COM func = (device << 3) | func; 69010923SEvan.Yan@Sun.COM device = 0; 69110923SEvan.Yan@Sun.COM } 69210923SEvan.Yan@Sun.COM 69310187SKrishna.Elango@Sun.COM if (func != 0) 69410923SEvan.Yan@Sun.COM (void) snprintf(name, namelen, "%x,%x", device, func); 69510187SKrishna.Elango@Sun.COM else 69610923SEvan.Yan@Sun.COM (void) snprintf(name, namelen, "%x", device); 69710187SKrishna.Elango@Sun.COM 69810187SKrishna.Elango@Sun.COM ddi_prop_free(pci_rp); 69910187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 70010187SKrishna.Elango@Sun.COM } 70110187SKrishna.Elango@Sun.COM 70210187SKrishna.Elango@Sun.COM static int 70310187SKrishna.Elango@Sun.COM pcieb_initchild(dev_info_t *child) 70410187SKrishna.Elango@Sun.COM { 70510187SKrishna.Elango@Sun.COM char name[MAXNAMELEN]; 70610187SKrishna.Elango@Sun.COM int result = DDI_FAILURE; 70710187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb = 70810187SKrishna.Elango@Sun.COM (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state, 70910187SKrishna.Elango@Sun.COM ddi_get_instance(ddi_get_parent(child))); 71010187SKrishna.Elango@Sun.COM 71110187SKrishna.Elango@Sun.COM /* 71210187SKrishna.Elango@Sun.COM * Name the child 71310187SKrishna.Elango@Sun.COM */ 71410187SKrishna.Elango@Sun.COM if (pcieb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS) { 71510187SKrishna.Elango@Sun.COM result = DDI_FAILURE; 71610187SKrishna.Elango@Sun.COM goto done; 71710187SKrishna.Elango@Sun.COM } 71810187SKrishna.Elango@Sun.COM ddi_set_name_addr(child, name); 71910187SKrishna.Elango@Sun.COM 72010187SKrishna.Elango@Sun.COM /* 72110187SKrishna.Elango@Sun.COM * Pseudo nodes indicate a prototype node with per-instance 72210187SKrishna.Elango@Sun.COM * properties to be merged into the real h/w device node. 72310187SKrishna.Elango@Sun.COM * The interpretation of the unit-address is DD[,F] 72410187SKrishna.Elango@Sun.COM * where DD is the device id and F is the function. 72510187SKrishna.Elango@Sun.COM */ 72610187SKrishna.Elango@Sun.COM if (ndi_dev_is_persistent_node(child) == 0) { 72710187SKrishna.Elango@Sun.COM extern int pci_allow_pseudo_children; 72810187SKrishna.Elango@Sun.COM 72910187SKrishna.Elango@Sun.COM /* 73010187SKrishna.Elango@Sun.COM * Try to merge the properties from this prototype 73110187SKrishna.Elango@Sun.COM * node into real h/w nodes. 73210187SKrishna.Elango@Sun.COM */ 73310187SKrishna.Elango@Sun.COM if (ndi_merge_node(child, pcieb_name_child) != DDI_SUCCESS) { 73410187SKrishna.Elango@Sun.COM /* 73510187SKrishna.Elango@Sun.COM * Merged ok - return failure to remove the node. 73610187SKrishna.Elango@Sun.COM */ 73710187SKrishna.Elango@Sun.COM ddi_set_name_addr(child, NULL); 73810187SKrishna.Elango@Sun.COM result = DDI_FAILURE; 73910187SKrishna.Elango@Sun.COM goto done; 74010187SKrishna.Elango@Sun.COM } 74110187SKrishna.Elango@Sun.COM 74210187SKrishna.Elango@Sun.COM /* workaround for ddivs to run under PCI-E */ 74310187SKrishna.Elango@Sun.COM if (pci_allow_pseudo_children) { 74410187SKrishna.Elango@Sun.COM result = DDI_SUCCESS; 74510187SKrishna.Elango@Sun.COM goto done; 74610187SKrishna.Elango@Sun.COM } 74710187SKrishna.Elango@Sun.COM 74810187SKrishna.Elango@Sun.COM /* 74910187SKrishna.Elango@Sun.COM * The child was not merged into a h/w node, 75010187SKrishna.Elango@Sun.COM * but there's not much we can do with it other 75110187SKrishna.Elango@Sun.COM * than return failure to cause the node to be removed. 75210187SKrishna.Elango@Sun.COM */ 75310187SKrishna.Elango@Sun.COM cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", 75410187SKrishna.Elango@Sun.COM ddi_driver_name(child), ddi_get_name_addr(child), 75510187SKrishna.Elango@Sun.COM ddi_driver_name(child)); 75610187SKrishna.Elango@Sun.COM ddi_set_name_addr(child, NULL); 75710187SKrishna.Elango@Sun.COM result = DDI_NOT_WELL_FORMED; 75810187SKrishna.Elango@Sun.COM goto done; 75910187SKrishna.Elango@Sun.COM } 76010187SKrishna.Elango@Sun.COM 76110187SKrishna.Elango@Sun.COM /* platform specific initchild */ 76210187SKrishna.Elango@Sun.COM pcieb_plat_initchild(child); 76310187SKrishna.Elango@Sun.COM 76410187SKrishna.Elango@Sun.COM if (pcie_pm_hold(pcieb->pcieb_dip) != DDI_SUCCESS) { 76510187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, pcieb->pcieb_dip, 76610187SKrishna.Elango@Sun.COM "INITCHILD: px_pm_hold failed\n"); 76710187SKrishna.Elango@Sun.COM result = DDI_FAILURE; 76810187SKrishna.Elango@Sun.COM goto done; 76910187SKrishna.Elango@Sun.COM } 77010187SKrishna.Elango@Sun.COM /* Any return from here must call pcie_pm_release */ 77110187SKrishna.Elango@Sun.COM 77210187SKrishna.Elango@Sun.COM /* 77310187SKrishna.Elango@Sun.COM * If configuration registers were previously saved by 77410187SKrishna.Elango@Sun.COM * child (before it entered D3), then let the child do the 77510187SKrishna.Elango@Sun.COM * restore to set up the config regs as it'll first need to 77610187SKrishna.Elango@Sun.COM * power the device out of D3. 77710187SKrishna.Elango@Sun.COM */ 77810187SKrishna.Elango@Sun.COM if (ddi_prop_exists(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 77910187SKrishna.Elango@Sun.COM "config-regs-saved-by-child") == 1) { 78010187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, ddi_get_parent(child), 78110187SKrishna.Elango@Sun.COM "INITCHILD: config regs to be restored by child" 78210187SKrishna.Elango@Sun.COM " for %s@%s\n", ddi_node_name(child), 78310187SKrishna.Elango@Sun.COM ddi_get_name_addr(child)); 78410187SKrishna.Elango@Sun.COM 78510187SKrishna.Elango@Sun.COM result = DDI_SUCCESS; 78610187SKrishna.Elango@Sun.COM goto cleanup; 78710187SKrishna.Elango@Sun.COM } 78810187SKrishna.Elango@Sun.COM 78910187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, ddi_get_parent(child), 79010187SKrishna.Elango@Sun.COM "INITCHILD: config regs setup for %s@%s\n", 79110187SKrishna.Elango@Sun.COM ddi_node_name(child), ddi_get_name_addr(child)); 79210187SKrishna.Elango@Sun.COM 793*11596SJason.Beloro@Sun.COM pcie_init_dom(child); 794*11596SJason.Beloro@Sun.COM 79511245SZhijun.Fu@Sun.COM if (pcie_initchild(child) != DDI_SUCCESS) { 79610187SKrishna.Elango@Sun.COM result = DDI_FAILURE; 797*11596SJason.Beloro@Sun.COM pcie_fini_dom(child); 79810187SKrishna.Elango@Sun.COM goto cleanup; 79910187SKrishna.Elango@Sun.COM } 80010187SKrishna.Elango@Sun.COM 80110187SKrishna.Elango@Sun.COM #ifdef PX_PLX 80210187SKrishna.Elango@Sun.COM if (pcieb_init_plx_workarounds(pcieb, child) == DDI_FAILURE) { 80310187SKrishna.Elango@Sun.COM result = DDI_FAILURE; 804*11596SJason.Beloro@Sun.COM pcie_fini_dom(child); 80510187SKrishna.Elango@Sun.COM goto cleanup; 80610187SKrishna.Elango@Sun.COM } 80710187SKrishna.Elango@Sun.COM #endif /* PX_PLX */ 80810187SKrishna.Elango@Sun.COM 80910187SKrishna.Elango@Sun.COM result = DDI_SUCCESS; 81010187SKrishna.Elango@Sun.COM cleanup: 81110187SKrishna.Elango@Sun.COM pcie_pm_release(pcieb->pcieb_dip); 81210187SKrishna.Elango@Sun.COM done: 81310187SKrishna.Elango@Sun.COM return (result); 81410187SKrishna.Elango@Sun.COM } 81510187SKrishna.Elango@Sun.COM 81610187SKrishna.Elango@Sun.COM static void 81710187SKrishna.Elango@Sun.COM pcieb_uninitchild(dev_info_t *dip) 81810187SKrishna.Elango@Sun.COM { 81910187SKrishna.Elango@Sun.COM 82010187SKrishna.Elango@Sun.COM pcie_uninitchild(dip); 82110187SKrishna.Elango@Sun.COM 82210187SKrishna.Elango@Sun.COM pcieb_plat_uninitchild(dip); 82310187SKrishna.Elango@Sun.COM 82410187SKrishna.Elango@Sun.COM ddi_set_name_addr(dip, NULL); 82510187SKrishna.Elango@Sun.COM 82610187SKrishna.Elango@Sun.COM /* 82710187SKrishna.Elango@Sun.COM * Strip the node to properly convert it back to prototype form 82810187SKrishna.Elango@Sun.COM */ 82910187SKrishna.Elango@Sun.COM ddi_remove_minor_node(dip, NULL); 83010187SKrishna.Elango@Sun.COM 83110187SKrishna.Elango@Sun.COM ddi_prop_remove_all(dip); 83210187SKrishna.Elango@Sun.COM } 83310187SKrishna.Elango@Sun.COM 83410187SKrishna.Elango@Sun.COM static boolean_t 83510187SKrishna.Elango@Sun.COM pcieb_is_pcie_device_type(dev_info_t *dip) 83610187SKrishna.Elango@Sun.COM { 83710187SKrishna.Elango@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 83810187SKrishna.Elango@Sun.COM 83910187SKrishna.Elango@Sun.COM if (PCIE_IS_SW(bus_p) || PCIE_IS_RP(bus_p) || PCIE_IS_PCI2PCIE(bus_p)) 84010187SKrishna.Elango@Sun.COM return (B_TRUE); 84110187SKrishna.Elango@Sun.COM 84210187SKrishna.Elango@Sun.COM return (B_FALSE); 84310187SKrishna.Elango@Sun.COM } 84410187SKrishna.Elango@Sun.COM 84510187SKrishna.Elango@Sun.COM static int 84610187SKrishna.Elango@Sun.COM pcieb_intr_attach(pcieb_devstate_t *pcieb) 84710187SKrishna.Elango@Sun.COM { 84810187SKrishna.Elango@Sun.COM int intr_types; 84910187SKrishna.Elango@Sun.COM dev_info_t *dip = pcieb->pcieb_dip; 85010187SKrishna.Elango@Sun.COM 85110187SKrishna.Elango@Sun.COM /* Allow platform specific code to do any initialization first */ 85210187SKrishna.Elango@Sun.COM pcieb_plat_intr_attach(pcieb); 85310187SKrishna.Elango@Sun.COM 85410187SKrishna.Elango@Sun.COM /* 85510187SKrishna.Elango@Sun.COM * Initialize interrupt handlers. 85610187SKrishna.Elango@Sun.COM * If both MSI and FIXED are supported, try to attach MSI first. 85710187SKrishna.Elango@Sun.COM * If MSI fails for any reason, then try FIXED, but only allow one 85810187SKrishna.Elango@Sun.COM * type to be attached. 85910187SKrishna.Elango@Sun.COM */ 86010187SKrishna.Elango@Sun.COM if (ddi_intr_get_supported_types(dip, &intr_types) != DDI_SUCCESS) { 86110187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_get_supported_types" 86210187SKrishna.Elango@Sun.COM " failed\n"); 86310187SKrishna.Elango@Sun.COM goto FAIL; 86410187SKrishna.Elango@Sun.COM } 86510187SKrishna.Elango@Sun.COM 86610187SKrishna.Elango@Sun.COM if ((intr_types & DDI_INTR_TYPE_MSI) && 86710187SKrishna.Elango@Sun.COM (pcieb_msi_supported(dip) == DDI_SUCCESS)) { 86810187SKrishna.Elango@Sun.COM if (pcieb_intr_init(pcieb, DDI_INTR_TYPE_MSI) == DDI_SUCCESS) 86910187SKrishna.Elango@Sun.COM intr_types = DDI_INTR_TYPE_MSI; 87010187SKrishna.Elango@Sun.COM else { 87110187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "Unable to attach MSI" 87210187SKrishna.Elango@Sun.COM " handler\n"); 87310187SKrishna.Elango@Sun.COM } 87410187SKrishna.Elango@Sun.COM } 87510187SKrishna.Elango@Sun.COM 87610187SKrishna.Elango@Sun.COM if (intr_types != DDI_INTR_TYPE_MSI) { 87710187SKrishna.Elango@Sun.COM /* 87810187SKrishna.Elango@Sun.COM * MSIs are not supported or MSI initialization failed. For Root 87910187SKrishna.Elango@Sun.COM * Ports mark this so error handling might try to fallback to 88010187SKrishna.Elango@Sun.COM * some other mechanism if available (machinecheck etc.). 88110187SKrishna.Elango@Sun.COM */ 88210187SKrishna.Elango@Sun.COM if (PCIE_IS_RP(PCIE_DIP2UPBUS(dip))) 88310187SKrishna.Elango@Sun.COM pcieb->pcieb_no_aer_msi = B_TRUE; 88410187SKrishna.Elango@Sun.COM } 88510187SKrishna.Elango@Sun.COM 88610187SKrishna.Elango@Sun.COM if (intr_types & DDI_INTR_TYPE_FIXED) { 88710187SKrishna.Elango@Sun.COM if (pcieb_intr_init(pcieb, DDI_INTR_TYPE_FIXED) != 88810187SKrishna.Elango@Sun.COM DDI_SUCCESS) { 88910187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, 89010187SKrishna.Elango@Sun.COM "Unable to attach INTx handler\n"); 89110187SKrishna.Elango@Sun.COM goto FAIL; 89210187SKrishna.Elango@Sun.COM } 89310187SKrishna.Elango@Sun.COM } 89410187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 89510187SKrishna.Elango@Sun.COM 89610187SKrishna.Elango@Sun.COM FAIL: 89710187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 89810187SKrishna.Elango@Sun.COM } 89910187SKrishna.Elango@Sun.COM 90010187SKrishna.Elango@Sun.COM /* 90110187SKrishna.Elango@Sun.COM * This function initializes internally generated interrupts only. 90210187SKrishna.Elango@Sun.COM * It does not affect any interrupts generated by downstream devices 90310187SKrishna.Elango@Sun.COM * or the forwarding of them. 90410187SKrishna.Elango@Sun.COM * 90510187SKrishna.Elango@Sun.COM * Enable Device Specific Interrupts or Hotplug features here. 90610187SKrishna.Elango@Sun.COM * Enabling features may change how many interrupts are requested 90710187SKrishna.Elango@Sun.COM * by the device. If features are not enabled first, the 90810187SKrishna.Elango@Sun.COM * device might not ask for any interrupts. 90910187SKrishna.Elango@Sun.COM */ 91010397SShesha.Sreenivasamurthy@Sun.COM 91110187SKrishna.Elango@Sun.COM static int 91210187SKrishna.Elango@Sun.COM pcieb_intr_init(pcieb_devstate_t *pcieb, int intr_type) 91310187SKrishna.Elango@Sun.COM { 91410187SKrishna.Elango@Sun.COM dev_info_t *dip = pcieb->pcieb_dip; 91510187SKrishna.Elango@Sun.COM int nintrs, request, count, x; 91610187SKrishna.Elango@Sun.COM int intr_cap = 0; 91710187SKrishna.Elango@Sun.COM int inum = 0; 91810187SKrishna.Elango@Sun.COM int ret, hp_msi_off; 91910187SKrishna.Elango@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip); 92010187SKrishna.Elango@Sun.COM uint16_t vendorid = bus_p->bus_dev_ven_id & 0xFFFF; 92110187SKrishna.Elango@Sun.COM boolean_t is_hp = B_FALSE; 92210187SKrishna.Elango@Sun.COM boolean_t is_pme = B_FALSE; 92310187SKrishna.Elango@Sun.COM 92410187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "pcieb_intr_init: Attaching %s handler\n", 92510187SKrishna.Elango@Sun.COM (intr_type == DDI_INTR_TYPE_MSI) ? "MSI" : "INTx"); 92610187SKrishna.Elango@Sun.COM 92710187SKrishna.Elango@Sun.COM request = 0; 92810923SEvan.Yan@Sun.COM if (PCIE_IS_HOTPLUG_ENABLED(dip)) { 92910187SKrishna.Elango@Sun.COM request++; 93010187SKrishna.Elango@Sun.COM is_hp = B_TRUE; 93110187SKrishna.Elango@Sun.COM } 93210187SKrishna.Elango@Sun.COM 93310187SKrishna.Elango@Sun.COM /* 93410187SKrishna.Elango@Sun.COM * Hotplug and PME share the same MSI vector. If hotplug is not 93510187SKrishna.Elango@Sun.COM * supported check if MSI is needed for PME. 93610187SKrishna.Elango@Sun.COM */ 93710187SKrishna.Elango@Sun.COM if ((intr_type == DDI_INTR_TYPE_MSI) && PCIE_IS_RP(bus_p) && 93810187SKrishna.Elango@Sun.COM (vendorid == NVIDIA_VENDOR_ID)) { 93910187SKrishna.Elango@Sun.COM is_pme = B_TRUE; 94010187SKrishna.Elango@Sun.COM if (!is_hp) 94110187SKrishna.Elango@Sun.COM request++; 94210187SKrishna.Elango@Sun.COM } 94310187SKrishna.Elango@Sun.COM 94410187SKrishna.Elango@Sun.COM /* 94510187SKrishna.Elango@Sun.COM * Setup MSI if this device is a Rootport and has AER. Currently no 94610187SKrishna.Elango@Sun.COM * SPARC Root Port supports fabric errors being reported through it. 94710187SKrishna.Elango@Sun.COM */ 94810187SKrishna.Elango@Sun.COM if (intr_type == DDI_INTR_TYPE_MSI) { 94910187SKrishna.Elango@Sun.COM if (PCIE_IS_RP(bus_p) && PCIE_HAS_AER(bus_p)) 95010187SKrishna.Elango@Sun.COM request++; 95110187SKrishna.Elango@Sun.COM } 95210187SKrishna.Elango@Sun.COM 95310187SKrishna.Elango@Sun.COM if (request == 0) 95410187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 95510187SKrishna.Elango@Sun.COM 95610187SKrishna.Elango@Sun.COM /* 95710187SKrishna.Elango@Sun.COM * Get number of supported interrupts. 95810187SKrishna.Elango@Sun.COM * 95910187SKrishna.Elango@Sun.COM * Several Bridges/Switches will not have this property set, resulting 96010187SKrishna.Elango@Sun.COM * in a FAILURE, if the device is not configured in a way that 96110187SKrishna.Elango@Sun.COM * interrupts are needed. (eg. hotplugging) 96210187SKrishna.Elango@Sun.COM */ 96310187SKrishna.Elango@Sun.COM ret = ddi_intr_get_nintrs(dip, intr_type, &nintrs); 96410187SKrishna.Elango@Sun.COM if ((ret != DDI_SUCCESS) || (nintrs == 0)) { 96510187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_get_nintrs ret:%d" 96610187SKrishna.Elango@Sun.COM " req:%d\n", ret, nintrs); 96710187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 96810187SKrishna.Elango@Sun.COM } 96910187SKrishna.Elango@Sun.COM 97010187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "bdf 0x%x: ddi_intr_get_nintrs: nintrs %d", 97110187SKrishna.Elango@Sun.COM " request %d\n", bus_p->bus_bdf, nintrs, request); 97210187SKrishna.Elango@Sun.COM 97310187SKrishna.Elango@Sun.COM if (request > nintrs) 97410187SKrishna.Elango@Sun.COM request = nintrs; 97510187SKrishna.Elango@Sun.COM 97610187SKrishna.Elango@Sun.COM /* Allocate an array of interrupt handlers */ 97710187SKrishna.Elango@Sun.COM pcieb->pcieb_htable_size = sizeof (ddi_intr_handle_t) * request; 97810187SKrishna.Elango@Sun.COM pcieb->pcieb_htable = kmem_zalloc(pcieb->pcieb_htable_size, 97910187SKrishna.Elango@Sun.COM KM_SLEEP); 98010187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_HTABLE; 98110187SKrishna.Elango@Sun.COM 98210187SKrishna.Elango@Sun.COM ret = ddi_intr_alloc(dip, pcieb->pcieb_htable, intr_type, inum, 98310187SKrishna.Elango@Sun.COM request, &count, DDI_INTR_ALLOC_NORMAL); 98410187SKrishna.Elango@Sun.COM if ((ret != DDI_SUCCESS) || (count == 0)) { 98510187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_alloc() ret: %d ask: %d" 98610187SKrishna.Elango@Sun.COM " actual: %d\n", ret, request, count); 98710187SKrishna.Elango@Sun.COM goto FAIL; 98810187SKrishna.Elango@Sun.COM } 98910187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_ALLOC; 99010187SKrishna.Elango@Sun.COM 99110187SKrishna.Elango@Sun.COM /* Save the actual number of interrupts allocated */ 99210187SKrishna.Elango@Sun.COM pcieb->pcieb_intr_count = count; 99310187SKrishna.Elango@Sun.COM if (count < request) { 99410187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "bdf 0%x: Requested Intr: %d" 99510187SKrishna.Elango@Sun.COM " Received: %d\n", bus_p->bus_bdf, request, count); 99610187SKrishna.Elango@Sun.COM } 99710187SKrishna.Elango@Sun.COM 99810187SKrishna.Elango@Sun.COM /* 99910187SKrishna.Elango@Sun.COM * NVidia (MCP55 and other) chipsets have a errata that if the number 100010187SKrishna.Elango@Sun.COM * of requested MSI intrs is not allocated we have to fall back to INTx. 100110187SKrishna.Elango@Sun.COM */ 100210187SKrishna.Elango@Sun.COM if (intr_type == DDI_INTR_TYPE_MSI) { 100310187SKrishna.Elango@Sun.COM if (PCIE_IS_RP(bus_p) && (vendorid == NVIDIA_VENDOR_ID)) { 100410187SKrishna.Elango@Sun.COM if (request != count) 100510187SKrishna.Elango@Sun.COM goto FAIL; 100610187SKrishna.Elango@Sun.COM } 100710187SKrishna.Elango@Sun.COM } 100810187SKrishna.Elango@Sun.COM 100910187SKrishna.Elango@Sun.COM /* Get interrupt priority */ 101010187SKrishna.Elango@Sun.COM ret = ddi_intr_get_pri(pcieb->pcieb_htable[0], 101110187SKrishna.Elango@Sun.COM &pcieb->pcieb_intr_priority); 101210187SKrishna.Elango@Sun.COM if (ret != DDI_SUCCESS) { 101310187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_get_pri() ret: %d\n", 101410187SKrishna.Elango@Sun.COM ret); 101510187SKrishna.Elango@Sun.COM goto FAIL; 101610187SKrishna.Elango@Sun.COM } 101710187SKrishna.Elango@Sun.COM 101810187SKrishna.Elango@Sun.COM if (pcieb->pcieb_intr_priority >= LOCK_LEVEL) { 101910187SKrishna.Elango@Sun.COM pcieb->pcieb_intr_priority = LOCK_LEVEL - 1; 102010187SKrishna.Elango@Sun.COM ret = ddi_intr_set_pri(pcieb->pcieb_htable[0], 102110187SKrishna.Elango@Sun.COM pcieb->pcieb_intr_priority); 102210187SKrishna.Elango@Sun.COM if (ret != DDI_SUCCESS) { 102310187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_set_pri() ret:" 102410187SKrishna.Elango@Sun.COM " %d\n", ret); 102510187SKrishna.Elango@Sun.COM 102610187SKrishna.Elango@Sun.COM goto FAIL; 102710187SKrishna.Elango@Sun.COM } 102810187SKrishna.Elango@Sun.COM } 102910187SKrishna.Elango@Sun.COM 103010187SKrishna.Elango@Sun.COM mutex_init(&pcieb->pcieb_intr_mutex, NULL, MUTEX_DRIVER, NULL); 103110187SKrishna.Elango@Sun.COM 103210187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_MUTEX; 103310187SKrishna.Elango@Sun.COM 103410187SKrishna.Elango@Sun.COM for (count = 0; count < pcieb->pcieb_intr_count; count++) { 103510187SKrishna.Elango@Sun.COM ret = ddi_intr_add_handler(pcieb->pcieb_htable[count], 103610187SKrishna.Elango@Sun.COM pcieb_intr_handler, (caddr_t)pcieb, 103710187SKrishna.Elango@Sun.COM (caddr_t)(uintptr_t)(inum + count)); 103810187SKrishna.Elango@Sun.COM 103910187SKrishna.Elango@Sun.COM if (ret != DDI_SUCCESS) { 104010187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "Cannot add " 104110187SKrishna.Elango@Sun.COM "interrupt(%d)\n", ret); 104210187SKrishna.Elango@Sun.COM break; 104310187SKrishna.Elango@Sun.COM } 104410187SKrishna.Elango@Sun.COM } 104510187SKrishna.Elango@Sun.COM 104610187SKrishna.Elango@Sun.COM /* If unsucessful, remove the added handlers */ 104710187SKrishna.Elango@Sun.COM if (ret != DDI_SUCCESS) { 104810187SKrishna.Elango@Sun.COM for (x = 0; x < count; x++) { 104910187SKrishna.Elango@Sun.COM (void) ddi_intr_remove_handler(pcieb->pcieb_htable[x]); 105010187SKrishna.Elango@Sun.COM } 105110187SKrishna.Elango@Sun.COM goto FAIL; 105210187SKrishna.Elango@Sun.COM } 105310187SKrishna.Elango@Sun.COM 105410187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_HANDLER; 105510187SKrishna.Elango@Sun.COM 105610187SKrishna.Elango@Sun.COM (void) ddi_intr_get_cap(pcieb->pcieb_htable[0], &intr_cap); 105710187SKrishna.Elango@Sun.COM 105810187SKrishna.Elango@Sun.COM /* 105910187SKrishna.Elango@Sun.COM * Get this intr lock because we are not quite ready to handle 106010187SKrishna.Elango@Sun.COM * interrupts immediately after enabling it. The MSI multi register 106110187SKrishna.Elango@Sun.COM * gets programmed in ddi_intr_enable after which we need to get the 106210187SKrishna.Elango@Sun.COM * MSI offsets for Hotplug/AER. 106310187SKrishna.Elango@Sun.COM */ 106410187SKrishna.Elango@Sun.COM mutex_enter(&pcieb->pcieb_intr_mutex); 106510187SKrishna.Elango@Sun.COM 106610187SKrishna.Elango@Sun.COM if (intr_cap & DDI_INTR_FLAG_BLOCK) { 106710187SKrishna.Elango@Sun.COM (void) ddi_intr_block_enable(pcieb->pcieb_htable, 106810187SKrishna.Elango@Sun.COM pcieb->pcieb_intr_count); 106910187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_BLOCK; 107010187SKrishna.Elango@Sun.COM } else { 107110187SKrishna.Elango@Sun.COM for (count = 0; count < pcieb->pcieb_intr_count; count++) { 107210187SKrishna.Elango@Sun.COM (void) ddi_intr_enable(pcieb->pcieb_htable[count]); 107310187SKrishna.Elango@Sun.COM } 107410187SKrishna.Elango@Sun.COM } 107510187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_ENABLE; 107610187SKrishna.Elango@Sun.COM 107710187SKrishna.Elango@Sun.COM /* Save the interrupt type */ 107810187SKrishna.Elango@Sun.COM pcieb->pcieb_intr_type = intr_type; 107910187SKrishna.Elango@Sun.COM 108010187SKrishna.Elango@Sun.COM /* Get the MSI offset for hotplug/PME from the PCIe cap reg */ 108110187SKrishna.Elango@Sun.COM if (intr_type == DDI_INTR_TYPE_MSI) { 108210187SKrishna.Elango@Sun.COM hp_msi_off = PCI_CAP_GET16(bus_p->bus_cfg_hdl, NULL, 108310187SKrishna.Elango@Sun.COM bus_p->bus_pcie_off, PCIE_PCIECAP) & 108410187SKrishna.Elango@Sun.COM PCIE_PCIECAP_INT_MSG_NUM; 108510187SKrishna.Elango@Sun.COM 108610187SKrishna.Elango@Sun.COM if (hp_msi_off >= count) { 108710187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "MSI number %d in PCIe " 108810187SKrishna.Elango@Sun.COM "cap > max allocated %d\n", hp_msi_off, count); 108910187SKrishna.Elango@Sun.COM mutex_exit(&pcieb->pcieb_intr_mutex); 109010187SKrishna.Elango@Sun.COM goto FAIL; 109110187SKrishna.Elango@Sun.COM } 109210187SKrishna.Elango@Sun.COM 109310187SKrishna.Elango@Sun.COM if (is_hp) 109410187SKrishna.Elango@Sun.COM pcieb->pcieb_isr_tab[hp_msi_off] |= PCIEB_INTR_SRC_HP; 109510187SKrishna.Elango@Sun.COM 109610187SKrishna.Elango@Sun.COM if (is_pme) 109710187SKrishna.Elango@Sun.COM pcieb->pcieb_isr_tab[hp_msi_off] |= PCIEB_INTR_SRC_PME; 109810187SKrishna.Elango@Sun.COM } else { 109910187SKrishna.Elango@Sun.COM /* INTx handles only Hotplug interrupts */ 110010187SKrishna.Elango@Sun.COM if (is_hp) 110110187SKrishna.Elango@Sun.COM pcieb->pcieb_isr_tab[0] |= PCIEB_INTR_SRC_HP; 110210187SKrishna.Elango@Sun.COM } 110310187SKrishna.Elango@Sun.COM 110410187SKrishna.Elango@Sun.COM 110510187SKrishna.Elango@Sun.COM /* 110610187SKrishna.Elango@Sun.COM * Get the MSI offset for errors from the AER Root Error status 110710187SKrishna.Elango@Sun.COM * register. 110810187SKrishna.Elango@Sun.COM */ 110910187SKrishna.Elango@Sun.COM if ((intr_type == DDI_INTR_TYPE_MSI) && PCIE_IS_RP(bus_p)) { 111010187SKrishna.Elango@Sun.COM if (PCIE_HAS_AER(bus_p)) { 111110187SKrishna.Elango@Sun.COM int aer_msi_off; 111210187SKrishna.Elango@Sun.COM aer_msi_off = (PCI_XCAP_GET32(bus_p->bus_cfg_hdl, NULL, 111310187SKrishna.Elango@Sun.COM bus_p->bus_aer_off, PCIE_AER_RE_STS) >> 111410187SKrishna.Elango@Sun.COM PCIE_AER_RE_STS_MSG_NUM_SHIFT) & 111510187SKrishna.Elango@Sun.COM PCIE_AER_RE_STS_MSG_NUM_MASK; 111610187SKrishna.Elango@Sun.COM 111710187SKrishna.Elango@Sun.COM if (aer_msi_off >= count) { 111810187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "MSI number %d in" 111910187SKrishna.Elango@Sun.COM " AER cap > max allocated %d\n", 112010187SKrishna.Elango@Sun.COM aer_msi_off, count); 112110187SKrishna.Elango@Sun.COM mutex_exit(&pcieb->pcieb_intr_mutex); 112210187SKrishna.Elango@Sun.COM goto FAIL; 112310187SKrishna.Elango@Sun.COM } 112410187SKrishna.Elango@Sun.COM pcieb->pcieb_isr_tab[aer_msi_off] |= PCIEB_INTR_SRC_AER; 112510187SKrishna.Elango@Sun.COM } else { 112610187SKrishna.Elango@Sun.COM /* 112710187SKrishna.Elango@Sun.COM * This RP does not have AER. Fallback to the 112810187SKrishna.Elango@Sun.COM * SERR+Machinecheck approach if available. 112910187SKrishna.Elango@Sun.COM */ 113010187SKrishna.Elango@Sun.COM pcieb->pcieb_no_aer_msi = B_TRUE; 113110187SKrishna.Elango@Sun.COM } 113210187SKrishna.Elango@Sun.COM } 113310187SKrishna.Elango@Sun.COM 113410187SKrishna.Elango@Sun.COM mutex_exit(&pcieb->pcieb_intr_mutex); 113510187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 113610187SKrishna.Elango@Sun.COM 113710187SKrishna.Elango@Sun.COM FAIL: 113810397SShesha.Sreenivasamurthy@Sun.COM pcieb_intr_fini(pcieb); 113910187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 114010187SKrishna.Elango@Sun.COM } 114110187SKrishna.Elango@Sun.COM 114210187SKrishna.Elango@Sun.COM static void 114310187SKrishna.Elango@Sun.COM pcieb_intr_fini(pcieb_devstate_t *pcieb) 114410187SKrishna.Elango@Sun.COM { 114510187SKrishna.Elango@Sun.COM int x; 114610187SKrishna.Elango@Sun.COM int count = pcieb->pcieb_intr_count; 114710187SKrishna.Elango@Sun.COM int flags = pcieb->pcieb_init_flags; 114810187SKrishna.Elango@Sun.COM 114910187SKrishna.Elango@Sun.COM if ((flags & PCIEB_INIT_ENABLE) && 115010187SKrishna.Elango@Sun.COM (flags & PCIEB_INIT_BLOCK)) { 115110187SKrishna.Elango@Sun.COM (void) ddi_intr_block_disable(pcieb->pcieb_htable, count); 115210187SKrishna.Elango@Sun.COM flags &= ~(PCIEB_INIT_ENABLE | 115310187SKrishna.Elango@Sun.COM PCIEB_INIT_BLOCK); 115410187SKrishna.Elango@Sun.COM } 115510187SKrishna.Elango@Sun.COM 115610187SKrishna.Elango@Sun.COM if (flags & PCIEB_INIT_MUTEX) 115710187SKrishna.Elango@Sun.COM mutex_destroy(&pcieb->pcieb_intr_mutex); 115810187SKrishna.Elango@Sun.COM 115910187SKrishna.Elango@Sun.COM for (x = 0; x < count; x++) { 116010187SKrishna.Elango@Sun.COM if (flags & PCIEB_INIT_ENABLE) 116110187SKrishna.Elango@Sun.COM (void) ddi_intr_disable(pcieb->pcieb_htable[x]); 116210187SKrishna.Elango@Sun.COM 116310187SKrishna.Elango@Sun.COM if (flags & PCIEB_INIT_HANDLER) 116410187SKrishna.Elango@Sun.COM (void) ddi_intr_remove_handler(pcieb->pcieb_htable[x]); 116510187SKrishna.Elango@Sun.COM 116610187SKrishna.Elango@Sun.COM if (flags & PCIEB_INIT_ALLOC) 116710187SKrishna.Elango@Sun.COM (void) ddi_intr_free(pcieb->pcieb_htable[x]); 116810187SKrishna.Elango@Sun.COM } 116910187SKrishna.Elango@Sun.COM 117010187SKrishna.Elango@Sun.COM flags &= ~(PCIEB_INIT_ENABLE | PCIEB_INIT_HANDLER | PCIEB_INIT_ALLOC | 117110187SKrishna.Elango@Sun.COM PCIEB_INIT_MUTEX); 117210187SKrishna.Elango@Sun.COM 117310187SKrishna.Elango@Sun.COM if (flags & PCIEB_INIT_HTABLE) 117410187SKrishna.Elango@Sun.COM kmem_free(pcieb->pcieb_htable, pcieb->pcieb_htable_size); 117510187SKrishna.Elango@Sun.COM 117610187SKrishna.Elango@Sun.COM flags &= ~PCIEB_INIT_HTABLE; 117710187SKrishna.Elango@Sun.COM 117810187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags &= flags; 117910187SKrishna.Elango@Sun.COM } 118010187SKrishna.Elango@Sun.COM 118110187SKrishna.Elango@Sun.COM /* 118210187SKrishna.Elango@Sun.COM * Checks if this device needs MSIs enabled or not. 118310187SKrishna.Elango@Sun.COM */ 118410187SKrishna.Elango@Sun.COM /*ARGSUSED*/ 118510187SKrishna.Elango@Sun.COM static int 118610187SKrishna.Elango@Sun.COM pcieb_msi_supported(dev_info_t *dip) 118710187SKrishna.Elango@Sun.COM { 118810187SKrishna.Elango@Sun.COM return ((pcieb_enable_msi && pcieb_plat_msi_supported(dip)) ? 118910187SKrishna.Elango@Sun.COM DDI_SUCCESS: DDI_FAILURE); 119010187SKrishna.Elango@Sun.COM } 119110187SKrishna.Elango@Sun.COM 119210187SKrishna.Elango@Sun.COM /*ARGSUSED*/ 119310397SShesha.Sreenivasamurthy@Sun.COM static int 119410187SKrishna.Elango@Sun.COM pcieb_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap, 119510187SKrishna.Elango@Sun.COM ddi_iblock_cookie_t *ibc) 119610187SKrishna.Elango@Sun.COM { 119710187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, 119810187SKrishna.Elango@Sun.COM ddi_get_instance(dip)); 119910187SKrishna.Elango@Sun.COM 120010187SKrishna.Elango@Sun.COM ASSERT(ibc != NULL); 120110187SKrishna.Elango@Sun.COM *ibc = pcieb->pcieb_fm_ibc; 120210187SKrishna.Elango@Sun.COM 120310187SKrishna.Elango@Sun.COM return (DEVI(dip)->devi_fmhdl->fh_cap | DDI_FM_ACCCHK_CAPABLE | 120410187SKrishna.Elango@Sun.COM DDI_FM_DMACHK_CAPABLE); 120510187SKrishna.Elango@Sun.COM } 120610187SKrishna.Elango@Sun.COM 120710187SKrishna.Elango@Sun.COM static int 120810187SKrishna.Elango@Sun.COM pcieb_fm_init(pcieb_devstate_t *pcieb_p) 120910187SKrishna.Elango@Sun.COM { 121010187SKrishna.Elango@Sun.COM dev_info_t *dip = pcieb_p->pcieb_dip; 121110187SKrishna.Elango@Sun.COM int fm_cap = DDI_FM_EREPORT_CAPABLE; 121210187SKrishna.Elango@Sun.COM 121310187SKrishna.Elango@Sun.COM /* 121410187SKrishna.Elango@Sun.COM * Request our capability level and get our parents capability 121510187SKrishna.Elango@Sun.COM * and ibc. 121610187SKrishna.Elango@Sun.COM */ 121710187SKrishna.Elango@Sun.COM ddi_fm_init(dip, &fm_cap, &pcieb_p->pcieb_fm_ibc); 121810187SKrishna.Elango@Sun.COM 121910187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 122010187SKrishna.Elango@Sun.COM } 122110187SKrishna.Elango@Sun.COM 122210187SKrishna.Elango@Sun.COM /* 122310187SKrishna.Elango@Sun.COM * Breakdown our FMA resources 122410187SKrishna.Elango@Sun.COM */ 122510187SKrishna.Elango@Sun.COM static void 122610187SKrishna.Elango@Sun.COM pcieb_fm_fini(pcieb_devstate_t *pcieb_p) 122710187SKrishna.Elango@Sun.COM { 122810187SKrishna.Elango@Sun.COM /* 122910187SKrishna.Elango@Sun.COM * Clean up allocated fm structures 123010187SKrishna.Elango@Sun.COM */ 123110187SKrishna.Elango@Sun.COM ddi_fm_fini(pcieb_p->pcieb_dip); 123210187SKrishna.Elango@Sun.COM } 123310187SKrishna.Elango@Sun.COM 123410187SKrishna.Elango@Sun.COM static int 123510187SKrishna.Elango@Sun.COM pcieb_open(dev_t *devp, int flags, int otyp, cred_t *credp) 123610187SKrishna.Elango@Sun.COM { 123710923SEvan.Yan@Sun.COM int inst = PCI_MINOR_NUM_TO_INSTANCE(getminor(*devp)); 123810923SEvan.Yan@Sun.COM pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, inst); 123910923SEvan.Yan@Sun.COM int rv; 124010187SKrishna.Elango@Sun.COM 124110923SEvan.Yan@Sun.COM if (pcieb == NULL) 124210187SKrishna.Elango@Sun.COM return (ENXIO); 124310187SKrishna.Elango@Sun.COM 124410923SEvan.Yan@Sun.COM mutex_enter(&pcieb->pcieb_mutex); 124510923SEvan.Yan@Sun.COM rv = pcie_open(pcieb->pcieb_dip, devp, flags, otyp, credp); 124610923SEvan.Yan@Sun.COM mutex_exit(&pcieb->pcieb_mutex); 124710187SKrishna.Elango@Sun.COM 124810923SEvan.Yan@Sun.COM return (rv); 124910187SKrishna.Elango@Sun.COM } 125010187SKrishna.Elango@Sun.COM 125110187SKrishna.Elango@Sun.COM static int 125210187SKrishna.Elango@Sun.COM pcieb_close(dev_t dev, int flags, int otyp, cred_t *credp) 125310187SKrishna.Elango@Sun.COM { 125410923SEvan.Yan@Sun.COM int inst = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev)); 125510923SEvan.Yan@Sun.COM pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, inst); 125610923SEvan.Yan@Sun.COM int rv; 125710187SKrishna.Elango@Sun.COM 125810923SEvan.Yan@Sun.COM if (pcieb == NULL) 125910187SKrishna.Elango@Sun.COM return (ENXIO); 126010187SKrishna.Elango@Sun.COM 126110923SEvan.Yan@Sun.COM mutex_enter(&pcieb->pcieb_mutex); 126210923SEvan.Yan@Sun.COM rv = pcie_close(pcieb->pcieb_dip, dev, flags, otyp, credp); 126310923SEvan.Yan@Sun.COM mutex_exit(&pcieb->pcieb_mutex); 126410187SKrishna.Elango@Sun.COM 126510923SEvan.Yan@Sun.COM return (rv); 126610187SKrishna.Elango@Sun.COM } 126710187SKrishna.Elango@Sun.COM 126810187SKrishna.Elango@Sun.COM static int 126910187SKrishna.Elango@Sun.COM pcieb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 127010187SKrishna.Elango@Sun.COM int *rvalp) 127110187SKrishna.Elango@Sun.COM { 127210923SEvan.Yan@Sun.COM int inst = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev)); 127310923SEvan.Yan@Sun.COM pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, inst); 127410923SEvan.Yan@Sun.COM int rv; 127510187SKrishna.Elango@Sun.COM 127610923SEvan.Yan@Sun.COM if (pcieb == NULL) 127710187SKrishna.Elango@Sun.COM return (ENXIO); 127810187SKrishna.Elango@Sun.COM 127910923SEvan.Yan@Sun.COM /* To handle devctl and hotplug related ioctls */ 128010923SEvan.Yan@Sun.COM rv = pcie_ioctl(pcieb->pcieb_dip, dev, cmd, arg, mode, credp, rvalp); 128110187SKrishna.Elango@Sun.COM 128210187SKrishna.Elango@Sun.COM return (rv); 128310187SKrishna.Elango@Sun.COM } 128410187SKrishna.Elango@Sun.COM 128510187SKrishna.Elango@Sun.COM /* 128610187SKrishna.Elango@Sun.COM * Common interrupt handler for hotplug, PME and errors. 128710187SKrishna.Elango@Sun.COM */ 128810187SKrishna.Elango@Sun.COM static uint_t 128910187SKrishna.Elango@Sun.COM pcieb_intr_handler(caddr_t arg1, caddr_t arg2) 129010187SKrishna.Elango@Sun.COM { 129110187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb_p = (pcieb_devstate_t *)arg1; 129210187SKrishna.Elango@Sun.COM dev_info_t *dip = pcieb_p->pcieb_dip; 129310187SKrishna.Elango@Sun.COM ddi_fm_error_t derr; 129410187SKrishna.Elango@Sun.COM int sts = 0; 129510187SKrishna.Elango@Sun.COM int ret = DDI_INTR_UNCLAIMED; 129610187SKrishna.Elango@Sun.COM int isrc; 129710187SKrishna.Elango@Sun.COM 129810187SKrishna.Elango@Sun.COM if (!(pcieb_p->pcieb_init_flags & PCIEB_INIT_ENABLE)) 129910187SKrishna.Elango@Sun.COM goto FAIL; 130010187SKrishna.Elango@Sun.COM 130110187SKrishna.Elango@Sun.COM mutex_enter(&pcieb_p->pcieb_intr_mutex); 130210187SKrishna.Elango@Sun.COM isrc = pcieb_p->pcieb_isr_tab[(int)(uintptr_t)arg2]; 130310187SKrishna.Elango@Sun.COM mutex_exit(&pcieb_p->pcieb_intr_mutex); 130410187SKrishna.Elango@Sun.COM 130510187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_INTR, dip, "Received intr number %d\n", 130610187SKrishna.Elango@Sun.COM (int)(uintptr_t)arg2); 130710187SKrishna.Elango@Sun.COM 130810187SKrishna.Elango@Sun.COM if (isrc == PCIEB_INTR_SRC_UNKNOWN) 130910187SKrishna.Elango@Sun.COM goto FAIL; 131010187SKrishna.Elango@Sun.COM 131110923SEvan.Yan@Sun.COM if (isrc & PCIEB_INTR_SRC_HP) 131210923SEvan.Yan@Sun.COM ret = pcie_intr(dip); 131310187SKrishna.Elango@Sun.COM 131410187SKrishna.Elango@Sun.COM if (isrc & PCIEB_INTR_SRC_PME) 131510187SKrishna.Elango@Sun.COM ret = DDI_INTR_CLAIMED; 131610187SKrishna.Elango@Sun.COM 131710187SKrishna.Elango@Sun.COM /* AER Error */ 131810187SKrishna.Elango@Sun.COM if (isrc & PCIEB_INTR_SRC_AER) { 131910187SKrishna.Elango@Sun.COM /* 132010187SKrishna.Elango@Sun.COM * If MSI is shared with PME/hotplug then check Root Error 132110187SKrishna.Elango@Sun.COM * Status Reg before claiming it. For now it's ok since 132210187SKrishna.Elango@Sun.COM * we know we get 2 MSIs. 132310187SKrishna.Elango@Sun.COM */ 132410187SKrishna.Elango@Sun.COM ret = DDI_INTR_CLAIMED; 132510187SKrishna.Elango@Sun.COM bzero(&derr, sizeof (ddi_fm_error_t)); 132610187SKrishna.Elango@Sun.COM derr.fme_version = DDI_FME_VERSION; 132710187SKrishna.Elango@Sun.COM mutex_enter(&pcieb_p->pcieb_peek_poke_mutex); 132810187SKrishna.Elango@Sun.COM mutex_enter(&pcieb_p->pcieb_err_mutex); 132910187SKrishna.Elango@Sun.COM 1330*11596SJason.Beloro@Sun.COM pf_eh_enter(PCIE_DIP2BUS(dip)); 1331*11596SJason.Beloro@Sun.COM PCIE_ROOT_EH_SRC(PCIE_DIP2PFD(dip))->intr_type = 1332*11596SJason.Beloro@Sun.COM PF_INTR_TYPE_AER; 1333*11596SJason.Beloro@Sun.COM 133410187SKrishna.Elango@Sun.COM if ((DEVI(dip)->devi_fmhdl->fh_cap) & DDI_FM_EREPORT_CAPABLE) 133510187SKrishna.Elango@Sun.COM sts = pf_scan_fabric(dip, &derr, NULL); 1336*11596SJason.Beloro@Sun.COM pf_eh_exit(PCIE_DIP2BUS(dip)); 133710187SKrishna.Elango@Sun.COM 133810187SKrishna.Elango@Sun.COM mutex_exit(&pcieb_p->pcieb_err_mutex); 133910187SKrishna.Elango@Sun.COM mutex_exit(&pcieb_p->pcieb_peek_poke_mutex); 134010187SKrishna.Elango@Sun.COM if (pcieb_die & sts) 134110187SKrishna.Elango@Sun.COM fm_panic("%s-%d: PCI(-X) Express Fatal Error. (0x%x)", 134210187SKrishna.Elango@Sun.COM ddi_driver_name(dip), ddi_get_instance(dip), sts); 134310187SKrishna.Elango@Sun.COM } 134410187SKrishna.Elango@Sun.COM FAIL: 134510187SKrishna.Elango@Sun.COM return (ret); 134610187SKrishna.Elango@Sun.COM } 134710187SKrishna.Elango@Sun.COM 134810187SKrishna.Elango@Sun.COM /* 134910187SKrishna.Elango@Sun.COM * Some PCI-X to PCI-E bridges do not support full 64-bit addressing on the 135010187SKrishna.Elango@Sun.COM * PCI-X side of the bridge. We build a special version of this driver for 135110187SKrishna.Elango@Sun.COM * those bridges, which uses PCIEB_ADDR_LIMIT_LO and/or PCIEB_ADDR_LIMIT_HI 135210187SKrishna.Elango@Sun.COM * to define the range of values which the chip can handle. The code below 135310187SKrishna.Elango@Sun.COM * then clamps the DMA address range supplied by the driver, preventing the 135410187SKrishna.Elango@Sun.COM * PCI-E nexus driver from allocating any memory the bridge can't deal 135510187SKrishna.Elango@Sun.COM * with. 135610187SKrishna.Elango@Sun.COM */ 135710187SKrishna.Elango@Sun.COM static int 135810187SKrishna.Elango@Sun.COM pcieb_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, 135910187SKrishna.Elango@Sun.COM ddi_dma_attr_t *attr_p, int (*waitfp)(caddr_t), caddr_t arg, 136010187SKrishna.Elango@Sun.COM ddi_dma_handle_t *handlep) 136110187SKrishna.Elango@Sun.COM { 136210187SKrishna.Elango@Sun.COM int ret; 136310187SKrishna.Elango@Sun.COM #ifdef BCM_SW_WORKAROUNDS 136410187SKrishna.Elango@Sun.COM uint64_t lim; 136510187SKrishna.Elango@Sun.COM 136610187SKrishna.Elango@Sun.COM /* 136710187SKrishna.Elango@Sun.COM * If the leaf device's limits are outside than what the Broadcom 136810187SKrishna.Elango@Sun.COM * bridge can handle, we need to clip the values passed up the chain. 136910187SKrishna.Elango@Sun.COM */ 137010187SKrishna.Elango@Sun.COM lim = attr_p->dma_attr_addr_lo; 137110187SKrishna.Elango@Sun.COM attr_p->dma_attr_addr_lo = MAX(lim, PCIEB_ADDR_LIMIT_LO); 137210187SKrishna.Elango@Sun.COM 137310187SKrishna.Elango@Sun.COM lim = attr_p->dma_attr_addr_hi; 137410187SKrishna.Elango@Sun.COM attr_p->dma_attr_addr_hi = MIN(lim, PCIEB_ADDR_LIMIT_HI); 137510187SKrishna.Elango@Sun.COM 137610187SKrishna.Elango@Sun.COM #endif /* BCM_SW_WORKAROUNDS */ 137710187SKrishna.Elango@Sun.COM 137810187SKrishna.Elango@Sun.COM /* 137910187SKrishna.Elango@Sun.COM * This is a software workaround to fix the Broadcom 5714/5715 PCIe-PCI 138010187SKrishna.Elango@Sun.COM * bridge prefetch bug. Intercept the DMA alloc handle request and set 138110187SKrishna.Elango@Sun.COM * PX_DMAI_FLAGS_MAP_BUFZONE flag in the handle. If this flag is set, 138210187SKrishna.Elango@Sun.COM * the px nexus driver will allocate an extra page & make it valid one, 138310187SKrishna.Elango@Sun.COM * for any DVMA request that comes from any of the Broadcom bridge child 138410187SKrishna.Elango@Sun.COM * devices. 138510187SKrishna.Elango@Sun.COM */ 138610187SKrishna.Elango@Sun.COM if ((ret = ddi_dma_allochdl(dip, rdip, attr_p, waitfp, arg, 138710187SKrishna.Elango@Sun.COM handlep)) == DDI_SUCCESS) { 138810187SKrishna.Elango@Sun.COM ddi_dma_impl_t *mp = (ddi_dma_impl_t *)*handlep; 138910187SKrishna.Elango@Sun.COM #ifdef BCM_SW_WORKAROUNDS 139010187SKrishna.Elango@Sun.COM mp->dmai_inuse |= PX_DMAI_FLAGS_MAP_BUFZONE; 139110187SKrishna.Elango@Sun.COM #endif /* BCM_SW_WORKAROUNDS */ 139210187SKrishna.Elango@Sun.COM /* 139310187SKrishna.Elango@Sun.COM * For a given rdip, update mp->dmai_bdf with the bdf value 139410187SKrishna.Elango@Sun.COM * of pcieb's immediate child or secondary bus-id of the 139510187SKrishna.Elango@Sun.COM * PCIe2PCI bridge. 139610187SKrishna.Elango@Sun.COM */ 139710187SKrishna.Elango@Sun.COM mp->dmai_minxfer = pcie_get_bdf_for_dma_xfer(dip, rdip); 139810187SKrishna.Elango@Sun.COM } 139910187SKrishna.Elango@Sun.COM 140010187SKrishna.Elango@Sun.COM return (ret); 140110187SKrishna.Elango@Sun.COM } 140210187SKrishna.Elango@Sun.COM 140310187SKrishna.Elango@Sun.COM /* 140410187SKrishna.Elango@Sun.COM * FDVMA feature is not supported for any child device of Broadcom 5714/5715 140510187SKrishna.Elango@Sun.COM * PCIe-PCI bridge due to prefetch bug. Return failure immediately, so that 140610187SKrishna.Elango@Sun.COM * these drivers will switch to regular DVMA path. 140710187SKrishna.Elango@Sun.COM */ 140810187SKrishna.Elango@Sun.COM /*ARGSUSED*/ 140910187SKrishna.Elango@Sun.COM static int 141010187SKrishna.Elango@Sun.COM pcieb_dma_mctl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle, 141110187SKrishna.Elango@Sun.COM enum ddi_dma_ctlops cmd, off_t *offp, size_t *lenp, caddr_t *objp, 141210187SKrishna.Elango@Sun.COM uint_t cache_flags) 141310187SKrishna.Elango@Sun.COM { 141410187SKrishna.Elango@Sun.COM int ret; 141510187SKrishna.Elango@Sun.COM 141610187SKrishna.Elango@Sun.COM #ifdef BCM_SW_WORKAROUNDS 141710187SKrishna.Elango@Sun.COM if (cmd == DDI_DMA_RESERVE) 141810187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 141910187SKrishna.Elango@Sun.COM #endif /* BCM_SW_WORKAROUNDS */ 142010187SKrishna.Elango@Sun.COM 142110187SKrishna.Elango@Sun.COM if (((ret = ddi_dma_mctl(dip, rdip, handle, cmd, offp, lenp, objp, 142210187SKrishna.Elango@Sun.COM cache_flags)) == DDI_SUCCESS) && (cmd == DDI_DMA_RESERVE)) { 142310187SKrishna.Elango@Sun.COM ddi_dma_impl_t *mp = (ddi_dma_impl_t *)*objp; 142410187SKrishna.Elango@Sun.COM 142510187SKrishna.Elango@Sun.COM /* 142610187SKrishna.Elango@Sun.COM * For a given rdip, update mp->dmai_bdf with the bdf value 142710187SKrishna.Elango@Sun.COM * of pcieb's immediate child or secondary bus-id of the 142810187SKrishna.Elango@Sun.COM * PCIe2PCI bridge. 142910187SKrishna.Elango@Sun.COM */ 143010187SKrishna.Elango@Sun.COM mp->dmai_minxfer = pcie_get_bdf_for_dma_xfer(dip, rdip); 143110187SKrishna.Elango@Sun.COM } 143210187SKrishna.Elango@Sun.COM 143310187SKrishna.Elango@Sun.COM return (ret); 143410187SKrishna.Elango@Sun.COM } 143510187SKrishna.Elango@Sun.COM 143610187SKrishna.Elango@Sun.COM static int 143710187SKrishna.Elango@Sun.COM pcieb_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 143810187SKrishna.Elango@Sun.COM ddi_intr_handle_impl_t *hdlp, void *result) 143910187SKrishna.Elango@Sun.COM { 144010187SKrishna.Elango@Sun.COM return (pcieb_plat_intr_ops(dip, rdip, intr_op, hdlp, result)); 144110187SKrishna.Elango@Sun.COM 144210187SKrishna.Elango@Sun.COM } 144310187SKrishna.Elango@Sun.COM 144410187SKrishna.Elango@Sun.COM /* 144510187SKrishna.Elango@Sun.COM * Power management related initialization specific to pcieb. 144610187SKrishna.Elango@Sun.COM * Called by pcieb_attach() 144710187SKrishna.Elango@Sun.COM */ 144810187SKrishna.Elango@Sun.COM static int 144910187SKrishna.Elango@Sun.COM pcieb_pwr_setup(dev_info_t *dip) 145010187SKrishna.Elango@Sun.COM { 145110187SKrishna.Elango@Sun.COM char *comp_array[5]; 145210187SKrishna.Elango@Sun.COM int i; 145310187SKrishna.Elango@Sun.COM ddi_acc_handle_t conf_hdl; 145410187SKrishna.Elango@Sun.COM uint16_t pmcap, cap_ptr; 145510187SKrishna.Elango@Sun.COM pcie_pwr_t *pwr_p; 145610187SKrishna.Elango@Sun.COM 145710187SKrishna.Elango@Sun.COM /* Some platforms/devices may choose to disable PM */ 145810187SKrishna.Elango@Sun.COM if (pcieb_plat_pwr_disable(dip)) { 145910187SKrishna.Elango@Sun.COM (void) pcieb_pwr_disable(dip); 146010187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 146110187SKrishna.Elango@Sun.COM } 146210187SKrishna.Elango@Sun.COM 146310187SKrishna.Elango@Sun.COM ASSERT(PCIE_PMINFO(dip)); 146410187SKrishna.Elango@Sun.COM pwr_p = PCIE_NEXUS_PMINFO(dip); 146510187SKrishna.Elango@Sun.COM ASSERT(pwr_p); 146610187SKrishna.Elango@Sun.COM 146710187SKrishna.Elango@Sun.COM /* Code taken from pci_pci driver */ 146810187SKrishna.Elango@Sun.COM if (pci_config_setup(dip, &pwr_p->pwr_conf_hdl) != DDI_SUCCESS) { 146910187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "pcieb_pwr_setup: pci_config_setup " 147010187SKrishna.Elango@Sun.COM "failed\n"); 147110187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 147210187SKrishna.Elango@Sun.COM } 147310187SKrishna.Elango@Sun.COM conf_hdl = pwr_p->pwr_conf_hdl; 147410187SKrishna.Elango@Sun.COM 147510187SKrishna.Elango@Sun.COM /* 147610187SKrishna.Elango@Sun.COM * Walk the capabilities searching for a PM entry. 147710187SKrishna.Elango@Sun.COM */ 147810187SKrishna.Elango@Sun.COM if ((PCI_CAP_LOCATE(conf_hdl, PCI_CAP_ID_PM, &cap_ptr)) == 147910187SKrishna.Elango@Sun.COM DDI_FAILURE) { 148010187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "switch/bridge does not support PM. " 148110187SKrishna.Elango@Sun.COM " PCI PM data structure not found in config header\n"); 148210187SKrishna.Elango@Sun.COM pci_config_teardown(&conf_hdl); 148310187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 148410187SKrishna.Elango@Sun.COM } 148510187SKrishna.Elango@Sun.COM /* 148610187SKrishna.Elango@Sun.COM * Save offset to pmcsr for future references. 148710187SKrishna.Elango@Sun.COM */ 148810187SKrishna.Elango@Sun.COM pwr_p->pwr_pmcsr_offset = cap_ptr + PCI_PMCSR; 148910187SKrishna.Elango@Sun.COM pmcap = PCI_CAP_GET16(conf_hdl, NULL, cap_ptr, PCI_PMCAP); 149010187SKrishna.Elango@Sun.COM if (pmcap & PCI_PMCAP_D1) { 149110187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "D1 state supported\n"); 149210187SKrishna.Elango@Sun.COM pwr_p->pwr_pmcaps |= PCIE_SUPPORTS_D1; 149310187SKrishna.Elango@Sun.COM } 149410187SKrishna.Elango@Sun.COM if (pmcap & PCI_PMCAP_D2) { 149510187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "D2 state supported\n"); 149610187SKrishna.Elango@Sun.COM pwr_p->pwr_pmcaps |= PCIE_SUPPORTS_D2; 149710187SKrishna.Elango@Sun.COM } 149810187SKrishna.Elango@Sun.COM 149910187SKrishna.Elango@Sun.COM i = 0; 150010187SKrishna.Elango@Sun.COM comp_array[i++] = "NAME=PCIe switch/bridge PM"; 150110187SKrishna.Elango@Sun.COM comp_array[i++] = "0=Power Off (D3)"; 150210187SKrishna.Elango@Sun.COM if (pwr_p->pwr_pmcaps & PCIE_SUPPORTS_D2) 150310187SKrishna.Elango@Sun.COM comp_array[i++] = "1=D2"; 150410187SKrishna.Elango@Sun.COM if (pwr_p->pwr_pmcaps & PCIE_SUPPORTS_D1) 150510187SKrishna.Elango@Sun.COM comp_array[i++] = "2=D1"; 150610187SKrishna.Elango@Sun.COM comp_array[i++] = "3=Full Power D0"; 150710187SKrishna.Elango@Sun.COM 150810187SKrishna.Elango@Sun.COM /* 150910187SKrishna.Elango@Sun.COM * Create pm-components property, if it does not exist already. 151010187SKrishna.Elango@Sun.COM */ 151110187SKrishna.Elango@Sun.COM if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip, 151210187SKrishna.Elango@Sun.COM "pm-components", comp_array, i) != DDI_PROP_SUCCESS) { 151310187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "could not create pm-components " 151410187SKrishna.Elango@Sun.COM " prop\n"); 151510187SKrishna.Elango@Sun.COM pci_config_teardown(&conf_hdl); 151610187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 151710187SKrishna.Elango@Sun.COM } 151810187SKrishna.Elango@Sun.COM return (pcieb_pwr_init_and_raise(dip, pwr_p)); 151910187SKrishna.Elango@Sun.COM } 152010187SKrishna.Elango@Sun.COM 152110187SKrishna.Elango@Sun.COM /* 152210187SKrishna.Elango@Sun.COM * undo whatever is done in pcieb_pwr_setup. called by pcieb_detach() 152310187SKrishna.Elango@Sun.COM */ 152410187SKrishna.Elango@Sun.COM static void 152510187SKrishna.Elango@Sun.COM pcieb_pwr_teardown(dev_info_t *dip) 152610187SKrishna.Elango@Sun.COM { 152710187SKrishna.Elango@Sun.COM pcie_pwr_t *pwr_p; 152810187SKrishna.Elango@Sun.COM 152910187SKrishna.Elango@Sun.COM if (!PCIE_PMINFO(dip) || !(pwr_p = PCIE_NEXUS_PMINFO(dip))) 153010187SKrishna.Elango@Sun.COM return; 153110187SKrishna.Elango@Sun.COM 153210187SKrishna.Elango@Sun.COM (void) ddi_prop_remove(DDI_DEV_T_NONE, dip, "pm-components"); 153310187SKrishna.Elango@Sun.COM if (pwr_p->pwr_conf_hdl) 153410187SKrishna.Elango@Sun.COM pci_config_teardown(&pwr_p->pwr_conf_hdl); 153510187SKrishna.Elango@Sun.COM } 153610187SKrishna.Elango@Sun.COM 153710187SKrishna.Elango@Sun.COM /* 153810187SKrishna.Elango@Sun.COM * Initializes the power level and raise the power to D0, if it is 153910187SKrishna.Elango@Sun.COM * not at D0. 154010187SKrishna.Elango@Sun.COM */ 154110187SKrishna.Elango@Sun.COM static int 154210187SKrishna.Elango@Sun.COM pcieb_pwr_init_and_raise(dev_info_t *dip, pcie_pwr_t *pwr_p) 154310187SKrishna.Elango@Sun.COM { 154410187SKrishna.Elango@Sun.COM uint16_t pmcsr; 154510187SKrishna.Elango@Sun.COM int ret = DDI_SUCCESS; 154610187SKrishna.Elango@Sun.COM 154710187SKrishna.Elango@Sun.COM /* 154810187SKrishna.Elango@Sun.COM * Intialize our power level from PMCSR. The common code initializes 154910187SKrishna.Elango@Sun.COM * this to UNKNOWN. There is no guarantee that we will be at full 155010187SKrishna.Elango@Sun.COM * power at attach. If we are not at D0, raise the power. 155110187SKrishna.Elango@Sun.COM */ 155210187SKrishna.Elango@Sun.COM pmcsr = pci_config_get16(pwr_p->pwr_conf_hdl, pwr_p->pwr_pmcsr_offset); 155310187SKrishna.Elango@Sun.COM pmcsr &= PCI_PMCSR_STATE_MASK; 155410187SKrishna.Elango@Sun.COM switch (pmcsr) { 155510187SKrishna.Elango@Sun.COM case PCI_PMCSR_D0: 155610187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D0; 155710187SKrishna.Elango@Sun.COM break; 155810187SKrishna.Elango@Sun.COM 155910187SKrishna.Elango@Sun.COM case PCI_PMCSR_D1: 156010187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D1; 156110187SKrishna.Elango@Sun.COM break; 156210187SKrishna.Elango@Sun.COM 156310187SKrishna.Elango@Sun.COM case PCI_PMCSR_D2: 156410187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D2; 156510187SKrishna.Elango@Sun.COM break; 156610187SKrishna.Elango@Sun.COM 156710187SKrishna.Elango@Sun.COM case PCI_PMCSR_D3HOT: 156810187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D3; 156910187SKrishna.Elango@Sun.COM break; 157010187SKrishna.Elango@Sun.COM 157110187SKrishna.Elango@Sun.COM default: 157210187SKrishna.Elango@Sun.COM break; 157310187SKrishna.Elango@Sun.COM } 157410187SKrishna.Elango@Sun.COM 157510187SKrishna.Elango@Sun.COM /* Raise the power to D0. */ 157610187SKrishna.Elango@Sun.COM if (pwr_p->pwr_func_lvl != PM_LEVEL_D0 && 157710187SKrishna.Elango@Sun.COM ((ret = pm_raise_power(dip, 0, PM_LEVEL_D0)) != DDI_SUCCESS)) { 157810187SKrishna.Elango@Sun.COM /* 157910187SKrishna.Elango@Sun.COM * Read PMCSR again. If it is at D0, ignore the return 158010187SKrishna.Elango@Sun.COM * value from pm_raise_power. 158110187SKrishna.Elango@Sun.COM */ 158210187SKrishna.Elango@Sun.COM pmcsr = pci_config_get16(pwr_p->pwr_conf_hdl, 158310187SKrishna.Elango@Sun.COM pwr_p->pwr_pmcsr_offset); 158410187SKrishna.Elango@Sun.COM if ((pmcsr & PCI_PMCSR_STATE_MASK) == PCI_PMCSR_D0) 158510187SKrishna.Elango@Sun.COM ret = DDI_SUCCESS; 158610187SKrishna.Elango@Sun.COM else { 158710187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "pcieb_pwr_setup: could not " 158810187SKrishna.Elango@Sun.COM "raise power to D0 \n"); 158910187SKrishna.Elango@Sun.COM } 159010187SKrishna.Elango@Sun.COM } 159110187SKrishna.Elango@Sun.COM if (ret == DDI_SUCCESS) 159210187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D0; 159310187SKrishna.Elango@Sun.COM return (ret); 159410187SKrishna.Elango@Sun.COM } 159510187SKrishna.Elango@Sun.COM 159610187SKrishna.Elango@Sun.COM /* 159710187SKrishna.Elango@Sun.COM * Disable PM for x86 and PLX 8532 switch. 159810187SKrishna.Elango@Sun.COM * For PLX Transitioning one port on this switch to low power causes links 159910187SKrishna.Elango@Sun.COM * on other ports on the same station to die. Due to PLX erratum #34, we 160010187SKrishna.Elango@Sun.COM * can't allow the downstream device go to non-D0 state. 160110187SKrishna.Elango@Sun.COM */ 160210187SKrishna.Elango@Sun.COM static int 160310187SKrishna.Elango@Sun.COM pcieb_pwr_disable(dev_info_t *dip) 160410187SKrishna.Elango@Sun.COM { 160510187SKrishna.Elango@Sun.COM pcie_pwr_t *pwr_p; 160610187SKrishna.Elango@Sun.COM 160710187SKrishna.Elango@Sun.COM ASSERT(PCIE_PMINFO(dip)); 160810187SKrishna.Elango@Sun.COM pwr_p = PCIE_NEXUS_PMINFO(dip); 160910187SKrishna.Elango@Sun.COM ASSERT(pwr_p); 161010187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "pcieb_pwr_disable: disabling PM\n"); 161110187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D0; 161210187SKrishna.Elango@Sun.COM pwr_p->pwr_flags = PCIE_NO_CHILD_PM; 161310187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 161410187SKrishna.Elango@Sun.COM } 161510187SKrishna.Elango@Sun.COM 161610187SKrishna.Elango@Sun.COM #ifdef DEBUG 161710187SKrishna.Elango@Sun.COM int pcieb_dbg_intr_print = 0; 161810187SKrishna.Elango@Sun.COM void 161910187SKrishna.Elango@Sun.COM pcieb_dbg(uint_t bit, dev_info_t *dip, char *fmt, ...) 162010187SKrishna.Elango@Sun.COM { 162110187SKrishna.Elango@Sun.COM va_list ap; 162210187SKrishna.Elango@Sun.COM 162310187SKrishna.Elango@Sun.COM if (!pcieb_dbg_print) 162410187SKrishna.Elango@Sun.COM return; 162510187SKrishna.Elango@Sun.COM 162610187SKrishna.Elango@Sun.COM if (dip) 162710187SKrishna.Elango@Sun.COM prom_printf("%s(%d): %s", ddi_driver_name(dip), 162810187SKrishna.Elango@Sun.COM ddi_get_instance(dip), pcieb_debug_sym[bit]); 162910187SKrishna.Elango@Sun.COM 163010187SKrishna.Elango@Sun.COM va_start(ap, fmt); 163110187SKrishna.Elango@Sun.COM if (servicing_interrupt()) { 163210187SKrishna.Elango@Sun.COM if (pcieb_dbg_intr_print) 163310187SKrishna.Elango@Sun.COM prom_vprintf(fmt, ap); 163410187SKrishna.Elango@Sun.COM } else { 163510187SKrishna.Elango@Sun.COM prom_vprintf(fmt, ap); 163610187SKrishna.Elango@Sun.COM } 163710187SKrishna.Elango@Sun.COM 163810187SKrishna.Elango@Sun.COM va_end(ap); 163910187SKrishna.Elango@Sun.COM } 164010187SKrishna.Elango@Sun.COM #endif 164110187SKrishna.Elango@Sun.COM 164210187SKrishna.Elango@Sun.COM static void 164310187SKrishna.Elango@Sun.COM pcieb_id_props(pcieb_devstate_t *pcieb) 164410187SKrishna.Elango@Sun.COM { 164510187SKrishna.Elango@Sun.COM uint64_t serialid = 0; /* 40b field of EUI-64 serial no. register */ 164610187SKrishna.Elango@Sun.COM uint16_t cap_ptr; 164710187SKrishna.Elango@Sun.COM uint8_t fic = 0; /* 1 = first in chassis device */ 164810187SKrishna.Elango@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2BUS(pcieb->pcieb_dip); 164910187SKrishna.Elango@Sun.COM ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl; 165010187SKrishna.Elango@Sun.COM 165110187SKrishna.Elango@Sun.COM /* 165210187SKrishna.Elango@Sun.COM * Identify first in chassis. In the special case of a Sun branded 165310187SKrishna.Elango@Sun.COM * PLX device, it obviously is first in chassis. Otherwise, in the 165410187SKrishna.Elango@Sun.COM * general case, look for an Expansion Slot Register and check its 165510187SKrishna.Elango@Sun.COM * first-in-chassis bit. 165610187SKrishna.Elango@Sun.COM */ 165710187SKrishna.Elango@Sun.COM #ifdef PX_PLX 165810187SKrishna.Elango@Sun.COM uint16_t vendor_id = bus_p->bus_dev_ven_id & 0xFFFF; 165910187SKrishna.Elango@Sun.COM uint16_t device_id = bus_p->bus_dev_ven_id >> 16; 166010187SKrishna.Elango@Sun.COM if ((vendor_id == PXB_VENDOR_SUN) && 166110187SKrishna.Elango@Sun.COM ((device_id == PXB_DEVICE_PLX_PCIX) || 166210187SKrishna.Elango@Sun.COM (device_id == PXB_DEVICE_PLX_PCIE))) { 166310187SKrishna.Elango@Sun.COM fic = 1; 166410187SKrishna.Elango@Sun.COM } 166510187SKrishna.Elango@Sun.COM #endif /* PX_PLX */ 166610187SKrishna.Elango@Sun.COM if ((fic == 0) && ((PCI_CAP_LOCATE(config_handle, 166710187SKrishna.Elango@Sun.COM PCI_CAP_ID_SLOT_ID, &cap_ptr)) != DDI_FAILURE)) { 166810187SKrishna.Elango@Sun.COM uint8_t esr = PCI_CAP_GET8(config_handle, NULL, 166910187SKrishna.Elango@Sun.COM cap_ptr, PCI_CAP_ID_REGS_OFF); 167010187SKrishna.Elango@Sun.COM if (PCI_CAPSLOT_FIC(esr)) 167110187SKrishna.Elango@Sun.COM fic = 1; 167210187SKrishna.Elango@Sun.COM } 167310187SKrishna.Elango@Sun.COM 167410187SKrishna.Elango@Sun.COM if ((PCI_CAP_LOCATE(config_handle, 167510187SKrishna.Elango@Sun.COM PCI_CAP_XCFG_SPC(PCIE_EXT_CAP_ID_SER), &cap_ptr)) != DDI_FAILURE) { 167610187SKrishna.Elango@Sun.COM /* Serialid can be 0 thru a full 40b number */ 167710187SKrishna.Elango@Sun.COM serialid = PCI_XCAP_GET32(config_handle, NULL, 167810187SKrishna.Elango@Sun.COM cap_ptr, PCIE_SER_SID_UPPER_DW); 167910187SKrishna.Elango@Sun.COM serialid <<= 32; 168010187SKrishna.Elango@Sun.COM serialid |= PCI_XCAP_GET32(config_handle, NULL, 168110187SKrishna.Elango@Sun.COM cap_ptr, PCIE_SER_SID_LOWER_DW); 168210187SKrishna.Elango@Sun.COM } 168310187SKrishna.Elango@Sun.COM 168410187SKrishna.Elango@Sun.COM if (fic) 168510187SKrishna.Elango@Sun.COM (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, pcieb->pcieb_dip, 168610187SKrishna.Elango@Sun.COM "first-in-chassis"); 168710187SKrishna.Elango@Sun.COM if (serialid) 168810187SKrishna.Elango@Sun.COM (void) ddi_prop_update_int64(DDI_DEV_T_NONE, pcieb->pcieb_dip, 168910187SKrishna.Elango@Sun.COM "serialid#", serialid); 169010187SKrishna.Elango@Sun.COM } 169110187SKrishna.Elango@Sun.COM 169210187SKrishna.Elango@Sun.COM static void 169310187SKrishna.Elango@Sun.COM pcieb_create_ranges_prop(dev_info_t *dip, 169410187SKrishna.Elango@Sun.COM ddi_acc_handle_t config_handle) 169510187SKrishna.Elango@Sun.COM { 169610187SKrishna.Elango@Sun.COM uint32_t base, limit; 169710923SEvan.Yan@Sun.COM ppb_ranges_t ranges[PCIEB_RANGE_LEN]; 169810187SKrishna.Elango@Sun.COM uint8_t io_base_lo, io_limit_lo; 169910187SKrishna.Elango@Sun.COM uint16_t io_base_hi, io_limit_hi, mem_base, mem_limit; 170010923SEvan.Yan@Sun.COM int i = 0, rangelen = sizeof (ppb_ranges_t)/sizeof (int); 170110187SKrishna.Elango@Sun.COM 170210187SKrishna.Elango@Sun.COM io_base_lo = pci_config_get8(config_handle, PCI_BCNF_IO_BASE_LOW); 170310187SKrishna.Elango@Sun.COM io_limit_lo = pci_config_get8(config_handle, PCI_BCNF_IO_LIMIT_LOW); 170410187SKrishna.Elango@Sun.COM io_base_hi = pci_config_get16(config_handle, PCI_BCNF_IO_BASE_HI); 170510187SKrishna.Elango@Sun.COM io_limit_hi = pci_config_get16(config_handle, PCI_BCNF_IO_LIMIT_HI); 170610187SKrishna.Elango@Sun.COM mem_base = pci_config_get16(config_handle, PCI_BCNF_MEM_BASE); 170710187SKrishna.Elango@Sun.COM mem_limit = pci_config_get16(config_handle, PCI_BCNF_MEM_LIMIT); 170810187SKrishna.Elango@Sun.COM 170910187SKrishna.Elango@Sun.COM /* 171010187SKrishna.Elango@Sun.COM * Create ranges for IO space 171110187SKrishna.Elango@Sun.COM */ 171210187SKrishna.Elango@Sun.COM ranges[i].size_low = ranges[i].size_high = 0; 171310187SKrishna.Elango@Sun.COM ranges[i].parent_mid = ranges[i].child_mid = ranges[i].parent_high = 0; 171410187SKrishna.Elango@Sun.COM ranges[i].child_high = ranges[i].parent_high |= 171510187SKrishna.Elango@Sun.COM (PCI_REG_REL_M | PCI_ADDR_IO); 171610187SKrishna.Elango@Sun.COM base = PCIEB_16bit_IOADDR(io_base_lo); 171710187SKrishna.Elango@Sun.COM limit = PCIEB_16bit_IOADDR(io_limit_lo); 171810187SKrishna.Elango@Sun.COM 171910187SKrishna.Elango@Sun.COM if ((io_base_lo & 0xf) == PCIEB_32BIT_IO) { 172010187SKrishna.Elango@Sun.COM base = PCIEB_LADDR(base, io_base_hi); 172110187SKrishna.Elango@Sun.COM } 172210187SKrishna.Elango@Sun.COM if ((io_limit_lo & 0xf) == PCIEB_32BIT_IO) { 172310187SKrishna.Elango@Sun.COM limit = PCIEB_LADDR(limit, io_limit_hi); 172410187SKrishna.Elango@Sun.COM } 172510187SKrishna.Elango@Sun.COM 172610187SKrishna.Elango@Sun.COM if ((io_base_lo & PCIEB_32BIT_IO) && (io_limit_hi > 0)) { 172710187SKrishna.Elango@Sun.COM base = PCIEB_LADDR(base, io_base_hi); 172810187SKrishna.Elango@Sun.COM limit = PCIEB_LADDR(limit, io_limit_hi); 172910187SKrishna.Elango@Sun.COM } 173010187SKrishna.Elango@Sun.COM 173110187SKrishna.Elango@Sun.COM /* 173210187SKrishna.Elango@Sun.COM * Create ranges for 32bit memory space 173310187SKrishna.Elango@Sun.COM */ 173410187SKrishna.Elango@Sun.COM base = PCIEB_32bit_MEMADDR(mem_base); 173510187SKrishna.Elango@Sun.COM limit = PCIEB_32bit_MEMADDR(mem_limit); 173610187SKrishna.Elango@Sun.COM ranges[i].size_low = ranges[i].size_high = 0; 173710187SKrishna.Elango@Sun.COM ranges[i].parent_mid = ranges[i].child_mid = ranges[i].parent_high = 0; 173810187SKrishna.Elango@Sun.COM ranges[i].child_high = ranges[i].parent_high |= 173910187SKrishna.Elango@Sun.COM (PCI_REG_REL_M | PCI_ADDR_MEM32); 174010187SKrishna.Elango@Sun.COM ranges[i].child_low = ranges[i].parent_low = base; 174110187SKrishna.Elango@Sun.COM if (limit >= base) { 174210187SKrishna.Elango@Sun.COM ranges[i].size_low = limit - base + PCIEB_MEMGRAIN; 174310187SKrishna.Elango@Sun.COM i++; 174410187SKrishna.Elango@Sun.COM } 174510187SKrishna.Elango@Sun.COM 174610187SKrishna.Elango@Sun.COM if (i) { 174710187SKrishna.Elango@Sun.COM (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "ranges", 174810187SKrishna.Elango@Sun.COM (int *)ranges, i * rangelen); 174910187SKrishna.Elango@Sun.COM } 175010187SKrishna.Elango@Sun.COM } 175110187SKrishna.Elango@Sun.COM 175210187SKrishna.Elango@Sun.COM /* 175310187SKrishna.Elango@Sun.COM * For PCI and PCI-X devices including PCIe2PCI bridge, initialize 175410187SKrishna.Elango@Sun.COM * cache-line-size and latency timer configuration registers. 175510187SKrishna.Elango@Sun.COM */ 175610187SKrishna.Elango@Sun.COM void 175710187SKrishna.Elango@Sun.COM pcieb_set_pci_perf_parameters(dev_info_t *dip, ddi_acc_handle_t cfg_hdl) 175810187SKrishna.Elango@Sun.COM { 175910187SKrishna.Elango@Sun.COM uint_t n; 176010187SKrishna.Elango@Sun.COM 176110187SKrishna.Elango@Sun.COM /* Initialize cache-line-size configuration register if needed */ 176210187SKrishna.Elango@Sun.COM if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 176310187SKrishna.Elango@Sun.COM "cache-line-size", 0) == 0) { 176410187SKrishna.Elango@Sun.COM pci_config_put8(cfg_hdl, PCI_CONF_CACHE_LINESZ, 176510187SKrishna.Elango@Sun.COM PCIEB_CACHE_LINE_SIZE); 176610187SKrishna.Elango@Sun.COM n = pci_config_get8(cfg_hdl, PCI_CONF_CACHE_LINESZ); 176710187SKrishna.Elango@Sun.COM if (n != 0) { 176810187SKrishna.Elango@Sun.COM (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, 176910187SKrishna.Elango@Sun.COM "cache-line-size", n); 177010187SKrishna.Elango@Sun.COM } 177110187SKrishna.Elango@Sun.COM } 177210187SKrishna.Elango@Sun.COM 177310187SKrishna.Elango@Sun.COM /* Initialize latency timer configuration registers if needed */ 177410187SKrishna.Elango@Sun.COM if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 177510187SKrishna.Elango@Sun.COM "latency-timer", 0) == 0) { 177610187SKrishna.Elango@Sun.COM uchar_t min_gnt, latency_timer; 177710187SKrishna.Elango@Sun.COM uchar_t header_type; 177810187SKrishna.Elango@Sun.COM 177910187SKrishna.Elango@Sun.COM /* Determine the configuration header type */ 178010187SKrishna.Elango@Sun.COM header_type = pci_config_get8(cfg_hdl, PCI_CONF_HEADER); 178110187SKrishna.Elango@Sun.COM 178210187SKrishna.Elango@Sun.COM if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) { 178310187SKrishna.Elango@Sun.COM latency_timer = PCIEB_LATENCY_TIMER; 178410187SKrishna.Elango@Sun.COM pci_config_put8(cfg_hdl, PCI_BCNF_LATENCY_TIMER, 178510187SKrishna.Elango@Sun.COM latency_timer); 178610187SKrishna.Elango@Sun.COM } else { 178710187SKrishna.Elango@Sun.COM min_gnt = pci_config_get8(cfg_hdl, PCI_CONF_MIN_G); 178810187SKrishna.Elango@Sun.COM latency_timer = min_gnt * 8; 178910187SKrishna.Elango@Sun.COM } 179010187SKrishna.Elango@Sun.COM 179110187SKrishna.Elango@Sun.COM pci_config_put8(cfg_hdl, PCI_CONF_LATENCY_TIMER, 179210187SKrishna.Elango@Sun.COM latency_timer); 179310187SKrishna.Elango@Sun.COM n = pci_config_get8(cfg_hdl, PCI_CONF_LATENCY_TIMER); 179410187SKrishna.Elango@Sun.COM if (n != 0) { 179510187SKrishna.Elango@Sun.COM (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, 179610187SKrishna.Elango@Sun.COM "latency-timer", n); 179710187SKrishna.Elango@Sun.COM } 179810187SKrishna.Elango@Sun.COM } 179910187SKrishna.Elango@Sun.COM } 1800