exynos_uart.c revision 279724
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: head/sys/arm/samsung/exynos/exynos_uart.c 279724 2015-03-07 15:24:15Z ian $");
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
53static int sscomspeed(long, long);
54static int exynos4210_uart_param(struct uart_bas *, int, int, int, int);
55
56/*
57 * Low-level UART interface.
58 */
59static int exynos4210_probe(struct uart_bas *bas);
60static void exynos4210_init(struct uart_bas *bas, int, int, int, int);
61static void exynos4210_term(struct uart_bas *bas);
62static void exynos4210_putc(struct uart_bas *bas, int);
63static int exynos4210_rxready(struct uart_bas *bas);
64static int exynos4210_getc(struct uart_bas *bas, struct mtx *mtx);
65
66extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
67
68static int
69sscomspeed(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
79static int
80exynos4210_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
131struct 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
140static int
141exynos4210_probe(struct uart_bas *bas)
142{
143
144	return (0);
145}
146
147static void
148exynos4210_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
170static void
171exynos4210_term(struct uart_bas *bas)
172{
173	/* XXX */
174}
175
176static void
177exynos4210_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
187static int
188exynos4210_rxready(struct uart_bas *bas)
189{
190
191	return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) ==
192	    UTRSTAT_RXREADY);
193}
194
195static int
196exynos4210_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
209static int exynos4210_bus_probe(struct uart_softc *sc);
210static int exynos4210_bus_attach(struct uart_softc *sc);
211static int exynos4210_bus_flush(struct uart_softc *, int);
212static int exynos4210_bus_getsig(struct uart_softc *);
213static int exynos4210_bus_ioctl(struct uart_softc *, int, intptr_t);
214static int exynos4210_bus_ipend(struct uart_softc *);
215static int exynos4210_bus_param(struct uart_softc *, int, int, int, int);
216static int exynos4210_bus_receive(struct uart_softc *);
217static int exynos4210_bus_setsig(struct uart_softc *, int);
218static int exynos4210_bus_transmit(struct uart_softc *);
219
220static 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
235int
236exynos4210_bus_probe(struct uart_softc *sc)
237{
238
239	sc->sc_txfifosz = 16;
240	sc->sc_rxfifosz = 16;
241
242	return (0);
243}
244
245static int
246exynos4210_bus_attach(struct uart_softc *sc)
247{
248
249	sc->sc_hwiflow = 0;
250	sc->sc_hwoflow = 0;
251
252	return (0);
253}
254
255static int
256exynos4210_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
280static int
281exynos4210_bus_setsig(struct uart_softc *sc, int sig)
282{
283
284	return (0);
285}
286
287static int
288exynos4210_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
300static int
301exynos4210_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
319static int
320exynos4210_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
355static int
356exynos4210_bus_flush(struct uart_softc *sc, int what)
357{
358
359	return (0);
360}
361
362static int
363exynos4210_bus_getsig(struct uart_softc *sc)
364{
365
366	return (0);
367}
368
369static int
370exynos4210_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
371{
372
373	return (EINVAL);
374}
375
376static 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
385static struct ofw_compat_data compat_data[] = {
386	{"exynos",		(uintptr_t)&uart_exynos4210_class},
387	{NULL,			(uintptr_t)NULL},
388};
389UART_FDT_CLASS_AND_DEVICE(compat_data);
390