diff options
author | Ben Hutchings <bhutchings@solarflare.com> | 2009-11-29 10:15:41 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-11-29 20:23:57 -0500 |
commit | 8880f4ec21e668dcab3c6d387524a887e5bcbf73 (patch) | |
tree | d2f34eec2fba31f3b3141c2e580846e92c4e554c /drivers/net/sfc/mtd.c | |
parent | afd4aea03f597f29421dc5767e7d1f754730ec23 (diff) |
sfc: Add support for SFC9000 family (2)
This integrates support for the SFC9000 family of 10G Ethernet
controllers and LAN-on-motherboard chips, starting with the SFL9021
'Siena' and SFC9020 'Bethpage'.
Credit for this code is largely due to my colleagues at Solarflare:
Guido Barzini
Steve Hodgson
Kieran Mansley
Matthew Slattery
Neil Turton
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/sfc/mtd.c')
-rw-r--r-- | drivers/net/sfc/mtd.c | 267 |
1 files changed, 262 insertions, 5 deletions
diff --git a/drivers/net/sfc/mtd.c b/drivers/net/sfc/mtd.c index 65a22f193f92..ef561f8af4d1 100644 --- a/drivers/net/sfc/mtd.c +++ b/drivers/net/sfc/mtd.c | |||
@@ -8,6 +8,7 @@ | |||
8 | * by the Free Software Foundation, incorporated herein by reference. | 8 | * by the Free Software Foundation, incorporated herein by reference. |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <linux/bitops.h> | ||
11 | #include <linux/module.h> | 12 | #include <linux/module.h> |
12 | #include <linux/mtd/mtd.h> | 13 | #include <linux/mtd/mtd.h> |
13 | #include <linux/delay.h> | 14 | #include <linux/delay.h> |
@@ -18,12 +19,22 @@ | |||
18 | #include "spi.h" | 19 | #include "spi.h" |
19 | #include "efx.h" | 20 | #include "efx.h" |
20 | #include "nic.h" | 21 | #include "nic.h" |
22 | #include "mcdi.h" | ||
23 | #include "mcdi_pcol.h" | ||
21 | 24 | ||
22 | #define EFX_SPI_VERIFY_BUF_LEN 16 | 25 | #define EFX_SPI_VERIFY_BUF_LEN 16 |
26 | #define EFX_MCDI_CHUNK_LEN 128 | ||
23 | 27 | ||
24 | struct efx_mtd_partition { | 28 | struct efx_mtd_partition { |
25 | struct mtd_info mtd; | 29 | struct mtd_info mtd; |
26 | size_t offset; | 30 | union { |
31 | struct { | ||
32 | bool updating; | ||
33 | u8 nvram_type; | ||
34 | u16 fw_subtype; | ||
35 | } mcdi; | ||
36 | size_t offset; | ||
37 | }; | ||
27 | const char *type_name; | 38 | const char *type_name; |
28 | char name[IFNAMSIZ + 20]; | 39 | char name[IFNAMSIZ + 20]; |
29 | }; | 40 | }; |
@@ -56,6 +67,7 @@ struct efx_mtd { | |||
56 | container_of(mtd, struct efx_mtd_partition, mtd) | 67 | container_of(mtd, struct efx_mtd_partition, mtd) |
57 | 68 | ||
58 | static int falcon_mtd_probe(struct efx_nic *efx); | 69 | static int falcon_mtd_probe(struct efx_nic *efx); |
70 | static int siena_mtd_probe(struct efx_nic *efx); | ||
59 | 71 | ||
60 | /* SPI utilities */ | 72 | /* SPI utilities */ |
61 | 73 | ||
@@ -223,9 +235,14 @@ static void efx_mtd_rename_device(struct efx_mtd *efx_mtd) | |||
223 | struct efx_mtd_partition *part; | 235 | struct efx_mtd_partition *part; |
224 | 236 | ||
225 | efx_for_each_partition(part, efx_mtd) | 237 | efx_for_each_partition(part, efx_mtd) |
226 | snprintf(part->name, sizeof(part->name), | 238 | if (efx_nic_rev(efx_mtd->efx) >= EFX_REV_SIENA_A0) |
227 | "%s %s", efx_mtd->efx->name, | 239 | snprintf(part->name, sizeof(part->name), |
228 | part->type_name); | 240 | "%s %s:%02x", efx_mtd->efx->name, |
241 | part->type_name, part->mcdi.fw_subtype); | ||
242 | else | ||
243 | snprintf(part->name, sizeof(part->name), | ||
244 | "%s %s", efx_mtd->efx->name, | ||
245 | part->type_name); | ||
229 | } | 246 | } |
230 | 247 | ||
231 | static int efx_mtd_probe_device(struct efx_nic *efx, struct efx_mtd *efx_mtd) | 248 | static int efx_mtd_probe_device(struct efx_nic *efx, struct efx_mtd *efx_mtd) |
@@ -285,7 +302,10 @@ void efx_mtd_rename(struct efx_nic *efx) | |||
285 | 302 | ||
286 | int efx_mtd_probe(struct efx_nic *efx) | 303 | int efx_mtd_probe(struct efx_nic *efx) |
287 | { | 304 | { |
288 | return falcon_mtd_probe(efx); | 305 | if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) |
306 | return siena_mtd_probe(efx); | ||
307 | else | ||
308 | return falcon_mtd_probe(efx); | ||
289 | } | 309 | } |
290 | 310 | ||
291 | /* Implementation of MTD operations for Falcon */ | 311 | /* Implementation of MTD operations for Falcon */ |
@@ -393,3 +413,240 @@ static int falcon_mtd_probe(struct efx_nic *efx) | |||
393 | kfree(efx_mtd); | 413 | kfree(efx_mtd); |
394 | return rc; | 414 | return rc; |
395 | } | 415 | } |
416 | |||
417 | /* Implementation of MTD operations for Siena */ | ||
418 | |||
419 | static int siena_mtd_read(struct mtd_info *mtd, loff_t start, | ||
420 | size_t len, size_t *retlen, u8 *buffer) | ||
421 | { | ||
422 | struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); | ||
423 | struct efx_mtd *efx_mtd = mtd->priv; | ||
424 | struct efx_nic *efx = efx_mtd->efx; | ||
425 | loff_t offset = start; | ||
426 | loff_t end = min_t(loff_t, start + len, mtd->size); | ||
427 | size_t chunk; | ||
428 | int rc = 0; | ||
429 | |||
430 | while (offset < end) { | ||
431 | chunk = min_t(size_t, end - offset, EFX_MCDI_CHUNK_LEN); | ||
432 | rc = efx_mcdi_nvram_read(efx, part->mcdi.nvram_type, offset, | ||
433 | buffer, chunk); | ||
434 | if (rc) | ||
435 | goto out; | ||
436 | offset += chunk; | ||
437 | buffer += chunk; | ||
438 | } | ||
439 | out: | ||
440 | *retlen = offset - start; | ||
441 | return rc; | ||
442 | } | ||
443 | |||
444 | static int siena_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len) | ||
445 | { | ||
446 | struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); | ||
447 | struct efx_mtd *efx_mtd = mtd->priv; | ||
448 | struct efx_nic *efx = efx_mtd->efx; | ||
449 | loff_t offset = start & ~((loff_t)(mtd->erasesize - 1)); | ||
450 | loff_t end = min_t(loff_t, start + len, mtd->size); | ||
451 | size_t chunk = part->mtd.erasesize; | ||
452 | int rc = 0; | ||
453 | |||
454 | if (!part->mcdi.updating) { | ||
455 | rc = efx_mcdi_nvram_update_start(efx, part->mcdi.nvram_type); | ||
456 | if (rc) | ||
457 | goto out; | ||
458 | part->mcdi.updating = 1; | ||
459 | } | ||
460 | |||
461 | /* The MCDI interface can in fact do multiple erase blocks at once; | ||
462 | * but erasing may be slow, so we make multiple calls here to avoid | ||
463 | * tripping the MCDI RPC timeout. */ | ||
464 | while (offset < end) { | ||
465 | rc = efx_mcdi_nvram_erase(efx, part->mcdi.nvram_type, offset, | ||
466 | chunk); | ||
467 | if (rc) | ||
468 | goto out; | ||
469 | offset += chunk; | ||
470 | } | ||
471 | out: | ||
472 | return rc; | ||
473 | } | ||
474 | |||
475 | static int siena_mtd_write(struct mtd_info *mtd, loff_t start, | ||
476 | size_t len, size_t *retlen, const u8 *buffer) | ||
477 | { | ||
478 | struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); | ||
479 | struct efx_mtd *efx_mtd = mtd->priv; | ||
480 | struct efx_nic *efx = efx_mtd->efx; | ||
481 | loff_t offset = start; | ||
482 | loff_t end = min_t(loff_t, start + len, mtd->size); | ||
483 | size_t chunk; | ||
484 | int rc = 0; | ||
485 | |||
486 | if (!part->mcdi.updating) { | ||
487 | rc = efx_mcdi_nvram_update_start(efx, part->mcdi.nvram_type); | ||
488 | if (rc) | ||
489 | goto out; | ||
490 | part->mcdi.updating = 1; | ||
491 | } | ||
492 | |||
493 | while (offset < end) { | ||
494 | chunk = min_t(size_t, end - offset, EFX_MCDI_CHUNK_LEN); | ||
495 | rc = efx_mcdi_nvram_write(efx, part->mcdi.nvram_type, offset, | ||
496 | buffer, chunk); | ||
497 | if (rc) | ||
498 | goto out; | ||
499 | offset += chunk; | ||
500 | buffer += chunk; | ||
501 | } | ||
502 | out: | ||
503 | *retlen = offset - start; | ||
504 | return rc; | ||
505 | } | ||
506 | |||
507 | static int siena_mtd_sync(struct mtd_info *mtd) | ||
508 | { | ||
509 | struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); | ||
510 | struct efx_mtd *efx_mtd = mtd->priv; | ||
511 | struct efx_nic *efx = efx_mtd->efx; | ||
512 | int rc = 0; | ||
513 | |||
514 | if (part->mcdi.updating) { | ||
515 | part->mcdi.updating = 0; | ||
516 | rc = efx_mcdi_nvram_update_finish(efx, part->mcdi.nvram_type); | ||
517 | } | ||
518 | |||
519 | return rc; | ||
520 | } | ||
521 | |||
522 | static struct efx_mtd_ops siena_mtd_ops = { | ||
523 | .read = siena_mtd_read, | ||
524 | .erase = siena_mtd_erase, | ||
525 | .write = siena_mtd_write, | ||
526 | .sync = siena_mtd_sync, | ||
527 | }; | ||
528 | |||
529 | struct siena_nvram_type_info { | ||
530 | int port; | ||
531 | const char *name; | ||
532 | }; | ||
533 | |||
534 | static struct siena_nvram_type_info siena_nvram_types[] = { | ||
535 | [MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO] = { 0, "sfc_dummy_phy" }, | ||
536 | [MC_CMD_NVRAM_TYPE_MC_FW] = { 0, "sfc_mcfw" }, | ||
537 | [MC_CMD_NVRAM_TYPE_MC_FW_BACKUP] = { 0, "sfc_mcfw_backup" }, | ||
538 | [MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0] = { 0, "sfc_static_cfg" }, | ||
539 | [MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1] = { 1, "sfc_static_cfg" }, | ||
540 | [MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0] = { 0, "sfc_dynamic_cfg" }, | ||
541 | [MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1] = { 1, "sfc_dynamic_cfg" }, | ||
542 | [MC_CMD_NVRAM_TYPE_EXP_ROM] = { 0, "sfc_exp_rom" }, | ||
543 | [MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0] = { 0, "sfc_exp_rom_cfg" }, | ||
544 | [MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1] = { 1, "sfc_exp_rom_cfg" }, | ||
545 | [MC_CMD_NVRAM_TYPE_PHY_PORT0] = { 0, "sfc_phy_fw" }, | ||
546 | [MC_CMD_NVRAM_TYPE_PHY_PORT1] = { 1, "sfc_phy_fw" }, | ||
547 | }; | ||
548 | |||
549 | static int siena_mtd_probe_partition(struct efx_nic *efx, | ||
550 | struct efx_mtd *efx_mtd, | ||
551 | unsigned int part_id, | ||
552 | unsigned int type) | ||
553 | { | ||
554 | struct efx_mtd_partition *part = &efx_mtd->part[part_id]; | ||
555 | struct siena_nvram_type_info *info; | ||
556 | size_t size, erase_size; | ||
557 | bool protected; | ||
558 | int rc; | ||
559 | |||
560 | if (type >= ARRAY_SIZE(siena_nvram_types)) | ||
561 | return -ENODEV; | ||
562 | |||
563 | info = &siena_nvram_types[type]; | ||
564 | |||
565 | if (info->port != efx_port_num(efx)) | ||
566 | return -ENODEV; | ||
567 | |||
568 | rc = efx_mcdi_nvram_info(efx, type, &size, &erase_size, &protected); | ||
569 | if (rc) | ||
570 | return rc; | ||
571 | if (protected) | ||
572 | return -ENODEV; /* hide it */ | ||
573 | |||
574 | part->mcdi.nvram_type = type; | ||
575 | part->type_name = info->name; | ||
576 | |||
577 | part->mtd.type = MTD_NORFLASH; | ||
578 | part->mtd.flags = MTD_CAP_NORFLASH; | ||
579 | part->mtd.size = size; | ||
580 | part->mtd.erasesize = erase_size; | ||
581 | |||
582 | return 0; | ||
583 | } | ||
584 | |||
585 | static int siena_mtd_get_fw_subtypes(struct efx_nic *efx, | ||
586 | struct efx_mtd *efx_mtd) | ||
587 | { | ||
588 | struct efx_mtd_partition *part; | ||
589 | uint16_t fw_subtype_list[MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_LEN / | ||
590 | sizeof(uint16_t)]; | ||
591 | int rc; | ||
592 | |||
593 | rc = efx_mcdi_get_board_cfg(efx, NULL, fw_subtype_list); | ||
594 | if (rc) | ||
595 | return rc; | ||
596 | |||
597 | efx_for_each_partition(part, efx_mtd) | ||
598 | part->mcdi.fw_subtype = fw_subtype_list[part->mcdi.nvram_type]; | ||
599 | |||
600 | return 0; | ||
601 | } | ||
602 | |||
603 | static int siena_mtd_probe(struct efx_nic *efx) | ||
604 | { | ||
605 | struct efx_mtd *efx_mtd; | ||
606 | int rc = -ENODEV; | ||
607 | u32 nvram_types; | ||
608 | unsigned int type; | ||
609 | |||
610 | ASSERT_RTNL(); | ||
611 | |||
612 | rc = efx_mcdi_nvram_types(efx, &nvram_types); | ||
613 | if (rc) | ||
614 | return rc; | ||
615 | |||
616 | efx_mtd = kzalloc(sizeof(*efx_mtd) + | ||
617 | hweight32(nvram_types) * sizeof(efx_mtd->part[0]), | ||
618 | GFP_KERNEL); | ||
619 | if (!efx_mtd) | ||
620 | return -ENOMEM; | ||
621 | |||
622 | efx_mtd->name = "Siena NVRAM manager"; | ||
623 | |||
624 | efx_mtd->ops = &siena_mtd_ops; | ||
625 | |||
626 | type = 0; | ||
627 | efx_mtd->n_parts = 0; | ||
628 | |||
629 | while (nvram_types != 0) { | ||
630 | if (nvram_types & 1) { | ||
631 | rc = siena_mtd_probe_partition(efx, efx_mtd, | ||
632 | efx_mtd->n_parts, type); | ||
633 | if (rc == 0) | ||
634 | efx_mtd->n_parts++; | ||
635 | else if (rc != -ENODEV) | ||
636 | goto fail; | ||
637 | } | ||
638 | type++; | ||
639 | nvram_types >>= 1; | ||
640 | } | ||
641 | |||
642 | rc = siena_mtd_get_fw_subtypes(efx, efx_mtd); | ||
643 | if (rc) | ||
644 | goto fail; | ||
645 | |||
646 | rc = efx_mtd_probe_device(efx, efx_mtd); | ||
647 | fail: | ||
648 | if (rc) | ||
649 | kfree(efx_mtd); | ||
650 | return rc; | ||
651 | } | ||
652 | |||