diff --git a/linux/switchtec.h b/linux/switchtec.h index 027a842..095e8fe 100644 --- a/linux/switchtec.h +++ b/linux/switchtec.h @@ -32,6 +32,7 @@ #define SWITCHTEC_EVENT_EN_CLI BIT(2) #define SWITCHTEC_EVENT_EN_IRQ BIT(3) #define SWITCHTEC_EVENT_FATAL BIT(4) +#define SWITCHTEC_EVENT_NOT_SUPP BIT(31) #define SWITCHTEC_DMA_MRPC_EN BIT(0) @@ -137,17 +138,21 @@ enum { }; enum { - SWITCHTEC_GEN4_BL2_0_RUNNING = 0x01, - SWITCHTEC_GEN4_BL2_1_RUNNING = 0x02, - SWITCHTEC_GEN4_MAP0_RUNNING = 0x03, - SWITCHTEC_GEN4_MAP1_RUNNING = 0x04, - SWITCHTEC_GEN4_CFG0_RUNNING = 0x05, - SWITCHTEC_GEN4_CFG1_RUNNING = 0x06, - SWITCHTEC_GEN4_IMG0_RUNNING = 0x07, - SWITCHTEC_GEN4_IMG1_RUNNING = 0x08, + SWITCHTEC_GEN4_MAP0_RUNNING = 0x00, + SWITCHTEC_GEN4_MAP1_RUNNING = 0x01, + SWITCHTEC_GEN4_KEY0_RUNNING = 0x02, + SWITCHTEC_GEN4_KEY1_RUNNING = 0x03, + SWITCHTEC_GEN4_BL2_0_RUNNING = 0x04, + SWITCHTEC_GEN4_BL2_1_RUNNING = 0x05, + SWITCHTEC_GEN4_CFG0_RUNNING = 0x06, + SWITCHTEC_GEN4_CFG1_RUNNING = 0x07, + SWITCHTEC_GEN4_IMG0_RUNNING = 0x08, + SWITCHTEC_GEN4_IMG1_RUNNING = 0x09, }; enum { + SWITCHTEC_GEN4_KEY0_ACTIVE = 0, + SWITCHTEC_GEN4_KEY1_ACTIVE = 1, SWITCHTEC_GEN4_BL2_0_ACTIVE = 0, SWITCHTEC_GEN4_BL2_1_ACTIVE = 1, SWITCHTEC_GEN4_CFG0_ACTIVE = 0, @@ -186,32 +191,32 @@ struct sys_info_regs_gen4 { u8 mrpc_inband_ver; u32 reserved3[7]; u32 fw_update_tmo; - u32 table_version_cfg; - u32 table_version_img; + u32 xml_version_cfg; + u32 xml_version_img; u32 partition_id; u16 bl2_running; u16 cfg_running; u16 img_running; - u16 reserved4; - u32 reserved5[43]; + u16 key_running; + u32 reserved4[43]; u32 vendor_seeprom_twi; u32 vendor_table_revision; u32 vendor_specific_info[2]; u16 p2p_vendor_id; u16 p2p_device_id; u8 p2p_revision_id; - u8 reserved6[3]; + u8 reserved5[3]; u32 p2p_class_id; u16 subsystem_vendor_id; u16 subsystem_id; u32 p2p_serial_number[2]; u8 mac_addr[6]; - u8 reserved7[2]; - u32 reserved8[3]; + u8 reserved6[2]; + u32 reserved7[3]; char vendor_id[8]; char product_id[24]; char product_revision[2]; - u16 reserved9; + u16 reserved8; } __packed; struct sys_info_regs { @@ -260,16 +265,17 @@ struct flash_info_regs_gen4 { unsigned char bl2; unsigned char cfg; unsigned char img; - unsigned char reserved; + unsigned char key; } active_flag; u32 reserved[3]; - struct partition_info bl1; - struct partition_info bl2_0; - struct partition_info bl2_1; struct partition_info map0; struct partition_info map1; + struct partition_info key0; + struct partition_info key1; + struct partition_info bl2_0; + struct partition_info bl2_1; struct partition_info cfg0; struct partition_info cfg1; struct partition_info img0; diff --git a/linux/switchtec_ioctl.h b/linux/switchtec_ioctl.h index d962812..aaace58 100644 --- a/linux/switchtec_ioctl.h +++ b/linux/switchtec_ioctl.h @@ -34,9 +34,13 @@ #define SWITCHTEC_IOCTL_PART_VENDOR7 12 #define SWITCHTEC_IOCTL_PART_BL2_0 13 #define SWITCHTEC_IOCTL_PART_BL2_1 14 +#define SWITCHTEC_IOCTL_PART_MAP_0 15 +#define SWITCHTEC_IOCTL_PART_MAP_1 16 +#define SWITCHTEC_IOCTL_PART_KEY_0 17 +#define SWITCHTEC_IOCTL_PART_KEY_1 18 #define SWITCHTEC_NUM_PARTITIONS_GEN3 13 -#define SWITCHTEC_NUM_PARTITIONS_GEN4 15 +#define SWITCHTEC_NUM_PARTITIONS_GEN4 19 struct switchtec_ioctl_flash_info { __u64 flash_length; diff --git a/ntb_hw_switchtec.c b/ntb_hw_switchtec.c index 004be5c..497adee 100644 --- a/ntb_hw_switchtec.c +++ b/ntb_hw_switchtec.c @@ -396,7 +396,7 @@ static int switchtec_ntb_mw_set_trans(struct ntb_dev *ntb, int idx, if (rc) return rc; - if (addr == 0 || size == 0) { + if (size == 0) { if (idx < nr_direct_mw) switchtec_ntb_mw_clr_direct(sndev, idx); else @@ -434,9 +434,19 @@ static void switchtec_ntb_part_link_speed(struct switchtec_ntb *sndev, enum ntb_width *width) { struct switchtec_dev *stdev = sndev->stdev; + u32 pff; + u32 linksta; + + pff = ioread32(&stdev->mmio_part_cfg_all[partition].vep_pff_inst_id); + if (pff == 0xFFFFFFFF) { + dev_warn(&sndev->stdev->dev, + "Invalid pff, setting speed/width to 0"); + *speed = 0; + *width = 0; + return; + } - u32 pff = ioread32(&stdev->mmio_part_cfg[partition].vep_pff_inst_id); - u32 linksta = ioread32(&stdev->mmio_pff_csr[pff].pci_cap_region[13]); + linksta = ioread32(&stdev->mmio_pff_csr[pff].pci_cap_region[13]); if (speed) *speed = (linksta >> 16) & 0xF; @@ -853,6 +863,7 @@ static int switchtec_ntb_init_sndev(struct switchtec_ntb *sndev) tpart_vec |= ioread32(&sndev->mmio_ntb->ntp_info[self].target_part_low); part_map = ioread64(&sndev->mmio_ntb->ep_map); + tpart_vec &= part_map; part_map &= ~(1 << sndev->self_partition); if (!tpart_vec) { @@ -877,7 +888,7 @@ static int switchtec_ntb_init_sndev(struct switchtec_ntb *sndev) } sndev->peer_partition = __ffs64(tpart_vec); - if (!(part_map & (1 << sndev->peer_partition))) { + if (!(part_map & (1ULL << sndev->peer_partition))) { dev_err(&sndev->stdev->dev, "ntb target partition is not NT partition\n"); return -ENODEV; diff --git a/switchtec.c b/switchtec.c index 86c8c31..66b4978 100644 --- a/switchtec.c +++ b/switchtec.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "version.h" MODULE_DESCRIPTION("Microsemi Switchtec(tm) PCIe Management Driver"); @@ -705,6 +706,26 @@ static int flash_part_info_gen4(struct switchtec_dev *stdev, struct active_partition_info_gen4 __iomem *af = &fi->active_flag; switch (info->flash_partition) { + case SWITCHTEC_IOCTL_PART_MAP_0: + set_fw_info_part(info, &fi->map0); + break; + case SWITCHTEC_IOCTL_PART_MAP_1: + set_fw_info_part(info, &fi->map1); + break; + case SWITCHTEC_IOCTL_PART_KEY_0: + set_fw_info_part(info, &fi->key0); + if (ioread8(&af->key) == SWITCHTEC_GEN4_KEY0_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->key_running) == SWITCHTEC_GEN4_KEY0_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; + case SWITCHTEC_IOCTL_PART_KEY_1: + set_fw_info_part(info, &fi->key1); + if (ioread8(&af->key) == SWITCHTEC_GEN4_KEY1_ACTIVE) + info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; + if (ioread16(&si->key_running) == SWITCHTEC_GEN4_KEY1_RUNNING) + info->active |= SWITCHTEC_IOCTL_PART_RUNNING; + break; case SWITCHTEC_IOCTL_PART_BL2_0: set_fw_info_part(info, &fi->bl2_0); if (ioread8(&af->bl2) == SWITCHTEC_GEN4_BL2_0_ACTIVE) @@ -947,6 +968,9 @@ static int event_ctl(struct switchtec_dev *stdev, return PTR_ERR(reg); hdr = ioread32(reg); + if (hdr & SWITCHTEC_EVENT_NOT_SUPP) + return -ENOTSUPP; + for (i = 0; i < ARRAY_SIZE(ctl->data); i++) ctl->data[i] = ioread32(®[i + 1]); @@ -1019,7 +1043,7 @@ static int ioctl_event_ctl(struct switchtec_dev *stdev, for (ctl.index = 0; ctl.index < nr_idxs; ctl.index++) { ctl.flags = event_flags; ret = event_ctl(stdev, &ctl); - if (ret < 0) + if (ret < 0 && ret != -ENOTSUPP) return ret; } } else { @@ -1324,6 +1348,9 @@ static int mask_event(struct switchtec_dev *stdev, int eid, int idx) hdr_reg = event_regs[eid].map_reg(stdev, off, idx); hdr = ioread32(hdr_reg); + if (hdr & SWITCHTEC_EVENT_NOT_SUPP) + return 0; + if (!(hdr & SWITCHTEC_EVENT_OCCURRED && hdr & SWITCHTEC_EVENT_EN_IRQ)) return 0; @@ -1662,6 +1689,8 @@ static int switchtec_pci_probe(struct pci_dev *pdev, goto err_devadd; dev_info(&stdev->dev, "Management device registered.\n"); + pci_enable_pcie_error_reporting(pdev); + pci_save_state(pdev); return 0; @@ -1688,6 +1717,111 @@ static void switchtec_pci_remove(struct pci_dev *pdev) put_device(&stdev->dev); } +static void switchtec_pci_disable(struct pci_dev *pdev) +{ + struct switchtec_dev *stdev = pci_get_drvdata(pdev); + + if (pci_is_enabled(pdev)) { + pci_disable_pcie_error_reporting(pdev); + pci_disable_device(pdev); + } + + stdev_kill(stdev); +} + +static pci_ers_result_t switchtec_pci_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct switchtec_dev *stdev = pci_get_drvdata(pdev); + + /* + * A frozen channel requires a reset. When detected, this method + * will disable the device. The device will be restarted + * after the slot reset through driver's slot_reset callback. + */ + switch (state) { + case pci_channel_io_normal: + return PCI_ERS_RESULT_CAN_RECOVER; + case pci_channel_io_frozen: + switchtec_pci_disable(pdev); + dev_info(&stdev->dev, "frozen state error detected - reset needed\n"); + return PCI_ERS_RESULT_NEED_RESET; + case pci_channel_io_perm_failure: + switchtec_pci_disable(pdev); + dev_info(&stdev->dev, "failure state error detected - request disconnect\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + return PCI_ERS_RESULT_NEED_RESET; +} + +static pci_ers_result_t switchtec_pci_slot_reset(struct pci_dev *pdev) +{ + struct switchtec_dev *stdev = pci_get_drvdata(pdev); + int rc; + unsigned long res_start, res_len; + + dev_info(&stdev->dev, "slot_reset.\n"); + + pci_restore_state(pdev); + + /* + * First, release PCI resources and memory regions + */ + if (stdev->dma_mrpc){ + iowrite32(0, &stdev->mmio_mrpc->dma_en); + flush_wc_buf(stdev); + writeq(0, &stdev->mmio_mrpc->dma_addr); + dma_free_coherent(&stdev->pdev->dev, sizeof(*stdev->dma_mrpc), + stdev->dma_mrpc, stdev->dma_mrpc_dma_addr); + } + + res_start = pci_resource_start(pdev, 0); + res_len = pci_resource_len(pdev, 0); + + devm_release_mem_region(&pdev->dev, res_start, res_len); + + /* + * Second, reinitialize PCI resources, remap memory regions and reenable events. + */ + rc = switchtec_init_pci(stdev, pdev); + if (rc) { + dev_err(&stdev->dev, "failed to reinitialize pci.\n"); + goto err_ret; + } + + iowrite32(SWITCHTEC_EVENT_CLEAR | + SWITCHTEC_EVENT_EN_IRQ, + &stdev->mmio_part_cfg->mrpc_comp_hdr); + enable_link_state_events(stdev); + + if (stdev->dma_mrpc) + enable_dma_mrpc(stdev); + + stdev->alive = true; + stdev->mrpc_busy = 0; + + pci_enable_pcie_error_reporting(pdev); + pci_save_state(pdev); + + return PCI_ERS_RESULT_RECOVERED; +err_ret: + return PCI_ERS_RESULT_DISCONNECT; +} + +static void switchtec_pci_error_resume(struct pci_dev *pdev) +{ + struct switchtec_dev *stdev = pci_get_drvdata(pdev); + + dev_info(&stdev->dev, "resume.\n"); + pci_cleanup_aer_uncorrect_error_status(pdev); +} + +static const struct pci_error_handlers switchtec_pci_err_handler = { + .error_detected = switchtec_pci_error_detected, + .slot_reset = switchtec_pci_slot_reset, + .resume = switchtec_pci_error_resume, +}; + #define SWITCHTEC_PCI_DEVICE(device_id, gen) \ { \ .vendor = MICROSEMI_VENDOR_ID, \ @@ -1766,6 +1900,7 @@ static struct pci_driver switchtec_pci_driver = { .id_table = switchtec_pci_tbl, .probe = switchtec_pci_probe, .remove = switchtec_pci_remove, + .err_handler = &switchtec_pci_err_handler, }; static int __init switchtec_init(void) diff --git a/version b/version index 7e32cd5..c068b24 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.3 +1.4