MIPS: Cavium: Add EDAC support.
[linux-2.6-block.git] / drivers / edac / octeon_edac-lmc.c
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) 2009 Wind River Systems,
7  *   written by Ralf Baechle <ralf@linux-mips.org>
8  */
9 #include <linux/module.h>
10 #include <linux/init.h>
11 #include <linux/slab.h>
12 #include <linux/io.h>
13 #include <linux/edac.h>
14
15 #include <asm/octeon/cvmx.h>
16
17 #include "edac_core.h"
18 #include "edac_module.h"
19 #include "octeon_edac-lmc.h"
20
21 #define EDAC_MOD_STR "octeon"
22
23 static struct mem_ctl_info *mc_cavium;
24 static void *lmc_base;
25
26 static void co_lmc_poll(struct mem_ctl_info *mci)
27 {
28         union lmc_mem_cfg0 cfg0;
29         union lmc_fadr fadr;
30         char msg[64];
31
32         fadr.u64 = readq(lmc_base + LMC_FADR);
33         cfg0.u64 = readq(lmc_base + LMC_MEM_CFG0);
34         snprintf(msg, sizeof(msg), "DIMM %d rank %d bank %d row %d col %d",
35                 fadr.fdimm, fadr.fbunk, fadr.fbank, fadr.frow, fadr.fcol);
36
37         if (cfg0.sec_err) {
38                 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0, -1, -1, -1,
39                                      msg, "");
40
41                 cfg0.intr_sec_ena = -1;         /* Done, re-arm */
42         }
43
44         if (cfg0.ded_err) {
45                 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, -1, -1, -1,
46                                      msg, "");
47                 cfg0.intr_ded_ena = -1;         /* Done, re-arm */
48         }
49
50         writeq(cfg0.u64, lmc_base + LMC_MEM_CFG0);
51 }
52
53 static int __devinit co_lmc_probe(struct platform_device *pdev)
54 {
55         struct mem_ctl_info *mci;
56         union lmc_mem_cfg0 cfg0;
57         int res = 0;
58
59         mci = edac_mc_alloc(0, 0, 0, 0);
60         if (!mci)
61                 return -ENOMEM;
62
63         mci->pdev = &pdev->dev;
64         platform_set_drvdata(pdev, mci);
65         mci->dev_name = dev_name(&pdev->dev);
66
67         mci->mod_name = "octeon-lmc";
68         mci->ctl_name = "co_lmc_err";
69         mci->edac_check = co_lmc_poll;
70
71         if (edac_mc_add_mc(mci) > 0) {
72                 pr_err("%s: edac_mc_add_mc() failed\n", __func__);
73                 goto err;
74         }
75
76         cfg0.u64 = readq(lmc_base + LMC_MEM_CFG0);      /* We poll */
77         cfg0.intr_ded_ena = 0;
78         cfg0.intr_sec_ena = 0;
79         writeq(cfg0.u64, lmc_base + LMC_MEM_CFG0);
80
81         mc_cavium = mci;
82
83         return 0;
84
85 err:
86         edac_mc_free(mci);
87
88         return res;
89 }
90
91 static int co_lmc_remove(struct platform_device *pdev)
92 {
93         struct mem_ctl_info *mci = platform_get_drvdata(pdev);
94
95         mc_cavium = NULL;
96         edac_mc_del_mc(&pdev->dev);
97         edac_mc_free(mci);
98
99         return 0;
100 }
101
102 static struct platform_driver co_lmc_driver = {
103         .probe = co_lmc_probe,
104         .remove = co_lmc_remove,
105         .driver = {
106                    .name = "co_lmc_edac",
107         }
108 };
109
110 static int __init co_edac_init(void)
111 {
112         union lmc_mem_cfg0 cfg0;
113         int ret;
114
115         lmc_base = ioremap_nocache(LMC_BASE, LMC_SIZE);
116         if (!lmc_base)
117                 return -ENOMEM;
118
119         cfg0.u64 = readq(lmc_base + LMC_MEM_CFG0);
120         if (!cfg0.ecc_ena) {
121                 pr_info(EDAC_MOD_STR " LMC EDAC: ECC disabled, good bye\n");
122                 ret = -ENODEV;
123                 goto out;
124         }
125
126         ret = platform_driver_register(&co_lmc_driver);
127         if (ret) {
128                 pr_warning(EDAC_MOD_STR " LMC EDAC failed to register\n");
129                 goto out;
130         }
131
132         return ret;
133
134 out:
135         iounmap(lmc_base);
136
137         return ret;
138 }
139
140 static void __exit co_edac_exit(void)
141 {
142         platform_driver_unregister(&co_lmc_driver);
143         iounmap(lmc_base);
144 }
145
146 module_init(co_edac_init);
147 module_exit(co_edac_exit);
148
149 MODULE_LICENSE("GPL");
150 MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");