summaryrefslogtreecommitdiff
path: root/sys/arch/arm/gemini/gemini_ipm.c
blob: 21dcf28a149ab5da084288f7f263b2a4bb3e106e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
#include "opt_gemini.h"
#if !defined(GEMINI_MASTER)  && !defined(GEMINI_SLAVE)
# error IPI needs GEMINI_MASTER or GEMINI_SLAVE
#endif
#include "locators.h"
#include "gpn.h"

#include <sys/cdefs.h>

__KERNEL_RCSID(0, "$NetBSD: gemini_ipm.c,v 1.7 2023/05/26 20:50:21 andvar Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/intr.h>
#include <arm/cpufunc.h>
#include <arm/arm32/pte.h>
#include <arch/arm/gemini/gemini_obiovar.h>
#include <arch/arm/gemini/gemini_ipivar.h>
#include <arch/arm/gemini/gemini_ipm.h>
#include <arch/arm/gemini/gemini_ipmvar.h>
#include <evbarm/gemini/gemini.h>

// #define IPMDEBUG
#if defined IPMDEBUG
static int ipmdebug;
# define DPRINTFN(n, x)	do { if ((n) >= ipmdebug) printf x ; } while (1)
#else
# define DPRINTFN(n, x)
#endif

typedef struct dispatch_entry {
	unsigned int ipl;
	size_t quota;
	void *arg; 
	void (*consume)(void *, const void *);
	void (*counter)(void *, size_t);
#ifdef NOTYET
	void *sih;		/* softint handle */
#endif
} ipm_dispatch_entry_t;

typedef struct gemini_ipm_softc {
	device_t              sc_dev;
	void		     *sc_ih;
	ipm_queue_t	     *sc_rxqueue;
	ipm_queue_t	     *sc_txqueue;
	size_t		      sc_txqavail;	/* quota available */
	unsigned long long    sc_rxcount;
	unsigned long long    sc_txcount;
	ipm_dispatch_entry_t  sc_dispatch_tab[256];
} gemini_ipm_softc_t;


static int  gemini_ipm_match(device_t, cfdata_t, void *);
static void gemini_ipm_attach(device_t, device_t, void *);
static int  gemini_ipm_intr(void *);
static void gemini_ipm_count_txdone(gemini_ipm_softc_t *);


CFATTACH_DECL_NEW(geminiipm, sizeof(struct gemini_ipm_softc),
	gemini_ipm_match, gemini_ipm_attach, NULL, NULL);

gemini_ipm_softc_t *gemini_ipm_sc = NULL;


/*
 * copy from shared queue to private copy
 * SW coherency would go here if desc_src were in cached mem
 */
static inline void
gemini_ipm_desc_read(ipm_desc_t *desc_dst, const ipm_desc_t *desc_src)
{
	extern void gpn_print_gd(const void *);	/* XXX DEBUG */
	DPRINTFN(2, ("%s: %p %p\n", __FUNCTION__, desc_dst, desc_src));
#ifdef IPMDEBUG
	if (ipmdebug >= 3)
		gpn_print_gd(desc_src);
#endif
	*desc_dst = *desc_src;
	KASSERT(desc_dst->tag != IPM_TAG_NONE);
}

/*
 * copy from private copy to shared queue
 * SW coherency would go here if desc_dst were in cached mem
 */
static inline void
gemini_ipm_desc_write(ipm_desc_t *desc_dst, const ipm_desc_t *desc_src)
{
	extern void gpn_print_gd(const void *);	/* XXX DEBUG */
	DPRINTFN(2, ("%s: %p %p\n", __FUNCTION__, desc_dst, desc_src));
#ifdef IPMDEBUG
	if (ipmdebug >= 3)
		gpn_print_gd(desc_src);
#endif
	KASSERT(desc_src->tag != IPM_TAG_NONE);
	*desc_dst = *desc_src;
}


static int
gemini_ipm_match(device_t parent, cfdata_t cf, void *aux)
{
        char *name = aux;

        if (strcmp(name, "geminiipm") != 0)
		return 0;

        return 1;
}

static void
gemini_ipm_attach(device_t parent, device_t self, void *aux)
{
        gemini_ipm_softc_t *sc = device_private(self);
	void *ih;

	sc->sc_dev = self;
	ih = ipi_intr_establish(gemini_ipm_intr, sc);
	if (ih == NULL)
		panic("%s: Cannot establish IPI interrupt\n",
			device_xname(self));
	sc->sc_ih = ih;
	memset(&sc->sc_dispatch_tab, 0, sizeof(sc->sc_dispatch_tab));


	/*
	 * queues are flipped tx/rx for master/slave
	 */
	KASSERT(GEMINI_IPMQ_SIZE == (2 * sizeof(ipm_queue_t)));
#if defined(GEMINI_MASTER)
	sc->sc_rxqueue = (ipm_queue_t *)GEMINI_IPMQ_VBASE;
	sc->sc_txqueue = sc->sc_rxqueue + 1;
	memset(sc->sc_rxqueue, 0, sizeof(ipm_queue_t));
	memset(sc->sc_txqueue, 0, sizeof(ipm_queue_t));
#elif defined(GEMINI_SLAVE)
	sc->sc_txqueue = (ipm_queue_t *)GEMINI_IPMQ_VBASE;
	sc->sc_rxqueue = sc->sc_txqueue + 1;
#else
# error one of GEMINI_MASTER or GEMINI_SLAVE must be defined
#endif
	sc->sc_txqavail = NIPMDESC;

	sc->sc_rxcount = 0LL;
	sc->sc_txcount = 0LL;

	gemini_ipm_sc = sc;

	aprint_normal("\n");
	aprint_naive("\n");

#if NGPN > 0
	config_found(self, __UNCONST("gpn"), NULL, CFARGS_NONE);
#endif
}

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)
{
	gemini_ipm_softc_t *sc = gemini_ipm_sc;
	ipm_dispatch_entry_t *disp;
	void *ipmh = NULL;
	int psw;

	DPRINTFN(1, ("%s:%d: %d %d %ld %p %p %p\n", __FUNCTION__, __LINE__,
		tag, ipl, quota, consume, counter, arg));
	if (sc == NULL)
		return NULL;		/* not attached yet */

	if (tag == 0)
		return NULL;		/* tag #0 is reserved */

	psw = disable_interrupts(I32_bit);
	disp = &sc->sc_dispatch_tab[tag];
	if (disp->consume == 0) {
		if (sc->sc_txqavail >= quota) {
			sc->sc_txqavail -= quota;
			disp->ipl = ipl;
			disp->consume = consume;
			disp->counter = counter;
			disp->arg = arg;
#ifdef NOTYET
			if (ipl > SOFTINT_LVLMASK)
				panic("%s: bad level %d",
					device_xname(sc->sc_dev), ipl);
			disp->sih = softint_establish(ipl, consume, arg);
#endif
			ipmh = disp;
		}
	}
	restore_interrupts(psw);

	return ipmh;
}

