1252391Sray/*
2252391Sray * Copyright (c) 2003 Marcel Moolenaar
3252391Sray * Copyright (c) 2007-2009 Andrew Turner
4252391Sray * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
5252391Sray * All rights reserved.
6252391Sray *
7252391Sray * Redistribution and use in source and binary forms, with or without
8252391Sray * modification, are permitted provided that the following conditions
9252391Sray * are met:
10252391Sray *
11252391Sray * 1. Redistributions of source code must retain the above copyright
12252391Sray *    notice, this list of conditions and the following disclaimer.
13252391Sray * 2. Redistributions in binary form must reproduce the above copyright
14252391Sray *    notice, this list of conditions and the following disclaimer in the
15252391Sray *    documentation and/or other materials provided with the distribution.
16252391Sray *
17252391Sray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18252391Sray * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19252391Sray * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20252391Sray * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21252391Sray * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22252391Sray * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23252391Sray * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24252391Sray * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25252391Sray * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26252391Sray * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27252391Sray */
28252391Sray
29252391Sray#include <sys/cdefs.h>
30252391Sray__FBSDID("$FreeBSD: releng/10.2/sys/arm/samsung/exynos/exynos_uart.c 283327 2015-05-23 20:54:25Z ian $");
31252391Sray
32252391Sray#include <sys/param.h>
33252391Sray#include <sys/systm.h>
34252391Sray#include <sys/bus.h>
35252391Sray#include <sys/conf.h>
36252391Sray#include <sys/cons.h>
37252391Sray#include <sys/tty.h>
38252391Sray#include <sys/rman.h>
39252391Sray#include <machine/bus.h>
40252391Sray#include <machine/intr.h>
41252391Sray
42252391Sray#include <dev/uart/uart.h>
43252391Sray#include <dev/uart/uart_cpu.h>
44283327Sian#include <dev/uart/uart_cpu_fdt.h>
45252391Sray#include <dev/uart/uart_bus.h>
46252391Sray
47266944Sbr#include <arm/samsung/exynos/exynos_uart.h>
48252391Sray
49252391Sray#include "uart_if.h"
50252391Sray
51252391Sray#define	DEF_CLK		100000000
52252391Sray
53252391Sraystatic int sscomspeed(long, long);
54283323Sianstatic int exynos4210_uart_param(struct uart_bas *, int, int, int, int);
55252391Sray
56252391Sray/*
57252391Sray * Low-level UART interface.
58252391Sray */
59283323Sianstatic int exynos4210_probe(struct uart_bas *bas);
60283323Sianstatic void exynos4210_init(struct uart_bas *bas, int, int, int, int);
61283323Sianstatic void exynos4210_term(struct uart_bas *bas);
62283323Sianstatic void exynos4210_putc(struct uart_bas *bas, int);
63283323Sianstatic int exynos4210_rxready(struct uart_bas *bas);
64283323Sianstatic int exynos4210_getc(struct uart_bas *bas, struct mtx *mtx);
65252391Sray
66252391Srayextern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
67252391Sray
68252391Sraystatic int
69252391Sraysscomspeed(long speed, long frequency)
70252391Sray{
71252391Sray	int x;
72252391Sray
73252391Sray	if (speed <= 0 || frequency <= 0)
74252391Sray		return (-1);
75252391Sray	x = (frequency / 16) / speed;
76252391Sray	return (x-1);
77252391Sray}
78252391Sray
79252391Sraystatic int
80283323Sianexynos4210_uart_param(struct uart_bas *bas, int baudrate, int databits,
81252391Sray    int stopbits, int parity)
82252391Sray{
83252391Sray	int brd, ulcon;
84252391Sray
85252391Sray	ulcon = 0;
86252391Sray
87252391Sray	switch(databits) {
88252391Sray	case 5:
89252391Sray		ulcon |= ULCON_LENGTH_5;
90252391Sray		break;
91252391Sray	case 6:
92252391Sray		ulcon |= ULCON_LENGTH_6;
93252391Sray		break;
94252391Sray	case 7:
95252391Sray		ulcon |= ULCON_LENGTH_7;
96252391Sray		break;
97252391Sray	case 8:
98252391Sray		ulcon |= ULCON_LENGTH_8;
99252391Sray		break;
100252391Sray	default:
101252391Sray		return (EINVAL);
102252391Sray	}
103252391Sray
104252391Sray	switch (parity) {
105252391Sray	case UART_PARITY_NONE:
106252391Sray		ulcon |= ULCON_PARITY_NONE;
107252391Sray		break;
108252391Sray	case UART_PARITY_ODD:
109252391Sray		ulcon |= ULCON_PARITY_ODD;
110252391Sray		break;
111252391Sray	case UART_PARITY_EVEN:
112252391Sray		ulcon |= ULCON_PARITY_EVEN;
113252391Sray		break;
114252391Sray	case UART_PARITY_MARK:
115252391Sray	case UART_PARITY_SPACE:
116252391Sray	default:
117252391Sray		return (EINVAL);
118252391Sray	}
119252391Sray
120252391Sray	if (stopbits == 2)
121252391Sray		ulcon |= ULCON_STOP;
122252391Sray
123252391Sray	uart_setreg(bas, SSCOM_ULCON, ulcon);
124252391Sray
125252391Sray	brd = sscomspeed(baudrate, bas->rclk);
126252391Sray	uart_setreg(bas, SSCOM_UBRDIV, brd);
127252391Sray
128252391Sray	return (0);
129252391Sray}
130252391Sray
131283323Sianstruct uart_ops uart_exynos4210_ops = {
132283323Sian	.probe = exynos4210_probe,
133283323Sian	.init = exynos4210_init,
134283323Sian	.term = exynos4210_term,
135283323Sian	.putc = exynos4210_putc,
136283323Sian	.rxready = exynos4210_rxready,
137283323Sian	.getc = exynos4210_getc,
138252391Sray};
139252391Sray
140252391Sraystatic int
141283323Sianexynos4210_probe(struct uart_bas *bas)
142252391Sray{
143252391Sray
144252391Sray	return (0);
145252391Sray}
146252391Sray
147252391Sraystatic void
148283323Sianexynos4210_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
149252391Sray    int parity)
150252391Sray{
151252391Sray
152252391Sray	if (bas->rclk == 0)
153252391Sray		bas->rclk = DEF_CLK;
154252391Sray
155283323Sian	KASSERT(bas->rclk != 0, ("exynos4210_init: Invalid rclk"));
156252391Sray
157252391Sray	uart_setreg(bas, SSCOM_UCON, 0);
158252391Sray	uart_setreg(bas, SSCOM_UFCON,
159252391Sray	    UFCON_TXTRIGGER_8 | UFCON_RXTRIGGER_8 |
160252391Sray	    UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET |
161252391Sray	    UFCON_FIFO_ENABLE);
162283323Sian	exynos4210_uart_param(bas, baudrate, databits, stopbits, parity);
163252391Sray
164252391Sray	/* Enable UART. */
165252391Sray	uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT |
166252391Sray	    UCON_TOINT);
167252391Sray	uart_setreg(bas, SSCOM_UMCON, UMCON_RTS);
168252391Sray}
169252391Sray
170252391Sraystatic void
171283323Sianexynos4210_term(struct uart_bas *bas)
172252391Sray{
173252391Sray	/* XXX */
174252391Sray}
175252391Sray
176252391Sraystatic void
177283323Sianexynos4210_putc(struct uart_bas *bas, int c)
178252391Sray{
179252391Sray
180252391Sray	while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) &
181252391Sray		UFSTAT_TXFULL) == UFSTAT_TXFULL)
182252391Sray		continue;
183252391Sray
184252391Sray	uart_setreg(bas, SSCOM_UTXH, c);
185252391Sray}
186252391Sray
187252391Sraystatic int
188283323Sianexynos4210_rxready(struct uart_bas *bas)
189252391Sray{
190252391Sray
191252391Sray	return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) ==
192252391Sray	    UTRSTAT_RXREADY);
193252391Sray}
194252391Sray
195252391Sraystatic int
196283323Sianexynos4210_getc(struct uart_bas *bas, struct mtx *mtx)
197252391Sray{
198252391Sray	int utrstat;
199252391Sray
200252391Sray	utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT);
201252391Sray	while (!(utrstat & UTRSTAT_RXREADY)) {
202252391Sray		utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT);
203252391Sray		continue;
204252391Sray	}
205252391Sray
206252391Sray	return (bus_space_read_1(bas->bst, bas->bsh, SSCOM_URXH));
207252391Sray}
208252391Sray
209283323Sianstatic int exynos4210_bus_probe(struct uart_softc *sc);
210283323Sianstatic int exynos4210_bus_attach(struct uart_softc *sc);
211283323Sianstatic int exynos4210_bus_flush(struct uart_softc *, int);
212283323Sianstatic int exynos4210_bus_getsig(struct uart_softc *);
213283323Sianstatic int exynos4210_bus_ioctl(struct uart_softc *, int, intptr_t);
214283323Sianstatic int exynos4210_bus_ipend(struct uart_softc *);
215283323Sianstatic int exynos4210_bus_param(struct uart_softc *, int, int, int, int);
216283323Sianstatic int exynos4210_bus_receive(struct uart_softc *);
217283323Sianstatic int exynos4210_bus_setsig(struct uart_softc *, int);
218283323Sianstatic int exynos4210_bus_transmit(struct uart_softc *);
219252391Sray
220283323Sianstatic kobj_method_t exynos4210_methods[] = {
221283323Sian	KOBJMETHOD(uart_probe,		exynos4210_bus_probe),
222283323Sian	KOBJMETHOD(uart_attach, 	exynos4210_bus_attach),
223283323Sian	KOBJMETHOD(uart_flush,		exynos4210_bus_flush),
224283323Sian	KOBJMETHOD(uart_getsig,		exynos4210_bus_getsig),
225283323Sian	KOBJMETHOD(uart_ioctl,		exynos4210_bus_ioctl),
226283323Sian	KOBJMETHOD(uart_ipend,		exynos4210_bus_ipend),
227283323Sian	KOBJMETHOD(uart_param,		exynos4210_bus_param),
228283323Sian	KOBJMETHOD(uart_receive,	exynos4210_bus_receive),
229283323Sian	KOBJMETHOD(uart_setsig,		exynos4210_bus_setsig),
230283323Sian	KOBJMETHOD(uart_transmit,	exynos4210_bus_transmit),
231252391Sray
232252391Sray	{0, 0 }
233252391Sray};
234252391Sray
235252391Srayint
236283323Sianexynos4210_bus_probe(struct uart_softc *sc)
237252391Sray{
238252391Sray
239252391Sray	sc->sc_txfifosz = 16;
240252391Sray	sc->sc_rxfifosz = 16;
241252391Sray
242252391Sray	return (0);
243252391Sray}
244252391Sray
245252391Sraystatic int
246283323Sianexynos4210_bus_attach(struct uart_softc *sc)
247252391Sray{
248252391Sray
249252391Sray	sc->sc_hwiflow = 0;
250252391Sray	sc->sc_hwoflow = 0;
251252391Sray
252252391Sray	return (0);
253252391Sray}
254252391Sray
255252391Sraystatic int
256283323Sianexynos4210_bus_transmit(struct uart_softc *sc)
257252391Sray{
258252391Sray	int i;
259252391Sray	int reg;
260252391Sray
261252391Sray	uart_lock(sc->sc_hwmtx);
262252391Sray
263252391Sray	for (i = 0; i < sc->sc_txdatasz; i++) {
264283323Sian		exynos4210_putc(&sc->sc_bas, sc->sc_txbuf[i]);
265252391Sray		uart_barrier(&sc->sc_bas);
266252391Sray	}
267252391Sray
268252391Sray	sc->sc_txbusy = 1;
269252391Sray
270252391Sray	uart_unlock(sc->sc_hwmtx);
271252391Sray
272252391Sray	/* unmask TX interrupt */
273252391Sray	reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM);
274252391Sray	reg &= ~(1 << 2);
275252391Sray	bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM, reg);
276252391Sray
277252391Sray	return (0);
278252391Sray}
279252391Sray
280252391Sraystatic int
281283323Sianexynos4210_bus_setsig(struct uart_softc *sc, int sig)
282252391Sray{
283252391Sray
284252391Sray	return (0);
285252391Sray}
286252391Sray
287252391Sraystatic int
288283323Sianexynos4210_bus_receive(struct uart_softc *sc)
289252391Sray{
290266942Sbr	struct uart_bas *bas;
291252391Sray
292266942Sbr	bas = &sc->sc_bas;
293266942Sbr	while (bus_space_read_4(bas->bst, bas->bsh,
294266942Sbr		SSCOM_UFSTAT) & UFSTAT_RXCOUNT)
295266942Sbr		uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH));
296266942Sbr
297252391Sray	return (0);
298252391Sray}
299252391Sray
300252391Sraystatic int
301283323Sianexynos4210_bus_param(struct uart_softc *sc, int baudrate, int databits,
302252391Sray    int stopbits, int parity)
303252391Sray{
304252391Sray	int error;
305252391Sray
306252391Sray	if (sc->sc_bas.rclk == 0)
307252391Sray		sc->sc_bas.rclk = DEF_CLK;
308252391Sray
309283323Sian	KASSERT(sc->sc_bas.rclk != 0, ("exynos4210_init: Invalid rclk"));
310252391Sray
311252391Sray	uart_lock(sc->sc_hwmtx);
312283323Sian	error = exynos4210_uart_param(&sc->sc_bas, baudrate, databits, stopbits,
313252391Sray	    parity);
314252391Sray	uart_unlock(sc->sc_hwmtx);
315252391Sray
316252391Sray	return (error);
317252391Sray}
318252391Sray
319252391Sraystatic int
320283323Sianexynos4210_bus_ipend(struct uart_softc *sc)
321252391Sray{
322252391Sray	uint32_t ints;
323252391Sray	uint32_t txempty, rxready;
324252391Sray	int reg;
325252391Sray	int ipend;
326252391Sray
327252391Sray	uart_lock(sc->sc_hwmtx);
328252391Sray	ints = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP);
329252391Sray	bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP, ints);
330252391Sray
331252391Sray	txempty = (1 << 2);
332252391Sray	rxready = (1 << 0);
333252391Sray
334252391Sray	ipend = 0;
335252391Sray	if ((ints & txempty) > 0) {
336252391Sray		if (sc->sc_txbusy != 0)
337252391Sray			ipend |= SER_INT_TXIDLE;
338252391Sray
339252391Sray		/* mask TX interrupt */
340252391Sray		reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh,
341252391Sray		    SSCOM_UINTM);
342252391Sray		reg |= (1 << 2);
343252391Sray		bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh,
344252391Sray		    SSCOM_UINTM, reg);
345252391Sray	}
346252391Sray
347252391Sray	if ((ints & rxready) > 0) {
348252391Sray		ipend |= SER_INT_RXREADY;
349252391Sray	}
350252391Sray
351252391Sray	uart_unlock(sc->sc_hwmtx);
352252391Sray	return (ipend);
353252391Sray}
354252391Sray
355252391Sraystatic int
356283323Sianexynos4210_bus_flush(struct uart_softc *sc, int what)
357252391Sray{
358252391Sray
359252391Sray	return (0);
360252391Sray}
361252391Sray
362252391Sraystatic int
363283323Sianexynos4210_bus_getsig(struct uart_softc *sc)
364252391Sray{
365252391Sray
366252391Sray	return (0);
367252391Sray}
368252391Sray
369252391Sraystatic int
370283323Sianexynos4210_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
371252391Sray{
372252391Sray
373252391Sray	return (EINVAL);
374252391Sray}
375252391Sray
376283327Sianstatic struct uart_class uart_exynos4210_class = {
377283323Sian	"exynos4210 class",
378283323Sian	exynos4210_methods,
379252391Sray	1,
380283323Sian	.uc_ops = &uart_exynos4210_ops,
381252391Sray	.uc_range = 8,
382252391Sray	.uc_rclk = 0,
383252391Sray};
384283327Sian
385283327Sianstatic struct ofw_compat_data compat_data[] = {
386283327Sian	{"exynos",		(uintptr_t)&uart_exynos4210_class},
387283327Sian	{NULL,			(uintptr_t)NULL},
388283327Sian};
389283327SianUART_FDT_CLASS_AND_DEVICE(compat_data);
390