]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/arm/samsung/exynos/exynos_uart.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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/tty.h>
38 #include <sys/rman.h>
39 #include <machine/bus.h>
40 #include <machine/intr.h>
41
42 #include <dev/uart/uart.h>
43 #include <dev/uart/uart_cpu.h>
44 #include <dev/uart/uart_cpu_fdt.h>
45 #include <dev/uart/uart_bus.h>
46
47 #include <arm/samsung/exynos/exynos_uart.h>
48
49 #include "uart_if.h"
50
51 #define DEF_CLK         100000000
52
53 static int sscomspeed(long, long);
54 static int exynos4210_uart_param(struct uart_bas *, int, int, int, int);
55
56 /*
57  * Low-level UART interface.
58  */
59 static int exynos4210_probe(struct uart_bas *bas);
60 static void exynos4210_init(struct uart_bas *bas, int, int, int, int);
61 static void exynos4210_term(struct uart_bas *bas);
62 static void exynos4210_putc(struct uart_bas *bas, int);
63 static int exynos4210_rxready(struct uart_bas *bas);
64 static int exynos4210_getc(struct uart_bas *bas, struct mtx *mtx);
65
66 extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
67
68 static int
69 sscomspeed(long speed, long frequency)
70 {
71         int x;
72
73         if (speed <= 0 || frequency <= 0)
74                 return (-1);
75         x = (frequency / 16) / speed;
76         return (x-1);
77 }
78
79 static int
80 exynos4210_uart_param(struct uart_bas *bas, int baudrate, int databits,
81     int stopbits, int parity)
82 {
83         int brd, ulcon;
84
85         ulcon = 0;
86
87         switch(databits) {
88         case 5:
89                 ulcon |= ULCON_LENGTH_5;
90                 break;
91         case 6:
92                 ulcon |= ULCON_LENGTH_6;
93                 break;
94         case 7:
95                 ulcon |= ULCON_LENGTH_7;
96                 break;
97         case 8:
98                 ulcon |= ULCON_LENGTH_8;
99                 break;
100         default:
101                 return (EINVAL);
102         }
103
104         switch (parity) {
105         case UART_PARITY_NONE:
106                 ulcon |= ULCON_PARITY_NONE;
107                 break;
108         case UART_PARITY_ODD:
109                 ulcon |= ULCON_PARITY_ODD;
110                 break;
111         case UART_PARITY_EVEN:
112                 ulcon |= ULCON_PARITY_EVEN;
113                 break;
114         case UART_PARITY_MARK:
115         case UART_PARITY_SPACE:
116         default:
117                 return (EINVAL);
118         }
119
120         if (stopbits == 2)
121                 ulcon |= ULCON_STOP;
122
123         uart_setreg(bas, SSCOM_ULCON, ulcon);
124
125         brd = sscomspeed(baudrate, bas->rclk);
126         uart_setreg(bas, SSCOM_UBRDIV, brd);
127
128         return (0);
129 }
130
131 struct uart_ops uart_exynos4210_ops = {
132         .probe = exynos4210_probe,
133         .init = exynos4210_init,
134         .term = exynos4210_term,
135         .putc = exynos4210_putc,
136         .rxready = exynos4210_rxready,
137         .getc = exynos4210_getc,
138 };
139
140 static int
141 exynos4210_probe(struct uart_bas *bas)
142 {
143
144         return (0);
145 }
146
147 static void
148 exynos4210_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
149     int parity)
150 {
151
152         if (bas->rclk == 0)
153                 bas->rclk = DEF_CLK;
154
155         KASSERT(bas->rclk != 0, ("exynos4210_init: Invalid rclk"));
156
157         uart_setreg(bas, SSCOM_UCON, 0);
158         uart_setreg(bas, SSCOM_UFCON,
159             UFCON_TXTRIGGER_8 | UFCON_RXTRIGGER_8 |
160             UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET |
161             UFCON_FIFO_ENABLE);
162         exynos4210_uart_param(bas, baudrate, databits, stopbits, parity);
163
164         /* Enable UART. */
165         uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT |
166             UCON_TOINT);
167         uart_setreg(bas, SSCOM_UMCON, UMCON_RTS);
168 }
169
170 static void
171 exynos4210_term(struct uart_bas *bas)
172 {
173         /* XXX */
174 }
175
176 static void
177 exynos4210_putc(struct uart_bas *bas, int c)
178 {
179
180         while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) &
181                 UFSTAT_TXFULL) == UFSTAT_TXFULL)
182                 continue;
183
184         uart_setreg(bas, SSCOM_UTXH, c);
185 }
186
187 static int
188 exynos4210_rxready(struct uart_bas *bas)
189 {
190
191         return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) ==
192             UTRSTAT_RXREADY);
193 }
194
195 static int
196 exynos4210_getc(struct uart_bas *bas, struct mtx *mtx)
197 {
198         int utrstat;
199
200         utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT);
201         while (!(utrstat & UTRSTAT_RXREADY)) {
202                 utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT);
203                 continue;
204         }
205
206         return (bus_space_read_1(bas->bst, bas->bsh, SSCOM_URXH));
207 }
208
209 static int exynos4210_bus_probe(struct uart_softc *sc);
210 static int exynos4210_bus_attach(struct uart_softc *sc);
211 static int exynos4210_bus_flush(struct uart_softc *, int);
212 static int exynos4210_bus_getsig(struct uart_softc *);
213 static int exynos4210_bus_ioctl(struct uart_softc *, int, intptr_t);
214 static int exynos4210_bus_ipend(struct uart_softc *);
215 static int exynos4210_bus_param(struct uart_softc *, int, int, int, int);
216 static int exynos4210_bus_receive(struct uart_softc *);
217 static int exynos4210_bus_setsig(struct uart_softc *, int);
218 static int exynos4210_bus_transmit(struct uart_softc *);
219
220 static kobj_method_t exynos4210_methods[] = {
221         KOBJMETHOD(uart_probe,          exynos4210_bus_probe),
222         KOBJMETHOD(uart_attach,         exynos4210_bus_attach),
223         KOBJMETHOD(uart_flush,          exynos4210_bus_flush),
224         KOBJMETHOD(uart_getsig,         exynos4210_bus_getsig),
225         KOBJMETHOD(uart_ioctl,          exynos4210_bus_ioctl),
226         KOBJMETHOD(uart_ipend,          exynos4210_bus_ipend),
227         KOBJMETHOD(uart_param,          exynos4210_bus_param),
228         KOBJMETHOD(uart_receive,        exynos4210_bus_receive),
229         KOBJMETHOD(uart_setsig,         exynos4210_bus_setsig),
230         KOBJMETHOD(uart_transmit,       exynos4210_bus_transmit),
231
232         {0, 0 }
233 };
234
235 int
236 exynos4210_bus_probe(struct uart_softc *sc)
237 {
238
239         sc->sc_txfifosz = 16;
240         sc->sc_rxfifosz = 16;
241
242         return (0);
243 }
244
245 static int
246 exynos4210_bus_attach(struct uart_softc *sc)
247 {
248
249         sc->sc_hwiflow = 0;
250         sc->sc_hwoflow = 0;
251
252         return (0);
253 }
254
255 static int
256 exynos4210_bus_transmit(struct uart_softc *sc)
257 {
258         int i;
259         int reg;
260
261         uart_lock(sc->sc_hwmtx);
262
263         for (i = 0; i < sc->sc_txdatasz; i++) {
264                 exynos4210_putc(&sc->sc_bas, sc->sc_txbuf[i]);
265                 uart_barrier(&sc->sc_bas);
266         }
267
268         sc->sc_txbusy = 1;
269
270         uart_unlock(sc->sc_hwmtx);
271
272         /* unmask TX interrupt */
273         reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM);
274         reg &= ~(1 << 2);
275         bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM, reg);
276
277         return (0);
278 }
279
280 static int
281 exynos4210_bus_setsig(struct uart_softc *sc, int sig)
282 {
283
284         return (0);
285 }
286
287 static int
288 exynos4210_bus_receive(struct uart_softc *sc)
289 {
290         struct uart_bas *bas;
291
292         bas = &sc->sc_bas;
293         while (bus_space_read_4(bas->bst, bas->bsh,
294                 SSCOM_UFSTAT) & UFSTAT_RXCOUNT)
295                 uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH));
296
297         return (0);
298 }
299
300 static int
301 exynos4210_bus_param(struct uart_softc *sc, int baudrate, int databits,
302     int stopbits, int parity)
303 {
304         int error;
305
306         if (sc->sc_bas.rclk == 0)
307                 sc->sc_bas.rclk = DEF_CLK;
308
309         KASSERT(sc->sc_bas.rclk != 0, ("exynos4210_init: Invalid rclk"));
310
311         uart_lock(sc->sc_hwmtx);
312         error = exynos4210_uart_param(&sc->sc_bas, baudrate, databits, stopbits,
313             parity);
314         uart_unlock(sc->sc_hwmtx);
315
316         return (error);
317 }
318
319 static int
320 exynos4210_bus_ipend(struct uart_softc *sc)
321 {
322         uint32_t ints;
323         uint32_t txempty, rxready;
324         int reg;
325         int ipend;
326
327         uart_lock(sc->sc_hwmtx);
328         ints = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP);
329         bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP, ints);
330
331         txempty = (1 << 2);
332         rxready = (1 << 0);
333
334         ipend = 0;
335         if ((ints & txempty) > 0) {
336                 if (sc->sc_txbusy != 0)
337                         ipend |= SER_INT_TXIDLE;
338
339                 /* mask TX interrupt */
340                 reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh,
341                     SSCOM_UINTM);
342                 reg |= (1 << 2);
343                 bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh,
344                     SSCOM_UINTM, reg);
345         }
346
347         if ((ints & rxready) > 0) {
348                 ipend |= SER_INT_RXREADY;
349         }
350
351         uart_unlock(sc->sc_hwmtx);
352         return (ipend);
353 }
354
355 static int
356 exynos4210_bus_flush(struct uart_softc *sc, int what)
357 {
358
359         return (0);
360 }
361
362 static int
363 exynos4210_bus_getsig(struct uart_softc *sc)
364 {
365
366         return (0);
367 }
368
369 static int
370 exynos4210_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
371 {
372
373         return (EINVAL);
374 }
375
376 static struct uart_class uart_exynos4210_class = {
377         "exynos4210 class",
378         exynos4210_methods,
379         1,
380         .uc_ops = &uart_exynos4210_ops,
381         .uc_range = 8,
382         .uc_rclk = 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);