summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/1394/adapters/hci1394_attach.c
blob: 061253cd984e2410bbab1047e3ab0470cfce78d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * hci1394_attach.c
 *    HBA attach() routine with associated funtions.
 */

#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/modctl.h>
#include <sys/stat.h>
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
#include <sys/pci.h>

#include <sys/1394/h1394.h>
#include <sys/1394/adapters/hci1394.h>
#include <sys/1394/adapters/hci1394_extern.h>


/*
 * Attach State Information. These states are used to track the status of the
 * attach.  They are bit offsets.
 */
#define	STATE_ZALLOC		0
#define	STATE_ISR_INIT		1
#define	STATE_MINOR_NODE	2
#define	STATE_HW_INIT		3
#define	STATE_PHASE2		4
#define	STATE_POWER_INIT	5
#define	STATE_H1394_ATTACH	6
#define	STATE_ISR_HANDLER	7
#define	STATE_STARTUP		8

static void hci1394_statebit_set(uint64_t *state, uint_t statebit);
static boolean_t hci1394_statebit_tst(uint64_t state, uint_t statebit);

static void hci1394_cleanup(hci1394_state_t *soft_state, uint64_t attach_state);

static int hci1394_hardware_init(hci1394_state_t *soft_state);
static int hci1394_hardware_resume(hci1394_state_t *soft_state);

static int hci1394_pci_init(hci1394_state_t *soft_state);
static void hci1394_pci_resume(hci1394_state_t *soft_state);

static void hci1394_soft_state_phase1_init(hci1394_state_t *soft_state,
    dev_info_t *dip, int instance);
static void hci1394_soft_state_phase2_init(hci1394_state_t *soft_state);

static int hci1394_resmap_get(hci1394_state_t *soft_state);
static void hci1394_resmap_free(hci1394_state_t *soft_state);



