static void rts(struct pi_local *lp, int x) { int tc; long br; int cmd; int dummy; /* assumes interrupts are off */ cmd = CTL + lp->base; /* Reprogram BRG and turn on transmitter to send flags */ if (x == ON) { /* Turn Tx ON and Receive OFF */ /* Exints off first to avoid abort int */ wrtscc(lp->cardbase, cmd, R15, 0); wrtscc(lp->cardbase, cmd, R3, Rx8); /* Rx off */ lp->rstate = IDLE; if (cmd & 2) { /* if channel a */ /* Set up for TX dma */ wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB); } else { wrtscc(lp->cardbase, cmd, R1, 0); /* No interrupts */ } if (!lp->clockmode) { if (lp->speed) { /* if internally clocked */ br = lp->speed; /* get desired speed */ tc = (lp->xtal / br) - 2; /* calc 1X BRG divisor */ wrtscc(lp->cardbase, cmd, R12, tc & 0xFF); /* lower byte */ wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF); /* upper byte */ } }
static void tdelay(struct pi_local *lp, int time) { int port; unsigned int t1; unsigned char sc; if (lp->base & 2) { /* If A channel */ sc = SC1; t1 = time; port = lp->cardbase + TMR1; } else { sc = SC2; t1 = 10 * time; /* 10s of milliseconds for the B channel */ port = lp->cardbase + TMR2; wrtscc(lp->cardbase, lp->base + CTL, R1, INT_ALL_Rx | EXT_INT_ENAB); } /* Setup timer sc */ outb_p(sc | LSB_MSB | MODE0, lp->cardbase + TMRCMD); /* times 2 to make millisecs */ outb_p((t1 << 1) & 0xFF, port); outb_p((t1 >> 7) & 0xFF, port); /* Enable correct int for timeout */ wrtscc(lp->cardbase, lp->base + CTL, R15, CTSIE); wrtscc(lp->cardbase, lp->base + CTL, R0, RES_EXT_INT); }
static void a_txint(struct pi_local *lp) { int cmd; unsigned long flags; save_flags(flags); cli(); cmd = CTL + lp->base; switch (lp->tstate) { case IDLE: /* Transmitter idle. Find a frame for transmission */ if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) { rts(lp, OFF); restore_flags(flags); return; } /* If a buffer to send, we drop thru here */ case DEFER: /* we may have deferred prev xmit attempt */ /* Check DCD - debounce it * See Intel Microcommunications Handbook, p2-308 */ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) { lp->tstate = DEFER; tdelay(lp, 100); /* defer until DCD transition or timeout */ wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE); restore_flags(flags); return; } if (random() > lp->persist) { lp->tstate = DEFER; tdelay(lp, lp->slotime); restore_flags(flags); return; } /* Assert RTS early minimize collision window */ wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8); rts(lp, ON); /* Transmitter on */ lp->tstate = ST_TXDELAY; tdelay(lp, lp->txdelay); restore_flags(flags); return; default: break; } /* end switch(lp->state) */ restore_flags(flags); } /*a_txint */
static void setup_rx_dma(struct pt_local *lp) { unsigned long flags; int cmd; unsigned long dma_abs; unsigned char dmachan; save_flags(flags); cli(); dma_abs = (unsigned long) (lp->rcvbuf->data); dmachan = lp->dmachan; cmd = lp->base + CTL; if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf))) panic("PI: RX buffer violates DMA boundary!"); /* Get ready for RX DMA */ wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB); disable_dma(dmachan); clear_dma_ff(dmachan); /* * Set DMA mode register to single transfers, incrementing address, * auto init, writes */ set_dma_mode(dmachan, DMA_MODE_READ | 0x10); set_dma_addr(dmachan, dma_abs); set_dma_count(dmachan, lp->bufsiz); enable_dma(dmachan); /* * If a packet is already coming in, this line is supposed to * avoid receiving a partial packet. */ wrtscc(lp->cardbase, cmd, R0, RES_Rx_CRC); /* Enable RX dma */ wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB); restore_flags(flags); }
/* Pi SIO External/Status interrupts (for the B channel) * This can be caused by a receiver abort, or a Tx UNDERRUN/EOM. * Receiver automatically goes to Hunt on an abort. * * If the Tx Underrun interrupt hits, change state and * issue a reset command for it, and return. */ static void b_exint(struct pi_local *lp) { unsigned long flags; char st; int cmd; char c; cmd = CTL + lp->base; save_flags(flags); cli(); /* disable interrupts */ st = rdscc(lp->cardbase, cmd, R0); /* Fetch status */ /* reset external status latch */ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); switch (lp->tstate) { case ACTIVE: /* Unexpected underrun */ free_p(lp->sndbuf); lp->sndbuf = NULL; wrtscc(lp->cardbase, cmd, R0, SEND_ABORT); lp->tstate = FLAGOUT; lp->stats.tx_errors++; lp->stats.tx_fifo_errors++; tdelay(lp, lp->squeldelay); restore_flags(flags); return; case UNDERRUN: lp->tstate = CRCOUT; restore_flags(flags); return; case FLAGOUT: /* Find a frame for transmission */ if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) { /* Nothing to send - return to receive mode * Tx OFF now - flag should have gone */ rts(lp, OFF); lp->tstate = IDLE; restore_flags(flags); return; } lp->txptr = lp->sndbuf->data; lp->txptr++; /* Ignore KISS control byte */ lp->txcnt = (int) lp->sndbuf->len - 1; /* Get first char to send */ lp->txcnt--; c = *lp->txptr++; wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC); /* reset for next frame */ /* Send abort on underrun */ if (lp->speed) { /* If internally clocked */ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER); } else { wrtscc(lp->cardbase, cmd, R10, CRCPS | ABUNDER); } wrtscc(lp->cardbase, cmd, R8, c); /* First char out now */ wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); /* Reset end of message latch */ #ifdef STUFF2 /* stuff an extra one if we can */ if (lp->txcnt) { lp->txcnt--; c = *lp->txptr++; /* Wait for tx buffer empty */ while((rdscc(lp->cardbase, cmd, R0) & 0x04) == 0) ; wrtscc(lp->cardbase, cmd, R8, c); } #endif /* select transmit interrupts to enable */ wrtscc(lp->cardbase, cmd, R15, TxUIE); /* allow Underrun int only */ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB); /* Tx/Ext ints */ lp->tstate = ACTIVE; /* char going out now */ restore_flags(flags); return; case DEFER: /* Check DCD - debounce it * See Intel Microcommunications Handbook, p2-308 */ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) { lp->tstate = DEFER; tdelay(lp, 100); /* defer until DCD transition or timeout */ wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE); restore_flags(flags); return; } if (random() > lp->persist) { lp->tstate = DEFER; tdelay(lp, lp->slotime); restore_flags(flags); return; } rts(lp, ON); /* Transmitter on */ lp->tstate = ST_TXDELAY; tdelay(lp, lp->txdelay); restore_flags(flags); return; case ST_TXDELAY: /* Get first char to send */ lp->txcnt--; c = *lp->txptr++; wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC); /* reset for next frame */ /* Send abort on underrun */ if (lp->speed) { /* If internally clocked */ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER); } else { wrtscc(lp->cardbase, cmd, R10, CRCPS | ABUNDER); } wrtscc(lp->cardbase, cmd, R8, c); /* First char out now */ wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); /* Reset end of message latch */ #ifdef STUFF2 /* stuff an extra one if we can */ if (lp->txcnt) { lp->txcnt--; c = *lp->txptr++; /* Wait for tx buffer empty */ while((rdscc(lp->cardbase, cmd, R0) & 0x04) == 0) ; wrtscc(lp->cardbase, cmd, R8, c); } #endif /* select transmit interrupts to enable */ wrtscc(lp->cardbase, cmd, R15, TxUIE); /* allow Underrun int only */ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); /* Tx/Extern ints on */ wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB); lp->tstate = ACTIVE; /* char going out now */ restore_flags(flags); return; } /* Receive Mode only * This triggers when hunt mode is entered, & since an ABORT * automatically enters hunt mode, we use that to clean up * any waiting garbage */ if ((lp->rstate == ACTIVE) && (st & BRK_ABRT)) { (void) rdscc(lp->cardbase, cmd, R8); (void) rdscc(lp->cardbase, cmd, R8); (void) rdscc(lp->cardbase, cmd, R8); lp->rcp = lp->rcvbuf->data; lp->rcvbuf->cnt = 0; /* rewind on DCD transition */ } restore_flags(flags); }
static void b_txint(struct pi_local *lp) { unsigned long flags; int cmd; unsigned char c; save_flags(flags); cli(); cmd = CTL + lp->base; switch (lp->tstate) { case CRCOUT: lp->tstate = FLAGOUT; tdelay(lp, lp->squeldelay); restore_flags(flags); return; case IDLE: /* Transmitter idle. Find a frame for transmission */ if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) { /* Nothing to send - return to receive mode * Tx OFF now - flag should have gone */ rts(lp, OFF); restore_flags(flags); return; } lp->txptr = lp->sndbuf->data; lp->txptr++; /* Ignore KISS control byte */ lp->txcnt = (int) lp->sndbuf->len - 1; /* If a buffer to send, we drop thru here */ case DEFER: /* we may have deferred prev xmit attempt */ /* Check DCD - debounce it */ /* See Intel Microcommunications Handbook, p2-308 */ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) { lp->tstate = DEFER; tdelay(lp, 100); /* defer until DCD transition or timeout */ wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE); restore_flags(flags); return; } if (random() > lp->persist) { lp->tstate = DEFER; tdelay(lp, lp->slotime); restore_flags(flags); return; } rts(lp, ON); /* Transmitter on */ lp->tstate = ST_TXDELAY; tdelay(lp, lp->txdelay); restore_flags(flags); return; case ACTIVE: /* Here we are actively sending a frame */ if (lp->txcnt--) { c = *lp->txptr++; /* next char is gone */ wrtscc(lp->cardbase, cmd, R8, c); /* stuffing a char satisfies Interrupt condition */ } else { /* No more to send */ free_p(lp->sndbuf); lp->sndbuf = NULL; if ((rdscc(lp->cardbase, cmd, R0) & 0x40)) { /* Did we underrun? */ /* unexpected underrun */ lp->stats.tx_errors++; lp->stats.tx_fifo_errors++; wrtscc(lp->cardbase, cmd, R0, SEND_ABORT); lp->tstate = FLAGOUT; tdelay(lp, lp->squeldelay); restore_flags(flags); return; } lp->tstate = UNDERRUN; /* Now we expect to underrun */ /* Send flags on underrun */ if (lp->speed) { /* If internally clocked */ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI); } else { wrtscc(lp->cardbase, cmd, R10, CRCPS); } wrtscc(lp->cardbase, cmd, R0, RES_Tx_P); /* reset Tx Int Pend */ } restore_flags(flags); return; /* back to wait for interrupt */ } /* end switch */ restore_flags(flags); }
static void b_rxint(struct device *dev, struct pi_local *lp) { unsigned long flags; int cmd; char rse; struct sk_buff *skb; int sksize; int pkt_len; unsigned char *cfix; save_flags(flags); cli(); /* disable interrupts */ cmd = CTL + lp->base; rse = rdscc(lp->cardbase, cmd, R1); /* get status byte from R1 */ if ((rdscc(lp->cardbase, cmd, R0)) & Rx_CH_AV) { /* there is a char to be stored * read special condition bits before reading the data char */ if (rse & Rx_OVR) { /* Rx overrun - toss buffer */ /* reset buffer pointers */ lp->rcp = lp->rcvbuf->data; lp->rcvbuf->cnt = 0; lp->rstate = RXERROR; /* set error flag */ lp->stats.rx_errors++; lp->stats.rx_over_errors++; } else if (lp->rcvbuf->cnt >= lp->bufsiz) { /* Too large -- toss buffer */ /* reset buffer pointers */ lp->rcp = lp->rcvbuf->data; lp->rcvbuf->cnt = 0; lp->rstate = TOOBIG;/* when set, chars are not stored */ } /* ok, we can store the received character now */ if (lp->rstate == ACTIVE) { /* If no errors... */ *lp->rcp++ = rdscc(lp->cardbase, cmd, R8); /* char to rcv buff */ lp->rcvbuf->cnt++; /* bump count */ } else { /* got to empty FIFO */ (void) rdscc(lp->cardbase, cmd, R8); wrtscc(lp->cardbase, cmd, R0, ERR_RES); /* reset err latch */ lp->rstate = ACTIVE; } } if (rse & END_FR) { /* END OF FRAME -- Make sure Rx was active */ if (lp->rcvbuf->cnt > 0) { if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (lp->rcvbuf->cnt < 10)) { if ((lp->rcvbuf->cnt >= 10) && (rse & CRC_ERR)) { lp->stats.rx_crc_errors++; } lp->rcp = lp->rcvbuf->data; lp->rcvbuf->cnt = 0; } else { /* Here we have a valid frame */ pkt_len = lp->rcvbuf->cnt -= 2; /* Toss 2 crc bytes */ pkt_len += 1; /* Make room for KISS control byte */ /* Malloc up new buffer. */ sksize = pkt_len; skb = dev_alloc_skb(sksize); if (skb == NULL) { printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; restore_flags(flags); return; } skb->dev = dev; /* KISS kludge - prefix with a 0 byte */ cfix=skb_put(skb,pkt_len); *cfix++=0; /* 'skb->data' points to the start of sk_buff data area. */ memcpy(cfix, lp->rcvbuf->data, pkt_len - 1); skb->protocol=ntohs(ETH_P_AX25); skb->mac.raw=skb->data; IS_SKB(skb); netif_rx(skb); lp->stats.rx_packets++; /* packet queued - initialize buffer for next frame */ lp->rcp = lp->rcvbuf->data; lp->rcvbuf->cnt = 0; } /* end good frame queued */ } /* end check for active receive upon EOF */ lp->rstate = ACTIVE; /* and clear error status */ } /* end EOF check */ restore_flags(flags); }
/* Receive interrupt handler for the A channel */ static void a_rxint(struct device *dev, struct pi_local *lp) { unsigned long flags; int cmd; int bytecount; char rse; struct sk_buff *skb; int sksize, pkt_len; struct mbuf *cur_buf; unsigned char *cfix; save_flags(flags); cli(); /* disable interrupts */ cmd = lp->base + CTL; rse = rdscc(lp->cardbase, cmd, R1); /* Get special condition bits from R1 */ if (rse & Rx_OVR) lp->rstate = RXERROR; if (rse & END_FR) { /* If end of frame */ /* figure length of frame from 8237 */ clear_dma_ff(lp->dmachan); bytecount = lp->bufsiz - get_dma_residue(lp->dmachan); if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (bytecount < 10)) { if ((bytecount >= 10) && (rse & CRC_ERR)) { lp->stats.rx_crc_errors++; } if (lp->rstate == RXERROR) { lp->stats.rx_errors++; lp->stats.rx_over_errors++; } /* Reset buffer pointers */ lp->rstate = ACTIVE; setup_rx_dma(lp); } else { /* Here we have a valid frame */ /* Toss 2 crc bytes , add one for KISS */ pkt_len = lp->rcvbuf->cnt = bytecount - 2 + 1; /* Get buffer for next frame */ cur_buf = lp->rcvbuf; switchbuffers(lp); setup_rx_dma(lp); /* Malloc up new buffer. */ sksize = pkt_len; skb = dev_alloc_skb(sksize); if (skb == NULL) { printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; restore_flags(flags); return; } skb->dev = dev; /* KISS kludge - prefix with a 0 byte */ cfix=skb_put(skb,pkt_len); *cfix++=0; /* 'skb->data' points to the start of sk_buff data area. */ memcpy(cfix, (char *) cur_buf->data, pkt_len - 1); skb->protocol=htons(ETH_P_AX25); skb->mac.raw=skb->data; IS_SKB(skb); netif_rx(skb); lp->stats.rx_packets++; } /* end good frame */ } /* end EOF check */ wrtscc(lp->cardbase, lp->base + CTL, R0, ERR_RES); /* error reset */ restore_flags(flags); }
static void a_exint(struct pi_local *lp) { unsigned long flags; int cmd; char st; int length; save_flags(flags); cli(); /* disable interrupts */ st = rdscc(lp->cardbase, lp->base + CTL, R0); /* Fetch status */ /* reset external status latch */ wrtscc(lp->cardbase, CTL + lp->base, R0, RES_EXT_INT); cmd = lp->base + CTL; if ((lp->rstate >= ACTIVE) && (st & BRK_ABRT)) { setup_rx_dma(lp); lp->rstate = ACTIVE; } switch (lp->tstate) { case ACTIVE: free_p(lp->sndbuf); lp->sndbuf = NULL; lp->tstate = FLAGOUT; tdelay(lp, lp->squeldelay); break; case FLAGOUT: if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) { /* Nothing to send - return to receive mode */ lp->tstate = IDLE; rts(lp, OFF); restore_flags(flags); return; } /* NOTE - fall through if more to send */ case ST_TXDELAY: /* Disable DMA chan */ disable_dma(lp->dmachan); /* Set up for TX dma */ wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB); /* Get all chars */ /* Strip KISS control byte */ length = lp->sndbuf->len - 1; memcpy(lp->txdmabuf, &lp->sndbuf->data[1], length); /* Setup DMA controller for tx */ setup_tx_dma(lp, length); /* select transmit interrupts to enable */ /* Allow DMA on chan */ enable_dma(lp->dmachan); /* reset CRC, Txint pend*/ wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC | RES_Tx_P); /* allow Underrun int only */ wrtscc(lp->cardbase, cmd, R15, TxUIE); /* Enable TX DMA */ wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | EXT_INT_ENAB); /* Send CRC on underrun */ wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); /* packet going out now */ lp->tstate = ACTIVE; break; case DEFER: /* we have deferred prev xmit attempt * See Intel Microcommunications Handbook, p2-308 */ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) { lp->tstate = DEFER; tdelay(lp, 100); /* Defer until dcd transition or 100mS timeout */ wrtscc(lp->cardbase, CTL + lp->base, R15, CTSIE | DCDIE); restore_flags(flags); return; } if (random() > lp->persist) { lp->tstate = DEFER; tdelay(lp, lp->slotime); restore_flags(flags); return; } /* Assert RTS early minimize collision window */ wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8); rts(lp, ON); /* Transmitter on */ lp->tstate = ST_TXDELAY; tdelay(lp, lp->txdelay); restore_flags(flags); return; } /* switch(lp->tstate) */ restore_flags(flags); } /* a_exint() */
/* * This sets up all the registers in the SCC for the given channel * based upon tsync_hwint() */ static void scc_init(struct device *dev) { unsigned long flags; struct pt_local *lp = (struct pt_local*) dev->priv; register int cmd = lp->base + CTL; int tc, br; #ifdef PT_DEBUG printk(KERN_DEBUG "PT: scc_init(): (%d).\n", lp->base & CHANA); #endif save_flags(flags); cli(); /* We may put something here to enable_escc */ if (cmd & CHANA) { wrtscc(lp->cardbase, cmd, R9, CHRA); /* Reset channel A */ wrtscc(lp->cardbase, cmd, R2, 0xff); /* Initialise interrupt vector */ } else wrtscc(lp->cardbase, cmd, R9, CHRB); /* Reset channel B */ /* Deselect all Rx and Tx interrupts */ wrtscc(lp->cardbase, cmd, R1, 0); /* Turn off external interrupts (like CTS/CD) */ wrtscc(lp->cardbase, cmd, R15, 0); /* X1 clock, SDLC mode */ wrtscc(lp->cardbase, cmd, R4, SDLC | X1CLK); /* Preset CRC and set mode */ if (lp->nrzi) /* Preset Tx CRC, put into NRZI mode */ wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI); else /* Preset Tx CRC, put into NRZ mode */ wrtscc(lp->cardbase, cmd, R10, CRCPS); /* Tx/Rx parameters */ if (lp->speed) /* Use internal clocking */ /* Tx Clk from BRG. Rx Clk form DPLL, TRxC pin outputs DPLL */ wrtscc(lp->cardbase, cmd, R11, TCBR | RCDPLL | TRxCDP | TRxCOI); else /* Use external clocking */ { /* Tx Clk from TRxCL. Rx Clk from RTxCL, TRxC pin if input */ wrtscc(lp->cardbase, cmd, R11, TCTRxCP | RCRTxCP | TRxCBR); wrtscc(lp->cardbase,cmd, R14, 0); /* wiz1 */ } /* Null out SDLC start address */ wrtscc(lp->cardbase, cmd, R6, 0); /* SDLC flag */ wrtscc(lp->cardbase, cmd, R7, FLAG); /* Setup Tx but don't enable it */ wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR); /* Setup Rx */ wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8); /* Setup the BRG, turn it off first */ wrtscc(lp->cardbase, cmd, R14, BRSRC); /* set the 32x time constant for the BRG in Rx mode */ if (lp->speed) { br = lp->speed; tc = ((lp->xtal / 32) / (br * 2)) - 2; wrtscc(lp->cardbase, cmd, R12, tc & 0xff); /* lower byte */ wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff); /* upper byte */ }