xref: /openbsd/sys/dev/fdt/mvgicp.c (revision 4bdff4be)
1 /*	$OpenBSD: mvgicp.c,v 1.5 2021/10/24 17:52:26 mpi Exp $	*/
2 /*
3  * Copyright (c) 2019 Patrick Wildt <patrick@blueri.se>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 #include <sys/malloc.h>
22 
23 #include <uvm/uvm_extern.h>
24 
25 #include <machine/intr.h>
26 #include <machine/bus.h>
27 #include <machine/fdt.h>
28 
29 #include <dev/ofw/openfirm.h>
30 #include <dev/ofw/ofw_misc.h>
31 #include <dev/ofw/fdt.h>
32 
33 struct mvgicp_softc {
34 	struct device		  sc_dev;
35 	bus_space_tag_t		  sc_iot;
36 	bus_space_handle_t	  sc_ioh;
37 	paddr_t			  sc_addr;
38 
39 	uint32_t		  sc_spi_ranges[4];
40 	void			**sc_spi;
41 	uint32_t		  sc_nspi;
42 
43 	struct interrupt_controller sc_ic;
44 	struct interrupt_controller *sc_parent_ic;
45 };
46 
47 int	mvgicp_match(struct device *, void *, void *);
48 void	mvgicp_attach(struct device *, struct device *, void *);
49 
50 void *	mvgicp_intr_establish(void *, uint64_t *, uint64_t *,
51 	    int, struct cpu_info *, int (*)(void *), void *, char *);
52 void	mvgicp_intr_disestablish(void *);
53 void	mvgicp_intr_barrier(void *);
54 
55 const struct cfattach mvgicp_ca = {
56 	sizeof(struct mvgicp_softc), mvgicp_match, mvgicp_attach
57 };
58 
59 struct cfdriver mvgicp_cd = {
60 	NULL, "mvgicp", DV_DULL
61 };
62 
63 int
64 mvgicp_match(struct device *parent, void *match, void *aux)
65 {
66 	struct fdt_attach_args *faa = aux;
67 
68 	return OF_is_compatible(faa->fa_node, "marvell,ap806-gicp");
69 }
70 
71 void
72 mvgicp_attach(struct device *parent, struct device *self, void *aux)
73 {
74 	struct mvgicp_softc *sc = (struct mvgicp_softc *)self;
75 	struct fdt_attach_args *faa = aux;
76 	struct interrupt_controller *ic;
77 	uint32_t phandle;
78 
79 	if (faa->fa_nreg < 1) {
80 		printf(": no registers\n");
81 		return;
82 	}
83 
84 	OF_getpropintarray(faa->fa_node, "marvell,spi-ranges",
85 	    sc->sc_spi_ranges, sizeof(sc->sc_spi_ranges));
86 	sc->sc_nspi = sc->sc_spi_ranges[1] + sc->sc_spi_ranges[3];
87 	sc->sc_spi = mallocarray(sc->sc_nspi, sizeof(void *),
88 	    M_DEVBUF, M_WAITOK | M_ZERO);
89 
90 	sc->sc_iot = faa->fa_iot;
91 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
92 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
93 		printf(": can't map registers\n");
94 		return;
95 	}
96 
97 	/* XXX: Hack to retrieve the physical address (from a CPU PoV). */
98 	if (!pmap_extract(pmap_kernel(), sc->sc_ioh, &sc->sc_addr)) {
99 		printf(": cannot retrieve msi addr\n");
100 		return;
101 	}
102 
103 	extern uint32_t fdt_intr_get_parent(int);
104 	phandle = fdt_intr_get_parent(faa->fa_node);
105 	extern LIST_HEAD(, interrupt_controller) interrupt_controllers;
106 	LIST_FOREACH(ic, &interrupt_controllers, ic_list) {
107 		if (ic->ic_phandle == phandle)
108 			break;
109 	}
110 	sc->sc_parent_ic = ic;
111 
112 	sc->sc_ic.ic_node = faa->fa_node;
113 	sc->sc_ic.ic_cookie = sc;
114 	sc->sc_ic.ic_establish_msi = mvgicp_intr_establish;
115 	sc->sc_ic.ic_disestablish = mvgicp_intr_disestablish;
116 	sc->sc_ic.ic_barrier = mvgicp_intr_barrier;
117 	fdt_intr_register(&sc->sc_ic);
118 
119 	printf("\n");
120 }
121 
122 void *
123 mvgicp_intr_establish(void *self, uint64_t *addr, uint64_t *data,
124     int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
125 {
126 	struct mvgicp_softc *sc = (struct mvgicp_softc *)self;
127 	struct interrupt_controller *ic = sc->sc_parent_ic;
128 	struct machine_intr_handle *ih;
129 	uint32_t interrupt[3];
130 	uint32_t flags;
131 	void *cookie;
132 	int i, spi;
133 
134 	if (ic == NULL)
135 		return NULL;
136 
137 	for (i = 0; i < sc->sc_nspi; i++) {
138 		if (sc->sc_spi[i] == NULL) {
139 			spi = i;
140 			break;
141 		}
142 	}
143 	if (i == sc->sc_nspi)
144 		return NULL;
145 
146 	flags = *data;
147 
148 	*addr = sc->sc_addr;
149 	*data = spi;
150 
151 	/* Convert to GIC interrupt source. */
152 	for (i = 0; i < nitems(sc->sc_spi_ranges); i += 2) {
153 		if (spi < sc->sc_spi_ranges[i + 1]) {
154 			spi += sc->sc_spi_ranges[i];
155 			break;
156 		}
157 		spi -= sc->sc_spi_ranges[i + 1];
158 	}
159 	if (i == nitems(sc->sc_spi_ranges))
160 		return NULL;
161 
162 	interrupt[0] = 0;
163 	interrupt[1] = spi - 32;
164 	interrupt[2] = flags;
165 	cookie = ic->ic_establish(ic->ic_cookie, interrupt, level,
166 	    ci, func, arg, name);
167 	if (cookie == NULL)
168 		return NULL;
169 
170 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
171 	ih->ih_ic = ic;
172 	ih->ih_ih = cookie;
173 
174 	sc->sc_spi[*data] = ih;
175 	return &sc->sc_spi[*data];
176 }
177 
178 void
179 mvgicp_intr_disestablish(void *cookie)
180 {
181 	struct machine_intr_handle *ih = *(void **)cookie;
182 	struct interrupt_controller *ic = ih->ih_ic;
183 
184 	ic->ic_disestablish(ih->ih_ih);
185 	free(ih, M_DEVBUF, sizeof(*ih));
186 	*(void **)cookie = NULL;
187 }
188 
189 void
190 mvgicp_intr_barrier(void *cookie)
191 {
192 	struct machine_intr_handle *ih = *(void **)cookie;
193 	struct interrupt_controller *ic = ih->ih_ic;
194 
195 	ic->ic_barrier(ih->ih_ih);
196 }
197