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