162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci * Copyright 2016-2022 HabanaLabs, Ltd. 562306a36Sopenharmony_ci * All Rights Reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "habanalabs.h" 962306a36Sopenharmony_ci#include "../include/common/hl_boot_if.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/firmware.h> 1262306a36Sopenharmony_ci#include <linux/crc32.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/ctype.h> 1562306a36Sopenharmony_ci#include <linux/vmalloc.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <trace/events/habanalabs.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define FW_FILE_MAX_SIZE 0x1400000 /* maximum size of 20MB */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic char *comms_cmd_str_arr[COMMS_INVLD_LAST] = { 2262306a36Sopenharmony_ci [COMMS_NOOP] = __stringify(COMMS_NOOP), 2362306a36Sopenharmony_ci [COMMS_CLR_STS] = __stringify(COMMS_CLR_STS), 2462306a36Sopenharmony_ci [COMMS_RST_STATE] = __stringify(COMMS_RST_STATE), 2562306a36Sopenharmony_ci [COMMS_PREP_DESC] = __stringify(COMMS_PREP_DESC), 2662306a36Sopenharmony_ci [COMMS_DATA_RDY] = __stringify(COMMS_DATA_RDY), 2762306a36Sopenharmony_ci [COMMS_EXEC] = __stringify(COMMS_EXEC), 2862306a36Sopenharmony_ci [COMMS_RST_DEV] = __stringify(COMMS_RST_DEV), 2962306a36Sopenharmony_ci [COMMS_GOTO_WFE] = __stringify(COMMS_GOTO_WFE), 3062306a36Sopenharmony_ci [COMMS_SKIP_BMC] = __stringify(COMMS_SKIP_BMC), 3162306a36Sopenharmony_ci [COMMS_PREP_DESC_ELBI] = __stringify(COMMS_PREP_DESC_ELBI), 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic char *comms_sts_str_arr[COMMS_STS_INVLD_LAST] = { 3562306a36Sopenharmony_ci [COMMS_STS_NOOP] = __stringify(COMMS_STS_NOOP), 3662306a36Sopenharmony_ci [COMMS_STS_ACK] = __stringify(COMMS_STS_ACK), 3762306a36Sopenharmony_ci [COMMS_STS_OK] = __stringify(COMMS_STS_OK), 3862306a36Sopenharmony_ci [COMMS_STS_ERR] = __stringify(COMMS_STS_ERR), 3962306a36Sopenharmony_ci [COMMS_STS_VALID_ERR] = __stringify(COMMS_STS_VALID_ERR), 4062306a36Sopenharmony_ci [COMMS_STS_TIMEOUT_ERR] = __stringify(COMMS_STS_TIMEOUT_ERR), 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic char *extract_fw_ver_from_str(const char *fw_str) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci char *str, *fw_ver, *whitespace; 4662306a36Sopenharmony_ci u32 ver_offset; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci fw_ver = kmalloc(VERSION_MAX_LEN, GFP_KERNEL); 4962306a36Sopenharmony_ci if (!fw_ver) 5062306a36Sopenharmony_ci return NULL; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci str = strnstr(fw_str, "fw-", VERSION_MAX_LEN); 5362306a36Sopenharmony_ci if (!str) 5462306a36Sopenharmony_ci goto free_fw_ver; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* Skip the fw- part */ 5762306a36Sopenharmony_ci str += 3; 5862306a36Sopenharmony_ci ver_offset = str - fw_str; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* Copy until the next whitespace */ 6162306a36Sopenharmony_ci whitespace = strnstr(str, " ", VERSION_MAX_LEN - ver_offset); 6262306a36Sopenharmony_ci if (!whitespace) 6362306a36Sopenharmony_ci goto free_fw_ver; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci strscpy(fw_ver, str, whitespace - str + 1); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return fw_ver; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cifree_fw_ver: 7062306a36Sopenharmony_ci kfree(fw_ver); 7162306a36Sopenharmony_ci return NULL; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/** 7562306a36Sopenharmony_ci * extract_u32_until_given_char() - given a string of the format "<u32><char>*", extract the u32. 7662306a36Sopenharmony_ci * @str: the given string 7762306a36Sopenharmony_ci * @ver_num: the pointer to the extracted u32 to be returned to the caller. 7862306a36Sopenharmony_ci * @given_char: the given char at the end of the u32 in the string 7962306a36Sopenharmony_ci * 8062306a36Sopenharmony_ci * Return: Upon success, return a pointer to the given_char in the string. Upon failure, return NULL 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_cistatic char *extract_u32_until_given_char(char *str, u32 *ver_num, char given_char) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci char num_str[8] = {}, *ch; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci ch = strchrnul(str, given_char); 8762306a36Sopenharmony_ci if (*ch == '\0' || ch == str || ch - str >= sizeof(num_str)) 8862306a36Sopenharmony_ci return NULL; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci memcpy(num_str, str, ch - str); 9162306a36Sopenharmony_ci if (kstrtou32(num_str, 10, ver_num)) 9262306a36Sopenharmony_ci return NULL; 9362306a36Sopenharmony_ci return ch; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/** 9762306a36Sopenharmony_ci * hl_get_sw_major_minor_subminor() - extract the FW's SW version major, minor, sub-minor 9862306a36Sopenharmony_ci * from the version string 9962306a36Sopenharmony_ci * @hdev: pointer to the hl_device 10062306a36Sopenharmony_ci * @fw_str: the FW's version string 10162306a36Sopenharmony_ci * 10262306a36Sopenharmony_ci * The extracted version is set in the hdev fields: fw_sw_{major/minor/sub_minor}_ver. 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * fw_str is expected to have one of two possible formats, examples: 10562306a36Sopenharmony_ci * 1) 'Preboot version hl-gaudi2-1.9.0-fw-42.0.1-sec-3' 10662306a36Sopenharmony_ci * 2) 'Preboot version hl-gaudi2-1.9.0-rc-fw-42.0.1-sec-3' 10762306a36Sopenharmony_ci * In those examples, the SW major,minor,subminor are correspondingly: 1,9,0. 10862306a36Sopenharmony_ci * 10962306a36Sopenharmony_ci * Return: 0 for success or a negative error code for failure. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_cistatic int hl_get_sw_major_minor_subminor(struct hl_device *hdev, const char *fw_str) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci char *end, *start; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci end = strnstr(fw_str, "-rc-", VERSION_MAX_LEN); 11662306a36Sopenharmony_ci if (end == fw_str) 11762306a36Sopenharmony_ci return -EINVAL; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (!end) 12062306a36Sopenharmony_ci end = strnstr(fw_str, "-fw-", VERSION_MAX_LEN); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (end == fw_str) 12362306a36Sopenharmony_ci return -EINVAL; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (!end) 12662306a36Sopenharmony_ci return -EINVAL; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci for (start = end - 1; start != fw_str; start--) { 12962306a36Sopenharmony_ci if (*start == '-') 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (start == fw_str) 13462306a36Sopenharmony_ci return -EINVAL; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* start/end point each to the starting and ending hyphen of the sw version e.g. -1.9.0- */ 13762306a36Sopenharmony_ci start++; 13862306a36Sopenharmony_ci start = extract_u32_until_given_char(start, &hdev->fw_sw_major_ver, '.'); 13962306a36Sopenharmony_ci if (!start) 14062306a36Sopenharmony_ci goto err_zero_ver; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci start++; 14362306a36Sopenharmony_ci start = extract_u32_until_given_char(start, &hdev->fw_sw_minor_ver, '.'); 14462306a36Sopenharmony_ci if (!start) 14562306a36Sopenharmony_ci goto err_zero_ver; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci start++; 14862306a36Sopenharmony_ci start = extract_u32_until_given_char(start, &hdev->fw_sw_sub_minor_ver, '-'); 14962306a36Sopenharmony_ci if (!start) 15062306a36Sopenharmony_ci goto err_zero_ver; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cierr_zero_ver: 15562306a36Sopenharmony_ci hdev->fw_sw_major_ver = 0; 15662306a36Sopenharmony_ci hdev->fw_sw_minor_ver = 0; 15762306a36Sopenharmony_ci hdev->fw_sw_sub_minor_ver = 0; 15862306a36Sopenharmony_ci return -EINVAL; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/** 16262306a36Sopenharmony_ci * hl_get_preboot_major_minor() - extract the FW's version major, minor from the version string. 16362306a36Sopenharmony_ci * @hdev: pointer to the hl_device 16462306a36Sopenharmony_ci * @preboot_ver: the FW's version string 16562306a36Sopenharmony_ci * 16662306a36Sopenharmony_ci * preboot_ver is expected to be the format of <major>.<minor>.<sub minor>*, e.g: 42.0.1-sec-3 16762306a36Sopenharmony_ci * The extracted version is set in the hdev fields: fw_inner_{major/minor}_ver. 16862306a36Sopenharmony_ci * 16962306a36Sopenharmony_ci * Return: 0 on success, negative error code for failure. 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_cistatic int hl_get_preboot_major_minor(struct hl_device *hdev, char *preboot_ver) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci preboot_ver = extract_u32_until_given_char(preboot_ver, &hdev->fw_inner_major_ver, '.'); 17462306a36Sopenharmony_ci if (!preboot_ver) { 17562306a36Sopenharmony_ci dev_err(hdev->dev, "Error parsing preboot major version\n"); 17662306a36Sopenharmony_ci goto err_zero_ver; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci preboot_ver++; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci preboot_ver = extract_u32_until_given_char(preboot_ver, &hdev->fw_inner_minor_ver, '.'); 18262306a36Sopenharmony_ci if (!preboot_ver) { 18362306a36Sopenharmony_ci dev_err(hdev->dev, "Error parsing preboot minor version\n"); 18462306a36Sopenharmony_ci goto err_zero_ver; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci return 0; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cierr_zero_ver: 18962306a36Sopenharmony_ci hdev->fw_inner_major_ver = 0; 19062306a36Sopenharmony_ci hdev->fw_inner_minor_ver = 0; 19162306a36Sopenharmony_ci return -EINVAL; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int hl_request_fw(struct hl_device *hdev, 19562306a36Sopenharmony_ci const struct firmware **firmware_p, 19662306a36Sopenharmony_ci const char *fw_name) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci size_t fw_size; 19962306a36Sopenharmony_ci int rc; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci rc = request_firmware(firmware_p, fw_name, hdev->dev); 20262306a36Sopenharmony_ci if (rc) { 20362306a36Sopenharmony_ci dev_err(hdev->dev, "Firmware file %s is not found! (error %d)\n", 20462306a36Sopenharmony_ci fw_name, rc); 20562306a36Sopenharmony_ci goto out; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci fw_size = (*firmware_p)->size; 20962306a36Sopenharmony_ci if ((fw_size % 4) != 0) { 21062306a36Sopenharmony_ci dev_err(hdev->dev, "Illegal %s firmware size %zu\n", 21162306a36Sopenharmony_ci fw_name, fw_size); 21262306a36Sopenharmony_ci rc = -EINVAL; 21362306a36Sopenharmony_ci goto release_fw; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci dev_dbg(hdev->dev, "%s firmware size == %zu\n", fw_name, fw_size); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (fw_size > FW_FILE_MAX_SIZE) { 21962306a36Sopenharmony_ci dev_err(hdev->dev, 22062306a36Sopenharmony_ci "FW file size %zu exceeds maximum of %u bytes\n", 22162306a36Sopenharmony_ci fw_size, FW_FILE_MAX_SIZE); 22262306a36Sopenharmony_ci rc = -EINVAL; 22362306a36Sopenharmony_ci goto release_fw; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cirelease_fw: 22962306a36Sopenharmony_ci release_firmware(*firmware_p); 23062306a36Sopenharmony_ciout: 23162306a36Sopenharmony_ci return rc; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/** 23562306a36Sopenharmony_ci * hl_release_firmware() - release FW 23662306a36Sopenharmony_ci * 23762306a36Sopenharmony_ci * @fw: fw descriptor 23862306a36Sopenharmony_ci * 23962306a36Sopenharmony_ci * note: this inline function added to serve as a comprehensive mirror for the 24062306a36Sopenharmony_ci * hl_request_fw function. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_cistatic inline void hl_release_firmware(const struct firmware *fw) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci release_firmware(fw); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/** 24862306a36Sopenharmony_ci * hl_fw_copy_fw_to_device() - copy FW to device 24962306a36Sopenharmony_ci * 25062306a36Sopenharmony_ci * @hdev: pointer to hl_device structure. 25162306a36Sopenharmony_ci * @fw: fw descriptor 25262306a36Sopenharmony_ci * @dst: IO memory mapped address space to copy firmware to 25362306a36Sopenharmony_ci * @src_offset: offset in src FW to copy from 25462306a36Sopenharmony_ci * @size: amount of bytes to copy (0 to copy the whole binary) 25562306a36Sopenharmony_ci * 25662306a36Sopenharmony_ci * actual copy of FW binary data to device, shared by static and dynamic loaders 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_cistatic int hl_fw_copy_fw_to_device(struct hl_device *hdev, 25962306a36Sopenharmony_ci const struct firmware *fw, void __iomem *dst, 26062306a36Sopenharmony_ci u32 src_offset, u32 size) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci const void *fw_data; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* size 0 indicates to copy the whole file */ 26562306a36Sopenharmony_ci if (!size) 26662306a36Sopenharmony_ci size = fw->size; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (src_offset + size > fw->size) { 26962306a36Sopenharmony_ci dev_err(hdev->dev, 27062306a36Sopenharmony_ci "size to copy(%u) and offset(%u) are invalid\n", 27162306a36Sopenharmony_ci size, src_offset); 27262306a36Sopenharmony_ci return -EINVAL; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci fw_data = (const void *) fw->data; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci memcpy_toio(dst, fw_data + src_offset, size); 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/** 28262306a36Sopenharmony_ci * hl_fw_copy_msg_to_device() - copy message to device 28362306a36Sopenharmony_ci * 28462306a36Sopenharmony_ci * @hdev: pointer to hl_device structure. 28562306a36Sopenharmony_ci * @msg: message 28662306a36Sopenharmony_ci * @dst: IO memory mapped address space to copy firmware to 28762306a36Sopenharmony_ci * @src_offset: offset in src message to copy from 28862306a36Sopenharmony_ci * @size: amount of bytes to copy (0 to copy the whole binary) 28962306a36Sopenharmony_ci * 29062306a36Sopenharmony_ci * actual copy of message data to device. 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_cistatic int hl_fw_copy_msg_to_device(struct hl_device *hdev, 29362306a36Sopenharmony_ci struct lkd_msg_comms *msg, void __iomem *dst, 29462306a36Sopenharmony_ci u32 src_offset, u32 size) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci void *msg_data; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* size 0 indicates to copy the whole file */ 29962306a36Sopenharmony_ci if (!size) 30062306a36Sopenharmony_ci size = sizeof(struct lkd_msg_comms); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (src_offset + size > sizeof(struct lkd_msg_comms)) { 30362306a36Sopenharmony_ci dev_err(hdev->dev, 30462306a36Sopenharmony_ci "size to copy(%u) and offset(%u) are invalid\n", 30562306a36Sopenharmony_ci size, src_offset); 30662306a36Sopenharmony_ci return -EINVAL; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci msg_data = (void *) msg; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci memcpy_toio(dst, msg_data + src_offset, size); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci/** 31762306a36Sopenharmony_ci * hl_fw_load_fw_to_device() - Load F/W code to device's memory. 31862306a36Sopenharmony_ci * 31962306a36Sopenharmony_ci * @hdev: pointer to hl_device structure. 32062306a36Sopenharmony_ci * @fw_name: the firmware image name 32162306a36Sopenharmony_ci * @dst: IO memory mapped address space to copy firmware to 32262306a36Sopenharmony_ci * @src_offset: offset in src FW to copy from 32362306a36Sopenharmony_ci * @size: amount of bytes to copy (0 to copy the whole binary) 32462306a36Sopenharmony_ci * 32562306a36Sopenharmony_ci * Copy fw code from firmware file to device memory. 32662306a36Sopenharmony_ci * 32762306a36Sopenharmony_ci * Return: 0 on success, non-zero for failure. 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_ciint hl_fw_load_fw_to_device(struct hl_device *hdev, const char *fw_name, 33062306a36Sopenharmony_ci void __iomem *dst, u32 src_offset, u32 size) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci const struct firmware *fw; 33362306a36Sopenharmony_ci int rc; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci rc = hl_request_fw(hdev, &fw, fw_name); 33662306a36Sopenharmony_ci if (rc) 33762306a36Sopenharmony_ci return rc; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci rc = hl_fw_copy_fw_to_device(hdev, fw, dst, src_offset, size); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci hl_release_firmware(fw); 34262306a36Sopenharmony_ci return rc; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ciint hl_fw_send_pci_access_msg(struct hl_device *hdev, u32 opcode, u64 value) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct cpucp_packet pkt = {}; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(opcode << CPUCP_PKT_CTL_OPCODE_SHIFT); 35062306a36Sopenharmony_ci pkt.value = cpu_to_le64(value); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ciint hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, 35662306a36Sopenharmony_ci u16 len, u32 timeout, u64 *result) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct hl_hw_queue *queue = &hdev->kernel_queues[hw_queue_id]; 35962306a36Sopenharmony_ci struct asic_fixed_properties *prop = &hdev->asic_prop; 36062306a36Sopenharmony_ci struct cpucp_packet *pkt; 36162306a36Sopenharmony_ci dma_addr_t pkt_dma_addr; 36262306a36Sopenharmony_ci struct hl_bd *sent_bd; 36362306a36Sopenharmony_ci u32 tmp, expected_ack_val, pi, opcode; 36462306a36Sopenharmony_ci int rc; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci pkt = hl_cpu_accessible_dma_pool_alloc(hdev, len, &pkt_dma_addr); 36762306a36Sopenharmony_ci if (!pkt) { 36862306a36Sopenharmony_ci dev_err(hdev->dev, 36962306a36Sopenharmony_ci "Failed to allocate DMA memory for packet to CPU\n"); 37062306a36Sopenharmony_ci return -ENOMEM; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci memcpy(pkt, msg, len); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci mutex_lock(&hdev->send_cpu_message_lock); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* CPU-CP messages can be sent during soft-reset */ 37862306a36Sopenharmony_ci if (hdev->disabled && !hdev->reset_info.in_compute_reset) { 37962306a36Sopenharmony_ci rc = 0; 38062306a36Sopenharmony_ci goto out; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (hdev->device_cpu_disabled) { 38462306a36Sopenharmony_ci rc = -EIO; 38562306a36Sopenharmony_ci goto out; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* set fence to a non valid value */ 38962306a36Sopenharmony_ci pkt->fence = cpu_to_le32(UINT_MAX); 39062306a36Sopenharmony_ci pi = queue->pi; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* 39362306a36Sopenharmony_ci * The CPU queue is a synchronous queue with an effective depth of 39462306a36Sopenharmony_ci * a single entry (although it is allocated with room for multiple 39562306a36Sopenharmony_ci * entries). We lock on it using 'send_cpu_message_lock' which 39662306a36Sopenharmony_ci * serializes accesses to the CPU queue. 39762306a36Sopenharmony_ci * Which means that we don't need to lock the access to the entire H/W 39862306a36Sopenharmony_ci * queues module when submitting a JOB to the CPU queue. 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_ci hl_hw_queue_submit_bd(hdev, queue, hl_queue_inc_ptr(queue->pi), len, pkt_dma_addr); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (prop->fw_app_cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_PKT_PI_ACK_EN) 40362306a36Sopenharmony_ci expected_ack_val = queue->pi; 40462306a36Sopenharmony_ci else 40562306a36Sopenharmony_ci expected_ack_val = CPUCP_PACKET_FENCE_VAL; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci rc = hl_poll_timeout_memory(hdev, &pkt->fence, tmp, 40862306a36Sopenharmony_ci (tmp == expected_ack_val), 1000, 40962306a36Sopenharmony_ci timeout, true); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci hl_hw_queue_inc_ci_kernel(hdev, hw_queue_id); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (rc == -ETIMEDOUT) { 41462306a36Sopenharmony_ci /* If FW performed reset just before sending it a packet, we will get a timeout. 41562306a36Sopenharmony_ci * This is expected behavior, hence no need for error message. 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_ci if (!hl_device_operational(hdev, NULL) && !hdev->reset_info.in_compute_reset) 41862306a36Sopenharmony_ci dev_dbg(hdev->dev, "Device CPU packet timeout (0x%x) due to FW reset\n", 41962306a36Sopenharmony_ci tmp); 42062306a36Sopenharmony_ci else 42162306a36Sopenharmony_ci dev_err(hdev->dev, "Device CPU packet timeout (status = 0x%x)\n", tmp); 42262306a36Sopenharmony_ci hdev->device_cpu_disabled = true; 42362306a36Sopenharmony_ci goto out; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci tmp = le32_to_cpu(pkt->ctl); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci rc = (tmp & CPUCP_PKT_CTL_RC_MASK) >> CPUCP_PKT_CTL_RC_SHIFT; 42962306a36Sopenharmony_ci if (rc) { 43062306a36Sopenharmony_ci opcode = (tmp & CPUCP_PKT_CTL_OPCODE_MASK) >> CPUCP_PKT_CTL_OPCODE_SHIFT; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (!prop->supports_advanced_cpucp_rc) { 43362306a36Sopenharmony_ci dev_dbg(hdev->dev, "F/W ERROR %d for CPU packet %d\n", rc, opcode); 43462306a36Sopenharmony_ci rc = -EIO; 43562306a36Sopenharmony_ci goto scrub_descriptor; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci switch (rc) { 43962306a36Sopenharmony_ci case cpucp_packet_invalid: 44062306a36Sopenharmony_ci dev_err(hdev->dev, 44162306a36Sopenharmony_ci "CPU packet %d is not supported by F/W\n", opcode); 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci case cpucp_packet_fault: 44462306a36Sopenharmony_ci dev_err(hdev->dev, 44562306a36Sopenharmony_ci "F/W failed processing CPU packet %d\n", opcode); 44662306a36Sopenharmony_ci break; 44762306a36Sopenharmony_ci case cpucp_packet_invalid_pkt: 44862306a36Sopenharmony_ci dev_dbg(hdev->dev, 44962306a36Sopenharmony_ci "CPU packet %d is not supported by F/W\n", opcode); 45062306a36Sopenharmony_ci break; 45162306a36Sopenharmony_ci case cpucp_packet_invalid_params: 45262306a36Sopenharmony_ci dev_err(hdev->dev, 45362306a36Sopenharmony_ci "F/W reports invalid parameters for CPU packet %d\n", opcode); 45462306a36Sopenharmony_ci break; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci default: 45762306a36Sopenharmony_ci dev_err(hdev->dev, 45862306a36Sopenharmony_ci "Unknown F/W ERROR %d for CPU packet %d\n", rc, opcode); 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* propagate the return code from the f/w to the callers who want to check it */ 46262306a36Sopenharmony_ci if (result) 46362306a36Sopenharmony_ci *result = rc; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci rc = -EIO; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci } else if (result) { 46862306a36Sopenharmony_ci *result = le64_to_cpu(pkt->result); 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ciscrub_descriptor: 47262306a36Sopenharmony_ci /* Scrub previous buffer descriptor 'ctl' field which contains the 47362306a36Sopenharmony_ci * previous PI value written during packet submission. 47462306a36Sopenharmony_ci * We must do this or else F/W can read an old value upon queue wraparound. 47562306a36Sopenharmony_ci */ 47662306a36Sopenharmony_ci sent_bd = queue->kernel_address; 47762306a36Sopenharmony_ci sent_bd += hl_pi_2_offset(pi); 47862306a36Sopenharmony_ci sent_bd->ctl = cpu_to_le32(UINT_MAX); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ciout: 48162306a36Sopenharmony_ci mutex_unlock(&hdev->send_cpu_message_lock); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci hl_cpu_accessible_dma_pool_free(hdev, len, pkt); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return rc; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ciint hl_fw_unmask_irq(struct hl_device *hdev, u16 event_type) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct cpucp_packet pkt; 49162306a36Sopenharmony_ci u64 result; 49262306a36Sopenharmony_ci int rc; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_UNMASK_RAZWI_IRQ << 49762306a36Sopenharmony_ci CPUCP_PKT_CTL_OPCODE_SHIFT); 49862306a36Sopenharmony_ci pkt.value = cpu_to_le64(event_type); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 50162306a36Sopenharmony_ci 0, &result); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (rc) 50462306a36Sopenharmony_ci dev_err(hdev->dev, "failed to unmask RAZWI IRQ %d", event_type); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return rc; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ciint hl_fw_unmask_irq_arr(struct hl_device *hdev, const u32 *irq_arr, 51062306a36Sopenharmony_ci size_t irq_arr_size) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct cpucp_unmask_irq_arr_packet *pkt; 51362306a36Sopenharmony_ci size_t total_pkt_size; 51462306a36Sopenharmony_ci u64 result; 51562306a36Sopenharmony_ci int rc; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci total_pkt_size = sizeof(struct cpucp_unmask_irq_arr_packet) + 51862306a36Sopenharmony_ci irq_arr_size; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* data should be aligned to 8 bytes in order to CPU-CP to copy it */ 52162306a36Sopenharmony_ci total_pkt_size = (total_pkt_size + 0x7) & ~0x7; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* total_pkt_size is casted to u16 later on */ 52462306a36Sopenharmony_ci if (total_pkt_size > USHRT_MAX) { 52562306a36Sopenharmony_ci dev_err(hdev->dev, "too many elements in IRQ array\n"); 52662306a36Sopenharmony_ci return -EINVAL; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci pkt = kzalloc(total_pkt_size, GFP_KERNEL); 53062306a36Sopenharmony_ci if (!pkt) 53162306a36Sopenharmony_ci return -ENOMEM; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci pkt->length = cpu_to_le32(irq_arr_size / sizeof(irq_arr[0])); 53462306a36Sopenharmony_ci memcpy(&pkt->irqs, irq_arr, irq_arr_size); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci pkt->cpucp_pkt.ctl = cpu_to_le32(CPUCP_PACKET_UNMASK_RAZWI_IRQ_ARRAY << 53762306a36Sopenharmony_ci CPUCP_PKT_CTL_OPCODE_SHIFT); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) pkt, 54062306a36Sopenharmony_ci total_pkt_size, 0, &result); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (rc) 54362306a36Sopenharmony_ci dev_err(hdev->dev, "failed to unmask IRQ array\n"); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci kfree(pkt); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci return rc; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ciint hl_fw_test_cpu_queue(struct hl_device *hdev) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct cpucp_packet test_pkt = {}; 55362306a36Sopenharmony_ci u64 result; 55462306a36Sopenharmony_ci int rc; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci test_pkt.ctl = cpu_to_le32(CPUCP_PACKET_TEST << 55762306a36Sopenharmony_ci CPUCP_PKT_CTL_OPCODE_SHIFT); 55862306a36Sopenharmony_ci test_pkt.value = cpu_to_le64(CPUCP_PACKET_FENCE_VAL); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &test_pkt, 56162306a36Sopenharmony_ci sizeof(test_pkt), 0, &result); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (!rc) { 56462306a36Sopenharmony_ci if (result != CPUCP_PACKET_FENCE_VAL) 56562306a36Sopenharmony_ci dev_err(hdev->dev, 56662306a36Sopenharmony_ci "CPU queue test failed (%#08llx)\n", result); 56762306a36Sopenharmony_ci } else { 56862306a36Sopenharmony_ci dev_err(hdev->dev, "CPU queue test failed, error %d\n", rc); 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return rc; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_civoid *hl_fw_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size, 57562306a36Sopenharmony_ci dma_addr_t *dma_handle) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci u64 kernel_addr; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci kernel_addr = gen_pool_alloc(hdev->cpu_accessible_dma_pool, size); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci *dma_handle = hdev->cpu_accessible_dma_address + 58262306a36Sopenharmony_ci (kernel_addr - (u64) (uintptr_t) hdev->cpu_accessible_dma_mem); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci return (void *) (uintptr_t) kernel_addr; 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_civoid hl_fw_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size, 58862306a36Sopenharmony_ci void *vaddr) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci gen_pool_free(hdev->cpu_accessible_dma_pool, (u64) (uintptr_t) vaddr, 59162306a36Sopenharmony_ci size); 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ciint hl_fw_send_soft_reset(struct hl_device *hdev) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci struct cpucp_packet pkt; 59762306a36Sopenharmony_ci int rc; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 60062306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_SOFT_RESET << CPUCP_PKT_CTL_OPCODE_SHIFT); 60162306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL); 60262306a36Sopenharmony_ci if (rc) 60362306a36Sopenharmony_ci dev_err(hdev->dev, "failed to send soft-reset msg (err = %d)\n", rc); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci return rc; 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ciint hl_fw_send_device_activity(struct hl_device *hdev, bool open) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct cpucp_packet pkt; 61162306a36Sopenharmony_ci int rc; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 61462306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_ACTIVE_STATUS_SET << CPUCP_PKT_CTL_OPCODE_SHIFT); 61562306a36Sopenharmony_ci pkt.value = cpu_to_le64(open); 61662306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL); 61762306a36Sopenharmony_ci if (rc) 61862306a36Sopenharmony_ci dev_err(hdev->dev, "failed to send device activity msg(%u)\n", open); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return rc; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ciint hl_fw_send_heartbeat(struct hl_device *hdev) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct cpucp_packet hb_pkt; 62662306a36Sopenharmony_ci u64 result; 62762306a36Sopenharmony_ci int rc; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci memset(&hb_pkt, 0, sizeof(hb_pkt)); 63062306a36Sopenharmony_ci hb_pkt.ctl = cpu_to_le32(CPUCP_PACKET_TEST << 63162306a36Sopenharmony_ci CPUCP_PKT_CTL_OPCODE_SHIFT); 63262306a36Sopenharmony_ci hb_pkt.value = cpu_to_le64(CPUCP_PACKET_FENCE_VAL); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &hb_pkt, 63562306a36Sopenharmony_ci sizeof(hb_pkt), 0, &result); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if ((rc) || (result != CPUCP_PACKET_FENCE_VAL)) 63862306a36Sopenharmony_ci return -EIO; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (le32_to_cpu(hb_pkt.status_mask) & 64162306a36Sopenharmony_ci CPUCP_PKT_HB_STATUS_EQ_FAULT_MASK) { 64262306a36Sopenharmony_ci dev_warn(hdev->dev, "FW reported EQ fault during heartbeat\n"); 64362306a36Sopenharmony_ci rc = -EIO; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci return rc; 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic bool fw_report_boot_dev0(struct hl_device *hdev, u32 err_val, 65062306a36Sopenharmony_ci u32 sts_val) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci bool err_exists = false; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (!(err_val & CPU_BOOT_ERR0_ENABLED)) 65562306a36Sopenharmony_ci return false; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_DRAM_INIT_FAIL) { 65862306a36Sopenharmony_ci dev_err(hdev->dev, 65962306a36Sopenharmony_ci "Device boot error - DRAM initialization failed\n"); 66062306a36Sopenharmony_ci err_exists = true; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_FIT_CORRUPTED) { 66462306a36Sopenharmony_ci dev_err(hdev->dev, "Device boot error - FIT image corrupted\n"); 66562306a36Sopenharmony_ci err_exists = true; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_TS_INIT_FAIL) { 66962306a36Sopenharmony_ci dev_err(hdev->dev, 67062306a36Sopenharmony_ci "Device boot error - Thermal Sensor initialization failed\n"); 67162306a36Sopenharmony_ci err_exists = true; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_BMC_WAIT_SKIPPED) { 67562306a36Sopenharmony_ci if (hdev->bmc_enable) { 67662306a36Sopenharmony_ci dev_err(hdev->dev, 67762306a36Sopenharmony_ci "Device boot error - Skipped waiting for BMC\n"); 67862306a36Sopenharmony_ci err_exists = true; 67962306a36Sopenharmony_ci } else { 68062306a36Sopenharmony_ci dev_info(hdev->dev, 68162306a36Sopenharmony_ci "Device boot message - Skipped waiting for BMC\n"); 68262306a36Sopenharmony_ci /* This is an info so we don't want it to disable the 68362306a36Sopenharmony_ci * device 68462306a36Sopenharmony_ci */ 68562306a36Sopenharmony_ci err_val &= ~CPU_BOOT_ERR0_BMC_WAIT_SKIPPED; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_NIC_DATA_NOT_RDY) { 69062306a36Sopenharmony_ci dev_err(hdev->dev, 69162306a36Sopenharmony_ci "Device boot error - Serdes data from BMC not available\n"); 69262306a36Sopenharmony_ci err_exists = true; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_NIC_FW_FAIL) { 69662306a36Sopenharmony_ci dev_err(hdev->dev, 69762306a36Sopenharmony_ci "Device boot error - NIC F/W initialization failed\n"); 69862306a36Sopenharmony_ci err_exists = true; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_SECURITY_NOT_RDY) { 70262306a36Sopenharmony_ci dev_err(hdev->dev, 70362306a36Sopenharmony_ci "Device boot warning - security not ready\n"); 70462306a36Sopenharmony_ci err_exists = true; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_SECURITY_FAIL) { 70862306a36Sopenharmony_ci dev_err(hdev->dev, "Device boot error - security failure\n"); 70962306a36Sopenharmony_ci err_exists = true; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_EFUSE_FAIL) { 71362306a36Sopenharmony_ci dev_err(hdev->dev, "Device boot error - eFuse failure\n"); 71462306a36Sopenharmony_ci err_exists = true; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_SEC_IMG_VER_FAIL) { 71862306a36Sopenharmony_ci dev_err(hdev->dev, "Device boot error - Failed to load preboot secondary image\n"); 71962306a36Sopenharmony_ci err_exists = true; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_PLL_FAIL) { 72362306a36Sopenharmony_ci dev_err(hdev->dev, "Device boot error - PLL failure\n"); 72462306a36Sopenharmony_ci err_exists = true; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_DEVICE_UNUSABLE_FAIL) { 72862306a36Sopenharmony_ci /* Ignore this bit, don't prevent driver loading */ 72962306a36Sopenharmony_ci dev_dbg(hdev->dev, "device unusable status is set\n"); 73062306a36Sopenharmony_ci err_val &= ~CPU_BOOT_ERR0_DEVICE_UNUSABLE_FAIL; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_BINNING_FAIL) { 73462306a36Sopenharmony_ci dev_err(hdev->dev, "Device boot error - binning failure\n"); 73562306a36Sopenharmony_ci err_exists = true; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (sts_val & CPU_BOOT_DEV_STS0_ENABLED) 73962306a36Sopenharmony_ci dev_dbg(hdev->dev, "Device status0 %#x\n", sts_val); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_EEPROM_FAIL) { 74262306a36Sopenharmony_ci dev_err(hdev->dev, "Device boot error - EEPROM failure detected\n"); 74362306a36Sopenharmony_ci err_exists = true; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* All warnings should go here in order not to reach the unknown error validation */ 74762306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_DRAM_SKIPPED) { 74862306a36Sopenharmony_ci dev_warn(hdev->dev, 74962306a36Sopenharmony_ci "Device boot warning - Skipped DRAM initialization\n"); 75062306a36Sopenharmony_ci /* This is a warning so we don't want it to disable the 75162306a36Sopenharmony_ci * device 75262306a36Sopenharmony_ci */ 75362306a36Sopenharmony_ci err_val &= ~CPU_BOOT_ERR0_DRAM_SKIPPED; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_PRI_IMG_VER_FAIL) { 75762306a36Sopenharmony_ci dev_warn(hdev->dev, 75862306a36Sopenharmony_ci "Device boot warning - Failed to load preboot primary image\n"); 75962306a36Sopenharmony_ci /* This is a warning so we don't want it to disable the 76062306a36Sopenharmony_ci * device as we have a secondary preboot image 76162306a36Sopenharmony_ci */ 76262306a36Sopenharmony_ci err_val &= ~CPU_BOOT_ERR0_PRI_IMG_VER_FAIL; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci if (err_val & CPU_BOOT_ERR0_TPM_FAIL) { 76662306a36Sopenharmony_ci dev_warn(hdev->dev, 76762306a36Sopenharmony_ci "Device boot warning - TPM failure\n"); 76862306a36Sopenharmony_ci /* This is a warning so we don't want it to disable the 76962306a36Sopenharmony_ci * device 77062306a36Sopenharmony_ci */ 77162306a36Sopenharmony_ci err_val &= ~CPU_BOOT_ERR0_TPM_FAIL; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (!err_exists && (err_val & ~CPU_BOOT_ERR0_ENABLED)) { 77562306a36Sopenharmony_ci dev_err(hdev->dev, 77662306a36Sopenharmony_ci "Device boot error - unknown ERR0 error 0x%08x\n", err_val); 77762306a36Sopenharmony_ci err_exists = true; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* return error only if it's in the predefined mask */ 78162306a36Sopenharmony_ci if (err_exists && ((err_val & ~CPU_BOOT_ERR0_ENABLED) & 78262306a36Sopenharmony_ci lower_32_bits(hdev->boot_error_status_mask))) 78362306a36Sopenharmony_ci return true; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci return false; 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci/* placeholder for ERR1 as no errors defined there yet */ 78962306a36Sopenharmony_cistatic bool fw_report_boot_dev1(struct hl_device *hdev, u32 err_val, 79062306a36Sopenharmony_ci u32 sts_val) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci /* 79362306a36Sopenharmony_ci * keep this variable to preserve the logic of the function. 79462306a36Sopenharmony_ci * this way it would require less modifications when error will be 79562306a36Sopenharmony_ci * added to DEV_ERR1 79662306a36Sopenharmony_ci */ 79762306a36Sopenharmony_ci bool err_exists = false; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (!(err_val & CPU_BOOT_ERR1_ENABLED)) 80062306a36Sopenharmony_ci return false; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (sts_val & CPU_BOOT_DEV_STS1_ENABLED) 80362306a36Sopenharmony_ci dev_dbg(hdev->dev, "Device status1 %#x\n", sts_val); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (!err_exists && (err_val & ~CPU_BOOT_ERR1_ENABLED)) { 80662306a36Sopenharmony_ci dev_err(hdev->dev, 80762306a36Sopenharmony_ci "Device boot error - unknown ERR1 error 0x%08x\n", 80862306a36Sopenharmony_ci err_val); 80962306a36Sopenharmony_ci err_exists = true; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci /* return error only if it's in the predefined mask */ 81362306a36Sopenharmony_ci if (err_exists && ((err_val & ~CPU_BOOT_ERR1_ENABLED) & 81462306a36Sopenharmony_ci upper_32_bits(hdev->boot_error_status_mask))) 81562306a36Sopenharmony_ci return true; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci return false; 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic int fw_read_errors(struct hl_device *hdev, u32 boot_err0_reg, 82162306a36Sopenharmony_ci u32 boot_err1_reg, u32 cpu_boot_dev_status0_reg, 82262306a36Sopenharmony_ci u32 cpu_boot_dev_status1_reg) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci u32 err_val, status_val; 82562306a36Sopenharmony_ci bool err_exists = false; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* Some of the firmware status codes are deprecated in newer f/w 82862306a36Sopenharmony_ci * versions. In those versions, the errors are reported 82962306a36Sopenharmony_ci * in different registers. Therefore, we need to check those 83062306a36Sopenharmony_ci * registers and print the exact errors. Moreover, there 83162306a36Sopenharmony_ci * may be multiple errors, so we need to report on each error 83262306a36Sopenharmony_ci * separately. Some of the error codes might indicate a state 83362306a36Sopenharmony_ci * that is not an error per-se, but it is an error in production 83462306a36Sopenharmony_ci * environment 83562306a36Sopenharmony_ci */ 83662306a36Sopenharmony_ci err_val = RREG32(boot_err0_reg); 83762306a36Sopenharmony_ci status_val = RREG32(cpu_boot_dev_status0_reg); 83862306a36Sopenharmony_ci err_exists = fw_report_boot_dev0(hdev, err_val, status_val); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci err_val = RREG32(boot_err1_reg); 84162306a36Sopenharmony_ci status_val = RREG32(cpu_boot_dev_status1_reg); 84262306a36Sopenharmony_ci err_exists |= fw_report_boot_dev1(hdev, err_val, status_val); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci if (err_exists) 84562306a36Sopenharmony_ci return -EIO; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return 0; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ciint hl_fw_cpucp_info_get(struct hl_device *hdev, 85162306a36Sopenharmony_ci u32 sts_boot_dev_sts0_reg, 85262306a36Sopenharmony_ci u32 sts_boot_dev_sts1_reg, u32 boot_err0_reg, 85362306a36Sopenharmony_ci u32 boot_err1_reg) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci struct asic_fixed_properties *prop = &hdev->asic_prop; 85662306a36Sopenharmony_ci struct cpucp_packet pkt = {}; 85762306a36Sopenharmony_ci dma_addr_t cpucp_info_dma_addr; 85862306a36Sopenharmony_ci void *cpucp_info_cpu_addr; 85962306a36Sopenharmony_ci char *kernel_ver; 86062306a36Sopenharmony_ci u64 result; 86162306a36Sopenharmony_ci int rc; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci cpucp_info_cpu_addr = hl_cpu_accessible_dma_pool_alloc(hdev, sizeof(struct cpucp_info), 86462306a36Sopenharmony_ci &cpucp_info_dma_addr); 86562306a36Sopenharmony_ci if (!cpucp_info_cpu_addr) { 86662306a36Sopenharmony_ci dev_err(hdev->dev, 86762306a36Sopenharmony_ci "Failed to allocate DMA memory for CPU-CP info packet\n"); 86862306a36Sopenharmony_ci return -ENOMEM; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci memset(cpucp_info_cpu_addr, 0, sizeof(struct cpucp_info)); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_INFO_GET << 87462306a36Sopenharmony_ci CPUCP_PKT_CTL_OPCODE_SHIFT); 87562306a36Sopenharmony_ci pkt.addr = cpu_to_le64(cpucp_info_dma_addr); 87662306a36Sopenharmony_ci pkt.data_max_size = cpu_to_le32(sizeof(struct cpucp_info)); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 87962306a36Sopenharmony_ci HL_CPUCP_INFO_TIMEOUT_USEC, &result); 88062306a36Sopenharmony_ci if (rc) { 88162306a36Sopenharmony_ci dev_err(hdev->dev, 88262306a36Sopenharmony_ci "Failed to handle CPU-CP info pkt, error %d\n", rc); 88362306a36Sopenharmony_ci goto out; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci rc = fw_read_errors(hdev, boot_err0_reg, boot_err1_reg, 88762306a36Sopenharmony_ci sts_boot_dev_sts0_reg, sts_boot_dev_sts1_reg); 88862306a36Sopenharmony_ci if (rc) { 88962306a36Sopenharmony_ci dev_err(hdev->dev, "Errors in device boot\n"); 89062306a36Sopenharmony_ci goto out; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci memcpy(&prop->cpucp_info, cpucp_info_cpu_addr, 89462306a36Sopenharmony_ci sizeof(prop->cpucp_info)); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci rc = hl_build_hwmon_channel_info(hdev, prop->cpucp_info.sensors); 89762306a36Sopenharmony_ci if (rc) { 89862306a36Sopenharmony_ci dev_err(hdev->dev, 89962306a36Sopenharmony_ci "Failed to build hwmon channel info, error %d\n", rc); 90062306a36Sopenharmony_ci rc = -EFAULT; 90162306a36Sopenharmony_ci goto out; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci kernel_ver = extract_fw_ver_from_str(prop->cpucp_info.kernel_version); 90562306a36Sopenharmony_ci if (kernel_ver) { 90662306a36Sopenharmony_ci dev_info(hdev->dev, "Linux version %s", kernel_ver); 90762306a36Sopenharmony_ci kfree(kernel_ver); 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci /* assume EQ code doesn't need to check eqe index */ 91162306a36Sopenharmony_ci hdev->event_queue.check_eqe_index = false; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci /* Read FW application security bits again */ 91462306a36Sopenharmony_ci if (prop->fw_cpu_boot_dev_sts0_valid) { 91562306a36Sopenharmony_ci prop->fw_app_cpu_boot_dev_sts0 = RREG32(sts_boot_dev_sts0_reg); 91662306a36Sopenharmony_ci if (prop->fw_app_cpu_boot_dev_sts0 & 91762306a36Sopenharmony_ci CPU_BOOT_DEV_STS0_EQ_INDEX_EN) 91862306a36Sopenharmony_ci hdev->event_queue.check_eqe_index = true; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (prop->fw_cpu_boot_dev_sts1_valid) 92262306a36Sopenharmony_ci prop->fw_app_cpu_boot_dev_sts1 = RREG32(sts_boot_dev_sts1_reg); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ciout: 92562306a36Sopenharmony_ci hl_cpu_accessible_dma_pool_free(hdev, sizeof(struct cpucp_info), cpucp_info_cpu_addr); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci return rc; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic int hl_fw_send_msi_info_msg(struct hl_device *hdev) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci struct cpucp_array_data_packet *pkt; 93362306a36Sopenharmony_ci size_t total_pkt_size, data_size; 93462306a36Sopenharmony_ci u64 result; 93562306a36Sopenharmony_ci int rc; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci /* skip sending this info for unsupported ASICs */ 93862306a36Sopenharmony_ci if (!hdev->asic_funcs->get_msi_info) 93962306a36Sopenharmony_ci return 0; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci data_size = CPUCP_NUM_OF_MSI_TYPES * sizeof(u32); 94262306a36Sopenharmony_ci total_pkt_size = sizeof(struct cpucp_array_data_packet) + data_size; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci /* data should be aligned to 8 bytes in order to CPU-CP to copy it */ 94562306a36Sopenharmony_ci total_pkt_size = (total_pkt_size + 0x7) & ~0x7; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci /* total_pkt_size is casted to u16 later on */ 94862306a36Sopenharmony_ci if (total_pkt_size > USHRT_MAX) { 94962306a36Sopenharmony_ci dev_err(hdev->dev, "CPUCP array data is too big\n"); 95062306a36Sopenharmony_ci return -EINVAL; 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci pkt = kzalloc(total_pkt_size, GFP_KERNEL); 95462306a36Sopenharmony_ci if (!pkt) 95562306a36Sopenharmony_ci return -ENOMEM; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci pkt->length = cpu_to_le32(CPUCP_NUM_OF_MSI_TYPES); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci memset((void *) &pkt->data, 0xFF, data_size); 96062306a36Sopenharmony_ci hdev->asic_funcs->get_msi_info(pkt->data); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci pkt->cpucp_pkt.ctl = cpu_to_le32(CPUCP_PACKET_MSI_INFO_SET << 96362306a36Sopenharmony_ci CPUCP_PKT_CTL_OPCODE_SHIFT); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *)pkt, 96662306a36Sopenharmony_ci total_pkt_size, 0, &result); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci /* 96962306a36Sopenharmony_ci * in case packet result is invalid it means that FW does not support 97062306a36Sopenharmony_ci * this feature and will use default/hard coded MSI values. no reason 97162306a36Sopenharmony_ci * to stop the boot 97262306a36Sopenharmony_ci */ 97362306a36Sopenharmony_ci if (rc && result == cpucp_packet_invalid) 97462306a36Sopenharmony_ci rc = 0; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci if (rc) 97762306a36Sopenharmony_ci dev_err(hdev->dev, "failed to send CPUCP array data\n"); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci kfree(pkt); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci return rc; 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ciint hl_fw_cpucp_handshake(struct hl_device *hdev, 98562306a36Sopenharmony_ci u32 sts_boot_dev_sts0_reg, 98662306a36Sopenharmony_ci u32 sts_boot_dev_sts1_reg, u32 boot_err0_reg, 98762306a36Sopenharmony_ci u32 boot_err1_reg) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci int rc; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci rc = hl_fw_cpucp_info_get(hdev, sts_boot_dev_sts0_reg, 99262306a36Sopenharmony_ci sts_boot_dev_sts1_reg, boot_err0_reg, 99362306a36Sopenharmony_ci boot_err1_reg); 99462306a36Sopenharmony_ci if (rc) 99562306a36Sopenharmony_ci return rc; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci return hl_fw_send_msi_info_msg(hdev); 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ciint hl_fw_get_eeprom_data(struct hl_device *hdev, void *data, size_t max_size) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci struct cpucp_packet pkt = {}; 100362306a36Sopenharmony_ci void *eeprom_info_cpu_addr; 100462306a36Sopenharmony_ci dma_addr_t eeprom_info_dma_addr; 100562306a36Sopenharmony_ci u64 result; 100662306a36Sopenharmony_ci int rc; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci eeprom_info_cpu_addr = hl_cpu_accessible_dma_pool_alloc(hdev, max_size, 100962306a36Sopenharmony_ci &eeprom_info_dma_addr); 101062306a36Sopenharmony_ci if (!eeprom_info_cpu_addr) { 101162306a36Sopenharmony_ci dev_err(hdev->dev, 101262306a36Sopenharmony_ci "Failed to allocate DMA memory for CPU-CP EEPROM packet\n"); 101362306a36Sopenharmony_ci return -ENOMEM; 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci memset(eeprom_info_cpu_addr, 0, max_size); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_EEPROM_DATA_GET << 101962306a36Sopenharmony_ci CPUCP_PKT_CTL_OPCODE_SHIFT); 102062306a36Sopenharmony_ci pkt.addr = cpu_to_le64(eeprom_info_dma_addr); 102162306a36Sopenharmony_ci pkt.data_max_size = cpu_to_le32(max_size); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 102462306a36Sopenharmony_ci HL_CPUCP_EEPROM_TIMEOUT_USEC, &result); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci if (rc) { 102762306a36Sopenharmony_ci dev_err(hdev->dev, 102862306a36Sopenharmony_ci "Failed to handle CPU-CP EEPROM packet, error %d\n", 102962306a36Sopenharmony_ci rc); 103062306a36Sopenharmony_ci goto out; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci /* result contains the actual size */ 103462306a36Sopenharmony_ci memcpy(data, eeprom_info_cpu_addr, min((size_t)result, max_size)); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ciout: 103762306a36Sopenharmony_ci hl_cpu_accessible_dma_pool_free(hdev, max_size, eeprom_info_cpu_addr); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci return rc; 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ciint hl_fw_get_monitor_dump(struct hl_device *hdev, void *data) 104362306a36Sopenharmony_ci{ 104462306a36Sopenharmony_ci struct cpucp_monitor_dump *mon_dump_cpu_addr; 104562306a36Sopenharmony_ci dma_addr_t mon_dump_dma_addr; 104662306a36Sopenharmony_ci struct cpucp_packet pkt = {}; 104762306a36Sopenharmony_ci size_t data_size; 104862306a36Sopenharmony_ci __le32 *src_ptr; 104962306a36Sopenharmony_ci u32 *dst_ptr; 105062306a36Sopenharmony_ci u64 result; 105162306a36Sopenharmony_ci int i, rc; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci data_size = sizeof(struct cpucp_monitor_dump); 105462306a36Sopenharmony_ci mon_dump_cpu_addr = hl_cpu_accessible_dma_pool_alloc(hdev, data_size, &mon_dump_dma_addr); 105562306a36Sopenharmony_ci if (!mon_dump_cpu_addr) { 105662306a36Sopenharmony_ci dev_err(hdev->dev, 105762306a36Sopenharmony_ci "Failed to allocate DMA memory for CPU-CP monitor-dump packet\n"); 105862306a36Sopenharmony_ci return -ENOMEM; 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci memset(mon_dump_cpu_addr, 0, data_size); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_MONITOR_DUMP_GET << CPUCP_PKT_CTL_OPCODE_SHIFT); 106462306a36Sopenharmony_ci pkt.addr = cpu_to_le64(mon_dump_dma_addr); 106562306a36Sopenharmony_ci pkt.data_max_size = cpu_to_le32(data_size); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 106862306a36Sopenharmony_ci HL_CPUCP_MON_DUMP_TIMEOUT_USEC, &result); 106962306a36Sopenharmony_ci if (rc) { 107062306a36Sopenharmony_ci dev_err(hdev->dev, "Failed to handle CPU-CP monitor-dump packet, error %d\n", rc); 107162306a36Sopenharmony_ci goto out; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci /* result contains the actual size */ 107562306a36Sopenharmony_ci src_ptr = (__le32 *) mon_dump_cpu_addr; 107662306a36Sopenharmony_ci dst_ptr = data; 107762306a36Sopenharmony_ci for (i = 0; i < (data_size / sizeof(u32)); i++) { 107862306a36Sopenharmony_ci *dst_ptr = le32_to_cpu(*src_ptr); 107962306a36Sopenharmony_ci src_ptr++; 108062306a36Sopenharmony_ci dst_ptr++; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ciout: 108462306a36Sopenharmony_ci hl_cpu_accessible_dma_pool_free(hdev, data_size, mon_dump_cpu_addr); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci return rc; 108762306a36Sopenharmony_ci} 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ciint hl_fw_cpucp_pci_counters_get(struct hl_device *hdev, 109062306a36Sopenharmony_ci struct hl_info_pci_counters *counters) 109162306a36Sopenharmony_ci{ 109262306a36Sopenharmony_ci struct cpucp_packet pkt = {}; 109362306a36Sopenharmony_ci u64 result; 109462306a36Sopenharmony_ci int rc; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_PCIE_THROUGHPUT_GET << 109762306a36Sopenharmony_ci CPUCP_PKT_CTL_OPCODE_SHIFT); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci /* Fetch PCI rx counter */ 110062306a36Sopenharmony_ci pkt.index = cpu_to_le32(cpucp_pcie_throughput_rx); 110162306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 110262306a36Sopenharmony_ci HL_CPUCP_INFO_TIMEOUT_USEC, &result); 110362306a36Sopenharmony_ci if (rc) { 110462306a36Sopenharmony_ci dev_err(hdev->dev, 110562306a36Sopenharmony_ci "Failed to handle CPU-CP PCI info pkt, error %d\n", rc); 110662306a36Sopenharmony_ci return rc; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci counters->rx_throughput = result; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 111162306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_PCIE_THROUGHPUT_GET << 111262306a36Sopenharmony_ci CPUCP_PKT_CTL_OPCODE_SHIFT); 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci /* Fetch PCI tx counter */ 111562306a36Sopenharmony_ci pkt.index = cpu_to_le32(cpucp_pcie_throughput_tx); 111662306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 111762306a36Sopenharmony_ci HL_CPUCP_INFO_TIMEOUT_USEC, &result); 111862306a36Sopenharmony_ci if (rc) { 111962306a36Sopenharmony_ci dev_err(hdev->dev, 112062306a36Sopenharmony_ci "Failed to handle CPU-CP PCI info pkt, error %d\n", rc); 112162306a36Sopenharmony_ci return rc; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci counters->tx_throughput = result; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci /* Fetch PCI replay counter */ 112662306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 112762306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_PCIE_REPLAY_CNT_GET << 112862306a36Sopenharmony_ci CPUCP_PKT_CTL_OPCODE_SHIFT); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 113162306a36Sopenharmony_ci HL_CPUCP_INFO_TIMEOUT_USEC, &result); 113262306a36Sopenharmony_ci if (rc) { 113362306a36Sopenharmony_ci dev_err(hdev->dev, 113462306a36Sopenharmony_ci "Failed to handle CPU-CP PCI info pkt, error %d\n", rc); 113562306a36Sopenharmony_ci return rc; 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci counters->replay_cnt = (u32) result; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci return rc; 114062306a36Sopenharmony_ci} 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ciint hl_fw_cpucp_total_energy_get(struct hl_device *hdev, u64 *total_energy) 114362306a36Sopenharmony_ci{ 114462306a36Sopenharmony_ci struct cpucp_packet pkt = {}; 114562306a36Sopenharmony_ci u64 result; 114662306a36Sopenharmony_ci int rc; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_TOTAL_ENERGY_GET << 114962306a36Sopenharmony_ci CPUCP_PKT_CTL_OPCODE_SHIFT); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 115262306a36Sopenharmony_ci HL_CPUCP_INFO_TIMEOUT_USEC, &result); 115362306a36Sopenharmony_ci if (rc) { 115462306a36Sopenharmony_ci dev_err(hdev->dev, 115562306a36Sopenharmony_ci "Failed to handle CpuCP total energy pkt, error %d\n", 115662306a36Sopenharmony_ci rc); 115762306a36Sopenharmony_ci return rc; 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci *total_energy = result; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci return rc; 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ciint get_used_pll_index(struct hl_device *hdev, u32 input_pll_index, 116662306a36Sopenharmony_ci enum pll_index *pll_index) 116762306a36Sopenharmony_ci{ 116862306a36Sopenharmony_ci struct asic_fixed_properties *prop = &hdev->asic_prop; 116962306a36Sopenharmony_ci u8 pll_byte, pll_bit_off; 117062306a36Sopenharmony_ci bool dynamic_pll; 117162306a36Sopenharmony_ci int fw_pll_idx; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci dynamic_pll = !!(prop->fw_app_cpu_boot_dev_sts0 & 117462306a36Sopenharmony_ci CPU_BOOT_DEV_STS0_DYN_PLL_EN); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci if (!dynamic_pll) { 117762306a36Sopenharmony_ci /* 117862306a36Sopenharmony_ci * in case we are working with legacy FW (each asic has unique 117962306a36Sopenharmony_ci * PLL numbering) use the driver based index as they are 118062306a36Sopenharmony_ci * aligned with fw legacy numbering 118162306a36Sopenharmony_ci */ 118262306a36Sopenharmony_ci *pll_index = input_pll_index; 118362306a36Sopenharmony_ci return 0; 118462306a36Sopenharmony_ci } 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci /* retrieve a FW compatible PLL index based on 118762306a36Sopenharmony_ci * ASIC specific user request 118862306a36Sopenharmony_ci */ 118962306a36Sopenharmony_ci fw_pll_idx = hdev->asic_funcs->map_pll_idx_to_fw_idx(input_pll_index); 119062306a36Sopenharmony_ci if (fw_pll_idx < 0) { 119162306a36Sopenharmony_ci dev_err(hdev->dev, "Invalid PLL index (%u) error %d\n", 119262306a36Sopenharmony_ci input_pll_index, fw_pll_idx); 119362306a36Sopenharmony_ci return -EINVAL; 119462306a36Sopenharmony_ci } 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci /* PLL map is a u8 array */ 119762306a36Sopenharmony_ci pll_byte = prop->cpucp_info.pll_map[fw_pll_idx >> 3]; 119862306a36Sopenharmony_ci pll_bit_off = fw_pll_idx & 0x7; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci if (!(pll_byte & BIT(pll_bit_off))) { 120162306a36Sopenharmony_ci dev_err(hdev->dev, "PLL index %d is not supported\n", 120262306a36Sopenharmony_ci fw_pll_idx); 120362306a36Sopenharmony_ci return -EINVAL; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci *pll_index = fw_pll_idx; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci return 0; 120962306a36Sopenharmony_ci} 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ciint hl_fw_cpucp_pll_info_get(struct hl_device *hdev, u32 pll_index, 121262306a36Sopenharmony_ci u16 *pll_freq_arr) 121362306a36Sopenharmony_ci{ 121462306a36Sopenharmony_ci struct cpucp_packet pkt; 121562306a36Sopenharmony_ci enum pll_index used_pll_idx; 121662306a36Sopenharmony_ci u64 result; 121762306a36Sopenharmony_ci int rc; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci rc = get_used_pll_index(hdev, pll_index, &used_pll_idx); 122062306a36Sopenharmony_ci if (rc) 122162306a36Sopenharmony_ci return rc; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_PLL_INFO_GET << 122662306a36Sopenharmony_ci CPUCP_PKT_CTL_OPCODE_SHIFT); 122762306a36Sopenharmony_ci pkt.pll_type = __cpu_to_le16((u16)used_pll_idx); 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 123062306a36Sopenharmony_ci HL_CPUCP_INFO_TIMEOUT_USEC, &result); 123162306a36Sopenharmony_ci if (rc) { 123262306a36Sopenharmony_ci dev_err(hdev->dev, "Failed to read PLL info, error %d\n", rc); 123362306a36Sopenharmony_ci return rc; 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci pll_freq_arr[0] = FIELD_GET(CPUCP_PKT_RES_PLL_OUT0_MASK, result); 123762306a36Sopenharmony_ci pll_freq_arr[1] = FIELD_GET(CPUCP_PKT_RES_PLL_OUT1_MASK, result); 123862306a36Sopenharmony_ci pll_freq_arr[2] = FIELD_GET(CPUCP_PKT_RES_PLL_OUT2_MASK, result); 123962306a36Sopenharmony_ci pll_freq_arr[3] = FIELD_GET(CPUCP_PKT_RES_PLL_OUT3_MASK, result); 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci return 0; 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ciint hl_fw_cpucp_power_get(struct hl_device *hdev, u64 *power) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct cpucp_packet pkt; 124762306a36Sopenharmony_ci u64 result; 124862306a36Sopenharmony_ci int rc; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_POWER_GET << 125362306a36Sopenharmony_ci CPUCP_PKT_CTL_OPCODE_SHIFT); 125462306a36Sopenharmony_ci pkt.type = cpu_to_le16(CPUCP_POWER_INPUT); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 125762306a36Sopenharmony_ci HL_CPUCP_INFO_TIMEOUT_USEC, &result); 125862306a36Sopenharmony_ci if (rc) { 125962306a36Sopenharmony_ci dev_err(hdev->dev, "Failed to read power, error %d\n", rc); 126062306a36Sopenharmony_ci return rc; 126162306a36Sopenharmony_ci } 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci *power = result; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci return rc; 126662306a36Sopenharmony_ci} 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ciint hl_fw_dram_replaced_row_get(struct hl_device *hdev, 126962306a36Sopenharmony_ci struct cpucp_hbm_row_info *info) 127062306a36Sopenharmony_ci{ 127162306a36Sopenharmony_ci struct cpucp_hbm_row_info *cpucp_repl_rows_info_cpu_addr; 127262306a36Sopenharmony_ci dma_addr_t cpucp_repl_rows_info_dma_addr; 127362306a36Sopenharmony_ci struct cpucp_packet pkt = {}; 127462306a36Sopenharmony_ci u64 result; 127562306a36Sopenharmony_ci int rc; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci cpucp_repl_rows_info_cpu_addr = hl_cpu_accessible_dma_pool_alloc(hdev, 127862306a36Sopenharmony_ci sizeof(struct cpucp_hbm_row_info), 127962306a36Sopenharmony_ci &cpucp_repl_rows_info_dma_addr); 128062306a36Sopenharmony_ci if (!cpucp_repl_rows_info_cpu_addr) { 128162306a36Sopenharmony_ci dev_err(hdev->dev, 128262306a36Sopenharmony_ci "Failed to allocate DMA memory for CPU-CP replaced rows info packet\n"); 128362306a36Sopenharmony_ci return -ENOMEM; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci memset(cpucp_repl_rows_info_cpu_addr, 0, sizeof(struct cpucp_hbm_row_info)); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_HBM_REPLACED_ROWS_INFO_GET << 128962306a36Sopenharmony_ci CPUCP_PKT_CTL_OPCODE_SHIFT); 129062306a36Sopenharmony_ci pkt.addr = cpu_to_le64(cpucp_repl_rows_info_dma_addr); 129162306a36Sopenharmony_ci pkt.data_max_size = cpu_to_le32(sizeof(struct cpucp_hbm_row_info)); 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 129462306a36Sopenharmony_ci HL_CPUCP_INFO_TIMEOUT_USEC, &result); 129562306a36Sopenharmony_ci if (rc) { 129662306a36Sopenharmony_ci dev_err(hdev->dev, 129762306a36Sopenharmony_ci "Failed to handle CPU-CP replaced rows info pkt, error %d\n", rc); 129862306a36Sopenharmony_ci goto out; 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci memcpy(info, cpucp_repl_rows_info_cpu_addr, sizeof(*info)); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ciout: 130462306a36Sopenharmony_ci hl_cpu_accessible_dma_pool_free(hdev, sizeof(struct cpucp_hbm_row_info), 130562306a36Sopenharmony_ci cpucp_repl_rows_info_cpu_addr); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci return rc; 130862306a36Sopenharmony_ci} 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ciint hl_fw_dram_pending_row_get(struct hl_device *hdev, u32 *pend_rows_num) 131162306a36Sopenharmony_ci{ 131262306a36Sopenharmony_ci struct cpucp_packet pkt; 131362306a36Sopenharmony_ci u64 result; 131462306a36Sopenharmony_ci int rc; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_HBM_PENDING_ROWS_STATUS << CPUCP_PKT_CTL_OPCODE_SHIFT); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, &result); 132162306a36Sopenharmony_ci if (rc) { 132262306a36Sopenharmony_ci dev_err(hdev->dev, 132362306a36Sopenharmony_ci "Failed to handle CPU-CP pending rows info pkt, error %d\n", rc); 132462306a36Sopenharmony_ci goto out; 132562306a36Sopenharmony_ci } 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci *pend_rows_num = (u32) result; 132862306a36Sopenharmony_ciout: 132962306a36Sopenharmony_ci return rc; 133062306a36Sopenharmony_ci} 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ciint hl_fw_cpucp_engine_core_asid_set(struct hl_device *hdev, u32 asid) 133362306a36Sopenharmony_ci{ 133462306a36Sopenharmony_ci struct cpucp_packet pkt; 133562306a36Sopenharmony_ci int rc; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_ENGINE_CORE_ASID_SET << CPUCP_PKT_CTL_OPCODE_SHIFT); 134062306a36Sopenharmony_ci pkt.value = cpu_to_le64(asid); 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 134362306a36Sopenharmony_ci HL_CPUCP_INFO_TIMEOUT_USEC, NULL); 134462306a36Sopenharmony_ci if (rc) 134562306a36Sopenharmony_ci dev_err(hdev->dev, 134662306a36Sopenharmony_ci "Failed on ASID configuration request for engine core, error %d\n", 134762306a36Sopenharmony_ci rc); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci return rc; 135062306a36Sopenharmony_ci} 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_civoid hl_fw_ask_hard_reset_without_linux(struct hl_device *hdev) 135362306a36Sopenharmony_ci{ 135462306a36Sopenharmony_ci struct static_fw_load_mgr *static_loader = 135562306a36Sopenharmony_ci &hdev->fw_loader.static_loader; 135662306a36Sopenharmony_ci int rc; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci if (hdev->asic_prop.dynamic_fw_load) { 135962306a36Sopenharmony_ci rc = hl_fw_dynamic_send_protocol_cmd(hdev, &hdev->fw_loader, 136062306a36Sopenharmony_ci COMMS_RST_DEV, 0, false, 136162306a36Sopenharmony_ci hdev->fw_loader.cpu_timeout); 136262306a36Sopenharmony_ci if (rc) 136362306a36Sopenharmony_ci dev_err(hdev->dev, "Failed sending COMMS_RST_DEV\n"); 136462306a36Sopenharmony_ci } else { 136562306a36Sopenharmony_ci WREG32(static_loader->kmd_msg_to_cpu_reg, KMD_MSG_RST_DEV); 136662306a36Sopenharmony_ci } 136762306a36Sopenharmony_ci} 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_civoid hl_fw_ask_halt_machine_without_linux(struct hl_device *hdev) 137062306a36Sopenharmony_ci{ 137162306a36Sopenharmony_ci struct fw_load_mgr *fw_loader = &hdev->fw_loader; 137262306a36Sopenharmony_ci u32 status, cpu_boot_status_reg, cpu_timeout; 137362306a36Sopenharmony_ci struct static_fw_load_mgr *static_loader; 137462306a36Sopenharmony_ci struct pre_fw_load_props *pre_fw_load; 137562306a36Sopenharmony_ci int rc; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci if (hdev->device_cpu_is_halted) 137862306a36Sopenharmony_ci return; 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci /* Stop device CPU to make sure nothing bad happens */ 138162306a36Sopenharmony_ci if (hdev->asic_prop.dynamic_fw_load) { 138262306a36Sopenharmony_ci pre_fw_load = &fw_loader->pre_fw_load; 138362306a36Sopenharmony_ci cpu_timeout = fw_loader->cpu_timeout; 138462306a36Sopenharmony_ci cpu_boot_status_reg = pre_fw_load->cpu_boot_status_reg; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci rc = hl_fw_dynamic_send_protocol_cmd(hdev, &hdev->fw_loader, 138762306a36Sopenharmony_ci COMMS_GOTO_WFE, 0, false, cpu_timeout); 138862306a36Sopenharmony_ci if (rc) { 138962306a36Sopenharmony_ci dev_err(hdev->dev, "Failed sending COMMS_GOTO_WFE\n"); 139062306a36Sopenharmony_ci } else { 139162306a36Sopenharmony_ci rc = hl_poll_timeout( 139262306a36Sopenharmony_ci hdev, 139362306a36Sopenharmony_ci cpu_boot_status_reg, 139462306a36Sopenharmony_ci status, 139562306a36Sopenharmony_ci status == CPU_BOOT_STATUS_IN_WFE, 139662306a36Sopenharmony_ci hdev->fw_poll_interval_usec, 139762306a36Sopenharmony_ci cpu_timeout); 139862306a36Sopenharmony_ci if (rc) 139962306a36Sopenharmony_ci dev_err(hdev->dev, "Current status=%u. Timed-out updating to WFE\n", 140062306a36Sopenharmony_ci status); 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci } else { 140362306a36Sopenharmony_ci static_loader = &hdev->fw_loader.static_loader; 140462306a36Sopenharmony_ci WREG32(static_loader->kmd_msg_to_cpu_reg, KMD_MSG_GOTO_WFE); 140562306a36Sopenharmony_ci msleep(static_loader->cpu_reset_wait_msec); 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci /* Must clear this register in order to prevent preboot 140862306a36Sopenharmony_ci * from reading WFE after reboot 140962306a36Sopenharmony_ci */ 141062306a36Sopenharmony_ci WREG32(static_loader->kmd_msg_to_cpu_reg, KMD_MSG_NA); 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci hdev->device_cpu_is_halted = true; 141462306a36Sopenharmony_ci} 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_cistatic void detect_cpu_boot_status(struct hl_device *hdev, u32 status) 141762306a36Sopenharmony_ci{ 141862306a36Sopenharmony_ci /* Some of the status codes below are deprecated in newer f/w 141962306a36Sopenharmony_ci * versions but we keep them here for backward compatibility 142062306a36Sopenharmony_ci */ 142162306a36Sopenharmony_ci switch (status) { 142262306a36Sopenharmony_ci case CPU_BOOT_STATUS_NA: 142362306a36Sopenharmony_ci dev_err(hdev->dev, 142462306a36Sopenharmony_ci "Device boot progress - BTL/ROM did NOT run\n"); 142562306a36Sopenharmony_ci break; 142662306a36Sopenharmony_ci case CPU_BOOT_STATUS_IN_WFE: 142762306a36Sopenharmony_ci dev_err(hdev->dev, 142862306a36Sopenharmony_ci "Device boot progress - Stuck inside WFE loop\n"); 142962306a36Sopenharmony_ci break; 143062306a36Sopenharmony_ci case CPU_BOOT_STATUS_IN_BTL: 143162306a36Sopenharmony_ci dev_err(hdev->dev, 143262306a36Sopenharmony_ci "Device boot progress - Stuck in BTL\n"); 143362306a36Sopenharmony_ci break; 143462306a36Sopenharmony_ci case CPU_BOOT_STATUS_IN_PREBOOT: 143562306a36Sopenharmony_ci dev_err(hdev->dev, 143662306a36Sopenharmony_ci "Device boot progress - Stuck in Preboot\n"); 143762306a36Sopenharmony_ci break; 143862306a36Sopenharmony_ci case CPU_BOOT_STATUS_IN_SPL: 143962306a36Sopenharmony_ci dev_err(hdev->dev, 144062306a36Sopenharmony_ci "Device boot progress - Stuck in SPL\n"); 144162306a36Sopenharmony_ci break; 144262306a36Sopenharmony_ci case CPU_BOOT_STATUS_IN_UBOOT: 144362306a36Sopenharmony_ci dev_err(hdev->dev, 144462306a36Sopenharmony_ci "Device boot progress - Stuck in u-boot\n"); 144562306a36Sopenharmony_ci break; 144662306a36Sopenharmony_ci case CPU_BOOT_STATUS_DRAM_INIT_FAIL: 144762306a36Sopenharmony_ci dev_err(hdev->dev, 144862306a36Sopenharmony_ci "Device boot progress - DRAM initialization failed\n"); 144962306a36Sopenharmony_ci break; 145062306a36Sopenharmony_ci case CPU_BOOT_STATUS_UBOOT_NOT_READY: 145162306a36Sopenharmony_ci dev_err(hdev->dev, 145262306a36Sopenharmony_ci "Device boot progress - Cannot boot\n"); 145362306a36Sopenharmony_ci break; 145462306a36Sopenharmony_ci case CPU_BOOT_STATUS_TS_INIT_FAIL: 145562306a36Sopenharmony_ci dev_err(hdev->dev, 145662306a36Sopenharmony_ci "Device boot progress - Thermal Sensor initialization failed\n"); 145762306a36Sopenharmony_ci break; 145862306a36Sopenharmony_ci case CPU_BOOT_STATUS_SECURITY_READY: 145962306a36Sopenharmony_ci dev_err(hdev->dev, 146062306a36Sopenharmony_ci "Device boot progress - Stuck in preboot after security initialization\n"); 146162306a36Sopenharmony_ci break; 146262306a36Sopenharmony_ci default: 146362306a36Sopenharmony_ci dev_err(hdev->dev, 146462306a36Sopenharmony_ci "Device boot progress - Invalid or unexpected status code %d\n", status); 146562306a36Sopenharmony_ci break; 146662306a36Sopenharmony_ci } 146762306a36Sopenharmony_ci} 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ciint hl_fw_wait_preboot_ready(struct hl_device *hdev) 147062306a36Sopenharmony_ci{ 147162306a36Sopenharmony_ci struct pre_fw_load_props *pre_fw_load = &hdev->fw_loader.pre_fw_load; 147262306a36Sopenharmony_ci u32 status; 147362306a36Sopenharmony_ci int rc; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci /* Need to check two possible scenarios: 147662306a36Sopenharmony_ci * 147762306a36Sopenharmony_ci * CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT - for newer firmwares where 147862306a36Sopenharmony_ci * the preboot is waiting for the boot fit 147962306a36Sopenharmony_ci * 148062306a36Sopenharmony_ci * All other status values - for older firmwares where the uboot was 148162306a36Sopenharmony_ci * loaded from the FLASH 148262306a36Sopenharmony_ci */ 148362306a36Sopenharmony_ci rc = hl_poll_timeout( 148462306a36Sopenharmony_ci hdev, 148562306a36Sopenharmony_ci pre_fw_load->cpu_boot_status_reg, 148662306a36Sopenharmony_ci status, 148762306a36Sopenharmony_ci (status == CPU_BOOT_STATUS_NIC_FW_RDY) || 148862306a36Sopenharmony_ci (status == CPU_BOOT_STATUS_READY_TO_BOOT) || 148962306a36Sopenharmony_ci (status == CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT), 149062306a36Sopenharmony_ci hdev->fw_poll_interval_usec, 149162306a36Sopenharmony_ci pre_fw_load->wait_for_preboot_timeout); 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci if (rc) { 149462306a36Sopenharmony_ci detect_cpu_boot_status(hdev, status); 149562306a36Sopenharmony_ci dev_err(hdev->dev, "CPU boot ready timeout (status = %d)\n", status); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci /* If we read all FF, then something is totally wrong, no point 149862306a36Sopenharmony_ci * of reading specific errors 149962306a36Sopenharmony_ci */ 150062306a36Sopenharmony_ci if (status != -1) 150162306a36Sopenharmony_ci fw_read_errors(hdev, pre_fw_load->boot_err0_reg, 150262306a36Sopenharmony_ci pre_fw_load->boot_err1_reg, 150362306a36Sopenharmony_ci pre_fw_load->sts_boot_dev_sts0_reg, 150462306a36Sopenharmony_ci pre_fw_load->sts_boot_dev_sts1_reg); 150562306a36Sopenharmony_ci return -EIO; 150662306a36Sopenharmony_ci } 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci hdev->fw_loader.fw_comp_loaded |= FW_TYPE_PREBOOT_CPU; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci return 0; 151162306a36Sopenharmony_ci} 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_cistatic int hl_fw_read_preboot_caps(struct hl_device *hdev) 151462306a36Sopenharmony_ci{ 151562306a36Sopenharmony_ci struct pre_fw_load_props *pre_fw_load; 151662306a36Sopenharmony_ci struct asic_fixed_properties *prop; 151762306a36Sopenharmony_ci u32 reg_val; 151862306a36Sopenharmony_ci int rc; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci prop = &hdev->asic_prop; 152162306a36Sopenharmony_ci pre_fw_load = &hdev->fw_loader.pre_fw_load; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci rc = hl_fw_wait_preboot_ready(hdev); 152462306a36Sopenharmony_ci if (rc) 152562306a36Sopenharmony_ci return rc; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci /* 152862306a36Sopenharmony_ci * the registers DEV_STS* contain FW capabilities/features. 152962306a36Sopenharmony_ci * We can rely on this registers only if bit CPU_BOOT_DEV_STS*_ENABLED 153062306a36Sopenharmony_ci * is set. 153162306a36Sopenharmony_ci * In the first read of this register we store the value of this 153262306a36Sopenharmony_ci * register ONLY if the register is enabled (which will be propagated 153362306a36Sopenharmony_ci * to next stages) and also mark the register as valid. 153462306a36Sopenharmony_ci * In case it is not enabled the stored value will be left 0- all 153562306a36Sopenharmony_ci * caps/features are off 153662306a36Sopenharmony_ci */ 153762306a36Sopenharmony_ci reg_val = RREG32(pre_fw_load->sts_boot_dev_sts0_reg); 153862306a36Sopenharmony_ci if (reg_val & CPU_BOOT_DEV_STS0_ENABLED) { 153962306a36Sopenharmony_ci prop->fw_cpu_boot_dev_sts0_valid = true; 154062306a36Sopenharmony_ci prop->fw_preboot_cpu_boot_dev_sts0 = reg_val; 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci reg_val = RREG32(pre_fw_load->sts_boot_dev_sts1_reg); 154462306a36Sopenharmony_ci if (reg_val & CPU_BOOT_DEV_STS1_ENABLED) { 154562306a36Sopenharmony_ci prop->fw_cpu_boot_dev_sts1_valid = true; 154662306a36Sopenharmony_ci prop->fw_preboot_cpu_boot_dev_sts1 = reg_val; 154762306a36Sopenharmony_ci } 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci prop->dynamic_fw_load = !!(prop->fw_preboot_cpu_boot_dev_sts0 & 155062306a36Sopenharmony_ci CPU_BOOT_DEV_STS0_FW_LD_COM_EN); 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci /* initialize FW loader once we know what load protocol is used */ 155362306a36Sopenharmony_ci hdev->asic_funcs->init_firmware_loader(hdev); 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci dev_dbg(hdev->dev, "Attempting %s FW load\n", 155662306a36Sopenharmony_ci prop->dynamic_fw_load ? "dynamic" : "legacy"); 155762306a36Sopenharmony_ci return 0; 155862306a36Sopenharmony_ci} 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_cistatic int hl_fw_static_read_device_fw_version(struct hl_device *hdev, 156162306a36Sopenharmony_ci enum hl_fw_component fwc) 156262306a36Sopenharmony_ci{ 156362306a36Sopenharmony_ci struct asic_fixed_properties *prop = &hdev->asic_prop; 156462306a36Sopenharmony_ci struct fw_load_mgr *fw_loader = &hdev->fw_loader; 156562306a36Sopenharmony_ci struct static_fw_load_mgr *static_loader; 156662306a36Sopenharmony_ci char *dest, *boot_ver, *preboot_ver; 156762306a36Sopenharmony_ci u32 ver_off, limit; 156862306a36Sopenharmony_ci const char *name; 156962306a36Sopenharmony_ci char btl_ver[32]; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci static_loader = &hdev->fw_loader.static_loader; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci switch (fwc) { 157462306a36Sopenharmony_ci case FW_COMP_BOOT_FIT: 157562306a36Sopenharmony_ci ver_off = RREG32(static_loader->boot_fit_version_offset_reg); 157662306a36Sopenharmony_ci dest = prop->uboot_ver; 157762306a36Sopenharmony_ci name = "Boot-fit"; 157862306a36Sopenharmony_ci limit = static_loader->boot_fit_version_max_off; 157962306a36Sopenharmony_ci break; 158062306a36Sopenharmony_ci case FW_COMP_PREBOOT: 158162306a36Sopenharmony_ci ver_off = RREG32(static_loader->preboot_version_offset_reg); 158262306a36Sopenharmony_ci dest = prop->preboot_ver; 158362306a36Sopenharmony_ci name = "Preboot"; 158462306a36Sopenharmony_ci limit = static_loader->preboot_version_max_off; 158562306a36Sopenharmony_ci break; 158662306a36Sopenharmony_ci default: 158762306a36Sopenharmony_ci dev_warn(hdev->dev, "Undefined FW component: %d\n", fwc); 158862306a36Sopenharmony_ci return -EIO; 158962306a36Sopenharmony_ci } 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci ver_off &= static_loader->sram_offset_mask; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci if (ver_off < limit) { 159462306a36Sopenharmony_ci memcpy_fromio(dest, 159562306a36Sopenharmony_ci hdev->pcie_bar[fw_loader->sram_bar_id] + ver_off, 159662306a36Sopenharmony_ci VERSION_MAX_LEN); 159762306a36Sopenharmony_ci } else { 159862306a36Sopenharmony_ci dev_err(hdev->dev, "%s version offset (0x%x) is above SRAM\n", 159962306a36Sopenharmony_ci name, ver_off); 160062306a36Sopenharmony_ci strscpy(dest, "unavailable", VERSION_MAX_LEN); 160162306a36Sopenharmony_ci return -EIO; 160262306a36Sopenharmony_ci } 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci if (fwc == FW_COMP_BOOT_FIT) { 160562306a36Sopenharmony_ci boot_ver = extract_fw_ver_from_str(prop->uboot_ver); 160662306a36Sopenharmony_ci if (boot_ver) { 160762306a36Sopenharmony_ci dev_info(hdev->dev, "boot-fit version %s\n", boot_ver); 160862306a36Sopenharmony_ci kfree(boot_ver); 160962306a36Sopenharmony_ci } 161062306a36Sopenharmony_ci } else if (fwc == FW_COMP_PREBOOT) { 161162306a36Sopenharmony_ci preboot_ver = strnstr(prop->preboot_ver, "Preboot", 161262306a36Sopenharmony_ci VERSION_MAX_LEN); 161362306a36Sopenharmony_ci if (preboot_ver && preboot_ver != prop->preboot_ver) { 161462306a36Sopenharmony_ci strscpy(btl_ver, prop->preboot_ver, 161562306a36Sopenharmony_ci min((int) (preboot_ver - prop->preboot_ver), 161662306a36Sopenharmony_ci 31)); 161762306a36Sopenharmony_ci dev_info(hdev->dev, "%s\n", btl_ver); 161862306a36Sopenharmony_ci } 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci preboot_ver = extract_fw_ver_from_str(prop->preboot_ver); 162162306a36Sopenharmony_ci if (preboot_ver) { 162262306a36Sopenharmony_ci dev_info(hdev->dev, "preboot version %s\n", 162362306a36Sopenharmony_ci preboot_ver); 162462306a36Sopenharmony_ci kfree(preboot_ver); 162562306a36Sopenharmony_ci } 162662306a36Sopenharmony_ci } 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci return 0; 162962306a36Sopenharmony_ci} 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci/** 163262306a36Sopenharmony_ci * hl_fw_preboot_update_state - update internal data structures during 163362306a36Sopenharmony_ci * handshake with preboot 163462306a36Sopenharmony_ci * 163562306a36Sopenharmony_ci * 163662306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 163762306a36Sopenharmony_ci * 163862306a36Sopenharmony_ci * @return 0 on success, otherwise non-zero error code 163962306a36Sopenharmony_ci */ 164062306a36Sopenharmony_cistatic void hl_fw_preboot_update_state(struct hl_device *hdev) 164162306a36Sopenharmony_ci{ 164262306a36Sopenharmony_ci struct asic_fixed_properties *prop = &hdev->asic_prop; 164362306a36Sopenharmony_ci u32 cpu_boot_dev_sts0, cpu_boot_dev_sts1; 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci cpu_boot_dev_sts0 = prop->fw_preboot_cpu_boot_dev_sts0; 164662306a36Sopenharmony_ci cpu_boot_dev_sts1 = prop->fw_preboot_cpu_boot_dev_sts1; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci /* We read boot_dev_sts registers multiple times during boot: 164962306a36Sopenharmony_ci * 1. preboot - a. Check whether the security status bits are valid 165062306a36Sopenharmony_ci * b. Check whether fw security is enabled 165162306a36Sopenharmony_ci * c. Check whether hard reset is done by preboot 165262306a36Sopenharmony_ci * 2. boot cpu - a. Fetch boot cpu security status 165362306a36Sopenharmony_ci * b. Check whether hard reset is done by boot cpu 165462306a36Sopenharmony_ci * 3. FW application - a. Fetch fw application security status 165562306a36Sopenharmony_ci * b. Check whether hard reset is done by fw app 165662306a36Sopenharmony_ci */ 165762306a36Sopenharmony_ci prop->hard_reset_done_by_fw = !!(cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_FW_HARD_RST_EN); 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci prop->fw_security_enabled = !!(cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_SECURITY_EN); 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci dev_dbg(hdev->dev, "Firmware preboot boot device status0 %#x\n", 166262306a36Sopenharmony_ci cpu_boot_dev_sts0); 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci dev_dbg(hdev->dev, "Firmware preboot boot device status1 %#x\n", 166562306a36Sopenharmony_ci cpu_boot_dev_sts1); 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci dev_dbg(hdev->dev, "Firmware preboot hard-reset is %s\n", 166862306a36Sopenharmony_ci prop->hard_reset_done_by_fw ? "enabled" : "disabled"); 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci dev_dbg(hdev->dev, "firmware-level security is %s\n", 167162306a36Sopenharmony_ci prop->fw_security_enabled ? "enabled" : "disabled"); 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci dev_dbg(hdev->dev, "GIC controller is %s\n", 167462306a36Sopenharmony_ci prop->gic_interrupts_enable ? "enabled" : "disabled"); 167562306a36Sopenharmony_ci} 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_cistatic int hl_fw_static_read_preboot_status(struct hl_device *hdev) 167862306a36Sopenharmony_ci{ 167962306a36Sopenharmony_ci int rc; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci rc = hl_fw_static_read_device_fw_version(hdev, FW_COMP_PREBOOT); 168262306a36Sopenharmony_ci if (rc) 168362306a36Sopenharmony_ci return rc; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci return 0; 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ciint hl_fw_read_preboot_status(struct hl_device *hdev) 168962306a36Sopenharmony_ci{ 169062306a36Sopenharmony_ci int rc; 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci if (!(hdev->fw_components & FW_TYPE_PREBOOT_CPU)) 169362306a36Sopenharmony_ci return 0; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci /* get FW pre-load parameters */ 169662306a36Sopenharmony_ci hdev->asic_funcs->init_firmware_preload_params(hdev); 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci /* 169962306a36Sopenharmony_ci * In order to determine boot method (static VS dynamic) we need to 170062306a36Sopenharmony_ci * read the boot caps register 170162306a36Sopenharmony_ci */ 170262306a36Sopenharmony_ci rc = hl_fw_read_preboot_caps(hdev); 170362306a36Sopenharmony_ci if (rc) 170462306a36Sopenharmony_ci return rc; 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci hl_fw_preboot_update_state(hdev); 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci /* no need to read preboot status in dynamic load */ 170962306a36Sopenharmony_ci if (hdev->asic_prop.dynamic_fw_load) 171062306a36Sopenharmony_ci return 0; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci return hl_fw_static_read_preboot_status(hdev); 171362306a36Sopenharmony_ci} 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci/* associate string with COMM status */ 171662306a36Sopenharmony_cistatic char *hl_dynamic_fw_status_str[COMMS_STS_INVLD_LAST] = { 171762306a36Sopenharmony_ci [COMMS_STS_NOOP] = "NOOP", 171862306a36Sopenharmony_ci [COMMS_STS_ACK] = "ACK", 171962306a36Sopenharmony_ci [COMMS_STS_OK] = "OK", 172062306a36Sopenharmony_ci [COMMS_STS_ERR] = "ERR", 172162306a36Sopenharmony_ci [COMMS_STS_VALID_ERR] = "VALID_ERR", 172262306a36Sopenharmony_ci [COMMS_STS_TIMEOUT_ERR] = "TIMEOUT_ERR", 172362306a36Sopenharmony_ci}; 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci/** 172662306a36Sopenharmony_ci * hl_fw_dynamic_report_error_status - report error status 172762306a36Sopenharmony_ci * 172862306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 172962306a36Sopenharmony_ci * @status: value of FW status register 173062306a36Sopenharmony_ci * @expected_status: the expected status 173162306a36Sopenharmony_ci */ 173262306a36Sopenharmony_cistatic void hl_fw_dynamic_report_error_status(struct hl_device *hdev, 173362306a36Sopenharmony_ci u32 status, 173462306a36Sopenharmony_ci enum comms_sts expected_status) 173562306a36Sopenharmony_ci{ 173662306a36Sopenharmony_ci enum comms_sts comm_status = 173762306a36Sopenharmony_ci FIELD_GET(COMMS_STATUS_STATUS_MASK, status); 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci if (comm_status < COMMS_STS_INVLD_LAST) 174062306a36Sopenharmony_ci dev_err(hdev->dev, "Device status %s, expected status: %s\n", 174162306a36Sopenharmony_ci hl_dynamic_fw_status_str[comm_status], 174262306a36Sopenharmony_ci hl_dynamic_fw_status_str[expected_status]); 174362306a36Sopenharmony_ci else 174462306a36Sopenharmony_ci dev_err(hdev->dev, "Device status unknown %d, expected status: %s\n", 174562306a36Sopenharmony_ci comm_status, 174662306a36Sopenharmony_ci hl_dynamic_fw_status_str[expected_status]); 174762306a36Sopenharmony_ci} 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci/** 175062306a36Sopenharmony_ci * hl_fw_dynamic_send_cmd - send LKD to FW cmd 175162306a36Sopenharmony_ci * 175262306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 175362306a36Sopenharmony_ci * @fw_loader: managing structure for loading device's FW 175462306a36Sopenharmony_ci * @cmd: LKD to FW cmd code 175562306a36Sopenharmony_ci * @size: size of next FW component to be loaded (0 if not necessary) 175662306a36Sopenharmony_ci * 175762306a36Sopenharmony_ci * LDK to FW exact command layout is defined at struct comms_command. 175862306a36Sopenharmony_ci * note: the size argument is used only when the next FW component should be 175962306a36Sopenharmony_ci * loaded, otherwise it shall be 0. the size is used by the FW in later 176062306a36Sopenharmony_ci * protocol stages and when sending only indicating the amount of memory 176162306a36Sopenharmony_ci * to be allocated by the FW to receive the next boot component. 176262306a36Sopenharmony_ci */ 176362306a36Sopenharmony_cistatic void hl_fw_dynamic_send_cmd(struct hl_device *hdev, 176462306a36Sopenharmony_ci struct fw_load_mgr *fw_loader, 176562306a36Sopenharmony_ci enum comms_cmd cmd, unsigned int size) 176662306a36Sopenharmony_ci{ 176762306a36Sopenharmony_ci struct cpu_dyn_regs *dyn_regs; 176862306a36Sopenharmony_ci u32 val; 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci dyn_regs = &fw_loader->dynamic_loader.comm_desc.cpu_dyn_regs; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci val = FIELD_PREP(COMMS_COMMAND_CMD_MASK, cmd); 177362306a36Sopenharmony_ci val |= FIELD_PREP(COMMS_COMMAND_SIZE_MASK, size); 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci trace_habanalabs_comms_send_cmd(hdev->dev, comms_cmd_str_arr[cmd]); 177662306a36Sopenharmony_ci WREG32(le32_to_cpu(dyn_regs->kmd_msg_to_cpu), val); 177762306a36Sopenharmony_ci} 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci/** 178062306a36Sopenharmony_ci * hl_fw_dynamic_extract_fw_response - update the FW response 178162306a36Sopenharmony_ci * 178262306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 178362306a36Sopenharmony_ci * @fw_loader: managing structure for loading device's FW 178462306a36Sopenharmony_ci * @response: FW response 178562306a36Sopenharmony_ci * @status: the status read from CPU status register 178662306a36Sopenharmony_ci * 178762306a36Sopenharmony_ci * @return 0 on success, otherwise non-zero error code 178862306a36Sopenharmony_ci */ 178962306a36Sopenharmony_cistatic int hl_fw_dynamic_extract_fw_response(struct hl_device *hdev, 179062306a36Sopenharmony_ci struct fw_load_mgr *fw_loader, 179162306a36Sopenharmony_ci struct fw_response *response, 179262306a36Sopenharmony_ci u32 status) 179362306a36Sopenharmony_ci{ 179462306a36Sopenharmony_ci response->status = FIELD_GET(COMMS_STATUS_STATUS_MASK, status); 179562306a36Sopenharmony_ci response->ram_offset = FIELD_GET(COMMS_STATUS_OFFSET_MASK, status) << 179662306a36Sopenharmony_ci COMMS_STATUS_OFFSET_ALIGN_SHIFT; 179762306a36Sopenharmony_ci response->ram_type = FIELD_GET(COMMS_STATUS_RAM_TYPE_MASK, status); 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci if ((response->ram_type != COMMS_SRAM) && 180062306a36Sopenharmony_ci (response->ram_type != COMMS_DRAM)) { 180162306a36Sopenharmony_ci dev_err(hdev->dev, "FW status: invalid RAM type %u\n", 180262306a36Sopenharmony_ci response->ram_type); 180362306a36Sopenharmony_ci return -EIO; 180462306a36Sopenharmony_ci } 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci return 0; 180762306a36Sopenharmony_ci} 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci/** 181062306a36Sopenharmony_ci * hl_fw_dynamic_wait_for_status - wait for status in dynamic FW load 181162306a36Sopenharmony_ci * 181262306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 181362306a36Sopenharmony_ci * @fw_loader: managing structure for loading device's FW 181462306a36Sopenharmony_ci * @expected_status: expected status to wait for 181562306a36Sopenharmony_ci * @timeout: timeout for status wait 181662306a36Sopenharmony_ci * 181762306a36Sopenharmony_ci * @return 0 on success, otherwise non-zero error code 181862306a36Sopenharmony_ci * 181962306a36Sopenharmony_ci * waiting for status from FW include polling the FW status register until 182062306a36Sopenharmony_ci * expected status is received or timeout occurs (whatever occurs first). 182162306a36Sopenharmony_ci */ 182262306a36Sopenharmony_cistatic int hl_fw_dynamic_wait_for_status(struct hl_device *hdev, 182362306a36Sopenharmony_ci struct fw_load_mgr *fw_loader, 182462306a36Sopenharmony_ci enum comms_sts expected_status, 182562306a36Sopenharmony_ci u32 timeout) 182662306a36Sopenharmony_ci{ 182762306a36Sopenharmony_ci struct cpu_dyn_regs *dyn_regs; 182862306a36Sopenharmony_ci u32 status; 182962306a36Sopenharmony_ci int rc; 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci dyn_regs = &fw_loader->dynamic_loader.comm_desc.cpu_dyn_regs; 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci trace_habanalabs_comms_wait_status(hdev->dev, comms_sts_str_arr[expected_status]); 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci /* Wait for expected status */ 183662306a36Sopenharmony_ci rc = hl_poll_timeout( 183762306a36Sopenharmony_ci hdev, 183862306a36Sopenharmony_ci le32_to_cpu(dyn_regs->cpu_cmd_status_to_host), 183962306a36Sopenharmony_ci status, 184062306a36Sopenharmony_ci FIELD_GET(COMMS_STATUS_STATUS_MASK, status) == expected_status, 184162306a36Sopenharmony_ci hdev->fw_comms_poll_interval_usec, 184262306a36Sopenharmony_ci timeout); 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci if (rc) { 184562306a36Sopenharmony_ci hl_fw_dynamic_report_error_status(hdev, status, 184662306a36Sopenharmony_ci expected_status); 184762306a36Sopenharmony_ci return -EIO; 184862306a36Sopenharmony_ci } 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci trace_habanalabs_comms_wait_status_done(hdev->dev, comms_sts_str_arr[expected_status]); 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci /* 185362306a36Sopenharmony_ci * skip storing FW response for NOOP to preserve the actual desired 185462306a36Sopenharmony_ci * FW status 185562306a36Sopenharmony_ci */ 185662306a36Sopenharmony_ci if (expected_status == COMMS_STS_NOOP) 185762306a36Sopenharmony_ci return 0; 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci rc = hl_fw_dynamic_extract_fw_response(hdev, fw_loader, 186062306a36Sopenharmony_ci &fw_loader->dynamic_loader.response, 186162306a36Sopenharmony_ci status); 186262306a36Sopenharmony_ci return rc; 186362306a36Sopenharmony_ci} 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci/** 186662306a36Sopenharmony_ci * hl_fw_dynamic_send_clear_cmd - send clear command to FW 186762306a36Sopenharmony_ci * 186862306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 186962306a36Sopenharmony_ci * @fw_loader: managing structure for loading device's FW 187062306a36Sopenharmony_ci * 187162306a36Sopenharmony_ci * @return 0 on success, otherwise non-zero error code 187262306a36Sopenharmony_ci * 187362306a36Sopenharmony_ci * after command cycle between LKD to FW CPU (i.e. LKD got an expected status 187462306a36Sopenharmony_ci * from FW) we need to clear the CPU status register in order to avoid garbage 187562306a36Sopenharmony_ci * between command cycles. 187662306a36Sopenharmony_ci * This is done by sending clear command and polling the CPU to LKD status 187762306a36Sopenharmony_ci * register to hold the status NOOP 187862306a36Sopenharmony_ci */ 187962306a36Sopenharmony_cistatic int hl_fw_dynamic_send_clear_cmd(struct hl_device *hdev, 188062306a36Sopenharmony_ci struct fw_load_mgr *fw_loader) 188162306a36Sopenharmony_ci{ 188262306a36Sopenharmony_ci hl_fw_dynamic_send_cmd(hdev, fw_loader, COMMS_CLR_STS, 0); 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci return hl_fw_dynamic_wait_for_status(hdev, fw_loader, COMMS_STS_NOOP, 188562306a36Sopenharmony_ci fw_loader->cpu_timeout); 188662306a36Sopenharmony_ci} 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci/** 188962306a36Sopenharmony_ci * hl_fw_dynamic_send_protocol_cmd - send LKD to FW cmd and wait for ACK 189062306a36Sopenharmony_ci * 189162306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 189262306a36Sopenharmony_ci * @fw_loader: managing structure for loading device's FW 189362306a36Sopenharmony_ci * @cmd: LKD to FW cmd code 189462306a36Sopenharmony_ci * @size: size of next FW component to be loaded (0 if not necessary) 189562306a36Sopenharmony_ci * @wait_ok: if true also wait for OK response from FW 189662306a36Sopenharmony_ci * @timeout: timeout for status wait 189762306a36Sopenharmony_ci * 189862306a36Sopenharmony_ci * @return 0 on success, otherwise non-zero error code 189962306a36Sopenharmony_ci * 190062306a36Sopenharmony_ci * brief: 190162306a36Sopenharmony_ci * when sending protocol command we have the following steps: 190262306a36Sopenharmony_ci * - send clear (clear command and verify clear status register) 190362306a36Sopenharmony_ci * - send the actual protocol command 190462306a36Sopenharmony_ci * - wait for ACK on the protocol command 190562306a36Sopenharmony_ci * - send clear 190662306a36Sopenharmony_ci * - send NOOP 190762306a36Sopenharmony_ci * if, in addition, the specific protocol command should wait for OK then: 190862306a36Sopenharmony_ci * - wait for OK 190962306a36Sopenharmony_ci * - send clear 191062306a36Sopenharmony_ci * - send NOOP 191162306a36Sopenharmony_ci * 191262306a36Sopenharmony_ci * NOTES: 191362306a36Sopenharmony_ci * send clear: this is necessary in order to clear the status register to avoid 191462306a36Sopenharmony_ci * leftovers between command 191562306a36Sopenharmony_ci * NOOP command: necessary to avoid loop on the clear command by the FW 191662306a36Sopenharmony_ci */ 191762306a36Sopenharmony_ciint hl_fw_dynamic_send_protocol_cmd(struct hl_device *hdev, 191862306a36Sopenharmony_ci struct fw_load_mgr *fw_loader, 191962306a36Sopenharmony_ci enum comms_cmd cmd, unsigned int size, 192062306a36Sopenharmony_ci bool wait_ok, u32 timeout) 192162306a36Sopenharmony_ci{ 192262306a36Sopenharmony_ci int rc; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci trace_habanalabs_comms_protocol_cmd(hdev->dev, comms_cmd_str_arr[cmd]); 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci /* first send clear command to clean former commands */ 192762306a36Sopenharmony_ci rc = hl_fw_dynamic_send_clear_cmd(hdev, fw_loader); 192862306a36Sopenharmony_ci if (rc) 192962306a36Sopenharmony_ci return rc; 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci /* send the actual command */ 193262306a36Sopenharmony_ci hl_fw_dynamic_send_cmd(hdev, fw_loader, cmd, size); 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci /* wait for ACK for the command */ 193562306a36Sopenharmony_ci rc = hl_fw_dynamic_wait_for_status(hdev, fw_loader, COMMS_STS_ACK, 193662306a36Sopenharmony_ci timeout); 193762306a36Sopenharmony_ci if (rc) 193862306a36Sopenharmony_ci return rc; 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci /* clear command to prepare for NOOP command */ 194162306a36Sopenharmony_ci rc = hl_fw_dynamic_send_clear_cmd(hdev, fw_loader); 194262306a36Sopenharmony_ci if (rc) 194362306a36Sopenharmony_ci return rc; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci /* send the actual NOOP command */ 194662306a36Sopenharmony_ci hl_fw_dynamic_send_cmd(hdev, fw_loader, COMMS_NOOP, 0); 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci if (!wait_ok) 194962306a36Sopenharmony_ci return 0; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci rc = hl_fw_dynamic_wait_for_status(hdev, fw_loader, COMMS_STS_OK, 195262306a36Sopenharmony_ci timeout); 195362306a36Sopenharmony_ci if (rc) 195462306a36Sopenharmony_ci return rc; 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci /* clear command to prepare for NOOP command */ 195762306a36Sopenharmony_ci rc = hl_fw_dynamic_send_clear_cmd(hdev, fw_loader); 195862306a36Sopenharmony_ci if (rc) 195962306a36Sopenharmony_ci return rc; 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci /* send the actual NOOP command */ 196262306a36Sopenharmony_ci hl_fw_dynamic_send_cmd(hdev, fw_loader, COMMS_NOOP, 0); 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci return 0; 196562306a36Sopenharmony_ci} 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci/** 196862306a36Sopenharmony_ci * hl_fw_compat_crc32 - CRC compatible with FW 196962306a36Sopenharmony_ci * 197062306a36Sopenharmony_ci * @data: pointer to the data 197162306a36Sopenharmony_ci * @size: size of the data 197262306a36Sopenharmony_ci * 197362306a36Sopenharmony_ci * @return the CRC32 result 197462306a36Sopenharmony_ci * 197562306a36Sopenharmony_ci * NOTE: kernel's CRC32 differs from standard CRC32 calculation. 197662306a36Sopenharmony_ci * in order to be aligned we need to flip the bits of both the input 197762306a36Sopenharmony_ci * initial CRC and kernel's CRC32 result. 197862306a36Sopenharmony_ci * in addition both sides use initial CRC of 0, 197962306a36Sopenharmony_ci */ 198062306a36Sopenharmony_cistatic u32 hl_fw_compat_crc32(u8 *data, size_t size) 198162306a36Sopenharmony_ci{ 198262306a36Sopenharmony_ci return ~crc32_le(~((u32)0), data, size); 198362306a36Sopenharmony_ci} 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci/** 198662306a36Sopenharmony_ci * hl_fw_dynamic_validate_memory_bound - validate memory bounds for memory 198762306a36Sopenharmony_ci * transfer (image or descriptor) between 198862306a36Sopenharmony_ci * host and FW 198962306a36Sopenharmony_ci * 199062306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 199162306a36Sopenharmony_ci * @addr: device address of memory transfer 199262306a36Sopenharmony_ci * @size: memory transfer size 199362306a36Sopenharmony_ci * @region: PCI memory region 199462306a36Sopenharmony_ci * 199562306a36Sopenharmony_ci * @return 0 on success, otherwise non-zero error code 199662306a36Sopenharmony_ci */ 199762306a36Sopenharmony_cistatic int hl_fw_dynamic_validate_memory_bound(struct hl_device *hdev, 199862306a36Sopenharmony_ci u64 addr, size_t size, 199962306a36Sopenharmony_ci struct pci_mem_region *region) 200062306a36Sopenharmony_ci{ 200162306a36Sopenharmony_ci u64 end_addr; 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci /* now make sure that the memory transfer is within region's bounds */ 200462306a36Sopenharmony_ci end_addr = addr + size; 200562306a36Sopenharmony_ci if (end_addr >= region->region_base + region->region_size) { 200662306a36Sopenharmony_ci dev_err(hdev->dev, 200762306a36Sopenharmony_ci "dynamic FW load: memory transfer end address out of memory region bounds. addr: %llx\n", 200862306a36Sopenharmony_ci end_addr); 200962306a36Sopenharmony_ci return -EIO; 201062306a36Sopenharmony_ci } 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci /* 201362306a36Sopenharmony_ci * now make sure memory transfer is within predefined BAR bounds. 201462306a36Sopenharmony_ci * this is to make sure we do not need to set the bar (e.g. for DRAM 201562306a36Sopenharmony_ci * memory transfers) 201662306a36Sopenharmony_ci */ 201762306a36Sopenharmony_ci if (end_addr >= region->region_base - region->offset_in_bar + 201862306a36Sopenharmony_ci region->bar_size) { 201962306a36Sopenharmony_ci dev_err(hdev->dev, 202062306a36Sopenharmony_ci "FW image beyond PCI BAR bounds\n"); 202162306a36Sopenharmony_ci return -EIO; 202262306a36Sopenharmony_ci } 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci return 0; 202562306a36Sopenharmony_ci} 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci/** 202862306a36Sopenharmony_ci * hl_fw_dynamic_validate_descriptor - validate FW descriptor 202962306a36Sopenharmony_ci * 203062306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 203162306a36Sopenharmony_ci * @fw_loader: managing structure for loading device's FW 203262306a36Sopenharmony_ci * @fw_desc: the descriptor from FW 203362306a36Sopenharmony_ci * 203462306a36Sopenharmony_ci * @return 0 on success, otherwise non-zero error code 203562306a36Sopenharmony_ci */ 203662306a36Sopenharmony_cistatic int hl_fw_dynamic_validate_descriptor(struct hl_device *hdev, 203762306a36Sopenharmony_ci struct fw_load_mgr *fw_loader, 203862306a36Sopenharmony_ci struct lkd_fw_comms_desc *fw_desc) 203962306a36Sopenharmony_ci{ 204062306a36Sopenharmony_ci struct pci_mem_region *region; 204162306a36Sopenharmony_ci enum pci_region region_id; 204262306a36Sopenharmony_ci size_t data_size; 204362306a36Sopenharmony_ci u32 data_crc32; 204462306a36Sopenharmony_ci u8 *data_ptr; 204562306a36Sopenharmony_ci u64 addr; 204662306a36Sopenharmony_ci int rc; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci if (le32_to_cpu(fw_desc->header.magic) != HL_COMMS_DESC_MAGIC) 204962306a36Sopenharmony_ci dev_dbg(hdev->dev, "Invalid magic for dynamic FW descriptor (%x)\n", 205062306a36Sopenharmony_ci fw_desc->header.magic); 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci if (fw_desc->header.version != HL_COMMS_DESC_VER) 205362306a36Sopenharmony_ci dev_dbg(hdev->dev, "Invalid version for dynamic FW descriptor (%x)\n", 205462306a36Sopenharmony_ci fw_desc->header.version); 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci /* 205762306a36Sopenharmony_ci * Calc CRC32 of data without header. use the size of the descriptor 205862306a36Sopenharmony_ci * reported by firmware, without calculating it ourself, to allow adding 205962306a36Sopenharmony_ci * more fields to the lkd_fw_comms_desc structure. 206062306a36Sopenharmony_ci * note that no alignment/stride address issues here as all structures 206162306a36Sopenharmony_ci * are 64 bit padded. 206262306a36Sopenharmony_ci */ 206362306a36Sopenharmony_ci data_ptr = (u8 *)fw_desc + sizeof(struct comms_desc_header); 206462306a36Sopenharmony_ci data_size = le16_to_cpu(fw_desc->header.size); 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci data_crc32 = hl_fw_compat_crc32(data_ptr, data_size); 206762306a36Sopenharmony_ci if (data_crc32 != le32_to_cpu(fw_desc->header.crc32)) { 206862306a36Sopenharmony_ci dev_err(hdev->dev, "CRC32 mismatch for dynamic FW descriptor (%x:%x)\n", 206962306a36Sopenharmony_ci data_crc32, fw_desc->header.crc32); 207062306a36Sopenharmony_ci return -EIO; 207162306a36Sopenharmony_ci } 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci /* find memory region to which to copy the image */ 207462306a36Sopenharmony_ci addr = le64_to_cpu(fw_desc->img_addr); 207562306a36Sopenharmony_ci region_id = hl_get_pci_memory_region(hdev, addr); 207662306a36Sopenharmony_ci if ((region_id != PCI_REGION_SRAM) && ((region_id != PCI_REGION_DRAM))) { 207762306a36Sopenharmony_ci dev_err(hdev->dev, "Invalid region to copy FW image address=%llx\n", addr); 207862306a36Sopenharmony_ci return -EIO; 207962306a36Sopenharmony_ci } 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci region = &hdev->pci_mem_region[region_id]; 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci /* store the region for the copy stage */ 208462306a36Sopenharmony_ci fw_loader->dynamic_loader.image_region = region; 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci /* 208762306a36Sopenharmony_ci * here we know that the start address is valid, now make sure that the 208862306a36Sopenharmony_ci * image is within region's bounds 208962306a36Sopenharmony_ci */ 209062306a36Sopenharmony_ci rc = hl_fw_dynamic_validate_memory_bound(hdev, addr, 209162306a36Sopenharmony_ci fw_loader->dynamic_loader.fw_image_size, 209262306a36Sopenharmony_ci region); 209362306a36Sopenharmony_ci if (rc) { 209462306a36Sopenharmony_ci dev_err(hdev->dev, "invalid mem transfer request for FW image\n"); 209562306a36Sopenharmony_ci return rc; 209662306a36Sopenharmony_ci } 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci /* here we can mark the descriptor as valid as the content has been validated */ 209962306a36Sopenharmony_ci fw_loader->dynamic_loader.fw_desc_valid = true; 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci return 0; 210262306a36Sopenharmony_ci} 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_cistatic int hl_fw_dynamic_validate_response(struct hl_device *hdev, 210562306a36Sopenharmony_ci struct fw_response *response, 210662306a36Sopenharmony_ci struct pci_mem_region *region) 210762306a36Sopenharmony_ci{ 210862306a36Sopenharmony_ci u64 device_addr; 210962306a36Sopenharmony_ci int rc; 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci device_addr = region->region_base + response->ram_offset; 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci /* 211462306a36Sopenharmony_ci * validate that the descriptor is within region's bounds 211562306a36Sopenharmony_ci * Note that as the start address was supplied according to the RAM 211662306a36Sopenharmony_ci * type- testing only the end address is enough 211762306a36Sopenharmony_ci */ 211862306a36Sopenharmony_ci rc = hl_fw_dynamic_validate_memory_bound(hdev, device_addr, 211962306a36Sopenharmony_ci sizeof(struct lkd_fw_comms_desc), 212062306a36Sopenharmony_ci region); 212162306a36Sopenharmony_ci return rc; 212262306a36Sopenharmony_ci} 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci/* 212562306a36Sopenharmony_ci * hl_fw_dynamic_read_descriptor_msg - read and show the ascii msg that sent by fw 212662306a36Sopenharmony_ci * 212762306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 212862306a36Sopenharmony_ci * @fw_desc: the descriptor from FW 212962306a36Sopenharmony_ci */ 213062306a36Sopenharmony_cistatic void hl_fw_dynamic_read_descriptor_msg(struct hl_device *hdev, 213162306a36Sopenharmony_ci struct lkd_fw_comms_desc *fw_desc) 213262306a36Sopenharmony_ci{ 213362306a36Sopenharmony_ci int i; 213462306a36Sopenharmony_ci char *msg; 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci for (i = 0 ; i < LKD_FW_ASCII_MSG_MAX ; i++) { 213762306a36Sopenharmony_ci if (!fw_desc->ascii_msg[i].valid) 213862306a36Sopenharmony_ci return; 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci /* force NULL termination */ 214162306a36Sopenharmony_ci msg = fw_desc->ascii_msg[i].msg; 214262306a36Sopenharmony_ci msg[LKD_FW_ASCII_MSG_MAX_LEN - 1] = '\0'; 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci switch (fw_desc->ascii_msg[i].msg_lvl) { 214562306a36Sopenharmony_ci case LKD_FW_ASCII_MSG_ERR: 214662306a36Sopenharmony_ci dev_err(hdev->dev, "fw: %s", fw_desc->ascii_msg[i].msg); 214762306a36Sopenharmony_ci break; 214862306a36Sopenharmony_ci case LKD_FW_ASCII_MSG_WRN: 214962306a36Sopenharmony_ci dev_warn(hdev->dev, "fw: %s", fw_desc->ascii_msg[i].msg); 215062306a36Sopenharmony_ci break; 215162306a36Sopenharmony_ci case LKD_FW_ASCII_MSG_INF: 215262306a36Sopenharmony_ci dev_info(hdev->dev, "fw: %s", fw_desc->ascii_msg[i].msg); 215362306a36Sopenharmony_ci break; 215462306a36Sopenharmony_ci default: 215562306a36Sopenharmony_ci dev_dbg(hdev->dev, "fw: %s", fw_desc->ascii_msg[i].msg); 215662306a36Sopenharmony_ci break; 215762306a36Sopenharmony_ci } 215862306a36Sopenharmony_ci } 215962306a36Sopenharmony_ci} 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci/** 216262306a36Sopenharmony_ci * hl_fw_dynamic_read_and_validate_descriptor - read and validate FW descriptor 216362306a36Sopenharmony_ci * 216462306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 216562306a36Sopenharmony_ci * @fw_loader: managing structure for loading device's FW 216662306a36Sopenharmony_ci * 216762306a36Sopenharmony_ci * @return 0 on success, otherwise non-zero error code 216862306a36Sopenharmony_ci */ 216962306a36Sopenharmony_cistatic int hl_fw_dynamic_read_and_validate_descriptor(struct hl_device *hdev, 217062306a36Sopenharmony_ci struct fw_load_mgr *fw_loader) 217162306a36Sopenharmony_ci{ 217262306a36Sopenharmony_ci struct lkd_fw_comms_desc *fw_desc; 217362306a36Sopenharmony_ci struct pci_mem_region *region; 217462306a36Sopenharmony_ci struct fw_response *response; 217562306a36Sopenharmony_ci void *temp_fw_desc; 217662306a36Sopenharmony_ci void __iomem *src; 217762306a36Sopenharmony_ci u16 fw_data_size; 217862306a36Sopenharmony_ci enum pci_region region_id; 217962306a36Sopenharmony_ci int rc; 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci fw_desc = &fw_loader->dynamic_loader.comm_desc; 218262306a36Sopenharmony_ci response = &fw_loader->dynamic_loader.response; 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci region_id = (response->ram_type == COMMS_SRAM) ? 218562306a36Sopenharmony_ci PCI_REGION_SRAM : PCI_REGION_DRAM; 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci region = &hdev->pci_mem_region[region_id]; 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci rc = hl_fw_dynamic_validate_response(hdev, response, region); 219062306a36Sopenharmony_ci if (rc) { 219162306a36Sopenharmony_ci dev_err(hdev->dev, 219262306a36Sopenharmony_ci "invalid mem transfer request for FW descriptor\n"); 219362306a36Sopenharmony_ci return rc; 219462306a36Sopenharmony_ci } 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci /* 219762306a36Sopenharmony_ci * extract address to copy the descriptor from 219862306a36Sopenharmony_ci * in addition, as the descriptor value is going to be over-ridden by new data- we mark it 219962306a36Sopenharmony_ci * as invalid. 220062306a36Sopenharmony_ci * it will be marked again as valid once validated 220162306a36Sopenharmony_ci */ 220262306a36Sopenharmony_ci fw_loader->dynamic_loader.fw_desc_valid = false; 220362306a36Sopenharmony_ci src = hdev->pcie_bar[region->bar_id] + region->offset_in_bar + 220462306a36Sopenharmony_ci response->ram_offset; 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci /* 220762306a36Sopenharmony_ci * We do the copy of the fw descriptor in 2 phases: 220862306a36Sopenharmony_ci * 1. copy the header + data info according to our lkd_fw_comms_desc definition. 220962306a36Sopenharmony_ci * then we're able to read the actual data size provided by fw. 221062306a36Sopenharmony_ci * this is needed for cases where data in descriptor was changed(add/remove) 221162306a36Sopenharmony_ci * in embedded specs header file before updating lkd copy of the header file 221262306a36Sopenharmony_ci * 2. copy descriptor to temporary buffer with aligned size and send it to validation 221362306a36Sopenharmony_ci */ 221462306a36Sopenharmony_ci memcpy_fromio(fw_desc, src, sizeof(struct lkd_fw_comms_desc)); 221562306a36Sopenharmony_ci fw_data_size = le16_to_cpu(fw_desc->header.size); 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci temp_fw_desc = vzalloc(sizeof(struct comms_desc_header) + fw_data_size); 221862306a36Sopenharmony_ci if (!temp_fw_desc) 221962306a36Sopenharmony_ci return -ENOMEM; 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_ci memcpy_fromio(temp_fw_desc, src, sizeof(struct comms_desc_header) + fw_data_size); 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci rc = hl_fw_dynamic_validate_descriptor(hdev, fw_loader, 222462306a36Sopenharmony_ci (struct lkd_fw_comms_desc *) temp_fw_desc); 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci if (!rc) 222762306a36Sopenharmony_ci hl_fw_dynamic_read_descriptor_msg(hdev, temp_fw_desc); 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci vfree(temp_fw_desc); 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci return rc; 223262306a36Sopenharmony_ci} 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci/** 223562306a36Sopenharmony_ci * hl_fw_dynamic_request_descriptor - handshake with CPU to get FW descriptor 223662306a36Sopenharmony_ci * 223762306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 223862306a36Sopenharmony_ci * @fw_loader: managing structure for loading device's FW 223962306a36Sopenharmony_ci * @next_image_size: size to allocate for next FW component 224062306a36Sopenharmony_ci * 224162306a36Sopenharmony_ci * @return 0 on success, otherwise non-zero error code 224262306a36Sopenharmony_ci */ 224362306a36Sopenharmony_cistatic int hl_fw_dynamic_request_descriptor(struct hl_device *hdev, 224462306a36Sopenharmony_ci struct fw_load_mgr *fw_loader, 224562306a36Sopenharmony_ci size_t next_image_size) 224662306a36Sopenharmony_ci{ 224762306a36Sopenharmony_ci int rc; 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_PREP_DESC, 225062306a36Sopenharmony_ci next_image_size, true, 225162306a36Sopenharmony_ci fw_loader->cpu_timeout); 225262306a36Sopenharmony_ci if (rc) 225362306a36Sopenharmony_ci return rc; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci return hl_fw_dynamic_read_and_validate_descriptor(hdev, fw_loader); 225662306a36Sopenharmony_ci} 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci/** 225962306a36Sopenharmony_ci * hl_fw_dynamic_read_device_fw_version - read FW version to exposed properties 226062306a36Sopenharmony_ci * 226162306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 226262306a36Sopenharmony_ci * @fwc: the firmware component 226362306a36Sopenharmony_ci * @fw_version: fw component's version string 226462306a36Sopenharmony_ci */ 226562306a36Sopenharmony_cistatic int hl_fw_dynamic_read_device_fw_version(struct hl_device *hdev, 226662306a36Sopenharmony_ci enum hl_fw_component fwc, 226762306a36Sopenharmony_ci const char *fw_version) 226862306a36Sopenharmony_ci{ 226962306a36Sopenharmony_ci struct asic_fixed_properties *prop = &hdev->asic_prop; 227062306a36Sopenharmony_ci char *preboot_ver, *boot_ver; 227162306a36Sopenharmony_ci char btl_ver[32]; 227262306a36Sopenharmony_ci int rc; 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci switch (fwc) { 227562306a36Sopenharmony_ci case FW_COMP_BOOT_FIT: 227662306a36Sopenharmony_ci strscpy(prop->uboot_ver, fw_version, VERSION_MAX_LEN); 227762306a36Sopenharmony_ci boot_ver = extract_fw_ver_from_str(prop->uboot_ver); 227862306a36Sopenharmony_ci if (boot_ver) { 227962306a36Sopenharmony_ci dev_info(hdev->dev, "boot-fit version %s\n", boot_ver); 228062306a36Sopenharmony_ci kfree(boot_ver); 228162306a36Sopenharmony_ci } 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_ci break; 228462306a36Sopenharmony_ci case FW_COMP_PREBOOT: 228562306a36Sopenharmony_ci strscpy(prop->preboot_ver, fw_version, VERSION_MAX_LEN); 228662306a36Sopenharmony_ci preboot_ver = strnstr(prop->preboot_ver, "Preboot", VERSION_MAX_LEN); 228762306a36Sopenharmony_ci dev_info(hdev->dev, "preboot full version: '%s'\n", preboot_ver); 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci if (preboot_ver && preboot_ver != prop->preboot_ver) { 229062306a36Sopenharmony_ci strscpy(btl_ver, prop->preboot_ver, 229162306a36Sopenharmony_ci min((int) (preboot_ver - prop->preboot_ver), 31)); 229262306a36Sopenharmony_ci dev_info(hdev->dev, "%s\n", btl_ver); 229362306a36Sopenharmony_ci } 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci rc = hl_get_sw_major_minor_subminor(hdev, preboot_ver); 229662306a36Sopenharmony_ci if (rc) 229762306a36Sopenharmony_ci return rc; 229862306a36Sopenharmony_ci preboot_ver = extract_fw_ver_from_str(prop->preboot_ver); 229962306a36Sopenharmony_ci if (preboot_ver) { 230062306a36Sopenharmony_ci rc = hl_get_preboot_major_minor(hdev, preboot_ver); 230162306a36Sopenharmony_ci kfree(preboot_ver); 230262306a36Sopenharmony_ci if (rc) 230362306a36Sopenharmony_ci return rc; 230462306a36Sopenharmony_ci } 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci break; 230762306a36Sopenharmony_ci default: 230862306a36Sopenharmony_ci dev_warn(hdev->dev, "Undefined FW component: %d\n", fwc); 230962306a36Sopenharmony_ci return -EINVAL; 231062306a36Sopenharmony_ci } 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci return 0; 231362306a36Sopenharmony_ci} 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci/** 231662306a36Sopenharmony_ci * hl_fw_dynamic_copy_image - copy image to memory allocated by the FW 231762306a36Sopenharmony_ci * 231862306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 231962306a36Sopenharmony_ci * @fw: fw descriptor 232062306a36Sopenharmony_ci * @fw_loader: managing structure for loading device's FW 232162306a36Sopenharmony_ci */ 232262306a36Sopenharmony_cistatic int hl_fw_dynamic_copy_image(struct hl_device *hdev, 232362306a36Sopenharmony_ci const struct firmware *fw, 232462306a36Sopenharmony_ci struct fw_load_mgr *fw_loader) 232562306a36Sopenharmony_ci{ 232662306a36Sopenharmony_ci struct lkd_fw_comms_desc *fw_desc; 232762306a36Sopenharmony_ci struct pci_mem_region *region; 232862306a36Sopenharmony_ci void __iomem *dest; 232962306a36Sopenharmony_ci u64 addr; 233062306a36Sopenharmony_ci int rc; 233162306a36Sopenharmony_ci 233262306a36Sopenharmony_ci fw_desc = &fw_loader->dynamic_loader.comm_desc; 233362306a36Sopenharmony_ci addr = le64_to_cpu(fw_desc->img_addr); 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci /* find memory region to which to copy the image */ 233662306a36Sopenharmony_ci region = fw_loader->dynamic_loader.image_region; 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci dest = hdev->pcie_bar[region->bar_id] + region->offset_in_bar + 233962306a36Sopenharmony_ci (addr - region->region_base); 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci rc = hl_fw_copy_fw_to_device(hdev, fw, dest, 234262306a36Sopenharmony_ci fw_loader->boot_fit_img.src_off, 234362306a36Sopenharmony_ci fw_loader->boot_fit_img.copy_size); 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ci return rc; 234662306a36Sopenharmony_ci} 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci/** 234962306a36Sopenharmony_ci * hl_fw_dynamic_copy_msg - copy msg to memory allocated by the FW 235062306a36Sopenharmony_ci * 235162306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 235262306a36Sopenharmony_ci * @msg: message 235362306a36Sopenharmony_ci * @fw_loader: managing structure for loading device's FW 235462306a36Sopenharmony_ci */ 235562306a36Sopenharmony_cistatic int hl_fw_dynamic_copy_msg(struct hl_device *hdev, 235662306a36Sopenharmony_ci struct lkd_msg_comms *msg, struct fw_load_mgr *fw_loader) 235762306a36Sopenharmony_ci{ 235862306a36Sopenharmony_ci struct lkd_fw_comms_desc *fw_desc; 235962306a36Sopenharmony_ci struct pci_mem_region *region; 236062306a36Sopenharmony_ci void __iomem *dest; 236162306a36Sopenharmony_ci u64 addr; 236262306a36Sopenharmony_ci int rc; 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_ci fw_desc = &fw_loader->dynamic_loader.comm_desc; 236562306a36Sopenharmony_ci addr = le64_to_cpu(fw_desc->img_addr); 236662306a36Sopenharmony_ci 236762306a36Sopenharmony_ci /* find memory region to which to copy the image */ 236862306a36Sopenharmony_ci region = fw_loader->dynamic_loader.image_region; 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci dest = hdev->pcie_bar[region->bar_id] + region->offset_in_bar + 237162306a36Sopenharmony_ci (addr - region->region_base); 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_ci rc = hl_fw_copy_msg_to_device(hdev, msg, dest, 0, 0); 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci return rc; 237662306a36Sopenharmony_ci} 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci/** 237962306a36Sopenharmony_ci * hl_fw_boot_fit_update_state - update internal data structures after boot-fit 238062306a36Sopenharmony_ci * is loaded 238162306a36Sopenharmony_ci * 238262306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 238362306a36Sopenharmony_ci * @cpu_boot_dev_sts0_reg: register holding CPU boot dev status 0 238462306a36Sopenharmony_ci * @cpu_boot_dev_sts1_reg: register holding CPU boot dev status 1 238562306a36Sopenharmony_ci * 238662306a36Sopenharmony_ci * @return 0 on success, otherwise non-zero error code 238762306a36Sopenharmony_ci */ 238862306a36Sopenharmony_cistatic void hl_fw_boot_fit_update_state(struct hl_device *hdev, 238962306a36Sopenharmony_ci u32 cpu_boot_dev_sts0_reg, 239062306a36Sopenharmony_ci u32 cpu_boot_dev_sts1_reg) 239162306a36Sopenharmony_ci{ 239262306a36Sopenharmony_ci struct asic_fixed_properties *prop = &hdev->asic_prop; 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci hdev->fw_loader.fw_comp_loaded |= FW_TYPE_BOOT_CPU; 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_ci /* Read boot_cpu status bits */ 239762306a36Sopenharmony_ci if (prop->fw_preboot_cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_ENABLED) { 239862306a36Sopenharmony_ci prop->fw_bootfit_cpu_boot_dev_sts0 = 239962306a36Sopenharmony_ci RREG32(cpu_boot_dev_sts0_reg); 240062306a36Sopenharmony_ci 240162306a36Sopenharmony_ci prop->hard_reset_done_by_fw = !!(prop->fw_bootfit_cpu_boot_dev_sts0 & 240262306a36Sopenharmony_ci CPU_BOOT_DEV_STS0_FW_HARD_RST_EN); 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci dev_dbg(hdev->dev, "Firmware boot CPU status0 %#x\n", 240562306a36Sopenharmony_ci prop->fw_bootfit_cpu_boot_dev_sts0); 240662306a36Sopenharmony_ci } 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_ci if (prop->fw_cpu_boot_dev_sts1_valid) { 240962306a36Sopenharmony_ci prop->fw_bootfit_cpu_boot_dev_sts1 = 241062306a36Sopenharmony_ci RREG32(cpu_boot_dev_sts1_reg); 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci dev_dbg(hdev->dev, "Firmware boot CPU status1 %#x\n", 241362306a36Sopenharmony_ci prop->fw_bootfit_cpu_boot_dev_sts1); 241462306a36Sopenharmony_ci } 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci dev_dbg(hdev->dev, "Firmware boot CPU hard-reset is %s\n", 241762306a36Sopenharmony_ci prop->hard_reset_done_by_fw ? "enabled" : "disabled"); 241862306a36Sopenharmony_ci} 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_cistatic void hl_fw_dynamic_update_linux_interrupt_if(struct hl_device *hdev) 242162306a36Sopenharmony_ci{ 242262306a36Sopenharmony_ci struct cpu_dyn_regs *dyn_regs = 242362306a36Sopenharmony_ci &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs; 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_ci /* Check whether all 3 interrupt interfaces are set, if not use a 242662306a36Sopenharmony_ci * single interface 242762306a36Sopenharmony_ci */ 242862306a36Sopenharmony_ci if (!hdev->asic_prop.gic_interrupts_enable && 242962306a36Sopenharmony_ci !(hdev->asic_prop.fw_app_cpu_boot_dev_sts0 & 243062306a36Sopenharmony_ci CPU_BOOT_DEV_STS0_MULTI_IRQ_POLL_EN)) { 243162306a36Sopenharmony_ci dyn_regs->gic_host_halt_irq = dyn_regs->gic_host_pi_upd_irq; 243262306a36Sopenharmony_ci dyn_regs->gic_host_ints_irq = dyn_regs->gic_host_pi_upd_irq; 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci dev_warn(hdev->dev, 243562306a36Sopenharmony_ci "Using a single interrupt interface towards cpucp"); 243662306a36Sopenharmony_ci } 243762306a36Sopenharmony_ci} 243862306a36Sopenharmony_ci/** 243962306a36Sopenharmony_ci * hl_fw_dynamic_load_image - load FW image using dynamic protocol 244062306a36Sopenharmony_ci * 244162306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 244262306a36Sopenharmony_ci * @fw_loader: managing structure for loading device's FW 244362306a36Sopenharmony_ci * @load_fwc: the FW component to be loaded 244462306a36Sopenharmony_ci * @img_ld_timeout: image load timeout 244562306a36Sopenharmony_ci * 244662306a36Sopenharmony_ci * @return 0 on success, otherwise non-zero error code 244762306a36Sopenharmony_ci */ 244862306a36Sopenharmony_cistatic int hl_fw_dynamic_load_image(struct hl_device *hdev, 244962306a36Sopenharmony_ci struct fw_load_mgr *fw_loader, 245062306a36Sopenharmony_ci enum hl_fw_component load_fwc, 245162306a36Sopenharmony_ci u32 img_ld_timeout) 245262306a36Sopenharmony_ci{ 245362306a36Sopenharmony_ci enum hl_fw_component cur_fwc; 245462306a36Sopenharmony_ci const struct firmware *fw; 245562306a36Sopenharmony_ci char *fw_name; 245662306a36Sopenharmony_ci int rc = 0; 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci /* 245962306a36Sopenharmony_ci * when loading image we have one of 2 scenarios: 246062306a36Sopenharmony_ci * 1. current FW component is preboot and we want to load boot-fit 246162306a36Sopenharmony_ci * 2. current FW component is boot-fit and we want to load linux 246262306a36Sopenharmony_ci */ 246362306a36Sopenharmony_ci if (load_fwc == FW_COMP_BOOT_FIT) { 246462306a36Sopenharmony_ci cur_fwc = FW_COMP_PREBOOT; 246562306a36Sopenharmony_ci fw_name = fw_loader->boot_fit_img.image_name; 246662306a36Sopenharmony_ci } else { 246762306a36Sopenharmony_ci cur_fwc = FW_COMP_BOOT_FIT; 246862306a36Sopenharmony_ci fw_name = fw_loader->linux_img.image_name; 246962306a36Sopenharmony_ci } 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_ci /* request FW in order to communicate to FW the size to be allocated */ 247262306a36Sopenharmony_ci rc = hl_request_fw(hdev, &fw, fw_name); 247362306a36Sopenharmony_ci if (rc) 247462306a36Sopenharmony_ci return rc; 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci /* store the image size for future validation */ 247762306a36Sopenharmony_ci fw_loader->dynamic_loader.fw_image_size = fw->size; 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci rc = hl_fw_dynamic_request_descriptor(hdev, fw_loader, fw->size); 248062306a36Sopenharmony_ci if (rc) 248162306a36Sopenharmony_ci goto release_fw; 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_ci /* read preboot version */ 248462306a36Sopenharmony_ci rc = hl_fw_dynamic_read_device_fw_version(hdev, cur_fwc, 248562306a36Sopenharmony_ci fw_loader->dynamic_loader.comm_desc.cur_fw_ver); 248662306a36Sopenharmony_ci if (rc) 248762306a36Sopenharmony_ci goto release_fw; 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci /* copy boot fit to space allocated by FW */ 249062306a36Sopenharmony_ci rc = hl_fw_dynamic_copy_image(hdev, fw, fw_loader); 249162306a36Sopenharmony_ci if (rc) 249262306a36Sopenharmony_ci goto release_fw; 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ci rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_DATA_RDY, 249562306a36Sopenharmony_ci 0, true, 249662306a36Sopenharmony_ci fw_loader->cpu_timeout); 249762306a36Sopenharmony_ci if (rc) 249862306a36Sopenharmony_ci goto release_fw; 249962306a36Sopenharmony_ci 250062306a36Sopenharmony_ci rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_EXEC, 250162306a36Sopenharmony_ci 0, false, 250262306a36Sopenharmony_ci img_ld_timeout); 250362306a36Sopenharmony_ci 250462306a36Sopenharmony_cirelease_fw: 250562306a36Sopenharmony_ci hl_release_firmware(fw); 250662306a36Sopenharmony_ci return rc; 250762306a36Sopenharmony_ci} 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_cistatic int hl_fw_dynamic_wait_for_boot_fit_active(struct hl_device *hdev, 251062306a36Sopenharmony_ci struct fw_load_mgr *fw_loader) 251162306a36Sopenharmony_ci{ 251262306a36Sopenharmony_ci struct dynamic_fw_load_mgr *dyn_loader; 251362306a36Sopenharmony_ci u32 status; 251462306a36Sopenharmony_ci int rc; 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci dyn_loader = &fw_loader->dynamic_loader; 251762306a36Sopenharmony_ci 251862306a36Sopenharmony_ci /* 251962306a36Sopenharmony_ci * Make sure CPU boot-loader is running 252062306a36Sopenharmony_ci * Note that the CPU_BOOT_STATUS_SRAM_AVAIL is generally set by Linux 252162306a36Sopenharmony_ci * yet there is a debug scenario in which we loading uboot (without Linux) 252262306a36Sopenharmony_ci * which at later stage is relocated to DRAM. In this case we expect 252362306a36Sopenharmony_ci * uboot to set the CPU_BOOT_STATUS_SRAM_AVAIL and so we add it to the 252462306a36Sopenharmony_ci * poll flags 252562306a36Sopenharmony_ci */ 252662306a36Sopenharmony_ci rc = hl_poll_timeout( 252762306a36Sopenharmony_ci hdev, 252862306a36Sopenharmony_ci le32_to_cpu(dyn_loader->comm_desc.cpu_dyn_regs.cpu_boot_status), 252962306a36Sopenharmony_ci status, 253062306a36Sopenharmony_ci (status == CPU_BOOT_STATUS_READY_TO_BOOT) || 253162306a36Sopenharmony_ci (status == CPU_BOOT_STATUS_SRAM_AVAIL), 253262306a36Sopenharmony_ci hdev->fw_poll_interval_usec, 253362306a36Sopenharmony_ci dyn_loader->wait_for_bl_timeout); 253462306a36Sopenharmony_ci if (rc) { 253562306a36Sopenharmony_ci dev_err(hdev->dev, "failed to wait for boot (status = %d)\n", status); 253662306a36Sopenharmony_ci return rc; 253762306a36Sopenharmony_ci } 253862306a36Sopenharmony_ci 253962306a36Sopenharmony_ci dev_dbg(hdev->dev, "uboot status = %d\n", status); 254062306a36Sopenharmony_ci return 0; 254162306a36Sopenharmony_ci} 254262306a36Sopenharmony_ci 254362306a36Sopenharmony_cistatic int hl_fw_dynamic_wait_for_linux_active(struct hl_device *hdev, 254462306a36Sopenharmony_ci struct fw_load_mgr *fw_loader) 254562306a36Sopenharmony_ci{ 254662306a36Sopenharmony_ci struct dynamic_fw_load_mgr *dyn_loader; 254762306a36Sopenharmony_ci u32 status; 254862306a36Sopenharmony_ci int rc; 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci dyn_loader = &fw_loader->dynamic_loader; 255162306a36Sopenharmony_ci 255262306a36Sopenharmony_ci /* Make sure CPU linux is running */ 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci rc = hl_poll_timeout( 255562306a36Sopenharmony_ci hdev, 255662306a36Sopenharmony_ci le32_to_cpu(dyn_loader->comm_desc.cpu_dyn_regs.cpu_boot_status), 255762306a36Sopenharmony_ci status, 255862306a36Sopenharmony_ci (status == CPU_BOOT_STATUS_SRAM_AVAIL), 255962306a36Sopenharmony_ci hdev->fw_poll_interval_usec, 256062306a36Sopenharmony_ci fw_loader->cpu_timeout); 256162306a36Sopenharmony_ci if (rc) { 256262306a36Sopenharmony_ci dev_err(hdev->dev, "failed to wait for Linux (status = %d)\n", status); 256362306a36Sopenharmony_ci return rc; 256462306a36Sopenharmony_ci } 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci dev_dbg(hdev->dev, "Boot status = %d\n", status); 256762306a36Sopenharmony_ci return 0; 256862306a36Sopenharmony_ci} 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci/** 257162306a36Sopenharmony_ci * hl_fw_linux_update_state - update internal data structures after Linux 257262306a36Sopenharmony_ci * is loaded. 257362306a36Sopenharmony_ci * Note: Linux initialization is comprised mainly 257462306a36Sopenharmony_ci * of two stages - loading kernel (SRAM_AVAIL) 257562306a36Sopenharmony_ci * & loading ARMCP. 257662306a36Sopenharmony_ci * Therefore reading boot device status in any of 257762306a36Sopenharmony_ci * these stages might result in different values. 257862306a36Sopenharmony_ci * 257962306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 258062306a36Sopenharmony_ci * @cpu_boot_dev_sts0_reg: register holding CPU boot dev status 0 258162306a36Sopenharmony_ci * @cpu_boot_dev_sts1_reg: register holding CPU boot dev status 1 258262306a36Sopenharmony_ci * 258362306a36Sopenharmony_ci * @return 0 on success, otherwise non-zero error code 258462306a36Sopenharmony_ci */ 258562306a36Sopenharmony_cistatic void hl_fw_linux_update_state(struct hl_device *hdev, 258662306a36Sopenharmony_ci u32 cpu_boot_dev_sts0_reg, 258762306a36Sopenharmony_ci u32 cpu_boot_dev_sts1_reg) 258862306a36Sopenharmony_ci{ 258962306a36Sopenharmony_ci struct asic_fixed_properties *prop = &hdev->asic_prop; 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_ci hdev->fw_loader.fw_comp_loaded |= FW_TYPE_LINUX; 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_ci /* Read FW application security bits */ 259462306a36Sopenharmony_ci if (prop->fw_cpu_boot_dev_sts0_valid) { 259562306a36Sopenharmony_ci prop->fw_app_cpu_boot_dev_sts0 = RREG32(cpu_boot_dev_sts0_reg); 259662306a36Sopenharmony_ci 259762306a36Sopenharmony_ci prop->hard_reset_done_by_fw = !!(prop->fw_app_cpu_boot_dev_sts0 & 259862306a36Sopenharmony_ci CPU_BOOT_DEV_STS0_FW_HARD_RST_EN); 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_ci if (prop->fw_app_cpu_boot_dev_sts0 & 260162306a36Sopenharmony_ci CPU_BOOT_DEV_STS0_GIC_PRIVILEGED_EN) 260262306a36Sopenharmony_ci prop->gic_interrupts_enable = false; 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_ci dev_dbg(hdev->dev, 260562306a36Sopenharmony_ci "Firmware application CPU status0 %#x\n", 260662306a36Sopenharmony_ci prop->fw_app_cpu_boot_dev_sts0); 260762306a36Sopenharmony_ci 260862306a36Sopenharmony_ci dev_dbg(hdev->dev, "GIC controller is %s\n", 260962306a36Sopenharmony_ci prop->gic_interrupts_enable ? 261062306a36Sopenharmony_ci "enabled" : "disabled"); 261162306a36Sopenharmony_ci } 261262306a36Sopenharmony_ci 261362306a36Sopenharmony_ci if (prop->fw_cpu_boot_dev_sts1_valid) { 261462306a36Sopenharmony_ci prop->fw_app_cpu_boot_dev_sts1 = RREG32(cpu_boot_dev_sts1_reg); 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci dev_dbg(hdev->dev, 261762306a36Sopenharmony_ci "Firmware application CPU status1 %#x\n", 261862306a36Sopenharmony_ci prop->fw_app_cpu_boot_dev_sts1); 261962306a36Sopenharmony_ci } 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci dev_dbg(hdev->dev, "Firmware application CPU hard-reset is %s\n", 262262306a36Sopenharmony_ci prop->hard_reset_done_by_fw ? "enabled" : "disabled"); 262362306a36Sopenharmony_ci 262462306a36Sopenharmony_ci dev_info(hdev->dev, "Successfully loaded firmware to device\n"); 262562306a36Sopenharmony_ci} 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ci/** 262862306a36Sopenharmony_ci * hl_fw_dynamic_send_msg - send a COMMS message with attached data 262962306a36Sopenharmony_ci * 263062306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 263162306a36Sopenharmony_ci * @fw_loader: managing structure for loading device's FW 263262306a36Sopenharmony_ci * @msg_type: message type 263362306a36Sopenharmony_ci * @data: data to be sent 263462306a36Sopenharmony_ci * 263562306a36Sopenharmony_ci * @return 0 on success, otherwise non-zero error code 263662306a36Sopenharmony_ci */ 263762306a36Sopenharmony_cistatic int hl_fw_dynamic_send_msg(struct hl_device *hdev, 263862306a36Sopenharmony_ci struct fw_load_mgr *fw_loader, u8 msg_type, void *data) 263962306a36Sopenharmony_ci{ 264062306a36Sopenharmony_ci struct lkd_msg_comms *msg; 264162306a36Sopenharmony_ci int rc; 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ci msg = kzalloc(sizeof(*msg), GFP_KERNEL); 264462306a36Sopenharmony_ci if (!msg) 264562306a36Sopenharmony_ci return -ENOMEM; 264662306a36Sopenharmony_ci 264762306a36Sopenharmony_ci /* create message to be sent */ 264862306a36Sopenharmony_ci msg->header.type = msg_type; 264962306a36Sopenharmony_ci msg->header.size = cpu_to_le16(sizeof(struct comms_msg_header)); 265062306a36Sopenharmony_ci msg->header.magic = cpu_to_le32(HL_COMMS_MSG_MAGIC); 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci switch (msg_type) { 265362306a36Sopenharmony_ci case HL_COMMS_RESET_CAUSE_TYPE: 265462306a36Sopenharmony_ci msg->reset_cause = *(__u8 *) data; 265562306a36Sopenharmony_ci break; 265662306a36Sopenharmony_ci 265762306a36Sopenharmony_ci default: 265862306a36Sopenharmony_ci dev_err(hdev->dev, 265962306a36Sopenharmony_ci "Send COMMS message - invalid message type %u\n", 266062306a36Sopenharmony_ci msg_type); 266162306a36Sopenharmony_ci rc = -EINVAL; 266262306a36Sopenharmony_ci goto out; 266362306a36Sopenharmony_ci } 266462306a36Sopenharmony_ci 266562306a36Sopenharmony_ci rc = hl_fw_dynamic_request_descriptor(hdev, fw_loader, 266662306a36Sopenharmony_ci sizeof(struct lkd_msg_comms)); 266762306a36Sopenharmony_ci if (rc) 266862306a36Sopenharmony_ci goto out; 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_ci /* copy message to space allocated by FW */ 267162306a36Sopenharmony_ci rc = hl_fw_dynamic_copy_msg(hdev, msg, fw_loader); 267262306a36Sopenharmony_ci if (rc) 267362306a36Sopenharmony_ci goto out; 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_DATA_RDY, 267662306a36Sopenharmony_ci 0, true, 267762306a36Sopenharmony_ci fw_loader->cpu_timeout); 267862306a36Sopenharmony_ci if (rc) 267962306a36Sopenharmony_ci goto out; 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_EXEC, 268262306a36Sopenharmony_ci 0, true, 268362306a36Sopenharmony_ci fw_loader->cpu_timeout); 268462306a36Sopenharmony_ci 268562306a36Sopenharmony_ciout: 268662306a36Sopenharmony_ci kfree(msg); 268762306a36Sopenharmony_ci return rc; 268862306a36Sopenharmony_ci} 268962306a36Sopenharmony_ci 269062306a36Sopenharmony_ci/** 269162306a36Sopenharmony_ci * hl_fw_dynamic_init_cpu - initialize the device CPU using dynamic protocol 269262306a36Sopenharmony_ci * 269362306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 269462306a36Sopenharmony_ci * @fw_loader: managing structure for loading device's FW 269562306a36Sopenharmony_ci * 269662306a36Sopenharmony_ci * @return 0 on success, otherwise non-zero error code 269762306a36Sopenharmony_ci * 269862306a36Sopenharmony_ci * brief: the dynamic protocol is master (LKD) slave (FW CPU) protocol. 269962306a36Sopenharmony_ci * the communication is done using registers: 270062306a36Sopenharmony_ci * - LKD command register 270162306a36Sopenharmony_ci * - FW status register 270262306a36Sopenharmony_ci * the protocol is race free. this goal is achieved by splitting the requests 270362306a36Sopenharmony_ci * and response to known synchronization points between the LKD and the FW. 270462306a36Sopenharmony_ci * each response to LKD request is known and bound to a predefined timeout. 270562306a36Sopenharmony_ci * in case of timeout expiration without the desired status from FW- the 270662306a36Sopenharmony_ci * protocol (and hence the boot) will fail. 270762306a36Sopenharmony_ci */ 270862306a36Sopenharmony_cistatic int hl_fw_dynamic_init_cpu(struct hl_device *hdev, 270962306a36Sopenharmony_ci struct fw_load_mgr *fw_loader) 271062306a36Sopenharmony_ci{ 271162306a36Sopenharmony_ci struct cpu_dyn_regs *dyn_regs; 271262306a36Sopenharmony_ci int rc, fw_error_rc; 271362306a36Sopenharmony_ci 271462306a36Sopenharmony_ci dev_info(hdev->dev, 271562306a36Sopenharmony_ci "Loading %sfirmware to device, may take some time...\n", 271662306a36Sopenharmony_ci hdev->asic_prop.fw_security_enabled ? "secured " : ""); 271762306a36Sopenharmony_ci 271862306a36Sopenharmony_ci /* initialize FW descriptor as invalid */ 271962306a36Sopenharmony_ci fw_loader->dynamic_loader.fw_desc_valid = false; 272062306a36Sopenharmony_ci 272162306a36Sopenharmony_ci /* 272262306a36Sopenharmony_ci * In this stage, "cpu_dyn_regs" contains only LKD's hard coded values! 272362306a36Sopenharmony_ci * It will be updated from FW after hl_fw_dynamic_request_descriptor(). 272462306a36Sopenharmony_ci */ 272562306a36Sopenharmony_ci dyn_regs = &fw_loader->dynamic_loader.comm_desc.cpu_dyn_regs; 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_RST_STATE, 272862306a36Sopenharmony_ci 0, true, 272962306a36Sopenharmony_ci fw_loader->cpu_timeout); 273062306a36Sopenharmony_ci if (rc) 273162306a36Sopenharmony_ci goto protocol_err; 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_ci if (hdev->reset_info.curr_reset_cause) { 273462306a36Sopenharmony_ci rc = hl_fw_dynamic_send_msg(hdev, fw_loader, 273562306a36Sopenharmony_ci HL_COMMS_RESET_CAUSE_TYPE, &hdev->reset_info.curr_reset_cause); 273662306a36Sopenharmony_ci if (rc) 273762306a36Sopenharmony_ci goto protocol_err; 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_ci /* Clear current reset cause */ 274062306a36Sopenharmony_ci hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; 274162306a36Sopenharmony_ci } 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_ci if (!(hdev->fw_components & FW_TYPE_BOOT_CPU)) { 274462306a36Sopenharmony_ci struct lkd_fw_binning_info *binning_info; 274562306a36Sopenharmony_ci 274662306a36Sopenharmony_ci rc = hl_fw_dynamic_request_descriptor(hdev, fw_loader, 0); 274762306a36Sopenharmony_ci if (rc) 274862306a36Sopenharmony_ci goto protocol_err; 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci /* read preboot version */ 275162306a36Sopenharmony_ci rc = hl_fw_dynamic_read_device_fw_version(hdev, FW_COMP_PREBOOT, 275262306a36Sopenharmony_ci fw_loader->dynamic_loader.comm_desc.cur_fw_ver); 275362306a36Sopenharmony_ci 275462306a36Sopenharmony_ci if (rc) 275562306a36Sopenharmony_ci return rc; 275662306a36Sopenharmony_ci 275762306a36Sopenharmony_ci /* read binning info from preboot */ 275862306a36Sopenharmony_ci if (hdev->support_preboot_binning) { 275962306a36Sopenharmony_ci binning_info = &fw_loader->dynamic_loader.comm_desc.binning_info; 276062306a36Sopenharmony_ci hdev->tpc_binning = le64_to_cpu(binning_info->tpc_mask_l); 276162306a36Sopenharmony_ci hdev->dram_binning = le32_to_cpu(binning_info->dram_mask); 276262306a36Sopenharmony_ci hdev->edma_binning = le32_to_cpu(binning_info->edma_mask); 276362306a36Sopenharmony_ci hdev->decoder_binning = le32_to_cpu(binning_info->dec_mask); 276462306a36Sopenharmony_ci hdev->rotator_binning = le32_to_cpu(binning_info->rot_mask); 276562306a36Sopenharmony_ci 276662306a36Sopenharmony_ci rc = hdev->asic_funcs->set_dram_properties(hdev); 276762306a36Sopenharmony_ci if (rc) 276862306a36Sopenharmony_ci return rc; 276962306a36Sopenharmony_ci 277062306a36Sopenharmony_ci rc = hdev->asic_funcs->set_binning_masks(hdev); 277162306a36Sopenharmony_ci if (rc) 277262306a36Sopenharmony_ci return rc; 277362306a36Sopenharmony_ci 277462306a36Sopenharmony_ci dev_dbg(hdev->dev, 277562306a36Sopenharmony_ci "Read binning masks: tpc: 0x%llx, dram: 0x%llx, edma: 0x%x, dec: 0x%x, rot:0x%x\n", 277662306a36Sopenharmony_ci hdev->tpc_binning, hdev->dram_binning, hdev->edma_binning, 277762306a36Sopenharmony_ci hdev->decoder_binning, hdev->rotator_binning); 277862306a36Sopenharmony_ci } 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_ci return 0; 278162306a36Sopenharmony_ci } 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_ci /* load boot fit to FW */ 278462306a36Sopenharmony_ci rc = hl_fw_dynamic_load_image(hdev, fw_loader, FW_COMP_BOOT_FIT, 278562306a36Sopenharmony_ci fw_loader->boot_fit_timeout); 278662306a36Sopenharmony_ci if (rc) { 278762306a36Sopenharmony_ci dev_err(hdev->dev, "failed to load boot fit\n"); 278862306a36Sopenharmony_ci goto protocol_err; 278962306a36Sopenharmony_ci } 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_ci rc = hl_fw_dynamic_wait_for_boot_fit_active(hdev, fw_loader); 279262306a36Sopenharmony_ci if (rc) 279362306a36Sopenharmony_ci goto protocol_err; 279462306a36Sopenharmony_ci 279562306a36Sopenharmony_ci hl_fw_boot_fit_update_state(hdev, 279662306a36Sopenharmony_ci le32_to_cpu(dyn_regs->cpu_boot_dev_sts0), 279762306a36Sopenharmony_ci le32_to_cpu(dyn_regs->cpu_boot_dev_sts1)); 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci /* 280062306a36Sopenharmony_ci * when testing FW load (without Linux) on PLDM we don't want to 280162306a36Sopenharmony_ci * wait until boot fit is active as it may take several hours. 280262306a36Sopenharmony_ci * instead, we load the bootfit and let it do all initialization in 280362306a36Sopenharmony_ci * the background. 280462306a36Sopenharmony_ci */ 280562306a36Sopenharmony_ci if (hdev->pldm && !(hdev->fw_components & FW_TYPE_LINUX)) 280662306a36Sopenharmony_ci return 0; 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci /* Enable DRAM scrambling before Linux boot and after successful 280962306a36Sopenharmony_ci * UBoot 281062306a36Sopenharmony_ci */ 281162306a36Sopenharmony_ci hdev->asic_funcs->init_cpu_scrambler_dram(hdev); 281262306a36Sopenharmony_ci 281362306a36Sopenharmony_ci if (!(hdev->fw_components & FW_TYPE_LINUX)) { 281462306a36Sopenharmony_ci dev_info(hdev->dev, "Skip loading Linux F/W\n"); 281562306a36Sopenharmony_ci return 0; 281662306a36Sopenharmony_ci } 281762306a36Sopenharmony_ci 281862306a36Sopenharmony_ci if (fw_loader->skip_bmc) { 281962306a36Sopenharmony_ci rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, 282062306a36Sopenharmony_ci COMMS_SKIP_BMC, 0, 282162306a36Sopenharmony_ci true, 282262306a36Sopenharmony_ci fw_loader->cpu_timeout); 282362306a36Sopenharmony_ci if (rc) { 282462306a36Sopenharmony_ci dev_err(hdev->dev, "failed to load boot fit\n"); 282562306a36Sopenharmony_ci goto protocol_err; 282662306a36Sopenharmony_ci } 282762306a36Sopenharmony_ci } 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci /* load Linux image to FW */ 283062306a36Sopenharmony_ci rc = hl_fw_dynamic_load_image(hdev, fw_loader, FW_COMP_LINUX, 283162306a36Sopenharmony_ci fw_loader->cpu_timeout); 283262306a36Sopenharmony_ci if (rc) { 283362306a36Sopenharmony_ci dev_err(hdev->dev, "failed to load Linux\n"); 283462306a36Sopenharmony_ci goto protocol_err; 283562306a36Sopenharmony_ci } 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci rc = hl_fw_dynamic_wait_for_linux_active(hdev, fw_loader); 283862306a36Sopenharmony_ci if (rc) 283962306a36Sopenharmony_ci goto protocol_err; 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci hl_fw_linux_update_state(hdev, 284262306a36Sopenharmony_ci le32_to_cpu(dyn_regs->cpu_boot_dev_sts0), 284362306a36Sopenharmony_ci le32_to_cpu(dyn_regs->cpu_boot_dev_sts1)); 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ci hl_fw_dynamic_update_linux_interrupt_if(hdev); 284662306a36Sopenharmony_ci 284762306a36Sopenharmony_ciprotocol_err: 284862306a36Sopenharmony_ci if (fw_loader->dynamic_loader.fw_desc_valid) { 284962306a36Sopenharmony_ci fw_error_rc = fw_read_errors(hdev, le32_to_cpu(dyn_regs->cpu_boot_err0), 285062306a36Sopenharmony_ci le32_to_cpu(dyn_regs->cpu_boot_err1), 285162306a36Sopenharmony_ci le32_to_cpu(dyn_regs->cpu_boot_dev_sts0), 285262306a36Sopenharmony_ci le32_to_cpu(dyn_regs->cpu_boot_dev_sts1)); 285362306a36Sopenharmony_ci 285462306a36Sopenharmony_ci if (fw_error_rc) 285562306a36Sopenharmony_ci return fw_error_rc; 285662306a36Sopenharmony_ci } 285762306a36Sopenharmony_ci 285862306a36Sopenharmony_ci return rc; 285962306a36Sopenharmony_ci} 286062306a36Sopenharmony_ci 286162306a36Sopenharmony_ci/** 286262306a36Sopenharmony_ci * hl_fw_static_init_cpu - initialize the device CPU using static protocol 286362306a36Sopenharmony_ci * 286462306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 286562306a36Sopenharmony_ci * @fw_loader: managing structure for loading device's FW 286662306a36Sopenharmony_ci * 286762306a36Sopenharmony_ci * @return 0 on success, otherwise non-zero error code 286862306a36Sopenharmony_ci */ 286962306a36Sopenharmony_cistatic int hl_fw_static_init_cpu(struct hl_device *hdev, 287062306a36Sopenharmony_ci struct fw_load_mgr *fw_loader) 287162306a36Sopenharmony_ci{ 287262306a36Sopenharmony_ci u32 cpu_msg_status_reg, cpu_timeout, msg_to_cpu_reg, status; 287362306a36Sopenharmony_ci u32 cpu_boot_dev_status0_reg, cpu_boot_dev_status1_reg; 287462306a36Sopenharmony_ci struct static_fw_load_mgr *static_loader; 287562306a36Sopenharmony_ci u32 cpu_boot_status_reg; 287662306a36Sopenharmony_ci int rc; 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci if (!(hdev->fw_components & FW_TYPE_BOOT_CPU)) 287962306a36Sopenharmony_ci return 0; 288062306a36Sopenharmony_ci 288162306a36Sopenharmony_ci /* init common loader parameters */ 288262306a36Sopenharmony_ci cpu_timeout = fw_loader->cpu_timeout; 288362306a36Sopenharmony_ci 288462306a36Sopenharmony_ci /* init static loader parameters */ 288562306a36Sopenharmony_ci static_loader = &fw_loader->static_loader; 288662306a36Sopenharmony_ci cpu_msg_status_reg = static_loader->cpu_cmd_status_to_host_reg; 288762306a36Sopenharmony_ci msg_to_cpu_reg = static_loader->kmd_msg_to_cpu_reg; 288862306a36Sopenharmony_ci cpu_boot_dev_status0_reg = static_loader->cpu_boot_dev_status0_reg; 288962306a36Sopenharmony_ci cpu_boot_dev_status1_reg = static_loader->cpu_boot_dev_status1_reg; 289062306a36Sopenharmony_ci cpu_boot_status_reg = static_loader->cpu_boot_status_reg; 289162306a36Sopenharmony_ci 289262306a36Sopenharmony_ci dev_info(hdev->dev, "Going to wait for device boot (up to %lds)\n", 289362306a36Sopenharmony_ci cpu_timeout / USEC_PER_SEC); 289462306a36Sopenharmony_ci 289562306a36Sopenharmony_ci /* Wait for boot FIT request */ 289662306a36Sopenharmony_ci rc = hl_poll_timeout( 289762306a36Sopenharmony_ci hdev, 289862306a36Sopenharmony_ci cpu_boot_status_reg, 289962306a36Sopenharmony_ci status, 290062306a36Sopenharmony_ci status == CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT, 290162306a36Sopenharmony_ci hdev->fw_poll_interval_usec, 290262306a36Sopenharmony_ci fw_loader->boot_fit_timeout); 290362306a36Sopenharmony_ci 290462306a36Sopenharmony_ci if (rc) { 290562306a36Sopenharmony_ci dev_dbg(hdev->dev, 290662306a36Sopenharmony_ci "No boot fit request received (status = %d), resuming boot\n", status); 290762306a36Sopenharmony_ci } else { 290862306a36Sopenharmony_ci rc = hdev->asic_funcs->load_boot_fit_to_device(hdev); 290962306a36Sopenharmony_ci if (rc) 291062306a36Sopenharmony_ci goto out; 291162306a36Sopenharmony_ci 291262306a36Sopenharmony_ci /* Clear device CPU message status */ 291362306a36Sopenharmony_ci WREG32(cpu_msg_status_reg, CPU_MSG_CLR); 291462306a36Sopenharmony_ci 291562306a36Sopenharmony_ci /* Signal device CPU that boot loader is ready */ 291662306a36Sopenharmony_ci WREG32(msg_to_cpu_reg, KMD_MSG_FIT_RDY); 291762306a36Sopenharmony_ci 291862306a36Sopenharmony_ci /* Poll for CPU device ack */ 291962306a36Sopenharmony_ci rc = hl_poll_timeout( 292062306a36Sopenharmony_ci hdev, 292162306a36Sopenharmony_ci cpu_msg_status_reg, 292262306a36Sopenharmony_ci status, 292362306a36Sopenharmony_ci status == CPU_MSG_OK, 292462306a36Sopenharmony_ci hdev->fw_poll_interval_usec, 292562306a36Sopenharmony_ci fw_loader->boot_fit_timeout); 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_ci if (rc) { 292862306a36Sopenharmony_ci dev_err(hdev->dev, 292962306a36Sopenharmony_ci "Timeout waiting for boot fit load ack (status = %d)\n", status); 293062306a36Sopenharmony_ci goto out; 293162306a36Sopenharmony_ci } 293262306a36Sopenharmony_ci 293362306a36Sopenharmony_ci /* Clear message */ 293462306a36Sopenharmony_ci WREG32(msg_to_cpu_reg, KMD_MSG_NA); 293562306a36Sopenharmony_ci } 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_ci /* 293862306a36Sopenharmony_ci * Make sure CPU boot-loader is running 293962306a36Sopenharmony_ci * Note that the CPU_BOOT_STATUS_SRAM_AVAIL is generally set by Linux 294062306a36Sopenharmony_ci * yet there is a debug scenario in which we loading uboot (without Linux) 294162306a36Sopenharmony_ci * which at later stage is relocated to DRAM. In this case we expect 294262306a36Sopenharmony_ci * uboot to set the CPU_BOOT_STATUS_SRAM_AVAIL and so we add it to the 294362306a36Sopenharmony_ci * poll flags 294462306a36Sopenharmony_ci */ 294562306a36Sopenharmony_ci rc = hl_poll_timeout( 294662306a36Sopenharmony_ci hdev, 294762306a36Sopenharmony_ci cpu_boot_status_reg, 294862306a36Sopenharmony_ci status, 294962306a36Sopenharmony_ci (status == CPU_BOOT_STATUS_DRAM_RDY) || 295062306a36Sopenharmony_ci (status == CPU_BOOT_STATUS_NIC_FW_RDY) || 295162306a36Sopenharmony_ci (status == CPU_BOOT_STATUS_READY_TO_BOOT) || 295262306a36Sopenharmony_ci (status == CPU_BOOT_STATUS_SRAM_AVAIL), 295362306a36Sopenharmony_ci hdev->fw_poll_interval_usec, 295462306a36Sopenharmony_ci cpu_timeout); 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_ci dev_dbg(hdev->dev, "uboot status = %d\n", status); 295762306a36Sopenharmony_ci 295862306a36Sopenharmony_ci /* Read U-Boot version now in case we will later fail */ 295962306a36Sopenharmony_ci hl_fw_static_read_device_fw_version(hdev, FW_COMP_BOOT_FIT); 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci /* update state according to boot stage */ 296262306a36Sopenharmony_ci hl_fw_boot_fit_update_state(hdev, cpu_boot_dev_status0_reg, 296362306a36Sopenharmony_ci cpu_boot_dev_status1_reg); 296462306a36Sopenharmony_ci 296562306a36Sopenharmony_ci if (rc) { 296662306a36Sopenharmony_ci detect_cpu_boot_status(hdev, status); 296762306a36Sopenharmony_ci rc = -EIO; 296862306a36Sopenharmony_ci goto out; 296962306a36Sopenharmony_ci } 297062306a36Sopenharmony_ci 297162306a36Sopenharmony_ci /* Enable DRAM scrambling before Linux boot and after successful 297262306a36Sopenharmony_ci * UBoot 297362306a36Sopenharmony_ci */ 297462306a36Sopenharmony_ci hdev->asic_funcs->init_cpu_scrambler_dram(hdev); 297562306a36Sopenharmony_ci 297662306a36Sopenharmony_ci if (!(hdev->fw_components & FW_TYPE_LINUX)) { 297762306a36Sopenharmony_ci dev_info(hdev->dev, "Skip loading Linux F/W\n"); 297862306a36Sopenharmony_ci rc = 0; 297962306a36Sopenharmony_ci goto out; 298062306a36Sopenharmony_ci } 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_ci if (status == CPU_BOOT_STATUS_SRAM_AVAIL) { 298362306a36Sopenharmony_ci rc = 0; 298462306a36Sopenharmony_ci goto out; 298562306a36Sopenharmony_ci } 298662306a36Sopenharmony_ci 298762306a36Sopenharmony_ci dev_info(hdev->dev, 298862306a36Sopenharmony_ci "Loading firmware to device, may take some time...\n"); 298962306a36Sopenharmony_ci 299062306a36Sopenharmony_ci rc = hdev->asic_funcs->load_firmware_to_device(hdev); 299162306a36Sopenharmony_ci if (rc) 299262306a36Sopenharmony_ci goto out; 299362306a36Sopenharmony_ci 299462306a36Sopenharmony_ci if (fw_loader->skip_bmc) { 299562306a36Sopenharmony_ci WREG32(msg_to_cpu_reg, KMD_MSG_SKIP_BMC); 299662306a36Sopenharmony_ci 299762306a36Sopenharmony_ci rc = hl_poll_timeout( 299862306a36Sopenharmony_ci hdev, 299962306a36Sopenharmony_ci cpu_boot_status_reg, 300062306a36Sopenharmony_ci status, 300162306a36Sopenharmony_ci (status == CPU_BOOT_STATUS_BMC_WAITING_SKIPPED), 300262306a36Sopenharmony_ci hdev->fw_poll_interval_usec, 300362306a36Sopenharmony_ci cpu_timeout); 300462306a36Sopenharmony_ci 300562306a36Sopenharmony_ci if (rc) { 300662306a36Sopenharmony_ci dev_err(hdev->dev, 300762306a36Sopenharmony_ci "Failed to get ACK on skipping BMC (status = %d)\n", 300862306a36Sopenharmony_ci status); 300962306a36Sopenharmony_ci WREG32(msg_to_cpu_reg, KMD_MSG_NA); 301062306a36Sopenharmony_ci rc = -EIO; 301162306a36Sopenharmony_ci goto out; 301262306a36Sopenharmony_ci } 301362306a36Sopenharmony_ci } 301462306a36Sopenharmony_ci 301562306a36Sopenharmony_ci WREG32(msg_to_cpu_reg, KMD_MSG_FIT_RDY); 301662306a36Sopenharmony_ci 301762306a36Sopenharmony_ci rc = hl_poll_timeout( 301862306a36Sopenharmony_ci hdev, 301962306a36Sopenharmony_ci cpu_boot_status_reg, 302062306a36Sopenharmony_ci status, 302162306a36Sopenharmony_ci (status == CPU_BOOT_STATUS_SRAM_AVAIL), 302262306a36Sopenharmony_ci hdev->fw_poll_interval_usec, 302362306a36Sopenharmony_ci cpu_timeout); 302462306a36Sopenharmony_ci 302562306a36Sopenharmony_ci /* Clear message */ 302662306a36Sopenharmony_ci WREG32(msg_to_cpu_reg, KMD_MSG_NA); 302762306a36Sopenharmony_ci 302862306a36Sopenharmony_ci if (rc) { 302962306a36Sopenharmony_ci if (status == CPU_BOOT_STATUS_FIT_CORRUPTED) 303062306a36Sopenharmony_ci dev_err(hdev->dev, 303162306a36Sopenharmony_ci "Device reports FIT image is corrupted\n"); 303262306a36Sopenharmony_ci else 303362306a36Sopenharmony_ci dev_err(hdev->dev, 303462306a36Sopenharmony_ci "Failed to load firmware to device (status = %d)\n", 303562306a36Sopenharmony_ci status); 303662306a36Sopenharmony_ci 303762306a36Sopenharmony_ci rc = -EIO; 303862306a36Sopenharmony_ci goto out; 303962306a36Sopenharmony_ci } 304062306a36Sopenharmony_ci 304162306a36Sopenharmony_ci rc = fw_read_errors(hdev, fw_loader->static_loader.boot_err0_reg, 304262306a36Sopenharmony_ci fw_loader->static_loader.boot_err1_reg, 304362306a36Sopenharmony_ci cpu_boot_dev_status0_reg, 304462306a36Sopenharmony_ci cpu_boot_dev_status1_reg); 304562306a36Sopenharmony_ci if (rc) 304662306a36Sopenharmony_ci return rc; 304762306a36Sopenharmony_ci 304862306a36Sopenharmony_ci hl_fw_linux_update_state(hdev, cpu_boot_dev_status0_reg, 304962306a36Sopenharmony_ci cpu_boot_dev_status1_reg); 305062306a36Sopenharmony_ci 305162306a36Sopenharmony_ci return 0; 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_ciout: 305462306a36Sopenharmony_ci fw_read_errors(hdev, fw_loader->static_loader.boot_err0_reg, 305562306a36Sopenharmony_ci fw_loader->static_loader.boot_err1_reg, 305662306a36Sopenharmony_ci cpu_boot_dev_status0_reg, 305762306a36Sopenharmony_ci cpu_boot_dev_status1_reg); 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_ci return rc; 306062306a36Sopenharmony_ci} 306162306a36Sopenharmony_ci 306262306a36Sopenharmony_ci/** 306362306a36Sopenharmony_ci * hl_fw_init_cpu - initialize the device CPU 306462306a36Sopenharmony_ci * 306562306a36Sopenharmony_ci * @hdev: pointer to the habanalabs device structure 306662306a36Sopenharmony_ci * 306762306a36Sopenharmony_ci * @return 0 on success, otherwise non-zero error code 306862306a36Sopenharmony_ci * 306962306a36Sopenharmony_ci * perform necessary initializations for device's CPU. takes into account if 307062306a36Sopenharmony_ci * init protocol is static or dynamic. 307162306a36Sopenharmony_ci */ 307262306a36Sopenharmony_ciint hl_fw_init_cpu(struct hl_device *hdev) 307362306a36Sopenharmony_ci{ 307462306a36Sopenharmony_ci struct asic_fixed_properties *prop = &hdev->asic_prop; 307562306a36Sopenharmony_ci struct fw_load_mgr *fw_loader = &hdev->fw_loader; 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ci return prop->dynamic_fw_load ? 307862306a36Sopenharmony_ci hl_fw_dynamic_init_cpu(hdev, fw_loader) : 307962306a36Sopenharmony_ci hl_fw_static_init_cpu(hdev, fw_loader); 308062306a36Sopenharmony_ci} 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_civoid hl_fw_set_pll_profile(struct hl_device *hdev) 308362306a36Sopenharmony_ci{ 308462306a36Sopenharmony_ci hl_fw_set_frequency(hdev, hdev->asic_prop.clk_pll_index, 308562306a36Sopenharmony_ci hdev->asic_prop.max_freq_value); 308662306a36Sopenharmony_ci} 308762306a36Sopenharmony_ci 308862306a36Sopenharmony_ciint hl_fw_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk) 308962306a36Sopenharmony_ci{ 309062306a36Sopenharmony_ci long value; 309162306a36Sopenharmony_ci 309262306a36Sopenharmony_ci if (!hl_device_operational(hdev, NULL)) 309362306a36Sopenharmony_ci return -ENODEV; 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_ci if (!hdev->pdev) { 309662306a36Sopenharmony_ci *cur_clk = 0; 309762306a36Sopenharmony_ci *max_clk = 0; 309862306a36Sopenharmony_ci return 0; 309962306a36Sopenharmony_ci } 310062306a36Sopenharmony_ci 310162306a36Sopenharmony_ci value = hl_fw_get_frequency(hdev, hdev->asic_prop.clk_pll_index, false); 310262306a36Sopenharmony_ci 310362306a36Sopenharmony_ci if (value < 0) { 310462306a36Sopenharmony_ci dev_err(hdev->dev, "Failed to retrieve device max clock %ld\n", value); 310562306a36Sopenharmony_ci return value; 310662306a36Sopenharmony_ci } 310762306a36Sopenharmony_ci 310862306a36Sopenharmony_ci *max_clk = (value / 1000 / 1000); 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci value = hl_fw_get_frequency(hdev, hdev->asic_prop.clk_pll_index, true); 311162306a36Sopenharmony_ci 311262306a36Sopenharmony_ci if (value < 0) { 311362306a36Sopenharmony_ci dev_err(hdev->dev, "Failed to retrieve device current clock %ld\n", value); 311462306a36Sopenharmony_ci return value; 311562306a36Sopenharmony_ci } 311662306a36Sopenharmony_ci 311762306a36Sopenharmony_ci *cur_clk = (value / 1000 / 1000); 311862306a36Sopenharmony_ci 311962306a36Sopenharmony_ci return 0; 312062306a36Sopenharmony_ci} 312162306a36Sopenharmony_ci 312262306a36Sopenharmony_cilong hl_fw_get_frequency(struct hl_device *hdev, u32 pll_index, bool curr) 312362306a36Sopenharmony_ci{ 312462306a36Sopenharmony_ci struct cpucp_packet pkt; 312562306a36Sopenharmony_ci u32 used_pll_idx; 312662306a36Sopenharmony_ci u64 result; 312762306a36Sopenharmony_ci int rc; 312862306a36Sopenharmony_ci 312962306a36Sopenharmony_ci rc = get_used_pll_index(hdev, pll_index, &used_pll_idx); 313062306a36Sopenharmony_ci if (rc) 313162306a36Sopenharmony_ci return rc; 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_ci if (curr) 313662306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_CURR_GET << 313762306a36Sopenharmony_ci CPUCP_PKT_CTL_OPCODE_SHIFT); 313862306a36Sopenharmony_ci else 313962306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_GET << CPUCP_PKT_CTL_OPCODE_SHIFT); 314062306a36Sopenharmony_ci 314162306a36Sopenharmony_ci pkt.pll_index = cpu_to_le32((u32)used_pll_idx); 314262306a36Sopenharmony_ci 314362306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, &result); 314462306a36Sopenharmony_ci 314562306a36Sopenharmony_ci if (rc) { 314662306a36Sopenharmony_ci dev_err(hdev->dev, "Failed to get frequency of PLL %d, error %d\n", 314762306a36Sopenharmony_ci used_pll_idx, rc); 314862306a36Sopenharmony_ci return rc; 314962306a36Sopenharmony_ci } 315062306a36Sopenharmony_ci 315162306a36Sopenharmony_ci return (long) result; 315262306a36Sopenharmony_ci} 315362306a36Sopenharmony_ci 315462306a36Sopenharmony_civoid hl_fw_set_frequency(struct hl_device *hdev, u32 pll_index, u64 freq) 315562306a36Sopenharmony_ci{ 315662306a36Sopenharmony_ci struct cpucp_packet pkt; 315762306a36Sopenharmony_ci u32 used_pll_idx; 315862306a36Sopenharmony_ci int rc; 315962306a36Sopenharmony_ci 316062306a36Sopenharmony_ci rc = get_used_pll_index(hdev, pll_index, &used_pll_idx); 316162306a36Sopenharmony_ci if (rc) 316262306a36Sopenharmony_ci return; 316362306a36Sopenharmony_ci 316462306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 316562306a36Sopenharmony_ci 316662306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_SET << CPUCP_PKT_CTL_OPCODE_SHIFT); 316762306a36Sopenharmony_ci pkt.pll_index = cpu_to_le32((u32)used_pll_idx); 316862306a36Sopenharmony_ci pkt.value = cpu_to_le64(freq); 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL); 317162306a36Sopenharmony_ci 317262306a36Sopenharmony_ci if (rc) 317362306a36Sopenharmony_ci dev_err(hdev->dev, "Failed to set frequency to PLL %d, error %d\n", 317462306a36Sopenharmony_ci used_pll_idx, rc); 317562306a36Sopenharmony_ci} 317662306a36Sopenharmony_ci 317762306a36Sopenharmony_cilong hl_fw_get_max_power(struct hl_device *hdev) 317862306a36Sopenharmony_ci{ 317962306a36Sopenharmony_ci struct cpucp_packet pkt; 318062306a36Sopenharmony_ci u64 result; 318162306a36Sopenharmony_ci int rc; 318262306a36Sopenharmony_ci 318362306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_MAX_POWER_GET << CPUCP_PKT_CTL_OPCODE_SHIFT); 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, &result); 318862306a36Sopenharmony_ci 318962306a36Sopenharmony_ci if (rc) { 319062306a36Sopenharmony_ci dev_err(hdev->dev, "Failed to get max power, error %d\n", rc); 319162306a36Sopenharmony_ci return rc; 319262306a36Sopenharmony_ci } 319362306a36Sopenharmony_ci 319462306a36Sopenharmony_ci return result; 319562306a36Sopenharmony_ci} 319662306a36Sopenharmony_ci 319762306a36Sopenharmony_civoid hl_fw_set_max_power(struct hl_device *hdev) 319862306a36Sopenharmony_ci{ 319962306a36Sopenharmony_ci struct cpucp_packet pkt; 320062306a36Sopenharmony_ci int rc; 320162306a36Sopenharmony_ci 320262306a36Sopenharmony_ci /* TODO: remove this after simulator supports this packet */ 320362306a36Sopenharmony_ci if (!hdev->pdev) 320462306a36Sopenharmony_ci return; 320562306a36Sopenharmony_ci 320662306a36Sopenharmony_ci memset(&pkt, 0, sizeof(pkt)); 320762306a36Sopenharmony_ci 320862306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_MAX_POWER_SET << CPUCP_PKT_CTL_OPCODE_SHIFT); 320962306a36Sopenharmony_ci pkt.value = cpu_to_le64(hdev->max_power); 321062306a36Sopenharmony_ci 321162306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL); 321262306a36Sopenharmony_ci 321362306a36Sopenharmony_ci if (rc) 321462306a36Sopenharmony_ci dev_err(hdev->dev, "Failed to set max power, error %d\n", rc); 321562306a36Sopenharmony_ci} 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_cistatic int hl_fw_get_sec_attest_data(struct hl_device *hdev, u32 packet_id, void *data, u32 size, 321862306a36Sopenharmony_ci u32 nonce, u32 timeout) 321962306a36Sopenharmony_ci{ 322062306a36Sopenharmony_ci struct cpucp_packet pkt = {}; 322162306a36Sopenharmony_ci dma_addr_t req_dma_addr; 322262306a36Sopenharmony_ci void *req_cpu_addr; 322362306a36Sopenharmony_ci int rc; 322462306a36Sopenharmony_ci 322562306a36Sopenharmony_ci req_cpu_addr = hl_cpu_accessible_dma_pool_alloc(hdev, size, &req_dma_addr); 322662306a36Sopenharmony_ci if (!req_cpu_addr) { 322762306a36Sopenharmony_ci dev_err(hdev->dev, 322862306a36Sopenharmony_ci "Failed to allocate DMA memory for CPU-CP packet %u\n", packet_id); 322962306a36Sopenharmony_ci return -ENOMEM; 323062306a36Sopenharmony_ci } 323162306a36Sopenharmony_ci 323262306a36Sopenharmony_ci memset(data, 0, size); 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(packet_id << CPUCP_PKT_CTL_OPCODE_SHIFT); 323562306a36Sopenharmony_ci pkt.addr = cpu_to_le64(req_dma_addr); 323662306a36Sopenharmony_ci pkt.data_max_size = cpu_to_le32(size); 323762306a36Sopenharmony_ci pkt.nonce = cpu_to_le32(nonce); 323862306a36Sopenharmony_ci 323962306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 324062306a36Sopenharmony_ci timeout, NULL); 324162306a36Sopenharmony_ci if (rc) { 324262306a36Sopenharmony_ci dev_err(hdev->dev, 324362306a36Sopenharmony_ci "Failed to handle CPU-CP pkt %u, error %d\n", packet_id, rc); 324462306a36Sopenharmony_ci goto out; 324562306a36Sopenharmony_ci } 324662306a36Sopenharmony_ci 324762306a36Sopenharmony_ci memcpy(data, req_cpu_addr, size); 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_ciout: 325062306a36Sopenharmony_ci hl_cpu_accessible_dma_pool_free(hdev, size, req_cpu_addr); 325162306a36Sopenharmony_ci 325262306a36Sopenharmony_ci return rc; 325362306a36Sopenharmony_ci} 325462306a36Sopenharmony_ci 325562306a36Sopenharmony_ciint hl_fw_get_sec_attest_info(struct hl_device *hdev, struct cpucp_sec_attest_info *sec_attest_info, 325662306a36Sopenharmony_ci u32 nonce) 325762306a36Sopenharmony_ci{ 325862306a36Sopenharmony_ci return hl_fw_get_sec_attest_data(hdev, CPUCP_PACKET_SEC_ATTEST_GET, sec_attest_info, 325962306a36Sopenharmony_ci sizeof(struct cpucp_sec_attest_info), nonce, 326062306a36Sopenharmony_ci HL_CPUCP_SEC_ATTEST_INFO_TINEOUT_USEC); 326162306a36Sopenharmony_ci} 326262306a36Sopenharmony_ci 326362306a36Sopenharmony_ciint hl_fw_send_generic_request(struct hl_device *hdev, enum hl_passthrough_type sub_opcode, 326462306a36Sopenharmony_ci dma_addr_t buff, u32 *size) 326562306a36Sopenharmony_ci{ 326662306a36Sopenharmony_ci struct cpucp_packet pkt = {}; 326762306a36Sopenharmony_ci u64 result; 326862306a36Sopenharmony_ci int rc = 0; 326962306a36Sopenharmony_ci 327062306a36Sopenharmony_ci pkt.ctl = cpu_to_le32(CPUCP_PACKET_GENERIC_PASSTHROUGH << CPUCP_PKT_CTL_OPCODE_SHIFT); 327162306a36Sopenharmony_ci pkt.addr = cpu_to_le64(buff); 327262306a36Sopenharmony_ci pkt.data_max_size = cpu_to_le32(*size); 327362306a36Sopenharmony_ci pkt.pkt_subidx = cpu_to_le32(sub_opcode); 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_ci rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *)&pkt, sizeof(pkt), 327662306a36Sopenharmony_ci HL_CPUCP_INFO_TIMEOUT_USEC, &result); 327762306a36Sopenharmony_ci if (rc) 327862306a36Sopenharmony_ci dev_err(hdev->dev, "failed to send CPUCP data of generic fw pkt\n"); 327962306a36Sopenharmony_ci else 328062306a36Sopenharmony_ci dev_dbg(hdev->dev, "generic pkt was successful, result: 0x%llx\n", result); 328162306a36Sopenharmony_ci 328262306a36Sopenharmony_ci *size = (u32)result; 328362306a36Sopenharmony_ci 328462306a36Sopenharmony_ci return rc; 328562306a36Sopenharmony_ci} 3286