xref: /freebsd/sys/dev/uart/uart_dev_imx.c (revision 864c53ead899f7838cd2e1cca3b485a4a82f5cdc)
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 the problem of coming up with a
135 	 * workable pair of numbers by assuming a pre-divider and numerator of
136 	 * one because our base clock is so fast we can reach virtually any
137 	 * reasonable speed with a simple divisor.  The numerator value actually
138 	 * includes the 16x over-sampling (so a value of 16 means divide by 1);
139 	 * the register value is the numerator-1, so we have a hard-coded 15.
140 	 * Note that a quirk of the hardware requires that both UBIR and UBMR be
141 	 * set back to back in order for the change to take effect.
142 	 */
143 	if (baudrate > 0) {
144 		baseclk = imx_ccm_uart_hz();
145 		reg = GETREG(bas, REG(UFCR));
146 		reg = (reg & ~IMXUART_UFCR_RFDIV_MASK) | IMXUART_UFCR_RFDIV_DIV1;
147 		SETREG(bas, REG(UFCR), reg);
148 		SETREG(bas, REG(UBIR), 15);
149 		SETREG(bas, REG(UBMR), (baseclk / baudrate) - 1);
150 	}
151 }
152 
153 static void
154 imx_uart_term(struct uart_bas *bas)
155 {
156 
157 }
158 
159 static void
160 imx_uart_putc(struct uart_bas *bas, int c)
161 {
162 
163 	while (!(IS(bas, USR2, TXFE)))
164 		;
165 	SETREG(bas, REG(UTXD), c);
166 }
167 
168 static int
169 imx_uart_rxready(struct uart_bas *bas)
170 {
171 
172 	return ((IS(bas, USR2, RDR)) ? 1 : 0);
173 }
174 
175 static int
176 imx_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
177 {
178 	int c;
179 
180 	uart_lock(hwmtx);
181 	while (!(IS(bas, USR2, RDR)))
182 		;
183 
184 	c = GETREG(bas, REG(URXD));
185 	uart_unlock(hwmtx);
186 #if defined(KDB)
187 	if (c & FLD(URXD, BRK)) {
188 		if (kdb_break())
189 			return (0);
190 	}
191 #endif
192 	return (c & 0xff);
193 }
194 
195 /*
196  * High-level UART interface.
197  */
198 struct imx_uart_softc {
199 	struct uart_softc base;
200 };
201 
202 static int imx_uart_bus_attach(struct uart_softc *);
203 static int imx_uart_bus_detach(struct uart_softc *);
204 static int imx_uart_bus_flush(struct uart_softc *, int);
205 static int imx_uart_bus_getsig(struct uart_softc *);
206 static int imx_uart_bus_ioctl(struct uart_softc *, int, intptr_t);
207 static int imx_uart_bus_ipend(struct uart_softc *);
208 static int imx_uart_bus_param(struct uart_softc *, int, int, int, int);
209 static int imx_uart_bus_probe(struct uart_softc *);
210 static int imx_uart_bus_receive(struct uart_softc *);
211 static int imx_uart_bus_setsig(struct uart_softc *, int);
212 static int imx_uart_bus_transmit(struct uart_softc *);
213 static void imx_uart_bus_grab(struct uart_softc *);
214 static void imx_uart_bus_ungrab(struct uart_softc *);
215 
216 static kobj_method_t imx_uart_methods[] = {
217 	KOBJMETHOD(uart_attach,		imx_uart_bus_attach),
218 	KOBJMETHOD(uart_detach,		imx_uart_bus_detach),
219 	KOBJMETHOD(uart_flush,		imx_uart_bus_flush),
220 	KOBJMETHOD(uart_getsig,		imx_uart_bus_getsig),
221 	KOBJMETHOD(uart_ioctl,		imx_uart_bus_ioctl),
222 	KOBJMETHOD(uart_ipend,		imx_uart_bus_ipend),
223 	KOBJMETHOD(uart_param,		imx_uart_bus_param),
224 	KOBJMETHOD(uart_probe,		imx_uart_bus_probe),
225 	KOBJMETHOD(uart_receive,	imx_uart_bus_receive),
226 	KOBJMETHOD(uart_setsig,		imx_uart_bus_setsig),
227 	KOBJMETHOD(uart_transmit,	imx_uart_bus_transmit),
228 	KOBJMETHOD(uart_grab,		imx_uart_bus_grab),
229 	KOBJMETHOD(uart_ungrab,		imx_uart_bus_ungrab),
230 	{ 0, 0 }
231 };
232 
233 struct uart_class uart_imx_class = {
234 	"imx",
235 	imx_uart_methods,
236 	sizeof(struct imx_uart_softc),
237 	.uc_ops = &uart_imx_uart_ops,
238 	.uc_range = 0x100,
239 	.uc_rclk = 24000000 /* TODO: get value from CCM */
240 };
241 
242 #define	SIGCHG(c, i, s, d)				\
243 	if (c) {					\
244 		i |= (i & s) ? s : s | d;		\
245 	} else {					\
246 		i = (i & s) ? (i & ~s) | d : i;		\
247 	}
248 
249 static int
250 imx_uart_bus_attach(struct uart_softc *sc)
251 {
252 	struct uart_bas *bas;
253 	struct uart_devinfo *di;
254 
255 	bas = &sc->sc_bas;
256 	if (sc->sc_sysdev != NULL) {
257 		di = sc->sc_sysdev;
258 		imx_uart_init(bas, di->baudrate, di->databits, di->stopbits,
259 		    di->parity);
260 	} else {
261 		imx_uart_init(bas, 115200, 8, 1, 0);
262 	}
263 
264 	(void)imx_uart_bus_getsig(sc);
265 
266 	ENA(bas, UCR4, DREN);
267 	DIS(bas, UCR1, RRDYEN);
268 	DIS(bas, UCR1, IDEN);
269 	DIS(bas, UCR3, RXDSEN);
270 	DIS(bas, UCR2, ATEN);
271 	DIS(bas, UCR1, TXMPTYEN);
272 	DIS(bas, UCR1, TRDYEN);
273 	DIS(bas, UCR4, TCEN);
274 	DIS(bas, UCR4, OREN);
275 	ENA(bas, UCR4, BKEN);
276 	DIS(bas, UCR4, WKEN);
277 	DIS(bas, UCR1, ADEN);
278 	DIS(bas, UCR3, ACIEN);
279 	DIS(bas, UCR2, ESCI);
280 	DIS(bas, UCR4, ENIRI);
281 	DIS(bas, UCR3, AIRINTEN);
282 	DIS(bas, UCR3, AWAKEN);
283 	DIS(bas, UCR3, FRAERREN);
284 	DIS(bas, UCR3, PARERREN);
285 	DIS(bas, UCR1, RTSDEN);
286 	DIS(bas, UCR2, RTSEN);
287 	DIS(bas, UCR3, DTREN);
288 	DIS(bas, UCR3, RI);
289 	DIS(bas, UCR3, DCD);
290 	DIS(bas, UCR3, DTRDEN);
291 	ENA(bas, UCR2, IRTS);
292 	ENA(bas, UCR3, RXDMUXSEL);
293 
294 	/* ACK all interrupts */
295 	SETREG(bas, REG(USR1), 0xffff);
296 	SETREG(bas, REG(USR2), 0xffff);
297 	return (0);
298 }
299 
300 static int
301 imx_uart_bus_detach(struct uart_softc *sc)
302 {
303 
304 	SETREG(&sc->sc_bas, REG(UCR4), 0);
305 
306 	return (0);
307 }
308 
309 static int
310 imx_uart_bus_flush(struct uart_softc *sc, int what)
311 {
312 
313 	/* TODO */
314 	return (0);
315 }
316 
317 static int
318 imx_uart_bus_getsig(struct uart_softc *sc)
319 {
320 	uint32_t new, old, sig;
321 	uint8_t bes;
322 
323 	do {
324 		old = sc->sc_hwsig;
325 		sig = old;
326 		uart_lock(sc->sc_hwmtx);
327 		bes = GETREG(&sc->sc_bas, REG(USR2));
328 		uart_unlock(sc->sc_hwmtx);
329 		/* XXX: chip can show delta */
330 		SIGCHG(bes & FLD(USR2, DCDIN), sig, SER_DCD, SER_DDCD);
331 		new = sig & ~SER_MASK_DELTA;
332 	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
333 
334 	return (sig);
335 }
336 
337 static int
338 imx_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
339 {
340 	struct uart_bas *bas;
341 	int error;
342 
343 	bas = &sc->sc_bas;
344 	error = 0;
345 	uart_lock(sc->sc_hwmtx);
346 	switch (request) {
347 	case UART_IOCTL_BREAK:
348 		/* TODO */
349 		break;
350 	case UART_IOCTL_BAUD:
351 		/* TODO */
352 		*(int*)data = 115200;
353 		break;
354 	default:
355 		error = EINVAL;
356 		break;
357 	}
358 	uart_unlock(sc->sc_hwmtx);
359 
360 	return (error);
361 }
362 
363 static int
364 imx_uart_bus_ipend(struct uart_softc *sc)
365 {
366 	struct uart_bas *bas;
367 	int ipend;
368 	uint32_t usr1, usr2;
369 	uint32_t ucr1, ucr4;
370 
371 	bas = &sc->sc_bas;
372 	ipend = 0;
373 
374 	uart_lock(sc->sc_hwmtx);
375 
376 	/* Read pending interrupts */
377 	usr1 = GETREG(bas, REG(USR1));
378 	usr2 = GETREG(bas, REG(USR2));
379 	/* ACK interrupts */
380 	SETREG(bas, REG(USR1), usr1);
381 	SETREG(bas, REG(USR2), usr2);
382 
383 	ucr1 = GETREG(bas, REG(UCR1));
384 	ucr4 = GETREG(bas, REG(UCR4));
385 
386 	if ((usr2 & FLD(USR2, TXFE)) && (ucr1 & FLD(UCR1, TXMPTYEN))) {
387 		DIS(bas, UCR1, TXMPTYEN);
388 		/* Continue TXing */
389 		ipend |= SER_INT_TXIDLE;
390 	}
391 	if ((usr2 & FLD(USR2, RDR)) && (ucr4 & FLD(UCR4, DREN))) {
392 		DIS(bas, UCR4, DREN);
393 		/* Wow, new char on input */
394 		ipend |= SER_INT_RXREADY;
395 	}
396 	if ((usr2 & FLD(USR2, BRCD)) && (ucr4 & FLD(UCR4, BKEN)))
397 		ipend |= SER_INT_BREAK;
398 
399 	uart_unlock(sc->sc_hwmtx);
400 
401 	return (ipend);
402 }
403 
404 static int
405 imx_uart_bus_param(struct uart_softc *sc, int baudrate, int databits,
406     int stopbits, int parity)
407 {
408 
409 	uart_lock(sc->sc_hwmtx);
410 	imx_uart_init(&sc->sc_bas, baudrate, databits, stopbits, parity);
411 	uart_unlock(sc->sc_hwmtx);
412 	return (0);
413 }
414 
415 static int
416 imx_uart_bus_probe(struct uart_softc *sc)
417 {
418 	int error;
419 
420 	error = imx_uart_probe(&sc->sc_bas);
421 	if (error)
422 		return (error);
423 
424 	sc->sc_rxfifosz = 1;
425 	sc->sc_txfifosz = 1;
426 
427 	device_set_desc(sc->sc_dev, "Freescale i.MX UART");
428 	return (0);
429 }
430 
431 static int
432 imx_uart_bus_receive(struct uart_softc *sc)
433 {
434 	struct uart_bas *bas;
435 	int xc, out;
436 
437 	bas = &sc->sc_bas;
438 	uart_lock(sc->sc_hwmtx);
439 
440 	/* Read while we have anything in FIFO */
441 	while (IS(bas, USR2, RDR)) {
442 		if (uart_rx_full(sc)) {
443 			/* No space left in input buffer */
444 			sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
445 			break;
446 		}
447 		out = 0;
448 		xc = GETREG(bas, REG(URXD));
449 
450 		/* We have valid char */
451 		if (xc & FLD(URXD, CHARRDY))
452 			out = xc & 0x000000ff;
453 
454 		if (xc & FLD(URXD, FRMERR))
455 			out |= UART_STAT_FRAMERR;
456 		if (xc & FLD(URXD, PRERR))
457 			out |= UART_STAT_PARERR;
458 		if (xc & FLD(URXD, OVRRUN))
459 			out |= UART_STAT_OVERRUN;
460 		if (xc & FLD(URXD, BRK))
461 			out |= UART_STAT_BREAK;
462 
463 		uart_rx_put(sc, out);
464 	}
465 	/* Reenable Data Ready interrupt */
466 	ENA(bas, UCR4, DREN);
467 
468 	uart_unlock(sc->sc_hwmtx);
469 	return (0);
470 }
471 
472 static int
473 imx_uart_bus_setsig(struct uart_softc *sc, int sig)
474 {
475 
476 	return (0);
477 }
478 
479 static int
480 imx_uart_bus_transmit(struct uart_softc *sc)
481 {
482 	struct uart_bas *bas = &sc->sc_bas;
483 	int i;
484 
485 	bas = &sc->sc_bas;
486 	uart_lock(sc->sc_hwmtx);
487 
488 	/* Fill TX FIFO */
489 	for (i = 0; i < sc->sc_txdatasz; i++) {
490 		SETREG(bas, REG(UTXD), sc->sc_txbuf[i] & 0xff);
491 	}
492 
493 	sc->sc_txbusy = 1;
494 	/* Call me when ready */
495 	ENA(bas, UCR1, TXMPTYEN);
496 
497 	uart_unlock(sc->sc_hwmtx);
498 
499 	return (0);
500 }
501 
502 static void
503 imx_uart_bus_grab(struct uart_softc *sc)
504 {
505 	struct uart_bas *bas = &sc->sc_bas;
506 
507 	bas = &sc->sc_bas;
508 	uart_lock(sc->sc_hwmtx);
509 	DIS(bas, UCR4, DREN);
510 	uart_unlock(sc->sc_hwmtx);
511 }
512 
513 static void
514 imx_uart_bus_ungrab(struct uart_softc *sc)
515 {
516 	struct uart_bas *bas = &sc->sc_bas;
517 
518 	bas = &sc->sc_bas;
519 	uart_lock(sc->sc_hwmtx);
520 	ENA(bas, UCR4, DREN);
521 	uart_unlock(sc->sc_hwmtx);
522 }
523