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