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
hci1394_isoch_init(hci1394_drvinfo_t * drvinfo,hci1394_ohci_handle_t ohci,hci1394_isoch_handle_t * isoch_hdl)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
hci1394_isoch_fini(hci1394_isoch_handle_t * isoch_hdl)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
hci1394_isoch_resume(hci1394_state_t * soft_state)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
hci1394_alloc_isoch_dma(void * hal_private,id1394_isoch_dmainfo_t * idi,void ** hal_idma_handlep,int * resultp)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
hci1394_start_isoch_dma(void * hal_private,void * hal_isoch_dma_handle,id1394_isoch_dma_ctrlinfo_t * idma_ctrlinfop,uint_t flags,int * result)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
hci1394_update_isoch_dma(void * hal_private,void * hal_isoch_dma_handle,id1394_isoch_dma_updateinfo_t * idma_updateinfop,uint_t flags,int * resultp)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
hci1394_stop_isoch_dma(void * hal_private,void * hal_isoch_dma_handle,int * result)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
hci1394_do_stop(hci1394_state_t * soft_statep,hci1394_iso_ctxt_t * ctxtp,boolean_t do_callback,id1394_isoch_dma_stopped_t stop_args)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
hci1394_free_isoch_dma(void * hal_private,void * hal_isoch_dma_handle)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
hci1394_isoch_recv_count_get(hci1394_isoch_handle_t isoch_hdl)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 *
hci1394_isoch_recv_ctxt_get(hci1394_isoch_handle_t isoch_hdl,int num)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
hci1394_isoch_xmit_count_get(hci1394_isoch_handle_t isoch_hdl)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 *
hci1394_isoch_xmit_ctxt_get(hci1394_isoch_handle_t isoch_hdl,int num)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
hci1394_isoch_error_ints_enable(hci1394_state_t * soft_statep)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