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