code:
// SPDX-License-Identifier: GPL-2.0
/*
 *	linux/arch/alpha/kernel/core_titan.c
 *
 * Code common to all TITAN core logic chips.
 */

#define __EXTERN_INLINE inline
#include <asm/io.h>
#include <asm/core_titan.h>
#undef __EXTERN_INLINE

#include <linux/module.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/memblock.h>

#include <asm/ptrace.h>
#include <asm/smp.h>
#include <asm/tlbflush.h>
#include <asm/vga.h>

#include "proto.h"
#include "pci_impl.h"

/* Save Titan configuration data as the console had it set up.  */

struct
{
	unsigned long wsba[4];
	unsigned long wsm[4];
	unsigned long tba[4];
} saved_config[4] __attribute__((common));

/*
 * Is PChip 1 present? No need to query it more than once.
 */
static int titan_pchip1_present;

/*
 * BIOS32-style PCI interface:
 */

#define DEBUG_CONFIG 0

#if DEBUG_CONFIG
# define DBG_CFG(args)	printk args
#else
# define DBG_CFG(args)
#endif

/*
 * Routines to access TIG registers.
 */
static inline volatile unsigned long *
mk_tig_addr(int offset)
{
	return (volatile unsigned long *)(TITAN_TIG_SPACE + (offset << 6));
}

static inline u8
titan_read_tig(int offset, u8 value)
{
	volatile unsigned long *tig_addr = mk_tig_addr(offset);
	return (u8)(*tig_addr & 0xff);
}

static inline void
titan_write_tig(int offset, u8 value)
{
	volatile unsigned long *tig_addr = mk_tig_addr(offset);
	*tig_addr = (unsigned long)value;
}

/*
 * Given a bus, device, and function number, compute resulting
 * configuration space address
 * accordingly.  It is therefore not safe to have concurrent
 * invocations to configuration space access routines, but there
 * really shouldn't be any need for this.
 *
 * Note that all config space accesses use Type 1 address format.
 *
 * Note also that type 1 is determined by non-zero bus number.
 *
 * Type 1:
 *
 *  3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
 *  3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 *	31:24	reserved
 *	23:16	bus number (8 bits = 128 possible buses)
 *	15:11	Device number (5 bits)
 *	10:8	function number
 *	 7:2	register number
 *
 * Notes:
 *	The function number selects which function of a multi-function device
 *	(e.g., SCSI and Ethernet).
 *
 *	The register selects a DWORD (32 bit) register offset.  Hence it
 *	doesn't get shifted by 2 bits as we want to "drop" the bottom two
 *	bits.
 */

static int
mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where,
	     unsigned long *pci_addr, unsigned char *type1)
{
	struct pci_controller *hose = pbus->sysdata;
	unsigned long addr;
	u8 bus = pbus->number;

	DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, "
		 "pci_addr=0x%p, type1=0x%p)\n",
		 bus, device_fn, where, pci_addr, type1));

	if (!pbus->parent) /* No parent means peer PCI bus. */
		bus = 0;
        *type1 = (bus != 0);

        addr = (bus << 16) | (device_fn << 8) | where;
	addr |= hose->config_space_base;

	*pci_addr = addr;
	DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
	return 0;
}

static int
titan_read_config(struct pci_bus *bus, unsigned int devfn, int where,
		  int size, u32 *value)
{
	unsigned long addr;
	unsigned char type1;

	if (mk_conf_addr(bus, devfn, where, &addr, &type1))
		return PCIBIOS_DEVICE_NOT_FOUND;

	switch (size) {
	case 1:
		*value = __kernel_ldbu(*(vucp)addr);
		break;
	case 2:
		*value = __kernel_ldwu(*(vusp)addr);
		break;
	case 4:
		*value = *(vuip)addr;
		break;
	}

	return PCIBIOS_SUCCESSFUL;
}

static int
titan_write_config(struct pci_bus *bus, unsigned int devfn, int where,
		   int size, u32 value)
{
	unsigned long addr;
	unsigned char type1;

	if (mk_conf_addr(bus, devfn, where, &addr, &type1))
		return PCIBIOS_DEVICE_NOT_FOUND;

	switch (size) {
	case 1:
		__kernel_stb(value, *(vucp)addr);
		mb();
		__kernel_ldbu(*(vucp)addr);
		break;
	case 2:
		__kernel_stw(value, *(vusp)addr);
		mb();
		__kernel_ldwu(*(vusp)addr);
		break;
	case 4:
		*(vuip)addr = value;
		mb();
		*(vuip)addr;
		break;
	}

	return PCIBIOS_SUCCESSFUL;
}

struct pci_ops titan_pci_ops =
{
	.read =		titan_read_config,
	.write =	titan_write_config,
};

void
titan_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end)
{
	titan_pachip *pachip =
	  (hose->index & 1) ? TITAN_pachip1 : TITAN_pachip0;
	titan_pachip_port *port;
	volatile unsigned long *csr;
	unsigned long value;

	/* Get the right hose.  */
	port = &pachip->g_port;
	if (hose->index & 2)
		port = &pachip->a_port;

	/* We can invalidate up to 8 tlb entries in a go.  The flush
	   matches against <31:16> in the pci address.
	   Note that gtlbi* and atlbi* are in the same place in the g_port
	   and a_port, respectively, so the g_port offset can be used
	   even if hose is an a_port */
	csr = &port->port_specific.g.gtlbia.csr;
	if (((start ^ end) & 0xffff0000) == 0)
		csr = &port->port_specific.g.gtlbiv.csr;

	/* For TBIA, it doesn't matter what value we write.  For TBI,
	   it's the shifted tag bits.  */
	value = (start & 0xffff0000) >> 12;

	wmb();
	*csr = value;
	mb();
	*csr;
}

static int
titan_query_agp(titan_pachip_port *port)
{
	union TPAchipPCTL pctl;

	/* set up APCTL */
	pctl.pctl_q_whole = port->pctl.csr;

	return pctl.pctl_r_bits.apctl_v_agp_present;

}

static void __init
titan_init_one_pachip_port(titan_pachip_port *port, int index)
{
	struct pci_controller *hose;

	hose = alloc_pci_controller();
	if (index == 0)
		pci_isa_hose = hose;
	hose->io_space = alloc_resource();
	hose->mem_space = alloc_resource();

	/*
	 * This is for userland consumption.  The 40-bit PIO bias that we
	 * use in the kernel through KSEG doesn't work in the page table
	 * based user mappings. (43-bit KSEG sign extends the physical
	 * address from bit 40 to hit the I/O bit - mapped addresses don't).
	 * So make sure we get the 43-bit PIO bias.
	 */
	hose->sparse_mem_base = 0;
	hose->sparse_io_base = 0;
	hose->dense_mem_base
	  = (TITAN_MEM(index) & 0xffffffffffUL) | 0x80000000000UL;
	hose->dense_io_base
	  = (TITAN_IO(index) & 0xffffffffffUL) | 0x80000000000UL;

	hose->config_space_base = TITAN_CONF(index);
	hose->index = index;

	hose->io_space->start = TITAN_IO(index) - TITAN_IO_BIAS;
	hose->io_space->end = hose->io_space->start + TITAN_IO_SPACE - 1;
	hose->io_space->name = pci_io_names[index];
	hose->io_space->flags = IORESOURCE_IO;

	hose->mem_space->start = TITAN_MEM(index) - TITAN_MEM_BIAS;
	hose->mem_space->end = hose->mem_space->start + 0xffffffff;
	hose->mem_space->name = pci_mem_names[index];
	hose->mem_space->flags = IORESOURCE_MEM;

	if (request_resource(&ioport_resource, hose->io_space) < 0)
		printk(KERN_ERR "Failed to request IO on hose %d\n", index);
	if (request_resource(&iomem_resource, hose->mem_space) < 0)
		printk(KERN_ERR "Failed to request MEM on hose %d\n", index);

	/*
	 * Save the existing PCI window translations.  SRM will
	 * need them when we go to reboot.
	 */
	saved_config[index].wsba[0] = port->wsba[0].csr;
	saved_config[index].wsm[0]  = port->wsm[0].csr;
	saved_config[index].tba[0]  = port->tba[0].csr;

	saved_config[index].wsba[1] = port->wsba[1].csr;
	saved_config[index].wsm[1]  = port->wsm[1].csr;
	saved_config[index].tba[1]  = port->tba[1].csr;

	saved_config[index].wsba[2] = port->wsba[2].csr;
	saved_config[index].wsm[2]  = port->wsm[2].csr;
	saved_config[index].tba[2]  = port->tba[2].csr;

	saved_config[index].wsba[3] = port->wsba[3].csr;
	saved_config[index].wsm[3]  = port->wsm[3].csr;
	saved_config[index].tba[3]  = port->tba[3].csr;

	/*
	 * Set up the PCI to main memory translation windows.
	 *
	 * Note: Window 3 on Titan is Scatter-Gather ONLY.
	 *
	 * Window 0 is scatter-gather 8MB at 8MB (for isa)
	 * Window 1 is direct access 1GB at 2GB
	 * Window 2 is scatter-gather 1GB at 3GB
	 */
	hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000,
				       SMP_CACHE_BYTES);
	hose->sg_isa->align_entry = 8; /* 64KB for ISA */

	hose->sg_pci = iommu_arena_new(hose, 0xc0000000, 0x40000000,
				       SMP_CACHE_BYTES);
	hose->sg_pci->align_entry = 4; /* Titan caches 4 PTEs at a time */

	port->wsba[0].csr = hose->sg_isa->dma_base | 3;
	port->wsm[0].csr  = (hose->sg_isa->size - 1) & 0xfff00000;
	port->tba[0].csr  = virt_to_phys(hose->sg_isa->ptes);

	port->wsba[1].csr = __direct_map_base | 1;
	port->wsm[1].csr  = (__direct_map_size - 1) & 0xfff00000;
	port->tba[1].csr  = 0;

	port->wsba[2].csr = hose->sg_pci->dma_base | 3;
	port->wsm[2].csr  = (hose->sg_pci->size - 1) & 0xfff00000;
	port->tba[2].csr  = virt_to_phys(hose->sg_pci->ptes);

	port->wsba[3].csr = 0;

	/* Enable the Monster Window to make DAC pci64 possible.  */
	port->pctl.csr |= pctl_m_mwin;

	/*
	 * If it's an AGP port, initialize agplastwr.
	 */
	if (titan_query_agp(port))
		port->port_specific.a.agplastwr.csr = __direct_map_base;

	titan_pci_tbi(hose, 0, -1);
}

static void __init
titan_init_pachips(titan_pachip *pachip0, titan_pachip *pachip1)
{
	titan_pchip1_present = TITAN_cchip->csc.csr & 1L<<14;

	/* Init the ports in hose order... */
	titan_init_one_pachip_port(&pachip0->g_port, 0);	/* hose 0 */
	if (titan_pchip1_present)
		titan_init_one_pachip_port(&pachip1->g_port, 1);/* hose 1 */
	titan_init_one_pachip_port(&pachip0->a_port, 2);	/* hose 2 */
	if (titan_pchip1_present)
		titan_init_one_pachip_port(&pachip1->a_port, 3);/* hose 3 */
}

void __init
titan_init_arch(void)
{
#if 0
	printk("%s: titan_init_arch()\n", __func__);
	printk("%s: CChip registers:\n", __func__);
	printk("%s: CSR_CSC 0x%lx\n", __func__, TITAN_cchip->csc.csr);
	printk("%s: CSR_MTR 0x%lx\n", __func__, TITAN_cchip->mtr.csr);
	printk("%s: CSR_MISC 0x%lx\n", __func__, TITAN_cchip->misc.csr);
	printk("%s: CSR_DIM0 0x%lx\n", __func__, TITAN_cchip->dim0.csr);
	printk("%s: CSR_DIM1 0x%lx\n", __func__, TITAN_cchip->dim1.csr);
	printk("%s: CSR_DIR0 0x%lx\n", __func__, TITAN_cchip->dir0.csr);
	printk("%s: CSR_DIR1 0x%lx\n", __func__, TITAN_cchip->dir1.csr);
	printk("%s: CSR_DRIR 0x%lx\n", __func__, TITAN_cchip->drir.csr);

	printk("%s: DChip registers:\n", __func__);
	printk("%s: CSR_DSC 0x%lx\n", __func__, TITAN_dchip->dsc.csr);
	printk("%s: CSR_STR 0x%lx\n", __func__, TITAN_dchip->str.csr);
	printk("%s: CSR_DREV 0x%lx\n", __func__, TITAN_dchip->drev.csr);
#endif

	boot_cpuid = __hard_smp_processor_id();

	/* With multiple PCI busses, we play with I/O as physical addrs.  */
	ioport_resource.end = ~0UL;
	iomem_resource.end = ~0UL;

	/* PCI DMA Direct Mapping is 1GB at 2GB.  */
	__direct_map_base = 0x80000000;
	__direct_map_size = 0x40000000;

	/* Init the PA chip(s).  */
	titan_init_pachips(TITAN_pachip0, TITAN_pachip1);

	/* Check for graphic console location (if any).  */
	find_console_vga_hose();
}

static void
titan_kill_one_pachip_port(titan_pachip_port *port, int index)
{
	port->wsba[0].csr = saved_config[index].wsba[0];
	port->wsm[0].csr  = saved_config[index].wsm[0];
	port->tba[0].csr  = saved_config[index].tba[0];

	port->wsba[1].csr = saved_config[index].wsba[1];
	port->wsm[1].csr  = saved_config[index].wsm[1];
	port->tba[1].csr  = saved_config[index].tba[1];

	port->wsba[2].csr = saved_config[index].wsba[2];
	port->wsm[2].csr  = saved_config[index].wsm[2];
	port->tba[2].csr  = saved_config[index].tba[2];

	port->wsba[3].csr = saved_config[index].wsba[3];
	port->wsm[3].csr  = saved_config[index].wsm[3];
	port->tba[3].csr  = saved_config[index].tba[3];
}

static void
titan_kill_pachips(titan_pachip *pachip0, titan_pachip *pachip1)
{
	if (titan_pchip1_present) {
		titan_kill_one_pachip_port(&pachip1->g_port, 1);
		titan_kill_one_pachip_port(&pachip1->a_port, 3);
	}
	titan_kill_one_pachip_port(&pachip0->g_port, 0);
	titan_kill_one_pachip_port(&pachip0->a_port, 2);
}

void
titan_kill_arch(int mode)
{
	titan_kill_pachips(TITAN_pachip0, TITAN_pachip1);
}

/*
 * IO map support.
 */

void __iomem *
titan_ioportmap(unsigned long addr)
{
	FIXUP_IOADDR_VGA(addr);
	return (void __iomem *)(addr + TITAN_IO_BIAS);
}

void __iomem *
titan_ioremap(unsigned long addr, unsigned long size)
{
	int h = (addr & TITAN_HOSE_MASK) >> TITAN_HOSE_SHIFT;
	unsigned long baddr = addr & ~TITAN_HOSE_MASK;
	unsigned long last = baddr + size - 1;
	struct pci_controller *hose;
	struct vm_struct *area;
	unsigned long vaddr;
	unsigned long *ptes;
	unsigned long pfn;

#ifdef CONFIG_VGA_HOSE
	/*
	 * Adjust the address and hose, if necessary.
	 */
	if (pci_vga_hose && __is_mem_vga(addr)) {
		h = pci_vga_hose->index;
		addr += pci_vga_hose->mem_space->start;
	}
#endif

	/*
	 * Find the hose.
	 */
	for (hose = hose_head; hose; hose = hose->next)
		if (hose->index == h)
			break;
	if (!hose)
		return NULL;

	/*
	 * Is it direct-mapped?
	 */
	if ((baddr >= __direct_map_base) &&
	    ((baddr + size - 1) < __direct_map_base + __direct_map_size)) {
		vaddr = addr - __direct_map_base + TITAN_MEM_BIAS;
		return (void __iomem *) vaddr;
	}

	/*
	 * Check the scatter-gather arena.
	 */
	if (hose->sg_pci &&
	    baddr >= (unsigned long)hose->sg_pci->dma_base &&
	    last < (unsigned long)hose->sg_pci->dma_base + hose->sg_pci->size){

		/*
		 * Adjust the limits (mappings must be page aligned)
		 */
		baddr -= hose->sg_pci->dma_base;
		last -= hose->sg_pci->dma_base;
		baddr &= PAGE_MASK;
		size = PAGE_ALIGN(last) - baddr;

		/*
		 * Map it
		 */
		area = get_vm_area(size, VM_IOREMAP);
		if (!area) {
			printk("ioremap failed... no vm_area...\n");
			return NULL;
		}

		ptes = hose->sg_pci->ptes;
		for (vaddr = (unsigned long)area->addr;
		    baddr <= last;
		    baddr += PAGE_SIZE, vaddr += PAGE_SIZE) {
			pfn = ptes[baddr >> PAGE_SHIFT];
			if (!(pfn & 1)) {
				printk("ioremap failed... pte not valid...\n");
				vfree(area->addr);
				return NULL;
			}
			pfn >>= 1;	/* make it a true pfn */

			if (__alpha_remap_area_pages(vaddr,
						     pfn << PAGE_SHIFT,
						     PAGE_SIZE, 0)) {
				printk("FAILED to remap_area_pages...\n");
				vfree(area->addr);
				return NULL;
			}
		}

		flush_tlb_all();

		vaddr = (unsigned long)area->addr + (addr & ~PAGE_MASK);
		return (void __iomem *) vaddr;
	}

	/* Assume a legacy (read: VGA) address, and return appropriately. */
	return (void __iomem *)(addr + TITAN_MEM_BIAS);
}

