1*26947304SEvan Yan /* 2*26947304SEvan Yan * CDDL HEADER START 3*26947304SEvan Yan * 4*26947304SEvan Yan * The contents of this file are subject to the terms of the 5*26947304SEvan Yan * Common Development and Distribution License (the "License"). 6*26947304SEvan Yan * You may not use this file except in compliance with the License. 7*26947304SEvan Yan * 8*26947304SEvan Yan * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*26947304SEvan Yan * or http://www.opensolaris.org/os/licensing. 10*26947304SEvan Yan * See the License for the specific language governing permissions 11*26947304SEvan Yan * and limitations under the License. 12*26947304SEvan Yan * 13*26947304SEvan Yan * When distributing Covered Code, include this CDDL HEADER in each 14*26947304SEvan Yan * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*26947304SEvan Yan * If applicable, add the following below this CDDL HEADER, with the 16*26947304SEvan Yan * fields enclosed by brackets "[]" replaced with your own identifying 17*26947304SEvan Yan * information: Portions Copyright [yyyy] [name of copyright owner] 18*26947304SEvan Yan * 19*26947304SEvan Yan * CDDL HEADER END 20*26947304SEvan Yan */ 21*26947304SEvan Yan 22*26947304SEvan Yan /* 23*26947304SEvan Yan * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24*26947304SEvan Yan * Use is subject to license terms. 25*26947304SEvan Yan */ 26*26947304SEvan Yan 27*26947304SEvan Yan /* 28*26947304SEvan Yan * This file contains the common hotplug code that is used by Standard 29*26947304SEvan Yan * PCIe and PCI HotPlug Controller code. 30*26947304SEvan Yan * 31*26947304SEvan Yan * NOTE: This file is compiled and delivered through misc/pcie module. 32*26947304SEvan Yan */ 33*26947304SEvan Yan 34*26947304SEvan Yan #include <sys/types.h> 35*26947304SEvan Yan #include <sys/conf.h> 36*26947304SEvan Yan #include <sys/kmem.h> 37*26947304SEvan Yan #include <sys/debug.h> 38*26947304SEvan Yan #include <sys/vtrace.h> 39*26947304SEvan Yan #include <sys/autoconf.h> 40*26947304SEvan Yan #include <sys/varargs.h> 41*26947304SEvan Yan #include <sys/ddi_impldefs.h> 42*26947304SEvan Yan #include <sys/time.h> 43*26947304SEvan Yan #include <sys/note.h> 44*26947304SEvan Yan #include <sys/callb.h> 45*26947304SEvan Yan #include <sys/ddi.h> 46*26947304SEvan Yan #include <sys/sunddi.h> 47*26947304SEvan Yan #include <sys/sunndi.h> 48*26947304SEvan Yan #include <sys/sysevent.h> 49*26947304SEvan Yan #include <sys/sysevent/eventdefs.h> 50*26947304SEvan Yan #include <sys/sysevent/dr.h> 51*26947304SEvan Yan #include <sys/pci_impl.h> 52*26947304SEvan Yan #include <sys/pci_cap.h> 53*26947304SEvan Yan #include <sys/hotplug/pci/pcicfg.h> 54*26947304SEvan Yan #include <sys/hotplug/pci/pcie_hp.h> 55*26947304SEvan Yan #include <sys/hotplug/pci/pciehpc.h> 56*26947304SEvan Yan #include <sys/hotplug/pci/pcishpc.h> 57*26947304SEvan Yan #include <io/pciex/pcieb.h> 58*26947304SEvan Yan 59*26947304SEvan Yan /* Local functions prototype */ 60*26947304SEvan Yan static int pcie_hp_list_occupants(dev_info_t *dip, void *arg); 61*26947304SEvan Yan static int pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip, 62*26947304SEvan Yan char *cn_name); 63*26947304SEvan Yan static int pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num); 64*26947304SEvan Yan static int pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg); 65*26947304SEvan Yan static int pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg); 66*26947304SEvan Yan static int pcie_hp_match_dev_func(dev_info_t *dip, void *hdl); 67*26947304SEvan Yan static boolean_t pcie_hp_match_dev(dev_info_t *dip, int dev_num); 68*26947304SEvan Yan static int pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num, 69*26947304SEvan Yan int *func_num); 70*26947304SEvan Yan static int pcie_hp_create_port_name_num(dev_info_t *dip, 71*26947304SEvan Yan ddi_hp_cn_info_t *cn_info); 72*26947304SEvan Yan static int pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num, 73*26947304SEvan Yan int func_num); 74*26947304SEvan Yan 75*26947304SEvan Yan /* 76*26947304SEvan Yan * Global functions (called by other drivers/modules) 77*26947304SEvan Yan */ 78*26947304SEvan Yan 79*26947304SEvan Yan /* 80*26947304SEvan Yan * return description text for led state 81*26947304SEvan Yan */ 82*26947304SEvan Yan char * 83*26947304SEvan Yan pcie_led_state_text(pcie_hp_led_state_t state) 84*26947304SEvan Yan { 85*26947304SEvan Yan switch (state) { 86*26947304SEvan Yan case PCIE_HP_LED_ON: 87*26947304SEvan Yan return (PCIEHPC_PROP_VALUE_ON); 88*26947304SEvan Yan case PCIE_HP_LED_OFF: 89*26947304SEvan Yan return (PCIEHPC_PROP_VALUE_OFF); 90*26947304SEvan Yan case PCIE_HP_LED_BLINK: 91*26947304SEvan Yan default: 92*26947304SEvan Yan return (PCIEHPC_PROP_VALUE_BLINK); 93*26947304SEvan Yan } 94*26947304SEvan Yan } 95*26947304SEvan Yan 96*26947304SEvan Yan /* 97*26947304SEvan Yan * return description text for slot condition 98*26947304SEvan Yan */ 99*26947304SEvan Yan char * 100*26947304SEvan Yan pcie_slot_condition_text(ap_condition_t condition) 101*26947304SEvan Yan { 102*26947304SEvan Yan switch (condition) { 103*26947304SEvan Yan case AP_COND_UNKNOWN: 104*26947304SEvan Yan return (PCIEHPC_PROP_VALUE_UNKNOWN); 105*26947304SEvan Yan case AP_COND_OK: 106*26947304SEvan Yan return (PCIEHPC_PROP_VALUE_OK); 107*26947304SEvan Yan case AP_COND_FAILING: 108*26947304SEvan Yan return (PCIEHPC_PROP_VALUE_FAILING); 109*26947304SEvan Yan case AP_COND_FAILED: 110*26947304SEvan Yan return (PCIEHPC_PROP_VALUE_FAILED); 111*26947304SEvan Yan case AP_COND_UNUSABLE: 112*26947304SEvan Yan return (PCIEHPC_PROP_VALUE_UNUSABLE); 113*26947304SEvan Yan default: 114*26947304SEvan Yan return (PCIEHPC_PROP_VALUE_UNKNOWN); 115*26947304SEvan Yan } 116*26947304SEvan Yan } 117*26947304SEvan Yan 118*26947304SEvan Yan /* 119*26947304SEvan Yan * routine to copy in a nvlist from userland 120*26947304SEvan Yan */ 121*26947304SEvan Yan int 122*26947304SEvan Yan pcie_copyin_nvlist(char *packed_buf, size_t packed_sz, nvlist_t **nvlp) 123*26947304SEvan Yan { 124*26947304SEvan Yan int ret = DDI_SUCCESS; 125*26947304SEvan Yan char *packed; 126*26947304SEvan Yan nvlist_t *dest = NULL; 127*26947304SEvan Yan 128*26947304SEvan Yan if (packed_buf == NULL || packed_sz == 0) 129*26947304SEvan Yan return (DDI_EINVAL); 130*26947304SEvan Yan 131*26947304SEvan Yan /* copyin packed nvlist */ 132*26947304SEvan Yan if ((packed = kmem_alloc(packed_sz, KM_SLEEP)) == NULL) 133*26947304SEvan Yan return (DDI_ENOMEM); 134*26947304SEvan Yan 135*26947304SEvan Yan if (copyin(packed_buf, packed, packed_sz) != 0) { 136*26947304SEvan Yan cmn_err(CE_WARN, "pcie_copyin_nvlist: copyin failed.\n"); 137*26947304SEvan Yan ret = DDI_FAILURE; 138*26947304SEvan Yan goto copyin_cleanup; 139*26947304SEvan Yan } 140*26947304SEvan Yan 141*26947304SEvan Yan /* unpack packed nvlist */ 142*26947304SEvan Yan if ((ret = nvlist_unpack(packed, packed_sz, &dest, KM_SLEEP)) != 0) { 143*26947304SEvan Yan cmn_err(CE_WARN, "pcie_copyin_nvlist: nvlist_unpack " 144*26947304SEvan Yan "failed with err %d\n", ret); 145*26947304SEvan Yan switch (ret) { 146*26947304SEvan Yan case EINVAL: 147*26947304SEvan Yan case ENOTSUP: 148*26947304SEvan Yan ret = DDI_EINVAL; 149*26947304SEvan Yan goto copyin_cleanup; 150*26947304SEvan Yan case ENOMEM: 151*26947304SEvan Yan ret = DDI_ENOMEM; 152*26947304SEvan Yan goto copyin_cleanup; 153*26947304SEvan Yan default: 154*26947304SEvan Yan ret = DDI_FAILURE; 155*26947304SEvan Yan goto copyin_cleanup; 156*26947304SEvan Yan } 157*26947304SEvan Yan } 158*26947304SEvan Yan *nvlp = dest; 159*26947304SEvan Yan copyin_cleanup: 160*26947304SEvan Yan kmem_free(packed, packed_sz); 161*26947304SEvan Yan return (ret); 162*26947304SEvan Yan } 163*26947304SEvan Yan 164*26947304SEvan Yan /* 165*26947304SEvan Yan * routine to copy out a nvlist to userland 166*26947304SEvan Yan */ 167*26947304SEvan Yan int 168*26947304SEvan Yan pcie_copyout_nvlist(nvlist_t *nvl, char *packed_buf, size_t *buf_sz) 169*26947304SEvan Yan { 170*26947304SEvan Yan int err = 0; 171*26947304SEvan Yan char *buf = NULL; 172*26947304SEvan Yan size_t packed_sz; 173*26947304SEvan Yan 174*26947304SEvan Yan if (nvl == NULL || packed_buf == NULL || buf_sz == NULL) 175*26947304SEvan Yan return (DDI_EINVAL); 176*26947304SEvan Yan 177*26947304SEvan Yan /* pack nvlist, the library will allocate memory */ 178*26947304SEvan Yan if ((err = nvlist_pack(nvl, &buf, &packed_sz, NV_ENCODE_NATIVE, 0)) 179*26947304SEvan Yan != 0) { 180*26947304SEvan Yan cmn_err(CE_WARN, "pcie_copyout_nvlist: nvlist_pack " 181*26947304SEvan Yan "failed with err %d\n", err); 182*26947304SEvan Yan switch (err) { 183*26947304SEvan Yan case EINVAL: 184*26947304SEvan Yan case ENOTSUP: 185*26947304SEvan Yan return (DDI_EINVAL); 186*26947304SEvan Yan case ENOMEM: 187*26947304SEvan Yan return (DDI_ENOMEM); 188*26947304SEvan Yan default: 189*26947304SEvan Yan return (DDI_FAILURE); 190*26947304SEvan Yan } 191*26947304SEvan Yan } 192*26947304SEvan Yan if (packed_sz > *buf_sz) { 193*26947304SEvan Yan return (DDI_EINVAL); 194*26947304SEvan Yan } 195*26947304SEvan Yan 196*26947304SEvan Yan /* copyout packed nvlist */ 197*26947304SEvan Yan if (copyout(buf, packed_buf, packed_sz) != 0) { 198*26947304SEvan Yan cmn_err(CE_WARN, "pcie_copyout_nvlist: copyout " "failed.\n"); 199*26947304SEvan Yan kmem_free(buf, packed_sz); 200*26947304SEvan Yan return (DDI_FAILURE); 201*26947304SEvan Yan } 202*26947304SEvan Yan 203*26947304SEvan Yan *buf_sz = packed_sz; 204*26947304SEvan Yan kmem_free(buf, packed_sz); 205*26947304SEvan Yan return (DDI_SUCCESS); 206*26947304SEvan Yan } 207*26947304SEvan Yan 208*26947304SEvan Yan /* 209*26947304SEvan Yan * init bus_hp_op entry and init hotpluggable slots & virtual ports 210*26947304SEvan Yan */ 211*26947304SEvan Yan int 212*26947304SEvan Yan pcie_hp_init(dev_info_t *dip, caddr_t arg) 213*26947304SEvan Yan { 214*26947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 215*26947304SEvan Yan int ret = DDI_SUCCESS, count; 216*26947304SEvan Yan dev_info_t *cdip; 217*26947304SEvan Yan 218*26947304SEvan Yan if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) { 219*26947304SEvan Yan /* Init hotplug controller */ 220*26947304SEvan Yan ret = pciehpc_init(dip, arg); 221*26947304SEvan Yan } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) { 222*26947304SEvan Yan ret = pcishpc_init(dip); 223*26947304SEvan Yan } 224*26947304SEvan Yan 225*26947304SEvan Yan if (ret != DDI_SUCCESS) { 226*26947304SEvan Yan cmn_err(CE_WARN, "pcie_hp_init: initialize hotplug " 227*26947304SEvan Yan "controller failed with %d\n", ret); 228*26947304SEvan Yan return (ret); 229*26947304SEvan Yan } 230*26947304SEvan Yan 231*26947304SEvan Yan ndi_devi_enter(dip, &count); 232*26947304SEvan Yan 233*26947304SEvan Yan /* Create port for the first level children */ 234*26947304SEvan Yan cdip = ddi_get_child(dip); 235*26947304SEvan Yan while (cdip != NULL) { 236*26947304SEvan Yan if ((ret = pcie_hp_register_port(cdip, dip, NULL)) 237*26947304SEvan Yan != DDI_SUCCESS) { 238*26947304SEvan Yan /* stop and cleanup */ 239*26947304SEvan Yan break; 240*26947304SEvan Yan } 241*26947304SEvan Yan cdip = ddi_get_next_sibling(cdip); 242*26947304SEvan Yan } 243*26947304SEvan Yan ndi_devi_exit(dip, count); 244*26947304SEvan Yan if (ret != DDI_SUCCESS) { 245*26947304SEvan Yan cmn_err(CE_WARN, "pcie_hp_init: initialize virtual " 246*26947304SEvan Yan "hotplug port failed with %d\n", ret); 247*26947304SEvan Yan (void) pcie_hp_uninit(dip); 248*26947304SEvan Yan 249*26947304SEvan Yan return (ret); 250*26947304SEvan Yan } 251*26947304SEvan Yan 252*26947304SEvan Yan return (DDI_SUCCESS); 253*26947304SEvan Yan } 254*26947304SEvan Yan 255*26947304SEvan Yan /* 256*26947304SEvan Yan * uninit the hotpluggable slots and virtual ports 257*26947304SEvan Yan */ 258*26947304SEvan Yan int 259*26947304SEvan Yan pcie_hp_uninit(dev_info_t *dip) 260*26947304SEvan Yan { 261*26947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 262*26947304SEvan Yan pcie_hp_unreg_port_t arg; 263*26947304SEvan Yan 264*26947304SEvan Yan /* 265*26947304SEvan Yan * Must set arg.rv to NDI_SUCCESS so that if there's no port 266*26947304SEvan Yan * under this dip, we still return success thus the bridge 267*26947304SEvan Yan * driver can be successfully detached. 268*26947304SEvan Yan * 269*26947304SEvan Yan * Note that during the probe PCI configurator calls 270*26947304SEvan Yan * ndi_devi_offline() to detach driver for a new probed bridge, 271*26947304SEvan Yan * so that it can reprogram the resources for the bridge, 272*26947304SEvan Yan * ndi_devi_offline() calls into pcieb_detach() which in turn 273*26947304SEvan Yan * calls into this function. In this case there are no ports 274*26947304SEvan Yan * created under a new probe bridge dip, as ports are only 275*26947304SEvan Yan * created after the configurator finishing probing, thus the 276*26947304SEvan Yan * ndi_hp_walk_cn() will see no ports when this is called 277*26947304SEvan Yan * from the PCI configurtor. 278*26947304SEvan Yan */ 279*26947304SEvan Yan arg.nexus_dip = dip; 280*26947304SEvan Yan arg.connector_num = DDI_HP_CN_NUM_NONE; 281*26947304SEvan Yan arg.rv = NDI_SUCCESS; 282*26947304SEvan Yan 283*26947304SEvan Yan /* tear down all virtual hotplug handles */ 284*26947304SEvan Yan ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg); 285*26947304SEvan Yan 286*26947304SEvan Yan if (arg.rv != NDI_SUCCESS) 287*26947304SEvan Yan return (DDI_FAILURE); 288*26947304SEvan Yan 289*26947304SEvan Yan if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) 290*26947304SEvan Yan (void) pciehpc_uninit(dip); 291*26947304SEvan Yan else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) 292*26947304SEvan Yan (void) pcishpc_uninit(dip); 293*26947304SEvan Yan 294*26947304SEvan Yan return (DDI_SUCCESS); 295*26947304SEvan Yan } 296*26947304SEvan Yan 297*26947304SEvan Yan /* 298*26947304SEvan Yan * interrupt handler 299*26947304SEvan Yan */ 300*26947304SEvan Yan int 301*26947304SEvan Yan pcie_hp_intr(dev_info_t *dip) 302*26947304SEvan Yan { 303*26947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 304*26947304SEvan Yan int ret = DDI_INTR_UNCLAIMED; 305*26947304SEvan Yan 306*26947304SEvan Yan if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) 307*26947304SEvan Yan ret = pciehpc_intr(dip); 308*26947304SEvan Yan else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) 309*26947304SEvan Yan ret = pcishpc_intr(dip); 310*26947304SEvan Yan 311*26947304SEvan Yan return (ret); 312*26947304SEvan Yan } 313*26947304SEvan Yan 314*26947304SEvan Yan /* 315*26947304SEvan Yan * Probe the given PCIe/PCI Hotplug Connection (CN). 316*26947304SEvan Yan */ 317*26947304SEvan Yan /*ARGSUSED*/ 318*26947304SEvan Yan int 319*26947304SEvan Yan pcie_hp_probe(pcie_hp_slot_t *slot_p) 320*26947304SEvan Yan { 321*26947304SEvan Yan pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 322*26947304SEvan Yan dev_info_t *dip = ctrl_p->hc_dip; 323*26947304SEvan Yan 324*26947304SEvan Yan /* 325*26947304SEvan Yan * Call the configurator to probe a given PCI hotplug 326*26947304SEvan Yan * Hotplug Connection (CN). 327*26947304SEvan Yan */ 328*26947304SEvan Yan if (pcicfg_configure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0) 329*26947304SEvan Yan != PCICFG_SUCCESS) { 330*26947304SEvan Yan PCIE_DBG("pcie_hp_probe() failed\n"); 331*26947304SEvan Yan return (DDI_FAILURE); 332*26947304SEvan Yan } 333*26947304SEvan Yan slot_p->hs_condition = AP_COND_OK; 334*26947304SEvan Yan pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip), 335*26947304SEvan Yan slot_p->hs_minor), slot_p->hs_device_num); 336*26947304SEvan Yan 337*26947304SEvan Yan /* 338*26947304SEvan Yan * Create ports for the newly probed devices. 339*26947304SEvan Yan * Note, this is only for the first level children because the 340*26947304SEvan Yan * descendants' ports will be created during bridge driver attach. 341*26947304SEvan Yan */ 342*26947304SEvan Yan return (pcie_hp_register_ports_for_dev(dip, slot_p->hs_device_num)); 343*26947304SEvan Yan } 344*26947304SEvan Yan 345*26947304SEvan Yan /* 346*26947304SEvan Yan * Unprobe the given PCIe/PCI Hotplug Connection (CN): 347*26947304SEvan Yan * 1. remove all child device nodes 348*26947304SEvan Yan * 2. unregister all dependent ports 349*26947304SEvan Yan */ 350*26947304SEvan Yan /*ARGSUSED*/ 351*26947304SEvan Yan int 352*26947304SEvan Yan pcie_hp_unprobe(pcie_hp_slot_t *slot_p) 353*26947304SEvan Yan { 354*26947304SEvan Yan pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 355*26947304SEvan Yan dev_info_t *dip = ctrl_p->hc_dip; 356*26947304SEvan Yan pcie_hp_unreg_port_t arg; 357*26947304SEvan Yan 358*26947304SEvan Yan /* 359*26947304SEvan Yan * Call the configurator to unprobe a given PCI hotplug 360*26947304SEvan Yan * Hotplug Connection (CN). 361*26947304SEvan Yan */ 362*26947304SEvan Yan if (pcicfg_unconfigure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0) 363*26947304SEvan Yan != PCICFG_SUCCESS) { 364*26947304SEvan Yan PCIE_DBG("pcie_hp_unprobe() failed\n"); 365*26947304SEvan Yan return (DDI_FAILURE); 366*26947304SEvan Yan } 367*26947304SEvan Yan slot_p->hs_condition = AP_COND_UNKNOWN; 368*26947304SEvan Yan pcie_hp_delete_occupant_props(dip, makedevice(ddi_driver_major(dip), 369*26947304SEvan Yan slot_p->hs_minor)); 370*26947304SEvan Yan 371*26947304SEvan Yan /* 372*26947304SEvan Yan * Remove ports for the unprobed devices. 373*26947304SEvan Yan * Note, this is only for the first level children because the 374*26947304SEvan Yan * descendants' ports were already removed during bridge driver dettach. 375*26947304SEvan Yan */ 376*26947304SEvan Yan arg.nexus_dip = dip; 377*26947304SEvan Yan arg.connector_num = slot_p->hs_info.cn_num; 378*26947304SEvan Yan arg.rv = NDI_SUCCESS; 379*26947304SEvan Yan ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg); 380*26947304SEvan Yan 381*26947304SEvan Yan return (arg.rv == NDI_SUCCESS) ? (DDI_SUCCESS) : (DDI_FAILURE); 382*26947304SEvan Yan } 383*26947304SEvan Yan 384*26947304SEvan Yan /* Read-only probe: no hardware register programming. */ 385*26947304SEvan Yan int 386*26947304SEvan Yan pcie_read_only_probe(dev_info_t *dip, char *cn_name, dev_info_t **pcdip) 387*26947304SEvan Yan { 388*26947304SEvan Yan long dev, func; 389*26947304SEvan Yan int ret; 390*26947304SEvan Yan char *sp; 391*26947304SEvan Yan dev_info_t *cdip; 392*26947304SEvan Yan 393*26947304SEvan Yan *pcdip = NULL; 394*26947304SEvan Yan /* 395*26947304SEvan Yan * Parse the string of a pci Port name and get the device number 396*26947304SEvan Yan * and function number. 397*26947304SEvan Yan */ 398*26947304SEvan Yan if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0) 399*26947304SEvan Yan return (DDI_EINVAL); 400*26947304SEvan Yan if (ddi_strtol(sp + 1, NULL, 10, &func) != 0) 401*26947304SEvan Yan return (DDI_EINVAL); 402*26947304SEvan Yan 403*26947304SEvan Yan ret = pcicfg_configure(dip, (int)dev, (int)func, 404*26947304SEvan Yan PCICFG_FLAG_READ_ONLY); 405*26947304SEvan Yan if (ret == PCICFG_SUCCESS) { 406*26947304SEvan Yan cdip = pcie_hp_devi_find(dip, (int)dev, (int)func); 407*26947304SEvan Yan *pcdip = cdip; 408*26947304SEvan Yan } 409*26947304SEvan Yan return (ret); 410*26947304SEvan Yan } 411*26947304SEvan Yan 412*26947304SEvan Yan /* Read-only unprobe: no hardware register programming. */ 413*26947304SEvan Yan int 414*26947304SEvan Yan pcie_read_only_unprobe(dev_info_t *dip, char *cn_name) 415*26947304SEvan Yan { 416*26947304SEvan Yan long dev, func; 417*26947304SEvan Yan int ret; 418*26947304SEvan Yan char *sp; 419*26947304SEvan Yan 420*26947304SEvan Yan /* 421*26947304SEvan Yan * Parse the string of a pci Port name and get the device number 422*26947304SEvan Yan * and function number. 423*26947304SEvan Yan */ 424*26947304SEvan Yan if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0) 425*26947304SEvan Yan return (DDI_EINVAL); 426*26947304SEvan Yan if (ddi_strtol(sp + 1, NULL, 10, &func) != 0) 427*26947304SEvan Yan return (DDI_EINVAL); 428*26947304SEvan Yan 429*26947304SEvan Yan ret = pcicfg_unconfigure(dip, (int)dev, (int)func, 430*26947304SEvan Yan PCICFG_FLAG_READ_ONLY); 431*26947304SEvan Yan 432*26947304SEvan Yan return (ret); 433*26947304SEvan Yan } 434*26947304SEvan Yan 435*26947304SEvan Yan /* Control structure used to find a device in the devinfo tree */ 436*26947304SEvan Yan struct pcie_hp_find_ctrl { 437*26947304SEvan Yan uint_t device; 438*26947304SEvan Yan uint_t function; 439*26947304SEvan Yan dev_info_t *dip; 440*26947304SEvan Yan }; 441*26947304SEvan Yan 442*26947304SEvan Yan /* 443*26947304SEvan Yan * find a devinfo node with specified device and function number 444*26947304SEvan Yan * in the device tree under 'dip' 445*26947304SEvan Yan */ 446*26947304SEvan Yan dev_info_t * 447*26947304SEvan Yan pcie_hp_devi_find(dev_info_t *dip, uint_t device, uint_t function) 448*26947304SEvan Yan { 449*26947304SEvan Yan struct pcie_hp_find_ctrl ctrl; 450*26947304SEvan Yan int count; 451*26947304SEvan Yan 452*26947304SEvan Yan ctrl.device = device; 453*26947304SEvan Yan ctrl.function = function; 454*26947304SEvan Yan ctrl.dip = NULL; 455*26947304SEvan Yan 456*26947304SEvan Yan ndi_devi_enter(dip, &count); 457*26947304SEvan Yan ddi_walk_devs(ddi_get_child(dip), pcie_hp_match_dev_func, 458*26947304SEvan Yan (void *)&ctrl); 459*26947304SEvan Yan ndi_devi_exit(dip, count); 460*26947304SEvan Yan 461*26947304SEvan Yan return (ctrl.dip); 462*26947304SEvan Yan } 463*26947304SEvan Yan 464*26947304SEvan Yan /* 465*26947304SEvan Yan * routine to create 'pci-occupant' property for a hotplug slot 466*26947304SEvan Yan */ 467*26947304SEvan Yan void 468*26947304SEvan Yan pcie_hp_create_occupant_props(dev_info_t *dip, dev_t dev, int pci_dev) 469*26947304SEvan Yan { 470*26947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 471*26947304SEvan Yan pcie_hp_ctrl_t *ctrl_p = (pcie_hp_ctrl_t *)bus_p->bus_hp_ctrl; 472*26947304SEvan Yan pcie_hp_slot_t *slotp; 473*26947304SEvan Yan pcie_hp_cn_cfg_t cn_cfg; 474*26947304SEvan Yan pcie_hp_occupant_info_t *occupant; 475*26947304SEvan Yan int circular, i; 476*26947304SEvan Yan 477*26947304SEvan Yan ndi_devi_enter(dip, &circular); 478*26947304SEvan Yan 479*26947304SEvan Yan if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) { 480*26947304SEvan Yan slotp = (ctrl_p && (pci_dev == 0)) ? 481*26947304SEvan Yan ctrl_p->hc_slots[pci_dev] : NULL; 482*26947304SEvan Yan } else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) { 483*26947304SEvan Yan if (ctrl_p) { 484*26947304SEvan Yan int slot_num; 485*26947304SEvan Yan 486*26947304SEvan Yan slot_num = (ctrl_p->hc_device_increases) ? 487*26947304SEvan Yan (pci_dev - ctrl_p->hc_device_start) : 488*26947304SEvan Yan (pci_dev + ctrl_p->hc_device_start); 489*26947304SEvan Yan 490*26947304SEvan Yan slotp = ctrl_p->hc_slots[slot_num]; 491*26947304SEvan Yan } else { 492*26947304SEvan Yan slotp = NULL; 493*26947304SEvan Yan } 494*26947304SEvan Yan } 495*26947304SEvan Yan 496*26947304SEvan Yan if (slotp == NULL) 497*26947304SEvan Yan return; 498*26947304SEvan Yan 499*26947304SEvan Yan occupant = kmem_alloc(sizeof (pcie_hp_occupant_info_t), KM_SLEEP); 500*26947304SEvan Yan occupant->i = 0; 501*26947304SEvan Yan 502*26947304SEvan Yan cn_cfg.flag = B_FALSE; 503*26947304SEvan Yan cn_cfg.rv = NDI_SUCCESS; 504*26947304SEvan Yan cn_cfg.dip = NULL; 505*26947304SEvan Yan cn_cfg.slotp = (void *)slotp; 506*26947304SEvan Yan cn_cfg.cn_private = (void *)occupant; 507*26947304SEvan Yan 508*26947304SEvan Yan ddi_walk_devs(ddi_get_child(dip), pcie_hp_list_occupants, 509*26947304SEvan Yan (void *)&cn_cfg); 510*26947304SEvan Yan 511*26947304SEvan Yan if (occupant->i == 0) { 512*26947304SEvan Yan /* no occupants right now, need to create stub property */ 513*26947304SEvan Yan char *c[] = { "" }; 514*26947304SEvan Yan (void) ddi_prop_update_string_array(dev, dip, "pci-occupant", 515*26947304SEvan Yan c, 1); 516*26947304SEvan Yan } else { 517*26947304SEvan Yan (void) ddi_prop_update_string_array(dev, dip, "pci-occupant", 518*26947304SEvan Yan occupant->id, occupant->i); 519*26947304SEvan Yan } 520*26947304SEvan Yan 521*26947304SEvan Yan for (i = 0; i < occupant->i; i++) 522*26947304SEvan Yan kmem_free(occupant->id[i], sizeof (char[MAXPATHLEN])); 523*26947304SEvan Yan 524*26947304SEvan Yan kmem_free(occupant, sizeof (pcie_hp_occupant_info_t)); 525*26947304SEvan Yan 526*26947304SEvan Yan ndi_devi_exit(dip, circular); 527*26947304SEvan Yan } 528*26947304SEvan Yan 529*26947304SEvan Yan /* 530*26947304SEvan Yan * routine to remove 'pci-occupant' property for a hotplug slot 531*26947304SEvan Yan */ 532*26947304SEvan Yan void 533*26947304SEvan Yan pcie_hp_delete_occupant_props(dev_info_t *dip, dev_t dev) 534*26947304SEvan Yan { 535*26947304SEvan Yan (void) ddi_prop_remove(dev, dip, "pci-occupant"); 536*26947304SEvan Yan } 537*26947304SEvan Yan 538*26947304SEvan Yan /* 539*26947304SEvan Yan * general code to create a minor node, called from hotplug controller 540*26947304SEvan Yan * drivers. 541*26947304SEvan Yan */ 542*26947304SEvan Yan int 543*26947304SEvan Yan pcie_create_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot) 544*26947304SEvan Yan { 545*26947304SEvan Yan dev_info_t *dip = ctrl_p->hc_dip; 546*26947304SEvan Yan pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[slot]; 547*26947304SEvan Yan ddi_hp_cn_info_t *info_p = &slot_p->hs_info; 548*26947304SEvan Yan 549*26947304SEvan Yan if (ddi_create_minor_node(dip, info_p->cn_name, 550*26947304SEvan Yan S_IFCHR, slot_p->hs_minor, 551*26947304SEvan Yan DDI_NT_PCI_ATTACHMENT_POINT, 0) != DDI_SUCCESS) { 552*26947304SEvan Yan return (DDI_FAILURE); 553*26947304SEvan Yan } 554*26947304SEvan Yan 555*26947304SEvan Yan (void) ddi_prop_update_int(DDI_DEV_T_NONE, 556*26947304SEvan Yan dip, "ap-names", 1 << slot_p->hs_device_num); 557*26947304SEvan Yan 558*26947304SEvan Yan return (DDI_SUCCESS); 559*26947304SEvan Yan } 560*26947304SEvan Yan 561*26947304SEvan Yan /* 562*26947304SEvan Yan * general code to remove a minor node, called from hotplug controller 563*26947304SEvan Yan * drivers. 564*26947304SEvan Yan */ 565*26947304SEvan Yan void 566*26947304SEvan Yan pcie_remove_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot) 567*26947304SEvan Yan { 568*26947304SEvan Yan ddi_remove_minor_node(ctrl_p->hc_dip, 569*26947304SEvan Yan ctrl_p->hc_slots[slot]->hs_info.cn_name); 570*26947304SEvan Yan } 571*26947304SEvan Yan 572*26947304SEvan Yan /* 573*26947304SEvan Yan * Local functions (called within this file) 574*26947304SEvan Yan */ 575*26947304SEvan Yan 576*26947304SEvan Yan /* 577*26947304SEvan Yan * Register ports for all the children with device number device_num 578*26947304SEvan Yan */ 579*26947304SEvan Yan static int 580*26947304SEvan Yan pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num) 581*26947304SEvan Yan { 582*26947304SEvan Yan dev_info_t *cdip; 583*26947304SEvan Yan int rv; 584*26947304SEvan Yan 585*26947304SEvan Yan for (cdip = ddi_get_child(dip); cdip; 586*26947304SEvan Yan cdip = ddi_get_next_sibling(cdip)) { 587*26947304SEvan Yan if (pcie_hp_match_dev(cdip, device_num)) { 588*26947304SEvan Yan /* 589*26947304SEvan Yan * Found the newly probed device under the 590*26947304SEvan Yan * current slot. Register a port for it. 591*26947304SEvan Yan */ 592*26947304SEvan Yan if ((rv = pcie_hp_register_port(cdip, dip, NULL)) 593*26947304SEvan Yan != DDI_SUCCESS) 594*26947304SEvan Yan return (rv); 595*26947304SEvan Yan } else { 596*26947304SEvan Yan continue; 597*26947304SEvan Yan } 598*26947304SEvan Yan } 599*26947304SEvan Yan 600*26947304SEvan Yan return (DDI_SUCCESS); 601*26947304SEvan Yan } 602*26947304SEvan Yan 603*26947304SEvan Yan /* 604*26947304SEvan Yan * Unregister ports of a pci bridge dip, get called from ndi_hp_walk_cn() 605*26947304SEvan Yan * 606*26947304SEvan Yan * If connector_num is specified, then unregister the slot's dependent ports 607*26947304SEvan Yan * only; Otherwise, unregister all ports of a pci bridge dip. 608*26947304SEvan Yan */ 609*26947304SEvan Yan static int 610*26947304SEvan Yan pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg) 611*26947304SEvan Yan { 612*26947304SEvan Yan pcie_hp_unreg_port_t *unreg_arg = (pcie_hp_unreg_port_t *)arg; 613*26947304SEvan Yan dev_info_t *dip = unreg_arg->nexus_dip; 614*26947304SEvan Yan int rv = NDI_SUCCESS; 615*26947304SEvan Yan 616*26947304SEvan Yan if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT) { 617*26947304SEvan Yan unreg_arg->rv = rv; 618*26947304SEvan Yan return (DDI_WALK_CONTINUE); 619*26947304SEvan Yan } 620*26947304SEvan Yan 621*26947304SEvan Yan if (unreg_arg->connector_num != DDI_HP_CN_NUM_NONE) { 622*26947304SEvan Yan /* Unregister ports for all unprobed devices under a slot. */ 623*26947304SEvan Yan if (unreg_arg->connector_num == info->cn_num_dpd_on) { 624*26947304SEvan Yan 625*26947304SEvan Yan rv = ndi_hp_unregister(dip, info->cn_name); 626*26947304SEvan Yan } 627*26947304SEvan Yan } else { 628*26947304SEvan Yan 629*26947304SEvan Yan /* Unregister all ports of a pci bridge dip. */ 630*26947304SEvan Yan rv = ndi_hp_unregister(dip, info->cn_name); 631*26947304SEvan Yan } 632*26947304SEvan Yan 633*26947304SEvan Yan unreg_arg->rv = rv; 634*26947304SEvan Yan if (rv == NDI_SUCCESS) 635*26947304SEvan Yan return (DDI_WALK_CONTINUE); 636*26947304SEvan Yan else 637*26947304SEvan Yan return (DDI_WALK_TERMINATE); 638*26947304SEvan Yan } 639*26947304SEvan Yan 640*26947304SEvan Yan /* 641*26947304SEvan Yan * Find a port according to cn_name and get the port's state. 642*26947304SEvan Yan */ 643*26947304SEvan Yan static int 644*26947304SEvan Yan pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg) 645*26947304SEvan Yan { 646*26947304SEvan Yan pcie_hp_port_state_t *port = (pcie_hp_port_state_t *)arg; 647*26947304SEvan Yan 648*26947304SEvan Yan if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT) 649*26947304SEvan Yan return (DDI_WALK_CONTINUE); 650*26947304SEvan Yan 651*26947304SEvan Yan if (strcmp(info->cn_name, port->cn_name) == 0) { 652*26947304SEvan Yan /* Matched. */ 653*26947304SEvan Yan port->cn_state = info->cn_state; 654*26947304SEvan Yan port->rv = DDI_SUCCESS; 655*26947304SEvan Yan 656*26947304SEvan Yan return (DDI_WALK_TERMINATE); 657*26947304SEvan Yan } 658*26947304SEvan Yan 659*26947304SEvan Yan return (DDI_WALK_CONTINUE); 660*26947304SEvan Yan } 661*26947304SEvan Yan 662*26947304SEvan Yan /* 663*26947304SEvan Yan * Find the physical slot with the given device number; 664*26947304SEvan Yan * return the slot if found. 665*26947304SEvan Yan */ 666*26947304SEvan Yan static pcie_hp_slot_t * 667*26947304SEvan Yan pcie_find_physical_slot(dev_info_t *dip, int dev_num) 668*26947304SEvan Yan { 669*26947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 670*26947304SEvan Yan pcie_hp_ctrl_t *ctrl = PCIE_GET_HP_CTRL(dip); 671*26947304SEvan Yan 672*26947304SEvan Yan if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) { 673*26947304SEvan Yan /* PCIe has only one slot */ 674*26947304SEvan Yan return (dev_num == 0) ? (ctrl->hc_slots[0]) : (NULL); 675*26947304SEvan Yan } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) { 676*26947304SEvan Yan for (int slot = 0; slot < ctrl->hc_num_slots_impl; slot++) { 677*26947304SEvan Yan if (ctrl->hc_slots[slot]->hs_device_num == dev_num) { 678*26947304SEvan Yan /* found */ 679*26947304SEvan Yan return (ctrl->hc_slots[slot]); 680*26947304SEvan Yan } 681*26947304SEvan Yan } 682*26947304SEvan Yan } 683*26947304SEvan Yan 684*26947304SEvan Yan return (NULL); 685*26947304SEvan Yan } 686*26947304SEvan Yan 687*26947304SEvan Yan /* 688*26947304SEvan Yan * setup slot name/slot-number info for the port which is being registered. 689*26947304SEvan Yan */ 690*26947304SEvan Yan static int 691*26947304SEvan Yan pcie_hp_create_port_name_num(dev_info_t *dip, ddi_hp_cn_info_t *cn_info) 692*26947304SEvan Yan { 693*26947304SEvan Yan int ret, dev_num, func_num, name_len; 694*26947304SEvan Yan dev_info_t *pdip = ddi_get_parent(dip); 695*26947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(pdip); 696*26947304SEvan Yan pcie_hp_slot_t *slot; 697*26947304SEvan Yan pcie_req_id_t bdf; 698*26947304SEvan Yan char tmp[PCIE_HP_DEV_FUNC_NUM_STRING_LEN]; 699*26947304SEvan Yan 700*26947304SEvan Yan ret = pcie_get_bdf_from_dip(dip, &bdf); 701*26947304SEvan Yan if (ret != DDI_SUCCESS) { 702*26947304SEvan Yan return (ret); 703*26947304SEvan Yan } 704*26947304SEvan Yan if (PCIE_IS_RP(bus_p) || PCIE_IS_SWD(bus_p) || 705*26947304SEvan Yan PCIE_IS_PCI2PCIE(bus_p)) { 706*26947304SEvan Yan /* 707*26947304SEvan Yan * It is under a PCIe device, devcie number is always 0; 708*26947304SEvan Yan * function number might > 8 in ARI supported case. 709*26947304SEvan Yan */ 710*26947304SEvan Yan dev_num = 0; 711*26947304SEvan Yan func_num = (bdf & ((~PCI_REG_BUS_M) >> 8)); 712*26947304SEvan Yan } else { 713*26947304SEvan Yan dev_num = (bdf & (PCI_REG_DEV_M >> 8)) >> 3; 714*26947304SEvan Yan func_num = bdf & (PCI_REG_FUNC_M >> 8); 715*26947304SEvan Yan } 716*26947304SEvan Yan /* 717*26947304SEvan Yan * The string length of dev_num and func_num must be no longer than 4 718*26947304SEvan Yan * including the string end mark. (With ARI case considered, e.g., 719*26947304SEvan Yan * dev_num=0x0, func_num=0xff.) 720*26947304SEvan Yan */ 721*26947304SEvan Yan (void) snprintf(tmp, PCIE_HP_DEV_FUNC_NUM_STRING_LEN, "%x%x", 722*26947304SEvan Yan dev_num, func_num); 723*26947304SEvan Yan /* 724*26947304SEvan Yan * Calculate the length of cn_name. 725*26947304SEvan Yan * The format of pci port name is: pci.d,f 726*26947304SEvan Yan * d stands for dev_num, f stands for func_num. So the length of the 727*26947304SEvan Yan * name string can be calculated as following. 728*26947304SEvan Yan */ 729*26947304SEvan Yan name_len = strlen(tmp) + PCIE_HP_PORT_NAME_STRING_LEN + 1; 730*26947304SEvan Yan 731*26947304SEvan Yan cn_info->cn_name = (char *)kmem_zalloc(name_len, KM_SLEEP); 732*26947304SEvan Yan (void) snprintf(cn_info->cn_name, name_len, "pci.%x,%x", 733*26947304SEvan Yan dev_num, func_num); 734*26947304SEvan Yan cn_info->cn_num = (dev_num << 8) | func_num; 735*26947304SEvan Yan slot = pcie_find_physical_slot(pdip, dev_num); 736*26947304SEvan Yan 737*26947304SEvan Yan cn_info->cn_num_dpd_on = slot ? 738*26947304SEvan Yan slot->hs_info.cn_num : DDI_HP_CN_NUM_NONE; 739*26947304SEvan Yan 740*26947304SEvan Yan return (DDI_SUCCESS); 741*26947304SEvan Yan } 742*26947304SEvan Yan 743*26947304SEvan Yan /* 744*26947304SEvan Yan * Extract device and function number from port name, whose format is 745*26947304SEvan Yan * something like 'pci.1,0' 746*26947304SEvan Yan */ 747*26947304SEvan Yan static int 748*26947304SEvan Yan pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num, int *func_num) 749*26947304SEvan Yan { 750*26947304SEvan Yan int name_len, ret; 751*26947304SEvan Yan long d, f; 752*26947304SEvan Yan char *sp; 753*26947304SEvan Yan 754*26947304SEvan Yan /* some checks for the input name */ 755*26947304SEvan Yan name_len = strlen(cn_name); 756*26947304SEvan Yan if ((name_len <= PCIE_HP_PORT_NAME_STRING_LEN) || 757*26947304SEvan Yan (name_len > (PCIE_HP_PORT_NAME_STRING_LEN + 758*26947304SEvan Yan PCIE_HP_DEV_FUNC_NUM_STRING_LEN - 1)) || 759*26947304SEvan Yan (strncmp("pci.", cn_name, 4) != 0)) { 760*26947304SEvan Yan return (DDI_EINVAL); 761*26947304SEvan Yan } 762*26947304SEvan Yan ret = ddi_strtol(cn_name + 4, &sp, 10, &d); 763*26947304SEvan Yan if (ret != DDI_SUCCESS) 764*26947304SEvan Yan return (ret); 765*26947304SEvan Yan 766*26947304SEvan Yan if (strncmp(",", sp, 1) != 0) 767*26947304SEvan Yan return (DDI_EINVAL); 768*26947304SEvan Yan 769*26947304SEvan Yan ret = ddi_strtol(sp + 1, NULL, 10, &f); 770*26947304SEvan Yan if (ret != DDI_SUCCESS) 771*26947304SEvan Yan return (ret); 772*26947304SEvan Yan *dev_num = (int)d; 773*26947304SEvan Yan *func_num = (int)f; 774*26947304SEvan Yan 775*26947304SEvan Yan return (ret); 776*26947304SEvan Yan } 777*26947304SEvan Yan 778*26947304SEvan Yan /* 779*26947304SEvan Yan * Check/copy cn_name and set connection numbers. 780*26947304SEvan Yan * If it is a valid name, then setup cn_info for the newly created port. 781*26947304SEvan Yan */ 782*26947304SEvan Yan static int 783*26947304SEvan Yan pcie_hp_setup_port_name_num(dev_info_t *pdip, char *cn_name, 784*26947304SEvan Yan ddi_hp_cn_info_t *cn_info) 785*26947304SEvan Yan { 786*26947304SEvan Yan int dev_num, func_num, ret; 787*26947304SEvan Yan pcie_hp_slot_t *slot; 788*26947304SEvan Yan 789*26947304SEvan Yan if ((ret = pcie_hp_get_df_from_port_name(cn_name, &dev_num, &func_num)) 790*26947304SEvan Yan != DDI_SUCCESS) 791*26947304SEvan Yan return (ret); 792*26947304SEvan Yan 793*26947304SEvan Yan if (pcie_hp_check_hardware_existence(pdip, dev_num, func_num) == 794*26947304SEvan Yan DDI_SUCCESS) { 795*26947304SEvan Yan cn_info->cn_state = DDI_HP_CN_STATE_PRESENT; 796*26947304SEvan Yan } else { 797*26947304SEvan Yan cn_info->cn_state = DDI_HP_CN_STATE_EMPTY; 798*26947304SEvan Yan } 799*26947304SEvan Yan 800*26947304SEvan Yan cn_info->cn_name = ddi_strdup(cn_name, KM_SLEEP); 801*26947304SEvan Yan cn_info->cn_num = (dev_num << 8) | func_num; 802*26947304SEvan Yan 803*26947304SEvan Yan slot = pcie_find_physical_slot(pdip, dev_num); 804*26947304SEvan Yan if (slot) { 805*26947304SEvan Yan cn_info->cn_num_dpd_on = slot->hs_info.cn_num; 806*26947304SEvan Yan } else { 807*26947304SEvan Yan cn_info->cn_num_dpd_on = DDI_HP_CN_NUM_NONE; 808*26947304SEvan Yan } 809*26947304SEvan Yan return (DDI_SUCCESS); 810*26947304SEvan Yan } 811*26947304SEvan Yan 812*26947304SEvan Yan static int 813*26947304SEvan Yan ndi2ddi(int n) 814*26947304SEvan Yan { 815*26947304SEvan Yan int ret; 816*26947304SEvan Yan 817*26947304SEvan Yan switch (n) { 818*26947304SEvan Yan case NDI_SUCCESS: 819*26947304SEvan Yan ret = DDI_SUCCESS; 820*26947304SEvan Yan break; 821*26947304SEvan Yan case NDI_NOMEM: 822*26947304SEvan Yan ret = DDI_ENOMEM; 823*26947304SEvan Yan break; 824*26947304SEvan Yan case NDI_BUSY: 825*26947304SEvan Yan ret = DDI_EBUSY; 826*26947304SEvan Yan break; 827*26947304SEvan Yan case NDI_EINVAL: 828*26947304SEvan Yan ret = DDI_EINVAL; 829*26947304SEvan Yan break; 830*26947304SEvan Yan case NDI_ENOTSUP: 831*26947304SEvan Yan ret = DDI_ENOTSUP; 832*26947304SEvan Yan break; 833*26947304SEvan Yan case NDI_FAILURE: 834*26947304SEvan Yan default: 835*26947304SEvan Yan ret = DDI_FAILURE; 836*26947304SEvan Yan break; 837*26947304SEvan Yan } 838*26947304SEvan Yan return (ret); 839*26947304SEvan Yan } 840*26947304SEvan Yan 841*26947304SEvan Yan /* 842*26947304SEvan Yan * Common routine to create and register a new port 843*26947304SEvan Yan * 844*26947304SEvan Yan * Create an empty port if dip is NULL, and cn_name needs to be specified in 845*26947304SEvan Yan * this case. Otherwise, create a port mapping to the specified dip, and cn_name 846*26947304SEvan Yan * is not needed in this case. 847*26947304SEvan Yan */ 848*26947304SEvan Yan static int 849*26947304SEvan Yan pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip, char *cn_name) 850*26947304SEvan Yan { 851*26947304SEvan Yan ddi_hp_cn_info_t *cn_info; 852*26947304SEvan Yan int ret; 853*26947304SEvan Yan 854*26947304SEvan Yan ASSERT((dip == NULL) != (cn_name == NULL)); 855*26947304SEvan Yan cn_info = kmem_zalloc(sizeof (ddi_hp_cn_info_t), KM_SLEEP); 856*26947304SEvan Yan if (dip != NULL) 857*26947304SEvan Yan ret = pcie_hp_create_port_name_num(dip, cn_info); 858*26947304SEvan Yan else 859*26947304SEvan Yan ret = pcie_hp_setup_port_name_num(pdip, cn_name, cn_info); 860*26947304SEvan Yan 861*26947304SEvan Yan if (ret != DDI_SUCCESS) { 862*26947304SEvan Yan kmem_free(cn_info, sizeof (ddi_hp_cn_info_t)); 863*26947304SEvan Yan return (ret); 864*26947304SEvan Yan } 865*26947304SEvan Yan 866*26947304SEvan Yan cn_info->cn_child = dip; 867*26947304SEvan Yan cn_info->cn_type = DDI_HP_CN_TYPE_VIRTUAL_PORT; 868*26947304SEvan Yan cn_info->cn_type_str = DDI_HP_CN_TYPE_STR_PORT; 869*26947304SEvan Yan 870*26947304SEvan Yan ret = ndi_hp_register(pdip, cn_info); 871*26947304SEvan Yan 872*26947304SEvan Yan kmem_free(cn_info->cn_name, strlen(cn_info->cn_name) + 1); 873*26947304SEvan Yan kmem_free(cn_info, sizeof (ddi_hp_cn_info_t)); 874*26947304SEvan Yan 875*26947304SEvan Yan return (ndi2ddi(ret)); 876*26947304SEvan Yan } 877*26947304SEvan Yan 878*26947304SEvan Yan /* Check if there is a piece of hardware exist corresponding to the cn_name */ 879*26947304SEvan Yan static int 880*26947304SEvan Yan pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num, int func_num) 881*26947304SEvan Yan { 882*26947304SEvan Yan 883*26947304SEvan Yan /* 884*26947304SEvan Yan * VHPTODO: 885*26947304SEvan Yan * According to device and function number, check if there is a hardware 886*26947304SEvan Yan * device exists. Currently, this function can not be reached before 887*26947304SEvan Yan * we enable state transition to or from "Port-Empty" or "Port-Present" 888*26947304SEvan Yan * states. When the pci device type project is integrated, we are going 889*26947304SEvan Yan * to call the pci config space access interfaces introduced by it. 890*26947304SEvan Yan */ 891*26947304SEvan Yan _NOTE(ARGUNUSED(dip, dev_num, func_num)); 892*26947304SEvan Yan 893*26947304SEvan Yan return (DDI_SUCCESS); 894*26947304SEvan Yan } 895*26947304SEvan Yan 896*26947304SEvan Yan /* 897*26947304SEvan Yan * Dispatch hotplug commands to different hotplug controller drivers, including 898*26947304SEvan Yan * physical and virtual hotplug operations. 899*26947304SEvan Yan */ 900*26947304SEvan Yan /* ARGSUSED */ 901*26947304SEvan Yan int 902*26947304SEvan Yan pcie_hp_common_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op, 903*26947304SEvan Yan void *arg, void *result) 904*26947304SEvan Yan { 905*26947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 906*26947304SEvan Yan int ret = DDI_SUCCESS; 907*26947304SEvan Yan 908*26947304SEvan Yan PCIE_DBG("pcie_hp_common_ops: dip=%p cn_name=%s op=%x arg=%p\n", 909*26947304SEvan Yan dip, cn_name, op, arg); 910*26947304SEvan Yan 911*26947304SEvan Yan switch (op) { 912*26947304SEvan Yan case DDI_HPOP_CN_CREATE_PORT: 913*26947304SEvan Yan { 914*26947304SEvan Yan /* create an empty port */ 915*26947304SEvan Yan return (pcie_hp_register_port(NULL, dip, cn_name)); 916*26947304SEvan Yan } 917*26947304SEvan Yan case DDI_HPOP_CN_CHANGE_STATE: 918*26947304SEvan Yan { 919*26947304SEvan Yan ddi_hp_cn_state_t curr_state; 920*26947304SEvan Yan ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg; 921*26947304SEvan Yan pcie_hp_port_state_t state_arg; 922*26947304SEvan Yan 923*26947304SEvan Yan if (target_state < DDI_HP_CN_STATE_PORT_EMPTY) { 924*26947304SEvan Yan /* this is for physical slot state change */ 925*26947304SEvan Yan break; 926*26947304SEvan Yan } 927*26947304SEvan Yan PCIE_DBG("pcie_hp_common_ops: change port state" 928*26947304SEvan Yan " dip=%p cn_name=%s" 929*26947304SEvan Yan " op=%x arg=%p\n", (void *)dip, cn_name, op, arg); 930*26947304SEvan Yan 931*26947304SEvan Yan state_arg.rv = DDI_FAILURE; 932*26947304SEvan Yan state_arg.cn_name = cn_name; 933*26947304SEvan Yan ndi_hp_walk_cn(dip, pcie_hp_get_port_state, &state_arg); 934*26947304SEvan Yan if (state_arg.rv != DDI_SUCCESS) { 935*26947304SEvan Yan /* can not find the port */ 936*26947304SEvan Yan return (DDI_EINVAL); 937*26947304SEvan Yan } 938*26947304SEvan Yan curr_state = state_arg.cn_state; 939*26947304SEvan Yan /* 940*26947304SEvan Yan * Check if this is for changing port's state: change to/from 941*26947304SEvan Yan * PORT_EMPTY/PRESENT states. 942*26947304SEvan Yan */ 943*26947304SEvan Yan if (curr_state < target_state) { 944*26947304SEvan Yan /* Upgrade state */ 945*26947304SEvan Yan switch (curr_state) { 946*26947304SEvan Yan case DDI_HP_CN_STATE_PORT_EMPTY: 947*26947304SEvan Yan if (target_state == 948*26947304SEvan Yan DDI_HP_CN_STATE_PORT_PRESENT) { 949*26947304SEvan Yan int dev_num, func_num; 950*26947304SEvan Yan 951*26947304SEvan Yan ret = pcie_hp_get_df_from_port_name( 952*26947304SEvan Yan cn_name, &dev_num, &func_num); 953*26947304SEvan Yan if (ret != DDI_SUCCESS) 954*26947304SEvan Yan goto port_state_done; 955*26947304SEvan Yan 956*26947304SEvan Yan ret = pcie_hp_check_hardware_existence( 957*26947304SEvan Yan dip, dev_num, func_num); 958*26947304SEvan Yan } else if (target_state == 959*26947304SEvan Yan DDI_HP_CN_STATE_OFFLINE) { 960*26947304SEvan Yan ret = pcie_read_only_probe(dip, 961*26947304SEvan Yan cn_name, (dev_info_t **)result); 962*26947304SEvan Yan } else 963*26947304SEvan Yan ret = DDI_EINVAL; 964*26947304SEvan Yan 965*26947304SEvan Yan goto port_state_done; 966*26947304SEvan Yan case DDI_HP_CN_STATE_PORT_PRESENT: 967*26947304SEvan Yan if (target_state == 968*26947304SEvan Yan DDI_HP_CN_STATE_OFFLINE) 969*26947304SEvan Yan ret = pcie_read_only_probe(dip, 970*26947304SEvan Yan cn_name, (dev_info_t **)result); 971*26947304SEvan Yan else 972*26947304SEvan Yan ret = DDI_EINVAL; 973*26947304SEvan Yan 974*26947304SEvan Yan goto port_state_done; 975*26947304SEvan Yan default: 976*26947304SEvan Yan ASSERT("unexpected state"); 977*26947304SEvan Yan } 978*26947304SEvan Yan } else { 979*26947304SEvan Yan /* Downgrade state */ 980*26947304SEvan Yan switch (curr_state) { 981*26947304SEvan Yan case DDI_HP_CN_STATE_PORT_PRESENT: 982*26947304SEvan Yan { 983*26947304SEvan Yan int dev_num, func_num; 984*26947304SEvan Yan 985*26947304SEvan Yan ret = pcie_hp_get_df_from_port_name(cn_name, 986*26947304SEvan Yan &dev_num, &func_num); 987*26947304SEvan Yan if (ret != DDI_SUCCESS) 988*26947304SEvan Yan goto port_state_done; 989*26947304SEvan Yan 990*26947304SEvan Yan ret = pcie_hp_check_hardware_existence(dip, 991*26947304SEvan Yan dev_num, func_num); 992*26947304SEvan Yan 993*26947304SEvan Yan goto port_state_done; 994*26947304SEvan Yan } 995*26947304SEvan Yan case DDI_HP_CN_STATE_OFFLINE: 996*26947304SEvan Yan ret = pcie_read_only_unprobe(dip, cn_name); 997*26947304SEvan Yan 998*26947304SEvan Yan goto port_state_done; 999*26947304SEvan Yan default: 1000*26947304SEvan Yan ASSERT("unexpected state"); 1001*26947304SEvan Yan } 1002*26947304SEvan Yan } 1003*26947304SEvan Yan port_state_done: 1004*26947304SEvan Yan *(ddi_hp_cn_state_t *)result = curr_state; 1005*26947304SEvan Yan return (ret); 1006*26947304SEvan Yan } 1007*26947304SEvan Yan default: 1008*26947304SEvan Yan break; 1009*26947304SEvan Yan } 1010*26947304SEvan Yan 1011*26947304SEvan Yan if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) { 1012*26947304SEvan Yan /* PCIe hotplug */ 1013*26947304SEvan Yan ret = pciehpc_hp_ops(dip, cn_name, op, arg, result); 1014*26947304SEvan Yan } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) { 1015*26947304SEvan Yan /* PCI SHPC hotplug */ 1016*26947304SEvan Yan ret = pcishpc_hp_ops(dip, cn_name, op, arg, result); 1017*26947304SEvan Yan } else { 1018*26947304SEvan Yan cmn_err(CE_WARN, "pcie_hp_common_ops: op is not supported." 1019*26947304SEvan Yan " dip=%p cn_name=%s" 1020*26947304SEvan Yan " op=%x arg=%p\n", (void *)dip, cn_name, op, arg); 1021*26947304SEvan Yan ret = DDI_ENOTSUP; 1022*26947304SEvan Yan } 1023*26947304SEvan Yan 1024*26947304SEvan Yan #if defined(__i386) || defined(__amd64) 1025*26947304SEvan Yan /* 1026*26947304SEvan Yan * like in attach, since hotplugging can change error registers, 1027*26947304SEvan Yan * we need to ensure that the proper bits are set on this port 1028*26947304SEvan Yan * after a configure operation 1029*26947304SEvan Yan */ 1030*26947304SEvan Yan if ((ret == DDI_SUCCESS) && (op == DDI_HPOP_CN_CHANGE_STATE) && 1031*26947304SEvan Yan (*(ddi_hp_cn_state_t *)arg == DDI_HP_CN_STATE_ENABLED)) 1032*26947304SEvan Yan pcieb_intel_error_workaround(dip); 1033*26947304SEvan Yan #endif 1034*26947304SEvan Yan 1035*26947304SEvan Yan return (ret); 1036*26947304SEvan Yan } 1037*26947304SEvan Yan 1038*26947304SEvan Yan /* 1039*26947304SEvan Yan * pcie_hp_match_dev_func: 1040*26947304SEvan Yan * Match dip's PCI device number and function number with input ones. 1041*26947304SEvan Yan */ 1042*26947304SEvan Yan static int 1043*26947304SEvan Yan pcie_hp_match_dev_func(dev_info_t *dip, void *hdl) 1044*26947304SEvan Yan { 1045*26947304SEvan Yan struct pcie_hp_find_ctrl *ctrl = (struct pcie_hp_find_ctrl *)hdl; 1046*26947304SEvan Yan pci_regspec_t *pci_rp; 1047*26947304SEvan Yan int length; 1048*26947304SEvan Yan int pci_dev, pci_func; 1049*26947304SEvan Yan 1050*26947304SEvan Yan if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1051*26947304SEvan Yan "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 1052*26947304SEvan Yan ctrl->dip = NULL; 1053*26947304SEvan Yan return (DDI_WALK_TERMINATE); 1054*26947304SEvan Yan } 1055*26947304SEvan Yan 1056*26947304SEvan Yan /* get the PCI device address info */ 1057*26947304SEvan Yan pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 1058*26947304SEvan Yan pci_func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 1059*26947304SEvan Yan 1060*26947304SEvan Yan /* 1061*26947304SEvan Yan * free the memory allocated by ddi_prop_lookup_int_array 1062*26947304SEvan Yan */ 1063*26947304SEvan Yan ddi_prop_free(pci_rp); 1064*26947304SEvan Yan 1065*26947304SEvan Yan if ((pci_dev == ctrl->device) && (pci_func == ctrl->function)) { 1066*26947304SEvan Yan /* found the match for the specified device address */ 1067*26947304SEvan Yan ctrl->dip = dip; 1068*26947304SEvan Yan return (DDI_WALK_TERMINATE); 1069*26947304SEvan Yan } 1070*26947304SEvan Yan 1071*26947304SEvan Yan /* 1072*26947304SEvan Yan * continue the walk to the next sibling to look for a match. 1073*26947304SEvan Yan */ 1074*26947304SEvan Yan return (DDI_WALK_PRUNECHILD); 1075*26947304SEvan Yan } 1076*26947304SEvan Yan 1077*26947304SEvan Yan /* 1078*26947304SEvan Yan * pcie_hp_match_dev: 1079*26947304SEvan Yan * Match the dip's pci device number with the input dev_num 1080*26947304SEvan Yan */ 1081*26947304SEvan Yan static boolean_t 1082*26947304SEvan Yan pcie_hp_match_dev(dev_info_t *dip, int dev_num) 1083*26947304SEvan Yan { 1084*26947304SEvan Yan pci_regspec_t *pci_rp; 1085*26947304SEvan Yan int length; 1086*26947304SEvan Yan int pci_dev; 1087*26947304SEvan Yan 1088*26947304SEvan Yan if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1089*26947304SEvan Yan "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 1090*26947304SEvan Yan return (B_FALSE); 1091*26947304SEvan Yan } 1092*26947304SEvan Yan 1093*26947304SEvan Yan /* get the PCI device address info */ 1094*26947304SEvan Yan pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 1095*26947304SEvan Yan 1096*26947304SEvan Yan /* 1097*26947304SEvan Yan * free the memory allocated by ddi_prop_lookup_int_array 1098*26947304SEvan Yan */ 1099*26947304SEvan Yan ddi_prop_free(pci_rp); 1100*26947304SEvan Yan 1101*26947304SEvan Yan if (pci_dev == dev_num) { 1102*26947304SEvan Yan /* found the match for the specified device address */ 1103*26947304SEvan Yan return (B_TRUE); 1104*26947304SEvan Yan } 1105*26947304SEvan Yan 1106*26947304SEvan Yan return (B_FALSE); 1107*26947304SEvan Yan } 1108*26947304SEvan Yan 1109*26947304SEvan Yan /* 1110*26947304SEvan Yan * Callback function to match with device number in order to list 1111*26947304SEvan Yan * occupants under a specific slot 1112*26947304SEvan Yan */ 1113*26947304SEvan Yan static int 1114*26947304SEvan Yan pcie_hp_list_occupants(dev_info_t *dip, void *arg) 1115*26947304SEvan Yan { 1116*26947304SEvan Yan pcie_hp_cn_cfg_t *cn_cfg_p = (pcie_hp_cn_cfg_t *)arg; 1117*26947304SEvan Yan pcie_hp_occupant_info_t *occupant = 1118*26947304SEvan Yan (pcie_hp_occupant_info_t *)cn_cfg_p->cn_private; 1119*26947304SEvan Yan pcie_hp_slot_t *slot_p = 1120*26947304SEvan Yan (pcie_hp_slot_t *)cn_cfg_p->slotp; 1121*26947304SEvan Yan int pci_dev; 1122*26947304SEvan Yan pci_regspec_t *pci_rp; 1123*26947304SEvan Yan int length; 1124*26947304SEvan Yan major_t major; 1125*26947304SEvan Yan 1126*26947304SEvan Yan /* 1127*26947304SEvan Yan * Get the PCI device number information from the devinfo 1128*26947304SEvan Yan * node. Since the node may not have the address field 1129*26947304SEvan Yan * setup (this is done in the DDI_INITCHILD of the parent) 1130*26947304SEvan Yan * we look up the 'reg' property to decode that information. 1131*26947304SEvan Yan */ 1132*26947304SEvan Yan if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 1133*26947304SEvan Yan DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, 1134*26947304SEvan Yan (uint_t *)&length) != DDI_PROP_SUCCESS) { 1135*26947304SEvan Yan cn_cfg_p->rv = DDI_FAILURE; 1136*26947304SEvan Yan cn_cfg_p->dip = dip; 1137*26947304SEvan Yan return (DDI_WALK_TERMINATE); 1138*26947304SEvan Yan } 1139*26947304SEvan Yan 1140*26947304SEvan Yan /* get the pci device id information */ 1141*26947304SEvan Yan pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 1142*26947304SEvan Yan 1143*26947304SEvan Yan /* 1144*26947304SEvan Yan * free the memory allocated by ddi_prop_lookup_int_array 1145*26947304SEvan Yan */ 1146*26947304SEvan Yan ddi_prop_free(pci_rp); 1147*26947304SEvan Yan 1148*26947304SEvan Yan /* 1149*26947304SEvan Yan * Match the node for the device number of the slot. 1150*26947304SEvan Yan */ 1151*26947304SEvan Yan if (pci_dev == slot_p->hs_device_num) { 1152*26947304SEvan Yan 1153*26947304SEvan Yan major = ddi_driver_major(dip); 1154*26947304SEvan Yan 1155*26947304SEvan Yan /* 1156*26947304SEvan Yan * If the node is not yet attached, then don't list it 1157*26947304SEvan Yan * as an occupant. This is valid, since nothing can be 1158*26947304SEvan Yan * consuming it until it is attached, and cfgadm will 1159*26947304SEvan Yan * ask for the property explicitly which will cause it 1160*26947304SEvan Yan * to be re-freshed right before checking with rcm. 1161*26947304SEvan Yan */ 1162*26947304SEvan Yan if ((major == DDI_MAJOR_T_NONE) || !i_ddi_devi_attached(dip)) 1163*26947304SEvan Yan return (DDI_WALK_PRUNECHILD); 1164*26947304SEvan Yan 1165*26947304SEvan Yan /* 1166*26947304SEvan Yan * If we have used all our occupants then print mesage 1167*26947304SEvan Yan * and terminate walk. 1168*26947304SEvan Yan */ 1169*26947304SEvan Yan if (occupant->i >= PCIE_HP_MAX_OCCUPANTS) { 1170*26947304SEvan Yan cmn_err(CE_WARN, 1171*26947304SEvan Yan "pcie (%s%d): unable to list all occupants", 1172*26947304SEvan Yan ddi_driver_name(ddi_get_parent(dip)), 1173*26947304SEvan Yan ddi_get_instance(ddi_get_parent(dip))); 1174*26947304SEvan Yan return (DDI_WALK_TERMINATE); 1175*26947304SEvan Yan } 1176*26947304SEvan Yan 1177*26947304SEvan Yan /* 1178*26947304SEvan Yan * No need to hold the dip as ddi_walk_devs 1179*26947304SEvan Yan * has already arranged that for us. 1180*26947304SEvan Yan */ 1181*26947304SEvan Yan occupant->id[occupant->i] = 1182*26947304SEvan Yan kmem_alloc(sizeof (char[MAXPATHLEN]), KM_SLEEP); 1183*26947304SEvan Yan (void) ddi_pathname(dip, (char *)occupant->id[occupant->i]); 1184*26947304SEvan Yan occupant->i++; 1185*26947304SEvan Yan } 1186*26947304SEvan Yan 1187*26947304SEvan Yan /* 1188*26947304SEvan Yan * continue the walk to the next sibling to look for a match 1189*26947304SEvan Yan * or to find other nodes if this card is a multi-function card. 1190*26947304SEvan Yan */ 1191*26947304SEvan Yan return (DDI_WALK_PRUNECHILD); 1192*26947304SEvan Yan } 1193*26947304SEvan Yan 1194*26947304SEvan Yan /* 1195*26947304SEvan Yan * Generate the System Event for ESC_DR_REQ. 1196*26947304SEvan Yan * One of the consumers is pcidr, it calls to libcfgadm to perform a 1197*26947304SEvan Yan * configure or unconfigure operation to the AP. 1198*26947304SEvan Yan */ 1199*26947304SEvan Yan void 1200*26947304SEvan Yan pcie_hp_gen_sysevent_req(char *slot_name, int hint, 1201*26947304SEvan Yan dev_info_t *self, int kmflag) 1202*26947304SEvan Yan { 1203*26947304SEvan Yan sysevent_id_t eid; 1204*26947304SEvan Yan nvlist_t *ev_attr_list = NULL; 1205*26947304SEvan Yan char cn_path[MAXPATHLEN]; 1206*26947304SEvan Yan char *ap_id; 1207*26947304SEvan Yan int err, ap_id_len; 1208*26947304SEvan Yan 1209*26947304SEvan Yan /* 1210*26947304SEvan Yan * Minor device name (AP) will be bus path 1211*26947304SEvan Yan * concatenated with slot name 1212*26947304SEvan Yan */ 1213*26947304SEvan Yan (void) strcpy(cn_path, "/devices"); 1214*26947304SEvan Yan (void) ddi_pathname(self, cn_path + strlen("/devices")); 1215*26947304SEvan Yan 1216*26947304SEvan Yan ap_id_len = strlen(cn_path) + strlen(":") + 1217*26947304SEvan Yan strlen(slot_name) + 1; 1218*26947304SEvan Yan ap_id = kmem_zalloc(ap_id_len, kmflag); 1219*26947304SEvan Yan if (ap_id == NULL) { 1220*26947304SEvan Yan cmn_err(CE_WARN, 1221*26947304SEvan Yan "%s%d: Failed to allocate memory for AP ID: %s:%s", 1222*26947304SEvan Yan ddi_driver_name(self), ddi_get_instance(self), 1223*26947304SEvan Yan cn_path, slot_name); 1224*26947304SEvan Yan 1225*26947304SEvan Yan return; 1226*26947304SEvan Yan } 1227*26947304SEvan Yan 1228*26947304SEvan Yan (void) strcpy(ap_id, cn_path); 1229*26947304SEvan Yan (void) strcat(ap_id, ":"); 1230*26947304SEvan Yan (void) strcat(ap_id, slot_name); 1231*26947304SEvan Yan 1232*26947304SEvan Yan err = nvlist_alloc(&ev_attr_list, NV_UNIQUE_NAME_TYPE, kmflag); 1233*26947304SEvan Yan if (err != 0) { 1234*26947304SEvan Yan cmn_err(CE_WARN, 1235*26947304SEvan Yan "%s%d: Failed to allocate memory " 1236*26947304SEvan Yan "for event attributes%s", ddi_driver_name(self), 1237*26947304SEvan Yan ddi_get_instance(self), ESC_DR_REQ); 1238*26947304SEvan Yan 1239*26947304SEvan Yan kmem_free(ap_id, ap_id_len); 1240*26947304SEvan Yan return; 1241*26947304SEvan Yan } 1242*26947304SEvan Yan 1243*26947304SEvan Yan switch (hint) { 1244*26947304SEvan Yan 1245*26947304SEvan Yan case SE_INVESTIGATE_RES: /* fall through */ 1246*26947304SEvan Yan case SE_INCOMING_RES: /* fall through */ 1247*26947304SEvan Yan case SE_OUTGOING_RES: /* fall through */ 1248*26947304SEvan Yan 1249*26947304SEvan Yan err = nvlist_add_string(ev_attr_list, DR_REQ_TYPE, 1250*26947304SEvan Yan SE_REQ2STR(hint)); 1251*26947304SEvan Yan 1252*26947304SEvan Yan if (err != 0) { 1253*26947304SEvan Yan cmn_err(CE_WARN, 1254*26947304SEvan Yan "%s%d: Failed to add attr [%s] " 1255*26947304SEvan Yan "for %s event", ddi_driver_name(self), 1256*26947304SEvan Yan ddi_get_instance(self), 1257*26947304SEvan Yan DR_REQ_TYPE, ESC_DR_REQ); 1258*26947304SEvan Yan 1259*26947304SEvan Yan goto done; 1260*26947304SEvan Yan } 1261*26947304SEvan Yan break; 1262*26947304SEvan Yan 1263*26947304SEvan Yan default: 1264*26947304SEvan Yan cmn_err(CE_WARN, "%s%d: Unknown hint on sysevent", 1265*26947304SEvan Yan ddi_driver_name(self), ddi_get_instance(self)); 1266*26947304SEvan Yan 1267*26947304SEvan Yan goto done; 1268*26947304SEvan Yan } 1269*26947304SEvan Yan 1270*26947304SEvan Yan /* 1271*26947304SEvan Yan * Add attachment point as attribute (common attribute) 1272*26947304SEvan Yan */ 1273*26947304SEvan Yan 1274*26947304SEvan Yan err = nvlist_add_string(ev_attr_list, DR_AP_ID, ap_id); 1275*26947304SEvan Yan 1276*26947304SEvan Yan if (err != 0) { 1277*26947304SEvan Yan cmn_err(CE_WARN, "%s%d: Failed to add attr [%s] for %s event", 1278*26947304SEvan Yan ddi_driver_name(self), ddi_get_instance(self), 1279*26947304SEvan Yan DR_AP_ID, EC_DR); 1280*26947304SEvan Yan 1281*26947304SEvan Yan goto done; 1282*26947304SEvan Yan } 1283*26947304SEvan Yan 1284*26947304SEvan Yan 1285*26947304SEvan Yan /* 1286*26947304SEvan Yan * Log this event with sysevent framework. 1287*26947304SEvan Yan */ 1288*26947304SEvan Yan 1289*26947304SEvan Yan err = ddi_log_sysevent(self, DDI_VENDOR_SUNW, EC_DR, 1290*26947304SEvan Yan ESC_DR_REQ, ev_attr_list, &eid, 1291*26947304SEvan Yan ((kmflag == KM_SLEEP) ? DDI_SLEEP : DDI_NOSLEEP)); 1292*26947304SEvan Yan if (err != 0) { 1293*26947304SEvan Yan cmn_err(CE_WARN, "%s%d: Failed to log %s event", 1294*26947304SEvan Yan ddi_driver_name(self), ddi_get_instance(self), EC_DR); 1295*26947304SEvan Yan } 1296*26947304SEvan Yan 1297*26947304SEvan Yan done: 1298*26947304SEvan Yan nvlist_free(ev_attr_list); 1299*26947304SEvan Yan kmem_free(ap_id, ap_id_len); 1300*26947304SEvan Yan } 1301