keystone_mc_edac.c
4.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/*
* Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
*
* Keystone DDR3 MC ECC error detection driver
*
* TODO: Need to investgiate how to hook this to the edac core driver.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/module.h>
/* DDR3 controller registers */
#define DDR3_EOI 0x0A0
#define DDR3_IRQ_STATUS_RAW_SYS 0x0A4
#define DDR3_IRQ_STATUS_SYS 0x0AC
#define DDR3_IRQ_ENABLE_SET_SYS 0x0B4
#define DDR3_IRQ_ENABLE_CLR_SYS 0x0BC
#define DDR3_ECC_CTRL 0x110
#define DDR3_ONE_BIT_ECC_ERR_CNT 0x130
#define TWO_BIT_ECC_ERR_ADDR_LOG 0x140
#define DDR3_1B_ECC_ERR BIT(5)
#define DDR3_2B_ECC_ERR BIT(4)
#define DDR3_WR_ECC_ERR BIT(3)
#define DDR3_SYS_ERR BIT(0)
/* Bit 31 enables ECC and 28 enables RMW */
#define ECC_ENABLED (BIT(31) | BIT(28))
static void ks2_mc_ddr3_ecc_check(void __iomem *reg)
{
u32 irq_status;
irq_status = readl(reg + DDR3_IRQ_STATUS_SYS);
if ((irq_status & DDR3_2B_ECC_ERR) ||
(irq_status & DDR3_WR_ECC_ERR)) {
/*
* Do a kernel panic as this is double bit ECC error
* or ECC write that are fatal
*/
if (irq_status & DDR3_2B_ECC_ERR)
panic("UC DDR3 ECC err, irq stats 0x%x, addr 0x%x..\n",
irq_status,
readl(reg + TWO_BIT_ECC_ERR_ADDR_LOG));
else
panic("UC DDR3 ECC err, irq stats 0x%x..\n",
irq_status);
}
}
static irqreturn_t ks2_mc_ddr3_ecc_isr(int irq, void *reg_virt)
{
void __iomem *reg = (void __iomem *)reg_virt;
ks2_mc_ddr3_ecc_check(reg);
/*
* Other errors should be handled by hardware
* So, nothing to do here. For now it never reaches here
* as panic will be triggerred for ECC errors
*/
return IRQ_HANDLED;
}
static const struct of_device_id ks2_mc_ddr3_ecc_of_match[] = {
{.compatible = "ti,keystone-ddr3-mc-edac", },
{},
};
static int ks2_mc_ddr3_ecc_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
int error_irq = 0, ret = -ENODEV;
struct device *dev = &pdev->dev;
struct resource res;
void __iomem *reg;
u32 val;
if (of_address_to_resource(np, 0, &res) < 0) {
dev_err(dev, "no edac resource address\n");
return ret;
}
reg = devm_ioremap_resource(dev, &res);
if (IS_ERR(reg)) {
dev_err(dev,
"DDR3 controller regs not defined\n");
return PTR_ERR(reg);
}
/* Check if ECC is enabled. If not, just return */
val = readl(reg + DDR3_ECC_CTRL);
if (!(val & ECC_ENABLED)) {
dev_info(&pdev->dev, "ECC is not enabled, disable edac\n");
return ret;
}
/* disable and clear unused ECC interrupts */
writel(DDR3_1B_ECC_ERR | DDR3_SYS_ERR, reg + DDR3_IRQ_ENABLE_CLR_SYS);
writel(DDR3_1B_ECC_ERR | DDR3_SYS_ERR, reg + DDR3_IRQ_STATUS_SYS);
/* check if we already have unrecoverable errors */
ks2_mc_ddr3_ecc_check(reg);
writel(DDR3_2B_ECC_ERR | DDR3_WR_ECC_ERR,
reg + DDR3_IRQ_ENABLE_CLR_SYS);
/* add DDR3 ECC error handler */
error_irq = platform_get_irq(pdev, 0);
if (!error_irq) {
dev_err(&pdev->dev,
"DDR3 ECC irq number not defined\n");
return ret;
}
ret = devm_request_irq(dev, error_irq, ks2_mc_ddr3_ecc_isr, 0,
"ddr3-ecc-err-irq", (void *)reg);
if (ret) {
dev_err(&pdev->dev,
"request_irq fail for DDR3 ECC error irq\n");
return ret;
}
writel(DDR3_2B_ECC_ERR | DDR3_WR_ECC_ERR,
reg + DDR3_IRQ_ENABLE_SET_SYS);
return ret;
}
static struct platform_driver ks2_mc_ddr3_ecc_driver = {
.probe = ks2_mc_ddr3_ecc_probe,
.driver = {
.name = "ks2_mc_ddr3_ecc",
.of_match_table = ks2_mc_ddr3_ecc_of_match,
},
};
static int __init ks2_mc_ddr3_ecc_init(void)
{
int ret = 0;
ret = platform_driver_register(&ks2_mc_ddr3_ecc_driver);
if (ret)
pr_warn("keystone DDR3 DDR3 ecc_init failed\n");
return ret;
}
subsys_initcall(ks2_mc_ddr3_ecc_init);
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_DESCRIPTION("EDAC Driver for Keystone DDR3 MC");
MODULE_LICENSE("GPL v2");