void
titan_iounmap(volatile void __iomem *xaddr)
{
	unsigned long addr = (unsigned long) xaddr;
	if (addr >= VMALLOC_START)
		vfree((void *)(PAGE_MASK & addr));
}

int
titan_is_mmio(const volatile void __iomem *xaddr)
{
	unsigned long addr = (unsigned long) xaddr;

	if (addr >= VMALLOC_START)
		return 1;
	else
		return (addr & 0x100000000UL) == 0;
}

#ifndef CONFIG_ALPHA_GENERIC
EXPORT_SYMBOL(titan_ioportmap);
EXPORT_SYMBOL(titan_ioremap);
EXPORT_SYMBOL(titan_iounmap);
EXPORT_SYMBOL(titan_is_mmio);
#endif

/*
 * AGP GART Support.
 */
#include <linux/agp_backend.h>
#include <asm/agp_backend.h>
#include <linux/slab.h>
#include <linux/delay.h>

struct titan_agp_aperture {
	struct pci_iommu_arena *arena;
	long pg_start;
	long pg_count;
};

static int
titan_agp_setup(alpha_agp_info *agp)
{
	struct titan_agp_aperture *aper;

	if (!alpha_agpgart_size)
		return -ENOMEM;

	aper = kmalloc(sizeof(struct titan_agp_aperture), GFP_KERNEL);
	if (aper == NULL)
		return -ENOMEM;

	aper->arena = agp->hose->sg_pci;
	aper->pg_count = alpha_agpgart_size / PAGE_SIZE;
	aper->pg_start = iommu_reserve(aper->arena, aper->pg_count,
				       aper->pg_count - 1);
	if (aper->pg_start < 0) {
		printk(KERN_ERR "Failed to reserve AGP memory\n");
		kfree(aper);
		return -ENOMEM;
	}

	agp->aperture.bus_base =
		aper->arena->dma_base + aper->pg_start * PAGE_SIZE;
	agp->aperture.size = aper->pg_count * PAGE_SIZE;
	agp->aperture.sysdata = aper;

	return 0;
}

static void
titan_agp_cleanup(alpha_agp_info *agp)
{
	struct titan_agp_aperture *aper = agp->aperture.sysdata;
	int status;

	status = iommu_release(aper->arena, aper->pg_start, aper->pg_count);
	if (status == -EBUSY) {
		printk(KERN_WARNING
		       "Attempted to release bound AGP memory - unbinding\n");
		iommu_unbind(aper->arena, aper->pg_start, aper->pg_count);
		status = iommu_release(aper->arena, aper->pg_start,
				       aper->pg_count);
	}
	if (status < 0)
		printk(KERN_ERR "Failed to release AGP memory\n");

	kfree(aper);
	kfree(agp);
}

static int
titan_agp_configure(alpha_agp_info *agp)
{
	union TPAchipPCTL pctl;
	titan_pachip_port *port = agp->private;
	pctl.pctl_q_whole = port->pctl.csr;

	/* Side-Band Addressing? */
	pctl.pctl_r_bits.apctl_v_agp_sba_en = agp->mode.bits.sba;

	/* AGP Rate? */
	pctl.pctl_r_bits.apctl_v_agp_rate = 0;		/* 1x */
	if (agp->mode.bits.rate & 2)
		pctl.pctl_r_bits.apctl_v_agp_rate = 1;	/* 2x */
#if 0
	if (agp->mode.bits.rate & 4)
		pctl.pctl_r_bits.apctl_v_agp_rate = 2;	/* 4x */
#endif

	/* RQ Depth? */
	pctl.pctl_r_bits.apctl_v_agp_hp_rd = 2;
	pctl.pctl_r_bits.apctl_v_agp_lp_rd = 7;

	/*
	 * AGP Enable.
	 */
	pctl.pctl_r_bits.apctl_v_agp_en = agp->mode.bits.enable;

	/* Tell the user.  */
	printk("Enabling AGP: %dX%s\n",
	       1 << pctl.pctl_r_bits.apctl_v_agp_rate,
	       pctl.pctl_r_bits.apctl_v_agp_sba_en ? " - SBA" : "");

	/* Write it.  */
	port->pctl.csr = pctl.pctl_q_whole;

	/* And wait at least 5000 66MHz cycles (per Titan spec).  */
	udelay(100);

	return 0;
}

static int
titan_agp_bind_memory(alpha_agp_info *agp, off_t pg_start, struct agp_memory *mem)
{
	struct titan_agp_aperture *aper = agp->aperture.sysdata;
	return iommu_bind(aper->arena, aper->pg_start + pg_start,
			  mem->page_count, mem->pages);
}

static int
titan_agp_unbind_memory(alpha_agp_info *agp, off_t pg_start, struct agp_memory *mem)
{
	struct titan_agp_aperture *aper = agp->aperture.sysdata;
	return iommu_unbind(aper->arena, aper->pg_start + pg_start,
			    mem->page_count);
}

static unsigned long
titan_agp_translate(alpha_agp_info *agp, dma_addr_t addr)
{
	struct titan_agp_aperture *aper = agp->aperture.sysdata;
	unsigned long baddr = addr - aper->arena->dma_base;
	unsigned long pte;

	if (addr < agp->aperture.bus_base ||
	    addr >= agp->aperture.bus_base + agp->aperture.size) {
		printk("%s: addr out of range\n", __func__);
		return -EINVAL;
	}

	pte = aper->arena->ptes[baddr >> PAGE_SHIFT];
	if (!(pte & 1)) {
		printk("%s: pte not valid\n", __func__);
		return -EINVAL;
	}

	return (pte >> 1) << PAGE_SHIFT;
}

struct alpha_agp_ops titan_agp_ops =
{
	.setup		= titan_agp_setup,
	.cleanup	= titan_agp_cleanup,
	.configure	= titan_agp_configure,
	.bind		= titan_agp_bind_memory,
	.unbind		= titan_agp_unbind_memory,
	.translate	= titan_agp_translate
};

alpha_agp_info *
titan_agp_info(void)
{
	alpha_agp_info *agp;
	struct pci_controller *hose;
	titan_pachip_port *port;
	int hosenum = -1;
	union TPAchipPCTL pctl;

	/*
	 * Find the AGP port.
	 */
	port = &TITAN_pachip0->a_port;
	if (titan_query_agp(port))
		hosenum = 2;
	if (hosenum < 0 &&
	    titan_pchip1_present &&
	    titan_query_agp(port = &TITAN_pachip1->a_port))
		hosenum = 3;

	/*
	 * Find the hose the port is on.
	 */
	for (hose = hose_head; hose; hose = hose->next)
		if (hose->index == hosenum)
			break;

	if (!hose || !hose->sg_pci)
		return NULL;

	/*
	 * Allocate the info structure.
	 */
	agp = kmalloc(sizeof(*agp), GFP_KERNEL);
	if (!agp)
		return NULL;

	/*
	 * Fill it in.
	 */
	agp->hose = hose;
	agp->private = port;
	agp->ops = &titan_agp_ops;

	/*
	 * Aperture - not configured until ops.setup().
	 *
	 * FIXME - should we go ahead and allocate it here?
	 */
	agp->aperture.bus_base = 0;
	agp->aperture.size = 0;
	agp->aperture.sysdata = NULL;

	/*
	 * Capabilities.
	 */
	agp->capability.lw = 0;
	agp->capability.bits.rate = 3; 	/* 2x, 1x */
	agp->capability.bits.sba = 1;
	agp->capability.bits.rq = 7;	/* 8 - 1 */

	/*
	 * Mode.
	 */
	pctl.pctl_q_whole = port->pctl.csr;
	agp->mode.lw = 0;
	agp->mode.bits.rate = 1 << pctl.pctl_r_bits.apctl_v_agp_rate;
	agp->mode.bits.sba = pctl.pctl_r_bits.apctl_v_agp_sba_en;
	agp->mode.bits.rq = 7;	/* RQ Depth? */
	agp->mode.bits.enable = pctl.pctl_r_bits.apctl_v_agp_en;

	return agp;
}


code:
// SPDX-License-Identifier: GPL-2.0
/*
 *	linux/arch/alpha/kernel/core_tsunami.c
 *
 * Based on code written by David A. Rusling (david.rusling@reo.mts.dec.com).
 *
 * Code common to all TSUNAMI core logic chips.
 */

#define __EXTERN_INLINE inline
#include <asm/io.h>
#include <asm/core_tsunami.h>
#undef __EXTERN_INLINE

#include <linux/module.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/memblock.h>

#include <asm/ptrace.h>
#include <asm/smp.h>
#include <asm/vga.h>

#include "proto.h"
#include "pci_impl.h"

/* Save Tsunami configuration data as the console had it set up.  */

struct
{
	unsigned long wsba[4];
	unsigned long wsm[4];
	unsigned long tba[4];
} saved_config[2] __attribute__((common));

/*
 * NOTE: Herein lie back-to-back mb instructions.  They are magic.
 * One plausible explanation is that the I/O controller does not properly
 * handle the system transaction.  Another involves timing.  Ho hum.
 */

/*
 * BIOS32-style PCI interface:
 */

#define DEBUG_CONFIG 0

#if DEBUG_CONFIG
# define DBG_CFG(args)	printk args
#else
# define DBG_CFG(args)
#endif

/*
 * Given a bus, device, and function number, compute resulting
 * configuration space address
 * accordingly.  It is therefore not safe to have concurrent
 * invocations to configuration space access routines, but there
 * really shouldn't be any need for this.
 *
 * Note that all config space accesses use Type 1 address format.
 *
 * Note also that type 1 is determined by non-zero bus number.
 *
 * Type 1:
 *
 *  3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
 *  3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 *	31:24	reserved
 *	23:16	bus number (8 bits = 128 possible buses)
 *	15:11	Device number (5 bits)
 *	10:8	function number
 *	 7:2	register number
 *
 * Notes:
 *	The function number selects which function of a multi-function device
 *	(e.g., SCSI and Ethernet).
 *
 *	The register selects a DWORD (32 bit) register offset.  Hence it
 *	doesn't get shifted by 2 bits as we want to "drop" the bottom two
 *	bits.
 */

static int
mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where,
	     unsigned long *pci_addr, unsigned char *type1)
{
	struct pci_controller *hose = pbus->sysdata;
	unsigned long addr;
	u8 bus = pbus->number;

	DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, "
		 "pci_addr=0x%p, type1=0x%p)\n",
		 bus, device_fn, where, pci_addr, type1));

	if (!pbus->parent) /* No parent means peer PCI bus. */
		bus = 0;
	*type1 = (bus != 0);

	addr = (bus << 16) | (device_fn << 8) | where;
	addr |= hose->config_space_base;

	*pci_addr = addr;
	DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
	return 0;
}

static int
tsunami_read_config(struct pci_bus *bus, unsigned int devfn, int where,
		    int size, u32 *value)
{
	unsigned long addr;
	unsigned char type1;

	if (mk_conf_addr(bus, devfn, where, &addr, &type1))
		return PCIBIOS_DEVICE_NOT_FOUND;

	switch (size) {
	case 1:
		*value = __kernel_ldbu(*(vucp)addr);
		break;
	case 2:
		*value = __kernel_ldwu(*(vusp)addr);
		break;
	case 4:
		*value = *(vuip)addr;
		break;
	}

	return PCIBIOS_SUCCESSFUL;
}

static int
tsunami_write_config(struct pci_bus *bus, unsigned int devfn, int where,
		     int size, u32 value)
{
	unsigned long addr;
	unsigned char type1;

	if (mk_conf_addr(bus, devfn, where, &addr, &type1))
		return PCIBIOS_DEVICE_NOT_FOUND;

	switch (size) {
	case 1:
		__kernel_stb(value, *(vucp)addr);
		mb();
		__kernel_ldbu(*(vucp)addr);
		break;
	case 2:
		__kernel_stw(value, *(vusp)addr);
		mb();
		__kernel_ldwu(*(vusp)addr);
		break;
	case 4:
		*(vuip)addr = value;
		mb();
		*(vuip)addr;
		break;
	}

	return PCIBIOS_SUCCESSFUL;
}

struct pci_ops tsunami_pci_ops =
{
	.read =		tsunami_read_config,
	.write = 	tsunami_write_config,
};

void
tsunami_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end)
{
	tsunami_pchip *pchip = hose->index ? TSUNAMI_pchip1 : TSUNAMI_pchip0;
	volatile unsigned long *csr;
	unsigned long value;

	/* We can invalidate up to 8 tlb entries in a go.  The flush
	   matches against <31:16> in the pci address.  */
	csr = &pchip->tlbia.csr;
	if (((start ^ end) & 0xffff0000) == 0)
		csr = &pchip->tlbiv.csr;

	/* For TBIA, it doesn't matter what value we write.  For TBI,
	   it's the shifted tag bits.  */
	value = (start & 0xffff0000) >> 12;

	*csr = value;
	mb();
	*csr;
}

#ifdef NXM_MACHINE_CHECKS_ON_TSUNAMI
static long __init
tsunami_probe_read(volatile unsigned long *vaddr)
{
	long dont_care, probe_result;
	int cpu = smp_processor_id();
	int s = swpipl(IPL_MCHECK - 1);

	mcheck_taken(cpu) = 0;
	mcheck_expected(cpu) = 1;
	mb();
	dont_care = *vaddr;
	draina();
	mcheck_expected(cpu) = 0;
	probe_result = !mcheck_taken(cpu);
	mcheck_taken(cpu) = 0;
	setipl(s);

	printk("dont_care == 0x%lx\n", dont_care);

	return probe_result;
}

static long __init
tsunami_probe_write(volatile unsigned long *vaddr)
{
	long true_contents, probe_result = 1;

	TSUNAMI_cchip->misc.csr |= (1L << 28); /* clear NXM... */
	true_contents = *vaddr;
	*vaddr = 0;
	draina();
	if (TSUNAMI_cchip->misc.csr & (1L << 28)) {
		int source = (TSUNAMI_cchip->misc.csr >> 29) & 7;
		TSUNAMI_cchip->misc.csr |= (1L << 28); /* ...and unlock NXS. */
		probe_result = 0;
		printk("tsunami_probe_write: unit %d at 0x%016lx\n", source,
		       (unsigned long)vaddr);
	}
	if (probe_result)
		*vaddr = true_contents;
	return probe_result;
}
#else
#define tsunami_probe_read(ADDR) 1
#endif /* NXM_MACHINE_CHECKS_ON_TSUNAMI */

