diff options
author | Robert Richter <robert.richter@linaro.org> | 2013-10-10 11:32:30 -0400 |
---|---|---|
committer | Robert Richter <rric@kernel.org> | 2013-11-04 18:01:06 -0500 |
commit | 0ec8579e16bfaed5587ae8bb02adcb089f29c397 (patch) | |
tree | e5480110f7bc94d08beade443f199142ae29d943 /drivers/edac | |
parent | 982ac2a7b757708845df4339243a91ba180a5744 (diff) |
edac, highbank: Add Calxeda ECX-2000 support
Implement edac support for Calxeda ECX-2000.
The ECX-2000 memory controller is similar to Highbank but has
different register bases for error and interrupt registers. There is
an own device tree name "calxeda,ecx-2000-ddr-ctrl" for identification
and initialization of the ECX-2000 and its base addresses.
Signed-off-by: Robert Richter <robert.richter@linaro.org>
Acked-by: Rob Herring <rob.herring@calxeda.com>
Signed-off-by: Robert Richter <rric@kernel.org>
Diffstat (limited to 'drivers/edac')
-rw-r--r-- | drivers/edac/highbank_mc_edac.c | 105 |
1 files changed, 70 insertions, 35 deletions
diff --git a/drivers/edac/highbank_mc_edac.c b/drivers/edac/highbank_mc_edac.c index 7a78307588bc..79cb872eef5f 100644 --- a/drivers/edac/highbank_mc_edac.c +++ b/drivers/edac/highbank_mc_edac.c | |||
@@ -26,31 +26,40 @@ | |||
26 | #include "edac_module.h" | 26 | #include "edac_module.h" |
27 | 27 | ||
28 | /* DDR Ctrlr Error Registers */ | 28 | /* DDR Ctrlr Error Registers */ |
29 | #define HB_DDR_ECC_OPT 0x128 | ||
30 | #define HB_DDR_ECC_U_ERR_ADDR 0x130 | ||
31 | #define HB_DDR_ECC_U_ERR_STAT 0x134 | ||
32 | #define HB_DDR_ECC_U_ERR_DATAL 0x138 | ||
33 | #define HB_DDR_ECC_U_ERR_DATAH 0x13c | ||
34 | #define HB_DDR_ECC_C_ERR_ADDR 0x140 | ||
35 | #define HB_DDR_ECC_C_ERR_STAT 0x144 | ||
36 | #define HB_DDR_ECC_C_ERR_DATAL 0x148 | ||
37 | #define HB_DDR_ECC_C_ERR_DATAH 0x14c | ||
38 | #define HB_DDR_ECC_INT_STATUS 0x180 | ||
39 | #define HB_DDR_ECC_INT_ACK 0x184 | ||
40 | #define HB_DDR_ECC_U_ERR_ID 0x424 | ||
41 | #define HB_DDR_ECC_C_ERR_ID 0x428 | ||
42 | 29 | ||
43 | #define HB_DDR_ECC_INT_STAT_CE 0x8 | 30 | #define HB_DDR_ECC_ERR_BASE 0x128 |
44 | #define HB_DDR_ECC_INT_STAT_DOUBLE_CE 0x10 | 31 | #define MW_DDR_ECC_ERR_BASE 0x1b4 |
45 | #define HB_DDR_ECC_INT_STAT_UE 0x20 | 32 | |
46 | #define HB_DDR_ECC_INT_STAT_DOUBLE_UE 0x40 | 33 | #define HB_DDR_ECC_OPT 0x00 |
34 | #define HB_DDR_ECC_U_ERR_ADDR 0x08 | ||
35 | #define HB_DDR_ECC_U_ERR_STAT 0x0c | ||
36 | #define HB_DDR_ECC_U_ERR_DATAL 0x10 | ||
37 | #define HB_DDR_ECC_U_ERR_DATAH 0x14 | ||
38 | #define HB_DDR_ECC_C_ERR_ADDR 0x18 | ||
39 | #define HB_DDR_ECC_C_ERR_STAT 0x1c | ||
40 | #define HB_DDR_ECC_C_ERR_DATAL 0x20 | ||
41 | #define HB_DDR_ECC_C_ERR_DATAH 0x24 | ||
47 | 42 | ||
48 | #define HB_DDR_ECC_OPT_MODE_MASK 0x3 | 43 | #define HB_DDR_ECC_OPT_MODE_MASK 0x3 |
49 | #define HB_DDR_ECC_OPT_FWC 0x100 | 44 | #define HB_DDR_ECC_OPT_FWC 0x100 |
50 | #define HB_DDR_ECC_OPT_XOR_SHIFT 16 | 45 | #define HB_DDR_ECC_OPT_XOR_SHIFT 16 |
51 | 46 | ||
47 | /* DDR Ctrlr Interrupt Registers */ | ||
48 | |||
49 | #define HB_DDR_ECC_INT_BASE 0x180 | ||
50 | #define MW_DDR_ECC_INT_BASE 0x218 | ||
51 | |||
52 | #define HB_DDR_ECC_INT_STATUS 0x00 | ||
53 | #define HB_DDR_ECC_INT_ACK 0x04 | ||
54 | |||
55 | #define HB_DDR_ECC_INT_STAT_CE 0x8 | ||
56 | #define HB_DDR_ECC_INT_STAT_DOUBLE_CE 0x10 | ||
57 | #define HB_DDR_ECC_INT_STAT_UE 0x20 | ||
58 | #define HB_DDR_ECC_INT_STAT_DOUBLE_UE 0x40 | ||
59 | |||
52 | struct hb_mc_drvdata { | 60 | struct hb_mc_drvdata { |
53 | void __iomem *mc_vbase; | 61 | void __iomem *mc_err_base; |
62 | void __iomem *mc_int_base; | ||
54 | }; | 63 | }; |
55 | 64 | ||
56 | static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id) | 65 | static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id) |
@@ -60,10 +69,10 @@ static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id) | |||
60 | u32 status, err_addr; | 69 | u32 status, err_addr; |
61 | 70 | ||
62 | /* Read the interrupt status register */ | 71 | /* Read the interrupt status register */ |
63 | status = readl(drvdata->mc_vbase + HB_DDR_ECC_INT_STATUS); | 72 | status = readl(drvdata->mc_int_base + HB_DDR_ECC_INT_STATUS); |
64 | 73 | ||
65 | if (status & HB_DDR_ECC_INT_STAT_UE) { | 74 | if (status & HB_DDR_ECC_INT_STAT_UE) { |
66 | err_addr = readl(drvdata->mc_vbase + HB_DDR_ECC_U_ERR_ADDR); | 75 | err_addr = readl(drvdata->mc_err_base + HB_DDR_ECC_U_ERR_ADDR); |
67 | edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, | 76 | edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, |
68 | err_addr >> PAGE_SHIFT, | 77 | err_addr >> PAGE_SHIFT, |
69 | err_addr & ~PAGE_MASK, 0, | 78 | err_addr & ~PAGE_MASK, 0, |
@@ -71,9 +80,9 @@ static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id) | |||
71 | mci->ctl_name, ""); | 80 | mci->ctl_name, ""); |
72 | } | 81 | } |
73 | if (status & HB_DDR_ECC_INT_STAT_CE) { | 82 | if (status & HB_DDR_ECC_INT_STAT_CE) { |
74 | u32 syndrome = readl(drvdata->mc_vbase + HB_DDR_ECC_C_ERR_STAT); | 83 | u32 syndrome = readl(drvdata->mc_err_base + HB_DDR_ECC_C_ERR_STAT); |
75 | syndrome = (syndrome >> 8) & 0xff; | 84 | syndrome = (syndrome >> 8) & 0xff; |
76 | err_addr = readl(drvdata->mc_vbase + HB_DDR_ECC_C_ERR_ADDR); | 85 | err_addr = readl(drvdata->mc_err_base + HB_DDR_ECC_C_ERR_ADDR); |
77 | edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, | 86 | edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, |
78 | err_addr >> PAGE_SHIFT, | 87 | err_addr >> PAGE_SHIFT, |
79 | err_addr & ~PAGE_MASK, syndrome, | 88 | err_addr & ~PAGE_MASK, syndrome, |
@@ -82,7 +91,7 @@ static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id) | |||
82 | } | 91 | } |
83 | 92 | ||
84 | /* clear the error, clears the interrupt */ | 93 | /* clear the error, clears the interrupt */ |
85 | writel(status, drvdata->mc_vbase + HB_DDR_ECC_INT_ACK); | 94 | writel(status, drvdata->mc_int_base + HB_DDR_ECC_INT_ACK); |
86 | return IRQ_HANDLED; | 95 | return IRQ_HANDLED; |
87 | } | 96 | } |
88 | 97 | ||
@@ -104,10 +113,10 @@ static ssize_t highbank_mc_err_inject_write(struct file *file, | |||
104 | buf[buf_size] = 0; | 113 | buf[buf_size] = 0; |
105 | 114 | ||
106 | if (!kstrtou8(buf, 16, &synd)) { | 115 | if (!kstrtou8(buf, 16, &synd)) { |
107 | reg = readl(pdata->mc_vbase + HB_DDR_ECC_OPT); | 116 | reg = readl(pdata->mc_err_base + HB_DDR_ECC_OPT); |
108 | reg &= HB_DDR_ECC_OPT_MODE_MASK; | 117 | reg &= HB_DDR_ECC_OPT_MODE_MASK; |
109 | reg |= (synd << HB_DDR_ECC_OPT_XOR_SHIFT) | HB_DDR_ECC_OPT_FWC; | 118 | reg |= (synd << HB_DDR_ECC_OPT_XOR_SHIFT) | HB_DDR_ECC_OPT_FWC; |
110 | writel(reg, pdata->mc_vbase + HB_DDR_ECC_OPT); | 119 | writel(reg, pdata->mc_err_base + HB_DDR_ECC_OPT); |
111 | } | 120 | } |
112 | 121 | ||
113 | return count; | 122 | return count; |
@@ -131,17 +140,46 @@ static void highbank_mc_create_debugfs_nodes(struct mem_ctl_info *mci) | |||
131 | {} | 140 | {} |
132 | #endif | 141 | #endif |
133 | 142 | ||
143 | struct hb_mc_settings { | ||
144 | int err_offset; | ||
145 | int int_offset; | ||
146 | }; | ||
147 | |||
148 | static struct hb_mc_settings hb_settings = { | ||
149 | .err_offset = HB_DDR_ECC_ERR_BASE, | ||
150 | .int_offset = HB_DDR_ECC_INT_BASE, | ||
151 | }; | ||
152 | |||
153 | static struct hb_mc_settings mw_settings = { | ||
154 | .err_offset = MW_DDR_ECC_ERR_BASE, | ||
155 | .int_offset = MW_DDR_ECC_INT_BASE, | ||
156 | }; | ||
157 | |||
158 | static struct of_device_id hb_ddr_ctrl_of_match[] = { | ||
159 | { .compatible = "calxeda,hb-ddr-ctrl", .data = &hb_settings }, | ||
160 | { .compatible = "calxeda,ecx-2000-ddr-ctrl", .data = &mw_settings }, | ||
161 | {}, | ||
162 | }; | ||
163 | MODULE_DEVICE_TABLE(of, hb_ddr_ctrl_of_match); | ||
164 | |||
134 | static int highbank_mc_probe(struct platform_device *pdev) | 165 | static int highbank_mc_probe(struct platform_device *pdev) |
135 | { | 166 | { |
167 | const struct of_device_id *id; | ||
168 | const struct hb_mc_settings *settings; | ||
136 | struct edac_mc_layer layers[2]; | 169 | struct edac_mc_layer layers[2]; |
137 | struct mem_ctl_info *mci; | 170 | struct mem_ctl_info *mci; |
138 | struct hb_mc_drvdata *drvdata; | 171 | struct hb_mc_drvdata *drvdata; |
139 | struct dimm_info *dimm; | 172 | struct dimm_info *dimm; |
140 | struct resource *r; | 173 | struct resource *r; |
174 | void __iomem *base; | ||
141 | u32 control; | 175 | u32 control; |
142 | int irq; | 176 | int irq; |
143 | int res = 0; | 177 | int res = 0; |
144 | 178 | ||
179 | id = of_match_device(hb_ddr_ctrl_of_match, &pdev->dev); | ||
180 | if (!id) | ||
181 | return -ENODEV; | ||
182 | |||
145 | layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; | 183 | layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; |
146 | layers[0].size = 1; | 184 | layers[0].size = 1; |
147 | layers[0].is_virt_csrow = true; | 185 | layers[0].is_virt_csrow = true; |
@@ -174,15 +212,18 @@ static int highbank_mc_probe(struct platform_device *pdev) | |||
174 | goto err; | 212 | goto err; |
175 | } | 213 | } |
176 | 214 | ||
177 | drvdata->mc_vbase = devm_ioremap(&pdev->dev, | 215 | base = devm_ioremap(&pdev->dev, r->start, resource_size(r)); |
178 | r->start, resource_size(r)); | 216 | if (!base) { |
179 | if (!drvdata->mc_vbase) { | ||
180 | dev_err(&pdev->dev, "Unable to map regs\n"); | 217 | dev_err(&pdev->dev, "Unable to map regs\n"); |
181 | res = -ENOMEM; | 218 | res = -ENOMEM; |
182 | goto err; | 219 | goto err; |
183 | } | 220 | } |
184 | 221 | ||
185 | control = readl(drvdata->mc_vbase + HB_DDR_ECC_OPT) & 0x3; | 222 | settings = id->data; |
223 | drvdata->mc_err_base = base + settings->err_offset; | ||
224 | drvdata->mc_int_base = base + settings->int_offset; | ||
225 | |||
226 | control = readl(drvdata->mc_err_base + HB_DDR_ECC_OPT) & 0x3; | ||
186 | if (!control || (control == 0x2)) { | 227 | if (!control || (control == 0x2)) { |
187 | dev_err(&pdev->dev, "No ECC present, or ECC disabled\n"); | 228 | dev_err(&pdev->dev, "No ECC present, or ECC disabled\n"); |
188 | res = -ENODEV; | 229 | res = -ENODEV; |
@@ -238,12 +279,6 @@ static int highbank_mc_remove(struct platform_device *pdev) | |||
238 | return 0; | 279 | return 0; |
239 | } | 280 | } |
240 | 281 | ||
241 | static const struct of_device_id hb_ddr_ctrl_of_match[] = { | ||
242 | { .compatible = "calxeda,hb-ddr-ctrl", }, | ||
243 | {}, | ||
244 | }; | ||
245 | MODULE_DEVICE_TABLE(of, hb_ddr_ctrl_of_match); | ||
246 | |||
247 | static struct platform_driver highbank_mc_edac_driver = { | 282 | static struct platform_driver highbank_mc_edac_driver = { |
248 | .probe = highbank_mc_probe, | 283 | .probe = highbank_mc_probe, |
249 | .remove = highbank_mc_remove, | 284 | .remove = highbank_mc_remove, |