126947304SEvan Yan /* 226947304SEvan Yan * CDDL HEADER START 326947304SEvan Yan * 426947304SEvan Yan * The contents of this file are subject to the terms of the 526947304SEvan Yan * Common Development and Distribution License (the "License"). 626947304SEvan Yan * You may not use this file except in compliance with the License. 726947304SEvan Yan * 826947304SEvan Yan * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 926947304SEvan Yan * or http://www.opensolaris.org/os/licensing. 1026947304SEvan Yan * See the License for the specific language governing permissions 1126947304SEvan Yan * and limitations under the License. 1226947304SEvan Yan * 1326947304SEvan Yan * When distributing Covered Code, include this CDDL HEADER in each 1426947304SEvan Yan * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1526947304SEvan Yan * If applicable, add the following below this CDDL HEADER, with the 1626947304SEvan Yan * fields enclosed by brackets "[]" replaced with your own identifying 1726947304SEvan Yan * information: Portions Copyright [yyyy] [name of copyright owner] 1826947304SEvan Yan * 1926947304SEvan Yan * CDDL HEADER END 2026947304SEvan Yan */ 2126947304SEvan Yan 2226947304SEvan Yan /* 2326947304SEvan Yan * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2426947304SEvan Yan * Use is subject to license terms. 25*b3d69c05SRobert Mustacchi * Copyright 2019 Joyent, Inc. 2626947304SEvan Yan */ 2726947304SEvan Yan 2826947304SEvan Yan /* 2926947304SEvan Yan * This file contains the common hotplug code that is used by Standard 3026947304SEvan Yan * PCIe and PCI HotPlug Controller code. 3126947304SEvan Yan * 3226947304SEvan Yan * NOTE: This file is compiled and delivered through misc/pcie module. 3326947304SEvan Yan */ 3426947304SEvan Yan 3526947304SEvan Yan #include <sys/types.h> 3626947304SEvan Yan #include <sys/conf.h> 3726947304SEvan Yan #include <sys/kmem.h> 3826947304SEvan Yan #include <sys/debug.h> 3926947304SEvan Yan #include <sys/vtrace.h> 4026947304SEvan Yan #include <sys/autoconf.h> 4126947304SEvan Yan #include <sys/varargs.h> 4226947304SEvan Yan #include <sys/ddi_impldefs.h> 4326947304SEvan Yan #include <sys/time.h> 4426947304SEvan Yan #include <sys/note.h> 4526947304SEvan Yan #include <sys/callb.h> 4626947304SEvan Yan #include <sys/ddi.h> 4726947304SEvan Yan #include <sys/sunddi.h> 4826947304SEvan Yan #include <sys/sunndi.h> 4926947304SEvan Yan #include <sys/sysevent.h> 5026947304SEvan Yan #include <sys/sysevent/eventdefs.h> 5126947304SEvan Yan #include <sys/sysevent/dr.h> 5226947304SEvan Yan #include <sys/pci_impl.h> 5326947304SEvan Yan #include <sys/pci_cap.h> 5426947304SEvan Yan #include <sys/hotplug/pci/pcicfg.h> 5526947304SEvan Yan #include <sys/hotplug/pci/pcie_hp.h> 5626947304SEvan Yan #include <sys/hotplug/pci/pciehpc.h> 5726947304SEvan Yan #include <sys/hotplug/pci/pcishpc.h> 5826947304SEvan Yan #include <io/pciex/pcieb.h> 5926947304SEvan Yan 6026947304SEvan Yan /* Local functions prototype */ 6126947304SEvan Yan static int pcie_hp_list_occupants(dev_info_t *dip, void *arg); 6226947304SEvan Yan static int pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip, 6326947304SEvan Yan char *cn_name); 6426947304SEvan Yan static int pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num); 6526947304SEvan Yan static int pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg); 6626947304SEvan Yan static int pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg); 6726947304SEvan Yan static int pcie_hp_match_dev_func(dev_info_t *dip, void *hdl); 6826947304SEvan Yan static boolean_t pcie_hp_match_dev(dev_info_t *dip, int dev_num); 6926947304SEvan Yan static int pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num, 7026947304SEvan Yan int *func_num); 7126947304SEvan Yan static int pcie_hp_create_port_name_num(dev_info_t *dip, 7226947304SEvan Yan ddi_hp_cn_info_t *cn_info); 7326947304SEvan Yan static int pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num, 7426947304SEvan Yan int func_num); 7526947304SEvan Yan 7626947304SEvan Yan /* 7726947304SEvan Yan * Global functions (called by other drivers/modules) 7826947304SEvan Yan */ 7926947304SEvan Yan 8026947304SEvan Yan /* 8126947304SEvan Yan * return description text for led state 8226947304SEvan Yan */ 8326947304SEvan Yan char * 8426947304SEvan Yan pcie_led_state_text(pcie_hp_led_state_t state) 8526947304SEvan Yan { 8626947304SEvan Yan switch (state) { 8726947304SEvan Yan case PCIE_HP_LED_ON: 8826947304SEvan Yan return (PCIEHPC_PROP_VALUE_ON); 8926947304SEvan Yan case PCIE_HP_LED_OFF: 9026947304SEvan Yan return (PCIEHPC_PROP_VALUE_OFF); 9126947304SEvan Yan case PCIE_HP_LED_BLINK: 9226947304SEvan Yan default: 9326947304SEvan Yan return (PCIEHPC_PROP_VALUE_BLINK); 9426947304SEvan Yan } 9526947304SEvan Yan } 9626947304SEvan Yan 9726947304SEvan Yan /* 9826947304SEvan Yan * return description text for slot condition 9926947304SEvan Yan */ 10026947304SEvan Yan char * 10126947304SEvan Yan pcie_slot_condition_text(ap_condition_t condition) 10226947304SEvan Yan { 10326947304SEvan Yan switch (condition) { 10426947304SEvan Yan case AP_COND_UNKNOWN: 10526947304SEvan Yan return (PCIEHPC_PROP_VALUE_UNKNOWN); 10626947304SEvan Yan case AP_COND_OK: 10726947304SEvan Yan return (PCIEHPC_PROP_VALUE_OK); 10826947304SEvan Yan case AP_COND_FAILING: 10926947304SEvan Yan return (PCIEHPC_PROP_VALUE_FAILING); 11026947304SEvan Yan case AP_COND_FAILED: 11126947304SEvan Yan return (PCIEHPC_PROP_VALUE_FAILED); 11226947304SEvan Yan case AP_COND_UNUSABLE: 11326947304SEvan Yan return (PCIEHPC_PROP_VALUE_UNUSABLE); 11426947304SEvan Yan default: 11526947304SEvan Yan return (PCIEHPC_PROP_VALUE_UNKNOWN); 11626947304SEvan Yan } 11726947304SEvan Yan } 11826947304SEvan Yan 11926947304SEvan Yan /* 12026947304SEvan Yan * routine to copy in a nvlist from userland 12126947304SEvan Yan */ 12226947304SEvan Yan int 12326947304SEvan Yan pcie_copyin_nvlist(char *packed_buf, size_t packed_sz, nvlist_t **nvlp) 12426947304SEvan Yan { 12526947304SEvan Yan int ret = DDI_SUCCESS; 12626947304SEvan Yan char *packed; 12726947304SEvan Yan nvlist_t *dest = NULL; 12826947304SEvan Yan 12926947304SEvan Yan if (packed_buf == NULL || packed_sz == 0) 13026947304SEvan Yan return (DDI_EINVAL); 13126947304SEvan Yan 13226947304SEvan Yan /* copyin packed nvlist */ 13326947304SEvan Yan if ((packed = kmem_alloc(packed_sz, KM_SLEEP)) == NULL) 13426947304SEvan Yan return (DDI_ENOMEM); 13526947304SEvan Yan 13626947304SEvan Yan if (copyin(packed_buf, packed, packed_sz) != 0) { 13726947304SEvan Yan cmn_err(CE_WARN, "pcie_copyin_nvlist: copyin failed.\n"); 13826947304SEvan Yan ret = DDI_FAILURE; 13926947304SEvan Yan goto copyin_cleanup; 14026947304SEvan Yan } 14126947304SEvan Yan 14226947304SEvan Yan /* unpack packed nvlist */ 14326947304SEvan Yan if ((ret = nvlist_unpack(packed, packed_sz, &dest, KM_SLEEP)) != 0) { 14426947304SEvan Yan cmn_err(CE_WARN, "pcie_copyin_nvlist: nvlist_unpack " 14526947304SEvan Yan "failed with err %d\n", ret); 14626947304SEvan Yan switch (ret) { 14726947304SEvan Yan case EINVAL: 14826947304SEvan Yan case ENOTSUP: 14926947304SEvan Yan ret = DDI_EINVAL; 15026947304SEvan Yan goto copyin_cleanup; 15126947304SEvan Yan case ENOMEM: 15226947304SEvan Yan ret = DDI_ENOMEM; 15326947304SEvan Yan goto copyin_cleanup; 15426947304SEvan Yan default: 15526947304SEvan Yan ret = DDI_FAILURE; 15626947304SEvan Yan goto copyin_cleanup; 15726947304SEvan Yan } 15826947304SEvan Yan } 15926947304SEvan Yan *nvlp = dest; 16026947304SEvan Yan copyin_cleanup: 16126947304SEvan Yan kmem_free(packed, packed_sz); 16226947304SEvan Yan return (ret); 16326947304SEvan Yan } 16426947304SEvan Yan 16526947304SEvan Yan /* 16626947304SEvan Yan * routine to copy out a nvlist to userland 16726947304SEvan Yan */ 16826947304SEvan Yan int 16926947304SEvan Yan pcie_copyout_nvlist(nvlist_t *nvl, char *packed_buf, size_t *buf_sz) 17026947304SEvan Yan { 17126947304SEvan Yan int err = 0; 17226947304SEvan Yan char *buf = NULL; 17326947304SEvan Yan size_t packed_sz; 17426947304SEvan Yan 17526947304SEvan Yan if (nvl == NULL || packed_buf == NULL || buf_sz == NULL) 17626947304SEvan Yan return (DDI_EINVAL); 17726947304SEvan Yan 17826947304SEvan Yan /* pack nvlist, the library will allocate memory */ 17926947304SEvan Yan if ((err = nvlist_pack(nvl, &buf, &packed_sz, NV_ENCODE_NATIVE, 0)) 18026947304SEvan Yan != 0) { 18126947304SEvan Yan cmn_err(CE_WARN, "pcie_copyout_nvlist: nvlist_pack " 18226947304SEvan Yan "failed with err %d\n", err); 18326947304SEvan Yan switch (err) { 18426947304SEvan Yan case EINVAL: 18526947304SEvan Yan case ENOTSUP: 18626947304SEvan Yan return (DDI_EINVAL); 18726947304SEvan Yan case ENOMEM: 18826947304SEvan Yan return (DDI_ENOMEM); 18926947304SEvan Yan default: 19026947304SEvan Yan return (DDI_FAILURE); 19126947304SEvan Yan } 19226947304SEvan Yan } 19326947304SEvan Yan if (packed_sz > *buf_sz) { 19426947304SEvan Yan return (DDI_EINVAL); 19526947304SEvan Yan } 19626947304SEvan Yan 19726947304SEvan Yan /* copyout packed nvlist */ 19826947304SEvan Yan if (copyout(buf, packed_buf, packed_sz) != 0) { 19926947304SEvan Yan cmn_err(CE_WARN, "pcie_copyout_nvlist: copyout " "failed.\n"); 20026947304SEvan Yan kmem_free(buf, packed_sz); 20126947304SEvan Yan return (DDI_FAILURE); 20226947304SEvan Yan } 20326947304SEvan Yan 20426947304SEvan Yan *buf_sz = packed_sz; 20526947304SEvan Yan kmem_free(buf, packed_sz); 20626947304SEvan Yan return (DDI_SUCCESS); 20726947304SEvan Yan } 20826947304SEvan Yan 20926947304SEvan Yan /* 21026947304SEvan Yan * init bus_hp_op entry and init hotpluggable slots & virtual ports 21126947304SEvan Yan */ 21226947304SEvan Yan int 21326947304SEvan Yan pcie_hp_init(dev_info_t *dip, caddr_t arg) 21426947304SEvan Yan { 21526947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 21626947304SEvan Yan int ret = DDI_SUCCESS, count; 21726947304SEvan Yan dev_info_t *cdip; 21826947304SEvan Yan 21926947304SEvan Yan if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) { 22026947304SEvan Yan /* Init hotplug controller */ 22126947304SEvan Yan ret = pciehpc_init(dip, arg); 22226947304SEvan Yan } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) { 22326947304SEvan Yan ret = pcishpc_init(dip); 22426947304SEvan Yan } 22526947304SEvan Yan 22626947304SEvan Yan if (ret != DDI_SUCCESS) { 227ed11b501SColin Zou - Sun Microsystems - Beijing China PCIE_DBG("pcie_hp_init: initialize hotplug " 22826947304SEvan Yan "controller failed with %d\n", ret); 22926947304SEvan Yan return (ret); 23026947304SEvan Yan } 23126947304SEvan Yan 23226947304SEvan Yan ndi_devi_enter(dip, &count); 23326947304SEvan Yan 23426947304SEvan Yan /* Create port for the first level children */ 23526947304SEvan Yan cdip = ddi_get_child(dip); 23626947304SEvan Yan while (cdip != NULL) { 23726947304SEvan Yan if ((ret = pcie_hp_register_port(cdip, dip, NULL)) 23826947304SEvan Yan != DDI_SUCCESS) { 23926947304SEvan Yan /* stop and cleanup */ 24026947304SEvan Yan break; 24126947304SEvan Yan } 24226947304SEvan Yan cdip = ddi_get_next_sibling(cdip); 24326947304SEvan Yan } 24426947304SEvan Yan ndi_devi_exit(dip, count); 24526947304SEvan Yan if (ret != DDI_SUCCESS) { 24626947304SEvan Yan cmn_err(CE_WARN, "pcie_hp_init: initialize virtual " 24726947304SEvan Yan "hotplug port failed with %d\n", ret); 24826947304SEvan Yan (void) pcie_hp_uninit(dip); 24926947304SEvan Yan 25026947304SEvan Yan return (ret); 25126947304SEvan Yan } 25226947304SEvan Yan 25326947304SEvan Yan return (DDI_SUCCESS); 25426947304SEvan Yan } 25526947304SEvan Yan 25626947304SEvan Yan /* 25726947304SEvan Yan * uninit the hotpluggable slots and virtual ports 25826947304SEvan Yan */ 25926947304SEvan Yan int 26026947304SEvan Yan pcie_hp_uninit(dev_info_t *dip) 26126947304SEvan Yan { 26226947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 26326947304SEvan Yan pcie_hp_unreg_port_t arg; 26426947304SEvan Yan 26526947304SEvan Yan /* 26626947304SEvan Yan * Must set arg.rv to NDI_SUCCESS so that if there's no port 26726947304SEvan Yan * under this dip, we still return success thus the bridge 26826947304SEvan Yan * driver can be successfully detached. 26926947304SEvan Yan * 27026947304SEvan Yan * Note that during the probe PCI configurator calls 27126947304SEvan Yan * ndi_devi_offline() to detach driver for a new probed bridge, 27226947304SEvan Yan * so that it can reprogram the resources for the bridge, 27326947304SEvan Yan * ndi_devi_offline() calls into pcieb_detach() which in turn 27426947304SEvan Yan * calls into this function. In this case there are no ports 27526947304SEvan Yan * created under a new probe bridge dip, as ports are only 27626947304SEvan Yan * created after the configurator finishing probing, thus the 27726947304SEvan Yan * ndi_hp_walk_cn() will see no ports when this is called 27826947304SEvan Yan * from the PCI configurtor. 27926947304SEvan Yan */ 28026947304SEvan Yan arg.nexus_dip = dip; 28126947304SEvan Yan arg.connector_num = DDI_HP_CN_NUM_NONE; 28226947304SEvan Yan arg.rv = NDI_SUCCESS; 28326947304SEvan Yan 28426947304SEvan Yan /* tear down all virtual hotplug handles */ 28526947304SEvan Yan ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg); 28626947304SEvan Yan 28726947304SEvan Yan if (arg.rv != NDI_SUCCESS) 28826947304SEvan Yan return (DDI_FAILURE); 28926947304SEvan Yan 29026947304SEvan Yan if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) 29126947304SEvan Yan (void) pciehpc_uninit(dip); 29226947304SEvan Yan else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) 29326947304SEvan Yan (void) pcishpc_uninit(dip); 29426947304SEvan Yan 29526947304SEvan Yan return (DDI_SUCCESS); 29626947304SEvan Yan } 29726947304SEvan Yan 29826947304SEvan Yan /* 29926947304SEvan Yan * interrupt handler 30026947304SEvan Yan */ 30126947304SEvan Yan int 30226947304SEvan Yan pcie_hp_intr(dev_info_t *dip) 30326947304SEvan Yan { 30426947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 30526947304SEvan Yan int ret = DDI_INTR_UNCLAIMED; 30626947304SEvan Yan 30726947304SEvan Yan if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) 30826947304SEvan Yan ret = pciehpc_intr(dip); 30926947304SEvan Yan else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) 31026947304SEvan Yan ret = pcishpc_intr(dip); 31126947304SEvan Yan 31226947304SEvan Yan return (ret); 31326947304SEvan Yan } 31426947304SEvan Yan 31526947304SEvan Yan /* 31626947304SEvan Yan * Probe the given PCIe/PCI Hotplug Connection (CN). 31726947304SEvan Yan */ 31826947304SEvan Yan /*ARGSUSED*/ 31926947304SEvan Yan int 32026947304SEvan Yan pcie_hp_probe(pcie_hp_slot_t *slot_p) 32126947304SEvan Yan { 32226947304SEvan Yan pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 32326947304SEvan Yan dev_info_t *dip = ctrl_p->hc_dip; 32426947304SEvan Yan 32526947304SEvan Yan /* 32626947304SEvan Yan * Call the configurator to probe a given PCI hotplug 32726947304SEvan Yan * Hotplug Connection (CN). 32826947304SEvan Yan */ 32926947304SEvan Yan if (pcicfg_configure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0) 33026947304SEvan Yan != PCICFG_SUCCESS) { 33126947304SEvan Yan PCIE_DBG("pcie_hp_probe() failed\n"); 33226947304SEvan Yan return (DDI_FAILURE); 33326947304SEvan Yan } 33426947304SEvan Yan slot_p->hs_condition = AP_COND_OK; 33526947304SEvan Yan pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip), 33626947304SEvan Yan slot_p->hs_minor), slot_p->hs_device_num); 33726947304SEvan Yan 33826947304SEvan Yan /* 33926947304SEvan Yan * Create ports for the newly probed devices. 34026947304SEvan Yan * Note, this is only for the first level children because the 34126947304SEvan Yan * descendants' ports will be created during bridge driver attach. 34226947304SEvan Yan */ 34326947304SEvan Yan return (pcie_hp_register_ports_for_dev(dip, slot_p->hs_device_num)); 34426947304SEvan Yan } 34526947304SEvan Yan 34626947304SEvan Yan /* 34726947304SEvan Yan * Unprobe the given PCIe/PCI Hotplug Connection (CN): 34826947304SEvan Yan * 1. remove all child device nodes 34926947304SEvan Yan * 2. unregister all dependent ports 35026947304SEvan Yan */ 35126947304SEvan Yan /*ARGSUSED*/ 35226947304SEvan Yan int 35326947304SEvan Yan pcie_hp_unprobe(pcie_hp_slot_t *slot_p) 35426947304SEvan Yan { 35526947304SEvan Yan pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl; 35626947304SEvan Yan dev_info_t *dip = ctrl_p->hc_dip; 35726947304SEvan Yan pcie_hp_unreg_port_t arg; 35826947304SEvan Yan 35926947304SEvan Yan /* 36026947304SEvan Yan * Call the configurator to unprobe a given PCI hotplug 36126947304SEvan Yan * Hotplug Connection (CN). 36226947304SEvan Yan */ 36326947304SEvan Yan if (pcicfg_unconfigure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0) 36426947304SEvan Yan != PCICFG_SUCCESS) { 36526947304SEvan Yan PCIE_DBG("pcie_hp_unprobe() failed\n"); 36626947304SEvan Yan return (DDI_FAILURE); 36726947304SEvan Yan } 36826947304SEvan Yan slot_p->hs_condition = AP_COND_UNKNOWN; 36926947304SEvan Yan pcie_hp_delete_occupant_props(dip, makedevice(ddi_driver_major(dip), 37026947304SEvan Yan slot_p->hs_minor)); 37126947304SEvan Yan 37226947304SEvan Yan /* 37326947304SEvan Yan * Remove ports for the unprobed devices. 37426947304SEvan Yan * Note, this is only for the first level children because the 37526947304SEvan Yan * descendants' ports were already removed during bridge driver dettach. 37626947304SEvan Yan */ 37726947304SEvan Yan arg.nexus_dip = dip; 37826947304SEvan Yan arg.connector_num = slot_p->hs_info.cn_num; 37926947304SEvan Yan arg.rv = NDI_SUCCESS; 38026947304SEvan Yan ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg); 38126947304SEvan Yan 38226947304SEvan Yan return (arg.rv == NDI_SUCCESS) ? (DDI_SUCCESS) : (DDI_FAILURE); 38326947304SEvan Yan } 38426947304SEvan Yan 38526947304SEvan Yan /* Read-only probe: no hardware register programming. */ 38626947304SEvan Yan int 38726947304SEvan Yan pcie_read_only_probe(dev_info_t *dip, char *cn_name, dev_info_t **pcdip) 38826947304SEvan Yan { 38926947304SEvan Yan long dev, func; 39026947304SEvan Yan int ret; 39126947304SEvan Yan char *sp; 39226947304SEvan Yan dev_info_t *cdip; 39326947304SEvan Yan 39426947304SEvan Yan *pcdip = NULL; 39526947304SEvan Yan /* 39626947304SEvan Yan * Parse the string of a pci Port name and get the device number 39726947304SEvan Yan * and function number. 39826947304SEvan Yan */ 39926947304SEvan Yan if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0) 40026947304SEvan Yan return (DDI_EINVAL); 40126947304SEvan Yan if (ddi_strtol(sp + 1, NULL, 10, &func) != 0) 40226947304SEvan Yan return (DDI_EINVAL); 40326947304SEvan Yan 40426947304SEvan Yan ret = pcicfg_configure(dip, (int)dev, (int)func, 40526947304SEvan Yan PCICFG_FLAG_READ_ONLY); 40626947304SEvan Yan if (ret == PCICFG_SUCCESS) { 40726947304SEvan Yan cdip = pcie_hp_devi_find(dip, (int)dev, (int)func); 40826947304SEvan Yan *pcdip = cdip; 40926947304SEvan Yan } 41026947304SEvan Yan return (ret); 41126947304SEvan Yan } 41226947304SEvan Yan 41326947304SEvan Yan /* Read-only unprobe: no hardware register programming. */ 41426947304SEvan Yan int 41526947304SEvan Yan pcie_read_only_unprobe(dev_info_t *dip, char *cn_name) 41626947304SEvan Yan { 41726947304SEvan Yan long dev, func; 41826947304SEvan Yan int ret; 41926947304SEvan Yan char *sp; 42026947304SEvan Yan 42126947304SEvan Yan /* 42226947304SEvan Yan * Parse the string of a pci Port name and get the device number 42326947304SEvan Yan * and function number. 42426947304SEvan Yan */ 42526947304SEvan Yan if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0) 42626947304SEvan Yan return (DDI_EINVAL); 42726947304SEvan Yan if (ddi_strtol(sp + 1, NULL, 10, &func) != 0) 42826947304SEvan Yan return (DDI_EINVAL); 42926947304SEvan Yan 43026947304SEvan Yan ret = pcicfg_unconfigure(dip, (int)dev, (int)func, 43126947304SEvan Yan PCICFG_FLAG_READ_ONLY); 43226947304SEvan Yan 43326947304SEvan Yan return (ret); 43426947304SEvan Yan } 43526947304SEvan Yan 43626947304SEvan Yan /* Control structure used to find a device in the devinfo tree */ 43726947304SEvan Yan struct pcie_hp_find_ctrl { 43826947304SEvan Yan uint_t device; 43926947304SEvan Yan uint_t function; 44026947304SEvan Yan dev_info_t *dip; 44126947304SEvan Yan }; 44226947304SEvan Yan 44326947304SEvan Yan /* 44426947304SEvan Yan * find a devinfo node with specified device and function number 44526947304SEvan Yan * in the device tree under 'dip' 44626947304SEvan Yan */ 44726947304SEvan Yan dev_info_t * 44826947304SEvan Yan pcie_hp_devi_find(dev_info_t *dip, uint_t device, uint_t function) 44926947304SEvan Yan { 45026947304SEvan Yan struct pcie_hp_find_ctrl ctrl; 45126947304SEvan Yan int count; 45226947304SEvan Yan 45326947304SEvan Yan ctrl.device = device; 45426947304SEvan Yan ctrl.function = function; 45526947304SEvan Yan ctrl.dip = NULL; 45626947304SEvan Yan 45726947304SEvan Yan ndi_devi_enter(dip, &count); 45826947304SEvan Yan ddi_walk_devs(ddi_get_child(dip), pcie_hp_match_dev_func, 45926947304SEvan Yan (void *)&ctrl); 46026947304SEvan Yan ndi_devi_exit(dip, count); 46126947304SEvan Yan 46226947304SEvan Yan return (ctrl.dip); 46326947304SEvan Yan } 46426947304SEvan Yan 46526947304SEvan Yan /* 46626947304SEvan Yan * routine to create 'pci-occupant' property for a hotplug slot 46726947304SEvan Yan */ 46826947304SEvan Yan void 46926947304SEvan Yan pcie_hp_create_occupant_props(dev_info_t *dip, dev_t dev, int pci_dev) 47026947304SEvan Yan { 47126947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 47226947304SEvan Yan pcie_hp_ctrl_t *ctrl_p = (pcie_hp_ctrl_t *)bus_p->bus_hp_ctrl; 473*b3d69c05SRobert Mustacchi pcie_hp_slot_t *slotp = NULL; 47426947304SEvan Yan pcie_hp_cn_cfg_t cn_cfg; 47526947304SEvan Yan pcie_hp_occupant_info_t *occupant; 47626947304SEvan Yan int circular, i; 47726947304SEvan Yan 47826947304SEvan Yan ndi_devi_enter(dip, &circular); 47926947304SEvan Yan 48026947304SEvan Yan if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) { 48126947304SEvan Yan slotp = (ctrl_p && (pci_dev == 0)) ? 48226947304SEvan Yan ctrl_p->hc_slots[pci_dev] : NULL; 48326947304SEvan Yan } else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) { 48426947304SEvan Yan if (ctrl_p) { 48526947304SEvan Yan int slot_num; 48626947304SEvan Yan 48726947304SEvan Yan slot_num = (ctrl_p->hc_device_increases) ? 48826947304SEvan Yan (pci_dev - ctrl_p->hc_device_start) : 48926947304SEvan Yan (pci_dev + ctrl_p->hc_device_start); 49026947304SEvan Yan 49126947304SEvan Yan slotp = ctrl_p->hc_slots[slot_num]; 49226947304SEvan Yan } else { 49326947304SEvan Yan slotp = NULL; 49426947304SEvan Yan } 49526947304SEvan Yan } 49626947304SEvan Yan 49726947304SEvan Yan if (slotp == NULL) 49826947304SEvan Yan return; 49926947304SEvan Yan 50026947304SEvan Yan occupant = kmem_alloc(sizeof (pcie_hp_occupant_info_t), KM_SLEEP); 50126947304SEvan Yan occupant->i = 0; 50226947304SEvan Yan 50326947304SEvan Yan cn_cfg.flag = B_FALSE; 50426947304SEvan Yan cn_cfg.rv = NDI_SUCCESS; 50526947304SEvan Yan cn_cfg.dip = NULL; 50626947304SEvan Yan cn_cfg.slotp = (void *)slotp; 50726947304SEvan Yan cn_cfg.cn_private = (void *)occupant; 50826947304SEvan Yan 50926947304SEvan Yan ddi_walk_devs(ddi_get_child(dip), pcie_hp_list_occupants, 51026947304SEvan Yan (void *)&cn_cfg); 51126947304SEvan Yan 51226947304SEvan Yan if (occupant->i == 0) { 51326947304SEvan Yan /* no occupants right now, need to create stub property */ 51426947304SEvan Yan char *c[] = { "" }; 51526947304SEvan Yan (void) ddi_prop_update_string_array(dev, dip, "pci-occupant", 51626947304SEvan Yan c, 1); 51726947304SEvan Yan } else { 51826947304SEvan Yan (void) ddi_prop_update_string_array(dev, dip, "pci-occupant", 51926947304SEvan Yan occupant->id, occupant->i); 52026947304SEvan Yan } 52126947304SEvan Yan 52226947304SEvan Yan for (i = 0; i < occupant->i; i++) 52326947304SEvan Yan kmem_free(occupant->id[i], sizeof (char[MAXPATHLEN])); 52426947304SEvan Yan 52526947304SEvan Yan kmem_free(occupant, sizeof (pcie_hp_occupant_info_t)); 52626947304SEvan Yan 52726947304SEvan Yan ndi_devi_exit(dip, circular); 52826947304SEvan Yan } 52926947304SEvan Yan 53026947304SEvan Yan /* 53126947304SEvan Yan * routine to remove 'pci-occupant' property for a hotplug slot 53226947304SEvan Yan */ 53326947304SEvan Yan void 53426947304SEvan Yan pcie_hp_delete_occupant_props(dev_info_t *dip, dev_t dev) 53526947304SEvan Yan { 53626947304SEvan Yan (void) ddi_prop_remove(dev, dip, "pci-occupant"); 53726947304SEvan Yan } 53826947304SEvan Yan 53926947304SEvan Yan /* 54026947304SEvan Yan * general code to create a minor node, called from hotplug controller 54126947304SEvan Yan * drivers. 54226947304SEvan Yan */ 54326947304SEvan Yan int 54426947304SEvan Yan pcie_create_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot) 54526947304SEvan Yan { 54626947304SEvan Yan dev_info_t *dip = ctrl_p->hc_dip; 54726947304SEvan Yan pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[slot]; 54826947304SEvan Yan ddi_hp_cn_info_t *info_p = &slot_p->hs_info; 54926947304SEvan Yan 55026947304SEvan Yan if (ddi_create_minor_node(dip, info_p->cn_name, 55126947304SEvan Yan S_IFCHR, slot_p->hs_minor, 55226947304SEvan Yan DDI_NT_PCI_ATTACHMENT_POINT, 0) != DDI_SUCCESS) { 55326947304SEvan Yan return (DDI_FAILURE); 55426947304SEvan Yan } 55526947304SEvan Yan 55626947304SEvan Yan (void) ddi_prop_update_int(DDI_DEV_T_NONE, 55726947304SEvan Yan dip, "ap-names", 1 << slot_p->hs_device_num); 55826947304SEvan Yan 55926947304SEvan Yan return (DDI_SUCCESS); 56026947304SEvan Yan } 56126947304SEvan Yan 56226947304SEvan Yan /* 56326947304SEvan Yan * general code to remove a minor node, called from hotplug controller 56426947304SEvan Yan * drivers. 56526947304SEvan Yan */ 56626947304SEvan Yan void 56726947304SEvan Yan pcie_remove_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot) 56826947304SEvan Yan { 56926947304SEvan Yan ddi_remove_minor_node(ctrl_p->hc_dip, 57026947304SEvan Yan ctrl_p->hc_slots[slot]->hs_info.cn_name); 57126947304SEvan Yan } 57226947304SEvan Yan 57326947304SEvan Yan /* 57426947304SEvan Yan * Local functions (called within this file) 57526947304SEvan Yan */ 57626947304SEvan Yan 57726947304SEvan Yan /* 57826947304SEvan Yan * Register ports for all the children with device number device_num 57926947304SEvan Yan */ 58026947304SEvan Yan static int 58126947304SEvan Yan pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num) 58226947304SEvan Yan { 58326947304SEvan Yan dev_info_t *cdip; 58426947304SEvan Yan int rv; 58526947304SEvan Yan 58626947304SEvan Yan for (cdip = ddi_get_child(dip); cdip; 58726947304SEvan Yan cdip = ddi_get_next_sibling(cdip)) { 58826947304SEvan Yan if (pcie_hp_match_dev(cdip, device_num)) { 58926947304SEvan Yan /* 59026947304SEvan Yan * Found the newly probed device under the 59126947304SEvan Yan * current slot. Register a port for it. 59226947304SEvan Yan */ 59326947304SEvan Yan if ((rv = pcie_hp_register_port(cdip, dip, NULL)) 59426947304SEvan Yan != DDI_SUCCESS) 59526947304SEvan Yan return (rv); 59626947304SEvan Yan } else { 59726947304SEvan Yan continue; 59826947304SEvan Yan } 59926947304SEvan Yan } 60026947304SEvan Yan 60126947304SEvan Yan return (DDI_SUCCESS); 60226947304SEvan Yan } 60326947304SEvan Yan 60426947304SEvan Yan /* 60526947304SEvan Yan * Unregister ports of a pci bridge dip, get called from ndi_hp_walk_cn() 60626947304SEvan Yan * 60726947304SEvan Yan * If connector_num is specified, then unregister the slot's dependent ports 60826947304SEvan Yan * only; Otherwise, unregister all ports of a pci bridge dip. 60926947304SEvan Yan */ 61026947304SEvan Yan static int 61126947304SEvan Yan pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg) 61226947304SEvan Yan { 61326947304SEvan Yan pcie_hp_unreg_port_t *unreg_arg = (pcie_hp_unreg_port_t *)arg; 61426947304SEvan Yan dev_info_t *dip = unreg_arg->nexus_dip; 61526947304SEvan Yan int rv = NDI_SUCCESS; 61626947304SEvan Yan 61726947304SEvan Yan if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT) { 61826947304SEvan Yan unreg_arg->rv = rv; 61926947304SEvan Yan return (DDI_WALK_CONTINUE); 62026947304SEvan Yan } 62126947304SEvan Yan 62226947304SEvan Yan if (unreg_arg->connector_num != DDI_HP_CN_NUM_NONE) { 62326947304SEvan Yan /* Unregister ports for all unprobed devices under a slot. */ 62426947304SEvan Yan if (unreg_arg->connector_num == info->cn_num_dpd_on) { 62526947304SEvan Yan 62626947304SEvan Yan rv = ndi_hp_unregister(dip, info->cn_name); 62726947304SEvan Yan } 62826947304SEvan Yan } else { 62926947304SEvan Yan 63026947304SEvan Yan /* Unregister all ports of a pci bridge dip. */ 63126947304SEvan Yan rv = ndi_hp_unregister(dip, info->cn_name); 63226947304SEvan Yan } 63326947304SEvan Yan 63426947304SEvan Yan unreg_arg->rv = rv; 63526947304SEvan Yan if (rv == NDI_SUCCESS) 63626947304SEvan Yan return (DDI_WALK_CONTINUE); 63726947304SEvan Yan else 63826947304SEvan Yan return (DDI_WALK_TERMINATE); 63926947304SEvan Yan } 64026947304SEvan Yan 64126947304SEvan Yan /* 64226947304SEvan Yan * Find a port according to cn_name and get the port's state. 64326947304SEvan Yan */ 64426947304SEvan Yan static int 64526947304SEvan Yan pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg) 64626947304SEvan Yan { 64726947304SEvan Yan pcie_hp_port_state_t *port = (pcie_hp_port_state_t *)arg; 64826947304SEvan Yan 64926947304SEvan Yan if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT) 65026947304SEvan Yan return (DDI_WALK_CONTINUE); 65126947304SEvan Yan 65226947304SEvan Yan if (strcmp(info->cn_name, port->cn_name) == 0) { 65326947304SEvan Yan /* Matched. */ 65426947304SEvan Yan port->cn_state = info->cn_state; 65526947304SEvan Yan port->rv = DDI_SUCCESS; 65626947304SEvan Yan 65726947304SEvan Yan return (DDI_WALK_TERMINATE); 65826947304SEvan Yan } 65926947304SEvan Yan 66026947304SEvan Yan return (DDI_WALK_CONTINUE); 66126947304SEvan Yan } 66226947304SEvan Yan 66326947304SEvan Yan /* 66426947304SEvan Yan * Find the physical slot with the given device number; 66526947304SEvan Yan * return the slot if found. 66626947304SEvan Yan */ 66726947304SEvan Yan static pcie_hp_slot_t * 66826947304SEvan Yan pcie_find_physical_slot(dev_info_t *dip, int dev_num) 66926947304SEvan Yan { 67026947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 67126947304SEvan Yan pcie_hp_ctrl_t *ctrl = PCIE_GET_HP_CTRL(dip); 67226947304SEvan Yan 67326947304SEvan Yan if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) { 67426947304SEvan Yan /* PCIe has only one slot */ 67526947304SEvan Yan return (dev_num == 0) ? (ctrl->hc_slots[0]) : (NULL); 67626947304SEvan Yan } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) { 67726947304SEvan Yan for (int slot = 0; slot < ctrl->hc_num_slots_impl; slot++) { 67826947304SEvan Yan if (ctrl->hc_slots[slot]->hs_device_num == dev_num) { 67926947304SEvan Yan /* found */ 68026947304SEvan Yan return (ctrl->hc_slots[slot]); 68126947304SEvan Yan } 68226947304SEvan Yan } 68326947304SEvan Yan } 68426947304SEvan Yan 68526947304SEvan Yan return (NULL); 68626947304SEvan Yan } 68726947304SEvan Yan 68826947304SEvan Yan /* 68926947304SEvan Yan * setup slot name/slot-number info for the port which is being registered. 69026947304SEvan Yan */ 69126947304SEvan Yan static int 69226947304SEvan Yan pcie_hp_create_port_name_num(dev_info_t *dip, ddi_hp_cn_info_t *cn_info) 69326947304SEvan Yan { 69426947304SEvan Yan int ret, dev_num, func_num, name_len; 69526947304SEvan Yan dev_info_t *pdip = ddi_get_parent(dip); 69626947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(pdip); 69726947304SEvan Yan pcie_hp_slot_t *slot; 69826947304SEvan Yan pcie_req_id_t bdf; 69926947304SEvan Yan char tmp[PCIE_HP_DEV_FUNC_NUM_STRING_LEN]; 70026947304SEvan Yan 70126947304SEvan Yan ret = pcie_get_bdf_from_dip(dip, &bdf); 70226947304SEvan Yan if (ret != DDI_SUCCESS) { 70326947304SEvan Yan return (ret); 70426947304SEvan Yan } 70526947304SEvan Yan if (PCIE_IS_RP(bus_p) || PCIE_IS_SWD(bus_p) || 70626947304SEvan Yan PCIE_IS_PCI2PCIE(bus_p)) { 70726947304SEvan Yan /* 70826947304SEvan Yan * It is under a PCIe device, devcie number is always 0; 70926947304SEvan Yan * function number might > 8 in ARI supported case. 71026947304SEvan Yan */ 71126947304SEvan Yan dev_num = 0; 71226947304SEvan Yan func_num = (bdf & ((~PCI_REG_BUS_M) >> 8)); 71326947304SEvan Yan } else { 71426947304SEvan Yan dev_num = (bdf & (PCI_REG_DEV_M >> 8)) >> 3; 71526947304SEvan Yan func_num = bdf & (PCI_REG_FUNC_M >> 8); 71626947304SEvan Yan } 71726947304SEvan Yan /* 71826947304SEvan Yan * The string length of dev_num and func_num must be no longer than 4 71926947304SEvan Yan * including the string end mark. (With ARI case considered, e.g., 72026947304SEvan Yan * dev_num=0x0, func_num=0xff.) 72126947304SEvan Yan */ 72226947304SEvan Yan (void) snprintf(tmp, PCIE_HP_DEV_FUNC_NUM_STRING_LEN, "%x%x", 72326947304SEvan Yan dev_num, func_num); 72426947304SEvan Yan /* 72526947304SEvan Yan * Calculate the length of cn_name. 72626947304SEvan Yan * The format of pci port name is: pci.d,f 72726947304SEvan Yan * d stands for dev_num, f stands for func_num. So the length of the 72826947304SEvan Yan * name string can be calculated as following. 72926947304SEvan Yan */ 73026947304SEvan Yan name_len = strlen(tmp) + PCIE_HP_PORT_NAME_STRING_LEN + 1; 73126947304SEvan Yan 73226947304SEvan Yan cn_info->cn_name = (char *)kmem_zalloc(name_len, KM_SLEEP); 73326947304SEvan Yan (void) snprintf(cn_info->cn_name, name_len, "pci.%x,%x", 73426947304SEvan Yan dev_num, func_num); 73526947304SEvan Yan cn_info->cn_num = (dev_num << 8) | func_num; 73626947304SEvan Yan slot = pcie_find_physical_slot(pdip, dev_num); 73726947304SEvan Yan 73826947304SEvan Yan cn_info->cn_num_dpd_on = slot ? 73926947304SEvan Yan slot->hs_info.cn_num : DDI_HP_CN_NUM_NONE; 74026947304SEvan Yan 74126947304SEvan Yan return (DDI_SUCCESS); 74226947304SEvan Yan } 74326947304SEvan Yan 74426947304SEvan Yan /* 74526947304SEvan Yan * Extract device and function number from port name, whose format is 74626947304SEvan Yan * something like 'pci.1,0' 74726947304SEvan Yan */ 74826947304SEvan Yan static int 74926947304SEvan Yan pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num, int *func_num) 75026947304SEvan Yan { 75126947304SEvan Yan int name_len, ret; 75226947304SEvan Yan long d, f; 75326947304SEvan Yan char *sp; 75426947304SEvan Yan 75526947304SEvan Yan /* some checks for the input name */ 75626947304SEvan Yan name_len = strlen(cn_name); 75726947304SEvan Yan if ((name_len <= PCIE_HP_PORT_NAME_STRING_LEN) || 75826947304SEvan Yan (name_len > (PCIE_HP_PORT_NAME_STRING_LEN + 75926947304SEvan Yan PCIE_HP_DEV_FUNC_NUM_STRING_LEN - 1)) || 76026947304SEvan Yan (strncmp("pci.", cn_name, 4) != 0)) { 76126947304SEvan Yan return (DDI_EINVAL); 76226947304SEvan Yan } 76326947304SEvan Yan ret = ddi_strtol(cn_name + 4, &sp, 10, &d); 76426947304SEvan Yan if (ret != DDI_SUCCESS) 76526947304SEvan Yan return (ret); 76626947304SEvan Yan 76726947304SEvan Yan if (strncmp(",", sp, 1) != 0) 76826947304SEvan Yan return (DDI_EINVAL); 76926947304SEvan Yan 77026947304SEvan Yan ret = ddi_strtol(sp + 1, NULL, 10, &f); 77126947304SEvan Yan if (ret != DDI_SUCCESS) 77226947304SEvan Yan return (ret); 77326947304SEvan Yan *dev_num = (int)d; 77426947304SEvan Yan *func_num = (int)f; 77526947304SEvan Yan 77626947304SEvan Yan return (ret); 77726947304SEvan Yan } 77826947304SEvan Yan 77926947304SEvan Yan /* 78026947304SEvan Yan * Check/copy cn_name and set connection numbers. 78126947304SEvan Yan * If it is a valid name, then setup cn_info for the newly created port. 78226947304SEvan Yan */ 78326947304SEvan Yan static int 78426947304SEvan Yan pcie_hp_setup_port_name_num(dev_info_t *pdip, char *cn_name, 78526947304SEvan Yan ddi_hp_cn_info_t *cn_info) 78626947304SEvan Yan { 78726947304SEvan Yan int dev_num, func_num, ret; 78826947304SEvan Yan pcie_hp_slot_t *slot; 78926947304SEvan Yan 79026947304SEvan Yan if ((ret = pcie_hp_get_df_from_port_name(cn_name, &dev_num, &func_num)) 79126947304SEvan Yan != DDI_SUCCESS) 79226947304SEvan Yan return (ret); 79326947304SEvan Yan 79426947304SEvan Yan if (pcie_hp_check_hardware_existence(pdip, dev_num, func_num) == 79526947304SEvan Yan DDI_SUCCESS) { 79626947304SEvan Yan cn_info->cn_state = DDI_HP_CN_STATE_PRESENT; 79726947304SEvan Yan } else { 79826947304SEvan Yan cn_info->cn_state = DDI_HP_CN_STATE_EMPTY; 79926947304SEvan Yan } 80026947304SEvan Yan 80126947304SEvan Yan cn_info->cn_name = ddi_strdup(cn_name, KM_SLEEP); 80226947304SEvan Yan cn_info->cn_num = (dev_num << 8) | func_num; 80326947304SEvan Yan 80426947304SEvan Yan slot = pcie_find_physical_slot(pdip, dev_num); 80526947304SEvan Yan if (slot) { 80626947304SEvan Yan cn_info->cn_num_dpd_on = slot->hs_info.cn_num; 80726947304SEvan Yan } else { 80826947304SEvan Yan cn_info->cn_num_dpd_on = DDI_HP_CN_NUM_NONE; 80926947304SEvan Yan } 81026947304SEvan Yan return (DDI_SUCCESS); 81126947304SEvan Yan } 81226947304SEvan Yan 81326947304SEvan Yan static int 81426947304SEvan Yan ndi2ddi(int n) 81526947304SEvan Yan { 81626947304SEvan Yan int ret; 81726947304SEvan Yan 81826947304SEvan Yan switch (n) { 81926947304SEvan Yan case NDI_SUCCESS: 82026947304SEvan Yan ret = DDI_SUCCESS; 82126947304SEvan Yan break; 82226947304SEvan Yan case NDI_NOMEM: 82326947304SEvan Yan ret = DDI_ENOMEM; 82426947304SEvan Yan break; 82526947304SEvan Yan case NDI_BUSY: 82626947304SEvan Yan ret = DDI_EBUSY; 82726947304SEvan Yan break; 82826947304SEvan Yan case NDI_EINVAL: 82926947304SEvan Yan ret = DDI_EINVAL; 83026947304SEvan Yan break; 83126947304SEvan Yan case NDI_ENOTSUP: 83226947304SEvan Yan ret = DDI_ENOTSUP; 83326947304SEvan Yan break; 83426947304SEvan Yan case NDI_FAILURE: 83526947304SEvan Yan default: 83626947304SEvan Yan ret = DDI_FAILURE; 83726947304SEvan Yan break; 83826947304SEvan Yan } 83926947304SEvan Yan return (ret); 84026947304SEvan Yan } 84126947304SEvan Yan 84226947304SEvan Yan /* 84326947304SEvan Yan * Common routine to create and register a new port 84426947304SEvan Yan * 84526947304SEvan Yan * Create an empty port if dip is NULL, and cn_name needs to be specified in 84626947304SEvan Yan * this case. Otherwise, create a port mapping to the specified dip, and cn_name 84726947304SEvan Yan * is not needed in this case. 84826947304SEvan Yan */ 84926947304SEvan Yan static int 85026947304SEvan Yan pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip, char *cn_name) 85126947304SEvan Yan { 85226947304SEvan Yan ddi_hp_cn_info_t *cn_info; 85326947304SEvan Yan int ret; 85426947304SEvan Yan 85526947304SEvan Yan ASSERT((dip == NULL) != (cn_name == NULL)); 85626947304SEvan Yan cn_info = kmem_zalloc(sizeof (ddi_hp_cn_info_t), KM_SLEEP); 85726947304SEvan Yan if (dip != NULL) 85826947304SEvan Yan ret = pcie_hp_create_port_name_num(dip, cn_info); 85926947304SEvan Yan else 86026947304SEvan Yan ret = pcie_hp_setup_port_name_num(pdip, cn_name, cn_info); 86126947304SEvan Yan 86226947304SEvan Yan if (ret != DDI_SUCCESS) { 86326947304SEvan Yan kmem_free(cn_info, sizeof (ddi_hp_cn_info_t)); 86426947304SEvan Yan return (ret); 86526947304SEvan Yan } 86626947304SEvan Yan 86726947304SEvan Yan cn_info->cn_child = dip; 86826947304SEvan Yan cn_info->cn_type = DDI_HP_CN_TYPE_VIRTUAL_PORT; 86926947304SEvan Yan cn_info->cn_type_str = DDI_HP_CN_TYPE_STR_PORT; 87026947304SEvan Yan 87126947304SEvan Yan ret = ndi_hp_register(pdip, cn_info); 87226947304SEvan Yan 87326947304SEvan Yan kmem_free(cn_info->cn_name, strlen(cn_info->cn_name) + 1); 87426947304SEvan Yan kmem_free(cn_info, sizeof (ddi_hp_cn_info_t)); 87526947304SEvan Yan 87626947304SEvan Yan return (ndi2ddi(ret)); 87726947304SEvan Yan } 87826947304SEvan Yan 87926947304SEvan Yan /* Check if there is a piece of hardware exist corresponding to the cn_name */ 88026947304SEvan Yan static int 88126947304SEvan Yan pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num, int func_num) 88226947304SEvan Yan { 88326947304SEvan Yan 88426947304SEvan Yan /* 88526947304SEvan Yan * VHPTODO: 88626947304SEvan Yan * According to device and function number, check if there is a hardware 88726947304SEvan Yan * device exists. Currently, this function can not be reached before 88826947304SEvan Yan * we enable state transition to or from "Port-Empty" or "Port-Present" 88926947304SEvan Yan * states. When the pci device type project is integrated, we are going 89026947304SEvan Yan * to call the pci config space access interfaces introduced by it. 89126947304SEvan Yan */ 89226947304SEvan Yan _NOTE(ARGUNUSED(dip, dev_num, func_num)); 89326947304SEvan Yan 89426947304SEvan Yan return (DDI_SUCCESS); 89526947304SEvan Yan } 89626947304SEvan Yan 89726947304SEvan Yan /* 89826947304SEvan Yan * Dispatch hotplug commands to different hotplug controller drivers, including 89926947304SEvan Yan * physical and virtual hotplug operations. 90026947304SEvan Yan */ 90126947304SEvan Yan /* ARGSUSED */ 90226947304SEvan Yan int 90326947304SEvan Yan pcie_hp_common_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op, 90426947304SEvan Yan void *arg, void *result) 90526947304SEvan Yan { 90626947304SEvan Yan pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 90726947304SEvan Yan int ret = DDI_SUCCESS; 90826947304SEvan Yan 90926947304SEvan Yan PCIE_DBG("pcie_hp_common_ops: dip=%p cn_name=%s op=%x arg=%p\n", 91026947304SEvan Yan dip, cn_name, op, arg); 91126947304SEvan Yan 91226947304SEvan Yan switch (op) { 91326947304SEvan Yan case DDI_HPOP_CN_CREATE_PORT: 91426947304SEvan Yan { 91526947304SEvan Yan /* create an empty port */ 91626947304SEvan Yan return (pcie_hp_register_port(NULL, dip, cn_name)); 91726947304SEvan Yan } 91826947304SEvan Yan case DDI_HPOP_CN_CHANGE_STATE: 91926947304SEvan Yan { 92026947304SEvan Yan ddi_hp_cn_state_t curr_state; 92126947304SEvan Yan ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg; 92226947304SEvan Yan pcie_hp_port_state_t state_arg; 92326947304SEvan Yan 92426947304SEvan Yan if (target_state < DDI_HP_CN_STATE_PORT_EMPTY) { 92526947304SEvan Yan /* this is for physical slot state change */ 92626947304SEvan Yan break; 92726947304SEvan Yan } 92826947304SEvan Yan PCIE_DBG("pcie_hp_common_ops: change port state" 92926947304SEvan Yan " dip=%p cn_name=%s" 93026947304SEvan Yan " op=%x arg=%p\n", (void *)dip, cn_name, op, arg); 93126947304SEvan Yan 93226947304SEvan Yan state_arg.rv = DDI_FAILURE; 93326947304SEvan Yan state_arg.cn_name = cn_name; 93426947304SEvan Yan ndi_hp_walk_cn(dip, pcie_hp_get_port_state, &state_arg); 93526947304SEvan Yan if (state_arg.rv != DDI_SUCCESS) { 93626947304SEvan Yan /* can not find the port */ 93726947304SEvan Yan return (DDI_EINVAL); 93826947304SEvan Yan } 93926947304SEvan Yan curr_state = state_arg.cn_state; 94026947304SEvan Yan /* 94126947304SEvan Yan * Check if this is for changing port's state: change to/from 94226947304SEvan Yan * PORT_EMPTY/PRESENT states. 94326947304SEvan Yan */ 94426947304SEvan Yan if (curr_state < target_state) { 94526947304SEvan Yan /* Upgrade state */ 94626947304SEvan Yan switch (curr_state) { 94726947304SEvan Yan case DDI_HP_CN_STATE_PORT_EMPTY: 94826947304SEvan Yan if (target_state == 94926947304SEvan Yan DDI_HP_CN_STATE_PORT_PRESENT) { 95026947304SEvan Yan int dev_num, func_num; 95126947304SEvan Yan 95226947304SEvan Yan ret = pcie_hp_get_df_from_port_name( 95326947304SEvan Yan cn_name, &dev_num, &func_num); 95426947304SEvan Yan if (ret != DDI_SUCCESS) 95526947304SEvan Yan goto port_state_done; 95626947304SEvan Yan 95726947304SEvan Yan ret = pcie_hp_check_hardware_existence( 95826947304SEvan Yan dip, dev_num, func_num); 95926947304SEvan Yan } else if (target_state == 96026947304SEvan Yan DDI_HP_CN_STATE_OFFLINE) { 96126947304SEvan Yan ret = pcie_read_only_probe(dip, 96226947304SEvan Yan cn_name, (dev_info_t **)result); 96326947304SEvan Yan } else 96426947304SEvan Yan ret = DDI_EINVAL; 96526947304SEvan Yan 96626947304SEvan Yan goto port_state_done; 96726947304SEvan Yan case DDI_HP_CN_STATE_PORT_PRESENT: 96826947304SEvan Yan if (target_state == 96926947304SEvan Yan DDI_HP_CN_STATE_OFFLINE) 97026947304SEvan Yan ret = pcie_read_only_probe(dip, 97126947304SEvan Yan cn_name, (dev_info_t **)result); 97226947304SEvan Yan else 97326947304SEvan Yan ret = DDI_EINVAL; 97426947304SEvan Yan 97526947304SEvan Yan goto port_state_done; 97626947304SEvan Yan default: 97726947304SEvan Yan ASSERT("unexpected state"); 97826947304SEvan Yan } 97926947304SEvan Yan } else { 98026947304SEvan Yan /* Downgrade state */ 98126947304SEvan Yan switch (curr_state) { 98226947304SEvan Yan case DDI_HP_CN_STATE_PORT_PRESENT: 98326947304SEvan Yan { 98426947304SEvan Yan int dev_num, func_num; 98526947304SEvan Yan 98626947304SEvan Yan ret = pcie_hp_get_df_from_port_name(cn_name, 98726947304SEvan Yan &dev_num, &func_num); 98826947304SEvan Yan if (ret != DDI_SUCCESS) 98926947304SEvan Yan goto port_state_done; 99026947304SEvan Yan 99126947304SEvan Yan ret = pcie_hp_check_hardware_existence(dip, 99226947304SEvan Yan dev_num, func_num); 99326947304SEvan Yan 99426947304SEvan Yan goto port_state_done; 99526947304SEvan Yan } 99626947304SEvan Yan case DDI_HP_CN_STATE_OFFLINE: 99726947304SEvan Yan ret = pcie_read_only_unprobe(dip, cn_name); 99826947304SEvan Yan 99926947304SEvan Yan goto port_state_done; 100026947304SEvan Yan default: 100126947304SEvan Yan ASSERT("unexpected state"); 100226947304SEvan Yan } 100326947304SEvan Yan } 100426947304SEvan Yan port_state_done: 100526947304SEvan Yan *(ddi_hp_cn_state_t *)result = curr_state; 100626947304SEvan Yan return (ret); 100726947304SEvan Yan } 100826947304SEvan Yan default: 100926947304SEvan Yan break; 101026947304SEvan Yan } 101126947304SEvan Yan 101226947304SEvan Yan if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) { 101326947304SEvan Yan /* PCIe hotplug */ 101426947304SEvan Yan ret = pciehpc_hp_ops(dip, cn_name, op, arg, result); 101526947304SEvan Yan } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) { 101626947304SEvan Yan /* PCI SHPC hotplug */ 101726947304SEvan Yan ret = pcishpc_hp_ops(dip, cn_name, op, arg, result); 101826947304SEvan Yan } else { 101926947304SEvan Yan cmn_err(CE_WARN, "pcie_hp_common_ops: op is not supported." 102026947304SEvan Yan " dip=%p cn_name=%s" 102126947304SEvan Yan " op=%x arg=%p\n", (void *)dip, cn_name, op, arg); 102226947304SEvan Yan ret = DDI_ENOTSUP; 102326947304SEvan Yan } 102426947304SEvan Yan 102526947304SEvan Yan #if defined(__i386) || defined(__amd64) 102626947304SEvan Yan /* 102726947304SEvan Yan * like in attach, since hotplugging can change error registers, 102826947304SEvan Yan * we need to ensure that the proper bits are set on this port 102926947304SEvan Yan * after a configure operation 103026947304SEvan Yan */ 103126947304SEvan Yan if ((ret == DDI_SUCCESS) && (op == DDI_HPOP_CN_CHANGE_STATE) && 103226947304SEvan Yan (*(ddi_hp_cn_state_t *)arg == DDI_HP_CN_STATE_ENABLED)) 103326947304SEvan Yan pcieb_intel_error_workaround(dip); 103426947304SEvan Yan #endif 103526947304SEvan Yan 103626947304SEvan Yan return (ret); 103726947304SEvan Yan } 103826947304SEvan Yan 103926947304SEvan Yan /* 104026947304SEvan Yan * pcie_hp_match_dev_func: 104126947304SEvan Yan * Match dip's PCI device number and function number with input ones. 104226947304SEvan Yan */ 104326947304SEvan Yan static int 104426947304SEvan Yan pcie_hp_match_dev_func(dev_info_t *dip, void *hdl) 104526947304SEvan Yan { 104626947304SEvan Yan struct pcie_hp_find_ctrl *ctrl = (struct pcie_hp_find_ctrl *)hdl; 104726947304SEvan Yan pci_regspec_t *pci_rp; 104826947304SEvan Yan int length; 104926947304SEvan Yan int pci_dev, pci_func; 105026947304SEvan Yan 105126947304SEvan Yan if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 105226947304SEvan Yan "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 105326947304SEvan Yan ctrl->dip = NULL; 105426947304SEvan Yan return (DDI_WALK_TERMINATE); 105526947304SEvan Yan } 105626947304SEvan Yan 105726947304SEvan Yan /* get the PCI device address info */ 105826947304SEvan Yan pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 105926947304SEvan Yan pci_func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi); 106026947304SEvan Yan 106126947304SEvan Yan /* 106226947304SEvan Yan * free the memory allocated by ddi_prop_lookup_int_array 106326947304SEvan Yan */ 106426947304SEvan Yan ddi_prop_free(pci_rp); 106526947304SEvan Yan 106626947304SEvan Yan if ((pci_dev == ctrl->device) && (pci_func == ctrl->function)) { 106726947304SEvan Yan /* found the match for the specified device address */ 106826947304SEvan Yan ctrl->dip = dip; 106926947304SEvan Yan return (DDI_WALK_TERMINATE); 107026947304SEvan Yan } 107126947304SEvan Yan 107226947304SEvan Yan /* 107326947304SEvan Yan * continue the walk to the next sibling to look for a match. 107426947304SEvan Yan */ 107526947304SEvan Yan return (DDI_WALK_PRUNECHILD); 107626947304SEvan Yan } 107726947304SEvan Yan 107826947304SEvan Yan /* 107926947304SEvan Yan * pcie_hp_match_dev: 108026947304SEvan Yan * Match the dip's pci device number with the input dev_num 108126947304SEvan Yan */ 108226947304SEvan Yan static boolean_t 108326947304SEvan Yan pcie_hp_match_dev(dev_info_t *dip, int dev_num) 108426947304SEvan Yan { 108526947304SEvan Yan pci_regspec_t *pci_rp; 108626947304SEvan Yan int length; 108726947304SEvan Yan int pci_dev; 108826947304SEvan Yan 108926947304SEvan Yan if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 109026947304SEvan Yan "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) { 109126947304SEvan Yan return (B_FALSE); 109226947304SEvan Yan } 109326947304SEvan Yan 109426947304SEvan Yan /* get the PCI device address info */ 109526947304SEvan Yan pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 109626947304SEvan Yan 109726947304SEvan Yan /* 109826947304SEvan Yan * free the memory allocated by ddi_prop_lookup_int_array 109926947304SEvan Yan */ 110026947304SEvan Yan ddi_prop_free(pci_rp); 110126947304SEvan Yan 110226947304SEvan Yan if (pci_dev == dev_num) { 110326947304SEvan Yan /* found the match for the specified device address */ 110426947304SEvan Yan return (B_TRUE); 110526947304SEvan Yan } 110626947304SEvan Yan 110726947304SEvan Yan return (B_FALSE); 110826947304SEvan Yan } 110926947304SEvan Yan 111026947304SEvan Yan /* 111126947304SEvan Yan * Callback function to match with device number in order to list 111226947304SEvan Yan * occupants under a specific slot 111326947304SEvan Yan */ 111426947304SEvan Yan static int 111526947304SEvan Yan pcie_hp_list_occupants(dev_info_t *dip, void *arg) 111626947304SEvan Yan { 111726947304SEvan Yan pcie_hp_cn_cfg_t *cn_cfg_p = (pcie_hp_cn_cfg_t *)arg; 111826947304SEvan Yan pcie_hp_occupant_info_t *occupant = 111926947304SEvan Yan (pcie_hp_occupant_info_t *)cn_cfg_p->cn_private; 112026947304SEvan Yan pcie_hp_slot_t *slot_p = 112126947304SEvan Yan (pcie_hp_slot_t *)cn_cfg_p->slotp; 112226947304SEvan Yan int pci_dev; 112326947304SEvan Yan pci_regspec_t *pci_rp; 112426947304SEvan Yan int length; 112526947304SEvan Yan major_t major; 112626947304SEvan Yan 112726947304SEvan Yan /* 112826947304SEvan Yan * Get the PCI device number information from the devinfo 112926947304SEvan Yan * node. Since the node may not have the address field 113026947304SEvan Yan * setup (this is done in the DDI_INITCHILD of the parent) 113126947304SEvan Yan * we look up the 'reg' property to decode that information. 113226947304SEvan Yan */ 113326947304SEvan Yan if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, 113426947304SEvan Yan DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, 113526947304SEvan Yan (uint_t *)&length) != DDI_PROP_SUCCESS) { 113626947304SEvan Yan cn_cfg_p->rv = DDI_FAILURE; 113726947304SEvan Yan cn_cfg_p->dip = dip; 113826947304SEvan Yan return (DDI_WALK_TERMINATE); 113926947304SEvan Yan } 114026947304SEvan Yan 114126947304SEvan Yan /* get the pci device id information */ 114226947304SEvan Yan pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi); 114326947304SEvan Yan 114426947304SEvan Yan /* 114526947304SEvan Yan * free the memory allocated by ddi_prop_lookup_int_array 114626947304SEvan Yan */ 114726947304SEvan Yan ddi_prop_free(pci_rp); 114826947304SEvan Yan 114926947304SEvan Yan /* 115026947304SEvan Yan * Match the node for the device number of the slot. 115126947304SEvan Yan */ 115226947304SEvan Yan if (pci_dev == slot_p->hs_device_num) { 115326947304SEvan Yan 115426947304SEvan Yan major = ddi_driver_major(dip); 115526947304SEvan Yan 115626947304SEvan Yan /* 115726947304SEvan Yan * If the node is not yet attached, then don't list it 115826947304SEvan Yan * as an occupant. This is valid, since nothing can be 115926947304SEvan Yan * consuming it until it is attached, and cfgadm will 116026947304SEvan Yan * ask for the property explicitly which will cause it 116126947304SEvan Yan * to be re-freshed right before checking with rcm. 116226947304SEvan Yan */ 116326947304SEvan Yan if ((major == DDI_MAJOR_T_NONE) || !i_ddi_devi_attached(dip)) 116426947304SEvan Yan return (DDI_WALK_PRUNECHILD); 116526947304SEvan Yan 116626947304SEvan Yan /* 116726947304SEvan Yan * If we have used all our occupants then print mesage 116826947304SEvan Yan * and terminate walk. 116926947304SEvan Yan */ 117026947304SEvan Yan if (occupant->i >= PCIE_HP_MAX_OCCUPANTS) { 117126947304SEvan Yan cmn_err(CE_WARN, 117226947304SEvan Yan "pcie (%s%d): unable to list all occupants", 117326947304SEvan Yan ddi_driver_name(ddi_get_parent(dip)), 117426947304SEvan Yan ddi_get_instance(ddi_get_parent(dip))); 117526947304SEvan Yan return (DDI_WALK_TERMINATE); 117626947304SEvan Yan } 117726947304SEvan Yan 117826947304SEvan Yan /* 117926947304SEvan Yan * No need to hold the dip as ddi_walk_devs 118026947304SEvan Yan * has already arranged that for us. 118126947304SEvan Yan */ 118226947304SEvan Yan occupant->id[occupant->i] = 118326947304SEvan Yan kmem_alloc(sizeof (char[MAXPATHLEN]), KM_SLEEP); 118426947304SEvan Yan (void) ddi_pathname(dip, (char *)occupant->id[occupant->i]); 118526947304SEvan Yan occupant->i++; 118626947304SEvan Yan } 118726947304SEvan Yan 118826947304SEvan Yan /* 118926947304SEvan Yan * continue the walk to the next sibling to look for a match 119026947304SEvan Yan * or to find other nodes if this card is a multi-function card. 119126947304SEvan Yan */ 119226947304SEvan Yan return (DDI_WALK_PRUNECHILD); 119326947304SEvan Yan } 119426947304SEvan Yan 119526947304SEvan Yan /* 119626947304SEvan Yan * Generate the System Event for ESC_DR_REQ. 119726947304SEvan Yan * One of the consumers is pcidr, it calls to libcfgadm to perform a 119826947304SEvan Yan * configure or unconfigure operation to the AP. 119926947304SEvan Yan */ 120026947304SEvan Yan void 120126947304SEvan Yan pcie_hp_gen_sysevent_req(char *slot_name, int hint, 120226947304SEvan Yan dev_info_t *self, int kmflag) 120326947304SEvan Yan { 120426947304SEvan Yan sysevent_id_t eid; 120526947304SEvan Yan nvlist_t *ev_attr_list = NULL; 120626947304SEvan Yan char cn_path[MAXPATHLEN]; 120726947304SEvan Yan char *ap_id; 120826947304SEvan Yan int err, ap_id_len; 120926947304SEvan Yan 121026947304SEvan Yan /* 121126947304SEvan Yan * Minor device name (AP) will be bus path 121226947304SEvan Yan * concatenated with slot name 121326947304SEvan Yan */ 121426947304SEvan Yan (void) strcpy(cn_path, "/devices"); 121526947304SEvan Yan (void) ddi_pathname(self, cn_path + strlen("/devices")); 121626947304SEvan Yan 121726947304SEvan Yan ap_id_len = strlen(cn_path) + strlen(":") + 121826947304SEvan Yan strlen(slot_name) + 1; 121926947304SEvan Yan ap_id = kmem_zalloc(ap_id_len, kmflag); 122026947304SEvan Yan if (ap_id == NULL) { 122126947304SEvan Yan cmn_err(CE_WARN, 122226947304SEvan Yan "%s%d: Failed to allocate memory for AP ID: %s:%s", 122326947304SEvan Yan ddi_driver_name(self), ddi_get_instance(self), 122426947304SEvan Yan cn_path, slot_name); 122526947304SEvan Yan 122626947304SEvan Yan return; 122726947304SEvan Yan } 122826947304SEvan Yan 122926947304SEvan Yan (void) strcpy(ap_id, cn_path); 123026947304SEvan Yan (void) strcat(ap_id, ":"); 123126947304SEvan Yan (void) strcat(ap_id, slot_name); 123226947304SEvan Yan 123326947304SEvan Yan err = nvlist_alloc(&ev_attr_list, NV_UNIQUE_NAME_TYPE, kmflag); 123426947304SEvan Yan if (err != 0) { 123526947304SEvan Yan cmn_err(CE_WARN, 123626947304SEvan Yan "%s%d: Failed to allocate memory " 123726947304SEvan Yan "for event attributes%s", ddi_driver_name(self), 123826947304SEvan Yan ddi_get_instance(self), ESC_DR_REQ); 123926947304SEvan Yan 124026947304SEvan Yan kmem_free(ap_id, ap_id_len); 124126947304SEvan Yan return; 124226947304SEvan Yan } 124326947304SEvan Yan 124426947304SEvan Yan switch (hint) { 124526947304SEvan Yan 124626947304SEvan Yan case SE_INVESTIGATE_RES: /* fall through */ 124726947304SEvan Yan case SE_INCOMING_RES: /* fall through */ 124826947304SEvan Yan case SE_OUTGOING_RES: /* fall through */ 124926947304SEvan Yan 125026947304SEvan Yan err = nvlist_add_string(ev_attr_list, DR_REQ_TYPE, 125126947304SEvan Yan SE_REQ2STR(hint)); 125226947304SEvan Yan 125326947304SEvan Yan if (err != 0) { 125426947304SEvan Yan cmn_err(CE_WARN, 125526947304SEvan Yan "%s%d: Failed to add attr [%s] " 125626947304SEvan Yan "for %s event", ddi_driver_name(self), 125726947304SEvan Yan ddi_get_instance(self), 125826947304SEvan Yan DR_REQ_TYPE, ESC_DR_REQ); 125926947304SEvan Yan 126026947304SEvan Yan goto done; 126126947304SEvan Yan } 126226947304SEvan Yan break; 126326947304SEvan Yan 126426947304SEvan Yan default: 126526947304SEvan Yan cmn_err(CE_WARN, "%s%d: Unknown hint on sysevent", 126626947304SEvan Yan ddi_driver_name(self), ddi_get_instance(self)); 126726947304SEvan Yan 126826947304SEvan Yan goto done; 126926947304SEvan Yan } 127026947304SEvan Yan 127126947304SEvan Yan /* 127226947304SEvan Yan * Add attachment point as attribute (common attribute) 127326947304SEvan Yan */ 127426947304SEvan Yan 127526947304SEvan Yan err = nvlist_add_string(ev_attr_list, DR_AP_ID, ap_id); 127626947304SEvan Yan 127726947304SEvan Yan if (err != 0) { 127826947304SEvan Yan cmn_err(CE_WARN, "%s%d: Failed to add attr [%s] for %s event", 127926947304SEvan Yan ddi_driver_name(self), ddi_get_instance(self), 128026947304SEvan Yan DR_AP_ID, EC_DR); 128126947304SEvan Yan 128226947304SEvan Yan goto done; 128326947304SEvan Yan } 128426947304SEvan Yan 128526947304SEvan Yan 128626947304SEvan Yan /* 128726947304SEvan Yan * Log this event with sysevent framework. 128826947304SEvan Yan */ 128926947304SEvan Yan 129026947304SEvan Yan err = ddi_log_sysevent(self, DDI_VENDOR_SUNW, EC_DR, 129126947304SEvan Yan ESC_DR_REQ, ev_attr_list, &eid, 129226947304SEvan Yan ((kmflag == KM_SLEEP) ? DDI_SLEEP : DDI_NOSLEEP)); 129326947304SEvan Yan if (err != 0) { 129426947304SEvan Yan cmn_err(CE_WARN, "%s%d: Failed to log %s event", 129526947304SEvan Yan ddi_driver_name(self), ddi_get_instance(self), EC_DR); 129626947304SEvan Yan } 129726947304SEvan Yan 129826947304SEvan Yan done: 129926947304SEvan Yan nvlist_free(ev_attr_list); 130026947304SEvan Yan kmem_free(ap_id, ap_id_len); 130126947304SEvan Yan } 1302