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>
44272103Sgavin#include <arm/samsung/s3c2xx0/s3c2440reg.h>
45272103Sgavin#include <arm/samsung/s3c2xx0/uart_dev_s3c2410.h>
46272103Sgavin#include <arm/samsung/s3c2xx0/s3c2xx0reg.h>
47272103Sgavin#include <arm/samsung/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}
206205354Simpstatic int s3c2410_bus_probe(struct uart_softc *sc);
207205354Simpstatic int s3c2410_bus_attach(struct uart_softc *sc);
208205354Simpstatic int s3c2410_bus_flush(struct uart_softc *, int);
209205354Simpstatic int s3c2410_bus_getsig(struct uart_softc *);
210205354Simpstatic int s3c2410_bus_ioctl(struct uart_softc *, int, intptr_t);
211205354Simpstatic int s3c2410_bus_ipend(struct uart_softc *);
212205354Simpstatic int s3c2410_bus_param(struct uart_softc *, int, int, int, int);
213205354Simpstatic int s3c2410_bus_receive(struct uart_softc *);
214205354Simpstatic int s3c2410_bus_setsig(struct uart_softc *, int);
215205354Simpstatic int s3c2410_bus_transmit(struct uart_softc *);
216260889Simpstatic void s3c2410_bus_grab(struct uart_softc *);
217260889Simpstatic void s3c2410_bus_ungrab(struct uart_softc *);
218205354Simp
219205354Simpstatic kobj_method_t s3c2410_methods[] = {
220205354Simp	KOBJMETHOD(uart_probe,		s3c2410_bus_probe),
221205354Simp	KOBJMETHOD(uart_attach, 	s3c2410_bus_attach),
222205354Simp	KOBJMETHOD(uart_flush,		s3c2410_bus_flush),
223205354Simp	KOBJMETHOD(uart_getsig,		s3c2410_bus_getsig),
224205354Simp	KOBJMETHOD(uart_ioctl,		s3c2410_bus_ioctl),
225205354Simp	KOBJMETHOD(uart_ipend,		s3c2410_bus_ipend),
226205354Simp	KOBJMETHOD(uart_param,		s3c2410_bus_param),
227205354Simp	KOBJMETHOD(uart_receive,	s3c2410_bus_receive),
228205354Simp	KOBJMETHOD(uart_setsig,		s3c2410_bus_setsig),
229205354Simp	KOBJMETHOD(uart_transmit,	s3c2410_bus_transmit),
230260889Simp	KOBJMETHOD(uart_grab,		s3c2410_bus_grab),
231260889Simp	KOBJMETHOD(uart_ungrab,		s3c2410_bus_ungrab),
232205354Simp
233205354Simp	{0, 0 }
234205354Simp};
235205354Simp
236205354Simpint
237205354Simps3c2410_bus_probe(struct uart_softc *sc)
238205354Simp{
239205354Simp	switch(s3c2xx0_softc->sc_cpu) {
240205354Simp	case CPU_S3C2410:
241205354Simp		sc->sc_txfifosz = 16;
242205354Simp		sc->sc_rxfifosz = 16;
243205354Simp		break;
244205354Simp	case CPU_S3C2440:
245205354Simp		sc->sc_txfifosz = 64;
246205354Simp		sc->sc_rxfifosz = 64;
247205354Simp		break;
248205354Simp	default:
249205354Simp		return (ENXIO);
250205354Simp	}
251248965Sian
252248965Sian	return (0);
253248965Sian}
254248965Sian
255248965Sianstatic int
256248965Sians3c2410_bus_attach(struct uart_softc *sc)
257248965Sian{
258248965Sian	uintptr_t irq;
259248965Sian
260205354Simp	sc->sc_hwiflow = 0;
261205354Simp	sc->sc_hwoflow = 0;
262205354Simp
263205354Simp	irq = rman_get_start(sc->sc_ires);
264205354Simp	arm_unmask_irq(irq);
265205354Simp	arm_unmask_irq(get_sub_irq(irq, RX_OFF));
266205354Simp	arm_unmask_irq(get_sub_irq(irq, TX_OFF));
267205354Simp	arm_unmask_irq(get_sub_irq(irq, ERR_OFF));
268205354Simp
269205354Simp	return (0);
270205354Simp}
271205354Simp
272205354Simpstatic int
273205354Simps3c2410_bus_transmit(struct uart_softc *sc)
274205354Simp{
275205354Simp	uintptr_t irq;
276205354Simp
277205354Simp	uart_lock(sc->sc_hwmtx);
278205354Simp
279205354Simp	for (int i = 0; i < sc->sc_txdatasz; i++) {
280205354Simp		s3c2410_putc(&sc->sc_bas, sc->sc_txbuf[i]);
281205354Simp		uart_barrier(&sc->sc_bas);
282205354Simp	}
283205354Simp
284205354Simp	sc->sc_txbusy = 1;
285205354Simp
286205354Simp	uart_unlock(sc->sc_hwmtx);
287205354Simp
288205354Simp	irq = rman_get_start(sc->sc_ires);
289205354Simp	arm_unmask_irq(get_sub_irq(irq, TX_OFF));
290205354Simp
291205354Simp	return (0);
292205354Simp}
293205354Simp
294205354Simpstatic int
295205354Simps3c2410_bus_setsig(struct uart_softc *sc, int sig)
296205354Simp{
297205354Simp	return (0);
298205354Simp}
299205354Simp
300205354Simpstatic int
301205354Simps3c2410_bus_receive(struct uart_softc *sc)
302205354Simp{
303205354Simp
304205354Simp	uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH));
305205354Simp	return (0);
306205354Simp}
307205354Simp
308205354Simpstatic int
309205354Simps3c2410_bus_param(struct uart_softc *sc, int baudrate, int databits,
310205354Simp    int stopbits, int parity)
311205354Simp{
312205354Simp	int error;
313205354Simp
314205354Simp	if (sc->sc_bas.rclk == 0)
315205354Simp		sc->sc_bas.rclk = s3c2410_pclk;
316205354Simp	KASSERT(sc->sc_bas.rclk != 0, ("s3c2410_init: Invalid rclk"));
317205354Simp
318205354Simp	uart_lock(sc->sc_hwmtx);
319205354Simp	error = s3c24x0_uart_param(&sc->sc_bas, baudrate, databits, stopbits,
320205354Simp	    parity);
321205354Simp	uart_unlock(sc->sc_hwmtx);
322205354Simp
323205354Simp	return (error);
324205354Simp}
325205354Simp
326205354Simpstatic int
327205354Simps3c2410_bus_ipend(struct uart_softc *sc)
328205354Simp{
329205354Simp	uint32_t ufstat, txmask, rxmask;
330205354Simp	uintptr_t irq;
331205354Simp	int ipend = 0;
332205354Simp
333205354Simp	uart_lock(sc->sc_hwmtx);
334205354Simp	ufstat = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UFSTAT);
335205354Simp	uart_unlock(sc->sc_hwmtx);
336205354Simp
337205354Simp	txmask = rxmask = 0;
338205354Simp	switch (s3c2xx0_softc->sc_cpu) {
339205354Simp	case CPU_S3C2410:
340205354Simp		txmask = UFSTAT_TXCOUNT;
341205354Simp		rxmask = UFSTAT_RXCOUNT;
342205354Simp		break;
343205354Simp	case CPU_S3C2440:
344205354Simp		txmask = S3C2440_UFSTAT_TXCOUNT;
345205354Simp		rxmask = S3C2440_UFSTAT_RXCOUNT;
346205354Simp		break;
347205354Simp	}
348205354Simp	if ((ufstat & txmask) == 0) {
349205354Simp		if (sc->sc_txbusy != 0)
350205354Simp			ipend |= SER_INT_TXIDLE;
351205354Simp		irq = rman_get_start(sc->sc_ires);
352205354Simp		arm_mask_irq(get_sub_irq(irq, TX_OFF));
353205354Simp	}
354205354Simp	if ((ufstat & rxmask) > 0) {
355205354Simp		ipend |= SER_INT_RXREADY;
356205354Simp	}
357205354Simp
358205354Simp	return (ipend);
359205354Simp}
360205354Simp
361205354Simpstatic int
362205354Simps3c2410_bus_flush(struct uart_softc *sc, int what)
363205354Simp{
364205354Simp	return (0);
365205354Simp}
366205354Simp
367205354Simpstatic int
368205354Simps3c2410_bus_getsig(struct uart_softc *sc)
369205354Simp{
370205354Simp	return (0);
371205354Simp}
372205354Simp
373205354Simpstatic int
374205354Simps3c2410_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
375205354Simp{
376205354Simp	return (EINVAL);
377205354Simp}
378205354Simp
379260889Simp
380260889Simpstatic void
381260889Simps3c2410_bus_grab(struct uart_softc *sc)
382260889Simp{
383260889Simp	uintptr_t irq;
384260889Simp
385260889Simp	irq = rman_get_start(sc->sc_ires);
386260889Simp	arm_mask_irq(get_sub_irq(irq, RX_OFF));
387260889Simp}
388260889Simp
389260889Simpstatic void
390260889Simps3c2410_bus_ungrab(struct uart_softc *sc)
391260889Simp{
392260889Simp	uintptr_t irq;
393260889Simp
394260889Simp	irq = rman_get_start(sc->sc_ires);
395260889Simp	arm_unmask_irq(get_sub_irq(irq, RX_OFF));
396260889Simp}
397260889Simp
398205354Simpstruct uart_class uart_s3c2410_class = {
399205354Simp	"s3c2410 class",
400205354Simp	s3c2410_methods,
401205354Simp	1,
402205354Simp	.uc_ops = &uart_s3c2410_ops,
403205354Simp	.uc_range = 8,
404205354Simp	.uc_rclk = 0,
405205354Simp};
406