code:
// SPDX-License-Identifier: GPL-2.0
/*
 *	linux/arch/alpha/kernel/core_irongate.c
 *
 * Based on code written by David A. Rusling (david.rusling@reo.mts.dec.com).
 *
 *	Copyright (C) 1999 Alpha Processor, Inc.,
 *		(David Daniel, Stig Telfer, Soohoon Lee)
 *
 * Code common to all IRONGATE core logic chips.
 */

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

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

#include <asm/ptrace.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>

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

/*
 * BIOS32-style PCI interface:
 */

#define DEBUG_CONFIG 0

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

igcsr32 *IronECC;

/*
 * 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.
 *
 *	addr[31:24]		reserved
 *	addr[23:16]		bus number (8 bits = 128 possible buses)
 *	addr[15:11]		Device number (5 bits)
 *	addr[10: 8]		function number
 *	addr[ 7: 2]		register number
 *
 * For IRONGATE:
 *    if (bus = addr[23:16]) == 0
 *    then
 *	  type 0 config cycle:
 *	      addr_on_pci[31:11] = id selection for device = addr[15:11]
 *	      addr_on_pci[10: 2] = addr[10: 2] ???
 *	      addr_on_pci[ 1: 0] = 00
 *    else
 *	  type 1 config cycle (pass on with no decoding):
 *	      addr_on_pci[31:24] = 0
 *	      addr_on_pci[23: 2] = addr[23: 2]
 *	      addr_on_pci[ 1: 0] = 01
 *    fi
 *
 * 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)
{
	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));

	*type1 = (bus != 0);

	addr = (bus << 16) | (device_fn << 8) | where;
	addr |= IRONGATE_CONF;

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

static int
irongate_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
irongate_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 irongate_pci_ops =
{
	.read =		irongate_read_config,
	.write =	irongate_write_config,
};

int
irongate_pci_clr_err(void)
{
	unsigned int nmi_ctl=0;
	unsigned int IRONGATE_jd;

again:
	IRONGATE_jd = IRONGATE0->stat_cmd;
	printk("Iron stat_cmd %x\n", IRONGATE_jd);
	IRONGATE0->stat_cmd = IRONGATE_jd; /* write again clears error bits */
	mb();
	IRONGATE_jd = IRONGATE0->stat_cmd;  /* re-read to force write */

	IRONGATE_jd = *IronECC;
	printk("Iron ECC %x\n", IRONGATE_jd);
	*IronECC = IRONGATE_jd; /* write again clears error bits */
	mb();
	IRONGATE_jd = *IronECC;  /* re-read to force write */

	/* Clear ALI NMI */
        nmi_ctl = inb(0x61);
        nmi_ctl |= 0x0c;
        outb(nmi_ctl, 0x61);
        nmi_ctl &= ~0x0c;
        outb(nmi_ctl, 0x61);

	IRONGATE_jd = *IronECC;
	if (IRONGATE_jd & 0x300) goto again;

	return 0;
}

#define IRONGATE_3GB 0xc0000000UL

/* On Albacore (aka UP1500) with 4Gb of RAM we have to reserve some
   memory for PCI. At this point we just reserve memory above 3Gb. Most
   of this memory will be freed after PCI setup is done. */
static void __init
albacore_init_arch(void)
{
	unsigned long memtop = max_low_pfn << PAGE_SHIFT;
	unsigned long pci_mem = (memtop + 0x1000000UL) & ~0xffffffUL;
	struct percpu_struct *cpu;
	int pal_rev, pal_var;

	cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset);
	pal_rev = cpu->pal_revision & 0xffff;
	pal_var = (cpu->pal_revision >> 16) & 0xff;

	/* Consoles earlier than A5.6-18 (OSF PALcode v1.62-2) set up
	   the CPU incorrectly (leave speculative stores enabled),
	   which causes memory corruption under certain conditions.
	   Issue a warning for such consoles. */
	if (alpha_using_srm &&
	    (pal_rev < 0x13e ||	(pal_rev == 0x13e && pal_var < 2)))
		printk(KERN_WARNING "WARNING! Upgrade to SRM A5.6-19 "
				    "or later\n");

	if (pci_mem > IRONGATE_3GB)
		pci_mem = IRONGATE_3GB;
	IRONGATE0->pci_mem = pci_mem;
	alpha_mv.min_mem_address = pci_mem;
	if (memtop > pci_mem) {
#ifdef CONFIG_BLK_DEV_INITRD
		extern unsigned long initrd_start, initrd_end;
		extern void *move_initrd(unsigned long);

		/* Move the initrd out of the way. */
		if (initrd_end && __pa(initrd_end) > pci_mem) {
			unsigned long size;

			size = initrd_end - initrd_start;
			memblock_free((void *)initrd_start, PAGE_ALIGN(size));
			if (!move_initrd(pci_mem))
				printk("irongate_init_arch: initrd too big "
				       "(%ldK)\ndisabling initrd\n",
				       size / 1024);
		}
#endif
		memblock_reserve(pci_mem, memtop - pci_mem);
		printk("irongate_init_arch: temporarily reserving "
			"region %08lx-%08lx for PCI\n", pci_mem, memtop - 1);
	}
}

static void __init
irongate_setup_agp(void)
{
	/* Disable the GART window. AGPGART doesn't work due to yet
	   unresolved memory coherency issues... */
	IRONGATE0->agpva = IRONGATE0->agpva & ~0xf;
	alpha_agpgart_size = 0;
}

void __init
irongate_init_arch(void)
{
	struct pci_controller *hose;
	int amd761 = (IRONGATE0->dev_vendor >> 16) > 0x7006;	/* Albacore? */

	IronECC = amd761 ? &IRONGATE0->bacsr54_eccms761 : &IRONGATE0->dramms;

	irongate_pci_clr_err();

	if (amd761)
		albacore_init_arch();

	irongate_setup_agp();

	/*
	 * Create our single hose.
	 */

	pci_isa_hose = hose = alloc_pci_controller();
	hose->io_space = &ioport_resource;
	hose->mem_space = &iomem_resource;
	hose->index = 0;

	/* 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
	  = (IRONGATE_MEM & 0xffffffffffUL) | 0x80000000000UL;
	hose->dense_io_base
	  = (IRONGATE_IO & 0xffffffffffUL) | 0x80000000000UL;

	hose->sg_isa = hose->sg_pci = NULL;
	__direct_map_base = 0;
	__direct_map_size = 0xffffffff;
}

/*
 * IO map and AGP support
 */
#include <linux/vmalloc.h>
#include <linux/agp_backend.h>
#include <linux/agpgart.h>
#include <linux/export.h>

#define GET_PAGE_DIR_OFF(addr) (addr >> 22)
#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr))

#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12)
#define GET_GATT(addr) (gatt_pages[GET_PAGE_DIR_IDX(addr)])

void __iomem *
irongate_ioremap(unsigned long addr, unsigned long size)
{
	struct vm_struct *area;
	unsigned long vaddr;
	unsigned long baddr, last;
	u32 *mmio_regs, *gatt_pages, *cur_gatt, pte;
	unsigned long gart_bus_addr;

	if (!alpha_agpgart_size)
		return (void __iomem *)(addr + IRONGATE_MEM);

	gart_bus_addr = (unsigned long)IRONGATE0->bar0 &
			PCI_BASE_ADDRESS_MEM_MASK;

	/*
	 * Check for within the AGP aperture...
	 */
	do {
		/*
		 * Check the AGP area
		 */
		if (addr >= gart_bus_addr && addr + size - 1 <
		    gart_bus_addr + alpha_agpgart_size)
			break;

		/*
		 * Not found - assume legacy ioremap
		 */
		return (void __iomem *)(addr + IRONGATE_MEM);
	} while(0);

	mmio_regs = (u32 *)(((unsigned long)IRONGATE0->bar1 &
			PCI_BASE_ADDRESS_MEM_MASK) + IRONGATE_MEM);

	gatt_pages = (u32 *)(phys_to_virt(mmio_regs[1])); /* FIXME */

	/*
	 * Adjust the limits (mappings must be page aligned)
	 */
	if (addr & ~PAGE_MASK) {
		printk("AGP ioremap failed... addr not page aligned (0x%lx)\n",
		       addr);
		return (void __iomem *)(addr + IRONGATE_MEM);
	}
	last = addr + size - 1;
	size = PAGE_ALIGN(last) - addr;

#if 0
	printk("irongate_ioremap(0x%lx, 0x%lx)\n", addr, size);
	printk("irongate_ioremap:  gart_bus_addr  0x%lx\n", gart_bus_addr);
	printk("irongate_ioremap:  gart_aper_size 0x%lx\n", gart_aper_size);
	printk("irongate_ioremap:  mmio_regs      %p\n", mmio_regs);
	printk("irongate_ioremap:  gatt_pages     %p\n", gatt_pages);

	for(baddr = addr; baddr <= last; baddr += PAGE_SIZE)
	{
		cur_gatt = phys_to_virt(GET_GATT(baddr) & ~1);
		pte = cur_gatt[GET_GATT_OFF(baddr)] & ~1;
		printk("irongate_ioremap:  cur_gatt %p pte 0x%x\n",
		       cur_gatt, pte);
	}
#endif

	/*
	 * Map it
	 */
	area = get_vm_area(size, VM_IOREMAP);
	if (!area) return NULL;

	for(baddr = addr, vaddr = (unsigned long)area->addr;
	    baddr <= last;
	    baddr += PAGE_SIZE, vaddr += PAGE_SIZE)
	{
		cur_gatt = phys_to_virt(GET_GATT(baddr) & ~1);
		pte = cur_gatt[GET_GATT_OFF(baddr)] & ~1;

		if (__alpha_remap_area_pages(vaddr,
					     pte, PAGE_SIZE, 0)) {
			printk("AGP ioremap: FAILED to map...\n");
			vfree(area->addr);
			return NULL;
		}
	}

	flush_tlb_all();

	vaddr = (unsigned long)area->addr + (addr & ~PAGE_MASK);
#if 0
	printk("irongate_ioremap(0x%lx, 0x%lx) returning 0x%lx\n",
	       addr, size, vaddr);
#endif
	return (void __iomem *)vaddr;
}
EXPORT_SYMBOL(irongate_ioremap);

void
irongate_iounmap(volatile void __iomem *xaddr)
{
	unsigned long addr = (unsigned long) xaddr;
	if (((long)addr >> 41) == -2)
		return;	/* kseg map, nothing to do */
	if (addr)
		return vfree((void *)(PAGE_MASK & addr));
}
EXPORT_SYMBOL(irongate_iounmap);


code:
// SPDX-License-Identifier: GPL-2.0
/*
 *	linux/arch/alpha/kernel/core_lca.c
 *
 * Written by David Mosberger (davidm@cs.arizona.edu) with some code
 * taken from Dave Rusling's (david.rusling@reo.mts.dec.com) 32-bit
 * bios code.
 *
 * Code common to all LCA core logic chips.
 */

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

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

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

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

/*
 * BIOS32-style PCI interface:
 */

/*
 * Machine check reasons.  Defined according to PALcode sources
 * (osf.h and platform.h).
 */
#define MCHK_K_TPERR		0x0080
#define MCHK_K_TCPERR		0x0082
#define MCHK_K_HERR		0x0084
#define MCHK_K_ECC_C		0x0086
#define MCHK_K_ECC_NC		0x0088
#define MCHK_K_UNKNOWN		0x008A
#define MCHK_K_CACKSOFT		0x008C
#define MCHK_K_BUGCHECK		0x008E
#define MCHK_K_OS_BUGCHECK	0x0090
#define MCHK_K_DCPERR		0x0092
#define MCHK_K_ICPERR		0x0094

/*
 * Platform-specific machine-check reasons:
 */
#define MCHK_K_SIO_SERR		0x204	/* all platforms so far */
#define MCHK_K_SIO_IOCHK	0x206	/* all platforms so far */
#define MCHK_K_DCSR		0x208	/* all but Noname */

/*
 * Given a bus, device, and function number, compute resulting
 * configuration space address and setup the LCA_IOC_CONF register
 * 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.
 *
 * Type 0:
 *
 *  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
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * | | | | | | | | | | | | | | | | | | | | | | | |F|F|F|R|R|R|R|R|R|0|0|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 *	31:11	Device select bit.
 * 	10:8	Function number
 * 	 7:2	Register 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 long addr;
	u8 bus = pbus->number;

	if (bus == 0) {
		int device = device_fn >> 3;
		int func = device_fn & 0x7;

		/* Type 0 configuration cycle.  */

		if (device > 12) {
			return -1;
		}

		*(vulp)LCA_IOC_CONF = 0;
		addr = (1 << (11 + device)) | (func << 8) | where;
	} else {
		/* Type 1 configuration cycle.  */
		*(vulp)LCA_IOC_CONF = 1;
		addr = (bus << 16) | (device_fn << 8) | where;
	}
	*pci_addr = addr;
	return 0;
}

