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