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