1 /* $OpenBSD: mvgicp.c,v 1.6 2024/08/05 18:39:34 kettenis 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
mvgicp_match(struct device * parent,void * match,void * aux)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
mvgicp_attach(struct device * parent,struct device * self,void * aux)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 int node;
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 int fdt_intr_get_parent(int);
104 node = 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_node == node)
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 *
mvgicp_intr_establish(void * self,uint64_t * addr,uint64_t * data,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)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
mvgicp_intr_disestablish(void * cookie)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
mvgicp_intr_barrier(void * cookie)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