static unsigned int
conf_read(unsigned long addr)
{
	unsigned long flags, code, stat0;
	unsigned int value;

	local_irq_save(flags);

	/* Reset status register to avoid losing errors.  */
	stat0 = *(vulp)LCA_IOC_STAT0;
	*(vulp)LCA_IOC_STAT0 = stat0;
	mb();

	/* Access configuration space.  */
	value = *(vuip)addr;
	draina();

	stat0 = *(vulp)LCA_IOC_STAT0;
	if (stat0 & LCA_IOC_STAT0_ERR) {
		code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT)
			& LCA_IOC_STAT0_CODE_MASK);
		if (code != 1) {
			printk("lca.c:conf_read: got stat0=%lx\n", stat0);
		}

		/* Reset error status.  */
		*(vulp)LCA_IOC_STAT0 = stat0;
		mb();

		/* Reset machine check.  */
		wrmces(0x7);

		value = 0xffffffff;
	}
	local_irq_restore(flags);
	return value;
}

static void
conf_write(unsigned long addr, unsigned int value)
{
	unsigned long flags, code, stat0;

	local_irq_save(flags);	/* avoid getting hit by machine check */

	/* Reset status register to avoid losing errors.  */
	stat0 = *(vulp)LCA_IOC_STAT0;
	*(vulp)LCA_IOC_STAT0 = stat0;
	mb();

	/* Access configuration space.  */
	*(vuip)addr = value;
	draina();

	stat0 = *(vulp)LCA_IOC_STAT0;
	if (stat0 & LCA_IOC_STAT0_ERR) {
		code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT)
			& LCA_IOC_STAT0_CODE_MASK);
		if (code != 1) {
			printk("lca.c:conf_write: got stat0=%lx\n", stat0);
		}

		/* Reset error status.  */
		*(vulp)LCA_IOC_STAT0 = stat0;
		mb();

		/* Reset machine check. */
		wrmces(0x7);
	}
	local_irq_restore(flags);
}

static int
lca_read_config(struct pci_bus *bus, unsigned int devfn, int where,
		int size, u32 *value)
{
	unsigned long addr, pci_addr;
	long mask;
	int shift;

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

	shift = (where & 3) * 8;
	mask = (size - 1) * 8;
	addr = (pci_addr << 5) + mask + LCA_CONF;
	*value = conf_read(addr) >> (shift);
	return PCIBIOS_SUCCESSFUL;
}

static int
lca_write_config(struct pci_bus *bus, unsigned int devfn, int where, int size,
		 u32 value)
{
	unsigned long addr, pci_addr;
	long mask;

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

	mask = (size - 1) * 8;
	addr = (pci_addr << 5) + mask + LCA_CONF;
	conf_write(addr, value << ((where & 3) * 8));
	return PCIBIOS_SUCCESSFUL;
}

struct pci_ops lca_pci_ops =
{
	.read =		lca_read_config,
	.write =	lca_write_config,
};

void
lca_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end)
{
	wmb();
	*(vulp)LCA_IOC_TBIA = 0;
	mb();
}

void __init
lca_init_arch(void)
{
	struct pci_controller *hose;

	/*
	 * Create our single hose.
	 */

	pci_isa_hose = hose = alloc_pci_controller();
	hose->io_space = &ioport_resource;
	hose->mem_space = &iomem_resource;
	hose->index = 0;

	hose->sparse_mem_base = LCA_SPARSE_MEM - IDENT_ADDR;
	hose->dense_mem_base = LCA_DENSE_MEM - IDENT_ADDR;
	hose->sparse_io_base = LCA_IO - IDENT_ADDR;
	hose->dense_io_base = 0;

	/*
	 * Set up the PCI to main memory translation windows.
	 *
	 * Mimic the SRM settings for the direct-map window.
	 *   Window 0 is scatter-gather 8MB at 8MB (for isa).
	 *   Window 1 is direct access 1GB at 1GB.
	 *
	 * Note that we do not try to save any of the DMA window CSRs
	 * before setting them, since we cannot read those CSRs on LCA.
	 */
	hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000,
				       SMP_CACHE_BYTES);
	hose->sg_pci = NULL;
	__direct_map_base = 0x40000000;
	__direct_map_size = 0x40000000;

	*(vulp)LCA_IOC_W_BASE0 = hose->sg_isa->dma_base | (3UL << 32);
	*(vulp)LCA_IOC_W_MASK0 = (hose->sg_isa->size - 1) & 0xfff00000;
	*(vulp)LCA_IOC_T_BASE0 = virt_to_phys(hose->sg_isa->ptes);

	*(vulp)LCA_IOC_W_BASE1 = __direct_map_base | (2UL << 32);
	*(vulp)LCA_IOC_W_MASK1 = (__direct_map_size - 1) & 0xfff00000;
	*(vulp)LCA_IOC_T_BASE1 = 0;

	*(vulp)LCA_IOC_TB_ENA = 0x80;

	lca_pci_tbi(hose, 0, -1);

	/*
	 * Disable PCI parity for now.  The NCR53c810 chip has
	 * troubles meeting the PCI spec which results in
	 * data parity errors.
	 */
	*(vulp)LCA_IOC_PAR_DIS = 1UL<<5;

	/*
	 * Finally, set up for restoring the correct HAE if using SRM.
	 * Again, since we cannot read many of the CSRs on the LCA,
	 * one of which happens to be the HAE, we save the value that
	 * the SRM will expect...
	 */
	if (alpha_using_srm)
		srm_hae = 0x80000000UL;
}

/*
 * Constants used during machine-check handling.  I suppose these
 * could be moved into lca.h but I don't see much reason why anybody
 * else would want to use them.
 */

#define ESR_EAV		(1UL<< 0)	/* error address valid */
#define ESR_CEE		(1UL<< 1)	/* correctable error */
#define ESR_UEE		(1UL<< 2)	/* uncorrectable error */
#define ESR_WRE		(1UL<< 3)	/* write-error */
#define ESR_SOR		(1UL<< 4)	/* error source */
#define ESR_CTE		(1UL<< 7)	/* cache-tag error */
#define ESR_MSE		(1UL<< 9)	/* multiple soft errors */
#define ESR_MHE		(1UL<<10)	/* multiple hard errors */
#define ESR_NXM		(1UL<<12)	/* non-existent memory */

#define IOC_ERR		(  1<<4)	/* ioc logs an error */
#define IOC_CMD_SHIFT	0
#define IOC_CMD		(0xf<<IOC_CMD_SHIFT)
#define IOC_CODE_SHIFT	8
#define IOC_CODE	(0xf<<IOC_CODE_SHIFT)
#define IOC_LOST	(  1<<5)
#define IOC_P_NBR	((__u32) ~((1<<13) - 1))

static void
mem_error(unsigned long esr, unsigned long ear)
{
	printk("    %s %s error to %s occurred at address %x\n",
	       ((esr & ESR_CEE) ? "Correctable" :
		(esr & ESR_UEE) ? "Uncorrectable" : "A"),
	       (esr & ESR_WRE) ? "write" : "read",
	       (esr & ESR_SOR) ? "memory" : "b-cache",
	       (unsigned) (ear & 0x1ffffff8));
	if (esr & ESR_CTE) {
		printk("    A b-cache tag parity error was detected.\n");
	}
	if (esr & ESR_MSE) {
		printk("    Several other correctable errors occurred.\n");
	}
	if (esr & ESR_MHE) {
		printk("    Several other uncorrectable errors occurred.\n");
	}
	if (esr & ESR_NXM) {
		printk("    Attempted to access non-existent memory.\n");
	}
}

static void
ioc_error(__u32 stat0, __u32 stat1)
{
	static const char * const pci_cmd[] = {
		"Interrupt Acknowledge", "Special", "I/O Read", "I/O Write",
		"Rsvd 1", "Rsvd 2", "Memory Read", "Memory Write", "Rsvd3",
		"Rsvd4", "Configuration Read", "Configuration Write",
		"Memory Read Multiple", "Dual Address", "Memory Read Line",
		"Memory Write and Invalidate"
	};
	static const char * const err_name[] = {
		"exceeded retry limit", "no device", "bad data parity",
		"target abort", "bad address parity", "page table read error",
		"invalid page", "data error"
	};
	unsigned code = (stat0 & IOC_CODE) >> IOC_CODE_SHIFT;
	unsigned cmd  = (stat0 & IOC_CMD)  >> IOC_CMD_SHIFT;

	printk("    %s initiated PCI %s cycle to address %x"
	       " failed due to %s.\n",
	       code > 3 ? "PCI" : "CPU", pci_cmd[cmd], stat1, err_name[code]);

	if (code == 5 || code == 6) {
		printk("    (Error occurred at PCI memory address %x.)\n",
		       (stat0 & ~IOC_P_NBR));
	}
	if (stat0 & IOC_LOST) {
		printk("    Other PCI errors occurred simultaneously.\n");
	}
}