int
hci1394_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
	hci1394_state_t *soft_state;
	uint64_t attach_state = 0;
	int instance;
	int status;

	switch (cmd) {
	case DDI_ATTACH:
		instance = ddi_get_instance(dip);
		status = ddi_soft_state_zalloc(hci1394_statep, instance);
		if (status != DDI_SUCCESS) {
			return (DDI_FAILURE);
		}
		soft_state = ddi_get_soft_state(hci1394_statep, instance);
		if (soft_state == NULL) {
			ddi_soft_state_free(hci1394_statep, instance);
			return (DDI_FAILURE);
		}
		hci1394_statebit_set(&attach_state, STATE_ZALLOC);

		hci1394_soft_state_phase1_init(soft_state, dip, instance);

		/* get iblock cookie, other interrupt init stuff */
		status = hci1394_isr_init(soft_state);
		if (status != DDI_SUCCESS) {
			hci1394_cleanup(soft_state, attach_state);
			return (DDI_FAILURE);
		}
		hci1394_statebit_set(&attach_state, STATE_ISR_INIT);

		status = ddi_create_minor_node(dip, "devctl", S_IFCHR,
		    instance, DDI_NT_NEXUS, 0);
		if (status != DDI_SUCCESS) {
			hci1394_cleanup(soft_state, attach_state);
			return (DDI_FAILURE);
		}
		hci1394_statebit_set(&attach_state, STATE_MINOR_NODE);

		status = hci1394_hardware_init(soft_state);
		if (status != DDI_SUCCESS) {
			hci1394_cleanup(soft_state, attach_state);
			return (DDI_FAILURE);
		}
		hci1394_statebit_set(&attach_state, STATE_HW_INIT);

		hci1394_soft_state_phase2_init(soft_state);
		hci1394_statebit_set(&attach_state, STATE_PHASE2);

		/* build up the reserved addresses map */
		status = hci1394_resmap_get(soft_state);
		if (status != DDI_SUCCESS) {
			hci1394_cleanup(soft_state, attach_state);
			return (DDI_FAILURE);
		}

		/* "attach" to the Services Layer */
		status = h1394_attach(&soft_state->halinfo, DDI_ATTACH,
		    &soft_state->drvinfo.di_sl_private);
		if (status != DDI_SUCCESS) {
			hci1394_resmap_free(soft_state);
			hci1394_cleanup(soft_state, attach_state);
			return (DDI_FAILURE);
		}
		/* free the reserved addresses map */
		hci1394_resmap_free(soft_state);

		hci1394_statebit_set(&attach_state, STATE_H1394_ATTACH);
		status = hci1394_isr_handler_init(soft_state);
		if (status != DDI_SUCCESS) {
			hci1394_cleanup(soft_state, attach_state);
			return (DDI_FAILURE);
		}
		hci1394_statebit_set(&attach_state, STATE_ISR_HANDLER);

		/* Report that driver was loaded */
		ddi_report_dev(dip);

		/*
		 * Turn on link, Reset Bus, enable interrupts.  Should be the
		 * last routine called in attach. The statebit for starup must
		 * be set before startup is called since startup enables
		 * interrupts.
		 */
		hci1394_statebit_set(&attach_state, STATE_STARTUP);
		status = hci1394_ohci_startup(soft_state->ohci);
		if (status != DDI_SUCCESS) {
			hci1394_cleanup(soft_state, attach_state);
			return (DDI_FAILURE);
		}

		return (DDI_SUCCESS);

	case DDI_RESUME:
		instance = ddi_get_instance(dip);
		soft_state = ddi_get_soft_state(hci1394_statep, instance);
		if (soft_state == NULL) {
			return (DDI_FAILURE);
		}

		status = hci1394_hardware_resume(soft_state);
		if (status != DDI_SUCCESS) {
			return (DDI_FAILURE);
		}

		/*
		 * set our state back to initial.  The next bus reset were
		 * about to generate will set us in motion.
		 */
		soft_state->drvinfo.di_drvstate.ds_state = HCI1394_INITIAL;

		/* turn on the link, enable interrupts, reset the bus */
		status = hci1394_ohci_startup(soft_state->ohci);
		if (status != DDI_SUCCESS) {
			return (DDI_FAILURE);
		}

		/* tell the Services Layer that we are resuming */
		status = h1394_attach(&soft_state->halinfo, DDI_RESUME,
		    &soft_state->drvinfo.di_sl_private);
		if (status != DDI_SUCCESS) {
			return (DDI_FAILURE);
		}

		return (DDI_SUCCESS);

	default:
		break;
	}

	return (DDI_FAILURE);
}


/*
 * hci1394_soft_state_phase1_init()
 *    First part soft_state initialization.  This should be called before any
 *    other initialization routines are called.  Anything that requires cleanup
 *    on detach or after an attach failure should be setup in phase2 init (i.e.
 *    mutex's, cv's, etc.)
 */
static void
hci1394_soft_state_phase1_init(hci1394_state_t *soft_state, dev_info_t *dip,
    int instance)
{
	ASSERT(soft_state != NULL);

	soft_state->drvinfo.di_dip = dip;
	soft_state->drvinfo.di_instance = instance;

	/* current bus generation */
	soft_state->drvinfo.di_gencnt = 0;

	soft_state->drvinfo.di_sl_private = NULL;

	/* initialize statistics */
	soft_state->drvinfo.di_stats.st_bus_reset_count = 0;
	soft_state->drvinfo.di_stats.st_selfid_count = 0;
	soft_state->drvinfo.di_stats.st_phy_isr = 0;
	soft_state->drvinfo.di_stats.st_phy_loop_err = 0;
	soft_state->drvinfo.di_stats.st_phy_pwrfail_err = 0;
	soft_state->drvinfo.di_stats.st_phy_timeout_err = 0;
	soft_state->drvinfo.di_stats.st_phy_portevt_err = 0;

	soft_state->swap_data = B_FALSE;
	soft_state->sl_selfid_buf = NULL;

	/* halinfo is what is passed up to the Services Layer */
	soft_state->halinfo.hal_private = soft_state;
	soft_state->halinfo.dip = soft_state->drvinfo.di_dip;
	soft_state->halinfo.hal_events = hci1394_evts;
	soft_state->halinfo.max_generation = OHCI_BUSGEN_MAX;
	soft_state->halinfo.addr_map_num_entries = HCI1394_ADDR_MAP_SIZE;
	soft_state->halinfo.addr_map = hci1394_addr_map;
	hci1394_buf_attr_get(&soft_state->halinfo.dma_attr);
}


