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