ps3bus.c revision 271114
1/*-
2 * Copyright (C) 2010 Nathan Whitehorn
3 * Copyright (C) 2011 glevand (geoffrey.levand@mail.ru)
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 ``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 TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/10/sys/powerpc/ps3/ps3bus.c 271114 2014-09-04 18:28:30Z nwhitehorn $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/kernel.h>
33#include <sys/module.h>
34#include <sys/malloc.h>
35#include <sys/bus.h>
36#include <sys/clock.h>
37#include <sys/cpu.h>
38#include <sys/resource.h>
39#include <sys/rman.h>
40
41#include <vm/vm.h>
42#include <vm/pmap.h>
43
44#include <machine/bus.h>
45#include <machine/platform.h>
46#include <machine/pmap.h>
47#include <machine/resource.h>
48
49#include "ps3bus.h"
50#include "ps3-hvcall.h"
51#include "iommu_if.h"
52#include "clock_if.h"
53
54static void	ps3bus_identify(driver_t *, device_t);
55static int	ps3bus_probe(device_t);
56static int	ps3bus_attach(device_t);
57static int	ps3bus_print_child(device_t dev, device_t child);
58static int	ps3bus_read_ivar(device_t bus, device_t child, int which,
59		    uintptr_t *result);
60static struct resource *ps3bus_alloc_resource(device_t bus, device_t child,
61		    int type, int *rid, u_long start, u_long end,
62		    u_long count, u_int flags);
63static int	ps3bus_activate_resource(device_t bus, device_t child, int type,
64		    int rid, struct resource *res);
65static bus_dma_tag_t ps3bus_get_dma_tag(device_t dev, device_t child);
66static int	ps3_iommu_map(device_t dev, bus_dma_segment_t *segs, int *nsegs,		    bus_addr_t min, bus_addr_t max, bus_size_t alignment,
67		    bus_addr_t boundary, void *cookie);
68static int	ps3_iommu_unmap(device_t dev, bus_dma_segment_t *segs,
69		    int nsegs, void *cookie);
70static int	ps3_gettime(device_t dev, struct timespec *ts);
71static int	ps3_settime(device_t dev, struct timespec *ts);
72
73struct ps3bus_devinfo {
74	int bus;
75	int dev;
76	uint64_t bustype;
77	uint64_t devtype;
78	int busidx;
79	int devidx;
80
81	struct resource_list resources;
82	bus_dma_tag_t dma_tag;
83
84	struct mtx iommu_mtx;
85	bus_addr_t dma_base[4];
86};
87
88static MALLOC_DEFINE(M_PS3BUS, "ps3bus", "PS3 system bus device information");
89
90enum ps3bus_irq_type {
91	SB_IRQ = 2,
92	OHCI_IRQ = 3,
93	EHCI_IRQ = 4,
94};
95
96enum ps3bus_reg_type {
97	OHCI_REG = 3,
98	EHCI_REG = 4,
99};
100
101static device_method_t ps3bus_methods[] = {
102	/* Device interface */
103	DEVMETHOD(device_identify,	ps3bus_identify),
104	DEVMETHOD(device_probe,		ps3bus_probe),
105	DEVMETHOD(device_attach,	ps3bus_attach),
106
107	/* Bus interface */
108	DEVMETHOD(bus_add_child,	bus_generic_add_child),
109	DEVMETHOD(bus_get_dma_tag,	ps3bus_get_dma_tag),
110	DEVMETHOD(bus_print_child,	ps3bus_print_child),
111	DEVMETHOD(bus_read_ivar,	ps3bus_read_ivar),
112	DEVMETHOD(bus_alloc_resource,	ps3bus_alloc_resource),
113	DEVMETHOD(bus_activate_resource, ps3bus_activate_resource),
114	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
115	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
116
117	/* IOMMU interface */
118	DEVMETHOD(iommu_map,		ps3_iommu_map),
119	DEVMETHOD(iommu_unmap,		ps3_iommu_unmap),
120
121	/* Clock interface */
122	DEVMETHOD(clock_gettime,	ps3_gettime),
123	DEVMETHOD(clock_settime,	ps3_settime),
124
125	DEVMETHOD_END
126};
127
128struct ps3bus_softc {
129	struct rman sc_mem_rman;
130	struct rman sc_intr_rman;
131	struct mem_region *regions;
132	int rcount;
133};
134
135static driver_t ps3bus_driver = {
136	"ps3bus",
137	ps3bus_methods,
138	sizeof(struct ps3bus_softc)
139};
140
141static devclass_t ps3bus_devclass;
142
143DRIVER_MODULE(ps3bus, nexus, ps3bus_driver, ps3bus_devclass, 0, 0);
144
145static void
146ps3bus_identify(driver_t *driver, device_t parent)
147{
148	if (strcmp(installed_platform(), "ps3") != 0)
149		return;
150
151	if (device_find_child(parent, "ps3bus", -1) == NULL)
152		BUS_ADD_CHILD(parent, 0, "ps3bus", 0);
153}
154
155static int
156ps3bus_probe(device_t dev)
157{
158	/* Do not attach to any OF nodes that may be present */
159
160	device_set_desc(dev, "Playstation 3 System Bus");
161
162	return (BUS_PROBE_NOWILDCARD);
163}
164
165static void
166ps3bus_resources_init(struct rman *rm, int bus_index, int dev_index,
167    struct ps3bus_devinfo *dinfo)
168{
169	uint64_t irq_type, irq, outlet;
170	uint64_t reg_type, paddr, len;
171	uint64_t ppe, junk;
172	int i, result;
173	int thread;
174
175	resource_list_init(&dinfo->resources);
176
177	lv1_get_logical_ppe_id(&ppe);
178	thread = 32 - fls(mfctrl());
179
180	/* Scan for interrupts */
181	for (i = 0; i < 10; i++) {
182		result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
183		    (lv1_repository_string("bus") >> 32) | bus_index,
184		    lv1_repository_string("dev") | dev_index,
185		    lv1_repository_string("intr") | i, 0, &irq_type, &irq);
186
187		if (result != 0)
188			break;
189
190		switch (irq_type) {
191		case SB_IRQ:
192			lv1_construct_event_receive_port(&outlet);
193			lv1_connect_irq_plug_ext(ppe, thread, outlet, outlet,
194			    0);
195			lv1_connect_interrupt_event_receive_port(dinfo->bus,
196			    dinfo->dev, outlet, irq);
197			break;
198		case OHCI_IRQ:
199		case EHCI_IRQ:
200			lv1_construct_io_irq_outlet(irq, &outlet);
201			lv1_connect_irq_plug_ext(ppe, thread, outlet, outlet,
202			    0);
203			break;
204		default:
205			printf("Unknown IRQ type %ld for device %d.%d\n",
206			    irq_type, dinfo->bus, dinfo->dev);
207			break;
208		}
209
210		resource_list_add(&dinfo->resources, SYS_RES_IRQ, i,
211		    outlet, outlet, 1);
212	}
213
214	/* Scan for registers */
215	for (i = 0; i < 10; i++) {
216		result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
217		    (lv1_repository_string("bus") >> 32) | bus_index,
218		    lv1_repository_string("dev") | dev_index,
219		    lv1_repository_string("reg") | i,
220		    lv1_repository_string("type"), &reg_type, &junk);
221
222		if (result != 0)
223			break;
224
225		result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
226		    (lv1_repository_string("bus") >> 32) | bus_index,
227		    lv1_repository_string("dev") | dev_index,
228		    lv1_repository_string("reg") | i,
229		    lv1_repository_string("data"), &paddr, &len);
230
231		result = lv1_map_device_mmio_region(dinfo->bus, dinfo->dev,
232		    paddr, len, 12 /* log_2(4 KB) */, &paddr);
233
234		if (result != 0) {
235			printf("Mapping registers failed for device "
236			    "%d.%d (%ld.%ld): %d\n", dinfo->bus, dinfo->dev,
237			    dinfo->bustype, dinfo->devtype, result);
238			continue;
239		}
240
241		rman_manage_region(rm, paddr, paddr + len - 1);
242		resource_list_add(&dinfo->resources, SYS_RES_MEMORY, i,
243		    paddr, paddr + len, len);
244	}
245}
246
247static void
248ps3bus_resources_init_by_type(struct rman *rm, int bus_index, int dev_index,
249    uint64_t irq_type, uint64_t reg_type, struct ps3bus_devinfo *dinfo)
250{
251	uint64_t _irq_type, irq, outlet;
252	uint64_t _reg_type, paddr, len;
253	uint64_t ppe, junk;
254	int i, result;
255	int thread;
256
257	resource_list_init(&dinfo->resources);
258
259	lv1_get_logical_ppe_id(&ppe);
260	thread = 32 - fls(mfctrl());
261
262	/* Scan for interrupts */
263	for (i = 0; i < 10; i++) {
264		result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
265		    (lv1_repository_string("bus") >> 32) | bus_index,
266		    lv1_repository_string("dev") | dev_index,
267		    lv1_repository_string("intr") | i, 0, &_irq_type, &irq);
268
269		if (result != 0)
270			break;
271
272		if (_irq_type != irq_type)
273			continue;
274
275		lv1_construct_io_irq_outlet(irq, &outlet);
276		lv1_connect_irq_plug_ext(ppe, thread, outlet, outlet,
277		    0);
278		resource_list_add(&dinfo->resources, SYS_RES_IRQ, i,
279		    outlet, outlet, 1);
280	}
281
282	/* Scan for registers */
283	for (i = 0; i < 10; i++) {
284		result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
285		    (lv1_repository_string("bus") >> 32) | bus_index,
286		    lv1_repository_string("dev") | dev_index,
287		    lv1_repository_string("reg") | i,
288		    lv1_repository_string("type"), &_reg_type, &junk);
289
290		if (result != 0)
291			break;
292
293		if (_reg_type != reg_type)
294			continue;
295
296		result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
297		    (lv1_repository_string("bus") >> 32) | bus_index,
298		    lv1_repository_string("dev") | dev_index,
299		    lv1_repository_string("reg") | i,
300		    lv1_repository_string("data"), &paddr, &len);
301
302		result = lv1_map_device_mmio_region(dinfo->bus, dinfo->dev,
303		    paddr, len, 12 /* log_2(4 KB) */, &paddr);
304
305		if (result != 0) {
306			printf("Mapping registers failed for device "
307			    "%d.%d (%ld.%ld): %d\n", dinfo->bus, dinfo->dev,
308			    dinfo->bustype, dinfo->devtype, result);
309			break;
310		}
311
312		rman_manage_region(rm, paddr, paddr + len - 1);
313		resource_list_add(&dinfo->resources, SYS_RES_MEMORY, i,
314		    paddr, paddr + len, len);
315	}
316}
317
318static int
319ps3bus_attach(device_t self)
320{
321	struct ps3bus_softc *sc;
322	struct ps3bus_devinfo *dinfo;
323	int bus_index, dev_index, result;
324	uint64_t bustype, bus, devs;
325	uint64_t dev, devtype;
326	uint64_t junk;
327	device_t cdev;
328
329	sc = device_get_softc(self);
330	sc->sc_mem_rman.rm_type = RMAN_ARRAY;
331	sc->sc_mem_rman.rm_descr = "PS3Bus Memory Mapped I/O";
332	sc->sc_intr_rman.rm_type = RMAN_ARRAY;
333	sc->sc_intr_rman.rm_descr = "PS3Bus Interrupts";
334	rman_init(&sc->sc_mem_rman);
335	rman_init(&sc->sc_intr_rman);
336	rman_manage_region(&sc->sc_intr_rman, 0, ~0);
337
338	/* Get memory regions for DMA */
339	mem_regions(&sc->regions, &sc->rcount, &sc->regions, &sc->rcount);
340
341	/*
342	 * Probe all the PS3's buses.
343	 */
344
345	for (bus_index = 0; bus_index < 5; bus_index++) {
346		result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
347		    (lv1_repository_string("bus") >> 32) | bus_index,
348		    lv1_repository_string("type"), 0, 0, &bustype, &junk);
349
350		if (result != 0)
351			continue;
352
353		result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
354		    (lv1_repository_string("bus") >> 32) | bus_index,
355		    lv1_repository_string("id"), 0, 0, &bus, &junk);
356
357		if (result != 0)
358			continue;
359
360		result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
361		    (lv1_repository_string("bus") >> 32) | bus_index,
362		    lv1_repository_string("num_dev"), 0, 0, &devs, &junk);
363
364		for (dev_index = 0; dev_index < devs; dev_index++) {
365			result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
366			    (lv1_repository_string("bus") >> 32) | bus_index,
367			    lv1_repository_string("dev") | dev_index,
368			    lv1_repository_string("type"), 0, &devtype, &junk);
369
370			if (result != 0)
371				continue;
372
373			result = lv1_get_repository_node_value(PS3_LPAR_ID_PME,
374			    (lv1_repository_string("bus") >> 32) | bus_index,
375			    lv1_repository_string("dev") | dev_index,
376			    lv1_repository_string("id"), 0, &dev, &junk);
377
378			if (result != 0)
379				continue;
380
381			switch (devtype) {
382			case PS3_DEVTYPE_USB:
383				/* USB device has OHCI and EHCI USB host controllers */
384
385				lv1_open_device(bus, dev, 0);
386
387				/* OHCI host controller */
388
389				dinfo = malloc(sizeof(*dinfo), M_PS3BUS,
390				    M_WAITOK | M_ZERO);
391
392				dinfo->bus = bus;
393				dinfo->dev = dev;
394				dinfo->bustype = bustype;
395				dinfo->devtype = devtype;
396				dinfo->busidx = bus_index;
397				dinfo->devidx = dev_index;
398
399				ps3bus_resources_init_by_type(&sc->sc_mem_rman, bus_index,
400				    dev_index, OHCI_IRQ, OHCI_REG, dinfo);
401
402				cdev = device_add_child(self, "ohci", -1);
403				if (cdev == NULL) {
404					device_printf(self,
405					    "device_add_child failed\n");
406					free(dinfo, M_PS3BUS);
407					continue;
408				}
409
410				mtx_init(&dinfo->iommu_mtx, "iommu", NULL, MTX_DEF);
411				device_set_ivars(cdev, dinfo);
412
413				/* EHCI host controller */
414
415				dinfo = malloc(sizeof(*dinfo), M_PS3BUS,
416				    M_WAITOK | M_ZERO);
417
418				dinfo->bus = bus;
419				dinfo->dev = dev;
420				dinfo->bustype = bustype;
421				dinfo->devtype = devtype;
422				dinfo->busidx = bus_index;
423				dinfo->devidx = dev_index;
424
425				ps3bus_resources_init_by_type(&sc->sc_mem_rman, bus_index,
426				    dev_index, EHCI_IRQ, EHCI_REG, dinfo);
427
428				cdev = device_add_child(self, "ehci", -1);
429				if (cdev == NULL) {
430					device_printf(self,
431					    "device_add_child failed\n");
432					free(dinfo, M_PS3BUS);
433					continue;
434				}
435
436				mtx_init(&dinfo->iommu_mtx, "iommu", NULL, MTX_DEF);
437				device_set_ivars(cdev, dinfo);
438				break;
439			default:
440				dinfo = malloc(sizeof(*dinfo), M_PS3BUS,
441				    M_WAITOK | M_ZERO);
442
443				dinfo->bus = bus;
444				dinfo->dev = dev;
445				dinfo->bustype = bustype;
446				dinfo->devtype = devtype;
447				dinfo->busidx = bus_index;
448				dinfo->devidx = dev_index;
449
450				if (dinfo->bustype == PS3_BUSTYPE_SYSBUS ||
451				    dinfo->bustype == PS3_BUSTYPE_STORAGE)
452					lv1_open_device(bus, dev, 0);
453
454				ps3bus_resources_init(&sc->sc_mem_rman, bus_index,
455				    dev_index, dinfo);
456
457				cdev = device_add_child(self, NULL, -1);
458				if (cdev == NULL) {
459					device_printf(self,
460					    "device_add_child failed\n");
461					free(dinfo, M_PS3BUS);
462					continue;
463				}
464
465				mtx_init(&dinfo->iommu_mtx, "iommu", NULL, MTX_DEF);
466				device_set_ivars(cdev, dinfo);
467			}
468		}
469	}
470
471	clock_register(self, 1000);
472
473	return (bus_generic_attach(self));
474}
475
476static int
477ps3bus_print_child(device_t dev, device_t child)
478{
479	struct ps3bus_devinfo *dinfo = device_get_ivars(child);
480	int retval = 0;
481
482	retval += bus_print_child_header(dev, child);
483	retval += resource_list_print_type(&dinfo->resources, "mem",
484	    SYS_RES_MEMORY, "%#lx");
485	retval += resource_list_print_type(&dinfo->resources, "irq",
486	    SYS_RES_IRQ, "%ld");
487
488	retval += bus_print_child_footer(dev, child);
489
490	return (retval);
491}
492
493static int
494ps3bus_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
495{
496	struct ps3bus_devinfo *dinfo = device_get_ivars(child);
497
498	switch (which) {
499	case PS3BUS_IVAR_BUS:
500		*result = dinfo->bus;
501		break;
502	case PS3BUS_IVAR_DEVICE:
503		*result = dinfo->dev;
504		break;
505	case PS3BUS_IVAR_BUSTYPE:
506		*result = dinfo->bustype;
507		break;
508	case PS3BUS_IVAR_DEVTYPE:
509		*result = dinfo->devtype;
510		break;
511	case PS3BUS_IVAR_BUSIDX:
512		*result = dinfo->busidx;
513		break;
514	case PS3BUS_IVAR_DEVIDX:
515		*result = dinfo->devidx;
516		break;
517	default:
518		return (EINVAL);
519	}
520
521	return (0);
522}
523
524static struct resource *
525ps3bus_alloc_resource(device_t bus, device_t child, int type, int *rid,
526    u_long start, u_long end, u_long count, u_int flags)
527{
528	struct	ps3bus_devinfo *dinfo;
529	struct	ps3bus_softc *sc;
530	int	needactivate;
531        struct	resource *rv;
532        struct	rman *rm;
533        u_long	adjstart, adjend, adjcount;
534        struct	resource_list_entry *rle;
535
536	sc = device_get_softc(bus);
537	dinfo = device_get_ivars(child);
538	needactivate = flags & RF_ACTIVE;
539	flags &= ~RF_ACTIVE;
540
541	switch (type) {
542	case SYS_RES_MEMORY:
543		rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY,
544		    *rid);
545		if (rle == NULL) {
546			device_printf(bus, "no rle for %s memory %d\n",
547				      device_get_nameunit(child), *rid);
548			return (NULL);
549		}
550
551		if (start < rle->start)
552			adjstart = rle->start;
553		else if (start > rle->end)
554			adjstart = rle->end;
555		else
556			adjstart = start;
557
558		if (end < rle->start)
559			adjend = rle->start;
560		else if (end > rle->end)
561			adjend = rle->end;
562		else
563			adjend = end;
564
565		adjcount = adjend - adjstart;
566
567		rm = &sc->sc_mem_rman;
568		break;
569	case SYS_RES_IRQ:
570		rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ,
571		    *rid);
572		rm = &sc->sc_intr_rman;
573		adjstart = rle->start;
574		adjcount = ulmax(count, rle->count);
575		adjend = ulmax(rle->end, rle->start + adjcount - 1);
576		break;
577	default:
578		device_printf(bus, "unknown resource request from %s\n",
579			      device_get_nameunit(child));
580		return (NULL);
581        }
582
583	rv = rman_reserve_resource(rm, adjstart, adjend, adjcount, flags,
584	    child);
585	if (rv == NULL) {
586		device_printf(bus,
587			"failed to reserve resource %#lx - %#lx (%#lx)"
588			" for %s\n", adjstart, adjend, adjcount,
589			device_get_nameunit(child));
590		return (NULL);
591	}
592
593	rman_set_rid(rv, *rid);
594
595	if (needactivate) {
596		if (bus_activate_resource(child, type, *rid, rv) != 0) {
597			device_printf(bus,
598				"failed to activate resource for %s\n",
599				device_get_nameunit(child));
600				rman_release_resource(rv);
601			return (NULL);
602		}
603	}
604
605	return (rv);
606}
607
608static int
609ps3bus_activate_resource(device_t bus, device_t child, int type, int rid,
610    struct resource *res)
611{
612	void *p;
613
614	if (type == SYS_RES_IRQ)
615		return (bus_activate_resource(bus, type, rid, res));
616
617	if (type == SYS_RES_MEMORY) {
618		vm_offset_t start;
619
620		start = (vm_offset_t) rman_get_start(res);
621
622		if (bootverbose)
623			printf("ps3 mapdev: start %zx, len %ld\n", start,
624			       rman_get_size(res));
625
626		p = pmap_mapdev(start, (vm_size_t) rman_get_size(res));
627		if (p == NULL)
628			return (ENOMEM);
629		rman_set_virtual(res, p);
630		rman_set_bustag(res, &bs_be_tag);
631		rman_set_bushandle(res, (u_long)p);
632	}
633
634	return (rman_activate_resource(res));
635}
636
637static bus_dma_tag_t
638ps3bus_get_dma_tag(device_t dev, device_t child)
639{
640	struct ps3bus_devinfo *dinfo = device_get_ivars(child);
641	struct ps3bus_softc *sc = device_get_softc(dev);
642	int i, err, flags, pagesize;
643
644	if (dinfo->bustype != PS3_BUSTYPE_SYSBUS &&
645	    dinfo->bustype != PS3_BUSTYPE_STORAGE)
646		return (bus_get_dma_tag(dev));
647
648	mtx_lock(&dinfo->iommu_mtx);
649	if (dinfo->dma_tag != NULL) {
650		mtx_unlock(&dinfo->iommu_mtx);
651		return (dinfo->dma_tag);
652	}
653
654	flags = 0; /* 32-bit mode */
655	if (dinfo->bustype == PS3_BUSTYPE_SYSBUS &&
656	    dinfo->devtype == PS3_DEVTYPE_USB)
657		flags = 2; /* 8-bit mode */
658
659	pagesize = 24; /* log_2(16 MB) */
660	if (dinfo->bustype == PS3_BUSTYPE_STORAGE)
661		pagesize = 12; /* 4 KB */
662
663	for (i = 0; i < sc->rcount; i++) {
664		err = lv1_allocate_device_dma_region(dinfo->bus, dinfo->dev,
665		    sc->regions[i].mr_size, pagesize, flags,
666		    &dinfo->dma_base[i]);
667		if (err != 0) {
668			device_printf(child,
669			    "could not allocate DMA region %d: %d\n", i, err);
670			goto fail;
671		}
672
673		err = lv1_map_device_dma_region(dinfo->bus, dinfo->dev,
674		    sc->regions[i].mr_start, dinfo->dma_base[i],
675		    sc->regions[i].mr_size,
676		    0xf800000000000800UL /* Cell Handbook Figure 7.3.4.1 */);
677		if (err != 0) {
678			device_printf(child,
679			    "could not map DMA region %d: %d\n", i, err);
680			goto fail;
681		}
682	}
683
684	err = bus_dma_tag_create(bus_get_dma_tag(dev),
685	    1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR,
686	    NULL, NULL, BUS_SPACE_MAXSIZE, 0, BUS_SPACE_MAXSIZE,
687	    0, NULL, NULL, &dinfo->dma_tag);
688
689	/*
690	 * Note: storage devices have IOMMU mappings set up by the hypervisor,
691	 * but use physical, non-translated addresses. The above IOMMU
692	 * initialization is necessary for the hypervisor to be able to set up
693	 * the mappings, but actual DMA mappings should not use the IOMMU
694	 * routines.
695	 */
696	if (dinfo->bustype != PS3_BUSTYPE_STORAGE)
697		bus_dma_tag_set_iommu(dinfo->dma_tag, dev, dinfo);
698
699fail:
700	mtx_unlock(&dinfo->iommu_mtx);
701
702	if (err)
703		return (NULL);
704
705	return (dinfo->dma_tag);
706}
707
708static int
709ps3_iommu_map(device_t dev, bus_dma_segment_t *segs, int *nsegs,
710    bus_addr_t min, bus_addr_t max, bus_size_t alignment, bus_addr_t boundary,
711    void *cookie)
712{
713	struct ps3bus_devinfo *dinfo = cookie;
714	struct ps3bus_softc *sc = device_get_softc(dev);
715	int i, j;
716
717	for (i = 0; i < *nsegs; i++) {
718		for (j = 0; j < sc->rcount; j++) {
719			if (segs[i].ds_addr >= sc->regions[j].mr_start &&
720			    segs[i].ds_addr < sc->regions[j].mr_start +
721			      sc->regions[j].mr_size)
722				break;
723		}
724		KASSERT(j < sc->rcount,
725		    ("Trying to map address %#lx not in physical memory",
726		    segs[i].ds_addr));
727
728		segs[i].ds_addr = dinfo->dma_base[j] +
729		    (segs[i].ds_addr - sc->regions[j].mr_start);
730	}
731
732	return (0);
733}
734
735static int
736ps3_iommu_unmap(device_t dev, bus_dma_segment_t *segs, int nsegs, void *cookie)
737{
738
739	return (0);
740}
741
742#define Y2K 946684800
743
744static int
745ps3_gettime(device_t dev, struct timespec *ts)
746{
747	uint64_t rtc, tb;
748	int result;
749
750	result = lv1_get_rtc(&rtc, &tb);
751	if (result)
752		return (result);
753
754	ts->tv_sec = rtc + Y2K;
755	ts->tv_nsec = 0;
756	return (0);
757}
758
759static int
760ps3_settime(device_t dev, struct timespec *ts)
761{
762	return (-1);
763}
764
765