/* $NetBSD: autoconf.c,v 1.26 2008/03/30 12:39:32 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 <sys/cdefs.h> __KERNEL_RCSID(0, "$NetBSD: autoconf.c,v 1.26 2008/03/30 12:39:32 skrll Exp $"); #include "opt_kgdb.h" #include "opt_useleds.h" #include "opt_power_switch.h" #include <sys/param.h> #include <sys/systm.h> #include <sys/buf.h> #include <sys/disklabel.h> #include <sys/conf.h> #include <sys/kernel.h> #include <sys/reboot.h> #include <sys/device.h> #include <sys/callout.h> #ifdef KGDB #include <sys/kgdb.h> #endif #include <machine/iomod.h> #include <machine/autoconf.h> #include <dev/pci/pcivar.h> #include <dev/scsipi/scsi_all.h> #include <dev/scsipi/scsipi_all.h> #include <dev/scsipi/scsiconf.h> #include <dev/cons.h> #include <hp700/hp700/machdep.h> #include <hp700/hp700/power.h> #include <hp700/dev/cpudevs.h> #include <hp700/gsc/gscbusvar.h> register_t kpsw = PSW_Q | PSW_P | PSW_C | PSW_D; /* * LED blinking thing */ #ifdef USELEDS int _hp700_led_on_cycles[_HP700_LEDS_BLINKABLE]; static struct callout hp700_led_callout; static void hp700_led_blinker(void *); extern int hz; #endif /* * 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 hp700_kgdb_attached; if (hp700_kgdb_attached) kgdb_connect(1); #elif defined(DDB) Debugger(); #endif /* DDB */ } splhigh(); if (config_rootfound("mainbus", NULL) == NULL) panic("no mainbus found"); /* in spl*() we trust */ hp700_intr_init(); __asm volatile("ssm %0, %%r0" :: "i" (PSW_I)); kpsw |= PSW_I; spl0(); cold = 0; #ifdef POWER_SWITCH /* Give OS control over the power switch. */ pwr_sw_ctrl(PWR_SW_CTRL_ENABLE); #endif /* POWER_SWITCH */ #ifdef USELEDS memset(_hp700_led_on_cycles, 0, sizeof(_hp700_led_on_cycles)); callout_init(&hp700_led_callout, 0); hp700_led_blinker((void *) 0); #endif } #ifdef USELEDS /* * This sets LEDs. */ void hp700_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 HP700_LED_DATA 0x01 #define HP700_LED_STROBE 0x02 int b; for (b = 0x80; b; b >>= 1) { *machine_ledaddr = (r & b)? HP700_LED_DATA : 0; DELAY(1); *machine_ledaddr = ((r & b)? HP700_LED_DATA : 0) | HP700_LED_STROBE; } #undef HP700_LED_DATA #undef HP700_LED_STROBE } } /* * This callout handler blinks LEDs. */ static void hp700_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 HP700_HEARTBEAT_CYCLES (_HP700_LED_FREQ / 8) if (led_cycle == (0 * HP700_HEARTBEAT_CYCLES) || led_cycle == (2 * HP700_HEARTBEAT_CYCLES)) { _hp700_led_on_cycles[HP700_LED_HEARTBEAT] = HP700_HEARTBEAT_CYCLES; } /* Form the new LED mask. */ leds = 0; for (led_i = 0, led = (1 << 0); led_i < _HP700_LEDS_BLINKABLE; led_i++, led <<= 1) { if (_hp700_led_on_cycles[led_i] > 0) leds |= led; if (_hp700_led_on_cycles[led_i] >= 0) _hp700_led_on_cycles[led_i]--; } /* Add in the system load. */ load = averunnable.ldavg[0] >> FSHIFT; if (load >= (1 << (_HP700_LEDS_COUNT - _HP700_LEDS_BLINKABLE))) load = (1 << (_HP700_LEDS_COUNT - _HP700_LEDS_BLINKABLE)) - 1; leds |= (load << _HP700_LEDS_BLINKABLE); /* Set the LEDs. */ hp700_led_ctl(-1, leds, 0); /* NB: this assumes _HP700_LED_FREQ is a power of two. */ led_cycle = (led_cycle + 1) & (_HP700_LED_FREQ - 1); callout_reset(&hp700_led_callout, hz / _HP700_LED_FREQ, hp700_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) { const struct bdevsw *bdev; extern int dumpsize; int nblks, dumpblks; /* size of dump area */ if (dumpdev == NODEV) goto bad; bdev = bdevsw_lookup(dumpdev); if (bdev == NULL) { dumpdev = NODEV; goto bad; } if (bdev->d_psize == NULL) goto bad; nblks = (*bdev->d_psize)(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; } /****************************************************************/ struct device *boot_device = NULL; void device_register(struct device *dev, void *aux) { int pagezero_cookie; struct device *pdev; if ((pdev = device_parent(dev)) == NULL || device_parent(pdev) == NULL) return; pagezero_cookie = hp700_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 hp700 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 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")) && (hppa_hpa_t)PAGE0->mem_boot.pz_hpa == ((struct gsc_attach_args *)aux)->ga_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 maped 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")) { 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]) { hp700_pagezero_unmap(pagezero_cookie); return; } n++; } if (PAGE0->mem_boot.pz_dp.dp_bc[i] != ca->ca_dp.dp_mod) { hp700_pagezero_unmap(pagezero_cookie); return; } /* This is the PCI host bridge in front of the boot device. */ boot_device = dev; } /* XXX Guesswork. No hardware to test how firmware handles a ppb. */ if (device_is_a(dev, "ppb") && boot_device == device_parent(pdev) && ((struct pci_attach_args*)aux)->pa_device == PAGE0->mem_boot.pz_dp.dp_bc[3] && ((struct pci_attach_args*)aux)->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*)aux)->pa_device == PAGE0->mem_boot.pz_dp.dp_bc[5] && ((struct pci_attach_args*)aux)->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 *)aux)->sa_periph->periph_target == PAGE0->mem_boot.pz_dp.dp_layers[0] && ((struct scsipibus_attach_args *)aux)->sa_periph->periph_lun == PAGE0->mem_boot.pz_dp.dp_layers[1]) /* This is the boot device. */ boot_device = dev; hp700_pagezero_unmap(pagezero_cookie); return; } /* * Choose root and swap devices. */ void cpu_rootconf(void) { #ifdef DEBUG int pagezero_cookie; int n; pagezero_cookie = hp700_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); #endif /* DEBUG */ if (boot_device != NULL) printf("boot device: %s\n", boot_device->dv_xname ); setroot(boot_device, 0); } #ifdef RAMDISK_HOOKS /*static struct device fakerdrootdev = { DV_DISK, {}, NULL, 0, "rd0", NULL };*/ #endif void pdc_scanbus_memory_map(struct device *self, struct confargs *ca, void (*callback)(struct device *, struct confargs *)) { int i; struct confargs nca; struct pdc_memmap pdc_memmap PDC_ALIGNMENT; struct pdc_iodc_read pdc_iodc_read PDC_ALIGNMENT; for (i = 0; i < 16; i++) { memset(&nca, 0, sizeof(nca)); nca.ca_dp.dp_bc[0] = -1; nca.ca_dp.dp_bc[1] = -1; nca.ca_dp.dp_bc[2] = -1; nca.ca_dp.dp_bc[3] = -1; nca.ca_dp.dp_bc[4] = ca->ca_dp.dp_mod; nca.ca_dp.dp_bc[5] = ca->ca_dp.dp_mod < 0 ? -1 : 0; nca.ca_dp.dp_mod = i; if (pdc_call((iodcio_t)pdc, 0, PDC_MEMMAP, PDC_MEMMAP_HPA, &pdc_memmap, &nca.ca_dp) < 0) continue; if (pdc_call((iodcio_t)pdc, 0, PDC_IODC, PDC_IODC_READ, &pdc_iodc_read, pdc_memmap.hpa, IODC_DATA, &nca.ca_type, sizeof(nca.ca_type)) < 0) continue; nca.ca_mod = i; nca.ca_hpa = pdc_memmap.hpa; nca.ca_iot = ca->ca_iot; nca.ca_dmatag = ca->ca_dmatag; nca.ca_irq = HP700CF_IRQ_UNDEF; nca.ca_pdc_iodc_read = &pdc_iodc_read; nca.ca_name = hppa_mod_info(nca.ca_type.iodc_type, nca.ca_type.iodc_sv_model); (*callback)(self, &nca); } } void pdc_scanbus_system_map(struct device *self, struct confargs *ca, void (*callback)(struct device *, struct confargs *)) { int i; int ia; struct confargs nca; struct pdc_iodc_read pdc_iodc_read PDC_ALIGNMENT; struct pdc_system_map_find_mod pdc_find_mod PDC_ALIGNMENT; struct pdc_system_map_find_addr pdc_find_addr PDC_ALIGNMENT; for (i = 0; i <= 64; i++) { memset(&nca, 0, sizeof(nca)); nca.ca_dp.dp_bc[0] = ca->ca_dp.dp_bc[1]; nca.ca_dp.dp_bc[1] = ca->ca_dp.dp_bc[2]; nca.ca_dp.dp_bc[2] = ca->ca_dp.dp_bc[3]; nca.ca_dp.dp_bc[3] = ca->ca_dp.dp_bc[4]; nca.ca_dp.dp_bc[4] = ca->ca_dp.dp_bc[5]; nca.ca_dp.dp_bc[5] = ca->ca_dp.dp_mod; nca.ca_dp.dp_mod = i; if (pdc_call((iodcio_t)pdc, 0, PDC_SYSTEM_MAP, PDC_SYSTEM_MAP_TRANS_PATH, &pdc_find_mod, &nca.ca_dp) != 0) continue; nca.ca_hpa = pdc_find_mod.hpa; nca.ca_hpasz = pdc_find_mod.size << PGSHIFT; if (pdc_find_mod.naddrs > 0) { nca.ca_naddrs = pdc_find_mod.naddrs; if (nca.ca_naddrs > 16) { nca.ca_naddrs = 16; printf("WARNING: too many (%d) addrs\n", pdc_find_mod.naddrs); } for (ia = 0; pdc_call((iodcio_t)pdc, 0, PDC_SYSTEM_MAP, PDC_SYSTEM_MAP_FIND_ADDR, &pdc_find_addr, pdc_find_mod.mod_index, ia) == 0 && ia < nca.ca_naddrs; ia++) { nca.ca_addrs[ia].addr = pdc_find_addr.hpa; nca.ca_addrs[ia].size = pdc_find_addr.size << PGSHIFT; } } if (pdc_call((iodcio_t)pdc, 0, PDC_IODC, PDC_IODC_READ, &pdc_iodc_read, nca.ca_hpa, IODC_DATA, &nca.ca_type, sizeof(nca.ca_type)) < 0) { continue; } nca.ca_mod = i; nca.ca_iot = ca->ca_iot; nca.ca_dmatag = ca->ca_dmatag; nca.ca_irq = HP700CF_IRQ_UNDEF; nca.ca_pdc_iodc_read = &pdc_iodc_read; nca.ca_name = hppa_mod_info(nca.ca_type.iodc_type, nca.ca_type.iodc_sv_model); (*callback)(self, &nca); } } static const struct hppa_mod_info hppa_knownmods[] = { #include <hp700/dev/cpudevs_data.h> }; const char * hppa_mod_info(int type, int sv) { const struct hppa_mod_info *mi; static char fakeid[32]; for (mi = hppa_knownmods; mi->mi_type >= 0 && (mi->mi_type != type || mi->mi_sv != sv); mi++); if (mi->mi_type < 0) { sprintf(fakeid, "type %x, sv %x", type, sv); return fakeid; } else return mi->mi_name; }