]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/arm/samsung/exynos/uart.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / arm / samsung / 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_bus.h>
45
46 #include <arm/samsung/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 s3c24x0_uart_param(struct uart_bas *, int, int, int, int);
54
55 /*
56  * Low-level UART interface.
57  */
58 static int s3c2410_probe(struct uart_bas *bas);
59 static void s3c2410_init(struct uart_bas *bas, int, int, int, int);
60 static void s3c2410_term(struct uart_bas *bas);
61 static void s3c2410_putc(struct uart_bas *bas, int);
62 static int s3c2410_rxready(struct uart_bas *bas);
63 static int s3c2410_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 s3c24x0_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_s3c2410_ops = {
131         .probe = s3c2410_probe,
132         .init = s3c2410_init,
133         .term = s3c2410_term,
134         .putc = s3c2410_putc,
135         .rxready = s3c2410_rxready,
136         .getc = s3c2410_getc,
137 };
138
139 static int
140 s3c2410_probe(struct uart_bas *bas)
141 {
142
143         return (0);
144 }
145
146 static void
147 s3c2410_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, ("s3c2410_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         s3c24x0_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 s3c2410_term(struct uart_bas *bas)
171 {
172         /* XXX */
173 }
174
175 static void
176 s3c2410_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 s3c2410_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 s3c2410_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 s3c2410_bus_probe(struct uart_softc *sc);
209 static int s3c2410_bus_attach(struct uart_softc *sc);
210 static int s3c2410_bus_flush(struct uart_softc *, int);
211 static int s3c2410_bus_getsig(struct uart_softc *);
212 static int s3c2410_bus_ioctl(struct uart_softc *, int, intptr_t);
213 static int s3c2410_bus_ipend(struct uart_softc *);
214 static int s3c2410_bus_param(struct uart_softc *, int, int, int, int);
215 static int s3c2410_bus_receive(struct uart_softc *);
216 static int s3c2410_bus_setsig(struct uart_softc *, int);
217 static int s3c2410_bus_transmit(struct uart_softc *);
218
219 static kobj_method_t s3c2410_methods[] = {
220         KOBJMETHOD(uart_probe,          s3c2410_bus_probe),
221         KOBJMETHOD(uart_attach,         s3c2410_bus_attach),
222         KOBJMETHOD(uart_flush,          s3c2410_bus_flush),
223         KOBJMETHOD(uart_getsig,         s3c2410_bus_getsig),
224         KOBJMETHOD(uart_ioctl,          s3c2410_bus_ioctl),
225         KOBJMETHOD(uart_ipend,          s3c2410_bus_ipend),
226         KOBJMETHOD(uart_param,          s3c2410_bus_param),
227         KOBJMETHOD(uart_receive,        s3c2410_bus_receive),
228         KOBJMETHOD(uart_setsig,         s3c2410_bus_setsig),
229         KOBJMETHOD(uart_transmit,       s3c2410_bus_transmit),
230
231         {0, 0 }
232 };
233
234 int
235 s3c2410_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 s3c2410_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 s3c2410_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                 s3c2410_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 s3c2410_bus_setsig(struct uart_softc *sc, int sig)
281 {
282
283         return (0);
284 }
285
286 static int
287 s3c2410_bus_receive(struct uart_softc *sc)
288 {
289
290         uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH));
291         return (0);
292 }
293
294 static int
295 s3c2410_bus_param(struct uart_softc *sc, int baudrate, int databits,
296     int stopbits, int parity)
297 {
298         int error;
299
300         if (sc->sc_bas.rclk == 0)
301                 sc->sc_bas.rclk = DEF_CLK;
302
303         KASSERT(sc->sc_bas.rclk != 0, ("s3c2410_init: Invalid rclk"));
304
305         uart_lock(sc->sc_hwmtx);
306         error = s3c24x0_uart_param(&sc->sc_bas, baudrate, databits, stopbits,
307             parity);
308         uart_unlock(sc->sc_hwmtx);
309
310         return (error);
311 }
312
313 static int
314 s3c2410_bus_ipend(struct uart_softc *sc)
315 {
316         uint32_t ints;
317         uint32_t txempty, rxready;
318         int reg;
319         int ipend;
320
321         uart_lock(sc->sc_hwmtx);
322         ints = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP);
323         bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP, ints);
324
325         txempty = (1 << 2);
326         rxready = (1 << 0);
327
328         ipend = 0;
329         if ((ints & txempty) > 0) {
330                 if (sc->sc_txbusy != 0)
331                         ipend |= SER_INT_TXIDLE;
332
333                 /* mask TX interrupt */
334                 reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh,
335                     SSCOM_UINTM);
336                 reg |= (1 << 2);
337                 bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh,
338                     SSCOM_UINTM, reg);
339         }
340
341         if ((ints & rxready) > 0) {
342                 ipend |= SER_INT_RXREADY;
343         }
344
345         uart_unlock(sc->sc_hwmtx);
346         return (ipend);
347 }
348
349 static int
350 s3c2410_bus_flush(struct uart_softc *sc, int what)
351 {
352
353         return (0);
354 }
355
356 static int
357 s3c2410_bus_getsig(struct uart_softc *sc)
358 {
359
360         return (0);
361 }
362
363 static int
364 s3c2410_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
365 {
366
367         return (EINVAL);
368 }
369
370 struct uart_class uart_s3c2410_class = {
371         "s3c2410 class",
372         s3c2410_methods,
373         1,
374         .uc_ops = &uart_s3c2410_ops,
375         .uc_range = 8,
376         .uc_rclk = 0,
377 };