static void __init
tsunami_init_one_pchip(tsunami_pchip *pchip, int index)
{
	struct pci_controller *hose;

	if (tsunami_probe_read(&pchip->pctl.csr) == 0)
		return;

	hose = alloc_pci_controller();
	if (index == 0)
		pci_isa_hose = hose;
	hose->io_space = alloc_resource();
	hose->mem_space = alloc_resource();

	/* This is for userland consumption.  For some reason, the 40-bit
	   PIO bias that we use in the kernel through KSEG didn't work for
	   the page table based user mappings.  So make sure we get the
	   43-bit PIO bias.  */
	hose->sparse_mem_base = 0;
	hose->sparse_io_base = 0;
	hose->dense_mem_base
	  = (TSUNAMI_MEM(index) & 0xffffffffffL) | 0x80000000000L;
	hose->dense_io_base
	  = (TSUNAMI_IO(index) & 0xffffffffffL) | 0x80000000000L;

	hose->config_space_base = TSUNAMI_CONF(index);
	hose->index = index;

	hose->io_space->start = TSUNAMI_IO(index) - TSUNAMI_IO_BIAS;
	hose->io_space->end = hose->io_space->start + TSUNAMI_IO_SPACE - 1;
	hose->io_space->name = pci_io_names[index];
	hose->io_space->flags = IORESOURCE_IO;

	hose->mem_space->start = TSUNAMI_MEM(index) - TSUNAMI_MEM_BIAS;
	hose->mem_space->end = hose->mem_space->start + 0xffffffff;
	hose->mem_space->name = pci_mem_names[index];
	hose->mem_space->flags = IORESOURCE_MEM;

	if (request_resource(&ioport_resource, hose->io_space) < 0)
		printk(KERN_ERR "Failed to request IO on hose %d\n", index);
	if (request_resource(&iomem_resource, hose->mem_space) < 0)
		printk(KERN_ERR "Failed to request MEM on hose %d\n", index);

	/*
	 * Save the existing PCI window translations.  SRM will
	 * need them when we go to reboot.
	 */

	saved_config[index].wsba[0] = pchip->wsba[0].csr;
	saved_config[index].wsm[0] = pchip->wsm[0].csr;
	saved_config[index].tba[0] = pchip->tba[0].csr;

	saved_config[index].wsba[1] = pchip->wsba[1].csr;
	saved_config[index].wsm[1] = pchip->wsm[1].csr;
	saved_config[index].tba[1] = pchip->tba[1].csr;

	saved_config[index].wsba[2] = pchip->wsba[2].csr;
	saved_config[index].wsm[2] = pchip->wsm[2].csr;
	saved_config[index].tba[2] = pchip->tba[2].csr;

	saved_config[index].wsba[3] = pchip->wsba[3].csr;
	saved_config[index].wsm[3] = pchip->wsm[3].csr;
	saved_config[index].tba[3] = pchip->tba[3].csr;

	/*
	 * Set up the PCI to main memory translation windows.
	 *
	 * Note: Window 3 is scatter-gather only
	 *
	 * Window 0 is scatter-gather 8MB at 8MB (for isa)
	 * Window 1 is scatter-gather (up to) 1GB at 1GB
	 * Window 2 is direct access 2GB at 2GB
	 *
	 * NOTE: we need the align_entry settings for Acer devices on ES40,
	 * specifically floppy and IDE when memory is larger than 2GB.
	 */
	hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000,
				       SMP_CACHE_BYTES);
	/* Initially set for 4 PTEs, but will be overridden to 64K for ISA. */
        hose->sg_isa->align_entry = 4;

	hose->sg_pci = iommu_arena_new(hose, 0x40000000,
				       size_for_memory(0x40000000),
				       SMP_CACHE_BYTES);
        hose->sg_pci->align_entry = 4; /* Tsunami caches 4 PTEs at a time */

	__direct_map_base = 0x80000000;
	__direct_map_size = 0x80000000;

	pchip->wsba[0].csr = hose->sg_isa->dma_base | 3;
	pchip->wsm[0].csr  = (hose->sg_isa->size - 1) & 0xfff00000;
	pchip->tba[0].csr  = virt_to_phys(hose->sg_isa->ptes);

	pchip->wsba[1].csr = hose->sg_pci->dma_base | 3;
	pchip->wsm[1].csr  = (hose->sg_pci->size - 1) & 0xfff00000;
	pchip->tba[1].csr  = virt_to_phys(hose->sg_pci->ptes);

	pchip->wsba[2].csr = 0x80000000 | 1;
	pchip->wsm[2].csr  = (0x80000000 - 1) & 0xfff00000;
	pchip->tba[2].csr  = 0;

	pchip->wsba[3].csr = 0;

	/* Enable the Monster Window to make DAC pci64 possible. */
	pchip->pctl.csr |= pctl_m_mwin;

	tsunami_pci_tbi(hose, 0, -1);
}

void __iomem *
tsunami_ioportmap(unsigned long addr)
{
	FIXUP_IOADDR_VGA(addr);
	return (void __iomem *)(addr + TSUNAMI_IO_BIAS);
}

void __iomem *
tsunami_ioremap(unsigned long addr, unsigned long size)
{
	FIXUP_MEMADDR_VGA(addr);
	return (void __iomem *)(addr + TSUNAMI_MEM_BIAS);
}

#ifndef CONFIG_ALPHA_GENERIC
EXPORT_SYMBOL(tsunami_ioportmap);
EXPORT_SYMBOL(tsunami_ioremap);
#endif

void __init
tsunami_init_arch(void)
{
#ifdef NXM_MACHINE_CHECKS_ON_TSUNAMI
	unsigned long tmp;

	/* Ho hum.. init_arch is called before init_IRQ, but we need to be
	   able to handle machine checks.  So install the handler now.  */
	wrent(entInt, 0);

	/* NXMs just don't matter to Tsunami--unless they make it
	   choke completely. */
	tmp = (unsigned long)(TSUNAMI_cchip - 1);
	printk("%s: probing bogus address:  0x%016lx\n", __func__, bogus_addr);
	printk("\tprobe %s\n",
	       tsunami_probe_write((unsigned long *)bogus_addr)
	       ? "succeeded" : "failed");
#endif /* NXM_MACHINE_CHECKS_ON_TSUNAMI */

#if 0
	printk("%s: CChip registers:\n", __func__);
	printk("%s: CSR_CSC 0x%lx\n", __func__, TSUNAMI_cchip->csc.csr);
	printk("%s: CSR_MTR 0x%lx\n", __func__, TSUNAMI_cchip.mtr.csr);
	printk("%s: CSR_MISC 0x%lx\n", __func__, TSUNAMI_cchip->misc.csr);
	printk("%s: CSR_DIM0 0x%lx\n", __func__, TSUNAMI_cchip->dim0.csr);
	printk("%s: CSR_DIM1 0x%lx\n", __func__, TSUNAMI_cchip->dim1.csr);
	printk("%s: CSR_DIR0 0x%lx\n", __func__, TSUNAMI_cchip->dir0.csr);
	printk("%s: CSR_DIR1 0x%lx\n", __func__, TSUNAMI_cchip->dir1.csr);
	printk("%s: CSR_DRIR 0x%lx\n", __func__, TSUNAMI_cchip->drir.csr);

	printk("%s: DChip registers:\n");
	printk("%s: CSR_DSC 0x%lx\n", __func__, TSUNAMI_dchip->dsc.csr);
	printk("%s: CSR_STR 0x%lx\n", __func__, TSUNAMI_dchip->str.csr);
	printk("%s: CSR_DREV 0x%lx\n", __func__, TSUNAMI_dchip->drev.csr);
#endif
	/* With multiple PCI busses, we play with I/O as physical addrs.  */
	ioport_resource.end = ~0UL;

	/* Find how many hoses we have, and initialize them.  TSUNAMI
	   and TYPHOON can have 2, but might only have 1 (DS10).  */

	tsunami_init_one_pchip(TSUNAMI_pchip0, 0);
	if (TSUNAMI_cchip->csc.csr & 1L<<14)
		tsunami_init_one_pchip(TSUNAMI_pchip1, 1);

	/* Check for graphic console location (if any).  */
	find_console_vga_hose();
}

static void
tsunami_kill_one_pchip(tsunami_pchip *pchip, int index)
{
	pchip->wsba[0].csr = saved_config[index].wsba[0];
	pchip->wsm[0].csr = saved_config[index].wsm[0];
	pchip->tba[0].csr = saved_config[index].tba[0];

	pchip->wsba[1].csr = saved_config[index].wsba[1];
	pchip->wsm[1].csr = saved_config[index].wsm[1];
	pchip->tba[1].csr = saved_config[index].tba[1];

	pchip->wsba[2].csr = saved_config[index].wsba[2];
	pchip->wsm[2].csr = saved_config[index].wsm[2];
	pchip->tba[2].csr = saved_config[index].tba[2];

	pchip->wsba[3].csr = saved_config[index].wsba[3];
	pchip->wsm[3].csr = saved_config[index].wsm[3];
	pchip->tba[3].csr = saved_config[index].tba[3];
}

void
tsunami_kill_arch(int mode)
{
	tsunami_kill_one_pchip(TSUNAMI_pchip0, 0);
	if (TSUNAMI_cchip->csc.csr & 1L<<14)
		tsunami_kill_one_pchip(TSUNAMI_pchip1, 1);
}

static inline void
tsunami_pci_clr_err_1(tsunami_pchip *pchip)
{
	pchip->perror.csr;
	pchip->perror.csr = 0x040;
	mb();
	pchip->perror.csr;
}

static inline void
tsunami_pci_clr_err(void)
{
	tsunami_pci_clr_err_1(TSUNAMI_pchip0);

	/* TSUNAMI and TYPHOON can have 2, but might only have 1 (DS10) */
	if (TSUNAMI_cchip->csc.csr & 1L<<14)
		tsunami_pci_clr_err_1(TSUNAMI_pchip1);
}

void
tsunami_machine_check(unsigned long vector, unsigned long la_ptr)
{
	/* Clear error before any reporting.  */
	mb();
	mb();  /* magic */
	draina();
	tsunami_pci_clr_err();
	wrmces(0x7);
	mb();

	process_mcheck_info(vector, la_ptr, "TSUNAMI",
			    mcheck_expected(smp_processor_id()));
}

repo_name: angery/linux


code:
// SPDX-License-Identifier: GPL-2.0
/*
 *  linux/arch/alpha/kernel/core_wildfire.c
 *
 *  Wildfire support.
 *
 *  Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
 */

#define __EXTERN_INLINE inline
#include <asm/io.h>
#include <asm/core_wildfire.h>
#undef __EXTERN_INLINE

#include <linux/types.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/init.h>

#include <asm/ptrace.h>
#include <asm/smp.h>

#include "proto.h"
#include "pci_impl.h"

#define DEBUG_CONFIG 0
#define DEBUG_DUMP_REGS 0
#define DEBUG_DUMP_CONFIG 1

#if DEBUG_CONFIG
# define DBG_CFG(args)	printk args
#else
# define DBG_CFG(args)
#endif

#if DEBUG_DUMP_REGS
static void wildfire_dump_pci_regs(int qbbno, int hoseno);
static void wildfire_dump_pca_regs(int qbbno, int pcano);
static void wildfire_dump_qsa_regs(int qbbno);
static void wildfire_dump_qsd_regs(int qbbno);
static void wildfire_dump_iop_regs(int qbbno);
static void wildfire_dump_gp_regs(int qbbno);
#endif
#if DEBUG_DUMP_CONFIG
static void wildfire_dump_hardware_config(void);
#endif

unsigned char wildfire_hard_qbb_map[WILDFIRE_MAX_QBB];
unsigned char wildfire_soft_qbb_map[WILDFIRE_MAX_QBB];
#define QBB_MAP_EMPTY	0xff

unsigned long wildfire_hard_qbb_mask;
unsigned long wildfire_soft_qbb_mask;
unsigned long wildfire_gp_mask;
unsigned long wildfire_hs_mask;
unsigned long wildfire_iop_mask;
unsigned long wildfire_ior_mask;
unsigned long wildfire_pca_mask;
unsigned long wildfire_cpu_mask;
unsigned long wildfire_mem_mask;

void __init
wildfire_init_hose(int qbbno, int hoseno)
{
	struct pci_controller *hose;
	wildfire_pci *pci;

	hose = alloc_pci_controller();
	hose->io_space = alloc_resource();
	hose->mem_space = alloc_resource();

        /* This is for userland consumption. */
        hose->sparse_mem_base = 0;
        hose->sparse_io_base  = 0;
        hose->dense_mem_base  = WILDFIRE_MEM(qbbno, hoseno);
        hose->dense_io_base   = WILDFIRE_IO(qbbno, hoseno);

	hose->config_space_base = WILDFIRE_CONF(qbbno, hoseno);
	hose->index = (qbbno << 3) + hoseno;

	hose->io_space->start = WILDFIRE_IO(qbbno, hoseno) - WILDFIRE_IO_BIAS;
	hose->io_space->end = hose->io_space->start + WILDFIRE_IO_SPACE - 1;
	hose->io_space->name = pci_io_names[hoseno];
	hose->io_space->flags = IORESOURCE_IO;

	hose->mem_space->start = WILDFIRE_MEM(qbbno, hoseno)-WILDFIRE_MEM_BIAS;
	hose->mem_space->end = hose->mem_space->start + 0xffffffff;
	hose->mem_space->name = pci_mem_names[hoseno];
	hose->mem_space->flags = IORESOURCE_MEM;

	if (request_resource(&ioport_resource, hose->io_space) < 0)
		printk(KERN_ERR "Failed to request IO on qbb %d hose %d\n",
		       qbbno, hoseno);
	if (request_resource(&iomem_resource, hose->mem_space) < 0)
		printk(KERN_ERR "Failed to request MEM on qbb %d hose %d\n",
		       qbbno, hoseno);

#if DEBUG_DUMP_REGS
	wildfire_dump_pci_regs(qbbno, hoseno);
#endif

        /*
         * Set up the PCI to main memory translation windows.
         *
         * Note: Window 3 is scatter-gather only
         *
         * Window 0 is scatter-gather 8MB at 8MB (for isa)
	 * Window 1 is direct access 1GB at 1GB
	 * Window 2 is direct access 1GB at 2GB
         * Window 3 is scatter-gather 128MB at 3GB
         * ??? We ought to scale window 3 memory.
         *
         */
	hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000,
				       SMP_CACHE_BYTES);
	hose->sg_pci = iommu_arena_new(hose, 0xc0000000, 0x08000000,
				       SMP_CACHE_BYTES);

	pci = WILDFIRE_pci(qbbno, hoseno);

	pci->pci_window[0].wbase.csr = hose->sg_isa->dma_base | 3;
	pci->pci_window[0].wmask.csr = (hose->sg_isa->size - 1) & 0xfff00000;
	pci->pci_window[0].tbase.csr = virt_to_phys(hose->sg_isa->ptes);

	pci->pci_window[1].wbase.csr = 0x40000000 | 1;
	pci->pci_window[1].wmask.csr = (0x40000000 -1) & 0xfff00000;
	pci->pci_window[1].tbase.csr = 0;

	pci->pci_window[2].wbase.csr = 0x80000000 | 1;
	pci->pci_window[2].wmask.csr = (0x40000000 -1) & 0xfff00000;
	pci->pci_window[2].tbase.csr = 0x40000000;

	pci->pci_window[3].wbase.csr = hose->sg_pci->dma_base | 3;
	pci->pci_window[3].wmask.csr = (hose->sg_pci->size - 1) & 0xfff00000;
	pci->pci_window[3].tbase.csr = virt_to_phys(hose->sg_pci->ptes);

	wildfire_pci_tbi(hose, 0, 0); /* Flush TLB at the end. */
}

void __init
wildfire_init_pca(int qbbno, int pcano)
{

	/* Test for PCA existence first. */
	if (!WILDFIRE_PCA_EXISTS(qbbno, pcano))
	    return;

#if DEBUG_DUMP_REGS
	wildfire_dump_pca_regs(qbbno, pcano);
#endif

	/* Do both hoses of the PCA. */
	wildfire_init_hose(qbbno, (pcano << 1) + 0);
	wildfire_init_hose(qbbno, (pcano << 1) + 1);
}

void __init
wildfire_init_qbb(int qbbno)
{
	int pcano;

	/* Test for QBB existence first. */
	if (!WILDFIRE_QBB_EXISTS(qbbno))
		return;

#if DEBUG_DUMP_REGS
	wildfire_dump_qsa_regs(qbbno);
	wildfire_dump_qsd_regs(qbbno);
	wildfire_dump_iop_regs(qbbno);
	wildfire_dump_gp_regs(qbbno);
#endif

	/* Init all PCAs here. */
	for (pcano = 0; pcano < WILDFIRE_PCA_PER_QBB; pcano++) {
		wildfire_init_pca(qbbno, pcano);
	}
}

