1*6e54367aSthorpej /* $NetBSD: tegra_com.c,v 1.15 2021/01/27 03:10:19 thorpej Exp $ */
2d4fd1143Sjmcneill
3d4fd1143Sjmcneill /*-
4d4fd1143Sjmcneill * Copyright (c) 2013 The NetBSD Foundation, Inc.
5d4fd1143Sjmcneill * All rights reserved.
6d4fd1143Sjmcneill *
7d4fd1143Sjmcneill * This code is derived from software contributed to The NetBSD Foundation
8d4fd1143Sjmcneill * by Matt Thomas of 3am Software Foundry.
9d4fd1143Sjmcneill *
10d4fd1143Sjmcneill * Redistribution and use in source and binary forms, with or without
11d4fd1143Sjmcneill * modification, are permitted provided that the following conditions
12d4fd1143Sjmcneill * are met:
13d4fd1143Sjmcneill * 1. Redistributions of source code must retain the above copyright
14d4fd1143Sjmcneill * notice, this list of conditions and the following disclaimer.
15d4fd1143Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
16d4fd1143Sjmcneill * notice, this list of conditions and the following disclaimer in the
17d4fd1143Sjmcneill * documentation and/or other materials provided with the distribution.
18d4fd1143Sjmcneill *
19d4fd1143Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20d4fd1143Sjmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21d4fd1143Sjmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22d4fd1143Sjmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23d4fd1143Sjmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24d4fd1143Sjmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25d4fd1143Sjmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26d4fd1143Sjmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27d4fd1143Sjmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28d4fd1143Sjmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29d4fd1143Sjmcneill * POSSIBILITY OF SUCH DAMAGE.
30d4fd1143Sjmcneill */
31d4fd1143Sjmcneill
32d4fd1143Sjmcneill #include <sys/cdefs.h>
33d4fd1143Sjmcneill
34*6e54367aSthorpej __KERNEL_RCSID(1, "$NetBSD: tegra_com.c,v 1.15 2021/01/27 03:10:19 thorpej Exp $");
35d4fd1143Sjmcneill
36d4fd1143Sjmcneill #include <sys/param.h>
37d4fd1143Sjmcneill #include <sys/bus.h>
38d4fd1143Sjmcneill #include <sys/device.h>
39d4fd1143Sjmcneill #include <sys/intr.h>
40d4fd1143Sjmcneill #include <sys/systm.h>
41d4fd1143Sjmcneill #include <sys/time.h>
42d4fd1143Sjmcneill #include <sys/termios.h>
43d4fd1143Sjmcneill
44d4fd1143Sjmcneill #include <arm/nvidia/tegra_reg.h>
45d4fd1143Sjmcneill #include <arm/nvidia/tegra_var.h>
46d4fd1143Sjmcneill
47d4fd1143Sjmcneill #include <dev/ic/comvar.h>
48d4fd1143Sjmcneill
49d59db8d0Sjmcneill #include <dev/fdt/fdtvar.h>
50d59db8d0Sjmcneill
51d4fd1143Sjmcneill static int tegra_com_match(device_t, cfdata_t, void *);
52d4fd1143Sjmcneill static void tegra_com_attach(device_t, device_t, void *);
53d4fd1143Sjmcneill
54*6e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
55*6e54367aSthorpej { .compat = "nvidia,tegra210-uart" },
56*6e54367aSthorpej { .compat = "nvidia,tegra124-uart" },
57*6e54367aSthorpej { .compat = "nvidia,tegra20-uart" },
58*6e54367aSthorpej DEVICE_COMPAT_EOL
5973b64c76Sjmcneill };
6073b64c76Sjmcneill
61d4fd1143Sjmcneill struct tegra_com_softc {
62d4fd1143Sjmcneill struct com_softc tsc_sc;
63d4fd1143Sjmcneill void *tsc_ih;
6493e0bfebSjmcneill
6593e0bfebSjmcneill struct clk *tsc_clk;
6693e0bfebSjmcneill struct fdtbus_reset *tsc_rst;
67d4fd1143Sjmcneill };
68d4fd1143Sjmcneill
69d4fd1143Sjmcneill CFATTACH_DECL_NEW(tegra_com, sizeof(struct tegra_com_softc),
70d4fd1143Sjmcneill tegra_com_match, tegra_com_attach, NULL, NULL);
71d4fd1143Sjmcneill
72d4fd1143Sjmcneill static int
tegra_com_match(device_t parent,cfdata_t cf,void * aux)73d4fd1143Sjmcneill tegra_com_match(device_t parent, cfdata_t cf, void *aux)
74d4fd1143Sjmcneill {
75d59db8d0Sjmcneill struct fdt_attach_args * const faa = aux;
76d4fd1143Sjmcneill
77*6e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
78d4fd1143Sjmcneill }
79d4fd1143Sjmcneill
80d4fd1143Sjmcneill static void
tegra_com_attach(device_t parent,device_t self,void * aux)81d4fd1143Sjmcneill tegra_com_attach(device_t parent, device_t self, void *aux)
82d4fd1143Sjmcneill {
83d4fd1143Sjmcneill struct tegra_com_softc * const tsc = device_private(self);
84d4fd1143Sjmcneill struct com_softc * const sc = &tsc->tsc_sc;
85d59db8d0Sjmcneill struct fdt_attach_args * const faa = aux;
86c30892caSjmcneill bus_space_tag_t bst = faa->faa_bst;
87d4fd1143Sjmcneill bus_space_handle_t bsh;
88d59db8d0Sjmcneill char intrstr[128];
89d59db8d0Sjmcneill bus_addr_t addr;
90d59db8d0Sjmcneill bus_size_t size;
91d59db8d0Sjmcneill u_int reg_shift;
924e8cdc22Sjmcneill int error;
93d59db8d0Sjmcneill
94d59db8d0Sjmcneill if (fdtbus_get_reg(faa->faa_phandle, 0, &addr, &size) != 0) {
95d59db8d0Sjmcneill aprint_error(": couldn't get registers\n");
96d59db8d0Sjmcneill return;
97d59db8d0Sjmcneill }
98d59db8d0Sjmcneill
994e8cdc22Sjmcneill if (of_getprop_uint32(faa->faa_phandle, "reg-shift", ®_shift)) {
1004e8cdc22Sjmcneill /* missing or bad reg-shift property, assume 2 */
101c30892caSjmcneill reg_shift = 2;
102d59db8d0Sjmcneill }
103d4fd1143Sjmcneill
104d4fd1143Sjmcneill sc->sc_dev = self;
105d59db8d0Sjmcneill
10693e0bfebSjmcneill tsc->tsc_clk = fdtbus_clock_get_index(faa->faa_phandle, 0);
10793e0bfebSjmcneill tsc->tsc_rst = fdtbus_reset_get(faa->faa_phandle, "serial");
10893e0bfebSjmcneill
10993e0bfebSjmcneill if (tsc->tsc_clk == NULL) {
11093e0bfebSjmcneill aprint_error(": couldn't get frequency\n");
11193e0bfebSjmcneill return;
112d59db8d0Sjmcneill }
11393e0bfebSjmcneill
11493e0bfebSjmcneill sc->sc_frequency = clk_get_rate(tsc->tsc_clk);
115ec484ab4Sjmcneill sc->sc_type = COM_TYPE_TEGRA;
116d4fd1143Sjmcneill
117d59db8d0Sjmcneill error = bus_space_map(bst, addr, size, 0, &bsh);
118d59db8d0Sjmcneill if (error) {
1192e65b46dSskrll aprint_error(": couldn't map %#" PRIxBUSADDR ": %d", addr, error);
120d59db8d0Sjmcneill return;
121d4fd1143Sjmcneill }
122d59db8d0Sjmcneill
123c30892caSjmcneill com_init_regs_stride(&sc->sc_regs, bst, bsh, addr, reg_shift);
124d4fd1143Sjmcneill
125d4fd1143Sjmcneill com_attach_subr(sc);
126d4fd1143Sjmcneill aprint_naive("\n");
127d4fd1143Sjmcneill
128d59db8d0Sjmcneill if (!fdtbus_intr_str(faa->faa_phandle, 0, intrstr, sizeof(intrstr))) {
129d59db8d0Sjmcneill aprint_error_dev(self, "failed to decode interrupt\n");
130d59db8d0Sjmcneill return;
131d59db8d0Sjmcneill }
132d59db8d0Sjmcneill
1333f513eddSjmcneill tsc->tsc_ih = fdtbus_intr_establish_xname(faa->faa_phandle, 0,
1343f513eddSjmcneill IPL_SERIAL, FDT_INTR_MPSAFE, comintr, sc, device_xname(self));
135d59db8d0Sjmcneill if (tsc->tsc_ih == NULL) {
136d59db8d0Sjmcneill aprint_error_dev(self, "failed to establish interrupt on %s\n",
137d59db8d0Sjmcneill intrstr);
138d59db8d0Sjmcneill }
139d59db8d0Sjmcneill aprint_normal_dev(self, "interrupting on %s\n", intrstr);
140d4fd1143Sjmcneill }
14173b64c76Sjmcneill
14273b64c76Sjmcneill /*
14373b64c76Sjmcneill * Console support
14473b64c76Sjmcneill */
14573b64c76Sjmcneill
14673b64c76Sjmcneill static int
tegra_com_console_match(int phandle)14773b64c76Sjmcneill tegra_com_console_match(int phandle)
14873b64c76Sjmcneill {
149*6e54367aSthorpej return of_compatible_match(phandle, compat_data);
15073b64c76Sjmcneill }
15173b64c76Sjmcneill
15273b64c76Sjmcneill static void
tegra_com_console_consinit(struct fdt_attach_args * faa,u_int uart_freq)153faf8273eSjmcneill tegra_com_console_consinit(struct fdt_attach_args *faa, u_int uart_freq)
15473b64c76Sjmcneill {
15573b64c76Sjmcneill const int phandle = faa->faa_phandle;
156c30892caSjmcneill bus_space_tag_t bst = faa->faa_bst;
157c30892caSjmcneill bus_space_handle_t dummy_bsh;
158c30892caSjmcneill struct com_regs regs;
15973b64c76Sjmcneill bus_addr_t addr;
16073b64c76Sjmcneill tcflag_t flags;
161c30892caSjmcneill u_int reg_shift;
16273b64c76Sjmcneill int speed;
16373b64c76Sjmcneill
16473b64c76Sjmcneill fdtbus_get_reg(phandle, 0, &addr, NULL);
16573b64c76Sjmcneill speed = fdtbus_get_stdout_speed();
16673b64c76Sjmcneill if (speed < 0)
16773b64c76Sjmcneill speed = 115200; /* default */
16873b64c76Sjmcneill flags = fdtbus_get_stdout_flags();
16973b64c76Sjmcneill
170c30892caSjmcneill if (of_getprop_uint32(faa->faa_phandle, "reg-shift", ®_shift)) {
171c30892caSjmcneill /* missing or bad reg-shift property, assume 2 */
172c30892caSjmcneill reg_shift = 2;
173c30892caSjmcneill }
174c30892caSjmcneill
175c30892caSjmcneill memset(&dummy_bsh, 0, sizeof(dummy_bsh));
176c30892caSjmcneill com_init_regs_stride(®s, bst, dummy_bsh, addr, reg_shift);
177c30892caSjmcneill
178c30892caSjmcneill if (comcnattach1(®s, speed, uart_freq, COM_TYPE_TEGRA, flags))
17973b64c76Sjmcneill panic("Cannot initialize tegra com console");
18073b64c76Sjmcneill }
18173b64c76Sjmcneill
18273b64c76Sjmcneill static const struct fdt_console tegra_com_console = {
18373b64c76Sjmcneill .match = tegra_com_console_match,
18473b64c76Sjmcneill .consinit = tegra_com_console_consinit,
18573b64c76Sjmcneill };
18673b64c76Sjmcneill
18773b64c76Sjmcneill FDT_CONSOLE(tegra_com, &tegra_com_console);
188