Commit 52d5244096017bbd11164479116baceaede342b0
Committed by
James Bottomley
1 parent
912e3acde6
Exists in
master
and in
39 other branches
[SCSI] lpfc 8.3.24: Add request-firmware support
Add request-firmware support: - Add support for request_firmware interface for INTF2 SLI4 ports. - Add ability to reset SLI4 INTF2 ports. Signed-off-by: Alex Iannicelli <alex.iannicelli@emulex.com> Signed-off-by: James Smart <james.smart@emulex.com> Signed-off-by: James Bottomley <jbottomley@parallels.com>
Showing 5 changed files with 259 additions and 11 deletions Side-by-side Diff
drivers/scsi/lpfc/lpfc_attr.c
... | ... | @@ -755,18 +755,18 @@ |
755 | 755 | } |
756 | 756 | |
757 | 757 | /** |
758 | - * lpfc_sli4_fw_dump_request - Request firmware to perform a firmware dump | |
758 | + * lpfc_sli4_pdev_reg_request - Request physical dev to perform a register acc | |
759 | 759 | * @phba: lpfc_hba pointer. |
760 | 760 | * |
761 | 761 | * Description: |
762 | - * Request SLI4 interface type-2 device to perform a dump of firmware dump | |
763 | - * object into it's /dbg directory of the flash file system. | |
762 | + * Request SLI4 interface type-2 device to perform a physical register set | |
763 | + * access. | |
764 | 764 | * |
765 | 765 | * Returns: |
766 | 766 | * zero for success |
767 | 767 | **/ |
768 | 768 | static ssize_t |
769 | -lpfc_sli4_fw_dump_request(struct lpfc_hba *phba) | |
769 | +lpfc_sli4_pdev_reg_request(struct lpfc_hba *phba, uint32_t opcode) | |
770 | 770 | { |
771 | 771 | struct completion online_compl; |
772 | 772 | uint32_t reg_val; |
... | ... | @@ -776,6 +776,11 @@ |
776 | 776 | if (!phba->cfg_enable_hba_reset) |
777 | 777 | return -EIO; |
778 | 778 | |
779 | + if ((phba->sli_rev < LPFC_SLI_REV4) || | |
780 | + (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) != | |
781 | + LPFC_SLI_INTF_IF_TYPE_2)) | |
782 | + return -EPERM; | |
783 | + | |
779 | 784 | status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE); |
780 | 785 | |
781 | 786 | if (status != 0) |
... | ... | @@ -786,7 +791,14 @@ |
786 | 791 | |
787 | 792 | reg_val = readl(phba->sli4_hba.conf_regs_memmap_p + |
788 | 793 | LPFC_CTL_PDEV_CTL_OFFSET); |
789 | - reg_val |= LPFC_FW_DUMP_REQUEST; | |
794 | + | |
795 | + if (opcode == LPFC_FW_DUMP) | |
796 | + reg_val |= LPFC_FW_DUMP_REQUEST; | |
797 | + else if (opcode == LPFC_FW_RESET) | |
798 | + reg_val |= LPFC_CTL_PDEV_CTL_FRST; | |
799 | + else if (opcode == LPFC_DV_RESET) | |
800 | + reg_val |= LPFC_CTL_PDEV_CTL_DRST; | |
801 | + | |
790 | 802 | writel(reg_val, phba->sli4_hba.conf_regs_memmap_p + |
791 | 803 | LPFC_CTL_PDEV_CTL_OFFSET); |
792 | 804 | /* flush */ |
... | ... | @@ -904,12 +916,11 @@ |
904 | 916 | else |
905 | 917 | status = lpfc_do_offline(phba, LPFC_EVT_KILL); |
906 | 918 | else if (strncmp(buf, "dump", sizeof("dump") - 1) == 0) |
907 | - if ((phba->sli_rev < LPFC_SLI_REV4) || | |
908 | - (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) != | |
909 | - LPFC_SLI_INTF_IF_TYPE_2)) | |
910 | - return -EPERM; | |
911 | - else | |
912 | - status = lpfc_sli4_fw_dump_request(phba); | |
919 | + status = lpfc_sli4_pdev_reg_request(phba, LPFC_FW_DUMP); | |
920 | + else if (strncmp(buf, "fw_reset", sizeof("fw_reset") - 1) == 0) | |
921 | + status = lpfc_sli4_pdev_reg_request(phba, LPFC_FW_RESET); | |
922 | + else if (strncmp(buf, "dv_reset", sizeof("dv_reset") - 1) == 0) | |
923 | + status = lpfc_sli4_pdev_reg_request(phba, LPFC_DV_RESET); | |
913 | 924 | else |
914 | 925 | return -EINVAL; |
915 | 926 |
drivers/scsi/lpfc/lpfc_crtn.h
... | ... | @@ -430,6 +430,7 @@ |
430 | 430 | void lpfc_cleanup_vports_rrqs(struct lpfc_vport *, struct lpfc_nodelist *); |
431 | 431 | struct lpfc_node_rrq *lpfc_get_active_rrq(struct lpfc_vport *, uint16_t, |
432 | 432 | uint32_t); |
433 | +int lpfc_wr_object(struct lpfc_hba *, struct list_head *, uint32_t, uint32_t *); | |
433 | 434 | /* functions to support SR-IOV */ |
434 | 435 | int lpfc_sli_probe_sriov_nr_virtfn(struct lpfc_hba *, int); |
drivers/scsi/lpfc/lpfc_hw4.h
... | ... | @@ -821,6 +821,7 @@ |
821 | 821 | #define LPFC_MBOX_OPCODE_MQ_CREATE_EXT 0x5A |
822 | 822 | #define LPFC_MBOX_OPCODE_GET_FUNCTION_CONFIG 0xA0 |
823 | 823 | #define LPFC_MBOX_OPCODE_GET_PROFILE_CONFIG 0xA4 |
824 | +#define LPFC_MBOX_OPCODE_WRITE_OBJECT 0xAC | |
824 | 825 | #define LPFC_MBOX_OPCODE_GET_SLI4_PARAMETERS 0xB5 |
825 | 826 | |
826 | 827 | /* FCoE Opcodes */ |
... | ... | @@ -2372,6 +2373,29 @@ |
2372 | 2373 | #define MB_CEQ_STATUS_QUEUE_FLUSHING 0x4 |
2373 | 2374 | #define MB_CQE_STATUS_DMA_FAILED 0x5 |
2374 | 2375 | |
2376 | +#define LPFC_MBX_WR_CONFIG_MAX_BDE 8 | |
2377 | +struct lpfc_mbx_wr_object { | |
2378 | + struct mbox_header header; | |
2379 | + union { | |
2380 | + struct { | |
2381 | + uint32_t word4; | |
2382 | +#define lpfc_wr_object_eof_SHIFT 31 | |
2383 | +#define lpfc_wr_object_eof_MASK 0x00000001 | |
2384 | +#define lpfc_wr_object_eof_WORD word4 | |
2385 | +#define lpfc_wr_object_write_length_SHIFT 0 | |
2386 | +#define lpfc_wr_object_write_length_MASK 0x00FFFFFF | |
2387 | +#define lpfc_wr_object_write_length_WORD word4 | |
2388 | + uint32_t write_offset; | |
2389 | + uint32_t object_name[26]; | |
2390 | + uint32_t bde_count; | |
2391 | + struct ulp_bde64 bde[LPFC_MBX_WR_CONFIG_MAX_BDE]; | |
2392 | + } request; | |
2393 | + struct { | |
2394 | + uint32_t actual_write_length; | |
2395 | + } response; | |
2396 | + } u; | |
2397 | +}; | |
2398 | + | |
2375 | 2399 | /* mailbox queue entry structure */ |
2376 | 2400 | struct lpfc_mqe { |
2377 | 2401 | uint32_t word0; |
... | ... | @@ -2421,6 +2445,7 @@ |
2421 | 2445 | struct lpfc_mbx_get_func_cfg get_func_cfg; |
2422 | 2446 | struct lpfc_mbx_get_prof_cfg get_prof_cfg; |
2423 | 2447 | struct lpfc_mbx_nop nop; |
2448 | + struct lpfc_mbx_wr_object wr_object; | |
2424 | 2449 | } un; |
2425 | 2450 | }; |
2426 | 2451 | |
2427 | 2452 | |
... | ... | @@ -2966,9 +2991,29 @@ |
2966 | 2991 | struct gen_req64_wqe gen_req; |
2967 | 2992 | }; |
2968 | 2993 | |
2994 | +#define LPFC_GROUP_OJECT_MAGIC_NUM 0xfeaa0001 | |
2995 | +#define LPFC_FILE_TYPE_GROUP 0xf7 | |
2996 | +#define LPFC_FILE_ID_GROUP 0xa2 | |
2997 | +struct lpfc_grp_hdr { | |
2998 | + uint32_t size; | |
2999 | + uint32_t magic_number; | |
3000 | + uint32_t word2; | |
3001 | +#define lpfc_grp_hdr_file_type_SHIFT 24 | |
3002 | +#define lpfc_grp_hdr_file_type_MASK 0x000000FF | |
3003 | +#define lpfc_grp_hdr_file_type_WORD word2 | |
3004 | +#define lpfc_grp_hdr_id_SHIFT 16 | |
3005 | +#define lpfc_grp_hdr_id_MASK 0x000000FF | |
3006 | +#define lpfc_grp_hdr_id_WORD word2 | |
3007 | + uint8_t rev_name[128]; | |
3008 | +}; | |
3009 | + | |
2969 | 3010 | #define FCP_COMMAND 0x0 |
2970 | 3011 | #define FCP_COMMAND_DATA_OUT 0x1 |
2971 | 3012 | #define ELS_COMMAND_NON_FIP 0xC |
2972 | 3013 | #define ELS_COMMAND_FIP 0xD |
2973 | 3014 | #define OTHER_COMMAND 0x8 |
3015 | + | |
3016 | +#define LPFC_FW_DUMP 1 | |
3017 | +#define LPFC_FW_RESET 2 | |
3018 | +#define LPFC_DV_RESET 3 |
drivers/scsi/lpfc/lpfc_init.c
... | ... | @@ -30,6 +30,7 @@ |
30 | 30 | #include <linux/ctype.h> |
31 | 31 | #include <linux/aer.h> |
32 | 32 | #include <linux/slab.h> |
33 | +#include <linux/firmware.h> | |
33 | 34 | |
34 | 35 | #include <scsi/scsi.h> |
35 | 36 | #include <scsi/scsi_device.h> |
... | ... | @@ -8775,6 +8776,97 @@ |
8775 | 8776 | } |
8776 | 8777 | |
8777 | 8778 | /** |
8779 | + * lpfc_write_firmware - attempt to write a firmware image to the port | |
8780 | + * @phba: pointer to lpfc hba data structure. | |
8781 | + * @fw: pointer to firmware image returned from request_firmware. | |
8782 | + * | |
8783 | + * returns the number of bytes written if write is successful. | |
8784 | + * returns a negative error value if there were errors. | |
8785 | + * returns 0 if firmware matches currently active firmware on port. | |
8786 | + **/ | |
8787 | +int | |
8788 | +lpfc_write_firmware(struct lpfc_hba *phba, const struct firmware *fw) | |
8789 | +{ | |
8790 | + char fwrev[32]; | |
8791 | + struct lpfc_grp_hdr *image = (struct lpfc_grp_hdr *)fw->data; | |
8792 | + struct list_head dma_buffer_list; | |
8793 | + int i, rc = 0; | |
8794 | + struct lpfc_dmabuf *dmabuf, *next; | |
8795 | + uint32_t offset = 0, temp_offset = 0; | |
8796 | + | |
8797 | + INIT_LIST_HEAD(&dma_buffer_list); | |
8798 | + if ((image->magic_number != LPFC_GROUP_OJECT_MAGIC_NUM) || | |
8799 | + (bf_get(lpfc_grp_hdr_file_type, image) != LPFC_FILE_TYPE_GROUP) || | |
8800 | + (bf_get(lpfc_grp_hdr_id, image) != LPFC_FILE_ID_GROUP) || | |
8801 | + (image->size != fw->size)) { | |
8802 | + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, | |
8803 | + "3022 Invalid FW image found. " | |
8804 | + "Magic:%d Type:%x ID:%x\n", | |
8805 | + image->magic_number, | |
8806 | + bf_get(lpfc_grp_hdr_file_type, image), | |
8807 | + bf_get(lpfc_grp_hdr_id, image)); | |
8808 | + return -EINVAL; | |
8809 | + } | |
8810 | + lpfc_decode_firmware_rev(phba, fwrev, 1); | |
8811 | + if (strncmp(fwrev, image->rev_name, strnlen(fwrev, 16))) { | |
8812 | + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, | |
8813 | + "3023 Updating Firmware. Current Version:%s " | |
8814 | + "New Version:%s\n", | |
8815 | + fwrev, image->rev_name); | |
8816 | + for (i = 0; i < LPFC_MBX_WR_CONFIG_MAX_BDE; i++) { | |
8817 | + dmabuf = kzalloc(sizeof(struct lpfc_dmabuf), | |
8818 | + GFP_KERNEL); | |
8819 | + if (!dmabuf) { | |
8820 | + rc = -ENOMEM; | |
8821 | + goto out; | |
8822 | + } | |
8823 | + dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev, | |
8824 | + SLI4_PAGE_SIZE, | |
8825 | + &dmabuf->phys, | |
8826 | + GFP_KERNEL); | |
8827 | + if (!dmabuf->virt) { | |
8828 | + kfree(dmabuf); | |
8829 | + rc = -ENOMEM; | |
8830 | + goto out; | |
8831 | + } | |
8832 | + list_add_tail(&dmabuf->list, &dma_buffer_list); | |
8833 | + } | |
8834 | + while (offset < fw->size) { | |
8835 | + temp_offset = offset; | |
8836 | + list_for_each_entry(dmabuf, &dma_buffer_list, list) { | |
8837 | + if (offset + SLI4_PAGE_SIZE > fw->size) { | |
8838 | + temp_offset += fw->size - offset; | |
8839 | + memcpy(dmabuf->virt, | |
8840 | + fw->data + temp_offset, | |
8841 | + fw->size - offset); | |
8842 | + break; | |
8843 | + } | |
8844 | + temp_offset += SLI4_PAGE_SIZE; | |
8845 | + memcpy(dmabuf->virt, fw->data + temp_offset, | |
8846 | + SLI4_PAGE_SIZE); | |
8847 | + } | |
8848 | + rc = lpfc_wr_object(phba, &dma_buffer_list, | |
8849 | + (fw->size - offset), &offset); | |
8850 | + if (rc) { | |
8851 | + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, | |
8852 | + "3024 Firmware update failed. " | |
8853 | + "%d\n", rc); | |
8854 | + goto out; | |
8855 | + } | |
8856 | + } | |
8857 | + rc = offset; | |
8858 | + } | |
8859 | +out: | |
8860 | + list_for_each_entry_safe(dmabuf, next, &dma_buffer_list, list) { | |
8861 | + list_del(&dmabuf->list); | |
8862 | + dma_free_coherent(&phba->pcidev->dev, SLI4_PAGE_SIZE, | |
8863 | + dmabuf->virt, dmabuf->phys); | |
8864 | + kfree(dmabuf); | |
8865 | + } | |
8866 | + return rc; | |
8867 | +} | |
8868 | + | |
8869 | +/** | |
8778 | 8870 | * lpfc_pci_probe_one_s4 - PCI probe func to reg SLI-4 device to PCI subsys |
8779 | 8871 | * @pdev: pointer to PCI device |
8780 | 8872 | * @pid: pointer to PCI device identifier |
... | ... | @@ -8803,6 +8895,8 @@ |
8803 | 8895 | int mcnt; |
8804 | 8896 | int adjusted_fcp_eq_count; |
8805 | 8897 | int fcp_qidx; |
8898 | + const struct firmware *fw; | |
8899 | + uint8_t file_name[16]; | |
8806 | 8900 | |
8807 | 8901 | /* Allocate memory for HBA structure */ |
8808 | 8902 | phba = lpfc_hba_alloc(pdev); |
... | ... | @@ -8956,6 +9050,14 @@ |
8956 | 9050 | |
8957 | 9051 | /* Perform post initialization setup */ |
8958 | 9052 | lpfc_post_init_setup(phba); |
9053 | + | |
9054 | + /* check for firmware upgrade or downgrade */ | |
9055 | + snprintf(file_name, 16, "%s.grp", phba->ModelName); | |
9056 | + error = request_firmware(&fw, file_name, &phba->pcidev->dev); | |
9057 | + if (!error) { | |
9058 | + lpfc_write_firmware(phba, fw); | |
9059 | + release_firmware(fw); | |
9060 | + } | |
8959 | 9061 | |
8960 | 9062 | /* Check if there are static vports to be created. */ |
8961 | 9063 | lpfc_create_static_vport(phba); |
drivers/scsi/lpfc/lpfc_sli.c
... | ... | @@ -13500,6 +13500,96 @@ |
13500 | 13500 | } |
13501 | 13501 | |
13502 | 13502 | /** |
13503 | + * lpfc_wr_object - write an object to the firmware | |
13504 | + * @phba: HBA structure that indicates port to create a queue on. | |
13505 | + * @dmabuf_list: list of dmabufs to write to the port. | |
13506 | + * @size: the total byte value of the objects to write to the port. | |
13507 | + * @offset: the current offset to be used to start the transfer. | |
13508 | + * | |
13509 | + * This routine will create a wr_object mailbox command to send to the port. | |
13510 | + * the mailbox command will be constructed using the dma buffers described in | |
13511 | + * @dmabuf_list to create a list of BDEs. This routine will fill in as many | |
13512 | + * BDEs that the imbedded mailbox can support. The @offset variable will be | |
13513 | + * used to indicate the starting offset of the transfer and will also return | |
13514 | + * the offset after the write object mailbox has completed. @size is used to | |
13515 | + * determine the end of the object and whether the eof bit should be set. | |
13516 | + * | |
13517 | + * Return 0 is successful and offset will contain the the new offset to use | |
13518 | + * for the next write. | |
13519 | + * Return negative value for error cases. | |
13520 | + **/ | |
13521 | +int | |
13522 | +lpfc_wr_object(struct lpfc_hba *phba, struct list_head *dmabuf_list, | |
13523 | + uint32_t size, uint32_t *offset) | |
13524 | +{ | |
13525 | + struct lpfc_mbx_wr_object *wr_object; | |
13526 | + LPFC_MBOXQ_t *mbox; | |
13527 | + int rc = 0, i = 0; | |
13528 | + uint32_t shdr_status, shdr_add_status; | |
13529 | + uint32_t mbox_tmo; | |
13530 | + union lpfc_sli4_cfg_shdr *shdr; | |
13531 | + struct lpfc_dmabuf *dmabuf; | |
13532 | + uint32_t written = 0; | |
13533 | + | |
13534 | + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); | |
13535 | + if (!mbox) | |
13536 | + return -ENOMEM; | |
13537 | + | |
13538 | + lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON, | |
13539 | + LPFC_MBOX_OPCODE_WRITE_OBJECT, | |
13540 | + sizeof(struct lpfc_mbx_wr_object) - | |
13541 | + sizeof(struct lpfc_sli4_cfg_mhdr), LPFC_SLI4_MBX_EMBED); | |
13542 | + | |
13543 | + wr_object = (struct lpfc_mbx_wr_object *)&mbox->u.mqe.un.wr_object; | |
13544 | + wr_object->u.request.write_offset = *offset; | |
13545 | + sprintf((uint8_t *)wr_object->u.request.object_name, "/"); | |
13546 | + wr_object->u.request.object_name[0] = | |
13547 | + cpu_to_le32(wr_object->u.request.object_name[0]); | |
13548 | + bf_set(lpfc_wr_object_eof, &wr_object->u.request, 0); | |
13549 | + list_for_each_entry(dmabuf, dmabuf_list, list) { | |
13550 | + if (i >= LPFC_MBX_WR_CONFIG_MAX_BDE || written >= size) | |
13551 | + break; | |
13552 | + wr_object->u.request.bde[i].addrLow = putPaddrLow(dmabuf->phys); | |
13553 | + wr_object->u.request.bde[i].addrHigh = | |
13554 | + putPaddrHigh(dmabuf->phys); | |
13555 | + if (written + SLI4_PAGE_SIZE >= size) { | |
13556 | + wr_object->u.request.bde[i].tus.f.bdeSize = | |
13557 | + (size - written); | |
13558 | + written += (size - written); | |
13559 | + bf_set(lpfc_wr_object_eof, &wr_object->u.request, 1); | |
13560 | + } else { | |
13561 | + wr_object->u.request.bde[i].tus.f.bdeSize = | |
13562 | + SLI4_PAGE_SIZE; | |
13563 | + written += SLI4_PAGE_SIZE; | |
13564 | + } | |
13565 | + i++; | |
13566 | + } | |
13567 | + wr_object->u.request.bde_count = i; | |
13568 | + bf_set(lpfc_wr_object_write_length, &wr_object->u.request, written); | |
13569 | + if (!phba->sli4_hba.intr_enable) | |
13570 | + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); | |
13571 | + else { | |
13572 | + mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_SLI4_CONFIG); | |
13573 | + rc = lpfc_sli_issue_mbox_wait(phba, mbox, mbox_tmo); | |
13574 | + } | |
13575 | + /* The IOCTL status is embedded in the mailbox subheader. */ | |
13576 | + shdr = (union lpfc_sli4_cfg_shdr *) &wr_object->header.cfg_shdr; | |
13577 | + shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); | |
13578 | + shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response); | |
13579 | + if (rc != MBX_TIMEOUT) | |
13580 | + mempool_free(mbox, phba->mbox_mem_pool); | |
13581 | + if (shdr_status || shdr_add_status || rc) { | |
13582 | + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, | |
13583 | + "3025 Write Object mailbox failed with " | |
13584 | + "status x%x add_status x%x, mbx status x%x\n", | |
13585 | + shdr_status, shdr_add_status, rc); | |
13586 | + rc = -ENXIO; | |
13587 | + } else | |
13588 | + *offset += wr_object->u.response.actual_write_length; | |
13589 | + return rc; | |
13590 | +} | |
13591 | + | |
13592 | +/** | |
13503 | 13593 | * lpfc_cleanup_pending_mbox - Free up vport discovery mailbox commands. |
13504 | 13594 | * @vport: pointer to vport data structure. |
13505 | 13595 | * |