18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* psycho_common.c: Code common to PSYCHO and derivative PCI controllers.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include <linux/kernel.h>
78c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
88c2ecf20Sopenharmony_ci#include <linux/numa.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <asm/upa.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "pci_impl.h"
138c2ecf20Sopenharmony_ci#include "iommu_common.h"
148c2ecf20Sopenharmony_ci#include "psycho_common.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define  PSYCHO_STRBUF_CTRL_DENAB	0x0000000000000002ULL
178c2ecf20Sopenharmony_ci#define  PSYCHO_STCERR_WRITE		0x0000000000000002ULL
188c2ecf20Sopenharmony_ci#define  PSYCHO_STCERR_READ		0x0000000000000001ULL
198c2ecf20Sopenharmony_ci#define  PSYCHO_STCTAG_PPN		0x0fffffff00000000ULL
208c2ecf20Sopenharmony_ci#define  PSYCHO_STCTAG_VPN		0x00000000ffffe000ULL
218c2ecf20Sopenharmony_ci#define  PSYCHO_STCTAG_VALID		0x0000000000000002ULL
228c2ecf20Sopenharmony_ci#define  PSYCHO_STCTAG_WRITE		0x0000000000000001ULL
238c2ecf20Sopenharmony_ci#define  PSYCHO_STCLINE_LINDX		0x0000000001e00000ULL
248c2ecf20Sopenharmony_ci#define  PSYCHO_STCLINE_SPTR		0x00000000001f8000ULL
258c2ecf20Sopenharmony_ci#define  PSYCHO_STCLINE_LADDR		0x0000000000007f00ULL
268c2ecf20Sopenharmony_ci#define  PSYCHO_STCLINE_EPTR		0x00000000000000fcULL
278c2ecf20Sopenharmony_ci#define  PSYCHO_STCLINE_VALID		0x0000000000000002ULL
288c2ecf20Sopenharmony_ci#define  PSYCHO_STCLINE_FOFN		0x0000000000000001ULL
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(stc_buf_lock);
318c2ecf20Sopenharmony_cistatic unsigned long stc_error_buf[128];
328c2ecf20Sopenharmony_cistatic unsigned long stc_tag_buf[16];
338c2ecf20Sopenharmony_cistatic unsigned long stc_line_buf[16];
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic void psycho_check_stc_error(struct pci_pbm_info *pbm)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	unsigned long err_base, tag_base, line_base;
388c2ecf20Sopenharmony_ci	struct strbuf *strbuf = &pbm->stc;
398c2ecf20Sopenharmony_ci	u64 control;
408c2ecf20Sopenharmony_ci	int i;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (!strbuf->strbuf_control)
438c2ecf20Sopenharmony_ci		return;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	err_base = strbuf->strbuf_err_stat;
468c2ecf20Sopenharmony_ci	tag_base = strbuf->strbuf_tag_diag;
478c2ecf20Sopenharmony_ci	line_base = strbuf->strbuf_line_diag;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	spin_lock(&stc_buf_lock);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/* This is __REALLY__ dangerous.  When we put the streaming
528c2ecf20Sopenharmony_ci	 * buffer into diagnostic mode to probe it's tags and error
538c2ecf20Sopenharmony_ci	 * status, we _must_ clear all of the line tag valid bits
548c2ecf20Sopenharmony_ci	 * before re-enabling the streaming buffer.  If any dirty data
558c2ecf20Sopenharmony_ci	 * lives in the STC when we do this, we will end up
568c2ecf20Sopenharmony_ci	 * invalidating it before it has a chance to reach main
578c2ecf20Sopenharmony_ci	 * memory.
588c2ecf20Sopenharmony_ci	 */
598c2ecf20Sopenharmony_ci	control = upa_readq(strbuf->strbuf_control);
608c2ecf20Sopenharmony_ci	upa_writeq(control | PSYCHO_STRBUF_CTRL_DENAB, strbuf->strbuf_control);
618c2ecf20Sopenharmony_ci	for (i = 0; i < 128; i++) {
628c2ecf20Sopenharmony_ci		u64 val;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci		val = upa_readq(err_base + (i * 8UL));
658c2ecf20Sopenharmony_ci		upa_writeq(0UL, err_base + (i * 8UL));
668c2ecf20Sopenharmony_ci		stc_error_buf[i] = val;
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++) {
698c2ecf20Sopenharmony_ci		stc_tag_buf[i] = upa_readq(tag_base + (i * 8UL));
708c2ecf20Sopenharmony_ci		stc_line_buf[i] = upa_readq(line_base + (i * 8UL));
718c2ecf20Sopenharmony_ci		upa_writeq(0UL, tag_base + (i * 8UL));
728c2ecf20Sopenharmony_ci		upa_writeq(0UL, line_base + (i * 8UL));
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/* OK, state is logged, exit diagnostic mode. */
768c2ecf20Sopenharmony_ci	upa_writeq(control, strbuf->strbuf_control);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++) {
798c2ecf20Sopenharmony_ci		int j, saw_error, first, last;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci		saw_error = 0;
828c2ecf20Sopenharmony_ci		first = i * 8;
838c2ecf20Sopenharmony_ci		last = first + 8;
848c2ecf20Sopenharmony_ci		for (j = first; j < last; j++) {
858c2ecf20Sopenharmony_ci			u64 errval = stc_error_buf[j];
868c2ecf20Sopenharmony_ci			if (errval != 0) {
878c2ecf20Sopenharmony_ci				saw_error++;
888c2ecf20Sopenharmony_ci				printk(KERN_ERR "%s: STC_ERR(%d)[wr(%d)"
898c2ecf20Sopenharmony_ci				       "rd(%d)]\n",
908c2ecf20Sopenharmony_ci				       pbm->name,
918c2ecf20Sopenharmony_ci				       j,
928c2ecf20Sopenharmony_ci				       (errval & PSYCHO_STCERR_WRITE) ? 1 : 0,
938c2ecf20Sopenharmony_ci				       (errval & PSYCHO_STCERR_READ) ? 1 : 0);
948c2ecf20Sopenharmony_ci			}
958c2ecf20Sopenharmony_ci		}
968c2ecf20Sopenharmony_ci		if (saw_error != 0) {
978c2ecf20Sopenharmony_ci			u64 tagval = stc_tag_buf[i];
988c2ecf20Sopenharmony_ci			u64 lineval = stc_line_buf[i];
998c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: STC_TAG(%d)[PA(%016llx)VA(%08llx)"
1008c2ecf20Sopenharmony_ci			       "V(%d)W(%d)]\n",
1018c2ecf20Sopenharmony_ci			       pbm->name,
1028c2ecf20Sopenharmony_ci			       i,
1038c2ecf20Sopenharmony_ci			       ((tagval & PSYCHO_STCTAG_PPN) >> 19UL),
1048c2ecf20Sopenharmony_ci			       (tagval & PSYCHO_STCTAG_VPN),
1058c2ecf20Sopenharmony_ci			       ((tagval & PSYCHO_STCTAG_VALID) ? 1 : 0),
1068c2ecf20Sopenharmony_ci			       ((tagval & PSYCHO_STCTAG_WRITE) ? 1 : 0));
1078c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: STC_LINE(%d)[LIDX(%llx)SP(%llx)"
1088c2ecf20Sopenharmony_ci			       "LADDR(%llx)EP(%llx)V(%d)FOFN(%d)]\n",
1098c2ecf20Sopenharmony_ci			       pbm->name,
1108c2ecf20Sopenharmony_ci			       i,
1118c2ecf20Sopenharmony_ci			       ((lineval & PSYCHO_STCLINE_LINDX) >> 21UL),
1128c2ecf20Sopenharmony_ci			       ((lineval & PSYCHO_STCLINE_SPTR) >> 15UL),
1138c2ecf20Sopenharmony_ci			       ((lineval & PSYCHO_STCLINE_LADDR) >> 8UL),
1148c2ecf20Sopenharmony_ci			       ((lineval & PSYCHO_STCLINE_EPTR) >> 2UL),
1158c2ecf20Sopenharmony_ci			       ((lineval & PSYCHO_STCLINE_VALID) ? 1 : 0),
1168c2ecf20Sopenharmony_ci			       ((lineval & PSYCHO_STCLINE_FOFN) ? 1 : 0));
1178c2ecf20Sopenharmony_ci		}
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	spin_unlock(&stc_buf_lock);
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#define PSYCHO_IOMMU_TAG		0xa580UL
1248c2ecf20Sopenharmony_ci#define PSYCHO_IOMMU_DATA		0xa600UL
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic void psycho_record_iommu_tags_and_data(struct pci_pbm_info *pbm,
1278c2ecf20Sopenharmony_ci					      u64 *tag, u64 *data)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	int i;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++) {
1328c2ecf20Sopenharmony_ci		unsigned long base = pbm->controller_regs;
1338c2ecf20Sopenharmony_ci		unsigned long off = i * 8UL;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci		tag[i] = upa_readq(base + PSYCHO_IOMMU_TAG+off);
1368c2ecf20Sopenharmony_ci		data[i] = upa_readq(base + PSYCHO_IOMMU_DATA+off);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci		/* Now clear out the entry. */
1398c2ecf20Sopenharmony_ci		upa_writeq(0, base + PSYCHO_IOMMU_TAG + off);
1408c2ecf20Sopenharmony_ci		upa_writeq(0, base + PSYCHO_IOMMU_DATA + off);
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_TAG_ERRSTS (0x3UL << 23UL)
1458c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_TAG_ERR	 (0x1UL << 22UL)
1468c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_TAG_WRITE	 (0x1UL << 21UL)
1478c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_TAG_STREAM (0x1UL << 20UL)
1488c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_TAG_SIZE	 (0x1UL << 19UL)
1498c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_TAG_VPAGE	 0x7ffffULL
1508c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_DATA_VALID (1UL << 30UL)
1518c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_DATA_CACHE (1UL << 28UL)
1528c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_DATA_PPAGE 0xfffffffULL
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic void psycho_dump_iommu_tags_and_data(struct pci_pbm_info *pbm,
1558c2ecf20Sopenharmony_ci					    u64 *tag, u64 *data)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	int i;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++) {
1608c2ecf20Sopenharmony_ci		u64 tag_val, data_val;
1618c2ecf20Sopenharmony_ci		const char *type_str;
1628c2ecf20Sopenharmony_ci		tag_val = tag[i];
1638c2ecf20Sopenharmony_ci		if (!(tag_val & PSYCHO_IOMMU_TAG_ERR))
1648c2ecf20Sopenharmony_ci			continue;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci		data_val = data[i];
1678c2ecf20Sopenharmony_ci		switch((tag_val & PSYCHO_IOMMU_TAG_ERRSTS) >> 23UL) {
1688c2ecf20Sopenharmony_ci		case 0:
1698c2ecf20Sopenharmony_ci			type_str = "Protection Error";
1708c2ecf20Sopenharmony_ci			break;
1718c2ecf20Sopenharmony_ci		case 1:
1728c2ecf20Sopenharmony_ci			type_str = "Invalid Error";
1738c2ecf20Sopenharmony_ci			break;
1748c2ecf20Sopenharmony_ci		case 2:
1758c2ecf20Sopenharmony_ci			type_str = "TimeOut Error";
1768c2ecf20Sopenharmony_ci			break;
1778c2ecf20Sopenharmony_ci		case 3:
1788c2ecf20Sopenharmony_ci		default:
1798c2ecf20Sopenharmony_ci			type_str = "ECC Error";
1808c2ecf20Sopenharmony_ci			break;
1818c2ecf20Sopenharmony_ci		}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: IOMMU TAG(%d)[error(%s) wr(%d) "
1848c2ecf20Sopenharmony_ci		       "str(%d) sz(%dK) vpg(%08llx)]\n",
1858c2ecf20Sopenharmony_ci		       pbm->name, i, type_str,
1868c2ecf20Sopenharmony_ci		       ((tag_val & PSYCHO_IOMMU_TAG_WRITE) ? 1 : 0),
1878c2ecf20Sopenharmony_ci		       ((tag_val & PSYCHO_IOMMU_TAG_STREAM) ? 1 : 0),
1888c2ecf20Sopenharmony_ci		       ((tag_val & PSYCHO_IOMMU_TAG_SIZE) ? 64 : 8),
1898c2ecf20Sopenharmony_ci		       (tag_val & PSYCHO_IOMMU_TAG_VPAGE) << IOMMU_PAGE_SHIFT);
1908c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: IOMMU DATA(%d)[valid(%d) cache(%d) "
1918c2ecf20Sopenharmony_ci		       "ppg(%016llx)]\n",
1928c2ecf20Sopenharmony_ci		       pbm->name, i,
1938c2ecf20Sopenharmony_ci		       ((data_val & PSYCHO_IOMMU_DATA_VALID) ? 1 : 0),
1948c2ecf20Sopenharmony_ci		       ((data_val & PSYCHO_IOMMU_DATA_CACHE) ? 1 : 0),
1958c2ecf20Sopenharmony_ci		       (data_val & PSYCHO_IOMMU_DATA_PPAGE) << IOMMU_PAGE_SHIFT);
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_CTRL_XLTESTAT	0x0000000006000000UL
2008c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_CTRL_XLTEERR	0x0000000001000000UL
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_civoid psycho_check_iommu_error(struct pci_pbm_info *pbm,
2038c2ecf20Sopenharmony_ci			      unsigned long afsr,
2048c2ecf20Sopenharmony_ci			      unsigned long afar,
2058c2ecf20Sopenharmony_ci			      enum psycho_error_type type)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	u64 control, iommu_tag[16], iommu_data[16];
2088c2ecf20Sopenharmony_ci	struct iommu *iommu = pbm->iommu;
2098c2ecf20Sopenharmony_ci	unsigned long flags;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	spin_lock_irqsave(&iommu->lock, flags);
2128c2ecf20Sopenharmony_ci	control = upa_readq(iommu->iommu_control);
2138c2ecf20Sopenharmony_ci	if (control & PSYCHO_IOMMU_CTRL_XLTEERR) {
2148c2ecf20Sopenharmony_ci		const char *type_str;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci		control &= ~PSYCHO_IOMMU_CTRL_XLTEERR;
2178c2ecf20Sopenharmony_ci		upa_writeq(control, iommu->iommu_control);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		switch ((control & PSYCHO_IOMMU_CTRL_XLTESTAT) >> 25UL) {
2208c2ecf20Sopenharmony_ci		case 0:
2218c2ecf20Sopenharmony_ci			type_str = "Protection Error";
2228c2ecf20Sopenharmony_ci			break;
2238c2ecf20Sopenharmony_ci		case 1:
2248c2ecf20Sopenharmony_ci			type_str = "Invalid Error";
2258c2ecf20Sopenharmony_ci			break;
2268c2ecf20Sopenharmony_ci		case 2:
2278c2ecf20Sopenharmony_ci			type_str = "TimeOut Error";
2288c2ecf20Sopenharmony_ci			break;
2298c2ecf20Sopenharmony_ci		case 3:
2308c2ecf20Sopenharmony_ci		default:
2318c2ecf20Sopenharmony_ci			type_str = "ECC Error";
2328c2ecf20Sopenharmony_ci			break;
2338c2ecf20Sopenharmony_ci		}
2348c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: IOMMU Error, type[%s]\n",
2358c2ecf20Sopenharmony_ci		       pbm->name, type_str);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci		/* It is very possible for another DVMA to occur while
2388c2ecf20Sopenharmony_ci		 * we do this probe, and corrupt the system further.
2398c2ecf20Sopenharmony_ci		 * But we are so screwed at this point that we are
2408c2ecf20Sopenharmony_ci		 * likely to crash hard anyways, so get as much
2418c2ecf20Sopenharmony_ci		 * diagnostic information to the console as we can.
2428c2ecf20Sopenharmony_ci		 */
2438c2ecf20Sopenharmony_ci		psycho_record_iommu_tags_and_data(pbm, iommu_tag, iommu_data);
2448c2ecf20Sopenharmony_ci		psycho_dump_iommu_tags_and_data(pbm, iommu_tag, iommu_data);
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci	psycho_check_stc_error(pbm);
2478c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&iommu->lock, flags);
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci#define  PSYCHO_PCICTRL_SBH_ERR	 0x0000000800000000UL
2518c2ecf20Sopenharmony_ci#define  PSYCHO_PCICTRL_SERR	 0x0000000400000000UL
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic irqreturn_t psycho_pcierr_intr_other(struct pci_pbm_info *pbm)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
2568c2ecf20Sopenharmony_ci	u64 csr, csr_error_bits;
2578c2ecf20Sopenharmony_ci	u16 stat, *addr;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	csr = upa_readq(pbm->pci_csr);
2608c2ecf20Sopenharmony_ci	csr_error_bits = csr & (PSYCHO_PCICTRL_SBH_ERR | PSYCHO_PCICTRL_SERR);
2618c2ecf20Sopenharmony_ci	if (csr_error_bits) {
2628c2ecf20Sopenharmony_ci		/* Clear the errors.  */
2638c2ecf20Sopenharmony_ci		upa_writeq(csr, pbm->pci_csr);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci		/* Log 'em.  */
2668c2ecf20Sopenharmony_ci		if (csr_error_bits & PSYCHO_PCICTRL_SBH_ERR)
2678c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: PCI streaming byte hole "
2688c2ecf20Sopenharmony_ci			       "error asserted.\n", pbm->name);
2698c2ecf20Sopenharmony_ci		if (csr_error_bits & PSYCHO_PCICTRL_SERR)
2708c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: PCI SERR signal asserted.\n",
2718c2ecf20Sopenharmony_ci			       pbm->name);
2728c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci	addr = psycho_pci_config_mkaddr(pbm, pbm->pci_first_busno,
2758c2ecf20Sopenharmony_ci					0, PCI_STATUS);
2768c2ecf20Sopenharmony_ci	pci_config_read16(addr, &stat);
2778c2ecf20Sopenharmony_ci	if (stat & (PCI_STATUS_PARITY |
2788c2ecf20Sopenharmony_ci		    PCI_STATUS_SIG_TARGET_ABORT |
2798c2ecf20Sopenharmony_ci		    PCI_STATUS_REC_TARGET_ABORT |
2808c2ecf20Sopenharmony_ci		    PCI_STATUS_REC_MASTER_ABORT |
2818c2ecf20Sopenharmony_ci		    PCI_STATUS_SIG_SYSTEM_ERROR)) {
2828c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: PCI bus error, PCI_STATUS[%04x]\n",
2838c2ecf20Sopenharmony_ci		       pbm->name, stat);
2848c2ecf20Sopenharmony_ci		pci_config_write16(addr, 0xffff);
2858c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci	return ret;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci#define  PSYCHO_PCIAFSR_PMA	0x8000000000000000ULL
2918c2ecf20Sopenharmony_ci#define  PSYCHO_PCIAFSR_PTA	0x4000000000000000ULL
2928c2ecf20Sopenharmony_ci#define  PSYCHO_PCIAFSR_PRTRY	0x2000000000000000ULL
2938c2ecf20Sopenharmony_ci#define  PSYCHO_PCIAFSR_PPERR	0x1000000000000000ULL
2948c2ecf20Sopenharmony_ci#define  PSYCHO_PCIAFSR_SMA	0x0800000000000000ULL
2958c2ecf20Sopenharmony_ci#define  PSYCHO_PCIAFSR_STA	0x0400000000000000ULL
2968c2ecf20Sopenharmony_ci#define  PSYCHO_PCIAFSR_SRTRY	0x0200000000000000ULL
2978c2ecf20Sopenharmony_ci#define  PSYCHO_PCIAFSR_SPERR	0x0100000000000000ULL
2988c2ecf20Sopenharmony_ci#define  PSYCHO_PCIAFSR_RESV1	0x00ff000000000000ULL
2998c2ecf20Sopenharmony_ci#define  PSYCHO_PCIAFSR_BMSK	0x0000ffff00000000ULL
3008c2ecf20Sopenharmony_ci#define  PSYCHO_PCIAFSR_BLK	0x0000000080000000ULL
3018c2ecf20Sopenharmony_ci#define  PSYCHO_PCIAFSR_RESV2	0x0000000040000000ULL
3028c2ecf20Sopenharmony_ci#define  PSYCHO_PCIAFSR_MID	0x000000003e000000ULL
3038c2ecf20Sopenharmony_ci#define  PSYCHO_PCIAFSR_RESV3	0x0000000001ffffffULL
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ciirqreturn_t psycho_pcierr_intr(int irq, void *dev_id)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct pci_pbm_info *pbm = dev_id;
3088c2ecf20Sopenharmony_ci	u64 afsr, afar, error_bits;
3098c2ecf20Sopenharmony_ci	int reported;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	afsr = upa_readq(pbm->pci_afsr);
3128c2ecf20Sopenharmony_ci	afar = upa_readq(pbm->pci_afar);
3138c2ecf20Sopenharmony_ci	error_bits = afsr &
3148c2ecf20Sopenharmony_ci		(PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_PTA |
3158c2ecf20Sopenharmony_ci		 PSYCHO_PCIAFSR_PRTRY | PSYCHO_PCIAFSR_PPERR |
3168c2ecf20Sopenharmony_ci		 PSYCHO_PCIAFSR_SMA | PSYCHO_PCIAFSR_STA |
3178c2ecf20Sopenharmony_ci		 PSYCHO_PCIAFSR_SRTRY | PSYCHO_PCIAFSR_SPERR);
3188c2ecf20Sopenharmony_ci	if (!error_bits)
3198c2ecf20Sopenharmony_ci		return psycho_pcierr_intr_other(pbm);
3208c2ecf20Sopenharmony_ci	upa_writeq(error_bits, pbm->pci_afsr);
3218c2ecf20Sopenharmony_ci	printk(KERN_ERR "%s: PCI Error, primary error type[%s]\n",
3228c2ecf20Sopenharmony_ci	       pbm->name,
3238c2ecf20Sopenharmony_ci	       (((error_bits & PSYCHO_PCIAFSR_PMA) ?
3248c2ecf20Sopenharmony_ci		 "Master Abort" :
3258c2ecf20Sopenharmony_ci		 ((error_bits & PSYCHO_PCIAFSR_PTA) ?
3268c2ecf20Sopenharmony_ci		  "Target Abort" :
3278c2ecf20Sopenharmony_ci		  ((error_bits & PSYCHO_PCIAFSR_PRTRY) ?
3288c2ecf20Sopenharmony_ci		   "Excessive Retries" :
3298c2ecf20Sopenharmony_ci		   ((error_bits & PSYCHO_PCIAFSR_PPERR) ?
3308c2ecf20Sopenharmony_ci		    "Parity Error" : "???"))))));
3318c2ecf20Sopenharmony_ci	printk(KERN_ERR "%s: bytemask[%04llx] UPA_MID[%02llx] was_block(%d)\n",
3328c2ecf20Sopenharmony_ci	       pbm->name,
3338c2ecf20Sopenharmony_ci	       (afsr & PSYCHO_PCIAFSR_BMSK) >> 32UL,
3348c2ecf20Sopenharmony_ci	       (afsr & PSYCHO_PCIAFSR_MID) >> 25UL,
3358c2ecf20Sopenharmony_ci	       (afsr & PSYCHO_PCIAFSR_BLK) ? 1 : 0);
3368c2ecf20Sopenharmony_ci	printk(KERN_ERR "%s: PCI AFAR [%016llx]\n", pbm->name, afar);
3378c2ecf20Sopenharmony_ci	printk(KERN_ERR "%s: PCI Secondary errors [", pbm->name);
3388c2ecf20Sopenharmony_ci	reported = 0;
3398c2ecf20Sopenharmony_ci	if (afsr & PSYCHO_PCIAFSR_SMA) {
3408c2ecf20Sopenharmony_ci		reported++;
3418c2ecf20Sopenharmony_ci		printk("(Master Abort)");
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci	if (afsr & PSYCHO_PCIAFSR_STA) {
3448c2ecf20Sopenharmony_ci		reported++;
3458c2ecf20Sopenharmony_ci		printk("(Target Abort)");
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci	if (afsr & PSYCHO_PCIAFSR_SRTRY) {
3488c2ecf20Sopenharmony_ci		reported++;
3498c2ecf20Sopenharmony_ci		printk("(Excessive Retries)");
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci	if (afsr & PSYCHO_PCIAFSR_SPERR) {
3528c2ecf20Sopenharmony_ci		reported++;
3538c2ecf20Sopenharmony_ci		printk("(Parity Error)");
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci	if (!reported)
3568c2ecf20Sopenharmony_ci		printk("(none)");
3578c2ecf20Sopenharmony_ci	printk("]\n");
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (error_bits & (PSYCHO_PCIAFSR_PTA | PSYCHO_PCIAFSR_STA)) {
3608c2ecf20Sopenharmony_ci		psycho_check_iommu_error(pbm, afsr, afar, PCI_ERR);
3618c2ecf20Sopenharmony_ci		pci_scan_for_target_abort(pbm, pbm->pci_bus);
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci	if (error_bits & (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_SMA))
3648c2ecf20Sopenharmony_ci		pci_scan_for_master_abort(pbm, pbm->pci_bus);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (error_bits & (PSYCHO_PCIAFSR_PPERR | PSYCHO_PCIAFSR_SPERR))
3678c2ecf20Sopenharmony_ci		pci_scan_for_parity_error(pbm, pbm->pci_bus);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic void psycho_iommu_flush(struct pci_pbm_info *pbm)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	int i;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++) {
3778c2ecf20Sopenharmony_ci		unsigned long off = i * 8;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci		upa_writeq(0, pbm->controller_regs + PSYCHO_IOMMU_TAG + off);
3808c2ecf20Sopenharmony_ci		upa_writeq(0, pbm->controller_regs + PSYCHO_IOMMU_DATA + off);
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci#define PSYCHO_IOMMU_CONTROL		0x0200UL
3858c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_CTRL_TSBSZ	0x0000000000070000UL
3868c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_TSBSZ_1K      	0x0000000000000000UL
3878c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_TSBSZ_2K      	0x0000000000010000UL
3888c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_TSBSZ_4K      	0x0000000000020000UL
3898c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_TSBSZ_8K      	0x0000000000030000UL
3908c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_TSBSZ_16K     	0x0000000000040000UL
3918c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_TSBSZ_32K     	0x0000000000050000UL
3928c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_TSBSZ_64K     	0x0000000000060000UL
3938c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_TSBSZ_128K    	0x0000000000070000UL
3948c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_CTRL_TBWSZ    	0x0000000000000004UL
3958c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_CTRL_DENAB    	0x0000000000000002UL
3968c2ecf20Sopenharmony_ci#define  PSYCHO_IOMMU_CTRL_ENAB     	0x0000000000000001UL
3978c2ecf20Sopenharmony_ci#define PSYCHO_IOMMU_FLUSH		0x0210UL
3988c2ecf20Sopenharmony_ci#define PSYCHO_IOMMU_TSBBASE		0x0208UL
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ciint psycho_iommu_init(struct pci_pbm_info *pbm, int tsbsize,
4018c2ecf20Sopenharmony_ci		      u32 dvma_offset, u32 dma_mask,
4028c2ecf20Sopenharmony_ci		      unsigned long write_complete_offset)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	struct iommu *iommu = pbm->iommu;
4058c2ecf20Sopenharmony_ci	u64 control;
4068c2ecf20Sopenharmony_ci	int err;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	iommu->iommu_control  = pbm->controller_regs + PSYCHO_IOMMU_CONTROL;
4098c2ecf20Sopenharmony_ci	iommu->iommu_tsbbase  = pbm->controller_regs + PSYCHO_IOMMU_TSBBASE;
4108c2ecf20Sopenharmony_ci	iommu->iommu_flush    = pbm->controller_regs + PSYCHO_IOMMU_FLUSH;
4118c2ecf20Sopenharmony_ci	iommu->iommu_tags     = pbm->controller_regs + PSYCHO_IOMMU_TAG;
4128c2ecf20Sopenharmony_ci	iommu->write_complete_reg = (pbm->controller_regs +
4138c2ecf20Sopenharmony_ci				     write_complete_offset);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	iommu->iommu_ctxflush = 0;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	control = upa_readq(iommu->iommu_control);
4188c2ecf20Sopenharmony_ci	control |= PSYCHO_IOMMU_CTRL_DENAB;
4198c2ecf20Sopenharmony_ci	upa_writeq(control, iommu->iommu_control);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	psycho_iommu_flush(pbm);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	/* Leave diag mode enabled for full-flushing done in pci_iommu.c */
4248c2ecf20Sopenharmony_ci	err = iommu_table_init(iommu, tsbsize * 1024 * 8,
4258c2ecf20Sopenharmony_ci			       dvma_offset, dma_mask, pbm->numa_node);
4268c2ecf20Sopenharmony_ci	if (err)
4278c2ecf20Sopenharmony_ci		return err;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	upa_writeq(__pa(iommu->page_table), iommu->iommu_tsbbase);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	control = upa_readq(iommu->iommu_control);
4328c2ecf20Sopenharmony_ci	control &= ~(PSYCHO_IOMMU_CTRL_TSBSZ | PSYCHO_IOMMU_CTRL_TBWSZ);
4338c2ecf20Sopenharmony_ci	control |= PSYCHO_IOMMU_CTRL_ENAB;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	switch (tsbsize) {
4368c2ecf20Sopenharmony_ci	case 64:
4378c2ecf20Sopenharmony_ci		control |= PSYCHO_IOMMU_TSBSZ_64K;
4388c2ecf20Sopenharmony_ci		break;
4398c2ecf20Sopenharmony_ci	case 128:
4408c2ecf20Sopenharmony_ci		control |= PSYCHO_IOMMU_TSBSZ_128K;
4418c2ecf20Sopenharmony_ci		break;
4428c2ecf20Sopenharmony_ci	default:
4438c2ecf20Sopenharmony_ci		return -EINVAL;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	upa_writeq(control, iommu->iommu_control);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	return 0;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_civoid psycho_pbm_init_common(struct pci_pbm_info *pbm, struct platform_device *op,
4538c2ecf20Sopenharmony_ci			    const char *chip_name, int chip_type)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	struct device_node *dp = op->dev.of_node;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	pbm->name = dp->full_name;
4588c2ecf20Sopenharmony_ci	pbm->numa_node = NUMA_NO_NODE;
4598c2ecf20Sopenharmony_ci	pbm->chip_type = chip_type;
4608c2ecf20Sopenharmony_ci	pbm->chip_version = of_getintprop_default(dp, "version#", 0);
4618c2ecf20Sopenharmony_ci	pbm->chip_revision = of_getintprop_default(dp, "module-revision#", 0);
4628c2ecf20Sopenharmony_ci	pbm->op = op;
4638c2ecf20Sopenharmony_ci	pbm->pci_ops = &sun4u_pci_ops;
4648c2ecf20Sopenharmony_ci	pbm->config_space_reg_bits = 8;
4658c2ecf20Sopenharmony_ci	pbm->index = pci_num_pbms++;
4668c2ecf20Sopenharmony_ci	pci_get_pbm_props(pbm);
4678c2ecf20Sopenharmony_ci	pci_determine_mem_io_space(pbm);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	printk(KERN_INFO "%s: %s PCI Bus Module ver[%x:%x]\n",
4708c2ecf20Sopenharmony_ci	       pbm->name, chip_name,
4718c2ecf20Sopenharmony_ci	       pbm->chip_version, pbm->chip_revision);
4728c2ecf20Sopenharmony_ci}
473