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