13446Smrj /* 23446Smrj * CDDL HEADER START 33446Smrj * 43446Smrj * The contents of this file are subject to the terms of the 53446Smrj * Common Development and Distribution License (the "License"). 63446Smrj * You may not use this file except in compliance with the License. 73446Smrj * 83446Smrj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 93446Smrj * or http://www.opensolaris.org/os/licensing. 103446Smrj * See the License for the specific language governing permissions 113446Smrj * and limitations under the License. 123446Smrj * 133446Smrj * When distributing Covered Code, include this CDDL HEADER in each 143446Smrj * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 153446Smrj * If applicable, add the following below this CDDL HEADER, with the 163446Smrj * fields enclosed by brackets "[]" replaced with your own identifying 173446Smrj * information: Portions Copyright [yyyy] [name of copyright owner] 183446Smrj * 193446Smrj * CDDL HEADER END 203446Smrj */ 213446Smrj /* 229921SKrishna.Elango@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 233446Smrj * Use is subject to license terms. 243446Smrj */ 253446Smrj 263446Smrj /* 273446Smrj * PCI to PCI bus bridge nexus driver 283446Smrj */ 293446Smrj 303446Smrj #include <sys/conf.h> 313446Smrj #include <sys/kmem.h> 323446Smrj #include <sys/debug.h> 333446Smrj #include <sys/modctl.h> 343446Smrj #include <sys/autoconf.h> 353446Smrj #include <sys/ddi_impldefs.h> 363446Smrj #include <sys/pci.h> 3710923SEvan.Yan@Sun.COM #include <sys/pci_impl.h> 386313Skrishnae #include <sys/pcie_impl.h> 393446Smrj #include <sys/ddi.h> 403446Smrj #include <sys/sunddi.h> 413446Smrj #include <sys/sunndi.h> 423446Smrj #include <sys/ddifm.h> 433446Smrj #include <sys/ndifm.h> 443446Smrj #include <sys/fm/protocol.h> 4510923SEvan.Yan@Sun.COM #include <sys/hotplug/pci/pcie_hp.h> 463446Smrj #include <sys/hotplug/pci/pcihp.h> 473446Smrj #include <sys/pci_intr_lib.h> 483446Smrj #include <sys/psm.h> 499970SJimmy.Vetayases@Sun.COM #include <sys/pci_cap.h> 503446Smrj 513446Smrj /* 523446Smrj * The variable controls the default setting of the command register 533446Smrj * for pci devices. See ppb_initchild() for details. 543446Smrj */ 553446Smrj static ushort_t ppb_command_default = PCI_COMM_ME | PCI_COMM_MAE | PCI_COMM_IO; 563446Smrj 573446Smrj 583446Smrj static int ppb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *, 593446Smrj off_t, off_t, caddr_t *); 603446Smrj static int ppb_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, 613446Smrj void *, void *); 623446Smrj static int ppb_fm_init(dev_info_t *, dev_info_t *, int, 633446Smrj ddi_iblock_cookie_t *); 643446Smrj static int ppb_fm_callback(dev_info_t *, ddi_fm_error_t *, const void *); 653446Smrj static int ppb_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t, 663446Smrj ddi_intr_handle_impl_t *, void *); 673446Smrj 683446Smrj /* 693446Smrj * ppb_support_msi: Flag that controls MSI support across P2P Bridges. 703446Smrj * By default, MSI is not supported except for special cases like HT 713446Smrj * bridges/tunnels that have HT MSI mapping enabled. 723446Smrj * 733446Smrj * However, MSI support behavior can be patched on a system by changing 743446Smrj * the value of this flag as shown below:- 753446Smrj * 0 = default value, MSI is allowed by this driver for special cases 763446Smrj * 1 = MSI supported without any checks for this driver 773446Smrj * -1 = MSI not supported at all 783446Smrj */ 793446Smrj int ppb_support_msi = 0; 803446Smrj 813446Smrj /* 823446Smrj * Controls the usage of the Hypertransport MSI mapping capability 833446Smrj * 0 = default value, leave hardware function as it is 843446Smrj * 1 = always enable HT MSI mapping 853446Smrj * -1 = always disable HT MSI mapping 863446Smrj */ 873446Smrj int ppb_support_ht_msimap = 0; 883446Smrj 893446Smrj struct bus_ops ppb_bus_ops = { 903446Smrj BUSO_REV, 913446Smrj ppb_bus_map, 923446Smrj 0, 933446Smrj 0, 943446Smrj 0, 953446Smrj i_ddi_map_fault, 963446Smrj ddi_dma_map, 973446Smrj ddi_dma_allochdl, 983446Smrj ddi_dma_freehdl, 993446Smrj ddi_dma_bindhdl, 1003446Smrj ddi_dma_unbindhdl, 1013446Smrj ddi_dma_flush, 1023446Smrj ddi_dma_win, 1033446Smrj ddi_dma_mctl, 1043446Smrj ppb_ctlops, 1053446Smrj ddi_bus_prop_op, 10610923SEvan.Yan@Sun.COM 0, /* (*bus_get_eventcookie)(); */ 10710923SEvan.Yan@Sun.COM 0, /* (*bus_add_eventcall)(); */ 10810923SEvan.Yan@Sun.COM 0, /* (*bus_remove_eventcall)(); */ 10910923SEvan.Yan@Sun.COM 0, /* (*bus_post_event)(); */ 11010923SEvan.Yan@Sun.COM 0, /* (*bus_intr_ctl)(); */ 11110923SEvan.Yan@Sun.COM 0, /* (*bus_config)(); */ 11210923SEvan.Yan@Sun.COM 0, /* (*bus_unconfig)(); */ 11310923SEvan.Yan@Sun.COM ppb_fm_init, /* (*bus_fm_init)(); */ 11410923SEvan.Yan@Sun.COM NULL, /* (*bus_fm_fini)(); */ 11510923SEvan.Yan@Sun.COM NULL, /* (*bus_fm_access_enter)(); */ 11610923SEvan.Yan@Sun.COM NULL, /* (*bus_fm_access_exit)(); */ 11710923SEvan.Yan@Sun.COM NULL, /* (*bus_power)(); */ 11810923SEvan.Yan@Sun.COM ppb_intr_ops, /* (*bus_intr_op)(); */ 11910923SEvan.Yan@Sun.COM pcie_hp_common_ops /* (*bus_hp_op)(); */ 1203446Smrj }; 1213446Smrj 1223446Smrj /* 1233446Smrj * The goal here is to leverage off of the pcihp.c source without making 1243446Smrj * changes to it. Call into it's cb_ops directly if needed. 1253446Smrj */ 1263446Smrj static int ppb_open(dev_t *, int, int, cred_t *); 1273446Smrj static int ppb_close(dev_t, int, int, cred_t *); 1283446Smrj static int ppb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 1293446Smrj static int ppb_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *, 1303446Smrj caddr_t, int *); 1313446Smrj static int ppb_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 1326313Skrishnae static void ppb_peekpoke_cb(dev_info_t *, ddi_fm_error_t *); 1333446Smrj 1343446Smrj struct cb_ops ppb_cb_ops = { 1353446Smrj ppb_open, /* open */ 1363446Smrj ppb_close, /* close */ 1373446Smrj nodev, /* strategy */ 1383446Smrj nodev, /* print */ 1393446Smrj nodev, /* dump */ 1403446Smrj nodev, /* read */ 1413446Smrj nodev, /* write */ 1423446Smrj ppb_ioctl, /* ioctl */ 1433446Smrj nodev, /* devmap */ 1443446Smrj nodev, /* mmap */ 1453446Smrj nodev, /* segmap */ 1463446Smrj nochpoll, /* poll */ 1473446Smrj ppb_prop_op, /* cb_prop_op */ 1483446Smrj NULL, /* streamtab */ 1493446Smrj D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 1503446Smrj CB_REV, /* rev */ 1513446Smrj nodev, /* int (*cb_aread)() */ 1523446Smrj nodev /* int (*cb_awrite)() */ 1533446Smrj }; 1543446Smrj 1553446Smrj 1563446Smrj static int ppb_probe(dev_info_t *); 1573446Smrj static int ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); 1583446Smrj static int ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd); 1593446Smrj 1603446Smrj struct dev_ops ppb_ops = { 1613446Smrj DEVO_REV, /* devo_rev */ 1623446Smrj 0, /* refcnt */ 1633446Smrj ppb_info, /* info */ 1643446Smrj nulldev, /* identify */ 1653446Smrj ppb_probe, /* probe */ 1663446Smrj ppb_attach, /* attach */ 1673446Smrj ppb_detach, /* detach */ 1683446Smrj nulldev, /* reset */ 1693446Smrj &ppb_cb_ops, /* driver operations */ 1707656SSherry.Moore@Sun.COM &ppb_bus_ops, /* bus operations */ 1717656SSherry.Moore@Sun.COM NULL, /* power */ 1727656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */ 1733446Smrj }; 1743446Smrj 1753446Smrj /* 1763446Smrj * Module linkage information for the kernel. 1773446Smrj */ 1783446Smrj 1793446Smrj static struct modldrv modldrv = { 1803446Smrj &mod_driverops, /* Type of module */ 18110923SEvan.Yan@Sun.COM "Standard PCI to PCI bridge nexus driver", 1823446Smrj &ppb_ops, /* driver ops */ 1833446Smrj }; 1843446Smrj 1853446Smrj static struct modlinkage modlinkage = { 1863446Smrj MODREV_1, 1873446Smrj (void *)&modldrv, 1883446Smrj NULL 1893446Smrj }; 1903446Smrj 1913446Smrj /* 1923446Smrj * soft state pointer and structure template: 1933446Smrj */ 1943446Smrj static void *ppb_state; 1953446Smrj 1963446Smrj typedef struct { 1973446Smrj dev_info_t *dip; 1983446Smrj int ppb_fmcap; 1993446Smrj ddi_iblock_cookie_t ppb_fm_ibc; 20010923SEvan.Yan@Sun.COM kmutex_t ppb_mutex; 2013446Smrj kmutex_t ppb_peek_poke_mutex; 2023446Smrj kmutex_t ppb_err_mutex; 2033446Smrj 2043446Smrj /* 2053446Smrj * cpr support: 2063446Smrj */ 2073446Smrj uint_t config_state_index; 2083446Smrj struct { 2093446Smrj dev_info_t *dip; 2103446Smrj ushort_t command; 2113446Smrj uchar_t cache_line_size; 2123446Smrj uchar_t latency_timer; 2133446Smrj uchar_t header_type; 2143446Smrj uchar_t sec_latency_timer; 2153446Smrj ushort_t bridge_control; 2163446Smrj } config_state[PCI_MAX_CHILDREN]; 2176313Skrishnae 2189921SKrishna.Elango@Sun.COM uint16_t parent_bus; 2193446Smrj } ppb_devstate_t; 2203446Smrj 2213446Smrj 2223446Smrj /* 2233446Smrj * forward function declarations: 2243446Smrj */ 2253446Smrj static void ppb_removechild(dev_info_t *); 2263446Smrj static int ppb_initchild(dev_info_t *child); 2273446Smrj static void ppb_save_config_regs(ppb_devstate_t *ppb_p); 2283446Smrj static void ppb_restore_config_regs(ppb_devstate_t *ppb_p); 2293446Smrj static boolean_t ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl); 2303446Smrj static int ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd); 2313446Smrj 2323446Smrj /* 2333446Smrj * for <cmd> in ppb_ht_msimap_set 2343446Smrj */ 2353446Smrj #define HT_MSIMAP_ENABLE 1 2363446Smrj #define HT_MSIMAP_DISABLE 0 2373446Smrj 2383446Smrj 2393446Smrj int 2403446Smrj _init(void) 2413446Smrj { 2423446Smrj int e; 2433446Smrj 2443446Smrj if ((e = ddi_soft_state_init(&ppb_state, sizeof (ppb_devstate_t), 2453446Smrj 1)) == 0 && (e = mod_install(&modlinkage)) != 0) 2463446Smrj ddi_soft_state_fini(&ppb_state); 2473446Smrj return (e); 2483446Smrj } 2493446Smrj 2503446Smrj int 2513446Smrj _fini(void) 2523446Smrj { 2533446Smrj int e; 2543446Smrj 2553446Smrj if ((e = mod_remove(&modlinkage)) == 0) 2563446Smrj ddi_soft_state_fini(&ppb_state); 2573446Smrj return (e); 2583446Smrj } 2593446Smrj 2603446Smrj int 2613446Smrj _info(struct modinfo *modinfop) 2623446Smrj { 2633446Smrj return (mod_info(&modlinkage, modinfop)); 2643446Smrj } 2653446Smrj 2663446Smrj /*ARGSUSED*/ 2673446Smrj static int 2683446Smrj ppb_probe(dev_info_t *devi) 2693446Smrj { 2703446Smrj return (DDI_PROBE_SUCCESS); 2713446Smrj } 2723446Smrj 2733446Smrj /*ARGSUSED*/ 2743446Smrj static int 2753446Smrj ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 2763446Smrj { 2776313Skrishnae dev_info_t *root = ddi_root_node(); 2783446Smrj int instance; 2793446Smrj ppb_devstate_t *ppb; 2806313Skrishnae dev_info_t *pdip; 2813446Smrj ddi_acc_handle_t config_handle; 2826313Skrishnae char *bus; 28310923SEvan.Yan@Sun.COM int ret; 2843446Smrj 2853446Smrj switch (cmd) { 2863446Smrj case DDI_ATTACH: 2873446Smrj 2883446Smrj /* 2893446Smrj * Make sure the "device_type" property exists. 2903446Smrj */ 2913446Smrj (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi, 2923446Smrj "device_type", "pci"); 2933446Smrj 2943446Smrj /* 2953446Smrj * Allocate and get soft state structure. 2963446Smrj */ 2973446Smrj instance = ddi_get_instance(devi); 2983446Smrj if (ddi_soft_state_zalloc(ppb_state, instance) != DDI_SUCCESS) 2993446Smrj return (DDI_FAILURE); 3003446Smrj ppb = ddi_get_soft_state(ppb_state, instance); 3013446Smrj ppb->dip = devi; 3023446Smrj 3033446Smrj /* 3043446Smrj * don't enable ereports if immediate child of npe 3053446Smrj */ 3063446Smrj if (strcmp(ddi_driver_name(ddi_get_parent(devi)), "npe") == 0) 3073446Smrj ppb->ppb_fmcap = DDI_FM_ERRCB_CAPABLE | 3083446Smrj DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE; 3093446Smrj else 3103446Smrj ppb->ppb_fmcap = DDI_FM_EREPORT_CAPABLE | 3113446Smrj DDI_FM_ERRCB_CAPABLE | DDI_FM_ACCCHK_CAPABLE | 3123446Smrj DDI_FM_DMACHK_CAPABLE; 3133446Smrj 3143446Smrj ddi_fm_init(devi, &ppb->ppb_fmcap, &ppb->ppb_fm_ibc); 31510923SEvan.Yan@Sun.COM mutex_init(&ppb->ppb_mutex, NULL, MUTEX_DRIVER, NULL); 3163446Smrj mutex_init(&ppb->ppb_err_mutex, NULL, MUTEX_DRIVER, 3173446Smrj (void *)ppb->ppb_fm_ibc); 3183446Smrj mutex_init(&ppb->ppb_peek_poke_mutex, NULL, MUTEX_DRIVER, 3193446Smrj (void *)ppb->ppb_fm_ibc); 3203446Smrj 3213446Smrj if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE | 3223446Smrj DDI_FM_EREPORT_CAPABLE)) 3233446Smrj pci_ereport_setup(devi); 3243446Smrj if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE) 3255295Srandyf ddi_fm_handler_register(devi, ppb_fm_callback, NULL); 3263446Smrj 3273446Smrj if (pci_config_setup(devi, &config_handle) != DDI_SUCCESS) { 3283446Smrj if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE) 3293446Smrj ddi_fm_handler_unregister(devi); 3303446Smrj if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE | 3313446Smrj DDI_FM_EREPORT_CAPABLE)) 3323446Smrj pci_ereport_teardown(devi); 3333446Smrj ddi_fm_fini(devi); 3343446Smrj ddi_soft_state_free(ppb_state, instance); 3353446Smrj return (DDI_FAILURE); 3363446Smrj } 3373446Smrj 3389921SKrishna.Elango@Sun.COM ppb->parent_bus = PCIE_PCIECAP_DEV_TYPE_PCI_PSEUDO; 3396313Skrishnae for (pdip = ddi_get_parent(devi); pdip && (pdip != root) && 3406313Skrishnae (ppb->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV); 3416313Skrishnae pdip = ddi_get_parent(pdip)) { 3426313Skrishnae if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, 3436313Skrishnae DDI_PROP_DONTPASS, "device_type", &bus) != 3446313Skrishnae DDI_PROP_SUCCESS) 3456313Skrishnae break; 3466313Skrishnae 3476313Skrishnae if (strcmp(bus, "pciex") == 0) 3486313Skrishnae ppb->parent_bus = 3496313Skrishnae PCIE_PCIECAP_DEV_TYPE_PCIE_DEV; 3506313Skrishnae 3516313Skrishnae ddi_prop_free(bus); 3526313Skrishnae } 3536313Skrishnae 3543446Smrj if (ppb_support_ht_msimap == 1) 3553446Smrj (void) ppb_ht_msimap_set(config_handle, 3563446Smrj HT_MSIMAP_ENABLE); 3573446Smrj else if (ppb_support_ht_msimap == -1) 3583446Smrj (void) ppb_ht_msimap_set(config_handle, 3593446Smrj HT_MSIMAP_DISABLE); 3603446Smrj 3613446Smrj pci_config_teardown(&config_handle); 3623446Smrj 3633446Smrj /* 36410923SEvan.Yan@Sun.COM * Initialize hotplug support on this bus. 3653446Smrj */ 36610923SEvan.Yan@Sun.COM if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) 36710923SEvan.Yan@Sun.COM ret = pcie_init(devi, NULL); 36810923SEvan.Yan@Sun.COM else 36910923SEvan.Yan@Sun.COM ret = pcihp_init(devi); 37010923SEvan.Yan@Sun.COM 37110923SEvan.Yan@Sun.COM if (ret != DDI_SUCCESS) { 3725295Srandyf cmn_err(CE_WARN, 3735295Srandyf "pci: Failed to setup hotplug framework"); 37410923SEvan.Yan@Sun.COM (void) ppb_detach(devi, DDI_DETACH); 37510923SEvan.Yan@Sun.COM return (ret); 37610923SEvan.Yan@Sun.COM } 3773446Smrj 3783446Smrj ddi_report_dev(devi); 3793446Smrj return (DDI_SUCCESS); 3803446Smrj 3813446Smrj case DDI_RESUME: 3823446Smrj 3833446Smrj /* 3843446Smrj * Get the soft state structure for the bridge. 3853446Smrj */ 3863446Smrj ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi)); 3873446Smrj ppb_restore_config_regs(ppb); 3883446Smrj return (DDI_SUCCESS); 3893446Smrj 3903446Smrj default: 3913446Smrj break; 3923446Smrj } 3933446Smrj return (DDI_FAILURE); 3943446Smrj } 3953446Smrj 3963446Smrj /*ARGSUSED*/ 3973446Smrj static int 3983446Smrj ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 3993446Smrj { 4003446Smrj ppb_devstate_t *ppb; 40110923SEvan.Yan@Sun.COM int ret; 4023446Smrj 4033446Smrj switch (cmd) { 4043446Smrj case DDI_DETACH: 4053446Smrj (void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type"); 4063446Smrj 4073446Smrj ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi)); 4083446Smrj if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE) 4093446Smrj ddi_fm_handler_unregister(devi); 4103446Smrj if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE | 4113446Smrj DDI_FM_EREPORT_CAPABLE)) 4123446Smrj pci_ereport_teardown(devi); 41310923SEvan.Yan@Sun.COM 41410923SEvan.Yan@Sun.COM /* 41510923SEvan.Yan@Sun.COM * Uninitialize hotplug support on this bus. 41610923SEvan.Yan@Sun.COM */ 41710923SEvan.Yan@Sun.COM ret = (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) ? 41810923SEvan.Yan@Sun.COM pcie_uninit(devi) : pcihp_uninit(devi); 41910923SEvan.Yan@Sun.COM if (ret != DDI_SUCCESS) 42010923SEvan.Yan@Sun.COM return (DDI_FAILURE); 42110923SEvan.Yan@Sun.COM 4223446Smrj mutex_destroy(&ppb->ppb_peek_poke_mutex); 4233446Smrj mutex_destroy(&ppb->ppb_err_mutex); 42410923SEvan.Yan@Sun.COM mutex_destroy(&ppb->ppb_mutex); 4253446Smrj ddi_fm_fini(devi); 4263446Smrj 4273446Smrj /* 4283446Smrj * And finally free the per-pci soft state. 4293446Smrj */ 4303446Smrj ddi_soft_state_free(ppb_state, ddi_get_instance(devi)); 4313446Smrj 4323446Smrj return (DDI_SUCCESS); 4333446Smrj 4343446Smrj case DDI_SUSPEND: 4353446Smrj ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi)); 4363446Smrj ppb_save_config_regs(ppb); 4373446Smrj return (DDI_SUCCESS); 4383446Smrj 4393446Smrj default: 4403446Smrj break; 4413446Smrj } 4423446Smrj return (DDI_FAILURE); 4433446Smrj } 4443446Smrj 4453446Smrj /*ARGSUSED*/ 4463446Smrj static int 4473446Smrj ppb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 4483446Smrj off_t offset, off_t len, caddr_t *vaddrp) 4493446Smrj { 4503446Smrj dev_info_t *pdip; 451*11236SStephen.Hanson@Sun.COM ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state, 452*11236SStephen.Hanson@Sun.COM ddi_get_instance(dip)); 4533446Smrj 454*11236SStephen.Hanson@Sun.COM if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") == 0) { 455*11236SStephen.Hanson@Sun.COM ddi_acc_impl_t *hdlp = 456*11236SStephen.Hanson@Sun.COM (ddi_acc_impl_t *)(mp->map_handlep)->ah_platform_private; 457*11236SStephen.Hanson@Sun.COM hdlp->ahi_err_mutexp = &ppb->ppb_err_mutex; 458*11236SStephen.Hanson@Sun.COM hdlp->ahi_peekpoke_mutexp = &ppb->ppb_peek_poke_mutex; 459*11236SStephen.Hanson@Sun.COM hdlp->ahi_scan_dip = dip; 460*11236SStephen.Hanson@Sun.COM hdlp->ahi_scan = ppb_peekpoke_cb; 461*11236SStephen.Hanson@Sun.COM } 4623446Smrj pdip = (dev_info_t *)DEVI(dip)->devi_parent; 4633446Smrj return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip, 4645295Srandyf rdip, mp, offset, len, vaddrp)); 4653446Smrj } 4663446Smrj 4673446Smrj /*ARGSUSED*/ 4683446Smrj static int 4693446Smrj ppb_ctlops(dev_info_t *dip, dev_info_t *rdip, 4703446Smrj ddi_ctl_enum_t ctlop, void *arg, void *result) 4713446Smrj { 4723446Smrj pci_regspec_t *drv_regp; 4733446Smrj int reglen; 4743446Smrj int rn; 4753446Smrj int totreg; 4766313Skrishnae ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state, 4776313Skrishnae ddi_get_instance(dip)); 4786313Skrishnae struct detachspec *dsp; 4795295Srandyf struct attachspec *asp; 4803446Smrj 4813446Smrj switch (ctlop) { 4823446Smrj case DDI_CTLOPS_REPORTDEV: 4833446Smrj if (rdip == (dev_info_t *)0) 4843446Smrj return (DDI_FAILURE); 4853446Smrj cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n", 4863446Smrj ddi_node_name(rdip), ddi_get_name_addr(rdip), 4873446Smrj ddi_driver_name(rdip), 4883446Smrj ddi_get_instance(rdip)); 4893446Smrj return (DDI_SUCCESS); 4903446Smrj 4913446Smrj case DDI_CTLOPS_INITCHILD: 4923446Smrj return (ppb_initchild((dev_info_t *)arg)); 4933446Smrj 4943446Smrj case DDI_CTLOPS_UNINITCHILD: 4953446Smrj ppb_removechild((dev_info_t *)arg); 4963446Smrj return (DDI_SUCCESS); 4973446Smrj 4983446Smrj case DDI_CTLOPS_SIDDEV: 4993446Smrj return (DDI_SUCCESS); 5003446Smrj 5013446Smrj case DDI_CTLOPS_REGSIZE: 5023446Smrj case DDI_CTLOPS_NREGS: 5033446Smrj if (rdip == (dev_info_t *)0) 5043446Smrj return (DDI_FAILURE); 5053446Smrj break; 5063446Smrj 5075295Srandyf /* X86 systems support PME wakeup from suspend */ 5085295Srandyf case DDI_CTLOPS_ATTACH: 5096313Skrishnae if (!pcie_is_child(dip, rdip)) 5106313Skrishnae return (DDI_SUCCESS); 5116313Skrishnae 5125295Srandyf asp = (struct attachspec *)arg; 5136313Skrishnae if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) && 5146313Skrishnae (asp->when == DDI_POST) && (asp->result == DDI_SUCCESS)) 5156313Skrishnae pf_init(rdip, (void *)ppb->ppb_fm_ibc, asp->cmd); 5166313Skrishnae 5175295Srandyf if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE) 5185295Srandyf if (pci_pre_resume(rdip) != DDI_SUCCESS) 5195295Srandyf return (DDI_FAILURE); 5205295Srandyf 5216313Skrishnae return (DDI_SUCCESS); 5225295Srandyf 5235295Srandyf case DDI_CTLOPS_DETACH: 5246313Skrishnae if (!pcie_is_child(dip, rdip)) 5256313Skrishnae return (DDI_SUCCESS); 5266313Skrishnae 5276313Skrishnae dsp = (struct detachspec *)arg; 5286313Skrishnae if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) && 5296313Skrishnae (dsp->when == DDI_PRE)) 5306313Skrishnae pf_fini(rdip, dsp->cmd); 5316313Skrishnae 5326313Skrishnae if (dsp->cmd == DDI_SUSPEND && dsp->when == DDI_POST) 5335295Srandyf if (pci_post_suspend(rdip) != DDI_SUCCESS) 5345295Srandyf return (DDI_FAILURE); 5356313Skrishnae 5366313Skrishnae return (DDI_SUCCESS); 5375295Srandyf 5383446Smrj case DDI_CTLOPS_PEEK: 5393446Smrj case DDI_CTLOPS_POKE: 5403446Smrj if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") != 0) 5413446Smrj return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 5423446Smrj return (pci_peekpoke_check(dip, rdip, ctlop, arg, result, 5433446Smrj ddi_ctlops, &ppb->ppb_err_mutex, 5446313Skrishnae &ppb->ppb_peek_poke_mutex, ppb_peekpoke_cb)); 5453446Smrj 5463446Smrj default: 5473446Smrj return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 5483446Smrj } 5493446Smrj 5503446Smrj *(int *)result = 0; 5513446Smrj if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, 5525295Srandyf DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg", 5535295Srandyf (caddr_t)&drv_regp, ®len) != DDI_SUCCESS) 5543446Smrj return (DDI_FAILURE); 5553446Smrj 5563446Smrj totreg = reglen / sizeof (pci_regspec_t); 5573446Smrj if (ctlop == DDI_CTLOPS_NREGS) 5583446Smrj *(int *)result = totreg; 5593446Smrj else if (ctlop == DDI_CTLOPS_REGSIZE) { 5603446Smrj rn = *(int *)arg; 5613446Smrj if (rn >= totreg) { 5623446Smrj kmem_free(drv_regp, reglen); 5633446Smrj return (DDI_FAILURE); 5643446Smrj } 5653446Smrj *(off_t *)result = drv_regp[rn].pci_size_low; 5663446Smrj } 5673446Smrj 5683446Smrj kmem_free(drv_regp, reglen); 5693446Smrj return (DDI_SUCCESS); 5703446Smrj } 5713446Smrj 5723446Smrj static int 5733446Smrj ppb_name_child(dev_info_t *child, char *name, int namelen) 5743446Smrj { 5753446Smrj pci_regspec_t *pci_rp; 5763446Smrj uint_t slot, func; 5773446Smrj char **unit_addr; 5783446Smrj uint_t n; 5793446Smrj 5803446Smrj /* 5813446Smrj * For .conf nodes, use unit-address property as name 5823446Smrj */ 5833446Smrj if (ndi_dev_is_persistent_node(child) == 0) { 5843446Smrj if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 5853446Smrj DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != 5863446Smrj DDI_PROP_SUCCESS) { 5873446Smrj cmn_err(CE_WARN, 5883446Smrj "cannot find unit-address in %s.conf", 5893446Smrj ddi_driver_name(child)); 5903446Smrj return (DDI_FAILURE); 5913446Smrj } 5923446Smrj if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 5933446Smrj cmn_err(CE_WARN, "unit-address property in %s.conf" 5943446Smrj " not well-formed", ddi_driver_name(child)); 5953446Smrj ddi_prop_free(unit_addr); 5963446Smrj return (DDI_SUCCESS); 5973446Smrj } 5983446Smrj (void) snprintf(name, namelen, "%s", *unit_addr); 5993446Smrj ddi_prop_free(unit_addr); 6003446Smrj return (DDI_SUCCESS); 6013446Smrj } 6023446Smrj 6033446Smrj /* get child "reg" property */ 6043446Smrj if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 6053446Smrj DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) { 6063446Smrj return (DDI_FAILURE); 6073446Smrj } 6083446Smrj 6093446Smrj /* copy the device identifications */ 6103446Smrj slot = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 6113446Smrj func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 6123446Smrj 6133446Smrj if (func != 0) 6143446Smrj (void) snprintf(name, namelen, "%x,%x", slot, func); 6153446Smrj else 6163446Smrj (void) snprintf(name, namelen, "%x", slot); 6173446Smrj 6183446Smrj ddi_prop_free(pci_rp); 6193446Smrj return (DDI_SUCCESS); 6203446Smrj } 6213446Smrj 6223446Smrj static int 6233446Smrj ppb_initchild(dev_info_t *child) 6243446Smrj { 6253446Smrj struct ddi_parent_private_data *pdptr; 6266313Skrishnae ppb_devstate_t *ppb; 6273446Smrj char name[MAXNAMELEN]; 6283446Smrj ddi_acc_handle_t config_handle; 6293446Smrj ushort_t command_preserve, command; 6303446Smrj 6316313Skrishnae ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state, 6326313Skrishnae ddi_get_instance(ddi_get_parent(child))); 6336313Skrishnae 6343446Smrj if (ppb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS) 6353446Smrj return (DDI_FAILURE); 6363446Smrj ddi_set_name_addr(child, name); 6373446Smrj 6383446Smrj /* 6393446Smrj * Pseudo nodes indicate a prototype node with per-instance 6403446Smrj * properties to be merged into the real h/w device node. 6413446Smrj * The interpretation of the unit-address is DD[,F] 6423446Smrj * where DD is the device id and F is the function. 6433446Smrj */ 6443446Smrj if (ndi_dev_is_persistent_node(child) == 0) { 6453446Smrj extern int pci_allow_pseudo_children; 6463446Smrj 6473446Smrj ddi_set_parent_data(child, NULL); 6483446Smrj 6493446Smrj /* 6503446Smrj * Try to merge the properties from this prototype 6513446Smrj * node into real h/w nodes. 6523446Smrj */ 6533446Smrj if (ndi_merge_node(child, ppb_name_child) == DDI_SUCCESS) { 6543446Smrj /* 6553446Smrj * Merged ok - return failure to remove the node. 6563446Smrj */ 6573446Smrj ddi_set_name_addr(child, NULL); 6583446Smrj return (DDI_FAILURE); 6593446Smrj } 6603446Smrj 6613446Smrj /* workaround for ddivs to run under PCI */ 6623446Smrj if (pci_allow_pseudo_children) 6633446Smrj return (DDI_SUCCESS); 6643446Smrj 6653446Smrj /* 6663446Smrj * The child was not merged into a h/w node, 6673446Smrj * but there's not much we can do with it other 6683446Smrj * than return failure to cause the node to be removed. 6693446Smrj */ 6703446Smrj cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", 6713446Smrj ddi_driver_name(child), ddi_get_name_addr(child), 6723446Smrj ddi_driver_name(child)); 6733446Smrj ddi_set_name_addr(child, NULL); 6743446Smrj return (DDI_NOT_WELL_FORMED); 6753446Smrj } 6763446Smrj 6776313Skrishnae ddi_set_parent_data(child, NULL); 6786313Skrishnae 6796313Skrishnae /* 6806313Skrishnae * PCIe FMA specific 6816313Skrishnae * 6826313Skrishnae * Note: parent_data for parent is created only if this is PCI-E 6836313Skrishnae * platform, for which, SG take a different route to handle device 6846313Skrishnae * errors. 6856313Skrishnae */ 6866313Skrishnae if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) { 6876313Skrishnae if (pcie_init_bus(child) == NULL) 6886313Skrishnae return (DDI_FAILURE); 6896313Skrishnae } 6906313Skrishnae 6913446Smrj /* transfer select properties from PROM to kernel */ 6925295Srandyf if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS, 6935295Srandyf "interrupts", -1) != -1) { 6943446Smrj pdptr = kmem_zalloc((sizeof (struct ddi_parent_private_data) + 6953446Smrj sizeof (struct intrspec)), KM_SLEEP); 6963446Smrj pdptr->par_intr = (struct intrspec *)(pdptr + 1); 6973446Smrj pdptr->par_nintr = 1; 6983446Smrj ddi_set_parent_data(child, pdptr); 6993446Smrj } else 7003446Smrj ddi_set_parent_data(child, NULL); 7013446Smrj 7023446Smrj if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) 7033446Smrj return (DDI_FAILURE); 7043446Smrj 7053446Smrj /* 7063446Smrj * Support for the "command-preserve" property. 7073446Smrj */ 7083446Smrj command_preserve = ddi_prop_get_int(DDI_DEV_T_ANY, child, 7093446Smrj DDI_PROP_DONTPASS, "command-preserve", 0); 7103446Smrj command = pci_config_get16(config_handle, PCI_CONF_COMM); 7113446Smrj command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB); 7123446Smrj command |= (ppb_command_default & ~command_preserve); 7133446Smrj pci_config_put16(config_handle, PCI_CONF_COMM, command); 7143446Smrj 7153446Smrj pci_config_teardown(&config_handle); 7163446Smrj return (DDI_SUCCESS); 7173446Smrj } 7183446Smrj 7193446Smrj static void 7203446Smrj ppb_removechild(dev_info_t *dip) 7213446Smrj { 7223446Smrj struct ddi_parent_private_data *pdptr; 7236313Skrishnae ppb_devstate_t *ppb; 7243446Smrj 7256313Skrishnae ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state, 7266313Skrishnae ddi_get_instance(ddi_get_parent(dip))); 7276313Skrishnae 7286313Skrishnae if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) 7296313Skrishnae pcie_fini_bus(dip); 7306313Skrishnae else if ((pdptr = ddi_get_parent_data(dip)) != NULL) { 7313446Smrj kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec))); 7323446Smrj ddi_set_parent_data(dip, NULL); 7333446Smrj } 7343446Smrj ddi_set_name_addr(dip, NULL); 7353446Smrj 7363446Smrj /* 7373446Smrj * Strip the node to properly convert it back to prototype form 7383446Smrj */ 7393446Smrj ddi_remove_minor_node(dip, NULL); 7403446Smrj 7413446Smrj impl_rem_dev_props(dip); 7423446Smrj } 7433446Smrj 7443446Smrj /* 7453446Smrj * ppb_save_config_regs 7463446Smrj * 7473446Smrj * This routine saves the state of the configuration registers of all 7483446Smrj * the child nodes of each PBM. 7493446Smrj * 7503446Smrj * used by: ppb_detach() on suspends 7513446Smrj * 7523446Smrj * return value: none 7533446Smrj */ 7543446Smrj static void 7553446Smrj ppb_save_config_regs(ppb_devstate_t *ppb_p) 7563446Smrj { 7573446Smrj int i; 7583446Smrj dev_info_t *dip; 7593446Smrj ddi_acc_handle_t config_handle; 7603446Smrj 7613446Smrj for (i = 0, dip = ddi_get_child(ppb_p->dip); dip != NULL; 7625295Srandyf i++, dip = ddi_get_next_sibling(dip)) { 7633446Smrj 7643446Smrj if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) { 7653446Smrj cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n", 7665295Srandyf ddi_driver_name(ppb_p->dip), 7675295Srandyf ddi_get_instance(ppb_p->dip), 7685295Srandyf ddi_driver_name(dip), 7695295Srandyf ddi_get_instance(dip)); 7703446Smrj continue; 7713446Smrj } 7723446Smrj 7733446Smrj ppb_p->config_state[i].dip = dip; 7743446Smrj ppb_p->config_state[i].command = 7755295Srandyf pci_config_get16(config_handle, PCI_CONF_COMM); 7763446Smrj pci_config_teardown(&config_handle); 7773446Smrj } 7783446Smrj ppb_p->config_state_index = i; 7793446Smrj } 7803446Smrj 7813446Smrj 7823446Smrj /* 7833446Smrj * ppb_restore_config_regs 7843446Smrj * 7853446Smrj * This routine restores the state of the configuration registers of all 7863446Smrj * the child nodes of each PBM. 7873446Smrj * 7883446Smrj * used by: ppb_attach() on resume 7893446Smrj * 7903446Smrj * return value: none 7913446Smrj */ 7923446Smrj static void 7933446Smrj ppb_restore_config_regs(ppb_devstate_t *ppb_p) 7943446Smrj { 7953446Smrj int i; 7963446Smrj dev_info_t *dip; 7973446Smrj ddi_acc_handle_t config_handle; 7983446Smrj 7993446Smrj for (i = 0; i < ppb_p->config_state_index; i++) { 8003446Smrj dip = ppb_p->config_state[i].dip; 8013446Smrj if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) { 8023446Smrj cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n", 8035295Srandyf ddi_driver_name(ppb_p->dip), 8045295Srandyf ddi_get_instance(ppb_p->dip), 8055295Srandyf ddi_driver_name(dip), 8065295Srandyf ddi_get_instance(dip)); 8073446Smrj continue; 8083446Smrj } 8093446Smrj pci_config_put16(config_handle, PCI_CONF_COMM, 8105295Srandyf ppb_p->config_state[i].command); 8113446Smrj pci_config_teardown(&config_handle); 8123446Smrj } 8133446Smrj } 8143446Smrj 8153446Smrj 8163446Smrj static boolean_t 8173446Smrj ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl) 8183446Smrj { 8199970SJimmy.Vetayases@Sun.COM uint16_t ptr; 8203446Smrj 8219970SJimmy.Vetayases@Sun.COM if (pci_htcap_locate(cfg_hdl, 8229970SJimmy.Vetayases@Sun.COM PCI_HTCAP_TYPE_MASK | PCI_HTCAP_MSIMAP_ENABLE_MASK, 8239970SJimmy.Vetayases@Sun.COM PCI_HTCAP_MSIMAP_TYPE | PCI_HTCAP_MSIMAP_ENABLE, &ptr) != 8249970SJimmy.Vetayases@Sun.COM DDI_SUCCESS) 8253446Smrj return (B_FALSE); 8263446Smrj 8273446Smrj return (B_TRUE); 8283446Smrj } 8293446Smrj 8303446Smrj 8313446Smrj static int 8323446Smrj ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd) 8333446Smrj { 8349970SJimmy.Vetayases@Sun.COM uint16_t ptr; 8353446Smrj uint16_t reg; 8363446Smrj 8379970SJimmy.Vetayases@Sun.COM if (pci_htcap_locate(cfg_hdl, PCI_HTCAP_TYPE_MASK, 8389970SJimmy.Vetayases@Sun.COM PCI_HTCAP_MSIMAP_TYPE, &ptr) != DDI_SUCCESS) 8393446Smrj return (0); 8403446Smrj 8413446Smrj reg = pci_config_get16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF); 8423446Smrj switch (cmd) { 8433446Smrj case HT_MSIMAP_ENABLE: 8449970SJimmy.Vetayases@Sun.COM reg |= PCI_HTCAP_MSIMAP_ENABLE; 8453446Smrj break; 8463446Smrj case HT_MSIMAP_DISABLE: 8473446Smrj default: 8489970SJimmy.Vetayases@Sun.COM reg &= ~(uint16_t)PCI_HTCAP_MSIMAP_ENABLE; 8493446Smrj } 8503446Smrj 8513446Smrj pci_config_put16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF, reg); 8523446Smrj return (1); 8533446Smrj } 8543446Smrj 8553446Smrj 8563446Smrj /* 8573446Smrj * intercept certain interrupt services to handle special cases 8583446Smrj */ 8593446Smrj static int 8603446Smrj ppb_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 8613446Smrj ddi_intr_handle_impl_t *hdlp, void *result) 8623446Smrj { 8633446Smrj ddi_acc_handle_t cfg_hdl; 8643446Smrj int rv = DDI_SUCCESS; 8653446Smrj 8663446Smrj if (intr_op != DDI_INTROP_SUPPORTED_TYPES) 8673446Smrj return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); 8683446Smrj 8693446Smrj DDI_INTR_NEXDBG((CE_CONT, 8703446Smrj "ppb_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n", 8713446Smrj (void *)pdip, (void *)rdip, intr_op, (void *)hdlp)); 8723446Smrj 8733446Smrj /* Fixed interrupt is supported by default */ 8743446Smrj *(int *)result = DDI_INTR_TYPE_FIXED; 8753446Smrj 8763446Smrj if (ppb_support_msi == -1) { 8773446Smrj DDI_INTR_NEXDBG((CE_CONT, 8783446Smrj "ppb_intr_ops: MSI is not allowed\n")); 8793446Smrj goto OUT; 8803446Smrj } 8813446Smrj 8823446Smrj if (ppb_support_msi == 1) { 8833446Smrj DDI_INTR_NEXDBG((CE_CONT, 8843446Smrj "ppb_intr_ops: MSI is always allowed\n")); 8853446Smrj rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result); 8863446Smrj goto OUT; 8873446Smrj } 8883446Smrj 8893446Smrj if (pci_config_setup(pdip, &cfg_hdl) != DDI_SUCCESS) { 8903446Smrj DDI_INTR_NEXDBG((CE_CONT, 8913446Smrj "ppb_intr_ops: pci_config_setup() failed\n")); 8923446Smrj goto OUT; 8933446Smrj } 8943446Smrj 8953446Smrj /* 8963446Smrj * check for hypertransport msi mapping capability 8973446Smrj */ 8983446Smrj if (ppb_ht_msimap_check(cfg_hdl)) { 8993446Smrj DDI_INTR_NEXDBG((CE_CONT, 9003446Smrj "ppb_intr_ops: HT MSI mapping enabled\n")); 9013446Smrj rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result); 9023446Smrj } 9033446Smrj 9043446Smrj /* 9053446Smrj * if we add failure conditions after pci_config_setup, move this to 9063446Smrj * OUT and use an extra flag to indicate the need to teardown cfg_hdl 9073446Smrj */ 9083446Smrj pci_config_teardown(&cfg_hdl); 9093446Smrj 9103446Smrj OUT: 9113446Smrj DDI_INTR_NEXDBG((CE_CONT, 9123446Smrj "ppb_intr_ops: rdip 0x%p, returns supported types: 0x%x\n", 9133446Smrj (void *)rdip, *(int *)result)); 9143446Smrj return (rv); 9153446Smrj } 9163446Smrj 91710923SEvan.Yan@Sun.COM /* ARGSUSED */ 9183446Smrj static int 9193446Smrj ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp) 9203446Smrj { 92110923SEvan.Yan@Sun.COM int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(*devp)); 92210923SEvan.Yan@Sun.COM ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance); 92310923SEvan.Yan@Sun.COM int rv; 92410923SEvan.Yan@Sun.COM 92510923SEvan.Yan@Sun.COM if (ppb_p == NULL) 92610923SEvan.Yan@Sun.COM return (ENXIO); 92710923SEvan.Yan@Sun.COM 92810923SEvan.Yan@Sun.COM /* 92910923SEvan.Yan@Sun.COM * Ioctls will be handled by PCI Express framework for all 93010923SEvan.Yan@Sun.COM * PCIe platforms 93110923SEvan.Yan@Sun.COM */ 93210923SEvan.Yan@Sun.COM if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) { 93310923SEvan.Yan@Sun.COM mutex_enter(&ppb_p->ppb_mutex); 93410923SEvan.Yan@Sun.COM rv = pcie_open(ppb_p->dip, devp, flags, otyp, credp); 93510923SEvan.Yan@Sun.COM mutex_exit(&ppb_p->ppb_mutex); 93610923SEvan.Yan@Sun.COM return (rv); 93710923SEvan.Yan@Sun.COM } 93810923SEvan.Yan@Sun.COM 9393446Smrj return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp)); 9403446Smrj } 9413446Smrj 94210923SEvan.Yan@Sun.COM /* ARGSUSED */ 9433446Smrj static int 9443446Smrj ppb_close(dev_t dev, int flags, int otyp, cred_t *credp) 9453446Smrj { 94610923SEvan.Yan@Sun.COM int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev)); 94710923SEvan.Yan@Sun.COM ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance); 94810923SEvan.Yan@Sun.COM int rv; 94910923SEvan.Yan@Sun.COM 95010923SEvan.Yan@Sun.COM if (ppb_p == NULL) 95110923SEvan.Yan@Sun.COM return (ENXIO); 95210923SEvan.Yan@Sun.COM 95310923SEvan.Yan@Sun.COM mutex_enter(&ppb_p->ppb_mutex); 95410923SEvan.Yan@Sun.COM /* 95510923SEvan.Yan@Sun.COM * Ioctls will be handled by PCI Express framework for all 95610923SEvan.Yan@Sun.COM * PCIe platforms 95710923SEvan.Yan@Sun.COM */ 95810923SEvan.Yan@Sun.COM if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) { 95910923SEvan.Yan@Sun.COM rv = pcie_close(ppb_p->dip, dev, flags, otyp, credp); 96010923SEvan.Yan@Sun.COM mutex_exit(&ppb_p->ppb_mutex); 96110923SEvan.Yan@Sun.COM return (rv); 96210923SEvan.Yan@Sun.COM } 96310923SEvan.Yan@Sun.COM 96410923SEvan.Yan@Sun.COM mutex_exit(&ppb_p->ppb_mutex); 9653446Smrj return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp)); 9663446Smrj } 9673446Smrj 96810923SEvan.Yan@Sun.COM /* 96910923SEvan.Yan@Sun.COM * ppb_ioctl: devctl hotplug controls 97010923SEvan.Yan@Sun.COM */ 97110923SEvan.Yan@Sun.COM /* ARGSUSED */ 9723446Smrj static int 97310923SEvan.Yan@Sun.COM ppb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 97410923SEvan.Yan@Sun.COM int *rvalp) 9753446Smrj { 97610923SEvan.Yan@Sun.COM int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev)); 97710923SEvan.Yan@Sun.COM ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance); 97810923SEvan.Yan@Sun.COM 97910923SEvan.Yan@Sun.COM if (ppb_p == NULL) 98010923SEvan.Yan@Sun.COM return (ENXIO); 98110923SEvan.Yan@Sun.COM 98210923SEvan.Yan@Sun.COM /* 98310923SEvan.Yan@Sun.COM * Ioctls will be handled by PCI Express framework for all 98410923SEvan.Yan@Sun.COM * PCIe platforms 98510923SEvan.Yan@Sun.COM */ 98610923SEvan.Yan@Sun.COM if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) 98710923SEvan.Yan@Sun.COM return (pcie_ioctl(ppb_p->dip, dev, cmd, arg, mode, credp, 98810923SEvan.Yan@Sun.COM rvalp)); 98910923SEvan.Yan@Sun.COM 9903446Smrj return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, credp, 9913446Smrj rvalp)); 9923446Smrj } 9933446Smrj 9943446Smrj static int 99510923SEvan.Yan@Sun.COM ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int flags, 99610923SEvan.Yan@Sun.COM char *name, caddr_t valuep, int *lengthp) 9973446Smrj { 99810923SEvan.Yan@Sun.COM int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev)); 99910923SEvan.Yan@Sun.COM ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance); 100010923SEvan.Yan@Sun.COM 100110923SEvan.Yan@Sun.COM if (ppb_p == NULL) 100210923SEvan.Yan@Sun.COM return (ENXIO); 100310923SEvan.Yan@Sun.COM 100410923SEvan.Yan@Sun.COM if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) 100510923SEvan.Yan@Sun.COM return (pcie_prop_op(dev, dip, prop_op, flags, name, 100610923SEvan.Yan@Sun.COM valuep, lengthp)); 100710923SEvan.Yan@Sun.COM 10083446Smrj return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags, 10093446Smrj name, valuep, lengthp)); 10103446Smrj } 10113446Smrj 10123446Smrj static int 10133446Smrj ppb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 10143446Smrj { 101510923SEvan.Yan@Sun.COM minor_t minor = getminor((dev_t)arg); 101610923SEvan.Yan@Sun.COM int instance = PCI_MINOR_NUM_TO_INSTANCE(minor); 101710923SEvan.Yan@Sun.COM ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance); 101810923SEvan.Yan@Sun.COM 101910923SEvan.Yan@Sun.COM if (ppb_p == NULL) 102010923SEvan.Yan@Sun.COM return (DDI_FAILURE); 102110923SEvan.Yan@Sun.COM 102210923SEvan.Yan@Sun.COM if (ppb_p->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) 102310923SEvan.Yan@Sun.COM return (pcihp_info(dip, cmd, arg, result)); 102410923SEvan.Yan@Sun.COM 102510923SEvan.Yan@Sun.COM switch (cmd) { 102610923SEvan.Yan@Sun.COM default: 102710923SEvan.Yan@Sun.COM return (DDI_FAILURE); 102810923SEvan.Yan@Sun.COM 102910923SEvan.Yan@Sun.COM case DDI_INFO_DEVT2INSTANCE: 103010923SEvan.Yan@Sun.COM *result = (void *)(uintptr_t)instance; 103110923SEvan.Yan@Sun.COM return (DDI_SUCCESS); 103210923SEvan.Yan@Sun.COM 103310923SEvan.Yan@Sun.COM case DDI_INFO_DEVT2DEVINFO: 103410923SEvan.Yan@Sun.COM if (ppb_p == NULL) 103510923SEvan.Yan@Sun.COM return (DDI_FAILURE); 103610923SEvan.Yan@Sun.COM *result = (void *)ppb_p->dip; 103710923SEvan.Yan@Sun.COM return (DDI_SUCCESS); 103810923SEvan.Yan@Sun.COM } 10393446Smrj } 10403446Smrj 10416313Skrishnae void ppb_peekpoke_cb(dev_info_t *dip, ddi_fm_error_t *derr) { 10426313Skrishnae (void) pci_ereport_post(dip, derr, NULL); 10436313Skrishnae } 10446313Skrishnae 10453446Smrj /*ARGSUSED*/ 10463446Smrj static int 10473446Smrj ppb_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap, 10483446Smrj ddi_iblock_cookie_t *ibc) 10493446Smrj { 10503446Smrj ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state, 10513446Smrj ddi_get_instance(dip)); 10523446Smrj 10533446Smrj ASSERT(ibc != NULL); 10543446Smrj *ibc = ppb->ppb_fm_ibc; 10553446Smrj 10563446Smrj return (ppb->ppb_fmcap); 10573446Smrj } 10583446Smrj 10593446Smrj /*ARGSUSED*/ 10603446Smrj static int 10613446Smrj ppb_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used) 10623446Smrj { 10633446Smrj ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state, 10643446Smrj ddi_get_instance(dip)); 10653446Smrj 10663446Smrj mutex_enter(&ppb->ppb_err_mutex); 10673446Smrj pci_ereport_post(dip, derr, NULL); 10683446Smrj mutex_exit(&ppb->ppb_err_mutex); 10693446Smrj return (derr->fme_status); 10703446Smrj } 1071