110923SEvan.Yan@Sun.COM /*
210923SEvan.Yan@Sun.COM * CDDL HEADER START
310923SEvan.Yan@Sun.COM *
410923SEvan.Yan@Sun.COM * The contents of this file are subject to the terms of the
510923SEvan.Yan@Sun.COM * Common Development and Distribution License (the "License").
610923SEvan.Yan@Sun.COM * You may not use this file except in compliance with the License.
710923SEvan.Yan@Sun.COM *
810923SEvan.Yan@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910923SEvan.Yan@Sun.COM * or http://www.opensolaris.org/os/licensing.
1010923SEvan.Yan@Sun.COM * See the License for the specific language governing permissions
1110923SEvan.Yan@Sun.COM * and limitations under the License.
1210923SEvan.Yan@Sun.COM *
1310923SEvan.Yan@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
1410923SEvan.Yan@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510923SEvan.Yan@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
1610923SEvan.Yan@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
1710923SEvan.Yan@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
1810923SEvan.Yan@Sun.COM *
1910923SEvan.Yan@Sun.COM * CDDL HEADER END
2010923SEvan.Yan@Sun.COM */
2110923SEvan.Yan@Sun.COM
2210923SEvan.Yan@Sun.COM /*
2310923SEvan.Yan@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2410923SEvan.Yan@Sun.COM * Use is subject to license terms.
2510923SEvan.Yan@Sun.COM */
2610923SEvan.Yan@Sun.COM
2710923SEvan.Yan@Sun.COM /*
2810923SEvan.Yan@Sun.COM * This file contains the common hotplug code that is used by Standard
2910923SEvan.Yan@Sun.COM * PCIe and PCI HotPlug Controller code.
3010923SEvan.Yan@Sun.COM *
3110923SEvan.Yan@Sun.COM * NOTE: This file is compiled and delivered through misc/pcie module.
3210923SEvan.Yan@Sun.COM */
3310923SEvan.Yan@Sun.COM
3410923SEvan.Yan@Sun.COM #include <sys/types.h>
3510923SEvan.Yan@Sun.COM #include <sys/conf.h>
3610923SEvan.Yan@Sun.COM #include <sys/kmem.h>
3710923SEvan.Yan@Sun.COM #include <sys/debug.h>
3810923SEvan.Yan@Sun.COM #include <sys/vtrace.h>
3910923SEvan.Yan@Sun.COM #include <sys/autoconf.h>
4010923SEvan.Yan@Sun.COM #include <sys/varargs.h>
4110923SEvan.Yan@Sun.COM #include <sys/ddi_impldefs.h>
4210923SEvan.Yan@Sun.COM #include <sys/time.h>
4310923SEvan.Yan@Sun.COM #include <sys/note.h>
4410923SEvan.Yan@Sun.COM #include <sys/callb.h>
4510923SEvan.Yan@Sun.COM #include <sys/ddi.h>
4610923SEvan.Yan@Sun.COM #include <sys/sunddi.h>
4710923SEvan.Yan@Sun.COM #include <sys/sunndi.h>
4810923SEvan.Yan@Sun.COM #include <sys/sysevent.h>
4910923SEvan.Yan@Sun.COM #include <sys/sysevent/eventdefs.h>
5010923SEvan.Yan@Sun.COM #include <sys/sysevent/dr.h>
5110923SEvan.Yan@Sun.COM #include <sys/pci_impl.h>
5210923SEvan.Yan@Sun.COM #include <sys/pci_cap.h>
5310923SEvan.Yan@Sun.COM #include <sys/hotplug/pci/pcicfg.h>
5410923SEvan.Yan@Sun.COM #include <sys/hotplug/pci/pcie_hp.h>
5510923SEvan.Yan@Sun.COM #include <sys/hotplug/pci/pciehpc.h>
5610923SEvan.Yan@Sun.COM #include <sys/hotplug/pci/pcishpc.h>
5710923SEvan.Yan@Sun.COM #include <io/pciex/pcieb.h>
5810923SEvan.Yan@Sun.COM
5910923SEvan.Yan@Sun.COM /* Local functions prototype */
6010923SEvan.Yan@Sun.COM static int pcie_hp_list_occupants(dev_info_t *dip, void *arg);
6110923SEvan.Yan@Sun.COM static int pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip,
6210923SEvan.Yan@Sun.COM char *cn_name);
6310923SEvan.Yan@Sun.COM static int pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num);
6410923SEvan.Yan@Sun.COM static int pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg);
6510923SEvan.Yan@Sun.COM static int pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg);
6610923SEvan.Yan@Sun.COM static int pcie_hp_match_dev_func(dev_info_t *dip, void *hdl);
6710923SEvan.Yan@Sun.COM static boolean_t pcie_hp_match_dev(dev_info_t *dip, int dev_num);
6810923SEvan.Yan@Sun.COM static int pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num,
6910923SEvan.Yan@Sun.COM int *func_num);
7010923SEvan.Yan@Sun.COM static int pcie_hp_create_port_name_num(dev_info_t *dip,
7110923SEvan.Yan@Sun.COM ddi_hp_cn_info_t *cn_info);
7210923SEvan.Yan@Sun.COM static int pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num,
7310923SEvan.Yan@Sun.COM int func_num);
7410923SEvan.Yan@Sun.COM
7510923SEvan.Yan@Sun.COM /*
7610923SEvan.Yan@Sun.COM * Global functions (called by other drivers/modules)
7710923SEvan.Yan@Sun.COM */
7810923SEvan.Yan@Sun.COM
7910923SEvan.Yan@Sun.COM /*
8010923SEvan.Yan@Sun.COM * return description text for led state
8110923SEvan.Yan@Sun.COM */
8210923SEvan.Yan@Sun.COM char *
pcie_led_state_text(pcie_hp_led_state_t state)8310923SEvan.Yan@Sun.COM pcie_led_state_text(pcie_hp_led_state_t state)
8410923SEvan.Yan@Sun.COM {
8510923SEvan.Yan@Sun.COM switch (state) {
8610923SEvan.Yan@Sun.COM case PCIE_HP_LED_ON:
8710923SEvan.Yan@Sun.COM return (PCIEHPC_PROP_VALUE_ON);
8810923SEvan.Yan@Sun.COM case PCIE_HP_LED_OFF:
8910923SEvan.Yan@Sun.COM return (PCIEHPC_PROP_VALUE_OFF);
9010923SEvan.Yan@Sun.COM case PCIE_HP_LED_BLINK:
9110923SEvan.Yan@Sun.COM default:
9210923SEvan.Yan@Sun.COM return (PCIEHPC_PROP_VALUE_BLINK);
9310923SEvan.Yan@Sun.COM }
9410923SEvan.Yan@Sun.COM }
9510923SEvan.Yan@Sun.COM
9610923SEvan.Yan@Sun.COM /*
9710923SEvan.Yan@Sun.COM * return description text for slot condition
9810923SEvan.Yan@Sun.COM */
9910923SEvan.Yan@Sun.COM char *
pcie_slot_condition_text(ap_condition_t condition)10010923SEvan.Yan@Sun.COM pcie_slot_condition_text(ap_condition_t condition)
10110923SEvan.Yan@Sun.COM {
10210923SEvan.Yan@Sun.COM switch (condition) {
10310923SEvan.Yan@Sun.COM case AP_COND_UNKNOWN:
10410923SEvan.Yan@Sun.COM return (PCIEHPC_PROP_VALUE_UNKNOWN);
10510923SEvan.Yan@Sun.COM case AP_COND_OK:
10610923SEvan.Yan@Sun.COM return (PCIEHPC_PROP_VALUE_OK);
10710923SEvan.Yan@Sun.COM case AP_COND_FAILING:
10810923SEvan.Yan@Sun.COM return (PCIEHPC_PROP_VALUE_FAILING);
10910923SEvan.Yan@Sun.COM case AP_COND_FAILED:
11010923SEvan.Yan@Sun.COM return (PCIEHPC_PROP_VALUE_FAILED);
11110923SEvan.Yan@Sun.COM case AP_COND_UNUSABLE:
11210923SEvan.Yan@Sun.COM return (PCIEHPC_PROP_VALUE_UNUSABLE);
11310923SEvan.Yan@Sun.COM default:
11410923SEvan.Yan@Sun.COM return (PCIEHPC_PROP_VALUE_UNKNOWN);
11510923SEvan.Yan@Sun.COM }
11610923SEvan.Yan@Sun.COM }
11710923SEvan.Yan@Sun.COM
11810923SEvan.Yan@Sun.COM /*
11910923SEvan.Yan@Sun.COM * routine to copy in a nvlist from userland
12010923SEvan.Yan@Sun.COM */
12110923SEvan.Yan@Sun.COM int
pcie_copyin_nvlist(char * packed_buf,size_t packed_sz,nvlist_t ** nvlp)12210923SEvan.Yan@Sun.COM pcie_copyin_nvlist(char *packed_buf, size_t packed_sz, nvlist_t **nvlp)
12310923SEvan.Yan@Sun.COM {
12410923SEvan.Yan@Sun.COM int ret = DDI_SUCCESS;
12510923SEvan.Yan@Sun.COM char *packed;
12610923SEvan.Yan@Sun.COM nvlist_t *dest = NULL;
12710923SEvan.Yan@Sun.COM
12810923SEvan.Yan@Sun.COM if (packed_buf == NULL || packed_sz == 0)
12910923SEvan.Yan@Sun.COM return (DDI_EINVAL);
13010923SEvan.Yan@Sun.COM
13110923SEvan.Yan@Sun.COM /* copyin packed nvlist */
13210923SEvan.Yan@Sun.COM if ((packed = kmem_alloc(packed_sz, KM_SLEEP)) == NULL)
13310923SEvan.Yan@Sun.COM return (DDI_ENOMEM);
13410923SEvan.Yan@Sun.COM
13510923SEvan.Yan@Sun.COM if (copyin(packed_buf, packed, packed_sz) != 0) {
13610923SEvan.Yan@Sun.COM cmn_err(CE_WARN, "pcie_copyin_nvlist: copyin failed.\n");
13710923SEvan.Yan@Sun.COM ret = DDI_FAILURE;
13810923SEvan.Yan@Sun.COM goto copyin_cleanup;
13910923SEvan.Yan@Sun.COM }
14010923SEvan.Yan@Sun.COM
14110923SEvan.Yan@Sun.COM /* unpack packed nvlist */
14210923SEvan.Yan@Sun.COM if ((ret = nvlist_unpack(packed, packed_sz, &dest, KM_SLEEP)) != 0) {
14310923SEvan.Yan@Sun.COM cmn_err(CE_WARN, "pcie_copyin_nvlist: nvlist_unpack "
14410923SEvan.Yan@Sun.COM "failed with err %d\n", ret);
14510923SEvan.Yan@Sun.COM switch (ret) {
14610923SEvan.Yan@Sun.COM case EINVAL:
14710923SEvan.Yan@Sun.COM case ENOTSUP:
14810923SEvan.Yan@Sun.COM ret = DDI_EINVAL;
14910923SEvan.Yan@Sun.COM goto copyin_cleanup;
15010923SEvan.Yan@Sun.COM case ENOMEM:
15110923SEvan.Yan@Sun.COM ret = DDI_ENOMEM;
15210923SEvan.Yan@Sun.COM goto copyin_cleanup;
15310923SEvan.Yan@Sun.COM default:
15410923SEvan.Yan@Sun.COM ret = DDI_FAILURE;
15510923SEvan.Yan@Sun.COM goto copyin_cleanup;
15610923SEvan.Yan@Sun.COM }
15710923SEvan.Yan@Sun.COM }
15810923SEvan.Yan@Sun.COM *nvlp = dest;
15910923SEvan.Yan@Sun.COM copyin_cleanup:
16010923SEvan.Yan@Sun.COM kmem_free(packed, packed_sz);
16110923SEvan.Yan@Sun.COM return (ret);
16210923SEvan.Yan@Sun.COM }
16310923SEvan.Yan@Sun.COM
16410923SEvan.Yan@Sun.COM /*
16510923SEvan.Yan@Sun.COM * routine to copy out a nvlist to userland
16610923SEvan.Yan@Sun.COM */
16710923SEvan.Yan@Sun.COM int
pcie_copyout_nvlist(nvlist_t * nvl,char * packed_buf,size_t * buf_sz)16810923SEvan.Yan@Sun.COM pcie_copyout_nvlist(nvlist_t *nvl, char *packed_buf, size_t *buf_sz)
16910923SEvan.Yan@Sun.COM {
17010923SEvan.Yan@Sun.COM int err = 0;
17110923SEvan.Yan@Sun.COM char *buf = NULL;
17210923SEvan.Yan@Sun.COM size_t packed_sz;
17310923SEvan.Yan@Sun.COM
17410923SEvan.Yan@Sun.COM if (nvl == NULL || packed_buf == NULL || buf_sz == NULL)
17510923SEvan.Yan@Sun.COM return (DDI_EINVAL);
17610923SEvan.Yan@Sun.COM
17710923SEvan.Yan@Sun.COM /* pack nvlist, the library will allocate memory */
17810923SEvan.Yan@Sun.COM if ((err = nvlist_pack(nvl, &buf, &packed_sz, NV_ENCODE_NATIVE, 0))
17910923SEvan.Yan@Sun.COM != 0) {
18010923SEvan.Yan@Sun.COM cmn_err(CE_WARN, "pcie_copyout_nvlist: nvlist_pack "
18110923SEvan.Yan@Sun.COM "failed with err %d\n", err);
18210923SEvan.Yan@Sun.COM switch (err) {
18310923SEvan.Yan@Sun.COM case EINVAL:
18410923SEvan.Yan@Sun.COM case ENOTSUP:
18510923SEvan.Yan@Sun.COM return (DDI_EINVAL);
18610923SEvan.Yan@Sun.COM case ENOMEM:
18710923SEvan.Yan@Sun.COM return (DDI_ENOMEM);
18810923SEvan.Yan@Sun.COM default:
18910923SEvan.Yan@Sun.COM return (DDI_FAILURE);
19010923SEvan.Yan@Sun.COM }
19110923SEvan.Yan@Sun.COM }
19210923SEvan.Yan@Sun.COM if (packed_sz > *buf_sz) {
19310923SEvan.Yan@Sun.COM return (DDI_EINVAL);
19410923SEvan.Yan@Sun.COM }
19510923SEvan.Yan@Sun.COM
19610923SEvan.Yan@Sun.COM /* copyout packed nvlist */
19710923SEvan.Yan@Sun.COM if (copyout(buf, packed_buf, packed_sz) != 0) {
19810923SEvan.Yan@Sun.COM cmn_err(CE_WARN, "pcie_copyout_nvlist: copyout " "failed.\n");
19910923SEvan.Yan@Sun.COM kmem_free(buf, packed_sz);
20010923SEvan.Yan@Sun.COM return (DDI_FAILURE);
20110923SEvan.Yan@Sun.COM }
20210923SEvan.Yan@Sun.COM
20310923SEvan.Yan@Sun.COM *buf_sz = packed_sz;
20410923SEvan.Yan@Sun.COM kmem_free(buf, packed_sz);
20510923SEvan.Yan@Sun.COM return (DDI_SUCCESS);
20610923SEvan.Yan@Sun.COM }
20710923SEvan.Yan@Sun.COM
20810923SEvan.Yan@Sun.COM /*
20910923SEvan.Yan@Sun.COM * init bus_hp_op entry and init hotpluggable slots & virtual ports
21010923SEvan.Yan@Sun.COM */
21110923SEvan.Yan@Sun.COM int
pcie_hp_init(dev_info_t * dip,caddr_t arg)21210923SEvan.Yan@Sun.COM pcie_hp_init(dev_info_t *dip, caddr_t arg)
21310923SEvan.Yan@Sun.COM {
21410923SEvan.Yan@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
21510923SEvan.Yan@Sun.COM int ret = DDI_SUCCESS, count;
21610923SEvan.Yan@Sun.COM dev_info_t *cdip;
21710923SEvan.Yan@Sun.COM
21810923SEvan.Yan@Sun.COM if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
21910923SEvan.Yan@Sun.COM /* Init hotplug controller */
22010923SEvan.Yan@Sun.COM ret = pciehpc_init(dip, arg);
22110923SEvan.Yan@Sun.COM } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
22210923SEvan.Yan@Sun.COM ret = pcishpc_init(dip);
22310923SEvan.Yan@Sun.COM }
22410923SEvan.Yan@Sun.COM
22510923SEvan.Yan@Sun.COM if (ret != DDI_SUCCESS) {
226*11366SColin.Zou@Sun.COM PCIE_DBG("pcie_hp_init: initialize hotplug "
22710923SEvan.Yan@Sun.COM "controller failed with %d\n", ret);
22810923SEvan.Yan@Sun.COM return (ret);
22910923SEvan.Yan@Sun.COM }
23010923SEvan.Yan@Sun.COM
23110923SEvan.Yan@Sun.COM ndi_devi_enter(dip, &count);
23210923SEvan.Yan@Sun.COM
23310923SEvan.Yan@Sun.COM /* Create port for the first level children */
23410923SEvan.Yan@Sun.COM cdip = ddi_get_child(dip);
23510923SEvan.Yan@Sun.COM while (cdip != NULL) {
23610923SEvan.Yan@Sun.COM if ((ret = pcie_hp_register_port(cdip, dip, NULL))
23710923SEvan.Yan@Sun.COM != DDI_SUCCESS) {
23810923SEvan.Yan@Sun.COM /* stop and cleanup */
23910923SEvan.Yan@Sun.COM break;
24010923SEvan.Yan@Sun.COM }
24110923SEvan.Yan@Sun.COM cdip = ddi_get_next_sibling(cdip);
24210923SEvan.Yan@Sun.COM }
24310923SEvan.Yan@Sun.COM ndi_devi_exit(dip, count);
24410923SEvan.Yan@Sun.COM if (ret != DDI_SUCCESS) {
24510923SEvan.Yan@Sun.COM cmn_err(CE_WARN, "pcie_hp_init: initialize virtual "
24610923SEvan.Yan@Sun.COM "hotplug port failed with %d\n", ret);
24710923SEvan.Yan@Sun.COM (void) pcie_hp_uninit(dip);
24810923SEvan.Yan@Sun.COM
24910923SEvan.Yan@Sun.COM return (ret);
25010923SEvan.Yan@Sun.COM }
25110923SEvan.Yan@Sun.COM
25210923SEvan.Yan@Sun.COM return (DDI_SUCCESS);
25310923SEvan.Yan@Sun.COM }
25410923SEvan.Yan@Sun.COM
25510923SEvan.Yan@Sun.COM /*
25610923SEvan.Yan@Sun.COM * uninit the hotpluggable slots and virtual ports
25710923SEvan.Yan@Sun.COM */
25810923SEvan.Yan@Sun.COM int
pcie_hp_uninit(dev_info_t * dip)25910923SEvan.Yan@Sun.COM pcie_hp_uninit(dev_info_t *dip)
26010923SEvan.Yan@Sun.COM {
26110923SEvan.Yan@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
26210923SEvan.Yan@Sun.COM pcie_hp_unreg_port_t arg;
26310923SEvan.Yan@Sun.COM
26410923SEvan.Yan@Sun.COM /*
26510923SEvan.Yan@Sun.COM * Must set arg.rv to NDI_SUCCESS so that if there's no port
26610923SEvan.Yan@Sun.COM * under this dip, we still return success thus the bridge
26710923SEvan.Yan@Sun.COM * driver can be successfully detached.
26810923SEvan.Yan@Sun.COM *
26910923SEvan.Yan@Sun.COM * Note that during the probe PCI configurator calls
27010923SEvan.Yan@Sun.COM * ndi_devi_offline() to detach driver for a new probed bridge,
27110923SEvan.Yan@Sun.COM * so that it can reprogram the resources for the bridge,
27210923SEvan.Yan@Sun.COM * ndi_devi_offline() calls into pcieb_detach() which in turn
27310923SEvan.Yan@Sun.COM * calls into this function. In this case there are no ports
27410923SEvan.Yan@Sun.COM * created under a new probe bridge dip, as ports are only
27510923SEvan.Yan@Sun.COM * created after the configurator finishing probing, thus the
27610923SEvan.Yan@Sun.COM * ndi_hp_walk_cn() will see no ports when this is called
27710923SEvan.Yan@Sun.COM * from the PCI configurtor.
27810923SEvan.Yan@Sun.COM */
27910923SEvan.Yan@Sun.COM arg.nexus_dip = dip;
28010923SEvan.Yan@Sun.COM arg.connector_num = DDI_HP_CN_NUM_NONE;
28110923SEvan.Yan@Sun.COM arg.rv = NDI_SUCCESS;
28210923SEvan.Yan@Sun.COM
28310923SEvan.Yan@Sun.COM /* tear down all virtual hotplug handles */
28410923SEvan.Yan@Sun.COM ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg);
28510923SEvan.Yan@Sun.COM
28610923SEvan.Yan@Sun.COM if (arg.rv != NDI_SUCCESS)
28710923SEvan.Yan@Sun.COM return (DDI_FAILURE);
28810923SEvan.Yan@Sun.COM
28910923SEvan.Yan@Sun.COM if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p))
29010923SEvan.Yan@Sun.COM (void) pciehpc_uninit(dip);
29110923SEvan.Yan@Sun.COM else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p))
29210923SEvan.Yan@Sun.COM (void) pcishpc_uninit(dip);
29310923SEvan.Yan@Sun.COM
29410923SEvan.Yan@Sun.COM return (DDI_SUCCESS);
29510923SEvan.Yan@Sun.COM }
29610923SEvan.Yan@Sun.COM
29710923SEvan.Yan@Sun.COM /*
29810923SEvan.Yan@Sun.COM * interrupt handler
29910923SEvan.Yan@Sun.COM */
30010923SEvan.Yan@Sun.COM int
pcie_hp_intr(dev_info_t * dip)30110923SEvan.Yan@Sun.COM pcie_hp_intr(dev_info_t *dip)
30210923SEvan.Yan@Sun.COM {
30310923SEvan.Yan@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
30410923SEvan.Yan@Sun.COM int ret = DDI_INTR_UNCLAIMED;
30510923SEvan.Yan@Sun.COM
30610923SEvan.Yan@Sun.COM if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p))
30710923SEvan.Yan@Sun.COM ret = pciehpc_intr(dip);
30810923SEvan.Yan@Sun.COM else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p))
30910923SEvan.Yan@Sun.COM ret = pcishpc_intr(dip);
31010923SEvan.Yan@Sun.COM
31110923SEvan.Yan@Sun.COM return (ret);
31210923SEvan.Yan@Sun.COM }
31310923SEvan.Yan@Sun.COM
31410923SEvan.Yan@Sun.COM /*
31510923SEvan.Yan@Sun.COM * Probe the given PCIe/PCI Hotplug Connection (CN).
31610923SEvan.Yan@Sun.COM */
31710923SEvan.Yan@Sun.COM /*ARGSUSED*/
31810923SEvan.Yan@Sun.COM int
pcie_hp_probe(pcie_hp_slot_t * slot_p)31910923SEvan.Yan@Sun.COM pcie_hp_probe(pcie_hp_slot_t *slot_p)
32010923SEvan.Yan@Sun.COM {
32110923SEvan.Yan@Sun.COM pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
32210923SEvan.Yan@Sun.COM dev_info_t *dip = ctrl_p->hc_dip;
32310923SEvan.Yan@Sun.COM
32410923SEvan.Yan@Sun.COM /*
32510923SEvan.Yan@Sun.COM * Call the configurator to probe a given PCI hotplug
32610923SEvan.Yan@Sun.COM * Hotplug Connection (CN).
32710923SEvan.Yan@Sun.COM */
32810923SEvan.Yan@Sun.COM if (pcicfg_configure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0)
32910923SEvan.Yan@Sun.COM != PCICFG_SUCCESS) {
33010923SEvan.Yan@Sun.COM PCIE_DBG("pcie_hp_probe() failed\n");
33110923SEvan.Yan@Sun.COM return (DDI_FAILURE);
33210923SEvan.Yan@Sun.COM }
33310923SEvan.Yan@Sun.COM slot_p->hs_condition = AP_COND_OK;
33410923SEvan.Yan@Sun.COM pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip),
33510923SEvan.Yan@Sun.COM slot_p->hs_minor), slot_p->hs_device_num);
33610923SEvan.Yan@Sun.COM
33710923SEvan.Yan@Sun.COM /*
33810923SEvan.Yan@Sun.COM * Create ports for the newly probed devices.
33910923SEvan.Yan@Sun.COM * Note, this is only for the first level children because the
34010923SEvan.Yan@Sun.COM * descendants' ports will be created during bridge driver attach.
34110923SEvan.Yan@Sun.COM */
34210923SEvan.Yan@Sun.COM return (pcie_hp_register_ports_for_dev(dip, slot_p->hs_device_num));
34310923SEvan.Yan@Sun.COM }
34410923SEvan.Yan@Sun.COM
34510923SEvan.Yan@Sun.COM /*
34610923SEvan.Yan@Sun.COM * Unprobe the given PCIe/PCI Hotplug Connection (CN):
34710923SEvan.Yan@Sun.COM * 1. remove all child device nodes
34810923SEvan.Yan@Sun.COM * 2. unregister all dependent ports
34910923SEvan.Yan@Sun.COM */
35010923SEvan.Yan@Sun.COM /*ARGSUSED*/
35110923SEvan.Yan@Sun.COM int
pcie_hp_unprobe(pcie_hp_slot_t * slot_p)35210923SEvan.Yan@Sun.COM pcie_hp_unprobe(pcie_hp_slot_t *slot_p)
35310923SEvan.Yan@Sun.COM {
35410923SEvan.Yan@Sun.COM pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
35510923SEvan.Yan@Sun.COM dev_info_t *dip = ctrl_p->hc_dip;
35610923SEvan.Yan@Sun.COM pcie_hp_unreg_port_t arg;
35710923SEvan.Yan@Sun.COM
35810923SEvan.Yan@Sun.COM /*
35910923SEvan.Yan@Sun.COM * Call the configurator to unprobe a given PCI hotplug
36010923SEvan.Yan@Sun.COM * Hotplug Connection (CN).
36110923SEvan.Yan@Sun.COM */
36210923SEvan.Yan@Sun.COM if (pcicfg_unconfigure(dip, slot_p->hs_device_num, PCICFG_ALL_FUNC, 0)
36310923SEvan.Yan@Sun.COM != PCICFG_SUCCESS) {
36410923SEvan.Yan@Sun.COM PCIE_DBG("pcie_hp_unprobe() failed\n");
36510923SEvan.Yan@Sun.COM return (DDI_FAILURE);
36610923SEvan.Yan@Sun.COM }
36710923SEvan.Yan@Sun.COM slot_p->hs_condition = AP_COND_UNKNOWN;
36810923SEvan.Yan@Sun.COM pcie_hp_delete_occupant_props(dip, makedevice(ddi_driver_major(dip),
36910923SEvan.Yan@Sun.COM slot_p->hs_minor));
37010923SEvan.Yan@Sun.COM
37110923SEvan.Yan@Sun.COM /*
37210923SEvan.Yan@Sun.COM * Remove ports for the unprobed devices.
37310923SEvan.Yan@Sun.COM * Note, this is only for the first level children because the
37410923SEvan.Yan@Sun.COM * descendants' ports were already removed during bridge driver dettach.
37510923SEvan.Yan@Sun.COM */
37610923SEvan.Yan@Sun.COM arg.nexus_dip = dip;
37710923SEvan.Yan@Sun.COM arg.connector_num = slot_p->hs_info.cn_num;
37810923SEvan.Yan@Sun.COM arg.rv = NDI_SUCCESS;
37910923SEvan.Yan@Sun.COM ndi_hp_walk_cn(dip, pcie_hp_unregister_ports_cb, &arg);
38010923SEvan.Yan@Sun.COM
38110923SEvan.Yan@Sun.COM return (arg.rv == NDI_SUCCESS) ? (DDI_SUCCESS) : (DDI_FAILURE);
38210923SEvan.Yan@Sun.COM }
38310923SEvan.Yan@Sun.COM
38410923SEvan.Yan@Sun.COM /* Read-only probe: no hardware register programming. */
38510923SEvan.Yan@Sun.COM int
pcie_read_only_probe(dev_info_t * dip,char * cn_name,dev_info_t ** pcdip)38610923SEvan.Yan@Sun.COM pcie_read_only_probe(dev_info_t *dip, char *cn_name, dev_info_t **pcdip)
38710923SEvan.Yan@Sun.COM {
38810923SEvan.Yan@Sun.COM long dev, func;
38910923SEvan.Yan@Sun.COM int ret;
39010923SEvan.Yan@Sun.COM char *sp;
39110923SEvan.Yan@Sun.COM dev_info_t *cdip;
39210923SEvan.Yan@Sun.COM
39310923SEvan.Yan@Sun.COM *pcdip = NULL;
39410923SEvan.Yan@Sun.COM /*
39510923SEvan.Yan@Sun.COM * Parse the string of a pci Port name and get the device number
39610923SEvan.Yan@Sun.COM * and function number.
39710923SEvan.Yan@Sun.COM */
39810923SEvan.Yan@Sun.COM if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0)
39910923SEvan.Yan@Sun.COM return (DDI_EINVAL);
40010923SEvan.Yan@Sun.COM if (ddi_strtol(sp + 1, NULL, 10, &func) != 0)
40110923SEvan.Yan@Sun.COM return (DDI_EINVAL);
40210923SEvan.Yan@Sun.COM
40310923SEvan.Yan@Sun.COM ret = pcicfg_configure(dip, (int)dev, (int)func,
40410923SEvan.Yan@Sun.COM PCICFG_FLAG_READ_ONLY);
40510923SEvan.Yan@Sun.COM if (ret == PCICFG_SUCCESS) {
40610923SEvan.Yan@Sun.COM cdip = pcie_hp_devi_find(dip, (int)dev, (int)func);
40710923SEvan.Yan@Sun.COM *pcdip = cdip;
40810923SEvan.Yan@Sun.COM }
40910923SEvan.Yan@Sun.COM return (ret);
41010923SEvan.Yan@Sun.COM }
41110923SEvan.Yan@Sun.COM
41210923SEvan.Yan@Sun.COM /* Read-only unprobe: no hardware register programming. */
41310923SEvan.Yan@Sun.COM int
pcie_read_only_unprobe(dev_info_t * dip,char * cn_name)41410923SEvan.Yan@Sun.COM pcie_read_only_unprobe(dev_info_t *dip, char *cn_name)
41510923SEvan.Yan@Sun.COM {
41610923SEvan.Yan@Sun.COM long dev, func;
41710923SEvan.Yan@Sun.COM int ret;
41810923SEvan.Yan@Sun.COM char *sp;
41910923SEvan.Yan@Sun.COM
42010923SEvan.Yan@Sun.COM /*
42110923SEvan.Yan@Sun.COM * Parse the string of a pci Port name and get the device number
42210923SEvan.Yan@Sun.COM * and function number.
42310923SEvan.Yan@Sun.COM */
42410923SEvan.Yan@Sun.COM if (ddi_strtol(cn_name + 4, &sp, 10, &dev) != 0)
42510923SEvan.Yan@Sun.COM return (DDI_EINVAL);
42610923SEvan.Yan@Sun.COM if (ddi_strtol(sp + 1, NULL, 10, &func) != 0)
42710923SEvan.Yan@Sun.COM return (DDI_EINVAL);
42810923SEvan.Yan@Sun.COM
42910923SEvan.Yan@Sun.COM ret = pcicfg_unconfigure(dip, (int)dev, (int)func,
43010923SEvan.Yan@Sun.COM PCICFG_FLAG_READ_ONLY);
43110923SEvan.Yan@Sun.COM
43210923SEvan.Yan@Sun.COM return (ret);
43310923SEvan.Yan@Sun.COM }
43410923SEvan.Yan@Sun.COM
43510923SEvan.Yan@Sun.COM /* Control structure used to find a device in the devinfo tree */
43610923SEvan.Yan@Sun.COM struct pcie_hp_find_ctrl {
43710923SEvan.Yan@Sun.COM uint_t device;
43810923SEvan.Yan@Sun.COM uint_t function;
43910923SEvan.Yan@Sun.COM dev_info_t *dip;
44010923SEvan.Yan@Sun.COM };
44110923SEvan.Yan@Sun.COM
44210923SEvan.Yan@Sun.COM /*
44310923SEvan.Yan@Sun.COM * find a devinfo node with specified device and function number
44410923SEvan.Yan@Sun.COM * in the device tree under 'dip'
44510923SEvan.Yan@Sun.COM */
44610923SEvan.Yan@Sun.COM dev_info_t *
pcie_hp_devi_find(dev_info_t * dip,uint_t device,uint_t function)44710923SEvan.Yan@Sun.COM pcie_hp_devi_find(dev_info_t *dip, uint_t device, uint_t function)
44810923SEvan.Yan@Sun.COM {
44910923SEvan.Yan@Sun.COM struct pcie_hp_find_ctrl ctrl;
45010923SEvan.Yan@Sun.COM int count;
45110923SEvan.Yan@Sun.COM
45210923SEvan.Yan@Sun.COM ctrl.device = device;
45310923SEvan.Yan@Sun.COM ctrl.function = function;
45410923SEvan.Yan@Sun.COM ctrl.dip = NULL;
45510923SEvan.Yan@Sun.COM
45610923SEvan.Yan@Sun.COM ndi_devi_enter(dip, &count);
45710923SEvan.Yan@Sun.COM ddi_walk_devs(ddi_get_child(dip), pcie_hp_match_dev_func,
45810923SEvan.Yan@Sun.COM (void *)&ctrl);
45910923SEvan.Yan@Sun.COM ndi_devi_exit(dip, count);
46010923SEvan.Yan@Sun.COM
46110923SEvan.Yan@Sun.COM return (ctrl.dip);
46210923SEvan.Yan@Sun.COM }
46310923SEvan.Yan@Sun.COM
46410923SEvan.Yan@Sun.COM /*
46510923SEvan.Yan@Sun.COM * routine to create 'pci-occupant' property for a hotplug slot
46610923SEvan.Yan@Sun.COM */
46710923SEvan.Yan@Sun.COM void
pcie_hp_create_occupant_props(dev_info_t * dip,dev_t dev,int pci_dev)46810923SEvan.Yan@Sun.COM pcie_hp_create_occupant_props(dev_info_t *dip, dev_t dev, int pci_dev)
46910923SEvan.Yan@Sun.COM {
47010923SEvan.Yan@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
47110923SEvan.Yan@Sun.COM pcie_hp_ctrl_t *ctrl_p = (pcie_hp_ctrl_t *)bus_p->bus_hp_ctrl;
47210923SEvan.Yan@Sun.COM pcie_hp_slot_t *slotp;
47310923SEvan.Yan@Sun.COM pcie_hp_cn_cfg_t cn_cfg;
47410923SEvan.Yan@Sun.COM pcie_hp_occupant_info_t *occupant;
47510923SEvan.Yan@Sun.COM int circular, i;
47610923SEvan.Yan@Sun.COM
47710923SEvan.Yan@Sun.COM ndi_devi_enter(dip, &circular);
47810923SEvan.Yan@Sun.COM
47910923SEvan.Yan@Sun.COM if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) {
48010923SEvan.Yan@Sun.COM slotp = (ctrl_p && (pci_dev == 0)) ?
48110923SEvan.Yan@Sun.COM ctrl_p->hc_slots[pci_dev] : NULL;
48210923SEvan.Yan@Sun.COM } else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) {
48310923SEvan.Yan@Sun.COM if (ctrl_p) {
48410923SEvan.Yan@Sun.COM int slot_num;
48510923SEvan.Yan@Sun.COM
48610923SEvan.Yan@Sun.COM slot_num = (ctrl_p->hc_device_increases) ?
48710923SEvan.Yan@Sun.COM (pci_dev - ctrl_p->hc_device_start) :
48810923SEvan.Yan@Sun.COM (pci_dev + ctrl_p->hc_device_start);
48910923SEvan.Yan@Sun.COM
49010923SEvan.Yan@Sun.COM slotp = ctrl_p->hc_slots[slot_num];
49110923SEvan.Yan@Sun.COM } else {
49210923SEvan.Yan@Sun.COM slotp = NULL;
49310923SEvan.Yan@Sun.COM }
49410923SEvan.Yan@Sun.COM }
49510923SEvan.Yan@Sun.COM
49610923SEvan.Yan@Sun.COM if (slotp == NULL)
49710923SEvan.Yan@Sun.COM return;
49810923SEvan.Yan@Sun.COM
49910923SEvan.Yan@Sun.COM occupant = kmem_alloc(sizeof (pcie_hp_occupant_info_t), KM_SLEEP);
50010923SEvan.Yan@Sun.COM occupant->i = 0;
50110923SEvan.Yan@Sun.COM
50210923SEvan.Yan@Sun.COM cn_cfg.flag = B_FALSE;
50310923SEvan.Yan@Sun.COM cn_cfg.rv = NDI_SUCCESS;
50410923SEvan.Yan@Sun.COM cn_cfg.dip = NULL;
50510923SEvan.Yan@Sun.COM cn_cfg.slotp = (void *)slotp;
50610923SEvan.Yan@Sun.COM cn_cfg.cn_private = (void *)occupant;
50710923SEvan.Yan@Sun.COM
50810923SEvan.Yan@Sun.COM ddi_walk_devs(ddi_get_child(dip), pcie_hp_list_occupants,
50910923SEvan.Yan@Sun.COM (void *)&cn_cfg);
51010923SEvan.Yan@Sun.COM
51110923SEvan.Yan@Sun.COM if (occupant->i == 0) {
51210923SEvan.Yan@Sun.COM /* no occupants right now, need to create stub property */
51310923SEvan.Yan@Sun.COM char *c[] = { "" };
51410923SEvan.Yan@Sun.COM (void) ddi_prop_update_string_array(dev, dip, "pci-occupant",
51510923SEvan.Yan@Sun.COM c, 1);
51610923SEvan.Yan@Sun.COM } else {
51710923SEvan.Yan@Sun.COM (void) ddi_prop_update_string_array(dev, dip, "pci-occupant",
51810923SEvan.Yan@Sun.COM occupant->id, occupant->i);
51910923SEvan.Yan@Sun.COM }
52010923SEvan.Yan@Sun.COM
52110923SEvan.Yan@Sun.COM for (i = 0; i < occupant->i; i++)
52210923SEvan.Yan@Sun.COM kmem_free(occupant->id[i], sizeof (char[MAXPATHLEN]));
52310923SEvan.Yan@Sun.COM
52410923SEvan.Yan@Sun.COM kmem_free(occupant, sizeof (pcie_hp_occupant_info_t));
52510923SEvan.Yan@Sun.COM
52610923SEvan.Yan@Sun.COM ndi_devi_exit(dip, circular);
52710923SEvan.Yan@Sun.COM }
52810923SEvan.Yan@Sun.COM
52910923SEvan.Yan@Sun.COM /*
53010923SEvan.Yan@Sun.COM * routine to remove 'pci-occupant' property for a hotplug slot
53110923SEvan.Yan@Sun.COM */
53210923SEvan.Yan@Sun.COM void
pcie_hp_delete_occupant_props(dev_info_t * dip,dev_t dev)53310923SEvan.Yan@Sun.COM pcie_hp_delete_occupant_props(dev_info_t *dip, dev_t dev)
53410923SEvan.Yan@Sun.COM {
53510923SEvan.Yan@Sun.COM (void) ddi_prop_remove(dev, dip, "pci-occupant");
53610923SEvan.Yan@Sun.COM }
53710923SEvan.Yan@Sun.COM
53810923SEvan.Yan@Sun.COM /*
53910923SEvan.Yan@Sun.COM * general code to create a minor node, called from hotplug controller
54010923SEvan.Yan@Sun.COM * drivers.
54110923SEvan.Yan@Sun.COM */
54210923SEvan.Yan@Sun.COM int
pcie_create_minor_node(pcie_hp_ctrl_t * ctrl_p,int slot)54310923SEvan.Yan@Sun.COM pcie_create_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot)
54410923SEvan.Yan@Sun.COM {
54510923SEvan.Yan@Sun.COM dev_info_t *dip = ctrl_p->hc_dip;
54610923SEvan.Yan@Sun.COM pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[slot];
54710923SEvan.Yan@Sun.COM ddi_hp_cn_info_t *info_p = &slot_p->hs_info;
54810923SEvan.Yan@Sun.COM
54910923SEvan.Yan@Sun.COM if (ddi_create_minor_node(dip, info_p->cn_name,
55010923SEvan.Yan@Sun.COM S_IFCHR, slot_p->hs_minor,
55110923SEvan.Yan@Sun.COM DDI_NT_PCI_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
55210923SEvan.Yan@Sun.COM return (DDI_FAILURE);
55310923SEvan.Yan@Sun.COM }
55410923SEvan.Yan@Sun.COM
55510923SEvan.Yan@Sun.COM (void) ddi_prop_update_int(DDI_DEV_T_NONE,
55610923SEvan.Yan@Sun.COM dip, "ap-names", 1 << slot_p->hs_device_num);
55710923SEvan.Yan@Sun.COM
55810923SEvan.Yan@Sun.COM return (DDI_SUCCESS);
55910923SEvan.Yan@Sun.COM }
56010923SEvan.Yan@Sun.COM
56110923SEvan.Yan@Sun.COM /*
56210923SEvan.Yan@Sun.COM * general code to remove a minor node, called from hotplug controller
56310923SEvan.Yan@Sun.COM * drivers.
56410923SEvan.Yan@Sun.COM */
56510923SEvan.Yan@Sun.COM void
pcie_remove_minor_node(pcie_hp_ctrl_t * ctrl_p,int slot)56610923SEvan.Yan@Sun.COM pcie_remove_minor_node(pcie_hp_ctrl_t *ctrl_p, int slot)
56710923SEvan.Yan@Sun.COM {
56810923SEvan.Yan@Sun.COM ddi_remove_minor_node(ctrl_p->hc_dip,
56910923SEvan.Yan@Sun.COM ctrl_p->hc_slots[slot]->hs_info.cn_name);
57010923SEvan.Yan@Sun.COM }
57110923SEvan.Yan@Sun.COM
57210923SEvan.Yan@Sun.COM /*
57310923SEvan.Yan@Sun.COM * Local functions (called within this file)
57410923SEvan.Yan@Sun.COM */
57510923SEvan.Yan@Sun.COM
57610923SEvan.Yan@Sun.COM /*
57710923SEvan.Yan@Sun.COM * Register ports for all the children with device number device_num
57810923SEvan.Yan@Sun.COM */
57910923SEvan.Yan@Sun.COM static int
pcie_hp_register_ports_for_dev(dev_info_t * dip,int device_num)58010923SEvan.Yan@Sun.COM pcie_hp_register_ports_for_dev(dev_info_t *dip, int device_num)
58110923SEvan.Yan@Sun.COM {
58210923SEvan.Yan@Sun.COM dev_info_t *cdip;
58310923SEvan.Yan@Sun.COM int rv;
58410923SEvan.Yan@Sun.COM
58510923SEvan.Yan@Sun.COM for (cdip = ddi_get_child(dip); cdip;
58610923SEvan.Yan@Sun.COM cdip = ddi_get_next_sibling(cdip)) {
58710923SEvan.Yan@Sun.COM if (pcie_hp_match_dev(cdip, device_num)) {
58810923SEvan.Yan@Sun.COM /*
58910923SEvan.Yan@Sun.COM * Found the newly probed device under the
59010923SEvan.Yan@Sun.COM * current slot. Register a port for it.
59110923SEvan.Yan@Sun.COM */
59210923SEvan.Yan@Sun.COM if ((rv = pcie_hp_register_port(cdip, dip, NULL))
59310923SEvan.Yan@Sun.COM != DDI_SUCCESS)
59410923SEvan.Yan@Sun.COM return (rv);
59510923SEvan.Yan@Sun.COM } else {
59610923SEvan.Yan@Sun.COM continue;
59710923SEvan.Yan@Sun.COM }
59810923SEvan.Yan@Sun.COM }
59910923SEvan.Yan@Sun.COM
60010923SEvan.Yan@Sun.COM return (DDI_SUCCESS);
60110923SEvan.Yan@Sun.COM }
60210923SEvan.Yan@Sun.COM
60310923SEvan.Yan@Sun.COM /*
60410923SEvan.Yan@Sun.COM * Unregister ports of a pci bridge dip, get called from ndi_hp_walk_cn()
60510923SEvan.Yan@Sun.COM *
60610923SEvan.Yan@Sun.COM * If connector_num is specified, then unregister the slot's dependent ports
60710923SEvan.Yan@Sun.COM * only; Otherwise, unregister all ports of a pci bridge dip.
60810923SEvan.Yan@Sun.COM */
60910923SEvan.Yan@Sun.COM static int
pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t * info,void * arg)61010923SEvan.Yan@Sun.COM pcie_hp_unregister_ports_cb(ddi_hp_cn_info_t *info, void *arg)
61110923SEvan.Yan@Sun.COM {
61210923SEvan.Yan@Sun.COM pcie_hp_unreg_port_t *unreg_arg = (pcie_hp_unreg_port_t *)arg;
61310923SEvan.Yan@Sun.COM dev_info_t *dip = unreg_arg->nexus_dip;
61410923SEvan.Yan@Sun.COM int rv = NDI_SUCCESS;
61510923SEvan.Yan@Sun.COM
61610923SEvan.Yan@Sun.COM if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT) {
61710923SEvan.Yan@Sun.COM unreg_arg->rv = rv;
61810923SEvan.Yan@Sun.COM return (DDI_WALK_CONTINUE);
61910923SEvan.Yan@Sun.COM }
62010923SEvan.Yan@Sun.COM
62110923SEvan.Yan@Sun.COM if (unreg_arg->connector_num != DDI_HP_CN_NUM_NONE) {
62210923SEvan.Yan@Sun.COM /* Unregister ports for all unprobed devices under a slot. */
62310923SEvan.Yan@Sun.COM if (unreg_arg->connector_num == info->cn_num_dpd_on) {
62410923SEvan.Yan@Sun.COM
62510923SEvan.Yan@Sun.COM rv = ndi_hp_unregister(dip, info->cn_name);
62610923SEvan.Yan@Sun.COM }
62710923SEvan.Yan@Sun.COM } else {
62810923SEvan.Yan@Sun.COM
62910923SEvan.Yan@Sun.COM /* Unregister all ports of a pci bridge dip. */
63010923SEvan.Yan@Sun.COM rv = ndi_hp_unregister(dip, info->cn_name);
63110923SEvan.Yan@Sun.COM }
63210923SEvan.Yan@Sun.COM
63310923SEvan.Yan@Sun.COM unreg_arg->rv = rv;
63410923SEvan.Yan@Sun.COM if (rv == NDI_SUCCESS)
63510923SEvan.Yan@Sun.COM return (DDI_WALK_CONTINUE);
63610923SEvan.Yan@Sun.COM else
63710923SEvan.Yan@Sun.COM return (DDI_WALK_TERMINATE);
63810923SEvan.Yan@Sun.COM }
63910923SEvan.Yan@Sun.COM
64010923SEvan.Yan@Sun.COM /*
64110923SEvan.Yan@Sun.COM * Find a port according to cn_name and get the port's state.
64210923SEvan.Yan@Sun.COM */
64310923SEvan.Yan@Sun.COM static int
pcie_hp_get_port_state(ddi_hp_cn_info_t * info,void * arg)64410923SEvan.Yan@Sun.COM pcie_hp_get_port_state(ddi_hp_cn_info_t *info, void *arg)
64510923SEvan.Yan@Sun.COM {
64610923SEvan.Yan@Sun.COM pcie_hp_port_state_t *port = (pcie_hp_port_state_t *)arg;
64710923SEvan.Yan@Sun.COM
64810923SEvan.Yan@Sun.COM if (info->cn_type != DDI_HP_CN_TYPE_VIRTUAL_PORT)
64910923SEvan.Yan@Sun.COM return (DDI_WALK_CONTINUE);
65010923SEvan.Yan@Sun.COM
65110923SEvan.Yan@Sun.COM if (strcmp(info->cn_name, port->cn_name) == 0) {
65210923SEvan.Yan@Sun.COM /* Matched. */
65310923SEvan.Yan@Sun.COM port->cn_state = info->cn_state;
65410923SEvan.Yan@Sun.COM port->rv = DDI_SUCCESS;
65510923SEvan.Yan@Sun.COM
65610923SEvan.Yan@Sun.COM return (DDI_WALK_TERMINATE);
65710923SEvan.Yan@Sun.COM }
65810923SEvan.Yan@Sun.COM
65910923SEvan.Yan@Sun.COM return (DDI_WALK_CONTINUE);
66010923SEvan.Yan@Sun.COM }
66110923SEvan.Yan@Sun.COM
66210923SEvan.Yan@Sun.COM /*
66310923SEvan.Yan@Sun.COM * Find the physical slot with the given device number;
66410923SEvan.Yan@Sun.COM * return the slot if found.
66510923SEvan.Yan@Sun.COM */
66610923SEvan.Yan@Sun.COM static pcie_hp_slot_t *
pcie_find_physical_slot(dev_info_t * dip,int dev_num)66710923SEvan.Yan@Sun.COM pcie_find_physical_slot(dev_info_t *dip, int dev_num)
66810923SEvan.Yan@Sun.COM {
66910923SEvan.Yan@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
67010923SEvan.Yan@Sun.COM pcie_hp_ctrl_t *ctrl = PCIE_GET_HP_CTRL(dip);
67110923SEvan.Yan@Sun.COM
67210923SEvan.Yan@Sun.COM if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
67310923SEvan.Yan@Sun.COM /* PCIe has only one slot */
67410923SEvan.Yan@Sun.COM return (dev_num == 0) ? (ctrl->hc_slots[0]) : (NULL);
67510923SEvan.Yan@Sun.COM } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
67610923SEvan.Yan@Sun.COM for (int slot = 0; slot < ctrl->hc_num_slots_impl; slot++) {
67710923SEvan.Yan@Sun.COM if (ctrl->hc_slots[slot]->hs_device_num == dev_num) {
67810923SEvan.Yan@Sun.COM /* found */
67910923SEvan.Yan@Sun.COM return (ctrl->hc_slots[slot]);
68010923SEvan.Yan@Sun.COM }
68110923SEvan.Yan@Sun.COM }
68210923SEvan.Yan@Sun.COM }
68310923SEvan.Yan@Sun.COM
68410923SEvan.Yan@Sun.COM return (NULL);
68510923SEvan.Yan@Sun.COM }
68610923SEvan.Yan@Sun.COM
68710923SEvan.Yan@Sun.COM /*
68810923SEvan.Yan@Sun.COM * setup slot name/slot-number info for the port which is being registered.
68910923SEvan.Yan@Sun.COM */
69010923SEvan.Yan@Sun.COM static int
pcie_hp_create_port_name_num(dev_info_t * dip,ddi_hp_cn_info_t * cn_info)69110923SEvan.Yan@Sun.COM pcie_hp_create_port_name_num(dev_info_t *dip, ddi_hp_cn_info_t *cn_info)
69210923SEvan.Yan@Sun.COM {
69310923SEvan.Yan@Sun.COM int ret, dev_num, func_num, name_len;
69410923SEvan.Yan@Sun.COM dev_info_t *pdip = ddi_get_parent(dip);
69510923SEvan.Yan@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2BUS(pdip);
69610923SEvan.Yan@Sun.COM pcie_hp_slot_t *slot;
69710923SEvan.Yan@Sun.COM pcie_req_id_t bdf;
69810923SEvan.Yan@Sun.COM char tmp[PCIE_HP_DEV_FUNC_NUM_STRING_LEN];
69910923SEvan.Yan@Sun.COM
70010923SEvan.Yan@Sun.COM ret = pcie_get_bdf_from_dip(dip, &bdf);
70110923SEvan.Yan@Sun.COM if (ret != DDI_SUCCESS) {
70210923SEvan.Yan@Sun.COM return (ret);
70310923SEvan.Yan@Sun.COM }
70410923SEvan.Yan@Sun.COM if (PCIE_IS_RP(bus_p) || PCIE_IS_SWD(bus_p) ||
70510923SEvan.Yan@Sun.COM PCIE_IS_PCI2PCIE(bus_p)) {
70610923SEvan.Yan@Sun.COM /*
70710923SEvan.Yan@Sun.COM * It is under a PCIe device, devcie number is always 0;
70810923SEvan.Yan@Sun.COM * function number might > 8 in ARI supported case.
70910923SEvan.Yan@Sun.COM */
71010923SEvan.Yan@Sun.COM dev_num = 0;
71110923SEvan.Yan@Sun.COM func_num = (bdf & ((~PCI_REG_BUS_M) >> 8));
71210923SEvan.Yan@Sun.COM } else {
71310923SEvan.Yan@Sun.COM dev_num = (bdf & (PCI_REG_DEV_M >> 8)) >> 3;
71410923SEvan.Yan@Sun.COM func_num = bdf & (PCI_REG_FUNC_M >> 8);
71510923SEvan.Yan@Sun.COM }
71610923SEvan.Yan@Sun.COM /*
71710923SEvan.Yan@Sun.COM * The string length of dev_num and func_num must be no longer than 4
71810923SEvan.Yan@Sun.COM * including the string end mark. (With ARI case considered, e.g.,
71910923SEvan.Yan@Sun.COM * dev_num=0x0, func_num=0xff.)
72010923SEvan.Yan@Sun.COM */
72110923SEvan.Yan@Sun.COM (void) snprintf(tmp, PCIE_HP_DEV_FUNC_NUM_STRING_LEN, "%x%x",
72210923SEvan.Yan@Sun.COM dev_num, func_num);
72310923SEvan.Yan@Sun.COM /*
72410923SEvan.Yan@Sun.COM * Calculate the length of cn_name.
72510923SEvan.Yan@Sun.COM * The format of pci port name is: pci.d,f
72610923SEvan.Yan@Sun.COM * d stands for dev_num, f stands for func_num. So the length of the
72710923SEvan.Yan@Sun.COM * name string can be calculated as following.
72810923SEvan.Yan@Sun.COM */
72910923SEvan.Yan@Sun.COM name_len = strlen(tmp) + PCIE_HP_PORT_NAME_STRING_LEN + 1;
73010923SEvan.Yan@Sun.COM
73110923SEvan.Yan@Sun.COM cn_info->cn_name = (char *)kmem_zalloc(name_len, KM_SLEEP);
73210923SEvan.Yan@Sun.COM (void) snprintf(cn_info->cn_name, name_len, "pci.%x,%x",
73310923SEvan.Yan@Sun.COM dev_num, func_num);
73410923SEvan.Yan@Sun.COM cn_info->cn_num = (dev_num << 8) | func_num;
73510923SEvan.Yan@Sun.COM slot = pcie_find_physical_slot(pdip, dev_num);
73610923SEvan.Yan@Sun.COM
73710923SEvan.Yan@Sun.COM cn_info->cn_num_dpd_on = slot ?
73810923SEvan.Yan@Sun.COM slot->hs_info.cn_num : DDI_HP_CN_NUM_NONE;
73910923SEvan.Yan@Sun.COM
74010923SEvan.Yan@Sun.COM return (DDI_SUCCESS);
74110923SEvan.Yan@Sun.COM }
74210923SEvan.Yan@Sun.COM
74310923SEvan.Yan@Sun.COM /*
74410923SEvan.Yan@Sun.COM * Extract device and function number from port name, whose format is
74510923SEvan.Yan@Sun.COM * something like 'pci.1,0'
74610923SEvan.Yan@Sun.COM */
74710923SEvan.Yan@Sun.COM static int
pcie_hp_get_df_from_port_name(char * cn_name,int * dev_num,int * func_num)74810923SEvan.Yan@Sun.COM pcie_hp_get_df_from_port_name(char *cn_name, int *dev_num, int *func_num)
74910923SEvan.Yan@Sun.COM {
75010923SEvan.Yan@Sun.COM int name_len, ret;
75110923SEvan.Yan@Sun.COM long d, f;
75210923SEvan.Yan@Sun.COM char *sp;
75310923SEvan.Yan@Sun.COM
75410923SEvan.Yan@Sun.COM /* some checks for the input name */
75510923SEvan.Yan@Sun.COM name_len = strlen(cn_name);
75610923SEvan.Yan@Sun.COM if ((name_len <= PCIE_HP_PORT_NAME_STRING_LEN) ||
75710923SEvan.Yan@Sun.COM (name_len > (PCIE_HP_PORT_NAME_STRING_LEN +
75810923SEvan.Yan@Sun.COM PCIE_HP_DEV_FUNC_NUM_STRING_LEN - 1)) ||
75910923SEvan.Yan@Sun.COM (strncmp("pci.", cn_name, 4) != 0)) {
76010923SEvan.Yan@Sun.COM return (DDI_EINVAL);
76110923SEvan.Yan@Sun.COM }
76210923SEvan.Yan@Sun.COM ret = ddi_strtol(cn_name + 4, &sp, 10, &d);
76310923SEvan.Yan@Sun.COM if (ret != DDI_SUCCESS)
76410923SEvan.Yan@Sun.COM return (ret);
76510923SEvan.Yan@Sun.COM
76610923SEvan.Yan@Sun.COM if (strncmp(",", sp, 1) != 0)
76710923SEvan.Yan@Sun.COM return (DDI_EINVAL);
76810923SEvan.Yan@Sun.COM
76910923SEvan.Yan@Sun.COM ret = ddi_strtol(sp + 1, NULL, 10, &f);
77010923SEvan.Yan@Sun.COM if (ret != DDI_SUCCESS)
77110923SEvan.Yan@Sun.COM return (ret);
77210923SEvan.Yan@Sun.COM *dev_num = (int)d;
77310923SEvan.Yan@Sun.COM *func_num = (int)f;
77410923SEvan.Yan@Sun.COM
77510923SEvan.Yan@Sun.COM return (ret);
77610923SEvan.Yan@Sun.COM }
77710923SEvan.Yan@Sun.COM
77810923SEvan.Yan@Sun.COM /*
77910923SEvan.Yan@Sun.COM * Check/copy cn_name and set connection numbers.
78010923SEvan.Yan@Sun.COM * If it is a valid name, then setup cn_info for the newly created port.
78110923SEvan.Yan@Sun.COM */
78210923SEvan.Yan@Sun.COM static int
pcie_hp_setup_port_name_num(dev_info_t * pdip,char * cn_name,ddi_hp_cn_info_t * cn_info)78310923SEvan.Yan@Sun.COM pcie_hp_setup_port_name_num(dev_info_t *pdip, char *cn_name,
78410923SEvan.Yan@Sun.COM ddi_hp_cn_info_t *cn_info)
78510923SEvan.Yan@Sun.COM {
78610923SEvan.Yan@Sun.COM int dev_num, func_num, ret;
78710923SEvan.Yan@Sun.COM pcie_hp_slot_t *slot;
78810923SEvan.Yan@Sun.COM
78910923SEvan.Yan@Sun.COM if ((ret = pcie_hp_get_df_from_port_name(cn_name, &dev_num, &func_num))
79010923SEvan.Yan@Sun.COM != DDI_SUCCESS)
79110923SEvan.Yan@Sun.COM return (ret);
79210923SEvan.Yan@Sun.COM
79310923SEvan.Yan@Sun.COM if (pcie_hp_check_hardware_existence(pdip, dev_num, func_num) ==
79410923SEvan.Yan@Sun.COM DDI_SUCCESS) {
79510923SEvan.Yan@Sun.COM cn_info->cn_state = DDI_HP_CN_STATE_PRESENT;
79610923SEvan.Yan@Sun.COM } else {
79710923SEvan.Yan@Sun.COM cn_info->cn_state = DDI_HP_CN_STATE_EMPTY;
79810923SEvan.Yan@Sun.COM }
79910923SEvan.Yan@Sun.COM
80010923SEvan.Yan@Sun.COM cn_info->cn_name = ddi_strdup(cn_name, KM_SLEEP);
80110923SEvan.Yan@Sun.COM cn_info->cn_num = (dev_num << 8) | func_num;
80210923SEvan.Yan@Sun.COM
80310923SEvan.Yan@Sun.COM slot = pcie_find_physical_slot(pdip, dev_num);
80410923SEvan.Yan@Sun.COM if (slot) {
80510923SEvan.Yan@Sun.COM cn_info->cn_num_dpd_on = slot->hs_info.cn_num;
80610923SEvan.Yan@Sun.COM } else {
80710923SEvan.Yan@Sun.COM cn_info->cn_num_dpd_on = DDI_HP_CN_NUM_NONE;
80810923SEvan.Yan@Sun.COM }
80910923SEvan.Yan@Sun.COM return (DDI_SUCCESS);
81010923SEvan.Yan@Sun.COM }
81110923SEvan.Yan@Sun.COM
81210923SEvan.Yan@Sun.COM static int
ndi2ddi(int n)81310923SEvan.Yan@Sun.COM ndi2ddi(int n)
81410923SEvan.Yan@Sun.COM {
81510923SEvan.Yan@Sun.COM int ret;
81610923SEvan.Yan@Sun.COM
81710923SEvan.Yan@Sun.COM switch (n) {
81810923SEvan.Yan@Sun.COM case NDI_SUCCESS:
81910923SEvan.Yan@Sun.COM ret = DDI_SUCCESS;
82010923SEvan.Yan@Sun.COM break;
82110923SEvan.Yan@Sun.COM case NDI_NOMEM:
82210923SEvan.Yan@Sun.COM ret = DDI_ENOMEM;
82310923SEvan.Yan@Sun.COM break;
82410923SEvan.Yan@Sun.COM case NDI_BUSY:
82510923SEvan.Yan@Sun.COM ret = DDI_EBUSY;
82610923SEvan.Yan@Sun.COM break;
82710923SEvan.Yan@Sun.COM case NDI_EINVAL:
82810923SEvan.Yan@Sun.COM ret = DDI_EINVAL;
82910923SEvan.Yan@Sun.COM break;
83010923SEvan.Yan@Sun.COM case NDI_ENOTSUP:
83110923SEvan.Yan@Sun.COM ret = DDI_ENOTSUP;
83210923SEvan.Yan@Sun.COM break;
83310923SEvan.Yan@Sun.COM case NDI_FAILURE:
83410923SEvan.Yan@Sun.COM default:
83510923SEvan.Yan@Sun.COM ret = DDI_FAILURE;
83610923SEvan.Yan@Sun.COM break;
83710923SEvan.Yan@Sun.COM }
83810923SEvan.Yan@Sun.COM return (ret);
83910923SEvan.Yan@Sun.COM }
84010923SEvan.Yan@Sun.COM
84110923SEvan.Yan@Sun.COM /*
84210923SEvan.Yan@Sun.COM * Common routine to create and register a new port
84310923SEvan.Yan@Sun.COM *
84410923SEvan.Yan@Sun.COM * Create an empty port if dip is NULL, and cn_name needs to be specified in
84510923SEvan.Yan@Sun.COM * this case. Otherwise, create a port mapping to the specified dip, and cn_name
84610923SEvan.Yan@Sun.COM * is not needed in this case.
84710923SEvan.Yan@Sun.COM */
84810923SEvan.Yan@Sun.COM static int
pcie_hp_register_port(dev_info_t * dip,dev_info_t * pdip,char * cn_name)84910923SEvan.Yan@Sun.COM pcie_hp_register_port(dev_info_t *dip, dev_info_t *pdip, char *cn_name)
85010923SEvan.Yan@Sun.COM {
85110923SEvan.Yan@Sun.COM ddi_hp_cn_info_t *cn_info;
85210923SEvan.Yan@Sun.COM int ret;
85310923SEvan.Yan@Sun.COM
85410923SEvan.Yan@Sun.COM ASSERT((dip == NULL) != (cn_name == NULL));
85510923SEvan.Yan@Sun.COM cn_info = kmem_zalloc(sizeof (ddi_hp_cn_info_t), KM_SLEEP);
85610923SEvan.Yan@Sun.COM if (dip != NULL)
85710923SEvan.Yan@Sun.COM ret = pcie_hp_create_port_name_num(dip, cn_info);
85810923SEvan.Yan@Sun.COM else
85910923SEvan.Yan@Sun.COM ret = pcie_hp_setup_port_name_num(pdip, cn_name, cn_info);
86010923SEvan.Yan@Sun.COM
86110923SEvan.Yan@Sun.COM if (ret != DDI_SUCCESS) {
86210923SEvan.Yan@Sun.COM kmem_free(cn_info, sizeof (ddi_hp_cn_info_t));
86310923SEvan.Yan@Sun.COM return (ret);
86410923SEvan.Yan@Sun.COM }
86510923SEvan.Yan@Sun.COM
86610923SEvan.Yan@Sun.COM cn_info->cn_child = dip;
86710923SEvan.Yan@Sun.COM cn_info->cn_type = DDI_HP_CN_TYPE_VIRTUAL_PORT;
86810923SEvan.Yan@Sun.COM cn_info->cn_type_str = DDI_HP_CN_TYPE_STR_PORT;
86910923SEvan.Yan@Sun.COM
87010923SEvan.Yan@Sun.COM ret = ndi_hp_register(pdip, cn_info);
87110923SEvan.Yan@Sun.COM
87210923SEvan.Yan@Sun.COM kmem_free(cn_info->cn_name, strlen(cn_info->cn_name) + 1);
87310923SEvan.Yan@Sun.COM kmem_free(cn_info, sizeof (ddi_hp_cn_info_t));
87410923SEvan.Yan@Sun.COM
87510923SEvan.Yan@Sun.COM return (ndi2ddi(ret));
87610923SEvan.Yan@Sun.COM }
87710923SEvan.Yan@Sun.COM
87810923SEvan.Yan@Sun.COM /* Check if there is a piece of hardware exist corresponding to the cn_name */
87910923SEvan.Yan@Sun.COM static int
pcie_hp_check_hardware_existence(dev_info_t * dip,int dev_num,int func_num)88010923SEvan.Yan@Sun.COM pcie_hp_check_hardware_existence(dev_info_t *dip, int dev_num, int func_num)
88110923SEvan.Yan@Sun.COM {
88210923SEvan.Yan@Sun.COM
88310923SEvan.Yan@Sun.COM /*
88410923SEvan.Yan@Sun.COM * VHPTODO:
88510923SEvan.Yan@Sun.COM * According to device and function number, check if there is a hardware
88610923SEvan.Yan@Sun.COM * device exists. Currently, this function can not be reached before
88710923SEvan.Yan@Sun.COM * we enable state transition to or from "Port-Empty" or "Port-Present"
88810923SEvan.Yan@Sun.COM * states. When the pci device type project is integrated, we are going
88910923SEvan.Yan@Sun.COM * to call the pci config space access interfaces introduced by it.
89010923SEvan.Yan@Sun.COM */
89110923SEvan.Yan@Sun.COM _NOTE(ARGUNUSED(dip, dev_num, func_num));
89210923SEvan.Yan@Sun.COM
89310923SEvan.Yan@Sun.COM return (DDI_SUCCESS);
89410923SEvan.Yan@Sun.COM }
89510923SEvan.Yan@Sun.COM
89610923SEvan.Yan@Sun.COM /*
89710923SEvan.Yan@Sun.COM * Dispatch hotplug commands to different hotplug controller drivers, including
89810923SEvan.Yan@Sun.COM * physical and virtual hotplug operations.
89910923SEvan.Yan@Sun.COM */
90010923SEvan.Yan@Sun.COM /* ARGSUSED */
90110923SEvan.Yan@Sun.COM int
pcie_hp_common_ops(dev_info_t * dip,char * cn_name,ddi_hp_op_t op,void * arg,void * result)90210923SEvan.Yan@Sun.COM pcie_hp_common_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
90310923SEvan.Yan@Sun.COM void *arg, void *result)
90410923SEvan.Yan@Sun.COM {
90510923SEvan.Yan@Sun.COM pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
90610923SEvan.Yan@Sun.COM int ret = DDI_SUCCESS;
90710923SEvan.Yan@Sun.COM
90810923SEvan.Yan@Sun.COM PCIE_DBG("pcie_hp_common_ops: dip=%p cn_name=%s op=%x arg=%p\n",
90910923SEvan.Yan@Sun.COM dip, cn_name, op, arg);
91010923SEvan.Yan@Sun.COM
91110923SEvan.Yan@Sun.COM switch (op) {
91210923SEvan.Yan@Sun.COM case DDI_HPOP_CN_CREATE_PORT:
91310923SEvan.Yan@Sun.COM {
91410923SEvan.Yan@Sun.COM /* create an empty port */
91510923SEvan.Yan@Sun.COM return (pcie_hp_register_port(NULL, dip, cn_name));
91610923SEvan.Yan@Sun.COM }
91710923SEvan.Yan@Sun.COM case DDI_HPOP_CN_CHANGE_STATE:
91810923SEvan.Yan@Sun.COM {
91910923SEvan.Yan@Sun.COM ddi_hp_cn_state_t curr_state;
92010923SEvan.Yan@Sun.COM ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
92110923SEvan.Yan@Sun.COM pcie_hp_port_state_t state_arg;
92210923SEvan.Yan@Sun.COM
92310923SEvan.Yan@Sun.COM if (target_state < DDI_HP_CN_STATE_PORT_EMPTY) {
92410923SEvan.Yan@Sun.COM /* this is for physical slot state change */
92510923SEvan.Yan@Sun.COM break;
92610923SEvan.Yan@Sun.COM }
92710923SEvan.Yan@Sun.COM PCIE_DBG("pcie_hp_common_ops: change port state"
92810923SEvan.Yan@Sun.COM " dip=%p cn_name=%s"
92910923SEvan.Yan@Sun.COM " op=%x arg=%p\n", (void *)dip, cn_name, op, arg);
93010923SEvan.Yan@Sun.COM
93110923SEvan.Yan@Sun.COM state_arg.rv = DDI_FAILURE;
93210923SEvan.Yan@Sun.COM state_arg.cn_name = cn_name;
93310923SEvan.Yan@Sun.COM ndi_hp_walk_cn(dip, pcie_hp_get_port_state, &state_arg);
93410923SEvan.Yan@Sun.COM if (state_arg.rv != DDI_SUCCESS) {
93510923SEvan.Yan@Sun.COM /* can not find the port */
93610923SEvan.Yan@Sun.COM return (DDI_EINVAL);
93710923SEvan.Yan@Sun.COM }
93810923SEvan.Yan@Sun.COM curr_state = state_arg.cn_state;
93910923SEvan.Yan@Sun.COM /*
94010923SEvan.Yan@Sun.COM * Check if this is for changing port's state: change to/from
94110923SEvan.Yan@Sun.COM * PORT_EMPTY/PRESENT states.
94210923SEvan.Yan@Sun.COM */
94310923SEvan.Yan@Sun.COM if (curr_state < target_state) {
94410923SEvan.Yan@Sun.COM /* Upgrade state */
94510923SEvan.Yan@Sun.COM switch (curr_state) {
94610923SEvan.Yan@Sun.COM case DDI_HP_CN_STATE_PORT_EMPTY:
94710923SEvan.Yan@Sun.COM if (target_state ==
94810923SEvan.Yan@Sun.COM DDI_HP_CN_STATE_PORT_PRESENT) {
94910923SEvan.Yan@Sun.COM int dev_num, func_num;
95010923SEvan.Yan@Sun.COM
95110923SEvan.Yan@Sun.COM ret = pcie_hp_get_df_from_port_name(
95210923SEvan.Yan@Sun.COM cn_name, &dev_num, &func_num);
95310923SEvan.Yan@Sun.COM if (ret != DDI_SUCCESS)
95410923SEvan.Yan@Sun.COM goto port_state_done;
95510923SEvan.Yan@Sun.COM
95610923SEvan.Yan@Sun.COM ret = pcie_hp_check_hardware_existence(
95710923SEvan.Yan@Sun.COM dip, dev_num, func_num);
95810923SEvan.Yan@Sun.COM } else if (target_state ==
95910923SEvan.Yan@Sun.COM DDI_HP_CN_STATE_OFFLINE) {
96010923SEvan.Yan@Sun.COM ret = pcie_read_only_probe(dip,
96110923SEvan.Yan@Sun.COM cn_name, (dev_info_t **)result);
96210923SEvan.Yan@Sun.COM } else
96310923SEvan.Yan@Sun.COM ret = DDI_EINVAL;
96410923SEvan.Yan@Sun.COM
96510923SEvan.Yan@Sun.COM goto port_state_done;
96610923SEvan.Yan@Sun.COM case DDI_HP_CN_STATE_PORT_PRESENT:
96710923SEvan.Yan@Sun.COM if (target_state ==
96810923SEvan.Yan@Sun.COM DDI_HP_CN_STATE_OFFLINE)
96910923SEvan.Yan@Sun.COM ret = pcie_read_only_probe(dip,
97010923SEvan.Yan@Sun.COM cn_name, (dev_info_t **)result);
97110923SEvan.Yan@Sun.COM else
97210923SEvan.Yan@Sun.COM ret = DDI_EINVAL;
97310923SEvan.Yan@Sun.COM
97410923SEvan.Yan@Sun.COM goto port_state_done;
97510923SEvan.Yan@Sun.COM default:
97610923SEvan.Yan@Sun.COM ASSERT("unexpected state");
97710923SEvan.Yan@Sun.COM }
97810923SEvan.Yan@Sun.COM } else {
97910923SEvan.Yan@Sun.COM /* Downgrade state */
98010923SEvan.Yan@Sun.COM switch (curr_state) {
98110923SEvan.Yan@Sun.COM case DDI_HP_CN_STATE_PORT_PRESENT:
98210923SEvan.Yan@Sun.COM {
98310923SEvan.Yan@Sun.COM int dev_num, func_num;
98410923SEvan.Yan@Sun.COM
98510923SEvan.Yan@Sun.COM ret = pcie_hp_get_df_from_port_name(cn_name,
98610923SEvan.Yan@Sun.COM &dev_num, &func_num);
98710923SEvan.Yan@Sun.COM if (ret != DDI_SUCCESS)
98810923SEvan.Yan@Sun.COM goto port_state_done;
98910923SEvan.Yan@Sun.COM
99010923SEvan.Yan@Sun.COM ret = pcie_hp_check_hardware_existence(dip,
99110923SEvan.Yan@Sun.COM dev_num, func_num);
99210923SEvan.Yan@Sun.COM
99310923SEvan.Yan@Sun.COM goto port_state_done;
99410923SEvan.Yan@Sun.COM }
99510923SEvan.Yan@Sun.COM case DDI_HP_CN_STATE_OFFLINE:
99610923SEvan.Yan@Sun.COM ret = pcie_read_only_unprobe(dip, cn_name);
99710923SEvan.Yan@Sun.COM
99810923SEvan.Yan@Sun.COM goto port_state_done;
99910923SEvan.Yan@Sun.COM default:
100010923SEvan.Yan@Sun.COM ASSERT("unexpected state");
100110923SEvan.Yan@Sun.COM }
100210923SEvan.Yan@Sun.COM }
100310923SEvan.Yan@Sun.COM port_state_done:
100410923SEvan.Yan@Sun.COM *(ddi_hp_cn_state_t *)result = curr_state;
100510923SEvan.Yan@Sun.COM return (ret);
100610923SEvan.Yan@Sun.COM }
100710923SEvan.Yan@Sun.COM default:
100810923SEvan.Yan@Sun.COM break;
100910923SEvan.Yan@Sun.COM }
101010923SEvan.Yan@Sun.COM
101110923SEvan.Yan@Sun.COM if (PCIE_IS_PCIE_HOTPLUG_CAPABLE(bus_p)) {
101210923SEvan.Yan@Sun.COM /* PCIe hotplug */
101310923SEvan.Yan@Sun.COM ret = pciehpc_hp_ops(dip, cn_name, op, arg, result);
101410923SEvan.Yan@Sun.COM } else if (PCIE_IS_PCI_HOTPLUG_CAPABLE(bus_p)) {
101510923SEvan.Yan@Sun.COM /* PCI SHPC hotplug */
101610923SEvan.Yan@Sun.COM ret = pcishpc_hp_ops(dip, cn_name, op, arg, result);
101710923SEvan.Yan@Sun.COM } else {
101810923SEvan.Yan@Sun.COM cmn_err(CE_WARN, "pcie_hp_common_ops: op is not supported."
101910923SEvan.Yan@Sun.COM " dip=%p cn_name=%s"
102010923SEvan.Yan@Sun.COM " op=%x arg=%p\n", (void *)dip, cn_name, op, arg);
102110923SEvan.Yan@Sun.COM ret = DDI_ENOTSUP;
102210923SEvan.Yan@Sun.COM }
102310923SEvan.Yan@Sun.COM
102410923SEvan.Yan@Sun.COM #if defined(__i386) || defined(__amd64)
102510923SEvan.Yan@Sun.COM /*
102610923SEvan.Yan@Sun.COM * like in attach, since hotplugging can change error registers,
102710923SEvan.Yan@Sun.COM * we need to ensure that the proper bits are set on this port
102810923SEvan.Yan@Sun.COM * after a configure operation
102910923SEvan.Yan@Sun.COM */
103010923SEvan.Yan@Sun.COM if ((ret == DDI_SUCCESS) && (op == DDI_HPOP_CN_CHANGE_STATE) &&
103110923SEvan.Yan@Sun.COM (*(ddi_hp_cn_state_t *)arg == DDI_HP_CN_STATE_ENABLED))
103210923SEvan.Yan@Sun.COM pcieb_intel_error_workaround(dip);
103310923SEvan.Yan@Sun.COM #endif
103410923SEvan.Yan@Sun.COM
103510923SEvan.Yan@Sun.COM return (ret);
103610923SEvan.Yan@Sun.COM }
103710923SEvan.Yan@Sun.COM
103810923SEvan.Yan@Sun.COM /*
103910923SEvan.Yan@Sun.COM * pcie_hp_match_dev_func:
104010923SEvan.Yan@Sun.COM * Match dip's PCI device number and function number with input ones.
104110923SEvan.Yan@Sun.COM */
104210923SEvan.Yan@Sun.COM static int
pcie_hp_match_dev_func(dev_info_t * dip,void * hdl)104310923SEvan.Yan@Sun.COM pcie_hp_match_dev_func(dev_info_t *dip, void *hdl)
104410923SEvan.Yan@Sun.COM {
104510923SEvan.Yan@Sun.COM struct pcie_hp_find_ctrl *ctrl = (struct pcie_hp_find_ctrl *)hdl;
104610923SEvan.Yan@Sun.COM pci_regspec_t *pci_rp;
104710923SEvan.Yan@Sun.COM int length;
104810923SEvan.Yan@Sun.COM int pci_dev, pci_func;
104910923SEvan.Yan@Sun.COM
105010923SEvan.Yan@Sun.COM if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
105110923SEvan.Yan@Sun.COM "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
105210923SEvan.Yan@Sun.COM ctrl->dip = NULL;
105310923SEvan.Yan@Sun.COM return (DDI_WALK_TERMINATE);
105410923SEvan.Yan@Sun.COM }
105510923SEvan.Yan@Sun.COM
105610923SEvan.Yan@Sun.COM /* get the PCI device address info */
105710923SEvan.Yan@Sun.COM pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
105810923SEvan.Yan@Sun.COM pci_func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
105910923SEvan.Yan@Sun.COM
106010923SEvan.Yan@Sun.COM /*
106110923SEvan.Yan@Sun.COM * free the memory allocated by ddi_prop_lookup_int_array
106210923SEvan.Yan@Sun.COM */
106310923SEvan.Yan@Sun.COM ddi_prop_free(pci_rp);
106410923SEvan.Yan@Sun.COM
106510923SEvan.Yan@Sun.COM if ((pci_dev == ctrl->device) && (pci_func == ctrl->function)) {
106610923SEvan.Yan@Sun.COM /* found the match for the specified device address */
106710923SEvan.Yan@Sun.COM ctrl->dip = dip;
106810923SEvan.Yan@Sun.COM return (DDI_WALK_TERMINATE);
106910923SEvan.Yan@Sun.COM }
107010923SEvan.Yan@Sun.COM
107110923SEvan.Yan@Sun.COM /*
107210923SEvan.Yan@Sun.COM * continue the walk to the next sibling to look for a match.
107310923SEvan.Yan@Sun.COM */
107410923SEvan.Yan@Sun.COM return (DDI_WALK_PRUNECHILD);
107510923SEvan.Yan@Sun.COM }
107610923SEvan.Yan@Sun.COM
107710923SEvan.Yan@Sun.COM /*
107810923SEvan.Yan@Sun.COM * pcie_hp_match_dev:
107910923SEvan.Yan@Sun.COM * Match the dip's pci device number with the input dev_num
108010923SEvan.Yan@Sun.COM */
108110923SEvan.Yan@Sun.COM static boolean_t
pcie_hp_match_dev(dev_info_t * dip,int dev_num)108210923SEvan.Yan@Sun.COM pcie_hp_match_dev(dev_info_t *dip, int dev_num)
108310923SEvan.Yan@Sun.COM {
108410923SEvan.Yan@Sun.COM pci_regspec_t *pci_rp;
108510923SEvan.Yan@Sun.COM int length;
108610923SEvan.Yan@Sun.COM int pci_dev;
108710923SEvan.Yan@Sun.COM
108810923SEvan.Yan@Sun.COM if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
108910923SEvan.Yan@Sun.COM "reg", (int **)&pci_rp, (uint_t *)&length) != DDI_PROP_SUCCESS) {
109010923SEvan.Yan@Sun.COM return (B_FALSE);
109110923SEvan.Yan@Sun.COM }
109210923SEvan.Yan@Sun.COM
109310923SEvan.Yan@Sun.COM /* get the PCI device address info */
109410923SEvan.Yan@Sun.COM pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
109510923SEvan.Yan@Sun.COM
109610923SEvan.Yan@Sun.COM /*
109710923SEvan.Yan@Sun.COM * free the memory allocated by ddi_prop_lookup_int_array
109810923SEvan.Yan@Sun.COM */
109910923SEvan.Yan@Sun.COM ddi_prop_free(pci_rp);
110010923SEvan.Yan@Sun.COM
110110923SEvan.Yan@Sun.COM if (pci_dev == dev_num) {
110210923SEvan.Yan@Sun.COM /* found the match for the specified device address */
110310923SEvan.Yan@Sun.COM return (B_TRUE);
110410923SEvan.Yan@Sun.COM }
110510923SEvan.Yan@Sun.COM
110610923SEvan.Yan@Sun.COM return (B_FALSE);
110710923SEvan.Yan@Sun.COM }
110810923SEvan.Yan@Sun.COM
110910923SEvan.Yan@Sun.COM /*
111010923SEvan.Yan@Sun.COM * Callback function to match with device number in order to list
111110923SEvan.Yan@Sun.COM * occupants under a specific slot
111210923SEvan.Yan@Sun.COM */
111310923SEvan.Yan@Sun.COM static int
pcie_hp_list_occupants(dev_info_t * dip,void * arg)111410923SEvan.Yan@Sun.COM pcie_hp_list_occupants(dev_info_t *dip, void *arg)
111510923SEvan.Yan@Sun.COM {
111610923SEvan.Yan@Sun.COM pcie_hp_cn_cfg_t *cn_cfg_p = (pcie_hp_cn_cfg_t *)arg;
111710923SEvan.Yan@Sun.COM pcie_hp_occupant_info_t *occupant =
111810923SEvan.Yan@Sun.COM (pcie_hp_occupant_info_t *)cn_cfg_p->cn_private;
111910923SEvan.Yan@Sun.COM pcie_hp_slot_t *slot_p =
112010923SEvan.Yan@Sun.COM (pcie_hp_slot_t *)cn_cfg_p->slotp;
112110923SEvan.Yan@Sun.COM int pci_dev;
112210923SEvan.Yan@Sun.COM pci_regspec_t *pci_rp;
112310923SEvan.Yan@Sun.COM int length;
112410923SEvan.Yan@Sun.COM major_t major;
112510923SEvan.Yan@Sun.COM
112610923SEvan.Yan@Sun.COM /*
112710923SEvan.Yan@Sun.COM * Get the PCI device number information from the devinfo
112810923SEvan.Yan@Sun.COM * node. Since the node may not have the address field
112910923SEvan.Yan@Sun.COM * setup (this is done in the DDI_INITCHILD of the parent)
113010923SEvan.Yan@Sun.COM * we look up the 'reg' property to decode that information.
113110923SEvan.Yan@Sun.COM */
113210923SEvan.Yan@Sun.COM if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
113310923SEvan.Yan@Sun.COM DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
113410923SEvan.Yan@Sun.COM (uint_t *)&length) != DDI_PROP_SUCCESS) {
113510923SEvan.Yan@Sun.COM cn_cfg_p->rv = DDI_FAILURE;
113610923SEvan.Yan@Sun.COM cn_cfg_p->dip = dip;
113710923SEvan.Yan@Sun.COM return (DDI_WALK_TERMINATE);
113810923SEvan.Yan@Sun.COM }
113910923SEvan.Yan@Sun.COM
114010923SEvan.Yan@Sun.COM /* get the pci device id information */
114110923SEvan.Yan@Sun.COM pci_dev = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
114210923SEvan.Yan@Sun.COM
114310923SEvan.Yan@Sun.COM /*
114410923SEvan.Yan@Sun.COM * free the memory allocated by ddi_prop_lookup_int_array
114510923SEvan.Yan@Sun.COM */
114610923SEvan.Yan@Sun.COM ddi_prop_free(pci_rp);
114710923SEvan.Yan@Sun.COM
114810923SEvan.Yan@Sun.COM /*
114910923SEvan.Yan@Sun.COM * Match the node for the device number of the slot.
115010923SEvan.Yan@Sun.COM */
115110923SEvan.Yan@Sun.COM if (pci_dev == slot_p->hs_device_num) {
115210923SEvan.Yan@Sun.COM
115310923SEvan.Yan@Sun.COM major = ddi_driver_major(dip);
115410923SEvan.Yan@Sun.COM
115510923SEvan.Yan@Sun.COM /*
115610923SEvan.Yan@Sun.COM * If the node is not yet attached, then don't list it
115710923SEvan.Yan@Sun.COM * as an occupant. This is valid, since nothing can be
115810923SEvan.Yan@Sun.COM * consuming it until it is attached, and cfgadm will
115910923SEvan.Yan@Sun.COM * ask for the property explicitly which will cause it
116010923SEvan.Yan@Sun.COM * to be re-freshed right before checking with rcm.
116110923SEvan.Yan@Sun.COM */
116210923SEvan.Yan@Sun.COM if ((major == DDI_MAJOR_T_NONE) || !i_ddi_devi_attached(dip))
116310923SEvan.Yan@Sun.COM return (DDI_WALK_PRUNECHILD);
116410923SEvan.Yan@Sun.COM
116510923SEvan.Yan@Sun.COM /*
116610923SEvan.Yan@Sun.COM * If we have used all our occupants then print mesage
116710923SEvan.Yan@Sun.COM * and terminate walk.
116810923SEvan.Yan@Sun.COM */
116910923SEvan.Yan@Sun.COM if (occupant->i >= PCIE_HP_MAX_OCCUPANTS) {
117010923SEvan.Yan@Sun.COM cmn_err(CE_WARN,
117110923SEvan.Yan@Sun.COM "pcie (%s%d): unable to list all occupants",
117210923SEvan.Yan@Sun.COM ddi_driver_name(ddi_get_parent(dip)),
117310923SEvan.Yan@Sun.COM ddi_get_instance(ddi_get_parent(dip)));
117410923SEvan.Yan@Sun.COM return (DDI_WALK_TERMINATE);
117510923SEvan.Yan@Sun.COM }
117610923SEvan.Yan@Sun.COM
117710923SEvan.Yan@Sun.COM /*
117810923SEvan.Yan@Sun.COM * No need to hold the dip as ddi_walk_devs
117910923SEvan.Yan@Sun.COM * has already arranged that for us.
118010923SEvan.Yan@Sun.COM */
118110923SEvan.Yan@Sun.COM occupant->id[occupant->i] =
118210923SEvan.Yan@Sun.COM kmem_alloc(sizeof (char[MAXPATHLEN]), KM_SLEEP);
118310923SEvan.Yan@Sun.COM (void) ddi_pathname(dip, (char *)occupant->id[occupant->i]);
118410923SEvan.Yan@Sun.COM occupant->i++;
118510923SEvan.Yan@Sun.COM }
118610923SEvan.Yan@Sun.COM
118710923SEvan.Yan@Sun.COM /*
118810923SEvan.Yan@Sun.COM * continue the walk to the next sibling to look for a match
118910923SEvan.Yan@Sun.COM * or to find other nodes if this card is a multi-function card.
119010923SEvan.Yan@Sun.COM */
119110923SEvan.Yan@Sun.COM return (DDI_WALK_PRUNECHILD);
119210923SEvan.Yan@Sun.COM }
119310923SEvan.Yan@Sun.COM
119410923SEvan.Yan@Sun.COM /*
119510923SEvan.Yan@Sun.COM * Generate the System Event for ESC_DR_REQ.
119610923SEvan.Yan@Sun.COM * One of the consumers is pcidr, it calls to libcfgadm to perform a
119710923SEvan.Yan@Sun.COM * configure or unconfigure operation to the AP.
119810923SEvan.Yan@Sun.COM */
119910923SEvan.Yan@Sun.COM void
pcie_hp_gen_sysevent_req(char * slot_name,int hint,dev_info_t * self,int kmflag)120010923SEvan.Yan@Sun.COM pcie_hp_gen_sysevent_req(char *slot_name, int hint,
120110923SEvan.Yan@Sun.COM dev_info_t *self, int kmflag)
120210923SEvan.Yan@Sun.COM {
120310923SEvan.Yan@Sun.COM sysevent_id_t eid;
120410923SEvan.Yan@Sun.COM nvlist_t *ev_attr_list = NULL;
120510923SEvan.Yan@Sun.COM char cn_path[MAXPATHLEN];
120610923SEvan.Yan@Sun.COM char *ap_id;
120710923SEvan.Yan@Sun.COM int err, ap_id_len;
120810923SEvan.Yan@Sun.COM
120910923SEvan.Yan@Sun.COM /*
121010923SEvan.Yan@Sun.COM * Minor device name (AP) will be bus path
121110923SEvan.Yan@Sun.COM * concatenated with slot name
121210923SEvan.Yan@Sun.COM */
121310923SEvan.Yan@Sun.COM (void) strcpy(cn_path, "/devices");
121410923SEvan.Yan@Sun.COM (void) ddi_pathname(self, cn_path + strlen("/devices"));
121510923SEvan.Yan@Sun.COM
121610923SEvan.Yan@Sun.COM ap_id_len = strlen(cn_path) + strlen(":") +
121710923SEvan.Yan@Sun.COM strlen(slot_name) + 1;
121810923SEvan.Yan@Sun.COM ap_id = kmem_zalloc(ap_id_len, kmflag);
121910923SEvan.Yan@Sun.COM if (ap_id == NULL) {
122010923SEvan.Yan@Sun.COM cmn_err(CE_WARN,
122110923SEvan.Yan@Sun.COM "%s%d: Failed to allocate memory for AP ID: %s:%s",
122210923SEvan.Yan@Sun.COM ddi_driver_name(self), ddi_get_instance(self),
122310923SEvan.Yan@Sun.COM cn_path, slot_name);
122410923SEvan.Yan@Sun.COM
122510923SEvan.Yan@Sun.COM return;
122610923SEvan.Yan@Sun.COM }
122710923SEvan.Yan@Sun.COM
122810923SEvan.Yan@Sun.COM (void) strcpy(ap_id, cn_path);
122910923SEvan.Yan@Sun.COM (void) strcat(ap_id, ":");
123010923SEvan.Yan@Sun.COM (void) strcat(ap_id, slot_name);
123110923SEvan.Yan@Sun.COM
123210923SEvan.Yan@Sun.COM err = nvlist_alloc(&ev_attr_list, NV_UNIQUE_NAME_TYPE, kmflag);
123310923SEvan.Yan@Sun.COM if (err != 0) {
123410923SEvan.Yan@Sun.COM cmn_err(CE_WARN,
123510923SEvan.Yan@Sun.COM "%s%d: Failed to allocate memory "
123610923SEvan.Yan@Sun.COM "for event attributes%s", ddi_driver_name(self),
123710923SEvan.Yan@Sun.COM ddi_get_instance(self), ESC_DR_REQ);
123810923SEvan.Yan@Sun.COM
123910923SEvan.Yan@Sun.COM kmem_free(ap_id, ap_id_len);
124010923SEvan.Yan@Sun.COM return;
124110923SEvan.Yan@Sun.COM }
124210923SEvan.Yan@Sun.COM
124310923SEvan.Yan@Sun.COM switch (hint) {
124410923SEvan.Yan@Sun.COM
124510923SEvan.Yan@Sun.COM case SE_INVESTIGATE_RES: /* fall through */
124610923SEvan.Yan@Sun.COM case SE_INCOMING_RES: /* fall through */
124710923SEvan.Yan@Sun.COM case SE_OUTGOING_RES: /* fall through */
124810923SEvan.Yan@Sun.COM
124910923SEvan.Yan@Sun.COM err = nvlist_add_string(ev_attr_list, DR_REQ_TYPE,
125010923SEvan.Yan@Sun.COM SE_REQ2STR(hint));
125110923SEvan.Yan@Sun.COM
125210923SEvan.Yan@Sun.COM if (err != 0) {
125310923SEvan.Yan@Sun.COM cmn_err(CE_WARN,
125410923SEvan.Yan@Sun.COM "%s%d: Failed to add attr [%s] "
125510923SEvan.Yan@Sun.COM "for %s event", ddi_driver_name(self),
125610923SEvan.Yan@Sun.COM ddi_get_instance(self),
125710923SEvan.Yan@Sun.COM DR_REQ_TYPE, ESC_DR_REQ);
125810923SEvan.Yan@Sun.COM
125910923SEvan.Yan@Sun.COM goto done;
126010923SEvan.Yan@Sun.COM }
126110923SEvan.Yan@Sun.COM break;
126210923SEvan.Yan@Sun.COM
126310923SEvan.Yan@Sun.COM default:
126410923SEvan.Yan@Sun.COM cmn_err(CE_WARN, "%s%d: Unknown hint on sysevent",
126510923SEvan.Yan@Sun.COM ddi_driver_name(self), ddi_get_instance(self));
126610923SEvan.Yan@Sun.COM
126710923SEvan.Yan@Sun.COM goto done;
126810923SEvan.Yan@Sun.COM }
126910923SEvan.Yan@Sun.COM
127010923SEvan.Yan@Sun.COM /*
127110923SEvan.Yan@Sun.COM * Add attachment point as attribute (common attribute)
127210923SEvan.Yan@Sun.COM */
127310923SEvan.Yan@Sun.COM
127410923SEvan.Yan@Sun.COM err = nvlist_add_string(ev_attr_list, DR_AP_ID, ap_id);
127510923SEvan.Yan@Sun.COM
127610923SEvan.Yan@Sun.COM if (err != 0) {
127710923SEvan.Yan@Sun.COM cmn_err(CE_WARN, "%s%d: Failed to add attr [%s] for %s event",
127810923SEvan.Yan@Sun.COM ddi_driver_name(self), ddi_get_instance(self),
127910923SEvan.Yan@Sun.COM DR_AP_ID, EC_DR);
128010923SEvan.Yan@Sun.COM
128110923SEvan.Yan@Sun.COM goto done;
128210923SEvan.Yan@Sun.COM }
128310923SEvan.Yan@Sun.COM
128410923SEvan.Yan@Sun.COM
128510923SEvan.Yan@Sun.COM /*
128610923SEvan.Yan@Sun.COM * Log this event with sysevent framework.
128710923SEvan.Yan@Sun.COM */
128810923SEvan.Yan@Sun.COM
128910923SEvan.Yan@Sun.COM err = ddi_log_sysevent(self, DDI_VENDOR_SUNW, EC_DR,
129010923SEvan.Yan@Sun.COM ESC_DR_REQ, ev_attr_list, &eid,
129110923SEvan.Yan@Sun.COM ((kmflag == KM_SLEEP) ? DDI_SLEEP : DDI_NOSLEEP));
129210923SEvan.Yan@Sun.COM if (err != 0) {
129310923SEvan.Yan@Sun.COM cmn_err(CE_WARN, "%s%d: Failed to log %s event",
129410923SEvan.Yan@Sun.COM ddi_driver_name(self), ddi_get_instance(self), EC_DR);
129510923SEvan.Yan@Sun.COM }
129610923SEvan.Yan@Sun.COM
129710923SEvan.Yan@Sun.COM done:
129810923SEvan.Yan@Sun.COM nvlist_free(ev_attr_list);
129910923SEvan.Yan@Sun.COM kmem_free(ap_id, ap_id_len);
130010923SEvan.Yan@Sun.COM }
1301