1/*
2 * Copyright (c) 2003 Marcel Moolenaar
3 * Copyright (c) 2007-2009 Andrew Turner
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD$");
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/bus.h>
34#include <sys/conf.h>
35#include <sys/cons.h>
36#include <sys/tty.h>
37#include <sys/rman.h>
38#include <machine/bus.h>
39#include <machine/intr.h>
40
41#include <dev/uart/uart.h>
42#include <dev/uart/uart_cpu.h>
43#include <dev/uart/uart_bus.h>
44#include <arm/s3c2xx0/s3c2440reg.h>
45#include <arm/s3c2xx0/uart_dev_s3c2410.h>
46#include <arm/s3c2xx0/s3c2xx0reg.h>
47#include <arm/s3c2xx0/s3c2xx0var.h>
48#include "uart_if.h"
49
50/* Finds the subirq from the parent */
51#define get_sub_irq(parent, offset) \
52	((parent == S3C24X0_INT_UART0) ? S3C24X0_SUBIRQ_MIN + offset : \
53	((parent == S3C24X0_INT_UART1) ? S3C24X0_SUBIRQ_MIN + 3 + offset : \
54	  S3C24X0_SUBIRQ_MIN + 6 + offset))
55#define RX_OFF	0
56#define TX_OFF	1
57#define ERR_OFF	2
58
59extern unsigned int s3c2410_pclk;
60
61static int sscomspeed(long, long);
62static int s3c24x0_uart_param(struct uart_bas *, int, int, int, int);
63
64/*
65 * Low-level UART interface.
66 */
67static int s3c2410_probe(struct uart_bas *bas);
68static void s3c2410_init(struct uart_bas *bas, int, int, int, int);
69static void s3c2410_term(struct uart_bas *bas);
70static void s3c2410_putc(struct uart_bas *bas, int);
71static int s3c2410_rxready(struct uart_bas *bas);
72static int s3c2410_getc(struct uart_bas *bas, struct mtx *mtx);
73
74extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
75
76static int
77sscomspeed(long speed, long frequency)
78{
79	int x;
80
81	if (speed <= 0 || frequency <= 0)
82		return -1;
83	x = (frequency / 16) / speed;
84	return x-1;
85}
86
87static int
88s3c24x0_uart_param(struct uart_bas *bas, int baudrate, int databits,
89    int stopbits, int parity)
90{
91	int brd, ulcon;
92
93	ulcon = 0;
94
95	switch(databits) {
96	case 5:
97		ulcon |= ULCON_LENGTH_5;
98		break;
99	case 6:
100		ulcon |= ULCON_LENGTH_6;
101		break;
102	case 7:
103		ulcon |= ULCON_LENGTH_7;
104		break;
105	case 8:
106		ulcon |= ULCON_LENGTH_8;
107		break;
108	default:
109		return (EINVAL);
110	}
111
112	switch (parity) {
113	case UART_PARITY_NONE:
114		ulcon |= ULCON_PARITY_NONE;
115		break;
116	case UART_PARITY_ODD:
117		ulcon |= ULCON_PARITY_ODD;
118		break;
119	case UART_PARITY_EVEN:
120		ulcon |= ULCON_PARITY_EVEN;
121		break;
122	case UART_PARITY_MARK:
123	case UART_PARITY_SPACE:
124	default:
125		return (EINVAL);
126	}
127
128	if (stopbits == 2)
129		ulcon |= ULCON_STOP;
130
131	uart_setreg(bas, SSCOM_ULCON, ulcon);
132
133	brd = sscomspeed(baudrate, bas->rclk);
134	uart_setreg(bas, SSCOM_UBRDIV, brd);
135
136	return (0);
137}
138
139struct uart_ops uart_s3c2410_ops = {
140	.probe = s3c2410_probe,
141	.init = s3c2410_init,
142	.term = s3c2410_term,
143	.putc = s3c2410_putc,
144	.rxready = s3c2410_rxready,
145	.getc = s3c2410_getc,
146};
147
148static int
149s3c2410_probe(struct uart_bas *bas)
150{
151	return (0);
152}
153
154static void
155s3c2410_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
156    int parity)
157{
158	if (bas->rclk == 0)
159		bas->rclk = s3c2410_pclk;
160	KASSERT(bas->rclk != 0, ("s3c2410_init: Invalid rclk"));
161
162	uart_setreg(bas, SSCOM_UCON, 0);
163	uart_setreg(bas, SSCOM_UFCON,
164	    UFCON_TXTRIGGER_8 | UFCON_RXTRIGGER_8 |
165	    UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET |
166	    UFCON_FIFO_ENABLE);
167	s3c24x0_uart_param(bas, baudrate, databits, stopbits, parity);
168
169	/* Enable UART. */
170	uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT |
171	    UCON_TOINT);
172	uart_setreg(bas, SSCOM_UMCON, UMCON_RTS);
173}
174
175static void
176s3c2410_term(struct uart_bas *bas)
177{
178	/* XXX */
179}
180
181static void
182s3c2410_putc(struct uart_bas *bas, int c)
183{
184	while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) &
185	    UFSTAT_TXFULL) == UFSTAT_TXFULL)
186		continue;
187
188	uart_setreg(bas, SSCOM_UTXH, c);
189}
190
191static int
192s3c2410_rxready(struct uart_bas *bas)
193{
194	return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) ==
195	    UTRSTAT_RXREADY);
196}
197
198static int
199s3c2410_getc(struct uart_bas *bas, struct mtx *mtx)
200{
201	while (!sscom_rxrdy(bas->bst, bas->bsh))
202		continue;
203
204	return sscom_getc(bas->bst, bas->bsh);
205}
206
207static int s3c2410_bus_probe(struct uart_softc *sc);
208static int s3c2410_bus_attach(struct uart_softc *sc);
209static int s3c2410_bus_flush(struct uart_softc *, int);
210static int s3c2410_bus_getsig(struct uart_softc *);
211static int s3c2410_bus_ioctl(struct uart_softc *, int, intptr_t);
212static int s3c2410_bus_ipend(struct uart_softc *);
213static int s3c2410_bus_param(struct uart_softc *, int, int, int, int);
214static int s3c2410_bus_receive(struct uart_softc *);
215static int s3c2410_bus_setsig(struct uart_softc *, int);
216static int s3c2410_bus_transmit(struct uart_softc *);
217
218static kobj_method_t s3c2410_methods[] = {
219	KOBJMETHOD(uart_probe,		s3c2410_bus_probe),
220	KOBJMETHOD(uart_attach, 	s3c2410_bus_attach),
221	KOBJMETHOD(uart_flush,		s3c2410_bus_flush),
222	KOBJMETHOD(uart_getsig,		s3c2410_bus_getsig),
223	KOBJMETHOD(uart_ioctl,		s3c2410_bus_ioctl),
224	KOBJMETHOD(uart_ipend,		s3c2410_bus_ipend),
225	KOBJMETHOD(uart_param,		s3c2410_bus_param),
226	KOBJMETHOD(uart_receive,	s3c2410_bus_receive),
227	KOBJMETHOD(uart_setsig,		s3c2410_bus_setsig),
228	KOBJMETHOD(uart_transmit,	s3c2410_bus_transmit),
229
230	{0, 0 }
231};
232
233int
234s3c2410_bus_probe(struct uart_softc *sc)
235{
236	return (0);
237}
238
239static int
240s3c2410_bus_attach(struct uart_softc *sc)
241{
242	uintptr_t irq;
243
244	switch(s3c2xx0_softc->sc_cpu) {
245	case CPU_S3C2410:
246		sc->sc_txfifosz = 16;
247		sc->sc_rxfifosz = 16;
248		break;
249	case CPU_S3C2440:
250		sc->sc_txfifosz = 64;
251		sc->sc_rxfifosz = 64;
252		break;
253	default:
254		return (ENXIO);
255	}
256
257	sc->sc_hwiflow = 0;
258	sc->sc_hwoflow = 0;
259
260	irq = rman_get_start(sc->sc_ires);
261	arm_unmask_irq(irq);
262	arm_unmask_irq(get_sub_irq(irq, RX_OFF));
263	arm_unmask_irq(get_sub_irq(irq, TX_OFF));
264	arm_unmask_irq(get_sub_irq(irq, ERR_OFF));
265
266	return (0);
267}
268
269static int
270s3c2410_bus_transmit(struct uart_softc *sc)
271{
272	uintptr_t irq;
273
274	uart_lock(sc->sc_hwmtx);
275
276	for (int i = 0; i < sc->sc_txdatasz; i++) {
277		s3c2410_putc(&sc->sc_bas, sc->sc_txbuf[i]);
278		uart_barrier(&sc->sc_bas);
279	}
280
281	sc->sc_txbusy = 1;
282
283	uart_unlock(sc->sc_hwmtx);
284
285	irq = rman_get_start(sc->sc_ires);
286	arm_unmask_irq(get_sub_irq(irq, TX_OFF));
287
288	return (0);
289}
290
291static int
292s3c2410_bus_setsig(struct uart_softc *sc, int sig)
293{
294	return (0);
295}
296
297static int
298s3c2410_bus_receive(struct uart_softc *sc)
299{
300
301	uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH));
302	return (0);
303}
304
305static int
306s3c2410_bus_param(struct uart_softc *sc, int baudrate, int databits,
307    int stopbits, int parity)
308{
309	int error;
310
311	if (sc->sc_bas.rclk == 0)
312		sc->sc_bas.rclk = s3c2410_pclk;
313	KASSERT(sc->sc_bas.rclk != 0, ("s3c2410_init: Invalid rclk"));
314
315	uart_lock(sc->sc_hwmtx);
316	error = s3c24x0_uart_param(&sc->sc_bas, baudrate, databits, stopbits,
317	    parity);
318	uart_unlock(sc->sc_hwmtx);
319
320	return (error);
321}
322
323static int
324s3c2410_bus_ipend(struct uart_softc *sc)
325{
326	uint32_t ufstat, txmask, rxmask;
327	uintptr_t irq;
328	int ipend = 0;
329
330	uart_lock(sc->sc_hwmtx);
331	ufstat = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UFSTAT);
332	uart_unlock(sc->sc_hwmtx);
333
334	txmask = rxmask = 0;
335	switch (s3c2xx0_softc->sc_cpu) {
336	case CPU_S3C2410:
337		txmask = UFSTAT_TXCOUNT;
338		rxmask = UFSTAT_RXCOUNT;
339		break;
340	case CPU_S3C2440:
341		txmask = S3C2440_UFSTAT_TXCOUNT;
342		rxmask = S3C2440_UFSTAT_RXCOUNT;
343		break;
344	}
345	if ((ufstat & txmask) == 0) {
346		if (sc->sc_txbusy != 0)
347			ipend |= SER_INT_TXIDLE;
348		irq = rman_get_start(sc->sc_ires);
349		arm_mask_irq(get_sub_irq(irq, TX_OFF));
350	}
351	if ((ufstat & rxmask) > 0) {
352		ipend |= SER_INT_RXREADY;
353	}
354
355	return (ipend);
356}
357
358static int
359s3c2410_bus_flush(struct uart_softc *sc, int what)
360{
361	return (0);
362}
363
364static int
365s3c2410_bus_getsig(struct uart_softc *sc)
366{
367	return (0);
368}
369
370static int
371s3c2410_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
372{
373	return (EINVAL);
374}
375
376struct uart_class uart_s3c2410_class = {
377	"s3c2410 class",
378	s3c2410_methods,
379	1,
380	.uc_ops = &uart_s3c2410_ops,
381	.uc_range = 8,
382	.uc_rclk = 0,
383};
384