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 /*
2211412SStephen.Hanson@Sun.COM * Copyright 2010 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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)2613446Smrj _info(struct modinfo *modinfop)
2623446Smrj {
2633446Smrj return (mod_info(&modlinkage, modinfop));
2643446Smrj }
2653446Smrj
2663446Smrj /*ARGSUSED*/
2673446Smrj static int
ppb_probe(dev_info_t * devi)2683446Smrj ppb_probe(dev_info_t *devi)
2693446Smrj {
2703446Smrj return (DDI_PROBE_SUCCESS);
2713446Smrj }
2723446Smrj
2733446Smrj /*ARGSUSED*/
2743446Smrj static int
ppb_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)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
ppb_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)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
ppb_bus_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t offset,off_t len,caddr_t * vaddrp)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;
45111236SStephen.Hanson@Sun.COM ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state,
45211236SStephen.Hanson@Sun.COM ddi_get_instance(dip));
4533446Smrj
45411412SStephen.Hanson@Sun.COM if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") == 0 &&
45511412SStephen.Hanson@Sun.COM mp->map_handlep != NULL) {
45611236SStephen.Hanson@Sun.COM ddi_acc_impl_t *hdlp =
45711236SStephen.Hanson@Sun.COM (ddi_acc_impl_t *)(mp->map_handlep)->ah_platform_private;
45811236SStephen.Hanson@Sun.COM hdlp->ahi_err_mutexp = &ppb->ppb_err_mutex;
45911236SStephen.Hanson@Sun.COM hdlp->ahi_peekpoke_mutexp = &ppb->ppb_peek_poke_mutex;
46011236SStephen.Hanson@Sun.COM hdlp->ahi_scan_dip = dip;
46111236SStephen.Hanson@Sun.COM hdlp->ahi_scan = ppb_peekpoke_cb;
46211236SStephen.Hanson@Sun.COM }
4633446Smrj pdip = (dev_info_t *)DEVI(dip)->devi_parent;
4643446Smrj return ((DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)(pdip,
4655295Srandyf rdip, mp, offset, len, vaddrp));
4663446Smrj }
4673446Smrj
4683446Smrj /*ARGSUSED*/
4693446Smrj static int
ppb_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)4703446Smrj ppb_ctlops(dev_info_t *dip, dev_info_t *rdip,
4713446Smrj ddi_ctl_enum_t ctlop, void *arg, void *result)
4723446Smrj {
4733446Smrj pci_regspec_t *drv_regp;
4743446Smrj int reglen;
4753446Smrj int rn;
4763446Smrj int totreg;
4776313Skrishnae ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state,
4786313Skrishnae ddi_get_instance(dip));
4796313Skrishnae struct detachspec *dsp;
4805295Srandyf struct attachspec *asp;
4813446Smrj
4823446Smrj switch (ctlop) {
4833446Smrj case DDI_CTLOPS_REPORTDEV:
4843446Smrj if (rdip == (dev_info_t *)0)
4853446Smrj return (DDI_FAILURE);
4863446Smrj cmn_err(CE_CONT, "?PCI-device: %s@%s, %s%d\n",
4873446Smrj ddi_node_name(rdip), ddi_get_name_addr(rdip),
4883446Smrj ddi_driver_name(rdip),
4893446Smrj ddi_get_instance(rdip));
4903446Smrj return (DDI_SUCCESS);
4913446Smrj
4923446Smrj case DDI_CTLOPS_INITCHILD:
4933446Smrj return (ppb_initchild((dev_info_t *)arg));
4943446Smrj
4953446Smrj case DDI_CTLOPS_UNINITCHILD:
4963446Smrj ppb_removechild((dev_info_t *)arg);
4973446Smrj return (DDI_SUCCESS);
4983446Smrj
4993446Smrj case DDI_CTLOPS_SIDDEV:
5003446Smrj return (DDI_SUCCESS);
5013446Smrj
5023446Smrj case DDI_CTLOPS_REGSIZE:
5033446Smrj case DDI_CTLOPS_NREGS:
5043446Smrj if (rdip == (dev_info_t *)0)
5053446Smrj return (DDI_FAILURE);
5063446Smrj break;
5073446Smrj
5085295Srandyf /* X86 systems support PME wakeup from suspend */
5095295Srandyf case DDI_CTLOPS_ATTACH:
5106313Skrishnae if (!pcie_is_child(dip, rdip))
5116313Skrishnae return (DDI_SUCCESS);
5126313Skrishnae
5135295Srandyf asp = (struct attachspec *)arg;
5146313Skrishnae if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
5156313Skrishnae (asp->when == DDI_POST) && (asp->result == DDI_SUCCESS))
5166313Skrishnae pf_init(rdip, (void *)ppb->ppb_fm_ibc, asp->cmd);
5176313Skrishnae
5185295Srandyf if (asp->cmd == DDI_RESUME && asp->when == DDI_PRE)
5195295Srandyf if (pci_pre_resume(rdip) != DDI_SUCCESS)
5205295Srandyf return (DDI_FAILURE);
5215295Srandyf
5226313Skrishnae return (DDI_SUCCESS);
5235295Srandyf
5245295Srandyf case DDI_CTLOPS_DETACH:
5256313Skrishnae if (!pcie_is_child(dip, rdip))
5266313Skrishnae return (DDI_SUCCESS);
5276313Skrishnae
5286313Skrishnae dsp = (struct detachspec *)arg;
5296313Skrishnae if ((ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) &&
5306313Skrishnae (dsp->when == DDI_PRE))
5316313Skrishnae pf_fini(rdip, dsp->cmd);
5326313Skrishnae
5336313Skrishnae if (dsp->cmd == DDI_SUSPEND && dsp->when == DDI_POST)
5345295Srandyf if (pci_post_suspend(rdip) != DDI_SUCCESS)
5355295Srandyf return (DDI_FAILURE);
5366313Skrishnae
5376313Skrishnae return (DDI_SUCCESS);
5385295Srandyf
5393446Smrj case DDI_CTLOPS_PEEK:
5403446Smrj case DDI_CTLOPS_POKE:
5413446Smrj if (strcmp(ddi_driver_name(ddi_get_parent(dip)), "npe") != 0)
5423446Smrj return (ddi_ctlops(dip, rdip, ctlop, arg, result));
5433446Smrj return (pci_peekpoke_check(dip, rdip, ctlop, arg, result,
5443446Smrj ddi_ctlops, &ppb->ppb_err_mutex,
5456313Skrishnae &ppb->ppb_peek_poke_mutex, ppb_peekpoke_cb));
5463446Smrj
5473446Smrj default:
5483446Smrj return (ddi_ctlops(dip, rdip, ctlop, arg, result));
5493446Smrj }
5503446Smrj
5513446Smrj *(int *)result = 0;
5523446Smrj if (ddi_getlongprop(DDI_DEV_T_ANY, rdip,
5535295Srandyf DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg",
5545295Srandyf (caddr_t)&drv_regp, ®len) != DDI_SUCCESS)
5553446Smrj return (DDI_FAILURE);
5563446Smrj
5573446Smrj totreg = reglen / sizeof (pci_regspec_t);
5583446Smrj if (ctlop == DDI_CTLOPS_NREGS)
5593446Smrj *(int *)result = totreg;
5603446Smrj else if (ctlop == DDI_CTLOPS_REGSIZE) {
5613446Smrj rn = *(int *)arg;
5623446Smrj if (rn >= totreg) {
5633446Smrj kmem_free(drv_regp, reglen);
5643446Smrj return (DDI_FAILURE);
5653446Smrj }
5663446Smrj *(off_t *)result = drv_regp[rn].pci_size_low;
5673446Smrj }
5683446Smrj
5693446Smrj kmem_free(drv_regp, reglen);
5703446Smrj return (DDI_SUCCESS);
5713446Smrj }
5723446Smrj
5733446Smrj static int
ppb_name_child(dev_info_t * child,char * name,int namelen)5743446Smrj ppb_name_child(dev_info_t *child, char *name, int namelen)
5753446Smrj {
5763446Smrj pci_regspec_t *pci_rp;
5773446Smrj uint_t slot, func;
5783446Smrj char **unit_addr;
5793446Smrj uint_t n;
5803446Smrj
5813446Smrj /*
5823446Smrj * For .conf nodes, use unit-address property as name
5833446Smrj */
5843446Smrj if (ndi_dev_is_persistent_node(child) == 0) {
5853446Smrj if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, child,
5863446Smrj DDI_PROP_DONTPASS, "unit-address", &unit_addr, &n) !=
5873446Smrj DDI_PROP_SUCCESS) {
5883446Smrj cmn_err(CE_WARN,
5893446Smrj "cannot find unit-address in %s.conf",
5903446Smrj ddi_driver_name(child));
5913446Smrj return (DDI_FAILURE);
5923446Smrj }
5933446Smrj if (n != 1 || *unit_addr == NULL || **unit_addr == 0) {
5943446Smrj cmn_err(CE_WARN, "unit-address property in %s.conf"
5953446Smrj " not well-formed", ddi_driver_name(child));
5963446Smrj ddi_prop_free(unit_addr);
5973446Smrj return (DDI_SUCCESS);
5983446Smrj }
5993446Smrj (void) snprintf(name, namelen, "%s", *unit_addr);
6003446Smrj ddi_prop_free(unit_addr);
6013446Smrj return (DDI_SUCCESS);
6023446Smrj }
6033446Smrj
6043446Smrj /* get child "reg" property */
6053446Smrj if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, child,
6063446Smrj DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &n) != DDI_SUCCESS) {
6073446Smrj return (DDI_FAILURE);
6083446Smrj }
6093446Smrj
6103446Smrj /* copy the device identifications */
6113446Smrj slot = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
6123446Smrj func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
6133446Smrj
6143446Smrj if (func != 0)
6153446Smrj (void) snprintf(name, namelen, "%x,%x", slot, func);
6163446Smrj else
6173446Smrj (void) snprintf(name, namelen, "%x", slot);
6183446Smrj
6193446Smrj ddi_prop_free(pci_rp);
6203446Smrj return (DDI_SUCCESS);
6213446Smrj }
6223446Smrj
6233446Smrj static int
ppb_initchild(dev_info_t * child)6243446Smrj ppb_initchild(dev_info_t *child)
6253446Smrj {
6263446Smrj struct ddi_parent_private_data *pdptr;
6276313Skrishnae ppb_devstate_t *ppb;
6283446Smrj char name[MAXNAMELEN];
6293446Smrj ddi_acc_handle_t config_handle;
6303446Smrj ushort_t command_preserve, command;
6313446Smrj
6326313Skrishnae ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
6336313Skrishnae ddi_get_instance(ddi_get_parent(child)));
6346313Skrishnae
6353446Smrj if (ppb_name_child(child, name, MAXNAMELEN) != DDI_SUCCESS)
6363446Smrj return (DDI_FAILURE);
6373446Smrj ddi_set_name_addr(child, name);
6383446Smrj
6393446Smrj /*
6403446Smrj * Pseudo nodes indicate a prototype node with per-instance
6413446Smrj * properties to be merged into the real h/w device node.
6423446Smrj * The interpretation of the unit-address is DD[,F]
6433446Smrj * where DD is the device id and F is the function.
6443446Smrj */
6453446Smrj if (ndi_dev_is_persistent_node(child) == 0) {
6463446Smrj extern int pci_allow_pseudo_children;
6473446Smrj
6483446Smrj ddi_set_parent_data(child, NULL);
6493446Smrj
6503446Smrj /*
6513446Smrj * Try to merge the properties from this prototype
6523446Smrj * node into real h/w nodes.
6533446Smrj */
6543446Smrj if (ndi_merge_node(child, ppb_name_child) == DDI_SUCCESS) {
6553446Smrj /*
6563446Smrj * Merged ok - return failure to remove the node.
6573446Smrj */
6583446Smrj ddi_set_name_addr(child, NULL);
6593446Smrj return (DDI_FAILURE);
6603446Smrj }
6613446Smrj
6623446Smrj /* workaround for ddivs to run under PCI */
6633446Smrj if (pci_allow_pseudo_children)
6643446Smrj return (DDI_SUCCESS);
6653446Smrj
6663446Smrj /*
6673446Smrj * The child was not merged into a h/w node,
6683446Smrj * but there's not much we can do with it other
6693446Smrj * than return failure to cause the node to be removed.
6703446Smrj */
6713446Smrj cmn_err(CE_WARN, "!%s@%s: %s.conf properties not merged",
6723446Smrj ddi_driver_name(child), ddi_get_name_addr(child),
6733446Smrj ddi_driver_name(child));
6743446Smrj ddi_set_name_addr(child, NULL);
6753446Smrj return (DDI_NOT_WELL_FORMED);
6763446Smrj }
6773446Smrj
6786313Skrishnae ddi_set_parent_data(child, NULL);
6796313Skrishnae
6806313Skrishnae /*
6816313Skrishnae * PCIe FMA specific
6826313Skrishnae *
6836313Skrishnae * Note: parent_data for parent is created only if this is PCI-E
6846313Skrishnae * platform, for which, SG take a different route to handle device
6856313Skrishnae * errors.
6866313Skrishnae */
6876313Skrishnae if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
68811245SZhijun.Fu@Sun.COM if (pcie_init_cfghdl(child) != DDI_SUCCESS)
6896313Skrishnae return (DDI_FAILURE);
690*11596SJason.Beloro@Sun.COM pcie_init_dom(child);
6916313Skrishnae }
6926313Skrishnae
6933446Smrj /* transfer select properties from PROM to kernel */
6945295Srandyf if (ddi_getprop(DDI_DEV_T_NONE, child, DDI_PROP_DONTPASS,
6955295Srandyf "interrupts", -1) != -1) {
6963446Smrj pdptr = kmem_zalloc((sizeof (struct ddi_parent_private_data) +
6973446Smrj sizeof (struct intrspec)), KM_SLEEP);
6983446Smrj pdptr->par_intr = (struct intrspec *)(pdptr + 1);
6993446Smrj pdptr->par_nintr = 1;
7003446Smrj ddi_set_parent_data(child, pdptr);
7013446Smrj } else
7023446Smrj ddi_set_parent_data(child, NULL);
7033446Smrj
704*11596SJason.Beloro@Sun.COM if (pci_config_setup(child, &config_handle) != DDI_SUCCESS) {
705*11596SJason.Beloro@Sun.COM pcie_fini_dom(child);
7063446Smrj return (DDI_FAILURE);
707*11596SJason.Beloro@Sun.COM }
7083446Smrj
7093446Smrj /*
7103446Smrj * Support for the "command-preserve" property.
7113446Smrj */
7123446Smrj command_preserve = ddi_prop_get_int(DDI_DEV_T_ANY, child,
7133446Smrj DDI_PROP_DONTPASS, "command-preserve", 0);
7143446Smrj command = pci_config_get16(config_handle, PCI_CONF_COMM);
7153446Smrj command &= (command_preserve | PCI_COMM_BACK2BACK_ENAB);
7163446Smrj command |= (ppb_command_default & ~command_preserve);
7173446Smrj pci_config_put16(config_handle, PCI_CONF_COMM, command);
7183446Smrj
7193446Smrj pci_config_teardown(&config_handle);
7203446Smrj return (DDI_SUCCESS);
7213446Smrj }
7223446Smrj
7233446Smrj static void
ppb_removechild(dev_info_t * dip)7243446Smrj ppb_removechild(dev_info_t *dip)
7253446Smrj {
7263446Smrj struct ddi_parent_private_data *pdptr;
7276313Skrishnae ppb_devstate_t *ppb;
7283446Smrj
7296313Skrishnae ppb = (ppb_devstate_t *)ddi_get_soft_state(ppb_state,
7306313Skrishnae ddi_get_instance(ddi_get_parent(dip)));
7316313Skrishnae
732*11596SJason.Beloro@Sun.COM if (ppb->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
733*11596SJason.Beloro@Sun.COM pcie_fini_dom(dip);
73411245SZhijun.Fu@Sun.COM pcie_fini_cfghdl(dip);
735*11596SJason.Beloro@Sun.COM } else if ((pdptr = ddi_get_parent_data(dip)) != NULL) {
7363446Smrj kmem_free(pdptr, (sizeof (*pdptr) + sizeof (struct intrspec)));
7373446Smrj ddi_set_parent_data(dip, NULL);
7383446Smrj }
7393446Smrj ddi_set_name_addr(dip, NULL);
7403446Smrj
7413446Smrj /*
7423446Smrj * Strip the node to properly convert it back to prototype form
7433446Smrj */
7443446Smrj ddi_remove_minor_node(dip, NULL);
7453446Smrj
7463446Smrj impl_rem_dev_props(dip);
7473446Smrj }
7483446Smrj
7493446Smrj /*
7503446Smrj * ppb_save_config_regs
7513446Smrj *
7523446Smrj * This routine saves the state of the configuration registers of all
7533446Smrj * the child nodes of each PBM.
7543446Smrj *
7553446Smrj * used by: ppb_detach() on suspends
7563446Smrj *
7573446Smrj * return value: none
7583446Smrj */
7593446Smrj static void
ppb_save_config_regs(ppb_devstate_t * ppb_p)7603446Smrj ppb_save_config_regs(ppb_devstate_t *ppb_p)
7613446Smrj {
7623446Smrj int i;
7633446Smrj dev_info_t *dip;
7643446Smrj ddi_acc_handle_t config_handle;
7653446Smrj
7663446Smrj for (i = 0, dip = ddi_get_child(ppb_p->dip); dip != NULL;
7675295Srandyf i++, dip = ddi_get_next_sibling(dip)) {
7683446Smrj
7693446Smrj if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
7703446Smrj cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
7715295Srandyf ddi_driver_name(ppb_p->dip),
7725295Srandyf ddi_get_instance(ppb_p->dip),
7735295Srandyf ddi_driver_name(dip),
7745295Srandyf ddi_get_instance(dip));
7753446Smrj continue;
7763446Smrj }
7773446Smrj
7783446Smrj ppb_p->config_state[i].dip = dip;
7793446Smrj ppb_p->config_state[i].command =
7805295Srandyf pci_config_get16(config_handle, PCI_CONF_COMM);
7813446Smrj pci_config_teardown(&config_handle);
7823446Smrj }
7833446Smrj ppb_p->config_state_index = i;
7843446Smrj }
7853446Smrj
7863446Smrj
7873446Smrj /*
7883446Smrj * ppb_restore_config_regs
7893446Smrj *
7903446Smrj * This routine restores the state of the configuration registers of all
7913446Smrj * the child nodes of each PBM.
7923446Smrj *
7933446Smrj * used by: ppb_attach() on resume
7943446Smrj *
7953446Smrj * return value: none
7963446Smrj */
7973446Smrj static void
ppb_restore_config_regs(ppb_devstate_t * ppb_p)7983446Smrj ppb_restore_config_regs(ppb_devstate_t *ppb_p)
7993446Smrj {
8003446Smrj int i;
8013446Smrj dev_info_t *dip;
8023446Smrj ddi_acc_handle_t config_handle;
8033446Smrj
8043446Smrj for (i = 0; i < ppb_p->config_state_index; i++) {
8053446Smrj dip = ppb_p->config_state[i].dip;
8063446Smrj if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
8073446Smrj cmn_err(CE_WARN, "%s%d: can't config space for %s%d\n",
8085295Srandyf ddi_driver_name(ppb_p->dip),
8095295Srandyf ddi_get_instance(ppb_p->dip),
8105295Srandyf ddi_driver_name(dip),
8115295Srandyf ddi_get_instance(dip));
8123446Smrj continue;
8133446Smrj }
8143446Smrj pci_config_put16(config_handle, PCI_CONF_COMM,
8155295Srandyf ppb_p->config_state[i].command);
8163446Smrj pci_config_teardown(&config_handle);
8173446Smrj }
8183446Smrj }
8193446Smrj
8203446Smrj
8213446Smrj static boolean_t
ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl)8223446Smrj ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl)
8233446Smrj {
8249970SJimmy.Vetayases@Sun.COM uint16_t ptr;
8253446Smrj
8269970SJimmy.Vetayases@Sun.COM if (pci_htcap_locate(cfg_hdl,
8279970SJimmy.Vetayases@Sun.COM PCI_HTCAP_TYPE_MASK | PCI_HTCAP_MSIMAP_ENABLE_MASK,
8289970SJimmy.Vetayases@Sun.COM PCI_HTCAP_MSIMAP_TYPE | PCI_HTCAP_MSIMAP_ENABLE, &ptr) !=
8299970SJimmy.Vetayases@Sun.COM DDI_SUCCESS)
8303446Smrj return (B_FALSE);
8313446Smrj
8323446Smrj return (B_TRUE);
8333446Smrj }
8343446Smrj
8353446Smrj
8363446Smrj static int
ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl,int cmd)8373446Smrj ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd)
8383446Smrj {
8399970SJimmy.Vetayases@Sun.COM uint16_t ptr;
8403446Smrj uint16_t reg;
8413446Smrj
8429970SJimmy.Vetayases@Sun.COM if (pci_htcap_locate(cfg_hdl, PCI_HTCAP_TYPE_MASK,
8439970SJimmy.Vetayases@Sun.COM PCI_HTCAP_MSIMAP_TYPE, &ptr) != DDI_SUCCESS)
8443446Smrj return (0);
8453446Smrj
8463446Smrj reg = pci_config_get16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF);
8473446Smrj switch (cmd) {
8483446Smrj case HT_MSIMAP_ENABLE:
8499970SJimmy.Vetayases@Sun.COM reg |= PCI_HTCAP_MSIMAP_ENABLE;
8503446Smrj break;
8513446Smrj case HT_MSIMAP_DISABLE:
8523446Smrj default:
8539970SJimmy.Vetayases@Sun.COM reg &= ~(uint16_t)PCI_HTCAP_MSIMAP_ENABLE;
8543446Smrj }
8553446Smrj
8563446Smrj pci_config_put16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF, reg);
8573446Smrj return (1);
8583446Smrj }
8593446Smrj
8603446Smrj
8613446Smrj /*
8623446Smrj * intercept certain interrupt services to handle special cases
8633446Smrj */
8643446Smrj static int
ppb_intr_ops(dev_info_t * pdip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)8653446Smrj ppb_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
8663446Smrj ddi_intr_handle_impl_t *hdlp, void *result)
8673446Smrj {
8683446Smrj ddi_acc_handle_t cfg_hdl;
8693446Smrj int rv = DDI_SUCCESS;
8703446Smrj
8713446Smrj if (intr_op != DDI_INTROP_SUPPORTED_TYPES)
8723446Smrj return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result));
8733446Smrj
8743446Smrj DDI_INTR_NEXDBG((CE_CONT,
8753446Smrj "ppb_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
8763446Smrj (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
8773446Smrj
8783446Smrj /* Fixed interrupt is supported by default */
8793446Smrj *(int *)result = DDI_INTR_TYPE_FIXED;
8803446Smrj
8813446Smrj if (ppb_support_msi == -1) {
8823446Smrj DDI_INTR_NEXDBG((CE_CONT,
8833446Smrj "ppb_intr_ops: MSI is not allowed\n"));
8843446Smrj goto OUT;
8853446Smrj }
8863446Smrj
8873446Smrj if (ppb_support_msi == 1) {
8883446Smrj DDI_INTR_NEXDBG((CE_CONT,
8893446Smrj "ppb_intr_ops: MSI is always allowed\n"));
8903446Smrj rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
8913446Smrj goto OUT;
8923446Smrj }
8933446Smrj
8943446Smrj if (pci_config_setup(pdip, &cfg_hdl) != DDI_SUCCESS) {
8953446Smrj DDI_INTR_NEXDBG((CE_CONT,
8963446Smrj "ppb_intr_ops: pci_config_setup() failed\n"));
8973446Smrj goto OUT;
8983446Smrj }
8993446Smrj
9003446Smrj /*
9013446Smrj * check for hypertransport msi mapping capability
9023446Smrj */
9033446Smrj if (ppb_ht_msimap_check(cfg_hdl)) {
9043446Smrj DDI_INTR_NEXDBG((CE_CONT,
9053446Smrj "ppb_intr_ops: HT MSI mapping enabled\n"));
9063446Smrj rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result);
9073446Smrj }
9083446Smrj
9093446Smrj /*
9103446Smrj * if we add failure conditions after pci_config_setup, move this to
9113446Smrj * OUT and use an extra flag to indicate the need to teardown cfg_hdl
9123446Smrj */
9133446Smrj pci_config_teardown(&cfg_hdl);
9143446Smrj
9153446Smrj OUT:
9163446Smrj DDI_INTR_NEXDBG((CE_CONT,
9173446Smrj "ppb_intr_ops: rdip 0x%p, returns supported types: 0x%x\n",
9183446Smrj (void *)rdip, *(int *)result));
9193446Smrj return (rv);
9203446Smrj }
9213446Smrj
92210923SEvan.Yan@Sun.COM /* ARGSUSED */
9233446Smrj static int
ppb_open(dev_t * devp,int flags,int otyp,cred_t * credp)9243446Smrj ppb_open(dev_t *devp, int flags, int otyp, cred_t *credp)
9253446Smrj {
92610923SEvan.Yan@Sun.COM int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(*devp));
92710923SEvan.Yan@Sun.COM ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
92810923SEvan.Yan@Sun.COM int rv;
92910923SEvan.Yan@Sun.COM
93010923SEvan.Yan@Sun.COM if (ppb_p == NULL)
93110923SEvan.Yan@Sun.COM return (ENXIO);
93210923SEvan.Yan@Sun.COM
93310923SEvan.Yan@Sun.COM /*
93410923SEvan.Yan@Sun.COM * Ioctls will be handled by PCI Express framework for all
93510923SEvan.Yan@Sun.COM * PCIe platforms
93610923SEvan.Yan@Sun.COM */
93710923SEvan.Yan@Sun.COM if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
93810923SEvan.Yan@Sun.COM mutex_enter(&ppb_p->ppb_mutex);
93910923SEvan.Yan@Sun.COM rv = pcie_open(ppb_p->dip, devp, flags, otyp, credp);
94010923SEvan.Yan@Sun.COM mutex_exit(&ppb_p->ppb_mutex);
94110923SEvan.Yan@Sun.COM return (rv);
94210923SEvan.Yan@Sun.COM }
94310923SEvan.Yan@Sun.COM
9443446Smrj return ((pcihp_get_cb_ops())->cb_open(devp, flags, otyp, credp));
9453446Smrj }
9463446Smrj
94710923SEvan.Yan@Sun.COM /* ARGSUSED */
9483446Smrj static int
ppb_close(dev_t dev,int flags,int otyp,cred_t * credp)9493446Smrj ppb_close(dev_t dev, int flags, int otyp, cred_t *credp)
9503446Smrj {
95110923SEvan.Yan@Sun.COM int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
95210923SEvan.Yan@Sun.COM ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
95310923SEvan.Yan@Sun.COM int rv;
95410923SEvan.Yan@Sun.COM
95510923SEvan.Yan@Sun.COM if (ppb_p == NULL)
95610923SEvan.Yan@Sun.COM return (ENXIO);
95710923SEvan.Yan@Sun.COM
95810923SEvan.Yan@Sun.COM mutex_enter(&ppb_p->ppb_mutex);
95910923SEvan.Yan@Sun.COM /*
96010923SEvan.Yan@Sun.COM * Ioctls will be handled by PCI Express framework for all
96110923SEvan.Yan@Sun.COM * PCIe platforms
96210923SEvan.Yan@Sun.COM */
96310923SEvan.Yan@Sun.COM if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV) {
96410923SEvan.Yan@Sun.COM rv = pcie_close(ppb_p->dip, dev, flags, otyp, credp);
96510923SEvan.Yan@Sun.COM mutex_exit(&ppb_p->ppb_mutex);
96610923SEvan.Yan@Sun.COM return (rv);
96710923SEvan.Yan@Sun.COM }
96810923SEvan.Yan@Sun.COM
96910923SEvan.Yan@Sun.COM mutex_exit(&ppb_p->ppb_mutex);
9703446Smrj return ((pcihp_get_cb_ops())->cb_close(dev, flags, otyp, credp));
9713446Smrj }
9723446Smrj
97310923SEvan.Yan@Sun.COM /*
97410923SEvan.Yan@Sun.COM * ppb_ioctl: devctl hotplug controls
97510923SEvan.Yan@Sun.COM */
97610923SEvan.Yan@Sun.COM /* ARGSUSED */
9773446Smrj static int
ppb_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)97810923SEvan.Yan@Sun.COM ppb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
97910923SEvan.Yan@Sun.COM int *rvalp)
9803446Smrj {
98110923SEvan.Yan@Sun.COM int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
98210923SEvan.Yan@Sun.COM ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
98310923SEvan.Yan@Sun.COM
98410923SEvan.Yan@Sun.COM if (ppb_p == NULL)
98510923SEvan.Yan@Sun.COM return (ENXIO);
98610923SEvan.Yan@Sun.COM
98710923SEvan.Yan@Sun.COM /*
98810923SEvan.Yan@Sun.COM * Ioctls will be handled by PCI Express framework for all
98910923SEvan.Yan@Sun.COM * PCIe platforms
99010923SEvan.Yan@Sun.COM */
99110923SEvan.Yan@Sun.COM if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
99210923SEvan.Yan@Sun.COM return (pcie_ioctl(ppb_p->dip, dev, cmd, arg, mode, credp,
99310923SEvan.Yan@Sun.COM rvalp));
99410923SEvan.Yan@Sun.COM
9953446Smrj return ((pcihp_get_cb_ops())->cb_ioctl(dev, cmd, arg, mode, credp,
9963446Smrj rvalp));
9973446Smrj }
9983446Smrj
9993446Smrj static int
ppb_prop_op(dev_t dev,dev_info_t * dip,ddi_prop_op_t prop_op,int flags,char * name,caddr_t valuep,int * lengthp)100010923SEvan.Yan@Sun.COM ppb_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int flags,
100110923SEvan.Yan@Sun.COM char *name, caddr_t valuep, int *lengthp)
10023446Smrj {
100310923SEvan.Yan@Sun.COM int instance = PCI_MINOR_NUM_TO_INSTANCE(getminor(dev));
100410923SEvan.Yan@Sun.COM ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
100510923SEvan.Yan@Sun.COM
100610923SEvan.Yan@Sun.COM if (ppb_p == NULL)
100710923SEvan.Yan@Sun.COM return (ENXIO);
100810923SEvan.Yan@Sun.COM
100910923SEvan.Yan@Sun.COM if (ppb_p->parent_bus == PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
101010923SEvan.Yan@Sun.COM return (pcie_prop_op(dev, dip, prop_op, flags, name,
101110923SEvan.Yan@Sun.COM valuep, lengthp));
101210923SEvan.Yan@Sun.COM
10133446Smrj return ((pcihp_get_cb_ops())->cb_prop_op(dev, dip, prop_op, flags,
10143446Smrj name, valuep, lengthp));
10153446Smrj }
10163446Smrj
10173446Smrj static int
ppb_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)10183446Smrj ppb_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
10193446Smrj {
102010923SEvan.Yan@Sun.COM minor_t minor = getminor((dev_t)arg);
102110923SEvan.Yan@Sun.COM int instance = PCI_MINOR_NUM_TO_INSTANCE(minor);
102210923SEvan.Yan@Sun.COM ppb_devstate_t *ppb_p = ddi_get_soft_state(ppb_state, instance);
102310923SEvan.Yan@Sun.COM
102410923SEvan.Yan@Sun.COM if (ppb_p == NULL)
102510923SEvan.Yan@Sun.COM return (DDI_FAILURE);
102610923SEvan.Yan@Sun.COM
102710923SEvan.Yan@Sun.COM if (ppb_p->parent_bus != PCIE_PCIECAP_DEV_TYPE_PCIE_DEV)
102810923SEvan.Yan@Sun.COM return (pcihp_info(dip, cmd, arg, result));
102910923SEvan.Yan@Sun.COM
103010923SEvan.Yan@Sun.COM switch (cmd) {
103110923SEvan.Yan@Sun.COM default:
103210923SEvan.Yan@Sun.COM return (DDI_FAILURE);
103310923SEvan.Yan@Sun.COM
103410923SEvan.Yan@Sun.COM case DDI_INFO_DEVT2INSTANCE:
103510923SEvan.Yan@Sun.COM *result = (void *)(uintptr_t)instance;
103610923SEvan.Yan@Sun.COM return (DDI_SUCCESS);
103710923SEvan.Yan@Sun.COM
103810923SEvan.Yan@Sun.COM case DDI_INFO_DEVT2DEVINFO:
103910923SEvan.Yan@Sun.COM if (ppb_p == NULL)
104010923SEvan.Yan@Sun.COM return (DDI_FAILURE);
104110923SEvan.Yan@Sun.COM *result = (void *)ppb_p->dip;
104210923SEvan.Yan@Sun.COM return (DDI_SUCCESS);
104310923SEvan.Yan@Sun.COM }
10443446Smrj }
10453446Smrj
ppb_peekpoke_cb(dev_info_t * dip,ddi_fm_error_t * derr)10466313Skrishnae void ppb_peekpoke_cb(dev_info_t *dip, ddi_fm_error_t *derr) {
10476313Skrishnae (void) pci_ereport_post(dip, derr, NULL);
10486313Skrishnae }
10496313Skrishnae
10503446Smrj /*ARGSUSED*/
10513446Smrj static int
ppb_fm_init(dev_info_t * dip,dev_info_t * tdip,int cap,ddi_iblock_cookie_t * ibc)10523446Smrj ppb_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap,
10533446Smrj ddi_iblock_cookie_t *ibc)
10543446Smrj {
10553446Smrj ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state,
10563446Smrj ddi_get_instance(dip));
10573446Smrj
10583446Smrj ASSERT(ibc != NULL);
10593446Smrj *ibc = ppb->ppb_fm_ibc;
10603446Smrj
10613446Smrj return (ppb->ppb_fmcap);
10623446Smrj }
10633446Smrj
10643446Smrj /*ARGSUSED*/
10653446Smrj static int
ppb_fm_callback(dev_info_t * dip,ddi_fm_error_t * derr,const void * no_used)10663446Smrj ppb_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *no_used)
10673446Smrj {
10683446Smrj ppb_devstate_t *ppb = ddi_get_soft_state(ppb_state,
10693446Smrj ddi_get_instance(dip));
10703446Smrj
10713446Smrj mutex_enter(&ppb->ppb_err_mutex);
10723446Smrj pci_ereport_post(dip, derr, NULL);
10733446Smrj mutex_exit(&ppb->ppb_err_mutex);
10743446Smrj return (derr->fme_status);
10753446Smrj }
1076