162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/ 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Texas Instruments DDR3 ECC error correction and detection driver 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 862306a36Sopenharmony_ci * under the terms and conditions of the GNU General Public License, 962306a36Sopenharmony_ci * version 2, as published by the Free Software Foundation. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * This program is distributed in the hope it will be useful, but WITHOUT 1262306a36Sopenharmony_ci * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1362306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 1462306a36Sopenharmony_ci * more details. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License along with 1762306a36Sopenharmony_ci * this program. If not, see <http://www.gnu.org/licenses/>. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/init.h> 2162306a36Sopenharmony_ci#include <linux/edac.h> 2262306a36Sopenharmony_ci#include <linux/io.h> 2362306a36Sopenharmony_ci#include <linux/interrupt.h> 2462306a36Sopenharmony_ci#include <linux/of_address.h> 2562306a36Sopenharmony_ci#include <linux/of_device.h> 2662306a36Sopenharmony_ci#include <linux/module.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "edac_module.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* EMIF controller registers */ 3162306a36Sopenharmony_ci#define EMIF_SDRAM_CONFIG 0x008 3262306a36Sopenharmony_ci#define EMIF_IRQ_STATUS 0x0ac 3362306a36Sopenharmony_ci#define EMIF_IRQ_ENABLE_SET 0x0b4 3462306a36Sopenharmony_ci#define EMIF_ECC_CTRL 0x110 3562306a36Sopenharmony_ci#define EMIF_1B_ECC_ERR_CNT 0x130 3662306a36Sopenharmony_ci#define EMIF_1B_ECC_ERR_THRSH 0x134 3762306a36Sopenharmony_ci#define EMIF_1B_ECC_ERR_ADDR_LOG 0x13c 3862306a36Sopenharmony_ci#define EMIF_2B_ECC_ERR_ADDR_LOG 0x140 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* Bit definitions for EMIF_SDRAM_CONFIG */ 4162306a36Sopenharmony_ci#define SDRAM_TYPE_SHIFT 29 4262306a36Sopenharmony_ci#define SDRAM_TYPE_MASK GENMASK(31, 29) 4362306a36Sopenharmony_ci#define SDRAM_TYPE_DDR3 (3 << SDRAM_TYPE_SHIFT) 4462306a36Sopenharmony_ci#define SDRAM_TYPE_DDR2 (2 << SDRAM_TYPE_SHIFT) 4562306a36Sopenharmony_ci#define SDRAM_NARROW_MODE_MASK GENMASK(15, 14) 4662306a36Sopenharmony_ci#define SDRAM_K2_NARROW_MODE_SHIFT 12 4762306a36Sopenharmony_ci#define SDRAM_K2_NARROW_MODE_MASK GENMASK(13, 12) 4862306a36Sopenharmony_ci#define SDRAM_ROWSIZE_SHIFT 7 4962306a36Sopenharmony_ci#define SDRAM_ROWSIZE_MASK GENMASK(9, 7) 5062306a36Sopenharmony_ci#define SDRAM_IBANK_SHIFT 4 5162306a36Sopenharmony_ci#define SDRAM_IBANK_MASK GENMASK(6, 4) 5262306a36Sopenharmony_ci#define SDRAM_K2_IBANK_SHIFT 5 5362306a36Sopenharmony_ci#define SDRAM_K2_IBANK_MASK GENMASK(6, 5) 5462306a36Sopenharmony_ci#define SDRAM_K2_EBANK_SHIFT 3 5562306a36Sopenharmony_ci#define SDRAM_K2_EBANK_MASK BIT(SDRAM_K2_EBANK_SHIFT) 5662306a36Sopenharmony_ci#define SDRAM_PAGESIZE_SHIFT 0 5762306a36Sopenharmony_ci#define SDRAM_PAGESIZE_MASK GENMASK(2, 0) 5862306a36Sopenharmony_ci#define SDRAM_K2_PAGESIZE_SHIFT 0 5962306a36Sopenharmony_ci#define SDRAM_K2_PAGESIZE_MASK GENMASK(1, 0) 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define EMIF_1B_ECC_ERR_THRSH_SHIFT 24 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* IRQ bit definitions */ 6462306a36Sopenharmony_ci#define EMIF_1B_ECC_ERR BIT(5) 6562306a36Sopenharmony_ci#define EMIF_2B_ECC_ERR BIT(4) 6662306a36Sopenharmony_ci#define EMIF_WR_ECC_ERR BIT(3) 6762306a36Sopenharmony_ci#define EMIF_SYS_ERR BIT(0) 6862306a36Sopenharmony_ci/* Bit 31 enables ECC and 28 enables RMW */ 6962306a36Sopenharmony_ci#define ECC_ENABLED (BIT(31) | BIT(28)) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define EDAC_MOD_NAME "ti-emif-edac" 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cienum { 7462306a36Sopenharmony_ci EMIF_TYPE_DRA7, 7562306a36Sopenharmony_ci EMIF_TYPE_K2 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistruct ti_edac { 7962306a36Sopenharmony_ci void __iomem *reg; 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic u32 ti_edac_readl(struct ti_edac *edac, u16 offset) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci return readl_relaxed(edac->reg + offset); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void ti_edac_writel(struct ti_edac *edac, u32 val, u16 offset) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci writel_relaxed(val, edac->reg + offset); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic irqreturn_t ti_edac_isr(int irq, void *data) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct mem_ctl_info *mci = data; 9562306a36Sopenharmony_ci struct ti_edac *edac = mci->pvt_info; 9662306a36Sopenharmony_ci u32 irq_status; 9762306a36Sopenharmony_ci u32 err_addr; 9862306a36Sopenharmony_ci int err_count; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci irq_status = ti_edac_readl(edac, EMIF_IRQ_STATUS); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (irq_status & EMIF_1B_ECC_ERR) { 10362306a36Sopenharmony_ci err_addr = ti_edac_readl(edac, EMIF_1B_ECC_ERR_ADDR_LOG); 10462306a36Sopenharmony_ci err_count = ti_edac_readl(edac, EMIF_1B_ECC_ERR_CNT); 10562306a36Sopenharmony_ci ti_edac_writel(edac, err_count, EMIF_1B_ECC_ERR_CNT); 10662306a36Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, err_count, 10762306a36Sopenharmony_ci err_addr >> PAGE_SHIFT, 10862306a36Sopenharmony_ci err_addr & ~PAGE_MASK, -1, 0, 0, 0, 10962306a36Sopenharmony_ci mci->ctl_name, "1B"); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (irq_status & EMIF_2B_ECC_ERR) { 11362306a36Sopenharmony_ci err_addr = ti_edac_readl(edac, EMIF_2B_ECC_ERR_ADDR_LOG); 11462306a36Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 11562306a36Sopenharmony_ci err_addr >> PAGE_SHIFT, 11662306a36Sopenharmony_ci err_addr & ~PAGE_MASK, -1, 0, 0, 0, 11762306a36Sopenharmony_ci mci->ctl_name, "2B"); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (irq_status & EMIF_WR_ECC_ERR) 12162306a36Sopenharmony_ci edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 12262306a36Sopenharmony_ci 0, 0, -1, 0, 0, 0, 12362306a36Sopenharmony_ci mci->ctl_name, "WR"); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci ti_edac_writel(edac, irq_status, EMIF_IRQ_STATUS); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return IRQ_HANDLED; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void ti_edac_setup_dimm(struct mem_ctl_info *mci, u32 type) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct dimm_info *dimm; 13362306a36Sopenharmony_ci struct ti_edac *edac = mci->pvt_info; 13462306a36Sopenharmony_ci int bits; 13562306a36Sopenharmony_ci u32 val; 13662306a36Sopenharmony_ci u32 memsize; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci dimm = edac_get_dimm(mci, 0, 0, 0); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci val = ti_edac_readl(edac, EMIF_SDRAM_CONFIG); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (type == EMIF_TYPE_DRA7) { 14362306a36Sopenharmony_ci bits = ((val & SDRAM_PAGESIZE_MASK) >> SDRAM_PAGESIZE_SHIFT) + 8; 14462306a36Sopenharmony_ci bits += ((val & SDRAM_ROWSIZE_MASK) >> SDRAM_ROWSIZE_SHIFT) + 9; 14562306a36Sopenharmony_ci bits += (val & SDRAM_IBANK_MASK) >> SDRAM_IBANK_SHIFT; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (val & SDRAM_NARROW_MODE_MASK) { 14862306a36Sopenharmony_ci bits++; 14962306a36Sopenharmony_ci dimm->dtype = DEV_X16; 15062306a36Sopenharmony_ci } else { 15162306a36Sopenharmony_ci bits += 2; 15262306a36Sopenharmony_ci dimm->dtype = DEV_X32; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci } else { 15562306a36Sopenharmony_ci bits = 16; 15662306a36Sopenharmony_ci bits += ((val & SDRAM_K2_PAGESIZE_MASK) >> 15762306a36Sopenharmony_ci SDRAM_K2_PAGESIZE_SHIFT) + 8; 15862306a36Sopenharmony_ci bits += (val & SDRAM_K2_IBANK_MASK) >> SDRAM_K2_IBANK_SHIFT; 15962306a36Sopenharmony_ci bits += (val & SDRAM_K2_EBANK_MASK) >> SDRAM_K2_EBANK_SHIFT; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci val = (val & SDRAM_K2_NARROW_MODE_MASK) >> 16262306a36Sopenharmony_ci SDRAM_K2_NARROW_MODE_SHIFT; 16362306a36Sopenharmony_ci switch (val) { 16462306a36Sopenharmony_ci case 0: 16562306a36Sopenharmony_ci bits += 3; 16662306a36Sopenharmony_ci dimm->dtype = DEV_X64; 16762306a36Sopenharmony_ci break; 16862306a36Sopenharmony_ci case 1: 16962306a36Sopenharmony_ci bits += 2; 17062306a36Sopenharmony_ci dimm->dtype = DEV_X32; 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci case 2: 17362306a36Sopenharmony_ci bits++; 17462306a36Sopenharmony_ci dimm->dtype = DEV_X16; 17562306a36Sopenharmony_ci break; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci memsize = 1 << bits; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci dimm->nr_pages = memsize >> PAGE_SHIFT; 18262306a36Sopenharmony_ci dimm->grain = 4; 18362306a36Sopenharmony_ci if ((val & SDRAM_TYPE_MASK) == SDRAM_TYPE_DDR2) 18462306a36Sopenharmony_ci dimm->mtype = MEM_DDR2; 18562306a36Sopenharmony_ci else 18662306a36Sopenharmony_ci dimm->mtype = MEM_DDR3; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci val = ti_edac_readl(edac, EMIF_ECC_CTRL); 18962306a36Sopenharmony_ci if (val & ECC_ENABLED) 19062306a36Sopenharmony_ci dimm->edac_mode = EDAC_SECDED; 19162306a36Sopenharmony_ci else 19262306a36Sopenharmony_ci dimm->edac_mode = EDAC_NONE; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic const struct of_device_id ti_edac_of_match[] = { 19662306a36Sopenharmony_ci { .compatible = "ti,emif-keystone", .data = (void *)EMIF_TYPE_K2 }, 19762306a36Sopenharmony_ci { .compatible = "ti,emif-dra7xx", .data = (void *)EMIF_TYPE_DRA7 }, 19862306a36Sopenharmony_ci {}, 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ti_edac_of_match); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int _emif_get_id(struct device_node *node) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct device_node *np; 20562306a36Sopenharmony_ci const __be32 *addrp; 20662306a36Sopenharmony_ci u32 addr, my_addr; 20762306a36Sopenharmony_ci int my_id = 0; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci addrp = of_get_address(node, 0, NULL, NULL); 21062306a36Sopenharmony_ci my_addr = (u32)of_translate_address(node, addrp); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci for_each_matching_node(np, ti_edac_of_match) { 21362306a36Sopenharmony_ci if (np == node) 21462306a36Sopenharmony_ci continue; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci addrp = of_get_address(np, 0, NULL, NULL); 21762306a36Sopenharmony_ci addr = (u32)of_translate_address(np, addrp); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci edac_printk(KERN_INFO, EDAC_MOD_NAME, 22062306a36Sopenharmony_ci "addr=%x, my_addr=%x\n", 22162306a36Sopenharmony_ci addr, my_addr); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (addr < my_addr) 22462306a36Sopenharmony_ci my_id++; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return my_id; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic int ti_edac_probe(struct platform_device *pdev) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci int error_irq = 0, ret = -ENODEV; 23362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 23462306a36Sopenharmony_ci struct resource *res; 23562306a36Sopenharmony_ci void __iomem *reg; 23662306a36Sopenharmony_ci struct mem_ctl_info *mci; 23762306a36Sopenharmony_ci struct edac_mc_layer layers[1]; 23862306a36Sopenharmony_ci const struct of_device_id *id; 23962306a36Sopenharmony_ci struct ti_edac *edac; 24062306a36Sopenharmony_ci int emif_id; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci id = of_match_device(ti_edac_of_match, &pdev->dev); 24362306a36Sopenharmony_ci if (!id) 24462306a36Sopenharmony_ci return -ENODEV; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 24762306a36Sopenharmony_ci reg = devm_ioremap_resource(dev, res); 24862306a36Sopenharmony_ci if (IS_ERR(reg)) 24962306a36Sopenharmony_ci return PTR_ERR(reg); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci layers[0].type = EDAC_MC_LAYER_ALL_MEM; 25262306a36Sopenharmony_ci layers[0].size = 1; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Allocate ID number for our EMIF controller */ 25562306a36Sopenharmony_ci emif_id = _emif_get_id(pdev->dev.of_node); 25662306a36Sopenharmony_ci if (emif_id < 0) 25762306a36Sopenharmony_ci return -EINVAL; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci mci = edac_mc_alloc(emif_id, 1, layers, sizeof(*edac)); 26062306a36Sopenharmony_ci if (!mci) 26162306a36Sopenharmony_ci return -ENOMEM; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci mci->pdev = &pdev->dev; 26462306a36Sopenharmony_ci edac = mci->pvt_info; 26562306a36Sopenharmony_ci edac->reg = reg; 26662306a36Sopenharmony_ci platform_set_drvdata(pdev, mci); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR2; 26962306a36Sopenharmony_ci mci->edac_ctl_cap = EDAC_FLAG_SECDED | EDAC_FLAG_NONE; 27062306a36Sopenharmony_ci mci->mod_name = EDAC_MOD_NAME; 27162306a36Sopenharmony_ci mci->ctl_name = id->compatible; 27262306a36Sopenharmony_ci mci->dev_name = dev_name(&pdev->dev); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* Setup memory layout */ 27562306a36Sopenharmony_ci ti_edac_setup_dimm(mci, (u32)(id->data)); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* add EMIF ECC error handler */ 27862306a36Sopenharmony_ci error_irq = platform_get_irq(pdev, 0); 27962306a36Sopenharmony_ci if (error_irq < 0) { 28062306a36Sopenharmony_ci ret = error_irq; 28162306a36Sopenharmony_ci goto err; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci ret = devm_request_irq(dev, error_irq, ti_edac_isr, 0, 28562306a36Sopenharmony_ci "emif-edac-irq", mci); 28662306a36Sopenharmony_ci if (ret) { 28762306a36Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MOD_NAME, 28862306a36Sopenharmony_ci "request_irq fail for EMIF EDAC irq\n"); 28962306a36Sopenharmony_ci goto err; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ret = edac_mc_add_mc(mci); 29362306a36Sopenharmony_ci if (ret) { 29462306a36Sopenharmony_ci edac_printk(KERN_ERR, EDAC_MOD_NAME, 29562306a36Sopenharmony_ci "Failed to register mci: %d.\n", ret); 29662306a36Sopenharmony_ci goto err; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* Generate an interrupt with each 1b error */ 30062306a36Sopenharmony_ci ti_edac_writel(edac, 1 << EMIF_1B_ECC_ERR_THRSH_SHIFT, 30162306a36Sopenharmony_ci EMIF_1B_ECC_ERR_THRSH); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* Enable interrupts */ 30462306a36Sopenharmony_ci ti_edac_writel(edac, 30562306a36Sopenharmony_ci EMIF_1B_ECC_ERR | EMIF_2B_ECC_ERR | EMIF_WR_ECC_ERR, 30662306a36Sopenharmony_ci EMIF_IRQ_ENABLE_SET); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cierr: 31162306a36Sopenharmony_ci edac_mc_free(mci); 31262306a36Sopenharmony_ci return ret; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic int ti_edac_remove(struct platform_device *pdev) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct mem_ctl_info *mci = platform_get_drvdata(pdev); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci edac_mc_del_mc(&pdev->dev); 32062306a36Sopenharmony_ci edac_mc_free(mci); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic struct platform_driver ti_edac_driver = { 32662306a36Sopenharmony_ci .probe = ti_edac_probe, 32762306a36Sopenharmony_ci .remove = ti_edac_remove, 32862306a36Sopenharmony_ci .driver = { 32962306a36Sopenharmony_ci .name = EDAC_MOD_NAME, 33062306a36Sopenharmony_ci .of_match_table = ti_edac_of_match, 33162306a36Sopenharmony_ci }, 33262306a36Sopenharmony_ci}; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cimodule_platform_driver(ti_edac_driver); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ciMODULE_AUTHOR("Texas Instruments Inc."); 33762306a36Sopenharmony_ciMODULE_DESCRIPTION("EDAC Driver for Texas Instruments DDR3 MC"); 33862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 339