* Copyright (c) 1982, 1986, 1990, 1993
* The Regents of the University of California. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* @(#)dca.c 8.3 (Berkeley) 1/9/95
* Driver for National Semiconductor INS8250/NS16550AF/WD16C552 UARTs.
* 98626/98644/internal serial interface on hp300/hp400
* internal serial ports on hp700
* N.B. On the hp700, there is a "secret bit" with undocumented behavior.
* The third bit of the Modem Control Register (MCR_IEN == 0x08) must be
* set to enable interrupts.
#include <hp/dev/device.h>
#include <hp/dev/dcareg.h>
#include <hp300/hp300/isr.h>
struct driver dcadriver
= {
int dcaparam(), dcaintr();
int dcaconsole
= DCACONSOLE
;
int dcadefaultrate
= TTYDEF_SPEED
;
struct dcadevice
*dca_addr
[NDCA
];
struct tty dca_tty
[NDCA
];
struct speedtab dcaspeedtab
[] = {
#include <machine/remote-sl.h>
extern int kgdb_debug_init
;
register struct hp_device
*hd
;
register struct dcadevice
*dca
;
dca
= (struct dcadevice
*)hd
->hp_addr
;
if (dca
->dca_id
!= DCAID0
&&
dca
->dca_id
!= DCAREMID0
&&
dca
->dca_id
!= DCAREMID1
)
/* look for a NS 16550AF UART with FIFOs */
dca
->dca_fifo
= FIFO_ENABLE
|FIFO_RCV_RST
|FIFO_XMT_RST
|FIFO_TRIGGER_14
;
if ((dca
->dca_iir
& IIR_FIFO_MASK
) == IIR_FIFO_MASK
)
dca_hasfifo
|= 1 << unit
;
hd
->hp_ipl
= DCAIPL(dca
->dca_ic
);
dcaisr
[unit
].isr_ipl
= hd
->hp_ipl
;
dcaisr
[unit
].isr_arg
= unit
;
dcaisr
[unit
].isr_intr
= dcaintr
;
dcasoftCAR
|= (1 << unit
);
if (kgdb_dev
== makedev(dcamajor
, unit
)) {
kgdb_dev
= NODEV
; /* can't debug over console port */
(void) dcainit(unit
, kgdb_rate
);
dcaconsinit
= 1; /* don't re-init in dcaputc */
* Print prefix of device name,
* let kgdb_connect print the rest.
printf("dca%d: kgdb enabled\n", unit
);
* Need to reset baud rate, etc. of next print so reset dcaconsinit.
* Also make sure console is always "hardwired."
if (unit
== dcaconsole
) {
dcasoftCAR
|= (1 << unit
);
dcaopen(dev_t dev
, int flag
, int mode
, struct proc
*p
)
dcaopen(dev
, flag
, mode
, p
)
if (unit
>= NDCA
|| (dca_active
& (1 << unit
)) == 0)
if ((tp
->t_state
& TS_ISOPEN
) == 0) {
tp
->t_iflag
= TTYDEF_IFLAG
;
tp
->t_oflag
= TTYDEF_OFLAG
;
tp
->t_cflag
= TTYDEF_CFLAG
;
tp
->t_lflag
= TTYDEF_LFLAG
;
tp
->t_ispeed
= tp
->t_ospeed
= dcadefaultrate
;
dcaparam(tp
, &tp
->t_termios
);
} else if (tp
->t_state
&TS_XCLUDE
&& p
->p_ucred
->cr_uid
!= 0)
(void) dcamctl(dev
, MCR_DTR
| MCR_RTS
, DMSET
);
if ((dcasoftCAR
& (1 << unit
)) || (dcamctl(dev
, 0, DMGET
) & MSR_DCD
))
tp
->t_state
|= TS_CARR_ON
;
while ((flag
&O_NONBLOCK
) == 0 && (tp
->t_cflag
&CLOCAL
) == 0 &&
(tp
->t_state
& TS_CARR_ON
) == 0) {
if (error
= ttysleep(tp
, (caddr_t
)&tp
->t_rawq
, TTIPRI
| PCATCH
,
error
= (*linesw
[tp
->t_line
].l_open
)(dev
, tp
);
* XXX hack to speed up unbuffered builtin port.
* If dca_fastservice is set, a level 5 interrupt
* will be directed to dcaintr first.
if (error
== 0 && unit
== 0 && (dca_hasfifo
& 1) == 0)
dcaclose(dev
, flag
, mode
, p
)
register struct dcadevice
*dca
;
(*linesw
[tp
->t_line
].l_close
)(tp
, flag
);
dca
->dca_cfcr
&= ~CFCR_SBREAK
;
/* do not disable interrupts if debugging */
if (tp
->t_cflag
&HUPCL
|| tp
->t_state
&TS_WOPEN
||
(tp
->t_state
&TS_ISOPEN
) == 0)
(void) dcamctl(dev
, 0, DMSET
);
register struct tty
*tp
= &dca_tty
[unit
];
error
= (*linesw
[tp
->t_line
].l_read
)(tp
, uio
, flag
);
* XXX hardly a reasonable thing to do, but reporting overflows
* at interrupt time just exacerbates the problem.
if (dcaoflows
[unit
] != of
)
log(LOG_WARNING
, "dca%d: silo overflow\n", unit
);
register struct tty
*tp
= &dca_tty
[unit
];
extern struct tty
*constty
;
* (XXX) We disallow virtual consoles if the physical console is
* a serial port. This is in case there is a display attached that
* is not the console. In that situation we don't need/want the X
* server taking over the console.
if (constty
&& unit
== dcaconsole
)
return ((*linesw
[tp
->t_line
].l_write
)(tp
, uio
, flag
));
register struct dcadevice
*dca
;
if ((dca
->dca_ic
& (IC_IR
|IC_IE
)) != (IC_IR
|IC_IE
))
dcaintrcount
[code
& IIR_IMASK
]++;
switch (code
& IIR_IMASK
) {
/* do time-critical read in-line */
* Process a received byte. Inline for speed...
if ((tp->t_state & TS_ISOPEN) == 0) { \
if (code == FRAME_END && \
kgdb_dev == makedev(dcamajor, unit)) \
kgdb_connect(0); /* trap into kgdb */ \
(*linesw[tp->t_line].l_rint)(code, tp)
if ((tp->t_state & TS_ISOPEN) != 0) \
(*linesw[tp->t_line].l_rint)(code, tp)
if (dca_hasfifo
& (1 << unit
)) {
register int fifocnt
= 1;
while ((code
= dca
->dca_lsr
) & LSR_RCV_MASK
) {
dcaeint(unit
, code
, dca
);
if (!iflowdone
&& (tp
->t_cflag
&CRTS_IFLOW
) &&
tp
->t_rawq
.c_cc
> TTYHOG
/2) {
dca
->dca_mcr
&= ~MCR_RTS
;
tp
->t_state
&=~ (TS_BUSY
|TS_FLUSH
);
(*linesw
[tp
->t_line
].l_start
)(tp
);
dcaeint(unit
, dca
->dca_lsr
, dca
);
log(LOG_WARNING
, "dca%d: weird interrupt: 0x%x\n",
register struct dcadevice
*dca
;
if ((tp
->t_state
& TS_ISOPEN
) == 0) {
/* we don't care about parity errors */
if (((stat
& (LSR_BI
|LSR_FE
|LSR_PE
)) == LSR_PE
) &&
kgdb_dev
== makedev(dcamajor
, unit
) && c
== FRAME_END
)
kgdb_connect(0); /* trap into kgdb */
if (stat
& (LSR_BI
| LSR_FE
))
(*linesw
[tp
->t_line
].l_rint
)(c
, tp
);
register struct dcadevice
*dca
;
dcamintcount
[stat
& 0xf]++;
(dcasoftCAR
& (1 << unit
)) == 0) {
(void)(*linesw
[tp
->t_line
].l_modem
)(tp
, 1);
else if ((*linesw
[tp
->t_line
].l_modem
)(tp
, 0) == 0)
dca
->dca_mcr
&= ~(MCR_DTR
| MCR_RTS
);
* If doing HW output flow control start/stop output as appropriate.
(tp
->t_state
& TS_ISOPEN
) && (tp
->t_cflag
& CCTS_OFLOW
)) {
tp
->t_state
&=~ TS_TTSTOP
;
tp
->t_state
|= TS_TTSTOP
;
dcaioctl(dev
, cmd
, data
, flag
, p
)
register int unit
= UNIT(dev
);
register struct dcadevice
*dca
;
error
= (*linesw
[tp
->t_line
].l_ioctl
)(tp
, cmd
, data
, flag
, p
);
error
= ttioctl(tp
, cmd
, data
, flag
);
dca
->dca_cfcr
|= CFCR_SBREAK
;
dca
->dca_cfcr
&= ~CFCR_SBREAK
;
(void) dcamctl(dev
, MCR_DTR
| MCR_RTS
, DMBIS
);
(void) dcamctl(dev
, MCR_DTR
| MCR_RTS
, DMBIC
);
(void) dcamctl(dev
, *(int *)data
, DMSET
);
(void) dcamctl(dev
, *(int *)data
, DMBIS
);
(void) dcamctl(dev
, *(int *)data
, DMBIC
);
*(int *)data
= dcamctl(dev
, 0, DMGET
);
register struct termios
*t
;
register struct dcadevice
*dca
;
register int cfcr
, cflag
= t
->c_cflag
;
int unit
= UNIT(tp
->t_dev
);
int ospeed
= ttspeedtab(t
->c_ospeed
, dcaspeedtab
);
/* check requested parameters */
if (ospeed
< 0 || (t
->c_ispeed
&& t
->c_ispeed
!= t
->c_ospeed
))
tp
->t_ispeed
= t
->c_ispeed
;
tp
->t_ospeed
= t
->c_ospeed
;
dca
->dca_ier
= IER_ERXRDY
| IER_ETXRDY
| IER_ERLS
| IER_EMSC
;
(void) dcamctl(unit
, 0, DMSET
); /* hang up line */
dca
->dca_cfcr
|= CFCR_DLAB
;
dca
->dca_data
= ospeed
& 0xFF;
dca
->dca_ier
= ospeed
>> 8;
cfcr
= CFCR_5BITS
; break;
cfcr
= CFCR_6BITS
; break;
cfcr
= CFCR_7BITS
; break;
cfcr
= CFCR_8BITS
; break;
if (dca_hasfifo
& (1 << unit
))
dca
->dca_fifo
= FIFO_ENABLE
| FIFO_TRIGGER_14
;
register struct dcadevice
*dca
;
if (tp
->t_state
& (TS_TIMEOUT
|TS_TTSTOP
))
if (tp
->t_outq
.c_cc
<= tp
->t_lowat
) {
if (tp
->t_state
&TS_ASLEEP
) {
tp
->t_state
&= ~TS_ASLEEP
;
wakeup((caddr_t
)&tp
->t_outq
);
if (tp
->t_outq
.c_cc
== 0)
if (dca
->dca_lsr
& LSR_TXRDY
) {
if (dca_hasfifo
& (1 << unit
)) {
for (c
= 1; c
< 16 && tp
->t_outq
.c_cc
; ++c
)
dca
->dca_data
= getc(&tp
->t_outq
);
if (tp
->t_state
& TS_BUSY
) {
if ((tp
->t_state
&TS_TTSTOP
)==0)
register struct dcadevice
*dca
;
* Always make sure MCR_IEN is set (unless setting to 0)
if (how
== DMSET
&& kgdb_dev
== makedev(dcamajor
, unit
))
if (how
== DMBIS
|| (how
== DMSET
&& bits
))
* Following are all routines needed for DCA to act as console
/* locate the major number */
for (dcamajor
= 0; dcamajor
< nchrdev
; dcamajor
++)
if (cdevsw
[dcamajor
].d_open
== dcaopen
)
dca_addr
[CONUNIT
] = (struct dcadevice
*) sctova(CONSCODE
);
/* make sure hardware exists */
if (badaddr((short *)dca_addr
[unit
])) {
dca_addr
[CONUNIT
] = CONPORT
;
/* initialize required fields */
cp
->cn_dev
= makedev(dcamajor
, unit
);
cp
->cn_tp
= &dca_tty
[unit
];
switch (dca_addr
[unit
]->dca_id
) {
* If dcaconsole is initialized, raise our priority.
if (major(kgdb_dev
) == 1) /* XXX */
kgdb_dev
= makedev(dcamajor
, minor(kgdb_dev
));
int unit
= UNIT(cp
->cn_dev
);
dcainit(unit
, dcadefaultrate
);
register struct dcadevice
*dca
;
stat
= unit
; if (stat
) return;
dca
->dca_cfcr
= CFCR_DLAB
;
rate
= ttspeedtab(rate
, dcaspeedtab
);
dca
->dca_data
= rate
& 0xFF;
dca
->dca_ier
= rate
>> 8;
dca
->dca_cfcr
= CFCR_8BITS
;
dca
->dca_ier
= IER_ERXRDY
| IER_ETXRDY
;
dca
->dca_fifo
= FIFO_ENABLE
|FIFO_RCV_RST
|FIFO_XMT_RST
|FIFO_TRIGGER_14
;
register struct dcadevice
*dca
= dca_addr
[UNIT(dev
)];
stat
= dev
; if (stat
) return (0);
while (((stat
= dca
->dca_lsr
) & LSR_RXRDY
) == 0)
* Console kernel output character routine.
register struct dcadevice
*dca
= dca_addr
[UNIT(dev
)];
stat
= dev
; if (stat
) return;
(void) dcainit(UNIT(dev
), dcadefaultrate
);
/* wait for any pending transmission to finish */
while (((stat
= dca
->dca_lsr
) & LSR_TXRDY
) == 0 && --timo
)
/* wait for this transmission to complete */
while (((stat
= dca
->dca_lsr
) & LSR_TXRDY
) == 0 && --timo
)
* If the "normal" interface was busy transfering a character
* we must let our interrupt through to keep things moving.
* Otherwise, we clear the interrupt that we have caused.
if ((dca_tty
[UNIT(dev
)].t_state
& TS_BUSY
) == 0)