#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: autoconf.c,v 1.119 2025/11/29 19:32:52 thorpej Exp $");
#include "dvbox.h"
#include "gbox.h"
#include "hyper.h"
#include "rbox.h"
#include "topcat.h"
#include "tvrx.h"
#include "gendiofb.h"
#include "sti_dio.h"
#include "sti_sgc.h"
#include "com_dio.h"
#include "com_frodo.h"
#include "dcm.h"
#define _M68K_INTR_PRIVATE
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/kmem.h>
#include <sys/mount.h>
#include <sys/queue.h>
#include <sys/reboot.h>
#include <sys/tty.h>
#include <sys/vmem.h>
#include <sys/vmem_impl.h>
#include <uvm/uvm_extern.h>
#include <dev/cons.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/rasops/rasops.h>
#include <dev/scsipi/scsi_all.h>
#include <dev/scsipi/scsipi_all.h>
#include <dev/scsipi/scsiconf.h>
#include <machine/autoconf.h>
#include <machine/vmparam.h>
#include <machine/cpu.h>
#include <machine/hp300spu.h>
#include <machine/intr.h>
#include <machine/pte.h>
#include <hp300/dev/dioreg.h>
#include <hp300/dev/diovar.h>
#include <hp300/dev/diodevs.h>
#include <hp300/dev/intioreg.h>
#include <hp300/dev/dmavar.h>
#include <hp300/dev/frodoreg.h>
#include <hp300/dev/hpibvar.h>
#if NCOM_DIO > 0
#include <hp300/dev/com_diovar.h>
#endif
#if NCOM_FRODO > 0
#include <hp300/dev/com_frodovar.h>
#endif
#if NSTI_DIO > 0 || NSTI_SGC > 0
#include <hp300/dev/sti_machdep.h>
#endif
#if NSTI_DIO > 0
#include <hp300/dev/sti_diovar.h>
#endif
#if NSTI_SGC > 0
#include <hp300/dev/sgcreg.h>
#include <hp300/dev/sgcvar.h>
#include <hp300/dev/sti_sgcvar.h>
#endif
#include <hp300/dev/diofbreg.h>
#include <hp300/dev/diofbvar.h>
extern int dcmcnattach(bus_space_tag_t, bus_addr_t, int);
extern int dnkbdcnattach(bus_space_tag_t, bus_addr_t);
static int dio_scan(int (*func)(bus_space_tag_t, bus_addr_t, int));
static int dio_scode_probe(int,
int (*func)(bus_space_tag_t, bus_addr_t, int));
u_int bootdev;
#define EXTIO_BTAG_COUNT VMEM_EST_BTCOUNT(1, 8)
static struct vmem extio_arena_store;
static struct vmem_btag extio_btag_store[EXTIO_BTAG_COUNT];
vmem_t *extio_arena;
struct dev_data {
LIST_ENTRY(dev_data) dd_list;
LIST_ENTRY(dev_data) dd_clist;
device_t dd_dev;
int dd_scode;
int dd_slave;
int dd_punit;
};
typedef LIST_HEAD(, dev_data) ddlist_t;
static ddlist_t dev_data_list;
static ddlist_t dev_data_list_hpib;
static ddlist_t dev_data_list_scsi;
static void findbootdev(void);
static void findbootdev_slave(ddlist_t *, int, int, int);
static void setbootdev(void);
static struct dev_data *dev_data_lookup(device_t);
static void dev_data_insert(struct dev_data *, ddlist_t *);
static int mainbusmatch(device_t, cfdata_t, void *);
static void mainbusattach(device_t, device_t, void *);
static int mainbussearch(device_t, cfdata_t, const int *, void *);
CFATTACH_DECL_NEW(mainbus, 0,
mainbusmatch, mainbusattach, NULL, NULL);
static int
mainbusmatch(device_t parent, cfdata_t cf, void *aux)
{
static int mainbus_matched = 0;
if (mainbus_matched)
return 0;
mainbus_matched = 1;
return 1;
}
static void
mainbusattach(device_t parent, device_t self, void *aux)
{
aprint_normal("\n");
config_search(self, NULL,
CFARGS(.search = mainbussearch));
}
static int
mainbussearch(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
{
if (config_probe(parent, cf, NULL))
config_attach(parent, cf, NULL, NULL, CFARGS_NONE);
return 0;
}
static struct m68k_intrhand *
hp300_ih_alloc(int km_flag)
{
return kmem_zalloc(sizeof(struct hp300_intrhand), km_flag);
}
static void
hp300_ih_free(struct m68k_intrhand *ih)
{
kmem_free(ih, sizeof(struct hp300_intrhand));
}
static const struct m68k_ih_allocfuncs hp300_ih_allocfuncs = {
.alloc = hp300_ih_alloc,
.free = hp300_ih_free,
};
void
cpu_configure(void)
{
LIST_INIT(&dev_data_list);
LIST_INIT(&dev_data_list_hpib);
LIST_INIT(&dev_data_list_scsi);
(void)splhigh();
m68k_intr_init(&hp300_ih_allocfuncs);
if (config_rootfound("mainbus", NULL) == NULL)
panic("no mainbus found");
(void)spl0();
}
void
cpu_rootconf(void)
{
struct dev_data *dd;
struct vfsops *vops;
if ((bootdev & B_MAGICMASK) != B_DEVMAGIC) {
printf("WARNING: boot program didn't supply boot device.\n");
printf("Please update your boot program.\n");
} else {
findbootdev();
if (booted_device == NULL) {
printf("WARNING: can't find match for bootdev:\n");
printf(
"type = %d, ctlr = %d, slave = %d, punit = %d, part = %d\n",
B_TYPE(bootdev), B_ADAPTOR(bootdev),
B_CONTROLLER(bootdev), B_UNIT(bootdev),
B_PARTITION(bootdev));
bootdev = 0;
} else {
printf("boot device: %s\n", device_xname(booted_device));
}
}
if (rootspec == NULL) {
vops = vfs_getopsbyname(MOUNT_NFS);
if (vops != NULL && vops->vfs_mountroot != NULL &&
strcmp(rootfstype, MOUNT_NFS) == 0) {
for (dd = LIST_FIRST(&dev_data_list);
dd != NULL; dd = LIST_NEXT(dd, dd_list)) {
if (device_class(dd->dd_dev) == DV_IFNET) {
booted_device = dd->dd_dev;
break;
}
}
if (dd == NULL) {
printf("no network interface for NFS root");
}
}
if (vops != NULL)
vfs_delref(vops);
}
if (bootdev == 0)
boothowto |= RB_ASKNAME;
if (booted_device != NULL && device_class(booted_device) == DV_TAPE)
boothowto |= RB_ASKNAME;
rootconf();
setbootdev();
}
void
device_register(device_t dev, void *aux)
{
struct dev_data *dd;
static int seen_netdevice = 0;
dd = kmem_zalloc(sizeof(*dd), KM_SLEEP);
dd->dd_dev = dev;
if (device_class(dev) == DV_IFNET && seen_netdevice == 0) {
struct dio_attach_args *da = aux;
seen_netdevice = 1;
dd->dd_scode = da->da_scode;
goto linkup;
}
if (device_is_a(dev, "fhpib") ||
device_is_a(dev, "nhpib") ||
device_is_a(dev, "spc")) {
struct dio_attach_args *da = aux;
dd->dd_scode = da->da_scode;
goto linkup;
}
if (device_is_a(dev, "rd")) {
struct hpibbus_attach_args *ha = aux;
dd->dd_slave = ha->ha_slave;
dd->dd_punit = ha->ha_punit;
goto linkup;
}
if (device_is_a(dev, "sd") ||
device_is_a(dev, "cd")) {
struct scsipibus_attach_args *sa = aux;
dd->dd_slave = sa->sa_periph->periph_target;
dd->dd_punit = sa->sa_periph->periph_lun;
goto linkup;
}
kmem_free(dd, sizeof(*dd));
return;
linkup:
LIST_INSERT_HEAD(&dev_data_list, dd, dd_list);
if (device_is_a(dev, "fhpib") ||
device_is_a(dev, "nhpib")) {
dev_data_insert(dd, &dev_data_list_hpib);
return;
}
if (device_is_a(dev, "spc")) {
dev_data_insert(dd, &dev_data_list_scsi);
return;
}
}
static void
findbootdev(void)
{
int type, ctlr, slave, punit, part;
int scsiboot, hpibboot, netboot;
struct dev_data *dd;
booted_device = NULL;
booted_partition = 0;
if ((bootdev & B_MAGICMASK) != B_DEVMAGIC)
return;
type = B_TYPE(bootdev);
ctlr = B_ADAPTOR(bootdev);
slave = B_CONTROLLER(bootdev);
punit = B_UNIT(bootdev);
part = B_PARTITION(bootdev);
scsiboot = (type == 4);
hpibboot = (type == 0 || type == 2);
netboot = (type == 6);
if (netboot) {
for (dd = LIST_FIRST(&dev_data_list); dd != NULL;
dd = LIST_NEXT(dd, dd_list)) {
if (device_class(dd->dd_dev) == DV_IFNET) {
booted_device = dd->dd_dev;
break;
}
}
return;
}
if (hpibboot) {
findbootdev_slave(&dev_data_list_hpib, ctlr,
slave, punit);
if (booted_device == NULL)
return;
if ((type == 0 && !device_is_a(booted_device, "ct")) ||
(type == 2 && !device_is_a(booted_device, "rd"))) {
printf("WARNING: boot device/type mismatch!\n");
printf("device = %s, type = %d\n",
device_xname(booted_device), type);
booted_device = NULL;
}
goto out;
}
if (scsiboot) {
findbootdev_slave(&dev_data_list_scsi, ctlr,
slave, punit);
if (booted_device == NULL)
return;
if (type == 4 &&
!device_is_a(booted_device, "sd") &&
!device_is_a(booted_device, "cd")) {
printf("WARNING: boot device/type mismatch!\n");
printf("device = %s, type = %d\n",
device_xname(booted_device), type);
booted_device = NULL;
}
goto out;
}
printf("WARNING: UNKNOWN BOOT DEVICE TYPE = %d\n", type);
out:
if (booted_device != NULL)
booted_partition = part;
}
static void
findbootdev_slave(ddlist_t *ddlist, int ctlr, int slave, int punit)
{
struct dev_data *cdd, *dd;
for (cdd = LIST_FIRST(ddlist); ctlr != 0 && cdd != NULL;
cdd = LIST_NEXT(cdd, dd_clist))
ctlr--;
if (cdd == NULL) {
return;
}
for (dd = LIST_FIRST(&dev_data_list); dd != NULL;
dd = LIST_NEXT(dd, dd_list)) {
if (device_parent(device_parent(dd->dd_dev)) != cdd->dd_dev)
continue;
if (dd->dd_slave == slave &&
dd->dd_punit == punit) {
booted_device = dd->dd_dev;
break;
}
}
}
static void
setbootdev(void)
{
struct dev_data *cdd, *dd;
int type, ctlr;
bootdev = 0;
if (device_class(root_device) == DV_IFNET) {
bootdev = MAKEBOOTDEV(6, 0, 0, 0, 0);
goto out;
}
if (device_is_a(root_device, "rd"))
type = 2;
else if (device_is_a(root_device, "sd"))
type = 4;
else if (device_is_a(root_device, "cd"))
type = 4;
else if (device_is_a(root_device, "md"))
goto out;
else {
printf("WARNING: strange root device!\n");
goto out;
}
dd = dev_data_lookup(root_device);
switch (type) {
case 2:
for (cdd = LIST_FIRST(&dev_data_list_hpib), ctlr = 0;
cdd != NULL; cdd = LIST_NEXT(cdd, dd_clist), ctlr++) {
if (cdd->dd_dev ==
device_parent(device_parent(root_device))) {
bootdev = MAKEBOOTDEV(type,
ctlr, dd->dd_slave, dd->dd_punit,
DISKPART(rootdev));
break;
}
}
break;
case 4:
for (cdd = LIST_FIRST(&dev_data_list_scsi), ctlr = 0;
cdd != NULL; cdd = LIST_NEXT(cdd, dd_clist), ctlr++) {
if (cdd->dd_dev ==
device_parent(device_parent(root_device))) {
bootdev = MAKEBOOTDEV(type,
ctlr, dd->dd_slave, dd->dd_punit,
DISKPART(rootdev));
break;
}
}
break;
}
out:
for (dd = LIST_FIRST(&dev_data_list); dd != NULL; ) {
cdd = dd;
dd = LIST_NEXT(dd, dd_list);
kmem_free(cdd, sizeof(*cdd));
}
}
static struct dev_data *
dev_data_lookup(device_t dev)
{
struct dev_data *dd;
for (dd = LIST_FIRST(&dev_data_list); dd != NULL;
dd = LIST_NEXT(dd, dd_list))
if (dd->dd_dev == dev)
return dd;
panic("dev_data_lookup");
}
static void
dev_data_insert(struct dev_data *dd, ddlist_t *ddlist)
{
struct dev_data *de;
#ifdef DIAGNOSTIC
if (dd->dd_scode < 0 || dd->dd_scode > 255) {
printf("bogus select code for %s\n", device_xname(dd->dd_dev));
panic("dev_data_insert");
}
#endif
de = LIST_FIRST(ddlist);
if (de == NULL) {
LIST_INSERT_HEAD(ddlist, dd, dd_clist);
return;
}
for (; LIST_NEXT(de, dd_clist) != NULL; de = LIST_NEXT(de, dd_clist)) {
if (de->dd_scode > dd->dd_scode) {
LIST_INSERT_BEFORE(de, dd, dd_clist);
return;
}
}
LIST_INSERT_AFTER(de, dd, dd_clist);
}
int conscode;
void *conaddr;
static bool cninit_deferred;
#if NSTI_SGC > 0
static int consslot = -1;
#endif
void
hp300_cninit(void)
{
struct bus_space_tag tag;
bus_space_tag_t bst;
bst = &tag;
memset(bst, 0, sizeof(struct bus_space_tag));
bst->bustype = HP300_BUS_SPACE_INTIO;
#if NCOM_FRODO > 0
if (!com_frodo_cnattach(bst, FRODO_BASE + FRODO_APCI_OFFSET(1),
CONSCODE_INTERNAL))
return;
#endif
#if NCOM_DIO > 0
if (!dio_scan(com_dio_cnattach))
return;
#endif
#if NDCM > 0
if (!dio_scan(dcmcnattach))
return;
#endif
#ifndef CONSCODE
#if NDVBOX > 0
if (!dvboxcnattach(bst, FB_BASE, CONSCODE_INTERNAL))
goto find_kbd;
#endif
#if NGBOX > 0
if (!gboxcnattach(bst, FB_BASE, CONSCODE_INTERNAL))
goto find_kbd;
#endif
#if NRBOX > 0
if (!rboxcnattach(bst, FB_BASE, CONSCODE_INTERNAL))
goto find_kbd;
#endif
#if NTOPCAT > 0
if (!topcatcnattach(bst, FB_BASE, CONSCODE_INTERNAL))
goto find_kbd;
#endif
#endif
#if NDVBOX > 0
if (!dio_scan(dvboxcnattach))
goto find_kbd;
#endif
#if NGBOX > 0
if (!dio_scan(gboxcnattach))
goto find_kbd;
#endif
#if NHYPER > 0
if (!dio_scan(hypercnattach))
goto find_kbd;
#endif
#if NRBOX > 0
if (!dio_scan(rboxcnattach))
goto find_kbd;
#endif
#if NTOPCAT > 0
if (!dio_scan(topcatcnattach))
goto find_kbd;
#endif
#if NTVRX > 0
if (!dio_scan(tvrxcnattach))
goto find_kbd;
#endif
#if NSTI_DIO > 0
if (!dio_scan(sti_dio_cnprobe)) {
cninit_deferred = true;
goto find_kbd;
}
#endif
#if NGENDIOFB > 0
if (!dio_scan(gendiofbcnattach))
goto find_kbd;
#endif
#if NSTI_SGC > 0
if (machineid == HP_400 ||
machineid == HP_425 ||
machineid == HP_433) {
struct bus_space_tag sgc_tag;
bus_space_tag_t sgc_bst;
u_int slot;
sgc_bst = &sgc_tag;
memset(sgc_bst, 0, sizeof(struct bus_space_tag));
sgc_bst->bustype = HP300_BUS_SPACE_SGC;
for (slot = 0; slot < SGC_NSLOTS; slot++) {
if (sti_sgc_cnprobe(sgc_bst, slot)) {
cninit_deferred = true;
consslot = slot;
goto find_kbd;
}
}
}
#endif
#if (NDVBOX + NGBOX + NRBOX + NTOPCAT + NDVBOX + NGBOX + NHYPER + NRBOX + \
NTOPCAT + NTVRX + NGENDIOFB + NSTI_DIO + NSTI_SGC) > 0
find_kbd:
#endif
#if NDNKBD > 0
dnkbdcnattach(bst, FRODO_BASE + FRODO_APCI_OFFSET(0))
#endif
#if NHILKBD > 0
hilkbdcnattach(bst, HIL_BASE);
#endif
return;
}
static int
dio_scan(int (*func)(bus_space_tag_t, bus_addr_t, int))
{
#ifndef CONSCODE
int scode, sctop;
sctop = DIO_SCMAX(machineid);
for (scode = 0; scode < sctop; ++scode) {
if (DIO_INHOLE(scode) || ((scode == 7) && internalhpib))
continue;
if (!dio_scode_probe(scode, func))
return 0;
}
#else
if (!dio_scode_probe(CONSCODE, func))
return 0;
#endif
return 1;
}
static int
dio_scode_probe(int scode, int (*func)(bus_space_tag_t, bus_addr_t, int))
{
struct bus_space_tag tag;
bus_space_tag_t bst;
void *pa, *va;
bst = &tag;
memset(bst, 0, sizeof(struct bus_space_tag));
bst->bustype = HP300_BUS_SPACE_DIO;
pa = dio_scodetopa(scode);
va = iomap(pa, PAGE_SIZE);
if (va == 0)
return 1;
if (badaddr(va)) {
iounmap(va, PAGE_SIZE);
return 1;
}
iounmap(va, PAGE_SIZE);
return (*func)(bst, (bus_addr_t)pa, scode);
}
void
hp300_cninit_deferred(void)
{
if (!cninit_deferred)
return;
#if NSTI_DIO > 0
if (machineid == HP_362 ||
machineid == HP_382) {
struct bus_space_tag dio_tag;
bus_space_tag_t dio_bst;
dio_bst = &dio_tag;
memset(dio_bst, 0, sizeof(struct bus_space_tag));
dio_bst->bustype = HP300_BUS_SPACE_DIO;
sti_dio_cnattach(dio_bst, conscode);
}
#endif
#if NSTI_SGC > 0
if (machineid == HP_400 ||
machineid == HP_425 ||
machineid == HP_433) {
struct bus_space_tag sgc_tag;
bus_space_tag_t sgc_bst;
sgc_bst = &sgc_tag;
memset(sgc_bst, 0, sizeof(struct bus_space_tag));
sgc_bst->bustype = HP300_BUS_SPACE_SGC;
sti_sgc_cnattach(sgc_bst, consslot);
}
#endif
}
void
iomap_init(void)
{
int error;
extio_arena = vmem_init(&extio_arena_store,
"extio",
0,
0,
PAGE_SIZE,
NULL,
NULL,
NULL,
0,
VM_NOSLEEP | VM_PRIVTAGS,
IPL_NONE);
KASSERT(extio_arena != NULL);
vmem_add_bts(extio_arena, extio_btag_store, EXTIO_BTAG_COUNT);
error = vmem_add(extio_arena, (vmem_addr_t)extiobase,
ptoa(EIOMAPSIZE), VM_NOSLEEP);
KASSERT(error == 0);
}
void *
iomap(void *vpa, int size)
{
paddr_t pa = (paddr_t)vpa;
vmem_addr_t kva;
int error;
KASSERT((pa & PGOFSET) == 0);
KASSERT((size & PGOFSET) == 0);
error = vmem_alloc(extio_arena, size, VM_BESTFIT | VM_NOSLEEP, &kva);
if (error) {
return NULL;
}
vaddr_t va = kva;
vaddr_t eva = kva + size;
while (va < eva) {
pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE,
PMAP_NOCACHE);
va += PAGE_SIZE;
pa += PAGE_SIZE;
}
return (void *)kva;
}
void
iounmap(void *kva, int size)
{
vaddr_t va = (vaddr_t)kva;
KASSERT((va & PGOFSET) == 0);
KASSERT((size & PGOFSET) == 0);
KASSERT((uint8_t *)kva >= extiobase);
KASSERT((uint8_t *)kva < extiobase + ptoa(EIOMAPSIZE));
pmap_kremove(va, size);
vmem_free(extio_arena, va, size);
}