1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Copyright 2018, 2019 Cisco Systems |
4 | */ |
5 | |
6 | #include <linux/edac.h> |
7 | #include <linux/module.h> |
8 | #include <linux/init.h> |
9 | #include <linux/interrupt.h> |
10 | #include <linux/platform_device.h> |
11 | #include <linux/stop_machine.h> |
12 | #include <linux/io.h> |
13 | #include <linux/of_address.h> |
14 | #include <linux/regmap.h> |
15 | #include "edac_module.h" |
16 | |
17 | |
18 | #define DRV_NAME "aspeed-edac" |
19 | |
20 | |
21 | #define ASPEED_MCR_PROT 0x00 /* protection key register */ |
22 | #define ASPEED_MCR_CONF 0x04 /* configuration register */ |
23 | #define ASPEED_MCR_INTR_CTRL 0x50 /* interrupt control/status register */ |
24 | #define ASPEED_MCR_ADDR_UNREC 0x58 /* address of first un-recoverable error */ |
25 | #define ASPEED_MCR_ADDR_REC 0x5c /* address of last recoverable error */ |
26 | #define ASPEED_MCR_LAST ASPEED_MCR_ADDR_REC |
27 | |
28 | |
29 | #define ASPEED_MCR_PROT_PASSWD 0xfc600309 |
30 | #define ASPEED_MCR_CONF_DRAM_TYPE BIT(4) |
31 | #define ASPEED_MCR_CONF_ECC BIT(7) |
32 | #define ASPEED_MCR_INTR_CTRL_CLEAR BIT(31) |
33 | #define ASPEED_MCR_INTR_CTRL_CNT_REC GENMASK(23, 16) |
34 | #define ASPEED_MCR_INTR_CTRL_CNT_UNREC GENMASK(15, 12) |
35 | #define ASPEED_MCR_INTR_CTRL_ENABLE (BIT(0) | BIT(1)) |
36 | |
37 | |
38 | static struct regmap *aspeed_regmap; |
39 | |
40 | |
41 | static int regmap_reg_write(void *context, unsigned int reg, unsigned int val) |
42 | { |
43 | void __iomem *regs = (void __iomem *)context; |
44 | |
45 | /* enable write to MCR register set */ |
46 | writel(ASPEED_MCR_PROT_PASSWD, addr: regs + ASPEED_MCR_PROT); |
47 | |
48 | writel(val, addr: regs + reg); |
49 | |
50 | /* disable write to MCR register set */ |
51 | writel(val: ~ASPEED_MCR_PROT_PASSWD, addr: regs + ASPEED_MCR_PROT); |
52 | |
53 | return 0; |
54 | } |
55 | |
56 | |
57 | static int regmap_reg_read(void *context, unsigned int reg, unsigned int *val) |
58 | { |
59 | void __iomem *regs = (void __iomem *)context; |
60 | |
61 | *val = readl(addr: regs + reg); |
62 | |
63 | return 0; |
64 | } |
65 | |
66 | static bool regmap_is_volatile(struct device *dev, unsigned int reg) |
67 | { |
68 | switch (reg) { |
69 | case ASPEED_MCR_PROT: |
70 | case ASPEED_MCR_INTR_CTRL: |
71 | case ASPEED_MCR_ADDR_UNREC: |
72 | case ASPEED_MCR_ADDR_REC: |
73 | return true; |
74 | default: |
75 | return false; |
76 | } |
77 | } |
78 | |
79 | |
80 | static const struct regmap_config aspeed_regmap_config = { |
81 | .reg_bits = 32, |
82 | .val_bits = 32, |
83 | .reg_stride = 4, |
84 | .max_register = ASPEED_MCR_LAST, |
85 | .reg_write = regmap_reg_write, |
86 | .reg_read = regmap_reg_read, |
87 | .volatile_reg = regmap_is_volatile, |
88 | .fast_io = true, |
89 | }; |
90 | |
91 | |
92 | static void count_rec(struct mem_ctl_info *mci, u8 rec_cnt, u32 rec_addr) |
93 | { |
94 | struct csrow_info *csrow = mci->csrows[0]; |
95 | u32 page, offset, syndrome; |
96 | |
97 | if (!rec_cnt) |
98 | return; |
99 | |
100 | /* report first few errors (if there are) */ |
101 | /* note: no addresses are recorded */ |
102 | if (rec_cnt > 1) { |
103 | /* page, offset and syndrome are not available */ |
104 | page = 0; |
105 | offset = 0; |
106 | syndrome = 0; |
107 | edac_mc_handle_error(type: HW_EVENT_ERR_CORRECTED, mci, error_count: rec_cnt-1, |
108 | page_frame_number: page, offset_in_page: offset, syndrome, top_layer: 0, mid_layer: 0, low_layer: -1, |
109 | msg: "address(es) not available" , other_detail: "" ); |
110 | } |
111 | |
112 | /* report last error */ |
113 | /* note: rec_addr is the last recoverable error addr */ |
114 | page = rec_addr >> PAGE_SHIFT; |
115 | offset = rec_addr & ~PAGE_MASK; |
116 | /* syndrome is not available */ |
117 | syndrome = 0; |
118 | edac_mc_handle_error(type: HW_EVENT_ERR_CORRECTED, mci, error_count: 1, |
119 | page_frame_number: csrow->first_page + page, offset_in_page: offset, syndrome, |
120 | top_layer: 0, mid_layer: 0, low_layer: -1, msg: "" , other_detail: "" ); |
121 | } |
122 | |
123 | |
124 | static void count_un_rec(struct mem_ctl_info *mci, u8 un_rec_cnt, |
125 | u32 un_rec_addr) |
126 | { |
127 | struct csrow_info *csrow = mci->csrows[0]; |
128 | u32 page, offset, syndrome; |
129 | |
130 | if (!un_rec_cnt) |
131 | return; |
132 | |
133 | /* report 1. error */ |
134 | /* note: un_rec_addr is the first unrecoverable error addr */ |
135 | page = un_rec_addr >> PAGE_SHIFT; |
136 | offset = un_rec_addr & ~PAGE_MASK; |
137 | /* syndrome is not available */ |
138 | syndrome = 0; |
139 | edac_mc_handle_error(type: HW_EVENT_ERR_UNCORRECTED, mci, error_count: 1, |
140 | page_frame_number: csrow->first_page + page, offset_in_page: offset, syndrome, |
141 | top_layer: 0, mid_layer: 0, low_layer: -1, msg: "" , other_detail: "" ); |
142 | |
143 | /* report further errors (if there are) */ |
144 | /* note: no addresses are recorded */ |
145 | if (un_rec_cnt > 1) { |
146 | /* page, offset and syndrome are not available */ |
147 | page = 0; |
148 | offset = 0; |
149 | syndrome = 0; |
150 | edac_mc_handle_error(type: HW_EVENT_ERR_UNCORRECTED, mci, error_count: un_rec_cnt-1, |
151 | page_frame_number: page, offset_in_page: offset, syndrome, top_layer: 0, mid_layer: 0, low_layer: -1, |
152 | msg: "address(es) not available" , other_detail: "" ); |
153 | } |
154 | } |
155 | |
156 | |
157 | static irqreturn_t mcr_isr(int irq, void *arg) |
158 | { |
159 | struct mem_ctl_info *mci = arg; |
160 | u32 rec_addr, un_rec_addr; |
161 | u32 reg50, reg5c, reg58; |
162 | u8 rec_cnt, un_rec_cnt; |
163 | |
164 | regmap_read(map: aspeed_regmap, ASPEED_MCR_INTR_CTRL, val: ®50); |
165 | dev_dbg(mci->pdev, "received edac interrupt w/ mcr register 50: 0x%x\n" , |
166 | reg50); |
167 | |
168 | /* collect data about recoverable and unrecoverable errors */ |
169 | rec_cnt = (reg50 & ASPEED_MCR_INTR_CTRL_CNT_REC) >> 16; |
170 | un_rec_cnt = (reg50 & ASPEED_MCR_INTR_CTRL_CNT_UNREC) >> 12; |
171 | |
172 | dev_dbg(mci->pdev, "%d recoverable interrupts and %d unrecoverable interrupts\n" , |
173 | rec_cnt, un_rec_cnt); |
174 | |
175 | regmap_read(map: aspeed_regmap, ASPEED_MCR_ADDR_UNREC, val: ®58); |
176 | un_rec_addr = reg58; |
177 | |
178 | regmap_read(map: aspeed_regmap, ASPEED_MCR_ADDR_REC, val: ®5c); |
179 | rec_addr = reg5c; |
180 | |
181 | /* clear interrupt flags and error counters: */ |
182 | regmap_update_bits(map: aspeed_regmap, ASPEED_MCR_INTR_CTRL, |
183 | ASPEED_MCR_INTR_CTRL_CLEAR, |
184 | ASPEED_MCR_INTR_CTRL_CLEAR); |
185 | |
186 | regmap_update_bits(map: aspeed_regmap, ASPEED_MCR_INTR_CTRL, |
187 | ASPEED_MCR_INTR_CTRL_CLEAR, val: 0); |
188 | |
189 | /* process recoverable and unrecoverable errors */ |
190 | count_rec(mci, rec_cnt, rec_addr); |
191 | count_un_rec(mci, un_rec_cnt, un_rec_addr); |
192 | |
193 | if (!rec_cnt && !un_rec_cnt) |
194 | dev_dbg(mci->pdev, "received edac interrupt, but did not find any ECC counters\n" ); |
195 | |
196 | regmap_read(map: aspeed_regmap, ASPEED_MCR_INTR_CTRL, val: ®50); |
197 | dev_dbg(mci->pdev, "edac interrupt handled. mcr reg 50 is now: 0x%x\n" , |
198 | reg50); |
199 | |
200 | return IRQ_HANDLED; |
201 | } |
202 | |
203 | |
204 | static int config_irq(void *ctx, struct platform_device *pdev) |
205 | { |
206 | int irq; |
207 | int rc; |
208 | |
209 | /* register interrupt handler */ |
210 | irq = platform_get_irq(pdev, 0); |
211 | dev_dbg(&pdev->dev, "got irq %d\n" , irq); |
212 | if (irq < 0) |
213 | return irq; |
214 | |
215 | rc = devm_request_irq(dev: &pdev->dev, irq, handler: mcr_isr, IRQF_TRIGGER_HIGH, |
216 | DRV_NAME, dev_id: ctx); |
217 | if (rc) { |
218 | dev_err(&pdev->dev, "unable to request irq %d\n" , irq); |
219 | return rc; |
220 | } |
221 | |
222 | /* enable interrupts */ |
223 | regmap_update_bits(map: aspeed_regmap, ASPEED_MCR_INTR_CTRL, |
224 | ASPEED_MCR_INTR_CTRL_ENABLE, |
225 | ASPEED_MCR_INTR_CTRL_ENABLE); |
226 | |
227 | return 0; |
228 | } |
229 | |
230 | |
231 | static int init_csrows(struct mem_ctl_info *mci) |
232 | { |
233 | struct csrow_info *csrow = mci->csrows[0]; |
234 | u32 nr_pages, dram_type; |
235 | struct dimm_info *dimm; |
236 | struct device_node *np; |
237 | struct resource r; |
238 | u32 reg04; |
239 | int rc; |
240 | |
241 | /* retrieve info about physical memory from device tree */ |
242 | np = of_find_node_by_name(NULL, name: "memory" ); |
243 | if (!np) { |
244 | dev_err(mci->pdev, "dt: missing /memory node\n" ); |
245 | return -ENODEV; |
246 | } |
247 | |
248 | rc = of_address_to_resource(dev: np, index: 0, r: &r); |
249 | |
250 | of_node_put(node: np); |
251 | |
252 | if (rc) { |
253 | dev_err(mci->pdev, "dt: failed requesting resource for /memory node\n" ); |
254 | return rc; |
255 | } |
256 | |
257 | dev_dbg(mci->pdev, "dt: /memory node resources: first page %pR, PAGE_SHIFT macro=0x%x\n" , |
258 | &r, PAGE_SHIFT); |
259 | |
260 | csrow->first_page = r.start >> PAGE_SHIFT; |
261 | nr_pages = resource_size(res: &r) >> PAGE_SHIFT; |
262 | csrow->last_page = csrow->first_page + nr_pages - 1; |
263 | |
264 | regmap_read(map: aspeed_regmap, ASPEED_MCR_CONF, val: ®04); |
265 | dram_type = (reg04 & ASPEED_MCR_CONF_DRAM_TYPE) ? MEM_DDR4 : MEM_DDR3; |
266 | |
267 | dimm = csrow->channels[0]->dimm; |
268 | dimm->mtype = dram_type; |
269 | dimm->edac_mode = EDAC_SECDED; |
270 | dimm->nr_pages = nr_pages / csrow->nr_channels; |
271 | |
272 | dev_dbg(mci->pdev, "initialized dimm with first_page=0x%lx and nr_pages=0x%x\n" , |
273 | csrow->first_page, nr_pages); |
274 | |
275 | return 0; |
276 | } |
277 | |
278 | |
279 | static int aspeed_probe(struct platform_device *pdev) |
280 | { |
281 | struct device *dev = &pdev->dev; |
282 | struct edac_mc_layer layers[2]; |
283 | struct mem_ctl_info *mci; |
284 | void __iomem *regs; |
285 | u32 reg04; |
286 | int rc; |
287 | |
288 | regs = devm_platform_ioremap_resource(pdev, index: 0); |
289 | if (IS_ERR(ptr: regs)) |
290 | return PTR_ERR(ptr: regs); |
291 | |
292 | aspeed_regmap = devm_regmap_init(dev, NULL, (__force void *)regs, |
293 | &aspeed_regmap_config); |
294 | if (IS_ERR(ptr: aspeed_regmap)) |
295 | return PTR_ERR(ptr: aspeed_regmap); |
296 | |
297 | /* bail out if ECC mode is not configured */ |
298 | regmap_read(map: aspeed_regmap, ASPEED_MCR_CONF, val: ®04); |
299 | if (!(reg04 & ASPEED_MCR_CONF_ECC)) { |
300 | dev_err(&pdev->dev, "ECC mode is not configured in u-boot\n" ); |
301 | return -EPERM; |
302 | } |
303 | |
304 | edac_op_state = EDAC_OPSTATE_INT; |
305 | |
306 | /* allocate & init EDAC MC data structure */ |
307 | layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; |
308 | layers[0].size = 1; |
309 | layers[0].is_virt_csrow = true; |
310 | layers[1].type = EDAC_MC_LAYER_CHANNEL; |
311 | layers[1].size = 1; |
312 | layers[1].is_virt_csrow = false; |
313 | |
314 | mci = edac_mc_alloc(mc_num: 0, ARRAY_SIZE(layers), layers, sz_pvt: 0); |
315 | if (!mci) |
316 | return -ENOMEM; |
317 | |
318 | mci->pdev = &pdev->dev; |
319 | mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR4; |
320 | mci->edac_ctl_cap = EDAC_FLAG_SECDED; |
321 | mci->edac_cap = EDAC_FLAG_SECDED; |
322 | mci->scrub_cap = SCRUB_FLAG_HW_SRC; |
323 | mci->scrub_mode = SCRUB_HW_SRC; |
324 | mci->mod_name = DRV_NAME; |
325 | mci->ctl_name = "MIC" ; |
326 | mci->dev_name = dev_name(dev: &pdev->dev); |
327 | |
328 | rc = init_csrows(mci); |
329 | if (rc) { |
330 | dev_err(&pdev->dev, "failed to init csrows\n" ); |
331 | goto probe_exit02; |
332 | } |
333 | |
334 | platform_set_drvdata(pdev, data: mci); |
335 | |
336 | /* register with edac core */ |
337 | rc = edac_mc_add_mc(mci); |
338 | if (rc) { |
339 | dev_err(&pdev->dev, "failed to register with EDAC core\n" ); |
340 | goto probe_exit02; |
341 | } |
342 | |
343 | /* register interrupt handler and enable interrupts */ |
344 | rc = config_irq(ctx: mci, pdev); |
345 | if (rc) { |
346 | dev_err(&pdev->dev, "failed setting up irq\n" ); |
347 | goto probe_exit01; |
348 | } |
349 | |
350 | return 0; |
351 | |
352 | probe_exit01: |
353 | edac_mc_del_mc(dev: &pdev->dev); |
354 | probe_exit02: |
355 | edac_mc_free(mci); |
356 | return rc; |
357 | } |
358 | |
359 | |
360 | static void aspeed_remove(struct platform_device *pdev) |
361 | { |
362 | struct mem_ctl_info *mci; |
363 | |
364 | /* disable interrupts */ |
365 | regmap_update_bits(map: aspeed_regmap, ASPEED_MCR_INTR_CTRL, |
366 | ASPEED_MCR_INTR_CTRL_ENABLE, val: 0); |
367 | |
368 | /* free resources */ |
369 | mci = edac_mc_del_mc(dev: &pdev->dev); |
370 | if (mci) |
371 | edac_mc_free(mci); |
372 | } |
373 | |
374 | |
375 | static const struct of_device_id aspeed_of_match[] = { |
376 | { .compatible = "aspeed,ast2400-sdram-edac" }, |
377 | { .compatible = "aspeed,ast2500-sdram-edac" }, |
378 | { .compatible = "aspeed,ast2600-sdram-edac" }, |
379 | {}, |
380 | }; |
381 | |
382 | MODULE_DEVICE_TABLE(of, aspeed_of_match); |
383 | |
384 | static struct platform_driver aspeed_driver = { |
385 | .driver = { |
386 | .name = DRV_NAME, |
387 | .of_match_table = aspeed_of_match |
388 | }, |
389 | .probe = aspeed_probe, |
390 | .remove_new = aspeed_remove |
391 | }; |
392 | module_platform_driver(aspeed_driver); |
393 | |
394 | MODULE_LICENSE("GPL" ); |
395 | MODULE_AUTHOR("Stefan Schaeckeler <sschaeck@cisco.com>" ); |
396 | MODULE_DESCRIPTION("Aspeed BMC SoC EDAC driver" ); |
397 | MODULE_VERSION("1.0" ); |
398 | |