void __init
wildfire_hardware_probe(void)
{
	unsigned long temp;
	unsigned int hard_qbb, soft_qbb;
	wildfire_fast_qsd *fast = WILDFIRE_fast_qsd();
	wildfire_qsd *qsd;
	wildfire_qsa *qsa;
	wildfire_iop *iop;
	wildfire_gp *gp;
	wildfire_ne *ne;
	wildfire_fe *fe;
	int i;

	temp = fast->qsd_whami.csr;
#if 0
	printk(KERN_ERR "fast QSD_WHAMI at base %p is 0x%lx\n", fast, temp);
#endif

	hard_qbb = (temp >> 8) & 7;
	soft_qbb = (temp >> 4) & 7;

	/* Init the HW configuration variables. */
	wildfire_hard_qbb_mask = (1 << hard_qbb);
	wildfire_soft_qbb_mask = (1 << soft_qbb);

	wildfire_gp_mask = 0;
	wildfire_hs_mask = 0;
	wildfire_iop_mask = 0;
	wildfire_ior_mask = 0;
	wildfire_pca_mask = 0;

	wildfire_cpu_mask = 0;
	wildfire_mem_mask = 0;

	memset(wildfire_hard_qbb_map, QBB_MAP_EMPTY, WILDFIRE_MAX_QBB);
	memset(wildfire_soft_qbb_map, QBB_MAP_EMPTY, WILDFIRE_MAX_QBB);

	/* First, determine which QBBs are present. */
	qsa = WILDFIRE_qsa(soft_qbb);

	temp = qsa->qsa_qbb_id.csr;
#if 0
	printk(KERN_ERR "QSA_QBB_ID at base %p is 0x%lx\n", qsa, temp);
#endif

	if (temp & 0x40) /* Is there an HS? */
		wildfire_hs_mask = 1;

	if (temp & 0x20) { /* Is there a GP? */
		gp = WILDFIRE_gp(soft_qbb);
		temp = 0;
		for (i = 0; i < 4; i++) {
			temp |= gp->gpa_qbb_map[i].csr << (i * 8);
#if 0
			printk(KERN_ERR "GPA_QBB_MAP[%d] at base %p is 0x%lx\n",
			       i, gp, temp);
#endif
		}

		for (hard_qbb = 0; hard_qbb < WILDFIRE_MAX_QBB; hard_qbb++) {
			if (temp & 8) { /* Is there a QBB? */
				soft_qbb = temp & 7;
				wildfire_hard_qbb_mask |= (1 << hard_qbb);
				wildfire_soft_qbb_mask |= (1 << soft_qbb);
			}
			temp >>= 4;
		}
		wildfire_gp_mask = wildfire_soft_qbb_mask;
        }

	/* Next determine each QBBs resources. */
	for (soft_qbb = 0; soft_qbb < WILDFIRE_MAX_QBB; soft_qbb++) {
	    if (WILDFIRE_QBB_EXISTS(soft_qbb)) {
	        qsd = WILDFIRE_qsd(soft_qbb);
		temp = qsd->qsd_whami.csr;
#if 0
	printk(KERN_ERR "QSD_WHAMI at base %p is 0x%lx\n", qsd, temp);
#endif
		hard_qbb = (temp >> 8) & 7;
		wildfire_hard_qbb_map[hard_qbb] = soft_qbb;
		wildfire_soft_qbb_map[soft_qbb] = hard_qbb;

		qsa = WILDFIRE_qsa(soft_qbb);
		temp = qsa->qsa_qbb_pop[0].csr;
#if 0
	printk(KERN_ERR "QSA_QBB_POP_0 at base %p is 0x%lx\n", qsa, temp);
#endif
		wildfire_cpu_mask |= ((temp >> 0) & 0xf) << (soft_qbb << 2);
		wildfire_mem_mask |= ((temp >> 4) & 0xf) << (soft_qbb << 2);

		temp = qsa->qsa_qbb_pop[1].csr;
#if 0
	printk(KERN_ERR "QSA_QBB_POP_1 at base %p is 0x%lx\n", qsa, temp);
#endif
		wildfire_iop_mask |= (1 << soft_qbb);
		wildfire_ior_mask |= ((temp >> 4) & 0xf) << (soft_qbb << 2);

		temp = qsa->qsa_qbb_id.csr;
#if 0
	printk(KERN_ERR "QSA_QBB_ID at %p is 0x%lx\n", qsa, temp);
#endif
		if (temp & 0x20)
		    wildfire_gp_mask |= (1 << soft_qbb);

		/* Probe for PCA existence here. */
		for (i = 0; i < WILDFIRE_PCA_PER_QBB; i++) {
		    iop = WILDFIRE_iop(soft_qbb);
		    ne = WILDFIRE_ne(soft_qbb, i);
		    fe = WILDFIRE_fe(soft_qbb, i);

		    if ((iop->iop_hose[i].init.csr & 1) == 1 &&
			((ne->ne_what_am_i.csr & 0xf00000300UL) == 0x100000300UL) &&
			((fe->fe_what_am_i.csr & 0xf00000300UL) == 0x100000200UL))
		    {
		        wildfire_pca_mask |= 1 << ((soft_qbb << 2) + i);
		    }
		}

	    }
	}
#if DEBUG_DUMP_CONFIG
	wildfire_dump_hardware_config();
#endif
}

void __init
wildfire_init_arch(void)
{
	int qbbno;

	/* With multiple PCI buses, we play with I/O as physical addrs.  */
	ioport_resource.end = ~0UL;

	/* Probe the hardware for info about configuration. */
	wildfire_hardware_probe();

	/* Now init all the found QBBs. */
	for (qbbno = 0; qbbno < WILDFIRE_MAX_QBB; qbbno++) {
		wildfire_init_qbb(qbbno);
	}

	/* Normal direct PCI DMA mapping. */
	__direct_map_base = 0x40000000UL;
	__direct_map_size = 0x80000000UL;
}

void
wildfire_machine_check(unsigned long vector, unsigned long la_ptr)
{
	mb();
	mb();  /* magic */
	draina();
	/* FIXME: clear pci errors */
	wrmces(0x7);
	mb();

	process_mcheck_info(vector, la_ptr, "WILDFIRE",
			    mcheck_expected(smp_processor_id()));
}

void
wildfire_kill_arch(int mode)
{
}

void
wildfire_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end)
{
	int qbbno = hose->index >> 3;
	int hoseno = hose->index & 7;
	wildfire_pci *pci = WILDFIRE_pci(qbbno, hoseno);

	mb();
	pci->pci_flush_tlb.csr; /* reading does the trick */
}

static int
mk_conf_addr(struct pci_bus *pbus, unsigned int device_fn, int where,
	     unsigned long *pci_addr, unsigned char *type1)
{
	struct pci_controller *hose = pbus->sysdata;
	unsigned long addr;
	u8 bus = pbus->number;

	DBG_CFG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, "
		 "pci_addr=0x%p, type1=0x%p)\n",
		 bus, device_fn, where, pci_addr, type1));

	if (!pbus->parent) /* No parent means peer PCI bus. */
		bus = 0;
	*type1 = (bus != 0);

	addr = (bus << 16) | (device_fn << 8) | where;
	addr |= hose->config_space_base;

	*pci_addr = addr;
	DBG_CFG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
	return 0;
}

static int
wildfire_read_config(struct pci_bus *bus, unsigned int devfn, int where,
		     int size, u32 *value)
{
	unsigned long addr;
	unsigned char type1;

	if (mk_conf_addr(bus, devfn, where, &addr, &type1))
		return PCIBIOS_DEVICE_NOT_FOUND;

	switch (size) {
	case 1:
		*value = __kernel_ldbu(*(vucp)addr);
		break;
	case 2:
		*value = __kernel_ldwu(*(vusp)addr);
		break;
	case 4:
		*value = *(vuip)addr;
		break;
	}

	return PCIBIOS_SUCCESSFUL;
}

static int
wildfire_write_config(struct pci_bus *bus, unsigned int devfn, int where,
		      int size, u32 value)
{
	unsigned long addr;
	unsigned char type1;

	if (mk_conf_addr(bus, devfn, where, &addr, &type1))
		return PCIBIOS_DEVICE_NOT_FOUND;

	switch (size) {
	case 1:
		__kernel_stb(value, *(vucp)addr);
		mb();
		__kernel_ldbu(*(vucp)addr);
		break;
	case 2:
		__kernel_stw(value, *(vusp)addr);
		mb();
		__kernel_ldwu(*(vusp)addr);
		break;
	case 4:
		*(vuip)addr = value;
		mb();
		*(vuip)addr;
		break;
	}

	return PCIBIOS_SUCCESSFUL;
}

struct pci_ops wildfire_pci_ops =
{
	.read =		wildfire_read_config,
	.write =	wildfire_write_config,
};

#if DEBUG_DUMP_REGS

static void __init
wildfire_dump_pci_regs(int qbbno, int hoseno)
{
	wildfire_pci *pci = WILDFIRE_pci(qbbno, hoseno);
	int i;

	printk(KERN_ERR "PCI registers for QBB %d hose %d (%p)\n",
	       qbbno, hoseno, pci);

	printk(KERN_ERR " PCI_IO_ADDR_EXT: 0x%16lx\n",
	       pci->pci_io_addr_ext.csr);
	printk(KERN_ERR " PCI_CTRL:        0x%16lx\n", pci->pci_ctrl.csr);
	printk(KERN_ERR " PCI_ERR_SUM:     0x%16lx\n", pci->pci_err_sum.csr);
	printk(KERN_ERR " PCI_ERR_ADDR:    0x%16lx\n", pci->pci_err_addr.csr);
	printk(KERN_ERR " PCI_STALL_CNT:   0x%16lx\n", pci->pci_stall_cnt.csr);
	printk(KERN_ERR " PCI_PEND_INT:    0x%16lx\n", pci->pci_pend_int.csr);
	printk(KERN_ERR " PCI_SENT_INT:    0x%16lx\n", pci->pci_sent_int.csr);

	printk(KERN_ERR " DMA window registers for QBB %d hose %d (%p)\n",
	       qbbno, hoseno, pci);
	for (i = 0; i < 4; i++) {
		printk(KERN_ERR "  window %d: 0x%16lx 0x%16lx 0x%16lx\n", i,
		       pci->pci_window[i].wbase.csr,
		       pci->pci_window[i].wmask.csr,
		       pci->pci_window[i].tbase.csr);
	}
	printk(KERN_ERR "\n");
}

static void __init
wildfire_dump_pca_regs(int qbbno, int pcano)
{
	wildfire_pca *pca = WILDFIRE_pca(qbbno, pcano);
	int i;

	printk(KERN_ERR "PCA registers for QBB %d PCA %d (%p)\n",
	       qbbno, pcano, pca);

	printk(KERN_ERR " PCA_WHAT_AM_I: 0x%16lx\n", pca->pca_what_am_i.csr);
	printk(KERN_ERR " PCA_ERR_SUM:   0x%16lx\n", pca->pca_err_sum.csr);
	printk(KERN_ERR " PCA_PEND_INT:  0x%16lx\n", pca->pca_pend_int.csr);
	printk(KERN_ERR " PCA_SENT_INT:  0x%16lx\n", pca->pca_sent_int.csr);
	printk(KERN_ERR " PCA_STDIO_EL:  0x%16lx\n",
	       pca->pca_stdio_edge_level.csr);

	printk(KERN_ERR " PCA target registers for QBB %d PCA %d (%p)\n",
	       qbbno, pcano, pca);
	for (i = 0; i < 4; i++) {
	  printk(KERN_ERR "  target %d: 0x%16lx 0x%16lx\n", i,
		       pca->pca_int[i].target.csr,
		       pca->pca_int[i].enable.csr);
	}

	printk(KERN_ERR "\n");
}

static void __init
wildfire_dump_qsa_regs(int qbbno)
{
	wildfire_qsa *qsa = WILDFIRE_qsa(qbbno);
	int i;

	printk(KERN_ERR "QSA registers for QBB %d (%p)\n", qbbno, qsa);

	printk(KERN_ERR " QSA_QBB_ID:      0x%16lx\n", qsa->qsa_qbb_id.csr);
	printk(KERN_ERR " QSA_PORT_ENA:    0x%16lx\n", qsa->qsa_port_ena.csr);
	printk(KERN_ERR " QSA_REF_INT:     0x%16lx\n", qsa->qsa_ref_int.csr);

	for (i = 0; i < 5; i++)
		printk(KERN_ERR " QSA_CONFIG_%d:    0x%16lx\n",
		       i, qsa->qsa_config[i].csr);

	for (i = 0; i < 2; i++)
		printk(KERN_ERR " QSA_QBB_POP_%d:   0x%16lx\n",
		       i, qsa->qsa_qbb_pop[0].csr);

	printk(KERN_ERR "\n");
}

static void __init
wildfire_dump_qsd_regs(int qbbno)
{
	wildfire_qsd *qsd = WILDFIRE_qsd(qbbno);

	printk(KERN_ERR "QSD registers for QBB %d (%p)\n", qbbno, qsd);

	printk(KERN_ERR " QSD_WHAMI:         0x%16lx\n", qsd->qsd_whami.csr);
	printk(KERN_ERR " QSD_REV:           0x%16lx\n", qsd->qsd_rev.csr);
	printk(KERN_ERR " QSD_PORT_PRESENT:  0x%16lx\n",
	       qsd->qsd_port_present.csr);
	printk(KERN_ERR " QSD_PORT_ACTIVE:   0x%16lx\n",
	       qsd->qsd_port_active.csr);
	printk(KERN_ERR " QSD_FAULT_ENA:     0x%16lx\n",
	       qsd->qsd_fault_ena.csr);
	printk(KERN_ERR " QSD_CPU_INT_ENA:   0x%16lx\n",
	       qsd->qsd_cpu_int_ena.csr);
	printk(KERN_ERR " QSD_MEM_CONFIG:    0x%16lx\n",
	       qsd->qsd_mem_config.csr);
	printk(KERN_ERR " QSD_ERR_SUM:       0x%16lx\n",
	       qsd->qsd_err_sum.csr);

	printk(KERN_ERR "\n");
}

static void __init
wildfire_dump_iop_regs(int qbbno)
{
	wildfire_iop *iop = WILDFIRE_iop(qbbno);
	int i;

	printk(KERN_ERR "IOP registers for QBB %d (%p)\n", qbbno, iop);

	printk(KERN_ERR " IOA_CONFIG:          0x%16lx\n", iop->ioa_config.csr);
	printk(KERN_ERR " IOD_CONFIG:          0x%16lx\n", iop->iod_config.csr);
	printk(KERN_ERR " IOP_SWITCH_CREDITS:  0x%16lx\n",
	       iop->iop_switch_credits.csr);
	printk(KERN_ERR " IOP_HOSE_CREDITS:    0x%16lx\n",
	       iop->iop_hose_credits.csr);

	for (i = 0; i < 4; i++)
		printk(KERN_ERR " IOP_HOSE_%d_INIT:     0x%16lx\n",
		       i, iop->iop_hose[i].init.csr);
	for (i = 0; i < 4; i++)
		printk(KERN_ERR " IOP_DEV_INT_TARGET_%d: 0x%16lx\n",
		       i, iop->iop_dev_int[i].target.csr);

	printk(KERN_ERR "\n");
}

static void __init
wildfire_dump_gp_regs(int qbbno)
{
	wildfire_gp *gp = WILDFIRE_gp(qbbno);
	int i;

	printk(KERN_ERR "GP registers for QBB %d (%p)\n", qbbno, gp);
	for (i = 0; i < 4; i++)
		printk(KERN_ERR " GPA_QBB_MAP_%d:     0x%16lx\n",
		       i, gp->gpa_qbb_map[i].csr);

	printk(KERN_ERR " GPA_MEM_POP_MAP:   0x%16lx\n",
	       gp->gpa_mem_pop_map.csr);
	printk(KERN_ERR " GPA_SCRATCH:       0x%16lx\n", gp->gpa_scratch.csr);
	printk(KERN_ERR " GPA_DIAG:          0x%16lx\n", gp->gpa_diag.csr);
	printk(KERN_ERR " GPA_CONFIG_0:      0x%16lx\n", gp->gpa_config_0.csr);
	printk(KERN_ERR " GPA_INIT_ID:       0x%16lx\n", gp->gpa_init_id.csr);
	printk(KERN_ERR " GPA_CONFIG_2:      0x%16lx\n", gp->gpa_config_2.csr);

	printk(KERN_ERR "\n");
}
#endif /* DUMP_REGS */

