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