void
lca_machine_check(unsigned long vector, unsigned long la_ptr)
{
	const char * reason;
	union el_lca el;

	el.c = (struct el_common *) la_ptr;

	wrmces(rdmces());	/* reset machine check pending flag */

	printk(KERN_CRIT "LCA machine check: vector=%#lx pc=%#lx code=%#x\n",
	       vector, get_irq_regs()->pc, (unsigned int) el.c->code);

	/*
	 * The first quadword after the common header always seems to
	 * be the machine check reason---don't know why this isn't
	 * part of the common header instead.  In the case of a long
	 * logout frame, the upper 32 bits is the machine check
	 * revision level, which we ignore for now.
	 */
	switch ((unsigned int) el.c->code) {
	case MCHK_K_TPERR:	reason = "tag parity error"; break;
	case MCHK_K_TCPERR:	reason = "tag control parity error"; break;
	case MCHK_K_HERR:	reason = "access to non-existent memory"; break;
	case MCHK_K_ECC_C:	reason = "correctable ECC error"; break;
	case MCHK_K_ECC_NC:	reason = "non-correctable ECC error"; break;
	case MCHK_K_CACKSOFT:	reason = "MCHK_K_CACKSOFT"; break;
	case MCHK_K_BUGCHECK:	reason = "illegal exception in PAL mode"; break;
	case MCHK_K_OS_BUGCHECK: reason = "callsys in kernel mode"; break;
	case MCHK_K_DCPERR:	reason = "d-cache parity error"; break;
	case MCHK_K_ICPERR:	reason = "i-cache parity error"; break;
	case MCHK_K_SIO_SERR:	reason = "SIO SERR occurred on PCI bus"; break;
	case MCHK_K_SIO_IOCHK:	reason = "SIO IOCHK occurred on ISA bus"; break;
	case MCHK_K_DCSR:	reason = "MCHK_K_DCSR"; break;
	case MCHK_K_UNKNOWN:
	default:		reason = "unknown"; break;
	}

	switch (el.c->size) {
	case sizeof(struct el_lca_mcheck_short):
		printk(KERN_CRIT
		       "  Reason: %s (short frame%s, dc_stat=%#lx):\n",
		       reason, el.c->retry ? ", retryable" : "",
		       el.s->dc_stat);
		if (el.s->esr & ESR_EAV) {
			mem_error(el.s->esr, el.s->ear);
		}
		if (el.s->ioc_stat0 & IOC_ERR) {
			ioc_error(el.s->ioc_stat0, el.s->ioc_stat1);
		}
		break;

	case sizeof(struct el_lca_mcheck_long):
		printk(KERN_CRIT "  Reason: %s (long frame%s):\n",
		       reason, el.c->retry ? ", retryable" : "");
		printk(KERN_CRIT
		       "    reason: %#lx  exc_addr: %#lx  dc_stat: %#lx\n",
		       el.l->pt[0], el.l->exc_addr, el.l->dc_stat);
		printk(KERN_CRIT "    car: %#lx\n", el.l->car);
		if (el.l->esr & ESR_EAV) {
			mem_error(el.l->esr, el.l->ear);
		}
		if (el.l->ioc_stat0 & IOC_ERR) {
			ioc_error(el.l->ioc_stat0, el.l->ioc_stat1);
		}
		break;

	default:
		printk(KERN_CRIT "  Unknown errorlog size %d\n", el.c->size);
	}

	/* Dump the logout area to give all info.  */
#ifdef CONFIG_VERBOSE_MCHECK
	if (alpha_verbose_mcheck > 1) {
		unsigned long * ptr = (unsigned long *) la_ptr;
		long i;
		for (i = 0; i < el.c->size / sizeof(long); i += 2) {
			printk(KERN_CRIT " +%8lx %016lx %016lx\n",
			       i*sizeof(long), ptr[i], ptr[i+1]);
		}
	}
#endif /* CONFIG_VERBOSE_MCHECK */
}

/*
 * The following routines are needed to support the SPEED changing
 * necessary to successfully manage the thermal problem on the AlphaBook1.
 */

void
lca_clock_print(void)
{
        long    pmr_reg;

        pmr_reg = LCA_READ_PMR;

        printk("Status of clock control:\n");
        printk("\tPrimary clock divisor\t0x%lx\n", LCA_GET_PRIMARY(pmr_reg));
        printk("\tOverride clock divisor\t0x%lx\n", LCA_GET_OVERRIDE(pmr_reg));
        printk("\tInterrupt override is %s\n",
	       (pmr_reg & LCA_PMR_INTO) ? "on" : "off");
        printk("\tDMA override is %s\n",
	       (pmr_reg & LCA_PMR_DMAO) ? "on" : "off");

}

int
lca_get_clock(void)
{
        long    pmr_reg;

        pmr_reg = LCA_READ_PMR;
        return(LCA_GET_PRIMARY(pmr_reg));

}

void
lca_clock_fiddle(int divisor)
{
        long    pmr_reg;

        pmr_reg = LCA_READ_PMR;
        LCA_SET_PRIMARY_CLOCK(pmr_reg, divisor);
	/* lca_norm_clock = divisor; */
        LCA_WRITE_PMR(pmr_reg);
        mb();
}

repo_name: angery/linux


code:
// SPDX-License-Identifier: GPL-2.0
/*
 *	linux/arch/alpha/kernel/core_marvel.c
 *
 * Code common to all Marvel based systems.
 */

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

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

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

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

/*
 * Debug helpers
 */
#define DEBUG_CONFIG 0

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

/*
 * Private data
 */
static struct io7 *io7_head = NULL;

/*
 * Helper functions
 */
static unsigned long __attribute__ ((unused))
read_ev7_csr(int pe, unsigned long offset)
{
	ev7_csr *ev7csr = EV7_CSR_KERN(pe, offset);
	unsigned long q;

	mb();
	q = ev7csr->csr;
	mb();

	return q;
}

static void __attribute__ ((unused))
write_ev7_csr(int pe, unsigned long offset, unsigned long q)
{
	ev7_csr *ev7csr = EV7_CSR_KERN(pe, offset);

	mb();
	ev7csr->csr = q;
	mb();
}

static char * __init
mk_resource_name(int pe, int port, char *str)
{
	char tmp[80];
	char *name;

	sprintf(tmp, "PCI %s PE %d PORT %d", str, pe, port);
	name = memblock_alloc(strlen(tmp) + 1, SMP_CACHE_BYTES);
	if (!name)
		panic("%s: Failed to allocate %zu bytes\n", __func__,
		      strlen(tmp) + 1);
	strcpy(name, tmp);

	return name;
}

inline struct io7 *
marvel_next_io7(struct io7 *prev)
{
	return (prev ? prev->next : io7_head);
}

struct io7 *
marvel_find_io7(int pe)
{
	struct io7 *io7;

	for (io7 = io7_head; io7 && io7->pe != pe; io7 = io7->next)
		continue;

	return io7;
}

static struct io7 * __init
alloc_io7(unsigned int pe)
{
	struct io7 *io7;
	struct io7 *insp;
	int h;

	if (marvel_find_io7(pe)) {
		printk(KERN_WARNING "IO7 at PE %d already allocated!\n", pe);
		return NULL;
	}

	io7 = memblock_alloc(sizeof(*io7), SMP_CACHE_BYTES);
	if (!io7)
		panic("%s: Failed to allocate %zu bytes\n", __func__,
		      sizeof(*io7));
	io7->pe = pe;
	raw_spin_lock_init(&io7->irq_lock);

	for (h = 0; h < 4; h++) {
		io7->ports[h].io7 = io7;
		io7->ports[h].port = h;
		io7->ports[h].enabled = 0; /* default to disabled */
	}

	/*
	 * Insert in pe sorted order.
	 */
	if (NULL == io7_head)			/* empty list */
		io7_head = io7;
	else if (io7_head->pe > io7->pe) {	/* insert at head */
		io7->next = io7_head;
		io7_head = io7;
	} else {				/* insert at position */
		for (insp = io7_head; insp; insp = insp->next) {
			if (insp->pe == io7->pe) {
				printk(KERN_ERR "Too many IO7s at PE %d\n",
				       io7->pe);
				return NULL;
			}

			if (NULL == insp->next ||
			    insp->next->pe > io7->pe) { /* insert here */
				io7->next = insp->next;
				insp->next = io7;
				break;
			}
		}

		if (NULL == insp) { /* couldn't insert ?!? */
			printk(KERN_WARNING "Failed to insert IO7 at PE %d "
			       " - adding at head of list\n", io7->pe);
			io7->next = io7_head;
			io7_head = io7;
		}
	}

	return io7;
}

void
io7_clear_errors(struct io7 *io7)
{
	io7_port7_csrs *p7csrs;
	io7_ioport_csrs *csrs;
	int port;

	/*
	 * First the IO ports.
	 */
	for (port = 0; port < 4; port++) {
		csrs = IO7_CSRS_KERN(io7->pe, port);

		csrs->POx_ERR_SUM.csr = -1UL;
		csrs->POx_TLB_ERR.csr = -1UL;
		csrs->POx_SPL_COMPLT.csr = -1UL;
		csrs->POx_TRANS_SUM.csr = -1UL;
	}

	/*
	 * Then the common ones.
	 */
	p7csrs = IO7_PORT7_CSRS_KERN(io7->pe);

	p7csrs->PO7_ERROR_SUM.csr = -1UL;
	p7csrs->PO7_UNCRR_SYM.csr = -1UL;
	p7csrs->PO7_CRRCT_SYM.csr = -1UL;
}

/*
 * IO7 PCI, PCI/X, AGP configuration.
 */
static void __init
io7_init_hose(struct io7 *io7, int port)
{
	static int hose_index = 0;

	struct pci_controller *hose = alloc_pci_controller();
	struct io7_port *io7_port = &io7->ports[port];
	io7_ioport_csrs *csrs = IO7_CSRS_KERN(io7->pe, port);
	int i;

	hose->index = hose_index++;	/* arbitrary */

	/*
	 * We don't have an isa or legacy hose, but glibc expects to be
	 * able to use the bus == 0 / dev == 0 form of the iobase syscall
	 * to determine information about the i/o system. Since XFree86
	 * relies on glibc's determination to tell whether or not to use
	 * sparse access, we need to point the pci_isa_hose at a real hose
	 * so at least that determination is correct.
	 */
	if (hose->index == 0)
		pci_isa_hose = hose;

	io7_port->csrs = csrs;
	io7_port->hose = hose;
	hose->sysdata = io7_port;

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

	/*
	 * Base addresses for userland consumption. Since these are going
	 * to be mapped, they are pure physical addresses.
	 */
	hose->sparse_mem_base = hose->sparse_io_base = 0;
	hose->dense_mem_base = IO7_MEM_PHYS(io7->pe, port);
	hose->dense_io_base = IO7_IO_PHYS(io7->pe, port);

	/*
	 * Base addresses and resource ranges for kernel consumption.
	 */
	hose->config_space_base = (unsigned long)IO7_CONF_KERN(io7->pe, port);

	hose->io_space->start = (unsigned long)IO7_IO_KERN(io7->pe, port);
	hose->io_space->end = hose->io_space->start + IO7_IO_SPACE - 1;
	hose->io_space->name = mk_resource_name(io7->pe, port, "IO");
	hose->io_space->flags = IORESOURCE_IO;

	hose->mem_space->start = (unsigned long)IO7_MEM_KERN(io7->pe, port);
	hose->mem_space->end = hose->mem_space->start + IO7_MEM_SPACE - 1;
	hose->mem_space->name = mk_resource_name(io7->pe, port, "MEM");
	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",
		       hose->index);
	if (request_resource(&iomem_resource, hose->mem_space) < 0)
		printk(KERN_ERR "Failed to request MEM on hose %d\n",
		       hose->index);

	/*
	 * Save the existing DMA window settings for later restoration.
	 */
	for (i = 0; i < 4; i++) {
		io7_port->saved_wbase[i] = csrs->POx_WBASE[i].csr;
		io7_port->saved_wmask[i] = csrs->POx_WMASK[i].csr;
		io7_port->saved_tbase[i] = csrs->POx_TBASE[i].csr;
	}

	/*
	 * Set up the PCI to main memory translation windows.
	 *
	 * Window 0 is scatter-gather 8MB at 8MB
	 * Window 1 is direct access 1GB at 2GB
	 * Window 2 is scatter-gather (up-to) 1GB at 3GB
	 * Window 3 is disabled
	 */

	/*
	 * TBIA before modifying windows.
	 */
	marvel_pci_tbi(hose, 0, -1);

	/*
	 * Set up window 0 for scatter-gather 8MB at 8MB.
	 */
	hose->sg_isa = iommu_arena_new_node(0, hose, 0x00800000, 0x00800000, 0);
	hose->sg_isa->align_entry = 8;	/* cache line boundary */
	csrs->POx_WBASE[0].csr =
		hose->sg_isa->dma_base | wbase_m_ena | wbase_m_sg;
	csrs->POx_WMASK[0].csr = (hose->sg_isa->size - 1) & wbase_m_addr;
	csrs->POx_TBASE[0].csr = virt_to_phys(hose->sg_isa->ptes);

	/*
	 * Set up window 1 for direct-mapped 1GB at 2GB.
	 */
	csrs->POx_WBASE[1].csr = __direct_map_base | wbase_m_ena;
	csrs->POx_WMASK[1].csr = (__direct_map_size - 1) & wbase_m_addr;
	csrs->POx_TBASE[1].csr = 0;

	/*
	 * Set up window 2 for scatter-gather (up-to) 1GB at 3GB.
	 */
	hose->sg_pci = iommu_arena_new_node(0, hose, 0xc0000000, 0x40000000, 0);
	hose->sg_pci->align_entry = 8;	/* cache line boundary */
	csrs->POx_WBASE[2].csr =
		hose->sg_pci->dma_base | wbase_m_ena | wbase_m_sg;
	csrs->POx_WMASK[2].csr = (hose->sg_pci->size - 1) & wbase_m_addr;
	csrs->POx_TBASE[2].csr = virt_to_phys(hose->sg_pci->ptes);

	/*
	 * Disable window 3.
	 */
	csrs->POx_WBASE[3].csr = 0;

	/*
	 * Make sure that the AGP Monster Window is disabled.
	 */
	csrs->POx_CTRL.csr &= ~(1UL << 61);

