1296936Smmel/*-
2296936Smmel * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3296936Smmel * All rights reserved.
4296936Smmel *
5296936Smmel * Redistribution and use in source and binary forms, with or without
6296936Smmel * modification, are permitted provided that the following conditions
7296936Smmel * are met:
8296936Smmel * 1. Redistributions of source code must retain the above copyright
9296936Smmel *    notice, this list of conditions and the following disclaimer.
10296936Smmel * 2. Redistributions in binary form must reproduce the above copyright
11296936Smmel *    notice, this list of conditions and the following disclaimer in the
12296936Smmel *    documentation and/or other materials provided with the distribution.
13296936Smmel *
14296936Smmel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15296936Smmel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16296936Smmel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17296936Smmel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18296936Smmel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19296936Smmel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20296936Smmel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21296936Smmel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22296936Smmel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23296936Smmel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24296936Smmel * SUCH DAMAGE.
25296936Smmel */
26296936Smmel
27296936Smmel#include <sys/cdefs.h>
28296936Smmel__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/tegra_uart.c 340145 2018-11-04 23:28:56Z mmacy $");
29296936Smmel
30296936Smmel
31296936Smmel/*
32296936Smmel * UART driver for Tegra SoCs.
33296936Smmel */
34296936Smmel#include "opt_platform.h"
35296936Smmel
36296936Smmel#include <sys/param.h>
37296936Smmel#include <sys/systm.h>
38296936Smmel#include <sys/bus.h>
39296936Smmel#include <sys/conf.h>
40296936Smmel#include <sys/kernel.h>
41296936Smmel#include <sys/module.h>
42296936Smmel#include <sys/sysctl.h>
43296936Smmel#include <machine/bus.h>
44296936Smmel
45296936Smmel#include <dev/extres/clk/clk.h>
46296936Smmel#include <dev/extres/hwreset/hwreset.h>
47296936Smmel#include <dev/fdt/fdt_common.h>
48296936Smmel#include <dev/ofw/ofw_bus.h>
49296936Smmel#include <dev/ofw/ofw_bus_subr.h>
50296936Smmel#include <dev/uart/uart.h>
51296936Smmel#include <dev/uart/uart_cpu.h>
52296936Smmel#include <dev/uart/uart_cpu_fdt.h>
53296936Smmel#include <dev/uart/uart_bus.h>
54296936Smmel#include <dev/uart/uart_dev_ns8250.h>
55296936Smmel#include <dev/ic/ns16550.h>
56296936Smmel
57296936Smmel#include "uart_if.h"
58296936Smmel
59296936Smmel/*
60296936Smmel * High-level UART interface.
61296936Smmel */
62296936Smmelstruct tegra_softc {
63297288Smmel	struct ns8250_softc 	ns8250_base;
64297288Smmel	clk_t			clk;
65296936Smmel	hwreset_t		reset;
66296936Smmel};
67296936Smmel
68296936Smmel/*
69296936Smmel * UART class interface.
70296936Smmel */
71296936Smmelstatic int
72296936Smmeltegra_uart_attach(struct uart_softc *sc)
73296936Smmel{
74296936Smmel	int rv;
75296936Smmel	struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc;
76296936Smmel	struct uart_bas *bas = &sc->sc_bas;
77296936Smmel
78296936Smmel	rv = ns8250_bus_attach(sc);
79296936Smmel	if (rv != 0)
80296936Smmel		return (rv);
81296936Smmel
82296936Smmel	ns8250->ier_rxbits = 0x1d;
83296936Smmel	ns8250->ier_mask = 0xc0;
84296936Smmel	ns8250->ier = uart_getreg(bas, REG_IER) & ns8250->ier_mask;
85297288Smmel	ns8250->ier |= ns8250->ier_rxbits;
86296936Smmel	uart_setreg(bas, REG_IER, ns8250->ier);
87296936Smmel	uart_barrier(bas);
88296936Smmel	return (0);
89296936Smmel}
90296936Smmel
91296936Smmelstatic void
92296936Smmeltegra_uart_grab(struct uart_softc *sc)
93296936Smmel{
94296936Smmel	struct uart_bas *bas = &sc->sc_bas;
95296936Smmel	struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc;
96296936Smmel	u_char ier;
97296936Smmel
98296936Smmel	/*
99296936Smmel	 * turn off all interrupts to enter polling mode. Leave the
100296936Smmel	 * saved mask alone. We'll restore whatever it was in ungrab.
101296936Smmel	 * All pending interrupt signals are reset when IER is set to 0.
102296936Smmel	 */
103296936Smmel	uart_lock(sc->sc_hwmtx);
104296936Smmel	ier = uart_getreg(bas, REG_IER);
105296936Smmel	uart_setreg(bas, REG_IER, ier & ns8250->ier_mask);
106296936Smmel	uart_setreg(bas, REG_FCR, 0);
107296936Smmel	uart_barrier(bas);
108296936Smmel	uart_unlock(sc->sc_hwmtx);
109296936Smmel}
110296936Smmel
111296936Smmelstatic void
112296936Smmeltegra_uart_ungrab(struct uart_softc *sc)
113296936Smmel{
114296936Smmel	struct ns8250_softc *ns8250 = (struct ns8250_softc*)sc;
115296936Smmel	struct uart_bas *bas = &sc->sc_bas;
116296936Smmel
117296936Smmel	/*
118296936Smmel	 * Restore previous interrupt mask
119296936Smmel	 */
120296936Smmel	uart_lock(sc->sc_hwmtx);
121296936Smmel	uart_setreg(bas, REG_FCR, ns8250->fcr);
122296936Smmel	uart_setreg(bas, REG_IER, ns8250->ier);
123296936Smmel	uart_barrier(bas);
124296936Smmel	uart_unlock(sc->sc_hwmtx);
125296936Smmel}
126296936Smmel
127296936Smmelstatic kobj_method_t tegra_methods[] = {
128296936Smmel	KOBJMETHOD(uart_probe,		ns8250_bus_probe),
129296936Smmel	KOBJMETHOD(uart_attach,		tegra_uart_attach),
130296936Smmel	KOBJMETHOD(uart_detach,		ns8250_bus_detach),
131296936Smmel	KOBJMETHOD(uart_flush,		ns8250_bus_flush),
132296936Smmel	KOBJMETHOD(uart_getsig,		ns8250_bus_getsig),
133296936Smmel	KOBJMETHOD(uart_ioctl,		ns8250_bus_ioctl),
134296936Smmel	KOBJMETHOD(uart_ipend,		ns8250_bus_ipend),
135296936Smmel	KOBJMETHOD(uart_param,		ns8250_bus_param),
136296936Smmel	KOBJMETHOD(uart_receive,	ns8250_bus_receive),
137296936Smmel	KOBJMETHOD(uart_setsig,		ns8250_bus_setsig),
138296936Smmel	KOBJMETHOD(uart_transmit,	ns8250_bus_transmit),
139296936Smmel	KOBJMETHOD(uart_grab,		tegra_uart_grab),
140296936Smmel	KOBJMETHOD(uart_ungrab,		tegra_uart_ungrab),
141296936Smmel	KOBJMETHOD_END
142296936Smmel};
143296936Smmel
144296936Smmelstatic struct uart_class tegra_uart_class = {
145296936Smmel	"tegra class",
146296936Smmel	tegra_methods,
147296936Smmel	sizeof(struct tegra_softc),
148296936Smmel	.uc_ops = &uart_ns8250_ops,
149296936Smmel	.uc_range = 8,
150296936Smmel	.uc_rclk = 0,
151296936Smmel};
152296936Smmel
153296936Smmel/* Compatible devices. */
154296936Smmelstatic struct ofw_compat_data compat_data[] = {
155296936Smmel	{"nvidia,tegra124-uart", (uintptr_t)&tegra_uart_class},
156296936Smmel	{NULL,			(uintptr_t)NULL},
157296936Smmel};
158296936Smmel
159296936SmmelUART_FDT_CLASS(compat_data);
160296936Smmel
161296936Smmel/*
162296936Smmel * UART Driver interface.
163296936Smmel */
164296936Smmelstatic int
165296936Smmeluart_fdt_get_shift1(phandle_t node)
166296936Smmel{
167296936Smmel	pcell_t shift;
168296936Smmel
169296936Smmel	if ((OF_getencprop(node, "reg-shift", &shift, sizeof(shift))) <= 0)
170296936Smmel		shift = 2;
171296936Smmel	return ((int)shift);
172296936Smmel}
173296936Smmel
174296936Smmelstatic int
175296936Smmeltegra_uart_probe(device_t dev)
176296936Smmel{
177296936Smmel	struct tegra_softc *sc;
178296936Smmel	phandle_t node;
179296936Smmel	uint64_t freq;
180296936Smmel	int shift;
181296936Smmel	int rv;
182296936Smmel	const struct ofw_compat_data *cd;
183296936Smmel
184296936Smmel	sc = device_get_softc(dev);
185296936Smmel	if (!ofw_bus_status_okay(dev))
186296936Smmel		return (ENXIO);
187296936Smmel	cd = ofw_bus_search_compatible(dev, compat_data);
188296936Smmel	if (cd->ocd_data == 0)
189296936Smmel		return (ENXIO);
190296936Smmel	sc->ns8250_base.base.sc_class = (struct uart_class *)cd->ocd_data;
191296936Smmel
192308324Smmel	rv = hwreset_get_by_ofw_name(dev, 0, "serial", &sc->reset);
193296936Smmel	if (rv != 0) {
194296936Smmel		device_printf(dev, "Cannot get 'serial' reset\n");
195296936Smmel		return (ENXIO);
196296936Smmel	}
197296936Smmel	rv = hwreset_deassert(sc->reset);
198296936Smmel	if (rv != 0) {
199296936Smmel		device_printf(dev, "Cannot unreset 'serial' reset\n");
200296936Smmel		return (ENXIO);
201296936Smmel	}
202296936Smmel
203296936Smmel	node = ofw_bus_get_node(dev);
204296936Smmel	shift = uart_fdt_get_shift1(node);
205308324Smmel	rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
206296936Smmel	if (rv != 0) {
207296936Smmel		device_printf(dev, "Cannot get UART clock: %d\n", rv);
208296936Smmel		return (ENXIO);
209296936Smmel	}
210296936Smmel	rv = clk_enable(sc->clk);
211296936Smmel	if (rv != 0) {
212296936Smmel		device_printf(dev, "Cannot enable UART clock: %d\n", rv);
213296936Smmel		return (ENXIO);
214296936Smmel	}
215296936Smmel	rv = clk_get_freq(sc->clk, &freq);
216296936Smmel	if (rv != 0) {
217296936Smmel		device_printf(dev, "Cannot enable UART clock: %d\n", rv);
218296936Smmel		return (ENXIO);
219296936Smmel	}
220340145Smmacy	return (uart_bus_probe(dev, shift, 0, (int)freq, 0, 0, 0));
221296936Smmel}
222296936Smmel
223296936Smmelstatic int
224296936Smmeltegra_uart_detach(device_t dev)
225296936Smmel{
226296936Smmel	struct tegra_softc *sc;
227296936Smmel
228296936Smmel	sc = device_get_softc(dev);
229296936Smmel	if (sc->clk != NULL) {
230296936Smmel		clk_release(sc->clk);
231296936Smmel	}
232296936Smmel
233296936Smmel	return (uart_bus_detach(dev));
234296936Smmel}
235296936Smmel
236296936Smmelstatic device_method_t tegra_uart_bus_methods[] = {
237296936Smmel	/* Device interface */
238296936Smmel	DEVMETHOD(device_probe,		tegra_uart_probe),
239296936Smmel	DEVMETHOD(device_attach,	uart_bus_attach),
240296936Smmel	DEVMETHOD(device_detach,	tegra_uart_detach),
241296936Smmel	{ 0, 0 }
242296936Smmel};
243296936Smmel
244296936Smmelstatic driver_t tegra_uart_driver = {
245296936Smmel	uart_driver_name,
246296936Smmel	tegra_uart_bus_methods,
247296936Smmel	sizeof(struct tegra_softc),
248296936Smmel};
249296936Smmel
250296936SmmelDRIVER_MODULE(tegra_uart, simplebus,  tegra_uart_driver, uart_devclass,
251340145Smmacy    0, 0);
252