xref: /netbsd-src/sys/arch/arm/nvidia/tegra_lic.c (revision 6e54367a22fbc89a1139d033e95bec0c0cf0975b)
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