#if 1
	printk("FIXME: disabling master aborts\n");
	csrs->POx_MSK_HEI.csr &= ~(3UL << 14);
#endif
	/*
	 * TBIA after modifying windows.
	 */
	marvel_pci_tbi(hose, 0, -1);
}

static void __init
marvel_init_io7(struct io7 *io7)
{
	int i;

	printk("Initializing IO7 at PID %d\n", io7->pe);

	/*
	 * Get the Port 7 CSR pointer.
	 */
	io7->csrs = IO7_PORT7_CSRS_KERN(io7->pe);

	/*
	 * Init this IO7's hoses.
	 */
	for (i = 0; i < IO7_NUM_PORTS; i++) {
		io7_ioport_csrs *csrs = IO7_CSRS_KERN(io7->pe, i);
		if (csrs->POx_CACHE_CTL.csr == 8) {
			io7->ports[i].enabled = 1;
			io7_init_hose(io7, i);
		}
	}
}

void __init
marvel_io7_present(gct6_node *node)
{
	int pe;

	if (node->type != GCT_TYPE_HOSE ||
	    node->subtype != GCT_SUBTYPE_IO_PORT_MODULE)
		return;

	pe = (node->id >> 8) & 0xff;
	printk("Found an IO7 at PID %d\n", pe);

	alloc_io7(pe);
}

static void __init
marvel_find_console_vga_hose(void)
{
#ifdef CONFIG_VGA_HOSE
	u64 *pu64 = (u64 *)((u64)hwrpb + hwrpb->ctbt_offset);

	if (pu64[7] == 3) {	/* TERM_TYPE == graphics */
		struct pci_controller *hose = NULL;
		int h = (pu64[30] >> 24) & 0xff; /* TERM_OUT_LOC, hose # */
		struct io7 *io7;
		int pid, port;

		/* FIXME - encoding is going to have to change for Marvel
		 *         since hose will be able to overflow a byte...
		 *         need to fix this decode when the console
		 *         changes its encoding
		 */
		printk("console graphics is on hose %d (console)\n", h);

		/*
		 * The console's hose numbering is:
		 *
		 *	hose<n:2>: PID
		 *	hose<1:0>: PORT
		 *
		 * We need to find the hose at that pid and port
		 */
		pid = h >> 2;
		port = h & 3;
		if ((io7 = marvel_find_io7(pid)))
			hose = io7->ports[port].hose;

		if (hose) {
			printk("Console graphics on hose %d\n", hose->index);
			pci_vga_hose = hose;
		}
	}
#endif
}

gct6_search_struct gct_wanted_node_list[] __initdata = {
	{ GCT_TYPE_HOSE, GCT_SUBTYPE_IO_PORT_MODULE, marvel_io7_present },
	{ 0, 0, NULL }
};

/*
 * In case the GCT is not complete, let the user specify PIDs with IO7s
 * at boot time. Syntax is 'io7=a,b,c,...,n' where a-n are the PIDs (decimal)
 * where IO7s are connected
 */
static int __init
marvel_specify_io7(char *str)
{
	unsigned long pid;
	struct io7 *io7;
	char *pchar;

	do {
		pid = simple_strtoul(str, &pchar, 0);
		if (pchar != str) {
			printk("User-specified IO7 at PID %lu\n", pid);
			io7 = alloc_io7(pid);
			if (io7) marvel_init_io7(io7);
		}

		if (pchar == str) pchar++;
		str = pchar;
	} while(*str);

	return 1;
}
__setup("io7=", marvel_specify_io7);

void __init
marvel_init_arch(void)
{
	struct io7 *io7;

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

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

	/* Parse the config tree.  */
	gct6_find_nodes(GCT_NODE_PTR(0), gct_wanted_node_list);

	/* Init the io7s.  */
	for (io7 = NULL; NULL != (io7 = marvel_next_io7(io7)); )
		marvel_init_io7(io7);

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

void
marvel_kill_arch(int mode)
{
}

/*
 * PCI Configuration Space access functions
 *
 * Configuration space addresses have the following format:
 *
 * 	|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
 * 	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * 	|B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|R|R|
 * 	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 *	 n:24	reserved for hose base
 *	23:16	bus number (8 bits = 128 possible buses)
 *	15:11	Device number (5 bits)
 *	10:8	function number
 *	 7:2	register number
 *
 * Notes:
 *	IO7 determines whether to use a type 0 or type 1 config cycle
 *	based on the bus number. Therefore the bus number must be set
 *	to 0 for the root bus on any hose.
 *
 *	The function number selects which function of a multi-function device
 *	(e.g., SCSI and Ethernet).
 *
 */

static inline unsigned long
build_conf_addr(struct pci_controller *hose, u8 bus,
		unsigned int devfn, int where)
{
	return (hose->config_space_base | (bus << 16) | (devfn << 8) | where);
}

static unsigned long
mk_conf_addr(struct pci_bus *pbus, unsigned int devfn, int where)
{
	struct pci_controller *hose = pbus->sysdata;
	struct io7_port *io7_port;
	unsigned long addr = 0;
	u8 bus = pbus->number;

	if (!hose)
		return addr;

	/* Check for enabled.  */
	io7_port = hose->sysdata;
	if (!io7_port->enabled)
		return addr;

	if (!pbus->parent) { /* No parent means peer PCI bus. */
		/* Don't support idsel > 20 on primary bus.  */
		if (devfn >= PCI_DEVFN(21, 0))
			return addr;
		bus = 0;
	}

	addr = build_conf_addr(hose, bus, devfn, where);

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

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

	if (0 == (addr = mk_conf_addr(bus, devfn, where)))
		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;
	default:
		return PCIBIOS_FUNC_NOT_SUPPORTED;
	}

	return PCIBIOS_SUCCESSFUL;
}

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

	if (0 == (addr = mk_conf_addr(bus, devfn, where)))
		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;
	default:
		return PCIBIOS_FUNC_NOT_SUPPORTED;
	}

	return PCIBIOS_SUCCESSFUL;
}

struct pci_ops marvel_pci_ops =
{
	.read =		marvel_read_config,
	.write = 	marvel_write_config,
};

/*
 * Other PCI helper functions.
 */
void
marvel_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end)
{
	io7_ioport_csrs *csrs = ((struct io7_port *)hose->sysdata)->csrs;

	wmb();
	csrs->POx_SG_TBIA.csr = 0;
	mb();
	csrs->POx_SG_TBIA.csr;
}

/*
 * RTC Support
 */
struct marvel_rtc_access_info {
	unsigned long function;
	unsigned long index;
	unsigned long data;
};

static void
__marvel_access_rtc(void *info)
{
	struct marvel_rtc_access_info *rtc_access = info;

	register unsigned long __r0 __asm__("$0");
	register unsigned long __r16 __asm__("$16") = rtc_access->function;
	register unsigned long __r17 __asm__("$17") = rtc_access->index;
	register unsigned long __r18 __asm__("$18") = rtc_access->data;

	__asm__ __volatile__(
		"call_pal %4 # cserve rtc"
		: "=r"(__r16), "=r"(__r17), "=r"(__r18), "=r"(__r0)
		: "i"(PAL_cserve), "0"(__r16), "1"(__r17), "2"(__r18)
		: "$1", "$22", "$23", "$24", "$25");

	rtc_access->data = __r0;
}

static u8
__marvel_rtc_io(u8 b, unsigned long addr, int write)
{
	static u8 index = 0;

	struct marvel_rtc_access_info rtc_access;
	u8 ret = 0;

	switch(addr) {
	case 0x70:					/* RTC_PORT(0) */
		if (write) index = b;
		ret = index;
		break;

	case 0x71:					/* RTC_PORT(1) */
		rtc_access.index = index;
		rtc_access.data = bcd2bin(b);
		rtc_access.function = 0x48 + !write;	/* GET/PUT_TOY */

		__marvel_access_rtc(&rtc_access);

		ret = bin2bcd(rtc_access.data);
		break;

	default:
		printk(KERN_WARNING "Illegal RTC port %lx\n", addr);
		break;
	}

	return ret;
}

/*
 * IO map support.
 */
void __iomem *
marvel_ioremap(unsigned long addr, unsigned long size)
{
	struct pci_controller *hose;
	unsigned long baddr, last;
	struct vm_struct *area;
	unsigned long vaddr;
	unsigned long *ptes;
	unsigned long pfn;

	/*
	 * Adjust the address.
	 */
	FIXUP_MEMADDR_VGA(addr);

	/*
	 * Find the hose.
	 */
	for (hose = hose_head; hose; hose = hose->next) {
		if ((addr >> 32) == (hose->mem_space->start >> 32))
			break;
	}
	if (!hose)
		return NULL;

	/*
	 * We have the hose - calculate the bus limits.
	 */
	baddr = addr - hose->mem_space->start;
	last = baddr + size - 1;

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

	/*
	 * 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)
			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 map...\n");
				vfree(area->addr);
				return NULL;
			}
		}

		flush_tlb_all();

		vaddr = (unsigned long)area->addr + (addr & ~PAGE_MASK);

		return (void __iomem *) vaddr;
	}

	/* Assume it was already a reasonable address */
	vaddr = baddr + hose->mem_space->start;
	return (void __iomem *) vaddr;
}

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

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

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

#define __marvel_is_port_kbd(a)	(((a) == 0x60) || ((a) == 0x64))
#define __marvel_is_port_rtc(a)	(((a) == 0x70) || ((a) == 0x71))

void __iomem *marvel_ioportmap (unsigned long addr)
{
	FIXUP_IOADDR_VGA(addr);
	return (void __iomem *)addr;
}

u8
marvel_ioread8(const void __iomem *xaddr)
{
	unsigned long addr = (unsigned long) xaddr;
	if (__marvel_is_port_kbd(addr))
		return 0;
	else if (__marvel_is_port_rtc(addr))
		return __marvel_rtc_io(0, addr, 0);
	else if (marvel_is_ioaddr(addr))
		return __kernel_ldbu(*(vucp)addr);
	else
		/* this should catch other legacy addresses
		   that would normally fail on MARVEL,
		   because there really is nothing there...
		*/
		return ~0;
}

void
marvel_iowrite8(u8 b, void __iomem *xaddr)
{
	unsigned long addr = (unsigned long) xaddr;
	if (__marvel_is_port_kbd(addr))
		return;
	else if (__marvel_is_port_rtc(addr))
		__marvel_rtc_io(b, addr, 1);
	else if (marvel_is_ioaddr(addr))
		__kernel_stb(b, *(vucp)addr);
}

