xref: /openbsd-src/sys/arch/octeon/dev/cn30xxuart.c (revision 471aeecfc619bc9b69519928152daf993376c2a1)
1*471aeecfSnaddy /*	$OpenBSD: cn30xxuart.c,v 1.13 2022/04/06 18:59:27 naddy Exp $	*/
204dde687Ssyuu 
304dde687Ssyuu /*
404dde687Ssyuu  * Copyright (c) 2001-2004 Opsycon AB  (www.opsycon.se / www.opsycon.com)
504dde687Ssyuu  *
604dde687Ssyuu  * Redistribution and use in source and binary forms, with or without
704dde687Ssyuu  * modification, are permitted provided that the following conditions
804dde687Ssyuu  * are met:
904dde687Ssyuu  * 1. Redistributions of source code must retain the above copyright
1004dde687Ssyuu  *    notice, this list of conditions and the following disclaimer.
1104dde687Ssyuu  * 2. Redistributions in binary form must reproduce the above copyright
1204dde687Ssyuu  *    notice, this list of conditions and the following disclaimer in the
1304dde687Ssyuu  *    documentation and/or other materials provided with the distribution.
1404dde687Ssyuu  *
1504dde687Ssyuu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
1604dde687Ssyuu  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1704dde687Ssyuu  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1804dde687Ssyuu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
1904dde687Ssyuu  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2004dde687Ssyuu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2104dde687Ssyuu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2204dde687Ssyuu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2304dde687Ssyuu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2404dde687Ssyuu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2504dde687Ssyuu  * SUCH DAMAGE.
2604dde687Ssyuu  *
2704dde687Ssyuu  */
2804dde687Ssyuu 
2904dde687Ssyuu #include <sys/param.h>
3004dde687Ssyuu #include <sys/systm.h>
3104dde687Ssyuu #include <sys/device.h>
3204dde687Ssyuu #include <sys/tty.h>
3304dde687Ssyuu #include <sys/conf.h>
3404dde687Ssyuu 
3523da198fSvisa #include <dev/ofw/fdt.h>
3623da198fSvisa #include <dev/ofw/openfirm.h>
3723da198fSvisa 
3823da198fSvisa #include <machine/octeonreg.h>
395f139a3aSjmatthew #include <machine/octeonvar.h>
4004dde687Ssyuu #include <machine/autoconf.h>
4104dde687Ssyuu #include <machine/bus.h>
4223da198fSvisa #include <machine/fdt.h>
4304dde687Ssyuu 
4404dde687Ssyuu #include <dev/ic/comreg.h>
4504dde687Ssyuu #include <dev/ic/comvar.h>
4604dde687Ssyuu #include <dev/cons.h>
4704dde687Ssyuu 
4804dde687Ssyuu #include <octeon/dev/iobusvar.h>
4940763d4fSuebayasi #include <octeon/dev/cn30xxuartreg.h>
5004dde687Ssyuu 
51d5550bbfSvisa #define OCTEON_UART_FIFO_SIZE		64
52d5550bbfSvisa 
5304dde687Ssyuu int	cn30xxuart_probe(struct device *, void *, void *);
5404dde687Ssyuu void	cn30xxuart_attach(struct device *, struct device *, void *);
5510ba6958Svisa int	cn30xxuart_intr(void *);
5604dde687Ssyuu 
57*471aeecfSnaddy const struct cfattach octuart_ca = {
5804dde687Ssyuu 	sizeof(struct com_softc), cn30xxuart_probe, cn30xxuart_attach
5904dde687Ssyuu };
6004dde687Ssyuu 
6104dde687Ssyuu extern struct cfdriver com_cd;
6204dde687Ssyuu 
63874cea07Sderaadt cons_decl(octuart);
6404dde687Ssyuu 
6504dde687Ssyuu #define  USR_TXFIFO_NOTFULL		2
6604dde687Ssyuu 
67bce2b5ccSjasper /* XXX: What is this used for? Removed from stand/boot/uart.c -r1.2 */
6804dde687Ssyuu static int delay_changed = 1;
6904dde687Ssyuu int cn30xxuart_delay(void);
7004dde687Ssyuu void cn30xxuart_wait_txhr_empty(int);
7104dde687Ssyuu 
7223da198fSvisa uint8_t	 uartbus_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t);
7323da198fSvisa void	 uartbus_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t,
7423da198fSvisa 	    uint8_t);
7523da198fSvisa 
7623da198fSvisa bus_space_t uartbus_tag = {
7723da198fSvisa 	.bus_base = PHYS_TO_XKPHYS(0, CCA_NC),
7823da198fSvisa 	.bus_private = NULL,
7923da198fSvisa 	._space_read_1 = uartbus_read_1,
8023da198fSvisa 	._space_write_1 = uartbus_write_1,
8123da198fSvisa 	._space_map = iobus_space_map,
8223da198fSvisa 	._space_unmap = iobus_space_unmap
8323da198fSvisa };
8423da198fSvisa 
8523da198fSvisa void
com_fdt_init_cons(void)8623da198fSvisa com_fdt_init_cons(void)
8723da198fSvisa {
8823da198fSvisa 	comconsiot = &uartbus_tag;
8923da198fSvisa 	comconsaddr = OCTEON_UART0_BASE;
9023da198fSvisa 	comconsfreq = octeon_ioclock_speed();
9123da198fSvisa 	comconsrate = B115200;
9223da198fSvisa 	comconscflag = (TTYDEF_CFLAG & ~(CSIZE | PARENB)) | CS8;
9323da198fSvisa }
9423da198fSvisa 
9504dde687Ssyuu int
cn30xxuart_probe(struct device * parent,void * match,void * aux)9604dde687Ssyuu cn30xxuart_probe(struct device *parent, void *match, void *aux)
9704dde687Ssyuu {
9823da198fSvisa 	struct fdt_attach_args *faa = aux;
9904dde687Ssyuu 
10023da198fSvisa 	return OF_is_compatible(faa->fa_node, "cavium,octeon-3860-uart");
10104dde687Ssyuu }
10204dde687Ssyuu 
10304dde687Ssyuu void
cn30xxuart_attach(struct device * parent,struct device * self,void * aux)10404dde687Ssyuu cn30xxuart_attach(struct device *parent, struct device *self, void *aux)
10504dde687Ssyuu {
10623da198fSvisa 	struct fdt_attach_args *faa = aux;
10704dde687Ssyuu 	struct com_softc *sc = (void *)self;
10823da198fSvisa 	int console = 0;
10904dde687Ssyuu 
11023da198fSvisa 	if (faa->fa_nreg != 1)
11123da198fSvisa 		return;
11223da198fSvisa 
11323da198fSvisa 	if (comconsiot == &uartbus_tag && comconsaddr == faa->fa_reg[0].addr)
11404dde687Ssyuu 		console = 1;
11504dde687Ssyuu 
11604dde687Ssyuu 	sc->sc_hwflags = 0;
11704dde687Ssyuu 	sc->sc_swflags = 0;
1185f139a3aSjmatthew 	sc->sc_frequency = octeon_ioclock_speed();
119d5550bbfSvisa 	sc->sc_uarttype = COM_UART_16550A;
120d5550bbfSvisa 	sc->sc_fifolen = OCTEON_UART_FIFO_SIZE;
12104dde687Ssyuu 
12223da198fSvisa 	if (!console || comconsattached) {
12323da198fSvisa 		sc->sc_iot = &uartbus_tag;
12423da198fSvisa 		sc->sc_iobase = faa->fa_reg[0].addr;
12523da198fSvisa 		if (bus_space_map(sc->sc_iot, sc->sc_iobase, COM_NPORTS, 0,
12623da198fSvisa 		    &sc->sc_ioh)) {
12723da198fSvisa 			printf(": could not map UART registers\n");
12804dde687Ssyuu 			return;
12904dde687Ssyuu 		}
13023da198fSvisa 	} else {
13123da198fSvisa 		/* Reuse the early console settings. */
13223da198fSvisa 		sc->sc_iot = comconsiot;
13323da198fSvisa 		sc->sc_iobase = comconsaddr;
13423da198fSvisa 		if (comcnattach(sc->sc_iot, sc->sc_iobase, comconsrate,
13523da198fSvisa 		    sc->sc_frequency, comconscflag))
13623da198fSvisa 			panic("could not set up serial console");
13723da198fSvisa 		sc->sc_ioh = comconsioh;
13804dde687Ssyuu 	}
13904dde687Ssyuu 
14004dde687Ssyuu 	com_attach_subr(sc);
14104dde687Ssyuu 
14223da198fSvisa 	octeon_intr_establish_fdt(faa->fa_node, IPL_TTY, cn30xxuart_intr, sc,
14323da198fSvisa 	    sc->sc_dev.dv_xname);
14404dde687Ssyuu }
14504dde687Ssyuu 
14610ba6958Svisa int
cn30xxuart_intr(void * arg)14710ba6958Svisa cn30xxuart_intr(void *arg)
14810ba6958Svisa {
14910ba6958Svisa 	comintr(arg);
15010ba6958Svisa 
15110ba6958Svisa 	/*
15210ba6958Svisa 	 * Always return non-zero to prevent console clutter about spurious
15310ba6958Svisa 	 * interrupts. comstart() enables the transmitter holding register
15410ba6958Svisa 	 * empty interrupt before adding data to the FIFO, which can trigger
15510ba6958Svisa 	 * a premature interrupt on the primary CPU in a multiprocessor system.
15610ba6958Svisa 	 */
15710ba6958Svisa 	return 1;
15810ba6958Svisa }
15910ba6958Svisa 
16004dde687Ssyuu /*
16104dde687Ssyuu  * Early console routines.
16204dde687Ssyuu  */
16323da198fSvisa 
16404dde687Ssyuu int
cn30xxuart_delay(void)16504dde687Ssyuu cn30xxuart_delay(void)
16604dde687Ssyuu {
16704dde687Ssyuu 	int divisor;
16804dde687Ssyuu 	u_char lcr;
16904dde687Ssyuu 	static int d = 0;
17004dde687Ssyuu 
17140763d4fSuebayasi 	if (!delay_changed)
17240763d4fSuebayasi 		return d;
17304dde687Ssyuu 	delay_changed = 0;
17440763d4fSuebayasi 	lcr = octeon_xkphys_read_8(MIO_UART0_LCR);
17540763d4fSuebayasi 	octeon_xkphys_write_8(MIO_UART0_LCR, lcr | LCR_DLAB);
17640763d4fSuebayasi 	divisor = octeon_xkphys_read_8(MIO_UART0_DLL) |
17740763d4fSuebayasi 		octeon_xkphys_read_8(MIO_UART0_DLH) << 8;
17840763d4fSuebayasi 	octeon_xkphys_write_8(MIO_UART0_LCR, lcr);
17904dde687Ssyuu 
18004dde687Ssyuu 	return 10; /* return an approx delay value */
18104dde687Ssyuu }
18204dde687Ssyuu 
18304dde687Ssyuu void
cn30xxuart_wait_txhr_empty(int d)18404dde687Ssyuu cn30xxuart_wait_txhr_empty(int d)
18504dde687Ssyuu {
18640763d4fSuebayasi 	while (((octeon_xkphys_read_8(MIO_UART0_LSR) & LSR_TXRDY) == 0) &&
18740763d4fSuebayasi 	    ((octeon_xkphys_read_8(MIO_UART0_USR) & USR_TXFIFO_NOTFULL) == 0))
18804dde687Ssyuu 		delay(d);
18904dde687Ssyuu }
19004dde687Ssyuu 
19104dde687Ssyuu void
octuartcninit(struct consdev * consdev)192874cea07Sderaadt octuartcninit(struct consdev *consdev)
19304dde687Ssyuu {
19404dde687Ssyuu }
19504dde687Ssyuu 
19604dde687Ssyuu void
octuartcnprobe(struct consdev * consdev)197874cea07Sderaadt octuartcnprobe(struct consdev *consdev)
19804dde687Ssyuu {
19904dde687Ssyuu }
20004dde687Ssyuu 
20104dde687Ssyuu void
octuartcnpollc(dev_t dev,int c)202874cea07Sderaadt octuartcnpollc(dev_t dev, int c)
20304dde687Ssyuu {
20404dde687Ssyuu }
20504dde687Ssyuu 
20604dde687Ssyuu void
octuartcnputc(dev_t dev,int c)207874cea07Sderaadt octuartcnputc(dev_t dev, int c)
20804dde687Ssyuu {
20904dde687Ssyuu 	int d;
21004dde687Ssyuu 
21104dde687Ssyuu 	/* 1/10th the time to transmit 1 character (estimate). */
21204dde687Ssyuu 	d = cn30xxuart_delay();
21304dde687Ssyuu 	cn30xxuart_wait_txhr_empty(d);
21440763d4fSuebayasi 	octeon_xkphys_write_8(MIO_UART0_RBR, (uint8_t)c);
21504dde687Ssyuu 	cn30xxuart_wait_txhr_empty(d);
21604dde687Ssyuu }
21704dde687Ssyuu 
21804dde687Ssyuu int
octuartcngetc(dev_t dev)219874cea07Sderaadt octuartcngetc(dev_t dev)
22004dde687Ssyuu {
22104dde687Ssyuu 	int c, d;
22204dde687Ssyuu 
22304dde687Ssyuu 	/* 1/10th the time to transmit 1 character (estimate). */
22404dde687Ssyuu 	d = cn30xxuart_delay();
22504dde687Ssyuu 
22640763d4fSuebayasi 	while ((octeon_xkphys_read_8(MIO_UART0_LSR) & LSR_RXRDY) == 0)
22704dde687Ssyuu 		delay(d);
22804dde687Ssyuu 
22940763d4fSuebayasi 	c = (uint8_t)octeon_xkphys_read_8(MIO_UART0_RBR);
23004dde687Ssyuu 
23104dde687Ssyuu 	return (c);
23204dde687Ssyuu }
23323da198fSvisa 
23423da198fSvisa /*
23523da198fSvisa  * Bus access routines. These let com(4) work with the 64-bit registers.
23623da198fSvisa  */
23723da198fSvisa 
23823da198fSvisa uint8_t
uartbus_read_1(bus_space_tag_t tag,bus_space_handle_t handle,bus_size_t off)23923da198fSvisa uartbus_read_1(bus_space_tag_t tag, bus_space_handle_t handle, bus_size_t off)
24023da198fSvisa {
24123da198fSvisa 	return *(volatile uint64_t *)(handle + (off << 3));
24223da198fSvisa }
24323da198fSvisa 
24423da198fSvisa void
uartbus_write_1(bus_space_tag_t tag,bus_space_handle_t handle,bus_size_t off,uint8_t value)24523da198fSvisa uartbus_write_1(bus_space_tag_t tag, bus_space_handle_t handle, bus_size_t off,
24623da198fSvisa     uint8_t value)
24723da198fSvisa {
2484f8afe3bSvisa 	volatile uint64_t *reg = (uint64_t *)(handle + (off << 3));
2494f8afe3bSvisa 
2504f8afe3bSvisa 	*reg = value;
2514f8afe3bSvisa 	(void)*reg;
25223da198fSvisa }
253