1 /* $NetBSD: xlcom.c,v 1.6 2008/01/09 08:15:53 elad Exp $ */
4 * Copyright (c) 2006 Jachym Holecek
7 * Written for DFC Design, s.r.o.
9 * Redistribution and use in source and binary forms, with or without
10 * 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.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: xlcom.c,v 1.6 2008/01/09 08:15:53 elad Exp $");
37 #include <sys/param.h>
38 #include <sys/systm.h>
40 #include <sys/device.h>
42 #include <sys/ioctl.h>
43 #include <sys/kauth.h>
44 #include <sys/kernel.h>
48 #include <sys/syslog.h>
58 #include <evbppc/virtex/virtex.h>
59 #include <evbppc/virtex/dev/xcvbusvar.h>
60 #include <evbppc/virtex/dev/xlcomreg.h>
63 #define XLCOM_UNIT_MASK 0x7f
64 #define XLCOM_DIALOUT_MASK 0x80
66 #define UNIT(dev) (minor(dev) & XLCOM_UNIT_MASK)
67 #define DIALOUT(dev) (minor(dev) & XLCOM_DIALOUT_MASK)
69 #define XLCOM_CHAR_PE 0x8000 /* Parity error flag */
70 #define XLCOM_CHAR_FE 0x4000 /* Frame error flag */
71 #define next(idx) (void)((idx) = ((idx) + 1) % XLCOM_RXBUF_SIZE)
73 #define XLCOM_RXBUF_SIZE 1024
80 bus_space_tag_t sc_iot
;
81 bus_space_handle_t sc_ioh
;
83 /* Deffered execution context. */
88 u_short sc_rbuf
[XLCOM_RXBUF_SIZE
];
89 volatile u_int sc_rput
;
90 volatile u_int sc_rget
;
91 volatile u_int sc_ravail
;
98 static int xlcom_intr(void *);
99 static void xlcom_rx_soft(void *);
100 static void xlcom_tx_soft(void *);
101 static void xlcom_reset(bus_space_tag_t
, bus_space_handle_t
);
102 static int xlcom_busy_getc(bus_space_tag_t
, bus_space_handle_t
);
103 static void xlcom_busy_putc(bus_space_tag_t
, bus_space_handle_t
, int);
105 /* System console interface. */
106 static int xlcom_cngetc(dev_t
);
107 static void xlcom_cnputc(dev_t
, int);
108 void xlcom_cninit(struct consdev
*);
112 void xlcom_kgdbinit(void);
113 static void xlcom_kgdb_putc(void *, int);
114 static int xlcom_kgdb_getc(void *);
118 static struct cnm_state xlcom_cnm_state
;
120 struct consdev consdev_xlcom
= {
121 .cn_probe
= nullcnprobe
,
122 .cn_init
= xlcom_cninit
,
123 .cn_getc
= xlcom_cngetc
,
124 .cn_putc
= xlcom_cnputc
,
125 .cn_pollc
= nullcnpollc
,
129 /* Character device. */
130 static dev_type_open(xlcom_open
);
131 static dev_type_read(xlcom_read
);
132 static dev_type_write(xlcom_write
);
133 static dev_type_ioctl(xlcom_ioctl
);
134 static dev_type_poll(xlcom_poll
);
135 static dev_type_close(xlcom_close
);
137 static dev_type_tty(xlcom_tty
);
138 static dev_type_stop(xlcom_stop
);
140 const struct cdevsw xlcom_cdevsw
= {
141 xlcom_open
, xlcom_close
, xlcom_read
, xlcom_write
, xlcom_ioctl
,
142 xlcom_stop
, xlcom_tty
, xlcom_poll
, nommap
, ttykqfilter
, D_TTY
145 extern struct cfdriver xlcom_cd
;
148 static int xlcom_param(struct tty
*, struct termios
*);
149 static void xlcom_start(struct tty
*);
151 /* Generic device. */
152 static void xlcom_attach(struct device
*, struct device
*, void *);
154 CFATTACH_DECL(xlcom
, sizeof(struct xlcom_softc
),
155 xcvbus_child_match
, xlcom_attach
, NULL
, NULL
);
159 xlcom_attach(struct device
*parent
, struct device
*self
, void *aux
)
161 struct xcvbus_attach_args
*vaa
= aux
;
162 struct xlcom_softc
*sc
= (struct xlcom_softc
*)self
;
166 printf(": UartLite serial port\n");
169 /* We don't want to share kgdb port with the user. */
170 if (sc
->sc_iot
== kgdb_iot
&& sc
->sc_ioh
== kgdb_ioh
) {
171 printf("%s: already in use by kgdb\n", device_xname(self
));
176 if ((sc
->sc_ih
= intr_establish(vaa
->vaa_intr
, IST_LEVEL
, IPL_SERIAL
,
177 xlcom_intr
, sc
)) == NULL
) {
178 printf("%s: could not establish interrupt\n",
183 dev
= makedev(cdevsw_lookup_major(&xlcom_cdevsw
), device_unit(self
));
184 if (cn_tab
== &consdev_xlcom
) {
185 cn_init_magic(&xlcom_cnm_state
);
186 cn_set_magic("\x23\x2e"); /* #. */
187 cn_tab
->cn_dev
= dev
;
189 sc
->sc_iot
= consdev_iot
;
190 sc
->sc_ioh
= consdev_ioh
;
192 printf("%s: console\n", sc
->sc_dev
.dv_xname
);
194 sc
->sc_iot
= vaa
->vaa_iot
;
196 if (bus_space_map(vaa
->vaa_iot
, vaa
->vaa_addr
, XLCOM_SIZE
, 0,
198 printf("%s: could not map registers\n",
204 xlcom_reset(sc
->sc_iot
, sc
->sc_ioh
);
210 sc
->sc_rput
= sc
->sc_rget
= 0;
211 sc
->sc_ravail
= XLCOM_RXBUF_SIZE
;
213 sc
->sc_rx_soft
= softint_establish(SOFTINT_SERIAL
, xlcom_rx_soft
, sc
);
214 sc
->sc_tx_soft
= softint_establish(SOFTINT_SERIAL
, xlcom_tx_soft
, sc
);
216 if (sc
->sc_rx_soft
== NULL
|| sc
->sc_tx_soft
== NULL
) {
217 printf("%s: could not establish Rx or Tx softintr\n",
218 sc
->sc_dev
.dv_xname
);
224 tp
->t_oproc
= xlcom_start
;
225 tp
->t_param
= xlcom_param
;
226 tp
->t_hwiflow
= NULL
; /* No HW flow control */
229 /* XXX anything else to do for console early? */
230 if (cn_tab
== &consdev_xlcom
) {
231 /* Before first open, so that we can enter ddb(4). */
232 bus_space_write_4(sc
->sc_iot
, sc
->sc_ioh
, XLCOM_CNTL
,
243 xlcom_tx_soft(void *arg
)
245 struct xlcom_softc
*sc
= (struct xlcom_softc
*)arg
;
246 struct tty
*tp
= sc
->sc_tty
;
248 if (tp
->t_state
& TS_FLUSH
)
249 tp
->t_state
&= ~TS_FLUSH
;
252 (int)(sc
->sc_tba
- tp
->t_outq
.c_cf
));
253 (tp
->t_linesw
->l_start
)(tp
);
257 xlcom_rx_soft(void *arg
)
259 struct xlcom_softc
*sc
= (struct xlcom_softc
*)arg
;
260 struct tty
*tp
= sc
->sc_tty
;
261 int (*rint
)(int, struct tty
*);
266 * XXX: we don't do any synchronization, rput may change below
267 * XXX: our hands -- it doesn't seem to be troublesome as long
268 * XXX: as "sc->sc_rget = sc->sc_rput" is atomic.
270 rint
= tp
->t_linesw
->l_rint
;
272 /* Run until we catch our tail. */
273 while (sc
->sc_rput
!= sc
->sc_rget
) {
274 c
= sc
->sc_rbuf
[sc
->sc_rget
];
280 ((c
& XLCOM_CHAR_PE
) != 0 ? TTY_PE
: 0) |
281 ((c
& XLCOM_CHAR_FE
) != 0 ? TTY_FE
: 0);
284 * Drop the rest of data if discipline runs out of buffer
285 * space. We'd use flow control here, if we had any.
287 if ((rint
)(d
, tp
) == -1) {
288 sc
->sc_rget
= sc
->sc_rput
;
295 xlcom_send_chunk(struct xlcom_softc
*sc
)
299 /* Chunk flushed, no more data available. */
300 if (sc
->sc_tbc
<= 0) {
304 /* Run as long as we have space and data. */
305 while (sc
->sc_tbc
> 0) {
306 stat
= bus_space_read_4(sc
->sc_iot
, sc
->sc_ioh
, XLCOM_STAT
);
307 if (stat
& STAT_TX_FULL
)
310 bus_space_write_4(sc
->sc_iot
, sc
->sc_ioh
, XLCOM_TX_FIFO
,
317 /* Try to grab more data while FIFO drains. */
318 if (sc
->sc_tbc
== 0) {
319 sc
->sc_tty
->t_state
&= ~TS_BUSY
;
320 softint_schedule(sc
->sc_tx_soft
);
325 xlcom_recv_chunk(struct xlcom_softc
*sc
)
332 stat
= bus_space_read_4(sc
->sc_iot
, sc
->sc_ioh
, XLCOM_STAT
);
334 /* Run as long as we have data and space. */
335 while ((stat
& STAT_RX_DATA
) != 0 && sc
->sc_ravail
> 0) {
336 c
= bus_space_read_4(sc
->sc_iot
, sc
->sc_ioh
, XLCOM_RX_FIFO
);
338 cn_check_magic(sc
->sc_tty
->t_dev
, c
, xlcom_cnm_state
);
340 /* XXX: Should we pass rx-overrun upstream too? */
341 c
|= ((stat
& STAT_PARITY_ERR
) != 0 ? XLCOM_CHAR_PE
: 0) |
342 ((stat
& STAT_FRAME_ERR
) != 0 ? XLCOM_CHAR_FE
: 0);
343 sc
->sc_rbuf
[sc
->sc_rput
] = c
;
347 stat
= bus_space_read_4(sc
->sc_iot
, sc
->sc_ioh
, XLCOM_STAT
);
350 /* Shedule completion hook if we received any. */
351 if (n
!= sc
->sc_ravail
)
352 softint_schedule(sc
->sc_rx_soft
);
356 xlcom_intr(void *arg
)
358 struct xlcom_softc
*sc
= arg
;
362 * Xilinx DS422, "OPB UART Lite v1.00b"
364 * If interrupts are enabled, an interrupt is generated when one
365 * of the following conditions is true:
367 stat
= bus_space_read_4(sc
->sc_iot
, sc
->sc_ioh
, XLCOM_STAT
);
370 * 1. When there exists any valid character in the receive FIFO,
371 * the interrupt stays active until the receive FIFO is empty.
372 * This is a level interrupt.
374 if (stat
& STAT_RX_DATA
)
375 xlcom_recv_chunk(sc
);
378 * 2. When the transmit FIFO goes from not empty to empty, such
379 * as when the last character in the transmit FIFO is transmitted,
380 * the interrupt is only active one clock cycle. This is an
383 if (stat
& STAT_TX_EMPTY
)
384 xlcom_send_chunk(sc
);
393 xlcom_open(dev_t dev
, int flags
, int mode
, struct lwp
*l
)
395 struct xlcom_softc
*sc
;
399 sc
= device_lookup_private(&xlcom_cd
, minor(dev
));
405 s
= spltty(); /* { */
407 if (kauth_authorize_device_tty(l
->l_cred
, KAUTH_DEVICE_TTY_OPEN
,
413 /* Is this the first open? */
414 if (!(tp
->t_state
& TS_ISOPEN
) && tp
->t_wopen
== 0) {
417 /* Values hardwired at synthesis time. XXXFreza: xparam.h */
418 tp
->t_ispeed
= tp
->t_ospeed
= B38400
;
419 tp
->t_cflag
= CLOCAL
| CREAD
| CS8
;
420 tp
->t_iflag
= TTYDEF_IFLAG
;
421 tp
->t_oflag
= TTYDEF_OFLAG
;
422 tp
->t_lflag
= TTYDEF_LFLAG
;
427 /* Enable interrupt. */
428 bus_space_write_4(sc
->sc_iot
, sc
->sc_ioh
, XLCOM_CNTL
,
432 error
= ttyopen(tp
, DIALOUT(dev
), (flags
& O_NONBLOCK
));
436 error
= (tp
->t_linesw
->l_open
)(dev
, tp
);
444 /* XXXFreza: Shutdown if nobody else has the device open. */
450 xlcom_read(dev_t dev
, struct uio
*uio
, int flag
)
452 struct xlcom_softc
*sc
;
455 sc
= device_lookup_private(&xlcom_cd
, minor(dev
));
460 return (tp
->t_linesw
->l_read
)(tp
, uio
, flag
);
464 xlcom_write(dev_t dev
, struct uio
*uio
, int flag
)
466 struct xlcom_softc
*sc
;
469 sc
= device_lookup_private(&xlcom_cd
, minor(dev
));
474 return (tp
->t_linesw
->l_write
)(tp
, uio
, flag
);
478 xlcom_poll(dev_t dev
, int events
, struct lwp
*l
)
480 struct xlcom_softc
*sc
;
483 sc
= device_lookup_private(&xlcom_cd
, minor(dev
));
488 return (tp
->t_linesw
->l_poll
)(tp
, events
, l
);
494 struct xlcom_softc
*sc
;
497 sc
= device_lookup_private(&xlcom_cd
, minor(dev
));
506 xlcom_ioctl(dev_t dev
, u_long cmd
, void *data
, int flag
, struct lwp
*l
)
508 struct xlcom_softc
*sc
;
512 sc
= device_lookup_private(&xlcom_cd
, minor(dev
));
517 error
= (*tp
->t_linesw
->l_ioctl
)(tp
, cmd
, data
, flag
, l
);
518 if (error
!= EPASSTHROUGH
)
521 error
= ttioctl(tp
, cmd
, data
, flag
, l
);
522 if (error
!= EPASSTHROUGH
)
525 /* XXXFreza: error = 0; switch (cmd); return error; */
531 xlcom_close(dev_t dev
, int flag
, int mode
, struct lwp
*l
)
533 struct xlcom_softc
*sc
;
536 sc
= device_lookup_private(&xlcom_cd
, minor(dev
));
541 (tp
->t_linesw
->l_close
)(tp
, flag
);
544 /* Is this the last close? XXXFreza: hum? */
545 if (!(tp
->t_state
& TS_ISOPEN
) && tp
->t_wopen
== 0) {
552 xlcom_stop(struct tty
*tp
, int flag
)
554 struct xlcom_softc
*sc
;
557 sc
= device_lookup_private(&xlcom_cd
, UNIT(tp
->t_dev
));
562 if (tp
->t_state
& TS_BUSY
) {
563 /* XXXFreza: make sure we stop xmitting at next chunk */
565 if (! (tp
->t_state
& TS_TTSTOP
))
566 tp
->t_state
|= TS_FLUSH
;
575 xlcom_param(struct tty
*tp
, struct termios
*t
)
577 t
->c_cflag
&= ~HUPCL
;
579 if (tp
->t_ospeed
== t
->c_ospeed
&&
580 tp
->t_ispeed
== t
->c_ispeed
&&
581 tp
->t_cflag
== t
->c_cflag
)
588 xlcom_start(struct tty
*tp
)
590 struct xlcom_softc
*sc
;
593 sc
= device_lookup_private(&xlcom_cd
, UNIT(tp
->t_dev
));
599 if (tp
->t_state
& (TS_BUSY
| TS_TIMEOUT
| TS_TTSTOP
)) {
609 tp
->t_state
|= TS_BUSY
;
613 sc
->sc_tba
= tp
->t_outq
.c_cf
;
614 sc
->sc_tbc
= ndqb(&tp
->t_outq
, 0);
615 xlcom_send_chunk(sc
);
620 xlcom_reset(bus_space_tag_t iot
, bus_space_handle_t ioh
)
622 /* Wait while the fifo drains. */
623 while (! (bus_space_read_4(iot
, ioh
, XLCOM_STAT
) & STAT_TX_EMPTY
))
626 bus_space_write_4(iot
, ioh
, XLCOM_CNTL
, CNTL_RX_CLEAR
| CNTL_TX_CLEAR
);
630 xlcom_busy_getc(bus_space_tag_t t
, bus_space_handle_t h
)
632 while (! (bus_space_read_4(t
, h
, XLCOM_STAT
) & STAT_RX_DATA
))
635 return (bus_space_read_4(t
, h
, XLCOM_RX_FIFO
));
639 xlcom_busy_putc(bus_space_tag_t t
, bus_space_handle_t h
, int c
)
641 while (bus_space_read_4(t
, h
, XLCOM_STAT
) & STAT_TX_FULL
)
644 bus_space_write_4(t
, h
, XLCOM_TX_FIFO
, c
);
648 * Console on UartLite.
651 nullcnprobe(struct consdev
*cn
)
656 xlcom_cninit(struct consdev
*cn
)
658 if (bus_space_map(consdev_iot
, CONS_ADDR
, XLCOM_SIZE
, 0, &consdev_ioh
))
659 panic("xlcom_cninit: could not map consdev_ioh");
661 xlcom_reset(consdev_iot
, consdev_ioh
);
665 xlcom_cngetc(dev_t dev
)
667 return (xlcom_busy_getc(consdev_iot
, consdev_ioh
));
671 xlcom_cnputc(dev_t dev
, int c
)
673 xlcom_busy_putc(consdev_iot
, consdev_ioh
, c
);
677 * Remote GDB (aka "kgdb") interface.
682 xlcom_kgdb_getc(void *arg
)
684 return (xlcom_busy_getc(kgdb_iot
, kgdb_ioh
));
688 xlcom_kgdb_putc(void *arg
, int c
)
690 xlcom_busy_putc(kgdb_iot
, kgdb_ioh
, c
);
696 if (bus_space_map(kgdb_iot
, KGDB_ADDR
, XLCOM_SIZE
, 0, &kgdb_ioh
))
697 panic("xlcom_kgdbinit: could not map kgdb_ioh");
699 xlcom_reset(kgdb_iot
, kgdb_ioh
);
701 kgdb_attach(xlcom_kgdb_getc
, xlcom_kgdb_putc
, NULL
);
702 kgdb_dev
= 123; /* arbitrary strictly positive value */