#ifndef CONFIG_ALPHA_GENERIC
EXPORT_SYMBOL(marvel_ioremap);
EXPORT_SYMBOL(marvel_iounmap);
EXPORT_SYMBOL(marvel_is_mmio);
EXPORT_SYMBOL(marvel_ioportmap);
EXPORT_SYMBOL(marvel_ioread8);
EXPORT_SYMBOL(marvel_iowrite8);
#endif

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

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

static int
marvel_agp_setup(alpha_agp_info *agp)
{
	struct marvel_agp_aperture *aper;

	if (!alpha_agpgart_size)
		return -ENOMEM;

	aper = kmalloc(sizeof(*aper), 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
marvel_agp_cleanup(alpha_agp_info *agp)
{
	struct marvel_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
marvel_agp_configure(alpha_agp_info *agp)
{
	io7_ioport_csrs *csrs = ((struct io7_port *)agp->hose->sysdata)->csrs;
	struct io7 *io7 = ((struct io7_port *)agp->hose->sysdata)->io7;
	unsigned int new_rate = 0;
	unsigned long agp_pll;

	/*
	 * Check the requested mode against the PLL setting.
	 * The agpgart_be code has not programmed the card yet,
	 * so we can still tweak mode here.
	 */
	agp_pll = io7->csrs->POx_RST[IO7_AGP_PORT].csr;
	switch(IO7_PLL_RNGB(agp_pll)) {
	case 0x4:				/* 2x only */
		/*
		 * The PLL is only programmed for 2x, so adjust the
		 * rate to 2x, if necessary.
		 */
		if (agp->mode.bits.rate != 2)
			new_rate = 2;
		break;

	case 0x6:				/* 1x / 4x */
		/*
		 * The PLL is programmed for 1x or 4x.  Don't go faster
		 * than requested, so if the requested rate is 2x, use 1x.
		 */
		if (agp->mode.bits.rate == 2)
			new_rate = 1;
		break;

	default:				/* ??????? */
		/*
		 * Don't know what this PLL setting is, take the requested
		 * rate, but warn the user.
		 */
		printk("%s: unknown PLL setting RNGB=%lx (PLL6_CTL=%016lx)\n",
		       __func__, IO7_PLL_RNGB(agp_pll), agp_pll);
		break;
	}

	/*
	 * Set the new rate, if necessary.
	 */
	if (new_rate) {
		printk("Requested AGP Rate %dX not compatible "
		       "with PLL setting - using %dX\n",
		       agp->mode.bits.rate,
		       new_rate);

		agp->mode.bits.rate = new_rate;
	}

	printk("Enabling AGP on hose %d: %dX%s RQ %d\n",
	       agp->hose->index, agp->mode.bits.rate,
	       agp->mode.bits.sba ? " - SBA" : "", agp->mode.bits.rq);

	csrs->AGP_CMD.csr = agp->mode.lw;

	return 0;
}

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

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

static unsigned long
marvel_agp_translate(alpha_agp_info *agp, dma_addr_t addr)
{
	struct marvel_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 marvel_agp_ops =
{
	.setup		= marvel_agp_setup,
	.cleanup	= marvel_agp_cleanup,
	.configure	= marvel_agp_configure,
	.bind		= marvel_agp_bind_memory,
	.unbind		= marvel_agp_unbind_memory,
	.translate	= marvel_agp_translate
};

alpha_agp_info *
marvel_agp_info(void)
{
	struct pci_controller *hose;
	io7_ioport_csrs *csrs;
	alpha_agp_info *agp;
	struct io7 *io7;

	/*
	 * Find the first IO7 with an AGP card.
	 *
	 * FIXME -- there should be a better way (we want to be able to
	 * specify and what if the agp card is not video???)
	 */
	hose = NULL;
	for (io7 = NULL; (io7 = marvel_next_io7(io7)) != NULL; ) {
		struct pci_controller *h;
		vuip addr;

		if (!io7->ports[IO7_AGP_PORT].enabled)
			continue;

		h = io7->ports[IO7_AGP_PORT].hose;
		addr = (vuip)build_conf_addr(h, 0, PCI_DEVFN(5, 0), 0);

		if (*addr != 0xffffffffu) {
			hose = h;
			break;
		}
	}

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

	printk("MARVEL - using hose %d as AGP\n", hose->index);

	/*
	 * Get the csrs from the hose.
	 */
	csrs = ((struct io7_port *)hose->sysdata)->csrs;

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

	/*
	 * Fill it in.
	 */
	agp->hose = hose;
	agp->private = NULL;
	agp->ops = &marvel_agp_ops;

	/*
	 * Aperture - not configured until ops.setup().
	 */
	agp->aperture.bus_base = 0;
	agp->aperture.size = 0;
	agp->aperture.sysdata = NULL;

	/*
	 * Capabilities.
	 *
	 * NOTE: IO7 reports through AGP_STAT that it can support a read queue
	 *       depth of 17 (rq = 0x10). It actually only supports a depth of
	 * 	 16 (rq = 0xf).
	 */
	agp->capability.lw = csrs->AGP_STAT.csr;
	agp->capability.bits.rq = 0xf;

	/*
	 * Mode.
	 */
	agp->mode.lw = csrs->AGP_CMD.csr;

	return agp;
}


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

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

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

#include <asm/ptrace.h>

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

/*
 * 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_CFG 0

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

/*
 * Given a bus, device, and function number, compute resulting
 * configuration space address and setup the MCPCIA_HAXR2 register
 * 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.
 *
 * Type 0:
 *
 *  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
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 *	31:11	Device select bit.
 * 	10:8	Function number
 * 	 7:2	Register 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 unsigned int
conf_read(unsigned long addr, unsigned char type1,
	  struct pci_controller *hose)
{
	unsigned long flags;
	unsigned long mid = MCPCIA_HOSE2MID(hose->index);
	unsigned int stat0, value, cpu;

	cpu = smp_processor_id();

	local_irq_save(flags);

	DBG_CFG(("conf_read(addr=0x%lx, type1=%d, hose=%d)\n",
		 addr, type1, mid));

	/* Reset status register to avoid losing errors.  */
	stat0 = *(vuip)MCPCIA_CAP_ERR(mid);
	*(vuip)MCPCIA_CAP_ERR(mid) = stat0;
	mb();
	*(vuip)MCPCIA_CAP_ERR(mid);
	DBG_CFG(("conf_read: MCPCIA_CAP_ERR(%d) was 0x%x\n", mid, stat0));

	mb();
	draina();
	mcheck_expected(cpu) = 1;
	mcheck_taken(cpu) = 0;
	mcheck_extra(cpu) = mid;
	mb();

	/* Access configuration space.  */
	value = *((vuip)addr);
	mb();
	mb();  /* magic */

	if (mcheck_taken(cpu)) {
		mcheck_taken(cpu) = 0;
		value = 0xffffffffU;
		mb();
	}
	mcheck_expected(cpu) = 0;
	mb();

	DBG_CFG(("conf_read(): finished\n"));

	local_irq_restore(flags);
	return value;
}

static void
conf_write(unsigned long addr, unsigned int value, unsigned char type1,
	   struct pci_controller *hose)
{
	unsigned long flags;
	unsigned long mid = MCPCIA_HOSE2MID(hose->index);
	unsigned int stat0, cpu;

	cpu = smp_processor_id();

	local_irq_save(flags);	/* avoid getting hit by machine check */

	/* Reset status register to avoid losing errors.  */
	stat0 = *(vuip)MCPCIA_CAP_ERR(mid);
	*(vuip)MCPCIA_CAP_ERR(mid) = stat0; mb();
	*(vuip)MCPCIA_CAP_ERR(mid);
	DBG_CFG(("conf_write: MCPCIA CAP_ERR(%d) was 0x%x\n", mid, stat0));

	draina();
	mcheck_expected(cpu) = 1;
	mcheck_extra(cpu) = mid;
	mb();

	/* Access configuration space.  */
	*((vuip)addr) = value;
	mb();
	mb();  /* magic */
	*(vuip)MCPCIA_CAP_ERR(mid); /* read to force the write */
	mcheck_expected(cpu) = 0;
	mb();

	DBG_CFG(("conf_write(): finished\n"));
	local_irq_restore(flags);
}

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

	DBG_CFG(("mk_conf_addr(bus=%d,devfn=0x%x,hose=%d,where=0x%x,"
		 " pci_addr=0x%p, type1=0x%p)\n",
		 bus, devfn, hose->index, where, pci_addr, type1));

	/* Type 1 configuration cycle for *ALL* busses.  */
	*type1 = 1;

	if (!pbus->parent) /* No parent means peer PCI bus. */
		bus = 0;
	addr = (bus << 16) | (devfn << 8) | (where);
	addr <<= 5; /* swizzle for SPARSE */
	addr |= hose->config_space_base;

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

static int
mcpcia_read_config(struct pci_bus *bus, unsigned int devfn, int where,
		   int size, u32 *value)
{
	struct pci_controller *hose = bus->sysdata;
	unsigned long addr, w;
	unsigned char type1;

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

	addr |= (size - 1) * 8;
	w = conf_read(addr, type1, hose);
	switch (size) {
	case 1:
		*value = __kernel_extbl(w, where & 3);
		break;
	case 2:
		*value = __kernel_extwl(w, where & 3);
		break;
	case 4:
		*value = w;
		break;
	}
	return PCIBIOS_SUCCESSFUL;
}

static int
mcpcia_write_config(struct pci_bus *bus, unsigned int devfn, int where,
		    int size, u32 value)
{
	struct pci_controller *hose = bus->sysdata;
	unsigned long addr;
	unsigned char type1;

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

	addr |= (size - 1) * 8;
	value = __kernel_insql(value, where & 3);
	conf_write(addr, value, type1, hose);
	return PCIBIOS_SUCCESSFUL;
}

struct pci_ops mcpcia_pci_ops =
{
	.read =		mcpcia_read_config,
	.write =	mcpcia_write_config,
};

void
mcpcia_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end)
{
	wmb();
	*(vuip)MCPCIA_SG_TBIA(MCPCIA_HOSE2MID(hose->index)) = 0;
	mb();
}

static int __init
mcpcia_probe_hose(int h)
{
	int cpu = smp_processor_id();
	int mid = MCPCIA_HOSE2MID(h);
	unsigned int pci_rev;

	/* Gotta be REAL careful.  If hose is absent, we get an mcheck.  */

	mb();
	mb();
	draina();
	wrmces(7);

	mcheck_expected(cpu) = 2;	/* indicates probing */
	mcheck_taken(cpu) = 0;
	mcheck_extra(cpu) = mid;
	mb();

	/* Access the bus revision word. */
	pci_rev = *(vuip)MCPCIA_REV(mid);

	mb();
	mb();  /* magic */
	if (mcheck_taken(cpu)) {
		mcheck_taken(cpu) = 0;
		pci_rev = 0xffffffff;
		mb();
	}
	mcheck_expected(cpu) = 0;
	mb();

	return (pci_rev >> 16) == PCI_CLASS_BRIDGE_HOST;
}

static void __init
mcpcia_new_hose(int h)
{
	struct pci_controller *hose;
	struct resource *io, *mem, *hae_mem;
	int mid = MCPCIA_HOSE2MID(h);

	hose = alloc_pci_controller();
	if (h == 0)
		pci_isa_hose = hose;
	io = alloc_resource();
	mem = alloc_resource();
	hae_mem = alloc_resource();

	hose->io_space = io;
	hose->mem_space = hae_mem;
	hose->sparse_mem_base = MCPCIA_SPARSE(mid) - IDENT_ADDR;
	hose->dense_mem_base = MCPCIA_DENSE(mid) - IDENT_ADDR;
	hose->sparse_io_base = MCPCIA_IO(mid) - IDENT_ADDR;
	hose->dense_io_base = 0;
	hose->config_space_base = MCPCIA_CONF(mid);
	hose->index = h;

	io->start = MCPCIA_IO(mid) - MCPCIA_IO_BIAS;
	io->end = io->start + 0xffff;
	io->name = pci_io_names[h];
	io->flags = IORESOURCE_IO;

	mem->start = MCPCIA_DENSE(mid) - MCPCIA_MEM_BIAS;
	mem->end = mem->start + 0xffffffff;
	mem->name = pci_mem_names[h];
	mem->flags = IORESOURCE_MEM;

	hae_mem->start = mem->start;
	hae_mem->end = mem->start + MCPCIA_MEM_MASK;
	hae_mem->name = pci_hae0_name;
	hae_mem->flags = IORESOURCE_MEM;

	if (request_resource(&ioport_resource, io) < 0)
		printk(KERN_ERR "Failed to request IO on hose %d\n", h);
	if (request_resource(&iomem_resource, mem) < 0)
		printk(KERN_ERR "Failed to request MEM on hose %d\n", h);
	if (request_resource(mem, hae_mem) < 0)
		printk(KERN_ERR "Failed to request HAE_MEM on hose %d\n", h);
}

static void
mcpcia_pci_clr_err(int mid)
{
	*(vuip)MCPCIA_CAP_ERR(mid);
	*(vuip)MCPCIA_CAP_ERR(mid) = 0xffffffff;   /* Clear them all.  */
	mb();
	*(vuip)MCPCIA_CAP_ERR(mid);  /* Re-read for force write.  */
}

static void __init
mcpcia_startup_hose(struct pci_controller *hose)
{
	int mid = MCPCIA_HOSE2MID(hose->index);
	unsigned int tmp;

	mcpcia_pci_clr_err(mid);

	/*
	 * Set up error reporting.
	 */
	tmp = *(vuip)MCPCIA_CAP_ERR(mid);
	tmp |= 0x0006;		/* master/target abort */
	*(vuip)MCPCIA_CAP_ERR(mid) = tmp;
	mb();
	tmp = *(vuip)MCPCIA_CAP_ERR(mid);

	/*
	 * Set up the PCI->physical memory translation windows.
	 *
	 * Window 0 is scatter-gather 8MB at 8MB (for isa)
	 * Window 1 is scatter-gather (up to) 1GB at 1GB (for pci)
	 * Window 2 is direct access 2GB at 2GB
	 */
	hose->sg_isa = iommu_arena_new(hose, 0x00800000, 0x00800000,
				       SMP_CACHE_BYTES);
	hose->sg_pci = iommu_arena_new(hose, 0x40000000,
				       size_for_memory(0x40000000),
				       SMP_CACHE_BYTES);

	__direct_map_base = 0x80000000;
	__direct_map_size = 0x80000000;

	*(vuip)MCPCIA_W0_BASE(mid) = hose->sg_isa->dma_base | 3;
	*(vuip)MCPCIA_W0_MASK(mid) = (hose->sg_isa->size - 1) & 0xfff00000;
	*(vuip)MCPCIA_T0_BASE(mid) = virt_to_phys(hose->sg_isa->ptes) >> 8;

	*(vuip)MCPCIA_W1_BASE(mid) = hose->sg_pci->dma_base | 3;
	*(vuip)MCPCIA_W1_MASK(mid) = (hose->sg_pci->size - 1) & 0xfff00000;
	*(vuip)MCPCIA_T1_BASE(mid) = virt_to_phys(hose->sg_pci->ptes) >> 8;

	*(vuip)MCPCIA_W2_BASE(mid) = __direct_map_base | 1;
	*(vuip)MCPCIA_W2_MASK(mid) = (__direct_map_size - 1) & 0xfff00000;
	*(vuip)MCPCIA_T2_BASE(mid) = 0;

	*(vuip)MCPCIA_W3_BASE(mid) = 0x0;

	mcpcia_pci_tbi(hose, 0, -1);

	*(vuip)MCPCIA_HBASE(mid) = 0x0;
	mb();

	*(vuip)MCPCIA_HAE_MEM(mid) = 0U;
	mb();
	*(vuip)MCPCIA_HAE_MEM(mid); /* read it back. */
	*(vuip)MCPCIA_HAE_IO(mid) = 0;
	mb();
	*(vuip)MCPCIA_HAE_IO(mid);  /* read it back. */
}

void __init
mcpcia_init_arch(void)
{
	/* With multiple PCI busses, we play with I/O as physical addrs.  */
	ioport_resource.end = ~0UL;

	/* Allocate hose 0.  That's the one that all the ISA junk hangs
	   off of, from which we'll be registering stuff here in a bit.
	   Other hose detection is done in mcpcia_init_hoses, which is
	   called from init_IRQ.  */

	mcpcia_new_hose(0);
}

/* This is called from init_IRQ, since we cannot take interrupts
   before then.  Which means we cannot do this in init_arch.  */

void __init
mcpcia_init_hoses(void)
{
	struct pci_controller *hose;
	int hose_count;
	int h;

	/* First, find how many hoses we have.  */
	hose_count = 0;
	for (h = 0; h < MCPCIA_MAX_HOSES; ++h) {
		if (mcpcia_probe_hose(h)) {
			if (h != 0)
				mcpcia_new_hose(h);
			hose_count++;
		}
	}

	printk("mcpcia_init_hoses: found %d hoses\n", hose_count);

	/* Now do init for each hose.  */
	for (hose = hose_head; hose; hose = hose->next)
		mcpcia_startup_hose(hose);
}

static void
mcpcia_print_uncorrectable(struct el_MCPCIA_uncorrected_frame_mcheck *logout)
{
	struct el_common_EV5_uncorrectable_mcheck *frame;
	int i;

	frame = &logout->procdata;

	/* Print PAL fields */
	for (i = 0; i < 24; i += 2) {
		printk("  paltmp[%d-%d] = %16lx %16lx\n",
		       i, i+1, frame->paltemp[i], frame->paltemp[i+1]);
	}
	for (i = 0; i < 8; i += 2) {
		printk("  shadow[%d-%d] = %16lx %16lx\n",
		       i, i+1, frame->shadow[i],
		       frame->shadow[i+1]);
	}
	printk("  Addr of excepting instruction  = %16lx\n",
	       frame->exc_addr);
	printk("  Summary of arithmetic traps    = %16lx\n",
	       frame->exc_sum);
	printk("  Exception mask                 = %16lx\n",
	       frame->exc_mask);
	printk("  Base address for PALcode       = %16lx\n",
	       frame->pal_base);
	printk("  Interrupt Status Reg           = %16lx\n",
	       frame->isr);
	printk("  CURRENT SETUP OF EV5 IBOX      = %16lx\n",
	       frame->icsr);
	printk("  I-CACHE Reg %s parity error   = %16lx\n",
	       (frame->ic_perr_stat & 0x800L) ?
	       "Data" : "Tag",
	       frame->ic_perr_stat);
	printk("  D-CACHE error Reg              = %16lx\n",
	       frame->dc_perr_stat);
	if (frame->dc_perr_stat & 0x2) {
		switch (frame->dc_perr_stat & 0x03c) {
		case 8:
			printk("    Data error in bank 1\n");
			break;
		case 4:
			printk("    Data error in bank 0\n");
			break;
		case 20:
			printk("    Tag error in bank 1\n");
			break;
		case 10:
			printk("    Tag error in bank 0\n");
			break;
		}
	}
	printk("  Effective VA                   = %16lx\n",
	       frame->va);
	printk("  Reason for D-stream            = %16lx\n",
	       frame->mm_stat);
	printk("  EV5 SCache address             = %16lx\n",
	       frame->sc_addr);
	printk("  EV5 SCache TAG/Data parity     = %16lx\n",
	       frame->sc_stat);
	printk("  EV5 BC_TAG_ADDR                = %16lx\n",
	       frame->bc_tag_addr);
	printk("  EV5 EI_ADDR: Phys addr of Xfer = %16lx\n",
	       frame->ei_addr);
	printk("  Fill Syndrome                  = %16lx\n",
	       frame->fill_syndrome);
	printk("  EI_STAT reg                    = %16lx\n",
	       frame->ei_stat);
	printk("  LD_LOCK                        = %16lx\n",
	       frame->ld_lock);
}

static void
mcpcia_print_system_area(unsigned long la_ptr)
{
	struct el_common *frame;
	struct pci_controller *hose;

	struct IOD_subpacket {
	  unsigned long base;
	  unsigned int whoami;
	  unsigned int rsvd1;
	  unsigned int pci_rev;
	  unsigned int cap_ctrl;
	  unsigned int hae_mem;
	  unsigned int hae_io;
	  unsigned int int_ctl;
	  unsigned int int_reg;
	  unsigned int int_mask0;
	  unsigned int int_mask1;
	  unsigned int mc_err0;
	  unsigned int mc_err1;
	  unsigned int cap_err;
	  unsigned int rsvd2;
	  unsigned int pci_err1;
	  unsigned int mdpa_stat;
	  unsigned int mdpa_syn;
	  unsigned int mdpb_stat;
	  unsigned int mdpb_syn;
	  unsigned int rsvd3;
	  unsigned int rsvd4;
	  unsigned int rsvd5;
	} *iodpp;

	frame = (struct el_common *)la_ptr;
	iodpp = (struct IOD_subpacket *) (la_ptr + frame->sys_offset);

	for (hose = hose_head; hose; hose = hose->next, iodpp++) {

	  printk("IOD %d Register Subpacket - Bridge Base Address %16lx\n",
		 hose->index, iodpp->base);
	  printk("  WHOAMI      = %8x\n", iodpp->whoami);
	  printk("  PCI_REV     = %8x\n", iodpp->pci_rev);
	  printk("  CAP_CTRL    = %8x\n", iodpp->cap_ctrl);
	  printk("  HAE_MEM     = %8x\n", iodpp->hae_mem);
	  printk("  HAE_IO      = %8x\n", iodpp->hae_io);
	  printk("  INT_CTL     = %8x\n", iodpp->int_ctl);
	  printk("  INT_REG     = %8x\n", iodpp->int_reg);
	  printk("  INT_MASK0   = %8x\n", iodpp->int_mask0);
	  printk("  INT_MASK1   = %8x\n", iodpp->int_mask1);
	  printk("  MC_ERR0     = %8x\n", iodpp->mc_err0);
	  printk("  MC_ERR1     = %8x\n", iodpp->mc_err1);
	  printk("  CAP_ERR     = %8x\n", iodpp->cap_err);
	  printk("  PCI_ERR1    = %8x\n", iodpp->pci_err1);
	  printk("  MDPA_STAT   = %8x\n", iodpp->mdpa_stat);
	  printk("  MDPA_SYN    = %8x\n", iodpp->mdpa_syn);
	  printk("  MDPB_STAT   = %8x\n", iodpp->mdpb_stat);
	  printk("  MDPB_SYN    = %8x\n", iodpp->mdpb_syn);
	}
}

void
mcpcia_machine_check(unsigned long vector, unsigned long la_ptr)
{
	struct el_MCPCIA_uncorrected_frame_mcheck *mchk_logout;
	unsigned int cpu = smp_processor_id();
	int expected;

	mchk_logout = (struct el_MCPCIA_uncorrected_frame_mcheck *)la_ptr;
	expected = mcheck_expected(cpu);

	mb();
	mb();  /* magic */
	draina();

	switch (expected) {
	case 0:
	    {
		/* FIXME: how do we figure out which hose the
		   error was on?  */
		struct pci_controller *hose;
		for (hose = hose_head; hose; hose = hose->next)
			mcpcia_pci_clr_err(MCPCIA_HOSE2MID(hose->index));
		break;
	    }
	case 1:
		mcpcia_pci_clr_err(mcheck_extra(cpu));
		break;
	default:
		/* Otherwise, we're being called from mcpcia_probe_hose
		   and there's no hose clear an error from.  */
		break;
	}

	wrmces(0x7);
	mb();

	process_mcheck_info(vector, la_ptr, "MCPCIA", expected != 0);
	if (!expected && vector != 0x620 && vector != 0x630) {
		mcpcia_print_uncorrectable(mchk_logout);
		mcpcia_print_system_area(la_ptr);
	}
}


code:
// SPDX-License-Identifier: GPL-2.0
/*
 *      linux/arch/alpha/kernel/core_polaris.c
 *
 * POLARIS chip-specific code
 */

#define __EXTERN_INLINE inline
#include <asm/io.h>
#include <asm/core_polaris.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 "proto.h"
#include "pci_impl.h"

/*
 * 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.  This is fairly straightforward
 * on POLARIS, since the chip itself generates Type 0 or Type 1
 * cycles automatically depending on the bus number (Bus 0 is
 * hardwired to Type 0, all others are Type 1.  Peer bridges
 * are not supported).
 *
 * All types:
 *
 *  3 3 3 3|3 3 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1
 *  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 9 8|7 6 5 4|3 2 1 0
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |1|1|1|1|1|0|0|1|1|1|1|1|1|1|1|0|B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|x|x|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 *	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, u8 *type1)
{
	u8 bus = pbus->number;

	*type1 = (bus == 0) ? 0 : 1;
	*pci_addr = (bus << 16) | (device_fn << 8) | (where) |
		    POLARIS_DENSE_CONFIG_BASE;

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

	return 0;
}

static int
polaris_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
polaris_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 polaris_pci_ops =
{
	.read =		polaris_read_config,
	.write =	polaris_write_config,
};

void __init
polaris_init_arch(void)
{
	struct pci_controller *hose;

	/* May need to initialize error reporting (see PCICTL0/1), but
	 * for now assume that the firmware has done the right thing
	 * already.
	 */
#if 0
	printk("polaris_init_arch(): trusting firmware for setup\n");
#endif

	/*
	 * Create our single hose.
	 */

	pci_isa_hose = hose = alloc_pci_controller();
	hose->io_space = &ioport_resource;
	hose->mem_space = &iomem_resource;
	hose->index = 0;

	hose->sparse_mem_base = 0;
	hose->dense_mem_base = POLARIS_DENSE_MEM_BASE - IDENT_ADDR;
	hose->sparse_io_base = 0;
	hose->dense_io_base = POLARIS_DENSE_IO_BASE - IDENT_ADDR;

	hose->sg_isa = hose->sg_pci = NULL;

	/* The I/O window is fixed at 2G @ 2G.  */
	__direct_map_base = 0x80000000;
	__direct_map_size = 0x80000000;
}

static inline void
polaris_pci_clr_err(void)
{
	*(vusp)POLARIS_W_STATUS;
	/* Write 1's to settable bits to clear errors */
	*(vusp)POLARIS_W_STATUS = 0x7800;
	mb();
	*(vusp)POLARIS_W_STATUS;
}

void
polaris_machine_check(unsigned long vector, unsigned long la_ptr)
{
	/* Clear the error before any reporting.  */
	mb();
	mb();
	draina();
	polaris_pci_clr_err();
	wrmces(0x7);
	mb();

	process_mcheck_info(vector, la_ptr, "POLARIS",
			    mcheck_expected(0));
}


code:
// SPDX-License-Identifier: GPL-2.0
/*
 *	linux/arch/alpha/kernel/core_t2.c
 *
 * Written by Jay A Estabrook (jestabro@amt.tay1.dec.com).
 * December 1996.
 *
 * based on CIA code by David A Rusling (david.rusling@reo.mts.dec.com)
 *
 * Code common to all T2 core logic chips.
 */

#define __EXTERN_INLINE
#include <asm/io.h>
#include <asm/core_t2.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/delay.h>
#include <asm/mce.h>

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

/* For dumping initial DMA window settings. */
#define DEBUG_PRINT_INITIAL_SETTINGS 0

/* For dumping final DMA window settings. */
#define DEBUG_PRINT_FINAL_SETTINGS 0

/*
 * By default, we direct-map starting at 2GB, in order to allow the
 * maximum size direct-map window (2GB) to match the maximum amount of
 * memory (2GB) that can be present on SABLEs. But that limits the
 * floppy to DMA only via the scatter/gather window set up for 8MB
 * ISA DMA, since the maximum ISA DMA address is 2GB-1.
 *
 * For now, this seems a reasonable trade-off: even though most SABLEs
 * have less than 1GB of memory, floppy usage/performance will not
 * really be affected by forcing it to go via scatter/gather...
 */
#define T2_DIRECTMAP_2G 1

#if T2_DIRECTMAP_2G
# define T2_DIRECTMAP_START	0x80000000UL
# define T2_DIRECTMAP_LENGTH	0x80000000UL
#else
# define T2_DIRECTMAP_START	0x40000000UL
# define T2_DIRECTMAP_LENGTH	0x40000000UL
#endif

/* The ISA scatter/gather window settings. */
#define T2_ISA_SG_START		0x00800000UL
#define T2_ISA_SG_LENGTH	0x00800000UL

/*
 * 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(args)	printk args
#else
# define DBG(args)
#endif

static volatile unsigned int t2_mcheck_any_expected;
static volatile unsigned int t2_mcheck_last_taken;

/* Place to save the DMA Window registers as set up by SRM
   for restoration during shutdown. */
static struct
{
	struct {
		unsigned long wbase;
		unsigned long wmask;
		unsigned long tbase;
	} window[2];
	unsigned long hae_1;
  	unsigned long hae_2;
	unsigned long hae_3;
	unsigned long hae_4;
	unsigned long hbase;
} t2_saved_config __attribute((common));

/*
 * Given a bus, device, and function number, compute resulting
 * configuration space address and setup the T2_HAXR2 register
 * 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.
 *
 * Type 0:
 *
 *  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
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0|
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 *	31:11	Device select bit.
 * 	10:8	Function number
 * 	 7:2	Register 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)
{
	unsigned long addr;
	u8 bus = pbus->number;

	DBG(("mk_conf_addr(bus=%d, dfn=0x%x, where=0x%x,"
	     " addr=0x%lx, type1=0x%x)\n",
	     bus, device_fn, where, pci_addr, type1));

	if (bus == 0) {
		int device = device_fn >> 3;

		/* Type 0 configuration cycle.  */

		if (device > 8) {
			DBG(("mk_conf_addr: device (%d)>20, returning -1\n",
			     device));
			return -1;
		}

		*type1 = 0;
		addr = (0x0800L << device) | ((device_fn & 7) << 8) | (where);
	} else {
		/* Type 1 configuration cycle.  */
		*type1 = 1;
		addr = (bus << 16) | (device_fn << 8) | (where);
	}
	*pci_addr = addr;
	DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr));
	return 0;
}

