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>
38 #include <machine/bus.h>
39 #include <machine/intr.h>
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>
46 #include <arm/samsung/exynos/exynos_uart.h>
50 #define DEF_CLK 100000000
52 static int sscomspeed(long, long);
53 static int exynos4210_uart_param(struct uart_bas *, int, int, int, int);
56 * Low-level UART interface.
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);
65 extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
68 sscomspeed(long speed, long frequency)
72 if (speed <= 0 || frequency <= 0)
74 x = (frequency / 16) / speed;
79 exynos4210_uart_param(struct uart_bas *bas, int baudrate, int databits,
80 int stopbits, int parity)
88 ulcon |= ULCON_LENGTH_5;
91 ulcon |= ULCON_LENGTH_6;
94 ulcon |= ULCON_LENGTH_7;
97 ulcon |= ULCON_LENGTH_8;
104 case UART_PARITY_NONE:
105 ulcon |= ULCON_PARITY_NONE;
107 case UART_PARITY_ODD:
108 ulcon |= ULCON_PARITY_ODD;
110 case UART_PARITY_EVEN:
111 ulcon |= ULCON_PARITY_EVEN;
113 case UART_PARITY_MARK:
114 case UART_PARITY_SPACE:
122 uart_setreg(bas, SSCOM_ULCON, ulcon);
124 brd = sscomspeed(baudrate, bas->rclk);
125 uart_setreg(bas, SSCOM_UBRDIV, brd);
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,
140 exynos4210_probe(struct uart_bas *bas)
147 exynos4210_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
154 KASSERT(bas->rclk != 0, ("exynos4210_init: Invalid rclk"));
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 |
161 exynos4210_uart_param(bas, baudrate, databits, stopbits, parity);
164 uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT |
166 uart_setreg(bas, SSCOM_UMCON, UMCON_RTS);
170 exynos4210_term(struct uart_bas *bas)
176 exynos4210_putc(struct uart_bas *bas, int c)
179 while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) &
180 UFSTAT_TXFULL) == UFSTAT_TXFULL)
183 uart_setreg(bas, SSCOM_UTXH, c);
187 exynos4210_rxready(struct uart_bas *bas)
190 return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) ==
195 exynos4210_getc(struct uart_bas *bas, struct mtx *mtx)
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);
205 return (bus_space_read_1(bas->bst, bas->bsh, SSCOM_URXH));
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 *);
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),
235 exynos4210_bus_probe(struct uart_softc *sc)
238 sc->sc_txfifosz = 16;
239 sc->sc_rxfifosz = 16;
245 exynos4210_bus_attach(struct uart_softc *sc)
255 exynos4210_bus_transmit(struct uart_softc *sc)
260 uart_lock(sc->sc_hwmtx);
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);
269 uart_unlock(sc->sc_hwmtx);
271 /* unmask TX interrupt */
272 reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM);
274 bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM, reg);
280 exynos4210_bus_setsig(struct uart_softc *sc, int sig)
287 exynos4210_bus_receive(struct uart_softc *sc)
289 struct uart_bas *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));
300 exynos4210_bus_param(struct uart_softc *sc, int baudrate, int databits,
301 int stopbits, int parity)
305 if (sc->sc_bas.rclk == 0)
306 sc->sc_bas.rclk = DEF_CLK;
308 KASSERT(sc->sc_bas.rclk != 0, ("exynos4210_init: Invalid rclk"));
310 uart_lock(sc->sc_hwmtx);
311 error = exynos4210_uart_param(&sc->sc_bas, baudrate, databits, stopbits,
313 uart_unlock(sc->sc_hwmtx);
319 exynos4210_bus_ipend(struct uart_softc *sc)
322 uint32_t txempty, rxready;
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);
334 if ((ints & txempty) > 0) {
335 if (sc->sc_txbusy != 0)
336 ipend |= SER_INT_TXIDLE;
338 /* mask TX interrupt */
339 reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh,
342 bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh,
346 if ((ints & rxready) > 0) {
347 ipend |= SER_INT_RXREADY;
350 uart_unlock(sc->sc_hwmtx);
355 exynos4210_bus_flush(struct uart_softc *sc, int what)
362 exynos4210_bus_getsig(struct uart_softc *sc)
369 exynos4210_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
375 static struct uart_class uart_exynos4210_class = {
379 .uc_ops = &uart_exynos4210_ops,
385 static struct ofw_compat_data compat_data[] = {
386 {"exynos", (uintptr_t)&uart_exynos4210_class},
387 {NULL, (uintptr_t)NULL},
389 UART_FDT_CLASS_AND_DEVICE(compat_data);