1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate #include <sys/types.h> 30*7c478bd9Sstevel@tonic-gate #include <sys/sunndi.h> 31*7c478bd9Sstevel@tonic-gate #include <sys/ddifm_impl.h> 32*7c478bd9Sstevel@tonic-gate #include <sys/fm/util.h> 33*7c478bd9Sstevel@tonic-gate #include <sys/fm/protocol.h> 34*7c478bd9Sstevel@tonic-gate #include <sys/fm/io/pci.h> 35*7c478bd9Sstevel@tonic-gate #include <sys/fm/io/ddi.h> 36*7c478bd9Sstevel@tonic-gate #include <sys/pci.h> 37*7c478bd9Sstevel@tonic-gate #include <sys/pci_impl.h> 38*7c478bd9Sstevel@tonic-gate #include <sys/epm.h> 39*7c478bd9Sstevel@tonic-gate 40*7c478bd9Sstevel@tonic-gate 41*7c478bd9Sstevel@tonic-gate int 42*7c478bd9Sstevel@tonic-gate pci_config_setup(dev_info_t *dip, ddi_acc_handle_t *handle) 43*7c478bd9Sstevel@tonic-gate { 44*7c478bd9Sstevel@tonic-gate caddr_t cfgaddr; 45*7c478bd9Sstevel@tonic-gate ddi_device_acc_attr_t attr; 46*7c478bd9Sstevel@tonic-gate 47*7c478bd9Sstevel@tonic-gate attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 48*7c478bd9Sstevel@tonic-gate attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 49*7c478bd9Sstevel@tonic-gate attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 50*7c478bd9Sstevel@tonic-gate 51*7c478bd9Sstevel@tonic-gate /* Check for fault management capabilities */ 52*7c478bd9Sstevel@tonic-gate if (DDI_FM_ACC_ERR_CAP(ddi_fm_capable(dip))) 53*7c478bd9Sstevel@tonic-gate attr.devacc_attr_access = DDI_FLAGERR_ACC; 54*7c478bd9Sstevel@tonic-gate 55*7c478bd9Sstevel@tonic-gate return (ddi_regs_map_setup(dip, 0, &cfgaddr, 0, 0, &attr, handle)); 56*7c478bd9Sstevel@tonic-gate } 57*7c478bd9Sstevel@tonic-gate 58*7c478bd9Sstevel@tonic-gate void 59*7c478bd9Sstevel@tonic-gate pci_config_teardown(ddi_acc_handle_t *handle) 60*7c478bd9Sstevel@tonic-gate { 61*7c478bd9Sstevel@tonic-gate ddi_regs_map_free(handle); 62*7c478bd9Sstevel@tonic-gate } 63*7c478bd9Sstevel@tonic-gate 64*7c478bd9Sstevel@tonic-gate /* 65*7c478bd9Sstevel@tonic-gate * pci_ereport_setup, pci_ereport_teardown, pci_ereport_post: 66*7c478bd9Sstevel@tonic-gate * Interfaces to be used by ereport capable PCI device drivers to setup, 67*7c478bd9Sstevel@tonic-gate * teardown, and post generic PCI error reports. This is to guarantee a 68*7c478bd9Sstevel@tonic-gate * consistant error report model for all PCI devices. Please see 69*7c478bd9Sstevel@tonic-gate * PSARC/2004/391. 70*7c478bd9Sstevel@tonic-gate */ 71*7c478bd9Sstevel@tonic-gate 72*7c478bd9Sstevel@tonic-gate typedef struct pci_erpt { 73*7c478bd9Sstevel@tonic-gate caddr_t pci_cfg_addr; /* Config space address */ 74*7c478bd9Sstevel@tonic-gate ddi_acc_handle_t pci_cfg_hdl; /* Config space access handle */ 75*7c478bd9Sstevel@tonic-gate } pci_erpt_t; 76*7c478bd9Sstevel@tonic-gate 77*7c478bd9Sstevel@tonic-gate pci_fm_err_t pci_err_tbl[] = { 78*7c478bd9Sstevel@tonic-gate PCI_DET_PERR, PCI_STAT_PERROR, NULL, 79*7c478bd9Sstevel@tonic-gate PCI_MDPE, PCI_STAT_S_PERROR, PCI_TARG_MDPE, 80*7c478bd9Sstevel@tonic-gate PCI_SIG_SERR, PCI_STAT_S_SYSERR, NULL, 81*7c478bd9Sstevel@tonic-gate PCI_MA, PCI_STAT_R_MAST_AB, PCI_TARG_MA, 82*7c478bd9Sstevel@tonic-gate PCI_REC_TA, PCI_STAT_R_TARG_AB, PCI_TARG_REC_TA, 83*7c478bd9Sstevel@tonic-gate PCI_SIG_TA, PCI_STAT_S_TARG_AB, NULL, 84*7c478bd9Sstevel@tonic-gate NULL, NULL, 85*7c478bd9Sstevel@tonic-gate }; 86*7c478bd9Sstevel@tonic-gate 87*7c478bd9Sstevel@tonic-gate pci_fm_err_t pci_bdg_err_tbl[] = { 88*7c478bd9Sstevel@tonic-gate PCI_DET_PERR, PCI_STAT_PERROR, NULL, 89*7c478bd9Sstevel@tonic-gate PCI_MDPE, PCI_STAT_S_PERROR, NULL, 90*7c478bd9Sstevel@tonic-gate PCI_REC_SERR, PCI_STAT_S_SYSERR, NULL, 91*7c478bd9Sstevel@tonic-gate PCI_MA, PCI_STAT_R_MAST_AB, NULL, 92*7c478bd9Sstevel@tonic-gate PCI_REC_TA, PCI_STAT_R_TARG_AB, NULL, 93*7c478bd9Sstevel@tonic-gate PCI_SIG_TA, PCI_STAT_S_TARG_AB, NULL, 94*7c478bd9Sstevel@tonic-gate NULL, NULL, 95*7c478bd9Sstevel@tonic-gate }; 96*7c478bd9Sstevel@tonic-gate void 97*7c478bd9Sstevel@tonic-gate pci_ereport_setup(dev_info_t *dip) 98*7c478bd9Sstevel@tonic-gate { 99*7c478bd9Sstevel@tonic-gate struct dev_info *devi = DEVI(dip); 100*7c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl = devi->devi_fmhdl; 101*7c478bd9Sstevel@tonic-gate pci_erpt_t *erpt_p; 102*7c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *hp; 103*7c478bd9Sstevel@tonic-gate 104*7c478bd9Sstevel@tonic-gate if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip))) { 105*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP); 106*7c478bd9Sstevel@tonic-gate return; 107*7c478bd9Sstevel@tonic-gate } 108*7c478bd9Sstevel@tonic-gate 109*7c478bd9Sstevel@tonic-gate ASSERT(fmhdl); 110*7c478bd9Sstevel@tonic-gate ASSERT(fmhdl->fh_bus_specific == NULL); 111*7c478bd9Sstevel@tonic-gate 112*7c478bd9Sstevel@tonic-gate if ((erpt_p = kmem_zalloc(sizeof (pci_erpt_t), KM_SLEEP)) == NULL) 113*7c478bd9Sstevel@tonic-gate return; 114*7c478bd9Sstevel@tonic-gate 115*7c478bd9Sstevel@tonic-gate if (pci_config_setup(dip, &erpt_p->pci_cfg_hdl) == DDI_SUCCESS) { 116*7c478bd9Sstevel@tonic-gate hp = impl_acc_hdl_get(erpt_p->pci_cfg_hdl); 117*7c478bd9Sstevel@tonic-gate erpt_p->pci_cfg_addr = (caddr_t)hp->ah_addr; 118*7c478bd9Sstevel@tonic-gate fmhdl->fh_bus_specific = (void *)erpt_p; 119*7c478bd9Sstevel@tonic-gate } 120*7c478bd9Sstevel@tonic-gate } 121*7c478bd9Sstevel@tonic-gate 122*7c478bd9Sstevel@tonic-gate void 123*7c478bd9Sstevel@tonic-gate pci_ereport_teardown(dev_info_t *dip) 124*7c478bd9Sstevel@tonic-gate { 125*7c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl; 126*7c478bd9Sstevel@tonic-gate pci_erpt_t *erpt_p; 127*7c478bd9Sstevel@tonic-gate 128*7c478bd9Sstevel@tonic-gate if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip))) { 129*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP); 130*7c478bd9Sstevel@tonic-gate } 131*7c478bd9Sstevel@tonic-gate 132*7c478bd9Sstevel@tonic-gate ASSERT(fmhdl); 133*7c478bd9Sstevel@tonic-gate 134*7c478bd9Sstevel@tonic-gate erpt_p = (pci_erpt_t *)fmhdl->fh_bus_specific; 135*7c478bd9Sstevel@tonic-gate if (erpt_p == NULL) 136*7c478bd9Sstevel@tonic-gate return; 137*7c478bd9Sstevel@tonic-gate 138*7c478bd9Sstevel@tonic-gate pci_config_teardown(&erpt_p->pci_cfg_hdl); 139*7c478bd9Sstevel@tonic-gate kmem_free(erpt_p, sizeof (pci_erpt_t)); 140*7c478bd9Sstevel@tonic-gate fmhdl->fh_bus_specific = NULL; 141*7c478bd9Sstevel@tonic-gate } 142*7c478bd9Sstevel@tonic-gate 143*7c478bd9Sstevel@tonic-gate void 144*7c478bd9Sstevel@tonic-gate pci_ereport_post(dev_info_t *dip, ddi_fm_error_t *derr, uint16_t *status) 145*7c478bd9Sstevel@tonic-gate { 146*7c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl; 147*7c478bd9Sstevel@tonic-gate pci_erpt_t *erpt_p; 148*7c478bd9Sstevel@tonic-gate char buf[FM_MAX_CLASS]; 149*7c478bd9Sstevel@tonic-gate uint16_t cfg_comm = 0xffff; 150*7c478bd9Sstevel@tonic-gate uint16_t cfg_stat = 0xffff; 151*7c478bd9Sstevel@tonic-gate int i; 152*7c478bd9Sstevel@tonic-gate fmhdl = DEVI(dip)->devi_fmhdl; 153*7c478bd9Sstevel@tonic-gate 154*7c478bd9Sstevel@tonic-gate if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip))) { 155*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP); 156*7c478bd9Sstevel@tonic-gate return; 157*7c478bd9Sstevel@tonic-gate } 158*7c478bd9Sstevel@tonic-gate 159*7c478bd9Sstevel@tonic-gate ASSERT(fmhdl); 160*7c478bd9Sstevel@tonic-gate 161*7c478bd9Sstevel@tonic-gate derr->fme_ena = derr->fme_ena ? derr->fme_ena : fm_ena_generate(0, 162*7c478bd9Sstevel@tonic-gate FM_ENA_FMT1); 163*7c478bd9Sstevel@tonic-gate 164*7c478bd9Sstevel@tonic-gate erpt_p = (pci_erpt_t *)fmhdl->fh_bus_specific; 165*7c478bd9Sstevel@tonic-gate if (erpt_p == NULL) { 166*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP); 167*7c478bd9Sstevel@tonic-gate return; 168*7c478bd9Sstevel@tonic-gate } 169*7c478bd9Sstevel@tonic-gate 170*7c478bd9Sstevel@tonic-gate if ((cfg_stat = ddi_get16(erpt_p->pci_cfg_hdl, 171*7c478bd9Sstevel@tonic-gate (uint16_t *)(erpt_p->pci_cfg_addr + PCI_CONF_STAT))) == 0xffff) { 172*7c478bd9Sstevel@tonic-gate (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", 173*7c478bd9Sstevel@tonic-gate PCI_ERROR_SUBCLASS, PCI_NR); 174*7c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, 175*7c478bd9Sstevel@tonic-gate FM_VERSION, DATA_TYPE_UINT8, 0, NULL); 176*7c478bd9Sstevel@tonic-gate goto done; 177*7c478bd9Sstevel@tonic-gate } 178*7c478bd9Sstevel@tonic-gate if ((cfg_comm = ddi_get16(erpt_p->pci_cfg_hdl, 179*7c478bd9Sstevel@tonic-gate (uint16_t *)(erpt_p->pci_cfg_addr + PCI_CONF_COMM))) == 0xffff) { 180*7c478bd9Sstevel@tonic-gate (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", 181*7c478bd9Sstevel@tonic-gate PCI_ERROR_SUBCLASS, PCI_NR); 182*7c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, 183*7c478bd9Sstevel@tonic-gate FM_VERSION, DATA_TYPE_UINT8, 0, NULL); 184*7c478bd9Sstevel@tonic-gate goto done; 185*7c478bd9Sstevel@tonic-gate } 186*7c478bd9Sstevel@tonic-gate 187*7c478bd9Sstevel@tonic-gate if (derr->fme_flag == DDI_FM_ERR_UNEXPECTED) { 188*7c478bd9Sstevel@tonic-gate for (i = 0; pci_err_tbl[i].err_class != NULL; i++) { 189*7c478bd9Sstevel@tonic-gate if (cfg_stat & pci_err_tbl[i].reg_bit) { 190*7c478bd9Sstevel@tonic-gate 191*7c478bd9Sstevel@tonic-gate /* 192*7c478bd9Sstevel@tonic-gate * Generate an ereport for this error bit. 193*7c478bd9Sstevel@tonic-gate */ 194*7c478bd9Sstevel@tonic-gate (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", 195*7c478bd9Sstevel@tonic-gate PCI_ERROR_SUBCLASS, 196*7c478bd9Sstevel@tonic-gate pci_err_tbl[i].err_class); 197*7c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(dip, buf, derr->fme_ena, 198*7c478bd9Sstevel@tonic-gate DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, 199*7c478bd9Sstevel@tonic-gate PCI_CONFIG_STATUS, DATA_TYPE_UINT16, 200*7c478bd9Sstevel@tonic-gate cfg_stat, PCI_CONFIG_COMMAND, 201*7c478bd9Sstevel@tonic-gate DATA_TYPE_UINT16, cfg_comm, NULL); 202*7c478bd9Sstevel@tonic-gate 203*7c478bd9Sstevel@tonic-gate /* 204*7c478bd9Sstevel@tonic-gate * Generate a corresponding ereport on behalf 205*7c478bd9Sstevel@tonic-gate * of the target (the parent dip) of the 206*7c478bd9Sstevel@tonic-gate * transaction. 207*7c478bd9Sstevel@tonic-gate */ 208*7c478bd9Sstevel@tonic-gate if (pci_err_tbl[i].terr_class != NULL && 209*7c478bd9Sstevel@tonic-gate DDI_FM_EREPORT_CAP(ddi_fm_capable( 210*7c478bd9Sstevel@tonic-gate (dev_info_t *)DEVI(dip)->devi_parent))) { 211*7c478bd9Sstevel@tonic-gate (void) snprintf(buf, FM_MAX_CLASS, 212*7c478bd9Sstevel@tonic-gate "%s.%s", PCI_ERROR_SUBCLASS, 213*7c478bd9Sstevel@tonic-gate pci_err_tbl[i].terr_class); 214*7c478bd9Sstevel@tonic-gate ddi_fm_ereport_post((dev_info_t *) 215*7c478bd9Sstevel@tonic-gate DEVI(dip)->devi_parent, buf, 216*7c478bd9Sstevel@tonic-gate derr->fme_ena, DDI_NOSLEEP, 217*7c478bd9Sstevel@tonic-gate FM_VERSION, DATA_TYPE_UINT8, 0, 218*7c478bd9Sstevel@tonic-gate NULL); 219*7c478bd9Sstevel@tonic-gate } 220*7c478bd9Sstevel@tonic-gate } 221*7c478bd9Sstevel@tonic-gate } 222*7c478bd9Sstevel@tonic-gate } 223*7c478bd9Sstevel@tonic-gate 224*7c478bd9Sstevel@tonic-gate /* 225*7c478bd9Sstevel@tonic-gate * Clear error bits 226*7c478bd9Sstevel@tonic-gate */ 227*7c478bd9Sstevel@tonic-gate ddi_put16(erpt_p->pci_cfg_hdl, 228*7c478bd9Sstevel@tonic-gate (uint16_t *)(erpt_p->pci_cfg_addr + PCI_CONF_STAT), 229*7c478bd9Sstevel@tonic-gate (uint16_t)cfg_stat); 230*7c478bd9Sstevel@tonic-gate done: 231*7c478bd9Sstevel@tonic-gate if (status != NULL) 232*7c478bd9Sstevel@tonic-gate *status = cfg_stat; 233*7c478bd9Sstevel@tonic-gate } 234*7c478bd9Sstevel@tonic-gate 235*7c478bd9Sstevel@tonic-gate /* 236*7c478bd9Sstevel@tonic-gate * Generic pci-pci bridge error report function 237*7c478bd9Sstevel@tonic-gate */ 238*7c478bd9Sstevel@tonic-gate void 239*7c478bd9Sstevel@tonic-gate pci_bdg_ereport_post(dev_info_t *dip, ddi_fm_error_t *derr, uint16_t *status) 240*7c478bd9Sstevel@tonic-gate { 241*7c478bd9Sstevel@tonic-gate struct i_ddi_fmhdl *fmhdl; 242*7c478bd9Sstevel@tonic-gate pci_erpt_t *erpt_p; 243*7c478bd9Sstevel@tonic-gate char buf[FM_MAX_CLASS]; 244*7c478bd9Sstevel@tonic-gate uint16_t bdg_ctrl = 0xffff; 245*7c478bd9Sstevel@tonic-gate uint16_t cfg_sec_stat = 0xffff; 246*7c478bd9Sstevel@tonic-gate int i; 247*7c478bd9Sstevel@tonic-gate 248*7c478bd9Sstevel@tonic-gate if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip))) { 249*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP); 250*7c478bd9Sstevel@tonic-gate return; 251*7c478bd9Sstevel@tonic-gate } 252*7c478bd9Sstevel@tonic-gate 253*7c478bd9Sstevel@tonic-gate fmhdl = DEVI(dip)->devi_fmhdl; 254*7c478bd9Sstevel@tonic-gate 255*7c478bd9Sstevel@tonic-gate ASSERT(fmhdl); 256*7c478bd9Sstevel@tonic-gate 257*7c478bd9Sstevel@tonic-gate derr->fme_ena = derr->fme_ena ? derr->fme_ena : fm_ena_generate(0, 258*7c478bd9Sstevel@tonic-gate FM_ENA_FMT1); 259*7c478bd9Sstevel@tonic-gate 260*7c478bd9Sstevel@tonic-gate erpt_p = (pci_erpt_t *)fmhdl->fh_bus_specific; 261*7c478bd9Sstevel@tonic-gate if (erpt_p == NULL) { 262*7c478bd9Sstevel@tonic-gate i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP); 263*7c478bd9Sstevel@tonic-gate return; 264*7c478bd9Sstevel@tonic-gate } 265*7c478bd9Sstevel@tonic-gate 266*7c478bd9Sstevel@tonic-gate if ((cfg_sec_stat = ddi_get16(erpt_p->pci_cfg_hdl, 267*7c478bd9Sstevel@tonic-gate (uint16_t *)(erpt_p->pci_cfg_addr + PCI_BCNF_SEC_STATUS))) 268*7c478bd9Sstevel@tonic-gate == 0xffff) { 269*7c478bd9Sstevel@tonic-gate (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCI_ERROR_SUBCLASS, 270*7c478bd9Sstevel@tonic-gate PCI_NR); 271*7c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, 272*7c478bd9Sstevel@tonic-gate FM_VERSION, DATA_TYPE_UINT8, 0, NULL); 273*7c478bd9Sstevel@tonic-gate goto done; 274*7c478bd9Sstevel@tonic-gate } 275*7c478bd9Sstevel@tonic-gate 276*7c478bd9Sstevel@tonic-gate if ((bdg_ctrl = ddi_get16(erpt_p->pci_cfg_hdl, 277*7c478bd9Sstevel@tonic-gate (uint16_t *)(erpt_p->pci_cfg_addr + PCI_BCNF_BCNTRL))) == 0xffff) { 278*7c478bd9Sstevel@tonic-gate (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCI_ERROR_SUBCLASS, 279*7c478bd9Sstevel@tonic-gate PCI_NR); 280*7c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, 281*7c478bd9Sstevel@tonic-gate FM_VERSION, DATA_TYPE_UINT8, 0, NULL); 282*7c478bd9Sstevel@tonic-gate goto done; 283*7c478bd9Sstevel@tonic-gate } 284*7c478bd9Sstevel@tonic-gate 285*7c478bd9Sstevel@tonic-gate if (derr->fme_flag == DDI_FM_ERR_UNEXPECTED) { 286*7c478bd9Sstevel@tonic-gate if (bdg_ctrl & PCI_BCNF_BCNTRL_DTO_STAT) { 287*7c478bd9Sstevel@tonic-gate (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", 288*7c478bd9Sstevel@tonic-gate PCI_ERROR_SUBCLASS, PCI_DTO); 289*7c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(dip, buf, derr->fme_ena, 290*7c478bd9Sstevel@tonic-gate DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, 291*7c478bd9Sstevel@tonic-gate PCI_SEC_CONFIG_STATUS, DATA_TYPE_UINT16, 292*7c478bd9Sstevel@tonic-gate cfg_sec_stat, PCI_BCNTRL, DATA_TYPE_UINT16, 293*7c478bd9Sstevel@tonic-gate bdg_ctrl, NULL); 294*7c478bd9Sstevel@tonic-gate } 295*7c478bd9Sstevel@tonic-gate 296*7c478bd9Sstevel@tonic-gate for (i = 0; pci_bdg_err_tbl[i].err_class != NULL; i++) { 297*7c478bd9Sstevel@tonic-gate if (cfg_sec_stat & pci_bdg_err_tbl[i].reg_bit) { 298*7c478bd9Sstevel@tonic-gate (void) snprintf(buf, FM_MAX_CLASS, "%s.%s-%s", 299*7c478bd9Sstevel@tonic-gate PCI_ERROR_SUBCLASS, PCI_SEC_ERROR_SUBCLASS, 300*7c478bd9Sstevel@tonic-gate pci_bdg_err_tbl[i].err_class); 301*7c478bd9Sstevel@tonic-gate ddi_fm_ereport_post(dip, buf, derr->fme_ena, 302*7c478bd9Sstevel@tonic-gate DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, 303*7c478bd9Sstevel@tonic-gate PCI_SEC_CONFIG_STATUS, DATA_TYPE_UINT16, 304*7c478bd9Sstevel@tonic-gate cfg_sec_stat, PCI_BCNTRL, DATA_TYPE_UINT16, 305*7c478bd9Sstevel@tonic-gate bdg_ctrl, NULL); 306*7c478bd9Sstevel@tonic-gate } 307*7c478bd9Sstevel@tonic-gate } 308*7c478bd9Sstevel@tonic-gate } 309*7c478bd9Sstevel@tonic-gate 310*7c478bd9Sstevel@tonic-gate /* 311*7c478bd9Sstevel@tonic-gate * Clear error bits 312*7c478bd9Sstevel@tonic-gate */ 313*7c478bd9Sstevel@tonic-gate ddi_put16(erpt_p->pci_cfg_hdl, (uint16_t *) 314*7c478bd9Sstevel@tonic-gate (erpt_p->pci_cfg_addr + PCI_BCNF_SEC_STATUS), 315*7c478bd9Sstevel@tonic-gate (uint16_t)cfg_sec_stat); 316*7c478bd9Sstevel@tonic-gate ddi_put16(erpt_p->pci_cfg_hdl, (uint16_t *) 317*7c478bd9Sstevel@tonic-gate (erpt_p->pci_cfg_addr + PCI_BCNF_BCNTRL), 318*7c478bd9Sstevel@tonic-gate (uint16_t)bdg_ctrl); 319*7c478bd9Sstevel@tonic-gate 320*7c478bd9Sstevel@tonic-gate done: 321*7c478bd9Sstevel@tonic-gate if (status != NULL) 322*7c478bd9Sstevel@tonic-gate *status = cfg_sec_stat; 323*7c478bd9Sstevel@tonic-gate } 324*7c478bd9Sstevel@tonic-gate 325*7c478bd9Sstevel@tonic-gate /* 326*7c478bd9Sstevel@tonic-gate * Generic pci-pci bridge error analysis function 327*7c478bd9Sstevel@tonic-gate */ 328*7c478bd9Sstevel@tonic-gate int 329*7c478bd9Sstevel@tonic-gate pci_bdg_check_status(dev_info_t *dip, ddi_fm_error_t *derr, 330*7c478bd9Sstevel@tonic-gate uint16_t pci_cfg_stat, uint16_t pci_cfg_sec_stat) 331*7c478bd9Sstevel@tonic-gate { 332*7c478bd9Sstevel@tonic-gate int ret; 333*7c478bd9Sstevel@tonic-gate int fatal = 0; 334*7c478bd9Sstevel@tonic-gate int nonfatal = 0; 335*7c478bd9Sstevel@tonic-gate int unknown = 0; 336*7c478bd9Sstevel@tonic-gate 337*7c478bd9Sstevel@tonic-gate if (derr->fme_flag == DDI_FM_ERR_POKE) { 338*7c478bd9Sstevel@tonic-gate /* 339*7c478bd9Sstevel@tonic-gate * special case for pokes - we only consider master abort 340*7c478bd9Sstevel@tonic-gate * and target abort as nonfatal. Sserr with no master abort is 341*7c478bd9Sstevel@tonic-gate * fatal, but master/target abort can come in on separate 342*7c478bd9Sstevel@tonic-gate * instance, so return unknown and parent will determine if 343*7c478bd9Sstevel@tonic-gate * nonfatal (if another child returned nonfatal - ie master 344*7c478bd9Sstevel@tonic-gate * or target abort) or fatal otherwise 345*7c478bd9Sstevel@tonic-gate */ 346*7c478bd9Sstevel@tonic-gate if (pci_cfg_sec_stat & (PCI_STAT_R_TARG_AB | 347*7c478bd9Sstevel@tonic-gate PCI_STAT_R_MAST_AB)) 348*7c478bd9Sstevel@tonic-gate nonfatal++; 349*7c478bd9Sstevel@tonic-gate if (pci_cfg_stat & PCI_STAT_S_SYSERR) 350*7c478bd9Sstevel@tonic-gate unknown++; 351*7c478bd9Sstevel@tonic-gate } else if (derr->fme_flag == DDI_FM_ERR_UNEXPECTED) { 352*7c478bd9Sstevel@tonic-gate /* 353*7c478bd9Sstevel@tonic-gate * Only sserr on primary bus is considered fatal. 354*7c478bd9Sstevel@tonic-gate * In all other conditions, the bridge has been able to notify 355*7c478bd9Sstevel@tonic-gate * the initiator of the error condition, so let the initiator 356*7c478bd9Sstevel@tonic-gate * (be it the host for PIO or the leaf device for DMA) handle it 357*7c478bd9Sstevel@tonic-gate */ 358*7c478bd9Sstevel@tonic-gate if (pci_cfg_stat & PCI_STAT_S_SYSERR) 359*7c478bd9Sstevel@tonic-gate fatal++; 360*7c478bd9Sstevel@tonic-gate if (pci_cfg_stat & (PCI_STAT_PERROR | 361*7c478bd9Sstevel@tonic-gate PCI_STAT_R_MAST_AB | PCI_STAT_S_PERROR | 362*7c478bd9Sstevel@tonic-gate PCI_STAT_R_TARG_AB | PCI_STAT_S_TARG_AB)) 363*7c478bd9Sstevel@tonic-gate nonfatal++; 364*7c478bd9Sstevel@tonic-gate if (pci_cfg_sec_stat & (PCI_STAT_R_TARG_AB | 365*7c478bd9Sstevel@tonic-gate PCI_STAT_S_SYSERR | PCI_STAT_R_MAST_AB | PCI_STAT_S_PERROR | 366*7c478bd9Sstevel@tonic-gate PCI_STAT_PERROR | PCI_STAT_S_TARG_AB)) 367*7c478bd9Sstevel@tonic-gate nonfatal++; 368*7c478bd9Sstevel@tonic-gate } 369*7c478bd9Sstevel@tonic-gate 370*7c478bd9Sstevel@tonic-gate /* 371*7c478bd9Sstevel@tonic-gate * now check children below the bridge 372*7c478bd9Sstevel@tonic-gate */ 373*7c478bd9Sstevel@tonic-gate ret = ndi_fm_handler_dispatch(dip, NULL, derr); 374*7c478bd9Sstevel@tonic-gate if (ret == DDI_FM_FATAL) 375*7c478bd9Sstevel@tonic-gate fatal++; 376*7c478bd9Sstevel@tonic-gate else if (ret == DDI_FM_NONFATAL) 377*7c478bd9Sstevel@tonic-gate nonfatal++; 378*7c478bd9Sstevel@tonic-gate else if (ret == DDI_FM_UNKNOWN) 379*7c478bd9Sstevel@tonic-gate unknown++; 380*7c478bd9Sstevel@tonic-gate 381*7c478bd9Sstevel@tonic-gate return (fatal ? DDI_FM_FATAL : (nonfatal ? DDI_FM_NONFATAL : 382*7c478bd9Sstevel@tonic-gate (unknown ? DDI_FM_UNKNOWN : DDI_FM_OK))); 383*7c478bd9Sstevel@tonic-gate } 384*7c478bd9Sstevel@tonic-gate 385*7c478bd9Sstevel@tonic-gate #ifdef _LP64 386*7c478bd9Sstevel@tonic-gate uint8_t 387*7c478bd9Sstevel@tonic-gate pci_config_get8(ddi_acc_handle_t handle, off_t offset) 388*7c478bd9Sstevel@tonic-gate #else /* _ILP32 */ 389*7c478bd9Sstevel@tonic-gate uint8_t 390*7c478bd9Sstevel@tonic-gate pci_config_getb(ddi_acc_handle_t handle, off_t offset) 391*7c478bd9Sstevel@tonic-gate #endif 392*7c478bd9Sstevel@tonic-gate { 393*7c478bd9Sstevel@tonic-gate caddr_t cfgaddr; 394*7c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *hp; 395*7c478bd9Sstevel@tonic-gate 396*7c478bd9Sstevel@tonic-gate hp = impl_acc_hdl_get(handle); 397*7c478bd9Sstevel@tonic-gate cfgaddr = hp->ah_addr + offset; 398*7c478bd9Sstevel@tonic-gate return (ddi_get8(handle, (uint8_t *)cfgaddr)); 399*7c478bd9Sstevel@tonic-gate } 400*7c478bd9Sstevel@tonic-gate 401*7c478bd9Sstevel@tonic-gate #ifdef _LP64 402*7c478bd9Sstevel@tonic-gate uint16_t 403*7c478bd9Sstevel@tonic-gate pci_config_get16(ddi_acc_handle_t handle, off_t offset) 404*7c478bd9Sstevel@tonic-gate #else /* _ILP32 */ 405*7c478bd9Sstevel@tonic-gate uint16_t 406*7c478bd9Sstevel@tonic-gate pci_config_getw(ddi_acc_handle_t handle, off_t offset) 407*7c478bd9Sstevel@tonic-gate #endif 408*7c478bd9Sstevel@tonic-gate { 409*7c478bd9Sstevel@tonic-gate caddr_t cfgaddr; 410*7c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *hp; 411*7c478bd9Sstevel@tonic-gate 412*7c478bd9Sstevel@tonic-gate hp = impl_acc_hdl_get(handle); 413*7c478bd9Sstevel@tonic-gate cfgaddr = hp->ah_addr + offset; 414*7c478bd9Sstevel@tonic-gate return (ddi_get16(handle, (uint16_t *)cfgaddr)); 415*7c478bd9Sstevel@tonic-gate } 416*7c478bd9Sstevel@tonic-gate 417*7c478bd9Sstevel@tonic-gate #ifdef _LP64 418*7c478bd9Sstevel@tonic-gate uint32_t 419*7c478bd9Sstevel@tonic-gate pci_config_get32(ddi_acc_handle_t handle, off_t offset) 420*7c478bd9Sstevel@tonic-gate #else /* _ILP32 */ 421*7c478bd9Sstevel@tonic-gate uint32_t 422*7c478bd9Sstevel@tonic-gate pci_config_getl(ddi_acc_handle_t handle, off_t offset) 423*7c478bd9Sstevel@tonic-gate #endif 424*7c478bd9Sstevel@tonic-gate { 425*7c478bd9Sstevel@tonic-gate caddr_t cfgaddr; 426*7c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *hp; 427*7c478bd9Sstevel@tonic-gate 428*7c478bd9Sstevel@tonic-gate hp = impl_acc_hdl_get(handle); 429*7c478bd9Sstevel@tonic-gate cfgaddr = hp->ah_addr + offset; 430*7c478bd9Sstevel@tonic-gate return (ddi_get32(handle, (uint32_t *)cfgaddr)); 431*7c478bd9Sstevel@tonic-gate } 432*7c478bd9Sstevel@tonic-gate 433*7c478bd9Sstevel@tonic-gate #ifdef _LP64 434*7c478bd9Sstevel@tonic-gate uint64_t 435*7c478bd9Sstevel@tonic-gate pci_config_get64(ddi_acc_handle_t handle, off_t offset) 436*7c478bd9Sstevel@tonic-gate #else /* _ILP32 */ 437*7c478bd9Sstevel@tonic-gate uint64_t 438*7c478bd9Sstevel@tonic-gate pci_config_getll(ddi_acc_handle_t handle, off_t offset) 439*7c478bd9Sstevel@tonic-gate #endif 440*7c478bd9Sstevel@tonic-gate { 441*7c478bd9Sstevel@tonic-gate caddr_t cfgaddr; 442*7c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *hp; 443*7c478bd9Sstevel@tonic-gate 444*7c478bd9Sstevel@tonic-gate hp = impl_acc_hdl_get(handle); 445*7c478bd9Sstevel@tonic-gate cfgaddr = hp->ah_addr + offset; 446*7c478bd9Sstevel@tonic-gate return (ddi_get64(handle, (uint64_t *)cfgaddr)); 447*7c478bd9Sstevel@tonic-gate } 448*7c478bd9Sstevel@tonic-gate 449*7c478bd9Sstevel@tonic-gate #ifdef _LP64 450*7c478bd9Sstevel@tonic-gate void 451*7c478bd9Sstevel@tonic-gate pci_config_put8(ddi_acc_handle_t handle, off_t offset, uint8_t value) 452*7c478bd9Sstevel@tonic-gate #else /* _ILP32 */ 453*7c478bd9Sstevel@tonic-gate void 454*7c478bd9Sstevel@tonic-gate pci_config_putb(ddi_acc_handle_t handle, off_t offset, uint8_t value) 455*7c478bd9Sstevel@tonic-gate #endif 456*7c478bd9Sstevel@tonic-gate { 457*7c478bd9Sstevel@tonic-gate caddr_t cfgaddr; 458*7c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *hp; 459*7c478bd9Sstevel@tonic-gate 460*7c478bd9Sstevel@tonic-gate hp = impl_acc_hdl_get(handle); 461*7c478bd9Sstevel@tonic-gate cfgaddr = hp->ah_addr + offset; 462*7c478bd9Sstevel@tonic-gate ddi_put8(handle, (uint8_t *)cfgaddr, value); 463*7c478bd9Sstevel@tonic-gate } 464*7c478bd9Sstevel@tonic-gate 465*7c478bd9Sstevel@tonic-gate #ifdef _LP64 466*7c478bd9Sstevel@tonic-gate void 467*7c478bd9Sstevel@tonic-gate pci_config_put16(ddi_acc_handle_t handle, off_t offset, uint16_t value) 468*7c478bd9Sstevel@tonic-gate #else /* _ILP32 */ 469*7c478bd9Sstevel@tonic-gate void 470*7c478bd9Sstevel@tonic-gate pci_config_putw(ddi_acc_handle_t handle, off_t offset, uint16_t value) 471*7c478bd9Sstevel@tonic-gate #endif 472*7c478bd9Sstevel@tonic-gate { 473*7c478bd9Sstevel@tonic-gate caddr_t cfgaddr; 474*7c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *hp; 475*7c478bd9Sstevel@tonic-gate 476*7c478bd9Sstevel@tonic-gate hp = impl_acc_hdl_get(handle); 477*7c478bd9Sstevel@tonic-gate cfgaddr = hp->ah_addr + offset; 478*7c478bd9Sstevel@tonic-gate ddi_put16(handle, (uint16_t *)cfgaddr, value); 479*7c478bd9Sstevel@tonic-gate } 480*7c478bd9Sstevel@tonic-gate 481*7c478bd9Sstevel@tonic-gate #ifdef _LP64 482*7c478bd9Sstevel@tonic-gate void 483*7c478bd9Sstevel@tonic-gate pci_config_put32(ddi_acc_handle_t handle, off_t offset, uint32_t value) 484*7c478bd9Sstevel@tonic-gate #else /* _ILP32 */ 485*7c478bd9Sstevel@tonic-gate void 486*7c478bd9Sstevel@tonic-gate pci_config_putl(ddi_acc_handle_t handle, off_t offset, uint32_t value) 487*7c478bd9Sstevel@tonic-gate #endif 488*7c478bd9Sstevel@tonic-gate { 489*7c478bd9Sstevel@tonic-gate caddr_t cfgaddr; 490*7c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *hp; 491*7c478bd9Sstevel@tonic-gate 492*7c478bd9Sstevel@tonic-gate hp = impl_acc_hdl_get(handle); 493*7c478bd9Sstevel@tonic-gate cfgaddr = hp->ah_addr + offset; 494*7c478bd9Sstevel@tonic-gate ddi_put32(handle, (uint32_t *)cfgaddr, value); 495*7c478bd9Sstevel@tonic-gate } 496*7c478bd9Sstevel@tonic-gate 497*7c478bd9Sstevel@tonic-gate #ifdef _LP64 498*7c478bd9Sstevel@tonic-gate void 499*7c478bd9Sstevel@tonic-gate pci_config_put64(ddi_acc_handle_t handle, off_t offset, uint64_t value) 500*7c478bd9Sstevel@tonic-gate #else /* _ILP32 */ 501*7c478bd9Sstevel@tonic-gate void 502*7c478bd9Sstevel@tonic-gate pci_config_putll(ddi_acc_handle_t handle, off_t offset, uint64_t value) 503*7c478bd9Sstevel@tonic-gate #endif 504*7c478bd9Sstevel@tonic-gate { 505*7c478bd9Sstevel@tonic-gate caddr_t cfgaddr; 506*7c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *hp; 507*7c478bd9Sstevel@tonic-gate 508*7c478bd9Sstevel@tonic-gate hp = impl_acc_hdl_get(handle); 509*7c478bd9Sstevel@tonic-gate cfgaddr = hp->ah_addr + offset; 510*7c478bd9Sstevel@tonic-gate ddi_put64(handle, (uint64_t *)cfgaddr, value); 511*7c478bd9Sstevel@tonic-gate } 512*7c478bd9Sstevel@tonic-gate 513*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 514*7c478bd9Sstevel@tonic-gate int 515*7c478bd9Sstevel@tonic-gate pci_report_pmcap(dev_info_t *dip, int cap, void *arg) 516*7c478bd9Sstevel@tonic-gate { 517*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 518*7c478bd9Sstevel@tonic-gate } 519*7c478bd9Sstevel@tonic-gate 520*7c478bd9Sstevel@tonic-gate /* 521*7c478bd9Sstevel@tonic-gate * Note about saving and restoring config space. 522*7c478bd9Sstevel@tonic-gate * PCI devices have only upto 256 bytes of config space while PCI Express 523*7c478bd9Sstevel@tonic-gate * devices can have upto 4k config space. In case of PCI Express device, 524*7c478bd9Sstevel@tonic-gate * we save all 4k config space and restore it even if it doesn't make use 525*7c478bd9Sstevel@tonic-gate * of all 4k. But some devices don't respond to reads to non-existent 526*7c478bd9Sstevel@tonic-gate * registers within the config space. To avoid any panics, we use ddi_peek 527*7c478bd9Sstevel@tonic-gate * to do the reads. A bit mask is used to indicate which words of the 528*7c478bd9Sstevel@tonic-gate * config space are accessible. While restoring the config space, only those 529*7c478bd9Sstevel@tonic-gate * readable words are restored. We do all this in 32 bit size words. 530*7c478bd9Sstevel@tonic-gate */ 531*7c478bd9Sstevel@tonic-gate #define INDEX_SHIFT 3 532*7c478bd9Sstevel@tonic-gate #define BITMASK 0x7 533*7c478bd9Sstevel@tonic-gate 534*7c478bd9Sstevel@tonic-gate static uint32_t pci_save_caps(ddi_acc_handle_t confhdl, uint32_t *regbuf, 535*7c478bd9Sstevel@tonic-gate pci_cap_save_desc_t *cap_descp, uint32_t *ncapsp); 536*7c478bd9Sstevel@tonic-gate static void pci_restore_caps(ddi_acc_handle_t confhdl, uint32_t *regbuf, 537*7c478bd9Sstevel@tonic-gate pci_cap_save_desc_t *cap_descp, uint32_t elements); 538*7c478bd9Sstevel@tonic-gate static uint32_t pci_generic_save(ddi_acc_handle_t confhdl, uint16_t cap_ptr, 539*7c478bd9Sstevel@tonic-gate uint32_t *regbuf, uint32_t nwords); 540*7c478bd9Sstevel@tonic-gate static uint32_t pci_msi_save(ddi_acc_handle_t confhdl, uint16_t cap_ptr, 541*7c478bd9Sstevel@tonic-gate uint32_t *regbuf, uint32_t notused); 542*7c478bd9Sstevel@tonic-gate static uint32_t pci_pcix_save(ddi_acc_handle_t confhdl, uint16_t cap_ptr, 543*7c478bd9Sstevel@tonic-gate uint32_t *regbuf, uint32_t notused); 544*7c478bd9Sstevel@tonic-gate static uint32_t pci_pcie_save(ddi_acc_handle_t confhdl, uint16_t cap_ptr, 545*7c478bd9Sstevel@tonic-gate uint32_t *regbuf, uint32_t notused); 546*7c478bd9Sstevel@tonic-gate static void pci_fill_buf(ddi_acc_handle_t confhdl, uint16_t cap_ptr, 547*7c478bd9Sstevel@tonic-gate uint32_t *regbuf, uint32_t nwords); 548*7c478bd9Sstevel@tonic-gate static uint32_t cap_walk_and_save(ddi_acc_handle_t confhdl, uint32_t *regbuf, 549*7c478bd9Sstevel@tonic-gate pci_cap_save_desc_t *cap_descp, uint32_t *ncapsp, int xspace); 550*7c478bd9Sstevel@tonic-gate static void pci_pmcap_check(ddi_acc_handle_t confhdl, uint32_t *regbuf, 551*7c478bd9Sstevel@tonic-gate uint16_t pmcap_offset); 552*7c478bd9Sstevel@tonic-gate 553*7c478bd9Sstevel@tonic-gate /* 554*7c478bd9Sstevel@tonic-gate * Table below specifies the number of registers to be saved for each PCI 555*7c478bd9Sstevel@tonic-gate * capability. pci_generic_save saves the number of words specified in the 556*7c478bd9Sstevel@tonic-gate * table. Any special considerations will be taken care by the capability 557*7c478bd9Sstevel@tonic-gate * specific save function e.g. use pci_msi_save to save registers associated 558*7c478bd9Sstevel@tonic-gate * with MSI capability. PCI_UNKNOWN_SIZE indicates that number of registers 559*7c478bd9Sstevel@tonic-gate * to be saved is variable and will be determined by the specific save function. 560*7c478bd9Sstevel@tonic-gate * Currently we save/restore all the registers associated with the capability 561*7c478bd9Sstevel@tonic-gate * including read only registers. Regsiters are saved and restored in 32 bit 562*7c478bd9Sstevel@tonic-gate * size words. 563*7c478bd9Sstevel@tonic-gate */ 564*7c478bd9Sstevel@tonic-gate static pci_cap_entry_t pci_cap_table[] = { 565*7c478bd9Sstevel@tonic-gate {PCI_CAP_ID_PM, PCI_PMCAP_NDWORDS, pci_generic_save}, 566*7c478bd9Sstevel@tonic-gate {PCI_CAP_ID_AGP, PCI_AGP_NDWORDS, pci_generic_save}, 567*7c478bd9Sstevel@tonic-gate {PCI_CAP_ID_SLOT_ID, PCI_SLOTID_NDWORDS, pci_generic_save}, 568*7c478bd9Sstevel@tonic-gate {PCI_CAP_ID_MSI_X, PCI_MSIX_NDWORDS, pci_generic_save}, 569*7c478bd9Sstevel@tonic-gate {PCI_CAP_ID_MSI, PCI_CAP_SZUNKNOWN, pci_msi_save}, 570*7c478bd9Sstevel@tonic-gate {PCI_CAP_ID_PCIX, PCI_CAP_SZUNKNOWN, pci_pcix_save}, 571*7c478bd9Sstevel@tonic-gate {PCI_CAP_ID_PCI_E, PCI_CAP_SZUNKNOWN, pci_pcie_save}, 572*7c478bd9Sstevel@tonic-gate /* 573*7c478bd9Sstevel@tonic-gate * {PCI_CAP_ID_cPCI_CRC, 0, NULL}, 574*7c478bd9Sstevel@tonic-gate * {PCI_CAP_ID_VPD, 0, NULL}, 575*7c478bd9Sstevel@tonic-gate * {PCI_CAP_ID_cPCI_HS, 0, NULL}, 576*7c478bd9Sstevel@tonic-gate * {PCI_CAP_ID_PCI_HOTPLUG, 0, NULL}, 577*7c478bd9Sstevel@tonic-gate * {PCI_CAP_ID_AGP_8X, 0, NULL}, 578*7c478bd9Sstevel@tonic-gate * {PCI_CAP_ID_SECURE_DEV, 0, NULL}, 579*7c478bd9Sstevel@tonic-gate */ 580*7c478bd9Sstevel@tonic-gate {PCI_CAP_NEXT_PTR_NULL, 0, NULL} 581*7c478bd9Sstevel@tonic-gate }; 582*7c478bd9Sstevel@tonic-gate 583*7c478bd9Sstevel@tonic-gate /* 584*7c478bd9Sstevel@tonic-gate * Save the configuration registers for cdip as a property 585*7c478bd9Sstevel@tonic-gate * so that it persists after detach/uninitchild. 586*7c478bd9Sstevel@tonic-gate */ 587*7c478bd9Sstevel@tonic-gate int 588*7c478bd9Sstevel@tonic-gate pci_save_config_regs(dev_info_t *dip) 589*7c478bd9Sstevel@tonic-gate { 590*7c478bd9Sstevel@tonic-gate ddi_acc_handle_t confhdl; 591*7c478bd9Sstevel@tonic-gate pci_config_header_state_t *chsp; 592*7c478bd9Sstevel@tonic-gate pci_cap_save_desc_t *pci_cap_descp; 593*7c478bd9Sstevel@tonic-gate int ret; 594*7c478bd9Sstevel@tonic-gate uint32_t i, ncaps, nwords; 595*7c478bd9Sstevel@tonic-gate uint32_t *regbuf, *p; 596*7c478bd9Sstevel@tonic-gate uint8_t *maskbuf; 597*7c478bd9Sstevel@tonic-gate size_t maskbufsz, regbufsz, capbufsz; 598*7c478bd9Sstevel@tonic-gate ddi_acc_hdl_t *hp; 599*7c478bd9Sstevel@tonic-gate off_t offset = 0; 600*7c478bd9Sstevel@tonic-gate uint8_t cap_ptr, cap_id; 601*7c478bd9Sstevel@tonic-gate int pcie = 0; 602*7c478bd9Sstevel@tonic-gate 603*7c478bd9Sstevel@tonic-gate if (pci_config_setup(dip, &confhdl) != DDI_SUCCESS) { 604*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d can't get config handle", 605*7c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip)); 606*7c478bd9Sstevel@tonic-gate 607*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 608*7c478bd9Sstevel@tonic-gate } 609*7c478bd9Sstevel@tonic-gate /* 610*7c478bd9Sstevel@tonic-gate * Determine if it is a pci express device. If it is, save entire 611*7c478bd9Sstevel@tonic-gate * 4k config space treating it as a array of 32 bit integers. 612*7c478bd9Sstevel@tonic-gate * If it is not, do it in a usual PCI way. 613*7c478bd9Sstevel@tonic-gate */ 614*7c478bd9Sstevel@tonic-gate cap_ptr = pci_config_get8(confhdl, PCI_BCNF_CAP_PTR); 615*7c478bd9Sstevel@tonic-gate /* 616*7c478bd9Sstevel@tonic-gate * Walk the capabilities searching for pci express capability 617*7c478bd9Sstevel@tonic-gate */ 618*7c478bd9Sstevel@tonic-gate while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) { 619*7c478bd9Sstevel@tonic-gate cap_id = pci_config_get8(confhdl, 620*7c478bd9Sstevel@tonic-gate cap_ptr + PCI_CAP_ID); 621*7c478bd9Sstevel@tonic-gate if (cap_id == PCI_CAP_ID_PCI_E) { 622*7c478bd9Sstevel@tonic-gate pcie = 1; 623*7c478bd9Sstevel@tonic-gate break; 624*7c478bd9Sstevel@tonic-gate } 625*7c478bd9Sstevel@tonic-gate cap_ptr = pci_config_get8(confhdl, 626*7c478bd9Sstevel@tonic-gate cap_ptr + PCI_CAP_NEXT_PTR); 627*7c478bd9Sstevel@tonic-gate } 628*7c478bd9Sstevel@tonic-gate 629*7c478bd9Sstevel@tonic-gate if (pcie) { 630*7c478bd9Sstevel@tonic-gate /* PCI express device. Can have data in all 4k space */ 631*7c478bd9Sstevel@tonic-gate regbuf = (uint32_t *)kmem_zalloc((size_t)PCIE_CONF_HDR_SIZE, 632*7c478bd9Sstevel@tonic-gate KM_SLEEP); 633*7c478bd9Sstevel@tonic-gate p = regbuf; 634*7c478bd9Sstevel@tonic-gate /* 635*7c478bd9Sstevel@tonic-gate * Allocate space for mask. 636*7c478bd9Sstevel@tonic-gate * mask size is 128 bytes (4096 / 4 / 8 ) 637*7c478bd9Sstevel@tonic-gate */ 638*7c478bd9Sstevel@tonic-gate maskbufsz = (size_t)((PCIE_CONF_HDR_SIZE/ sizeof (uint32_t)) >> 639*7c478bd9Sstevel@tonic-gate INDEX_SHIFT); 640*7c478bd9Sstevel@tonic-gate maskbuf = (uint8_t *)kmem_zalloc(maskbufsz, KM_SLEEP); 641*7c478bd9Sstevel@tonic-gate hp = impl_acc_hdl_get(confhdl); 642*7c478bd9Sstevel@tonic-gate for (i = 0; i < (PCIE_CONF_HDR_SIZE / sizeof (uint32_t)); i++) { 643*7c478bd9Sstevel@tonic-gate if (ddi_peek32(dip, (int32_t *)(hp->ah_addr + offset), 644*7c478bd9Sstevel@tonic-gate (int32_t *)p) == DDI_SUCCESS) { 645*7c478bd9Sstevel@tonic-gate /* it is readable register. set the bit */ 646*7c478bd9Sstevel@tonic-gate maskbuf[i >> INDEX_SHIFT] |= 647*7c478bd9Sstevel@tonic-gate (uint8_t)(1 << (i & BITMASK)); 648*7c478bd9Sstevel@tonic-gate } 649*7c478bd9Sstevel@tonic-gate p++; 650*7c478bd9Sstevel@tonic-gate offset += sizeof (uint32_t); 651*7c478bd9Sstevel@tonic-gate } 652*7c478bd9Sstevel@tonic-gate 653*7c478bd9Sstevel@tonic-gate if ((ret = ndi_prop_update_byte_array(DDI_DEV_T_NONE, dip, 654*7c478bd9Sstevel@tonic-gate SAVED_CONFIG_REGS_MASK, (uchar_t *)maskbuf, 655*7c478bd9Sstevel@tonic-gate maskbufsz)) != DDI_PROP_SUCCESS) { 656*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "couldn't create %s property while" 657*7c478bd9Sstevel@tonic-gate "saving config space for %s@%d\n", 658*7c478bd9Sstevel@tonic-gate SAVED_CONFIG_REGS_MASK, ddi_driver_name(dip), 659*7c478bd9Sstevel@tonic-gate ddi_get_instance(dip)); 660*7c478bd9Sstevel@tonic-gate } else if ((ret = ndi_prop_update_byte_array(DDI_DEV_T_NONE, 661*7c478bd9Sstevel@tonic-gate dip, SAVED_CONFIG_REGS, (uchar_t *)regbuf, 662*7c478bd9Sstevel@tonic-gate (size_t)PCIE_CONF_HDR_SIZE)) != DDI_PROP_SUCCESS) { 663*7c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(DDI_DEV_T_NONE, dip, 664*7c478bd9Sstevel@tonic-gate SAVED_CONFIG_REGS_MASK); 665*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d can't update prop %s", 666*7c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 667*7c478bd9Sstevel@tonic-gate SAVED_CONFIG_REGS); 668*7c478bd9Sstevel@tonic-gate } 669*7c478bd9Sstevel@tonic-gate 670*7c478bd9Sstevel@tonic-gate kmem_free(maskbuf, (size_t)maskbufsz); 671*7c478bd9Sstevel@tonic-gate kmem_free(regbuf, (size_t)PCIE_CONF_HDR_SIZE); 672*7c478bd9Sstevel@tonic-gate } else { 673*7c478bd9Sstevel@tonic-gate regbuf = (uint32_t *)kmem_zalloc((size_t)PCI_CONF_HDR_SIZE, 674*7c478bd9Sstevel@tonic-gate KM_SLEEP); 675*7c478bd9Sstevel@tonic-gate chsp = (pci_config_header_state_t *)regbuf; 676*7c478bd9Sstevel@tonic-gate 677*7c478bd9Sstevel@tonic-gate chsp->chs_command = pci_config_get16(confhdl, PCI_CONF_COMM); 678*7c478bd9Sstevel@tonic-gate chsp->chs_header_type = pci_config_get8(confhdl, 679*7c478bd9Sstevel@tonic-gate PCI_CONF_HEADER); 680*7c478bd9Sstevel@tonic-gate if ((chsp->chs_header_type & PCI_HEADER_TYPE_M) == 681*7c478bd9Sstevel@tonic-gate PCI_HEADER_ONE) 682*7c478bd9Sstevel@tonic-gate chsp->chs_bridge_control = 683*7c478bd9Sstevel@tonic-gate pci_config_get16(confhdl, PCI_BCNF_BCNTRL); 684*7c478bd9Sstevel@tonic-gate chsp->chs_cache_line_size = pci_config_get8(confhdl, 685*7c478bd9Sstevel@tonic-gate PCI_CONF_CACHE_LINESZ); 686*7c478bd9Sstevel@tonic-gate chsp->chs_latency_timer = pci_config_get8(confhdl, 687*7c478bd9Sstevel@tonic-gate PCI_CONF_LATENCY_TIMER); 688*7c478bd9Sstevel@tonic-gate if ((chsp->chs_header_type & PCI_HEADER_TYPE_M) == 689*7c478bd9Sstevel@tonic-gate PCI_HEADER_ONE) { 690*7c478bd9Sstevel@tonic-gate chsp->chs_sec_latency_timer = 691*7c478bd9Sstevel@tonic-gate pci_config_get8(confhdl, PCI_BCNF_LATENCY_TIMER); 692*7c478bd9Sstevel@tonic-gate } 693*7c478bd9Sstevel@tonic-gate 694*7c478bd9Sstevel@tonic-gate chsp->chs_base0 = pci_config_get32(confhdl, PCI_CONF_BASE0); 695*7c478bd9Sstevel@tonic-gate chsp->chs_base1 = pci_config_get32(confhdl, PCI_CONF_BASE1); 696*7c478bd9Sstevel@tonic-gate chsp->chs_base2 = pci_config_get32(confhdl, PCI_CONF_BASE2); 697*7c478bd9Sstevel@tonic-gate chsp->chs_base3 = pci_config_get32(confhdl, PCI_CONF_BASE3); 698*7c478bd9Sstevel@tonic-gate chsp->chs_base4 = pci_config_get32(confhdl, PCI_CONF_BASE4); 699*7c478bd9Sstevel@tonic-gate chsp->chs_base5 = pci_config_get32(confhdl, PCI_CONF_BASE5); 700*7c478bd9Sstevel@tonic-gate 701*7c478bd9Sstevel@tonic-gate /* 702*7c478bd9Sstevel@tonic-gate * Allocate maximum space required for capability descriptions. 703*7c478bd9Sstevel@tonic-gate * The maximum number of capabilties saved is the number of 704*7c478bd9Sstevel@tonic-gate * capabilities listed in the pci_cap_table. 705*7c478bd9Sstevel@tonic-gate */ 706*7c478bd9Sstevel@tonic-gate ncaps = (sizeof (pci_cap_table) / sizeof (pci_cap_entry_t)); 707*7c478bd9Sstevel@tonic-gate capbufsz = ncaps * sizeof (pci_cap_save_desc_t); 708*7c478bd9Sstevel@tonic-gate pci_cap_descp = (pci_cap_save_desc_t *)kmem_zalloc( 709*7c478bd9Sstevel@tonic-gate capbufsz, KM_SLEEP); 710*7c478bd9Sstevel@tonic-gate p = (uint32_t *)((caddr_t)regbuf + 711*7c478bd9Sstevel@tonic-gate sizeof (pci_config_header_state_t)); 712*7c478bd9Sstevel@tonic-gate nwords = pci_save_caps(confhdl, p, pci_cap_descp, &ncaps); 713*7c478bd9Sstevel@tonic-gate regbufsz = sizeof (pci_config_header_state_t) + 714*7c478bd9Sstevel@tonic-gate nwords * sizeof (uint32_t); 715*7c478bd9Sstevel@tonic-gate 716*7c478bd9Sstevel@tonic-gate if ((ret = ndi_prop_update_byte_array(DDI_DEV_T_NONE, dip, 717*7c478bd9Sstevel@tonic-gate SAVED_CONFIG_REGS, (uchar_t *)regbuf, regbufsz)) != 718*7c478bd9Sstevel@tonic-gate DDI_PROP_SUCCESS) { 719*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d can't update prop %s", 720*7c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 721*7c478bd9Sstevel@tonic-gate SAVED_CONFIG_REGS); 722*7c478bd9Sstevel@tonic-gate } else if (ncaps) { 723*7c478bd9Sstevel@tonic-gate ret = ndi_prop_update_byte_array(DDI_DEV_T_NONE, dip, 724*7c478bd9Sstevel@tonic-gate SAVED_CONFIG_REGS_CAPINFO, (uchar_t *)pci_cap_descp, 725*7c478bd9Sstevel@tonic-gate ncaps * sizeof (pci_cap_save_desc_t)); 726*7c478bd9Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) 727*7c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(DDI_DEV_T_NONE, dip, 728*7c478bd9Sstevel@tonic-gate SAVED_CONFIG_REGS); 729*7c478bd9Sstevel@tonic-gate } 730*7c478bd9Sstevel@tonic-gate kmem_free(regbuf, (size_t)PCI_CONF_HDR_SIZE); 731*7c478bd9Sstevel@tonic-gate kmem_free(pci_cap_descp, capbufsz); 732*7c478bd9Sstevel@tonic-gate } 733*7c478bd9Sstevel@tonic-gate pci_config_teardown(&confhdl); 734*7c478bd9Sstevel@tonic-gate 735*7c478bd9Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) 736*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 737*7c478bd9Sstevel@tonic-gate 738*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 739*7c478bd9Sstevel@tonic-gate } 740*7c478bd9Sstevel@tonic-gate 741*7c478bd9Sstevel@tonic-gate /* 742*7c478bd9Sstevel@tonic-gate * Saves registers associated with PCI capabilities. 743*7c478bd9Sstevel@tonic-gate * Returns number of 32 bit words saved. 744*7c478bd9Sstevel@tonic-gate * Number of capabilities saved is returned in ncapsp. 745*7c478bd9Sstevel@tonic-gate */ 746*7c478bd9Sstevel@tonic-gate static uint32_t 747*7c478bd9Sstevel@tonic-gate pci_save_caps(ddi_acc_handle_t confhdl, uint32_t *regbuf, 748*7c478bd9Sstevel@tonic-gate pci_cap_save_desc_t *cap_descp, uint32_t *ncapsp) 749*7c478bd9Sstevel@tonic-gate { 750*7c478bd9Sstevel@tonic-gate return (cap_walk_and_save(confhdl, regbuf, cap_descp, ncapsp, 0)); 751*7c478bd9Sstevel@tonic-gate } 752*7c478bd9Sstevel@tonic-gate 753*7c478bd9Sstevel@tonic-gate static uint32_t 754*7c478bd9Sstevel@tonic-gate cap_walk_and_save(ddi_acc_handle_t confhdl, uint32_t *regbuf, 755*7c478bd9Sstevel@tonic-gate pci_cap_save_desc_t *cap_descp, uint32_t *ncapsp, int xspace) 756*7c478bd9Sstevel@tonic-gate { 757*7c478bd9Sstevel@tonic-gate pci_cap_entry_t *pci_cap_entp; 758*7c478bd9Sstevel@tonic-gate uint16_t cap_id, offset; 759*7c478bd9Sstevel@tonic-gate uint32_t words_saved = 0, nwords = 0; 760*7c478bd9Sstevel@tonic-gate uint16_t cap_ptr = PCI_CAP_NEXT_PTR_NULL; 761*7c478bd9Sstevel@tonic-gate 762*7c478bd9Sstevel@tonic-gate *ncapsp = 0; 763*7c478bd9Sstevel@tonic-gate if (!xspace) 764*7c478bd9Sstevel@tonic-gate cap_ptr = pci_config_get8(confhdl, PCI_BCNF_CAP_PTR); 765*7c478bd9Sstevel@tonic-gate /* 766*7c478bd9Sstevel@tonic-gate * Walk the capabilities 767*7c478bd9Sstevel@tonic-gate */ 768*7c478bd9Sstevel@tonic-gate while (cap_ptr != PCI_CAP_NEXT_PTR_NULL) { 769*7c478bd9Sstevel@tonic-gate cap_id = CAP_ID(confhdl, cap_ptr, xspace); 770*7c478bd9Sstevel@tonic-gate /* Search for this cap id in our table */ 771*7c478bd9Sstevel@tonic-gate if (!xspace) 772*7c478bd9Sstevel@tonic-gate pci_cap_entp = pci_cap_table; 773*7c478bd9Sstevel@tonic-gate while (pci_cap_entp->cap_id != PCI_CAP_NEXT_PTR_NULL && 774*7c478bd9Sstevel@tonic-gate pci_cap_entp->cap_id != cap_id) 775*7c478bd9Sstevel@tonic-gate pci_cap_entp++; 776*7c478bd9Sstevel@tonic-gate 777*7c478bd9Sstevel@tonic-gate offset = cap_ptr; 778*7c478bd9Sstevel@tonic-gate cap_ptr = NEXT_CAP(confhdl, cap_ptr, xspace); 779*7c478bd9Sstevel@tonic-gate /* 780*7c478bd9Sstevel@tonic-gate * If this cap id is not found in the table, there is nothing 781*7c478bd9Sstevel@tonic-gate * to save. 782*7c478bd9Sstevel@tonic-gate */ 783*7c478bd9Sstevel@tonic-gate if (pci_cap_entp->cap_id == PCI_CAP_NEXT_PTR_NULL) 784*7c478bd9Sstevel@tonic-gate continue; 785*7c478bd9Sstevel@tonic-gate if (pci_cap_entp->cap_save_func) { 786*7c478bd9Sstevel@tonic-gate if ((nwords = pci_cap_entp->cap_save_func(confhdl, 787*7c478bd9Sstevel@tonic-gate offset, regbuf, pci_cap_entp->cap_ndwords))) { 788*7c478bd9Sstevel@tonic-gate cap_descp->cap_nregs = nwords; 789*7c478bd9Sstevel@tonic-gate cap_descp->cap_offset = offset; 790*7c478bd9Sstevel@tonic-gate cap_descp->cap_id = cap_id; 791*7c478bd9Sstevel@tonic-gate regbuf += nwords; 792*7c478bd9Sstevel@tonic-gate cap_descp++; 793*7c478bd9Sstevel@tonic-gate words_saved += nwords; 794*7c478bd9Sstevel@tonic-gate (*ncapsp)++; 795*7c478bd9Sstevel@tonic-gate } 796*7c478bd9Sstevel@tonic-gate } 797*7c478bd9Sstevel@tonic-gate 798*7c478bd9Sstevel@tonic-gate } 799*7c478bd9Sstevel@tonic-gate return (words_saved); 800*7c478bd9Sstevel@tonic-gate } 801*7c478bd9Sstevel@tonic-gate 802*7c478bd9Sstevel@tonic-gate static void 803*7c478bd9Sstevel@tonic-gate pci_fill_buf(ddi_acc_handle_t confhdl, uint16_t cap_ptr, 804*7c478bd9Sstevel@tonic-gate uint32_t *regbuf, uint32_t nwords) 805*7c478bd9Sstevel@tonic-gate { 806*7c478bd9Sstevel@tonic-gate int i; 807*7c478bd9Sstevel@tonic-gate 808*7c478bd9Sstevel@tonic-gate for (i = 0; i < nwords; i++) { 809*7c478bd9Sstevel@tonic-gate *regbuf = pci_config_get32(confhdl, cap_ptr); 810*7c478bd9Sstevel@tonic-gate regbuf++; 811*7c478bd9Sstevel@tonic-gate cap_ptr += 4; 812*7c478bd9Sstevel@tonic-gate } 813*7c478bd9Sstevel@tonic-gate } 814*7c478bd9Sstevel@tonic-gate 815*7c478bd9Sstevel@tonic-gate static uint32_t 816*7c478bd9Sstevel@tonic-gate pci_generic_save(ddi_acc_handle_t confhdl, uint16_t cap_ptr, uint32_t *regbuf, 817*7c478bd9Sstevel@tonic-gate uint32_t nwords) 818*7c478bd9Sstevel@tonic-gate { 819*7c478bd9Sstevel@tonic-gate pci_fill_buf(confhdl, cap_ptr, regbuf, nwords); 820*7c478bd9Sstevel@tonic-gate return (nwords); 821*7c478bd9Sstevel@tonic-gate } 822*7c478bd9Sstevel@tonic-gate 823*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 824*7c478bd9Sstevel@tonic-gate static uint32_t 825*7c478bd9Sstevel@tonic-gate pci_msi_save(ddi_acc_handle_t confhdl, uint16_t cap_ptr, uint32_t *regbuf, 826*7c478bd9Sstevel@tonic-gate uint32_t notused) 827*7c478bd9Sstevel@tonic-gate { 828*7c478bd9Sstevel@tonic-gate uint32_t nwords = PCI_MSI_MIN_WORDS; 829*7c478bd9Sstevel@tonic-gate uint16_t msi_ctrl; 830*7c478bd9Sstevel@tonic-gate 831*7c478bd9Sstevel@tonic-gate /* Figure out how many registers to be saved */ 832*7c478bd9Sstevel@tonic-gate msi_ctrl = pci_config_get16(confhdl, cap_ptr + PCI_MSI_CTRL); 833*7c478bd9Sstevel@tonic-gate /* If 64 bit address capable add one word */ 834*7c478bd9Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_64BIT_MASK) 835*7c478bd9Sstevel@tonic-gate nwords++; 836*7c478bd9Sstevel@tonic-gate /* If per vector masking capable, add two more words */ 837*7c478bd9Sstevel@tonic-gate if (msi_ctrl & PCI_MSI_PVM_MASK) 838*7c478bd9Sstevel@tonic-gate nwords += 2; 839*7c478bd9Sstevel@tonic-gate pci_fill_buf(confhdl, cap_ptr, regbuf, nwords); 840*7c478bd9Sstevel@tonic-gate 841*7c478bd9Sstevel@tonic-gate return (nwords); 842*7c478bd9Sstevel@tonic-gate } 843*7c478bd9Sstevel@tonic-gate 844*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 845*7c478bd9Sstevel@tonic-gate static uint32_t 846*7c478bd9Sstevel@tonic-gate pci_pcix_save(ddi_acc_handle_t confhdl, uint16_t cap_ptr, uint32_t *regbuf, 847*7c478bd9Sstevel@tonic-gate uint32_t notused) 848*7c478bd9Sstevel@tonic-gate { 849*7c478bd9Sstevel@tonic-gate uint32_t nwords = PCI_PCIX_MIN_WORDS; 850*7c478bd9Sstevel@tonic-gate uint16_t pcix_command; 851*7c478bd9Sstevel@tonic-gate 852*7c478bd9Sstevel@tonic-gate /* Figure out how many registers to be saved */ 853*7c478bd9Sstevel@tonic-gate pcix_command = pci_config_get16(confhdl, cap_ptr + PCI_PCIX_COMMAND); 854*7c478bd9Sstevel@tonic-gate /* If it is version 1 or version 2, add 4 words */ 855*7c478bd9Sstevel@tonic-gate if (((pcix_command & PCI_PCIX_VER_MASK) == PCI_PCIX_VER_1) || 856*7c478bd9Sstevel@tonic-gate ((pcix_command & PCI_PCIX_VER_MASK) == PCI_PCIX_VER_2)) 857*7c478bd9Sstevel@tonic-gate nwords += 4; 858*7c478bd9Sstevel@tonic-gate pci_fill_buf(confhdl, cap_ptr, regbuf, nwords); 859*7c478bd9Sstevel@tonic-gate 860*7c478bd9Sstevel@tonic-gate return (nwords); 861*7c478bd9Sstevel@tonic-gate } 862*7c478bd9Sstevel@tonic-gate 863*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 864*7c478bd9Sstevel@tonic-gate static uint32_t 865*7c478bd9Sstevel@tonic-gate pci_pcie_save(ddi_acc_handle_t confhdl, uint16_t cap_ptr, uint32_t *regbuf, 866*7c478bd9Sstevel@tonic-gate uint32_t notused) 867*7c478bd9Sstevel@tonic-gate { 868*7c478bd9Sstevel@tonic-gate return (0); 869*7c478bd9Sstevel@tonic-gate } 870*7c478bd9Sstevel@tonic-gate 871*7c478bd9Sstevel@tonic-gate static void 872*7c478bd9Sstevel@tonic-gate pci_pmcap_check(ddi_acc_handle_t confhdl, uint32_t *regbuf, 873*7c478bd9Sstevel@tonic-gate uint16_t pmcap_offset) 874*7c478bd9Sstevel@tonic-gate { 875*7c478bd9Sstevel@tonic-gate uint16_t pmcsr; 876*7c478bd9Sstevel@tonic-gate uint16_t pmcsr_offset = pmcap_offset + PCI_PMCSR; 877*7c478bd9Sstevel@tonic-gate uint32_t *saved_pmcsrp = (uint32_t *)((caddr_t)regbuf + PCI_PMCSR); 878*7c478bd9Sstevel@tonic-gate 879*7c478bd9Sstevel@tonic-gate /* 880*7c478bd9Sstevel@tonic-gate * Copy the power state bits from the PMCSR to our saved copy. 881*7c478bd9Sstevel@tonic-gate * This is to make sure that we don't change the D state when 882*7c478bd9Sstevel@tonic-gate * we restore config space of the device. 883*7c478bd9Sstevel@tonic-gate */ 884*7c478bd9Sstevel@tonic-gate pmcsr = pci_config_get16(confhdl, pmcsr_offset); 885*7c478bd9Sstevel@tonic-gate (*saved_pmcsrp) &= ~PCI_PMCSR_STATE_MASK; 886*7c478bd9Sstevel@tonic-gate (*saved_pmcsrp) |= (pmcsr & PCI_PMCSR_STATE_MASK); 887*7c478bd9Sstevel@tonic-gate } 888*7c478bd9Sstevel@tonic-gate 889*7c478bd9Sstevel@tonic-gate static void 890*7c478bd9Sstevel@tonic-gate pci_restore_caps(ddi_acc_handle_t confhdl, uint32_t *regbuf, 891*7c478bd9Sstevel@tonic-gate pci_cap_save_desc_t *cap_descp, uint32_t elements) 892*7c478bd9Sstevel@tonic-gate { 893*7c478bd9Sstevel@tonic-gate int i, j; 894*7c478bd9Sstevel@tonic-gate uint16_t offset; 895*7c478bd9Sstevel@tonic-gate 896*7c478bd9Sstevel@tonic-gate for (i = 0; i < (elements / sizeof (pci_cap_save_desc_t)); i++) { 897*7c478bd9Sstevel@tonic-gate offset = cap_descp->cap_offset; 898*7c478bd9Sstevel@tonic-gate if (cap_descp->cap_id == PCI_CAP_ID_PM) 899*7c478bd9Sstevel@tonic-gate pci_pmcap_check(confhdl, regbuf, offset); 900*7c478bd9Sstevel@tonic-gate for (j = 0; j < cap_descp->cap_nregs; j++) { 901*7c478bd9Sstevel@tonic-gate pci_config_put32(confhdl, offset, *regbuf); 902*7c478bd9Sstevel@tonic-gate regbuf++; 903*7c478bd9Sstevel@tonic-gate offset += 4; 904*7c478bd9Sstevel@tonic-gate } 905*7c478bd9Sstevel@tonic-gate cap_descp++; 906*7c478bd9Sstevel@tonic-gate } 907*7c478bd9Sstevel@tonic-gate } 908*7c478bd9Sstevel@tonic-gate 909*7c478bd9Sstevel@tonic-gate /* 910*7c478bd9Sstevel@tonic-gate * Restore config_regs from a single devinfo node. 911*7c478bd9Sstevel@tonic-gate */ 912*7c478bd9Sstevel@tonic-gate int 913*7c478bd9Sstevel@tonic-gate pci_restore_config_regs(dev_info_t *dip) 914*7c478bd9Sstevel@tonic-gate { 915*7c478bd9Sstevel@tonic-gate ddi_acc_handle_t confhdl; 916*7c478bd9Sstevel@tonic-gate pci_config_header_state_t *chs_p; 917*7c478bd9Sstevel@tonic-gate pci_cap_save_desc_t *cap_descp; 918*7c478bd9Sstevel@tonic-gate uint32_t elements, i; 919*7c478bd9Sstevel@tonic-gate uint8_t *maskbuf; 920*7c478bd9Sstevel@tonic-gate uint32_t *regbuf, *p; 921*7c478bd9Sstevel@tonic-gate off_t offset = 0; 922*7c478bd9Sstevel@tonic-gate 923*7c478bd9Sstevel@tonic-gate if (pci_config_setup(dip, &confhdl) != DDI_SUCCESS) { 924*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d can't get config handle", 925*7c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip)); 926*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 927*7c478bd9Sstevel@tonic-gate } 928*7c478bd9Sstevel@tonic-gate 929*7c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip, 930*7c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, SAVED_CONFIG_REGS_MASK, 931*7c478bd9Sstevel@tonic-gate (uchar_t **)&maskbuf, &elements) == DDI_PROP_SUCCESS) { 932*7c478bd9Sstevel@tonic-gate 933*7c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip, 934*7c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, SAVED_CONFIG_REGS, 935*7c478bd9Sstevel@tonic-gate (uchar_t **)®buf, &elements) != DDI_PROP_SUCCESS) { 936*7c478bd9Sstevel@tonic-gate goto restoreconfig_err; 937*7c478bd9Sstevel@tonic-gate } 938*7c478bd9Sstevel@tonic-gate ASSERT(elements == PCIE_CONF_HDR_SIZE); 939*7c478bd9Sstevel@tonic-gate /* pcie device and has 4k config space saved */ 940*7c478bd9Sstevel@tonic-gate p = regbuf; 941*7c478bd9Sstevel@tonic-gate for (i = 0; i < PCIE_CONF_HDR_SIZE / sizeof (uint32_t); i++) { 942*7c478bd9Sstevel@tonic-gate /* If the word is readable then restore it */ 943*7c478bd9Sstevel@tonic-gate if (maskbuf[i >> INDEX_SHIFT] & 944*7c478bd9Sstevel@tonic-gate (uint8_t)(1 << (i & BITMASK))) 945*7c478bd9Sstevel@tonic-gate pci_config_put32(confhdl, offset, *p); 946*7c478bd9Sstevel@tonic-gate p++; 947*7c478bd9Sstevel@tonic-gate offset += sizeof (uint32_t); 948*7c478bd9Sstevel@tonic-gate } 949*7c478bd9Sstevel@tonic-gate ddi_prop_free(regbuf); 950*7c478bd9Sstevel@tonic-gate ddi_prop_free(maskbuf); 951*7c478bd9Sstevel@tonic-gate if (ndi_prop_remove(DDI_DEV_T_NONE, dip, 952*7c478bd9Sstevel@tonic-gate SAVED_CONFIG_REGS_MASK) != DDI_PROP_SUCCESS) { 953*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d can't remove prop %s", 954*7c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 955*7c478bd9Sstevel@tonic-gate SAVED_CONFIG_REGS_MASK); 956*7c478bd9Sstevel@tonic-gate } 957*7c478bd9Sstevel@tonic-gate } else { 958*7c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip, 959*7c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, SAVED_CONFIG_REGS, 960*7c478bd9Sstevel@tonic-gate (uchar_t **)®buf, &elements) != DDI_PROP_SUCCESS) { 961*7c478bd9Sstevel@tonic-gate 962*7c478bd9Sstevel@tonic-gate pci_config_teardown(&confhdl); 963*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 964*7c478bd9Sstevel@tonic-gate } 965*7c478bd9Sstevel@tonic-gate 966*7c478bd9Sstevel@tonic-gate chs_p = (pci_config_header_state_t *)regbuf; 967*7c478bd9Sstevel@tonic-gate pci_config_put16(confhdl, PCI_CONF_COMM, 968*7c478bd9Sstevel@tonic-gate chs_p->chs_command); 969*7c478bd9Sstevel@tonic-gate if ((chs_p->chs_header_type & PCI_HEADER_TYPE_M) == 970*7c478bd9Sstevel@tonic-gate PCI_HEADER_ONE) { 971*7c478bd9Sstevel@tonic-gate pci_config_put16(confhdl, PCI_BCNF_BCNTRL, 972*7c478bd9Sstevel@tonic-gate chs_p->chs_bridge_control); 973*7c478bd9Sstevel@tonic-gate } 974*7c478bd9Sstevel@tonic-gate pci_config_put8(confhdl, PCI_CONF_CACHE_LINESZ, 975*7c478bd9Sstevel@tonic-gate chs_p->chs_cache_line_size); 976*7c478bd9Sstevel@tonic-gate pci_config_put8(confhdl, PCI_CONF_LATENCY_TIMER, 977*7c478bd9Sstevel@tonic-gate chs_p->chs_latency_timer); 978*7c478bd9Sstevel@tonic-gate if ((chs_p->chs_header_type & PCI_HEADER_TYPE_M) == 979*7c478bd9Sstevel@tonic-gate PCI_HEADER_ONE) 980*7c478bd9Sstevel@tonic-gate pci_config_put8(confhdl, PCI_BCNF_LATENCY_TIMER, 981*7c478bd9Sstevel@tonic-gate chs_p->chs_sec_latency_timer); 982*7c478bd9Sstevel@tonic-gate 983*7c478bd9Sstevel@tonic-gate pci_config_put32(confhdl, PCI_CONF_BASE0, chs_p->chs_base0); 984*7c478bd9Sstevel@tonic-gate pci_config_put32(confhdl, PCI_CONF_BASE1, chs_p->chs_base1); 985*7c478bd9Sstevel@tonic-gate pci_config_put32(confhdl, PCI_CONF_BASE2, chs_p->chs_base2); 986*7c478bd9Sstevel@tonic-gate pci_config_put32(confhdl, PCI_CONF_BASE3, chs_p->chs_base3); 987*7c478bd9Sstevel@tonic-gate pci_config_put32(confhdl, PCI_CONF_BASE4, chs_p->chs_base4); 988*7c478bd9Sstevel@tonic-gate pci_config_put32(confhdl, PCI_CONF_BASE5, chs_p->chs_base5); 989*7c478bd9Sstevel@tonic-gate 990*7c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip, 991*7c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, 992*7c478bd9Sstevel@tonic-gate SAVED_CONFIG_REGS_CAPINFO, 993*7c478bd9Sstevel@tonic-gate (uchar_t **)&cap_descp, &elements) == DDI_PROP_SUCCESS) { 994*7c478bd9Sstevel@tonic-gate /* 995*7c478bd9Sstevel@tonic-gate * PCI capability related regsiters are saved. 996*7c478bd9Sstevel@tonic-gate * Restore them based on the description. 997*7c478bd9Sstevel@tonic-gate */ 998*7c478bd9Sstevel@tonic-gate p = (uint32_t *)((caddr_t)regbuf + 999*7c478bd9Sstevel@tonic-gate sizeof (pci_config_header_state_t)); 1000*7c478bd9Sstevel@tonic-gate pci_restore_caps(confhdl, p, cap_descp, elements); 1001*7c478bd9Sstevel@tonic-gate ddi_prop_free(cap_descp); 1002*7c478bd9Sstevel@tonic-gate } 1003*7c478bd9Sstevel@tonic-gate 1004*7c478bd9Sstevel@tonic-gate ddi_prop_free(regbuf); 1005*7c478bd9Sstevel@tonic-gate } 1006*7c478bd9Sstevel@tonic-gate 1007*7c478bd9Sstevel@tonic-gate /* 1008*7c478bd9Sstevel@tonic-gate * Make sure registers are flushed 1009*7c478bd9Sstevel@tonic-gate */ 1010*7c478bd9Sstevel@tonic-gate (void) pci_config_get32(confhdl, PCI_CONF_BASE5); 1011*7c478bd9Sstevel@tonic-gate 1012*7c478bd9Sstevel@tonic-gate 1013*7c478bd9Sstevel@tonic-gate if (ndi_prop_remove(DDI_DEV_T_NONE, dip, SAVED_CONFIG_REGS) != 1014*7c478bd9Sstevel@tonic-gate DDI_PROP_SUCCESS) { 1015*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d can't remove prop %s", 1016*7c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 1017*7c478bd9Sstevel@tonic-gate SAVED_CONFIG_REGS); 1018*7c478bd9Sstevel@tonic-gate } 1019*7c478bd9Sstevel@tonic-gate 1020*7c478bd9Sstevel@tonic-gate pci_config_teardown(&confhdl); 1021*7c478bd9Sstevel@tonic-gate 1022*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 1023*7c478bd9Sstevel@tonic-gate 1024*7c478bd9Sstevel@tonic-gate restoreconfig_err: 1025*7c478bd9Sstevel@tonic-gate ddi_prop_free(maskbuf); 1026*7c478bd9Sstevel@tonic-gate if (ndi_prop_remove(DDI_DEV_T_NONE, dip, SAVED_CONFIG_REGS_MASK) != 1027*7c478bd9Sstevel@tonic-gate DDI_PROP_SUCCESS) { 1028*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d can't remove prop %s", 1029*7c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 1030*7c478bd9Sstevel@tonic-gate SAVED_CONFIG_REGS_MASK); 1031*7c478bd9Sstevel@tonic-gate } 1032*7c478bd9Sstevel@tonic-gate pci_config_teardown(&confhdl); 1033*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 1034*7c478bd9Sstevel@tonic-gate } 1035