/*
 * NOTE: both conf_read() and conf_write() may set HAE_3 when needing
 *       to do type1 access. This is protected by the use of spinlock IRQ
 *       primitives in the wrapper functions pci_{read,write}_config_*()
 *       defined in drivers/pci/pci.c.
 */
static unsigned int
conf_read(unsigned long addr, unsigned char type1)
{
	unsigned int value, cpu, taken;
	unsigned long t2_cfg = 0;

	cpu = smp_processor_id();

	DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1));

	/* If Type1 access, must set T2 CFG.  */
	if (type1) {
		t2_cfg = *(vulp)T2_HAE_3 & ~0xc0000000UL;
		*(vulp)T2_HAE_3 = 0x40000000UL | t2_cfg;
		mb();
	}
	mb();
	draina();

	mcheck_expected(cpu) = 1;
	mcheck_taken(cpu) = 0;
	t2_mcheck_any_expected |= (1 << cpu);
	mb();

	/* Access configuration space. */
	value = *(vuip)addr;
	mb();
	mb();  /* magic */

	/* Wait for possible mcheck. Also, this lets other CPUs clear
	   their mchecks as well, as they can reliably tell when
	   another CPU is in the midst of handling a real mcheck via
	   the "taken" function. */
	udelay(100);

	if ((taken = mcheck_taken(cpu))) {
		mcheck_taken(cpu) = 0;
		t2_mcheck_last_taken |= (1 << cpu);
		value = 0xffffffffU;
		mb();
	}
	mcheck_expected(cpu) = 0;
	t2_mcheck_any_expected = 0;
	mb();

	/* If Type1 access, must reset T2 CFG so normal IO space ops work.  */
	if (type1) {
		*(vulp)T2_HAE_3 = t2_cfg;
		mb();
	}

	return value;
}