/*
 * hci1394_soft_state_phase2_init()
 *    Second part of soft_state initialization.  This should be called after a
 *    successful hardware_init() and before the call to h1394_attach().
 */
static void
hci1394_soft_state_phase2_init(hci1394_state_t *soft_state)
{
	ASSERT(soft_state != NULL);

	/*
	 * Setup our initial driver state.  This requires the HW iblock
	 * cookie so this must be setup in phase2_init()
	 */
	soft_state->drvinfo.di_drvstate.ds_state = HCI1394_INITIAL;
	mutex_init(&soft_state->drvinfo.di_drvstate.ds_mutex, NULL,
	    MUTEX_DRIVER, soft_state->drvinfo.di_iblock_cookie);

	/*
	 * halinfo.acc_attr tells the services layer what our buffer access
	 * attributes are.  drvinfo.di_buf_attr it initialized in pci_init so
	 * this must be setup in phase2_init()
	 */
	soft_state->halinfo.acc_attr = soft_state->drvinfo.di_buf_attr;

	/*
	 * halinfo.hw_interrupt tells the services layer what our
	 * iblock_cookie is. drvinfo.di_iblock_cookie is setup in isr_init so
	 * this must be setup in phase2_init()
	 */
	soft_state->halinfo.hw_interrupt = soft_state->drvinfo.di_iblock_cookie;

	/*
	 * Read in our node capabilities.  Since we are calling into csr
	 * we must have first called hardware_init().  Therefore, this must
	 * be in phase2_init().
	 */
	hci1394_csr_node_capabilities(soft_state->csr,
	    &soft_state->halinfo.node_capabilities);

	/*
	 * Read in our bus capabilities.  Since we are calling into ohci
	 * we must have first called hardware_init().  Therefore, this must
	 * be in phase2_init().
	 */
	hci1394_ohci_bus_capabilities(soft_state->ohci,
	    &soft_state->halinfo.bus_capabilities);

	/*
	 * Setup our async command overhead. When a target driver or the ARREQ
	 * engine allocates a command, the services layer will tack on space
	 * for itself and the HAL so we do not have to manage memory for every
	 * command.  hal_overhead is how much memory the hal requires to track
	 * an async command. Since we are calling into async we must have first
	 * called hardware_init().  Therefore, this must be in phase2_init().
	 */
	soft_state->halinfo.hal_overhead = hci1394_async_cmd_overhead();
}


/*
 * hci1394_hardware_init()
 *    Initialize the adapter hardware.  This should be called during
 *    the initial attach().
 */
