2 * Copyright (c) 2003 Marcel Moolenaar
3 * Copyright (c) 2007-2009 Andrew Turner
4 * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/systm.h>
39 #include <machine/bus.h>
40 #include <machine/intr.h>
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>
47 #include <arm/samsung/exynos/exynos_uart.h>
51 #define DEF_CLK 100000000
53 static int sscomspeed(long, long);
54 static int exynos4210_uart_param(struct uart_bas *, int, int, int, int);
57 * Low-level UART interface.
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);
66 extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
69 sscomspeed(long speed, long frequency)
73 if (speed <= 0 || frequency <= 0)
75 x = (frequency / 16) / speed;
80 exynos4210_uart_param(struct uart_bas *bas, int baudrate, int databits,
81 int stopbits, int parity)
89 ulcon |= ULCON_LENGTH_5;
92 ulcon |= ULCON_LENGTH_6;
95 ulcon |= ULCON_LENGTH_7;
98 ulcon |= ULCON_LENGTH_8;
105 case UART_PARITY_NONE:
106 ulcon |= ULCON_PARITY_NONE;
108 case UART_PARITY_ODD:
109 ulcon |= ULCON_PARITY_ODD;
111 case UART_PARITY_EVEN:
112 ulcon |= ULCON_PARITY_EVEN;
114 case UART_PARITY_MARK:
115 case UART_PARITY_SPACE:
123 uart_setreg(bas, SSCOM_ULCON, ulcon);
125 brd = sscomspeed(baudrate, bas->rclk);
126 uart_setreg(bas, SSCOM_UBRDIV, brd);
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,
141 exynos4210_probe(struct uart_bas *bas)
148 exynos4210_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
155 KASSERT(bas->rclk != 0, ("exynos4210_init: Invalid rclk"));
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 |
162 exynos4210_uart_param(bas, baudrate, databits, stopbits, parity);
165 uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT |
167 uart_setreg(bas, SSCOM_UMCON, UMCON_RTS);
171 exynos4210_term(struct uart_bas *bas)
177 exynos4210_putc(struct uart_bas *bas, int c)
180 while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) &
181 UFSTAT_TXFULL) == UFSTAT_TXFULL)
184 uart_setreg(bas, SSCOM_UTXH, c);
188 exynos4210_rxready(struct uart_bas *bas)
191 return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) ==
196 exynos4210_getc(struct uart_bas *bas, struct mtx *mtx)
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);
206 return (bus_space_read_1(bas->bst, bas->bsh, SSCOM_URXH));
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 *);
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),
236 exynos4210_bus_probe(struct uart_softc *sc)
239 sc->sc_txfifosz = 16;
240 sc->sc_rxfifosz = 16;
246 exynos4210_bus_attach(struct uart_softc *sc)
256 exynos4210_bus_transmit(struct uart_softc *sc)
261 uart_lock(sc->sc_hwmtx);
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);
270 uart_unlock(sc->sc_hwmtx);
272 /* unmask TX interrupt */
273 reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM);
275 bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM, reg);
281 exynos4210_bus_setsig(struct uart_softc *sc, int sig)
288 exynos4210_bus_receive(struct uart_softc *sc)
290 struct uart_bas *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));
301 exynos4210_bus_param(struct uart_softc *sc, int baudrate, int databits,
302 int stopbits, int parity)
306 if (sc->sc_bas.rclk == 0)
307 sc->sc_bas.rclk = DEF_CLK;
309 KASSERT(sc->sc_bas.rclk != 0, ("exynos4210_init: Invalid rclk"));
311 uart_lock(sc->sc_hwmtx);
312 error = exynos4210_uart_param(&sc->sc_bas, baudrate, databits, stopbits,
314 uart_unlock(sc->sc_hwmtx);
320 exynos4210_bus_ipend(struct uart_softc *sc)
323 uint32_t txempty, rxready;
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);
335 if ((ints & txempty) > 0) {
336 if (sc->sc_txbusy != 0)
337 ipend |= SER_INT_TXIDLE;
339 /* mask TX interrupt */
340 reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh,
343 bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh,
347 if ((ints & rxready) > 0) {
348 ipend |= SER_INT_RXREADY;
351 uart_unlock(sc->sc_hwmtx);
356 exynos4210_bus_flush(struct uart_softc *sc, int what)
363 exynos4210_bus_getsig(struct uart_softc *sc)
370 exynos4210_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
376 static struct uart_class uart_exynos4210_class = {
380 .uc_ops = &uart_exynos4210_ops,
386 static struct ofw_compat_data compat_data[] = {
387 {"exynos", (uintptr_t)&uart_exynos4210_class},
388 {NULL, (uintptr_t)NULL},
390 UART_FDT_CLASS_AND_DEVICE(compat_data);