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/pcie.h> 4310187SKrishna.Elango@Sun.COM #include <sys/pci_cap.h> 4410187SKrishna.Elango@Sun.COM #include <sys/pcie_impl.h> 4510187SKrishna.Elango@Sun.COM #include <sys/hotplug/pci/pcihp.h> 4610187SKrishna.Elango@Sun.COM #include <sys/hotplug/pci/pciehpc.h> 4710187SKrishna.Elango@Sun.COM #include <sys/hotplug/pci/pcishpc.h> 4810187SKrishna.Elango@Sun.COM #include <sys/open.h> 4910187SKrishna.Elango@Sun.COM #include <sys/stat.h> 5010187SKrishna.Elango@Sun.COM #include <sys/file.h> 5110187SKrishna.Elango@Sun.COM #include <sys/promif.h> /* prom_printf */ 5210187SKrishna.Elango@Sun.COM #include <sys/disp.h> 5310187SKrishna.Elango@Sun.COM #include <sys/pcie_pwr.h> 5410187SKrishna.Elango@Sun.COM #include "pcieb.h" 5510187SKrishna.Elango@Sun.COM #ifdef PX_PLX 5610187SKrishna.Elango@Sun.COM #include <io/pciex/pcieb_plx.h> 5710187SKrishna.Elango@Sun.COM #endif /* PX_PLX */ 5810187SKrishna.Elango@Sun.COM 5910187SKrishna.Elango@Sun.COM /*LINTLIBRARY*/ 6010187SKrishna.Elango@Sun.COM 6110187SKrishna.Elango@Sun.COM /* panic flag */ 6210187SKrishna.Elango@Sun.COM int pcieb_die = PF_ERR_FATAL_FLAGS; 6310187SKrishna.Elango@Sun.COM 6410187SKrishna.Elango@Sun.COM /* flag to turn on MSI support */ 6510187SKrishna.Elango@Sun.COM int pcieb_enable_msi = 1; 6610187SKrishna.Elango@Sun.COM 6710187SKrishna.Elango@Sun.COM #if defined(DEBUG) 6810187SKrishna.Elango@Sun.COM uint_t pcieb_dbg_print = 0; 6910187SKrishna.Elango@Sun.COM 7010187SKrishna.Elango@Sun.COM static char *pcieb_debug_sym [] = { /* same sequence as pcieb_debug_bit */ 7110187SKrishna.Elango@Sun.COM /* 0 */ "attach", 7210187SKrishna.Elango@Sun.COM /* 1 */ "pwr", 7310187SKrishna.Elango@Sun.COM /* 2 */ "intr" 7410187SKrishna.Elango@Sun.COM }; 7510187SKrishna.Elango@Sun.COM #endif /* DEBUG */ 7610187SKrishna.Elango@Sun.COM 7710187SKrishna.Elango@Sun.COM static int pcieb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *, off_t, 7810187SKrishna.Elango@Sun.COM off_t, caddr_t *); 7910187SKrishna.Elango@Sun.COM static int pcieb_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, 8010187SKrishna.Elango@Sun.COM void *); 8110187SKrishna.Elango@Sun.COM static int pcieb_fm_init(pcieb_devstate_t *pcieb_p); 8210187SKrishna.Elango@Sun.COM static void pcieb_fm_fini(pcieb_devstate_t *pcieb_p); 8310187SKrishna.Elango@Sun.COM static int pcieb_fm_init_child(dev_info_t *dip, dev_info_t *cdip, int cap, 8410187SKrishna.Elango@Sun.COM ddi_iblock_cookie_t *ibc_p); 8510187SKrishna.Elango@Sun.COM static int pcieb_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, 8610187SKrishna.Elango@Sun.COM ddi_dma_attr_t *attr_p, int (*waitfp)(caddr_t), caddr_t arg, 8710187SKrishna.Elango@Sun.COM ddi_dma_handle_t *handlep); 8810187SKrishna.Elango@Sun.COM static int pcieb_dma_mctl(dev_info_t *dip, dev_info_t *rdip, 8910187SKrishna.Elango@Sun.COM ddi_dma_handle_t handle, enum ddi_dma_ctlops cmd, off_t *offp, 9010187SKrishna.Elango@Sun.COM size_t *lenp, caddr_t *objp, uint_t cache_flags); 9110187SKrishna.Elango@Sun.COM static int pcieb_intr_ops(dev_info_t *dip, dev_info_t *rdip, 9210187SKrishna.Elango@Sun.COM ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result); 9310187SKrishna.Elango@Sun.COM 9410187SKrishna.Elango@Sun.COM static struct bus_ops pcieb_bus_ops = { 9510187SKrishna.Elango@Sun.COM BUSO_REV, 9610187SKrishna.Elango@Sun.COM pcieb_bus_map, 9710187SKrishna.Elango@Sun.COM 0, 9810187SKrishna.Elango@Sun.COM 0, 9910187SKrishna.Elango@Sun.COM 0, 10010187SKrishna.Elango@Sun.COM i_ddi_map_fault, 10110187SKrishna.Elango@Sun.COM ddi_dma_map, 10210187SKrishna.Elango@Sun.COM pcieb_dma_allochdl, 10310187SKrishna.Elango@Sun.COM ddi_dma_freehdl, 10410187SKrishna.Elango@Sun.COM ddi_dma_bindhdl, 10510187SKrishna.Elango@Sun.COM ddi_dma_unbindhdl, 10610187SKrishna.Elango@Sun.COM ddi_dma_flush, 10710187SKrishna.Elango@Sun.COM ddi_dma_win, 10810187SKrishna.Elango@Sun.COM pcieb_dma_mctl, 10910187SKrishna.Elango@Sun.COM pcieb_ctlops, 11010187SKrishna.Elango@Sun.COM ddi_bus_prop_op, 11110187SKrishna.Elango@Sun.COM ndi_busop_get_eventcookie, /* (*bus_get_eventcookie)(); */ 11210187SKrishna.Elango@Sun.COM ndi_busop_add_eventcall, /* (*bus_add_eventcall)(); */ 11310187SKrishna.Elango@Sun.COM ndi_busop_remove_eventcall, /* (*bus_remove_eventcall)(); */ 11410187SKrishna.Elango@Sun.COM ndi_post_event, /* (*bus_post_event)(); */ 11510187SKrishna.Elango@Sun.COM NULL, /* (*bus_intr_ctl)(); */ 11610187SKrishna.Elango@Sun.COM NULL, /* (*bus_config)(); */ 11710187SKrishna.Elango@Sun.COM NULL, /* (*bus_unconfig)(); */ 11810187SKrishna.Elango@Sun.COM pcieb_fm_init_child, /* (*bus_fm_init)(); */ 11910187SKrishna.Elango@Sun.COM NULL, /* (*bus_fm_fini)(); */ 12010187SKrishna.Elango@Sun.COM i_ndi_busop_access_enter, /* (*bus_fm_access_enter)(); */ 12110187SKrishna.Elango@Sun.COM i_ndi_busop_access_exit, /* (*bus_fm_access_exit)(); */ 12210187SKrishna.Elango@Sun.COM pcie_bus_power, /* (*bus_power)(); */ 12310187SKrishna.Elango@Sun.COM pcieb_intr_ops /* (*bus_intr_op)(); */ 12410187SKrishna.Elango@Sun.COM }; 12510187SKrishna.Elango@Sun.COM 12610187SKrishna.Elango@Sun.COM static int pcieb_open(dev_t *, int, int, cred_t *); 12710187SKrishna.Elango@Sun.COM static int pcieb_close(dev_t, int, int, cred_t *); 12810187SKrishna.Elango@Sun.COM static int pcieb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 12910187SKrishna.Elango@Sun.COM static int pcieb_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *, 13010187SKrishna.Elango@Sun.COM caddr_t, int *); 13110187SKrishna.Elango@Sun.COM static int pcieb_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 13210187SKrishna.Elango@Sun.COM static uint_t pcieb_intr_handler(caddr_t arg1, caddr_t arg2); 13310187SKrishna.Elango@Sun.COM 13410187SKrishna.Elango@Sun.COM /* PM related functions */ 13510187SKrishna.Elango@Sun.COM static int pcieb_pwr_setup(dev_info_t *dip); 13610187SKrishna.Elango@Sun.COM static int pcieb_pwr_init_and_raise(dev_info_t *dip, pcie_pwr_t *pwr_p); 13710187SKrishna.Elango@Sun.COM static void pcieb_pwr_teardown(dev_info_t *dip); 13810187SKrishna.Elango@Sun.COM static int pcieb_pwr_disable(dev_info_t *dip); 13910187SKrishna.Elango@Sun.COM 14010187SKrishna.Elango@Sun.COM /* Hotplug related functions */ 14110187SKrishna.Elango@Sun.COM static int pcieb_pciehpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle); 14210187SKrishna.Elango@Sun.COM static int pcieb_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle); 14310187SKrishna.Elango@Sun.COM static int pcieb_init_hotplug(pcieb_devstate_t *pcieb); 14410187SKrishna.Elango@Sun.COM static void pcieb_id_props(pcieb_devstate_t *pcieb); 14510187SKrishna.Elango@Sun.COM 14610187SKrishna.Elango@Sun.COM /* 14710187SKrishna.Elango@Sun.COM * soft state pointer 14810187SKrishna.Elango@Sun.COM */ 14910187SKrishna.Elango@Sun.COM void *pcieb_state; 15010187SKrishna.Elango@Sun.COM 15110187SKrishna.Elango@Sun.COM static struct cb_ops pcieb_cb_ops = { 15210187SKrishna.Elango@Sun.COM pcieb_open, /* open */ 15310187SKrishna.Elango@Sun.COM pcieb_close, /* close */ 15410187SKrishna.Elango@Sun.COM nodev, /* strategy */ 15510187SKrishna.Elango@Sun.COM nodev, /* print */ 15610187SKrishna.Elango@Sun.COM nodev, /* dump */ 15710187SKrishna.Elango@Sun.COM nodev, /* read */ 15810187SKrishna.Elango@Sun.COM nodev, /* write */ 15910187SKrishna.Elango@Sun.COM pcieb_ioctl, /* ioctl */ 16010187SKrishna.Elango@Sun.COM nodev, /* devmap */ 16110187SKrishna.Elango@Sun.COM nodev, /* mmap */ 16210187SKrishna.Elango@Sun.COM nodev, /* segmap */ 16310187SKrishna.Elango@Sun.COM nochpoll, /* poll */ 16410187SKrishna.Elango@Sun.COM pcieb_prop_op, /* cb_prop_op */ 16510187SKrishna.Elango@Sun.COM NULL, /* streamtab */ 16610187SKrishna.Elango@Sun.COM D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 16710187SKrishna.Elango@Sun.COM CB_REV, /* rev */ 16810187SKrishna.Elango@Sun.COM nodev, /* int (*cb_aread)() */ 16910187SKrishna.Elango@Sun.COM nodev /* int (*cb_awrite)() */ 17010187SKrishna.Elango@Sun.COM }; 17110187SKrishna.Elango@Sun.COM 17210187SKrishna.Elango@Sun.COM static int pcieb_probe(dev_info_t *); 17310187SKrishna.Elango@Sun.COM static int pcieb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); 17410187SKrishna.Elango@Sun.COM static int pcieb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd); 17510187SKrishna.Elango@Sun.COM 17610187SKrishna.Elango@Sun.COM static struct dev_ops pcieb_ops = { 17710187SKrishna.Elango@Sun.COM DEVO_REV, /* devo_rev */ 17810187SKrishna.Elango@Sun.COM 0, /* refcnt */ 17910187SKrishna.Elango@Sun.COM pcieb_info, /* info */ 18010187SKrishna.Elango@Sun.COM nulldev, /* identify */ 18110187SKrishna.Elango@Sun.COM pcieb_probe, /* probe */ 18210187SKrishna.Elango@Sun.COM pcieb_attach, /* attach */ 18310187SKrishna.Elango@Sun.COM pcieb_detach, /* detach */ 18410187SKrishna.Elango@Sun.COM nulldev, /* reset */ 18510187SKrishna.Elango@Sun.COM &pcieb_cb_ops, /* driver operations */ 18610187SKrishna.Elango@Sun.COM &pcieb_bus_ops, /* bus operations */ 18710187SKrishna.Elango@Sun.COM pcie_power, /* power */ 18810187SKrishna.Elango@Sun.COM ddi_quiesce_not_needed, /* quiesce */ 18910187SKrishna.Elango@Sun.COM }; 19010187SKrishna.Elango@Sun.COM 19110187SKrishna.Elango@Sun.COM /* 19210187SKrishna.Elango@Sun.COM * Module linkage information for the kernel. 19310187SKrishna.Elango@Sun.COM */ 19410187SKrishna.Elango@Sun.COM 19510187SKrishna.Elango@Sun.COM static struct modldrv modldrv = { 19610187SKrishna.Elango@Sun.COM &mod_driverops, /* Type of module */ 19710187SKrishna.Elango@Sun.COM "PCIe to PCI nexus driver", 19810187SKrishna.Elango@Sun.COM &pcieb_ops, /* driver ops */ 19910187SKrishna.Elango@Sun.COM }; 20010187SKrishna.Elango@Sun.COM 20110187SKrishna.Elango@Sun.COM static struct modlinkage modlinkage = { 20210187SKrishna.Elango@Sun.COM MODREV_1, 20310187SKrishna.Elango@Sun.COM (void *)&modldrv, 20410187SKrishna.Elango@Sun.COM NULL 20510187SKrishna.Elango@Sun.COM }; 20610187SKrishna.Elango@Sun.COM 20710187SKrishna.Elango@Sun.COM /* 20810187SKrishna.Elango@Sun.COM * forward function declarations: 20910187SKrishna.Elango@Sun.COM */ 21010187SKrishna.Elango@Sun.COM static void pcieb_uninitchild(dev_info_t *); 21110187SKrishna.Elango@Sun.COM static int pcieb_initchild(dev_info_t *child); 21210187SKrishna.Elango@Sun.COM static void pcieb_create_ranges_prop(dev_info_t *, ddi_acc_handle_t); 21310187SKrishna.Elango@Sun.COM static boolean_t pcieb_is_pcie_device_type(dev_info_t *dip); 21410187SKrishna.Elango@Sun.COM 21510187SKrishna.Elango@Sun.COM /* interrupt related declarations */ 21610187SKrishna.Elango@Sun.COM static int pcieb_msi_supported(dev_info_t *); 21710187SKrishna.Elango@Sun.COM static int pcieb_intr_attach(pcieb_devstate_t *pcieb); 21810187SKrishna.Elango@Sun.COM static int pcieb_intr_init(pcieb_devstate_t *pcieb_p, int intr_type); 21910187SKrishna.Elango@Sun.COM static void pcieb_intr_fini(pcieb_devstate_t *pcieb_p); 22010187SKrishna.Elango@Sun.COM 22110187SKrishna.Elango@Sun.COM int 22210187SKrishna.Elango@Sun.COM _init(void) 22310187SKrishna.Elango@Sun.COM { 22410187SKrishna.Elango@Sun.COM int e; 22510187SKrishna.Elango@Sun.COM 22610187SKrishna.Elango@Sun.COM if ((e = ddi_soft_state_init(&pcieb_state, sizeof (pcieb_devstate_t), 22710187SKrishna.Elango@Sun.COM 1)) == 0 && (e = mod_install(&modlinkage)) != 0) 22810187SKrishna.Elango@Sun.COM ddi_soft_state_fini(&pcieb_state); 22910187SKrishna.Elango@Sun.COM return (e); 23010187SKrishna.Elango@Sun.COM } 23110187SKrishna.Elango@Sun.COM 23210187SKrishna.Elango@Sun.COM int 23310187SKrishna.Elango@Sun.COM _fini(void) 23410187SKrishna.Elango@Sun.COM { 23510187SKrishna.Elango@Sun.COM int e; 23610187SKrishna.Elango@Sun.COM 23710187SKrishna.Elango@Sun.COM if ((e = mod_remove(&modlinkage)) == 0) { 23810187SKrishna.Elango@Sun.COM ddi_soft_state_fini(&pcieb_state); 23910187SKrishna.Elango@Sun.COM } 24010187SKrishna.Elango@Sun.COM return (e); 24110187SKrishna.Elango@Sun.COM } 24210187SKrishna.Elango@Sun.COM 24310187SKrishna.Elango@Sun.COM int 24410187SKrishna.Elango@Sun.COM _info(struct modinfo *modinfop) 24510187SKrishna.Elango@Sun.COM { 24610187SKrishna.Elango@Sun.COM return (mod_info(&modlinkage, modinfop)); 24710187SKrishna.Elango@Sun.COM } 24810187SKrishna.Elango@Sun.COM 24910187SKrishna.Elango@Sun.COM /*ARGSUSED*/ 25010187SKrishna.Elango@Sun.COM static int 25110187SKrishna.Elango@Sun.COM pcieb_probe(dev_info_t *devi) 25210187SKrishna.Elango@Sun.COM { 25310187SKrishna.Elango@Sun.COM return (DDI_PROBE_SUCCESS); 25410187SKrishna.Elango@Sun.COM } 25510187SKrishna.Elango@Sun.COM 25610187SKrishna.Elango@Sun.COM static int 25710187SKrishna.Elango@Sun.COM pcieb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 25810187SKrishna.Elango@Sun.COM { 25910187SKrishna.Elango@Sun.COM int instance; 26010187SKrishna.Elango@Sun.COM char device_type[8]; 26110187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb; 26210187SKrishna.Elango@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2UPBUS(devi); 26310187SKrishna.Elango@Sun.COM ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl; 26410187SKrishna.Elango@Sun.COM uint8_t dev_type = bus_p->bus_dev_type; 26510187SKrishna.Elango@Sun.COM 26610187SKrishna.Elango@Sun.COM switch (cmd) { 26710187SKrishna.Elango@Sun.COM case DDI_RESUME: 26810187SKrishna.Elango@Sun.COM (void) pcie_pwr_resume(devi); 26910187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 27010187SKrishna.Elango@Sun.COM 27110187SKrishna.Elango@Sun.COM default: 27210187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 27310187SKrishna.Elango@Sun.COM 27410187SKrishna.Elango@Sun.COM case DDI_ATTACH: 27510187SKrishna.Elango@Sun.COM break; 27610187SKrishna.Elango@Sun.COM } 27710187SKrishna.Elango@Sun.COM 27810187SKrishna.Elango@Sun.COM if (!(PCIE_IS_BDG(bus_p))) { 27910187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, devi, "This is not a switch or" 28010187SKrishna.Elango@Sun.COM " bridge\n"); 28110187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 28210187SKrishna.Elango@Sun.COM } 28310187SKrishna.Elango@Sun.COM 28410187SKrishna.Elango@Sun.COM /* 28510187SKrishna.Elango@Sun.COM * If PCIE_LINKCTL_LINK_DISABLE bit in the PCIe Config 28610187SKrishna.Elango@Sun.COM * Space (PCIe Capability Link Control Register) is set, 28710187SKrishna.Elango@Sun.COM * then do not bind the driver. 28810187SKrishna.Elango@Sun.COM */ 28910187SKrishna.Elango@Sun.COM if (PCIE_CAP_GET(16, bus_p, PCIE_LINKCTL) & PCIE_LINKCTL_LINK_DISABLE) 29010187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 29110187SKrishna.Elango@Sun.COM 29210187SKrishna.Elango@Sun.COM /* 29310187SKrishna.Elango@Sun.COM * Allocate and get soft state structure. 29410187SKrishna.Elango@Sun.COM */ 29510187SKrishna.Elango@Sun.COM instance = ddi_get_instance(devi); 29610187SKrishna.Elango@Sun.COM if (ddi_soft_state_zalloc(pcieb_state, instance) != DDI_SUCCESS) 29710187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 29810187SKrishna.Elango@Sun.COM pcieb = ddi_get_soft_state(pcieb_state, instance); 29910187SKrishna.Elango@Sun.COM pcieb->pcieb_dip = devi; 30010187SKrishna.Elango@Sun.COM pcieb->pcieb_soft_state = PCIEB_SOFT_STATE_CLOSED; 30110187SKrishna.Elango@Sun.COM 30210187SKrishna.Elango@Sun.COM if ((pcieb_fm_init(pcieb)) != DDI_SUCCESS) { 30310187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, devi, "Failed in pcieb_fm_init\n"); 30410187SKrishna.Elango@Sun.COM goto fail; 30510187SKrishna.Elango@Sun.COM } 30610187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_FM; 30710187SKrishna.Elango@Sun.COM 30810187SKrishna.Elango@Sun.COM mutex_init(&pcieb->pcieb_mutex, NULL, MUTEX_DRIVER, NULL); 30910187SKrishna.Elango@Sun.COM mutex_init(&pcieb->pcieb_err_mutex, NULL, MUTEX_DRIVER, 31010187SKrishna.Elango@Sun.COM (void *)pcieb->pcieb_fm_ibc); 31110187SKrishna.Elango@Sun.COM mutex_init(&pcieb->pcieb_peek_poke_mutex, NULL, MUTEX_DRIVER, 31210187SKrishna.Elango@Sun.COM (void *)pcieb->pcieb_fm_ibc); 31310187SKrishna.Elango@Sun.COM 31410187SKrishna.Elango@Sun.COM /* create special properties for device identification */ 31510187SKrishna.Elango@Sun.COM pcieb_id_props(pcieb); 31610187SKrishna.Elango@Sun.COM 31710187SKrishna.Elango@Sun.COM /* 31810187SKrishna.Elango@Sun.COM * Power management setup. This also makes sure that switch/bridge 31910187SKrishna.Elango@Sun.COM * is at D0 during attach. 32010187SKrishna.Elango@Sun.COM */ 32110187SKrishna.Elango@Sun.COM if (pwr_common_setup(devi) != DDI_SUCCESS) { 32210187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, devi, "pwr_common_setup failed\n"); 32310187SKrishna.Elango@Sun.COM goto fail; 32410187SKrishna.Elango@Sun.COM } 32510187SKrishna.Elango@Sun.COM 32610187SKrishna.Elango@Sun.COM if (pcieb_pwr_setup(devi) != DDI_SUCCESS) { 32710187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, devi, "pxb_pwr_setup failed \n"); 32810187SKrishna.Elango@Sun.COM goto fail; 32910187SKrishna.Elango@Sun.COM } 33010187SKrishna.Elango@Sun.COM 33110187SKrishna.Elango@Sun.COM /* 33210187SKrishna.Elango@Sun.COM * Make sure the "device_type" property exists. 33310187SKrishna.Elango@Sun.COM */ 33410187SKrishna.Elango@Sun.COM if (pcieb_is_pcie_device_type(devi)) 33510187SKrishna.Elango@Sun.COM (void) strcpy(device_type, "pciex"); 33610187SKrishna.Elango@Sun.COM else 33710187SKrishna.Elango@Sun.COM (void) strcpy(device_type, "pci"); 33810187SKrishna.Elango@Sun.COM 33910187SKrishna.Elango@Sun.COM (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi, 34010187SKrishna.Elango@Sun.COM "device_type", device_type); 34110187SKrishna.Elango@Sun.COM 34210187SKrishna.Elango@Sun.COM /* 34310187SKrishna.Elango@Sun.COM * Check whether the "ranges" property is present. 34410187SKrishna.Elango@Sun.COM * Otherwise create the ranges property by reading 34510187SKrishna.Elango@Sun.COM * the configuration registers 34610187SKrishna.Elango@Sun.COM */ 34710187SKrishna.Elango@Sun.COM if (ddi_prop_exists(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 34810187SKrishna.Elango@Sun.COM "ranges") == 0) { 34910187SKrishna.Elango@Sun.COM pcieb_create_ranges_prop(devi, config_handle); 35010187SKrishna.Elango@Sun.COM } 35110187SKrishna.Elango@Sun.COM 35210187SKrishna.Elango@Sun.COM if (PCIE_IS_PCI_BDG(bus_p)) 35310187SKrishna.Elango@Sun.COM pcieb_set_pci_perf_parameters(devi, config_handle); 35410187SKrishna.Elango@Sun.COM 35510187SKrishna.Elango@Sun.COM #ifdef PX_PLX 35610187SKrishna.Elango@Sun.COM pcieb_attach_plx_workarounds(pcieb); 35710187SKrishna.Elango@Sun.COM #endif /* PX_PLX */ 35810187SKrishna.Elango@Sun.COM 35910187SKrishna.Elango@Sun.COM /* Initialize hotplug */ 36010187SKrishna.Elango@Sun.COM pcieb->pcieb_hotplug_capable = B_FALSE; 36110187SKrishna.Elango@Sun.COM 36210187SKrishna.Elango@Sun.COM if ((dev_type == PCIE_PCIECAP_DEV_TYPE_DOWN) || 36310187SKrishna.Elango@Sun.COM (dev_type == PCIE_PCIECAP_DEV_TYPE_ROOT) || 36410187SKrishna.Elango@Sun.COM (dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) || 36510187SKrishna.Elango@Sun.COM (dev_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE)) { 36610187SKrishna.Elango@Sun.COM (void) pcieb_init_hotplug(pcieb); 36710187SKrishna.Elango@Sun.COM } 36810187SKrishna.Elango@Sun.COM 36910187SKrishna.Elango@Sun.COM /* 37010187SKrishna.Elango@Sun.COM * Initialize interrupt handlers. Ignore return value. 37110187SKrishna.Elango@Sun.COM */ 37210187SKrishna.Elango@Sun.COM (void) pcieb_intr_attach(pcieb); 37310187SKrishna.Elango@Sun.COM 37410187SKrishna.Elango@Sun.COM if (pcieb->pcieb_hotplug_capable == B_FALSE) { 37510187SKrishna.Elango@Sun.COM /* 37610187SKrishna.Elango@Sun.COM * (for non hotplug bus) this would create ":devctl" minor 37710187SKrishna.Elango@Sun.COM * node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls 37810187SKrishna.Elango@Sun.COM * to this bus. 37910187SKrishna.Elango@Sun.COM */ 38010187SKrishna.Elango@Sun.COM if (ddi_create_minor_node(devi, "devctl", S_IFCHR, 38110187SKrishna.Elango@Sun.COM PCIHP_AP_MINOR_NUM(instance, PCIHP_DEVCTL_MINOR), 38210187SKrishna.Elango@Sun.COM DDI_NT_NEXUS, 0) != DDI_SUCCESS) 38310187SKrishna.Elango@Sun.COM goto fail; 38410187SKrishna.Elango@Sun.COM } 38510187SKrishna.Elango@Sun.COM 38610187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, devi, 38710187SKrishna.Elango@Sun.COM "pcieb_attach: this nexus %s hotplug slots\n", 38810187SKrishna.Elango@Sun.COM pcieb->pcieb_hotplug_capable == B_TRUE ? "has":"has no"); 38910187SKrishna.Elango@Sun.COM 39010187SKrishna.Elango@Sun.COM /* Do any platform specific workarounds needed at this time */ 39110187SKrishna.Elango@Sun.COM pcieb_plat_attach_workaround(devi); 39210187SKrishna.Elango@Sun.COM 39310187SKrishna.Elango@Sun.COM /* 39410187SKrishna.Elango@Sun.COM * If this is a root port, determine and set the max payload size. 39510187SKrishna.Elango@Sun.COM * Since this will involve scanning the fabric, all error enabling 39610187SKrishna.Elango@Sun.COM * and sw workarounds should be in place before doing this. 39710187SKrishna.Elango@Sun.COM */ 39810187SKrishna.Elango@Sun.COM if (PCIE_IS_RP(bus_p)) 39910187SKrishna.Elango@Sun.COM pcie_init_root_port_mps(devi); 40010187SKrishna.Elango@Sun.COM 40110187SKrishna.Elango@Sun.COM ddi_report_dev(devi); 40210187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 40310187SKrishna.Elango@Sun.COM 40410187SKrishna.Elango@Sun.COM fail: 40510187SKrishna.Elango@Sun.COM (void) pcieb_detach(devi, DDI_DETACH); 40610187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 40710187SKrishna.Elango@Sun.COM } 40810187SKrishna.Elango@Sun.COM 40910187SKrishna.Elango@Sun.COM static int 41010187SKrishna.Elango@Sun.COM pcieb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 41110187SKrishna.Elango@Sun.COM { 41210187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb; 41310187SKrishna.Elango@Sun.COM int error = DDI_SUCCESS; 41410187SKrishna.Elango@Sun.COM 41510187SKrishna.Elango@Sun.COM switch (cmd) { 41610187SKrishna.Elango@Sun.COM case DDI_SUSPEND: 41710187SKrishna.Elango@Sun.COM error = pcie_pwr_suspend(devi); 41810187SKrishna.Elango@Sun.COM return (error); 41910187SKrishna.Elango@Sun.COM 42010187SKrishna.Elango@Sun.COM case DDI_DETACH: 42110187SKrishna.Elango@Sun.COM break; 42210187SKrishna.Elango@Sun.COM 42310187SKrishna.Elango@Sun.COM default: 42410187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 42510187SKrishna.Elango@Sun.COM } 42610187SKrishna.Elango@Sun.COM 42710187SKrishna.Elango@Sun.COM pcieb = ddi_get_soft_state(pcieb_state, ddi_get_instance(devi)); 42810187SKrishna.Elango@Sun.COM 42910187SKrishna.Elango@Sun.COM /* remove interrupt handlers */ 43010187SKrishna.Elango@Sun.COM pcieb_intr_fini(pcieb); 43110187SKrishna.Elango@Sun.COM 43210187SKrishna.Elango@Sun.COM if (pcieb->pcieb_hotplug_capable == B_TRUE) { 43310187SKrishna.Elango@Sun.COM if (pcihp_uninit(devi) == DDI_FAILURE) 43410187SKrishna.Elango@Sun.COM error = DDI_FAILURE; 43510187SKrishna.Elango@Sun.COM 43610187SKrishna.Elango@Sun.COM if (pcieb->pcieb_hpc_type == HPC_PCIE) 43710187SKrishna.Elango@Sun.COM (void) pciehpc_uninit(devi); 43810187SKrishna.Elango@Sun.COM else if (pcieb->pcieb_hpc_type == HPC_SHPC) 43910187SKrishna.Elango@Sun.COM (void) pcishpc_uninit(devi); 44010397SShesha.Sreenivasamurthy@Sun.COM 44110397SShesha.Sreenivasamurthy@Sun.COM (void) ndi_prop_remove(DDI_DEV_T_NONE, devi, "hotplug-capable"); 44210187SKrishna.Elango@Sun.COM } else { 44310187SKrishna.Elango@Sun.COM ddi_remove_minor_node(devi, "devctl"); 44410187SKrishna.Elango@Sun.COM } 44510187SKrishna.Elango@Sun.COM 44610187SKrishna.Elango@Sun.COM (void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type"); 44710187SKrishna.Elango@Sun.COM 44810187SKrishna.Elango@Sun.COM (void) ndi_prop_remove(DDI_DEV_T_NONE, pcieb->pcieb_dip, 44910187SKrishna.Elango@Sun.COM "pcie_ce_mask"); 45010187SKrishna.Elango@Sun.COM 45110187SKrishna.Elango@Sun.COM if (pcieb->pcieb_init_flags & PCIEB_INIT_FM) 45210187SKrishna.Elango@Sun.COM pcieb_fm_fini(pcieb); 45310187SKrishna.Elango@Sun.COM 45410187SKrishna.Elango@Sun.COM pcieb_pwr_teardown(devi); 45510187SKrishna.Elango@Sun.COM pwr_common_teardown(devi); 45610187SKrishna.Elango@Sun.COM 45710187SKrishna.Elango@Sun.COM mutex_destroy(&pcieb->pcieb_peek_poke_mutex); 45810187SKrishna.Elango@Sun.COM mutex_destroy(&pcieb->pcieb_err_mutex); 45910187SKrishna.Elango@Sun.COM mutex_destroy(&pcieb->pcieb_mutex); 46010187SKrishna.Elango@Sun.COM 46110187SKrishna.Elango@Sun.COM /* 46210187SKrishna.Elango@Sun.COM * And finally free the per-pci soft state. 46310187SKrishna.Elango@Sun.COM */ 46410187SKrishna.Elango@Sun.COM ddi_soft_state_free(pcieb_state, ddi_get_instance(devi)); 46510187SKrishna.Elango@Sun.COM 46610187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 46710187SKrishna.Elango@Sun.COM } 46810187SKrishna.Elango@Sun.COM 46910187SKrishna.Elango@Sun.COM static int 47010187SKrishna.Elango@Sun.COM pcieb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 47110187SKrishna.Elango@Sun.COM off_t offset, off_t len, caddr_t *vaddrp) 47210187SKrishna.Elango@Sun.COM { 47310187SKrishna.Elango@Sun.COM dev_info_t *pdip; 47410187SKrishna.Elango@Sun.COM 47510187SKrishna.Elango@Sun.COM pdip = (dev_info_t *)DEVI(dip)->devi_parent; 47610187SKrishna.Elango@Sun.COM return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip, rdip, mp, 47710187SKrishna.Elango@Sun.COM offset, len, vaddrp)); 47810187SKrishna.Elango@Sun.COM } 47910187SKrishna.Elango@Sun.COM 48010187SKrishna.Elango@Sun.COM static int 48110187SKrishna.Elango@Sun.COM pcieb_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop, 48210187SKrishna.Elango@Sun.COM void *arg, void *result) 48310187SKrishna.Elango@Sun.COM { 48410187SKrishna.Elango@Sun.COM pci_regspec_t *drv_regp; 48510187SKrishna.Elango@Sun.COM int reglen; 48610187SKrishna.Elango@Sun.COM int rn; 48710187SKrishna.Elango@Sun.COM int totreg; 48810187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, 48910187SKrishna.Elango@Sun.COM ddi_get_instance(dip)); 49010187SKrishna.Elango@Sun.COM struct detachspec *ds; 49110187SKrishna.Elango@Sun.COM struct attachspec *as; 49210187SKrishna.Elango@Sun.COM 49310187SKrishna.Elango@Sun.COM switch (ctlop) { 49410187SKrishna.Elango@Sun.COM case DDI_CTLOPS_REPORTDEV: 49510187SKrishna.Elango@Sun.COM if (rdip == (dev_info_t *)0) 49610187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 49710187SKrishna.Elango@Sun.COM cmn_err(CE_CONT, "?PCIE-device: %s@%s, %s%d\n", 49810187SKrishna.Elango@Sun.COM ddi_node_name(rdip), ddi_get_name_addr(rdip), 49910187SKrishna.Elango@Sun.COM ddi_driver_name(rdip), 50010187SKrishna.Elango@Sun.COM ddi_get_instance(rdip)); 50110187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 50210187SKrishna.Elango@Sun.COM 50310187SKrishna.Elango@Sun.COM case DDI_CTLOPS_INITCHILD: 50410187SKrishna.Elango@Sun.COM return (pcieb_initchild((dev_info_t *)arg)); 50510187SKrishna.Elango@Sun.COM 50610187SKrishna.Elango@Sun.COM case DDI_CTLOPS_UNINITCHILD: 50710187SKrishna.Elango@Sun.COM pcieb_uninitchild((dev_info_t *)arg); 50810187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 50910187SKrishna.Elango@Sun.COM 51010187SKrishna.Elango@Sun.COM case DDI_CTLOPS_SIDDEV: 51110187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 51210187SKrishna.Elango@Sun.COM 51310187SKrishna.Elango@Sun.COM case DDI_CTLOPS_REGSIZE: 51410187SKrishna.Elango@Sun.COM case DDI_CTLOPS_NREGS: 51510187SKrishna.Elango@Sun.COM if (rdip == (dev_info_t *)0) 51610187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 51710187SKrishna.Elango@Sun.COM break; 51810187SKrishna.Elango@Sun.COM 51910187SKrishna.Elango@Sun.COM case DDI_CTLOPS_PEEK: 52010187SKrishna.Elango@Sun.COM case DDI_CTLOPS_POKE: 52110187SKrishna.Elango@Sun.COM return (pcieb_plat_peekpoke(dip, rdip, ctlop, arg, result)); 52210187SKrishna.Elango@Sun.COM case DDI_CTLOPS_ATTACH: 52310187SKrishna.Elango@Sun.COM if (!pcie_is_child(dip, rdip)) 52410187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 52510187SKrishna.Elango@Sun.COM 52610187SKrishna.Elango@Sun.COM as = (struct attachspec *)arg; 52710187SKrishna.Elango@Sun.COM switch (as->when) { 52810187SKrishna.Elango@Sun.COM case DDI_PRE: 52910187SKrishna.Elango@Sun.COM if (as->cmd == DDI_RESUME) { 53010187SKrishna.Elango@Sun.COM pcie_clear_errors(rdip); 53110187SKrishna.Elango@Sun.COM if (pcieb_plat_ctlops(rdip, ctlop, arg) != 53210187SKrishna.Elango@Sun.COM DDI_SUCCESS) 53310187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 53410187SKrishna.Elango@Sun.COM } 53510187SKrishna.Elango@Sun.COM 53610187SKrishna.Elango@Sun.COM if (as->cmd == DDI_ATTACH) 53710187SKrishna.Elango@Sun.COM return (pcie_pm_hold(dip)); 53810187SKrishna.Elango@Sun.COM 53910187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 54010187SKrishna.Elango@Sun.COM 54110187SKrishna.Elango@Sun.COM case DDI_POST: 542*10768SRamesh.Chitrothu@Sun.COM if (as->cmd == DDI_ATTACH && 543*10768SRamesh.Chitrothu@Sun.COM as->result != DDI_SUCCESS) { 544*10768SRamesh.Chitrothu@Sun.COM /* 545*10768SRamesh.Chitrothu@Sun.COM * Attach failed for the child device. The child 546*10768SRamesh.Chitrothu@Sun.COM * driver may have made PM calls before the 547*10768SRamesh.Chitrothu@Sun.COM * attach failed. pcie_pm_remove_child() should 548*10768SRamesh.Chitrothu@Sun.COM * cleanup PM state and holds (if any) 549*10768SRamesh.Chitrothu@Sun.COM * associated with the child device. 550*10768SRamesh.Chitrothu@Sun.COM */ 551*10768SRamesh.Chitrothu@Sun.COM return (pcie_pm_remove_child(dip, rdip)); 552*10768SRamesh.Chitrothu@Sun.COM } 55310187SKrishna.Elango@Sun.COM 55410187SKrishna.Elango@Sun.COM if (as->result == DDI_SUCCESS) { 55510187SKrishna.Elango@Sun.COM pf_init(rdip, (void *)pcieb->pcieb_fm_ibc, 55610187SKrishna.Elango@Sun.COM as->cmd); 55710187SKrishna.Elango@Sun.COM 55810187SKrishna.Elango@Sun.COM (void) pcieb_plat_ctlops(rdip, ctlop, arg); 55910187SKrishna.Elango@Sun.COM } 56010187SKrishna.Elango@Sun.COM 56110187SKrishna.Elango@Sun.COM /* 56210187SKrishna.Elango@Sun.COM * For empty hotplug-capable slots, we should explicitly 56310187SKrishna.Elango@Sun.COM * disable the errors, so that we won't panic upon 56410187SKrishna.Elango@Sun.COM * unsupported hotplug messages. 56510187SKrishna.Elango@Sun.COM */ 56610187SKrishna.Elango@Sun.COM if ((!ddi_prop_exists(DDI_DEV_T_ANY, rdip, 56710187SKrishna.Elango@Sun.COM DDI_PROP_DONTPASS, "hotplug-capable")) || 56810187SKrishna.Elango@Sun.COM ddi_get_child(rdip)) { 56910187SKrishna.Elango@Sun.COM (void) pcie_postattach_child(rdip); 57010187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 57110187SKrishna.Elango@Sun.COM } 57210187SKrishna.Elango@Sun.COM 57310187SKrishna.Elango@Sun.COM pcie_disable_errors(rdip); 57410187SKrishna.Elango@Sun.COM 57510187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 57610187SKrishna.Elango@Sun.COM default: 57710187SKrishna.Elango@Sun.COM break; 57810187SKrishna.Elango@Sun.COM } 57910187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 58010187SKrishna.Elango@Sun.COM 58110187SKrishna.Elango@Sun.COM case DDI_CTLOPS_DETACH: 58210187SKrishna.Elango@Sun.COM if (!pcie_is_child(dip, rdip)) 58310187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 58410187SKrishna.Elango@Sun.COM 58510187SKrishna.Elango@Sun.COM ds = (struct detachspec *)arg; 58610187SKrishna.Elango@Sun.COM switch (ds->when) { 58710187SKrishna.Elango@Sun.COM case DDI_PRE: 58810187SKrishna.Elango@Sun.COM pf_fini(rdip, ds->cmd); 58910187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 59010187SKrishna.Elango@Sun.COM 59110187SKrishna.Elango@Sun.COM case DDI_POST: 59210187SKrishna.Elango@Sun.COM if (pcieb_plat_ctlops(rdip, ctlop, arg) != DDI_SUCCESS) 59310187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 59410187SKrishna.Elango@Sun.COM if (ds->cmd == DDI_DETACH && 59510187SKrishna.Elango@Sun.COM ds->result == DDI_SUCCESS) { 59610187SKrishna.Elango@Sun.COM return (pcie_pm_remove_child(dip, rdip)); 59710187SKrishna.Elango@Sun.COM } 59810187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 59910187SKrishna.Elango@Sun.COM default: 60010187SKrishna.Elango@Sun.COM break; 60110187SKrishna.Elango@Sun.COM } 60210187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 60310187SKrishna.Elango@Sun.COM default: 60410187SKrishna.Elango@Sun.COM return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 60510187SKrishna.Elango@Sun.COM } 60610187SKrishna.Elango@Sun.COM 60710187SKrishna.Elango@Sun.COM *(int *)result = 0; 60810187SKrishna.Elango@Sun.COM if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, 60910187SKrishna.Elango@Sun.COM DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg", (caddr_t)&drv_regp, 61010187SKrishna.Elango@Sun.COM ®len) != DDI_SUCCESS) 61110187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 61210187SKrishna.Elango@Sun.COM 61310187SKrishna.Elango@Sun.COM totreg = reglen / sizeof (pci_regspec_t); 61410187SKrishna.Elango@Sun.COM if (ctlop == DDI_CTLOPS_NREGS) 61510187SKrishna.Elango@Sun.COM *(int *)result = totreg; 61610187SKrishna.Elango@Sun.COM else if (ctlop == DDI_CTLOPS_REGSIZE) { 61710187SKrishna.Elango@Sun.COM rn = *(int *)arg; 61810187SKrishna.Elango@Sun.COM if (rn >= totreg) { 61910187SKrishna.Elango@Sun.COM kmem_free(drv_regp, reglen); 62010187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 62110187SKrishna.Elango@Sun.COM } 62210187SKrishna.Elango@Sun.COM 62310187SKrishna.Elango@Sun.COM *(off_t *)result = drv_regp[rn].pci_size_low | 62410187SKrishna.Elango@Sun.COM ((uint64_t)drv_regp[rn].pci_size_hi << 32); 62510187SKrishna.Elango@Sun.COM } 62610187SKrishna.Elango@Sun.COM 62710187SKrishna.Elango@Sun.COM kmem_free(drv_regp, reglen); 62810187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 62910187SKrishna.Elango@Sun.COM } 63010187SKrishna.Elango@Sun.COM 63110187SKrishna.Elango@Sun.COM /* 63210187SKrishna.Elango@Sun.COM * name_child 63310187SKrishna.Elango@Sun.COM * 63410187SKrishna.Elango@Sun.COM * This function is called from init_child to name a node. It is 63510187SKrishna.Elango@Sun.COM * also passed as a callback for node merging functions. 63610187SKrishna.Elango@Sun.COM * 63710187SKrishna.Elango@Sun.COM * return value: DDI_SUCCESS, DDI_FAILURE 63810187SKrishna.Elango@Sun.COM */ 63910187SKrishna.Elango@Sun.COM static int 64010187SKrishna.Elango@Sun.COM pcieb_name_child(dev_info_t *child, char *name, int namelen) 64110187SKrishna.Elango@Sun.COM { 64210187SKrishna.Elango@Sun.COM pci_regspec_t *pci_rp; 64310187SKrishna.Elango@Sun.COM uint_t slot, func; 64410187SKrishna.Elango@Sun.COM char **unit_addr; 64510187SKrishna.Elango@Sun.COM uint_t n; 64610187SKrishna.Elango@Sun.COM 64710187SKrishna.Elango@Sun.COM /* 64810187SKrishna.Elango@Sun.COM * For .conf nodes, use unit-address property as name 64910187SKrishna.Elango@Sun.COM */ 65010187SKrishna.Elango@Sun.COM if (ndi_dev_is_persistent_node(child) == 0) { 65110187SKrishna.Elango@Sun.COM if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 65210187SKrishna.Elango@Sun.COM DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != 65310187SKrishna.Elango@Sun.COM DDI_PROP_SUCCESS) { 65410187SKrishna.Elango@Sun.COM cmn_err(CE_WARN, 65510187SKrishna.Elango@Sun.COM "cannot find unit-address in %s.conf", 65610187SKrishna.Elango@Sun.COM ddi_driver_name(child)); 65710187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 65810187SKrishna.Elango@Sun.COM } 65910187SKrishna.Elango@Sun.COM if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 66010187SKrishna.Elango@Sun.COM cmn_err(CE_WARN, "unit-address property in %s.conf" 66110187SKrishna.Elango@Sun.COM " not well-formed", ddi_driver_name(child)); 66210187SKrishna.Elango@Sun.COM ddi_prop_free(unit_addr); 66310187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 66410187SKrishna.Elango@Sun.COM } 66510187SKrishna.Elango@Sun.COM (void) snprintf(name, namelen, "%s", *unit_addr); 66610187SKrishna.Elango@Sun.COM ddi_prop_free(unit_addr); 66710187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 66810187SKrishna.Elango@Sun.COM } 66910187SKrishna.Elango@Sun.COM 67010187SKrishna.Elango@Sun.COM /* 67110187SKrishna.Elango@Sun.COM * Get the address portion of the node name based on 67210187SKrishna.Elango@Sun.COM * the function and device number. 67310187SKrishna.Elango@Sun.COM */ 67410187SKrishna.Elango@Sun.COM if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 67510187SKrishna.Elango@Sun.COM DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) { 67610187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 67710187SKrishna.Elango@Sun.COM } 67810187SKrishna.Elango@Sun.COM 67910187SKrishna.Elango@Sun.COM /* copy the device identifications */ 68010187SKrishna.Elango@Sun.COM slot = PCI_REG_DEV_G(pci_rp[0].pci_phys_hi); 68110187SKrishna.Elango@Sun.COM func = PCI_REG_FUNC_G(pci_rp[0].pci_phys_hi); 68210187SKrishna.Elango@Sun.COM 68310187SKrishna.Elango@Sun.COM if (func != 0) 68410187SKrishna.Elango@Sun.COM (void) snprintf(name, namelen, "%x,%x", slot, func); 68510187SKrishna.Elango@Sun.COM else 68610187SKrishna.Elango@Sun.COM (void) snprintf(name, namelen, "%x", slot); 68710187SKrishna.Elango@Sun.COM 68810187SKrishna.Elango@Sun.COM ddi_prop_free(pci_rp); 68910187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 69010187SKrishna.Elango@Sun.COM } 69110187SKrishna.Elango@Sun.COM 69210187SKrishna.Elango@Sun.COM static int 69310187SKrishna.Elango@Sun.COM pcieb_initchild(dev_info_t *child) 69410187SKrishna.Elango@Sun.COM { 69510187SKrishna.Elango@Sun.COM char name[MAXNAMELEN]; 69610187SKrishna.Elango@Sun.COM int result = DDI_FAILURE; 69710187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb = 69810187SKrishna.Elango@Sun.COM (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state, 69910187SKrishna.Elango@Sun.COM ddi_get_instance(ddi_get_parent(child))); 70010187SKrishna.Elango@Sun.COM 70110187SKrishna.Elango@Sun.COM /* 70210187SKrishna.Elango@Sun.COM * Name the child 70310187SKrishna.Elango@Sun.COM */ 70410187SKrishna.Elango@Sun.COM if (pcieb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS) { 70510187SKrishna.Elango@Sun.COM result = DDI_FAILURE; 70610187SKrishna.Elango@Sun.COM goto done; 70710187SKrishna.Elango@Sun.COM } 70810187SKrishna.Elango@Sun.COM ddi_set_name_addr(child, name); 70910187SKrishna.Elango@Sun.COM 71010187SKrishna.Elango@Sun.COM /* 71110187SKrishna.Elango@Sun.COM * Pseudo nodes indicate a prototype node with per-instance 71210187SKrishna.Elango@Sun.COM * properties to be merged into the real h/w device node. 71310187SKrishna.Elango@Sun.COM * The interpretation of the unit-address is DD[,F] 71410187SKrishna.Elango@Sun.COM * where DD is the device id and F is the function. 71510187SKrishna.Elango@Sun.COM */ 71610187SKrishna.Elango@Sun.COM if (ndi_dev_is_persistent_node(child) == 0) { 71710187SKrishna.Elango@Sun.COM extern int pci_allow_pseudo_children; 71810187SKrishna.Elango@Sun.COM 71910187SKrishna.Elango@Sun.COM /* 72010187SKrishna.Elango@Sun.COM * Try to merge the properties from this prototype 72110187SKrishna.Elango@Sun.COM * node into real h/w nodes. 72210187SKrishna.Elango@Sun.COM */ 72310187SKrishna.Elango@Sun.COM if (ndi_merge_node(child, pcieb_name_child) != DDI_SUCCESS) { 72410187SKrishna.Elango@Sun.COM /* 72510187SKrishna.Elango@Sun.COM * Merged ok - return failure to remove the node. 72610187SKrishna.Elango@Sun.COM */ 72710187SKrishna.Elango@Sun.COM ddi_set_name_addr(child, NULL); 72810187SKrishna.Elango@Sun.COM result = DDI_FAILURE; 72910187SKrishna.Elango@Sun.COM goto done; 73010187SKrishna.Elango@Sun.COM } 73110187SKrishna.Elango@Sun.COM 73210187SKrishna.Elango@Sun.COM /* workaround for ddivs to run under PCI-E */ 73310187SKrishna.Elango@Sun.COM if (pci_allow_pseudo_children) { 73410187SKrishna.Elango@Sun.COM result = DDI_SUCCESS; 73510187SKrishna.Elango@Sun.COM goto done; 73610187SKrishna.Elango@Sun.COM } 73710187SKrishna.Elango@Sun.COM 73810187SKrishna.Elango@Sun.COM /* 73910187SKrishna.Elango@Sun.COM * The child was not merged into a h/w node, 74010187SKrishna.Elango@Sun.COM * but there's not much we can do with it other 74110187SKrishna.Elango@Sun.COM * than return failure to cause the node to be removed. 74210187SKrishna.Elango@Sun.COM */ 74310187SKrishna.Elango@Sun.COM cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", 74410187SKrishna.Elango@Sun.COM ddi_driver_name(child), ddi_get_name_addr(child), 74510187SKrishna.Elango@Sun.COM ddi_driver_name(child)); 74610187SKrishna.Elango@Sun.COM ddi_set_name_addr(child, NULL); 74710187SKrishna.Elango@Sun.COM result = DDI_NOT_WELL_FORMED; 74810187SKrishna.Elango@Sun.COM goto done; 74910187SKrishna.Elango@Sun.COM } 75010187SKrishna.Elango@Sun.COM 75110187SKrishna.Elango@Sun.COM /* platform specific initchild */ 75210187SKrishna.Elango@Sun.COM pcieb_plat_initchild(child); 75310187SKrishna.Elango@Sun.COM 75410187SKrishna.Elango@Sun.COM if (pcie_pm_hold(pcieb->pcieb_dip) != DDI_SUCCESS) { 75510187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, pcieb->pcieb_dip, 75610187SKrishna.Elango@Sun.COM "INITCHILD: px_pm_hold failed\n"); 75710187SKrishna.Elango@Sun.COM result = DDI_FAILURE; 75810187SKrishna.Elango@Sun.COM goto done; 75910187SKrishna.Elango@Sun.COM } 76010187SKrishna.Elango@Sun.COM /* Any return from here must call pcie_pm_release */ 76110187SKrishna.Elango@Sun.COM 76210187SKrishna.Elango@Sun.COM /* 76310187SKrishna.Elango@Sun.COM * If configuration registers were previously saved by 76410187SKrishna.Elango@Sun.COM * child (before it entered D3), then let the child do the 76510187SKrishna.Elango@Sun.COM * restore to set up the config regs as it'll first need to 76610187SKrishna.Elango@Sun.COM * power the device out of D3. 76710187SKrishna.Elango@Sun.COM */ 76810187SKrishna.Elango@Sun.COM if (ddi_prop_exists(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, 76910187SKrishna.Elango@Sun.COM "config-regs-saved-by-child") == 1) { 77010187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, ddi_get_parent(child), 77110187SKrishna.Elango@Sun.COM "INITCHILD: config regs to be restored by child" 77210187SKrishna.Elango@Sun.COM " for %s@%s\n", ddi_node_name(child), 77310187SKrishna.Elango@Sun.COM ddi_get_name_addr(child)); 77410187SKrishna.Elango@Sun.COM 77510187SKrishna.Elango@Sun.COM result = DDI_SUCCESS; 77610187SKrishna.Elango@Sun.COM goto cleanup; 77710187SKrishna.Elango@Sun.COM } 77810187SKrishna.Elango@Sun.COM 77910187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, ddi_get_parent(child), 78010187SKrishna.Elango@Sun.COM "INITCHILD: config regs setup for %s@%s\n", 78110187SKrishna.Elango@Sun.COM ddi_node_name(child), ddi_get_name_addr(child)); 78210187SKrishna.Elango@Sun.COM 78310187SKrishna.Elango@Sun.COM if (!pcie_init_bus(child) || pcie_initchild(child) != DDI_SUCCESS) { 78410187SKrishna.Elango@Sun.COM result = DDI_FAILURE; 78510187SKrishna.Elango@Sun.COM goto cleanup; 78610187SKrishna.Elango@Sun.COM } 78710187SKrishna.Elango@Sun.COM 78810187SKrishna.Elango@Sun.COM #ifdef PX_PLX 78910187SKrishna.Elango@Sun.COM if (pcieb_init_plx_workarounds(pcieb, child) == DDI_FAILURE) { 79010187SKrishna.Elango@Sun.COM result = DDI_FAILURE; 79110187SKrishna.Elango@Sun.COM goto cleanup; 79210187SKrishna.Elango@Sun.COM } 79310187SKrishna.Elango@Sun.COM #endif /* PX_PLX */ 79410187SKrishna.Elango@Sun.COM 79510187SKrishna.Elango@Sun.COM result = DDI_SUCCESS; 79610187SKrishna.Elango@Sun.COM cleanup: 79710187SKrishna.Elango@Sun.COM pcie_pm_release(pcieb->pcieb_dip); 79810187SKrishna.Elango@Sun.COM done: 79910187SKrishna.Elango@Sun.COM return (result); 80010187SKrishna.Elango@Sun.COM } 80110187SKrishna.Elango@Sun.COM 80210187SKrishna.Elango@Sun.COM static void 80310187SKrishna.Elango@Sun.COM pcieb_uninitchild(dev_info_t *dip) 80410187SKrishna.Elango@Sun.COM { 80510187SKrishna.Elango@Sun.COM 80610187SKrishna.Elango@Sun.COM pcie_uninitchild(dip); 80710187SKrishna.Elango@Sun.COM 80810187SKrishna.Elango@Sun.COM pcieb_plat_uninitchild(dip); 80910187SKrishna.Elango@Sun.COM 81010187SKrishna.Elango@Sun.COM ddi_set_name_addr(dip, NULL); 81110187SKrishna.Elango@Sun.COM 81210187SKrishna.Elango@Sun.COM /* 81310187SKrishna.Elango@Sun.COM * Strip the node to properly convert it back to prototype form 81410187SKrishna.Elango@Sun.COM */ 81510187SKrishna.Elango@Sun.COM ddi_remove_minor_node(dip, NULL); 81610187SKrishna.Elango@Sun.COM 81710187SKrishna.Elango@Sun.COM ddi_prop_remove_all(dip); 81810187SKrishna.Elango@Sun.COM } 81910187SKrishna.Elango@Sun.COM 82010187SKrishna.Elango@Sun.COM static boolean_t 82110187SKrishna.Elango@Sun.COM pcieb_is_pcie_device_type(dev_info_t *dip) 82210187SKrishna.Elango@Sun.COM { 82310187SKrishna.Elango@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 82410187SKrishna.Elango@Sun.COM 82510187SKrishna.Elango@Sun.COM if (PCIE_IS_SW(bus_p) || PCIE_IS_RP(bus_p) || PCIE_IS_PCI2PCIE(bus_p)) 82610187SKrishna.Elango@Sun.COM return (B_TRUE); 82710187SKrishna.Elango@Sun.COM 82810187SKrishna.Elango@Sun.COM return (B_FALSE); 82910187SKrishna.Elango@Sun.COM } 83010187SKrishna.Elango@Sun.COM 83110187SKrishna.Elango@Sun.COM static int 83210187SKrishna.Elango@Sun.COM pcieb_intr_attach(pcieb_devstate_t *pcieb) 83310187SKrishna.Elango@Sun.COM { 83410187SKrishna.Elango@Sun.COM int intr_types; 83510187SKrishna.Elango@Sun.COM dev_info_t *dip = pcieb->pcieb_dip; 83610187SKrishna.Elango@Sun.COM 83710187SKrishna.Elango@Sun.COM /* Allow platform specific code to do any initialization first */ 83810187SKrishna.Elango@Sun.COM pcieb_plat_intr_attach(pcieb); 83910187SKrishna.Elango@Sun.COM 84010187SKrishna.Elango@Sun.COM /* 84110187SKrishna.Elango@Sun.COM * Initialize interrupt handlers. 84210187SKrishna.Elango@Sun.COM * If both MSI and FIXED are supported, try to attach MSI first. 84310187SKrishna.Elango@Sun.COM * If MSI fails for any reason, then try FIXED, but only allow one 84410187SKrishna.Elango@Sun.COM * type to be attached. 84510187SKrishna.Elango@Sun.COM */ 84610187SKrishna.Elango@Sun.COM if (ddi_intr_get_supported_types(dip, &intr_types) != DDI_SUCCESS) { 84710187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_get_supported_types" 84810187SKrishna.Elango@Sun.COM " failed\n"); 84910187SKrishna.Elango@Sun.COM goto FAIL; 85010187SKrishna.Elango@Sun.COM } 85110187SKrishna.Elango@Sun.COM 85210187SKrishna.Elango@Sun.COM if ((intr_types & DDI_INTR_TYPE_MSI) && 85310187SKrishna.Elango@Sun.COM (pcieb_msi_supported(dip) == DDI_SUCCESS)) { 85410187SKrishna.Elango@Sun.COM if (pcieb_intr_init(pcieb, DDI_INTR_TYPE_MSI) == DDI_SUCCESS) 85510187SKrishna.Elango@Sun.COM intr_types = DDI_INTR_TYPE_MSI; 85610187SKrishna.Elango@Sun.COM else { 85710187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "Unable to attach MSI" 85810187SKrishna.Elango@Sun.COM " handler\n"); 85910187SKrishna.Elango@Sun.COM } 86010187SKrishna.Elango@Sun.COM } 86110187SKrishna.Elango@Sun.COM 86210187SKrishna.Elango@Sun.COM if (intr_types != DDI_INTR_TYPE_MSI) { 86310187SKrishna.Elango@Sun.COM /* 86410187SKrishna.Elango@Sun.COM * MSIs are not supported or MSI initialization failed. For Root 86510187SKrishna.Elango@Sun.COM * Ports mark this so error handling might try to fallback to 86610187SKrishna.Elango@Sun.COM * some other mechanism if available (machinecheck etc.). 86710187SKrishna.Elango@Sun.COM */ 86810187SKrishna.Elango@Sun.COM if (PCIE_IS_RP(PCIE_DIP2UPBUS(dip))) 86910187SKrishna.Elango@Sun.COM pcieb->pcieb_no_aer_msi = B_TRUE; 87010187SKrishna.Elango@Sun.COM } 87110187SKrishna.Elango@Sun.COM 87210187SKrishna.Elango@Sun.COM if (intr_types & DDI_INTR_TYPE_FIXED) { 87310187SKrishna.Elango@Sun.COM if (pcieb_intr_init(pcieb, DDI_INTR_TYPE_FIXED) != 87410187SKrishna.Elango@Sun.COM DDI_SUCCESS) { 87510187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, 87610187SKrishna.Elango@Sun.COM "Unable to attach INTx handler\n"); 87710187SKrishna.Elango@Sun.COM goto FAIL; 87810187SKrishna.Elango@Sun.COM } 87910187SKrishna.Elango@Sun.COM } 88010187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 88110187SKrishna.Elango@Sun.COM 88210187SKrishna.Elango@Sun.COM FAIL: 88310187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 88410187SKrishna.Elango@Sun.COM } 88510187SKrishna.Elango@Sun.COM 88610187SKrishna.Elango@Sun.COM /* 88710187SKrishna.Elango@Sun.COM * This function initializes internally generated interrupts only. 88810187SKrishna.Elango@Sun.COM * It does not affect any interrupts generated by downstream devices 88910187SKrishna.Elango@Sun.COM * or the forwarding of them. 89010187SKrishna.Elango@Sun.COM * 89110187SKrishna.Elango@Sun.COM * Enable Device Specific Interrupts or Hotplug features here. 89210187SKrishna.Elango@Sun.COM * Enabling features may change how many interrupts are requested 89310187SKrishna.Elango@Sun.COM * by the device. If features are not enabled first, the 89410187SKrishna.Elango@Sun.COM * device might not ask for any interrupts. 89510187SKrishna.Elango@Sun.COM */ 89610397SShesha.Sreenivasamurthy@Sun.COM 89710187SKrishna.Elango@Sun.COM static int 89810187SKrishna.Elango@Sun.COM pcieb_intr_init(pcieb_devstate_t *pcieb, int intr_type) 89910187SKrishna.Elango@Sun.COM { 90010187SKrishna.Elango@Sun.COM dev_info_t *dip = pcieb->pcieb_dip; 90110187SKrishna.Elango@Sun.COM int nintrs, request, count, x; 90210187SKrishna.Elango@Sun.COM int intr_cap = 0; 90310187SKrishna.Elango@Sun.COM int inum = 0; 90410187SKrishna.Elango@Sun.COM int ret, hp_msi_off; 90510187SKrishna.Elango@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip); 90610187SKrishna.Elango@Sun.COM uint16_t vendorid = bus_p->bus_dev_ven_id & 0xFFFF; 90710187SKrishna.Elango@Sun.COM boolean_t is_hp = B_FALSE; 90810187SKrishna.Elango@Sun.COM boolean_t is_pme = B_FALSE; 90910187SKrishna.Elango@Sun.COM 91010187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "pcieb_intr_init: Attaching %s handler\n", 91110187SKrishna.Elango@Sun.COM (intr_type == DDI_INTR_TYPE_MSI) ? "MSI" : "INTx"); 91210187SKrishna.Elango@Sun.COM 91310187SKrishna.Elango@Sun.COM request = 0; 91410187SKrishna.Elango@Sun.COM if (pcieb->pcieb_hotplug_capable) { 91510187SKrishna.Elango@Sun.COM request++; 91610187SKrishna.Elango@Sun.COM is_hp = B_TRUE; 91710187SKrishna.Elango@Sun.COM } 91810187SKrishna.Elango@Sun.COM 91910187SKrishna.Elango@Sun.COM /* 92010187SKrishna.Elango@Sun.COM * Hotplug and PME share the same MSI vector. If hotplug is not 92110187SKrishna.Elango@Sun.COM * supported check if MSI is needed for PME. 92210187SKrishna.Elango@Sun.COM */ 92310187SKrishna.Elango@Sun.COM if ((intr_type == DDI_INTR_TYPE_MSI) && PCIE_IS_RP(bus_p) && 92410187SKrishna.Elango@Sun.COM (vendorid == NVIDIA_VENDOR_ID)) { 92510187SKrishna.Elango@Sun.COM is_pme = B_TRUE; 92610187SKrishna.Elango@Sun.COM if (!is_hp) 92710187SKrishna.Elango@Sun.COM request++; 92810187SKrishna.Elango@Sun.COM } 92910187SKrishna.Elango@Sun.COM 93010187SKrishna.Elango@Sun.COM /* 93110187SKrishna.Elango@Sun.COM * Setup MSI if this device is a Rootport and has AER. Currently no 93210187SKrishna.Elango@Sun.COM * SPARC Root Port supports fabric errors being reported through it. 93310187SKrishna.Elango@Sun.COM */ 93410187SKrishna.Elango@Sun.COM if (intr_type == DDI_INTR_TYPE_MSI) { 93510187SKrishna.Elango@Sun.COM if (PCIE_IS_RP(bus_p) && PCIE_HAS_AER(bus_p)) 93610187SKrishna.Elango@Sun.COM request++; 93710187SKrishna.Elango@Sun.COM } 93810187SKrishna.Elango@Sun.COM 93910187SKrishna.Elango@Sun.COM if (request == 0) 94010187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 94110187SKrishna.Elango@Sun.COM 94210187SKrishna.Elango@Sun.COM /* 94310187SKrishna.Elango@Sun.COM * Get number of supported interrupts. 94410187SKrishna.Elango@Sun.COM * 94510187SKrishna.Elango@Sun.COM * Several Bridges/Switches will not have this property set, resulting 94610187SKrishna.Elango@Sun.COM * in a FAILURE, if the device is not configured in a way that 94710187SKrishna.Elango@Sun.COM * interrupts are needed. (eg. hotplugging) 94810187SKrishna.Elango@Sun.COM */ 94910187SKrishna.Elango@Sun.COM ret = ddi_intr_get_nintrs(dip, intr_type, &nintrs); 95010187SKrishna.Elango@Sun.COM if ((ret != DDI_SUCCESS) || (nintrs == 0)) { 95110187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_get_nintrs ret:%d" 95210187SKrishna.Elango@Sun.COM " req:%d\n", ret, nintrs); 95310187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 95410187SKrishna.Elango@Sun.COM } 95510187SKrishna.Elango@Sun.COM 95610187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "bdf 0x%x: ddi_intr_get_nintrs: nintrs %d", 95710187SKrishna.Elango@Sun.COM " request %d\n", bus_p->bus_bdf, nintrs, request); 95810187SKrishna.Elango@Sun.COM 95910187SKrishna.Elango@Sun.COM if (request > nintrs) 96010187SKrishna.Elango@Sun.COM request = nintrs; 96110187SKrishna.Elango@Sun.COM 96210187SKrishna.Elango@Sun.COM /* Allocate an array of interrupt handlers */ 96310187SKrishna.Elango@Sun.COM pcieb->pcieb_htable_size = sizeof (ddi_intr_handle_t) * request; 96410187SKrishna.Elango@Sun.COM pcieb->pcieb_htable = kmem_zalloc(pcieb->pcieb_htable_size, 96510187SKrishna.Elango@Sun.COM KM_SLEEP); 96610187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_HTABLE; 96710187SKrishna.Elango@Sun.COM 96810187SKrishna.Elango@Sun.COM ret = ddi_intr_alloc(dip, pcieb->pcieb_htable, intr_type, inum, 96910187SKrishna.Elango@Sun.COM request, &count, DDI_INTR_ALLOC_NORMAL); 97010187SKrishna.Elango@Sun.COM if ((ret != DDI_SUCCESS) || (count == 0)) { 97110187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_alloc() ret: %d ask: %d" 97210187SKrishna.Elango@Sun.COM " actual: %d\n", ret, request, count); 97310187SKrishna.Elango@Sun.COM goto FAIL; 97410187SKrishna.Elango@Sun.COM } 97510187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_ALLOC; 97610187SKrishna.Elango@Sun.COM 97710187SKrishna.Elango@Sun.COM /* Save the actual number of interrupts allocated */ 97810187SKrishna.Elango@Sun.COM pcieb->pcieb_intr_count = count; 97910187SKrishna.Elango@Sun.COM if (count < request) { 98010187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "bdf 0%x: Requested Intr: %d" 98110187SKrishna.Elango@Sun.COM " Received: %d\n", bus_p->bus_bdf, request, count); 98210187SKrishna.Elango@Sun.COM } 98310187SKrishna.Elango@Sun.COM 98410187SKrishna.Elango@Sun.COM /* 98510187SKrishna.Elango@Sun.COM * NVidia (MCP55 and other) chipsets have a errata that if the number 98610187SKrishna.Elango@Sun.COM * of requested MSI intrs is not allocated we have to fall back to INTx. 98710187SKrishna.Elango@Sun.COM */ 98810187SKrishna.Elango@Sun.COM if (intr_type == DDI_INTR_TYPE_MSI) { 98910187SKrishna.Elango@Sun.COM if (PCIE_IS_RP(bus_p) && (vendorid == NVIDIA_VENDOR_ID)) { 99010187SKrishna.Elango@Sun.COM if (request != count) 99110187SKrishna.Elango@Sun.COM goto FAIL; 99210187SKrishna.Elango@Sun.COM } 99310187SKrishna.Elango@Sun.COM } 99410187SKrishna.Elango@Sun.COM 99510187SKrishna.Elango@Sun.COM /* Get interrupt priority */ 99610187SKrishna.Elango@Sun.COM ret = ddi_intr_get_pri(pcieb->pcieb_htable[0], 99710187SKrishna.Elango@Sun.COM &pcieb->pcieb_intr_priority); 99810187SKrishna.Elango@Sun.COM if (ret != DDI_SUCCESS) { 99910187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_get_pri() ret: %d\n", 100010187SKrishna.Elango@Sun.COM ret); 100110187SKrishna.Elango@Sun.COM goto FAIL; 100210187SKrishna.Elango@Sun.COM } 100310187SKrishna.Elango@Sun.COM 100410187SKrishna.Elango@Sun.COM if (pcieb->pcieb_intr_priority >= LOCK_LEVEL) { 100510187SKrishna.Elango@Sun.COM pcieb->pcieb_intr_priority = LOCK_LEVEL - 1; 100610187SKrishna.Elango@Sun.COM ret = ddi_intr_set_pri(pcieb->pcieb_htable[0], 100710187SKrishna.Elango@Sun.COM pcieb->pcieb_intr_priority); 100810187SKrishna.Elango@Sun.COM if (ret != DDI_SUCCESS) { 100910187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "ddi_intr_set_pri() ret:" 101010187SKrishna.Elango@Sun.COM " %d\n", ret); 101110187SKrishna.Elango@Sun.COM 101210187SKrishna.Elango@Sun.COM goto FAIL; 101310187SKrishna.Elango@Sun.COM } 101410187SKrishna.Elango@Sun.COM } 101510187SKrishna.Elango@Sun.COM 101610187SKrishna.Elango@Sun.COM mutex_init(&pcieb->pcieb_intr_mutex, NULL, MUTEX_DRIVER, NULL); 101710187SKrishna.Elango@Sun.COM 101810187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_MUTEX; 101910187SKrishna.Elango@Sun.COM 102010187SKrishna.Elango@Sun.COM for (count = 0; count < pcieb->pcieb_intr_count; count++) { 102110187SKrishna.Elango@Sun.COM ret = ddi_intr_add_handler(pcieb->pcieb_htable[count], 102210187SKrishna.Elango@Sun.COM pcieb_intr_handler, (caddr_t)pcieb, 102310187SKrishna.Elango@Sun.COM (caddr_t)(uintptr_t)(inum + count)); 102410187SKrishna.Elango@Sun.COM 102510187SKrishna.Elango@Sun.COM if (ret != DDI_SUCCESS) { 102610187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "Cannot add " 102710187SKrishna.Elango@Sun.COM "interrupt(%d)\n", ret); 102810187SKrishna.Elango@Sun.COM break; 102910187SKrishna.Elango@Sun.COM } 103010187SKrishna.Elango@Sun.COM } 103110187SKrishna.Elango@Sun.COM 103210187SKrishna.Elango@Sun.COM /* If unsucessful, remove the added handlers */ 103310187SKrishna.Elango@Sun.COM if (ret != DDI_SUCCESS) { 103410187SKrishna.Elango@Sun.COM for (x = 0; x < count; x++) { 103510187SKrishna.Elango@Sun.COM (void) ddi_intr_remove_handler(pcieb->pcieb_htable[x]); 103610187SKrishna.Elango@Sun.COM } 103710187SKrishna.Elango@Sun.COM goto FAIL; 103810187SKrishna.Elango@Sun.COM } 103910187SKrishna.Elango@Sun.COM 104010187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_HANDLER; 104110187SKrishna.Elango@Sun.COM 104210187SKrishna.Elango@Sun.COM (void) ddi_intr_get_cap(pcieb->pcieb_htable[0], &intr_cap); 104310187SKrishna.Elango@Sun.COM 104410187SKrishna.Elango@Sun.COM /* 104510187SKrishna.Elango@Sun.COM * Get this intr lock because we are not quite ready to handle 104610187SKrishna.Elango@Sun.COM * interrupts immediately after enabling it. The MSI multi register 104710187SKrishna.Elango@Sun.COM * gets programmed in ddi_intr_enable after which we need to get the 104810187SKrishna.Elango@Sun.COM * MSI offsets for Hotplug/AER. 104910187SKrishna.Elango@Sun.COM */ 105010187SKrishna.Elango@Sun.COM mutex_enter(&pcieb->pcieb_intr_mutex); 105110187SKrishna.Elango@Sun.COM 105210187SKrishna.Elango@Sun.COM if (intr_cap & DDI_INTR_FLAG_BLOCK) { 105310187SKrishna.Elango@Sun.COM (void) ddi_intr_block_enable(pcieb->pcieb_htable, 105410187SKrishna.Elango@Sun.COM pcieb->pcieb_intr_count); 105510187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_BLOCK; 105610187SKrishna.Elango@Sun.COM } else { 105710187SKrishna.Elango@Sun.COM for (count = 0; count < pcieb->pcieb_intr_count; count++) { 105810187SKrishna.Elango@Sun.COM (void) ddi_intr_enable(pcieb->pcieb_htable[count]); 105910187SKrishna.Elango@Sun.COM } 106010187SKrishna.Elango@Sun.COM } 106110187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags |= PCIEB_INIT_ENABLE; 106210187SKrishna.Elango@Sun.COM 106310187SKrishna.Elango@Sun.COM /* Save the interrupt type */ 106410187SKrishna.Elango@Sun.COM pcieb->pcieb_intr_type = intr_type; 106510187SKrishna.Elango@Sun.COM 106610187SKrishna.Elango@Sun.COM /* Get the MSI offset for hotplug/PME from the PCIe cap reg */ 106710187SKrishna.Elango@Sun.COM if (intr_type == DDI_INTR_TYPE_MSI) { 106810187SKrishna.Elango@Sun.COM hp_msi_off = PCI_CAP_GET16(bus_p->bus_cfg_hdl, NULL, 106910187SKrishna.Elango@Sun.COM bus_p->bus_pcie_off, PCIE_PCIECAP) & 107010187SKrishna.Elango@Sun.COM PCIE_PCIECAP_INT_MSG_NUM; 107110187SKrishna.Elango@Sun.COM 107210187SKrishna.Elango@Sun.COM if (hp_msi_off >= count) { 107310187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "MSI number %d in PCIe " 107410187SKrishna.Elango@Sun.COM "cap > max allocated %d\n", hp_msi_off, count); 107510187SKrishna.Elango@Sun.COM mutex_exit(&pcieb->pcieb_intr_mutex); 107610187SKrishna.Elango@Sun.COM goto FAIL; 107710187SKrishna.Elango@Sun.COM } 107810187SKrishna.Elango@Sun.COM 107910187SKrishna.Elango@Sun.COM if (is_hp) 108010187SKrishna.Elango@Sun.COM pcieb->pcieb_isr_tab[hp_msi_off] |= PCIEB_INTR_SRC_HP; 108110187SKrishna.Elango@Sun.COM 108210187SKrishna.Elango@Sun.COM if (is_pme) 108310187SKrishna.Elango@Sun.COM pcieb->pcieb_isr_tab[hp_msi_off] |= PCIEB_INTR_SRC_PME; 108410187SKrishna.Elango@Sun.COM } else { 108510187SKrishna.Elango@Sun.COM /* INTx handles only Hotplug interrupts */ 108610187SKrishna.Elango@Sun.COM if (is_hp) 108710187SKrishna.Elango@Sun.COM pcieb->pcieb_isr_tab[0] |= PCIEB_INTR_SRC_HP; 108810187SKrishna.Elango@Sun.COM } 108910187SKrishna.Elango@Sun.COM 109010187SKrishna.Elango@Sun.COM 109110187SKrishna.Elango@Sun.COM /* 109210187SKrishna.Elango@Sun.COM * Get the MSI offset for errors from the AER Root Error status 109310187SKrishna.Elango@Sun.COM * register. 109410187SKrishna.Elango@Sun.COM */ 109510187SKrishna.Elango@Sun.COM if ((intr_type == DDI_INTR_TYPE_MSI) && PCIE_IS_RP(bus_p)) { 109610187SKrishna.Elango@Sun.COM if (PCIE_HAS_AER(bus_p)) { 109710187SKrishna.Elango@Sun.COM int aer_msi_off; 109810187SKrishna.Elango@Sun.COM aer_msi_off = (PCI_XCAP_GET32(bus_p->bus_cfg_hdl, NULL, 109910187SKrishna.Elango@Sun.COM bus_p->bus_aer_off, PCIE_AER_RE_STS) >> 110010187SKrishna.Elango@Sun.COM PCIE_AER_RE_STS_MSG_NUM_SHIFT) & 110110187SKrishna.Elango@Sun.COM PCIE_AER_RE_STS_MSG_NUM_MASK; 110210187SKrishna.Elango@Sun.COM 110310187SKrishna.Elango@Sun.COM if (aer_msi_off >= count) { 110410187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, dip, "MSI number %d in" 110510187SKrishna.Elango@Sun.COM " AER cap > max allocated %d\n", 110610187SKrishna.Elango@Sun.COM aer_msi_off, count); 110710187SKrishna.Elango@Sun.COM mutex_exit(&pcieb->pcieb_intr_mutex); 110810187SKrishna.Elango@Sun.COM goto FAIL; 110910187SKrishna.Elango@Sun.COM } 111010187SKrishna.Elango@Sun.COM pcieb->pcieb_isr_tab[aer_msi_off] |= PCIEB_INTR_SRC_AER; 111110187SKrishna.Elango@Sun.COM } else { 111210187SKrishna.Elango@Sun.COM /* 111310187SKrishna.Elango@Sun.COM * This RP does not have AER. Fallback to the 111410187SKrishna.Elango@Sun.COM * SERR+Machinecheck approach if available. 111510187SKrishna.Elango@Sun.COM */ 111610187SKrishna.Elango@Sun.COM pcieb->pcieb_no_aer_msi = B_TRUE; 111710187SKrishna.Elango@Sun.COM } 111810187SKrishna.Elango@Sun.COM } 111910187SKrishna.Elango@Sun.COM 112010187SKrishna.Elango@Sun.COM mutex_exit(&pcieb->pcieb_intr_mutex); 112110187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 112210187SKrishna.Elango@Sun.COM 112310187SKrishna.Elango@Sun.COM FAIL: 112410397SShesha.Sreenivasamurthy@Sun.COM pcieb_intr_fini(pcieb); 112510187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 112610187SKrishna.Elango@Sun.COM } 112710187SKrishna.Elango@Sun.COM 112810187SKrishna.Elango@Sun.COM static void 112910187SKrishna.Elango@Sun.COM pcieb_intr_fini(pcieb_devstate_t *pcieb) 113010187SKrishna.Elango@Sun.COM { 113110187SKrishna.Elango@Sun.COM int x; 113210187SKrishna.Elango@Sun.COM int count = pcieb->pcieb_intr_count; 113310187SKrishna.Elango@Sun.COM int flags = pcieb->pcieb_init_flags; 113410187SKrishna.Elango@Sun.COM 113510187SKrishna.Elango@Sun.COM if ((flags & PCIEB_INIT_ENABLE) && 113610187SKrishna.Elango@Sun.COM (flags & PCIEB_INIT_BLOCK)) { 113710187SKrishna.Elango@Sun.COM (void) ddi_intr_block_disable(pcieb->pcieb_htable, count); 113810187SKrishna.Elango@Sun.COM flags &= ~(PCIEB_INIT_ENABLE | 113910187SKrishna.Elango@Sun.COM PCIEB_INIT_BLOCK); 114010187SKrishna.Elango@Sun.COM } 114110187SKrishna.Elango@Sun.COM 114210187SKrishna.Elango@Sun.COM if (flags & PCIEB_INIT_MUTEX) 114310187SKrishna.Elango@Sun.COM mutex_destroy(&pcieb->pcieb_intr_mutex); 114410187SKrishna.Elango@Sun.COM 114510187SKrishna.Elango@Sun.COM for (x = 0; x < count; x++) { 114610187SKrishna.Elango@Sun.COM if (flags & PCIEB_INIT_ENABLE) 114710187SKrishna.Elango@Sun.COM (void) ddi_intr_disable(pcieb->pcieb_htable[x]); 114810187SKrishna.Elango@Sun.COM 114910187SKrishna.Elango@Sun.COM if (flags & PCIEB_INIT_HANDLER) 115010187SKrishna.Elango@Sun.COM (void) ddi_intr_remove_handler(pcieb->pcieb_htable[x]); 115110187SKrishna.Elango@Sun.COM 115210187SKrishna.Elango@Sun.COM if (flags & PCIEB_INIT_ALLOC) 115310187SKrishna.Elango@Sun.COM (void) ddi_intr_free(pcieb->pcieb_htable[x]); 115410187SKrishna.Elango@Sun.COM } 115510187SKrishna.Elango@Sun.COM 115610187SKrishna.Elango@Sun.COM flags &= ~(PCIEB_INIT_ENABLE | PCIEB_INIT_HANDLER | PCIEB_INIT_ALLOC | 115710187SKrishna.Elango@Sun.COM PCIEB_INIT_MUTEX); 115810187SKrishna.Elango@Sun.COM 115910187SKrishna.Elango@Sun.COM if (flags & PCIEB_INIT_HTABLE) 116010187SKrishna.Elango@Sun.COM kmem_free(pcieb->pcieb_htable, pcieb->pcieb_htable_size); 116110187SKrishna.Elango@Sun.COM 116210187SKrishna.Elango@Sun.COM flags &= ~PCIEB_INIT_HTABLE; 116310187SKrishna.Elango@Sun.COM 116410187SKrishna.Elango@Sun.COM pcieb->pcieb_init_flags &= flags; 116510187SKrishna.Elango@Sun.COM } 116610187SKrishna.Elango@Sun.COM 116710187SKrishna.Elango@Sun.COM /* 116810187SKrishna.Elango@Sun.COM * Checks if this device needs MSIs enabled or not. 116910187SKrishna.Elango@Sun.COM */ 117010187SKrishna.Elango@Sun.COM /*ARGSUSED*/ 117110187SKrishna.Elango@Sun.COM static int 117210187SKrishna.Elango@Sun.COM pcieb_msi_supported(dev_info_t *dip) 117310187SKrishna.Elango@Sun.COM { 117410187SKrishna.Elango@Sun.COM return ((pcieb_enable_msi && pcieb_plat_msi_supported(dip)) ? 117510187SKrishna.Elango@Sun.COM DDI_SUCCESS: DDI_FAILURE); 117610187SKrishna.Elango@Sun.COM } 117710187SKrishna.Elango@Sun.COM 117810187SKrishna.Elango@Sun.COM /*ARGSUSED*/ 117910397SShesha.Sreenivasamurthy@Sun.COM static int 118010187SKrishna.Elango@Sun.COM pcieb_fm_init_child(dev_info_t *dip, dev_info_t *tdip, int cap, 118110187SKrishna.Elango@Sun.COM ddi_iblock_cookie_t *ibc) 118210187SKrishna.Elango@Sun.COM { 118310187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb = ddi_get_soft_state(pcieb_state, 118410187SKrishna.Elango@Sun.COM ddi_get_instance(dip)); 118510187SKrishna.Elango@Sun.COM 118610187SKrishna.Elango@Sun.COM ASSERT(ibc != NULL); 118710187SKrishna.Elango@Sun.COM *ibc = pcieb->pcieb_fm_ibc; 118810187SKrishna.Elango@Sun.COM 118910187SKrishna.Elango@Sun.COM return (DEVI(dip)->devi_fmhdl->fh_cap | DDI_FM_ACCCHK_CAPABLE | 119010187SKrishna.Elango@Sun.COM DDI_FM_DMACHK_CAPABLE); 119110187SKrishna.Elango@Sun.COM } 119210187SKrishna.Elango@Sun.COM 119310187SKrishna.Elango@Sun.COM static int 119410187SKrishna.Elango@Sun.COM pcieb_fm_init(pcieb_devstate_t *pcieb_p) 119510187SKrishna.Elango@Sun.COM { 119610187SKrishna.Elango@Sun.COM dev_info_t *dip = pcieb_p->pcieb_dip; 119710187SKrishna.Elango@Sun.COM int fm_cap = DDI_FM_EREPORT_CAPABLE; 119810187SKrishna.Elango@Sun.COM 119910187SKrishna.Elango@Sun.COM /* 120010187SKrishna.Elango@Sun.COM * Request our capability level and get our parents capability 120110187SKrishna.Elango@Sun.COM * and ibc. 120210187SKrishna.Elango@Sun.COM */ 120310187SKrishna.Elango@Sun.COM ddi_fm_init(dip, &fm_cap, &pcieb_p->pcieb_fm_ibc); 120410187SKrishna.Elango@Sun.COM 120510187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 120610187SKrishna.Elango@Sun.COM } 120710187SKrishna.Elango@Sun.COM 120810187SKrishna.Elango@Sun.COM /* 120910187SKrishna.Elango@Sun.COM * Breakdown our FMA resources 121010187SKrishna.Elango@Sun.COM */ 121110187SKrishna.Elango@Sun.COM static void 121210187SKrishna.Elango@Sun.COM pcieb_fm_fini(pcieb_devstate_t *pcieb_p) 121310187SKrishna.Elango@Sun.COM { 121410187SKrishna.Elango@Sun.COM /* 121510187SKrishna.Elango@Sun.COM * Clean up allocated fm structures 121610187SKrishna.Elango@Sun.COM */ 121710187SKrishna.Elango@Sun.COM ddi_fm_fini(pcieb_p->pcieb_dip); 121810187SKrishna.Elango@Sun.COM } 121910187SKrishna.Elango@Sun.COM 122010187SKrishna.Elango@Sun.COM static int 122110187SKrishna.Elango@Sun.COM pcieb_open(dev_t *devp, int flags, int otyp, cred_t *credp) 122210187SKrishna.Elango@Sun.COM { 122310187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb_p; 122410187SKrishna.Elango@Sun.COM minor_t minor = getminor(*devp); 122510187SKrishna.Elango@Sun.COM int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor); 122610187SKrishna.Elango@Sun.COM 122710187SKrishna.Elango@Sun.COM /* 122810187SKrishna.Elango@Sun.COM * Make sure the open is for the right file type. 122910187SKrishna.Elango@Sun.COM */ 123010187SKrishna.Elango@Sun.COM if (otyp != OTYP_CHR) 123110187SKrishna.Elango@Sun.COM return (EINVAL); 123210187SKrishna.Elango@Sun.COM 123310187SKrishna.Elango@Sun.COM /* 123410187SKrishna.Elango@Sun.COM * Get the soft state structure for the device. 123510187SKrishna.Elango@Sun.COM */ 123610187SKrishna.Elango@Sun.COM pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state, 123710187SKrishna.Elango@Sun.COM instance); 123810187SKrishna.Elango@Sun.COM 123910187SKrishna.Elango@Sun.COM if (pcieb_p == NULL) 124010187SKrishna.Elango@Sun.COM return (ENXIO); 124110187SKrishna.Elango@Sun.COM 124210187SKrishna.Elango@Sun.COM if (pcieb_p->pcieb_hotplug_capable == B_TRUE) 124310187SKrishna.Elango@Sun.COM return ((pcihp_get_cb_ops())->cb_open(devp, flags, 124410187SKrishna.Elango@Sun.COM otyp, credp)); 124510187SKrishna.Elango@Sun.COM 124610187SKrishna.Elango@Sun.COM /* 124710187SKrishna.Elango@Sun.COM * Handle the open by tracking the device state. 124810187SKrishna.Elango@Sun.COM */ 124910187SKrishna.Elango@Sun.COM mutex_enter(&pcieb_p->pcieb_mutex); 125010187SKrishna.Elango@Sun.COM if (flags & FEXCL) { 125110187SKrishna.Elango@Sun.COM if (pcieb_p->pcieb_soft_state != PCIEB_SOFT_STATE_CLOSED) { 125210187SKrishna.Elango@Sun.COM mutex_exit(&pcieb_p->pcieb_mutex); 125310187SKrishna.Elango@Sun.COM return (EBUSY); 125410187SKrishna.Elango@Sun.COM } 125510187SKrishna.Elango@Sun.COM pcieb_p->pcieb_soft_state = PCIEB_SOFT_STATE_OPEN_EXCL; 125610187SKrishna.Elango@Sun.COM } else { 125710187SKrishna.Elango@Sun.COM if (pcieb_p->pcieb_soft_state == PCIEB_SOFT_STATE_OPEN_EXCL) { 125810187SKrishna.Elango@Sun.COM mutex_exit(&pcieb_p->pcieb_mutex); 125910187SKrishna.Elango@Sun.COM return (EBUSY); 126010187SKrishna.Elango@Sun.COM } 126110187SKrishna.Elango@Sun.COM pcieb_p->pcieb_soft_state = PCIEB_SOFT_STATE_OPEN; 126210187SKrishna.Elango@Sun.COM } 126310187SKrishna.Elango@Sun.COM mutex_exit(&pcieb_p->pcieb_mutex); 126410187SKrishna.Elango@Sun.COM return (0); 126510187SKrishna.Elango@Sun.COM } 126610187SKrishna.Elango@Sun.COM 126710187SKrishna.Elango@Sun.COM static int 126810187SKrishna.Elango@Sun.COM pcieb_close(dev_t dev, int flags, int otyp, cred_t *credp) 126910187SKrishna.Elango@Sun.COM { 127010187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb_p; 127110187SKrishna.Elango@Sun.COM minor_t minor = getminor(dev); 127210187SKrishna.Elango@Sun.COM int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor); 127310187SKrishna.Elango@Sun.COM 127410187SKrishna.Elango@Sun.COM if (otyp != OTYP_CHR) 127510187SKrishna.Elango@Sun.COM return (EINVAL); 127610187SKrishna.Elango@Sun.COM 127710187SKrishna.Elango@Sun.COM pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state, 127810187SKrishna.Elango@Sun.COM instance); 127910187SKrishna.Elango@Sun.COM 128010187SKrishna.Elango@Sun.COM if (pcieb_p == NULL) 128110187SKrishna.Elango@Sun.COM return (ENXIO); 128210187SKrishna.Elango@Sun.COM 128310187SKrishna.Elango@Sun.COM if (pcieb_p->pcieb_hotplug_capable == B_TRUE) 128410187SKrishna.Elango@Sun.COM return ((pcihp_get_cb_ops())->cb_close(dev, flags, 128510187SKrishna.Elango@Sun.COM otyp, credp)); 128610187SKrishna.Elango@Sun.COM 128710187SKrishna.Elango@Sun.COM mutex_enter(&pcieb_p->pcieb_mutex); 128810187SKrishna.Elango@Sun.COM pcieb_p->pcieb_soft_state = PCIEB_SOFT_STATE_CLOSED; 128910187SKrishna.Elango@Sun.COM mutex_exit(&pcieb_p->pcieb_mutex); 129010187SKrishna.Elango@Sun.COM return (0); 129110187SKrishna.Elango@Sun.COM } 129210187SKrishna.Elango@Sun.COM 129310187SKrishna.Elango@Sun.COM static int 129410187SKrishna.Elango@Sun.COM pcieb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 129510187SKrishna.Elango@Sun.COM int *rvalp) 129610187SKrishna.Elango@Sun.COM { 129710187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb_p; 129810187SKrishna.Elango@Sun.COM dev_info_t *self; 129910187SKrishna.Elango@Sun.COM struct devctl_iocdata *dcp; 130010187SKrishna.Elango@Sun.COM uint_t bus_state; 130110187SKrishna.Elango@Sun.COM int rv = 0; 130210187SKrishna.Elango@Sun.COM minor_t minor = getminor(dev); 130310187SKrishna.Elango@Sun.COM int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor); 130410187SKrishna.Elango@Sun.COM 130510187SKrishna.Elango@Sun.COM pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state, 130610187SKrishna.Elango@Sun.COM instance); 130710187SKrishna.Elango@Sun.COM 130810187SKrishna.Elango@Sun.COM if (pcieb_p == NULL) 130910187SKrishna.Elango@Sun.COM return (ENXIO); 131010187SKrishna.Elango@Sun.COM 131110187SKrishna.Elango@Sun.COM self = pcieb_p->pcieb_dip; 131210187SKrishna.Elango@Sun.COM if (pcieb_p->pcieb_hotplug_capable == B_TRUE) { 131310187SKrishna.Elango@Sun.COM rv = ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, 131410187SKrishna.Elango@Sun.COM arg, mode, credp, rvalp)); 131510187SKrishna.Elango@Sun.COM 131610187SKrishna.Elango@Sun.COM pcieb_plat_ioctl_hotplug(self, rv, cmd); 131710187SKrishna.Elango@Sun.COM return (rv); 131810187SKrishna.Elango@Sun.COM } 131910187SKrishna.Elango@Sun.COM 132010187SKrishna.Elango@Sun.COM /* 132110187SKrishna.Elango@Sun.COM * We can use the generic implementation for these ioctls 132210187SKrishna.Elango@Sun.COM */ 132310187SKrishna.Elango@Sun.COM switch (cmd) { 132410187SKrishna.Elango@Sun.COM case DEVCTL_DEVICE_GETSTATE: 132510187SKrishna.Elango@Sun.COM case DEVCTL_DEVICE_ONLINE: 132610187SKrishna.Elango@Sun.COM case DEVCTL_DEVICE_OFFLINE: 132710187SKrishna.Elango@Sun.COM case DEVCTL_BUS_GETSTATE: 132810187SKrishna.Elango@Sun.COM return (ndi_devctl_ioctl(self, cmd, arg, mode, 0)); 132910187SKrishna.Elango@Sun.COM } 133010187SKrishna.Elango@Sun.COM 133110187SKrishna.Elango@Sun.COM /* 133210187SKrishna.Elango@Sun.COM * read devctl ioctl data 133310187SKrishna.Elango@Sun.COM */ 133410187SKrishna.Elango@Sun.COM if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) 133510187SKrishna.Elango@Sun.COM return (EFAULT); 133610187SKrishna.Elango@Sun.COM 133710187SKrishna.Elango@Sun.COM switch (cmd) { 133810187SKrishna.Elango@Sun.COM 133910187SKrishna.Elango@Sun.COM case DEVCTL_DEVICE_RESET: 134010187SKrishna.Elango@Sun.COM rv = ENOTSUP; 134110187SKrishna.Elango@Sun.COM break; 134210187SKrishna.Elango@Sun.COM 134310187SKrishna.Elango@Sun.COM case DEVCTL_BUS_QUIESCE: 134410187SKrishna.Elango@Sun.COM if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS) 134510187SKrishna.Elango@Sun.COM if (bus_state == BUS_QUIESCED) 134610187SKrishna.Elango@Sun.COM break; 134710187SKrishna.Elango@Sun.COM (void) ndi_set_bus_state(self, BUS_QUIESCED); 134810187SKrishna.Elango@Sun.COM break; 134910187SKrishna.Elango@Sun.COM 135010187SKrishna.Elango@Sun.COM case DEVCTL_BUS_UNQUIESCE: 135110187SKrishna.Elango@Sun.COM if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS) 135210187SKrishna.Elango@Sun.COM if (bus_state == BUS_ACTIVE) 135310187SKrishna.Elango@Sun.COM break; 135410187SKrishna.Elango@Sun.COM (void) ndi_set_bus_state(self, BUS_ACTIVE); 135510187SKrishna.Elango@Sun.COM break; 135610187SKrishna.Elango@Sun.COM 135710187SKrishna.Elango@Sun.COM case DEVCTL_BUS_RESET: 135810187SKrishna.Elango@Sun.COM rv = ENOTSUP; 135910187SKrishna.Elango@Sun.COM break; 136010187SKrishna.Elango@Sun.COM 136110187SKrishna.Elango@Sun.COM case DEVCTL_BUS_RESETALL: 136210187SKrishna.Elango@Sun.COM rv = ENOTSUP; 136310187SKrishna.Elango@Sun.COM break; 136410187SKrishna.Elango@Sun.COM 136510187SKrishna.Elango@Sun.COM default: 136610187SKrishna.Elango@Sun.COM rv = ENOTTY; 136710187SKrishna.Elango@Sun.COM } 136810187SKrishna.Elango@Sun.COM 136910187SKrishna.Elango@Sun.COM ndi_dc_freehdl(dcp); 137010187SKrishna.Elango@Sun.COM return (rv); 137110187SKrishna.Elango@Sun.COM } 137210187SKrishna.Elango@Sun.COM 137310187SKrishna.Elango@Sun.COM static int 137410187SKrishna.Elango@Sun.COM pcieb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, 137510187SKrishna.Elango@Sun.COM int flags, char *name, caddr_t valuep, int *lengthp) 137610187SKrishna.Elango@Sun.COM { 137710187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb_p; 137810187SKrishna.Elango@Sun.COM minor_t minor = getminor(dev); 137910187SKrishna.Elango@Sun.COM int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor); 138010187SKrishna.Elango@Sun.COM 138110187SKrishna.Elango@Sun.COM pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state, 138210187SKrishna.Elango@Sun.COM instance); 138310187SKrishna.Elango@Sun.COM 138410187SKrishna.Elango@Sun.COM if (pcieb_p == NULL) 138510187SKrishna.Elango@Sun.COM return (ENXIO); 138610187SKrishna.Elango@Sun.COM 138710187SKrishna.Elango@Sun.COM if (pcieb_p->pcieb_hotplug_capable == B_TRUE) 138810187SKrishna.Elango@Sun.COM return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, 138910187SKrishna.Elango@Sun.COM flags, name, valuep, lengthp)); 139010187SKrishna.Elango@Sun.COM 139110187SKrishna.Elango@Sun.COM return (ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp)); 139210187SKrishna.Elango@Sun.COM } 139310187SKrishna.Elango@Sun.COM 139410187SKrishna.Elango@Sun.COM /*ARGSUSED*/ 139510187SKrishna.Elango@Sun.COM static int 139610187SKrishna.Elango@Sun.COM pcieb_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 139710187SKrishna.Elango@Sun.COM { 139810187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb_p; /* per pcieb state pointer */ 139910187SKrishna.Elango@Sun.COM minor_t minor = getminor((dev_t)arg); 140010187SKrishna.Elango@Sun.COM int instance = PCIHP_AP_MINOR_NUM_TO_INSTANCE(minor); 140110187SKrishna.Elango@Sun.COM 140210187SKrishna.Elango@Sun.COM pcieb_p = (pcieb_devstate_t *)ddi_get_soft_state(pcieb_state, 140310187SKrishna.Elango@Sun.COM instance); 140410187SKrishna.Elango@Sun.COM 140510187SKrishna.Elango@Sun.COM switch (infocmd) { 140610187SKrishna.Elango@Sun.COM default: 140710187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 140810187SKrishna.Elango@Sun.COM 140910187SKrishna.Elango@Sun.COM case DDI_INFO_DEVT2INSTANCE: 141010187SKrishna.Elango@Sun.COM *result = (void *)(intptr_t)instance; 141110187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 141210187SKrishna.Elango@Sun.COM 141310187SKrishna.Elango@Sun.COM case DDI_INFO_DEVT2DEVINFO: 141410187SKrishna.Elango@Sun.COM if (pcieb_p == NULL) 141510187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 141610187SKrishna.Elango@Sun.COM *result = (void *)pcieb_p->pcieb_dip; 141710187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 141810187SKrishna.Elango@Sun.COM } 141910187SKrishna.Elango@Sun.COM } 142010187SKrishna.Elango@Sun.COM 142110187SKrishna.Elango@Sun.COM /* 142210187SKrishna.Elango@Sun.COM * Common interrupt handler for hotplug, PME and errors. 142310187SKrishna.Elango@Sun.COM */ 142410187SKrishna.Elango@Sun.COM static uint_t 142510187SKrishna.Elango@Sun.COM pcieb_intr_handler(caddr_t arg1, caddr_t arg2) 142610187SKrishna.Elango@Sun.COM { 142710187SKrishna.Elango@Sun.COM pcieb_devstate_t *pcieb_p = (pcieb_devstate_t *)arg1; 142810187SKrishna.Elango@Sun.COM dev_info_t *dip = pcieb_p->pcieb_dip; 142910187SKrishna.Elango@Sun.COM ddi_fm_error_t derr; 143010187SKrishna.Elango@Sun.COM int sts = 0; 143110187SKrishna.Elango@Sun.COM int ret = DDI_INTR_UNCLAIMED; 143210187SKrishna.Elango@Sun.COM int isrc; 143310187SKrishna.Elango@Sun.COM 143410187SKrishna.Elango@Sun.COM if (!(pcieb_p->pcieb_init_flags & PCIEB_INIT_ENABLE)) 143510187SKrishna.Elango@Sun.COM goto FAIL; 143610187SKrishna.Elango@Sun.COM 143710187SKrishna.Elango@Sun.COM mutex_enter(&pcieb_p->pcieb_intr_mutex); 143810187SKrishna.Elango@Sun.COM isrc = pcieb_p->pcieb_isr_tab[(int)(uintptr_t)arg2]; 143910187SKrishna.Elango@Sun.COM mutex_exit(&pcieb_p->pcieb_intr_mutex); 144010187SKrishna.Elango@Sun.COM 144110187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_INTR, dip, "Received intr number %d\n", 144210187SKrishna.Elango@Sun.COM (int)(uintptr_t)arg2); 144310187SKrishna.Elango@Sun.COM 144410187SKrishna.Elango@Sun.COM if (isrc == PCIEB_INTR_SRC_UNKNOWN) 144510187SKrishna.Elango@Sun.COM goto FAIL; 144610187SKrishna.Elango@Sun.COM 144710187SKrishna.Elango@Sun.COM if (isrc & PCIEB_INTR_SRC_HP) { 144810187SKrishna.Elango@Sun.COM if (pcieb_p->pcieb_hpc_type == HPC_PCIE) 144910187SKrishna.Elango@Sun.COM ret = pciehpc_intr(dip); 145010187SKrishna.Elango@Sun.COM else if (pcieb_p->pcieb_hpc_type == HPC_SHPC) 145110187SKrishna.Elango@Sun.COM ret = pcishpc_intr(dip); 145210187SKrishna.Elango@Sun.COM } 145310187SKrishna.Elango@Sun.COM 145410187SKrishna.Elango@Sun.COM if (isrc & PCIEB_INTR_SRC_PME) 145510187SKrishna.Elango@Sun.COM ret = DDI_INTR_CLAIMED; 145610187SKrishna.Elango@Sun.COM 145710187SKrishna.Elango@Sun.COM /* AER Error */ 145810187SKrishna.Elango@Sun.COM if (isrc & PCIEB_INTR_SRC_AER) { 145910187SKrishna.Elango@Sun.COM /* 146010187SKrishna.Elango@Sun.COM * If MSI is shared with PME/hotplug then check Root Error 146110187SKrishna.Elango@Sun.COM * Status Reg before claiming it. For now it's ok since 146210187SKrishna.Elango@Sun.COM * we know we get 2 MSIs. 146310187SKrishna.Elango@Sun.COM */ 146410187SKrishna.Elango@Sun.COM ret = DDI_INTR_CLAIMED; 146510187SKrishna.Elango@Sun.COM bzero(&derr, sizeof (ddi_fm_error_t)); 146610187SKrishna.Elango@Sun.COM derr.fme_version = DDI_FME_VERSION; 146710187SKrishna.Elango@Sun.COM mutex_enter(&pcieb_p->pcieb_peek_poke_mutex); 146810187SKrishna.Elango@Sun.COM mutex_enter(&pcieb_p->pcieb_err_mutex); 146910187SKrishna.Elango@Sun.COM 147010187SKrishna.Elango@Sun.COM if ((DEVI(dip)->devi_fmhdl->fh_cap) & DDI_FM_EREPORT_CAPABLE) 147110187SKrishna.Elango@Sun.COM sts = pf_scan_fabric(dip, &derr, NULL); 147210187SKrishna.Elango@Sun.COM 147310187SKrishna.Elango@Sun.COM mutex_exit(&pcieb_p->pcieb_err_mutex); 147410187SKrishna.Elango@Sun.COM mutex_exit(&pcieb_p->pcieb_peek_poke_mutex); 147510187SKrishna.Elango@Sun.COM if (pcieb_die & sts) 147610187SKrishna.Elango@Sun.COM fm_panic("%s-%d: PCI(-X) Express Fatal Error. (0x%x)", 147710187SKrishna.Elango@Sun.COM ddi_driver_name(dip), ddi_get_instance(dip), sts); 147810187SKrishna.Elango@Sun.COM } 147910187SKrishna.Elango@Sun.COM FAIL: 148010187SKrishna.Elango@Sun.COM return (ret); 148110187SKrishna.Elango@Sun.COM } 148210187SKrishna.Elango@Sun.COM 148310187SKrishna.Elango@Sun.COM /* 148410187SKrishna.Elango@Sun.COM * Some PCI-X to PCI-E bridges do not support full 64-bit addressing on the 148510187SKrishna.Elango@Sun.COM * PCI-X side of the bridge. We build a special version of this driver for 148610187SKrishna.Elango@Sun.COM * those bridges, which uses PCIEB_ADDR_LIMIT_LO and/or PCIEB_ADDR_LIMIT_HI 148710187SKrishna.Elango@Sun.COM * to define the range of values which the chip can handle. The code below 148810187SKrishna.Elango@Sun.COM * then clamps the DMA address range supplied by the driver, preventing the 148910187SKrishna.Elango@Sun.COM * PCI-E nexus driver from allocating any memory the bridge can't deal 149010187SKrishna.Elango@Sun.COM * with. 149110187SKrishna.Elango@Sun.COM */ 149210187SKrishna.Elango@Sun.COM static int 149310187SKrishna.Elango@Sun.COM pcieb_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, 149410187SKrishna.Elango@Sun.COM ddi_dma_attr_t *attr_p, int (*waitfp)(caddr_t), caddr_t arg, 149510187SKrishna.Elango@Sun.COM ddi_dma_handle_t *handlep) 149610187SKrishna.Elango@Sun.COM { 149710187SKrishna.Elango@Sun.COM int ret; 149810187SKrishna.Elango@Sun.COM #ifdef BCM_SW_WORKAROUNDS 149910187SKrishna.Elango@Sun.COM uint64_t lim; 150010187SKrishna.Elango@Sun.COM 150110187SKrishna.Elango@Sun.COM /* 150210187SKrishna.Elango@Sun.COM * If the leaf device's limits are outside than what the Broadcom 150310187SKrishna.Elango@Sun.COM * bridge can handle, we need to clip the values passed up the chain. 150410187SKrishna.Elango@Sun.COM */ 150510187SKrishna.Elango@Sun.COM lim = attr_p->dma_attr_addr_lo; 150610187SKrishna.Elango@Sun.COM attr_p->dma_attr_addr_lo = MAX(lim, PCIEB_ADDR_LIMIT_LO); 150710187SKrishna.Elango@Sun.COM 150810187SKrishna.Elango@Sun.COM lim = attr_p->dma_attr_addr_hi; 150910187SKrishna.Elango@Sun.COM attr_p->dma_attr_addr_hi = MIN(lim, PCIEB_ADDR_LIMIT_HI); 151010187SKrishna.Elango@Sun.COM 151110187SKrishna.Elango@Sun.COM #endif /* BCM_SW_WORKAROUNDS */ 151210187SKrishna.Elango@Sun.COM 151310187SKrishna.Elango@Sun.COM /* 151410187SKrishna.Elango@Sun.COM * This is a software workaround to fix the Broadcom 5714/5715 PCIe-PCI 151510187SKrishna.Elango@Sun.COM * bridge prefetch bug. Intercept the DMA alloc handle request and set 151610187SKrishna.Elango@Sun.COM * PX_DMAI_FLAGS_MAP_BUFZONE flag in the handle. If this flag is set, 151710187SKrishna.Elango@Sun.COM * the px nexus driver will allocate an extra page & make it valid one, 151810187SKrishna.Elango@Sun.COM * for any DVMA request that comes from any of the Broadcom bridge child 151910187SKrishna.Elango@Sun.COM * devices. 152010187SKrishna.Elango@Sun.COM */ 152110187SKrishna.Elango@Sun.COM if ((ret = ddi_dma_allochdl(dip, rdip, attr_p, waitfp, arg, 152210187SKrishna.Elango@Sun.COM handlep)) == DDI_SUCCESS) { 152310187SKrishna.Elango@Sun.COM ddi_dma_impl_t *mp = (ddi_dma_impl_t *)*handlep; 152410187SKrishna.Elango@Sun.COM #ifdef BCM_SW_WORKAROUNDS 152510187SKrishna.Elango@Sun.COM mp->dmai_inuse |= PX_DMAI_FLAGS_MAP_BUFZONE; 152610187SKrishna.Elango@Sun.COM #endif /* BCM_SW_WORKAROUNDS */ 152710187SKrishna.Elango@Sun.COM /* 152810187SKrishna.Elango@Sun.COM * For a given rdip, update mp->dmai_bdf with the bdf value 152910187SKrishna.Elango@Sun.COM * of pcieb's immediate child or secondary bus-id of the 153010187SKrishna.Elango@Sun.COM * PCIe2PCI bridge. 153110187SKrishna.Elango@Sun.COM */ 153210187SKrishna.Elango@Sun.COM mp->dmai_minxfer = pcie_get_bdf_for_dma_xfer(dip, rdip); 153310187SKrishna.Elango@Sun.COM } 153410187SKrishna.Elango@Sun.COM 153510187SKrishna.Elango@Sun.COM return (ret); 153610187SKrishna.Elango@Sun.COM } 153710187SKrishna.Elango@Sun.COM 153810187SKrishna.Elango@Sun.COM /* 153910187SKrishna.Elango@Sun.COM * FDVMA feature is not supported for any child device of Broadcom 5714/5715 154010187SKrishna.Elango@Sun.COM * PCIe-PCI bridge due to prefetch bug. Return failure immediately, so that 154110187SKrishna.Elango@Sun.COM * these drivers will switch to regular DVMA path. 154210187SKrishna.Elango@Sun.COM */ 154310187SKrishna.Elango@Sun.COM /*ARGSUSED*/ 154410187SKrishna.Elango@Sun.COM static int 154510187SKrishna.Elango@Sun.COM pcieb_dma_mctl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_handle_t handle, 154610187SKrishna.Elango@Sun.COM enum ddi_dma_ctlops cmd, off_t *offp, size_t *lenp, caddr_t *objp, 154710187SKrishna.Elango@Sun.COM uint_t cache_flags) 154810187SKrishna.Elango@Sun.COM { 154910187SKrishna.Elango@Sun.COM int ret; 155010187SKrishna.Elango@Sun.COM 155110187SKrishna.Elango@Sun.COM #ifdef BCM_SW_WORKAROUNDS 155210187SKrishna.Elango@Sun.COM if (cmd == DDI_DMA_RESERVE) 155310187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 155410187SKrishna.Elango@Sun.COM #endif /* BCM_SW_WORKAROUNDS */ 155510187SKrishna.Elango@Sun.COM 155610187SKrishna.Elango@Sun.COM if (((ret = ddi_dma_mctl(dip, rdip, handle, cmd, offp, lenp, objp, 155710187SKrishna.Elango@Sun.COM cache_flags)) == DDI_SUCCESS) && (cmd == DDI_DMA_RESERVE)) { 155810187SKrishna.Elango@Sun.COM ddi_dma_impl_t *mp = (ddi_dma_impl_t *)*objp; 155910187SKrishna.Elango@Sun.COM 156010187SKrishna.Elango@Sun.COM /* 156110187SKrishna.Elango@Sun.COM * For a given rdip, update mp->dmai_bdf with the bdf value 156210187SKrishna.Elango@Sun.COM * of pcieb's immediate child or secondary bus-id of the 156310187SKrishna.Elango@Sun.COM * PCIe2PCI bridge. 156410187SKrishna.Elango@Sun.COM */ 156510187SKrishna.Elango@Sun.COM mp->dmai_minxfer = pcie_get_bdf_for_dma_xfer(dip, rdip); 156610187SKrishna.Elango@Sun.COM } 156710187SKrishna.Elango@Sun.COM 156810187SKrishna.Elango@Sun.COM return (ret); 156910187SKrishna.Elango@Sun.COM } 157010187SKrishna.Elango@Sun.COM 157110187SKrishna.Elango@Sun.COM static int 157210187SKrishna.Elango@Sun.COM pcieb_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op, 157310187SKrishna.Elango@Sun.COM ddi_intr_handle_impl_t *hdlp, void *result) 157410187SKrishna.Elango@Sun.COM { 157510187SKrishna.Elango@Sun.COM return (pcieb_plat_intr_ops(dip, rdip, intr_op, hdlp, result)); 157610187SKrishna.Elango@Sun.COM 157710187SKrishna.Elango@Sun.COM } 157810187SKrishna.Elango@Sun.COM 157910187SKrishna.Elango@Sun.COM /*ARGSUSED*/ 158010187SKrishna.Elango@Sun.COM static int pcieb_pciehpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle) 158110187SKrishna.Elango@Sun.COM { 158210187SKrishna.Elango@Sun.COM uint16_t cap_ptr; 158310187SKrishna.Elango@Sun.COM 158410187SKrishna.Elango@Sun.COM if ((PCI_CAP_LOCATE(config_handle, PCI_CAP_ID_PCI_E, &cap_ptr)) != 158510187SKrishna.Elango@Sun.COM DDI_FAILURE) { 158610187SKrishna.Elango@Sun.COM uint16_t slotimpl = PCI_CAP_GET16(config_handle, NULL, cap_ptr, 158710187SKrishna.Elango@Sun.COM PCIE_PCIECAP) & PCIE_PCIECAP_SLOT_IMPL; 158810187SKrishna.Elango@Sun.COM if (slotimpl) 158910187SKrishna.Elango@Sun.COM if (PCI_CAP_GET32(config_handle, NULL, cap_ptr, 159010187SKrishna.Elango@Sun.COM PCIE_SLOTCAP) & PCIE_SLOTCAP_HP_CAPABLE) 159110187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 159210187SKrishna.Elango@Sun.COM } 159310187SKrishna.Elango@Sun.COM 159410187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 159510187SKrishna.Elango@Sun.COM } 159610187SKrishna.Elango@Sun.COM 159710187SKrishna.Elango@Sun.COM static int pcieb_pcishpc_probe(dev_info_t *dip, ddi_acc_handle_t config_handle) 159810187SKrishna.Elango@Sun.COM { 159910187SKrishna.Elango@Sun.COM return (pcieb_plat_pcishpc_probe(dip, config_handle)); 160010187SKrishna.Elango@Sun.COM } 160110187SKrishna.Elango@Sun.COM 160210187SKrishna.Elango@Sun.COM /* 160310187SKrishna.Elango@Sun.COM * Initialize hotplug framework if we are hotpluggable. 160410187SKrishna.Elango@Sun.COM * Sets flag in the soft state if Hot Plug is supported and initialized 160510187SKrishna.Elango@Sun.COM * properly. 160610187SKrishna.Elango@Sun.COM */ 160710187SKrishna.Elango@Sun.COM /*ARGSUSED*/ 160810187SKrishna.Elango@Sun.COM static int 160910187SKrishna.Elango@Sun.COM pcieb_init_hotplug(pcieb_devstate_t *pcieb) 161010187SKrishna.Elango@Sun.COM { 161110187SKrishna.Elango@Sun.COM int rv = DDI_FAILURE; 161210187SKrishna.Elango@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2BUS(pcieb->pcieb_dip); 161310187SKrishna.Elango@Sun.COM ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl; 161410187SKrishna.Elango@Sun.COM uint8_t dev_type = bus_p->bus_dev_type; 161510187SKrishna.Elango@Sun.COM 161610187SKrishna.Elango@Sun.COM #ifdef PX_PLX 161710187SKrishna.Elango@Sun.COM uint16_t vid = bus_p->bus_dev_ven_id & 0xFFFF; 161810187SKrishna.Elango@Sun.COM uint16_t did = bus_p->bus_dev_ven_id >> 16; 161910187SKrishna.Elango@Sun.COM if ((vid == PXB_VENDOR_PLX) && (did == PXB_DEVICE_PLX_8532) && 162010187SKrishna.Elango@Sun.COM (bus_p->bus_rev_id <= PXB_DEVICE_PLX_AA_REV)) 162110187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 162210187SKrishna.Elango@Sun.COM #endif /* PX_PLX */ 162310187SKrishna.Elango@Sun.COM 162410187SKrishna.Elango@Sun.COM if (((dev_type == PCIE_PCIECAP_DEV_TYPE_DOWN) || 162510187SKrishna.Elango@Sun.COM (dev_type == PCIE_PCIECAP_DEV_TYPE_PCI2PCIE) || 162610187SKrishna.Elango@Sun.COM (dev_type == PCIE_PCIECAP_DEV_TYPE_ROOT)) && 162710187SKrishna.Elango@Sun.COM (pcieb_pciehpc_probe(pcieb->pcieb_dip, 162810187SKrishna.Elango@Sun.COM config_handle) == DDI_SUCCESS)) { 162910187SKrishna.Elango@Sun.COM pcieb->pcieb_hpc_type = HPC_PCIE; 163010187SKrishna.Elango@Sun.COM } else if ((dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) && 163110187SKrishna.Elango@Sun.COM (pcieb_pcishpc_probe(pcieb->pcieb_dip, 163210187SKrishna.Elango@Sun.COM config_handle) == DDI_SUCCESS)) { 163310187SKrishna.Elango@Sun.COM pcieb->pcieb_hpc_type = HPC_SHPC; 163410187SKrishna.Elango@Sun.COM } else { 163510187SKrishna.Elango@Sun.COM pcieb->pcieb_hpc_type = HPC_NONE; 163610187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 163710187SKrishna.Elango@Sun.COM } 163810187SKrishna.Elango@Sun.COM 163910187SKrishna.Elango@Sun.COM pcieb->pcieb_hotplug_capable = B_TRUE; 164010187SKrishna.Elango@Sun.COM 164110187SKrishna.Elango@Sun.COM if (pcieb->pcieb_hpc_type == HPC_PCIE) 164210187SKrishna.Elango@Sun.COM rv = pciehpc_init(pcieb->pcieb_dip, NULL); 164310187SKrishna.Elango@Sun.COM else if (pcieb->pcieb_hpc_type == HPC_SHPC) 164410187SKrishna.Elango@Sun.COM rv = pcishpc_init(pcieb->pcieb_dip); 164510187SKrishna.Elango@Sun.COM 164610187SKrishna.Elango@Sun.COM if (rv != DDI_SUCCESS) 164710187SKrishna.Elango@Sun.COM goto fail; 164810187SKrishna.Elango@Sun.COM 164910187SKrishna.Elango@Sun.COM if (pcihp_init(pcieb->pcieb_dip) != DDI_SUCCESS) { 165010187SKrishna.Elango@Sun.COM if (pcieb->pcieb_hpc_type == HPC_PCIE) 165110187SKrishna.Elango@Sun.COM (void) pciehpc_uninit(pcieb->pcieb_dip); 165210187SKrishna.Elango@Sun.COM else if (pcieb->pcieb_hpc_type == HPC_SHPC) 165310187SKrishna.Elango@Sun.COM (void) pcishpc_uninit(pcieb->pcieb_dip); 165410187SKrishna.Elango@Sun.COM 165510187SKrishna.Elango@Sun.COM goto fail; 165610187SKrishna.Elango@Sun.COM } 165710187SKrishna.Elango@Sun.COM 165810187SKrishna.Elango@Sun.COM (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, pcieb->pcieb_dip, 165910187SKrishna.Elango@Sun.COM "hotplug-capable"); 166010187SKrishna.Elango@Sun.COM 166110187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 166210187SKrishna.Elango@Sun.COM 166310187SKrishna.Elango@Sun.COM fail: 166410187SKrishna.Elango@Sun.COM pcieb->pcieb_hpc_type = HPC_NONE; 166510187SKrishna.Elango@Sun.COM pcieb->pcieb_hotplug_capable = B_FALSE; 166610330SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_ATTACH, pcieb->pcieb_dip, "Failed setting hotplug" 166710330SKrishna.Elango@Sun.COM " framework\n"); 166810187SKrishna.Elango@Sun.COM 166910187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 167010187SKrishna.Elango@Sun.COM } 167110187SKrishna.Elango@Sun.COM 167210187SKrishna.Elango@Sun.COM /* 167310187SKrishna.Elango@Sun.COM * Power management related initialization specific to pcieb. 167410187SKrishna.Elango@Sun.COM * Called by pcieb_attach() 167510187SKrishna.Elango@Sun.COM */ 167610187SKrishna.Elango@Sun.COM static int 167710187SKrishna.Elango@Sun.COM pcieb_pwr_setup(dev_info_t *dip) 167810187SKrishna.Elango@Sun.COM { 167910187SKrishna.Elango@Sun.COM char *comp_array[5]; 168010187SKrishna.Elango@Sun.COM int i; 168110187SKrishna.Elango@Sun.COM ddi_acc_handle_t conf_hdl; 168210187SKrishna.Elango@Sun.COM uint16_t pmcap, cap_ptr; 168310187SKrishna.Elango@Sun.COM pcie_pwr_t *pwr_p; 168410187SKrishna.Elango@Sun.COM 168510187SKrishna.Elango@Sun.COM /* Some platforms/devices may choose to disable PM */ 168610187SKrishna.Elango@Sun.COM if (pcieb_plat_pwr_disable(dip)) { 168710187SKrishna.Elango@Sun.COM (void) pcieb_pwr_disable(dip); 168810187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 168910187SKrishna.Elango@Sun.COM } 169010187SKrishna.Elango@Sun.COM 169110187SKrishna.Elango@Sun.COM ASSERT(PCIE_PMINFO(dip)); 169210187SKrishna.Elango@Sun.COM pwr_p = PCIE_NEXUS_PMINFO(dip); 169310187SKrishna.Elango@Sun.COM ASSERT(pwr_p); 169410187SKrishna.Elango@Sun.COM 169510187SKrishna.Elango@Sun.COM /* Code taken from pci_pci driver */ 169610187SKrishna.Elango@Sun.COM if (pci_config_setup(dip, &pwr_p->pwr_conf_hdl) != DDI_SUCCESS) { 169710187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "pcieb_pwr_setup: pci_config_setup " 169810187SKrishna.Elango@Sun.COM "failed\n"); 169910187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 170010187SKrishna.Elango@Sun.COM } 170110187SKrishna.Elango@Sun.COM conf_hdl = pwr_p->pwr_conf_hdl; 170210187SKrishna.Elango@Sun.COM 170310187SKrishna.Elango@Sun.COM /* 170410187SKrishna.Elango@Sun.COM * Walk the capabilities searching for a PM entry. 170510187SKrishna.Elango@Sun.COM */ 170610187SKrishna.Elango@Sun.COM if ((PCI_CAP_LOCATE(conf_hdl, PCI_CAP_ID_PM, &cap_ptr)) == 170710187SKrishna.Elango@Sun.COM DDI_FAILURE) { 170810187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "switch/bridge does not support PM. " 170910187SKrishna.Elango@Sun.COM " PCI PM data structure not found in config header\n"); 171010187SKrishna.Elango@Sun.COM pci_config_teardown(&conf_hdl); 171110187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 171210187SKrishna.Elango@Sun.COM } 171310187SKrishna.Elango@Sun.COM /* 171410187SKrishna.Elango@Sun.COM * Save offset to pmcsr for future references. 171510187SKrishna.Elango@Sun.COM */ 171610187SKrishna.Elango@Sun.COM pwr_p->pwr_pmcsr_offset = cap_ptr + PCI_PMCSR; 171710187SKrishna.Elango@Sun.COM pmcap = PCI_CAP_GET16(conf_hdl, NULL, cap_ptr, PCI_PMCAP); 171810187SKrishna.Elango@Sun.COM if (pmcap & PCI_PMCAP_D1) { 171910187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "D1 state supported\n"); 172010187SKrishna.Elango@Sun.COM pwr_p->pwr_pmcaps |= PCIE_SUPPORTS_D1; 172110187SKrishna.Elango@Sun.COM } 172210187SKrishna.Elango@Sun.COM if (pmcap & PCI_PMCAP_D2) { 172310187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "D2 state supported\n"); 172410187SKrishna.Elango@Sun.COM pwr_p->pwr_pmcaps |= PCIE_SUPPORTS_D2; 172510187SKrishna.Elango@Sun.COM } 172610187SKrishna.Elango@Sun.COM 172710187SKrishna.Elango@Sun.COM i = 0; 172810187SKrishna.Elango@Sun.COM comp_array[i++] = "NAME=PCIe switch/bridge PM"; 172910187SKrishna.Elango@Sun.COM comp_array[i++] = "0=Power Off (D3)"; 173010187SKrishna.Elango@Sun.COM if (pwr_p->pwr_pmcaps & PCIE_SUPPORTS_D2) 173110187SKrishna.Elango@Sun.COM comp_array[i++] = "1=D2"; 173210187SKrishna.Elango@Sun.COM if (pwr_p->pwr_pmcaps & PCIE_SUPPORTS_D1) 173310187SKrishna.Elango@Sun.COM comp_array[i++] = "2=D1"; 173410187SKrishna.Elango@Sun.COM comp_array[i++] = "3=Full Power D0"; 173510187SKrishna.Elango@Sun.COM 173610187SKrishna.Elango@Sun.COM /* 173710187SKrishna.Elango@Sun.COM * Create pm-components property, if it does not exist already. 173810187SKrishna.Elango@Sun.COM */ 173910187SKrishna.Elango@Sun.COM if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip, 174010187SKrishna.Elango@Sun.COM "pm-components", comp_array, i) != DDI_PROP_SUCCESS) { 174110187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "could not create pm-components " 174210187SKrishna.Elango@Sun.COM " prop\n"); 174310187SKrishna.Elango@Sun.COM pci_config_teardown(&conf_hdl); 174410187SKrishna.Elango@Sun.COM return (DDI_FAILURE); 174510187SKrishna.Elango@Sun.COM } 174610187SKrishna.Elango@Sun.COM return (pcieb_pwr_init_and_raise(dip, pwr_p)); 174710187SKrishna.Elango@Sun.COM } 174810187SKrishna.Elango@Sun.COM 174910187SKrishna.Elango@Sun.COM /* 175010187SKrishna.Elango@Sun.COM * undo whatever is done in pcieb_pwr_setup. called by pcieb_detach() 175110187SKrishna.Elango@Sun.COM */ 175210187SKrishna.Elango@Sun.COM static void 175310187SKrishna.Elango@Sun.COM pcieb_pwr_teardown(dev_info_t *dip) 175410187SKrishna.Elango@Sun.COM { 175510187SKrishna.Elango@Sun.COM pcie_pwr_t *pwr_p; 175610187SKrishna.Elango@Sun.COM 175710187SKrishna.Elango@Sun.COM if (!PCIE_PMINFO(dip) || !(pwr_p = PCIE_NEXUS_PMINFO(dip))) 175810187SKrishna.Elango@Sun.COM return; 175910187SKrishna.Elango@Sun.COM 176010187SKrishna.Elango@Sun.COM (void) ddi_prop_remove(DDI_DEV_T_NONE, dip, "pm-components"); 176110187SKrishna.Elango@Sun.COM if (pwr_p->pwr_conf_hdl) 176210187SKrishna.Elango@Sun.COM pci_config_teardown(&pwr_p->pwr_conf_hdl); 176310187SKrishna.Elango@Sun.COM } 176410187SKrishna.Elango@Sun.COM 176510187SKrishna.Elango@Sun.COM /* 176610187SKrishna.Elango@Sun.COM * Initializes the power level and raise the power to D0, if it is 176710187SKrishna.Elango@Sun.COM * not at D0. 176810187SKrishna.Elango@Sun.COM */ 176910187SKrishna.Elango@Sun.COM static int 177010187SKrishna.Elango@Sun.COM pcieb_pwr_init_and_raise(dev_info_t *dip, pcie_pwr_t *pwr_p) 177110187SKrishna.Elango@Sun.COM { 177210187SKrishna.Elango@Sun.COM uint16_t pmcsr; 177310187SKrishna.Elango@Sun.COM int ret = DDI_SUCCESS; 177410187SKrishna.Elango@Sun.COM 177510187SKrishna.Elango@Sun.COM /* 177610187SKrishna.Elango@Sun.COM * Intialize our power level from PMCSR. The common code initializes 177710187SKrishna.Elango@Sun.COM * this to UNKNOWN. There is no guarantee that we will be at full 177810187SKrishna.Elango@Sun.COM * power at attach. If we are not at D0, raise the power. 177910187SKrishna.Elango@Sun.COM */ 178010187SKrishna.Elango@Sun.COM pmcsr = pci_config_get16(pwr_p->pwr_conf_hdl, pwr_p->pwr_pmcsr_offset); 178110187SKrishna.Elango@Sun.COM pmcsr &= PCI_PMCSR_STATE_MASK; 178210187SKrishna.Elango@Sun.COM switch (pmcsr) { 178310187SKrishna.Elango@Sun.COM case PCI_PMCSR_D0: 178410187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D0; 178510187SKrishna.Elango@Sun.COM break; 178610187SKrishna.Elango@Sun.COM 178710187SKrishna.Elango@Sun.COM case PCI_PMCSR_D1: 178810187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D1; 178910187SKrishna.Elango@Sun.COM break; 179010187SKrishna.Elango@Sun.COM 179110187SKrishna.Elango@Sun.COM case PCI_PMCSR_D2: 179210187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D2; 179310187SKrishna.Elango@Sun.COM break; 179410187SKrishna.Elango@Sun.COM 179510187SKrishna.Elango@Sun.COM case PCI_PMCSR_D3HOT: 179610187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D3; 179710187SKrishna.Elango@Sun.COM break; 179810187SKrishna.Elango@Sun.COM 179910187SKrishna.Elango@Sun.COM default: 180010187SKrishna.Elango@Sun.COM break; 180110187SKrishna.Elango@Sun.COM } 180210187SKrishna.Elango@Sun.COM 180310187SKrishna.Elango@Sun.COM /* Raise the power to D0. */ 180410187SKrishna.Elango@Sun.COM if (pwr_p->pwr_func_lvl != PM_LEVEL_D0 && 180510187SKrishna.Elango@Sun.COM ((ret = pm_raise_power(dip, 0, PM_LEVEL_D0)) != DDI_SUCCESS)) { 180610187SKrishna.Elango@Sun.COM /* 180710187SKrishna.Elango@Sun.COM * Read PMCSR again. If it is at D0, ignore the return 180810187SKrishna.Elango@Sun.COM * value from pm_raise_power. 180910187SKrishna.Elango@Sun.COM */ 181010187SKrishna.Elango@Sun.COM pmcsr = pci_config_get16(pwr_p->pwr_conf_hdl, 181110187SKrishna.Elango@Sun.COM pwr_p->pwr_pmcsr_offset); 181210187SKrishna.Elango@Sun.COM if ((pmcsr & PCI_PMCSR_STATE_MASK) == PCI_PMCSR_D0) 181310187SKrishna.Elango@Sun.COM ret = DDI_SUCCESS; 181410187SKrishna.Elango@Sun.COM else { 181510187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "pcieb_pwr_setup: could not " 181610187SKrishna.Elango@Sun.COM "raise power to D0 \n"); 181710187SKrishna.Elango@Sun.COM } 181810187SKrishna.Elango@Sun.COM } 181910187SKrishna.Elango@Sun.COM if (ret == DDI_SUCCESS) 182010187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D0; 182110187SKrishna.Elango@Sun.COM return (ret); 182210187SKrishna.Elango@Sun.COM } 182310187SKrishna.Elango@Sun.COM 182410187SKrishna.Elango@Sun.COM /* 182510187SKrishna.Elango@Sun.COM * Disable PM for x86 and PLX 8532 switch. 182610187SKrishna.Elango@Sun.COM * For PLX Transitioning one port on this switch to low power causes links 182710187SKrishna.Elango@Sun.COM * on other ports on the same station to die. Due to PLX erratum #34, we 182810187SKrishna.Elango@Sun.COM * can't allow the downstream device go to non-D0 state. 182910187SKrishna.Elango@Sun.COM */ 183010187SKrishna.Elango@Sun.COM static int 183110187SKrishna.Elango@Sun.COM pcieb_pwr_disable(dev_info_t *dip) 183210187SKrishna.Elango@Sun.COM { 183310187SKrishna.Elango@Sun.COM pcie_pwr_t *pwr_p; 183410187SKrishna.Elango@Sun.COM 183510187SKrishna.Elango@Sun.COM ASSERT(PCIE_PMINFO(dip)); 183610187SKrishna.Elango@Sun.COM pwr_p = PCIE_NEXUS_PMINFO(dip); 183710187SKrishna.Elango@Sun.COM ASSERT(pwr_p); 183810187SKrishna.Elango@Sun.COM PCIEB_DEBUG(DBG_PWR, dip, "pcieb_pwr_disable: disabling PM\n"); 183910187SKrishna.Elango@Sun.COM pwr_p->pwr_func_lvl = PM_LEVEL_D0; 184010187SKrishna.Elango@Sun.COM pwr_p->pwr_flags = PCIE_NO_CHILD_PM; 184110187SKrishna.Elango@Sun.COM return (DDI_SUCCESS); 184210187SKrishna.Elango@Sun.COM } 184310187SKrishna.Elango@Sun.COM 184410187SKrishna.Elango@Sun.COM #ifdef DEBUG 184510187SKrishna.Elango@Sun.COM int pcieb_dbg_intr_print = 0; 184610187SKrishna.Elango@Sun.COM void 184710187SKrishna.Elango@Sun.COM pcieb_dbg(uint_t bit, dev_info_t *dip, char *fmt, ...) 184810187SKrishna.Elango@Sun.COM { 184910187SKrishna.Elango@Sun.COM va_list ap; 185010187SKrishna.Elango@Sun.COM 185110187SKrishna.Elango@Sun.COM if (!pcieb_dbg_print) 185210187SKrishna.Elango@Sun.COM return; 185310187SKrishna.Elango@Sun.COM 185410187SKrishna.Elango@Sun.COM if (dip) 185510187SKrishna.Elango@Sun.COM prom_printf("%s(%d): %s", ddi_driver_name(dip), 185610187SKrishna.Elango@Sun.COM ddi_get_instance(dip), pcieb_debug_sym[bit]); 185710187SKrishna.Elango@Sun.COM 185810187SKrishna.Elango@Sun.COM va_start(ap, fmt); 185910187SKrishna.Elango@Sun.COM if (servicing_interrupt()) { 186010187SKrishna.Elango@Sun.COM if (pcieb_dbg_intr_print) 186110187SKrishna.Elango@Sun.COM prom_vprintf(fmt, ap); 186210187SKrishna.Elango@Sun.COM } else { 186310187SKrishna.Elango@Sun.COM prom_vprintf(fmt, ap); 186410187SKrishna.Elango@Sun.COM } 186510187SKrishna.Elango@Sun.COM 186610187SKrishna.Elango@Sun.COM va_end(ap); 186710187SKrishna.Elango@Sun.COM } 186810187SKrishna.Elango@Sun.COM #endif 186910187SKrishna.Elango@Sun.COM 187010187SKrishna.Elango@Sun.COM static void 187110187SKrishna.Elango@Sun.COM pcieb_id_props(pcieb_devstate_t *pcieb) 187210187SKrishna.Elango@Sun.COM { 187310187SKrishna.Elango@Sun.COM uint64_t serialid = 0; /* 40b field of EUI-64 serial no. register */ 187410187SKrishna.Elango@Sun.COM uint16_t cap_ptr; 187510187SKrishna.Elango@Sun.COM uint8_t fic = 0; /* 1 = first in chassis device */ 187610187SKrishna.Elango@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2BUS(pcieb->pcieb_dip); 187710187SKrishna.Elango@Sun.COM ddi_acc_handle_t config_handle = bus_p->bus_cfg_hdl; 187810187SKrishna.Elango@Sun.COM 187910187SKrishna.Elango@Sun.COM /* 188010187SKrishna.Elango@Sun.COM * Identify first in chassis. In the special case of a Sun branded 188110187SKrishna.Elango@Sun.COM * PLX device, it obviously is first in chassis. Otherwise, in the 188210187SKrishna.Elango@Sun.COM * general case, look for an Expansion Slot Register and check its 188310187SKrishna.Elango@Sun.COM * first-in-chassis bit. 188410187SKrishna.Elango@Sun.COM */ 188510187SKrishna.Elango@Sun.COM #ifdef PX_PLX 188610187SKrishna.Elango@Sun.COM uint16_t vendor_id = bus_p->bus_dev_ven_id & 0xFFFF; 188710187SKrishna.Elango@Sun.COM uint16_t device_id = bus_p->bus_dev_ven_id >> 16; 188810187SKrishna.Elango@Sun.COM if ((vendor_id == PXB_VENDOR_SUN) && 188910187SKrishna.Elango@Sun.COM ((device_id == PXB_DEVICE_PLX_PCIX) || 189010187SKrishna.Elango@Sun.COM (device_id == PXB_DEVICE_PLX_PCIE))) { 189110187SKrishna.Elango@Sun.COM fic = 1; 189210187SKrishna.Elango@Sun.COM } 189310187SKrishna.Elango@Sun.COM #endif /* PX_PLX */ 189410187SKrishna.Elango@Sun.COM if ((fic == 0) && ((PCI_CAP_LOCATE(config_handle, 189510187SKrishna.Elango@Sun.COM PCI_CAP_ID_SLOT_ID, &cap_ptr)) != DDI_FAILURE)) { 189610187SKrishna.Elango@Sun.COM uint8_t esr = PCI_CAP_GET8(config_handle, NULL, 189710187SKrishna.Elango@Sun.COM cap_ptr, PCI_CAP_ID_REGS_OFF); 189810187SKrishna.Elango@Sun.COM if (PCI_CAPSLOT_FIC(esr)) 189910187SKrishna.Elango@Sun.COM fic = 1; 190010187SKrishna.Elango@Sun.COM } 190110187SKrishna.Elango@Sun.COM 190210187SKrishna.Elango@Sun.COM if ((PCI_CAP_LOCATE(config_handle, 190310187SKrishna.Elango@Sun.COM PCI_CAP_XCFG_SPC(PCIE_EXT_CAP_ID_SER), &cap_ptr)) != DDI_FAILURE) { 190410187SKrishna.Elango@Sun.COM /* Serialid can be 0 thru a full 40b number */ 190510187SKrishna.Elango@Sun.COM serialid = PCI_XCAP_GET32(config_handle, NULL, 190610187SKrishna.Elango@Sun.COM cap_ptr, PCIE_SER_SID_UPPER_DW); 190710187SKrishna.Elango@Sun.COM serialid <<= 32; 190810187SKrishna.Elango@Sun.COM serialid |= PCI_XCAP_GET32(config_handle, NULL, 190910187SKrishna.Elango@Sun.COM cap_ptr, PCIE_SER_SID_LOWER_DW); 191010187SKrishna.Elango@Sun.COM } 191110187SKrishna.Elango@Sun.COM 191210187SKrishna.Elango@Sun.COM if (fic) 191310187SKrishna.Elango@Sun.COM (void) ndi_prop_create_boolean(DDI_DEV_T_NONE, pcieb->pcieb_dip, 191410187SKrishna.Elango@Sun.COM "first-in-chassis"); 191510187SKrishna.Elango@Sun.COM if (serialid) 191610187SKrishna.Elango@Sun.COM (void) ddi_prop_update_int64(DDI_DEV_T_NONE, pcieb->pcieb_dip, 191710187SKrishna.Elango@Sun.COM "serialid#", serialid); 191810187SKrishna.Elango@Sun.COM } 191910187SKrishna.Elango@Sun.COM 192010187SKrishna.Elango@Sun.COM static void 192110187SKrishna.Elango@Sun.COM pcieb_create_ranges_prop(dev_info_t *dip, 192210187SKrishna.Elango@Sun.COM ddi_acc_handle_t config_handle) 192310187SKrishna.Elango@Sun.COM { 192410187SKrishna.Elango@Sun.COM uint32_t base, limit; 192510187SKrishna.Elango@Sun.COM pcieb_ranges_t ranges[PCIEB_RANGE_LEN]; 192610187SKrishna.Elango@Sun.COM uint8_t io_base_lo, io_limit_lo; 192710187SKrishna.Elango@Sun.COM uint16_t io_base_hi, io_limit_hi, mem_base, mem_limit; 192810187SKrishna.Elango@Sun.COM int i = 0, rangelen = sizeof (pcieb_ranges_t)/sizeof (int); 192910187SKrishna.Elango@Sun.COM 193010187SKrishna.Elango@Sun.COM io_base_lo = pci_config_get8(config_handle, PCI_BCNF_IO_BASE_LOW); 193110187SKrishna.Elango@Sun.COM io_limit_lo = pci_config_get8(config_handle, PCI_BCNF_IO_LIMIT_LOW); 193210187SKrishna.Elango@Sun.COM io_base_hi = pci_config_get16(config_handle, PCI_BCNF_IO_BASE_HI); 193310187SKrishna.Elango@Sun.COM io_limit_hi = pci_config_get16(config_handle, PCI_BCNF_IO_LIMIT_HI); 193410187SKrishna.Elango@Sun.COM mem_base = pci_config_get16(config_handle, PCI_BCNF_MEM_BASE); 193510187SKrishna.Elango@Sun.COM mem_limit = pci_config_get16(config_handle, PCI_BCNF_MEM_LIMIT); 193610187SKrishna.Elango@Sun.COM 193710187SKrishna.Elango@Sun.COM /* 193810187SKrishna.Elango@Sun.COM * Create ranges for IO space 193910187SKrishna.Elango@Sun.COM */ 194010187SKrishna.Elango@Sun.COM ranges[i].size_low = ranges[i].size_high = 0; 194110187SKrishna.Elango@Sun.COM ranges[i].parent_mid = ranges[i].child_mid = ranges[i].parent_high = 0; 194210187SKrishna.Elango@Sun.COM ranges[i].child_high = ranges[i].parent_high |= 194310187SKrishna.Elango@Sun.COM (PCI_REG_REL_M | PCI_ADDR_IO); 194410187SKrishna.Elango@Sun.COM base = PCIEB_16bit_IOADDR(io_base_lo); 194510187SKrishna.Elango@Sun.COM limit = PCIEB_16bit_IOADDR(io_limit_lo); 194610187SKrishna.Elango@Sun.COM 194710187SKrishna.Elango@Sun.COM if ((io_base_lo & 0xf) == PCIEB_32BIT_IO) { 194810187SKrishna.Elango@Sun.COM base = PCIEB_LADDR(base, io_base_hi); 194910187SKrishna.Elango@Sun.COM } 195010187SKrishna.Elango@Sun.COM if ((io_limit_lo & 0xf) == PCIEB_32BIT_IO) { 195110187SKrishna.Elango@Sun.COM limit = PCIEB_LADDR(limit, io_limit_hi); 195210187SKrishna.Elango@Sun.COM } 195310187SKrishna.Elango@Sun.COM 195410187SKrishna.Elango@Sun.COM if ((io_base_lo & PCIEB_32BIT_IO) && (io_limit_hi > 0)) { 195510187SKrishna.Elango@Sun.COM base = PCIEB_LADDR(base, io_base_hi); 195610187SKrishna.Elango@Sun.COM limit = PCIEB_LADDR(limit, io_limit_hi); 195710187SKrishna.Elango@Sun.COM } 195810187SKrishna.Elango@Sun.COM 195910187SKrishna.Elango@Sun.COM /* 196010187SKrishna.Elango@Sun.COM * Create ranges for 32bit memory space 196110187SKrishna.Elango@Sun.COM */ 196210187SKrishna.Elango@Sun.COM base = PCIEB_32bit_MEMADDR(mem_base); 196310187SKrishna.Elango@Sun.COM limit = PCIEB_32bit_MEMADDR(mem_limit); 196410187SKrishna.Elango@Sun.COM ranges[i].size_low = ranges[i].size_high = 0; 196510187SKrishna.Elango@Sun.COM ranges[i].parent_mid = ranges[i].child_mid = ranges[i].parent_high = 0; 196610187SKrishna.Elango@Sun.COM ranges[i].child_high = ranges[i].parent_high |= 196710187SKrishna.Elango@Sun.COM (PCI_REG_REL_M | PCI_ADDR_MEM32); 196810187SKrishna.Elango@Sun.COM ranges[i].child_low = ranges[i].parent_low = base; 196910187SKrishna.Elango@Sun.COM if (limit >= base) { 197010187SKrishna.Elango@Sun.COM ranges[i].size_low = limit - base + PCIEB_MEMGRAIN; 197110187SKrishna.Elango@Sun.COM i++; 197210187SKrishna.Elango@Sun.COM } 197310187SKrishna.Elango@Sun.COM 197410187SKrishna.Elango@Sun.COM if (i) { 197510187SKrishna.Elango@Sun.COM (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, dip, "ranges", 197610187SKrishna.Elango@Sun.COM (int *)ranges, i * rangelen); 197710187SKrishna.Elango@Sun.COM } 197810187SKrishna.Elango@Sun.COM } 197910187SKrishna.Elango@Sun.COM 198010187SKrishna.Elango@Sun.COM /* 198110187SKrishna.Elango@Sun.COM * For PCI and PCI-X devices including PCIe2PCI bridge, initialize 198210187SKrishna.Elango@Sun.COM * cache-line-size and latency timer configuration registers. 198310187SKrishna.Elango@Sun.COM */ 198410187SKrishna.Elango@Sun.COM void 198510187SKrishna.Elango@Sun.COM pcieb_set_pci_perf_parameters(dev_info_t *dip, ddi_acc_handle_t cfg_hdl) 198610187SKrishna.Elango@Sun.COM { 198710187SKrishna.Elango@Sun.COM uint_t n; 198810187SKrishna.Elango@Sun.COM 198910187SKrishna.Elango@Sun.COM /* Initialize cache-line-size configuration register if needed */ 199010187SKrishna.Elango@Sun.COM if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 199110187SKrishna.Elango@Sun.COM "cache-line-size", 0) == 0) { 199210187SKrishna.Elango@Sun.COM pci_config_put8(cfg_hdl, PCI_CONF_CACHE_LINESZ, 199310187SKrishna.Elango@Sun.COM PCIEB_CACHE_LINE_SIZE); 199410187SKrishna.Elango@Sun.COM n = pci_config_get8(cfg_hdl, PCI_CONF_CACHE_LINESZ); 199510187SKrishna.Elango@Sun.COM if (n != 0) { 199610187SKrishna.Elango@Sun.COM (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, 199710187SKrishna.Elango@Sun.COM "cache-line-size", n); 199810187SKrishna.Elango@Sun.COM } 199910187SKrishna.Elango@Sun.COM } 200010187SKrishna.Elango@Sun.COM 200110187SKrishna.Elango@Sun.COM /* Initialize latency timer configuration registers if needed */ 200210187SKrishna.Elango@Sun.COM if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 200310187SKrishna.Elango@Sun.COM "latency-timer", 0) == 0) { 200410187SKrishna.Elango@Sun.COM uchar_t min_gnt, latency_timer; 200510187SKrishna.Elango@Sun.COM uchar_t header_type; 200610187SKrishna.Elango@Sun.COM 200710187SKrishna.Elango@Sun.COM /* Determine the configuration header type */ 200810187SKrishna.Elango@Sun.COM header_type = pci_config_get8(cfg_hdl, PCI_CONF_HEADER); 200910187SKrishna.Elango@Sun.COM 201010187SKrishna.Elango@Sun.COM if ((header_type & PCI_HEADER_TYPE_M) == PCI_HEADER_ONE) { 201110187SKrishna.Elango@Sun.COM latency_timer = PCIEB_LATENCY_TIMER; 201210187SKrishna.Elango@Sun.COM pci_config_put8(cfg_hdl, PCI_BCNF_LATENCY_TIMER, 201310187SKrishna.Elango@Sun.COM latency_timer); 201410187SKrishna.Elango@Sun.COM } else { 201510187SKrishna.Elango@Sun.COM min_gnt = pci_config_get8(cfg_hdl, PCI_CONF_MIN_G); 201610187SKrishna.Elango@Sun.COM latency_timer = min_gnt * 8; 201710187SKrishna.Elango@Sun.COM } 201810187SKrishna.Elango@Sun.COM 201910187SKrishna.Elango@Sun.COM pci_config_put8(cfg_hdl, PCI_CONF_LATENCY_TIMER, 202010187SKrishna.Elango@Sun.COM latency_timer); 202110187SKrishna.Elango@Sun.COM n = pci_config_get8(cfg_hdl, PCI_CONF_LATENCY_TIMER); 202210187SKrishna.Elango@Sun.COM if (n != 0) { 202310187SKrishna.Elango@Sun.COM (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, 202410187SKrishna.Elango@Sun.COM "latency-timer", n); 202510187SKrishna.Elango@Sun.COM } 202610187SKrishna.Elango@Sun.COM } 202710187SKrishna.Elango@Sun.COM } 2028