xref: /openbsd-src/sys/dev/fdt/qcsmptp.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*	$OpenBSD: qcsmptp.c,v 1.1 2023/05/19 21:26:10 patrick Exp $	*/
2 /*
3  * Copyright (c) 2023 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 #include <sys/atomic.h>
23 
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26 
27 #include <dev/ofw/openfirm.h>
28 #include <dev/ofw/ofw_misc.h>
29 #include <dev/ofw/fdt.h>
30 
31 #define SMP2P_MAX_ENTRY		16
32 #define SMP2P_MAX_ENTRY_NAME	16
33 
34 struct qcsmptp_smem_item {
35 	uint32_t magic;
36 #define SMP2P_MAGIC			0x504d5324
37 	uint8_t version;
38 #define SMP2P_VERSION			1
39 	unsigned features:24;
40 #define SMP2P_FEATURE_SSR_ACK		(1 << 0)
41 	uint16_t local_pid;
42 	uint16_t remote_pid;
43 	uint16_t total_entries;
44 	uint16_t valid_entries;
45 	uint32_t flags;
46 #define SMP2P_FLAGS_RESTART_DONE	(1 << 0)
47 #define SMP2P_FLAGS_RESTART_ACK		(1 << 1)
48 
49 	struct {
50 		uint8_t name[SMP2P_MAX_ENTRY_NAME];
51 		uint32_t value;
52 	} entries[SMP2P_MAX_ENTRY];
53 } __packed;
54 
55 struct qcsmptp_intrhand {
56 	TAILQ_ENTRY(qcsmptp_intrhand) ih_q;
57 	int (*ih_func)(void *);
58 	void *ih_arg;
59 	void *ih_ic;
60 	int ih_pin;
61 	int ih_enabled;
62 };
63 
64 struct qcsmptp_interrupt_controller {
65 	TAILQ_HEAD(,qcsmptp_intrhand) ic_intrq;
66 	struct qcsmptp_softc *ic_sc;
67 	struct interrupt_controller ic_ic;
68 };
69 
70 struct qcsmptp_entry {
71 	TAILQ_ENTRY(qcsmptp_entry) e_q;
72 	char *e_name;
73 	int e_namelen;
74 	uint32_t *e_value;
75 	uint32_t e_last_value;
76 	struct qcsmptp_interrupt_controller *e_ic;
77 };
78 
79 struct qcsmptp_softc {
80 	struct device		sc_dev;
81 	int			sc_node;
82 	void			*sc_ih;
83 
84 	uint16_t		sc_local_pid;
85 	uint16_t		sc_remote_pid;
86 	uint32_t		sc_smem_id[2];
87 
88 	struct qcsmptp_smem_item *sc_in;
89 	struct qcsmptp_smem_item *sc_out;
90 
91 	TAILQ_HEAD(,qcsmptp_entry) sc_inboundq;
92 	TAILQ_HEAD(,qcsmptp_entry) sc_outboundq;
93 
94 	int			sc_negotiated;
95 	int			sc_ssr_ack_enabled;
96 	int			sc_ssr_ack;
97 
98 	uint16_t		sc_valid_entries;
99 
100 	struct mbox_channel	*sc_mc;
101 };
102 
103 int	qcsmptp_match(struct device *, void *, void *);
104 void	qcsmptp_attach(struct device *, struct device *, void *);
105 void	qcsmptp_deferred(struct device *);
106 
107 const struct cfattach qcsmptp_ca = {
108 	sizeof (struct qcsmptp_softc), qcsmptp_match, qcsmptp_attach
109 };
110 
111 struct cfdriver qcsmptp_cd = {
112 	NULL, "qcsmptp", DV_DULL
113 };
114 
115 int	qcsmptp_intr(void *);
116 void	*qcsmptp_intr_establish(void *, int *, int, struct cpu_info *,
117 	    int (*)(void *), void *, char *);
118 void	qcsmptp_intr_disestablish(void *);
119 void	qcsmptp_intr_enable(void *);
120 void	qcsmptp_intr_disable(void *);
121 void	qcsmptp_intr_barrier(void *);
122 
123 extern int qcsmem_alloc(int, int, int);
124 extern void *qcsmem_get(int, int, int *);
125 
126 int
127 qcsmptp_match(struct device *parent, void *match, void *aux)
128 {
129 	struct fdt_attach_args *faa = aux;
130 
131 	return OF_is_compatible(faa->fa_node, "qcom,smp2p");
132 }
133 
134 void
135 qcsmptp_attach(struct device *parent, struct device *self, void *aux)
136 {
137 	struct qcsmptp_softc *sc = (struct qcsmptp_softc *)self;
138 	struct fdt_attach_args *faa = aux;
139 
140 	sc->sc_node = faa->fa_node;
141 
142 	TAILQ_INIT(&sc->sc_inboundq);
143 	TAILQ_INIT(&sc->sc_outboundq);
144 
145 	sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_BIO,
146 	    qcsmptp_intr, sc, sc->sc_dev.dv_xname);
147 	if (sc->sc_ih == NULL) {
148 		printf(": can't establish interrupt\n");
149 		return;
150 	}
151 
152 	sc->sc_local_pid = OF_getpropint(faa->fa_node,
153 	    "qcom,local-pid", 0xffff);
154 	sc->sc_remote_pid = OF_getpropint(faa->fa_node,
155 	    "qcom,remote-pid", 0xffff);
156 	if (sc->sc_local_pid == 0xffff || sc->sc_remote_pid == 0xffff) {
157 		printf(": can't get pids\n");
158 		return;
159 	}
160 
161 	if (OF_getpropintarray(faa->fa_node, "qcom,smem", sc->sc_smem_id,
162 	    sizeof(sc->sc_smem_id)) != sizeof(sc->sc_smem_id)) {
163 		printf(": can't get smem property \n");
164 		return;
165 	}
166 
167 	printf("\n");
168 
169 	config_defer(self, qcsmptp_deferred);
170 }
171 
172 void
173 qcsmptp_deferred(struct device *self)
174 {
175 	struct qcsmptp_softc *sc = (struct qcsmptp_softc *)self;
176 	struct qcsmptp_entry *e;
177 	int node, size;
178 
179 	sc->sc_mc = mbox_channel_idx(sc->sc_node, 0, NULL);
180 	if (sc->sc_mc == NULL) {
181 		printf(": can't get mailbox\n");
182 		return;
183 	}
184 
185 	if (qcsmem_alloc(sc->sc_remote_pid, sc->sc_smem_id[1],
186 	    sizeof(*sc->sc_out)) != 0) {
187 		printf(": can't alloc smp2p item\n");
188 		return;
189 	}
190 
191 	sc->sc_out = qcsmem_get(sc->sc_remote_pid, sc->sc_smem_id[1], NULL);
192 	if (sc->sc_out == NULL) {
193 		printf(": can't get smp2p item\n");
194 		return;
195 	}
196 
197 	memset(sc->sc_out, 0, sizeof(*sc->sc_out));
198 	sc->sc_out->magic = SMP2P_MAGIC;
199 	sc->sc_out->local_pid = sc->sc_local_pid;
200 	sc->sc_out->remote_pid = sc->sc_remote_pid;
201 	sc->sc_out->total_entries = SMP2P_MAX_ENTRY;
202 	sc->sc_out->features = SMP2P_FEATURE_SSR_ACK;
203 	membar_sync();
204 	sc->sc_out->version = SMP2P_VERSION;
205 	mbox_send(sc->sc_mc, NULL, 0);
206 
207 	for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) {
208 		size = OF_getproplen(node, "qcom,entry-name");
209 		if (size <= 0)
210 			continue;
211 		e = malloc(sizeof(*e), M_DEVBUF, M_WAITOK | M_ZERO);
212 		e->e_namelen = size;
213 		e->e_name = malloc(e->e_namelen, M_DEVBUF, M_WAITOK);
214 		OF_getprop(node, "qcom,entry-name", e->e_name,
215 		    e->e_namelen);
216 		e->e_name[e->e_namelen - 1] = '\0';
217 
218 		if (OF_getproplen(node, "interrupt-controller") == 0) {
219 			struct qcsmptp_interrupt_controller *ic;
220 			ic = malloc(sizeof(*ic), M_DEVBUF, M_WAITOK | M_ZERO);
221 			TAILQ_INIT(&ic->ic_intrq);
222 			ic->ic_sc = sc;
223 			ic->ic_ic.ic_node = node;
224 			ic->ic_ic.ic_cookie = ic;
225 			ic->ic_ic.ic_establish = qcsmptp_intr_establish;
226 			ic->ic_ic.ic_disestablish = qcsmptp_intr_disestablish;
227 			ic->ic_ic.ic_enable = qcsmptp_intr_enable;
228 			ic->ic_ic.ic_disable = qcsmptp_intr_disable;
229 			ic->ic_ic.ic_barrier = qcsmptp_intr_barrier;
230 			fdt_intr_register(&ic->ic_ic);
231 			e->e_ic = ic;
232 			TAILQ_INSERT_TAIL(&sc->sc_inboundq, e, e_q);
233 		} else {
234 			if (sc->sc_out->valid_entries >= SMP2P_MAX_ENTRY)
235 				continue;
236 
237 			strlcpy(sc->sc_out->entries[sc->sc_out->valid_entries].name,
238 			    e->e_name, SMP2P_MAX_ENTRY_NAME);
239 			e->e_value = &sc->sc_out->entries[sc->sc_out->valid_entries].value;
240 			sc->sc_out->valid_entries++;
241 			TAILQ_INSERT_TAIL(&sc->sc_outboundq, e, e_q);
242 
243 			/* TODO: provide as smem state */
244 		}
245 	}
246 }
247 
248 int
249 qcsmptp_intr(void *arg)
250 {
251 	struct qcsmptp_softc *sc = arg;
252 	struct qcsmptp_entry *e;
253 	struct qcsmptp_intrhand *ih;
254 	uint32_t changed, val;
255 	int do_ack = 0, i;
256 
257 	/* Inbound item exists as soon as remoteproc is up. */
258 	if (sc->sc_in == NULL)
259 		sc->sc_in = qcsmem_get(sc->sc_remote_pid,
260 		    sc->sc_smem_id[0], NULL);
261 	if (sc->sc_in == NULL) {
262 		printf("%s: can't get smp2p item\n", sc->sc_dev.dv_xname);
263 		return 1;
264 	}
265 
266 	/* Do initial feature negotiation if inbound is new. */
267 	if (!sc->sc_negotiated) {
268 		if (sc->sc_in->version != sc->sc_out->version)
269 			return 1;
270 		sc->sc_out->features &= sc->sc_in->features;
271 		if (sc->sc_out->features & SMP2P_FEATURE_SSR_ACK)
272 			sc->sc_ssr_ack_enabled = 1;
273 		sc->sc_negotiated = 1;
274 	}
275 	if (!sc->sc_negotiated)
276 		return 1;
277 
278 	/* Use ACK mechanism if negotiated. */
279 	if (sc->sc_ssr_ack_enabled &&
280 	    !!(sc->sc_in->flags & SMP2P_FLAGS_RESTART_DONE) != sc->sc_ssr_ack)
281 		do_ack = 1;
282 
283 	/* Catch up on new inbound entries that got added in the meantime. */
284 	for (i = sc->sc_valid_entries; i < sc->sc_in->valid_entries; i++) {
285 		TAILQ_FOREACH(e, &sc->sc_inboundq, e_q) {
286 			if (strncmp(sc->sc_in->entries[i].name, e->e_name,
287 			    SMP2P_MAX_ENTRY_NAME) != 0)
288 				continue;
289 			e->e_value = &sc->sc_in->entries[i].value;
290 		}
291 	}
292 	sc->sc_valid_entries = i;
293 
294 	/* For each inbound "interrupt controller". */
295 	TAILQ_FOREACH(e, &sc->sc_inboundq, e_q) {
296 		if (e->e_value == NULL)
297 			continue;
298 		val = *e->e_value;
299 		if (val == e->e_last_value)
300 			continue;
301 		changed = val ^ e->e_last_value;
302 		e->e_last_value = val;
303 		TAILQ_FOREACH(ih, &e->e_ic->ic_intrq, ih_q) {
304 			if (!ih->ih_enabled)
305 				continue;
306 			if ((changed & (1 << ih->ih_pin)) == 0)
307 				continue;
308 			ih->ih_func(ih->ih_arg);
309 		}
310 	}
311 
312 	if (do_ack) {
313 		sc->sc_ssr_ack = !sc->sc_ssr_ack;
314 		if (sc->sc_ssr_ack)
315 			sc->sc_out->flags |= SMP2P_FLAGS_RESTART_ACK;
316 		else
317 			sc->sc_out->flags &= ~SMP2P_FLAGS_RESTART_ACK;
318 		membar_sync();
319 		mbox_send(sc->sc_mc, NULL, 0);
320 	}
321 
322 	return 1;
323 }
324 
325 void *
326 qcsmptp_intr_establish(void *cookie, int *cells, int ipl,
327     struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
328 {
329 	struct qcsmptp_interrupt_controller *ic = cookie;
330 	struct qcsmptp_softc *sc = ic->ic_sc;
331 	struct qcsmptp_intrhand *ih;
332 
333 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK | M_ZERO);
334 	ih->ih_func = func;
335 	ih->ih_arg = arg;
336 	ih->ih_ic = ic;
337 	ih->ih_pin = cells[0];
338 	TAILQ_INSERT_TAIL(&ic->ic_intrq, ih, ih_q);
339 
340 	qcsmptp_intr_enable(ih);
341 
342 	if (ipl & IPL_WAKEUP)
343 		intr_set_wakeup(sc->sc_ih);
344 
345 	return ih;
346 }
347 
348 void
349 qcsmptp_intr_disestablish(void *cookie)
350 {
351 	struct qcsmptp_intrhand *ih = cookie;
352 	struct qcsmptp_interrupt_controller *ic = ih->ih_ic;
353 
354 	qcsmptp_intr_disable(ih);
355 
356 	TAILQ_REMOVE(&ic->ic_intrq, ih, ih_q);
357 	free(ih, M_DEVBUF, sizeof(*ih));
358 }
359 
360 void
361 qcsmptp_intr_enable(void *cookie)
362 {
363 	struct qcsmptp_intrhand *ih = cookie;
364 
365 	ih->ih_enabled = 1;
366 }
367 
368 void
369 qcsmptp_intr_disable(void *cookie)
370 {
371 	struct qcsmptp_intrhand *ih = cookie;
372 
373 	ih->ih_enabled = 0;
374 }
375 
376 void
377 qcsmptp_intr_barrier(void *cookie)
378 {
379 	struct qcsmptp_intrhand *ih = cookie;
380 	struct qcsmptp_interrupt_controller *ic = ih->ih_ic;
381 	struct qcsmptp_softc *sc = ic->ic_sc;
382 
383 	intr_barrier(sc->sc_ih);
384 }
385