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