Add GAUDI2 ASIC specific support for AI scaling over the network. Signed-off-by: Omer Shpigelman <oshpigelman@xxxxxxxxx> Co-developed-by: Abhilash K V <kvabhilash@xxxxxxxxx> Signed-off-by: Abhilash K V <kvabhilash@xxxxxxxxx> Co-developed-by: Andrey Agranovich <aagranovich@xxxxxxxxx> Signed-off-by: Andrey Agranovich <aagranovich@xxxxxxxxx> Co-developed-by: Bharat Jauhari <bjauhari@xxxxxxxxx> Signed-off-by: Bharat Jauhari <bjauhari@xxxxxxxxx> Co-developed-by: David Meriin <dmeriin@xxxxxxxxx> Signed-off-by: David Meriin <dmeriin@xxxxxxxxx> Co-developed-by: Sagiv Ozeri <sozeri@xxxxxxxxx> Signed-off-by: Sagiv Ozeri <sozeri@xxxxxxxxx> Co-developed-by: Zvika Yehudai <zyehudai@xxxxxxxxx> Signed-off-by: Zvika Yehudai <zyehudai@xxxxxxxxx> --- drivers/accel/habanalabs/gaudi2/Makefile | 2 +- drivers/accel/habanalabs/gaudi2/gaudi2.c | 426 +++++++++++++++++- drivers/accel/habanalabs/gaudi2/gaudi2P.h | 41 +- drivers/accel/habanalabs/gaudi2/gaudi2_cn.c | 424 +++++++++++++++++ drivers/accel/habanalabs/gaudi2/gaudi2_cn.h | 42 ++ .../habanalabs/gaudi2/gaudi2_coresight.c | 145 +++++- 6 files changed, 1050 insertions(+), 30 deletions(-) create mode 100644 drivers/accel/habanalabs/gaudi2/gaudi2_cn.c create mode 100644 drivers/accel/habanalabs/gaudi2/gaudi2_cn.h diff --git a/drivers/accel/habanalabs/gaudi2/Makefile b/drivers/accel/habanalabs/gaudi2/Makefile index 1e047883ba74..289e69a355b1 100644 --- a/drivers/accel/habanalabs/gaudi2/Makefile +++ b/drivers/accel/habanalabs/gaudi2/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only HL_GAUDI2_FILES := gaudi2/gaudi2.o gaudi2/gaudi2_security.o \ - gaudi2/gaudi2_coresight.o + gaudi2/gaudi2_coresight.o gaudi2/gaudi2_cn.o diff --git a/drivers/accel/habanalabs/gaudi2/gaudi2.c b/drivers/accel/habanalabs/gaudi2/gaudi2.c index a22d2a93394e..6417c69d47af 100644 --- a/drivers/accel/habanalabs/gaudi2/gaudi2.c +++ b/drivers/accel/habanalabs/gaudi2/gaudi2.c @@ -1,12 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2020-2022 HabanaLabs, Ltd. + * Copyright 2020-2024 HabanaLabs, Ltd. * All Rights Reserved. */ #include "gaudi2P.h" +#include "gaudi2_cn.h" #include "gaudi2_masks.h" +#include "gaudi2_coresight_regs.h" #include "../include/gaudi2/gaudi2_special_blocks.h" #include "../include/hw_ip/mmu/mmu_general.h" #include "../include/hw_ip/mmu/mmu_v2_0.h" @@ -2216,6 +2218,9 @@ static bool gaudi2_get_edma_idle_status(struct hl_device *hdev, u64 *mask_arr, u struct engines_data *e); static u64 gaudi2_mmu_scramble_addr(struct hl_device *hdev, u64 raw_addr); static u64 gaudi2_mmu_descramble_addr(struct hl_device *hdev, u64 scrambled_addr); +static int gaudi2_send_cpu_message(struct hl_device *hdev, u32 *msg, u16 len, u32 timeout, + u64 *result); +static void gaudi2_init_cn(struct hl_device *hdev); static void gaudi2_init_scrambler_hbm(struct hl_device *hdev) { @@ -2382,15 +2387,17 @@ static int gaudi2_set_dram_properties(struct hl_device *hdev) hbm_drv_base_offset = roundup(CPU_FW_IMAGE_SIZE, prop->num_functional_hbms * SZ_8M); /* - * The NIC driver section size and the HMMU page tables section in the HBM needs - * to be the remaining size in the first dram page after taking into - * account the F/W image size + * The NIC driver section size and the HMMU page tables section in the HBM needs to be the + * remaining size in the first dram page after taking into account the F/W image size. */ + prop->nic_drv_size = (prop->dram_page_size - hbm_drv_base_offset) - + (HMMU_PAGE_TABLES_SIZE + EDMA_PQS_SIZE + EDMA_SCRATCHPAD_SIZE); + prop->nic_drv_addr = DRAM_PHYS_BASE + hbm_drv_base_offset; + + prop->clk = GAUDI2_NIC_CLK_FREQ / USEC_PER_SEC; /* Reserve region in HBM for HMMU page tables */ - prop->mmu_pgt_addr = DRAM_PHYS_BASE + hbm_drv_base_offset + - ((prop->dram_page_size - hbm_drv_base_offset) - - (HMMU_PAGE_TABLES_SIZE + EDMA_PQS_SIZE + EDMA_SCRATCHPAD_SIZE)); + prop->mmu_pgt_addr = prop->nic_drv_addr + prop->nic_drv_size; /* Set EDMA PQs HBM addresses */ edma_pq_base_addr = prop->mmu_pgt_addr + HMMU_PAGE_TABLES_SIZE; @@ -2409,6 +2416,7 @@ static int gaudi2_set_dram_properties(struct hl_device *hdev) static int gaudi2_set_fixed_properties(struct hl_device *hdev) { struct asic_fixed_properties *prop = &hdev->asic_prop; + struct hl_cn_properties *cn_prop = &prop->cn_props; struct hw_queue_properties *q_props; u32 num_sync_stream_queues = 0; int i, rc; @@ -2601,6 +2609,10 @@ static int gaudi2_set_fixed_properties(struct hl_device *hdev) prop->hbw_flush_reg = mmPCIE_WRAP_SPECIAL_GLBL_SPARE_0; + prop->macro_cfg_size = NIC_OFFSET; + cn_prop->status_packet_size = sizeof(struct cpucp_nic_status); + cn_prop->max_num_of_ports = NIC_NUMBER_OF_PORTS; + return 0; free_qprops: @@ -2996,8 +3008,16 @@ static int gaudi2_cpucp_info_get(struct hl_device *hdev) u64 dram_size; int rc; - if (!(gaudi2->hw_cap_initialized & HW_CAP_CPU_Q)) + if (!(gaudi2->hw_cap_initialized & HW_CAP_CPU_Q)) { + /* Skip for hard or device release reset flow. No need to repopulate. */ + if (!hdev->reset_info.in_reset) { + rc = gaudi2_cn_set_info(hdev, false); + if (rc) + return rc; + } + return 0; + } /* No point of asking this information again when not doing hard reset, as the device * CPU hasn't been reset @@ -3058,23 +3078,47 @@ static int gaudi2_cpucp_info_get(struct hl_device *hdev) prop->max_power_default = (u64) max_power; - return 0; + /* Repopulate post hard reset since device CPU has been reset */ + return gaudi2_cn_set_info(hdev, true); } -static int gaudi2_fetch_psoc_frequency(struct hl_device *hdev) +static int gaudi2_fetch_frequency(struct hl_device *hdev, u32 pll_index, u16 *pll_freq_arr) { struct gaudi2_device *gaudi2 = hdev->asic_specific; - u16 pll_freq_arr[HL_PLL_NUM_OUTPUTS]; - int rc; if (!(gaudi2->hw_cap_initialized & HW_CAP_CPU_Q)) return 0; - rc = hl_fw_cpucp_pll_info_get(hdev, HL_GAUDI2_CPU_PLL, pll_freq_arr); + return hl_fw_cpucp_pll_info_get(hdev, pll_index, pll_freq_arr); +} + +static int gaudi2_fetch_psoc_frequency(struct hl_device *hdev) +{ + u16 pll_freq_arr[HL_PLL_NUM_OUTPUTS] = {0}; + int rc; + + rc = gaudi2_fetch_frequency(hdev, HL_GAUDI2_CPU_PLL, pll_freq_arr); + if (rc) + return rc; + + if (pll_freq_arr[3] != 0) + hdev->asic_prop.psoc_timestamp_frequency = pll_freq_arr[3]; + + return 0; +} + +static int gaudi2_fetch_nic_frequency(struct hl_device *hdev) +{ + u16 pll_freq_arr[HL_PLL_NUM_OUTPUTS] = {0}; + int rc; + + rc = gaudi2_fetch_frequency(hdev, HL_GAUDI2_NIC_PLL, pll_freq_arr); if (rc) return rc; - hdev->asic_prop.psoc_timestamp_frequency = pll_freq_arr[3]; + /* DIV1 - nic_clk */ + if (pll_freq_arr[1] != 0) + hdev->asic_prop.clk = pll_freq_arr[1]; return 0; } @@ -3237,6 +3281,79 @@ static void gaudi2_init_arcs(struct hl_device *hdev) CFG_BASE + le32_to_cpu(dyn_regs->eng_arc_irq_ctrl); } +static int gaudi2_cn_clear_mem(struct hl_device *hdev) +{ + u32 nic_drv_size, gran_per_packet, num_iter; + struct asic_fixed_properties *asic_prop; + struct cpucp_cn_clear_mem_packet pkt; + bool use_cpucp; + int i, rc = 0; + u64 val = 0; + + if (!hdev->cn.ports_mask) + return rc; + + asic_prop = &hdev->asic_prop; + + use_cpucp = !!(hdev->asic_prop.fw_app_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_NIC_MEM_CLEAR_EN); + if (use_cpucp) { + /* nic driver size is expected to be less than 4GB in gaudi2 */ + if (asic_prop->nic_drv_size > BIT(32)) { + dev_err(hdev->dev, + "Failed to clear NIC memory, nic size is 0x%llx is bigger than 4GB\n", + asic_prop->nic_drv_size); + return -EINVAL; + } + + /* subtract 1 to support size of 4GB as well */ + nic_drv_size = (asic_prop->nic_drv_size - 1) & 0xFFFFFFFF; + /* max 250 MB per packet, in order to avoid CPUCP packet timeout */ + gran_per_packet = 250 * 1024 * 1024; + num_iter = (nic_drv_size + gran_per_packet) / gran_per_packet; + + for (i = 0; i < num_iter; i++) { + memset(&pkt, 0, sizeof(pkt)); + pkt.cpucp_pkt.ctl = cpu_to_le32(CPUCP_PACKET_NIC_CLR_MEM << + CPUCP_PKT_CTL_OPCODE_SHIFT); + pkt.mem_base_addr = cpu_to_le64(asic_prop->nic_drv_addr + + i * gran_per_packet); + + if (i < num_iter - 1) + pkt.size = cpu_to_le32(gran_per_packet); + else + /* add 1 as it was subtracted in original size calculation */ + pkt.size = cpu_to_le32(nic_drv_size - gran_per_packet * i + 1); + + rc = gaudi2_send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL); + if (rc) { + dev_err(hdev->dev, + "Failed to handle CPU-CP pkt %u, error %d\n", + CPUCP_PACKET_NIC_CLR_MEM, rc); + return rc; + } + } + } else { + for (i = 0; i < hdev->asic_prop.nic_drv_size; i += sizeof(val)) { + rc = hdev->asic_funcs->access_dev_mem(hdev, PCI_REGION_DRAM, + hdev->asic_prop.nic_drv_addr + i, + &val, DEBUGFS_WRITE64); + + if (rc) { + dev_err(hdev->dev, "Failed to set nic memory. addr: 0x%llx", + hdev->asic_prop.nic_drv_addr + i); + return rc; + } + + /* sleep every 32MB to avoid high CPU utilization */ + if (i && !(i % SZ_32M)) + usleep_range(50, 100); + } + } + + return rc; +} + static int gaudi2_scrub_arc_dccm(struct hl_device *hdev, u32 cpu_id) { u32 reg_base, reg_val; @@ -3303,6 +3420,15 @@ static int gaudi2_scrub_arcs_dccm(struct hl_device *hdev) return 0; } +static int gaudi2_nic_unmask_ecc_interrupts(struct hl_device *hdev) +{ + struct cpucp_packet pkt = {}; + + pkt.ctl = cpu_to_le32(CPUCP_PACKET_NIC_ECC_INTRS_UNMASK << CPUCP_PKT_CTL_OPCODE_SHIFT); + + return gaudi2_send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL); +} + static int gaudi2_late_init(struct hl_device *hdev) { struct gaudi2_device *gaudi2 = hdev->asic_specific; @@ -3329,6 +3455,24 @@ static int gaudi2_late_init(struct hl_device *hdev) goto disable_pci_access; } + rc = gaudi2_fetch_nic_frequency(hdev); + if (rc) { + dev_err(hdev->dev, "Failed to fetch NIC frequency\n"); + goto disable_pci_access; + } + + rc = gaudi2_cn_clear_mem(hdev); + if (rc) { + dev_err(hdev->dev, "Failed to clear CN memory\n"); + goto disable_pci_access; + } + + rc = gaudi2_nic_unmask_ecc_interrupts(hdev); + if (rc) { + dev_err(hdev->dev, "Failed to unmask NIC ECC interrupts\n"); + goto disable_pci_access; + } + gaudi2_init_arcs(hdev); rc = gaudi2_scrub_arcs_dccm(hdev); @@ -4836,6 +4980,13 @@ static void gaudi2_halt_engines(struct hl_device *hdev, bool hard_reset, bool fw else wait_timeout_ms = GAUDI2_RESET_WAIT_MSEC; + /* + * Mark the NIC as in reset to avoid any new NIC accesses to the H/W. This must be done + * before we stop the CPU as the NIC might use it e.g. get/set EEPROM data. + */ + if (hard_reset) + hl_cn_hard_reset_prepare(hdev, fw_reset); + if (fw_reset) goto skip_engines; @@ -4872,11 +5023,14 @@ static void gaudi2_halt_engines(struct hl_device *hdev, bool hard_reset, bool fw skip_engines: if (hard_reset) { + hl_cn_stop(hdev); gaudi2_disable_msix(hdev); return; } + gaudi2_cn_compute_reset_prepare(hdev); gaudi2_sync_irqs(hdev); + hl_cn_synchronize_irqs(hdev); } static void gaudi2_init_firmware_preload_params(struct hl_device *hdev) @@ -5377,7 +5531,7 @@ static void gaudi2_arm_monitors_for_virt_msix_db(struct hl_device *hdev, u32 sob static void gaudi2_prepare_sm_for_virt_msix_db(struct hl_device *hdev) { - u32 decoder_id, sob_id, first_mon_id, interrupt_id; + u32 decoder_id, port, sob_id, first_mon_id, interrupt_id; struct asic_fixed_properties *prop = &hdev->asic_prop; /* Decoder normal/abnormal interrupts */ @@ -5395,6 +5549,14 @@ static void gaudi2_prepare_sm_for_virt_msix_db(struct hl_device *hdev) interrupt_id += 1; gaudi2_arm_monitors_for_virt_msix_db(hdev, sob_id, first_mon_id, interrupt_id); } + + /* NIC EQ interrupts */ + for (port = 0 ; port < NIC_NUMBER_OF_PORTS ; ++port) { + sob_id = GAUDI2_RESERVED_SOB_NIC_PORT_FIRST + port; + first_mon_id = GAUDI2_RESERVED_MON_NIC_PORT_FIRST + 3 * port; + interrupt_id = GAUDI2_IRQ_NUM_NIC_PORT_FIRST + port; + gaudi2_arm_monitors_for_virt_msix_db(hdev, sob_id, first_mon_id, interrupt_id); + } } static void gaudi2_init_sm(struct hl_device *hdev) @@ -6161,6 +6323,8 @@ static int gaudi2_hw_init(struct hl_device *hdev) return rc; } + gaudi2_cn_quiescence(hdev); + gaudi2_init_scrambler_hbm(hdev); gaudi2_init_kdma(hdev); @@ -6187,6 +6351,7 @@ static int gaudi2_hw_init(struct hl_device *hdev) gaudi2_init_mme(hdev); gaudi2_init_rotator(hdev); gaudi2_init_dec(hdev); + gaudi2_init_cn(hdev); gaudi2_enable_timestamp(hdev); rc = gaudi2_coresight_init(hdev); @@ -6469,6 +6634,12 @@ static int gaudi2_hw_fini(struct hl_device *hdev, bool hard_reset, bool fw_reset gaudi2_poll_btm_indication(hdev, poll_timeout_us); } + /* On PLDM the NIC PHY link is always up, and because the NIC interrupts are enabled by + * default - need to disable the interrupts ASAP. + */ + if (hard_reset && hdev->pldm) + gaudi2_cn_disable_interrupts(hdev); + if (!gaudi2) return 0; @@ -6488,7 +6659,7 @@ static int gaudi2_hw_fini(struct hl_device *hdev, bool hard_reset, bool fw_reset HW_CAP_PMMU | HW_CAP_CPU | HW_CAP_CPU_Q | HW_CAP_SRAM_SCRAMBLER | HW_CAP_DMMU_MASK | HW_CAP_PDMA_MASK | HW_CAP_EDMA_MASK | HW_CAP_KDMA | - HW_CAP_MME_MASK | HW_CAP_ROT_MASK); + HW_CAP_MME_MASK | HW_CAP_ROT_MASK | HW_CAP_NIC_DRV); memset(gaudi2->events_stat, 0, sizeof(gaudi2->events_stat)); } else { @@ -7173,6 +7344,8 @@ static int gaudi2_compute_reset_late_init(struct hl_device *hdev) gaudi2_init_security(hdev); + gaudi2_cn_compute_reset_late_init(hdev); + /* Unmask all IRQs since some could have been received during the soft reset */ irq_arr_size = gaudi2->num_of_valid_hw_events * sizeof(gaudi2->hw_events[0]); return hl_fw_unmask_irq_arr(hdev, gaudi2->hw_events, irq_arr_size); @@ -7557,6 +7730,30 @@ static void gaudi2_hw_queues_unlock(struct hl_device *hdev) spin_unlock(&gaudi2->hw_queues_lock); } +void gaudi2_init_cn(struct hl_device *hdev) +{ + struct gaudi2_device *gaudi2 = hdev->asic_specific; + u32 i, reg_base, queue_id; + + if (!hdev->cn.ports_mask) + return; + + if (gaudi2->nic_hw_cap_initialized & HW_CAP_NIC_MASK) + return; + + queue_id = GAUDI2_QUEUE_ID_NIC_0_0; + + for (i = 0; i < NIC_NUMBER_OF_ENGINES; + i++, queue_id += NUM_OF_PQ_PER_QMAN) { + if (!(hdev->cn.ports_mask & BIT(i))) + continue; + + reg_base = gaudi2_qm_blocks_bases[queue_id]; + gaudi2_init_qman(hdev, reg_base, queue_id); + gaudi2->nic_hw_cap_initialized |= BIT_ULL(i); + } +} + static u32 gaudi2_get_pci_id(struct hl_device *hdev) { return hdev->pdev->device; @@ -7779,7 +7976,7 @@ static int gaudi2_mmu_shared_prepare(struct hl_device *hdev, u32 asid) { struct asic_fixed_properties *prop = &hdev->asic_prop; u32 rw_asid, offset; - int rc, i; + int rc, i, q; rw_asid = FIELD_PREP(ARC_FARM_KDMA_CTX_AXUSER_HB_ASID_RD_MASK, asid) | FIELD_PREP(ARC_FARM_KDMA_CTX_AXUSER_HB_ASID_WR_MASK, asid); @@ -7819,6 +8016,18 @@ static int gaudi2_mmu_shared_prepare(struct hl_device *hdev, u32 asid) if (rc) return rc; + /* NIC */ + for (i = 0 ; i < NIC_NUMBER_OF_MACROS ; i++) + for (q = 0 ; q < NIC_NUMBER_OF_QM_PER_MACRO ; q++) { + if (!(hdev->cn.ports_mask & BIT(i * NIC_NUMBER_OF_QM_PER_MACRO + q))) + continue; + + WREG32(mmNIC0_QM0_AXUSER_NONSECURED_HB_ASID + + i * NIC_OFFSET + q * NIC_QM_OFFSET, rw_asid); + WREG32(mmNIC0_QM0_AXUSER_NONSECURED_HB_MMU_BP + + i * NIC_OFFSET + q * NIC_QM_OFFSET, 0); + } + return 0; } @@ -7936,6 +8145,47 @@ static bool gaudi2_handle_ecc_event(struct hl_device *hdev, u16 event_type, return !!ecc_data->is_critical; } +static void gaudi2_handle_bmon_spmu_event(struct hl_device *hdev, u8 macro_index) +{ + /* For this point a profiler is not configuring the BMON and SPMU block and + * therefore we can't deduce which port and entity triggered the interrupt. + * So for now we only clear all interrupt registers to prevent interrupt flood + */ + u32 macro_offset = GAUDI2_SPMU_NIC1_DBG_0 - GAUDI2_SPMU_NIC0_DBG_0; + + WREG32(debug_spmu_regs[GAUDI2_SPMU_NIC0_DBG_0 + macro_index * macro_offset] + + mmSPMU_PMINTENCLR_EL1_OFFSET, 0x1); + WREG32(debug_spmu_regs[GAUDI2_SPMU_NIC0_DBG_1 + macro_index * macro_offset] + + mmSPMU_PMINTENCLR_EL1_OFFSET, 0x1); + + macro_offset = GAUDI2_BMON_NIC1_DBG_0_0 - GAUDI2_BMON_NIC0_DBG_0_0; + WREG32(debug_bmon_regs[GAUDI2_BMON_NIC0_DBG_0_0 + macro_index * macro_offset] + + mmBMON_INT_CLR_OFFSET, 0x1); + WREG32(debug_bmon_regs[GAUDI2_BMON_NIC0_DBG_1_0 + macro_index * macro_offset] + + mmBMON_INT_CLR_OFFSET, 0x1); + WREG32(debug_bmon_regs[GAUDI2_BMON_NIC0_DBG_2_0 + macro_index * macro_offset] + + mmBMON_INT_CLR_OFFSET, 0x1); + WREG32(debug_bmon_regs[GAUDI2_BMON_NIC0_DBG_0_1 + macro_index * macro_offset] + + mmBMON_INT_CLR_OFFSET, 0x1); + WREG32(debug_bmon_regs[GAUDI2_BMON_NIC0_DBG_1_1 + macro_index * macro_offset] + + mmBMON_INT_CLR_OFFSET, 0x1); + WREG32(debug_bmon_regs[GAUDI2_BMON_NIC0_DBG_2_1 + macro_index * macro_offset] + + mmBMON_INT_CLR_OFFSET, 0x1); +} + +static int gaudi2_handle_nic_sw_error_event(struct hl_device *hdev, u16 event_type, u8 macro_index, + struct hl_eq_nic_intr_cause *nic_intr_cause) +{ + u32 error_count; + + error_count = gaudi2_cn_handle_sw_error_event(hdev, event_type, macro_index, + nic_intr_cause); + + hl_check_for_glbl_errors(hdev); + + return error_count; +} + static void handle_lower_qman_data_on_err(struct hl_device *hdev, u64 qman_base, u32 engine_id) { struct undefined_opcode_info *undef_opcode = &hdev->captured_err_info.undef_opcode; @@ -8347,6 +8597,22 @@ static void gaudi2_check_if_razwi_happened(struct hl_device *hdev) gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_ROT, mod_idx, 0, NULL); } +static int gaudi2_handle_nic_axi_error_response_event(struct hl_device *hdev, u16 event_type, + u8 macro_index, struct hl_eq_nic_intr_cause *nic_intr_cause, + u64 *event_mask) +{ + u32 error_count = 0; + + error_count = gaudi2_cn_handle_axi_error_response_event(hdev, event_type, macro_index, + nic_intr_cause); + + /* check if RAZWI happened */ + gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_NIC, macro_index, 0, event_mask); + hl_check_for_glbl_errors(hdev); + + return error_count; +} + static int gaudi2_psoc_razwi_get_engines(struct gaudi2_razwi_info *razwi_info, u32 array_size, u32 axuser_xy, u32 *base, u16 *eng_id, char *eng_name) @@ -8560,6 +8826,11 @@ static int gaudi2_handle_qm_sei_err(struct hl_device *hdev, u16 event_type, qman_base = mmROT0_QM_BASE + index * ROT_OFFSET; module = RAZWI_ROT; break; + case GAUDI2_EVENT_NIC0_AXI_ERROR_RESPONSE ... GAUDI2_EVENT_NIC11_AXI_ERROR_RESPONSE: + index = event_type - GAUDI2_EVENT_NIC0_AXI_ERROR_RESPONSE; + qman_base = mmNIC0_QM0_BASE + index * NIC_OFFSET; + module = RAZWI_NIC; + break; default: return 0; } @@ -8684,6 +8955,13 @@ static int gaudi2_handle_qman_err(struct hl_device *hdev, u16 event_type, u64 *e qid_base = GAUDI2_QUEUE_ID_ROT_1_0; qman_base = mmROT1_QM_BASE; break; + case GAUDI2_EVENT_NIC0_QM0 ... GAUDI2_EVENT_NIC11_QM1: + index = event_type - GAUDI2_EVENT_NIC0_QM0; + qid_base = GAUDI2_QUEUE_ID_NIC_0_0 + index * QMAN_STREAMS; + qman_base = mmNIC0_QM0_BASE + + (index / NIC_NUMBER_OF_QM_PER_MACRO) * NIC_OFFSET + + (index % NIC_NUMBER_OF_QM_PER_MACRO) * NIC_QM_OFFSET; + break; default: return 0; } @@ -10176,6 +10454,25 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; break; + case GAUDI2_EVENT_NIC0_BMON_SPMU: + case GAUDI2_EVENT_NIC1_BMON_SPMU: + case GAUDI2_EVENT_NIC2_BMON_SPMU: + case GAUDI2_EVENT_NIC3_BMON_SPMU: + case GAUDI2_EVENT_NIC4_BMON_SPMU: + case GAUDI2_EVENT_NIC5_BMON_SPMU: + case GAUDI2_EVENT_NIC6_BMON_SPMU: + case GAUDI2_EVENT_NIC7_BMON_SPMU: + case GAUDI2_EVENT_NIC8_BMON_SPMU: + case GAUDI2_EVENT_NIC9_BMON_SPMU: + case GAUDI2_EVENT_NIC10_BMON_SPMU: + case GAUDI2_EVENT_NIC11_BMON_SPMU: + index = (event_type - GAUDI2_EVENT_NIC0_BMON_SPMU) / + (GAUDI2_EVENT_NIC1_BMON_SPMU - GAUDI2_EVENT_NIC0_BMON_SPMU); + gaudi2_handle_bmon_spmu_event(hdev, index); + error_count = GAUDI2_NA_EVENT_CAUSE; + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; + break; + case GAUDI2_EVENT_CPU_FIX_POWER_ENV_S: case GAUDI2_EVENT_CPU_FIX_POWER_ENV_E: case GAUDI2_EVENT_CPU_FIX_THERMAL_ENV_S: @@ -10213,6 +10510,33 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; + case GAUDI2_EVENT_NIC0_AXI_ERROR_RESPONSE ... GAUDI2_EVENT_NIC11_AXI_ERROR_RESPONSE: + index = (event_type - GAUDI2_EVENT_NIC0_AXI_ERROR_RESPONSE); + error_count = gaudi2_handle_nic_axi_error_response_event(hdev, event_type, index, + &eq_entry->nic_intr_cause, &event_mask); + error_count += gaudi2_handle_qm_sei_err(hdev, event_type, false, &event_mask); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; + break; + + case GAUDI2_EVENT_NIC0_SW_ERROR: + case GAUDI2_EVENT_NIC1_SW_ERROR: + case GAUDI2_EVENT_NIC2_SW_ERROR: + case GAUDI2_EVENT_NIC3_SW_ERROR: + case GAUDI2_EVENT_NIC4_SW_ERROR: + case GAUDI2_EVENT_NIC5_SW_ERROR: + case GAUDI2_EVENT_NIC6_SW_ERROR: + case GAUDI2_EVENT_NIC7_SW_ERROR: + case GAUDI2_EVENT_NIC8_SW_ERROR: + case GAUDI2_EVENT_NIC9_SW_ERROR: + case GAUDI2_EVENT_NIC10_SW_ERROR: + case GAUDI2_EVENT_NIC11_SW_ERROR: + index = (event_type - GAUDI2_EVENT_NIC0_SW_ERROR) / + (GAUDI2_EVENT_NIC1_SW_ERROR - GAUDI2_EVENT_NIC0_SW_ERROR); + error_count = gaudi2_handle_nic_sw_error_event(hdev, event_type, index, + &eq_entry->nic_intr_cause); + event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; + break; + case GAUDI2_EVENT_CPU_CPLD_SHUTDOWN_CAUSE: dev_info(hdev->dev, "CPLD shutdown cause, reset reason: 0x%llx\n", le64_to_cpu(eq_entry->data[0])); @@ -10233,6 +10557,11 @@ static void gaudi2_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_ent event_mask |= HL_NOTIFIER_EVENT_GENERAL_HW_ERR; break; + case GAUDI2_EVENT_CPU0_STATUS_NIC0_ENG0 ... GAUDI2_EVENT_CPU11_STATUS_NIC11_ENG1: + hl_cn_send_status(hdev, event_type - GAUDI2_EVENT_CPU0_STATUS_NIC0_ENG0, 0, 0); + error_count = GAUDI2_NA_EVENT_CAUSE; + break; + case GAUDI2_EVENT_ARC_DCCM_FULL: error_count = hl_arc_event_handle(hdev, event_type, &eq_entry->arc_data); event_mask |= HL_NOTIFIER_EVENT_USER_ENGINE_ERR; @@ -11510,6 +11839,54 @@ static u32 *gaudi2_get_stream_master_qid_arr(void) return NULL; } +static void gaudi2_update_nic_qmans_state(struct hl_device *hdev) +{ + struct gaudi2_device *gaudi2 = hdev->asic_specific; + u32 nic_mask, queue_id; + int nic_id; + + queue_id = GAUDI2_QUEUE_ID_NIC_0_0; + + for (nic_id = 0 ; nic_id < NIC_NUMBER_OF_ENGINES; + nic_id++, queue_id += NUM_OF_PQ_PER_QMAN) { + + nic_mask = BIT(HW_CAP_NIC_SHIFT + nic_id); + + /* We need to stop and disable the QMAN in case we already + * initialized it AND the firmware reported that the matching + * NIC is disabled + */ + if ((gaudi2->nic_hw_cap_initialized & nic_mask) && + (!(hdev->cn.ports_mask & BIT(nic_id)))) { + + gaudi2_stop_qman_common(hdev, gaudi2_qm_blocks_bases[queue_id]); + gaudi2_disable_qman_common(hdev, gaudi2_qm_blocks_bases[queue_id]); + + /* Remove that capability bit so no one would be able + * to submit to that NIC's QMAN + */ + gaudi2->nic_hw_cap_initialized &= ~nic_mask; + } + } +} + +static int gaudi2_cn_init(struct hl_device *hdev) +{ + int rc; + + rc = hl_cn_init(hdev); + if (rc) + return rc; + + /* After ports initialization (for the first time), we need to check + * whether the f/w reported on ports that are disabled. For those + * ports, we need to disable their QMANs and update the HW_CAP bits + */ + gaudi2_update_nic_qmans_state(hdev); + + return 0; +} + static void gaudi2_add_device_attr(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp, struct attribute_group *dev_vrm_attr_grp) { @@ -11590,6 +11967,17 @@ static void gaudi2_write_pte(struct hl_device *hdev, u64 addr, u64 val) writeq(val, hdev->pcie_bar[DRAM_BAR_ID] + (addr - gaudi2->dram_bar_cur_addr)); } +static int gaudi2_get_reg_pcie_addr(struct hl_device *hdev, u32 reg, u64 *pci_addr) +{ + u64 offset = CFG_BASE - STM_FLASH_BASE_ADDR + reg; + + if (pci_resource_len(hdev->pdev, SRAM_CFG_BAR_ID) < offset) + return -EINVAL; + + *pci_addr = offset; + return 0; +} + static const struct hl_asic_funcs gaudi2_funcs = { .early_init = gaudi2_early_init, .early_fini = gaudi2_early_fini, @@ -11641,10 +12029,13 @@ static const struct hl_asic_funcs gaudi2_funcs = { .get_eeprom_data = gaudi2_get_eeprom_data, .get_monitor_dump = gaudi2_get_monitor_dump, .send_cpu_message = gaudi2_send_cpu_message, + .cn_init = gaudi2_cn_init, + .cn_fini = hl_cn_fini, .pci_bars_map = gaudi2_pci_bars_map, .init_iatu = gaudi2_init_iatu, .rreg = hl_rreg, .wreg = hl_wreg, + .get_reg_pcie_addr = gaudi2_get_reg_pcie_addr, .halt_coresight = gaudi2_halt_coresight, .ctx_init = gaudi2_ctx_init, .ctx_fini = gaudi2_ctx_fini, @@ -11679,6 +12070,7 @@ static const struct hl_asic_funcs gaudi2_funcs = { .get_sob_addr = &gaudi2_get_sob_addr, .set_pci_memory_regions = gaudi2_set_pci_memory_regions, .get_stream_master_qid_arr = gaudi2_get_stream_master_qid_arr, + .cn_funcs = &gaudi2_cn_funcs, .check_if_razwi_happened = gaudi2_check_if_razwi_happened, .mmu_get_real_page_size = gaudi2_mmu_get_real_page_size, .access_dev_mem = hl_access_dev_mem, diff --git a/drivers/accel/habanalabs/gaudi2/gaudi2P.h b/drivers/accel/habanalabs/gaudi2/gaudi2P.h index eee41387b269..429e89703e36 100644 --- a/drivers/accel/habanalabs/gaudi2/gaudi2P.h +++ b/drivers/accel/habanalabs/gaudi2/gaudi2P.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 * - * Copyright 2020-2022 HabanaLabs, Ltd. + * Copyright 2020-2024 HabanaLabs, Ltd. * All Rights Reserved. * */ @@ -8,12 +8,15 @@ #ifndef GAUDI2P_H_ #define GAUDI2P_H_ +#include <linux/net/intel/cn_aux.h> +#include <linux/net/intel/gaudi2_aux.h> #include <uapi/drm/habanalabs_accel.h> #include "../common/habanalabs.h" #include <linux/habanalabs/hl_boot_if.h> #include "../include/gaudi2/gaudi2.h" #include "../include/gaudi2/gaudi2_packets.h" #include "../include/gaudi2/gaudi2_fw_if.h" +#include "../include/gaudi2/gaudi2_coresight.h" #include "../include/gaudi2/gaudi2_async_events.h" #define GAUDI2_LINUX_FW_FILE "habanalabs/gaudi2/gaudi2-fit.itb" @@ -86,7 +89,7 @@ #define GAUDI2_BOOT_FIT_REQ_TIMEOUT_USEC 10000000 /* 10s */ -#define GAUDI2_NIC_CLK_FREQ 450000000ull /* 450 MHz */ +#define GAUDI2_NIC_CLK_FREQ 488000000ull /* 488 MHz */ #define DC_POWER_DEFAULT 60000 /* 60W */ @@ -198,6 +201,7 @@ HW_CAP_HBM_SCRAMBLER_SW_RESET) #define HW_CAP_HBM_SCRAMBLER_SHIFT 41 #define HW_CAP_RESERVED BIT(43) +#define HW_CAP_NIC_DRV BIT(44) #define HW_CAP_MMU_MASK (HW_CAP_PMMU | HW_CAP_DMMU_MASK) /* Range Registers */ @@ -239,6 +243,8 @@ #define GAUDI2_NUM_TESTED_QS (GAUDI2_QUEUE_ID_CPU_PQ - GAUDI2_QUEUE_ID_PDMA_0_0) +extern u64 debug_bmon_regs[GAUDI2_BMON_LAST + 1]; +extern u64 debug_spmu_regs[GAUDI2_SPMU_LAST + 1]; enum gaudi2_reserved_sob_id { GAUDI2_RESERVED_SOB_CS_COMPLETION_FIRST, @@ -251,6 +257,9 @@ enum gaudi2_reserved_sob_id { GAUDI2_RESERVED_SOB_DEC_ABNRM_FIRST, GAUDI2_RESERVED_SOB_DEC_ABNRM_LAST = GAUDI2_RESERVED_SOB_DEC_ABNRM_FIRST + NUMBER_OF_DEC - 1, + GAUDI2_RESERVED_SOB_NIC_PORT_FIRST, + GAUDI2_RESERVED_SOB_NIC_PORT_LAST = + GAUDI2_RESERVED_SOB_NIC_PORT_FIRST + NIC_NUMBER_OF_PORTS - 1, GAUDI2_RESERVED_SOB_NUMBER }; @@ -265,6 +274,9 @@ enum gaudi2_reserved_mon_id { GAUDI2_RESERVED_MON_DEC_ABNRM_FIRST, GAUDI2_RESERVED_MON_DEC_ABNRM_LAST = GAUDI2_RESERVED_MON_DEC_ABNRM_FIRST + 3 * NUMBER_OF_DEC - 1, + GAUDI2_RESERVED_MON_NIC_PORT_FIRST, + GAUDI2_RESERVED_MON_NIC_PORT_LAST = + GAUDI2_RESERVED_MON_NIC_PORT_FIRST + 3 * NIC_NUMBER_OF_PORTS - 1, GAUDI2_RESERVED_MON_NUMBER }; @@ -513,13 +525,8 @@ struct gaudi2_queues_test_info { * @events_stat: array that holds histogram of all received events. * @events_stat_aggregate: same as events_stat but doesn't get cleared on reset. * @num_of_valid_hw_events: used to hold the number of valid H/W events. - * @nic_ports: array that holds all NIC ports manage structures. - * @nic_macros: array that holds all NIC macro manage structures. - * @core_info: core info to be used by the Ethernet driver. - * @aux_ops: functions for core <-> aux drivers communication. - * @flush_db_fifo: flag to force flush DB FIFO after a write. - * @hbm_cfg: HBM subsystem settings - * @hw_queues_lock_mutex: used by simulator instead of hw_queues_lock. + * @cn_aux_ops: functions for core <-> accel drivers communication. + * @cn_aux_data: data to be used by the core driver. * @queues_test_info: information used by the driver when testing the HW queues. */ struct gaudi2_device { @@ -549,6 +556,10 @@ struct gaudi2_device { u32 events_stat_aggregate[GAUDI2_EVENT_SIZE]; u32 num_of_valid_hw_events; + /* NIC fields */ + struct gaudi2_cn_aux_ops cn_aux_ops; + struct gaudi2_cn_aux_data cn_aux_data; + /* Queue testing */ struct gaudi2_queues_test_info queues_test_info[GAUDI2_NUM_TESTED_QS]; }; @@ -588,6 +599,7 @@ enum gaudi2_block_types { GAUDI2_BLOCK_TYPE_MAX }; +extern struct hl_cn_funcs gaudi2_cn_funcs; extern const u32 gaudi2_dma_core_blocks_bases[DMA_CORE_ID_SIZE]; extern const u32 gaudi2_qm_blocks_bases[GAUDI2_QUEUE_ID_SIZE]; extern const u32 gaudi2_mme_acc_blocks_bases[MME_ID_SIZE]; @@ -609,4 +621,15 @@ int gaudi2_init_security(struct hl_device *hdev); void gaudi2_ack_protection_bits_errors(struct hl_device *hdev); int gaudi2_send_device_activity(struct hl_device *hdev, bool open); +/* Functions exported for NIC */ +void gaudi2_cn_spmu_get_stats_info(struct hl_device *hdev, u32 port, struct hbl_cn_stat **stats, + u32 *n_stats); +int gaudi2_cn_spmu_config(struct hl_device *hdev, u32 port, u32 num_event_types, u32 event_types[], + bool enable); +int gaudi2_cn_spmu_sample(struct hl_device *hdev, u32 port, u32 num_out_data, u64 out_data[]); +void gaudi2_cn_disable_interrupts(struct hl_device *hdev); +void gaudi2_cn_quiescence(struct hl_device *hdev); +void gaudi2_cn_compute_reset_prepare(struct hl_device *hdev); +void gaudi2_cn_compute_reset_late_init(struct hl_device *hdev); + #endif /* GAUDI2P_H_ */ diff --git a/drivers/accel/habanalabs/gaudi2/gaudi2_cn.c b/drivers/accel/habanalabs/gaudi2/gaudi2_cn.c new file mode 100644 index 000000000000..9beb11b579e4 --- /dev/null +++ b/drivers/accel/habanalabs/gaudi2/gaudi2_cn.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright 2020-2022 HabanaLabs, Ltd. + * Copyright (C) 2023-2024, Intel Corporation. + * All Rights Reserved. + */ + +#include "gaudi2_cn.h" +#include "../include/gaudi2/asic_reg/gaudi2_regs.h" +#include "../include/gaudi2/gaudi2_async_ids_map_extended.h" +#include "../include/hw_ip/nic/nic_general.h" + +static bool gaudi2_cn_get_hw_cap(struct hl_device *hdev); + +int gaudi2_cn_handle_sw_error_event(struct hl_device *hdev, u16 event_type, u8 macro_index, + struct hl_eq_nic_intr_cause *nic_intr_cause) +{ + struct hbl_aux_dev *aux_dev = &hdev->cn.cn_aux_dev; + struct gaudi2_device *gaudi2 = hdev->asic_specific; + struct gaudi2_cn_aux_ops *aux_ops = &gaudi2->cn_aux_ops; + u32 error_count = 0; + + if (aux_ops->sw_err_event_handler) + error_count = aux_ops->sw_err_event_handler(aux_dev, event_type, macro_index, + nic_intr_cause); + + return error_count; +} + +int gaudi2_cn_handle_axi_error_response_event(struct hl_device *hdev, u16 event_type, + u8 macro_index, + struct hl_eq_nic_intr_cause *nic_intr_cause) +{ + struct hbl_aux_dev *aux_dev = &hdev->cn.cn_aux_dev; + struct gaudi2_device *gaudi2 = hdev->asic_specific; + struct gaudi2_cn_aux_ops *aux_ops = &gaudi2->cn_aux_ops; + u32 error_count = 0; + + if (aux_ops->axi_error_response_event_handler) + error_count = aux_ops->axi_error_response_event_handler(aux_dev, event_type, + macro_index, + nic_intr_cause); + + return error_count; +} + +/** + * gaudi2_cn_disable_interrupts() - Disable interrupts of all ports. + * Gaudi2 CN interrupts are enabled by default, need to disable them ASAP + * before ports init and after hard reset. + * + * @hdev: habanalabs device structure. + */ +void gaudi2_cn_disable_interrupts(struct hl_device *hdev) +{ + u32 port; + + if (!hdev->cn.ports_mask) + return; + + /* we only need the port number for NIC_WREG32 */ + for (port = 0 ; port < NIC_NUMBER_OF_PORTS ; port++) { + NIC_WREG32(mmNIC0_QPC0_EVENT_QUE_CFG, 0); + NIC_WREG32(mmNIC0_QPC0_INTERRUPT_EN, 0); + NIC_WREG32(mmNIC0_QPC0_INTERRUPT_MASK, 0xFFFFFFFF); + + /* This registers needs to be configured only in case of PLDM */ + if (hdev->pldm) { + NIC_WREG32(mmNIC0_QPC0_INTERRUPT_RESP_ERR_MASK, 0xFFFFFFFF); + NIC_WREG32(mmNIC0_TXE0_INTERRUPT_MASK, 0xFFFFFFFF); + NIC_WREG32(mmNIC0_RXE0_SPI_INTR_MASK, 0xFFFFFFFF); + NIC_WREG32(mmNIC0_RXE0_SEI_INTR_MASK, 0xFFFFFFFF); + NIC_WREG32(mmNIC0_TXS0_INTERRUPT_MASK, 0xFFFFFFFF); + } + + /* WA for H/W bug H6-3339 - mask the link UP interrupt */ + NIC_MACRO_WREG32(mmNIC0_PHY_PHY_LINK_STS_INTR, 0x1); + } + + /* flush */ + port = 0; + NIC_RREG32(mmNIC0_QPC0_EVENT_QUE_CFG); +} + +/** + * gaudi2_cn_quiescence() - make sure that NIC does not generate events nor + * receives traffic. + * Gaudi2 default values at power-up and after hard-reset are interrupts enabled + * and Rx enabled, we need to disable them until driver configuration is + * complete. + * + * @hdev: habanalabs device structure. + */ +void gaudi2_cn_quiescence(struct hl_device *hdev) +{ + /* + * Do not quiescence the ports during device release + * reset aka soft reset flow. + */ + if (gaudi2_cn_get_hw_cap(hdev)) + return; + + dev_dbg(hdev->dev, "Quiescence the NICs\n"); + + gaudi2_cn_disable_interrupts(hdev); +} + +static bool gaudi2_cn_get_hw_cap(struct hl_device *hdev) +{ + struct gaudi2_device *gaudi2 = hdev->asic_specific; + + return (gaudi2->hw_cap_initialized & HW_CAP_NIC_DRV); +} + +static void gaudi2_cn_set_hw_cap(struct hl_device *hdev, bool enable) +{ + struct gaudi2_device *gaudi2 = hdev->asic_specific; + + if (enable) + gaudi2->hw_cap_initialized |= HW_CAP_NIC_DRV; + else + gaudi2->hw_cap_initialized &= ~HW_CAP_NIC_DRV; +} + +/** + * gaudi2_cn_override_ports_ext_mask() - Set the external ports mask. + * @hdev: Hl device whose external ports mask to return. + * @ports_ext_mask: Out, the external ports mask. + */ +static void gaudi2_cn_override_ports_ext_mask(struct hl_device *hdev, uint64_t *ports_ext_mask) +{ + /* For asic type GAUDI2B, the external ports mask shouldn't be changed */ + if (hdev->asic_type == ASIC_GAUDI2B) { + *ports_ext_mask = hdev->cn.ports_ext_mask; + return; + } + + /* If we are running on a PCI card, all the ports should be set as external */ + if (hdev->card_type == cpucp_card_type_pci) { + *ports_ext_mask = hdev->cn.ports_mask; + return; + } + + /* For HLS2 setup type, the external ports mask shouldn't be changed */ + *ports_ext_mask = hdev->cn.ports_ext_mask; +} + +static int gaudi2_cn_check_oui_prefix_validity(u8 *mac_addr) +{ + u8 mac[ETH_ALEN]; + int i; + + for (i = 0 ; i < 3 ; i++) + mac[i] = HABANALABS_MAC_OUI_1 >> (8 * (2 - i)); + + if (!strncmp(mac, mac_addr, 3)) + return 1; + + for (i = 0 ; i < 3 ; i++) + mac[i] = HABANALABS_MAC_OUI_2 >> (8 * (2 - i)); + + if (!strncmp(mac, mac_addr, 3)) + return 1; + + return 0; +} + +int gaudi2_cn_set_info(struct hl_device *hdev, bool get_from_fw) +{ + struct hbl_cn_cpucp_info *cn_cpucp_info = &hdev->asic_prop.cn_props.cpucp_info; + struct cpucp_info *cpucp_info = &hdev->asic_prop.cpucp_info; + struct hbl_cn_cpucp_mac_addr *mac_arr = cn_cpucp_info->mac_addrs; + struct hl_cn *cn = &hdev->cn; + u32 serdes_type = MAX_NUM_SERDES_TYPE; + u8 mac[ETH_ALEN], *mac_addr; + int rc, i; + + /* copy the MAC OUI in reverse */ + for (i = 0 ; i < 3 ; i++) + mac[i] = HABANALABS_MAC_OUI_1 >> (8 * (2 - i)); + + if (get_from_fw) { + rc = hl_cn_cpucp_info_get(hdev); + if (rc) + return rc; + + cn->ports_mask &= cn_cpucp_info->link_mask[0]; + cn->ports_ext_mask &= cn_cpucp_info->link_ext_mask[0]; + cn->auto_neg_mask &= cn_cpucp_info->auto_neg_mask[0]; + + serdes_type = cn_cpucp_info->serdes_type; + + /* check for invalid MAC addresses from F/W (bad OUI) */ + for (i = 0 ; i < NIC_NUMBER_OF_PORTS ; i++) { + if (!(cn->ports_mask & BIT(i))) + continue; + + mac_addr = mac_arr[i].mac_addr; + if (!gaudi2_cn_check_oui_prefix_validity(mac_addr)) + dev_warn(hdev->dev, "unrecognized MAC OUI %pM, port %d\n", + mac_addr, i); + } + + cn->card_location = le32_to_cpu(cpucp_info->card_location); + cn->use_fw_serdes_info = true; + } else { + /* No F/W, hence need to set the MACs manually (randomize) */ + get_random_bytes(&mac[3], 2); + + for (i = 0 ; i < NIC_NUMBER_OF_PORTS ; i++) { + if (!(cn->ports_mask & BIT(i))) + continue; + + mac[ETH_ALEN - 1] = i; + memcpy(mac_arr[i].mac_addr, mac, ETH_ALEN); + } + + dev_warn(hdev->dev, "can't read card location as FW security is enabled\n"); + } + + switch (serdes_type) { + case HLS2_SERDES_TYPE: + hdev->asic_prop.server_type = HL_SERVER_GAUDI2_HLS2; + break; + case HLS2_TYPE_1_SERDES_TYPE: + hdev->asic_prop.server_type = HL_SERVER_GAUDI2_TYPE1; + break; + default: + hdev->asic_prop.server_type = HL_SERVER_TYPE_UNKNOWN; + + if (get_from_fw) { + dev_err(hdev->dev, "bad SerDes type %d\n", serdes_type); + return -EFAULT; + } + break; + } + + /* If we are running on non HLS2 setup or a PCI card, all the ports should be set as + * external (the only exception is when the asic type is GADUI2B). + */ + if (hdev->card_type == cpucp_card_type_pci) { + if (hdev->asic_type != ASIC_GAUDI2B) + cn->ports_ext_mask = cn->ports_mask; + + cn->auto_neg_mask &= ~cn->ports_ext_mask; + } + + gaudi2_cn_override_ports_ext_mask(hdev, &cn->ports_ext_mask); + + if (hdev->card_type == cpucp_card_type_pci) + cn->auto_neg_mask &= ~cn->ports_ext_mask; + + /* Disable ANLT on NIC 0 ports (due to lane swapping) */ + cn->auto_neg_mask &= ~0x3; + + cn->lanes_per_port = 2; + cn->load_fw = false; + cn->eth_on_internal = false; + + return 0; +} + +static int gaudi2_cn_pre_core_init(struct hl_device *hdev) +{ + return 0; +} + +static char *gaudi2_cn_get_event_name(struct hbl_aux_dev *aux_dev, u16 event_type) +{ + return gaudi2_irq_map_table[event_type].valid ? gaudi2_irq_map_table[event_type].name : + "N/A Event"; +} + +static int gaudi2_cn_poll_mem(struct hbl_aux_dev *aux_dev, u32 *addr, u32 *val, + hbl_cn_poll_cond_func func) +{ + return hl_poll_timeout_memory(hdev, addr, *val, func(*val, NULL), 10, + HL_DEVICE_TIMEOUT_USEC, true); +} + +static void *gaudi2_cn_dma_alloc_coherent(struct hbl_aux_dev *aux_dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ + return hl_cn_dma_alloc_coherent(aux_dev, size, dma_handle, flag); +} + +static void gaudi2_cn_dma_free_coherent(struct hbl_aux_dev *aux_dev, size_t size, void *cpu_addr, + dma_addr_t dma_handle) +{ + hl_cn_dma_free_coherent(aux_dev, size, cpu_addr, dma_handle); +} + +static void *gaudi2_cn_dma_pool_zalloc(struct hbl_aux_dev *aux_dev, size_t size, gfp_t mem_flags, + dma_addr_t *dma_handle) +{ + return hl_cn_dma_pool_zalloc(aux_dev, size, mem_flags, dma_handle); +} + +static void gaudi2_cn_dma_pool_free(struct hbl_aux_dev *aux_dev, void *vaddr, dma_addr_t dma_addr) +{ + hl_cn_dma_pool_free(aux_dev, vaddr, dma_addr); +} + +static void gaudi2_cn_set_cn_data(struct hl_device *hdev) +{ + struct gaudi2_device *gaudi2 = hdev->asic_specific; + struct gaudi2_cn_aux_data *gaudi2_aux_data; + struct gaudi2_cn_aux_ops *gaudi2_aux_ops; + struct hbl_cn_aux_data *aux_data; + struct hbl_cn_aux_ops *aux_ops; + struct hl_cn *cn = &hdev->cn; + struct hbl_aux_dev *aux_dev; + + aux_dev = &cn->cn_aux_dev; + aux_data = aux_dev->aux_data; + gaudi2_aux_data = &gaudi2->cn_aux_data; + aux_data->asic_specific = gaudi2_aux_data; + aux_ops = aux_dev->aux_ops; + gaudi2_aux_ops = &gaudi2->cn_aux_ops; + aux_ops->asic_ops = gaudi2_aux_ops; + + gaudi2_aux_data->cfg_base = CFG_BASE; + gaudi2_aux_data->fw_security_enabled = hdev->asic_prop.fw_security_enabled; + gaudi2_aux_data->msix_enabled = !!(gaudi2->hw_cap_initialized & HW_CAP_MSIX); + gaudi2_aux_data->irq_num_port_base = GAUDI2_IRQ_NUM_NIC_PORT_FIRST; + gaudi2_aux_data->sob_id_base = GAUDI2_RESERVED_SOB_NIC_PORT_FIRST; + gaudi2_aux_data->sob_inc_cfg_val = GAUDI2_SOB_INCREMENT_BY_ONE; + gaudi2_aux_data->setup_type = GAUDI2_SETUP_TYPE_HLS2; + + /* cn2accel */ + gaudi2_aux_ops->get_event_name = gaudi2_cn_get_event_name; + gaudi2_aux_ops->poll_mem = gaudi2_cn_poll_mem; + gaudi2_aux_ops->dma_alloc_coherent = gaudi2_cn_dma_alloc_coherent; + gaudi2_aux_ops->dma_free_coherent = gaudi2_cn_dma_free_coherent; + gaudi2_aux_ops->dma_pool_zalloc = gaudi2_cn_dma_pool_zalloc; + gaudi2_aux_ops->dma_pool_free = gaudi2_cn_dma_pool_free; + gaudi2_aux_ops->spmu_get_stats_info = hl_cn_spmu_get_stats_info; + gaudi2_aux_ops->spmu_config = hl_cn_spmu_config; + gaudi2_aux_ops->spmu_sample = hl_cn_spmu_sample; + gaudi2_aux_ops->poll_reg = hl_cn_poll_reg; + gaudi2_aux_ops->send_cpu_message = hl_cn_send_cpu_message; + gaudi2_aux_ops->post_send_status = hl_cn_post_send_status; +} + +void gaudi2_cn_compute_reset_prepare(struct hl_device *hdev) +{ + struct gaudi2_device *gaudi2 = hdev->asic_specific; + struct gaudi2_cn_aux_ops *gaudi2_aux_ops; + struct hl_cn *cn = &hdev->cn; + struct hbl_aux_dev *aux_dev; + + aux_dev = &cn->cn_aux_dev; + gaudi2_aux_ops = &gaudi2->cn_aux_ops; + + if (gaudi2_aux_ops->reset_prepare) + gaudi2_aux_ops->reset_prepare(aux_dev); +} + +void gaudi2_cn_compute_reset_late_init(struct hl_device *hdev) +{ + struct gaudi2_device *gaudi2 = hdev->asic_specific; + struct gaudi2_cn_aux_ops *gaudi2_aux_ops; + struct hl_cn *cn = &hdev->cn; + struct hbl_aux_dev *aux_dev; + + aux_dev = &cn->cn_aux_dev; + gaudi2_aux_ops = &gaudi2->cn_aux_ops; + + if (gaudi2_aux_ops->reset_late_init) + gaudi2_aux_ops->reset_late_init(aux_dev); +} + +static void gaudi2_cn_post_send_status(struct hl_device *hdev, u32 port) +{ + hl_fw_unmask_irq(hdev, GAUDI2_EVENT_CPU0_STATUS_NIC0_ENG0 + port); +} + +static void gaudi2_cn_ports_stop_prepare(struct hl_device *hdev, bool fw_reset, bool in_teardown) +{ + struct gaudi2_device *gaudi2 = hdev->asic_specific; + struct gaudi2_cn_aux_ops *gaudi2_aux_ops; + struct hl_cn *cn = &hdev->cn; + struct hbl_aux_dev *aux_dev; + + aux_dev = &cn->cn_aux_dev; + gaudi2_aux_ops = &gaudi2->cn_aux_ops; + + if (gaudi2_aux_ops->ports_stop_prepare) + gaudi2_aux_ops->ports_stop_prepare(aux_dev, fw_reset, in_teardown); +} + +static int gaudi2_cn_send_port_cpucp_status(struct hl_device *hdev, u32 port, u8 cmd, u8 period) +{ + struct gaudi2_device *gaudi2 = hdev->asic_specific; + struct gaudi2_cn_aux_ops *gaudi2_aux_ops; + struct hl_cn *cn = &hdev->cn; + struct hbl_aux_dev *aux_dev; + + aux_dev = &cn->cn_aux_dev; + gaudi2_aux_ops = &gaudi2->cn_aux_ops; + + if (gaudi2_aux_ops->send_port_cpucp_status) + return gaudi2_aux_ops->send_port_cpucp_status(aux_dev, port, cmd, period); + + return -ENODEV; +} + +static struct hl_cn_port_funcs gaudi2_cn_port_funcs = { + .spmu_get_stats_info = gaudi2_cn_spmu_get_stats_info, + .spmu_config = gaudi2_cn_spmu_config, + .spmu_sample = gaudi2_cn_spmu_sample, + .post_send_status = gaudi2_cn_post_send_status, + .ports_stop_prepare = gaudi2_cn_ports_stop_prepare, + .send_port_cpucp_status = gaudi2_cn_send_port_cpucp_status, +}; + +struct hl_cn_funcs gaudi2_cn_funcs = { + .get_hw_cap = gaudi2_cn_get_hw_cap, + .set_hw_cap = gaudi2_cn_set_hw_cap, + .pre_core_init = gaudi2_cn_pre_core_init, + .set_cn_data = gaudi2_cn_set_cn_data, + .port_funcs = &gaudi2_cn_port_funcs, +}; diff --git a/drivers/accel/habanalabs/gaudi2/gaudi2_cn.h b/drivers/accel/habanalabs/gaudi2/gaudi2_cn.h new file mode 100644 index 000000000000..dbc8ede23779 --- /dev/null +++ b/drivers/accel/habanalabs/gaudi2/gaudi2_cn.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright 2020-2024 HabanaLabs, Ltd. + * Copyright (C) 2023-2024, Intel Corporation. + * All Rights Reserved. + * + */ + +#ifndef GAUDI2_CN_H_ +#define GAUDI2_CN_H_ + +#include "gaudi2P.h" +#include "../include/gaudi2/asic_reg/gaudi2_regs.h" + +#define NIC_MAX_RC_MTU SZ_8K +/* This is the max frame length the H/W supports (Tx/Rx) */ +#define NIC_MAX_RDMA_HDRS 128 +#define NIC_MAX_FRM_LEN (NIC_MAX_RC_MTU + NIC_MAX_RDMA_HDRS) + +#define NIC_CFG_LO_SIZE (mmNIC0_QPC1_REQ_STATIC_CONFIG - \ + mmNIC0_QPC0_REQ_STATIC_CONFIG) + +#define NIC_CFG_HI_SIZE (mmNIC0_RXE1_CONTROL - mmNIC0_RXE0_CONTROL) + +#define NIC_CFG_BASE(port, reg) \ + ((u64) (NIC_MACRO_CFG_BASE(port) + \ + ((reg < mmNIC0_RXE0_CONTROL) ? \ + (NIC_CFG_LO_SIZE * (u64) ((port) & 1)) : \ + (NIC_CFG_HI_SIZE * (u64) ((port) & 1))))) + +#define NIC_RREG32(reg) RREG32(NIC_CFG_BASE(port, (reg)) + (reg)) +#define NIC_WREG32(reg, val) WREG32(NIC_CFG_BASE(port, (reg)) + (reg), (val)) +#define NIC_RMWREG32(reg, val, mask) \ + RMWREG32(NIC_CFG_BASE(port, reg) + (reg), (val), (mask)) + +int gaudi2_cn_set_info(struct hl_device *hdev, bool get_from_fw); +int gaudi2_cn_handle_sw_error_event(struct hl_device *hdev, u16 event_type, u8 macro_index, + struct hl_eq_nic_intr_cause *nic_intr_cause); +int gaudi2_cn_handle_axi_error_response_event(struct hl_device *hdev, u16 event_type, + u8 macro_index, struct hl_eq_nic_intr_cause *nic_intr_cause); + +#endif /* GAUDI2_CN_H_ */ diff --git a/drivers/accel/habanalabs/gaudi2/gaudi2_coresight.c b/drivers/accel/habanalabs/gaudi2/gaudi2_coresight.c index 2423620ff358..f7b3692f6bff 100644 --- a/drivers/accel/habanalabs/gaudi2/gaudi2_coresight.c +++ b/drivers/accel/habanalabs/gaudi2/gaudi2_coresight.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2019-2022 HabanaLabs, Ltd. + * Copyright 2019-2024 HabanaLabs, Ltd. * All Rights Reserved. */ #include "gaudi2_coresight_regs.h" @@ -9,6 +9,8 @@ #define GAUDI2_PLDM_CORESIGHT_TIMEOUT_USEC (CORESIGHT_TIMEOUT_USEC * 2000) #define SPMU_MAX_COUNTERS 6 +/* SPMU should also include overflow_idx and cycle_cnt_idx */ +#define SPMU_DATA_LEN (SPMU_MAX_COUNTERS + 2) #define COMPONENT_ID_INVALID ((u32)(-1)) #define MAX_BMONS_PER_UNIT 8 @@ -59,6 +61,23 @@ struct component_config_offsets { u32 bmon_ids[MAX_BMONS_PER_UNIT]; }; +static struct hbl_cn_stat gaudi2_nic0_spmu_stats[] = { + {"spmu_req_out_of_range_psn", 17}, + {"spmu_req_unset_psn", 18}, + {"spmu_res_duplicate_psn", 20}, + {"spmu_res_out_of_sequence_psn", 21} +}; + +static struct hbl_cn_stat gaudi2_nic1_spmu_stats[] = { + {"spmu_req_out_of_range_psn", 5}, + {"spmu_req_unset_psn", 6}, + {"spmu_res_duplicate_psn", 8}, + {"spmu_res_out_of_sequence_psn", 9} +}; + +static size_t gaudi2_nic0_spmu_stats_len = ARRAY_SIZE(gaudi2_nic0_spmu_stats); +static size_t gaudi2_nic1_spmu_stats_len = ARRAY_SIZE(gaudi2_nic1_spmu_stats); + static u64 debug_stm_regs[GAUDI2_STM_LAST + 1] = { [GAUDI2_STM_DCORE0_TPC0_EML] = mmDCORE0_TPC0_EML_STM_BASE, [GAUDI2_STM_DCORE0_TPC1_EML] = mmDCORE0_TPC1_EML_STM_BASE, @@ -489,7 +508,7 @@ static u64 debug_funnel_regs[GAUDI2_FUNNEL_LAST + 1] = { [GAUDI2_FUNNEL_NIC11_DBG_NCH] = mmNIC11_DBG_FUNNEL_NCH_BASE }; -static u64 debug_bmon_regs[GAUDI2_BMON_LAST + 1] = { +u64 debug_bmon_regs[GAUDI2_BMON_LAST + 1] = { [GAUDI2_BMON_DCORE0_TPC0_EML_0] = mmDCORE0_TPC0_EML_BUSMON_0_BASE, [GAUDI2_BMON_DCORE0_TPC0_EML_1] = mmDCORE0_TPC0_EML_BUSMON_1_BASE, [GAUDI2_BMON_DCORE0_TPC0_EML_2] = mmDCORE0_TPC0_EML_BUSMON_2_BASE, @@ -877,7 +896,7 @@ static u64 debug_bmon_regs[GAUDI2_BMON_LAST + 1] = { [GAUDI2_BMON_NIC11_DBG_2_1] = mmNIC11_DBG_BMON2_1_BASE }; -static u64 debug_spmu_regs[GAUDI2_SPMU_LAST + 1] = { +u64 debug_spmu_regs[GAUDI2_SPMU_LAST + 1] = { [GAUDI2_SPMU_DCORE0_TPC0_EML] = mmDCORE0_TPC0_EML_SPMU_BASE, [GAUDI2_SPMU_DCORE0_TPC1_EML] = mmDCORE0_TPC1_EML_SPMU_BASE, [GAUDI2_SPMU_DCORE0_TPC2_EML] = mmDCORE0_TPC2_EML_SPMU_BASE, @@ -2432,6 +2451,11 @@ static int gaudi2_config_bmon(struct hl_device *hdev, struct hl_debug_params *pa return 0; } +static bool gaudi2_reg_is_nic_spmu(enum gaudi2_debug_spmu_regs_index reg_idx) +{ + return reg_idx >= GAUDI2_SPMU_NIC0_DBG_0 && reg_idx <= GAUDI2_SPMU_NIC11_DBG_1; +} + static int gaudi2_config_spmu(struct hl_device *hdev, struct hl_debug_params *params) { struct hl_debug_params_spmu *input = params->input; @@ -2542,6 +2566,121 @@ static int gaudi2_config_spmu(struct hl_device *hdev, struct hl_debug_params *pa return 0; } +static int gaudi2_sample_spmu(struct hl_device *hdev, struct hl_debug_params *params) +{ + u32 output_arr_len; + u32 events_num; + u64 base_reg; + u64 *output; + int i; + + if (params->reg_idx >= ARRAY_SIZE(debug_spmu_regs)) { + dev_err(hdev->dev, "Invalid register index in SPMU\n"); + return -EINVAL; + } + + base_reg = debug_spmu_regs[params->reg_idx]; + + /* in case base reg is 0x0 we ignore this configuration */ + if (!base_reg) + return 0; + + output = params->output; + output_arr_len = params->output_size / sizeof(u64); + events_num = output_arr_len; + + if (output_arr_len < 1) { + dev_err(hdev->dev, "not enough values for SPMU sample\n"); + return -EINVAL; + } + + if (events_num > SPMU_MAX_COUNTERS) { + dev_err(hdev->dev, "too many events values for SPMU sample\n"); + return -EINVAL; + } + + /* capture */ + WREG32(base_reg + mmSPMU_PMSCR_OFFSET, 1); + + /* read the shadow registers */ + for (i = 0 ; i < events_num ; i++) + output[i] = RREG32(base_reg + mmSPMU_PMEVCNTSR0_OFFSET + i * 4); + + return 0; +} + +void gaudi2_cn_spmu_get_stats_info(struct hl_device *hdev, u32 port, struct hbl_cn_stat **stats, + u32 *n_stats) +{ + if (!hdev->supports_coresight) { + *n_stats = 0; + return; + } + + if (port & 1) { + *n_stats = gaudi2_nic1_spmu_stats_len; + *stats = gaudi2_nic1_spmu_stats; + } else { + *n_stats = gaudi2_nic0_spmu_stats_len; + *stats = gaudi2_nic0_spmu_stats; + } +} + +int gaudi2_cn_spmu_config(struct hl_device *hdev, u32 port, u32 num_event_types, u32 event_types[], + bool enable) +{ + struct hl_debug_params_spmu spmu; + struct hl_debug_params params; + u64 event_counters[SPMU_DATA_LEN]; + int i; + + if (!hdev->supports_coresight) + return 0; + + /* validate nic port */ + if (!gaudi2_reg_is_nic_spmu(GAUDI2_SPMU_NIC0_DBG_0 + port)) { + dev_err(hdev->dev, "Invalid nic port %u\n", port); + return -EINVAL; + } + + memset(¶ms, 0, sizeof(struct hl_debug_params)); + params.op = HL_DEBUG_OP_SPMU; + params.input = &spmu; + params.enable = enable; + params.output_size = sizeof(event_counters); + params.output = event_counters; + params.reg_idx = GAUDI2_SPMU_NIC0_DBG_0 + port; + + memset(&spmu, 0, sizeof(struct hl_debug_params_spmu)); + spmu.event_types_num = num_event_types; + + for (i = 0 ; i < spmu.event_types_num ; i++) + spmu.event_types[i] = event_types[i]; + + return gaudi2_config_spmu(hdev, ¶ms); +} + +int gaudi2_cn_spmu_sample(struct hl_device *hdev, u32 port, u32 num_out_data, u64 out_data[]) +{ + struct hl_debug_params params; + + if (!hdev->supports_coresight) + return 0; + + /* validate nic port */ + if (!gaudi2_reg_is_nic_spmu(GAUDI2_SPMU_NIC0_DBG_0 + port)) { + dev_err(hdev->dev, "Invalid nic port %u\n", port); + return -EINVAL; + } + + memset(¶ms, 0, sizeof(struct hl_debug_params)); + params.output = out_data; + params.output_size = num_out_data * sizeof(u64); + params.reg_idx = GAUDI2_SPMU_NIC0_DBG_0 + port; + + return gaudi2_sample_spmu(hdev, ¶ms); +} + int gaudi2_debug_coresight(struct hl_device *hdev, struct hl_ctx *ctx, void *data) { struct hl_debug_params *params = data; -- 2.34.1