18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 38c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 48c2ecf20Sopenharmony_ci * for more details. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2012 Cavium, Inc. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2009 Wind River Systems, 98c2ecf20Sopenharmony_ci * written by Ralf Baechle <ralf@linux-mips.org> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/edac.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <asm/octeon/cvmx.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "edac_module.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define EDAC_MOD_STR "octeon-l2c" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic void octeon_l2c_poll_oct1(struct edac_device_ctl_info *l2c) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci union cvmx_l2t_err l2t_err, l2t_err_reset; 268c2ecf20Sopenharmony_ci union cvmx_l2d_err l2d_err, l2d_err_reset; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci l2t_err_reset.u64 = 0; 298c2ecf20Sopenharmony_ci l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR); 308c2ecf20Sopenharmony_ci if (l2t_err.s.sec_err) { 318c2ecf20Sopenharmony_ci edac_device_handle_ce(l2c, 0, 0, 328c2ecf20Sopenharmony_ci "Tag Single bit error (corrected)"); 338c2ecf20Sopenharmony_ci l2t_err_reset.s.sec_err = 1; 348c2ecf20Sopenharmony_ci } 358c2ecf20Sopenharmony_ci if (l2t_err.s.ded_err) { 368c2ecf20Sopenharmony_ci edac_device_handle_ue(l2c, 0, 0, 378c2ecf20Sopenharmony_ci "Tag Double bit error (detected)"); 388c2ecf20Sopenharmony_ci l2t_err_reset.s.ded_err = 1; 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci if (l2t_err_reset.u64) 418c2ecf20Sopenharmony_ci cvmx_write_csr(CVMX_L2T_ERR, l2t_err_reset.u64); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci l2d_err_reset.u64 = 0; 448c2ecf20Sopenharmony_ci l2d_err.u64 = cvmx_read_csr(CVMX_L2D_ERR); 458c2ecf20Sopenharmony_ci if (l2d_err.s.sec_err) { 468c2ecf20Sopenharmony_ci edac_device_handle_ce(l2c, 0, 1, 478c2ecf20Sopenharmony_ci "Data Single bit error (corrected)"); 488c2ecf20Sopenharmony_ci l2d_err_reset.s.sec_err = 1; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci if (l2d_err.s.ded_err) { 518c2ecf20Sopenharmony_ci edac_device_handle_ue(l2c, 0, 1, 528c2ecf20Sopenharmony_ci "Data Double bit error (detected)"); 538c2ecf20Sopenharmony_ci l2d_err_reset.s.ded_err = 1; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci if (l2d_err_reset.u64) 568c2ecf20Sopenharmony_ci cvmx_write_csr(CVMX_L2D_ERR, l2d_err_reset.u64); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void _octeon_l2c_poll_oct2(struct edac_device_ctl_info *l2c, int tad) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci union cvmx_l2c_err_tdtx err_tdtx, err_tdtx_reset; 638c2ecf20Sopenharmony_ci union cvmx_l2c_err_ttgx err_ttgx, err_ttgx_reset; 648c2ecf20Sopenharmony_ci char buf1[64]; 658c2ecf20Sopenharmony_ci char buf2[80]; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci err_tdtx_reset.u64 = 0; 688c2ecf20Sopenharmony_ci err_tdtx.u64 = cvmx_read_csr(CVMX_L2C_ERR_TDTX(tad)); 698c2ecf20Sopenharmony_ci if (err_tdtx.s.dbe || err_tdtx.s.sbe || 708c2ecf20Sopenharmony_ci err_tdtx.s.vdbe || err_tdtx.s.vsbe) 718c2ecf20Sopenharmony_ci snprintf(buf1, sizeof(buf1), 728c2ecf20Sopenharmony_ci "type:%d, syn:0x%x, way:%d", 738c2ecf20Sopenharmony_ci err_tdtx.s.type, err_tdtx.s.syn, err_tdtx.s.wayidx); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (err_tdtx.s.dbe) { 768c2ecf20Sopenharmony_ci snprintf(buf2, sizeof(buf2), 778c2ecf20Sopenharmony_ci "L2D Double bit error (detected):%s", buf1); 788c2ecf20Sopenharmony_ci err_tdtx_reset.s.dbe = 1; 798c2ecf20Sopenharmony_ci edac_device_handle_ue(l2c, tad, 1, buf2); 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci if (err_tdtx.s.sbe) { 828c2ecf20Sopenharmony_ci snprintf(buf2, sizeof(buf2), 838c2ecf20Sopenharmony_ci "L2D Single bit error (corrected):%s", buf1); 848c2ecf20Sopenharmony_ci err_tdtx_reset.s.sbe = 1; 858c2ecf20Sopenharmony_ci edac_device_handle_ce(l2c, tad, 1, buf2); 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci if (err_tdtx.s.vdbe) { 888c2ecf20Sopenharmony_ci snprintf(buf2, sizeof(buf2), 898c2ecf20Sopenharmony_ci "VBF Double bit error (detected):%s", buf1); 908c2ecf20Sopenharmony_ci err_tdtx_reset.s.vdbe = 1; 918c2ecf20Sopenharmony_ci edac_device_handle_ue(l2c, tad, 1, buf2); 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci if (err_tdtx.s.vsbe) { 948c2ecf20Sopenharmony_ci snprintf(buf2, sizeof(buf2), 958c2ecf20Sopenharmony_ci "VBF Single bit error (corrected):%s", buf1); 968c2ecf20Sopenharmony_ci err_tdtx_reset.s.vsbe = 1; 978c2ecf20Sopenharmony_ci edac_device_handle_ce(l2c, tad, 1, buf2); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci if (err_tdtx_reset.u64) 1008c2ecf20Sopenharmony_ci cvmx_write_csr(CVMX_L2C_ERR_TDTX(tad), err_tdtx_reset.u64); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci err_ttgx_reset.u64 = 0; 1038c2ecf20Sopenharmony_ci err_ttgx.u64 = cvmx_read_csr(CVMX_L2C_ERR_TTGX(tad)); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (err_ttgx.s.dbe || err_ttgx.s.sbe) 1068c2ecf20Sopenharmony_ci snprintf(buf1, sizeof(buf1), 1078c2ecf20Sopenharmony_ci "type:%d, syn:0x%x, way:%d", 1088c2ecf20Sopenharmony_ci err_ttgx.s.type, err_ttgx.s.syn, err_ttgx.s.wayidx); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (err_ttgx.s.dbe) { 1118c2ecf20Sopenharmony_ci snprintf(buf2, sizeof(buf2), 1128c2ecf20Sopenharmony_ci "Tag Double bit error (detected):%s", buf1); 1138c2ecf20Sopenharmony_ci err_ttgx_reset.s.dbe = 1; 1148c2ecf20Sopenharmony_ci edac_device_handle_ue(l2c, tad, 0, buf2); 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci if (err_ttgx.s.sbe) { 1178c2ecf20Sopenharmony_ci snprintf(buf2, sizeof(buf2), 1188c2ecf20Sopenharmony_ci "Tag Single bit error (corrected):%s", buf1); 1198c2ecf20Sopenharmony_ci err_ttgx_reset.s.sbe = 1; 1208c2ecf20Sopenharmony_ci edac_device_handle_ce(l2c, tad, 0, buf2); 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci if (err_ttgx_reset.u64) 1238c2ecf20Sopenharmony_ci cvmx_write_csr(CVMX_L2C_ERR_TTGX(tad), err_ttgx_reset.u64); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void octeon_l2c_poll_oct2(struct edac_device_ctl_info *l2c) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci int i; 1298c2ecf20Sopenharmony_ci for (i = 0; i < l2c->nr_instances; i++) 1308c2ecf20Sopenharmony_ci _octeon_l2c_poll_oct2(l2c, i); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int octeon_l2c_probe(struct platform_device *pdev) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct edac_device_ctl_info *l2c; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci int num_tads = OCTEON_IS_MODEL(OCTEON_CN68XX) ? 4 : 1; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* 'Tags' are block 0, 'Data' is block 1*/ 1408c2ecf20Sopenharmony_ci l2c = edac_device_alloc_ctl_info(0, "l2c", num_tads, "l2c", 2, 0, 1418c2ecf20Sopenharmony_ci NULL, 0, edac_device_alloc_index()); 1428c2ecf20Sopenharmony_ci if (!l2c) 1438c2ecf20Sopenharmony_ci return -ENOMEM; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci l2c->dev = &pdev->dev; 1468c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, l2c); 1478c2ecf20Sopenharmony_ci l2c->dev_name = dev_name(&pdev->dev); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci l2c->mod_name = "octeon-l2c"; 1508c2ecf20Sopenharmony_ci l2c->ctl_name = "octeon_l2c_err"; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (OCTEON_IS_OCTEON1PLUS()) { 1548c2ecf20Sopenharmony_ci union cvmx_l2t_err l2t_err; 1558c2ecf20Sopenharmony_ci union cvmx_l2d_err l2d_err; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR); 1588c2ecf20Sopenharmony_ci l2t_err.s.sec_intena = 0; /* We poll */ 1598c2ecf20Sopenharmony_ci l2t_err.s.ded_intena = 0; 1608c2ecf20Sopenharmony_ci cvmx_write_csr(CVMX_L2T_ERR, l2t_err.u64); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci l2d_err.u64 = cvmx_read_csr(CVMX_L2D_ERR); 1638c2ecf20Sopenharmony_ci l2d_err.s.sec_intena = 0; /* We poll */ 1648c2ecf20Sopenharmony_ci l2d_err.s.ded_intena = 0; 1658c2ecf20Sopenharmony_ci cvmx_write_csr(CVMX_L2T_ERR, l2d_err.u64); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci l2c->edac_check = octeon_l2c_poll_oct1; 1688c2ecf20Sopenharmony_ci } else { 1698c2ecf20Sopenharmony_ci /* OCTEON II */ 1708c2ecf20Sopenharmony_ci l2c->edac_check = octeon_l2c_poll_oct2; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (edac_device_add_device(l2c) > 0) { 1748c2ecf20Sopenharmony_ci pr_err("%s: edac_device_add_device() failed\n", __func__); 1758c2ecf20Sopenharmony_ci goto err; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cierr: 1828c2ecf20Sopenharmony_ci edac_device_free_ctl_info(l2c); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return -ENXIO; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int octeon_l2c_remove(struct platform_device *pdev) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct edac_device_ctl_info *l2c = platform_get_drvdata(pdev); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci edac_device_del_device(&pdev->dev); 1928c2ecf20Sopenharmony_ci edac_device_free_ctl_info(l2c); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic struct platform_driver octeon_l2c_driver = { 1988c2ecf20Sopenharmony_ci .probe = octeon_l2c_probe, 1998c2ecf20Sopenharmony_ci .remove = octeon_l2c_remove, 2008c2ecf20Sopenharmony_ci .driver = { 2018c2ecf20Sopenharmony_ci .name = "octeon_l2c_edac", 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_cimodule_platform_driver(octeon_l2c_driver); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2078c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>"); 208