#if DEBUG_DUMP_CONFIG
static void __init
wildfire_dump_hardware_config(void)
{
	int i;

	printk(KERN_ERR "Probed Hardware Configuration\n");

	printk(KERN_ERR " hard_qbb_mask:  0x%16lx\n", wildfire_hard_qbb_mask);
	printk(KERN_ERR " soft_qbb_mask:  0x%16lx\n", wildfire_soft_qbb_mask);

	printk(KERN_ERR " gp_mask:        0x%16lx\n", wildfire_gp_mask);
	printk(KERN_ERR " hs_mask:        0x%16lx\n", wildfire_hs_mask);
	printk(KERN_ERR " iop_mask:       0x%16lx\n", wildfire_iop_mask);
	printk(KERN_ERR " ior_mask:       0x%16lx\n", wildfire_ior_mask);
	printk(KERN_ERR " pca_mask:       0x%16lx\n", wildfire_pca_mask);

	printk(KERN_ERR " cpu_mask:       0x%16lx\n", wildfire_cpu_mask);
	printk(KERN_ERR " mem_mask:       0x%16lx\n", wildfire_mem_mask);

	printk(" hard_qbb_map: ");
	for (i = 0; i < WILDFIRE_MAX_QBB; i++)
	    if (wildfire_hard_qbb_map[i] == QBB_MAP_EMPTY)
		printk("--- ");
	    else
		printk("%3d ", wildfire_hard_qbb_map[i]);
	printk("\n");

	printk(" soft_qbb_map: ");
	for (i = 0; i < WILDFIRE_MAX_QBB; i++)
	    if (wildfire_soft_qbb_map[i] == QBB_MAP_EMPTY)
		printk("--- ");
	    else
		printk("%3d ", wildfire_soft_qbb_map[i]);
	printk("\n");
}
#endif /* DUMP_CONFIG */


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * arch/alpha/kernel/entry.S
 *
 * Kernel entry-points.
 */

#include <asm/asm-offsets.h>
#include <asm/thread_info.h>
#include <asm/pal.h>
#include <asm/errno.h>
#include <asm/unistd.h>

	.text
	.set noat
	.cfi_sections	.debug_frame

/* Stack offsets.  */
#define SP_OFF			184
#define SWITCH_STACK_SIZE	320

.macro	CFI_START_OSF_FRAME	func
	.align	4
	.globl	\func
	.type	\func,@function
\func:
	.cfi_startproc simple
	.cfi_return_column 64
	.cfi_def_cfa	$sp, 48
	.cfi_rel_offset	64, 8
	.cfi_rel_offset	$gp, 16
	.cfi_rel_offset	$16, 24
	.cfi_rel_offset	$17, 32
	.cfi_rel_offset	$18, 40
.endm

.macro	CFI_END_OSF_FRAME	func
	.cfi_endproc
	.size	\func, . - \func
.endm

/*
 * This defines the normal kernel pt-regs layout.
 *
 * regs 9-15 preserved by C code
 * regs 16-18 saved by PAL-code
 * regs 29-30 saved and set up by PAL-code
 * JRP - Save regs 16-18 in a special area of the stack, so that
 * the palcode-provided values are available to the signal handler.
 */

.macro	SAVE_ALL
	subq	$sp, SP_OFF, $sp
	.cfi_adjust_cfa_offset	SP_OFF
	stq	$0, 0($sp)
	stq	$1, 8($sp)
	stq	$2, 16($sp)
	stq	$3, 24($sp)
	stq	$4, 32($sp)
	stq	$28, 144($sp)
	.cfi_rel_offset	$0, 0
	.cfi_rel_offset $1, 8
	.cfi_rel_offset	$2, 16
	.cfi_rel_offset	$3, 24
	.cfi_rel_offset	$4, 32
	.cfi_rel_offset	$28, 144
	lda	$2, alpha_mv
	stq	$5, 40($sp)
	stq	$6, 48($sp)
	stq	$7, 56($sp)
	stq	$8, 64($sp)
	stq	$19, 72($sp)
	stq	$20, 80($sp)
	stq	$21, 88($sp)
	ldq	$2, HAE_CACHE($2)
	stq	$22, 96($sp)
	stq	$23, 104($sp)
	stq	$24, 112($sp)
	stq	$25, 120($sp)
	stq	$26, 128($sp)
	stq	$27, 136($sp)
	stq	$2, 152($sp)
	stq	$16, 160($sp)
	stq	$17, 168($sp)
	stq	$18, 176($sp)
	.cfi_rel_offset	$5, 40
	.cfi_rel_offset	$6, 48
	.cfi_rel_offset	$7, 56
	.cfi_rel_offset	$8, 64
	.cfi_rel_offset $19, 72
	.cfi_rel_offset	$20, 80
	.cfi_rel_offset	$21, 88
	.cfi_rel_offset $22, 96
	.cfi_rel_offset	$23, 104
	.cfi_rel_offset	$24, 112
	.cfi_rel_offset	$25, 120
	.cfi_rel_offset	$26, 128
	.cfi_rel_offset	$27, 136
.endm

.macro	RESTORE_ALL
	lda	$19, alpha_mv
	ldq	$0, 0($sp)
	ldq	$1, 8($sp)
	ldq	$2, 16($sp)
	ldq	$3, 24($sp)
	ldq	$21, 152($sp)
	ldq	$20, HAE_CACHE($19)
	ldq	$4, 32($sp)
	ldq	$5, 40($sp)
	ldq	$6, 48($sp)
	ldq	$7, 56($sp)
	subq	$20, $21, $20
	ldq	$8, 64($sp)
	beq	$20, 99f
	ldq	$20, HAE_REG($19)
	stq	$21, HAE_CACHE($19)
	stq	$21, 0($20)
99:	ldq	$19, 72($sp)
	ldq	$20, 80($sp)
	ldq	$21, 88($sp)
	ldq	$22, 96($sp)
	ldq	$23, 104($sp)
	ldq	$24, 112($sp)
	ldq	$25, 120($sp)
	ldq	$26, 128($sp)
	ldq	$27, 136($sp)
	ldq	$28, 144($sp)
	addq	$sp, SP_OFF, $sp
	.cfi_restore	$0
	.cfi_restore	$1
	.cfi_restore	$2
	.cfi_restore	$3
	.cfi_restore	$4
	.cfi_restore	$5
	.cfi_restore	$6
	.cfi_restore	$7
	.cfi_restore	$8
	.cfi_restore	$19
	.cfi_restore	$20
	.cfi_restore	$21
	.cfi_restore	$22
	.cfi_restore	$23
	.cfi_restore	$24
	.cfi_restore	$25
	.cfi_restore	$26
	.cfi_restore	$27
	.cfi_restore	$28
	.cfi_adjust_cfa_offset	-SP_OFF
.endm

.macro	DO_SWITCH_STACK
	bsr	$1, do_switch_stack
	.cfi_adjust_cfa_offset	SWITCH_STACK_SIZE
	.cfi_rel_offset	$9, 0
	.cfi_rel_offset	$10, 8
	.cfi_rel_offset	$11, 16
	.cfi_rel_offset	$12, 24
	.cfi_rel_offset	$13, 32
	.cfi_rel_offset	$14, 40
	.cfi_rel_offset	$15, 48
	/* We don't really care about the FP registers for debugging.  */
.endm

.macro	UNDO_SWITCH_STACK
	bsr	$1, undo_switch_stack
	.cfi_restore	$9
	.cfi_restore	$10
	.cfi_restore	$11
	.cfi_restore	$12
	.cfi_restore	$13
	.cfi_restore	$14
	.cfi_restore	$15
	.cfi_adjust_cfa_offset	-SWITCH_STACK_SIZE
.endm

/*
 * Non-syscall kernel entry points.
 */

CFI_START_OSF_FRAME entInt
	SAVE_ALL
	lda	$8, 0x3fff
	lda	$26, ret_from_sys_call
	bic	$sp, $8, $8
	mov	$sp, $19
	jsr	$31, do_entInt
CFI_END_OSF_FRAME entInt

CFI_START_OSF_FRAME entArith
	SAVE_ALL
	lda	$8, 0x3fff
	lda	$26, ret_from_sys_call
	bic	$sp, $8, $8
	mov	$sp, $18
	jsr	$31, do_entArith
CFI_END_OSF_FRAME entArith

CFI_START_OSF_FRAME entMM
	SAVE_ALL
/* save $9 - $15 so the inline exception code can manipulate them.  */
	subq	$sp, 56, $sp
	.cfi_adjust_cfa_offset	56
	stq	$9, 0($sp)
	stq	$10, 8($sp)
	stq	$11, 16($sp)
	stq	$12, 24($sp)
	stq	$13, 32($sp)
	stq	$14, 40($sp)
	stq	$15, 48($sp)
	.cfi_rel_offset	$9, 0
	.cfi_rel_offset	$10, 8
	.cfi_rel_offset	$11, 16
	.cfi_rel_offset	$12, 24
	.cfi_rel_offset	$13, 32
	.cfi_rel_offset	$14, 40
	.cfi_rel_offset	$15, 48
	addq	$sp, 56, $19
/* handle the fault */
	lda	$8, 0x3fff
	bic	$sp, $8, $8
	jsr	$26, do_page_fault
/* reload the registers after the exception code played.  */
	ldq	$9, 0($sp)
	ldq	$10, 8($sp)
	ldq	$11, 16($sp)
	ldq	$12, 24($sp)
	ldq	$13, 32($sp)
	ldq	$14, 40($sp)
	ldq	$15, 48($sp)
	addq	$sp, 56, $sp
	.cfi_restore	$9
	.cfi_restore	$10
	.cfi_restore	$11
	.cfi_restore	$12
	.cfi_restore	$13
	.cfi_restore	$14
	.cfi_restore	$15
	.cfi_adjust_cfa_offset	-56
/* finish up the syscall as normal.  */
	br	ret_from_sys_call
CFI_END_OSF_FRAME entMM

CFI_START_OSF_FRAME entIF
	SAVE_ALL
	lda	$8, 0x3fff
	lda	$26, ret_from_sys_call
	bic	$sp, $8, $8
	mov	$sp, $17
	jsr	$31, do_entIF
CFI_END_OSF_FRAME entIF

CFI_START_OSF_FRAME entUna
	lda	$sp, -256($sp)
	.cfi_adjust_cfa_offset	256
	stq	$0, 0($sp)
	.cfi_rel_offset	$0, 0
	.cfi_remember_state
	ldq	$0, 256($sp)	/* get PS */
	stq	$1, 8($sp)
	stq	$2, 16($sp)
	stq	$3, 24($sp)
	and	$0, 8, $0		/* user mode? */
	stq	$4, 32($sp)
	bne	$0, entUnaUser	/* yup -> do user-level unaligned fault */
	stq	$5, 40($sp)
	stq	$6, 48($sp)
	stq	$7, 56($sp)
	stq	$8, 64($sp)
	stq	$9, 72($sp)
	stq	$10, 80($sp)
	stq	$11, 88($sp)
	stq	$12, 96($sp)
	stq	$13, 104($sp)
	stq	$14, 112($sp)
	stq	$15, 120($sp)
	/* 16-18 PAL-saved */
	stq	$19, 152($sp)
	stq	$20, 160($sp)
	stq	$21, 168($sp)
	stq	$22, 176($sp)
	stq	$23, 184($sp)
	stq	$24, 192($sp)
	stq	$25, 200($sp)
	stq	$26, 208($sp)
	stq	$27, 216($sp)
	stq	$28, 224($sp)
	mov	$sp, $19
	stq	$gp, 232($sp)
	.cfi_rel_offset	$1, 1*8
	.cfi_rel_offset	$2, 2*8
	.cfi_rel_offset	$3, 3*8
	.cfi_rel_offset	$4, 4*8
	.cfi_rel_offset	$5, 5*8
	.cfi_rel_offset	$6, 6*8
	.cfi_rel_offset	$7, 7*8
	.cfi_rel_offset	$8, 8*8
	.cfi_rel_offset	$9, 9*8
	.cfi_rel_offset	$10, 10*8
	.cfi_rel_offset	$11, 11*8
	.cfi_rel_offset	$12, 12*8
	.cfi_rel_offset	$13, 13*8
	.cfi_rel_offset	$14, 14*8
	.cfi_rel_offset	$15, 15*8
	.cfi_rel_offset	$19, 19*8
	.cfi_rel_offset	$20, 20*8
	.cfi_rel_offset	$21, 21*8
	.cfi_rel_offset	$22, 22*8
	.cfi_rel_offset	$23, 23*8
	.cfi_rel_offset	$24, 24*8
	.cfi_rel_offset	$25, 25*8
	.cfi_rel_offset	$26, 26*8
	.cfi_rel_offset	$27, 27*8
	.cfi_rel_offset	$28, 28*8
	.cfi_rel_offset	$29, 29*8
	lda	$8, 0x3fff
	stq	$31, 248($sp)
	bic	$sp, $8, $8
	jsr	$26, do_entUna
	ldq	$0, 0($sp)
	ldq	$1, 8($sp)
	ldq	$2, 16($sp)
	ldq	$3, 24($sp)
	ldq	$4, 32($sp)
	ldq	$5, 40($sp)
	ldq	$6, 48($sp)
	ldq	$7, 56($sp)
	ldq	$8, 64($sp)
	ldq	$9, 72($sp)
	ldq	$10, 80($sp)
	ldq	$11, 88($sp)
	ldq	$12, 96($sp)
	ldq	$13, 104($sp)
	ldq	$14, 112($sp)
	ldq	$15, 120($sp)
	/* 16-18 PAL-saved */
	ldq	$19, 152($sp)
	ldq	$20, 160($sp)
	ldq	$21, 168($sp)
	ldq	$22, 176($sp)
	ldq	$23, 184($sp)
	ldq	$24, 192($sp)
	ldq	$25, 200($sp)
	ldq	$26, 208($sp)
	ldq	$27, 216($sp)
	ldq	$28, 224($sp)
	ldq	$gp, 232($sp)
	lda	$sp, 256($sp)
	.cfi_restore	$1
	.cfi_restore	$2
	.cfi_restore	$3
	.cfi_restore	$4
	.cfi_restore	$5
	.cfi_restore	$6
	.cfi_restore	$7
	.cfi_restore	$8
	.cfi_restore	$9
	.cfi_restore	$10
	.cfi_restore	$11
	.cfi_restore	$12
	.cfi_restore	$13
	.cfi_restore	$14
	.cfi_restore	$15
	.cfi_restore	$19
	.cfi_restore	$20
	.cfi_restore	$21
	.cfi_restore	$22
	.cfi_restore	$23
	.cfi_restore	$24
	.cfi_restore	$25
	.cfi_restore	$26
	.cfi_restore	$27
	.cfi_restore	$28
	.cfi_restore	$29
	.cfi_adjust_cfa_offset	-256
	call_pal PAL_rti

	.align	4
entUnaUser:
	.cfi_restore_state
	ldq	$0, 0($sp)	/* restore original $0 */
	lda	$sp, 256($sp)	/* pop entUna's stack frame */
	.cfi_restore	$0
	.cfi_adjust_cfa_offset	-256
	SAVE_ALL		/* setup normal kernel stack */
	lda	$sp, -56($sp)
	.cfi_adjust_cfa_offset	56
	stq	$9, 0($sp)
	stq	$10, 8($sp)
	stq	$11, 16($sp)
	stq	$12, 24($sp)
	stq	$13, 32($sp)
	stq	$14, 40($sp)
	stq	$15, 48($sp)
	.cfi_rel_offset	$9, 0
	.cfi_rel_offset	$10, 8
	.cfi_rel_offset	$11, 16
	.cfi_rel_offset	$12, 24
	.cfi_rel_offset	$13, 32
	.cfi_rel_offset	$14, 40
	.cfi_rel_offset	$15, 48
	lda	$8, 0x3fff
	addq	$sp, 56, $19
	bic	$sp, $8, $8
	jsr	$26, do_entUnaUser
	ldq	$9, 0($sp)
	ldq	$10, 8($sp)
	ldq	$11, 16($sp)
	ldq	$12, 24($sp)
	ldq	$13, 32($sp)
	ldq	$14, 40($sp)
	ldq	$15, 48($sp)
	lda	$sp, 56($sp)
	.cfi_restore	$9
	.cfi_restore	$10
	.cfi_restore	$11
	.cfi_restore	$12
	.cfi_restore	$13
	.cfi_restore	$14
	.cfi_restore	$15
	.cfi_adjust_cfa_offset	-56
	br	ret_from_sys_call
