18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * cpc925_edac.c, EDAC driver for IBM CPC925 Bridge and Memory Controller. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2008 Wind River Systems, Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: Cao Qingtao <qingtao.cao@windriver.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/edac.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/gfp.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "edac_module.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define CPC925_EDAC_REVISION " Ver: 1.0.0" 218c2ecf20Sopenharmony_ci#define CPC925_EDAC_MOD_STR "cpc925_edac" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define cpc925_printk(level, fmt, arg...) \ 248c2ecf20Sopenharmony_ci edac_printk(level, "CPC925", fmt, ##arg) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define cpc925_mc_printk(mci, level, fmt, arg...) \ 278c2ecf20Sopenharmony_ci edac_mc_chipset_printk(mci, level, "CPC925", fmt, ##arg) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * CPC925 registers are of 32 bits with bit0 defined at the 318c2ecf20Sopenharmony_ci * most significant bit and bit31 at that of least significant. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci#define CPC925_BITS_PER_REG 32 348c2ecf20Sopenharmony_ci#define CPC925_BIT(nr) (1UL << (CPC925_BITS_PER_REG - 1 - nr)) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * EDAC device names for the error detections of 388c2ecf20Sopenharmony_ci * CPU Interface and Hypertransport Link. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci#define CPC925_CPU_ERR_DEV "cpu" 418c2ecf20Sopenharmony_ci#define CPC925_HT_LINK_DEV "htlink" 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Suppose DDR Refresh cycle is 15.6 microsecond */ 448c2ecf20Sopenharmony_ci#define CPC925_REF_FREQ 0xFA69 458c2ecf20Sopenharmony_ci#define CPC925_SCRUB_BLOCK_SIZE 64 /* bytes */ 468c2ecf20Sopenharmony_ci#define CPC925_NR_CSROWS 8 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * All registers and bits definitions are taken from 508c2ecf20Sopenharmony_ci * "CPC925 Bridge and Memory Controller User Manual, SA14-2761-02". 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * CPU and Memory Controller Registers 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci/************************************************************ 578c2ecf20Sopenharmony_ci * Processor Interface Exception Mask Register (APIMASK) 588c2ecf20Sopenharmony_ci ************************************************************/ 598c2ecf20Sopenharmony_ci#define REG_APIMASK_OFFSET 0x30070 608c2ecf20Sopenharmony_cienum apimask_bits { 618c2ecf20Sopenharmony_ci APIMASK_DART = CPC925_BIT(0), /* DART Exception */ 628c2ecf20Sopenharmony_ci APIMASK_ADI0 = CPC925_BIT(1), /* Handshake Error on PI0_ADI */ 638c2ecf20Sopenharmony_ci APIMASK_ADI1 = CPC925_BIT(2), /* Handshake Error on PI1_ADI */ 648c2ecf20Sopenharmony_ci APIMASK_STAT = CPC925_BIT(3), /* Status Exception */ 658c2ecf20Sopenharmony_ci APIMASK_DERR = CPC925_BIT(4), /* Data Error Exception */ 668c2ecf20Sopenharmony_ci APIMASK_ADRS0 = CPC925_BIT(5), /* Addressing Exception on PI0 */ 678c2ecf20Sopenharmony_ci APIMASK_ADRS1 = CPC925_BIT(6), /* Addressing Exception on PI1 */ 688c2ecf20Sopenharmony_ci /* BIT(7) Reserved */ 698c2ecf20Sopenharmony_ci APIMASK_ECC_UE_H = CPC925_BIT(8), /* UECC upper */ 708c2ecf20Sopenharmony_ci APIMASK_ECC_CE_H = CPC925_BIT(9), /* CECC upper */ 718c2ecf20Sopenharmony_ci APIMASK_ECC_UE_L = CPC925_BIT(10), /* UECC lower */ 728c2ecf20Sopenharmony_ci APIMASK_ECC_CE_L = CPC925_BIT(11), /* CECC lower */ 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci CPU_MASK_ENABLE = (APIMASK_DART | APIMASK_ADI0 | APIMASK_ADI1 | 758c2ecf20Sopenharmony_ci APIMASK_STAT | APIMASK_DERR | APIMASK_ADRS0 | 768c2ecf20Sopenharmony_ci APIMASK_ADRS1), 778c2ecf20Sopenharmony_ci ECC_MASK_ENABLE = (APIMASK_ECC_UE_H | APIMASK_ECC_CE_H | 788c2ecf20Sopenharmony_ci APIMASK_ECC_UE_L | APIMASK_ECC_CE_L), 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci#define APIMASK_ADI(n) CPC925_BIT(((n)+1)) 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/************************************************************ 838c2ecf20Sopenharmony_ci * Processor Interface Exception Register (APIEXCP) 848c2ecf20Sopenharmony_ci ************************************************************/ 858c2ecf20Sopenharmony_ci#define REG_APIEXCP_OFFSET 0x30060 868c2ecf20Sopenharmony_cienum apiexcp_bits { 878c2ecf20Sopenharmony_ci APIEXCP_DART = CPC925_BIT(0), /* DART Exception */ 888c2ecf20Sopenharmony_ci APIEXCP_ADI0 = CPC925_BIT(1), /* Handshake Error on PI0_ADI */ 898c2ecf20Sopenharmony_ci APIEXCP_ADI1 = CPC925_BIT(2), /* Handshake Error on PI1_ADI */ 908c2ecf20Sopenharmony_ci APIEXCP_STAT = CPC925_BIT(3), /* Status Exception */ 918c2ecf20Sopenharmony_ci APIEXCP_DERR = CPC925_BIT(4), /* Data Error Exception */ 928c2ecf20Sopenharmony_ci APIEXCP_ADRS0 = CPC925_BIT(5), /* Addressing Exception on PI0 */ 938c2ecf20Sopenharmony_ci APIEXCP_ADRS1 = CPC925_BIT(6), /* Addressing Exception on PI1 */ 948c2ecf20Sopenharmony_ci /* BIT(7) Reserved */ 958c2ecf20Sopenharmony_ci APIEXCP_ECC_UE_H = CPC925_BIT(8), /* UECC upper */ 968c2ecf20Sopenharmony_ci APIEXCP_ECC_CE_H = CPC925_BIT(9), /* CECC upper */ 978c2ecf20Sopenharmony_ci APIEXCP_ECC_UE_L = CPC925_BIT(10), /* UECC lower */ 988c2ecf20Sopenharmony_ci APIEXCP_ECC_CE_L = CPC925_BIT(11), /* CECC lower */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci CPU_EXCP_DETECTED = (APIEXCP_DART | APIEXCP_ADI0 | APIEXCP_ADI1 | 1018c2ecf20Sopenharmony_ci APIEXCP_STAT | APIEXCP_DERR | APIEXCP_ADRS0 | 1028c2ecf20Sopenharmony_ci APIEXCP_ADRS1), 1038c2ecf20Sopenharmony_ci UECC_EXCP_DETECTED = (APIEXCP_ECC_UE_H | APIEXCP_ECC_UE_L), 1048c2ecf20Sopenharmony_ci CECC_EXCP_DETECTED = (APIEXCP_ECC_CE_H | APIEXCP_ECC_CE_L), 1058c2ecf20Sopenharmony_ci ECC_EXCP_DETECTED = (UECC_EXCP_DETECTED | CECC_EXCP_DETECTED), 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/************************************************************ 1098c2ecf20Sopenharmony_ci * Memory Bus Configuration Register (MBCR) 1108c2ecf20Sopenharmony_ci************************************************************/ 1118c2ecf20Sopenharmony_ci#define REG_MBCR_OFFSET 0x2190 1128c2ecf20Sopenharmony_ci#define MBCR_64BITCFG_SHIFT 23 1138c2ecf20Sopenharmony_ci#define MBCR_64BITCFG_MASK (1UL << MBCR_64BITCFG_SHIFT) 1148c2ecf20Sopenharmony_ci#define MBCR_64BITBUS_SHIFT 22 1158c2ecf20Sopenharmony_ci#define MBCR_64BITBUS_MASK (1UL << MBCR_64BITBUS_SHIFT) 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/************************************************************ 1188c2ecf20Sopenharmony_ci * Memory Bank Mode Register (MBMR) 1198c2ecf20Sopenharmony_ci************************************************************/ 1208c2ecf20Sopenharmony_ci#define REG_MBMR_OFFSET 0x21C0 1218c2ecf20Sopenharmony_ci#define MBMR_MODE_MAX_VALUE 0xF 1228c2ecf20Sopenharmony_ci#define MBMR_MODE_SHIFT 25 1238c2ecf20Sopenharmony_ci#define MBMR_MODE_MASK (MBMR_MODE_MAX_VALUE << MBMR_MODE_SHIFT) 1248c2ecf20Sopenharmony_ci#define MBMR_BBA_SHIFT 24 1258c2ecf20Sopenharmony_ci#define MBMR_BBA_MASK (1UL << MBMR_BBA_SHIFT) 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/************************************************************ 1288c2ecf20Sopenharmony_ci * Memory Bank Boundary Address Register (MBBAR) 1298c2ecf20Sopenharmony_ci ************************************************************/ 1308c2ecf20Sopenharmony_ci#define REG_MBBAR_OFFSET 0x21D0 1318c2ecf20Sopenharmony_ci#define MBBAR_BBA_MAX_VALUE 0xFF 1328c2ecf20Sopenharmony_ci#define MBBAR_BBA_SHIFT 24 1338c2ecf20Sopenharmony_ci#define MBBAR_BBA_MASK (MBBAR_BBA_MAX_VALUE << MBBAR_BBA_SHIFT) 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/************************************************************ 1368c2ecf20Sopenharmony_ci * Memory Scrub Control Register (MSCR) 1378c2ecf20Sopenharmony_ci ************************************************************/ 1388c2ecf20Sopenharmony_ci#define REG_MSCR_OFFSET 0x2400 1398c2ecf20Sopenharmony_ci#define MSCR_SCRUB_MOD_MASK 0xC0000000 /* scrub_mod - bit0:1*/ 1408c2ecf20Sopenharmony_ci#define MSCR_BACKGR_SCRUB 0x40000000 /* 01 */ 1418c2ecf20Sopenharmony_ci#define MSCR_SI_SHIFT 16 /* si - bit8:15*/ 1428c2ecf20Sopenharmony_ci#define MSCR_SI_MAX_VALUE 0xFF 1438c2ecf20Sopenharmony_ci#define MSCR_SI_MASK (MSCR_SI_MAX_VALUE << MSCR_SI_SHIFT) 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/************************************************************ 1468c2ecf20Sopenharmony_ci * Memory Scrub Range Start Register (MSRSR) 1478c2ecf20Sopenharmony_ci ************************************************************/ 1488c2ecf20Sopenharmony_ci#define REG_MSRSR_OFFSET 0x2410 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/************************************************************ 1518c2ecf20Sopenharmony_ci * Memory Scrub Range End Register (MSRER) 1528c2ecf20Sopenharmony_ci ************************************************************/ 1538c2ecf20Sopenharmony_ci#define REG_MSRER_OFFSET 0x2420 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/************************************************************ 1568c2ecf20Sopenharmony_ci * Memory Scrub Pattern Register (MSPR) 1578c2ecf20Sopenharmony_ci ************************************************************/ 1588c2ecf20Sopenharmony_ci#define REG_MSPR_OFFSET 0x2430 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/************************************************************ 1618c2ecf20Sopenharmony_ci * Memory Check Control Register (MCCR) 1628c2ecf20Sopenharmony_ci ************************************************************/ 1638c2ecf20Sopenharmony_ci#define REG_MCCR_OFFSET 0x2440 1648c2ecf20Sopenharmony_cienum mccr_bits { 1658c2ecf20Sopenharmony_ci MCCR_ECC_EN = CPC925_BIT(0), /* ECC high and low check */ 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/************************************************************ 1698c2ecf20Sopenharmony_ci * Memory Check Range End Register (MCRER) 1708c2ecf20Sopenharmony_ci ************************************************************/ 1718c2ecf20Sopenharmony_ci#define REG_MCRER_OFFSET 0x2450 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/************************************************************ 1748c2ecf20Sopenharmony_ci * Memory Error Address Register (MEAR) 1758c2ecf20Sopenharmony_ci ************************************************************/ 1768c2ecf20Sopenharmony_ci#define REG_MEAR_OFFSET 0x2460 1778c2ecf20Sopenharmony_ci#define MEAR_BCNT_MAX_VALUE 0x3 1788c2ecf20Sopenharmony_ci#define MEAR_BCNT_SHIFT 30 1798c2ecf20Sopenharmony_ci#define MEAR_BCNT_MASK (MEAR_BCNT_MAX_VALUE << MEAR_BCNT_SHIFT) 1808c2ecf20Sopenharmony_ci#define MEAR_RANK_MAX_VALUE 0x7 1818c2ecf20Sopenharmony_ci#define MEAR_RANK_SHIFT 27 1828c2ecf20Sopenharmony_ci#define MEAR_RANK_MASK (MEAR_RANK_MAX_VALUE << MEAR_RANK_SHIFT) 1838c2ecf20Sopenharmony_ci#define MEAR_COL_MAX_VALUE 0x7FF 1848c2ecf20Sopenharmony_ci#define MEAR_COL_SHIFT 16 1858c2ecf20Sopenharmony_ci#define MEAR_COL_MASK (MEAR_COL_MAX_VALUE << MEAR_COL_SHIFT) 1868c2ecf20Sopenharmony_ci#define MEAR_BANK_MAX_VALUE 0x3 1878c2ecf20Sopenharmony_ci#define MEAR_BANK_SHIFT 14 1888c2ecf20Sopenharmony_ci#define MEAR_BANK_MASK (MEAR_BANK_MAX_VALUE << MEAR_BANK_SHIFT) 1898c2ecf20Sopenharmony_ci#define MEAR_ROW_MASK 0x00003FFF 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/************************************************************ 1928c2ecf20Sopenharmony_ci * Memory Error Syndrome Register (MESR) 1938c2ecf20Sopenharmony_ci ************************************************************/ 1948c2ecf20Sopenharmony_ci#define REG_MESR_OFFSET 0x2470 1958c2ecf20Sopenharmony_ci#define MESR_ECC_SYN_H_MASK 0xFF00 1968c2ecf20Sopenharmony_ci#define MESR_ECC_SYN_L_MASK 0x00FF 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/************************************************************ 1998c2ecf20Sopenharmony_ci * Memory Mode Control Register (MMCR) 2008c2ecf20Sopenharmony_ci ************************************************************/ 2018c2ecf20Sopenharmony_ci#define REG_MMCR_OFFSET 0x2500 2028c2ecf20Sopenharmony_cienum mmcr_bits { 2038c2ecf20Sopenharmony_ci MMCR_REG_DIMM_MODE = CPC925_BIT(3), 2048c2ecf20Sopenharmony_ci}; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* 2078c2ecf20Sopenharmony_ci * HyperTransport Link Registers 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_ci/************************************************************ 2108c2ecf20Sopenharmony_ci * Error Handling/Enumeration Scratch Pad Register (ERRCTRL) 2118c2ecf20Sopenharmony_ci ************************************************************/ 2128c2ecf20Sopenharmony_ci#define REG_ERRCTRL_OFFSET 0x70140 2138c2ecf20Sopenharmony_cienum errctrl_bits { /* nonfatal interrupts for */ 2148c2ecf20Sopenharmony_ci ERRCTRL_SERR_NF = CPC925_BIT(0), /* system error */ 2158c2ecf20Sopenharmony_ci ERRCTRL_CRC_NF = CPC925_BIT(1), /* CRC error */ 2168c2ecf20Sopenharmony_ci ERRCTRL_RSP_NF = CPC925_BIT(2), /* Response error */ 2178c2ecf20Sopenharmony_ci ERRCTRL_EOC_NF = CPC925_BIT(3), /* End-Of-Chain error */ 2188c2ecf20Sopenharmony_ci ERRCTRL_OVF_NF = CPC925_BIT(4), /* Overflow error */ 2198c2ecf20Sopenharmony_ci ERRCTRL_PROT_NF = CPC925_BIT(5), /* Protocol error */ 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci ERRCTRL_RSP_ERR = CPC925_BIT(6), /* Response error received */ 2228c2ecf20Sopenharmony_ci ERRCTRL_CHN_FAL = CPC925_BIT(7), /* Sync flooding detected */ 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci HT_ERRCTRL_ENABLE = (ERRCTRL_SERR_NF | ERRCTRL_CRC_NF | 2258c2ecf20Sopenharmony_ci ERRCTRL_RSP_NF | ERRCTRL_EOC_NF | 2268c2ecf20Sopenharmony_ci ERRCTRL_OVF_NF | ERRCTRL_PROT_NF), 2278c2ecf20Sopenharmony_ci HT_ERRCTRL_DETECTED = (ERRCTRL_RSP_ERR | ERRCTRL_CHN_FAL), 2288c2ecf20Sopenharmony_ci}; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/************************************************************ 2318c2ecf20Sopenharmony_ci * Link Configuration and Link Control Register (LINKCTRL) 2328c2ecf20Sopenharmony_ci ************************************************************/ 2338c2ecf20Sopenharmony_ci#define REG_LINKCTRL_OFFSET 0x70110 2348c2ecf20Sopenharmony_cienum linkctrl_bits { 2358c2ecf20Sopenharmony_ci LINKCTRL_CRC_ERR = (CPC925_BIT(22) | CPC925_BIT(23)), 2368c2ecf20Sopenharmony_ci LINKCTRL_LINK_FAIL = CPC925_BIT(27), 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci HT_LINKCTRL_DETECTED = (LINKCTRL_CRC_ERR | LINKCTRL_LINK_FAIL), 2398c2ecf20Sopenharmony_ci}; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci/************************************************************ 2428c2ecf20Sopenharmony_ci * Link FreqCap/Error/Freq/Revision ID Register (LINKERR) 2438c2ecf20Sopenharmony_ci ************************************************************/ 2448c2ecf20Sopenharmony_ci#define REG_LINKERR_OFFSET 0x70120 2458c2ecf20Sopenharmony_cienum linkerr_bits { 2468c2ecf20Sopenharmony_ci LINKERR_EOC_ERR = CPC925_BIT(17), /* End-Of-Chain error */ 2478c2ecf20Sopenharmony_ci LINKERR_OVF_ERR = CPC925_BIT(18), /* Receive Buffer Overflow */ 2488c2ecf20Sopenharmony_ci LINKERR_PROT_ERR = CPC925_BIT(19), /* Protocol error */ 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci HT_LINKERR_DETECTED = (LINKERR_EOC_ERR | LINKERR_OVF_ERR | 2518c2ecf20Sopenharmony_ci LINKERR_PROT_ERR), 2528c2ecf20Sopenharmony_ci}; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/************************************************************ 2558c2ecf20Sopenharmony_ci * Bridge Control Register (BRGCTRL) 2568c2ecf20Sopenharmony_ci ************************************************************/ 2578c2ecf20Sopenharmony_ci#define REG_BRGCTRL_OFFSET 0x70300 2588c2ecf20Sopenharmony_cienum brgctrl_bits { 2598c2ecf20Sopenharmony_ci BRGCTRL_DETSERR = CPC925_BIT(0), /* SERR on Secondary Bus */ 2608c2ecf20Sopenharmony_ci BRGCTRL_SECBUSRESET = CPC925_BIT(9), /* Secondary Bus Reset */ 2618c2ecf20Sopenharmony_ci}; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci/* Private structure for edac memory controller */ 2648c2ecf20Sopenharmony_cistruct cpc925_mc_pdata { 2658c2ecf20Sopenharmony_ci void __iomem *vbase; 2668c2ecf20Sopenharmony_ci unsigned long total_mem; 2678c2ecf20Sopenharmony_ci const char *name; 2688c2ecf20Sopenharmony_ci int edac_idx; 2698c2ecf20Sopenharmony_ci}; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/* Private structure for common edac device */ 2728c2ecf20Sopenharmony_cistruct cpc925_dev_info { 2738c2ecf20Sopenharmony_ci void __iomem *vbase; 2748c2ecf20Sopenharmony_ci struct platform_device *pdev; 2758c2ecf20Sopenharmony_ci char *ctl_name; 2768c2ecf20Sopenharmony_ci int edac_idx; 2778c2ecf20Sopenharmony_ci struct edac_device_ctl_info *edac_dev; 2788c2ecf20Sopenharmony_ci void (*init)(struct cpc925_dev_info *dev_info); 2798c2ecf20Sopenharmony_ci void (*exit)(struct cpc925_dev_info *dev_info); 2808c2ecf20Sopenharmony_ci void (*check)(struct edac_device_ctl_info *edac_dev); 2818c2ecf20Sopenharmony_ci}; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/* Get total memory size from Open Firmware DTB */ 2848c2ecf20Sopenharmony_cistatic void get_total_mem(struct cpc925_mc_pdata *pdata) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct device_node *np = NULL; 2878c2ecf20Sopenharmony_ci const unsigned int *reg, *reg_end; 2888c2ecf20Sopenharmony_ci int len, sw, aw; 2898c2ecf20Sopenharmony_ci unsigned long start, size; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci np = of_find_node_by_type(NULL, "memory"); 2928c2ecf20Sopenharmony_ci if (!np) 2938c2ecf20Sopenharmony_ci return; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci aw = of_n_addr_cells(np); 2968c2ecf20Sopenharmony_ci sw = of_n_size_cells(np); 2978c2ecf20Sopenharmony_ci reg = (const unsigned int *)of_get_property(np, "reg", &len); 2988c2ecf20Sopenharmony_ci reg_end = reg + len/4; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci pdata->total_mem = 0; 3018c2ecf20Sopenharmony_ci do { 3028c2ecf20Sopenharmony_ci start = of_read_number(reg, aw); 3038c2ecf20Sopenharmony_ci reg += aw; 3048c2ecf20Sopenharmony_ci size = of_read_number(reg, sw); 3058c2ecf20Sopenharmony_ci reg += sw; 3068c2ecf20Sopenharmony_ci edac_dbg(1, "start 0x%lx, size 0x%lx\n", start, size); 3078c2ecf20Sopenharmony_ci pdata->total_mem += size; 3088c2ecf20Sopenharmony_ci } while (reg < reg_end); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci of_node_put(np); 3118c2ecf20Sopenharmony_ci edac_dbg(0, "total_mem 0x%lx\n", pdata->total_mem); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic void cpc925_init_csrows(struct mem_ctl_info *mci) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct cpc925_mc_pdata *pdata = mci->pvt_info; 3178c2ecf20Sopenharmony_ci struct csrow_info *csrow; 3188c2ecf20Sopenharmony_ci struct dimm_info *dimm; 3198c2ecf20Sopenharmony_ci enum dev_type dtype; 3208c2ecf20Sopenharmony_ci int index, j; 3218c2ecf20Sopenharmony_ci u32 mbmr, mbbar, bba, grain; 3228c2ecf20Sopenharmony_ci unsigned long row_size, nr_pages, last_nr_pages = 0; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci get_total_mem(pdata); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci for (index = 0; index < mci->nr_csrows; index++) { 3278c2ecf20Sopenharmony_ci mbmr = __raw_readl(pdata->vbase + REG_MBMR_OFFSET + 3288c2ecf20Sopenharmony_ci 0x20 * index); 3298c2ecf20Sopenharmony_ci mbbar = __raw_readl(pdata->vbase + REG_MBBAR_OFFSET + 3308c2ecf20Sopenharmony_ci 0x20 + index); 3318c2ecf20Sopenharmony_ci bba = (((mbmr & MBMR_BBA_MASK) >> MBMR_BBA_SHIFT) << 8) | 3328c2ecf20Sopenharmony_ci ((mbbar & MBBAR_BBA_MASK) >> MBBAR_BBA_SHIFT); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (bba == 0) 3358c2ecf20Sopenharmony_ci continue; /* not populated */ 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci csrow = mci->csrows[index]; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci row_size = bba * (1UL << 28); /* 256M */ 3408c2ecf20Sopenharmony_ci csrow->first_page = last_nr_pages; 3418c2ecf20Sopenharmony_ci nr_pages = row_size >> PAGE_SHIFT; 3428c2ecf20Sopenharmony_ci csrow->last_page = csrow->first_page + nr_pages - 1; 3438c2ecf20Sopenharmony_ci last_nr_pages = csrow->last_page + 1; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci switch (csrow->nr_channels) { 3468c2ecf20Sopenharmony_ci case 1: /* Single channel */ 3478c2ecf20Sopenharmony_ci grain = 32; /* four-beat burst of 32 bytes */ 3488c2ecf20Sopenharmony_ci break; 3498c2ecf20Sopenharmony_ci case 2: /* Dual channel */ 3508c2ecf20Sopenharmony_ci default: 3518c2ecf20Sopenharmony_ci grain = 64; /* four-beat burst of 64 bytes */ 3528c2ecf20Sopenharmony_ci break; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci switch ((mbmr & MBMR_MODE_MASK) >> MBMR_MODE_SHIFT) { 3558c2ecf20Sopenharmony_ci case 6: /* 0110, no way to differentiate X8 VS X16 */ 3568c2ecf20Sopenharmony_ci case 5: /* 0101 */ 3578c2ecf20Sopenharmony_ci case 8: /* 1000 */ 3588c2ecf20Sopenharmony_ci dtype = DEV_X16; 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci case 7: /* 0111 */ 3618c2ecf20Sopenharmony_ci case 9: /* 1001 */ 3628c2ecf20Sopenharmony_ci dtype = DEV_X8; 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci default: 3658c2ecf20Sopenharmony_ci dtype = DEV_UNKNOWN; 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci for (j = 0; j < csrow->nr_channels; j++) { 3698c2ecf20Sopenharmony_ci dimm = csrow->channels[j]->dimm; 3708c2ecf20Sopenharmony_ci dimm->nr_pages = nr_pages / csrow->nr_channels; 3718c2ecf20Sopenharmony_ci dimm->mtype = MEM_RDDR; 3728c2ecf20Sopenharmony_ci dimm->edac_mode = EDAC_SECDED; 3738c2ecf20Sopenharmony_ci dimm->grain = grain; 3748c2ecf20Sopenharmony_ci dimm->dtype = dtype; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci/* Enable memory controller ECC detection */ 3808c2ecf20Sopenharmony_cistatic void cpc925_mc_init(struct mem_ctl_info *mci) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct cpc925_mc_pdata *pdata = mci->pvt_info; 3838c2ecf20Sopenharmony_ci u32 apimask; 3848c2ecf20Sopenharmony_ci u32 mccr; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* Enable various ECC error exceptions */ 3878c2ecf20Sopenharmony_ci apimask = __raw_readl(pdata->vbase + REG_APIMASK_OFFSET); 3888c2ecf20Sopenharmony_ci if ((apimask & ECC_MASK_ENABLE) == 0) { 3898c2ecf20Sopenharmony_ci apimask |= ECC_MASK_ENABLE; 3908c2ecf20Sopenharmony_ci __raw_writel(apimask, pdata->vbase + REG_APIMASK_OFFSET); 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* Enable ECC detection */ 3948c2ecf20Sopenharmony_ci mccr = __raw_readl(pdata->vbase + REG_MCCR_OFFSET); 3958c2ecf20Sopenharmony_ci if ((mccr & MCCR_ECC_EN) == 0) { 3968c2ecf20Sopenharmony_ci mccr |= MCCR_ECC_EN; 3978c2ecf20Sopenharmony_ci __raw_writel(mccr, pdata->vbase + REG_MCCR_OFFSET); 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci/* Disable memory controller ECC detection */ 4028c2ecf20Sopenharmony_cistatic void cpc925_mc_exit(struct mem_ctl_info *mci) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci /* 4058c2ecf20Sopenharmony_ci * WARNING: 4068c2ecf20Sopenharmony_ci * We are supposed to clear the ECC error detection bits, 4078c2ecf20Sopenharmony_ci * and it will be no problem to do so. However, once they 4088c2ecf20Sopenharmony_ci * are cleared here if we want to re-install CPC925 EDAC 4098c2ecf20Sopenharmony_ci * module later, setting them up in cpc925_mc_init() will 4108c2ecf20Sopenharmony_ci * trigger machine check exception. 4118c2ecf20Sopenharmony_ci * Also, it's ok to leave ECC error detection bits enabled, 4128c2ecf20Sopenharmony_ci * since they are reset to 1 by default or by boot loader. 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci return; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci/* 4198c2ecf20Sopenharmony_ci * Revert DDR column/row/bank addresses into page frame number and 4208c2ecf20Sopenharmony_ci * offset in page. 4218c2ecf20Sopenharmony_ci * 4228c2ecf20Sopenharmony_ci * Suppose memory mode is 0x0111(128-bit mode, identical DIMM pairs), 4238c2ecf20Sopenharmony_ci * physical address(PA) bits to column address(CA) bits mappings are: 4248c2ecf20Sopenharmony_ci * CA 0 1 2 3 4 5 6 7 8 9 10 4258c2ecf20Sopenharmony_ci * PA 59 58 57 56 55 54 53 52 51 50 49 4268c2ecf20Sopenharmony_ci * 4278c2ecf20Sopenharmony_ci * physical address(PA) bits to bank address(BA) bits mappings are: 4288c2ecf20Sopenharmony_ci * BA 0 1 4298c2ecf20Sopenharmony_ci * PA 43 44 4308c2ecf20Sopenharmony_ci * 4318c2ecf20Sopenharmony_ci * physical address(PA) bits to row address(RA) bits mappings are: 4328c2ecf20Sopenharmony_ci * RA 0 1 2 3 4 5 6 7 8 9 10 11 12 4338c2ecf20Sopenharmony_ci * PA 36 35 34 48 47 46 45 40 41 42 39 38 37 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_cistatic void cpc925_mc_get_pfn(struct mem_ctl_info *mci, u32 mear, 4368c2ecf20Sopenharmony_ci unsigned long *pfn, unsigned long *offset, int *csrow) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci u32 bcnt, rank, col, bank, row; 4398c2ecf20Sopenharmony_ci u32 c; 4408c2ecf20Sopenharmony_ci unsigned long pa; 4418c2ecf20Sopenharmony_ci int i; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci bcnt = (mear & MEAR_BCNT_MASK) >> MEAR_BCNT_SHIFT; 4448c2ecf20Sopenharmony_ci rank = (mear & MEAR_RANK_MASK) >> MEAR_RANK_SHIFT; 4458c2ecf20Sopenharmony_ci col = (mear & MEAR_COL_MASK) >> MEAR_COL_SHIFT; 4468c2ecf20Sopenharmony_ci bank = (mear & MEAR_BANK_MASK) >> MEAR_BANK_SHIFT; 4478c2ecf20Sopenharmony_ci row = mear & MEAR_ROW_MASK; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci *csrow = rank; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 4528c2ecf20Sopenharmony_ci if (mci->csrows[rank]->first_page == 0) { 4538c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_ERR, "ECC occurs in a " 4548c2ecf20Sopenharmony_ci "non-populated csrow, broken hardware?\n"); 4558c2ecf20Sopenharmony_ci return; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci#endif 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* Revert csrow number */ 4608c2ecf20Sopenharmony_ci pa = mci->csrows[rank]->first_page << PAGE_SHIFT; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* Revert column address */ 4638c2ecf20Sopenharmony_ci col += bcnt; 4648c2ecf20Sopenharmony_ci for (i = 0; i < 11; i++) { 4658c2ecf20Sopenharmony_ci c = col & 0x1; 4668c2ecf20Sopenharmony_ci col >>= 1; 4678c2ecf20Sopenharmony_ci pa |= c << (14 - i); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* Revert bank address */ 4718c2ecf20Sopenharmony_ci pa |= bank << 19; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* Revert row address, in 4 steps */ 4748c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 4758c2ecf20Sopenharmony_ci c = row & 0x1; 4768c2ecf20Sopenharmony_ci row >>= 1; 4778c2ecf20Sopenharmony_ci pa |= c << (26 - i); 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 4818c2ecf20Sopenharmony_ci c = row & 0x1; 4828c2ecf20Sopenharmony_ci row >>= 1; 4838c2ecf20Sopenharmony_ci pa |= c << (21 + i); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 4878c2ecf20Sopenharmony_ci c = row & 0x1; 4888c2ecf20Sopenharmony_ci row >>= 1; 4898c2ecf20Sopenharmony_ci pa |= c << (18 - i); 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 4938c2ecf20Sopenharmony_ci c = row & 0x1; 4948c2ecf20Sopenharmony_ci row >>= 1; 4958c2ecf20Sopenharmony_ci pa |= c << (29 - i); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci *offset = pa & (PAGE_SIZE - 1); 4998c2ecf20Sopenharmony_ci *pfn = pa >> PAGE_SHIFT; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci edac_dbg(0, "ECC physical address 0x%lx\n", pa); 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic int cpc925_mc_find_channel(struct mem_ctl_info *mci, u16 syndrome) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci if ((syndrome & MESR_ECC_SYN_H_MASK) == 0) 5078c2ecf20Sopenharmony_ci return 0; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if ((syndrome & MESR_ECC_SYN_L_MASK) == 0) 5108c2ecf20Sopenharmony_ci return 1; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_INFO, "Unexpected syndrome value: 0x%x\n", 5138c2ecf20Sopenharmony_ci syndrome); 5148c2ecf20Sopenharmony_ci return 1; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci/* Check memory controller registers for ECC errors */ 5188c2ecf20Sopenharmony_cistatic void cpc925_mc_check(struct mem_ctl_info *mci) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci struct cpc925_mc_pdata *pdata = mci->pvt_info; 5218c2ecf20Sopenharmony_ci u32 apiexcp; 5228c2ecf20Sopenharmony_ci u32 mear; 5238c2ecf20Sopenharmony_ci u32 mesr; 5248c2ecf20Sopenharmony_ci u16 syndrome; 5258c2ecf20Sopenharmony_ci unsigned long pfn = 0, offset = 0; 5268c2ecf20Sopenharmony_ci int csrow = 0, channel = 0; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* APIEXCP is cleared when read */ 5298c2ecf20Sopenharmony_ci apiexcp = __raw_readl(pdata->vbase + REG_APIEXCP_OFFSET); 5308c2ecf20Sopenharmony_ci if ((apiexcp & ECC_EXCP_DETECTED) == 0) 5318c2ecf20Sopenharmony_ci return; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci mesr = __raw_readl(pdata->vbase + REG_MESR_OFFSET); 5348c2ecf20Sopenharmony_ci syndrome = mesr | (MESR_ECC_SYN_H_MASK | MESR_ECC_SYN_L_MASK); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci mear = __raw_readl(pdata->vbase + REG_MEAR_OFFSET); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* Revert column/row addresses into page frame number, etc */ 5398c2ecf20Sopenharmony_ci cpc925_mc_get_pfn(mci, mear, &pfn, &offset, &csrow); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (apiexcp & CECC_EXCP_DETECTED) { 5428c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_INFO, "DRAM CECC Fault\n"); 5438c2ecf20Sopenharmony_ci channel = cpc925_mc_find_channel(mci, syndrome); 5448c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 5458c2ecf20Sopenharmony_ci pfn, offset, syndrome, 5468c2ecf20Sopenharmony_ci csrow, channel, -1, 5478c2ecf20Sopenharmony_ci mci->ctl_name, ""); 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (apiexcp & UECC_EXCP_DETECTED) { 5518c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_INFO, "DRAM UECC Fault\n"); 5528c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 5538c2ecf20Sopenharmony_ci pfn, offset, 0, 5548c2ecf20Sopenharmony_ci csrow, -1, -1, 5558c2ecf20Sopenharmony_ci mci->ctl_name, ""); 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_INFO, "Dump registers:\n"); 5598c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_INFO, "APIMASK 0x%08x\n", 5608c2ecf20Sopenharmony_ci __raw_readl(pdata->vbase + REG_APIMASK_OFFSET)); 5618c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_INFO, "APIEXCP 0x%08x\n", 5628c2ecf20Sopenharmony_ci apiexcp); 5638c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_INFO, "Mem Scrub Ctrl 0x%08x\n", 5648c2ecf20Sopenharmony_ci __raw_readl(pdata->vbase + REG_MSCR_OFFSET)); 5658c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_INFO, "Mem Scrub Rge Start 0x%08x\n", 5668c2ecf20Sopenharmony_ci __raw_readl(pdata->vbase + REG_MSRSR_OFFSET)); 5678c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_INFO, "Mem Scrub Rge End 0x%08x\n", 5688c2ecf20Sopenharmony_ci __raw_readl(pdata->vbase + REG_MSRER_OFFSET)); 5698c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_INFO, "Mem Scrub Pattern 0x%08x\n", 5708c2ecf20Sopenharmony_ci __raw_readl(pdata->vbase + REG_MSPR_OFFSET)); 5718c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_INFO, "Mem Chk Ctrl 0x%08x\n", 5728c2ecf20Sopenharmony_ci __raw_readl(pdata->vbase + REG_MCCR_OFFSET)); 5738c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_INFO, "Mem Chk Rge End 0x%08x\n", 5748c2ecf20Sopenharmony_ci __raw_readl(pdata->vbase + REG_MCRER_OFFSET)); 5758c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_INFO, "Mem Err Address 0x%08x\n", 5768c2ecf20Sopenharmony_ci mesr); 5778c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_INFO, "Mem Err Syndrome 0x%08x\n", 5788c2ecf20Sopenharmony_ci syndrome); 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci/******************** CPU err device********************************/ 5828c2ecf20Sopenharmony_cistatic u32 cpc925_cpu_mask_disabled(void) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct device_node *cpunode; 5858c2ecf20Sopenharmony_ci static u32 mask = 0; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* use cached value if available */ 5888c2ecf20Sopenharmony_ci if (mask != 0) 5898c2ecf20Sopenharmony_ci return mask; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci mask = APIMASK_ADI0 | APIMASK_ADI1; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci for_each_of_cpu_node(cpunode) { 5948c2ecf20Sopenharmony_ci const u32 *reg = of_get_property(cpunode, "reg", NULL); 5958c2ecf20Sopenharmony_ci if (reg == NULL || *reg > 2) { 5968c2ecf20Sopenharmony_ci cpc925_printk(KERN_ERR, "Bad reg value at %pOF\n", cpunode); 5978c2ecf20Sopenharmony_ci continue; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci mask &= ~APIMASK_ADI(*reg); 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (mask != (APIMASK_ADI0 | APIMASK_ADI1)) { 6048c2ecf20Sopenharmony_ci /* We assume that each CPU sits on it's own PI and that 6058c2ecf20Sopenharmony_ci * for present CPUs the reg property equals to the PI 6068c2ecf20Sopenharmony_ci * interface id */ 6078c2ecf20Sopenharmony_ci cpc925_printk(KERN_WARNING, 6088c2ecf20Sopenharmony_ci "Assuming PI id is equal to CPU MPIC id!\n"); 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci return mask; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci/* Enable CPU Errors detection */ 6158c2ecf20Sopenharmony_cistatic void cpc925_cpu_init(struct cpc925_dev_info *dev_info) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci u32 apimask; 6188c2ecf20Sopenharmony_ci u32 cpumask; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci apimask = __raw_readl(dev_info->vbase + REG_APIMASK_OFFSET); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci cpumask = cpc925_cpu_mask_disabled(); 6238c2ecf20Sopenharmony_ci if (apimask & cpumask) { 6248c2ecf20Sopenharmony_ci cpc925_printk(KERN_WARNING, "CPU(s) not present, " 6258c2ecf20Sopenharmony_ci "but enabled in APIMASK, disabling\n"); 6268c2ecf20Sopenharmony_ci apimask &= ~cpumask; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if ((apimask & CPU_MASK_ENABLE) == 0) 6308c2ecf20Sopenharmony_ci apimask |= CPU_MASK_ENABLE; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci __raw_writel(apimask, dev_info->vbase + REG_APIMASK_OFFSET); 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci/* Disable CPU Errors detection */ 6368c2ecf20Sopenharmony_cistatic void cpc925_cpu_exit(struct cpc925_dev_info *dev_info) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci /* 6398c2ecf20Sopenharmony_ci * WARNING: 6408c2ecf20Sopenharmony_ci * We are supposed to clear the CPU error detection bits, 6418c2ecf20Sopenharmony_ci * and it will be no problem to do so. However, once they 6428c2ecf20Sopenharmony_ci * are cleared here if we want to re-install CPC925 EDAC 6438c2ecf20Sopenharmony_ci * module later, setting them up in cpc925_cpu_init() will 6448c2ecf20Sopenharmony_ci * trigger machine check exception. 6458c2ecf20Sopenharmony_ci * Also, it's ok to leave CPU error detection bits enabled, 6468c2ecf20Sopenharmony_ci * since they are reset to 1 by default. 6478c2ecf20Sopenharmony_ci */ 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci return; 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci/* Check for CPU Errors */ 6538c2ecf20Sopenharmony_cistatic void cpc925_cpu_check(struct edac_device_ctl_info *edac_dev) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct cpc925_dev_info *dev_info = edac_dev->pvt_info; 6568c2ecf20Sopenharmony_ci u32 apiexcp; 6578c2ecf20Sopenharmony_ci u32 apimask; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* APIEXCP is cleared when read */ 6608c2ecf20Sopenharmony_ci apiexcp = __raw_readl(dev_info->vbase + REG_APIEXCP_OFFSET); 6618c2ecf20Sopenharmony_ci if ((apiexcp & CPU_EXCP_DETECTED) == 0) 6628c2ecf20Sopenharmony_ci return; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci if ((apiexcp & ~cpc925_cpu_mask_disabled()) == 0) 6658c2ecf20Sopenharmony_ci return; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci apimask = __raw_readl(dev_info->vbase + REG_APIMASK_OFFSET); 6688c2ecf20Sopenharmony_ci cpc925_printk(KERN_INFO, "Processor Interface Fault\n" 6698c2ecf20Sopenharmony_ci "Processor Interface register dump:\n"); 6708c2ecf20Sopenharmony_ci cpc925_printk(KERN_INFO, "APIMASK 0x%08x\n", apimask); 6718c2ecf20Sopenharmony_ci cpc925_printk(KERN_INFO, "APIEXCP 0x%08x\n", apiexcp); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name); 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci/******************** HT Link err device****************************/ 6778c2ecf20Sopenharmony_ci/* Enable HyperTransport Link Error detection */ 6788c2ecf20Sopenharmony_cistatic void cpc925_htlink_init(struct cpc925_dev_info *dev_info) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci u32 ht_errctrl; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci ht_errctrl = __raw_readl(dev_info->vbase + REG_ERRCTRL_OFFSET); 6838c2ecf20Sopenharmony_ci if ((ht_errctrl & HT_ERRCTRL_ENABLE) == 0) { 6848c2ecf20Sopenharmony_ci ht_errctrl |= HT_ERRCTRL_ENABLE; 6858c2ecf20Sopenharmony_ci __raw_writel(ht_errctrl, dev_info->vbase + REG_ERRCTRL_OFFSET); 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci/* Disable HyperTransport Link Error detection */ 6908c2ecf20Sopenharmony_cistatic void cpc925_htlink_exit(struct cpc925_dev_info *dev_info) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci u32 ht_errctrl; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci ht_errctrl = __raw_readl(dev_info->vbase + REG_ERRCTRL_OFFSET); 6958c2ecf20Sopenharmony_ci ht_errctrl &= ~HT_ERRCTRL_ENABLE; 6968c2ecf20Sopenharmony_ci __raw_writel(ht_errctrl, dev_info->vbase + REG_ERRCTRL_OFFSET); 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci/* Check for HyperTransport Link errors */ 7008c2ecf20Sopenharmony_cistatic void cpc925_htlink_check(struct edac_device_ctl_info *edac_dev) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci struct cpc925_dev_info *dev_info = edac_dev->pvt_info; 7038c2ecf20Sopenharmony_ci u32 brgctrl = __raw_readl(dev_info->vbase + REG_BRGCTRL_OFFSET); 7048c2ecf20Sopenharmony_ci u32 linkctrl = __raw_readl(dev_info->vbase + REG_LINKCTRL_OFFSET); 7058c2ecf20Sopenharmony_ci u32 errctrl = __raw_readl(dev_info->vbase + REG_ERRCTRL_OFFSET); 7068c2ecf20Sopenharmony_ci u32 linkerr = __raw_readl(dev_info->vbase + REG_LINKERR_OFFSET); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (!((brgctrl & BRGCTRL_DETSERR) || 7098c2ecf20Sopenharmony_ci (linkctrl & HT_LINKCTRL_DETECTED) || 7108c2ecf20Sopenharmony_ci (errctrl & HT_ERRCTRL_DETECTED) || 7118c2ecf20Sopenharmony_ci (linkerr & HT_LINKERR_DETECTED))) 7128c2ecf20Sopenharmony_ci return; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci cpc925_printk(KERN_INFO, "HT Link Fault\n" 7158c2ecf20Sopenharmony_ci "HT register dump:\n"); 7168c2ecf20Sopenharmony_ci cpc925_printk(KERN_INFO, "Bridge Ctrl 0x%08x\n", 7178c2ecf20Sopenharmony_ci brgctrl); 7188c2ecf20Sopenharmony_ci cpc925_printk(KERN_INFO, "Link Config Ctrl 0x%08x\n", 7198c2ecf20Sopenharmony_ci linkctrl); 7208c2ecf20Sopenharmony_ci cpc925_printk(KERN_INFO, "Error Enum and Ctrl 0x%08x\n", 7218c2ecf20Sopenharmony_ci errctrl); 7228c2ecf20Sopenharmony_ci cpc925_printk(KERN_INFO, "Link Error 0x%08x\n", 7238c2ecf20Sopenharmony_ci linkerr); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci /* Clear by write 1 */ 7268c2ecf20Sopenharmony_ci if (brgctrl & BRGCTRL_DETSERR) 7278c2ecf20Sopenharmony_ci __raw_writel(BRGCTRL_DETSERR, 7288c2ecf20Sopenharmony_ci dev_info->vbase + REG_BRGCTRL_OFFSET); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (linkctrl & HT_LINKCTRL_DETECTED) 7318c2ecf20Sopenharmony_ci __raw_writel(HT_LINKCTRL_DETECTED, 7328c2ecf20Sopenharmony_ci dev_info->vbase + REG_LINKCTRL_OFFSET); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci /* Initiate Secondary Bus Reset to clear the chain failure */ 7358c2ecf20Sopenharmony_ci if (errctrl & ERRCTRL_CHN_FAL) 7368c2ecf20Sopenharmony_ci __raw_writel(BRGCTRL_SECBUSRESET, 7378c2ecf20Sopenharmony_ci dev_info->vbase + REG_BRGCTRL_OFFSET); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci if (errctrl & ERRCTRL_RSP_ERR) 7408c2ecf20Sopenharmony_ci __raw_writel(ERRCTRL_RSP_ERR, 7418c2ecf20Sopenharmony_ci dev_info->vbase + REG_ERRCTRL_OFFSET); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (linkerr & HT_LINKERR_DETECTED) 7448c2ecf20Sopenharmony_ci __raw_writel(HT_LINKERR_DETECTED, 7458c2ecf20Sopenharmony_ci dev_info->vbase + REG_LINKERR_OFFSET); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name); 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic struct cpc925_dev_info cpc925_devs[] = { 7518c2ecf20Sopenharmony_ci { 7528c2ecf20Sopenharmony_ci .ctl_name = CPC925_CPU_ERR_DEV, 7538c2ecf20Sopenharmony_ci .init = cpc925_cpu_init, 7548c2ecf20Sopenharmony_ci .exit = cpc925_cpu_exit, 7558c2ecf20Sopenharmony_ci .check = cpc925_cpu_check, 7568c2ecf20Sopenharmony_ci }, 7578c2ecf20Sopenharmony_ci { 7588c2ecf20Sopenharmony_ci .ctl_name = CPC925_HT_LINK_DEV, 7598c2ecf20Sopenharmony_ci .init = cpc925_htlink_init, 7608c2ecf20Sopenharmony_ci .exit = cpc925_htlink_exit, 7618c2ecf20Sopenharmony_ci .check = cpc925_htlink_check, 7628c2ecf20Sopenharmony_ci }, 7638c2ecf20Sopenharmony_ci { } 7648c2ecf20Sopenharmony_ci}; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci/* 7678c2ecf20Sopenharmony_ci * Add CPU Err detection and HyperTransport Link Err detection 7688c2ecf20Sopenharmony_ci * as common "edac_device", they have no corresponding device 7698c2ecf20Sopenharmony_ci * nodes in the Open Firmware DTB and we have to add platform 7708c2ecf20Sopenharmony_ci * devices for them. Also, they will share the MMIO with that 7718c2ecf20Sopenharmony_ci * of memory controller. 7728c2ecf20Sopenharmony_ci */ 7738c2ecf20Sopenharmony_cistatic void cpc925_add_edac_devices(void __iomem *vbase) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci struct cpc925_dev_info *dev_info; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci if (!vbase) { 7788c2ecf20Sopenharmony_ci cpc925_printk(KERN_ERR, "MMIO not established yet\n"); 7798c2ecf20Sopenharmony_ci return; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci for (dev_info = &cpc925_devs[0]; dev_info->init; dev_info++) { 7838c2ecf20Sopenharmony_ci dev_info->vbase = vbase; 7848c2ecf20Sopenharmony_ci dev_info->pdev = platform_device_register_simple( 7858c2ecf20Sopenharmony_ci dev_info->ctl_name, 0, NULL, 0); 7868c2ecf20Sopenharmony_ci if (IS_ERR(dev_info->pdev)) { 7878c2ecf20Sopenharmony_ci cpc925_printk(KERN_ERR, 7888c2ecf20Sopenharmony_ci "Can't register platform device for %s\n", 7898c2ecf20Sopenharmony_ci dev_info->ctl_name); 7908c2ecf20Sopenharmony_ci continue; 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci /* 7948c2ecf20Sopenharmony_ci * Don't have to allocate private structure but 7958c2ecf20Sopenharmony_ci * make use of cpc925_devs[] instead. 7968c2ecf20Sopenharmony_ci */ 7978c2ecf20Sopenharmony_ci dev_info->edac_idx = edac_device_alloc_index(); 7988c2ecf20Sopenharmony_ci dev_info->edac_dev = 7998c2ecf20Sopenharmony_ci edac_device_alloc_ctl_info(0, dev_info->ctl_name, 8008c2ecf20Sopenharmony_ci 1, NULL, 0, 0, NULL, 0, dev_info->edac_idx); 8018c2ecf20Sopenharmony_ci if (!dev_info->edac_dev) { 8028c2ecf20Sopenharmony_ci cpc925_printk(KERN_ERR, "No memory for edac device\n"); 8038c2ecf20Sopenharmony_ci goto err1; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci dev_info->edac_dev->pvt_info = dev_info; 8078c2ecf20Sopenharmony_ci dev_info->edac_dev->dev = &dev_info->pdev->dev; 8088c2ecf20Sopenharmony_ci dev_info->edac_dev->ctl_name = dev_info->ctl_name; 8098c2ecf20Sopenharmony_ci dev_info->edac_dev->mod_name = CPC925_EDAC_MOD_STR; 8108c2ecf20Sopenharmony_ci dev_info->edac_dev->dev_name = dev_name(&dev_info->pdev->dev); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci if (edac_op_state == EDAC_OPSTATE_POLL) 8138c2ecf20Sopenharmony_ci dev_info->edac_dev->edac_check = dev_info->check; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci if (dev_info->init) 8168c2ecf20Sopenharmony_ci dev_info->init(dev_info); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (edac_device_add_device(dev_info->edac_dev) > 0) { 8198c2ecf20Sopenharmony_ci cpc925_printk(KERN_ERR, 8208c2ecf20Sopenharmony_ci "Unable to add edac device for %s\n", 8218c2ecf20Sopenharmony_ci dev_info->ctl_name); 8228c2ecf20Sopenharmony_ci goto err2; 8238c2ecf20Sopenharmony_ci } 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci edac_dbg(0, "Successfully added edac device for %s\n", 8268c2ecf20Sopenharmony_ci dev_info->ctl_name); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci continue; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cierr2: 8318c2ecf20Sopenharmony_ci if (dev_info->exit) 8328c2ecf20Sopenharmony_ci dev_info->exit(dev_info); 8338c2ecf20Sopenharmony_ci edac_device_free_ctl_info(dev_info->edac_dev); 8348c2ecf20Sopenharmony_cierr1: 8358c2ecf20Sopenharmony_ci platform_device_unregister(dev_info->pdev); 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci} 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci/* 8408c2ecf20Sopenharmony_ci * Delete the common "edac_device" for CPU Err Detection 8418c2ecf20Sopenharmony_ci * and HyperTransport Link Err Detection 8428c2ecf20Sopenharmony_ci */ 8438c2ecf20Sopenharmony_cistatic void cpc925_del_edac_devices(void) 8448c2ecf20Sopenharmony_ci{ 8458c2ecf20Sopenharmony_ci struct cpc925_dev_info *dev_info; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci for (dev_info = &cpc925_devs[0]; dev_info->init; dev_info++) { 8488c2ecf20Sopenharmony_ci if (dev_info->edac_dev) { 8498c2ecf20Sopenharmony_ci edac_device_del_device(dev_info->edac_dev->dev); 8508c2ecf20Sopenharmony_ci edac_device_free_ctl_info(dev_info->edac_dev); 8518c2ecf20Sopenharmony_ci platform_device_unregister(dev_info->pdev); 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci if (dev_info->exit) 8558c2ecf20Sopenharmony_ci dev_info->exit(dev_info); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci edac_dbg(0, "Successfully deleted edac device for %s\n", 8588c2ecf20Sopenharmony_ci dev_info->ctl_name); 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci/* Convert current back-ground scrub rate into byte/sec bandwidth */ 8638c2ecf20Sopenharmony_cistatic int cpc925_get_sdram_scrub_rate(struct mem_ctl_info *mci) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci struct cpc925_mc_pdata *pdata = mci->pvt_info; 8668c2ecf20Sopenharmony_ci int bw; 8678c2ecf20Sopenharmony_ci u32 mscr; 8688c2ecf20Sopenharmony_ci u8 si; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci mscr = __raw_readl(pdata->vbase + REG_MSCR_OFFSET); 8718c2ecf20Sopenharmony_ci si = (mscr & MSCR_SI_MASK) >> MSCR_SI_SHIFT; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci edac_dbg(0, "Mem Scrub Ctrl Register 0x%x\n", mscr); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (((mscr & MSCR_SCRUB_MOD_MASK) != MSCR_BACKGR_SCRUB) || 8768c2ecf20Sopenharmony_ci (si == 0)) { 8778c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_INFO, "Scrub mode not enabled\n"); 8788c2ecf20Sopenharmony_ci bw = 0; 8798c2ecf20Sopenharmony_ci } else 8808c2ecf20Sopenharmony_ci bw = CPC925_SCRUB_BLOCK_SIZE * 0xFA67 / si; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci return bw; 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci/* Return 0 for single channel; 1 for dual channel */ 8868c2ecf20Sopenharmony_cistatic int cpc925_mc_get_channels(void __iomem *vbase) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci int dual = 0; 8898c2ecf20Sopenharmony_ci u32 mbcr; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci mbcr = __raw_readl(vbase + REG_MBCR_OFFSET); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci /* 8948c2ecf20Sopenharmony_ci * Dual channel only when 128-bit wide physical bus 8958c2ecf20Sopenharmony_ci * and 128-bit configuration. 8968c2ecf20Sopenharmony_ci */ 8978c2ecf20Sopenharmony_ci if (((mbcr & MBCR_64BITCFG_MASK) == 0) && 8988c2ecf20Sopenharmony_ci ((mbcr & MBCR_64BITBUS_MASK) == 0)) 8998c2ecf20Sopenharmony_ci dual = 1; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci edac_dbg(0, "%s channel\n", (dual > 0) ? "Dual" : "Single"); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci return dual; 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_cistatic int cpc925_probe(struct platform_device *pdev) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci static int edac_mc_idx; 9098c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 9108c2ecf20Sopenharmony_ci struct edac_mc_layer layers[2]; 9118c2ecf20Sopenharmony_ci void __iomem *vbase; 9128c2ecf20Sopenharmony_ci struct cpc925_mc_pdata *pdata; 9138c2ecf20Sopenharmony_ci struct resource *r; 9148c2ecf20Sopenharmony_ci int res = 0, nr_channels; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci edac_dbg(0, "%s platform device found!\n", pdev->name); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (!devres_open_group(&pdev->dev, cpc925_probe, GFP_KERNEL)) { 9198c2ecf20Sopenharmony_ci res = -ENOMEM; 9208c2ecf20Sopenharmony_ci goto out; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 9248c2ecf20Sopenharmony_ci if (!r) { 9258c2ecf20Sopenharmony_ci cpc925_printk(KERN_ERR, "Unable to get resource\n"); 9268c2ecf20Sopenharmony_ci res = -ENOENT; 9278c2ecf20Sopenharmony_ci goto err1; 9288c2ecf20Sopenharmony_ci } 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci if (!devm_request_mem_region(&pdev->dev, 9318c2ecf20Sopenharmony_ci r->start, 9328c2ecf20Sopenharmony_ci resource_size(r), 9338c2ecf20Sopenharmony_ci pdev->name)) { 9348c2ecf20Sopenharmony_ci cpc925_printk(KERN_ERR, "Unable to request mem region\n"); 9358c2ecf20Sopenharmony_ci res = -EBUSY; 9368c2ecf20Sopenharmony_ci goto err1; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci vbase = devm_ioremap(&pdev->dev, r->start, resource_size(r)); 9408c2ecf20Sopenharmony_ci if (!vbase) { 9418c2ecf20Sopenharmony_ci cpc925_printk(KERN_ERR, "Unable to ioremap device\n"); 9428c2ecf20Sopenharmony_ci res = -ENOMEM; 9438c2ecf20Sopenharmony_ci goto err2; 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci nr_channels = cpc925_mc_get_channels(vbase) + 1; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 9498c2ecf20Sopenharmony_ci layers[0].size = CPC925_NR_CSROWS; 9508c2ecf20Sopenharmony_ci layers[0].is_virt_csrow = true; 9518c2ecf20Sopenharmony_ci layers[1].type = EDAC_MC_LAYER_CHANNEL; 9528c2ecf20Sopenharmony_ci layers[1].size = nr_channels; 9538c2ecf20Sopenharmony_ci layers[1].is_virt_csrow = false; 9548c2ecf20Sopenharmony_ci mci = edac_mc_alloc(edac_mc_idx, ARRAY_SIZE(layers), layers, 9558c2ecf20Sopenharmony_ci sizeof(struct cpc925_mc_pdata)); 9568c2ecf20Sopenharmony_ci if (!mci) { 9578c2ecf20Sopenharmony_ci cpc925_printk(KERN_ERR, "No memory for mem_ctl_info\n"); 9588c2ecf20Sopenharmony_ci res = -ENOMEM; 9598c2ecf20Sopenharmony_ci goto err2; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci pdata = mci->pvt_info; 9638c2ecf20Sopenharmony_ci pdata->vbase = vbase; 9648c2ecf20Sopenharmony_ci pdata->edac_idx = edac_mc_idx++; 9658c2ecf20Sopenharmony_ci pdata->name = pdev->name; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci mci->pdev = &pdev->dev; 9688c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mci); 9698c2ecf20Sopenharmony_ci mci->dev_name = dev_name(&pdev->dev); 9708c2ecf20Sopenharmony_ci mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR; 9718c2ecf20Sopenharmony_ci mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; 9728c2ecf20Sopenharmony_ci mci->edac_cap = EDAC_FLAG_SECDED; 9738c2ecf20Sopenharmony_ci mci->mod_name = CPC925_EDAC_MOD_STR; 9748c2ecf20Sopenharmony_ci mci->ctl_name = pdev->name; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci if (edac_op_state == EDAC_OPSTATE_POLL) 9778c2ecf20Sopenharmony_ci mci->edac_check = cpc925_mc_check; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci mci->ctl_page_to_phys = NULL; 9808c2ecf20Sopenharmony_ci mci->scrub_mode = SCRUB_SW_SRC; 9818c2ecf20Sopenharmony_ci mci->set_sdram_scrub_rate = NULL; 9828c2ecf20Sopenharmony_ci mci->get_sdram_scrub_rate = cpc925_get_sdram_scrub_rate; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci cpc925_init_csrows(mci); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci /* Setup memory controller registers */ 9878c2ecf20Sopenharmony_ci cpc925_mc_init(mci); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci if (edac_mc_add_mc(mci) > 0) { 9908c2ecf20Sopenharmony_ci cpc925_mc_printk(mci, KERN_ERR, "Failed edac_mc_add_mc()\n"); 9918c2ecf20Sopenharmony_ci goto err3; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci cpc925_add_edac_devices(vbase); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci /* get this far and it's successful */ 9978c2ecf20Sopenharmony_ci edac_dbg(0, "success\n"); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci res = 0; 10008c2ecf20Sopenharmony_ci goto out; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cierr3: 10038c2ecf20Sopenharmony_ci cpc925_mc_exit(mci); 10048c2ecf20Sopenharmony_ci edac_mc_free(mci); 10058c2ecf20Sopenharmony_cierr2: 10068c2ecf20Sopenharmony_ci devm_release_mem_region(&pdev->dev, r->start, resource_size(r)); 10078c2ecf20Sopenharmony_cierr1: 10088c2ecf20Sopenharmony_ci devres_release_group(&pdev->dev, cpc925_probe); 10098c2ecf20Sopenharmony_ciout: 10108c2ecf20Sopenharmony_ci return res; 10118c2ecf20Sopenharmony_ci} 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_cistatic int cpc925_remove(struct platform_device *pdev) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = platform_get_drvdata(pdev); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci /* 10188c2ecf20Sopenharmony_ci * Delete common edac devices before edac mc, because 10198c2ecf20Sopenharmony_ci * the former share the MMIO of the latter. 10208c2ecf20Sopenharmony_ci */ 10218c2ecf20Sopenharmony_ci cpc925_del_edac_devices(); 10228c2ecf20Sopenharmony_ci cpc925_mc_exit(mci); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci edac_mc_del_mc(&pdev->dev); 10258c2ecf20Sopenharmony_ci edac_mc_free(mci); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci return 0; 10288c2ecf20Sopenharmony_ci} 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_cistatic struct platform_driver cpc925_edac_driver = { 10318c2ecf20Sopenharmony_ci .probe = cpc925_probe, 10328c2ecf20Sopenharmony_ci .remove = cpc925_remove, 10338c2ecf20Sopenharmony_ci .driver = { 10348c2ecf20Sopenharmony_ci .name = "cpc925_edac", 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci}; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_cistatic int __init cpc925_edac_init(void) 10398c2ecf20Sopenharmony_ci{ 10408c2ecf20Sopenharmony_ci int ret = 0; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci printk(KERN_INFO "IBM CPC925 EDAC driver " CPC925_EDAC_REVISION "\n"); 10438c2ecf20Sopenharmony_ci printk(KERN_INFO "\t(c) 2008 Wind River Systems, Inc\n"); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci /* Only support POLL mode so far */ 10468c2ecf20Sopenharmony_ci edac_op_state = EDAC_OPSTATE_POLL; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci ret = platform_driver_register(&cpc925_edac_driver); 10498c2ecf20Sopenharmony_ci if (ret) { 10508c2ecf20Sopenharmony_ci printk(KERN_WARNING "Failed to register %s\n", 10518c2ecf20Sopenharmony_ci CPC925_EDAC_MOD_STR); 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci return ret; 10558c2ecf20Sopenharmony_ci} 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_cistatic void __exit cpc925_edac_exit(void) 10588c2ecf20Sopenharmony_ci{ 10598c2ecf20Sopenharmony_ci platform_driver_unregister(&cpc925_edac_driver); 10608c2ecf20Sopenharmony_ci} 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_cimodule_init(cpc925_edac_init); 10638c2ecf20Sopenharmony_cimodule_exit(cpc925_edac_exit); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 10668c2ecf20Sopenharmony_ciMODULE_AUTHOR("Cao Qingtao <qingtao.cao@windriver.com>"); 10678c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IBM CPC925 Bridge and MC EDAC kernel module"); 1068