162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * starfire.c: Starfire/E10000 support.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 1998 David S. Miller (davem@redhat.com)
662306a36Sopenharmony_ci * Copyright (C) 2000 Anton Blanchard (anton@samba.org)
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <asm/page.h>
1362306a36Sopenharmony_ci#include <asm/oplib.h>
1462306a36Sopenharmony_ci#include <asm/smp.h>
1562306a36Sopenharmony_ci#include <asm/upa.h>
1662306a36Sopenharmony_ci#include <asm/starfire.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/*
1962306a36Sopenharmony_ci * A few places around the kernel check this to see if
2062306a36Sopenharmony_ci * they need to call us to do things in a Starfire specific
2162306a36Sopenharmony_ci * way.
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ciint this_is_starfire = 0;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_civoid check_if_starfire(void)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	phandle ssnode = prom_finddevice("/ssp-serial");
2862306a36Sopenharmony_ci	if (ssnode != 0 && (s32)ssnode != -1)
2962306a36Sopenharmony_ci		this_is_starfire = 1;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * Each Starfire board has 32 registers which perform translation
3462306a36Sopenharmony_ci * and delivery of traditional interrupt packets into the extended
3562306a36Sopenharmony_ci * Starfire hardware format.  Essentially UPAID's now have 2 more
3662306a36Sopenharmony_ci * bits than in all previous Sun5 systems.
3762306a36Sopenharmony_ci */
3862306a36Sopenharmony_cistruct starfire_irqinfo {
3962306a36Sopenharmony_ci	unsigned long imap_slots[32];
4062306a36Sopenharmony_ci	unsigned long tregs[32];
4162306a36Sopenharmony_ci	struct starfire_irqinfo *next;
4262306a36Sopenharmony_ci	int upaid, hwmid;
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic struct starfire_irqinfo *sflist = NULL;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* Beam me up Scott(McNeil)y... */
4862306a36Sopenharmony_civoid starfire_hookup(int upaid)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct starfire_irqinfo *p;
5162306a36Sopenharmony_ci	unsigned long treg_base, hwmid, i;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	p = kmalloc(sizeof(*p), GFP_KERNEL);
5462306a36Sopenharmony_ci	if (!p) {
5562306a36Sopenharmony_ci		prom_printf("starfire_hookup: No memory, this is insane.\n");
5662306a36Sopenharmony_ci		prom_halt();
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci	treg_base = 0x100fc000000UL;
5962306a36Sopenharmony_ci	hwmid = ((upaid & 0x3c) << 1) |
6062306a36Sopenharmony_ci		((upaid & 0x40) >> 4) |
6162306a36Sopenharmony_ci		(upaid & 0x3);
6262306a36Sopenharmony_ci	p->hwmid = hwmid;
6362306a36Sopenharmony_ci	treg_base += (hwmid << 33UL);
6462306a36Sopenharmony_ci	treg_base += 0x200UL;
6562306a36Sopenharmony_ci	for (i = 0; i < 32; i++) {
6662306a36Sopenharmony_ci		p->imap_slots[i] = 0UL;
6762306a36Sopenharmony_ci		p->tregs[i] = treg_base + (i * 0x10UL);
6862306a36Sopenharmony_ci		/* Lets play it safe and not overwrite existing mappings */
6962306a36Sopenharmony_ci		if (upa_readl(p->tregs[i]) != 0)
7062306a36Sopenharmony_ci			p->imap_slots[i] = 0xdeadbeaf;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci	p->upaid = upaid;
7362306a36Sopenharmony_ci	p->next = sflist;
7462306a36Sopenharmony_ci	sflist = p;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ciunsigned int starfire_translate(unsigned long imap,
7862306a36Sopenharmony_ci				unsigned int upaid)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct starfire_irqinfo *p;
8162306a36Sopenharmony_ci	unsigned int bus_hwmid;
8262306a36Sopenharmony_ci	unsigned int i;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	bus_hwmid = (((unsigned long)imap) >> 33) & 0x7f;
8562306a36Sopenharmony_ci	for (p = sflist; p != NULL; p = p->next)
8662306a36Sopenharmony_ci		if (p->hwmid == bus_hwmid)
8762306a36Sopenharmony_ci			break;
8862306a36Sopenharmony_ci	if (p == NULL) {
8962306a36Sopenharmony_ci		prom_printf("XFIRE: Cannot find irqinfo for imap %016lx\n",
9062306a36Sopenharmony_ci			    ((unsigned long)imap));
9162306a36Sopenharmony_ci		prom_halt();
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci	for (i = 0; i < 32; i++) {
9462306a36Sopenharmony_ci		if (p->imap_slots[i] == imap ||
9562306a36Sopenharmony_ci		    p->imap_slots[i] == 0UL)
9662306a36Sopenharmony_ci			break;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci	if (i == 32) {
9962306a36Sopenharmony_ci		printk("starfire_translate: Are you kidding me?\n");
10062306a36Sopenharmony_ci		panic("Lucy in the sky....");
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci	p->imap_slots[i] = imap;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* map to real upaid */
10562306a36Sopenharmony_ci	upaid = (((upaid & 0x3c) << 1) |
10662306a36Sopenharmony_ci		 ((upaid & 0x40) >> 4) |
10762306a36Sopenharmony_ci		 (upaid & 0x3));
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	upa_writel(upaid, p->tregs[i]);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return i;
11262306a36Sopenharmony_ci}
113