// SPDX-License-Identifier: GPL-2.0
/* pci_schizo.c: SCHIZO/TOMATILLO specific PCI controller support.
*
* Copyright (C) 2001, 2002, 2003, 2007, 2008 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/export.h>
#include <linux/interrupt.h>
#include <linux/of_device.h>
#include <linux/numa.h>
#include <asm/iommu.h>
#include <asm/irq.h>
#include <asm/pstate.h>
#include <asm/prom.h>
#include <asm/upa.h>
#include "pci_impl.h"
#include "iommu_common.h"
#define DRIVER_NAME "schizo"
#define PFX DRIVER_NAME ": "
/* 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 0x0000000000180000UL
static 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 = upa_readq(strbuf->strbuf_control);
upa_writeq((control | SCHIZO_STRBUF_CTRL_DENAB),
strbuf->strbuf_control);
for (i = 0; i < 128; i++) {
unsigned long val;
val = upa_readq(err_base + (i * 8UL));
upa_writeq(0UL, err_base + (i * 8UL));
stc_error_buf[i] = val;
}
for (i = 0; i < 16; i++) {
stc_tag_buf[i] = upa_readq(tag_base + (i * 8UL));
stc_line_buf[i] = upa_readq(line_base + (i * 8UL));
upa_writeq(0UL, tag_base + (i * 8UL));
upa_writeq(0UL, line_base + (i * 8UL));
}
/* OK, state is logged, exit diagnostic mode. */
upa_writeq(control, strbuf->strbuf_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_unloc
|