]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/samsung/exynos/exynos_uart.c
Add liblutok a lightweight C++ API for lua.
[FreeBSD/FreeBSD.git] / sys / arm / samsung / exynos / exynos_uart.c
1 /*
2  * Copyright (c) 2003 Marcel Moolenaar
3  * Copyright (c) 2007-2009 Andrew Turner
4  * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
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 ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/conf.h>
36 #include <sys/cons.h>
37 #include <sys/rman.h>
38 #include <machine/bus.h>
39 #include <machine/intr.h>
40
41 #include <dev/uart/uart.h>
42 #include <dev/uart/uart_cpu.h>
43 #include <dev/uart/uart_cpu_fdt.h>
44 #include <dev/uart/uart_bus.h>
45
46 #include <arm/samsung/exynos/exynos_uart.h>
47
48 #include "uart_if.h"
49
50 #define DEF_CLK         100000000
51
52 static int sscomspeed(long, long);
53 static int exynos4210_uart_param(struct uart_bas *, int, int, int, int);
54
55 /*
56  * Low-level UART interface.
57  */
58 static int exynos4210_probe(struct uart_bas *bas);
59 static void exynos4210_init(struct uart_bas *bas, int, int, int, int);
60 static void exynos4210_term(struct uart_bas *bas);
61 static void exynos4210_putc(struct uart_bas *bas, int);
62 static int exynos4210_rxready(struct uart_bas *bas);
63 static int exynos4210_getc(struct uart_bas *bas, struct mtx *mtx);
64
65 extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
66
67 static int
68 sscomspeed(long speed, long frequency)
69 {
70         int x;
71
72         if (speed <= 0 || frequency <= 0)
73                 return (-1);
74         x = (frequency / 16) / speed;
75         return (x-1);
76 }
77
78 static int
79 exynos4210_uart_param(struct uart_bas *bas, int baudrate, int databits,
80     int stopbits, int parity)
81 {
82         int brd, ulcon;
83
84         ulcon = 0;
85
86         switch(databits) {
87         case 5:
88                 ulcon |= ULCON_LENGTH_5;
89                 break;
90         case 6:
91                 ulcon |= ULCON_LENGTH_6;
92                 break;
93         case 7:
94                 ulcon |= ULCON_LENGTH_7;
95                 break;
96         case 8:
97                 ulcon |= ULCON_LENGTH_8;
98                 break;
99         default:
100                 return (EINVAL);
101         }
102
103         switch (parity) {
104         case UART_PARITY_NONE:
105                 ulcon |= ULCON_PARITY_NONE;
106                 break;
107         case UART_PARITY_ODD:
108                 ulcon |= ULCON_PARITY_ODD;
109                 break;
110         case UART_PARITY_EVEN:
111                 ulcon |= ULCON_PARITY_EVEN;
112                 break;
113         case UART_PARITY_MARK:
114         case UART_PARITY_SPACE:
115         default:
116                 return (EINVAL);
117         }
118
119         if (stopbits == 2)
120                 ulcon |= ULCON_STOP;
121
122         uart_setreg(bas, SSCOM_ULCON, ulcon);
123
124         brd = sscomspeed(baudrate, bas->rclk);
125         uart_setreg(bas, SSCOM_UBRDIV, brd);
126
127         return (0);
128 }
129
130 struct uart_ops uart_exynos4210_ops = {
131         .probe = exynos4210_probe,
132         .init = exynos4210_init,
133         .term = exynos4210_term,
134         .putc = exynos4210_putc,
135         .rxready = exynos4210_rxready,
136         .getc = exynos4210_getc,
137 };
138
139 static int
140 exynos4210_probe(struct uart_bas *bas)
141 {
142
143         return (0);
144 }
145
146 static void
147 exynos4210_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
148     int parity)
149 {
150
151         if (bas->rclk == 0)
152                 bas->rclk = DEF_CLK;
153
154         KASSERT(bas->rclk != 0, ("exynos4210_init: Invalid rclk"));
155
156         uart_setreg(bas, SSCOM_UCON, 0);
157         uart_setreg(bas, SSCOM_UFCON,
158             UFCON_TXTRIGGER_8 | UFCON_RXTRIGGER_8 |
159             UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET |
160             UFCON_FIFO_ENABLE);
161         exynos4210_uart_param(bas, baudrate, databits, stopbits, parity);
162
163         /* Enable UART. */
164         uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT |
165             UCON_TOINT);
166         uart_setreg(bas, SSCOM_UMCON, UMCON_RTS);
167 }
168
169 static void
170 exynos4210_term(struct uart_bas *bas)
171 {
172         /* XXX */
173 }
174
175 static void
176 exynos4210_putc(struct uart_bas *bas, int c)
177 {
178
179         while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) &
180                 UFSTAT_TXFULL) == UFSTAT_TXFULL)
181                 continue;
182
183         uart_setreg(bas, SSCOM_UTXH, c);
184 }
185
186 static int
187 exynos4210_rxready(struct uart_bas *bas)
188 {
189
190         return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) ==
191             UTRSTAT_RXREADY);
192 }
193
194 static int
195 exynos4210_getc(struct uart_bas *bas, struct mtx *mtx)
196 {
197         int utrstat;
198
199         utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT);
200         while (!(utrstat & UTRSTAT_RXREADY)) {
201                 utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT);
202                 continue;
203         }
204
205         return (bus_space_read_1(bas->bst, bas->bsh, SSCOM_URXH));
206 }
207
208 static int exynos4210_bus_probe(struct uart_softc *sc);
209 static int exynos4210_bus_attach(struct uart_softc *sc);
210 static int exynos4210_bus_flush(struct uart_softc *, int);
211 static int exynos4210_bus_getsig(struct uart_softc *);
212 static int exynos4210_bus_ioctl(struct uart_softc *, int, intptr_t);
213 static int exynos4210_bus_ipend(struct uart_softc *);
214 static int exynos4210_bus_param(struct uart_softc *, int, int, int, int);
215 static int exynos4210_bus_receive(struct uart_softc *);
216 static int exynos4210_bus_setsig(struct uart_softc *, int);
217 static int exynos4210_bus_transmit(struct uart_softc *);
218
219 static kobj_method_t exynos4210_methods[] = {
220         KOBJMETHOD(uart_probe,          exynos4210_bus_probe),
221         KOBJMETHOD(uart_attach,         exynos4210_bus_attach),
222         KOBJMETHOD(uart_flush,          exynos4210_bus_flush),
223         KOBJMETHOD(uart_getsig,         exynos4210_bus_getsig),
224         KOBJMETHOD(uart_ioctl,          exynos4210_bus_ioctl),
225         KOBJMETHOD(uart_ipend,          exynos4210_bus_ipend),
226         KOBJMETHOD(uart_param,          exynos4210_bus_param),
227         KOBJMETHOD(uart_receive,        exynos4210_bus_receive),
228         KOBJMETHOD(uart_setsig,         exynos4210_bus_setsig),
229         KOBJMETHOD(uart_transmit,       exynos4210_bus_transmit),
230
231         {0, 0 }
232 };
233
234 int
235 exynos4210_bus_probe(struct uart_softc *sc)
236 {
237
238         sc->sc_txfifosz = 16;
239         sc->sc_rxfifosz = 16;
240
241         return (0);
242 }
243
244 static int
245 exynos4210_bus_attach(struct uart_softc *sc)
246 {
247
248         sc->sc_hwiflow = 0;
249         sc->sc_hwoflow = 0;
250
251         return (0);
252 }
253
254 static int
255 exynos4210_bus_transmit(struct uart_softc *sc)
256 {
257         int i;
258         int reg;
259
260         uart_lock(sc->sc_hwmtx);
261
262         for (i = 0; i < sc->sc_txdatasz; i++) {
263                 exynos4210_putc(&sc->sc_bas, sc->sc_txbuf[i]);
264                 uart_barrier(&sc->sc_bas);
265         }
266
267         sc->sc_txbusy = 1;
268
269         uart_unlock(sc->sc_hwmtx);
270
271         /* unmask TX interrupt */
272         reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM);
273         reg &= ~(1 << 2);
274         bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM, reg);
275
276         return (0);
277 }
278
279 static int
280 exynos4210_bus_setsig(struct uart_softc *sc, int sig)
281 {
282
283         return (0);
284 }
285
286 static int
287 exynos4210_bus_receive(struct uart_softc *sc)
288 {
289         struct uart_bas *bas;
290
291         bas = &sc->sc_bas;
292         while (bus_space_read_4(bas->bst, bas->bsh,
293                 SSCOM_UFSTAT) & UFSTAT_RXCOUNT)
294                 uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH));
295
296         return (0);
297 }
298
299 static int
300 exynos4210_bus_param(struct uart_softc *sc, int baudrate, int databits,
301     int stopbits, int parity)
302 {
303         int error;
304
305         if (sc->sc_bas.rclk == 0)
306                 sc->sc_bas.rclk = DEF_CLK;
307
308         KASSERT(sc->sc_bas.rclk != 0, ("exynos4210_init: Invalid rclk"));
309
310         uart_lock(sc->sc_hwmtx);
311         error = exynos4210_uart_param(&sc->sc_bas, baudrate, databits, stopbits,
312             parity);
313         uart_unlock(sc->sc_hwmtx);
314
315         return (error);
316 }
317
318 static int
319 exynos4210_bus_ipend(struct uart_softc *sc)
320 {
321         uint32_t ints;
322         uint32_t txempty, rxready;
323         int reg;
324         int ipend;
325
326         uart_lock(sc->sc_hwmtx);
327         ints = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP);
328         bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP, ints);
329
330         txempty = (1 << 2);
331         rxready = (1 << 0);
332
333         ipend = 0;
334         if ((ints & txempty) > 0) {
335                 if (sc->sc_txbusy != 0)
336                         ipend |= SER_INT_TXIDLE;
337
338                 /* mask TX interrupt */
339                 reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh,
340                     SSCOM_UINTM);
341                 reg |= (1 << 2);
342                 bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh,
343                     SSCOM_UINTM, reg);
344         }
345
346         if ((ints & rxready) > 0) {
347                 ipend |= SER_INT_RXREADY;
348         }
349
350         uart_unlock(sc->sc_hwmtx);
351         return (ipend);
352 }
353
354 static int
355 exynos4210_bus_flush(struct uart_softc *sc, int what)
356 {
357
358         return (0);
359 }
360
361 static int
362 exynos4210_bus_getsig(struct uart_softc *sc)
363 {
364
365         return (0);
366 }
367
368 static int
369 exynos4210_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
370 {
371
372         return (EINVAL);
373 }
374
375 static struct uart_class uart_exynos4210_class = {
376         "exynos4210 class",
377         exynos4210_methods,
378         1,
379         .uc_ops = &uart_exynos4210_ops,
380         .uc_range = 8,
381         .uc_rclk = 0,
382         .uc_rshift = 0
383 };
384
385 static struct ofw_compat_data compat_data[] = {
386         {"exynos",              (uintptr_t)&uart_exynos4210_class},
387         {NULL,                  (uintptr_t)NULL},
388 };
389 UART_FDT_CLASS_AND_DEVICE(compat_data);