gemini_ipi.c revision 1.1 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
7 #include <sys/cdefs.h>
8
9 __KERNEL_RCSID(0, "$NetBSD: gemini_ipi.c,v 1.1 2008/11/20 08:02:48 cliff Exp $");
10
11 #include <sys/param.h>
12 #include <sys/systm.h>
13 #include <sys/device.h>
14 #include <sys/intr.h>
15 #include <sys/malloc.h>
16 #include <arch/arm/gemini/gemini_obiovar.h>
17 #include <arch/arm/gemini/gemini_ipivar.h>
18 #include <arch/arm/gemini/gemini_reg.h>
19
20 static int gemini_ipi_match(struct device *, struct cfdata *, void *);
21 static void gemini_ipi_attach(struct device *, struct device *, void *);
22 static int gemini_ipiintr(void *);
23
24 CFATTACH_DECL_NEW(geminiipi, sizeof(struct gemini_ipi_softc),
25 gemini_ipi_match, gemini_ipi_attach, NULL, NULL);
26
27 static gemini_ipi_softc_t *gemini_ipi_sc;
28
29
30 static int
31 gemini_ipi_match(struct device *parent, struct cfdata *cf, void *aux)
32 {
33 struct obio_attach_args *obio = aux;
34
35 if (obio->obio_intr == LPCCF_INTR_DEFAULT)
36 panic("ipi must specify intr in config.");
37
38 return 1;
39 }
40
41 static void
42 gemini_ipi_attach(struct device *parent, struct device *self, void *aux)
43 {
44 gemini_ipi_softc_t *sc = device_private(self);
45 struct obio_attach_args *obio = aux;
46 bus_space_tag_t iot;
47 bus_space_handle_t ioh;
48 bus_size_t size;
49 bus_addr_t addr;
50 void *ih;
51
52 iot = obio->obio_iot;
53 addr = GEMINI_GLOBAL_BASE;
54 size = 4096; /* XXX */
55
56 if (bus_space_map(iot, addr, size, 0, &ioh))
57 panic("%s: Cannot map registers", device_xname(self));
58
59 /*
60 * NOTE we are using IPL_NET, not IPL_IPI a.k.a. IPL_HIGH
61 * use of IPI on this system is (mainly) networking
62 * keep simple (for now) and force all IPIs to same level
63 * 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
91 static inline int
92 gemini_ipi_intrq_empty(gemini_ipi_softc_t *sc)
93 {
94 return SIMPLEQ_EMPTY(&sc->sc_intrq);
95 }
96
97 static inline void *
98 gemini_ipi_intrq_insert(gemini_ipi_softc_t *sc, int (*func)(void *), void *arg)
99 {
100 gemini_ipi_intrq_t *iqp;
101
102 iqp = malloc(sizeof(*iqp), M_DEVBUF, M_NOWAIT|M_ZERO);
103 if (iqp == NULL) {
104 printf("gemini_ipi_intrq_insert: malloc failed\n");
105 return NULL;
106 }
107
108 iqp->iq_func = func;
109 iqp->iq_arg = arg;
110 SIMPLEQ_INSERT_TAIL(&sc->sc_intrq, iqp, iq_q);
111
112 return (void *)iqp;
113 }
114
115 static inline void
116 gemini_ipi_intrq_remove(gemini_ipi_softc_t *sc, void *cookie)
117 {
118 gemini_ipi_intrq_t *iqp;
119
120 SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q) {
121 if ((void *)iqp == cookie) {
122 SIMPLEQ_REMOVE(&sc->sc_intrq,
123 iqp, gemini_ipi_intrq, iq_q);
124 free(iqp, M_DEVBUF);
125 return;
126 }
127 }
128 }
129
130 static inline int
131 gemini_ipi_intrq_dispatch(gemini_ipi_softc_t *sc)
132 {
133 gemini_ipi_intrq_t *iqp;
134 int rv = 0;
135
136 SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q)
137 rv |= (*iqp->iq_func)(iqp->iq_arg);
138
139 return (rv != 0);
140 }
141
142
143 void *
144 ipi_intr_establish(int (*func)(void *), void *arg)
145 {
146 gemini_ipi_softc_t *sc = gemini_ipi_sc;
147 void *ih;
148
149 if (sc == NULL)
150 return NULL;
151
152 ih = gemini_ipi_intrq_insert(sc, func, arg);
153 #ifdef DEBUG
154 if (ih == NULL)
155 panic("%s: gemini_ipi_intrq_insert failed",
156 device_xname(&sc->sc_dev));
157 #endif
158
159 return ih;
160 }
161
162 void
163 ipi_intr_disestablish(void *ih)
164 {
165 gemini_ipi_softc_t *sc = gemini_ipi_sc;
166
167 if (sc == NULL)
168 panic("%s: NULL gemini_ipi_sc", device_xname(&sc->sc_dev));
169
170 gemini_ipi_intrq_remove(sc, ih);
171 }
172
173 int
174 ipi_send(void)
175 {
176 gemini_ipi_softc_t *sc = gemini_ipi_sc;
177 uint32_t r;
178 uint32_t bit;
179 bus_addr_t off;
180
181 if (sc == NULL)
182 return -1;
183
184 #if defined(GEMINI_MASTER)
185 off = GEMINI_GLOBAL_CPU0;
186 bit = GLOBAL_CPU0_IPICPU1;
187 #elif defined(GEMINI_SLAVE)
188 off = GEMINI_GLOBAL_CPU1;
189 bit = GLOBAL_CPU1_IPICPU0;
190 #endif
191
192 r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
193 r |= bit;
194 bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, r);
195
196 return 0;
197 }
198
199 static inline void
200 ipi_ack(gemini_ipi_softc_t *sc)
201 {
202 uint32_t r;
203 uint32_t bit;
204 bus_addr_t off;
205
206 #if defined(GEMINI_MASTER)
207 off = GEMINI_GLOBAL_CPU1;
208 bit = GLOBAL_CPU1_IPICPU0;
209 #elif defined(GEMINI_SLAVE)
210 off = GEMINI_GLOBAL_CPU0;
211 bit = GLOBAL_CPU0_IPICPU1;
212 #endif
213
214 r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
215 r &= ~bit;
216 bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, r);
217 }
218
219 static int
220 gemini_ipiintr(void *arg)
221 {
222 gemini_ipi_softc_t *sc = arg;
223 int rv;
224
225 if (sc == NULL)
226 return -1;
227
228 ipi_ack(sc);
229
230 rv = gemini_ipi_intrq_dispatch(sc);
231
232 return rv;
233 }
234