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 /* 22*9921SKrishna.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> 376313Skrishnae #include <sys/pcie_impl.h> 383446Smrj #include <sys/ddi.h> 393446Smrj #include <sys/sunddi.h> 403446Smrj #include <sys/sunndi.h> 413446Smrj #include <sys/ddifm.h> 423446Smrj #include <sys/ndifm.h> 433446Smrj #include <sys/fm/protocol.h> 443446Smrj #include <sys/hotplug/pci/pcihp.h> 453446Smrj #include <sys/pci_intr_lib.h> 463446Smrj #include <sys/psm.h> 473446Smrj 483446Smrj /* 493446Smrj * The variable controls the default setting of the command register 503446Smrj * for pci devices. See ppb_initchild() for details. 513446Smrj */ 523446Smrj static ushort_t ppb_command_default = PCI_COMM_ME | PCI_COMM_MAE | PCI_COMM_IO; 533446Smrj 543446Smrj 553446Smrj static int ppb_bus_map(dev_info_t *, dev_info_t *, ddi_map_req_t *, 563446Smrj off_t, off_t, caddr_t *); 573446Smrj static int ppb_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, 583446Smrj void *, void *); 593446Smrj static int ppb_fm_init(dev_info_t *, dev_info_t *, int, 603446Smrj ddi_iblock_cookie_t *); 613446Smrj static int ppb_fm_callback(dev_info_t *, ddi_fm_error_t *, const void *); 623446Smrj static int ppb_intr_ops(dev_info_t *, dev_info_t *, ddi_intr_op_t, 633446Smrj ddi_intr_handle_impl_t *, void *); 643446Smrj 653446Smrj /* 663446Smrj * ppb_support_msi: Flag that controls MSI support across P2P Bridges. 673446Smrj * By default, MSI is not supported except for special cases like HT 683446Smrj * bridges/tunnels that have HT MSI mapping enabled. 693446Smrj * 703446Smrj * However, MSI support behavior can be patched on a system by changing 713446Smrj * the value of this flag as shown below:- 723446Smrj * 0 = default value, MSI is allowed by this driver for special cases 733446Smrj * 1 = MSI supported without any checks for this driver 743446Smrj * -1 = MSI not supported at all 753446Smrj */ 763446Smrj int ppb_support_msi = 0; 773446Smrj 783446Smrj /* 793446Smrj * Controls the usage of the Hypertransport MSI mapping capability 803446Smrj * 0 = default value, leave hardware function as it is 813446Smrj * 1 = always enable HT MSI mapping 823446Smrj * -1 = always disable HT MSI mapping 833446Smrj */ 843446Smrj int ppb_support_ht_msimap = 0; 853446Smrj 863446Smrj /* 873446Smrj * masks and values for the upper 16-bits of hypertransport cap headers 883446Smrj */ 893446Smrj #define PCI_CAP_HT_MSIMAP_TYPE 0xA800 903446Smrj #define PCI_CAP_HT_MSIMAP_TYPE_MASK 0xFF00 913446Smrj #define PCI_CAP_HT_MSIMAP_ENABLE 0x0001 923446Smrj #define PCI_CAP_HT_MSIMAP_ENABLE_MASK 0x0001 933446Smrj 943446Smrj 953446Smrj struct bus_ops ppb_bus_ops = { 963446Smrj BUSO_REV, 973446Smrj ppb_bus_map, 983446Smrj 0, 993446Smrj 0, 1003446Smrj 0, 1013446Smrj i_ddi_map_fault, 1023446Smrj ddi_dma_map, 1033446Smrj ddi_dma_allochdl, 1043446Smrj ddi_dma_freehdl, 1053446Smrj ddi_dma_bindhdl, 1063446Smrj ddi_dma_unbindhdl, 1073446Smrj ddi_dma_flush, 1083446Smrj ddi_dma_win, 1093446Smrj ddi_dma_mctl, 1103446Smrj ppb_ctlops, 1113446Smrj ddi_bus_prop_op, 1123446Smrj 0, /* (*bus_get_eventcookie)(); */ 1133446Smrj 0, /* (*bus_add_eventcall)(); */ 1143446Smrj 0, /* (*bus_remove_eventcall)(); */ 1153446Smrj 0, /* (*bus_post_event)(); */ 1163446Smrj 0, /* (*bus_intr_ctl)(); */ 1173446Smrj 0, /* (*bus_config)(); */ 1183446Smrj 0, /* (*bus_unconfig)(); */ 1193446Smrj ppb_fm_init, /* (*bus_fm_init)(); */ 1203446Smrj NULL, /* (*bus_fm_fini)(); */ 1213446Smrj NULL, /* (*bus_fm_access_enter)(); */ 1223446Smrj NULL, /* (*bus_fm_access_exit)(); */ 1233446Smrj NULL, /* (*bus_power)(); */ 1243446Smrj ppb_intr_ops /* (*bus_intr_op)(); */ 1253446Smrj }; 1263446Smrj 1273446Smrj /* 1283446Smrj * The goal here is to leverage off of the pcihp.c source without making 1293446Smrj * changes to it. Call into it's cb_ops directly if needed. 1303446Smrj */ 1313446Smrj static int ppb_open(dev_t *, int, int, cred_t *); 1323446Smrj static int ppb_close(dev_t, int, int, cred_t *); 1333446Smrj static int ppb_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 1343446Smrj static int ppb_prop_op(dev_t, dev_info_t *, ddi_prop_op_t, int, char *, 1353446Smrj caddr_t, int *); 1363446Smrj static int ppb_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 1376313Skrishnae static void ppb_peekpoke_cb(dev_info_t *, ddi_fm_error_t *); 1383446Smrj 1393446Smrj struct cb_ops ppb_cb_ops = { 1403446Smrj ppb_open, /* open */ 1413446Smrj ppb_close, /* close */ 1423446Smrj nodev, /* strategy */ 1433446Smrj nodev, /* print */ 1443446Smrj nodev, /* dump */ 1453446Smrj nodev, /* read */ 1463446Smrj nodev, /* write */ 1473446Smrj ppb_ioctl, /* ioctl */ 1483446Smrj nodev, /* devmap */ 1493446Smrj nodev, /* mmap */ 1503446Smrj nodev, /* segmap */ 1513446Smrj nochpoll, /* poll */ 1523446Smrj ppb_prop_op, /* cb_prop_op */ 1533446Smrj NULL, /* streamtab */ 1543446Smrj D_NEW | D_MP | D_HOTPLUG, /* Driver compatibility flag */ 1553446Smrj CB_REV, /* rev */ 1563446Smrj nodev, /* int (*cb_aread)() */ 1573446Smrj nodev /* int (*cb_awrite)() */ 1583446Smrj }; 1593446Smrj 1603446Smrj 1613446Smrj static int ppb_probe(dev_info_t *); 1623446Smrj static int ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); 1633446Smrj static int ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd); 1643446Smrj 1653446Smrj struct dev_ops ppb_ops = { 1663446Smrj DEVO_REV, /* devo_rev */ 1673446Smrj 0, /* refcnt */ 1683446Smrj ppb_info, /* info */ 1693446Smrj nulldev, /* identify */ 1703446Smrj ppb_probe, /* probe */ 1713446Smrj ppb_attach, /* attach */ 1723446Smrj ppb_detach, /* detach */ 1733446Smrj nulldev, /* reset */ 1743446Smrj &ppb_cb_ops, /* driver operations */ 1757656SSherry.Moore@Sun.COM &ppb_bus_ops, /* bus operations */ 1767656SSherry.Moore@Sun.COM NULL, /* power */ 1777656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */ 1783446Smrj }; 1793446Smrj 1803446Smrj /* 1813446Smrj * Module linkage information for the kernel. 1823446Smrj */ 1833446Smrj 1843446Smrj static struct modldrv modldrv = { 1853446Smrj &mod_driverops, /* Type of module */ 1867542SRichard.Bean@Sun.COM "PCI to PCI bridge nexus driver", 1873446Smrj &ppb_ops, /* driver ops */ 1883446Smrj }; 1893446Smrj 1903446Smrj static struct modlinkage modlinkage = { 1913446Smrj MODREV_1, 1923446Smrj (void *)&modldrv, 1933446Smrj NULL 1943446Smrj }; 1953446Smrj 1963446Smrj /* 1973446Smrj * soft state pointer and structure template: 1983446Smrj */ 1993446Smrj static void *ppb_state; 2003446Smrj 2013446Smrj typedef struct { 2023446Smrj dev_info_t *dip; 2033446Smrj int ppb_fmcap; 2043446Smrj ddi_iblock_cookie_t ppb_fm_ibc; 2053446Smrj kmutex_t ppb_peek_poke_mutex; 2063446Smrj kmutex_t ppb_err_mutex; 2073446Smrj 2083446Smrj /* 2093446Smrj * cpr support: 2103446Smrj */ 2113446Smrj uint_t config_state_index; 2123446Smrj struct { 2133446Smrj dev_info_t *dip; 2143446Smrj ushort_t command; 2153446Smrj uchar_t cache_line_size; 2163446Smrj uchar_t latency_timer; 2173446Smrj uchar_t header_type; 2183446Smrj uchar_t sec_latency_timer; 2193446Smrj ushort_t bridge_control; 2203446Smrj } config_state[PCI_MAX_CHILDREN]; 2216313Skrishnae 222*9921SKrishna.Elango@Sun.COM uint16_t parent_bus; 2233446Smrj } ppb_devstate_t; 2243446Smrj 2253446Smrj 2263446Smrj /* 2273446Smrj * forward function declarations: 2283446Smrj */ 2293446Smrj static void ppb_removechild(dev_info_t *); 2303446Smrj static int ppb_initchild(dev_info_t *child); 2313446Smrj static void ppb_save_config_regs(ppb_devstate_t *ppb_p); 2323446Smrj static void ppb_restore_config_regs(ppb_devstate_t *ppb_p); 2333446Smrj static uint8_t ppb_find_ht_cap(ddi_acc_handle_t cfg_hdl, uint16_t reg_mask, 2343446Smrj uint16_t reg_val); 2353446Smrj static boolean_t ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl); 2363446Smrj static int ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd); 2373446Smrj 2383446Smrj /* 2393446Smrj * for <cmd> in ppb_ht_msimap_set 2403446Smrj */ 2413446Smrj #define HT_MSIMAP_ENABLE 1 2423446Smrj #define HT_MSIMAP_DISABLE 0 2433446Smrj 2443446Smrj 2453446Smrj int 2463446Smrj _init(void) 2473446Smrj { 2483446Smrj int e; 2493446Smrj 2503446Smrj if ((e = ddi_soft_state_init(&ppb_state, sizeof (ppb_devstate_t), 2513446Smrj 1)) == 0 && (e = mod_install(&modlinkage)) != 0) 2523446Smrj ddi_soft_state_fini(&ppb_state); 2533446Smrj return (e); 2543446Smrj } 2553446Smrj 2563446Smrj int 2573446Smrj _fini(void) 2583446Smrj { 2593446Smrj int e; 2603446Smrj 2613446Smrj if ((e = mod_remove(&modlinkage)) == 0) 2623446Smrj ddi_soft_state_fini(&ppb_state); 2633446Smrj return (e); 2643446Smrj } 2653446Smrj 2663446Smrj int 2673446Smrj _info(struct modinfo *modinfop) 2683446Smrj { 2693446Smrj return (mod_info(&modlinkage, modinfop)); 2703446Smrj } 2713446Smrj 2723446Smrj /*ARGSUSED*/ 2733446Smrj static int 2743446Smrj ppb_probe(dev_info_t *devi) 2753446Smrj { 2763446Smrj return (DDI_PROBE_SUCCESS); 2773446Smrj } 2783446Smrj 2793446Smrj /*ARGSUSED*/ 2803446Smrj static int 2813446Smrj ppb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 2823446Smrj { 2836313Skrishnae dev_info_t *root = ddi_root_node(); 2843446Smrj int instance; 2853446Smrj ppb_devstate_t *ppb; 2866313Skrishnae dev_info_t *pdip; 2873446Smrj ddi_acc_handle_t config_handle; 2886313Skrishnae char *bus; 2893446Smrj 2903446Smrj switch (cmd) { 2913446Smrj case DDI_ATTACH: 2923446Smrj 2933446Smrj /* 2943446Smrj * Make sure the "device_type" property exists. 2953446Smrj */ 2963446Smrj (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi, 2973446Smrj "device_type", "pci"); 2983446Smrj 2993446Smrj /* 3003446Smrj * Allocate and get soft state structure. 3013446Smrj */ 3023446Smrj instance = ddi_get_instance(devi); 3033446Smrj if (ddi_soft_state_zalloc(ppb_state, instance) != DDI_SUCCESS) 3043446Smrj return (DDI_FAILURE); 3053446Smrj ppb = ddi_get_soft_state(ppb_state, instance); 3063446Smrj ppb->dip = devi; 3073446Smrj 3083446Smrj /* 3093446Smrj * don't enable ereports if immediate child of npe 3103446Smrj */ 3113446Smrj if (strcmp(ddi_driver_name(ddi_get_parent(devi)), "npe") == 0) 3123446Smrj ppb->ppb_fmcap = DDI_FM_ERRCB_CAPABLE | 3133446Smrj DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE; 3143446Smrj else 3153446Smrj ppb->ppb_fmcap = DDI_FM_EREPORT_CAPABLE | 3163446Smrj DDI_FM_ERRCB_CAPABLE | DDI_FM_ACCCHK_CAPABLE | 3173446Smrj DDI_FM_DMACHK_CAPABLE; 3183446Smrj 3193446Smrj ddi_fm_init(devi, &ppb->ppb_fmcap, &ppb->ppb_fm_ibc); 3203446Smrj mutex_init(&ppb->ppb_err_mutex, NULL, MUTEX_DRIVER, 3213446Smrj (void *)ppb->ppb_fm_ibc); 3223446Smrj mutex_init(&ppb->ppb_peek_poke_mutex, NULL, MUTEX_DRIVER, 3233446Smrj (void *)ppb->ppb_fm_ibc); 3243446Smrj 3253446Smrj if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE | 3263446Smrj DDI_FM_EREPORT_CAPABLE)) 3273446Smrj pci_ereport_setup(devi); 3283446Smrj if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE) 3295295Srandyf ddi_fm_handler_register(devi, ppb_fm_callback, NULL); 3303446Smrj 3313446Smrj if (pci_config_setup(devi, &config_handle) != DDI_SUCCESS) { 3323446Smrj if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE) 3333446Smrj ddi_fm_handler_unregister(devi); 3343446Smrj if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE | 3353446Smrj DDI_FM_EREPORT_CAPABLE)) 3363446Smrj pci_ereport_teardown(devi); 3373446Smrj ddi_fm_fini(devi); 3383446Smrj ddi_soft_state_free(ppb_state, instance); 3393446Smrj return (DDI_FAILURE); 3403446Smrj } 3413446Smrj 342*9921SKrishna.Elango@Sun.COM ppb->parent_bus = PCIE_PCIECAP_DEV_TYPE_PCI_PSEUDO; 3436313Skrishnae for (pdip = ddi_get_parent(devi); pdip && (pdip != root) && 3446313Skrishnae (ppb->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV); 3456313Skrishnae pdip = ddi_get_parent(pdip)) { 3466313Skrishnae if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, 3476313Skrishnae DDI_PROP_DONTPASS, "device_type", &bus) != 3486313Skrishnae DDI_PROP_SUCCESS) 3496313Skrishnae break; 3506313Skrishnae 3516313Skrishnae if (strcmp(bus, "pciex") == 0) 3526313Skrishnae ppb->parent_bus = 3536313Skrishnae PCIE_PCIECAP_DEV_TYPE_PCIE_DEV; 3546313Skrishnae 3556313Skrishnae ddi_prop_free(bus); 3566313Skrishnae } 3576313Skrishnae 3583446Smrj if (ppb_support_ht_msimap == 1) 3593446Smrj (void) ppb_ht_msimap_set(config_handle, 3603446Smrj HT_MSIMAP_ENABLE); 3613446Smrj else if (ppb_support_ht_msimap == -1) 3623446Smrj (void) ppb_ht_msimap_set(config_handle, 3633446Smrj HT_MSIMAP_DISABLE); 3643446Smrj 3653446Smrj pci_config_teardown(&config_handle); 3663446Smrj 3673446Smrj /* 3683446Smrj * Initialize hotplug support on this bus. At minimum 3693446Smrj * (for non hotplug bus) this would create ":devctl" minor 3703446Smrj * node to support DEVCTL_DEVICE_* and DEVCTL_BUS_* ioctls 3713446Smrj * to this bus. 3723446Smrj */ 3733446Smrj if (pcihp_init(devi) != DDI_SUCCESS) 3745295Srandyf cmn_err(CE_WARN, 3755295Srandyf "pci: Failed to setup hotplug framework"); 3763446Smrj 3773446Smrj ddi_report_dev(devi); 3783446Smrj return (DDI_SUCCESS); 3793446Smrj 3803446Smrj case DDI_RESUME: 3813446Smrj 3823446Smrj /* 3833446Smrj * Get the soft state structure for the bridge. 3843446Smrj */ 3853446Smrj ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi)); 3863446Smrj ppb_restore_config_regs(ppb); 3873446Smrj return (DDI_SUCCESS); 3883446Smrj 3893446Smrj default: 3903446Smrj break; 3913446Smrj } 3923446Smrj return (DDI_FAILURE); 3933446Smrj } 3943446Smrj 3953446Smrj /*ARGSUSED*/ 3963446Smrj static int 3973446Smrj ppb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 3983446Smrj { 3993446Smrj ppb_devstate_t *ppb; 4003446Smrj 4013446Smrj switch (cmd) { 4023446Smrj case DDI_DETACH: 4033446Smrj (void) ddi_prop_remove(DDI_DEV_T_NONE, devi, "device_type"); 4043446Smrj 4053446Smrj ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi)); 4063446Smrj if (ppb->ppb_fmcap & DDI_FM_ERRCB_CAPABLE) 4073446Smrj ddi_fm_handler_unregister(devi); 4083446Smrj if (ppb->ppb_fmcap & (DDI_FM_ERRCB_CAPABLE | 4093446Smrj DDI_FM_EREPORT_CAPABLE)) 4103446Smrj pci_ereport_teardown(devi); 4113446Smrj mutex_destroy(&ppb->ppb_peek_poke_mutex); 4123446Smrj mutex_destroy(&ppb->ppb_err_mutex); 4133446Smrj ddi_fm_fini(devi); 4143446Smrj 4153446Smrj /* 4163446Smrj * And finally free the per-pci soft state. 4173446Smrj */ 4183446Smrj ddi_soft_state_free(ppb_state, ddi_get_instance(devi)); 4193446Smrj 4203446Smrj /* 4213446Smrj * Uninitialize hotplug support on this bus. 4223446Smrj */ 4233446Smrj (void) pcihp_uninit(devi); 4243446Smrj return (DDI_SUCCESS); 4253446Smrj 4263446Smrj case DDI_SUSPEND: 4273446Smrj ppb = ddi_get_soft_state(ppb_state, ddi_get_instance(devi)); 4283446Smrj ppb_save_config_regs(ppb); 4293446Smrj return (DDI_SUCCESS); 4303446Smrj 4313446Smrj default: 4323446Smrj break; 4333446Smrj } 4343446Smrj return (DDI_FAILURE); 4353446Smrj } 4363446Smrj 4373446Smrj /*ARGSUSED*/ 4383446Smrj static int 4393446Smrj ppb_bus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 4403446Smrj off_t offset, off_t len, caddr_t *vaddrp) 4413446Smrj { 4423446Smrj dev_info_t *pdip; 4433446Smrj 4443446Smrj pdip = (dev_info_t *)DEVI(dip)->devi_parent; 4453446Smrj return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip, 4465295Srandyf rdip, mp, offset, len, vaddrp)); 4473446Smrj } 4483446Smrj 4493446Smrj /*ARGSUSED*/ 4503446Smrj static int 4513446Smrj ppb_ctlops(dev_info_t *dip, dev_info_t *rdip, 4523446Smrj ddi_ctl_enum_t ctlop, void *arg, void *result) 4533446Smrj { 4543446Smrj pci_regspec_t *drv_regp; 4553446Smrj int reglen; 4563446Smrj int rn; 4573446Smrj int totreg; 4586313Skrishnae ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state, 4596313Skrishnae ddi_get_instance(dip)); 4606313Skrishnae struct detachspec *dsp; 4615295Srandyf struct attachspec *asp; 4623446Smrj 4633446Smrj switch (ctlop) { 4643446Smrj case DDI_CTLOPS_REPORTDEV: 4653446Smrj if (rdip == (dev_info_t *)0) 4663446Smrj return (DDI_FAILURE); 4673446Smrj cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n", 4683446Smrj ddi_node_name(rdip), ddi_get_name_addr(rdip), 4693446Smrj ddi_driver_name(rdip), 4703446Smrj ddi_get_instance(rdip)); 4713446Smrj return (DDI_SUCCESS); 4723446Smrj 4733446Smrj case DDI_CTLOPS_INITCHILD: 4743446Smrj return (ppb_initchild((dev_info_t *)arg)); 4753446Smrj 4763446Smrj case DDI_CTLOPS_UNINITCHILD: 4773446Smrj ppb_removechild((dev_info_t *)arg); 4783446Smrj return (DDI_SUCCESS); 4793446Smrj 4803446Smrj case DDI_CTLOPS_SIDDEV: 4813446Smrj return (DDI_SUCCESS); 4823446Smrj 4833446Smrj case DDI_CTLOPS_REGSIZE: 4843446Smrj case DDI_CTLOPS_NREGS: 4853446Smrj if (rdip == (dev_info_t *)0) 4863446Smrj return (DDI_FAILURE); 4873446Smrj break; 4883446Smrj 4895295Srandyf /* X86 systems support PME wakeup from suspend */ 4905295Srandyf case DDI_CTLOPS_ATTACH: 4916313Skrishnae if (!pcie_is_child(dip, rdip)) 4926313Skrishnae return (DDI_SUCCESS); 4936313Skrishnae 4945295Srandyf asp = (struct attachspec *)arg; 4956313Skrishnae if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) && 4966313Skrishnae (asp->when == DDI_POST) && (asp->result == DDI_SUCCESS)) 4976313Skrishnae pf_init(rdip, (void *)ppb->ppb_fm_ibc, asp->cmd); 4986313Skrishnae 4995295Srandyf if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE) 5005295Srandyf if (pci_pre_resume(rdip) != DDI_SUCCESS) 5015295Srandyf return (DDI_FAILURE); 5025295Srandyf 5036313Skrishnae return (DDI_SUCCESS); 5045295Srandyf 5055295Srandyf case DDI_CTLOPS_DETACH: 5066313Skrishnae if (!pcie_is_child(dip, rdip)) 5076313Skrishnae return (DDI_SUCCESS); 5086313Skrishnae 5096313Skrishnae dsp = (struct detachspec *)arg; 5106313Skrishnae if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) && 5116313Skrishnae (dsp->when == DDI_PRE)) 5126313Skrishnae pf_fini(rdip, dsp->cmd); 5136313Skrishnae 5146313Skrishnae if (dsp->cmd == DDI_SUSPEND && dsp->when == DDI_POST) 5155295Srandyf if (pci_post_suspend(rdip) != DDI_SUCCESS) 5165295Srandyf return (DDI_FAILURE); 5176313Skrishnae 5186313Skrishnae return (DDI_SUCCESS); 5195295Srandyf 5203446Smrj case DDI_CTLOPS_PEEK: 5213446Smrj case DDI_CTLOPS_POKE: 5223446Smrj if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") != 0) 5233446Smrj return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 5243446Smrj return (pci_peekpoke_check(dip, rdip, ctlop, arg, result, 5253446Smrj ddi_ctlops, &ppb->ppb_err_mutex, 5266313Skrishnae &ppb->ppb_peek_poke_mutex, ppb_peekpoke_cb)); 5273446Smrj 5283446Smrj default: 5293446Smrj return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 5303446Smrj } 5313446Smrj 5323446Smrj *(int *)result = 0; 5333446Smrj if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, 5345295Srandyf DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg", 5355295Srandyf (caddr_t)&drv_regp, ®len) != DDI_SUCCESS) 5363446Smrj return (DDI_FAILURE); 5373446Smrj 5383446Smrj totreg = reglen / sizeof (pci_regspec_t); 5393446Smrj if (ctlop == DDI_CTLOPS_NREGS) 5403446Smrj *(int *)result = totreg; 5413446Smrj else if (ctlop == DDI_CTLOPS_REGSIZE) { 5423446Smrj rn = *(int *)arg; 5433446Smrj if (rn >= totreg) { 5443446Smrj kmem_free(drv_regp, reglen); 5453446Smrj return (DDI_FAILURE); 5463446Smrj } 5473446Smrj *(off_t *)result = drv_regp[rn].pci_size_low; 5483446Smrj } 5493446Smrj 5503446Smrj kmem_free(drv_regp, reglen); 5513446Smrj return (DDI_SUCCESS); 5523446Smrj } 5533446Smrj 5543446Smrj static int 5553446Smrj ppb_name_child(dev_info_t *child, char *name, int namelen) 5563446Smrj { 5573446Smrj pci_regspec_t *pci_rp; 5583446Smrj uint_t slot, func; 5593446Smrj char **unit_addr; 5603446Smrj uint_t n; 5613446Smrj 5623446Smrj /* 5633446Smrj * For .conf nodes, use unit-address property as name 5643446Smrj */ 5653446Smrj if (ndi_dev_is_persistent_node(child) == 0) { 5663446Smrj if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child, 5673446Smrj DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) != 5683446Smrj DDI_PROP_SUCCESS) { 5693446Smrj cmn_err(CE_WARN, 5703446Smrj "cannot find unit-address in %s.conf", 5713446Smrj ddi_driver_name(child)); 5723446Smrj return (DDI_FAILURE); 5733446Smrj } 5743446Smrj if (n != 1 || *unit_addr == NULL || **unit_addr == 0) { 5753446Smrj cmn_err(CE_WARN, "unit-address property in %s.conf" 5763446Smrj " not well-formed", ddi_driver_name(child)); 5773446Smrj ddi_prop_free(unit_addr); 5783446Smrj return (DDI_SUCCESS); 5793446Smrj } 5803446Smrj (void) snprintf(name, namelen, "%s", *unit_addr); 5813446Smrj ddi_prop_free(unit_addr); 5823446Smrj return (DDI_SUCCESS); 5833446Smrj } 5843446Smrj 5853446Smrj /* get child "reg" property */ 5863446Smrj if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child, 5873446Smrj DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) { 5883446Smrj return (DDI_FAILURE); 5893446Smrj } 5903446Smrj 5913446Smrj /* copy the device identifications */ 5923446Smrj slot = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 5933446Smrj func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 5943446Smrj 5953446Smrj if (func != 0) 5963446Smrj (void) snprintf(name, namelen, "%x,%x", slot, func); 5973446Smrj else 5983446Smrj (void) snprintf(name, namelen, "%x", slot); 5993446Smrj 6003446Smrj ddi_prop_free(pci_rp); 6013446Smrj return (DDI_SUCCESS); 6023446Smrj } 6033446Smrj 6043446Smrj static int 6053446Smrj ppb_initchild(dev_info_t *child) 6063446Smrj { 6073446Smrj struct ddi_parent_private_data *pdptr; 6086313Skrishnae ppb_devstate_t *ppb; 6093446Smrj char name[MAXNAMELEN]; 6103446Smrj ddi_acc_handle_t config_handle; 6113446Smrj ushort_t command_preserve, command; 6123446Smrj 6136313Skrishnae ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state, 6146313Skrishnae ddi_get_instance(ddi_get_parent(child))); 6156313Skrishnae 6163446Smrj if (ppb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS) 6173446Smrj return (DDI_FAILURE); 6183446Smrj ddi_set_name_addr(child, name); 6193446Smrj 6203446Smrj /* 6213446Smrj * Pseudo nodes indicate a prototype node with per-instance 6223446Smrj * properties to be merged into the real h/w device node. 6233446Smrj * The interpretation of the unit-address is DD[,F] 6243446Smrj * where DD is the device id and F is the function. 6253446Smrj */ 6263446Smrj if (ndi_dev_is_persistent_node(child) == 0) { 6273446Smrj extern int pci_allow_pseudo_children; 6283446Smrj 6293446Smrj ddi_set_parent_data(child, NULL); 6303446Smrj 6313446Smrj /* 6323446Smrj * Try to merge the properties from this prototype 6333446Smrj * node into real h/w nodes. 6343446Smrj */ 6353446Smrj if (ndi_merge_node(child, ppb_name_child) == DDI_SUCCESS) { 6363446Smrj /* 6373446Smrj * Merged ok - return failure to remove the node. 6383446Smrj */ 6393446Smrj ddi_set_name_addr(child, NULL); 6403446Smrj return (DDI_FAILURE); 6413446Smrj } 6423446Smrj 6433446Smrj /* workaround for ddivs to run under PCI */ 6443446Smrj if (pci_allow_pseudo_children) 6453446Smrj return (DDI_SUCCESS); 6463446Smrj 6473446Smrj /* 6483446Smrj * The child was not merged into a h/w node, 6493446Smrj * but there's not much we can do with it other 6503446Smrj * than return failure to cause the node to be removed. 6513446Smrj */ 6523446Smrj cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged", 6533446Smrj ddi_driver_name(child), ddi_get_name_addr(child), 6543446Smrj ddi_driver_name(child)); 6553446Smrj ddi_set_name_addr(child, NULL); 6563446Smrj return (DDI_NOT_WELL_FORMED); 6573446Smrj } 6583446Smrj 6596313Skrishnae ddi_set_parent_data(child, NULL); 6606313Skrishnae 6616313Skrishnae /* 6626313Skrishnae * PCIe FMA specific 6636313Skrishnae * 6646313Skrishnae * Note: parent_data for parent is created only if this is PCI-E 6656313Skrishnae * platform, for which, SG take a different route to handle device 6666313Skrishnae * errors. 6676313Skrishnae */ 6686313Skrishnae if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) { 6696313Skrishnae if (pcie_init_bus(child) == NULL) 6706313Skrishnae return (DDI_FAILURE); 6716313Skrishnae } 6726313Skrishnae 6733446Smrj /* transfer select properties from PROM to kernel */ 6745295Srandyf if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS, 6755295Srandyf "interrupts", -1) != -1) { 6763446Smrj pdptr = kmem_zalloc((sizeof (struct ddi_parent_private_data) + 6773446Smrj sizeof (struct intrspec)), KM_SLEEP); 6783446Smrj pdptr->par_intr = (struct intrspec *)(pdptr + 1); 6793446Smrj pdptr->par_nintr = 1; 6803446Smrj ddi_set_parent_data(child, pdptr); 6813446Smrj } else 6823446Smrj ddi_set_parent_data(child, NULL); 6833446Smrj 6843446Smrj if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) 6853446Smrj return (DDI_FAILURE); 6863446Smrj 6873446Smrj /* 6883446Smrj * Support for the "command-preserve" property. 6893446Smrj */ 6903446Smrj command_preserve = ddi_prop_get_int(DDI_DEV_T_ANY, child, 6913446Smrj DDI_PROP_DONTPASS, "command-preserve", 0); 6923446Smrj command = pci_config_get16(config_handle, PCI_CONF_COMM); 6933446Smrj command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB); 6943446Smrj command |= (ppb_command_default & ~command_preserve); 6953446Smrj pci_config_put16(config_handle, PCI_CONF_COMM, command); 6963446Smrj 6973446Smrj pci_config_teardown(&config_handle); 6983446Smrj return (DDI_SUCCESS); 6993446Smrj } 7003446Smrj 7013446Smrj static void 7023446Smrj ppb_removechild(dev_info_t *dip) 7033446Smrj { 7043446Smrj struct ddi_parent_private_data *pdptr; 7056313Skrishnae ppb_devstate_t *ppb; 7063446Smrj 7076313Skrishnae ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state, 7086313Skrishnae ddi_get_instance(ddi_get_parent(dip))); 7096313Skrishnae 7106313Skrishnae if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) 7116313Skrishnae pcie_fini_bus(dip); 7126313Skrishnae else if ((pdptr = ddi_get_parent_data(dip)) != NULL) { 7133446Smrj kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec))); 7143446Smrj ddi_set_parent_data(dip, NULL); 7153446Smrj } 7163446Smrj ddi_set_name_addr(dip, NULL); 7173446Smrj 7183446Smrj /* 7193446Smrj * Strip the node to properly convert it back to prototype form 7203446Smrj */ 7213446Smrj ddi_remove_minor_node(dip, NULL); 7223446Smrj 7233446Smrj impl_rem_dev_props(dip); 7243446Smrj } 7253446Smrj 7263446Smrj /* 7273446Smrj * ppb_save_config_regs 7283446Smrj * 7293446Smrj * This routine saves the state of the configuration registers of all 7303446Smrj * the child nodes of each PBM. 7313446Smrj * 7323446Smrj * used by: ppb_detach() on suspends 7333446Smrj * 7343446Smrj * return value: none 7353446Smrj */ 7363446Smrj static void 7373446Smrj ppb_save_config_regs(ppb_devstate_t *ppb_p) 7383446Smrj { 7393446Smrj int i; 7403446Smrj dev_info_t *dip; 7413446Smrj ddi_acc_handle_t config_handle; 7423446Smrj 7433446Smrj for (i = 0, dip = ddi_get_child(ppb_p->dip); dip != NULL; 7445295Srandyf i++, dip = ddi_get_next_sibling(dip)) { 7453446Smrj 7463446Smrj if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) { 7473446Smrj cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n", 7485295Srandyf ddi_driver_name(ppb_p->dip), 7495295Srandyf ddi_get_instance(ppb_p->dip), 7505295Srandyf ddi_driver_name(dip), 7515295Srandyf ddi_get_instance(dip)); 7523446Smrj continue; 7533446Smrj } 7543446Smrj 7553446Smrj ppb_p->config_state[i].dip = dip; 7563446Smrj ppb_p->config_state[i].command = 7575295Srandyf pci_config_get16(config_handle, PCI_CONF_COMM); 7583446Smrj pci_config_teardown(&config_handle); 7593446Smrj } 7603446Smrj ppb_p->config_state_index = i; 7613446Smrj } 7623446Smrj 7633446Smrj 7643446Smrj /* 7653446Smrj * ppb_restore_config_regs 7663446Smrj * 7673446Smrj * This routine restores the state of the configuration registers of all 7683446Smrj * the child nodes of each PBM. 7693446Smrj * 7703446Smrj * used by: ppb_attach() on resume 7713446Smrj * 7723446Smrj * return value: none 7733446Smrj */ 7743446Smrj static void 7753446Smrj ppb_restore_config_regs(ppb_devstate_t *ppb_p) 7763446Smrj { 7773446Smrj int i; 7783446Smrj dev_info_t *dip; 7793446Smrj ddi_acc_handle_t config_handle; 7803446Smrj 7813446Smrj for (i = 0; i < ppb_p->config_state_index; i++) { 7823446Smrj dip = ppb_p->config_state[i].dip; 7833446Smrj if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) { 7843446Smrj cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n", 7855295Srandyf ddi_driver_name(ppb_p->dip), 7865295Srandyf ddi_get_instance(ppb_p->dip), 7875295Srandyf ddi_driver_name(dip), 7885295Srandyf ddi_get_instance(dip)); 7893446Smrj continue; 7903446Smrj } 7913446Smrj pci_config_put16(config_handle, PCI_CONF_COMM, 7925295Srandyf ppb_p->config_state[i].command); 7933446Smrj pci_config_teardown(&config_handle); 7943446Smrj } 7953446Smrj } 7963446Smrj 7973446Smrj 7983446Smrj /* 7993446Smrj * returns the location of a hypertransport capability whose upper 16-bit 8003446Smrj * register of the cap header matches <reg_val> after masking the register 8013446Smrj * with <reg_mask>; if both <reg_mask> and <reg_val> are 0, it will return the 8023446Smrj * first HT cap found 8033446Smrj */ 8043446Smrj static uint8_t 8053446Smrj ppb_find_ht_cap(ddi_acc_handle_t cfg_hdl, uint16_t reg_mask, uint16_t reg_val) 8063446Smrj { 8073446Smrj uint16_t status, reg; 8083446Smrj uint8_t ptr, id; 8093446Smrj 8103446Smrj status = pci_config_get16(cfg_hdl, PCI_CONF_STAT); 8113446Smrj if (status == 0xffff || !((status & PCI_STAT_CAP))) 8123446Smrj return (PCI_CAP_NEXT_PTR_NULL); 8133446Smrj 8143446Smrj ptr = pci_config_get8(cfg_hdl, PCI_CONF_CAP_PTR); 8153446Smrj while (ptr != 0xFF && 8163446Smrj ptr != PCI_CAP_NEXT_PTR_NULL && 8173446Smrj ptr >= PCI_CAP_PTR_OFF) { 8183446Smrj 8193446Smrj ptr &= PCI_CAP_PTR_MASK; 8203446Smrj id = pci_config_get8(cfg_hdl, ptr + PCI_CAP_ID); 8213446Smrj 8223446Smrj if (id == PCI_CAP_ID_HT) { 8233446Smrj reg = pci_config_get16(cfg_hdl, 8243446Smrj ptr + PCI_CAP_ID_REGS_OFF); 8253446Smrj if ((reg & reg_mask) == reg_val) 8263446Smrj return (ptr); 8273446Smrj } 8283446Smrj ptr = pci_config_get8(cfg_hdl, ptr + PCI_CAP_NEXT_PTR); 8293446Smrj } 8303446Smrj 8313446Smrj return (PCI_CAP_NEXT_PTR_NULL); 8323446Smrj } 8333446Smrj 8343446Smrj 8353446Smrj static boolean_t 8363446Smrj ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl) 8373446Smrj { 8383446Smrj uint8_t ptr; 8393446Smrj 8403446Smrj ptr = ppb_find_ht_cap(cfg_hdl, 8413446Smrj PCI_CAP_HT_MSIMAP_TYPE_MASK | PCI_CAP_HT_MSIMAP_ENABLE_MASK, 8423446Smrj PCI_CAP_HT_MSIMAP_TYPE | PCI_CAP_HT_MSIMAP_ENABLE); 8433446Smrj 8443446Smrj if (ptr == PCI_CAP_NEXT_PTR_NULL) 8453446Smrj return (B_FALSE); 8463446Smrj 8473446Smrj return (B_TRUE); 8483446Smrj } 8493446Smrj 8503446Smrj 8513446Smrj static int 8523446Smrj ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd) 8533446Smrj { 8543446Smrj uint8_t ptr; 8553446Smrj uint16_t reg; 8563446Smrj 8573446Smrj ptr = ppb_find_ht_cap(cfg_hdl, PCI_CAP_HT_MSIMAP_TYPE_MASK, 8583446Smrj PCI_CAP_HT_MSIMAP_TYPE); 8593446Smrj if (ptr == PCI_CAP_NEXT_PTR_NULL) 8603446Smrj return (0); 8613446Smrj 8623446Smrj reg = pci_config_get16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF); 8633446Smrj switch (cmd) { 8643446Smrj case HT_MSIMAP_ENABLE: 8653446Smrj reg |= PCI_CAP_HT_MSIMAP_ENABLE; 8663446Smrj break; 8673446Smrj case HT_MSIMAP_DISABLE: 8683446Smrj default: 8693446Smrj reg &= ~(uint16_t)PCI_CAP_HT_MSIMAP_ENABLE; 8703446Smrj } 8713446Smrj 8723446Smrj pci_config_put16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF, reg); 8733446Smrj return (1); 8743446Smrj } 8753446Smrj 8763446Smrj 8773446Smrj /* 8783446Smrj * intercept certain interrupt services to handle special cases 8793446Smrj */ 8803446Smrj static int 8813446Smrj ppb_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, 8823446Smrj ddi_intr_handle_impl_t *hdlp, void *result) 8833446Smrj { 8843446Smrj ddi_acc_handle_t cfg_hdl; 8853446Smrj int rv = DDI_SUCCESS; 8863446Smrj 8873446Smrj if (intr_op != DDI_INTROP_SUPPORTED_TYPES) 8883446Smrj return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); 8893446Smrj 8903446Smrj DDI_INTR_NEXDBG((CE_CONT, 8913446Smrj "ppb_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n", 8923446Smrj (void *)pdip, (void *)rdip, intr_op, (void *)hdlp)); 8933446Smrj 8943446Smrj /* Fixed interrupt is supported by default */ 8953446Smrj *(int *)result = DDI_INTR_TYPE_FIXED; 8963446Smrj 8973446Smrj if (ppb_support_msi == -1) { 8983446Smrj DDI_INTR_NEXDBG((CE_CONT, 8993446Smrj "ppb_intr_ops: MSI is not allowed\n")); 9003446Smrj goto OUT; 9013446Smrj } 9023446Smrj 9033446Smrj if (ppb_support_msi == 1) { 9043446Smrj DDI_INTR_NEXDBG((CE_CONT, 9053446Smrj "ppb_intr_ops: MSI is always allowed\n")); 9063446Smrj rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result); 9073446Smrj goto OUT; 9083446Smrj } 9093446Smrj 9103446Smrj if (pci_config_setup(pdip, &cfg_hdl) != DDI_SUCCESS) { 9113446Smrj DDI_INTR_NEXDBG((CE_CONT, 9123446Smrj "ppb_intr_ops: pci_config_setup() failed\n")); 9133446Smrj goto OUT; 9143446Smrj } 9153446Smrj 9163446Smrj /* 9173446Smrj * check for hypertransport msi mapping capability 9183446Smrj */ 9193446Smrj if (ppb_ht_msimap_check(cfg_hdl)) { 9203446Smrj DDI_INTR_NEXDBG((CE_CONT, 9213446Smrj "ppb_intr_ops: HT MSI mapping enabled\n")); 9223446Smrj rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result); 9233446Smrj } 9243446Smrj 9253446Smrj /* 9263446Smrj * if we add failure conditions after pci_config_setup, move this to 9273446Smrj * OUT and use an extra flag to indicate the need to teardown cfg_hdl 9283446Smrj */ 9293446Smrj pci_config_teardown(&cfg_hdl); 9303446Smrj 9313446Smrj OUT: 9323446Smrj DDI_INTR_NEXDBG((CE_CONT, 9333446Smrj "ppb_intr_ops: rdip 0x%p, returns supported types: 0x%x\n", 9343446Smrj (void *)rdip, *(int *)result)); 9353446Smrj return (rv); 9363446Smrj } 9373446Smrj 9383446Smrj static int 9393446Smrj ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp) 9403446Smrj { 9413446Smrj return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp)); 9423446Smrj } 9433446Smrj 9443446Smrj static int 9453446Smrj ppb_close(dev_t dev, int flags, int otyp, cred_t *credp) 9463446Smrj { 9473446Smrj return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp)); 9483446Smrj } 9493446Smrj 9503446Smrj static int 9513446Smrj ppb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) 9523446Smrj { 9533446Smrj return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, credp, 9543446Smrj rvalp)); 9553446Smrj } 9563446Smrj 9573446Smrj static int 9583446Smrj ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, 9593446Smrj int flags, char *name, caddr_t valuep, int *lengthp) 9603446Smrj { 9613446Smrj return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags, 9623446Smrj name, valuep, lengthp)); 9633446Smrj } 9643446Smrj 9653446Smrj static int 9663446Smrj ppb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 9673446Smrj { 9683446Smrj return (pcihp_info(dip, cmd, arg, result)); 9693446Smrj } 9703446Smrj 9716313Skrishnae void ppb_peekpoke_cb(dev_info_t *dip, ddi_fm_error_t *derr) { 9726313Skrishnae (void) pci_ereport_post(dip, derr, NULL); 9736313Skrishnae } 9746313Skrishnae 9753446Smrj /*ARGSUSED*/ 9763446Smrj static int 9773446Smrj ppb_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap, 9783446Smrj ddi_iblock_cookie_t *ibc) 9793446Smrj { 9803446Smrj ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state, 9813446Smrj ddi_get_instance(dip)); 9823446Smrj 9833446Smrj ASSERT(ibc != NULL); 9843446Smrj *ibc = ppb->ppb_fm_ibc; 9853446Smrj 9863446Smrj return (ppb->ppb_fmcap); 9873446Smrj } 9883446Smrj 9893446Smrj /*ARGSUSED*/ 9903446Smrj static int 9913446Smrj ppb_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used) 9923446Smrj { 9933446Smrj ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state, 9943446Smrj ddi_get_instance(dip)); 9953446Smrj 9963446Smrj mutex_enter(&ppb->ppb_err_mutex); 9973446Smrj pci_ereport_post(dip, derr, NULL); 9983446Smrj mutex_exit(&ppb->ppb_err_mutex); 9993446Smrj return (derr->fme_status); 10003446Smrj } 1001