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