diff options
Diffstat (limited to 'drivers/infiniband/hw/qib/qib_eeprom.c')
-rw-r--r-- | drivers/infiniband/hw/qib/qib_eeprom.c | 451 |
1 files changed, 451 insertions, 0 deletions
diff --git a/drivers/infiniband/hw/qib/qib_eeprom.c b/drivers/infiniband/hw/qib/qib_eeprom.c new file mode 100644 index 000000000000..92d9cfe98a68 --- /dev/null +++ b/drivers/infiniband/hw/qib/qib_eeprom.c | |||
@@ -0,0 +1,451 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2006, 2007, 2008, 2009 QLogic Corporation. All rights reserved. | ||
3 | * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. | ||
4 | * | ||
5 | * This software is available to you under a choice of one of two | ||
6 | * licenses. You may choose to be licensed under the terms of the GNU | ||
7 | * General Public License (GPL) Version 2, available from the file | ||
8 | * COPYING in the main directory of this source tree, or the | ||
9 | * OpenIB.org BSD license below: | ||
10 | * | ||
11 | * Redistribution and use in source and binary forms, with or | ||
12 | * without modification, are permitted provided that the following | ||
13 | * conditions are met: | ||
14 | * | ||
15 | * - Redistributions of source code must retain the above | ||
16 | * copyright notice, this list of conditions and the following | ||
17 | * disclaimer. | ||
18 | * | ||
19 | * - Redistributions in binary form must reproduce the above | ||
20 | * copyright notice, this list of conditions and the following | ||
21 | * disclaimer in the documentation and/or other materials | ||
22 | * provided with the distribution. | ||
23 | * | ||
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
31 | * SOFTWARE. | ||
32 | */ | ||
33 | |||
34 | #include <linux/delay.h> | ||
35 | #include <linux/pci.h> | ||
36 | #include <linux/vmalloc.h> | ||
37 | |||
38 | #include "qib.h" | ||
39 | |||
40 | /* | ||
41 | * Functions specific to the serial EEPROM on cards handled by ib_qib. | ||
42 | * The actual serail interface code is in qib_twsi.c. This file is a client | ||
43 | */ | ||
44 | |||
45 | /** | ||
46 | * qib_eeprom_read - receives bytes from the eeprom via I2C | ||
47 | * @dd: the qlogic_ib device | ||
48 | * @eeprom_offset: address to read from | ||
49 | * @buffer: where to store result | ||
50 | * @len: number of bytes to receive | ||
51 | */ | ||
52 | int qib_eeprom_read(struct qib_devdata *dd, u8 eeprom_offset, | ||
53 | void *buff, int len) | ||
54 | { | ||
55 | int ret; | ||
56 | |||
57 | ret = mutex_lock_interruptible(&dd->eep_lock); | ||
58 | if (!ret) { | ||
59 | ret = qib_twsi_reset(dd); | ||
60 | if (ret) | ||
61 | qib_dev_err(dd, "EEPROM Reset for read failed\n"); | ||
62 | else | ||
63 | ret = qib_twsi_blk_rd(dd, dd->twsi_eeprom_dev, | ||
64 | eeprom_offset, buff, len); | ||
65 | mutex_unlock(&dd->eep_lock); | ||
66 | } | ||
67 | |||
68 | return ret; | ||
69 | } | ||
70 | |||
71 | /* | ||
72 | * Actually update the eeprom, first doing write enable if | ||
73 | * needed, then restoring write enable state. | ||
74 | * Must be called with eep_lock held | ||
75 | */ | ||
76 | static int eeprom_write_with_enable(struct qib_devdata *dd, u8 offset, | ||
77 | const void *buf, int len) | ||
78 | { | ||
79 | int ret, pwen; | ||
80 | |||
81 | pwen = dd->f_eeprom_wen(dd, 1); | ||
82 | ret = qib_twsi_reset(dd); | ||
83 | if (ret) | ||
84 | qib_dev_err(dd, "EEPROM Reset for write failed\n"); | ||
85 | else | ||
86 | ret = qib_twsi_blk_wr(dd, dd->twsi_eeprom_dev, | ||
87 | offset, buf, len); | ||
88 | dd->f_eeprom_wen(dd, pwen); | ||
89 | return ret; | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * qib_eeprom_write - writes data to the eeprom via I2C | ||
94 | * @dd: the qlogic_ib device | ||
95 | * @eeprom_offset: where to place data | ||
96 | * @buffer: data to write | ||
97 | * @len: number of bytes to write | ||
98 | */ | ||
99 | int qib_eeprom_write(struct qib_devdata *dd, u8 eeprom_offset, | ||
100 | const void *buff, int len) | ||
101 | { | ||
102 | int ret; | ||
103 | |||
104 | ret = mutex_lock_interruptible(&dd->eep_lock); | ||
105 | if (!ret) { | ||
106 | ret = eeprom_write_with_enable(dd, eeprom_offset, buff, len); | ||
107 | mutex_unlock(&dd->eep_lock); | ||
108 | } | ||
109 | |||
110 | return ret; | ||
111 | } | ||
112 | |||
113 | static u8 flash_csum(struct qib_flash *ifp, int adjust) | ||
114 | { | ||
115 | u8 *ip = (u8 *) ifp; | ||
116 | u8 csum = 0, len; | ||
117 | |||
118 | /* | ||
119 | * Limit length checksummed to max length of actual data. | ||
120 | * Checksum of erased eeprom will still be bad, but we avoid | ||
121 | * reading past the end of the buffer we were passed. | ||
122 | */ | ||
123 | len = ifp->if_length; | ||
124 | if (len > sizeof(struct qib_flash)) | ||
125 | len = sizeof(struct qib_flash); | ||
126 | while (len--) | ||
127 | csum += *ip++; | ||
128 | csum -= ifp->if_csum; | ||
129 | csum = ~csum; | ||
130 | if (adjust) | ||
131 | ifp->if_csum = csum; | ||
132 | |||
133 | return csum; | ||
134 | } | ||
135 | |||
136 | /** | ||
137 | * qib_get_eeprom_info- get the GUID et al. from the TSWI EEPROM device | ||
138 | * @dd: the qlogic_ib device | ||
139 | * | ||
140 | * We have the capability to use the nguid field, and get | ||
141 | * the guid from the first chip's flash, to use for all of them. | ||
142 | */ | ||
143 | void qib_get_eeprom_info(struct qib_devdata *dd) | ||
144 | { | ||
145 | void *buf; | ||
146 | struct qib_flash *ifp; | ||
147 | __be64 guid; | ||
148 | int len, eep_stat; | ||
149 | u8 csum, *bguid; | ||
150 | int t = dd->unit; | ||
151 | struct qib_devdata *dd0 = qib_lookup(0); | ||
152 | |||
153 | if (t && dd0->nguid > 1 && t <= dd0->nguid) { | ||
154 | u8 oguid; | ||
155 | dd->base_guid = dd0->base_guid; | ||
156 | bguid = (u8 *) &dd->base_guid; | ||
157 | |||
158 | oguid = bguid[7]; | ||
159 | bguid[7] += t; | ||
160 | if (oguid > bguid[7]) { | ||
161 | if (bguid[6] == 0xff) { | ||
162 | if (bguid[5] == 0xff) { | ||
163 | qib_dev_err(dd, "Can't set %s GUID" | ||
164 | " from base, wraps to" | ||
165 | " OUI!\n", | ||
166 | qib_get_unit_name(t)); | ||
167 | dd->base_guid = 0; | ||
168 | goto bail; | ||
169 | } | ||
170 | bguid[5]++; | ||
171 | } | ||
172 | bguid[6]++; | ||
173 | } | ||
174 | dd->nguid = 1; | ||
175 | goto bail; | ||
176 | } | ||
177 | |||
178 | /* | ||
179 | * Read full flash, not just currently used part, since it may have | ||
180 | * been written with a newer definition. | ||
181 | * */ | ||
182 | len = sizeof(struct qib_flash); | ||
183 | buf = vmalloc(len); | ||
184 | if (!buf) { | ||
185 | qib_dev_err(dd, "Couldn't allocate memory to read %u " | ||
186 | "bytes from eeprom for GUID\n", len); | ||
187 | goto bail; | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * Use "public" eeprom read function, which does locking and | ||
192 | * figures out device. This will migrate to chip-specific. | ||
193 | */ | ||
194 | eep_stat = qib_eeprom_read(dd, 0, buf, len); | ||
195 | |||
196 | if (eep_stat) { | ||
197 | qib_dev_err(dd, "Failed reading GUID from eeprom\n"); | ||
198 | goto done; | ||
199 | } | ||
200 | ifp = (struct qib_flash *)buf; | ||
201 | |||
202 | csum = flash_csum(ifp, 0); | ||
203 | if (csum != ifp->if_csum) { | ||
204 | qib_devinfo(dd->pcidev, "Bad I2C flash checksum: " | ||
205 | "0x%x, not 0x%x\n", csum, ifp->if_csum); | ||
206 | goto done; | ||
207 | } | ||
208 | if (*(__be64 *) ifp->if_guid == cpu_to_be64(0) || | ||
209 | *(__be64 *) ifp->if_guid == ~cpu_to_be64(0)) { | ||
210 | qib_dev_err(dd, "Invalid GUID %llx from flash; ignoring\n", | ||
211 | *(unsigned long long *) ifp->if_guid); | ||
212 | /* don't allow GUID if all 0 or all 1's */ | ||
213 | goto done; | ||
214 | } | ||
215 | |||
216 | /* complain, but allow it */ | ||
217 | if (*(u64 *) ifp->if_guid == 0x100007511000000ULL) | ||
218 | qib_devinfo(dd->pcidev, "Warning, GUID %llx is " | ||
219 | "default, probably not correct!\n", | ||
220 | *(unsigned long long *) ifp->if_guid); | ||
221 | |||
222 | bguid = ifp->if_guid; | ||
223 | if (!bguid[0] && !bguid[1] && !bguid[2]) { | ||
224 | /* | ||
225 | * Original incorrect GUID format in flash; fix in | ||
226 | * core copy, by shifting up 2 octets; don't need to | ||
227 | * change top octet, since both it and shifted are 0. | ||
228 | */ | ||
229 | bguid[1] = bguid[3]; | ||
230 | bguid[2] = bguid[4]; | ||
231 | bguid[3] = 0; | ||
232 | bguid[4] = 0; | ||
233 | guid = *(__be64 *) ifp->if_guid; | ||
234 | } else | ||
235 | guid = *(__be64 *) ifp->if_guid; | ||
236 | dd->base_guid = guid; | ||
237 | dd->nguid = ifp->if_numguid; | ||
238 | /* | ||
239 | * Things are slightly complicated by the desire to transparently | ||
240 | * support both the Pathscale 10-digit serial number and the QLogic | ||
241 | * 13-character version. | ||
242 | */ | ||
243 | if ((ifp->if_fversion > 1) && ifp->if_sprefix[0] && | ||
244 | ((u8 *) ifp->if_sprefix)[0] != 0xFF) { | ||
245 | char *snp = dd->serial; | ||
246 | |||
247 | /* | ||
248 | * This board has a Serial-prefix, which is stored | ||
249 | * elsewhere for backward-compatibility. | ||
250 | */ | ||
251 | memcpy(snp, ifp->if_sprefix, sizeof ifp->if_sprefix); | ||
252 | snp[sizeof ifp->if_sprefix] = '\0'; | ||
253 | len = strlen(snp); | ||
254 | snp += len; | ||
255 | len = (sizeof dd->serial) - len; | ||
256 | if (len > sizeof ifp->if_serial) | ||
257 | len = sizeof ifp->if_serial; | ||
258 | memcpy(snp, ifp->if_serial, len); | ||
259 | } else | ||
260 | memcpy(dd->serial, ifp->if_serial, | ||
261 | sizeof ifp->if_serial); | ||
262 | if (!strstr(ifp->if_comment, "Tested successfully")) | ||
263 | qib_dev_err(dd, "Board SN %s did not pass functional " | ||
264 | "test: %s\n", dd->serial, ifp->if_comment); | ||
265 | |||
266 | memcpy(&dd->eep_st_errs, &ifp->if_errcntp, QIB_EEP_LOG_CNT); | ||
267 | /* | ||
268 | * Power-on (actually "active") hours are kept as little-endian value | ||
269 | * in EEPROM, but as seconds in a (possibly as small as 24-bit) | ||
270 | * atomic_t while running. | ||
271 | */ | ||
272 | atomic_set(&dd->active_time, 0); | ||
273 | dd->eep_hrs = ifp->if_powerhour[0] | (ifp->if_powerhour[1] << 8); | ||
274 | |||
275 | done: | ||
276 | vfree(buf); | ||
277 | |||
278 | bail:; | ||
279 | } | ||
280 | |||
281 | /** | ||
282 | * qib_update_eeprom_log - copy active-time and error counters to eeprom | ||
283 | * @dd: the qlogic_ib device | ||
284 | * | ||
285 | * Although the time is kept as seconds in the qib_devdata struct, it is | ||
286 | * rounded to hours for re-write, as we have only 16 bits in EEPROM. | ||
287 | * First-cut code reads whole (expected) struct qib_flash, modifies, | ||
288 | * re-writes. Future direction: read/write only what we need, assuming | ||
289 | * that the EEPROM had to have been "good enough" for driver init, and | ||
290 | * if not, we aren't making it worse. | ||
291 | * | ||
292 | */ | ||
293 | int qib_update_eeprom_log(struct qib_devdata *dd) | ||
294 | { | ||
295 | void *buf; | ||
296 | struct qib_flash *ifp; | ||
297 | int len, hi_water; | ||
298 | uint32_t new_time, new_hrs; | ||
299 | u8 csum; | ||
300 | int ret, idx; | ||
301 | unsigned long flags; | ||
302 | |||
303 | /* first, check if we actually need to do anything. */ | ||
304 | ret = 0; | ||
305 | for (idx = 0; idx < QIB_EEP_LOG_CNT; ++idx) { | ||
306 | if (dd->eep_st_new_errs[idx]) { | ||
307 | ret = 1; | ||
308 | break; | ||
309 | } | ||
310 | } | ||
311 | new_time = atomic_read(&dd->active_time); | ||
312 | |||
313 | if (ret == 0 && new_time < 3600) | ||
314 | goto bail; | ||
315 | |||
316 | /* | ||
317 | * The quick-check above determined that there is something worthy | ||
318 | * of logging, so get current contents and do a more detailed idea. | ||
319 | * read full flash, not just currently used part, since it may have | ||
320 | * been written with a newer definition | ||
321 | */ | ||
322 | len = sizeof(struct qib_flash); | ||
323 | buf = vmalloc(len); | ||
324 | ret = 1; | ||
325 | if (!buf) { | ||
326 | qib_dev_err(dd, "Couldn't allocate memory to read %u " | ||
327 | "bytes from eeprom for logging\n", len); | ||
328 | goto bail; | ||
329 | } | ||
330 | |||
331 | /* Grab semaphore and read current EEPROM. If we get an | ||
332 | * error, let go, but if not, keep it until we finish write. | ||
333 | */ | ||
334 | ret = mutex_lock_interruptible(&dd->eep_lock); | ||
335 | if (ret) { | ||
336 | qib_dev_err(dd, "Unable to acquire EEPROM for logging\n"); | ||
337 | goto free_bail; | ||
338 | } | ||
339 | ret = qib_twsi_blk_rd(dd, dd->twsi_eeprom_dev, 0, buf, len); | ||
340 | if (ret) { | ||
341 | mutex_unlock(&dd->eep_lock); | ||
342 | qib_dev_err(dd, "Unable read EEPROM for logging\n"); | ||
343 | goto free_bail; | ||
344 | } | ||
345 | ifp = (struct qib_flash *)buf; | ||
346 | |||
347 | csum = flash_csum(ifp, 0); | ||
348 | if (csum != ifp->if_csum) { | ||
349 | mutex_unlock(&dd->eep_lock); | ||
350 | qib_dev_err(dd, "EEPROM cks err (0x%02X, S/B 0x%02X)\n", | ||
351 | csum, ifp->if_csum); | ||
352 | ret = 1; | ||
353 | goto free_bail; | ||
354 | } | ||
355 | hi_water = 0; | ||
356 | spin_lock_irqsave(&dd->eep_st_lock, flags); | ||
357 | for (idx = 0; idx < QIB_EEP_LOG_CNT; ++idx) { | ||
358 | int new_val = dd->eep_st_new_errs[idx]; | ||
359 | if (new_val) { | ||
360 | /* | ||
361 | * If we have seen any errors, add to EEPROM values | ||
362 | * We need to saturate at 0xFF (255) and we also | ||
363 | * would need to adjust the checksum if we were | ||
364 | * trying to minimize EEPROM traffic | ||
365 | * Note that we add to actual current count in EEPROM, | ||
366 | * in case it was altered while we were running. | ||
367 | */ | ||
368 | new_val += ifp->if_errcntp[idx]; | ||
369 | if (new_val > 0xFF) | ||
370 | new_val = 0xFF; | ||
371 | if (ifp->if_errcntp[idx] != new_val) { | ||
372 | ifp->if_errcntp[idx] = new_val; | ||
373 | hi_water = offsetof(struct qib_flash, | ||
374 | if_errcntp) + idx; | ||
375 | } | ||
376 | /* | ||
377 | * update our shadow (used to minimize EEPROM | ||
378 | * traffic), to match what we are about to write. | ||
379 | */ | ||
380 | dd->eep_st_errs[idx] = new_val; | ||
381 | dd->eep_st_new_errs[idx] = 0; | ||
382 | } | ||
383 | } | ||
384 | /* | ||
385 | * Now update active-time. We would like to round to the nearest hour | ||
386 | * but unless atomic_t are sure to be proper signed ints we cannot, | ||
387 | * because we need to account for what we "transfer" to EEPROM and | ||
388 | * if we log an hour at 31 minutes, then we would need to set | ||
389 | * active_time to -29 to accurately count the _next_ hour. | ||
390 | */ | ||
391 | if (new_time >= 3600) { | ||
392 | new_hrs = new_time / 3600; | ||
393 | atomic_sub((new_hrs * 3600), &dd->active_time); | ||
394 | new_hrs += dd->eep_hrs; | ||
395 | if (new_hrs > 0xFFFF) | ||
396 | new_hrs = 0xFFFF; | ||
397 | dd->eep_hrs = new_hrs; | ||
398 | if ((new_hrs & 0xFF) != ifp->if_powerhour[0]) { | ||
399 | ifp->if_powerhour[0] = new_hrs & 0xFF; | ||
400 | hi_water = offsetof(struct qib_flash, if_powerhour); | ||
401 | } | ||
402 | if ((new_hrs >> 8) != ifp->if_powerhour[1]) { | ||
403 | ifp->if_powerhour[1] = new_hrs >> 8; | ||
404 | hi_water = offsetof(struct qib_flash, if_powerhour) + 1; | ||
405 | } | ||
406 | } | ||
407 | /* | ||
408 | * There is a tiny possibility that we could somehow fail to write | ||
409 | * the EEPROM after updating our shadows, but problems from holding | ||
410 | * the spinlock too long are a much bigger issue. | ||
411 | */ | ||
412 | spin_unlock_irqrestore(&dd->eep_st_lock, flags); | ||
413 | if (hi_water) { | ||
414 | /* we made some change to the data, uopdate cksum and write */ | ||
415 | csum = flash_csum(ifp, 1); | ||
416 | ret = eeprom_write_with_enable(dd, 0, buf, hi_water + 1); | ||
417 | } | ||
418 | mutex_unlock(&dd->eep_lock); | ||
419 | if (ret) | ||
420 | qib_dev_err(dd, "Failed updating EEPROM\n"); | ||
421 | |||
422 | free_bail: | ||
423 | vfree(buf); | ||
424 | bail: | ||
425 | return ret; | ||
426 | } | ||
427 | |||
428 | /** | ||
429 | * qib_inc_eeprom_err - increment one of the four error counters | ||
430 | * that are logged to EEPROM. | ||
431 | * @dd: the qlogic_ib device | ||
432 | * @eidx: 0..3, the counter to increment | ||
433 | * @incr: how much to add | ||
434 | * | ||
435 | * Each counter is 8-bits, and saturates at 255 (0xFF). They | ||
436 | * are copied to the EEPROM (aka flash) whenever qib_update_eeprom_log() | ||
437 | * is called, but it can only be called in a context that allows sleep. | ||
438 | * This function can be called even at interrupt level. | ||
439 | */ | ||
440 | void qib_inc_eeprom_err(struct qib_devdata *dd, u32 eidx, u32 incr) | ||
441 | { | ||
442 | uint new_val; | ||
443 | unsigned long flags; | ||
444 | |||
445 | spin_lock_irqsave(&dd->eep_st_lock, flags); | ||
446 | new_val = dd->eep_st_new_errs[eidx] + incr; | ||
447 | if (new_val > 255) | ||
448 | new_val = 255; | ||
449 | dd->eep_st_new_errs[eidx] = new_val; | ||
450 | spin_unlock_irqrestore(&dd->eep_st_lock, flags); | ||
451 | } | ||