#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: platform.c,v 1.29 2023/12/20 15:29:06 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/intr.h>
#include <sys/inttypes.h>
#include <powerpc/pio.h>
#include <powerpc/psl.h>
#include <dev/pci/pcivar.h>
#include <machine/platform.h>
#include <machine/pcipnp.h>
#include <machine/residual.h>
u_int32_t prep_pci_baseaddr = 0x80000cf8;
u_int32_t prep_pci_basedata = 0x80000cfc;
struct pciroutinginfo *pciroutinginfo;
extern struct prep_pci_chipset *genppc_pct;
extern void pci_intr_fixup_ibm_6015(void);
#if NMCCLOCK > 0
extern void ds1585_reboot(void);
#endif
struct platform_quirkdata platform_quirks[] = {
{ "IBM PPS Model 6015", PLAT_QUIRK_INTRFIXUP,
pci_intr_fixup_ibm_6015, NULL, 0 },
{ "(e1)", PLAT_QUIRK_ISA_HANDLER, NULL, NULL, EXT_INTR_I8259 },
{ "000000000000000000000000000(e2)", PLAT_QUIRK_ISA_HANDLER, NULL,
NULL, EXT_INTR_I8259 },
{ NULL, 0, NULL, NULL, 0 }
};
int
find_platform_quirk(const char *model)
{
int i;
for (i = 0; platform_quirks[i].model != NULL; i++)
if (strcmp(model, platform_quirks[i].model) == 0)
return i;
return -1;
}
void
cpu_setup_prep_generic(device_t dev)
{
u_int8_t l2ctrl;
l2ctrl = inb(PREP_BUS_SPACE_IO + 0x81c);
(void)inb(PREP_BUS_SPACE_IO + 0x80c);
outb(PREP_BUS_SPACE_IO + 0x81c, l2ctrl | 0xc0);
}
static void
reset_prep_generic(void)
{
u_int8_t reg;
mtmsr(mfmsr() | PSL_IP);
#if NMCCLOCK > 0
ds1585_reboot();
#endif
reg = inb(PREP_BUS_SPACE_IO + 0x92);
reg &= ~1UL;
outb(PREP_BUS_SPACE_IO + 0x92, reg);
reg = inb(PREP_BUS_SPACE_IO + 0x92);
reg |= 1;
outb(PREP_BUS_SPACE_IO + 0x92, reg);
}
void
reset_prep(void)
{
int i;
i = find_platform_quirk(res->VitalProductData.PrintableModel);
if (i != -1) {
if (platform_quirks[i].quirk & PLAT_QUIRK_RESET &&
platform_quirks[i].reset != NULL)
(*platform_quirks[i].reset)();
}
reset_prep_generic();
}
static int
count_pnp_pci_devices(void *v, int *device)
{
int item, size, i;
int tag = *(unsigned char *)v;
unsigned char *q = v;
struct _L4_Pack *pack = v;
struct _L4_PPCPack *p = &pack->L4_Data.L4_PPCPack;
item = tag_large_item_name(tag);
size = (q[1] | (q[2] << 8)) + 3 ;
if (item != LargeVendorItem)
return size;
if (p->Type != LV_PCIBridge)
return size;
for (i = 20; i < size - 4; i += 12)
(*device)++;
return size;
}
static int
pnp_small_pkt(void *v)
{
int tag = *(unsigned char *)v;
return tag_small_count(tag) + 1 ;
}
int
pci_chipset_tag_type(void)
{
PPC_DEVICE *dev;
uint32_t addr, data, l;
unsigned char *p;
int size;
dev = find_nth_pnp_device("PNP0A03", 0, 0);
if (dev == NULL)
return PCIBridgeIndirect;
l = be32toh(dev->AllocatedOffset);
p = res->DevicePnPHeap + l;
if (p == NULL)
return PCIBridgeIndirect;
for (; p[0] != END_TAG; p += size) {
if (tag_type(p[0]) == PNP_SMALL)
size = pnp_small_pkt(p);
else {
size = pnp_pci_configbase(p, &addr, &data);
if (addr != 0 && data != 0) {
prep_pci_baseaddr = addr;
prep_pci_basedata = data;
break;
}
}
}
return dev->DeviceId.Interface;
}
static int
create_intr_map(void *v, prop_dictionary_t dict)
{
prop_dictionary_t sub;
int item, size, i, j, numslots;
int tag = *(unsigned char *)v;
unsigned char *q = v;
PCIInfoPack *pi = v;
struct _L4_Pack *pack = v;
struct _L4_PPCPack *p = &pack->L4_Data.L4_PPCPack;
item = tag_large_item_name(tag);
size = (q[1] | (q[2] << 8)) + 3 ;
if (item != LargeVendorItem)
return size;
if (p->Type != LV_PCIBridge)
return size;
numslots = (le16dec(&pi->count0)-21)/sizeof(IntrMap);
for (i = 0; i < numslots; i++) {
int lines[MAX_PCI_INTRS] = { 0, 0, 0, 0 };
int offset = 0;
int dev;
char key[20];
sub = prop_dictionary_create_with_capacity(MAX_PCI_INTRS);
dev = pi->map[i].devfunc / 0x8;
for (j = 0; j < MAX_PCI_INTRS; j++) {
int line = bswap16(pi->map[i].intr[j]);
if (line != 0xffff)
lines[j] = 1;
}
if (pi->map[i].intrctrltype == 2)
offset += I8259_INTR_NUM;
for (j = 0; j < MAX_PCI_INTRS; j++) {
int line = bswap16(pi->map[i].intr[j]);
prop_number_t intr_num;
if (line == 0xffff || lines[j] == 0)
intr_num = prop_number_create_integer(0);
else
intr_num = prop_number_create_integer(
(line & 0x7fff) + offset);
snprintf(key, sizeof(key), "pin-%c", 'A' + j);
prop_dictionary_set(sub, key, intr_num);
prop_object_release(intr_num);
}
snprintf(key, sizeof(key), "devfunc-%d", dev);
prop_dictionary_set(dict, key, sub);
prop_object_release(sub);
}
return size;
}
void
setup_pciintr_map(struct genppc_pci_chipset_businfo *pbi, int bus, int device,
int func)
{
int devfunc, nbus, size, i, found = 0, nrofpcidevs = 0;
uint32_t l;
PPC_DEVICE *dev;
BUS_ACCESS *busacc;
unsigned char *p;
prop_dictionary_t dict;
if (res->Revision == 0)
return;
devfunc = device * 8 + func;
nbus = count_pnp_devices("PNP0A03");
for (i = 0; i < nbus; i++) {
dev = find_nth_pnp_device("PNP0A03", 0, i);
busacc = &dev->BusAccess;
l = be32toh(dev->AllocatedOffset);
p = res->DevicePnPHeap + l;
if (p == NULL)
return;
if (busacc->PCIAccess.BusNumber == bus &&
busacc->PCIAccess.DevFuncNumber == devfunc) {
found++;
break;
}
}
if (!found) {
aprint_error("Couldn't find PNP data for bus %d devfunc 0x%x\n",
bus, devfunc);
return;
}
for (; p[0] != END_TAG; p += size) {
if (tag_type(p[0]) == PNP_SMALL)
size = pnp_small_pkt(p);
else
size = count_pnp_pci_devices(p, &nrofpcidevs);
}
dict = prop_dictionary_create_with_capacity(nrofpcidevs*2);
KASSERT(dict != NULL);
prop_dictionary_set(pbi->pbi_properties, "prep-pci-intrmap", dict);
p = res->DevicePnPHeap + l;
for (; p[0] != END_TAG; p += size) {
if (tag_type(p[0]) == PNP_SMALL)
size = pnp_small_pkt(p);
else
size = create_intr_map(p, dict);
}
prop_object_release(dict);
}
void
setup_pciroutinginfo(void)
{
int i;
i = find_platform_quirk(res->VitalProductData.PrintableModel);
if (i == -1)
return;
if (platform_quirks[i].quirk & PLAT_QUIRK_INTRFIXUP &&
platform_quirks[i].pci_intr_fixup != NULL)
(*platform_quirks[i].pci_intr_fixup)();
}