1 /* $NetBSD: siotty.c,v 1.28 2009/10/26 19:16:56 cegger Exp $ */
4 * Copyright (c) 2000 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
34 __KERNEL_RCSID(0, "$NetBSD: siotty.c,v 1.28 2009/10/26 19:16:56 cegger Exp $");
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/device.h>
42 #include <sys/ioctl.h>
46 #include <sys/callout.h>
47 #include <sys/fcntl.h>
49 #include <sys/kauth.h>
51 #include <machine/cpu.h>
53 #include <luna68k/dev/sioreg.h>
54 #include <luna68k/dev/siovar.h>
56 #define TIOCM_BREAK 01000 /* non standard use */
58 static const u_int8_t ch0_regs
[6] = {
59 WR0_RSTINT
, /* reset E/S interrupt */
60 WR1_RXALLS
| WR1_TXENBL
, /* Rx per char, Tx */
62 WR3_RX8BIT
| WR3_RXENBL
, /* Rx */
63 WR4_BAUD96
| WR4_STOP1
, /* Tx/Rx */
64 WR5_TX8BIT
| WR5_TXENBL
| WR5_DTR
| WR5_RTS
, /* Tx */
67 static const struct speedtab siospeedtab
[] = {
68 { 2400, WR4_BAUD24
, },
69 { 4800, WR4_BAUD48
, },
70 { 9600, WR4_BAUD96
, },
77 struct sioreg
*sc_ctl
;
83 static void siostart(struct tty
*);
84 static int sioparam(struct tty
*, struct termios
*);
85 static void siottyintr(int);
86 static int siomctl(struct siotty_softc
*, int, int);
88 static int siotty_match(struct device
*, struct cfdata
*, void *);
89 static void siotty_attach(struct device
*, struct device
*, void *);
91 CFATTACH_DECL(siotty
, sizeof(struct siotty_softc
),
92 siotty_match
, siotty_attach
, NULL
, NULL
);
93 extern struct cfdriver siotty_cd
;
95 dev_type_open(sioopen
);
96 dev_type_close(sioclose
);
97 dev_type_read(sioread
);
98 dev_type_write(siowrite
);
99 dev_type_ioctl(sioioctl
);
100 dev_type_stop(siostop
);
101 dev_type_tty(siotty
);
102 dev_type_poll(siopoll
);
104 const struct cdevsw siotty_cdevsw
= {
105 sioopen
, sioclose
, sioread
, siowrite
, sioioctl
,
106 siostop
, siotty
, siopoll
, nommap
, ttykqfilter
, D_TTY
110 siotty_match(struct device
*parent
, struct cfdata
*cf
, void *aux
)
112 struct sio_attach_args
*args
= aux
;
114 if (args
->channel
!= 0) /* XXX allow tty on Ch.B XXX */
120 siotty_attach(struct device
*parent
, struct device
*self
, void *aux
)
122 struct sio_softc
*scp
= (void *)parent
;
123 struct siotty_softc
*sc
= (void *)self
;
124 struct sio_attach_args
*args
= aux
;
126 sc
->sc_ctl
= (struct sioreg
*)scp
->scp_ctl
+ args
->channel
;
127 memcpy(sc
->sc_wr
, ch0_regs
, sizeof(ch0_regs
));
128 scp
->scp_intr
[args
->channel
] = siottyintr
;
130 if (args
->hwflags
== 1) {
131 printf(" (console)");
132 sc
->sc_flags
= TIOCFLAG_SOFTCAR
;
135 setsioreg(sc
->sc_ctl
, WR0
, WR0_CHANRST
);
136 setsioreg(sc
->sc_ctl
, WR2A
, WR2_VEC86
| WR2_INTR_1
);
137 setsioreg(sc
->sc_ctl
, WR2B
, 0);
138 setsioreg(sc
->sc_ctl
, WR0
, sc
->sc_wr
[WR0
]);
139 setsioreg(sc
->sc_ctl
, WR4
, sc
->sc_wr
[WR4
]);
140 setsioreg(sc
->sc_ctl
, WR3
, sc
->sc_wr
[WR3
]);
141 setsioreg(sc
->sc_ctl
, WR5
, sc
->sc_wr
[WR5
]);
142 setsioreg(sc
->sc_ctl
, WR0
, sc
->sc_wr
[WR0
]);
144 setsioreg(sc
->sc_ctl
, WR1
, sc
->sc_wr
[WR1
]); /* now interrupt driven */
149 /*-------------------- low level routine --------------------*/
154 struct siotty_softc
*sc
;
160 sc
= device_lookup_private(&siotty_cd
, chan
);
169 code
= sio
->sio_data
;
170 if (rr
& (RR_FRAMING
| RR_OVERRUN
| RR_PARITY
)) {
171 sio
->sio_cmd
= WR0_ERRRST
;
172 if (sio
->sio_stat
& RR_FRAMING
)
174 else if (sio
->sio_stat
& RR_PARITY
)
177 if (tp
== NULL
|| (tp
->t_state
& TS_ISOPEN
) == 0)
179 #if 0 && defined(DDB) /* ?!?! fails to resume ?!?! */
180 if ((rr
& RR_BREAK
) && tp
->t_dev
== cn_tab
->cn_dev
) {
185 (*tp
->t_linesw
->l_rint
)(code
, tp
);
186 } while ((rr
= getsiocsr(sio
)) & RR_RXRDY
);
189 sio
->sio_cmd
= WR0_RSTPEND
;
191 tp
->t_state
&= ~(TS_BUSY
|TS_FLUSH
);
192 (*tp
->t_linesw
->l_start
)(tp
);
198 siostart(struct tty
*tp
)
200 struct siotty_softc
*sc
= device_lookup_private(&siotty_cd
,minor(tp
->t_dev
));
204 if (tp
->t_state
& (TS_BUSY
|TS_TIMEOUT
|TS_TTSTOP
))
208 tp
->t_state
|= TS_BUSY
;
209 while (getsiocsr(sc
->sc_ctl
) & RR_TXRDY
) {
210 if ((c
= getc(&tp
->t_outq
)) == -1)
212 sc
->sc_ctl
->sio_data
= c
;
219 siostop(struct tty
*tp
, int flag
)
224 if (TS_BUSY
== (tp
->t_state
& (TS_BUSY
|TS_TTSTOP
))) {
226 * Device is transmitting; must stop it.
228 tp
->t_state
|= TS_FLUSH
;
234 sioparam(struct tty
*tp
, struct termios
*t
)
236 struct siotty_softc
*sc
= device_lookup_private(&siotty_cd
,minor(tp
->t_dev
));
239 if (t
->c_ispeed
&& t
->c_ispeed
!= t
->c_ospeed
)
241 wr4
= ttspeedtab(t
->c_ospeed
, siospeedtab
);
245 if (sc
->sc_flags
& TIOCFLAG_SOFTCAR
) {
246 t
->c_cflag
|= CLOCAL
;
247 t
->c_cflag
&= ~HUPCL
;
249 if (sc
->sc_flags
& TIOCFLAG_CLOCAL
)
250 t
->c_cflag
|= CLOCAL
;
253 * If there were no changes, don't do anything. This avoids dropping
254 * input and improves performance when all we did was frob things like
257 if (tp
->t_ospeed
== t
->c_ospeed
&& tp
->t_cflag
== t
->c_cflag
)
260 tp
->t_ispeed
= t
->c_ispeed
;
261 tp
->t_ospeed
= t
->c_ospeed
;
262 tp
->t_cflag
= t
->c_cflag
;
264 sc
->sc_wr
[WR3
] &= 0x3f;
265 sc
->sc_wr
[WR5
] &= 0x9f;
266 switch (tp
->t_cflag
& CSIZE
) {
268 sc
->sc_wr
[WR3
] |= WR3_RX7BIT
; sc
->sc_wr
[WR5
] |= WR5_TX7BIT
;
271 sc
->sc_wr
[WR3
] |= WR3_RX8BIT
; sc
->sc_wr
[WR5
] |= WR5_TX8BIT
;
274 if (tp
->t_cflag
& PARENB
) {
276 if ((tp
->t_cflag
& PARODD
) == 0)
279 wr4
|= (tp
->t_cflag
& CSTOPB
) ? WR4_STOP2
: WR4_STOP1
;
280 sc
->sc_wr
[WR4
] = wr4
;
283 setsioreg(sc
->sc_ctl
, WR4
, sc
->sc_wr
[WR4
]);
284 setsioreg(sc
->sc_ctl
, WR3
, sc
->sc_wr
[WR3
]);
285 setsioreg(sc
->sc_ctl
, WR5
, sc
->sc_wr
[WR5
]);
292 siomctl(struct siotty_softc
*sc
, int control
, int op
)
297 if (control
& TIOCM_BREAK
)
299 if (control
& TIOCM_DTR
)
301 if (control
& TIOCM_RTS
)
304 wr5
= sc
->sc_wr
[WR5
];
307 wr5
&= ~(WR5_BREAK
|WR5_DTR
|WR5_RTS
);
317 rr
= getsiocsr(sc
->sc_ctl
);
328 sc
->sc_wr
[WR5
] = wr5
;
329 setsioreg(sc
->sc_ctl
, WR5
, wr5
);
336 /*-------------------- cdevsw[] interface --------------------*/
339 sioopen(dev_t dev
, int flag
, int mode
, struct lwp
*l
)
341 struct siotty_softc
*sc
;
345 sc
= device_lookup_private(&siotty_cd
, minor(dev
));
348 if ((tp
= sc
->sc_tty
) == NULL
) {
349 tp
= sc
->sc_tty
= ttymalloc();
353 tp
->t_oproc
= siostart
;
354 tp
->t_param
= sioparam
;
355 tp
->t_hwiflow
= NULL
/* XXX siohwiflow XXX */;
358 if (kauth_authorize_device_tty(l
->l_cred
, KAUTH_DEVICE_TTY_OPEN
, tp
))
361 if ((tp
->t_state
& TS_ISOPEN
) == 0 && tp
->t_wopen
== 0) {
364 t
.c_ispeed
= t
.c_ospeed
= TTYDEF_SPEED
;
365 t
.c_cflag
= TTYDEF_CFLAG
;
366 tp
->t_ospeed
= 0; /* force register update */
367 (void)sioparam(tp
, &t
);
368 tp
->t_iflag
= TTYDEF_IFLAG
;
369 tp
->t_oflag
= TTYDEF_OFLAG
;
370 tp
->t_lflag
= TTYDEF_LFLAG
;
373 /* raise RTS and DTR here; but, DTR lead is not wired */
374 /* then check DCD condition; but, DCD lead is not wired */
375 tp
->t_state
|= TS_CARR_ON
; /* assume detected all the time */
377 if ((sc
->sc_flags
& TIOCFLAG_SOFTCAR
)
378 || (tp
->t_cflag
& MDMBUF
)
379 || (getsiocsr(sc
->sc_ctl
) & RR_DCD
))
380 tp
->t_state
|= TS_CARR_ON
;
382 tp
->t_state
&= ~TS_CARR_ON
;
386 error
= ttyopen(tp
, 0, (flag
& O_NONBLOCK
));
389 return (*tp
->t_linesw
->l_open
)(dev
, tp
);
393 sioclose(dev_t dev
, int flag
, int mode
, struct lwp
*l
)
395 struct siotty_softc
*sc
= device_lookup_private(&siotty_cd
,minor(dev
));
396 struct tty
*tp
= sc
->sc_tty
;
399 (*tp
->t_linesw
->l_close
)(tp
, flag
);
402 siomctl(sc
, TIOCM_BREAK
, DMBIC
);
403 #if 0 /* because unable to feed DTR signal */
404 if ((tp
->t_cflag
& HUPCL
)
405 || tp
->t_wopen
|| (tp
->t_state
& TS_ISOPEN
) == 0) {
406 siomctl(sc
, TIOCM_DTR
, DMBIC
);
407 /* Yield CPU time to others for 1 second, then ... */
408 siomctl(sc
, TIOCM_DTR
, DMBIS
);
416 sioread(dev_t dev
, struct uio
*uio
, int flag
)
418 struct siotty_softc
*sc
= device_lookup_private(&siotty_cd
,minor(dev
));
419 struct tty
*tp
= sc
->sc_tty
;
421 return (*tp
->t_linesw
->l_read
)(tp
, uio
, flag
);
425 siowrite(dev_t dev
, struct uio
*uio
, int flag
)
427 struct siotty_softc
*sc
= device_lookup_private(&siotty_cd
,minor(dev
));
428 struct tty
*tp
= sc
->sc_tty
;
430 return (*tp
->t_linesw
->l_write
)(tp
, uio
, flag
);
434 siopoll(dev_t dev
, int events
, struct lwp
*l
)
436 struct siotty_softc
*sc
= device_lookup_private(&siotty_cd
,minor(dev
));
437 struct tty
*tp
= sc
->sc_tty
;
439 return ((*tp
->t_linesw
->l_poll
)(tp
, events
, l
));
443 sioioctl(dev_t dev
, u_long cmd
, void *data
, int flag
, struct lwp
*l
)
445 struct siotty_softc
*sc
= device_lookup_private(&siotty_cd
,minor(dev
));
446 struct tty
*tp
= sc
->sc_tty
;
449 error
= (*tp
->t_linesw
->l_ioctl
)(tp
, cmd
, data
, flag
, l
);
450 if (error
!= EPASSTHROUGH
)
453 error
= ttioctl(tp
, cmd
, data
, flag
, l
);
454 if (error
!= EPASSTHROUGH
)
457 /* the last resort for TIOC ioctl tranversing */
459 case TIOCSBRK
: /* Set the hardware into BREAK condition */
460 siomctl(sc
, TIOCM_BREAK
, DMBIS
);
462 case TIOCCBRK
: /* Clear the hardware BREAK condition */
463 siomctl(sc
, TIOCM_BREAK
, DMBIC
);
465 case TIOCSDTR
: /* Assert DTR signal */
466 siomctl(sc
, TIOCM_DTR
|TIOCM_RTS
, DMBIS
);
468 case TIOCCDTR
: /* Clear DTR signal */
469 siomctl(sc
, TIOCM_DTR
|TIOCM_RTS
, DMBIC
);
471 case TIOCMSET
: /* Set modem state replacing current one */
472 siomctl(sc
, *(int *)data
, DMSET
);
474 case TIOCMGET
: /* Return current modem state */
475 *(int *)data
= siomctl(sc
, 0, DMGET
);
477 case TIOCMBIS
: /* Set individual bits of modem state */
478 siomctl(sc
, *(int *)data
, DMBIS
);
480 case TIOCMBIC
: /* Clear individual bits of modem state */
481 siomctl(sc
, *(int *)data
, DMBIC
);
483 case TIOCSFLAGS
: /* Instruct how serial port behaves */
484 sc
->sc_flags
= *(int *)data
;
486 case TIOCGFLAGS
: /* Return current serial port state */
487 *(int *)data
= sc
->sc_flags
;
499 struct siotty_softc
*sc
= device_lookup_private(&siotty_cd
,minor(dev
));
504 /*-------------------- miscelleneous routine --------------------*/
507 setsioreg(struct sioreg
*sio
, int regno
, int val
)
510 sio
->sio_cmd
= regno
; /* DELAY(); */
511 sio
->sio_cmd
= val
; /* DELAY(); */
515 getsiocsr(struct sioreg
*sio
)
519 val
= sio
->sio_stat
<< 8; /* DELAY(); */
520 sio
->sio_cmd
= 1; /* DELAY(); */
521 val
|= sio
->sio_stat
; /* DELAY(); */
525 /*--------------------- console interface ----------------------*/
527 void syscnattach(int);
528 int syscngetc(dev_t
);
529 void syscnputc(dev_t
, int);
531 struct consdev syscons
= {
545 syscnattach(int channel
)
548 * Channel A is immediately initialized with 9600N1 right after cold
549 * boot/reset/poweron. ROM monitor emits one line message on CH.A.
552 sio
= (struct sioreg
*)0x51000000 + channel
;
554 syscons
.cn_dev
= makedev(cdevsw_lookup_major(&siotty_cdevsw
),
558 setsioreg(sio
, WR0
, WR0_CHANRST
);
559 setsioreg(sio
, WR2A
, WR2_VEC86
| WR2_INTR_1
);
560 setsioreg(sio
, WR2B
, 0);
561 setsioreg(sio
, WR0
, ch0_regs
[WR0
]);
562 setsioreg(sio
, WR4
, ch0_regs
[WR4
]);
563 setsioreg(sio
, WR3
, ch0_regs
[WR3
]);
564 setsioreg(sio
, WR5
, ch0_regs
[WR5
]);
565 setsioreg(sio
, WR0
, ch0_regs
[WR0
]);
574 sio
= (struct sioreg
*)0x51000000 + ((int)dev
& 0x1);
576 while ((getsiocsr(sio
) & RR_RXRDY
) == 0)
585 syscnputc(dev_t dev
, int c
)
590 sio
= (struct sioreg
*)0x51000000 + ((int)dev
& 0x1);
592 while ((getsiocsr(sio
) & RR_TXRDY
) == 0)
594 sio
->sio_cmd
= WR0_RSTPEND
;