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 "geminiipm.h"
7
8#include <sys/cdefs.h>
9
10__KERNEL_RCSID(0, "$NetBSD: gemini_ipi.c,v 1.11 2021/08/07 16:18:44 thorpej Exp $");
11
12#include <sys/param.h>
13#include <sys/systm.h>
14#include <sys/device.h>
15#include <sys/intr.h>
16#include <sys/kmem.h>
17#include <arch/arm/gemini/gemini_obiovar.h>
18#include <arch/arm/gemini/gemini_ipivar.h>
19#include <arch/arm/gemini/gemini_reg.h>
20
21static int  gemini_ipi_match(device_t, cfdata_t, void *);
22static void gemini_ipi_attach(device_t, device_t, void *);
23static int  gemini_ipiintr(void *);
24
25CFATTACH_DECL_NEW(geminiipi, sizeof(struct gemini_ipi_softc),
26	gemini_ipi_match, gemini_ipi_attach, NULL, NULL);
27
28static gemini_ipi_softc_t *gemini_ipi_sc;
29
30
31static int
32gemini_ipi_match(device_t parent, cfdata_t cf, void *aux)
33{
34        struct obio_attach_args *obio = aux;
35
36        if (obio->obio_intr == LPCCF_INTR_DEFAULT)
37                panic("ipi must specify intr in config.");
38
39        return 1;
40}
41
42static void
43gemini_ipi_attach(device_t parent, device_t self, void *aux)
44{
45        gemini_ipi_softc_t *sc = device_private(self);
46        struct obio_attach_args *obio = aux;
47	bus_space_tag_t iot;
48	bus_space_handle_t ioh;
49	bus_size_t size;
50	bus_addr_t addr;
51	void *ih;
52
53	iot = obio->obio_iot;
54	addr = GEMINI_GLOBAL_BASE;
55	size = 4096;		/* XXX */
56
57	if (bus_space_map(iot, addr, size, 0, &ioh))
58                panic("%s: Cannot map registers", device_xname(self));
59
60	/*
61	 * NOTE: we are using IPL_NET, not IPL_HIGH use of IPI on this system
62	 * is (mainly) networking keep simple (for now) and force all IPIs
63	 * to same level so splnet() can block them as any other NIC.
64	 */
65#if 0
66	ih = intr_establish(obio->obio_intr, IPL_NET, IST_LEVEL_HIGH,
67		gemini_ipiintr, sc);
68#else
69	ih = intr_establish(obio->obio_intr, IPL_NET, IST_EDGE_RISING,
70		gemini_ipiintr, sc);
71#endif
72	if (ih == NULL)
73		panic("%s: Cannot establish interrupt %d\n",
74			device_xname(self), obio->obio_intr);
75
76	SIMPLEQ_INIT(&sc->sc_intrq);
77
78	sc->sc_iot = iot;
79	sc->sc_ioh = ioh;
80	sc->sc_addr = addr;
81	sc->sc_size = size;
82	sc->sc_intr = obio->obio_intr;
83	sc->sc_ih = ih;
84
85	gemini_ipi_sc = sc;
86
87	aprint_normal("\n");
88	aprint_naive("\n");
89
90#if NGEMINIIPM > 0
91	config_found(self, __UNCONST("geminiipm"), NULL, CFARGS_NONE);
92#endif
93}
94
95static inline int
96gemini_ipi_intrq_empty(gemini_ipi_softc_t *sc)
97{
98	return SIMPLEQ_EMPTY(&sc->sc_intrq);
99}
100
101static inline void *
102gemini_ipi_intrq_insert(gemini_ipi_softc_t *sc, int (*func)(void *), void *arg)
103{
104	gemini_ipi_intrq_t *iqp;
105
106        iqp = kmem_zalloc(sizeof(*iqp), KM_SLEEP);
107        iqp->iq_func = func;
108        iqp->iq_arg = arg;
109        SIMPLEQ_INSERT_TAIL(&sc->sc_intrq, iqp, iq_q);
110
111	return (void *)iqp;
112}
113
114static inline void
115gemini_ipi_intrq_remove(gemini_ipi_softc_t *sc, void *cookie)
116{
117	gemini_ipi_intrq_t *iqp;
118
119	SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q) {
120		if ((void *)iqp == cookie) {
121			SIMPLEQ_REMOVE(&sc->sc_intrq,
122				iqp, gemini_ipi_intrq, iq_q);
123			kmem_free(iqp, sizeof(*iqp));
124			return;
125		}
126	}
127}
128
129static inline int
130gemini_ipi_intrq_dispatch(gemini_ipi_softc_t *sc)
131{
132	gemini_ipi_intrq_t *iqp;
133	int rv = 0;
134
135	SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q)
136		rv |= (*iqp->iq_func)(iqp->iq_arg);
137
138	return (rv != 0);
139}
140
141
142void *
143ipi_intr_establish(int (*func)(void *), void *arg)
144{
145        gemini_ipi_softc_t *sc = gemini_ipi_sc;
146	void *ih;
147
148	if (sc == NULL)
149		return NULL;
150
151	ih = gemini_ipi_intrq_insert(sc, func, arg);
152	return ih;
153}
154
155void
156ipi_intr_disestablish(void *ih)
157{
158        gemini_ipi_softc_t *sc = gemini_ipi_sc;
159
160	if (sc == NULL)
161		panic("NULL gemini_ipi_sc");
162
163        gemini_ipi_intrq_remove(sc, ih);
164}
165
166int
167ipi_send(void)
168{
169        gemini_ipi_softc_t *sc = gemini_ipi_sc;
170	uint32_t r;
171	uint32_t bit;
172	bus_addr_t off;
173
174	if (sc == NULL)
175		return -1;
176
177#if defined(GEMINI_MASTER)
178	off = GEMINI_GLOBAL_CPU0;
179	bit = GLOBAL_CPU0_IPICPU1;
180#elif defined(GEMINI_SLAVE)
181	off = GEMINI_GLOBAL_CPU1;
182	bit = GLOBAL_CPU1_IPICPU0;
183#endif
184
185	r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
186	r |= bit;
187	bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, r);
188
189	return 0;
190}
191
192static inline void
193ipi_ack(gemini_ipi_softc_t *sc)
194{
195	uint32_t r;
196	uint32_t bit;
197	bus_addr_t off;
198
199#if defined(GEMINI_MASTER)
200	off = GEMINI_GLOBAL_CPU1;
201	bit = GLOBAL_CPU1_IPICPU0;
202#elif defined(GEMINI_SLAVE)
203	off = GEMINI_GLOBAL_CPU0;
204	bit = GLOBAL_CPU0_IPICPU1;
205#endif
206
207	r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
208	r &= ~bit;
209	bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, r);
210}
211
212static int
213gemini_ipiintr(void *arg)
214{
215	gemini_ipi_softc_t *sc = arg;
216	int rv;
217
218	if (sc == NULL)
219		return -1;
220
221	ipi_ack(sc);
222
223	rv = gemini_ipi_intrq_dispatch(sc);
224
225	return rv;
226}
227