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/samsung/s3c2xx0/s3c2440reg.h>
45#include <arm/samsung/s3c2xx0/uart_dev_s3c2410.h>
46#include <arm/samsung/s3c2xx0/s3c2xx0reg.h>
47#include <arm/samsung/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}
206static int s3c2410_bus_probe(struct uart_softc *sc);
207static int s3c2410_bus_attach(struct uart_softc *sc);
208static int s3c2410_bus_flush(struct uart_softc *, int);
209static int s3c2410_bus_getsig(struct uart_softc *);
210static int s3c2410_bus_ioctl(struct uart_softc *, int, intptr_t);
211static int s3c2410_bus_ipend(struct uart_softc *);
212static int s3c2410_bus_param(struct uart_softc *, int, int, int, int);
213static int s3c2410_bus_receive(struct uart_softc *);
214static int s3c2410_bus_setsig(struct uart_softc *, int);
215static int s3c2410_bus_transmit(struct uart_softc *);
216static void s3c2410_bus_grab(struct uart_softc *);
217static void s3c2410_bus_ungrab(struct uart_softc *);
218
219static kobj_method_t s3c2410_methods[] = {
220	KOBJMETHOD(uart_probe,		s3c2410_bus_probe),
221	KOBJMETHOD(uart_attach, 	s3c2410_bus_attach),
222	KOBJMETHOD(uart_flush,		s3c2410_bus_flush),
223	KOBJMETHOD(uart_getsig,		s3c2410_bus_getsig),
224	KOBJMETHOD(uart_ioctl,		s3c2410_bus_ioctl),
225	KOBJMETHOD(uart_ipend,		s3c2410_bus_ipend),
226	KOBJMETHOD(uart_param,		s3c2410_bus_param),
227	KOBJMETHOD(uart_receive,	s3c2410_bus_receive),
228	KOBJMETHOD(uart_setsig,		s3c2410_bus_setsig),
229	KOBJMETHOD(uart_transmit,	s3c2410_bus_transmit),
230	KOBJMETHOD(uart_grab,		s3c2410_bus_grab),
231	KOBJMETHOD(uart_ungrab,		s3c2410_bus_ungrab),
232
233	{0, 0 }
234};
235
236int
237s3c2410_bus_probe(struct uart_softc *sc)
238{
239	switch(s3c2xx0_softc->sc_cpu) {
240	case CPU_S3C2410:
241		sc->sc_txfifosz = 16;
242		sc->sc_rxfifosz = 16;
243		break;
244	case CPU_S3C2440:
245		sc->sc_txfifosz = 64;
246		sc->sc_rxfifosz = 64;
247		break;
248	default:
249		return (ENXIO);
250	}
251
252	return (0);
253}
254
255static int
256s3c2410_bus_attach(struct uart_softc *sc)
257{
258	uintptr_t irq;
259
260	sc->sc_hwiflow = 0;
261	sc->sc_hwoflow = 0;
262
263	irq = rman_get_start(sc->sc_ires);
264	arm_unmask_irq(irq);
265	arm_unmask_irq(get_sub_irq(irq, RX_OFF));
266	arm_unmask_irq(get_sub_irq(irq, TX_OFF));
267	arm_unmask_irq(get_sub_irq(irq, ERR_OFF));
268
269	return (0);
270}
271
272static int
273s3c2410_bus_transmit(struct uart_softc *sc)
274{
275	uintptr_t irq;
276
277	uart_lock(sc->sc_hwmtx);
278
279	for (int i = 0; i < sc->sc_txdatasz; i++) {
280		s3c2410_putc(&sc->sc_bas, sc->sc_txbuf[i]);
281		uart_barrier(&sc->sc_bas);
282	}
283
284	sc->sc_txbusy = 1;
285
286	uart_unlock(sc->sc_hwmtx);
287
288	irq = rman_get_start(sc->sc_ires);
289	arm_unmask_irq(get_sub_irq(irq, TX_OFF));
290
291	return (0);
292}
293
294static int
295s3c2410_bus_setsig(struct uart_softc *sc, int sig)
296{
297	return (0);
298}
299
300static int
301s3c2410_bus_receive(struct uart_softc *sc)
302{
303
304	uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH));
305	return (0);
306}
307
308static int
309s3c2410_bus_param(struct uart_softc *sc, int baudrate, int databits,
310    int stopbits, int parity)
311{
312	int error;
313
314	if (sc->sc_bas.rclk == 0)
315		sc->sc_bas.rclk = s3c2410_pclk;
316	KASSERT(sc->sc_bas.rclk != 0, ("s3c2410_init: Invalid rclk"));
317
318	uart_lock(sc->sc_hwmtx);
319	error = s3c24x0_uart_param(&sc->sc_bas, baudrate, databits, stopbits,
320	    parity);
321	uart_unlock(sc->sc_hwmtx);
322
323	return (error);
324}
325
326static int
327s3c2410_bus_ipend(struct uart_softc *sc)
328{
329	uint32_t ufstat, txmask, rxmask;
330	uintptr_t irq;
331	int ipend = 0;
332
333	uart_lock(sc->sc_hwmtx);
334	ufstat = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UFSTAT);
335	uart_unlock(sc->sc_hwmtx);
336
337	txmask = rxmask = 0;
338	switch (s3c2xx0_softc->sc_cpu) {
339	case CPU_S3C2410:
340		txmask = UFSTAT_TXCOUNT;
341		rxmask = UFSTAT_RXCOUNT;
342		break;
343	case CPU_S3C2440:
344		txmask = S3C2440_UFSTAT_TXCOUNT;
345		rxmask = S3C2440_UFSTAT_RXCOUNT;
346		break;
347	}
348	if ((ufstat & txmask) == 0) {
349		if (sc->sc_txbusy != 0)
350			ipend |= SER_INT_TXIDLE;
351		irq = rman_get_start(sc->sc_ires);
352		arm_mask_irq(get_sub_irq(irq, TX_OFF));
353	}
354	if ((ufstat & rxmask) > 0) {
355		ipend |= SER_INT_RXREADY;
356	}
357
358	return (ipend);
359}
360
361static int
362s3c2410_bus_flush(struct uart_softc *sc, int what)
363{
364	return (0);
365}
366
367static int
368s3c2410_bus_getsig(struct uart_softc *sc)
369{
370	return (0);
371}
372
373static int
374s3c2410_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
375{
376	return (EINVAL);
377}
378
379
380static void
381s3c2410_bus_grab(struct uart_softc *sc)
382{
383	uintptr_t irq;
384
385	irq = rman_get_start(sc->sc_ires);
386	arm_mask_irq(get_sub_irq(irq, RX_OFF));
387}
388
389static void
390s3c2410_bus_ungrab(struct uart_softc *sc)
391{
392	uintptr_t irq;
393
394	irq = rman_get_start(sc->sc_ires);
395	arm_unmask_irq(get_sub_irq(irq, RX_OFF));
396}
397
398struct uart_class uart_s3c2410_class = {
399	"s3c2410 class",
400	s3c2410_methods,
401	1,
402	.uc_ops = &uart_s3c2410_ops,
403	.uc_range = 8,
404	.uc_rclk = 0,
405};
406