xref: /freebsd-14.2/sys/dev/uart/uart_dev_imx.c (revision 2d40ec16)
1 /*-
2  * Copyright (c) 2012 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Oleksandr Rybalko under sponsorship
6  * from the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1.	Redistributions of source code must retain the above copyright
12  *	notice, this list of conditions and the following disclaimer.
13  * 2.	Redistributions in binary form must reproduce the above copyright
14  *	notice, this list of conditions and the following disclaimer in the
15  *	documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include "opt_ddb.h"
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bus.h>
38 #include <sys/conf.h>
39 #include <sys/kdb.h>
40 #include <machine/bus.h>
41 #include <machine/fdt.h>
42 
43 #include <dev/uart/uart.h>
44 #include <dev/uart/uart_cpu.h>
45 #include <dev/uart/uart_bus.h>
46 #include <dev/uart/uart_dev_imx.h>
47 #include "uart_if.h"
48 
49 #include <arm/freescale/imx/imx_ccmvar.h>
50 
51 /*
52  * Low-level UART interface.
53  */
54 static int imx_uart_probe(struct uart_bas *bas);
55 static void imx_uart_init(struct uart_bas *bas, int, int, int, int);
56 static void imx_uart_term(struct uart_bas *bas);
57 static void imx_uart_putc(struct uart_bas *bas, int);
58 static int imx_uart_rxready(struct uart_bas *bas);
59 static int imx_uart_getc(struct uart_bas *bas, struct mtx *);
60 
61 static struct uart_ops uart_imx_uart_ops = {
62 	.probe = imx_uart_probe,
63 	.init = imx_uart_init,
64 	.term = imx_uart_term,
65 	.putc = imx_uart_putc,
66 	.rxready = imx_uart_rxready,
67 	.getc = imx_uart_getc,
68 };
69 
70 #if 0 /* Handy when debugging. */
71 static void
72 dumpregs(struct uart_bas *bas, const char * msg)
73 {
74 
75 	if (!bootverbose)
76 		return;
77 	printf("%s bsh 0x%08lx UCR1 0x%08x UCR2 0x%08x "
78 		"UCR3 0x%08x UCR4 0x%08x USR1 0x%08x USR2 0x%08x\n",
79 	    msg, bas->bsh,
80 	    GETREG(bas, REG(UCR1)), GETREG(bas, REG(UCR2)),
81 	    GETREG(bas, REG(UCR3)), GETREG(bas, REG(UCR4)),
82 	    GETREG(bas, REG(USR1)), GETREG(bas, REG(USR2)));
83 }
84 #endif
85 
86 static int
87 imx_uart_probe(struct uart_bas *bas)
88 {
89 
90 	return (0);
91 }
92 
93 static void
94 imx_uart_init(struct uart_bas *bas, int baudrate, int databits,
95     int stopbits, int parity)
96 {
97 	uint32_t baseclk, reg;
98 
99         /* Enable the device and the RX/TX channels. */
100 	SET(bas, REG(UCR1), FLD(UCR1, UARTEN));
101 	SET(bas, REG(UCR2), FLD(UCR2, RXEN) | FLD(UCR2, TXEN));
102 
103 	if (databits == 7)
104 		DIS(bas, UCR2, WS);
105 	else
106 		ENA(bas, UCR2, WS);
107 
108 	if (stopbits == 2)
109 		ENA(bas, UCR2, STPB);
110 	else
111 		DIS(bas, UCR2, STPB);
112 
113 	switch (parity) {
114 	case UART_PARITY_ODD:
115 		DIS(bas, UCR2, PROE);
116 		ENA(bas, UCR2, PREN);
117 		break;
118 	case UART_PARITY_EVEN:
119 		ENA(bas, UCR2, PROE);
120 		ENA(bas, UCR2, PREN);
121 		break;
122 	case UART_PARITY_MARK:
123 	case UART_PARITY_SPACE:
124                 /* FALLTHROUGH: Hardware doesn't support mark/space. */
125 	case UART_PARITY_NONE:
126 	default:
127 		DIS(bas, UCR2, PREN);
128 		break;
129 	}
130 
131 	/*
132 	 * The hardware has an extremely flexible baud clock: it allows setting
133 	 * both the numerator and denominator of the divider, as well as a
134 	 * separate pre-divider.  We simplify that problem by assuming a
135 	 * pre-divider and numerator of one because our base clock is so fast we
136 	 * can reach virtually any reasonable speed with a simple divisor.  The
137 	 * numerator value actually includes the 16x over-sampling; the register
138 	 * value is the numerator-1, so we have a hard-coded 15.  Note that a
139 	 * quirk of the hardware requires that both UBIR and UBMR be set back to
140 	 * back in order for the change to take effect.
141 	 */
142 	if (baudrate > 0) {
143 		baseclk = imx_ccm_uart_hz();
144 		reg = GETREG(bas, REG(UFCR));
145 		reg = (reg & ~IMXUART_UFCR_RFDIV_MASK) | IMXUART_UFCR_RFDIV_DIV1;
146 		SETREG(bas, REG(UFCR), reg);
147 		SETREG(bas, REG(UBIR), 15);
148 		SETREG(bas, REG(UBMR), (baseclk / baudrate) - 1);
149 	}
150 }
151 
152 static void
153 imx_uart_term(struct uart_bas *bas)
154 {
155 
156 }
157 
158 static void
159 imx_uart_putc(struct uart_bas *bas, int c)
160 {
161 
162 	while (!(IS(bas, USR2, TXFE)))
163 		;
164 	SETREG(bas, REG(UTXD), c);
165 }
166 
167 static int
168 imx_uart_rxready(struct uart_bas *bas)
169 {
170 
171 	return ((IS(bas, USR2, RDR)) ? 1 : 0);
172 }
173 
174 static int
175 imx_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
176 {
177 	int c;
178 
179 	uart_lock(hwmtx);
180 	while (!(IS(bas, USR2, RDR)))
181 		;
182 
183 	c = GETREG(bas, REG(URXD));
184 	uart_unlock(hwmtx);
185 #if defined(KDB)
186 	if (c & FLD(URXD, BRK)) {
187 		if (kdb_break())
188 			return (0);
189 	}
190 #endif
191 	return (c & 0xff);
192 }
193 
194 /*
195  * High-level UART interface.
196  */
197 struct imx_uart_softc {
198 	struct uart_softc base;
199 };
200 
201 static int imx_uart_bus_attach(struct uart_softc *);
202 static int imx_uart_bus_detach(struct uart_softc *);
203 static int imx_uart_bus_flush(struct uart_softc *, int);
204 static int imx_uart_bus_getsig(struct uart_softc *);
205 static int imx_uart_bus_ioctl(struct uart_softc *, int, intptr_t);
206 static int imx_uart_bus_ipend(struct uart_softc *);
207 static int imx_uart_bus_param(struct uart_softc *, int, int, int, int);
208 static int imx_uart_bus_probe(struct uart_softc *);
209 static int imx_uart_bus_receive(struct uart_softc *);
210 static int imx_uart_bus_setsig(struct uart_softc *, int);
211 static int imx_uart_bus_transmit(struct uart_softc *);
212 static void imx_uart_bus_grab(struct uart_softc *);
213 static void imx_uart_bus_ungrab(struct uart_softc *);
214 
215 static kobj_method_t imx_uart_methods[] = {
216 	KOBJMETHOD(uart_attach,		imx_uart_bus_attach),
217 	KOBJMETHOD(uart_detach,		imx_uart_bus_detach),
218 	KOBJMETHOD(uart_flush,		imx_uart_bus_flush),
219 	KOBJMETHOD(uart_getsig,		imx_uart_bus_getsig),
220 	KOBJMETHOD(uart_ioctl,		imx_uart_bus_ioctl),
221 	KOBJMETHOD(uart_ipend,		imx_uart_bus_ipend),
222 	KOBJMETHOD(uart_param,		imx_uart_bus_param),
223 	KOBJMETHOD(uart_probe,		imx_uart_bus_probe),
224 	KOBJMETHOD(uart_receive,	imx_uart_bus_receive),
225 	KOBJMETHOD(uart_setsig,		imx_uart_bus_setsig),
226 	KOBJMETHOD(uart_transmit,	imx_uart_bus_transmit),
227 	KOBJMETHOD(uart_grab,		imx_uart_bus_grab),
228 	KOBJMETHOD(uart_ungrab,		imx_uart_bus_ungrab),
229 	{ 0, 0 }
230 };
231 
232 struct uart_class uart_imx_class = {
233 	"imx",
234 	imx_uart_methods,
235 	sizeof(struct imx_uart_softc),
236 	.uc_ops = &uart_imx_uart_ops,
237 	.uc_range = 0x100,
238 	.uc_rclk = 24000000 /* TODO: get value from CCM */
239 };
240 
241 #define	SIGCHG(c, i, s, d)				\
242 	if (c) {					\
243 		i |= (i & s) ? s : s | d;		\
244 	} else {					\
245 		i = (i & s) ? (i & ~s) | d : i;		\
246 	}
247 
248 static int
249 imx_uart_bus_attach(struct uart_softc *sc)
250 {
251 	struct uart_bas *bas;
252 	struct uart_devinfo *di;
253 
254 	bas = &sc->sc_bas;
255 	if (sc->sc_sysdev != NULL) {
256 		di = sc->sc_sysdev;
257 		imx_uart_init(bas, di->baudrate, di->databits, di->stopbits,
258 		    di->parity);
259 	} else {
260 		imx_uart_init(bas, 115200, 8, 1, 0);
261 	}
262 
263 	(void)imx_uart_bus_getsig(sc);
264 
265 	ENA(bas, UCR4, DREN);
266 	DIS(bas, UCR1, RRDYEN);
267 	DIS(bas, UCR1, IDEN);
268 	DIS(bas, UCR3, RXDSEN);
269 	DIS(bas, UCR2, ATEN);
270 	DIS(bas, UCR1, TXMPTYEN);
271 	DIS(bas, UCR1, TRDYEN);
272 	DIS(bas, UCR4, TCEN);
273 	DIS(bas, UCR4, OREN);
274 	ENA(bas, UCR4, BKEN);
275 	DIS(bas, UCR4, WKEN);
276 	DIS(bas, UCR1, ADEN);
277 	DIS(bas, UCR3, ACIEN);
278 	DIS(bas, UCR2, ESCI);
279 	DIS(bas, UCR4, ENIRI);
280 	DIS(bas, UCR3, AIRINTEN);
281 	DIS(bas, UCR3, AWAKEN);
282 	DIS(bas, UCR3, FRAERREN);
283 	DIS(bas, UCR3, PARERREN);
284 	DIS(bas, UCR1, RTSDEN);
285 	DIS(bas, UCR2, RTSEN);
286 	DIS(bas, UCR3, DTREN);
287 	DIS(bas, UCR3, RI);
288 	DIS(bas, UCR3, DCD);
289 	DIS(bas, UCR3, DTRDEN);
290 	ENA(bas, UCR2, IRTS);
291 	ENA(bas, UCR3, RXDMUXSEL);
292 
293 	/* ACK all interrupts */
294 	SETREG(bas, REG(USR1), 0xffff);
295 	SETREG(bas, REG(USR2), 0xffff);
296 	return (0);
297 }
298 
299 static int
300 imx_uart_bus_detach(struct uart_softc *sc)
301 {
302 
303 	SETREG(&sc->sc_bas, REG(UCR4), 0);
304 
305 	return (0);
306 }
307 
308 static int
309 imx_uart_bus_flush(struct uart_softc *sc, int what)
310 {
311 
312 	/* TODO */
313 	return (0);
314 }
315 
316 static int
317 imx_uart_bus_getsig(struct uart_softc *sc)
318 {
319 	uint32_t new, old, sig;
320 	uint8_t bes;
321 
322 	do {
323 		old = sc->sc_hwsig;
324 		sig = old;
325 		uart_lock(sc->sc_hwmtx);
326 		bes = GETREG(&sc->sc_bas, REG(USR2));
327 		uart_unlock(sc->sc_hwmtx);
328 		/* XXX: chip can show delta */
329 		SIGCHG(bes & FLD(USR2, DCDIN), sig, SER_DCD, SER_DDCD);
330 		new = sig & ~SER_MASK_DELTA;
331 	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
332 
333 	return (sig);
334 }
335 
336 static int
337 imx_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
338 {
339 	struct uart_bas *bas;
340 	int error;
341 
342 	bas = &sc->sc_bas;
343 	error = 0;
344 	uart_lock(sc->sc_hwmtx);
345 	switch (request) {
346 	case UART_IOCTL_BREAK:
347 		/* TODO */
348 		break;
349 	case UART_IOCTL_BAUD:
350 		/* TODO */
351 		*(int*)data = 115200;
352 		break;
353 	default:
354 		error = EINVAL;
355 		break;
356 	}
357 	uart_unlock(sc->sc_hwmtx);
358 
359 	return (error);
360 }
361 
362 static int
363 imx_uart_bus_ipend(struct uart_softc *sc)
364 {
365 	struct uart_bas *bas;
366 	int ipend;
367 	uint32_t usr1, usr2;
368 	uint32_t ucr1, ucr4;
369 
370 	bas = &sc->sc_bas;
371 	ipend = 0;
372 
373 	uart_lock(sc->sc_hwmtx);
374 
375 	/* Read pending interrupts */
376 	usr1 = GETREG(bas, REG(USR1));
377 	usr2 = GETREG(bas, REG(USR2));
378 	/* ACK interrupts */
379 	SETREG(bas, REG(USR1), usr1);
380 	SETREG(bas, REG(USR2), usr2);
381 
382 	ucr1 = GETREG(bas, REG(UCR1));
383 	ucr4 = GETREG(bas, REG(UCR4));
384 
385 	if ((usr2 & FLD(USR2, TXFE)) && (ucr1 & FLD(UCR1, TXMPTYEN))) {
386 		DIS(bas, UCR1, TXMPTYEN);
387 		/* Continue TXing */
388 		ipend |= SER_INT_TXIDLE;
389 	}
390 	if ((usr2 & FLD(USR2, RDR)) && (ucr4 & FLD(UCR4, DREN))) {
391 		DIS(bas, UCR4, DREN);
392 		/* Wow, new char on input */
393 		ipend |= SER_INT_RXREADY;
394 	}
395 	if ((usr2 & FLD(USR2, BRCD)) && (ucr4 & FLD(UCR4, BKEN)))
396 		ipend |= SER_INT_BREAK;
397 
398 	uart_unlock(sc->sc_hwmtx);
399 
400 	return (ipend);
401 }
402 
403 static int
404 imx_uart_bus_param(struct uart_softc *sc, int baudrate, int databits,
405     int stopbits, int parity)
406 {
407 
408 	uart_lock(sc->sc_hwmtx);
409 	imx_uart_init(&sc->sc_bas, baudrate, databits, stopbits, parity);
410 	uart_unlock(sc->sc_hwmtx);
411 	return (0);
412 }
413 
414 static int
415 imx_uart_bus_probe(struct uart_softc *sc)
416 {
417 	int error;
418 
419 	error = imx_uart_probe(&sc->sc_bas);
420 	if (error)
421 		return (error);
422 
423 	sc->sc_rxfifosz = 1;
424 	sc->sc_txfifosz = 1;
425 
426 	device_set_desc(sc->sc_dev, "Freescale i.MX UART");
427 	return (0);
428 }
429 
430 static int
431 imx_uart_bus_receive(struct uart_softc *sc)
432 {
433 	struct uart_bas *bas;
434 	int xc, out;
435 
436 	bas = &sc->sc_bas;
437 	uart_lock(sc->sc_hwmtx);
438 
439 	/* Read while we have anything in FIFO */
440 	while (IS(bas, USR2, RDR)) {
441 		if (uart_rx_full(sc)) {
442 			/* No space left in input buffer */
443 			sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
444 			break;
445 		}
446 		out = 0;
447 		xc = GETREG(bas, REG(URXD));
448 
449 		/* We have valid char */
450 		if (xc & FLD(URXD, CHARRDY))
451 			out = xc & 0x000000ff;
452 
453 		if (xc & FLD(URXD, FRMERR))
454 			out |= UART_STAT_FRAMERR;
455 		if (xc & FLD(URXD, PRERR))
456 			out |= UART_STAT_PARERR;
457 		if (xc & FLD(URXD, OVRRUN))
458 			out |= UART_STAT_OVERRUN;
459 		if (xc & FLD(URXD, BRK))
460 			out |= UART_STAT_BREAK;
461 
462 		uart_rx_put(sc, out);
463 	}
464 	/* Reenable Data Ready interrupt */
465 	ENA(bas, UCR4, DREN);
466 
467 	uart_unlock(sc->sc_hwmtx);
468 	return (0);
469 }
470 
471 static int
472 imx_uart_bus_setsig(struct uart_softc *sc, int sig)
473 {
474 
475 	return (0);
476 }
477 
478 static int
479 imx_uart_bus_transmit(struct uart_softc *sc)
480 {
481 	struct uart_bas *bas = &sc->sc_bas;
482 	int i;
483 
484 	bas = &sc->sc_bas;
485 	uart_lock(sc->sc_hwmtx);
486 
487 	/* Fill TX FIFO */
488 	for (i = 0; i < sc->sc_txdatasz; i++) {
489 		SETREG(bas, REG(UTXD), sc->sc_txbuf[i] & 0xff);
490 	}
491 
492 	sc->sc_txbusy = 1;
493 	/* Call me when ready */
494 	ENA(bas, UCR1, TXMPTYEN);
495 
496 	uart_unlock(sc->sc_hwmtx);
497 
498 	return (0);
499 }
500 
501 static void
502 imx_uart_bus_grab(struct uart_softc *sc)
503 {
504 	struct uart_bas *bas = &sc->sc_bas;
505 
506 	bas = &sc->sc_bas;
507 	uart_lock(sc->sc_hwmtx);
508 	DIS(bas, UCR4, DREN);
509 	uart_unlock(sc->sc_hwmtx);
510 }
511 
512 static void
513 imx_uart_bus_ungrab(struct uart_softc *sc)
514 {
515 	struct uart_bas *bas = &sc->sc_bas;
516 
517 	bas = &sc->sc_bas;
518 	uart_lock(sc->sc_hwmtx);
519 	ENA(bas, UCR4, DREN);
520 	uart_unlock(sc->sc_hwmtx);
521 }
522