1 /* $NetBSD: footbridge_com.c,v 1.31 2009/03/14 21:04:05 dsl Exp $ */
4 * Copyright (c) 1997 Mark Brinicombe
5 * Copyright (c) 1997 Causality Limited
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Mark Brinicombe
18 * for the NetBSD Project.
19 * 4. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 * COM driver, using the footbridge UART
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: footbridge_com.c,v 1.31 2009/03/14 21:04:05 dsl Exp $");
42 #include "opt_ddbparam.h"
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/ioctl.h>
47 #include <sys/select.h>
51 #include <sys/syslog.h>
52 #include <sys/device.h>
53 #include <sys/malloc.h>
54 #include <sys/termios.h>
55 #include <sys/kauth.h>
56 #include <machine/bus.h>
57 #include <machine/intr.h>
58 #include <arm/footbridge/dc21285mem.h>
59 #include <arm/footbridge/dc21285reg.h>
60 #include <arm/footbridge/footbridgevar.h>
61 #include <arm/footbridge/footbridge.h>
67 extern u_int dc21285_fclk
;
72 * Define the keycode recognised as a request to call the debugger
73 * A value of 0 disables the feature when DDB is built in
77 #endif /* DDB_KEYCODE */
82 bus_space_tag_t sc_iot
;
83 bus_space_handle_t sc_ioh
;
85 struct callout sc_softintr_ch
;
89 #define HW_FLAG_CONSOLE 0x01
101 #define RX_BUFFER_SIZE 0x100
103 static int fcom_probe(device_t
, cfdata_t
, void *);
104 static void fcom_attach(device_t
, device_t
, void *);
105 static void fcom_softintr(void *);
107 static int fcom_rxintr(void *);
108 /*static int fcom_txintr(void *);*/
111 /*void fcomcnprobe(struct consdev *);
112 void fcomcninit(struct consdev *);*/
113 int fcomcngetc(dev_t
);
114 void fcomcnputc(dev_t
, int);
115 void fcomcnpollc(dev_t
, int);
117 CFATTACH_DECL_NEW(fcom
, sizeof(struct fcom_softc
),
118 fcom_probe
, fcom_attach
, NULL
, NULL
);
120 extern struct cfdriver fcom_cd
;
122 dev_type_open(fcomopen
);
123 dev_type_close(fcomclose
);
124 dev_type_read(fcomread
);
125 dev_type_write(fcomwrite
);
126 dev_type_ioctl(fcomioctl
);
127 dev_type_tty(fcomtty
);
128 dev_type_poll(fcompoll
);
130 const struct cdevsw fcom_cdevsw
= {
131 fcomopen
, fcomclose
, fcomread
, fcomwrite
, fcomioctl
,
132 nostop
, fcomtty
, fcompoll
, nommap
, ttykqfilter
, D_TTY
135 void fcominit(bus_space_tag_t
, bus_space_handle_t
, int, int);
136 void fcominitcons(bus_space_tag_t
, bus_space_handle_t
);
138 bus_space_tag_t fcomconstag
;
139 bus_space_handle_t fcomconsioh
;
140 extern int comcnmode
;
141 extern int comcnspeed
;
143 #define COMUNIT(x) (minor(x))
149 * The console is set up at init time, well in advance of the reset of the
150 * system and thus we have a private bus space tag for the console.
152 * The tag is provided by fcom_io.c and fcom_io_asm.S
154 extern struct bus_space fcomcons_bs_tag
;
157 * int fcom_probe(struct device *parent, struct cfdata *cf, void *aux)
159 * Make sure we are trying to attach a com device and then
164 fcom_probe(device_t parent
, cfdata_t cf
, void *aux
)
166 union footbridge_attach_args
*fba
= aux
;
168 if (strcmp(fba
->fba_name
, "fcom") == 0)
174 * void fcom_attach(device_t parent, device_t self, void *aux)
176 * attach the com device
180 fcom_attach(device_t parent
, device_t self
, void *aux
)
182 union footbridge_attach_args
*fba
= aux
;
183 struct fcom_softc
*sc
= device_private(self
);
185 /* Set up the softc */
187 sc
->sc_iot
= fba
->fba_fca
.fca_iot
;
188 sc
->sc_ioh
= fba
->fba_fca
.fca_ioh
;
189 callout_init(&sc
->sc_softintr_ch
, 0);
190 sc
->sc_rx_irq
= fba
->fba_fca
.fca_rx_irq
;
191 sc
->sc_tx_irq
= fba
->fba_fca
.fca_tx_irq
;
195 /* If we have a console tag then make a note of it */
197 sc
->sc_hwflags
|= HW_FLAG_CONSOLE
;
199 if (sc
->sc_hwflags
& HW_FLAG_CONSOLE
) {
202 /* locate the major number */
203 major
= cdevsw_lookup_major(&fcom_cdevsw
);
205 cn_tab
->cn_dev
= makedev(major
, device_unit(sc
->sc_dev
));
206 aprint_normal(": console");
210 sc
->sc_ih
= footbridge_intr_claim(sc
->sc_rx_irq
, IPL_SERIAL
,
211 "serial rx", fcom_rxintr
, sc
);
212 if (sc
->sc_ih
== NULL
)
213 panic("%s: Cannot install rx interrupt handler",
214 device_xname(sc
->sc_dev
));
217 static void fcomstart(struct tty
*);
218 static int fcomparam(struct tty
*, struct termios
*);
221 fcomopen(dev_t dev
, int flag
, int mode
, struct lwp
*l
)
223 struct fcom_softc
*sc
;
226 sc
= device_lookup_private(&fcom_cd
, minor(dev
));
229 if (!(tp
= sc
->sc_tty
))
230 sc
->sc_tty
= tp
= ttymalloc();
231 if (!sc
->sc_rxbuffer
[0]) {
232 sc
->sc_rxbuffer
[0] = malloc(RX_BUFFER_SIZE
, M_DEVBUF
, M_WAITOK
);
233 sc
->sc_rxbuffer
[1] = malloc(RX_BUFFER_SIZE
, M_DEVBUF
, M_WAITOK
);
236 sc
->sc_rxbuf
= sc
->sc_rxbuffer
[sc
->sc_rxcur
];
238 panic("%s: Cannot allocate rx buffer memory",
239 device_xname(sc
->sc_dev
));
241 tp
->t_oproc
= fcomstart
;
242 tp
->t_param
= fcomparam
;
245 if (kauth_authorize_device_tty(l
->l_cred
, KAUTH_DEVICE_TTY_OPEN
, tp
))
248 if (!(tp
->t_state
& TS_ISOPEN
&& tp
->t_wopen
== 0)) {
250 tp
->t_cflag
= TTYDEF_CFLAG
;
251 tp
->t_iflag
= TTYDEF_IFLAG
;
252 tp
->t_oflag
= TTYDEF_OFLAG
;
253 tp
->t_lflag
= TTYDEF_LFLAG
;
256 * Initialize the termios status to the defaults. Add in the
257 * sticky bits from TIOCSFLAGS.
260 if (ISSET(sc
->sc_hwflags
, HW_FLAG_CONSOLE
))
261 tp
->t_ospeed
= comcnspeed
;
263 tp
->t_ospeed
= TTYDEF_SPEED
;
265 fcomparam(tp
, &tp
->t_termios
);
268 tp
->t_state
|= TS_CARR_ON
;
270 return (*tp
->t_linesw
->l_open
)(dev
, tp
);
274 fcomclose(dev_t dev
, int flag
, int mode
, struct lwp
*l
)
276 struct fcom_softc
*sc
= device_lookup_private(&fcom_cd
, minor(dev
));
277 struct tty
*tp
= sc
->sc_tty
;
278 /* XXX This is for cons.c. */
279 if (!ISSET(tp
->t_state
, TS_ISOPEN
))
282 (*tp
->t_linesw
->l_close
)(tp
, flag
);
285 if (sc
->sc_rxbuffer
[0] == NULL
)
286 panic("fcomclose: rx buffers not allocated");
287 #endif /* DIAGNOSTIC */
288 free(sc
->sc_rxbuffer
[0], M_DEVBUF
);
289 free(sc
->sc_rxbuffer
[1], M_DEVBUF
);
290 sc
->sc_rxbuffer
[0] = NULL
;
291 sc
->sc_rxbuffer
[1] = NULL
;
297 fcomread(dev_t dev
, struct uio
*uio
, int flag
)
299 struct fcom_softc
*sc
= device_lookup_private(&fcom_cd
, minor(dev
));
300 struct tty
*tp
= sc
->sc_tty
;
302 return (*tp
->t_linesw
->l_read
)(tp
, uio
, flag
);
306 fcomwrite(dev_t dev
, struct uio
*uio
, int flag
)
308 struct fcom_softc
*sc
= device_lookup_private(&fcom_cd
, minor(dev
));
309 struct tty
*tp
= sc
->sc_tty
;
311 return (*tp
->t_linesw
->l_write
)(tp
, uio
, flag
);
315 fcompoll(dev_t dev
, int events
, struct lwp
*l
)
317 struct fcom_softc
*sc
= device_lookup_private(&fcom_cd
, minor(dev
));
318 struct tty
*tp
= sc
->sc_tty
;
320 return ((*tp
->t_linesw
->l_poll
)(tp
, events
, l
));
324 fcomioctl(dev_t dev
, u_long cmd
, void *data
, int flag
, struct lwp
*l
)
326 struct fcom_softc
*sc
= device_lookup_private(&fcom_cd
, minor(dev
));
327 struct tty
*tp
= sc
->sc_tty
;
330 if ((error
= (*tp
->t_linesw
->l_ioctl
)(tp
, cmd
, data
, flag
, l
)) !=
333 if ((error
= ttioctl(tp
, cmd
, data
, flag
, l
)) != EPASSTHROUGH
)
338 *(int *)data
= sc
->sc_swflags
;
342 error
= kauth_authorize_device_tty(l
->l_cred
,
343 KAUTH_DEVICE_TTY_PRIVSET
, tp
);
346 sc
->sc_swflags
= *(int *)data
;
356 struct fcom_softc
*sc
= device_lookup_private(&fcom_cd
, minor(dev
));
362 fcomstart(struct tty
*tp
)
368 struct fcom_softc
*sc
= device_lookup_private(&fcom_cd
, minor(tp
->t_dev
));
369 bus_space_tag_t iot
= sc
->sc_iot
;
370 bus_space_handle_t ioh
= sc
->sc_ioh
;
374 if (tp
->t_state
& (TS_TIMEOUT
| TS_BUSY
| TS_TTSTOP
)) {
378 tp
->t_state
|= TS_BUSY
;
381 /* s = splserial();*/
382 /* wait for any pending transmission to finish */
384 while ((bus_space_read_4(iot
, ioh
, UART_FLAGS
) & UART_TX_BUSY
) && --timo
)
388 if (bus_space_read_4(iot
, ioh
, UART_FLAGS
) & UART_TX_BUSY
) {
389 tp
->t_state
|= TS_TIMEOUT
;
390 callout_schedule(&tp
->t_rstrt_ch
, 1);
398 len
= q_to_b(cl
, buf
, 64);
399 for (loop
= 0; loop
< len
; ++loop
) {
400 /* s = splserial();*/
402 bus_space_write_4(iot
, ioh
, UART_DATA
, buf
[loop
]);
404 /* wait for this transmission to complete */
406 while ((bus_space_read_4(iot
, ioh
, UART_FLAGS
) & UART_TX_BUSY
) && --timo
)
411 tp
->t_state
&= ~TS_BUSY
;
413 tp
->t_state
|= TS_TIMEOUT
;
414 callout_schedule(&tp
->t_rstrt_ch
, 1);
420 fcomparam(struct tty
*tp
, struct termios
*t
)
422 struct fcom_softc
*sc
= device_lookup_private(&fcom_cd
, minor(tp
->t_dev
));
423 bus_space_tag_t iot
= sc
->sc_iot
;
424 bus_space_handle_t ioh
= sc
->sc_ioh
;
431 /* check requested parameters */
434 if (t
->c_ispeed
&& t
->c_ispeed
!= t
->c_ospeed
)
437 switch (t
->c_ospeed
) {
444 baudrate
= UART_BRD(dc21285_fclk
, t
->c_ospeed
);
447 baudrate
= UART_BRD(dc21285_fclk
, 9600);
451 l_ubrlcr
= baudrate
& 0xff;
452 m_ubrlcr
= (baudrate
>> 8) & 0xf;
455 switch (ISSET(t
->c_cflag
, CSIZE
)) {
457 h_ubrlcr
|= UART_DATA_BITS_5
;
460 h_ubrlcr
|= UART_DATA_BITS_6
;
463 h_ubrlcr
|= UART_DATA_BITS_7
;
466 h_ubrlcr
|= UART_DATA_BITS_8
;
470 if (ISSET(t
->c_cflag
, PARENB
)) {
471 h_ubrlcr
|= UART_PARITY_ENABLE
;
472 if (ISSET(t
->c_cflag
, PARODD
))
473 h_ubrlcr
|= UART_ODD_PARITY
;
475 h_ubrlcr
|= UART_EVEN_PARITY
;
478 if (ISSET(t
->c_cflag
, CSTOPB
))
479 h_ubrlcr
|= UART_STOP_BITS_2
;
481 bus_space_write_4(iot
, ioh
, UART_L_UBRLCR
, l_ubrlcr
);
482 bus_space_write_4(iot
, ioh
, UART_M_UBRLCR
, m_ubrlcr
);
483 bus_space_write_4(iot
, ioh
, UART_H_UBRLCR
, h_ubrlcr
);
487 sc
->sc_l_ubrlcr
= l_ubrlcr
;
488 sc
->sc_m_ubrlcr
= m_ubrlcr
;
489 sc
->sc_h_ubrlcr
= h_ubrlcr
;
492 * For the console, always force CLOCAL and !HUPCL, so that the port
495 if (ISSET(sc
->sc_swflags
, TIOCFLAG_SOFTCAR
) ||
496 ISSET(sc
->sc_hwflags
, HW_FLAG_CONSOLE
)) {
497 SET(t
->c_cflag
, CLOCAL
);
498 CLR(t
->c_cflag
, HUPCL
);
501 /* and copy to tty */
503 tp
->t_ospeed
= t
->c_ospeed
;
504 tp
->t_cflag
= t
->c_cflag
;
506 bus_space_write_4(iot
, ioh
, UART_L_UBRLCR
, l_ubrlcr
);
507 bus_space_write_4(iot
, ioh
, UART_M_UBRLCR
, m_ubrlcr
);
508 bus_space_write_4(iot
, ioh
, UART_H_UBRLCR
, h_ubrlcr
);
515 static int softint_scheduled
= 0;
518 fcom_softintr(void *arg
)
520 struct fcom_softc
*sc
= arg
;
521 struct tty
*tp
= sc
->sc_tty
;
531 sc
->sc_rxbuf
= sc
->sc_rxbuffer
[sc
->sc_rxcur
];
535 for (loop
= 0; loop
< len
; ++loop
)
536 (*tp
->t_linesw
->l_rint
)(ptr
[loop
], tp
);
537 softint_scheduled
= 0;
542 fcom_txintr(void *arg
)
544 /* struct fcom_softc *sc = arg;*/
546 printf("fcom_txintr()\n");
552 fcom_rxintr(void *arg
)
554 struct fcom_softc
*sc
= arg
;
555 bus_space_tag_t iot
= sc
->sc_iot
;
556 bus_space_handle_t ioh
= sc
->sc_ioh
;
557 struct tty
*tp
= sc
->sc_tty
;
562 status
= bus_space_read_4(iot
, ioh
, UART_FLAGS
);
563 if ((status
& UART_RX_FULL
))
565 byte
= bus_space_read_4(iot
, ioh
, UART_DATA
);
566 status
= bus_space_read_4(iot
, ioh
, UART_RX_STAT
);
567 #if defined(DDB) && DDB_KEYCODE > 0
569 * Temporary hack so that I can force the kernel into
570 * the debugger via the serial port
572 if (byte
== DDB_KEYCODE
) Debugger();
574 if (tp
&& (tp
->t_state
& TS_ISOPEN
))
575 if (sc
->sc_rxpos
< RX_BUFFER_SIZE
) {
576 sc
->sc_rxbuf
[sc
->sc_rxpos
++] = byte
;
577 if (!softint_scheduled
) {
578 softint_scheduled
= 1;
579 callout_reset(&sc
->sc_softintr_ch
,
580 1, fcom_softintr
, sc
);
589 fcom_iflush(struct fcom_softc
*sc
)
591 bus_space_tag_t iot
= sc
->sc_iot
;
592 bus_space_handle_t ioh
= sc
->sc_ioh
;
594 /* flush any pending I/O */
595 while (!ISSET(bus_space_read_4(iot
, ioh
, UART_FLAGS
), UART_RX_FULL
))
596 (void) bus_space_read_4(iot
, ioh
, UART_DATA
);
601 * Following are all routines needed for COM to act as console
606 fcomcnprobe(struct consdev
*cp
)
610 /* Serial console is always present so no probe */
612 /* locate the major number */
613 major
= cdevsw_lookup_major(&fcom_cdevsw
);
615 /* initialize required fields */
616 cp
->cn_dev
= makedev(major
, CONUNIT
);
617 cp
->cn_pri
= CN_REMOTE
; /* Force a serial port console */
621 fcomcninit(struct consdev
*cp
)
623 fcomconstag
= &fcomcons_bs_tag
;
625 if (bus_space_map(fcomconstag
, DC21285_ARMCSR_BASE
, DC21285_ARMCSR_SIZE
, 0, &fcomconsioh
))
626 panic("fcomcninit: mapping failed");
628 fcominitcons(fcomconstag
, fcomconsioh
);
633 fcomcnattach(u_int iobase
, int rate
, tcflag_t cflag
)
635 static struct consdev fcomcons
= {
636 NULL
, NULL
, fcomcngetc
, fcomcnputc
, fcomcnpollc
, NULL
,
637 NULL
, NULL
, NODEV
, CN_NORMAL
640 fcomconstag
= &fcomcons_bs_tag
;
642 if (bus_space_map(fcomconstag
, iobase
, DC21285_ARMCSR_SIZE
,
644 panic("fcomcninit: mapping failed");
646 fcominit(fcomconstag
, fcomconsioh
, rate
, cflag
);
650 /* comcnspeed = rate;
658 bus_space_unmap(fcomconstag
, fcomconsioh
, DC21285_ARMCSR_SIZE
);
665 * Initialize UART to known state.
668 fcominit(bus_space_tag_t iot
, bus_space_handle_t ioh
, int rate
, int mode
)
682 baudrate
= UART_BRD(dc21285_fclk
, rate
);
685 baudrate
= UART_BRD(dc21285_fclk
, 9600);
690 switch (mode
& CSIZE
) {
692 h_ubrlcr
|= UART_DATA_BITS_5
;
695 h_ubrlcr
|= UART_DATA_BITS_6
;
698 h_ubrlcr
|= UART_DATA_BITS_7
;
701 h_ubrlcr
|= UART_DATA_BITS_8
;
706 h_ubrlcr
|= UART_PARITY_ENABLE
;
708 h_ubrlcr
|= UART_ODD_PARITY
;
710 h_ubrlcr
|= UART_EVEN_PARITY
;
713 h_ubrlcr
|= UART_STOP_BITS_2
;
715 m_ubrlcr
= (baudrate
>> 8) & 0xf;
716 l_ubrlcr
= baudrate
& 0xff;
718 bus_space_write_4(iot
, ioh
, UART_L_UBRLCR
, l_ubrlcr
);
719 bus_space_write_4(iot
, ioh
, UART_M_UBRLCR
, m_ubrlcr
);
720 bus_space_write_4(iot
, ioh
, UART_H_UBRLCR
, h_ubrlcr
);
724 * Set UART for console use. Do normal init, then enable interrupts.
727 fcominitcons(bus_space_tag_t iot
, bus_space_handle_t ioh
)
731 fcominit(iot
, ioh
, comcnspeed
, comcnmode
);
740 fcomcngetc(dev_t dev
)
743 bus_space_tag_t iot
= fcomconstag
;
744 bus_space_handle_t ioh
= fcomconsioh
;
747 while ((bus_space_read_4(iot
, ioh
, UART_FLAGS
) & UART_RX_FULL
) != 0)
749 c
= bus_space_read_4(iot
, ioh
, UART_DATA
);
750 stat
= bus_space_read_4(iot
, ioh
, UART_RX_STAT
);
752 #if defined(DDB) && DDB_KEYCODE > 0
754 * Temporary hack so that I can force the kernel into
755 * the debugger via the serial port
757 if (c
== DDB_KEYCODE
) Debugger();
764 * Console kernel output character routine.
767 fcomcnputc(dev_t dev
, int c
)
770 bus_space_tag_t iot
= fcomconstag
;
771 bus_space_handle_t ioh
= fcomconsioh
;
774 /* wait for any pending transmission to finish */
776 while ((bus_space_read_4(iot
, ioh
, UART_FLAGS
) & UART_TX_BUSY
) && --timo
)
778 bus_space_write_4(iot
, ioh
, UART_DATA
, c
);
780 /* wait for this transmission to complete */
782 while ((bus_space_read_4(iot
, ioh
, UART_FLAGS
) & UART_TX_BUSY
) && --timo
)
784 /* Clear interrupt status here */
789 fcomcnpollc(dev_t dev
, int on
)