162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci * Copyright 2016-2021 HabanaLabs, Ltd. 562306a36Sopenharmony_ci * All Rights Reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define pr_fmt(fmt) "habanalabs: " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "habanalabs.h" 1262306a36Sopenharmony_ci#include "../include/hw_ip/pci/pci_general.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/pci.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/vmalloc.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define CREATE_TRACE_POINTS 1962306a36Sopenharmony_ci#include <trace/events/habanalabs.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define HL_DRIVER_AUTHOR "HabanaLabs Kernel Driver Team" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define HL_DRIVER_DESC "Driver for HabanaLabs's AI Accelerators" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciMODULE_AUTHOR(HL_DRIVER_AUTHOR); 2662306a36Sopenharmony_ciMODULE_DESCRIPTION(HL_DRIVER_DESC); 2762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic int hl_major; 3062306a36Sopenharmony_cistatic struct class *hl_class; 3162306a36Sopenharmony_cistatic DEFINE_IDR(hl_devs_idr); 3262306a36Sopenharmony_cistatic DEFINE_MUTEX(hl_devs_idr_lock); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define HL_DEFAULT_TIMEOUT_LOCKED 30 /* 30 seconds */ 3562306a36Sopenharmony_ci#define GAUDI_DEFAULT_TIMEOUT_LOCKED 600 /* 10 minutes */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int timeout_locked = HL_DEFAULT_TIMEOUT_LOCKED; 3862306a36Sopenharmony_cistatic int reset_on_lockup = 1; 3962306a36Sopenharmony_cistatic int memory_scrub; 4062306a36Sopenharmony_cistatic ulong boot_error_status_mask = ULONG_MAX; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cimodule_param(timeout_locked, int, 0444); 4362306a36Sopenharmony_ciMODULE_PARM_DESC(timeout_locked, 4462306a36Sopenharmony_ci "Device lockup timeout in seconds (0 = disabled, default 30s)"); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cimodule_param(reset_on_lockup, int, 0444); 4762306a36Sopenharmony_ciMODULE_PARM_DESC(reset_on_lockup, 4862306a36Sopenharmony_ci "Do device reset on lockup (0 = no, 1 = yes, default yes)"); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cimodule_param(memory_scrub, int, 0444); 5162306a36Sopenharmony_ciMODULE_PARM_DESC(memory_scrub, 5262306a36Sopenharmony_ci "Scrub device memory in various states (0 = no, 1 = yes, default no)"); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cimodule_param(boot_error_status_mask, ulong, 0444); 5562306a36Sopenharmony_ciMODULE_PARM_DESC(boot_error_status_mask, 5662306a36Sopenharmony_ci "Mask of the error status during device CPU boot (If bitX is cleared then error X is masked. Default all 1's)"); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define PCI_IDS_GOYA 0x0001 5962306a36Sopenharmony_ci#define PCI_IDS_GAUDI 0x1000 6062306a36Sopenharmony_ci#define PCI_IDS_GAUDI_SEC 0x1010 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define PCI_IDS_GAUDI2 0x1020 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic const struct pci_device_id ids[] = { 6562306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GOYA), }, 6662306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GAUDI), }, 6762306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GAUDI_SEC), }, 6862306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GAUDI2), }, 6962306a36Sopenharmony_ci { 0, } 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ids); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* 7462306a36Sopenharmony_ci * get_asic_type - translate device id to asic type 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * @hdev: pointer to habanalabs device structure. 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * Translate device id and revision id to asic type. 7962306a36Sopenharmony_ci * In case of unidentified device, return -1 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_cistatic enum hl_asic_type get_asic_type(struct hl_device *hdev) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct pci_dev *pdev = hdev->pdev; 8462306a36Sopenharmony_ci enum hl_asic_type asic_type = ASIC_INVALID; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci switch (pdev->device) { 8762306a36Sopenharmony_ci case PCI_IDS_GOYA: 8862306a36Sopenharmony_ci asic_type = ASIC_GOYA; 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci case PCI_IDS_GAUDI: 9162306a36Sopenharmony_ci asic_type = ASIC_GAUDI; 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci case PCI_IDS_GAUDI_SEC: 9462306a36Sopenharmony_ci asic_type = ASIC_GAUDI_SEC; 9562306a36Sopenharmony_ci break; 9662306a36Sopenharmony_ci case PCI_IDS_GAUDI2: 9762306a36Sopenharmony_ci switch (pdev->revision) { 9862306a36Sopenharmony_ci case REV_ID_A: 9962306a36Sopenharmony_ci asic_type = ASIC_GAUDI2; 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci case REV_ID_B: 10262306a36Sopenharmony_ci asic_type = ASIC_GAUDI2B; 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci case REV_ID_C: 10562306a36Sopenharmony_ci asic_type = ASIC_GAUDI2C; 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci default: 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci default: 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return asic_type; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic bool is_asic_secured(enum hl_asic_type asic_type) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci switch (asic_type) { 12162306a36Sopenharmony_ci case ASIC_GAUDI_SEC: 12262306a36Sopenharmony_ci return true; 12362306a36Sopenharmony_ci default: 12462306a36Sopenharmony_ci return false; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* 12962306a36Sopenharmony_ci * hl_device_open - open function for habanalabs device 13062306a36Sopenharmony_ci * 13162306a36Sopenharmony_ci * @inode: pointer to inode structure 13262306a36Sopenharmony_ci * @filp: pointer to file structure 13362306a36Sopenharmony_ci * 13462306a36Sopenharmony_ci * Called when process opens an habanalabs device. 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ciint hl_device_open(struct inode *inode, struct file *filp) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci enum hl_device_status status; 13962306a36Sopenharmony_ci struct hl_device *hdev; 14062306a36Sopenharmony_ci struct hl_fpriv *hpriv; 14162306a36Sopenharmony_ci int rc; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci mutex_lock(&hl_devs_idr_lock); 14462306a36Sopenharmony_ci hdev = idr_find(&hl_devs_idr, iminor(inode)); 14562306a36Sopenharmony_ci mutex_unlock(&hl_devs_idr_lock); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (!hdev) { 14862306a36Sopenharmony_ci pr_err("Couldn't find device %d:%d\n", 14962306a36Sopenharmony_ci imajor(inode), iminor(inode)); 15062306a36Sopenharmony_ci return -ENXIO; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci hpriv = kzalloc(sizeof(*hpriv), GFP_KERNEL); 15462306a36Sopenharmony_ci if (!hpriv) 15562306a36Sopenharmony_ci return -ENOMEM; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci hpriv->hdev = hdev; 15862306a36Sopenharmony_ci filp->private_data = hpriv; 15962306a36Sopenharmony_ci hpriv->filp = filp; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci mutex_init(&hpriv->notifier_event.lock); 16262306a36Sopenharmony_ci mutex_init(&hpriv->restore_phase_mutex); 16362306a36Sopenharmony_ci mutex_init(&hpriv->ctx_lock); 16462306a36Sopenharmony_ci kref_init(&hpriv->refcount); 16562306a36Sopenharmony_ci nonseekable_open(inode, filp); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci hl_ctx_mgr_init(&hpriv->ctx_mgr); 16862306a36Sopenharmony_ci hl_mem_mgr_init(hpriv->hdev->dev, &hpriv->mem_mgr); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci hpriv->taskpid = get_task_pid(current, PIDTYPE_PID); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci mutex_lock(&hdev->fpriv_list_lock); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (!hl_device_operational(hdev, &status)) { 17562306a36Sopenharmony_ci dev_dbg_ratelimited(hdev->dev, 17662306a36Sopenharmony_ci "Can't open %s because it is %s\n", 17762306a36Sopenharmony_ci dev_name(hdev->dev), hdev->status[status]); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (status == HL_DEVICE_STATUS_IN_RESET || 18062306a36Sopenharmony_ci status == HL_DEVICE_STATUS_IN_RESET_AFTER_DEVICE_RELEASE) 18162306a36Sopenharmony_ci rc = -EAGAIN; 18262306a36Sopenharmony_ci else 18362306a36Sopenharmony_ci rc = -EPERM; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci goto out_err; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (hdev->is_in_dram_scrub) { 18962306a36Sopenharmony_ci dev_dbg_ratelimited(hdev->dev, 19062306a36Sopenharmony_ci "Can't open %s during dram scrub\n", 19162306a36Sopenharmony_ci dev_name(hdev->dev)); 19262306a36Sopenharmony_ci rc = -EAGAIN; 19362306a36Sopenharmony_ci goto out_err; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (hdev->compute_ctx_in_release) { 19762306a36Sopenharmony_ci dev_dbg_ratelimited(hdev->dev, 19862306a36Sopenharmony_ci "Can't open %s because another user is still releasing it\n", 19962306a36Sopenharmony_ci dev_name(hdev->dev)); 20062306a36Sopenharmony_ci rc = -EAGAIN; 20162306a36Sopenharmony_ci goto out_err; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (hdev->is_compute_ctx_active) { 20562306a36Sopenharmony_ci dev_dbg_ratelimited(hdev->dev, 20662306a36Sopenharmony_ci "Can't open %s because another user is working on it\n", 20762306a36Sopenharmony_ci dev_name(hdev->dev)); 20862306a36Sopenharmony_ci rc = -EBUSY; 20962306a36Sopenharmony_ci goto out_err; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci rc = hl_ctx_create(hdev, hpriv); 21362306a36Sopenharmony_ci if (rc) { 21462306a36Sopenharmony_ci dev_err(hdev->dev, "Failed to create context %d\n", rc); 21562306a36Sopenharmony_ci goto out_err; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci list_add(&hpriv->dev_node, &hdev->fpriv_list); 21962306a36Sopenharmony_ci mutex_unlock(&hdev->fpriv_list_lock); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci hdev->asic_funcs->send_device_activity(hdev, true); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci hl_debugfs_add_file(hpriv); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci hl_enable_err_info_capture(&hdev->captured_err_info); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci hdev->open_counter++; 22862306a36Sopenharmony_ci hdev->last_successful_open_jif = jiffies; 22962306a36Sopenharmony_ci hdev->last_successful_open_ktime = ktime_get(); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ciout_err: 23462306a36Sopenharmony_ci mutex_unlock(&hdev->fpriv_list_lock); 23562306a36Sopenharmony_ci hl_mem_mgr_fini(&hpriv->mem_mgr); 23662306a36Sopenharmony_ci hl_mem_mgr_idr_destroy(&hpriv->mem_mgr); 23762306a36Sopenharmony_ci hl_ctx_mgr_fini(hpriv->hdev, &hpriv->ctx_mgr); 23862306a36Sopenharmony_ci filp->private_data = NULL; 23962306a36Sopenharmony_ci mutex_destroy(&hpriv->ctx_lock); 24062306a36Sopenharmony_ci mutex_destroy(&hpriv->restore_phase_mutex); 24162306a36Sopenharmony_ci mutex_destroy(&hpriv->notifier_event.lock); 24262306a36Sopenharmony_ci put_pid(hpriv->taskpid); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci kfree(hpriv); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return rc; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ciint hl_device_open_ctrl(struct inode *inode, struct file *filp) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct hl_device *hdev; 25262306a36Sopenharmony_ci struct hl_fpriv *hpriv; 25362306a36Sopenharmony_ci int rc; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci mutex_lock(&hl_devs_idr_lock); 25662306a36Sopenharmony_ci hdev = idr_find(&hl_devs_idr, iminor(inode)); 25762306a36Sopenharmony_ci mutex_unlock(&hl_devs_idr_lock); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (!hdev) { 26062306a36Sopenharmony_ci pr_err("Couldn't find device %d:%d\n", 26162306a36Sopenharmony_ci imajor(inode), iminor(inode)); 26262306a36Sopenharmony_ci return -ENXIO; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci hpriv = kzalloc(sizeof(*hpriv), GFP_KERNEL); 26662306a36Sopenharmony_ci if (!hpriv) 26762306a36Sopenharmony_ci return -ENOMEM; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* Prevent other routines from reading partial hpriv data by 27062306a36Sopenharmony_ci * initializing hpriv fields before inserting it to the list 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci hpriv->hdev = hdev; 27362306a36Sopenharmony_ci filp->private_data = hpriv; 27462306a36Sopenharmony_ci hpriv->filp = filp; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci mutex_init(&hpriv->notifier_event.lock); 27762306a36Sopenharmony_ci nonseekable_open(inode, filp); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci hpriv->taskpid = get_task_pid(current, PIDTYPE_PID); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci mutex_lock(&hdev->fpriv_ctrl_list_lock); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (!hl_ctrl_device_operational(hdev, NULL)) { 28462306a36Sopenharmony_ci dev_dbg_ratelimited(hdev->dev_ctrl, 28562306a36Sopenharmony_ci "Can't open %s because it is disabled\n", 28662306a36Sopenharmony_ci dev_name(hdev->dev_ctrl)); 28762306a36Sopenharmony_ci rc = -EPERM; 28862306a36Sopenharmony_ci goto out_err; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci list_add(&hpriv->dev_node, &hdev->fpriv_ctrl_list); 29262306a36Sopenharmony_ci mutex_unlock(&hdev->fpriv_ctrl_list_lock); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ciout_err: 29762306a36Sopenharmony_ci mutex_unlock(&hdev->fpriv_ctrl_list_lock); 29862306a36Sopenharmony_ci filp->private_data = NULL; 29962306a36Sopenharmony_ci put_pid(hpriv->taskpid); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci kfree(hpriv); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return rc; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic void set_driver_behavior_per_device(struct hl_device *hdev) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci hdev->nic_ports_mask = 0; 30962306a36Sopenharmony_ci hdev->fw_components = FW_TYPE_ALL_TYPES; 31062306a36Sopenharmony_ci hdev->cpu_queues_enable = 1; 31162306a36Sopenharmony_ci hdev->pldm = 0; 31262306a36Sopenharmony_ci hdev->hard_reset_on_fw_events = 1; 31362306a36Sopenharmony_ci hdev->bmc_enable = 1; 31462306a36Sopenharmony_ci hdev->reset_on_preboot_fail = 1; 31562306a36Sopenharmony_ci hdev->heartbeat = 1; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic void copy_kernel_module_params_to_device(struct hl_device *hdev) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci hdev->asic_prop.fw_security_enabled = is_asic_secured(hdev->asic_type); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci hdev->major = hl_major; 32362306a36Sopenharmony_ci hdev->hclass = hl_class; 32462306a36Sopenharmony_ci hdev->memory_scrub = memory_scrub; 32562306a36Sopenharmony_ci hdev->reset_on_lockup = reset_on_lockup; 32662306a36Sopenharmony_ci hdev->boot_error_status_mask = boot_error_status_mask; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic void fixup_device_params_per_asic(struct hl_device *hdev, int timeout) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci switch (hdev->asic_type) { 33262306a36Sopenharmony_ci case ASIC_GAUDI: 33362306a36Sopenharmony_ci case ASIC_GAUDI_SEC: 33462306a36Sopenharmony_ci /* If user didn't request a different timeout than the default one, we have 33562306a36Sopenharmony_ci * a different default timeout for Gaudi 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci if (timeout == HL_DEFAULT_TIMEOUT_LOCKED) 33862306a36Sopenharmony_ci hdev->timeout_jiffies = msecs_to_jiffies(GAUDI_DEFAULT_TIMEOUT_LOCKED * 33962306a36Sopenharmony_ci MSEC_PER_SEC); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci hdev->reset_upon_device_release = 0; 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci case ASIC_GOYA: 34562306a36Sopenharmony_ci hdev->reset_upon_device_release = 0; 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci default: 34962306a36Sopenharmony_ci hdev->reset_upon_device_release = 1; 35062306a36Sopenharmony_ci break; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int fixup_device_params(struct hl_device *hdev) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci int tmp_timeout; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci tmp_timeout = timeout_locked; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci hdev->fw_poll_interval_usec = HL_FW_STATUS_POLL_INTERVAL_USEC; 36162306a36Sopenharmony_ci hdev->fw_comms_poll_interval_usec = HL_FW_STATUS_POLL_INTERVAL_USEC; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (tmp_timeout) 36462306a36Sopenharmony_ci hdev->timeout_jiffies = msecs_to_jiffies(tmp_timeout * MSEC_PER_SEC); 36562306a36Sopenharmony_ci else 36662306a36Sopenharmony_ci hdev->timeout_jiffies = MAX_SCHEDULE_TIMEOUT; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci hdev->stop_on_err = true; 36962306a36Sopenharmony_ci hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; 37062306a36Sopenharmony_ci hdev->reset_info.prev_reset_trigger = HL_RESET_TRIGGER_DEFAULT; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* Enable only after the initialization of the device */ 37362306a36Sopenharmony_ci hdev->disabled = true; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (!(hdev->fw_components & FW_TYPE_PREBOOT_CPU) && 37662306a36Sopenharmony_ci (hdev->fw_components & ~FW_TYPE_PREBOOT_CPU)) { 37762306a36Sopenharmony_ci pr_err("Preboot must be set along with other components"); 37862306a36Sopenharmony_ci return -EINVAL; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* If CPU queues not enabled, no way to do heartbeat */ 38262306a36Sopenharmony_ci if (!hdev->cpu_queues_enable) 38362306a36Sopenharmony_ci hdev->heartbeat = 0; 38462306a36Sopenharmony_ci fixup_device_params_per_asic(hdev, tmp_timeout); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/** 39062306a36Sopenharmony_ci * create_hdev - create habanalabs device instance 39162306a36Sopenharmony_ci * 39262306a36Sopenharmony_ci * @dev: will hold the pointer to the new habanalabs device structure 39362306a36Sopenharmony_ci * @pdev: pointer to the pci device 39462306a36Sopenharmony_ci * 39562306a36Sopenharmony_ci * Allocate memory for habanalabs device and initialize basic fields 39662306a36Sopenharmony_ci * Identify the ASIC type 39762306a36Sopenharmony_ci * Allocate ID (minor) for the device (only for real devices) 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_cistatic int create_hdev(struct hl_device **dev, struct pci_dev *pdev) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci int main_id, ctrl_id = 0, rc = 0; 40262306a36Sopenharmony_ci struct hl_device *hdev; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci *dev = NULL; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); 40762306a36Sopenharmony_ci if (!hdev) 40862306a36Sopenharmony_ci return -ENOMEM; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* Will be NULL in case of simulator device */ 41162306a36Sopenharmony_ci hdev->pdev = pdev; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* Assign status description string */ 41462306a36Sopenharmony_ci strncpy(hdev->status[HL_DEVICE_STATUS_OPERATIONAL], "operational", HL_STR_MAX); 41562306a36Sopenharmony_ci strncpy(hdev->status[HL_DEVICE_STATUS_IN_RESET], "in reset", HL_STR_MAX); 41662306a36Sopenharmony_ci strncpy(hdev->status[HL_DEVICE_STATUS_MALFUNCTION], "disabled", HL_STR_MAX); 41762306a36Sopenharmony_ci strncpy(hdev->status[HL_DEVICE_STATUS_NEEDS_RESET], "needs reset", HL_STR_MAX); 41862306a36Sopenharmony_ci strncpy(hdev->status[HL_DEVICE_STATUS_IN_DEVICE_CREATION], 41962306a36Sopenharmony_ci "in device creation", HL_STR_MAX); 42062306a36Sopenharmony_ci strncpy(hdev->status[HL_DEVICE_STATUS_IN_RESET_AFTER_DEVICE_RELEASE], 42162306a36Sopenharmony_ci "in reset after device release", HL_STR_MAX); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* First, we must find out which ASIC are we handling. This is needed 42562306a36Sopenharmony_ci * to configure the behavior of the driver (kernel parameters) 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_ci hdev->asic_type = get_asic_type(hdev); 42862306a36Sopenharmony_ci if (hdev->asic_type == ASIC_INVALID) { 42962306a36Sopenharmony_ci dev_err(&pdev->dev, "Unsupported ASIC\n"); 43062306a36Sopenharmony_ci rc = -ENODEV; 43162306a36Sopenharmony_ci goto free_hdev; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci copy_kernel_module_params_to_device(hdev); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci set_driver_behavior_per_device(hdev); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci fixup_device_params(hdev); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci mutex_lock(&hl_devs_idr_lock); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* Always save 2 numbers, 1 for main device and 1 for control. 44362306a36Sopenharmony_ci * They must be consecutive 44462306a36Sopenharmony_ci */ 44562306a36Sopenharmony_ci main_id = idr_alloc(&hl_devs_idr, hdev, 0, HL_MAX_MINORS, GFP_KERNEL); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (main_id >= 0) 44862306a36Sopenharmony_ci ctrl_id = idr_alloc(&hl_devs_idr, hdev, main_id + 1, 44962306a36Sopenharmony_ci main_id + 2, GFP_KERNEL); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci mutex_unlock(&hl_devs_idr_lock); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if ((main_id < 0) || (ctrl_id < 0)) { 45462306a36Sopenharmony_ci if ((main_id == -ENOSPC) || (ctrl_id == -ENOSPC)) 45562306a36Sopenharmony_ci pr_err("too many devices in the system\n"); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (main_id >= 0) { 45862306a36Sopenharmony_ci mutex_lock(&hl_devs_idr_lock); 45962306a36Sopenharmony_ci idr_remove(&hl_devs_idr, main_id); 46062306a36Sopenharmony_ci mutex_unlock(&hl_devs_idr_lock); 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci rc = -EBUSY; 46462306a36Sopenharmony_ci goto free_hdev; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci hdev->id = main_id; 46862306a36Sopenharmony_ci hdev->id_control = ctrl_id; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci *dev = hdev; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cifree_hdev: 47562306a36Sopenharmony_ci kfree(hdev); 47662306a36Sopenharmony_ci return rc; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci/* 48062306a36Sopenharmony_ci * destroy_hdev - destroy habanalabs device instance 48162306a36Sopenharmony_ci * 48262306a36Sopenharmony_ci * @dev: pointer to the habanalabs device structure 48362306a36Sopenharmony_ci * 48462306a36Sopenharmony_ci */ 48562306a36Sopenharmony_cistatic void destroy_hdev(struct hl_device *hdev) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci /* Remove device from the device list */ 48862306a36Sopenharmony_ci mutex_lock(&hl_devs_idr_lock); 48962306a36Sopenharmony_ci idr_remove(&hl_devs_idr, hdev->id); 49062306a36Sopenharmony_ci idr_remove(&hl_devs_idr, hdev->id_control); 49162306a36Sopenharmony_ci mutex_unlock(&hl_devs_idr_lock); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci kfree(hdev); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic int hl_pmops_suspend(struct device *dev) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct hl_device *hdev = dev_get_drvdata(dev); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci pr_debug("Going to suspend PCI device\n"); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (!hdev) { 50362306a36Sopenharmony_ci pr_err("device pointer is NULL in suspend\n"); 50462306a36Sopenharmony_ci return 0; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return hl_device_suspend(hdev); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int hl_pmops_resume(struct device *dev) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct hl_device *hdev = dev_get_drvdata(dev); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci pr_debug("Going to resume PCI device\n"); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (!hdev) { 51762306a36Sopenharmony_ci pr_err("device pointer is NULL in resume\n"); 51862306a36Sopenharmony_ci return 0; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return hl_device_resume(hdev); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci/** 52562306a36Sopenharmony_ci * hl_pci_probe - probe PCI habanalabs devices 52662306a36Sopenharmony_ci * 52762306a36Sopenharmony_ci * @pdev: pointer to pci device 52862306a36Sopenharmony_ci * @id: pointer to pci device id structure 52962306a36Sopenharmony_ci * 53062306a36Sopenharmony_ci * Standard PCI probe function for habanalabs device. 53162306a36Sopenharmony_ci * Create a new habanalabs device and initialize it according to the 53262306a36Sopenharmony_ci * device's type 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_cistatic int hl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct hl_device *hdev; 53762306a36Sopenharmony_ci int rc; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci dev_info(&pdev->dev, HL_NAME 54062306a36Sopenharmony_ci " device found [%04x:%04x] (rev %x)\n", 54162306a36Sopenharmony_ci (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci rc = create_hdev(&hdev, pdev); 54462306a36Sopenharmony_ci if (rc) 54562306a36Sopenharmony_ci return rc; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci pci_set_drvdata(pdev, hdev); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci rc = hl_device_init(hdev); 55062306a36Sopenharmony_ci if (rc) { 55162306a36Sopenharmony_ci dev_err(&pdev->dev, "Fatal error during habanalabs device init\n"); 55262306a36Sopenharmony_ci rc = -ENODEV; 55362306a36Sopenharmony_ci goto disable_device; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cidisable_device: 55962306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 56062306a36Sopenharmony_ci destroy_hdev(hdev); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci return rc; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci/* 56662306a36Sopenharmony_ci * hl_pci_remove - remove PCI habanalabs devices 56762306a36Sopenharmony_ci * 56862306a36Sopenharmony_ci * @pdev: pointer to pci device 56962306a36Sopenharmony_ci * 57062306a36Sopenharmony_ci * Standard PCI remove function for habanalabs device 57162306a36Sopenharmony_ci */ 57262306a36Sopenharmony_cistatic void hl_pci_remove(struct pci_dev *pdev) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct hl_device *hdev; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci hdev = pci_get_drvdata(pdev); 57762306a36Sopenharmony_ci if (!hdev) 57862306a36Sopenharmony_ci return; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci hl_device_fini(hdev); 58162306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 58262306a36Sopenharmony_ci destroy_hdev(hdev); 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci/** 58662306a36Sopenharmony_ci * hl_pci_err_detected - a PCI bus error detected on this device 58762306a36Sopenharmony_ci * 58862306a36Sopenharmony_ci * @pdev: pointer to pci device 58962306a36Sopenharmony_ci * @state: PCI error type 59062306a36Sopenharmony_ci * 59162306a36Sopenharmony_ci * Called by the PCI subsystem whenever a non-correctable 59262306a36Sopenharmony_ci * PCI bus error is detected 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_cistatic pci_ers_result_t 59562306a36Sopenharmony_cihl_pci_err_detected(struct pci_dev *pdev, pci_channel_state_t state) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci struct hl_device *hdev = pci_get_drvdata(pdev); 59862306a36Sopenharmony_ci enum pci_ers_result result; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci switch (state) { 60162306a36Sopenharmony_ci case pci_channel_io_normal: 60262306a36Sopenharmony_ci dev_warn(hdev->dev, "PCI normal state error detected\n"); 60362306a36Sopenharmony_ci return PCI_ERS_RESULT_CAN_RECOVER; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci case pci_channel_io_frozen: 60662306a36Sopenharmony_ci dev_warn(hdev->dev, "PCI frozen state error detected\n"); 60762306a36Sopenharmony_ci result = PCI_ERS_RESULT_NEED_RESET; 60862306a36Sopenharmony_ci break; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci case pci_channel_io_perm_failure: 61162306a36Sopenharmony_ci dev_warn(hdev->dev, "PCI failure state error detected\n"); 61262306a36Sopenharmony_ci result = PCI_ERS_RESULT_DISCONNECT; 61362306a36Sopenharmony_ci break; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci default: 61662306a36Sopenharmony_ci result = PCI_ERS_RESULT_NONE; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci hdev->asic_funcs->halt_engines(hdev, true, false); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return result; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci/** 62562306a36Sopenharmony_ci * hl_pci_err_resume - resume after a PCI slot reset 62662306a36Sopenharmony_ci * 62762306a36Sopenharmony_ci * @pdev: pointer to pci device 62862306a36Sopenharmony_ci * 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_cistatic void hl_pci_err_resume(struct pci_dev *pdev) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct hl_device *hdev = pci_get_drvdata(pdev); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci dev_warn(hdev->dev, "Resuming device after PCI slot reset\n"); 63562306a36Sopenharmony_ci hl_device_resume(hdev); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci/** 63962306a36Sopenharmony_ci * hl_pci_err_slot_reset - a PCI slot reset has just happened 64062306a36Sopenharmony_ci * 64162306a36Sopenharmony_ci * @pdev: pointer to pci device 64262306a36Sopenharmony_ci * 64362306a36Sopenharmony_ci * Determine if the driver can recover from the PCI slot reset 64462306a36Sopenharmony_ci */ 64562306a36Sopenharmony_cistatic pci_ers_result_t hl_pci_err_slot_reset(struct pci_dev *pdev) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci struct hl_device *hdev = pci_get_drvdata(pdev); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci dev_warn(hdev->dev, "PCI slot reset detected\n"); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return PCI_ERS_RESULT_RECOVERED; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic const struct dev_pm_ops hl_pm_ops = { 65562306a36Sopenharmony_ci .suspend = hl_pmops_suspend, 65662306a36Sopenharmony_ci .resume = hl_pmops_resume, 65762306a36Sopenharmony_ci}; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic const struct pci_error_handlers hl_pci_err_handler = { 66062306a36Sopenharmony_ci .error_detected = hl_pci_err_detected, 66162306a36Sopenharmony_ci .slot_reset = hl_pci_err_slot_reset, 66262306a36Sopenharmony_ci .resume = hl_pci_err_resume, 66362306a36Sopenharmony_ci}; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic struct pci_driver hl_pci_driver = { 66662306a36Sopenharmony_ci .name = HL_NAME, 66762306a36Sopenharmony_ci .id_table = ids, 66862306a36Sopenharmony_ci .probe = hl_pci_probe, 66962306a36Sopenharmony_ci .remove = hl_pci_remove, 67062306a36Sopenharmony_ci .shutdown = hl_pci_remove, 67162306a36Sopenharmony_ci .driver = { 67262306a36Sopenharmony_ci .name = HL_NAME, 67362306a36Sopenharmony_ci .pm = &hl_pm_ops, 67462306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 67562306a36Sopenharmony_ci }, 67662306a36Sopenharmony_ci .err_handler = &hl_pci_err_handler, 67762306a36Sopenharmony_ci}; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci/* 68062306a36Sopenharmony_ci * hl_init - Initialize the habanalabs kernel driver 68162306a36Sopenharmony_ci */ 68262306a36Sopenharmony_cistatic int __init hl_init(void) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci int rc; 68562306a36Sopenharmony_ci dev_t dev; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci pr_info("loading driver\n"); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci rc = alloc_chrdev_region(&dev, 0, HL_MAX_MINORS, HL_NAME); 69062306a36Sopenharmony_ci if (rc < 0) { 69162306a36Sopenharmony_ci pr_err("unable to get major\n"); 69262306a36Sopenharmony_ci return rc; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci hl_major = MAJOR(dev); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci hl_class = class_create(HL_NAME); 69862306a36Sopenharmony_ci if (IS_ERR(hl_class)) { 69962306a36Sopenharmony_ci pr_err("failed to allocate class\n"); 70062306a36Sopenharmony_ci rc = PTR_ERR(hl_class); 70162306a36Sopenharmony_ci goto remove_major; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci hl_debugfs_init(); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci rc = pci_register_driver(&hl_pci_driver); 70762306a36Sopenharmony_ci if (rc) { 70862306a36Sopenharmony_ci pr_err("failed to register pci device\n"); 70962306a36Sopenharmony_ci goto remove_debugfs; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci pr_debug("driver loaded\n"); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci return 0; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ciremove_debugfs: 71762306a36Sopenharmony_ci hl_debugfs_fini(); 71862306a36Sopenharmony_ci class_destroy(hl_class); 71962306a36Sopenharmony_ciremove_major: 72062306a36Sopenharmony_ci unregister_chrdev_region(MKDEV(hl_major, 0), HL_MAX_MINORS); 72162306a36Sopenharmony_ci return rc; 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci/* 72562306a36Sopenharmony_ci * hl_exit - Release all resources of the habanalabs kernel driver 72662306a36Sopenharmony_ci */ 72762306a36Sopenharmony_cistatic void __exit hl_exit(void) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci pci_unregister_driver(&hl_pci_driver); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* 73262306a36Sopenharmony_ci * Removing debugfs must be after all devices or simulator devices 73362306a36Sopenharmony_ci * have been removed because otherwise we get a bug in the 73462306a36Sopenharmony_ci * debugfs module for referencing NULL objects 73562306a36Sopenharmony_ci */ 73662306a36Sopenharmony_ci hl_debugfs_fini(); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci class_destroy(hl_class); 73962306a36Sopenharmony_ci unregister_chrdev_region(MKDEV(hl_major, 0), HL_MAX_MINORS); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci idr_destroy(&hl_devs_idr); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci pr_debug("driver removed\n"); 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cimodule_init(hl_init); 74762306a36Sopenharmony_cimodule_exit(hl_exit); 748