diff options
Diffstat (limited to 'drivers/net/ethernet/intel/i40e/i40e_common.c')
-rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_common.c | 253 |
1 files changed, 231 insertions, 22 deletions
diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 97a9b1fb4763..486a406789b8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c | |||
@@ -5448,6 +5448,163 @@ i40e_find_segment_in_package(u32 segment_type, | |||
5448 | return NULL; | 5448 | return NULL; |
5449 | } | 5449 | } |
5450 | 5450 | ||
5451 | /* Get section table in profile */ | ||
5452 | #define I40E_SECTION_TABLE(profile, sec_tbl) \ | ||
5453 | do { \ | ||
5454 | struct i40e_profile_segment *p = (profile); \ | ||
5455 | u32 count; \ | ||
5456 | u32 *nvm; \ | ||
5457 | count = p->device_table_count; \ | ||
5458 | nvm = (u32 *)&p->device_table[count]; \ | ||
5459 | sec_tbl = (struct i40e_section_table *)&nvm[nvm[0] + 1]; \ | ||
5460 | } while (0) | ||
5461 | |||
5462 | /* Get section header in profile */ | ||
5463 | #define I40E_SECTION_HEADER(profile, offset) \ | ||
5464 | (struct i40e_profile_section_header *)((u8 *)(profile) + (offset)) | ||
5465 | |||
5466 | /** | ||
5467 | * i40e_find_section_in_profile | ||
5468 | * @section_type: the section type to search for (i.e., SECTION_TYPE_NOTE) | ||
5469 | * @profile: pointer to the i40e segment header to be searched | ||
5470 | * | ||
5471 | * This function searches i40e segment for a particular section type. On | ||
5472 | * success it returns a pointer to the section header, otherwise it will | ||
5473 | * return NULL. | ||
5474 | **/ | ||
5475 | struct i40e_profile_section_header * | ||
5476 | i40e_find_section_in_profile(u32 section_type, | ||
5477 | struct i40e_profile_segment *profile) | ||
5478 | { | ||
5479 | struct i40e_profile_section_header *sec; | ||
5480 | struct i40e_section_table *sec_tbl; | ||
5481 | u32 sec_off; | ||
5482 | u32 i; | ||
5483 | |||
5484 | if (profile->header.type != SEGMENT_TYPE_I40E) | ||
5485 | return NULL; | ||
5486 | |||
5487 | I40E_SECTION_TABLE(profile, sec_tbl); | ||
5488 | |||
5489 | for (i = 0; i < sec_tbl->section_count; i++) { | ||
5490 | sec_off = sec_tbl->section_offset[i]; | ||
5491 | sec = I40E_SECTION_HEADER(profile, sec_off); | ||
5492 | if (sec->section.type == section_type) | ||
5493 | return sec; | ||
5494 | } | ||
5495 | |||
5496 | return NULL; | ||
5497 | } | ||
5498 | |||
5499 | /** | ||
5500 | * i40e_ddp_exec_aq_section - Execute generic AQ for DDP | ||
5501 | * @hw: pointer to the hw struct | ||
5502 | * @aq: command buffer containing all data to execute AQ | ||
5503 | **/ | ||
5504 | static enum | ||
5505 | i40e_status_code i40e_ddp_exec_aq_section(struct i40e_hw *hw, | ||
5506 | struct i40e_profile_aq_section *aq) | ||
5507 | { | ||
5508 | i40e_status status; | ||
5509 | struct i40e_aq_desc desc; | ||
5510 | u8 *msg = NULL; | ||
5511 | u16 msglen; | ||
5512 | |||
5513 | i40e_fill_default_direct_cmd_desc(&desc, aq->opcode); | ||
5514 | desc.flags |= cpu_to_le16(aq->flags); | ||
5515 | memcpy(desc.params.raw, aq->param, sizeof(desc.params.raw)); | ||
5516 | |||
5517 | msglen = aq->datalen; | ||
5518 | if (msglen) { | ||
5519 | desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | | ||
5520 | I40E_AQ_FLAG_RD)); | ||
5521 | if (msglen > I40E_AQ_LARGE_BUF) | ||
5522 | desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); | ||
5523 | desc.datalen = cpu_to_le16(msglen); | ||
5524 | msg = &aq->data[0]; | ||
5525 | } | ||
5526 | |||
5527 | status = i40e_asq_send_command(hw, &desc, msg, msglen, NULL); | ||
5528 | |||
5529 | if (status) { | ||
5530 | i40e_debug(hw, I40E_DEBUG_PACKAGE, | ||
5531 | "unable to exec DDP AQ opcode %u, error %d\n", | ||
5532 | aq->opcode, status); | ||
5533 | return status; | ||
5534 | } | ||
5535 | |||
5536 | /* copy returned desc to aq_buf */ | ||
5537 | memcpy(aq->param, desc.params.raw, sizeof(desc.params.raw)); | ||
5538 | |||
5539 | return 0; | ||
5540 | } | ||
5541 | |||
5542 | /** | ||
5543 | * i40e_validate_profile | ||
5544 | * @hw: pointer to the hardware structure | ||
5545 | * @profile: pointer to the profile segment of the package to be validated | ||
5546 | * @track_id: package tracking id | ||
5547 | * @rollback: flag if the profile is for rollback. | ||
5548 | * | ||
5549 | * Validates supported devices and profile's sections. | ||
5550 | */ | ||
5551 | static enum i40e_status_code | ||
5552 | i40e_validate_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile, | ||
5553 | u32 track_id, bool rollback) | ||
5554 | { | ||
5555 | struct i40e_profile_section_header *sec = NULL; | ||
5556 | i40e_status status = 0; | ||
5557 | struct i40e_section_table *sec_tbl; | ||
5558 | u32 vendor_dev_id; | ||
5559 | u32 dev_cnt; | ||
5560 | u32 sec_off; | ||
5561 | u32 i; | ||
5562 | |||
5563 | if (track_id == I40E_DDP_TRACKID_INVALID) { | ||
5564 | i40e_debug(hw, I40E_DEBUG_PACKAGE, "Invalid track_id\n"); | ||
5565 | return I40E_NOT_SUPPORTED; | ||
5566 | } | ||
5567 | |||
5568 | dev_cnt = profile->device_table_count; | ||
5569 | for (i = 0; i < dev_cnt; i++) { | ||
5570 | vendor_dev_id = profile->device_table[i].vendor_dev_id; | ||
5571 | if ((vendor_dev_id >> 16) == PCI_VENDOR_ID_INTEL && | ||
5572 | hw->device_id == (vendor_dev_id & 0xFFFF)) | ||
5573 | break; | ||
5574 | } | ||
5575 | if (dev_cnt && i == dev_cnt) { | ||
5576 | i40e_debug(hw, I40E_DEBUG_PACKAGE, | ||
5577 | "Device doesn't support DDP\n"); | ||
5578 | return I40E_ERR_DEVICE_NOT_SUPPORTED; | ||
5579 | } | ||
5580 | |||
5581 | I40E_SECTION_TABLE(profile, sec_tbl); | ||
5582 | |||
5583 | /* Validate sections types */ | ||
5584 | for (i = 0; i < sec_tbl->section_count; i++) { | ||
5585 | sec_off = sec_tbl->section_offset[i]; | ||
5586 | sec = I40E_SECTION_HEADER(profile, sec_off); | ||
5587 | if (rollback) { | ||
5588 | if (sec->section.type == SECTION_TYPE_MMIO || | ||
5589 | sec->section.type == SECTION_TYPE_AQ || | ||
5590 | sec->section.type == SECTION_TYPE_RB_AQ) { | ||
5591 | i40e_debug(hw, I40E_DEBUG_PACKAGE, | ||
5592 | "Not a roll-back package\n"); | ||
5593 | return I40E_NOT_SUPPORTED; | ||
5594 | } | ||
5595 | } else { | ||
5596 | if (sec->section.type == SECTION_TYPE_RB_AQ || | ||
5597 | sec->section.type == SECTION_TYPE_RB_MMIO) { | ||
5598 | i40e_debug(hw, I40E_DEBUG_PACKAGE, | ||
5599 | "Not an original package\n"); | ||
5600 | return I40E_NOT_SUPPORTED; | ||
5601 | } | ||
5602 | } | ||
5603 | } | ||
5604 | |||
5605 | return status; | ||
5606 | } | ||
5607 | |||
5451 | /** | 5608 | /** |
5452 | * i40e_write_profile | 5609 | * i40e_write_profile |
5453 | * @hw: pointer to the hardware structure | 5610 | * @hw: pointer to the hardware structure |
@@ -5463,47 +5620,99 @@ i40e_write_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile, | |||
5463 | i40e_status status = 0; | 5620 | i40e_status status = 0; |
5464 | struct i40e_section_table *sec_tbl; | 5621 | struct i40e_section_table *sec_tbl; |
5465 | struct i40e_profile_section_header *sec = NULL; | 5622 | struct i40e_profile_section_header *sec = NULL; |
5466 | u32 dev_cnt; | 5623 | struct i40e_profile_aq_section *ddp_aq; |
5467 | u32 vendor_dev_id; | ||
5468 | u32 *nvm; | ||
5469 | u32 section_size = 0; | 5624 | u32 section_size = 0; |
5470 | u32 offset = 0, info = 0; | 5625 | u32 offset = 0, info = 0; |
5626 | u32 sec_off; | ||
5471 | u32 i; | 5627 | u32 i; |
5472 | 5628 | ||
5473 | dev_cnt = profile->device_table_count; | 5629 | status = i40e_validate_profile(hw, profile, track_id, false); |
5630 | if (status) | ||
5631 | return status; | ||
5474 | 5632 | ||
5475 | for (i = 0; i < dev_cnt; i++) { | 5633 | I40E_SECTION_TABLE(profile, sec_tbl); |
5476 | vendor_dev_id = profile->device_table[i].vendor_dev_id; | 5634 | |
5477 | if ((vendor_dev_id >> 16) == PCI_VENDOR_ID_INTEL) | 5635 | for (i = 0; i < sec_tbl->section_count; i++) { |
5478 | if (hw->device_id == (vendor_dev_id & 0xFFFF)) | 5636 | sec_off = sec_tbl->section_offset[i]; |
5637 | sec = I40E_SECTION_HEADER(profile, sec_off); | ||
5638 | /* Process generic admin command */ | ||
5639 | if (sec->section.type == SECTION_TYPE_AQ) { | ||
5640 | ddp_aq = (struct i40e_profile_aq_section *)&sec[1]; | ||
5641 | status = i40e_ddp_exec_aq_section(hw, ddp_aq); | ||
5642 | if (status) { | ||
5643 | i40e_debug(hw, I40E_DEBUG_PACKAGE, | ||
5644 | "Failed to execute aq: section %d, opcode %u\n", | ||
5645 | i, ddp_aq->opcode); | ||
5479 | break; | 5646 | break; |
5647 | } | ||
5648 | sec->section.type = SECTION_TYPE_RB_AQ; | ||
5649 | } | ||
5650 | |||
5651 | /* Skip any non-mmio sections */ | ||
5652 | if (sec->section.type != SECTION_TYPE_MMIO) | ||
5653 | continue; | ||
5654 | |||
5655 | section_size = sec->section.size + | ||
5656 | sizeof(struct i40e_profile_section_header); | ||
5657 | |||
5658 | /* Write MMIO section */ | ||
5659 | status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size, | ||
5660 | track_id, &offset, &info, NULL); | ||
5661 | if (status) { | ||
5662 | i40e_debug(hw, I40E_DEBUG_PACKAGE, | ||
5663 | "Failed to write profile: section %d, offset %d, info %d\n", | ||
5664 | i, offset, info); | ||
5665 | break; | ||
5666 | } | ||
5480 | } | 5667 | } |
5481 | if (i == dev_cnt) { | 5668 | return status; |
5482 | i40e_debug(hw, I40E_DEBUG_PACKAGE, "Device doesn't support DDP"); | 5669 | } |
5483 | return I40E_ERR_DEVICE_NOT_SUPPORTED; | 5670 | |
5484 | } | 5671 | /** |
5672 | * i40e_rollback_profile | ||
5673 | * @hw: pointer to the hardware structure | ||
5674 | * @profile: pointer to the profile segment of the package to be removed | ||
5675 | * @track_id: package tracking id | ||
5676 | * | ||
5677 | * Rolls back previously loaded package. | ||
5678 | */ | ||
5679 | enum i40e_status_code | ||
5680 | i40e_rollback_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile, | ||
5681 | u32 track_id) | ||
5682 | { | ||
5683 | struct i40e_profile_section_header *sec = NULL; | ||
5684 | i40e_status status = 0; | ||
5685 | struct i40e_section_table *sec_tbl; | ||
5686 | u32 offset = 0, info = 0; | ||
5687 | u32 section_size = 0; | ||
5688 | u32 sec_off; | ||
5689 | int i; | ||
5485 | 5690 | ||
5486 | nvm = (u32 *)&profile->device_table[dev_cnt]; | 5691 | status = i40e_validate_profile(hw, profile, track_id, true); |
5487 | sec_tbl = (struct i40e_section_table *)&nvm[nvm[0] + 1]; | 5692 | if (status) |
5693 | return status; | ||
5488 | 5694 | ||
5489 | for (i = 0; i < sec_tbl->section_count; i++) { | 5695 | I40E_SECTION_TABLE(profile, sec_tbl); |
5490 | sec = (struct i40e_profile_section_header *)((u8 *)profile + | ||
5491 | sec_tbl->section_offset[i]); | ||
5492 | 5696 | ||
5493 | /* Skip 'AQ', 'note' and 'name' sections */ | 5697 | /* For rollback write sections in reverse */ |
5494 | if (sec->section.type != SECTION_TYPE_MMIO) | 5698 | for (i = sec_tbl->section_count - 1; i >= 0; i--) { |
5699 | sec_off = sec_tbl->section_offset[i]; | ||
5700 | sec = I40E_SECTION_HEADER(profile, sec_off); | ||
5701 | |||
5702 | /* Skip any non-rollback sections */ | ||
5703 | if (sec->section.type != SECTION_TYPE_RB_MMIO) | ||
5495 | continue; | 5704 | continue; |
5496 | 5705 | ||
5497 | section_size = sec->section.size + | 5706 | section_size = sec->section.size + |
5498 | sizeof(struct i40e_profile_section_header); | 5707 | sizeof(struct i40e_profile_section_header); |
5499 | 5708 | ||
5500 | /* Write profile */ | 5709 | /* Write roll-back MMIO section */ |
5501 | status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size, | 5710 | status = i40e_aq_write_ddp(hw, (void *)sec, (u16)section_size, |
5502 | track_id, &offset, &info, NULL); | 5711 | track_id, &offset, &info, NULL); |
5503 | if (status) { | 5712 | if (status) { |
5504 | i40e_debug(hw, I40E_DEBUG_PACKAGE, | 5713 | i40e_debug(hw, I40E_DEBUG_PACKAGE, |
5505 | "Failed to write profile: offset %d, info %d", | 5714 | "Failed to write profile: section %d, offset %d, info %d\n", |
5506 | offset, info); | 5715 | i, offset, info); |
5507 | break; | 5716 | break; |
5508 | } | 5717 | } |
5509 | } | 5718 | } |