1249997Swkoszek/*-
2249997Swkoszek * Copyright (c) 2005 M. Warner Losh
3249997Swkoszek * Copyright (c) 2005 Olivier Houchard
4249997Swkoszek * Copyright (c) 2012 Thomas Skibo
5249997Swkoszek * All rights reserved.
6249997Swkoszek *
7249997Swkoszek * Redistribution and use in source and binary forms, with or without
8249997Swkoszek * modification, are permitted provided that the following conditions
9249997Swkoszek * are met:
10249997Swkoszek *
11249997Swkoszek * 1. Redistributions of source code must retain the above copyright
12249997Swkoszek *    notice, this list of conditions and the following disclaimer.
13249997Swkoszek * 2. Redistributions in binary form must reproduce the above copyright
14249997Swkoszek *    notice, this list of conditions and the following disclaimer in the
15249997Swkoszek *    documentation and/or other materials provided with the distribution.
16249997Swkoszek *
17249997Swkoszek * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18249997Swkoszek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19249997Swkoszek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20249997Swkoszek * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21249997Swkoszek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22249997Swkoszek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23249997Swkoszek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24249997Swkoszek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25249997Swkoszek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26249997Swkoszek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27249997Swkoszek * SUCH DAMAGE.
28249997Swkoszek */
29249997Swkoszek
30249997Swkoszek/* A driver for the Cadence AMBA UART as used by the Xilinx Zynq-7000.
31249997Swkoszek *
32249997Swkoszek * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
33249997Swkoszek * (v1.4) November 16, 2012.  Xilinx doc UG585.  UART is covered in Ch. 19
34249997Swkoszek * and register definitions are in appendix B.33.
35249997Swkoszek */
36249997Swkoszek
37249997Swkoszek
38249997Swkoszek#include <sys/cdefs.h>
39249997Swkoszek__FBSDID("$FreeBSD$");
40249997Swkoszek
41249997Swkoszek#include "opt_global.h"
42249997Swkoszek
43249997Swkoszek#include <sys/param.h>
44249997Swkoszek#include <sys/systm.h>
45249997Swkoszek#include <sys/bus.h>
46249997Swkoszek#include <sys/conf.h>
47249997Swkoszek#include <sys/cons.h>
48249997Swkoszek#include <sys/tty.h>
49249997Swkoszek#include <machine/bus.h>
50249997Swkoszek
51249997Swkoszek#include <dev/uart/uart.h>
52249997Swkoszek#include <dev/uart/uart_cpu.h>
53283327Sian#include <dev/uart/uart_cpu_fdt.h>
54249997Swkoszek#include <dev/uart/uart_bus.h>
55249997Swkoszek
56249997Swkoszek#include "uart_if.h"
57249997Swkoszek
58249997Swkoszek#define	UART_FIFO_SIZE	64
59249997Swkoszek
60249997Swkoszek#define	RD4(bas, reg)		\
61249997Swkoszek	bus_space_read_4((bas)->bst, (bas)->bsh, uart_regofs((bas), (reg)))
62249997Swkoszek#define	WR4(bas, reg, value)	\
63249997Swkoszek	bus_space_write_4((bas)->bst, (bas)->bsh, uart_regofs((bas), (reg)), \
64249997Swkoszek			  (value))
65249997Swkoszek
66249997Swkoszek/* Register definitions for Cadence UART Controller.
67249997Swkoszek */
68249997Swkoszek#define CDNC_UART_CTRL_REG	0x00		/* Control Register. */
69249997Swkoszek#define CDNC_UART_CTRL_REG_STOPBRK	(1<<8)
70249997Swkoszek#define CDNC_UART_CTRL_REG_STARTBRK	(1<<7)
71249997Swkoszek#define CDNC_UART_CTRL_REG_TORST	(1<<6)
72249997Swkoszek#define CDNC_UART_CTRL_REG_TX_DIS	(1<<5)
73249997Swkoszek#define CDNC_UART_CTRL_REG_TX_EN	(1<<4)
74249997Swkoszek#define CDNC_UART_CTRL_REG_RX_DIS	(1<<3)
75249997Swkoszek#define CDNC_UART_CTRL_REG_RX_EN	(1<<2)
76249997Swkoszek#define CDNC_UART_CTRL_REG_TXRST	(1<<1)
77249997Swkoszek#define CDNC_UART_CTRL_REG_RXRST	(1<<0)
78249997Swkoszek
79249997Swkoszek#define CDNC_UART_MODE_REG	0x04		/* Mode Register. */
80249997Swkoszek#define CDNC_UART_MODE_REG_CHMOD_R_LOOP	(3<<8)	/* [9:8] - channel mode */
81249997Swkoszek#define CDNC_UART_MODE_REG_CHMOD_L_LOOP	(2<<8)
82249997Swkoszek#define CDNC_UART_MODE_REG_CHMOD_AUTECHO (1<<8)
83249997Swkoszek#define CDNC_UART_MODE_REG_STOP2	(2<<6)	/* [7:6] - stop bits */
84249997Swkoszek#define CDNC_UART_MODE_REG_PAR_NONE	(4<<3)	/* [5:3] - parity type */
85249997Swkoszek#define CDNC_UART_MODE_REG_PAR_MARK	(3<<3)
86249997Swkoszek#define CDNC_UART_MODE_REG_PAR_SPACE	(2<<3)
87249997Swkoszek#define CDNC_UART_MODE_REG_PAR_ODD	(1<<3)
88249997Swkoszek#define CDNC_UART_MODE_REG_PAR_EVEN	(0<<3)
89249997Swkoszek#define CDNC_UART_MODE_REG_6BIT		(3<<1)	/* [2:1] - character len */
90249997Swkoszek#define CDNC_UART_MODE_REG_7BIT		(2<<1)
91249997Swkoszek#define CDNC_UART_MODE_REG_8BIT		(0<<1)
92249997Swkoszek#define CDNC_UART_MODE_REG_CLKSEL	(1<<0)
93249997Swkoszek
94249997Swkoszek#define CDNC_UART_IEN_REG	0x08		/* Interrupt registers. */
95249997Swkoszek#define CDNC_UART_IDIS_REG	0x0C
96249997Swkoszek#define CDNC_UART_IMASK_REG	0x10
97249997Swkoszek#define CDNC_UART_ISTAT_REG	0x14
98249997Swkoszek#define CDNC_UART_INT_TXOVR		(1<<12)
99249997Swkoszek#define CDNC_UART_INT_TXNRLYFUL		(1<<11)	/* tx "nearly" full */
100249997Swkoszek#define CDNC_UART_INT_TXTRIG		(1<<10)
101249997Swkoszek#define CDNC_UART_INT_DMSI		(1<<9)	/* delta modem status */
102249997Swkoszek#define CDNC_UART_INT_RXTMOUT		(1<<8)
103249997Swkoszek#define CDNC_UART_INT_PARITY		(1<<7)
104249997Swkoszek#define CDNC_UART_INT_FRAMING		(1<<6)
105249997Swkoszek#define CDNC_UART_INT_RXOVR		(1<<5)
106249997Swkoszek#define CDNC_UART_INT_TXFULL		(1<<4)
107249997Swkoszek#define CDNC_UART_INT_TXEMPTY		(1<<3)
108249997Swkoszek#define CDNC_UART_INT_RXFULL		(1<<2)
109249997Swkoszek#define CDNC_UART_INT_RXEMPTY		(1<<1)
110249997Swkoszek#define CDNC_UART_INT_RXTRIG		(1<<0)
111249997Swkoszek#define CDNC_UART_INT_ALL		0x1FFF
112249997Swkoszek
113249997Swkoszek#define CDNC_UART_BAUDGEN_REG	0x18
114249997Swkoszek#define CDNC_UART_RX_TIMEO_REG	0x1C
115249997Swkoszek#define CDNC_UART_RX_WATER_REG	0x20
116249997Swkoszek
117249997Swkoszek#define CDNC_UART_MODEM_CTRL_REG 0x24
118249997Swkoszek#define CDNC_UART_MODEM_CTRL_REG_FCM	(1<<5)	/* automatic flow control */
119249997Swkoszek#define CDNC_UART_MODEM_CTRL_REG_RTS	(1<<1)
120249997Swkoszek#define CDNC_UART_MODEM_CTRL_REG_DTR	(1<<0)
121249997Swkoszek
122249997Swkoszek#define CDNC_UART_MODEM_STAT_REG 0x28
123249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_FCMS	(1<<8)	/* flow control mode (rw) */
124249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_DCD	(1<<7)
125249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_RI	(1<<6)
126249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_DSR	(1<<5)
127249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_CTS	(1<<4)
128249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_DDCD	(1<<3)	/* change in DCD (w1tc) */
129249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_TERI	(1<<2)	/* trail edge ring (w1tc) */
130249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_DDSR	(1<<1)	/* change in DSR (w1tc) */
131249997Swkoszek#define CDNC_UART_MODEM_STAT_REG_DCTS	(1<<0)	/* change in CTS (w1tc) */
132249997Swkoszek
133249997Swkoszek#define CDNC_UART_CHAN_STAT_REG	0x2C		/* Channel status register. */
134249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_TXNRLYFUL (1<<14) /* tx "nearly" full */
135249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_TXTRIG	(1<<13)
136249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_FDELT	(1<<12)
137249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_TXACTIVE (1<<11)
138249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_RXACTIVE (1<<10)
139249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_TXFULL	(1<<4)
140249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_TXEMPTY	(1<<3)
141249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_RXEMPTY	(1<<1)
142249997Swkoszek#define CDNC_UART_CHAN_STAT_REG_RXTRIG	(1<<0)
143249997Swkoszek
144249997Swkoszek#define CDNC_UART_FIFO		0x30		/* Data FIFO (tx and rx) */
145249997Swkoszek#define CDNC_UART_BAUDDIV_REG	0x34
146249997Swkoszek#define CDNC_UART_FLOWDEL_REG	0x38
147249997Swkoszek#define CDNC_UART_TX_WATER_REG	0x44
148249997Swkoszek
149249997Swkoszek
150249997Swkoszek/*
151249997Swkoszek * Low-level UART interface.
152249997Swkoszek */
153249997Swkoszekstatic int cdnc_uart_probe(struct uart_bas *bas);
154249997Swkoszekstatic void cdnc_uart_init(struct uart_bas *bas, int, int, int, int);
155249997Swkoszekstatic void cdnc_uart_term(struct uart_bas *bas);
156249997Swkoszekstatic void cdnc_uart_putc(struct uart_bas *bas, int);
157249997Swkoszekstatic int cdnc_uart_rxready(struct uart_bas *bas);
158249997Swkoszekstatic int cdnc_uart_getc(struct uart_bas *bas, struct mtx *mtx);
159249997Swkoszek
160249997Swkoszekextern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
161249997Swkoszek
162249997Swkoszekstatic struct uart_ops cdnc_uart_ops = {
163249997Swkoszek	.probe = cdnc_uart_probe,
164249997Swkoszek	.init = cdnc_uart_init,
165249997Swkoszek	.term = cdnc_uart_term,
166249997Swkoszek	.putc = cdnc_uart_putc,
167249997Swkoszek	.rxready = cdnc_uart_rxready,
168249997Swkoszek	.getc = cdnc_uart_getc,
169249997Swkoszek};
170249997Swkoszek
171249997Swkoszek#define	SIGCHG(c, i, s, d)				\
172249997Swkoszek	if (c) {					\
173249997Swkoszek		i |= (i & s) ? s : s | d;		\
174249997Swkoszek	} else {					\
175249997Swkoszek		i = (i & s) ? (i & ~s) | d : i;		\
176249997Swkoszek	}
177249997Swkoszek
178249997Swkoszekstatic int
179249997Swkoszekcdnc_uart_probe(struct uart_bas *bas)
180249997Swkoszek{
181249997Swkoszek
182249997Swkoszek	return (0);
183249997Swkoszek}
184249997Swkoszek
185249997Swkoszekstatic int
186249997Swkoszekcdnc_uart_set_baud(struct uart_bas *bas, int baudrate)
187249997Swkoszek{
188249997Swkoszek	uint32_t baudgen, bauddiv;
189249997Swkoszek	uint32_t best_bauddiv, best_baudgen, best_error;
190249997Swkoszek	uint32_t baud_out, err;
191249997Swkoszek
192249997Swkoszek	best_bauddiv = 0;
193249997Swkoszek	best_baudgen = 0;
194249997Swkoszek	best_error = ~0;
195249997Swkoszek
196249997Swkoszek	/* Try all possible bauddiv values and pick best match. */
197249997Swkoszek	for (bauddiv = 4; bauddiv <= 255; bauddiv++) {
198249997Swkoszek		baudgen = (bas->rclk + (baudrate * (bauddiv + 1)) / 2) /
199249997Swkoszek			(baudrate * (bauddiv + 1));
200249997Swkoszek		if (baudgen < 1 || baudgen > 0xffff)
201249997Swkoszek			continue;
202249997Swkoszek
203249997Swkoszek		baud_out = bas->rclk / (baudgen * (bauddiv + 1));
204249997Swkoszek		err = baud_out > baudrate ?
205249997Swkoszek			baud_out - baudrate : baudrate - baud_out;
206249997Swkoszek
207249997Swkoszek		if (err < best_error) {
208249997Swkoszek			best_error = err;
209249997Swkoszek			best_bauddiv = bauddiv;
210249997Swkoszek			best_baudgen = baudgen;
211249997Swkoszek		}
212249997Swkoszek	}
213249997Swkoszek
214249997Swkoszek	if (best_bauddiv > 0) {
215249997Swkoszek		WR4(bas, CDNC_UART_BAUDDIV_REG, best_bauddiv);
216249997Swkoszek		WR4(bas, CDNC_UART_BAUDGEN_REG, best_baudgen);
217249997Swkoszek		return (0);
218249997Swkoszek	} else
219249997Swkoszek		return (-1); /* out of range */
220249997Swkoszek}
221249997Swkoszek
222249997Swkoszekstatic int
223249997Swkoszekcdnc_uart_set_params(struct uart_bas *bas, int baudrate, int databits,
224249997Swkoszek		      int stopbits, int parity)
225249997Swkoszek{
226249997Swkoszek	uint32_t mode_reg_value = 0;
227249997Swkoszek
228249997Swkoszek	switch (databits) {
229249997Swkoszek	case 6:
230249997Swkoszek		mode_reg_value |= CDNC_UART_MODE_REG_6BIT;
231249997Swkoszek		break;
232249997Swkoszek	case 7:
233249997Swkoszek		mode_reg_value |= CDNC_UART_MODE_REG_7BIT;
234249997Swkoszek		break;
235249997Swkoszek	case 8:
236249997Swkoszek	default:
237249997Swkoszek		mode_reg_value |= CDNC_UART_MODE_REG_8BIT;
238249997Swkoszek		break;
239249997Swkoszek	}
240249997Swkoszek
241249997Swkoszek	if (stopbits == 2)
242249997Swkoszek		mode_reg_value |= CDNC_UART_MODE_REG_STOP2;
243249997Swkoszek
244249997Swkoszek	switch (parity) {
245249997Swkoszek	case UART_PARITY_MARK:
246249997Swkoszek		mode_reg_value |= CDNC_UART_MODE_REG_PAR_MARK;
247249997Swkoszek		break;
248249997Swkoszek	case UART_PARITY_SPACE:
249249997Swkoszek		mode_reg_value |= CDNC_UART_MODE_REG_PAR_SPACE;
250249997Swkoszek		break;
251249997Swkoszek	case UART_PARITY_ODD:
252249997Swkoszek		mode_reg_value |= CDNC_UART_MODE_REG_PAR_ODD;
253249997Swkoszek		break;
254249997Swkoszek	case UART_PARITY_EVEN:
255249997Swkoszek		mode_reg_value |= CDNC_UART_MODE_REG_PAR_EVEN;
256249997Swkoszek		break;
257249997Swkoszek	case UART_PARITY_NONE:
258249997Swkoszek	default:
259249997Swkoszek		mode_reg_value |= CDNC_UART_MODE_REG_PAR_NONE;
260249997Swkoszek		break;
261249997Swkoszek	}
262249997Swkoszek
263249997Swkoszek	WR4(bas, CDNC_UART_MODE_REG, mode_reg_value);
264249997Swkoszek
265249997Swkoszek	if (baudrate > 0 && cdnc_uart_set_baud(bas, baudrate) < 0)
266249997Swkoszek		return (EINVAL);
267249997Swkoszek
268249997Swkoszek	return(0);
269249997Swkoszek}
270249997Swkoszek
271249997Swkoszekstatic void
272249997Swkoszekcdnc_uart_hw_init(struct uart_bas *bas)
273249997Swkoszek{
274249997Swkoszek
275249997Swkoszek	/* Reset RX and TX. */
276249997Swkoszek	WR4(bas, CDNC_UART_CTRL_REG,
277249997Swkoszek	    CDNC_UART_CTRL_REG_RXRST | CDNC_UART_CTRL_REG_TXRST);
278249997Swkoszek
279249997Swkoszek	/* Interrupts all off. */
280249997Swkoszek	WR4(bas, CDNC_UART_IDIS_REG, CDNC_UART_INT_ALL);
281249997Swkoszek	WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_ALL);
282249997Swkoszek
283249997Swkoszek	/* Clear delta bits. */
284249997Swkoszek	WR4(bas, CDNC_UART_MODEM_STAT_REG,
285249997Swkoszek	    CDNC_UART_MODEM_STAT_REG_DDCD | CDNC_UART_MODEM_STAT_REG_TERI |
286249997Swkoszek	    CDNC_UART_MODEM_STAT_REG_DDSR | CDNC_UART_MODEM_STAT_REG_DCTS);
287249997Swkoszek
288249997Swkoszek	/* RX FIFO water level, stale timeout */
289249997Swkoszek	WR4(bas, CDNC_UART_RX_WATER_REG, UART_FIFO_SIZE/2);
290249997Swkoszek	WR4(bas, CDNC_UART_RX_TIMEO_REG, 10);
291249997Swkoszek
292249997Swkoszek	/* TX FIFO water level (not used.) */
293249997Swkoszek	WR4(bas, CDNC_UART_TX_WATER_REG, UART_FIFO_SIZE/2);
294249997Swkoszek
295249997Swkoszek	/* Bring RX and TX online. */
296249997Swkoszek	WR4(bas, CDNC_UART_CTRL_REG,
297249997Swkoszek	    CDNC_UART_CTRL_REG_RX_EN | CDNC_UART_CTRL_REG_TX_EN |
298249997Swkoszek	    CDNC_UART_CTRL_REG_TORST | CDNC_UART_CTRL_REG_STOPBRK);
299249997Swkoszek
300249997Swkoszek	/* Set DTR and RTS. */
301249997Swkoszek	WR4(bas, CDNC_UART_MODEM_CTRL_REG, CDNC_UART_MODEM_CTRL_REG_DTR |
302249997Swkoszek	    CDNC_UART_MODEM_CTRL_REG_RTS);
303249997Swkoszek}
304249997Swkoszek
305249997Swkoszek/*
306249997Swkoszek * Initialize this device for use as a console.
307249997Swkoszek */
308249997Swkoszekstatic void
309249997Swkoszekcdnc_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
310249997Swkoszek	      int parity)
311249997Swkoszek{
312249997Swkoszek
313249997Swkoszek	/* Initialize hardware. */
314249997Swkoszek	cdnc_uart_hw_init(bas);
315249997Swkoszek
316249997Swkoszek	/* Set baudrate, parameters. */
317249997Swkoszek	(void)cdnc_uart_set_params(bas, baudrate, databits, stopbits, parity);
318249997Swkoszek}
319249997Swkoszek
320249997Swkoszek/*
321249997Swkoszek * Free resources now that we're no longer the console.  This appears to
322249997Swkoszek * be never called, and I'm unsure quite what to do if I am called.
323249997Swkoszek */
324249997Swkoszekstatic void
325249997Swkoszekcdnc_uart_term(struct uart_bas *bas)
326249997Swkoszek{
327249997Swkoszek
328249997Swkoszek	/* XXX */
329249997Swkoszek}
330249997Swkoszek
331249997Swkoszek/*
332249997Swkoszek * Put a character of console output (so we do it here polling rather than
333249997Swkoszek * interrutp driven).
334249997Swkoszek */
335249997Swkoszekstatic void
336249997Swkoszekcdnc_uart_putc(struct uart_bas *bas, int c)
337249997Swkoszek{
338249997Swkoszek
339249997Swkoszek	/* Wait for room. */
340249997Swkoszek	while ((RD4(bas,CDNC_UART_CHAN_STAT_REG) &
341249997Swkoszek		CDNC_UART_CHAN_STAT_REG_TXFULL) != 0)
342249997Swkoszek		;
343249997Swkoszek
344249997Swkoszek	WR4(bas, CDNC_UART_FIFO, c);
345249997Swkoszek
346249997Swkoszek	while ((RD4(bas,CDNC_UART_CHAN_STAT_REG) &
347249997Swkoszek		CDNC_UART_CHAN_STAT_REG_TXEMPTY) == 0)
348249997Swkoszek		;
349249997Swkoszek}
350249997Swkoszek
351249997Swkoszek/*
352249997Swkoszek * Check for a character available.
353249997Swkoszek */
354249997Swkoszekstatic int
355249997Swkoszekcdnc_uart_rxready(struct uart_bas *bas)
356249997Swkoszek{
357249997Swkoszek
358249997Swkoszek	return ((RD4(bas, CDNC_UART_CHAN_STAT_REG) &
359249997Swkoszek		 CDNC_UART_CHAN_STAT_REG_RXEMPTY) == 0);
360249997Swkoszek}
361249997Swkoszek
362249997Swkoszek/*
363249997Swkoszek * Block waiting for a character.
364249997Swkoszek */
365249997Swkoszekstatic int
366249997Swkoszekcdnc_uart_getc(struct uart_bas *bas, struct mtx *mtx)
367249997Swkoszek{
368249997Swkoszek	int c;
369249997Swkoszek
370249997Swkoszek	uart_lock(mtx);
371249997Swkoszek
372249997Swkoszek	while ((RD4(bas, CDNC_UART_CHAN_STAT_REG) &
373249997Swkoszek		CDNC_UART_CHAN_STAT_REG_RXEMPTY) != 0) {
374249997Swkoszek		uart_unlock(mtx);
375249997Swkoszek		DELAY(4);
376249997Swkoszek		uart_lock(mtx);
377249997Swkoszek	}
378249997Swkoszek
379249997Swkoszek	c = RD4(bas, CDNC_UART_FIFO);
380249997Swkoszek
381249997Swkoszek	uart_unlock(mtx);
382249997Swkoszek
383249997Swkoszek	c &= 0xff;
384249997Swkoszek	return (c);
385249997Swkoszek}
386249997Swkoszek
387249997Swkoszek/*****************************************************************************/
388249997Swkoszek/*
389249997Swkoszek * High-level UART interface.
390249997Swkoszek */
391249997Swkoszek
392249997Swkoszekstatic int cdnc_uart_bus_probe(struct uart_softc *sc);
393249997Swkoszekstatic int cdnc_uart_bus_attach(struct uart_softc *sc);
394249997Swkoszekstatic int cdnc_uart_bus_flush(struct uart_softc *, int);
395249997Swkoszekstatic int cdnc_uart_bus_getsig(struct uart_softc *);
396249997Swkoszekstatic int cdnc_uart_bus_ioctl(struct uart_softc *, int, intptr_t);
397249997Swkoszekstatic int cdnc_uart_bus_ipend(struct uart_softc *);
398249997Swkoszekstatic int cdnc_uart_bus_param(struct uart_softc *, int, int, int, int);
399249997Swkoszekstatic int cdnc_uart_bus_receive(struct uart_softc *);
400249997Swkoszekstatic int cdnc_uart_bus_setsig(struct uart_softc *, int);
401249997Swkoszekstatic int cdnc_uart_bus_transmit(struct uart_softc *);
402262649Simpstatic void cdnc_uart_bus_grab(struct uart_softc *);
403262649Simpstatic void cdnc_uart_bus_ungrab(struct uart_softc *);
404249997Swkoszek
405249997Swkoszekstatic kobj_method_t cdnc_uart_bus_methods[] = {
406249997Swkoszek	KOBJMETHOD(uart_probe,		cdnc_uart_bus_probe),
407249997Swkoszek	KOBJMETHOD(uart_attach, 	cdnc_uart_bus_attach),
408249997Swkoszek	KOBJMETHOD(uart_flush,		cdnc_uart_bus_flush),
409249997Swkoszek	KOBJMETHOD(uart_getsig,		cdnc_uart_bus_getsig),
410249997Swkoszek	KOBJMETHOD(uart_ioctl,		cdnc_uart_bus_ioctl),
411249997Swkoszek	KOBJMETHOD(uart_ipend,		cdnc_uart_bus_ipend),
412249997Swkoszek	KOBJMETHOD(uart_param,		cdnc_uart_bus_param),
413249997Swkoszek	KOBJMETHOD(uart_receive,	cdnc_uart_bus_receive),
414249997Swkoszek	KOBJMETHOD(uart_setsig,		cdnc_uart_bus_setsig),
415249997Swkoszek	KOBJMETHOD(uart_transmit,	cdnc_uart_bus_transmit),
416262649Simp	KOBJMETHOD(uart_grab,		cdnc_uart_bus_grab),
417262649Simp	KOBJMETHOD(uart_ungrab,		cdnc_uart_bus_ungrab),
418249997Swkoszek
419249997Swkoszek	KOBJMETHOD_END
420249997Swkoszek};
421249997Swkoszek
422249997Swkoszekint
423249997Swkoszekcdnc_uart_bus_probe(struct uart_softc *sc)
424249997Swkoszek{
425249997Swkoszek
426249997Swkoszek	sc->sc_txfifosz = UART_FIFO_SIZE;
427249997Swkoszek	sc->sc_rxfifosz = UART_FIFO_SIZE;
428249997Swkoszek	sc->sc_hwiflow = 0;
429249997Swkoszek	sc->sc_hwoflow = 0;
430249997Swkoszek
431249997Swkoszek	device_set_desc(sc->sc_dev, "Cadence UART");
432249997Swkoszek
433249997Swkoszek	return (0);
434249997Swkoszek}
435249997Swkoszek
436249997Swkoszekstatic int
437249997Swkoszekcdnc_uart_bus_attach(struct uart_softc *sc)
438249997Swkoszek{
439249997Swkoszek	struct uart_bas *bas = &sc->sc_bas;
440249997Swkoszek	struct uart_devinfo *di;
441249997Swkoszek
442249997Swkoszek	if (sc->sc_sysdev != NULL) {
443249997Swkoszek		di = sc->sc_sysdev;
444249997Swkoszek		(void)cdnc_uart_set_params(bas, di->baudrate, di->databits,
445249997Swkoszek					   di->stopbits, di->parity);
446249997Swkoszek	} else
447249997Swkoszek		cdnc_uart_hw_init(bas);
448249997Swkoszek
449249997Swkoszek	(void)cdnc_uart_bus_getsig(sc);
450249997Swkoszek
451249997Swkoszek	/* Enable interrupts. */
452249997Swkoszek	WR4(bas, CDNC_UART_IEN_REG,
453249997Swkoszek	    CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT |
454249997Swkoszek	    CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR |
455249997Swkoszek	    CDNC_UART_INT_DMSI);
456249997Swkoszek
457249997Swkoszek	return (0);
458249997Swkoszek}
459249997Swkoszek
460249997Swkoszekstatic int
461249997Swkoszekcdnc_uart_bus_transmit(struct uart_softc *sc)
462249997Swkoszek{
463249997Swkoszek	int i;
464249997Swkoszek	struct uart_bas *bas = &sc->sc_bas;
465249997Swkoszek
466249997Swkoszek	uart_lock(sc->sc_hwmtx);
467249997Swkoszek
468249997Swkoszek	/* Clear sticky TXEMPTY status bit. */
469249997Swkoszek	WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_TXEMPTY);
470249997Swkoszek
471249997Swkoszek	for (i = 0; i < sc->sc_txdatasz; i++)
472249997Swkoszek		WR4(bas, CDNC_UART_FIFO, sc->sc_txbuf[i]);
473249997Swkoszek
474249997Swkoszek	/* Enable TX empty interrupt. */
475249997Swkoszek	WR4(bas, CDNC_UART_IEN_REG, CDNC_UART_INT_TXEMPTY);
476249997Swkoszek	sc->sc_txbusy = 1;
477249997Swkoszek
478249997Swkoszek	uart_unlock(sc->sc_hwmtx);
479249997Swkoszek
480249997Swkoszek	return (0);
481249997Swkoszek}
482249997Swkoszek
483249997Swkoszekstatic int
484249997Swkoszekcdnc_uart_bus_setsig(struct uart_softc *sc, int sig)
485249997Swkoszek{
486249997Swkoszek	struct uart_bas *bas = &sc->sc_bas;
487249997Swkoszek	uint32_t new, old, modem_ctrl;
488249997Swkoszek
489249997Swkoszek	do {
490249997Swkoszek		old = sc->sc_hwsig;
491249997Swkoszek		new = old;
492249997Swkoszek		if (sig & SER_DDTR) {
493249997Swkoszek			SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
494249997Swkoszek		}
495249997Swkoszek		if (sig & SER_DRTS) {
496249997Swkoszek			SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
497249997Swkoszek		}
498249997Swkoszek	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
499249997Swkoszek	uart_lock(sc->sc_hwmtx);
500249997Swkoszek	modem_ctrl = RD4(bas, CDNC_UART_MODEM_CTRL_REG) &
501249997Swkoszek		~(CDNC_UART_MODEM_CTRL_REG_DTR | CDNC_UART_MODEM_CTRL_REG_RTS);
502249997Swkoszek	if ((new & SER_DTR) != 0)
503249997Swkoszek		modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_DTR;
504249997Swkoszek	if ((new & SER_RTS) != 0)
505249997Swkoszek		modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_RTS;
506249997Swkoszek	WR4(bas, CDNC_UART_MODEM_CTRL_REG, modem_ctrl);
507249997Swkoszek
508249997Swkoszek	uart_unlock(sc->sc_hwmtx);
509249997Swkoszek	return (0);
510249997Swkoszek}
511249997Swkoszek
512249997Swkoszekstatic int
513249997Swkoszekcdnc_uart_bus_receive(struct uart_softc *sc)
514249997Swkoszek{
515249997Swkoszek	struct uart_bas *bas = &sc->sc_bas;
516249997Swkoszek	uint32_t status;
517249997Swkoszek	int c, c_status = 0;
518249997Swkoszek
519249997Swkoszek	uart_lock(sc->sc_hwmtx);
520249997Swkoszek
521249997Swkoszek	/* Check for parity or framing errors and clear the status bits. */
522249997Swkoszek	status = RD4(bas, CDNC_UART_ISTAT_REG);
523249997Swkoszek	if ((status & (CDNC_UART_INT_FRAMING | CDNC_UART_INT_PARITY)) != 0) {
524249997Swkoszek		WR4(bas, CDNC_UART_ISTAT_REG,
525249997Swkoszek		    status & (CDNC_UART_INT_FRAMING | CDNC_UART_INT_PARITY));
526249997Swkoszek		if ((status & CDNC_UART_INT_PARITY) != 0)
527249997Swkoszek			c_status |= UART_STAT_PARERR;
528249997Swkoszek		if ((status & CDNC_UART_INT_FRAMING) != 0)
529249997Swkoszek			c_status |= UART_STAT_FRAMERR;
530249997Swkoszek	}
531249997Swkoszek
532249997Swkoszek	while ((RD4(bas, CDNC_UART_CHAN_STAT_REG) &
533249997Swkoszek		CDNC_UART_CHAN_STAT_REG_RXEMPTY) == 0) {
534249997Swkoszek		c = RD4(bas, CDNC_UART_FIFO) & 0xff;
535249997Swkoszek#ifdef KDB
536249997Swkoszek		/* Detect break and drop into debugger. */
537249997Swkoszek		if (c == 0 && (c_status & UART_STAT_FRAMERR) != 0 &&
538249997Swkoszek		    sc->sc_sysdev != NULL &&
539249997Swkoszek		    sc->sc_sysdev->type == UART_DEV_CONSOLE) {
540249997Swkoszek			kdb_break();
541249997Swkoszek			WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_FRAMING);
542249997Swkoszek		}
543249997Swkoszek#endif
544249997Swkoszek		uart_rx_put(sc, c | c_status);
545249997Swkoszek	}
546249997Swkoszek
547249997Swkoszek	uart_unlock(sc->sc_hwmtx);
548249997Swkoszek
549249997Swkoszek	return (0);
550249997Swkoszek}
551249997Swkoszek
552249997Swkoszekstatic int
553249997Swkoszekcdnc_uart_bus_param(struct uart_softc *sc, int baudrate, int databits,
554249997Swkoszek		   int stopbits, int parity)
555249997Swkoszek{
556249997Swkoszek
557249997Swkoszek	return (cdnc_uart_set_params(&sc->sc_bas, baudrate,
558249997Swkoszek				    databits, stopbits, parity));
559249997Swkoszek}
560249997Swkoszek
561249997Swkoszekstatic int
562249997Swkoszekcdnc_uart_bus_ipend(struct uart_softc *sc)
563249997Swkoszek{
564249997Swkoszek	int ipend = 0;
565249997Swkoszek	struct uart_bas *bas = &sc->sc_bas;
566249997Swkoszek	uint32_t istatus;
567249997Swkoszek
568249997Swkoszek	uart_lock(sc->sc_hwmtx);
569249997Swkoszek
570249997Swkoszek	istatus = RD4(bas, CDNC_UART_ISTAT_REG);
571249997Swkoszek
572249997Swkoszek	/* Clear interrupt bits. */
573249997Swkoszek	WR4(bas, CDNC_UART_ISTAT_REG, istatus &
574249997Swkoszek	    (CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT |
575249997Swkoszek	     CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR |
576249997Swkoszek	     CDNC_UART_INT_TXEMPTY | CDNC_UART_INT_DMSI));
577249997Swkoszek
578249997Swkoszek	/* Receive data. */
579249997Swkoszek	if ((istatus & (CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT)) != 0)
580249997Swkoszek		ipend |= SER_INT_RXREADY;
581249997Swkoszek
582249997Swkoszek	/* Transmit fifo empty. */
583249997Swkoszek	if (sc->sc_txbusy && (istatus & CDNC_UART_INT_TXEMPTY) != 0) {
584249997Swkoszek		/* disable txempty interrupt. */
585249997Swkoszek		WR4(bas, CDNC_UART_IDIS_REG, CDNC_UART_INT_TXEMPTY);
586249997Swkoszek		ipend |= SER_INT_TXIDLE;
587249997Swkoszek	}
588249997Swkoszek
589249997Swkoszek	/* TX Overflow. */
590249997Swkoszek	if ((istatus & CDNC_UART_INT_TXOVR) != 0)
591249997Swkoszek		ipend |= SER_INT_OVERRUN;
592249997Swkoszek
593249997Swkoszek	/* RX Overflow. */
594249997Swkoszek	if ((istatus & CDNC_UART_INT_RXOVR) != 0)
595249997Swkoszek		ipend |= SER_INT_OVERRUN;
596249997Swkoszek
597249997Swkoszek	/* Modem signal change. */
598249997Swkoszek	if ((istatus & CDNC_UART_INT_DMSI) != 0) {
599249997Swkoszek		WR4(bas, CDNC_UART_MODEM_STAT_REG,
600249997Swkoszek		    CDNC_UART_MODEM_STAT_REG_DDCD |
601249997Swkoszek		    CDNC_UART_MODEM_STAT_REG_TERI |
602249997Swkoszek		    CDNC_UART_MODEM_STAT_REG_DDSR |
603249997Swkoszek		    CDNC_UART_MODEM_STAT_REG_DCTS);
604249997Swkoszek		ipend |= SER_INT_SIGCHG;
605249997Swkoszek	}
606249997Swkoszek
607249997Swkoszek	uart_unlock(sc->sc_hwmtx);
608249997Swkoszek	return (ipend);
609249997Swkoszek}
610249997Swkoszek
611249997Swkoszekstatic int
612249997Swkoszekcdnc_uart_bus_flush(struct uart_softc *sc, int what)
613249997Swkoszek{
614249997Swkoszek
615249997Swkoszek	return (0);
616249997Swkoszek}
617249997Swkoszek
618249997Swkoszekstatic int
619249997Swkoszekcdnc_uart_bus_getsig(struct uart_softc *sc)
620249997Swkoszek{
621249997Swkoszek	struct uart_bas *bas = &sc->sc_bas;
622249997Swkoszek	uint32_t new, old, sig;
623249997Swkoszek	uint8_t modem_status;
624249997Swkoszek
625249997Swkoszek	do {
626249997Swkoszek		old = sc->sc_hwsig;
627249997Swkoszek		sig = old;
628249997Swkoszek		uart_lock(sc->sc_hwmtx);
629249997Swkoszek		modem_status = RD4(bas, CDNC_UART_MODEM_STAT_REG);
630249997Swkoszek		uart_unlock(sc->sc_hwmtx);
631249997Swkoszek		SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_DSR,
632249997Swkoszek		       sig, SER_DSR, SER_DDSR);
633249997Swkoszek		SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_CTS,
634249997Swkoszek		       sig, SER_CTS, SER_DCTS);
635249997Swkoszek		SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_DCD,
636249997Swkoszek		       sig, SER_DCD, SER_DDCD);
637249997Swkoszek		SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_RI,
638249997Swkoszek		       sig, SER_RI,  SER_DRI);
639249997Swkoszek		new = sig & ~SER_MASK_DELTA;
640249997Swkoszek	} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
641249997Swkoszek	return (sig);
642249997Swkoszek}
643249997Swkoszek
644249997Swkoszekstatic int
645249997Swkoszekcdnc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
646249997Swkoszek{
647249997Swkoszek	struct uart_bas *bas = &sc->sc_bas;
648249997Swkoszek	uint32_t uart_ctrl, modem_ctrl;
649249997Swkoszek	int error = 0;
650249997Swkoszek
651249997Swkoszek	uart_lock(sc->sc_hwmtx);
652249997Swkoszek
653249997Swkoszek	switch (request) {
654249997Swkoszek	case UART_IOCTL_BREAK:
655249997Swkoszek		uart_ctrl = RD4(bas, CDNC_UART_CTRL_REG);
656249997Swkoszek		if (data) {
657249997Swkoszek			uart_ctrl |= CDNC_UART_CTRL_REG_STARTBRK;
658249997Swkoszek			uart_ctrl &= ~CDNC_UART_CTRL_REG_STOPBRK;
659249997Swkoszek		} else {
660249997Swkoszek			uart_ctrl |= CDNC_UART_CTRL_REG_STOPBRK;
661249997Swkoszek			uart_ctrl &= ~CDNC_UART_CTRL_REG_STARTBRK;
662249997Swkoszek		}
663249997Swkoszek		WR4(bas, CDNC_UART_CTRL_REG, uart_ctrl);
664249997Swkoszek		break;
665249997Swkoszek	case UART_IOCTL_IFLOW:
666249997Swkoszek		modem_ctrl = RD4(bas, CDNC_UART_MODEM_CTRL_REG);
667249997Swkoszek		if (data)
668249997Swkoszek			modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_RTS;
669249997Swkoszek		else
670249997Swkoszek			modem_ctrl &= ~CDNC_UART_MODEM_CTRL_REG_RTS;
671249997Swkoszek		WR4(bas, CDNC_UART_MODEM_CTRL_REG, modem_ctrl);
672249997Swkoszek		break;
673249997Swkoszek	default:
674249997Swkoszek		error = EINVAL;
675249997Swkoszek		break;
676249997Swkoszek	}
677249997Swkoszek
678249997Swkoszek	uart_unlock(sc->sc_hwmtx);
679249997Swkoszek
680249997Swkoszek	return (error);
681249997Swkoszek}
682249997Swkoszek
683262649Simpstatic void
684262649Simpcdnc_uart_bus_grab(struct uart_softc *sc)
685262649Simp{
686262649Simp
687262649Simp	/* Enable interrupts. */
688262649Simp	WR4(&sc->sc_bas, CDNC_UART_IEN_REG,
689262649Simp	    CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR |
690262649Simp	    CDNC_UART_INT_DMSI);
691262649Simp}
692262649Simp
693262649Simpstatic void
694262649Simpcdnc_uart_bus_ungrab(struct uart_softc *sc)
695262649Simp{
696262649Simp
697262649Simp	/* Enable interrupts. */
698262649Simp	WR4(&sc->sc_bas, CDNC_UART_IEN_REG,
699262649Simp	    CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT |
700262649Simp	    CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR |
701262649Simp	    CDNC_UART_INT_DMSI);
702262649Simp}
703262649Simp
704283327Sianstatic struct uart_class uart_cdnc_class = {
705249997Swkoszek	"cdnc_uart",
706249997Swkoszek	cdnc_uart_bus_methods,
707249997Swkoszek	sizeof(struct uart_softc),
708249997Swkoszek	.uc_ops = &cdnc_uart_ops,
709249997Swkoszek	.uc_range = 8
710249997Swkoszek};
711283327Sian
712283327Sianstatic struct ofw_compat_data compat_data[] = {
713283327Sian	{"cadence,uart",	(uintptr_t)&uart_cdnc_class},
714283327Sian	{NULL,			(uintptr_t)NULL},
715283327Sian};
716283327SianUART_FDT_CLASS_AND_DEVICE(compat_data);
717