/* $NetBSD: autoconf.c,v 1.4 2019/04/15 20:46:10 skrll Exp $ */ /* $OpenBSD: autoconf.c,v 1.15 2001/06/25 00:43:10 mickey Exp $ */ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Lawrence Berkeley Laboratory. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)autoconf.c 8.4 (Berkeley) 10/1/93 */ /* * Copyright (c) 1998-2001 Michael Shalayeff * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Lawrence Berkeley Laboratory. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)autoconf.c 8.4 (Berkeley) 10/1/93 */ #include __KERNEL_RCSID(0, "$NetBSD: autoconf.c,v 1.4 2019/04/15 20:46:10 skrll Exp $"); #include "opt_kgdb.h" #include "opt_useleds.h" #include #include #include #include #include #include #include #include #include #include #ifdef KGDB #include #endif #include #include #include #include #include #include #include #include #include #include #include static TAILQ_HEAD(hppa_pdcmodule_head, hppa_pdcmodule) hppa_pdcmodule_list = TAILQ_HEAD_INITIALIZER(hppa_pdcmodule_list); struct hppa_pdcmodule { TAILQ_ENTRY(hppa_pdcmodule) hm_link; bool hm_registered; struct pdc_iodc_read hm_pir; struct iodc_data hm_type; struct device_path hm_dp; hppa_hpa_t hm_hpa; u_int hm_hpasz; u_int hm_naddrs; /* only PDC_SYSTEM_MAP */ u_int hm_modindex; /* only PDC_SYSTEM_MAP */ }; #define HPPA_SYSTEMMAPMODULES 256 /* * LED blinking thing */ #ifdef USELEDS int _hppa_led_on_cycles[_HPPA_LEDS_BLINKABLE]; static struct callout hppa_led_callout; static void hppa_led_blinker(void *); extern int hz; #endif void (*cold_hook)(int); /* see below */ struct hppa_pdcmodule *hppa_pdcmodule_create(struct hppa_pdcmodule *, const char *); void hppa_walkbus(struct confargs *ca); static void hppa_pdc_snake_scan(void); static void hppa_pdc_system_map_scan(void); /* * cpu_configure: * called at boot time, configure all devices on system */ void cpu_configure(void) { /* * Consider stopping for a debugger before * autoconfiguration. */ if (boothowto & RB_KDB) { #ifdef KGDB extern int hppa_kgdb_attached; if (hppa_kgdb_attached) kgdb_connect(1); #elif defined(DDB) Debugger(); #endif /* DDB */ } splhigh(); if (config_rootfound("mainbus", NULL) == NULL) panic("no mainbus found"); /* Allow interrupts - we're trusting spl* here */ hppa_intr_enable(); spl0(); if (cold_hook) (*cold_hook)(HPPA_COLD_HOT); #ifdef USELEDS memset(_hppa_led_on_cycles, 0, sizeof(_hppa_led_on_cycles)); callout_init(&hppa_led_callout, 0); hppa_led_blinker((void *) 0); #endif } #ifdef USELEDS /* * This sets LEDs. */ void hppa_led_ctl(int off, int on, int toggle) { int r; if (machine_ledaddr == NULL) return; /* The mask is reversed when pushed out to the hardware. */ r = ~(machine_leds = ((machine_leds & ~off) | on) ^ toggle); if (machine_ledword) *machine_ledaddr = r; else { #define HPPA_LED_DATA 0x01 #define HPPA_LED_STROBE 0x02 int b; for (b = 0x80; b; b >>= 1) { *machine_ledaddr = (r & b)? HPPA_LED_DATA : 0; DELAY(1); *machine_ledaddr = ((r & b)? HPPA_LED_DATA : 0) | HPPA_LED_STROBE; } #undef HPPA_LED_DATA #undef HPPA_LED_STROBE } } /* * This callout handler blinks LEDs. */ static void hppa_led_blinker(void *arg) { u_int led_cycle = (u_int) arg; int leds, led_i, led; int load; /* * Blink the heartbeat LED like this: * * |~| |~| * _| |_| |_,_,_,_ * 0 1 2 3 4 6 7 */ #define HPPA_HEARTBEAT_CYCLES (_HPPA_LED_FREQ / 8) if (led_cycle == (0 * HPPA_HEARTBEAT_CYCLES) || led_cycle == (2 * HPPA_HEARTBEAT_CYCLES)) { _hppa_led_on_cycles[HPPA_LED_HEARTBEAT] = HPPA_HEARTBEAT_CYCLES; } /* Form the new LED mask. */ leds = 0; for (led_i = 0, led = (1 << 0); led_i < _HPPA_LEDS_BLINKABLE; led_i++, led <<= 1) { if (_hppa_led_on_cycles[led_i] > 0) leds |= led; if (_hppa_led_on_cycles[led_i] >= 0) _hppa_led_on_cycles[led_i]--; } /* Add in the system load. */ load = averunnable.ldavg[0] >> FSHIFT; if (load >= (1 << (_HPPA_LEDS_COUNT - _HPPA_LEDS_BLINKABLE))) load = (1 << (_HPPA_LEDS_COUNT - _HPPA_LEDS_BLINKABLE)) - 1; leds |= (load << _HPPA_LEDS_BLINKABLE); /* Set the LEDs. */ hppa_led_ctl(-1, leds, 0); /* NB: this assumes _HPPA_LED_FREQ is a power of two. */ led_cycle = (led_cycle + 1) & (_HPPA_LED_FREQ - 1); callout_reset(&hppa_led_callout, hz / _HPPA_LED_FREQ, hppa_led_blinker, (void *) led_cycle); } #endif /* USELEDS */ /* * This is called by configure to set dumplo and dumpsize. * Dumps always skip the first CLBYTES of disk space * in case there might be a disk label stored there. * If there is extra space, put dump at the end to * reduce the chance that swapping trashes it. */ void cpu_dumpconf(void) { extern int dumpsize; int nblks, dumpblks; /* size of dump area */ if (dumpdev == NODEV) goto bad; nblks = bdev_size(dumpdev); if (nblks <= ctod(1)) goto bad; dumpblks = cpu_dumpsize(); if (dumpblks < 0) goto bad; dumpblks += ctod(physmem); /* If dump won't fit (incl. room for possible label), punt. */ if (dumpblks > (nblks - ctod(1))) goto bad; /* Put dump at end of partition */ dumplo = nblks - dumpblks; /* dumpsize is in page units, and doesn't include headers. */ dumpsize = physmem; return; bad: dumpsize = 0; return; } /****************************************************************/ device_t boot_device = NULL; void device_register(device_t dev, void *aux) { int pagezero_cookie; device_t pdev; if ((pdev = device_parent(dev)) == NULL || device_parent(pdev) == NULL) return; pagezero_cookie = hppa_pagezero_map(); /* * The boot device is described in PAGE0->mem_boot. We need to do it * this way as the MD device path (DP) information in struct confargs * is only available in hppa MD devices. So boot_device is used to * propagate information down the device tree. * * If the boot device is a GSC network device all we need to compare * is the HPA or device path (DP) to get the boot device. * If the boot device is a SCSI device below a GSC attached SCSI * controller PAGE0->mem_boot.pz_hpa contains the HPA of the SCSI * controller. In that case we remember the pointer to the * controller's struct dev in boot_device. The SCSI device is located * later, see below. */ if (device_is_a(pdev, "gsc") || device_is_a(pdev, "phantomas") || device_is_a(pdev, "uturn")) { struct confargs *ca = aux; if ((hppa_hpa_t)PAGE0->mem_boot.pz_hpa == ca->ca_hpa) { /* This is (the controller of) the boot device. */ boot_device = dev; } } /* * If the boot device is a PCI device the HPA is the address where the * firmware has mapped the PCI memory of the PCI device. This is quite * device dependent, so we compare the DP. It encodes the bus routing * information to the PCI bus bridge in the DP head and the PCI device * and PCI function in the last two DP components. So we compare the * head of the DP when a PCI bridge attaches and remember the struct * dev of the PCI bridge in boot_dev if it machtes. Later, when PCI * devices are attached, we look if this PCI device hangs below the * boot PCI bridge. If yes we compare the PCI device and PCI function * to the DP tail. In case of a network boot we found the boot device * on a match. In case of a SCSI boot device we have to do the same * check when SCSI devices are attached like on GSC SCSI controllers. */ if (device_is_a(dev, "dino") || device_is_a(dev, "elroy")) { struct confargs *ca = (struct confargs *)aux; int i, n; for (n = 0 ; ca->ca_dp.dp_bc[n] < 0 ; n++) { /* Skip unused DP components. */ } for (i = 0 ; i < 6 && n < 6 ; i++) { /* Skip unused DP components... */ if (PAGE0->mem_boot.pz_dp.dp_bc[i] < 0) continue; /* and compare the rest. */ if (PAGE0->mem_boot.pz_dp.dp_bc[i] != ca->ca_dp.dp_bc[n]) { hppa_pagezero_unmap(pagezero_cookie); return; } n++; } if (PAGE0->mem_boot.pz_dp.dp_bc[i] != ca->ca_dp.dp_mod) { hppa_pagezero_unmap(pagezero_cookie); return; } /* This is the PCI host bridge in front of the boot device. */ boot_device = dev; } if (device_is_a(dev, "ppb") && boot_device == device_parent(pdev)) { /* * XXX Guesswork. No hardware to test how firmware handles * a ppb. */ struct pci_attach_args *paa = (struct pci_attach_args*)aux; if (paa->pa_device == PAGE0->mem_boot.pz_dp.dp_bc[3] && paa->pa_function == PAGE0->mem_boot.pz_dp.dp_bc[4]) { /* * This is the PCI - PCI bridge in front of the boot * device. */ boot_device = dev; } } if (device_is_a(pdev, "pci") && boot_device == device_parent(pdev)) { struct pci_attach_args *paa = (struct pci_attach_args*)aux; if (paa->pa_device == PAGE0->mem_boot.pz_dp.dp_bc[5] && paa->pa_function == PAGE0->mem_boot.pz_dp.dp_mod) { /* * This is (the controller of) the boot device. */ boot_device = dev; } } /* * When SCSI devices are attached, we look if the SCSI device hangs * below the controller remembered in boot_device. If so, we compare * the SCSI ID and LUN with the DP layer information. If they match * we found the boot device. */ if (device_is_a(pdev, "scsibus") && boot_device == device_parent(pdev)) { struct scsipibus_attach_args *saa = aux; struct scsipi_periph *p = saa->sa_periph; if (p->periph_target == PAGE0->mem_boot.pz_dp.dp_layers[0] && p->periph_lun == PAGE0->mem_boot.pz_dp.dp_layers[1]) { /* This is the boot device. */ boot_device = dev; } } hppa_pagezero_unmap(pagezero_cookie); return; } /* * Choose root and swap devices. */ void cpu_rootconf(void) { #ifdef DEBUG int pagezero_cookie; int n; pagezero_cookie = hppa_pagezero_map(); printf("PROM boot device: hpa %p path ", PAGE0->mem_boot.pz_hpa); for (n = 0 ; n < 6 ; n++) { if (PAGE0->mem_boot.pz_dp.dp_bc[n] >= 0) printf("%d/", PAGE0->mem_boot.pz_dp.dp_bc[n]); } printf("%d dp_layers ", PAGE0->mem_boot.pz_dp.dp_mod); for (n = 0 ; n < 6 ; n++) { printf( "0x%x%c", PAGE0->mem_boot.pz_dp.dp_layers[n], n < 5 ? '/' : ' '); } printf("dp_flags 0x%x pz_class 0x%x\n", PAGE0->mem_boot.pz_dp.dp_flags, PAGE0->mem_boot.pz_class); hppa_pagezero_unmap(pagezero_cookie); #endif /* DEBUG */ if (boot_device != NULL) printf("boot device: %s\n", device_xname(boot_device)); booted_device = boot_device; rootconf(); } void hppa_walkbus(struct confargs *ca) { struct hppa_pdcmodule nhm, *hm; int i; if (ca->ca_hpabase == 0) return; aprint_debug(">> Walking bus at HPA 0x%lx\n", ca->ca_hpabase); for (i = 0; i < ca->ca_nmodules; i++) { int error; memset(&nhm, 0, sizeof(nhm)); nhm.hm_dp.dp_bc[0] = ca->ca_dp.dp_bc[1]; nhm.hm_dp.dp_bc[1] = ca->ca_dp.dp_bc[2]; nhm.hm_dp.dp_bc[2] = ca->ca_dp.dp_bc[3]; nhm.hm_dp.dp_bc[3] = ca->ca_dp.dp_bc[4]; nhm.hm_dp.dp_bc[4] = ca->ca_dp.dp_bc[5]; nhm.hm_dp.dp_bc[5] = ca->ca_dp.dp_mod; nhm.hm_hpa = ca->ca_hpabase + IOMOD_HPASIZE * i; nhm.hm_hpasz = 0; nhm.hm_dp.dp_mod = i; nhm.hm_naddrs = 0; error = pdcproc_iodc_read(nhm.hm_hpa, IODC_DATA, NULL, &nhm.hm_pir, sizeof(nhm.hm_pir), &nhm.hm_type, sizeof(nhm.hm_type)); if (error < 0) continue; aprint_debug(">> HPA 0x%lx[0x%x]", nhm.hm_hpa, nhm.hm_hpasz); TAILQ_FOREACH(hm, &hppa_pdcmodule_list, hm_link) { if (nhm.hm_hpa == hm->hm_hpa) { aprint_debug(" found by firmware\n"); break; } } /* If we've found the module move onto the next one. */ if (hm) continue; /* Expect PDC to report devices of the following types */ if (nhm.hm_type.iodc_type == HPPA_TYPE_FIO) { aprint_debug(" expected to be missing\n"); continue; } hppa_pdcmodule_create(&nhm, "Bus walk"); } } void pdc_scanbus(device_t self, struct confargs *ca, device_t (*callback)(device_t, struct confargs *)) { struct hppa_pdcmodule *hm; struct confargs nca; device_t dev; int ia; hppa_walkbus(ca); TAILQ_FOREACH(hm, &hppa_pdcmodule_list, hm_link) { char buf[128]; int error; if (hm->hm_registered) continue; if (!(hm->hm_dp.dp_bc[0] == ca->ca_dp.dp_bc[1] && hm->hm_dp.dp_bc[1] == ca->ca_dp.dp_bc[2] && hm->hm_dp.dp_bc[2] == ca->ca_dp.dp_bc[3] && hm->hm_dp.dp_bc[3] == ca->ca_dp.dp_bc[4] && hm->hm_dp.dp_bc[4] == ca->ca_dp.dp_bc[5] && hm->hm_dp.dp_bc[5] == ca->ca_dp.dp_mod)) continue; memset(&nca, 0, sizeof(nca)); nca.ca_iot = ca->ca_iot; nca.ca_dmatag = ca->ca_dmatag; nca.ca_pir = hm->hm_pir; nca.ca_type = hm->hm_type; nca.ca_hpa = hm->hm_hpa; nca.ca_dp = hm->hm_dp; nca.ca_hpa = hm->hm_hpa; nca.ca_hpasz = hm->hm_hpasz; if (hm->hm_naddrs) { if (hm->hm_naddrs > HPPA_MAXIOADDRS) { nca.ca_naddrs = HPPA_MAXIOADDRS; aprint_error("WARNING: too many (%d) addrs\n", hm->hm_naddrs); } else nca.ca_naddrs = hm->hm_naddrs; aprint_debug(">> ADDRS[%d/%d]: ", nca.ca_naddrs, hm->hm_modindex); KASSERT(hm->hm_modindex != -1); for (ia = 0; ia < nca.ca_naddrs; ia++) { struct pdc_system_map_find_addr pdc_find_addr; error = pdcproc_system_map_find_addr( &pdc_find_addr, hm->hm_modindex, ia + 1); if (error < 0) break; nca.ca_addrs[ia].addr = pdc_find_addr.hpa; nca.ca_addrs[ia].size = pdc_find_addr.size << PGSHIFT; aprint_debug(" 0x%lx[0x%x]", nca.ca_addrs[ia].addr, nca.ca_addrs[ia].size); } aprint_debug("\n"); } aprint_debug(">> HPA 0x%lx[0x%x]\n", nca.ca_hpa, nca.ca_hpasz); snprintb(buf, sizeof(buf), PZF_BITS, nca.ca_dp.dp_flags); aprint_debug(">> probing: flags %s ", buf); if (nca.ca_dp.dp_mod >=0) { int n; aprint_debug(" path "); for (n = 0; n < 6; n++) { if (nca.ca_dp.dp_bc[n] >= 0) aprint_debug("%d/", nca.ca_dp.dp_bc[n]); } aprint_debug("%d", nca.ca_dp.dp_mod); } aprint_debug(" type %x sv %x\n", nca.ca_type.iodc_type, nca.ca_type.iodc_sv_model); nca.ca_irq = HPPACF_IRQ_UNDEF; nca.ca_name = hppa_mod_info(nca.ca_type.iodc_type, nca.ca_type.iodc_sv_model); dev = callback(self, &nca); if (dev) hm->hm_registered = true; } } static const struct hppa_mod_info hppa_knownmods[] = { #include }; const char * hppa_mod_info(int type, int sv) { const struct hppa_mod_info *mi; static char fakeid[32]; int i; for (i = 0, mi = hppa_knownmods; i < __arraycount(hppa_knownmods); i++, mi++) { if (mi->mi_type == type && mi->mi_sv == sv) { break; } } if (i == __arraycount(hppa_knownmods)) { snprintf(fakeid, sizeof(fakeid), "type %x, sv %x", type, sv); return fakeid; } return mi->mi_name; } /* * Create the device on our device list. Keep the devices in order. */ struct hppa_pdcmodule * hppa_pdcmodule_create(struct hppa_pdcmodule *hm, const char *who) { struct hppa_pdcmodule *nhm, *ahm; int i; nhm = kmem_zalloc(sizeof(*nhm), KM_SLEEP); nhm->hm_registered = false; nhm->hm_pir = hm->hm_pir; nhm->hm_type = hm->hm_type; nhm->hm_dp = hm->hm_dp; nhm->hm_hpa = hm->hm_hpa; nhm->hm_hpasz = hm->hm_hpasz; nhm->hm_naddrs = hm->hm_naddrs; nhm->hm_modindex = hm->hm_modindex; /* Find start of new path */ for (i = 0; i < 6; i++) { if (hm->hm_dp.dp_bc[i] != -1) break; } /* * Look, in reverse, for the first device that has a path before our * new one. In reverse because PDC reports most (all?) devices in path * order and therefore the common case is to add to the end of the * list. */ TAILQ_FOREACH_REVERSE(ahm, &hppa_pdcmodule_list, hppa_pdcmodule_head, hm_link) { int check; int j, k; for (j = 0; j < 6; j++) { if (ahm->hm_dp.dp_bc[j] != -1) break; } for (check = 0, k = i; j < 7 && k < 7; j++, k++) { char nid, aid; nid = (k == 6) ? hm->hm_dp.dp_mod : hm->hm_dp.dp_bc[k]; aid = (j == 6) ? ahm->hm_dp.dp_mod : ahm->hm_dp.dp_bc[j]; if (nid == aid) continue; check = nid - aid; break; } if (check >= 0) break; else if (check < 0) continue; } if (ahm == NULL) TAILQ_INSERT_HEAD(&hppa_pdcmodule_list, nhm, hm_link); else TAILQ_INSERT_AFTER(&hppa_pdcmodule_list, ahm, nhm, hm_link); if (hm->hm_dp.dp_mod >= 0) { int n; aprint_debug(">> %s device at path ", who); for (n = 0; n < 6; n++) { if (hm->hm_dp.dp_bc[n] >= 0) aprint_debug("%d/", hm->hm_dp.dp_bc[n]); } aprint_debug("%d addrs %d\n", hm->hm_dp.dp_mod, hm->hm_naddrs); } return nhm; } /* * This is used for Snake machines */ static struct hppa_pdcmodule * hppa_memmap_query(struct device_path *devp) { static struct hppa_pdcmodule nhm; struct pdc_memmap pdc_memmap; int error; error = pdcproc_memmap(&pdc_memmap, devp); if (error < 0) return NULL; memset(&nhm, 0, sizeof(nhm)); nhm.hm_dp = *devp; nhm.hm_hpa = pdc_memmap.hpa; nhm.hm_hpasz = pdc_memmap.morepages; nhm.hm_naddrs = 0; nhm.hm_modindex = -1; error = pdcproc_iodc_read(nhm.hm_hpa, IODC_DATA, NULL, &nhm.hm_pir, sizeof(nhm.hm_pir), &nhm.hm_type, sizeof(nhm.hm_type)); if (error < 0) return NULL; return hppa_pdcmodule_create(&nhm, "PDC (memmap)"); } static void hppa_pdc_snake_scan(void) { struct device_path path; struct hppa_pdcmodule *hm; int im, ba; memset(&path, 0, sizeof(path)); for (im = 0; im < 16; im++) { path.dp_bc[0] = path.dp_bc[1] = path.dp_bc[2] = path.dp_bc[3] = path.dp_bc[4] = path.dp_bc[5] = -1; path.dp_mod = im; hm = hppa_memmap_query(&path); if (!hm) continue; if (hm->hm_type.iodc_type != HPPA_TYPE_BHA) continue; path.dp_bc[0] = path.dp_bc[1] = path.dp_bc[2] = path.dp_bc[3] = -1; path.dp_bc[4] = im; path.dp_bc[5] = 0; for (ba = 0; ba < 16; ba++) { path.dp_mod = ba; hppa_memmap_query(&path); } } } static void hppa_pdc_system_map_scan(void) { struct pdc_system_map_find_mod pdc_find_mod; struct device_path path; struct hppa_pdcmodule hm; int error; int im; for (im = 0; im < HPPA_SYSTEMMAPMODULES; im++) { memset(&path, 0, sizeof(path)); error = pdcproc_system_map_find_mod(&pdc_find_mod, &path, im); if (error == PDC_ERR_NMOD) break; if (error < 0) continue; memset(&hm, 0, sizeof(hm)); hm.hm_dp = path; hm.hm_hpa = pdc_find_mod.hpa; hm.hm_hpasz = pdc_find_mod.size << PGSHIFT; hm.hm_naddrs = pdc_find_mod.naddrs; hm.hm_modindex = im; error = pdcproc_iodc_read(hm.hm_hpa, IODC_DATA, NULL, &hm.hm_pir, sizeof(hm.hm_pir), &hm.hm_type, sizeof(hm.hm_type)); if (error < 0) continue; hppa_pdcmodule_create(&hm, "PDC (system map)"); } } void hppa_modules_scan(void) { switch (pdc_gettype()) { case PDC_TYPE_SNAKE: hppa_pdc_snake_scan(); break; case PDC_TYPE_UNKNOWN: hppa_pdc_system_map_scan(); } } void hppa_modules_done(void) { struct hppa_pdcmodule *hm, *nhm; TAILQ_FOREACH_SAFE(hm, &hppa_pdcmodule_list, hm_link, nhm) { TAILQ_REMOVE(&hppa_pdcmodule_list, hm, hm_link); kmem_free(hm, sizeof(*hm)); } }