diff options
Diffstat (limited to 'drivers/infiniband/hw/qib/qib_eeprom.c')
-rw-r--r-- | drivers/infiniband/hw/qib/qib_eeprom.c | 198 |
1 files changed, 9 insertions, 189 deletions
diff --git a/drivers/infiniband/hw/qib/qib_eeprom.c b/drivers/infiniband/hw/qib/qib_eeprom.c index 4d5d71aaa2b4..311ee6c3dd5e 100644 --- a/drivers/infiniband/hw/qib/qib_eeprom.c +++ b/drivers/infiniband/hw/qib/qib_eeprom.c | |||
@@ -153,6 +153,7 @@ void qib_get_eeprom_info(struct qib_devdata *dd) | |||
153 | 153 | ||
154 | if (t && dd0->nguid > 1 && t <= dd0->nguid) { | 154 | if (t && dd0->nguid > 1 && t <= dd0->nguid) { |
155 | u8 oguid; | 155 | u8 oguid; |
156 | |||
156 | dd->base_guid = dd0->base_guid; | 157 | dd->base_guid = dd0->base_guid; |
157 | bguid = (u8 *) &dd->base_guid; | 158 | bguid = (u8 *) &dd->base_guid; |
158 | 159 | ||
@@ -251,206 +252,25 @@ void qib_get_eeprom_info(struct qib_devdata *dd) | |||
251 | * This board has a Serial-prefix, which is stored | 252 | * This board has a Serial-prefix, which is stored |
252 | * elsewhere for backward-compatibility. | 253 | * elsewhere for backward-compatibility. |
253 | */ | 254 | */ |
254 | memcpy(snp, ifp->if_sprefix, sizeof ifp->if_sprefix); | 255 | memcpy(snp, ifp->if_sprefix, sizeof(ifp->if_sprefix)); |
255 | snp[sizeof ifp->if_sprefix] = '\0'; | 256 | snp[sizeof(ifp->if_sprefix)] = '\0'; |
256 | len = strlen(snp); | 257 | len = strlen(snp); |
257 | snp += len; | 258 | snp += len; |
258 | len = (sizeof dd->serial) - len; | 259 | len = sizeof(dd->serial) - len; |
259 | if (len > sizeof ifp->if_serial) | 260 | if (len > sizeof(ifp->if_serial)) |
260 | len = sizeof ifp->if_serial; | 261 | len = sizeof(ifp->if_serial); |
261 | memcpy(snp, ifp->if_serial, len); | 262 | memcpy(snp, ifp->if_serial, len); |
262 | } else | 263 | } else { |
263 | memcpy(dd->serial, ifp->if_serial, | 264 | memcpy(dd->serial, ifp->if_serial, sizeof(ifp->if_serial)); |
264 | sizeof ifp->if_serial); | 265 | } |
265 | if (!strstr(ifp->if_comment, "Tested successfully")) | 266 | if (!strstr(ifp->if_comment, "Tested successfully")) |
266 | qib_dev_err(dd, | 267 | qib_dev_err(dd, |
267 | "Board SN %s did not pass functional test: %s\n", | 268 | "Board SN %s did not pass functional test: %s\n", |
268 | dd->serial, ifp->if_comment); | 269 | dd->serial, ifp->if_comment); |
269 | 270 | ||
270 | memcpy(&dd->eep_st_errs, &ifp->if_errcntp, QIB_EEP_LOG_CNT); | ||
271 | /* | ||
272 | * Power-on (actually "active") hours are kept as little-endian value | ||
273 | * in EEPROM, but as seconds in a (possibly as small as 24-bit) | ||
274 | * atomic_t while running. | ||
275 | */ | ||
276 | atomic_set(&dd->active_time, 0); | ||
277 | dd->eep_hrs = ifp->if_powerhour[0] | (ifp->if_powerhour[1] << 8); | ||
278 | |||
279 | done: | 271 | done: |
280 | vfree(buf); | 272 | vfree(buf); |
281 | 273 | ||
282 | bail:; | 274 | bail:; |
283 | } | 275 | } |
284 | 276 | ||
285 | /** | ||
286 | * qib_update_eeprom_log - copy active-time and error counters to eeprom | ||
287 | * @dd: the qlogic_ib device | ||
288 | * | ||
289 | * Although the time is kept as seconds in the qib_devdata struct, it is | ||
290 | * rounded to hours for re-write, as we have only 16 bits in EEPROM. | ||
291 | * First-cut code reads whole (expected) struct qib_flash, modifies, | ||
292 | * re-writes. Future direction: read/write only what we need, assuming | ||
293 | * that the EEPROM had to have been "good enough" for driver init, and | ||
294 | * if not, we aren't making it worse. | ||
295 | * | ||
296 | */ | ||
297 | int qib_update_eeprom_log(struct qib_devdata *dd) | ||
298 | { | ||
299 | void *buf; | ||
300 | struct qib_flash *ifp; | ||
301 | int len, hi_water; | ||
302 | uint32_t new_time, new_hrs; | ||
303 | u8 csum; | ||
304 | int ret, idx; | ||
305 | unsigned long flags; | ||
306 | |||
307 | /* first, check if we actually need to do anything. */ | ||
308 | ret = 0; | ||
309 | for (idx = 0; idx < QIB_EEP_LOG_CNT; ++idx) { | ||
310 | if (dd->eep_st_new_errs[idx]) { | ||
311 | ret = 1; | ||
312 | break; | ||
313 | } | ||
314 | } | ||
315 | new_time = atomic_read(&dd->active_time); | ||
316 | |||
317 | if (ret == 0 && new_time < 3600) | ||
318 | goto bail; | ||
319 | |||
320 | /* | ||
321 | * The quick-check above determined that there is something worthy | ||
322 | * of logging, so get current contents and do a more detailed idea. | ||
323 | * read full flash, not just currently used part, since it may have | ||
324 | * been written with a newer definition | ||
325 | */ | ||
326 | len = sizeof(struct qib_flash); | ||
327 | buf = vmalloc(len); | ||
328 | ret = 1; | ||
329 | if (!buf) { | ||
330 | qib_dev_err(dd, | ||
331 | "Couldn't allocate memory to read %u bytes from eeprom for logging\n", | ||
332 | len); | ||
333 | goto bail; | ||
334 | } | ||
335 | |||
336 | /* Grab semaphore and read current EEPROM. If we get an | ||
337 | * error, let go, but if not, keep it until we finish write. | ||
338 | */ | ||
339 | ret = mutex_lock_interruptible(&dd->eep_lock); | ||
340 | if (ret) { | ||
341 | qib_dev_err(dd, "Unable to acquire EEPROM for logging\n"); | ||
342 | goto free_bail; | ||
343 | } | ||
344 | ret = qib_twsi_blk_rd(dd, dd->twsi_eeprom_dev, 0, buf, len); | ||
345 | if (ret) { | ||
346 | mutex_unlock(&dd->eep_lock); | ||
347 | qib_dev_err(dd, "Unable read EEPROM for logging\n"); | ||
348 | goto free_bail; | ||
349 | } | ||
350 | ifp = (struct qib_flash *)buf; | ||
351 | |||
352 | csum = flash_csum(ifp, 0); | ||
353 | if (csum != ifp->if_csum) { | ||
354 | mutex_unlock(&dd->eep_lock); | ||
355 | qib_dev_err(dd, "EEPROM cks err (0x%02X, S/B 0x%02X)\n", | ||
356 | csum, ifp->if_csum); | ||
357 | ret = 1; | ||
358 | goto free_bail; | ||
359 | } | ||
360 | hi_water = 0; | ||
361 | spin_lock_irqsave(&dd->eep_st_lock, flags); | ||
362 | for (idx = 0; idx < QIB_EEP_LOG_CNT; ++idx) { | ||
363 | int new_val = dd->eep_st_new_errs[idx]; | ||
364 | if (new_val) { | ||
365 | /* | ||
366 | * If we have seen any errors, add to EEPROM values | ||
367 | * We need to saturate at 0xFF (255) and we also | ||
368 | * would need to adjust the checksum if we were | ||
369 | * trying to minimize EEPROM traffic | ||
370 | * Note that we add to actual current count in EEPROM, | ||
371 | * in case it was altered while we were running. | ||
372 | */ | ||
373 | new_val += ifp->if_errcntp[idx]; | ||
374 | if (new_val > 0xFF) | ||
375 | new_val = 0xFF; | ||
376 | if (ifp->if_errcntp[idx] != new_val) { | ||
377 | ifp->if_errcntp[idx] = new_val; | ||
378 | hi_water = offsetof(struct qib_flash, | ||
379 | if_errcntp) + idx; | ||
380 | } | ||
381 | /* | ||
382 | * update our shadow (used to minimize EEPROM | ||
383 | * traffic), to match what we are about to write. | ||
384 | */ | ||
385 | dd->eep_st_errs[idx] = new_val; | ||
386 | dd->eep_st_new_errs[idx] = 0; | ||
387 | } | ||
388 | } | ||
389 | /* | ||
390 | * Now update active-time. We would like to round to the nearest hour | ||
391 | * but unless atomic_t are sure to be proper signed ints we cannot, | ||
392 | * because we need to account for what we "transfer" to EEPROM and | ||
393 | * if we log an hour at 31 minutes, then we would need to set | ||
394 | * active_time to -29 to accurately count the _next_ hour. | ||
395 | */ | ||
396 | if (new_time >= 3600) { | ||
397 | new_hrs = new_time / 3600; | ||
398 | atomic_sub((new_hrs * 3600), &dd->active_time); | ||
399 | new_hrs += dd->eep_hrs; | ||
400 | if (new_hrs > 0xFFFF) | ||
401 | new_hrs = 0xFFFF; | ||
402 | dd->eep_hrs = new_hrs; | ||
403 | if ((new_hrs & 0xFF) != ifp->if_powerhour[0]) { | ||
404 | ifp->if_powerhour[0] = new_hrs & 0xFF; | ||
405 | hi_water = offsetof(struct qib_flash, if_powerhour); | ||
406 | } | ||
407 | if ((new_hrs >> 8) != ifp->if_powerhour[1]) { | ||
408 | ifp->if_powerhour[1] = new_hrs >> 8; | ||
409 | hi_water = offsetof(struct qib_flash, if_powerhour) + 1; | ||
410 | } | ||
411 | } | ||
412 | /* | ||
413 | * There is a tiny possibility that we could somehow fail to write | ||
414 | * the EEPROM after updating our shadows, but problems from holding | ||
415 | * the spinlock too long are a much bigger issue. | ||
416 | */ | ||
417 | spin_unlock_irqrestore(&dd->eep_st_lock, flags); | ||
418 | if (hi_water) { | ||
419 | /* we made some change to the data, uopdate cksum and write */ | ||
420 | csum = flash_csum(ifp, 1); | ||
421 | ret = eeprom_write_with_enable(dd, 0, buf, hi_water + 1); | ||
422 | } | ||
423 | mutex_unlock(&dd->eep_lock); | ||
424 | if (ret) | ||
425 | qib_dev_err(dd, "Failed updating EEPROM\n"); | ||
426 | |||
427 | free_bail: | ||
428 | vfree(buf); | ||
429 | bail: | ||
430 | return ret; | ||
431 | } | ||
432 | |||
433 | /** | ||
434 | * qib_inc_eeprom_err - increment one of the four error counters | ||
435 | * that are logged to EEPROM. | ||
436 | * @dd: the qlogic_ib device | ||
437 | * @eidx: 0..3, the counter to increment | ||
438 | * @incr: how much to add | ||
439 | * | ||
440 | * Each counter is 8-bits, and saturates at 255 (0xFF). They | ||
441 | * are copied to the EEPROM (aka flash) whenever qib_update_eeprom_log() | ||
442 | * is called, but it can only be called in a context that allows sleep. | ||
443 | * This function can be called even at interrupt level. | ||
444 | */ | ||
445 | void qib_inc_eeprom_err(struct qib_devdata *dd, u32 eidx, u32 incr) | ||
446 | { | ||
447 | uint new_val; | ||
448 | unsigned long flags; | ||
449 | |||
450 | spin_lock_irqsave(&dd->eep_st_lock, flags); | ||
451 | new_val = dd->eep_st_new_errs[eidx] + incr; | ||
452 | if (new_val > 255) | ||
453 | new_val = 255; | ||
454 | dd->eep_st_new_errs[eidx] = new_val; | ||
455 | spin_unlock_irqrestore(&dd->eep_st_lock, flags); | ||
456 | } | ||