static int
hci1394_hardware_init(hci1394_state_t *soft_state)
{
	int status;


	ASSERT(soft_state != NULL);

	/* Initialize PCI config registers */
	status = hci1394_pci_init(soft_state);
	if (status != DDI_SUCCESS) {
		return (DDI_FAILURE);
	}

	/* Initialize the OpenHCI Hardware */
	status = hci1394_ohci_init(soft_state, &soft_state->drvinfo,
	    &soft_state->ohci);
	if (status != DDI_SUCCESS) {
		hci1394_pci_fini(soft_state);
		return (DDI_FAILURE);
	}

	/* Initialize SW based CSR registers */
	hci1394_csr_init(&soft_state->drvinfo, soft_state->ohci,
	    &soft_state->csr);

	/* Initialize the Asynchronous Q's */
	status = hci1394_async_init(&soft_state->drvinfo, soft_state->ohci,
	    soft_state->csr, &soft_state->async);
	if (status != DDI_SUCCESS) {
		hci1394_csr_fini(&soft_state->csr);
		hci1394_ohci_fini(&soft_state->ohci);
		hci1394_pci_fini(soft_state);
		return (DDI_FAILURE);
	}

	/* Initialize the Isochronous logic */
	hci1394_isoch_init(&soft_state->drvinfo, soft_state->ohci,
	    &soft_state->isoch);

	/* Initialize any Vendor Specific Registers */
	status = hci1394_vendor_init(&soft_state->drvinfo, soft_state->ohci,
	    &soft_state->vendor_info, &soft_state->vendor);
	if (status != DDI_SUCCESS) {
		hci1394_isoch_fini(&soft_state->isoch);
		hci1394_async_fini(&soft_state->async);
		hci1394_csr_fini(&soft_state->csr);
		hci1394_ohci_fini(&soft_state->ohci);
		hci1394_pci_fini(soft_state);
		return (DDI_FAILURE);
	}

	return (DDI_SUCCESS);
}


/*
 * hci1394_hardware_resume()
 *    Resume the adapter HW.  This routine will be called during resume after
 *    a successful system suspend.  All memory should be in the state it was
 *    before the suspend.  All we have to do is re-setup the HW.
 */
static int
hci1394_hardware_resume(hci1394_state_t *soft_state)
{
	int status;


	ASSERT(soft_state != NULL);

	/* re-enable global byte swap (if we using it) */
	hci1394_pci_resume(soft_state);

	/* Re-init the OpenHCI HW */
	status = hci1394_ohci_resume(soft_state->ohci);
	if (status != DDI_SUCCESS) {
		return (DDI_FAILURE);
	}

	/* re-setup our SW based CSR registers */
	hci1394_csr_resume(soft_state->csr);

	/* Re-setup the Async Q's */
	status = hci1394_async_resume(soft_state->async);
	if (status != DDI_SUCCESS) {
		return (DDI_FAILURE);
	}

	/* Re-setup any Vendor Specific Registers */
	status = hci1394_vendor_resume(soft_state->vendor);
	if (status != DDI_SUCCESS) {
		return (DDI_FAILURE);
	}

	return (DDI_SUCCESS);
}


/*
 * hci1394_pci_init()
 *    Map in PCI config space and initialize PCI config space registers.
 */
