xref: /netbsd-src/sys/arch/arm/gemini/gemini_ipm.c (revision 7d62b00eb9ad855ffcd7da46b41e23feb5476fac)
1 #include "opt_gemini.h"
2 #if !defined(GEMINI_MASTER)  && !defined(GEMINI_SLAVE)
3 # error IPI needs GEMINI_MASTER or GEMINI_SLAVE
4 #endif
5 #include "locators.h"
6 #include "gpn.h"
7 
8 #include <sys/cdefs.h>
9 
10 __KERNEL_RCSID(0, "$NetBSD: gemini_ipm.c,v 1.6 2022/09/27 06:36:41 skrll Exp $");
11 
12 #include <sys/param.h>
13 #include <sys/systm.h>
14 #include <sys/device.h>
15 #include <sys/intr.h>
16 #include <arm/cpufunc.h>
17 #include <arch/arm/gemini/gemini_obiovar.h>
18 #include <arch/arm/gemini/gemini_ipivar.h>
19 #include <arch/arm/gemini/gemini_ipm.h>
20 #include <arch/arm/gemini/gemini_ipmvar.h>
21 #include <evbarm/gemini/gemini.h>
22 
23 // #define IPMDEBUG
24 #if defined IPMDEBUG
25 static int ipmdebug;
26 # define DPRINTFN(n, x)	do { if ((n) >= ipmdebug) printf x ; } while (1)
27 #else
28 # define DPRINTFN(n, x)
29 #endif
30 
31 typedef struct dispatch_entry {
32 	unsigned int ipl;
33 	size_t quota;
34 	void *arg;
35 	void (*consume)(void *, const void *);
36 	void (*counter)(void *, size_t);
37 #ifdef NOTYET
38 	void *sih;		/* softint handle */
39 #endif
40 } ipm_dispatch_entry_t;
41 
42 typedef struct gemini_ipm_softc {
43 	device_t              sc_dev;
44 	void		     *sc_ih;
45 	ipm_queue_t	     *sc_rxqueue;
46 	ipm_queue_t	     *sc_txqueue;
47 	size_t		      sc_txqavail;	/* quota available */
48 	unsigned long long    sc_rxcount;
49 	unsigned long long    sc_txcount;
50 	ipm_dispatch_entry_t  sc_dispatch_tab[256];
51 } gemini_ipm_softc_t;
52 
53 
54 static int  gemini_ipm_match(device_t, cfdata_t, void *);
55 static void gemini_ipm_attach(device_t, device_t, void *);
56 static int  gemini_ipm_intr(void *);
57 static void gemini_ipm_count_txdone(gemini_ipm_softc_t *);
58 
59 
60 CFATTACH_DECL_NEW(geminiipm, sizeof(struct gemini_ipm_softc),
61 	gemini_ipm_match, gemini_ipm_attach, NULL, NULL);
62 
63 gemini_ipm_softc_t *gemini_ipm_sc = NULL;
64 
65 
66 /*
67  * copy from shared queue to private copy
68  * SW coherency would go here if desc_src were in cached mem
69  */
70 static inline void
71 gemini_ipm_desc_read(ipm_desc_t *desc_dst, const ipm_desc_t *desc_src)
72 {
73 	extern void gpn_print_gd(const void *);	/* XXX DEBUG */
74 	DPRINTFN(2, ("%s: %p %p\n", __FUNCTION__, desc_dst, desc_src));
75 #ifdef IPMDEBUG
76 	if (ipmdebug >= 3)
77 		gpn_print_gd(desc_src);
78 #endif
79 	*desc_dst = *desc_src;
80 	KASSERT(desc_dst->tag != IPM_TAG_NONE);
81 }
82 
83 /*
84  * copy from private copy to shared queue
85  * SW coherency would go here if desc_dst were in cached mem
86  */
87 static inline void
88 gemini_ipm_desc_write(ipm_desc_t *desc_dst, const ipm_desc_t *desc_src)
89 {
90 	extern void gpn_print_gd(const void *);	/* XXX DEBUG */
91 	DPRINTFN(2, ("%s: %p %p\n", __FUNCTION__, desc_dst, desc_src));
92 #ifdef IPMDEBUG
93 	if (ipmdebug >= 3)
94 		gpn_print_gd(desc_src);
95 #endif
96 	KASSERT(desc_src->tag != IPM_TAG_NONE);
97 	*desc_dst = *desc_src;
98 }
99 
100 
101 static int
102 gemini_ipm_match(device_t parent, cfdata_t cf, void *aux)
103 {
104         char *name = aux;
105 
106         if (strcmp(name, "geminiipm") != 0)
107 		return 0;
108 
109         return 1;
110 }
111 
112 static void
113 gemini_ipm_attach(device_t parent, device_t self, void *aux)
114 {
115         gemini_ipm_softc_t *sc = device_private(self);
116 	void *ih;
117 
118 	sc->sc_dev = self;
119 	ih = ipi_intr_establish(gemini_ipm_intr, sc);
120 	if (ih == NULL)
121 		panic("%s: Cannot establish IPI interrupt\n",
122 			device_xname(self));
123 	sc->sc_ih = ih;
124 	memset(&sc->sc_dispatch_tab, 0, sizeof(sc->sc_dispatch_tab));
125 
126 
127 	/*
128 	 * queues are flipped tx/rx for master/slave
129 	 */
130 	KASSERT(GEMINI_IPMQ_SIZE == (2 * sizeof(ipm_queue_t)));
131 #if defined(GEMINI_MASTER)
132 	sc->sc_rxqueue = (ipm_queue_t *)GEMINI_IPMQ_VBASE;
133 	sc->sc_txqueue = sc->sc_rxqueue + 1;
134 	memset(sc->sc_rxqueue, 0, sizeof(ipm_queue_t));
135 	memset(sc->sc_txqueue, 0, sizeof(ipm_queue_t));
136 #elif defined(GEMINI_SLAVE)
137 	sc->sc_txqueue = (ipm_queue_t *)GEMINI_IPMQ_VBASE;
138 	sc->sc_rxqueue = sc->sc_txqueue + 1;
139 #else
140 # error one of GEMINI_MASTER or GEMINI_SLAVE must be defined
141 #endif
142 	sc->sc_txqavail = NIPMDESC;
143 
144 	sc->sc_rxcount = 0LL;
145 	sc->sc_txcount = 0LL;
146 
147 	gemini_ipm_sc = sc;
148 
149 	aprint_normal("\n");
150 	aprint_naive("\n");
151 
152 #if NGPN > 0
153 	config_found(self, __UNCONST("gpn"), NULL, CFARGS_NONE);
154 #endif
155 }
156 
157 void *
158 gemini_ipm_register(uint8_t tag, unsigned int ipl, size_t quota,
159         void (*consume)(void *, const void *),
160         void (*counter)(void *, size_t),
161 	void *arg)
162 {
163 	gemini_ipm_softc_t *sc = gemini_ipm_sc;
164 	ipm_dispatch_entry_t *disp;
165 	void *ipmh = NULL;
166 	int psw;
167 
168 	DPRINTFN(1, ("%s:%d: %d %d %ld %p %p %p\n", __FUNCTION__, __LINE__,
169 		tag, ipl, quota, consume, counter, arg));
170 	if (sc == NULL)
171 		return NULL;		/* not attached yet */
172 
173 	if (tag == 0)
174 		return NULL;		/* tag #0 is reserved */
175 
176 	psw = disable_interrupts(I32_bit);
177 	disp = &sc->sc_dispatch_tab[tag];
178 	if (disp->consume == 0) {
179 		if (sc->sc_txqavail >= quota) {
180 			sc->sc_txqavail -= quota;
181 			disp->ipl = ipl;
182 			disp->consume = consume;
183 			disp->counter = counter;
184 			disp->arg = arg;
185 #ifdef NOTYET
186 			if (ipl > SOFTINT_LVLMASK)
187 				panic("%s: bad level %d",
188 					device_xname(sc->sc_dev), ipl);
189 			disp->sih = softint_establish(ipl, consume, arg);
190 #endif
191 			ipmh = disp;
192 		}
193 	}
194 	restore_interrupts(psw);
195 
196 	return ipmh;
197 }
198 
199 void
200 gemini_ipm_deregister(void *ipmh)
201 {
202 	gemini_ipm_softc_t *sc = gemini_ipm_sc;
203 	ipm_dispatch_entry_t *disp = ipmh;
204 	int psw;
205 
206 	if (sc == NULL)
207 		return;
208 
209 	psw = disable_interrupts(I32_bit);
210 	memset(disp, 0, sizeof(*disp));
211 #ifdef NOTYET
212 	softint_disestablish(sc->sih);
213 #endif
214 	restore_interrupts(psw);
215 }
216 
217 static inline int
218 gemini_ipm_dispatch(gemini_ipm_softc_t *sc)
219 {
220 	ipm_dispatch_entry_t *disp;
221 	ipm_desc_t desc;
222 	ipmqindex_t ix_read;
223 	ipmqindex_t ix_write;
224 	int rv = 0;
225 
226 	ix_read = sc->sc_rxqueue->ix_read;
227 	ix_write = sc->sc_rxqueue->ix_write;
228 
229 	if (! ipmqisempty(ix_read, ix_write)) {
230 		rv = 1;
231 		do {
232 			gemini_ipm_desc_read(&desc,
233 				&sc->sc_rxqueue->ipm_desc[ix_read]);
234 			ix_read = ipmqnext(ix_read);
235 			KASSERT(desc.tag != IPM_TAG_NONE);
236 			disp = &sc->sc_dispatch_tab[desc.tag];
237 #ifdef NOTYET
238 			softint_schedule(disp->sih);
239 #else
240 			(*disp->consume)(disp->arg, &desc);
241 #endif
242 			ix_write = sc->sc_rxqueue->ix_write;
243 			sc->sc_rxqueue->ix_read = ix_read;
244 			sc->sc_rxcount++;
245 		} while (! ipmqisempty(ix_read, ix_write));
246 	} else {
247 		DPRINTFN(1, ("%s: ipmqisempty %d %d\n",
248 			__FUNCTION__, ix_read, ix_write));
249 	}
250 	return rv;
251 }
252 
253 static int
254 gemini_ipm_intr(void *arg)
255 {
256 	gemini_ipm_softc_t *sc = arg;
257 	int rv;
258 
259 	rv = gemini_ipm_dispatch(sc);
260 	gemini_ipm_count_txdone(sc);
261 
262 	return rv;
263 }
264 
265 int
266 gemini_ipm_produce(const void *adescp, size_t ndesc)
267 {
268 	const ipm_desc_t *descp = adescp;
269 	gemini_ipm_softc_t *sc = gemini_ipm_sc;
270 	ipmqindex_t ix_read;
271 	ipmqindex_t ix_write;
272 
273 	KASSERT(ndesc == 1);			/* XXX TMP */
274 
275 	DPRINTFN(2, ("%s:%d: %p %ld, tag %d\n",
276 		__FUNCTION__, __LINE__, descp, ndesc, descp->tag));
277 	ix_read = sc->sc_txqueue->ix_read;
278 	ix_write = sc->sc_txqueue->ix_write;
279 	if (ipmqisfull(ix_read, ix_write)) {
280 		/* we expect this to "never" happen; check your quotas */
281 		panic("%s: queue full\n", device_xname(sc->sc_dev));
282 	}
283 	gemini_ipm_desc_write(&sc->sc_txqueue->ipm_desc[ix_write], descp);
284 	sc->sc_txqueue->ix_write = ipmqnext(ix_write);
285 	sc->sc_txcount++;
286 
287 	ipi_send();
288 
289 	gemini_ipm_count_txdone(sc);
290 
291 	return 0;
292 }
293 
294 static void *
295 gemini_ba_to_va(bus_addr_t ba)
296 {
297 	return (void *)(GEMINI_ALLMEM_VBASE + ba);
298 }
299 
300 void
301 gemini_ipm_copyin(void *dst, bus_addr_t ba, size_t len)
302 {
303 	void *src;
304 
305 	DPRINTFN(2, ("%s:%d: %p %#lx %ld\n",
306 		__FUNCTION__, __LINE__, dst, ba, len));
307 	src = gemini_ba_to_va(ba);
308 	memcpy(dst, src, len);
309 	cpu_dcache_inv_range((vaddr_t)src, len);
310 }
311 
312 
313 static void
314 gemini_ipm_count_txdone(gemini_ipm_softc_t *sc)
315 {
316 	ipmqindex_t count = 0;		/* XXX must count per tag */
317 	ipm_dispatch_entry_t *disp;
318 	ipmqindex_t ixr = sc->sc_txqueue->ix_read;
319 	uint8_t tag = IPM_TAG_GPN;
320 	static ipmqindex_t oixr = 0;
321 
322 	while (oixr != ixr) {
323 		oixr = ipmqnext(oixr);
324 		count++;
325 	}
326 	if (count != 0) {
327 		disp = &sc->sc_dispatch_tab[tag];
328 		(*disp->counter)(disp->arg, count);
329 	}
330 }
331 
332 void gemini_ipm_stats_print(void);
333 void
334 gemini_ipm_stats_print(void)
335 {
336 	gemini_ipm_softc_t *sc = gemini_ipm_sc;
337 
338 	printf("rxcount %lld, txcount %lld\n", sc->sc_rxcount, sc->sc_txcount);
339 }
340