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 /* 2210187SKrishna.Elango@Sun.COM * Copyright 2009 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 */ 6310187SKrishna.Elango@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 38910187SKrishna.Elango@Sun.COM /* Do any platform specific workarounds needed at this time */ 39010187SKrishna.Elango@Sun.COM pcieb_plat_attach_workaround(devi); 39110187SKrishna.Elango@Sun.COM 39210187SKrishna.Elango@Sun.COM /* 39310187SKrishna.Elango@Sun.COM * If this is a root port, determine and set the max payload size. 39410187SKrishna.Elango@Sun.COM * Since this will involve scanning the fabric, all error enabling 39510187SKrishna.Elango@Sun.COM * and sw workarounds should be in place before doing this. 39610187SKrishna.Elango@Sun.COM */ 39710187SKrishna.Elango@Sun.COM if (PCIE_IS_RP(bus_p)) 39810187SKrishna.Elango@Sun.COM pcie_init_root_port_mps(devi); 39910187SKrishna.Elango@Sun.COM 40010187SKrishna.Elango@Sun.COM ddi_report_dev(devi); 40110187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 40210187SKrishna.Elango@Sun.COM 40310187SKrishna.Elango@Sun.COM fail: 40410187SKrishna.Elango@Sun.COM (void) pcieb_detach(devi, DDI_DETACH); 40510187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 40610187SKrishna.Elango@Sun.COM } 40710187SKrishna.Elango@Sun.COM 40810187SKrishna.Elango@Sun.COM static int 40910187SKrishna.Elango@Sun.COM pcieb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 41010187SKrishna.Elango@Sun.COM { 41110187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb; 41210187SKrishna.Elango@Sun.COM int error = DDI_SUCCESS; 41310187SKrishna.Elango@Sun.COM 41410187SKrishna.Elango@Sun.COM switch (cmd) { 41510187SKrishna.Elango@Sun.COM case DDI_SUSPEND: 41610187SKrishna.Elango@Sun.COM error = pcie_pwr_suspend(devi); 41710187SKrishna.Elango@Sun.COM return (error); 41810187SKrishna.Elango@Sun.COM 41910187SKrishna.Elango@Sun.COM case DDI_DETACH: 42010187SKrishna.Elango@Sun.COM break; 42110187SKrishna.Elango@Sun.COM 42210187SKrishna.Elango@Sun.COM default: 42310187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 42410187SKrishna.Elango@Sun.COM } 42510187SKrishna.Elango@Sun.COM 42610187SKrishna.Elango@Sun.COM pcieb = ddi_get_soft_state(pcieb_state, ddi_get_instance(devi)); 42710187SKrishna.Elango@Sun.COM 42810187SKrishna.Elango@Sun.COM /* remove interrupt handlers */ 42910187SKrishna.Elango@Sun.COM pcieb_intr_fini(pcieb); 43010187SKrishna.Elango@Sun.COM 43110923SEvan.Yan@Sun.COM /* uninitialize inband PCI-E HPC if present */ 43210923SEvan.Yan@Sun.COM (void) pcie_uninit(devi); 43310187SKrishna.Elango@Sun.COM 43410187SKrishna.Elango@Sun.COM (void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type"); 43510187SKrishna.Elango@Sun.COM 43610187SKrishna.Elango@Sun.COM (void) ndi_prop_remove(DDI_DEV_T_NONE, pcieb->pcieb_dip, 43710187SKrishna.Elango@Sun.COM "pcie_ce_mask"); 43810187SKrishna.Elango@Sun.COM 43910187SKrishna.Elango@Sun.COM if (pcieb->pcieb_init_flags & PCIEB_INIT_FM) 44010187SKrishna.Elango@Sun.COM pcieb_fm_fini(pcieb); 44110187SKrishna.Elango@Sun.COM 44210187SKrishna.Elango@Sun.COM pcieb_pwr_teardown(devi); 44310187SKrishna.Elango@Sun.COM pwr_common_teardown(devi); 44410187SKrishna.Elango@Sun.COM 44510187SKrishna.Elango@Sun.COM mutex_destroy(&pcieb->pcieb_peek_poke_mutex); 44610187SKrishna.Elango@Sun.COM mutex_destroy(&pcieb->pcieb_err_mutex); 44710187SKrishna.Elango@Sun.COM mutex_destroy(&pcieb->pcieb_mutex); 44810187SKrishna.Elango@Sun.COM 44910187SKrishna.Elango@Sun.COM /* 45010187SKrishna.Elango@Sun.COM * And finally free the per-pci soft state. 45110187SKrishna.Elango@Sun.COM */ 45210187SKrishna.Elango@Sun.COM ddi_soft_state_free(pcieb_state, ddi_get_instance(devi)); 45310187SKrishna.Elango@Sun.COM 45410187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 45510187SKrishna.Elango@Sun.COM } 45610187SKrishna.Elango@Sun.COM 45710187SKrishna.Elango@Sun.COM static int 45810187SKrishna.Elango@Sun.COM pcieb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 45910187SKrishna.Elango@Sun.COM off_t offset, off_t len, caddr_t *vaddrp) 46010187SKrishna.Elango@Sun.COM { 46110187SKrishna.Elango@Sun.COM dev_info_t *pdip; 46210187SKrishna.Elango@Sun.COM 46311236SStephen.Hanson@Sun.COM if (PCIE_IS_RP(PCIE_DIP2BUS(dip))) { 46411236SStephen.Hanson@Sun.COM ddi_acc_impl_t *hdlp = 46511236SStephen.Hanson@Sun.COM (ddi_acc_impl_t *)(mp->map_handlep)->ah_platform_private; 46611236SStephen.Hanson@Sun.COM 46711236SStephen.Hanson@Sun.COM pcieb_set_prot_scan(dip, hdlp); 46811236SStephen.Hanson@Sun.COM } 46910187SKrishna.Elango@Sun.COM pdip = (dev_info_t *)DEVI(dip)->devi_parent; 47010187SKrishna.Elango@Sun.COM return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip, rdip, mp, 47110187SKrishna.Elango@Sun.COM offset, len, vaddrp)); 47210187SKrishna.Elango@Sun.COM } 47310187SKrishna.Elango@Sun.COM 47410187SKrishna.Elango@Sun.COM static int 47510187SKrishna.Elango@Sun.COM pcieb_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, 47610187SKrishna.Elango@Sun.COM void *arg, void *result) 47710187SKrishna.Elango@Sun.COM { 47810187SKrishna.Elango@Sun.COM pci_regspec_t *drv_regp; 47910187SKrishna.Elango@Sun.COM int reglen; 48010187SKrishna.Elango@Sun.COM int rn; 48110187SKrishna.Elango@Sun.COM int totreg; 48210187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, 48310187SKrishna.Elango@Sun.COM ddi_get_instance(dip)); 48410187SKrishna.Elango@Sun.COM struct detachspec *ds; 48510187SKrishna.Elango@Sun.COM struct attachspec *as; 48610187SKrishna.Elango@Sun.COM 48710187SKrishna.Elango@Sun.COM switch (ctlop) { 48810187SKrishna.Elango@Sun.COM case DDI_CTLOPS_REPORTDEV: 48910187SKrishna.Elango@Sun.COM if (rdip == (dev_info_t *)0) 49010187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 49110187SKrishna.Elango@Sun.COM cmn_err(CE_CONT, "?PCIE-device: %s@%s, %s%d\n", 49210187SKrishna.Elango@Sun.COM ddi_node_name(rdip), ddi_get_name_addr(rdip), 49310187SKrishna.Elango@Sun.COM ddi_driver_name(rdip), 49410187SKrishna.Elango@Sun.COM ddi_get_instance(rdip)); 49510187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 49610187SKrishna.Elango@Sun.COM 49710187SKrishna.Elango@Sun.COM case DDI_CTLOPS_INITCHILD: 49810187SKrishna.Elango@Sun.COM return (pcieb_initchild((dev_info_t *)arg)); 49910187SKrishna.Elango@Sun.COM 50010187SKrishna.Elango@Sun.COM case DDI_CTLOPS_UNINITCHILD: 50110187SKrishna.Elango@Sun.COM pcieb_uninitchild((dev_info_t *)arg); 50210187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 50310187SKrishna.Elango@Sun.COM 50410187SKrishna.Elango@Sun.COM case DDI_CTLOPS_SIDDEV: 50510187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 50610187SKrishna.Elango@Sun.COM 50710187SKrishna.Elango@Sun.COM case DDI_CTLOPS_REGSIZE: 50810187SKrishna.Elango@Sun.COM case DDI_CTLOPS_NREGS: 50910187SKrishna.Elango@Sun.COM if (rdip == (dev_info_t *)0) 51010187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 51110187SKrishna.Elango@Sun.COM break; 51210187SKrishna.Elango@Sun.COM 51310187SKrishna.Elango@Sun.COM case DDI_CTLOPS_PEEK: 51410187SKrishna.Elango@Sun.COM case DDI_CTLOPS_POKE: 51510187SKrishna.Elango@Sun.COM return (pcieb_plat_peekpoke(dip, rdip, ctlop, arg, result)); 51610187SKrishna.Elango@Sun.COM case DDI_CTLOPS_ATTACH: 51710187SKrishna.Elango@Sun.COM if (!pcie_is_child(dip, rdip)) 51810187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 51910187SKrishna.Elango@Sun.COM 52010187SKrishna.Elango@Sun.COM as = (struct attachspec *)arg; 52110187SKrishna.Elango@Sun.COM switch (as->when) { 52210187SKrishna.Elango@Sun.COM case DDI_PRE: 52310187SKrishna.Elango@Sun.COM if (as->cmd == DDI_RESUME) { 52410187SKrishna.Elango@Sun.COM pcie_clear_errors(rdip); 52510187SKrishna.Elango@Sun.COM if (pcieb_plat_ctlops(rdip, ctlop, arg) != 52610187SKrishna.Elango@Sun.COM DDI_SUCCESS) 52710187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 52810187SKrishna.Elango@Sun.COM } 52910187SKrishna.Elango@Sun.COM 53010187SKrishna.Elango@Sun.COM if (as->cmd == DDI_ATTACH) 53110187SKrishna.Elango@Sun.COM return (pcie_pm_hold(dip)); 53210187SKrishna.Elango@Sun.COM 53310187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 53410187SKrishna.Elango@Sun.COM 53510187SKrishna.Elango@Sun.COM case DDI_POST: 53610768SRamesh.Chitrothu@Sun.COM if (as->cmd == DDI_ATTACH && 53710768SRamesh.Chitrothu@Sun.COM as->result != DDI_SUCCESS) { 53810768SRamesh.Chitrothu@Sun.COM /* 53910768SRamesh.Chitrothu@Sun.COM * Attach failed for the child device. The child 54010768SRamesh.Chitrothu@Sun.COM * driver may have made PM calls before the 54110768SRamesh.Chitrothu@Sun.COM * attach failed. pcie_pm_remove_child() should 54210768SRamesh.Chitrothu@Sun.COM * cleanup PM state and holds (if any) 54310768SRamesh.Chitrothu@Sun.COM * associated with the child device. 54410768SRamesh.Chitrothu@Sun.COM */ 54510768SRamesh.Chitrothu@Sun.COM return (pcie_pm_remove_child(dip, rdip)); 54610768SRamesh.Chitrothu@Sun.COM } 54710187SKrishna.Elango@Sun.COM 54810187SKrishna.Elango@Sun.COM if (as->result == DDI_SUCCESS) { 54910187SKrishna.Elango@Sun.COM pf_init(rdip, (void *)pcieb->pcieb_fm_ibc, 55010187SKrishna.Elango@Sun.COM as->cmd); 55110187SKrishna.Elango@Sun.COM 55210187SKrishna.Elango@Sun.COM (void) pcieb_plat_ctlops(rdip, ctlop, arg); 55310187SKrishna.Elango@Sun.COM } 55410187SKrishna.Elango@Sun.COM 55510187SKrishna.Elango@Sun.COM /* 55610187SKrishna.Elango@Sun.COM * For empty hotplug-capable slots, we should explicitly 55710187SKrishna.Elango@Sun.COM * disable the errors, so that we won't panic upon 55810187SKrishna.Elango@Sun.COM * unsupported hotplug messages. 55910187SKrishna.Elango@Sun.COM */ 56010187SKrishna.Elango@Sun.COM if ((!ddi_prop_exists(DDI_DEV_T_ANY, rdip, 56110187SKrishna.Elango@Sun.COM DDI_PROP_DONTPASS, "hotplug-capable")) || 56210187SKrishna.Elango@Sun.COM ddi_get_child(rdip)) { 56310187SKrishna.Elango@Sun.COM (void) pcie_postattach_child(rdip); 56410187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 56510187SKrishna.Elango@Sun.COM } 56610187SKrishna.Elango@Sun.COM 56710187SKrishna.Elango@Sun.COM pcie_disable_errors(rdip); 56810187SKrishna.Elango@Sun.COM 56910187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 57010187SKrishna.Elango@Sun.COM default: 57110187SKrishna.Elango@Sun.COM break; 57210187SKrishna.Elango@Sun.COM } 57310187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 57410187SKrishna.Elango@Sun.COM 57510187SKrishna.Elango@Sun.COM case DDI_CTLOPS_DETACH: 57610187SKrishna.Elango@Sun.COM if (!pcie_is_child(dip, rdip)) 57710187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 57810187SKrishna.Elango@Sun.COM 57910187SKrishna.Elango@Sun.COM ds = (struct detachspec *)arg; 58010187SKrishna.Elango@Sun.COM switch (ds->when) { 58110187SKrishna.Elango@Sun.COM case DDI_PRE: 58210187SKrishna.Elango@Sun.COM pf_fini(rdip, ds->cmd); 58310187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 58410187SKrishna.Elango@Sun.COM 58510187SKrishna.Elango@Sun.COM case DDI_POST: 58610187SKrishna.Elango@Sun.COM if (pcieb_plat_ctlops(rdip, ctlop, arg) != DDI_SUCCESS) 58710187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 58810187SKrishna.Elango@Sun.COM if (ds->cmd == DDI_DETACH && 58910187SKrishna.Elango@Sun.COM ds->result == DDI_SUCCESS) { 59010187SKrishna.Elango@Sun.COM return (pcie_pm_remove_child(dip, rdip)); 59110187SKrishna.Elango@Sun.COM } 59210187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 59310187SKrishna.Elango@Sun.COM default: 59410187SKrishna.Elango@Sun.COM break; 59510187SKrishna.Elango@Sun.COM } 59610187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 59710187SKrishna.Elango@Sun.COM default: 59810187SKrishna.Elango@Sun.COM return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 59910187SKrishna.Elango@Sun.COM } 60010187SKrishna.Elango@Sun.COM 60110187SKrishna.Elango@Sun.COM *(int *)result = 0; 60210187SKrishna.Elango@Sun.COM if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, 60310187SKrishna.Elango@Sun.COM DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg", (caddr_t)&drv_regp, 60410187SKrishna.Elango@Sun.COM ®len) != DDI_SUCCESS) 60510187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 60610187SKrishna.Elango@Sun.COM 60710187SKrishna.Elango@Sun.COM totreg = reglen / sizeof (pci_regspec_t); 60810187SKrishna.Elango@Sun.COM if (ctlop == DDI_CTLOPS_NREGS) 60910187SKrishna.Elango@Sun.COM *(int *)result = totreg; 61010187SKrishna.Elango@Sun.COM else if (ctlop == DDI_CTLOPS_REGSIZE) { 61110187SKrishna.Elango@Sun.COM rn = *(int *)arg; 61210187SKrishna.Elango@Sun.COM if (rn >= totreg) { 61310187SKrishna.Elango@Sun.COM kmem_free(drv_regp, reglen); 61410187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 61510187SKrishna.Elango@Sun.COM } 61610187SKrishna.Elango@Sun.COM 61710187SKrishna.Elango@Sun.COM *(off_t *)result = drv_regp[rn].pci_size_low | 61810187SKrishna.Elango@Sun.COM ((uint64_t)drv_regp[rn].pci_size_hi << 32); 61910187SKrishna.Elango@Sun.COM } 62010187SKrishna.Elango@Sun.COM 62110187SKrishna.Elango@Sun.COM kmem_free(drv_regp, reglen); 62210187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 62310187SKrishna.Elango@Sun.COM } 62410187SKrishna.Elango@Sun.COM 62510187SKrishna.Elango@Sun.COM /* 62610187SKrishna.Elango@Sun.COM * name_child 62710187SKrishna.Elango@Sun.COM * 62810187SKrishna.Elango@Sun.COM * This function is called from init_child to name a node. It is 62910187SKrishna.Elango@Sun.COM * also passed as a callback for node merging functions. 63010187SKrishna.Elango@Sun.COM * 63110187SKrishna.Elango@Sun.COM * return value: DDI_SUCCESS, DDI_FAILURE 63210187SKrishna.Elango@Sun.COM */ 63310187SKrishna.Elango@Sun.COM static int 63410187SKrishna.Elango@Sun.COM pcieb_name_child(dev_info_t *child, char *name, int namelen) 63510187SKrishna.Elango@Sun.COM { 63610187SKrishna.Elango@Sun.COM pci_regspec_t *pci_rp; 63710923SEvan.Yan@Sun.COM uint_t device, func; 63810187SKrishna.Elango@Sun.COM char **unit_addr; 63910187SKrishna.Elango@Sun.COM uint_t n; 64010187SKrishna.Elango@Sun.COM 64110187SKrishna.Elango@Sun.COM /* 64210187SKrishna.Elango@Sun.COM * For .conf nodes, use unit-address property as name 64310187SKrishna.Elango@Sun.COM */ 64410187SKrishna.Elango@Sun.COM if (ndi_dev_is_persistent_node(child) == 0) { 64510187SKrishna.Elango@Sun.COM if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 64610187SKrishna.Elango@Sun.COM DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != 64710187SKrishna.Elango@Sun.COM DDI_PROP_SUCCESS) { 64810187SKrishna.Elango@Sun.COM cmn_err(CE_WARN, 64910187SKrishna.Elango@Sun.COM "cannot find unit-address in %s.conf", 65010187SKrishna.Elango@Sun.COM ddi_driver_name(child)); 65110187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 65210187SKrishna.Elango@Sun.COM } 65310187SKrishna.Elango@Sun.COM if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 65410187SKrishna.Elango@Sun.COM cmn_err(CE_WARN, "unit-address property in %s.conf" 65510187SKrishna.Elango@Sun.COM " not well-formed", ddi_driver_name(child)); 65610187SKrishna.Elango@Sun.COM ddi_prop_free(unit_addr); 65710187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 65810187SKrishna.Elango@Sun.COM } 65910187SKrishna.Elango@Sun.COM (void) snprintf(name, namelen, "%s", *unit_addr); 66010187SKrishna.Elango@Sun.COM ddi_prop_free(unit_addr); 66110187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 66210187SKrishna.Elango@Sun.COM } 66310187SKrishna.Elango@Sun.COM 66410187SKrishna.Elango@Sun.COM /* 66510187SKrishna.Elango@Sun.COM * Get the address portion of the node name based on 66610187SKrishna.Elango@Sun.COM * the function and device number. 66710187SKrishna.Elango@Sun.COM */ 66810187SKrishna.Elango@Sun.COM if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 66910187SKrishna.Elango@Sun.COM DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) { 67010187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 67110187SKrishna.Elango@Sun.COM } 67210187SKrishna.Elango@Sun.COM 67310187SKrishna.Elango@Sun.COM /* copy the device identifications */ 67410923SEvan.Yan@Sun.COM device = PCI_REG_DEV_G(pci_rp[0].pci_phys_hi); 67510187SKrishna.Elango@Sun.COM func = PCI_REG_FUNC_G(pci_rp[0].pci_phys_hi); 67610187SKrishna.Elango@Sun.COM 67710923SEvan.Yan@Sun.COM if (pcie_ari_is_enabled(ddi_get_parent(child)) 67810923SEvan.Yan@Sun.COM == PCIE_ARI_FORW_ENABLED) { 67910923SEvan.Yan@Sun.COM func = (device << 3) | func; 68010923SEvan.Yan@Sun.COM device = 0; 68110923SEvan.Yan@Sun.COM } 68210923SEvan.Yan@Sun.COM 68310187SKrishna.Elango@Sun.COM if (func != 0) 68410923SEvan.Yan@Sun.COM (void) snprintf(name, namelen, "%x,%x", device, func); 68510187SKrishna.Elango@Sun.COM else 68610923SEvan.Yan@Sun.COM (void) snprintf(name, namelen, "%x", device); 68710187SKrishna.Elango@Sun.COM 68810187SKrishna.Elango@Sun.COM ddi_prop_free(pci_rp); 68910187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 69010187SKrishna.Elango@Sun.COM } 69110187SKrishna.Elango@Sun.COM 69210187SKrishna.Elango@Sun.COM static int 69310187SKrishna.Elango@Sun.COM pcieb_initchild(dev_info_t *child) 69410187SKrishna.Elango@Sun.COM { 69510187SKrishna.Elango@Sun.COM char name[MAXNAMELEN]; 69610187SKrishna.Elango@Sun.COM int result = DDI_FAILURE; 69710187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb = 69810187SKrishna.Elango@Sun.COM (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state, 69910187SKrishna.Elango@Sun.COM ddi_get_instance(ddi_get_parent(child))); 70010187SKrishna.Elango@Sun.COM 70110187SKrishna.Elango@Sun.COM /* 70210187SKrishna.Elango@Sun.COM * Name the child 70310187SKrishna.Elango@Sun.COM */ 70410187SKrishna.Elango@Sun.COM if (pcieb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS) { 70510187SKrishna.Elango@Sun.COM result = DDI_FAILURE; 70610187SKrishna.Elango@Sun.COM goto done; 70710187SKrishna.Elango@Sun.COM } 70810187SKrishna.Elango@Sun.COM ddi_set_name_addr(child, name); 70910187SKrishna.Elango@Sun.COM 71010187SKrishna.Elango@Sun.COM /* 71110187SKrishna.Elango@Sun.COM * Pseudo nodes indicate a prototype node with per-instance 71210187SKrishna.Elango@Sun.COM * properties to be merged into the real h/w device node. 71310187SKrishna.Elango@Sun.COM * The interpretation of the unit-address is DD[,F] 71410187SKrishna.Elango@Sun.COM * where DD is the device id and F is the function. 71510187SKrishna.Elango@Sun.COM */ 71610187SKrishna.Elango@Sun.COM if (ndi_dev_is_persistent_node(child) == 0) { 71710187SKrishna.Elango@Sun.COM extern int pci_allow_pseudo_children; 71810187SKrishna.Elango@Sun.COM 71910187SKrishna.Elango@Sun.COM /* 72010187SKrishna.Elango@Sun.COM * Try to merge the properties from this prototype 72110187SKrishna.Elango@Sun.COM * node into real h/w nodes. 72210187SKrishna.Elango@Sun.COM */ 72310187SKrishna.Elango@Sun.COM if (ndi_merge_node(child, pcieb_name_child) != DDI_SUCCESS) { 72410187SKrishna.Elango@Sun.COM /* 72510187SKrishna.Elango@Sun.COM * Merged ok - return failure to remove the node. 72610187SKrishna.Elango@Sun.COM */ 72710187SKrishna.Elango@Sun.COM ddi_set_name_addr(child, NULL); 72810187SKrishna.Elango@Sun.COM result = DDI_FAILURE; 72910187SKrishna.Elango@Sun.COM goto done; 73010187SKrishna.Elango@Sun.COM } 73110187SKrishna.Elango@Sun.COM 73210187SKrishna.Elango@Sun.COM /* workaround for ddivs to run under PCI-E */ 73310187SKrishna.Elango@Sun.COM if (pci_allow_pseudo_children) { 73410187SKrishna.Elango@Sun.COM result = DDI_SUCCESS; 73510187SKrishna.Elango@Sun.COM goto done; 73610187SKrishna.Elango@Sun.COM } 73710187SKrishna.Elango@Sun.COM 73810187SKrishna.Elango@Sun.COM /* 73910187SKrishna.Elango@Sun.COM * The child was not merged into a h/w node, 74010187SKrishna.Elango@Sun.COM * but there's not much we can do with it other 74110187SKrishna.Elango@Sun.COM * than return failure to cause the node to be removed. 74210187SKrishna.Elango@Sun.COM */ 74310187SKrishna.Elango@Sun.COM cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", 74410187SKrishna.Elango@Sun.COM ddi_driver_name(child), ddi_get_name_addr(child), 74510187SKrishna.Elango@Sun.COM ddi_driver_name(child)); 74610187SKrishna.Elango@Sun.COM ddi_set_name_addr(child, NULL); 74710187SKrishna.Elango@Sun.COM result = DDI_NOT_WELL_FORMED; 74810187SKrishna.Elango@Sun.COM goto done; 74910187SKrishna.Elango@Sun.COM } 75010187SKrishna.Elango@Sun.COM 75110187SKrishna.Elango@Sun.COM /* platform specific initchild */ 75210187SKrishna.Elango@Sun.COM pcieb_plat_initchild(child); 75310187SKrishna.Elango@Sun.COM 75410187SKrishna.Elango@Sun.COM if (pcie_pm_hold(pcieb->pcieb_dip) != DDI_SUCCESS) { 75510187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, pcieb->pcieb_dip, 75610187SKrishna.Elango@Sun.COM "INITCHILD: px_pm_hold failed\n"); 75710187SKrishna.Elango@Sun.COM result = DDI_FAILURE; 75810187SKrishna.Elango@Sun.COM goto done; 75910187SKrishna.Elango@Sun.COM } 76010187SKrishna.Elango@Sun.COM /* Any return from here must call pcie_pm_release */ 76110187SKrishna.Elango@Sun.COM 76210187SKrishna.Elango@Sun.COM /* 76310187SKrishna.Elango@Sun.COM * If configuration registers were previously saved by 76410187SKrishna.Elango@Sun.COM * child (before it entered D3), then let the child do the 76510187SKrishna.Elango@Sun.COM * restore to set up the config regs as it'll first need to 76610187SKrishna.Elango@Sun.COM * power the device out of D3. 76710187SKrishna.Elango@Sun.COM */ 76810187SKrishna.Elango@Sun.COM if (ddi_prop_exists(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 76910187SKrishna.Elango@Sun.COM "config-regs-saved-by-child") == 1) { 77010187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, ddi_get_parent(child), 77110187SKrishna.Elango@Sun.COM "INITCHILD: config regs to be restored by child" 77210187SKrishna.Elango@Sun.COM " for %s@%s\n", ddi_node_name(child), 77310187SKrishna.Elango@Sun.COM ddi_get_name_addr(child)); 77410187SKrishna.Elango@Sun.COM 77510187SKrishna.Elango@Sun.COM result = DDI_SUCCESS; 77610187SKrishna.Elango@Sun.COM goto cleanup; 77710187SKrishna.Elango@Sun.COM } 77810187SKrishna.Elango@Sun.COM 77910187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, ddi_get_parent(child), 78010187SKrishna.Elango@Sun.COM "INITCHILD: config regs setup for %s@%s\n", 78110187SKrishna.Elango@Sun.COM ddi_node_name(child), ddi_get_name_addr(child)); 78210187SKrishna.Elango@Sun.COM 783*11245SZhijun.Fu@Sun.COM if (pcie_initchild(child) != DDI_SUCCESS) { 78410187SKrishna.Elango@Sun.COM result = DDI_FAILURE; 78510187SKrishna.Elango@Sun.COM goto cleanup; 78610187SKrishna.Elango@Sun.COM } 78710187SKrishna.Elango@Sun.COM 78810187SKrishna.Elango@Sun.COM #ifdef PX_PLX 78910187SKrishna.Elango@Sun.COM if (pcieb_init_plx_workarounds(pcieb, child) == DDI_FAILURE) { 79010187SKrishna.Elango@Sun.COM result = DDI_FAILURE; 79110187SKrishna.Elango@Sun.COM goto cleanup; 79210187SKrishna.Elango@Sun.COM } 79310187SKrishna.Elango@Sun.COM #endif /* PX_PLX */ 79410187SKrishna.Elango@Sun.COM 79510187SKrishna.Elango@Sun.COM result = DDI_SUCCESS; 79610187SKrishna.Elango@Sun.COM cleanup: 79710187SKrishna.Elango@Sun.COM pcie_pm_release(pcieb->pcieb_dip); 79810187SKrishna.Elango@Sun.COM done: 79910187SKrishna.Elango@Sun.COM return (result); 80010187SKrishna.Elango@Sun.COM } 80110187SKrishna.Elango@Sun.COM 80210187SKrishna.Elango@Sun.COM static void 80310187SKrishna.Elango@Sun.COM pcieb_uninitchild(dev_info_t *dip) 80410187SKrishna.Elango@Sun.COM { 80510187SKrishna.Elango@Sun.COM 80610187SKrishna.Elango@Sun.COM pcie_uninitchild(dip); 80710187SKrishna.Elango@Sun.COM 80810187SKrishna.Elango@Sun.COM pcieb_plat_uninitchild(dip); 80910187SKrishna.Elango@Sun.COM 81010187SKrishna.Elango@Sun.COM ddi_set_name_addr(dip, NULL); 81110187SKrishna.Elango@Sun.COM 81210187SKrishna.Elango@Sun.COM /* 81310187SKrishna.Elango@Sun.COM * Strip the node to properly convert it back to prototype form 81410187SKrishna.Elango@Sun.COM */ 81510187SKrishna.Elango@Sun.COM ddi_remove_minor_node(dip, NULL); 81610187SKrishna.Elango@Sun.COM 81710187SKrishna.Elango@Sun.COM ddi_prop_remove_all(dip); 81810187SKrishna.Elango@Sun.COM } 81910187SKrishna.Elango@Sun.COM 82010187SKrishna.Elango@Sun.COM static boolean_t 82110187SKrishna.Elango@Sun.COM pcieb_is_pcie_device_type(dev_info_t *dip) 82210187SKrishna.Elango@Sun.COM { 82310187SKrishna.Elango@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 82410187SKrishna.Elango@Sun.COM 82510187SKrishna.Elango@Sun.COM if (PCIE_IS_SW(bus_p) || PCIE_IS_RP(bus_p) || PCIE_IS_PCI2PCIE(bus_p)) 82610187SKrishna.Elango@Sun.COM return (B_TRUE); 82710187SKrishna.Elango@Sun.COM 82810187SKrishna.Elango@Sun.COM return (B_FALSE); 82910187SKrishna.Elango@Sun.COM } 83010187SKrishna.Elango@Sun.COM 83110187SKrishna.Elango@Sun.COM static int 83210187SKrishna.Elango@Sun.COM pcieb_intr_attach(pcieb_devstate_t *pcieb) 83310187SKrishna.Elango@Sun.COM { 83410187SKrishna.Elango@Sun.COM int intr_types; 83510187SKrishna.Elango@Sun.COM dev_info_t *dip = pcieb->pcieb_dip; 83610187SKrishna.Elango@Sun.COM 83710187SKrishna.Elango@Sun.COM /* Allow platform specific code to do any initialization first */ 83810187SKrishna.Elango@Sun.COM pcieb_plat_intr_attach(pcieb); 83910187SKrishna.Elango@Sun.COM 84010187SKrishna.Elango@Sun.COM /* 84110187SKrishna.Elango@Sun.COM * Initialize interrupt handlers. 84210187SKrishna.Elango@Sun.COM * If both MSI and FIXED are supported, try to attach MSI first. 84310187SKrishna.Elango@Sun.COM * If MSI fails for any reason, then try FIXED, but only allow one 84410187SKrishna.Elango@Sun.COM * type to be attached. 84510187SKrishna.Elango@Sun.COM */ 84610187SKrishna.Elango@Sun.COM if (ddi_intr_get_supported_types(dip, &intr_types) != DDI_SUCCESS) { 84710187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_get_supported_types" 84810187SKrishna.Elango@Sun.COM " failed\n"); 84910187SKrishna.Elango@Sun.COM goto FAIL; 85010187SKrishna.Elango@Sun.COM } 85110187SKrishna.Elango@Sun.COM 85210187SKrishna.Elango@Sun.COM if ((intr_types & DDI_INTR_TYPE_MSI) && 85310187SKrishna.Elango@Sun.COM (pcieb_msi_supported(dip) == DDI_SUCCESS)) { 85410187SKrishna.Elango@Sun.COM if (pcieb_intr_init(pcieb, DDI_INTR_TYPE_MSI) == DDI_SUCCESS) 85510187SKrishna.Elango@Sun.COM intr_types = DDI_INTR_TYPE_MSI; 85610187SKrishna.Elango@Sun.COM else { 85710187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "Unable to attach MSI" 85810187SKrishna.Elango@Sun.COM " handler\n"); 85910187SKrishna.Elango@Sun.COM } 86010187SKrishna.Elango@Sun.COM } 86110187SKrishna.Elango@Sun.COM 86210187SKrishna.Elango@Sun.COM if (intr_types != DDI_INTR_TYPE_MSI) { 86310187SKrishna.Elango@Sun.COM /* 86410187SKrishna.Elango@Sun.COM * MSIs are not supported or MSI initialization failed. For Root 86510187SKrishna.Elango@Sun.COM * Ports mark this so error handling might try to fallback to 86610187SKrishna.Elango@Sun.COM * some other mechanism if available (machinecheck etc.). 86710187SKrishna.Elango@Sun.COM */ 86810187SKrishna.Elango@Sun.COM if (PCIE_IS_RP(PCIE_DIP2UPBUS(dip))) 86910187SKrishna.Elango@Sun.COM pcieb->pcieb_no_aer_msi = B_TRUE; 87010187SKrishna.Elango@Sun.COM } 87110187SKrishna.Elango@Sun.COM 87210187SKrishna.Elango@Sun.COM if (intr_types & DDI_INTR_TYPE_FIXED) { 87310187SKrishna.Elango@Sun.COM if (pcieb_intr_init(pcieb, DDI_INTR_TYPE_FIXED) != 87410187SKrishna.Elango@Sun.COM DDI_SUCCESS) { 87510187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, 87610187SKrishna.Elango@Sun.COM "Unable to attach INTx handler\n"); 87710187SKrishna.Elango@Sun.COM goto FAIL; 87810187SKrishna.Elango@Sun.COM } 87910187SKrishna.Elango@Sun.COM } 88010187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 88110187SKrishna.Elango@Sun.COM 88210187SKrishna.Elango@Sun.COM FAIL: 88310187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 88410187SKrishna.Elango@Sun.COM } 88510187SKrishna.Elango@Sun.COM 88610187SKrishna.Elango@Sun.COM /* 88710187SKrishna.Elango@Sun.COM * This function initializes internally generated interrupts only. 88810187SKrishna.Elango@Sun.COM * It does not affect any interrupts generated by downstream devices 88910187SKrishna.Elango@Sun.COM * or the forwarding of them. 89010187SKrishna.Elango@Sun.COM * 89110187SKrishna.Elango@Sun.COM * Enable Device Specific Interrupts or Hotplug features here. 89210187SKrishna.Elango@Sun.COM * Enabling features may change how many interrupts are requested 89310187SKrishna.Elango@Sun.COM * by the device. If features are not enabled first, the 89410187SKrishna.Elango@Sun.COM * device might not ask for any interrupts. 89510187SKrishna.Elango@Sun.COM */ 89610397SShesha.Sreenivasamurthy@Sun.COM 89710187SKrishna.Elango@Sun.COM static int 89810187SKrishna.Elango@Sun.COM pcieb_intr_init(pcieb_devstate_t *pcieb, int intr_type) 89910187SKrishna.Elango@Sun.COM { 90010187SKrishna.Elango@Sun.COM dev_info_t *dip = pcieb->pcieb_dip; 90110187SKrishna.Elango@Sun.COM int nintrs, request, count, x; 90210187SKrishna.Elango@Sun.COM int intr_cap = 0; 90310187SKrishna.Elango@Sun.COM int inum = 0; 90410187SKrishna.Elango@Sun.COM int ret, hp_msi_off; 90510187SKrishna.Elango@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip); 90610187SKrishna.Elango@Sun.COM uint16_t vendorid = bus_p->bus_dev_ven_id & 0xFFFF; 90710187SKrishna.Elango@Sun.COM boolean_t is_hp = B_FALSE; 90810187SKrishna.Elango@Sun.COM boolean_t is_pme = B_FALSE; 90910187SKrishna.Elango@Sun.COM 91010187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "pcieb_intr_init: Attaching %s handler\n", 91110187SKrishna.Elango@Sun.COM (intr_type == DDI_INTR_TYPE_MSI) ? "MSI" : "INTx"); 91210187SKrishna.Elango@Sun.COM 91310187SKrishna.Elango@Sun.COM request = 0; 91410923SEvan.Yan@Sun.COM if (PCIE_IS_HOTPLUG_ENABLED(dip)) { 91510187SKrishna.Elango@Sun.COM request++; 91610187SKrishna.Elango@Sun.COM is_hp = B_TRUE; 91710187SKrishna.Elango@Sun.COM } 91810187SKrishna.Elango@Sun.COM 91910187SKrishna.Elango@Sun.COM /* 92010187SKrishna.Elango@Sun.COM * Hotplug and PME share the same MSI vector. If hotplug is not 92110187SKrishna.Elango@Sun.COM * supported check if MSI is needed for PME. 92210187SKrishna.Elango@Sun.COM */ 92310187SKrishna.Elango@Sun.COM if ((intr_type == DDI_INTR_TYPE_MSI) && PCIE_IS_RP(bus_p) && 92410187SKrishna.Elango@Sun.COM (vendorid == NVIDIA_VENDOR_ID)) { 92510187SKrishna.Elango@Sun.COM is_pme = B_TRUE; 92610187SKrishna.Elango@Sun.COM if (!is_hp) 92710187SKrishna.Elango@Sun.COM request++; 92810187SKrishna.Elango@Sun.COM } 92910187SKrishna.Elango@Sun.COM 93010187SKrishna.Elango@Sun.COM /* 93110187SKrishna.Elango@Sun.COM * Setup MSI if this device is a Rootport and has AER. Currently no 93210187SKrishna.Elango@Sun.COM * SPARC Root Port supports fabric errors being reported through it. 93310187SKrishna.Elango@Sun.COM */ 93410187SKrishna.Elango@Sun.COM if (intr_type == DDI_INTR_TYPE_MSI) { 93510187SKrishna.Elango@Sun.COM if (PCIE_IS_RP(bus_p) && PCIE_HAS_AER(bus_p)) 93610187SKrishna.Elango@Sun.COM request++; 93710187SKrishna.Elango@Sun.COM } 93810187SKrishna.Elango@Sun.COM 93910187SKrishna.Elango@Sun.COM if (request == 0) 94010187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 94110187SKrishna.Elango@Sun.COM 94210187SKrishna.Elango@Sun.COM /* 94310187SKrishna.Elango@Sun.COM * Get number of supported interrupts. 94410187SKrishna.Elango@Sun.COM * 94510187SKrishna.Elango@Sun.COM * Several Bridges/Switches will not have this property set, resulting 94610187SKrishna.Elango@Sun.COM * in a FAILURE, if the device is not configured in a way that 94710187SKrishna.Elango@Sun.COM * interrupts are needed. (eg. hotplugging) 94810187SKrishna.Elango@Sun.COM */ 94910187SKrishna.Elango@Sun.COM ret = ddi_intr_get_nintrs(dip, intr_type, &nintrs); 95010187SKrishna.Elango@Sun.COM if ((ret != DDI_SUCCESS) || (nintrs == 0)) { 95110187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_get_nintrs ret:%d" 95210187SKrishna.Elango@Sun.COM " req:%d\n", ret, nintrs); 95310187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 95410187SKrishna.Elango@Sun.COM } 95510187SKrishna.Elango@Sun.COM 95610187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "bdf 0x%x: ddi_intr_get_nintrs: nintrs %d", 95710187SKrishna.Elango@Sun.COM " request %d\n", bus_p->bus_bdf, nintrs, request); 95810187SKrishna.Elango@Sun.COM 95910187SKrishna.Elango@Sun.COM if (request > nintrs) 96010187SKrishna.Elango@Sun.COM request = nintrs; 96110187SKrishna.Elango@Sun.COM 96210187SKrishna.Elango@Sun.COM /* Allocate an array of interrupt handlers */ 96310187SKrishna.Elango@Sun.COM pcieb->pcieb_htable_size = sizeof (ddi_intr_handle_t) * request; 96410187SKrishna.Elango@Sun.COM pcieb->pcieb_htable = kmem_zalloc(pcieb->pcieb_htable_size, 96510187SKrishna.Elango@Sun.COM KM_SLEEP); 96610187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_HTABLE; 96710187SKrishna.Elango@Sun.COM 96810187SKrishna.Elango@Sun.COM ret = ddi_intr_alloc(dip, pcieb->pcieb_htable, intr_type, inum, 96910187SKrishna.Elango@Sun.COM request, &count, DDI_INTR_ALLOC_NORMAL); 97010187SKrishna.Elango@Sun.COM if ((ret != DDI_SUCCESS) || (count == 0)) { 97110187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_alloc() ret: %d ask: %d" 97210187SKrishna.Elango@Sun.COM " actual: %d\n", ret, request, count); 97310187SKrishna.Elango@Sun.COM goto FAIL; 97410187SKrishna.Elango@Sun.COM } 97510187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_ALLOC; 97610187SKrishna.Elango@Sun.COM 97710187SKrishna.Elango@Sun.COM /* Save the actual number of interrupts allocated */ 97810187SKrishna.Elango@Sun.COM pcieb->pcieb_intr_count = count; 97910187SKrishna.Elango@Sun.COM if (count < request) { 98010187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "bdf 0%x: Requested Intr: %d" 98110187SKrishna.Elango@Sun.COM " Received: %d\n", bus_p->bus_bdf, request, count); 98210187SKrishna.Elango@Sun.COM } 98310187SKrishna.Elango@Sun.COM 98410187SKrishna.Elango@Sun.COM /* 98510187SKrishna.Elango@Sun.COM * NVidia (MCP55 and other) chipsets have a errata that if the number 98610187SKrishna.Elango@Sun.COM * of requested MSI intrs is not allocated we have to fall back to INTx. 98710187SKrishna.Elango@Sun.COM */ 98810187SKrishna.Elango@Sun.COM if (intr_type == DDI_INTR_TYPE_MSI) { 98910187SKrishna.Elango@Sun.COM if (PCIE_IS_RP(bus_p) && (vendorid == NVIDIA_VENDOR_ID)) { 99010187SKrishna.Elango@Sun.COM if (request != count) 99110187SKrishna.Elango@Sun.COM goto FAIL; 99210187SKrishna.Elango@Sun.COM } 99310187SKrishna.Elango@Sun.COM } 99410187SKrishna.Elango@Sun.COM 99510187SKrishna.Elango@Sun.COM /* Get interrupt priority */ 99610187SKrishna.Elango@Sun.COM ret = ddi_intr_get_pri(pcieb->pcieb_htable[0], 99710187SKrishna.Elango@Sun.COM &pcieb->pcieb_intr_priority); 99810187SKrishna.Elango@Sun.COM if (ret != DDI_SUCCESS) { 99910187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_get_pri() ret: %d\n", 100010187SKrishna.Elango@Sun.COM ret); 100110187SKrishna.Elango@Sun.COM goto FAIL; 100210187SKrishna.Elango@Sun.COM } 100310187SKrishna.Elango@Sun.COM 100410187SKrishna.Elango@Sun.COM if (pcieb->pcieb_intr_priority >= LOCK_LEVEL) { 100510187SKrishna.Elango@Sun.COM pcieb->pcieb_intr_priority = LOCK_LEVEL - 1; 100610187SKrishna.Elango@Sun.COM ret = ddi_intr_set_pri(pcieb->pcieb_htable[0], 100710187SKrishna.Elango@Sun.COM pcieb->pcieb_intr_priority); 100810187SKrishna.Elango@Sun.COM if (ret != DDI_SUCCESS) { 100910187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_set_pri() ret:" 101010187SKrishna.Elango@Sun.COM " %d\n", ret); 101110187SKrishna.Elango@Sun.COM 101210187SKrishna.Elango@Sun.COM goto FAIL; 101310187SKrishna.Elango@Sun.COM } 101410187SKrishna.Elango@Sun.COM } 101510187SKrishna.Elango@Sun.COM 101610187SKrishna.Elango@Sun.COM mutex_init(&pcieb->pcieb_intr_mutex, NULL, MUTEX_DRIVER, NULL); 101710187SKrishna.Elango@Sun.COM 101810187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_MUTEX; 101910187SKrishna.Elango@Sun.COM 102010187SKrishna.Elango@Sun.COM for (count = 0; count < pcieb->pcieb_intr_count; count++) { 102110187SKrishna.Elango@Sun.COM ret = ddi_intr_add_handler(pcieb->pcieb_htable[count], 102210187SKrishna.Elango@Sun.COM pcieb_intr_handler, (caddr_t)pcieb, 102310187SKrishna.Elango@Sun.COM (caddr_t)(uintptr_t)(inum + count)); 102410187SKrishna.Elango@Sun.COM 102510187SKrishna.Elango@Sun.COM if (ret != DDI_SUCCESS) { 102610187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "Cannot add " 102710187SKrishna.Elango@Sun.COM "interrupt(%d)\n", ret); 102810187SKrishna.Elango@Sun.COM break; 102910187SKrishna.Elango@Sun.COM } 103010187SKrishna.Elango@Sun.COM } 103110187SKrishna.Elango@Sun.COM 103210187SKrishna.Elango@Sun.COM /* If unsucessful, remove the added handlers */ 103310187SKrishna.Elango@Sun.COM if (ret != DDI_SUCCESS) { 103410187SKrishna.Elango@Sun.COM for (x = 0; x < count; x++) { 103510187SKrishna.Elango@Sun.COM (void) ddi_intr_remove_handler(pcieb->pcieb_htable[x]); 103610187SKrishna.Elango@Sun.COM } 103710187SKrishna.Elango@Sun.COM goto FAIL; 103810187SKrishna.Elango@Sun.COM } 103910187SKrishna.Elango@Sun.COM 104010187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_HANDLER; 104110187SKrishna.Elango@Sun.COM 104210187SKrishna.Elango@Sun.COM (void) ddi_intr_get_cap(pcieb->pcieb_htable[0], &intr_cap); 104310187SKrishna.Elango@Sun.COM 104410187SKrishna.Elango@Sun.COM /* 104510187SKrishna.Elango@Sun.COM * Get this intr lock because we are not quite ready to handle 104610187SKrishna.Elango@Sun.COM * interrupts immediately after enabling it. The MSI multi register 104710187SKrishna.Elango@Sun.COM * gets programmed in ddi_intr_enable after which we need to get the 104810187SKrishna.Elango@Sun.COM * MSI offsets for Hotplug/AER. 104910187SKrishna.Elango@Sun.COM */ 105010187SKrishna.Elango@Sun.COM mutex_enter(&pcieb->pcieb_intr_mutex); 105110187SKrishna.Elango@Sun.COM 105210187SKrishna.Elango@Sun.COM if (intr_cap & DDI_INTR_FLAG_BLOCK) { 105310187SKrishna.Elango@Sun.COM (void) ddi_intr_block_enable(pcieb->pcieb_htable, 105410187SKrishna.Elango@Sun.COM pcieb->pcieb_intr_count); 105510187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_BLOCK; 105610187SKrishna.Elango@Sun.COM } else { 105710187SKrishna.Elango@Sun.COM for (count = 0; count < pcieb->pcieb_intr_count; count++) { 105810187SKrishna.Elango@Sun.COM (void) ddi_intr_enable(pcieb->pcieb_htable[count]); 105910187SKrishna.Elango@Sun.COM } 106010187SKrishna.Elango@Sun.COM } 106110187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_ENABLE; 106210187SKrishna.Elango@Sun.COM 106310187SKrishna.Elango@Sun.COM /* Save the interrupt type */ 106410187SKrishna.Elango@Sun.COM pcieb->pcieb_intr_type = intr_type; 106510187SKrishna.Elango@Sun.COM 106610187SKrishna.Elango@Sun.COM /* Get the MSI offset for hotplug/PME from the PCIe cap reg */ 106710187SKrishna.Elango@Sun.COM if (intr_type == DDI_INTR_TYPE_MSI) { 106810187SKrishna.Elango@Sun.COM hp_msi_off = PCI_CAP_GET16(bus_p->bus_cfg_hdl, NULL, 106910187SKrishna.Elango@Sun.COM bus_p->bus_pcie_off, PCIE_PCIECAP) & 107010187SKrishna.Elango@Sun.COM PCIE_PCIECAP_INT_MSG_NUM; 107110187SKrishna.Elango@Sun.COM 107210187SKrishna.Elango@Sun.COM if (hp_msi_off >= count) { 107310187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "MSI number %d in PCIe " 107410187SKrishna.Elango@Sun.COM "cap > max allocated %d\n", hp_msi_off, count); 107510187SKrishna.Elango@Sun.COM mutex_exit(&pcieb->pcieb_intr_mutex); 107610187SKrishna.Elango@Sun.COM goto FAIL; 107710187SKrishna.Elango@Sun.COM } 107810187SKrishna.Elango@Sun.COM 107910187SKrishna.Elango@Sun.COM if (is_hp) 108010187SKrishna.Elango@Sun.COM pcieb->pcieb_isr_tab[hp_msi_off] |= PCIEB_INTR_SRC_HP; 108110187SKrishna.Elango@Sun.COM 108210187SKrishna.Elango@Sun.COM if (is_pme) 108310187SKrishna.Elango@Sun.COM pcieb->pcieb_isr_tab[hp_msi_off] |= PCIEB_INTR_SRC_PME; 108410187SKrishna.Elango@Sun.COM } else { 108510187SKrishna.Elango@Sun.COM /* INTx handles only Hotplug interrupts */ 108610187SKrishna.Elango@Sun.COM if (is_hp) 108710187SKrishna.Elango@Sun.COM pcieb->pcieb_isr_tab[0] |= PCIEB_INTR_SRC_HP; 108810187SKrishna.Elango@Sun.COM } 108910187SKrishna.Elango@Sun.COM 109010187SKrishna.Elango@Sun.COM 109110187SKrishna.Elango@Sun.COM /* 109210187SKrishna.Elango@Sun.COM * Get the MSI offset for errors from the AER Root Error status 109310187SKrishna.Elango@Sun.COM * register. 109410187SKrishna.Elango@Sun.COM */ 109510187SKrishna.Elango@Sun.COM if ((intr_type == DDI_INTR_TYPE_MSI) && PCIE_IS_RP(bus_p)) { 109610187SKrishna.Elango@Sun.COM if (PCIE_HAS_AER(bus_p)) { 109710187SKrishna.Elango@Sun.COM int aer_msi_off; 109810187SKrishna.Elango@Sun.COM aer_msi_off = (PCI_XCAP_GET32(bus_p->bus_cfg_hdl, NULL, 109910187SKrishna.Elango@Sun.COM bus_p->bus_aer_off, PCIE_AER_RE_STS) >> 110010187SKrishna.Elango@Sun.COM PCIE_AER_RE_STS_MSG_NUM_SHIFT) & 110110187SKrishna.Elango@Sun.COM PCIE_AER_RE_STS_MSG_NUM_MASK; 110210187SKrishna.Elango@Sun.COM 110310187SKrishna.Elango@Sun.COM if (aer_msi_off >= count) { 110410187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "MSI number %d in" 110510187SKrishna.Elango@Sun.COM " AER cap > max allocated %d\n", 110610187SKrishna.Elango@Sun.COM aer_msi_off, count); 110710187SKrishna.Elango@Sun.COM mutex_exit(&pcieb->pcieb_intr_mutex); 110810187SKrishna.Elango@Sun.COM goto FAIL; 110910187SKrishna.Elango@Sun.COM } 111010187SKrishna.Elango@Sun.COM pcieb->pcieb_isr_tab[aer_msi_off] |= PCIEB_INTR_SRC_AER; 111110187SKrishna.Elango@Sun.COM } else { 111210187SKrishna.Elango@Sun.COM /* 111310187SKrishna.Elango@Sun.COM * This RP does not have AER. Fallback to the 111410187SKrishna.Elango@Sun.COM * SERR+Machinecheck approach if available. 111510187SKrishna.Elango@Sun.COM */ 111610187SKrishna.Elango@Sun.COM pcieb->pcieb_no_aer_msi = B_TRUE; 111710187SKrishna.Elango@Sun.COM } 111810187SKrishna.Elango@Sun.COM } 111910187SKrishna.Elango@Sun.COM 112010187SKrishna.Elango@Sun.COM mutex_exit(&pcieb->pcieb_intr_mutex); 112110187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 112210187SKrishna.Elango@Sun.COM 112310187SKrishna.Elango@Sun.COM FAIL: 112410397SShesha.Sreenivasamurthy@Sun.COM pcieb_intr_fini(pcieb); 112510187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 112610187SKrishna.Elango@Sun.COM } 112710187SKrishna.Elango@Sun.COM 112810187SKrishna.Elango@Sun.COM static void 112910187SKrishna.Elango@Sun.COM pcieb_intr_fini(pcieb_devstate_t *pcieb) 113010187SKrishna.Elango@Sun.COM { 113110187SKrishna.Elango@Sun.COM int x; 113210187SKrishna.Elango@Sun.COM int count = pcieb->pcieb_intr_count; 113310187SKrishna.Elango@Sun.COM int flags = pcieb->pcieb_init_flags; 113410187SKrishna.Elango@Sun.COM 113510187SKrishna.Elango@Sun.COM if ((flags & PCIEB_INIT_ENABLE) && 113610187SKrishna.Elango@Sun.COM (flags & PCIEB_INIT_BLOCK)) { 113710187SKrishna.Elango@Sun.COM (void) ddi_intr_block_disable(pcieb->pcieb_htable, count); 113810187SKrishna.Elango@Sun.COM flags &= ~(PCIEB_INIT_ENABLE | 113910187SKrishna.Elango@Sun.COM PCIEB_INIT_BLOCK); 114010187SKrishna.Elango@Sun.COM } 114110187SKrishna.Elango@Sun.COM 114210187SKrishna.Elango@Sun.COM if (flags & PCIEB_INIT_MUTEX) 114310187SKrishna.Elango@Sun.COM mutex_destroy(&pcieb->pcieb_intr_mutex); 114410187SKrishna.Elango@Sun.COM 114510187SKrishna.Elango@Sun.COM for (x = 0; x < count; x++) { 114610187SKrishna.Elango@Sun.COM if (flags & PCIEB_INIT_ENABLE) 114710187SKrishna.Elango@Sun.COM (void) ddi_intr_disable(pcieb->pcieb_htable[x]); 114810187SKrishna.Elango@Sun.COM 114910187SKrishna.Elango@Sun.COM if (flags & PCIEB_INIT_HANDLER) 115010187SKrishna.Elango@Sun.COM (void) ddi_intr_remove_handler(pcieb->pcieb_htable[x]); 115110187SKrishna.Elango@Sun.COM 115210187SKrishna.Elango@Sun.COM if (flags & PCIEB_INIT_ALLOC) 115310187SKrishna.Elango@Sun.COM (void) ddi_intr_free(pcieb->pcieb_htable[x]); 115410187SKrishna.Elango@Sun.COM } 115510187SKrishna.Elango@Sun.COM 115610187SKrishna.Elango@Sun.COM flags &= ~(PCIEB_INIT_ENABLE | PCIEB_INIT_HANDLER | PCIEB_INIT_ALLOC | 115710187SKrishna.Elango@Sun.COM PCIEB_INIT_MUTEX); 115810187SKrishna.Elango@Sun.COM 115910187SKrishna.Elango@Sun.COM if (flags & PCIEB_INIT_HTABLE) 116010187SKrishna.Elango@Sun.COM kmem_free(pcieb->pcieb_htable, pcieb->pcieb_htable_size); 116110187SKrishna.Elango@Sun.COM 116210187SKrishna.Elango@Sun.COM flags &= ~PCIEB_INIT_HTABLE; 116310187SKrishna.Elango@Sun.COM 116410187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags &= flags; 116510187SKrishna.Elango@Sun.COM } 116610187SKrishna.Elango@Sun.COM 116710187SKrishna.Elango@Sun.COM /* 116810187SKrishna.Elango@Sun.COM * Checks if this device needs MSIs enabled or not. 116910187SKrishna.Elango@Sun.COM */ 117010187SKrishna.Elango@Sun.COM /*ARGSUSED*/ 117110187SKrishna.Elango@Sun.COM static int 117210187SKrishna.Elango@Sun.COM pcieb_msi_supported(dev_info_t *dip) 117310187SKrishna.Elango@Sun.COM { 117410187SKrishna.Elango@Sun.COM return ((pcieb_enable_msi && pcieb_plat_msi_supported(dip)) ? 117510187SKrishna.Elango@Sun.COM DDI_SUCCESS: DDI_FAILURE); 117610187SKrishna.Elango@Sun.COM } 117710187SKrishna.Elango@Sun.COM 117810187SKrishna.Elango@Sun.COM /*ARGSUSED*/ 117910397SShesha.Sreenivasamurthy@Sun.COM static int 118010187SKrishna.Elango@Sun.COM pcieb_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap, 118110187SKrishna.Elango@Sun.COM ddi_iblock_cookie_t *ibc) 118210187SKrishna.Elango@Sun.COM { 118310187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, 118410187SKrishna.Elango@Sun.COM ddi_get_instance(dip)); 118510187SKrishna.Elango@Sun.COM 118610187SKrishna.Elango@Sun.COM ASSERT(ibc != NULL); 118710187SKrishna.Elango@Sun.COM *ibc = pcieb->pcieb_fm_ibc; 118810187SKrishna.Elango@Sun.COM 118910187SKrishna.Elango@Sun.COM return (DEVI(dip)->devi_fmhdl->fh_cap | DDI_FM_ACCCHK_CAPABLE | 119010187SKrishna.Elango@Sun.COM DDI_FM_DMACHK_CAPABLE); 119110187SKrishna.Elango@Sun.COM } 119210187SKrishna.Elango@Sun.COM 119310187SKrishna.Elango@Sun.COM static int 119410187SKrishna.Elango@Sun.COM pcieb_fm_init(pcieb_devstate_t *pcieb_p) 119510187SKrishna.Elango@Sun.COM { 119610187SKrishna.Elango@Sun.COM dev_info_t *dip = pcieb_p->pcieb_dip; 119710187SKrishna.Elango@Sun.COM int fm_cap = DDI_FM_EREPORT_CAPABLE; 119810187SKrishna.Elango@Sun.COM 119910187SKrishna.Elango@Sun.COM /* 120010187SKrishna.Elango@Sun.COM * Request our capability level and get our parents capability 120110187SKrishna.Elango@Sun.COM * and ibc. 120210187SKrishna.Elango@Sun.COM */ 120310187SKrishna.Elango@Sun.COM ddi_fm_init(dip, &fm_cap, &pcieb_p->pcieb_fm_ibc); 120410187SKrishna.Elango@Sun.COM 120510187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 120610187SKrishna.Elango@Sun.COM } 120710187SKrishna.Elango@Sun.COM 120810187SKrishna.Elango@Sun.COM /* 120910187SKrishna.Elango@Sun.COM * Breakdown our FMA resources 121010187SKrishna.Elango@Sun.COM */ 121110187SKrishna.Elango@Sun.COM static void 121210187SKrishna.Elango@Sun.COM pcieb_fm_fini(pcieb_devstate_t *pcieb_p) 121310187SKrishna.Elango@Sun.COM { 121410187SKrishna.Elango@Sun.COM /* 121510187SKrishna.Elango@Sun.COM * Clean up allocated fm structures 121610187SKrishna.Elango@Sun.COM */ 121710187SKrishna.Elango@Sun.COM ddi_fm_fini(pcieb_p->pcieb_dip); 121810187SKrishna.Elango@Sun.COM } 121910187SKrishna.Elango@Sun.COM 122010187SKrishna.Elango@Sun.COM static int 122110187SKrishna.Elango@Sun.COM pcieb_open(dev_t *devp, int flags, int otyp, cred_t *credp) 122210187SKrishna.Elango@Sun.COM { 122310923SEvan.Yan@Sun.COM int inst = PCI_MINOR_NUM_TO_INSTANCE(getminor(*devp)); 122410923SEvan.Yan@Sun.COM pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, inst); 122510923SEvan.Yan@Sun.COM int rv; 122610187SKrishna.Elango@Sun.COM 122710923SEvan.Yan@Sun.COM if (pcieb == NULL) 122810187SKrishna.Elango@Sun.COM return (ENXIO); 122910187SKrishna.Elango@Sun.COM 123010923SEvan.Yan@Sun.COM mutex_enter(&pcieb->pcieb_mutex); 123110923SEvan.Yan@Sun.COM rv = pcie_open(pcieb->pcieb_dip, devp, flags, otyp, credp); 123210923SEvan.Yan@Sun.COM mutex_exit(&pcieb->pcieb_mutex); 123310187SKrishna.Elango@Sun.COM 123410923SEvan.Yan@Sun.COM return (rv); 123510187SKrishna.Elango@Sun.COM } 123610187SKrishna.Elango@Sun.COM 123710187SKrishna.Elango@Sun.COM static int 123810187SKrishna.Elango@Sun.COM pcieb_close(dev_t dev, int flags, int otyp, cred_t *credp) 123910187SKrishna.Elango@Sun.COM { 124010923SEvan.Yan@Sun.COM int inst = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev)); 124110923SEvan.Yan@Sun.COM pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, inst); 124210923SEvan.Yan@Sun.COM int rv; 124310187SKrishna.Elango@Sun.COM 124410923SEvan.Yan@Sun.COM if (pcieb == NULL) 124510187SKrishna.Elango@Sun.COM return (ENXIO); 124610187SKrishna.Elango@Sun.COM 124710923SEvan.Yan@Sun.COM mutex_enter(&pcieb->pcieb_mutex); 124810923SEvan.Yan@Sun.COM rv = pcie_close(pcieb->pcieb_dip, dev, flags, otyp, credp); 124910923SEvan.Yan@Sun.COM mutex_exit(&pcieb->pcieb_mutex); 125010187SKrishna.Elango@Sun.COM 125110923SEvan.Yan@Sun.COM return (rv); 125210187SKrishna.Elango@Sun.COM } 125310187SKrishna.Elango@Sun.COM 125410187SKrishna.Elango@Sun.COM static int 125510187SKrishna.Elango@Sun.COM pcieb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 125610187SKrishna.Elango@Sun.COM int *rvalp) 125710187SKrishna.Elango@Sun.COM { 125810923SEvan.Yan@Sun.COM int inst = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev)); 125910923SEvan.Yan@Sun.COM pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, inst); 126010923SEvan.Yan@Sun.COM int rv; 126110187SKrishna.Elango@Sun.COM 126210923SEvan.Yan@Sun.COM if (pcieb == NULL) 126310187SKrishna.Elango@Sun.COM return (ENXIO); 126410187SKrishna.Elango@Sun.COM 126510923SEvan.Yan@Sun.COM /* To handle devctl and hotplug related ioctls */ 126610923SEvan.Yan@Sun.COM rv = pcie_ioctl(pcieb->pcieb_dip, dev, cmd, arg, mode, credp, rvalp); 126710187SKrishna.Elango@Sun.COM 126810187SKrishna.Elango@Sun.COM return (rv); 126910187SKrishna.Elango@Sun.COM } 127010187SKrishna.Elango@Sun.COM 127110187SKrishna.Elango@Sun.COM /* 127210187SKrishna.Elango@Sun.COM * Common interrupt handler for hotplug, PME and errors. 127310187SKrishna.Elango@Sun.COM */ 127410187SKrishna.Elango@Sun.COM static uint_t 127510187SKrishna.Elango@Sun.COM pcieb_intr_handler(caddr_t arg1, caddr_t arg2) 127610187SKrishna.Elango@Sun.COM { 127710187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb_p = (pcieb_devstate_t *)arg1; 127810187SKrishna.Elango@Sun.COM dev_info_t *dip = pcieb_p->pcieb_dip; 127910187SKrishna.Elango@Sun.COM ddi_fm_error_t derr; 128010187SKrishna.Elango@Sun.COM int sts = 0; 128110187SKrishna.Elango@Sun.COM int ret = DDI_INTR_UNCLAIMED; 128210187SKrishna.Elango@Sun.COM int isrc; 128310187SKrishna.Elango@Sun.COM 128410187SKrishna.Elango@Sun.COM if (!(pcieb_p->pcieb_init_flags & PCIEB_INIT_ENABLE)) 128510187SKrishna.Elango@Sun.COM goto FAIL; 128610187SKrishna.Elango@Sun.COM 128710187SKrishna.Elango@Sun.COM mutex_enter(&pcieb_p->pcieb_intr_mutex); 128810187SKrishna.Elango@Sun.COM isrc = pcieb_p->pcieb_isr_tab[(int)(uintptr_t)arg2]; 128910187SKrishna.Elango@Sun.COM mutex_exit(&pcieb_p->pcieb_intr_mutex); 129010187SKrishna.Elango@Sun.COM 129110187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_INTR, dip, "Received intr number %d\n", 129210187SKrishna.Elango@Sun.COM (int)(uintptr_t)arg2); 129310187SKrishna.Elango@Sun.COM 129410187SKrishna.Elango@Sun.COM if (isrc == PCIEB_INTR_SRC_UNKNOWN) 129510187SKrishna.Elango@Sun.COM goto FAIL; 129610187SKrishna.Elango@Sun.COM 129710923SEvan.Yan@Sun.COM if (isrc & PCIEB_INTR_SRC_HP) 129810923SEvan.Yan@Sun.COM ret = pcie_intr(dip); 129910187SKrishna.Elango@Sun.COM 130010187SKrishna.Elango@Sun.COM if (isrc & PCIEB_INTR_SRC_PME) 130110187SKrishna.Elango@Sun.COM ret = DDI_INTR_CLAIMED; 130210187SKrishna.Elango@Sun.COM 130310187SKrishna.Elango@Sun.COM /* AER Error */ 130410187SKrishna.Elango@Sun.COM if (isrc & PCIEB_INTR_SRC_AER) { 130510187SKrishna.Elango@Sun.COM /* 130610187SKrishna.Elango@Sun.COM * If MSI is shared with PME/hotplug then check Root Error 130710187SKrishna.Elango@Sun.COM * Status Reg before claiming it. For now it's ok since 130810187SKrishna.Elango@Sun.COM * we know we get 2 MSIs. 130910187SKrishna.Elango@Sun.COM */ 131010187SKrishna.Elango@Sun.COM ret = DDI_INTR_CLAIMED; 131110187SKrishna.Elango@Sun.COM bzero(&derr, sizeof (ddi_fm_error_t)); 131210187SKrishna.Elango@Sun.COM derr.fme_version = DDI_FME_VERSION; 131310187SKrishna.Elango@Sun.COM mutex_enter(&pcieb_p->pcieb_peek_poke_mutex); 131410187SKrishna.Elango@Sun.COM mutex_enter(&pcieb_p->pcieb_err_mutex); 131510187SKrishna.Elango@Sun.COM 131610187SKrishna.Elango@Sun.COM if ((DEVI(dip)->devi_fmhdl->fh_cap) & DDI_FM_EREPORT_CAPABLE) 131710187SKrishna.Elango@Sun.COM sts = pf_scan_fabric(dip, &derr, NULL); 131810187SKrishna.Elango@Sun.COM 131910187SKrishna.Elango@Sun.COM mutex_exit(&pcieb_p->pcieb_err_mutex); 132010187SKrishna.Elango@Sun.COM mutex_exit(&pcieb_p->pcieb_peek_poke_mutex); 132110187SKrishna.Elango@Sun.COM if (pcieb_die & sts) 132210187SKrishna.Elango@Sun.COM fm_panic("%s-%d: PCI(-X) Express Fatal Error. (0x%x)", 132310187SKrishna.Elango@Sun.COM ddi_driver_name(dip), ddi_get_instance(dip), sts); 132410187SKrishna.Elango@Sun.COM } 132510187SKrishna.Elango@Sun.COM FAIL: 132610187SKrishna.Elango@Sun.COM return (ret); 132710187SKrishna.Elango@Sun.COM } 132810187SKrishna.Elango@Sun.COM 132910187SKrishna.Elango@Sun.COM /* 133010187SKrishna.Elango@Sun.COM * Some PCI-X to PCI-E bridges do not support full 64-bit addressing on the 133110187SKrishna.Elango@Sun.COM * PCI-X side of the bridge. We build a special version of this driver for 133210187SKrishna.Elango@Sun.COM * those bridges, which uses PCIEB_ADDR_LIMIT_LO and/or PCIEB_ADDR_LIMIT_HI 133310187SKrishna.Elango@Sun.COM * to define the range of values which the chip can handle. The code below 133410187SKrishna.Elango@Sun.COM * then clamps the DMA address range supplied by the driver, preventing the 133510187SKrishna.Elango@Sun.COM * PCI-E nexus driver from allocating any memory the bridge can't deal 133610187SKrishna.Elango@Sun.COM * with. 133710187SKrishna.Elango@Sun.COM */ 133810187SKrishna.Elango@Sun.COM static int 133910187SKrishna.Elango@Sun.COM pcieb_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, 134010187SKrishna.Elango@Sun.COM ddi_dma_attr_t *attr_p, int (*waitfp)(caddr_t), caddr_t arg, 134110187SKrishna.Elango@Sun.COM ddi_dma_handle_t *handlep) 134210187SKrishna.Elango@Sun.COM { 134310187SKrishna.Elango@Sun.COM int ret; 134410187SKrishna.Elango@Sun.COM #ifdef BCM_SW_WORKAROUNDS 134510187SKrishna.Elango@Sun.COM uint64_t lim; 134610187SKrishna.Elango@Sun.COM 134710187SKrishna.Elango@Sun.COM /* 134810187SKrishna.Elango@Sun.COM * If the leaf device's limits are outside than what the Broadcom 134910187SKrishna.Elango@Sun.COM * bridge can handle, we need to clip the values passed up the chain. 135010187SKrishna.Elango@Sun.COM */ 135110187SKrishna.Elango@Sun.COM lim = attr_p->dma_attr_addr_lo; 135210187SKrishna.Elango@Sun.COM attr_p->dma_attr_addr_lo = MAX(lim, PCIEB_ADDR_LIMIT_LO); 135310187SKrishna.Elango@Sun.COM 135410187SKrishna.Elango@Sun.COM lim = attr_p->dma_attr_addr_hi; 135510187SKrishna.Elango@Sun.COM attr_p->dma_attr_addr_hi = MIN(lim, PCIEB_ADDR_LIMIT_HI); 135610187SKrishna.Elango@Sun.COM 135710187SKrishna.Elango@Sun.COM #endif /* BCM_SW_WORKAROUNDS */ 135810187SKrishna.Elango@Sun.COM 135910187SKrishna.Elango@Sun.COM /* 136010187SKrishna.Elango@Sun.COM * This is a software workaround to fix the Broadcom 5714/5715 PCIe-PCI 136110187SKrishna.Elango@Sun.COM * bridge prefetch bug. Intercept the DMA alloc handle request and set 136210187SKrishna.Elango@Sun.COM * PX_DMAI_FLAGS_MAP_BUFZONE flag in the handle. If this flag is set, 136310187SKrishna.Elango@Sun.COM * the px nexus driver will allocate an extra page & make it valid one, 136410187SKrishna.Elango@Sun.COM * for any DVMA request that comes from any of the Broadcom bridge child 136510187SKrishna.Elango@Sun.COM * devices. 136610187SKrishna.Elango@Sun.COM */ 136710187SKrishna.Elango@Sun.COM if ((ret = ddi_dma_allochdl(dip, rdip, attr_p, waitfp, arg, 136810187SKrishna.Elango@Sun.COM handlep)) == DDI_SUCCESS) { 136910187SKrishna.Elango@Sun.COM ddi_dma_impl_t *mp = (ddi_dma_impl_t *)*handlep; 137010187SKrishna.Elango@Sun.COM #ifdef BCM_SW_WORKAROUNDS 137110187SKrishna.Elango@Sun.COM mp->dmai_inuse |= PX_DMAI_FLAGS_MAP_BUFZONE; 137210187SKrishna.Elango@Sun.COM #endif /* BCM_SW_WORKAROUNDS */ 137310187SKrishna.Elango@Sun.COM /* 137410187SKrishna.Elango@Sun.COM * For a given rdip, update mp->dmai_bdf with the bdf value 137510187SKrishna.Elango@Sun.COM * of pcieb's immediate child or secondary bus-id of the 137610187SKrishna.Elango@Sun.COM * PCIe2PCI bridge. 137710187SKrishna.Elango@Sun.COM */ 137810187SKrishna.Elango@Sun.COM mp->dmai_minxfer = pcie_get_bdf_for_dma_xfer(dip, rdip); 137910187SKrishna.Elango@Sun.COM } 138010187SKrishna.Elango@Sun.COM 138110187SKrishna.Elango@Sun.COM return (ret); 138210187SKrishna.Elango@Sun.COM } 138310187SKrishna.Elango@Sun.COM 138410187SKrishna.Elango@Sun.COM /* 138510187SKrishna.Elango@Sun.COM * FDVMA feature is not supported for any child device of Broadcom 5714/5715 138610187SKrishna.Elango@Sun.COM * PCIe-PCI bridge due to prefetch bug. Return failure immediately, so that 138710187SKrishna.Elango@Sun.COM * these drivers will switch to regular DVMA path. 138810187SKrishna.Elango@Sun.COM */ 138910187SKrishna.Elango@Sun.COM /*ARGSUSED*/ 139010187SKrishna.Elango@Sun.COM static int 139110187SKrishna.Elango@Sun.COM pcieb_dma_mctl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle, 139210187SKrishna.Elango@Sun.COM enum ddi_dma_ctlops cmd, off_t *offp, size_t *lenp, caddr_t *objp, 139310187SKrishna.Elango@Sun.COM uint_t cache_flags) 139410187SKrishna.Elango@Sun.COM { 139510187SKrishna.Elango@Sun.COM int ret; 139610187SKrishna.Elango@Sun.COM 139710187SKrishna.Elango@Sun.COM #ifdef BCM_SW_WORKAROUNDS 139810187SKrishna.Elango@Sun.COM if (cmd == DDI_DMA_RESERVE) 139910187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 140010187SKrishna.Elango@Sun.COM #endif /* BCM_SW_WORKAROUNDS */ 140110187SKrishna.Elango@Sun.COM 140210187SKrishna.Elango@Sun.COM if (((ret = ddi_dma_mctl(dip, rdip, handle, cmd, offp, lenp, objp, 140310187SKrishna.Elango@Sun.COM cache_flags)) == DDI_SUCCESS) && (cmd == DDI_DMA_RESERVE)) { 140410187SKrishna.Elango@Sun.COM ddi_dma_impl_t *mp = (ddi_dma_impl_t *)*objp; 140510187SKrishna.Elango@Sun.COM 140610187SKrishna.Elango@Sun.COM /* 140710187SKrishna.Elango@Sun.COM * For a given rdip, update mp->dmai_bdf with the bdf value 140810187SKrishna.Elango@Sun.COM * of pcieb's immediate child or secondary bus-id of the 140910187SKrishna.Elango@Sun.COM * PCIe2PCI bridge. 141010187SKrishna.Elango@Sun.COM */ 141110187SKrishna.Elango@Sun.COM mp->dmai_minxfer = pcie_get_bdf_for_dma_xfer(dip, rdip); 141210187SKrishna.Elango@Sun.COM } 141310187SKrishna.Elango@Sun.COM 141410187SKrishna.Elango@Sun.COM return (ret); 141510187SKrishna.Elango@Sun.COM } 141610187SKrishna.Elango@Sun.COM 141710187SKrishna.Elango@Sun.COM static int 141810187SKrishna.Elango@Sun.COM pcieb_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 141910187SKrishna.Elango@Sun.COM ddi_intr_handle_impl_t *hdlp, void *result) 142010187SKrishna.Elango@Sun.COM { 142110187SKrishna.Elango@Sun.COM return (pcieb_plat_intr_ops(dip, rdip, intr_op, hdlp, result)); 142210187SKrishna.Elango@Sun.COM 142310187SKrishna.Elango@Sun.COM } 142410187SKrishna.Elango@Sun.COM 142510187SKrishna.Elango@Sun.COM /* 142610187SKrishna.Elango@Sun.COM * Power management related initialization specific to pcieb. 142710187SKrishna.Elango@Sun.COM * Called by pcieb_attach() 142810187SKrishna.Elango@Sun.COM */ 142910187SKrishna.Elango@Sun.COM static int 143010187SKrishna.Elango@Sun.COM pcieb_pwr_setup(dev_info_t *dip) 143110187SKrishna.Elango@Sun.COM { 143210187SKrishna.Elango@Sun.COM char *comp_array[5]; 143310187SKrishna.Elango@Sun.COM int i; 143410187SKrishna.Elango@Sun.COM ddi_acc_handle_t conf_hdl; 143510187SKrishna.Elango@Sun.COM uint16_t pmcap, cap_ptr; 143610187SKrishna.Elango@Sun.COM pcie_pwr_t *pwr_p; 143710187SKrishna.Elango@Sun.COM 143810187SKrishna.Elango@Sun.COM /* Some platforms/devices may choose to disable PM */ 143910187SKrishna.Elango@Sun.COM if (pcieb_plat_pwr_disable(dip)) { 144010187SKrishna.Elango@Sun.COM (void) pcieb_pwr_disable(dip); 144110187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 144210187SKrishna.Elango@Sun.COM } 144310187SKrishna.Elango@Sun.COM 144410187SKrishna.Elango@Sun.COM ASSERT(PCIE_PMINFO(dip)); 144510187SKrishna.Elango@Sun.COM pwr_p = PCIE_NEXUS_PMINFO(dip); 144610187SKrishna.Elango@Sun.COM ASSERT(pwr_p); 144710187SKrishna.Elango@Sun.COM 144810187SKrishna.Elango@Sun.COM /* Code taken from pci_pci driver */ 144910187SKrishna.Elango@Sun.COM if (pci_config_setup(dip, &pwr_p->pwr_conf_hdl) != DDI_SUCCESS) { 145010187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "pcieb_pwr_setup: pci_config_setup " 145110187SKrishna.Elango@Sun.COM "failed\n"); 145210187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 145310187SKrishna.Elango@Sun.COM } 145410187SKrishna.Elango@Sun.COM conf_hdl = pwr_p->pwr_conf_hdl; 145510187SKrishna.Elango@Sun.COM 145610187SKrishna.Elango@Sun.COM /* 145710187SKrishna.Elango@Sun.COM * Walk the capabilities searching for a PM entry. 145810187SKrishna.Elango@Sun.COM */ 145910187SKrishna.Elango@Sun.COM if ((PCI_CAP_LOCATE(conf_hdl, PCI_CAP_ID_PM, &cap_ptr)) == 146010187SKrishna.Elango@Sun.COM DDI_FAILURE) { 146110187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "switch/bridge does not support PM. " 146210187SKrishna.Elango@Sun.COM " PCI PM data structure not found in config header\n"); 146310187SKrishna.Elango@Sun.COM pci_config_teardown(&conf_hdl); 146410187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 146510187SKrishna.Elango@Sun.COM } 146610187SKrishna.Elango@Sun.COM /* 146710187SKrishna.Elango@Sun.COM * Save offset to pmcsr for future references. 146810187SKrishna.Elango@Sun.COM */ 146910187SKrishna.Elango@Sun.COM pwr_p->pwr_pmcsr_offset = cap_ptr + PCI_PMCSR; 147010187SKrishna.Elango@Sun.COM pmcap = PCI_CAP_GET16(conf_hdl, NULL, cap_ptr, PCI_PMCAP); 147110187SKrishna.Elango@Sun.COM if (pmcap & PCI_PMCAP_D1) { 147210187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "D1 state supported\n"); 147310187SKrishna.Elango@Sun.COM pwr_p->pwr_pmcaps |= PCIE_SUPPORTS_D1; 147410187SKrishna.Elango@Sun.COM } 147510187SKrishna.Elango@Sun.COM if (pmcap & PCI_PMCAP_D2) { 147610187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "D2 state supported\n"); 147710187SKrishna.Elango@Sun.COM pwr_p->pwr_pmcaps |= PCIE_SUPPORTS_D2; 147810187SKrishna.Elango@Sun.COM } 147910187SKrishna.Elango@Sun.COM 148010187SKrishna.Elango@Sun.COM i = 0; 148110187SKrishna.Elango@Sun.COM comp_array[i++] = "NAME=PCIe switch/bridge PM"; 148210187SKrishna.Elango@Sun.COM comp_array[i++] = "0=Power Off (D3)"; 148310187SKrishna.Elango@Sun.COM if (pwr_p->pwr_pmcaps & PCIE_SUPPORTS_D2) 148410187SKrishna.Elango@Sun.COM comp_array[i++] = "1=D2"; 148510187SKrishna.Elango@Sun.COM if (pwr_p->pwr_pmcaps & PCIE_SUPPORTS_D1) 148610187SKrishna.Elango@Sun.COM comp_array[i++] = "2=D1"; 148710187SKrishna.Elango@Sun.COM comp_array[i++] = "3=Full Power D0"; 148810187SKrishna.Elango@Sun.COM 148910187SKrishna.Elango@Sun.COM /* 149010187SKrishna.Elango@Sun.COM * Create pm-components property, if it does not exist already. 149110187SKrishna.Elango@Sun.COM */ 149210187SKrishna.Elango@Sun.COM if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip, 149310187SKrishna.Elango@Sun.COM "pm-components", comp_array, i) != DDI_PROP_SUCCESS) { 149410187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "could not create pm-components " 149510187SKrishna.Elango@Sun.COM " prop\n"); 149610187SKrishna.Elango@Sun.COM pci_config_teardown(&conf_hdl); 149710187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 149810187SKrishna.Elango@Sun.COM } 149910187SKrishna.Elango@Sun.COM return (pcieb_pwr_init_and_raise(dip, pwr_p)); 150010187SKrishna.Elango@Sun.COM } 150110187SKrishna.Elango@Sun.COM 150210187SKrishna.Elango@Sun.COM /* 150310187SKrishna.Elango@Sun.COM * undo whatever is done in pcieb_pwr_setup. called by pcieb_detach() 150410187SKrishna.Elango@Sun.COM */ 150510187SKrishna.Elango@Sun.COM static void 150610187SKrishna.Elango@Sun.COM pcieb_pwr_teardown(dev_info_t *dip) 150710187SKrishna.Elango@Sun.COM { 150810187SKrishna.Elango@Sun.COM pcie_pwr_t *pwr_p; 150910187SKrishna.Elango@Sun.COM 151010187SKrishna.Elango@Sun.COM if (!PCIE_PMINFO(dip) || !(pwr_p = PCIE_NEXUS_PMINFO(dip))) 151110187SKrishna.Elango@Sun.COM return; 151210187SKrishna.Elango@Sun.COM 151310187SKrishna.Elango@Sun.COM (void) ddi_prop_remove(DDI_DEV_T_NONE, dip, "pm-components"); 151410187SKrishna.Elango@Sun.COM if (pwr_p->pwr_conf_hdl) 151510187SKrishna.Elango@Sun.COM pci_config_teardown(&pwr_p->pwr_conf_hdl); 151610187SKrishna.Elango@Sun.COM } 151710187SKrishna.Elango@Sun.COM 151810187SKrishna.Elango@Sun.COM /* 151910187SKrishna.Elango@Sun.COM * Initializes the power level and raise the power to D0, if it is 152010187SKrishna.Elango@Sun.COM * not at D0. 152110187SKrishna.Elango@Sun.COM */ 152210187SKrishna.Elango@Sun.COM static int 152310187SKrishna.Elango@Sun.COM pcieb_pwr_init_and_raise(dev_info_t *dip, pcie_pwr_t *pwr_p) 152410187SKrishna.Elango@Sun.COM { 152510187SKrishna.Elango@Sun.COM uint16_t pmcsr; 152610187SKrishna.Elango@Sun.COM int ret = DDI_SUCCESS; 152710187SKrishna.Elango@Sun.COM 152810187SKrishna.Elango@Sun.COM /* 152910187SKrishna.Elango@Sun.COM * Intialize our power level from PMCSR. The common code initializes 153010187SKrishna.Elango@Sun.COM * this to UNKNOWN. There is no guarantee that we will be at full 153110187SKrishna.Elango@Sun.COM * power at attach. If we are not at D0, raise the power. 153210187SKrishna.Elango@Sun.COM */ 153310187SKrishna.Elango@Sun.COM pmcsr = pci_config_get16(pwr_p->pwr_conf_hdl, pwr_p->pwr_pmcsr_offset); 153410187SKrishna.Elango@Sun.COM pmcsr &= PCI_PMCSR_STATE_MASK; 153510187SKrishna.Elango@Sun.COM switch (pmcsr) { 153610187SKrishna.Elango@Sun.COM case PCI_PMCSR_D0: 153710187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D0; 153810187SKrishna.Elango@Sun.COM break; 153910187SKrishna.Elango@Sun.COM 154010187SKrishna.Elango@Sun.COM case PCI_PMCSR_D1: 154110187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D1; 154210187SKrishna.Elango@Sun.COM break; 154310187SKrishna.Elango@Sun.COM 154410187SKrishna.Elango@Sun.COM case PCI_PMCSR_D2: 154510187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D2; 154610187SKrishna.Elango@Sun.COM break; 154710187SKrishna.Elango@Sun.COM 154810187SKrishna.Elango@Sun.COM case PCI_PMCSR_D3HOT: 154910187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D3; 155010187SKrishna.Elango@Sun.COM break; 155110187SKrishna.Elango@Sun.COM 155210187SKrishna.Elango@Sun.COM default: 155310187SKrishna.Elango@Sun.COM break; 155410187SKrishna.Elango@Sun.COM } 155510187SKrishna.Elango@Sun.COM 155610187SKrishna.Elango@Sun.COM /* Raise the power to D0. */ 155710187SKrishna.Elango@Sun.COM if (pwr_p->pwr_func_lvl != PM_LEVEL_D0 && 155810187SKrishna.Elango@Sun.COM ((ret = pm_raise_power(dip, 0, PM_LEVEL_D0)) != DDI_SUCCESS)) { 155910187SKrishna.Elango@Sun.COM /* 156010187SKrishna.Elango@Sun.COM * Read PMCSR again. If it is at D0, ignore the return 156110187SKrishna.Elango@Sun.COM * value from pm_raise_power. 156210187SKrishna.Elango@Sun.COM */ 156310187SKrishna.Elango@Sun.COM pmcsr = pci_config_get16(pwr_p->pwr_conf_hdl, 156410187SKrishna.Elango@Sun.COM pwr_p->pwr_pmcsr_offset); 156510187SKrishna.Elango@Sun.COM if ((pmcsr & PCI_PMCSR_STATE_MASK) == PCI_PMCSR_D0) 156610187SKrishna.Elango@Sun.COM ret = DDI_SUCCESS; 156710187SKrishna.Elango@Sun.COM else { 156810187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "pcieb_pwr_setup: could not " 156910187SKrishna.Elango@Sun.COM "raise power to D0 \n"); 157010187SKrishna.Elango@Sun.COM } 157110187SKrishna.Elango@Sun.COM } 157210187SKrishna.Elango@Sun.COM if (ret == DDI_SUCCESS) 157310187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D0; 157410187SKrishna.Elango@Sun.COM return (ret); 157510187SKrishna.Elango@Sun.COM } 157610187SKrishna.Elango@Sun.COM 157710187SKrishna.Elango@Sun.COM /* 157810187SKrishna.Elango@Sun.COM * Disable PM for x86 and PLX 8532 switch. 157910187SKrishna.Elango@Sun.COM * For PLX Transitioning one port on this switch to low power causes links 158010187SKrishna.Elango@Sun.COM * on other ports on the same station to die. Due to PLX erratum #34, we 158110187SKrishna.Elango@Sun.COM * can't allow the downstream device go to non-D0 state. 158210187SKrishna.Elango@Sun.COM */ 158310187SKrishna.Elango@Sun.COM static int 158410187SKrishna.Elango@Sun.COM pcieb_pwr_disable(dev_info_t *dip) 158510187SKrishna.Elango@Sun.COM { 158610187SKrishna.Elango@Sun.COM pcie_pwr_t *pwr_p; 158710187SKrishna.Elango@Sun.COM 158810187SKrishna.Elango@Sun.COM ASSERT(PCIE_PMINFO(dip)); 158910187SKrishna.Elango@Sun.COM pwr_p = PCIE_NEXUS_PMINFO(dip); 159010187SKrishna.Elango@Sun.COM ASSERT(pwr_p); 159110187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "pcieb_pwr_disable: disabling PM\n"); 159210187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D0; 159310187SKrishna.Elango@Sun.COM pwr_p->pwr_flags = PCIE_NO_CHILD_PM; 159410187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 159510187SKrishna.Elango@Sun.COM } 159610187SKrishna.Elango@Sun.COM 159710187SKrishna.Elango@Sun.COM #ifdef DEBUG 159810187SKrishna.Elango@Sun.COM int pcieb_dbg_intr_print = 0; 159910187SKrishna.Elango@Sun.COM void 160010187SKrishna.Elango@Sun.COM pcieb_dbg(uint_t bit, dev_info_t *dip, char *fmt, ...) 160110187SKrishna.Elango@Sun.COM { 160210187SKrishna.Elango@Sun.COM va_list ap; 160310187SKrishna.Elango@Sun.COM 160410187SKrishna.Elango@Sun.COM if (!pcieb_dbg_print) 160510187SKrishna.Elango@Sun.COM return; 160610187SKrishna.Elango@Sun.COM 160710187SKrishna.Elango@Sun.COM if (dip) 160810187SKrishna.Elango@Sun.COM prom_printf("%s(%d): %s", ddi_driver_name(dip), 160910187SKrishna.Elango@Sun.COM ddi_get_instance(dip), pcieb_debug_sym[bit]); 161010187SKrishna.Elango@Sun.COM 161110187SKrishna.Elango@Sun.COM va_start(ap, fmt); 161210187SKrishna.Elango@Sun.COM if (servicing_interrupt()) { 161310187SKrishna.Elango@Sun.COM if (pcieb_dbg_intr_print) 161410187SKrishna.Elango@Sun.COM prom_vprintf(fmt, ap); 161510187SKrishna.Elango@Sun.COM } else { 161610187SKrishna.Elango@Sun.COM prom_vprintf(fmt, ap); 161710187SKrishna.Elango@Sun.COM } 161810187SKrishna.Elango@Sun.COM 161910187SKrishna.Elango@Sun.COM va_end(ap); 162010187SKrishna.Elango@Sun.COM } 162110187SKrishna.Elango@Sun.COM #endif 162210187SKrishna.Elango@Sun.COM 162310187SKrishna.Elango@Sun.COM static void 162410187SKrishna.Elango@Sun.COM pcieb_id_props(pcieb_devstate_t *pcieb) 162510187SKrishna.Elango@Sun.COM { 162610187SKrishna.Elango@Sun.COM uint64_t serialid = 0; /* 40b field of EUI-64 serial no. register */ 162710187SKrishna.Elango@Sun.COM uint16_t cap_ptr; 162810187SKrishna.Elango@Sun.COM uint8_t fic = 0; /* 1 = first in chassis device */ 162910187SKrishna.Elango@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2BUS(pcieb->pcieb_dip); 163010187SKrishna.Elango@Sun.COM ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl; 163110187SKrishna.Elango@Sun.COM 163210187SKrishna.Elango@Sun.COM /* 163310187SKrishna.Elango@Sun.COM * Identify first in chassis. In the special case of a Sun branded 163410187SKrishna.Elango@Sun.COM * PLX device, it obviously is first in chassis. Otherwise, in the 163510187SKrishna.Elango@Sun.COM * general case, look for an Expansion Slot Register and check its 163610187SKrishna.Elango@Sun.COM * first-in-chassis bit. 163710187SKrishna.Elango@Sun.COM */ 163810187SKrishna.Elango@Sun.COM #ifdef PX_PLX 163910187SKrishna.Elango@Sun.COM uint16_t vendor_id = bus_p->bus_dev_ven_id & 0xFFFF; 164010187SKrishna.Elango@Sun.COM uint16_t device_id = bus_p->bus_dev_ven_id >> 16; 164110187SKrishna.Elango@Sun.COM if ((vendor_id == PXB_VENDOR_SUN) && 164210187SKrishna.Elango@Sun.COM ((device_id == PXB_DEVICE_PLX_PCIX) || 164310187SKrishna.Elango@Sun.COM (device_id == PXB_DEVICE_PLX_PCIE))) { 164410187SKrishna.Elango@Sun.COM fic = 1; 164510187SKrishna.Elango@Sun.COM } 164610187SKrishna.Elango@Sun.COM #endif /* PX_PLX */ 164710187SKrishna.Elango@Sun.COM if ((fic == 0) && ((PCI_CAP_LOCATE(config_handle, 164810187SKrishna.Elango@Sun.COM PCI_CAP_ID_SLOT_ID, &cap_ptr)) != DDI_FAILURE)) { 164910187SKrishna.Elango@Sun.COM uint8_t esr = PCI_CAP_GET8(config_handle, NULL, 165010187SKrishna.Elango@Sun.COM cap_ptr, PCI_CAP_ID_REGS_OFF); 165110187SKrishna.Elango@Sun.COM if (PCI_CAPSLOT_FIC(esr)) 165210187SKrishna.Elango@Sun.COM fic = 1; 165310187SKrishna.Elango@Sun.COM } 165410187SKrishna.Elango@Sun.COM 165510187SKrishna.Elango@Sun.COM if ((PCI_CAP_LOCATE(config_handle, 165610187SKrishna.Elango@Sun.COM PCI_CAP_XCFG_SPC(PCIE_EXT_CAP_ID_SER), &cap_ptr)) != DDI_FAILURE) { 165710187SKrishna.Elango@Sun.COM /* Serialid can be 0 thru a full 40b number */ 165810187SKrishna.Elango@Sun.COM serialid = PCI_XCAP_GET32(config_handle, NULL, 165910187SKrishna.Elango@Sun.COM cap_ptr, PCIE_SER_SID_UPPER_DW); 166010187SKrishna.Elango@Sun.COM serialid <<= 32; 166110187SKrishna.Elango@Sun.COM serialid |= PCI_XCAP_GET32(config_handle, NULL, 166210187SKrishna.Elango@Sun.COM cap_ptr, PCIE_SER_SID_LOWER_DW); 166310187SKrishna.Elango@Sun.COM } 166410187SKrishna.Elango@Sun.COM 166510187SKrishna.Elango@Sun.COM if (fic) 166610187SKrishna.Elango@Sun.COM (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, pcieb->pcieb_dip, 166710187SKrishna.Elango@Sun.COM "first-in-chassis"); 166810187SKrishna.Elango@Sun.COM if (serialid) 166910187SKrishna.Elango@Sun.COM (void) ddi_prop_update_int64(DDI_DEV_T_NONE, pcieb->pcieb_dip, 167010187SKrishna.Elango@Sun.COM "serialid#", serialid); 167110187SKrishna.Elango@Sun.COM } 167210187SKrishna.Elango@Sun.COM 167310187SKrishna.Elango@Sun.COM static void 167410187SKrishna.Elango@Sun.COM pcieb_create_ranges_prop(dev_info_t *dip, 167510187SKrishna.Elango@Sun.COM ddi_acc_handle_t config_handle) 167610187SKrishna.Elango@Sun.COM { 167710187SKrishna.Elango@Sun.COM uint32_t base, limit; 167810923SEvan.Yan@Sun.COM ppb_ranges_t ranges[PCIEB_RANGE_LEN]; 167910187SKrishna.Elango@Sun.COM uint8_t io_base_lo, io_limit_lo; 168010187SKrishna.Elango@Sun.COM uint16_t io_base_hi, io_limit_hi, mem_base, mem_limit; 168110923SEvan.Yan@Sun.COM int i = 0, rangelen = sizeof (ppb_ranges_t)/sizeof (int); 168210187SKrishna.Elango@Sun.COM 168310187SKrishna.Elango@Sun.COM io_base_lo = pci_config_get8(config_handle, PCI_BCNF_IO_BASE_LOW); 168410187SKrishna.Elango@Sun.COM io_limit_lo = pci_config_get8(config_handle, PCI_BCNF_IO_LIMIT_LOW); 168510187SKrishna.Elango@Sun.COM io_base_hi = pci_config_get16(config_handle, PCI_BCNF_IO_BASE_HI); 168610187SKrishna.Elango@Sun.COM io_limit_hi = pci_config_get16(config_handle, PCI_BCNF_IO_LIMIT_HI); 168710187SKrishna.Elango@Sun.COM mem_base = pci_config_get16(config_handle, PCI_BCNF_MEM_BASE); 168810187SKrishna.Elango@Sun.COM mem_limit = pci_config_get16(config_handle, PCI_BCNF_MEM_LIMIT); 168910187SKrishna.Elango@Sun.COM 169010187SKrishna.Elango@Sun.COM /* 169110187SKrishna.Elango@Sun.COM * Create ranges for IO space 169210187SKrishna.Elango@Sun.COM */ 169310187SKrishna.Elango@Sun.COM ranges[i].size_low = ranges[i].size_high = 0; 169410187SKrishna.Elango@Sun.COM ranges[i].parent_mid = ranges[i].child_mid = ranges[i].parent_high = 0; 169510187SKrishna.Elango@Sun.COM ranges[i].child_high = ranges[i].parent_high |= 169610187SKrishna.Elango@Sun.COM (PCI_REG_REL_M | PCI_ADDR_IO); 169710187SKrishna.Elango@Sun.COM base = PCIEB_16bit_IOADDR(io_base_lo); 169810187SKrishna.Elango@Sun.COM limit = PCIEB_16bit_IOADDR(io_limit_lo); 169910187SKrishna.Elango@Sun.COM 170010187SKrishna.Elango@Sun.COM if ((io_base_lo & 0xf) == PCIEB_32BIT_IO) { 170110187SKrishna.Elango@Sun.COM base = PCIEB_LADDR(base, io_base_hi); 170210187SKrishna.Elango@Sun.COM } 170310187SKrishna.Elango@Sun.COM if ((io_limit_lo & 0xf) == PCIEB_32BIT_IO) { 170410187SKrishna.Elango@Sun.COM limit = PCIEB_LADDR(limit, io_limit_hi); 170510187SKrishna.Elango@Sun.COM } 170610187SKrishna.Elango@Sun.COM 170710187SKrishna.Elango@Sun.COM if ((io_base_lo & PCIEB_32BIT_IO) && (io_limit_hi > 0)) { 170810187SKrishna.Elango@Sun.COM base = PCIEB_LADDR(base, io_base_hi); 170910187SKrishna.Elango@Sun.COM limit = PCIEB_LADDR(limit, io_limit_hi); 171010187SKrishna.Elango@Sun.COM } 171110187SKrishna.Elango@Sun.COM 171210187SKrishna.Elango@Sun.COM /* 171310187SKrishna.Elango@Sun.COM * Create ranges for 32bit memory space 171410187SKrishna.Elango@Sun.COM */ 171510187SKrishna.Elango@Sun.COM base = PCIEB_32bit_MEMADDR(mem_base); 171610187SKrishna.Elango@Sun.COM limit = PCIEB_32bit_MEMADDR(mem_limit); 171710187SKrishna.Elango@Sun.COM ranges[i].size_low = ranges[i].size_high = 0; 171810187SKrishna.Elango@Sun.COM ranges[i].parent_mid = ranges[i].child_mid = ranges[i].parent_high = 0; 171910187SKrishna.Elango@Sun.COM ranges[i].child_high = ranges[i].parent_high |= 172010187SKrishna.Elango@Sun.COM (PCI_REG_REL_M | PCI_ADDR_MEM32); 172110187SKrishna.Elango@Sun.COM ranges[i].child_low = ranges[i].parent_low = base; 172210187SKrishna.Elango@Sun.COM if (limit >= base) { 172310187SKrishna.Elango@Sun.COM ranges[i].size_low = limit - base + PCIEB_MEMGRAIN; 172410187SKrishna.Elango@Sun.COM i++; 172510187SKrishna.Elango@Sun.COM } 172610187SKrishna.Elango@Sun.COM 172710187SKrishna.Elango@Sun.COM if (i) { 172810187SKrishna.Elango@Sun.COM (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "ranges", 172910187SKrishna.Elango@Sun.COM (int *)ranges, i * rangelen); 173010187SKrishna.Elango@Sun.COM } 173110187SKrishna.Elango@Sun.COM } 173210187SKrishna.Elango@Sun.COM 173310187SKrishna.Elango@Sun.COM /* 173410187SKrishna.Elango@Sun.COM * For PCI and PCI-X devices including PCIe2PCI bridge, initialize 173510187SKrishna.Elango@Sun.COM * cache-line-size and latency timer configuration registers. 173610187SKrishna.Elango@Sun.COM */ 173710187SKrishna.Elango@Sun.COM void 173810187SKrishna.Elango@Sun.COM pcieb_set_pci_perf_parameters(dev_info_t *dip, ddi_acc_handle_t cfg_hdl) 173910187SKrishna.Elango@Sun.COM { 174010187SKrishna.Elango@Sun.COM uint_t n; 174110187SKrishna.Elango@Sun.COM 174210187SKrishna.Elango@Sun.COM /* Initialize cache-line-size configuration register if needed */ 174310187SKrishna.Elango@Sun.COM if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 174410187SKrishna.Elango@Sun.COM "cache-line-size", 0) == 0) { 174510187SKrishna.Elango@Sun.COM pci_config_put8(cfg_hdl, PCI_CONF_CACHE_LINESZ, 174610187SKrishna.Elango@Sun.COM PCIEB_CACHE_LINE_SIZE); 174710187SKrishna.Elango@Sun.COM n = pci_config_get8(cfg_hdl, PCI_CONF_CACHE_LINESZ); 174810187SKrishna.Elango@Sun.COM if (n != 0) { 174910187SKrishna.Elango@Sun.COM (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, 175010187SKrishna.Elango@Sun.COM "cache-line-size", n); 175110187SKrishna.Elango@Sun.COM } 175210187SKrishna.Elango@Sun.COM } 175310187SKrishna.Elango@Sun.COM 175410187SKrishna.Elango@Sun.COM /* Initialize latency timer configuration registers if needed */ 175510187SKrishna.Elango@Sun.COM if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 175610187SKrishna.Elango@Sun.COM "latency-timer", 0) == 0) { 175710187SKrishna.Elango@Sun.COM uchar_t min_gnt, latency_timer; 175810187SKrishna.Elango@Sun.COM uchar_t header_type; 175910187SKrishna.Elango@Sun.COM 176010187SKrishna.Elango@Sun.COM /* Determine the configuration header type */ 176110187SKrishna.Elango@Sun.COM header_type = pci_config_get8(cfg_hdl, PCI_CONF_HEADER); 176210187SKrishna.Elango@Sun.COM 176310187SKrishna.Elango@Sun.COM if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) { 176410187SKrishna.Elango@Sun.COM latency_timer = PCIEB_LATENCY_TIMER; 176510187SKrishna.Elango@Sun.COM pci_config_put8(cfg_hdl, PCI_BCNF_LATENCY_TIMER, 176610187SKrishna.Elango@Sun.COM latency_timer); 176710187SKrishna.Elango@Sun.COM } else { 176810187SKrishna.Elango@Sun.COM min_gnt = pci_config_get8(cfg_hdl, PCI_CONF_MIN_G); 176910187SKrishna.Elango@Sun.COM latency_timer = min_gnt * 8; 177010187SKrishna.Elango@Sun.COM } 177110187SKrishna.Elango@Sun.COM 177210187SKrishna.Elango@Sun.COM pci_config_put8(cfg_hdl, PCI_CONF_LATENCY_TIMER, 177310187SKrishna.Elango@Sun.COM latency_timer); 177410187SKrishna.Elango@Sun.COM n = pci_config_get8(cfg_hdl, PCI_CONF_LATENCY_TIMER); 177510187SKrishna.Elango@Sun.COM if (n != 0) { 177610187SKrishna.Elango@Sun.COM (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, 177710187SKrishna.Elango@Sun.COM "latency-timer", n); 177810187SKrishna.Elango@Sun.COM } 177910187SKrishna.Elango@Sun.COM } 178010187SKrishna.Elango@Sun.COM } 1781