blob: 038e89c592521bf6e4c1204392ef9ca3a65446b5 [file] [log] [blame]
Channagoud Kadabi075db3b2017-03-16 14:26:17 -07001/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/kernel.h>
14#include <linux/edac.h>
15#include <linux/of_device.h>
16#include <linux/platform_device.h>
17#include <linux/smp.h>
18#include <linux/spinlock.h>
19#include <linux/mfd/syscon.h>
20#include <linux/regmap.h>
Channagoud Kadabic26a8912016-11-21 13:57:20 -080021#include <linux/interrupt.h>
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070022#include "edac_core.h"
23
24#ifdef CONFIG_EDAC_QCOM_LLCC_PANIC_ON_CE
25#define LLCC_ERP_PANIC_ON_CE 1
26#else
27#define LLCC_ERP_PANIC_ON_CE 0
28#endif
29
30#ifdef CONFIG_EDAC_QCOM_LLCC_PANIC_ON_UE
31#define LLCC_ERP_PANIC_ON_UE 1
32#else
33#define LLCC_ERP_PANIC_ON_UE 0
34#endif
35
36#define EDAC_LLCC "qcom_llcc"
37
38#define TRP_SYN_REG_CNT 6
39
40#define DRP_SYN_REG_CNT 8
41
Channagoud Kadabic0a72e72017-03-27 21:57:09 -070042#define LLCC_COMMON_STATUS0 0x0003000C
43#define LLCC_LB_CNT_MASK 0xf0000000
44#define LLCC_LB_CNT_SHIFT 28
45
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070046/* single & Double Bit syndrome register offsets */
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -070047#define TRP_ECC_SB_ERR_SYN0 0x0002304C
48#define TRP_ECC_DB_ERR_SYN0 0x00020370
49#define DRP_ECC_SB_ERR_SYN0 0x0004204C
50#define DRP_ECC_DB_ERR_SYN0 0x00042070
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070051
52/* Error register offsets */
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -070053#define TRP_ECC_ERROR_STATUS1 0x00020348
54#define TRP_ECC_ERROR_STATUS0 0x00020344
55#define DRP_ECC_ERROR_STATUS1 0x00042048
56#define DRP_ECC_ERROR_STATUS0 0x00042044
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070057
58/* TRP, DRP interrupt register offsets */
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -070059#define DRP_INTERRUPT_STATUS 0x00041000
60#define TRP_INTERRUPT_0_STATUS 0x00020480
61#define DRP_INTERRUPT_CLEAR 0x00041008
62#define DRP_ECC_ERROR_CNTR_CLEAR 0x00040004
63#define TRP_INTERRUPT_0_CLEAR 0x00020484
64#define TRP_ECC_ERROR_CNTR_CLEAR 0x00020440
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070065
66/* Mask and shift macros */
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -070067#define ECC_DB_ERR_COUNT_MASK 0x0000001f
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070068#define ECC_DB_ERR_WAYS_MASK 0xffff0000
69#define ECC_DB_ERR_WAYS_SHIFT 16
70
71#define ECC_SB_ERR_COUNT_MASK 0x00ff0000
72#define ECC_SB_ERR_COUNT_SHIFT 16
73#define ECC_SB_ERR_WAYS_MASK 0x0000ffff
74
75#define SB_ECC_ERROR 0x1
76#define DB_ECC_ERROR 0x2
77
78#define DRP_TRP_INT_CLEAR 0x3
79#define DRP_TRP_CNT_CLEAR 0x3
80
Channagoud Kadabi13d9d332017-04-14 20:41:15 -070081#ifdef CONFIG_EDAC_LLCC_POLL
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070082static int poll_msec = 5000;
83module_param(poll_msec, int, 0444);
Channagoud Kadabi13d9d332017-04-14 20:41:15 -070084#endif
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070085
Channagoud Kadabi13d9d332017-04-14 20:41:15 -070086static int interrupt_mode = 1;
Channagoud Kadabic26a8912016-11-21 13:57:20 -080087module_param(interrupt_mode, int, 0444);
88MODULE_PARM_DESC(interrupt_mode,
89 "Controls whether to use interrupt or poll mode");
90
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070091enum {
92 LLCC_DRAM_CE = 0,
93 LLCC_DRAM_UE,
94 LLCC_TRAM_CE,
95 LLCC_TRAM_UE,
96};
97
98struct errors_edac {
99 const char *msg;
100 void (*func)(struct edac_device_ctl_info *edev_ctl,
101 int inst_nr, int block_nr, const char *msg);
102};
103
104struct erp_drvdata {
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700105 struct regmap *llcc_map;
Channagoud Kadabi934571e2017-06-05 10:57:25 -0700106 u32 *llcc_banks;
Channagoud Kadabic26a8912016-11-21 13:57:20 -0800107 u32 ecc_irq;
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700108 u32 num_banks;
109 u32 b_off;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700110};
111
112static const struct errors_edac errors[] = {
113 {"LLCC Data RAM correctable Error", edac_device_handle_ce},
114 {"LLCC Data RAM uncorrectable Error", edac_device_handle_ue},
115 {"LLCC Tag RAM correctable Error", edac_device_handle_ce},
116 {"LLCC Tag RAM uncorrectable Error", edac_device_handle_ue},
117};
118
119/* Clear the error interrupt and counter registers */
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700120static void qcom_llcc_clear_errors(int err_type, struct erp_drvdata *drv)
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700121{
122 switch (err_type) {
123 case LLCC_DRAM_CE:
124 case LLCC_DRAM_UE:
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700125 /* Clear the interrupt */
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700126 regmap_write(drv->llcc_map, drv->b_off + DRP_INTERRUPT_CLEAR,
127 DRP_TRP_INT_CLEAR);
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700128 /* Clear the counters */
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700129 regmap_write(drv->llcc_map,
130 drv->b_off + DRP_ECC_ERROR_CNTR_CLEAR,
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700131 DRP_TRP_CNT_CLEAR);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700132 break;
133 case LLCC_TRAM_CE:
134 case LLCC_TRAM_UE:
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700135 regmap_write(drv->llcc_map, drv->b_off + TRP_INTERRUPT_0_CLEAR,
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700136 DRP_TRP_INT_CLEAR);
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700137 regmap_write(drv->llcc_map,
138 drv->b_off + TRP_ECC_ERROR_CNTR_CLEAR,
139 DRP_TRP_CNT_CLEAR);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700140 break;
141 }
142}
143
144/* Dump syndrome registers for tag Ram Double bit errors */
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700145static void dump_trp_db_syn_reg(struct erp_drvdata *drv, u32 bank)
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700146{
147 int i;
148 int db_err_cnt;
149 int db_err_ways;
150 u32 synd_reg;
151 u32 synd_val;
152
153 for (i = 0; i < TRP_SYN_REG_CNT; i++) {
154 synd_reg = TRP_ECC_DB_ERR_SYN0 + (i * 4);
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700155 regmap_read(drv->llcc_map, drv->llcc_banks[bank] + synd_reg,
156 &synd_val);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700157 edac_printk(KERN_CRIT, EDAC_LLCC, "TRP_ECC_SYN%d: 0x%8x\n",
158 i, synd_val);
159 }
160
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700161 regmap_read(drv->llcc_map,
162 drv->llcc_banks[bank] + TRP_ECC_ERROR_STATUS1, &db_err_cnt);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700163 db_err_cnt = (db_err_cnt & ECC_DB_ERR_COUNT_MASK);
164 edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error count: 0x%4x\n",
165 db_err_cnt);
166
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700167 regmap_read(drv->llcc_map,
168 drv->llcc_banks[bank] + TRP_ECC_ERROR_STATUS0, &db_err_ways);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700169 db_err_ways = (db_err_ways & ECC_DB_ERR_WAYS_MASK);
170 db_err_ways >>= ECC_DB_ERR_WAYS_SHIFT;
171
172 edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error ways: 0x%4x\n",
173 db_err_ways);
174}
175
176/* Dump syndrome register for tag Ram Single Bit Errors */
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700177static void dump_trp_sb_syn_reg(struct erp_drvdata *drv, u32 bank)
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700178{
179 int i;
180 int sb_err_cnt;
181 int sb_err_ways;
182 u32 synd_reg;
183 u32 synd_val;
184
185 for (i = 0; i < TRP_SYN_REG_CNT; i++) {
186 synd_reg = TRP_ECC_SB_ERR_SYN0 + (i * 4);
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700187 regmap_read(drv->llcc_map, drv->llcc_banks[bank] + synd_reg,
188 &synd_val);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700189 edac_printk(KERN_CRIT, EDAC_LLCC, "TRP_ECC_SYN%d: 0x%8x\n",
190 i, synd_val);
191 }
192
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700193 regmap_read(drv->llcc_map,
194 drv->llcc_banks[bank] + TRP_ECC_ERROR_STATUS1, &sb_err_cnt);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700195 sb_err_cnt = (sb_err_cnt & ECC_SB_ERR_COUNT_MASK);
196 sb_err_cnt >>= ECC_SB_ERR_COUNT_SHIFT;
197 edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error count: 0x%4x\n",
198 sb_err_cnt);
199
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700200 regmap_read(drv->llcc_map,
201 drv->llcc_banks[bank] + TRP_ECC_ERROR_STATUS0, &sb_err_ways);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700202 sb_err_ways = sb_err_ways & ECC_SB_ERR_WAYS_MASK;
203
204 edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error ways: 0x%4x\n",
205 sb_err_ways);
206}
207
208/* Dump syndrome registers for Data Ram Double bit errors */
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700209static void dump_drp_db_syn_reg(struct erp_drvdata *drv, u32 bank)
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700210{
211 int i;
212 int db_err_cnt;
213 int db_err_ways;
214 u32 synd_reg;
215 u32 synd_val;
216
217 for (i = 0; i < DRP_SYN_REG_CNT; i++) {
218 synd_reg = DRP_ECC_DB_ERR_SYN0 + (i * 4);
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700219 regmap_read(drv->llcc_map, drv->llcc_banks[bank] + synd_reg,
220 &synd_val);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700221 edac_printk(KERN_CRIT, EDAC_LLCC, "DRP_ECC_SYN%d: 0x%8x\n",
222 i, synd_val);
223 }
224
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700225 regmap_read(drv->llcc_map,
226 drv->llcc_banks[bank] + DRP_ECC_ERROR_STATUS1, &db_err_cnt);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700227 db_err_cnt = (db_err_cnt & ECC_DB_ERR_COUNT_MASK);
228 edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error count: 0x%4x\n",
229 db_err_cnt);
230
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700231 regmap_read(drv->llcc_map,
232 drv->llcc_banks[bank] + DRP_ECC_ERROR_STATUS0, &db_err_ways);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700233 db_err_ways &= ECC_DB_ERR_WAYS_MASK;
234 db_err_ways >>= ECC_DB_ERR_WAYS_SHIFT;
235 edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error ways: 0x%4x\n",
236 db_err_ways);
237}
238
239/* Dump Syndrome registers for Data Ram Single bit errors*/
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700240static void dump_drp_sb_syn_reg(struct erp_drvdata *drv, u32 bank)
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700241{
242 int i;
243 int sb_err_cnt;
244 int sb_err_ways;
245 u32 synd_reg;
246 u32 synd_val;
247
248 for (i = 0; i < DRP_SYN_REG_CNT; i++) {
Channagoud Kadabi075db3b2017-03-16 14:26:17 -0700249 synd_reg = DRP_ECC_SB_ERR_SYN0 + (i * 4);
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700250 regmap_read(drv->llcc_map, drv->llcc_banks[bank] + synd_reg,
251 &synd_val);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700252 edac_printk(KERN_CRIT, EDAC_LLCC, "DRP_ECC_SYN%d: 0x%8x\n",
253 i, synd_val);
254 }
255
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700256 regmap_read(drv->llcc_map,
257 drv->llcc_banks[bank] + DRP_ECC_ERROR_STATUS1, &sb_err_cnt);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700258 sb_err_cnt &= ECC_SB_ERR_COUNT_MASK;
259 sb_err_cnt >>= ECC_SB_ERR_COUNT_SHIFT;
260 edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error count: 0x%4x\n",
261 sb_err_cnt);
262
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700263 regmap_read(drv->llcc_map,
264 drv->llcc_banks[bank] + DRP_ECC_ERROR_STATUS0, &sb_err_ways);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700265 sb_err_ways = sb_err_ways & ECC_SB_ERR_WAYS_MASK;
266
267 edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error ways: 0x%4x\n",
268 sb_err_ways);
269}
270
271
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700272static void dump_syn_reg(struct edac_device_ctl_info *edev_ctl,
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700273 int err_type, u32 bank)
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700274{
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700275 struct erp_drvdata *drv = edev_ctl->pvt_info;
276
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700277 switch (err_type) {
278 case LLCC_DRAM_CE:
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700279 dump_drp_sb_syn_reg(drv, bank);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700280 break;
281 case LLCC_DRAM_UE:
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700282 dump_drp_db_syn_reg(drv, bank);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700283 break;
284 case LLCC_TRAM_CE:
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700285 dump_trp_sb_syn_reg(drv, bank);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700286 break;
287 case LLCC_TRAM_UE:
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700288 dump_trp_db_syn_reg(drv, bank);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700289 break;
290 }
291
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700292 qcom_llcc_clear_errors(err_type, drv);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700293
Channagoud Kadabi78144392017-08-09 13:25:52 -0700294 errors[err_type].func(edev_ctl, 0, bank, errors[err_type].msg);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700295}
296
Channagoud Kadabic26a8912016-11-21 13:57:20 -0800297static void qcom_llcc_check_cache_errors
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700298 (struct edac_device_ctl_info *edev_ctl)
299{
300 u32 drp_error;
301 u32 trp_error;
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700302 struct erp_drvdata *drv = edev_ctl->pvt_info;
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700303 u32 i;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700304
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700305 for (i = 0; i < drv->num_banks; i++) {
306 /* Look for Data RAM errors */
307 regmap_read(drv->llcc_map,
308 drv->llcc_banks[i] + DRP_INTERRUPT_STATUS, &drp_error);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700309
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700310 if (drp_error & SB_ECC_ERROR) {
311 edac_printk(KERN_CRIT, EDAC_LLCC,
312 "Single Bit Error detected in Data Ram\n");
313 dump_syn_reg(edev_ctl, LLCC_DRAM_CE, i);
314 } else if (drp_error & DB_ECC_ERROR) {
315 edac_printk(KERN_CRIT, EDAC_LLCC,
316 "Double Bit Error detected in Data Ram\n");
317 dump_syn_reg(edev_ctl, LLCC_DRAM_UE, i);
318 }
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700319
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700320 /* Look for Tag RAM errors */
321 regmap_read(drv->llcc_map,
322 drv->llcc_banks[i] + TRP_INTERRUPT_0_STATUS,
323 &trp_error);
324 if (trp_error & SB_ECC_ERROR) {
325 edac_printk(KERN_CRIT, EDAC_LLCC,
326 "Single Bit Error detected in Tag Ram\n");
327 dump_syn_reg(edev_ctl, LLCC_TRAM_CE, i);
328 } else if (trp_error & DB_ECC_ERROR) {
329 edac_printk(KERN_CRIT, EDAC_LLCC,
330 "Double Bit Error detected in Tag Ram\n");
331 dump_syn_reg(edev_ctl, LLCC_TRAM_UE, i);
332 }
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700333 }
334}
335
Channagoud Kadabi13d9d332017-04-14 20:41:15 -0700336#ifdef CONFIG_EDAC_LLCC_POLL
Channagoud Kadabic26a8912016-11-21 13:57:20 -0800337static void qcom_llcc_poll_cache_errors(struct edac_device_ctl_info *edev_ctl)
338{
339 qcom_llcc_check_cache_errors(edev_ctl);
340}
Channagoud Kadabi13d9d332017-04-14 20:41:15 -0700341#endif
Channagoud Kadabic26a8912016-11-21 13:57:20 -0800342
343static irqreturn_t llcc_ecc_irq_handler
344 (int irq, void *edev_ctl)
345{
346 qcom_llcc_check_cache_errors(edev_ctl);
347 return IRQ_HANDLED;
348}
349
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700350static int qcom_llcc_erp_probe(struct platform_device *pdev)
351{
352 int rc = 0;
353 struct erp_drvdata *drv;
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700354 struct edac_device_ctl_info *edev_ctl;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700355 struct device *dev = &pdev->dev;
Channagoud Kadabi78144392017-08-09 13:25:52 -0700356 u32 num_banks;
357 struct regmap *llcc_map = NULL;
358
359 llcc_map = syscon_node_to_regmap(dev->parent->of_node);
360 if (IS_ERR(llcc_map)) {
361 dev_err(dev, "no regmap for syscon llcc parent\n");
362 return -ENOMEM;
363 }
364
365 /* Find the number of LLC banks supported */
366 regmap_read(llcc_map, LLCC_COMMON_STATUS0,
367 &num_banks);
368
369 num_banks &= LLCC_LB_CNT_MASK;
370 num_banks >>= LLCC_LB_CNT_SHIFT;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700371
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700372 /* Allocate edac control info */
373 edev_ctl = edac_device_alloc_ctl_info(sizeof(*drv), "qcom-llcc", 1,
Channagoud Kadabi78144392017-08-09 13:25:52 -0700374 "bank", num_banks, 1, NULL, 0,
375 edac_device_alloc_index());
Channagoud Kadabi934571e2017-06-05 10:57:25 -0700376
377 if (!edev_ctl)
378 return -ENOMEM;
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700379
380 edev_ctl->dev = dev;
381 edev_ctl->mod_name = dev_name(dev);
382 edev_ctl->dev_name = dev_name(dev);
383 edev_ctl->ctl_name = "llcc";
Channagoud Kadabi13d9d332017-04-14 20:41:15 -0700384#ifdef CONFIG_EDAC_LLCC_POLL
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700385 edev_ctl->poll_msec = poll_msec;
386 edev_ctl->edac_check = qcom_llcc_poll_cache_errors;
387 edev_ctl->defer_work = 1;
Channagoud Kadabi13d9d332017-04-14 20:41:15 -0700388#endif
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700389 edev_ctl->panic_on_ce = LLCC_ERP_PANIC_ON_CE;
390 edev_ctl->panic_on_ue = LLCC_ERP_PANIC_ON_UE;
391
392 drv = edev_ctl->pvt_info;
Channagoud Kadabi78144392017-08-09 13:25:52 -0700393 drv->num_banks = num_banks;
394 drv->llcc_map = llcc_map;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700395
Channagoud Kadabi78144392017-08-09 13:25:52 -0700396 rc = edac_device_add_device(edev_ctl);
397 if (rc)
398 goto out_mem;
Channagoud Kadabic26a8912016-11-21 13:57:20 -0800399
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700400 drv->llcc_banks = devm_kzalloc(&pdev->dev,
Channagoud Kadabi934571e2017-06-05 10:57:25 -0700401 sizeof(u32) * drv->num_banks, GFP_KERNEL);
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700402
Channagoud Kadabi934571e2017-06-05 10:57:25 -0700403 if (!drv->llcc_banks) {
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700404 dev_err(dev, "Cannot allocate memory for llcc_banks\n");
Channagoud Kadabi78144392017-08-09 13:25:52 -0700405 rc = -ENOMEM;
406 goto out_dev;
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700407 }
408
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700409 rc = of_property_read_u32_array(dev->parent->of_node,
Channagoud Kadabi934571e2017-06-05 10:57:25 -0700410 "qcom,llcc-banks-off", drv->llcc_banks, drv->num_banks);
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700411 if (rc) {
412 dev_err(dev, "Cannot read llcc-banks-off property\n");
Channagoud Kadabi78144392017-08-09 13:25:52 -0700413 goto out_dev;
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700414 }
415
416 rc = of_property_read_u32(dev->parent->of_node,
417 "qcom,llcc-broadcast-off", &drv->b_off);
418 if (rc) {
419 dev_err(dev, "Cannot read llcc-broadcast-off property\n");
Channagoud Kadabi78144392017-08-09 13:25:52 -0700420 goto out_dev;
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700421 }
422
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700423 platform_set_drvdata(pdev, edev_ctl);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700424
Channagoud Kadabi31a58792017-08-31 16:34:05 -0700425 if (interrupt_mode) {
426 drv->ecc_irq = platform_get_irq_byname(pdev, "ecc_irq");
427 if (!drv->ecc_irq) {
428 rc = -ENODEV;
429 goto out_dev;
430 }
431
432 rc = devm_request_irq(dev, drv->ecc_irq, llcc_ecc_irq_handler,
433 IRQF_TRIGGER_HIGH, "llcc_ecc", edev_ctl);
434 if (rc) {
435 dev_err(dev, "failed to request ecc irq\n");
436 goto out_dev;
437 }
438 }
439
Channagoud Kadabi78144392017-08-09 13:25:52 -0700440 return 0;
441
442out_dev:
443 edac_device_del_device(edev_ctl->dev);
444out_mem:
445 edac_device_free_ctl_info(edev_ctl);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700446
447 return rc;
448}
449
450static int qcom_llcc_erp_remove(struct platform_device *pdev)
451{
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700452 struct edac_device_ctl_info *edev_ctl = dev_get_drvdata(&pdev->dev);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700453
454 edac_device_del_device(edev_ctl->dev);
455 edac_device_free_ctl_info(edev_ctl);
456
457 return 0;
458}
459
460static const struct of_device_id qcom_llcc_erp_match_table[] = {
461 { .compatible = "qcom,llcc-erp" },
462 { },
463};
464
465static struct platform_driver qcom_llcc_erp_driver = {
466 .probe = qcom_llcc_erp_probe,
467 .remove = qcom_llcc_erp_remove,
468 .driver = {
469 .name = "qcom_llcc_erp",
470 .owner = THIS_MODULE,
471 .of_match_table = qcom_llcc_erp_match_table,
472 },
473};
474
475static int __init qcom_llcc_erp_init(void)
476{
477 return platform_driver_register(&qcom_llcc_erp_driver);
478}
479module_init(qcom_llcc_erp_init);
480
481static void __exit qcom_llcc_erp_exit(void)
482{
483 platform_driver_unregister(&qcom_llcc_erp_driver);
484}
485module_exit(qcom_llcc_erp_exit);
486
487MODULE_DESCRIPTION("QCOM LLCC Error Reporting");
488MODULE_LICENSE("GPL v2");