]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/sun4v/sun4v/vnex.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / sun4v / sun4v / vnex.c
1 /*-
2  * Copyright (c) 2006 by Marius Strobl <marius@FreeBSD.org>.
3  * Copyright (c) 2006 Kip Macy <kmacy@FreeBSD.org>.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/pcpu.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/module.h>
39
40 #include <dev/ofw/ofw_bus.h>
41 #include <dev/ofw/ofw_bus_subr.h>
42 #include <dev/ofw/openfirm.h>
43
44 #include <machine/bus.h>
45 #include <machine/hypervisorvar.h>
46 #include <machine/hv_api.h>
47 #include <machine/intr_machdep.h>
48 #include <machine/nexusvar.h>
49 #include <machine/resource.h>
50
51 #include <machine/mdesc_bus.h>
52 #include <machine/cddl/mdesc.h>
53 #include <machine/cddl/mdesc_impl.h>
54
55 #include <sys/rman.h>
56
57
58 #define SUN4V_REG_SPEC2CFG_HDL(x)       ((x >> 32) & ~(0xfull << 28))
59
60 static device_probe_t vnex_probe;
61 static device_attach_t vnex_attach;
62 static bus_print_child_t vnex_print_child;
63 static bus_add_child_t vnex_add_child;
64 static bus_probe_nomatch_t vnex_probe_nomatch;
65 static bus_setup_intr_t vnex_setup_intr;
66 static bus_teardown_intr_t vnex_teardown_intr;
67 static bus_get_resource_list_t vnex_get_resource_list;
68 static mdesc_bus_get_devinfo_t vnex_get_devinfo;
69
70 static struct vnex_devinfo * vnex_setup_dinfo(device_t, mde_cookie_t node);
71 static void vnex_destroy_dinfo(struct vnex_devinfo *);
72 static int vnex_print_res(struct vnex_devinfo *);
73
74 struct vnex_devinfo {
75         struct mdesc_bus_devinfo        vndi_mbdinfo;
76         struct resource_list    vndi_rl;
77
78         /* Some common properties. */
79         struct          nexus_regs *vndi_reg;
80         int             vndi_nreg;
81 };
82
83 struct vnex_softc {
84         struct rman     sc_intr_rman;
85         struct rman     sc_mem_rman;
86 };
87
88 static device_method_t vnex_methods[] = {
89         /* Device interface */
90         DEVMETHOD(device_probe,         vnex_probe),
91         DEVMETHOD(device_attach,        vnex_attach),
92         DEVMETHOD(device_detach,        bus_generic_detach),
93         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
94         DEVMETHOD(device_suspend,       bus_generic_suspend),
95         DEVMETHOD(device_resume,        bus_generic_resume),
96
97         /* Bus interface */
98         DEVMETHOD(bus_print_child,      vnex_print_child),
99         DEVMETHOD(bus_probe_nomatch,    vnex_probe_nomatch),
100         DEVMETHOD(bus_read_ivar,        bus_generic_read_ivar),
101         DEVMETHOD(bus_write_ivar,       bus_generic_write_ivar),
102         DEVMETHOD(bus_add_child,        vnex_add_child),
103         DEVMETHOD(bus_alloc_resource,   bus_generic_alloc_resource),
104         DEVMETHOD(bus_activate_resource,        bus_generic_activate_resource),
105         DEVMETHOD(bus_deactivate_resource,      bus_generic_deactivate_resource),
106         DEVMETHOD(bus_release_resource, bus_generic_release_resource),
107         DEVMETHOD(bus_setup_intr,       vnex_setup_intr),
108         DEVMETHOD(bus_teardown_intr,    vnex_teardown_intr),
109         DEVMETHOD(bus_get_resource,     bus_generic_rl_get_resource),
110         DEVMETHOD(bus_get_resource_list, vnex_get_resource_list),
111
112         /* ofw_bus interface */
113         /* mdesc_bus interface */
114         DEVMETHOD(mdesc_bus_get_devinfo, vnex_get_devinfo),
115         DEVMETHOD(mdesc_bus_get_compat, mdesc_bus_gen_get_compat),
116         DEVMETHOD(mdesc_bus_get_name,   mdesc_bus_gen_get_name),
117         DEVMETHOD(mdesc_bus_get_type,   mdesc_bus_gen_get_type),
118
119         { 0, 0 }
120 };
121
122
123 static driver_t vnex_driver = {
124         "vnex",
125         vnex_methods,
126         sizeof(struct vnex_softc),
127 };
128
129
130 static devclass_t vnex_devclass;
131 DRIVER_MODULE(vnex, nexus, vnex_driver, vnex_devclass, 0, 0);
132
133
134 static int
135 vnex_probe(device_t dev)
136 {
137         if (strcmp(ofw_bus_get_name(dev), "virtual-devices"))
138                 return (ENXIO);
139
140         device_set_desc(dev, "virtual nexus device");
141         return (0);
142 }
143
144
145 static int
146 vnex_attach(device_t dev)
147 {
148         struct vnex_devinfo  *vndi;
149         struct vnex_softc    *sc;
150         device_t              cdev;
151         phandle_t             node;
152         mde_cookie_t          rootnode, *listp = NULL;
153         int                   i, listsz, num_nodes, num_devices;
154         md_t                 *mdp;
155
156
157         node = ofw_bus_get_node(dev);
158         if (node == -1)
159                 panic("%s: ofw_bus_get_node failed.", __func__);
160
161         sc = device_get_softc(dev);
162         sc->sc_intr_rman.rm_type = RMAN_ARRAY;
163         sc->sc_intr_rman.rm_descr = "Interrupts";
164         sc->sc_mem_rman.rm_type = RMAN_ARRAY;
165         sc->sc_mem_rman.rm_descr = "Device Memory";
166         if (rman_init(&sc->sc_intr_rman) != 0 ||
167             rman_init(&sc->sc_mem_rman) != 0 ||
168             rman_manage_region(&sc->sc_intr_rman, 0, IV_MAX - 1) != 0 ||
169             rman_manage_region(&sc->sc_mem_rman, 0ULL, ~0ULL) != 0)
170                 panic("%s: failed to set up rmans.", __func__);
171
172         if ((mdp = md_get()) == NULL) 
173                 return (ENXIO);
174
175         num_nodes = md_node_count(mdp);
176         listsz = num_nodes * sizeof(mde_cookie_t);
177         listp = (mde_cookie_t *)malloc(listsz, M_DEVBUF, M_WAITOK);
178         rootnode = md_root_node(mdp);
179         
180         /*
181          * scan the machine description for virtual devices
182          */
183         num_devices = md_scan_dag(mdp, rootnode, 
184                                   md_find_name(mdp, "virtual-device"),
185                                   md_find_name(mdp, "fwd"), listp);
186
187         for (i = 0; i < num_devices; i++) {
188                 if ((vndi = vnex_setup_dinfo(dev, listp[i])) == NULL)
189                         continue;
190
191                 cdev = device_add_child(dev, NULL, -1);
192                 if (cdev == NULL) {
193                         device_printf(dev, "<%s>: device_add_child failed\n",
194                             vndi->vndi_mbdinfo.mbd_name);
195                         vnex_destroy_dinfo(vndi);
196                         continue;
197                 }
198                 device_set_ivars(cdev, vndi);
199         }
200         bus_generic_attach(dev);
201         free(listp, M_DEVBUF);
202
203         return (0);
204 }
205
206 static device_t
207 vnex_add_child(device_t dev, int order, const char *name, int unit)
208 {
209         device_t cdev;
210         struct vnex_devinfo *vndi;
211
212         cdev = device_add_child_ordered(dev, order, name, unit);
213         if (cdev == NULL)
214                 return (NULL);
215
216         vndi = malloc(sizeof(*vndi), M_DEVBUF, M_WAITOK | M_ZERO);
217         vndi->vndi_mbdinfo.mbd_name = strdup(name, M_OFWPROP);
218         resource_list_init(&vndi->vndi_rl);
219         device_set_ivars(cdev, vndi);
220
221         return (cdev);
222 }
223
224 static int
225 vnex_print_child(device_t dev, device_t child)
226 {
227         int rv;
228
229         rv = bus_print_child_header(dev, child);
230         rv += vnex_print_res(device_get_ivars(child));
231         rv += bus_print_child_footer(dev, child);
232         return (rv);
233 }
234
235 static void
236 vnex_probe_nomatch(device_t dev, device_t child)
237 {
238         const char *type;
239
240         device_printf(dev, "<%s>", mdesc_bus_get_name(child));
241         vnex_print_res(device_get_ivars(child));
242         type = mdesc_bus_get_type(child);
243         printf(" type %s (no driver attached)\n",
244             type != NULL ? type : "unknown");
245 }
246
247
248 static int
249 vnex_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
250     driver_filter_t *filt,driver_intr_t *intr, void *arg, void **cookiep)
251 {
252
253         uint64_t reg, nreg;
254         uint64_t ihdl, cfg;
255         uint64_t ino, nino;
256         int error, cpuid;
257         
258         if (res == NULL)
259                 panic("%s: NULL interrupt resource!", __func__);
260
261         if ((error = bus_get_resource(dev, SYS_RES_MEMORY, 0, &reg, &nreg)))
262                 goto fail;
263
264         if ((error = bus_get_resource(child, SYS_RES_IRQ, 0, &ino, &nino)))
265                 goto fail;
266        
267         cfg = SUN4V_REG_SPEC2CFG_HDL(reg);
268
269         if (hv_intr_devino_to_sysino(cfg, (uint32_t)ino, &ihdl) != H_EOK) {
270                 error = ENXIO;
271                 goto fail;
272         }
273
274         cpuid = 0;
275
276         if (hv_intr_settarget(ihdl, cpuid) != H_EOK) {
277                 error = ENXIO;
278                 goto fail;
279         }
280
281         if (hv_intr_setstate(ihdl, HV_INTR_IDLE_STATE) != H_EOK) {
282                 error = ENXIO;
283                 goto fail;
284         }
285
286         if (hv_intr_setenabled(ihdl, HV_INTR_ENABLED) != H_EOK) {
287                 error = ENXIO;
288                 goto fail;
289         }
290         
291         if ((rman_get_flags(res) & RF_SHAREABLE) == 0)
292                 flags |= INTR_EXCL;
293
294         /* We depend here on rman_activate_resource() being idempotent. */
295         if ((error = rman_activate_resource(res)))
296                 goto fail;
297
298         error = inthand_add(device_get_nameunit(child), ihdl,
299                             filt, intr, arg, flags, cookiep);
300
301         printf("inthandler added\n");
302 fail:
303
304         return (error);
305 }
306
307 static int
308 vnex_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
309 {
310
311         inthand_remove(rman_get_start(r), ih);
312         return (0);
313 }
314
315 static struct resource_list *
316 vnex_get_resource_list(device_t dev, device_t child)
317 {
318         struct vnex_devinfo *vndi;
319
320         vndi = device_get_ivars(child);
321         return (&vndi->vndi_rl);
322 }
323
324 static const struct mdesc_bus_devinfo *
325 vnex_get_devinfo(device_t dev, device_t child)
326 {
327         struct vnex_devinfo *vndi;
328
329         vndi = device_get_ivars(child);
330         return (&vndi->vndi_mbdinfo);
331 }
332
333 static struct vnex_devinfo *
334 vnex_setup_dinfo(device_t dev, mde_cookie_t node)
335 {
336         struct vnex_devinfo *vndi;
337
338         vndi = malloc(sizeof(*vndi), M_DEVBUF, M_WAITOK | M_ZERO);
339         if (mdesc_bus_gen_setup_devinfo(&vndi->vndi_mbdinfo, node) != 0) {
340                 free(vndi, M_DEVBUF);
341                 return (NULL);
342         }
343
344         return (vndi);
345 }
346
347 static void
348 vnex_destroy_dinfo(struct vnex_devinfo *vndi)
349 {
350
351         resource_list_free(&vndi->vndi_rl);
352         mdesc_bus_gen_destroy_devinfo(&vndi->vndi_mbdinfo);
353         free(vndi, M_DEVBUF);
354 }
355
356
357 static int
358 vnex_print_res(struct vnex_devinfo *vndi)
359 {
360         int rv;
361
362         rv = 0;
363         rv += resource_list_print_type(&vndi->vndi_rl, "mem", SYS_RES_MEMORY,
364             "%#lx");
365         rv += resource_list_print_type(&vndi->vndi_rl, "irq", SYS_RES_IRQ,
366             "%ld");
367         return (rv);
368 }