initial import
[ps3freebsd_ps3vuart.git] / ps3vuart_bus.c
blob7369bf8f8413b62bfb502a94f51f3f2ac2d69284
1 /*-
2 * Copyright (C) 2011 glevand <geoffrey.levand@mail.ru>
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification, immediately at the beginning of the file.
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.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 * $FreeBSD$
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/module.h>
34 #include <sys/kernel.h>
35 #include <sys/systm.h>
36 #include <sys/bus.h>
37 #include <sys/malloc.h>
38 #include <sys/lock.h>
39 #include <sys/mutex.h>
40 #include <sys/resource.h>
41 #include <sys/rman.h>
43 #include <vm/vm.h>
44 #include <vm/pmap.h>
46 #include <machine/bus.h>
47 #include <machine/platform.h>
48 #include <machine/pmap.h>
49 #include <machine/resource.h>
51 #include <powerpc/ps3/ps3-hvcall.h>
53 #include "ps3vuart.h"
54 #include "ps3vuart_bus.h"
56 #define PS3VUART_BUS_LOCK_INIT(_sc) \
57 mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
58 "ps3vuart_bus", MTX_DEF)
59 #define PS3VUART_BUS_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
60 #define PS3VUART_BUS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
61 #define PS3VUART_BUS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
62 #define PS3VUART_BUS_ASSERT_LOCKED(_sc) \
63 mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
64 #define PS3VUART_BUS_ASSERT_UNLOCKED(_sc) \
65 mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED)
67 #define PS3VUART_BUS_PORT_DM 10
69 #define PS3VUART_BUS_MAX_PORTS (PS3VUART_BUS_PORT_DM + 1)
71 #define PS3VUART_BUS_IRQ_RID 0
73 #define PS3_LAID_GAMEOS 0x1070000002000001ul
75 struct ps3vuart_bus_info {
76 int type;
77 int port;
80 struct ps3vuart_bus_intr {
81 driver_intr_t *i_ithread;
82 void *i_arg;
85 struct ps3vuart_bus_softc {
86 device_t sc_dev;
88 struct mtx sc_mtx;
90 volatile uint64_t *sc_irq_bitmap;
91 int sc_irq_id;
92 struct resource *sc_irq;
93 void *sc_irq_ctx;
95 struct ps3vuart_bus_intr sc_intr[PS3VUART_BUS_MAX_PORTS];
98 static void ps3vuart_bus_intr(void *arg);
100 static int ps3vuart_bus_add_children(struct ps3vuart_bus_softc *sc);
101 static void ps3vuart_bus_delete_children(struct ps3vuart_bus_softc *sc);
102 static int ps3vuart_bus_add_child(struct ps3vuart_bus_softc *sc,
103 int type, int port);
105 static MALLOC_DEFINE(M_PS3VUART_BUS, "ps3vuart_bus", "PS3 VUART Bus");
107 static int
108 ps3vuart_bus_probe(device_t dev)
110 device_set_desc(dev, "Playstation 3 VUART Bus");
112 return (BUS_PROBE_SPECIFIC);
115 static int
116 ps3vuart_bus_attach(device_t dev)
118 struct ps3vuart_bus_softc *sc = device_get_softc(dev);
119 int err;
121 sc->sc_dev = dev;
123 PS3VUART_BUS_LOCK_INIT(sc);
125 /* Setup IRQ */
127 sc->sc_irq_bitmap = ps3vuart_get_irq_bitmap(dev);
129 sc->sc_irq_id = PS3VUART_BUS_IRQ_RID;
130 sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
131 &sc->sc_irq_id, RF_ACTIVE);
132 if (!sc->sc_irq) {
133 device_printf(dev, "Could not allocate IRQ\n");
134 err = ENXIO;
135 goto destroy_lock;
138 err = bus_setup_intr(dev, sc->sc_irq,
139 INTR_TYPE_MISC | INTR_MPSAFE,
140 NULL, ps3vuart_bus_intr, sc, &sc->sc_irq_ctx);
141 if (err) {
142 device_printf(dev, "Could not setup IRQ (%d)\n", err);
143 goto release_irq;
146 /* Setup children */
148 err = ps3vuart_bus_add_children(sc);
149 if (err) {
150 device_printf(dev, "Could not add children (%d)\n", err);
151 goto teardown_irq;
154 err = bus_generic_attach(sc->sc_dev);
155 if (err)
156 goto delete_children;
158 return (0);
160 delete_children:
162 ps3vuart_bus_delete_children(sc);
164 teardown_irq:
166 bus_teardown_intr(dev, sc->sc_irq, sc->sc_irq_ctx);
168 release_irq:
170 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_id, sc->sc_irq);
172 destroy_lock:
174 PS3VUART_BUS_LOCK_DESTROY(sc);
176 return (err);
179 static int
180 ps3vuart_bus_detach(device_t dev)
182 struct ps3vuart_bus_softc *sc = device_get_softc(dev);
184 /* Destroy children */
186 ps3vuart_bus_delete_children(sc);
188 /* Free IRQ */
190 bus_teardown_intr(dev, sc->sc_irq, sc->sc_irq_ctx);
191 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_id, sc->sc_irq);
193 sc->sc_irq_bitmap = NULL;
195 PS3VUART_BUS_LOCK_DESTROY(sc);
197 return (0);
200 static int
201 ps3vuart_bus_print_child(device_t dev, device_t child)
203 struct ps3vuart_bus_info *info = device_get_ivars(child);
204 int retval;
206 retval = bus_print_child_header(dev, child);
207 retval += printf(" at port %d", info->port);
208 retval += bus_print_child_footer(dev, child);
210 return (retval);
213 static int
214 ps3vuart_bus_read_ivar(device_t dev, device_t child,
215 int index, uintptr_t *result)
217 struct ps3vuart_bus_info *info = device_get_ivars(child);
219 switch (index) {
220 case PS3VUART_BUS_IVAR_TYPE:
221 *result = info->type;
222 break;
223 case PS3VUART_BUS_IVAR_PORT:
224 *result = info->port;
225 break;
226 default:
227 return (EINVAL);
230 return (0);
233 static struct resource *
234 ps3vuart_bus_alloc_resource(device_t dev, device_t child, int type, int *rid,
235 u_long start, u_long end, u_long count, u_int flags)
237 struct ps3vuart_bus_softc *sc = device_get_softc(dev);
238 struct resource *r = NULL;
240 switch (type) {
241 case SYS_RES_IRQ:
242 if (*rid == PS3VUART_BUS_IRQ_RID)
243 r = sc->sc_irq;
244 break;
247 return (r);
250 static int
251 ps3vuart_bus_release_resource(device_t dev, device_t child, int type, int rid,
252 struct resource *r)
254 switch (type) {
255 case SYS_RES_IRQ:
256 if (rid != PS3VUART_BUS_IRQ_RID)
257 return (ENOENT);
258 else
259 return (0);
260 break;
263 return (EINVAL);
266 static int
267 ps3vuart_bus_setup_intr(device_t dev, device_t child, struct resource *irq,
268 int flags, driver_filter_t *filter, driver_intr_t *ithread,
269 void *arg, void **cookiep)
271 struct ps3vuart_bus_softc *sc = device_get_softc(dev);
272 int port = ps3vuart_bus_get_port(child);
274 if (filter)
275 return (EINVAL);
277 sc->sc_intr[port].i_ithread = ithread;
278 sc->sc_intr[port].i_arg = arg;
280 return (0);
283 static int
284 ps3vuart_bus_teardown_intr(device_t dev, device_t child, struct resource *irq,
285 void *cookie)
287 struct ps3vuart_bus_softc *sc = device_get_softc(dev);
288 int port = ps3vuart_bus_get_port(child);
290 sc->sc_intr[port].i_ithread = NULL;
291 sc->sc_intr[port].i_arg = NULL;
293 return (0);
296 static int
297 ps3vuart_bus_child_location_str(device_t dev, device_t child,
298 char *buf, size_t buflen)
300 int port = ps3vuart_bus_get_port(child);
302 snprintf(buf, buflen, "port=%d", port);
304 return (0);
307 static void
308 ps3vuart_bus_intr(void *arg)
310 struct ps3vuart_bus_softc *sc = (struct ps3vuart_bus_softc *) arg;
311 int port;
313 PS3VUART_BUS_LOCK(sc);
315 while ((port = 64 - ffsl(sc->sc_irq_bitmap[0])) != 64) {
316 KASSERT(port < PS3VUART_BUS_MAX_PORTS,
317 ("invalid port %d", port));
318 KASSERT(sc->sc_intr[port].i_ithread != NULL,
319 ("unhandled IRQ on port %d", port));
321 /* Dispatch IRQ to child */
323 sc->sc_intr[port].i_ithread(sc->sc_intr[port].i_arg);
326 PS3VUART_BUS_UNLOCK(sc);
329 static int
330 ps3vuart_bus_add_children(struct ps3vuart_bus_softc *sc)
332 uint64_t lpar_id, port, laid, junk;
333 int err;
335 lv1_get_logical_partition_id(&lpar_id);
337 /* Attach AV */
339 err = lv1_get_repository_node_value(lpar_id,
340 (lv1_repository_string("bi") >> 32),
341 lv1_repository_string("vir_uart"),
342 lv1_repository_string("port"),
343 lv1_repository_string("avset"), &port, &junk);
344 if (!err) {
345 err = ps3vuart_bus_add_child(sc, PS3VUART_BUS_TYPE_AV, port);
346 if (err)
347 goto fail;
350 /* Attach System Manager */
352 err = lv1_get_repository_node_value(lpar_id,
353 (lv1_repository_string("bi") >> 32),
354 lv1_repository_string("vir_uart"),
355 lv1_repository_string("port"),
356 lv1_repository_string("sysmgr"), &port, &junk);
357 if (!err) {
358 err = ps3vuart_bus_add_child(sc, PS3VUART_BUS_TYPE_SM, port);
359 if (err)
360 goto fail;
363 /* Attach Dispatcher Manager which is only present in GameOS LPAR */
365 err = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
366 (lv1_repository_string("ss") >> 32),
367 lv1_repository_string("laid"), 2, 0, &laid, &junk);
368 if (!err && (laid == PS3_LAID_GAMEOS)) {
369 /* Dispatcher Manager port is not available in node repository */
370 err = ps3vuart_bus_add_child(sc, PS3VUART_BUS_TYPE_DM,
371 PS3VUART_BUS_PORT_DM);
372 if (err)
373 goto fail;
376 return (0);
378 fail:
380 ps3vuart_bus_delete_children(sc);
382 return (err);
385 static void
386 ps3vuart_bus_delete_children(struct ps3vuart_bus_softc *sc)
388 device_t *children;
389 int nchildren, i;
391 if (device_get_children(sc->sc_dev, &children, &nchildren))
392 return;
394 for (i = 0; i < nchildren; i++) {
395 free(device_get_ivars(children[i]), M_PS3VUART_BUS);
396 device_delete_child(sc->sc_dev, children[i]);
399 free(children, M_TEMP);
402 static int
403 ps3vuart_bus_add_child(struct ps3vuart_bus_softc *sc,
404 int type, int port)
406 struct ps3vuart_bus_info *info;
407 device_t child;
408 int err;
410 info = malloc(sizeof(*info), M_PS3VUART_BUS, M_WAITOK | M_ZERO);
411 if (!info)
412 return (ENOMEM);
414 info->type = type;
415 info->port = port;
417 child = device_add_child(sc->sc_dev, NULL, -1);
418 if (!child) {
419 err = ENOMEM;
420 goto fail;
423 device_set_ivars(child, info);
425 return (0);
427 fail:
429 free(info, M_PS3VUART_BUS);
431 return (err);
434 static device_method_t ps3vuart_bus_methods[] = {
435 /* Device interface */
436 DEVMETHOD(device_probe, ps3vuart_bus_probe),
437 DEVMETHOD(device_attach, ps3vuart_bus_attach),
438 DEVMETHOD(device_detach, ps3vuart_bus_detach),
440 /* Bus interface */
441 DEVMETHOD(bus_print_child, ps3vuart_bus_print_child),
442 DEVMETHOD(bus_read_ivar, ps3vuart_bus_read_ivar),
443 DEVMETHOD(bus_alloc_resource, ps3vuart_bus_alloc_resource),
444 DEVMETHOD(bus_release_resource, ps3vuart_bus_release_resource),
445 DEVMETHOD(bus_setup_intr, ps3vuart_bus_setup_intr),
446 DEVMETHOD(bus_teardown_intr, ps3vuart_bus_teardown_intr),
447 DEVMETHOD(bus_child_location_str, ps3vuart_bus_child_location_str),
449 { 0, 0 }
452 static driver_t ps3vuart_bus_driver = {
453 "ps3vuart_bus",
454 ps3vuart_bus_methods,
455 sizeof(struct ps3vuart_bus_softc)
458 static devclass_t ps3vuart_bus_devclass;
460 DRIVER_MODULE(ps3vuart_bus, ps3vuart, ps3vuart_bus_driver, ps3vuart_bus_devclass, 0, 0);
461 MODULE_VERSION(ps3vuart_bus, 1);