CFI_END_OSF_FRAME entUna

CFI_START_OSF_FRAME entDbg
	SAVE_ALL
	lda	$8, 0x3fff
	lda	$26, ret_from_sys_call
	bic	$sp, $8, $8
	mov	$sp, $16
	jsr	$31, do_entDbg
CFI_END_OSF_FRAME entDbg

/*
 * The system call entry point is special.  Most importantly, it looks
 * like a function call to userspace as far as clobbered registers.  We
 * do preserve the argument registers (for syscall restarts) and $26
 * (for leaf syscall functions).
 *
 * So much for theory.  We don't take advantage of this yet.
 *
 * Note that a0-a2 are not saved by PALcode as with the other entry points.
 */

	.align	4
	.globl	entSys
	.type	entSys, @function
	.cfi_startproc simple
	.cfi_return_column 64
	.cfi_def_cfa	$sp, 48
	.cfi_rel_offset	64, 8
	.cfi_rel_offset	$gp, 16
entSys:
	SAVE_ALL
	lda	$8, 0x3fff
	bic	$sp, $8, $8
	lda	$4, NR_SYSCALLS($31)
	stq	$16, SP_OFF+24($sp)
	lda	$5, sys_call_table
	lda	$27, sys_ni_syscall
	cmpult	$0, $4, $4
	ldl	$3, TI_FLAGS($8)
	stq	$17, SP_OFF+32($sp)
	s8addq	$0, $5, $5
	stq	$18, SP_OFF+40($sp)
	.cfi_rel_offset	$16, SP_OFF+24
	.cfi_rel_offset	$17, SP_OFF+32
	.cfi_rel_offset	$18, SP_OFF+40
#ifdef CONFIG_AUDITSYSCALL
	lda     $6, _TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT
	and     $3, $6, $3
	bne     $3, strace
#else
	blbs    $3, strace		/* check for SYSCALL_TRACE in disguise */
#endif
	beq	$4, 1f
	ldq	$27, 0($5)
1:	jsr	$26, ($27), sys_ni_syscall
	ldgp	$gp, 0($26)
	blt	$0, $syscall_error	/* the call failed */
$ret_success:
	stq	$0, 0($sp)
	stq	$31, 72($sp)		/* a3=0 => no error */

	.align	4
	.globl	ret_from_sys_call
ret_from_sys_call:
	cmovne	$26, 0, $18		/* $18 = 0 => non-restartable */
	ldq	$0, SP_OFF($sp)
	and	$0, 8, $0
	beq	$0, ret_to_kernel
ret_to_user:
	/* Make sure need_resched and sigpending don't change between
		sampling and the rti.  */
	lda	$16, 7
	call_pal PAL_swpipl
	ldl	$17, TI_FLAGS($8)
	and	$17, _TIF_WORK_MASK, $2
	bne	$2, work_pending
restore_all:
	.cfi_remember_state
	RESTORE_ALL
	call_pal PAL_rti

ret_to_kernel:
	.cfi_restore_state
	lda	$16, 7
	call_pal PAL_swpipl
	br restore_all

	.align 3
$syscall_error:
	/*
	 * Some system calls (e.g., ptrace) can return arbitrary
	 * values which might normally be mistaken as error numbers.
	 * Those functions must zero $0 (v0) directly in the stack
	 * frame to indicate that a negative return value wasn't an
	 * error number..
	 */
	ldq	$18, 0($sp)	/* old syscall nr (zero if success) */
	beq	$18, $ret_success

	ldq	$19, 72($sp)	/* .. and this a3 */
	subq	$31, $0, $0	/* with error in v0 */
	addq	$31, 1, $1	/* set a3 for errno return */
	stq	$0, 0($sp)
	mov	$31, $26	/* tell "ret_from_sys_call" we can restart */
	stq	$1, 72($sp)	/* a3 for return */
	br	ret_from_sys_call

/*
 * Do all cleanup when returning from all interrupts and system calls.
 *
 * Arguments:
 *       $8: current.
 *      $17: TI_FLAGS.
 *      $18: The old syscall number, or zero if this is not a return
 *           from a syscall that errored and is possibly restartable.
 *      $19: The old a3 value
 */

	.align	4
	.type	work_pending, @function
work_pending:
	and	$17, _TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL, $2
	bne	$2, $work_notifysig

$work_resched:
	/*
	 * We can get here only if we returned from syscall without SIGPENDING
	 * or got through work_notifysig already.  Either case means no syscall
	 * restarts for us, so let $18 and $19 burn.
	 */
	jsr	$26, schedule
	mov	0, $18
	br	ret_to_user

$work_notifysig:
	mov	$sp, $16
	DO_SWITCH_STACK
	jsr	$26, do_work_pending
	UNDO_SWITCH_STACK
	br	restore_all

/*
 * PTRACE syscall handler
 */

	.align	4
	.type	strace, @function
strace:
	/* set up signal stack, call syscall_trace */
	DO_SWITCH_STACK
	jsr	$26, syscall_trace_enter /* returns the syscall number */
	UNDO_SWITCH_STACK

	/* get the arguments back.. */
	ldq	$16, SP_OFF+24($sp)
	ldq	$17, SP_OFF+32($sp)
	ldq	$18, SP_OFF+40($sp)
	ldq	$19, 72($sp)
	ldq	$20, 80($sp)
	ldq	$21, 88($sp)

	/* get the system call pointer.. */
	lda	$1, NR_SYSCALLS($31)
	lda	$2, sys_call_table
	lda	$27, sys_ni_syscall
	cmpult	$0, $1, $1
	s8addq	$0, $2, $2
	beq	$1, 1f
	ldq	$27, 0($2)
1:	jsr	$26, ($27), sys_gettimeofday
ret_from_straced:
	ldgp	$gp, 0($26)

	/* check return.. */
	blt	$0, $strace_error	/* the call failed */
$strace_success:
	stq	$31, 72($sp)		/* a3=0 => no error */
	stq	$0, 0($sp)		/* save return value */

	DO_SWITCH_STACK
	jsr	$26, syscall_trace_leave
	UNDO_SWITCH_STACK
	br	$31, ret_from_sys_call

	.align	3
$strace_error:
	ldq	$18, 0($sp)	/* old syscall nr (zero if success) */
	beq	$18, $strace_success
	ldq	$19, 72($sp)	/* .. and this a3 */

	subq	$31, $0, $0	/* with error in v0 */
	addq	$31, 1, $1	/* set a3 for errno return */
	stq	$0, 0($sp)
	stq	$1, 72($sp)	/* a3 for return */

	DO_SWITCH_STACK
	mov	$18, $9		/* save old syscall number */
	mov	$19, $10	/* save old a3 */
	jsr	$26, syscall_trace_leave
	mov	$9, $18
	mov	$10, $19
	UNDO_SWITCH_STACK

	mov	$31, $26	/* tell "ret_from_sys_call" we can restart */
	br	ret_from_sys_call
CFI_END_OSF_FRAME entSys

/*
 * Save and restore the switch stack -- aka the balance of the user context.
 */

	.align	4
	.type	do_switch_stack, @function
	.cfi_startproc simple
	.cfi_return_column 64
	.cfi_def_cfa $sp, 0
	.cfi_register 64, $1
do_switch_stack:
	lda	$sp, -SWITCH_STACK_SIZE($sp)
	.cfi_adjust_cfa_offset	SWITCH_STACK_SIZE
	stq	$9, 0($sp)
	stq	$10, 8($sp)
	stq	$11, 16($sp)
	stq	$12, 24($sp)
	stq	$13, 32($sp)
	stq	$14, 40($sp)
	stq	$15, 48($sp)
	stq	$26, 56($sp)
	stt	$f0, 64($sp)
	stt	$f1, 72($sp)
	stt	$f2, 80($sp)
	stt	$f3, 88($sp)
	stt	$f4, 96($sp)
	stt	$f5, 104($sp)
	stt	$f6, 112($sp)
	stt	$f7, 120($sp)
	stt	$f8, 128($sp)
	stt	$f9, 136($sp)
	stt	$f10, 144($sp)
	stt	$f11, 152($sp)
	stt	$f12, 160($sp)
	stt	$f13, 168($sp)
	stt	$f14, 176($sp)
	stt	$f15, 184($sp)
	stt	$f16, 192($sp)
	stt	$f17, 200($sp)
	stt	$f18, 208($sp)
	stt	$f19, 216($sp)
	stt	$f20, 224($sp)
	stt	$f21, 232($sp)
	stt	$f22, 240($sp)
	stt	$f23, 248($sp)
	stt	$f24, 256($sp)
	stt	$f25, 264($sp)
	stt	$f26, 272($sp)
	stt	$f27, 280($sp)
	mf_fpcr	$f0		# get fpcr
	stt	$f28, 288($sp)
	stt	$f29, 296($sp)
	stt	$f30, 304($sp)
	stt	$f0, 312($sp)	# save fpcr in slot of $f31
	ldt	$f0, 64($sp)	# dont let "do_switch_stack" change fp state.
	ret	$31, ($1), 1
	.cfi_endproc
	.size	do_switch_stack, .-do_switch_stack

	.align	4
	.type	undo_switch_stack, @function
	.cfi_startproc simple
	.cfi_def_cfa $sp, 0
	.cfi_register 64, $1
undo_switch_stack:
	ldq	$9, 0($sp)
	ldq	$10, 8($sp)
	ldq	$11, 16($sp)
	ldq	$12, 24($sp)
	ldq	$13, 32($sp)
	ldq	$14, 40($sp)
	ldq	$15, 48($sp)
	ldq	$26, 56($sp)
	ldt	$f30, 312($sp)	# get saved fpcr
	ldt	$f0, 64($sp)
	ldt	$f1, 72($sp)
	ldt	$f2, 80($sp)
	ldt	$f3, 88($sp)
	mt_fpcr	$f30		# install saved fpcr
	ldt	$f4, 96($sp)
	ldt	$f5, 104($sp)
	ldt	$f6, 112($sp)
	ldt	$f7, 120($sp)
	ldt	$f8, 128($sp)
	ldt	$f9, 136($sp)
	ldt	$f10, 144($sp)
	ldt	$f11, 152($sp)
	ldt	$f12, 160($sp)
	ldt	$f13, 168($sp)
	ldt	$f14, 176($sp)
	ldt	$f15, 184($sp)
	ldt	$f16, 192($sp)
	ldt	$f17, 200($sp)
	ldt	$f18, 208($sp)
	ldt	$f19, 216($sp)
	ldt	$f20, 224($sp)
	ldt	$f21, 232($sp)
	ldt	$f22, 240($sp)
	ldt	$f23, 248($sp)
	ldt	$f24, 256($sp)
	ldt	$f25, 264($sp)
	ldt	$f26, 272($sp)
	ldt	$f27, 280($sp)
	ldt	$f28, 288($sp)
	ldt	$f29, 296($sp)
	ldt	$f30, 304($sp)
	lda	$sp, SWITCH_STACK_SIZE($sp)
	ret	$31, ($1), 1
	.cfi_endproc
	.size	undo_switch_stack, .-undo_switch_stack

/*
 * The meat of the context switch code.
 */

	.align	4
	.globl	alpha_switch_to
	.type	alpha_switch_to, @function
	.cfi_startproc
alpha_switch_to:
	DO_SWITCH_STACK
	call_pal PAL_swpctx
	lda	$8, 0x3fff
	UNDO_SWITCH_STACK
	bic	$sp, $8, $8
	mov	$17, $0
	ret
	.cfi_endproc
	.size	alpha_switch_to, .-alpha_switch_to

/*
 * New processes begin life here.
 */

	.globl	ret_from_fork
	.align	4
	.ent	ret_from_fork
ret_from_fork:
	lda	$26, ret_to_user
	mov	$17, $16
	jmp	$31, schedule_tail
.end ret_from_fork

/*
 * ... and new kernel threads - here
 */
	.align 4
	.globl	ret_from_kernel_thread
	.ent	ret_from_kernel_thread
ret_from_kernel_thread:
	mov	$17, $16
	jsr	$26, schedule_tail
	mov	$9, $27
	mov	$10, $16
	jsr	$26, ($9)
	br	$31, ret_to_user
.end ret_from_kernel_thread

/*
 * Special system calls.  Most of these are special in that they either
 * have to play switch_stack games.
 */

.macro	fork_like name
	.align	4
	.globl	alpha_\name
	.ent	alpha_\name
alpha_\name:
	.prologue 0
	bsr	$1, do_switch_stack
	jsr	$26, sys_\name
	ldq	$26, 56($sp)
	lda	$sp, SWITCH_STACK_SIZE($sp)
	ret
.end	alpha_\name
.endm

fork_like fork
fork_like vfork
fork_like clone

.macro	sigreturn_like name
	.align	4
	.globl	sys_\name
	.ent	sys_\name
sys_\name:
	.prologue 0
	lda	$9, ret_from_straced
	cmpult	$26, $9, $9
	lda	$sp, -SWITCH_STACK_SIZE($sp)
	jsr	$26, do_\name
	bne	$9, 1f
	jsr	$26, syscall_trace_leave
1:	br	$1, undo_switch_stack
	br	ret_from_sys_call
.end sys_\name
.endm

sigreturn_like sigreturn
sigreturn_like rt_sigreturn

	.align	4
	.globl	alpha_syscall_zero
	.ent	alpha_syscall_zero
alpha_syscall_zero:
	.prologue 0
	/* Special because it needs to do something opposite to
	   force_successful_syscall_return().  We use the saved
	   syscall number for that, zero meaning "not an error".
	   That works nicely, but for real syscall 0 we need to
	   make sure that this logics doesn't get confused.
	   Store a non-zero there - -ENOSYS we need in register
	   for our return value will do just fine.
	  */
	lda	$0, -ENOSYS
	unop
	stq	$0, 0($sp)
	ret
.end alpha_syscall_zero


code:
// SPDX-License-Identifier: GPL-2.0
/*
 *	linux/arch/alpha/kernel/err_common.c
 *
 *	Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation)
 *
 *	Error handling code supporting Alpha systems
 */

#include <linux/init.h>
#include <linux/sched.h>

#include <asm/io.h>
#include <asm/hwrpb.h>
#include <asm/smp.h>
#include <asm/err_common.h>

#include "err_impl.h"
#include "proto.h"

/*
 * err_print_prefix -- error handling print routines should prefix
 * all prints with this
 */
char *err_print_prefix = KERN_NOTICE;

/*
 * Generic
 */
void
mchk_dump_mem(void *data, size_t length, char **annotation)
{
	unsigned long *ldata = data;
	size_t i;

	for (i = 0; (i * sizeof(*ldata)) < length; i++) {
		if (annotation && !annotation[i])
			annotation = NULL;
		printk("%s    %08x: %016lx    %s\n",
		       err_print_prefix,
		       (unsigned)(i * sizeof(*ldata)), ldata[i],
		       annotation ? annotation[i] : "");
	}
}

void
mchk_dump_logout_frame(struct el_common *mchk_header)
{
	printk("%s  -- Frame Header --\n"
	         "    Frame Size:   %d (0x%x) bytes\n"
	         "    Flags:        %s%s\n"
	         "    MCHK Code:    0x%x\n"
	         "    Frame Rev:    %d\n"
	         "    Proc Offset:  0x%08x\n"
	         "    Sys Offset:   0x%08x\n"
  	         "  -- Processor Region --\n",
	       err_print_prefix,
	       mchk_header->size, mchk_header->size,
	       mchk_header->retry ? "RETRY " : "",
  	         mchk_header->err2 ? "SECOND_ERR " : "",
	       mchk_header->code,
	       mchk_header->frame_rev,
	       mchk_header->proc_offset,
	       mchk_header->sys_offset);

	mchk_dump_mem((void *)
		      ((unsigned long)mchk_header + mchk_header->proc_offset),
		      mchk_header->sys_offset - mchk_header->proc_offset,
		      NULL);

	printk("%s  -- System Region --\n", err_print_prefix);
	mchk_dump_mem((void *)
		      ((unsigned long)mchk_header + mchk_header->sys_offset),
		      mchk_header->size - mchk_header->sys_offset,
		      NULL);
	printk("%s  -- End of Frame --\n", err_print_prefix);
}

/*
 * Console Data Log
 */
/* Data */
static struct el_subpacket_handler *subpacket_handler_list = NULL;
static struct el_subpacket_annotation *subpacket_annotation_list = NULL;

