summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorscw <scw@NetBSD.org>1999-08-01 09:35:05 +0000
committerscw <scw@NetBSD.org>1999-08-01 09:35:05 +0000
commitba922d31d8cca4c32b979eada456eb1550fedfc6 (patch)
treec8262a7ba4bb399c7aef13f79b45bbe811b3c430 /sys/dev
parent9baa857f5b23453b02bef73034e3b8d12deda477 (diff)
Re-work the Tx side to avoid calling into the tty layer at
interrupt time. This was the cause of the 'mmu fault' panics seen by several people, as the Tx interrupt is at a higher priority that spltty().
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/ic/clmpcc.c167
-rw-r--r--sys/dev/ic/clmpccreg.h5
-rw-r--r--sys/dev/ic/clmpccvar.h27
3 files changed, 109 insertions, 90 deletions
diff --git a/sys/dev/ic/clmpcc.c b/sys/dev/ic/clmpcc.c
index 14929ca9214..5e4e5109b78 100644
--- a/sys/dev/ic/clmpcc.c
+++ b/sys/dev/ic/clmpcc.c
@@ -1,4 +1,4 @@
-/* $NetBSD: clmpcc.c,v 1.5 1999/04/03 13:13:19 scw Exp $ */
+/* $NetBSD: clmpcc.c,v 1.6 1999/08/01 09:35:05 scw Exp $ */
/*-
* Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -126,6 +126,8 @@ integrate u_int8_t clmpcc_rdreg __P((struct clmpcc_softc *, u_int));
integrate void clmpcc_wrreg __P((struct clmpcc_softc *, u_int, u_int));
integrate u_int8_t clmpcc_rdreg_odd __P((struct clmpcc_softc *, u_int));
integrate void clmpcc_wrreg_odd __P((struct clmpcc_softc *, u_int, u_int));
+integrate void clmpcc_wrtx_multi __P((struct clmpcc_softc *, u_int8_t *,
+ u_int));
integrate u_int8_t clmpcc_select_channel __P((struct clmpcc_softc *, u_int));
integrate void clmpcc_channel_cmd __P((struct clmpcc_softc *,int,int));
integrate void clmpcc_enable_transmitter __P((struct clmpcc_chan *));
@@ -191,6 +193,22 @@ clmpcc_wrreg_odd(sc, offset, val)
bus_space_write_1(sc->sc_iot, sc->sc_ioh, offset, val);
}
+integrate void
+clmpcc_wrtx_multi(sc, buff, count)
+ struct clmpcc_softc *sc;
+ u_int8_t *buff;
+ u_int count;
+{
+ u_int offset = CLMPCC_REG_TDR;
+
+#if !defined(CLMPCC_ONLY_BYTESWAP_LOW) && !defined(CLMPCC_ONLY_BYTESWAP_HIGH)
+ offset ^= (sc->sc_byteswap & 2);
+#elif defined(CLMPCC_ONLY_BYTESWAP_HIGH)
+ offset ^= (CLMPCC_BYTESWAP_HIGH & 2);
+#endif
+ bus_space_write_multi_1(sc->sc_iot, sc->sc_ioh, offset, buff, count);
+}
+
integrate u_int8_t
clmpcc_select_channel(sc, new_chan)
struct clmpcc_softc *sc;
@@ -234,13 +252,9 @@ clmpcc_enable_transmitter(ch)
old = clmpcc_select_channel(ch->ch_sc, ch->ch_car);
s = splserial();
-
clmpcc_wrreg(ch->ch_sc, CLMPCC_REG_IER,
clmpcc_rdreg(ch->ch_sc, CLMPCC_REG_IER) | CLMPCC_IER_TX_EMPTY);
-
- CLR(ch->ch_flags, CLMPCC_FLG_START);
SET(ch->ch_tty->t_state, TS_BUSY);
-
splx(s);
clmpcc_select_channel(ch->ch_sc, old);
@@ -1007,28 +1021,34 @@ clmpcc_start(tp)
{
struct clmpcc_softc *sc = clmpcc_cd.cd_devs[CLMPCCUNIT(tp->t_dev)];
struct clmpcc_chan *ch = &sc->sc_chans[CLMPCCCHAN(tp->t_dev)];
+ u_int oldch;
int s;
s = spltty();
- if ( ISCLR(tp->t_state, TS_TTSTOP | TS_TIMEOUT | TS_BUSY) &&
- ISCLR(ch->ch_flags, CLMPCC_FLG_STOP) ) {
+ if ( ISCLR(tp->t_state, TS_TTSTOP | TS_TIMEOUT | TS_BUSY) ) {
if ( tp->t_outq.c_cc <= tp->t_lowat ) {
if ( ISSET(tp->t_state, TS_ASLEEP) ) {
CLR(tp->t_state, TS_ASLEEP);
wakeup(&tp->t_outq);
}
selwakeup(&tp->t_wsel);
+ }
- if ( tp->t_outq.c_cc == 0 )
- goto out;
+ if ( tp->t_outq.c_cc > 0 ) {
+ ch->ch_obuf_addr = tp->t_outq.c_cf;
+ ch->ch_obuf_size = ndqb(&tp->t_outq, 0);
+
+ /* Enable TX empty interrupts */
+ oldch = clmpcc_select_channel(ch->ch_sc, ch->ch_car);
+ clmpcc_wrreg(ch->ch_sc, CLMPCC_REG_IER,
+ clmpcc_rdreg(ch->ch_sc, CLMPCC_REG_IER) |
+ CLMPCC_IER_TX_EMPTY);
+ clmpcc_select_channel(ch->ch_sc, oldch);
+ SET(tp->t_state, TS_BUSY);
}
- SET(tp->t_state, TS_BUSY);
- clmpcc_enable_transmitter(ch);
}
-out:
- CLR(ch->ch_flags, CLMPCC_FLG_START);
splx(s);
}
@@ -1044,17 +1064,12 @@ clmpccstop(tp, flag)
struct clmpcc_chan *ch = &sc->sc_chans[CLMPCCCHAN(tp->t_dev)];
int s;
- s = spltty();
+ s = splserial();
if ( ISSET(tp->t_state, TS_BUSY) ) {
if ( ISCLR(tp->t_state, TS_TTSTOP) )
SET(tp->t_state, TS_FLUSH);
-
- /*
- * The transmit interrupt routine will disable transmit when it
- * notices that CLMPCC_FLG_STOP has been set.
- */
- SET(ch->ch_flags, CLMPCC_FLG_STOP);
+ ch->ch_obuf_size = 0;
}
splx(s);
}
@@ -1247,62 +1262,48 @@ clmpcc_txintr(arg)
/* Handle a delayed parameter change */
if ( ISSET(ch->ch_flags, CLMPCC_FLG_UPDATE_PARMS) ) {
- clmpcc_set_params(ch);
CLR(ch->ch_flags, CLMPCC_FLG_UPDATE_PARMS);
- SET(ch->ch_flags, CLMPCC_FLG_START);
- goto tx_done;
+ clmpcc_set_params(ch);
}
- /* Stop transmitting if CLMPCC_FLG_STOP is set */
- if ( ISSET(ch->ch_flags, CLMPCC_FLG_STOP) )
- goto tx_done;
+ if ( ch->ch_obuf_size > 0 ) {
+ u_int n = min(ch->ch_obuf_size, ftc);
- CLR(ch->ch_flags, CLMPCC_FLG_UPDATE_PARMS);
+ clmpcc_wrtx_multi(sc, ch->ch_obuf_addr, n);
- if ( tp->t_outq.c_cc > 0 ) {
- SET(tp->t_state, TS_BUSY);
- while (tp->t_outq.c_cc > 0 && ftc > 0 ) {
- clmpcc_wr_txdata(sc, getc(&tp->t_outq));
- ftc--;
- }
+ ftc -= n;
+ ch->ch_obuf_size -= n;
+ ch->ch_obuf_addr += n;
} else {
/*
* No data to send -- check if we should
* start/stop a break
*/
- /*
- * XXX does this cause too much delay before
- * breaks?
- */
if ( ISSET(ch->ch_flags, CLMPCC_FLG_START_BREAK) ) {
CLR(ch->ch_flags, CLMPCC_FLG_START_BREAK);
+ /* TBD */
}
if ( ISSET(ch->ch_flags, CLMPCC_FLG_END_BREAK) ) {
CLR(ch->ch_flags, CLMPCC_FLG_END_BREAK);
+ /* TBD */
}
- }
- if ( tp->t_outq.c_cc == 0 ) {
-tx_done:
/*
- * No data to send, requested to stop or waiting for
- * an INIT following a parameter change.
* Disable transmit interrupt
*/
clmpcc_wrreg(ch->ch_sc, CLMPCC_REG_IER,
clmpcc_rdreg(ch->ch_sc, CLMPCC_REG_IER) &
~CLMPCC_IER_TX_EMPTY);
- CLR(ch->ch_flags, CLMPCC_FLG_STOP);
- CLR(tp->t_state, TS_BUSY);
- }
-
- if ( tp->t_outq.c_cc <= tp->t_lowat )
- SET(ch->ch_flags, CLMPCC_FLG_START);
- if ( ISSET(ch->ch_flags, CLMPCC_FLG_START) && ! sc->sc_soft_running ) {
- sc->sc_soft_running = 1;
- (sc->sc_softhook)(sc);
+ /*
+ * Request Tx processing in the soft interrupt handler
+ */
+ ch->ch_tx_done = 1;
+ if ( ! sc->sc_soft_running ) {
+ sc->sc_soft_running = 1;
+ (sc->sc_softhook)(sc);
+ }
}
if ( ftc != oftc )
@@ -1365,7 +1366,6 @@ clmpcc_softintr(arg)
sc->sc_soft_running = 0;
-
/* Handle Modem state changes too... */
for (chan = 0; chan < CLMPCC_NUM_CHANS; chan++) {
@@ -1380,8 +1380,8 @@ clmpcc_softintr(arg)
c = get[0];
c |= ((u_int)get[1]) << 8;
if ( (rint)(c, tp) == -1 ) {
- ch->ch_ibuf_rd = ch->ch_ibuf_wr;
- break;
+ ch->ch_ibuf_rd = ch->ch_ibuf_wr;
+ break;
}
get += 2;
@@ -1391,31 +1391,46 @@ clmpcc_softintr(arg)
ch->ch_ibuf_rd = get;
}
- if ( ISSET(ch->ch_flags, CLMPCC_FLG_NEED_INIT) ) {
- clmpcc_channel_cmd(sc, ch->ch_car, CLMPCC_CCR_T0_INIT |
- CLMPCC_CCR_T0_RX_EN |
- CLMPCC_CCR_T0_TX_EN);
- CLR(ch->ch_flags, CLMPCC_FLG_NEED_INIT);
-
- /*
- * Allow time for the channel to initialise.
- * (Empirically derived duration; the must be
- * another way to determine the command
- * has completed without busy-waiting...)
- */
- delay(800);
+ /*
+ * Is the transmitter idle and in need of attention?
+ */
+ if ( ch->ch_tx_done ) {
+ ch->ch_tx_done = 0;
+
+ if ( ISSET(ch->ch_flags, CLMPCC_FLG_NEED_INIT) ) {
+ clmpcc_channel_cmd(sc, ch->ch_car,
+ CLMPCC_CCR_T0_INIT |
+ CLMPCC_CCR_T0_RX_EN |
+ CLMPCC_CCR_T0_TX_EN);
+ CLR(ch->ch_flags, CLMPCC_FLG_NEED_INIT);
+
+ /*
+ * Allow time for the channel to initialise.
+ * (Empirically derived duration; there must
+ * be another way to determine the command
+ * has completed without busy-waiting...)
+ */
+ delay(800);
+
+ /*
+ * Update the tty layer's idea of the carrier
+ * bit, in case we changed CLOCAL or MDMBUF.
+ * We don't hang up here; we only do that by
+ * explicit request.
+ */
+ reg = clmpcc_rd_msvr(sc) & CLMPCC_MSVR_CD;
+ (*linesw[tp->t_line].l_modem)(tp, reg != 0);
+ }
- /*
- * Update the tty layer's idea of the carrier bit,
- * in case we changed CLOCAL or MDMBUF. We don't
- * hang up here; we only do that by explicit request.
- */
- reg = clmpcc_rd_msvr(sc) & CLMPCC_MSVR_CD;
- (void) (*linesw[tp->t_line].l_modem)(tp, reg != 0);
- }
+ CLR(tp->t_state, TS_BUSY);
+ if ( ISSET(tp->t_state, TS_FLUSH) )
+ CLR(tp->t_state, TS_FLUSH);
+ else
+ ndflush(&tp->t_outq,
+ (int)(ch->ch_obuf_addr - tp->t_outq.c_cf));
- if ( ISSET(ch->ch_flags, CLMPCC_FLG_START) )
(*linesw[tp->t_line].l_start)(tp);
+ }
}
return 0;
diff --git a/sys/dev/ic/clmpccreg.h b/sys/dev/ic/clmpccreg.h
index cda2d09cdd5..d8baf75e6e5 100644
--- a/sys/dev/ic/clmpccreg.h
+++ b/sys/dev/ic/clmpccreg.h
@@ -1,4 +1,4 @@
-/* $NetBSD: clmpccreg.h,v 1.1 1999/02/13 17:05:20 scw Exp $ */
+/* $NetBSD: clmpccreg.h,v 1.2 1999/08/01 09:35:05 scw Exp $ */
/*-
* Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -55,6 +55,9 @@
* <dev/ic/clmpccvar.h>.
*/
+/* Number of bytes of FIFO (Rx & Tx) */
+#define CLMPCC_FIFO_DEPTH 16
+
/* Global Registers */
#define CLMPCC_REG_GFRCR 0x81 /* Global Firmware Revision Code Register */
#define CLMPCC_REG_CAR 0xee /* Channel Access Register */
diff --git a/sys/dev/ic/clmpccvar.h b/sys/dev/ic/clmpccvar.h
index 9e7c902540b..17799058497 100644
--- a/sys/dev/ic/clmpccvar.h
+++ b/sys/dev/ic/clmpccvar.h
@@ -1,4 +1,4 @@
-/* $NetBSD: clmpccvar.h,v 1.2 1999/02/20 00:27:30 scw Exp $ */
+/* $NetBSD: clmpccvar.h,v 1.3 1999/08/01 09:35:05 scw Exp $ */
/*-
* Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -62,16 +62,15 @@ struct clmpcc_chan {
struct clmpcc_softc *ch_sc; /* Pointer to chip's softc structure */
u_char ch_car; /* Channel number (CD2400_REG_CAR) */
u_char ch_openflags; /* Persistant TIOC flags */
- u_short ch_flags; /* Various channel-specific flags */
+ volatile u_short ch_flags; /* Various channel-specific flags */
#define CLMPCC_FLG_IS_CONSOLE 0x0001 /* Channel is system console */
-#define CLMPCC_FLG_CARRIER_CHNG 0x0002
-#define CLMPCC_FLG_START_BREAK 0x0004
-#define CLMPCC_FLG_END_BREAK 0x0008
-#define CLMPCC_FLG_START 0x0010
-#define CLMPCC_FLG_STOP 0x0020
-#define CLMPCC_FLG_FIFO_CLEAR 0x0040
-#define CLMPCC_FLG_UPDATE_PARMS 0x0080
-#define CLMPCC_FLG_NEED_INIT 0x0100
+#define CLMPCC_FLG_START_BREAK 0x0002
+#define CLMPCC_FLG_END_BREAK 0x0004
+#define CLMPCC_FLG_FIFO_CLEAR 0x0008
+#define CLMPCC_FLG_UPDATE_PARMS 0x0010
+#define CLMPCC_FLG_NEED_INIT 0x0020
+
+ u_char ch_tx_done;
u_char ch_control;
@@ -90,6 +89,9 @@ struct clmpcc_chan {
u_int8_t *ch_ibuf_end; /* End of input ring buffer */
u_int8_t *ch_ibuf_rd; /* Input buffer tail (reader) */
u_int8_t *ch_ibuf_wr; /* Input buffer head (writer) */
+
+ u_int8_t *ch_obuf_addr; /* Output buffer address */
+ u_int ch_obuf_size; /* Output buffer size (in bytes) */
};
@@ -98,11 +100,10 @@ struct clmpcc_softc {
/*
* The bus/MD-specific attachment code must initialise the
- * following four fields before calling 'clmpcc_attach_subr()'.
+ * following fields before calling 'clmpcc_attach_subr()'.
*/
bus_space_tag_t sc_iot; /* Tag for parent bus */
bus_space_handle_t sc_ioh; /* Handle for chip's regs */
-
void *sc_data; /* MD-specific data */
int sc_clk; /* Clock-rate, in Hz */
u_char sc_vector_base; /* Vector base reg, or 0 for auto */
@@ -123,7 +124,7 @@ struct clmpcc_softc {
/*
* No user-serviceable parts below
*/
- int sc_soft_running;
+ volatile int sc_soft_running;
struct clmpcc_chan sc_chans[CLMPCC_NUM_CHANS];
};