1*6e54367aSthorpej /* $NetBSD: tegra_lic.c,v 1.8 2021/01/27 03:10:19 thorpej Exp $ */
2d59db8d0Sjmcneill
3d59db8d0Sjmcneill /*-
4d59db8d0Sjmcneill * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
5d59db8d0Sjmcneill * All rights reserved.
6d59db8d0Sjmcneill *
7d59db8d0Sjmcneill * Redistribution and use in source and binary forms, with or without
8d59db8d0Sjmcneill * modification, are permitted provided that the following conditions
9d59db8d0Sjmcneill * are met:
10d59db8d0Sjmcneill * 1. Redistributions of source code must retain the above copyright
11d59db8d0Sjmcneill * notice, this list of conditions and the following disclaimer.
12d59db8d0Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
13d59db8d0Sjmcneill * notice, this list of conditions and the following disclaimer in the
14d59db8d0Sjmcneill * documentation and/or other materials provided with the distribution.
15d59db8d0Sjmcneill *
16d59db8d0Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17d59db8d0Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18d59db8d0Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19d59db8d0Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20d59db8d0Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21d59db8d0Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22d59db8d0Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23d59db8d0Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24d59db8d0Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25d59db8d0Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26d59db8d0Sjmcneill * SUCH DAMAGE.
27d59db8d0Sjmcneill */
28d59db8d0Sjmcneill
29d59db8d0Sjmcneill #include <sys/cdefs.h>
30*6e54367aSthorpej __KERNEL_RCSID(0, "$NetBSD: tegra_lic.c,v 1.8 2021/01/27 03:10:19 thorpej Exp $");
31d59db8d0Sjmcneill
32d59db8d0Sjmcneill #include <sys/param.h>
33d59db8d0Sjmcneill #include <sys/bus.h>
34d59db8d0Sjmcneill #include <sys/device.h>
35d59db8d0Sjmcneill #include <sys/intr.h>
36d59db8d0Sjmcneill #include <sys/systm.h>
37d59db8d0Sjmcneill #include <sys/kernel.h>
38d59db8d0Sjmcneill #include <sys/kmem.h>
39d59db8d0Sjmcneill
40d59db8d0Sjmcneill #include <arm/nvidia/tegra_reg.h>
41d59db8d0Sjmcneill #include <arm/nvidia/tegra_var.h>
42d59db8d0Sjmcneill
43d59db8d0Sjmcneill #include <arm/cortex/gic_intr.h>
44d59db8d0Sjmcneill
45d59db8d0Sjmcneill #include <dev/fdt/fdtvar.h>
46d59db8d0Sjmcneill
471e3ee39fSjmcneill #define LIC_CPU_IER_CLR_REG 0x28
481e3ee39fSjmcneill #define LIC_CPU_IEP_CLASS_REG 0x2c
491e3ee39fSjmcneill
50d59db8d0Sjmcneill static int tegra_lic_match(device_t, cfdata_t, void *);
51d59db8d0Sjmcneill static void tegra_lic_attach(device_t, device_t, void *);
52d59db8d0Sjmcneill
535cba6278Smarty static void * tegra_lic_establish(device_t, u_int *, int, int,
5459ad346dSjmcneill int (*)(void *), void *, const char *);
55d59db8d0Sjmcneill static void tegra_lic_disestablish(device_t, void *);
565cba6278Smarty static bool tegra_lic_intrstr(device_t, u_int *, char *, size_t);
57d59db8d0Sjmcneill
58d59db8d0Sjmcneill struct fdtbus_interrupt_controller_func tegra_lic_funcs = {
59d59db8d0Sjmcneill .establish = tegra_lic_establish,
60d59db8d0Sjmcneill .disestablish = tegra_lic_disestablish,
61d59db8d0Sjmcneill .intrstr = tegra_lic_intrstr
62d59db8d0Sjmcneill };
63d59db8d0Sjmcneill
64d59db8d0Sjmcneill struct tegra_lic_softc {
65d59db8d0Sjmcneill device_t sc_dev;
66d59db8d0Sjmcneill int sc_phandle;
67d59db8d0Sjmcneill };
68d59db8d0Sjmcneill
69d59db8d0Sjmcneill CFATTACH_DECL_NEW(tegra_lic, sizeof(struct tegra_lic_softc),
70d59db8d0Sjmcneill tegra_lic_match, tegra_lic_attach, NULL, NULL);
71d59db8d0Sjmcneill
72*6e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
73*6e54367aSthorpej { .compat = "nvidia,tegra210-ictlr" },
74*6e54367aSthorpej { .compat = "nvidia,tegra124-ictlr" },
75*6e54367aSthorpej DEVICE_COMPAT_EOL
76*6e54367aSthorpej };
77*6e54367aSthorpej
78d59db8d0Sjmcneill static int
tegra_lic_match(device_t parent,cfdata_t cf,void * aux)79d59db8d0Sjmcneill tegra_lic_match(device_t parent, cfdata_t cf, void *aux)
80d59db8d0Sjmcneill {
81d59db8d0Sjmcneill struct fdt_attach_args * const faa = aux;
82d59db8d0Sjmcneill
83*6e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
84d59db8d0Sjmcneill }
85d59db8d0Sjmcneill
86d59db8d0Sjmcneill static void
tegra_lic_attach(device_t parent,device_t self,void * aux)87d59db8d0Sjmcneill tegra_lic_attach(device_t parent, device_t self, void *aux)
88d59db8d0Sjmcneill {
89d59db8d0Sjmcneill struct tegra_lic_softc * const sc = device_private(self);
90d59db8d0Sjmcneill struct fdt_attach_args * const faa = aux;
911e3ee39fSjmcneill bus_space_tag_t bst;
921e3ee39fSjmcneill bus_space_handle_t bsh;
931e3ee39fSjmcneill bus_addr_t addr;
941e3ee39fSjmcneill bus_size_t size;
951e3ee39fSjmcneill int error, index;
96d59db8d0Sjmcneill
97d59db8d0Sjmcneill sc->sc_dev = self;
98d59db8d0Sjmcneill sc->sc_phandle = faa->faa_phandle;
99d59db8d0Sjmcneill
100d59db8d0Sjmcneill error = fdtbus_register_interrupt_controller(self, faa->faa_phandle,
101d59db8d0Sjmcneill &tegra_lic_funcs);
102d59db8d0Sjmcneill if (error) {
103d59db8d0Sjmcneill aprint_error(": couldn't register with fdtbus: %d\n", error);
104d59db8d0Sjmcneill return;
105d59db8d0Sjmcneill }
106d59db8d0Sjmcneill
107d59db8d0Sjmcneill aprint_naive("\n");
108d59db8d0Sjmcneill aprint_normal(": LIC\n");
1091e3ee39fSjmcneill
1101e3ee39fSjmcneill bst = faa->faa_bst;
1111e3ee39fSjmcneill for (index = 0; ; index++) {
1121e3ee39fSjmcneill error = fdtbus_get_reg(faa->faa_phandle, index, &addr, &size);
1131e3ee39fSjmcneill if (error != 0)
1141e3ee39fSjmcneill break;
1151e3ee39fSjmcneill error = bus_space_map(bst, addr, size, 0, &bsh);
1161e3ee39fSjmcneill if (error) {
1171e3ee39fSjmcneill aprint_error_dev(self, "can't map IC#%d: %d\n",
1181e3ee39fSjmcneill index, error);
1191e3ee39fSjmcneill continue;
1201e3ee39fSjmcneill }
1211e3ee39fSjmcneill
1221e3ee39fSjmcneill /* Clear interrupt enable for CPU */
1231e3ee39fSjmcneill bus_space_write_4(bst, bsh, LIC_CPU_IER_CLR_REG, 0xffffffff);
1241e3ee39fSjmcneill
1251e3ee39fSjmcneill /* Route to IRQ */
1261e3ee39fSjmcneill bus_space_write_4(bst, bsh, LIC_CPU_IEP_CLASS_REG, 0);
1271e3ee39fSjmcneill
1281e3ee39fSjmcneill bus_space_unmap(bst, bsh, size);
1291e3ee39fSjmcneill }
130d59db8d0Sjmcneill }
131d59db8d0Sjmcneill
132d59db8d0Sjmcneill static void *
tegra_lic_establish(device_t dev,u_int * specifier,int ipl,int flags,int (* func)(void *),void * arg,const char * xname)1335cba6278Smarty tegra_lic_establish(device_t dev, u_int *specifier, int ipl, int flags,
13459ad346dSjmcneill int (*func)(void *), void *arg, const char *xname)
135d59db8d0Sjmcneill {
136d59db8d0Sjmcneill int iflags = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0;
137d59db8d0Sjmcneill
138d59db8d0Sjmcneill /* 1st cell is the interrupt type; 0 is SPI, 1 is PPI */
139d59db8d0Sjmcneill /* 2nd cell is the interrupt number */
140d59db8d0Sjmcneill /* 3rd cell is flags */
141d59db8d0Sjmcneill
1425cba6278Smarty const u_int type = be32toh(specifier[0]);
1435cba6278Smarty const u_int intr = be32toh(specifier[1]);
144d59db8d0Sjmcneill const u_int irq = type == 0 ? IRQ_SPI(intr) : IRQ_PPI(intr);
1455cba6278Smarty const u_int trig = be32toh(specifier[2]) & 0xf;
1469ee05496Sthorpej const u_int level = (trig & FDT_INTR_TYPE_DOUBLE_EDGE)
1479ee05496Sthorpej ? IST_EDGE : IST_LEVEL;
148d59db8d0Sjmcneill
14959ad346dSjmcneill return intr_establish_xname(irq, ipl, level | iflags, func, arg,
15059ad346dSjmcneill xname);
151d59db8d0Sjmcneill }
152d59db8d0Sjmcneill
153d59db8d0Sjmcneill static void
tegra_lic_disestablish(device_t dev,void * ih)154d59db8d0Sjmcneill tegra_lic_disestablish(device_t dev, void *ih)
155d59db8d0Sjmcneill {
156d59db8d0Sjmcneill intr_disestablish(ih);
157d59db8d0Sjmcneill }
158d59db8d0Sjmcneill
159d59db8d0Sjmcneill static bool
tegra_lic_intrstr(device_t dev,u_int * specifier,char * buf,size_t buflen)1605cba6278Smarty tegra_lic_intrstr(device_t dev, u_int *specifier, char *buf,
161d59db8d0Sjmcneill size_t buflen)
162d59db8d0Sjmcneill {
163d59db8d0Sjmcneill /* 1st cell is the interrupt type; 0 is SPI, 1 is PPI */
164d59db8d0Sjmcneill /* 2nd cell is the interrupt number */
165d59db8d0Sjmcneill /* 3rd cell is flags */
166d59db8d0Sjmcneill
1675cba6278Smarty const u_int type = be32toh(specifier[0]);
1685cba6278Smarty const u_int intr = be32toh(specifier[1]);
169d59db8d0Sjmcneill const u_int irq = type == 0 ? IRQ_SPI(intr) : IRQ_PPI(intr);
170d59db8d0Sjmcneill
1711e3ee39fSjmcneill snprintf(buf, buflen, "irq %d", irq);
172d59db8d0Sjmcneill
173d59db8d0Sjmcneill return true;
174d59db8d0Sjmcneill }
175