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