exynos_uart.c revision 283323
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: stable/10/sys/arm/samsung/exynos/exynos_uart.c 283323 2015-05-23 19:57:44Z 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>
44252391Sray#include <dev/uart/uart_bus.h>
45252391Sray
46266944Sbr#include <arm/samsung/exynos/exynos_uart.h>
47252391Sray
48252391Sray#include "uart_if.h"
49252391Sray
50252391Sray#define	DEF_CLK		100000000
51252391Sray
52252391Sraystatic int sscomspeed(long, long);
53283323Sianstatic int exynos4210_uart_param(struct uart_bas *, int, int, int, int);
54252391Sray
55252391Sray/*
56252391Sray * Low-level UART interface.
57252391Sray */
58283323Sianstatic int exynos4210_probe(struct uart_bas *bas);
59283323Sianstatic void exynos4210_init(struct uart_bas *bas, int, int, int, int);
60283323Sianstatic void exynos4210_term(struct uart_bas *bas);
61283323Sianstatic void exynos4210_putc(struct uart_bas *bas, int);
62283323Sianstatic int exynos4210_rxready(struct uart_bas *bas);
63283323Sianstatic int exynos4210_getc(struct uart_bas *bas, struct mtx *mtx);
64252391Sray
65252391Srayextern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
66252391Sray
67252391Sraystatic int
68252391Sraysscomspeed(long speed, long frequency)
69252391Sray{
70252391Sray	int x;
71252391Sray
72252391Sray	if (speed <= 0 || frequency <= 0)
73252391Sray		return (-1);
74252391Sray	x = (frequency / 16) / speed;
75252391Sray	return (x-1);
76252391Sray}
77252391Sray
78252391Sraystatic int
79283323Sianexynos4210_uart_param(struct uart_bas *bas, int baudrate, int databits,
80252391Sray    int stopbits, int parity)
81252391Sray{
82252391Sray	int brd, ulcon;
83252391Sray
84252391Sray	ulcon = 0;
85252391Sray
86252391Sray	switch(databits) {
87252391Sray	case 5:
88252391Sray		ulcon |= ULCON_LENGTH_5;
89252391Sray		break;
90252391Sray	case 6:
91252391Sray		ulcon |= ULCON_LENGTH_6;
92252391Sray		break;
93252391Sray	case 7:
94252391Sray		ulcon |= ULCON_LENGTH_7;
95252391Sray		break;
96252391Sray	case 8:
97252391Sray		ulcon |= ULCON_LENGTH_8;
98252391Sray		break;
99252391Sray	default:
100252391Sray		return (EINVAL);
101252391Sray	}
102252391Sray
103252391Sray	switch (parity) {
104252391Sray	case UART_PARITY_NONE:
105252391Sray		ulcon |= ULCON_PARITY_NONE;
106252391Sray		break;
107252391Sray	case UART_PARITY_ODD:
108252391Sray		ulcon |= ULCON_PARITY_ODD;
109252391Sray		break;
110252391Sray	case UART_PARITY_EVEN:
111252391Sray		ulcon |= ULCON_PARITY_EVEN;
112252391Sray		break;
113252391Sray	case UART_PARITY_MARK:
114252391Sray	case UART_PARITY_SPACE:
115252391Sray	default:
116252391Sray		return (EINVAL);
117252391Sray	}
118252391Sray
119252391Sray	if (stopbits == 2)
120252391Sray		ulcon |= ULCON_STOP;
121252391Sray
122252391Sray	uart_setreg(bas, SSCOM_ULCON, ulcon);
123252391Sray
124252391Sray	brd = sscomspeed(baudrate, bas->rclk);
125252391Sray	uart_setreg(bas, SSCOM_UBRDIV, brd);
126252391Sray
127252391Sray	return (0);
128252391Sray}
129252391Sray
130283323Sianstruct uart_ops uart_exynos4210_ops = {
131283323Sian	.probe = exynos4210_probe,
132283323Sian	.init = exynos4210_init,
133283323Sian	.term = exynos4210_term,
134283323Sian	.putc = exynos4210_putc,
135283323Sian	.rxready = exynos4210_rxready,
136283323Sian	.getc = exynos4210_getc,
137252391Sray};
138252391Sray
139252391Sraystatic int
140283323Sianexynos4210_probe(struct uart_bas *bas)
141252391Sray{
142252391Sray
143252391Sray	return (0);
144252391Sray}
145252391Sray
146252391Sraystatic void
147283323Sianexynos4210_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
148252391Sray    int parity)
149252391Sray{
150252391Sray
151252391Sray	if (bas->rclk == 0)
152252391Sray		bas->rclk = DEF_CLK;
153252391Sray
154283323Sian	KASSERT(bas->rclk != 0, ("exynos4210_init: Invalid rclk"));
155252391Sray
156252391Sray	uart_setreg(bas, SSCOM_UCON, 0);
157252391Sray	uart_setreg(bas, SSCOM_UFCON,
158252391Sray	    UFCON_TXTRIGGER_8 | UFCON_RXTRIGGER_8 |
159252391Sray	    UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET |
160252391Sray	    UFCON_FIFO_ENABLE);
161283323Sian	exynos4210_uart_param(bas, baudrate, databits, stopbits, parity);
162252391Sray
163252391Sray	/* Enable UART. */
164252391Sray	uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT |
165252391Sray	    UCON_TOINT);
166252391Sray	uart_setreg(bas, SSCOM_UMCON, UMCON_RTS);
167252391Sray}
168252391Sray
169252391Sraystatic void
170283323Sianexynos4210_term(struct uart_bas *bas)
171252391Sray{
172252391Sray	/* XXX */
173252391Sray}
174252391Sray
175252391Sraystatic void
176283323Sianexynos4210_putc(struct uart_bas *bas, int c)
177252391Sray{
178252391Sray
179252391Sray	while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) &
180252391Sray		UFSTAT_TXFULL) == UFSTAT_TXFULL)
181252391Sray		continue;
182252391Sray
183252391Sray	uart_setreg(bas, SSCOM_UTXH, c);
184252391Sray}
185252391Sray
186252391Sraystatic int
187283323Sianexynos4210_rxready(struct uart_bas *bas)
188252391Sray{
189252391Sray
190252391Sray	return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) ==
191252391Sray	    UTRSTAT_RXREADY);
192252391Sray}
193252391Sray
194252391Sraystatic int
195283323Sianexynos4210_getc(struct uart_bas *bas, struct mtx *mtx)
196252391Sray{
197252391Sray	int utrstat;
198252391Sray
199252391Sray	utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT);
200252391Sray	while (!(utrstat & UTRSTAT_RXREADY)) {
201252391Sray		utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT);
202252391Sray		continue;
203252391Sray	}
204252391Sray
205252391Sray	return (bus_space_read_1(bas->bst, bas->bsh, SSCOM_URXH));
206252391Sray}
207252391Sray
208283323Sianstatic int exynos4210_bus_probe(struct uart_softc *sc);
209283323Sianstatic int exynos4210_bus_attach(struct uart_softc *sc);
210283323Sianstatic int exynos4210_bus_flush(struct uart_softc *, int);
211283323Sianstatic int exynos4210_bus_getsig(struct uart_softc *);
212283323Sianstatic int exynos4210_bus_ioctl(struct uart_softc *, int, intptr_t);
213283323Sianstatic int exynos4210_bus_ipend(struct uart_softc *);
214283323Sianstatic int exynos4210_bus_param(struct uart_softc *, int, int, int, int);
215283323Sianstatic int exynos4210_bus_receive(struct uart_softc *);
216283323Sianstatic int exynos4210_bus_setsig(struct uart_softc *, int);
217283323Sianstatic int exynos4210_bus_transmit(struct uart_softc *);
218252391Sray
219283323Sianstatic kobj_method_t exynos4210_methods[] = {
220283323Sian	KOBJMETHOD(uart_probe,		exynos4210_bus_probe),
221283323Sian	KOBJMETHOD(uart_attach, 	exynos4210_bus_attach),
222283323Sian	KOBJMETHOD(uart_flush,		exynos4210_bus_flush),
223283323Sian	KOBJMETHOD(uart_getsig,		exynos4210_bus_getsig),
224283323Sian	KOBJMETHOD(uart_ioctl,		exynos4210_bus_ioctl),
225283323Sian	KOBJMETHOD(uart_ipend,		exynos4210_bus_ipend),
226283323Sian	KOBJMETHOD(uart_param,		exynos4210_bus_param),
227283323Sian	KOBJMETHOD(uart_receive,	exynos4210_bus_receive),
228283323Sian	KOBJMETHOD(uart_setsig,		exynos4210_bus_setsig),
229283323Sian	KOBJMETHOD(uart_transmit,	exynos4210_bus_transmit),
230252391Sray
231252391Sray	{0, 0 }
232252391Sray};
233252391Sray
234252391Srayint
235283323Sianexynos4210_bus_probe(struct uart_softc *sc)
236252391Sray{
237252391Sray
238252391Sray	sc->sc_txfifosz = 16;
239252391Sray	sc->sc_rxfifosz = 16;
240252391Sray
241252391Sray	return (0);
242252391Sray}
243252391Sray
244252391Sraystatic int
245283323Sianexynos4210_bus_attach(struct uart_softc *sc)
246252391Sray{
247252391Sray
248252391Sray	sc->sc_hwiflow = 0;
249252391Sray	sc->sc_hwoflow = 0;
250252391Sray
251252391Sray	return (0);
252252391Sray}
253252391Sray
254252391Sraystatic int
255283323Sianexynos4210_bus_transmit(struct uart_softc *sc)
256252391Sray{
257252391Sray	int i;
258252391Sray	int reg;
259252391Sray
260252391Sray	uart_lock(sc->sc_hwmtx);
261252391Sray
262252391Sray	for (i = 0; i < sc->sc_txdatasz; i++) {
263283323Sian		exynos4210_putc(&sc->sc_bas, sc->sc_txbuf[i]);
264252391Sray		uart_barrier(&sc->sc_bas);
265252391Sray	}
266252391Sray
267252391Sray	sc->sc_txbusy = 1;
268252391Sray
269252391Sray	uart_unlock(sc->sc_hwmtx);
270252391Sray
271252391Sray	/* unmask TX interrupt */
272252391Sray	reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM);
273252391Sray	reg &= ~(1 << 2);
274252391Sray	bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM, reg);
275252391Sray
276252391Sray	return (0);
277252391Sray}
278252391Sray
279252391Sraystatic int
280283323Sianexynos4210_bus_setsig(struct uart_softc *sc, int sig)
281252391Sray{
282252391Sray
283252391Sray	return (0);
284252391Sray}
285252391Sray
286252391Sraystatic int
287283323Sianexynos4210_bus_receive(struct uart_softc *sc)
288252391Sray{
289266942Sbr	struct uart_bas *bas;
290252391Sray
291266942Sbr	bas = &sc->sc_bas;
292266942Sbr	while (bus_space_read_4(bas->bst, bas->bsh,
293266942Sbr		SSCOM_UFSTAT) & UFSTAT_RXCOUNT)
294266942Sbr		uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH));
295266942Sbr
296252391Sray	return (0);
297252391Sray}
298252391Sray
299252391Sraystatic int
300283323Sianexynos4210_bus_param(struct uart_softc *sc, int baudrate, int databits,
301252391Sray    int stopbits, int parity)
302252391Sray{
303252391Sray	int error;
304252391Sray
305252391Sray	if (sc->sc_bas.rclk == 0)
306252391Sray		sc->sc_bas.rclk = DEF_CLK;
307252391Sray
308283323Sian	KASSERT(sc->sc_bas.rclk != 0, ("exynos4210_init: Invalid rclk"));
309252391Sray
310252391Sray	uart_lock(sc->sc_hwmtx);
311283323Sian	error = exynos4210_uart_param(&sc->sc_bas, baudrate, databits, stopbits,
312252391Sray	    parity);
313252391Sray	uart_unlock(sc->sc_hwmtx);
314252391Sray
315252391Sray	return (error);
316252391Sray}
317252391Sray
318252391Sraystatic int
319283323Sianexynos4210_bus_ipend(struct uart_softc *sc)
320252391Sray{
321252391Sray	uint32_t ints;
322252391Sray	uint32_t txempty, rxready;
323252391Sray	int reg;
324252391Sray	int ipend;
325252391Sray
326252391Sray	uart_lock(sc->sc_hwmtx);
327252391Sray	ints = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP);
328252391Sray	bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP, ints);
329252391Sray
330252391Sray	txempty = (1 << 2);
331252391Sray	rxready = (1 << 0);
332252391Sray
333252391Sray	ipend = 0;
334252391Sray	if ((ints & txempty) > 0) {
335252391Sray		if (sc->sc_txbusy != 0)
336252391Sray			ipend |= SER_INT_TXIDLE;
337252391Sray
338252391Sray		/* mask TX interrupt */
339252391Sray		reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh,
340252391Sray		    SSCOM_UINTM);
341252391Sray		reg |= (1 << 2);
342252391Sray		bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh,
343252391Sray		    SSCOM_UINTM, reg);
344252391Sray	}
345252391Sray
346252391Sray	if ((ints & rxready) > 0) {
347252391Sray		ipend |= SER_INT_RXREADY;
348252391Sray	}
349252391Sray
350252391Sray	uart_unlock(sc->sc_hwmtx);
351252391Sray	return (ipend);
352252391Sray}
353252391Sray
354252391Sraystatic int
355283323Sianexynos4210_bus_flush(struct uart_softc *sc, int what)
356252391Sray{
357252391Sray
358252391Sray	return (0);
359252391Sray}
360252391Sray
361252391Sraystatic int
362283323Sianexynos4210_bus_getsig(struct uart_softc *sc)
363252391Sray{
364252391Sray
365252391Sray	return (0);
366252391Sray}
367252391Sray
368252391Sraystatic int
369283323Sianexynos4210_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
370252391Sray{
371252391Sray
372252391Sray	return (EINVAL);
373252391Sray}
374252391Sray
375283323Sianstruct uart_class uart_exynos4210_class = {
376283323Sian	"exynos4210 class",
377283323Sian	exynos4210_methods,
378252391Sray	1,
379283323Sian	.uc_ops = &uart_exynos4210_ops,
380252391Sray	.uc_range = 8,
381252391Sray	.uc_rclk = 0,
382252391Sray};
383