1205354Simp/*
2205354Simp * Copyright (c) 2003 Marcel Moolenaar
3205354Simp * Copyright (c) 2007-2009 Andrew Turner
4205354Simp * All rights reserved.
5205354Simp *
6205354Simp * Redistribution and use in source and binary forms, with or without
7205354Simp * modification, are permitted provided that the following conditions
8205354Simp * are met:
9205354Simp *
10205354Simp * 1. Redistributions of source code must retain the above copyright
11205354Simp *    notice, this list of conditions and the following disclaimer.
12205354Simp * 2. Redistributions in binary form must reproduce the above copyright
13205354Simp *    notice, this list of conditions and the following disclaimer in the
14205354Simp *    documentation and/or other materials provided with the distribution.
15205354Simp *
16205354Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17205354Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18205354Simp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19205354Simp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20205354Simp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21205354Simp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22205354Simp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23205354Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24205354Simp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25205354Simp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26205354Simp */
27205354Simp
28205354Simp#include <sys/cdefs.h>
29205354Simp__FBSDID("$FreeBSD$");
30205354Simp
31205354Simp#include <sys/param.h>
32205354Simp#include <sys/systm.h>
33205354Simp#include <sys/bus.h>
34205354Simp#include <sys/conf.h>
35205354Simp#include <sys/cons.h>
36205354Simp#include <sys/tty.h>
37205354Simp#include <sys/rman.h>
38205354Simp#include <machine/bus.h>
39205354Simp#include <machine/intr.h>
40205354Simp
41205354Simp#include <dev/uart/uart.h>
42205354Simp#include <dev/uart/uart_cpu.h>
43205354Simp#include <dev/uart/uart_bus.h>
44205354Simp#include <arm/s3c2xx0/s3c2440reg.h>
45205354Simp#include <arm/s3c2xx0/uart_dev_s3c2410.h>
46205354Simp#include <arm/s3c2xx0/s3c2xx0reg.h>
47205354Simp#include <arm/s3c2xx0/s3c2xx0var.h>
48205354Simp#include "uart_if.h"
49205354Simp
50205354Simp/* Finds the subirq from the parent */
51205354Simp#define get_sub_irq(parent, offset) \
52205354Simp	((parent == S3C24X0_INT_UART0) ? S3C24X0_SUBIRQ_MIN + offset : \
53205354Simp	((parent == S3C24X0_INT_UART1) ? S3C24X0_SUBIRQ_MIN + 3 + offset : \
54205354Simp	  S3C24X0_SUBIRQ_MIN + 6 + offset))
55205354Simp#define RX_OFF	0
56205354Simp#define TX_OFF	1
57205354Simp#define ERR_OFF	2
58205354Simp
59205354Simpextern unsigned int s3c2410_pclk;
60205354Simp
61205354Simpstatic int sscomspeed(long, long);
62205354Simpstatic int s3c24x0_uart_param(struct uart_bas *, int, int, int, int);
63205354Simp
64205354Simp/*
65205354Simp * Low-level UART interface.
66205354Simp */
67205354Simpstatic int s3c2410_probe(struct uart_bas *bas);
68205354Simpstatic void s3c2410_init(struct uart_bas *bas, int, int, int, int);
69205354Simpstatic void s3c2410_term(struct uart_bas *bas);
70205354Simpstatic void s3c2410_putc(struct uart_bas *bas, int);
71205354Simpstatic int s3c2410_rxready(struct uart_bas *bas);
72205354Simpstatic int s3c2410_getc(struct uart_bas *bas, struct mtx *mtx);
73205354Simp
74205354Simpextern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
75205354Simp
76205354Simpstatic int
77205354Simpsscomspeed(long speed, long frequency)
78205354Simp{
79205354Simp	int x;
80205354Simp
81205354Simp	if (speed <= 0 || frequency <= 0)
82205354Simp		return -1;
83205354Simp	x = (frequency / 16) / speed;
84205354Simp	return x-1;
85205354Simp}
86205354Simp
87205354Simpstatic int
88205354Simps3c24x0_uart_param(struct uart_bas *bas, int baudrate, int databits,
89205354Simp    int stopbits, int parity)
90205354Simp{
91205354Simp	int brd, ulcon;
92205354Simp
93205354Simp	ulcon = 0;
94205354Simp
95205354Simp	switch(databits) {
96205354Simp	case 5:
97205354Simp		ulcon |= ULCON_LENGTH_5;
98205354Simp		break;
99205354Simp	case 6:
100205354Simp		ulcon |= ULCON_LENGTH_6;
101205354Simp		break;
102205354Simp	case 7:
103205354Simp		ulcon |= ULCON_LENGTH_7;
104205354Simp		break;
105205354Simp	case 8:
106205354Simp		ulcon |= ULCON_LENGTH_8;
107205354Simp		break;
108205354Simp	default:
109205354Simp		return (EINVAL);
110205354Simp	}
111205354Simp
112205354Simp	switch (parity) {
113205354Simp	case UART_PARITY_NONE:
114205354Simp		ulcon |= ULCON_PARITY_NONE;
115205354Simp		break;
116205354Simp	case UART_PARITY_ODD:
117205354Simp		ulcon |= ULCON_PARITY_ODD;
118205354Simp		break;
119205354Simp	case UART_PARITY_EVEN:
120205354Simp		ulcon |= ULCON_PARITY_EVEN;
121205354Simp		break;
122205354Simp	case UART_PARITY_MARK:
123205354Simp	case UART_PARITY_SPACE:
124205354Simp	default:
125205354Simp		return (EINVAL);
126205354Simp	}
127205354Simp
128205354Simp	if (stopbits == 2)
129205354Simp		ulcon |= ULCON_STOP;
130205354Simp
131205354Simp	uart_setreg(bas, SSCOM_ULCON, ulcon);
132205354Simp
133205354Simp	brd = sscomspeed(baudrate, bas->rclk);
134205354Simp	uart_setreg(bas, SSCOM_UBRDIV, brd);
135205354Simp
136205354Simp	return (0);
137205354Simp}
138205354Simp
139205354Simpstruct uart_ops uart_s3c2410_ops = {
140205354Simp	.probe = s3c2410_probe,
141205354Simp	.init = s3c2410_init,
142205354Simp	.term = s3c2410_term,
143205354Simp	.putc = s3c2410_putc,
144205354Simp	.rxready = s3c2410_rxready,
145205354Simp	.getc = s3c2410_getc,
146205354Simp};
147205354Simp
148205354Simpstatic int
149205354Simps3c2410_probe(struct uart_bas *bas)
150205354Simp{
151205354Simp	return (0);
152205354Simp}
153205354Simp
154205354Simpstatic void
155205354Simps3c2410_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
156205354Simp    int parity)
157205354Simp{
158205354Simp	if (bas->rclk == 0)
159205354Simp		bas->rclk = s3c2410_pclk;
160205354Simp	KASSERT(bas->rclk != 0, ("s3c2410_init: Invalid rclk"));
161205354Simp
162205354Simp	uart_setreg(bas, SSCOM_UCON, 0);
163205354Simp	uart_setreg(bas, SSCOM_UFCON,
164205354Simp	    UFCON_TXTRIGGER_8 | UFCON_RXTRIGGER_8 |
165205354Simp	    UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET |
166205354Simp	    UFCON_FIFO_ENABLE);
167205354Simp	s3c24x0_uart_param(bas, baudrate, databits, stopbits, parity);
168205354Simp
169205354Simp	/* Enable UART. */
170205354Simp	uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT |
171205354Simp	    UCON_TOINT);
172205354Simp	uart_setreg(bas, SSCOM_UMCON, UMCON_RTS);
173205354Simp}
174205354Simp
175205354Simpstatic void
176205354Simps3c2410_term(struct uart_bas *bas)
177205354Simp{
178205354Simp	/* XXX */
179205354Simp}
180205354Simp
181205354Simpstatic void
182205354Simps3c2410_putc(struct uart_bas *bas, int c)
183205354Simp{
184205354Simp	while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) &
185205354Simp	    UFSTAT_TXFULL) == UFSTAT_TXFULL)
186205354Simp		continue;
187205354Simp
188205354Simp	uart_setreg(bas, SSCOM_UTXH, c);
189205354Simp}
190205354Simp
191205354Simpstatic int
192205354Simps3c2410_rxready(struct uart_bas *bas)
193205354Simp{
194205354Simp	return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) ==
195205354Simp	    UTRSTAT_RXREADY);
196205354Simp}
197205354Simp
198205354Simpstatic int
199205354Simps3c2410_getc(struct uart_bas *bas, struct mtx *mtx)
200205354Simp{
201205354Simp	while (!sscom_rxrdy(bas->bst, bas->bsh))
202205354Simp		continue;
203205354Simp
204205354Simp	return sscom_getc(bas->bst, bas->bsh);
205205354Simp}
206205354Simp
207205354Simpstatic int s3c2410_bus_probe(struct uart_softc *sc);
208205354Simpstatic int s3c2410_bus_attach(struct uart_softc *sc);
209205354Simpstatic int s3c2410_bus_flush(struct uart_softc *, int);
210205354Simpstatic int s3c2410_bus_getsig(struct uart_softc *);
211205354Simpstatic int s3c2410_bus_ioctl(struct uart_softc *, int, intptr_t);
212205354Simpstatic int s3c2410_bus_ipend(struct uart_softc *);
213205354Simpstatic int s3c2410_bus_param(struct uart_softc *, int, int, int, int);
214205354Simpstatic int s3c2410_bus_receive(struct uart_softc *);
215205354Simpstatic int s3c2410_bus_setsig(struct uart_softc *, int);
216205354Simpstatic int s3c2410_bus_transmit(struct uart_softc *);
217205354Simp
218205354Simpstatic kobj_method_t s3c2410_methods[] = {
219205354Simp	KOBJMETHOD(uart_probe,		s3c2410_bus_probe),
220205354Simp	KOBJMETHOD(uart_attach, 	s3c2410_bus_attach),
221205354Simp	KOBJMETHOD(uart_flush,		s3c2410_bus_flush),
222205354Simp	KOBJMETHOD(uart_getsig,		s3c2410_bus_getsig),
223205354Simp	KOBJMETHOD(uart_ioctl,		s3c2410_bus_ioctl),
224205354Simp	KOBJMETHOD(uart_ipend,		s3c2410_bus_ipend),
225205354Simp	KOBJMETHOD(uart_param,		s3c2410_bus_param),
226205354Simp	KOBJMETHOD(uart_receive,	s3c2410_bus_receive),
227205354Simp	KOBJMETHOD(uart_setsig,		s3c2410_bus_setsig),
228205354Simp	KOBJMETHOD(uart_transmit,	s3c2410_bus_transmit),
229205354Simp
230205354Simp	{0, 0 }
231205354Simp};
232205354Simp
233205354Simpint
234205354Simps3c2410_bus_probe(struct uart_softc *sc)
235205354Simp{
236205354Simp	return (0);
237205354Simp}
238205354Simp
239205354Simpstatic int
240205354Simps3c2410_bus_attach(struct uart_softc *sc)
241205354Simp{
242205354Simp	uintptr_t irq;
243205354Simp
244205354Simp	switch(s3c2xx0_softc->sc_cpu) {
245205354Simp	case CPU_S3C2410:
246205354Simp		sc->sc_txfifosz = 16;
247205354Simp		sc->sc_rxfifosz = 16;
248205354Simp		break;
249205354Simp	case CPU_S3C2440:
250205354Simp		sc->sc_txfifosz = 64;
251205354Simp		sc->sc_rxfifosz = 64;
252205354Simp		break;
253205354Simp	default:
254205354Simp		return (ENXIO);
255205354Simp	}
256205354Simp
257205354Simp	sc->sc_hwiflow = 0;
258205354Simp	sc->sc_hwoflow = 0;
259205354Simp
260205354Simp	irq = rman_get_start(sc->sc_ires);
261205354Simp	arm_unmask_irq(irq);
262205354Simp	arm_unmask_irq(get_sub_irq(irq, RX_OFF));
263205354Simp	arm_unmask_irq(get_sub_irq(irq, TX_OFF));
264205354Simp	arm_unmask_irq(get_sub_irq(irq, ERR_OFF));
265205354Simp
266205354Simp	return (0);
267205354Simp}
268205354Simp
269205354Simpstatic int
270205354Simps3c2410_bus_transmit(struct uart_softc *sc)
271205354Simp{
272205354Simp	uintptr_t irq;
273205354Simp
274205354Simp	uart_lock(sc->sc_hwmtx);
275205354Simp
276205354Simp	for (int i = 0; i < sc->sc_txdatasz; i++) {
277205354Simp		s3c2410_putc(&sc->sc_bas, sc->sc_txbuf[i]);
278205354Simp		uart_barrier(&sc->sc_bas);
279205354Simp	}
280205354Simp
281205354Simp	sc->sc_txbusy = 1;
282205354Simp
283205354Simp	uart_unlock(sc->sc_hwmtx);
284205354Simp
285205354Simp	irq = rman_get_start(sc->sc_ires);
286205354Simp	arm_unmask_irq(get_sub_irq(irq, TX_OFF));
287205354Simp
288205354Simp	return (0);
289205354Simp}
290205354Simp
291205354Simpstatic int
292205354Simps3c2410_bus_setsig(struct uart_softc *sc, int sig)
293205354Simp{
294205354Simp	return (0);
295205354Simp}
296205354Simp
297205354Simpstatic int
298205354Simps3c2410_bus_receive(struct uart_softc *sc)
299205354Simp{
300205354Simp
301205354Simp	uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH));
302205354Simp	return (0);
303205354Simp}
304205354Simp
305205354Simpstatic int
306205354Simps3c2410_bus_param(struct uart_softc *sc, int baudrate, int databits,
307205354Simp    int stopbits, int parity)
308205354Simp{
309205354Simp	int error;
310205354Simp
311205354Simp	if (sc->sc_bas.rclk == 0)
312205354Simp		sc->sc_bas.rclk = s3c2410_pclk;
313205354Simp	KASSERT(sc->sc_bas.rclk != 0, ("s3c2410_init: Invalid rclk"));
314205354Simp
315205354Simp	uart_lock(sc->sc_hwmtx);
316205354Simp	error = s3c24x0_uart_param(&sc->sc_bas, baudrate, databits, stopbits,
317205354Simp	    parity);
318205354Simp	uart_unlock(sc->sc_hwmtx);
319205354Simp
320205354Simp	return (error);
321205354Simp}
322205354Simp
323205354Simpstatic int
324205354Simps3c2410_bus_ipend(struct uart_softc *sc)
325205354Simp{
326205354Simp	uint32_t ufstat, txmask, rxmask;
327205354Simp	uintptr_t irq;
328205354Simp	int ipend = 0;
329205354Simp
330205354Simp	uart_lock(sc->sc_hwmtx);
331205354Simp	ufstat = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UFSTAT);
332205354Simp	uart_unlock(sc->sc_hwmtx);
333205354Simp
334205354Simp	txmask = rxmask = 0;
335205354Simp	switch (s3c2xx0_softc->sc_cpu) {
336205354Simp	case CPU_S3C2410:
337205354Simp		txmask = UFSTAT_TXCOUNT;
338205354Simp		rxmask = UFSTAT_RXCOUNT;
339205354Simp		break;
340205354Simp	case CPU_S3C2440:
341205354Simp		txmask = S3C2440_UFSTAT_TXCOUNT;
342205354Simp		rxmask = S3C2440_UFSTAT_RXCOUNT;
343205354Simp		break;
344205354Simp	}
345205354Simp	if ((ufstat & txmask) == 0) {
346205354Simp		if (sc->sc_txbusy != 0)
347205354Simp			ipend |= SER_INT_TXIDLE;
348205354Simp		irq = rman_get_start(sc->sc_ires);
349205354Simp		arm_mask_irq(get_sub_irq(irq, TX_OFF));
350205354Simp	}
351205354Simp	if ((ufstat & rxmask) > 0) {
352205354Simp		ipend |= SER_INT_RXREADY;
353205354Simp	}
354205354Simp
355205354Simp	return (ipend);
356205354Simp}
357205354Simp
358205354Simpstatic int
359205354Simps3c2410_bus_flush(struct uart_softc *sc, int what)
360205354Simp{
361205354Simp	return (0);
362205354Simp}
363205354Simp
364205354Simpstatic int
365205354Simps3c2410_bus_getsig(struct uart_softc *sc)
366205354Simp{
367205354Simp	return (0);
368205354Simp}
369205354Simp
370205354Simpstatic int
371205354Simps3c2410_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
372205354Simp{
373205354Simp	return (EINVAL);
374205354Simp}
375205354Simp
376205354Simpstruct uart_class uart_s3c2410_class = {
377205354Simp	"s3c2410 class",
378205354Simp	s3c2410_methods,
379205354Simp	1,
380205354Simp	.uc_ops = &uart_s3c2410_ops,
381205354Simp	.uc_range = 8,
382205354Simp	.uc_rclk = 0,
383205354Simp};
384