]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/samsung/exynos/exynos_uart.c
Change POSIX compliance level for visibility of strerror_l(3).
[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         {0, 0 }
231 };
232
233 int
234 exynos4210_bus_probe(struct uart_softc *sc)
235 {
236
237         sc->sc_txfifosz = 16;
238         sc->sc_rxfifosz = 16;
239
240         return (0);
241 }
242
243 static int
244 exynos4210_bus_attach(struct uart_softc *sc)
245 {
246
247         sc->sc_hwiflow = 0;
248         sc->sc_hwoflow = 0;
249
250         return (0);
251 }
252
253 static int
254 exynos4210_bus_transmit(struct uart_softc *sc)
255 {
256         int i;
257         int reg;
258
259         uart_lock(sc->sc_hwmtx);
260
261         for (i = 0; i < sc->sc_txdatasz; i++) {
262                 exynos4210_putc(&sc->sc_bas, sc->sc_txbuf[i]);
263                 uart_barrier(&sc->sc_bas);
264         }
265
266         sc->sc_txbusy = 1;
267
268         uart_unlock(sc->sc_hwmtx);
269
270         /* unmask TX interrupt */
271         reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM);
272         reg &= ~(1 << 2);
273         bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM, reg);
274
275         return (0);
276 }
277
278 static int
279 exynos4210_bus_setsig(struct uart_softc *sc, int sig)
280 {
281
282         return (0);
283 }
284
285 static int
286 exynos4210_bus_receive(struct uart_softc *sc)
287 {
288         struct uart_bas *bas;
289
290         bas = &sc->sc_bas;
291         while (bus_space_read_4(bas->bst, bas->bsh,
292                 SSCOM_UFSTAT) & UFSTAT_RXCOUNT)
293                 uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH));
294
295         return (0);
296 }
297
298 static int
299 exynos4210_bus_param(struct uart_softc *sc, int baudrate, int databits,
300     int stopbits, int parity)
301 {
302         int error;
303
304         if (sc->sc_bas.rclk == 0)
305                 sc->sc_bas.rclk = DEF_CLK;
306
307         KASSERT(sc->sc_bas.rclk != 0, ("exynos4210_init: Invalid rclk"));
308
309         uart_lock(sc->sc_hwmtx);
310         error = exynos4210_uart_param(&sc->sc_bas, baudrate, databits, stopbits,
311             parity);
312         uart_unlock(sc->sc_hwmtx);
313
314         return (error);
315 }
316
317 static int
318 exynos4210_bus_ipend(struct uart_softc *sc)
319 {
320         uint32_t ints;
321         uint32_t txempty, rxready;
322         int reg;
323         int ipend;
324
325         uart_lock(sc->sc_hwmtx);
326         ints = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP);
327         bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP, ints);
328
329         txempty = (1 << 2);
330         rxready = (1 << 0);
331
332         ipend = 0;
333         if ((ints & txempty) > 0) {
334                 if (sc->sc_txbusy != 0)
335                         ipend |= SER_INT_TXIDLE;
336
337                 /* mask TX interrupt */
338                 reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh,
339                     SSCOM_UINTM);
340                 reg |= (1 << 2);
341                 bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh,
342                     SSCOM_UINTM, reg);
343         }
344
345         if ((ints & rxready) > 0) {
346                 ipend |= SER_INT_RXREADY;
347         }
348
349         uart_unlock(sc->sc_hwmtx);
350         return (ipend);
351 }
352
353 static int
354 exynos4210_bus_flush(struct uart_softc *sc, int what)
355 {
356
357         return (0);
358 }
359
360 static int
361 exynos4210_bus_getsig(struct uart_softc *sc)
362 {
363
364         return (0);
365 }
366
367 static int
368 exynos4210_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
369 {
370
371         return (EINVAL);
372 }
373
374 static struct uart_class uart_exynos4210_class = {
375         "exynos4210 class",
376         exynos4210_methods,
377         1,
378         .uc_ops = &uart_exynos4210_ops,
379         .uc_range = 8,
380         .uc_rclk = 0,
381         .uc_rshift = 0
382 };
383
384 static struct ofw_compat_data compat_data[] = {
385         {"exynos",              (uintptr_t)&uart_exynos4210_class},
386         {NULL,                  (uintptr_t)NULL},
387 };
388 UART_FDT_CLASS_AND_DEVICE(compat_data);