root/arch/sparc/kernel/starfire.c
// SPDX-License-Identifier: GPL-2.0
/*
 * starfire.c: Starfire/E10000 support.
 *
 * Copyright (C) 1998 David S. Miller (davem@redhat.com)
 * Copyright (C) 2000 Anton Blanchard (anton@samba.org)
 */

#include <linux/kernel.h>
#include <linux/slab.h>

#include <asm/page.h>
#include <asm/oplib.h>
#include <asm/smp.h>
#include <asm/upa.h>
#include <asm/starfire.h>

/*
 * A few places around the kernel check this to see if
 * they need to call us to do things in a Starfire specific
 * way.
 */
int this_is_starfire = 0;

void check_if_starfire(void)
{
        phandle ssnode = prom_finddevice("/ssp-serial");
        if (ssnode != 0 && (s32)ssnode != -1)
                this_is_starfire = 1;
}

/*
 * Each Starfire board has 32 registers which perform translation
 * and delivery of traditional interrupt packets into the extended
 * Starfire hardware format.  Essentially UPAID's now have 2 more
 * bits than in all previous Sun5 systems.
 */
struct starfire_irqinfo {
        unsigned long imap_slots[32];
        unsigned long tregs[32];
        struct starfire_irqinfo *next;
        int upaid, hwmid;
};

static struct starfire_irqinfo *sflist = NULL;

/* Beam me up Scott(McNeil)y... */
void starfire_hookup(int upaid)
{
        struct starfire_irqinfo *p;
        unsigned long treg_base, hwmid, i;

        p = kmalloc_obj(*p);
        if (!p) {
                prom_printf("starfire_hookup: No memory, this is insane.\n");
                prom_halt();
        }
        treg_base = 0x100fc000000UL;
        hwmid = ((upaid & 0x3c) << 1) |
                ((upaid & 0x40) >> 4) |
                (upaid & 0x3);
        p->hwmid = hwmid;
        treg_base += (hwmid << 33UL);
        treg_base += 0x200UL;
        for (i = 0; i < 32; i++) {
                p->imap_slots[i] = 0UL;
                p->tregs[i] = treg_base + (i * 0x10UL);
                /* Lets play it safe and not overwrite existing mappings */
                if (upa_readl(p->tregs[i]) != 0)
                        p->imap_slots[i] = 0xdeadbeaf;
        }
        p->upaid = upaid;
        p->next = sflist;
        sflist = p;
}

unsigned int starfire_translate(unsigned long imap,
                                unsigned int upaid)
{
        struct starfire_irqinfo *p;
        unsigned int bus_hwmid;
        unsigned int i;

        bus_hwmid = (((unsigned long)imap) >> 33) & 0x7f;
        for (p = sflist; p != NULL; p = p->next)
                if (p->hwmid == bus_hwmid)
                        break;
        if (p == NULL) {
                prom_printf("XFIRE: Cannot find irqinfo for imap %016lx\n",
                            ((unsigned long)imap));
                prom_halt();
        }
        for (i = 0; i < 32; i++) {
                if (p->imap_slots[i] == imap ||
                    p->imap_slots[i] == 0UL)
                        break;
        }
        if (i == 32) {
                printk("starfire_translate: Are you kidding me?\n");
                panic("Lucy in the sky....");
        }
        p->imap_slots[i] = imap;

        /* map to real upaid */
        upaid = (((upaid & 0x3c) << 1) |
                 ((upaid & 0x40) >> 4) |
                 (upaid & 0x3));

        upa_writel(upaid, p->tregs[i]);

        return i;
}