]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/uart/uart_dev_imx.c
Copy head (r256279) to stable/10 as part of the 10.0-RELEASE cycle.
[FreeBSD/stable/10.git] / sys / dev / uart / uart_dev_imx.c
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
47 #include <dev/uart/uart_dev_imx5xx.h>
48
49 #include "uart_if.h"
50 /*
51  * Low-level UART interface.
52  */
53 static int imx_uart_probe(struct uart_bas *bas);
54 static void imx_uart_init(struct uart_bas *bas, int, int, int, int);
55 static void imx_uart_term(struct uart_bas *bas);
56 static void imx_uart_putc(struct uart_bas *bas, int);
57 static int imx_uart_rxready(struct uart_bas *bas);
58 static int imx_uart_getc(struct uart_bas *bas, struct mtx *);
59
60 static struct uart_ops uart_imx_uart_ops = {
61         .probe = imx_uart_probe,
62         .init = imx_uart_init,
63         .term = imx_uart_term,
64         .putc = imx_uart_putc,
65         .rxready = imx_uart_rxready,
66         .getc = imx_uart_getc,
67 };
68
69 static int
70 imx_uart_probe(struct uart_bas *bas)
71 {
72
73         return (0);
74 }
75
76 static void
77 imx_uart_init(struct uart_bas *bas, int baudrate, int databits, 
78     int stopbits, int parity)
79 {
80
81 }
82
83 static void
84 imx_uart_term(struct uart_bas *bas)
85 {
86
87 }
88
89 static void
90 imx_uart_putc(struct uart_bas *bas, int c)
91 {
92
93         while (!(IS(bas, USR2, TXFE)))
94                 ;
95         SETREG(bas, REG(UTXD), c);
96 }
97
98 static int
99 imx_uart_rxready(struct uart_bas *bas)
100 {
101
102         return ((IS(bas, USR2, RDR)) ? 1 : 0);
103 }
104
105 static int
106 imx_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
107 {
108         int c;
109
110         uart_lock(hwmtx);
111         while (!(IS(bas, USR2, RDR)))
112                 ;
113
114         c = GETREG(bas, REG(URXD));
115         uart_unlock(hwmtx);
116 #if defined(KDB)
117         if (c & FLD(URXD, BRK)) {
118                 if (kdb_break())
119                         return (0);
120         }
121 #endif
122         return (c & 0xff);
123 }
124
125 /*
126  * High-level UART interface.
127  */
128 struct imx_uart_softc {
129         struct uart_softc base;
130 };
131
132 static int imx_uart_bus_attach(struct uart_softc *);
133 static int imx_uart_bus_detach(struct uart_softc *);
134 static int imx_uart_bus_flush(struct uart_softc *, int);
135 static int imx_uart_bus_getsig(struct uart_softc *);
136 static int imx_uart_bus_ioctl(struct uart_softc *, int, intptr_t);
137 static int imx_uart_bus_ipend(struct uart_softc *);
138 static int imx_uart_bus_param(struct uart_softc *, int, int, int, int);
139 static int imx_uart_bus_probe(struct uart_softc *);
140 static int imx_uart_bus_receive(struct uart_softc *);
141 static int imx_uart_bus_setsig(struct uart_softc *, int);
142 static int imx_uart_bus_transmit(struct uart_softc *);
143
144 static kobj_method_t imx_uart_methods[] = {
145         KOBJMETHOD(uart_attach,         imx_uart_bus_attach),
146         KOBJMETHOD(uart_detach,         imx_uart_bus_detach),
147         KOBJMETHOD(uart_flush,          imx_uart_bus_flush),
148         KOBJMETHOD(uart_getsig,         imx_uart_bus_getsig),
149         KOBJMETHOD(uart_ioctl,          imx_uart_bus_ioctl),
150         KOBJMETHOD(uart_ipend,          imx_uart_bus_ipend),
151         KOBJMETHOD(uart_param,          imx_uart_bus_param),
152         KOBJMETHOD(uart_probe,          imx_uart_bus_probe),
153         KOBJMETHOD(uart_receive,        imx_uart_bus_receive),
154         KOBJMETHOD(uart_setsig,         imx_uart_bus_setsig),
155         KOBJMETHOD(uart_transmit,       imx_uart_bus_transmit),
156         { 0, 0 }
157 };
158
159 struct uart_class uart_imx_class = {
160         "imx",
161         imx_uart_methods,
162         sizeof(struct imx_uart_softc),
163         .uc_ops = &uart_imx_uart_ops,
164         .uc_range = 0x100,
165         .uc_rclk = 24000000 /* TODO: get value from CCM */
166 };
167
168 #define SIGCHG(c, i, s, d)                              \
169         if (c) {                                        \
170                 i |= (i & s) ? s : s | d;               \
171         } else {                                        \
172                 i = (i & s) ? (i & ~s) | d : i;         \
173         }
174
175 static int
176 imx_uart_bus_attach(struct uart_softc *sc)
177 {
178         struct uart_bas *bas;
179         struct uart_devinfo *di;
180
181         bas = &sc->sc_bas;
182         if (sc->sc_sysdev != NULL) {
183                 di = sc->sc_sysdev;
184                 imx_uart_init(bas, di->baudrate, di->databits, di->stopbits,
185                     di->parity);
186         } else {
187                 imx_uart_init(bas, 115200, 8, 1, 0);
188         }
189
190         (void)imx_uart_bus_getsig(sc);
191
192         /* XXX workaround to have working console on manut prompt */
193         if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE){
194                 DIS(bas, UCR4, DREN);
195         } else {
196                 ENA(bas, UCR4, DREN);
197         }
198         DIS(bas, UCR1, RRDYEN);
199         DIS(bas, UCR1, IDEN);
200         DIS(bas, UCR3, RXDSEN);
201         DIS(bas, UCR2, ATEN);
202         DIS(bas, UCR1, TXMPTYEN);
203         DIS(bas, UCR1, TRDYEN);
204         DIS(bas, UCR4, TCEN);
205         DIS(bas, UCR4, OREN);
206         ENA(bas, UCR4, BKEN);
207         DIS(bas, UCR4, WKEN);
208         DIS(bas, UCR1, ADEN);
209         DIS(bas, UCR3, ACIEN);
210         DIS(bas, UCR2, ESCI);
211         DIS(bas, UCR4, ENIRI);
212         DIS(bas, UCR3, AIRINTEN);
213         DIS(bas, UCR3, AWAKEN);
214         DIS(bas, UCR3, FRAERREN);
215         DIS(bas, UCR3, PARERREN);
216         DIS(bas, UCR1, RTSDEN);
217         DIS(bas, UCR2, RTSEN);
218         DIS(bas, UCR3, DTREN);
219         DIS(bas, UCR3, RI);
220         DIS(bas, UCR3, DCD);
221         DIS(bas, UCR3, DTRDEN);
222
223         /* ACK all interrupts */
224         SETREG(bas, REG(USR1), 0xffff);
225         SETREG(bas, REG(USR2), 0xffff);
226         return (0);
227 }
228
229 static int
230 imx_uart_bus_detach(struct uart_softc *sc)
231 {
232
233         SETREG(&sc->sc_bas, REG(UCR4), 0);
234
235         return (0);
236 }
237
238 static int
239 imx_uart_bus_flush(struct uart_softc *sc, int what)
240 {
241
242         /* TODO */
243         return (0);
244 }
245
246 static int
247 imx_uart_bus_getsig(struct uart_softc *sc)
248 {
249         uint32_t new, old, sig;
250         uint8_t bes;
251
252         do {
253                 old = sc->sc_hwsig;
254                 sig = old;
255                 uart_lock(sc->sc_hwmtx);
256                 bes = GETREG(&sc->sc_bas, REG(USR2));
257                 uart_unlock(sc->sc_hwmtx);
258                 /* XXX: chip can show delta */
259                 SIGCHG(bes & FLD(USR2, DCDIN), sig, SER_DCD, SER_DDCD);
260                 new = sig & ~SER_MASK_DELTA;
261         } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
262
263         return (sig);
264 }
265
266 static int
267 imx_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
268 {
269         struct uart_bas *bas;
270         int error;
271
272         bas = &sc->sc_bas;
273         error = 0;
274         uart_lock(sc->sc_hwmtx);
275         switch (request) {
276         case UART_IOCTL_BREAK:
277                 /* TODO */
278                 break;
279         case UART_IOCTL_BAUD:
280                 /* TODO */
281                 *(int*)data = 115200;
282                 break;
283         default:
284                 error = EINVAL;
285                 break;
286         }
287         uart_unlock(sc->sc_hwmtx);
288
289         return (error);
290 }
291
292 static int
293 imx_uart_bus_ipend(struct uart_softc *sc)
294 {
295         struct uart_bas *bas;
296         int ipend;
297         uint32_t usr1, usr2;
298         uint32_t ucr1, ucr4;
299
300         bas = &sc->sc_bas;
301         ipend = 0;
302
303         uart_lock(sc->sc_hwmtx);
304
305         /* Read pending interrupts */
306         usr1 = GETREG(bas, REG(USR1));
307         usr2 = GETREG(bas, REG(USR2));
308         /* ACK interrupts */
309         SETREG(bas, REG(USR1), usr1);
310         SETREG(bas, REG(USR2), usr2);
311
312         ucr1 = GETREG(bas, REG(UCR1));
313         ucr4 = GETREG(bas, REG(UCR4));
314
315         if ((usr2 & FLD(USR2, TXFE)) && (ucr1 & FLD(UCR1, TXMPTYEN))) {
316                 DIS(bas, UCR1, TXMPTYEN);
317                 /* Continue TXing */
318                 ipend |= SER_INT_TXIDLE;
319         }
320         if ((usr2 & FLD(USR2, RDR)) && (ucr4 & FLD(UCR4, DREN))) {
321                 DIS(bas, UCR4, DREN);
322                 /* Wow, new char on input */
323                 ipend |= SER_INT_RXREADY;
324         }
325         if ((usr2 & FLD(USR2, BRCD)) && (ucr4 & FLD(UCR4, BKEN)))
326                 ipend |= SER_INT_BREAK;
327
328         uart_unlock(sc->sc_hwmtx);
329
330         return (ipend);
331 }
332
333 static int
334 imx_uart_bus_param(struct uart_softc *sc, int baudrate, int databits,
335     int stopbits, int parity)
336 {
337
338         uart_lock(sc->sc_hwmtx);
339         imx_uart_init(&sc->sc_bas, baudrate, databits, stopbits, parity);
340         uart_unlock(sc->sc_hwmtx);
341         return (0);
342 }
343
344 static int
345 imx_uart_bus_probe(struct uart_softc *sc)
346 {
347         int error;
348
349         error = imx_uart_probe(&sc->sc_bas);
350         if (error)
351                 return (error);
352
353         sc->sc_rxfifosz = 1;
354         sc->sc_txfifosz = 1;
355
356         device_set_desc(sc->sc_dev, "imx_uart");
357         return (0);
358 }
359
360 static int
361 imx_uart_bus_receive(struct uart_softc *sc)
362 {
363         struct uart_bas *bas;
364         int xc, out;
365
366         bas = &sc->sc_bas;
367         uart_lock(sc->sc_hwmtx);
368
369         /* Read while we have anything in FIFO */
370         while (IS(bas, USR2, RDR)) {
371                 if (uart_rx_full(sc)) {
372                         /* No space left in input buffer */
373                         sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
374                         break;
375                 }
376                 out = 0;
377                 xc = GETREG(bas, REG(URXD));
378
379                 /* We have valid char */
380                 if (xc & FLD(URXD, CHARRDY))
381                         out = xc & 0x000000ff;
382
383                 if (xc & FLD(URXD, FRMERR))
384                         out |= UART_STAT_FRAMERR;
385                 if (xc & FLD(URXD, PRERR))
386                         out |= UART_STAT_PARERR;
387                 if (xc & FLD(URXD, OVRRUN))
388                         out |= UART_STAT_OVERRUN;
389                 if (xc & FLD(URXD, BRK))
390                         out |= UART_STAT_BREAK;
391
392                 uart_rx_put(sc, out);
393         }
394         /* Reenable Data Ready interrupt */
395         ENA(bas, UCR4, DREN);
396
397         uart_unlock(sc->sc_hwmtx);
398         return (0);
399 }
400
401 static int
402 imx_uart_bus_setsig(struct uart_softc *sc, int sig)
403 {
404
405         /* TODO: implement (?) */
406
407         /* XXX workaround to have working console on mount prompt */
408         /* Enable RX interrupt */
409         if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE)
410                 if (!IS(&sc->sc_bas, UCR4, DREN))
411                         ENA(&sc->sc_bas, UCR4, DREN);
412         return (0);
413 }
414
415 static int
416 imx_uart_bus_transmit(struct uart_softc *sc)
417 {
418         struct uart_bas *bas = &sc->sc_bas;
419         int i;
420
421         bas = &sc->sc_bas;
422         uart_lock(sc->sc_hwmtx);
423
424         /* Fill TX FIFO */
425         for (i = 0; i < sc->sc_txdatasz; i++) {
426                 SETREG(bas, REG(UTXD), sc->sc_txbuf[i] & 0xff);
427         }
428
429         sc->sc_txbusy = 1;
430         /* Call me when ready */
431         ENA(bas, UCR1, TXMPTYEN);
432
433         uart_unlock(sc->sc_hwmtx);
434
435         return (0);
436 }