xref: /openbsd/sys/arch/octeon/dev/cn30xxuart.c (revision 471aeecf)
1 /*	$OpenBSD: cn30xxuart.c,v 1.13 2022/04/06 18:59:27 naddy Exp $	*/
2 
3 /*
4  * Copyright (c) 2001-2004 Opsycon AB  (www.opsycon.se / www.opsycon.com)
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/device.h>
32 #include <sys/tty.h>
33 #include <sys/conf.h>
34 
35 #include <dev/ofw/fdt.h>
36 #include <dev/ofw/openfirm.h>
37 
38 #include <machine/octeonreg.h>
39 #include <machine/octeonvar.h>
40 #include <machine/autoconf.h>
41 #include <machine/bus.h>
42 #include <machine/fdt.h>
43 
44 #include <dev/ic/comreg.h>
45 #include <dev/ic/comvar.h>
46 #include <dev/cons.h>
47 
48 #include <octeon/dev/iobusvar.h>
49 #include <octeon/dev/cn30xxuartreg.h>
50 
51 #define OCTEON_UART_FIFO_SIZE		64
52 
53 int	cn30xxuart_probe(struct device *, void *, void *);
54 void	cn30xxuart_attach(struct device *, struct device *, void *);
55 int	cn30xxuart_intr(void *);
56 
57 const struct cfattach octuart_ca = {
58 	sizeof(struct com_softc), cn30xxuart_probe, cn30xxuart_attach
59 };
60 
61 extern struct cfdriver com_cd;
62 
63 cons_decl(octuart);
64 
65 #define  USR_TXFIFO_NOTFULL		2
66 
67 /* XXX: What is this used for? Removed from stand/boot/uart.c -r1.2 */
68 static int delay_changed = 1;
69 int cn30xxuart_delay(void);
70 void cn30xxuart_wait_txhr_empty(int);
71 
72 uint8_t	 uartbus_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t);
73 void	 uartbus_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t,
74 	    uint8_t);
75 
76 bus_space_t uartbus_tag = {
77 	.bus_base = PHYS_TO_XKPHYS(0, CCA_NC),
78 	.bus_private = NULL,
79 	._space_read_1 = uartbus_read_1,
80 	._space_write_1 = uartbus_write_1,
81 	._space_map = iobus_space_map,
82 	._space_unmap = iobus_space_unmap
83 };
84 
85 void
com_fdt_init_cons(void)86 com_fdt_init_cons(void)
87 {
88 	comconsiot = &uartbus_tag;
89 	comconsaddr = OCTEON_UART0_BASE;
90 	comconsfreq = octeon_ioclock_speed();
91 	comconsrate = B115200;
92 	comconscflag = (TTYDEF_CFLAG & ~(CSIZE | PARENB)) | CS8;
93 }
94 
95 int
cn30xxuart_probe(struct device * parent,void * match,void * aux)96 cn30xxuart_probe(struct device *parent, void *match, void *aux)
97 {
98 	struct fdt_attach_args *faa = aux;
99 
100 	return OF_is_compatible(faa->fa_node, "cavium,octeon-3860-uart");
101 }
102 
103 void
cn30xxuart_attach(struct device * parent,struct device * self,void * aux)104 cn30xxuart_attach(struct device *parent, struct device *self, void *aux)
105 {
106 	struct fdt_attach_args *faa = aux;
107 	struct com_softc *sc = (void *)self;
108 	int console = 0;
109 
110 	if (faa->fa_nreg != 1)
111 		return;
112 
113 	if (comconsiot == &uartbus_tag && comconsaddr == faa->fa_reg[0].addr)
114 		console = 1;
115 
116 	sc->sc_hwflags = 0;
117 	sc->sc_swflags = 0;
118 	sc->sc_frequency = octeon_ioclock_speed();
119 	sc->sc_uarttype = COM_UART_16550A;
120 	sc->sc_fifolen = OCTEON_UART_FIFO_SIZE;
121 
122 	if (!console || comconsattached) {
123 		sc->sc_iot = &uartbus_tag;
124 		sc->sc_iobase = faa->fa_reg[0].addr;
125 		if (bus_space_map(sc->sc_iot, sc->sc_iobase, COM_NPORTS, 0,
126 		    &sc->sc_ioh)) {
127 			printf(": could not map UART registers\n");
128 			return;
129 		}
130 	} else {
131 		/* Reuse the early console settings. */
132 		sc->sc_iot = comconsiot;
133 		sc->sc_iobase = comconsaddr;
134 		if (comcnattach(sc->sc_iot, sc->sc_iobase, comconsrate,
135 		    sc->sc_frequency, comconscflag))
136 			panic("could not set up serial console");
137 		sc->sc_ioh = comconsioh;
138 	}
139 
140 	com_attach_subr(sc);
141 
142 	octeon_intr_establish_fdt(faa->fa_node, IPL_TTY, cn30xxuart_intr, sc,
143 	    sc->sc_dev.dv_xname);
144 }
145 
146 int
cn30xxuart_intr(void * arg)147 cn30xxuart_intr(void *arg)
148 {
149 	comintr(arg);
150 
151 	/*
152 	 * Always return non-zero to prevent console clutter about spurious
153 	 * interrupts. comstart() enables the transmitter holding register
154 	 * empty interrupt before adding data to the FIFO, which can trigger
155 	 * a premature interrupt on the primary CPU in a multiprocessor system.
156 	 */
157 	return 1;
158 }
159 
160 /*
161  * Early console routines.
162  */
163 
164 int
cn30xxuart_delay(void)165 cn30xxuart_delay(void)
166 {
167 	int divisor;
168 	u_char lcr;
169 	static int d = 0;
170 
171 	if (!delay_changed)
172 		return d;
173 	delay_changed = 0;
174 	lcr = octeon_xkphys_read_8(MIO_UART0_LCR);
175 	octeon_xkphys_write_8(MIO_UART0_LCR, lcr | LCR_DLAB);
176 	divisor = octeon_xkphys_read_8(MIO_UART0_DLL) |
177 		octeon_xkphys_read_8(MIO_UART0_DLH) << 8;
178 	octeon_xkphys_write_8(MIO_UART0_LCR, lcr);
179 
180 	return 10; /* return an approx delay value */
181 }
182 
183 void
cn30xxuart_wait_txhr_empty(int d)184 cn30xxuart_wait_txhr_empty(int d)
185 {
186 	while (((octeon_xkphys_read_8(MIO_UART0_LSR) & LSR_TXRDY) == 0) &&
187 	    ((octeon_xkphys_read_8(MIO_UART0_USR) & USR_TXFIFO_NOTFULL) == 0))
188 		delay(d);
189 }
190 
191 void
octuartcninit(struct consdev * consdev)192 octuartcninit(struct consdev *consdev)
193 {
194 }
195 
196 void
octuartcnprobe(struct consdev * consdev)197 octuartcnprobe(struct consdev *consdev)
198 {
199 }
200 
201 void
octuartcnpollc(dev_t dev,int c)202 octuartcnpollc(dev_t dev, int c)
203 {
204 }
205 
206 void
octuartcnputc(dev_t dev,int c)207 octuartcnputc(dev_t dev, int c)
208 {
209 	int d;
210 
211 	/* 1/10th the time to transmit 1 character (estimate). */
212 	d = cn30xxuart_delay();
213 	cn30xxuart_wait_txhr_empty(d);
214 	octeon_xkphys_write_8(MIO_UART0_RBR, (uint8_t)c);
215 	cn30xxuart_wait_txhr_empty(d);
216 }
217 
218 int
octuartcngetc(dev_t dev)219 octuartcngetc(dev_t dev)
220 {
221 	int c, d;
222 
223 	/* 1/10th the time to transmit 1 character (estimate). */
224 	d = cn30xxuart_delay();
225 
226 	while ((octeon_xkphys_read_8(MIO_UART0_LSR) & LSR_RXRDY) == 0)
227 		delay(d);
228 
229 	c = (uint8_t)octeon_xkphys_read_8(MIO_UART0_RBR);
230 
231 	return (c);
232 }
233 
234 /*
235  * Bus access routines. These let com(4) work with the 64-bit registers.
236  */
237 
238 uint8_t
uartbus_read_1(bus_space_tag_t tag,bus_space_handle_t handle,bus_size_t off)239 uartbus_read_1(bus_space_tag_t tag, bus_space_handle_t handle, bus_size_t off)
240 {
241 	return *(volatile uint64_t *)(handle + (off << 3));
242 }
243 
244 void
uartbus_write_1(bus_space_tag_t tag,bus_space_handle_t handle,bus_size_t off,uint8_t value)245 uartbus_write_1(bus_space_tag_t tag, bus_space_handle_t handle, bus_size_t off,
246     uint8_t value)
247 {
248 	volatile uint64_t *reg = (uint64_t *)(handle + (off << 3));
249 
250 	*reg = value;
251 	(void)*reg;
252 }
253