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 1999-2002 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * hci1394_ixl_isr.c
29  *    Isochronous IXL Interrupt Service Routines.
30  *    The interrupt handler determines which OpenHCI DMA descriptors
31  *    have been executed by the hardware, tracks the path in the
32  *    corresponding IXL program, issues callbacks as needed, and resets
33  *    the OpenHCI DMA descriptors.
34  */
35 
36 #include <sys/types.h>
37 #include <sys/conf.h>
38 #include <sys/1394/h1394.h>
39 #include <sys/1394/ixl1394.h>
40 #include <sys/1394/adapters/hci1394.h>
41 
42 
43 /* Return values for local hci1394_ixl_intr_check_done() */
44 #define	IXL_CHECK_LOST	(-1)	/* ixl cmd intr processing lost */
45 #define	IXL_CHECK_DONE	0	/* ixl cmd intr processing done */
46 #define	IXL_CHECK_SKIP	1	/* ixl cmd intr processing context skipped */
47 #define	IXL_CHECK_STOP	2	/* ixl cmd intr processing context stopped */
48 
49 static boolean_t hci1394_ixl_intr_check_xfer(hci1394_state_t *soft_statep,
50     hci1394_iso_ctxt_t *ctxtp, ixl1394_command_t *ixlp,
51     ixl1394_command_t **ixlnextpp, uint16_t *timestampp, int *donecodep);
52 static int hci1394_ixl_intr_check_done(hci1394_state_t *soft_statep,
53     hci1394_iso_ctxt_t *ctxtp);
54 
55 /*
56  * hci1394_ixl_interrupt
57  *    main entry point (front-end) into interrupt processing.
58  *    acquires mutex, checks if update in progress, sets flags accordingly,
59  *    and calls to do real interrupt processing.
60  */
61 void
hci1394_ixl_interrupt(hci1394_state_t * soft_statep,hci1394_iso_ctxt_t * ctxtp,boolean_t in_stop)62 hci1394_ixl_interrupt(hci1394_state_t *soft_statep,
63     hci1394_iso_ctxt_t *ctxtp, boolean_t in_stop)
64 {
65 	uint_t	status;
66 	int	retcode;
67 
68 	status = 1;
69 
70 	/* acquire the interrupt processing context mutex */
71 	mutex_enter(&ctxtp->intrprocmutex);
72 
73 	/* set flag to indicate that interrupt processing is required */
74 	ctxtp->intr_flags |= HCI1394_ISO_CTXT_INTRSET;
75 
76 	/* if update proc already in progress, let it handle intr processing */
77 	if (ctxtp->intr_flags & HCI1394_ISO_CTXT_INUPDATE) {
78 		retcode = HCI1394_IXL_INTR_INUPDATE;
79 		status = 0;
80 	} else if (ctxtp->intr_flags & HCI1394_ISO_CTXT_ININTR) {
81 		/* else fatal error if inter processing already in progress */
82 		retcode = HCI1394_IXL_INTR_ININTR;
83 		status = 0;
84 	} else if (ctxtp->intr_flags & HCI1394_ISO_CTXT_INCALL) {
85 		/* else fatal error if callback in progress flag is set */
86 		retcode = HCI1394_IXL_INTR_INCALL;
87 		status = 0;
88 	} else if (!in_stop && (ctxtp->intr_flags & HCI1394_ISO_CTXT_STOP)) {
89 		/* context is being stopped */
90 		retcode = HCI1394_IXL_INTR_STOP;
91 		status = 0;
92 	}
93 
94 	/*
95 	 * if context is available, reserve it, do interrupt processing
96 	 * and free it
97 	 */
98 	if (status) {
99 		ctxtp->intr_flags |= HCI1394_ISO_CTXT_ININTR;
100 		ctxtp->intr_flags &= ~HCI1394_ISO_CTXT_INTRSET;
101 		mutex_exit(&ctxtp->intrprocmutex);
102 
103 		retcode = hci1394_ixl_dma_sync(soft_statep, ctxtp);
104 
105 		mutex_enter(&ctxtp->intrprocmutex);
106 		ctxtp->intr_flags &= ~HCI1394_ISO_CTXT_ININTR;
107 
108 		/* notify stop thread that the interrupt is finished */
109 		if ((ctxtp->intr_flags & HCI1394_ISO_CTXT_STOP) && !in_stop) {
110 			cv_signal(&ctxtp->intr_cv);
111 		}
112 	};
113 
114 	/* free the intr processing context mutex before error checks */
115 	mutex_exit(&ctxtp->intrprocmutex);
116 
117 	/* if context stopped, invoke callback */
118 	if (retcode == HCI1394_IXL_INTR_DMASTOP) {
119 		hci1394_do_stop(soft_statep, ctxtp, B_TRUE, ID1394_DONE);
120 	}
121 	/* if error, stop and invoke callback */
122 	if (retcode == HCI1394_IXL_INTR_DMALOST) {
123 		hci1394_do_stop(soft_statep, ctxtp, B_TRUE, ID1394_FAIL);
124 	}
125 }
126 
127 /*
128  * hci1394_ixl_dma_sync()
129  *    the heart of interrupt processing, this routine correlates where the
130  *    hardware is for the specified context with the IXL program.  Invokes
131  *    callbacks as needed.  Also called by "update" to make sure ixl is
132  *    sync'ed up with where the hardware is.
133  *    Returns one of the ixl_intr defined return codes - HCI1394_IXL_INTR...
134  *    {..._DMALOST, ..._DMASTOP, ..._NOADV,... _NOERROR}
135  */
136 int
hci1394_ixl_dma_sync(hci1394_state_t * soft_statep,hci1394_iso_ctxt_t * ctxtp)137 hci1394_ixl_dma_sync(hci1394_state_t *soft_statep, hci1394_iso_ctxt_t *ctxtp)
138 {
139 	ixl1394_command_t *ixlp = NULL;	/* current ixl command */
140 	ixl1394_command_t *ixlnextp;	/* next ixl command */
141 	uint16_t	ixlopcode;
142 	uint16_t	timestamp;
143 	int		donecode;
144 	boolean_t	isdone;
145 
146 	void (*callback)(opaque_t, struct ixl1394_callback *);
147 
148 	ASSERT(MUTEX_NOT_HELD(&ctxtp->intrprocmutex));
149 
150 	/* xfer start ixl cmd where last left off */
151 	ixlnextp = ctxtp->ixl_execp;
152 
153 	/* last completed descriptor block's timestamp  */
154 	timestamp = ctxtp->dma_last_time;
155 
156 	/*
157 	 * follow execution path in IXL, until find dma descriptor in IXL
158 	 * xfer command whose status isn't set or until run out of IXL cmds
159 	 */
160 	while (ixlnextp != NULL) {
161 		ixlp = ixlnextp;
162 		ixlnextp = ixlp->next_ixlp;
163 		ixlopcode = ixlp->ixl_opcode & ~IXL1394_OPF_UPDATE;
164 
165 		/*
166 		 * process IXL commands: xfer start, callback, store timestamp
167 		 * and jump and ignore the others
168 		 */
169 
170 		/* determine if this is an xfer start IXL command */
171 		if (((ixlopcode & IXL1394_OPF_ISXFER) != 0) &&
172 		    ((ixlopcode & IXL1394_OPTY_MASK) != 0)) {
173 
174 			/* process xfer cmd to see if HW has been here */
175 			isdone = hci1394_ixl_intr_check_xfer(soft_statep, ctxtp,
176 			    ixlp, &ixlnextp, &timestamp, &donecode);
177 
178 			if (isdone == B_TRUE) {
179 				return (donecode);
180 			}
181 
182 			/* continue to process next IXL command */
183 			continue;
184 		}
185 
186 		/* else check if IXL cmd - jump, callback or store timestamp */
187 		switch (ixlopcode) {
188 		case IXL1394_OP_JUMP:
189 			/*
190 			 * set next IXL cmd to label ptr in current IXL jump cmd
191 			 */
192 			ixlnextp = ((ixl1394_jump_t *)ixlp)->label;
193 			break;
194 
195 		case IXL1394_OP_STORE_TIMESTAMP:
196 			/*
197 			 * set last timestamp value recorded into current IXL
198 			 * cmd
199 			 */
200 			((ixl1394_store_timestamp_t *)ixlp)->timestamp =
201 			    timestamp;
202 			break;
203 
204 		case IXL1394_OP_CALLBACK:
205 			/*
206 			 * if callback function is specified, call it with IXL
207 			 * cmd addr.  Make sure to grab the lock before setting
208 			 * the "in callback" flag in intr_flags.
209 			 */
210 			mutex_enter(&ctxtp->intrprocmutex);
211 			ctxtp->intr_flags |= HCI1394_ISO_CTXT_INCALL;
212 			mutex_exit(&ctxtp->intrprocmutex);
213 
214 			callback = ((ixl1394_callback_t *)ixlp)->callback;
215 			if (callback != NULL) {
216 				callback(ctxtp->global_callback_arg,
217 				    (ixl1394_callback_t *)ixlp);
218 			}
219 
220 			/*
221 			 * And grab the lock again before clearing
222 			 * the "in callback" flag.
223 			 */
224 			mutex_enter(&ctxtp->intrprocmutex);
225 			ctxtp->intr_flags &= ~HCI1394_ISO_CTXT_INCALL;
226 			mutex_exit(&ctxtp->intrprocmutex);
227 			break;
228 		}
229 	}
230 
231 	/*
232 	 * If we jumped to NULL because of an updateable JUMP, set ixl_execp
233 	 * back to ixlp.  The destination label might get updated to a
234 	 * non-NULL value.
235 	 */
236 	if ((ixlp != NULL) && (ixlp->ixl_opcode == IXL1394_OP_JUMP_U)) {
237 		ctxtp->ixl_execp = ixlp;
238 		return (HCI1394_IXL_INTR_NOERROR);
239 	}
240 
241 	/* save null IXL cmd and depth and last timestamp */
242 	ctxtp->ixl_execp = NULL;
243 	ctxtp->ixl_exec_depth = 0;
244 	ctxtp->dma_last_time = timestamp;
245 
246 	ctxtp->rem_noadv_intrs = 0;
247 
248 
249 	/* return stopped status if at end of IXL cmds & context stopped */
250 	if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) == 0) {
251 		return (HCI1394_IXL_INTR_DMASTOP);
252 	}
253 
254 	/* else interrupt processing is lost */
255 	return (HCI1394_IXL_INTR_DMALOST);
256 }
257 
258 /*
259  * hci1394_ixl_intr_check_xfer()
260  *    Process given IXL xfer cmd, checking status of each dma descriptor block
261  *    for the command until find one whose status isn't set or until full depth
262  *    reached at current IXL command or until find hardware skip has occurred.
263  *
264  *    Returns B_TRUE if processing should terminate (either have stopped
265  *    or encountered an error), and B_FALSE if it should continue looking.
266  *    If B_TRUE, donecodep contains the reason: HCI1394_IXL_INTR_DMALOST,
267  *    HCI1394_IXL_INTR_DMASTOP, HCI1394_IXL_INTR_NOADV, or
268  *    HCI1394_IXL_INTR_NOERROR.  NOERROR means that the current location
269  *    has been determined and do not need to look further.
270  */
271 static boolean_t
hci1394_ixl_intr_check_xfer(hci1394_state_t * soft_statep,hci1394_iso_ctxt_t * ctxtp,ixl1394_command_t * ixlp,ixl1394_command_t ** ixlnextpp,uint16_t * timestampp,int * donecodep)272 hci1394_ixl_intr_check_xfer(hci1394_state_t *soft_statep,
273     hci1394_iso_ctxt_t *ctxtp, ixl1394_command_t *ixlp,
274     ixl1394_command_t **ixlnextpp, uint16_t *timestampp, int *donecodep)
275 {
276 	uint_t		    dma_advances;
277 	int		    intrstatus;
278 	uint_t		    skipped;
279 	hci1394_xfer_ctl_t  *xferctlp;
280 	uint16_t	    ixldepth;
281 	uint16_t	    ixlopcode;
282 
283 	*donecodep = 0;
284 	dma_advances = 0;
285 	ixldepth = ctxtp->ixl_exec_depth;
286 	ixlopcode = ixlp->ixl_opcode & ~IXL1394_OPF_UPDATE;
287 
288 	/* get control struct for this xfer start IXL command */
289 	xferctlp = (hci1394_xfer_ctl_t *)ixlp->compiler_privatep;
290 
291 	skipped = 0;
292 	while ((skipped == 0) && (ixldepth < xferctlp->cnt)) {
293 		/*
294 		 * check if status is set in dma descriptor
295 		 * block at cur depth in cur xfer start IXL cmd
296 		 */
297 		if (hci1394_ixl_check_status(&xferctlp->dma[ixldepth],
298 		    ixlopcode, timestampp, B_TRUE) != 0) {
299 
300 			/* advance depth to next desc block in cur IXL cmd */
301 			ixldepth++;
302 
303 			/*
304 			 * count dma desc blks whose status was set
305 			 * (i.e. advanced to next dma desc)
306 			 */
307 			dma_advances++;
308 			continue;
309 		}
310 
311 		/* if get to here, status is not set */
312 
313 		/*
314 		 * cur IXL cmd dma desc status not set.  save IXL cur cmd
315 		 * and depth and last timestamp for next time.
316 		 */
317 		ctxtp->ixl_execp = ixlp;
318 		ctxtp->ixl_exec_depth = ixldepth;
319 		ctxtp->dma_last_time = *timestampp;
320 
321 		/*
322 		 * check if dma descriptor processing location is indeterminate
323 		 * (lost), context has either stopped, is done, or has skipped
324 		 */
325 		intrstatus = hci1394_ixl_intr_check_done(soft_statep, ctxtp);
326 		if (intrstatus == IXL_CHECK_LOST) {
327 			/*
328 			 * location indeterminate, try once more to determine
329 			 * current state.  First, recheck if status has become
330 			 * set in cur dma descriptor block.  (don't reset status
331 			 * here if is set)
332 			 */
333 			if (hci1394_ixl_check_status(&xferctlp->dma[ixldepth],
334 			    ixlopcode, timestampp, 1) != B_TRUE) {
335 				/* resume from where we left off */
336 				skipped = 0;
337 				continue;
338 			}
339 
340 			/*
341 			 * status not set, check intr processing
342 			 * completion status again
343 			 */
344 			if ((intrstatus = hci1394_ixl_intr_check_done(
345 				soft_statep, ctxtp)) == IXL_CHECK_LOST) {
346 				/*
347 				 * location still indeterminate,
348 				 * processing is lost
349 				 */
350 				*donecodep = HCI1394_IXL_INTR_DMALOST;
351 				return (B_TRUE);
352 			}
353 		}
354 
355 		/*
356 		 * if dma processing stopped. current location has been
357 		 * determined.
358 		 */
359 		if (intrstatus == IXL_CHECK_STOP) {
360 			/*
361 			 * save timestamp, clear currently executing IXL
362 			 * command and depth. return stopped.
363 			 */
364 			ctxtp->ixl_execp = NULL;
365 			ctxtp->ixl_exec_depth = 0;
366 			ctxtp->dma_last_time = *timestampp;
367 			ctxtp->rem_noadv_intrs = 0;
368 
369 			*donecodep = HCI1394_IXL_INTR_DMASTOP;
370 			return (B_TRUE);
371 		}
372 
373 		/*
374 		 * dma processing done for now. current location has
375 		 * has been determined
376 		 */
377 		if (intrstatus == IXL_CHECK_DONE) {
378 			/*
379 			 * if in update processing call:
380 			 *    clear update processing flag & return ok.
381 			 *    if dma advances happened, reset to max allowed.
382 			 *    however, if none have, don't reduce remaining
383 			 *    amount - that's for real interrupt call to adjust.
384 			 */
385 			if (ctxtp->intr_flags & HCI1394_ISO_CTXT_INUPDATE) {
386 
387 				if (dma_advances > 0) {
388 					ctxtp->rem_noadv_intrs =
389 					    ctxtp->max_noadv_intrs;
390 				}
391 
392 				*donecodep = HCI1394_IXL_INTR_NOERROR;
393 				return (B_TRUE);
394 			}
395 
396 			/*
397 			 * else, not in update call processing, are in normal
398 			 * intr call.  if no dma statuses were found set
399 			 * (i.e. no dma advances), reduce remaining count of
400 			 * interrupts allowed with no I/O completions
401 			 */
402 			if (dma_advances == 0) {
403 				ctxtp->rem_noadv_intrs--;
404 			} else {
405 				/*
406 				 * else some dma statuses were found set.
407 				 * reinit remaining count of interrupts allowed
408 				 * with no I/O completions
409 				 */
410 				ctxtp->rem_noadv_intrs = ctxtp->max_noadv_intrs;
411 			}
412 
413 			/*
414 			 * if no remaining count of interrupts allowed with no
415 			 * I/O completions, return failure (no dma advance after
416 			 * max retries), else return ok
417 			 */
418 			if (ctxtp->rem_noadv_intrs == 0) {
419 				*donecodep = HCI1394_IXL_INTR_NOADV;
420 				return (B_TRUE);
421 			}
422 
423 			*donecodep = HCI1394_IXL_INTR_NOERROR;
424 			return (B_TRUE);
425 		}
426 
427 		/*
428 		 * else (intrstatus == IXL_CHECK_SKIP) indicating skip has
429 		 * occured, retrieve current IXL cmd, depth, and timestamp and
430 		 * continue interrupt processing
431 		 */
432 		skipped = 1;
433 		*ixlnextpp = ctxtp->ixl_execp;
434 		ixldepth = ctxtp->ixl_exec_depth;
435 		*timestampp = ctxtp->dma_last_time;
436 
437 		/*
438 		 * also count as 1, intervening skips to next posted
439 		 * dma descriptor.
440 		 */
441 		dma_advances++;
442 	}
443 
444 	/*
445 	 * if full depth reached at current IXL cmd, set back to start for next
446 	 * IXL xfer command that will be processed
447 	 */
448 	if ((skipped == 0) && (ixldepth >= xferctlp->cnt)) {
449 		ctxtp->ixl_exec_depth = 0;
450 	}
451 
452 	/*
453 	 * make sure rem_noadv_intrs is reset to max if we advanced.
454 	 */
455 	if (dma_advances > 0) {
456 		ctxtp->rem_noadv_intrs = ctxtp->max_noadv_intrs;
457 	}
458 
459 	/* continue to process next IXL command */
460 	return (B_FALSE);
461 }
462 
463 /*
464  * hci1394_ixl_intr_check_done()
465  *    checks if context has stopped, or if able to match hardware location
466  *    with an expected IXL program location.
467  */
468 static int
hci1394_ixl_intr_check_done(hci1394_state_t * soft_statep,hci1394_iso_ctxt_t * ctxtp)469 hci1394_ixl_intr_check_done(hci1394_state_t *soft_statep,
470     hci1394_iso_ctxt_t *ctxtp)
471 {
472 	ixl1394_command_t   *ixlp;
473 	hci1394_xfer_ctl_t  *xferctlp;
474 	uint_t		    ixldepth;
475 	hci1394_xfer_ctl_dma_t *dma;
476 	ddi_acc_handle_t    acc_hdl;
477 	ddi_dma_handle_t    dma_hdl;
478 	uint32_t	    desc_status;
479 	hci1394_desc_t	    *hcidescp;
480 	off_t		    hcidesc_off;
481 	uint32_t	    dma_cmd_cur_loc;
482 	uint32_t	    dma_cmd_last_loc;
483 	uint32_t	    dma_loc_check_enabled;
484 	uint32_t	    dmastartp;
485 	uint32_t	    dmaendp;
486 
487 	uint_t		    rem_dma_skips;
488 	uint16_t	    skipmode;
489 	uint16_t	    skipdepth;
490 	ixl1394_command_t   *skipdestp;
491 	ixl1394_command_t   *skipxferp;
492 
493 	/*
494 	 * start looking through the IXL list from the xfer start command where
495 	 * we last left off (for composite opcodes, need to start from the
496 	 * appropriate depth).
497 	 */
498 
499 	ixlp = ctxtp->ixl_execp;
500 	ixldepth = ctxtp->ixl_exec_depth;
501 
502 	/* control struct for xfer start IXL command */
503 	xferctlp = (hci1394_xfer_ctl_t *)ixlp->compiler_privatep;
504 	dma = &xferctlp->dma[ixldepth];
505 
506 	/* determine if dma location checking is enabled */
507 	if ((dma_loc_check_enabled =
508 	    (ctxtp->ctxt_flags & HCI1394_ISO_CTXT_CMDREG)) != 0) {
509 
510 		/* if so, get current dma command location */
511 		dma_cmd_last_loc = 0xFFFFFFFF;
512 
513 		while ((dma_cmd_cur_loc = HCI1394_ISOCH_CTXT_CMD_PTR(
514 		    soft_statep, ctxtp)) != dma_cmd_last_loc) {
515 
516 			/* retry get until location register stabilizes */
517 			dma_cmd_last_loc = dma_cmd_cur_loc;
518 		}
519 	}
520 
521 	/*
522 	 * compare the (bound) address of the DMA descriptor corresponding to
523 	 * the current xfer IXL command against the current value in the
524 	 * DMA location register.  If exists and if matches, then
525 	 *    if context stopped, return stopped, else return done.
526 	 *
527 	 * The dma start address is the first address of the descriptor block.
528 	 * Since "Z" is a count of 16-byte descriptors in the block, calculate
529 	 * the end address by adding Z*16 to the start addr.
530 	 */
531 	dmastartp = dma->dma_bound & ~DESC_Z_MASK;
532 	dmaendp = dmastartp + ((dma->dma_bound & DESC_Z_MASK) << 4);
533 
534 	if (dma_loc_check_enabled &&
535 	    ((dma_cmd_cur_loc >= dmastartp) && (dma_cmd_cur_loc < dmaendp))) {
536 
537 		if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) == 0) {
538 			return (IXL_CHECK_STOP);
539 		}
540 
541 		return (IXL_CHECK_DONE);
542 	}
543 
544 	/*
545 	 * if receive mode:
546 	 */
547 	if ((ixlp->ixl_opcode & IXL1394_OPF_ONXMIT) == 0)  {
548 		/*
549 		 * if context stopped, return stopped, else,
550 		 * if there is no current dma location reg, return done
551 		 * else return location indeterminate
552 		 */
553 		if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) == 0) {
554 			return (IXL_CHECK_STOP);
555 		}
556 		if (!dma_loc_check_enabled) {
557 			return (IXL_CHECK_DONE);
558 		}
559 
560 		return (IXL_CHECK_LOST);
561 	}
562 
563 	/*
564 	 * else is xmit mode:
565 	 * check status of current xfer IXL command's dma descriptor
566 	 */
567 	acc_hdl  = dma->dma_buf->bi_handle;
568 	dma_hdl  = dma->dma_buf->bi_dma_handle;
569 	hcidescp = (hci1394_desc_t *)dma->dma_descp;
570 	hcidesc_off = (off_t)hcidescp - (off_t)dma->dma_buf->bi_kaddr;
571 
572 	/* Sync the descriptor before we get the status */
573 	(void) ddi_dma_sync(dma_hdl, hcidesc_off, sizeof (hci1394_desc_t),
574 	    DDI_DMA_SYNC_FORCPU);
575 	desc_status = ddi_get32(acc_hdl, &hcidescp->status);
576 
577 	if ((desc_status & DESC_XFER_ACTIVE_MASK) != 0) {
578 
579 		/*
580 		 * if status is now set here, return skipped, to cause calling
581 		 * function to continue, even though location hasn't changed
582 		 */
583 		return (IXL_CHECK_SKIP);
584 	}
585 
586 	/*
587 	 * At this point, we have gotten to a DMA descriptor with an empty
588 	 * status.  This is not enough information however to determine that
589 	 * we've found all processed DMA descriptors because during cycle-lost
590 	 * conditions, the HW will skip over some descriptors without writing
591 	 * status.  So we have to look ahead until we're convinced that the HW
592 	 * hasn't jumped ahead.
593 	 *
594 	 * Follow the IXL skip-to links until find one whose status is set
595 	 * or until dma location register (if any) matches an xfer IXL
596 	 * command's dma location or until have examined max_dma_skips
597 	 * IXL commands.
598 	 */
599 	rem_dma_skips = ctxtp->max_dma_skips;
600 
601 	while (rem_dma_skips-- > 0) {
602 
603 		/*
604 		 * get either IXL command specific or
605 		 * system default skipmode info
606 		 */
607 		skipdepth = 0;
608 		if (xferctlp->skipmodep != NULL) {
609 			skipmode  = xferctlp->skipmodep->skipmode;
610 			skipdestp = xferctlp->skipmodep->label;
611 			skipxferp = (ixl1394_command_t *)
612 			    xferctlp->skipmodep->compiler_privatep;
613 		} else {
614 			skipmode  = ctxtp->default_skipmode;
615 			skipdestp = ctxtp->default_skiplabelp;
616 			skipxferp = ctxtp->default_skipxferp;
617 		}
618 
619 		switch (skipmode) {
620 
621 		case IXL1394_SKIP_TO_SELF:
622 			/*
623 			 * mode is skip to self:
624 			 *   if context is stopped, return stopped, else
625 			 *   if dma location reg not enabled, return done
626 			 *   else, return location indeterminate
627 			 */
628 			if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) ==
629 			    0) {
630 				return (IXL_CHECK_STOP);
631 			}
632 
633 			if (!dma_loc_check_enabled) {
634 				return (IXL_CHECK_DONE);
635 			}
636 
637 			return (IXL_CHECK_LOST);
638 
639 		case IXL1394_SKIP_TO_NEXT:
640 			/*
641 			 * mode is skip to next:
642 			 *    set potential skip target to current command at
643 			 *    next depth
644 			 */
645 			skipdestp = ixlp;
646 			skipxferp = ixlp;
647 			skipdepth = ixldepth + 1;
648 
649 			/*
650 			 * else if at max depth at current cmd adjust to next
651 			 * IXL command.
652 			 *
653 			 * (NOTE: next means next IXL command along execution
654 			 * path,  whatever IXL command it might be.  e.g. store
655 			 * timestamp or callback or label or jump or send... )
656 			 */
657 			if (skipdepth >= xferctlp->cnt) {
658 				skipdepth = 0;
659 				skipdestp = ixlp->next_ixlp;
660 				skipxferp = xferctlp->execp;
661 			}
662 
663 			/* evaluate skip to status further, below */
664 			break;
665 
666 
667 		case IXL1394_SKIP_TO_LABEL:
668 			/*
669 			 * mode is skip to label:
670 			 *    set skip destination depth to 0 (should be
671 			 *    redundant)
672 			 */
673 			skipdepth = 0;
674 
675 			/* evaluate skip to status further, below */
676 			break;
677 
678 		case IXL1394_SKIP_TO_STOP:
679 			/*
680 			 * mode is skip to stop:
681 			 *    set all xfer and destination skip to locations to
682 			 *    null
683 			 */
684 			skipxferp = NULL;
685 			skipdestp = NULL;
686 			skipdepth = 0;
687 
688 			/* evaluate skip to status further, below */
689 			break;
690 
691 		} /* end switch */
692 
693 		/*
694 		 * if no xfer IXL command follows at or after current skip-to
695 		 * location
696 		 */
697 		if (skipxferp == NULL) {
698 			/*
699 			 *   if context is stopped, return stopped, else
700 			 *   if dma location reg not enabled, return done
701 			 *   else, return location indeterminate
702 			 */
703 			if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) ==
704 			    0) {
705 				return (IXL_CHECK_STOP);
706 			}
707 
708 			if (!dma_loc_check_enabled) {
709 				return (IXL_CHECK_DONE);
710 			}
711 			return (IXL_CHECK_LOST);
712 		}
713 
714 		/*
715 		 * if the skip to xfer IXL dma descriptor's status is set,
716 		 * then execution did skip
717 		 */
718 		xferctlp = (hci1394_xfer_ctl_t *)skipxferp->compiler_privatep;
719 		dma	 = &xferctlp->dma[skipdepth];
720 		acc_hdl  = dma->dma_buf->bi_handle;
721 		dma_hdl  = dma->dma_buf->bi_dma_handle;
722 		hcidescp = (hci1394_desc_t *)dma->dma_descp;
723 		hcidesc_off = (off_t)hcidescp - (off_t)dma->dma_buf->bi_kaddr;
724 
725 		/* Sync the descriptor before we get the status */
726 		(void) ddi_dma_sync(dma_hdl, hcidesc_off,
727 		    sizeof (hci1394_desc_t), DDI_DMA_SYNC_FORCPU);
728 		desc_status = ddi_get32(acc_hdl, &hcidescp->status);
729 
730 		if ((desc_status & DESC_XFER_ACTIVE_MASK) != 0) {
731 
732 			/*
733 			 * adjust to continue from skip to IXL command and
734 			 * return skipped, to have calling func continue.
735 			 * (Note: next IXL command may be any allowed IXL
736 			 * command)
737 			 */
738 			ctxtp->ixl_execp = skipdestp;
739 			ctxtp->ixl_exec_depth = skipdepth;
740 
741 			return (IXL_CHECK_SKIP);
742 		}
743 
744 		/*
745 		 * if dma location command register checking is enabled,
746 		 * and the skip to xfer IXL dma location matches current
747 		 * dma location register value, execution did skip
748 		 */
749 		dmastartp = dma->dma_bound & ~DESC_Z_MASK;
750 		dmaendp = dmastartp + ((dma->dma_bound & DESC_Z_MASK) << 4);
751 
752 		if (dma_loc_check_enabled && ((dma_cmd_cur_loc >= dmastartp) &&
753 		    (dma_cmd_cur_loc < dmaendp))) {
754 
755 			/* if the context is stopped, return stopped */
756 			if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) ==
757 			    0) {
758 				return (IXL_CHECK_STOP);
759 			}
760 			/*
761 			 * adjust to continue from skip to IXL command and
762 			 * return skipped, to have calling func continue
763 			 * (Note: next IXL command may be any allowed IXL cmd)
764 			 */
765 			ctxtp->ixl_execp = skipdestp;
766 			ctxtp->ixl_exec_depth = skipdepth;
767 
768 			return (IXL_CHECK_SKIP);
769 		}
770 
771 		/*
772 		 * else, advance working current locn to skipxferp and
773 		 * skipdepth and continue skip evaluation loop processing
774 		 */
775 		ixlp = skipxferp;
776 		ixldepth = skipdepth;
777 
778 	} /* end while */
779 
780 	/*
781 	 * didn't find dma status set, nor location reg match, along skip path
782 	 *
783 	 * if context is stopped, return stopped,
784 	 *
785 	 * else if no current location reg active don't change context values,
786 	 * just return done (no skip)
787 	 *
788 	 * else, return location indeterminate
789 	 */
790 
791 	if (HCI1394_ISOCH_CTXT_ACTIVE(soft_statep, ctxtp) == 0) {
792 		return (IXL_CHECK_STOP);
793 	}
794 	if (!dma_loc_check_enabled) {
795 		return (IXL_CHECK_DONE);
796 	}
797 
798 	return (IXL_CHECK_LOST);
799 }
800 
801 /*
802  * hci1394_isoch_cycle_inconsistent()
803  *    Called during interrupt notification to indicate that the cycle time
804  *    has changed unexpectedly.  We need to take this opportunity to
805  *    update our tracking of each running transmit context's execution.
806  *    cycle_inconsistent only affects transmit, so recv contexts are left alone.
807  */
808 void
hci1394_isoch_cycle_inconsistent(hci1394_state_t * soft_statep)809 hci1394_isoch_cycle_inconsistent(hci1394_state_t *soft_statep)
810 {
811 	int i, cnt_thresh;
812 	boolean_t note;
813 	hrtime_t current_time, last_time, delta, delta_thresh;
814 	hci1394_iso_ctxt_t *ctxtp; 	/* current context */
815 
816 	ASSERT(soft_statep);
817 
818 	hci1394_ohci_intr_clear(soft_statep->ohci, OHCI_INTR_CYC_INCONSISTENT);
819 
820 	/* grab the mutex before checking each context's INUSE and RUNNING */
821 	mutex_enter(&soft_statep->isoch->ctxt_list_mutex);
822 
823 	/* check for transmit contexts which are inuse and running */
824 	for (i = 0; i < soft_statep->isoch->ctxt_xmit_count; i++) {
825 		ctxtp = &soft_statep->isoch->ctxt_xmit[i];
826 
827 		if ((ctxtp->ctxt_flags &
828 		    (HCI1394_ISO_CTXT_INUSE | HCI1394_ISO_CTXT_RUNNING)) != 0) {
829 
830 			mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
831 			hci1394_ixl_interrupt(soft_statep, ctxtp, B_FALSE);
832 			mutex_enter(&soft_statep->isoch->ctxt_list_mutex);
833 		}
834 	}
835 
836 	/*
837 	 * get the current time and calculate the delta between now and
838 	 * when the last interrupt was processed.  (NOTE: if the time
839 	 * returned by gethrtime() rolls-over while we are counting these
840 	 * interrupts, we will incorrectly restart the counting process.
841 	 * However, because the probability of this happening is small and
842 	 * not catching the roll-over will AT MOST double the time it takes
843 	 * us to discover and correct from this condition, we can safely
844 	 * ignore it.)
845 	 */
846 	current_time = gethrtime();
847 	last_time = soft_statep->isoch->cycle_incon_thresh.last_intr_time;
848 	delta = current_time - last_time;
849 
850 	/*
851 	 * compare the calculated delta to the delta T threshold.  If it
852 	 * is less than the threshold, then increment the counter.  If it
853 	 * is not then reset the counter.
854 	 */
855 	delta_thresh = soft_statep->isoch->cycle_incon_thresh.delta_t_thresh;
856 	if (delta < delta_thresh)
857 		soft_statep->isoch->cycle_incon_thresh.delta_t_counter++;
858 	else
859 		soft_statep->isoch->cycle_incon_thresh.delta_t_counter = 0;
860 
861 	/*
862 	 * compare the counter to the counter threshold.  If it is greater,
863 	 * then disable the cycle inconsistent interrupt.
864 	 */
865 	cnt_thresh = soft_statep->isoch->cycle_incon_thresh.counter_thresh;
866 	note = B_FALSE;
867 	if (soft_statep->isoch->cycle_incon_thresh.delta_t_counter >
868 	    cnt_thresh) {
869 		hci1394_ohci_intr_disable(soft_statep->ohci,
870 		    OHCI_INTR_CYC_INCONSISTENT);
871 		note = B_TRUE;
872 	}
873 
874 	/* save away the current time into the last_intr_time field */
875 	soft_statep->isoch->cycle_incon_thresh.last_intr_time = current_time;
876 
877 	mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
878 
879 	if (note == B_TRUE) {
880 		cmn_err(CE_NOTE, "!hci1394(%d): cycle_inconsistent interrupt "
881 		    "disabled until next bus reset",
882 		    soft_statep->drvinfo.di_instance);
883 	}
884 }
885 
886 
887 /*
888  * hci1394_isoch_cycle_lost()
889  *    Interrupt indicates an expected cycle_start packet (and therefore our
890  *    opportunity to transmit) did not show up.  Update our tracking of each
891  *    running transmit context.
892  */
893 void
hci1394_isoch_cycle_lost(hci1394_state_t * soft_statep)894 hci1394_isoch_cycle_lost(hci1394_state_t *soft_statep)
895 {
896 	int i, cnt_thresh;
897 	boolean_t note;
898 	hrtime_t current_time, last_time, delta, delta_thresh;
899 	hci1394_iso_ctxt_t *ctxtp; 	/* current context */
900 
901 	ASSERT(soft_statep);
902 
903 	hci1394_ohci_intr_clear(soft_statep->ohci, OHCI_INTR_CYC_LOST);
904 
905 	/* grab the mutex before checking each context's INUSE and RUNNING */
906 	mutex_enter(&soft_statep->isoch->ctxt_list_mutex);
907 
908 	/* check for transmit contexts which are inuse and running */
909 	for (i = 0; i < soft_statep->isoch->ctxt_xmit_count; i++) {
910 		ctxtp = &soft_statep->isoch->ctxt_xmit[i];
911 
912 		if ((ctxtp->ctxt_flags &
913 		    (HCI1394_ISO_CTXT_INUSE | HCI1394_ISO_CTXT_RUNNING)) != 0) {
914 
915 			mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
916 			hci1394_ixl_interrupt(soft_statep, ctxtp, B_FALSE);
917 			mutex_enter(&soft_statep->isoch->ctxt_list_mutex);
918 		}
919 	}
920 
921 	/*
922 	 * get the current time and calculate the delta between now and
923 	 * when the last interrupt was processed.  (NOTE: if the time
924 	 * returned by gethrtime() rolls-over while we are counting these
925 	 * interrupts, we will incorrectly restart the counting process.
926 	 * However, because the probability of this happening is small and
927 	 * not catching the roll-over will AT MOST double the time it takes
928 	 * us to discover and correct from this condition, we can safely
929 	 * ignore it.)
930 	 */
931 	current_time = gethrtime();
932 	last_time = soft_statep->isoch->cycle_lost_thresh.last_intr_time;
933 	delta = current_time - last_time;
934 
935 	/*
936 	 * compare the calculated delta to the delta T threshold.  If it
937 	 * is less than the threshold, then increment the counter.  If it
938 	 * is not then reset the counter.
939 	 */
940 	delta_thresh = soft_statep->isoch->cycle_lost_thresh.delta_t_thresh;
941 	if (delta < delta_thresh)
942 		soft_statep->isoch->cycle_lost_thresh.delta_t_counter++;
943 	else
944 		soft_statep->isoch->cycle_lost_thresh.delta_t_counter = 0;
945 
946 	/*
947 	 * compare the counter to the counter threshold.  If it is greater,
948 	 * then disable the cycle lost interrupt.
949 	 */
950 	cnt_thresh = soft_statep->isoch->cycle_lost_thresh.counter_thresh;
951 	note = B_FALSE;
952 	if (soft_statep->isoch->cycle_lost_thresh.delta_t_counter >
953 	    cnt_thresh) {
954 		hci1394_ohci_intr_disable(soft_statep->ohci,
955 		    OHCI_INTR_CYC_LOST);
956 		note = B_TRUE;
957 	}
958 
959 	/* save away the current time into the last_intr_time field */
960 	soft_statep->isoch->cycle_lost_thresh.last_intr_time = current_time;
961 
962 	mutex_exit(&soft_statep->isoch->ctxt_list_mutex);
963 
964 	if (note == B_TRUE) {
965 		cmn_err(CE_NOTE, "!hci1394(%d): cycle_lost interrupt "
966 		    "disabled until next bus reset",
967 		    soft_statep->drvinfo.di_instance);
968 	}
969 }
970