]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/powerpc/ps3/ps3bus.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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/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
54 static void     ps3bus_identify(driver_t *, device_t);
55 static int      ps3bus_probe(device_t);
56 static int      ps3bus_attach(device_t);
57 static int      ps3bus_print_child(device_t dev, device_t child);
58 static int      ps3bus_read_ivar(device_t bus, device_t child, int which,
59                     uintptr_t *result);
60 static 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);
63 static int      ps3bus_activate_resource(device_t bus, device_t child, int type,
64                     int rid, struct resource *res);
65 static bus_dma_tag_t ps3bus_get_dma_tag(device_t dev, device_t child);
66 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,
67                     bus_addr_t boundary, void *cookie);
68 static int      ps3_iommu_unmap(device_t dev, bus_dma_segment_t *segs,
69                     int nsegs, void *cookie);
70 static int      ps3_gettime(device_t dev, struct timespec *ts);
71 static int      ps3_settime(device_t dev, struct timespec *ts);
72
73 struct 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
88 static MALLOC_DEFINE(M_PS3BUS, "ps3bus", "PS3 system bus device information");
89
90 enum ps3bus_irq_type {
91         SB_IRQ = 2,
92         OHCI_IRQ = 3,
93         EHCI_IRQ = 4,
94 };
95
96 enum ps3bus_reg_type {
97         OHCI_REG = 3,
98         EHCI_REG = 4,
99 };
100
101 static 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
128 struct ps3bus_softc {
129         struct rman sc_mem_rman;
130         struct rman sc_intr_rman;
131         struct mem_region *regions;
132         int rcount;
133 };
134
135 static driver_t ps3bus_driver = {
136         "ps3bus",
137         ps3bus_methods,
138         sizeof(struct ps3bus_softc)
139 };
140
141 static devclass_t ps3bus_devclass;
142
143 DRIVER_MODULE(ps3bus, nexus, ps3bus_driver, ps3bus_devclass, 0, 0);
144
145 static void
146 ps3bus_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
155 static int 
156 ps3bus_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
165 static void
166 ps3bus_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
247 static void
248 ps3bus_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
318 static int 
319 ps3bus_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
476 static int
477 ps3bus_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
493 static int
494 ps3bus_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
524 static struct resource *
525 ps3bus_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
608 static int
609 ps3bus_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
637 static bus_dma_tag_t
638 ps3bus_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
699 fail:
700         mtx_unlock(&dinfo->iommu_mtx);
701
702         if (err)
703                 return (NULL);
704
705         return (dinfo->dma_tag);
706 }
707
708 static int
709 ps3_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
735 static int
736 ps3_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
744 static int
745 ps3_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         
759 static int
760 ps3_settime(device_t dev, struct timespec *ts)
761 {
762         return (-1);
763 }
764