/* $NetBSD: ofw_autoconf.c,v 1.26 2023/09/23 21:26:16 andvar Exp $ */
/*
* Copyright (C) 1995, 1996 Wolfgang Solfrank.
* Copyright (C) 1995, 1996 TooLs GmbH.
* All rights reserved.
*
* 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 TooLs GmbH.
* 4. The name of TooLs GmbH may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
*/
/*
* OF_1.x (at least) always returns addr == 0 for
* SCSI disks (i.e. "/bandit@.../.../sd@0,0").
* also check for .../disk@ which some Adaptec firmware uses
*/
lastp = strrchr(cbootpath, '/');
if (lastp != NULL) {
lastp++;
if ((strncmp(lastp, "sd@", 3) == 0
&& strncmp(last, "sd@", 3) == 0) ||
(strncmp(lastp, "disk@", 5) == 0
&& strncmp(last, "disk@", 5) == 0))
strcpy(lastp, last);
} else {
lastp = cbootpath;
}
/*
* At this point, cbootpath contains like:
* "/pci@80000000/mac-io@10/ata-3@20000/disk"
*
* The last component may have no address... so append it.
*/
if (strchr(lastp, '@') == NULL) {
/* Append it. */
if ((p = strrchr(last, '@')) != NULL)
strcat(cbootpath, p);
}
/*
* device_register is called from config_attach as each device is
* attached. We use it to find the NetBSD device corresponding to the
* known OF boot device.
*/
void
device_register(device_t dev, void *aux)
{
static device_t parent;
static char *bp = bootpath + 1, *cp = cbootpath;
unsigned long addr, addr2;
char *p;
#if NGTPCI > 0
struct powerpc_bus_space *gtpci_mem_bs_tag = NULL;
#endif
/* Skip over devices not represented in the OF tree. */
if (device_is_a(dev, "mainbus")) {
parent = dev;
return;
}
/* skip over CPUs */
if (device_is_a(dev, "cpu")) {
return;
}
if (node == boot_node) {
/* we netbooted from whatever this is */
booted_device = dev;
}
/* see if this is going to be console */
memset(name, 0, sizeof(name));
OF_getprop(node, "device_type", name, sizeof(name));
if (strcmp(name, "display") == 0 ||
strcmp(name, "ATY,DDParent") == 0 ||
pci_class == PCI_CLASS_DISPLAY) {
/* setup display properties for fb driver */
prop_dictionary_set_bool(dict, "is_console", 0);
copy_disp_props(dev, node, dict);
}
if (pci_class == PCI_CLASS_NETWORK) {
of_to_dataprop(dict, node, "local-mac-address",
"mac-address");
of_to_dataprop(dict, node, "shared-pins",
"shared-pins");
}
}
#ifdef macppc
/*
* XXX
* some macppc boxes have onboard devices where parts or all of
* the PCI_INTERRUPT register are hardwired to 0
*/
if (pa->pa_intrpin == 0)
pa->pa_intrpin = 1;
#endif
}
if (booted_device)
return;
/*
* Skip over devices that are really just layers of NetBSD
* autoconf(9) we should just skip as they do not have any
* OFW devices.
* XXX except on G5, where we have /ht/pci* instead of /pci*
*/
if (device_is_a(device_parent(dev), "atapibus") ||
device_is_a(device_parent(dev), "atabus") ||
#ifndef PMAC_G5
device_is_a(device_parent(dev), "pci") ||
#endif
device_is_a(device_parent(dev), "scsibus")) {
if (device_parent(device_parent(dev)) != parent) {
return;
}
} else {
if (device_parent(dev) != parent)
return;
}
/*
* Get the address part of the current path component. The
* last component of the canonical bootpath may have no
* address (eg, "disk"), in which case we need to get the
* address from the original bootpath instead.
*/
p = strchr(cp, '@');
if (!p) {
if (bp)
p = strchr(bp, '@');
if (!p)
addr = 0;
else
addr = strtoul(p + 1, &p, 16);
} else
addr = strtoul(p + 1, &p, 16);
/* if the current path has more address, grab that too */
if (p && *p == ',')
addr2 = strtoul(p + 1, &p, 16);
else
addr2 = 0;
if (device_is_a(device_parent(dev), "mainbus")) {
struct confargs *ca = aux;
if (strcmp(ca->ca_name, "ofw") == 0) /* XXX */
return;
if (strcmp(ca->ca_name, "gt") == 0)
parent = dev;
if (addr != ca->ca_reg[0])
return;
if (addr2 != 0 && addr2 != ca->ca_reg[1])
return;
} else if (device_is_a(device_parent(dev), "gt")) {
/*
* Special handle for MV64361 on PegasosII(ofppc).
*/
if (device_is_a(dev, "mvgbec")) {
/*
* Fix cp to /port@N from /ethernet/portN. (N is 0...2)
*/
static char fix_cp[8] = "/port@N";
if (addr != pa->pa_device ||
addr2 != pa->pa_function)
return;
} else if (device_is_a(device_parent(dev), "obio")) {
struct confargs *ca = aux;
if (addr != ca->ca_reg[0])
return;
} else if (device_is_a(device_parent(dev), "scsibus") ||
device_is_a(device_parent(dev), "atapibus")) {
struct scsipibus_attach_args *sa = aux;
/* periph_target is target for scsi, drive # for atapi */
if (addr != sa->sa_periph->periph_target)
return;
} else if (device_is_a(device_parent(device_parent(dev)), "pciide") ||
device_is_a(device_parent(device_parent(dev)), "viaide") ||
device_is_a(device_parent(device_parent(dev)), "slide")) {
struct ata_device *adev = aux;
if (addr != adev->adev_channel ||
addr2 != adev->adev_drv_data->drive)
return;
} else if (device_is_a(device_parent(device_parent(dev)), "wdc")) {
struct ata_device *adev = aux;
if (addr != adev->adev_drv_data->drive)
return;
} else if (device_is_a(dev, "pci")) {
if (addr != device_unit(dev))
return;
} else if (device_is_a(device_parent(dev), "atabus")) {
/*
* XXX
* on svwsata this is the channel number and we ignore the
* drive number which is always 0 anyway
* needs to be revisited for other (S)ATA cards
*/
struct ata_device *adev = aux;
if (addr != adev->adev_channel)
return;
/* we have our match, cut off the rest */
if (p) *p = 0;
} else
return;
/* If we reach this point, then dev is a match for the current
* path component.
*/
if (p && *p) {
parent = dev;
cp = p;
bp = strchr(bp, '/');
if (bp)
bp++;
return;
} else {
booted_device = dev;
booted_partition = 0; /* XXX -- should be extracted from bootpath */
return;
}
}
/*
* Find OF-device corresponding to the PCI device.
*/
int
pcidev_to_ofdev(pci_chipset_tag_t pc, pcitag_t tag)
{
int bus, dev, func;
u_int reg[5];
int p, q;
int l, b, d, f;
pci_decompose_tag(pc, tag, &bus, &dev, &func);
for (q = OF_peer(0); q; q = p) {
l = OF_getprop(q, "assigned-addresses", reg, sizeof(reg));
if (l > 4) {
b = (reg[0] >> 16) & 0xff;
d = (reg[0] >> 11) & 0x1f;
f = (reg[0] >> 8) & 0x07;
if (b == bus && d == dev && f == func)
return q;
}
if ((p = OF_child(q)))
continue;
while (q) {
if ((p = OF_peer(q)))
break;
q = OF_parent(q);
}
}
return 0;
}