void
gemini_ipm_deregister(void *ipmh)
{
	gemini_ipm_softc_t *sc = gemini_ipm_sc;
	ipm_dispatch_entry_t *disp = ipmh;
	int psw;

	if (sc == NULL)
		return;

	psw = disable_interrupts(I32_bit);
	memset(disp, 0, sizeof(*disp));
#ifdef NOTYET
	softint_disestablish(sc->sih);
#endif
	restore_interrupts(psw);
}

static inline int
gemini_ipm_dispatch(gemini_ipm_softc_t *sc)
{
	ipm_dispatch_entry_t *disp;
	ipm_desc_t desc;
	ipmqindex_t ix_read;
	ipmqindex_t ix_write;
	int rv = 0;

	ix_read = sc->sc_rxqueue->ix_read;
	ix_write = sc->sc_rxqueue->ix_write;

	if (! ipmqisempty(ix_read, ix_write)) {
		rv = 1;
		do {
			gemini_ipm_desc_read(&desc,
				&sc->sc_rxqueue->ipm_desc[ix_read]);
			ix_read = ipmqnext(ix_read);
			KASSERT(desc.tag != IPM_TAG_NONE);
			disp = &sc->sc_dispatch_tab[desc.tag];
#ifdef NOTYET
			softint_schedule(disp->sih);
#else
			(*disp->consume)(disp->arg, &desc);
#endif
			ix_write = sc->sc_rxqueue->ix_write;
			sc->sc_rxqueue->ix_read = ix_read;
			sc->sc_rxcount++;
		} while (! ipmqisempty(ix_read, ix_write));
	} else {
		DPRINTFN(1, ("%s: ipmqisempty %d %d\n",
			__FUNCTION__, ix_read, ix_write));
	}
	return rv;
}