static int
hci1394_pci_init(hci1394_state_t *soft_state)
{
	int status;
#ifndef _LITTLE_ENDIAN
	uint32_t global_swap;
#endif


	ASSERT(soft_state != NULL);

	/* Setup PCI configuration space */
	status = pci_config_setup(soft_state->drvinfo.di_dip,
	    &soft_state->pci_config);
	if (status != DDI_SUCCESS) {
		return (DDI_FAILURE);
	}


#ifdef _LITTLE_ENDIAN
	/* Start of little endian specific code */
	soft_state->drvinfo.di_reg_attr.devacc_attr_version =
	    DDI_DEVICE_ATTR_V0;
	soft_state->drvinfo.di_reg_attr.devacc_attr_endian_flags =
	    DDI_STRUCTURE_LE_ACC;
	soft_state->drvinfo.di_reg_attr.devacc_attr_dataorder =
	    DDI_STRICTORDER_ACC;
	soft_state->drvinfo.di_buf_attr.devacc_attr_version =
	    DDI_DEVICE_ATTR_V0;
	soft_state->drvinfo.di_buf_attr.devacc_attr_endian_flags =
	    DDI_STRUCTURE_LE_ACC;
	soft_state->drvinfo.di_buf_attr.devacc_attr_dataorder =
	    DDI_STRICTORDER_ACC;
	soft_state->swap_data = B_TRUE;
	/* End of little endian specific code */
#else
	/* Start of big endian specific code */
	/* If PCI_Global_Swap bit is not set, try to set it */
	global_swap = pci_config_get32(soft_state->pci_config,
	    OHCI_PCI_HCI_CONTROL_REG);

	/* Lets see if the global byte swap feature is supported */
	if ((global_swap & OHCI_PCI_GLOBAL_SWAP) == 0) {
		global_swap = global_swap | OHCI_PCI_GLOBAL_SWAP;
		pci_config_put32(soft_state->pci_config,
		    OHCI_PCI_HCI_CONTROL_REG, global_swap);
	}

	global_swap = pci_config_get32(soft_state->pci_config,
	    OHCI_PCI_HCI_CONTROL_REG);

	/* If PCI_Global_Swap bit is not set, it is unsupported */
	if ((global_swap & OHCI_PCI_GLOBAL_SWAP) == 0) {
		soft_state->drvinfo.di_reg_attr.devacc_attr_version =
		    DDI_DEVICE_ATTR_V0;
		soft_state->drvinfo.di_reg_attr.devacc_attr_endian_flags =
		    DDI_STRUCTURE_LE_ACC;
		soft_state->drvinfo.di_reg_attr.devacc_attr_dataorder =
		    DDI_STRICTORDER_ACC;
		soft_state->drvinfo.di_buf_attr.devacc_attr_version =
		    DDI_DEVICE_ATTR_V0;
		soft_state->drvinfo.di_buf_attr.devacc_attr_endian_flags =
		    DDI_STRUCTURE_LE_ACC;
		soft_state->drvinfo.di_buf_attr.devacc_attr_dataorder =
		    DDI_STRICTORDER_ACC;
		soft_state->swap_data = B_TRUE;
	/*
	 * global byte swap is supported.  This should be the case
	 * for almost all of the adapters.
	 */
	} else {
		soft_state->drvinfo.di_reg_attr.devacc_attr_version =
		    DDI_DEVICE_ATTR_V0;
		soft_state->drvinfo.di_reg_attr.devacc_attr_endian_flags =
		    DDI_STRUCTURE_BE_ACC;
		soft_state->drvinfo.di_reg_attr.devacc_attr_dataorder =
		    DDI_STRICTORDER_ACC;
		soft_state->drvinfo.di_buf_attr.devacc_attr_version =
		    DDI_DEVICE_ATTR_V0;
		soft_state->drvinfo.di_buf_attr.devacc_attr_endian_flags =
		    DDI_STRUCTURE_BE_ACC;
		soft_state->drvinfo.di_buf_attr.devacc_attr_dataorder =
		    DDI_STRICTORDER_ACC;
		soft_state->swap_data = B_FALSE;
	}
	/* End of big endian specific code */
#endif

	/* read in vendor Information */
	soft_state->vendor_info.vendor_id =
	    (uint_t)pci_config_get16(soft_state->pci_config, PCI_CONF_VENID);
	soft_state->vendor_info.device_id =
	    (uint_t)pci_config_get16(soft_state->pci_config, PCI_CONF_DEVID);
	soft_state->vendor_info.revision_id =
	    (uint_t)pci_config_get8(soft_state->pci_config, PCI_CONF_REVID);

	return (DDI_SUCCESS);
}


/*
 * hci1394_pci_resume()
 *    Re-Initialize PCI config space registers during a resume.
 */
