blob: 6bec86073329d22540ad66c4ffc1a1b4187552d1 [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
81static int poll_msec = 5000;
82module_param(poll_msec, int, 0444);
83
Channagoud Kadabic26a8912016-11-21 13:57:20 -080084static int interrupt_mode;
85module_param(interrupt_mode, int, 0444);
86MODULE_PARM_DESC(interrupt_mode,
87 "Controls whether to use interrupt or poll mode");
88
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070089enum {
90 LLCC_DRAM_CE = 0,
91 LLCC_DRAM_UE,
92 LLCC_TRAM_CE,
93 LLCC_TRAM_UE,
94};
95
96struct errors_edac {
97 const char *msg;
98 void (*func)(struct edac_device_ctl_info *edev_ctl,
99 int inst_nr, int block_nr, const char *msg);
100};
101
102struct erp_drvdata {
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700103 struct regmap *llcc_map;
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700104 phys_addr_t *llcc_banks;
Channagoud Kadabic26a8912016-11-21 13:57:20 -0800105 u32 ecc_irq;
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700106 u32 num_banks;
107 u32 b_off;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700108};
109
110static const struct errors_edac errors[] = {
111 {"LLCC Data RAM correctable Error", edac_device_handle_ce},
112 {"LLCC Data RAM uncorrectable Error", edac_device_handle_ue},
113 {"LLCC Tag RAM correctable Error", edac_device_handle_ce},
114 {"LLCC Tag RAM uncorrectable Error", edac_device_handle_ue},
115};
116
117/* Clear the error interrupt and counter registers */
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700118static void qcom_llcc_clear_errors(int err_type, struct erp_drvdata *drv)
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700119{
120 switch (err_type) {
121 case LLCC_DRAM_CE:
122 case LLCC_DRAM_UE:
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700123 /* Clear the interrupt */
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700124 regmap_write(drv->llcc_map, drv->b_off + DRP_INTERRUPT_CLEAR,
125 DRP_TRP_INT_CLEAR);
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700126 /* Clear the counters */
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700127 regmap_write(drv->llcc_map,
128 drv->b_off + DRP_ECC_ERROR_CNTR_CLEAR,
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700129 DRP_TRP_CNT_CLEAR);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700130 break;
131 case LLCC_TRAM_CE:
132 case LLCC_TRAM_UE:
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700133 regmap_write(drv->llcc_map, drv->b_off + TRP_INTERRUPT_0_CLEAR,
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700134 DRP_TRP_INT_CLEAR);
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700135 regmap_write(drv->llcc_map,
136 drv->b_off + TRP_ECC_ERROR_CNTR_CLEAR,
137 DRP_TRP_CNT_CLEAR);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700138 break;
139 }
140}
141
142/* Dump syndrome registers for tag Ram Double bit errors */
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700143static void dump_trp_db_syn_reg(struct erp_drvdata *drv, u32 bank)
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700144{
145 int i;
146 int db_err_cnt;
147 int db_err_ways;
148 u32 synd_reg;
149 u32 synd_val;
150
151 for (i = 0; i < TRP_SYN_REG_CNT; i++) {
152 synd_reg = TRP_ECC_DB_ERR_SYN0 + (i * 4);
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700153 regmap_read(drv->llcc_map, drv->llcc_banks[bank] + synd_reg,
154 &synd_val);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700155 edac_printk(KERN_CRIT, EDAC_LLCC, "TRP_ECC_SYN%d: 0x%8x\n",
156 i, synd_val);
157 }
158
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700159 regmap_read(drv->llcc_map,
160 drv->llcc_banks[bank] + TRP_ECC_ERROR_STATUS1, &db_err_cnt);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700161 db_err_cnt = (db_err_cnt & ECC_DB_ERR_COUNT_MASK);
162 edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error count: 0x%4x\n",
163 db_err_cnt);
164
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700165 regmap_read(drv->llcc_map,
166 drv->llcc_banks[bank] + TRP_ECC_ERROR_STATUS0, &db_err_ways);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700167 db_err_ways = (db_err_ways & ECC_DB_ERR_WAYS_MASK);
168 db_err_ways >>= ECC_DB_ERR_WAYS_SHIFT;
169
170 edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error ways: 0x%4x\n",
171 db_err_ways);
172}
173
174/* Dump syndrome register for tag Ram Single Bit Errors */
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700175static void dump_trp_sb_syn_reg(struct erp_drvdata *drv, u32 bank)
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700176{
177 int i;
178 int sb_err_cnt;
179 int sb_err_ways;
180 u32 synd_reg;
181 u32 synd_val;
182
183 for (i = 0; i < TRP_SYN_REG_CNT; i++) {
184 synd_reg = TRP_ECC_SB_ERR_SYN0 + (i * 4);
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700185 regmap_read(drv->llcc_map, drv->llcc_banks[bank] + synd_reg,
186 &synd_val);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700187 edac_printk(KERN_CRIT, EDAC_LLCC, "TRP_ECC_SYN%d: 0x%8x\n",
188 i, synd_val);
189 }
190
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700191 regmap_read(drv->llcc_map,
192 drv->llcc_banks[bank] + TRP_ECC_ERROR_STATUS1, &sb_err_cnt);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700193 sb_err_cnt = (sb_err_cnt & ECC_SB_ERR_COUNT_MASK);
194 sb_err_cnt >>= ECC_SB_ERR_COUNT_SHIFT;
195 edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error count: 0x%4x\n",
196 sb_err_cnt);
197
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700198 regmap_read(drv->llcc_map,
199 drv->llcc_banks[bank] + TRP_ECC_ERROR_STATUS0, &sb_err_ways);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700200 sb_err_ways = sb_err_ways & ECC_SB_ERR_WAYS_MASK;
201
202 edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error ways: 0x%4x\n",
203 sb_err_ways);
204}
205
206/* Dump syndrome registers for Data Ram Double bit errors */
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700207static void dump_drp_db_syn_reg(struct erp_drvdata *drv, u32 bank)
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700208{
209 int i;
210 int db_err_cnt;
211 int db_err_ways;
212 u32 synd_reg;
213 u32 synd_val;
214
215 for (i = 0; i < DRP_SYN_REG_CNT; i++) {
216 synd_reg = DRP_ECC_DB_ERR_SYN0 + (i * 4);
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700217 regmap_read(drv->llcc_map, drv->llcc_banks[bank] + synd_reg,
218 &synd_val);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700219 edac_printk(KERN_CRIT, EDAC_LLCC, "DRP_ECC_SYN%d: 0x%8x\n",
220 i, synd_val);
221 }
222
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700223 regmap_read(drv->llcc_map,
224 drv->llcc_banks[bank] + DRP_ECC_ERROR_STATUS1, &db_err_cnt);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700225 db_err_cnt = (db_err_cnt & ECC_DB_ERR_COUNT_MASK);
226 edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error count: 0x%4x\n",
227 db_err_cnt);
228
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700229 regmap_read(drv->llcc_map,
230 drv->llcc_banks[bank] + DRP_ECC_ERROR_STATUS0, &db_err_ways);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700231 db_err_ways &= ECC_DB_ERR_WAYS_MASK;
232 db_err_ways >>= ECC_DB_ERR_WAYS_SHIFT;
233 edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error ways: 0x%4x\n",
234 db_err_ways);
235}
236
237/* Dump Syndrome registers for Data Ram Single bit errors*/
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700238static void dump_drp_sb_syn_reg(struct erp_drvdata *drv, u32 bank)
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700239{
240 int i;
241 int sb_err_cnt;
242 int sb_err_ways;
243 u32 synd_reg;
244 u32 synd_val;
245
246 for (i = 0; i < DRP_SYN_REG_CNT; i++) {
Channagoud Kadabi075db3b2017-03-16 14:26:17 -0700247 synd_reg = DRP_ECC_SB_ERR_SYN0 + (i * 4);
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700248 regmap_read(drv->llcc_map, drv->llcc_banks[bank] + synd_reg,
249 &synd_val);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700250 edac_printk(KERN_CRIT, EDAC_LLCC, "DRP_ECC_SYN%d: 0x%8x\n",
251 i, synd_val);
252 }
253
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700254 regmap_read(drv->llcc_map,
255 drv->llcc_banks[bank] + DRP_ECC_ERROR_STATUS1, &sb_err_cnt);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700256 sb_err_cnt &= ECC_SB_ERR_COUNT_MASK;
257 sb_err_cnt >>= ECC_SB_ERR_COUNT_SHIFT;
258 edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error count: 0x%4x\n",
259 sb_err_cnt);
260
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700261 regmap_read(drv->llcc_map,
262 drv->llcc_banks[bank] + DRP_ECC_ERROR_STATUS0, &sb_err_ways);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700263 sb_err_ways = sb_err_ways & ECC_SB_ERR_WAYS_MASK;
264
265 edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error ways: 0x%4x\n",
266 sb_err_ways);
267}
268
269
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700270static void dump_syn_reg(struct edac_device_ctl_info *edev_ctl,
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700271 int err_type, u32 bank)
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700272{
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700273 struct erp_drvdata *drv = edev_ctl->pvt_info;
274
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700275 switch (err_type) {
276 case LLCC_DRAM_CE:
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700277 dump_drp_sb_syn_reg(drv, bank);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700278 break;
279 case LLCC_DRAM_UE:
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700280 dump_drp_db_syn_reg(drv, bank);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700281 break;
282 case LLCC_TRAM_CE:
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700283 dump_trp_sb_syn_reg(drv, bank);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700284 break;
285 case LLCC_TRAM_UE:
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700286 dump_trp_db_syn_reg(drv, bank);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700287 break;
288 }
289
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700290 qcom_llcc_clear_errors(err_type, drv);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700291
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700292 errors[err_type].func(edev_ctl, 0, 0, errors[err_type].msg);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700293}
294
Channagoud Kadabic26a8912016-11-21 13:57:20 -0800295static void qcom_llcc_check_cache_errors
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700296 (struct edac_device_ctl_info *edev_ctl)
297{
298 u32 drp_error;
299 u32 trp_error;
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700300 struct erp_drvdata *drv = edev_ctl->pvt_info;
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700301 u32 i;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700302
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700303 for (i = 0; i < drv->num_banks; i++) {
304 /* Look for Data RAM errors */
305 regmap_read(drv->llcc_map,
306 drv->llcc_banks[i] + DRP_INTERRUPT_STATUS, &drp_error);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700307
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700308 if (drp_error & SB_ECC_ERROR) {
309 edac_printk(KERN_CRIT, EDAC_LLCC,
310 "Single Bit Error detected in Data Ram\n");
311 dump_syn_reg(edev_ctl, LLCC_DRAM_CE, i);
312 } else if (drp_error & DB_ECC_ERROR) {
313 edac_printk(KERN_CRIT, EDAC_LLCC,
314 "Double Bit Error detected in Data Ram\n");
315 dump_syn_reg(edev_ctl, LLCC_DRAM_UE, i);
316 }
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700317
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700318 /* Look for Tag RAM errors */
319 regmap_read(drv->llcc_map,
320 drv->llcc_banks[i] + TRP_INTERRUPT_0_STATUS,
321 &trp_error);
322 if (trp_error & SB_ECC_ERROR) {
323 edac_printk(KERN_CRIT, EDAC_LLCC,
324 "Single Bit Error detected in Tag Ram\n");
325 dump_syn_reg(edev_ctl, LLCC_TRAM_CE, i);
326 } else if (trp_error & DB_ECC_ERROR) {
327 edac_printk(KERN_CRIT, EDAC_LLCC,
328 "Double Bit Error detected in Tag Ram\n");
329 dump_syn_reg(edev_ctl, LLCC_TRAM_UE, i);
330 }
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700331 }
332}
333
Channagoud Kadabic26a8912016-11-21 13:57:20 -0800334static void qcom_llcc_poll_cache_errors(struct edac_device_ctl_info *edev_ctl)
335{
336 qcom_llcc_check_cache_errors(edev_ctl);
337}
338
339static irqreturn_t llcc_ecc_irq_handler
340 (int irq, void *edev_ctl)
341{
342 qcom_llcc_check_cache_errors(edev_ctl);
343 return IRQ_HANDLED;
344}
345
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700346static int qcom_llcc_erp_probe(struct platform_device *pdev)
347{
348 int rc = 0;
349 struct erp_drvdata *drv;
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700350 struct edac_device_ctl_info *edev_ctl;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700351 struct device *dev = &pdev->dev;
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700352 u32 *banks;
353 u32 i;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700354
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700355 /* Allocate edac control info */
356 edev_ctl = edac_device_alloc_ctl_info(sizeof(*drv), "qcom-llcc", 1,
357 NULL, 1, 1, NULL, 0, edac_device_alloc_index());
358
359 edev_ctl->dev = dev;
360 edev_ctl->mod_name = dev_name(dev);
361 edev_ctl->dev_name = dev_name(dev);
362 edev_ctl->ctl_name = "llcc";
363 edev_ctl->poll_msec = poll_msec;
364 edev_ctl->edac_check = qcom_llcc_poll_cache_errors;
365 edev_ctl->defer_work = 1;
366 edev_ctl->panic_on_ce = LLCC_ERP_PANIC_ON_CE;
367 edev_ctl->panic_on_ue = LLCC_ERP_PANIC_ON_UE;
368
369 drv = edev_ctl->pvt_info;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700370
371 drv->llcc_map = syscon_node_to_regmap(dev->parent->of_node);
372 if (IS_ERR(drv->llcc_map)) {
373 dev_err(dev, "no regmap for syscon llcc parent\n");
Channagoud Kadabic26a8912016-11-21 13:57:20 -0800374 rc = -ENOMEM;
375 goto out;
376 }
377
378 if (interrupt_mode) {
379 drv->ecc_irq = platform_get_irq_byname(pdev, "ecc_irq");
380 if (!drv->ecc_irq) {
381 rc = -ENODEV;
382 goto out;
383 }
384
385 rc = devm_request_irq(dev, drv->ecc_irq, llcc_ecc_irq_handler,
386 IRQF_TRIGGER_RISING, "llcc_ecc", edev_ctl);
387 if (rc) {
388 dev_err(dev, "failed to request ecc irq\n");
389 goto out;
390 }
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700391 }
392
Channagoud Kadabic0a72e72017-03-27 21:57:09 -0700393 /* Find the number of LLC banks supported */
394 regmap_read(drv->llcc_map, LLCC_COMMON_STATUS0,
395 &drv->num_banks);
396
397 drv->num_banks &= LLCC_LB_CNT_MASK;
398 drv->num_banks >>= LLCC_LB_CNT_SHIFT;
399
400 drv->llcc_banks = devm_kzalloc(&pdev->dev,
401 sizeof(phys_addr_t) * drv->num_banks, GFP_KERNEL);
402
403 if (!drv->num_banks) {
404 dev_err(dev, "Cannot allocate memory for llcc_banks\n");
405 return -ENOMEM;
406 }
407
408 banks = devm_kzalloc(&pdev->dev,
409 sizeof(u32) * drv->num_banks, GFP_KERNEL);
410 if (!banks)
411 return -ENOMEM;
412
413 rc = of_property_read_u32_array(dev->parent->of_node,
414 "qcom,llcc-banks-off", banks, drv->num_banks);
415 if (rc) {
416 dev_err(dev, "Cannot read llcc-banks-off property\n");
417 return -EINVAL;
418 }
419
420 rc = of_property_read_u32(dev->parent->of_node,
421 "qcom,llcc-broadcast-off", &drv->b_off);
422 if (rc) {
423 dev_err(dev, "Cannot read llcc-broadcast-off property\n");
424 return -EINVAL;
425 }
426
427 for (i = 0; i < drv->num_banks; i++)
428 drv->llcc_banks[i] = banks[i];
429
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700430 platform_set_drvdata(pdev, edev_ctl);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700431
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700432 rc = edac_device_add_device(edev_ctl);
Channagoud Kadabic26a8912016-11-21 13:57:20 -0800433out:
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700434 if (rc)
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700435 edac_device_free_ctl_info(edev_ctl);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700436
437 return rc;
438}
439
440static int qcom_llcc_erp_remove(struct platform_device *pdev)
441{
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700442 struct edac_device_ctl_info *edev_ctl = dev_get_drvdata(&pdev->dev);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700443
444 edac_device_del_device(edev_ctl->dev);
445 edac_device_free_ctl_info(edev_ctl);
446
447 return 0;
448}
449
450static const struct of_device_id qcom_llcc_erp_match_table[] = {
451 { .compatible = "qcom,llcc-erp" },
452 { },
453};
454
455static struct platform_driver qcom_llcc_erp_driver = {
456 .probe = qcom_llcc_erp_probe,
457 .remove = qcom_llcc_erp_remove,
458 .driver = {
459 .name = "qcom_llcc_erp",
460 .owner = THIS_MODULE,
461 .of_match_table = qcom_llcc_erp_match_table,
462 },
463};
464
465static int __init qcom_llcc_erp_init(void)
466{
467 return platform_driver_register(&qcom_llcc_erp_driver);
468}
469module_init(qcom_llcc_erp_init);
470
471static void __exit qcom_llcc_erp_exit(void)
472{
473 platform_driver_unregister(&qcom_llcc_erp_driver);
474}
475module_exit(qcom_llcc_erp_exit);
476
477MODULE_DESCRIPTION("QCOM LLCC Error Reporting");
478MODULE_LICENSE("GPL v2");