uart_dev_imx.c revision 272334
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: head/sys/dev/uart/uart_dev_imx.c 272334 2014-09-30 23:01:11Z ian $");
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 */
54static int imx_uart_probe(struct uart_bas *bas);
55static void imx_uart_init(struct uart_bas *bas, int, int, int, int);
56static void imx_uart_term(struct uart_bas *bas);
57static void imx_uart_putc(struct uart_bas *bas, int);
58static int imx_uart_rxready(struct uart_bas *bas);
59static int imx_uart_getc(struct uart_bas *bas, struct mtx *);
60
61static 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. */
71static void
72dumpregs(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
86static int
87imx_uart_probe(struct uart_bas *bas)
88{
89
90	return (0);
91}
92
93static u_int
94imx_uart_getbaud(struct uart_bas *bas)
95{
96	uint32_t rate, ubir, ubmr;
97	u_int baud, blo, bhi, i;
98	static const u_int predivs[] = {6, 5, 4, 3, 2, 1, 7, 1};
99	static const u_int std_rates[] = {
100		9600, 14400, 19200, 38400, 57600, 115200, 230400, 460800, 921600
101	};
102
103	/*
104	 * Get the baud rate the hardware is programmed for, then search the
105	 * table of standard baud rates for a number that's within 3% of the
106	 * actual rate the hardware is programmed for.  It's more comforting to
107	 * see that your console is running at 115200 than 114942.  Note that
108	 * here we cannot make a simplifying assumption that the predivider and
109	 * numerator are 1 (like we do when setting the baud rate), because we
110	 * don't know what u-boot might have set up.
111	 */
112	i = (GETREG(bas, REG(UFCR)) & IMXUART_UFCR_RFDIV_MASK) >>
113	    IMXUART_UFCR_RFDIV_SHIFT;
114	rate = imx_ccm_uart_hz() / predivs[i];
115	ubir = GETREG(bas, REG(UBIR)) + 1;
116	ubmr = GETREG(bas, REG(UBMR)) + 1;
117	baud = ((rate / 16 ) * ubir) / ubmr;
118
119	blo = (baud * 100) / 103;
120	bhi = (baud * 100) / 97;
121	for (i = 0; i < nitems(std_rates); i++) {
122		rate = std_rates[i];
123		if (rate >= blo && rate <= bhi) {
124			baud = rate;
125			break;
126		}
127	}
128
129	return (baud);
130}
131
132static void
133imx_uart_init(struct uart_bas *bas, int baudrate, int databits,
134    int stopbits, int parity)
135{
136	uint32_t baseclk, reg;
137
138        /* Enable the device and the RX/TX channels. */
139	SET(bas, REG(UCR1), FLD(UCR1, UARTEN));
140	SET(bas, REG(UCR2), FLD(UCR2, RXEN) | FLD(UCR2, TXEN));
141
142	if (databits == 7)
143		DIS(bas, UCR2, WS);
144	else
145		ENA(bas, UCR2, WS);
146
147	if (stopbits == 2)
148		ENA(bas, UCR2, STPB);
149	else
150		DIS(bas, UCR2, STPB);
151
152	switch (parity) {
153	case UART_PARITY_ODD:
154		DIS(bas, UCR2, PROE);
155		ENA(bas, UCR2, PREN);
156		break;
157	case UART_PARITY_EVEN:
158		ENA(bas, UCR2, PROE);
159		ENA(bas, UCR2, PREN);
160		break;
161	case UART_PARITY_MARK:
162	case UART_PARITY_SPACE:
163                /* FALLTHROUGH: Hardware doesn't support mark/space. */
164	case UART_PARITY_NONE:
165	default:
166		DIS(bas, UCR2, PREN);
167		break;
168	}
169
170	/*
171	 * The hardware has an extremely flexible baud clock: it allows setting
172	 * both the numerator and denominator of the divider, as well as a
173	 * separate pre-divider.  We simplify the problem of coming up with a
174	 * workable pair of numbers by assuming a pre-divider and numerator of
175	 * one because our base clock is so fast we can reach virtually any
176	 * reasonable speed with a simple divisor.  The numerator value actually
177	 * includes the 16x over-sampling (so a value of 16 means divide by 1);
178	 * the register value is the numerator-1, so we have a hard-coded 15.
179	 * Note that a quirk of the hardware requires that both UBIR and UBMR be
180	 * set back to back in order for the change to take effect.
181	 */
182	if (baudrate > 0) {
183		baseclk = imx_ccm_uart_hz();
184		reg = GETREG(bas, REG(UFCR));
185		reg = (reg & ~IMXUART_UFCR_RFDIV_MASK) | IMXUART_UFCR_RFDIV_DIV1;
186		SETREG(bas, REG(UFCR), reg);
187		SETREG(bas, REG(UBIR), 15);
188		SETREG(bas, REG(UBMR), (baseclk / baudrate) - 1);
189	}
190}
191
192static void
193imx_uart_term(struct uart_bas *bas)
194{
195
196}
197
198static void
199imx_uart_putc(struct uart_bas *bas, int c)
200{
201
202	while (!(IS(bas, USR2, TXFE)))
203		;
204	SETREG(bas, REG(UTXD), c);
205}
206
207static int
208imx_uart_rxready(struct uart_bas *bas)
209{
210
211	return ((IS(bas, USR2, RDR)) ? 1 : 0);
212}
213
214static int
215imx_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
216{
217	int c;
218
219	uart_lock(hwmtx);
220	while (!(IS(bas, USR2, RDR)))
221		;
222
223	c = GETREG(bas, REG(URXD));
224	uart_unlock(hwmtx);
225#if defined(KDB)
226	if (c & FLD(URXD, BRK)) {
227		if (kdb_break())
228			return (0);
229	}
230#endif
231	return (c & 0xff);
232}
233
234/*
235 * High-level UART interface.
236 */
237struct imx_uart_softc {
238	struct uart_softc base;
239};
240
241static int imx_uart_bus_attach(struct uart_softc *);
242static int imx_uart_bus_detach(struct uart_softc *);
243static int imx_uart_bus_flush(struct uart_softc *, int);
244static int imx_uart_bus_getsig(struct uart_softc *);
245static int imx_uart_bus_ioctl(struct uart_softc *, int, intptr_t);
246static int imx_uart_bus_ipend(struct uart_softc *);
247static int imx_uart_bus_param(struct uart_softc *, int, int, int, int);
248static int imx_uart_bus_probe(struct uart_softc *);
249static int imx_uart_bus_receive(struct uart_softc *);
250static int imx_uart_bus_setsig(struct uart_softc *, int);
251static int imx_uart_bus_transmit(struct uart_softc *);
252static void imx_uart_bus_grab(struct uart_softc *);
253static void imx_uart_bus_ungrab(struct uart_softc *);
254
255static kobj_method_t imx_uart_methods[] = {
256	KOBJMETHOD(uart_attach,		imx_uart_bus_attach),
257	KOBJMETHOD(uart_detach,		imx_uart_bus_detach),
258	KOBJMETHOD(uart_flush,		imx_uart_bus_flush),
259	KOBJMETHOD(uart_getsig,		imx_uart_bus_getsig),
260	KOBJMETHOD(uart_ioctl,		imx_uart_bus_ioctl),
261	KOBJMETHOD(uart_ipend,		imx_uart_bus_ipend),
262	KOBJMETHOD(uart_param,		imx_uart_bus_param),
263	KOBJMETHOD(uart_probe,		imx_uart_bus_probe),
264	KOBJMETHOD(uart_receive,	imx_uart_bus_receive),
265	KOBJMETHOD(uart_setsig,		imx_uart_bus_setsig),
266	KOBJMETHOD(uart_transmit,	imx_uart_bus_transmit),
267	KOBJMETHOD(uart_grab,		imx_uart_bus_grab),
268	KOBJMETHOD(uart_ungrab,		imx_uart_bus_ungrab),
269	{ 0, 0 }
270};
271
272struct uart_class uart_imx_class = {
273	"imx",
274	imx_uart_methods,
275	sizeof(struct imx_uart_softc),
276	.uc_ops = &uart_imx_uart_ops,
277	.uc_range = 0x100,
278	.uc_rclk = 24000000 /* TODO: get value from CCM */
279};
280
281#define	SIGCHG(c, i, s, d)				\
282	if (c) {					\
283		i |= (i & s) ? s : s | d;		\
284	} else {					\
285		i = (i & s) ? (i & ~s) | d : i;		\
286	}
287
288static int
289imx_uart_bus_attach(struct uart_softc *sc)
290{
291	struct uart_bas *bas;
292	struct uart_devinfo *di;
293
294	bas = &sc->sc_bas;
295	if (sc->sc_sysdev != NULL) {
296		di = sc->sc_sysdev;
297		imx_uart_init(bas, di->baudrate, di->databits, di->stopbits,
298		    di->parity);
299	} else {
300		imx_uart_init(bas, 115200, 8, 1, 0);
301	}
302
303	(void)imx_uart_bus_getsig(sc);
304
305	ENA(bas, UCR4, DREN);
306	DIS(bas, UCR1, RRDYEN);
307	DIS(bas, UCR1, IDEN);
308	DIS(bas, UCR3, RXDSEN);
309	DIS(bas, UCR2, ATEN);
310	DIS(bas, UCR1, TXMPTYEN);
311	DIS(bas, UCR1, TRDYEN);
312	DIS(bas, UCR4, TCEN);
313	DIS(bas, UCR4, OREN);
314	ENA(bas, UCR4, BKEN);
315	DIS(bas, UCR4, WKEN);
316	DIS(bas, UCR1, ADEN);
317	DIS(bas, UCR3, ACIEN);
318	DIS(bas, UCR2, ESCI);
319	DIS(bas, UCR4, ENIRI);
320	DIS(bas, UCR3, AIRINTEN);
321	DIS(bas, UCR3, AWAKEN);
322	DIS(bas, UCR3, FRAERREN);
323	DIS(bas, UCR3, PARERREN);
324	DIS(bas, UCR1, RTSDEN);
325	DIS(bas, UCR2, RTSEN);
326	DIS(bas, UCR3, DTREN);
327	DIS(bas, UCR3, RI);
328	DIS(bas, UCR3, DCD);
329	DIS(bas, UCR3, DTRDEN);
330	ENA(bas, UCR2, IRTS);
331	ENA(bas, UCR3, RXDMUXSEL);
332
333	/* ACK all interrupts */
334	SETREG(bas, REG(USR1), 0xffff);
335	SETREG(bas, REG(USR2), 0xffff);
336	return (0);
337}
338
339static int
340imx_uart_bus_detach(struct uart_softc *sc)
341{
342
343	SETREG(&sc->sc_bas, REG(UCR4), 0);
344
345	return (0);
346}
347
348static int
349imx_uart_bus_flush(struct uart_softc *sc, int what)
350{
351
352	/* TODO */
353	return (0);
354}
355
356static int
357imx_uart_bus_getsig(struct uart_softc *sc)
358{
359	uint32_t new, old, sig;
360	uint8_t bes;
361
362	do {
363		old = sc->sc_hwsig;
364		sig = old;
365		uart_lock(sc->sc_hwmtx);
366		bes = GETREG(&sc->sc_bas, REG(USR2));
367		uart_unlock(sc->sc_hwmtx);
368		/* XXX: chip can show delta */
369		SIGCHG(bes & FLD(USR2, DCDIN), sig, SER_DCD, SER_DDCD);
370		new = sig & ~SER_MASK_DELTA;
371	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
372
373	return (sig);
374}
375
376static int
377imx_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
378{
379	struct uart_bas *bas;
380	int error;
381
382	bas = &sc->sc_bas;
383	error = 0;
384	uart_lock(sc->sc_hwmtx);
385	switch (request) {
386	case UART_IOCTL_BREAK:
387		/* TODO */
388		break;
389	case UART_IOCTL_BAUD:
390		*(u_int*)data = imx_uart_getbaud(bas);
391		break;
392	default:
393		error = EINVAL;
394		break;
395	}
396	uart_unlock(sc->sc_hwmtx);
397
398	return (error);
399}
400
401static int
402imx_uart_bus_ipend(struct uart_softc *sc)
403{
404	struct uart_bas *bas;
405	int ipend;
406	uint32_t usr1, usr2;
407	uint32_t ucr1, ucr4;
408
409	bas = &sc->sc_bas;
410	ipend = 0;
411
412	uart_lock(sc->sc_hwmtx);
413
414	/* Read pending interrupts */
415	usr1 = GETREG(bas, REG(USR1));
416	usr2 = GETREG(bas, REG(USR2));
417	/* ACK interrupts */
418	SETREG(bas, REG(USR1), usr1);
419	SETREG(bas, REG(USR2), usr2);
420
421	ucr1 = GETREG(bas, REG(UCR1));
422	ucr4 = GETREG(bas, REG(UCR4));
423
424	if ((usr2 & FLD(USR2, TXFE)) && (ucr1 & FLD(UCR1, TXMPTYEN))) {
425		DIS(bas, UCR1, TXMPTYEN);
426		/* Continue TXing */
427		ipend |= SER_INT_TXIDLE;
428	}
429	if ((usr2 & FLD(USR2, RDR)) && (ucr4 & FLD(UCR4, DREN))) {
430		DIS(bas, UCR4, DREN);
431		/* Wow, new char on input */
432		ipend |= SER_INT_RXREADY;
433	}
434	if ((usr2 & FLD(USR2, BRCD)) && (ucr4 & FLD(UCR4, BKEN)))
435		ipend |= SER_INT_BREAK;
436
437	uart_unlock(sc->sc_hwmtx);
438
439	return (ipend);
440}
441
442static int
443imx_uart_bus_param(struct uart_softc *sc, int baudrate, int databits,
444    int stopbits, int parity)
445{
446
447	uart_lock(sc->sc_hwmtx);
448	imx_uart_init(&sc->sc_bas, baudrate, databits, stopbits, parity);
449	uart_unlock(sc->sc_hwmtx);
450	return (0);
451}
452
453static int
454imx_uart_bus_probe(struct uart_softc *sc)
455{
456	int error;
457
458	error = imx_uart_probe(&sc->sc_bas);
459	if (error)
460		return (error);
461
462	sc->sc_rxfifosz = 1;
463	sc->sc_txfifosz = 1;
464
465	device_set_desc(sc->sc_dev, "Freescale i.MX UART");
466	return (0);
467}
468
469static int
470imx_uart_bus_receive(struct uart_softc *sc)
471{
472	struct uart_bas *bas;
473	int xc, out;
474
475	bas = &sc->sc_bas;
476	uart_lock(sc->sc_hwmtx);
477
478	/* Read while we have anything in FIFO */
479	while (IS(bas, USR2, RDR)) {
480		if (uart_rx_full(sc)) {
481			/* No space left in input buffer */
482			sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
483			break;
484		}
485		out = 0;
486		xc = GETREG(bas, REG(URXD));
487
488		/* We have valid char */
489		if (xc & FLD(URXD, CHARRDY))
490			out = xc & 0x000000ff;
491
492		if (xc & FLD(URXD, FRMERR))
493			out |= UART_STAT_FRAMERR;
494		if (xc & FLD(URXD, PRERR))
495			out |= UART_STAT_PARERR;
496		if (xc & FLD(URXD, OVRRUN))
497			out |= UART_STAT_OVERRUN;
498		if (xc & FLD(URXD, BRK))
499			out |= UART_STAT_BREAK;
500
501		uart_rx_put(sc, out);
502	}
503	/* Reenable Data Ready interrupt */
504	ENA(bas, UCR4, DREN);
505
506	uart_unlock(sc->sc_hwmtx);
507	return (0);
508}
509
510static int
511imx_uart_bus_setsig(struct uart_softc *sc, int sig)
512{
513
514	return (0);
515}
516
517static int
518imx_uart_bus_transmit(struct uart_softc *sc)
519{
520	struct uart_bas *bas = &sc->sc_bas;
521	int i;
522
523	bas = &sc->sc_bas;
524	uart_lock(sc->sc_hwmtx);
525
526	/* Fill TX FIFO */
527	for (i = 0; i < sc->sc_txdatasz; i++) {
528		SETREG(bas, REG(UTXD), sc->sc_txbuf[i] & 0xff);
529	}
530
531	sc->sc_txbusy = 1;
532	/* Call me when ready */
533	ENA(bas, UCR1, TXMPTYEN);
534
535	uart_unlock(sc->sc_hwmtx);
536
537	return (0);
538}
539
540static void
541imx_uart_bus_grab(struct uart_softc *sc)
542{
543	struct uart_bas *bas = &sc->sc_bas;
544
545	bas = &sc->sc_bas;
546	uart_lock(sc->sc_hwmtx);
547	DIS(bas, UCR4, DREN);
548	uart_unlock(sc->sc_hwmtx);
549}
550
551static void
552imx_uart_bus_ungrab(struct uart_softc *sc)
553{
554	struct uart_bas *bas = &sc->sc_bas;
555
556	bas = &sc->sc_bas;
557	uart_lock(sc->sc_hwmtx);
558	ENA(bas, UCR4, DREN);
559	uart_unlock(sc->sc_hwmtx);
560}
561