static struct el_subpacket *
el_process_header_subpacket(struct el_subpacket *header)
{
	union el_timestamp timestamp;
	char *name = "UNKNOWN EVENT";
	int packet_count = 0;
	int length = 0;

	if (header->class != EL_CLASS__HEADER) {
		printk("%s** Unexpected header CLASS %d TYPE %d, aborting\n",
		       err_print_prefix,
		       header->class, header->type);
		return NULL;
	}

	switch(header->type) {
	case EL_TYPE__HEADER__SYSTEM_ERROR_FRAME:
		name = "SYSTEM ERROR";
		length = header->by_type.sys_err.frame_length;
		packet_count =
			header->by_type.sys_err.frame_packet_count;
		timestamp.as_int = 0;
		break;
	case EL_TYPE__HEADER__SYSTEM_EVENT_FRAME:
		name = "SYSTEM EVENT";
		length = header->by_type.sys_event.frame_length;
		packet_count =
			header->by_type.sys_event.frame_packet_count;
		timestamp = header->by_type.sys_event.timestamp;
		break;
	case EL_TYPE__HEADER__HALT_FRAME:
		name = "ERROR HALT";
		length = header->by_type.err_halt.frame_length;
		packet_count =
			header->by_type.err_halt.frame_packet_count;
		timestamp = header->by_type.err_halt.timestamp;
		break;
	case EL_TYPE__HEADER__LOGOUT_FRAME:
		name = "LOGOUT FRAME";
		length = header->by_type.logout_header.frame_length;
		packet_count = 1;
		timestamp.as_int = 0;
		break;
	default: /* Unknown */
		printk("%s** Unknown header - CLASS %d TYPE %d, aborting\n",
		       err_print_prefix,
		       header->class, header->type);
		return NULL;
	}

	printk("%s*** %s:\n"
	         "  CLASS %d, TYPE %d\n",
	       err_print_prefix,
	       name,
	       header->class, header->type);
	el_print_timestamp(&timestamp);

	/*
	 * Process the subpackets
	 */
	el_process_subpackets(header, packet_count);

	/* return the next header */
	header = (struct el_subpacket *)
		((unsigned long)header + header->length + length);
	return header;
}

static struct el_subpacket *
el_process_subpacket_reg(struct el_subpacket *header)
{
	struct el_subpacket *next = NULL;
	struct el_subpacket_handler *h = subpacket_handler_list;

	for (; h && h->class != header->class; h = h->next);
	if (h) next = h->handler(header);

	return next;
}

void
el_print_timestamp(union el_timestamp *timestamp)
{
	if (timestamp->as_int)
		printk("%s  TIMESTAMP: %d/%d/%02d %d:%02d:%0d\n",
		       err_print_prefix,
		       timestamp->b.month, timestamp->b.day,
		       timestamp->b.year, timestamp->b.hour,
		       timestamp->b.minute, timestamp->b.second);
}

void
el_process_subpackets(struct el_subpacket *header, int packet_count)
{
	struct el_subpacket *subpacket;
	int i;

	subpacket = (struct el_subpacket *)
		((unsigned long)header + header->length);

	for (i = 0; subpacket && i < packet_count; i++) {
		printk("%sPROCESSING SUBPACKET %d\n", err_print_prefix, i);
		subpacket = el_process_subpacket(subpacket);
	}
}

struct el_subpacket *
el_process_subpacket(struct el_subpacket *header)
{
	struct el_subpacket *next = NULL;

	switch(header->class) {
	case EL_CLASS__TERMINATION:
		/* Termination packet, there are no more */
		break;
	case EL_CLASS__HEADER:
		next = el_process_header_subpacket(header);
		break;
	default:
		if (NULL == (next = el_process_subpacket_reg(header))) {
			printk("%s** Unexpected header CLASS %d TYPE %d"
			       " -- aborting.\n",
			       err_print_prefix,
			       header->class, header->type);
		}
		break;
	}

	return next;
}

void
el_annotate_subpacket(struct el_subpacket *header)
{
	struct el_subpacket_annotation *a;
	char **annotation = NULL;

	for (a = subpacket_annotation_list; a; a = a->next) {
		if (a->class == header->class &&
		    a->type == header->type &&
		    a->revision == header->revision) {
			/*
			 * We found the annotation
			 */
			annotation = a->annotation;
			printk("%s  %s\n", err_print_prefix, a->description);
			break;
		}
	}

	mchk_dump_mem(header, header->length, annotation);
}

static void __init
cdl_process_console_data_log(int cpu, struct percpu_struct *pcpu)
{
	struct el_subpacket *header = (struct el_subpacket *)
		(IDENT_ADDR | pcpu->console_data_log_pa);
	int err;

	printk("%s******* CONSOLE DATA LOG FOR CPU %d. *******\n"
	         "*** Error(s) were logged on a previous boot\n",
	       err_print_prefix, cpu);

	for (err = 0; header && (header->class != EL_CLASS__TERMINATION); err++)
		header = el_process_subpacket(header);

	/* let the console know it's ok to clear the error(s) at restart */
	pcpu->console_data_log_pa = 0;

	printk("%s*** %d total error(s) logged\n"
	         "**** END OF CONSOLE DATA LOG FOR CPU %d ****\n",
	       err_print_prefix, err, cpu);
}

void __init
cdl_check_console_data_log(void)
{
	struct percpu_struct *pcpu;
	unsigned long cpu;

	for (cpu = 0; cpu < hwrpb->nr_processors; cpu++) {
		pcpu = (struct percpu_struct *)
			((unsigned long)hwrpb + hwrpb->processor_offset
			 + cpu * hwrpb->processor_size);
		if (pcpu->console_data_log_pa)
			cdl_process_console_data_log(cpu, pcpu);
	}

}

int __init
cdl_register_subpacket_annotation(struct el_subpacket_annotation *new)
{
	struct el_subpacket_annotation *a = subpacket_annotation_list;

	if (a == NULL) subpacket_annotation_list = new;
	else {
		for (; a->next != NULL; a = a->next) {
			if ((a->class == new->class && a->type == new->type) ||
			    a == new) {
				printk("Attempted to re-register "
				       "subpacket annotation\n");
				return -EINVAL;
			}
		}
		a->next = new;
	}
	new->next = NULL;

	return 0;
}

int __init
cdl_register_subpacket_handler(struct el_subpacket_handler *new)
{
	struct el_subpacket_handler *h = subpacket_handler_list;

	if (h == NULL) subpacket_handler_list = new;
	else {
		for (; h->next != NULL; h = h->next) {
			if (h->class == new->class || h == new) {
				printk("Attempted to re-register "
				       "subpacket handler\n");
				return -EINVAL;
			}
		}
		h->next = new;
	}
	new->next = NULL;

	return 0;
}


code:
// SPDX-License-Identifier: GPL-2.0
/*
 *	linux/arch/alpha/kernel/err_ev6.c
 *
 *	Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation)
 *
 *	Error handling code supporting Alpha systems
 */

#include <linux/sched.h>

#include <asm/io.h>
#include <asm/irq_regs.h>
#include <asm/hwrpb.h>
#include <asm/smp.h>
#include <asm/err_common.h>
#include <asm/err_ev6.h>

#include "err_impl.h"
#include "proto.h"

static int
ev6_parse_ibox(u64 i_stat, int print)
{
	int status = MCHK_DISPOSITION_REPORT;

#define EV6__I_STAT__PAR	(1UL << 29)
#define EV6__I_STAT__ERRMASK	(EV6__I_STAT__PAR)

	if (!(i_stat & EV6__I_STAT__ERRMASK))
		return MCHK_DISPOSITION_UNKNOWN_ERROR;

	if (!print)
		return status;

	if (i_stat & EV6__I_STAT__PAR)
		printk("%s    Icache parity error\n", err_print_prefix);

	return status;
}

static int
ev6_parse_mbox(u64 mm_stat, u64 d_stat, u64 c_stat, int print)
{
	int status = MCHK_DISPOSITION_REPORT;

#define EV6__MM_STAT__DC_TAG_PERR	(1UL << 10)
#define EV6__MM_STAT__ERRMASK		(EV6__MM_STAT__DC_TAG_PERR)
#define EV6__D_STAT__TPERR_P0		(1UL << 0)
#define EV6__D_STAT__TPERR_P1		(1UL << 1)
#define EV6__D_STAT__ECC_ERR_ST		(1UL << 2)
#define EV6__D_STAT__ECC_ERR_LD		(1UL << 3)
#define EV6__D_STAT__SEO		(1UL << 4)
#define EV6__D_STAT__ERRMASK		(EV6__D_STAT__TPERR_P0 |	\
                                         EV6__D_STAT__TPERR_P1 | 	\
                                         EV6__D_STAT__ECC_ERR_ST | 	\
                                         EV6__D_STAT__ECC_ERR_LD | 	\
                                         EV6__D_STAT__SEO)

	if (!(d_stat & EV6__D_STAT__ERRMASK) &&
	    !(mm_stat & EV6__MM_STAT__ERRMASK))
		return MCHK_DISPOSITION_UNKNOWN_ERROR;

	if (!print)
		return status;

	if (mm_stat & EV6__MM_STAT__DC_TAG_PERR)
		printk("%s    Dcache tag parity error on probe\n",
		       err_print_prefix);
	if (d_stat & EV6__D_STAT__TPERR_P0)
		printk("%s    Dcache tag parity error - pipe 0\n",
		       err_print_prefix);
	if (d_stat & EV6__D_STAT__TPERR_P1)
		printk("%s    Dcache tag parity error - pipe 1\n",
		       err_print_prefix);
	if (d_stat & EV6__D_STAT__ECC_ERR_ST)
		printk("%s    ECC error occurred on a store\n",
		       err_print_prefix);
	if (d_stat & EV6__D_STAT__ECC_ERR_LD)
		printk("%s    ECC error occurred on a %s load\n",
		       err_print_prefix,
		       c_stat ? "" : "speculative ");
	if (d_stat & EV6__D_STAT__SEO)
		printk("%s    Dcache second error\n", err_print_prefix);

	return status;
}

static int
ev6_parse_cbox(u64 c_addr, u64 c1_syn, u64 c2_syn,
	       u64 c_stat, u64 c_sts, int print)
{
	static const char * const sourcename[] = {
		"UNKNOWN", "UNKNOWN", "UNKNOWN",
		"MEMORY", "BCACHE", "DCACHE",
		"BCACHE PROBE", "BCACHE PROBE"
	};
	static const char * const streamname[] = { "D", "I" };
	static const char * const bitsname[] = { "SINGLE", "DOUBLE" };
	int status = MCHK_DISPOSITION_REPORT;
	int source = -1, stream = -1, bits = -1;

#define EV6__C_STAT__BC_PERR		(0x01)
#define EV6__C_STAT__DC_PERR		(0x02)
#define EV6__C_STAT__DSTREAM_MEM_ERR	(0x03)
#define EV6__C_STAT__DSTREAM_BC_ERR	(0x04)
#define EV6__C_STAT__DSTREAM_DC_ERR	(0x05)
#define EV6__C_STAT__PROBE_BC_ERR0	(0x06)	/* both 6 and 7 indicate... */
#define EV6__C_STAT__PROBE_BC_ERR1	(0x07)	/* ...probe bc error.       */
#define EV6__C_STAT__ISTREAM_MEM_ERR	(0x0B)
#define EV6__C_STAT__ISTREAM_BC_ERR	(0x0C)
#define EV6__C_STAT__DSTREAM_MEM_DBL	(0x13)
#define EV6__C_STAT__DSTREAM_BC_DBL	(0x14)
#define EV6__C_STAT__ISTREAM_MEM_DBL	(0x1B)
#define EV6__C_STAT__ISTREAM_BC_DBL	(0x1C)
#define EV6__C_STAT__SOURCE_MEMORY	(0x03)
#define EV6__C_STAT__SOURCE_BCACHE	(0x04)
#define EV6__C_STAT__SOURCE__S		(0)
#define EV6__C_STAT__SOURCE__M 		(0x07)
#define EV6__C_STAT__ISTREAM__S		(3)
#define EV6__C_STAT__ISTREAM__M		(0x01)
#define EV6__C_STAT__DOUBLE__S		(4)
#define EV6__C_STAT__DOUBLE__M		(0x01)
#define EV6__C_STAT__ERRMASK		(0x1F)
#define EV6__C_STS__SHARED		(1 << 0)
#define EV6__C_STS__DIRTY		(1 << 1)
#define EV6__C_STS__VALID		(1 << 2)
#define EV6__C_STS__PARITY		(1 << 3)

	if (!(c_stat & EV6__C_STAT__ERRMASK))
		return MCHK_DISPOSITION_UNKNOWN_ERROR;

	if (!print)
		return status;

	source = EXTRACT(c_stat, EV6__C_STAT__SOURCE);
	stream = EXTRACT(c_stat, EV6__C_STAT__ISTREAM);
	bits = EXTRACT(c_stat, EV6__C_STAT__DOUBLE);

	if (c_stat & EV6__C_STAT__BC_PERR) {
		printk("%s    Bcache tag parity error\n", err_print_prefix);
		source = -1;
	}

	if (c_stat & EV6__C_STAT__DC_PERR) {
		printk("%s    Dcache tag parity error\n", err_print_prefix);
		source = -1;
	}

	if (c_stat == EV6__C_STAT__PROBE_BC_ERR0 ||
	    c_stat == EV6__C_STAT__PROBE_BC_ERR1) {
		printk("%s    Bcache single-bit error on a probe hit\n",
		       err_print_prefix);
		source = -1;
	}

	if (source != -1)
		printk("%s    %s-STREAM %s-BIT ECC error from %s\n",
		       err_print_prefix,
		       streamname[stream], bitsname[bits], sourcename[source]);

	printk("%s    Address: 0x%016llx\n"
	         "    Syndrome[upper.lower]: %02llx.%02llx\n",
	       err_print_prefix,
	       c_addr,
	       c2_syn, c1_syn);

	if (source == EV6__C_STAT__SOURCE_MEMORY ||
	    source == EV6__C_STAT__SOURCE_BCACHE)
		printk("%s    Block status: %s%s%s%s\n",
		       err_print_prefix,
		       (c_sts & EV6__C_STS__SHARED) ? "SHARED " : "",
		       (c_sts & EV6__C_STS__DIRTY)  ? "DIRTY "  : "",
		       (c_sts & EV6__C_STS__VALID)  ? "VALID "  : "",
		       (c_sts & EV6__C_STS__PARITY) ? "PARITY " : "");

	return status;
}

void
ev6_register_error_handlers(void)
{
	/* None right now. */
}

int
ev6_process_logout_frame(struct el_common *mchk_header, int print)
{
	struct el_common_EV6_mcheck *ev6mchk =
		(struct el_common_EV6_mcheck *)mchk_header;
	int status = MCHK_DISPOSITION_UNKNOWN_ERROR;

	status |= ev6_parse_ibox(ev6mchk->I_STAT, print);
	status |= ev6_parse_mbox(ev6mchk->MM_STAT, ev6mchk->DC_STAT,
				 ev6mchk->C_STAT, print);
	status |= ev6_parse_cbox(ev6mchk->C_ADDR, ev6mchk->DC1_SYNDROME,
				 ev6mchk->DC0_SYNDROME, ev6mchk->C_STAT,
				 ev6mchk->C_STS, print);

	if (!print)
		return status;

	if (status != MCHK_DISPOSITION_DISMISS) {
		char *saved_err_prefix = err_print_prefix;

		/*
		 * Dump some additional information from the frame
		 */
		printk("%s    EXC_ADDR: 0x%016lx   IER_CM: 0x%016lx"
		            "   ISUM: 0x%016lx\n"
		         "    PAL_BASE: 0x%016lx   I_CTL:  0x%016lx"
		            "   PCTX: 0x%016lx\n",
		       err_print_prefix,
		       ev6mchk->EXC_ADDR, ev6mchk->IER_CM, ev6mchk->ISUM,
		       ev6mchk->PAL_BASE, ev6mchk->I_CTL, ev6mchk->PCTX);

		if (status == MCHK_DISPOSITION_UNKNOWN_ERROR) {
			printk("%s    UNKNOWN error, frame follows:\n",
			       err_print_prefix);
		} else {
			/* had decode -- downgrade print level for frame */
			err_print_prefix = KERN_NOTICE;
		}

		mchk_dump_logout_frame(mchk_header);

		err_print_prefix = saved_err_prefix;
	}

	return status;
}