static void
conf_write(unsigned long addr, unsigned int value, unsigned char type1)
{
	unsigned int cpu, taken;
	unsigned long t2_cfg = 0;

	cpu = smp_processor_id();

	/* If Type1 access, must set T2 CFG.  */
	if (type1) {
		t2_cfg = *(vulp)T2_HAE_3 & ~0xc0000000UL;
		*(vulp)T2_HAE_3 = t2_cfg | 0x40000000UL;
		mb();
	}
	mb();
	draina();

	mcheck_expected(cpu) = 1;
	mcheck_taken(cpu) = 0;
	t2_mcheck_any_expected |= (1 << cpu);
	mb();

	/* Access configuration space.  */
	*(vuip)addr = value;
	mb();
	mb();  /* magic */

	/* Wait for possible mcheck. Also, this lets other CPUs clear
	   their mchecks as well, as they can reliably tell when
	   this CPU is in the midst of handling a real mcheck via
	   the "taken" function. */
	udelay(100);

	if ((taken = mcheck_taken(cpu))) {
		mcheck_taken(cpu) = 0;
		t2_mcheck_last_taken |= (1 << cpu);
		mb();
	}
	mcheck_expected(cpu) = 0;
	t2_mcheck_any_expected = 0;
	mb();

	/* If Type1 access, must reset T2 CFG so normal IO space ops work.  */
	if (type1) {
		*(vulp)T2_HAE_3 = t2_cfg;
		mb();
	}
}

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

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

	mask = (size - 1) * 8;
	shift = (where & 3) * 8;
	addr = (pci_addr << 5) + mask + T2_CONF;
	*value = conf_read(addr, type1) >> (shift);
	return PCIBIOS_SUCCESSFUL;
}

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

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

	mask = (size - 1) * 8;
	addr = (pci_addr << 5) + mask + T2_CONF;
	conf_write(addr, value << ((where & 3) * 8), type1);
	return PCIBIOS_SUCCESSFUL;
}

struct pci_ops t2_pci_ops =
{
	.read =		t2_read_config,
	.write =	t2_write_config,
};

static void __init
t2_direct_map_window1(unsigned long base, unsigned long length)
{
	unsigned long temp;

	__direct_map_base = base;
	__direct_map_size = length;

	temp = (base & 0xfff00000UL) | ((base + length - 1) >> 20);
	*(vulp)T2_WBASE1 = temp | 0x80000UL; /* OR in ENABLE bit */
	temp = (length - 1) & 0xfff00000UL;
	*(vulp)T2_WMASK1 = temp;
	*(vulp)T2_TBASE1 = 0;

#if DEBUG_PRINT_FINAL_SETTINGS
	printk("%s: setting WBASE1=0x%lx WMASK1=0x%lx TBASE1=0x%lx\n",
	       __func__, *(vulp)T2_WBASE1, *(vulp)T2_WMASK1, *(vulp)T2_TBASE1);
#endif
}

