ns8250_uart.c revision 1.2
1/* $NetBSD: ns8250_uart.c,v 1.2 2020/07/16 16:38:40 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2017-2020 Jared McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30
31__KERNEL_RCSID(1, "$NetBSD: ns8250_uart.c,v 1.2 2020/07/16 16:38:40 jmcneill Exp $");
32
33#include <sys/param.h>
34#include <sys/bus.h>
35#include <sys/device.h>
36#include <sys/intr.h>
37#include <sys/systm.h>
38#include <sys/time.h>
39#include <sys/termios.h>
40
41#include <dev/ic/comvar.h>
42
43#include <dev/fdt/fdtvar.h>
44
45static int ns8250_uart_match(device_t, cfdata_t, void *);
46static void ns8250_uart_attach(device_t, device_t, void *);
47
48struct ns8250_config {
49	int			type;
50	int			(*enable)(struct com_softc *);
51	void			(*disable)(struct com_softc *);
52};
53
54static const struct ns8250_config ns8250_config = {
55	.type = COM_TYPE_NORMAL,
56};
57
58static const struct ns8250_config ns16750_config = {
59	.type = COM_TYPE_16750,
60};
61
62#define	NS8250_OCTEON_USR_REG		0x0138
63
64static int
65ns8250_octeon_enable(struct com_softc *sc)
66{
67	struct com_regs *regsp = &sc->sc_regs;
68
69	/* XXX Clear old busy detect interrupts */
70	bus_space_read_1(regsp->cr_iot, regsp->cr_ioh, NS8250_OCTEON_USR_REG);
71
72	return 0;
73}
74
75static const struct ns8250_config octeon_config = {
76	.type = COM_TYPE_16550_NOERS,
77	.enable = ns8250_octeon_enable,
78};
79
80static const struct of_compat_data compat_data[] = {
81	{ "cavium,octeon-3860-uart",	(uintptr_t)&octeon_config },
82	{ "ns8250",			(uintptr_t)&ns8250_config },
83	{ "ns16450",			(uintptr_t)&ns8250_config },
84	{ "ns16550a",			(uintptr_t)&ns8250_config },
85	{ "ns16550",			(uintptr_t)&ns8250_config },
86	{ "ns16750",			(uintptr_t)&ns16750_config },
87	{ NULL }
88};
89
90CFATTACH_DECL_NEW(ns8250_uart, sizeof(struct com_softc),
91	ns8250_uart_match, ns8250_uart_attach, NULL, NULL);
92
93static int
94ns8250_uart_match(device_t parent, cfdata_t cf, void *aux)
95{
96	struct fdt_attach_args * const faa = aux;
97
98	return of_match_compat_data(faa->faa_phandle, compat_data);
99}
100
101static void
102ns8250_uart_attach(device_t parent, device_t self, void *aux)
103{
104	struct com_softc * const sc = device_private(self);
105	struct fdt_attach_args * const faa = aux;
106	const int phandle = faa->faa_phandle;
107	bus_space_tag_t bst = faa->faa_bst;
108	bus_space_handle_t bsh;
109	char intrstr[128];
110	struct clk *clk;
111	bus_addr_t addr;
112	bus_size_t size;
113	u_int reg_shift;
114	int error;
115	void *ih;
116
117	const struct ns8250_config *config =
118	    (void *)of_search_compatible(phandle, compat_data)->data;
119
120	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
121		aprint_error(": couldn't get registers\n");
122		return;
123	}
124
125	if (of_getprop_uint32(phandle, "reg-shift", &reg_shift)) {
126		/* missing or bad reg-shift property, assume 0 */
127		reg_shift = 0;
128	}
129
130	sc->sc_dev = self;
131	if (of_getprop_uint32(phandle, "clock-frequency", &sc->sc_frequency)) {
132		clk = fdtbus_clock_get_index(phandle, 0);
133		if (clk != NULL)
134			sc->sc_frequency = clk_get_rate(clk);
135	}
136	if (sc->sc_frequency == 0) {
137		aprint_error(": couldn't get frequency\n");
138		return;
139	}
140
141	sc->sc_type = config->type;
142	sc->enable = config->enable;
143	sc->disable = config->disable;
144
145	error = bus_space_map(bst, addr, size, 0, &bsh);
146	if (error) {
147		aprint_error(": couldn't map %#" PRIx64 ": %d",
148		    (uint64_t)addr, error);
149		return;
150	}
151
152	com_init_regs_stride(&sc->sc_regs, bst, bsh, addr, reg_shift);
153
154	if (config->enable != NULL) {
155		sc->enable(sc);
156		sc->enabled = 1;
157	}
158
159	com_attach_subr(sc);
160
161	if (!fdtbus_intr_str(faa->faa_phandle, 0, intrstr, sizeof(intrstr))) {
162		aprint_error_dev(self, "failed to decode interrupt\n");
163		return;
164	}
165
166	ih = fdtbus_intr_establish(faa->faa_phandle, 0, IPL_SERIAL,
167	    FDT_INTR_MPSAFE, comintr, sc);
168	if (ih == NULL) {
169		aprint_error_dev(self, "failed to establish interrupt on %s\n",
170		    intrstr);
171		return;
172	}
173	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
174}
175
176/*
177 * Console support
178 */
179
180static int
181ns8250_uart_console_match(int phandle)
182{
183	return of_match_compat_data(phandle, compat_data);
184}
185
186static void
187ns8250_uart_console_consinit(struct fdt_attach_args *faa, u_int uart_freq)
188{
189	const int phandle = faa->faa_phandle;
190	bus_space_tag_t bst = faa->faa_bst;
191	bus_space_handle_t dummy_bsh;
192	struct com_regs regs;
193	bus_addr_t addr;
194	tcflag_t flags;
195	u_int reg_shift;
196	int speed;
197
198	const struct ns8250_config *config =
199	    (void *)of_search_compatible(phandle, compat_data)->data;
200
201	fdtbus_get_reg(phandle, 0, &addr, NULL);
202	speed = fdtbus_get_stdout_speed();
203	if (speed < 0)
204		speed = 115200;	/* default */
205	flags = fdtbus_get_stdout_flags();
206
207	if (of_getprop_uint32(phandle, "reg-shift", &reg_shift)) {
208		/* missing or bad reg-shift property, assume 0 */
209		reg_shift = 0;
210	}
211
212	memset(&dummy_bsh, 0, sizeof(dummy_bsh));
213	com_init_regs_stride(&regs, bst, dummy_bsh, addr, reg_shift);
214
215	if (comcnattach1(&regs, speed, uart_freq, config->type, flags))
216		panic("Cannot initialize ns8250 console");
217}
218
219static const struct fdt_console ns8250_uart_console = {
220	.match = ns8250_uart_console_match,
221	.consinit = ns8250_uart_console_consinit,
222};
223
224FDT_CONSOLE(ns8250_uart, &ns8250_uart_console);
225