void
ev6_machine_check(unsigned long vector, unsigned long la_ptr)
{
	struct el_common *mchk_header = (struct el_common *)la_ptr;

	/*
	 * Sync the processor
	 */
	mb();
	draina();

	/*
	 * Parse the logout frame without printing first. If the only error(s)
	 * found are have a disposition of "dismiss", then just dismiss them
	 * and don't print any message
	 */
	if (ev6_process_logout_frame(mchk_header, 0) !=
	    MCHK_DISPOSITION_DISMISS) {
		char *saved_err_prefix = err_print_prefix;
		err_print_prefix = KERN_CRIT;

		/*
		 * Either a nondismissable error was detected or no
		 * recognized error was detected  in the logout frame
		 * -- report the error in either case
		 */
		printk("%s*CPU %s Error (Vector 0x%x) reported on CPU %d:\n",
		       err_print_prefix,
		       (vector == SCB_Q_PROCERR)?"Correctable":"Uncorrectable",
		       (unsigned int)vector, (int)smp_processor_id());

		ev6_process_logout_frame(mchk_header, 1);
		dik_show_regs(get_irq_regs(), NULL);

		err_print_prefix = saved_err_prefix;
	}

	/*
	 * Release the logout frame
	 */
	wrmces(0x7);
	mb();
}


code:
// SPDX-License-Identifier: GPL-2.0
/*
 *	linux/arch/alpha/kernel/err_ev7.c
 *
 *	Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation)
 *
 *	Error handling code supporting Alpha systems
 */

#include <linux/init.h>
#include <linux/sched.h>

#include <asm/io.h>
#include <asm/hwrpb.h>
#include <asm/smp.h>
#include <asm/err_common.h>
#include <asm/err_ev7.h>

#include "err_impl.h"
#include "proto.h"

struct ev7_lf_subpackets *
ev7_collect_logout_frame_subpackets(struct el_subpacket *el_ptr,
				    struct ev7_lf_subpackets *lf_subpackets)
{
	struct el_subpacket *subpacket;
	int i;

	/*
	 * A Marvel machine check frame is always packaged in an
	 * el_subpacket of class HEADER, type LOGOUT_FRAME.
	 */
	if (el_ptr->class != EL_CLASS__HEADER ||
	    el_ptr->type != EL_TYPE__HEADER__LOGOUT_FRAME)
		return NULL;

	/*
	 * It is a logout frame header. Look at the one subpacket.
	 */
	el_ptr = (struct el_subpacket *)
		((unsigned long)el_ptr + el_ptr->length);

	/*
	 * It has to be class PAL, type LOGOUT_FRAME.
	 */
	if (el_ptr->class != EL_CLASS__PAL ||
	    el_ptr->type != EL_TYPE__PAL__LOGOUT_FRAME)
		return NULL;

	lf_subpackets->logout = (struct ev7_pal_logout_subpacket *)
		el_ptr->by_type.raw.data_start;

	/*
	 * Process the subpackets.
	 */
	subpacket = (struct el_subpacket *)
		((unsigned long)el_ptr + el_ptr->length);
	for (i = 0;
	     subpacket && i < lf_subpackets->logout->subpacket_count;
	     subpacket = (struct el_subpacket *)
		     ((unsigned long)subpacket + subpacket->length), i++) {
		/*
		 * All subpackets should be class PAL.
		 */
		if (subpacket->class != EL_CLASS__PAL) {
			printk("%s**UNEXPECTED SUBPACKET CLASS %d "
			       "IN LOGOUT FRAME (packet %d\n",
			       err_print_prefix, subpacket->class, i);
			return NULL;
		}

		/*
		 * Remember the subpacket.
		 */
		switch(subpacket->type) {
		case EL_TYPE__PAL__EV7_PROCESSOR:
			lf_subpackets->ev7 =
				(struct ev7_pal_processor_subpacket *)
				subpacket->by_type.raw.data_start;
			break;

		case EL_TYPE__PAL__EV7_RBOX:
			lf_subpackets->rbox = (struct ev7_pal_rbox_subpacket *)
				subpacket->by_type.raw.data_start;
			break;

		case EL_TYPE__PAL__EV7_ZBOX:
			lf_subpackets->zbox = (struct ev7_pal_zbox_subpacket *)
				subpacket->by_type.raw.data_start;
			break;

		case EL_TYPE__PAL__EV7_IO:
			lf_subpackets->io = (struct ev7_pal_io_subpacket *)
				subpacket->by_type.raw.data_start;
			break;

		case EL_TYPE__PAL__ENV__AMBIENT_TEMPERATURE:
		case EL_TYPE__PAL__ENV__AIRMOVER_FAN:
		case EL_TYPE__PAL__ENV__VOLTAGE:
		case EL_TYPE__PAL__ENV__INTRUSION:
		case EL_TYPE__PAL__ENV__POWER_SUPPLY:
		case EL_TYPE__PAL__ENV__LAN:
		case EL_TYPE__PAL__ENV__HOT_PLUG:
			lf_subpackets->env[ev7_lf_env_index(subpacket->type)] =
 				(struct ev7_pal_environmental_subpacket *)
				subpacket->by_type.raw.data_start;
			break;

		default:
			/*
			 * Don't know what kind of frame this is.
			 */
			return NULL;
		}
	}

	return lf_subpackets;
}

void
ev7_machine_check(unsigned long vector, unsigned long la_ptr)
{
	struct el_subpacket *el_ptr = (struct el_subpacket *)la_ptr;
	char *saved_err_prefix = err_print_prefix;

	/*
	 * Sync the processor
	 */
	mb();
	draina();

	err_print_prefix = KERN_CRIT;
	printk("%s*CPU %s Error (Vector 0x%x) reported on CPU %d\n",
	       err_print_prefix,
	       (vector == SCB_Q_PROCERR) ? "Correctable" : "Uncorrectable",
	       (unsigned int)vector, (int)smp_processor_id());
	el_process_subpacket(el_ptr);
	err_print_prefix = saved_err_prefix;

	/*
	 * Release the logout frame
	 */
	wrmces(0x7);
	mb();
}

static char *el_ev7_processor_subpacket_annotation[] = {
	"Subpacket Header",	"I_STAT",	"DC_STAT",
	"C_ADDR",		"C_SYNDROME_1",	"C_SYNDROME_0",
	"C_STAT",		"C_STS",	"MM_STAT",
	"EXC_ADDR",		"IER_CM",	"ISUM",
	"PAL_BASE",		"I_CTL",	"PROCESS_CONTEXT",
	"CBOX_CTL",		"CBOX_STP_CTL",	"CBOX_ACC_CTL",
	"CBOX_LCL_SET",		"CBOX_GLB_SET",	"BBOX_CTL",
	"BBOX_ERR_STS",		"BBOX_ERR_IDX",	"CBOX_DDP_ERR_STS",
	"BBOX_DAT_RMP",		NULL
};

static char *el_ev7_zbox_subpacket_annotation[] = {
	"Subpacket Header",
	"ZBOX(0): DRAM_ERR_STATUS_2 / DRAM_ERR_STATUS_1",
	"ZBOX(0): DRAM_ERROR_CTL    / DRAM_ERR_STATUS_3",
	"ZBOX(0): DIFT_TIMEOUT      / DRAM_ERR_ADR",
	"ZBOX(0): FRC_ERR_ADR       / DRAM_MAPPER_CTL",
	"ZBOX(0): reserved          / DIFT_ERR_STATUS",
	"ZBOX(1): DRAM_ERR_STATUS_2 / DRAM_ERR_STATUS_1",
	"ZBOX(1): DRAM_ERROR_CTL    / DRAM_ERR_STATUS_3",
	"ZBOX(1): DIFT_TIMEOUT      / DRAM_ERR_ADR",
	"ZBOX(1): FRC_ERR_ADR       / DRAM_MAPPER_CTL",
	"ZBOX(1): reserved          / DIFT_ERR_STATUS",
	"CBOX_CTL",		"CBOX_STP_CTL",
	"ZBOX(0)_ERROR_PA",	"ZBOX(1)_ERROR_PA",
	"ZBOX(0)_ORED_SYNDROME","ZBOX(1)_ORED_SYNDROME",
	NULL
};

static char *el_ev7_rbox_subpacket_annotation[] = {
	"Subpacket Header",	"RBOX_CFG",	"RBOX_N_CFG",
	"RBOX_S_CFG",		"RBOX_E_CFG",	"RBOX_W_CFG",
	"RBOX_N_ERR",		"RBOX_S_ERR",	"RBOX_E_ERR",
	"RBOX_W_ERR",		"RBOX_IO_CFG",	"RBOX_IO_ERR",
	"RBOX_L_ERR",		"RBOX_WHOAMI",	"RBOX_IMASL",
	"RBOX_INTQ",		"RBOX_INT",	NULL
};

static char *el_ev7_io_subpacket_annotation[] = {
	"Subpacket Header",	"IO_ASIC_REV",	"IO_SYS_REV",
	"IO7_UPH",		"HPI_CTL",	"CRD_CTL",
	"HEI_CTL",		"PO7_ERROR_SUM","PO7_UNCRR_SYM",
	"PO7_CRRCT_SYM",	"PO7_UGBGE_SYM","PO7_ERR_PKT0",
	"PO7_ERR_PKT1",		"reserved",	"reserved",
	"PO0_ERR_SUM",		"PO0_TLB_ERR",	"PO0_SPL_COMPLT",
	"PO0_TRANS_SUM",	"PO0_FIRST_ERR","PO0_MULT_ERR",
	"DM CSR PH",		"DM CSR PH",	"DM CSR PH",
	"DM CSR PH",		"reserved",
	"PO1_ERR_SUM",		"PO1_TLB_ERR",	"PO1_SPL_COMPLT",
	"PO1_TRANS_SUM",	"PO1_FIRST_ERR","PO1_MULT_ERR",
	"DM CSR PH",		"DM CSR PH",	"DM CSR PH",
	"DM CSR PH",		"reserved",
	"PO2_ERR_SUM",		"PO2_TLB_ERR",	"PO2_SPL_COMPLT",
	"PO2_TRANS_SUM",	"PO2_FIRST_ERR","PO2_MULT_ERR",
	"DM CSR PH",		"DM CSR PH",	"DM CSR PH",
	"DM CSR PH",		"reserved",
	"PO3_ERR_SUM",		"PO3_TLB_ERR",	"PO3_SPL_COMPLT",
	"PO3_TRANS_SUM",	"PO3_FIRST_ERR","PO3_MULT_ERR",
	"DM CSR PH",		"DM CSR PH",	"DM CSR PH",
	"DM CSR PH",		"reserved",
	NULL
};

static struct el_subpacket_annotation el_ev7_pal_annotations[] = {
	SUBPACKET_ANNOTATION(EL_CLASS__PAL,
			     EL_TYPE__PAL__EV7_PROCESSOR,
			     1,
			     "EV7 Processor Subpacket",
			     el_ev7_processor_subpacket_annotation),
	SUBPACKET_ANNOTATION(EL_CLASS__PAL,
			     EL_TYPE__PAL__EV7_ZBOX,
			     1,
			     "EV7 ZBOX Subpacket",
			     el_ev7_zbox_subpacket_annotation),
	SUBPACKET_ANNOTATION(EL_CLASS__PAL,
			     EL_TYPE__PAL__EV7_RBOX,
			     1,
			     "EV7 RBOX Subpacket",
			     el_ev7_rbox_subpacket_annotation),
	SUBPACKET_ANNOTATION(EL_CLASS__PAL,
			     EL_TYPE__PAL__EV7_IO,
			     1,
			     "EV7 IO Subpacket",
			     el_ev7_io_subpacket_annotation)
};

static struct el_subpacket *
ev7_process_pal_subpacket(struct el_subpacket *header)
{
	struct ev7_pal_subpacket *packet;

	if (header->class != EL_CLASS__PAL) {
		printk("%s  ** Unexpected header CLASS %d TYPE %d, aborting\n",
		       err_print_prefix,
		       header->class, header->type);
		return NULL;
	}

	packet = (struct ev7_pal_subpacket *)header->by_type.raw.data_start;

	switch(header->type) {
	case EL_TYPE__PAL__LOGOUT_FRAME:
		printk("%s*** MCHK occurred on LPID %lld (RBOX %llx)\n",
		       err_print_prefix,
		       packet->by_type.logout.whami,
		       packet->by_type.logout.rbox_whami);
		el_print_timestamp(&packet->by_type.logout.timestamp);
		printk("%s  EXC_ADDR: %016llx\n"
		         "  HALT_CODE: %llx\n",
		       err_print_prefix,
		       packet->by_type.logout.exc_addr,
		       packet->by_type.logout.halt_code);
		el_process_subpackets(header,
                                      packet->by_type.logout.subpacket_count);
		break;
	default:
		printk("%s  ** PAL TYPE %d SUBPACKET\n",
		       err_print_prefix,
		       header->type);
		el_annotate_subpacket(header);
		break;
	}

	return (struct el_subpacket *)((unsigned long)header + header->length);
}

struct el_subpacket_handler ev7_pal_subpacket_handler =
	SUBPACKET_HANDLER_INIT(EL_CLASS__PAL, ev7_process_pal_subpacket);

void __init
ev7_register_error_handlers(void)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(el_ev7_pal_annotations); i++)
		cdl_register_subpacket_annotation(&el_ev7_pal_annotations[i]);

	cdl_register_subpacket_handler(&ev7_pal_subpacket_handler);
}


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 *	linux/arch/alpha/kernel/err_impl.h
 *
 *	Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation)
 *
 *	Contains declarations and macros to support Alpha error handling
 * 	implementations.
 */

#include <asm/mce.h>

union el_timestamp;
struct el_subpacket;
struct ev7_lf_subpackets;

struct el_subpacket_annotation {
	struct el_subpacket_annotation *next;
	u16 class;
	u16 type;
	u16 revision;
	char *description;
	char **annotation;
};
#define SUBPACKET_ANNOTATION(c, t, r, d, a) {NULL, (c), (t), (r), (d), (a)}

struct el_subpacket_handler {
	struct el_subpacket_handler *next;
	u16 class;
	struct el_subpacket *(*handler)(struct el_subpacket *);
};
#define SUBPACKET_HANDLER_INIT(c, h) {NULL, (c), (h)}

/*
 * Manipulate a field from a register given it's name. defines
 * for the LSB (__S - shift count) and bitmask (__M) are required
 *
 * EXTRACT(u, f) - extracts the field and places it at bit position 0
 * GEN_MASK(f) - creates an in-position mask for the field
 */
#define EXTRACT(u, f) (((u) >> f##__S) & f##__M)
#define GEN_MASK(f) ((u64)f##__M << f##__S)

/*
 * err_common.c
 */
extern char *err_print_prefix;

extern void mchk_dump_mem(void *, size_t, char **);
extern void mchk_dump_logout_frame(struct el_common *);
extern void el_print_timestamp(union el_timestamp *);
extern void el_process_subpackets(struct el_subpacket *, int);
extern struct el_subpacket *el_process_subpacket(struct el_subpacket *);
extern void el_annotate_subpacket(struct el_subpacket *);
extern void cdl_check_console_data_log(void);
extern int cdl_register_subpacket_annotation(struct el_subpacket_annotation *);
extern int cdl_register_subpacket_handler(struct el_subpacket_handler *);

/*
 * err_ev7.c
 */
extern struct ev7_lf_subpackets *
ev7_collect_logout_frame_subpackets(struct el_subpacket *,
				    struct ev7_lf_subpackets *);
extern void ev7_register_error_handlers(void);
extern void ev7_machine_check(unsigned long, unsigned long);

/*
 * err_ev6.c
 */
extern void ev6_register_error_handlers(void);
extern int ev6_process_logout_frame(struct el_common *, int);
extern void ev6_machine_check(unsigned long, unsigned long);

/*
 * err_marvel.c
 */
extern void marvel_machine_check(unsigned long, unsigned long);
extern void marvel_register_error_handlers(void);

/*
 * err_titan.c
 */
extern int titan_process_logout_frame(struct el_common *, int);
extern void titan_machine_check(unsigned long, unsigned long);
extern void titan_register_error_handlers(void);
extern int privateer_process_logout_frame(struct el_common *, int);
extern void privateer_machine_check(unsigned long, unsigned long);