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
26static 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
32typedef 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
43typedef 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
55static int  gemini_ipm_match(device_t, cfdata_t, void *);
56static void gemini_ipm_attach(device_t, device_t, void *);
57static int  gemini_ipm_intr(void *);
58static void gemini_ipm_count_txdone(gemini_ipm_softc_t *);
59
60
61CFATTACH_DECL_NEW(geminiipm, sizeof(struct gemini_ipm_softc),
62	gemini_ipm_match, gemini_ipm_attach, NULL, NULL);
63
64gemini_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 */
71static inline void
72gemini_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 */
88static inline void
89gemini_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
102static int
103gemini_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
113static void
114gemini_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
158void *
159gemini_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
200void
201gemini_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
218static inline int
219gemini_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
254static int
255gemini_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
266int
267gemini_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
295static void *
296gemini_ba_to_va(bus_addr_t ba)
297{
298	return (void *)(GEMINI_ALLMEM_VBASE + ba);
299}
300
301void
302gemini_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
314static void
315gemini_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
333void gemini_ipm_stats_print(void);
334void
335gemini_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