diff options
author | Doug Thompson <dougthompson@xmission.com> | 2009-04-27 09:53:22 -0400 |
---|---|---|
committer | Borislav Petkov <borislav.petkov@amd.com> | 2009-06-10 06:18:49 -0400 |
commit | 6775763a2377e1ea865299b6daadc875d622de3f (patch) | |
tree | 618500f54598ba4dc7504eb815a1b990c897232a /drivers/edac/amd64_edac.c | |
parent | 2bc65418724a0ddbb20c9eeb9de8285af7748c16 (diff) |
amd64_edac: add sys addr to memory controller mapping helpers
Borislav:
- cleanup comments
- cleanup debug calls
- simplify find_mc_by_sys_addr's exit path
Reviewed-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Signed-off-by: Doug Thompson <dougthompson@xmission.com>
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
Diffstat (limited to 'drivers/edac/amd64_edac.c')
-rw-r--r-- | drivers/edac/amd64_edac.c | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 6876d4359794..a774f34d508b 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c | |||
@@ -128,3 +128,144 @@ static int amd64_get_scrub_rate(struct mem_ctl_info *mci, u32 *bw) | |||
128 | return status; | 128 | return status; |
129 | } | 129 | } |
130 | 130 | ||
131 | /* Map from a CSROW entry to the mask entry that operates on it */ | ||
132 | static inline u32 amd64_map_to_dcs_mask(struct amd64_pvt *pvt, int csrow) | ||
133 | { | ||
134 | return csrow >> (pvt->num_dcsm >> 3); | ||
135 | } | ||
136 | |||
137 | /* return the 'base' address the i'th CS entry of the 'dct' DRAM controller */ | ||
138 | static u32 amd64_get_dct_base(struct amd64_pvt *pvt, int dct, int csrow) | ||
139 | { | ||
140 | if (dct == 0) | ||
141 | return pvt->dcsb0[csrow]; | ||
142 | else | ||
143 | return pvt->dcsb1[csrow]; | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * Return the 'mask' address the i'th CS entry. This function is needed because | ||
148 | * there number of DCSM registers on Rev E and prior vs Rev F and later is | ||
149 | * different. | ||
150 | */ | ||
151 | static u32 amd64_get_dct_mask(struct amd64_pvt *pvt, int dct, int csrow) | ||
152 | { | ||
153 | if (dct == 0) | ||
154 | return pvt->dcsm0[amd64_map_to_dcs_mask(pvt, csrow)]; | ||
155 | else | ||
156 | return pvt->dcsm1[amd64_map_to_dcs_mask(pvt, csrow)]; | ||
157 | } | ||
158 | |||
159 | |||
160 | /* | ||
161 | * In *base and *limit, pass back the full 40-bit base and limit physical | ||
162 | * addresses for the node given by node_id. This information is obtained from | ||
163 | * DRAM Base (section 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers. The | ||
164 | * base and limit addresses are of type SysAddr, as defined at the start of | ||
165 | * section 3.4.4 (p. 70). They are the lowest and highest physical addresses | ||
166 | * in the address range they represent. | ||
167 | */ | ||
168 | static void amd64_get_base_and_limit(struct amd64_pvt *pvt, int node_id, | ||
169 | u64 *base, u64 *limit) | ||
170 | { | ||
171 | *base = pvt->dram_base[node_id]; | ||
172 | *limit = pvt->dram_limit[node_id]; | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * Return 1 if the SysAddr given by sys_addr matches the base/limit associated | ||
177 | * with node_id | ||
178 | */ | ||
179 | static int amd64_base_limit_match(struct amd64_pvt *pvt, | ||
180 | u64 sys_addr, int node_id) | ||
181 | { | ||
182 | u64 base, limit, addr; | ||
183 | |||
184 | amd64_get_base_and_limit(pvt, node_id, &base, &limit); | ||
185 | |||
186 | /* The K8 treats this as a 40-bit value. However, bits 63-40 will be | ||
187 | * all ones if the most significant implemented address bit is 1. | ||
188 | * Here we discard bits 63-40. See section 3.4.2 of AMD publication | ||
189 | * 24592: AMD x86-64 Architecture Programmer's Manual Volume 1 | ||
190 | * Application Programming. | ||
191 | */ | ||
192 | addr = sys_addr & 0x000000ffffffffffull; | ||
193 | |||
194 | return (addr >= base) && (addr <= limit); | ||
195 | } | ||
196 | |||
197 | /* | ||
198 | * Attempt to map a SysAddr to a node. On success, return a pointer to the | ||
199 | * mem_ctl_info structure for the node that the SysAddr maps to. | ||
200 | * | ||
201 | * On failure, return NULL. | ||
202 | */ | ||
203 | static struct mem_ctl_info *find_mc_by_sys_addr(struct mem_ctl_info *mci, | ||
204 | u64 sys_addr) | ||
205 | { | ||
206 | struct amd64_pvt *pvt; | ||
207 | int node_id; | ||
208 | u32 intlv_en, bits; | ||
209 | |||
210 | /* | ||
211 | * Here we use the DRAM Base (section 3.4.4.1) and DRAM Limit (section | ||
212 | * 3.4.4.2) registers to map the SysAddr to a node ID. | ||
213 | */ | ||
214 | pvt = mci->pvt_info; | ||
215 | |||
216 | /* | ||
217 | * The value of this field should be the same for all DRAM Base | ||
218 | * registers. Therefore we arbitrarily choose to read it from the | ||
219 | * register for node 0. | ||
220 | */ | ||
221 | intlv_en = pvt->dram_IntlvEn[0]; | ||
222 | |||
223 | if (intlv_en == 0) { | ||
224 | for (node_id = 0; ; ) { | ||
225 | if (amd64_base_limit_match(pvt, sys_addr, node_id)) | ||
226 | break; | ||
227 | |||
228 | if (++node_id >= DRAM_REG_COUNT) | ||
229 | goto err_no_match; | ||
230 | } | ||
231 | goto found; | ||
232 | } | ||
233 | |||
234 | if (unlikely((intlv_en != (0x01 << 8)) && | ||
235 | (intlv_en != (0x03 << 8)) && | ||
236 | (intlv_en != (0x07 << 8)))) { | ||
237 | amd64_printk(KERN_WARNING, "junk value of 0x%x extracted from " | ||
238 | "IntlvEn field of DRAM Base Register for node 0: " | ||
239 | "This probably indicates a BIOS bug.\n", intlv_en); | ||
240 | return NULL; | ||
241 | } | ||
242 | |||
243 | bits = (((u32) sys_addr) >> 12) & intlv_en; | ||
244 | |||
245 | for (node_id = 0; ; ) { | ||
246 | if ((pvt->dram_limit[node_id] & intlv_en) == bits) | ||
247 | break; /* intlv_sel field matches */ | ||
248 | |||
249 | if (++node_id >= DRAM_REG_COUNT) | ||
250 | goto err_no_match; | ||
251 | } | ||
252 | |||
253 | /* sanity test for sys_addr */ | ||
254 | if (unlikely(!amd64_base_limit_match(pvt, sys_addr, node_id))) { | ||
255 | amd64_printk(KERN_WARNING, | ||
256 | "%s(): sys_addr 0x%lx falls outside base/limit " | ||
257 | "address range for node %d with node interleaving " | ||
258 | "enabled.\n", __func__, (unsigned long)sys_addr, | ||
259 | node_id); | ||
260 | return NULL; | ||
261 | } | ||
262 | |||
263 | found: | ||
264 | return edac_mc_find(node_id); | ||
265 | |||
266 | err_no_match: | ||
267 | debugf2("sys_addr 0x%lx doesn't match any node\n", | ||
268 | (unsigned long)sys_addr); | ||
269 | |||
270 | return NULL; | ||
271 | } | ||