pci_schizo.c
来自「linux 内核源代码」· C语言 代码 · 共 1,492 行 · 第 1/4 页
C
1,492 行
/* pci_schizo.c: SCHIZO/TOMATILLO specific PCI controller support. * * Copyright (C) 2001, 2002, 2003, 2007 David S. Miller (davem@davemloft.net) */#include <linux/kernel.h>#include <linux/types.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <asm/iommu.h>#include <asm/irq.h>#include <asm/upa.h>#include <asm/pstate.h>#include <asm/prom.h>#include <asm/of_device.h>#include <asm/oplib.h>#include "pci_impl.h"#include "iommu_common.h"/* All SCHIZO registers are 64-bits. The following accessor * routines are how they are accessed. The REG parameter * is a physical address. */#define schizo_read(__reg) \({ u64 __ret; \ __asm__ __volatile__("ldxa [%1] %2, %0" \ : "=r" (__ret) \ : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \ : "memory"); \ __ret; \})#define schizo_write(__reg, __val) \ __asm__ __volatile__("stxa %0, [%1] %2" \ : /* no outputs */ \ : "r" (__val), "r" (__reg), \ "i" (ASI_PHYS_BYPASS_EC_E) \ : "memory")/* This is a convention that at least Excalibur and Merlin * follow. I suppose the SCHIZO used in Starcat and friends * will do similar. * * The only way I could see this changing is if the newlink * block requires more space in Schizo's address space than * they predicted, thus requiring an address space reorg when * the newer Schizo is taped out. *//* Streaming buffer control register. */#define SCHIZO_STRBUF_CTRL_LPTR 0x00000000000000f0UL /* LRU Lock Pointer */#define SCHIZO_STRBUF_CTRL_LENAB 0x0000000000000008UL /* LRU Lock Enable */#define SCHIZO_STRBUF_CTRL_RRDIS 0x0000000000000004UL /* Rerun Disable */#define SCHIZO_STRBUF_CTRL_DENAB 0x0000000000000002UL /* Diagnostic Mode Enable */#define SCHIZO_STRBUF_CTRL_ENAB 0x0000000000000001UL /* Streaming Buffer Enable *//* IOMMU control register. */#define SCHIZO_IOMMU_CTRL_RESV 0xfffffffff9000000UL /* Reserved */#define SCHIZO_IOMMU_CTRL_XLTESTAT 0x0000000006000000UL /* Translation Error Status */#define SCHIZO_IOMMU_CTRL_XLTEERR 0x0000000001000000UL /* Translation Error encountered */#define SCHIZO_IOMMU_CTRL_LCKEN 0x0000000000800000UL /* Enable translation locking */#define SCHIZO_IOMMU_CTRL_LCKPTR 0x0000000000780000UL /* Translation lock pointer */#define SCHIZO_IOMMU_CTRL_TSBSZ 0x0000000000070000UL /* TSB Size */#define SCHIZO_IOMMU_TSBSZ_1K 0x0000000000000000UL /* TSB Table 1024 8-byte entries */#define SCHIZO_IOMMU_TSBSZ_2K 0x0000000000010000UL /* TSB Table 2048 8-byte entries */#define SCHIZO_IOMMU_TSBSZ_4K 0x0000000000020000UL /* TSB Table 4096 8-byte entries */#define SCHIZO_IOMMU_TSBSZ_8K 0x0000000000030000UL /* TSB Table 8192 8-byte entries */#define SCHIZO_IOMMU_TSBSZ_16K 0x0000000000040000UL /* TSB Table 16k 8-byte entries */#define SCHIZO_IOMMU_TSBSZ_32K 0x0000000000050000UL /* TSB Table 32k 8-byte entries */#define SCHIZO_IOMMU_TSBSZ_64K 0x0000000000060000UL /* TSB Table 64k 8-byte entries */#define SCHIZO_IOMMU_TSBSZ_128K 0x0000000000070000UL /* TSB Table 128k 8-byte entries */#define SCHIZO_IOMMU_CTRL_RESV2 0x000000000000fff8UL /* Reserved */#define SCHIZO_IOMMU_CTRL_TBWSZ 0x0000000000000004UL /* Assumed page size, 0=8k 1=64k */#define SCHIZO_IOMMU_CTRL_DENAB 0x0000000000000002UL /* Diagnostic mode enable */#define SCHIZO_IOMMU_CTRL_ENAB 0x0000000000000001UL /* IOMMU Enable *//* Schizo config space address format is nearly identical to * that of PSYCHO: * * 32 24 23 16 15 11 10 8 7 2 1 0 * --------------------------------------------------------- * |0 0 0 0 0 0 0 0 0| bus | device | function | reg | 0 0 | * --------------------------------------------------------- */#define SCHIZO_CONFIG_BASE(PBM) ((PBM)->config_space)#define SCHIZO_CONFIG_ENCODE(BUS, DEVFN, REG) \ (((unsigned long)(BUS) << 16) | \ ((unsigned long)(DEVFN) << 8) | \ ((unsigned long)(REG)))static void *schizo_pci_config_mkaddr(struct pci_pbm_info *pbm, unsigned char bus, unsigned int devfn, int where){ if (!pbm) return NULL; bus -= pbm->pci_first_busno; return (void *) (SCHIZO_CONFIG_BASE(pbm) | SCHIZO_CONFIG_ENCODE(bus, devfn, where));}/* SCHIZO error handling support. */enum schizo_error_type { UE_ERR, CE_ERR, PCI_ERR, SAFARI_ERR};static DEFINE_SPINLOCK(stc_buf_lock);static unsigned long stc_error_buf[128];static unsigned long stc_tag_buf[16];static unsigned long stc_line_buf[16];#define SCHIZO_UE_INO 0x30 /* Uncorrectable ECC error */#define SCHIZO_CE_INO 0x31 /* Correctable ECC error */#define SCHIZO_PCIERR_A_INO 0x32 /* PBM A PCI bus error */#define SCHIZO_PCIERR_B_INO 0x33 /* PBM B PCI bus error */#define SCHIZO_SERR_INO 0x34 /* Safari interface error */#define SCHIZO_STC_ERR 0xb800UL /* --> 0xba00 */#define SCHIZO_STC_TAG 0xba00UL /* --> 0xba80 */#define SCHIZO_STC_LINE 0xbb00UL /* --> 0xbb80 */#define SCHIZO_STCERR_WRITE 0x2UL#define SCHIZO_STCERR_READ 0x1UL#define SCHIZO_STCTAG_PPN 0x3fffffff00000000UL#define SCHIZO_STCTAG_VPN 0x00000000ffffe000UL#define SCHIZO_STCTAG_VALID 0x8000000000000000UL#define SCHIZO_STCTAG_READ 0x4000000000000000UL#define SCHIZO_STCLINE_LINDX 0x0000000007800000UL#define SCHIZO_STCLINE_SPTR 0x000000000007e000UL#define SCHIZO_STCLINE_LADDR 0x0000000000001fc0UL#define SCHIZO_STCLINE_EPTR 0x000000000000003fUL#define SCHIZO_STCLINE_VALID 0x0000000000600000UL#define SCHIZO_STCLINE_FOFN 0x0000000000180000ULstatic void __schizo_check_stc_error_pbm(struct pci_pbm_info *pbm, enum schizo_error_type type){ struct strbuf *strbuf = &pbm->stc; unsigned long regbase = pbm->pbm_regs; unsigned long err_base, tag_base, line_base; u64 control; int i; err_base = regbase + SCHIZO_STC_ERR; tag_base = regbase + SCHIZO_STC_TAG; line_base = regbase + SCHIZO_STC_LINE; spin_lock(&stc_buf_lock); /* This is __REALLY__ dangerous. When we put the * streaming buffer into diagnostic mode to probe * it's tags and error status, we _must_ clear all * of the line tag valid bits before re-enabling * the streaming buffer. If any dirty data lives * in the STC when we do this, we will end up * invalidating it before it has a chance to reach * main memory. */ control = schizo_read(strbuf->strbuf_control); schizo_write(strbuf->strbuf_control, (control | SCHIZO_STRBUF_CTRL_DENAB)); for (i = 0; i < 128; i++) { unsigned long val; val = schizo_read(err_base + (i * 8UL)); schizo_write(err_base + (i * 8UL), 0UL); stc_error_buf[i] = val; } for (i = 0; i < 16; i++) { stc_tag_buf[i] = schizo_read(tag_base + (i * 8UL)); stc_line_buf[i] = schizo_read(line_base + (i * 8UL)); schizo_write(tag_base + (i * 8UL), 0UL); schizo_write(line_base + (i * 8UL), 0UL); } /* OK, state is logged, exit diagnostic mode. */ schizo_write(strbuf->strbuf_control, control); for (i = 0; i < 16; i++) { int j, saw_error, first, last; saw_error = 0; first = i * 8; last = first + 8; for (j = first; j < last; j++) { unsigned long errval = stc_error_buf[j]; if (errval != 0) { saw_error++; printk("%s: STC_ERR(%d)[wr(%d)rd(%d)]\n", pbm->name, j, (errval & SCHIZO_STCERR_WRITE) ? 1 : 0, (errval & SCHIZO_STCERR_READ) ? 1 : 0); } } if (saw_error != 0) { unsigned long tagval = stc_tag_buf[i]; unsigned long lineval = stc_line_buf[i]; printk("%s: STC_TAG(%d)[PA(%016lx)VA(%08lx)V(%d)R(%d)]\n", pbm->name, i, ((tagval & SCHIZO_STCTAG_PPN) >> 19UL), (tagval & SCHIZO_STCTAG_VPN), ((tagval & SCHIZO_STCTAG_VALID) ? 1 : 0), ((tagval & SCHIZO_STCTAG_READ) ? 1 : 0)); /* XXX Should spit out per-bank error information... -DaveM */ printk("%s: STC_LINE(%d)[LIDX(%lx)SP(%lx)LADDR(%lx)EP(%lx)" "V(%d)FOFN(%d)]\n", pbm->name, i, ((lineval & SCHIZO_STCLINE_LINDX) >> 23UL), ((lineval & SCHIZO_STCLINE_SPTR) >> 13UL), ((lineval & SCHIZO_STCLINE_LADDR) >> 6UL), ((lineval & SCHIZO_STCLINE_EPTR) >> 0UL), ((lineval & SCHIZO_STCLINE_VALID) ? 1 : 0), ((lineval & SCHIZO_STCLINE_FOFN) ? 1 : 0)); } } spin_unlock(&stc_buf_lock);}/* IOMMU is per-PBM in Schizo, so interrogate both for anonymous * controller level errors. */#define SCHIZO_IOMMU_TAG 0xa580UL#define SCHIZO_IOMMU_DATA 0xa600UL#define SCHIZO_IOMMU_TAG_CTXT 0x0000001ffe000000UL#define SCHIZO_IOMMU_TAG_ERRSTS 0x0000000001800000UL#define SCHIZO_IOMMU_TAG_ERR 0x0000000000400000UL#define SCHIZO_IOMMU_TAG_WRITE 0x0000000000200000UL#define SCHIZO_IOMMU_TAG_STREAM 0x0000000000100000UL#define SCHIZO_IOMMU_TAG_SIZE 0x0000000000080000UL#define SCHIZO_IOMMU_TAG_VPAGE 0x000000000007ffffUL#define SCHIZO_IOMMU_DATA_VALID 0x0000000100000000UL#define SCHIZO_IOMMU_DATA_CACHE 0x0000000040000000UL#define SCHIZO_IOMMU_DATA_PPAGE 0x000000003fffffffULstatic void schizo_check_iommu_error_pbm(struct pci_pbm_info *pbm, enum schizo_error_type type){ struct iommu *iommu = pbm->iommu; unsigned long iommu_tag[16]; unsigned long iommu_data[16]; unsigned long flags; u64 control; int i; spin_lock_irqsave(&iommu->lock, flags); control = schizo_read(iommu->iommu_control); if (control & SCHIZO_IOMMU_CTRL_XLTEERR) { unsigned long base; char *type_string; /* Clear the error encountered bit. */ control &= ~SCHIZO_IOMMU_CTRL_XLTEERR; schizo_write(iommu->iommu_control, control); switch((control & SCHIZO_IOMMU_CTRL_XLTESTAT) >> 25UL) { case 0: type_string = "Protection Error"; break; case 1: type_string = "Invalid Error"; break; case 2: type_string = "TimeOut Error"; break; case 3: default: type_string = "ECC Error"; break; }; printk("%s: IOMMU Error, type[%s]\n", pbm->name, type_string); /* Put the IOMMU into diagnostic mode and probe * it's TLB for entries with error status. * * It is very possible for another DVMA to occur * while we do this probe, and corrupt the system * further. But we are so screwed at this point * that we are likely to crash hard anyways, so * get as much diagnostic information to the * console as we can. */ schizo_write(iommu->iommu_control, control | SCHIZO_IOMMU_CTRL_DENAB); base = pbm->pbm_regs; for (i = 0; i < 16; i++) { iommu_tag[i] = schizo_read(base + SCHIZO_IOMMU_TAG + (i * 8UL)); iommu_data[i] = schizo_read(base + SCHIZO_IOMMU_DATA + (i * 8UL)); /* Now clear out the entry. */ schizo_write(base + SCHIZO_IOMMU_TAG + (i * 8UL), 0); schizo_write(base + SCHIZO_IOMMU_DATA + (i * 8UL), 0); } /* Leave diagnostic mode. */ schizo_write(iommu->iommu_control, control); for (i = 0; i < 16; i++) { unsigned long tag, data; tag = iommu_tag[i]; if (!(tag & SCHIZO_IOMMU_TAG_ERR)) continue; data = iommu_data[i]; switch((tag & SCHIZO_IOMMU_TAG_ERRSTS) >> 23UL) { case 0: type_string = "Protection Error"; break; case 1: type_string = "Invalid Error"; break; case 2: type_string = "TimeOut Error"; break; case 3: default: type_string = "ECC Error"; break; }; printk("%s: IOMMU TAG(%d)[error(%s) ctx(%x) wr(%d) str(%d) " "sz(%dK) vpg(%08lx)]\n", pbm->name, i, type_string, (int)((tag & SCHIZO_IOMMU_TAG_CTXT) >> 25UL), ((tag & SCHIZO_IOMMU_TAG_WRITE) ? 1 : 0), ((tag & SCHIZO_IOMMU_TAG_STREAM) ? 1 : 0), ((tag & SCHIZO_IOMMU_TAG_SIZE) ? 64 : 8), (tag & SCHIZO_IOMMU_TAG_VPAGE) << IOMMU_PAGE_SHIFT); printk("%s: IOMMU DATA(%d)[valid(%d) cache(%d) ppg(%016lx)]\n", pbm->name, i, ((data & SCHIZO_IOMMU_DATA_VALID) ? 1 : 0), ((data & SCHIZO_IOMMU_DATA_CACHE) ? 1 : 0), (data & SCHIZO_IOMMU_DATA_PPAGE) << IOMMU_PAGE_SHIFT); } } if (pbm->stc.strbuf_enabled) __schizo_check_stc_error_pbm(pbm, type); spin_unlock_irqrestore(&iommu->lock, flags);}static void schizo_check_iommu_error(struct pci_controller_info *p, enum schizo_error_type type){ schizo_check_iommu_error_pbm(&p->pbm_A, type); schizo_check_iommu_error_pbm(&p->pbm_B, type);}/* Uncorrectable ECC error status gathering. */#define SCHIZO_UE_AFSR 0x10030UL#define SCHIZO_UE_AFAR 0x10038UL#define SCHIZO_UEAFSR_PPIO 0x8000000000000000UL /* Safari */#define SCHIZO_UEAFSR_PDRD 0x4000000000000000UL /* Safari/Tomatillo */#define SCHIZO_UEAFSR_PDWR 0x2000000000000000UL /* Safari */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?