/* ARGSUSED */
static void
hci1394_pci_resume(hci1394_state_t *soft_state)
{
#ifndef _LITTLE_ENDIAN
	uint32_t global_swap;
#endif


	ASSERT(soft_state != NULL);

#ifdef _LITTLE_ENDIAN
	/* Start of little endian specific code */
	/* nothing to do here yet.  Maybe later?? */
	/* End of little endian specific code */
#else
	/* Start of big endian specific code */
	/* If PCI_Global_Swap bit is not set, try to set it */
	global_swap = pci_config_get32(soft_state->pci_config,
	    OHCI_PCI_HCI_CONTROL_REG);
	/* Try and set GlobalByteSwap */
	if ((global_swap & OHCI_PCI_GLOBAL_SWAP) == 0) {
		global_swap = global_swap | OHCI_PCI_GLOBAL_SWAP;
		pci_config_put32(soft_state->pci_config,
		    OHCI_PCI_HCI_CONTROL_REG, global_swap);
	}
	/* End of big endian specific code */
#endif
}


/*
 * hci1394_resmap_get()
 *    Look for adapter property "reserved-addresses".  This property is used to
 *    reserve 1394 address space so that it will not randomly be given to a
 *    target driver during a 1394 address space alloc.  Some protocols hard
 *    code addresses which make us do this.  The target driver must specifically
 *    ask for these addresses.  This routine should be called before the
 *    call to h1394_attach().
 */
static int
hci1394_resmap_get(hci1394_state_t *soft_state)
{
	h1394_addr_map_t *resv_map;
	int resv_num;
	int status;
	int reslen;
	uint32_t *resptr;
	int rescnt;
	int mapcnt;


	ASSERT(soft_state != NULL);

	/*
	 * See if the "reserved-addresses" property is defined.  The format
	 * should be:
	 *
	 * reserved-addresses=	0x0000ffff,0xf0000B00,0x200,
	 * 			0x0000ffff,0xf0000D00,0x200,
	 * 			0x0000ffff,0xf0000234,0x4;
	 * You can have multiple reserved addresses.  Each reserved address
	 * takes up 3 integers.
	 *    MSWofAddr,LSWofAddr,ByteCount
	 */
	status = ddi_prop_lookup_int_array(DDI_DEV_T_ANY,
	    soft_state->drvinfo.di_dip, DDI_PROP_DONTPASS, "reserved-addresses",
	    (int **)&resptr, (uint_t *)&reslen);
	if (status != DDI_PROP_SUCCESS) {
		/* the property is not defined,  0 reserved addresses */
		soft_state->halinfo.resv_map_num_entries = 0;
		soft_state->halinfo.resv_map = NULL;
		return (DDI_SUCCESS);
	} else if ((reslen < 3) || ((reslen % 3) != 0)) {
		/*
		 * the property is defined but the correct number of integers
		 * is not present.
		 */
		resv_num = 0;
		resv_map = NULL;
		cmn_err(CE_NOTE, "!%s(%d): Invalid reserved-addresses property."
		    " Property ignored", ddi_node_name(
		    soft_state->drvinfo.di_dip), ddi_get_instance(
		    soft_state->drvinfo.di_dip));
	} else {
		/* the property is defined. Alloc space to copy data into */
		resv_num = reslen / 3;
		resv_map = kmem_alloc((sizeof (h1394_addr_map_t) * (resv_num)),
		    KM_SLEEP);

		/* read in the address, length, and set the type to reserved */
		rescnt = 0;
		mapcnt = 0;
		while (rescnt < reslen) {
			resv_map[mapcnt].address =
			    (uint64_t)resptr[rescnt] << 32;
			rescnt++;
			resv_map[mapcnt].address |= (uint64_t)resptr[rescnt];
			rescnt++;
			resv_map[mapcnt].length = (uint64_t)resptr[rescnt];
			rescnt++;
			resv_map[mapcnt].addr_type = H1394_ADDR_RESERVED;
			mapcnt++;
		}
	}

	ddi_prop_free(resptr);

	/*
	 * copy the number of reserved address ranges and a pointer to the map
	 * into halinfo so we can tell the services layer about them in
	 * h1394_attach()
	 */
	soft_state->halinfo.resv_map_num_entries = resv_num;
	soft_state->halinfo.resv_map = resv_map;

	return (DDI_SUCCESS);
}


