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