1178173Simp/* $NetBSD: uart.c,v 1.2 2007/03/23 20:05:47 dogcow Exp $ */
2178173Simp
3178173Simp/*-
4178173Simp * Copyright (c) 2007 Ruslan Ermilov and Vsevolod Lobko.
5178173Simp * Copyright (c) 2007 Oleksandr Tymoshenko.
6178173Simp * All rights reserved.
7178173Simp *
8178173Simp * Redistribution and use in source and binary forms, with or
9178173Simp * without modification, are permitted provided that the following
10178173Simp * conditions are met:
11178173Simp * 1. Redistributions of source code must retain the above copyright
12178173Simp *    notice, this list of conditions and the following disclaimer.
13178173Simp * 2. Redistributions in binary form must reproduce the above
14178173Simp *    copyright notice, this list of conditions and the following
15178173Simp *    disclaimer in the documentation and/or other materials provided
16178173Simp *    with the distribution.
17178173Simp * 3. The names of the authors may not be used to endorse or promote
18178173Simp *    products derived from this software without specific prior
19178173Simp *    written permission.
20178173Simp *
21178173Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY
22178173Simp * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23178173Simp * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24178173Simp * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS
25178173Simp * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
26178173Simp * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27178173Simp * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
28178173Simp * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29178173Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
30178173Simp * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31178173Simp * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
32178173Simp * OF SUCH DAMAGE.
33178173Simp */
34178173Simp
35178173Simp#include <sys/cdefs.h>
36178173Simp__FBSDID("$FreeBSD$");
37178173Simp
38178173Simp#include <sys/param.h>
39178173Simp#include <sys/systm.h>
40178173Simp#include <sys/bus.h>
41178173Simp#include <sys/conf.h>
42178173Simp#include <machine/bus.h>
43178173Simp
44178173Simp#include <dev/uart/uart.h>
45178173Simp#include <dev/uart/uart_cpu.h>
46178173Simp#include <dev/uart/uart_bus.h>
47178173Simp
48182901Sgonzo#include <mips/adm5120/uart_dev_adm5120.h>
49178173Simp
50178173Simp#include "uart_if.h"
51178173Simp
52178173Simp/*
53178173Simp * Low-level UART interface.
54178173Simp */
55178173Simpstatic int adm5120_uart_probe(struct uart_bas *bas);
56178173Simpstatic void adm5120_uart_init(struct uart_bas *bas, int, int, int, int);
57178173Simpstatic void adm5120_uart_term(struct uart_bas *bas);
58178173Simpstatic void adm5120_uart_putc(struct uart_bas *bas, int);
59178173Simpstatic int adm5120_uart_rxready(struct uart_bas *bas);
60178173Simpstatic int adm5120_uart_getc(struct uart_bas *bas, struct mtx *);
61178173Simp
62178173Simpstatic struct uart_ops uart_adm5120_uart_ops = {
63178173Simp	.probe = adm5120_uart_probe,
64178173Simp	.init = adm5120_uart_init,
65178173Simp	.term = adm5120_uart_term,
66178173Simp	.putc = adm5120_uart_putc,
67178173Simp	.rxready = adm5120_uart_rxready,
68178173Simp	.getc = adm5120_uart_getc,
69178173Simp};
70178173Simp
71178173Simpstatic int
72178173Simpadm5120_uart_probe(struct uart_bas *bas)
73178173Simp{
74178173Simp
75178173Simp	return (0);
76178173Simp}
77178173Simp
78178173Simpstatic void
79178173Simpadm5120_uart_init(struct uart_bas *bas, int baudrate, int databits,
80178173Simp    int stopbits, int parity)
81178173Simp{
82178173Simp
83178173Simp	/* TODO: Set parameters for uart, meanwhile stick with 115200N1 */
84178173Simp}
85178173Simp
86178173Simpstatic void
87178173Simpadm5120_uart_term(struct uart_bas *bas)
88178173Simp{
89178173Simp
90178173Simp}
91178173Simp
92178173Simpstatic void
93178173Simpadm5120_uart_putc(struct uart_bas *bas, int c)
94178173Simp{
95178173Simp	char chr;
96178173Simp	chr = c;
97178173Simp	while (uart_getreg(bas, UART_FR_REG) & UART_FR_TX_FIFO_FULL)
98178173Simp		;
99178173Simp	uart_setreg(bas, UART_DR_REG, c);
100178173Simp	while (uart_getreg(bas, UART_FR_REG) & UART_FR_BUSY)
101178173Simp		;
102178173Simp	uart_barrier(bas);
103178173Simp}
104178173Simp
105178173Simpstatic int
106178173Simpadm5120_uart_rxready(struct uart_bas *bas)
107178173Simp{
108178173Simp	if (uart_getreg(bas, UART_FR_REG) & UART_FR_RX_FIFO_EMPTY)
109178173Simp		return (0);
110178173Simp
111178173Simp	return (1);
112178173Simp}
113178173Simp
114178173Simpstatic int
115178173Simpadm5120_uart_getc(struct uart_bas *bas, struct mtx *hwmtx)
116178173Simp{
117178173Simp	int c;
118178173Simp
119178173Simp	uart_lock(hwmtx);
120178173Simp
121178173Simp	while (uart_getreg(bas, UART_FR_REG) & UART_FR_RX_FIFO_EMPTY) {
122178173Simp		uart_unlock(hwmtx);
123178173Simp		DELAY(10);
124178173Simp		uart_lock(hwmtx);
125178173Simp	}
126178173Simp
127178173Simp	c = uart_getreg(bas, UART_DR_REG);
128178173Simp
129178173Simp	uart_unlock(hwmtx);
130178173Simp
131178173Simp	return (c);
132178173Simp}
133178173Simp
134178173Simp/*
135178173Simp * High-level UART interface.
136178173Simp */
137178173Simpstruct adm5120_uart_softc {
138178173Simp	struct uart_softc base;
139178173Simp};
140178173Simp
141178173Simpstatic int adm5120_uart_bus_attach(struct uart_softc *);
142178173Simpstatic int adm5120_uart_bus_detach(struct uart_softc *);
143178173Simpstatic int adm5120_uart_bus_flush(struct uart_softc *, int);
144178173Simpstatic int adm5120_uart_bus_getsig(struct uart_softc *);
145178173Simpstatic int adm5120_uart_bus_ioctl(struct uart_softc *, int, intptr_t);
146178173Simpstatic int adm5120_uart_bus_ipend(struct uart_softc *);
147178173Simpstatic int adm5120_uart_bus_param(struct uart_softc *, int, int, int, int);
148178173Simpstatic int adm5120_uart_bus_probe(struct uart_softc *);
149178173Simpstatic int adm5120_uart_bus_receive(struct uart_softc *);
150178173Simpstatic int adm5120_uart_bus_setsig(struct uart_softc *, int);
151178173Simpstatic int adm5120_uart_bus_transmit(struct uart_softc *);
152262649Simpstatic void adm5120_uart_bus_grab(struct uart_softc *);
153262649Simpstatic void adm5120_uart_bus_ungrab(struct uart_softc *);
154178173Simp
155178173Simpstatic kobj_method_t adm5120_uart_methods[] = {
156178173Simp	KOBJMETHOD(uart_attach,		adm5120_uart_bus_attach),
157178173Simp	KOBJMETHOD(uart_detach,		adm5120_uart_bus_detach),
158178173Simp	KOBJMETHOD(uart_flush,		adm5120_uart_bus_flush),
159178173Simp	KOBJMETHOD(uart_getsig,		adm5120_uart_bus_getsig),
160178173Simp	KOBJMETHOD(uart_ioctl,		adm5120_uart_bus_ioctl),
161178173Simp	KOBJMETHOD(uart_ipend,		adm5120_uart_bus_ipend),
162178173Simp	KOBJMETHOD(uart_param,		adm5120_uart_bus_param),
163178173Simp	KOBJMETHOD(uart_probe,		adm5120_uart_bus_probe),
164178173Simp	KOBJMETHOD(uart_receive,	adm5120_uart_bus_receive),
165178173Simp	KOBJMETHOD(uart_setsig,		adm5120_uart_bus_setsig),
166178173Simp	KOBJMETHOD(uart_transmit,	adm5120_uart_bus_transmit),
167262649Simp	KOBJMETHOD(uart_grab,		adm5120_uart_bus_grab),
168262649Simp	KOBJMETHOD(uart_ungrab,		adm5120_uart_bus_ungrab),
169178173Simp	{ 0, 0 }
170178173Simp};
171178173Simp
172178173Simpstruct uart_class uart_adm5120_uart_class = {
173178173Simp	"adm5120",
174178173Simp	adm5120_uart_methods,
175178173Simp	sizeof(struct adm5120_uart_softc),
176178173Simp	.uc_ops = &uart_adm5120_uart_ops,
177178173Simp	.uc_range = 1, /* use hinted range */
178178173Simp	.uc_rclk = 62500000
179178173Simp};
180178173Simp
181178173Simp#define	SIGCHG(c, i, s, d)				\
182178173Simp	if (c) {					\
183178173Simp		i |= (i & s) ? s : s | d;		\
184178173Simp	} else {					\
185178173Simp		i = (i & s) ? (i & ~s) | d : i;		\
186178173Simp	}
187178173Simp
188178173Simp/*
189178173Simp * Disable TX interrupt. uart should be locked
190178173Simp */
191178173Simpstatic __inline void
192178173Simpadm5120_uart_disable_txintr(struct uart_softc *sc)
193178173Simp{
194178173Simp	uint8_t cr;
195178173Simp
196178173Simp	cr = uart_getreg(&sc->sc_bas, UART_CR_REG);
197178173Simp	cr &= ~UART_CR_TX_INT_EN;
198178173Simp	uart_setreg(&sc->sc_bas, UART_CR_REG, cr);
199178173Simp}
200178173Simp
201178173Simp/*
202178173Simp * Enable TX interrupt. uart should be locked
203178173Simp */
204178173Simpstatic __inline void
205178173Simpadm5120_uart_enable_txintr(struct uart_softc *sc)
206178173Simp{
207178173Simp	uint8_t cr;
208178173Simp
209178173Simp	cr = uart_getreg(&sc->sc_bas, UART_CR_REG);
210178173Simp	cr |= UART_CR_TX_INT_EN;
211178173Simp	uart_setreg(&sc->sc_bas, UART_CR_REG, cr);
212178173Simp}
213178173Simp
214178173Simpstatic int
215178173Simpadm5120_uart_bus_attach(struct uart_softc *sc)
216178173Simp{
217178173Simp	struct uart_bas *bas;
218178173Simp	struct uart_devinfo *di;
219178173Simp
220178173Simp	bas = &sc->sc_bas;
221178173Simp	if (sc->sc_sysdev != NULL) {
222178173Simp		di = sc->sc_sysdev;
223178173Simp		/* TODO: set parameters from di */
224178173Simp	} else {
225178173Simp		/* TODO: set parameters 115200, 8N1 */
226178173Simp	}
227178173Simp
228178173Simp	(void)adm5120_uart_bus_getsig(sc);
229178173Simp
230178173Simp#if 1
231178173Simp	/* Enable FIFO */
232178173Simp	uart_setreg(bas, UART_LCR_H_REG,
233178173Simp	    uart_getreg(bas, UART_LCR_H_REG) | UART_LCR_H_FEN);
234178173Simp#endif
235178173Simp	/* Enable interrupts */
236178173Simp	uart_setreg(bas, UART_CR_REG,
237178173Simp	    UART_CR_PORT_EN|UART_CR_RX_INT_EN|UART_CR_RX_TIMEOUT_INT_EN|
238178173Simp	    UART_CR_MODEM_STATUS_INT_EN);
239178173Simp
240178173Simp	return (0);
241178173Simp}
242178173Simp
243178173Simpstatic int
244178173Simpadm5120_uart_bus_detach(struct uart_softc *sc)
245178173Simp{
246178173Simp
247178173Simp	return (0);
248178173Simp}
249178173Simp
250178173Simpstatic int
251178173Simpadm5120_uart_bus_flush(struct uart_softc *sc, int what)
252178173Simp{
253178173Simp
254178173Simp	return (0);
255178173Simp}
256178173Simp
257178173Simpstatic int
258178173Simpadm5120_uart_bus_getsig(struct uart_softc *sc)
259178173Simp{
260178173Simp	uint32_t new, old, sig;
261178173Simp	uint8_t bes;
262178173Simp
263178173Simp	do {
264178173Simp		old = sc->sc_hwsig;
265178173Simp		sig = old;
266178173Simp		uart_lock(sc->sc_hwmtx);
267178173Simp		bes = uart_getreg(&sc->sc_bas, UART_FR_REG);
268178173Simp		uart_unlock(sc->sc_hwmtx);
269178173Simp		SIGCHG(bes & UART_FR_CTS, sig, SER_CTS, SER_DCTS);
270178173Simp		SIGCHG(bes & UART_FR_DCD, sig, SER_DCD, SER_DDCD);
271178173Simp		SIGCHG(bes & UART_FR_DSR, sig, SER_DSR, SER_DDSR);
272178173Simp		new = sig & ~SER_MASK_DELTA;
273178173Simp	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
274178173Simp
275178173Simp	return (sig);
276178173Simp}
277178173Simp
278178173Simpstatic int
279178173Simpadm5120_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
280178173Simp{
281178173Simp	struct uart_bas *bas;
282178173Simp	int baudrate, divisor, error;
283178173Simp
284178173Simp	bas = &sc->sc_bas;
285178173Simp	error = 0;
286178173Simp	uart_lock(sc->sc_hwmtx);
287178173Simp	switch (request) {
288178173Simp	case UART_IOCTL_BREAK:
289178173Simp		/* TODO: Send BREAK */
290178173Simp		break;
291178173Simp	case UART_IOCTL_BAUD:
292178173Simp		divisor = uart_getreg(bas, UART_LCR_M_REG);
293178173Simp		divisor = (divisor << 8) |
294178173Simp		    uart_getreg(bas, UART_LCR_L_REG);
295178173Simp		baudrate = bas->rclk / 2 / (divisor + 2);
296178173Simp		*(int*)data = baudrate;
297178173Simp		break;
298178173Simp	default:
299178173Simp		error = EINVAL;
300178173Simp		break;
301178173Simp	}
302178173Simp	uart_unlock(sc->sc_hwmtx);
303178173Simp	return (error);
304178173Simp}
305178173Simp
306178173Simpstatic int
307178173Simpadm5120_uart_bus_ipend(struct uart_softc *sc)
308178173Simp{
309178173Simp	struct uart_bas *bas;
310178173Simp	int ipend;
311178173Simp	uint8_t ir, fr, rsr;
312178173Simp
313178173Simp	bas = &sc->sc_bas;
314178173Simp	ipend = 0;
315178173Simp
316178173Simp	uart_lock(sc->sc_hwmtx);
317178173Simp	ir = uart_getreg(&sc->sc_bas, UART_IR_REG);
318178173Simp	fr = uart_getreg(&sc->sc_bas, UART_FR_REG);
319178173Simp	rsr = uart_getreg(&sc->sc_bas, UART_RSR_REG);
320178173Simp
321178173Simp	if (ir & UART_IR_RX_INT)
322178173Simp		ipend |= SER_INT_RXREADY;
323178173Simp
324178173Simp	if (ir & UART_IR_RX_TIMEOUT_INT)
325178173Simp		ipend |= SER_INT_RXREADY;
326178173Simp
327178173Simp	if (ir & UART_IR_MODEM_STATUS_INT)
328178173Simp		ipend |= SER_INT_SIGCHG;
329178173Simp
330178173Simp	if (rsr & UART_RSR_BE)
331178173Simp		ipend |= SER_INT_BREAK;
332178173Simp
333178173Simp	if (rsr & UART_RSR_OE)
334178173Simp		ipend |= SER_INT_OVERRUN;
335178173Simp
336178173Simp	if (fr & UART_FR_TX_FIFO_EMPTY) {
337178173Simp		if (ir & UART_IR_TX_INT) {
338178173Simp			adm5120_uart_disable_txintr(sc);
339178173Simp			ipend |= SER_INT_TXIDLE;
340178173Simp		}
341178173Simp	}
342178173Simp
343178173Simp	if (ipend)
344178173Simp		uart_setreg(bas, UART_IR_REG, ir | UART_IR_UICR);
345178173Simp
346178173Simp	uart_unlock(sc->sc_hwmtx);
347178173Simp
348178173Simp	return (ipend);
349178173Simp}
350178173Simp
351178173Simpstatic int
352178173Simpadm5120_uart_bus_param(struct uart_softc *sc, int baudrate, int databits,
353178173Simp    int stopbits, int parity)
354178173Simp{
355178173Simp
356178173Simp	/* TODO: Set parameters for uart, meanwhile stick with 115200 8N1 */
357178173Simp	return (0);
358178173Simp}
359178173Simp
360178173Simpstatic int
361178173Simpadm5120_uart_bus_probe(struct uart_softc *sc)
362178173Simp{
363178173Simp	char buf[80];
364178173Simp	int error;
365178173Simp	char ch;
366178173Simp
367178173Simp	error = adm5120_uart_probe(&sc->sc_bas);
368178173Simp	if (error)
369178173Simp		return (error);
370178173Simp
371248965Sian	sc->sc_rxfifosz = 16;
372248965Sian	sc->sc_txfifosz = 16;
373248965Sian
374178173Simp	ch = sc->sc_bas.chan + 'A';
375178173Simp
376178173Simp	snprintf(buf, sizeof(buf), "adm5120_uart, channel %c", ch);
377178173Simp	device_set_desc_copy(sc->sc_dev, buf);
378178173Simp
379178173Simp	return (0);
380178173Simp}
381178173Simp
382178173Simpstatic int
383178173Simpadm5120_uart_bus_receive(struct uart_softc *sc)
384178173Simp{
385178173Simp	struct uart_bas *bas;
386178173Simp	int xc;
387178173Simp	uint8_t fr, rsr;
388178173Simp
389178173Simp	bas = &sc->sc_bas;
390178173Simp	uart_lock(sc->sc_hwmtx);
391178173Simp	fr = uart_getreg(bas, UART_FR_REG);
392178173Simp	while (!(fr & UART_FR_RX_FIFO_EMPTY)) {
393178173Simp		if (uart_rx_full(sc)) {
394178173Simp			sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
395178173Simp			break;
396178173Simp		}
397178173Simp		xc = 0;
398178173Simp		rsr = uart_getreg(bas, UART_RSR_REG);
399178173Simp		if (rsr & UART_RSR_FE)
400178173Simp			xc |= UART_STAT_FRAMERR;
401178173Simp		if (rsr & UART_RSR_PE)
402178173Simp			xc |= UART_STAT_PARERR;
403178173Simp		if (rsr & UART_RSR_OE)
404178173Simp			xc |= UART_STAT_OVERRUN;
405178173Simp		xc |= uart_getreg(bas, UART_DR_REG);
406178173Simp		uart_barrier(bas);
407178173Simp		uart_rx_put(sc, xc);
408178173Simp		if (rsr & (UART_RSR_FE | UART_RSR_PE | UART_RSR_OE)) {
409178173Simp			uart_setreg(bas, UART_ECR_REG, UART_ECR_RSR);
410178173Simp			uart_barrier(bas);
411178173Simp		}
412178173Simp		fr = uart_getreg(bas, UART_FR_REG);
413178173Simp	}
414178173Simp
415178173Simp	/* Discard everything left in the Rx FIFO. */
416178173Simp	while (!(fr & UART_FR_RX_FIFO_EMPTY)) {
417178173Simp		( void)uart_getreg(bas, UART_DR_REG);
418178173Simp		uart_barrier(bas);
419178173Simp		rsr = uart_getreg(bas, UART_RSR_REG);
420178173Simp		if (rsr & (UART_RSR_FE | UART_RSR_PE | UART_RSR_OE)) {
421178173Simp			uart_setreg(bas, UART_ECR_REG, UART_ECR_RSR);
422178173Simp			uart_barrier(bas);
423178173Simp		}
424178173Simp		fr = uart_getreg(bas, UART_FR_REG);
425178173Simp	}
426178173Simp	uart_unlock(sc->sc_hwmtx);
427178173Simp	return (0);
428178173Simp}
429178173Simp
430178173Simpstatic int
431178173Simpadm5120_uart_bus_setsig(struct uart_softc *sc, int sig)
432178173Simp{
433178173Simp
434178173Simp	/* TODO: implement (?) */
435178173Simp	return (0);
436178173Simp}
437178173Simp
438178173Simpstatic int
439178173Simpadm5120_uart_bus_transmit(struct uart_softc *sc)
440178173Simp{
441178173Simp	struct uart_bas *bas;
442178173Simp
443178173Simp	bas = &sc->sc_bas;
444178173Simp	uart_lock(sc->sc_hwmtx);
445178173Simp	sc->sc_txbusy = 1;
446178173Simp	for (int i = 0; i < sc->sc_txdatasz; i++) {
447178173Simp		if (uart_getreg(bas, UART_FR_REG) & UART_FR_TX_FIFO_FULL)
448178173Simp			break;
449178173Simp		uart_setreg(bas, UART_DR_REG, sc->sc_txbuf[i]);
450178173Simp	}
451178173Simp
452178173Simp	/* Enable TX interrupt */
453178173Simp	adm5120_uart_enable_txintr(sc);
454178173Simp	uart_unlock(sc->sc_hwmtx);
455178173Simp	return (0);
456178173Simp}
457262649Simp
458262649Simpstatic void
459262649Simpadm5120_uart_bus_grab(struct uart_softc *sc)
460262649Simp{
461262649Simp
462262649Simp	/* Enable interrupts - no RX_INT or RX_TIMEOUT */
463262649Simp	uart_lock(sc->sc_hwmtx);
464262649Simp	uart_setreg(&sc->sc_bas, UART_CR_REG,
465262649Simp	    UART_CR_PORT_EN | UART_CR_MODEM_STATUS_INT_EN);
466262649Simp	uart_unlock(sc->sc_hwmtx);
467262649Simp}
468262649Simp
469262649Simpstatic void
470262649Simpadm5120_uart_bus_ungrab(struct uart_softc *sc)
471262649Simp{
472262649Simp
473262649Simp	/* Enable interrupts */
474262649Simp	uart_lock(sc->sc_hwmtx);
475262649Simp	uart_setreg(&sc->sc_bas, UART_CR_REG,
476262649Simp	    UART_CR_PORT_EN|UART_CR_RX_INT_EN|UART_CR_RX_TIMEOUT_INT_EN|
477262649Simp	    UART_CR_MODEM_STATUS_INT_EN);
478262649Simp	uart_unlock(sc->sc_hwmtx);
479262649Simp}
480