18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2008 Nuovation System Designs, LLC
48c2ecf20Sopenharmony_ci *   Grant Erickson <gerickson@nuovations.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/edac.h>
88c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
98c2ecf20Sopenharmony_ci#include <linux/irq.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/mm.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/of_device.h>
148c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
158c2ecf20Sopenharmony_ci#include <linux/types.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <asm/dcr.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "edac_module.h"
208c2ecf20Sopenharmony_ci#include "ppc4xx_edac.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/*
238c2ecf20Sopenharmony_ci * This file implements a driver for monitoring and handling events
248c2ecf20Sopenharmony_ci * associated with the IMB DDR2 ECC controller found in the AMCC/IBM
258c2ecf20Sopenharmony_ci * 405EX[r], 440SP, 440SPe, 460EX, 460GT and 460SX.
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * As realized in the 405EX[r], this controller features:
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci *   - Support for registered- and non-registered DDR1 and DDR2 memory.
308c2ecf20Sopenharmony_ci *   - 32-bit or 16-bit memory interface with optional ECC.
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci *     o ECC support includes:
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci *       - 4-bit SEC/DED
358c2ecf20Sopenharmony_ci *       - Aligned-nibble error detect
368c2ecf20Sopenharmony_ci *       - Bypass mode
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci *   - Two (2) memory banks/ranks.
398c2ecf20Sopenharmony_ci *   - Up to 1 GiB per bank/rank in 32-bit mode and up to 512 MiB per
408c2ecf20Sopenharmony_ci *     bank/rank in 16-bit mode.
418c2ecf20Sopenharmony_ci *
428c2ecf20Sopenharmony_ci * As realized in the 440SP and 440SPe, this controller changes/adds:
438c2ecf20Sopenharmony_ci *
448c2ecf20Sopenharmony_ci *   - 64-bit or 32-bit memory interface with optional ECC.
458c2ecf20Sopenharmony_ci *
468c2ecf20Sopenharmony_ci *     o ECC support includes:
478c2ecf20Sopenharmony_ci *
488c2ecf20Sopenharmony_ci *       - 8-bit SEC/DED
498c2ecf20Sopenharmony_ci *       - Aligned-nibble error detect
508c2ecf20Sopenharmony_ci *       - Bypass mode
518c2ecf20Sopenharmony_ci *
528c2ecf20Sopenharmony_ci *   - Up to 4 GiB per bank/rank in 64-bit mode and up to 2 GiB
538c2ecf20Sopenharmony_ci *     per bank/rank in 32-bit mode.
548c2ecf20Sopenharmony_ci *
558c2ecf20Sopenharmony_ci * As realized in the 460EX and 460GT, this controller changes/adds:
568c2ecf20Sopenharmony_ci *
578c2ecf20Sopenharmony_ci *   - 64-bit or 32-bit memory interface with optional ECC.
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci *     o ECC support includes:
608c2ecf20Sopenharmony_ci *
618c2ecf20Sopenharmony_ci *       - 8-bit SEC/DED
628c2ecf20Sopenharmony_ci *       - Aligned-nibble error detect
638c2ecf20Sopenharmony_ci *       - Bypass mode
648c2ecf20Sopenharmony_ci *
658c2ecf20Sopenharmony_ci *   - Four (4) memory banks/ranks.
668c2ecf20Sopenharmony_ci *   - Up to 16 GiB per bank/rank in 64-bit mode and up to 8 GiB
678c2ecf20Sopenharmony_ci *     per bank/rank in 32-bit mode.
688c2ecf20Sopenharmony_ci *
698c2ecf20Sopenharmony_ci * At present, this driver has ONLY been tested against the controller
708c2ecf20Sopenharmony_ci * realization in the 405EX[r] on the AMCC Kilauea and Haleakala
718c2ecf20Sopenharmony_ci * boards (256 MiB w/o ECC memory soldered onto the board) and a
728c2ecf20Sopenharmony_ci * proprietary board based on those designs (128 MiB ECC memory, also
738c2ecf20Sopenharmony_ci * soldered onto the board).
748c2ecf20Sopenharmony_ci *
758c2ecf20Sopenharmony_ci * Dynamic feature detection and handling needs to be added for the
768c2ecf20Sopenharmony_ci * other realizations of this controller listed above.
778c2ecf20Sopenharmony_ci *
788c2ecf20Sopenharmony_ci * Eventually, this driver will likely be adapted to the above variant
798c2ecf20Sopenharmony_ci * realizations of this controller as well as broken apart to handle
808c2ecf20Sopenharmony_ci * the other known ECC-capable controllers prevalent in other 4xx
818c2ecf20Sopenharmony_ci * processors:
828c2ecf20Sopenharmony_ci *
838c2ecf20Sopenharmony_ci *   - IBM SDRAM (405GP, 405CR and 405EP) "ibm,sdram-4xx"
848c2ecf20Sopenharmony_ci *   - IBM DDR1 (440GP, 440GX, 440EP and 440GR) "ibm,sdram-4xx-ddr"
858c2ecf20Sopenharmony_ci *   - Denali DDR1/DDR2 (440EPX and 440GRX) "denali,sdram-4xx-ddr2"
868c2ecf20Sopenharmony_ci *
878c2ecf20Sopenharmony_ci * For this controller, unfortunately, correctable errors report
888c2ecf20Sopenharmony_ci * nothing more than the beat/cycle and byte/lane the correction
898c2ecf20Sopenharmony_ci * occurred on and the check bit group that covered the error.
908c2ecf20Sopenharmony_ci *
918c2ecf20Sopenharmony_ci * In contrast, uncorrectable errors also report the failing address,
928c2ecf20Sopenharmony_ci * the bus master and the transaction direction (i.e. read or write)
938c2ecf20Sopenharmony_ci *
948c2ecf20Sopenharmony_ci * Regardless of whether the error is a CE or a UE, we report the
958c2ecf20Sopenharmony_ci * following pieces of information in the driver-unique message to the
968c2ecf20Sopenharmony_ci * EDAC subsystem:
978c2ecf20Sopenharmony_ci *
988c2ecf20Sopenharmony_ci *   - Device tree path
998c2ecf20Sopenharmony_ci *   - Bank(s)
1008c2ecf20Sopenharmony_ci *   - Check bit error group
1018c2ecf20Sopenharmony_ci *   - Beat(s)/lane(s)
1028c2ecf20Sopenharmony_ci */
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/* Preprocessor Definitions */
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci#define EDAC_OPSTATE_INT_STR		"interrupt"
1078c2ecf20Sopenharmony_ci#define EDAC_OPSTATE_POLL_STR		"polled"
1088c2ecf20Sopenharmony_ci#define EDAC_OPSTATE_UNKNOWN_STR	"unknown"
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci#define PPC4XX_EDAC_MODULE_NAME		"ppc4xx_edac"
1118c2ecf20Sopenharmony_ci#define PPC4XX_EDAC_MODULE_REVISION	"v1.0.0"
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci#define PPC4XX_EDAC_MESSAGE_SIZE	256
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/*
1168c2ecf20Sopenharmony_ci * Kernel logging without an EDAC instance
1178c2ecf20Sopenharmony_ci */
1188c2ecf20Sopenharmony_ci#define ppc4xx_edac_printk(level, fmt, arg...) \
1198c2ecf20Sopenharmony_ci	edac_printk(level, "PPC4xx MC", fmt, ##arg)
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/*
1228c2ecf20Sopenharmony_ci * Kernel logging with an EDAC instance
1238c2ecf20Sopenharmony_ci */
1248c2ecf20Sopenharmony_ci#define ppc4xx_edac_mc_printk(level, mci, fmt, arg...) \
1258c2ecf20Sopenharmony_ci	edac_mc_chipset_printk(mci, level, "PPC4xx", fmt, ##arg)
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci/*
1288c2ecf20Sopenharmony_ci * Macros to convert bank configuration size enumerations into MiB and
1298c2ecf20Sopenharmony_ci * page values.
1308c2ecf20Sopenharmony_ci */
1318c2ecf20Sopenharmony_ci#define SDRAM_MBCF_SZ_MiB_MIN		4
1328c2ecf20Sopenharmony_ci#define SDRAM_MBCF_SZ_TO_MiB(n)		(SDRAM_MBCF_SZ_MiB_MIN \
1338c2ecf20Sopenharmony_ci					 << (SDRAM_MBCF_SZ_DECODE(n)))
1348c2ecf20Sopenharmony_ci#define SDRAM_MBCF_SZ_TO_PAGES(n)	(SDRAM_MBCF_SZ_MiB_MIN \
1358c2ecf20Sopenharmony_ci					 << (20 - PAGE_SHIFT + \
1368c2ecf20Sopenharmony_ci					     SDRAM_MBCF_SZ_DECODE(n)))
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/*
1398c2ecf20Sopenharmony_ci * The ibm,sdram-4xx-ddr2 Device Control Registers (DCRs) are
1408c2ecf20Sopenharmony_ci * indirectly accessed and have a base and length defined by the
1418c2ecf20Sopenharmony_ci * device tree. The base can be anything; however, we expect the
1428c2ecf20Sopenharmony_ci * length to be precisely two registers, the first for the address
1438c2ecf20Sopenharmony_ci * window and the second for the data window.
1448c2ecf20Sopenharmony_ci */
1458c2ecf20Sopenharmony_ci#define SDRAM_DCR_RESOURCE_LEN		2
1468c2ecf20Sopenharmony_ci#define SDRAM_DCR_ADDR_OFFSET		0
1478c2ecf20Sopenharmony_ci#define SDRAM_DCR_DATA_OFFSET		1
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/*
1508c2ecf20Sopenharmony_ci * Device tree interrupt indices
1518c2ecf20Sopenharmony_ci */
1528c2ecf20Sopenharmony_ci#define INTMAP_ECCDED_INDEX		0	/* Double-bit Error Detect */
1538c2ecf20Sopenharmony_ci#define INTMAP_ECCSEC_INDEX		1	/* Single-bit Error Correct */
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/* Type Definitions */
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci/*
1588c2ecf20Sopenharmony_ci * PPC4xx SDRAM memory controller private instance data
1598c2ecf20Sopenharmony_ci */
1608c2ecf20Sopenharmony_cistruct ppc4xx_edac_pdata {
1618c2ecf20Sopenharmony_ci	dcr_host_t dcr_host;	/* Indirect DCR address/data window mapping */
1628c2ecf20Sopenharmony_ci	struct {
1638c2ecf20Sopenharmony_ci		int sec;	/* Single-bit correctable error IRQ assigned */
1648c2ecf20Sopenharmony_ci		int ded;	/* Double-bit detectable error IRQ assigned */
1658c2ecf20Sopenharmony_ci	} irqs;
1668c2ecf20Sopenharmony_ci};
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci/*
1698c2ecf20Sopenharmony_ci * Various status data gathered and manipulated when checking and
1708c2ecf20Sopenharmony_ci * reporting ECC status.
1718c2ecf20Sopenharmony_ci */
1728c2ecf20Sopenharmony_cistruct ppc4xx_ecc_status {
1738c2ecf20Sopenharmony_ci	u32 ecces;
1748c2ecf20Sopenharmony_ci	u32 besr;
1758c2ecf20Sopenharmony_ci	u32 bearh;
1768c2ecf20Sopenharmony_ci	u32 bearl;
1778c2ecf20Sopenharmony_ci	u32 wmirq;
1788c2ecf20Sopenharmony_ci};
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci/* Function Prototypes */
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic int ppc4xx_edac_probe(struct platform_device *device);
1838c2ecf20Sopenharmony_cistatic int ppc4xx_edac_remove(struct platform_device *device);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci/* Global Variables */
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci/*
1888c2ecf20Sopenharmony_ci * Device tree node type and compatible tuples this driver can match
1898c2ecf20Sopenharmony_ci * on.
1908c2ecf20Sopenharmony_ci */
1918c2ecf20Sopenharmony_cistatic const struct of_device_id ppc4xx_edac_match[] = {
1928c2ecf20Sopenharmony_ci	{
1938c2ecf20Sopenharmony_ci		.compatible	= "ibm,sdram-4xx-ddr2"
1948c2ecf20Sopenharmony_ci	},
1958c2ecf20Sopenharmony_ci	{ }
1968c2ecf20Sopenharmony_ci};
1978c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ppc4xx_edac_match);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic struct platform_driver ppc4xx_edac_driver = {
2008c2ecf20Sopenharmony_ci	.probe			= ppc4xx_edac_probe,
2018c2ecf20Sopenharmony_ci	.remove			= ppc4xx_edac_remove,
2028c2ecf20Sopenharmony_ci	.driver = {
2038c2ecf20Sopenharmony_ci		.name = PPC4XX_EDAC_MODULE_NAME,
2048c2ecf20Sopenharmony_ci		.of_match_table = ppc4xx_edac_match,
2058c2ecf20Sopenharmony_ci	},
2068c2ecf20Sopenharmony_ci};
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci/*
2098c2ecf20Sopenharmony_ci * TODO: The row and channel parameters likely need to be dynamically
2108c2ecf20Sopenharmony_ci * set based on the aforementioned variant controller realizations.
2118c2ecf20Sopenharmony_ci */
2128c2ecf20Sopenharmony_cistatic const unsigned ppc4xx_edac_nr_csrows = 2;
2138c2ecf20Sopenharmony_cistatic const unsigned ppc4xx_edac_nr_chans = 1;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci/*
2168c2ecf20Sopenharmony_ci * Strings associated with PLB master IDs capable of being posted in
2178c2ecf20Sopenharmony_ci * SDRAM_BESR or SDRAM_WMIRQ on uncorrectable ECC errors.
2188c2ecf20Sopenharmony_ci */
2198c2ecf20Sopenharmony_cistatic const char * const ppc4xx_plb_masters[9] = {
2208c2ecf20Sopenharmony_ci	[SDRAM_PLB_M0ID_ICU]	= "ICU",
2218c2ecf20Sopenharmony_ci	[SDRAM_PLB_M0ID_PCIE0]	= "PCI-E 0",
2228c2ecf20Sopenharmony_ci	[SDRAM_PLB_M0ID_PCIE1]	= "PCI-E 1",
2238c2ecf20Sopenharmony_ci	[SDRAM_PLB_M0ID_DMA]	= "DMA",
2248c2ecf20Sopenharmony_ci	[SDRAM_PLB_M0ID_DCU]	= "DCU",
2258c2ecf20Sopenharmony_ci	[SDRAM_PLB_M0ID_OPB]	= "OPB",
2268c2ecf20Sopenharmony_ci	[SDRAM_PLB_M0ID_MAL]	= "MAL",
2278c2ecf20Sopenharmony_ci	[SDRAM_PLB_M0ID_SEC]	= "SEC",
2288c2ecf20Sopenharmony_ci	[SDRAM_PLB_M0ID_AHB]	= "AHB"
2298c2ecf20Sopenharmony_ci};
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci/**
2328c2ecf20Sopenharmony_ci * mfsdram - read and return controller register data
2338c2ecf20Sopenharmony_ci * @dcr_host: A pointer to the DCR mapping.
2348c2ecf20Sopenharmony_ci * @idcr_n: The indirect DCR register to read.
2358c2ecf20Sopenharmony_ci *
2368c2ecf20Sopenharmony_ci * This routine reads and returns the data associated with the
2378c2ecf20Sopenharmony_ci * controller's specified indirect DCR register.
2388c2ecf20Sopenharmony_ci *
2398c2ecf20Sopenharmony_ci * Returns the read data.
2408c2ecf20Sopenharmony_ci */
2418c2ecf20Sopenharmony_cistatic inline u32
2428c2ecf20Sopenharmony_cimfsdram(const dcr_host_t *dcr_host, unsigned int idcr_n)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	return __mfdcri(dcr_host->base + SDRAM_DCR_ADDR_OFFSET,
2458c2ecf20Sopenharmony_ci			dcr_host->base + SDRAM_DCR_DATA_OFFSET,
2468c2ecf20Sopenharmony_ci			idcr_n);
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci/**
2508c2ecf20Sopenharmony_ci * mtsdram - write controller register data
2518c2ecf20Sopenharmony_ci * @dcr_host: A pointer to the DCR mapping.
2528c2ecf20Sopenharmony_ci * @idcr_n: The indirect DCR register to write.
2538c2ecf20Sopenharmony_ci * @value: The data to write.
2548c2ecf20Sopenharmony_ci *
2558c2ecf20Sopenharmony_ci * This routine writes the provided data to the controller's specified
2568c2ecf20Sopenharmony_ci * indirect DCR register.
2578c2ecf20Sopenharmony_ci */
2588c2ecf20Sopenharmony_cistatic inline void
2598c2ecf20Sopenharmony_cimtsdram(const dcr_host_t *dcr_host, unsigned int idcr_n, u32 value)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	return __mtdcri(dcr_host->base + SDRAM_DCR_ADDR_OFFSET,
2628c2ecf20Sopenharmony_ci			dcr_host->base + SDRAM_DCR_DATA_OFFSET,
2638c2ecf20Sopenharmony_ci			idcr_n,
2648c2ecf20Sopenharmony_ci			value);
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci/**
2688c2ecf20Sopenharmony_ci * ppc4xx_edac_check_bank_error - check a bank for an ECC bank error
2698c2ecf20Sopenharmony_ci * @status: A pointer to the ECC status structure to check for an
2708c2ecf20Sopenharmony_ci *          ECC bank error.
2718c2ecf20Sopenharmony_ci * @bank: The bank to check for an ECC error.
2728c2ecf20Sopenharmony_ci *
2738c2ecf20Sopenharmony_ci * This routine determines whether the specified bank has an ECC
2748c2ecf20Sopenharmony_ci * error.
2758c2ecf20Sopenharmony_ci *
2768c2ecf20Sopenharmony_ci * Returns true if the specified bank has an ECC error; otherwise,
2778c2ecf20Sopenharmony_ci * false.
2788c2ecf20Sopenharmony_ci */
2798c2ecf20Sopenharmony_cistatic bool
2808c2ecf20Sopenharmony_cippc4xx_edac_check_bank_error(const struct ppc4xx_ecc_status *status,
2818c2ecf20Sopenharmony_ci			     unsigned int bank)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	switch (bank) {
2848c2ecf20Sopenharmony_ci	case 0:
2858c2ecf20Sopenharmony_ci		return status->ecces & SDRAM_ECCES_BK0ER;
2868c2ecf20Sopenharmony_ci	case 1:
2878c2ecf20Sopenharmony_ci		return status->ecces & SDRAM_ECCES_BK1ER;
2888c2ecf20Sopenharmony_ci	default:
2898c2ecf20Sopenharmony_ci		return false;
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci/**
2948c2ecf20Sopenharmony_ci * ppc4xx_edac_generate_bank_message - generate interpretted bank status message
2958c2ecf20Sopenharmony_ci * @mci: A pointer to the EDAC memory controller instance associated
2968c2ecf20Sopenharmony_ci *       with the bank message being generated.
2978c2ecf20Sopenharmony_ci * @status: A pointer to the ECC status structure to generate the
2988c2ecf20Sopenharmony_ci *          message from.
2998c2ecf20Sopenharmony_ci * @buffer: A pointer to the buffer in which to generate the
3008c2ecf20Sopenharmony_ci *          message.
3018c2ecf20Sopenharmony_ci * @size: The size, in bytes, of space available in buffer.
3028c2ecf20Sopenharmony_ci *
3038c2ecf20Sopenharmony_ci * This routine generates to the provided buffer the portion of the
3048c2ecf20Sopenharmony_ci * driver-unique report message associated with the ECCESS[BKNER]
3058c2ecf20Sopenharmony_ci * field of the specified ECC status.
3068c2ecf20Sopenharmony_ci *
3078c2ecf20Sopenharmony_ci * Returns the number of characters generated on success; otherwise, <
3088c2ecf20Sopenharmony_ci * 0 on error.
3098c2ecf20Sopenharmony_ci */
3108c2ecf20Sopenharmony_cistatic int
3118c2ecf20Sopenharmony_cippc4xx_edac_generate_bank_message(const struct mem_ctl_info *mci,
3128c2ecf20Sopenharmony_ci				  const struct ppc4xx_ecc_status *status,
3138c2ecf20Sopenharmony_ci				  char *buffer,
3148c2ecf20Sopenharmony_ci				  size_t size)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	int n, total = 0;
3178c2ecf20Sopenharmony_ci	unsigned int row, rows;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	n = snprintf(buffer, size, "%s: Banks: ", mci->dev_name);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (n < 0 || n >= size)
3228c2ecf20Sopenharmony_ci		goto fail;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	buffer += n;
3258c2ecf20Sopenharmony_ci	size -= n;
3268c2ecf20Sopenharmony_ci	total += n;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	for (rows = 0, row = 0; row < mci->nr_csrows; row++) {
3298c2ecf20Sopenharmony_ci		if (ppc4xx_edac_check_bank_error(status, row)) {
3308c2ecf20Sopenharmony_ci			n = snprintf(buffer, size, "%s%u",
3318c2ecf20Sopenharmony_ci					(rows++ ? ", " : ""), row);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci			if (n < 0 || n >= size)
3348c2ecf20Sopenharmony_ci				goto fail;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci			buffer += n;
3378c2ecf20Sopenharmony_ci			size -= n;
3388c2ecf20Sopenharmony_ci			total += n;
3398c2ecf20Sopenharmony_ci		}
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	n = snprintf(buffer, size, "%s; ", rows ? "" : "None");
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	if (n < 0 || n >= size)
3458c2ecf20Sopenharmony_ci		goto fail;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	buffer += n;
3488c2ecf20Sopenharmony_ci	size -= n;
3498c2ecf20Sopenharmony_ci	total += n;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci fail:
3528c2ecf20Sopenharmony_ci	return total;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci/**
3568c2ecf20Sopenharmony_ci * ppc4xx_edac_generate_checkbit_message - generate interpretted checkbit message
3578c2ecf20Sopenharmony_ci * @mci: A pointer to the EDAC memory controller instance associated
3588c2ecf20Sopenharmony_ci *       with the checkbit message being generated.
3598c2ecf20Sopenharmony_ci * @status: A pointer to the ECC status structure to generate the
3608c2ecf20Sopenharmony_ci *          message from.
3618c2ecf20Sopenharmony_ci * @buffer: A pointer to the buffer in which to generate the
3628c2ecf20Sopenharmony_ci *          message.
3638c2ecf20Sopenharmony_ci * @size: The size, in bytes, of space available in buffer.
3648c2ecf20Sopenharmony_ci *
3658c2ecf20Sopenharmony_ci * This routine generates to the provided buffer the portion of the
3668c2ecf20Sopenharmony_ci * driver-unique report message associated with the ECCESS[CKBER]
3678c2ecf20Sopenharmony_ci * field of the specified ECC status.
3688c2ecf20Sopenharmony_ci *
3698c2ecf20Sopenharmony_ci * Returns the number of characters generated on success; otherwise, <
3708c2ecf20Sopenharmony_ci * 0 on error.
3718c2ecf20Sopenharmony_ci */
3728c2ecf20Sopenharmony_cistatic int
3738c2ecf20Sopenharmony_cippc4xx_edac_generate_checkbit_message(const struct mem_ctl_info *mci,
3748c2ecf20Sopenharmony_ci				      const struct ppc4xx_ecc_status *status,
3758c2ecf20Sopenharmony_ci				      char *buffer,
3768c2ecf20Sopenharmony_ci				      size_t size)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	const struct ppc4xx_edac_pdata *pdata = mci->pvt_info;
3798c2ecf20Sopenharmony_ci	const char *ckber = NULL;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	switch (status->ecces & SDRAM_ECCES_CKBER_MASK) {
3828c2ecf20Sopenharmony_ci	case SDRAM_ECCES_CKBER_NONE:
3838c2ecf20Sopenharmony_ci		ckber = "None";
3848c2ecf20Sopenharmony_ci		break;
3858c2ecf20Sopenharmony_ci	case SDRAM_ECCES_CKBER_32_ECC_0_3:
3868c2ecf20Sopenharmony_ci		ckber = "ECC0:3";
3878c2ecf20Sopenharmony_ci		break;
3888c2ecf20Sopenharmony_ci	case SDRAM_ECCES_CKBER_32_ECC_4_8:
3898c2ecf20Sopenharmony_ci		switch (mfsdram(&pdata->dcr_host, SDRAM_MCOPT1) &
3908c2ecf20Sopenharmony_ci			SDRAM_MCOPT1_WDTH_MASK) {
3918c2ecf20Sopenharmony_ci		case SDRAM_MCOPT1_WDTH_16:
3928c2ecf20Sopenharmony_ci			ckber = "ECC0:3";
3938c2ecf20Sopenharmony_ci			break;
3948c2ecf20Sopenharmony_ci		case SDRAM_MCOPT1_WDTH_32:
3958c2ecf20Sopenharmony_ci			ckber = "ECC4:8";
3968c2ecf20Sopenharmony_ci			break;
3978c2ecf20Sopenharmony_ci		default:
3988c2ecf20Sopenharmony_ci			ckber = "Unknown";
3998c2ecf20Sopenharmony_ci			break;
4008c2ecf20Sopenharmony_ci		}
4018c2ecf20Sopenharmony_ci		break;
4028c2ecf20Sopenharmony_ci	case SDRAM_ECCES_CKBER_32_ECC_0_8:
4038c2ecf20Sopenharmony_ci		ckber = "ECC0:8";
4048c2ecf20Sopenharmony_ci		break;
4058c2ecf20Sopenharmony_ci	default:
4068c2ecf20Sopenharmony_ci		ckber = "Unknown";
4078c2ecf20Sopenharmony_ci		break;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	return snprintf(buffer, size, "Checkbit Error: %s", ckber);
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci/**
4148c2ecf20Sopenharmony_ci * ppc4xx_edac_generate_lane_message - generate interpretted byte lane message
4158c2ecf20Sopenharmony_ci * @mci: A pointer to the EDAC memory controller instance associated
4168c2ecf20Sopenharmony_ci *       with the byte lane message being generated.
4178c2ecf20Sopenharmony_ci * @status: A pointer to the ECC status structure to generate the
4188c2ecf20Sopenharmony_ci *          message from.
4198c2ecf20Sopenharmony_ci * @buffer: A pointer to the buffer in which to generate the
4208c2ecf20Sopenharmony_ci *          message.
4218c2ecf20Sopenharmony_ci * @size: The size, in bytes, of space available in buffer.
4228c2ecf20Sopenharmony_ci *
4238c2ecf20Sopenharmony_ci * This routine generates to the provided buffer the portion of the
4248c2ecf20Sopenharmony_ci * driver-unique report message associated with the ECCESS[BNCE]
4258c2ecf20Sopenharmony_ci * field of the specified ECC status.
4268c2ecf20Sopenharmony_ci *
4278c2ecf20Sopenharmony_ci * Returns the number of characters generated on success; otherwise, <
4288c2ecf20Sopenharmony_ci * 0 on error.
4298c2ecf20Sopenharmony_ci */
4308c2ecf20Sopenharmony_cistatic int
4318c2ecf20Sopenharmony_cippc4xx_edac_generate_lane_message(const struct mem_ctl_info *mci,
4328c2ecf20Sopenharmony_ci				  const struct ppc4xx_ecc_status *status,
4338c2ecf20Sopenharmony_ci				  char *buffer,
4348c2ecf20Sopenharmony_ci				  size_t size)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	int n, total = 0;
4378c2ecf20Sopenharmony_ci	unsigned int lane, lanes;
4388c2ecf20Sopenharmony_ci	const unsigned int first_lane = 0;
4398c2ecf20Sopenharmony_ci	const unsigned int lane_count = 16;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	n = snprintf(buffer, size, "; Byte Lane Errors: ");
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (n < 0 || n >= size)
4448c2ecf20Sopenharmony_ci		goto fail;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	buffer += n;
4478c2ecf20Sopenharmony_ci	size -= n;
4488c2ecf20Sopenharmony_ci	total += n;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	for (lanes = 0, lane = first_lane; lane < lane_count; lane++) {
4518c2ecf20Sopenharmony_ci		if ((status->ecces & SDRAM_ECCES_BNCE_ENCODE(lane)) != 0) {
4528c2ecf20Sopenharmony_ci			n = snprintf(buffer, size,
4538c2ecf20Sopenharmony_ci				     "%s%u",
4548c2ecf20Sopenharmony_ci				     (lanes++ ? ", " : ""), lane);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci			if (n < 0 || n >= size)
4578c2ecf20Sopenharmony_ci				goto fail;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci			buffer += n;
4608c2ecf20Sopenharmony_ci			size -= n;
4618c2ecf20Sopenharmony_ci			total += n;
4628c2ecf20Sopenharmony_ci		}
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	n = snprintf(buffer, size, "%s; ", lanes ? "" : "None");
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	if (n < 0 || n >= size)
4688c2ecf20Sopenharmony_ci		goto fail;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	buffer += n;
4718c2ecf20Sopenharmony_ci	size -= n;
4728c2ecf20Sopenharmony_ci	total += n;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci fail:
4758c2ecf20Sopenharmony_ci	return total;
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci/**
4798c2ecf20Sopenharmony_ci * ppc4xx_edac_generate_ecc_message - generate interpretted ECC status message
4808c2ecf20Sopenharmony_ci * @mci: A pointer to the EDAC memory controller instance associated
4818c2ecf20Sopenharmony_ci *       with the ECCES message being generated.
4828c2ecf20Sopenharmony_ci * @status: A pointer to the ECC status structure to generate the
4838c2ecf20Sopenharmony_ci *          message from.
4848c2ecf20Sopenharmony_ci * @buffer: A pointer to the buffer in which to generate the
4858c2ecf20Sopenharmony_ci *          message.
4868c2ecf20Sopenharmony_ci * @size: The size, in bytes, of space available in buffer.
4878c2ecf20Sopenharmony_ci *
4888c2ecf20Sopenharmony_ci * This routine generates to the provided buffer the portion of the
4898c2ecf20Sopenharmony_ci * driver-unique report message associated with the ECCESS register of
4908c2ecf20Sopenharmony_ci * the specified ECC status.
4918c2ecf20Sopenharmony_ci *
4928c2ecf20Sopenharmony_ci * Returns the number of characters generated on success; otherwise, <
4938c2ecf20Sopenharmony_ci * 0 on error.
4948c2ecf20Sopenharmony_ci */
4958c2ecf20Sopenharmony_cistatic int
4968c2ecf20Sopenharmony_cippc4xx_edac_generate_ecc_message(const struct mem_ctl_info *mci,
4978c2ecf20Sopenharmony_ci				 const struct ppc4xx_ecc_status *status,
4988c2ecf20Sopenharmony_ci				 char *buffer,
4998c2ecf20Sopenharmony_ci				 size_t size)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	int n, total = 0;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	n = ppc4xx_edac_generate_bank_message(mci, status, buffer, size);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	if (n < 0 || n >= size)
5068c2ecf20Sopenharmony_ci		goto fail;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	buffer += n;
5098c2ecf20Sopenharmony_ci	size -= n;
5108c2ecf20Sopenharmony_ci	total += n;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	n = ppc4xx_edac_generate_checkbit_message(mci, status, buffer, size);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	if (n < 0 || n >= size)
5158c2ecf20Sopenharmony_ci		goto fail;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	buffer += n;
5188c2ecf20Sopenharmony_ci	size -= n;
5198c2ecf20Sopenharmony_ci	total += n;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	n = ppc4xx_edac_generate_lane_message(mci, status, buffer, size);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	if (n < 0 || n >= size)
5248c2ecf20Sopenharmony_ci		goto fail;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	buffer += n;
5278c2ecf20Sopenharmony_ci	size -= n;
5288c2ecf20Sopenharmony_ci	total += n;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci fail:
5318c2ecf20Sopenharmony_ci	return total;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci/**
5358c2ecf20Sopenharmony_ci * ppc4xx_edac_generate_plb_message - generate interpretted PLB status message
5368c2ecf20Sopenharmony_ci * @mci: A pointer to the EDAC memory controller instance associated
5378c2ecf20Sopenharmony_ci *       with the PLB message being generated.
5388c2ecf20Sopenharmony_ci * @status: A pointer to the ECC status structure to generate the
5398c2ecf20Sopenharmony_ci *          message from.
5408c2ecf20Sopenharmony_ci * @buffer: A pointer to the buffer in which to generate the
5418c2ecf20Sopenharmony_ci *          message.
5428c2ecf20Sopenharmony_ci * @size: The size, in bytes, of space available in buffer.
5438c2ecf20Sopenharmony_ci *
5448c2ecf20Sopenharmony_ci * This routine generates to the provided buffer the portion of the
5458c2ecf20Sopenharmony_ci * driver-unique report message associated with the PLB-related BESR
5468c2ecf20Sopenharmony_ci * and/or WMIRQ registers of the specified ECC status.
5478c2ecf20Sopenharmony_ci *
5488c2ecf20Sopenharmony_ci * Returns the number of characters generated on success; otherwise, <
5498c2ecf20Sopenharmony_ci * 0 on error.
5508c2ecf20Sopenharmony_ci */
5518c2ecf20Sopenharmony_cistatic int
5528c2ecf20Sopenharmony_cippc4xx_edac_generate_plb_message(const struct mem_ctl_info *mci,
5538c2ecf20Sopenharmony_ci				 const struct ppc4xx_ecc_status *status,
5548c2ecf20Sopenharmony_ci				 char *buffer,
5558c2ecf20Sopenharmony_ci				 size_t size)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	unsigned int master;
5588c2ecf20Sopenharmony_ci	bool read;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	if ((status->besr & SDRAM_BESR_MASK) == 0)
5618c2ecf20Sopenharmony_ci		return 0;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	if ((status->besr & SDRAM_BESR_M0ET_MASK) == SDRAM_BESR_M0ET_NONE)
5648c2ecf20Sopenharmony_ci		return 0;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	read = ((status->besr & SDRAM_BESR_M0RW_MASK) == SDRAM_BESR_M0RW_READ);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	master = SDRAM_BESR_M0ID_DECODE(status->besr);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	return snprintf(buffer, size,
5718c2ecf20Sopenharmony_ci			"%s error w/ PLB master %u \"%s\"; ",
5728c2ecf20Sopenharmony_ci			(read ? "Read" : "Write"),
5738c2ecf20Sopenharmony_ci			master,
5748c2ecf20Sopenharmony_ci			(((master >= SDRAM_PLB_M0ID_FIRST) &&
5758c2ecf20Sopenharmony_ci			  (master <= SDRAM_PLB_M0ID_LAST)) ?
5768c2ecf20Sopenharmony_ci			 ppc4xx_plb_masters[master] : "UNKNOWN"));
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci/**
5808c2ecf20Sopenharmony_ci * ppc4xx_edac_generate_message - generate interpretted status message
5818c2ecf20Sopenharmony_ci * @mci: A pointer to the EDAC memory controller instance associated
5828c2ecf20Sopenharmony_ci *       with the driver-unique message being generated.
5838c2ecf20Sopenharmony_ci * @status: A pointer to the ECC status structure to generate the
5848c2ecf20Sopenharmony_ci *          message from.
5858c2ecf20Sopenharmony_ci * @buffer: A pointer to the buffer in which to generate the
5868c2ecf20Sopenharmony_ci *          message.
5878c2ecf20Sopenharmony_ci * @size: The size, in bytes, of space available in buffer.
5888c2ecf20Sopenharmony_ci *
5898c2ecf20Sopenharmony_ci * This routine generates to the provided buffer the driver-unique
5908c2ecf20Sopenharmony_ci * EDAC report message from the specified ECC status.
5918c2ecf20Sopenharmony_ci */
5928c2ecf20Sopenharmony_cistatic void
5938c2ecf20Sopenharmony_cippc4xx_edac_generate_message(const struct mem_ctl_info *mci,
5948c2ecf20Sopenharmony_ci			     const struct ppc4xx_ecc_status *status,
5958c2ecf20Sopenharmony_ci			     char *buffer,
5968c2ecf20Sopenharmony_ci			     size_t size)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	int n;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	if (buffer == NULL || size == 0)
6018c2ecf20Sopenharmony_ci		return;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	n = ppc4xx_edac_generate_ecc_message(mci, status, buffer, size);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (n < 0 || n >= size)
6068c2ecf20Sopenharmony_ci		return;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	buffer += n;
6098c2ecf20Sopenharmony_ci	size -= n;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	ppc4xx_edac_generate_plb_message(mci, status, buffer, size);
6128c2ecf20Sopenharmony_ci}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci#ifdef DEBUG
6158c2ecf20Sopenharmony_ci/**
6168c2ecf20Sopenharmony_ci * ppc4xx_ecc_dump_status - dump controller ECC status registers
6178c2ecf20Sopenharmony_ci * @mci: A pointer to the EDAC memory controller instance
6188c2ecf20Sopenharmony_ci *       associated with the status being dumped.
6198c2ecf20Sopenharmony_ci * @status: A pointer to the ECC status structure to generate the
6208c2ecf20Sopenharmony_ci *          dump from.
6218c2ecf20Sopenharmony_ci *
6228c2ecf20Sopenharmony_ci * This routine dumps to the kernel log buffer the raw and
6238c2ecf20Sopenharmony_ci * interpretted specified ECC status.
6248c2ecf20Sopenharmony_ci */
6258c2ecf20Sopenharmony_cistatic void
6268c2ecf20Sopenharmony_cippc4xx_ecc_dump_status(const struct mem_ctl_info *mci,
6278c2ecf20Sopenharmony_ci		       const struct ppc4xx_ecc_status *status)
6288c2ecf20Sopenharmony_ci{
6298c2ecf20Sopenharmony_ci	char message[PPC4XX_EDAC_MESSAGE_SIZE];
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	ppc4xx_edac_generate_message(mci, status, message, sizeof(message));
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	ppc4xx_edac_mc_printk(KERN_INFO, mci,
6348c2ecf20Sopenharmony_ci			      "\n"
6358c2ecf20Sopenharmony_ci			      "\tECCES: 0x%08x\n"
6368c2ecf20Sopenharmony_ci			      "\tWMIRQ: 0x%08x\n"
6378c2ecf20Sopenharmony_ci			      "\tBESR:  0x%08x\n"
6388c2ecf20Sopenharmony_ci			      "\tBEAR:  0x%08x%08x\n"
6398c2ecf20Sopenharmony_ci			      "\t%s\n",
6408c2ecf20Sopenharmony_ci			      status->ecces,
6418c2ecf20Sopenharmony_ci			      status->wmirq,
6428c2ecf20Sopenharmony_ci			      status->besr,
6438c2ecf20Sopenharmony_ci			      status->bearh,
6448c2ecf20Sopenharmony_ci			      status->bearl,
6458c2ecf20Sopenharmony_ci			      message);
6468c2ecf20Sopenharmony_ci}
6478c2ecf20Sopenharmony_ci#endif /* DEBUG */
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci/**
6508c2ecf20Sopenharmony_ci * ppc4xx_ecc_get_status - get controller ECC status
6518c2ecf20Sopenharmony_ci * @mci: A pointer to the EDAC memory controller instance
6528c2ecf20Sopenharmony_ci *       associated with the status being retrieved.
6538c2ecf20Sopenharmony_ci * @status: A pointer to the ECC status structure to populate the
6548c2ecf20Sopenharmony_ci *          ECC status with.
6558c2ecf20Sopenharmony_ci *
6568c2ecf20Sopenharmony_ci * This routine reads and masks, as appropriate, all the relevant
6578c2ecf20Sopenharmony_ci * status registers that deal with ibm,sdram-4xx-ddr2 ECC errors.
6588c2ecf20Sopenharmony_ci * While we read all of them, for correctable errors, we only expect
6598c2ecf20Sopenharmony_ci * to deal with ECCES. For uncorrectable errors, we expect to deal
6608c2ecf20Sopenharmony_ci * with all of them.
6618c2ecf20Sopenharmony_ci */
6628c2ecf20Sopenharmony_cistatic void
6638c2ecf20Sopenharmony_cippc4xx_ecc_get_status(const struct mem_ctl_info *mci,
6648c2ecf20Sopenharmony_ci		      struct ppc4xx_ecc_status *status)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	const struct ppc4xx_edac_pdata *pdata = mci->pvt_info;
6678c2ecf20Sopenharmony_ci	const dcr_host_t *dcr_host = &pdata->dcr_host;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	status->ecces = mfsdram(dcr_host, SDRAM_ECCES) & SDRAM_ECCES_MASK;
6708c2ecf20Sopenharmony_ci	status->wmirq = mfsdram(dcr_host, SDRAM_WMIRQ) & SDRAM_WMIRQ_MASK;
6718c2ecf20Sopenharmony_ci	status->besr  = mfsdram(dcr_host, SDRAM_BESR)  & SDRAM_BESR_MASK;
6728c2ecf20Sopenharmony_ci	status->bearl = mfsdram(dcr_host, SDRAM_BEARL);
6738c2ecf20Sopenharmony_ci	status->bearh = mfsdram(dcr_host, SDRAM_BEARH);
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci/**
6778c2ecf20Sopenharmony_ci * ppc4xx_ecc_clear_status - clear controller ECC status
6788c2ecf20Sopenharmony_ci * @mci: A pointer to the EDAC memory controller instance
6798c2ecf20Sopenharmony_ci *       associated with the status being cleared.
6808c2ecf20Sopenharmony_ci * @status: A pointer to the ECC status structure containing the
6818c2ecf20Sopenharmony_ci *          values to write to clear the ECC status.
6828c2ecf20Sopenharmony_ci *
6838c2ecf20Sopenharmony_ci * This routine clears--by writing the masked (as appropriate) status
6848c2ecf20Sopenharmony_ci * values back to--the status registers that deal with
6858c2ecf20Sopenharmony_ci * ibm,sdram-4xx-ddr2 ECC errors.
6868c2ecf20Sopenharmony_ci */
6878c2ecf20Sopenharmony_cistatic void
6888c2ecf20Sopenharmony_cippc4xx_ecc_clear_status(const struct mem_ctl_info *mci,
6898c2ecf20Sopenharmony_ci			const struct ppc4xx_ecc_status *status)
6908c2ecf20Sopenharmony_ci{
6918c2ecf20Sopenharmony_ci	const struct ppc4xx_edac_pdata *pdata = mci->pvt_info;
6928c2ecf20Sopenharmony_ci	const dcr_host_t *dcr_host = &pdata->dcr_host;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	mtsdram(dcr_host, SDRAM_ECCES,	status->ecces & SDRAM_ECCES_MASK);
6958c2ecf20Sopenharmony_ci	mtsdram(dcr_host, SDRAM_WMIRQ,	status->wmirq & SDRAM_WMIRQ_MASK);
6968c2ecf20Sopenharmony_ci	mtsdram(dcr_host, SDRAM_BESR,	status->besr & SDRAM_BESR_MASK);
6978c2ecf20Sopenharmony_ci	mtsdram(dcr_host, SDRAM_BEARL,	0);
6988c2ecf20Sopenharmony_ci	mtsdram(dcr_host, SDRAM_BEARH,	0);
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci/**
7028c2ecf20Sopenharmony_ci * ppc4xx_edac_handle_ce - handle controller correctable ECC error (CE)
7038c2ecf20Sopenharmony_ci * @mci: A pointer to the EDAC memory controller instance
7048c2ecf20Sopenharmony_ci *       associated with the correctable error being handled and reported.
7058c2ecf20Sopenharmony_ci * @status: A pointer to the ECC status structure associated with
7068c2ecf20Sopenharmony_ci *          the correctable error being handled and reported.
7078c2ecf20Sopenharmony_ci *
7088c2ecf20Sopenharmony_ci * This routine handles an ibm,sdram-4xx-ddr2 controller ECC
7098c2ecf20Sopenharmony_ci * correctable error. Per the aforementioned discussion, there's not
7108c2ecf20Sopenharmony_ci * enough status available to use the full EDAC correctable error
7118c2ecf20Sopenharmony_ci * interface, so we just pass driver-unique message to the "no info"
7128c2ecf20Sopenharmony_ci * interface.
7138c2ecf20Sopenharmony_ci */
7148c2ecf20Sopenharmony_cistatic void
7158c2ecf20Sopenharmony_cippc4xx_edac_handle_ce(struct mem_ctl_info *mci,
7168c2ecf20Sopenharmony_ci		      const struct ppc4xx_ecc_status *status)
7178c2ecf20Sopenharmony_ci{
7188c2ecf20Sopenharmony_ci	int row;
7198c2ecf20Sopenharmony_ci	char message[PPC4XX_EDAC_MESSAGE_SIZE];
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	ppc4xx_edac_generate_message(mci, status, message, sizeof(message));
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	for (row = 0; row < mci->nr_csrows; row++)
7248c2ecf20Sopenharmony_ci		if (ppc4xx_edac_check_bank_error(status, row))
7258c2ecf20Sopenharmony_ci			edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
7268c2ecf20Sopenharmony_ci					     0, 0, 0,
7278c2ecf20Sopenharmony_ci					     row, 0, -1,
7288c2ecf20Sopenharmony_ci					     message, "");
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci/**
7328c2ecf20Sopenharmony_ci * ppc4xx_edac_handle_ue - handle controller uncorrectable ECC error (UE)
7338c2ecf20Sopenharmony_ci * @mci: A pointer to the EDAC memory controller instance
7348c2ecf20Sopenharmony_ci *       associated with the uncorrectable error being handled and
7358c2ecf20Sopenharmony_ci *       reported.
7368c2ecf20Sopenharmony_ci * @status: A pointer to the ECC status structure associated with
7378c2ecf20Sopenharmony_ci *          the uncorrectable error being handled and reported.
7388c2ecf20Sopenharmony_ci *
7398c2ecf20Sopenharmony_ci * This routine handles an ibm,sdram-4xx-ddr2 controller ECC
7408c2ecf20Sopenharmony_ci * uncorrectable error.
7418c2ecf20Sopenharmony_ci */
7428c2ecf20Sopenharmony_cistatic void
7438c2ecf20Sopenharmony_cippc4xx_edac_handle_ue(struct mem_ctl_info *mci,
7448c2ecf20Sopenharmony_ci		      const struct ppc4xx_ecc_status *status)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	const u64 bear = ((u64)status->bearh << 32 | status->bearl);
7478c2ecf20Sopenharmony_ci	const unsigned long page = bear >> PAGE_SHIFT;
7488c2ecf20Sopenharmony_ci	const unsigned long offset = bear & ~PAGE_MASK;
7498c2ecf20Sopenharmony_ci	int row;
7508c2ecf20Sopenharmony_ci	char message[PPC4XX_EDAC_MESSAGE_SIZE];
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	ppc4xx_edac_generate_message(mci, status, message, sizeof(message));
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	for (row = 0; row < mci->nr_csrows; row++)
7558c2ecf20Sopenharmony_ci		if (ppc4xx_edac_check_bank_error(status, row))
7568c2ecf20Sopenharmony_ci			edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
7578c2ecf20Sopenharmony_ci					     page, offset, 0,
7588c2ecf20Sopenharmony_ci					     row, 0, -1,
7598c2ecf20Sopenharmony_ci					     message, "");
7608c2ecf20Sopenharmony_ci}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci/**
7638c2ecf20Sopenharmony_ci * ppc4xx_edac_check - check controller for ECC errors
7648c2ecf20Sopenharmony_ci * @mci: A pointer to the EDAC memory controller instance
7658c2ecf20Sopenharmony_ci *       associated with the ibm,sdram-4xx-ddr2 controller being
7668c2ecf20Sopenharmony_ci *       checked.
7678c2ecf20Sopenharmony_ci *
7688c2ecf20Sopenharmony_ci * This routine is used to check and post ECC errors and is called by
7698c2ecf20Sopenharmony_ci * both the EDAC polling thread and this driver's CE and UE interrupt
7708c2ecf20Sopenharmony_ci * handler.
7718c2ecf20Sopenharmony_ci */
7728c2ecf20Sopenharmony_cistatic void
7738c2ecf20Sopenharmony_cippc4xx_edac_check(struct mem_ctl_info *mci)
7748c2ecf20Sopenharmony_ci{
7758c2ecf20Sopenharmony_ci#ifdef DEBUG
7768c2ecf20Sopenharmony_ci	static unsigned int count;
7778c2ecf20Sopenharmony_ci#endif
7788c2ecf20Sopenharmony_ci	struct ppc4xx_ecc_status status;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	ppc4xx_ecc_get_status(mci, &status);
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci#ifdef DEBUG
7838c2ecf20Sopenharmony_ci	if (count++ % 30 == 0)
7848c2ecf20Sopenharmony_ci		ppc4xx_ecc_dump_status(mci, &status);
7858c2ecf20Sopenharmony_ci#endif
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	if (status.ecces & SDRAM_ECCES_UE)
7888c2ecf20Sopenharmony_ci		ppc4xx_edac_handle_ue(mci, &status);
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	if (status.ecces & SDRAM_ECCES_CE)
7918c2ecf20Sopenharmony_ci		ppc4xx_edac_handle_ce(mci, &status);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	ppc4xx_ecc_clear_status(mci, &status);
7948c2ecf20Sopenharmony_ci}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci/**
7978c2ecf20Sopenharmony_ci * ppc4xx_edac_isr - SEC (CE) and DED (UE) interrupt service routine
7988c2ecf20Sopenharmony_ci * @irq:    The virtual interrupt number being serviced.
7998c2ecf20Sopenharmony_ci * @dev_id: A pointer to the EDAC memory controller instance
8008c2ecf20Sopenharmony_ci *          associated with the interrupt being handled.
8018c2ecf20Sopenharmony_ci *
8028c2ecf20Sopenharmony_ci * This routine implements the interrupt handler for both correctable
8038c2ecf20Sopenharmony_ci * (CE) and uncorrectable (UE) ECC errors for the ibm,sdram-4xx-ddr2
8048c2ecf20Sopenharmony_ci * controller. It simply calls through to the same routine used during
8058c2ecf20Sopenharmony_ci * polling to check, report and clear the ECC status.
8068c2ecf20Sopenharmony_ci *
8078c2ecf20Sopenharmony_ci * Unconditionally returns IRQ_HANDLED.
8088c2ecf20Sopenharmony_ci */
8098c2ecf20Sopenharmony_cistatic irqreturn_t
8108c2ecf20Sopenharmony_cippc4xx_edac_isr(int irq, void *dev_id)
8118c2ecf20Sopenharmony_ci{
8128c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci = dev_id;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	ppc4xx_edac_check(mci);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci/**
8208c2ecf20Sopenharmony_ci * ppc4xx_edac_get_dtype - return the controller memory width
8218c2ecf20Sopenharmony_ci * @mcopt1: The 32-bit Memory Controller Option 1 register value
8228c2ecf20Sopenharmony_ci *          currently set for the controller, from which the width
8238c2ecf20Sopenharmony_ci *          is derived.
8248c2ecf20Sopenharmony_ci *
8258c2ecf20Sopenharmony_ci * This routine returns the EDAC device type width appropriate for the
8268c2ecf20Sopenharmony_ci * current controller configuration.
8278c2ecf20Sopenharmony_ci *
8288c2ecf20Sopenharmony_ci * TODO: This needs to be conditioned dynamically through feature
8298c2ecf20Sopenharmony_ci * flags or some such when other controller variants are supported as
8308c2ecf20Sopenharmony_ci * the 405EX[r] is 16-/32-bit and the others are 32-/64-bit with the
8318c2ecf20Sopenharmony_ci * 16- and 64-bit field definition/value/enumeration (b1) overloaded
8328c2ecf20Sopenharmony_ci * among them.
8338c2ecf20Sopenharmony_ci *
8348c2ecf20Sopenharmony_ci * Returns a device type width enumeration.
8358c2ecf20Sopenharmony_ci */
8368c2ecf20Sopenharmony_cistatic enum dev_type ppc4xx_edac_get_dtype(u32 mcopt1)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	switch (mcopt1 & SDRAM_MCOPT1_WDTH_MASK) {
8398c2ecf20Sopenharmony_ci	case SDRAM_MCOPT1_WDTH_16:
8408c2ecf20Sopenharmony_ci		return DEV_X2;
8418c2ecf20Sopenharmony_ci	case SDRAM_MCOPT1_WDTH_32:
8428c2ecf20Sopenharmony_ci		return DEV_X4;
8438c2ecf20Sopenharmony_ci	default:
8448c2ecf20Sopenharmony_ci		return DEV_UNKNOWN;
8458c2ecf20Sopenharmony_ci	}
8468c2ecf20Sopenharmony_ci}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci/**
8498c2ecf20Sopenharmony_ci * ppc4xx_edac_get_mtype - return controller memory type
8508c2ecf20Sopenharmony_ci * @mcopt1: The 32-bit Memory Controller Option 1 register value
8518c2ecf20Sopenharmony_ci *          currently set for the controller, from which the memory type
8528c2ecf20Sopenharmony_ci *          is derived.
8538c2ecf20Sopenharmony_ci *
8548c2ecf20Sopenharmony_ci * This routine returns the EDAC memory type appropriate for the
8558c2ecf20Sopenharmony_ci * current controller configuration.
8568c2ecf20Sopenharmony_ci *
8578c2ecf20Sopenharmony_ci * Returns a memory type enumeration.
8588c2ecf20Sopenharmony_ci */
8598c2ecf20Sopenharmony_cistatic enum mem_type ppc4xx_edac_get_mtype(u32 mcopt1)
8608c2ecf20Sopenharmony_ci{
8618c2ecf20Sopenharmony_ci	bool rden = ((mcopt1 & SDRAM_MCOPT1_RDEN_MASK) == SDRAM_MCOPT1_RDEN);
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	switch (mcopt1 & SDRAM_MCOPT1_DDR_TYPE_MASK) {
8648c2ecf20Sopenharmony_ci	case SDRAM_MCOPT1_DDR2_TYPE:
8658c2ecf20Sopenharmony_ci		return rden ? MEM_RDDR2 : MEM_DDR2;
8668c2ecf20Sopenharmony_ci	case SDRAM_MCOPT1_DDR1_TYPE:
8678c2ecf20Sopenharmony_ci		return rden ? MEM_RDDR : MEM_DDR;
8688c2ecf20Sopenharmony_ci	default:
8698c2ecf20Sopenharmony_ci		return MEM_UNKNOWN;
8708c2ecf20Sopenharmony_ci	}
8718c2ecf20Sopenharmony_ci}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci/**
8748c2ecf20Sopenharmony_ci * ppc4xx_edac_init_csrows - initialize driver instance rows
8758c2ecf20Sopenharmony_ci * @mci: A pointer to the EDAC memory controller instance
8768c2ecf20Sopenharmony_ci *       associated with the ibm,sdram-4xx-ddr2 controller for which
8778c2ecf20Sopenharmony_ci *       the csrows (i.e. banks/ranks) are being initialized.
8788c2ecf20Sopenharmony_ci * @mcopt1: The 32-bit Memory Controller Option 1 register value
8798c2ecf20Sopenharmony_ci *          currently set for the controller, from which bank width
8808c2ecf20Sopenharmony_ci *          and memory typ information is derived.
8818c2ecf20Sopenharmony_ci *
8828c2ecf20Sopenharmony_ci * This routine initializes the virtual "chip select rows" associated
8838c2ecf20Sopenharmony_ci * with the EDAC memory controller instance. An ibm,sdram-4xx-ddr2
8848c2ecf20Sopenharmony_ci * controller bank/rank is mapped to a row.
8858c2ecf20Sopenharmony_ci *
8868c2ecf20Sopenharmony_ci * Returns 0 if OK; otherwise, -EINVAL if the memory bank size
8878c2ecf20Sopenharmony_ci * configuration cannot be determined.
8888c2ecf20Sopenharmony_ci */
8898c2ecf20Sopenharmony_cistatic int ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1)
8908c2ecf20Sopenharmony_ci{
8918c2ecf20Sopenharmony_ci	const struct ppc4xx_edac_pdata *pdata = mci->pvt_info;
8928c2ecf20Sopenharmony_ci	int status = 0;
8938c2ecf20Sopenharmony_ci	enum mem_type mtype;
8948c2ecf20Sopenharmony_ci	enum dev_type dtype;
8958c2ecf20Sopenharmony_ci	enum edac_type edac_mode;
8968c2ecf20Sopenharmony_ci	int row, j;
8978c2ecf20Sopenharmony_ci	u32 mbxcf, size, nr_pages;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	/* Establish the memory type and width */
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	mtype = ppc4xx_edac_get_mtype(mcopt1);
9028c2ecf20Sopenharmony_ci	dtype = ppc4xx_edac_get_dtype(mcopt1);
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	/* Establish EDAC mode */
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	if (mci->edac_cap & EDAC_FLAG_SECDED)
9078c2ecf20Sopenharmony_ci		edac_mode = EDAC_SECDED;
9088c2ecf20Sopenharmony_ci	else if (mci->edac_cap & EDAC_FLAG_EC)
9098c2ecf20Sopenharmony_ci		edac_mode = EDAC_EC;
9108c2ecf20Sopenharmony_ci	else
9118c2ecf20Sopenharmony_ci		edac_mode = EDAC_NONE;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	/*
9148c2ecf20Sopenharmony_ci	 * Initialize each chip select row structure which correspond
9158c2ecf20Sopenharmony_ci	 * 1:1 with a controller bank/rank.
9168c2ecf20Sopenharmony_ci	 */
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	for (row = 0; row < mci->nr_csrows; row++) {
9198c2ecf20Sopenharmony_ci		struct csrow_info *csi = mci->csrows[row];
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci		/*
9228c2ecf20Sopenharmony_ci		 * Get the configuration settings for this
9238c2ecf20Sopenharmony_ci		 * row/bank/rank and skip disabled banks.
9248c2ecf20Sopenharmony_ci		 */
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci		mbxcf = mfsdram(&pdata->dcr_host, SDRAM_MBXCF(row));
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci		if ((mbxcf & SDRAM_MBCF_BE_MASK) != SDRAM_MBCF_BE_ENABLE)
9298c2ecf20Sopenharmony_ci			continue;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci		/* Map the bank configuration size setting to pages. */
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci		size = mbxcf & SDRAM_MBCF_SZ_MASK;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci		switch (size) {
9368c2ecf20Sopenharmony_ci		case SDRAM_MBCF_SZ_4MB:
9378c2ecf20Sopenharmony_ci		case SDRAM_MBCF_SZ_8MB:
9388c2ecf20Sopenharmony_ci		case SDRAM_MBCF_SZ_16MB:
9398c2ecf20Sopenharmony_ci		case SDRAM_MBCF_SZ_32MB:
9408c2ecf20Sopenharmony_ci		case SDRAM_MBCF_SZ_64MB:
9418c2ecf20Sopenharmony_ci		case SDRAM_MBCF_SZ_128MB:
9428c2ecf20Sopenharmony_ci		case SDRAM_MBCF_SZ_256MB:
9438c2ecf20Sopenharmony_ci		case SDRAM_MBCF_SZ_512MB:
9448c2ecf20Sopenharmony_ci		case SDRAM_MBCF_SZ_1GB:
9458c2ecf20Sopenharmony_ci		case SDRAM_MBCF_SZ_2GB:
9468c2ecf20Sopenharmony_ci		case SDRAM_MBCF_SZ_4GB:
9478c2ecf20Sopenharmony_ci		case SDRAM_MBCF_SZ_8GB:
9488c2ecf20Sopenharmony_ci			nr_pages = SDRAM_MBCF_SZ_TO_PAGES(size);
9498c2ecf20Sopenharmony_ci			break;
9508c2ecf20Sopenharmony_ci		default:
9518c2ecf20Sopenharmony_ci			ppc4xx_edac_mc_printk(KERN_ERR, mci,
9528c2ecf20Sopenharmony_ci					      "Unrecognized memory bank %d "
9538c2ecf20Sopenharmony_ci					      "size 0x%08x\n",
9548c2ecf20Sopenharmony_ci					      row, SDRAM_MBCF_SZ_DECODE(size));
9558c2ecf20Sopenharmony_ci			status = -EINVAL;
9568c2ecf20Sopenharmony_ci			goto done;
9578c2ecf20Sopenharmony_ci		}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci		/*
9608c2ecf20Sopenharmony_ci		 * It's unclear exactly what grain should be set to
9618c2ecf20Sopenharmony_ci		 * here. The SDRAM_ECCES register allows resolution of
9628c2ecf20Sopenharmony_ci		 * an error down to a nibble which would potentially
9638c2ecf20Sopenharmony_ci		 * argue for a grain of '1' byte, even though we only
9648c2ecf20Sopenharmony_ci		 * know the associated address for uncorrectable
9658c2ecf20Sopenharmony_ci		 * errors. This value is not used at present for
9668c2ecf20Sopenharmony_ci		 * anything other than error reporting so getting it
9678c2ecf20Sopenharmony_ci		 * wrong should be of little consequence. Other
9688c2ecf20Sopenharmony_ci		 * possible values would be the PLB width (16), the
9698c2ecf20Sopenharmony_ci		 * page size (PAGE_SIZE) or the memory width (2 or 4).
9708c2ecf20Sopenharmony_ci		 */
9718c2ecf20Sopenharmony_ci		for (j = 0; j < csi->nr_channels; j++) {
9728c2ecf20Sopenharmony_ci			struct dimm_info *dimm = csi->channels[j]->dimm;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci			dimm->nr_pages  = nr_pages / csi->nr_channels;
9758c2ecf20Sopenharmony_ci			dimm->grain	= 1;
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci			dimm->mtype	= mtype;
9788c2ecf20Sopenharmony_ci			dimm->dtype	= dtype;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci			dimm->edac_mode	= edac_mode;
9818c2ecf20Sopenharmony_ci		}
9828c2ecf20Sopenharmony_ci	}
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci done:
9858c2ecf20Sopenharmony_ci	return status;
9868c2ecf20Sopenharmony_ci}
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci/**
9898c2ecf20Sopenharmony_ci * ppc4xx_edac_mc_init - initialize driver instance
9908c2ecf20Sopenharmony_ci * @mci: A pointer to the EDAC memory controller instance being
9918c2ecf20Sopenharmony_ci *       initialized.
9928c2ecf20Sopenharmony_ci * @op: A pointer to the OpenFirmware device tree node associated
9938c2ecf20Sopenharmony_ci *      with the controller this EDAC instance is bound to.
9948c2ecf20Sopenharmony_ci * @dcr_host: A pointer to the DCR data containing the DCR mapping
9958c2ecf20Sopenharmony_ci *            for this controller instance.
9968c2ecf20Sopenharmony_ci * @mcopt1: The 32-bit Memory Controller Option 1 register value
9978c2ecf20Sopenharmony_ci *          currently set for the controller, from which ECC capabilities
9988c2ecf20Sopenharmony_ci *          and scrub mode are derived.
9998c2ecf20Sopenharmony_ci *
10008c2ecf20Sopenharmony_ci * This routine performs initialization of the EDAC memory controller
10018c2ecf20Sopenharmony_ci * instance and related driver-private data associated with the
10028c2ecf20Sopenharmony_ci * ibm,sdram-4xx-ddr2 memory controller the instance is bound to.
10038c2ecf20Sopenharmony_ci *
10048c2ecf20Sopenharmony_ci * Returns 0 if OK; otherwise, < 0 on error.
10058c2ecf20Sopenharmony_ci */
10068c2ecf20Sopenharmony_cistatic int ppc4xx_edac_mc_init(struct mem_ctl_info *mci,
10078c2ecf20Sopenharmony_ci			       struct platform_device *op,
10088c2ecf20Sopenharmony_ci			       const dcr_host_t *dcr_host, u32 mcopt1)
10098c2ecf20Sopenharmony_ci{
10108c2ecf20Sopenharmony_ci	int status = 0;
10118c2ecf20Sopenharmony_ci	const u32 memcheck = (mcopt1 & SDRAM_MCOPT1_MCHK_MASK);
10128c2ecf20Sopenharmony_ci	struct ppc4xx_edac_pdata *pdata = NULL;
10138c2ecf20Sopenharmony_ci	const struct device_node *np = op->dev.of_node;
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	if (of_match_device(ppc4xx_edac_match, &op->dev) == NULL)
10168c2ecf20Sopenharmony_ci		return -EINVAL;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	/* Initial driver pointers and private data */
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	mci->pdev		= &op->dev;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	dev_set_drvdata(mci->pdev, mci);
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	pdata			= mci->pvt_info;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	pdata->dcr_host		= *dcr_host;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	/* Initialize controller capabilities and configuration */
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	mci->mtype_cap		= (MEM_FLAG_DDR | MEM_FLAG_RDDR |
10318c2ecf20Sopenharmony_ci				   MEM_FLAG_DDR2 | MEM_FLAG_RDDR2);
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	mci->edac_ctl_cap	= (EDAC_FLAG_NONE |
10348c2ecf20Sopenharmony_ci				   EDAC_FLAG_EC |
10358c2ecf20Sopenharmony_ci				   EDAC_FLAG_SECDED);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	mci->scrub_cap		= SCRUB_NONE;
10388c2ecf20Sopenharmony_ci	mci->scrub_mode		= SCRUB_NONE;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	/*
10418c2ecf20Sopenharmony_ci	 * Update the actual capabilites based on the MCOPT1[MCHK]
10428c2ecf20Sopenharmony_ci	 * settings. Scrubbing is only useful if reporting is enabled.
10438c2ecf20Sopenharmony_ci	 */
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	switch (memcheck) {
10468c2ecf20Sopenharmony_ci	case SDRAM_MCOPT1_MCHK_CHK:
10478c2ecf20Sopenharmony_ci		mci->edac_cap	= EDAC_FLAG_EC;
10488c2ecf20Sopenharmony_ci		break;
10498c2ecf20Sopenharmony_ci	case SDRAM_MCOPT1_MCHK_CHK_REP:
10508c2ecf20Sopenharmony_ci		mci->edac_cap	= (EDAC_FLAG_EC | EDAC_FLAG_SECDED);
10518c2ecf20Sopenharmony_ci		mci->scrub_mode	= SCRUB_SW_SRC;
10528c2ecf20Sopenharmony_ci		break;
10538c2ecf20Sopenharmony_ci	default:
10548c2ecf20Sopenharmony_ci		mci->edac_cap	= EDAC_FLAG_NONE;
10558c2ecf20Sopenharmony_ci		break;
10568c2ecf20Sopenharmony_ci	}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	/* Initialize strings */
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	mci->mod_name		= PPC4XX_EDAC_MODULE_NAME;
10618c2ecf20Sopenharmony_ci	mci->ctl_name		= ppc4xx_edac_match->compatible,
10628c2ecf20Sopenharmony_ci	mci->dev_name		= np->full_name;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	/* Initialize callbacks */
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	mci->edac_check		= ppc4xx_edac_check;
10678c2ecf20Sopenharmony_ci	mci->ctl_page_to_phys	= NULL;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	/* Initialize chip select rows */
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	status = ppc4xx_edac_init_csrows(mci, mcopt1);
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	if (status)
10748c2ecf20Sopenharmony_ci		ppc4xx_edac_mc_printk(KERN_ERR, mci,
10758c2ecf20Sopenharmony_ci				      "Failed to initialize rows!\n");
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	return status;
10788c2ecf20Sopenharmony_ci}
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci/**
10818c2ecf20Sopenharmony_ci * ppc4xx_edac_register_irq - setup and register controller interrupts
10828c2ecf20Sopenharmony_ci * @op: A pointer to the OpenFirmware device tree node associated
10838c2ecf20Sopenharmony_ci *      with the controller this EDAC instance is bound to.
10848c2ecf20Sopenharmony_ci * @mci: A pointer to the EDAC memory controller instance
10858c2ecf20Sopenharmony_ci *       associated with the ibm,sdram-4xx-ddr2 controller for which
10868c2ecf20Sopenharmony_ci *       interrupts are being registered.
10878c2ecf20Sopenharmony_ci *
10888c2ecf20Sopenharmony_ci * This routine parses the correctable (CE) and uncorrectable error (UE)
10898c2ecf20Sopenharmony_ci * interrupts from the device tree node and maps and assigns them to
10908c2ecf20Sopenharmony_ci * the associated EDAC memory controller instance.
10918c2ecf20Sopenharmony_ci *
10928c2ecf20Sopenharmony_ci * Returns 0 if OK; otherwise, -ENODEV if the interrupts could not be
10938c2ecf20Sopenharmony_ci * mapped and assigned.
10948c2ecf20Sopenharmony_ci */
10958c2ecf20Sopenharmony_cistatic int ppc4xx_edac_register_irq(struct platform_device *op,
10968c2ecf20Sopenharmony_ci				    struct mem_ctl_info *mci)
10978c2ecf20Sopenharmony_ci{
10988c2ecf20Sopenharmony_ci	int status = 0;
10998c2ecf20Sopenharmony_ci	int ded_irq, sec_irq;
11008c2ecf20Sopenharmony_ci	struct ppc4xx_edac_pdata *pdata = mci->pvt_info;
11018c2ecf20Sopenharmony_ci	struct device_node *np = op->dev.of_node;
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	ded_irq = irq_of_parse_and_map(np, INTMAP_ECCDED_INDEX);
11048c2ecf20Sopenharmony_ci	sec_irq = irq_of_parse_and_map(np, INTMAP_ECCSEC_INDEX);
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	if (!ded_irq || !sec_irq) {
11078c2ecf20Sopenharmony_ci		ppc4xx_edac_mc_printk(KERN_ERR, mci,
11088c2ecf20Sopenharmony_ci				      "Unable to map interrupts.\n");
11098c2ecf20Sopenharmony_ci		status = -ENODEV;
11108c2ecf20Sopenharmony_ci		goto fail;
11118c2ecf20Sopenharmony_ci	}
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	status = request_irq(ded_irq,
11148c2ecf20Sopenharmony_ci			     ppc4xx_edac_isr,
11158c2ecf20Sopenharmony_ci			     0,
11168c2ecf20Sopenharmony_ci			     "[EDAC] MC ECCDED",
11178c2ecf20Sopenharmony_ci			     mci);
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	if (status < 0) {
11208c2ecf20Sopenharmony_ci		ppc4xx_edac_mc_printk(KERN_ERR, mci,
11218c2ecf20Sopenharmony_ci				      "Unable to request irq %d for ECC DED",
11228c2ecf20Sopenharmony_ci				      ded_irq);
11238c2ecf20Sopenharmony_ci		status = -ENODEV;
11248c2ecf20Sopenharmony_ci		goto fail1;
11258c2ecf20Sopenharmony_ci	}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	status = request_irq(sec_irq,
11288c2ecf20Sopenharmony_ci			     ppc4xx_edac_isr,
11298c2ecf20Sopenharmony_ci			     0,
11308c2ecf20Sopenharmony_ci			     "[EDAC] MC ECCSEC",
11318c2ecf20Sopenharmony_ci			     mci);
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	if (status < 0) {
11348c2ecf20Sopenharmony_ci		ppc4xx_edac_mc_printk(KERN_ERR, mci,
11358c2ecf20Sopenharmony_ci				      "Unable to request irq %d for ECC SEC",
11368c2ecf20Sopenharmony_ci				      sec_irq);
11378c2ecf20Sopenharmony_ci		status = -ENODEV;
11388c2ecf20Sopenharmony_ci		goto fail2;
11398c2ecf20Sopenharmony_ci	}
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci	ppc4xx_edac_mc_printk(KERN_INFO, mci, "ECCDED irq is %d\n", ded_irq);
11428c2ecf20Sopenharmony_ci	ppc4xx_edac_mc_printk(KERN_INFO, mci, "ECCSEC irq is %d\n", sec_irq);
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	pdata->irqs.ded = ded_irq;
11458c2ecf20Sopenharmony_ci	pdata->irqs.sec = sec_irq;
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	return 0;
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci fail2:
11508c2ecf20Sopenharmony_ci	free_irq(sec_irq, mci);
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci fail1:
11538c2ecf20Sopenharmony_ci	free_irq(ded_irq, mci);
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci fail:
11568c2ecf20Sopenharmony_ci	return status;
11578c2ecf20Sopenharmony_ci}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci/**
11608c2ecf20Sopenharmony_ci * ppc4xx_edac_map_dcrs - locate and map controller registers
11618c2ecf20Sopenharmony_ci * @np: A pointer to the device tree node containing the DCR
11628c2ecf20Sopenharmony_ci *      resources to map.
11638c2ecf20Sopenharmony_ci * @dcr_host: A pointer to the DCR data to populate with the
11648c2ecf20Sopenharmony_ci *            DCR mapping.
11658c2ecf20Sopenharmony_ci *
11668c2ecf20Sopenharmony_ci * This routine attempts to locate in the device tree and map the DCR
11678c2ecf20Sopenharmony_ci * register resources associated with the controller's indirect DCR
11688c2ecf20Sopenharmony_ci * address and data windows.
11698c2ecf20Sopenharmony_ci *
11708c2ecf20Sopenharmony_ci * Returns 0 if the DCRs were successfully mapped; otherwise, < 0 on
11718c2ecf20Sopenharmony_ci * error.
11728c2ecf20Sopenharmony_ci */
11738c2ecf20Sopenharmony_cistatic int ppc4xx_edac_map_dcrs(const struct device_node *np,
11748c2ecf20Sopenharmony_ci				dcr_host_t *dcr_host)
11758c2ecf20Sopenharmony_ci{
11768c2ecf20Sopenharmony_ci	unsigned int dcr_base, dcr_len;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	if (np == NULL || dcr_host == NULL)
11798c2ecf20Sopenharmony_ci		return -EINVAL;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	/* Get the DCR resource extent and sanity check the values. */
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	dcr_base = dcr_resource_start(np, 0);
11848c2ecf20Sopenharmony_ci	dcr_len = dcr_resource_len(np, 0);
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	if (dcr_base == 0 || dcr_len == 0) {
11878c2ecf20Sopenharmony_ci		ppc4xx_edac_printk(KERN_ERR,
11888c2ecf20Sopenharmony_ci				   "Failed to obtain DCR property.\n");
11898c2ecf20Sopenharmony_ci		return -ENODEV;
11908c2ecf20Sopenharmony_ci	}
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	if (dcr_len != SDRAM_DCR_RESOURCE_LEN) {
11938c2ecf20Sopenharmony_ci		ppc4xx_edac_printk(KERN_ERR,
11948c2ecf20Sopenharmony_ci				   "Unexpected DCR length %d, expected %d.\n",
11958c2ecf20Sopenharmony_ci				   dcr_len, SDRAM_DCR_RESOURCE_LEN);
11968c2ecf20Sopenharmony_ci		return -ENODEV;
11978c2ecf20Sopenharmony_ci	}
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	/*  Attempt to map the DCR extent. */
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	*dcr_host = dcr_map(np, dcr_base, dcr_len);
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	if (!DCR_MAP_OK(*dcr_host)) {
12048c2ecf20Sopenharmony_ci		ppc4xx_edac_printk(KERN_INFO, "Failed to map DCRs.\n");
12058c2ecf20Sopenharmony_ci		    return -ENODEV;
12068c2ecf20Sopenharmony_ci	}
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	return 0;
12098c2ecf20Sopenharmony_ci}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci/**
12128c2ecf20Sopenharmony_ci * ppc4xx_edac_probe - check controller and bind driver
12138c2ecf20Sopenharmony_ci * @op: A pointer to the OpenFirmware device tree node associated
12148c2ecf20Sopenharmony_ci *      with the controller being probed for driver binding.
12158c2ecf20Sopenharmony_ci *
12168c2ecf20Sopenharmony_ci * This routine probes a specific ibm,sdram-4xx-ddr2 controller
12178c2ecf20Sopenharmony_ci * instance for binding with the driver.
12188c2ecf20Sopenharmony_ci *
12198c2ecf20Sopenharmony_ci * Returns 0 if the controller instance was successfully bound to the
12208c2ecf20Sopenharmony_ci * driver; otherwise, < 0 on error.
12218c2ecf20Sopenharmony_ci */
12228c2ecf20Sopenharmony_cistatic int ppc4xx_edac_probe(struct platform_device *op)
12238c2ecf20Sopenharmony_ci{
12248c2ecf20Sopenharmony_ci	int status = 0;
12258c2ecf20Sopenharmony_ci	u32 mcopt1, memcheck;
12268c2ecf20Sopenharmony_ci	dcr_host_t dcr_host;
12278c2ecf20Sopenharmony_ci	const struct device_node *np = op->dev.of_node;
12288c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci = NULL;
12298c2ecf20Sopenharmony_ci	struct edac_mc_layer layers[2];
12308c2ecf20Sopenharmony_ci	static int ppc4xx_edac_instance;
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	/*
12338c2ecf20Sopenharmony_ci	 * At this point, we only support the controller realized on
12348c2ecf20Sopenharmony_ci	 * the AMCC PPC 405EX[r]. Reject anything else.
12358c2ecf20Sopenharmony_ci	 */
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	if (!of_device_is_compatible(np, "ibm,sdram-405ex") &&
12388c2ecf20Sopenharmony_ci	    !of_device_is_compatible(np, "ibm,sdram-405exr")) {
12398c2ecf20Sopenharmony_ci		ppc4xx_edac_printk(KERN_NOTICE,
12408c2ecf20Sopenharmony_ci				   "Only the PPC405EX[r] is supported.\n");
12418c2ecf20Sopenharmony_ci		return -ENODEV;
12428c2ecf20Sopenharmony_ci	}
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	/*
12458c2ecf20Sopenharmony_ci	 * Next, get the DCR property and attempt to map it so that we
12468c2ecf20Sopenharmony_ci	 * can probe the controller.
12478c2ecf20Sopenharmony_ci	 */
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	status = ppc4xx_edac_map_dcrs(np, &dcr_host);
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	if (status)
12528c2ecf20Sopenharmony_ci		return status;
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	/*
12558c2ecf20Sopenharmony_ci	 * First determine whether ECC is enabled at all. If not,
12568c2ecf20Sopenharmony_ci	 * there is no useful checking or monitoring that can be done
12578c2ecf20Sopenharmony_ci	 * for this controller.
12588c2ecf20Sopenharmony_ci	 */
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	mcopt1 = mfsdram(&dcr_host, SDRAM_MCOPT1);
12618c2ecf20Sopenharmony_ci	memcheck = (mcopt1 & SDRAM_MCOPT1_MCHK_MASK);
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	if (memcheck == SDRAM_MCOPT1_MCHK_NON) {
12648c2ecf20Sopenharmony_ci		ppc4xx_edac_printk(KERN_INFO, "%pOF: No ECC memory detected or "
12658c2ecf20Sopenharmony_ci				   "ECC is disabled.\n", np);
12668c2ecf20Sopenharmony_ci		status = -ENODEV;
12678c2ecf20Sopenharmony_ci		goto done;
12688c2ecf20Sopenharmony_ci	}
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	/*
12718c2ecf20Sopenharmony_ci	 * At this point, we know ECC is enabled, allocate an EDAC
12728c2ecf20Sopenharmony_ci	 * controller instance and perform the appropriate
12738c2ecf20Sopenharmony_ci	 * initialization.
12748c2ecf20Sopenharmony_ci	 */
12758c2ecf20Sopenharmony_ci	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
12768c2ecf20Sopenharmony_ci	layers[0].size = ppc4xx_edac_nr_csrows;
12778c2ecf20Sopenharmony_ci	layers[0].is_virt_csrow = true;
12788c2ecf20Sopenharmony_ci	layers[1].type = EDAC_MC_LAYER_CHANNEL;
12798c2ecf20Sopenharmony_ci	layers[1].size = ppc4xx_edac_nr_chans;
12808c2ecf20Sopenharmony_ci	layers[1].is_virt_csrow = false;
12818c2ecf20Sopenharmony_ci	mci = edac_mc_alloc(ppc4xx_edac_instance, ARRAY_SIZE(layers), layers,
12828c2ecf20Sopenharmony_ci			    sizeof(struct ppc4xx_edac_pdata));
12838c2ecf20Sopenharmony_ci	if (mci == NULL) {
12848c2ecf20Sopenharmony_ci		ppc4xx_edac_printk(KERN_ERR, "%pOF: "
12858c2ecf20Sopenharmony_ci				   "Failed to allocate EDAC MC instance!\n",
12868c2ecf20Sopenharmony_ci				   np);
12878c2ecf20Sopenharmony_ci		status = -ENOMEM;
12888c2ecf20Sopenharmony_ci		goto done;
12898c2ecf20Sopenharmony_ci	}
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	status = ppc4xx_edac_mc_init(mci, op, &dcr_host, mcopt1);
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	if (status) {
12948c2ecf20Sopenharmony_ci		ppc4xx_edac_mc_printk(KERN_ERR, mci,
12958c2ecf20Sopenharmony_ci				      "Failed to initialize instance!\n");
12968c2ecf20Sopenharmony_ci		goto fail;
12978c2ecf20Sopenharmony_ci	}
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	/*
13008c2ecf20Sopenharmony_ci	 * We have a valid, initialized EDAC instance bound to the
13018c2ecf20Sopenharmony_ci	 * controller. Attempt to register it with the EDAC subsystem
13028c2ecf20Sopenharmony_ci	 * and, if necessary, register interrupts.
13038c2ecf20Sopenharmony_ci	 */
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	if (edac_mc_add_mc(mci)) {
13068c2ecf20Sopenharmony_ci		ppc4xx_edac_mc_printk(KERN_ERR, mci,
13078c2ecf20Sopenharmony_ci				      "Failed to add instance!\n");
13088c2ecf20Sopenharmony_ci		status = -ENODEV;
13098c2ecf20Sopenharmony_ci		goto fail;
13108c2ecf20Sopenharmony_ci	}
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	if (edac_op_state == EDAC_OPSTATE_INT) {
13138c2ecf20Sopenharmony_ci		status = ppc4xx_edac_register_irq(op, mci);
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci		if (status)
13168c2ecf20Sopenharmony_ci			goto fail1;
13178c2ecf20Sopenharmony_ci	}
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	ppc4xx_edac_instance++;
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	return 0;
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci fail1:
13248c2ecf20Sopenharmony_ci	edac_mc_del_mc(mci->pdev);
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci fail:
13278c2ecf20Sopenharmony_ci	edac_mc_free(mci);
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci done:
13308c2ecf20Sopenharmony_ci	return status;
13318c2ecf20Sopenharmony_ci}
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci/**
13348c2ecf20Sopenharmony_ci * ppc4xx_edac_remove - unbind driver from controller
13358c2ecf20Sopenharmony_ci * @op: A pointer to the OpenFirmware device tree node associated
13368c2ecf20Sopenharmony_ci *      with the controller this EDAC instance is to be unbound/removed
13378c2ecf20Sopenharmony_ci *      from.
13388c2ecf20Sopenharmony_ci *
13398c2ecf20Sopenharmony_ci * This routine unbinds the EDAC memory controller instance associated
13408c2ecf20Sopenharmony_ci * with the specified ibm,sdram-4xx-ddr2 controller described by the
13418c2ecf20Sopenharmony_ci * OpenFirmware device tree node passed as a parameter.
13428c2ecf20Sopenharmony_ci *
13438c2ecf20Sopenharmony_ci * Unconditionally returns 0.
13448c2ecf20Sopenharmony_ci */
13458c2ecf20Sopenharmony_cistatic int
13468c2ecf20Sopenharmony_cippc4xx_edac_remove(struct platform_device *op)
13478c2ecf20Sopenharmony_ci{
13488c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci = dev_get_drvdata(&op->dev);
13498c2ecf20Sopenharmony_ci	struct ppc4xx_edac_pdata *pdata = mci->pvt_info;
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	if (edac_op_state == EDAC_OPSTATE_INT) {
13528c2ecf20Sopenharmony_ci		free_irq(pdata->irqs.sec, mci);
13538c2ecf20Sopenharmony_ci		free_irq(pdata->irqs.ded, mci);
13548c2ecf20Sopenharmony_ci	}
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci	dcr_unmap(pdata->dcr_host, SDRAM_DCR_RESOURCE_LEN);
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	edac_mc_del_mc(mci->pdev);
13598c2ecf20Sopenharmony_ci	edac_mc_free(mci);
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	return 0;
13628c2ecf20Sopenharmony_ci}
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci/**
13658c2ecf20Sopenharmony_ci * ppc4xx_edac_opstate_init - initialize EDAC reporting method
13668c2ecf20Sopenharmony_ci *
13678c2ecf20Sopenharmony_ci * This routine ensures that the EDAC memory controller reporting
13688c2ecf20Sopenharmony_ci * method is mapped to a sane value as the EDAC core defines the value
13698c2ecf20Sopenharmony_ci * to EDAC_OPSTATE_INVAL by default. We don't call the global
13708c2ecf20Sopenharmony_ci * opstate_init as that defaults to polling and we want interrupt as
13718c2ecf20Sopenharmony_ci * the default.
13728c2ecf20Sopenharmony_ci */
13738c2ecf20Sopenharmony_cistatic inline void __init
13748c2ecf20Sopenharmony_cippc4xx_edac_opstate_init(void)
13758c2ecf20Sopenharmony_ci{
13768c2ecf20Sopenharmony_ci	switch (edac_op_state) {
13778c2ecf20Sopenharmony_ci	case EDAC_OPSTATE_POLL:
13788c2ecf20Sopenharmony_ci	case EDAC_OPSTATE_INT:
13798c2ecf20Sopenharmony_ci		break;
13808c2ecf20Sopenharmony_ci	default:
13818c2ecf20Sopenharmony_ci		edac_op_state = EDAC_OPSTATE_INT;
13828c2ecf20Sopenharmony_ci		break;
13838c2ecf20Sopenharmony_ci	}
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	ppc4xx_edac_printk(KERN_INFO, "Reporting type: %s\n",
13868c2ecf20Sopenharmony_ci			   ((edac_op_state == EDAC_OPSTATE_POLL) ?
13878c2ecf20Sopenharmony_ci			    EDAC_OPSTATE_POLL_STR :
13888c2ecf20Sopenharmony_ci			    ((edac_op_state == EDAC_OPSTATE_INT) ?
13898c2ecf20Sopenharmony_ci			     EDAC_OPSTATE_INT_STR :
13908c2ecf20Sopenharmony_ci			     EDAC_OPSTATE_UNKNOWN_STR)));
13918c2ecf20Sopenharmony_ci}
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci/**
13948c2ecf20Sopenharmony_ci * ppc4xx_edac_init - driver/module insertion entry point
13958c2ecf20Sopenharmony_ci *
13968c2ecf20Sopenharmony_ci * This routine is the driver/module insertion entry point. It
13978c2ecf20Sopenharmony_ci * initializes the EDAC memory controller reporting state and
13988c2ecf20Sopenharmony_ci * registers the driver as an OpenFirmware device tree platform
13998c2ecf20Sopenharmony_ci * driver.
14008c2ecf20Sopenharmony_ci */
14018c2ecf20Sopenharmony_cistatic int __init
14028c2ecf20Sopenharmony_cippc4xx_edac_init(void)
14038c2ecf20Sopenharmony_ci{
14048c2ecf20Sopenharmony_ci	ppc4xx_edac_printk(KERN_INFO, PPC4XX_EDAC_MODULE_REVISION "\n");
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	ppc4xx_edac_opstate_init();
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	return platform_driver_register(&ppc4xx_edac_driver);
14098c2ecf20Sopenharmony_ci}
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci/**
14128c2ecf20Sopenharmony_ci * ppc4xx_edac_exit - driver/module removal entry point
14138c2ecf20Sopenharmony_ci *
14148c2ecf20Sopenharmony_ci * This routine is the driver/module removal entry point. It
14158c2ecf20Sopenharmony_ci * unregisters the driver as an OpenFirmware device tree platform
14168c2ecf20Sopenharmony_ci * driver.
14178c2ecf20Sopenharmony_ci */
14188c2ecf20Sopenharmony_cistatic void __exit
14198c2ecf20Sopenharmony_cippc4xx_edac_exit(void)
14208c2ecf20Sopenharmony_ci{
14218c2ecf20Sopenharmony_ci	platform_driver_unregister(&ppc4xx_edac_driver);
14228c2ecf20Sopenharmony_ci}
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_cimodule_init(ppc4xx_edac_init);
14258c2ecf20Sopenharmony_cimodule_exit(ppc4xx_edac_exit);
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
14288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Grant Erickson <gerickson@nuovations.com>");
14298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("EDAC MC Driver for the PPC4xx IBM DDR2 Memory Controller");
14308c2ecf20Sopenharmony_cimodule_param(edac_op_state, int, 0444);
14318c2ecf20Sopenharmony_ciMODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting State: "
14328c2ecf20Sopenharmony_ci		 "0=" EDAC_OPSTATE_POLL_STR ", 2=" EDAC_OPSTATE_INT_STR);
1433