1 /* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 2012 Cavium, Inc. 7 * 8 * Copyright (C) 2009 Wind River Systems, 9 * written by Ralf Baechle <ralf@linux-mips.org> 10 */ 11 #include <linux/module.h> 12 #include <linux/init.h> 13 #include <linux/slab.h> 14 #include <linux/io.h> 15 #include <linux/edac.h> 16 17 #include <asm/octeon/cvmx.h> 18 19 #include "edac_module.h" 20 21 #define EDAC_MOD_STR "octeon-l2c" 22 23 static void octeon_l2c_poll_oct1(struct edac_device_ctl_info *l2c) 24 { 25 union cvmx_l2t_err l2t_err, l2t_err_reset; 26 union cvmx_l2d_err l2d_err, l2d_err_reset; 27 28 l2t_err_reset.u64 = 0; 29 l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR); 30 if (l2t_err.s.sec_err) { 31 edac_device_handle_ce(l2c, 0, 0, 32 "Tag Single bit error (corrected)"); 33 l2t_err_reset.s.sec_err = 1; 34 } 35 if (l2t_err.s.ded_err) { 36 edac_device_handle_ue(l2c, 0, 0, 37 "Tag Double bit error (detected)"); 38 l2t_err_reset.s.ded_err = 1; 39 } 40 if (l2t_err_reset.u64) 41 cvmx_write_csr(CVMX_L2T_ERR, l2t_err_reset.u64); 42 43 l2d_err_reset.u64 = 0; 44 l2d_err.u64 = cvmx_read_csr(CVMX_L2D_ERR); 45 if (l2d_err.s.sec_err) { 46 edac_device_handle_ce(l2c, 0, 1, 47 "Data Single bit error (corrected)"); 48 l2d_err_reset.s.sec_err = 1; 49 } 50 if (l2d_err.s.ded_err) { 51 edac_device_handle_ue(l2c, 0, 1, 52 "Data Double bit error (detected)"); 53 l2d_err_reset.s.ded_err = 1; 54 } 55 if (l2d_err_reset.u64) 56 cvmx_write_csr(CVMX_L2D_ERR, l2d_err_reset.u64); 57 58 } 59 60 static void _octeon_l2c_poll_oct2(struct edac_device_ctl_info *l2c, int tad) 61 { 62 union cvmx_l2c_err_tdtx err_tdtx, err_tdtx_reset; 63 union cvmx_l2c_err_ttgx err_ttgx, err_ttgx_reset; 64 char buf1[64]; 65 char buf2[80]; 66 67 err_tdtx_reset.u64 = 0; 68 err_tdtx.u64 = cvmx_read_csr(CVMX_L2C_ERR_TDTX(tad)); 69 if (err_tdtx.s.dbe || err_tdtx.s.sbe || 70 err_tdtx.s.vdbe || err_tdtx.s.vsbe) 71 snprintf(buf1, sizeof(buf1), 72 "type:%d, syn:0x%x, way:%d", 73 err_tdtx.s.type, err_tdtx.s.syn, err_tdtx.s.wayidx); 74 75 if (err_tdtx.s.dbe) { 76 snprintf(buf2, sizeof(buf2), 77 "L2D Double bit error (detected):%s", buf1); 78 err_tdtx_reset.s.dbe = 1; 79 edac_device_handle_ue(l2c, tad, 1, buf2); 80 } 81 if (err_tdtx.s.sbe) { 82 snprintf(buf2, sizeof(buf2), 83 "L2D Single bit error (corrected):%s", buf1); 84 err_tdtx_reset.s.sbe = 1; 85 edac_device_handle_ce(l2c, tad, 1, buf2); 86 } 87 if (err_tdtx.s.vdbe) { 88 snprintf(buf2, sizeof(buf2), 89 "VBF Double bit error (detected):%s", buf1); 90 err_tdtx_reset.s.vdbe = 1; 91 edac_device_handle_ue(l2c, tad, 1, buf2); 92 } 93 if (err_tdtx.s.vsbe) { 94 snprintf(buf2, sizeof(buf2), 95 "VBF Single bit error (corrected):%s", buf1); 96 err_tdtx_reset.s.vsbe = 1; 97 edac_device_handle_ce(l2c, tad, 1, buf2); 98 } 99 if (err_tdtx_reset.u64) 100 cvmx_write_csr(CVMX_L2C_ERR_TDTX(tad), err_tdtx_reset.u64); 101 102 err_ttgx_reset.u64 = 0; 103 err_ttgx.u64 = cvmx_read_csr(CVMX_L2C_ERR_TTGX(tad)); 104 105 if (err_ttgx.s.dbe || err_ttgx.s.sbe) 106 snprintf(buf1, sizeof(buf1), 107 "type:%d, syn:0x%x, way:%d", 108 err_ttgx.s.type, err_ttgx.s.syn, err_ttgx.s.wayidx); 109 110 if (err_ttgx.s.dbe) { 111 snprintf(buf2, sizeof(buf2), 112 "Tag Double bit error (detected):%s", buf1); 113 err_ttgx_reset.s.dbe = 1; 114 edac_device_handle_ue(l2c, tad, 0, buf2); 115 } 116 if (err_ttgx.s.sbe) { 117 snprintf(buf2, sizeof(buf2), 118 "Tag Single bit error (corrected):%s", buf1); 119 err_ttgx_reset.s.sbe = 1; 120 edac_device_handle_ce(l2c, tad, 0, buf2); 121 } 122 if (err_ttgx_reset.u64) 123 cvmx_write_csr(CVMX_L2C_ERR_TTGX(tad), err_ttgx_reset.u64); 124 } 125 126 static void octeon_l2c_poll_oct2(struct edac_device_ctl_info *l2c) 127 { 128 int i; 129 for (i = 0; i < l2c->nr_instances; i++) 130 _octeon_l2c_poll_oct2(l2c, i); 131 } 132 133 static int octeon_l2c_probe(struct platform_device *pdev) 134 { 135 struct edac_device_ctl_info *l2c; 136 137 int num_tads = OCTEON_IS_MODEL(OCTEON_CN68XX) ? 4 : 1; 138 139 /* 'Tags' are block 0, 'Data' is block 1*/ 140 l2c = edac_device_alloc_ctl_info(0, "l2c", num_tads, "l2c", 2, 0, 141 edac_device_alloc_index()); 142 if (!l2c) 143 return -ENOMEM; 144 145 l2c->dev = &pdev->dev; 146 platform_set_drvdata(pdev, l2c); 147 l2c->dev_name = dev_name(&pdev->dev); 148 149 l2c->mod_name = "octeon-l2c"; 150 l2c->ctl_name = "octeon_l2c_err"; 151 152 153 if (OCTEON_IS_OCTEON1PLUS()) { 154 union cvmx_l2t_err l2t_err; 155 union cvmx_l2d_err l2d_err; 156 157 l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR); 158 l2t_err.s.sec_intena = 0; /* We poll */ 159 l2t_err.s.ded_intena = 0; 160 cvmx_write_csr(CVMX_L2T_ERR, l2t_err.u64); 161 162 l2d_err.u64 = cvmx_read_csr(CVMX_L2D_ERR); 163 l2d_err.s.sec_intena = 0; /* We poll */ 164 l2d_err.s.ded_intena = 0; 165 cvmx_write_csr(CVMX_L2T_ERR, l2d_err.u64); 166 167 l2c->edac_check = octeon_l2c_poll_oct1; 168 } else { 169 /* OCTEON II */ 170 l2c->edac_check = octeon_l2c_poll_oct2; 171 } 172 173 if (edac_device_add_device(l2c) > 0) { 174 pr_err("%s: edac_device_add_device() failed\n", __func__); 175 goto err; 176 } 177 178 179 return 0; 180 181 err: 182 edac_device_free_ctl_info(l2c); 183 184 return -ENXIO; 185 } 186 187 static void octeon_l2c_remove(struct platform_device *pdev) 188 { 189 struct edac_device_ctl_info *l2c = platform_get_drvdata(pdev); 190 191 edac_device_del_device(&pdev->dev); 192 edac_device_free_ctl_info(l2c); 193 } 194 195 static struct platform_driver octeon_l2c_driver = { 196 .probe = octeon_l2c_probe, 197 .remove_new = octeon_l2c_remove, 198 .driver = { 199 .name = "octeon_l2c_edac", 200 } 201 }; 202 module_platform_driver(octeon_l2c_driver); 203 204 MODULE_LICENSE("GPL"); 205 MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>"); 206