/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Isochronous IXL Interrupt Service Routines.
* The interrupt handler determines which OpenHCI DMA descriptors
* have been executed by the hardware, tracks the path in the
* corresponding IXL program, issues callbacks as needed, and resets
* the OpenHCI DMA descriptors.
*/
#include <sys/tnf_probe.h>
/* Return values for local hci1394_ixl_intr_check_done() */
/*
* hci1394_ixl_interrupt
* main entry point (front-end) into interrupt processing.
* acquires mutex, checks if update in progress, sets flags accordingly,
* and calls to do real interrupt processing.
*/
void
{
int retcode;
status = 1;
/* acquire the interrupt processing context mutex */
/* set flag to indicate that interrupt processing is required */
/* if update proc already in progress, let it handle intr processing */
status = 0;
"HCI1394_IXL_INTR_INUPDATE");
/* else fatal error if inter processing already in progress */
status = 0;
"HCI1394_IXL_INTR_ININTR");
/* else fatal error if callback in progress flag is set */
status = 0;
"HCI1394_IXL_INTR_INCALL");
/* context is being stopped */
status = 0;
"HCI1394_IXL_INTR_STOP");
}
/*
* if context is available, reserve it, do interrupt processing
* and free it
*/
if (status) {
/* notify stop thread that the interrupt is finished */
}
};
/* free the intr processing context mutex before error checks */
/* if context stopped, invoke callback */
if (retcode == HCI1394_IXL_INTR_DMASTOP) {
}
/* if error, stop and invoke callback */
if (retcode == HCI1394_IXL_INTR_DMALOST) {
}
}
/*
* hci1394_ixl_dma_sync()
* the heart of interrupt processing, this routine correlates where the
* hardware is for the specified context with the IXL program. Invokes
* callbacks as needed. Also called by "update" to make sure ixl is
* sync'ed up with where the hardware is.
* Returns one of the ixl_intr defined return codes - HCI1394_IXL_INTR...
* {..._DMALOST, ..._DMASTOP, ..._NOADV,... _NOERROR}
*/
int
{
int donecode;
/* xfer start ixl cmd where last left off */
/* last completed descriptor block's timestamp */
/*
* follow execution path in IXL, until find dma descriptor in IXL
* xfer command whose status isn't set or until run out of IXL cmds
*/
/*
* process IXL commands: xfer start, callback, store timestamp
* and jump and ignore the others
*/
/* determine if this is an xfer start IXL command */
if (((ixlopcode & IXL1394_OPF_ISXFER) != 0) &&
((ixlopcode & IXL1394_OPTY_MASK) != 0)) {
/* process xfer cmd to see if HW has been here */
return (donecode);
}
/* continue to process next IXL command */
continue;
}
/* else check if IXL cmd - jump, callback or store timestamp */
switch (ixlopcode) {
case IXL1394_OP_JUMP:
/*
* set next IXL cmd to label ptr in current IXL jump cmd
*/
break;
/*
* set last timestamp value recorded into current IXL
* cmd
*/
break;
case IXL1394_OP_CALLBACK:
/*
* if callback function is specified, call it with IXL
* cmd addr. Make sure to grab the lock before setting
* the "in callback" flag in intr_flags.
*/
(ixl1394_callback_t *)ixlp);
}
/*
* And grab the lock again before clearing
* the "in callback" flag.
*/
break;
}
}
/*
* If we jumped to NULL because of an updateable JUMP, set ixl_execp
* back to ixlp. The destination label might get updated to a
* non-NULL value.
*/
"INTR_NOERROR");
return (HCI1394_IXL_INTR_NOERROR);
}
/* save null IXL cmd and depth and last timestamp */
ctxtp->ixl_exec_depth = 0;
ctxtp->rem_noadv_intrs = 0;
/* return stopped status if at end of IXL cmds & context stopped */
"INTR_DMASTOP");
return (HCI1394_IXL_INTR_DMASTOP);
}
/* else interrupt processing is lost */
return (HCI1394_IXL_INTR_DMALOST);
}
/*
* hci1394_ixl_intr_check_xfer()
* Process given IXL xfer cmd, checking status of each dma descriptor block
* for the command until find one whose status isn't set or until full depth
* reached at current IXL command or until find hardware skip has occurred.
*
* Returns B_TRUE if processing should terminate (either have stopped
* or encountered an error), and B_FALSE if it should continue looking.
* If B_TRUE, donecodep contains the reason: HCI1394_IXL_INTR_DMALOST,
* HCI1394_IXL_INTR_DMASTOP, HCI1394_IXL_INTR_NOADV, or
* HCI1394_IXL_INTR_NOERROR. NOERROR means that the current location
* has been determined and do not need to look further.
*/
static boolean_t
{
int intrstatus;
*donecodep = 0;
dma_advances = 0;
/* get control struct for this xfer start IXL command */
skipped = 0;
/*
* check if status is set in dma descriptor
* block at cur depth in cur xfer start IXL cmd
*/
/* advance depth to next desc block in cur IXL cmd */
ixldepth++;
/*
* count dma desc blks whose status was set
* (i.e. advanced to next dma desc)
*/
dma_advances++;
continue;
}
/* if get to here, status is not set */
/*
* cur IXL cmd dma desc status not set. save IXL cur cmd
* and depth and last timestamp for next time.
*/
/*
* check if dma descriptor processing location is indeterminate
* (lost), context has either stopped, is done, or has skipped
*/
if (intrstatus == IXL_CHECK_LOST) {
/*
* location indeterminate, try once more to determine
* current state. First, recheck if status has become
* set in cur dma descriptor block. (don't reset status
* here if is set)
*/
/* resume from where we left off */
skipped = 0;
continue;
}
/*
* status not set, check intr processing
* completion status again
*/
/*
* location still indeterminate,
* processing is lost
*/
return (B_TRUE);
}
}
/*
* if dma processing stopped. current location has been
* determined.
*/
if (intrstatus == IXL_CHECK_STOP) {
/*
* save timestamp, clear currently executing IXL
* command and depth. return stopped.
*/
ctxtp->ixl_exec_depth = 0;
ctxtp->rem_noadv_intrs = 0;
"INTR_DMASTOP");
return (B_TRUE);
}
/*
* dma processing done for now. current location has
* has been determined
*/
if (intrstatus == IXL_CHECK_DONE) {
/*
* if in update processing call:
* clear update processing flag & return ok.
* if dma advances happened, reset to max allowed.
* however, if none have, don't reduce remaining
* amount - that's for real interrupt call to adjust.
*/
if (dma_advances > 0) {
}
return (B_TRUE);
}
/*
* else, not in update call processing, are in normal
* intr call. if no dma statuses were found set
* (i.e. no dma advances), reduce remaining count of
* interrupts allowed with no I/O completions
*/
if (dma_advances == 0) {
ctxtp->rem_noadv_intrs--;
} else {
/*
* else some dma statuses were found set.
* reinit remaining count of interrupts allowed
* with no I/O completions
*/
}
/*
* if no remaining count of interrupts allowed with no
* I/O completions, return failure (no dma advance after
* max retries), else return ok
*/
if (ctxtp->rem_noadv_intrs == 0) {
return (B_TRUE);
}
"INTR_NOERROR2");
return (B_TRUE);
}
/*
* else (intrstatus == IXL_CHECK_SKIP) indicating skip has
* occured, retrieve current IXL cmd, depth, and timestamp and
* continue interrupt processing
*/
skipped = 1;
/*
* also count as 1, intervening skips to next posted
* dma descriptor.
*/
dma_advances++;
}
/*
* if full depth reached at current IXL cmd, set back to start for next
* IXL xfer command that will be processed
*/
ctxtp->ixl_exec_depth = 0;
}
/*
* make sure rem_noadv_intrs is reset to max if we advanced.
*/
if (dma_advances > 0) {
}
/* continue to process next IXL command */
return (B_FALSE);
}
/*
* hci1394_ixl_intr_check_done()
* checks if context has stopped, or if able to match hardware location
* with an expected IXL program location.
*/
static int
{
int err;
/*
* start looking through the IXL list from the xfer start command where
* we last left off (for composite opcodes, need to start from the
* appropriate depth).
*/
/* control struct for xfer start IXL command */
/* determine if dma location checking is enabled */
if ((dma_loc_check_enabled =
/* if so, get current dma command location */
dma_cmd_last_loc = 0xFFFFFFFF;
while ((dma_cmd_cur_loc = HCI1394_ISOCH_CTXT_CMD_PTR(
/* retry get until location register stabilizes */
}
}
/*
* compare the (bound) address of the DMA descriptor corresponding to
* the current xfer IXL command against the current value in the
* DMA location register. If exists and if matches, then
* if context stopped, return stopped, else return done.
*
* The dma start address is the first address of the descriptor block.
* Since "Z" is a count of 16-byte descriptors in the block, calculate
* the end address by adding Z*16 to the start addr.
*/
if (dma_loc_check_enabled &&
"CHECK_STOP");
return (IXL_CHECK_STOP);
}
"CHECK_DONE");
return (IXL_CHECK_DONE);
}
/*
* if receive mode:
*/
/*
* if context stopped, return stopped, else,
* if there is no current dma location reg, return done
* else return location indeterminate
*/
"CHECK_STOP");
return (IXL_CHECK_STOP);
}
if (!dma_loc_check_enabled) {
"CHECK_DONE");
return (IXL_CHECK_DONE);
}
"CHECK_LOST");
return (IXL_CHECK_LOST);
}
/*
* else is xmit mode:
* check status of current xfer IXL command's dma descriptor
*/
/* Sync the descriptor before we get the status */
if (err != DDI_SUCCESS) {
"dma_sync() failed");
}
if ((desc_status & DESC_XFER_ACTIVE_MASK) != 0) {
/*
* if status is now set here, return skipped, to cause calling
* function to continue, even though location hasn't changed
*/
"CHECK_SKIP");
return (IXL_CHECK_SKIP);
}
/*
* At this point, we have gotten to a DMA descriptor with an empty
* status. This is not enough information however to determine that
* we've found all processed DMA descriptors because during cycle-lost
* conditions, the HW will skip over some descriptors without writing
* status. So we have to look ahead until we're convinced that the HW
* hasn't jumped ahead.
*
* Follow the IXL skip-to links until find one whose status is set
* or until dma location register (if any) matches an xfer IXL
* command's dma location or until have examined max_dma_skips
* IXL commands.
*/
while (rem_dma_skips-- > 0) {
/*
* get either IXL command specific or
* system default skipmode info
*/
skipdepth = 0;
skipxferp = (ixl1394_command_t *)
} else {
}
switch (skipmode) {
case IXL1394_SKIP_TO_SELF:
/*
* mode is skip to self:
* if context is stopped, return stopped, else
* if dma location reg not enabled, return done
* else, return location indeterminate
*/
0) {
return (IXL_CHECK_STOP);
}
if (!dma_loc_check_enabled) {
return (IXL_CHECK_DONE);
}
"CHECK_LOST");
return (IXL_CHECK_LOST);
case IXL1394_SKIP_TO_NEXT:
/*
* mode is skip to next:
* set potential skip target to current command at
* next depth
*/
/*
* else if at max depth at current cmd adjust to next
* IXL command.
*
* (NOTE: next means next IXL command along execution
* path, whatever IXL command it might be. e.g. store
* timestamp or callback or label or jump or send... )
*/
skipdepth = 0;
}
/* evaluate skip to status further, below */
break;
case IXL1394_SKIP_TO_LABEL:
/*
* mode is skip to label:
* set skip destination depth to 0 (should be
* redundant)
*/
skipdepth = 0;
/* evaluate skip to status further, below */
break;
case IXL1394_SKIP_TO_STOP:
/*
* mode is skip to stop:
* set all xfer and destination skip to locations to
* null
*/
skipdepth = 0;
/* evaluate skip to status further, below */
break;
} /* end switch */
/*
* if no xfer IXL command follows at or after current skip-to
* location
*/
/*
* if context is stopped, return stopped, else
* if dma location reg not enabled, return done
* else, return location indeterminate
*/
0) {
return (IXL_CHECK_STOP);
}
if (!dma_loc_check_enabled) {
return (IXL_CHECK_DONE);
}
"CHECK_LOST");
return (IXL_CHECK_LOST);
}
/*
* if the skip to xfer IXL dma descriptor's status is set,
* then execution did skip
*/
/* Sync the descriptor before we get the status */
sizeof (hci1394_desc_t), DDI_DMA_SYNC_FORCPU);
if (err != DDI_SUCCESS) {
"dma_sync() failed");
}
if ((desc_status & DESC_XFER_ACTIVE_MASK) != 0) {
/*
* adjust to continue from skip to IXL command and
* return skipped, to have calling func continue.
* (Note: next IXL command may be any allowed IXL
* command)
*/
"CHECK_SKIP");
return (IXL_CHECK_SKIP);
}
/*
* if dma location command register checking is enabled,
* and the skip to xfer IXL dma location matches current
* dma location register value, execution did skip
*/
(dma_cmd_cur_loc < dmaendp))) {
/* if the context is stopped, return stopped */
0) {
return (IXL_CHECK_STOP);
}
/*
* adjust to continue from skip to IXL command and
* return skipped, to have calling func continue
* (Note: next IXL command may be any allowed IXL cmd)
*/
"CHECK_SKIP");
return (IXL_CHECK_SKIP);
}
/*
* else, advance working current locn to skipxferp and
* skipdepth and continue skip evaluation loop processing
*/
} /* end while */
/*
* didn't find dma status set, nor location reg match, along skip path
*
* if context is stopped, return stopped,
*
* else if no current location reg active don't change context values,
* just return done (no skip)
*
* else, return location indeterminate
*/
"CHECK_STOP");
return (IXL_CHECK_STOP);
}
if (!dma_loc_check_enabled) {
"CHECK_DONE");
return (IXL_CHECK_DONE);
}
return (IXL_CHECK_LOST);
}
/*
* hci1394_isoch_cycle_inconsistent()
* Called during interrupt notification to indicate that the cycle time
* has changed unexpectedly. We need to take this opportunity to
* update our tracking of each running transmit context's execution.
* cycle_inconsistent only affects transmit, so recv contexts are left alone.
*/
void
{
int i, cnt_thresh;
/* grab the mutex before checking each context's INUSE and RUNNING */
/* check for transmit contexts which are inuse and running */
if ((ctxtp->ctxt_flags &
(HCI1394_ISO_CTXT_INUSE | HCI1394_ISO_CTXT_RUNNING)) != 0) {
}
}
/*
* get the current time and calculate the delta between now and
* when the last interrupt was processed. (NOTE: if the time
* returned by gethrtime() rolls-over while we are counting these
* interrupts, we will incorrectly restart the counting process.
* However, because the probability of this happening is small and
* not catching the roll-over will AT MOST double the time it takes
* us to discover and correct from this condition, we can safely
* ignore it.)
*/
current_time = gethrtime();
/*
* compare the calculated delta to the delta T threshold. If it
* is less than the threshold, then increment the counter. If it
* is not then reset the counter.
*/
if (delta < delta_thresh)
else
/*
* compare the counter to the counter threshold. If it is greater,
* then disable the cycle inconsistent interrupt.
*/
cnt_thresh) {
}
/* save away the current time into the last_intr_time field */
"disabled until next bus reset",
"CYCLE_INCONSISTENT intr disabled until next bus reset");
}
}
/*
* hci1394_isoch_cycle_lost()
* Interrupt indicates an expected cycle_start packet (and therefore our
* opportunity to transmit) did not show up. Update our tracking of each
* running transmit context.
*/
void
{
int i, cnt_thresh;
/* grab the mutex before checking each context's INUSE and RUNNING */
/* check for transmit contexts which are inuse and running */
if ((ctxtp->ctxt_flags &
(HCI1394_ISO_CTXT_INUSE | HCI1394_ISO_CTXT_RUNNING)) != 0) {
}
}
/*
* get the current time and calculate the delta between now and
* when the last interrupt was processed. (NOTE: if the time
* returned by gethrtime() rolls-over while we are counting these
* interrupts, we will incorrectly restart the counting process.
* However, because the probability of this happening is small and
* not catching the roll-over will AT MOST double the time it takes
* us to discover and correct from this condition, we can safely
* ignore it.)
*/
current_time = gethrtime();
/*
* compare the calculated delta to the delta T threshold. If it
* is less than the threshold, then increment the counter. If it
* is not then reset the counter.
*/
if (delta < delta_thresh)
else
/*
* compare the counter to the counter threshold. If it is greater,
* then disable the cycle lost interrupt.
*/
cnt_thresh) {
}
/* save away the current time into the last_intr_time field */
"disabled until next bus reset",
"CYCLE_LOST intr disabled until next bus reset");
}
}