xref: /netbsd-src/sys/arch/arm/gemini/gemini_ipm.c (revision 7e00fdd90e6eea0b6ca1e9669218a1d7de02ff86)
141bfa2c4Scliff #include "opt_gemini.h"
241bfa2c4Scliff #if !defined(GEMINI_MASTER)  && !defined(GEMINI_SLAVE)
341bfa2c4Scliff # error IPI needs GEMINI_MASTER or GEMINI_SLAVE
441bfa2c4Scliff #endif
541bfa2c4Scliff #include "locators.h"
641bfa2c4Scliff #include "gpn.h"
741bfa2c4Scliff 
841bfa2c4Scliff #include <sys/cdefs.h>
941bfa2c4Scliff 
10*7e00fdd9Sandvar __KERNEL_RCSID(0, "$NetBSD: gemini_ipm.c,v 1.7 2023/05/26 20:50:21 andvar Exp $");
1141bfa2c4Scliff 
1241bfa2c4Scliff #include <sys/param.h>
1341bfa2c4Scliff #include <sys/systm.h>
1441bfa2c4Scliff #include <sys/device.h>
1541bfa2c4Scliff #include <sys/intr.h>
1641bfa2c4Scliff #include <arm/cpufunc.h>
17*7e00fdd9Sandvar #include <arm/arm32/pte.h>
1841bfa2c4Scliff #include <arch/arm/gemini/gemini_obiovar.h>
1941bfa2c4Scliff #include <arch/arm/gemini/gemini_ipivar.h>
2041bfa2c4Scliff #include <arch/arm/gemini/gemini_ipm.h>
2141bfa2c4Scliff #include <arch/arm/gemini/gemini_ipmvar.h>
2241bfa2c4Scliff #include <evbarm/gemini/gemini.h>
2341bfa2c4Scliff 
2441bfa2c4Scliff // #define IPMDEBUG
2541bfa2c4Scliff #if defined IPMDEBUG
2641bfa2c4Scliff static int ipmdebug;
2741bfa2c4Scliff # define DPRINTFN(n, x)	do { if ((n) >= ipmdebug) printf x ; } while (1)
2841bfa2c4Scliff #else
2941bfa2c4Scliff # define DPRINTFN(n, x)
3041bfa2c4Scliff #endif
3141bfa2c4Scliff 
3241bfa2c4Scliff typedef struct dispatch_entry {
3341bfa2c4Scliff 	unsigned int ipl;
3441bfa2c4Scliff 	size_t quota;
3541bfa2c4Scliff 	void *arg;
3641bfa2c4Scliff 	void (*consume)(void *, const void *);
3741bfa2c4Scliff 	void (*counter)(void *, size_t);
3841bfa2c4Scliff #ifdef NOTYET
3941bfa2c4Scliff 	void *sih;		/* softint handle */
4041bfa2c4Scliff #endif
4141bfa2c4Scliff } ipm_dispatch_entry_t;
4241bfa2c4Scliff 
4341bfa2c4Scliff typedef struct gemini_ipm_softc {
4441bfa2c4Scliff 	device_t              sc_dev;
4541bfa2c4Scliff 	void		     *sc_ih;
4641bfa2c4Scliff 	ipm_queue_t	     *sc_rxqueue;
4741bfa2c4Scliff 	ipm_queue_t	     *sc_txqueue;
4841bfa2c4Scliff 	size_t		      sc_txqavail;	/* quota available */
4941bfa2c4Scliff 	unsigned long long    sc_rxcount;
5041bfa2c4Scliff 	unsigned long long    sc_txcount;
5141bfa2c4Scliff 	ipm_dispatch_entry_t  sc_dispatch_tab[256];
5241bfa2c4Scliff } gemini_ipm_softc_t;
5341bfa2c4Scliff 
5441bfa2c4Scliff 
55cbab9cadSchs static int  gemini_ipm_match(device_t, cfdata_t, void *);
56cbab9cadSchs static void gemini_ipm_attach(device_t, device_t, void *);
5741bfa2c4Scliff static int  gemini_ipm_intr(void *);
5841bfa2c4Scliff static void gemini_ipm_count_txdone(gemini_ipm_softc_t *);
5941bfa2c4Scliff 
6041bfa2c4Scliff 
6141bfa2c4Scliff CFATTACH_DECL_NEW(geminiipm, sizeof(struct gemini_ipm_softc),
6241bfa2c4Scliff 	gemini_ipm_match, gemini_ipm_attach, NULL, NULL);
6341bfa2c4Scliff 
6441bfa2c4Scliff gemini_ipm_softc_t *gemini_ipm_sc = NULL;
6541bfa2c4Scliff 
6641bfa2c4Scliff 
6741bfa2c4Scliff /*
6841bfa2c4Scliff  * copy from shared queue to private copy
6941bfa2c4Scliff  * SW coherency would go here if desc_src were in cached mem
7041bfa2c4Scliff  */
7141bfa2c4Scliff static inline void
gemini_ipm_desc_read(ipm_desc_t * desc_dst,const ipm_desc_t * desc_src)7241bfa2c4Scliff gemini_ipm_desc_read(ipm_desc_t *desc_dst, const ipm_desc_t *desc_src)
7341bfa2c4Scliff {
7441bfa2c4Scliff 	extern void gpn_print_gd(const void *);	/* XXX DEBUG */
7541bfa2c4Scliff 	DPRINTFN(2, ("%s: %p %p\n", __FUNCTION__, desc_dst, desc_src));
7641bfa2c4Scliff #ifdef IPMDEBUG
7741bfa2c4Scliff 	if (ipmdebug >= 3)
7841bfa2c4Scliff 		gpn_print_gd(desc_src);
7941bfa2c4Scliff #endif
8041bfa2c4Scliff 	*desc_dst = *desc_src;
8141bfa2c4Scliff 	KASSERT(desc_dst->tag != IPM_TAG_NONE);
8241bfa2c4Scliff }
8341bfa2c4Scliff 
8441bfa2c4Scliff /*
8541bfa2c4Scliff  * copy from private copy to shared queue
8641bfa2c4Scliff  * SW coherency would go here if desc_dst were in cached mem
8741bfa2c4Scliff  */
8841bfa2c4Scliff static inline void
gemini_ipm_desc_write(ipm_desc_t * desc_dst,const ipm_desc_t * desc_src)8941bfa2c4Scliff gemini_ipm_desc_write(ipm_desc_t *desc_dst, const ipm_desc_t *desc_src)
9041bfa2c4Scliff {
9141bfa2c4Scliff 	extern void gpn_print_gd(const void *);	/* XXX DEBUG */
9241bfa2c4Scliff 	DPRINTFN(2, ("%s: %p %p\n", __FUNCTION__, desc_dst, desc_src));
9341bfa2c4Scliff #ifdef IPMDEBUG
9441bfa2c4Scliff 	if (ipmdebug >= 3)
9541bfa2c4Scliff 		gpn_print_gd(desc_src);
9641bfa2c4Scliff #endif
9741bfa2c4Scliff 	KASSERT(desc_src->tag != IPM_TAG_NONE);
9841bfa2c4Scliff 	*desc_dst = *desc_src;
9941bfa2c4Scliff }
10041bfa2c4Scliff 
10141bfa2c4Scliff 
10241bfa2c4Scliff static int
gemini_ipm_match(device_t parent,cfdata_t cf,void * aux)103cbab9cadSchs gemini_ipm_match(device_t parent, cfdata_t cf, void *aux)
10441bfa2c4Scliff {
10541bfa2c4Scliff         char *name = aux;
10641bfa2c4Scliff 
10741bfa2c4Scliff         if (strcmp(name, "geminiipm") != 0)
10841bfa2c4Scliff 		return 0;
10941bfa2c4Scliff 
11041bfa2c4Scliff         return 1;
11141bfa2c4Scliff }
11241bfa2c4Scliff 
11341bfa2c4Scliff static void
gemini_ipm_attach(device_t parent,device_t self,void * aux)114cbab9cadSchs gemini_ipm_attach(device_t parent, device_t self, void *aux)
11541bfa2c4Scliff {
11641bfa2c4Scliff         gemini_ipm_softc_t *sc = device_private(self);
11741bfa2c4Scliff 	void *ih;
11841bfa2c4Scliff 
11941bfa2c4Scliff 	sc->sc_dev = self;
12041bfa2c4Scliff 	ih = ipi_intr_establish(gemini_ipm_intr, sc);
12141bfa2c4Scliff 	if (ih == NULL)
12241bfa2c4Scliff 		panic("%s: Cannot establish IPI interrupt\n",
12341bfa2c4Scliff 			device_xname(self));
12441bfa2c4Scliff 	sc->sc_ih = ih;
12541bfa2c4Scliff 	memset(&sc->sc_dispatch_tab, 0, sizeof(sc->sc_dispatch_tab));
12641bfa2c4Scliff 
12741bfa2c4Scliff 
12841bfa2c4Scliff 	/*
12932a556f9Sandvar 	 * queues are flipped tx/rx for master/slave
13041bfa2c4Scliff 	 */
13141bfa2c4Scliff 	KASSERT(GEMINI_IPMQ_SIZE == (2 * sizeof(ipm_queue_t)));
13241bfa2c4Scliff #if defined(GEMINI_MASTER)
13341bfa2c4Scliff 	sc->sc_rxqueue = (ipm_queue_t *)GEMINI_IPMQ_VBASE;
13441bfa2c4Scliff 	sc->sc_txqueue = sc->sc_rxqueue + 1;
13541bfa2c4Scliff 	memset(sc->sc_rxqueue, 0, sizeof(ipm_queue_t));
13641bfa2c4Scliff 	memset(sc->sc_txqueue, 0, sizeof(ipm_queue_t));
13741bfa2c4Scliff #elif defined(GEMINI_SLAVE)
13841bfa2c4Scliff 	sc->sc_txqueue = (ipm_queue_t *)GEMINI_IPMQ_VBASE;
13941bfa2c4Scliff 	sc->sc_rxqueue = sc->sc_txqueue + 1;
14041bfa2c4Scliff #else
14141bfa2c4Scliff # error one of GEMINI_MASTER or GEMINI_SLAVE must be defined
14241bfa2c4Scliff #endif
14341bfa2c4Scliff 	sc->sc_txqavail = NIPMDESC;
14441bfa2c4Scliff 
14541bfa2c4Scliff 	sc->sc_rxcount = 0LL;
14641bfa2c4Scliff 	sc->sc_txcount = 0LL;
14741bfa2c4Scliff 
14841bfa2c4Scliff 	gemini_ipm_sc = sc;
14941bfa2c4Scliff 
15041bfa2c4Scliff 	aprint_normal("\n");
15141bfa2c4Scliff 	aprint_naive("\n");
15241bfa2c4Scliff 
15341bfa2c4Scliff #if NGPN > 0
154c7fb772bSthorpej 	config_found(self, __UNCONST("gpn"), NULL, CFARGS_NONE);
15541bfa2c4Scliff #endif
15641bfa2c4Scliff }
15741bfa2c4Scliff 
15841bfa2c4Scliff 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)15941bfa2c4Scliff gemini_ipm_register(uint8_t tag, unsigned int ipl, size_t quota,
16041bfa2c4Scliff         void (*consume)(void *, const void *),
16141bfa2c4Scliff         void (*counter)(void *, size_t),
16241bfa2c4Scliff 	void *arg)
16341bfa2c4Scliff {
16441bfa2c4Scliff 	gemini_ipm_softc_t *sc = gemini_ipm_sc;
16541bfa2c4Scliff 	ipm_dispatch_entry_t *disp;
16641bfa2c4Scliff 	void *ipmh = NULL;
16741bfa2c4Scliff 	int psw;
16841bfa2c4Scliff 
16941bfa2c4Scliff 	DPRINTFN(1, ("%s:%d: %d %d %ld %p %p %p\n", __FUNCTION__, __LINE__,
17041bfa2c4Scliff 		tag, ipl, quota, consume, counter, arg));
17141bfa2c4Scliff 	if (sc == NULL)
17241bfa2c4Scliff 		return NULL;		/* not attached yet */
17341bfa2c4Scliff 
17441bfa2c4Scliff 	if (tag == 0)
17541bfa2c4Scliff 		return NULL;		/* tag #0 is reserved */
17641bfa2c4Scliff 
17741bfa2c4Scliff 	psw = disable_interrupts(I32_bit);
17841bfa2c4Scliff 	disp = &sc->sc_dispatch_tab[tag];
17941bfa2c4Scliff 	if (disp->consume == 0) {
18041bfa2c4Scliff 		if (sc->sc_txqavail >= quota) {
18141bfa2c4Scliff 			sc->sc_txqavail -= quota;
18241bfa2c4Scliff 			disp->ipl = ipl;
18341bfa2c4Scliff 			disp->consume = consume;
18441bfa2c4Scliff 			disp->counter = counter;
18541bfa2c4Scliff 			disp->arg = arg;
18641bfa2c4Scliff #ifdef NOTYET
18741bfa2c4Scliff 			if (ipl > SOFTINT_LVLMASK)
18841bfa2c4Scliff 				panic("%s: bad level %d",
18941bfa2c4Scliff 					device_xname(sc->sc_dev), ipl);
19041bfa2c4Scliff 			disp->sih = softint_establish(ipl, consume, arg);
19141bfa2c4Scliff #endif
19241bfa2c4Scliff 			ipmh = disp;
19341bfa2c4Scliff 		}
19441bfa2c4Scliff 	}
19541bfa2c4Scliff 	restore_interrupts(psw);
19641bfa2c4Scliff 
19741bfa2c4Scliff 	return ipmh;
19841bfa2c4Scliff }
19941bfa2c4Scliff 
20041bfa2c4Scliff void
gemini_ipm_deregister(void * ipmh)20141bfa2c4Scliff gemini_ipm_deregister(void *ipmh)
20241bfa2c4Scliff {
20341bfa2c4Scliff 	gemini_ipm_softc_t *sc = gemini_ipm_sc;
20441bfa2c4Scliff 	ipm_dispatch_entry_t *disp = ipmh;
20541bfa2c4Scliff 	int psw;
20641bfa2c4Scliff 
20741bfa2c4Scliff 	if (sc == NULL)
20841bfa2c4Scliff 		return;
20941bfa2c4Scliff 
21041bfa2c4Scliff 	psw = disable_interrupts(I32_bit);
21141bfa2c4Scliff 	memset(disp, 0, sizeof(*disp));
21241bfa2c4Scliff #ifdef NOTYET
21341bfa2c4Scliff 	softint_disestablish(sc->sih);
21441bfa2c4Scliff #endif
21541bfa2c4Scliff 	restore_interrupts(psw);
21641bfa2c4Scliff }
21741bfa2c4Scliff 
21841bfa2c4Scliff static inline int
gemini_ipm_dispatch(gemini_ipm_softc_t * sc)21941bfa2c4Scliff gemini_ipm_dispatch(gemini_ipm_softc_t *sc)
22041bfa2c4Scliff {
22141bfa2c4Scliff 	ipm_dispatch_entry_t *disp;
22241bfa2c4Scliff 	ipm_desc_t desc;
22341bfa2c4Scliff 	ipmqindex_t ix_read;
22441bfa2c4Scliff 	ipmqindex_t ix_write;
22541bfa2c4Scliff 	int rv = 0;
22641bfa2c4Scliff 
22741bfa2c4Scliff 	ix_read = sc->sc_rxqueue->ix_read;
22841bfa2c4Scliff 	ix_write = sc->sc_rxqueue->ix_write;
22941bfa2c4Scliff 
23041bfa2c4Scliff 	if (! ipmqisempty(ix_read, ix_write)) {
23141bfa2c4Scliff 		rv = 1;
23241bfa2c4Scliff 		do {
23341bfa2c4Scliff 			gemini_ipm_desc_read(&desc,
23441bfa2c4Scliff 				&sc->sc_rxqueue->ipm_desc[ix_read]);
23541bfa2c4Scliff 			ix_read = ipmqnext(ix_read);
23641bfa2c4Scliff 			KASSERT(desc.tag != IPM_TAG_NONE);
23741bfa2c4Scliff 			disp = &sc->sc_dispatch_tab[desc.tag];
23841bfa2c4Scliff #ifdef NOTYET
23941bfa2c4Scliff 			softint_schedule(disp->sih);
24041bfa2c4Scliff #else
24141bfa2c4Scliff 			(*disp->consume)(disp->arg, &desc);
24241bfa2c4Scliff #endif
24341bfa2c4Scliff 			ix_write = sc->sc_rxqueue->ix_write;
24441bfa2c4Scliff 			sc->sc_rxqueue->ix_read = ix_read;
24541bfa2c4Scliff 			sc->sc_rxcount++;
24641bfa2c4Scliff 		} while (! ipmqisempty(ix_read, ix_write));
24741bfa2c4Scliff 	} else {
24841bfa2c4Scliff 		DPRINTFN(1, ("%s: ipmqisempty %d %d\n",
24941bfa2c4Scliff 			__FUNCTION__, ix_read, ix_write));
25041bfa2c4Scliff 	}
25141bfa2c4Scliff 	return rv;
25241bfa2c4Scliff }
25341bfa2c4Scliff 
25441bfa2c4Scliff static int
gemini_ipm_intr(void * arg)25541bfa2c4Scliff gemini_ipm_intr(void *arg)
25641bfa2c4Scliff {
25741bfa2c4Scliff 	gemini_ipm_softc_t *sc = arg;
25841bfa2c4Scliff 	int rv;
25941bfa2c4Scliff 
26041bfa2c4Scliff 	rv = gemini_ipm_dispatch(sc);
26141bfa2c4Scliff 	gemini_ipm_count_txdone(sc);
26241bfa2c4Scliff 
26341bfa2c4Scliff 	return rv;
26441bfa2c4Scliff }
26541bfa2c4Scliff 
26641bfa2c4Scliff int
gemini_ipm_produce(const void * adescp,size_t ndesc)26741bfa2c4Scliff gemini_ipm_produce(const void *adescp, size_t ndesc)
26841bfa2c4Scliff {
26941bfa2c4Scliff 	const ipm_desc_t *descp = adescp;
27041bfa2c4Scliff 	gemini_ipm_softc_t *sc = gemini_ipm_sc;
27141bfa2c4Scliff 	ipmqindex_t ix_read;
27241bfa2c4Scliff 	ipmqindex_t ix_write;
27341bfa2c4Scliff 
27441bfa2c4Scliff 	KASSERT(ndesc == 1);			/* XXX TMP */
27541bfa2c4Scliff 
27641bfa2c4Scliff 	DPRINTFN(2, ("%s:%d: %p %ld, tag %d\n",
27741bfa2c4Scliff 		__FUNCTION__, __LINE__, descp, ndesc, descp->tag));
27841bfa2c4Scliff 	ix_read = sc->sc_txqueue->ix_read;
27941bfa2c4Scliff 	ix_write = sc->sc_txqueue->ix_write;
28041bfa2c4Scliff 	if (ipmqisfull(ix_read, ix_write)) {
28141bfa2c4Scliff 		/* we expect this to "never" happen; check your quotas */
28241bfa2c4Scliff 		panic("%s: queue full\n", device_xname(sc->sc_dev));
28341bfa2c4Scliff 	}
28441bfa2c4Scliff 	gemini_ipm_desc_write(&sc->sc_txqueue->ipm_desc[ix_write], descp);
28541bfa2c4Scliff 	sc->sc_txqueue->ix_write = ipmqnext(ix_write);
28641bfa2c4Scliff 	sc->sc_txcount++;
28741bfa2c4Scliff 
28841bfa2c4Scliff 	ipi_send();
28941bfa2c4Scliff 
29041bfa2c4Scliff 	gemini_ipm_count_txdone(sc);
29141bfa2c4Scliff 
29241bfa2c4Scliff 	return 0;
29341bfa2c4Scliff }
29441bfa2c4Scliff 
29541bfa2c4Scliff static void *
gemini_ba_to_va(bus_addr_t ba)29641bfa2c4Scliff gemini_ba_to_va(bus_addr_t ba)
29741bfa2c4Scliff {
29841bfa2c4Scliff 	return (void *)(GEMINI_ALLMEM_VBASE + ba);
29941bfa2c4Scliff }
30041bfa2c4Scliff 
30141bfa2c4Scliff void
gemini_ipm_copyin(void * dst,bus_addr_t ba,size_t len)30241bfa2c4Scliff gemini_ipm_copyin(void *dst, bus_addr_t ba, size_t len)
30341bfa2c4Scliff {
30441bfa2c4Scliff 	void *src;
30541bfa2c4Scliff 
30641bfa2c4Scliff 	DPRINTFN(2, ("%s:%d: %p %#lx %ld\n",
30741bfa2c4Scliff 		__FUNCTION__, __LINE__, dst, ba, len));
30841bfa2c4Scliff 	src = gemini_ba_to_va(ba);
30941bfa2c4Scliff 	memcpy(dst, src, len);
31041bfa2c4Scliff 	cpu_dcache_inv_range((vaddr_t)src, len);
31141bfa2c4Scliff }
31241bfa2c4Scliff 
31341bfa2c4Scliff 
31441bfa2c4Scliff static void
gemini_ipm_count_txdone(gemini_ipm_softc_t * sc)31541bfa2c4Scliff gemini_ipm_count_txdone(gemini_ipm_softc_t *sc)
31641bfa2c4Scliff {
31741bfa2c4Scliff 	ipmqindex_t count = 0;		/* XXX must count per tag */
31841bfa2c4Scliff 	ipm_dispatch_entry_t *disp;
31941bfa2c4Scliff 	ipmqindex_t ixr = sc->sc_txqueue->ix_read;
32041bfa2c4Scliff 	uint8_t tag = IPM_TAG_GPN;
32141bfa2c4Scliff 	static ipmqindex_t oixr = 0;
32241bfa2c4Scliff 
32341bfa2c4Scliff 	while (oixr != ixr) {
32441bfa2c4Scliff 		oixr = ipmqnext(oixr);
32541bfa2c4Scliff 		count++;
32641bfa2c4Scliff 	}
32741bfa2c4Scliff 	if (count != 0) {
32841bfa2c4Scliff 		disp = &sc->sc_dispatch_tab[tag];
32941bfa2c4Scliff 		(*disp->counter)(disp->arg, count);
33041bfa2c4Scliff 	}
33141bfa2c4Scliff }
33241bfa2c4Scliff 
33341bfa2c4Scliff void gemini_ipm_stats_print(void);
33441bfa2c4Scliff void
gemini_ipm_stats_print(void)33541bfa2c4Scliff gemini_ipm_stats_print(void)
33641bfa2c4Scliff {
33741bfa2c4Scliff 	gemini_ipm_softc_t *sc = gemini_ipm_sc;
33841bfa2c4Scliff 
33941bfa2c4Scliff 	printf("rxcount %lld, txcount %lld\n", sc->sc_rxcount, sc->sc_txcount);
34041bfa2c4Scliff }
341