diff options
author | Dimitris Michailidis <dm@chelsio.com> | 2010-04-27 08:24:15 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-04-27 18:46:42 -0400 |
commit | 226ec5fd6746c0ef2e9efb583b44d01264ea0bb5 (patch) | |
tree | 6191d304c270c8abae9bd99f9f942a439c1b3ad3 /drivers/net/cxgb4 | |
parent | c377411f2494a931ff7facdbb3a6839b1266bcf6 (diff) |
cxgb4: parse the VPD instead of relying on a static VPD layout
Some boards' VPDs contain additional keywords or have longer serial numbers,
meaning the keyword locations are variable. Ditch the static layout and
use the pci_vpd_* family of functions to parse the VPD instead.
Signed-off-by: Dimitris Michailidis <dm@chelsio.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/cxgb4')
-rw-r--r-- | drivers/net/cxgb4/t4_hw.c | 64 |
1 files changed, 39 insertions, 25 deletions
diff --git a/drivers/net/cxgb4/t4_hw.c b/drivers/net/cxgb4/t4_hw.c index cadead5170f5..2923dd43523d 100644 --- a/drivers/net/cxgb4/t4_hw.c +++ b/drivers/net/cxgb4/t4_hw.c | |||
@@ -347,33 +347,21 @@ int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *ecc) | |||
347 | return 0; | 347 | return 0; |
348 | } | 348 | } |
349 | 349 | ||
350 | #define VPD_ENTRY(name, len) \ | ||
351 | u8 name##_kword[2]; u8 name##_len; u8 name##_data[len] | ||
352 | |||
353 | /* | 350 | /* |
354 | * Partial EEPROM Vital Product Data structure. Includes only the ID and | 351 | * Partial EEPROM Vital Product Data structure. Includes only the ID and |
355 | * VPD-R sections. | 352 | * VPD-R header. |
356 | */ | 353 | */ |
357 | struct t4_vpd { | 354 | struct t4_vpd_hdr { |
358 | u8 id_tag; | 355 | u8 id_tag; |
359 | u8 id_len[2]; | 356 | u8 id_len[2]; |
360 | u8 id_data[ID_LEN]; | 357 | u8 id_data[ID_LEN]; |
361 | u8 vpdr_tag; | 358 | u8 vpdr_tag; |
362 | u8 vpdr_len[2]; | 359 | u8 vpdr_len[2]; |
363 | VPD_ENTRY(pn, 16); /* part number */ | ||
364 | VPD_ENTRY(ec, EC_LEN); /* EC level */ | ||
365 | VPD_ENTRY(sn, SERNUM_LEN); /* serial number */ | ||
366 | VPD_ENTRY(na, 12); /* MAC address base */ | ||
367 | VPD_ENTRY(port_type, 8); /* port types */ | ||
368 | VPD_ENTRY(gpio, 14); /* GPIO usage */ | ||
369 | VPD_ENTRY(cclk, 6); /* core clock */ | ||
370 | VPD_ENTRY(port_addr, 8); /* port MDIO addresses */ | ||
371 | VPD_ENTRY(rv, 1); /* csum */ | ||
372 | u32 pad; /* for multiple-of-4 sizing and alignment */ | ||
373 | }; | 360 | }; |
374 | 361 | ||
375 | #define EEPROM_STAT_ADDR 0x7bfc | 362 | #define EEPROM_STAT_ADDR 0x7bfc |
376 | #define VPD_BASE 0 | 363 | #define VPD_BASE 0 |
364 | #define VPD_LEN 512 | ||
377 | 365 | ||
378 | /** | 366 | /** |
379 | * t4_seeprom_wp - enable/disable EEPROM write protection | 367 | * t4_seeprom_wp - enable/disable EEPROM write protection |
@@ -398,16 +386,36 @@ int t4_seeprom_wp(struct adapter *adapter, bool enable) | |||
398 | */ | 386 | */ |
399 | static int get_vpd_params(struct adapter *adapter, struct vpd_params *p) | 387 | static int get_vpd_params(struct adapter *adapter, struct vpd_params *p) |
400 | { | 388 | { |
401 | int ret; | 389 | int i, ret; |
402 | struct t4_vpd vpd; | 390 | int ec, sn, v2; |
403 | u8 *q = (u8 *)&vpd, csum; | 391 | u8 vpd[VPD_LEN], csum; |
392 | unsigned int vpdr_len; | ||
393 | const struct t4_vpd_hdr *v; | ||
404 | 394 | ||
405 | ret = pci_read_vpd(adapter->pdev, VPD_BASE, sizeof(vpd), &vpd); | 395 | ret = pci_read_vpd(adapter->pdev, VPD_BASE, sizeof(vpd), vpd); |
406 | if (ret < 0) | 396 | if (ret < 0) |
407 | return ret; | 397 | return ret; |
408 | 398 | ||
409 | for (csum = 0; q <= vpd.rv_data; q++) | 399 | v = (const struct t4_vpd_hdr *)vpd; |
410 | csum += *q; | 400 | vpdr_len = pci_vpd_lrdt_size(&v->vpdr_tag); |
401 | if (vpdr_len + sizeof(struct t4_vpd_hdr) > VPD_LEN) { | ||
402 | dev_err(adapter->pdev_dev, "bad VPD-R length %u\n", vpdr_len); | ||
403 | return -EINVAL; | ||
404 | } | ||
405 | |||
406 | #define FIND_VPD_KW(var, name) do { \ | ||
407 | var = pci_vpd_find_info_keyword(&v->id_tag, sizeof(struct t4_vpd_hdr), \ | ||
408 | vpdr_len, name); \ | ||
409 | if (var < 0) { \ | ||
410 | dev_err(adapter->pdev_dev, "missing VPD keyword " name "\n"); \ | ||
411 | return -EINVAL; \ | ||
412 | } \ | ||
413 | var += PCI_VPD_INFO_FLD_HDR_SIZE; \ | ||
414 | } while (0) | ||
415 | |||
416 | FIND_VPD_KW(i, "RV"); | ||
417 | for (csum = 0; i >= 0; i--) | ||
418 | csum += vpd[i]; | ||
411 | 419 | ||
412 | if (csum) { | 420 | if (csum) { |
413 | dev_err(adapter->pdev_dev, | 421 | dev_err(adapter->pdev_dev, |
@@ -415,12 +423,18 @@ static int get_vpd_params(struct adapter *adapter, struct vpd_params *p) | |||
415 | return -EINVAL; | 423 | return -EINVAL; |
416 | } | 424 | } |
417 | 425 | ||
418 | p->cclk = simple_strtoul(vpd.cclk_data, NULL, 10); | 426 | FIND_VPD_KW(ec, "EC"); |
419 | memcpy(p->id, vpd.id_data, sizeof(vpd.id_data)); | 427 | FIND_VPD_KW(sn, "SN"); |
428 | FIND_VPD_KW(v2, "V2"); | ||
429 | #undef FIND_VPD_KW | ||
430 | |||
431 | p->cclk = simple_strtoul(vpd + v2, NULL, 10); | ||
432 | memcpy(p->id, v->id_data, ID_LEN); | ||
420 | strim(p->id); | 433 | strim(p->id); |
421 | memcpy(p->ec, vpd.ec_data, sizeof(vpd.ec_data)); | 434 | memcpy(p->ec, vpd + ec, EC_LEN); |
422 | strim(p->ec); | 435 | strim(p->ec); |
423 | memcpy(p->sn, vpd.sn_data, sizeof(vpd.sn_data)); | 436 | i = pci_vpd_info_field_size(vpd + sn - PCI_VPD_INFO_FLD_HDR_SIZE); |
437 | memcpy(p->sn, vpd + sn, min(i, SERNUM_LEN)); | ||
424 | strim(p->sn); | 438 | strim(p->sn); |
425 | return 0; | 439 | return 0; |
426 | } | 440 | } |