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
62typedef 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
79extern int cardbus_debug;
80extern int number_of_cardbus_cards;
81
82static int cardbus_autocfg_enabled = 1;	/* auto config is enabled by default */
83
84/* static functions */
85static int cardbus_event_handler(caddr_t slot_arg, uint_t event_mask);
86static int cardbus_pci_control(caddr_t ops_arg, hpc_slot_t slot_hdl,
87				int request, caddr_t arg);
88static int cardbus_new_slot_state(dev_info_t *dip, hpc_slot_t hdl,
89				hpc_slot_info_t *slot_info, int slot_state);
90static int cardbus_list_occupants(dev_info_t *dip, void *hdl);
91static void create_occupant_props(dev_info_t *self, dev_t dev);
92static void delete_occupant_props(dev_info_t *dip, dev_t dev);
93static int cardbus_configure_ap(cbus_t *cbp);
94static int cardbus_unconfigure_ap(cbus_t *cbp);
95static int cbus_unconfigure(dev_info_t *devi, int prim_bus);
96void cardbus_dump_pci_config(dev_info_t *dip);
97void cardbus_dump_pci_node(dev_info_t *dip);
98
99int
100cardbus_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
168static int
169cardbus_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
343static int
344cardbus_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 */
455static int
456cardbus_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
590static int
591cardbus_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
617static void
618create_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
648static void
649delete_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 */
662static int
663cardbus_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 */
787static int
788cardbus_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
851int
852cbus_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
923int
924cardbus_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 */
969static int
970cbus_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
1026boolean_t
1027cardbus_is_cb_minor(dev_t dev)
1028{
1029	return (AP_IS_CB_MINOR(getminor(dev)) ? B_TRUE : B_FALSE);
1030}
1031
1032int
1033cardbus_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*/
1084int
1085cardbus_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*/
1112int
1113cardbus_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
1682struct cardbus_pci_desc {
1683	char	*name;
1684	ushort_t	offset;
1685	int	(*cfg_get_func)();
1686	char	*fmt;
1687};
1688
1689#define	CFG_GET(f)	((int(*)())(uintptr_t)f)
1690
1691static struct cardbus_pci_desc generic_pci_cfg[] = {
1692	    { "VendorId    =", 0, CFG_GET(pci_config_get16), "%s 0x%04x" },
1693	    { "DeviceId    =", 2, CFG_GET(pci_config_get16), "%s 0x%04x" },
1694	    { "Command     =", 4, CFG_GET(pci_config_get16), "%s 0x%04x" },
1695	    { "Status      =", 6, CFG_GET(pci_config_get16), "%s 0x%04x" },
1696	    { "Latency     =", 0xd, CFG_GET(pci_config_get8), "%s 0x%02x" },
1697	    { "BASE0       =", 0x10, CFG_GET(pci_config_get32), "%s 0x%08x" },
1698	    { "BASE1       =", 0x14, CFG_GET(pci_config_get32), "%s 0x%08x" },
1699	    { "BASE2       =", 0x18, CFG_GET(pci_config_get32), "%s 0x%08x" },
1700	    { "BASE3       =", 0x1c, CFG_GET(pci_config_get32), "%s 0x%08x" },
1701	    { "BASE4       =", 0x20, CFG_GET(pci_config_get32), "%s 0x%08x" },
1702	    { "CIS Pointer =", 0x28, CFG_GET(pci_config_get32), "%s 0x%08x" },
1703	    { "ILINE       =", 0x3c, CFG_GET(pci_config_get8), "%s 0x%02x" },
1704	    { "IPIN        =", 0x3d, CFG_GET(pci_config_get8), "%s 0x%02x" },
1705	    { NULL, 0, NULL, NULL }
1706};
1707
1708static struct cardbus_pci_desc cardbus_pci_cfg[] = {
1709	    { "VendorId    =", 0, CFG_GET(pci_config_get16), "%s 0x%04x" },
1710	    { "DeviceId    =", 2, CFG_GET(pci_config_get16), "%s 0x%04x" },
1711	    { "Command     =", 4, CFG_GET(pci_config_get16), "%s 0x%04x" },
1712	    { "Status      =", 6, CFG_GET(pci_config_get16), "%s 0x%04x" },
1713	    { "CacheLineSz =", 0xc, CFG_GET(pci_config_get8), "%s 0x%02x" },
1714	    { "Latency     =", 0xd, CFG_GET(pci_config_get8), "%s 0x%02x" },
1715	    { "MemBase Addr=", 0x10, CFG_GET(pci_config_get32), "%s 0x%08x" },
1716	    { "Pri Bus     =", 0x18, CFG_GET(pci_config_get8), "%s 0x%02x" },
1717	    { "Sec Bus     =", 0x19, CFG_GET(pci_config_get8), "%s 0x%02x" },
1718	    { "Sub Bus     =", 0x1a, CFG_GET(pci_config_get8), "%s 0x%02x" },
1719	    { "CBus Latency=", 0x1b, CFG_GET(pci_config_get8), "%s 0x%02x" },
1720	    { "Mem0 Base   =", 0x1c, CFG_GET(pci_config_get32), "%s 0x%08x" },
1721	    { "Mem0 Limit  =", 0x20, CFG_GET(pci_config_get32), "%s 0x%08x" },
1722	    { "Mem1 Base   =", 0x24, CFG_GET(pci_config_get32), "%s 0x%08x" },
1723	    { "Mem1 Limit  =", 0x28, CFG_GET(pci_config_get32), "%s 0x%08x" },
1724	    { "I/O0 Base   =", 0x2c, CFG_GET(pci_config_get32), "%s 0x%08x" },
1725	    { "I/O0 Limit  =", 0x30, CFG_GET(pci_config_get32), "%s 0x%08x" },
1726	    { "I/O1 Base   =", 0x34, CFG_GET(pci_config_get32), "%s 0x%08x" },
1727	    { "I/O1 Limit  =", 0x38, CFG_GET(pci_config_get32), "%s 0x%08x" },
1728	    { "ILINE       =", 0x3c, CFG_GET(pci_config_get8), "%s 0x%02x" },
1729	    { "IPIN        =", 0x3d, CFG_GET(pci_config_get8), "%s 0x%02x" },
1730	    { "Bridge Ctrl =", 0x3e, CFG_GET(pci_config_get16), "%s 0x%04x" },
1731	    { "Legacy Addr =", 0x44, CFG_GET(pci_config_get32), "%s 0x%08x" },
1732	    { NULL, 0, NULL, NULL }
1733};
1734
1735static void
1736cardbus_dump(struct cardbus_pci_desc *spcfg, ddi_acc_handle_t handle)
1737{
1738	int	i;
1739	for (i = 0; spcfg[i].name; i++) {
1740
1741		cmn_err(CE_NOTE, spcfg[i].fmt, spcfg[i].name,
1742		    spcfg[i].cfg_get_func(handle, spcfg[i].offset));
1743	}
1744
1745}
1746
1747void
1748cardbus_dump_pci_node(dev_info_t *dip)
1749{
1750	dev_info_t *next;
1751	struct cardbus_pci_desc *spcfg;
1752	ddi_acc_handle_t config_handle;
1753	uint32_t VendorId;
1754
1755	cmn_err(CE_NOTE, "\nPCI leaf node of dip 0x%p:\n", (void *)dip);
1756	for (next = ddi_get_child(dip); next;
1757	    next = ddi_get_next_sibling(next)) {
1758
1759		VendorId = ddi_getprop(DDI_DEV_T_ANY, next,
1760		    DDI_PROP_CANSLEEP|DDI_PROP_DONTPASS,
1761		    "vendor-id", -1);
1762		if (VendorId == -1) {
1763			/* not a pci device */
1764			continue;
1765		}
1766
1767		if (pci_config_setup(next, &config_handle) != DDI_SUCCESS) {
1768			cmn_err(CE_WARN, "!pcic child: non pci device\n");
1769			continue;
1770		}
1771
1772		spcfg = generic_pci_cfg;
1773		cardbus_dump(spcfg, config_handle);
1774		pci_config_teardown(&config_handle);
1775
1776	}
1777
1778}
1779
1780void
1781cardbus_dump_pci_config(dev_info_t *dip)
1782{
1783	struct cardbus_pci_desc *spcfg;
1784	ddi_acc_handle_t config_handle;
1785
1786	if (pci_config_setup(dip, &config_handle) != DDI_SUCCESS) {
1787		cmn_err(CE_WARN,
1788		    "!pci_config_setup() failed on 0x%p", (void *)dip);
1789		return;
1790	}
1791
1792	spcfg = cardbus_pci_cfg;
1793	cardbus_dump(spcfg, config_handle);
1794
1795	pci_config_teardown(&config_handle);
1796}
1797
1798void
1799cardbus_dump_socket(dev_info_t *dip)
1800{
1801	ddi_acc_handle_t	iohandle;
1802	caddr_t		ioaddr;
1803	ddi_device_acc_attr_t attr;
1804	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
1805	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
1806	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
1807	if (ddi_regs_map_setup(dip, 1,
1808	    (caddr_t *)&ioaddr,
1809	    0,
1810	    4096,
1811	    &attr, &iohandle) != DDI_SUCCESS) {
1812		cmn_err(CE_WARN, "Failed to map address for 0x%p", (void *)dip);
1813		return;
1814	}
1815
1816	cmn_err(CE_NOTE, "////////////////////////////////////////");
1817	cmn_err(CE_NOTE, "SOCKET_EVENT  = [0x%x]",
1818	    ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_STATUS_EVENT)));
1819	cmn_err(CE_NOTE, "SOCKET_MASK   = [0x%x]",
1820	    ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_STATUS_MASK)));
1821	cmn_err(CE_NOTE, "SOCKET_STATE  = [0x%x]",
1822	    ddi_get32(iohandle, (uint32_t *)(ioaddr+CB_PRESENT_STATE)));
1823	cmn_err(CE_NOTE, "////////////////////////////////////////");
1824
1825	ddi_regs_map_free(&iohandle);
1826
1827}
1828