xref: /netbsd-src/sys/arch/arm/gemini/gemini_ipi.c (revision ef77c48fb262a77d43e3661bf9297f78b87bb401)
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_ipi.c,v 1.2 2008/11/26 05:25:27 matt 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/malloc.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 
21 static int  gemini_ipi_match(struct device *, struct cfdata *, void *);
22 static void gemini_ipi_attach(struct device *, struct device *, void *);
23 static int  gemini_ipiintr(void *);
24 
25 CFATTACH_DECL_NEW(geminiipi, sizeof(struct gemini_ipi_softc),
26 	gemini_ipi_match, gemini_ipi_attach, NULL, NULL);
27 
28 static gemini_ipi_softc_t *gemini_ipi_sc;
29 
30 
31 static int
32 gemini_ipi_match(struct device *parent, struct cfdata *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 
42 static void
43 gemini_ipi_attach(struct device *parent, struct device *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_IPI a.k.a. IPL_HIGH
62 	 * use of IPI on this system is (mainly) networking
63 	 * keep simple (for now) and force all IPIs to same level
64 	 * so splnet() can block them as any other NIC.
65 	 */
66 #if 0
67 	ih = intr_establish(obio->obio_intr, IPL_NET, IST_LEVEL_HIGH,
68 		gemini_ipiintr, sc);
69 #else
70 	ih = intr_establish(obio->obio_intr, IPL_NET, IST_EDGE_RISING,
71 		gemini_ipiintr, sc);
72 #endif
73 	if (ih == NULL)
74 		panic("%s: Cannot establish interrupt %d\n",
75 			device_xname(self), obio->obio_intr);
76 
77 	SIMPLEQ_INIT(&sc->sc_intrq);
78 
79 	sc->sc_iot = iot;
80 	sc->sc_ioh = ioh;
81 	sc->sc_addr = addr;
82 	sc->sc_size = size;
83 	sc->sc_intr = obio->obio_intr;
84 	sc->sc_ih = ih;
85 
86 	gemini_ipi_sc = sc;
87 
88 	aprint_normal("\n");
89 	aprint_naive("\n");
90 
91 #if NGNP > 0
92 	config_found(self, "gnp", NULL);
93 #endif
94 }
95 
96 static inline int
97 gemini_ipi_intrq_empty(gemini_ipi_softc_t *sc)
98 {
99 	return SIMPLEQ_EMPTY(&sc->sc_intrq);
100 }
101 
102 static inline void *
103 gemini_ipi_intrq_insert(gemini_ipi_softc_t *sc, int (*func)(void *), void *arg)
104 {
105 	gemini_ipi_intrq_t *iqp;
106 
107         iqp = malloc(sizeof(*iqp), M_DEVBUF, M_NOWAIT|M_ZERO);
108         if (iqp == NULL) {
109 		printf("gemini_ipi_intrq_insert: malloc failed\n");
110 		return NULL;
111 	}
112 
113         iqp->iq_func = func;
114         iqp->iq_arg = arg;
115         SIMPLEQ_INSERT_TAIL(&sc->sc_intrq, iqp, iq_q);
116 
117 	return (void *)iqp;
118 }
119 
120 static inline void
121 gemini_ipi_intrq_remove(gemini_ipi_softc_t *sc, void *cookie)
122 {
123 	gemini_ipi_intrq_t *iqp;
124 
125 	SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q) {
126 		if ((void *)iqp == cookie) {
127 			SIMPLEQ_REMOVE(&sc->sc_intrq,
128 				iqp, gemini_ipi_intrq, iq_q);
129 			free(iqp, M_DEVBUF);
130 			return;
131 		}
132 	}
133 }
134 
135 static inline int
136 gemini_ipi_intrq_dispatch(gemini_ipi_softc_t *sc)
137 {
138 	gemini_ipi_intrq_t *iqp;
139 	int rv = 0;
140 
141 	SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q)
142 		rv |= (*iqp->iq_func)(iqp->iq_arg);
143 
144 	return (rv != 0);
145 }
146 
147 
148 void *
149 ipi_intr_establish(int (*func)(void *), void *arg)
150 {
151         gemini_ipi_softc_t *sc = gemini_ipi_sc;
152 	void *ih;
153 
154 	if (sc == NULL)
155 		return NULL;
156 
157 	ih = gemini_ipi_intrq_insert(sc, func, arg);
158 #ifdef DEBUG
159         if (ih == NULL)
160 		panic("%s: gemini_ipi_intrq_insert failed",
161 			device_xname(&sc->sc_dev));
162 #endif
163 
164 	return ih;
165 }
166 
167 void
168 ipi_intr_disestablish(void *ih)
169 {
170         gemini_ipi_softc_t *sc = gemini_ipi_sc;
171 
172 	if (sc == NULL)
173 		panic("%s: NULL gemini_ipi_sc", device_xname(&sc->sc_dev));
174 
175         gemini_ipi_intrq_remove(sc, ih);
176 }
177 
178 int
179 ipi_send(void)
180 {
181         gemini_ipi_softc_t *sc = gemini_ipi_sc;
182 	uint32_t r;
183 	uint32_t bit;
184 	bus_addr_t off;
185 
186 	if (sc == NULL)
187 		return -1;
188 
189 #if defined(GEMINI_MASTER)
190 	off = GEMINI_GLOBAL_CPU0;
191 	bit = GLOBAL_CPU0_IPICPU1;
192 #elif defined(GEMINI_SLAVE)
193 	off = GEMINI_GLOBAL_CPU1;
194 	bit = GLOBAL_CPU1_IPICPU0;
195 #endif
196 
197 	r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
198 	r |= bit;
199 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, r);
200 
201 	return 0;
202 }
203 
204 static inline void
205 ipi_ack(gemini_ipi_softc_t *sc)
206 {
207 	uint32_t r;
208 	uint32_t bit;
209 	bus_addr_t off;
210 
211 #if defined(GEMINI_MASTER)
212 	off = GEMINI_GLOBAL_CPU1;
213 	bit = GLOBAL_CPU1_IPICPU0;
214 #elif defined(GEMINI_SLAVE)
215 	off = GEMINI_GLOBAL_CPU0;
216 	bit = GLOBAL_CPU0_IPICPU1;
217 #endif
218 
219 	r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
220 	r &= ~bit;
221 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, r);
222 }
223 
224 static int
225 gemini_ipiintr(void *arg)
226 {
227 	gemini_ipi_softc_t *sc = arg;
228 	int rv;
229 
230 	if (sc == NULL)
231 		return -1;
232 
233 	ipi_ack(sc);
234 
235 	rv = gemini_ipi_intrq_dispatch(sc);
236 
237 	return rv;
238 }
239