2 * Copyright (C) 2011 glevand <geoffrey.levand@mail.ru>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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>
37 #include <sys/malloc.h>
39 #include <sys/mutex.h>
40 #include <sys/resource.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>
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
{
80 struct ps3vuart_bus_intr
{
81 driver_intr_t
*i_ithread
;
85 struct ps3vuart_bus_softc
{
90 volatile uint64_t *sc_irq_bitmap
;
92 struct resource
*sc_irq
;
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
,
105 static MALLOC_DEFINE(M_PS3VUART_BUS
, "ps3vuart_bus", "PS3 VUART Bus");
108 ps3vuart_bus_probe(device_t dev
)
110 device_set_desc(dev
, "Playstation 3 VUART Bus");
112 return (BUS_PROBE_SPECIFIC
);
116 ps3vuart_bus_attach(device_t dev
)
118 struct ps3vuart_bus_softc
*sc
= device_get_softc(dev
);
123 PS3VUART_BUS_LOCK_INIT(sc
);
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
);
133 device_printf(dev
, "Could not allocate IRQ\n");
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
);
142 device_printf(dev
, "Could not setup IRQ (%d)\n", err
);
148 err
= ps3vuart_bus_add_children(sc
);
150 device_printf(dev
, "Could not add children (%d)\n", err
);
154 err
= bus_generic_attach(sc
->sc_dev
);
156 goto delete_children
;
162 ps3vuart_bus_delete_children(sc
);
166 bus_teardown_intr(dev
, sc
->sc_irq
, sc
->sc_irq_ctx
);
170 bus_release_resource(dev
, SYS_RES_IRQ
, sc
->sc_irq_id
, sc
->sc_irq
);
174 PS3VUART_BUS_LOCK_DESTROY(sc
);
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
);
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
);
201 ps3vuart_bus_print_child(device_t dev
, device_t child
)
203 struct ps3vuart_bus_info
*info
= device_get_ivars(child
);
206 retval
= bus_print_child_header(dev
, child
);
207 retval
+= printf(" at port %d", info
->port
);
208 retval
+= bus_print_child_footer(dev
, child
);
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
);
220 case PS3VUART_BUS_IVAR_TYPE
:
221 *result
= info
->type
;
223 case PS3VUART_BUS_IVAR_PORT
:
224 *result
= info
->port
;
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
;
242 if (*rid
== PS3VUART_BUS_IRQ_RID
)
251 ps3vuart_bus_release_resource(device_t dev
, device_t child
, int type
, int rid
,
256 if (rid
!= PS3VUART_BUS_IRQ_RID
)
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
);
277 sc
->sc_intr
[port
].i_ithread
= ithread
;
278 sc
->sc_intr
[port
].i_arg
= arg
;
284 ps3vuart_bus_teardown_intr(device_t dev
, device_t child
, struct resource
*irq
,
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
;
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
);
308 ps3vuart_bus_intr(void *arg
)
310 struct ps3vuart_bus_softc
*sc
= (struct ps3vuart_bus_softc
*) arg
;
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
);
330 ps3vuart_bus_add_children(struct ps3vuart_bus_softc
*sc
)
332 uint64_t lpar_id
, port
, laid
, junk
;
335 lv1_get_logical_partition_id(&lpar_id
);
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
);
345 err
= ps3vuart_bus_add_child(sc
, PS3VUART_BUS_TYPE_AV
, port
);
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
);
358 err
= ps3vuart_bus_add_child(sc
, PS3VUART_BUS_TYPE_SM
, port
);
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
);
380 ps3vuart_bus_delete_children(sc
);
386 ps3vuart_bus_delete_children(struct ps3vuart_bus_softc
*sc
)
391 if (device_get_children(sc
->sc_dev
, &children
, &nchildren
))
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
);
403 ps3vuart_bus_add_child(struct ps3vuart_bus_softc
*sc
,
406 struct ps3vuart_bus_info
*info
;
410 info
= malloc(sizeof(*info
), M_PS3VUART_BUS
, M_WAITOK
| M_ZERO
);
417 child
= device_add_child(sc
->sc_dev
, NULL
, -1);
423 device_set_ivars(child
, info
);
429 free(info
, M_PS3VUART_BUS
);
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
),
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
),
452 static driver_t ps3vuart_bus_driver
= {
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);