18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2017-2018, Intel Corporation. All rights reserved 48c2ecf20Sopenharmony_ci * Copyright Altera Corporation (C) 2014-2016. All rights reserved. 58c2ecf20Sopenharmony_ci * Copyright 2011-2012 Calxeda, Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 98c2ecf20Sopenharmony_ci#include <linux/ctype.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/edac.h> 128c2ecf20Sopenharmony_ci#include <linux/firmware/intel/stratix10-smc.h> 138c2ecf20Sopenharmony_ci#include <linux/genalloc.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/mfd/altera-sysmgr.h> 188c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 198c2ecf20Sopenharmony_ci#include <linux/notifier.h> 208c2ecf20Sopenharmony_ci#include <linux/of_address.h> 218c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 228c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 248c2ecf20Sopenharmony_ci#include <linux/regmap.h> 258c2ecf20Sopenharmony_ci#include <linux/types.h> 268c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "altera_edac.h" 298c2ecf20Sopenharmony_ci#include "edac_module.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define EDAC_MOD_STR "altera_edac" 328c2ecf20Sopenharmony_ci#define EDAC_DEVICE "Altera" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_SDRAM 358c2ecf20Sopenharmony_cistatic const struct altr_sdram_prv_data c5_data = { 368c2ecf20Sopenharmony_ci .ecc_ctrl_offset = CV_CTLCFG_OFST, 378c2ecf20Sopenharmony_ci .ecc_ctl_en_mask = CV_CTLCFG_ECC_AUTO_EN, 388c2ecf20Sopenharmony_ci .ecc_stat_offset = CV_DRAMSTS_OFST, 398c2ecf20Sopenharmony_ci .ecc_stat_ce_mask = CV_DRAMSTS_SBEERR, 408c2ecf20Sopenharmony_ci .ecc_stat_ue_mask = CV_DRAMSTS_DBEERR, 418c2ecf20Sopenharmony_ci .ecc_saddr_offset = CV_ERRADDR_OFST, 428c2ecf20Sopenharmony_ci .ecc_daddr_offset = CV_ERRADDR_OFST, 438c2ecf20Sopenharmony_ci .ecc_cecnt_offset = CV_SBECOUNT_OFST, 448c2ecf20Sopenharmony_ci .ecc_uecnt_offset = CV_DBECOUNT_OFST, 458c2ecf20Sopenharmony_ci .ecc_irq_en_offset = CV_DRAMINTR_OFST, 468c2ecf20Sopenharmony_ci .ecc_irq_en_mask = CV_DRAMINTR_INTREN, 478c2ecf20Sopenharmony_ci .ecc_irq_clr_offset = CV_DRAMINTR_OFST, 488c2ecf20Sopenharmony_ci .ecc_irq_clr_mask = (CV_DRAMINTR_INTRCLR | CV_DRAMINTR_INTREN), 498c2ecf20Sopenharmony_ci .ecc_cnt_rst_offset = CV_DRAMINTR_OFST, 508c2ecf20Sopenharmony_ci .ecc_cnt_rst_mask = CV_DRAMINTR_INTRCLR, 518c2ecf20Sopenharmony_ci .ce_ue_trgr_offset = CV_CTLCFG_OFST, 528c2ecf20Sopenharmony_ci .ce_set_mask = CV_CTLCFG_GEN_SB_ERR, 538c2ecf20Sopenharmony_ci .ue_set_mask = CV_CTLCFG_GEN_DB_ERR, 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic const struct altr_sdram_prv_data a10_data = { 578c2ecf20Sopenharmony_ci .ecc_ctrl_offset = A10_ECCCTRL1_OFST, 588c2ecf20Sopenharmony_ci .ecc_ctl_en_mask = A10_ECCCTRL1_ECC_EN, 598c2ecf20Sopenharmony_ci .ecc_stat_offset = A10_INTSTAT_OFST, 608c2ecf20Sopenharmony_ci .ecc_stat_ce_mask = A10_INTSTAT_SBEERR, 618c2ecf20Sopenharmony_ci .ecc_stat_ue_mask = A10_INTSTAT_DBEERR, 628c2ecf20Sopenharmony_ci .ecc_saddr_offset = A10_SERRADDR_OFST, 638c2ecf20Sopenharmony_ci .ecc_daddr_offset = A10_DERRADDR_OFST, 648c2ecf20Sopenharmony_ci .ecc_irq_en_offset = A10_ERRINTEN_OFST, 658c2ecf20Sopenharmony_ci .ecc_irq_en_mask = A10_ECC_IRQ_EN_MASK, 668c2ecf20Sopenharmony_ci .ecc_irq_clr_offset = A10_INTSTAT_OFST, 678c2ecf20Sopenharmony_ci .ecc_irq_clr_mask = (A10_INTSTAT_SBEERR | A10_INTSTAT_DBEERR), 688c2ecf20Sopenharmony_ci .ecc_cnt_rst_offset = A10_ECCCTRL1_OFST, 698c2ecf20Sopenharmony_ci .ecc_cnt_rst_mask = A10_ECC_CNT_RESET_MASK, 708c2ecf20Sopenharmony_ci .ce_ue_trgr_offset = A10_DIAGINTTEST_OFST, 718c2ecf20Sopenharmony_ci .ce_set_mask = A10_DIAGINT_TSERRA_MASK, 728c2ecf20Sopenharmony_ci .ue_set_mask = A10_DIAGINT_TDERRA_MASK, 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/*********************** EDAC Memory Controller Functions ****************/ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* The SDRAM controller uses the EDAC Memory Controller framework. */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = dev_id; 828c2ecf20Sopenharmony_ci struct altr_sdram_mc_data *drvdata = mci->pvt_info; 838c2ecf20Sopenharmony_ci const struct altr_sdram_prv_data *priv = drvdata->data; 848c2ecf20Sopenharmony_ci u32 status, err_count = 1, err_addr; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci regmap_read(drvdata->mc_vbase, priv->ecc_stat_offset, &status); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (status & priv->ecc_stat_ue_mask) { 898c2ecf20Sopenharmony_ci regmap_read(drvdata->mc_vbase, priv->ecc_daddr_offset, 908c2ecf20Sopenharmony_ci &err_addr); 918c2ecf20Sopenharmony_ci if (priv->ecc_uecnt_offset) 928c2ecf20Sopenharmony_ci regmap_read(drvdata->mc_vbase, priv->ecc_uecnt_offset, 938c2ecf20Sopenharmony_ci &err_count); 948c2ecf20Sopenharmony_ci panic("\nEDAC: [%d Uncorrectable errors @ 0x%08X]\n", 958c2ecf20Sopenharmony_ci err_count, err_addr); 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci if (status & priv->ecc_stat_ce_mask) { 988c2ecf20Sopenharmony_ci regmap_read(drvdata->mc_vbase, priv->ecc_saddr_offset, 998c2ecf20Sopenharmony_ci &err_addr); 1008c2ecf20Sopenharmony_ci if (priv->ecc_uecnt_offset) 1018c2ecf20Sopenharmony_ci regmap_read(drvdata->mc_vbase, priv->ecc_cecnt_offset, 1028c2ecf20Sopenharmony_ci &err_count); 1038c2ecf20Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, err_count, 1048c2ecf20Sopenharmony_ci err_addr >> PAGE_SHIFT, 1058c2ecf20Sopenharmony_ci err_addr & ~PAGE_MASK, 0, 1068c2ecf20Sopenharmony_ci 0, 0, -1, mci->ctl_name, ""); 1078c2ecf20Sopenharmony_ci /* Clear IRQ to resume */ 1088c2ecf20Sopenharmony_ci regmap_write(drvdata->mc_vbase, priv->ecc_irq_clr_offset, 1098c2ecf20Sopenharmony_ci priv->ecc_irq_clr_mask); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci return IRQ_NONE; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic ssize_t altr_sdr_mc_err_inject_write(struct file *file, 1178c2ecf20Sopenharmony_ci const char __user *data, 1188c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = file->private_data; 1218c2ecf20Sopenharmony_ci struct altr_sdram_mc_data *drvdata = mci->pvt_info; 1228c2ecf20Sopenharmony_ci const struct altr_sdram_prv_data *priv = drvdata->data; 1238c2ecf20Sopenharmony_ci u32 *ptemp; 1248c2ecf20Sopenharmony_ci dma_addr_t dma_handle; 1258c2ecf20Sopenharmony_ci u32 reg, read_reg; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci ptemp = dma_alloc_coherent(mci->pdev, 16, &dma_handle, GFP_KERNEL); 1288c2ecf20Sopenharmony_ci if (!ptemp) { 1298c2ecf20Sopenharmony_ci dma_free_coherent(mci->pdev, 16, ptemp, dma_handle); 1308c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 1318c2ecf20Sopenharmony_ci "Inject: Buffer Allocation error\n"); 1328c2ecf20Sopenharmony_ci return -ENOMEM; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci regmap_read(drvdata->mc_vbase, priv->ce_ue_trgr_offset, 1368c2ecf20Sopenharmony_ci &read_reg); 1378c2ecf20Sopenharmony_ci read_reg &= ~(priv->ce_set_mask | priv->ue_set_mask); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* Error are injected by writing a word while the SBE or DBE 1408c2ecf20Sopenharmony_ci * bit in the CTLCFG register is set. Reading the word will 1418c2ecf20Sopenharmony_ci * trigger the SBE or DBE error and the corresponding IRQ. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ci if (count == 3) { 1448c2ecf20Sopenharmony_ci edac_printk(KERN_ALERT, EDAC_MC, 1458c2ecf20Sopenharmony_ci "Inject Double bit error\n"); 1468c2ecf20Sopenharmony_ci local_irq_disable(); 1478c2ecf20Sopenharmony_ci regmap_write(drvdata->mc_vbase, priv->ce_ue_trgr_offset, 1488c2ecf20Sopenharmony_ci (read_reg | priv->ue_set_mask)); 1498c2ecf20Sopenharmony_ci local_irq_enable(); 1508c2ecf20Sopenharmony_ci } else { 1518c2ecf20Sopenharmony_ci edac_printk(KERN_ALERT, EDAC_MC, 1528c2ecf20Sopenharmony_ci "Inject Single bit error\n"); 1538c2ecf20Sopenharmony_ci local_irq_disable(); 1548c2ecf20Sopenharmony_ci regmap_write(drvdata->mc_vbase, priv->ce_ue_trgr_offset, 1558c2ecf20Sopenharmony_ci (read_reg | priv->ce_set_mask)); 1568c2ecf20Sopenharmony_ci local_irq_enable(); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci ptemp[0] = 0x5A5A5A5A; 1608c2ecf20Sopenharmony_ci ptemp[1] = 0xA5A5A5A5; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* Clear the error injection bits */ 1638c2ecf20Sopenharmony_ci regmap_write(drvdata->mc_vbase, priv->ce_ue_trgr_offset, read_reg); 1648c2ecf20Sopenharmony_ci /* Ensure it has been written out */ 1658c2ecf20Sopenharmony_ci wmb(); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* 1688c2ecf20Sopenharmony_ci * To trigger the error, we need to read the data back 1698c2ecf20Sopenharmony_ci * (the data was written with errors above). 1708c2ecf20Sopenharmony_ci * The READ_ONCE macros and printk are used to prevent the 1718c2ecf20Sopenharmony_ci * the compiler optimizing these reads out. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci reg = READ_ONCE(ptemp[0]); 1748c2ecf20Sopenharmony_ci read_reg = READ_ONCE(ptemp[1]); 1758c2ecf20Sopenharmony_ci /* Force Read */ 1768c2ecf20Sopenharmony_ci rmb(); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci edac_printk(KERN_ALERT, EDAC_MC, "Read Data [0x%X, 0x%X]\n", 1798c2ecf20Sopenharmony_ci reg, read_reg); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci dma_free_coherent(mci->pdev, 16, ptemp, dma_handle); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return count; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic const struct file_operations altr_sdr_mc_debug_inject_fops = { 1878c2ecf20Sopenharmony_ci .open = simple_open, 1888c2ecf20Sopenharmony_ci .write = altr_sdr_mc_err_inject_write, 1898c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_EDAC_DEBUG)) 1958c2ecf20Sopenharmony_ci return; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (!mci->debugfs) 1988c2ecf20Sopenharmony_ci return; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci edac_debugfs_create_file("altr_trigger", S_IWUSR, mci->debugfs, mci, 2018c2ecf20Sopenharmony_ci &altr_sdr_mc_debug_inject_fops); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/* Get total memory size from Open Firmware DTB */ 2058c2ecf20Sopenharmony_cistatic unsigned long get_total_mem(void) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct device_node *np = NULL; 2088c2ecf20Sopenharmony_ci struct resource res; 2098c2ecf20Sopenharmony_ci int ret; 2108c2ecf20Sopenharmony_ci unsigned long total_mem = 0; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci for_each_node_by_type(np, "memory") { 2138c2ecf20Sopenharmony_ci ret = of_address_to_resource(np, 0, &res); 2148c2ecf20Sopenharmony_ci if (ret) 2158c2ecf20Sopenharmony_ci continue; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci total_mem += resource_size(&res); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci edac_dbg(0, "total_mem 0x%lx\n", total_mem); 2208c2ecf20Sopenharmony_ci return total_mem; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic const struct of_device_id altr_sdram_ctrl_of_match[] = { 2248c2ecf20Sopenharmony_ci { .compatible = "altr,sdram-edac", .data = &c5_data}, 2258c2ecf20Sopenharmony_ci { .compatible = "altr,sdram-edac-a10", .data = &a10_data}, 2268c2ecf20Sopenharmony_ci {}, 2278c2ecf20Sopenharmony_ci}; 2288c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, altr_sdram_ctrl_of_match); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int a10_init(struct regmap *mc_vbase) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci if (regmap_update_bits(mc_vbase, A10_INTMODE_OFST, 2338c2ecf20Sopenharmony_ci A10_INTMODE_SB_INT, A10_INTMODE_SB_INT)) { 2348c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 2358c2ecf20Sopenharmony_ci "Error setting SB IRQ mode\n"); 2368c2ecf20Sopenharmony_ci return -ENODEV; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (regmap_write(mc_vbase, A10_SERRCNTREG_OFST, 1)) { 2408c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 2418c2ecf20Sopenharmony_ci "Error setting trigger count\n"); 2428c2ecf20Sopenharmony_ci return -ENODEV; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int a10_unmask_irq(struct platform_device *pdev, u32 mask) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci void __iomem *sm_base; 2518c2ecf20Sopenharmony_ci int ret = 0; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (!request_mem_region(A10_SYMAN_INTMASK_CLR, sizeof(u32), 2548c2ecf20Sopenharmony_ci dev_name(&pdev->dev))) { 2558c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 2568c2ecf20Sopenharmony_ci "Unable to request mem region\n"); 2578c2ecf20Sopenharmony_ci return -EBUSY; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci sm_base = ioremap(A10_SYMAN_INTMASK_CLR, sizeof(u32)); 2618c2ecf20Sopenharmony_ci if (!sm_base) { 2628c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 2638c2ecf20Sopenharmony_ci "Unable to ioremap device\n"); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ret = -ENOMEM; 2668c2ecf20Sopenharmony_ci goto release; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci iowrite32(mask, sm_base); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci iounmap(sm_base); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cirelease: 2748c2ecf20Sopenharmony_ci release_mem_region(A10_SYMAN_INTMASK_CLR, sizeof(u32)); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return ret; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int altr_sdram_probe(struct platform_device *pdev) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci const struct of_device_id *id; 2828c2ecf20Sopenharmony_ci struct edac_mc_layer layers[2]; 2838c2ecf20Sopenharmony_ci struct mem_ctl_info *mci; 2848c2ecf20Sopenharmony_ci struct altr_sdram_mc_data *drvdata; 2858c2ecf20Sopenharmony_ci const struct altr_sdram_prv_data *priv; 2868c2ecf20Sopenharmony_ci struct regmap *mc_vbase; 2878c2ecf20Sopenharmony_ci struct dimm_info *dimm; 2888c2ecf20Sopenharmony_ci u32 read_reg; 2898c2ecf20Sopenharmony_ci int irq, irq2, res = 0; 2908c2ecf20Sopenharmony_ci unsigned long mem_size, irqflags = 0; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci id = of_match_device(altr_sdram_ctrl_of_match, &pdev->dev); 2938c2ecf20Sopenharmony_ci if (!id) 2948c2ecf20Sopenharmony_ci return -ENODEV; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Grab the register range from the sdr controller in device tree */ 2978c2ecf20Sopenharmony_ci mc_vbase = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 2988c2ecf20Sopenharmony_ci "altr,sdr-syscon"); 2998c2ecf20Sopenharmony_ci if (IS_ERR(mc_vbase)) { 3008c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 3018c2ecf20Sopenharmony_ci "regmap for altr,sdr-syscon lookup failed.\n"); 3028c2ecf20Sopenharmony_ci return -ENODEV; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* Check specific dependencies for the module */ 3068c2ecf20Sopenharmony_ci priv = of_match_node(altr_sdram_ctrl_of_match, 3078c2ecf20Sopenharmony_ci pdev->dev.of_node)->data; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* Validate the SDRAM controller has ECC enabled */ 3108c2ecf20Sopenharmony_ci if (regmap_read(mc_vbase, priv->ecc_ctrl_offset, &read_reg) || 3118c2ecf20Sopenharmony_ci ((read_reg & priv->ecc_ctl_en_mask) != priv->ecc_ctl_en_mask)) { 3128c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 3138c2ecf20Sopenharmony_ci "No ECC/ECC disabled [0x%08X]\n", read_reg); 3148c2ecf20Sopenharmony_ci return -ENODEV; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* Grab memory size from device tree. */ 3188c2ecf20Sopenharmony_ci mem_size = get_total_mem(); 3198c2ecf20Sopenharmony_ci if (!mem_size) { 3208c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, "Unable to calculate memory size\n"); 3218c2ecf20Sopenharmony_ci return -ENODEV; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* Ensure the SDRAM Interrupt is disabled */ 3258c2ecf20Sopenharmony_ci if (regmap_update_bits(mc_vbase, priv->ecc_irq_en_offset, 3268c2ecf20Sopenharmony_ci priv->ecc_irq_en_mask, 0)) { 3278c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 3288c2ecf20Sopenharmony_ci "Error disabling SDRAM ECC IRQ\n"); 3298c2ecf20Sopenharmony_ci return -ENODEV; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* Toggle to clear the SDRAM Error count */ 3338c2ecf20Sopenharmony_ci if (regmap_update_bits(mc_vbase, priv->ecc_cnt_rst_offset, 3348c2ecf20Sopenharmony_ci priv->ecc_cnt_rst_mask, 3358c2ecf20Sopenharmony_ci priv->ecc_cnt_rst_mask)) { 3368c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 3378c2ecf20Sopenharmony_ci "Error clearing SDRAM ECC count\n"); 3388c2ecf20Sopenharmony_ci return -ENODEV; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (regmap_update_bits(mc_vbase, priv->ecc_cnt_rst_offset, 3428c2ecf20Sopenharmony_ci priv->ecc_cnt_rst_mask, 0)) { 3438c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 3448c2ecf20Sopenharmony_ci "Error clearing SDRAM ECC count\n"); 3458c2ecf20Sopenharmony_ci return -ENODEV; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 3498c2ecf20Sopenharmony_ci if (irq < 0) { 3508c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 3518c2ecf20Sopenharmony_ci "No irq %d in DT\n", irq); 3528c2ecf20Sopenharmony_ci return irq; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* Arria10 has a 2nd IRQ */ 3568c2ecf20Sopenharmony_ci irq2 = platform_get_irq(pdev, 1); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 3598c2ecf20Sopenharmony_ci layers[0].size = 1; 3608c2ecf20Sopenharmony_ci layers[0].is_virt_csrow = true; 3618c2ecf20Sopenharmony_ci layers[1].type = EDAC_MC_LAYER_CHANNEL; 3628c2ecf20Sopenharmony_ci layers[1].size = 1; 3638c2ecf20Sopenharmony_ci layers[1].is_virt_csrow = false; 3648c2ecf20Sopenharmony_ci mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 3658c2ecf20Sopenharmony_ci sizeof(struct altr_sdram_mc_data)); 3668c2ecf20Sopenharmony_ci if (!mci) 3678c2ecf20Sopenharmony_ci return -ENOMEM; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci mci->pdev = &pdev->dev; 3708c2ecf20Sopenharmony_ci drvdata = mci->pvt_info; 3718c2ecf20Sopenharmony_ci drvdata->mc_vbase = mc_vbase; 3728c2ecf20Sopenharmony_ci drvdata->data = priv; 3738c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mci); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) { 3768c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 3778c2ecf20Sopenharmony_ci "Unable to get managed device resource\n"); 3788c2ecf20Sopenharmony_ci res = -ENOMEM; 3798c2ecf20Sopenharmony_ci goto free; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci mci->mtype_cap = MEM_FLAG_DDR3; 3838c2ecf20Sopenharmony_ci mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; 3848c2ecf20Sopenharmony_ci mci->edac_cap = EDAC_FLAG_SECDED; 3858c2ecf20Sopenharmony_ci mci->mod_name = EDAC_MOD_STR; 3868c2ecf20Sopenharmony_ci mci->ctl_name = dev_name(&pdev->dev); 3878c2ecf20Sopenharmony_ci mci->scrub_mode = SCRUB_SW_SRC; 3888c2ecf20Sopenharmony_ci mci->dev_name = dev_name(&pdev->dev); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci dimm = *mci->dimms; 3918c2ecf20Sopenharmony_ci dimm->nr_pages = ((mem_size - 1) >> PAGE_SHIFT) + 1; 3928c2ecf20Sopenharmony_ci dimm->grain = 8; 3938c2ecf20Sopenharmony_ci dimm->dtype = DEV_X8; 3948c2ecf20Sopenharmony_ci dimm->mtype = MEM_DDR3; 3958c2ecf20Sopenharmony_ci dimm->edac_mode = EDAC_SECDED; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci res = edac_mc_add_mc(mci); 3988c2ecf20Sopenharmony_ci if (res < 0) 3998c2ecf20Sopenharmony_ci goto err; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* Only the Arria10 has separate IRQs */ 4028c2ecf20Sopenharmony_ci if (of_machine_is_compatible("altr,socfpga-arria10")) { 4038c2ecf20Sopenharmony_ci /* Arria10 specific initialization */ 4048c2ecf20Sopenharmony_ci res = a10_init(mc_vbase); 4058c2ecf20Sopenharmony_ci if (res < 0) 4068c2ecf20Sopenharmony_ci goto err2; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci res = devm_request_irq(&pdev->dev, irq2, 4098c2ecf20Sopenharmony_ci altr_sdram_mc_err_handler, 4108c2ecf20Sopenharmony_ci IRQF_SHARED, dev_name(&pdev->dev), mci); 4118c2ecf20Sopenharmony_ci if (res < 0) { 4128c2ecf20Sopenharmony_ci edac_mc_printk(mci, KERN_ERR, 4138c2ecf20Sopenharmony_ci "Unable to request irq %d\n", irq2); 4148c2ecf20Sopenharmony_ci res = -ENODEV; 4158c2ecf20Sopenharmony_ci goto err2; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci res = a10_unmask_irq(pdev, A10_DDR0_IRQ_MASK); 4198c2ecf20Sopenharmony_ci if (res < 0) 4208c2ecf20Sopenharmony_ci goto err2; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci irqflags = IRQF_SHARED; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci res = devm_request_irq(&pdev->dev, irq, altr_sdram_mc_err_handler, 4268c2ecf20Sopenharmony_ci irqflags, dev_name(&pdev->dev), mci); 4278c2ecf20Sopenharmony_ci if (res < 0) { 4288c2ecf20Sopenharmony_ci edac_mc_printk(mci, KERN_ERR, 4298c2ecf20Sopenharmony_ci "Unable to request irq %d\n", irq); 4308c2ecf20Sopenharmony_ci res = -ENODEV; 4318c2ecf20Sopenharmony_ci goto err2; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* Infrastructure ready - enable the IRQ */ 4358c2ecf20Sopenharmony_ci if (regmap_update_bits(drvdata->mc_vbase, priv->ecc_irq_en_offset, 4368c2ecf20Sopenharmony_ci priv->ecc_irq_en_mask, priv->ecc_irq_en_mask)) { 4378c2ecf20Sopenharmony_ci edac_mc_printk(mci, KERN_ERR, 4388c2ecf20Sopenharmony_ci "Error enabling SDRAM ECC IRQ\n"); 4398c2ecf20Sopenharmony_ci res = -ENODEV; 4408c2ecf20Sopenharmony_ci goto err2; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci altr_sdr_mc_create_debugfs_nodes(mci); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci devres_close_group(&pdev->dev, NULL); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cierr2: 4508c2ecf20Sopenharmony_ci edac_mc_del_mc(&pdev->dev); 4518c2ecf20Sopenharmony_cierr: 4528c2ecf20Sopenharmony_ci devres_release_group(&pdev->dev, NULL); 4538c2ecf20Sopenharmony_cifree: 4548c2ecf20Sopenharmony_ci edac_mc_free(mci); 4558c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MC, 4568c2ecf20Sopenharmony_ci "EDAC Probe Failed; Error %d\n", res); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci return res; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic int altr_sdram_remove(struct platform_device *pdev) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct mem_ctl_info *mci = platform_get_drvdata(pdev); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci edac_mc_del_mc(&pdev->dev); 4668c2ecf20Sopenharmony_ci edac_mc_free(mci); 4678c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, NULL); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci/* 4738c2ecf20Sopenharmony_ci * If you want to suspend, need to disable EDAC by removing it 4748c2ecf20Sopenharmony_ci * from the device tree or defconfig. 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4778c2ecf20Sopenharmony_cistatic int altr_sdram_prepare(struct device *dev) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci pr_err("Suspend not allowed when EDAC is enabled.\n"); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return -EPERM; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic const struct dev_pm_ops altr_sdram_pm_ops = { 4858c2ecf20Sopenharmony_ci .prepare = altr_sdram_prepare, 4868c2ecf20Sopenharmony_ci}; 4878c2ecf20Sopenharmony_ci#endif 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic struct platform_driver altr_sdram_edac_driver = { 4908c2ecf20Sopenharmony_ci .probe = altr_sdram_probe, 4918c2ecf20Sopenharmony_ci .remove = altr_sdram_remove, 4928c2ecf20Sopenharmony_ci .driver = { 4938c2ecf20Sopenharmony_ci .name = "altr_sdram_edac", 4948c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4958c2ecf20Sopenharmony_ci .pm = &altr_sdram_pm_ops, 4968c2ecf20Sopenharmony_ci#endif 4978c2ecf20Sopenharmony_ci .of_match_table = altr_sdram_ctrl_of_match, 4988c2ecf20Sopenharmony_ci }, 4998c2ecf20Sopenharmony_ci}; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cimodule_platform_driver(altr_sdram_edac_driver); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci#endif /* CONFIG_EDAC_ALTERA_SDRAM */ 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci/************************* EDAC Parent Probe *************************/ 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic const struct of_device_id altr_edac_device_of_match[]; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic const struct of_device_id altr_edac_of_match[] = { 5108c2ecf20Sopenharmony_ci { .compatible = "altr,socfpga-ecc-manager" }, 5118c2ecf20Sopenharmony_ci {}, 5128c2ecf20Sopenharmony_ci}; 5138c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, altr_edac_of_match); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic int altr_edac_probe(struct platform_device *pdev) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci of_platform_populate(pdev->dev.of_node, altr_edac_device_of_match, 5188c2ecf20Sopenharmony_ci NULL, &pdev->dev); 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic struct platform_driver altr_edac_driver = { 5238c2ecf20Sopenharmony_ci .probe = altr_edac_probe, 5248c2ecf20Sopenharmony_ci .driver = { 5258c2ecf20Sopenharmony_ci .name = "socfpga_ecc_manager", 5268c2ecf20Sopenharmony_ci .of_match_table = altr_edac_of_match, 5278c2ecf20Sopenharmony_ci }, 5288c2ecf20Sopenharmony_ci}; 5298c2ecf20Sopenharmony_cimodule_platform_driver(altr_edac_driver); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci/************************* EDAC Device Functions *************************/ 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci/* 5348c2ecf20Sopenharmony_ci * EDAC Device Functions (shared between various IPs). 5358c2ecf20Sopenharmony_ci * The discrete memories use the EDAC Device framework. The probe 5368c2ecf20Sopenharmony_ci * and error handling functions are very similar between memories 5378c2ecf20Sopenharmony_ci * so they are shared. The memory allocation and freeing for EDAC 5388c2ecf20Sopenharmony_ci * trigger testing are different for each memory. 5398c2ecf20Sopenharmony_ci */ 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data ocramecc_data; 5428c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data l2ecc_data; 5438c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data a10_ocramecc_data; 5448c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data a10_l2ecc_data; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic irqreturn_t altr_edac_device_handler(int irq, void *dev_id) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci irqreturn_t ret_value = IRQ_NONE; 5498c2ecf20Sopenharmony_ci struct edac_device_ctl_info *dci = dev_id; 5508c2ecf20Sopenharmony_ci struct altr_edac_device_dev *drvdata = dci->pvt_info; 5518c2ecf20Sopenharmony_ci const struct edac_device_prv_data *priv = drvdata->data; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (irq == drvdata->sb_irq) { 5548c2ecf20Sopenharmony_ci if (priv->ce_clear_mask) 5558c2ecf20Sopenharmony_ci writel(priv->ce_clear_mask, drvdata->base); 5568c2ecf20Sopenharmony_ci edac_device_handle_ce(dci, 0, 0, drvdata->edac_dev_name); 5578c2ecf20Sopenharmony_ci ret_value = IRQ_HANDLED; 5588c2ecf20Sopenharmony_ci } else if (irq == drvdata->db_irq) { 5598c2ecf20Sopenharmony_ci if (priv->ue_clear_mask) 5608c2ecf20Sopenharmony_ci writel(priv->ue_clear_mask, drvdata->base); 5618c2ecf20Sopenharmony_ci edac_device_handle_ue(dci, 0, 0, drvdata->edac_dev_name); 5628c2ecf20Sopenharmony_ci panic("\nEDAC:ECC_DEVICE[Uncorrectable errors]\n"); 5638c2ecf20Sopenharmony_ci ret_value = IRQ_HANDLED; 5648c2ecf20Sopenharmony_ci } else { 5658c2ecf20Sopenharmony_ci WARN_ON(1); 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci return ret_value; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic ssize_t altr_edac_device_trig(struct file *file, 5728c2ecf20Sopenharmony_ci const char __user *user_buf, 5738c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci u32 *ptemp, i, error_mask; 5778c2ecf20Sopenharmony_ci int result = 0; 5788c2ecf20Sopenharmony_ci u8 trig_type; 5798c2ecf20Sopenharmony_ci unsigned long flags; 5808c2ecf20Sopenharmony_ci struct edac_device_ctl_info *edac_dci = file->private_data; 5818c2ecf20Sopenharmony_ci struct altr_edac_device_dev *drvdata = edac_dci->pvt_info; 5828c2ecf20Sopenharmony_ci const struct edac_device_prv_data *priv = drvdata->data; 5838c2ecf20Sopenharmony_ci void *generic_ptr = edac_dci->dev; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (!user_buf || get_user(trig_type, user_buf)) 5868c2ecf20Sopenharmony_ci return -EFAULT; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (!priv->alloc_mem) 5898c2ecf20Sopenharmony_ci return -ENOMEM; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci /* 5928c2ecf20Sopenharmony_ci * Note that generic_ptr is initialized to the device * but in 5938c2ecf20Sopenharmony_ci * some alloc_functions, this is overridden and returns data. 5948c2ecf20Sopenharmony_ci */ 5958c2ecf20Sopenharmony_ci ptemp = priv->alloc_mem(priv->trig_alloc_sz, &generic_ptr); 5968c2ecf20Sopenharmony_ci if (!ptemp) { 5978c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 5988c2ecf20Sopenharmony_ci "Inject: Buffer Allocation error\n"); 5998c2ecf20Sopenharmony_ci return -ENOMEM; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (trig_type == ALTR_UE_TRIGGER_CHAR) 6038c2ecf20Sopenharmony_ci error_mask = priv->ue_set_mask; 6048c2ecf20Sopenharmony_ci else 6058c2ecf20Sopenharmony_ci error_mask = priv->ce_set_mask; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci edac_printk(KERN_ALERT, EDAC_DEVICE, 6088c2ecf20Sopenharmony_ci "Trigger Error Mask (0x%X)\n", error_mask); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci local_irq_save(flags); 6118c2ecf20Sopenharmony_ci /* write ECC corrupted data out. */ 6128c2ecf20Sopenharmony_ci for (i = 0; i < (priv->trig_alloc_sz / sizeof(*ptemp)); i++) { 6138c2ecf20Sopenharmony_ci /* Read data so we're in the correct state */ 6148c2ecf20Sopenharmony_ci rmb(); 6158c2ecf20Sopenharmony_ci if (READ_ONCE(ptemp[i])) 6168c2ecf20Sopenharmony_ci result = -1; 6178c2ecf20Sopenharmony_ci /* Toggle Error bit (it is latched), leave ECC enabled */ 6188c2ecf20Sopenharmony_ci writel(error_mask, (drvdata->base + priv->set_err_ofst)); 6198c2ecf20Sopenharmony_ci writel(priv->ecc_enable_mask, (drvdata->base + 6208c2ecf20Sopenharmony_ci priv->set_err_ofst)); 6218c2ecf20Sopenharmony_ci ptemp[i] = i; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci /* Ensure it has been written out */ 6248c2ecf20Sopenharmony_ci wmb(); 6258c2ecf20Sopenharmony_ci local_irq_restore(flags); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (result) 6288c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, "Mem Not Cleared\n"); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci /* Read out written data. ECC error caused here */ 6318c2ecf20Sopenharmony_ci for (i = 0; i < ALTR_TRIGGER_READ_WRD_CNT; i++) 6328c2ecf20Sopenharmony_ci if (READ_ONCE(ptemp[i]) != i) 6338c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 6348c2ecf20Sopenharmony_ci "Read doesn't match written data\n"); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci if (priv->free_mem) 6378c2ecf20Sopenharmony_ci priv->free_mem(ptemp, priv->trig_alloc_sz, generic_ptr); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci return count; 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic const struct file_operations altr_edac_device_inject_fops = { 6438c2ecf20Sopenharmony_ci .open = simple_open, 6448c2ecf20Sopenharmony_ci .write = altr_edac_device_trig, 6458c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 6468c2ecf20Sopenharmony_ci}; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic ssize_t altr_edac_a10_device_trig(struct file *file, 6498c2ecf20Sopenharmony_ci const char __user *user_buf, 6508c2ecf20Sopenharmony_ci size_t count, loff_t *ppos); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic const struct file_operations altr_edac_a10_device_inject_fops = { 6538c2ecf20Sopenharmony_ci .open = simple_open, 6548c2ecf20Sopenharmony_ci .write = altr_edac_a10_device_trig, 6558c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 6568c2ecf20Sopenharmony_ci}; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic ssize_t altr_edac_a10_device_trig2(struct file *file, 6598c2ecf20Sopenharmony_ci const char __user *user_buf, 6608c2ecf20Sopenharmony_ci size_t count, loff_t *ppos); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cistatic const struct file_operations altr_edac_a10_device_inject2_fops = { 6638c2ecf20Sopenharmony_ci .open = simple_open, 6648c2ecf20Sopenharmony_ci .write = altr_edac_a10_device_trig2, 6658c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 6668c2ecf20Sopenharmony_ci}; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic void altr_create_edacdev_dbgfs(struct edac_device_ctl_info *edac_dci, 6698c2ecf20Sopenharmony_ci const struct edac_device_prv_data *priv) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci struct altr_edac_device_dev *drvdata = edac_dci->pvt_info; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_EDAC_DEBUG)) 6748c2ecf20Sopenharmony_ci return; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci drvdata->debugfs_dir = edac_debugfs_create_dir(drvdata->edac_dev_name); 6778c2ecf20Sopenharmony_ci if (!drvdata->debugfs_dir) 6788c2ecf20Sopenharmony_ci return; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (!edac_debugfs_create_file("altr_trigger", S_IWUSR, 6818c2ecf20Sopenharmony_ci drvdata->debugfs_dir, edac_dci, 6828c2ecf20Sopenharmony_ci priv->inject_fops)) 6838c2ecf20Sopenharmony_ci debugfs_remove_recursive(drvdata->debugfs_dir); 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic const struct of_device_id altr_edac_device_of_match[] = { 6878c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_L2C 6888c2ecf20Sopenharmony_ci { .compatible = "altr,socfpga-l2-ecc", .data = &l2ecc_data }, 6898c2ecf20Sopenharmony_ci#endif 6908c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_OCRAM 6918c2ecf20Sopenharmony_ci { .compatible = "altr,socfpga-ocram-ecc", .data = &ocramecc_data }, 6928c2ecf20Sopenharmony_ci#endif 6938c2ecf20Sopenharmony_ci {}, 6948c2ecf20Sopenharmony_ci}; 6958c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, altr_edac_device_of_match); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci/* 6988c2ecf20Sopenharmony_ci * altr_edac_device_probe() 6998c2ecf20Sopenharmony_ci * This is a generic EDAC device driver that will support 7008c2ecf20Sopenharmony_ci * various Altera memory devices such as the L2 cache ECC and 7018c2ecf20Sopenharmony_ci * OCRAM ECC as well as the memories for other peripherals. 7028c2ecf20Sopenharmony_ci * Module specific initialization is done by passing the 7038c2ecf20Sopenharmony_ci * function index in the device tree. 7048c2ecf20Sopenharmony_ci */ 7058c2ecf20Sopenharmony_cistatic int altr_edac_device_probe(struct platform_device *pdev) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci struct edac_device_ctl_info *dci; 7088c2ecf20Sopenharmony_ci struct altr_edac_device_dev *drvdata; 7098c2ecf20Sopenharmony_ci struct resource *r; 7108c2ecf20Sopenharmony_ci int res = 0; 7118c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 7128c2ecf20Sopenharmony_ci char *ecc_name = (char *)np->name; 7138c2ecf20Sopenharmony_ci static int dev_instance; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) { 7168c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 7178c2ecf20Sopenharmony_ci "Unable to open devm\n"); 7188c2ecf20Sopenharmony_ci return -ENOMEM; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 7228c2ecf20Sopenharmony_ci if (!r) { 7238c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 7248c2ecf20Sopenharmony_ci "Unable to get mem resource\n"); 7258c2ecf20Sopenharmony_ci res = -ENODEV; 7268c2ecf20Sopenharmony_ci goto fail; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (!devm_request_mem_region(&pdev->dev, r->start, resource_size(r), 7308c2ecf20Sopenharmony_ci dev_name(&pdev->dev))) { 7318c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 7328c2ecf20Sopenharmony_ci "%s:Error requesting mem region\n", ecc_name); 7338c2ecf20Sopenharmony_ci res = -EBUSY; 7348c2ecf20Sopenharmony_ci goto fail; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci dci = edac_device_alloc_ctl_info(sizeof(*drvdata), ecc_name, 7388c2ecf20Sopenharmony_ci 1, ecc_name, 1, 0, NULL, 0, 7398c2ecf20Sopenharmony_ci dev_instance++); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (!dci) { 7428c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 7438c2ecf20Sopenharmony_ci "%s: Unable to allocate EDAC device\n", ecc_name); 7448c2ecf20Sopenharmony_ci res = -ENOMEM; 7458c2ecf20Sopenharmony_ci goto fail; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci drvdata = dci->pvt_info; 7498c2ecf20Sopenharmony_ci dci->dev = &pdev->dev; 7508c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dci); 7518c2ecf20Sopenharmony_ci drvdata->edac_dev_name = ecc_name; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r)); 7548c2ecf20Sopenharmony_ci if (!drvdata->base) { 7558c2ecf20Sopenharmony_ci res = -ENOMEM; 7568c2ecf20Sopenharmony_ci goto fail1; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci /* Get driver specific data for this EDAC device */ 7608c2ecf20Sopenharmony_ci drvdata->data = of_match_node(altr_edac_device_of_match, np)->data; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci /* Check specific dependencies for the module */ 7638c2ecf20Sopenharmony_ci if (drvdata->data->setup) { 7648c2ecf20Sopenharmony_ci res = drvdata->data->setup(drvdata); 7658c2ecf20Sopenharmony_ci if (res) 7668c2ecf20Sopenharmony_ci goto fail1; 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci drvdata->sb_irq = platform_get_irq(pdev, 0); 7708c2ecf20Sopenharmony_ci res = devm_request_irq(&pdev->dev, drvdata->sb_irq, 7718c2ecf20Sopenharmony_ci altr_edac_device_handler, 7728c2ecf20Sopenharmony_ci 0, dev_name(&pdev->dev), dci); 7738c2ecf20Sopenharmony_ci if (res) 7748c2ecf20Sopenharmony_ci goto fail1; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci drvdata->db_irq = platform_get_irq(pdev, 1); 7778c2ecf20Sopenharmony_ci res = devm_request_irq(&pdev->dev, drvdata->db_irq, 7788c2ecf20Sopenharmony_ci altr_edac_device_handler, 7798c2ecf20Sopenharmony_ci 0, dev_name(&pdev->dev), dci); 7808c2ecf20Sopenharmony_ci if (res) 7818c2ecf20Sopenharmony_ci goto fail1; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci dci->mod_name = "Altera ECC Manager"; 7848c2ecf20Sopenharmony_ci dci->dev_name = drvdata->edac_dev_name; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci res = edac_device_add_device(dci); 7878c2ecf20Sopenharmony_ci if (res) 7888c2ecf20Sopenharmony_ci goto fail1; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci altr_create_edacdev_dbgfs(dci, drvdata->data); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci devres_close_group(&pdev->dev, NULL); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci return 0; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_cifail1: 7978c2ecf20Sopenharmony_ci edac_device_free_ctl_info(dci); 7988c2ecf20Sopenharmony_cifail: 7998c2ecf20Sopenharmony_ci devres_release_group(&pdev->dev, NULL); 8008c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 8018c2ecf20Sopenharmony_ci "%s:Error setting up EDAC device: %d\n", ecc_name, res); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci return res; 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cistatic int altr_edac_device_remove(struct platform_device *pdev) 8078c2ecf20Sopenharmony_ci{ 8088c2ecf20Sopenharmony_ci struct edac_device_ctl_info *dci = platform_get_drvdata(pdev); 8098c2ecf20Sopenharmony_ci struct altr_edac_device_dev *drvdata = dci->pvt_info; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci debugfs_remove_recursive(drvdata->debugfs_dir); 8128c2ecf20Sopenharmony_ci edac_device_del_device(&pdev->dev); 8138c2ecf20Sopenharmony_ci edac_device_free_ctl_info(dci); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci return 0; 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_cistatic struct platform_driver altr_edac_device_driver = { 8198c2ecf20Sopenharmony_ci .probe = altr_edac_device_probe, 8208c2ecf20Sopenharmony_ci .remove = altr_edac_device_remove, 8218c2ecf20Sopenharmony_ci .driver = { 8228c2ecf20Sopenharmony_ci .name = "altr_edac_device", 8238c2ecf20Sopenharmony_ci .of_match_table = altr_edac_device_of_match, 8248c2ecf20Sopenharmony_ci }, 8258c2ecf20Sopenharmony_ci}; 8268c2ecf20Sopenharmony_cimodule_platform_driver(altr_edac_device_driver); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci/******************* Arria10 Device ECC Shared Functions *****************/ 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci/* 8318c2ecf20Sopenharmony_ci * Test for memory's ECC dependencies upon entry because platform specific 8328c2ecf20Sopenharmony_ci * startup should have initialized the memory and enabled the ECC. 8338c2ecf20Sopenharmony_ci * Can't turn on ECC here because accessing un-initialized memory will 8348c2ecf20Sopenharmony_ci * cause CE/UE errors possibly causing an ABORT. 8358c2ecf20Sopenharmony_ci */ 8368c2ecf20Sopenharmony_cistatic int __maybe_unused 8378c2ecf20Sopenharmony_cialtr_check_ecc_deps(struct altr_edac_device_dev *device) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci void __iomem *base = device->base; 8408c2ecf20Sopenharmony_ci const struct edac_device_prv_data *prv = device->data; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if (readl(base + prv->ecc_en_ofst) & prv->ecc_enable_mask) 8438c2ecf20Sopenharmony_ci return 0; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 8468c2ecf20Sopenharmony_ci "%s: No ECC present or ECC disabled.\n", 8478c2ecf20Sopenharmony_ci device->edac_dev_name); 8488c2ecf20Sopenharmony_ci return -ENODEV; 8498c2ecf20Sopenharmony_ci} 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_cistatic irqreturn_t __maybe_unused altr_edac_a10_ecc_irq(int irq, void *dev_id) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci struct altr_edac_device_dev *dci = dev_id; 8548c2ecf20Sopenharmony_ci void __iomem *base = dci->base; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci if (irq == dci->sb_irq) { 8578c2ecf20Sopenharmony_ci writel(ALTR_A10_ECC_SERRPENA, 8588c2ecf20Sopenharmony_ci base + ALTR_A10_ECC_INTSTAT_OFST); 8598c2ecf20Sopenharmony_ci edac_device_handle_ce(dci->edac_dev, 0, 0, dci->edac_dev_name); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci return IRQ_HANDLED; 8628c2ecf20Sopenharmony_ci } else if (irq == dci->db_irq) { 8638c2ecf20Sopenharmony_ci writel(ALTR_A10_ECC_DERRPENA, 8648c2ecf20Sopenharmony_ci base + ALTR_A10_ECC_INTSTAT_OFST); 8658c2ecf20Sopenharmony_ci edac_device_handle_ue(dci->edac_dev, 0, 0, dci->edac_dev_name); 8668c2ecf20Sopenharmony_ci if (dci->data->panic) 8678c2ecf20Sopenharmony_ci panic("\nEDAC:ECC_DEVICE[Uncorrectable errors]\n"); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci return IRQ_HANDLED; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci WARN_ON(1); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci return IRQ_NONE; 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci/******************* Arria10 Memory Buffer Functions *********************/ 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_cistatic inline int a10_get_irq_mask(struct device_node *np) 8808c2ecf20Sopenharmony_ci{ 8818c2ecf20Sopenharmony_ci int irq; 8828c2ecf20Sopenharmony_ci const u32 *handle = of_get_property(np, "interrupts", NULL); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci if (!handle) 8858c2ecf20Sopenharmony_ci return -ENODEV; 8868c2ecf20Sopenharmony_ci irq = be32_to_cpup(handle); 8878c2ecf20Sopenharmony_ci return irq; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic inline void ecc_set_bits(u32 bit_mask, void __iomem *ioaddr) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci u32 value = readl(ioaddr); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci value |= bit_mask; 8958c2ecf20Sopenharmony_ci writel(value, ioaddr); 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic inline void ecc_clear_bits(u32 bit_mask, void __iomem *ioaddr) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci u32 value = readl(ioaddr); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci value &= ~bit_mask; 9038c2ecf20Sopenharmony_ci writel(value, ioaddr); 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_cistatic inline int ecc_test_bits(u32 bit_mask, void __iomem *ioaddr) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci u32 value = readl(ioaddr); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci return (value & bit_mask) ? 1 : 0; 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci/* 9148c2ecf20Sopenharmony_ci * This function uses the memory initialization block in the Arria10 ECC 9158c2ecf20Sopenharmony_ci * controller to initialize/clear the entire memory data and ECC data. 9168c2ecf20Sopenharmony_ci */ 9178c2ecf20Sopenharmony_cistatic int __maybe_unused altr_init_memory_port(void __iomem *ioaddr, int port) 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci int limit = ALTR_A10_ECC_INIT_WATCHDOG_10US; 9208c2ecf20Sopenharmony_ci u32 init_mask, stat_mask, clear_mask; 9218c2ecf20Sopenharmony_ci int ret = 0; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if (port) { 9248c2ecf20Sopenharmony_ci init_mask = ALTR_A10_ECC_INITB; 9258c2ecf20Sopenharmony_ci stat_mask = ALTR_A10_ECC_INITCOMPLETEB; 9268c2ecf20Sopenharmony_ci clear_mask = ALTR_A10_ECC_ERRPENB_MASK; 9278c2ecf20Sopenharmony_ci } else { 9288c2ecf20Sopenharmony_ci init_mask = ALTR_A10_ECC_INITA; 9298c2ecf20Sopenharmony_ci stat_mask = ALTR_A10_ECC_INITCOMPLETEA; 9308c2ecf20Sopenharmony_ci clear_mask = ALTR_A10_ECC_ERRPENA_MASK; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci ecc_set_bits(init_mask, (ioaddr + ALTR_A10_ECC_CTRL_OFST)); 9348c2ecf20Sopenharmony_ci while (limit--) { 9358c2ecf20Sopenharmony_ci if (ecc_test_bits(stat_mask, 9368c2ecf20Sopenharmony_ci (ioaddr + ALTR_A10_ECC_INITSTAT_OFST))) 9378c2ecf20Sopenharmony_ci break; 9388c2ecf20Sopenharmony_ci udelay(1); 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci if (limit < 0) 9418c2ecf20Sopenharmony_ci ret = -EBUSY; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci /* Clear any pending ECC interrupts */ 9448c2ecf20Sopenharmony_ci writel(clear_mask, (ioaddr + ALTR_A10_ECC_INTSTAT_OFST)); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci return ret; 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cistatic __init int __maybe_unused 9508c2ecf20Sopenharmony_cialtr_init_a10_ecc_block(struct device_node *np, u32 irq_mask, 9518c2ecf20Sopenharmony_ci u32 ecc_ctrl_en_mask, bool dual_port) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci int ret = 0; 9548c2ecf20Sopenharmony_ci void __iomem *ecc_block_base; 9558c2ecf20Sopenharmony_ci struct regmap *ecc_mgr_map; 9568c2ecf20Sopenharmony_ci char *ecc_name; 9578c2ecf20Sopenharmony_ci struct device_node *np_eccmgr; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci ecc_name = (char *)np->name; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci /* Get the ECC Manager - parent of the device EDACs */ 9628c2ecf20Sopenharmony_ci np_eccmgr = of_get_parent(np); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci ecc_mgr_map = 9658c2ecf20Sopenharmony_ci altr_sysmgr_regmap_lookup_by_phandle(np_eccmgr, 9668c2ecf20Sopenharmony_ci "altr,sysmgr-syscon"); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci of_node_put(np_eccmgr); 9698c2ecf20Sopenharmony_ci if (IS_ERR(ecc_mgr_map)) { 9708c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 9718c2ecf20Sopenharmony_ci "Unable to get syscon altr,sysmgr-syscon\n"); 9728c2ecf20Sopenharmony_ci return -ENODEV; 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci /* Map the ECC Block */ 9768c2ecf20Sopenharmony_ci ecc_block_base = of_iomap(np, 0); 9778c2ecf20Sopenharmony_ci if (!ecc_block_base) { 9788c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 9798c2ecf20Sopenharmony_ci "Unable to map %s ECC block\n", ecc_name); 9808c2ecf20Sopenharmony_ci return -ENODEV; 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci /* Disable ECC */ 9848c2ecf20Sopenharmony_ci regmap_write(ecc_mgr_map, A10_SYSMGR_ECC_INTMASK_SET_OFST, irq_mask); 9858c2ecf20Sopenharmony_ci writel(ALTR_A10_ECC_SERRINTEN, 9868c2ecf20Sopenharmony_ci (ecc_block_base + ALTR_A10_ECC_ERRINTENR_OFST)); 9878c2ecf20Sopenharmony_ci ecc_clear_bits(ecc_ctrl_en_mask, 9888c2ecf20Sopenharmony_ci (ecc_block_base + ALTR_A10_ECC_CTRL_OFST)); 9898c2ecf20Sopenharmony_ci /* Ensure all writes complete */ 9908c2ecf20Sopenharmony_ci wmb(); 9918c2ecf20Sopenharmony_ci /* Use HW initialization block to initialize memory for ECC */ 9928c2ecf20Sopenharmony_ci ret = altr_init_memory_port(ecc_block_base, 0); 9938c2ecf20Sopenharmony_ci if (ret) { 9948c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 9958c2ecf20Sopenharmony_ci "ECC: cannot init %s PORTA memory\n", ecc_name); 9968c2ecf20Sopenharmony_ci goto out; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (dual_port) { 10008c2ecf20Sopenharmony_ci ret = altr_init_memory_port(ecc_block_base, 1); 10018c2ecf20Sopenharmony_ci if (ret) { 10028c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 10038c2ecf20Sopenharmony_ci "ECC: cannot init %s PORTB memory\n", 10048c2ecf20Sopenharmony_ci ecc_name); 10058c2ecf20Sopenharmony_ci goto out; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci /* Interrupt mode set to every SBERR */ 10108c2ecf20Sopenharmony_ci regmap_write(ecc_mgr_map, ALTR_A10_ECC_INTMODE_OFST, 10118c2ecf20Sopenharmony_ci ALTR_A10_ECC_INTMODE); 10128c2ecf20Sopenharmony_ci /* Enable ECC */ 10138c2ecf20Sopenharmony_ci ecc_set_bits(ecc_ctrl_en_mask, (ecc_block_base + 10148c2ecf20Sopenharmony_ci ALTR_A10_ECC_CTRL_OFST)); 10158c2ecf20Sopenharmony_ci writel(ALTR_A10_ECC_SERRINTEN, 10168c2ecf20Sopenharmony_ci (ecc_block_base + ALTR_A10_ECC_ERRINTENS_OFST)); 10178c2ecf20Sopenharmony_ci regmap_write(ecc_mgr_map, A10_SYSMGR_ECC_INTMASK_CLR_OFST, irq_mask); 10188c2ecf20Sopenharmony_ci /* Ensure all writes complete */ 10198c2ecf20Sopenharmony_ci wmb(); 10208c2ecf20Sopenharmony_ciout: 10218c2ecf20Sopenharmony_ci iounmap(ecc_block_base); 10228c2ecf20Sopenharmony_ci return ret; 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic int validate_parent_available(struct device_node *np); 10268c2ecf20Sopenharmony_cistatic const struct of_device_id altr_edac_a10_device_of_match[]; 10278c2ecf20Sopenharmony_cistatic int __init __maybe_unused altr_init_a10_ecc_device_type(char *compat) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci int irq; 10308c2ecf20Sopenharmony_ci struct device_node *child, *np; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, 10338c2ecf20Sopenharmony_ci "altr,socfpga-a10-ecc-manager"); 10348c2ecf20Sopenharmony_ci if (!np) { 10358c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, "ECC Manager not found\n"); 10368c2ecf20Sopenharmony_ci return -ENODEV; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci for_each_child_of_node(np, child) { 10408c2ecf20Sopenharmony_ci const struct of_device_id *pdev_id; 10418c2ecf20Sopenharmony_ci const struct edac_device_prv_data *prv; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci if (!of_device_is_available(child)) 10448c2ecf20Sopenharmony_ci continue; 10458c2ecf20Sopenharmony_ci if (!of_device_is_compatible(child, compat)) 10468c2ecf20Sopenharmony_ci continue; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci if (validate_parent_available(child)) 10498c2ecf20Sopenharmony_ci continue; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci irq = a10_get_irq_mask(child); 10528c2ecf20Sopenharmony_ci if (irq < 0) 10538c2ecf20Sopenharmony_ci continue; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci /* Get matching node and check for valid result */ 10568c2ecf20Sopenharmony_ci pdev_id = of_match_node(altr_edac_a10_device_of_match, child); 10578c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(pdev_id)) 10588c2ecf20Sopenharmony_ci continue; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci /* Validate private data pointer before dereferencing */ 10618c2ecf20Sopenharmony_ci prv = pdev_id->data; 10628c2ecf20Sopenharmony_ci if (!prv) 10638c2ecf20Sopenharmony_ci continue; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci altr_init_a10_ecc_block(child, BIT(irq), 10668c2ecf20Sopenharmony_ci prv->ecc_enable_mask, 0); 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci of_node_put(np); 10708c2ecf20Sopenharmony_ci return 0; 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci/*********************** SDRAM EDAC Device Functions *********************/ 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_SDRAM 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data s10_sdramecc_data = { 10788c2ecf20Sopenharmony_ci .setup = altr_check_ecc_deps, 10798c2ecf20Sopenharmony_ci .ce_clear_mask = ALTR_S10_ECC_SERRPENA, 10808c2ecf20Sopenharmony_ci .ue_clear_mask = ALTR_S10_ECC_DERRPENA, 10818c2ecf20Sopenharmony_ci .ecc_enable_mask = ALTR_S10_ECC_EN, 10828c2ecf20Sopenharmony_ci .ecc_en_ofst = ALTR_S10_ECC_CTRL_SDRAM_OFST, 10838c2ecf20Sopenharmony_ci .ce_set_mask = ALTR_S10_ECC_TSERRA, 10848c2ecf20Sopenharmony_ci .ue_set_mask = ALTR_S10_ECC_TDERRA, 10858c2ecf20Sopenharmony_ci .set_err_ofst = ALTR_S10_ECC_INTTEST_OFST, 10868c2ecf20Sopenharmony_ci .ecc_irq_handler = altr_edac_a10_ecc_irq, 10878c2ecf20Sopenharmony_ci .inject_fops = &altr_edac_a10_device_inject_fops, 10888c2ecf20Sopenharmony_ci}; 10898c2ecf20Sopenharmony_ci#endif /* CONFIG_EDAC_ALTERA_SDRAM */ 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci/*********************** OCRAM EDAC Device Functions *********************/ 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_OCRAM 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic void *ocram_alloc_mem(size_t size, void **other) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci struct device_node *np; 10988c2ecf20Sopenharmony_ci struct gen_pool *gp; 10998c2ecf20Sopenharmony_ci void *sram_addr; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "altr,socfpga-ocram-ecc"); 11028c2ecf20Sopenharmony_ci if (!np) 11038c2ecf20Sopenharmony_ci return NULL; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci gp = of_gen_pool_get(np, "iram", 0); 11068c2ecf20Sopenharmony_ci of_node_put(np); 11078c2ecf20Sopenharmony_ci if (!gp) 11088c2ecf20Sopenharmony_ci return NULL; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci sram_addr = (void *)gen_pool_alloc(gp, size); 11118c2ecf20Sopenharmony_ci if (!sram_addr) 11128c2ecf20Sopenharmony_ci return NULL; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci memset(sram_addr, 0, size); 11158c2ecf20Sopenharmony_ci /* Ensure data is written out */ 11168c2ecf20Sopenharmony_ci wmb(); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci /* Remember this handle for freeing later */ 11198c2ecf20Sopenharmony_ci *other = gp; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci return sram_addr; 11228c2ecf20Sopenharmony_ci} 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_cistatic void ocram_free_mem(void *p, size_t size, void *other) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci gen_pool_free((struct gen_pool *)other, (unsigned long)p, size); 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data ocramecc_data = { 11308c2ecf20Sopenharmony_ci .setup = altr_check_ecc_deps, 11318c2ecf20Sopenharmony_ci .ce_clear_mask = (ALTR_OCR_ECC_EN | ALTR_OCR_ECC_SERR), 11328c2ecf20Sopenharmony_ci .ue_clear_mask = (ALTR_OCR_ECC_EN | ALTR_OCR_ECC_DERR), 11338c2ecf20Sopenharmony_ci .alloc_mem = ocram_alloc_mem, 11348c2ecf20Sopenharmony_ci .free_mem = ocram_free_mem, 11358c2ecf20Sopenharmony_ci .ecc_enable_mask = ALTR_OCR_ECC_EN, 11368c2ecf20Sopenharmony_ci .ecc_en_ofst = ALTR_OCR_ECC_REG_OFFSET, 11378c2ecf20Sopenharmony_ci .ce_set_mask = (ALTR_OCR_ECC_EN | ALTR_OCR_ECC_INJS), 11388c2ecf20Sopenharmony_ci .ue_set_mask = (ALTR_OCR_ECC_EN | ALTR_OCR_ECC_INJD), 11398c2ecf20Sopenharmony_ci .set_err_ofst = ALTR_OCR_ECC_REG_OFFSET, 11408c2ecf20Sopenharmony_ci .trig_alloc_sz = ALTR_TRIG_OCRAM_BYTE_SIZE, 11418c2ecf20Sopenharmony_ci .inject_fops = &altr_edac_device_inject_fops, 11428c2ecf20Sopenharmony_ci}; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_cistatic int __maybe_unused 11458c2ecf20Sopenharmony_cialtr_check_ocram_deps_init(struct altr_edac_device_dev *device) 11468c2ecf20Sopenharmony_ci{ 11478c2ecf20Sopenharmony_ci void __iomem *base = device->base; 11488c2ecf20Sopenharmony_ci int ret; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci ret = altr_check_ecc_deps(device); 11518c2ecf20Sopenharmony_ci if (ret) 11528c2ecf20Sopenharmony_ci return ret; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci /* Verify OCRAM has been initialized */ 11558c2ecf20Sopenharmony_ci if (!ecc_test_bits(ALTR_A10_ECC_INITCOMPLETEA, 11568c2ecf20Sopenharmony_ci (base + ALTR_A10_ECC_INITSTAT_OFST))) 11578c2ecf20Sopenharmony_ci return -ENODEV; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci /* Enable IRQ on Single Bit Error */ 11608c2ecf20Sopenharmony_ci writel(ALTR_A10_ECC_SERRINTEN, (base + ALTR_A10_ECC_ERRINTENS_OFST)); 11618c2ecf20Sopenharmony_ci /* Ensure all writes complete */ 11628c2ecf20Sopenharmony_ci wmb(); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci return 0; 11658c2ecf20Sopenharmony_ci} 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data a10_ocramecc_data = { 11688c2ecf20Sopenharmony_ci .setup = altr_check_ocram_deps_init, 11698c2ecf20Sopenharmony_ci .ce_clear_mask = ALTR_A10_ECC_SERRPENA, 11708c2ecf20Sopenharmony_ci .ue_clear_mask = ALTR_A10_ECC_DERRPENA, 11718c2ecf20Sopenharmony_ci .irq_status_mask = A10_SYSMGR_ECC_INTSTAT_OCRAM, 11728c2ecf20Sopenharmony_ci .ecc_enable_mask = ALTR_A10_OCRAM_ECC_EN_CTL, 11738c2ecf20Sopenharmony_ci .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST, 11748c2ecf20Sopenharmony_ci .ce_set_mask = ALTR_A10_ECC_TSERRA, 11758c2ecf20Sopenharmony_ci .ue_set_mask = ALTR_A10_ECC_TDERRA, 11768c2ecf20Sopenharmony_ci .set_err_ofst = ALTR_A10_ECC_INTTEST_OFST, 11778c2ecf20Sopenharmony_ci .ecc_irq_handler = altr_edac_a10_ecc_irq, 11788c2ecf20Sopenharmony_ci .inject_fops = &altr_edac_a10_device_inject2_fops, 11798c2ecf20Sopenharmony_ci /* 11808c2ecf20Sopenharmony_ci * OCRAM panic on uncorrectable error because sleep/resume 11818c2ecf20Sopenharmony_ci * functions and FPGA contents are stored in OCRAM. Prefer 11828c2ecf20Sopenharmony_ci * a kernel panic over executing/loading corrupted data. 11838c2ecf20Sopenharmony_ci */ 11848c2ecf20Sopenharmony_ci .panic = true, 11858c2ecf20Sopenharmony_ci}; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci#endif /* CONFIG_EDAC_ALTERA_OCRAM */ 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci/********************* L2 Cache EDAC Device Functions ********************/ 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_L2C 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cistatic void *l2_alloc_mem(size_t size, void **other) 11948c2ecf20Sopenharmony_ci{ 11958c2ecf20Sopenharmony_ci struct device *dev = *other; 11968c2ecf20Sopenharmony_ci void *ptemp = devm_kzalloc(dev, size, GFP_KERNEL); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci if (!ptemp) 11998c2ecf20Sopenharmony_ci return NULL; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci /* Make sure everything is written out */ 12028c2ecf20Sopenharmony_ci wmb(); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci /* 12058c2ecf20Sopenharmony_ci * Clean all cache levels up to LoC (includes L2) 12068c2ecf20Sopenharmony_ci * This ensures the corrupted data is written into 12078c2ecf20Sopenharmony_ci * L2 cache for readback test (which causes ECC error). 12088c2ecf20Sopenharmony_ci */ 12098c2ecf20Sopenharmony_ci flush_cache_all(); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci return ptemp; 12128c2ecf20Sopenharmony_ci} 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_cistatic void l2_free_mem(void *p, size_t size, void *other) 12158c2ecf20Sopenharmony_ci{ 12168c2ecf20Sopenharmony_ci struct device *dev = other; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci if (dev && p) 12198c2ecf20Sopenharmony_ci devm_kfree(dev, p); 12208c2ecf20Sopenharmony_ci} 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci/* 12238c2ecf20Sopenharmony_ci * altr_l2_check_deps() 12248c2ecf20Sopenharmony_ci * Test for L2 cache ECC dependencies upon entry because 12258c2ecf20Sopenharmony_ci * platform specific startup should have initialized the L2 12268c2ecf20Sopenharmony_ci * memory and enabled the ECC. 12278c2ecf20Sopenharmony_ci * Bail if ECC is not enabled. 12288c2ecf20Sopenharmony_ci * Note that L2 Cache Enable is forced at build time. 12298c2ecf20Sopenharmony_ci */ 12308c2ecf20Sopenharmony_cistatic int altr_l2_check_deps(struct altr_edac_device_dev *device) 12318c2ecf20Sopenharmony_ci{ 12328c2ecf20Sopenharmony_ci void __iomem *base = device->base; 12338c2ecf20Sopenharmony_ci const struct edac_device_prv_data *prv = device->data; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci if ((readl(base) & prv->ecc_enable_mask) == 12368c2ecf20Sopenharmony_ci prv->ecc_enable_mask) 12378c2ecf20Sopenharmony_ci return 0; 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 12408c2ecf20Sopenharmony_ci "L2: No ECC present, or ECC disabled\n"); 12418c2ecf20Sopenharmony_ci return -ENODEV; 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_cistatic irqreturn_t altr_edac_a10_l2_irq(int irq, void *dev_id) 12458c2ecf20Sopenharmony_ci{ 12468c2ecf20Sopenharmony_ci struct altr_edac_device_dev *dci = dev_id; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci if (irq == dci->sb_irq) { 12498c2ecf20Sopenharmony_ci regmap_write(dci->edac->ecc_mgr_map, 12508c2ecf20Sopenharmony_ci A10_SYSGMR_MPU_CLEAR_L2_ECC_OFST, 12518c2ecf20Sopenharmony_ci A10_SYSGMR_MPU_CLEAR_L2_ECC_SB); 12528c2ecf20Sopenharmony_ci edac_device_handle_ce(dci->edac_dev, 0, 0, dci->edac_dev_name); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci return IRQ_HANDLED; 12558c2ecf20Sopenharmony_ci } else if (irq == dci->db_irq) { 12568c2ecf20Sopenharmony_ci regmap_write(dci->edac->ecc_mgr_map, 12578c2ecf20Sopenharmony_ci A10_SYSGMR_MPU_CLEAR_L2_ECC_OFST, 12588c2ecf20Sopenharmony_ci A10_SYSGMR_MPU_CLEAR_L2_ECC_MB); 12598c2ecf20Sopenharmony_ci edac_device_handle_ue(dci->edac_dev, 0, 0, dci->edac_dev_name); 12608c2ecf20Sopenharmony_ci panic("\nEDAC:ECC_DEVICE[Uncorrectable errors]\n"); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci return IRQ_HANDLED; 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci WARN_ON(1); 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci return IRQ_NONE; 12688c2ecf20Sopenharmony_ci} 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data l2ecc_data = { 12718c2ecf20Sopenharmony_ci .setup = altr_l2_check_deps, 12728c2ecf20Sopenharmony_ci .ce_clear_mask = 0, 12738c2ecf20Sopenharmony_ci .ue_clear_mask = 0, 12748c2ecf20Sopenharmony_ci .alloc_mem = l2_alloc_mem, 12758c2ecf20Sopenharmony_ci .free_mem = l2_free_mem, 12768c2ecf20Sopenharmony_ci .ecc_enable_mask = ALTR_L2_ECC_EN, 12778c2ecf20Sopenharmony_ci .ce_set_mask = (ALTR_L2_ECC_EN | ALTR_L2_ECC_INJS), 12788c2ecf20Sopenharmony_ci .ue_set_mask = (ALTR_L2_ECC_EN | ALTR_L2_ECC_INJD), 12798c2ecf20Sopenharmony_ci .set_err_ofst = ALTR_L2_ECC_REG_OFFSET, 12808c2ecf20Sopenharmony_ci .trig_alloc_sz = ALTR_TRIG_L2C_BYTE_SIZE, 12818c2ecf20Sopenharmony_ci .inject_fops = &altr_edac_device_inject_fops, 12828c2ecf20Sopenharmony_ci}; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data a10_l2ecc_data = { 12858c2ecf20Sopenharmony_ci .setup = altr_l2_check_deps, 12868c2ecf20Sopenharmony_ci .ce_clear_mask = ALTR_A10_L2_ECC_SERR_CLR, 12878c2ecf20Sopenharmony_ci .ue_clear_mask = ALTR_A10_L2_ECC_MERR_CLR, 12888c2ecf20Sopenharmony_ci .irq_status_mask = A10_SYSMGR_ECC_INTSTAT_L2, 12898c2ecf20Sopenharmony_ci .alloc_mem = l2_alloc_mem, 12908c2ecf20Sopenharmony_ci .free_mem = l2_free_mem, 12918c2ecf20Sopenharmony_ci .ecc_enable_mask = ALTR_A10_L2_ECC_EN_CTL, 12928c2ecf20Sopenharmony_ci .ce_set_mask = ALTR_A10_L2_ECC_CE_INJ_MASK, 12938c2ecf20Sopenharmony_ci .ue_set_mask = ALTR_A10_L2_ECC_UE_INJ_MASK, 12948c2ecf20Sopenharmony_ci .set_err_ofst = ALTR_A10_L2_ECC_INJ_OFST, 12958c2ecf20Sopenharmony_ci .ecc_irq_handler = altr_edac_a10_l2_irq, 12968c2ecf20Sopenharmony_ci .trig_alloc_sz = ALTR_TRIG_L2C_BYTE_SIZE, 12978c2ecf20Sopenharmony_ci .inject_fops = &altr_edac_device_inject_fops, 12988c2ecf20Sopenharmony_ci}; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci#endif /* CONFIG_EDAC_ALTERA_L2C */ 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci/********************* Ethernet Device Functions ********************/ 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_ETHERNET 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_cistatic int __init socfpga_init_ethernet_ecc(struct altr_edac_device_dev *dev) 13078c2ecf20Sopenharmony_ci{ 13088c2ecf20Sopenharmony_ci int ret; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci ret = altr_init_a10_ecc_device_type("altr,socfpga-eth-mac-ecc"); 13118c2ecf20Sopenharmony_ci if (ret) 13128c2ecf20Sopenharmony_ci return ret; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci return altr_check_ecc_deps(dev); 13158c2ecf20Sopenharmony_ci} 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data a10_enetecc_data = { 13188c2ecf20Sopenharmony_ci .setup = socfpga_init_ethernet_ecc, 13198c2ecf20Sopenharmony_ci .ce_clear_mask = ALTR_A10_ECC_SERRPENA, 13208c2ecf20Sopenharmony_ci .ue_clear_mask = ALTR_A10_ECC_DERRPENA, 13218c2ecf20Sopenharmony_ci .ecc_enable_mask = ALTR_A10_COMMON_ECC_EN_CTL, 13228c2ecf20Sopenharmony_ci .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST, 13238c2ecf20Sopenharmony_ci .ce_set_mask = ALTR_A10_ECC_TSERRA, 13248c2ecf20Sopenharmony_ci .ue_set_mask = ALTR_A10_ECC_TDERRA, 13258c2ecf20Sopenharmony_ci .set_err_ofst = ALTR_A10_ECC_INTTEST_OFST, 13268c2ecf20Sopenharmony_ci .ecc_irq_handler = altr_edac_a10_ecc_irq, 13278c2ecf20Sopenharmony_ci .inject_fops = &altr_edac_a10_device_inject2_fops, 13288c2ecf20Sopenharmony_ci}; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci#endif /* CONFIG_EDAC_ALTERA_ETHERNET */ 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci/********************** NAND Device Functions **********************/ 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_NAND 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_cistatic int __init socfpga_init_nand_ecc(struct altr_edac_device_dev *device) 13378c2ecf20Sopenharmony_ci{ 13388c2ecf20Sopenharmony_ci int ret; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci ret = altr_init_a10_ecc_device_type("altr,socfpga-nand-ecc"); 13418c2ecf20Sopenharmony_ci if (ret) 13428c2ecf20Sopenharmony_ci return ret; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci return altr_check_ecc_deps(device); 13458c2ecf20Sopenharmony_ci} 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data a10_nandecc_data = { 13488c2ecf20Sopenharmony_ci .setup = socfpga_init_nand_ecc, 13498c2ecf20Sopenharmony_ci .ce_clear_mask = ALTR_A10_ECC_SERRPENA, 13508c2ecf20Sopenharmony_ci .ue_clear_mask = ALTR_A10_ECC_DERRPENA, 13518c2ecf20Sopenharmony_ci .ecc_enable_mask = ALTR_A10_COMMON_ECC_EN_CTL, 13528c2ecf20Sopenharmony_ci .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST, 13538c2ecf20Sopenharmony_ci .ce_set_mask = ALTR_A10_ECC_TSERRA, 13548c2ecf20Sopenharmony_ci .ue_set_mask = ALTR_A10_ECC_TDERRA, 13558c2ecf20Sopenharmony_ci .set_err_ofst = ALTR_A10_ECC_INTTEST_OFST, 13568c2ecf20Sopenharmony_ci .ecc_irq_handler = altr_edac_a10_ecc_irq, 13578c2ecf20Sopenharmony_ci .inject_fops = &altr_edac_a10_device_inject_fops, 13588c2ecf20Sopenharmony_ci}; 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci#endif /* CONFIG_EDAC_ALTERA_NAND */ 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci/********************** DMA Device Functions **********************/ 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_DMA 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_cistatic int __init socfpga_init_dma_ecc(struct altr_edac_device_dev *device) 13678c2ecf20Sopenharmony_ci{ 13688c2ecf20Sopenharmony_ci int ret; 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci ret = altr_init_a10_ecc_device_type("altr,socfpga-dma-ecc"); 13718c2ecf20Sopenharmony_ci if (ret) 13728c2ecf20Sopenharmony_ci return ret; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci return altr_check_ecc_deps(device); 13758c2ecf20Sopenharmony_ci} 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data a10_dmaecc_data = { 13788c2ecf20Sopenharmony_ci .setup = socfpga_init_dma_ecc, 13798c2ecf20Sopenharmony_ci .ce_clear_mask = ALTR_A10_ECC_SERRPENA, 13808c2ecf20Sopenharmony_ci .ue_clear_mask = ALTR_A10_ECC_DERRPENA, 13818c2ecf20Sopenharmony_ci .ecc_enable_mask = ALTR_A10_COMMON_ECC_EN_CTL, 13828c2ecf20Sopenharmony_ci .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST, 13838c2ecf20Sopenharmony_ci .ce_set_mask = ALTR_A10_ECC_TSERRA, 13848c2ecf20Sopenharmony_ci .ue_set_mask = ALTR_A10_ECC_TDERRA, 13858c2ecf20Sopenharmony_ci .set_err_ofst = ALTR_A10_ECC_INTTEST_OFST, 13868c2ecf20Sopenharmony_ci .ecc_irq_handler = altr_edac_a10_ecc_irq, 13878c2ecf20Sopenharmony_ci .inject_fops = &altr_edac_a10_device_inject_fops, 13888c2ecf20Sopenharmony_ci}; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci#endif /* CONFIG_EDAC_ALTERA_DMA */ 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci/********************** USB Device Functions **********************/ 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_USB 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_cistatic int __init socfpga_init_usb_ecc(struct altr_edac_device_dev *device) 13978c2ecf20Sopenharmony_ci{ 13988c2ecf20Sopenharmony_ci int ret; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci ret = altr_init_a10_ecc_device_type("altr,socfpga-usb-ecc"); 14018c2ecf20Sopenharmony_ci if (ret) 14028c2ecf20Sopenharmony_ci return ret; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci return altr_check_ecc_deps(device); 14058c2ecf20Sopenharmony_ci} 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data a10_usbecc_data = { 14088c2ecf20Sopenharmony_ci .setup = socfpga_init_usb_ecc, 14098c2ecf20Sopenharmony_ci .ce_clear_mask = ALTR_A10_ECC_SERRPENA, 14108c2ecf20Sopenharmony_ci .ue_clear_mask = ALTR_A10_ECC_DERRPENA, 14118c2ecf20Sopenharmony_ci .ecc_enable_mask = ALTR_A10_COMMON_ECC_EN_CTL, 14128c2ecf20Sopenharmony_ci .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST, 14138c2ecf20Sopenharmony_ci .ce_set_mask = ALTR_A10_ECC_TSERRA, 14148c2ecf20Sopenharmony_ci .ue_set_mask = ALTR_A10_ECC_TDERRA, 14158c2ecf20Sopenharmony_ci .set_err_ofst = ALTR_A10_ECC_INTTEST_OFST, 14168c2ecf20Sopenharmony_ci .ecc_irq_handler = altr_edac_a10_ecc_irq, 14178c2ecf20Sopenharmony_ci .inject_fops = &altr_edac_a10_device_inject2_fops, 14188c2ecf20Sopenharmony_ci}; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci#endif /* CONFIG_EDAC_ALTERA_USB */ 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci/********************** QSPI Device Functions **********************/ 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_QSPI 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_cistatic int __init socfpga_init_qspi_ecc(struct altr_edac_device_dev *device) 14278c2ecf20Sopenharmony_ci{ 14288c2ecf20Sopenharmony_ci int ret; 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci ret = altr_init_a10_ecc_device_type("altr,socfpga-qspi-ecc"); 14318c2ecf20Sopenharmony_ci if (ret) 14328c2ecf20Sopenharmony_ci return ret; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci return altr_check_ecc_deps(device); 14358c2ecf20Sopenharmony_ci} 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data a10_qspiecc_data = { 14388c2ecf20Sopenharmony_ci .setup = socfpga_init_qspi_ecc, 14398c2ecf20Sopenharmony_ci .ce_clear_mask = ALTR_A10_ECC_SERRPENA, 14408c2ecf20Sopenharmony_ci .ue_clear_mask = ALTR_A10_ECC_DERRPENA, 14418c2ecf20Sopenharmony_ci .ecc_enable_mask = ALTR_A10_COMMON_ECC_EN_CTL, 14428c2ecf20Sopenharmony_ci .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST, 14438c2ecf20Sopenharmony_ci .ce_set_mask = ALTR_A10_ECC_TSERRA, 14448c2ecf20Sopenharmony_ci .ue_set_mask = ALTR_A10_ECC_TDERRA, 14458c2ecf20Sopenharmony_ci .set_err_ofst = ALTR_A10_ECC_INTTEST_OFST, 14468c2ecf20Sopenharmony_ci .ecc_irq_handler = altr_edac_a10_ecc_irq, 14478c2ecf20Sopenharmony_ci .inject_fops = &altr_edac_a10_device_inject_fops, 14488c2ecf20Sopenharmony_ci}; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci#endif /* CONFIG_EDAC_ALTERA_QSPI */ 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci/********************* SDMMC Device Functions **********************/ 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_SDMMC 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data a10_sdmmceccb_data; 14578c2ecf20Sopenharmony_cistatic int altr_portb_setup(struct altr_edac_device_dev *device) 14588c2ecf20Sopenharmony_ci{ 14598c2ecf20Sopenharmony_ci struct edac_device_ctl_info *dci; 14608c2ecf20Sopenharmony_ci struct altr_edac_device_dev *altdev; 14618c2ecf20Sopenharmony_ci char *ecc_name = "sdmmcb-ecc"; 14628c2ecf20Sopenharmony_ci int edac_idx, rc; 14638c2ecf20Sopenharmony_ci struct device_node *np; 14648c2ecf20Sopenharmony_ci const struct edac_device_prv_data *prv = &a10_sdmmceccb_data; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci rc = altr_check_ecc_deps(device); 14678c2ecf20Sopenharmony_ci if (rc) 14688c2ecf20Sopenharmony_ci return rc; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "altr,socfpga-sdmmc-ecc"); 14718c2ecf20Sopenharmony_ci if (!np) { 14728c2ecf20Sopenharmony_ci edac_printk(KERN_WARNING, EDAC_DEVICE, "SDMMC node not found\n"); 14738c2ecf20Sopenharmony_ci return -ENODEV; 14748c2ecf20Sopenharmony_ci } 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci /* Create the PortB EDAC device */ 14778c2ecf20Sopenharmony_ci edac_idx = edac_device_alloc_index(); 14788c2ecf20Sopenharmony_ci dci = edac_device_alloc_ctl_info(sizeof(*altdev), ecc_name, 1, 14798c2ecf20Sopenharmony_ci ecc_name, 1, 0, NULL, 0, edac_idx); 14808c2ecf20Sopenharmony_ci if (!dci) { 14818c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 14828c2ecf20Sopenharmony_ci "%s: Unable to allocate PortB EDAC device\n", 14838c2ecf20Sopenharmony_ci ecc_name); 14848c2ecf20Sopenharmony_ci return -ENOMEM; 14858c2ecf20Sopenharmony_ci } 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci /* Initialize the PortB EDAC device structure from PortA structure */ 14888c2ecf20Sopenharmony_ci altdev = dci->pvt_info; 14898c2ecf20Sopenharmony_ci *altdev = *device; 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci if (!devres_open_group(&altdev->ddev, altr_portb_setup, GFP_KERNEL)) 14928c2ecf20Sopenharmony_ci return -ENOMEM; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci /* Update PortB specific values */ 14958c2ecf20Sopenharmony_ci altdev->edac_dev_name = ecc_name; 14968c2ecf20Sopenharmony_ci altdev->edac_idx = edac_idx; 14978c2ecf20Sopenharmony_ci altdev->edac_dev = dci; 14988c2ecf20Sopenharmony_ci altdev->data = prv; 14998c2ecf20Sopenharmony_ci dci->dev = &altdev->ddev; 15008c2ecf20Sopenharmony_ci dci->ctl_name = "Altera ECC Manager"; 15018c2ecf20Sopenharmony_ci dci->mod_name = ecc_name; 15028c2ecf20Sopenharmony_ci dci->dev_name = ecc_name; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci /* Update the PortB IRQs - A10 has 4, S10 has 2, Index accordingly */ 15058c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_STRATIX10 15068c2ecf20Sopenharmony_ci altdev->sb_irq = irq_of_parse_and_map(np, 1); 15078c2ecf20Sopenharmony_ci#else 15088c2ecf20Sopenharmony_ci altdev->sb_irq = irq_of_parse_and_map(np, 2); 15098c2ecf20Sopenharmony_ci#endif 15108c2ecf20Sopenharmony_ci if (!altdev->sb_irq) { 15118c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB SBIRQ alloc\n"); 15128c2ecf20Sopenharmony_ci rc = -ENODEV; 15138c2ecf20Sopenharmony_ci goto err_release_group_1; 15148c2ecf20Sopenharmony_ci } 15158c2ecf20Sopenharmony_ci rc = devm_request_irq(&altdev->ddev, altdev->sb_irq, 15168c2ecf20Sopenharmony_ci prv->ecc_irq_handler, 15178c2ecf20Sopenharmony_ci IRQF_ONESHOT | IRQF_TRIGGER_HIGH, 15188c2ecf20Sopenharmony_ci ecc_name, altdev); 15198c2ecf20Sopenharmony_ci if (rc) { 15208c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, "PortB SBERR IRQ error\n"); 15218c2ecf20Sopenharmony_ci goto err_release_group_1; 15228c2ecf20Sopenharmony_ci } 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_STRATIX10 15258c2ecf20Sopenharmony_ci /* Use IRQ to determine SError origin instead of assigning IRQ */ 15268c2ecf20Sopenharmony_ci rc = of_property_read_u32_index(np, "interrupts", 1, &altdev->db_irq); 15278c2ecf20Sopenharmony_ci if (rc) { 15288c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 15298c2ecf20Sopenharmony_ci "Error PortB DBIRQ alloc\n"); 15308c2ecf20Sopenharmony_ci goto err_release_group_1; 15318c2ecf20Sopenharmony_ci } 15328c2ecf20Sopenharmony_ci#else 15338c2ecf20Sopenharmony_ci altdev->db_irq = irq_of_parse_and_map(np, 3); 15348c2ecf20Sopenharmony_ci if (!altdev->db_irq) { 15358c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n"); 15368c2ecf20Sopenharmony_ci rc = -ENODEV; 15378c2ecf20Sopenharmony_ci goto err_release_group_1; 15388c2ecf20Sopenharmony_ci } 15398c2ecf20Sopenharmony_ci rc = devm_request_irq(&altdev->ddev, altdev->db_irq, 15408c2ecf20Sopenharmony_ci prv->ecc_irq_handler, 15418c2ecf20Sopenharmony_ci IRQF_ONESHOT | IRQF_TRIGGER_HIGH, 15428c2ecf20Sopenharmony_ci ecc_name, altdev); 15438c2ecf20Sopenharmony_ci if (rc) { 15448c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, "PortB DBERR IRQ error\n"); 15458c2ecf20Sopenharmony_ci goto err_release_group_1; 15468c2ecf20Sopenharmony_ci } 15478c2ecf20Sopenharmony_ci#endif 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci rc = edac_device_add_device(dci); 15508c2ecf20Sopenharmony_ci if (rc) { 15518c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 15528c2ecf20Sopenharmony_ci "edac_device_add_device portB failed\n"); 15538c2ecf20Sopenharmony_ci rc = -ENOMEM; 15548c2ecf20Sopenharmony_ci goto err_release_group_1; 15558c2ecf20Sopenharmony_ci } 15568c2ecf20Sopenharmony_ci altr_create_edacdev_dbgfs(dci, prv); 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci list_add(&altdev->next, &altdev->edac->a10_ecc_devices); 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci devres_remove_group(&altdev->ddev, altr_portb_setup); 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci return 0; 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_cierr_release_group_1: 15658c2ecf20Sopenharmony_ci edac_device_free_ctl_info(dci); 15668c2ecf20Sopenharmony_ci devres_release_group(&altdev->ddev, altr_portb_setup); 15678c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 15688c2ecf20Sopenharmony_ci "%s:Error setting up EDAC device: %d\n", ecc_name, rc); 15698c2ecf20Sopenharmony_ci return rc; 15708c2ecf20Sopenharmony_ci} 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_cistatic int __init socfpga_init_sdmmc_ecc(struct altr_edac_device_dev *device) 15738c2ecf20Sopenharmony_ci{ 15748c2ecf20Sopenharmony_ci int rc = -ENODEV; 15758c2ecf20Sopenharmony_ci struct device_node *child; 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci child = of_find_compatible_node(NULL, NULL, "altr,socfpga-sdmmc-ecc"); 15788c2ecf20Sopenharmony_ci if (!child) 15798c2ecf20Sopenharmony_ci return -ENODEV; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci if (!of_device_is_available(child)) 15828c2ecf20Sopenharmony_ci goto exit; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci if (validate_parent_available(child)) 15858c2ecf20Sopenharmony_ci goto exit; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci /* Init portB */ 15888c2ecf20Sopenharmony_ci rc = altr_init_a10_ecc_block(child, ALTR_A10_SDMMC_IRQ_MASK, 15898c2ecf20Sopenharmony_ci a10_sdmmceccb_data.ecc_enable_mask, 1); 15908c2ecf20Sopenharmony_ci if (rc) 15918c2ecf20Sopenharmony_ci goto exit; 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci /* Setup portB */ 15948c2ecf20Sopenharmony_ci return altr_portb_setup(device); 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ciexit: 15978c2ecf20Sopenharmony_ci of_node_put(child); 15988c2ecf20Sopenharmony_ci return rc; 15998c2ecf20Sopenharmony_ci} 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_cistatic irqreturn_t altr_edac_a10_ecc_irq_portb(int irq, void *dev_id) 16028c2ecf20Sopenharmony_ci{ 16038c2ecf20Sopenharmony_ci struct altr_edac_device_dev *ad = dev_id; 16048c2ecf20Sopenharmony_ci void __iomem *base = ad->base; 16058c2ecf20Sopenharmony_ci const struct edac_device_prv_data *priv = ad->data; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci if (irq == ad->sb_irq) { 16088c2ecf20Sopenharmony_ci writel(priv->ce_clear_mask, 16098c2ecf20Sopenharmony_ci base + ALTR_A10_ECC_INTSTAT_OFST); 16108c2ecf20Sopenharmony_ci edac_device_handle_ce(ad->edac_dev, 0, 0, ad->edac_dev_name); 16118c2ecf20Sopenharmony_ci return IRQ_HANDLED; 16128c2ecf20Sopenharmony_ci } else if (irq == ad->db_irq) { 16138c2ecf20Sopenharmony_ci writel(priv->ue_clear_mask, 16148c2ecf20Sopenharmony_ci base + ALTR_A10_ECC_INTSTAT_OFST); 16158c2ecf20Sopenharmony_ci edac_device_handle_ue(ad->edac_dev, 0, 0, ad->edac_dev_name); 16168c2ecf20Sopenharmony_ci return IRQ_HANDLED; 16178c2ecf20Sopenharmony_ci } 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci WARN_ONCE(1, "Unhandled IRQ%d on Port B.", irq); 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci return IRQ_NONE; 16228c2ecf20Sopenharmony_ci} 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data a10_sdmmcecca_data = { 16258c2ecf20Sopenharmony_ci .setup = socfpga_init_sdmmc_ecc, 16268c2ecf20Sopenharmony_ci .ce_clear_mask = ALTR_A10_ECC_SERRPENA, 16278c2ecf20Sopenharmony_ci .ue_clear_mask = ALTR_A10_ECC_DERRPENA, 16288c2ecf20Sopenharmony_ci .ecc_enable_mask = ALTR_A10_COMMON_ECC_EN_CTL, 16298c2ecf20Sopenharmony_ci .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST, 16308c2ecf20Sopenharmony_ci .ce_set_mask = ALTR_A10_ECC_SERRPENA, 16318c2ecf20Sopenharmony_ci .ue_set_mask = ALTR_A10_ECC_DERRPENA, 16328c2ecf20Sopenharmony_ci .set_err_ofst = ALTR_A10_ECC_INTTEST_OFST, 16338c2ecf20Sopenharmony_ci .ecc_irq_handler = altr_edac_a10_ecc_irq, 16348c2ecf20Sopenharmony_ci .inject_fops = &altr_edac_a10_device_inject_fops, 16358c2ecf20Sopenharmony_ci}; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_cistatic const struct edac_device_prv_data a10_sdmmceccb_data = { 16388c2ecf20Sopenharmony_ci .setup = socfpga_init_sdmmc_ecc, 16398c2ecf20Sopenharmony_ci .ce_clear_mask = ALTR_A10_ECC_SERRPENB, 16408c2ecf20Sopenharmony_ci .ue_clear_mask = ALTR_A10_ECC_DERRPENB, 16418c2ecf20Sopenharmony_ci .ecc_enable_mask = ALTR_A10_COMMON_ECC_EN_CTL, 16428c2ecf20Sopenharmony_ci .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST, 16438c2ecf20Sopenharmony_ci .ce_set_mask = ALTR_A10_ECC_TSERRB, 16448c2ecf20Sopenharmony_ci .ue_set_mask = ALTR_A10_ECC_TDERRB, 16458c2ecf20Sopenharmony_ci .set_err_ofst = ALTR_A10_ECC_INTTEST_OFST, 16468c2ecf20Sopenharmony_ci .ecc_irq_handler = altr_edac_a10_ecc_irq_portb, 16478c2ecf20Sopenharmony_ci .inject_fops = &altr_edac_a10_device_inject_fops, 16488c2ecf20Sopenharmony_ci}; 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci#endif /* CONFIG_EDAC_ALTERA_SDMMC */ 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci/********************* Arria10 EDAC Device Functions *************************/ 16538c2ecf20Sopenharmony_cistatic const struct of_device_id altr_edac_a10_device_of_match[] = { 16548c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_L2C 16558c2ecf20Sopenharmony_ci { .compatible = "altr,socfpga-a10-l2-ecc", .data = &a10_l2ecc_data }, 16568c2ecf20Sopenharmony_ci#endif 16578c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_OCRAM 16588c2ecf20Sopenharmony_ci { .compatible = "altr,socfpga-a10-ocram-ecc", 16598c2ecf20Sopenharmony_ci .data = &a10_ocramecc_data }, 16608c2ecf20Sopenharmony_ci#endif 16618c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_ETHERNET 16628c2ecf20Sopenharmony_ci { .compatible = "altr,socfpga-eth-mac-ecc", 16638c2ecf20Sopenharmony_ci .data = &a10_enetecc_data }, 16648c2ecf20Sopenharmony_ci#endif 16658c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_NAND 16668c2ecf20Sopenharmony_ci { .compatible = "altr,socfpga-nand-ecc", .data = &a10_nandecc_data }, 16678c2ecf20Sopenharmony_ci#endif 16688c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_DMA 16698c2ecf20Sopenharmony_ci { .compatible = "altr,socfpga-dma-ecc", .data = &a10_dmaecc_data }, 16708c2ecf20Sopenharmony_ci#endif 16718c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_USB 16728c2ecf20Sopenharmony_ci { .compatible = "altr,socfpga-usb-ecc", .data = &a10_usbecc_data }, 16738c2ecf20Sopenharmony_ci#endif 16748c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_QSPI 16758c2ecf20Sopenharmony_ci { .compatible = "altr,socfpga-qspi-ecc", .data = &a10_qspiecc_data }, 16768c2ecf20Sopenharmony_ci#endif 16778c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_SDMMC 16788c2ecf20Sopenharmony_ci { .compatible = "altr,socfpga-sdmmc-ecc", .data = &a10_sdmmcecca_data }, 16798c2ecf20Sopenharmony_ci#endif 16808c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_SDRAM 16818c2ecf20Sopenharmony_ci { .compatible = "altr,sdram-edac-s10", .data = &s10_sdramecc_data }, 16828c2ecf20Sopenharmony_ci#endif 16838c2ecf20Sopenharmony_ci {}, 16848c2ecf20Sopenharmony_ci}; 16858c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, altr_edac_a10_device_of_match); 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci/* 16888c2ecf20Sopenharmony_ci * The Arria10 EDAC Device Functions differ from the Cyclone5/Arria5 16898c2ecf20Sopenharmony_ci * because 2 IRQs are shared among the all ECC peripherals. The ECC 16908c2ecf20Sopenharmony_ci * manager manages the IRQs and the children. 16918c2ecf20Sopenharmony_ci * Based on xgene_edac.c peripheral code. 16928c2ecf20Sopenharmony_ci */ 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_cistatic ssize_t altr_edac_a10_device_trig(struct file *file, 16958c2ecf20Sopenharmony_ci const char __user *user_buf, 16968c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 16978c2ecf20Sopenharmony_ci{ 16988c2ecf20Sopenharmony_ci struct edac_device_ctl_info *edac_dci = file->private_data; 16998c2ecf20Sopenharmony_ci struct altr_edac_device_dev *drvdata = edac_dci->pvt_info; 17008c2ecf20Sopenharmony_ci const struct edac_device_prv_data *priv = drvdata->data; 17018c2ecf20Sopenharmony_ci void __iomem *set_addr = (drvdata->base + priv->set_err_ofst); 17028c2ecf20Sopenharmony_ci unsigned long flags; 17038c2ecf20Sopenharmony_ci u8 trig_type; 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci if (!user_buf || get_user(trig_type, user_buf)) 17068c2ecf20Sopenharmony_ci return -EFAULT; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci local_irq_save(flags); 17098c2ecf20Sopenharmony_ci if (trig_type == ALTR_UE_TRIGGER_CHAR) 17108c2ecf20Sopenharmony_ci writel(priv->ue_set_mask, set_addr); 17118c2ecf20Sopenharmony_ci else 17128c2ecf20Sopenharmony_ci writel(priv->ce_set_mask, set_addr); 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci /* Ensure the interrupt test bits are set */ 17158c2ecf20Sopenharmony_ci wmb(); 17168c2ecf20Sopenharmony_ci local_irq_restore(flags); 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci return count; 17198c2ecf20Sopenharmony_ci} 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci/* 17228c2ecf20Sopenharmony_ci * The Stratix10 EDAC Error Injection Functions differ from Arria10 17238c2ecf20Sopenharmony_ci * slightly. A few Arria10 peripherals can use this injection function. 17248c2ecf20Sopenharmony_ci * Inject the error into the memory and then readback to trigger the IRQ. 17258c2ecf20Sopenharmony_ci */ 17268c2ecf20Sopenharmony_cistatic ssize_t altr_edac_a10_device_trig2(struct file *file, 17278c2ecf20Sopenharmony_ci const char __user *user_buf, 17288c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 17298c2ecf20Sopenharmony_ci{ 17308c2ecf20Sopenharmony_ci struct edac_device_ctl_info *edac_dci = file->private_data; 17318c2ecf20Sopenharmony_ci struct altr_edac_device_dev *drvdata = edac_dci->pvt_info; 17328c2ecf20Sopenharmony_ci const struct edac_device_prv_data *priv = drvdata->data; 17338c2ecf20Sopenharmony_ci void __iomem *set_addr = (drvdata->base + priv->set_err_ofst); 17348c2ecf20Sopenharmony_ci unsigned long flags; 17358c2ecf20Sopenharmony_ci u8 trig_type; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci if (!user_buf || get_user(trig_type, user_buf)) 17388c2ecf20Sopenharmony_ci return -EFAULT; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci local_irq_save(flags); 17418c2ecf20Sopenharmony_ci if (trig_type == ALTR_UE_TRIGGER_CHAR) { 17428c2ecf20Sopenharmony_ci writel(priv->ue_set_mask, set_addr); 17438c2ecf20Sopenharmony_ci } else { 17448c2ecf20Sopenharmony_ci /* Setup read/write of 4 bytes */ 17458c2ecf20Sopenharmony_ci writel(ECC_WORD_WRITE, drvdata->base + ECC_BLK_DBYTECTRL_OFST); 17468c2ecf20Sopenharmony_ci /* Setup Address to 0 */ 17478c2ecf20Sopenharmony_ci writel(0, drvdata->base + ECC_BLK_ADDRESS_OFST); 17488c2ecf20Sopenharmony_ci /* Setup accctrl to read & ecc & data override */ 17498c2ecf20Sopenharmony_ci writel(ECC_READ_EDOVR, drvdata->base + ECC_BLK_ACCCTRL_OFST); 17508c2ecf20Sopenharmony_ci /* Kick it. */ 17518c2ecf20Sopenharmony_ci writel(ECC_XACT_KICK, drvdata->base + ECC_BLK_STARTACC_OFST); 17528c2ecf20Sopenharmony_ci /* Setup write for single bit change */ 17538c2ecf20Sopenharmony_ci writel(readl(drvdata->base + ECC_BLK_RDATA0_OFST) ^ 0x1, 17548c2ecf20Sopenharmony_ci drvdata->base + ECC_BLK_WDATA0_OFST); 17558c2ecf20Sopenharmony_ci writel(readl(drvdata->base + ECC_BLK_RDATA1_OFST), 17568c2ecf20Sopenharmony_ci drvdata->base + ECC_BLK_WDATA1_OFST); 17578c2ecf20Sopenharmony_ci writel(readl(drvdata->base + ECC_BLK_RDATA2_OFST), 17588c2ecf20Sopenharmony_ci drvdata->base + ECC_BLK_WDATA2_OFST); 17598c2ecf20Sopenharmony_ci writel(readl(drvdata->base + ECC_BLK_RDATA3_OFST), 17608c2ecf20Sopenharmony_ci drvdata->base + ECC_BLK_WDATA3_OFST); 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci /* Copy Read ECC to Write ECC */ 17638c2ecf20Sopenharmony_ci writel(readl(drvdata->base + ECC_BLK_RECC0_OFST), 17648c2ecf20Sopenharmony_ci drvdata->base + ECC_BLK_WECC0_OFST); 17658c2ecf20Sopenharmony_ci writel(readl(drvdata->base + ECC_BLK_RECC1_OFST), 17668c2ecf20Sopenharmony_ci drvdata->base + ECC_BLK_WECC1_OFST); 17678c2ecf20Sopenharmony_ci /* Setup accctrl to write & ecc override & data override */ 17688c2ecf20Sopenharmony_ci writel(ECC_WRITE_EDOVR, drvdata->base + ECC_BLK_ACCCTRL_OFST); 17698c2ecf20Sopenharmony_ci /* Kick it. */ 17708c2ecf20Sopenharmony_ci writel(ECC_XACT_KICK, drvdata->base + ECC_BLK_STARTACC_OFST); 17718c2ecf20Sopenharmony_ci /* Setup accctrl to read & ecc overwrite & data overwrite */ 17728c2ecf20Sopenharmony_ci writel(ECC_READ_EDOVR, drvdata->base + ECC_BLK_ACCCTRL_OFST); 17738c2ecf20Sopenharmony_ci /* Kick it. */ 17748c2ecf20Sopenharmony_ci writel(ECC_XACT_KICK, drvdata->base + ECC_BLK_STARTACC_OFST); 17758c2ecf20Sopenharmony_ci } 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci /* Ensure the interrupt test bits are set */ 17788c2ecf20Sopenharmony_ci wmb(); 17798c2ecf20Sopenharmony_ci local_irq_restore(flags); 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci return count; 17828c2ecf20Sopenharmony_ci} 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_cistatic void altr_edac_a10_irq_handler(struct irq_desc *desc) 17858c2ecf20Sopenharmony_ci{ 17868c2ecf20Sopenharmony_ci int dberr, bit, sm_offset, irq_status; 17878c2ecf20Sopenharmony_ci struct altr_arria10_edac *edac = irq_desc_get_handler_data(desc); 17888c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 17898c2ecf20Sopenharmony_ci int irq = irq_desc_get_irq(desc); 17908c2ecf20Sopenharmony_ci unsigned long bits; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci dberr = (irq == edac->db_irq) ? 1 : 0; 17938c2ecf20Sopenharmony_ci sm_offset = dberr ? A10_SYSMGR_ECC_INTSTAT_DERR_OFST : 17948c2ecf20Sopenharmony_ci A10_SYSMGR_ECC_INTSTAT_SERR_OFST; 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci chained_irq_enter(chip, desc); 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci regmap_read(edac->ecc_mgr_map, sm_offset, &irq_status); 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci bits = irq_status; 18018c2ecf20Sopenharmony_ci for_each_set_bit(bit, &bits, 32) { 18028c2ecf20Sopenharmony_ci irq = irq_linear_revmap(edac->domain, dberr * 32 + bit); 18038c2ecf20Sopenharmony_ci if (irq) 18048c2ecf20Sopenharmony_ci generic_handle_irq(irq); 18058c2ecf20Sopenharmony_ci } 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci chained_irq_exit(chip, desc); 18088c2ecf20Sopenharmony_ci} 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_cistatic int validate_parent_available(struct device_node *np) 18118c2ecf20Sopenharmony_ci{ 18128c2ecf20Sopenharmony_ci struct device_node *parent; 18138c2ecf20Sopenharmony_ci int ret = 0; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci /* SDRAM must be present for Linux (implied parent) */ 18168c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "altr,sdram-edac-s10")) 18178c2ecf20Sopenharmony_ci return 0; 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci /* Ensure parent device is enabled if parent node exists */ 18208c2ecf20Sopenharmony_ci parent = of_parse_phandle(np, "altr,ecc-parent", 0); 18218c2ecf20Sopenharmony_ci if (parent && !of_device_is_available(parent)) 18228c2ecf20Sopenharmony_ci ret = -ENODEV; 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci of_node_put(parent); 18258c2ecf20Sopenharmony_ci return ret; 18268c2ecf20Sopenharmony_ci} 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_cistatic int get_s10_sdram_edac_resource(struct device_node *np, 18298c2ecf20Sopenharmony_ci struct resource *res) 18308c2ecf20Sopenharmony_ci{ 18318c2ecf20Sopenharmony_ci struct device_node *parent; 18328c2ecf20Sopenharmony_ci int ret; 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci parent = of_parse_phandle(np, "altr,sdr-syscon", 0); 18358c2ecf20Sopenharmony_ci if (!parent) 18368c2ecf20Sopenharmony_ci return -ENODEV; 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci ret = of_address_to_resource(parent, 0, res); 18398c2ecf20Sopenharmony_ci of_node_put(parent); 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci return ret; 18428c2ecf20Sopenharmony_ci} 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_cistatic int altr_edac_a10_device_add(struct altr_arria10_edac *edac, 18458c2ecf20Sopenharmony_ci struct device_node *np) 18468c2ecf20Sopenharmony_ci{ 18478c2ecf20Sopenharmony_ci struct edac_device_ctl_info *dci; 18488c2ecf20Sopenharmony_ci struct altr_edac_device_dev *altdev; 18498c2ecf20Sopenharmony_ci char *ecc_name = (char *)np->name; 18508c2ecf20Sopenharmony_ci struct resource res; 18518c2ecf20Sopenharmony_ci int edac_idx; 18528c2ecf20Sopenharmony_ci int rc = 0; 18538c2ecf20Sopenharmony_ci const struct edac_device_prv_data *prv; 18548c2ecf20Sopenharmony_ci /* Get matching node and check for valid result */ 18558c2ecf20Sopenharmony_ci const struct of_device_id *pdev_id = 18568c2ecf20Sopenharmony_ci of_match_node(altr_edac_a10_device_of_match, np); 18578c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(pdev_id)) 18588c2ecf20Sopenharmony_ci return -ENODEV; 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci /* Get driver specific data for this EDAC device */ 18618c2ecf20Sopenharmony_ci prv = pdev_id->data; 18628c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(prv)) 18638c2ecf20Sopenharmony_ci return -ENODEV; 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci if (validate_parent_available(np)) 18668c2ecf20Sopenharmony_ci return -ENODEV; 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci if (!devres_open_group(edac->dev, altr_edac_a10_device_add, GFP_KERNEL)) 18698c2ecf20Sopenharmony_ci return -ENOMEM; 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "altr,sdram-edac-s10")) 18728c2ecf20Sopenharmony_ci rc = get_s10_sdram_edac_resource(np, &res); 18738c2ecf20Sopenharmony_ci else 18748c2ecf20Sopenharmony_ci rc = of_address_to_resource(np, 0, &res); 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci if (rc < 0) { 18778c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 18788c2ecf20Sopenharmony_ci "%s: no resource address\n", ecc_name); 18798c2ecf20Sopenharmony_ci goto err_release_group; 18808c2ecf20Sopenharmony_ci } 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci edac_idx = edac_device_alloc_index(); 18838c2ecf20Sopenharmony_ci dci = edac_device_alloc_ctl_info(sizeof(*altdev), ecc_name, 18848c2ecf20Sopenharmony_ci 1, ecc_name, 1, 0, NULL, 0, 18858c2ecf20Sopenharmony_ci edac_idx); 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci if (!dci) { 18888c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 18898c2ecf20Sopenharmony_ci "%s: Unable to allocate EDAC device\n", ecc_name); 18908c2ecf20Sopenharmony_ci rc = -ENOMEM; 18918c2ecf20Sopenharmony_ci goto err_release_group; 18928c2ecf20Sopenharmony_ci } 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci altdev = dci->pvt_info; 18958c2ecf20Sopenharmony_ci dci->dev = edac->dev; 18968c2ecf20Sopenharmony_ci altdev->edac_dev_name = ecc_name; 18978c2ecf20Sopenharmony_ci altdev->edac_idx = edac_idx; 18988c2ecf20Sopenharmony_ci altdev->edac = edac; 18998c2ecf20Sopenharmony_ci altdev->edac_dev = dci; 19008c2ecf20Sopenharmony_ci altdev->data = prv; 19018c2ecf20Sopenharmony_ci altdev->ddev = *edac->dev; 19028c2ecf20Sopenharmony_ci dci->dev = &altdev->ddev; 19038c2ecf20Sopenharmony_ci dci->ctl_name = "Altera ECC Manager"; 19048c2ecf20Sopenharmony_ci dci->mod_name = ecc_name; 19058c2ecf20Sopenharmony_ci dci->dev_name = ecc_name; 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci altdev->base = devm_ioremap_resource(edac->dev, &res); 19088c2ecf20Sopenharmony_ci if (IS_ERR(altdev->base)) { 19098c2ecf20Sopenharmony_ci rc = PTR_ERR(altdev->base); 19108c2ecf20Sopenharmony_ci goto err_release_group1; 19118c2ecf20Sopenharmony_ci } 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci /* Check specific dependencies for the module */ 19148c2ecf20Sopenharmony_ci if (altdev->data->setup) { 19158c2ecf20Sopenharmony_ci rc = altdev->data->setup(altdev); 19168c2ecf20Sopenharmony_ci if (rc) 19178c2ecf20Sopenharmony_ci goto err_release_group1; 19188c2ecf20Sopenharmony_ci } 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci altdev->sb_irq = irq_of_parse_and_map(np, 0); 19218c2ecf20Sopenharmony_ci if (!altdev->sb_irq) { 19228c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating SBIRQ\n"); 19238c2ecf20Sopenharmony_ci rc = -ENODEV; 19248c2ecf20Sopenharmony_ci goto err_release_group1; 19258c2ecf20Sopenharmony_ci } 19268c2ecf20Sopenharmony_ci rc = devm_request_irq(edac->dev, altdev->sb_irq, prv->ecc_irq_handler, 19278c2ecf20Sopenharmony_ci IRQF_ONESHOT | IRQF_TRIGGER_HIGH, 19288c2ecf20Sopenharmony_ci ecc_name, altdev); 19298c2ecf20Sopenharmony_ci if (rc) { 19308c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, "No SBERR IRQ resource\n"); 19318c2ecf20Sopenharmony_ci goto err_release_group1; 19328c2ecf20Sopenharmony_ci } 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_STRATIX10 19358c2ecf20Sopenharmony_ci /* Use IRQ to determine SError origin instead of assigning IRQ */ 19368c2ecf20Sopenharmony_ci rc = of_property_read_u32_index(np, "interrupts", 0, &altdev->db_irq); 19378c2ecf20Sopenharmony_ci if (rc) { 19388c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 19398c2ecf20Sopenharmony_ci "Unable to parse DB IRQ index\n"); 19408c2ecf20Sopenharmony_ci goto err_release_group1; 19418c2ecf20Sopenharmony_ci } 19428c2ecf20Sopenharmony_ci#else 19438c2ecf20Sopenharmony_ci altdev->db_irq = irq_of_parse_and_map(np, 1); 19448c2ecf20Sopenharmony_ci if (!altdev->db_irq) { 19458c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating DBIRQ\n"); 19468c2ecf20Sopenharmony_ci rc = -ENODEV; 19478c2ecf20Sopenharmony_ci goto err_release_group1; 19488c2ecf20Sopenharmony_ci } 19498c2ecf20Sopenharmony_ci rc = devm_request_irq(edac->dev, altdev->db_irq, prv->ecc_irq_handler, 19508c2ecf20Sopenharmony_ci IRQF_ONESHOT | IRQF_TRIGGER_HIGH, 19518c2ecf20Sopenharmony_ci ecc_name, altdev); 19528c2ecf20Sopenharmony_ci if (rc) { 19538c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n"); 19548c2ecf20Sopenharmony_ci goto err_release_group1; 19558c2ecf20Sopenharmony_ci } 19568c2ecf20Sopenharmony_ci#endif 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci rc = edac_device_add_device(dci); 19598c2ecf20Sopenharmony_ci if (rc) { 19608c2ecf20Sopenharmony_ci dev_err(edac->dev, "edac_device_add_device failed\n"); 19618c2ecf20Sopenharmony_ci rc = -ENOMEM; 19628c2ecf20Sopenharmony_ci goto err_release_group1; 19638c2ecf20Sopenharmony_ci } 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci altr_create_edacdev_dbgfs(dci, prv); 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci list_add(&altdev->next, &edac->a10_ecc_devices); 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci devres_remove_group(edac->dev, altr_edac_a10_device_add); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci return 0; 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_cierr_release_group1: 19748c2ecf20Sopenharmony_ci edac_device_free_ctl_info(dci); 19758c2ecf20Sopenharmony_cierr_release_group: 19768c2ecf20Sopenharmony_ci devres_release_group(edac->dev, NULL); 19778c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 19788c2ecf20Sopenharmony_ci "%s:Error setting up EDAC device: %d\n", ecc_name, rc); 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci return rc; 19818c2ecf20Sopenharmony_ci} 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_cistatic void a10_eccmgr_irq_mask(struct irq_data *d) 19848c2ecf20Sopenharmony_ci{ 19858c2ecf20Sopenharmony_ci struct altr_arria10_edac *edac = irq_data_get_irq_chip_data(d); 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci regmap_write(edac->ecc_mgr_map, A10_SYSMGR_ECC_INTMASK_SET_OFST, 19888c2ecf20Sopenharmony_ci BIT(d->hwirq)); 19898c2ecf20Sopenharmony_ci} 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_cistatic void a10_eccmgr_irq_unmask(struct irq_data *d) 19928c2ecf20Sopenharmony_ci{ 19938c2ecf20Sopenharmony_ci struct altr_arria10_edac *edac = irq_data_get_irq_chip_data(d); 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci regmap_write(edac->ecc_mgr_map, A10_SYSMGR_ECC_INTMASK_CLR_OFST, 19968c2ecf20Sopenharmony_ci BIT(d->hwirq)); 19978c2ecf20Sopenharmony_ci} 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_cistatic int a10_eccmgr_irqdomain_map(struct irq_domain *d, unsigned int irq, 20008c2ecf20Sopenharmony_ci irq_hw_number_t hwirq) 20018c2ecf20Sopenharmony_ci{ 20028c2ecf20Sopenharmony_ci struct altr_arria10_edac *edac = d->host_data; 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci irq_set_chip_and_handler(irq, &edac->irq_chip, handle_simple_irq); 20058c2ecf20Sopenharmony_ci irq_set_chip_data(irq, edac); 20068c2ecf20Sopenharmony_ci irq_set_noprobe(irq); 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci return 0; 20098c2ecf20Sopenharmony_ci} 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_cistatic const struct irq_domain_ops a10_eccmgr_ic_ops = { 20128c2ecf20Sopenharmony_ci .map = a10_eccmgr_irqdomain_map, 20138c2ecf20Sopenharmony_ci .xlate = irq_domain_xlate_twocell, 20148c2ecf20Sopenharmony_ci}; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci/************** Stratix 10 EDAC Double Bit Error Handler ************/ 20178c2ecf20Sopenharmony_ci#define to_a10edac(p, m) container_of(p, struct altr_arria10_edac, m) 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_STRATIX10 20208c2ecf20Sopenharmony_ci/* panic routine issues reboot on non-zero panic_timeout */ 20218c2ecf20Sopenharmony_ciextern int panic_timeout; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci/* 20248c2ecf20Sopenharmony_ci * The double bit error is handled through SError which is fatal. This is 20258c2ecf20Sopenharmony_ci * called as a panic notifier to printout ECC error info as part of the panic. 20268c2ecf20Sopenharmony_ci */ 20278c2ecf20Sopenharmony_cistatic int s10_edac_dberr_handler(struct notifier_block *this, 20288c2ecf20Sopenharmony_ci unsigned long event, void *ptr) 20298c2ecf20Sopenharmony_ci{ 20308c2ecf20Sopenharmony_ci struct altr_arria10_edac *edac = to_a10edac(this, panic_notifier); 20318c2ecf20Sopenharmony_ci int err_addr, dberror; 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci regmap_read(edac->ecc_mgr_map, S10_SYSMGR_ECC_INTSTAT_DERR_OFST, 20348c2ecf20Sopenharmony_ci &dberror); 20358c2ecf20Sopenharmony_ci regmap_write(edac->ecc_mgr_map, S10_SYSMGR_UE_VAL_OFST, dberror); 20368c2ecf20Sopenharmony_ci if (dberror & S10_DBE_IRQ_MASK) { 20378c2ecf20Sopenharmony_ci struct list_head *position; 20388c2ecf20Sopenharmony_ci struct altr_edac_device_dev *ed; 20398c2ecf20Sopenharmony_ci struct arm_smccc_res result; 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci /* Find the matching DBE in the list of devices */ 20428c2ecf20Sopenharmony_ci list_for_each(position, &edac->a10_ecc_devices) { 20438c2ecf20Sopenharmony_ci ed = list_entry(position, struct altr_edac_device_dev, 20448c2ecf20Sopenharmony_ci next); 20458c2ecf20Sopenharmony_ci if (!(BIT(ed->db_irq) & dberror)) 20468c2ecf20Sopenharmony_ci continue; 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci writel(ALTR_A10_ECC_DERRPENA, 20498c2ecf20Sopenharmony_ci ed->base + ALTR_A10_ECC_INTSTAT_OFST); 20508c2ecf20Sopenharmony_ci err_addr = readl(ed->base + ALTR_S10_DERR_ADDRA_OFST); 20518c2ecf20Sopenharmony_ci regmap_write(edac->ecc_mgr_map, 20528c2ecf20Sopenharmony_ci S10_SYSMGR_UE_ADDR_OFST, err_addr); 20538c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 20548c2ecf20Sopenharmony_ci "EDAC: [Fatal DBE on %s @ 0x%08X]\n", 20558c2ecf20Sopenharmony_ci ed->edac_dev_name, err_addr); 20568c2ecf20Sopenharmony_ci break; 20578c2ecf20Sopenharmony_ci } 20588c2ecf20Sopenharmony_ci /* Notify the System through SMC. Reboot delay = 1 second */ 20598c2ecf20Sopenharmony_ci panic_timeout = 1; 20608c2ecf20Sopenharmony_ci arm_smccc_smc(INTEL_SIP_SMC_ECC_DBE, dberror, 0, 0, 0, 0, 20618c2ecf20Sopenharmony_ci 0, 0, &result); 20628c2ecf20Sopenharmony_ci } 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci return NOTIFY_DONE; 20658c2ecf20Sopenharmony_ci} 20668c2ecf20Sopenharmony_ci#endif 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci/****************** Arria 10 EDAC Probe Function *********************/ 20698c2ecf20Sopenharmony_cistatic int altr_edac_a10_probe(struct platform_device *pdev) 20708c2ecf20Sopenharmony_ci{ 20718c2ecf20Sopenharmony_ci struct altr_arria10_edac *edac; 20728c2ecf20Sopenharmony_ci struct device_node *child; 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci edac = devm_kzalloc(&pdev->dev, sizeof(*edac), GFP_KERNEL); 20758c2ecf20Sopenharmony_ci if (!edac) 20768c2ecf20Sopenharmony_ci return -ENOMEM; 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci edac->dev = &pdev->dev; 20798c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, edac); 20808c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&edac->a10_ecc_devices); 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci edac->ecc_mgr_map = 20838c2ecf20Sopenharmony_ci altr_sysmgr_regmap_lookup_by_phandle(pdev->dev.of_node, 20848c2ecf20Sopenharmony_ci "altr,sysmgr-syscon"); 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci if (IS_ERR(edac->ecc_mgr_map)) { 20878c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 20888c2ecf20Sopenharmony_ci "Unable to get syscon altr,sysmgr-syscon\n"); 20898c2ecf20Sopenharmony_ci return PTR_ERR(edac->ecc_mgr_map); 20908c2ecf20Sopenharmony_ci } 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci edac->irq_chip.name = pdev->dev.of_node->name; 20938c2ecf20Sopenharmony_ci edac->irq_chip.irq_mask = a10_eccmgr_irq_mask; 20948c2ecf20Sopenharmony_ci edac->irq_chip.irq_unmask = a10_eccmgr_irq_unmask; 20958c2ecf20Sopenharmony_ci edac->domain = irq_domain_add_linear(pdev->dev.of_node, 64, 20968c2ecf20Sopenharmony_ci &a10_eccmgr_ic_ops, edac); 20978c2ecf20Sopenharmony_ci if (!edac->domain) { 20988c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Error adding IRQ domain\n"); 20998c2ecf20Sopenharmony_ci return -ENOMEM; 21008c2ecf20Sopenharmony_ci } 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci edac->sb_irq = platform_get_irq(pdev, 0); 21038c2ecf20Sopenharmony_ci if (edac->sb_irq < 0) { 21048c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No SBERR IRQ resource\n"); 21058c2ecf20Sopenharmony_ci return edac->sb_irq; 21068c2ecf20Sopenharmony_ci } 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(edac->sb_irq, 21098c2ecf20Sopenharmony_ci altr_edac_a10_irq_handler, 21108c2ecf20Sopenharmony_ci edac); 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_STRATIX10 21138c2ecf20Sopenharmony_ci { 21148c2ecf20Sopenharmony_ci int dberror, err_addr; 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci edac->panic_notifier.notifier_call = s10_edac_dberr_handler; 21178c2ecf20Sopenharmony_ci atomic_notifier_chain_register(&panic_notifier_list, 21188c2ecf20Sopenharmony_ci &edac->panic_notifier); 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci /* Printout a message if uncorrectable error previously. */ 21218c2ecf20Sopenharmony_ci regmap_read(edac->ecc_mgr_map, S10_SYSMGR_UE_VAL_OFST, 21228c2ecf20Sopenharmony_ci &dberror); 21238c2ecf20Sopenharmony_ci if (dberror) { 21248c2ecf20Sopenharmony_ci regmap_read(edac->ecc_mgr_map, S10_SYSMGR_UE_ADDR_OFST, 21258c2ecf20Sopenharmony_ci &err_addr); 21268c2ecf20Sopenharmony_ci edac_printk(KERN_ERR, EDAC_DEVICE, 21278c2ecf20Sopenharmony_ci "Previous Boot UE detected[0x%X] @ 0x%X\n", 21288c2ecf20Sopenharmony_ci dberror, err_addr); 21298c2ecf20Sopenharmony_ci /* Reset the sticky registers */ 21308c2ecf20Sopenharmony_ci regmap_write(edac->ecc_mgr_map, 21318c2ecf20Sopenharmony_ci S10_SYSMGR_UE_VAL_OFST, 0); 21328c2ecf20Sopenharmony_ci regmap_write(edac->ecc_mgr_map, 21338c2ecf20Sopenharmony_ci S10_SYSMGR_UE_ADDR_OFST, 0); 21348c2ecf20Sopenharmony_ci } 21358c2ecf20Sopenharmony_ci } 21368c2ecf20Sopenharmony_ci#else 21378c2ecf20Sopenharmony_ci edac->db_irq = platform_get_irq(pdev, 1); 21388c2ecf20Sopenharmony_ci if (edac->db_irq < 0) { 21398c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No DBERR IRQ resource\n"); 21408c2ecf20Sopenharmony_ci return edac->db_irq; 21418c2ecf20Sopenharmony_ci } 21428c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(edac->db_irq, 21438c2ecf20Sopenharmony_ci altr_edac_a10_irq_handler, edac); 21448c2ecf20Sopenharmony_ci#endif 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci for_each_child_of_node(pdev->dev.of_node, child) { 21478c2ecf20Sopenharmony_ci if (!of_device_is_available(child)) 21488c2ecf20Sopenharmony_ci continue; 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ci if (of_match_node(altr_edac_a10_device_of_match, child)) 21518c2ecf20Sopenharmony_ci altr_edac_a10_device_add(edac, child); 21528c2ecf20Sopenharmony_ci 21538c2ecf20Sopenharmony_ci#ifdef CONFIG_EDAC_ALTERA_SDRAM 21548c2ecf20Sopenharmony_ci else if (of_device_is_compatible(child, "altr,sdram-edac-a10")) 21558c2ecf20Sopenharmony_ci of_platform_populate(pdev->dev.of_node, 21568c2ecf20Sopenharmony_ci altr_sdram_ctrl_of_match, 21578c2ecf20Sopenharmony_ci NULL, &pdev->dev); 21588c2ecf20Sopenharmony_ci#endif 21598c2ecf20Sopenharmony_ci } 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci return 0; 21628c2ecf20Sopenharmony_ci} 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_cistatic const struct of_device_id altr_edac_a10_of_match[] = { 21658c2ecf20Sopenharmony_ci { .compatible = "altr,socfpga-a10-ecc-manager" }, 21668c2ecf20Sopenharmony_ci { .compatible = "altr,socfpga-s10-ecc-manager" }, 21678c2ecf20Sopenharmony_ci {}, 21688c2ecf20Sopenharmony_ci}; 21698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, altr_edac_a10_of_match); 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_cistatic struct platform_driver altr_edac_a10_driver = { 21728c2ecf20Sopenharmony_ci .probe = altr_edac_a10_probe, 21738c2ecf20Sopenharmony_ci .driver = { 21748c2ecf20Sopenharmony_ci .name = "socfpga_a10_ecc_manager", 21758c2ecf20Sopenharmony_ci .of_match_table = altr_edac_a10_of_match, 21768c2ecf20Sopenharmony_ci }, 21778c2ecf20Sopenharmony_ci}; 21788c2ecf20Sopenharmony_cimodule_platform_driver(altr_edac_a10_driver); 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 21818c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thor Thayer"); 21828c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("EDAC Driver for Altera Memories"); 2183