/*
 * hci1394_resmap_free()
 *    Free up the space alloced in hci1394_resmap_get().  This routine should
 *    be called after h1394_attach().  The HAL does not need this information
 *    and the services layer only uses it for a calculation during attach and
 *    should not refer to the pointer after it returns from h1394_attach().
 */
static void
hci1394_resmap_free(hci1394_state_t *soft_state)
{
	ASSERT(soft_state != NULL);

	/*
	 * if we have one or more reserved map entries, free up the space that
	 * was allocated to store them
	 */
	if (soft_state->halinfo.resv_map_num_entries > 0) {
		ASSERT(soft_state->halinfo.resv_map != NULL);
		kmem_free(soft_state->halinfo.resv_map,
		    (sizeof (h1394_addr_map_t) *
		    soft_state->halinfo.resv_map_num_entries));
	}
}


/*
 * hci1394_statebit_set()
 *     Set bit "statebit" in "state"
 */
static void
hci1394_statebit_set(uint64_t *state, uint_t statebit)
{
	ASSERT(state != NULL);
	ASSERT(statebit < 64);
	*state |= (uint64_t)0x1 << statebit;
}


/*
 * hci1394_statebit_tst()
 *    Return status of bit "statebit".  Is it set or not?
 */
static boolean_t
hci1394_statebit_tst(uint64_t state, uint_t statebit)
{
	uint64_t bitset;
	int status;


	ASSERT(statebit < 64);
	bitset = state & ((uint64_t)0x1 << statebit);
	if (bitset == 0) {
		status = B_FALSE;
	} else {
		status = B_TRUE;
	}
	return (status);
}


/*
 * hci1394_cleanup()
 *    Cleanup after a failed attach
 */
static void
hci1394_cleanup(hci1394_state_t *soft_state, uint64_t attach_state)
{
	int status;


	ASSERT(soft_state != NULL);

	status = hci1394_statebit_tst(attach_state, STATE_STARTUP);
	if (status == B_TRUE) {
		/* Don't allow the HW to generate any more interrupts */
		hci1394_ohci_intr_master_disable(soft_state->ohci);

		/* don't accept anymore commands from services layer */
		(void) hci1394_state_set(&soft_state->drvinfo,
		    HCI1394_SHUTDOWN);

		/* Reset the chip */
		(void) hci1394_ohci_soft_reset(soft_state->ohci);

		/* Flush out async DMA Q's (cancels pendingQ timeouts too) */
		hci1394_async_flush(soft_state->async);
	}

	status = hci1394_statebit_tst(attach_state, STATE_ISR_HANDLER);
	if (status == B_TRUE) {
		hci1394_isr_handler_fini(soft_state);
	}

	status = hci1394_statebit_tst(attach_state, STATE_H1394_ATTACH);
	if (status == B_TRUE) {
		(void) h1394_detach(&soft_state->drvinfo.di_sl_private,
		    DDI_DETACH);
	}

	status = hci1394_statebit_tst(attach_state, STATE_HW_INIT);
	if (status == B_TRUE) {
		hci1394_detach_hardware(soft_state);
	}

	status = hci1394_statebit_tst(attach_state, STATE_MINOR_NODE);
	if (status == B_TRUE) {
		ddi_remove_minor_node(soft_state->drvinfo.di_dip, "devctl");
	}

	status = hci1394_statebit_tst(attach_state, STATE_ISR_INIT);
	if (status == B_TRUE) {
		hci1394_isr_fini(soft_state);
	}

	status = hci1394_statebit_tst(attach_state, STATE_PHASE2);
	if (status == B_TRUE) {
		hci1394_soft_state_fini(soft_state);
	}

	status = hci1394_statebit_tst(attach_state, STATE_ZALLOC);
	if (status == B_TRUE) {
		ddi_soft_state_free(hci1394_statep,
		    soft_state->drvinfo.di_instance);
	}
}