static int
gemini_ipm_intr(void *arg)
{
	gemini_ipm_softc_t *sc = arg;
	int rv;

	rv = gemini_ipm_dispatch(sc);
	gemini_ipm_count_txdone(sc); 

	return rv;
}

int
gemini_ipm_produce(const void *adescp, size_t ndesc)
{
	const ipm_desc_t *descp = adescp;
	gemini_ipm_softc_t *sc = gemini_ipm_sc;
	ipmqindex_t ix_read;
	ipmqindex_t ix_write;

	KASSERT(ndesc == 1);			/* XXX TMP */

	DPRINTFN(2, ("%s:%d: %p %ld, tag %d\n",
		__FUNCTION__, __LINE__, descp, ndesc, descp->tag));
	ix_read = sc->sc_txqueue->ix_read;
	ix_write = sc->sc_txqueue->ix_write;
	if (ipmqisfull(ix_read, ix_write)) {
		/* we expect this to "never" happen; check your quotas */
		panic("%s: queue full\n", device_xname(sc->sc_dev));
	}
	gemini_ipm_desc_write(&sc->sc_txqueue->ipm_desc[ix_write], descp);
	sc->sc_txqueue->ix_write = ipmqnext(ix_write);
	sc->sc_txcount++; 

	ipi_send(); 

	gemini_ipm_count_txdone(sc); 

	return 0;
} 

static void *
gemini_ba_to_va(bus_addr_t ba)
{
	return (void *)(GEMINI_ALLMEM_VBASE + ba);
}

void
gemini_ipm_copyin(void *dst, bus_addr_t ba, size_t len)
{
	void *src;

	DPRINTFN(2, ("%s:%d: %p %#lx %ld\n",
		__FUNCTION__, __LINE__, dst, ba, len));
	src = gemini_ba_to_va(ba);
	memcpy(dst, src, len);
	cpu_dcache_inv_range((vaddr_t)src, len);
}


static void
gemini_ipm_count_txdone(gemini_ipm_softc_t *sc)
{
	ipmqindex_t count = 0;		/* XXX must count per tag */
	ipm_dispatch_entry_t *disp;
	ipmqindex_t ixr = sc->sc_txqueue->ix_read;
	uint8_t tag = IPM_TAG_GPN;
	static ipmqindex_t oixr = 0;

	while (oixr != ixr) {
		oixr = ipmqnext(oixr);
		count++;
	}
	if (count != 0) {
		disp = &sc->sc_dispatch_tab[tag];
		(*disp->counter)(disp->arg, count);
	}
}

void gemini_ipm_stats_print(void);
void
gemini_ipm_stats_print(void)
{
	gemini_ipm_softc_t *sc = gemini_ipm_sc;

	printf("rxcount %lld, txcount %lld\n", sc->sc_rxcount, sc->sc_txcount);
}