2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (C) 2002 Benno Rice.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/module.h>
33 #include <sys/kernel.h>
35 #include <dev/ofw/openfirm.h>
36 #include <dev/ofw/ofw_pci.h>
37 #include <dev/ofw/ofw_bus.h>
38 #include <dev/ofw/ofw_bus_subr.h>
40 #include <dev/pci/pcivar.h>
41 #include <dev/pci/pcireg.h>
43 #include <machine/bus.h>
44 #include <machine/intr_machdep.h>
45 #include <machine/md_var.h>
46 #include <machine/pio.h>
47 #include <machine/resource.h>
51 #include <powerpc/powermac/uninorthvar.h>
57 * Driver for the Uninorth chip itself.
60 static MALLOC_DEFINE(M_UNIN
, "unin", "unin device information");
66 static int unin_chip_probe(device_t
);
67 static int unin_chip_attach(device_t
);
72 static int unin_chip_print_child(device_t dev
, device_t child
);
73 static void unin_chip_probe_nomatch(device_t
, device_t
);
74 static struct rman
*unin_chip_get_rman(device_t
, int, u_int
);
75 static struct resource
*unin_chip_alloc_resource(device_t
, device_t
, int, int *,
76 rman_res_t
, rman_res_t
,
78 static int unin_chip_adjust_resource(device_t
, device_t
,
79 struct resource
*, rman_res_t
,
81 static int unin_chip_activate_resource(device_t
, device_t
,
83 static int unin_chip_deactivate_resource(device_t
, device_t
,
85 static int unin_chip_map_resource(device_t
, device_t
, struct resource
*,
86 struct resource_map_request
*,
87 struct resource_map
*);
88 static int unin_chip_unmap_resource(device_t
, device_t
, struct resource
*,
89 struct resource_map
*);
90 static int unin_chip_release_resource(device_t
, device_t
, struct resource
*);
91 static struct resource_list
*unin_chip_get_resource_list (device_t
, device_t
);
97 static ofw_bus_get_devinfo_t unin_chip_get_devinfo
;
103 static void unin_enable_gmac(device_t dev
);
104 static void unin_enable_mpic(device_t dev
);
109 static device_method_t unin_chip_methods
[] = {
110 /* Device interface */
111 DEVMETHOD(device_probe
, unin_chip_probe
),
112 DEVMETHOD(device_attach
, unin_chip_attach
),
115 DEVMETHOD(bus_print_child
, unin_chip_print_child
),
116 DEVMETHOD(bus_probe_nomatch
, unin_chip_probe_nomatch
),
117 DEVMETHOD(bus_setup_intr
, bus_generic_setup_intr
),
118 DEVMETHOD(bus_teardown_intr
, bus_generic_teardown_intr
),
120 DEVMETHOD(bus_get_rman
, unin_chip_get_rman
),
121 DEVMETHOD(bus_alloc_resource
, unin_chip_alloc_resource
),
122 DEVMETHOD(bus_adjust_resource
, unin_chip_adjust_resource
),
123 DEVMETHOD(bus_release_resource
, unin_chip_release_resource
),
124 DEVMETHOD(bus_activate_resource
, unin_chip_activate_resource
),
125 DEVMETHOD(bus_deactivate_resource
, unin_chip_deactivate_resource
),
126 DEVMETHOD(bus_map_resource
, unin_chip_map_resource
),
127 DEVMETHOD(bus_unmap_resource
, unin_chip_unmap_resource
),
128 DEVMETHOD(bus_get_resource_list
, unin_chip_get_resource_list
),
130 DEVMETHOD(bus_child_pnpinfo
, ofw_bus_gen_child_pnpinfo
),
132 /* ofw_bus interface */
133 DEVMETHOD(ofw_bus_get_devinfo
, unin_chip_get_devinfo
),
134 DEVMETHOD(ofw_bus_get_compat
, ofw_bus_gen_get_compat
),
135 DEVMETHOD(ofw_bus_get_model
, ofw_bus_gen_get_model
),
136 DEVMETHOD(ofw_bus_get_name
, ofw_bus_gen_get_name
),
137 DEVMETHOD(ofw_bus_get_node
, ofw_bus_gen_get_node
),
138 DEVMETHOD(ofw_bus_get_type
, ofw_bus_gen_get_type
),
142 static driver_t unin_chip_driver
= {
145 sizeof(struct unin_chip_softc
)
149 * Assume there is only one unin chip in a PowerMac, so that pmu.c functions can
150 * suspend the chip after the whole rest of the device tree is suspended, not
153 static device_t unin_chip
;
155 EARLY_DRIVER_MODULE(unin
, ofwbus
, unin_chip_driver
, 0, 0, BUS_PASS_BUS
);
158 * Add an interrupt to the dev's resource list if present
161 unin_chip_add_intr(phandle_t devnode
, struct unin_chip_devinfo
*dinfo
)
168 if (dinfo
->udi_ninterrupts
>= 6) {
169 printf("unin: device has more than 6 interrupts\n");
173 nintr
= OF_getprop_alloc_multi(devnode
, "interrupts", sizeof(*intr
),
176 nintr
= OF_getprop_alloc_multi(devnode
, "AAPL,interrupts",
177 sizeof(*intr
), (void **)&intr
);
185 if (OF_getprop(devnode
, "interrupt-parent", &iparent
, sizeof(iparent
))
187 panic("Interrupt but no interrupt parent!\n");
189 if (OF_searchprop(iparent
, "#interrupt-cells", &icells
, sizeof(icells
))
193 for (i
= 0; i
< nintr
; i
+=icells
) {
194 u_int irq
= MAP_IRQ(iparent
, intr
[i
]);
196 resource_list_add(&dinfo
->udi_resources
, SYS_RES_IRQ
,
197 dinfo
->udi_ninterrupts
, irq
, irq
, 1);
200 powerpc_config_intr(irq
,
201 (intr
[i
+1] & 1) ? INTR_TRIGGER_LEVEL
:
202 INTR_TRIGGER_EDGE
, INTR_POLARITY_LOW
);
205 dinfo
->udi_interrupts
[dinfo
->udi_ninterrupts
] = irq
;
206 dinfo
->udi_ninterrupts
++;
211 unin_chip_add_reg(phandle_t devnode
, struct unin_chip_devinfo
*dinfo
)
213 struct unin_chip_reg
*reg
;
216 nreg
= OF_getprop_alloc_multi(devnode
, "reg", sizeof(*reg
), (void **)®
);
220 for (i
= 0; i
< nreg
; i
++) {
221 resource_list_add(&dinfo
->udi_resources
, SYS_RES_MEMORY
, i
,
223 reg
[i
].mr_base
+ reg
[i
].mr_size
,
229 unin_update_reg(device_t dev
, uint32_t regoff
, uint32_t set
, uint32_t clr
)
232 struct unin_chip_softc
*sc
;
235 sc
= device_get_softc(dev
);
236 reg
= (void *)(sc
->sc_addr
+ regoff
);
244 unin_enable_gmac(device_t dev
)
246 unin_update_reg(dev
, UNIN_CLOCKCNTL
, UNIN_CLOCKCNTL_GMAC
, 0);
250 unin_enable_mpic(device_t dev
)
252 unin_update_reg(dev
, UNIN_TOGGLE_REG
, UNIN_MPIC_RESET
| UNIN_MPIC_OUTPUT_ENABLE
, 0);
256 unin_chip_probe(device_t dev
)
260 name
= ofw_bus_get_name(dev
);
265 if (strcmp(name
, "uni-n") != 0 && strcmp(name
, "u3") != 0
266 && strcmp(name
, "u4") != 0)
269 device_set_desc(dev
, "Apple UniNorth System Controller");
274 unin_chip_attach(device_t dev
)
276 struct unin_chip_softc
*sc
;
277 struct unin_chip_devinfo
*dinfo
;
282 cell_t acells
, scells
;
288 sc
= device_get_softc(dev
);
289 root
= ofw_bus_get_node(dev
);
291 if (OF_getprop(root
, "reg", reg
, sizeof(reg
)) < 8)
295 OF_getprop(OF_parent(root
), "#address-cells", &acells
, sizeof(acells
));
296 OF_getprop(OF_parent(root
), "#size-cells", &scells
, sizeof(scells
));
299 sc
->sc_physaddr
= reg
[i
++];
301 sc
->sc_physaddr
<<= 32;
302 sc
->sc_physaddr
|= reg
[i
++];
304 sc
->sc_size
= reg
[i
++];
307 sc
->sc_size
|= reg
[i
++];
310 sc
->sc_mem_rman
.rm_type
= RMAN_ARRAY
;
311 sc
->sc_mem_rman
.rm_descr
= "UniNorth Device Memory";
313 error
= rman_init(&sc
->sc_mem_rman
);
316 device_printf(dev
, "rman_init() failed. error = %d\n", error
);
320 error
= rman_manage_region(&sc
->sc_mem_rman
, sc
->sc_physaddr
,
321 sc
->sc_physaddr
+ sc
->sc_size
- 1);
324 "rman_manage_region() failed. error = %d\n",
329 if (unin_chip
== NULL
)
333 * Iterate through the sub-devices
335 for (child
= OF_child(root
); child
!= 0; child
= OF_peer(child
)) {
336 dinfo
= malloc(sizeof(*dinfo
), M_UNIN
, M_WAITOK
| M_ZERO
);
337 if (ofw_bus_gen_setup_devinfo(&dinfo
->udi_obdinfo
, child
)
344 resource_list_init(&dinfo
->udi_resources
);
345 dinfo
->udi_ninterrupts
= 0;
346 unin_chip_add_intr(child
, dinfo
);
349 * Some Apple machines do have a bug in OF, they miss
350 * the interrupt entries on the U3 I2C node. That means they
351 * do not have an entry with number of interrupts nor the
352 * entry of the interrupt parent handle.
353 * We define an interrupt and hardwire it to the /u3/mpic
357 if (OF_getprop(child
, "name", name
, sizeof(name
)) <= 0)
358 device_printf(dev
, "device has no name!\n");
359 if (dinfo
->udi_ninterrupts
== 0 &&
360 (strcmp(name
, "i2c-bus") == 0 ||
361 strcmp(name
, "i2c") == 0)) {
362 if (OF_getprop(child
, "interrupt-parent", &iparent
,
363 sizeof(iparent
)) <= 0) {
364 iparent
= OF_finddevice("/u3/mpic");
365 device_printf(dev
, "Set /u3/mpic as iparent!\n");
367 /* Add an interrupt number 0 to the parent. */
368 irq
= MAP_IRQ(iparent
, 0);
369 resource_list_add(&dinfo
->udi_resources
, SYS_RES_IRQ
,
370 dinfo
->udi_ninterrupts
, irq
, irq
, 1);
371 dinfo
->udi_interrupts
[dinfo
->udi_ninterrupts
] = irq
;
372 dinfo
->udi_ninterrupts
++;
375 unin_chip_add_reg(child
, dinfo
);
377 cdev
= device_add_child(dev
, NULL
, DEVICE_UNIT_ANY
);
379 device_printf(dev
, "<%s>: device_add_child failed\n",
380 dinfo
->udi_obdinfo
.obd_name
);
381 resource_list_free(&dinfo
->udi_resources
);
382 ofw_bus_gen_destroy_devinfo(&dinfo
->udi_obdinfo
);
387 device_set_ivars(cdev
, dinfo
);
391 * Only map the first page, since that is where the registers
394 sc
->sc_addr
= (vm_offset_t
)pmap_mapdev(sc
->sc_physaddr
, PAGE_SIZE
);
396 sc
->sc_version
= *(u_int
*)sc
->sc_addr
;
397 device_printf(dev
, "Version %d\n", sc
->sc_version
);
400 * Enable the GMAC Ethernet cell and the integrated OpenPIC
401 * if Open Firmware says they are used.
403 for (child
= OF_child(root
); child
; child
= OF_peer(child
)) {
404 memset(compat
, 0, sizeof(compat
));
405 OF_getprop(child
, "compatible", compat
, sizeof(compat
));
406 if (strcmp(compat
, "gmac") == 0)
407 unin_enable_gmac(dev
);
408 if (strcmp(compat
, "chrp,open-pic") == 0)
409 unin_enable_mpic(dev
);
413 * GMAC lives under the PCI bus, so just check if enet is gmac.
415 child
= OF_finddevice("enet");
416 memset(compat
, 0, sizeof(compat
));
417 OF_getprop(child
, "compatible", compat
, sizeof(compat
));
418 if (strcmp(compat
, "gmac") == 0)
419 unin_enable_gmac(dev
);
421 bus_attach_children(dev
);
426 unin_chip_print_child(device_t dev
, device_t child
)
428 struct unin_chip_devinfo
*dinfo
;
429 struct resource_list
*rl
;
432 dinfo
= device_get_ivars(child
);
433 rl
= &dinfo
->udi_resources
;
435 retval
+= bus_print_child_header(dev
, child
);
437 retval
+= resource_list_print_type(rl
, "mem", SYS_RES_MEMORY
, "%#jx");
438 retval
+= resource_list_print_type(rl
, "irq", SYS_RES_IRQ
, "%jd");
440 retval
+= bus_print_child_footer(dev
, child
);
446 unin_chip_probe_nomatch(device_t dev
, device_t child
)
448 struct unin_chip_devinfo
*dinfo
;
449 struct resource_list
*rl
;
453 dinfo
= device_get_ivars(child
);
454 rl
= &dinfo
->udi_resources
;
456 if ((type
= ofw_bus_get_type(child
)) == NULL
)
458 device_printf(dev
, "<%s, %s>", type
, ofw_bus_get_name(child
));
459 resource_list_print_type(rl
, "mem", SYS_RES_MEMORY
, "%#jx");
460 resource_list_print_type(rl
, "irq", SYS_RES_IRQ
, "%jd");
461 printf(" (no driver attached)\n");
466 unin_chip_get_rman(device_t bus
, int type
, u_int flags
)
468 struct unin_chip_softc
*sc
;
470 sc
= device_get_softc(bus
);
474 return (&sc
->sc_mem_rman
);
480 static struct resource
*
481 unin_chip_alloc_resource(device_t bus
, device_t child
, int type
, int *rid
,
482 rman_res_t start
, rman_res_t end
, rman_res_t count
,
485 rman_res_t adjstart
, adjend
, adjcount
;
486 struct unin_chip_devinfo
*dinfo
;
487 struct resource_list_entry
*rle
;
489 dinfo
= device_get_ivars(child
);
494 rle
= resource_list_find(&dinfo
->udi_resources
, SYS_RES_MEMORY
,
497 device_printf(bus
, "no rle for %s memory %d\n",
498 device_get_nameunit(child
), *rid
);
502 rle
->end
= rle
->end
- 1; /* Hack? */
504 if (start
< rle
->start
)
505 adjstart
= rle
->start
;
506 else if (start
> rle
->end
)
511 if (end
< rle
->start
)
513 else if (end
> rle
->end
)
518 adjcount
= adjend
- adjstart
;
520 return (bus_generic_rman_alloc_resource(bus
, child
,
521 SYS_RES_MEMORY
, rid
, adjstart
, adjend
, adjcount
, flags
));
523 /* Check for passthrough from subattachments. */
524 if (device_get_parent(child
) != bus
)
525 return BUS_ALLOC_RESOURCE(device_get_parent(bus
), child
,
526 type
, rid
, start
, end
, count
,
529 rle
= resource_list_find(&dinfo
->udi_resources
, SYS_RES_IRQ
,
532 if (dinfo
->udi_ninterrupts
>= 6) {
534 "%s has more than 6 interrupts\n",
535 device_get_nameunit(child
));
538 resource_list_add(&dinfo
->udi_resources
, SYS_RES_IRQ
,
539 dinfo
->udi_ninterrupts
, start
, start
,
542 dinfo
->udi_interrupts
[dinfo
->udi_ninterrupts
] = start
;
543 dinfo
->udi_ninterrupts
++;
546 return (resource_list_alloc(&dinfo
->udi_resources
, bus
, child
,
547 type
, rid
, start
, end
, count
,
550 device_printf(bus
, "unknown resource request from %s\n",
551 device_get_nameunit(child
));
557 unin_chip_adjust_resource(device_t bus
, device_t child
, struct resource
*r
,
558 rman_res_t start
, rman_res_t end
)
560 switch (rman_get_type(r
)) {
563 return (bus_generic_rman_adjust_resource(bus
, child
, r
, start
,
566 return (bus_generic_adjust_resource(bus
, child
, r
, start
, end
));
573 unin_chip_release_resource(device_t bus
, device_t child
, struct resource
*res
)
575 switch (rman_get_type(res
)) {
578 return (bus_generic_rman_release_resource(bus
, child
, res
));
580 return (bus_generic_rl_release_resource(bus
, child
, res
));
587 unin_chip_activate_resource(device_t bus
, device_t child
, struct resource
*res
)
589 switch (rman_get_type(res
)) {
592 return (bus_generic_rman_activate_resource(bus
, child
, res
));
594 return (bus_generic_activate_resource(bus
, child
, res
));
601 unin_chip_deactivate_resource(device_t bus
, device_t child
,
602 struct resource
*res
)
604 switch (rman_get_type(res
)) {
607 return (bus_generic_rman_deactivate_resource(bus
, child
, res
));
609 return (bus_generic_deactivate_resource(bus
, child
, res
));
616 unin_chip_map_resource(device_t bus
, device_t child
, struct resource
*r
,
617 struct resource_map_request
*argsp
, struct resource_map
*map
)
619 struct resource_map_request args
;
620 rman_res_t length
, start
;
623 /* Resources must be active to be mapped. */
624 if (!(rman_get_flags(r
) & RF_ACTIVE
))
627 /* Mappings are only supported on I/O and memory resources. */
628 switch (rman_get_type(r
)) {
636 resource_init_map_request(&args
);
637 error
= resource_validate_map_request(r
, argsp
, &args
, &start
, &length
);
642 printf("nexus mapdev: start %jx, len %jd\n",
643 (uintmax_t)start
, (uintmax_t)length
);
645 map
->r_vaddr
= pmap_mapdev_attr(start
, length
, args
.memattr
);
646 if (map
->r_vaddr
== NULL
)
648 map
->r_bustag
= &bs_be_tag
;
649 map
->r_size
= length
;
650 map
->r_bushandle
= (bus_space_handle_t
)map
->r_vaddr
;
655 unin_chip_unmap_resource(device_t bus
, device_t child
, struct resource
*r
,
656 struct resource_map
*map
)
659 * If this is a memory resource, unmap it.
661 switch (rman_get_type(r
)) {
664 pmap_unmapdev(map
->r_vaddr
, map
->r_size
);
672 static struct resource_list
*
673 unin_chip_get_resource_list (device_t dev
, device_t child
)
675 struct unin_chip_devinfo
*dinfo
;
677 dinfo
= device_get_ivars(child
);
678 return (&dinfo
->udi_resources
);
681 static const struct ofw_bus_devinfo
*
682 unin_chip_get_devinfo(device_t dev
, device_t child
)
684 struct unin_chip_devinfo
*dinfo
;
686 dinfo
= device_get_ivars(child
);
687 return (&dinfo
->udi_obdinfo
);
691 unin_chip_wake(device_t dev
)
696 unin_update_reg(dev
, UNIN_PWR_MGMT
, UNIN_PWR_NORMAL
, UNIN_PWR_MASK
);
698 unin_update_reg(dev
, UNIN_HWINIT_STATE
, UNIN_RUNNING
, 0);
705 unin_chip_sleep(device_t dev
, int idle
)
710 unin_update_reg(dev
, UNIN_HWINIT_STATE
, UNIN_SLEEPING
, 0);
713 unin_update_reg(dev
, UNIN_PWR_MGMT
, UNIN_PWR_IDLE2
, UNIN_PWR_MASK
);
715 unin_update_reg(dev
, UNIN_PWR_MGMT
, UNIN_PWR_SLEEP
, UNIN_PWR_MASK
);