static void __init
t2_sg_map_window2(struct pci_controller *hose,
		  unsigned long base,
		  unsigned long length)
{
	unsigned long temp;

	/* Note we can only do 1 SG window, as the other is for direct, so
	   do an ISA SG area, especially for the floppy. */
	hose->sg_isa = iommu_arena_new(hose, base, length, SMP_CACHE_BYTES);
	hose->sg_pci = NULL;

	temp = (base & 0xfff00000UL) | ((base + length - 1) >> 20);
	*(vulp)T2_WBASE2 = temp | 0xc0000UL; /* OR in ENABLE/SG bits */
	temp = (length - 1) & 0xfff00000UL;
	*(vulp)T2_WMASK2 = temp;
	*(vulp)T2_TBASE2 = virt_to_phys(hose->sg_isa->ptes) >> 1;
	mb();

	t2_pci_tbi(hose, 0, -1); /* flush TLB all */

#if DEBUG_PRINT_FINAL_SETTINGS
	printk("%s: setting WBASE2=0x%lx WMASK2=0x%lx TBASE2=0x%lx\n",
	       __func__, *(vulp)T2_WBASE2, *(vulp)T2_WMASK2, *(vulp)T2_TBASE2);
#endif
}

static void __init
t2_save_configuration(void)
{
#if DEBUG_PRINT_INITIAL_SETTINGS
	printk("%s: HAE_1 was 0x%lx\n", __func__, srm_hae); /* HW is 0 */
	printk("%s: HAE_2 was 0x%lx\n", __func__, *(vulp)T2_HAE_2);
	printk("%s: HAE_3 was 0x%lx\n", __func__, *(vulp)T2_HAE_3);
	printk("%s: HAE_4 was 0x%lx\n", __func__, *(vulp)T2_HAE_4);
	printk("%s: HBASE was 0x%lx\n", __func__, *(vulp)T2_HBASE);

	printk("%s: WBASE1=0x%lx WMASK1=0x%lx TBASE1=0x%lx\n", __func__,
	       *(vulp)T2_WBASE1, *(vulp)T2_WMASK1, *(vulp)T2_TBASE1);
	printk("%s: WBASE2=0x%lx WMASK2=0x%lx TBASE2=0x%lx\n", __func__,
	       *(vulp)T2_WBASE2, *(vulp)T2_WMASK2, *(vulp)T2_TBASE2);
#endif

	/*
	 * Save the DMA Window registers.
	 */
	t2_saved_config.window[0].wbase = *(vulp)T2_WBASE1;
	t2_saved_config.window[0].wmask = *(vulp)T2_WMASK1;
	t2_saved_config.window[0].tbase = *(vulp)T2_TBASE1;
	t2_saved_config.window[1].wbase = *(vulp)T2_WBASE2;
	t2_saved_config.window[1].wmask = *(vulp)T2_WMASK2;
	t2_saved_config.window[1].tbase = *(vulp)T2_TBASE2;

	t2_saved_config.hae_1 = srm_hae; /* HW is already set to 0 */
	t2_saved_config.hae_2 = *(vulp)T2_HAE_2;
	t2_saved_config.hae_3 = *(vulp)T2_HAE_3;
	t2_saved_config.hae_4 = *(vulp)T2_HAE_4;
	t2_saved_config.hbase = *(vulp)T2_HBASE;
}

void __init
t2_init_arch(void)
{
	struct pci_controller *hose;
	struct resource *hae_mem;
	unsigned long temp;
	unsigned int i;

	for (i = 0; i < NR_CPUS; i++) {
		mcheck_expected(i) = 0;
		mcheck_taken(i) = 0;
	}
	t2_mcheck_any_expected = 0;
	t2_mcheck_last_taken = 0;

	/* Enable scatter/gather TLB use.  */
	temp = *(vulp)T2_IOCSR;
	if (!(temp & (0x1UL << 26))) {
		printk("t2_init_arch: enabling SG TLB, IOCSR was 0x%lx\n",
		       temp);
		*(vulp)T2_IOCSR = temp | (0x1UL << 26);
		mb();
		*(vulp)T2_IOCSR; /* read it back to make sure */
	}

	t2_save_configuration();

	/*
	 * Create our single hose.
	 */
	pci_isa_hose = hose = alloc_pci_controller();
	hose->io_space = &ioport_resource;
	hae_mem = alloc_resource();
	hae_mem->start = 0;
	hae_mem->end = T2_MEM_R1_MASK;
	hae_mem->name = pci_hae0_name;
	if (request_resource(&iomem_resource, hae_mem) < 0)
		printk(KERN_ERR "Failed to request HAE_MEM\n");
	hose->mem_space = hae_mem;
	hose->index = 0;

	hose->sparse_mem_base = T2_SPARSE_MEM - IDENT_ADDR;
	hose->dense_mem_base = T2_DENSE_MEM - IDENT_ADDR;
	hose->sparse_io_base = T2_IO - IDENT_ADDR;
	hose->dense_io_base = 0;

	/*
	 * Set up the PCI->physical memory translation windows.
	 *
	 * Window 1 is direct mapped.
	 * Window 2 is scatter/gather (for ISA).
	 */

	t2_direct_map_window1(T2_DIRECTMAP_START, T2_DIRECTMAP_LENGTH);

	/* Always make an ISA DMA window. */
	t2_sg_map_window2(hose, T2_ISA_SG_START, T2_ISA_SG_LENGTH);

	*(vulp)T2_HBASE = 0x0; /* Disable HOLES. */

	/* Zero HAE.  */
	*(vulp)T2_HAE_1 = 0; mb(); /* Sparse MEM HAE */
	*(vulp)T2_HAE_2 = 0; mb(); /* Sparse I/O HAE */
	*(vulp)T2_HAE_3 = 0; mb(); /* Config Space HAE */

	/*
	 * We also now zero out HAE_4, the dense memory HAE, so that
	 * we need not account for its "offset" when accessing dense
	 * memory resources which we allocated in our normal way. This
	 * HAE would need to stay untouched were we to keep the SRM
	 * resource settings.
	 *
	 * Thus we can now run standard X servers on SABLE/LYNX. :-)
	 */
	*(vulp)T2_HAE_4 = 0; mb();
}

void
t2_kill_arch(int mode)
{
	/*
	 * Restore the DMA Window registers.
	 */
	*(vulp)T2_WBASE1 = t2_saved_config.window[0].wbase;
	*(vulp)T2_WMASK1 = t2_saved_config.window[0].wmask;
	*(vulp)T2_TBASE1 = t2_saved_config.window[0].tbase;
	*(vulp)T2_WBASE2 = t2_saved_config.window[1].wbase;
	*(vulp)T2_WMASK2 = t2_saved_config.window[1].wmask;
	*(vulp)T2_TBASE2 = t2_saved_config.window[1].tbase;
	mb();

	*(vulp)T2_HAE_1 = srm_hae;
	*(vulp)T2_HAE_2 = t2_saved_config.hae_2;
	*(vulp)T2_HAE_3 = t2_saved_config.hae_3;
	*(vulp)T2_HAE_4 = t2_saved_config.hae_4;
	*(vulp)T2_HBASE = t2_saved_config.hbase;
	mb();
	*(vulp)T2_HBASE; /* READ it back to ensure WRITE occurred. */
}

void
t2_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end)
{
	unsigned long t2_iocsr;

	t2_iocsr = *(vulp)T2_IOCSR;

	/* set the TLB Clear bit */
	*(vulp)T2_IOCSR = t2_iocsr | (0x1UL << 28);
	mb();
	*(vulp)T2_IOCSR; /* read it back to make sure */

	/* clear the TLB Clear bit */
	*(vulp)T2_IOCSR = t2_iocsr & ~(0x1UL << 28);
	mb();
	*(vulp)T2_IOCSR; /* read it back to make sure */
}

#define SIC_SEIC (1UL << 33)    /* System Event Clear */

static void
t2_clear_errors(int cpu)
{
	struct sable_cpu_csr *cpu_regs;

	cpu_regs = (struct sable_cpu_csr *)T2_CPUn_BASE(cpu);

	cpu_regs->sic &= ~SIC_SEIC;

	/* Clear CPU errors.  */
	cpu_regs->bcce |= cpu_regs->bcce;
	cpu_regs->cbe  |= cpu_regs->cbe;
	cpu_regs->bcue |= cpu_regs->bcue;
	cpu_regs->dter |= cpu_regs->dter;

	*(vulp)T2_CERR1 |= *(vulp)T2_CERR1;
	*(vulp)T2_PERR1 |= *(vulp)T2_PERR1;

	mb();
	mb();  /* magic */
}

/*
 * SABLE seems to have a "broadcast" style machine check, in that all
 * CPUs receive it. And, the issuing CPU, in the case of PCI Config
 * space read/write faults, will also receive a second mcheck, upon
 * lowering IPL during completion processing in pci_read_config_byte()
 * et al.
 *
 * Hence all the taken/expected/any_expected/last_taken stuff...
 */
void
t2_machine_check(unsigned long vector, unsigned long la_ptr)
{
	int cpu = smp_processor_id();
#ifdef CONFIG_VERBOSE_MCHECK
	struct el_common *mchk_header = (struct el_common *)la_ptr;
#endif

	/* Clear the error before any reporting.  */
	mb();
	mb();  /* magic */
	draina();
	t2_clear_errors(cpu);

	/* This should not actually be done until the logout frame is
	   examined, but, since we don't do that, go on and do this... */
	wrmces(0x7);
	mb();

	/* Now, do testing for the anomalous conditions. */
	if (!mcheck_expected(cpu) && t2_mcheck_any_expected) {
		/*
		 * FUNKY: Received mcheck on a CPU and not
		 * expecting it, but another CPU is expecting one.
		 *
		 * Just dismiss it for now on this CPU...
		 */
#ifdef CONFIG_VERBOSE_MCHECK
		if (alpha_verbose_mcheck > 1) {
			printk("t2_machine_check(cpu%d): any_expected 0x%x -"
			       " (assumed) spurious -"
			       " code 0x%x\n", cpu, t2_mcheck_any_expected,
			       (unsigned int)mchk_header->code);
		}
#endif
		return;
	}

	if (!mcheck_expected(cpu) && !t2_mcheck_any_expected) {
		if (t2_mcheck_last_taken & (1 << cpu)) {
#ifdef CONFIG_VERBOSE_MCHECK
		    if (alpha_verbose_mcheck > 1) {
			printk("t2_machine_check(cpu%d): last_taken 0x%x - "
			       "unexpected mcheck - code 0x%x\n",
			       cpu, t2_mcheck_last_taken,
			       (unsigned int)mchk_header->code);
		    }
#endif
		    t2_mcheck_last_taken = 0;
		    mb();
		    return;
		} else {
			t2_mcheck_last_taken = 0;
			mb();
		}
	}

#ifdef CONFIG_VERBOSE_MCHECK
	if (alpha_verbose_mcheck > 1) {
		printk("%s t2_mcheck(cpu%d): last_taken 0x%x - "
		       "any_expected 0x%x - code 0x%x\n",
		       (mcheck_expected(cpu) ? "EX" : "UN"), cpu,
		       t2_mcheck_last_taken, t2_mcheck_any_expected,
		       (unsigned int)mchk_header->code);
	}
#endif

	process_mcheck_info(vector, la_ptr, "T2", mcheck_expected(cpu));
}