xref: /illumos-gate/usr/src/uts/common/io/1394/adapters/hci1394_isoch.c (revision 89b2a9fbeabf42fa54594df0e5927bcc50a07cc9)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2001-2002 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * hci1394_isoch.c
31  *    HCI HAL isochronous interface routines.  Contains routines used
32  *    internally within the HAL to manage isochronous contexts, and
33  *    also routines called from the Services Layer to manage an isochronous
34  *    DMA resource.
35  */
36 
37 #include <sys/types.h>
38 #include <sys/kmem.h>
39 #include <sys/conf.h>
40 #include <sys/ddi.h>
41 #include <sys/sunddi.h>
42 
43 #include <sys/1394/h1394.h>
44 #include <sys/1394/adapters/hci1394.h>
45 
46 /*
47  * Patchable variable used to indicate the number of microseconds to wait
48  * for an isoch ctxt to stop ("active" goes low) after clearing the "run"
49  * bit
50  */
51 uint_t hci1394_iso_ctxt_stop_delay_uS = 1000;
52 
53 /*
54  * Number of microseconds to wait in hci1394_do_stop() for an isoch ctxt
55  * interrupt handler to complete. Experiments showed that in some cases
56  * the timeout needed was as long as 2 seconds. This is probably due to
57  * significant interrupt processing overhead for certain IXL chains.
58  */
59 uint_t hci1394_iso_ctxt_stop_intr_timeout_uS = 5 * 1000000;
60 
61 /*
62  * hci1394_isoch_init()
63  *    Initialize the isochronous dma soft state.
64  */
65 void
66 hci1394_isoch_init(hci1394_drvinfo_t *drvinfo,  hci1394_ohci_handle_t ohci,
67     hci1394_isoch_handle_t *isoch_hdl)
68 {
69 	hci1394_isoch_t *isochp;
70 	int i;
71 
72 	ASSERT(drvinfo != NULL);
73 	ASSERT(isoch_hdl != NULL);
74 	TNF_PROBE_0_DEBUG(hci1394_isoch_init_enter, HCI1394_TNF_HAL_STACK_ISOCH,
75 	    "");
76 
77 	isochp = kmem_alloc(sizeof (hci1394_isoch_t), KM_SLEEP);
78 
79 	/* initialize contexts */
80 	for (i = 0; i < HCI1394_MAX_ISOCH_CONTEXTS; i++) {
81 		isochp->ctxt_xmit[i].ctxt_index = i;
82 
83 		/* init context flags to 0 */
84 		isochp->ctxt_xmit[i].ctxt_flags = 0;
85 
86 		mutex_init(&isochp->ctxt_xmit[i].intrprocmutex, NULL,
87 		    MUTEX_DRIVER, drvinfo->di_iblock_cookie);
88 		cv_init(&isochp->ctxt_xmit[i].intr_cv, NULL,
89 		    CV_DRIVER, NULL);
90 
91 		isochp->ctxt_recv[i].ctxt_index = i;
92 		isochp->ctxt_recv[i].ctxt_flags = HCI1394_ISO_CTXT_RECV;
93 		mutex_init(&isochp->ctxt_recv[i].intrprocmutex, NULL,
94 		    MUTEX_DRIVER, drvinfo->di_iblock_cookie);
95 		cv_init(&isochp->ctxt_recv[i].intr_cv, NULL,
96 		    CV_DRIVER, NULL);
97 	}
98 
99 	/* initialize the count for allocated isoch dma */
100 	isochp->isoch_dma_alloc_cnt = 0;
101 
102 	/* initialize the cycle_lost_thresh struct */
103 	isochp->cycle_lost_thresh.last_intr_time  = 0;
104 	isochp->cycle_lost_thresh.delta_t_counter = 0;
105 	isochp->cycle_lost_thresh.delta_t_thresh  = HCI1394_CYC_LOST_DELTA;
106 	isochp->cycle_lost_thresh.counter_thresh  = HCI1394_CYC_LOST_COUNT;
107 
108 	/* initialize the cycle_incon_thresh struct */
109 	isochp->cycle_incon_thresh.last_intr_time  = 0;
110 	isochp->cycle_incon_thresh.delta_t_counter = 0;
111 	isochp->cycle_incon_thresh.delta_t_thresh  = HCI1394_CYC_INCON_DELTA;
112 	isochp->cycle_incon_thresh.counter_thresh  = HCI1394_CYC_INCON_COUNT;
113 
114 	/* determine number of contexts supported */
115 	isochp->ctxt_xmit_count = hci1394_ohci_it_ctxt_count_get(ohci);
116 	isochp->ctxt_recv_count = hci1394_ohci_ir_ctxt_count_get(ohci);
117 
118 	/* the isochronous context mutex is used during some error interrupts */
119 	mutex_init(&isochp->ctxt_list_mutex, NULL, MUTEX_DRIVER,
120 	    drvinfo->di_iblock_cookie);
121 
122 	*isoch_hdl = isochp;
123 
124 	TNF_PROBE_0_DEBUG(hci1394_isoch_init_exit, HCI1394_TNF_HAL_STACK_ISOCH,
125 	    "");
126 }
127 
128 /*
129  * hci1394_isoch_fini()
130  *    Cleanup after hci1394_isoch_init.  This should be called during detach.
131  */
132 void
133 hci1394_isoch_fini(hci1394_isoch_handle_t *isoch_hdl)
134 {
135 	hci1394_isoch_t *isochp;
136 	int i;
137 
138 	ASSERT(isoch_hdl != NULL);
139 	TNF_PROBE_0_DEBUG(hci1394_isoch_fini_enter, HCI1394_TNF_HAL_STACK_ISOCH,
140 	    "");
141 
142 	isochp = *isoch_hdl;
143 
144 	for (i = 0; i < HCI1394_MAX_ISOCH_CONTEXTS; i++) {
145 		mutex_destroy(&isochp->ctxt_xmit[i].intrprocmutex);
146 		mutex_destroy(&isochp->ctxt_recv[i].intrprocmutex);
147 		cv_destroy(&isochp->ctxt_xmit[i].intr_cv);
148 		cv_destroy(&isochp->ctxt_recv[i].intr_cv);
149 	}
150 
151 	mutex_destroy(&isochp->ctxt_list_mutex);
152 	kmem_free(isochp, sizeof (hci1394_isoch_t));
153 	*isoch_hdl = NULL;
154 
155 	TNF_PROBE_0_DEBUG(hci1394_isoch_fini_exit, HCI1394_TNF_HAL_STACK_ISOCH,
156 	    "");
157 }
158 
159 
160 /*
161  * hci1394_isoch_resume()
162  *    There is currently nothing to do for resume.  This is a placeholder.
163  */
164 /* ARGSUSED */
165 int
166 hci1394_isoch_resume(hci1394_state_t *soft_state)
167 {
168 	TNF_PROBE_0_DEBUG(hci1394_isoch_resume_enter,
169 	    HCI1394_TNF_HAL_STACK_ISOCH, "");
170 	TNF_PROBE_0_DEBUG(hci1394_isoch_resume_exit,
171 	    HCI1394_TNF_HAL_STACK_ISOCH, "");
172 	return (DDI_SUCCESS);
173 }
174 
175 /*
176  * hci1394_alloc_isoch_dma ()
177  *    Called by the Services Layer. Used to allocate a local Isoch DMA context.
178  *    Goes through appropriate context list (either transmit or receive)
179  *    looking for an unused context.  Fails if none found.
180  *    Then compiles the provided IXL program.
181  */
182 int
183 hci1394_alloc_isoch_dma(void *hal_private, id1394_isoch_dmainfo_t *idi,
184     void **hal_idma_handlep, int *resultp)
185 {
186 	int		    i;
187 	int		    err;
188 	hci1394_state_t	    *soft_statep = (hci1394_state_t *)hal_private;
189 	hci1394_isoch_t	    *isochp;
190 	hci1394_iso_ctxt_t  *ctxtp;
191 
192 
193 	ASSERT(soft_statep != NULL);
194 	ASSERT(hal_idma_handlep != NULL);
195 	TNF_PROBE_0_DEBUG(hci1394_alloc_isoch_dma_enter,
196 	    HCI1394_TNF_HAL_STACK_ISOCH, "");
197 
198 	isochp = soft_statep->isoch;
199 	*hal_idma_handlep = NULL;
200 
201 	/*
202 	 * find context to use based on whether talking(send) or listening(recv)
203 	 */
204 	mutex_enter(&isochp->ctxt_list_mutex);
205 	if ((idi->idma_options & ID1394_TALK) != 0) {
206 		/* TRANSMIT */
207 
208 		TNF_PROBE_1_DEBUG(hci1394_alloc_isoch_dma_transmit,
209 		    HCI1394_TNF_HAL_INFO_ISOCH, "", tnf_string, msg,
210 		    "Allocating isoch transmit context");
211 
212 		/*
213 		 * search through list of hardware supported contexts for
214 		 * one that's not inuse
215 		 */
216 		for (i = 0; i < isochp->ctxt_xmit_count; i++) {
217 			if ((isochp->ctxt_xmit[i].ctxt_flags &
218 			    HCI1394_ISO_CTXT_INUSE) == 0) {
219 				break;
220 			}
221 		}
222 
223 		/* if there aren't any left, return an error */
224 		if (i >= isochp->ctxt_xmit_count) {
225 			TNF_PROBE_1(hci1394_alloc_isoch_dma_xmit_rsrc_error,
226 			    HCI1394_TNF_HAL_ERROR_ISOCH, "", tnf_string, errmsg,
227 			    "Out of isoch transmit resources");
228 			TNF_PROBE_0_DEBUG(hci1394_alloc_isoch_dma_exit,
229 			    HCI1394_TNF_HAL_STACK_ISOCH, "");
230 
231 			mutex_exit(&isochp->ctxt_list_mutex);
232 			*resultp = IXL1394_ENO_DMA_RESRCS;
233 			return (DDI_FAILURE);
234 		}
235 
236 		TNF_PROBE_1_DEBUG(t1394_alloc_isoch_dma_it_ctxtnum,
237 		    HCI1394_TNF_HAL_INFO_ISOCH, "", tnf_int, it_ctxt_num, i);
238 
239 		/* mark inuse and set up handle to context */
240 		isochp->ctxt_xmit[i].ctxt_flags |= HCI1394_ISO_CTXT_INUSE;
241 		ctxtp = &isochp->ctxt_xmit[i];
242 		isochp->ctxt_xmit[i].ctxt_regsp =
243 		    &soft_statep->ohci->ohci_regs->it[i];
244 	} else {
245 		/* RECEIVE */
246 
247 		TNF_PROBE_1_DEBUG(hci1394_alloc_isoch_dma_receive,
248 		    HCI1394_TNF_HAL_INFO_ISOCH, "", tnf_string, msg,
249 		    "Allocating isoch receive context");
250 
251 		/* search thru implemented contexts for one that's available */
252 		for (i = 0; i < isochp->ctxt_recv_count; i++) {
253 			if ((isochp->ctxt_recv[i].ctxt_flags &
254 			    HCI1394_ISO_CTXT_INUSE) == 0) {
255 				break;
256 			}
257 		}
258 
259 		/* if there aren't any left, return an error */
260 		/* XXX support for multi-chan could go here */
261 		if (i >= isochp->ctxt_recv_count) {
262 
263 			TNF_PROBE_1(t1394_alloc_isoch_dma_ir_rsrc_error,
264 			    HCI1394_TNF_HAL_ERROR_ISOCH, "", tnf_string, errmsg,
265 			    "Out of isoch receive resources");
266 			TNF_PROBE_0_DEBUG(hci1394_alloc_isoch_dma_exit,
267 			    HCI1394_TNF_HAL_STACK_ISOCH, "");
268 
269 			mutex_exit(&isochp->ctxt_list_mutex);
270 			*resultp = IXL1394_ENO_DMA_RESRCS;
271 			return (DDI_FAILURE);
272 		}
273 
274 		/* set up receive mode flags */
275 		if ((idi->idma_options & ID1394_LISTEN_BUF_MODE) != 0) {
276 			isochp->ctxt_recv[i].ctxt_flags |=
277 			    HCI1394_ISO_CTXT_BFFILL;
278 		}
279 		if ((idi->idma_options & ID1394_RECV_HEADERS) != 0) {
280 			isochp->ctxt_recv[i].ctxt_flags |=
281 			    HCI1394_ISO_CTXT_RHDRS;
282 		}
283 
284 		TNF_PROBE_1_DEBUG(hci1394_alloc_isoch_dma_recv_ctxtnum,
285 		    HCI1394_TNF_HAL_INFO_ISOCH, "", tnf_int, recv_ctxt_num, i);
286 
287 		/* mark inuse and set up handle to context */
288 		isochp->ctxt_recv[i].ctxt_flags |= HCI1394_ISO_CTXT_INUSE;
289 		ctxtp = &isochp->ctxt_recv[i];
290 
291 		isochp->ctxt_recv[i].ctxt_regsp = (hci1394_ctxt_regs_t *)
292 		    &soft_statep->ohci->ohci_regs->ir[i];
293 	}
294 	mutex_exit(&isochp->ctxt_list_mutex);
295 
296 	/* before compiling, set up some default context values */
297 	ctxtp->isochan = idi->channel_num;
298 	ctxtp->default_tag = idi->default_tag;
299 	ctxtp->default_sync = idi->default_sync;
300 	ctxtp->global_callback_arg = idi->global_callback_arg;
301 	ctxtp->isoch_dma_stopped = idi->isoch_dma_stopped;
302 	ctxtp->idma_evt_arg = idi->idma_evt_arg;
303 	ctxtp->isospd = idi->it_speed;
304 	ctxtp->default_skipmode = idi->it_default_skip;
305 	ctxtp->default_skiplabelp = idi->it_default_skiplabel;
306 
307 	err = hci1394_compile_ixl(soft_statep, ctxtp, idi->ixlp, resultp);
308 
309 
310 	/*
311 	 * if the compile failed, clear the appropriate flags.
312 	 * Note that the context mutex is needed to eliminate race condition
313 	 * with cycle_inconsistent and other error intrs.
314 	 */
315 	if (err != DDI_SUCCESS) {
316 
317 		mutex_enter(&isochp->ctxt_list_mutex);
318 		if ((ctxtp->ctxt_flags & HCI1394_ISO_CTXT_RECV) != 0) {
319 			/* undo the set up of receive mode flags */
320 			isochp->ctxt_recv[i].ctxt_flags &=
321 			    ~HCI1394_ISO_CTXT_BFFILL;
322 			isochp->ctxt_recv[i].ctxt_flags &=
323 			    ~HCI1394_ISO_CTXT_RHDRS;
324 		}
325 		ctxtp->ctxt_flags &= ~HCI1394_ISO_CTXT_INUSE;
326 		mutex_exit(&isochp->ctxt_list_mutex);
327 
328 		TNF_PROBE_2(t1394_alloc_isoch_dma_compile_fail,
329 		    HCI1394_TNF_HAL_ERROR_ISOCH, "", tnf_string, errmsg,
330 		    "IXL compilation error", tnf_int, ixl_error, *resultp);
331 		TNF_PROBE_0_DEBUG(hci1394_alloc_isoch_dma_exit,
332 		    HCI1394_TNF_HAL_STACK_ISOCH, "");
333 		return (DDI_FAILURE);
334 	}
335 
336 	/*
337 	 * Update count of allocated isoch dma (and enable interrupts
338 	 * if necessary)
339 	 */
340 	mutex_enter(&isochp->ctxt_list_mutex);
341 	if (isochp->isoch_dma_alloc_cnt == 0) {
342 		hci1394_ohci_intr_clear(soft_statep->ohci,
343 		    OHCI_INTR_CYC_LOST | OHCI_INTR_CYC_INCONSISTENT);
344 		hci1394_ohci_intr_enable(soft_statep->ohci,
345 		    OHCI_INTR_CYC_LOST | OHCI_INTR_CYC_INCONSISTENT);
346 	}
347 	isochp->isoch_dma_alloc_cnt++;
348 	mutex_exit(&isochp->ctxt_list_mutex);
349 
350 	/* No errors, so all set to go.  initialize interrupt/execution flags */
351 	ctxtp->intr_flags = 0;
352 
353 	TNF_PROBE_0_DEBUG(hci1394_alloc_isoch_dma_exit,
354 	    HCI1394_TNF_HAL_STACK_ISOCH, "");
355 
356 	*hal_idma_handlep = ctxtp;
357 	return (DDI_SUCCESS);
358 }
359 
360 
361 /*
362  * hci1394_start_isoch_dma()
363  *    Used to start an allocated isochronous dma resource.
364  *    Sets the context's command ptr to start at the first IXL,
365  *    sets up IR match register (if IR), and enables the context_control
366  *    register RUN bit.
367  */
368 /* ARGSUSED */
369 int
370 hci1394_start_isoch_dma(void *hal_private, void *hal_isoch_dma_handle,
371     id1394_isoch_dma_ctrlinfo_t *idma_ctrlinfop, uint_t flags, int *result)
372 {
373 	hci1394_state_t	    *soft_statep = (hci1394_state_t *)hal_private;
374 	hci1394_iso_ctxt_t  *ctxtp;
375 	int		    tag0, tag1, tag2, tag3;
376 
377 	TNF_PROBE_0_DEBUG(hci1394_start_isoch_dma_enter,
378 	    HCI1394_TNF_HAL_STACK_ISOCH, "");
379 
380 	/* pick up the context pointer from the private idma data */
381 	ctxtp = (hci1394_iso_ctxt_t *)hal_isoch_dma_handle;
382 
383 	ASSERT(hal_private != NULL);
384 	ASSERT(ctxtp != NULL);
385 	ASSERT(idma_ctrlinfop != NULL);
386 	TNF_PROBE_4_DEBUG(hci1394_start_isoch_dma_ctxt_info,
387 	    HCI1394_TNF_HAL_INFO_ISOCH, "", tnf_opaque, ctxt_ptr, ctxtp,
388 	    tnf_int, ctxt_index, ctxtp->ctxt_index, tnf_opaque, ctxt_flags,
389 	    ctxtp->ctxt_flags, tnf_opaque, first_ixl, ctxtp->ixl_firstp);
390 
391 
392 	/* if the context is already running, just exit.  else set running */
393 	mutex_enter(&soft_statep->isoch->ctxt_list_mutex);
394 	if ((ctxtp->ctxt_flags & HCI1394_ISO_CTXT_RUNNING) != 0) {
395 
396 		mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
397 
398 		TNF_PROBE_1_DEBUG(hci1394_start_isoch_dma_exit,
399 		    HCI1394_TNF_HAL_STACK_ISOCH, "", tnf_string, msg,
400 		    "context already running");
401 		return (DDI_SUCCESS);
402 	}
403 	ctxtp->ctxt_flags |= HCI1394_ISO_CTXT_RUNNING;
404 	mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
405 
406 	ctxtp->intr_flags &= ~HCI1394_ISO_CTXT_STOP;
407 
408 	/* initialize context values */
409 	ctxtp->ixl_execp = ctxtp->ixl_firstp;	/* start of ixl chain */
410 	ctxtp->ixl_exec_depth = 0;
411 	ctxtp->dma_last_time = 0;
412 	ctxtp->rem_noadv_intrs = ctxtp->max_noadv_intrs;
413 
414 	/*
415 	 * clear out hci DMA descriptor status to start with clean slate.
416 	 * note that statuses could be set if context was previously started
417 	 * then stopped.
418 	 */
419 	hci1394_ixl_reset_status(ctxtp);
420 
421 	/* set up registers, and start isoch */
422 	if (ctxtp->ctxt_flags & HCI1394_ISO_CTXT_RECV) {
423 
424 		/* set context's command ptr to the first descriptor */
425 		hci1394_ohci_ir_cmd_ptr_set(soft_statep->ohci,
426 		    ctxtp->ctxt_index, ctxtp->dma_mem_execp);
427 
428 		TNF_PROBE_2_DEBUG(hci1394_start_isoch_dma_index_info,
429 		    HCI1394_TNF_HAL_INFO_ISOCH, "", tnf_string, msg,
430 		    "starting IR ctxt", tnf_int, ctxt_num, ctxtp->ctxt_index);
431 
432 		/*
433 		 * determine correct tag values.  map target's requested 2-bit
434 		 * tag into one of the 4 openHCI tag bits.
435 		 * XXX for now the t1394 api only supports a single tag setting,
436 		 * whereas openhci supports a set of (non-mutually exclusive)
437 		 * valid tags. if the api changes to support multiple
438 		 * simultaneous tags, then this code must be changed.
439 		 */
440 		tag0 = 0;
441 		tag1 = 1;
442 		tag2 = 2;
443 		tag3 = 3;
444 		if (ctxtp->default_tag == 0x0)
445 			tag0 = 1;
446 		else if (ctxtp->default_tag == 0x1)
447 			tag1 = 1;
448 		else if (ctxtp->default_tag == 0x2)
449 			tag2 = 1;
450 		else if (ctxtp->default_tag == 0x3)
451 			tag3 = 1;
452 
453 		/* set match register as desired */
454 		HCI1394_IRCTXT_MATCH_WRITE(soft_statep, ctxtp->ctxt_index, tag3,
455 		    tag2, tag1, tag0,
456 		    idma_ctrlinfop->start_cycle /* cycleMatch */,
457 		    ctxtp->default_sync /* sync */, 0 /* tag1sync */,
458 		    ctxtp->isochan /* chan */);
459 
460 		/* clear all bits in context ctrl reg to init to known state */
461 		HCI1394_IRCTXT_CTRL_CLR(soft_statep, ctxtp->ctxt_index,
462 		    (uint32_t)1, 1, 1, 1, 1);
463 
464 		/* set desired values in context control register */
465 		HCI1394_IRCTXT_CTRL_SET(soft_statep, ctxtp->ctxt_index,
466 		    (ctxtp->ctxt_flags & HCI1394_ISO_CTXT_BFFILL) != 0 /* bf */,
467 		    (ctxtp->ctxt_flags & HCI1394_ISO_CTXT_RHDRS) != 0 /* hdr */,
468 		    (flags & ID1394_START_ON_CYCLE) != 0 /* match enbl */,
469 		    0 /* multi-chan mode */, 1 /* run */, 0 /* wake */);
470 
471 		/*
472 		 * before enabling interrupts, make sure any vestige interrupt
473 		 * event (from a previous use) is cleared.
474 		 */
475 		hci1394_ohci_ir_intr_clear(soft_statep->ohci,
476 		    (uint32_t)(0x1 << ctxtp->ctxt_index));
477 
478 		/* enable interrupts for this IR context */
479 		hci1394_ohci_ir_intr_enable(soft_statep->ohci,
480 		    (uint32_t)(0x1 << ctxtp->ctxt_index));
481 
482 	} else {
483 		/* TRANSMIT */
484 
485 		/* set context's command ptr to the first descriptor */
486 		hci1394_ohci_it_cmd_ptr_set(soft_statep->ohci,
487 		    ctxtp->ctxt_index, ctxtp->dma_mem_execp);
488 
489 		TNF_PROBE_2_DEBUG(hci1394_start_isoch_dma_index_info,
490 		    HCI1394_TNF_HAL_INFO_ISOCH, "", tnf_string, msg,
491 		    "starting IT ctxt", tnf_int, ctxt_num, ctxtp->ctxt_index);
492 
493 		/* set desired values in context control register */
494 		HCI1394_ITCTXT_CTRL_SET(soft_statep, ctxtp->ctxt_index,
495 		    ((flags & ID1394_START_ON_CYCLE) != 0) /* match enable */,
496 		    idma_ctrlinfop->start_cycle /* cycle Match */,
497 		    1 /* run */, 0 /* wake */);
498 
499 		/*
500 		 * before enabling interrupts, make sure any vestige interrupt
501 		 * event (from a previous use) is cleared.
502 		 */
503 		hci1394_ohci_it_intr_clear(soft_statep->ohci,
504 		    (uint32_t)(0x1 << ctxtp->ctxt_index));
505 
506 		/* enable interrupts for this IT context */
507 		hci1394_ohci_it_intr_enable(soft_statep->ohci,
508 		    (uint32_t)(0x1 << ctxtp->ctxt_index));
509 	}
510 
511 	TNF_PROBE_0_DEBUG(hci1394_start_isoch_dma_exit,
512 	    HCI1394_TNF_HAL_STACK_ISOCH, "");
513 	return (DDI_SUCCESS);
514 }
515 
516 /*
517  * hci1394_update_isoch_dma()
518  *
519  * Returns DDI_SUCCESS, or DDI_FAILURE.  If DDI_FAILURE, then resultp
520  * contains the error code.
521  */
522 /* ARGSUSED */
523 int
524 hci1394_update_isoch_dma(void *hal_private, void *hal_isoch_dma_handle,
525     id1394_isoch_dma_updateinfo_t *idma_updateinfop, uint_t flags, int *resultp)
526 {
527 	hci1394_state_t	    *soft_statep = (hci1394_state_t *)hal_private;
528 	hci1394_iso_ctxt_t  *ctxtp;
529 	ixl1394_command_t   *cur_new_ixlp;
530 	ixl1394_command_t   *cur_orig_ixlp;
531 	int		    ii;
532 	int		    err = DDI_SUCCESS;
533 
534 
535 	TNF_PROBE_0_DEBUG(hci1394_update_isoch_dma_enter,
536 	    HCI1394_TNF_HAL_STACK_ISOCH, "");
537 
538 	/* pick up the context pointer from the private idma data */
539 	ctxtp = (hci1394_iso_ctxt_t *)hal_isoch_dma_handle;
540 
541 	ASSERT(hal_private != NULL);
542 	ASSERT(ctxtp != NULL);
543 	ASSERT(idma_updateinfop != NULL);
544 
545 	/*
546 	 * regardless of the type of context (IR or IT), loop through each
547 	 * command pair (one from new, one from orig), updating the relevant
548 	 * fields of orig with those from new.
549 	 */
550 	cur_new_ixlp = idma_updateinfop->temp_ixlp;
551 	cur_orig_ixlp = idma_updateinfop->orig_ixlp;
552 
553 	ASSERT(cur_new_ixlp != NULL);
554 	ASSERT(cur_orig_ixlp != NULL);
555 
556 	/* lots of debug trace info */
557 	TNF_PROBE_4_DEBUG(hci1394_update_isoch_dma_ctxt_info,
558 	    HCI1394_TNF_HAL_INFO_ISOCH, "", tnf_opaque, ctxt_ptr, ctxtp,
559 	    tnf_int, ixlcount, idma_updateinfop->ixl_count,
560 	    tnf_opaque, new_ixl, cur_new_ixlp, tnf_opaque, orig_ixl,
561 	    cur_orig_ixlp);
562 
563 	for (ii = 0; (ii < idma_updateinfop->ixl_count) && (err == DDI_SUCCESS);
564 	    ii++) {
565 
566 		/* error if hit a null ixl command too soon */
567 		if ((cur_new_ixlp == NULL) || (cur_orig_ixlp == NULL)) {
568 			*resultp = IXL1394_ECOUNT_MISMATCH;
569 			err = DDI_FAILURE;
570 
571 			TNF_PROBE_3_DEBUG(hci1394_update_isoch_dma_mismatch,
572 			    HCI1394_TNF_HAL_ERROR_ISOCH, "", tnf_opaque, new,
573 			    cur_new_ixlp, tnf_opaque, orig, cur_orig_ixlp,
574 			    tnf_int, iteration, ii);
575 			break;
576 		}
577 
578 		/* proceed with the update */
579 		err = hci1394_ixl_update(soft_statep, ctxtp, cur_new_ixlp,
580 		    cur_orig_ixlp, 0, resultp);
581 
582 		/* advance new and orig chains */
583 		cur_new_ixlp = cur_new_ixlp->next_ixlp;
584 		cur_orig_ixlp = cur_orig_ixlp->next_ixlp;
585 	}
586 
587 	TNF_PROBE_0_DEBUG(hci1394_update_isoch_dma_exit,
588 	    HCI1394_TNF_HAL_STACK_ISOCH, "");
589 	return (err);
590 }
591 
592 
593 /*
594  * hci1394_stop_isoch_dma()
595  *    Used to stop a "running" isochronous dma resource.
596  *    This is a wrapper which calls the hci1394_do_stop to do the actual work,
597  *    but NOT to invoke the target's isoch_dma_stopped().
598  */
599 /* ARGSUSED */
600 void
601 hci1394_stop_isoch_dma(void *hal_private, void *hal_isoch_dma_handle,
602     int	*result)
603 {
604 	hci1394_state_t	    *soft_statep = (hci1394_state_t *)hal_private;
605 	hci1394_iso_ctxt_t  *ctxtp;
606 
607 	TNF_PROBE_0_DEBUG(hci1394_stop_isoch_dma_enter,
608 	    HCI1394_TNF_HAL_STACK_ISOCH, "");
609 
610 	/* pick up the context pointer from the private idma data */
611 	ctxtp = (hci1394_iso_ctxt_t *)hal_isoch_dma_handle;
612 
613 	ASSERT(hal_private != NULL);
614 	ASSERT(ctxtp != NULL);
615 
616 	/* stop the context, do not invoke target's stop callback */
617 	hci1394_do_stop(soft_statep, ctxtp, B_FALSE, 0);
618 
619 	/*
620 	 * call interrupt processing functions to bring callbacks and
621 	 * store_timestamps upto date.  Don't care about errors.
622 	 */
623 	hci1394_ixl_interrupt(soft_statep, ctxtp, B_TRUE);
624 
625 	TNF_PROBE_0_DEBUG(hci1394_stop_isoch_dma_exit,
626 	    HCI1394_TNF_HAL_STACK_ISOCH, "");
627 }
628 
629 /*
630  * hci1394_do_stop()
631  *    Used to stop a "running" isochronous dma resource.
632  *    Disables interrupts for the context, clears the context_control register's
633  *    RUN bit, and makes sure the ixl is up-to-date with where the hardware is
634  *    in the DMA chain.
635  *    If do_callback is B_TRUE, the target's isoch_dma_stopped() callback is
636  *    invoked.  Caller must not hold mutex(es) if calling with
637  *    do_callback==B_TRUE, otherwise mutex(es) will be held during callback.
638  *    If do_callback is B_FALSE, the isoch_dma_stopped() callback is NOT
639  *    invoked and stop_args is ignored.
640  */
641 void
642 hci1394_do_stop(hci1394_state_t *soft_statep, hci1394_iso_ctxt_t *ctxtp,
643     boolean_t do_callback, id1394_isoch_dma_stopped_t stop_args)
644 {
645 	int	count;
646 	clock_t	upto;
647 
648 	TNF_PROBE_0_DEBUG(hci1394_do_stop_enter, HCI1394_TNF_HAL_STACK_ISOCH,
649 	    "");
650 
651 	TNF_PROBE_4_DEBUG(hci1394_do_stop_info,
652 	    HCI1394_TNF_HAL_INFO_ISOCH, "", tnf_opaque, ctxt_ptr, ctxtp,
653 	    tnf_int, ctxt_index, ctxtp->ctxt_index, tnf_opaque, ctxt_flags,
654 	    ctxtp->ctxt_flags, tnf_string, reason,
655 	    (stop_args == ID1394_DONE) ? "DONE":"FAIL");
656 
657 	/* already stopped? if yes, done, else set state to not-running */
658 	mutex_enter(&soft_statep->isoch->ctxt_list_mutex);
659 	if ((ctxtp->ctxt_flags & HCI1394_ISO_CTXT_RUNNING) == 0) {
660 		mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
661 
662 		TNF_PROBE_1_DEBUG(hci1394_do_stop_exit,
663 		    HCI1394_TNF_HAL_STACK_ISOCH, "", tnf_string, msg,
664 		    "context already stopped");
665 		return;
666 	}
667 	ctxtp->ctxt_flags &= ~HCI1394_ISO_CTXT_RUNNING;
668 	mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
669 
670 	/* turn off context control register's run bit */
671 	if (ctxtp->ctxt_flags & HCI1394_ISO_CTXT_RECV) {
672 		/* RECEIVE */
673 
674 		/* disable interrupts for this IR context */
675 		hci1394_ohci_ir_intr_disable(soft_statep->ohci,
676 		    (uint32_t)(0x1 << ctxtp->ctxt_index));
677 
678 		/* turn off run bit */
679 		HCI1394_IRCTXT_CTRL_CLR(soft_statep, ctxtp->ctxt_index,
680 		    0 /* bffill */, 0 /* iso hdrs */, 0 /* match enbl */,
681 		    0 /* multi-chan mode (not implemented) */, 1 /* run */);
682 	} else {
683 		/* TRANSMIT */
684 
685 		/* disable interrupts for this IT context */
686 		hci1394_ohci_it_intr_disable(soft_statep->ohci,
687 		    (uint32_t)(0x1 << ctxtp->ctxt_index));
688 
689 		/* turn of run bit */
690 		HCI1394_ITCTXT_CTRL_CLR(soft_statep, ctxtp->ctxt_index,
691 		    0 /* match enbl */, 0 /* match */, 1 /* run */);
692 	}
693 
694 	/*
695 	 * If interrupt is already in progress, wait until it's over.
696 	 * Otherwise, set flag to prevent the new interrupt.
697 	 */
698 	mutex_enter(&ctxtp->intrprocmutex);
699 	ctxtp->intr_flags |= HCI1394_ISO_CTXT_STOP;
700 	if (ctxtp->intr_flags & HCI1394_ISO_CTXT_ININTR) {
701 		upto = ddi_get_lbolt() +
702 		    drv_usectohz(hci1394_iso_ctxt_stop_intr_timeout_uS);
703 		while (ctxtp->intr_flags & HCI1394_ISO_CTXT_ININTR) {
704 			if (cv_timedwait(&ctxtp->intr_cv, &ctxtp->intrprocmutex,
705 			    upto) <= 0) {
706 				break;
707 			}
708 		}
709 
710 		if (ctxtp->intr_flags & HCI1394_ISO_CTXT_ININTR) {
711 			TNF_PROBE_1(hci1394_do_stop_error,
712 			    HCI1394_TNF_HAL_ERROR_ISOCH, "",
713 			    tnf_string, msg, "intr completion timeout");
714 		}
715 	}
716 	mutex_exit(&ctxtp->intrprocmutex);
717 
718 	/* Wait until "active" bit is cleared before continuing */
719 	count = 0;
720 	while (count < hci1394_iso_ctxt_stop_delay_uS) {
721 		/* Has the "active" bit gone low yet? */
722 		if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) == 0)
723 			break;
724 
725 		/*
726 		 * The context did not stop yet.  Wait 1us, increment the
727 		 * count and try again.
728 		 */
729 		drv_usecwait(1);
730 		count++;
731 	}
732 
733 	/* Check to see if we timed out or not */
734 	if (count >= hci1394_iso_ctxt_stop_delay_uS) {
735 		h1394_error_detected(soft_statep->drvinfo.di_sl_private,
736 		    H1394_SELF_INITIATED_SHUTDOWN, NULL);
737 		cmn_err(CE_WARN, "hci1394(%d): driver shutdown: "
738 		    "unable to stop isoch context",
739 		    soft_statep->drvinfo.di_instance);
740 		hci1394_shutdown(soft_statep->drvinfo.di_dip);
741 
742 		TNF_PROBE_1_DEBUG(hci1394_do_stop_exit,
743 		    HCI1394_TNF_HAL_STACK_ISOCH, "", tnf_string, msg,
744 		    "context timed out trying to stop");
745 		return;
746 	}
747 
748 	/*
749 	 * invoke callback as directed.  Note that the CTXT_INCALL flag is NOT
750 	 * needed here.  That flag is only used when we have to drop a mutex
751 	 * that we want to grab back again. We're not doing that here.
752 	 */
753 	if (do_callback == B_TRUE) {
754 		if (ctxtp->isoch_dma_stopped != NULL) {
755 			ctxtp->isoch_dma_stopped(
756 			    (struct isoch_dma_handle *)ctxtp,
757 			    ctxtp->idma_evt_arg, stop_args);
758 		}
759 	}
760 	TNF_PROBE_0_DEBUG(hci1394_do_stop_exit, HCI1394_TNF_HAL_STACK_ISOCH,
761 	    "");
762 }
763 
764 /*
765  * hci1394_free_isoch_dma()
766  *    Used to free up usage of an isochronous context and any other
767  *    system resources acquired during IXL compilation.
768  *    This does NOT free up the IXL and it's data buffers which is
769  *    the target driver's responsibility.
770  */
771 void
772 hci1394_free_isoch_dma(void *hal_private, void *hal_isoch_dma_handle)
773 {
774 	hci1394_state_t	    *soft_statep = (hci1394_state_t *)hal_private;
775 	hci1394_iso_ctxt_t  *ctxtp;
776 	hci1394_isoch_t	    *isochp;
777 
778 	TNF_PROBE_0_DEBUG(hci1394_free_isoch_dma_enter,
779 	    HCI1394_TNF_HAL_STACK_ISOCH, "");
780 
781 	/* pick up the context pointer from the private idma data */
782 	ctxtp = (hci1394_iso_ctxt_t *)hal_isoch_dma_handle;
783 
784 	ASSERT(soft_statep);
785 	ASSERT(ctxtp);
786 
787 	isochp = soft_statep->isoch;
788 
789 	/* lots of debug trace info */
790 	TNF_PROBE_4_DEBUG(hci1394_free_isoch_dma_ctxt_info,
791 	    HCI1394_TNF_HAL_INFO_ISOCH, "", tnf_opaque, ctxt_ptr, ctxtp,
792 	    tnf_int, ctxt_index, ctxtp->ctxt_index, tnf_opaque, ctxt_flags,
793 	    ctxtp->ctxt_flags, tnf_opaque, first_ixl, ctxtp->ixl_firstp);
794 
795 	mutex_enter(&soft_statep->isoch->ctxt_list_mutex);
796 
797 	/* delete xfer_ctl structs and pages of allocated hci_desc memory */
798 	hci1394_ixl_cleanup(soft_statep, ctxtp);
799 
800 	/*
801 	 * free context. no need to determine if xmit or recv. clearing of recv
802 	 * flags is harmless for xmit.
803 	 */
804 	ctxtp->ctxt_flags &= ~(HCI1394_ISO_CTXT_INUSE |
805 	    HCI1394_ISO_CTXT_BFFILL | HCI1394_ISO_CTXT_RHDRS);
806 
807 	/*
808 	 * Update count of allocated isoch dma (and disable interrupts
809 	 * if necessary)
810 	 */
811 	ASSERT(isochp->isoch_dma_alloc_cnt > 0);
812 	isochp->isoch_dma_alloc_cnt--;
813 	if (isochp->isoch_dma_alloc_cnt == 0) {
814 		hci1394_ohci_intr_disable(soft_statep->ohci,
815 		    OHCI_INTR_CYC_LOST | OHCI_INTR_CYC_INCONSISTENT);
816 	}
817 
818 	mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
819 
820 	TNF_PROBE_0_DEBUG(hci1394_free_isoch_dma_exit,
821 	    HCI1394_TNF_HAL_STACK_ISOCH, "");
822 }
823 
824 /*
825  * hci1394_isoch_recv_count_get()
826  *    returns the number of supported isoch receive contexts.
827  */
828 int
829 hci1394_isoch_recv_count_get(hci1394_isoch_handle_t isoch_hdl)
830 {
831 	ASSERT(isoch_hdl != NULL);
832 	return (isoch_hdl->ctxt_recv_count);
833 }
834 
835 /*
836  * hci1394_isoch_recv_ctxt_get()
837  *    given a context index, returns its isoch receive context struct
838  */
839 hci1394_iso_ctxt_t *
840 hci1394_isoch_recv_ctxt_get(hci1394_isoch_handle_t isoch_hdl, int num)
841 {
842 	ASSERT(isoch_hdl != NULL);
843 	return (&isoch_hdl->ctxt_recv[num]);
844 }
845 
846 /*
847  * hci1394_isoch_xmit_count_get()
848  *    returns the number of supported isoch transmit contexts.
849  */
850 int
851 hci1394_isoch_xmit_count_get(hci1394_isoch_handle_t isoch_hdl)
852 {
853 	ASSERT(isoch_hdl != NULL);
854 	return (isoch_hdl->ctxt_xmit_count);
855 }
856 
857 /*
858  * hci1394_isoch_xmit_ctxt_get()
859  *    given a context index, returns its isoch transmit context struct
860  */
861 hci1394_iso_ctxt_t *
862 hci1394_isoch_xmit_ctxt_get(hci1394_isoch_handle_t isoch_hdl, int num)
863 {
864 	ASSERT(isoch_hdl != NULL);
865 	return (&isoch_hdl->ctxt_xmit[num]);
866 }
867 
868 /*
869  * hci1394_isoch_error_ints_enable()
870  *    after bus reset, reenable CYCLE_LOST and CYCLE_INCONSISTENT
871  *    interrupts (if necessary).
872  */
873 void
874 hci1394_isoch_error_ints_enable(hci1394_state_t *soft_statep)
875 {
876 	ASSERT(soft_statep);
877 
878 	TNF_PROBE_0_DEBUG(hci1394_isoch_error_ints_enable_enter,
879 	    HCI1394_TNF_HAL_STACK_ISOCH, "");
880 
881 	mutex_enter(&soft_statep->isoch->ctxt_list_mutex);
882 
883 	if (soft_statep->isoch->isoch_dma_alloc_cnt != 0) {
884 		soft_statep->isoch->cycle_lost_thresh.delta_t_counter  = 0;
885 		soft_statep->isoch->cycle_incon_thresh.delta_t_counter = 0;
886 		hci1394_ohci_intr_clear(soft_statep->ohci,
887 		    OHCI_INTR_CYC_LOST | OHCI_INTR_CYC_INCONSISTENT);
888 		hci1394_ohci_intr_enable(soft_statep->ohci,
889 		    OHCI_INTR_CYC_LOST | OHCI_INTR_CYC_INCONSISTENT);
890 	}
891 	mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
892 
893 	TNF_PROBE_0_DEBUG(hci1394_isoch_error_ints_enable_exit,
894 	    HCI1394_TNF_HAL_STACK_ISOCH, "");
895 }
896