#include <sys/cdefs.h>
__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 <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>
#include <sys/kmem.h>
#ifdef KGDB
#include <sys/kgdb.h>
#endif
#include <machine/pdc.h>
#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 <hppa/hppa/machdep.h>
#include <hppa/dev/cpudevs.h>
#include <hppa/gsc/gscbusvar.h>
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;
u_int hm_modindex;
};
#define HPPA_SYSTEMMAPMODULES 256
#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);
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);
void
cpu_configure(void)
{
if (boothowto & RB_KDB) {
#ifdef KGDB
extern int hppa_kgdb_attached;
if (hppa_kgdb_attached)
kgdb_connect(1);
#elif defined(DDB)
Debugger();
#endif
}
splhigh();
if (config_rootfound("mainbus", NULL) == NULL)
panic("no mainbus found");
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
void
hppa_led_ctl(int off, int on, int toggle)
{
int r;
if (machine_ledaddr == NULL)
return;
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
}
}
static void
hppa_led_blinker(void *arg)
{
u_int led_cycle = (u_int) arg;
int leds, led_i, led;
int load;
#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;
}
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]--;
}
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);
hppa_led_ctl(-1, leds, 0);
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
void
cpu_dumpconf(void)
{
extern int dumpsize;
int nblks, dumpblks;
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 (dumpblks > (nblks - ctod(1)))
goto bad;
dumplo = nblks - dumpblks;
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();
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) {
boot_device = dev;
}
}
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++) {
}
for (i = 0 ; i < 6 && n < 6 ; i++) {
if (PAGE0->mem_boot.pz_dp.dp_bc[i] < 0)
continue;
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;
}
boot_device = dev;
}
if (device_is_a(dev, "ppb") && 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[3] &&
paa->pa_function == PAGE0->mem_boot.pz_dp.dp_bc[4]) {
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) {
boot_device = dev;
}
}
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]) {
boot_device = dev;
}
}
hppa_pagezero_unmap(pagezero_cookie);
return;
}
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
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 (hm)
continue;
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 <hppa/dev/cpudevs_data.h>
};
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;
}
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;
for (i = 0; i < 6; i++) {
if (hm->hm_dp.dp_bc[i] != -1)
break;
}
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;
}
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));
}
}