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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * Copyright (c) * Copyright (c) 2001 Tadpole Technology plc
28 * All rights reserved.
29 * From "@(#)pcicfg.c 1.31 99/06/18 SMI"
30 */
31
32 /*
33 * Copyright 2023 Oxide Computer Company
34 */
35
36 /*
37 * Cardbus hotplug module
38 */
39
40 #include <sys/open.h>
41 #include <sys/file.h>
42 #include <sys/stat.h>
43 #include <sys/ddi.h>
44 #include <sys/sunndi.h>
45
46 #include <sys/note.h>
47
48 #include <sys/pci.h>
49
50 #include <sys/hotplug/hpcsvc.h>
51 #include <sys/hotplug/pci/pcicfg.h>
52 #include <sys/pcic_reg.h>
53
54 #include "cardbus.h"
55 #include "cardbus_hp.h"
56 #include "cardbus_cfg.h"
57
58 /*
59 * ************************************************************************
60 * *** Implementation specific data structures/definitions. ***
61 * ************************************************************************
62 */
63
64 #ifndef HPC_MAX_OCCUPANTS
65 #define HPC_MAX_OCCUPANTS 8
66 typedef struct hpc_occupant_info {
67 int i;
68 char *id[HPC_MAX_OCCUPANTS];
69 } hpc_occupant_info_t;
70 #endif
71
72 #define PCICFG_FLAGS_CONTINUE 0x1
73
74 #define PCICFG_OP_ONLINE 0x1
75 #define PCICFG_OP_OFFLINE 0x0
76
77 #define CBHP_DEVCTL_MINOR 255
78
79 #define AP_MINOR_NUM_TO_CB_INSTANCE(x) ((x) & 0xFF)
80 #define AP_MINOR_NUM(x) (((uint_t)(3) << 8) | ((x) & 0xFF))
81 #define AP_IS_CB_MINOR(x) (((x)>>8) == (3))
82
83 extern int cardbus_debug;
84 extern int number_of_cardbus_cards;
85
86 static int cardbus_autocfg_enabled = 1; /* auto config is enabled by default */
87
88 /* static functions */
89 static int cardbus_event_handler(caddr_t slot_arg, uint_t event_mask);
90 static int cardbus_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl,
91 int request, caddr_t arg);
92 static int cardbus_new_slot_state(dev_info_t *dip, hpc_slot_t hdl,
93 hpc_slot_info_t *slot_info, int slot_state);
94 static int cardbus_list_occupants(dev_info_t *dip, void *hdl);
95 static void create_occupant_props(dev_info_t *self, dev_t dev);
96 static void delete_occupant_props(dev_info_t *dip, dev_t dev);
97 static int cardbus_configure_ap(cbus_t *cbp);
98 static int cardbus_unconfigure_ap(cbus_t *cbp);
99 static int cbus_unconfigure(dev_info_t *devi, int prim_bus);
100 void cardbus_dump_pci_config(dev_info_t *dip);
101 void cardbus_dump_pci_node(dev_info_t *dip);
102
103 int
cardbus_init_hotplug(cbus_t * cbp)104 cardbus_init_hotplug(cbus_t *cbp)
105 {
106 char tbuf[MAXNAMELEN];
107 hpc_slot_info_t slot_info;
108 hpc_slot_ops_t *slot_ops;
109 hpc_slot_t slhandle; /* HPS slot handle */
110
111 /*
112 * register the bus instance with the HPS framework.
113 */
114 if (hpc_nexus_register_bus(cbp->cb_dip,
115 cardbus_new_slot_state, 0) != 0) {
116 cmn_err(CE_WARN, "%s%d: failed to register the bus with HPS\n",
117 ddi_driver_name(cbp->cb_dip), cbp->cb_instance);
118 return (DDI_FAILURE);
119 }
120
121 (void) sprintf(cbp->ap_id, "slot%d", cbp->cb_instance);
122 (void) ddi_pathname(cbp->cb_dip, tbuf);
123 cbp->nexus_path = kmem_alloc(strlen(tbuf) + 1, KM_SLEEP);
124 (void) strcpy(cbp->nexus_path, tbuf);
125 cardbus_err(cbp->cb_dip, 8,
126 "cardbus_init_hotplug: nexus_path set to %s", cbp->nexus_path);
127
128 slot_ops = hpc_alloc_slot_ops(KM_SLEEP);
129 cbp->slot_ops = slot_ops;
130
131 /*
132 * Fill in the slot information structure that
133 * describes the slot.
134 */
135 slot_info.version = HPC_SLOT_INFO_VERSION;
136 slot_info.slot_type = HPC_SLOT_TYPE_PCI;
137 slot_info.slot.pci.device_number = 0;
138 slot_info.slot.pci.slot_capabilities = 0;
139
140 (void) strcpy(slot_info.slot.pci.slot_logical_name, cbp->ap_id);
141
142 slot_ops->hpc_version = HPC_SLOT_OPS_VERSION;
143 slot_ops->hpc_op_connect = NULL;
144 slot_ops->hpc_op_disconnect = NULL;
145 slot_ops->hpc_op_insert = NULL;
146 slot_ops->hpc_op_remove = NULL;
147 slot_ops->hpc_op_control = cardbus_pci_control;
148
149 if (hpc_slot_register(cbp->cb_dip, cbp->nexus_path, &slot_info,
150 &slhandle, slot_ops, (caddr_t)cbp, 0) != 0) {
151 /*
152 * If the slot can not be registered,
153 * then the slot_ops need to be freed.
154 */
155 cmn_err(CE_WARN,
156 "cbp%d Unable to Register Slot %s", cbp->cb_instance,
157 slot_info.slot.pci.slot_logical_name);
158
159 (void) hpc_nexus_unregister_bus(cbp->cb_dip);
160 hpc_free_slot_ops(slot_ops);
161 cbp->slot_ops = NULL;
162 return (DDI_FAILURE);
163 }
164
165 ASSERT(slhandle == cbp->slot_handle);
166
167 cardbus_err(cbp->cb_dip, 8,
168 "cardbus_init_hotplug: slot_handle 0x%p", cbp->slot_handle);
169 return (DDI_SUCCESS);
170 }
171
172 static int
cardbus_event_handler(caddr_t slot_arg,uint_t event_mask)173 cardbus_event_handler(caddr_t slot_arg, uint_t event_mask)
174 {
175 int ap_minor = (int)((uintptr_t)slot_arg);
176 cbus_t *cbp;
177 int cb_instance;
178 int rv = HPC_EVENT_CLAIMED;
179
180 cb_instance = AP_MINOR_NUM_TO_CB_INSTANCE(ap_minor);
181
182 ASSERT(cb_instance >= 0);
183 cbp = (cbus_t *)ddi_get_soft_state(cardbus_state, cb_instance);
184 mutex_enter(&cbp->cb_mutex);
185
186 switch (event_mask) {
187
188 case HPC_EVENT_SLOT_INSERTION:
189 /*
190 * A card is inserted in the slot. Just report this
191 * event and return.
192 */
193 cardbus_err(cbp->cb_dip, 7,
194 "cardbus_event_handler(%s%d): card is inserted",
195 ddi_driver_name(cbp->cb_dip), cbp->cb_instance);
196
197 break;
198
199 case HPC_EVENT_SLOT_CONFIGURE:
200 /*
201 * Configure the occupant that is just inserted in the slot.
202 * The receptacle may or may not be in the connected state. If
203 * the receptacle is not connected and the auto configuration
204 * is enabled on this slot then connect the slot. If auto
205 * configuration is enabled then configure the card.
206 */
207 if (!(cbp->auto_config)) {
208 /*
209 * auto configuration is disabled.
210 */
211 cardbus_err(cbp->cb_dip, 7,
212 "cardbus_event_handler(%s%d): "
213 "SLOT_CONFIGURE event occured (slot %s)",
214 ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
215 cbp->name);
216
217 break;
218 }
219
220 cardbus_err(cbp->cb_dip, 7,
221 "cardbus_event_handler(%s%d): configure event",
222 ddi_driver_name(cbp->cb_dip), cbp->cb_instance);
223
224 if (cbp->ostate != AP_OSTATE_UNCONFIGURED) {
225 cmn_err(CE_WARN, "!slot%d already configured\n",
226 cbp->cb_instance);
227 break;
228 }
229
230 /*
231 * Auto configuration is enabled. First, make sure the
232 * receptacle is in the CONNECTED state.
233 */
234 if ((rv = hpc_nexus_connect(cbp->slot_handle,
235 NULL, 0)) == HPC_SUCCESS) {
236 cbp->rstate = AP_RSTATE_CONNECTED; /* record rstate */
237 }
238
239 if (cardbus_configure_ap(cbp) == HPC_SUCCESS)
240 create_occupant_props(cbp->cb_dip, makedevice(
241 ddi_driver_major((cbp->cb_dip)), ap_minor));
242 else
243 rv = HPC_ERR_FAILED;
244
245 break;
246
247 case HPC_EVENT_SLOT_UNCONFIGURE:
248 /*
249 * Unconfigure the occupant in this slot.
250 */
251 if (!(cbp->auto_config)) {
252 /*
253 * auto configuration is disabled.
254 */
255 cardbus_err(cbp->cb_dip, 7,
256 "cardbus_event_handler(%s%d): "
257 "SLOT_UNCONFIGURE event"
258 " occured - auto-conf disabled (slot %s)",
259 ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
260 cbp->name);
261
262 break;
263 }
264
265 cardbus_err(cbp->cb_dip, 7,
266 "cardbus_event_handler(%s%d): SLOT_UNCONFIGURE event"
267 " occured (slot %s)",
268 ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
269 cbp->name);
270
271 if (cardbus_unconfigure_ap(cbp) != HPC_SUCCESS)
272 rv = HPC_ERR_FAILED;
273
274 DEVI(cbp->cb_dip)->devi_ops->devo_bus_ops = cbp->orig_bopsp;
275 --number_of_cardbus_cards;
276 break;
277
278 case HPC_EVENT_SLOT_REMOVAL:
279 /*
280 * Card is removed from the slot. The card must have been
281 * unconfigured before this event.
282 */
283 if (cbp->ostate != AP_OSTATE_UNCONFIGURED) {
284 cardbus_err(cbp->cb_dip, 1,
285 "cardbus_event_handler(%s%d): "
286 "card is removed from"
287 " the slot %s before doing unconfigure!!",
288 ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
289 cbp->name);
290
291 break;
292 }
293
294 cardbus_err(cbp->cb_dip, 7,
295 "cardbus_event_handler(%s%d): "
296 "card is removed from the slot %s",
297 ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
298 cbp->name);
299
300 break;
301
302 case HPC_EVENT_SLOT_POWER_ON:
303 /*
304 * Slot is connected to the bus. i.e the card is powered
305 * on.
306 */
307 cardbus_err(cbp->cb_dip, 7,
308 "cardbus_event_handler(%s%d): "
309 "card is powered on in the slot %s",
310 ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
311 cbp->name);
312
313 cbp->rstate = AP_RSTATE_CONNECTED; /* record rstate */
314
315 break;
316
317 case HPC_EVENT_SLOT_POWER_OFF:
318 /*
319 * Slot is disconnected from the bus. i.e the card is powered
320 * off.
321 */
322 cardbus_err(cbp->cb_dip, 7,
323 "cardbus_event_handler(%s%d): "
324 "card is powered off in the slot %s",
325 ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
326 cbp->name);
327
328 cbp->rstate = AP_RSTATE_DISCONNECTED; /* record rstate */
329
330 break;
331
332 default:
333 cardbus_err(cbp->cb_dip, 4,
334 "cardbus_event_handler(%s%d): "
335 "unknown event %x for this slot %s",
336 ddi_driver_name(cbp->cb_dip), cbp->cb_instance,
337 event_mask, cbp->name);
338
339 break;
340 }
341
342 mutex_exit(&cbp->cb_mutex);
343
344 return (rv);
345 }
346
347 static int
cardbus_pci_control(caddr_t ops_arg,hpc_slot_t slot_hdl,int request,caddr_t arg)348 cardbus_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl, int request,
349 caddr_t arg)
350 {
351 cbus_t *cbp;
352 int rval = HPC_SUCCESS;
353 hpc_led_info_t *hpc_led_info;
354
355 _NOTE(ARGUNUSED(slot_hdl))
356
357 cbp = (cbus_t *)ops_arg;
358 ASSERT(mutex_owned(&cbp->cb_mutex));
359
360 switch (request) {
361
362 case HPC_CTRL_GET_SLOT_STATE: {
363 hpc_slot_state_t *hpc_slot_state;
364
365 hpc_slot_state = (hpc_slot_state_t *)arg;
366
367 cardbus_err(cbp->cb_dip, 7,
368 "cardbus_pci_control() - "
369 "HPC_CTRL_GET_SLOT_STATE hpc_slot_state=0x%p",
370 (void *) hpc_slot_state);
371
372 if (cbp->card_present)
373 *hpc_slot_state = HPC_SLOT_CONNECTED;
374 else
375 *hpc_slot_state = HPC_SLOT_EMPTY;
376
377 break;
378 }
379
380 case HPC_CTRL_GET_BOARD_TYPE: {
381 hpc_board_type_t *hpc_board_type;
382
383 hpc_board_type = (hpc_board_type_t *)arg;
384
385 cardbus_err(cbp->cb_dip, 7,
386 "cardbus_pci_control() - HPC_CTRL_GET_BOARD_TYPE");
387
388 /*
389 * The HPC driver does not know what board type
390 * is plugged in.
391 */
392 *hpc_board_type = HPC_BOARD_PCI_HOTPLUG;
393
394 break;
395 }
396
397 case HPC_CTRL_DEV_CONFIGURED:
398 case HPC_CTRL_DEV_UNCONFIGURED:
399 cardbus_err(cbp->cb_dip, 5,
400 "cardbus_pci_control() - HPC_CTRL_DEV_%sCONFIGURED",
401 request == HPC_CTRL_DEV_UNCONFIGURED ? "UN" : "");
402 break;
403
404 case HPC_CTRL_GET_LED_STATE:
405 hpc_led_info = (hpc_led_info_t *)arg;
406 cardbus_err(cbp->cb_dip, 5,
407 "cardbus_pci_control() - HPC_CTRL_GET_LED_STATE "
408 "led %d is %d",
409 hpc_led_info->led, cbp->leds[hpc_led_info->led]);
410
411 hpc_led_info->state = cbp->leds[hpc_led_info->led];
412 break;
413
414 case HPC_CTRL_SET_LED_STATE:
415 hpc_led_info = (hpc_led_info_t *)arg;
416
417 cardbus_err(cbp->cb_dip, 4,
418 "cardbus_pci_control() - HPC_CTRL_SET_LED_STATE "
419 "led %d to %d",
420 hpc_led_info->led, hpc_led_info->state);
421
422 cbp->leds[hpc_led_info->led] = hpc_led_info->state;
423 break;
424
425 case HPC_CTRL_ENABLE_AUTOCFG:
426 cardbus_err(cbp->cb_dip, 5,
427 "cardbus_pci_control() - HPC_CTRL_ENABLE_AUTOCFG");
428
429 /*
430 * Cardbus ALWAYS does auto config, from the slots point of
431 * view this is turning on the card and making sure it's ok.
432 * This is all done by the bridge driver before we see any
433 * indication.
434 */
435 break;
436
437 case HPC_CTRL_DISABLE_AUTOCFG:
438 cardbus_err(cbp->cb_dip, 5,
439 "cardbus_pci_control() - HPC_CTRL_DISABLE_AUTOCFG");
440 break;
441
442 case HPC_CTRL_DISABLE_ENUM:
443 case HPC_CTRL_ENABLE_ENUM:
444 default:
445 rval = HPC_ERR_NOTSUPPORTED;
446 break;
447 }
448
449 return (rval);
450 }
451
452 /*
453 * cardbus_new_slot_state()
454 *
455 * This function is called by the HPS when it finds a hot plug
456 * slot is added or being removed from the hot plug framework.
457 * It returns 0 for success and HPC_ERR_FAILED for errors.
458 */
459 static int
cardbus_new_slot_state(dev_info_t * dip,hpc_slot_t hdl,hpc_slot_info_t * slot_info,int slot_state)460 cardbus_new_slot_state(dev_info_t *dip, hpc_slot_t hdl,
461 hpc_slot_info_t *slot_info, int slot_state)
462 {
463 int cb_instance;
464 cbus_t *cbp;
465 int ap_minor;
466 int rv = 0;
467
468 cardbus_err(dip, 8,
469 "cardbus_new_slot_state: slot_handle 0x%p", hdl);
470
471 /*
472 * get the soft state structure for the bus instance.
473 */
474 cb_instance = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
475 DDI_PROP_DONTPASS, "cbus-instance", -1);
476 ASSERT(cb_instance >= 0);
477 cbp = (cbus_t *)ddi_get_soft_state(cardbus_state, cb_instance);
478
479 mutex_enter(&cbp->cb_mutex);
480
481 switch (slot_state) {
482
483 case HPC_SLOT_ONLINE:
484 /*
485 * Make sure the slot is not already ONLINE
486 */
487 if (cbp->slot_handle != NULL) {
488 cardbus_err(dip, 4,
489 "cardbus_new_slot_state: "
490 "cardbus already ONLINE!!");
491 rv = HPC_ERR_FAILED;
492 break;
493 }
494
495 /*
496 * Add the hot plug slot to the bus.
497 */
498
499 /* create the AP minor node */
500 ap_minor = AP_MINOR_NUM(cb_instance);
501 if (ddi_create_minor_node(dip, slot_info->pci_slot_name,
502 S_IFCHR, ap_minor,
503 DDI_NT_PCI_ATTACHMENT_POINT,
504 0) == DDI_FAILURE) {
505 cardbus_err(dip, 4,
506 "cardbus_new_slot_state: "
507 "ddi_create_minor_node failed");
508 rv = HPC_ERR_FAILED;
509 break;
510 }
511
512 /* save the slot handle */
513 cbp->slot_handle = hdl;
514
515 /* setup event handler for all hardware events on the slot */
516 if (hpc_install_event_handler(hdl, -1, cardbus_event_handler,
517 (caddr_t)((long)ap_minor)) != 0) {
518 cardbus_err(dip, 4,
519 "cardbus_new_slot_state: "
520 "install event handler failed");
521 rv = HPC_ERR_FAILED;
522 break;
523 }
524 cbp->event_mask = (uint32_t)0xFFFFFFFF;
525 create_occupant_props(dip,
526 makedevice(ddi_name_to_major(ddi_get_name(dip)),
527 ap_minor));
528
529 /* set default auto configuration enabled flag for this slot */
530 cbp->auto_config = cardbus_autocfg_enabled;
531
532 /* copy the slot information */
533 cbp->name = (char *)kmem_alloc(strlen(slot_info->pci_slot_name)
534 + 1, KM_SLEEP);
535 (void) strcpy(cbp->name, slot_info->pci_slot_name);
536 cardbus_err(cbp->cb_dip, 10,
537 "cardbus_new_slot_state: cbp->name set to %s", cbp->name);
538
539 cardbus_err(dip, 4,
540 "Cardbus slot \"%s\" ONLINE\n", slot_info->pci_slot_name);
541
542 cbp->ostate = AP_OSTATE_UNCONFIGURED;
543 cbp->rstate = AP_RSTATE_EMPTY;
544
545 break;
546
547 case HPC_SLOT_OFFLINE:
548 /*
549 * A hot plug slot is being removed from the bus.
550 * Make sure there is no occupant configured on the
551 * slot before removing the AP minor node.
552 */
553 if (cbp->ostate != AP_OSTATE_UNCONFIGURED) {
554 cmn_err(CE_WARN,
555 "cardbus: Card is still in configured state");
556 rv = HPC_ERR_FAILED;
557 break;
558 }
559
560 /*
561 * If the AP device is in open state then return
562 * error.
563 */
564 if (cbp->soft_state != PCIHP_SOFT_STATE_CLOSED) {
565 rv = HPC_ERR_FAILED;
566 break;
567 }
568
569 /* remove the minor node */
570 ddi_remove_minor_node(dip, cbp->name);
571 /* free up the memory for the name string */
572 kmem_free(cbp->name, strlen(cbp->name) + 1);
573
574 /* update the slot info data */
575 cbp->name = NULL;
576 cbp->slot_handle = NULL;
577
578 cardbus_err(dip, 6,
579 "cardbus_new_slot_state: Cardbus slot OFFLINE");
580 break;
581
582 default:
583 cmn_err(CE_WARN,
584 "cardbus_new_slot_state: unknown slot_state %d\n",
585 slot_state);
586 rv = HPC_ERR_FAILED;
587 }
588
589 mutex_exit(&cbp->cb_mutex);
590
591 return (rv);
592 }
593
594 static int
cardbus_list_occupants(dev_info_t * dip,void * hdl)595 cardbus_list_occupants(dev_info_t *dip, void *hdl)
596 {
597 hpc_occupant_info_t *occupant = (hpc_occupant_info_t *)hdl;
598 char pn[MAXPATHLEN];
599
600 /*
601 * Ignore the attachment point and pcs.
602 */
603 if (strcmp(ddi_binding_name(dip), "pcs") == 0) {
604 return (DDI_WALK_CONTINUE);
605 }
606
607 (void) ddi_pathname(dip, pn);
608
609 occupant->id[occupant->i] = kmem_alloc(strlen(pn) + 1, KM_SLEEP);
610 (void) strcpy(occupant->id[occupant->i], pn);
611
612 occupant->i++;
613
614 /*
615 * continue the walk to the next sibling to look for a match
616 * or to find other nodes if this card is a multi-function card.
617 */
618 return (DDI_WALK_PRUNECHILD);
619 }
620
621 static void
create_occupant_props(dev_info_t * self,dev_t dev)622 create_occupant_props(dev_info_t *self, dev_t dev)
623 {
624 hpc_occupant_info_t occupant;
625 int i;
626
627 occupant.i = 0;
628
629 ndi_devi_enter(self);
630 ddi_walk_devs(ddi_get_child(self), cardbus_list_occupants,
631 (void *)&occupant);
632 ndi_devi_exit(self);
633
634 if (occupant.i == 0) {
635 char *c[] = { "" };
636 cardbus_err(self, 1, "create_occupant_props: no occupant\n");
637 (void) ddi_prop_update_string_array(dev, self, "pci-occupant",
638 c, 1);
639 } else {
640 cardbus_err(self, 1,
641 "create_occupant_props: %d occupant\n", occupant.i);
642 (void) ddi_prop_update_string_array(dev, self, "pci-occupant",
643 occupant.id, occupant.i);
644 }
645
646 for (i = 0; i < occupant.i; i++) {
647 kmem_free(occupant.id[i], strlen(occupant.id[i]) + 1);
648 }
649 }
650
651 static void
delete_occupant_props(dev_info_t * dip,dev_t dev)652 delete_occupant_props(dev_info_t *dip, dev_t dev)
653 {
654 if (ddi_prop_remove(dev, dip, "pci-occupant")
655 != DDI_PROP_SUCCESS)
656 return; /* add error handling */
657
658 }
659
660 /*
661 * **************************************
662 * CONFIGURE the occupant in the slot.
663 * **************************************
664 */
665 static int
cardbus_configure_ap(cbus_t * cbp)666 cardbus_configure_ap(cbus_t *cbp)
667 {
668 dev_info_t *self = cbp->cb_dip;
669 int rv = HPC_SUCCESS;
670 hpc_slot_state_t rstate;
671 struct cardbus_config_ctrl ctrl;
672
673 /*
674 * check for valid request:
675 * 1. It is a hotplug slot.
676 * 2. The receptacle is in the CONNECTED state.
677 */
678 if (cbp->slot_handle == NULL || cbp->disabled) {
679 return (ENXIO);
680 }
681
682 /*
683 * If the occupant is already in (partially) configured
684 * state then call the ndi_devi_online() on the device
685 * subtree(s) for this attachment point.
686 */
687
688 if (cbp->ostate == AP_OSTATE_CONFIGURED) {
689 ctrl.flags = PCICFG_FLAGS_CONTINUE;
690 ctrl.busno = cardbus_primary_busno(self);
691 ctrl.rv = NDI_SUCCESS;
692 ctrl.dip = NULL;
693 ctrl.op = PCICFG_OP_ONLINE;
694
695 ndi_devi_enter(self);
696 ddi_walk_devs(ddi_get_child(self),
697 cbus_configure, (void *)&ctrl);
698 ndi_devi_exit(self);
699
700 if (cardbus_debug) {
701 cardbus_dump_pci_config(self);
702 cardbus_dump_pci_node(self);
703 }
704
705 if (ctrl.rv != NDI_SUCCESS) {
706 /*
707 * one or more of the devices are not
708 * onlined.
709 */
710 cmn_err(CE_WARN, "cardbus(%s%d): failed to attach "
711 "one or more drivers for the card in the slot %s",
712 ddi_driver_name(self), cbp->cb_instance,
713 cbp->name);
714 }
715
716 /* tell HPC driver that the occupant is configured */
717 (void) hpc_nexus_control(cbp->slot_handle,
718 HPC_CTRL_DEV_CONFIGURED, NULL);
719 return (rv);
720 }
721
722 /*
723 * Occupant is in the UNCONFIGURED state.
724 */
725
726 /* Check if the receptacle is in the CONNECTED state. */
727 if (hpc_nexus_control(cbp->slot_handle,
728 HPC_CTRL_GET_SLOT_STATE, (caddr_t)&rstate) != 0) {
729 return (ENXIO);
730 }
731
732 if (rstate != HPC_SLOT_CONNECTED) {
733 /* error. either the slot is empty or connect failed */
734 return (ENXIO);
735 }
736
737 cbp->rstate = AP_RSTATE_CONNECTED; /* record rstate */
738
739 /*
740 * Call the configurator to configure the card.
741 */
742 if (cardbus_configure(cbp) != PCICFG_SUCCESS) {
743 return (EIO);
744 }
745
746 /* record the occupant state as CONFIGURED */
747 cbp->ostate = AP_OSTATE_CONFIGURED;
748 cbp->condition = AP_COND_OK;
749
750 /* now, online all the devices in the AP */
751 ctrl.flags = PCICFG_FLAGS_CONTINUE;
752 ctrl.busno = cardbus_primary_busno(self);
753 ctrl.rv = NDI_SUCCESS;
754 ctrl.dip = NULL;
755 ctrl.op = PCICFG_OP_ONLINE;
756
757 ndi_devi_enter(self);
758 ddi_walk_devs(ddi_get_child(self), cbus_configure, (void *)&ctrl);
759 ndi_devi_exit(self);
760
761 if (cardbus_debug) {
762 cardbus_dump_pci_config(self);
763 cardbus_dump_pci_node(self);
764 }
765 if (ctrl.rv != NDI_SUCCESS) {
766 /*
767 * one or more of the devices are not
768 * ONLINE'd.
769 */
770 cmn_err(CE_WARN, "cbhp (%s%d): failed to attach one or"
771 " more drivers for the card in the slot %s",
772 ddi_driver_name(cbp->cb_dip),
773 cbp->cb_instance, cbp->name);
774 /* rv = EFAULT; */
775 }
776
777 /* tell HPC driver that the occupant is configured */
778 (void) hpc_nexus_control(cbp->slot_handle,
779 HPC_CTRL_DEV_CONFIGURED, NULL);
780
781 return (rv);
782 }
783
784 /*
785 * **************************************
786 * UNCONFIGURE the occupant in the slot.
787 * **************************************
788 */
789 static int
cardbus_unconfigure_ap(cbus_t * cbp)790 cardbus_unconfigure_ap(cbus_t *cbp)
791 {
792 dev_info_t *self = cbp->cb_dip;
793 int rv = HPC_SUCCESS, nrv;
794
795 /*
796 * check for valid request:
797 * 1. It is a hotplug slot.
798 * 2. The occupant is in the CONFIGURED state.
799 */
800
801 if (cbp->slot_handle == NULL || cbp->disabled) {
802 return (ENXIO);
803 }
804
805 /*
806 * If the occupant is in the CONFIGURED state then
807 * call the configurator to unconfigure the slot.
808 */
809 if (cbp->ostate == AP_OSTATE_CONFIGURED) {
810 /*
811 * Detach all the drivers for the devices in the
812 * slot.
813 */
814 nrv = cardbus_unconfigure_node(self,
815 cardbus_primary_busno(self),
816 B_TRUE);
817
818 if (nrv != NDI_SUCCESS) {
819 /*
820 * Failed to detach one or more drivers.
821 * Restore the status for the drivers
822 * which are offlined during this step.
823 */
824 cmn_err(CE_WARN,
825 "cbhp (%s%d): Failed to offline all devices"
826 " (slot %s)", ddi_driver_name(cbp->cb_dip),
827 cbp->cb_instance, cbp->name);
828 rv = EBUSY;
829 } else {
830
831 if (cardbus_unconfigure(cbp) == PCICFG_SUCCESS) {
832 /*
833 * Now that resources are freed,
834 * clear EXT and Turn LED ON.
835 */
836 cbp->ostate = AP_OSTATE_UNCONFIGURED;
837 cbp->condition = AP_COND_UNKNOWN;
838 /*
839 * send the notification of state change
840 * to the HPC driver.
841 */
842 (void) hpc_nexus_control(cbp->slot_handle,
843 HPC_CTRL_DEV_UNCONFIGURED, NULL);
844 } else {
845 rv = EIO;
846 }
847 }
848 }
849
850 return (rv);
851 }
852
853 int
cbus_configure(dev_info_t * dip,void * hdl)854 cbus_configure(dev_info_t *dip, void *hdl)
855 {
856 pci_regspec_t *pci_rp;
857 int length, rc;
858 struct cardbus_config_ctrl *ctrl = (struct cardbus_config_ctrl *)hdl;
859 uint8_t bus, device, function;
860
861 /*
862 * Ignore the attachment point and pcs.
863 */
864 if (strcmp(ddi_binding_name(dip), "hp_attachment") == 0 ||
865 strcmp(ddi_binding_name(dip), "pcs") == 0) {
866 cardbus_err(dip, 8, "cbus_configure: Ignoring\n");
867 return (DDI_WALK_CONTINUE);
868 }
869
870 cardbus_err(dip, 6, "cbus_configure\n");
871
872 ASSERT(ctrl->op == PCICFG_OP_ONLINE);
873
874 /*
875 * Get the PCI device number information from the devinfo
876 * node. Since the node may not have the address field
877 * setup (this is done in the DDI_INITCHILD of the parent)
878 * we look up the 'reg' property to decode that information.
879 */
880 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip,
881 DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
882 (uint_t *)&length) != DDI_PROP_SUCCESS) {
883 /* Porbably not a real device, like PCS for example */
884 if (ddi_get_child(dip) == NULL)
885 return (DDI_WALK_PRUNECHILD);
886
887 cardbus_err(dip, 1, "cubs_configure: Don't configure device\n");
888 ctrl->rv = DDI_FAILURE;
889 ctrl->dip = dip;
890 return (DDI_WALK_TERMINATE);
891 }
892
893 if (pci_rp->pci_phys_hi == 0)
894 return (DDI_WALK_CONTINUE);
895
896 /* get the pci device id information */
897 bus = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
898 device = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
899 function = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
900
901 /*
902 * free the memory allocated by ddi_prop_lookup_int_array
903 */
904 ddi_prop_free(pci_rp);
905
906 if (bus <= ctrl->busno)
907 return (DDI_WALK_CONTINUE);
908
909 cardbus_err(dip, 8,
910 "cbus_configure on-line device at: "
911 "[0x%x][0x%x][0x%x]\n", bus, device, function);
912
913 rc = ndi_devi_online(dip, NDI_ONLINE_ATTACH|NDI_CONFIG);
914
915 cardbus_err(dip, 7,
916 "cbus_configure %s\n",
917 rc == NDI_SUCCESS ? "Success": "Failure");
918
919 if (rc != NDI_SUCCESS)
920 return (DDI_WALK_PRUNECHILD);
921
922 return (DDI_WALK_CONTINUE);
923 }
924
925 int
cardbus_unconfigure_node(dev_info_t * dip,int prim_bus,boolean_t top_bridge)926 cardbus_unconfigure_node(dev_info_t *dip, int prim_bus, boolean_t top_bridge)
927 {
928 dev_info_t *child, *next;
929
930 cardbus_err(dip, 6, "cardbus_unconfigure_node\n");
931
932 /*
933 * Ignore pcs.
934 */
935 if (strcmp(ddi_binding_name(dip), "pcs") == 0) {
936 cardbus_err(dip, 8, "cardbus_unconfigure_node: Ignoring\n");
937 return (NDI_SUCCESS);
938 }
939
940 /*
941 * bottom up off-line
942 */
943 for (child = ddi_get_child(dip); child; child = next) {
944 int rc;
945 next = ddi_get_next_sibling(child);
946 rc = cardbus_unconfigure_node(child, prim_bus, B_FALSE);
947 if (rc != NDI_SUCCESS)
948 return (rc);
949 }
950
951 /*
952 * Don't unconfigure the bridge itself.
953 */
954 if (top_bridge)
955 return (NDI_SUCCESS);
956
957 if (cbus_unconfigure(dip, prim_bus) != NDI_SUCCESS) {
958 cardbus_err(dip, 1,
959 "cardbus_unconfigure_node: cardbus_unconfigure failed\n");
960 return (NDI_FAILURE);
961 }
962 return (NDI_SUCCESS);
963 }
964
965 /*
966 * This will turn resources allocated by cbus_configure()
967 * and remove the device tree from the attachment point
968 * and below. The routine assumes the devices have their
969 * drivers detached.
970 */
971 static int
cbus_unconfigure(dev_info_t * devi,int prim_bus)972 cbus_unconfigure(dev_info_t *devi, int prim_bus)
973 {
974 pci_regspec_t *pci_rp;
975 uint_t bus, device, func, length;
976 int ndi_flags = NDI_UNCONFIG;
977
978 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, devi,
979 DDI_PROP_DONTPASS, "reg", (int **)&pci_rp,
980 &length) != DDI_PROP_SUCCESS) {
981 /*
982 * This cannot be one of our devices. If it's something like a
983 * SCSI device then the attempt to offline the HBA
984 * (which probably is one of our devices)
985 * will also do bottom up offlining. That
986 * will fail if this device is busy. So always
987 * return success here
988 * so that the walk will continue.
989 */
990 return (NDI_SUCCESS);
991 }
992
993 if (pci_rp->pci_phys_hi == 0)
994 return (NDI_FAILURE);
995
996 bus = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
997
998 if (bus <= prim_bus)
999 return (NDI_SUCCESS);
1000
1001 device = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
1002 func = PCI_REG_FUNC_G(pci_rp->pci_phys_hi);
1003 ddi_prop_free(pci_rp);
1004
1005 cardbus_err(devi, 8,
1006 "cbus_unconfigure: "
1007 "offline bus [0x%x] device [0x%x] function [%x]\n",
1008 bus, device, func);
1009 if (ndi_devi_offline(devi, ndi_flags) != NDI_SUCCESS) {
1010 cardbus_err(devi, 1,
1011 "Device [0x%x] function [%x] is busy\n", device, func);
1012 return (NDI_FAILURE);
1013 }
1014
1015 cardbus_err(devi, 9,
1016 "Tearing down device [0x%x] function [0x%x]\n", device, func);
1017
1018 if (cardbus_teardown_device(devi) != PCICFG_SUCCESS) {
1019 cardbus_err(devi, 1,
1020 "Failed to tear down "
1021 "device [0x%x] function [0x%x]\n", device, func);
1022 return (NDI_FAILURE);
1023 }
1024
1025 return (NDI_SUCCESS);
1026 }
1027
1028 boolean_t
cardbus_is_cb_minor(dev_t dev)1029 cardbus_is_cb_minor(dev_t dev)
1030 {
1031 return (AP_IS_CB_MINOR(getminor(dev)) ? B_TRUE : B_FALSE);
1032 }
1033
1034 int
cardbus_open(dev_t * devp,int flags,int otyp,cred_t * credp)1035 cardbus_open(dev_t *devp, int flags, int otyp, cred_t *credp)
1036 {
1037 cbus_t *cbp;
1038 int minor;
1039
1040 _NOTE(ARGUNUSED(credp))
1041
1042 minor = getminor(*devp);
1043
1044 /*
1045 * Make sure the open is for the right file type.
1046 */
1047 if (otyp != OTYP_CHR)
1048 return (EINVAL);
1049
1050 /*
1051 * Get the soft state structure for the 'devctl' device.
1052 */
1053 cbp = (cbus_t *)ddi_get_soft_state(cardbus_state,
1054 AP_MINOR_NUM_TO_CB_INSTANCE(minor));
1055 if (cbp == NULL)
1056 return (ENXIO);
1057
1058 mutex_enter(&cbp->cb_mutex);
1059
1060 /*
1061 * Handle the open by tracking the device state.
1062 *
1063 * Note: Needs review w.r.t exclusive access to AP or the bus.
1064 * Currently in the pci plug-in we don't use EXCL open at all
1065 * so the code below implements EXCL access on the bus.
1066 */
1067
1068 /* enforce exclusive access to the bus */
1069 if ((cbp->soft_state == PCIHP_SOFT_STATE_OPEN_EXCL) ||
1070 ((flags & FEXCL) &&
1071 (cbp->soft_state != PCIHP_SOFT_STATE_CLOSED))) {
1072 mutex_exit(&cbp->cb_mutex);
1073 return (EBUSY);
1074 }
1075
1076 if (flags & FEXCL)
1077 cbp->soft_state = PCIHP_SOFT_STATE_OPEN_EXCL;
1078 else
1079 cbp->soft_state = PCIHP_SOFT_STATE_OPEN;
1080
1081 mutex_exit(&cbp->cb_mutex);
1082 return (0);
1083 }
1084
1085 /*ARGSUSED*/
1086 int
cardbus_close(dev_t dev,int flags,int otyp,cred_t * credp)1087 cardbus_close(dev_t dev, int flags, int otyp, cred_t *credp)
1088 {
1089 cbus_t *cbp;
1090 int minor;
1091
1092 _NOTE(ARGUNUSED(credp))
1093
1094 minor = getminor(dev);
1095
1096 if (otyp != OTYP_CHR)
1097 return (EINVAL);
1098
1099 cbp = (cbus_t *)ddi_get_soft_state(cardbus_state,
1100 AP_MINOR_NUM_TO_CB_INSTANCE(minor));
1101 if (cbp == NULL)
1102 return (ENXIO);
1103
1104 mutex_enter(&cbp->cb_mutex);
1105 cbp->soft_state = PCIHP_SOFT_STATE_CLOSED;
1106 mutex_exit(&cbp->cb_mutex);
1107 return (0);
1108 }
1109
1110 /*
1111 * cardbus_ioctl: devctl hotplug controls
1112 */
1113 /*ARGSUSED*/
1114 int
cardbus_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)1115 cardbus_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1116 int *rvalp)
1117 {
1118 cbus_t *cbp;
1119 dev_info_t *self;
1120 dev_info_t *child_dip = NULL;
1121 struct devctl_iocdata *dcp;
1122 uint_t bus_state;
1123 int rv = 0;
1124 int nrv = 0;
1125 int ap_minor;
1126 hpc_slot_state_t rstate;
1127 devctl_ap_state_t ap_state;
1128 struct hpc_control_data hpc_ctrldata;
1129 struct hpc_led_info led_info;
1130
1131 _NOTE(ARGUNUSED(credp))
1132
1133 ap_minor = getminor(dev);
1134 cbp = (cbus_t *)ddi_get_soft_state(cardbus_state,
1135 AP_MINOR_NUM_TO_CB_INSTANCE(ap_minor));
1136 if (cbp == NULL)
1137 return (ENXIO);
1138
1139 self = cbp->cb_dip;
1140 /*
1141 * read devctl ioctl data
1142 */
1143 if ((cmd != DEVCTL_AP_CONTROL) && ndi_dc_allochdl((void *)arg,
1144 &dcp) != NDI_SUCCESS)
1145 return (EFAULT);
1146
1147 #ifdef CARDBUS_DEBUG
1148 {
1149 char *cmd_name;
1150
1151 switch (cmd) {
1152 case DEVCTL_DEVICE_GETSTATE: cmd_name = "DEVCTL_DEVICE_GETSTATE"; break;
1153 case DEVCTL_DEVICE_ONLINE: cmd_name = "DEVCTL_DEVICE_ONLINE"; break;
1154 case DEVCTL_DEVICE_OFFLINE: cmd_name = "DEVCTL_DEVICE_OFFLINE"; break;
1155 case DEVCTL_DEVICE_RESET: cmd_name = "DEVCTL_DEVICE_RESET"; break;
1156 case DEVCTL_BUS_QUIESCE: cmd_name = "DEVCTL_BUS_QUIESCE"; break;
1157 case DEVCTL_BUS_UNQUIESCE: cmd_name = "DEVCTL_BUS_UNQUIESCE"; break;
1158 case DEVCTL_BUS_RESET: cmd_name = "DEVCTL_BUS_RESET"; break;
1159 case DEVCTL_BUS_RESETALL: cmd_name = "DEVCTL_BUS_RESETALL"; break;
1160 case DEVCTL_BUS_GETSTATE: cmd_name = "DEVCTL_BUS_GETSTATE"; break;
1161 case DEVCTL_AP_CONNECT: cmd_name = "DEVCTL_AP_CONNECT"; break;
1162 case DEVCTL_AP_DISCONNECT: cmd_name = "DEVCTL_AP_DISCONNECT"; break;
1163 case DEVCTL_AP_INSERT: cmd_name = "DEVCTL_AP_INSERT"; break;
1164 case DEVCTL_AP_REMOVE: cmd_name = "DEVCTL_AP_REMOVE"; break;
1165 case DEVCTL_AP_CONFIGURE: cmd_name = "DEVCTL_AP_CONFIGURE"; break;
1166 case DEVCTL_AP_UNCONFIGURE: cmd_name = "DEVCTL_AP_UNCONFIGURE"; break;
1167 case DEVCTL_AP_GETSTATE: cmd_name = "DEVCTL_AP_GETSTATE"; break;
1168 case DEVCTL_AP_CONTROL: cmd_name = "DEVCTL_AP_CONTROL"; break;
1169 default: cmd_name = "Unknown"; break;
1170 }
1171 cardbus_err(cbp->cb_dip, 7,
1172 "cardbus_ioctl: cmd = 0x%x, \"%s\"", cmd, cmd_name);
1173 }
1174 #endif
1175
1176 switch (cmd) {
1177 case DEVCTL_DEVICE_GETSTATE:
1178 case DEVCTL_DEVICE_ONLINE:
1179 case DEVCTL_DEVICE_OFFLINE:
1180 case DEVCTL_BUS_GETSTATE:
1181 rv = ndi_devctl_ioctl(self, cmd, arg, mode, 0);
1182 ndi_dc_freehdl(dcp);
1183 return (rv);
1184 default:
1185 break;
1186 }
1187
1188 switch (cmd) {
1189 case DEVCTL_DEVICE_RESET:
1190 rv = ENOTSUP;
1191 break;
1192
1193 case DEVCTL_BUS_QUIESCE:
1194 if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS)
1195 if (bus_state == BUS_QUIESCED)
1196 break;
1197 (void) ndi_set_bus_state(self, BUS_QUIESCED);
1198 break;
1199
1200 case DEVCTL_BUS_UNQUIESCE:
1201 if (ndi_get_bus_state(self, &bus_state) == NDI_SUCCESS)
1202 if (bus_state == BUS_ACTIVE)
1203 break;
1204 (void) ndi_set_bus_state(self, BUS_ACTIVE);
1205 break;
1206
1207 case DEVCTL_BUS_RESET:
1208 rv = ENOTSUP;
1209 break;
1210
1211 case DEVCTL_BUS_RESETALL:
1212 rv = ENOTSUP;
1213 break;
1214
1215 case DEVCTL_AP_CONNECT:
1216 case DEVCTL_AP_DISCONNECT:
1217 /*
1218 * CONNECT(DISCONNECT) the hot plug slot to(from) the bus.
1219 */
1220 case DEVCTL_AP_INSERT:
1221 case DEVCTL_AP_REMOVE:
1222 /*
1223 * Prepare the slot for INSERT/REMOVE operation.
1224 */
1225
1226 /*
1227 * check for valid request:
1228 * 1. It is a hotplug slot.
1229 * 2. The slot has no occupant that is in
1230 * the 'configured' state.
1231 *
1232 * The lower 8 bits of the minor number is the PCI
1233 * device number for the slot.
1234 */
1235 if ((cbp->slot_handle == NULL) || cbp->disabled) {
1236 rv = ENXIO;
1237 break;
1238 }
1239
1240 /* the slot occupant must be in the UNCONFIGURED state */
1241 if (cbp->ostate != AP_OSTATE_UNCONFIGURED) {
1242 rv = EINVAL;
1243 break;
1244 }
1245
1246 /*
1247 * Call the HPC driver to perform the operation on the slot.
1248 */
1249 mutex_enter(&cbp->cb_mutex);
1250 switch (cmd) {
1251 case DEVCTL_AP_INSERT:
1252 rv = hpc_nexus_insert(cbp->slot_handle, NULL, 0);
1253 break;
1254 case DEVCTL_AP_REMOVE:
1255 rv = hpc_nexus_remove(cbp->slot_handle, NULL, 0);
1256 break;
1257 case DEVCTL_AP_CONNECT:
1258 if ((rv = hpc_nexus_connect(cbp->slot_handle,
1259 NULL, 0)) == 0)
1260 cbp->rstate = AP_RSTATE_CONNECTED;
1261 break;
1262 case DEVCTL_AP_DISCONNECT:
1263 if ((rv = hpc_nexus_disconnect(cbp->slot_handle,
1264 NULL, 0)) == 0)
1265 cbp->rstate = AP_RSTATE_DISCONNECTED;
1266 break;
1267 }
1268 mutex_exit(&cbp->cb_mutex);
1269
1270 switch (rv) {
1271 case HPC_ERR_INVALID:
1272 rv = ENXIO;
1273 break;
1274 case HPC_ERR_NOTSUPPORTED:
1275 rv = ENOTSUP;
1276 break;
1277 case HPC_ERR_FAILED:
1278 rv = EIO;
1279 break;
1280 }
1281
1282 break;
1283
1284 case DEVCTL_AP_CONFIGURE:
1285 /*
1286 * **************************************
1287 * CONFIGURE the occupant in the slot.
1288 * **************************************
1289 */
1290
1291 mutex_enter(&cbp->cb_mutex);
1292 if ((nrv = cardbus_configure_ap(cbp)) == HPC_SUCCESS) {
1293 create_occupant_props(cbp->cb_dip, dev);
1294 } else
1295 rv = nrv;
1296 mutex_exit(&cbp->cb_mutex);
1297 break;
1298
1299 case DEVCTL_AP_UNCONFIGURE:
1300 /*
1301 * **************************************
1302 * UNCONFIGURE the occupant in the slot.
1303 * **************************************
1304 */
1305
1306 mutex_enter(&cbp->cb_mutex);
1307 if ((nrv = cardbus_unconfigure_ap(cbp)) == HPC_SUCCESS) {
1308 delete_occupant_props(cbp->cb_dip, dev);
1309 } else
1310 rv = nrv;
1311 mutex_exit(&cbp->cb_mutex);
1312 break;
1313
1314 case DEVCTL_AP_GETSTATE:
1315 {
1316 int mutex_held;
1317
1318 /*
1319 * return the state of Attachment Point.
1320 *
1321 * If the occupant is in UNCONFIGURED state then
1322 * we should get the receptacle state from the
1323 * HPC driver because the receptacle state
1324 * maintained in the nexus may not be accurate.
1325 */
1326
1327 /*
1328 * check for valid request:
1329 * 1. It is a hotplug slot.
1330 */
1331 if (cbp->slot_handle == NULL) {
1332 rv = ENXIO;
1333 break;
1334 }
1335
1336 /* try to acquire the slot mutex */
1337 mutex_held = mutex_tryenter(&cbp->cb_mutex);
1338
1339 if (cbp->ostate == AP_OSTATE_UNCONFIGURED) {
1340 if (hpc_nexus_control(cbp->slot_handle,
1341 HPC_CTRL_GET_SLOT_STATE,
1342 (caddr_t)&rstate) != 0) {
1343 rv = ENXIO;
1344 if (mutex_held)
1345 mutex_exit(&cbp->cb_mutex);
1346 break;
1347 }
1348 cbp->rstate = (ap_rstate_t)rstate;
1349 }
1350
1351 ap_state.ap_rstate = cbp->rstate;
1352 ap_state.ap_ostate = cbp->ostate;
1353 ap_state.ap_condition = cbp->condition;
1354 ap_state.ap_last_change = 0;
1355 ap_state.ap_error_code = 0;
1356 if (mutex_held)
1357 ap_state.ap_in_transition = 0; /* AP is not busy */
1358 else
1359 ap_state.ap_in_transition = 1; /* AP is busy */
1360
1361 if (mutex_held)
1362 mutex_exit(&cbp->cb_mutex);
1363
1364 /* copy the return-AP-state information to the user space */
1365 if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS)
1366 rv = ENXIO;
1367
1368 break;
1369
1370 }
1371
1372 case DEVCTL_AP_CONTROL:
1373 /*
1374 * HPC control functions:
1375 * HPC_CTRL_ENABLE_SLOT/HPC_CTRL_DISABLE_SLOT
1376 * Changes the state of the slot and preserves
1377 * the state across the reboot.
1378 * HPC_CTRL_ENABLE_AUTOCFG/HPC_CTRL_DISABLE_AUTOCFG
1379 * Enables or disables the auto configuration
1380 * of hot plugged occupant if the hardware
1381 * supports notification of the hot plug
1382 * events.
1383 * HPC_CTRL_GET_LED_STATE/HPC_CTRL_SET_LED_STATE
1384 * Controls the state of an LED.
1385 * HPC_CTRL_GET_SLOT_INFO
1386 * Get slot information data structure
1387 * (hpc_slot_info_t).
1388 * HPC_CTRL_GET_BOARD_TYPE
1389 * Get board type information (hpc_board_type_t).
1390 * HPC_CTRL_GET_CARD_INFO
1391 * Get card information (hpc_card_info_t).
1392 *
1393 * These control functions are used by the cfgadm plug-in
1394 * to implement "-x" and "-v" options.
1395 */
1396
1397 /* copy user ioctl data first */
1398 #ifdef _MULTI_DATAMODEL
1399 if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
1400 struct hpc_control32_data hpc_ctrldata32;
1401
1402 if (copyin((void *)arg, (void *)&hpc_ctrldata32,
1403 sizeof (struct hpc_control32_data)) != 0) {
1404 rv = EFAULT;
1405 break;
1406 }
1407 hpc_ctrldata.cmd = hpc_ctrldata32.cmd;
1408 hpc_ctrldata.data =
1409 (void *)(intptr_t)hpc_ctrldata32.data;
1410 }
1411 #else
1412 if (copyin((void *)arg, (void *)&hpc_ctrldata,
1413 sizeof (struct hpc_control_data)) != 0) {
1414 rv = EFAULT;
1415 break;
1416 }
1417 #endif
1418
1419 #ifdef CARDBUS_DEBUG
1420 {
1421 char *hpc_name;
1422 switch (hpc_ctrldata.cmd) {
1423 case HPC_CTRL_GET_LED_STATE:
1424 hpc_name = "HPC_CTRL_GET_LED_STATE";
1425 break;
1426 case HPC_CTRL_SET_LED_STATE:
1427 hpc_name = "HPC_CTRL_SET_LED_STATE";
1428 break;
1429 case HPC_CTRL_ENABLE_SLOT:
1430 hpc_name = "HPC_CTRL_ENABLE_SLOT";
1431 break;
1432 case HPC_CTRL_DISABLE_SLOT:
1433 hpc_name = "HPC_CTRL_DISABLE_SLOT";
1434 break;
1435 case HPC_CTRL_ENABLE_AUTOCFG:
1436 hpc_name = "HPC_CTRL_ENABLE_AUTOCFG";
1437 break;
1438 case HPC_CTRL_DISABLE_AUTOCFG:
1439 hpc_name = "HPC_CTRL_DISABLE_AUTOCFG";
1440 break;
1441 case HPC_CTRL_GET_BOARD_TYPE:
1442 hpc_name = "HPC_CTRL_GET_BOARD_TYPE";
1443 break;
1444 case HPC_CTRL_GET_SLOT_INFO:
1445 hpc_name = "HPC_CTRL_GET_SLOT_INFO";
1446 break;
1447 case HPC_CTRL_GET_CARD_INFO:
1448 hpc_name = "HPC_CTRL_GET_CARD_INFO";
1449 break;
1450 default: hpc_name = "Unknown"; break;
1451 }
1452 cardbus_err(cbp->cb_dip, 7,
1453 "cardbus_ioctl: HP Control cmd 0x%x - \"%s\"",
1454 hpc_ctrldata.cmd, hpc_name);
1455 }
1456 #endif
1457 /*
1458 * check for valid request:
1459 * 1. It is a hotplug slot.
1460 */
1461 if (cbp->slot_handle == NULL) {
1462 rv = ENXIO;
1463 break;
1464 }
1465
1466 mutex_enter(&cbp->cb_mutex);
1467 switch (hpc_ctrldata.cmd) {
1468 case HPC_CTRL_GET_LED_STATE:
1469 /* copy the led info from the user space */
1470 if (copyin(hpc_ctrldata.data, (void *)&led_info,
1471 sizeof (hpc_led_info_t)) != 0) {
1472 rv = ENXIO;
1473 break;
1474 }
1475
1476 /* get the state of LED information */
1477 if (hpc_nexus_control(cbp->slot_handle,
1478 HPC_CTRL_GET_LED_STATE,
1479 (caddr_t)&led_info) != 0) {
1480 rv = ENXIO;
1481 break;
1482 }
1483
1484 /* copy the led info to the user space */
1485 if (copyout((void *)&led_info, hpc_ctrldata.data,
1486 sizeof (hpc_led_info_t)) != 0) {
1487 rv = ENXIO;
1488 break;
1489 }
1490 break;
1491
1492 case HPC_CTRL_SET_LED_STATE:
1493 /* copy the led info from the user space */
1494 if (copyin(hpc_ctrldata.data, (void *)&led_info,
1495 sizeof (hpc_led_info_t)) != 0) {
1496 rv = ENXIO;
1497 break;
1498 }
1499
1500 /* set the state of an LED */
1501 if (hpc_nexus_control(cbp->slot_handle,
1502 HPC_CTRL_SET_LED_STATE,
1503 (caddr_t)&led_info) != 0) {
1504 rv = ENXIO;
1505 break;
1506 }
1507
1508 break;
1509
1510 case HPC_CTRL_ENABLE_SLOT:
1511 /*
1512 * Enable the slot for hotplug operations.
1513 */
1514 cbp->disabled = B_FALSE;
1515
1516 /* tell the HPC driver also */
1517 (void) hpc_nexus_control(cbp->slot_handle,
1518 HPC_CTRL_ENABLE_SLOT, NULL);
1519
1520 break;
1521
1522 case HPC_CTRL_DISABLE_SLOT:
1523 /*
1524 * Disable the slot for hotplug operations.
1525 */
1526 cbp->disabled = B_TRUE;
1527
1528 /* tell the HPC driver also */
1529 (void) hpc_nexus_control(cbp->slot_handle,
1530 HPC_CTRL_DISABLE_SLOT, NULL);
1531
1532 break;
1533
1534 case HPC_CTRL_ENABLE_AUTOCFG:
1535 /*
1536 * Enable auto configuration on this slot.
1537 */
1538 cbp->auto_config = B_TRUE;
1539
1540 /* tell the HPC driver also */
1541 (void) hpc_nexus_control(cbp->slot_handle,
1542 HPC_CTRL_ENABLE_AUTOCFG, NULL);
1543 break;
1544
1545 case HPC_CTRL_DISABLE_AUTOCFG:
1546 /*
1547 * Disable auto configuration on this slot.
1548 */
1549 cbp->auto_config = B_FALSE;
1550
1551 /* tell the HPC driver also */
1552 (void) hpc_nexus_control(cbp->slot_handle,
1553 HPC_CTRL_DISABLE_AUTOCFG, NULL);
1554
1555 break;
1556
1557 case HPC_CTRL_GET_BOARD_TYPE:
1558 {
1559 hpc_board_type_t board_type;
1560
1561 /*
1562 * Get board type data structure, hpc_board_type_t.
1563 */
1564 if (hpc_nexus_control(cbp->slot_handle,
1565 HPC_CTRL_GET_BOARD_TYPE,
1566 (caddr_t)&board_type) != 0) {
1567 rv = ENXIO;
1568 break;
1569 }
1570
1571 /* copy the board type info to the user space */
1572 if (copyout((void *)&board_type, hpc_ctrldata.data,
1573 sizeof (hpc_board_type_t)) != 0) {
1574 rv = ENXIO;
1575 break;
1576 }
1577
1578 break;
1579 }
1580
1581 case HPC_CTRL_GET_SLOT_INFO:
1582 {
1583 hpc_slot_info_t slot_info;
1584
1585 /*
1586 * Get slot information structure, hpc_slot_info_t.
1587 */
1588 slot_info.version = HPC_SLOT_INFO_VERSION;
1589 slot_info.slot_type = 0;
1590 slot_info.pci_slot_capabilities = 0;
1591 slot_info.pci_dev_num =
1592 (uint16_t)AP_MINOR_NUM_TO_CB_INSTANCE(ap_minor);
1593 (void) strcpy(slot_info.pci_slot_name, cbp->name);
1594
1595 /* copy the slot info structure to the user space */
1596 if (copyout((void *)&slot_info, hpc_ctrldata.data,
1597 sizeof (hpc_slot_info_t)) != 0) {
1598 rv = ENXIO;
1599 break;
1600 }
1601
1602 break;
1603 }
1604
1605 case HPC_CTRL_GET_CARD_INFO:
1606 {
1607 hpc_card_info_t card_info;
1608 ddi_acc_handle_t handle;
1609
1610 /*
1611 * Get card information structure, hpc_card_info_t.
1612 */
1613
1614 if (cbp->card_present == B_FALSE) {
1615 rv = ENXIO;
1616 break;
1617 }
1618 /* verify that the card is configured */
1619 if (cbp->ostate != AP_OSTATE_CONFIGURED) {
1620 /* either the card is not present or */
1621 /* it is not configured. */
1622 rv = ENXIO;
1623 break;
1624 }
1625
1626 /* get the information from the PCI config header */
1627 /* for the function 0. */
1628 for (child_dip = ddi_get_child(cbp->cb_dip); child_dip;
1629 child_dip = ddi_get_next_sibling(child_dip))
1630 if (strcmp("pcs", ddi_get_name(child_dip)))
1631 break;
1632
1633 if (!child_dip) {
1634 rv = ENXIO;
1635 break;
1636 }
1637
1638 if (pci_config_setup(child_dip, &handle)
1639 != DDI_SUCCESS) {
1640 rv = EIO;
1641 break;
1642 }
1643 card_info.prog_class = pci_config_get8(handle,
1644 PCI_CONF_PROGCLASS);
1645 card_info.base_class = pci_config_get8(handle,
1646 PCI_CONF_BASCLASS);
1647 card_info.sub_class = pci_config_get8(handle,
1648 PCI_CONF_SUBCLASS);
1649 card_info.header_type = pci_config_get8(handle,
1650 PCI_CONF_HEADER);
1651 pci_config_teardown(&handle);
1652
1653 /* copy the card info structure to the user space */
1654 if (copyout((void *)&card_info, hpc_ctrldata.data,
1655 sizeof (hpc_card_info_t)) != 0) {
1656 rv = ENXIO;
1657 break;
1658 }
1659
1660 break;
1661 }
1662
1663 default:
1664 rv = EINVAL;
1665 break;
1666 }
1667
1668 mutex_exit(&cbp->cb_mutex);
1669 break;
1670
1671 default:
1672 rv = ENOTTY;
1673 }
1674
1675 if (cmd != DEVCTL_AP_CONTROL)
1676 ndi_dc_freehdl(dcp);
1677
1678 cardbus_err(cbp->cb_dip, 7,
1679 "cardbus_ioctl: rv = 0x%x", rv);
1680
1681 return (rv);
1682 }
1683
1684 struct cardbus_pci_desc {
1685 char *name;
1686 ushort_t offset;
1687 int (*cfg_get_func)();
1688 char *fmt;
1689 };
1690
1691 #define CFG_GET(f) ((int(*)())(uintptr_t)f)
1692
1693 static struct cardbus_pci_desc generic_pci_cfg[] = {
1694 { "VendorId =", 0, CFG_GET(pci_config_get16), "%s 0x%04x" },
1695 { "DeviceId =", 2, CFG_GET(pci_config_get16), "%s 0x%04x" },
1696 { "Command =", 4, CFG_GET(pci_config_get16), "%s 0x%04x" },
1697 { "Status =", 6, CFG_GET(pci_config_get16), "%s 0x%04x" },
1698 { "Latency =", 0xd, CFG_GET(pci_config_get8), "%s 0x%02x" },
1699 { "BASE0 =", 0x10, CFG_GET(pci_config_get32), "%s 0x%08x" },
1700 { "BASE1 =", 0x14, CFG_GET(pci_config_get32), "%s 0x%08x" },
1701 { "BASE2 =", 0x18, CFG_GET(pci_config_get32), "%s 0x%08x" },
1702 { "BASE3 =", 0x1c, CFG_GET(pci_config_get32), "%s 0x%08x" },
1703 { "BASE4 =", 0x20, CFG_GET(pci_config_get32), "%s 0x%08x" },
1704 { "CIS Pointer =", 0x28, CFG_GET(pci_config_get32), "%s 0x%08x" },
1705 { "ILINE =", 0x3c, CFG_GET(pci_config_get8), "%s 0x%02x" },
1706 { "IPIN =", 0x3d, CFG_GET(pci_config_get8), "%s 0x%02x" },
1707 { NULL, 0, NULL, NULL }
1708 };
1709
1710 static struct cardbus_pci_desc cardbus_pci_cfg[] = {
1711 { "VendorId =", 0, CFG_GET(pci_config_get16), "%s 0x%04x" },
1712 { "DeviceId =", 2, CFG_GET(pci_config_get16), "%s 0x%04x" },
1713 { "Command =", 4, CFG_GET(pci_config_get16), "%s 0x%04x" },
1714 { "Status =", 6, CFG_GET(pci_config_get16), "%s 0x%04x" },
1715 { "CacheLineSz =", 0xc, CFG_GET(pci_config_get8), "%s 0x%02x" },
1716 { "Latency =", 0xd, CFG_GET(pci_config_get8), "%s 0x%02x" },
1717 { "MemBase Addr=", 0x10, CFG_GET(pci_config_get32), "%s 0x%08x" },
1718 { "Pri Bus =", 0x18, CFG_GET(pci_config_get8), "%s 0x%02x" },
1719 { "Sec Bus =", 0x19, CFG_GET(pci_config_get8), "%s 0x%02x" },
1720 { "Sub Bus =", 0x1a, CFG_GET(pci_config_get8), "%s 0x%02x" },
1721 { "CBus Latency=", 0x1b, CFG_GET(pci_config_get8), "%s 0x%02x" },
1722 { "Mem0 Base =", 0x1c, CFG_GET(pci_config_get32), "%s 0x%08x" },
1723 { "Mem0 Limit =", 0x20, CFG_GET(pci_config_get32), "%s 0x%08x" },
1724 { "Mem1 Base =", 0x24, CFG_GET(pci_config_get32), "%s 0x%08x" },
1725 { "Mem1 Limit =", 0x28, CFG_GET(pci_config_get32), "%s 0x%08x" },
1726 { "I/O0 Base =", 0x2c, CFG_GET(pci_config_get32), "%s 0x%08x" },
1727 { "I/O0 Limit =", 0x30, CFG_GET(pci_config_get32), "%s 0x%08x" },
1728 { "I/O1 Base =", 0x34, CFG_GET(pci_config_get32), "%s 0x%08x" },
1729 { "I/O1 Limit =", 0x38, CFG_GET(pci_config_get32), "%s 0x%08x" },
1730 { "ILINE =", 0x3c, CFG_GET(pci_config_get8), "%s 0x%02x" },
1731 { "IPIN =", 0x3d, CFG_GET(pci_config_get8), "%s 0x%02x" },
1732 { "Bridge Ctrl =", 0x3e, CFG_GET(pci_config_get16), "%s 0x%04x" },
1733 { "Legacy Addr =", 0x44, CFG_GET(pci_config_get32), "%s 0x%08x" },
1734 { NULL, 0, NULL, NULL }
1735 };
1736
1737 static void
cardbus_dump(struct cardbus_pci_desc * spcfg,ddi_acc_handle_t handle)1738 cardbus_dump(struct cardbus_pci_desc *spcfg, ddi_acc_handle_t handle)
1739 {
1740 int i;
1741 for (i = 0; spcfg[i].name; i++) {
1742
1743 cmn_err(CE_NOTE, spcfg[i].fmt, spcfg[i].name,
1744 spcfg[i].cfg_get_func(handle, spcfg[i].offset));
1745 }
1746
1747 }
1748
1749 void
cardbus_dump_pci_node(dev_info_t * dip)1750 cardbus_dump_pci_node(dev_info_t *dip)
1751 {
1752 dev_info_t *next;
1753 struct cardbus_pci_desc *spcfg;
1754 ddi_acc_handle_t config_handle;
1755 uint32_t VendorId;
1756
1757 cmn_err(CE_NOTE, "\nPCI leaf node of dip 0x%p:\n", (void *)dip);
1758 for (next = ddi_get_child(dip); next;
1759 next = ddi_get_next_sibling(next)) {
1760
1761 VendorId = ddi_getprop(DDI_DEV_T_ANY, next,
1762 DDI_PROP_CANSLEEP|DDI_PROP_DONTPASS,
1763 "vendor-id", -1);
1764 if (VendorId == -1) {
1765 /* not a pci device */
1766 continue;
1767 }
1768
1769 if (pci_config_setup(next, &config_handle) != DDI_SUCCESS) {
1770 cmn_err(CE_WARN, "!pcic child: non pci device\n");
1771 continue;
1772 }
1773
1774 spcfg = generic_pci_cfg;
1775 cardbus_dump(spcfg, config_handle);
1776 pci_config_teardown(&config_handle);
1777
1778 }
1779
1780 }
1781
1782 void
cardbus_dump_pci_config(dev_info_t * dip)1783 cardbus_dump_pci_config(dev_info_t *dip)
1784 {
1785 struct cardbus_pci_desc *spcfg;
1786 ddi_acc_handle_t config_handle;
1787
1788 if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
1789 cmn_err(CE_WARN,
1790 "!pci_config_setup() failed on 0x%p", (void *)dip);
1791 return;
1792 }
1793
1794 spcfg = cardbus_pci_cfg;
1795 cardbus_dump(spcfg, config_handle);
1796
1797 pci_config_teardown(&config_handle);
1798 }
1799
1800 void
cardbus_dump_socket(dev_info_t * dip)1801 cardbus_dump_socket(dev_info_t *dip)
1802 {
1803 ddi_acc_handle_t iohandle;
1804 caddr_t ioaddr;
1805 ddi_device_acc_attr_t attr;
1806 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
1807 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
1808 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
1809 if (ddi_regs_map_setup(dip, 1,
1810 (caddr_t *)&ioaddr,
1811 0,
1812 4096,
1813 &attr, &iohandle) != DDI_SUCCESS) {
1814 cmn_err(CE_WARN, "Failed to map address for 0x%p", (void *)dip);
1815 return;
1816 }
1817
1818 cmn_err(CE_NOTE, "////////////////////////////////////////");
1819 cmn_err(CE_NOTE, "SOCKET_EVENT = [0x%x]",
1820 ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_STATUS_EVENT)));
1821 cmn_err(CE_NOTE, "SOCKET_MASK = [0x%x]",
1822 ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_STATUS_MASK)));
1823 cmn_err(CE_NOTE, "SOCKET_STATE = [0x%x]",
1824 ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_PRESENT_STATE)));
1825 cmn_err(CE_NOTE, "////////////////////////////////////////");
1826
1827 ddi_regs_map_free(&iohandle);
1828
1829 }
1830