18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Synopsys DDR ECC Driver 38c2ecf20Sopenharmony_ci * This driver is based on ppc4xx_edac.c drivers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 - 2014 Xilinx, Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This program is free software: you can redistribute it and/or modify 88c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by 98c2ecf20Sopenharmony_ci * the Free Software Foundation, either version 2 of the License, or 108c2ecf20Sopenharmony_ci * (at your option) any later version. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 138c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 148c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 158c2ecf20Sopenharmony_ci * GNU General Public License for more details. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 188c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 198c2ecf20Sopenharmony_ci * for more details 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/edac.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 258c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 268c2ecf20Sopenharmony_ci#include <linux/of.h> 278c2ecf20Sopenharmony_ci#include <linux/of_device.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "edac_module.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* Number of cs_rows needed per memory controller */ 328c2ecf20Sopenharmony_ci#define SYNPS_EDAC_NR_CSROWS 1 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* Number of channels per memory controller */ 358c2ecf20Sopenharmony_ci#define SYNPS_EDAC_NR_CHANS 1 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Granularity of reported error in bytes */ 388c2ecf20Sopenharmony_ci#define SYNPS_EDAC_ERR_GRAIN 1 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define SYNPS_EDAC_MSG_SIZE 256 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define SYNPS_EDAC_MOD_STRING "synps_edac" 438c2ecf20Sopenharmony_ci#define SYNPS_EDAC_MOD_VER "1" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Synopsys DDR memory controller registers that are relevant to ECC */ 468c2ecf20Sopenharmony_ci#define CTRL_OFST 0x0 478c2ecf20Sopenharmony_ci#define T_ZQ_OFST 0xA4 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* ECC control register */ 508c2ecf20Sopenharmony_ci#define ECC_CTRL_OFST 0xC4 518c2ecf20Sopenharmony_ci/* ECC log register */ 528c2ecf20Sopenharmony_ci#define CE_LOG_OFST 0xC8 538c2ecf20Sopenharmony_ci/* ECC address register */ 548c2ecf20Sopenharmony_ci#define CE_ADDR_OFST 0xCC 558c2ecf20Sopenharmony_ci/* ECC data[31:0] register */ 568c2ecf20Sopenharmony_ci#define CE_DATA_31_0_OFST 0xD0 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* Uncorrectable error info registers */ 598c2ecf20Sopenharmony_ci#define UE_LOG_OFST 0xDC 608c2ecf20Sopenharmony_ci#define UE_ADDR_OFST 0xE0 618c2ecf20Sopenharmony_ci#define UE_DATA_31_0_OFST 0xE4 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define STAT_OFST 0xF0 648c2ecf20Sopenharmony_ci#define SCRUB_OFST 0xF4 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* Control register bit field definitions */ 678c2ecf20Sopenharmony_ci#define CTRL_BW_MASK 0xC 688c2ecf20Sopenharmony_ci#define CTRL_BW_SHIFT 2 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define DDRCTL_WDTH_16 1 718c2ecf20Sopenharmony_ci#define DDRCTL_WDTH_32 0 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* ZQ register bit field definitions */ 748c2ecf20Sopenharmony_ci#define T_ZQ_DDRMODE_MASK 0x2 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* ECC control register bit field definitions */ 778c2ecf20Sopenharmony_ci#define ECC_CTRL_CLR_CE_ERR 0x2 788c2ecf20Sopenharmony_ci#define ECC_CTRL_CLR_UE_ERR 0x1 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* ECC correctable/uncorrectable error log register definitions */ 818c2ecf20Sopenharmony_ci#define LOG_VALID 0x1 828c2ecf20Sopenharmony_ci#define CE_LOG_BITPOS_MASK 0xFE 838c2ecf20Sopenharmony_ci#define CE_LOG_BITPOS_SHIFT 1 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* ECC correctable/uncorrectable error address register definitions */ 868c2ecf20Sopenharmony_ci#define ADDR_COL_MASK 0xFFF 878c2ecf20Sopenharmony_ci#define ADDR_ROW_MASK 0xFFFF000 888c2ecf20Sopenharmony_ci#define ADDR_ROW_SHIFT 12 898c2ecf20Sopenharmony_ci#define ADDR_BANK_MASK 0x70000000 908c2ecf20Sopenharmony_ci#define ADDR_BANK_SHIFT 28 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* ECC statistic register definitions */ 938c2ecf20Sopenharmony_ci#define STAT_UECNT_MASK 0xFF 948c2ecf20Sopenharmony_ci#define STAT_CECNT_MASK 0xFF00 958c2ecf20Sopenharmony_ci#define STAT_CECNT_SHIFT 8 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* ECC scrub register definitions */ 988c2ecf20Sopenharmony_ci#define SCRUB_MODE_MASK 0x7 998c2ecf20Sopenharmony_ci#define SCRUB_MODE_SECDED 0x4 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* DDR ECC Quirks */ 1028c2ecf20Sopenharmony_ci#define DDR_ECC_INTR_SUPPORT BIT(0) 1038c2ecf20Sopenharmony_ci#define DDR_ECC_DATA_POISON_SUPPORT BIT(1) 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* ZynqMP Enhanced DDR memory controller registers that are relevant to ECC */ 1068c2ecf20Sopenharmony_ci/* ECC Configuration Registers */ 1078c2ecf20Sopenharmony_ci#define ECC_CFG0_OFST 0x70 1088c2ecf20Sopenharmony_ci#define ECC_CFG1_OFST 0x74 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* ECC Status Register */ 1118c2ecf20Sopenharmony_ci#define ECC_STAT_OFST 0x78 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* ECC Clear Register */ 1148c2ecf20Sopenharmony_ci#define ECC_CLR_OFST 0x7C 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* ECC Error count Register */ 1178c2ecf20Sopenharmony_ci#define ECC_ERRCNT_OFST 0x80 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* ECC Corrected Error Address Register */ 1208c2ecf20Sopenharmony_ci#define ECC_CEADDR0_OFST 0x84 1218c2ecf20Sopenharmony_ci#define ECC_CEADDR1_OFST 0x88 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* ECC Syndrome Registers */ 1248c2ecf20Sopenharmony_ci#define ECC_CSYND0_OFST 0x8C 1258c2ecf20Sopenharmony_ci#define ECC_CSYND1_OFST 0x90 1268c2ecf20Sopenharmony_ci#define ECC_CSYND2_OFST 0x94 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* ECC Bit Mask0 Address Register */ 1298c2ecf20Sopenharmony_ci#define ECC_BITMASK0_OFST 0x98 1308c2ecf20Sopenharmony_ci#define ECC_BITMASK1_OFST 0x9C 1318c2ecf20Sopenharmony_ci#define ECC_BITMASK2_OFST 0xA0 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* ECC UnCorrected Error Address Register */ 1348c2ecf20Sopenharmony_ci#define ECC_UEADDR0_OFST 0xA4 1358c2ecf20Sopenharmony_ci#define ECC_UEADDR1_OFST 0xA8 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* ECC Syndrome Registers */ 1388c2ecf20Sopenharmony_ci#define ECC_UESYND0_OFST 0xAC 1398c2ecf20Sopenharmony_ci#define ECC_UESYND1_OFST 0xB0 1408c2ecf20Sopenharmony_ci#define ECC_UESYND2_OFST 0xB4 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/* ECC Poison Address Reg */ 1438c2ecf20Sopenharmony_ci#define ECC_POISON0_OFST 0xB8 1448c2ecf20Sopenharmony_ci#define ECC_POISON1_OFST 0xBC 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci#define ECC_ADDRMAP0_OFFSET 0x200 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* Control register bitfield definitions */ 1498c2ecf20Sopenharmony_ci#define ECC_CTRL_BUSWIDTH_MASK 0x3000 1508c2ecf20Sopenharmony_ci#define ECC_CTRL_BUSWIDTH_SHIFT 12 1518c2ecf20Sopenharmony_ci#define ECC_CTRL_CLR_CE_ERRCNT BIT(2) 1528c2ecf20Sopenharmony_ci#define ECC_CTRL_CLR_UE_ERRCNT BIT(3) 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* DDR Control Register width definitions */ 1558c2ecf20Sopenharmony_ci#define DDRCTL_EWDTH_16 2 1568c2ecf20Sopenharmony_ci#define DDRCTL_EWDTH_32 1 1578c2ecf20Sopenharmony_ci#define DDRCTL_EWDTH_64 0 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/* ECC status register definitions */ 1608c2ecf20Sopenharmony_ci#define ECC_STAT_UECNT_MASK 0xF0000 1618c2ecf20Sopenharmony_ci#define ECC_STAT_UECNT_SHIFT 16 1628c2ecf20Sopenharmony_ci#define ECC_STAT_CECNT_MASK 0xF00 1638c2ecf20Sopenharmony_ci#define ECC_STAT_CECNT_SHIFT 8 1648c2ecf20Sopenharmony_ci#define ECC_STAT_BITNUM_MASK 0x7F 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci/* ECC error count register definitions */ 1678c2ecf20Sopenharmony_ci#define ECC_ERRCNT_UECNT_MASK 0xFFFF0000 1688c2ecf20Sopenharmony_ci#define ECC_ERRCNT_UECNT_SHIFT 16 1698c2ecf20Sopenharmony_ci#define ECC_ERRCNT_CECNT_MASK 0xFFFF 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* DDR QOS Interrupt register definitions */ 1728c2ecf20Sopenharmony_ci#define DDR_QOS_IRQ_STAT_OFST 0x20200 1738c2ecf20Sopenharmony_ci#define DDR_QOSUE_MASK 0x4 1748c2ecf20Sopenharmony_ci#define DDR_QOSCE_MASK 0x2 1758c2ecf20Sopenharmony_ci#define ECC_CE_UE_INTR_MASK 0x6 1768c2ecf20Sopenharmony_ci#define DDR_QOS_IRQ_EN_OFST 0x20208 1778c2ecf20Sopenharmony_ci#define DDR_QOS_IRQ_DB_OFST 0x2020C 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* ECC Corrected Error Register Mask and Shifts*/ 1808c2ecf20Sopenharmony_ci#define ECC_CEADDR0_RW_MASK 0x3FFFF 1818c2ecf20Sopenharmony_ci#define ECC_CEADDR0_RNK_MASK BIT(24) 1828c2ecf20Sopenharmony_ci#define ECC_CEADDR1_BNKGRP_MASK 0x3000000 1838c2ecf20Sopenharmony_ci#define ECC_CEADDR1_BNKNR_MASK 0x70000 1848c2ecf20Sopenharmony_ci#define ECC_CEADDR1_BLKNR_MASK 0xFFF 1858c2ecf20Sopenharmony_ci#define ECC_CEADDR1_BNKGRP_SHIFT 24 1868c2ecf20Sopenharmony_ci#define ECC_CEADDR1_BNKNR_SHIFT 16 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* ECC Poison register shifts */ 1898c2ecf20Sopenharmony_ci#define ECC_POISON0_RANK_SHIFT 24 1908c2ecf20Sopenharmony_ci#define ECC_POISON0_RANK_MASK BIT(24) 1918c2ecf20Sopenharmony_ci#define ECC_POISON0_COLUMN_SHIFT 0 1928c2ecf20Sopenharmony_ci#define ECC_POISON0_COLUMN_MASK 0xFFF 1938c2ecf20Sopenharmony_ci#define ECC_POISON1_BG_SHIFT 28 1948c2ecf20Sopenharmony_ci#define ECC_POISON1_BG_MASK 0x30000000 1958c2ecf20Sopenharmony_ci#define ECC_POISON1_BANKNR_SHIFT 24 1968c2ecf20Sopenharmony_ci#define ECC_POISON1_BANKNR_MASK 0x7000000 1978c2ecf20Sopenharmony_ci#define ECC_POISON1_ROW_SHIFT 0 1988c2ecf20Sopenharmony_ci#define ECC_POISON1_ROW_MASK 0x3FFFF 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/* DDR Memory type defines */ 2018c2ecf20Sopenharmony_ci#define MEM_TYPE_DDR3 0x1 2028c2ecf20Sopenharmony_ci#define MEM_TYPE_LPDDR3 0x8 2038c2ecf20Sopenharmony_ci#define MEM_TYPE_DDR2 0x4 2048c2ecf20Sopenharmony_ci#define MEM_TYPE_DDR4 0x10 2058c2ecf20Sopenharmony_ci#define MEM_TYPE_LPDDR4 0x20 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/* DDRC Software control register */ 2088c2ecf20Sopenharmony_ci#define DDRC_SWCTL 0x320 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci/* DDRC ECC CE & UE poison mask */ 2118c2ecf20Sopenharmony_ci#define ECC_CEPOISON_MASK 0x3 2128c2ecf20Sopenharmony_ci#define ECC_UEPOISON_MASK 0x1 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/* DDRC Device config masks */ 2158c2ecf20Sopenharmony_ci#define DDRC_MSTR_CFG_MASK 0xC0000000 2168c2ecf20Sopenharmony_ci#define DDRC_MSTR_CFG_SHIFT 30 2178c2ecf20Sopenharmony_ci#define DDRC_MSTR_CFG_X4_MASK 0x0 2188c2ecf20Sopenharmony_ci#define DDRC_MSTR_CFG_X8_MASK 0x1 2198c2ecf20Sopenharmony_ci#define DDRC_MSTR_CFG_X16_MASK 0x2 2208c2ecf20Sopenharmony_ci#define DDRC_MSTR_CFG_X32_MASK 0x3 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci#define DDR_MAX_ROW_SHIFT 18 2238c2ecf20Sopenharmony_ci#define DDR_MAX_COL_SHIFT 14 2248c2ecf20Sopenharmony_ci#define DDR_MAX_BANK_SHIFT 3 2258c2ecf20Sopenharmony_ci#define DDR_MAX_BANKGRP_SHIFT 2 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci#define ROW_MAX_VAL_MASK 0xF 2288c2ecf20Sopenharmony_ci#define COL_MAX_VAL_MASK 0xF 2298c2ecf20Sopenharmony_ci#define BANK_MAX_VAL_MASK 0x1F 2308c2ecf20Sopenharmony_ci#define BANKGRP_MAX_VAL_MASK 0x1F 2318c2ecf20Sopenharmony_ci#define RANK_MAX_VAL_MASK 0x1F 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci#define ROW_B0_BASE 6 2348c2ecf20Sopenharmony_ci#define ROW_B1_BASE 7 2358c2ecf20Sopenharmony_ci#define ROW_B2_BASE 8 2368c2ecf20Sopenharmony_ci#define ROW_B3_BASE 9 2378c2ecf20Sopenharmony_ci#define ROW_B4_BASE 10 2388c2ecf20Sopenharmony_ci#define ROW_B5_BASE 11 2398c2ecf20Sopenharmony_ci#define ROW_B6_BASE 12 2408c2ecf20Sopenharmony_ci#define ROW_B7_BASE 13 2418c2ecf20Sopenharmony_ci#define ROW_B8_BASE 14 2428c2ecf20Sopenharmony_ci#define ROW_B9_BASE 15 2438c2ecf20Sopenharmony_ci#define ROW_B10_BASE 16 2448c2ecf20Sopenharmony_ci#define ROW_B11_BASE 17 2458c2ecf20Sopenharmony_ci#define ROW_B12_BASE 18 2468c2ecf20Sopenharmony_ci#define ROW_B13_BASE 19 2478c2ecf20Sopenharmony_ci#define ROW_B14_BASE 20 2488c2ecf20Sopenharmony_ci#define ROW_B15_BASE 21 2498c2ecf20Sopenharmony_ci#define ROW_B16_BASE 22 2508c2ecf20Sopenharmony_ci#define ROW_B17_BASE 23 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci#define COL_B2_BASE 2 2538c2ecf20Sopenharmony_ci#define COL_B3_BASE 3 2548c2ecf20Sopenharmony_ci#define COL_B4_BASE 4 2558c2ecf20Sopenharmony_ci#define COL_B5_BASE 5 2568c2ecf20Sopenharmony_ci#define COL_B6_BASE 6 2578c2ecf20Sopenharmony_ci#define COL_B7_BASE 7 2588c2ecf20Sopenharmony_ci#define COL_B8_BASE 8 2598c2ecf20Sopenharmony_ci#define COL_B9_BASE 9 2608c2ecf20Sopenharmony_ci#define COL_B10_BASE 10 2618c2ecf20Sopenharmony_ci#define COL_B11_BASE 11 2628c2ecf20Sopenharmony_ci#define COL_B12_BASE 12 2638c2ecf20Sopenharmony_ci#define COL_B13_BASE 13 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci#define BANK_B0_BASE 2 2668c2ecf20Sopenharmony_ci#define BANK_B1_BASE 3 2678c2ecf20Sopenharmony_ci#define BANK_B2_BASE 4 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci#define BANKGRP_B0_BASE 2 2708c2ecf20Sopenharmony_ci#define BANKGRP_B1_BASE 3 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci#define RANK_B0_BASE 6 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci/** 2758c2ecf20Sopenharmony_ci * struct ecc_error_info - ECC error log information. 2768c2ecf20Sopenharmony_ci * @row: Row number. 2778c2ecf20Sopenharmony_ci * @col: Column number. 2788c2ecf20Sopenharmony_ci * @bank: Bank number. 2798c2ecf20Sopenharmony_ci * @bitpos: Bit position. 2808c2ecf20Sopenharmony_ci * @data: Data causing the error. 2818c2ecf20Sopenharmony_ci * @bankgrpnr: Bank group number. 2828c2ecf20Sopenharmony_ci * @blknr: Block number. 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_cistruct ecc_error_info { 2858c2ecf20Sopenharmony_ci u32 row; 2868c2ecf20Sopenharmony_ci u32 col; 2878c2ecf20Sopenharmony_ci u32 bank; 2888c2ecf20Sopenharmony_ci u32 bitpos; 2898c2ecf20Sopenharmony_ci u32 data; 2908c2ecf20Sopenharmony_ci u32 bankgrpnr; 2918c2ecf20Sopenharmony_ci u32 blknr; 2928c2ecf20Sopenharmony_ci}; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/** 2958c2ecf20Sopenharmony_ci * struct synps_ecc_status - ECC status information to report. 2968c2ecf20Sopenharmony_ci * @ce_cnt: Correctable error count. 2978c2ecf20Sopenharmony_ci * @ue_cnt: Uncorrectable error count. 2988c2ecf20Sopenharmony_ci * @ceinfo: Correctable error log information. 2998c2ecf20Sopenharmony_ci * @ueinfo: Uncorrectable error log information. 3008c2ecf20Sopenharmony_ci */ 3018c2ecf20Sopenharmony_cistruct synps_ecc_status { 3028c2ecf20Sopenharmony_ci u32 ce_cnt; 3038c2ecf20Sopenharmony_ci u32 ue_cnt; 3048c2ecf20Sopenharmony_ci struct ecc_error_info ceinfo; 3058c2ecf20Sopenharmony_ci struct ecc_error_info ueinfo; 3068c2ecf20Sopenharmony_ci}; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci/** 3098c2ecf20Sopenharmony_ci * struct synps_edac_priv - DDR memory controller private instance data. 3108c2ecf20Sopenharmony_ci * @baseaddr: Base address of the DDR controller. 3118c2ecf20Sopenharmony_ci * @message: Buffer for framing the event specific info. 3128c2ecf20Sopenharmony_ci * @stat: ECC status information. 3138c2ecf20Sopenharmony_ci * @p_data: Platform data. 3148c2ecf20Sopenharmony_ci * @ce_cnt: Correctable Error count. 3158c2ecf20Sopenharmony_ci * @ue_cnt: Uncorrectable Error count. 3168c2ecf20Sopenharmony_ci * @poison_addr: Data poison address. 3178c2ecf20Sopenharmony_ci * @row_shift: Bit shifts for row bit. 3188c2ecf20Sopenharmony_ci * @col_shift: Bit shifts for column bit. 3198c2ecf20Sopenharmony_ci * @bank_shift: Bit shifts for bank bit. 3208c2ecf20Sopenharmony_ci * @bankgrp_shift: Bit shifts for bank group bit. 3218c2ecf20Sopenharmony_ci * @rank_shift: Bit shifts for rank bit. 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_cistruct synps_edac_priv { 3248c2ecf20Sopenharmony_ci void __iomem *baseaddr; 3258c2ecf20Sopenharmony_ci char message[SYNPS_EDAC_MSG_SIZE]; 3268c2ecf20Sopenharmony_ci struct synps_ecc_status stat; 3278c2ecf20Sopenharmony_ci const struct synps_platform_data *p_data; 3288c2ecf20Sopenharmony_ci u32 ce_cnt; 3298c2ecf20Sopenharmony_ci u32 ue_cnt; 3308c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 3318c2ecf20Sopenharmony_ci ulong poison_addr; 3328c2ecf20Sopenharmony_ci u32 row_shift[18]; 3338c2ecf20Sopenharmony_ci u32 col_shift[14]; 3348c2ecf20Sopenharmony_ci u32 bank_shift[3]; 3358c2ecf20Sopenharmony_ci u32 bankgrp_shift[2]; 3368c2ecf20Sopenharmony_ci u32 rank_shift[1]; 3378c2ecf20Sopenharmony_ci#endif 3388c2ecf20Sopenharmony_ci}; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/** 3418c2ecf20Sopenharmony_ci * struct synps_platform_data - synps platform data structure. 3428c2ecf20Sopenharmony_ci * @get_error_info: Get EDAC error info. 3438c2ecf20Sopenharmony_ci * @get_mtype: Get mtype. 3448c2ecf20Sopenharmony_ci * @get_dtype: Get dtype. 3458c2ecf20Sopenharmony_ci * @get_ecc_state: Get ECC state. 3468c2ecf20Sopenharmony_ci * @quirks: To differentiate IPs. 3478c2ecf20Sopenharmony_ci */ 3488c2ecf20Sopenharmony_cistruct synps_platform_data { 3498c2ecf20Sopenharmony_ci int (*get_error_info)(struct synps_edac_priv *priv); 3508c2ecf20Sopenharmony_ci enum mem_type (*get_mtype)(const void __iomem *base); 3518c2ecf20Sopenharmony_ci enum dev_type (*get_dtype)(const void __iomem *base); 3528c2ecf20Sopenharmony_ci bool (*get_ecc_state)(void __iomem *base); 3538c2ecf20Sopenharmony_ci int quirks; 3548c2ecf20Sopenharmony_ci}; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci/** 3578c2ecf20Sopenharmony_ci * zynq_get_error_info - Get the current ECC error info. 3588c2ecf20Sopenharmony_ci * @priv: DDR memory controller private instance data. 3598c2ecf20Sopenharmony_ci * 3608c2ecf20Sopenharmony_ci * Return: one if there is no error, otherwise zero. 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_cistatic int zynq_get_error_info(struct synps_edac_priv *priv) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct synps_ecc_status *p; 3658c2ecf20Sopenharmony_ci u32 regval, clearval = 0; 3668c2ecf20Sopenharmony_ci void __iomem *base; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci base = priv->baseaddr; 3698c2ecf20Sopenharmony_ci p = &priv->stat; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci regval = readl(base + STAT_OFST); 3728c2ecf20Sopenharmony_ci if (!regval) 3738c2ecf20Sopenharmony_ci return 1; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci p->ce_cnt = (regval & STAT_CECNT_MASK) >> STAT_CECNT_SHIFT; 3768c2ecf20Sopenharmony_ci p->ue_cnt = regval & STAT_UECNT_MASK; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci regval = readl(base + CE_LOG_OFST); 3798c2ecf20Sopenharmony_ci if (!(p->ce_cnt && (regval & LOG_VALID))) 3808c2ecf20Sopenharmony_ci goto ue_err; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci p->ceinfo.bitpos = (regval & CE_LOG_BITPOS_MASK) >> CE_LOG_BITPOS_SHIFT; 3838c2ecf20Sopenharmony_ci regval = readl(base + CE_ADDR_OFST); 3848c2ecf20Sopenharmony_ci p->ceinfo.row = (regval & ADDR_ROW_MASK) >> ADDR_ROW_SHIFT; 3858c2ecf20Sopenharmony_ci p->ceinfo.col = regval & ADDR_COL_MASK; 3868c2ecf20Sopenharmony_ci p->ceinfo.bank = (regval & ADDR_BANK_MASK) >> ADDR_BANK_SHIFT; 3878c2ecf20Sopenharmony_ci p->ceinfo.data = readl(base + CE_DATA_31_0_OFST); 3888c2ecf20Sopenharmony_ci edac_dbg(3, "CE bit position: %d data: %d\n", p->ceinfo.bitpos, 3898c2ecf20Sopenharmony_ci p->ceinfo.data); 3908c2ecf20Sopenharmony_ci clearval = ECC_CTRL_CLR_CE_ERR; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ciue_err: 3938c2ecf20Sopenharmony_ci regval = readl(base + UE_LOG_OFST); 3948c2ecf20Sopenharmony_ci if (!(p->ue_cnt && (regval & LOG_VALID))) 3958c2ecf20Sopenharmony_ci goto out; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci regval = readl(base + UE_ADDR_OFST); 3988c2ecf20Sopenharmony_ci p->ueinfo.row = (regval & ADDR_ROW_MASK) >> ADDR_ROW_SHIFT; 3998c2ecf20Sopenharmony_ci p->ueinfo.col = regval & ADDR_COL_MASK; 4008c2ecf20Sopenharmony_ci p->ueinfo.bank = (regval & ADDR_BANK_MASK) >> ADDR_BANK_SHIFT; 4018c2ecf20Sopenharmony_ci p->ueinfo.data = readl(base + UE_DATA_31_0_OFST); 4028c2ecf20Sopenharmony_ci clearval |= ECC_CTRL_CLR_UE_ERR; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ciout: 4058c2ecf20Sopenharmony_ci writel(clearval, base + ECC_CTRL_OFST); 4068c2ecf20Sopenharmony_ci writel(0x0, base + ECC_CTRL_OFST); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci/** 4128c2ecf20Sopenharmony_ci * zynqmp_get_error_info - Get the current ECC error info. 4138c2ecf20Sopenharmony_ci * @priv: DDR memory controller private instance data. 4148c2ecf20Sopenharmony_ci * 4158c2ecf20Sopenharmony_ci * Return: one if there is no error otherwise returns zero. 4168c2ecf20Sopenharmony_ci */ 4178c2ecf20Sopenharmony_cistatic int zynqmp_get_error_info(struct synps_edac_priv *priv) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct synps_ecc_status *p; 4208c2ecf20Sopenharmony_ci u32 regval, clearval = 0; 4218c2ecf20Sopenharmony_ci void __iomem *base; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci base = priv->baseaddr; 4248c2ecf20Sopenharmony_ci p = &priv->stat; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci regval = readl(base + ECC_ERRCNT_OFST); 4278c2ecf20Sopenharmony_ci p->ce_cnt = regval & ECC_ERRCNT_CECNT_MASK; 4288c2ecf20Sopenharmony_ci p->ue_cnt = (regval & ECC_ERRCNT_UECNT_MASK) >> ECC_ERRCNT_UECNT_SHIFT; 4298c2ecf20Sopenharmony_ci if (!p->ce_cnt) 4308c2ecf20Sopenharmony_ci goto ue_err; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci regval = readl(base + ECC_STAT_OFST); 4338c2ecf20Sopenharmony_ci if (!regval) 4348c2ecf20Sopenharmony_ci return 1; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci p->ceinfo.bitpos = (regval & ECC_STAT_BITNUM_MASK); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci regval = readl(base + ECC_CEADDR0_OFST); 4398c2ecf20Sopenharmony_ci p->ceinfo.row = (regval & ECC_CEADDR0_RW_MASK); 4408c2ecf20Sopenharmony_ci regval = readl(base + ECC_CEADDR1_OFST); 4418c2ecf20Sopenharmony_ci p->ceinfo.bank = (regval & ECC_CEADDR1_BNKNR_MASK) >> 4428c2ecf20Sopenharmony_ci ECC_CEADDR1_BNKNR_SHIFT; 4438c2ecf20Sopenharmony_ci p->ceinfo.bankgrpnr = (regval & ECC_CEADDR1_BNKGRP_MASK) >> 4448c2ecf20Sopenharmony_ci ECC_CEADDR1_BNKGRP_SHIFT; 4458c2ecf20Sopenharmony_ci p->ceinfo.blknr = (regval & ECC_CEADDR1_BLKNR_MASK); 4468c2ecf20Sopenharmony_ci p->ceinfo.data = readl(base + ECC_CSYND0_OFST); 4478c2ecf20Sopenharmony_ci edac_dbg(2, "ECCCSYN0: 0x%08X ECCCSYN1: 0x%08X ECCCSYN2: 0x%08X\n", 4488c2ecf20Sopenharmony_ci readl(base + ECC_CSYND0_OFST), readl(base + ECC_CSYND1_OFST), 4498c2ecf20Sopenharmony_ci readl(base + ECC_CSYND2_OFST)); 4508c2ecf20Sopenharmony_ciue_err: 4518c2ecf20Sopenharmony_ci if (!p->ue_cnt) 4528c2ecf20Sopenharmony_ci goto out; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci regval = readl(base + ECC_UEADDR0_OFST); 4558c2ecf20Sopenharmony_ci p->ueinfo.row = (regval & ECC_CEADDR0_RW_MASK); 4568c2ecf20Sopenharmony_ci regval = readl(base + ECC_UEADDR1_OFST); 4578c2ecf20Sopenharmony_ci p->ueinfo.bankgrpnr = (regval & ECC_CEADDR1_BNKGRP_MASK) >> 4588c2ecf20Sopenharmony_ci ECC_CEADDR1_BNKGRP_SHIFT; 4598c2ecf20Sopenharmony_ci p->ueinfo.bank = (regval & ECC_CEADDR1_BNKNR_MASK) >> 4608c2ecf20Sopenharmony_ci ECC_CEADDR1_BNKNR_SHIFT; 4618c2ecf20Sopenharmony_ci p->ueinfo.blknr = (regval & ECC_CEADDR1_BLKNR_MASK); 4628c2ecf20Sopenharmony_ci p->ueinfo.data = readl(base + ECC_UESYND0_OFST); 4638c2ecf20Sopenharmony_ciout: 4648c2ecf20Sopenharmony_ci clearval = ECC_CTRL_CLR_CE_ERR | ECC_CTRL_CLR_CE_ERRCNT; 4658c2ecf20Sopenharmony_ci clearval |= ECC_CTRL_CLR_UE_ERR | ECC_CTRL_CLR_UE_ERRCNT; 4668c2ecf20Sopenharmony_ci writel(clearval, base + ECC_CLR_OFST); 4678c2ecf20Sopenharmony_ci writel(0x0, base + ECC_CLR_OFST); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci/** 4738c2ecf20Sopenharmony_ci * handle_error - Handle Correctable and Uncorrectable errors. 4748c2ecf20Sopenharmony_ci * @mci: EDAC memory controller instance. 4758c2ecf20Sopenharmony_ci * @p: Synopsys ECC status structure. 4768c2ecf20Sopenharmony_ci * 4778c2ecf20Sopenharmony_ci * Handles ECC correctable and uncorrectable errors. 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_cistatic void handle_error(struct mem_ctl_info *mci, struct synps_ecc_status *p) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct synps_edac_priv *priv = mci->pvt_info; 4828c2ecf20Sopenharmony_ci struct ecc_error_info *pinf; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (p->ce_cnt) { 4858c2ecf20Sopenharmony_ci pinf = &p->ceinfo; 4868c2ecf20Sopenharmony_ci if (priv->p_data->quirks & DDR_ECC_INTR_SUPPORT) { 4878c2ecf20Sopenharmony_ci snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, 4888c2ecf20Sopenharmony_ci "DDR ECC error type:%s Row %d Bank %d BankGroup Number %d Block Number %d Bit Position: %d Data: 0x%08x", 4898c2ecf20Sopenharmony_ci "CE", pinf->row, pinf->bank, 4908c2ecf20Sopenharmony_ci pinf->bankgrpnr, pinf->blknr, 4918c2ecf20Sopenharmony_ci pinf->bitpos, pinf->data); 4928c2ecf20Sopenharmony_ci } else { 4938c2ecf20Sopenharmony_ci snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, 4948c2ecf20Sopenharmony_ci "DDR ECC error type:%s Row %d Bank %d Col %d Bit Position: %d Data: 0x%08x", 4958c2ecf20Sopenharmony_ci "CE", pinf->row, pinf->bank, pinf->col, 4968c2ecf20Sopenharmony_ci pinf->bitpos, pinf->data); 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 5008c2ecf20Sopenharmony_ci p->ce_cnt, 0, 0, 0, 0, 0, -1, 5018c2ecf20Sopenharmony_ci priv->message, ""); 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (p->ue_cnt) { 5058c2ecf20Sopenharmony_ci pinf = &p->ueinfo; 5068c2ecf20Sopenharmony_ci if (priv->p_data->quirks & DDR_ECC_INTR_SUPPORT) { 5078c2ecf20Sopenharmony_ci snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, 5088c2ecf20Sopenharmony_ci "DDR ECC error type :%s Row %d Bank %d BankGroup Number %d Block Number %d", 5098c2ecf20Sopenharmony_ci "UE", pinf->row, pinf->bank, 5108c2ecf20Sopenharmony_ci pinf->bankgrpnr, pinf->blknr); 5118c2ecf20Sopenharmony_ci } else { 5128c2ecf20Sopenharmony_ci snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, 5138c2ecf20Sopenharmony_ci "DDR ECC error type :%s Row %d Bank %d Col %d ", 5148c2ecf20Sopenharmony_ci "UE", pinf->row, pinf->bank, pinf->col); 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 5188c2ecf20Sopenharmony_ci p->ue_cnt, 0, 0, 0, 0, 0, -1, 5198c2ecf20Sopenharmony_ci priv->message, ""); 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci memset(p, 0, sizeof(*p)); 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci/** 5268c2ecf20Sopenharmony_ci * intr_handler - Interrupt Handler for ECC interrupts. 5278c2ecf20Sopenharmony_ci * @irq: IRQ number. 5288c2ecf20Sopenharmony_ci * @dev_id: Device ID. 5298c2ecf20Sopenharmony_ci * 5308c2ecf20Sopenharmony_ci * Return: IRQ_NONE, if interrupt not set or IRQ_HANDLED otherwise. 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_cistatic irqreturn_t intr_handler(int irq, void *dev_id) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci const struct synps_platform_data *p_data; 5358c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = dev_id; 5368c2ecf20Sopenharmony_ci struct synps_edac_priv *priv; 5378c2ecf20Sopenharmony_ci int status, regval; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci priv = mci->pvt_info; 5408c2ecf20Sopenharmony_ci p_data = priv->p_data; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci regval = readl(priv->baseaddr + DDR_QOS_IRQ_STAT_OFST); 5438c2ecf20Sopenharmony_ci regval &= (DDR_QOSCE_MASK | DDR_QOSUE_MASK); 5448c2ecf20Sopenharmony_ci if (!(regval & ECC_CE_UE_INTR_MASK)) 5458c2ecf20Sopenharmony_ci return IRQ_NONE; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci status = p_data->get_error_info(priv); 5488c2ecf20Sopenharmony_ci if (status) 5498c2ecf20Sopenharmony_ci return IRQ_NONE; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci priv->ce_cnt += priv->stat.ce_cnt; 5528c2ecf20Sopenharmony_ci priv->ue_cnt += priv->stat.ue_cnt; 5538c2ecf20Sopenharmony_ci handle_error(mci, &priv->stat); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci edac_dbg(3, "Total error count CE %d UE %d\n", 5568c2ecf20Sopenharmony_ci priv->ce_cnt, priv->ue_cnt); 5578c2ecf20Sopenharmony_ci writel(regval, priv->baseaddr + DDR_QOS_IRQ_STAT_OFST); 5588c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci/** 5628c2ecf20Sopenharmony_ci * check_errors - Check controller for ECC errors. 5638c2ecf20Sopenharmony_ci * @mci: EDAC memory controller instance. 5648c2ecf20Sopenharmony_ci * 5658c2ecf20Sopenharmony_ci * Check and post ECC errors. Called by the polling thread. 5668c2ecf20Sopenharmony_ci */ 5678c2ecf20Sopenharmony_cistatic void check_errors(struct mem_ctl_info *mci) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci const struct synps_platform_data *p_data; 5708c2ecf20Sopenharmony_ci struct synps_edac_priv *priv; 5718c2ecf20Sopenharmony_ci int status; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci priv = mci->pvt_info; 5748c2ecf20Sopenharmony_ci p_data = priv->p_data; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci status = p_data->get_error_info(priv); 5778c2ecf20Sopenharmony_ci if (status) 5788c2ecf20Sopenharmony_ci return; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci priv->ce_cnt += priv->stat.ce_cnt; 5818c2ecf20Sopenharmony_ci priv->ue_cnt += priv->stat.ue_cnt; 5828c2ecf20Sopenharmony_ci handle_error(mci, &priv->stat); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci edac_dbg(3, "Total error count CE %d UE %d\n", 5858c2ecf20Sopenharmony_ci priv->ce_cnt, priv->ue_cnt); 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci/** 5898c2ecf20Sopenharmony_ci * zynq_get_dtype - Return the controller memory width. 5908c2ecf20Sopenharmony_ci * @base: DDR memory controller base address. 5918c2ecf20Sopenharmony_ci * 5928c2ecf20Sopenharmony_ci * Get the EDAC device type width appropriate for the current controller 5938c2ecf20Sopenharmony_ci * configuration. 5948c2ecf20Sopenharmony_ci * 5958c2ecf20Sopenharmony_ci * Return: a device type width enumeration. 5968c2ecf20Sopenharmony_ci */ 5978c2ecf20Sopenharmony_cistatic enum dev_type zynq_get_dtype(const void __iomem *base) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci enum dev_type dt; 6008c2ecf20Sopenharmony_ci u32 width; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci width = readl(base + CTRL_OFST); 6038c2ecf20Sopenharmony_ci width = (width & CTRL_BW_MASK) >> CTRL_BW_SHIFT; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci switch (width) { 6068c2ecf20Sopenharmony_ci case DDRCTL_WDTH_16: 6078c2ecf20Sopenharmony_ci dt = DEV_X2; 6088c2ecf20Sopenharmony_ci break; 6098c2ecf20Sopenharmony_ci case DDRCTL_WDTH_32: 6108c2ecf20Sopenharmony_ci dt = DEV_X4; 6118c2ecf20Sopenharmony_ci break; 6128c2ecf20Sopenharmony_ci default: 6138c2ecf20Sopenharmony_ci dt = DEV_UNKNOWN; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci return dt; 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci/** 6208c2ecf20Sopenharmony_ci * zynqmp_get_dtype - Return the controller memory width. 6218c2ecf20Sopenharmony_ci * @base: DDR memory controller base address. 6228c2ecf20Sopenharmony_ci * 6238c2ecf20Sopenharmony_ci * Get the EDAC device type width appropriate for the current controller 6248c2ecf20Sopenharmony_ci * configuration. 6258c2ecf20Sopenharmony_ci * 6268c2ecf20Sopenharmony_ci * Return: a device type width enumeration. 6278c2ecf20Sopenharmony_ci */ 6288c2ecf20Sopenharmony_cistatic enum dev_type zynqmp_get_dtype(const void __iomem *base) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci enum dev_type dt; 6318c2ecf20Sopenharmony_ci u32 width; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci width = readl(base + CTRL_OFST); 6348c2ecf20Sopenharmony_ci width = (width & ECC_CTRL_BUSWIDTH_MASK) >> ECC_CTRL_BUSWIDTH_SHIFT; 6358c2ecf20Sopenharmony_ci switch (width) { 6368c2ecf20Sopenharmony_ci case DDRCTL_EWDTH_16: 6378c2ecf20Sopenharmony_ci dt = DEV_X2; 6388c2ecf20Sopenharmony_ci break; 6398c2ecf20Sopenharmony_ci case DDRCTL_EWDTH_32: 6408c2ecf20Sopenharmony_ci dt = DEV_X4; 6418c2ecf20Sopenharmony_ci break; 6428c2ecf20Sopenharmony_ci case DDRCTL_EWDTH_64: 6438c2ecf20Sopenharmony_ci dt = DEV_X8; 6448c2ecf20Sopenharmony_ci break; 6458c2ecf20Sopenharmony_ci default: 6468c2ecf20Sopenharmony_ci dt = DEV_UNKNOWN; 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci return dt; 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci/** 6538c2ecf20Sopenharmony_ci * zynq_get_ecc_state - Return the controller ECC enable/disable status. 6548c2ecf20Sopenharmony_ci * @base: DDR memory controller base address. 6558c2ecf20Sopenharmony_ci * 6568c2ecf20Sopenharmony_ci * Get the ECC enable/disable status of the controller. 6578c2ecf20Sopenharmony_ci * 6588c2ecf20Sopenharmony_ci * Return: true if enabled, otherwise false. 6598c2ecf20Sopenharmony_ci */ 6608c2ecf20Sopenharmony_cistatic bool zynq_get_ecc_state(void __iomem *base) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci enum dev_type dt; 6638c2ecf20Sopenharmony_ci u32 ecctype; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci dt = zynq_get_dtype(base); 6668c2ecf20Sopenharmony_ci if (dt == DEV_UNKNOWN) 6678c2ecf20Sopenharmony_ci return false; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci ecctype = readl(base + SCRUB_OFST) & SCRUB_MODE_MASK; 6708c2ecf20Sopenharmony_ci if ((ecctype == SCRUB_MODE_SECDED) && (dt == DEV_X2)) 6718c2ecf20Sopenharmony_ci return true; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci return false; 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci/** 6778c2ecf20Sopenharmony_ci * zynqmp_get_ecc_state - Return the controller ECC enable/disable status. 6788c2ecf20Sopenharmony_ci * @base: DDR memory controller base address. 6798c2ecf20Sopenharmony_ci * 6808c2ecf20Sopenharmony_ci * Get the ECC enable/disable status for the controller. 6818c2ecf20Sopenharmony_ci * 6828c2ecf20Sopenharmony_ci * Return: a ECC status boolean i.e true/false - enabled/disabled. 6838c2ecf20Sopenharmony_ci */ 6848c2ecf20Sopenharmony_cistatic bool zynqmp_get_ecc_state(void __iomem *base) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci enum dev_type dt; 6878c2ecf20Sopenharmony_ci u32 ecctype; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci dt = zynqmp_get_dtype(base); 6908c2ecf20Sopenharmony_ci if (dt == DEV_UNKNOWN) 6918c2ecf20Sopenharmony_ci return false; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci ecctype = readl(base + ECC_CFG0_OFST) & SCRUB_MODE_MASK; 6948c2ecf20Sopenharmony_ci if ((ecctype == SCRUB_MODE_SECDED) && 6958c2ecf20Sopenharmony_ci ((dt == DEV_X2) || (dt == DEV_X4) || (dt == DEV_X8))) 6968c2ecf20Sopenharmony_ci return true; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci return false; 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci/** 7028c2ecf20Sopenharmony_ci * get_memsize - Read the size of the attached memory device. 7038c2ecf20Sopenharmony_ci * 7048c2ecf20Sopenharmony_ci * Return: the memory size in bytes. 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_cistatic u32 get_memsize(void) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci struct sysinfo inf; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci si_meminfo(&inf); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci return inf.totalram * inf.mem_unit; 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci/** 7168c2ecf20Sopenharmony_ci * zynq_get_mtype - Return the controller memory type. 7178c2ecf20Sopenharmony_ci * @base: Synopsys ECC status structure. 7188c2ecf20Sopenharmony_ci * 7198c2ecf20Sopenharmony_ci * Get the EDAC memory type appropriate for the current controller 7208c2ecf20Sopenharmony_ci * configuration. 7218c2ecf20Sopenharmony_ci * 7228c2ecf20Sopenharmony_ci * Return: a memory type enumeration. 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_cistatic enum mem_type zynq_get_mtype(const void __iomem *base) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci enum mem_type mt; 7278c2ecf20Sopenharmony_ci u32 memtype; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci memtype = readl(base + T_ZQ_OFST); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (memtype & T_ZQ_DDRMODE_MASK) 7328c2ecf20Sopenharmony_ci mt = MEM_DDR3; 7338c2ecf20Sopenharmony_ci else 7348c2ecf20Sopenharmony_ci mt = MEM_DDR2; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci return mt; 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci/** 7408c2ecf20Sopenharmony_ci * zynqmp_get_mtype - Returns controller memory type. 7418c2ecf20Sopenharmony_ci * @base: Synopsys ECC status structure. 7428c2ecf20Sopenharmony_ci * 7438c2ecf20Sopenharmony_ci * Get the EDAC memory type appropriate for the current controller 7448c2ecf20Sopenharmony_ci * configuration. 7458c2ecf20Sopenharmony_ci * 7468c2ecf20Sopenharmony_ci * Return: a memory type enumeration. 7478c2ecf20Sopenharmony_ci */ 7488c2ecf20Sopenharmony_cistatic enum mem_type zynqmp_get_mtype(const void __iomem *base) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci enum mem_type mt; 7518c2ecf20Sopenharmony_ci u32 memtype; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci memtype = readl(base + CTRL_OFST); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci if ((memtype & MEM_TYPE_DDR3) || (memtype & MEM_TYPE_LPDDR3)) 7568c2ecf20Sopenharmony_ci mt = MEM_DDR3; 7578c2ecf20Sopenharmony_ci else if (memtype & MEM_TYPE_DDR2) 7588c2ecf20Sopenharmony_ci mt = MEM_RDDR2; 7598c2ecf20Sopenharmony_ci else if ((memtype & MEM_TYPE_LPDDR4) || (memtype & MEM_TYPE_DDR4)) 7608c2ecf20Sopenharmony_ci mt = MEM_DDR4; 7618c2ecf20Sopenharmony_ci else 7628c2ecf20Sopenharmony_ci mt = MEM_EMPTY; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci return mt; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci/** 7688c2ecf20Sopenharmony_ci * init_csrows - Initialize the csrow data. 7698c2ecf20Sopenharmony_ci * @mci: EDAC memory controller instance. 7708c2ecf20Sopenharmony_ci * 7718c2ecf20Sopenharmony_ci * Initialize the chip select rows associated with the EDAC memory 7728c2ecf20Sopenharmony_ci * controller instance. 7738c2ecf20Sopenharmony_ci */ 7748c2ecf20Sopenharmony_cistatic void init_csrows(struct mem_ctl_info *mci) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci struct synps_edac_priv *priv = mci->pvt_info; 7778c2ecf20Sopenharmony_ci const struct synps_platform_data *p_data; 7788c2ecf20Sopenharmony_ci struct csrow_info *csi; 7798c2ecf20Sopenharmony_ci struct dimm_info *dimm; 7808c2ecf20Sopenharmony_ci u32 size, row; 7818c2ecf20Sopenharmony_ci int j; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci p_data = priv->p_data; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci for (row = 0; row < mci->nr_csrows; row++) { 7868c2ecf20Sopenharmony_ci csi = mci->csrows[row]; 7878c2ecf20Sopenharmony_ci size = get_memsize(); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci for (j = 0; j < csi->nr_channels; j++) { 7908c2ecf20Sopenharmony_ci dimm = csi->channels[j]->dimm; 7918c2ecf20Sopenharmony_ci dimm->edac_mode = EDAC_SECDED; 7928c2ecf20Sopenharmony_ci dimm->mtype = p_data->get_mtype(priv->baseaddr); 7938c2ecf20Sopenharmony_ci dimm->nr_pages = (size >> PAGE_SHIFT) / csi->nr_channels; 7948c2ecf20Sopenharmony_ci dimm->grain = SYNPS_EDAC_ERR_GRAIN; 7958c2ecf20Sopenharmony_ci dimm->dtype = p_data->get_dtype(priv->baseaddr); 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci/** 8018c2ecf20Sopenharmony_ci * mc_init - Initialize one driver instance. 8028c2ecf20Sopenharmony_ci * @mci: EDAC memory controller instance. 8038c2ecf20Sopenharmony_ci * @pdev: platform device. 8048c2ecf20Sopenharmony_ci * 8058c2ecf20Sopenharmony_ci * Perform initialization of the EDAC memory controller instance and 8068c2ecf20Sopenharmony_ci * related driver-private data associated with the memory controller the 8078c2ecf20Sopenharmony_ci * instance is bound to. 8088c2ecf20Sopenharmony_ci */ 8098c2ecf20Sopenharmony_cistatic void mc_init(struct mem_ctl_info *mci, struct platform_device *pdev) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci struct synps_edac_priv *priv; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci mci->pdev = &pdev->dev; 8148c2ecf20Sopenharmony_ci priv = mci->pvt_info; 8158c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mci); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci /* Initialize controller capabilities and configuration */ 8188c2ecf20Sopenharmony_ci mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR2; 8198c2ecf20Sopenharmony_ci mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; 8208c2ecf20Sopenharmony_ci mci->scrub_cap = SCRUB_HW_SRC; 8218c2ecf20Sopenharmony_ci mci->scrub_mode = SCRUB_NONE; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci mci->edac_cap = EDAC_FLAG_SECDED; 8248c2ecf20Sopenharmony_ci mci->ctl_name = "synps_ddr_controller"; 8258c2ecf20Sopenharmony_ci mci->dev_name = SYNPS_EDAC_MOD_STRING; 8268c2ecf20Sopenharmony_ci mci->mod_name = SYNPS_EDAC_MOD_VER; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci if (priv->p_data->quirks & DDR_ECC_INTR_SUPPORT) { 8298c2ecf20Sopenharmony_ci edac_op_state = EDAC_OPSTATE_INT; 8308c2ecf20Sopenharmony_ci } else { 8318c2ecf20Sopenharmony_ci edac_op_state = EDAC_OPSTATE_POLL; 8328c2ecf20Sopenharmony_ci mci->edac_check = check_errors; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci mci->ctl_page_to_phys = NULL; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci init_csrows(mci); 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistatic void enable_intr(struct synps_edac_priv *priv) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci /* Enable UE/CE Interrupts */ 8438c2ecf20Sopenharmony_ci writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK, 8448c2ecf20Sopenharmony_ci priv->baseaddr + DDR_QOS_IRQ_EN_OFST); 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic void disable_intr(struct synps_edac_priv *priv) 8488c2ecf20Sopenharmony_ci{ 8498c2ecf20Sopenharmony_ci /* Disable UE/CE Interrupts */ 8508c2ecf20Sopenharmony_ci writel(DDR_QOSUE_MASK | DDR_QOSCE_MASK, 8518c2ecf20Sopenharmony_ci priv->baseaddr + DDR_QOS_IRQ_DB_OFST); 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_cistatic int setup_irq(struct mem_ctl_info *mci, 8558c2ecf20Sopenharmony_ci struct platform_device *pdev) 8568c2ecf20Sopenharmony_ci{ 8578c2ecf20Sopenharmony_ci struct synps_edac_priv *priv = mci->pvt_info; 8588c2ecf20Sopenharmony_ci int ret, irq; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 8618c2ecf20Sopenharmony_ci if (irq < 0) { 8628c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 8638c2ecf20Sopenharmony_ci "No IRQ %d in DT\n", irq); 8648c2ecf20Sopenharmony_ci return irq; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, intr_handler, 8688c2ecf20Sopenharmony_ci 0, dev_name(&pdev->dev), mci); 8698c2ecf20Sopenharmony_ci if (ret < 0) { 8708c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, "Failed to request IRQ\n"); 8718c2ecf20Sopenharmony_ci return ret; 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci enable_intr(priv); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci return 0; 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_cistatic const struct synps_platform_data zynq_edac_def = { 8808c2ecf20Sopenharmony_ci .get_error_info = zynq_get_error_info, 8818c2ecf20Sopenharmony_ci .get_mtype = zynq_get_mtype, 8828c2ecf20Sopenharmony_ci .get_dtype = zynq_get_dtype, 8838c2ecf20Sopenharmony_ci .get_ecc_state = zynq_get_ecc_state, 8848c2ecf20Sopenharmony_ci .quirks = 0, 8858c2ecf20Sopenharmony_ci}; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_cistatic const struct synps_platform_data zynqmp_edac_def = { 8888c2ecf20Sopenharmony_ci .get_error_info = zynqmp_get_error_info, 8898c2ecf20Sopenharmony_ci .get_mtype = zynqmp_get_mtype, 8908c2ecf20Sopenharmony_ci .get_dtype = zynqmp_get_dtype, 8918c2ecf20Sopenharmony_ci .get_ecc_state = zynqmp_get_ecc_state, 8928c2ecf20Sopenharmony_ci .quirks = (DDR_ECC_INTR_SUPPORT 8938c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 8948c2ecf20Sopenharmony_ci | DDR_ECC_DATA_POISON_SUPPORT 8958c2ecf20Sopenharmony_ci#endif 8968c2ecf20Sopenharmony_ci ), 8978c2ecf20Sopenharmony_ci}; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic const struct of_device_id synps_edac_match[] = { 9008c2ecf20Sopenharmony_ci { 9018c2ecf20Sopenharmony_ci .compatible = "xlnx,zynq-ddrc-a05", 9028c2ecf20Sopenharmony_ci .data = (void *)&zynq_edac_def 9038c2ecf20Sopenharmony_ci }, 9048c2ecf20Sopenharmony_ci { 9058c2ecf20Sopenharmony_ci .compatible = "xlnx,zynqmp-ddrc-2.40a", 9068c2ecf20Sopenharmony_ci .data = (void *)&zynqmp_edac_def 9078c2ecf20Sopenharmony_ci }, 9088c2ecf20Sopenharmony_ci { 9098c2ecf20Sopenharmony_ci /* end of table */ 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci}; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, synps_edac_match); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 9168c2ecf20Sopenharmony_ci#define to_mci(k) container_of(k, struct mem_ctl_info, dev) 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci/** 9198c2ecf20Sopenharmony_ci * ddr_poison_setup - Update poison registers. 9208c2ecf20Sopenharmony_ci * @priv: DDR memory controller private instance data. 9218c2ecf20Sopenharmony_ci * 9228c2ecf20Sopenharmony_ci * Update poison registers as per DDR mapping. 9238c2ecf20Sopenharmony_ci * Return: none. 9248c2ecf20Sopenharmony_ci */ 9258c2ecf20Sopenharmony_cistatic void ddr_poison_setup(struct synps_edac_priv *priv) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci int col = 0, row = 0, bank = 0, bankgrp = 0, rank = 0, regval; 9288c2ecf20Sopenharmony_ci int index; 9298c2ecf20Sopenharmony_ci ulong hif_addr = 0; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci hif_addr = priv->poison_addr >> 3; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci for (index = 0; index < DDR_MAX_ROW_SHIFT; index++) { 9348c2ecf20Sopenharmony_ci if (priv->row_shift[index]) 9358c2ecf20Sopenharmony_ci row |= (((hif_addr >> priv->row_shift[index]) & 9368c2ecf20Sopenharmony_ci BIT(0)) << index); 9378c2ecf20Sopenharmony_ci else 9388c2ecf20Sopenharmony_ci break; 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci for (index = 0; index < DDR_MAX_COL_SHIFT; index++) { 9428c2ecf20Sopenharmony_ci if (priv->col_shift[index] || index < 3) 9438c2ecf20Sopenharmony_ci col |= (((hif_addr >> priv->col_shift[index]) & 9448c2ecf20Sopenharmony_ci BIT(0)) << index); 9458c2ecf20Sopenharmony_ci else 9468c2ecf20Sopenharmony_ci break; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci for (index = 0; index < DDR_MAX_BANK_SHIFT; index++) { 9508c2ecf20Sopenharmony_ci if (priv->bank_shift[index]) 9518c2ecf20Sopenharmony_ci bank |= (((hif_addr >> priv->bank_shift[index]) & 9528c2ecf20Sopenharmony_ci BIT(0)) << index); 9538c2ecf20Sopenharmony_ci else 9548c2ecf20Sopenharmony_ci break; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci for (index = 0; index < DDR_MAX_BANKGRP_SHIFT; index++) { 9588c2ecf20Sopenharmony_ci if (priv->bankgrp_shift[index]) 9598c2ecf20Sopenharmony_ci bankgrp |= (((hif_addr >> priv->bankgrp_shift[index]) 9608c2ecf20Sopenharmony_ci & BIT(0)) << index); 9618c2ecf20Sopenharmony_ci else 9628c2ecf20Sopenharmony_ci break; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci if (priv->rank_shift[0]) 9668c2ecf20Sopenharmony_ci rank = (hif_addr >> priv->rank_shift[0]) & BIT(0); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci regval = (rank << ECC_POISON0_RANK_SHIFT) & ECC_POISON0_RANK_MASK; 9698c2ecf20Sopenharmony_ci regval |= (col << ECC_POISON0_COLUMN_SHIFT) & ECC_POISON0_COLUMN_MASK; 9708c2ecf20Sopenharmony_ci writel(regval, priv->baseaddr + ECC_POISON0_OFST); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci regval = (bankgrp << ECC_POISON1_BG_SHIFT) & ECC_POISON1_BG_MASK; 9738c2ecf20Sopenharmony_ci regval |= (bank << ECC_POISON1_BANKNR_SHIFT) & ECC_POISON1_BANKNR_MASK; 9748c2ecf20Sopenharmony_ci regval |= (row << ECC_POISON1_ROW_SHIFT) & ECC_POISON1_ROW_MASK; 9758c2ecf20Sopenharmony_ci writel(regval, priv->baseaddr + ECC_POISON1_OFST); 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic ssize_t inject_data_error_show(struct device *dev, 9798c2ecf20Sopenharmony_ci struct device_attribute *mattr, 9808c2ecf20Sopenharmony_ci char *data) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 9838c2ecf20Sopenharmony_ci struct synps_edac_priv *priv = mci->pvt_info; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci return sprintf(data, "Poison0 Addr: 0x%08x\n\rPoison1 Addr: 0x%08x\n\r" 9868c2ecf20Sopenharmony_ci "Error injection Address: 0x%lx\n\r", 9878c2ecf20Sopenharmony_ci readl(priv->baseaddr + ECC_POISON0_OFST), 9888c2ecf20Sopenharmony_ci readl(priv->baseaddr + ECC_POISON1_OFST), 9898c2ecf20Sopenharmony_ci priv->poison_addr); 9908c2ecf20Sopenharmony_ci} 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_cistatic ssize_t inject_data_error_store(struct device *dev, 9938c2ecf20Sopenharmony_ci struct device_attribute *mattr, 9948c2ecf20Sopenharmony_ci const char *data, size_t count) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 9978c2ecf20Sopenharmony_ci struct synps_edac_priv *priv = mci->pvt_info; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (kstrtoul(data, 0, &priv->poison_addr)) 10008c2ecf20Sopenharmony_ci return -EINVAL; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci ddr_poison_setup(priv); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci return count; 10058c2ecf20Sopenharmony_ci} 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_cistatic ssize_t inject_data_poison_show(struct device *dev, 10088c2ecf20Sopenharmony_ci struct device_attribute *mattr, 10098c2ecf20Sopenharmony_ci char *data) 10108c2ecf20Sopenharmony_ci{ 10118c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 10128c2ecf20Sopenharmony_ci struct synps_edac_priv *priv = mci->pvt_info; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci return sprintf(data, "Data Poisoning: %s\n\r", 10158c2ecf20Sopenharmony_ci (((readl(priv->baseaddr + ECC_CFG1_OFST)) & 0x3) == 0x3) 10168c2ecf20Sopenharmony_ci ? ("Correctable Error") : ("UnCorrectable Error")); 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cistatic ssize_t inject_data_poison_store(struct device *dev, 10208c2ecf20Sopenharmony_ci struct device_attribute *mattr, 10218c2ecf20Sopenharmony_ci const char *data, size_t count) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = to_mci(dev); 10248c2ecf20Sopenharmony_ci struct synps_edac_priv *priv = mci->pvt_info; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci writel(0, priv->baseaddr + DDRC_SWCTL); 10278c2ecf20Sopenharmony_ci if (strncmp(data, "CE", 2) == 0) 10288c2ecf20Sopenharmony_ci writel(ECC_CEPOISON_MASK, priv->baseaddr + ECC_CFG1_OFST); 10298c2ecf20Sopenharmony_ci else 10308c2ecf20Sopenharmony_ci writel(ECC_UEPOISON_MASK, priv->baseaddr + ECC_CFG1_OFST); 10318c2ecf20Sopenharmony_ci writel(1, priv->baseaddr + DDRC_SWCTL); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci return count; 10348c2ecf20Sopenharmony_ci} 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(inject_data_error); 10378c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(inject_data_poison); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_cistatic int edac_create_sysfs_attributes(struct mem_ctl_info *mci) 10408c2ecf20Sopenharmony_ci{ 10418c2ecf20Sopenharmony_ci int rc; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci rc = device_create_file(&mci->dev, &dev_attr_inject_data_error); 10448c2ecf20Sopenharmony_ci if (rc < 0) 10458c2ecf20Sopenharmony_ci return rc; 10468c2ecf20Sopenharmony_ci rc = device_create_file(&mci->dev, &dev_attr_inject_data_poison); 10478c2ecf20Sopenharmony_ci if (rc < 0) 10488c2ecf20Sopenharmony_ci return rc; 10498c2ecf20Sopenharmony_ci return 0; 10508c2ecf20Sopenharmony_ci} 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_cistatic void edac_remove_sysfs_attributes(struct mem_ctl_info *mci) 10538c2ecf20Sopenharmony_ci{ 10548c2ecf20Sopenharmony_ci device_remove_file(&mci->dev, &dev_attr_inject_data_error); 10558c2ecf20Sopenharmony_ci device_remove_file(&mci->dev, &dev_attr_inject_data_poison); 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_cistatic void setup_row_address_map(struct synps_edac_priv *priv, u32 *addrmap) 10598c2ecf20Sopenharmony_ci{ 10608c2ecf20Sopenharmony_ci u32 addrmap_row_b2_10; 10618c2ecf20Sopenharmony_ci int index; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci priv->row_shift[0] = (addrmap[5] & ROW_MAX_VAL_MASK) + ROW_B0_BASE; 10648c2ecf20Sopenharmony_ci priv->row_shift[1] = ((addrmap[5] >> 8) & 10658c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B1_BASE; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci addrmap_row_b2_10 = (addrmap[5] >> 16) & ROW_MAX_VAL_MASK; 10688c2ecf20Sopenharmony_ci if (addrmap_row_b2_10 != ROW_MAX_VAL_MASK) { 10698c2ecf20Sopenharmony_ci for (index = 2; index < 11; index++) 10708c2ecf20Sopenharmony_ci priv->row_shift[index] = addrmap_row_b2_10 + 10718c2ecf20Sopenharmony_ci index + ROW_B0_BASE; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci } else { 10748c2ecf20Sopenharmony_ci priv->row_shift[2] = (addrmap[9] & 10758c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B2_BASE; 10768c2ecf20Sopenharmony_ci priv->row_shift[3] = ((addrmap[9] >> 8) & 10778c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B3_BASE; 10788c2ecf20Sopenharmony_ci priv->row_shift[4] = ((addrmap[9] >> 16) & 10798c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B4_BASE; 10808c2ecf20Sopenharmony_ci priv->row_shift[5] = ((addrmap[9] >> 24) & 10818c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B5_BASE; 10828c2ecf20Sopenharmony_ci priv->row_shift[6] = (addrmap[10] & 10838c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B6_BASE; 10848c2ecf20Sopenharmony_ci priv->row_shift[7] = ((addrmap[10] >> 8) & 10858c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B7_BASE; 10868c2ecf20Sopenharmony_ci priv->row_shift[8] = ((addrmap[10] >> 16) & 10878c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B8_BASE; 10888c2ecf20Sopenharmony_ci priv->row_shift[9] = ((addrmap[10] >> 24) & 10898c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B9_BASE; 10908c2ecf20Sopenharmony_ci priv->row_shift[10] = (addrmap[11] & 10918c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B10_BASE; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci priv->row_shift[11] = (((addrmap[5] >> 24) & ROW_MAX_VAL_MASK) == 10958c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) ? 0 : (((addrmap[5] >> 24) & 10968c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B11_BASE); 10978c2ecf20Sopenharmony_ci priv->row_shift[12] = ((addrmap[6] & ROW_MAX_VAL_MASK) == 10988c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) ? 0 : ((addrmap[6] & 10998c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B12_BASE); 11008c2ecf20Sopenharmony_ci priv->row_shift[13] = (((addrmap[6] >> 8) & ROW_MAX_VAL_MASK) == 11018c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) ? 0 : (((addrmap[6] >> 8) & 11028c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B13_BASE); 11038c2ecf20Sopenharmony_ci priv->row_shift[14] = (((addrmap[6] >> 16) & ROW_MAX_VAL_MASK) == 11048c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) ? 0 : (((addrmap[6] >> 16) & 11058c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B14_BASE); 11068c2ecf20Sopenharmony_ci priv->row_shift[15] = (((addrmap[6] >> 24) & ROW_MAX_VAL_MASK) == 11078c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) ? 0 : (((addrmap[6] >> 24) & 11088c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B15_BASE); 11098c2ecf20Sopenharmony_ci priv->row_shift[16] = ((addrmap[7] & ROW_MAX_VAL_MASK) == 11108c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) ? 0 : ((addrmap[7] & 11118c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B16_BASE); 11128c2ecf20Sopenharmony_ci priv->row_shift[17] = (((addrmap[7] >> 8) & ROW_MAX_VAL_MASK) == 11138c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) ? 0 : (((addrmap[7] >> 8) & 11148c2ecf20Sopenharmony_ci ROW_MAX_VAL_MASK) + ROW_B17_BASE); 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_cistatic void setup_column_address_map(struct synps_edac_priv *priv, u32 *addrmap) 11188c2ecf20Sopenharmony_ci{ 11198c2ecf20Sopenharmony_ci u32 width, memtype; 11208c2ecf20Sopenharmony_ci int index; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci memtype = readl(priv->baseaddr + CTRL_OFST); 11238c2ecf20Sopenharmony_ci width = (memtype & ECC_CTRL_BUSWIDTH_MASK) >> ECC_CTRL_BUSWIDTH_SHIFT; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci priv->col_shift[0] = 0; 11268c2ecf20Sopenharmony_ci priv->col_shift[1] = 1; 11278c2ecf20Sopenharmony_ci priv->col_shift[2] = (addrmap[2] & COL_MAX_VAL_MASK) + COL_B2_BASE; 11288c2ecf20Sopenharmony_ci priv->col_shift[3] = ((addrmap[2] >> 8) & 11298c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) + COL_B3_BASE; 11308c2ecf20Sopenharmony_ci priv->col_shift[4] = (((addrmap[2] >> 16) & COL_MAX_VAL_MASK) == 11318c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) ? 0 : (((addrmap[2] >> 16) & 11328c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) + COL_B4_BASE); 11338c2ecf20Sopenharmony_ci priv->col_shift[5] = (((addrmap[2] >> 24) & COL_MAX_VAL_MASK) == 11348c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) ? 0 : (((addrmap[2] >> 24) & 11358c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) + COL_B5_BASE); 11368c2ecf20Sopenharmony_ci priv->col_shift[6] = ((addrmap[3] & COL_MAX_VAL_MASK) == 11378c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) ? 0 : ((addrmap[3] & 11388c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) + COL_B6_BASE); 11398c2ecf20Sopenharmony_ci priv->col_shift[7] = (((addrmap[3] >> 8) & COL_MAX_VAL_MASK) == 11408c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) ? 0 : (((addrmap[3] >> 8) & 11418c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) + COL_B7_BASE); 11428c2ecf20Sopenharmony_ci priv->col_shift[8] = (((addrmap[3] >> 16) & COL_MAX_VAL_MASK) == 11438c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) ? 0 : (((addrmap[3] >> 16) & 11448c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) + COL_B8_BASE); 11458c2ecf20Sopenharmony_ci priv->col_shift[9] = (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) == 11468c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) ? 0 : (((addrmap[3] >> 24) & 11478c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) + COL_B9_BASE); 11488c2ecf20Sopenharmony_ci if (width == DDRCTL_EWDTH_64) { 11498c2ecf20Sopenharmony_ci if (memtype & MEM_TYPE_LPDDR3) { 11508c2ecf20Sopenharmony_ci priv->col_shift[10] = ((addrmap[4] & 11518c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : 11528c2ecf20Sopenharmony_ci ((addrmap[4] & COL_MAX_VAL_MASK) + 11538c2ecf20Sopenharmony_ci COL_B10_BASE); 11548c2ecf20Sopenharmony_ci priv->col_shift[11] = (((addrmap[4] >> 8) & 11558c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : 11568c2ecf20Sopenharmony_ci (((addrmap[4] >> 8) & COL_MAX_VAL_MASK) + 11578c2ecf20Sopenharmony_ci COL_B11_BASE); 11588c2ecf20Sopenharmony_ci } else { 11598c2ecf20Sopenharmony_ci priv->col_shift[11] = ((addrmap[4] & 11608c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : 11618c2ecf20Sopenharmony_ci ((addrmap[4] & COL_MAX_VAL_MASK) + 11628c2ecf20Sopenharmony_ci COL_B10_BASE); 11638c2ecf20Sopenharmony_ci priv->col_shift[13] = (((addrmap[4] >> 8) & 11648c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : 11658c2ecf20Sopenharmony_ci (((addrmap[4] >> 8) & COL_MAX_VAL_MASK) + 11668c2ecf20Sopenharmony_ci COL_B11_BASE); 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci } else if (width == DDRCTL_EWDTH_32) { 11698c2ecf20Sopenharmony_ci if (memtype & MEM_TYPE_LPDDR3) { 11708c2ecf20Sopenharmony_ci priv->col_shift[10] = (((addrmap[3] >> 24) & 11718c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : 11728c2ecf20Sopenharmony_ci (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) + 11738c2ecf20Sopenharmony_ci COL_B9_BASE); 11748c2ecf20Sopenharmony_ci priv->col_shift[11] = ((addrmap[4] & 11758c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : 11768c2ecf20Sopenharmony_ci ((addrmap[4] & COL_MAX_VAL_MASK) + 11778c2ecf20Sopenharmony_ci COL_B10_BASE); 11788c2ecf20Sopenharmony_ci } else { 11798c2ecf20Sopenharmony_ci priv->col_shift[11] = (((addrmap[3] >> 24) & 11808c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : 11818c2ecf20Sopenharmony_ci (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) + 11828c2ecf20Sopenharmony_ci COL_B9_BASE); 11838c2ecf20Sopenharmony_ci priv->col_shift[13] = ((addrmap[4] & 11848c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : 11858c2ecf20Sopenharmony_ci ((addrmap[4] & COL_MAX_VAL_MASK) + 11868c2ecf20Sopenharmony_ci COL_B10_BASE); 11878c2ecf20Sopenharmony_ci } 11888c2ecf20Sopenharmony_ci } else { 11898c2ecf20Sopenharmony_ci if (memtype & MEM_TYPE_LPDDR3) { 11908c2ecf20Sopenharmony_ci priv->col_shift[10] = (((addrmap[3] >> 16) & 11918c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : 11928c2ecf20Sopenharmony_ci (((addrmap[3] >> 16) & COL_MAX_VAL_MASK) + 11938c2ecf20Sopenharmony_ci COL_B8_BASE); 11948c2ecf20Sopenharmony_ci priv->col_shift[11] = (((addrmap[3] >> 24) & 11958c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : 11968c2ecf20Sopenharmony_ci (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) + 11978c2ecf20Sopenharmony_ci COL_B9_BASE); 11988c2ecf20Sopenharmony_ci priv->col_shift[13] = ((addrmap[4] & 11998c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : 12008c2ecf20Sopenharmony_ci ((addrmap[4] & COL_MAX_VAL_MASK) + 12018c2ecf20Sopenharmony_ci COL_B10_BASE); 12028c2ecf20Sopenharmony_ci } else { 12038c2ecf20Sopenharmony_ci priv->col_shift[11] = (((addrmap[3] >> 16) & 12048c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : 12058c2ecf20Sopenharmony_ci (((addrmap[3] >> 16) & COL_MAX_VAL_MASK) + 12068c2ecf20Sopenharmony_ci COL_B8_BASE); 12078c2ecf20Sopenharmony_ci priv->col_shift[13] = (((addrmap[3] >> 24) & 12088c2ecf20Sopenharmony_ci COL_MAX_VAL_MASK) == COL_MAX_VAL_MASK) ? 0 : 12098c2ecf20Sopenharmony_ci (((addrmap[3] >> 24) & COL_MAX_VAL_MASK) + 12108c2ecf20Sopenharmony_ci COL_B9_BASE); 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci if (width) { 12158c2ecf20Sopenharmony_ci for (index = 9; index > width; index--) { 12168c2ecf20Sopenharmony_ci priv->col_shift[index] = priv->col_shift[index - width]; 12178c2ecf20Sopenharmony_ci priv->col_shift[index - width] = 0; 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci} 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_cistatic void setup_bank_address_map(struct synps_edac_priv *priv, u32 *addrmap) 12248c2ecf20Sopenharmony_ci{ 12258c2ecf20Sopenharmony_ci priv->bank_shift[0] = (addrmap[1] & BANK_MAX_VAL_MASK) + BANK_B0_BASE; 12268c2ecf20Sopenharmony_ci priv->bank_shift[1] = ((addrmap[1] >> 8) & 12278c2ecf20Sopenharmony_ci BANK_MAX_VAL_MASK) + BANK_B1_BASE; 12288c2ecf20Sopenharmony_ci priv->bank_shift[2] = (((addrmap[1] >> 16) & 12298c2ecf20Sopenharmony_ci BANK_MAX_VAL_MASK) == BANK_MAX_VAL_MASK) ? 0 : 12308c2ecf20Sopenharmony_ci (((addrmap[1] >> 16) & BANK_MAX_VAL_MASK) + 12318c2ecf20Sopenharmony_ci BANK_B2_BASE); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci} 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_cistatic void setup_bg_address_map(struct synps_edac_priv *priv, u32 *addrmap) 12368c2ecf20Sopenharmony_ci{ 12378c2ecf20Sopenharmony_ci priv->bankgrp_shift[0] = (addrmap[8] & 12388c2ecf20Sopenharmony_ci BANKGRP_MAX_VAL_MASK) + BANKGRP_B0_BASE; 12398c2ecf20Sopenharmony_ci priv->bankgrp_shift[1] = (((addrmap[8] >> 8) & BANKGRP_MAX_VAL_MASK) == 12408c2ecf20Sopenharmony_ci BANKGRP_MAX_VAL_MASK) ? 0 : (((addrmap[8] >> 8) 12418c2ecf20Sopenharmony_ci & BANKGRP_MAX_VAL_MASK) + BANKGRP_B1_BASE); 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci} 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_cistatic void setup_rank_address_map(struct synps_edac_priv *priv, u32 *addrmap) 12468c2ecf20Sopenharmony_ci{ 12478c2ecf20Sopenharmony_ci priv->rank_shift[0] = ((addrmap[0] & RANK_MAX_VAL_MASK) == 12488c2ecf20Sopenharmony_ci RANK_MAX_VAL_MASK) ? 0 : ((addrmap[0] & 12498c2ecf20Sopenharmony_ci RANK_MAX_VAL_MASK) + RANK_B0_BASE); 12508c2ecf20Sopenharmony_ci} 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci/** 12538c2ecf20Sopenharmony_ci * setup_address_map - Set Address Map by querying ADDRMAP registers. 12548c2ecf20Sopenharmony_ci * @priv: DDR memory controller private instance data. 12558c2ecf20Sopenharmony_ci * 12568c2ecf20Sopenharmony_ci * Set Address Map by querying ADDRMAP registers. 12578c2ecf20Sopenharmony_ci * 12588c2ecf20Sopenharmony_ci * Return: none. 12598c2ecf20Sopenharmony_ci */ 12608c2ecf20Sopenharmony_cistatic void setup_address_map(struct synps_edac_priv *priv) 12618c2ecf20Sopenharmony_ci{ 12628c2ecf20Sopenharmony_ci u32 addrmap[12]; 12638c2ecf20Sopenharmony_ci int index; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci for (index = 0; index < 12; index++) { 12668c2ecf20Sopenharmony_ci u32 addrmap_offset; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci addrmap_offset = ECC_ADDRMAP0_OFFSET + (index * 4); 12698c2ecf20Sopenharmony_ci addrmap[index] = readl(priv->baseaddr + addrmap_offset); 12708c2ecf20Sopenharmony_ci } 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci setup_row_address_map(priv, addrmap); 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci setup_column_address_map(priv, addrmap); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci setup_bank_address_map(priv, addrmap); 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci setup_bg_address_map(priv, addrmap); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci setup_rank_address_map(priv, addrmap); 12818c2ecf20Sopenharmony_ci} 12828c2ecf20Sopenharmony_ci#endif /* CONFIG_EDAC_DEBUG */ 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci/** 12858c2ecf20Sopenharmony_ci * mc_probe - Check controller and bind driver. 12868c2ecf20Sopenharmony_ci * @pdev: platform device. 12878c2ecf20Sopenharmony_ci * 12888c2ecf20Sopenharmony_ci * Probe a specific controller instance for binding with the driver. 12898c2ecf20Sopenharmony_ci * 12908c2ecf20Sopenharmony_ci * Return: 0 if the controller instance was successfully bound to the 12918c2ecf20Sopenharmony_ci * driver; otherwise, < 0 on error. 12928c2ecf20Sopenharmony_ci */ 12938c2ecf20Sopenharmony_cistatic int mc_probe(struct platform_device *pdev) 12948c2ecf20Sopenharmony_ci{ 12958c2ecf20Sopenharmony_ci const struct synps_platform_data *p_data; 12968c2ecf20Sopenharmony_ci struct edac_mc_layer layers[2]; 12978c2ecf20Sopenharmony_ci struct synps_edac_priv *priv; 12988c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 12998c2ecf20Sopenharmony_ci void __iomem *baseaddr; 13008c2ecf20Sopenharmony_ci struct resource *res; 13018c2ecf20Sopenharmony_ci int rc; 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 13048c2ecf20Sopenharmony_ci baseaddr = devm_ioremap_resource(&pdev->dev, res); 13058c2ecf20Sopenharmony_ci if (IS_ERR(baseaddr)) 13068c2ecf20Sopenharmony_ci return PTR_ERR(baseaddr); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci p_data = of_device_get_match_data(&pdev->dev); 13098c2ecf20Sopenharmony_ci if (!p_data) 13108c2ecf20Sopenharmony_ci return -ENODEV; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci if (!p_data->get_ecc_state(baseaddr)) { 13138c2ecf20Sopenharmony_ci edac_printk(KERN_INFO, EDAC_MC, "ECC not enabled\n"); 13148c2ecf20Sopenharmony_ci return -ENXIO; 13158c2ecf20Sopenharmony_ci } 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 13188c2ecf20Sopenharmony_ci layers[0].size = SYNPS_EDAC_NR_CSROWS; 13198c2ecf20Sopenharmony_ci layers[0].is_virt_csrow = true; 13208c2ecf20Sopenharmony_ci layers[1].type = EDAC_MC_LAYER_CHANNEL; 13218c2ecf20Sopenharmony_ci layers[1].size = SYNPS_EDAC_NR_CHANS; 13228c2ecf20Sopenharmony_ci layers[1].is_virt_csrow = false; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 13258c2ecf20Sopenharmony_ci sizeof(struct synps_edac_priv)); 13268c2ecf20Sopenharmony_ci if (!mci) { 13278c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 13288c2ecf20Sopenharmony_ci "Failed memory allocation for mc instance\n"); 13298c2ecf20Sopenharmony_ci return -ENOMEM; 13308c2ecf20Sopenharmony_ci } 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci priv = mci->pvt_info; 13338c2ecf20Sopenharmony_ci priv->baseaddr = baseaddr; 13348c2ecf20Sopenharmony_ci priv->p_data = p_data; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci mc_init(mci, pdev); 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci if (priv->p_data->quirks & DDR_ECC_INTR_SUPPORT) { 13398c2ecf20Sopenharmony_ci rc = setup_irq(mci, pdev); 13408c2ecf20Sopenharmony_ci if (rc) 13418c2ecf20Sopenharmony_ci goto free_edac_mc; 13428c2ecf20Sopenharmony_ci } 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci rc = edac_mc_add_mc(mci); 13458c2ecf20Sopenharmony_ci if (rc) { 13468c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 13478c2ecf20Sopenharmony_ci "Failed to register with EDAC core\n"); 13488c2ecf20Sopenharmony_ci goto free_edac_mc; 13498c2ecf20Sopenharmony_ci } 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 13528c2ecf20Sopenharmony_ci if (priv->p_data->quirks & DDR_ECC_DATA_POISON_SUPPORT) { 13538c2ecf20Sopenharmony_ci if (edac_create_sysfs_attributes(mci)) { 13548c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 13558c2ecf20Sopenharmony_ci "Failed to create sysfs entries\n"); 13568c2ecf20Sopenharmony_ci goto free_edac_mc; 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci } 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci if (priv->p_data->quirks & DDR_ECC_INTR_SUPPORT) 13618c2ecf20Sopenharmony_ci setup_address_map(priv); 13628c2ecf20Sopenharmony_ci#endif 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci /* 13658c2ecf20Sopenharmony_ci * Start capturing the correctable and uncorrectable errors. A write of 13668c2ecf20Sopenharmony_ci * 0 starts the counters. 13678c2ecf20Sopenharmony_ci */ 13688c2ecf20Sopenharmony_ci if (!(priv->p_data->quirks & DDR_ECC_INTR_SUPPORT)) 13698c2ecf20Sopenharmony_ci writel(0x0, baseaddr + ECC_CTRL_OFST); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci return rc; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_cifree_edac_mc: 13748c2ecf20Sopenharmony_ci edac_mc_free(mci); 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci return rc; 13778c2ecf20Sopenharmony_ci} 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci/** 13808c2ecf20Sopenharmony_ci * mc_remove - Unbind driver from controller. 13818c2ecf20Sopenharmony_ci * @pdev: Platform device. 13828c2ecf20Sopenharmony_ci * 13838c2ecf20Sopenharmony_ci * Return: Unconditionally 0 13848c2ecf20Sopenharmony_ci */ 13858c2ecf20Sopenharmony_cistatic int mc_remove(struct platform_device *pdev) 13868c2ecf20Sopenharmony_ci{ 13878c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = platform_get_drvdata(pdev); 13888c2ecf20Sopenharmony_ci struct synps_edac_priv *priv = mci->pvt_info; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci if (priv->p_data->quirks & DDR_ECC_INTR_SUPPORT) 13918c2ecf20Sopenharmony_ci disable_intr(priv); 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_DEBUG 13948c2ecf20Sopenharmony_ci if (priv->p_data->quirks & DDR_ECC_DATA_POISON_SUPPORT) 13958c2ecf20Sopenharmony_ci edac_remove_sysfs_attributes(mci); 13968c2ecf20Sopenharmony_ci#endif 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci edac_mc_del_mc(&pdev->dev); 13998c2ecf20Sopenharmony_ci edac_mc_free(mci); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci return 0; 14028c2ecf20Sopenharmony_ci} 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_cistatic struct platform_driver synps_edac_mc_driver = { 14058c2ecf20Sopenharmony_ci .driver = { 14068c2ecf20Sopenharmony_ci .name = "synopsys-edac", 14078c2ecf20Sopenharmony_ci .of_match_table = synps_edac_match, 14088c2ecf20Sopenharmony_ci }, 14098c2ecf20Sopenharmony_ci .probe = mc_probe, 14108c2ecf20Sopenharmony_ci .remove = mc_remove, 14118c2ecf20Sopenharmony_ci}; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_cimodule_platform_driver(synps_edac_mc_driver); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ciMODULE_AUTHOR("Xilinx Inc"); 14168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Synopsys DDR ECC driver"); 14178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1418