summaryrefslogtreecommitdiff
path: root/drivers/misc/habanalabs/common
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/habanalabs/common')
-rw-r--r--drivers/misc/habanalabs/common/Makefile15
-rw-r--r--drivers/misc/habanalabs/common/asid.c58
-rw-r--r--drivers/misc/habanalabs/common/command_buffer.c558
-rw-r--r--drivers/misc/habanalabs/common/command_submission.c3538
-rw-r--r--drivers/misc/habanalabs/common/context.c445
-rw-r--r--drivers/misc/habanalabs/common/debugfs.c1948
-rw-r--r--drivers/misc/habanalabs/common/decoder.c133
-rw-r--r--drivers/misc/habanalabs/common/device.c2534
-rw-r--r--drivers/misc/habanalabs/common/firmware_if.c3171
-rw-r--r--drivers/misc/habanalabs/common/habanalabs.h4002
-rw-r--r--drivers/misc/habanalabs/common/habanalabs_drv.c753
-rw-r--r--drivers/misc/habanalabs/common/habanalabs_ioctl.c1190
-rw-r--r--drivers/misc/habanalabs/common/hw_queue.c1137
-rw-r--r--drivers/misc/habanalabs/common/hwmon.c934
-rw-r--r--drivers/misc/habanalabs/common/irq.c571
-rw-r--r--drivers/misc/habanalabs/common/memory.c3002
-rw-r--r--drivers/misc/habanalabs/common/memory_mgr.c350
-rw-r--r--drivers/misc/habanalabs/common/mmu/Makefile3
-rw-r--r--drivers/misc/habanalabs/common/mmu/mmu.c1246
-rw-r--r--drivers/misc/habanalabs/common/mmu/mmu_v1.c814
-rw-r--r--drivers/misc/habanalabs/common/mmu/mmu_v2_hr.c399
-rw-r--r--drivers/misc/habanalabs/common/pci/Makefile2
-rw-r--r--drivers/misc/habanalabs/common/pci/pci.c433
-rw-r--r--drivers/misc/habanalabs/common/security.c600
-rw-r--r--drivers/misc/habanalabs/common/state_dump.c718
-rw-r--r--drivers/misc/habanalabs/common/sysfs.c514
26 files changed, 0 insertions, 29068 deletions
diff --git a/drivers/misc/habanalabs/common/Makefile b/drivers/misc/habanalabs/common/Makefile
deleted file mode 100644
index e6abffea9f87..000000000000
--- a/drivers/misc/habanalabs/common/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-
-include $(src)/common/mmu/Makefile
-habanalabs-y += $(HL_COMMON_MMU_FILES)
-
-include $(src)/common/pci/Makefile
-habanalabs-y += $(HL_COMMON_PCI_FILES)
-
-HL_COMMON_FILES := common/habanalabs_drv.o common/device.o common/context.o \
- common/asid.o common/habanalabs_ioctl.o \
- common/command_buffer.o common/hw_queue.o common/irq.o \
- common/sysfs.o common/hwmon.o common/memory.o \
- common/command_submission.o common/firmware_if.o \
- common/security.o common/state_dump.o \
- common/memory_mgr.o common/decoder.o
diff --git a/drivers/misc/habanalabs/common/asid.c b/drivers/misc/habanalabs/common/asid.c
deleted file mode 100644
index c9c2619cc43d..000000000000
--- a/drivers/misc/habanalabs/common/asid.c
+++ /dev/null
@@ -1,58 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2019 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include "habanalabs.h"
-
-#include <linux/slab.h>
-
-int hl_asid_init(struct hl_device *hdev)
-{
- hdev->asid_bitmap = bitmap_zalloc(hdev->asic_prop.max_asid, GFP_KERNEL);
- if (!hdev->asid_bitmap)
- return -ENOMEM;
-
- mutex_init(&hdev->asid_mutex);
-
- /* ASID 0 is reserved for the kernel driver and device CPU */
- set_bit(0, hdev->asid_bitmap);
-
- return 0;
-}
-
-void hl_asid_fini(struct hl_device *hdev)
-{
- mutex_destroy(&hdev->asid_mutex);
- bitmap_free(hdev->asid_bitmap);
-}
-
-unsigned long hl_asid_alloc(struct hl_device *hdev)
-{
- unsigned long found;
-
- mutex_lock(&hdev->asid_mutex);
-
- found = find_first_zero_bit(hdev->asid_bitmap,
- hdev->asic_prop.max_asid);
- if (found == hdev->asic_prop.max_asid)
- found = 0;
- else
- set_bit(found, hdev->asid_bitmap);
-
- mutex_unlock(&hdev->asid_mutex);
-
- return found;
-}
-
-void hl_asid_free(struct hl_device *hdev, unsigned long asid)
-{
- if (asid == HL_KERNEL_ASID_ID || asid >= hdev->asic_prop.max_asid) {
- dev_crit(hdev->dev, "Invalid ASID %lu", asid);
- return;
- }
-
- clear_bit(asid, hdev->asid_bitmap);
-}
diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/misc/habanalabs/common/command_buffer.c
deleted file mode 100644
index 6263d01cb9c1..000000000000
--- a/drivers/misc/habanalabs/common/command_buffer.c
+++ /dev/null
@@ -1,558 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2019 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include <uapi/drm/habanalabs_accel.h>
-#include "habanalabs.h"
-
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-
-#define CB_VA_POOL_SIZE (4UL * SZ_1G)
-
-static int cb_map_mem(struct hl_ctx *ctx, struct hl_cb *cb)
-{
- struct hl_device *hdev = ctx->hdev;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u32 page_size = prop->pmmu.page_size;
- int rc;
-
- if (!hdev->supports_cb_mapping) {
- dev_err_ratelimited(hdev->dev,
- "Mapping a CB to the device's MMU is not supported\n");
- return -EINVAL;
- }
-
- if (!hdev->mmu_enable) {
- dev_err_ratelimited(hdev->dev,
- "Cannot map CB because MMU is disabled\n");
- return -EINVAL;
- }
-
- if (cb->is_mmu_mapped)
- return 0;
-
- cb->roundup_size = roundup(cb->size, page_size);
-
- cb->virtual_addr = (u64) gen_pool_alloc(ctx->cb_va_pool, cb->roundup_size);
- if (!cb->virtual_addr) {
- dev_err(hdev->dev, "Failed to allocate device virtual address for CB\n");
- return -ENOMEM;
- }
-
- mutex_lock(&hdev->mmu_lock);
- rc = hl_mmu_map_contiguous(ctx, cb->virtual_addr, cb->bus_address, cb->roundup_size);
- if (rc) {
- dev_err(hdev->dev, "Failed to map VA %#llx to CB\n", cb->virtual_addr);
- goto err_va_umap;
- }
- rc = hl_mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR | MMU_OP_SKIP_LOW_CACHE_INV);
- mutex_unlock(&hdev->mmu_lock);
-
- cb->is_mmu_mapped = true;
- return rc;
-
-err_va_umap:
- mutex_unlock(&hdev->mmu_lock);
- gen_pool_free(ctx->cb_va_pool, cb->virtual_addr, cb->roundup_size);
- return rc;
-}
-
-static void cb_unmap_mem(struct hl_ctx *ctx, struct hl_cb *cb)
-{
- struct hl_device *hdev = ctx->hdev;
-
- mutex_lock(&hdev->mmu_lock);
- hl_mmu_unmap_contiguous(ctx, cb->virtual_addr, cb->roundup_size);
- hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR);
- mutex_unlock(&hdev->mmu_lock);
-
- gen_pool_free(ctx->cb_va_pool, cb->virtual_addr, cb->roundup_size);
-}
-
-static void cb_fini(struct hl_device *hdev, struct hl_cb *cb)
-{
- if (cb->is_internal)
- gen_pool_free(hdev->internal_cb_pool,
- (uintptr_t)cb->kernel_address, cb->size);
- else
- hl_asic_dma_free_coherent(hdev, cb->size, cb->kernel_address, cb->bus_address);
-
- kfree(cb);
-}
-
-static void cb_do_release(struct hl_device *hdev, struct hl_cb *cb)
-{
- if (cb->is_pool) {
- spin_lock(&hdev->cb_pool_lock);
- list_add(&cb->pool_list, &hdev->cb_pool);
- spin_unlock(&hdev->cb_pool_lock);
- } else {
- cb_fini(hdev, cb);
- }
-}
-
-static struct hl_cb *hl_cb_alloc(struct hl_device *hdev, u32 cb_size,
- int ctx_id, bool internal_cb)
-{
- struct hl_cb *cb = NULL;
- u32 cb_offset;
- void *p;
-
- /*
- * We use of GFP_ATOMIC here because this function can be called from
- * the latency-sensitive code path for command submission. Due to H/W
- * limitations in some of the ASICs, the kernel must copy the user CB
- * that is designated for an external queue and actually enqueue
- * the kernel's copy. Hence, we must never sleep in this code section
- * and must use GFP_ATOMIC for all memory allocations.
- */
- if (ctx_id == HL_KERNEL_ASID_ID && !hdev->disabled)
- cb = kzalloc(sizeof(*cb), GFP_ATOMIC);
-
- if (!cb)
- cb = kzalloc(sizeof(*cb), GFP_KERNEL);
-
- if (!cb)
- return NULL;
-
- if (internal_cb) {
- p = (void *) gen_pool_alloc(hdev->internal_cb_pool, cb_size);
- if (!p) {
- kfree(cb);
- return NULL;
- }
-
- cb_offset = p - hdev->internal_cb_pool_virt_addr;
- cb->is_internal = true;
- cb->bus_address = hdev->internal_cb_va_base + cb_offset;
- } else if (ctx_id == HL_KERNEL_ASID_ID) {
- p = hl_asic_dma_alloc_coherent(hdev, cb_size, &cb->bus_address, GFP_ATOMIC);
- if (!p)
- p = hl_asic_dma_alloc_coherent(hdev, cb_size, &cb->bus_address, GFP_KERNEL);
- } else {
- p = hl_asic_dma_alloc_coherent(hdev, cb_size, &cb->bus_address,
- GFP_USER | __GFP_ZERO);
- }
-
- if (!p) {
- dev_err(hdev->dev,
- "failed to allocate %d of dma memory for CB\n",
- cb_size);
- kfree(cb);
- return NULL;
- }
-
- cb->kernel_address = p;
- cb->size = cb_size;
-
- return cb;
-}
-
-struct hl_cb_mmap_mem_alloc_args {
- struct hl_device *hdev;
- struct hl_ctx *ctx;
- u32 cb_size;
- bool internal_cb;
- bool map_cb;
-};
-
-static void hl_cb_mmap_mem_release(struct hl_mmap_mem_buf *buf)
-{
- struct hl_cb *cb = buf->private;
-
- hl_debugfs_remove_cb(cb);
-
- if (cb->is_mmu_mapped)
- cb_unmap_mem(cb->ctx, cb);
-
- hl_ctx_put(cb->ctx);
-
- cb_do_release(cb->hdev, cb);
-}
-
-static int hl_cb_mmap_mem_alloc(struct hl_mmap_mem_buf *buf, gfp_t gfp, void *args)
-{
- struct hl_cb_mmap_mem_alloc_args *cb_args = args;
- struct hl_cb *cb;
- int rc, ctx_id = cb_args->ctx->asid;
- bool alloc_new_cb = true;
-
- if (!cb_args->internal_cb) {
- /* Minimum allocation must be PAGE SIZE */
- if (cb_args->cb_size < PAGE_SIZE)
- cb_args->cb_size = PAGE_SIZE;
-
- if (ctx_id == HL_KERNEL_ASID_ID &&
- cb_args->cb_size <= cb_args->hdev->asic_prop.cb_pool_cb_size) {
-
- spin_lock(&cb_args->hdev->cb_pool_lock);
- if (!list_empty(&cb_args->hdev->cb_pool)) {
- cb = list_first_entry(&cb_args->hdev->cb_pool,
- typeof(*cb), pool_list);
- list_del(&cb->pool_list);
- spin_unlock(&cb_args->hdev->cb_pool_lock);
- alloc_new_cb = false;
- } else {
- spin_unlock(&cb_args->hdev->cb_pool_lock);
- dev_dbg(cb_args->hdev->dev, "CB pool is empty\n");
- }
- }
- }
-
- if (alloc_new_cb) {
- cb = hl_cb_alloc(cb_args->hdev, cb_args->cb_size, ctx_id, cb_args->internal_cb);
- if (!cb)
- return -ENOMEM;
- }
-
- cb->hdev = cb_args->hdev;
- cb->ctx = cb_args->ctx;
- cb->buf = buf;
- cb->buf->mappable_size = cb->size;
- cb->buf->private = cb;
-
- hl_ctx_get(cb->ctx);
-
- if (cb_args->map_cb) {
- if (ctx_id == HL_KERNEL_ASID_ID) {
- dev_err(cb_args->hdev->dev,
- "CB mapping is not supported for kernel context\n");
- rc = -EINVAL;
- goto release_cb;
- }
-
- rc = cb_map_mem(cb_args->ctx, cb);
- if (rc)
- goto release_cb;
- }
-
- hl_debugfs_add_cb(cb);
-
- return 0;
-
-release_cb:
- hl_ctx_put(cb->ctx);
- cb_do_release(cb_args->hdev, cb);
-
- return rc;
-}
-
-static int hl_cb_mmap(struct hl_mmap_mem_buf *buf,
- struct vm_area_struct *vma, void *args)
-{
- struct hl_cb *cb = buf->private;
-
- return cb->hdev->asic_funcs->mmap(cb->hdev, vma, cb->kernel_address,
- cb->bus_address, cb->size);
-}
-
-static struct hl_mmap_mem_buf_behavior cb_behavior = {
- .topic = "CB",
- .mem_id = HL_MMAP_TYPE_CB,
- .alloc = hl_cb_mmap_mem_alloc,
- .release = hl_cb_mmap_mem_release,
- .mmap = hl_cb_mmap,
-};
-
-int hl_cb_create(struct hl_device *hdev, struct hl_mem_mgr *mmg,
- struct hl_ctx *ctx, u32 cb_size, bool internal_cb,
- bool map_cb, u64 *handle)
-{
- struct hl_cb_mmap_mem_alloc_args args = {
- .hdev = hdev,
- .ctx = ctx,
- .cb_size = cb_size,
- .internal_cb = internal_cb,
- .map_cb = map_cb,
- };
- struct hl_mmap_mem_buf *buf;
- int ctx_id = ctx->asid;
-
- if ((hdev->disabled) || (hdev->reset_info.in_reset && (ctx_id != HL_KERNEL_ASID_ID))) {
- dev_warn_ratelimited(hdev->dev,
- "Device is disabled or in reset. Can't create new CBs\n");
- return -EBUSY;
- }
-
- if (cb_size > SZ_2M) {
- dev_err(hdev->dev, "CB size %d must be less than %d\n",
- cb_size, SZ_2M);
- return -EINVAL;
- }
-
- buf = hl_mmap_mem_buf_alloc(
- mmg, &cb_behavior,
- ctx_id == HL_KERNEL_ASID_ID ? GFP_ATOMIC : GFP_KERNEL, &args);
- if (!buf)
- return -ENOMEM;
-
- *handle = buf->handle;
-
- return 0;
-}
-
-int hl_cb_destroy(struct hl_mem_mgr *mmg, u64 cb_handle)
-{
- struct hl_cb *cb;
- int rc;
-
- /* Make sure that a CB handle isn't destroyed by user more than once */
- if (!mmg->is_kernel_mem_mgr) {
- cb = hl_cb_get(mmg, cb_handle);
- if (!cb) {
- dev_dbg(mmg->dev, "CB destroy failed, no CB was found for handle %#llx\n",
- cb_handle);
- rc = -EINVAL;
- goto out;
- }
-
- rc = atomic_cmpxchg(&cb->is_handle_destroyed, 0, 1);
- hl_cb_put(cb);
- if (rc) {
- dev_dbg(mmg->dev, "CB destroy failed, handle %#llx was already destroyed\n",
- cb_handle);
- rc = -EINVAL;
- goto out;
- }
- }
-
- rc = hl_mmap_mem_buf_put_handle(mmg, cb_handle);
-out:
- if (rc < 0)
- return rc; /* Invalid handle */
-
- if (rc == 0)
- dev_dbg(mmg->dev, "CB 0x%llx is destroyed while still in use\n", cb_handle);
-
- return 0;
-}
-
-static int hl_cb_info(struct hl_mem_mgr *mmg,
- u64 handle, u32 flags, u32 *usage_cnt, u64 *device_va)
-{
- struct hl_cb *cb;
- int rc = 0;
-
- cb = hl_cb_get(mmg, handle);
- if (!cb) {
- dev_err(mmg->dev,
- "CB info failed, no match to handle 0x%llx\n", handle);
- return -EINVAL;
- }
-
- if (flags & HL_CB_FLAGS_GET_DEVICE_VA) {
- if (cb->is_mmu_mapped) {
- *device_va = cb->virtual_addr;
- } else {
- dev_err(mmg->dev, "CB is not mapped to the device's MMU\n");
- rc = -EINVAL;
- goto out;
- }
- } else {
- *usage_cnt = atomic_read(&cb->cs_cnt);
- }
-
-out:
- hl_cb_put(cb);
- return rc;
-}
-
-int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data)
-{
- union hl_cb_args *args = data;
- struct hl_device *hdev = hpriv->hdev;
- u64 handle = 0, device_va = 0;
- enum hl_device_status status;
- u32 usage_cnt = 0;
- int rc;
-
- if (!hl_device_operational(hdev, &status)) {
- dev_warn_ratelimited(hdev->dev,
- "Device is %s. Can't execute CB IOCTL\n",
- hdev->status[status]);
- return -EBUSY;
- }
-
- switch (args->in.op) {
- case HL_CB_OP_CREATE:
- if (args->in.cb_size > HL_MAX_CB_SIZE) {
- dev_err(hdev->dev,
- "User requested CB size %d must be less than %d\n",
- args->in.cb_size, HL_MAX_CB_SIZE);
- rc = -EINVAL;
- } else {
- rc = hl_cb_create(hdev, &hpriv->mem_mgr, hpriv->ctx,
- args->in.cb_size, false,
- !!(args->in.flags & HL_CB_FLAGS_MAP),
- &handle);
- }
-
- memset(args, 0, sizeof(*args));
- args->out.cb_handle = handle;
- break;
-
- case HL_CB_OP_DESTROY:
- rc = hl_cb_destroy(&hpriv->mem_mgr,
- args->in.cb_handle);
- break;
-
- case HL_CB_OP_INFO:
- rc = hl_cb_info(&hpriv->mem_mgr, args->in.cb_handle,
- args->in.flags,
- &usage_cnt,
- &device_va);
- if (rc)
- break;
-
- memset(&args->out, 0, sizeof(args->out));
-
- if (args->in.flags & HL_CB_FLAGS_GET_DEVICE_VA)
- args->out.device_va = device_va;
- else
- args->out.usage_cnt = usage_cnt;
- break;
-
- default:
- rc = -EINVAL;
- break;
- }
-
- return rc;
-}
-
-struct hl_cb *hl_cb_get(struct hl_mem_mgr *mmg, u64 handle)
-{
- struct hl_mmap_mem_buf *buf;
-
- buf = hl_mmap_mem_buf_get(mmg, handle);
- if (!buf)
- return NULL;
- return buf->private;
-
-}
-
-void hl_cb_put(struct hl_cb *cb)
-{
- hl_mmap_mem_buf_put(cb->buf);
-}
-
-struct hl_cb *hl_cb_kernel_create(struct hl_device *hdev, u32 cb_size,
- bool internal_cb)
-{
- u64 cb_handle;
- struct hl_cb *cb;
- int rc;
-
- rc = hl_cb_create(hdev, &hdev->kernel_mem_mgr, hdev->kernel_ctx, cb_size,
- internal_cb, false, &cb_handle);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to allocate CB for the kernel driver %d\n", rc);
- return NULL;
- }
-
- cb = hl_cb_get(&hdev->kernel_mem_mgr, cb_handle);
- /* hl_cb_get should never fail here */
- if (!cb) {
- dev_crit(hdev->dev, "Kernel CB handle invalid 0x%x\n",
- (u32) cb_handle);
- goto destroy_cb;
- }
-
- return cb;
-
-destroy_cb:
- hl_cb_destroy(&hdev->kernel_mem_mgr, cb_handle);
-
- return NULL;
-}
-
-int hl_cb_pool_init(struct hl_device *hdev)
-{
- struct hl_cb *cb;
- int i;
-
- INIT_LIST_HEAD(&hdev->cb_pool);
- spin_lock_init(&hdev->cb_pool_lock);
-
- for (i = 0 ; i < hdev->asic_prop.cb_pool_cb_cnt ; i++) {
- cb = hl_cb_alloc(hdev, hdev->asic_prop.cb_pool_cb_size,
- HL_KERNEL_ASID_ID, false);
- if (cb) {
- cb->is_pool = true;
- list_add(&cb->pool_list, &hdev->cb_pool);
- } else {
- hl_cb_pool_fini(hdev);
- return -ENOMEM;
- }
- }
-
- return 0;
-}
-
-int hl_cb_pool_fini(struct hl_device *hdev)
-{
- struct hl_cb *cb, *tmp;
-
- list_for_each_entry_safe(cb, tmp, &hdev->cb_pool, pool_list) {
- list_del(&cb->pool_list);
- cb_fini(hdev, cb);
- }
-
- return 0;
-}
-
-int hl_cb_va_pool_init(struct hl_ctx *ctx)
-{
- struct hl_device *hdev = ctx->hdev;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- int rc;
-
- if (!hdev->supports_cb_mapping)
- return 0;
-
- ctx->cb_va_pool = gen_pool_create(__ffs(prop->pmmu.page_size), -1);
- if (!ctx->cb_va_pool) {
- dev_err(hdev->dev,
- "Failed to create VA gen pool for CB mapping\n");
- return -ENOMEM;
- }
-
- ctx->cb_va_pool_base = hl_reserve_va_block(hdev, ctx, HL_VA_RANGE_TYPE_HOST,
- CB_VA_POOL_SIZE, HL_MMU_VA_ALIGNMENT_NOT_NEEDED);
- if (!ctx->cb_va_pool_base) {
- rc = -ENOMEM;
- goto err_pool_destroy;
- }
- rc = gen_pool_add(ctx->cb_va_pool, ctx->cb_va_pool_base, CB_VA_POOL_SIZE, -1);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to add memory to VA gen pool for CB mapping\n");
- goto err_unreserve_va_block;
- }
-
- return 0;
-
-err_unreserve_va_block:
- hl_unreserve_va_block(hdev, ctx, ctx->cb_va_pool_base, CB_VA_POOL_SIZE);
-err_pool_destroy:
- gen_pool_destroy(ctx->cb_va_pool);
-
- return rc;
-}
-
-void hl_cb_va_pool_fini(struct hl_ctx *ctx)
-{
- struct hl_device *hdev = ctx->hdev;
-
- if (!hdev->supports_cb_mapping)
- return;
-
- gen_pool_destroy(ctx->cb_va_pool);
- hl_unreserve_va_block(hdev, ctx, ctx->cb_va_pool_base, CB_VA_POOL_SIZE);
-}
diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c
deleted file mode 100644
index f6ee10334235..000000000000
--- a/drivers/misc/habanalabs/common/command_submission.c
+++ /dev/null
@@ -1,3538 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2021 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include <uapi/drm/habanalabs_accel.h>
-#include "habanalabs.h"
-
-#include <linux/uaccess.h>
-#include <linux/slab.h>
-
-#define HL_CS_FLAGS_TYPE_MASK (HL_CS_FLAGS_SIGNAL | HL_CS_FLAGS_WAIT | \
- HL_CS_FLAGS_COLLECTIVE_WAIT | HL_CS_FLAGS_RESERVE_SIGNALS_ONLY | \
- HL_CS_FLAGS_UNRESERVE_SIGNALS_ONLY | HL_CS_FLAGS_ENGINE_CORE_COMMAND)
-
-
-#define MAX_TS_ITER_NUM 10
-
-/**
- * enum hl_cs_wait_status - cs wait status
- * @CS_WAIT_STATUS_BUSY: cs was not completed yet
- * @CS_WAIT_STATUS_COMPLETED: cs completed
- * @CS_WAIT_STATUS_GONE: cs completed but fence is already gone
- */
-enum hl_cs_wait_status {
- CS_WAIT_STATUS_BUSY,
- CS_WAIT_STATUS_COMPLETED,
- CS_WAIT_STATUS_GONE
-};
-
-static void job_wq_completion(struct work_struct *work);
-static int _hl_cs_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, u64 timeout_us, u64 seq,
- enum hl_cs_wait_status *status, s64 *timestamp);
-static void cs_do_release(struct kref *ref);
-
-static void hl_push_cs_outcome(struct hl_device *hdev,
- struct hl_cs_outcome_store *outcome_store,
- u64 seq, ktime_t ts, int error)
-{
- struct hl_cs_outcome *node;
- unsigned long flags;
-
- /*
- * CS outcome store supports the following operations:
- * push outcome - store a recent CS outcome in the store
- * pop outcome - retrieve a SPECIFIC (by seq) CS outcome from the store
- * It uses 2 lists: used list and free list.
- * It has a pre-allocated amount of nodes, each node stores
- * a single CS outcome.
- * Initially, all the nodes are in the free list.
- * On push outcome, a node (any) is taken from the free list, its
- * information is filled in, and the node is moved to the used list.
- * It is possible, that there are no nodes left in the free list.
- * In this case, we will lose some information about old outcomes. We
- * will pop the OLDEST node from the used list, and make it free.
- * On pop, the node is searched for in the used list (using a search
- * index).
- * If found, the node is then removed from the used list, and moved
- * back to the free list. The outcome data that the node contained is
- * returned back to the user.
- */
-
- spin_lock_irqsave(&outcome_store->db_lock, flags);
-
- if (list_empty(&outcome_store->free_list)) {
- node = list_last_entry(&outcome_store->used_list,
- struct hl_cs_outcome, list_link);
- hash_del(&node->map_link);
- dev_dbg(hdev->dev, "CS %llu outcome was lost\n", node->seq);
- } else {
- node = list_last_entry(&outcome_store->free_list,
- struct hl_cs_outcome, list_link);
- }
-
- list_del_init(&node->list_link);
-
- node->seq = seq;
- node->ts = ts;
- node->error = error;
-
- list_add(&node->list_link, &outcome_store->used_list);
- hash_add(outcome_store->outcome_map, &node->map_link, node->seq);
-
- spin_unlock_irqrestore(&outcome_store->db_lock, flags);
-}
-
-static bool hl_pop_cs_outcome(struct hl_cs_outcome_store *outcome_store,
- u64 seq, ktime_t *ts, int *error)
-{
- struct hl_cs_outcome *node;
- unsigned long flags;
-
- spin_lock_irqsave(&outcome_store->db_lock, flags);
-
- hash_for_each_possible(outcome_store->outcome_map, node, map_link, seq)
- if (node->seq == seq) {
- *ts = node->ts;
- *error = node->error;
-
- hash_del(&node->map_link);
- list_del_init(&node->list_link);
- list_add(&node->list_link, &outcome_store->free_list);
-
- spin_unlock_irqrestore(&outcome_store->db_lock, flags);
-
- return true;
- }
-
- spin_unlock_irqrestore(&outcome_store->db_lock, flags);
-
- return false;
-}
-
-static void hl_sob_reset(struct kref *ref)
-{
- struct hl_hw_sob *hw_sob = container_of(ref, struct hl_hw_sob,
- kref);
- struct hl_device *hdev = hw_sob->hdev;
-
- dev_dbg(hdev->dev, "reset sob id %u\n", hw_sob->sob_id);
-
- hdev->asic_funcs->reset_sob(hdev, hw_sob);
-
- hw_sob->need_reset = false;
-}
-
-void hl_sob_reset_error(struct kref *ref)
-{
- struct hl_hw_sob *hw_sob = container_of(ref, struct hl_hw_sob,
- kref);
- struct hl_device *hdev = hw_sob->hdev;
-
- dev_crit(hdev->dev,
- "SOB release shouldn't be called here, q_idx: %d, sob_id: %d\n",
- hw_sob->q_idx, hw_sob->sob_id);
-}
-
-void hw_sob_put(struct hl_hw_sob *hw_sob)
-{
- if (hw_sob)
- kref_put(&hw_sob->kref, hl_sob_reset);
-}
-
-static void hw_sob_put_err(struct hl_hw_sob *hw_sob)
-{
- if (hw_sob)
- kref_put(&hw_sob->kref, hl_sob_reset_error);
-}
-
-void hw_sob_get(struct hl_hw_sob *hw_sob)
-{
- if (hw_sob)
- kref_get(&hw_sob->kref);
-}
-
-/**
- * hl_gen_sob_mask() - Generates a sob mask to be used in a monitor arm packet
- * @sob_base: sob base id
- * @sob_mask: sob user mask, each bit represents a sob offset from sob base
- * @mask: generated mask
- *
- * Return: 0 if given parameters are valid
- */
-int hl_gen_sob_mask(u16 sob_base, u8 sob_mask, u8 *mask)
-{
- int i;
-
- if (sob_mask == 0)
- return -EINVAL;
-
- if (sob_mask == 0x1) {
- *mask = ~(1 << (sob_base & 0x7));
- } else {
- /* find msb in order to verify sob range is valid */
- for (i = BITS_PER_BYTE - 1 ; i >= 0 ; i--)
- if (BIT(i) & sob_mask)
- break;
-
- if (i > (HL_MAX_SOBS_PER_MONITOR - (sob_base & 0x7) - 1))
- return -EINVAL;
-
- *mask = ~sob_mask;
- }
-
- return 0;
-}
-
-static void hl_fence_release(struct kref *kref)
-{
- struct hl_fence *fence =
- container_of(kref, struct hl_fence, refcount);
- struct hl_cs_compl *hl_cs_cmpl =
- container_of(fence, struct hl_cs_compl, base_fence);
-
- kfree(hl_cs_cmpl);
-}
-
-void hl_fence_put(struct hl_fence *fence)
-{
- if (IS_ERR_OR_NULL(fence))
- return;
- kref_put(&fence->refcount, hl_fence_release);
-}
-
-void hl_fences_put(struct hl_fence **fence, int len)
-{
- int i;
-
- for (i = 0; i < len; i++, fence++)
- hl_fence_put(*fence);
-}
-
-void hl_fence_get(struct hl_fence *fence)
-{
- if (fence)
- kref_get(&fence->refcount);
-}
-
-static void hl_fence_init(struct hl_fence *fence, u64 sequence)
-{
- kref_init(&fence->refcount);
- fence->cs_sequence = sequence;
- fence->error = 0;
- fence->timestamp = ktime_set(0, 0);
- fence->mcs_handling_done = false;
- init_completion(&fence->completion);
-}
-
-void cs_get(struct hl_cs *cs)
-{
- kref_get(&cs->refcount);
-}
-
-static int cs_get_unless_zero(struct hl_cs *cs)
-{
- return kref_get_unless_zero(&cs->refcount);
-}
-
-static void cs_put(struct hl_cs *cs)
-{
- kref_put(&cs->refcount, cs_do_release);
-}
-
-static void cs_job_do_release(struct kref *ref)
-{
- struct hl_cs_job *job = container_of(ref, struct hl_cs_job, refcount);
-
- kfree(job);
-}
-
-static void hl_cs_job_put(struct hl_cs_job *job)
-{
- kref_put(&job->refcount, cs_job_do_release);
-}
-
-bool cs_needs_completion(struct hl_cs *cs)
-{
- /* In case this is a staged CS, only the last CS in sequence should
- * get a completion, any non staged CS will always get a completion
- */
- if (cs->staged_cs && !cs->staged_last)
- return false;
-
- return true;
-}
-
-bool cs_needs_timeout(struct hl_cs *cs)
-{
- /* In case this is a staged CS, only the first CS in sequence should
- * get a timeout, any non staged CS will always get a timeout
- */
- if (cs->staged_cs && !cs->staged_first)
- return false;
-
- return true;
-}
-
-static bool is_cb_patched(struct hl_device *hdev, struct hl_cs_job *job)
-{
- /*
- * Patched CB is created for external queues jobs, and for H/W queues
- * jobs if the user CB was allocated by driver and MMU is disabled.
- */
- return (job->queue_type == QUEUE_TYPE_EXT ||
- (job->queue_type == QUEUE_TYPE_HW &&
- job->is_kernel_allocated_cb &&
- !hdev->mmu_enable));
-}
-
-/*
- * cs_parser - parse the user command submission
- *
- * @hpriv : pointer to the private data of the fd
- * @job : pointer to the job that holds the command submission info
- *
- * The function parses the command submission of the user. It calls the
- * ASIC specific parser, which returns a list of memory blocks to send
- * to the device as different command buffers
- *
- */
-static int cs_parser(struct hl_fpriv *hpriv, struct hl_cs_job *job)
-{
- struct hl_device *hdev = hpriv->hdev;
- struct hl_cs_parser parser;
- int rc;
-
- parser.ctx_id = job->cs->ctx->asid;
- parser.cs_sequence = job->cs->sequence;
- parser.job_id = job->id;
-
- parser.hw_queue_id = job->hw_queue_id;
- parser.job_userptr_list = &job->userptr_list;
- parser.patched_cb = NULL;
- parser.user_cb = job->user_cb;
- parser.user_cb_size = job->user_cb_size;
- parser.queue_type = job->queue_type;
- parser.is_kernel_allocated_cb = job->is_kernel_allocated_cb;
- job->patched_cb = NULL;
- parser.completion = cs_needs_completion(job->cs);
-
- rc = hdev->asic_funcs->cs_parser(hdev, &parser);
-
- if (is_cb_patched(hdev, job)) {
- if (!rc) {
- job->patched_cb = parser.patched_cb;
- job->job_cb_size = parser.patched_cb_size;
- job->contains_dma_pkt = parser.contains_dma_pkt;
- atomic_inc(&job->patched_cb->cs_cnt);
- }
-
- /*
- * Whether the parsing worked or not, we don't need the
- * original CB anymore because it was already parsed and
- * won't be accessed again for this CS
- */
- atomic_dec(&job->user_cb->cs_cnt);
- hl_cb_put(job->user_cb);
- job->user_cb = NULL;
- } else if (!rc) {
- job->job_cb_size = job->user_cb_size;
- }
-
- return rc;
-}
-
-static void hl_complete_job(struct hl_device *hdev, struct hl_cs_job *job)
-{
- struct hl_cs *cs = job->cs;
-
- if (is_cb_patched(hdev, job)) {
- hl_userptr_delete_list(hdev, &job->userptr_list);
-
- /*
- * We might arrive here from rollback and patched CB wasn't
- * created, so we need to check it's not NULL
- */
- if (job->patched_cb) {
- atomic_dec(&job->patched_cb->cs_cnt);
- hl_cb_put(job->patched_cb);
- }
- }
-
- /* For H/W queue jobs, if a user CB was allocated by driver and MMU is
- * enabled, the user CB isn't released in cs_parser() and thus should be
- * released here. This is also true for INT queues jobs which were
- * allocated by driver.
- */
- if ((job->is_kernel_allocated_cb &&
- ((job->queue_type == QUEUE_TYPE_HW && hdev->mmu_enable) ||
- job->queue_type == QUEUE_TYPE_INT))) {
- atomic_dec(&job->user_cb->cs_cnt);
- hl_cb_put(job->user_cb);
- }
-
- /*
- * This is the only place where there can be multiple threads
- * modifying the list at the same time
- */
- spin_lock(&cs->job_lock);
- list_del(&job->cs_node);
- spin_unlock(&cs->job_lock);
-
- hl_debugfs_remove_job(hdev, job);
-
- /* We decrement reference only for a CS that gets completion
- * because the reference was incremented only for this kind of CS
- * right before it was scheduled.
- *
- * In staged submission, only the last CS marked as 'staged_last'
- * gets completion, hence its release function will be called from here.
- * As for all the rest CS's in the staged submission which do not get
- * completion, their CS reference will be decremented by the
- * 'staged_last' CS during the CS release flow.
- * All relevant PQ CI counters will be incremented during the CS release
- * flow by calling 'hl_hw_queue_update_ci'.
- */
- if (cs_needs_completion(cs) &&
- (job->queue_type == QUEUE_TYPE_EXT || job->queue_type == QUEUE_TYPE_HW))
- cs_put(cs);
-
- hl_cs_job_put(job);
-}
-
-/*
- * hl_staged_cs_find_first - locate the first CS in this staged submission
- *
- * @hdev: pointer to device structure
- * @cs_seq: staged submission sequence number
- *
- * @note: This function must be called under 'hdev->cs_mirror_lock'
- *
- * Find and return a CS pointer with the given sequence
- */
-struct hl_cs *hl_staged_cs_find_first(struct hl_device *hdev, u64 cs_seq)
-{
- struct hl_cs *cs;
-
- list_for_each_entry_reverse(cs, &hdev->cs_mirror_list, mirror_node)
- if (cs->staged_cs && cs->staged_first &&
- cs->sequence == cs_seq)
- return cs;
-
- return NULL;
-}
-
-/*
- * is_staged_cs_last_exists - returns true if the last CS in sequence exists
- *
- * @hdev: pointer to device structure
- * @cs: staged submission member
- *
- */
-bool is_staged_cs_last_exists(struct hl_device *hdev, struct hl_cs *cs)
-{
- struct hl_cs *last_entry;
-
- last_entry = list_last_entry(&cs->staged_cs_node, struct hl_cs,
- staged_cs_node);
-
- if (last_entry->staged_last)
- return true;
-
- return false;
-}
-
-/*
- * staged_cs_get - get CS reference if this CS is a part of a staged CS
- *
- * @hdev: pointer to device structure
- * @cs: current CS
- * @cs_seq: staged submission sequence number
- *
- * Increment CS reference for every CS in this staged submission except for
- * the CS which get completion.
- */
-static void staged_cs_get(struct hl_device *hdev, struct hl_cs *cs)
-{
- /* Only the last CS in this staged submission will get a completion.
- * We must increment the reference for all other CS's in this
- * staged submission.
- * Once we get a completion we will release the whole staged submission.
- */
- if (!cs->staged_last)
- cs_get(cs);
-}
-
-/*
- * staged_cs_put - put a CS in case it is part of staged submission
- *
- * @hdev: pointer to device structure
- * @cs: CS to put
- *
- * This function decrements a CS reference (for a non completion CS)
- */
-static void staged_cs_put(struct hl_device *hdev, struct hl_cs *cs)
-{
- /* We release all CS's in a staged submission except the last
- * CS which we have never incremented its reference.
- */
- if (!cs_needs_completion(cs))
- cs_put(cs);
-}
-
-static void cs_handle_tdr(struct hl_device *hdev, struct hl_cs *cs)
-{
- struct hl_cs *next = NULL, *iter, *first_cs;
-
- if (!cs_needs_timeout(cs))
- return;
-
- spin_lock(&hdev->cs_mirror_lock);
-
- /* We need to handle tdr only once for the complete staged submission.
- * Hence, we choose the CS that reaches this function first which is
- * the CS marked as 'staged_last'.
- * In case single staged cs was submitted which has both first and last
- * indications, then "cs_find_first" below will return NULL, since we
- * removed the cs node from the list before getting here,
- * in such cases just continue with the cs to cancel it's TDR work.
- */
- if (cs->staged_cs && cs->staged_last) {
- first_cs = hl_staged_cs_find_first(hdev, cs->staged_sequence);
- if (first_cs)
- cs = first_cs;
- }
-
- spin_unlock(&hdev->cs_mirror_lock);
-
- /* Don't cancel TDR in case this CS was timedout because we might be
- * running from the TDR context
- */
- if (cs->timedout || hdev->timeout_jiffies == MAX_SCHEDULE_TIMEOUT)
- return;
-
- if (cs->tdr_active)
- cancel_delayed_work_sync(&cs->work_tdr);
-
- spin_lock(&hdev->cs_mirror_lock);
-
- /* queue TDR for next CS */
- list_for_each_entry(iter, &hdev->cs_mirror_list, mirror_node)
- if (cs_needs_timeout(iter)) {
- next = iter;
- break;
- }
-
- if (next && !next->tdr_active) {
- next->tdr_active = true;
- schedule_delayed_work(&next->work_tdr, next->timeout_jiffies);
- }
-
- spin_unlock(&hdev->cs_mirror_lock);
-}
-
-/*
- * force_complete_multi_cs - complete all contexts that wait on multi-CS
- *
- * @hdev: pointer to habanalabs device structure
- */
-static void force_complete_multi_cs(struct hl_device *hdev)
-{
- int i;
-
- for (i = 0; i < MULTI_CS_MAX_USER_CTX; i++) {
- struct multi_cs_completion *mcs_compl;
-
- mcs_compl = &hdev->multi_cs_completion[i];
-
- spin_lock(&mcs_compl->lock);
-
- if (!mcs_compl->used) {
- spin_unlock(&mcs_compl->lock);
- continue;
- }
-
- /* when calling force complete no context should be waiting on
- * multi-cS.
- * We are calling the function as a protection for such case
- * to free any pending context and print error message
- */
- dev_err(hdev->dev,
- "multi-CS completion context %d still waiting when calling force completion\n",
- i);
- complete_all(&mcs_compl->completion);
- spin_unlock(&mcs_compl->lock);
- }
-}
-
-/*
- * complete_multi_cs - complete all waiting entities on multi-CS
- *
- * @hdev: pointer to habanalabs device structure
- * @cs: CS structure
- * The function signals a waiting entity that has an overlapping stream masters
- * with the completed CS.
- * For example:
- * - a completed CS worked on stream master QID 4, multi CS completion
- * is actively waiting on stream master QIDs 3, 5. don't send signal as no
- * common stream master QID
- * - a completed CS worked on stream master QID 4, multi CS completion
- * is actively waiting on stream master QIDs 3, 4. send signal as stream
- * master QID 4 is common
- */
-static void complete_multi_cs(struct hl_device *hdev, struct hl_cs *cs)
-{
- struct hl_fence *fence = cs->fence;
- int i;
-
- /* in case of multi CS check for completion only for the first CS */
- if (cs->staged_cs && !cs->staged_first)
- return;
-
- for (i = 0; i < MULTI_CS_MAX_USER_CTX; i++) {
- struct multi_cs_completion *mcs_compl;
-
- mcs_compl = &hdev->multi_cs_completion[i];
- if (!mcs_compl->used)
- continue;
-
- spin_lock(&mcs_compl->lock);
-
- /*
- * complete if:
- * 1. still waiting for completion
- * 2. the completed CS has at least one overlapping stream
- * master with the stream masters in the completion
- */
- if (mcs_compl->used &&
- (fence->stream_master_qid_map &
- mcs_compl->stream_master_qid_map)) {
- /* extract the timestamp only of first completed CS */
- if (!mcs_compl->timestamp)
- mcs_compl->timestamp = ktime_to_ns(fence->timestamp);
-
- complete_all(&mcs_compl->completion);
-
- /*
- * Setting mcs_handling_done inside the lock ensures
- * at least one fence have mcs_handling_done set to
- * true before wait for mcs finish. This ensures at
- * least one CS will be set as completed when polling
- * mcs fences.
- */
- fence->mcs_handling_done = true;
- }
-
- spin_unlock(&mcs_compl->lock);
- }
- /* In case CS completed without mcs completion initialized */
- fence->mcs_handling_done = true;
-}
-
-static inline void cs_release_sob_reset_handler(struct hl_device *hdev,
- struct hl_cs *cs,
- struct hl_cs_compl *hl_cs_cmpl)
-{
- /* Skip this handler if the cs wasn't submitted, to avoid putting
- * the hw_sob twice, since this case already handled at this point,
- * also skip if the hw_sob pointer wasn't set.
- */
- if (!hl_cs_cmpl->hw_sob || !cs->submitted)
- return;
-
- spin_lock(&hl_cs_cmpl->lock);
-
- /*
- * we get refcount upon reservation of signals or signal/wait cs for the
- * hw_sob object, and need to put it when the first staged cs
- * (which cotains the encaps signals) or cs signal/wait is completed.
- */
- if ((hl_cs_cmpl->type == CS_TYPE_SIGNAL) ||
- (hl_cs_cmpl->type == CS_TYPE_WAIT) ||
- (hl_cs_cmpl->type == CS_TYPE_COLLECTIVE_WAIT) ||
- (!!hl_cs_cmpl->encaps_signals)) {
- dev_dbg(hdev->dev,
- "CS 0x%llx type %d finished, sob_id: %d, sob_val: %u\n",
- hl_cs_cmpl->cs_seq,
- hl_cs_cmpl->type,
- hl_cs_cmpl->hw_sob->sob_id,
- hl_cs_cmpl->sob_val);
-
- hw_sob_put(hl_cs_cmpl->hw_sob);
-
- if (hl_cs_cmpl->type == CS_TYPE_COLLECTIVE_WAIT)
- hdev->asic_funcs->reset_sob_group(hdev,
- hl_cs_cmpl->sob_group);
- }
-
- spin_unlock(&hl_cs_cmpl->lock);
-}
-
-static void cs_do_release(struct kref *ref)
-{
- struct hl_cs *cs = container_of(ref, struct hl_cs, refcount);
- struct hl_device *hdev = cs->ctx->hdev;
- struct hl_cs_job *job, *tmp;
- struct hl_cs_compl *hl_cs_cmpl =
- container_of(cs->fence, struct hl_cs_compl, base_fence);
-
- cs->completed = true;
-
- /*
- * Although if we reached here it means that all external jobs have
- * finished, because each one of them took refcnt to CS, we still
- * need to go over the internal jobs and complete them. Otherwise, we
- * will have leaked memory and what's worse, the CS object (and
- * potentially the CTX object) could be released, while the JOB
- * still holds a pointer to them (but no reference).
- */
- list_for_each_entry_safe(job, tmp, &cs->job_list, cs_node)
- hl_complete_job(hdev, job);
-
- if (!cs->submitted) {
- /*
- * In case the wait for signal CS was submitted, the fence put
- * occurs in init_signal_wait_cs() or collective_wait_init_cs()
- * right before hanging on the PQ.
- */
- if (cs->type == CS_TYPE_WAIT ||
- cs->type == CS_TYPE_COLLECTIVE_WAIT)
- hl_fence_put(cs->signal_fence);
-
- goto out;
- }
-
- /* Need to update CI for all queue jobs that does not get completion */
- hl_hw_queue_update_ci(cs);
-
- /* remove CS from CS mirror list */
- spin_lock(&hdev->cs_mirror_lock);
- list_del_init(&cs->mirror_node);
- spin_unlock(&hdev->cs_mirror_lock);
-
- cs_handle_tdr(hdev, cs);
-
- if (cs->staged_cs) {
- /* the completion CS decrements reference for the entire
- * staged submission
- */
- if (cs->staged_last) {
- struct hl_cs *staged_cs, *tmp_cs;
-
- list_for_each_entry_safe(staged_cs, tmp_cs,
- &cs->staged_cs_node, staged_cs_node)
- staged_cs_put(hdev, staged_cs);
- }
-
- /* A staged CS will be a member in the list only after it
- * was submitted. We used 'cs_mirror_lock' when inserting
- * it to list so we will use it again when removing it
- */
- if (cs->submitted) {
- spin_lock(&hdev->cs_mirror_lock);
- list_del(&cs->staged_cs_node);
- spin_unlock(&hdev->cs_mirror_lock);
- }
-
- /* decrement refcount to handle when first staged cs
- * with encaps signals is completed.
- */
- if (hl_cs_cmpl->encaps_signals)
- kref_put(&hl_cs_cmpl->encaps_sig_hdl->refcount,
- hl_encaps_release_handle_and_put_ctx);
- }
-
- if ((cs->type == CS_TYPE_WAIT || cs->type == CS_TYPE_COLLECTIVE_WAIT) && cs->encaps_signals)
- kref_put(&cs->encaps_sig_hdl->refcount, hl_encaps_release_handle_and_put_ctx);
-
-out:
- /* Must be called before hl_ctx_put because inside we use ctx to get
- * the device
- */
- hl_debugfs_remove_cs(cs);
-
- hdev->shadow_cs_queue[cs->sequence & (hdev->asic_prop.max_pending_cs - 1)] = NULL;
-
- /* We need to mark an error for not submitted because in that case
- * the hl fence release flow is different. Mainly, we don't need
- * to handle hw_sob for signal/wait
- */
- if (cs->timedout)
- cs->fence->error = -ETIMEDOUT;
- else if (cs->aborted)
- cs->fence->error = -EIO;
- else if (!cs->submitted)
- cs->fence->error = -EBUSY;
-
- if (unlikely(cs->skip_reset_on_timeout)) {
- dev_err(hdev->dev,
- "Command submission %llu completed after %llu (s)\n",
- cs->sequence,
- div_u64(jiffies - cs->submission_time_jiffies, HZ));
- }
-
- if (cs->timestamp) {
- cs->fence->timestamp = ktime_get();
- hl_push_cs_outcome(hdev, &cs->ctx->outcome_store, cs->sequence,
- cs->fence->timestamp, cs->fence->error);
- }
-
- hl_ctx_put(cs->ctx);
-
- complete_all(&cs->fence->completion);
- complete_multi_cs(hdev, cs);
-
- cs_release_sob_reset_handler(hdev, cs, hl_cs_cmpl);
-
- hl_fence_put(cs->fence);
-
- kfree(cs->jobs_in_queue_cnt);
- kfree(cs);
-}
-
-static void cs_timedout(struct work_struct *work)
-{
- struct hl_device *hdev;
- u64 event_mask = 0x0;
- int rc;
- struct hl_cs *cs = container_of(work, struct hl_cs,
- work_tdr.work);
- bool skip_reset_on_timeout = cs->skip_reset_on_timeout, device_reset = false;
-
- rc = cs_get_unless_zero(cs);
- if (!rc)
- return;
-
- if ((!cs->submitted) || (cs->completed)) {
- cs_put(cs);
- return;
- }
-
- hdev = cs->ctx->hdev;
-
- if (likely(!skip_reset_on_timeout)) {
- if (hdev->reset_on_lockup)
- device_reset = true;
- else
- hdev->reset_info.needs_reset = true;
-
- /* Mark the CS is timed out so we won't try to cancel its TDR */
- cs->timedout = true;
- }
-
- /* Save only the first CS timeout parameters */
- rc = atomic_cmpxchg(&hdev->captured_err_info.cs_timeout.write_enable, 1, 0);
- if (rc) {
- hdev->captured_err_info.cs_timeout.timestamp = ktime_get();
- hdev->captured_err_info.cs_timeout.seq = cs->sequence;
- event_mask |= HL_NOTIFIER_EVENT_CS_TIMEOUT;
- }
-
- switch (cs->type) {
- case CS_TYPE_SIGNAL:
- dev_err(hdev->dev,
- "Signal command submission %llu has not finished in time!\n",
- cs->sequence);
- break;
-
- case CS_TYPE_WAIT:
- dev_err(hdev->dev,
- "Wait command submission %llu has not finished in time!\n",
- cs->sequence);
- break;
-
- case CS_TYPE_COLLECTIVE_WAIT:
- dev_err(hdev->dev,
- "Collective Wait command submission %llu has not finished in time!\n",
- cs->sequence);
- break;
-
- default:
- dev_err(hdev->dev,
- "Command submission %llu has not finished in time!\n",
- cs->sequence);
- break;
- }
-
- rc = hl_state_dump(hdev);
- if (rc)
- dev_err(hdev->dev, "Error during system state dump %d\n", rc);
-
- cs_put(cs);
-
- if (device_reset) {
- event_mask |= HL_NOTIFIER_EVENT_DEVICE_RESET;
- hl_device_cond_reset(hdev, HL_DRV_RESET_TDR, event_mask);
- } else if (event_mask) {
- hl_notifier_event_send_all(hdev, event_mask);
- }
-}
-
-static int allocate_cs(struct hl_device *hdev, struct hl_ctx *ctx,
- enum hl_cs_type cs_type, u64 user_sequence,
- struct hl_cs **cs_new, u32 flags, u32 timeout)
-{
- struct hl_cs_counters_atomic *cntr;
- struct hl_fence *other = NULL;
- struct hl_cs_compl *cs_cmpl;
- struct hl_cs *cs;
- int rc;
-
- cntr = &hdev->aggregated_cs_counters;
-
- cs = kzalloc(sizeof(*cs), GFP_ATOMIC);
- if (!cs)
- cs = kzalloc(sizeof(*cs), GFP_KERNEL);
-
- if (!cs) {
- atomic64_inc(&ctx->cs_counters.out_of_mem_drop_cnt);
- atomic64_inc(&cntr->out_of_mem_drop_cnt);
- return -ENOMEM;
- }
-
- /* increment refcnt for context */
- hl_ctx_get(ctx);
-
- cs->ctx = ctx;
- cs->submitted = false;
- cs->completed = false;
- cs->type = cs_type;
- cs->timestamp = !!(flags & HL_CS_FLAGS_TIMESTAMP);
- cs->encaps_signals = !!(flags & HL_CS_FLAGS_ENCAP_SIGNALS);
- cs->timeout_jiffies = timeout;
- cs->skip_reset_on_timeout =
- hdev->reset_info.skip_reset_on_timeout ||
- !!(flags & HL_CS_FLAGS_SKIP_RESET_ON_TIMEOUT);
- cs->submission_time_jiffies = jiffies;
- INIT_LIST_HEAD(&cs->job_list);
- INIT_DELAYED_WORK(&cs->work_tdr, cs_timedout);
- kref_init(&cs->refcount);
- spin_lock_init(&cs->job_lock);
-
- cs_cmpl = kzalloc(sizeof(*cs_cmpl), GFP_ATOMIC);
- if (!cs_cmpl)
- cs_cmpl = kzalloc(sizeof(*cs_cmpl), GFP_KERNEL);
-
- if (!cs_cmpl) {
- atomic64_inc(&ctx->cs_counters.out_of_mem_drop_cnt);
- atomic64_inc(&cntr->out_of_mem_drop_cnt);
- rc = -ENOMEM;
- goto free_cs;
- }
-
- cs->jobs_in_queue_cnt = kcalloc(hdev->asic_prop.max_queues,
- sizeof(*cs->jobs_in_queue_cnt), GFP_ATOMIC);
- if (!cs->jobs_in_queue_cnt)
- cs->jobs_in_queue_cnt = kcalloc(hdev->asic_prop.max_queues,
- sizeof(*cs->jobs_in_queue_cnt), GFP_KERNEL);
-
- if (!cs->jobs_in_queue_cnt) {
- atomic64_inc(&ctx->cs_counters.out_of_mem_drop_cnt);
- atomic64_inc(&cntr->out_of_mem_drop_cnt);
- rc = -ENOMEM;
- goto free_cs_cmpl;
- }
-
- cs_cmpl->hdev = hdev;
- cs_cmpl->type = cs->type;
- spin_lock_init(&cs_cmpl->lock);
- cs->fence = &cs_cmpl->base_fence;
-
- spin_lock(&ctx->cs_lock);
-
- cs_cmpl->cs_seq = ctx->cs_sequence;
- other = ctx->cs_pending[cs_cmpl->cs_seq &
- (hdev->asic_prop.max_pending_cs - 1)];
-
- if (other && !completion_done(&other->completion)) {
- /* If the following statement is true, it means we have reached
- * a point in which only part of the staged submission was
- * submitted and we don't have enough room in the 'cs_pending'
- * array for the rest of the submission.
- * This causes a deadlock because this CS will never be
- * completed as it depends on future CS's for completion.
- */
- if (other->cs_sequence == user_sequence)
- dev_crit_ratelimited(hdev->dev,
- "Staged CS %llu deadlock due to lack of resources",
- user_sequence);
-
- dev_dbg_ratelimited(hdev->dev,
- "Rejecting CS because of too many in-flights CS\n");
- atomic64_inc(&ctx->cs_counters.max_cs_in_flight_drop_cnt);
- atomic64_inc(&cntr->max_cs_in_flight_drop_cnt);
- rc = -EAGAIN;
- goto free_fence;
- }
-
- /* init hl_fence */
- hl_fence_init(&cs_cmpl->base_fence, cs_cmpl->cs_seq);
-
- cs->sequence = cs_cmpl->cs_seq;
-
- ctx->cs_pending[cs_cmpl->cs_seq &
- (hdev->asic_prop.max_pending_cs - 1)] =
- &cs_cmpl->base_fence;
- ctx->cs_sequence++;
-
- hl_fence_get(&cs_cmpl->base_fence);
-
- hl_fence_put(other);
-
- spin_unlock(&ctx->cs_lock);
-
- *cs_new = cs;
-
- return 0;
-
-free_fence:
- spin_unlock(&ctx->cs_lock);
- kfree(cs->jobs_in_queue_cnt);
-free_cs_cmpl:
- kfree(cs_cmpl);
-free_cs:
- kfree(cs);
- hl_ctx_put(ctx);
- return rc;
-}
-
-static void cs_rollback(struct hl_device *hdev, struct hl_cs *cs)
-{
- struct hl_cs_job *job, *tmp;
-
- staged_cs_put(hdev, cs);
-
- list_for_each_entry_safe(job, tmp, &cs->job_list, cs_node)
- hl_complete_job(hdev, job);
-}
-
-/*
- * release_reserved_encaps_signals() - release reserved encapsulated signals.
- * @hdev: pointer to habanalabs device structure
- *
- * Release reserved encapsulated signals which weren't un-reserved, or for which a CS with
- * encapsulated signals wasn't submitted and thus weren't released as part of CS roll-back.
- * For these signals need also to put the refcount of the H/W SOB which was taken at the
- * reservation.
- */
-static void release_reserved_encaps_signals(struct hl_device *hdev)
-{
- struct hl_ctx *ctx = hl_get_compute_ctx(hdev);
- struct hl_cs_encaps_sig_handle *handle;
- struct hl_encaps_signals_mgr *mgr;
- u32 id;
-
- if (!ctx)
- return;
-
- mgr = &ctx->sig_mgr;
-
- idr_for_each_entry(&mgr->handles, handle, id)
- if (handle->cs_seq == ULLONG_MAX)
- kref_put(&handle->refcount, hl_encaps_release_handle_and_put_sob_ctx);
-
- hl_ctx_put(ctx);
-}
-
-void hl_cs_rollback_all(struct hl_device *hdev, bool skip_wq_flush)
-{
- int i;
- struct hl_cs *cs, *tmp;
-
- if (!skip_wq_flush) {
- flush_workqueue(hdev->ts_free_obj_wq);
-
- /* flush all completions before iterating over the CS mirror list in
- * order to avoid a race with the release functions
- */
- for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++)
- flush_workqueue(hdev->cq_wq[i]);
-
- flush_workqueue(hdev->cs_cmplt_wq);
- }
-
- /* Make sure we don't have leftovers in the CS mirror list */
- list_for_each_entry_safe(cs, tmp, &hdev->cs_mirror_list, mirror_node) {
- cs_get(cs);
- cs->aborted = true;
- dev_warn_ratelimited(hdev->dev, "Killing CS %d.%llu\n",
- cs->ctx->asid, cs->sequence);
- cs_rollback(hdev, cs);
- cs_put(cs);
- }
-
- force_complete_multi_cs(hdev);
-
- release_reserved_encaps_signals(hdev);
-}
-
-static void
-wake_pending_user_interrupt_threads(struct hl_user_interrupt *interrupt)
-{
- struct hl_user_pending_interrupt *pend, *temp;
- unsigned long flags;
-
- spin_lock_irqsave(&interrupt->wait_list_lock, flags);
- list_for_each_entry_safe(pend, temp, &interrupt->wait_list_head, wait_list_node) {
- if (pend->ts_reg_info.buf) {
- list_del(&pend->wait_list_node);
- hl_mmap_mem_buf_put(pend->ts_reg_info.buf);
- hl_cb_put(pend->ts_reg_info.cq_cb);
- } else {
- pend->fence.error = -EIO;
- complete_all(&pend->fence.completion);
- }
- }
- spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
-}
-
-void hl_release_pending_user_interrupts(struct hl_device *hdev)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct hl_user_interrupt *interrupt;
- int i;
-
- if (!prop->user_interrupt_count)
- return;
-
- /* We iterate through the user interrupt requests and waking up all
- * user threads waiting for interrupt completion. We iterate the
- * list under a lock, this is why all user threads, once awake,
- * will wait on the same lock and will release the waiting object upon
- * unlock.
- */
-
- for (i = 0 ; i < prop->user_interrupt_count ; i++) {
- interrupt = &hdev->user_interrupt[i];
- wake_pending_user_interrupt_threads(interrupt);
- }
-
- interrupt = &hdev->common_user_cq_interrupt;
- wake_pending_user_interrupt_threads(interrupt);
-
- interrupt = &hdev->common_decoder_interrupt;
- wake_pending_user_interrupt_threads(interrupt);
-}
-
-static void force_complete_cs(struct hl_device *hdev)
-{
- struct hl_cs *cs;
-
- spin_lock(&hdev->cs_mirror_lock);
-
- list_for_each_entry(cs, &hdev->cs_mirror_list, mirror_node) {
- cs->fence->error = -EIO;
- complete_all(&cs->fence->completion);
- }
-
- spin_unlock(&hdev->cs_mirror_lock);
-}
-
-void hl_abort_waitings_for_completion(struct hl_device *hdev)
-{
- force_complete_cs(hdev);
- force_complete_multi_cs(hdev);
- hl_release_pending_user_interrupts(hdev);
-}
-
-static void job_wq_completion(struct work_struct *work)
-{
- struct hl_cs_job *job = container_of(work, struct hl_cs_job,
- finish_work);
- struct hl_cs *cs = job->cs;
- struct hl_device *hdev = cs->ctx->hdev;
-
- /* job is no longer needed */
- hl_complete_job(hdev, job);
-}
-
-static void cs_completion(struct work_struct *work)
-{
- struct hl_cs *cs = container_of(work, struct hl_cs, finish_work);
- struct hl_device *hdev = cs->ctx->hdev;
- struct hl_cs_job *job, *tmp;
-
- list_for_each_entry_safe(job, tmp, &cs->job_list, cs_node)
- hl_complete_job(hdev, job);
-}
-
-static int validate_queue_index(struct hl_device *hdev,
- struct hl_cs_chunk *chunk,
- enum hl_queue_type *queue_type,
- bool *is_kernel_allocated_cb)
-{
- struct asic_fixed_properties *asic = &hdev->asic_prop;
- struct hw_queue_properties *hw_queue_prop;
-
- /* This must be checked here to prevent out-of-bounds access to
- * hw_queues_props array
- */
- if (chunk->queue_index >= asic->max_queues) {
- dev_err(hdev->dev, "Queue index %d is invalid\n",
- chunk->queue_index);
- return -EINVAL;
- }
-
- hw_queue_prop = &asic->hw_queues_props[chunk->queue_index];
-
- if (hw_queue_prop->type == QUEUE_TYPE_NA) {
- dev_err(hdev->dev, "Queue index %d is not applicable\n",
- chunk->queue_index);
- return -EINVAL;
- }
-
- if (hw_queue_prop->binned) {
- dev_err(hdev->dev, "Queue index %d is binned out\n",
- chunk->queue_index);
- return -EINVAL;
- }
-
- if (hw_queue_prop->driver_only) {
- dev_err(hdev->dev,
- "Queue index %d is restricted for the kernel driver\n",
- chunk->queue_index);
- return -EINVAL;
- }
-
- /* When hw queue type isn't QUEUE_TYPE_HW,
- * USER_ALLOC_CB flag shall be referred as "don't care".
- */
- if (hw_queue_prop->type == QUEUE_TYPE_HW) {
- if (chunk->cs_chunk_flags & HL_CS_CHUNK_FLAGS_USER_ALLOC_CB) {
- if (!(hw_queue_prop->cb_alloc_flags & CB_ALLOC_USER)) {
- dev_err(hdev->dev,
- "Queue index %d doesn't support user CB\n",
- chunk->queue_index);
- return -EINVAL;
- }
-
- *is_kernel_allocated_cb = false;
- } else {
- if (!(hw_queue_prop->cb_alloc_flags &
- CB_ALLOC_KERNEL)) {
- dev_err(hdev->dev,
- "Queue index %d doesn't support kernel CB\n",
- chunk->queue_index);
- return -EINVAL;
- }
-
- *is_kernel_allocated_cb = true;
- }
- } else {
- *is_kernel_allocated_cb = !!(hw_queue_prop->cb_alloc_flags
- & CB_ALLOC_KERNEL);
- }
-
- *queue_type = hw_queue_prop->type;
- return 0;
-}
-
-static struct hl_cb *get_cb_from_cs_chunk(struct hl_device *hdev,
- struct hl_mem_mgr *mmg,
- struct hl_cs_chunk *chunk)
-{
- struct hl_cb *cb;
-
- cb = hl_cb_get(mmg, chunk->cb_handle);
- if (!cb) {
- dev_err(hdev->dev, "CB handle 0x%llx invalid\n", chunk->cb_handle);
- return NULL;
- }
-
- if ((chunk->cb_size < 8) || (chunk->cb_size > cb->size)) {
- dev_err(hdev->dev, "CB size %u invalid\n", chunk->cb_size);
- goto release_cb;
- }
-
- atomic_inc(&cb->cs_cnt);
-
- return cb;
-
-release_cb:
- hl_cb_put(cb);
- return NULL;
-}
-
-struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev,
- enum hl_queue_type queue_type, bool is_kernel_allocated_cb)
-{
- struct hl_cs_job *job;
-
- job = kzalloc(sizeof(*job), GFP_ATOMIC);
- if (!job)
- job = kzalloc(sizeof(*job), GFP_KERNEL);
-
- if (!job)
- return NULL;
-
- kref_init(&job->refcount);
- job->queue_type = queue_type;
- job->is_kernel_allocated_cb = is_kernel_allocated_cb;
-
- if (is_cb_patched(hdev, job))
- INIT_LIST_HEAD(&job->userptr_list);
-
- if (job->queue_type == QUEUE_TYPE_EXT)
- INIT_WORK(&job->finish_work, job_wq_completion);
-
- return job;
-}
-
-static enum hl_cs_type hl_cs_get_cs_type(u32 cs_type_flags)
-{
- if (cs_type_flags & HL_CS_FLAGS_SIGNAL)
- return CS_TYPE_SIGNAL;
- else if (cs_type_flags & HL_CS_FLAGS_WAIT)
- return CS_TYPE_WAIT;
- else if (cs_type_flags & HL_CS_FLAGS_COLLECTIVE_WAIT)
- return CS_TYPE_COLLECTIVE_WAIT;
- else if (cs_type_flags & HL_CS_FLAGS_RESERVE_SIGNALS_ONLY)
- return CS_RESERVE_SIGNALS;
- else if (cs_type_flags & HL_CS_FLAGS_UNRESERVE_SIGNALS_ONLY)
- return CS_UNRESERVE_SIGNALS;
- else if (cs_type_flags & HL_CS_FLAGS_ENGINE_CORE_COMMAND)
- return CS_TYPE_ENGINE_CORE;
- else
- return CS_TYPE_DEFAULT;
-}
-
-static int hl_cs_sanity_checks(struct hl_fpriv *hpriv, union hl_cs_args *args)
-{
- struct hl_device *hdev = hpriv->hdev;
- struct hl_ctx *ctx = hpriv->ctx;
- u32 cs_type_flags, num_chunks;
- enum hl_device_status status;
- enum hl_cs_type cs_type;
- bool is_sync_stream;
-
- if (!hl_device_operational(hdev, &status)) {
- return -EBUSY;
- }
-
- if ((args->in.cs_flags & HL_CS_FLAGS_STAGED_SUBMISSION) &&
- !hdev->supports_staged_submission) {
- dev_err(hdev->dev, "staged submission not supported");
- return -EPERM;
- }
-
- cs_type_flags = args->in.cs_flags & HL_CS_FLAGS_TYPE_MASK;
-
- if (unlikely(cs_type_flags && !is_power_of_2(cs_type_flags))) {
- dev_err(hdev->dev,
- "CS type flags are mutually exclusive, context %d\n",
- ctx->asid);
- return -EINVAL;
- }
-
- cs_type = hl_cs_get_cs_type(cs_type_flags);
- num_chunks = args->in.num_chunks_execute;
-
- is_sync_stream = (cs_type == CS_TYPE_SIGNAL || cs_type == CS_TYPE_WAIT ||
- cs_type == CS_TYPE_COLLECTIVE_WAIT);
-
- if (unlikely(is_sync_stream && !hdev->supports_sync_stream)) {
- dev_err(hdev->dev, "Sync stream CS is not supported\n");
- return -EINVAL;
- }
-
- if (cs_type == CS_TYPE_DEFAULT) {
- if (!num_chunks) {
- dev_err(hdev->dev, "Got execute CS with 0 chunks, context %d\n", ctx->asid);
- return -EINVAL;
- }
- } else if (is_sync_stream && num_chunks != 1) {
- dev_err(hdev->dev,
- "Sync stream CS mandates one chunk only, context %d\n",
- ctx->asid);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int hl_cs_copy_chunk_array(struct hl_device *hdev,
- struct hl_cs_chunk **cs_chunk_array,
- void __user *chunks, u32 num_chunks,
- struct hl_ctx *ctx)
-{
- u32 size_to_copy;
-
- if (num_chunks > HL_MAX_JOBS_PER_CS) {
- atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
- atomic64_inc(&hdev->aggregated_cs_counters.validation_drop_cnt);
- dev_err(hdev->dev,
- "Number of chunks can NOT be larger than %d\n",
- HL_MAX_JOBS_PER_CS);
- return -EINVAL;
- }
-
- *cs_chunk_array = kmalloc_array(num_chunks, sizeof(**cs_chunk_array),
- GFP_ATOMIC);
- if (!*cs_chunk_array)
- *cs_chunk_array = kmalloc_array(num_chunks,
- sizeof(**cs_chunk_array), GFP_KERNEL);
- if (!*cs_chunk_array) {
- atomic64_inc(&ctx->cs_counters.out_of_mem_drop_cnt);
- atomic64_inc(&hdev->aggregated_cs_counters.out_of_mem_drop_cnt);
- return -ENOMEM;
- }
-
- size_to_copy = num_chunks * sizeof(struct hl_cs_chunk);
- if (copy_from_user(*cs_chunk_array, chunks, size_to_copy)) {
- atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
- atomic64_inc(&hdev->aggregated_cs_counters.validation_drop_cnt);
- dev_err(hdev->dev, "Failed to copy cs chunk array from user\n");
- kfree(*cs_chunk_array);
- return -EFAULT;
- }
-
- return 0;
-}
-
-static int cs_staged_submission(struct hl_device *hdev, struct hl_cs *cs,
- u64 sequence, u32 flags,
- u32 encaps_signal_handle)
-{
- if (!(flags & HL_CS_FLAGS_STAGED_SUBMISSION))
- return 0;
-
- cs->staged_last = !!(flags & HL_CS_FLAGS_STAGED_SUBMISSION_LAST);
- cs->staged_first = !!(flags & HL_CS_FLAGS_STAGED_SUBMISSION_FIRST);
-
- if (cs->staged_first) {
- /* Staged CS sequence is the first CS sequence */
- INIT_LIST_HEAD(&cs->staged_cs_node);
- cs->staged_sequence = cs->sequence;
-
- if (cs->encaps_signals)
- cs->encaps_sig_hdl_id = encaps_signal_handle;
- } else {
- /* User sequence will be validated in 'hl_hw_queue_schedule_cs'
- * under the cs_mirror_lock
- */
- cs->staged_sequence = sequence;
- }
-
- /* Increment CS reference if needed */
- staged_cs_get(hdev, cs);
-
- cs->staged_cs = true;
-
- return 0;
-}
-
-static u32 get_stream_master_qid_mask(struct hl_device *hdev, u32 qid)
-{
- int i;
-
- for (i = 0; i < hdev->stream_master_qid_arr_size; i++)
- if (qid == hdev->stream_master_qid_arr[i])
- return BIT(i);
-
- return 0;
-}
-
-static int cs_ioctl_default(struct hl_fpriv *hpriv, void __user *chunks,
- u32 num_chunks, u64 *cs_seq, u32 flags,
- u32 encaps_signals_handle, u32 timeout,
- u16 *signal_initial_sob_count)
-{
- bool staged_mid, int_queues_only = true, using_hw_queues = false;
- struct hl_device *hdev = hpriv->hdev;
- struct hl_cs_chunk *cs_chunk_array;
- struct hl_cs_counters_atomic *cntr;
- struct hl_ctx *ctx = hpriv->ctx;
- struct hl_cs_job *job;
- struct hl_cs *cs;
- struct hl_cb *cb;
- u64 user_sequence;
- u8 stream_master_qid_map = 0;
- int rc, i;
-
- cntr = &hdev->aggregated_cs_counters;
- user_sequence = *cs_seq;
- *cs_seq = ULLONG_MAX;
-
- rc = hl_cs_copy_chunk_array(hdev, &cs_chunk_array, chunks, num_chunks,
- hpriv->ctx);
- if (rc)
- goto out;
-
- if ((flags & HL_CS_FLAGS_STAGED_SUBMISSION) &&
- !(flags & HL_CS_FLAGS_STAGED_SUBMISSION_FIRST))
- staged_mid = true;
- else
- staged_mid = false;
-
- rc = allocate_cs(hdev, hpriv->ctx, CS_TYPE_DEFAULT,
- staged_mid ? user_sequence : ULLONG_MAX, &cs, flags,
- timeout);
- if (rc)
- goto free_cs_chunk_array;
-
- *cs_seq = cs->sequence;
-
- hl_debugfs_add_cs(cs);
-
- rc = cs_staged_submission(hdev, cs, user_sequence, flags,
- encaps_signals_handle);
- if (rc)
- goto free_cs_object;
-
- /* If this is a staged submission we must return the staged sequence
- * rather than the internal CS sequence
- */
- if (cs->staged_cs)
- *cs_seq = cs->staged_sequence;
-
- /* Validate ALL the CS chunks before submitting the CS */
- for (i = 0 ; i < num_chunks ; i++) {
- struct hl_cs_chunk *chunk = &cs_chunk_array[i];
- enum hl_queue_type queue_type;
- bool is_kernel_allocated_cb;
-
- rc = validate_queue_index(hdev, chunk, &queue_type,
- &is_kernel_allocated_cb);
- if (rc) {
- atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
- atomic64_inc(&cntr->validation_drop_cnt);
- goto free_cs_object;
- }
-
- if (is_kernel_allocated_cb) {
- cb = get_cb_from_cs_chunk(hdev, &hpriv->mem_mgr, chunk);
- if (!cb) {
- atomic64_inc(
- &ctx->cs_counters.validation_drop_cnt);
- atomic64_inc(&cntr->validation_drop_cnt);
- rc = -EINVAL;
- goto free_cs_object;
- }
- } else {
- cb = (struct hl_cb *) (uintptr_t) chunk->cb_handle;
- }
-
- if (queue_type == QUEUE_TYPE_EXT ||
- queue_type == QUEUE_TYPE_HW) {
- int_queues_only = false;
-
- /*
- * store which stream are being used for external/HW
- * queues of this CS
- */
- if (hdev->supports_wait_for_multi_cs)
- stream_master_qid_map |=
- get_stream_master_qid_mask(hdev,
- chunk->queue_index);
- }
-
- if (queue_type == QUEUE_TYPE_HW)
- using_hw_queues = true;
-
- job = hl_cs_allocate_job(hdev, queue_type,
- is_kernel_allocated_cb);
- if (!job) {
- atomic64_inc(&ctx->cs_counters.out_of_mem_drop_cnt);
- atomic64_inc(&cntr->out_of_mem_drop_cnt);
- dev_err(hdev->dev, "Failed to allocate a new job\n");
- rc = -ENOMEM;
- if (is_kernel_allocated_cb)
- goto release_cb;
-
- goto free_cs_object;
- }
-
- job->id = i + 1;
- job->cs = cs;
- job->user_cb = cb;
- job->user_cb_size = chunk->cb_size;
- job->hw_queue_id = chunk->queue_index;
-
- cs->jobs_in_queue_cnt[job->hw_queue_id]++;
- cs->jobs_cnt++;
-
- list_add_tail(&job->cs_node, &cs->job_list);
-
- /*
- * Increment CS reference. When CS reference is 0, CS is
- * done and can be signaled to user and free all its resources
- * Only increment for JOB on external or H/W queues, because
- * only for those JOBs we get completion
- */
- if (cs_needs_completion(cs) &&
- (job->queue_type == QUEUE_TYPE_EXT ||
- job->queue_type == QUEUE_TYPE_HW))
- cs_get(cs);
-
- hl_debugfs_add_job(hdev, job);
-
- rc = cs_parser(hpriv, job);
- if (rc) {
- atomic64_inc(&ctx->cs_counters.parsing_drop_cnt);
- atomic64_inc(&cntr->parsing_drop_cnt);
- dev_err(hdev->dev,
- "Failed to parse JOB %d.%llu.%d, err %d, rejecting the CS\n",
- cs->ctx->asid, cs->sequence, job->id, rc);
- goto free_cs_object;
- }
- }
-
- /* We allow a CS with any queue type combination as long as it does
- * not get a completion
- */
- if (int_queues_only && cs_needs_completion(cs)) {
- atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
- atomic64_inc(&cntr->validation_drop_cnt);
- dev_err(hdev->dev,
- "Reject CS %d.%llu since it contains only internal queues jobs and needs completion\n",
- cs->ctx->asid, cs->sequence);
- rc = -EINVAL;
- goto free_cs_object;
- }
-
- if (using_hw_queues)
- INIT_WORK(&cs->finish_work, cs_completion);
-
- /*
- * store the (external/HW queues) streams used by the CS in the
- * fence object for multi-CS completion
- */
- if (hdev->supports_wait_for_multi_cs)
- cs->fence->stream_master_qid_map = stream_master_qid_map;
-
- rc = hl_hw_queue_schedule_cs(cs);
- if (rc) {
- if (rc != -EAGAIN)
- dev_err(hdev->dev,
- "Failed to submit CS %d.%llu to H/W queues, error %d\n",
- cs->ctx->asid, cs->sequence, rc);
- goto free_cs_object;
- }
-
- *signal_initial_sob_count = cs->initial_sob_count;
-
- rc = HL_CS_STATUS_SUCCESS;
- goto put_cs;
-
-release_cb:
- atomic_dec(&cb->cs_cnt);
- hl_cb_put(cb);
-free_cs_object:
- cs_rollback(hdev, cs);
- *cs_seq = ULLONG_MAX;
- /* The path below is both for good and erroneous exits */
-put_cs:
- /* We finished with the CS in this function, so put the ref */
- cs_put(cs);
-free_cs_chunk_array:
- kfree(cs_chunk_array);
-out:
- return rc;
-}
-
-static int hl_cs_ctx_switch(struct hl_fpriv *hpriv, union hl_cs_args *args,
- u64 *cs_seq)
-{
- struct hl_device *hdev = hpriv->hdev;
- struct hl_ctx *ctx = hpriv->ctx;
- bool need_soft_reset = false;
- int rc = 0, do_ctx_switch = 0;
- void __user *chunks;
- u32 num_chunks, tmp;
- u16 sob_count;
- int ret;
-
- if (hdev->supports_ctx_switch)
- do_ctx_switch = atomic_cmpxchg(&ctx->thread_ctx_switch_token, 1, 0);
-
- if (do_ctx_switch || (args->in.cs_flags & HL_CS_FLAGS_FORCE_RESTORE)) {
- mutex_lock(&hpriv->restore_phase_mutex);
-
- if (do_ctx_switch) {
- rc = hdev->asic_funcs->context_switch(hdev, ctx->asid);
- if (rc) {
- dev_err_ratelimited(hdev->dev,
- "Failed to switch to context %d, rejecting CS! %d\n",
- ctx->asid, rc);
- /*
- * If we timedout, or if the device is not IDLE
- * while we want to do context-switch (-EBUSY),
- * we need to soft-reset because QMAN is
- * probably stuck. However, we can't call to
- * reset here directly because of deadlock, so
- * need to do it at the very end of this
- * function
- */
- if ((rc == -ETIMEDOUT) || (rc == -EBUSY))
- need_soft_reset = true;
- mutex_unlock(&hpriv->restore_phase_mutex);
- goto out;
- }
- }
-
- hdev->asic_funcs->restore_phase_topology(hdev);
-
- chunks = (void __user *) (uintptr_t) args->in.chunks_restore;
- num_chunks = args->in.num_chunks_restore;
-
- if (!num_chunks) {
- dev_dbg(hdev->dev,
- "Need to run restore phase but restore CS is empty\n");
- rc = 0;
- } else {
- rc = cs_ioctl_default(hpriv, chunks, num_chunks,
- cs_seq, 0, 0, hdev->timeout_jiffies, &sob_count);
- }
-
- mutex_unlock(&hpriv->restore_phase_mutex);
-
- if (rc) {
- dev_err(hdev->dev,
- "Failed to submit restore CS for context %d (%d)\n",
- ctx->asid, rc);
- goto out;
- }
-
- /* Need to wait for restore completion before execution phase */
- if (num_chunks) {
- enum hl_cs_wait_status status;
-wait_again:
- ret = _hl_cs_wait_ioctl(hdev, ctx,
- jiffies_to_usecs(hdev->timeout_jiffies),
- *cs_seq, &status, NULL);
- if (ret) {
- if (ret == -ERESTARTSYS) {
- usleep_range(100, 200);
- goto wait_again;
- }
-
- dev_err(hdev->dev,
- "Restore CS for context %d failed to complete %d\n",
- ctx->asid, ret);
- rc = -ENOEXEC;
- goto out;
- }
- }
-
- if (hdev->supports_ctx_switch)
- ctx->thread_ctx_switch_wait_token = 1;
-
- } else if (hdev->supports_ctx_switch && !ctx->thread_ctx_switch_wait_token) {
- rc = hl_poll_timeout_memory(hdev,
- &ctx->thread_ctx_switch_wait_token, tmp, (tmp == 1),
- 100, jiffies_to_usecs(hdev->timeout_jiffies), false);
-
- if (rc == -ETIMEDOUT) {
- dev_err(hdev->dev,
- "context switch phase timeout (%d)\n", tmp);
- goto out;
- }
- }
-
-out:
- if ((rc == -ETIMEDOUT || rc == -EBUSY) && (need_soft_reset))
- hl_device_reset(hdev, 0);
-
- return rc;
-}
-
-/*
- * hl_cs_signal_sob_wraparound_handler: handle SOB value wrapaound case.
- * if the SOB value reaches the max value move to the other SOB reserved
- * to the queue.
- * @hdev: pointer to device structure
- * @q_idx: stream queue index
- * @hw_sob: the H/W SOB used in this signal CS.
- * @count: signals count
- * @encaps_sig: tells whether it's reservation for encaps signals or not.
- *
- * Note that this function must be called while hw_queues_lock is taken.
- */
-int hl_cs_signal_sob_wraparound_handler(struct hl_device *hdev, u32 q_idx,
- struct hl_hw_sob **hw_sob, u32 count, bool encaps_sig)
-
-{
- struct hl_sync_stream_properties *prop;
- struct hl_hw_sob *sob = *hw_sob, *other_sob;
- u8 other_sob_offset;
-
- prop = &hdev->kernel_queues[q_idx].sync_stream_prop;
-
- hw_sob_get(sob);
-
- /* check for wraparound */
- if (prop->next_sob_val + count >= HL_MAX_SOB_VAL) {
- /*
- * Decrement as we reached the max value.
- * The release function won't be called here as we've
- * just incremented the refcount right before calling this
- * function.
- */
- hw_sob_put_err(sob);
-
- /*
- * check the other sob value, if it still in use then fail
- * otherwise make the switch
- */
- other_sob_offset = (prop->curr_sob_offset + 1) % HL_RSVD_SOBS;
- other_sob = &prop->hw_sob[other_sob_offset];
-
- if (kref_read(&other_sob->kref) != 1) {
- dev_err(hdev->dev, "error: Cannot switch SOBs q_idx: %d\n",
- q_idx);
- return -EINVAL;
- }
-
- /*
- * next_sob_val always points to the next available signal
- * in the sob, so in encaps signals it will be the next one
- * after reserving the required amount.
- */
- if (encaps_sig)
- prop->next_sob_val = count + 1;
- else
- prop->next_sob_val = count;
-
- /* only two SOBs are currently in use */
- prop->curr_sob_offset = other_sob_offset;
- *hw_sob = other_sob;
-
- /*
- * check if other_sob needs reset, then do it before using it
- * for the reservation or the next signal cs.
- * we do it here, and for both encaps and regular signal cs
- * cases in order to avoid possible races of two kref_put
- * of the sob which can occur at the same time if we move the
- * sob reset(kref_put) to cs_do_release function.
- * in addition, if we have combination of cs signal and
- * encaps, and at the point we need to reset the sob there was
- * no more reservations and only signal cs keep coming,
- * in such case we need signal_cs to put the refcount and
- * reset the sob.
- */
- if (other_sob->need_reset)
- hw_sob_put(other_sob);
-
- if (encaps_sig) {
- /* set reset indication for the sob */
- sob->need_reset = true;
- hw_sob_get(other_sob);
- }
-
- dev_dbg(hdev->dev, "switched to SOB %d, q_idx: %d\n",
- prop->curr_sob_offset, q_idx);
- } else {
- prop->next_sob_val += count;
- }
-
- return 0;
-}
-
-static int cs_ioctl_extract_signal_seq(struct hl_device *hdev,
- struct hl_cs_chunk *chunk, u64 *signal_seq, struct hl_ctx *ctx,
- bool encaps_signals)
-{
- u64 *signal_seq_arr = NULL;
- u32 size_to_copy, signal_seq_arr_len;
- int rc = 0;
-
- if (encaps_signals) {
- *signal_seq = chunk->encaps_signal_seq;
- return 0;
- }
-
- signal_seq_arr_len = chunk->num_signal_seq_arr;
-
- /* currently only one signal seq is supported */
- if (signal_seq_arr_len != 1) {
- atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
- atomic64_inc(&hdev->aggregated_cs_counters.validation_drop_cnt);
- dev_err(hdev->dev,
- "Wait for signal CS supports only one signal CS seq\n");
- return -EINVAL;
- }
-
- signal_seq_arr = kmalloc_array(signal_seq_arr_len,
- sizeof(*signal_seq_arr),
- GFP_ATOMIC);
- if (!signal_seq_arr)
- signal_seq_arr = kmalloc_array(signal_seq_arr_len,
- sizeof(*signal_seq_arr),
- GFP_KERNEL);
- if (!signal_seq_arr) {
- atomic64_inc(&ctx->cs_counters.out_of_mem_drop_cnt);
- atomic64_inc(&hdev->aggregated_cs_counters.out_of_mem_drop_cnt);
- return -ENOMEM;
- }
-
- size_to_copy = signal_seq_arr_len * sizeof(*signal_seq_arr);
- if (copy_from_user(signal_seq_arr,
- u64_to_user_ptr(chunk->signal_seq_arr),
- size_to_copy)) {
- atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
- atomic64_inc(&hdev->aggregated_cs_counters.validation_drop_cnt);
- dev_err(hdev->dev,
- "Failed to copy signal seq array from user\n");
- rc = -EFAULT;
- goto out;
- }
-
- /* currently it is guaranteed to have only one signal seq */
- *signal_seq = signal_seq_arr[0];
-
-out:
- kfree(signal_seq_arr);
-
- return rc;
-}
-
-static int cs_ioctl_signal_wait_create_jobs(struct hl_device *hdev,
- struct hl_ctx *ctx, struct hl_cs *cs,
- enum hl_queue_type q_type, u32 q_idx, u32 encaps_signal_offset)
-{
- struct hl_cs_counters_atomic *cntr;
- struct hl_cs_job *job;
- struct hl_cb *cb;
- u32 cb_size;
-
- cntr = &hdev->aggregated_cs_counters;
-
- job = hl_cs_allocate_job(hdev, q_type, true);
- if (!job) {
- atomic64_inc(&ctx->cs_counters.out_of_mem_drop_cnt);
- atomic64_inc(&cntr->out_of_mem_drop_cnt);
- dev_err(hdev->dev, "Failed to allocate a new job\n");
- return -ENOMEM;
- }
-
- if (cs->type == CS_TYPE_WAIT)
- cb_size = hdev->asic_funcs->get_wait_cb_size(hdev);
- else
- cb_size = hdev->asic_funcs->get_signal_cb_size(hdev);
-
- cb = hl_cb_kernel_create(hdev, cb_size,
- q_type == QUEUE_TYPE_HW && hdev->mmu_enable);
- if (!cb) {
- atomic64_inc(&ctx->cs_counters.out_of_mem_drop_cnt);
- atomic64_inc(&cntr->out_of_mem_drop_cnt);
- kfree(job);
- return -EFAULT;
- }
-
- job->id = 0;
- job->cs = cs;
- job->user_cb = cb;
- atomic_inc(&job->user_cb->cs_cnt);
- job->user_cb_size = cb_size;
- job->hw_queue_id = q_idx;
-
- if ((cs->type == CS_TYPE_WAIT || cs->type == CS_TYPE_COLLECTIVE_WAIT)
- && cs->encaps_signals)
- job->encaps_sig_wait_offset = encaps_signal_offset;
- /*
- * No need in parsing, user CB is the patched CB.
- * We call hl_cb_destroy() out of two reasons - we don't need the CB in
- * the CB idr anymore and to decrement its refcount as it was
- * incremented inside hl_cb_kernel_create().
- */
- job->patched_cb = job->user_cb;
- job->job_cb_size = job->user_cb_size;
- hl_cb_destroy(&hdev->kernel_mem_mgr, cb->buf->handle);
-
- /* increment refcount as for external queues we get completion */
- cs_get(cs);
-
- cs->jobs_in_queue_cnt[job->hw_queue_id]++;
- cs->jobs_cnt++;
-
- list_add_tail(&job->cs_node, &cs->job_list);
-
- hl_debugfs_add_job(hdev, job);
-
- return 0;
-}
-
-static int cs_ioctl_reserve_signals(struct hl_fpriv *hpriv,
- u32 q_idx, u32 count,
- u32 *handle_id, u32 *sob_addr,
- u32 *signals_count)
-{
- struct hw_queue_properties *hw_queue_prop;
- struct hl_sync_stream_properties *prop;
- struct hl_device *hdev = hpriv->hdev;
- struct hl_cs_encaps_sig_handle *handle;
- struct hl_encaps_signals_mgr *mgr;
- struct hl_hw_sob *hw_sob;
- int hdl_id;
- int rc = 0;
-
- if (count >= HL_MAX_SOB_VAL) {
- dev_err(hdev->dev, "signals count(%u) exceeds the max SOB value\n",
- count);
- rc = -EINVAL;
- goto out;
- }
-
- if (q_idx >= hdev->asic_prop.max_queues) {
- dev_err(hdev->dev, "Queue index %d is invalid\n",
- q_idx);
- rc = -EINVAL;
- goto out;
- }
-
- hw_queue_prop = &hdev->asic_prop.hw_queues_props[q_idx];
-
- if (!hw_queue_prop->supports_sync_stream) {
- dev_err(hdev->dev,
- "Queue index %d does not support sync stream operations\n",
- q_idx);
- rc = -EINVAL;
- goto out;
- }
-
- prop = &hdev->kernel_queues[q_idx].sync_stream_prop;
-
- handle = kzalloc(sizeof(*handle), GFP_KERNEL);
- if (!handle) {
- rc = -ENOMEM;
- goto out;
- }
-
- handle->count = count;
-
- hl_ctx_get(hpriv->ctx);
- handle->ctx = hpriv->ctx;
- mgr = &hpriv->ctx->sig_mgr;
-
- spin_lock(&mgr->lock);
- hdl_id = idr_alloc(&mgr->handles, handle, 1, 0, GFP_ATOMIC);
- spin_unlock(&mgr->lock);
-
- if (hdl_id < 0) {
- dev_err(hdev->dev, "Failed to allocate IDR for a new signal reservation\n");
- rc = -EINVAL;
- goto put_ctx;
- }
-
- handle->id = hdl_id;
- handle->q_idx = q_idx;
- handle->hdev = hdev;
- kref_init(&handle->refcount);
-
- hdev->asic_funcs->hw_queues_lock(hdev);
-
- hw_sob = &prop->hw_sob[prop->curr_sob_offset];
-
- /*
- * Increment the SOB value by count by user request
- * to reserve those signals
- * check if the signals amount to reserve is not exceeding the max sob
- * value, if yes then switch sob.
- */
- rc = hl_cs_signal_sob_wraparound_handler(hdev, q_idx, &hw_sob, count,
- true);
- if (rc) {
- dev_err(hdev->dev, "Failed to switch SOB\n");
- hdev->asic_funcs->hw_queues_unlock(hdev);
- rc = -EINVAL;
- goto remove_idr;
- }
- /* set the hw_sob to the handle after calling the sob wraparound handler
- * since sob could have changed.
- */
- handle->hw_sob = hw_sob;
-
- /* store the current sob value for unreserve validity check, and
- * signal offset support
- */
- handle->pre_sob_val = prop->next_sob_val - handle->count;
-
- handle->cs_seq = ULLONG_MAX;
-
- *signals_count = prop->next_sob_val;
- hdev->asic_funcs->hw_queues_unlock(hdev);
-
- *sob_addr = handle->hw_sob->sob_addr;
- *handle_id = hdl_id;
-
- dev_dbg(hdev->dev,
- "Signals reserved, sob_id: %d, sob addr: 0x%x, last sob_val: %u, q_idx: %d, hdl_id: %d\n",
- hw_sob->sob_id, handle->hw_sob->sob_addr,
- prop->next_sob_val - 1, q_idx, hdl_id);
- goto out;
-
-remove_idr:
- spin_lock(&mgr->lock);
- idr_remove(&mgr->handles, hdl_id);
- spin_unlock(&mgr->lock);
-
-put_ctx:
- hl_ctx_put(handle->ctx);
- kfree(handle);
-
-out:
- return rc;
-}
-
-static int cs_ioctl_unreserve_signals(struct hl_fpriv *hpriv, u32 handle_id)
-{
- struct hl_cs_encaps_sig_handle *encaps_sig_hdl;
- struct hl_sync_stream_properties *prop;
- struct hl_device *hdev = hpriv->hdev;
- struct hl_encaps_signals_mgr *mgr;
- struct hl_hw_sob *hw_sob;
- u32 q_idx, sob_addr;
- int rc = 0;
-
- mgr = &hpriv->ctx->sig_mgr;
-
- spin_lock(&mgr->lock);
- encaps_sig_hdl = idr_find(&mgr->handles, handle_id);
- if (encaps_sig_hdl) {
- dev_dbg(hdev->dev, "unreserve signals, handle: %u, SOB:0x%x, count: %u\n",
- handle_id, encaps_sig_hdl->hw_sob->sob_addr,
- encaps_sig_hdl->count);
-
- hdev->asic_funcs->hw_queues_lock(hdev);
-
- q_idx = encaps_sig_hdl->q_idx;
- prop = &hdev->kernel_queues[q_idx].sync_stream_prop;
- hw_sob = &prop->hw_sob[prop->curr_sob_offset];
- sob_addr = hdev->asic_funcs->get_sob_addr(hdev, hw_sob->sob_id);
-
- /* Check if sob_val got out of sync due to other
- * signal submission requests which were handled
- * between the reserve-unreserve calls or SOB switch
- * upon reaching SOB max value.
- */
- if (encaps_sig_hdl->pre_sob_val + encaps_sig_hdl->count
- != prop->next_sob_val ||
- sob_addr != encaps_sig_hdl->hw_sob->sob_addr) {
- dev_err(hdev->dev, "Cannot unreserve signals, SOB val ran out of sync, expected: %u, actual val: %u\n",
- encaps_sig_hdl->pre_sob_val,
- (prop->next_sob_val - encaps_sig_hdl->count));
-
- hdev->asic_funcs->hw_queues_unlock(hdev);
- rc = -EINVAL;
- goto out;
- }
-
- /*
- * Decrement the SOB value by count by user request
- * to unreserve those signals
- */
- prop->next_sob_val -= encaps_sig_hdl->count;
-
- hdev->asic_funcs->hw_queues_unlock(hdev);
-
- hw_sob_put(hw_sob);
-
- /* Release the id and free allocated memory of the handle */
- idr_remove(&mgr->handles, handle_id);
- hl_ctx_put(encaps_sig_hdl->ctx);
- kfree(encaps_sig_hdl);
- } else {
- rc = -EINVAL;
- dev_err(hdev->dev, "failed to unreserve signals, cannot find handler\n");
- }
-out:
- spin_unlock(&mgr->lock);
-
- return rc;
-}
-
-static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type,
- void __user *chunks, u32 num_chunks,
- u64 *cs_seq, u32 flags, u32 timeout,
- u32 *signal_sob_addr_offset, u16 *signal_initial_sob_count)
-{
- struct hl_cs_encaps_sig_handle *encaps_sig_hdl = NULL;
- bool handle_found = false, is_wait_cs = false,
- wait_cs_submitted = false,
- cs_encaps_signals = false;
- struct hl_cs_chunk *cs_chunk_array, *chunk;
- bool staged_cs_with_encaps_signals = false;
- struct hw_queue_properties *hw_queue_prop;
- struct hl_device *hdev = hpriv->hdev;
- struct hl_cs_compl *sig_waitcs_cmpl;
- u32 q_idx, collective_engine_id = 0;
- struct hl_cs_counters_atomic *cntr;
- struct hl_fence *sig_fence = NULL;
- struct hl_ctx *ctx = hpriv->ctx;
- enum hl_queue_type q_type;
- struct hl_cs *cs;
- u64 signal_seq;
- int rc;
-
- cntr = &hdev->aggregated_cs_counters;
- *cs_seq = ULLONG_MAX;
-
- rc = hl_cs_copy_chunk_array(hdev, &cs_chunk_array, chunks, num_chunks,
- ctx);
- if (rc)
- goto out;
-
- /* currently it is guaranteed to have only one chunk */
- chunk = &cs_chunk_array[0];
-
- if (chunk->queue_index >= hdev->asic_prop.max_queues) {
- atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
- atomic64_inc(&cntr->validation_drop_cnt);
- dev_err(hdev->dev, "Queue index %d is invalid\n",
- chunk->queue_index);
- rc = -EINVAL;
- goto free_cs_chunk_array;
- }
-
- q_idx = chunk->queue_index;
- hw_queue_prop = &hdev->asic_prop.hw_queues_props[q_idx];
- q_type = hw_queue_prop->type;
-
- if (!hw_queue_prop->supports_sync_stream) {
- atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
- atomic64_inc(&cntr->validation_drop_cnt);
- dev_err(hdev->dev,
- "Queue index %d does not support sync stream operations\n",
- q_idx);
- rc = -EINVAL;
- goto free_cs_chunk_array;
- }
-
- if (cs_type == CS_TYPE_COLLECTIVE_WAIT) {
- if (!(hw_queue_prop->collective_mode == HL_COLLECTIVE_MASTER)) {
- atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
- atomic64_inc(&cntr->validation_drop_cnt);
- dev_err(hdev->dev,
- "Queue index %d is invalid\n", q_idx);
- rc = -EINVAL;
- goto free_cs_chunk_array;
- }
-
- if (!hdev->nic_ports_mask) {
- atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
- atomic64_inc(&cntr->validation_drop_cnt);
- dev_err(hdev->dev,
- "Collective operations not supported when NIC ports are disabled");
- rc = -EINVAL;
- goto free_cs_chunk_array;
- }
-
- collective_engine_id = chunk->collective_engine_id;
- }
-
- is_wait_cs = !!(cs_type == CS_TYPE_WAIT ||
- cs_type == CS_TYPE_COLLECTIVE_WAIT);
-
- cs_encaps_signals = !!(flags & HL_CS_FLAGS_ENCAP_SIGNALS);
-
- if (is_wait_cs) {
- rc = cs_ioctl_extract_signal_seq(hdev, chunk, &signal_seq,
- ctx, cs_encaps_signals);
- if (rc)
- goto free_cs_chunk_array;
-
- if (cs_encaps_signals) {
- /* check if cs sequence has encapsulated
- * signals handle
- */
- struct idr *idp;
- u32 id;
-
- spin_lock(&ctx->sig_mgr.lock);
- idp = &ctx->sig_mgr.handles;
- idr_for_each_entry(idp, encaps_sig_hdl, id) {
- if (encaps_sig_hdl->cs_seq == signal_seq) {
- /* get refcount to protect removing this handle from idr,
- * needed when multiple wait cs are used with offset
- * to wait on reserved encaps signals.
- * Since kref_put of this handle is executed outside the
- * current lock, it is possible that the handle refcount
- * is 0 but it yet to be removed from the list. In this
- * case need to consider the handle as not valid.
- */
- if (kref_get_unless_zero(&encaps_sig_hdl->refcount))
- handle_found = true;
- break;
- }
- }
- spin_unlock(&ctx->sig_mgr.lock);
-
- if (!handle_found) {
- /* treat as signal CS already finished */
- dev_dbg(hdev->dev, "Cannot find encapsulated signals handle for seq 0x%llx\n",
- signal_seq);
- rc = 0;
- goto free_cs_chunk_array;
- }
-
- /* validate also the signal offset value */
- if (chunk->encaps_signal_offset >
- encaps_sig_hdl->count) {
- dev_err(hdev->dev, "offset(%u) value exceed max reserved signals count(%u)!\n",
- chunk->encaps_signal_offset,
- encaps_sig_hdl->count);
- rc = -EINVAL;
- goto free_cs_chunk_array;
- }
- }
-
- sig_fence = hl_ctx_get_fence(ctx, signal_seq);
- if (IS_ERR(sig_fence)) {
- atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
- atomic64_inc(&cntr->validation_drop_cnt);
- dev_err(hdev->dev,
- "Failed to get signal CS with seq 0x%llx\n",
- signal_seq);
- rc = PTR_ERR(sig_fence);
- goto free_cs_chunk_array;
- }
-
- if (!sig_fence) {
- /* signal CS already finished */
- rc = 0;
- goto free_cs_chunk_array;
- }
-
- sig_waitcs_cmpl =
- container_of(sig_fence, struct hl_cs_compl, base_fence);
-
- staged_cs_with_encaps_signals = !!
- (sig_waitcs_cmpl->type == CS_TYPE_DEFAULT &&
- (flags & HL_CS_FLAGS_ENCAP_SIGNALS));
-
- if (sig_waitcs_cmpl->type != CS_TYPE_SIGNAL &&
- !staged_cs_with_encaps_signals) {
- atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
- atomic64_inc(&cntr->validation_drop_cnt);
- dev_err(hdev->dev,
- "CS seq 0x%llx is not of a signal/encaps-signal CS\n",
- signal_seq);
- hl_fence_put(sig_fence);
- rc = -EINVAL;
- goto free_cs_chunk_array;
- }
-
- if (completion_done(&sig_fence->completion)) {
- /* signal CS already finished */
- hl_fence_put(sig_fence);
- rc = 0;
- goto free_cs_chunk_array;
- }
- }
-
- rc = allocate_cs(hdev, ctx, cs_type, ULLONG_MAX, &cs, flags, timeout);
- if (rc) {
- if (is_wait_cs)
- hl_fence_put(sig_fence);
-
- goto free_cs_chunk_array;
- }
-
- /*
- * Save the signal CS fence for later initialization right before
- * hanging the wait CS on the queue.
- * for encaps signals case, we save the cs sequence and handle pointer
- * for later initialization.
- */
- if (is_wait_cs) {
- cs->signal_fence = sig_fence;
- /* store the handle pointer, so we don't have to
- * look for it again, later on the flow
- * when we need to set SOB info in hw_queue.
- */
- if (cs->encaps_signals)
- cs->encaps_sig_hdl = encaps_sig_hdl;
- }
-
- hl_debugfs_add_cs(cs);
-
- *cs_seq = cs->sequence;
-
- if (cs_type == CS_TYPE_WAIT || cs_type == CS_TYPE_SIGNAL)
- rc = cs_ioctl_signal_wait_create_jobs(hdev, ctx, cs, q_type,
- q_idx, chunk->encaps_signal_offset);
- else if (cs_type == CS_TYPE_COLLECTIVE_WAIT)
- rc = hdev->asic_funcs->collective_wait_create_jobs(hdev, ctx,
- cs, q_idx, collective_engine_id,
- chunk->encaps_signal_offset);
- else {
- atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
- atomic64_inc(&cntr->validation_drop_cnt);
- rc = -EINVAL;
- }
-
- if (rc)
- goto free_cs_object;
-
- if (q_type == QUEUE_TYPE_HW)
- INIT_WORK(&cs->finish_work, cs_completion);
-
- rc = hl_hw_queue_schedule_cs(cs);
- if (rc) {
- /* In case wait cs failed here, it means the signal cs
- * already completed. we want to free all it's related objects
- * but we don't want to fail the ioctl.
- */
- if (is_wait_cs)
- rc = 0;
- else if (rc != -EAGAIN)
- dev_err(hdev->dev,
- "Failed to submit CS %d.%llu to H/W queues, error %d\n",
- ctx->asid, cs->sequence, rc);
- goto free_cs_object;
- }
-
- *signal_sob_addr_offset = cs->sob_addr_offset;
- *signal_initial_sob_count = cs->initial_sob_count;
-
- rc = HL_CS_STATUS_SUCCESS;
- if (is_wait_cs)
- wait_cs_submitted = true;
- goto put_cs;
-
-free_cs_object:
- cs_rollback(hdev, cs);
- *cs_seq = ULLONG_MAX;
- /* The path below is both for good and erroneous exits */
-put_cs:
- /* We finished with the CS in this function, so put the ref */
- cs_put(cs);
-free_cs_chunk_array:
- if (!wait_cs_submitted && cs_encaps_signals && handle_found && is_wait_cs)
- kref_put(&encaps_sig_hdl->refcount, hl_encaps_release_handle_and_put_ctx);
- kfree(cs_chunk_array);
-out:
- return rc;
-}
-
-static int cs_ioctl_engine_cores(struct hl_fpriv *hpriv, u64 engine_cores,
- u32 num_engine_cores, u32 core_command)
-{
- int rc;
- struct hl_device *hdev = hpriv->hdev;
- void __user *engine_cores_arr;
- u32 *cores;
-
- if (!num_engine_cores || num_engine_cores > hdev->asic_prop.num_engine_cores) {
- dev_err(hdev->dev, "Number of engine cores %d is invalid\n", num_engine_cores);
- return -EINVAL;
- }
-
- if (core_command != HL_ENGINE_CORE_RUN && core_command != HL_ENGINE_CORE_HALT) {
- dev_err(hdev->dev, "Engine core command is invalid\n");
- return -EINVAL;
- }
-
- engine_cores_arr = (void __user *) (uintptr_t) engine_cores;
- cores = kmalloc_array(num_engine_cores, sizeof(u32), GFP_KERNEL);
- if (!cores)
- return -ENOMEM;
-
- if (copy_from_user(cores, engine_cores_arr, num_engine_cores * sizeof(u32))) {
- dev_err(hdev->dev, "Failed to copy core-ids array from user\n");
- kfree(cores);
- return -EFAULT;
- }
-
- rc = hdev->asic_funcs->set_engine_cores(hdev, cores, num_engine_cores, core_command);
- kfree(cores);
-
- return rc;
-}
-
-int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data)
-{
- union hl_cs_args *args = data;
- enum hl_cs_type cs_type = 0;
- u64 cs_seq = ULONG_MAX;
- void __user *chunks;
- u32 num_chunks, flags, timeout,
- signals_count = 0, sob_addr = 0, handle_id = 0;
- u16 sob_initial_count = 0;
- int rc;
-
- rc = hl_cs_sanity_checks(hpriv, args);
- if (rc)
- goto out;
-
- rc = hl_cs_ctx_switch(hpriv, args, &cs_seq);
- if (rc)
- goto out;
-
- cs_type = hl_cs_get_cs_type(args->in.cs_flags &
- ~HL_CS_FLAGS_FORCE_RESTORE);
- chunks = (void __user *) (uintptr_t) args->in.chunks_execute;
- num_chunks = args->in.num_chunks_execute;
- flags = args->in.cs_flags;
-
- /* In case this is a staged CS, user should supply the CS sequence */
- if ((flags & HL_CS_FLAGS_STAGED_SUBMISSION) &&
- !(flags & HL_CS_FLAGS_STAGED_SUBMISSION_FIRST))
- cs_seq = args->in.seq;
-
- timeout = flags & HL_CS_FLAGS_CUSTOM_TIMEOUT
- ? msecs_to_jiffies(args->in.timeout * 1000)
- : hpriv->hdev->timeout_jiffies;
-
- switch (cs_type) {
- case CS_TYPE_SIGNAL:
- case CS_TYPE_WAIT:
- case CS_TYPE_COLLECTIVE_WAIT:
- rc = cs_ioctl_signal_wait(hpriv, cs_type, chunks, num_chunks,
- &cs_seq, args->in.cs_flags, timeout,
- &sob_addr, &sob_initial_count);
- break;
- case CS_RESERVE_SIGNALS:
- rc = cs_ioctl_reserve_signals(hpriv,
- args->in.encaps_signals_q_idx,
- args->in.encaps_signals_count,
- &handle_id, &sob_addr, &signals_count);
- break;
- case CS_UNRESERVE_SIGNALS:
- rc = cs_ioctl_unreserve_signals(hpriv,
- args->in.encaps_sig_handle_id);
- break;
- case CS_TYPE_ENGINE_CORE:
- rc = cs_ioctl_engine_cores(hpriv, args->in.engine_cores,
- args->in.num_engine_cores, args->in.core_command);
- break;
- default:
- rc = cs_ioctl_default(hpriv, chunks, num_chunks, &cs_seq,
- args->in.cs_flags,
- args->in.encaps_sig_handle_id,
- timeout, &sob_initial_count);
- break;
- }
-out:
- if (rc != -EAGAIN) {
- memset(args, 0, sizeof(*args));
-
- switch (cs_type) {
- case CS_RESERVE_SIGNALS:
- args->out.handle_id = handle_id;
- args->out.sob_base_addr_offset = sob_addr;
- args->out.count = signals_count;
- break;
- case CS_TYPE_SIGNAL:
- args->out.sob_base_addr_offset = sob_addr;
- args->out.sob_count_before_submission = sob_initial_count;
- args->out.seq = cs_seq;
- break;
- case CS_TYPE_DEFAULT:
- args->out.sob_count_before_submission = sob_initial_count;
- args->out.seq = cs_seq;
- break;
- default:
- args->out.seq = cs_seq;
- break;
- }
-
- args->out.status = rc;
- }
-
- return rc;
-}
-
-static int hl_wait_for_fence(struct hl_ctx *ctx, u64 seq, struct hl_fence *fence,
- enum hl_cs_wait_status *status, u64 timeout_us, s64 *timestamp)
-{
- struct hl_device *hdev = ctx->hdev;
- ktime_t timestamp_kt;
- long completion_rc;
- int rc = 0, error;
-
- if (IS_ERR(fence)) {
- rc = PTR_ERR(fence);
- if (rc == -EINVAL)
- dev_notice_ratelimited(hdev->dev,
- "Can't wait on CS %llu because current CS is at seq %llu\n",
- seq, ctx->cs_sequence);
- return rc;
- }
-
- if (!fence) {
- if (!hl_pop_cs_outcome(&ctx->outcome_store, seq, &timestamp_kt, &error)) {
- dev_dbg(hdev->dev,
- "Can't wait on seq %llu because current CS is at seq %llu (Fence is gone)\n",
- seq, ctx->cs_sequence);
- *status = CS_WAIT_STATUS_GONE;
- return 0;
- }
-
- completion_rc = 1;
- goto report_results;
- }
-
- if (!timeout_us) {
- completion_rc = completion_done(&fence->completion);
- } else {
- unsigned long timeout;
-
- timeout = (timeout_us == MAX_SCHEDULE_TIMEOUT) ?
- timeout_us : usecs_to_jiffies(timeout_us);
- completion_rc =
- wait_for_completion_interruptible_timeout(
- &fence->completion, timeout);
- }
-
- error = fence->error;
- timestamp_kt = fence->timestamp;
-
-report_results:
- if (completion_rc > 0) {
- *status = CS_WAIT_STATUS_COMPLETED;
- if (timestamp)
- *timestamp = ktime_to_ns(timestamp_kt);
- } else {
- *status = CS_WAIT_STATUS_BUSY;
- }
-
- if (completion_rc == -ERESTARTSYS)
- rc = completion_rc;
- else if (error == -ETIMEDOUT || error == -EIO)
- rc = error;
-
- return rc;
-}
-
-/*
- * hl_cs_poll_fences - iterate CS fences to check for CS completion
- *
- * @mcs_data: multi-CS internal data
- * @mcs_compl: multi-CS completion structure
- *
- * @return 0 on success, otherwise non 0 error code
- *
- * The function iterates on all CS sequence in the list and set bit in
- * completion_bitmap for each completed CS.
- * While iterating, the function sets the stream map of each fence in the fence
- * array in the completion QID stream map to be used by CSs to perform
- * completion to the multi-CS context.
- * This function shall be called after taking context ref
- */
-static int hl_cs_poll_fences(struct multi_cs_data *mcs_data, struct multi_cs_completion *mcs_compl)
-{
- struct hl_fence **fence_ptr = mcs_data->fence_arr;
- struct hl_device *hdev = mcs_data->ctx->hdev;
- int i, rc, arr_len = mcs_data->arr_len;
- u64 *seq_arr = mcs_data->seq_arr;
- ktime_t max_ktime, first_cs_time;
- enum hl_cs_wait_status status;
-
- memset(fence_ptr, 0, arr_len * sizeof(struct hl_fence *));
-
- /* get all fences under the same lock */
- rc = hl_ctx_get_fences(mcs_data->ctx, seq_arr, fence_ptr, arr_len);
- if (rc)
- return rc;
-
- /*
- * re-initialize the completion here to handle 2 possible cases:
- * 1. CS will complete the multi-CS prior clearing the completion. in which
- * case the fence iteration is guaranteed to catch the CS completion.
- * 2. the completion will occur after re-init of the completion.
- * in which case we will wake up immediately in wait_for_completion.
- */
- reinit_completion(&mcs_compl->completion);
-
- /*
- * set to maximum time to verify timestamp is valid: if at the end
- * this value is maintained- no timestamp was updated
- */
- max_ktime = ktime_set(KTIME_SEC_MAX, 0);
- first_cs_time = max_ktime;
-
- for (i = 0; i < arr_len; i++, fence_ptr++) {
- struct hl_fence *fence = *fence_ptr;
-
- /*
- * In order to prevent case where we wait until timeout even though a CS associated
- * with the multi-CS actually completed we do things in the below order:
- * 1. for each fence set it's QID map in the multi-CS completion QID map. This way
- * any CS can, potentially, complete the multi CS for the specific QID (note
- * that once completion is initialized, calling complete* and then wait on the
- * completion will cause it to return at once)
- * 2. only after allowing multi-CS completion for the specific QID we check whether
- * the specific CS already completed (and thus the wait for completion part will
- * be skipped). if the CS not completed it is guaranteed that completing CS will
- * wake up the completion.
- */
- if (fence)
- mcs_compl->stream_master_qid_map |= fence->stream_master_qid_map;
-
- /*
- * function won't sleep as it is called with timeout 0 (i.e.
- * poll the fence)
- */
- rc = hl_wait_for_fence(mcs_data->ctx, seq_arr[i], fence, &status, 0, NULL);
- if (rc) {
- dev_err(hdev->dev,
- "wait_for_fence error :%d for CS seq %llu\n",
- rc, seq_arr[i]);
- break;
- }
-
- switch (status) {
- case CS_WAIT_STATUS_BUSY:
- /* CS did not finished, QID to wait on already stored */
- break;
- case CS_WAIT_STATUS_COMPLETED:
- /*
- * Using mcs_handling_done to avoid possibility of mcs_data
- * returns to user indicating CS completed before it finished
- * all of its mcs handling, to avoid race the next time the
- * user waits for mcs.
- * note: when reaching this case fence is definitely not NULL
- * but NULL check was added to overcome static analysis
- */
- if (fence && !fence->mcs_handling_done) {
- /*
- * in case multi CS is completed but MCS handling not done
- * we "complete" the multi CS to prevent it from waiting
- * until time-out and the "multi-CS handling done" will have
- * another chance at the next iteration
- */
- complete_all(&mcs_compl->completion);
- break;
- }
-
- mcs_data->completion_bitmap |= BIT(i);
- /*
- * For all completed CSs we take the earliest timestamp.
- * For this we have to validate that the timestamp is
- * earliest of all timestamps so far.
- */
- if (fence && mcs_data->update_ts &&
- (ktime_compare(fence->timestamp, first_cs_time) < 0))
- first_cs_time = fence->timestamp;
- break;
- case CS_WAIT_STATUS_GONE:
- mcs_data->update_ts = false;
- mcs_data->gone_cs = true;
- /*
- * It is possible to get an old sequence numbers from user
- * which related to already completed CSs and their fences
- * already gone. In this case, CS set as completed but
- * no need to consider its QID for mcs completion.
- */
- mcs_data->completion_bitmap |= BIT(i);
- break;
- default:
- dev_err(hdev->dev, "Invalid fence status\n");
- rc = -EINVAL;
- break;
- }
-
- }
-
- hl_fences_put(mcs_data->fence_arr, arr_len);
-
- if (mcs_data->update_ts &&
- (ktime_compare(first_cs_time, max_ktime) != 0))
- mcs_data->timestamp = ktime_to_ns(first_cs_time);
-
- return rc;
-}
-
-static int _hl_cs_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, u64 timeout_us, u64 seq,
- enum hl_cs_wait_status *status, s64 *timestamp)
-{
- struct hl_fence *fence;
- int rc = 0;
-
- if (timestamp)
- *timestamp = 0;
-
- hl_ctx_get(ctx);
-
- fence = hl_ctx_get_fence(ctx, seq);
-
- rc = hl_wait_for_fence(ctx, seq, fence, status, timeout_us, timestamp);
- hl_fence_put(fence);
- hl_ctx_put(ctx);
-
- return rc;
-}
-
-static inline unsigned long hl_usecs64_to_jiffies(const u64 usecs)
-{
- if (usecs <= U32_MAX)
- return usecs_to_jiffies(usecs);
-
- /*
- * If the value in nanoseconds is larger than 64 bit, use the largest
- * 64 bit value.
- */
- if (usecs >= ((u64)(U64_MAX / NSEC_PER_USEC)))
- return nsecs_to_jiffies(U64_MAX);
-
- return nsecs_to_jiffies(usecs * NSEC_PER_USEC);
-}
-
-/*
- * hl_wait_multi_cs_completion_init - init completion structure
- *
- * @hdev: pointer to habanalabs device structure
- * @stream_master_bitmap: stream master QIDs map, set bit indicates stream
- * master QID to wait on
- *
- * @return valid completion struct pointer on success, otherwise error pointer
- *
- * up to MULTI_CS_MAX_USER_CTX calls can be done concurrently to the driver.
- * the function gets the first available completion (by marking it "used")
- * and initialize its values.
- */
-static struct multi_cs_completion *hl_wait_multi_cs_completion_init(struct hl_device *hdev)
-{
- struct multi_cs_completion *mcs_compl;
- int i;
-
- /* find free multi_cs completion structure */
- for (i = 0; i < MULTI_CS_MAX_USER_CTX; i++) {
- mcs_compl = &hdev->multi_cs_completion[i];
- spin_lock(&mcs_compl->lock);
- if (!mcs_compl->used) {
- mcs_compl->used = 1;
- mcs_compl->timestamp = 0;
- /*
- * init QID map to 0 to avoid completion by CSs. the actual QID map
- * to multi-CS CSs will be set incrementally at a later stage
- */
- mcs_compl->stream_master_qid_map = 0;
- spin_unlock(&mcs_compl->lock);
- break;
- }
- spin_unlock(&mcs_compl->lock);
- }
-
- if (i == MULTI_CS_MAX_USER_CTX) {
- dev_err(hdev->dev, "no available multi-CS completion structure\n");
- return ERR_PTR(-ENOMEM);
- }
- return mcs_compl;
-}
-
-/*
- * hl_wait_multi_cs_completion_fini - return completion structure and set as
- * unused
- *
- * @mcs_compl: pointer to the completion structure
- */
-static void hl_wait_multi_cs_completion_fini(
- struct multi_cs_completion *mcs_compl)
-{
- /*
- * free completion structure, do it under lock to be in-sync with the
- * thread that signals completion
- */
- spin_lock(&mcs_compl->lock);
- mcs_compl->used = 0;
- spin_unlock(&mcs_compl->lock);
-}
-
-/*
- * hl_wait_multi_cs_completion - wait for first CS to complete
- *
- * @mcs_data: multi-CS internal data
- *
- * @return 0 on success, otherwise non 0 error code
- */
-static int hl_wait_multi_cs_completion(struct multi_cs_data *mcs_data,
- struct multi_cs_completion *mcs_compl)
-{
- long completion_rc;
-
- completion_rc = wait_for_completion_interruptible_timeout(&mcs_compl->completion,
- mcs_data->timeout_jiffies);
-
- /* update timestamp */
- if (completion_rc > 0)
- mcs_data->timestamp = mcs_compl->timestamp;
-
- if (completion_rc == -ERESTARTSYS)
- return completion_rc;
-
- mcs_data->wait_status = completion_rc;
-
- return 0;
-}
-
-/*
- * hl_multi_cs_completion_init - init array of multi-CS completion structures
- *
- * @hdev: pointer to habanalabs device structure
- */
-void hl_multi_cs_completion_init(struct hl_device *hdev)
-{
- struct multi_cs_completion *mcs_cmpl;
- int i;
-
- for (i = 0; i < MULTI_CS_MAX_USER_CTX; i++) {
- mcs_cmpl = &hdev->multi_cs_completion[i];
- mcs_cmpl->used = 0;
- spin_lock_init(&mcs_cmpl->lock);
- init_completion(&mcs_cmpl->completion);
- }
-}
-
-/*
- * hl_multi_cs_wait_ioctl - implementation of the multi-CS wait ioctl
- *
- * @hpriv: pointer to the private data of the fd
- * @data: pointer to multi-CS wait ioctl in/out args
- *
- */
-static int hl_multi_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data)
-{
- struct multi_cs_completion *mcs_compl;
- struct hl_device *hdev = hpriv->hdev;
- struct multi_cs_data mcs_data = {};
- union hl_wait_cs_args *args = data;
- struct hl_ctx *ctx = hpriv->ctx;
- struct hl_fence **fence_arr;
- void __user *seq_arr;
- u32 size_to_copy;
- u64 *cs_seq_arr;
- u8 seq_arr_len;
- int rc;
-
- if (!hdev->supports_wait_for_multi_cs) {
- dev_err(hdev->dev, "Wait for multi CS is not supported\n");
- return -EPERM;
- }
-
- seq_arr_len = args->in.seq_arr_len;
-
- if (seq_arr_len > HL_WAIT_MULTI_CS_LIST_MAX_LEN) {
- dev_err(hdev->dev, "Can wait only up to %d CSs, input sequence is of length %u\n",
- HL_WAIT_MULTI_CS_LIST_MAX_LEN, seq_arr_len);
- return -EINVAL;
- }
-
- /* allocate memory for sequence array */
- cs_seq_arr =
- kmalloc_array(seq_arr_len, sizeof(*cs_seq_arr), GFP_KERNEL);
- if (!cs_seq_arr)
- return -ENOMEM;
-
- /* copy CS sequence array from user */
- seq_arr = (void __user *) (uintptr_t) args->in.seq;
- size_to_copy = seq_arr_len * sizeof(*cs_seq_arr);
- if (copy_from_user(cs_seq_arr, seq_arr, size_to_copy)) {
- dev_err(hdev->dev, "Failed to copy multi-cs sequence array from user\n");
- rc = -EFAULT;
- goto free_seq_arr;
- }
-
- /* allocate array for the fences */
- fence_arr = kmalloc_array(seq_arr_len, sizeof(struct hl_fence *), GFP_KERNEL);
- if (!fence_arr) {
- rc = -ENOMEM;
- goto free_seq_arr;
- }
-
- /* initialize the multi-CS internal data */
- mcs_data.ctx = ctx;
- mcs_data.seq_arr = cs_seq_arr;
- mcs_data.fence_arr = fence_arr;
- mcs_data.arr_len = seq_arr_len;
-
- hl_ctx_get(ctx);
-
- /* wait (with timeout) for the first CS to be completed */
- mcs_data.timeout_jiffies = hl_usecs64_to_jiffies(args->in.timeout_us);
- mcs_compl = hl_wait_multi_cs_completion_init(hdev);
- if (IS_ERR(mcs_compl)) {
- rc = PTR_ERR(mcs_compl);
- goto put_ctx;
- }
-
- /* poll all CS fences, extract timestamp */
- mcs_data.update_ts = true;
- rc = hl_cs_poll_fences(&mcs_data, mcs_compl);
- /*
- * skip wait for CS completion when one of the below is true:
- * - an error on the poll function
- * - one or more CS in the list completed
- * - the user called ioctl with timeout 0
- */
- if (rc || mcs_data.completion_bitmap || !args->in.timeout_us)
- goto completion_fini;
-
- while (true) {
- rc = hl_wait_multi_cs_completion(&mcs_data, mcs_compl);
- if (rc || (mcs_data.wait_status == 0))
- break;
-
- /*
- * poll fences once again to update the CS map.
- * no timestamp should be updated this time.
- */
- mcs_data.update_ts = false;
- rc = hl_cs_poll_fences(&mcs_data, mcs_compl);
-
- if (rc || mcs_data.completion_bitmap)
- break;
-
- /*
- * if hl_wait_multi_cs_completion returned before timeout (i.e.
- * it got a completion) it either got completed by CS in the multi CS list
- * (in which case the indication will be non empty completion_bitmap) or it
- * got completed by CS submitted to one of the shared stream master but
- * not in the multi CS list (in which case we should wait again but modify
- * the timeout and set timestamp as zero to let a CS related to the current
- * multi-CS set a new, relevant, timestamp)
- */
- mcs_data.timeout_jiffies = mcs_data.wait_status;
- mcs_compl->timestamp = 0;
- }
-
-completion_fini:
- hl_wait_multi_cs_completion_fini(mcs_compl);
-
-put_ctx:
- hl_ctx_put(ctx);
- kfree(fence_arr);
-
-free_seq_arr:
- kfree(cs_seq_arr);
-
- if (rc == -ERESTARTSYS) {
- dev_err_ratelimited(hdev->dev,
- "user process got signal while waiting for Multi-CS\n");
- rc = -EINTR;
- }
-
- if (rc)
- return rc;
-
- /* update output args */
- memset(args, 0, sizeof(*args));
-
- if (mcs_data.completion_bitmap) {
- args->out.status = HL_WAIT_CS_STATUS_COMPLETED;
- args->out.cs_completion_map = mcs_data.completion_bitmap;
-
- /* if timestamp not 0- it's valid */
- if (mcs_data.timestamp) {
- args->out.timestamp_nsec = mcs_data.timestamp;
- args->out.flags |= HL_WAIT_CS_STATUS_FLAG_TIMESTAMP_VLD;
- }
-
- /* update if some CS was gone */
- if (!mcs_data.timestamp)
- args->out.flags |= HL_WAIT_CS_STATUS_FLAG_GONE;
- } else {
- args->out.status = HL_WAIT_CS_STATUS_BUSY;
- }
-
- return 0;
-}
-
-static int hl_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data)
-{
- struct hl_device *hdev = hpriv->hdev;
- union hl_wait_cs_args *args = data;
- enum hl_cs_wait_status status;
- u64 seq = args->in.seq;
- s64 timestamp;
- int rc;
-
- rc = _hl_cs_wait_ioctl(hdev, hpriv->ctx, args->in.timeout_us, seq, &status, &timestamp);
-
- if (rc == -ERESTARTSYS) {
- dev_err_ratelimited(hdev->dev,
- "user process got signal while waiting for CS handle %llu\n",
- seq);
- return -EINTR;
- }
-
- memset(args, 0, sizeof(*args));
-
- if (rc) {
- if (rc == -ETIMEDOUT) {
- dev_err_ratelimited(hdev->dev,
- "CS %llu has timed-out while user process is waiting for it\n",
- seq);
- args->out.status = HL_WAIT_CS_STATUS_TIMEDOUT;
- } else if (rc == -EIO) {
- dev_err_ratelimited(hdev->dev,
- "CS %llu has been aborted while user process is waiting for it\n",
- seq);
- args->out.status = HL_WAIT_CS_STATUS_ABORTED;
- }
- return rc;
- }
-
- if (timestamp) {
- args->out.flags |= HL_WAIT_CS_STATUS_FLAG_TIMESTAMP_VLD;
- args->out.timestamp_nsec = timestamp;
- }
-
- switch (status) {
- case CS_WAIT_STATUS_GONE:
- args->out.flags |= HL_WAIT_CS_STATUS_FLAG_GONE;
- fallthrough;
- case CS_WAIT_STATUS_COMPLETED:
- args->out.status = HL_WAIT_CS_STATUS_COMPLETED;
- break;
- case CS_WAIT_STATUS_BUSY:
- default:
- args->out.status = HL_WAIT_CS_STATUS_BUSY;
- break;
- }
-
- return 0;
-}
-
-static int ts_buff_get_kernel_ts_record(struct hl_mmap_mem_buf *buf,
- struct hl_cb *cq_cb,
- u64 ts_offset, u64 cq_offset, u64 target_value,
- spinlock_t *wait_list_lock,
- struct hl_user_pending_interrupt **pend)
-{
- struct hl_ts_buff *ts_buff = buf->private;
- struct hl_user_pending_interrupt *requested_offset_record =
- (struct hl_user_pending_interrupt *)ts_buff->kernel_buff_address +
- ts_offset;
- struct hl_user_pending_interrupt *cb_last =
- (struct hl_user_pending_interrupt *)ts_buff->kernel_buff_address +
- (ts_buff->kernel_buff_size / sizeof(struct hl_user_pending_interrupt));
- unsigned long flags, iter_counter = 0;
- u64 current_cq_counter;
-
- /* Validate ts_offset not exceeding last max */
- if (requested_offset_record >= cb_last) {
- dev_err(buf->mmg->dev, "Ts offset exceeds max CB offset(0x%llx)\n",
- (u64)(uintptr_t)cb_last);
- return -EINVAL;
- }
-
-start_over:
- spin_lock_irqsave(wait_list_lock, flags);
-
- /* Unregister only if we didn't reach the target value
- * since in this case there will be no handling in irq context
- * and then it's safe to delete the node out of the interrupt list
- * then re-use it on other interrupt
- */
- if (requested_offset_record->ts_reg_info.in_use) {
- current_cq_counter = *requested_offset_record->cq_kernel_addr;
- if (current_cq_counter < requested_offset_record->cq_target_value) {
- list_del(&requested_offset_record->wait_list_node);
- spin_unlock_irqrestore(wait_list_lock, flags);
-
- hl_mmap_mem_buf_put(requested_offset_record->ts_reg_info.buf);
- hl_cb_put(requested_offset_record->ts_reg_info.cq_cb);
-
- dev_dbg(buf->mmg->dev,
- "ts node removed from interrupt list now can re-use\n");
- } else {
- dev_dbg(buf->mmg->dev,
- "ts node in middle of irq handling\n");
-
- /* irq handling in the middle give it time to finish */
- spin_unlock_irqrestore(wait_list_lock, flags);
- usleep_range(1, 10);
- if (++iter_counter == MAX_TS_ITER_NUM) {
- dev_err(buf->mmg->dev,
- "handling registration interrupt took too long!!\n");
- return -EINVAL;
- }
-
- goto start_over;
- }
- } else {
- spin_unlock_irqrestore(wait_list_lock, flags);
- }
-
- /* Fill up the new registration node info */
- requested_offset_record->ts_reg_info.in_use = 1;
- requested_offset_record->ts_reg_info.buf = buf;
- requested_offset_record->ts_reg_info.cq_cb = cq_cb;
- requested_offset_record->ts_reg_info.timestamp_kernel_addr =
- (u64 *) ts_buff->user_buff_address + ts_offset;
- requested_offset_record->cq_kernel_addr =
- (u64 *) cq_cb->kernel_address + cq_offset;
- requested_offset_record->cq_target_value = target_value;
-
- *pend = requested_offset_record;
-
- dev_dbg(buf->mmg->dev, "Found available node in TS kernel CB %p\n",
- requested_offset_record);
- return 0;
-}
-
-static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx,
- struct hl_mem_mgr *cb_mmg, struct hl_mem_mgr *mmg,
- u64 timeout_us, u64 cq_counters_handle, u64 cq_counters_offset,
- u64 target_value, struct hl_user_interrupt *interrupt,
- bool register_ts_record, u64 ts_handle, u64 ts_offset,
- u32 *status, u64 *timestamp)
-{
- struct hl_user_pending_interrupt *pend;
- struct hl_mmap_mem_buf *buf;
- struct hl_cb *cq_cb;
- unsigned long timeout, flags;
- long completion_rc;
- int rc = 0;
-
- timeout = hl_usecs64_to_jiffies(timeout_us);
-
- hl_ctx_get(ctx);
-
- cq_cb = hl_cb_get(cb_mmg, cq_counters_handle);
- if (!cq_cb) {
- rc = -EINVAL;
- goto put_ctx;
- }
-
- /* Validate the cq offset */
- if (((u64 *) cq_cb->kernel_address + cq_counters_offset) >=
- ((u64 *) cq_cb->kernel_address + (cq_cb->size / sizeof(u64)))) {
- rc = -EINVAL;
- goto put_cq_cb;
- }
-
- if (register_ts_record) {
- dev_dbg(hdev->dev, "Timestamp registration: interrupt id: %u, ts offset: %llu, cq_offset: %llu\n",
- interrupt->interrupt_id, ts_offset, cq_counters_offset);
- buf = hl_mmap_mem_buf_get(mmg, ts_handle);
- if (!buf) {
- rc = -EINVAL;
- goto put_cq_cb;
- }
-
- /* Find first available record */
- rc = ts_buff_get_kernel_ts_record(buf, cq_cb, ts_offset,
- cq_counters_offset, target_value,
- &interrupt->wait_list_lock, &pend);
- if (rc)
- goto put_ts_buff;
- } else {
- pend = kzalloc(sizeof(*pend), GFP_KERNEL);
- if (!pend) {
- rc = -ENOMEM;
- goto put_cq_cb;
- }
- hl_fence_init(&pend->fence, ULONG_MAX);
- pend->cq_kernel_addr = (u64 *) cq_cb->kernel_address + cq_counters_offset;
- pend->cq_target_value = target_value;
- }
-
- spin_lock_irqsave(&interrupt->wait_list_lock, flags);
-
- /* We check for completion value as interrupt could have been received
- * before we added the node to the wait list
- */
- if (*pend->cq_kernel_addr >= target_value) {
- if (register_ts_record)
- pend->ts_reg_info.in_use = 0;
- spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
-
- *status = HL_WAIT_CS_STATUS_COMPLETED;
-
- if (register_ts_record) {
- *pend->ts_reg_info.timestamp_kernel_addr = ktime_get_ns();
- goto put_ts_buff;
- } else {
- pend->fence.timestamp = ktime_get();
- goto set_timestamp;
- }
- } else if (!timeout_us) {
- spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
- *status = HL_WAIT_CS_STATUS_BUSY;
- pend->fence.timestamp = ktime_get();
- goto set_timestamp;
- }
-
- /* Add pending user interrupt to relevant list for the interrupt
- * handler to monitor.
- * Note that we cannot have sorted list by target value,
- * in order to shorten the list pass loop, since
- * same list could have nodes for different cq counter handle.
- */
- list_add_tail(&pend->wait_list_node, &interrupt->wait_list_head);
- spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
-
- if (register_ts_record) {
- rc = *status = HL_WAIT_CS_STATUS_COMPLETED;
- goto ts_registration_exit;
- }
-
- /* Wait for interrupt handler to signal completion */
- completion_rc = wait_for_completion_interruptible_timeout(&pend->fence.completion,
- timeout);
- if (completion_rc > 0) {
- *status = HL_WAIT_CS_STATUS_COMPLETED;
- } else {
- if (completion_rc == -ERESTARTSYS) {
- dev_err_ratelimited(hdev->dev,
- "user process got signal while waiting for interrupt ID %d\n",
- interrupt->interrupt_id);
- rc = -EINTR;
- *status = HL_WAIT_CS_STATUS_ABORTED;
- } else {
- if (pend->fence.error == -EIO) {
- dev_err_ratelimited(hdev->dev,
- "interrupt based wait ioctl aborted(error:%d) due to a reset cycle initiated\n",
- pend->fence.error);
- rc = -EIO;
- *status = HL_WAIT_CS_STATUS_ABORTED;
- } else {
- /* The wait has timed-out. We don't know anything beyond that
- * because the workload wasn't submitted through the driver.
- * Therefore, from driver's perspective, the workload is still
- * executing.
- */
- rc = 0;
- *status = HL_WAIT_CS_STATUS_BUSY;
- }
- }
- }
-
- /*
- * We keep removing the node from list here, and not at the irq handler
- * for completion timeout case. and if it's a registration
- * for ts record, the node will be deleted in the irq handler after
- * we reach the target value.
- */
- spin_lock_irqsave(&interrupt->wait_list_lock, flags);
- list_del(&pend->wait_list_node);
- spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
-
-set_timestamp:
- *timestamp = ktime_to_ns(pend->fence.timestamp);
- kfree(pend);
- hl_cb_put(cq_cb);
-ts_registration_exit:
- hl_ctx_put(ctx);
-
- return rc;
-
-put_ts_buff:
- hl_mmap_mem_buf_put(buf);
-put_cq_cb:
- hl_cb_put(cq_cb);
-put_ctx:
- hl_ctx_put(ctx);
-
- return rc;
-}
-
-static int _hl_interrupt_wait_ioctl_user_addr(struct hl_device *hdev, struct hl_ctx *ctx,
- u64 timeout_us, u64 user_address,
- u64 target_value, struct hl_user_interrupt *interrupt,
- u32 *status,
- u64 *timestamp)
-{
- struct hl_user_pending_interrupt *pend;
- unsigned long timeout, flags;
- u64 completion_value;
- long completion_rc;
- int rc = 0;
-
- timeout = hl_usecs64_to_jiffies(timeout_us);
-
- hl_ctx_get(ctx);
-
- pend = kzalloc(sizeof(*pend), GFP_KERNEL);
- if (!pend) {
- hl_ctx_put(ctx);
- return -ENOMEM;
- }
-
- hl_fence_init(&pend->fence, ULONG_MAX);
-
- /* Add pending user interrupt to relevant list for the interrupt
- * handler to monitor
- */
- spin_lock_irqsave(&interrupt->wait_list_lock, flags);
- list_add_tail(&pend->wait_list_node, &interrupt->wait_list_head);
- spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
-
- /* We check for completion value as interrupt could have been received
- * before we added the node to the wait list
- */
- if (copy_from_user(&completion_value, u64_to_user_ptr(user_address), 8)) {
- dev_err(hdev->dev, "Failed to copy completion value from user\n");
- rc = -EFAULT;
- goto remove_pending_user_interrupt;
- }
-
- if (completion_value >= target_value) {
- *status = HL_WAIT_CS_STATUS_COMPLETED;
- /* There was no interrupt, we assume the completion is now. */
- pend->fence.timestamp = ktime_get();
- } else {
- *status = HL_WAIT_CS_STATUS_BUSY;
- }
-
- if (!timeout_us || (*status == HL_WAIT_CS_STATUS_COMPLETED))
- goto remove_pending_user_interrupt;
-
-wait_again:
- /* Wait for interrupt handler to signal completion */
- completion_rc = wait_for_completion_interruptible_timeout(&pend->fence.completion,
- timeout);
-
- /* If timeout did not expire we need to perform the comparison.
- * If comparison fails, keep waiting until timeout expires
- */
- if (completion_rc > 0) {
- spin_lock_irqsave(&interrupt->wait_list_lock, flags);
- /* reinit_completion must be called before we check for user
- * completion value, otherwise, if interrupt is received after
- * the comparison and before the next wait_for_completion,
- * we will reach timeout and fail
- */
- reinit_completion(&pend->fence.completion);
- spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
-
- if (copy_from_user(&completion_value, u64_to_user_ptr(user_address), 8)) {
- dev_err(hdev->dev, "Failed to copy completion value from user\n");
- rc = -EFAULT;
-
- goto remove_pending_user_interrupt;
- }
-
- if (completion_value >= target_value) {
- *status = HL_WAIT_CS_STATUS_COMPLETED;
- } else if (pend->fence.error) {
- dev_err_ratelimited(hdev->dev,
- "interrupt based wait ioctl aborted(error:%d) due to a reset cycle initiated\n",
- pend->fence.error);
- /* set the command completion status as ABORTED */
- *status = HL_WAIT_CS_STATUS_ABORTED;
- } else {
- timeout = completion_rc;
- goto wait_again;
- }
- } else if (completion_rc == -ERESTARTSYS) {
- dev_err_ratelimited(hdev->dev,
- "user process got signal while waiting for interrupt ID %d\n",
- interrupt->interrupt_id);
- rc = -EINTR;
- } else {
- /* The wait has timed-out. We don't know anything beyond that
- * because the workload wasn't submitted through the driver.
- * Therefore, from driver's perspective, the workload is still
- * executing.
- */
- rc = 0;
- *status = HL_WAIT_CS_STATUS_BUSY;
- }
-
-remove_pending_user_interrupt:
- spin_lock_irqsave(&interrupt->wait_list_lock, flags);
- list_del(&pend->wait_list_node);
- spin_unlock_irqrestore(&interrupt->wait_list_lock, flags);
-
- *timestamp = ktime_to_ns(pend->fence.timestamp);
-
- kfree(pend);
- hl_ctx_put(ctx);
-
- return rc;
-}
-
-static int hl_interrupt_wait_ioctl(struct hl_fpriv *hpriv, void *data)
-{
- u16 interrupt_id, first_interrupt, last_interrupt;
- struct hl_device *hdev = hpriv->hdev;
- struct asic_fixed_properties *prop;
- struct hl_user_interrupt *interrupt;
- union hl_wait_cs_args *args = data;
- u32 status = HL_WAIT_CS_STATUS_BUSY;
- u64 timestamp = 0;
- int rc, int_idx;
-
- prop = &hdev->asic_prop;
-
- if (!(prop->user_interrupt_count + prop->user_dec_intr_count)) {
- dev_err(hdev->dev, "no user interrupts allowed");
- return -EPERM;
- }
-
- interrupt_id = FIELD_GET(HL_WAIT_CS_FLAGS_INTERRUPT_MASK, args->in.flags);
-
- first_interrupt = prop->first_available_user_interrupt;
- last_interrupt = prop->first_available_user_interrupt + prop->user_interrupt_count - 1;
-
- if (interrupt_id < prop->user_dec_intr_count) {
-
- /* Check if the requested core is enabled */
- if (!(prop->decoder_enabled_mask & BIT(interrupt_id))) {
- dev_err(hdev->dev, "interrupt on a disabled core(%u) not allowed",
- interrupt_id);
- return -EINVAL;
- }
-
- interrupt = &hdev->user_interrupt[interrupt_id];
-
- } else if (interrupt_id >= first_interrupt && interrupt_id <= last_interrupt) {
-
- int_idx = interrupt_id - first_interrupt + prop->user_dec_intr_count;
- interrupt = &hdev->user_interrupt[int_idx];
-
- } else if (interrupt_id == HL_COMMON_USER_CQ_INTERRUPT_ID) {
- interrupt = &hdev->common_user_cq_interrupt;
- } else if (interrupt_id == HL_COMMON_DEC_INTERRUPT_ID) {
- interrupt = &hdev->common_decoder_interrupt;
- } else {
- dev_err(hdev->dev, "invalid user interrupt %u", interrupt_id);
- return -EINVAL;
- }
-
- if (args->in.flags & HL_WAIT_CS_FLAGS_INTERRUPT_KERNEL_CQ)
- rc = _hl_interrupt_wait_ioctl(hdev, hpriv->ctx, &hpriv->mem_mgr, &hpriv->mem_mgr,
- args->in.interrupt_timeout_us, args->in.cq_counters_handle,
- args->in.cq_counters_offset,
- args->in.target, interrupt,
- !!(args->in.flags & HL_WAIT_CS_FLAGS_REGISTER_INTERRUPT),
- args->in.timestamp_handle, args->in.timestamp_offset,
- &status, &timestamp);
- else
- rc = _hl_interrupt_wait_ioctl_user_addr(hdev, hpriv->ctx,
- args->in.interrupt_timeout_us, args->in.addr,
- args->in.target, interrupt, &status,
- &timestamp);
- if (rc)
- return rc;
-
- memset(args, 0, sizeof(*args));
- args->out.status = status;
-
- if (timestamp) {
- args->out.timestamp_nsec = timestamp;
- args->out.flags |= HL_WAIT_CS_STATUS_FLAG_TIMESTAMP_VLD;
- }
-
- return 0;
-}
-
-int hl_wait_ioctl(struct hl_fpriv *hpriv, void *data)
-{
- struct hl_device *hdev = hpriv->hdev;
- union hl_wait_cs_args *args = data;
- u32 flags = args->in.flags;
- int rc;
-
- /* If the device is not operational, or if an error has happened and user should release the
- * device, there is no point in waiting for any command submission or user interrupt.
- */
- if (!hl_device_operational(hpriv->hdev, NULL) || hdev->reset_info.watchdog_active)
- return -EBUSY;
-
- if (flags & HL_WAIT_CS_FLAGS_INTERRUPT)
- rc = hl_interrupt_wait_ioctl(hpriv, data);
- else if (flags & HL_WAIT_CS_FLAGS_MULTI_CS)
- rc = hl_multi_cs_wait_ioctl(hpriv, data);
- else
- rc = hl_cs_wait_ioctl(hpriv, data);
-
- return rc;
-}
diff --git a/drivers/misc/habanalabs/common/context.c b/drivers/misc/habanalabs/common/context.c
deleted file mode 100644
index 9c8b1b37b510..000000000000
--- a/drivers/misc/habanalabs/common/context.c
+++ /dev/null
@@ -1,445 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2021 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include "habanalabs.h"
-
-#include <linux/slab.h>
-
-static void encaps_handle_do_release(struct hl_cs_encaps_sig_handle *handle, bool put_hw_sob,
- bool put_ctx)
-{
- struct hl_encaps_signals_mgr *mgr = &handle->ctx->sig_mgr;
-
- if (put_hw_sob)
- hw_sob_put(handle->hw_sob);
-
- spin_lock(&mgr->lock);
- idr_remove(&mgr->handles, handle->id);
- spin_unlock(&mgr->lock);
-
- if (put_ctx)
- hl_ctx_put(handle->ctx);
-
- kfree(handle);
-}
-
-void hl_encaps_release_handle_and_put_ctx(struct kref *ref)
-{
- struct hl_cs_encaps_sig_handle *handle =
- container_of(ref, struct hl_cs_encaps_sig_handle, refcount);
-
- encaps_handle_do_release(handle, false, true);
-}
-
-static void hl_encaps_release_handle_and_put_sob(struct kref *ref)
-{
- struct hl_cs_encaps_sig_handle *handle =
- container_of(ref, struct hl_cs_encaps_sig_handle, refcount);
-
- encaps_handle_do_release(handle, true, false);
-}
-
-void hl_encaps_release_handle_and_put_sob_ctx(struct kref *ref)
-{
- struct hl_cs_encaps_sig_handle *handle =
- container_of(ref, struct hl_cs_encaps_sig_handle, refcount);
-
- encaps_handle_do_release(handle, true, true);
-}
-
-static void hl_encaps_sig_mgr_init(struct hl_encaps_signals_mgr *mgr)
-{
- spin_lock_init(&mgr->lock);
- idr_init(&mgr->handles);
-}
-
-static void hl_encaps_sig_mgr_fini(struct hl_device *hdev, struct hl_encaps_signals_mgr *mgr)
-{
- struct hl_cs_encaps_sig_handle *handle;
- struct idr *idp;
- u32 id;
-
- idp = &mgr->handles;
-
- /* The IDR is expected to be empty at this stage, because any left signal should have been
- * released as part of CS roll-back.
- */
- if (!idr_is_empty(idp)) {
- dev_warn(hdev->dev,
- "device released while some encaps signals handles are still allocated\n");
- idr_for_each_entry(idp, handle, id)
- kref_put(&handle->refcount, hl_encaps_release_handle_and_put_sob);
- }
-
- idr_destroy(&mgr->handles);
-}
-
-static void hl_ctx_fini(struct hl_ctx *ctx)
-{
- struct hl_device *hdev = ctx->hdev;
- int i;
-
- /* Release all allocated HW block mapped list entries and destroy
- * the mutex.
- */
- hl_hw_block_mem_fini(ctx);
-
- /*
- * If we arrived here, there are no jobs waiting for this context
- * on its queues so we can safely remove it.
- * This is because for each CS, we increment the ref count and for
- * every CS that was finished we decrement it and we won't arrive
- * to this function unless the ref count is 0
- */
-
- for (i = 0 ; i < hdev->asic_prop.max_pending_cs ; i++)
- hl_fence_put(ctx->cs_pending[i]);
-
- kfree(ctx->cs_pending);
-
- if (ctx->asid != HL_KERNEL_ASID_ID) {
- dev_dbg(hdev->dev, "closing user context %d\n", ctx->asid);
-
- /* The engines are stopped as there is no executing CS, but the
- * Coresight might be still working by accessing addresses
- * related to the stopped engines. Hence stop it explicitly.
- */
- if (hdev->in_debug)
- hl_device_set_debug_mode(hdev, ctx, false);
-
- hdev->asic_funcs->ctx_fini(ctx);
-
- hl_dec_ctx_fini(ctx);
-
- hl_cb_va_pool_fini(ctx);
- hl_vm_ctx_fini(ctx);
- hl_asid_free(hdev, ctx->asid);
- hl_encaps_sig_mgr_fini(hdev, &ctx->sig_mgr);
- } else {
- dev_dbg(hdev->dev, "closing kernel context\n");
- hdev->asic_funcs->ctx_fini(ctx);
- hl_vm_ctx_fini(ctx);
- hl_mmu_ctx_fini(ctx);
- }
-}
-
-void hl_ctx_do_release(struct kref *ref)
-{
- struct hl_ctx *ctx;
-
- ctx = container_of(ref, struct hl_ctx, refcount);
-
- hl_ctx_fini(ctx);
-
- if (ctx->hpriv) {
- struct hl_fpriv *hpriv = ctx->hpriv;
-
- mutex_lock(&hpriv->ctx_lock);
- hpriv->ctx = NULL;
- mutex_unlock(&hpriv->ctx_lock);
-
- hl_hpriv_put(hpriv);
- }
-
- kfree(ctx);
-}
-
-int hl_ctx_create(struct hl_device *hdev, struct hl_fpriv *hpriv)
-{
- struct hl_ctx_mgr *ctx_mgr = &hpriv->ctx_mgr;
- struct hl_ctx *ctx;
- int rc;
-
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (!ctx) {
- rc = -ENOMEM;
- goto out_err;
- }
-
- mutex_lock(&ctx_mgr->lock);
- rc = idr_alloc(&ctx_mgr->handles, ctx, 1, 0, GFP_KERNEL);
- mutex_unlock(&ctx_mgr->lock);
-
- if (rc < 0) {
- dev_err(hdev->dev, "Failed to allocate IDR for a new CTX\n");
- goto free_ctx;
- }
-
- ctx->handle = rc;
-
- rc = hl_ctx_init(hdev, ctx, false);
- if (rc)
- goto remove_from_idr;
-
- hl_hpriv_get(hpriv);
- ctx->hpriv = hpriv;
-
- /* TODO: remove for multiple contexts per process */
- hpriv->ctx = ctx;
-
- /* TODO: remove the following line for multiple process support */
- hdev->is_compute_ctx_active = true;
-
- return 0;
-
-remove_from_idr:
- mutex_lock(&ctx_mgr->lock);
- idr_remove(&ctx_mgr->handles, ctx->handle);
- mutex_unlock(&ctx_mgr->lock);
-free_ctx:
- kfree(ctx);
-out_err:
- return rc;
-}
-
-int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx)
-{
- int rc = 0, i;
-
- ctx->hdev = hdev;
-
- kref_init(&ctx->refcount);
-
- ctx->cs_sequence = 1;
- spin_lock_init(&ctx->cs_lock);
- atomic_set(&ctx->thread_ctx_switch_token, 1);
- ctx->thread_ctx_switch_wait_token = 0;
- ctx->cs_pending = kcalloc(hdev->asic_prop.max_pending_cs,
- sizeof(struct hl_fence *),
- GFP_KERNEL);
- if (!ctx->cs_pending)
- return -ENOMEM;
-
- INIT_LIST_HEAD(&ctx->outcome_store.used_list);
- INIT_LIST_HEAD(&ctx->outcome_store.free_list);
- hash_init(ctx->outcome_store.outcome_map);
- for (i = 0; i < ARRAY_SIZE(ctx->outcome_store.nodes_pool); ++i)
- list_add(&ctx->outcome_store.nodes_pool[i].list_link,
- &ctx->outcome_store.free_list);
-
- hl_hw_block_mem_init(ctx);
-
- if (is_kernel_ctx) {
- ctx->asid = HL_KERNEL_ASID_ID; /* Kernel driver gets ASID 0 */
- rc = hl_vm_ctx_init(ctx);
- if (rc) {
- dev_err(hdev->dev, "Failed to init mem ctx module\n");
- rc = -ENOMEM;
- goto err_hw_block_mem_fini;
- }
-
- rc = hdev->asic_funcs->ctx_init(ctx);
- if (rc) {
- dev_err(hdev->dev, "ctx_init failed\n");
- goto err_vm_ctx_fini;
- }
- } else {
- ctx->asid = hl_asid_alloc(hdev);
- if (!ctx->asid) {
- dev_err(hdev->dev, "No free ASID, failed to create context\n");
- rc = -ENOMEM;
- goto err_hw_block_mem_fini;
- }
-
- rc = hl_vm_ctx_init(ctx);
- if (rc) {
- dev_err(hdev->dev, "Failed to init mem ctx module\n");
- rc = -ENOMEM;
- goto err_asid_free;
- }
-
- rc = hl_cb_va_pool_init(ctx);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to init VA pool for mapped CB\n");
- goto err_vm_ctx_fini;
- }
-
- rc = hdev->asic_funcs->ctx_init(ctx);
- if (rc) {
- dev_err(hdev->dev, "ctx_init failed\n");
- goto err_cb_va_pool_fini;
- }
-
- hl_encaps_sig_mgr_init(&ctx->sig_mgr);
-
- dev_dbg(hdev->dev, "create user context %d\n", ctx->asid);
- }
-
- return 0;
-
-err_cb_va_pool_fini:
- hl_cb_va_pool_fini(ctx);
-err_vm_ctx_fini:
- hl_vm_ctx_fini(ctx);
-err_asid_free:
- if (ctx->asid != HL_KERNEL_ASID_ID)
- hl_asid_free(hdev, ctx->asid);
-err_hw_block_mem_fini:
- hl_hw_block_mem_fini(ctx);
- kfree(ctx->cs_pending);
-
- return rc;
-}
-
-static int hl_ctx_get_unless_zero(struct hl_ctx *ctx)
-{
- return kref_get_unless_zero(&ctx->refcount);
-}
-
-void hl_ctx_get(struct hl_ctx *ctx)
-{
- kref_get(&ctx->refcount);
-}
-
-int hl_ctx_put(struct hl_ctx *ctx)
-{
- return kref_put(&ctx->refcount, hl_ctx_do_release);
-}
-
-struct hl_ctx *hl_get_compute_ctx(struct hl_device *hdev)
-{
- struct hl_ctx *ctx = NULL;
- struct hl_fpriv *hpriv;
-
- mutex_lock(&hdev->fpriv_list_lock);
-
- list_for_each_entry(hpriv, &hdev->fpriv_list, dev_node) {
- mutex_lock(&hpriv->ctx_lock);
- ctx = hpriv->ctx;
- if (ctx && !hl_ctx_get_unless_zero(ctx))
- ctx = NULL;
- mutex_unlock(&hpriv->ctx_lock);
-
- /* There can only be a single user which has opened the compute device, so exit
- * immediately once we find its context or if we see that it has been released
- */
- break;
- }
-
- mutex_unlock(&hdev->fpriv_list_lock);
-
- return ctx;
-}
-
-/*
- * hl_ctx_get_fence_locked - get CS fence under CS lock
- *
- * @ctx: pointer to the context structure.
- * @seq: CS sequences number
- *
- * @return valid fence pointer on success, NULL if fence is gone, otherwise
- * error pointer.
- *
- * NOTE: this function shall be called with cs_lock locked
- */
-static struct hl_fence *hl_ctx_get_fence_locked(struct hl_ctx *ctx, u64 seq)
-{
- struct asic_fixed_properties *asic_prop = &ctx->hdev->asic_prop;
- struct hl_fence *fence;
-
- if (seq >= ctx->cs_sequence)
- return ERR_PTR(-EINVAL);
-
- if (seq + asic_prop->max_pending_cs < ctx->cs_sequence)
- return NULL;
-
- fence = ctx->cs_pending[seq & (asic_prop->max_pending_cs - 1)];
- hl_fence_get(fence);
- return fence;
-}
-
-struct hl_fence *hl_ctx_get_fence(struct hl_ctx *ctx, u64 seq)
-{
- struct hl_fence *fence;
-
- spin_lock(&ctx->cs_lock);
-
- fence = hl_ctx_get_fence_locked(ctx, seq);
-
- spin_unlock(&ctx->cs_lock);
-
- return fence;
-}
-
-/*
- * hl_ctx_get_fences - get multiple CS fences under the same CS lock
- *
- * @ctx: pointer to the context structure.
- * @seq_arr: array of CS sequences to wait for
- * @fence: fence array to store the CS fences
- * @arr_len: length of seq_arr and fence_arr
- *
- * @return 0 on success, otherwise non 0 error code
- */
-int hl_ctx_get_fences(struct hl_ctx *ctx, u64 *seq_arr,
- struct hl_fence **fence, u32 arr_len)
-{
- struct hl_fence **fence_arr_base = fence;
- int i, rc = 0;
-
- spin_lock(&ctx->cs_lock);
-
- for (i = 0; i < arr_len; i++, fence++) {
- u64 seq = seq_arr[i];
-
- *fence = hl_ctx_get_fence_locked(ctx, seq);
-
- if (IS_ERR(*fence)) {
- dev_err(ctx->hdev->dev,
- "Failed to get fence for CS with seq 0x%llx\n",
- seq);
- rc = PTR_ERR(*fence);
- break;
- }
- }
-
- spin_unlock(&ctx->cs_lock);
-
- if (rc)
- hl_fences_put(fence_arr_base, i);
-
- return rc;
-}
-
-/*
- * hl_ctx_mgr_init - initialize the context manager
- *
- * @ctx_mgr: pointer to context manager structure
- *
- * This manager is an object inside the hpriv object of the user process.
- * The function is called when a user process opens the FD.
- */
-void hl_ctx_mgr_init(struct hl_ctx_mgr *ctx_mgr)
-{
- mutex_init(&ctx_mgr->lock);
- idr_init(&ctx_mgr->handles);
-}
-
-/*
- * hl_ctx_mgr_fini - finalize the context manager
- *
- * @hdev: pointer to device structure
- * @ctx_mgr: pointer to context manager structure
- *
- * This function goes over all the contexts in the manager and frees them.
- * It is called when a process closes the FD.
- */
-void hl_ctx_mgr_fini(struct hl_device *hdev, struct hl_ctx_mgr *ctx_mgr)
-{
- struct hl_ctx *ctx;
- struct idr *idp;
- u32 id;
-
- idp = &ctx_mgr->handles;
-
- idr_for_each_entry(idp, ctx, id)
- kref_put(&ctx->refcount, hl_ctx_do_release);
-
- idr_destroy(&ctx_mgr->handles);
- mutex_destroy(&ctx_mgr->lock);
-}
diff --git a/drivers/misc/habanalabs/common/debugfs.c b/drivers/misc/habanalabs/common/debugfs.c
deleted file mode 100644
index 945c0e6758ca..000000000000
--- a/drivers/misc/habanalabs/common/debugfs.c
+++ /dev/null
@@ -1,1948 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2021 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include "habanalabs.h"
-#include "../include/hw_ip/mmu/mmu_general.h"
-
-#include <linux/pci.h>
-#include <linux/uaccess.h>
-#include <linux/vmalloc.h>
-#include <linux/iommu.h>
-
-#define MMU_ADDR_BUF_SIZE 40
-#define MMU_ASID_BUF_SIZE 10
-#define MMU_KBUF_SIZE (MMU_ADDR_BUF_SIZE + MMU_ASID_BUF_SIZE)
-#define I2C_MAX_TRANSACTION_LEN 8
-
-static struct dentry *hl_debug_root;
-
-static int hl_debugfs_i2c_read(struct hl_device *hdev, u8 i2c_bus, u8 i2c_addr,
- u8 i2c_reg, u8 i2c_len, u64 *val)
-{
- struct cpucp_packet pkt;
- int rc;
-
- if (!hl_device_operational(hdev, NULL))
- return -EBUSY;
-
- if (i2c_len > I2C_MAX_TRANSACTION_LEN) {
- dev_err(hdev->dev, "I2C transaction length %u, exceeds maximum of %u\n",
- i2c_len, I2C_MAX_TRANSACTION_LEN);
- return -EINVAL;
- }
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_I2C_RD <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.i2c_bus = i2c_bus;
- pkt.i2c_addr = i2c_addr;
- pkt.i2c_reg = i2c_reg;
- pkt.i2c_len = i2c_len;
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, val);
- if (rc)
- dev_err(hdev->dev, "Failed to read from I2C, error %d\n", rc);
-
- return rc;
-}
-
-static int hl_debugfs_i2c_write(struct hl_device *hdev, u8 i2c_bus, u8 i2c_addr,
- u8 i2c_reg, u8 i2c_len, u64 val)
-{
- struct cpucp_packet pkt;
- int rc;
-
- if (!hl_device_operational(hdev, NULL))
- return -EBUSY;
-
- if (i2c_len > I2C_MAX_TRANSACTION_LEN) {
- dev_err(hdev->dev, "I2C transaction length %u, exceeds maximum of %u\n",
- i2c_len, I2C_MAX_TRANSACTION_LEN);
- return -EINVAL;
- }
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_I2C_WR <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.i2c_bus = i2c_bus;
- pkt.i2c_addr = i2c_addr;
- pkt.i2c_reg = i2c_reg;
- pkt.i2c_len = i2c_len;
- pkt.value = cpu_to_le64(val);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, NULL);
-
- if (rc)
- dev_err(hdev->dev, "Failed to write to I2C, error %d\n", rc);
-
- return rc;
-}
-
-static void hl_debugfs_led_set(struct hl_device *hdev, u8 led, u8 state)
-{
- struct cpucp_packet pkt;
- int rc;
-
- if (!hl_device_operational(hdev, NULL))
- return;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_LED_SET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.led_index = cpu_to_le32(led);
- pkt.value = cpu_to_le64(state);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, NULL);
-
- if (rc)
- dev_err(hdev->dev, "Failed to set LED %d, error %d\n", led, rc);
-}
-
-static int command_buffers_show(struct seq_file *s, void *data)
-{
- struct hl_debugfs_entry *entry = s->private;
- struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
- struct hl_cb *cb;
- bool first = true;
-
- spin_lock(&dev_entry->cb_spinlock);
-
- list_for_each_entry(cb, &dev_entry->cb_list, debugfs_list) {
- if (first) {
- first = false;
- seq_puts(s, "\n");
- seq_puts(s, " CB ID CTX ID CB size CB RefCnt mmap? CS counter\n");
- seq_puts(s, "---------------------------------------------------------------\n");
- }
- seq_printf(s,
- " %03llu %d 0x%08x %d %d %d\n",
- cb->buf->handle, cb->ctx->asid, cb->size,
- kref_read(&cb->buf->refcount),
- atomic_read(&cb->buf->mmap), atomic_read(&cb->cs_cnt));
- }
-
- spin_unlock(&dev_entry->cb_spinlock);
-
- if (!first)
- seq_puts(s, "\n");
-
- return 0;
-}
-
-static int command_submission_show(struct seq_file *s, void *data)
-{
- struct hl_debugfs_entry *entry = s->private;
- struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
- struct hl_cs *cs;
- bool first = true;
-
- spin_lock(&dev_entry->cs_spinlock);
-
- list_for_each_entry(cs, &dev_entry->cs_list, debugfs_list) {
- if (first) {
- first = false;
- seq_puts(s, "\n");
- seq_puts(s, " CS ID CS TYPE CTX ASID CS RefCnt Submitted Completed\n");
- seq_puts(s, "----------------------------------------------------------------\n");
- }
- seq_printf(s,
- " %llu %d %d %d %d %d\n",
- cs->sequence, cs->type, cs->ctx->asid,
- kref_read(&cs->refcount),
- cs->submitted, cs->completed);
- }
-
- spin_unlock(&dev_entry->cs_spinlock);
-
- if (!first)
- seq_puts(s, "\n");
-
- return 0;
-}
-
-static int command_submission_jobs_show(struct seq_file *s, void *data)
-{
- struct hl_debugfs_entry *entry = s->private;
- struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
- struct hl_cs_job *job;
- bool first = true;
-
- spin_lock(&dev_entry->cs_job_spinlock);
-
- list_for_each_entry(job, &dev_entry->cs_job_list, debugfs_list) {
- if (first) {
- first = false;
- seq_puts(s, "\n");
- seq_puts(s, " JOB ID CS ID CS TYPE CTX ASID JOB RefCnt H/W Queue\n");
- seq_puts(s, "---------------------------------------------------------------\n");
- }
- if (job->cs)
- seq_printf(s,
- " %02d %llu %d %d %d %d\n",
- job->id, job->cs->sequence, job->cs->type,
- job->cs->ctx->asid, kref_read(&job->refcount),
- job->hw_queue_id);
- else
- seq_printf(s,
- " %02d 0 0 %d %d %d\n",
- job->id, HL_KERNEL_ASID_ID,
- kref_read(&job->refcount), job->hw_queue_id);
- }
-
- spin_unlock(&dev_entry->cs_job_spinlock);
-
- if (!first)
- seq_puts(s, "\n");
-
- return 0;
-}
-
-static int userptr_show(struct seq_file *s, void *data)
-{
- struct hl_debugfs_entry *entry = s->private;
- struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
- struct hl_userptr *userptr;
- char dma_dir[4][30] = {"DMA_BIDIRECTIONAL", "DMA_TO_DEVICE",
- "DMA_FROM_DEVICE", "DMA_NONE"};
- bool first = true;
-
- spin_lock(&dev_entry->userptr_spinlock);
-
- list_for_each_entry(userptr, &dev_entry->userptr_list, debugfs_list) {
- if (first) {
- first = false;
- seq_puts(s, "\n");
- seq_puts(s, " pid user virtual address size dma dir\n");
- seq_puts(s, "----------------------------------------------------------\n");
- }
- seq_printf(s, " %-7d 0x%-14llx %-10llu %-30s\n",
- userptr->pid, userptr->addr, userptr->size,
- dma_dir[userptr->dir]);
- }
-
- spin_unlock(&dev_entry->userptr_spinlock);
-
- if (!first)
- seq_puts(s, "\n");
-
- return 0;
-}
-
-static int vm_show(struct seq_file *s, void *data)
-{
- struct hl_debugfs_entry *entry = s->private;
- struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
- struct hl_vm_hw_block_list_node *lnode;
- struct hl_ctx *ctx;
- struct hl_vm *vm;
- struct hl_vm_hash_node *hnode;
- struct hl_userptr *userptr;
- struct hl_vm_phys_pg_pack *phys_pg_pack = NULL;
- struct hl_va_range *va_range;
- struct hl_vm_va_block *va_block;
- enum vm_type *vm_type;
- bool once = true;
- u64 j;
- int i;
-
- if (!dev_entry->hdev->mmu_enable)
- return 0;
-
- spin_lock(&dev_entry->ctx_mem_hash_spinlock);
-
- list_for_each_entry(ctx, &dev_entry->ctx_mem_hash_list, debugfs_list) {
- once = false;
- seq_puts(s, "\n\n----------------------------------------------------");
- seq_puts(s, "\n----------------------------------------------------\n\n");
- seq_printf(s, "ctx asid: %u\n", ctx->asid);
-
- seq_puts(s, "\nmappings:\n\n");
- seq_puts(s, " virtual address size handle\n");
- seq_puts(s, "----------------------------------------------------\n");
- mutex_lock(&ctx->mem_hash_lock);
- hash_for_each(ctx->mem_hash, i, hnode, node) {
- vm_type = hnode->ptr;
-
- if (*vm_type == VM_TYPE_USERPTR) {
- userptr = hnode->ptr;
- seq_printf(s,
- " 0x%-14llx %-10llu\n",
- hnode->vaddr, userptr->size);
- } else {
- phys_pg_pack = hnode->ptr;
- seq_printf(s,
- " 0x%-14llx %-10llu %-4u\n",
- hnode->vaddr, phys_pg_pack->total_size,
- phys_pg_pack->handle);
- }
- }
- mutex_unlock(&ctx->mem_hash_lock);
-
- if (ctx->asid != HL_KERNEL_ASID_ID &&
- !list_empty(&ctx->hw_block_mem_list)) {
- seq_puts(s, "\nhw_block mappings:\n\n");
- seq_puts(s,
- " virtual address block size mapped size HW block id\n");
- seq_puts(s,
- "---------------------------------------------------------------\n");
- mutex_lock(&ctx->hw_block_list_lock);
- list_for_each_entry(lnode, &ctx->hw_block_mem_list, node) {
- seq_printf(s,
- " 0x%-14lx %-6u %-6u %-9u\n",
- lnode->vaddr, lnode->block_size, lnode->mapped_size,
- lnode->id);
- }
- mutex_unlock(&ctx->hw_block_list_lock);
- }
-
- vm = &ctx->hdev->vm;
- spin_lock(&vm->idr_lock);
-
- if (!idr_is_empty(&vm->phys_pg_pack_handles))
- seq_puts(s, "\n\nallocations:\n");
-
- idr_for_each_entry(&vm->phys_pg_pack_handles, phys_pg_pack, i) {
- if (phys_pg_pack->asid != ctx->asid)
- continue;
-
- seq_printf(s, "\nhandle: %u\n", phys_pg_pack->handle);
- seq_printf(s, "page size: %u\n\n",
- phys_pg_pack->page_size);
- seq_puts(s, " physical address\n");
- seq_puts(s, "---------------------\n");
- for (j = 0 ; j < phys_pg_pack->npages ; j++) {
- seq_printf(s, " 0x%-14llx\n",
- phys_pg_pack->pages[j]);
- }
- }
- spin_unlock(&vm->idr_lock);
-
- }
-
- spin_unlock(&dev_entry->ctx_mem_hash_spinlock);
-
- ctx = hl_get_compute_ctx(dev_entry->hdev);
- if (ctx) {
- seq_puts(s, "\nVA ranges:\n\n");
- for (i = HL_VA_RANGE_TYPE_HOST ; i < HL_VA_RANGE_TYPE_MAX ; ++i) {
- va_range = ctx->va_range[i];
- seq_printf(s, " va_range %d\n", i);
- seq_puts(s, "---------------------\n");
- mutex_lock(&va_range->lock);
- list_for_each_entry(va_block, &va_range->list, node) {
- seq_printf(s, "%#16llx - %#16llx (%#llx)\n",
- va_block->start, va_block->end,
- va_block->size);
- }
- mutex_unlock(&va_range->lock);
- seq_puts(s, "\n");
- }
- hl_ctx_put(ctx);
- }
-
- if (!once)
- seq_puts(s, "\n");
-
- return 0;
-}
-
-static int userptr_lookup_show(struct seq_file *s, void *data)
-{
- struct hl_debugfs_entry *entry = s->private;
- struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
- struct scatterlist *sg;
- struct hl_userptr *userptr;
- bool first = true;
- u64 total_npages, npages, sg_start, sg_end;
- dma_addr_t dma_addr;
- int i;
-
- spin_lock(&dev_entry->userptr_spinlock);
-
- list_for_each_entry(userptr, &dev_entry->userptr_list, debugfs_list) {
- if (dev_entry->userptr_lookup >= userptr->addr &&
- dev_entry->userptr_lookup < userptr->addr + userptr->size) {
- total_npages = 0;
- for_each_sgtable_dma_sg(userptr->sgt, sg, i) {
- npages = hl_get_sg_info(sg, &dma_addr);
- sg_start = userptr->addr +
- total_npages * PAGE_SIZE;
- sg_end = userptr->addr +
- (total_npages + npages) * PAGE_SIZE;
-
- if (dev_entry->userptr_lookup >= sg_start &&
- dev_entry->userptr_lookup < sg_end) {
- dma_addr += (dev_entry->userptr_lookup -
- sg_start);
- if (first) {
- first = false;
- seq_puts(s, "\n");
- seq_puts(s, " user virtual address dma address pid region start region size\n");
- seq_puts(s, "---------------------------------------------------------------------------------------\n");
- }
- seq_printf(s, " 0x%-18llx 0x%-16llx %-8u 0x%-16llx %-12llu\n",
- dev_entry->userptr_lookup,
- (u64)dma_addr, userptr->pid,
- userptr->addr, userptr->size);
- }
- total_npages += npages;
- }
- }
- }
-
- spin_unlock(&dev_entry->userptr_spinlock);
-
- if (!first)
- seq_puts(s, "\n");
-
- return 0;
-}
-
-static ssize_t userptr_lookup_write(struct file *file, const char __user *buf,
- size_t count, loff_t *f_pos)
-{
- struct seq_file *s = file->private_data;
- struct hl_debugfs_entry *entry = s->private;
- struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
- ssize_t rc;
- u64 value;
-
- rc = kstrtoull_from_user(buf, count, 16, &value);
- if (rc)
- return rc;
-
- dev_entry->userptr_lookup = value;
-
- return count;
-}
-
-static int mmu_show(struct seq_file *s, void *data)
-{
- struct hl_debugfs_entry *entry = s->private;
- struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
- struct hl_device *hdev = dev_entry->hdev;
- struct hl_ctx *ctx;
- struct hl_mmu_hop_info hops_info = {0};
- u64 virt_addr = dev_entry->mmu_addr, phys_addr;
- int i;
-
- if (!hdev->mmu_enable)
- return 0;
-
- if (dev_entry->mmu_asid == HL_KERNEL_ASID_ID)
- ctx = hdev->kernel_ctx;
- else
- ctx = hl_get_compute_ctx(hdev);
-
- if (!ctx) {
- dev_err(hdev->dev, "no ctx available\n");
- return 0;
- }
-
- if (hl_mmu_get_tlb_info(ctx, virt_addr, &hops_info)) {
- dev_err(hdev->dev, "virt addr 0x%llx is not mapped to phys addr\n",
- virt_addr);
- goto put_ctx;
- }
-
- hl_mmu_va_to_pa(ctx, virt_addr, &phys_addr);
-
- if (hops_info.scrambled_vaddr &&
- (dev_entry->mmu_addr != hops_info.scrambled_vaddr))
- seq_printf(s,
- "asid: %u, virt_addr: 0x%llx, scrambled virt_addr: 0x%llx,\nphys_addr: 0x%llx, scrambled_phys_addr: 0x%llx\n",
- dev_entry->mmu_asid, dev_entry->mmu_addr,
- hops_info.scrambled_vaddr,
- hops_info.unscrambled_paddr, phys_addr);
- else
- seq_printf(s,
- "asid: %u, virt_addr: 0x%llx, phys_addr: 0x%llx\n",
- dev_entry->mmu_asid, dev_entry->mmu_addr, phys_addr);
-
- for (i = 0 ; i < hops_info.used_hops ; i++) {
- seq_printf(s, "hop%d_addr: 0x%llx\n",
- i, hops_info.hop_info[i].hop_addr);
- seq_printf(s, "hop%d_pte_addr: 0x%llx\n",
- i, hops_info.hop_info[i].hop_pte_addr);
- seq_printf(s, "hop%d_pte: 0x%llx\n",
- i, hops_info.hop_info[i].hop_pte_val);
- }
-
-put_ctx:
- if (dev_entry->mmu_asid != HL_KERNEL_ASID_ID)
- hl_ctx_put(ctx);
-
- return 0;
-}
-
-static ssize_t mmu_asid_va_write(struct file *file, const char __user *buf,
- size_t count, loff_t *f_pos)
-{
- struct seq_file *s = file->private_data;
- struct hl_debugfs_entry *entry = s->private;
- struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
- struct hl_device *hdev = dev_entry->hdev;
- char kbuf[MMU_KBUF_SIZE];
- char *c;
- ssize_t rc;
-
- if (!hdev->mmu_enable)
- return count;
-
- if (count > sizeof(kbuf) - 1)
- goto err;
- if (copy_from_user(kbuf, buf, count))
- goto err;
- kbuf[count] = 0;
-
- c = strchr(kbuf, ' ');
- if (!c)
- goto err;
- *c = '\0';
-
- rc = kstrtouint(kbuf, 10, &dev_entry->mmu_asid);
- if (rc)
- goto err;
-
- if (strncmp(c+1, "0x", 2))
- goto err;
- rc = kstrtoull(c+3, 16, &dev_entry->mmu_addr);
- if (rc)
- goto err;
-
- return count;
-
-err:
- dev_err(hdev->dev, "usage: echo <asid> <0xaddr> > mmu\n");
-
- return -EINVAL;
-}
-
-static int mmu_ack_error(struct seq_file *s, void *data)
-{
- struct hl_debugfs_entry *entry = s->private;
- struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
- struct hl_device *hdev = dev_entry->hdev;
- int rc;
-
- if (!hdev->mmu_enable)
- return 0;
-
- if (!dev_entry->mmu_cap_mask) {
- dev_err(hdev->dev, "mmu_cap_mask is not set\n");
- goto err;
- }
-
- rc = hdev->asic_funcs->ack_mmu_errors(hdev, dev_entry->mmu_cap_mask);
- if (rc)
- goto err;
-
- return 0;
-err:
- return -EINVAL;
-}
-
-static ssize_t mmu_ack_error_value_write(struct file *file,
- const char __user *buf,
- size_t count, loff_t *f_pos)
-{
- struct seq_file *s = file->private_data;
- struct hl_debugfs_entry *entry = s->private;
- struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
- struct hl_device *hdev = dev_entry->hdev;
- char kbuf[MMU_KBUF_SIZE];
- ssize_t rc;
-
- if (!hdev->mmu_enable)
- return count;
-
- if (count > sizeof(kbuf) - 1)
- goto err;
-
- if (copy_from_user(kbuf, buf, count))
- goto err;
-
- kbuf[count] = 0;
-
- if (strncmp(kbuf, "0x", 2))
- goto err;
-
- rc = kstrtoull(kbuf, 16, &dev_entry->mmu_cap_mask);
- if (rc)
- goto err;
-
- return count;
-err:
- dev_err(hdev->dev, "usage: echo <0xmmu_cap_mask > > mmu_error\n");
-
- return -EINVAL;
-}
-
-static int engines_show(struct seq_file *s, void *data)
-{
- struct hl_debugfs_entry *entry = s->private;
- struct hl_dbg_device_entry *dev_entry = entry->dev_entry;
- struct hl_device *hdev = dev_entry->hdev;
- struct engines_data eng_data;
-
- if (hdev->reset_info.in_reset) {
- dev_warn_ratelimited(hdev->dev,
- "Can't check device idle during reset\n");
- return 0;
- }
-
- eng_data.actual_size = 0;
- eng_data.allocated_buf_size = HL_ENGINES_DATA_MAX_SIZE;
- eng_data.buf = vmalloc(eng_data.allocated_buf_size);
- if (!eng_data.buf)
- return -ENOMEM;
-
- hdev->asic_funcs->is_device_idle(hdev, NULL, 0, &eng_data);
-
- if (eng_data.actual_size > eng_data.allocated_buf_size) {
- dev_err(hdev->dev,
- "Engines data size (%d Bytes) is bigger than allocated size (%u Bytes)\n",
- eng_data.actual_size, eng_data.allocated_buf_size);
- vfree(eng_data.buf);
- return -ENOMEM;
- }
-
- seq_write(s, eng_data.buf, eng_data.actual_size);
-
- vfree(eng_data.buf);
-
- return 0;
-}
-
-static ssize_t hl_memory_scrub(struct file *f, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- u64 val = hdev->memory_scrub_val;
- int rc;
-
- if (!hl_device_operational(hdev, NULL)) {
- dev_warn_ratelimited(hdev->dev, "Can't scrub memory, device is not operational\n");
- return -EIO;
- }
-
- mutex_lock(&hdev->fpriv_list_lock);
- if (hdev->is_compute_ctx_active) {
- mutex_unlock(&hdev->fpriv_list_lock);
- dev_err(hdev->dev, "can't scrub dram, context exist\n");
- return -EBUSY;
- }
- hdev->is_in_dram_scrub = true;
- mutex_unlock(&hdev->fpriv_list_lock);
-
- rc = hdev->asic_funcs->scrub_device_dram(hdev, val);
-
- mutex_lock(&hdev->fpriv_list_lock);
- hdev->is_in_dram_scrub = false;
- mutex_unlock(&hdev->fpriv_list_lock);
-
- if (rc)
- return rc;
- return count;
-}
-
-static bool hl_is_device_va(struct hl_device *hdev, u64 addr)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
-
- if (!hdev->mmu_enable)
- goto out;
-
- if (prop->dram_supports_virtual_memory &&
- (addr >= prop->dmmu.start_addr && addr < prop->dmmu.end_addr))
- return true;
-
- if (addr >= prop->pmmu.start_addr &&
- addr < prop->pmmu.end_addr)
- return true;
-
- if (addr >= prop->pmmu_huge.start_addr &&
- addr < prop->pmmu_huge.end_addr)
- return true;
-out:
- return false;
-}
-
-static bool hl_is_device_internal_memory_va(struct hl_device *hdev, u64 addr,
- u32 size)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u64 dram_start_addr, dram_end_addr;
-
- if (!hdev->mmu_enable)
- return false;
-
- if (prop->dram_supports_virtual_memory) {
- dram_start_addr = prop->dmmu.start_addr;
- dram_end_addr = prop->dmmu.end_addr;
- } else {
- dram_start_addr = prop->dram_base_address;
- dram_end_addr = prop->dram_end_address;
- }
-
- if (hl_mem_area_inside_range(addr, size, dram_start_addr,
- dram_end_addr))
- return true;
-
- if (hl_mem_area_inside_range(addr, size, prop->sram_base_address,
- prop->sram_end_address))
- return true;
-
- return false;
-}
-
-static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr, u32 size,
- u64 *phys_addr)
-{
- struct hl_vm_phys_pg_pack *phys_pg_pack;
- struct hl_ctx *ctx;
- struct hl_vm_hash_node *hnode;
- u64 end_address, range_size;
- struct hl_userptr *userptr;
- enum vm_type *vm_type;
- bool valid = false;
- int i, rc = 0;
-
- ctx = hl_get_compute_ctx(hdev);
-
- if (!ctx) {
- dev_err(hdev->dev, "no ctx available\n");
- return -EINVAL;
- }
-
- /* Verify address is mapped */
- mutex_lock(&ctx->mem_hash_lock);
- hash_for_each(ctx->mem_hash, i, hnode, node) {
- vm_type = hnode->ptr;
-
- if (*vm_type == VM_TYPE_USERPTR) {
- userptr = hnode->ptr;
- range_size = userptr->size;
- } else {
- phys_pg_pack = hnode->ptr;
- range_size = phys_pg_pack->total_size;
- }
-
- end_address = virt_addr + size;
- if ((virt_addr >= hnode->vaddr) &&
- (end_address <= hnode->vaddr + range_size)) {
- valid = true;
- break;
- }
- }
- mutex_unlock(&ctx->mem_hash_lock);
-
- if (!valid) {
- dev_err(hdev->dev,
- "virt addr 0x%llx is not mapped\n",
- virt_addr);
- rc = -EINVAL;
- goto put_ctx;
- }
-
- rc = hl_mmu_va_to_pa(ctx, virt_addr, phys_addr);
- if (rc) {
- dev_err(hdev->dev,
- "virt addr 0x%llx is not mapped to phys addr\n",
- virt_addr);
- rc = -EINVAL;
- }
-
-put_ctx:
- hl_ctx_put(ctx);
-
- return rc;
-}
-
-static int hl_access_dev_mem_by_region(struct hl_device *hdev, u64 addr,
- u64 *val, enum debugfs_access_type acc_type, bool *found)
-{
- size_t acc_size = (acc_type == DEBUGFS_READ64 || acc_type == DEBUGFS_WRITE64) ?
- sizeof(u64) : sizeof(u32);
- struct pci_mem_region *mem_reg;
- int i;
-
- for (i = 0; i < PCI_REGION_NUMBER; i++) {
- mem_reg = &hdev->pci_mem_region[i];
- if (!mem_reg->used)
- continue;
- if (addr >= mem_reg->region_base &&
- addr <= mem_reg->region_base + mem_reg->region_size - acc_size) {
- *found = true;
- return hdev->asic_funcs->access_dev_mem(hdev, i, addr, val, acc_type);
- }
- }
- return 0;
-}
-
-static void hl_access_host_mem(struct hl_device *hdev, u64 addr, u64 *val,
- enum debugfs_access_type acc_type)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u64 offset = prop->device_dma_offset_for_host_access;
-
- switch (acc_type) {
- case DEBUGFS_READ32:
- *val = *(u32 *) phys_to_virt(addr - offset);
- break;
- case DEBUGFS_WRITE32:
- *(u32 *) phys_to_virt(addr - offset) = *val;
- break;
- case DEBUGFS_READ64:
- *val = *(u64 *) phys_to_virt(addr - offset);
- break;
- case DEBUGFS_WRITE64:
- *(u64 *) phys_to_virt(addr - offset) = *val;
- break;
- default:
- dev_err(hdev->dev, "hostmem access-type %d id not supported\n", acc_type);
- break;
- }
-}
-
-static int hl_access_mem(struct hl_device *hdev, u64 addr, u64 *val,
- enum debugfs_access_type acc_type)
-{
- size_t acc_size = (acc_type == DEBUGFS_READ64 || acc_type == DEBUGFS_WRITE64) ?
- sizeof(u64) : sizeof(u32);
- u64 host_start = hdev->asic_prop.host_base_address;
- u64 host_end = hdev->asic_prop.host_end_address;
- bool user_address, found = false;
- int rc;
-
- user_address = hl_is_device_va(hdev, addr);
- if (user_address) {
- rc = device_va_to_pa(hdev, addr, acc_size, &addr);
- if (rc)
- return rc;
- }
-
- rc = hl_access_dev_mem_by_region(hdev, addr, val, acc_type, &found);
- if (rc) {
- dev_err(hdev->dev,
- "Failed reading addr %#llx from dev mem (%d)\n",
- addr, rc);
- return rc;
- }
-
- if (found)
- return 0;
-
- if (!user_address || device_iommu_mapped(&hdev->pdev->dev)) {
- rc = -EINVAL;
- goto err;
- }
-
- if (addr >= host_start && addr <= host_end - acc_size) {
- hl_access_host_mem(hdev, addr, val, acc_type);
- } else {
- rc = -EINVAL;
- goto err;
- }
-
- return 0;
-err:
- dev_err(hdev->dev, "invalid addr %#llx\n", addr);
- return rc;
-}
-
-static ssize_t hl_data_read32(struct file *f, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- u64 value64, addr = entry->addr;
- char tmp_buf[32];
- ssize_t rc;
- u32 val;
-
- if (hdev->reset_info.in_reset) {
- dev_warn_ratelimited(hdev->dev, "Can't read during reset\n");
- return 0;
- }
-
- if (*ppos)
- return 0;
-
- rc = hl_access_mem(hdev, addr, &value64, DEBUGFS_READ32);
- if (rc)
- return rc;
-
- val = value64; /* downcast back to 32 */
-
- sprintf(tmp_buf, "0x%08x\n", val);
- return simple_read_from_buffer(buf, count, ppos, tmp_buf,
- strlen(tmp_buf));
-}
-
-static ssize_t hl_data_write32(struct file *f, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- u64 value64, addr = entry->addr;
- u32 value;
- ssize_t rc;
-
- if (hdev->reset_info.in_reset) {
- dev_warn_ratelimited(hdev->dev, "Can't write during reset\n");
- return 0;
- }
-
- rc = kstrtouint_from_user(buf, count, 16, &value);
- if (rc)
- return rc;
-
- value64 = value;
- rc = hl_access_mem(hdev, addr, &value64, DEBUGFS_WRITE32);
- if (rc)
- return rc;
-
- return count;
-}
-
-static ssize_t hl_data_read64(struct file *f, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- u64 addr = entry->addr;
- char tmp_buf[32];
- ssize_t rc;
- u64 val;
-
- if (hdev->reset_info.in_reset) {
- dev_warn_ratelimited(hdev->dev, "Can't read during reset\n");
- return 0;
- }
-
- if (*ppos)
- return 0;
-
- rc = hl_access_mem(hdev, addr, &val, DEBUGFS_READ64);
- if (rc)
- return rc;
-
- sprintf(tmp_buf, "0x%016llx\n", val);
- return simple_read_from_buffer(buf, count, ppos, tmp_buf,
- strlen(tmp_buf));
-}
-
-static ssize_t hl_data_write64(struct file *f, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- u64 addr = entry->addr;
- u64 value;
- ssize_t rc;
-
- if (hdev->reset_info.in_reset) {
- dev_warn_ratelimited(hdev->dev, "Can't write during reset\n");
- return 0;
- }
-
- rc = kstrtoull_from_user(buf, count, 16, &value);
- if (rc)
- return rc;
-
- rc = hl_access_mem(hdev, addr, &value, DEBUGFS_WRITE64);
- if (rc)
- return rc;
-
- return count;
-}
-
-static ssize_t hl_dma_size_write(struct file *f, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- u64 addr = entry->addr;
- ssize_t rc;
- u32 size;
-
- if (hdev->reset_info.in_reset) {
- dev_warn_ratelimited(hdev->dev, "Can't DMA during reset\n");
- return 0;
- }
- rc = kstrtouint_from_user(buf, count, 16, &size);
- if (rc)
- return rc;
-
- if (!size) {
- dev_err(hdev->dev, "DMA read failed. size can't be 0\n");
- return -EINVAL;
- }
-
- if (size > SZ_128M) {
- dev_err(hdev->dev,
- "DMA read failed. size can't be larger than 128MB\n");
- return -EINVAL;
- }
-
- if (!hl_is_device_internal_memory_va(hdev, addr, size)) {
- dev_err(hdev->dev,
- "DMA read failed. Invalid 0x%010llx + 0x%08x\n",
- addr, size);
- return -EINVAL;
- }
-
- /* Free the previous allocation, if there was any */
- entry->data_dma_blob_desc.size = 0;
- vfree(entry->data_dma_blob_desc.data);
-
- entry->data_dma_blob_desc.data = vmalloc(size);
- if (!entry->data_dma_blob_desc.data)
- return -ENOMEM;
-
- rc = hdev->asic_funcs->debugfs_read_dma(hdev, addr, size,
- entry->data_dma_blob_desc.data);
- if (rc) {
- dev_err(hdev->dev, "Failed to DMA from 0x%010llx\n", addr);
- vfree(entry->data_dma_blob_desc.data);
- entry->data_dma_blob_desc.data = NULL;
- return -EIO;
- }
-
- entry->data_dma_blob_desc.size = size;
-
- return count;
-}
-
-static ssize_t hl_monitor_dump_trigger(struct file *f, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- u32 size, trig;
- ssize_t rc;
-
- if (hdev->reset_info.in_reset) {
- dev_warn_ratelimited(hdev->dev, "Can't dump monitors during reset\n");
- return 0;
- }
- rc = kstrtouint_from_user(buf, count, 10, &trig);
- if (rc)
- return rc;
-
- if (trig != 1) {
- dev_err(hdev->dev, "Must write 1 to trigger monitor dump\n");
- return -EINVAL;
- }
-
- size = sizeof(struct cpucp_monitor_dump);
-
- /* Free the previous allocation, if there was any */
- entry->mon_dump_blob_desc.size = 0;
- vfree(entry->mon_dump_blob_desc.data);
-
- entry->mon_dump_blob_desc.data = vmalloc(size);
- if (!entry->mon_dump_blob_desc.data)
- return -ENOMEM;
-
- rc = hdev->asic_funcs->get_monitor_dump(hdev, entry->mon_dump_blob_desc.data);
- if (rc) {
- dev_err(hdev->dev, "Failed to dump monitors\n");
- vfree(entry->mon_dump_blob_desc.data);
- entry->mon_dump_blob_desc.data = NULL;
- return -EIO;
- }
-
- entry->mon_dump_blob_desc.size = size;
-
- return count;
-}
-
-static ssize_t hl_get_power_state(struct file *f, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- char tmp_buf[200];
- int i;
-
- if (*ppos)
- return 0;
-
- if (hdev->pdev->current_state == PCI_D0)
- i = 1;
- else if (hdev->pdev->current_state == PCI_D3hot)
- i = 2;
- else
- i = 3;
-
- sprintf(tmp_buf,
- "current power state: %d\n1 - D0\n2 - D3hot\n3 - Unknown\n", i);
- return simple_read_from_buffer(buf, count, ppos, tmp_buf,
- strlen(tmp_buf));
-}
-
-static ssize_t hl_set_power_state(struct file *f, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- u32 value;
- ssize_t rc;
-
- rc = kstrtouint_from_user(buf, count, 10, &value);
- if (rc)
- return rc;
-
- if (value == 1) {
- pci_set_power_state(hdev->pdev, PCI_D0);
- pci_restore_state(hdev->pdev);
- rc = pci_enable_device(hdev->pdev);
- if (rc < 0)
- return rc;
- } else if (value == 2) {
- pci_save_state(hdev->pdev);
- pci_disable_device(hdev->pdev);
- pci_set_power_state(hdev->pdev, PCI_D3hot);
- } else {
- dev_dbg(hdev->dev, "invalid power state value %u\n", value);
- return -EINVAL;
- }
-
- return count;
-}
-
-static ssize_t hl_i2c_data_read(struct file *f, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- char tmp_buf[32];
- u64 val;
- ssize_t rc;
-
- if (*ppos)
- return 0;
-
- rc = hl_debugfs_i2c_read(hdev, entry->i2c_bus, entry->i2c_addr,
- entry->i2c_reg, entry->i2c_len, &val);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to read from I2C bus %d, addr %d, reg %d, len %d\n",
- entry->i2c_bus, entry->i2c_addr, entry->i2c_reg, entry->i2c_len);
- return rc;
- }
-
- sprintf(tmp_buf, "%#02llx\n", val);
- rc = simple_read_from_buffer(buf, count, ppos, tmp_buf,
- strlen(tmp_buf));
-
- return rc;
-}
-
-static ssize_t hl_i2c_data_write(struct file *f, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- u64 value;
- ssize_t rc;
-
- rc = kstrtou64_from_user(buf, count, 16, &value);
- if (rc)
- return rc;
-
- rc = hl_debugfs_i2c_write(hdev, entry->i2c_bus, entry->i2c_addr,
- entry->i2c_reg, entry->i2c_len, value);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to write %#02llx to I2C bus %d, addr %d, reg %d, len %d\n",
- value, entry->i2c_bus, entry->i2c_addr, entry->i2c_reg, entry->i2c_len);
- return rc;
- }
-
- return count;
-}
-
-static ssize_t hl_led0_write(struct file *f, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- u32 value;
- ssize_t rc;
-
- rc = kstrtouint_from_user(buf, count, 10, &value);
- if (rc)
- return rc;
-
- value = value ? 1 : 0;
-
- hl_debugfs_led_set(hdev, 0, value);
-
- return count;
-}
-
-static ssize_t hl_led1_write(struct file *f, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- u32 value;
- ssize_t rc;
-
- rc = kstrtouint_from_user(buf, count, 10, &value);
- if (rc)
- return rc;
-
- value = value ? 1 : 0;
-
- hl_debugfs_led_set(hdev, 1, value);
-
- return count;
-}
-
-static ssize_t hl_led2_write(struct file *f, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- u32 value;
- ssize_t rc;
-
- rc = kstrtouint_from_user(buf, count, 10, &value);
- if (rc)
- return rc;
-
- value = value ? 1 : 0;
-
- hl_debugfs_led_set(hdev, 2, value);
-
- return count;
-}
-
-static ssize_t hl_device_read(struct file *f, char __user *buf,
- size_t count, loff_t *ppos)
-{
- static const char *help =
- "Valid values: disable, enable, suspend, resume, cpu_timeout\n";
- return simple_read_from_buffer(buf, count, ppos, help, strlen(help));
-}
-
-static ssize_t hl_device_write(struct file *f, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- char data[30] = {0};
-
- /* don't allow partial writes */
- if (*ppos != 0)
- return 0;
-
- simple_write_to_buffer(data, 29, ppos, buf, count);
-
- if (strncmp("disable", data, strlen("disable")) == 0) {
- hdev->disabled = true;
- } else if (strncmp("enable", data, strlen("enable")) == 0) {
- hdev->disabled = false;
- } else if (strncmp("suspend", data, strlen("suspend")) == 0) {
- hdev->asic_funcs->suspend(hdev);
- } else if (strncmp("resume", data, strlen("resume")) == 0) {
- hdev->asic_funcs->resume(hdev);
- } else if (strncmp("cpu_timeout", data, strlen("cpu_timeout")) == 0) {
- hdev->device_cpu_disabled = true;
- } else {
- dev_err(hdev->dev,
- "Valid values: disable, enable, suspend, resume, cpu_timeout\n");
- count = -EINVAL;
- }
-
- return count;
-}
-
-static ssize_t hl_clk_gate_read(struct file *f, char __user *buf,
- size_t count, loff_t *ppos)
-{
- return 0;
-}
-
-static ssize_t hl_clk_gate_write(struct file *f, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- return count;
-}
-
-static ssize_t hl_stop_on_err_read(struct file *f, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- char tmp_buf[200];
- ssize_t rc;
-
- if (!hdev->asic_prop.configurable_stop_on_err)
- return -EOPNOTSUPP;
-
- if (*ppos)
- return 0;
-
- sprintf(tmp_buf, "%d\n", hdev->stop_on_err);
- rc = simple_read_from_buffer(buf, strlen(tmp_buf) + 1, ppos, tmp_buf,
- strlen(tmp_buf) + 1);
-
- return rc;
-}
-
-static ssize_t hl_stop_on_err_write(struct file *f, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- u32 value;
- ssize_t rc;
-
- if (!hdev->asic_prop.configurable_stop_on_err)
- return -EOPNOTSUPP;
-
- if (hdev->reset_info.in_reset) {
- dev_warn_ratelimited(hdev->dev,
- "Can't change stop on error during reset\n");
- return 0;
- }
-
- rc = kstrtouint_from_user(buf, count, 10, &value);
- if (rc)
- return rc;
-
- hdev->stop_on_err = value ? 1 : 0;
-
- hl_device_reset(hdev, 0);
-
- return count;
-}
-
-static ssize_t hl_security_violations_read(struct file *f, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
-
- hdev->asic_funcs->ack_protection_bits_errors(hdev);
-
- return 0;
-}
-
-static ssize_t hl_state_dump_read(struct file *f, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- ssize_t rc;
-
- down_read(&entry->state_dump_sem);
- if (!entry->state_dump[entry->state_dump_head])
- rc = 0;
- else
- rc = simple_read_from_buffer(
- buf, count, ppos,
- entry->state_dump[entry->state_dump_head],
- strlen(entry->state_dump[entry->state_dump_head]));
- up_read(&entry->state_dump_sem);
-
- return rc;
-}
-
-static ssize_t hl_state_dump_write(struct file *f, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- ssize_t rc;
- u32 size;
- int i;
-
- rc = kstrtouint_from_user(buf, count, 10, &size);
- if (rc)
- return rc;
-
- if (size <= 0 || size >= ARRAY_SIZE(entry->state_dump)) {
- dev_err(hdev->dev, "Invalid number of dumps to skip\n");
- return -EINVAL;
- }
-
- if (entry->state_dump[entry->state_dump_head]) {
- down_write(&entry->state_dump_sem);
- for (i = 0; i < size; ++i) {
- vfree(entry->state_dump[entry->state_dump_head]);
- entry->state_dump[entry->state_dump_head] = NULL;
- if (entry->state_dump_head > 0)
- entry->state_dump_head--;
- else
- entry->state_dump_head =
- ARRAY_SIZE(entry->state_dump) - 1;
- }
- up_write(&entry->state_dump_sem);
- }
-
- return count;
-}
-
-static ssize_t hl_timeout_locked_read(struct file *f, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- char tmp_buf[200];
- ssize_t rc;
-
- if (*ppos)
- return 0;
-
- sprintf(tmp_buf, "%d\n",
- jiffies_to_msecs(hdev->timeout_jiffies) / 1000);
- rc = simple_read_from_buffer(buf, strlen(tmp_buf) + 1, ppos, tmp_buf,
- strlen(tmp_buf) + 1);
-
- return rc;
-}
-
-static ssize_t hl_timeout_locked_write(struct file *f, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
- u32 value;
- ssize_t rc;
-
- rc = kstrtouint_from_user(buf, count, 10, &value);
- if (rc)
- return rc;
-
- if (value)
- hdev->timeout_jiffies = msecs_to_jiffies(value * 1000);
- else
- hdev->timeout_jiffies = MAX_SCHEDULE_TIMEOUT;
-
- return count;
-}
-
-static ssize_t hl_check_razwi_happened(struct file *f, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct hl_dbg_device_entry *entry = file_inode(f)->i_private;
- struct hl_device *hdev = entry->hdev;
-
- hdev->asic_funcs->check_if_razwi_happened(hdev);
-
- return 0;
-}
-
-static const struct file_operations hl_mem_scrub_fops = {
- .owner = THIS_MODULE,
- .write = hl_memory_scrub,
-};
-
-static const struct file_operations hl_data32b_fops = {
- .owner = THIS_MODULE,
- .read = hl_data_read32,
- .write = hl_data_write32
-};
-
-static const struct file_operations hl_data64b_fops = {
- .owner = THIS_MODULE,
- .read = hl_data_read64,
- .write = hl_data_write64
-};
-
-static const struct file_operations hl_dma_size_fops = {
- .owner = THIS_MODULE,
- .write = hl_dma_size_write
-};
-
-static const struct file_operations hl_monitor_dump_fops = {
- .owner = THIS_MODULE,
- .write = hl_monitor_dump_trigger
-};
-
-static const struct file_operations hl_i2c_data_fops = {
- .owner = THIS_MODULE,
- .read = hl_i2c_data_read,
- .write = hl_i2c_data_write
-};
-
-static const struct file_operations hl_power_fops = {
- .owner = THIS_MODULE,
- .read = hl_get_power_state,
- .write = hl_set_power_state
-};
-
-static const struct file_operations hl_led0_fops = {
- .owner = THIS_MODULE,
- .write = hl_led0_write
-};
-
-static const struct file_operations hl_led1_fops = {
- .owner = THIS_MODULE,
- .write = hl_led1_write
-};
-
-static const struct file_operations hl_led2_fops = {
- .owner = THIS_MODULE,
- .write = hl_led2_write
-};
-
-static const struct file_operations hl_device_fops = {
- .owner = THIS_MODULE,
- .read = hl_device_read,
- .write = hl_device_write
-};
-
-static const struct file_operations hl_clk_gate_fops = {
- .owner = THIS_MODULE,
- .read = hl_clk_gate_read,
- .write = hl_clk_gate_write
-};
-
-static const struct file_operations hl_stop_on_err_fops = {
- .owner = THIS_MODULE,
- .read = hl_stop_on_err_read,
- .write = hl_stop_on_err_write
-};
-
-static const struct file_operations hl_security_violations_fops = {
- .owner = THIS_MODULE,
- .read = hl_security_violations_read
-};
-
-static const struct file_operations hl_state_dump_fops = {
- .owner = THIS_MODULE,
- .read = hl_state_dump_read,
- .write = hl_state_dump_write
-};
-
-static const struct file_operations hl_timeout_locked_fops = {
- .owner = THIS_MODULE,
- .read = hl_timeout_locked_read,
- .write = hl_timeout_locked_write
-};
-
-static const struct file_operations hl_razwi_check_fops = {
- .owner = THIS_MODULE,
- .read = hl_check_razwi_happened
-};
-
-static const struct hl_info_list hl_debugfs_list[] = {
- {"command_buffers", command_buffers_show, NULL},
- {"command_submission", command_submission_show, NULL},
- {"command_submission_jobs", command_submission_jobs_show, NULL},
- {"userptr", userptr_show, NULL},
- {"vm", vm_show, NULL},
- {"userptr_lookup", userptr_lookup_show, userptr_lookup_write},
- {"mmu", mmu_show, mmu_asid_va_write},
- {"mmu_error", mmu_ack_error, mmu_ack_error_value_write},
- {"engines", engines_show, NULL},
-};
-
-static int hl_debugfs_open(struct inode *inode, struct file *file)
-{
- struct hl_debugfs_entry *node = inode->i_private;
-
- return single_open(file, node->info_ent->show, node);
-}
-
-static ssize_t hl_debugfs_write(struct file *file, const char __user *buf,
- size_t count, loff_t *f_pos)
-{
- struct hl_debugfs_entry *node = file->f_inode->i_private;
-
- if (node->info_ent->write)
- return node->info_ent->write(file, buf, count, f_pos);
- else
- return -EINVAL;
-
-}
-
-static const struct file_operations hl_debugfs_fops = {
- .owner = THIS_MODULE,
- .open = hl_debugfs_open,
- .read = seq_read,
- .write = hl_debugfs_write,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static void add_secured_nodes(struct hl_dbg_device_entry *dev_entry)
-{
- debugfs_create_u8("i2c_bus",
- 0644,
- dev_entry->root,
- &dev_entry->i2c_bus);
-
- debugfs_create_u8("i2c_addr",
- 0644,
- dev_entry->root,
- &dev_entry->i2c_addr);
-
- debugfs_create_u8("i2c_reg",
- 0644,
- dev_entry->root,
- &dev_entry->i2c_reg);
-
- debugfs_create_u8("i2c_len",
- 0644,
- dev_entry->root,
- &dev_entry->i2c_len);
-
- debugfs_create_file("i2c_data",
- 0644,
- dev_entry->root,
- dev_entry,
- &hl_i2c_data_fops);
-
- debugfs_create_file("led0",
- 0200,
- dev_entry->root,
- dev_entry,
- &hl_led0_fops);
-
- debugfs_create_file("led1",
- 0200,
- dev_entry->root,
- dev_entry,
- &hl_led1_fops);
-
- debugfs_create_file("led2",
- 0200,
- dev_entry->root,
- dev_entry,
- &hl_led2_fops);
-}
-
-void hl_debugfs_add_device(struct hl_device *hdev)
-{
- struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
- int count = ARRAY_SIZE(hl_debugfs_list);
- struct hl_debugfs_entry *entry;
- int i;
-
- dev_entry->hdev = hdev;
- dev_entry->entry_arr = kmalloc_array(count,
- sizeof(struct hl_debugfs_entry),
- GFP_KERNEL);
- if (!dev_entry->entry_arr)
- return;
-
- dev_entry->data_dma_blob_desc.size = 0;
- dev_entry->data_dma_blob_desc.data = NULL;
- dev_entry->mon_dump_blob_desc.size = 0;
- dev_entry->mon_dump_blob_desc.data = NULL;
-
- INIT_LIST_HEAD(&dev_entry->file_list);
- INIT_LIST_HEAD(&dev_entry->cb_list);
- INIT_LIST_HEAD(&dev_entry->cs_list);
- INIT_LIST_HEAD(&dev_entry->cs_job_list);
- INIT_LIST_HEAD(&dev_entry->userptr_list);
- INIT_LIST_HEAD(&dev_entry->ctx_mem_hash_list);
- mutex_init(&dev_entry->file_mutex);
- init_rwsem(&dev_entry->state_dump_sem);
- spin_lock_init(&dev_entry->cb_spinlock);
- spin_lock_init(&dev_entry->cs_spinlock);
- spin_lock_init(&dev_entry->cs_job_spinlock);
- spin_lock_init(&dev_entry->userptr_spinlock);
- spin_lock_init(&dev_entry->ctx_mem_hash_spinlock);
-
- dev_entry->root = debugfs_create_dir(dev_name(hdev->dev),
- hl_debug_root);
-
- debugfs_create_x64("memory_scrub_val",
- 0644,
- dev_entry->root,
- &hdev->memory_scrub_val);
-
- debugfs_create_file("memory_scrub",
- 0200,
- dev_entry->root,
- dev_entry,
- &hl_mem_scrub_fops);
-
- debugfs_create_x64("addr",
- 0644,
- dev_entry->root,
- &dev_entry->addr);
-
- debugfs_create_file("data32",
- 0644,
- dev_entry->root,
- dev_entry,
- &hl_data32b_fops);
-
- debugfs_create_file("data64",
- 0644,
- dev_entry->root,
- dev_entry,
- &hl_data64b_fops);
-
- debugfs_create_file("set_power_state",
- 0200,
- dev_entry->root,
- dev_entry,
- &hl_power_fops);
-
- debugfs_create_file("device",
- 0200,
- dev_entry->root,
- dev_entry,
- &hl_device_fops);
-
- debugfs_create_file("clk_gate",
- 0200,
- dev_entry->root,
- dev_entry,
- &hl_clk_gate_fops);
-
- debugfs_create_file("stop_on_err",
- 0644,
- dev_entry->root,
- dev_entry,
- &hl_stop_on_err_fops);
-
- debugfs_create_file("dump_security_violations",
- 0644,
- dev_entry->root,
- dev_entry,
- &hl_security_violations_fops);
-
- debugfs_create_file("dump_razwi_events",
- 0644,
- dev_entry->root,
- dev_entry,
- &hl_razwi_check_fops);
-
- debugfs_create_file("dma_size",
- 0200,
- dev_entry->root,
- dev_entry,
- &hl_dma_size_fops);
-
- debugfs_create_blob("data_dma",
- 0400,
- dev_entry->root,
- &dev_entry->data_dma_blob_desc);
-
- debugfs_create_file("monitor_dump_trig",
- 0200,
- dev_entry->root,
- dev_entry,
- &hl_monitor_dump_fops);
-
- debugfs_create_blob("monitor_dump",
- 0400,
- dev_entry->root,
- &dev_entry->mon_dump_blob_desc);
-
- debugfs_create_x8("skip_reset_on_timeout",
- 0644,
- dev_entry->root,
- &hdev->reset_info.skip_reset_on_timeout);
-
- debugfs_create_file("state_dump",
- 0600,
- dev_entry->root,
- dev_entry,
- &hl_state_dump_fops);
-
- debugfs_create_file("timeout_locked",
- 0644,
- dev_entry->root,
- dev_entry,
- &hl_timeout_locked_fops);
-
- debugfs_create_u32("device_release_watchdog_timeout",
- 0644,
- dev_entry->root,
- &hdev->device_release_watchdog_timeout_sec);
-
- for (i = 0, entry = dev_entry->entry_arr ; i < count ; i++, entry++) {
- debugfs_create_file(hl_debugfs_list[i].name,
- 0444,
- dev_entry->root,
- entry,
- &hl_debugfs_fops);
- entry->info_ent = &hl_debugfs_list[i];
- entry->dev_entry = dev_entry;
- }
-
- if (!hdev->asic_prop.fw_security_enabled)
- add_secured_nodes(dev_entry);
-}
-
-void hl_debugfs_remove_device(struct hl_device *hdev)
-{
- struct hl_dbg_device_entry *entry = &hdev->hl_debugfs;
- int i;
-
- debugfs_remove_recursive(entry->root);
-
- mutex_destroy(&entry->file_mutex);
-
- vfree(entry->data_dma_blob_desc.data);
- vfree(entry->mon_dump_blob_desc.data);
-
- for (i = 0; i < ARRAY_SIZE(entry->state_dump); ++i)
- vfree(entry->state_dump[i]);
-
- kfree(entry->entry_arr);
-}
-
-void hl_debugfs_add_file(struct hl_fpriv *hpriv)
-{
- struct hl_dbg_device_entry *dev_entry = &hpriv->hdev->hl_debugfs;
-
- mutex_lock(&dev_entry->file_mutex);
- list_add(&hpriv->debugfs_list, &dev_entry->file_list);
- mutex_unlock(&dev_entry->file_mutex);
-}
-
-void hl_debugfs_remove_file(struct hl_fpriv *hpriv)
-{
- struct hl_dbg_device_entry *dev_entry = &hpriv->hdev->hl_debugfs;
-
- mutex_lock(&dev_entry->file_mutex);
- list_del(&hpriv->debugfs_list);
- mutex_unlock(&dev_entry->file_mutex);
-}
-
-void hl_debugfs_add_cb(struct hl_cb *cb)
-{
- struct hl_dbg_device_entry *dev_entry = &cb->hdev->hl_debugfs;
-
- spin_lock(&dev_entry->cb_spinlock);
- list_add(&cb->debugfs_list, &dev_entry->cb_list);
- spin_unlock(&dev_entry->cb_spinlock);
-}
-
-void hl_debugfs_remove_cb(struct hl_cb *cb)
-{
- struct hl_dbg_device_entry *dev_entry = &cb->hdev->hl_debugfs;
-
- spin_lock(&dev_entry->cb_spinlock);
- list_del(&cb->debugfs_list);
- spin_unlock(&dev_entry->cb_spinlock);
-}
-
-void hl_debugfs_add_cs(struct hl_cs *cs)
-{
- struct hl_dbg_device_entry *dev_entry = &cs->ctx->hdev->hl_debugfs;
-
- spin_lock(&dev_entry->cs_spinlock);
- list_add(&cs->debugfs_list, &dev_entry->cs_list);
- spin_unlock(&dev_entry->cs_spinlock);
-}
-
-void hl_debugfs_remove_cs(struct hl_cs *cs)
-{
- struct hl_dbg_device_entry *dev_entry = &cs->ctx->hdev->hl_debugfs;
-
- spin_lock(&dev_entry->cs_spinlock);
- list_del(&cs->debugfs_list);
- spin_unlock(&dev_entry->cs_spinlock);
-}
-
-void hl_debugfs_add_job(struct hl_device *hdev, struct hl_cs_job *job)
-{
- struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
-
- spin_lock(&dev_entry->cs_job_spinlock);
- list_add(&job->debugfs_list, &dev_entry->cs_job_list);
- spin_unlock(&dev_entry->cs_job_spinlock);
-}
-
-void hl_debugfs_remove_job(struct hl_device *hdev, struct hl_cs_job *job)
-{
- struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
-
- spin_lock(&dev_entry->cs_job_spinlock);
- list_del(&job->debugfs_list);
- spin_unlock(&dev_entry->cs_job_spinlock);
-}
-
-void hl_debugfs_add_userptr(struct hl_device *hdev, struct hl_userptr *userptr)
-{
- struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
-
- spin_lock(&dev_entry->userptr_spinlock);
- list_add(&userptr->debugfs_list, &dev_entry->userptr_list);
- spin_unlock(&dev_entry->userptr_spinlock);
-}
-
-void hl_debugfs_remove_userptr(struct hl_device *hdev,
- struct hl_userptr *userptr)
-{
- struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
-
- spin_lock(&dev_entry->userptr_spinlock);
- list_del(&userptr->debugfs_list);
- spin_unlock(&dev_entry->userptr_spinlock);
-}
-
-void hl_debugfs_add_ctx_mem_hash(struct hl_device *hdev, struct hl_ctx *ctx)
-{
- struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
-
- spin_lock(&dev_entry->ctx_mem_hash_spinlock);
- list_add(&ctx->debugfs_list, &dev_entry->ctx_mem_hash_list);
- spin_unlock(&dev_entry->ctx_mem_hash_spinlock);
-}
-
-void hl_debugfs_remove_ctx_mem_hash(struct hl_device *hdev, struct hl_ctx *ctx)
-{
- struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
-
- spin_lock(&dev_entry->ctx_mem_hash_spinlock);
- list_del(&ctx->debugfs_list);
- spin_unlock(&dev_entry->ctx_mem_hash_spinlock);
-}
-
-/**
- * hl_debugfs_set_state_dump - register state dump making it accessible via
- * debugfs
- * @hdev: pointer to the device structure
- * @data: the actual dump data
- * @length: the length of the data
- */
-void hl_debugfs_set_state_dump(struct hl_device *hdev, char *data,
- unsigned long length)
-{
- struct hl_dbg_device_entry *dev_entry = &hdev->hl_debugfs;
-
- down_write(&dev_entry->state_dump_sem);
-
- dev_entry->state_dump_head = (dev_entry->state_dump_head + 1) %
- ARRAY_SIZE(dev_entry->state_dump);
- vfree(dev_entry->state_dump[dev_entry->state_dump_head]);
- dev_entry->state_dump[dev_entry->state_dump_head] = data;
-
- up_write(&dev_entry->state_dump_sem);
-}
-
-void __init hl_debugfs_init(void)
-{
- hl_debug_root = debugfs_create_dir("habanalabs", NULL);
-}
-
-void hl_debugfs_fini(void)
-{
- debugfs_remove_recursive(hl_debug_root);
-}
diff --git a/drivers/misc/habanalabs/common/decoder.c b/drivers/misc/habanalabs/common/decoder.c
deleted file mode 100644
index 2aab14d74b53..000000000000
--- a/drivers/misc/habanalabs/common/decoder.c
+++ /dev/null
@@ -1,133 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2022 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include "habanalabs.h"
-
-#define VCMD_CONTROL_OFFSET 0x40 /* SWREG16 */
-#define VCMD_IRQ_STATUS_OFFSET 0x44 /* SWREG17 */
-
-#define VCMD_IRQ_STATUS_ENDCMD_MASK 0x1
-#define VCMD_IRQ_STATUS_BUSERR_MASK 0x2
-#define VCMD_IRQ_STATUS_TIMEOUT_MASK 0x4
-#define VCMD_IRQ_STATUS_CMDERR_MASK 0x8
-#define VCMD_IRQ_STATUS_ABORT_MASK 0x10
-#define VCMD_IRQ_STATUS_RESET_MASK 0x20
-
-static void dec_print_abnrm_intr_source(struct hl_device *hdev, u32 irq_status)
-{
- const char *format = "abnormal interrupt source:%s%s%s%s%s%s\n";
- char *intr_source[6] = {"Unknown", "", "", "", "", ""};
- int i = 0;
-
- if (!irq_status)
- return;
-
- if (irq_status & VCMD_IRQ_STATUS_ENDCMD_MASK)
- intr_source[i++] = " ENDCMD";
- if (irq_status & VCMD_IRQ_STATUS_BUSERR_MASK)
- intr_source[i++] = " BUSERR";
- if (irq_status & VCMD_IRQ_STATUS_TIMEOUT_MASK)
- intr_source[i++] = " TIMEOUT";
- if (irq_status & VCMD_IRQ_STATUS_CMDERR_MASK)
- intr_source[i++] = " CMDERR";
- if (irq_status & VCMD_IRQ_STATUS_ABORT_MASK)
- intr_source[i++] = " ABORT";
- if (irq_status & VCMD_IRQ_STATUS_RESET_MASK)
- intr_source[i++] = " RESET";
-
- dev_err(hdev->dev, format, intr_source[0], intr_source[1],
- intr_source[2], intr_source[3], intr_source[4], intr_source[5]);
-}
-
-static void dec_error_intr_work(struct hl_device *hdev, u32 base_addr, u32 core_id)
-{
- bool reset_required = false;
- u32 irq_status;
-
- irq_status = RREG32(base_addr + VCMD_IRQ_STATUS_OFFSET);
-
- dev_err(hdev->dev, "Decoder abnormal interrupt %#x, core %d\n", irq_status, core_id);
-
- dec_print_abnrm_intr_source(hdev, irq_status);
-
- if (irq_status & VCMD_IRQ_STATUS_TIMEOUT_MASK)
- reset_required = true;
-
- /* Clear the interrupt */
- WREG32(base_addr + VCMD_IRQ_STATUS_OFFSET, irq_status);
-
- /* Flush the interrupt clear */
- RREG32(base_addr + VCMD_IRQ_STATUS_OFFSET);
-
- if (reset_required)
- hl_device_reset(hdev, HL_DRV_RESET_HARD);
-}
-
-static void dec_completion_abnrm(struct work_struct *work)
-{
- struct hl_dec *dec = container_of(work, struct hl_dec, completion_abnrm_work);
- struct hl_device *hdev = dec->hdev;
-
- dec_error_intr_work(hdev, dec->base_addr, dec->core_id);
-}
-
-void hl_dec_fini(struct hl_device *hdev)
-{
- kfree(hdev->dec);
-}
-
-int hl_dec_init(struct hl_device *hdev)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct hl_dec *dec;
- int rc, j;
-
- /* if max core is 0, nothing to do*/
- if (!prop->max_dec)
- return 0;
-
- hdev->dec = kcalloc(prop->max_dec, sizeof(struct hl_dec), GFP_KERNEL);
- if (!hdev->dec)
- return -ENOMEM;
-
- for (j = 0 ; j < prop->max_dec ; j++) {
- dec = hdev->dec + j;
-
- dec->hdev = hdev;
- INIT_WORK(&dec->completion_abnrm_work, dec_completion_abnrm);
- dec->core_id = j;
- dec->base_addr = hdev->asic_funcs->get_dec_base_addr(hdev, j);
- if (!dec->base_addr) {
- dev_err(hdev->dev, "Invalid base address of decoder %d\n", j);
- rc = -EINVAL;
- goto err_dec_fini;
- }
- }
-
- return 0;
-
-err_dec_fini:
- hl_dec_fini(hdev);
-
- return rc;
-}
-
-void hl_dec_ctx_fini(struct hl_ctx *ctx)
-{
- struct hl_device *hdev = ctx->hdev;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct hl_dec *dec;
- int j;
-
- for (j = 0 ; j < prop->max_dec ; j++) {
- if (!!(prop->decoder_enabled_mask & BIT(j))) {
- dec = hdev->dec + j;
- /* Stop the decoder */
- WREG32(dec->base_addr + VCMD_CONTROL_OFFSET, 0);
- }
- }
-}
diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c
deleted file mode 100644
index 6620580e9ba8..000000000000
--- a/drivers/misc/habanalabs/common/device.c
+++ /dev/null
@@ -1,2534 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2022 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#define pr_fmt(fmt) "habanalabs: " fmt
-
-#include <uapi/drm/habanalabs_accel.h>
-#include "habanalabs.h"
-
-#include <linux/pci.h>
-#include <linux/hwmon.h>
-#include <linux/vmalloc.h>
-
-#include <trace/events/habanalabs.h>
-
-#define HL_RESET_DELAY_USEC 10000 /* 10ms */
-
-#define HL_DEVICE_RELEASE_WATCHDOG_TIMEOUT_SEC 5
-
-enum dma_alloc_type {
- DMA_ALLOC_COHERENT,
- DMA_ALLOC_CPU_ACCESSIBLE,
- DMA_ALLOC_POOL,
-};
-
-#define MEM_SCRUB_DEFAULT_VAL 0x1122334455667788
-
-/*
- * hl_set_dram_bar- sets the bar to allow later access to address
- *
- * @hdev: pointer to habanalabs device structure.
- * @addr: the address the caller wants to access.
- * @region: the PCI region.
- * @new_bar_region_base: the new BAR region base address.
- *
- * @return: the old BAR base address on success, U64_MAX for failure.
- * The caller should set it back to the old address after use.
- *
- * In case the bar space does not cover the whole address space,
- * the bar base address should be set to allow access to a given address.
- * This function can be called also if the bar doesn't need to be set,
- * in that case it just won't change the base.
- */
-static u64 hl_set_dram_bar(struct hl_device *hdev, u64 addr, struct pci_mem_region *region,
- u64 *new_bar_region_base)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u64 bar_base_addr, old_base;
-
- if (is_power_of_2(prop->dram_pci_bar_size))
- bar_base_addr = addr & ~(prop->dram_pci_bar_size - 0x1ull);
- else
- bar_base_addr = DIV_ROUND_DOWN_ULL(addr, prop->dram_pci_bar_size) *
- prop->dram_pci_bar_size;
-
- old_base = hdev->asic_funcs->set_dram_bar_base(hdev, bar_base_addr);
-
- /* in case of success we need to update the new BAR base */
- if ((old_base != U64_MAX) && new_bar_region_base)
- *new_bar_region_base = bar_base_addr;
-
- return old_base;
-}
-
-int hl_access_sram_dram_region(struct hl_device *hdev, u64 addr, u64 *val,
- enum debugfs_access_type acc_type, enum pci_region region_type, bool set_dram_bar)
-{
- struct pci_mem_region *region = &hdev->pci_mem_region[region_type];
- u64 old_base = 0, rc, bar_region_base = region->region_base;
- void __iomem *acc_addr;
-
- if (set_dram_bar) {
- old_base = hl_set_dram_bar(hdev, addr, region, &bar_region_base);
- if (old_base == U64_MAX)
- return -EIO;
- }
-
- acc_addr = hdev->pcie_bar[region->bar_id] + region->offset_in_bar +
- (addr - bar_region_base);
-
- switch (acc_type) {
- case DEBUGFS_READ8:
- *val = readb(acc_addr);
- break;
- case DEBUGFS_WRITE8:
- writeb(*val, acc_addr);
- break;
- case DEBUGFS_READ32:
- *val = readl(acc_addr);
- break;
- case DEBUGFS_WRITE32:
- writel(*val, acc_addr);
- break;
- case DEBUGFS_READ64:
- *val = readq(acc_addr);
- break;
- case DEBUGFS_WRITE64:
- writeq(*val, acc_addr);
- break;
- }
-
- if (set_dram_bar) {
- rc = hl_set_dram_bar(hdev, old_base, region, NULL);
- if (rc == U64_MAX)
- return -EIO;
- }
-
- return 0;
-}
-
-static void *hl_dma_alloc_common(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle,
- gfp_t flag, enum dma_alloc_type alloc_type,
- const char *caller)
-{
- void *ptr = NULL;
-
- switch (alloc_type) {
- case DMA_ALLOC_COHERENT:
- ptr = hdev->asic_funcs->asic_dma_alloc_coherent(hdev, size, dma_handle, flag);
- break;
- case DMA_ALLOC_CPU_ACCESSIBLE:
- ptr = hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, size, dma_handle);
- break;
- case DMA_ALLOC_POOL:
- ptr = hdev->asic_funcs->asic_dma_pool_zalloc(hdev, size, flag, dma_handle);
- break;
- }
-
- if (trace_habanalabs_dma_alloc_enabled() && !ZERO_OR_NULL_PTR(ptr))
- trace_habanalabs_dma_alloc(hdev->dev, (u64) (uintptr_t) ptr, *dma_handle, size,
- caller);
-
- return ptr;
-}
-
-static void hl_asic_dma_free_common(struct hl_device *hdev, size_t size, void *cpu_addr,
- dma_addr_t dma_handle, enum dma_alloc_type alloc_type,
- const char *caller)
-{
- /* this is needed to avoid warning on using freed pointer */
- u64 store_cpu_addr = (u64) (uintptr_t) cpu_addr;
-
- switch (alloc_type) {
- case DMA_ALLOC_COHERENT:
- hdev->asic_funcs->asic_dma_free_coherent(hdev, size, cpu_addr, dma_handle);
- break;
- case DMA_ALLOC_CPU_ACCESSIBLE:
- hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, size, cpu_addr);
- break;
- case DMA_ALLOC_POOL:
- hdev->asic_funcs->asic_dma_pool_free(hdev, cpu_addr, dma_handle);
- break;
- }
-
- trace_habanalabs_dma_free(hdev->dev, store_cpu_addr, dma_handle, size, caller);
-}
-
-void *hl_asic_dma_alloc_coherent_caller(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle,
- gfp_t flag, const char *caller)
-{
- return hl_dma_alloc_common(hdev, size, dma_handle, flag, DMA_ALLOC_COHERENT, caller);
-}
-
-void hl_asic_dma_free_coherent_caller(struct hl_device *hdev, size_t size, void *cpu_addr,
- dma_addr_t dma_handle, const char *caller)
-{
- hl_asic_dma_free_common(hdev, size, cpu_addr, dma_handle, DMA_ALLOC_COHERENT, caller);
-}
-
-void *hl_cpu_accessible_dma_pool_alloc_caller(struct hl_device *hdev, size_t size,
- dma_addr_t *dma_handle, const char *caller)
-{
- return hl_dma_alloc_common(hdev, size, dma_handle, 0, DMA_ALLOC_CPU_ACCESSIBLE, caller);
-}
-
-void hl_cpu_accessible_dma_pool_free_caller(struct hl_device *hdev, size_t size, void *vaddr,
- const char *caller)
-{
- hl_asic_dma_free_common(hdev, size, vaddr, 0, DMA_ALLOC_CPU_ACCESSIBLE, caller);
-}
-
-void *hl_asic_dma_pool_zalloc_caller(struct hl_device *hdev, size_t size, gfp_t mem_flags,
- dma_addr_t *dma_handle, const char *caller)
-{
- return hl_dma_alloc_common(hdev, size, dma_handle, mem_flags, DMA_ALLOC_POOL, caller);
-}
-
-void hl_asic_dma_pool_free_caller(struct hl_device *hdev, void *vaddr, dma_addr_t dma_addr,
- const char *caller)
-{
- hl_asic_dma_free_common(hdev, 0, vaddr, dma_addr, DMA_ALLOC_POOL, caller);
-}
-
-int hl_dma_map_sgtable(struct hl_device *hdev, struct sg_table *sgt, enum dma_data_direction dir)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct scatterlist *sg;
- int rc, i;
-
- rc = dma_map_sgtable(&hdev->pdev->dev, sgt, dir, 0);
- if (rc)
- return rc;
-
- /* Shift to the device's base physical address of host memory if necessary */
- if (prop->device_dma_offset_for_host_access)
- for_each_sgtable_dma_sg(sgt, sg, i)
- sg->dma_address += prop->device_dma_offset_for_host_access;
-
- return 0;
-}
-
-void hl_dma_unmap_sgtable(struct hl_device *hdev, struct sg_table *sgt, enum dma_data_direction dir)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct scatterlist *sg;
- int i;
-
- /* Cancel the device's base physical address of host memory if necessary */
- if (prop->device_dma_offset_for_host_access)
- for_each_sgtable_dma_sg(sgt, sg, i)
- sg->dma_address -= prop->device_dma_offset_for_host_access;
-
- dma_unmap_sgtable(&hdev->pdev->dev, sgt, dir, 0);
-}
-
-/*
- * hl_access_cfg_region - access the config region
- *
- * @hdev: pointer to habanalabs device structure
- * @addr: the address to access
- * @val: the value to write from or read to
- * @acc_type: the type of access (read/write 64/32)
- */
-int hl_access_cfg_region(struct hl_device *hdev, u64 addr, u64 *val,
- enum debugfs_access_type acc_type)
-{
- struct pci_mem_region *cfg_region = &hdev->pci_mem_region[PCI_REGION_CFG];
- u32 val_h, val_l;
-
- if (!IS_ALIGNED(addr, sizeof(u32))) {
- dev_err(hdev->dev, "address %#llx not a multiple of %zu\n", addr, sizeof(u32));
- return -EINVAL;
- }
-
- switch (acc_type) {
- case DEBUGFS_READ32:
- *val = RREG32(addr - cfg_region->region_base);
- break;
- case DEBUGFS_WRITE32:
- WREG32(addr - cfg_region->region_base, *val);
- break;
- case DEBUGFS_READ64:
- val_l = RREG32(addr - cfg_region->region_base);
- val_h = RREG32(addr + sizeof(u32) - cfg_region->region_base);
-
- *val = (((u64) val_h) << 32) | val_l;
- break;
- case DEBUGFS_WRITE64:
- WREG32(addr - cfg_region->region_base, lower_32_bits(*val));
- WREG32(addr + sizeof(u32) - cfg_region->region_base, upper_32_bits(*val));
- break;
- default:
- dev_err(hdev->dev, "access type %d is not supported\n", acc_type);
- return -EOPNOTSUPP;
- }
-
- return 0;
-}
-
-/*
- * hl_access_dev_mem - access device memory
- *
- * @hdev: pointer to habanalabs device structure
- * @region_type: the type of the region the address belongs to
- * @addr: the address to access
- * @val: the value to write from or read to
- * @acc_type: the type of access (r/w, 32/64)
- */
-int hl_access_dev_mem(struct hl_device *hdev, enum pci_region region_type,
- u64 addr, u64 *val, enum debugfs_access_type acc_type)
-{
- switch (region_type) {
- case PCI_REGION_CFG:
- return hl_access_cfg_region(hdev, addr, val, acc_type);
- case PCI_REGION_SRAM:
- case PCI_REGION_DRAM:
- return hl_access_sram_dram_region(hdev, addr, val, acc_type,
- region_type, (region_type == PCI_REGION_DRAM));
- default:
- return -EFAULT;
- }
-
- return 0;
-}
-
-void hl_engine_data_sprintf(struct engines_data *e, const char *fmt, ...)
-{
- va_list args;
- int str_size;
-
- va_start(args, fmt);
- /* Calculate formatted string length. Assuming each string is null terminated, hence
- * increment result by 1
- */
- str_size = vsnprintf(NULL, 0, fmt, args) + 1;
- va_end(args);
-
- if ((e->actual_size + str_size) < e->allocated_buf_size) {
- va_start(args, fmt);
- vsnprintf(e->buf + e->actual_size, str_size, fmt, args);
- va_end(args);
- }
-
- /* Need to update the size even when not updating destination buffer to get the exact size
- * of all input strings
- */
- e->actual_size += str_size;
-}
-
-enum hl_device_status hl_device_status(struct hl_device *hdev)
-{
- enum hl_device_status status;
-
- if (hdev->reset_info.in_reset) {
- if (hdev->reset_info.in_compute_reset)
- status = HL_DEVICE_STATUS_IN_RESET_AFTER_DEVICE_RELEASE;
- else
- status = HL_DEVICE_STATUS_IN_RESET;
- } else if (hdev->reset_info.needs_reset) {
- status = HL_DEVICE_STATUS_NEEDS_RESET;
- } else if (hdev->disabled) {
- status = HL_DEVICE_STATUS_MALFUNCTION;
- } else if (!hdev->init_done) {
- status = HL_DEVICE_STATUS_IN_DEVICE_CREATION;
- } else {
- status = HL_DEVICE_STATUS_OPERATIONAL;
- }
-
- return status;
-}
-
-bool hl_device_operational(struct hl_device *hdev,
- enum hl_device_status *status)
-{
- enum hl_device_status current_status;
-
- current_status = hl_device_status(hdev);
- if (status)
- *status = current_status;
-
- switch (current_status) {
- case HL_DEVICE_STATUS_IN_RESET:
- case HL_DEVICE_STATUS_IN_RESET_AFTER_DEVICE_RELEASE:
- case HL_DEVICE_STATUS_MALFUNCTION:
- case HL_DEVICE_STATUS_NEEDS_RESET:
- return false;
- case HL_DEVICE_STATUS_OPERATIONAL:
- case HL_DEVICE_STATUS_IN_DEVICE_CREATION:
- default:
- return true;
- }
-}
-
-bool hl_ctrl_device_operational(struct hl_device *hdev,
- enum hl_device_status *status)
-{
- enum hl_device_status current_status;
-
- current_status = hl_device_status(hdev);
- if (status)
- *status = current_status;
-
- switch (current_status) {
- case HL_DEVICE_STATUS_MALFUNCTION:
- return false;
- case HL_DEVICE_STATUS_IN_RESET:
- case HL_DEVICE_STATUS_IN_RESET_AFTER_DEVICE_RELEASE:
- case HL_DEVICE_STATUS_NEEDS_RESET:
- case HL_DEVICE_STATUS_OPERATIONAL:
- case HL_DEVICE_STATUS_IN_DEVICE_CREATION:
- default:
- return true;
- }
-}
-
-static void print_idle_status_mask(struct hl_device *hdev, const char *message,
- u64 idle_mask[HL_BUSY_ENGINES_MASK_EXT_SIZE])
-{
- u32 pad_width[HL_BUSY_ENGINES_MASK_EXT_SIZE] = {};
-
- BUILD_BUG_ON(HL_BUSY_ENGINES_MASK_EXT_SIZE != 4);
-
- pad_width[3] = idle_mask[3] ? 16 : 0;
- pad_width[2] = idle_mask[2] || pad_width[3] ? 16 : 0;
- pad_width[1] = idle_mask[1] || pad_width[2] ? 16 : 0;
- pad_width[0] = idle_mask[0] || pad_width[1] ? 16 : 0;
-
- dev_err(hdev->dev, "%s (mask %0*llx_%0*llx_%0*llx_%0*llx)\n",
- message, pad_width[3], idle_mask[3], pad_width[2], idle_mask[2],
- pad_width[1], idle_mask[1], pad_width[0], idle_mask[0]);
-}
-
-static void hpriv_release(struct kref *ref)
-{
- u64 idle_mask[HL_BUSY_ENGINES_MASK_EXT_SIZE] = {0};
- bool reset_device, device_is_idle = true;
- struct hl_fpriv *hpriv;
- struct hl_device *hdev;
-
- hpriv = container_of(ref, struct hl_fpriv, refcount);
-
- hdev = hpriv->hdev;
-
- hdev->asic_funcs->send_device_activity(hdev, false);
-
- put_pid(hpriv->taskpid);
-
- hl_debugfs_remove_file(hpriv);
-
- mutex_destroy(&hpriv->ctx_lock);
- mutex_destroy(&hpriv->restore_phase_mutex);
-
- /* Device should be reset if reset-upon-device-release is enabled, or if there is a pending
- * reset that waits for device release.
- */
- reset_device = hdev->reset_upon_device_release || hdev->reset_info.watchdog_active;
-
- /* Check the device idle status and reset if not idle.
- * Skip it if already in reset, or if device is going to be reset in any case.
- */
- if (!hdev->reset_info.in_reset && !reset_device && hdev->pdev && !hdev->pldm)
- device_is_idle = hdev->asic_funcs->is_device_idle(hdev, idle_mask,
- HL_BUSY_ENGINES_MASK_EXT_SIZE, NULL);
- if (!device_is_idle) {
- print_idle_status_mask(hdev, "device is not idle after user context is closed",
- idle_mask);
- reset_device = true;
- }
-
- /* We need to remove the user from the list to make sure the reset process won't
- * try to kill the user process. Because, if we got here, it means there are no
- * more driver/device resources that the user process is occupying so there is
- * no need to kill it
- *
- * However, we can't set the compute_ctx to NULL at this stage. This is to prevent
- * a race between the release and opening the device again. We don't want to let
- * a user open the device while there a reset is about to happen.
- */
- mutex_lock(&hdev->fpriv_list_lock);
- list_del(&hpriv->dev_node);
- mutex_unlock(&hdev->fpriv_list_lock);
-
- if (reset_device) {
- hl_device_reset(hdev, HL_DRV_RESET_DEV_RELEASE);
- } else {
- /* Scrubbing is handled within hl_device_reset(), so here need to do it directly */
- int rc = hdev->asic_funcs->scrub_device_mem(hdev);
-
- if (rc)
- dev_err(hdev->dev, "failed to scrub memory from hpriv release (%d)\n", rc);
- }
-
- /* Now we can mark the compute_ctx as not active. Even if a reset is running in a different
- * thread, we don't care because the in_reset is marked so if a user will try to open
- * the device it will fail on that, even if compute_ctx is false.
- */
- mutex_lock(&hdev->fpriv_list_lock);
- hdev->is_compute_ctx_active = false;
- mutex_unlock(&hdev->fpriv_list_lock);
-
- hdev->compute_ctx_in_release = 0;
-
- /* release the eventfd */
- if (hpriv->notifier_event.eventfd)
- eventfd_ctx_put(hpriv->notifier_event.eventfd);
-
- mutex_destroy(&hpriv->notifier_event.lock);
-
- kfree(hpriv);
-}
-
-void hl_hpriv_get(struct hl_fpriv *hpriv)
-{
- kref_get(&hpriv->refcount);
-}
-
-int hl_hpriv_put(struct hl_fpriv *hpriv)
-{
- return kref_put(&hpriv->refcount, hpriv_release);
-}
-
-/*
- * hl_device_release - release function for habanalabs device
- *
- * @inode: pointer to inode structure
- * @filp: pointer to file structure
- *
- * Called when process closes an habanalabs device
- */
-static int hl_device_release(struct inode *inode, struct file *filp)
-{
- struct hl_fpriv *hpriv = filp->private_data;
- struct hl_device *hdev = hpriv->hdev;
-
- filp->private_data = NULL;
-
- if (!hdev) {
- pr_crit("Closing FD after device was removed. Memory leak will occur and it is advised to reboot.\n");
- put_pid(hpriv->taskpid);
- return 0;
- }
-
- hl_ctx_mgr_fini(hdev, &hpriv->ctx_mgr);
- hl_mem_mgr_fini(&hpriv->mem_mgr);
-
- hdev->compute_ctx_in_release = 1;
-
- if (!hl_hpriv_put(hpriv)) {
- dev_notice(hdev->dev, "User process closed FD but device still in use\n");
- hl_device_reset(hdev, HL_DRV_RESET_HARD);
- }
-
- hdev->last_open_session_duration_jif =
- jiffies - hdev->last_successful_open_jif;
-
- return 0;
-}
-
-static int hl_device_release_ctrl(struct inode *inode, struct file *filp)
-{
- struct hl_fpriv *hpriv = filp->private_data;
- struct hl_device *hdev = hpriv->hdev;
-
- filp->private_data = NULL;
-
- if (!hdev) {
- pr_err("Closing FD after device was removed\n");
- goto out;
- }
-
- mutex_lock(&hdev->fpriv_ctrl_list_lock);
- list_del(&hpriv->dev_node);
- mutex_unlock(&hdev->fpriv_ctrl_list_lock);
-out:
- /* release the eventfd */
- if (hpriv->notifier_event.eventfd)
- eventfd_ctx_put(hpriv->notifier_event.eventfd);
-
- mutex_destroy(&hpriv->notifier_event.lock);
- put_pid(hpriv->taskpid);
-
- kfree(hpriv);
-
- return 0;
-}
-
-/*
- * hl_mmap - mmap function for habanalabs device
- *
- * @*filp: pointer to file structure
- * @*vma: pointer to vm_area_struct of the process
- *
- * Called when process does an mmap on habanalabs device. Call the relevant mmap
- * function at the end of the common code.
- */
-static int hl_mmap(struct file *filp, struct vm_area_struct *vma)
-{
- struct hl_fpriv *hpriv = filp->private_data;
- struct hl_device *hdev = hpriv->hdev;
- unsigned long vm_pgoff;
-
- if (!hdev) {
- pr_err_ratelimited("Trying to mmap after device was removed! Please close FD\n");
- return -ENODEV;
- }
-
- vm_pgoff = vma->vm_pgoff;
-
- switch (vm_pgoff & HL_MMAP_TYPE_MASK) {
- case HL_MMAP_TYPE_BLOCK:
- vma->vm_pgoff = HL_MMAP_OFFSET_VALUE_GET(vm_pgoff);
- return hl_hw_block_mmap(hpriv, vma);
-
- case HL_MMAP_TYPE_CB:
- case HL_MMAP_TYPE_TS_BUFF:
- return hl_mem_mgr_mmap(&hpriv->mem_mgr, vma, NULL);
- }
- return -EINVAL;
-}
-
-static const struct file_operations hl_ops = {
- .owner = THIS_MODULE,
- .open = hl_device_open,
- .release = hl_device_release,
- .mmap = hl_mmap,
- .unlocked_ioctl = hl_ioctl,
- .compat_ioctl = hl_ioctl
-};
-
-static const struct file_operations hl_ctrl_ops = {
- .owner = THIS_MODULE,
- .open = hl_device_open_ctrl,
- .release = hl_device_release_ctrl,
- .unlocked_ioctl = hl_ioctl_control,
- .compat_ioctl = hl_ioctl_control
-};
-
-static void device_release_func(struct device *dev)
-{
- kfree(dev);
-}
-
-/*
- * device_init_cdev - Initialize cdev and device for habanalabs device
- *
- * @hdev: pointer to habanalabs device structure
- * @hclass: pointer to the class object of the device
- * @minor: minor number of the specific device
- * @fpos: file operations to install for this device
- * @name: name of the device as it will appear in the filesystem
- * @cdev: pointer to the char device object that will be initialized
- * @dev: pointer to the device object that will be initialized
- *
- * Initialize a cdev and a Linux device for habanalabs's device.
- */
-static int device_init_cdev(struct hl_device *hdev, struct class *hclass,
- int minor, const struct file_operations *fops,
- char *name, struct cdev *cdev,
- struct device **dev)
-{
- cdev_init(cdev, fops);
- cdev->owner = THIS_MODULE;
-
- *dev = kzalloc(sizeof(**dev), GFP_KERNEL);
- if (!*dev)
- return -ENOMEM;
-
- device_initialize(*dev);
- (*dev)->devt = MKDEV(hdev->major, minor);
- (*dev)->class = hclass;
- (*dev)->release = device_release_func;
- dev_set_drvdata(*dev, hdev);
- dev_set_name(*dev, "%s", name);
-
- return 0;
-}
-
-static int device_cdev_sysfs_add(struct hl_device *hdev)
-{
- int rc;
-
- rc = cdev_device_add(&hdev->cdev, hdev->dev);
- if (rc) {
- dev_err(hdev->dev,
- "failed to add a char device to the system\n");
- return rc;
- }
-
- rc = cdev_device_add(&hdev->cdev_ctrl, hdev->dev_ctrl);
- if (rc) {
- dev_err(hdev->dev,
- "failed to add a control char device to the system\n");
- goto delete_cdev_device;
- }
-
- /* hl_sysfs_init() must be done after adding the device to the system */
- rc = hl_sysfs_init(hdev);
- if (rc) {
- dev_err(hdev->dev, "failed to initialize sysfs\n");
- goto delete_ctrl_cdev_device;
- }
-
- hdev->cdev_sysfs_created = true;
-
- return 0;
-
-delete_ctrl_cdev_device:
- cdev_device_del(&hdev->cdev_ctrl, hdev->dev_ctrl);
-delete_cdev_device:
- cdev_device_del(&hdev->cdev, hdev->dev);
- return rc;
-}
-
-static void device_cdev_sysfs_del(struct hl_device *hdev)
-{
- if (!hdev->cdev_sysfs_created)
- goto put_devices;
-
- hl_sysfs_fini(hdev);
- cdev_device_del(&hdev->cdev_ctrl, hdev->dev_ctrl);
- cdev_device_del(&hdev->cdev, hdev->dev);
-
-put_devices:
- put_device(hdev->dev);
- put_device(hdev->dev_ctrl);
-}
-
-static void device_hard_reset_pending(struct work_struct *work)
-{
- struct hl_device_reset_work *device_reset_work =
- container_of(work, struct hl_device_reset_work, reset_work.work);
- struct hl_device *hdev = device_reset_work->hdev;
- u32 flags;
- int rc;
-
- flags = device_reset_work->flags | HL_DRV_RESET_FROM_RESET_THR;
-
- rc = hl_device_reset(hdev, flags);
-
- if ((rc == -EBUSY) && !hdev->device_fini_pending) {
- struct hl_ctx *ctx = hl_get_compute_ctx(hdev);
-
- if (ctx) {
- /* The read refcount value should subtracted by one, because the read is
- * protected with hl_get_compute_ctx().
- */
- dev_info(hdev->dev,
- "Could not reset device (compute_ctx refcount %u). will try again in %u seconds",
- kref_read(&ctx->refcount) - 1, HL_PENDING_RESET_PER_SEC);
- hl_ctx_put(ctx);
- } else {
- dev_info(hdev->dev, "Could not reset device. will try again in %u seconds",
- HL_PENDING_RESET_PER_SEC);
- }
-
- queue_delayed_work(hdev->reset_wq, &device_reset_work->reset_work,
- msecs_to_jiffies(HL_PENDING_RESET_PER_SEC * 1000));
- }
-}
-
-static void device_release_watchdog_func(struct work_struct *work)
-{
- struct hl_device_reset_work *device_release_watchdog_work =
- container_of(work, struct hl_device_reset_work, reset_work.work);
- struct hl_device *hdev = device_release_watchdog_work->hdev;
- u32 flags;
-
- dev_dbg(hdev->dev, "Device wasn't released in time. Initiate device reset.\n");
-
- flags = device_release_watchdog_work->flags | HL_DRV_RESET_FROM_WD_THR;
-
- hl_device_reset(hdev, flags);
-}
-
-/*
- * device_early_init - do some early initialization for the habanalabs device
- *
- * @hdev: pointer to habanalabs device structure
- *
- * Install the relevant function pointers and call the early_init function,
- * if such a function exists
- */
-static int device_early_init(struct hl_device *hdev)
-{
- int i, rc;
- char workq_name[32];
-
- switch (hdev->asic_type) {
- case ASIC_GOYA:
- goya_set_asic_funcs(hdev);
- strscpy(hdev->asic_name, "GOYA", sizeof(hdev->asic_name));
- break;
- case ASIC_GAUDI:
- gaudi_set_asic_funcs(hdev);
- strscpy(hdev->asic_name, "GAUDI", sizeof(hdev->asic_name));
- break;
- case ASIC_GAUDI_SEC:
- gaudi_set_asic_funcs(hdev);
- strscpy(hdev->asic_name, "GAUDI SEC", sizeof(hdev->asic_name));
- break;
- case ASIC_GAUDI2:
- gaudi2_set_asic_funcs(hdev);
- strscpy(hdev->asic_name, "GAUDI2", sizeof(hdev->asic_name));
- break;
- case ASIC_GAUDI2B:
- gaudi2_set_asic_funcs(hdev);
- strscpy(hdev->asic_name, "GAUDI2B", sizeof(hdev->asic_name));
- break;
- break;
- default:
- dev_err(hdev->dev, "Unrecognized ASIC type %d\n",
- hdev->asic_type);
- return -EINVAL;
- }
-
- rc = hdev->asic_funcs->early_init(hdev);
- if (rc)
- return rc;
-
- rc = hl_asid_init(hdev);
- if (rc)
- goto early_fini;
-
- if (hdev->asic_prop.completion_queues_count) {
- hdev->cq_wq = kcalloc(hdev->asic_prop.completion_queues_count,
- sizeof(struct workqueue_struct *),
- GFP_KERNEL);
- if (!hdev->cq_wq) {
- rc = -ENOMEM;
- goto asid_fini;
- }
- }
-
- for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) {
- snprintf(workq_name, 32, "hl-free-jobs-%u", (u32) i);
- hdev->cq_wq[i] = create_singlethread_workqueue(workq_name);
- if (hdev->cq_wq[i] == NULL) {
- dev_err(hdev->dev, "Failed to allocate CQ workqueue\n");
- rc = -ENOMEM;
- goto free_cq_wq;
- }
- }
-
- hdev->eq_wq = create_singlethread_workqueue("hl-events");
- if (hdev->eq_wq == NULL) {
- dev_err(hdev->dev, "Failed to allocate EQ workqueue\n");
- rc = -ENOMEM;
- goto free_cq_wq;
- }
-
- hdev->cs_cmplt_wq = alloc_workqueue("hl-cs-completions", WQ_UNBOUND, 0);
- if (!hdev->cs_cmplt_wq) {
- dev_err(hdev->dev,
- "Failed to allocate CS completions workqueue\n");
- rc = -ENOMEM;
- goto free_eq_wq;
- }
-
- hdev->ts_free_obj_wq = alloc_workqueue("hl-ts-free-obj", WQ_UNBOUND, 0);
- if (!hdev->ts_free_obj_wq) {
- dev_err(hdev->dev,
- "Failed to allocate Timestamp registration free workqueue\n");
- rc = -ENOMEM;
- goto free_cs_cmplt_wq;
- }
-
- hdev->prefetch_wq = alloc_workqueue("hl-prefetch", WQ_UNBOUND, 0);
- if (!hdev->prefetch_wq) {
- dev_err(hdev->dev, "Failed to allocate MMU prefetch workqueue\n");
- rc = -ENOMEM;
- goto free_ts_free_wq;
- }
-
- hdev->hl_chip_info = kzalloc(sizeof(struct hwmon_chip_info),
- GFP_KERNEL);
- if (!hdev->hl_chip_info) {
- rc = -ENOMEM;
- goto free_prefetch_wq;
- }
-
- rc = hl_mmu_if_set_funcs(hdev);
- if (rc)
- goto free_chip_info;
-
- hl_mem_mgr_init(hdev->dev, &hdev->kernel_mem_mgr, 1);
-
- hdev->reset_wq = create_singlethread_workqueue("hl_device_reset");
- if (!hdev->reset_wq) {
- rc = -ENOMEM;
- dev_err(hdev->dev, "Failed to create device reset WQ\n");
- goto free_cb_mgr;
- }
-
- INIT_DELAYED_WORK(&hdev->device_reset_work.reset_work, device_hard_reset_pending);
- hdev->device_reset_work.hdev = hdev;
- hdev->device_fini_pending = 0;
-
- INIT_DELAYED_WORK(&hdev->device_release_watchdog_work.reset_work,
- device_release_watchdog_func);
- hdev->device_release_watchdog_work.hdev = hdev;
-
- mutex_init(&hdev->send_cpu_message_lock);
- mutex_init(&hdev->debug_lock);
- INIT_LIST_HEAD(&hdev->cs_mirror_list);
- spin_lock_init(&hdev->cs_mirror_lock);
- spin_lock_init(&hdev->reset_info.lock);
- INIT_LIST_HEAD(&hdev->fpriv_list);
- INIT_LIST_HEAD(&hdev->fpriv_ctrl_list);
- mutex_init(&hdev->fpriv_list_lock);
- mutex_init(&hdev->fpriv_ctrl_list_lock);
- mutex_init(&hdev->clk_throttling.lock);
-
- return 0;
-
-free_cb_mgr:
- hl_mem_mgr_fini(&hdev->kernel_mem_mgr);
-free_chip_info:
- kfree(hdev->hl_chip_info);
-free_prefetch_wq:
- destroy_workqueue(hdev->prefetch_wq);
-free_ts_free_wq:
- destroy_workqueue(hdev->ts_free_obj_wq);
-free_cs_cmplt_wq:
- destroy_workqueue(hdev->cs_cmplt_wq);
-free_eq_wq:
- destroy_workqueue(hdev->eq_wq);
-free_cq_wq:
- for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++)
- if (hdev->cq_wq[i])
- destroy_workqueue(hdev->cq_wq[i]);
- kfree(hdev->cq_wq);
-asid_fini:
- hl_asid_fini(hdev);
-early_fini:
- if (hdev->asic_funcs->early_fini)
- hdev->asic_funcs->early_fini(hdev);
-
- return rc;
-}
-
-/*
- * device_early_fini - finalize all that was done in device_early_init
- *
- * @hdev: pointer to habanalabs device structure
- *
- */
-static void device_early_fini(struct hl_device *hdev)
-{
- int i;
-
- mutex_destroy(&hdev->debug_lock);
- mutex_destroy(&hdev->send_cpu_message_lock);
-
- mutex_destroy(&hdev->fpriv_list_lock);
- mutex_destroy(&hdev->fpriv_ctrl_list_lock);
-
- mutex_destroy(&hdev->clk_throttling.lock);
-
- hl_mem_mgr_fini(&hdev->kernel_mem_mgr);
-
- kfree(hdev->hl_chip_info);
-
- destroy_workqueue(hdev->prefetch_wq);
- destroy_workqueue(hdev->ts_free_obj_wq);
- destroy_workqueue(hdev->cs_cmplt_wq);
- destroy_workqueue(hdev->eq_wq);
- destroy_workqueue(hdev->reset_wq);
-
- for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++)
- destroy_workqueue(hdev->cq_wq[i]);
- kfree(hdev->cq_wq);
-
- hl_asid_fini(hdev);
-
- if (hdev->asic_funcs->early_fini)
- hdev->asic_funcs->early_fini(hdev);
-}
-
-static void hl_device_heartbeat(struct work_struct *work)
-{
- struct hl_device *hdev = container_of(work, struct hl_device,
- work_heartbeat.work);
-
- if (!hl_device_operational(hdev, NULL))
- goto reschedule;
-
- if (!hdev->asic_funcs->send_heartbeat(hdev))
- goto reschedule;
-
- if (hl_device_operational(hdev, NULL))
- dev_err(hdev->dev, "Device heartbeat failed!\n");
-
- hl_device_reset(hdev, HL_DRV_RESET_HARD | HL_DRV_RESET_HEARTBEAT);
-
- return;
-
-reschedule:
- /*
- * prev_reset_trigger tracks consecutive fatal h/w errors until first
- * heartbeat immediately post reset.
- * If control reached here, then at least one heartbeat work has been
- * scheduled since last reset/init cycle.
- * So if the device is not already in reset cycle, reset the flag
- * prev_reset_trigger as no reset occurred with HL_DRV_RESET_FW_FATAL_ERR
- * status for at least one heartbeat. From this point driver restarts
- * tracking future consecutive fatal errors.
- */
- if (!hdev->reset_info.in_reset)
- hdev->reset_info.prev_reset_trigger = HL_RESET_TRIGGER_DEFAULT;
-
- schedule_delayed_work(&hdev->work_heartbeat,
- usecs_to_jiffies(HL_HEARTBEAT_PER_USEC));
-}
-
-/*
- * device_late_init - do late stuff initialization for the habanalabs device
- *
- * @hdev: pointer to habanalabs device structure
- *
- * Do stuff that either needs the device H/W queues to be active or needs
- * to happen after all the rest of the initialization is finished
- */
-static int device_late_init(struct hl_device *hdev)
-{
- int rc;
-
- if (hdev->asic_funcs->late_init) {
- rc = hdev->asic_funcs->late_init(hdev);
- if (rc) {
- dev_err(hdev->dev,
- "failed late initialization for the H/W\n");
- return rc;
- }
- }
-
- hdev->high_pll = hdev->asic_prop.high_pll;
-
- if (hdev->heartbeat) {
- INIT_DELAYED_WORK(&hdev->work_heartbeat, hl_device_heartbeat);
- schedule_delayed_work(&hdev->work_heartbeat,
- usecs_to_jiffies(HL_HEARTBEAT_PER_USEC));
- }
-
- hdev->late_init_done = true;
-
- return 0;
-}
-
-/*
- * device_late_fini - finalize all that was done in device_late_init
- *
- * @hdev: pointer to habanalabs device structure
- *
- */
-static void device_late_fini(struct hl_device *hdev)
-{
- if (!hdev->late_init_done)
- return;
-
- if (hdev->heartbeat)
- cancel_delayed_work_sync(&hdev->work_heartbeat);
-
- if (hdev->asic_funcs->late_fini)
- hdev->asic_funcs->late_fini(hdev);
-
- hdev->late_init_done = false;
-}
-
-int hl_device_utilization(struct hl_device *hdev, u32 *utilization)
-{
- u64 max_power, curr_power, dc_power, dividend, divisor;
- int rc;
-
- max_power = hdev->max_power;
- dc_power = hdev->asic_prop.dc_power_default;
- divisor = max_power - dc_power;
- if (!divisor) {
- dev_warn(hdev->dev, "device utilization is not supported\n");
- return -EOPNOTSUPP;
- }
- rc = hl_fw_cpucp_power_get(hdev, &curr_power);
-
- if (rc)
- return rc;
-
- curr_power = clamp(curr_power, dc_power, max_power);
-
- dividend = (curr_power - dc_power) * 100;
- *utilization = (u32) div_u64(dividend, divisor);
-
- return 0;
-}
-
-int hl_device_set_debug_mode(struct hl_device *hdev, struct hl_ctx *ctx, bool enable)
-{
- int rc = 0;
-
- mutex_lock(&hdev->debug_lock);
-
- if (!enable) {
- if (!hdev->in_debug) {
- dev_err(hdev->dev,
- "Failed to disable debug mode because device was not in debug mode\n");
- rc = -EFAULT;
- goto out;
- }
-
- if (!hdev->reset_info.hard_reset_pending)
- hdev->asic_funcs->halt_coresight(hdev, ctx);
-
- hdev->in_debug = 0;
-
- goto out;
- }
-
- if (hdev->in_debug) {
- dev_err(hdev->dev,
- "Failed to enable debug mode because device is already in debug mode\n");
- rc = -EFAULT;
- goto out;
- }
-
- hdev->in_debug = 1;
-
-out:
- mutex_unlock(&hdev->debug_lock);
-
- return rc;
-}
-
-static void take_release_locks(struct hl_device *hdev)
-{
- /* Flush anyone that is inside the critical section of enqueue
- * jobs to the H/W
- */
- hdev->asic_funcs->hw_queues_lock(hdev);
- hdev->asic_funcs->hw_queues_unlock(hdev);
-
- /* Flush processes that are sending message to CPU */
- mutex_lock(&hdev->send_cpu_message_lock);
- mutex_unlock(&hdev->send_cpu_message_lock);
-
- /* Flush anyone that is inside device open */
- mutex_lock(&hdev->fpriv_list_lock);
- mutex_unlock(&hdev->fpriv_list_lock);
- mutex_lock(&hdev->fpriv_ctrl_list_lock);
- mutex_unlock(&hdev->fpriv_ctrl_list_lock);
-}
-
-static void cleanup_resources(struct hl_device *hdev, bool hard_reset, bool fw_reset,
- bool skip_wq_flush)
-{
- if (hard_reset)
- device_late_fini(hdev);
-
- /*
- * Halt the engines and disable interrupts so we won't get any more
- * completions from H/W and we won't have any accesses from the
- * H/W to the host machine
- */
- hdev->asic_funcs->halt_engines(hdev, hard_reset, fw_reset);
-
- /* Go over all the queues, release all CS and their jobs */
- hl_cs_rollback_all(hdev, skip_wq_flush);
-
- /* flush the MMU prefetch workqueue */
- flush_workqueue(hdev->prefetch_wq);
-
- /* Release all pending user interrupts, each pending user interrupt
- * holds a reference to user context
- */
- hl_release_pending_user_interrupts(hdev);
-}
-
-/*
- * hl_device_suspend - initiate device suspend
- *
- * @hdev: pointer to habanalabs device structure
- *
- * Puts the hw in the suspend state (all asics).
- * Returns 0 for success or an error on failure.
- * Called at driver suspend.
- */
-int hl_device_suspend(struct hl_device *hdev)
-{
- int rc;
-
- pci_save_state(hdev->pdev);
-
- /* Block future CS/VM/JOB completion operations */
- spin_lock(&hdev->reset_info.lock);
- if (hdev->reset_info.in_reset) {
- spin_unlock(&hdev->reset_info.lock);
- dev_err(hdev->dev, "Can't suspend while in reset\n");
- return -EIO;
- }
- hdev->reset_info.in_reset = 1;
- spin_unlock(&hdev->reset_info.lock);
-
- /* This blocks all other stuff that is not blocked by in_reset */
- hdev->disabled = true;
-
- take_release_locks(hdev);
-
- rc = hdev->asic_funcs->suspend(hdev);
- if (rc)
- dev_err(hdev->dev,
- "Failed to disable PCI access of device CPU\n");
-
- /* Shut down the device */
- pci_disable_device(hdev->pdev);
- pci_set_power_state(hdev->pdev, PCI_D3hot);
-
- return 0;
-}
-
-/*
- * hl_device_resume - initiate device resume
- *
- * @hdev: pointer to habanalabs device structure
- *
- * Bring the hw back to operating state (all asics).
- * Returns 0 for success or an error on failure.
- * Called at driver resume.
- */
-int hl_device_resume(struct hl_device *hdev)
-{
- int rc;
-
- pci_set_power_state(hdev->pdev, PCI_D0);
- pci_restore_state(hdev->pdev);
- rc = pci_enable_device_mem(hdev->pdev);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to enable PCI device in resume\n");
- return rc;
- }
-
- pci_set_master(hdev->pdev);
-
- rc = hdev->asic_funcs->resume(hdev);
- if (rc) {
- dev_err(hdev->dev, "Failed to resume device after suspend\n");
- goto disable_device;
- }
-
-
- /* 'in_reset' was set to true during suspend, now we must clear it in order
- * for hard reset to be performed
- */
- spin_lock(&hdev->reset_info.lock);
- hdev->reset_info.in_reset = 0;
- spin_unlock(&hdev->reset_info.lock);
-
- rc = hl_device_reset(hdev, HL_DRV_RESET_HARD);
- if (rc) {
- dev_err(hdev->dev, "Failed to reset device during resume\n");
- goto disable_device;
- }
-
- return 0;
-
-disable_device:
- pci_clear_master(hdev->pdev);
- pci_disable_device(hdev->pdev);
-
- return rc;
-}
-
-static int device_kill_open_processes(struct hl_device *hdev, u32 timeout, bool control_dev)
-{
- struct task_struct *task = NULL;
- struct list_head *fd_list;
- struct hl_fpriv *hpriv;
- struct mutex *fd_lock;
- u32 pending_cnt;
-
- fd_lock = control_dev ? &hdev->fpriv_ctrl_list_lock : &hdev->fpriv_list_lock;
- fd_list = control_dev ? &hdev->fpriv_ctrl_list : &hdev->fpriv_list;
-
- /* Giving time for user to close FD, and for processes that are inside
- * hl_device_open to finish
- */
- if (!list_empty(fd_list))
- ssleep(1);
-
- if (timeout) {
- pending_cnt = timeout;
- } else {
- if (hdev->process_kill_trial_cnt) {
- /* Processes have been already killed */
- pending_cnt = 1;
- goto wait_for_processes;
- } else {
- /* Wait a small period after process kill */
- pending_cnt = HL_PENDING_RESET_PER_SEC;
- }
- }
-
- mutex_lock(fd_lock);
-
- /* This section must be protected because we are dereferencing
- * pointers that are freed if the process exits
- */
- list_for_each_entry(hpriv, fd_list, dev_node) {
- task = get_pid_task(hpriv->taskpid, PIDTYPE_PID);
- if (task) {
- dev_info(hdev->dev, "Killing user process pid=%d\n",
- task_pid_nr(task));
- send_sig(SIGKILL, task, 1);
- usleep_range(1000, 10000);
-
- put_task_struct(task);
- } else {
- /*
- * If we got here, it means that process was killed from outside the driver
- * right after it started looping on fd_list and before get_pid_task, thus
- * we don't need to kill it.
- */
- dev_dbg(hdev->dev,
- "Can't get task struct for user process, assuming process was killed from outside the driver\n");
- }
- }
-
- mutex_unlock(fd_lock);
-
- /*
- * We killed the open users, but that doesn't mean they are closed.
- * It could be that they are running a long cleanup phase in the driver
- * e.g. MMU unmappings, or running other long teardown flow even before
- * our cleanup.
- * Therefore we need to wait again to make sure they are closed before
- * continuing with the reset.
- */
-
-wait_for_processes:
- while ((!list_empty(fd_list)) && (pending_cnt)) {
- dev_dbg(hdev->dev,
- "Waiting for all unmap operations to finish before hard reset\n");
-
- pending_cnt--;
-
- ssleep(1);
- }
-
- /* All processes exited successfully */
- if (list_empty(fd_list))
- return 0;
-
- /* Give up waiting for processes to exit */
- if (hdev->process_kill_trial_cnt == HL_PENDING_RESET_MAX_TRIALS)
- return -ETIME;
-
- hdev->process_kill_trial_cnt++;
-
- return -EBUSY;
-}
-
-static void device_disable_open_processes(struct hl_device *hdev, bool control_dev)
-{
- struct list_head *fd_list;
- struct hl_fpriv *hpriv;
- struct mutex *fd_lock;
-
- fd_lock = control_dev ? &hdev->fpriv_ctrl_list_lock : &hdev->fpriv_list_lock;
- fd_list = control_dev ? &hdev->fpriv_ctrl_list : &hdev->fpriv_list;
-
- mutex_lock(fd_lock);
- list_for_each_entry(hpriv, fd_list, dev_node)
- hpriv->hdev = NULL;
- mutex_unlock(fd_lock);
-}
-
-static void handle_reset_trigger(struct hl_device *hdev, u32 flags)
-{
- u32 cur_reset_trigger = HL_RESET_TRIGGER_DEFAULT;
-
- /* No consecutive mechanism when user context exists */
- if (hdev->is_compute_ctx_active)
- return;
-
- /*
- * 'reset cause' is being updated here, because getting here
- * means that it's the 1st time and the last time we're here
- * ('in_reset' makes sure of it). This makes sure that
- * 'reset_cause' will continue holding its 1st recorded reason!
- */
- if (flags & HL_DRV_RESET_HEARTBEAT) {
- hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_HEARTBEAT;
- cur_reset_trigger = HL_DRV_RESET_HEARTBEAT;
- } else if (flags & HL_DRV_RESET_TDR) {
- hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_TDR;
- cur_reset_trigger = HL_DRV_RESET_TDR;
- } else if (flags & HL_DRV_RESET_FW_FATAL_ERR) {
- hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_UNKNOWN;
- cur_reset_trigger = HL_DRV_RESET_FW_FATAL_ERR;
- } else {
- hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_UNKNOWN;
- }
-
- /*
- * If reset cause is same twice, then reset_trigger_repeated
- * is set and if this reset is due to a fatal FW error
- * device is set to an unstable state.
- */
- if (hdev->reset_info.prev_reset_trigger != cur_reset_trigger) {
- hdev->reset_info.prev_reset_trigger = cur_reset_trigger;
- hdev->reset_info.reset_trigger_repeated = 0;
- } else {
- hdev->reset_info.reset_trigger_repeated = 1;
- }
-
- /* If reset is due to heartbeat, device CPU is no responsive in
- * which case no point sending PCI disable message to it.
- *
- * If F/W is performing the reset, no need to send it a message to disable
- * PCI access
- */
- if ((flags & HL_DRV_RESET_HARD) &&
- !(flags & (HL_DRV_RESET_HEARTBEAT | HL_DRV_RESET_BYPASS_REQ_TO_FW))) {
- /* Disable PCI access from device F/W so he won't send
- * us additional interrupts. We disable MSI/MSI-X at
- * the halt_engines function and we can't have the F/W
- * sending us interrupts after that. We need to disable
- * the access here because if the device is marked
- * disable, the message won't be send. Also, in case
- * of heartbeat, the device CPU is marked as disable
- * so this message won't be sent
- */
- if (hl_fw_send_pci_access_msg(hdev, CPUCP_PACKET_DISABLE_PCI_ACCESS, 0x0))
- dev_warn(hdev->dev,
- "Failed to disable PCI access by F/W\n");
- }
-}
-
-/*
- * hl_device_reset - reset the device
- *
- * @hdev: pointer to habanalabs device structure
- * @flags: reset flags.
- *
- * Block future CS and wait for pending CS to be enqueued
- * Call ASIC H/W fini
- * Flush all completions
- * Re-initialize all internal data structures
- * Call ASIC H/W init, late_init
- * Test queues
- * Enable device
- *
- * Returns 0 for success or an error on failure.
- */
-int hl_device_reset(struct hl_device *hdev, u32 flags)
-{
- bool hard_reset, from_hard_reset_thread, fw_reset, hard_instead_soft = false,
- reset_upon_device_release = false, schedule_hard_reset = false, delay_reset,
- from_dev_release, from_watchdog_thread;
- u64 idle_mask[HL_BUSY_ENGINES_MASK_EXT_SIZE] = {0};
- struct hl_ctx *ctx;
- int i, rc;
-
- if (!hdev->init_done) {
- dev_err(hdev->dev, "Can't reset before initialization is done\n");
- return 0;
- }
-
- hard_reset = !!(flags & HL_DRV_RESET_HARD);
- from_hard_reset_thread = !!(flags & HL_DRV_RESET_FROM_RESET_THR);
- fw_reset = !!(flags & HL_DRV_RESET_BYPASS_REQ_TO_FW);
- from_dev_release = !!(flags & HL_DRV_RESET_DEV_RELEASE);
- delay_reset = !!(flags & HL_DRV_RESET_DELAY);
- from_watchdog_thread = !!(flags & HL_DRV_RESET_FROM_WD_THR);
-
- if (!hard_reset && !hdev->asic_prop.supports_compute_reset) {
- hard_instead_soft = true;
- hard_reset = true;
- }
-
- if (hdev->reset_upon_device_release && (flags & HL_DRV_RESET_DEV_RELEASE)) {
- if (hard_reset) {
- dev_crit(hdev->dev,
- "Aborting reset because hard-reset is mutually exclusive with reset-on-device-release\n");
- return -EINVAL;
- }
-
- reset_upon_device_release = true;
-
- goto do_reset;
- }
-
- if (!hard_reset && !hdev->asic_prop.allow_inference_soft_reset) {
- hard_instead_soft = true;
- hard_reset = true;
- }
-
- if (hard_instead_soft)
- dev_dbg(hdev->dev, "Doing hard-reset instead of compute reset\n");
-
-do_reset:
- /* Re-entry of reset thread */
- if (from_hard_reset_thread && hdev->process_kill_trial_cnt)
- goto kill_processes;
-
- /*
- * Prevent concurrency in this function - only one reset should be
- * done at any given time. Only need to perform this if we didn't
- * get from the dedicated hard reset thread
- */
- if (!from_hard_reset_thread) {
- /* Block future CS/VM/JOB completion operations */
- spin_lock(&hdev->reset_info.lock);
- if (hdev->reset_info.in_reset) {
- /* We only allow scheduling of a hard reset during compute reset */
- if (hard_reset && hdev->reset_info.in_compute_reset)
- hdev->reset_info.hard_reset_schedule_flags = flags;
- spin_unlock(&hdev->reset_info.lock);
- return 0;
- }
-
- /* This still allows the completion of some KDMA ops
- * Update this before in_reset because in_compute_reset implies we are in reset
- */
- hdev->reset_info.in_compute_reset = !hard_reset;
-
- hdev->reset_info.in_reset = 1;
-
- spin_unlock(&hdev->reset_info.lock);
-
- /* Cancel the device release watchdog work if required.
- * In case of reset-upon-device-release while the release watchdog work is
- * scheduled, do hard-reset instead of compute-reset.
- */
- if ((hard_reset || from_dev_release) && hdev->reset_info.watchdog_active) {
- hdev->reset_info.watchdog_active = 0;
- if (!from_watchdog_thread)
- cancel_delayed_work_sync(
- &hdev->device_release_watchdog_work.reset_work);
-
- if (from_dev_release) {
- flags |= HL_DRV_RESET_HARD;
- flags &= ~HL_DRV_RESET_DEV_RELEASE;
- hard_reset = true;
- }
- }
-
- if (delay_reset)
- usleep_range(HL_RESET_DELAY_USEC, HL_RESET_DELAY_USEC << 1);
-
- handle_reset_trigger(hdev, flags);
-
- /* This also blocks future CS/VM/JOB completion operations */
- hdev->disabled = true;
-
- take_release_locks(hdev);
-
- if (hard_reset)
- dev_info(hdev->dev, "Going to reset device\n");
- else if (reset_upon_device_release)
- dev_dbg(hdev->dev, "Going to reset device after release by user\n");
- else
- dev_dbg(hdev->dev, "Going to reset engines of inference device\n");
- }
-
-again:
- if ((hard_reset) && (!from_hard_reset_thread)) {
- hdev->reset_info.hard_reset_pending = true;
-
- hdev->process_kill_trial_cnt = 0;
-
- hdev->device_reset_work.flags = flags;
-
- /*
- * Because the reset function can't run from heartbeat work,
- * we need to call the reset function from a dedicated work.
- */
- queue_delayed_work(hdev->reset_wq, &hdev->device_reset_work.reset_work, 0);
-
- return 0;
- }
-
- cleanup_resources(hdev, hard_reset, fw_reset, from_dev_release);
-
-kill_processes:
- if (hard_reset) {
- /* Kill processes here after CS rollback. This is because the
- * process can't really exit until all its CSs are done, which
- * is what we do in cs rollback
- */
- rc = device_kill_open_processes(hdev, 0, false);
-
- if (rc == -EBUSY) {
- if (hdev->device_fini_pending) {
- dev_crit(hdev->dev,
- "Failed to kill all open processes, stopping hard reset\n");
- goto out_err;
- }
-
- /* signal reset thread to reschedule */
- return rc;
- }
-
- if (rc) {
- dev_crit(hdev->dev,
- "Failed to kill all open processes, stopping hard reset\n");
- goto out_err;
- }
-
- /* Flush the Event queue workers to make sure no other thread is
- * reading or writing to registers during the reset
- */
- flush_workqueue(hdev->eq_wq);
- }
-
- /* Reset the H/W. It will be in idle state after this returns */
- hdev->asic_funcs->hw_fini(hdev, hard_reset, fw_reset);
-
- if (hard_reset) {
- hdev->fw_loader.fw_comp_loaded = FW_TYPE_NONE;
-
- /* Release kernel context */
- if (hdev->kernel_ctx && hl_ctx_put(hdev->kernel_ctx) == 1)
- hdev->kernel_ctx = NULL;
-
- hl_vm_fini(hdev);
- hl_mmu_fini(hdev);
- hl_eq_reset(hdev, &hdev->event_queue);
- }
-
- /* Re-initialize PI,CI to 0 in all queues (hw queue, cq) */
- hl_hw_queue_reset(hdev, hard_reset);
- for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++)
- hl_cq_reset(hdev, &hdev->completion_queue[i]);
-
- /* Make sure the context switch phase will run again */
- ctx = hl_get_compute_ctx(hdev);
- if (ctx) {
- atomic_set(&ctx->thread_ctx_switch_token, 1);
- ctx->thread_ctx_switch_wait_token = 0;
- hl_ctx_put(ctx);
- }
-
- /* Finished tear-down, starting to re-initialize */
-
- if (hard_reset) {
- hdev->device_cpu_disabled = false;
- hdev->reset_info.hard_reset_pending = false;
-
- if (hdev->reset_info.reset_trigger_repeated &&
- (hdev->reset_info.prev_reset_trigger ==
- HL_DRV_RESET_FW_FATAL_ERR)) {
- /* if there 2 back to back resets from FW,
- * ensure driver puts the driver in a unusable state
- */
- dev_crit(hdev->dev,
- "Consecutive FW fatal errors received, stopping hard reset\n");
- rc = -EIO;
- goto out_err;
- }
-
- if (hdev->kernel_ctx) {
- dev_crit(hdev->dev,
- "kernel ctx was alive during hard reset, something is terribly wrong\n");
- rc = -EBUSY;
- goto out_err;
- }
-
- rc = hl_mmu_init(hdev);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to initialize MMU S/W after hard reset\n");
- goto out_err;
- }
-
- /* Allocate the kernel context */
- hdev->kernel_ctx = kzalloc(sizeof(*hdev->kernel_ctx),
- GFP_KERNEL);
- if (!hdev->kernel_ctx) {
- rc = -ENOMEM;
- hl_mmu_fini(hdev);
- goto out_err;
- }
-
- hdev->is_compute_ctx_active = false;
-
- rc = hl_ctx_init(hdev, hdev->kernel_ctx, true);
- if (rc) {
- dev_err(hdev->dev,
- "failed to init kernel ctx in hard reset\n");
- kfree(hdev->kernel_ctx);
- hdev->kernel_ctx = NULL;
- hl_mmu_fini(hdev);
- goto out_err;
- }
- }
-
- /* Device is now enabled as part of the initialization requires
- * communication with the device firmware to get information that
- * is required for the initialization itself
- */
- hdev->disabled = false;
-
- /* F/W security enabled indication might be updated after hard-reset */
- if (hard_reset) {
- rc = hl_fw_read_preboot_status(hdev);
- if (rc)
- goto out_err;
- }
-
- rc = hdev->asic_funcs->hw_init(hdev);
- if (rc) {
- dev_err(hdev->dev, "failed to initialize the H/W after reset\n");
- goto out_err;
- }
-
- /* If device is not idle fail the reset process */
- if (!hdev->asic_funcs->is_device_idle(hdev, idle_mask,
- HL_BUSY_ENGINES_MASK_EXT_SIZE, NULL)) {
- print_idle_status_mask(hdev, "device is not idle after reset", idle_mask);
- rc = -EIO;
- goto out_err;
- }
-
- /* Check that the communication with the device is working */
- rc = hdev->asic_funcs->test_queues(hdev);
- if (rc) {
- dev_err(hdev->dev, "Failed to detect if device is alive after reset\n");
- goto out_err;
- }
-
- if (hard_reset) {
- rc = device_late_init(hdev);
- if (rc) {
- dev_err(hdev->dev, "Failed late init after hard reset\n");
- goto out_err;
- }
-
- rc = hl_vm_init(hdev);
- if (rc) {
- dev_err(hdev->dev, "Failed to init memory module after hard reset\n");
- goto out_err;
- }
-
- if (!hdev->asic_prop.fw_security_enabled)
- hl_fw_set_max_power(hdev);
- } else {
- rc = hdev->asic_funcs->compute_reset_late_init(hdev);
- if (rc) {
- if (reset_upon_device_release)
- dev_err(hdev->dev,
- "Failed late init in reset after device release\n");
- else
- dev_err(hdev->dev, "Failed late init after compute reset\n");
- goto out_err;
- }
- }
-
- rc = hdev->asic_funcs->scrub_device_mem(hdev);
- if (rc) {
- dev_err(hdev->dev, "scrub mem failed from device reset (%d)\n", rc);
- return rc;
- }
-
- spin_lock(&hdev->reset_info.lock);
- hdev->reset_info.in_compute_reset = 0;
-
- /* Schedule hard reset only if requested and if not already in hard reset.
- * We keep 'in_reset' enabled, so no other reset can go in during the hard
- * reset schedule
- */
- if (!hard_reset && hdev->reset_info.hard_reset_schedule_flags)
- schedule_hard_reset = true;
- else
- hdev->reset_info.in_reset = 0;
-
- spin_unlock(&hdev->reset_info.lock);
-
- hdev->reset_info.needs_reset = false;
-
- if (hard_reset)
- dev_info(hdev->dev, "Successfully finished resetting the device\n");
- else
- dev_dbg(hdev->dev, "Successfully finished resetting the device\n");
-
- if (hard_reset) {
- hdev->reset_info.hard_reset_cnt++;
-
- /* After reset is done, we are ready to receive events from
- * the F/W. We can't do it before because we will ignore events
- * and if those events are fatal, we won't know about it and
- * the device will be operational although it shouldn't be
- */
- hdev->asic_funcs->enable_events_from_fw(hdev);
- } else {
- if (!reset_upon_device_release)
- hdev->reset_info.compute_reset_cnt++;
-
- if (schedule_hard_reset) {
- dev_info(hdev->dev, "Performing hard reset scheduled during compute reset\n");
- flags = hdev->reset_info.hard_reset_schedule_flags;
- hdev->reset_info.hard_reset_schedule_flags = 0;
- hdev->disabled = true;
- hard_reset = true;
- handle_reset_trigger(hdev, flags);
- goto again;
- }
- }
-
- return 0;
-
-out_err:
- hdev->disabled = true;
-
- spin_lock(&hdev->reset_info.lock);
- hdev->reset_info.in_compute_reset = 0;
-
- if (hard_reset) {
- dev_err(hdev->dev, "Failed to reset! Device is NOT usable\n");
- hdev->reset_info.hard_reset_cnt++;
- } else if (reset_upon_device_release) {
- spin_unlock(&hdev->reset_info.lock);
- dev_err(hdev->dev, "Failed to reset device after user release\n");
- flags |= HL_DRV_RESET_HARD;
- flags &= ~HL_DRV_RESET_DEV_RELEASE;
- hard_reset = true;
- goto again;
- } else {
- spin_unlock(&hdev->reset_info.lock);
- dev_err(hdev->dev, "Failed to do compute reset\n");
- hdev->reset_info.compute_reset_cnt++;
- flags |= HL_DRV_RESET_HARD;
- hard_reset = true;
- goto again;
- }
-
- hdev->reset_info.in_reset = 0;
-
- spin_unlock(&hdev->reset_info.lock);
-
- return rc;
-}
-
-/*
- * hl_device_cond_reset() - conditionally reset the device.
- * @hdev: pointer to habanalabs device structure.
- * @reset_flags: reset flags.
- * @event_mask: events to notify user about.
- *
- * Conditionally reset the device, or alternatively schedule a watchdog work to reset the device
- * unless another reset precedes it.
- */
-int hl_device_cond_reset(struct hl_device *hdev, u32 flags, u64 event_mask)
-{
- struct hl_ctx *ctx = NULL;
-
- /* Device release watchdog is only for hard reset */
- if (!(flags & HL_DRV_RESET_HARD) && hdev->asic_prop.allow_inference_soft_reset)
- goto device_reset;
-
- /* F/W reset cannot be postponed */
- if (flags & HL_DRV_RESET_BYPASS_REQ_TO_FW)
- goto device_reset;
-
- /* Device release watchdog is relevant only if user exists and gets a reset notification */
- if (!(event_mask & HL_NOTIFIER_EVENT_DEVICE_RESET)) {
- dev_err(hdev->dev, "Resetting device without a reset indication to user\n");
- goto device_reset;
- }
-
- ctx = hl_get_compute_ctx(hdev);
- if (!ctx || !ctx->hpriv->notifier_event.eventfd)
- goto device_reset;
-
- /* Schedule the device release watchdog work unless reset is already in progress or if the
- * work is already scheduled.
- */
- spin_lock(&hdev->reset_info.lock);
- if (hdev->reset_info.in_reset) {
- spin_unlock(&hdev->reset_info.lock);
- goto device_reset;
- }
-
- if (hdev->reset_info.watchdog_active)
- goto out;
-
- hdev->device_release_watchdog_work.flags = flags;
- dev_dbg(hdev->dev, "Device is going to be reset in %u sec unless being released\n",
- hdev->device_release_watchdog_timeout_sec);
- schedule_delayed_work(&hdev->device_release_watchdog_work.reset_work,
- msecs_to_jiffies(hdev->device_release_watchdog_timeout_sec * 1000));
- hdev->reset_info.watchdog_active = 1;
-out:
- spin_unlock(&hdev->reset_info.lock);
-
- hl_notifier_event_send_all(hdev, event_mask);
-
- hl_ctx_put(ctx);
-
- hl_abort_waitings_for_completion(hdev);
-
- return 0;
-
-device_reset:
- if (event_mask)
- hl_notifier_event_send_all(hdev, event_mask);
- if (ctx)
- hl_ctx_put(ctx);
-
- return hl_device_reset(hdev, flags);
-}
-
-static void hl_notifier_event_send(struct hl_notifier_event *notifier_event, u64 event_mask)
-{
- mutex_lock(&notifier_event->lock);
- notifier_event->events_mask |= event_mask;
-
- if (notifier_event->eventfd)
- eventfd_signal(notifier_event->eventfd, 1);
-
- mutex_unlock(&notifier_event->lock);
-}
-
-/*
- * hl_notifier_event_send_all - notify all user processes via eventfd
- *
- * @hdev: pointer to habanalabs device structure
- * @event_mask: the occurred event/s
- * Returns 0 for success or an error on failure.
- */
-void hl_notifier_event_send_all(struct hl_device *hdev, u64 event_mask)
-{
- struct hl_fpriv *hpriv;
-
- if (!event_mask) {
- dev_warn(hdev->dev, "Skip sending zero event");
- return;
- }
-
- mutex_lock(&hdev->fpriv_list_lock);
-
- list_for_each_entry(hpriv, &hdev->fpriv_list, dev_node)
- hl_notifier_event_send(&hpriv->notifier_event, event_mask);
-
- mutex_unlock(&hdev->fpriv_list_lock);
-
- /* control device */
- mutex_lock(&hdev->fpriv_ctrl_list_lock);
-
- list_for_each_entry(hpriv, &hdev->fpriv_ctrl_list, dev_node)
- hl_notifier_event_send(&hpriv->notifier_event, event_mask);
-
- mutex_unlock(&hdev->fpriv_ctrl_list_lock);
-}
-
-/*
- * hl_device_init - main initialization function for habanalabs device
- *
- * @hdev: pointer to habanalabs device structure
- *
- * Allocate an id for the device, do early initialization and then call the
- * ASIC specific initialization functions. Finally, create the cdev and the
- * Linux device to expose it to the user
- */
-int hl_device_init(struct hl_device *hdev, struct class *hclass)
-{
- int i, rc, cq_cnt, user_interrupt_cnt, cq_ready_cnt;
- char *name;
- bool add_cdev_sysfs_on_err = false;
-
- hdev->cdev_idx = hdev->id / 2;
-
- name = kasprintf(GFP_KERNEL, "hl%d", hdev->cdev_idx);
- if (!name) {
- rc = -ENOMEM;
- goto out_disabled;
- }
-
- /* Initialize cdev and device structures */
- rc = device_init_cdev(hdev, hclass, hdev->id, &hl_ops, name,
- &hdev->cdev, &hdev->dev);
-
- kfree(name);
-
- if (rc)
- goto out_disabled;
-
- name = kasprintf(GFP_KERNEL, "hl_controlD%d", hdev->cdev_idx);
- if (!name) {
- rc = -ENOMEM;
- goto free_dev;
- }
-
- /* Initialize cdev and device structures for control device */
- rc = device_init_cdev(hdev, hclass, hdev->id_control, &hl_ctrl_ops,
- name, &hdev->cdev_ctrl, &hdev->dev_ctrl);
-
- kfree(name);
-
- if (rc)
- goto free_dev;
-
- /* Initialize ASIC function pointers and perform early init */
- rc = device_early_init(hdev);
- if (rc)
- goto free_dev_ctrl;
-
- user_interrupt_cnt = hdev->asic_prop.user_dec_intr_count +
- hdev->asic_prop.user_interrupt_count;
-
- if (user_interrupt_cnt) {
- hdev->user_interrupt = kcalloc(user_interrupt_cnt, sizeof(*hdev->user_interrupt),
- GFP_KERNEL);
- if (!hdev->user_interrupt) {
- rc = -ENOMEM;
- goto early_fini;
- }
- }
-
- /*
- * Start calling ASIC initialization. First S/W then H/W and finally
- * late init
- */
- rc = hdev->asic_funcs->sw_init(hdev);
- if (rc)
- goto free_usr_intr_mem;
-
-
- /* initialize completion structure for multi CS wait */
- hl_multi_cs_completion_init(hdev);
-
- /*
- * Initialize the H/W queues. Must be done before hw_init, because
- * there the addresses of the kernel queue are being written to the
- * registers of the device
- */
- rc = hl_hw_queues_create(hdev);
- if (rc) {
- dev_err(hdev->dev, "failed to initialize kernel queues\n");
- goto sw_fini;
- }
-
- cq_cnt = hdev->asic_prop.completion_queues_count;
-
- /*
- * Initialize the completion queues. Must be done before hw_init,
- * because there the addresses of the completion queues are being
- * passed as arguments to request_irq
- */
- if (cq_cnt) {
- hdev->completion_queue = kcalloc(cq_cnt,
- sizeof(*hdev->completion_queue),
- GFP_KERNEL);
-
- if (!hdev->completion_queue) {
- dev_err(hdev->dev,
- "failed to allocate completion queues\n");
- rc = -ENOMEM;
- goto hw_queues_destroy;
- }
- }
-
- for (i = 0, cq_ready_cnt = 0 ; i < cq_cnt ; i++, cq_ready_cnt++) {
- rc = hl_cq_init(hdev, &hdev->completion_queue[i],
- hdev->asic_funcs->get_queue_id_for_cq(hdev, i));
- if (rc) {
- dev_err(hdev->dev,
- "failed to initialize completion queue\n");
- goto cq_fini;
- }
- hdev->completion_queue[i].cq_idx = i;
- }
-
- hdev->shadow_cs_queue = kcalloc(hdev->asic_prop.max_pending_cs,
- sizeof(struct hl_cs *), GFP_KERNEL);
- if (!hdev->shadow_cs_queue) {
- rc = -ENOMEM;
- goto cq_fini;
- }
-
- /*
- * Initialize the event queue. Must be done before hw_init,
- * because there the address of the event queue is being
- * passed as argument to request_irq
- */
- rc = hl_eq_init(hdev, &hdev->event_queue);
- if (rc) {
- dev_err(hdev->dev, "failed to initialize event queue\n");
- goto free_shadow_cs_queue;
- }
-
- /* MMU S/W must be initialized before kernel context is created */
- rc = hl_mmu_init(hdev);
- if (rc) {
- dev_err(hdev->dev, "Failed to initialize MMU S/W structures\n");
- goto eq_fini;
- }
-
- /* Allocate the kernel context */
- hdev->kernel_ctx = kzalloc(sizeof(*hdev->kernel_ctx), GFP_KERNEL);
- if (!hdev->kernel_ctx) {
- rc = -ENOMEM;
- goto mmu_fini;
- }
-
- hdev->is_compute_ctx_active = false;
-
- hdev->asic_funcs->state_dump_init(hdev);
-
- hdev->device_release_watchdog_timeout_sec = HL_DEVICE_RELEASE_WATCHDOG_TIMEOUT_SEC;
-
- hdev->memory_scrub_val = MEM_SCRUB_DEFAULT_VAL;
- hl_debugfs_add_device(hdev);
-
- /* debugfs nodes are created in hl_ctx_init so it must be called after
- * hl_debugfs_add_device.
- */
- rc = hl_ctx_init(hdev, hdev->kernel_ctx, true);
- if (rc) {
- dev_err(hdev->dev, "failed to initialize kernel context\n");
- kfree(hdev->kernel_ctx);
- goto remove_device_from_debugfs;
- }
-
- rc = hl_cb_pool_init(hdev);
- if (rc) {
- dev_err(hdev->dev, "failed to initialize CB pool\n");
- goto release_ctx;
- }
-
- rc = hl_dec_init(hdev);
- if (rc) {
- dev_err(hdev->dev, "Failed to initialize the decoder module\n");
- goto cb_pool_fini;
- }
-
- /*
- * From this point, override rc (=0) in case of an error to allow
- * debugging (by adding char devices and create sysfs nodes as part of
- * the error flow).
- */
- add_cdev_sysfs_on_err = true;
-
- /* Device is now enabled as part of the initialization requires
- * communication with the device firmware to get information that
- * is required for the initialization itself
- */
- hdev->disabled = false;
-
- rc = hdev->asic_funcs->hw_init(hdev);
- if (rc) {
- dev_err(hdev->dev, "failed to initialize the H/W\n");
- rc = 0;
- goto out_disabled;
- }
-
- /* Check that the communication with the device is working */
- rc = hdev->asic_funcs->test_queues(hdev);
- if (rc) {
- dev_err(hdev->dev, "Failed to detect if device is alive\n");
- rc = 0;
- goto out_disabled;
- }
-
- rc = device_late_init(hdev);
- if (rc) {
- dev_err(hdev->dev, "Failed late initialization\n");
- rc = 0;
- goto out_disabled;
- }
-
- dev_info(hdev->dev, "Found %s device with %lluGB DRAM\n",
- hdev->asic_name,
- hdev->asic_prop.dram_size / SZ_1G);
-
- rc = hl_vm_init(hdev);
- if (rc) {
- dev_err(hdev->dev, "Failed to initialize memory module\n");
- rc = 0;
- goto out_disabled;
- }
-
- /*
- * Expose devices and sysfs nodes to user.
- * From here there is no need to add char devices and create sysfs nodes
- * in case of an error.
- */
- add_cdev_sysfs_on_err = false;
- rc = device_cdev_sysfs_add(hdev);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to add char devices and sysfs nodes\n");
- rc = 0;
- goto out_disabled;
- }
-
- /* Need to call this again because the max power might change,
- * depending on card type for certain ASICs
- */
- if (hdev->asic_prop.set_max_power_on_device_init &&
- !hdev->asic_prop.fw_security_enabled)
- hl_fw_set_max_power(hdev);
-
- /*
- * hl_hwmon_init() must be called after device_late_init(), because only
- * there we get the information from the device about which
- * hwmon-related sensors the device supports.
- * Furthermore, it must be done after adding the device to the system.
- */
- rc = hl_hwmon_init(hdev);
- if (rc) {
- dev_err(hdev->dev, "Failed to initialize hwmon\n");
- rc = 0;
- goto out_disabled;
- }
-
- dev_notice(hdev->dev,
- "Successfully added device to habanalabs driver\n");
-
- hdev->init_done = true;
-
- /* After initialization is done, we are ready to receive events from
- * the F/W. We can't do it before because we will ignore events and if
- * those events are fatal, we won't know about it and the device will
- * be operational although it shouldn't be
- */
- hdev->asic_funcs->enable_events_from_fw(hdev);
-
- return 0;
-
-cb_pool_fini:
- hl_cb_pool_fini(hdev);
-release_ctx:
- if (hl_ctx_put(hdev->kernel_ctx) != 1)
- dev_err(hdev->dev,
- "kernel ctx is still alive on initialization failure\n");
-remove_device_from_debugfs:
- hl_debugfs_remove_device(hdev);
-mmu_fini:
- hl_mmu_fini(hdev);
-eq_fini:
- hl_eq_fini(hdev, &hdev->event_queue);
-free_shadow_cs_queue:
- kfree(hdev->shadow_cs_queue);
-cq_fini:
- for (i = 0 ; i < cq_ready_cnt ; i++)
- hl_cq_fini(hdev, &hdev->completion_queue[i]);
- kfree(hdev->completion_queue);
-hw_queues_destroy:
- hl_hw_queues_destroy(hdev);
-sw_fini:
- hdev->asic_funcs->sw_fini(hdev);
-free_usr_intr_mem:
- kfree(hdev->user_interrupt);
-early_fini:
- device_early_fini(hdev);
-free_dev_ctrl:
- put_device(hdev->dev_ctrl);
-free_dev:
- put_device(hdev->dev);
-out_disabled:
- hdev->disabled = true;
- if (add_cdev_sysfs_on_err)
- device_cdev_sysfs_add(hdev);
- if (hdev->pdev)
- dev_err(&hdev->pdev->dev,
- "Failed to initialize hl%d. Device is NOT usable !\n",
- hdev->cdev_idx);
- else
- pr_err("Failed to initialize hl%d. Device is NOT usable !\n",
- hdev->cdev_idx);
-
- return rc;
-}
-
-/*
- * hl_device_fini - main tear-down function for habanalabs device
- *
- * @hdev: pointer to habanalabs device structure
- *
- * Destroy the device, call ASIC fini functions and release the id
- */
-void hl_device_fini(struct hl_device *hdev)
-{
- bool device_in_reset;
- ktime_t timeout;
- u64 reset_sec;
- int i, rc;
-
- dev_info(hdev->dev, "Removing device\n");
-
- hdev->device_fini_pending = 1;
- flush_delayed_work(&hdev->device_reset_work.reset_work);
-
- if (hdev->pldm)
- reset_sec = HL_PLDM_HARD_RESET_MAX_TIMEOUT;
- else
- reset_sec = HL_HARD_RESET_MAX_TIMEOUT;
-
- /*
- * This function is competing with the reset function, so try to
- * take the reset atomic and if we are already in middle of reset,
- * wait until reset function is finished. Reset function is designed
- * to always finish. However, in Gaudi, because of all the network
- * ports, the hard reset could take between 10-30 seconds
- */
-
- timeout = ktime_add_us(ktime_get(), reset_sec * 1000 * 1000);
-
- spin_lock(&hdev->reset_info.lock);
- device_in_reset = !!hdev->reset_info.in_reset;
- if (!device_in_reset)
- hdev->reset_info.in_reset = 1;
- spin_unlock(&hdev->reset_info.lock);
-
- while (device_in_reset) {
- usleep_range(50, 200);
-
- spin_lock(&hdev->reset_info.lock);
- device_in_reset = !!hdev->reset_info.in_reset;
- if (!device_in_reset)
- hdev->reset_info.in_reset = 1;
- spin_unlock(&hdev->reset_info.lock);
-
- if (ktime_compare(ktime_get(), timeout) > 0) {
- dev_crit(hdev->dev,
- "Failed to remove device because reset function did not finish\n");
- return;
- }
- }
-
- cancel_delayed_work_sync(&hdev->device_release_watchdog_work.reset_work);
-
- /* Disable PCI access from device F/W so it won't send us additional
- * interrupts. We disable MSI/MSI-X at the halt_engines function and we
- * can't have the F/W sending us interrupts after that. We need to
- * disable the access here because if the device is marked disable, the
- * message won't be send. Also, in case of heartbeat, the device CPU is
- * marked as disable so this message won't be sent
- */
- hl_fw_send_pci_access_msg(hdev, CPUCP_PACKET_DISABLE_PCI_ACCESS, 0x0);
-
- /* Mark device as disabled */
- hdev->disabled = true;
-
- take_release_locks(hdev);
-
- hdev->reset_info.hard_reset_pending = true;
-
- hl_hwmon_fini(hdev);
-
- cleanup_resources(hdev, true, false, false);
-
- /* Kill processes here after CS rollback. This is because the process
- * can't really exit until all its CSs are done, which is what we
- * do in cs rollback
- */
- dev_info(hdev->dev,
- "Waiting for all processes to exit (timeout of %u seconds)",
- HL_WAIT_PROCESS_KILL_ON_DEVICE_FINI);
-
- hdev->process_kill_trial_cnt = 0;
- rc = device_kill_open_processes(hdev, HL_WAIT_PROCESS_KILL_ON_DEVICE_FINI, false);
- if (rc) {
- dev_crit(hdev->dev, "Failed to kill all open processes\n");
- device_disable_open_processes(hdev, false);
- }
-
- hdev->process_kill_trial_cnt = 0;
- rc = device_kill_open_processes(hdev, 0, true);
- if (rc) {
- dev_crit(hdev->dev, "Failed to kill all control device open processes\n");
- device_disable_open_processes(hdev, true);
- }
-
- hl_cb_pool_fini(hdev);
-
- /* Reset the H/W. It will be in idle state after this returns */
- hdev->asic_funcs->hw_fini(hdev, true, false);
-
- hdev->fw_loader.fw_comp_loaded = FW_TYPE_NONE;
-
- /* Release kernel context */
- if ((hdev->kernel_ctx) && (hl_ctx_put(hdev->kernel_ctx) != 1))
- dev_err(hdev->dev, "kernel ctx is still alive\n");
-
- hl_debugfs_remove_device(hdev);
-
- hl_dec_fini(hdev);
-
- hl_vm_fini(hdev);
-
- hl_mmu_fini(hdev);
-
- vfree(hdev->captured_err_info.pgf_info.user_mappings);
-
- hl_eq_fini(hdev, &hdev->event_queue);
-
- kfree(hdev->shadow_cs_queue);
-
- for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++)
- hl_cq_fini(hdev, &hdev->completion_queue[i]);
- kfree(hdev->completion_queue);
- kfree(hdev->user_interrupt);
-
- hl_hw_queues_destroy(hdev);
-
- /* Call ASIC S/W finalize function */
- hdev->asic_funcs->sw_fini(hdev);
-
- device_early_fini(hdev);
-
- /* Hide devices and sysfs nodes from user */
- device_cdev_sysfs_del(hdev);
-
- pr_info("removed device successfully\n");
-}
-
-/*
- * MMIO register access helper functions.
- */
-
-/*
- * hl_rreg - Read an MMIO register
- *
- * @hdev: pointer to habanalabs device structure
- * @reg: MMIO register offset (in bytes)
- *
- * Returns the value of the MMIO register we are asked to read
- *
- */
-inline u32 hl_rreg(struct hl_device *hdev, u32 reg)
-{
- return readl(hdev->rmmio + reg);
-}
-
-/*
- * hl_wreg - Write to an MMIO register
- *
- * @hdev: pointer to habanalabs device structure
- * @reg: MMIO register offset (in bytes)
- * @val: 32-bit value
- *
- * Writes the 32-bit value into the MMIO register
- *
- */
-inline void hl_wreg(struct hl_device *hdev, u32 reg, u32 val)
-{
- writel(val, hdev->rmmio + reg);
-}
-
-void hl_capture_razwi(struct hl_device *hdev, u64 addr, u16 *engine_id, u16 num_of_engines,
- u8 flags)
-{
- if (num_of_engines > HL_RAZWI_MAX_NUM_OF_ENGINES_PER_RTR) {
- dev_err(hdev->dev,
- "Number of possible razwi initiators (%u) exceeded limit (%u)\n",
- num_of_engines, HL_RAZWI_MAX_NUM_OF_ENGINES_PER_RTR);
- return;
- }
-
- /* In case it's the first razwi since the device was opened, capture its parameters */
- if (atomic_cmpxchg(&hdev->captured_err_info.razwi_info_recorded, 0, 1))
- return;
-
- hdev->captured_err_info.razwi.timestamp = ktime_to_ns(ktime_get());
- hdev->captured_err_info.razwi.addr = addr;
- hdev->captured_err_info.razwi.num_of_possible_engines = num_of_engines;
- memcpy(&hdev->captured_err_info.razwi.engine_id[0], &engine_id[0],
- num_of_engines * sizeof(u16));
- hdev->captured_err_info.razwi.flags = flags;
-}
-
-void hl_handle_razwi(struct hl_device *hdev, u64 addr, u16 *engine_id, u16 num_of_engines,
- u8 flags, u64 *event_mask)
-{
- hl_capture_razwi(hdev, addr, engine_id, num_of_engines, flags);
-
- if (event_mask)
- *event_mask |= HL_NOTIFIER_EVENT_RAZWI;
-}
-
-static void hl_capture_user_mappings(struct hl_device *hdev, bool is_pmmu)
-{
- struct page_fault_info *pgf_info = &hdev->captured_err_info.pgf_info;
- struct hl_vm_phys_pg_pack *phys_pg_pack = NULL;
- struct hl_vm_hash_node *hnode;
- struct hl_userptr *userptr;
- enum vm_type *vm_type;
- struct hl_ctx *ctx;
- u32 map_idx = 0;
- int i;
-
- /* Reset previous session count*/
- pgf_info->num_of_user_mappings = 0;
-
- ctx = hl_get_compute_ctx(hdev);
- if (!ctx) {
- dev_err(hdev->dev, "Can't get user context for user mappings\n");
- return;
- }
-
- mutex_lock(&ctx->mem_hash_lock);
- hash_for_each(ctx->mem_hash, i, hnode, node) {
- vm_type = hnode->ptr;
- if (((*vm_type == VM_TYPE_USERPTR) && is_pmmu) ||
- ((*vm_type == VM_TYPE_PHYS_PACK) && !is_pmmu))
- pgf_info->num_of_user_mappings++;
-
- }
-
- if (!pgf_info->num_of_user_mappings)
- goto finish;
-
- /* In case we already allocated in previous session, need to release it before
- * allocating new buffer.
- */
- vfree(pgf_info->user_mappings);
- pgf_info->user_mappings =
- vzalloc(pgf_info->num_of_user_mappings * sizeof(struct hl_user_mapping));
- if (!pgf_info->user_mappings) {
- pgf_info->num_of_user_mappings = 0;
- goto finish;
- }
-
- hash_for_each(ctx->mem_hash, i, hnode, node) {
- vm_type = hnode->ptr;
- if ((*vm_type == VM_TYPE_USERPTR) && (is_pmmu)) {
- userptr = hnode->ptr;
- pgf_info->user_mappings[map_idx].dev_va = hnode->vaddr;
- pgf_info->user_mappings[map_idx].size = userptr->size;
- map_idx++;
- } else if ((*vm_type == VM_TYPE_PHYS_PACK) && (!is_pmmu)) {
- phys_pg_pack = hnode->ptr;
- pgf_info->user_mappings[map_idx].dev_va = hnode->vaddr;
- pgf_info->user_mappings[map_idx].size = phys_pg_pack->total_size;
- map_idx++;
- }
- }
-finish:
- mutex_unlock(&ctx->mem_hash_lock);
- hl_ctx_put(ctx);
-}
-
-void hl_capture_page_fault(struct hl_device *hdev, u64 addr, u16 eng_id, bool is_pmmu)
-{
- /* Capture only the first page fault */
- if (atomic_cmpxchg(&hdev->captured_err_info.pgf_info_recorded, 0, 1))
- return;
-
- hdev->captured_err_info.pgf_info.pgf.timestamp = ktime_to_ns(ktime_get());
- hdev->captured_err_info.pgf_info.pgf.addr = addr;
- hdev->captured_err_info.pgf_info.pgf.engine_id = eng_id;
- hl_capture_user_mappings(hdev, is_pmmu);
-}
-
-void hl_handle_page_fault(struct hl_device *hdev, u64 addr, u16 eng_id, bool is_pmmu,
- u64 *event_mask)
-{
- hl_capture_page_fault(hdev, addr, eng_id, is_pmmu);
-
- if (event_mask)
- *event_mask |= HL_NOTIFIER_EVENT_PAGE_FAULT;
-}
diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c
deleted file mode 100644
index eb000e035026..000000000000
--- a/drivers/misc/habanalabs/common/firmware_if.c
+++ /dev/null
@@ -1,3171 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2022 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include "habanalabs.h"
-#include "../include/common/hl_boot_if.h"
-
-#include <linux/firmware.h>
-#include <linux/crc32.h>
-#include <linux/slab.h>
-#include <linux/ctype.h>
-#include <linux/vmalloc.h>
-
-#include <trace/events/habanalabs.h>
-
-#define FW_FILE_MAX_SIZE 0x1400000 /* maximum size of 20MB */
-
-static char *comms_cmd_str_arr[COMMS_INVLD_LAST] = {
- [COMMS_NOOP] = __stringify(COMMS_NOOP),
- [COMMS_CLR_STS] = __stringify(COMMS_CLR_STS),
- [COMMS_RST_STATE] = __stringify(COMMS_RST_STATE),
- [COMMS_PREP_DESC] = __stringify(COMMS_PREP_DESC),
- [COMMS_DATA_RDY] = __stringify(COMMS_DATA_RDY),
- [COMMS_EXEC] = __stringify(COMMS_EXEC),
- [COMMS_RST_DEV] = __stringify(COMMS_RST_DEV),
- [COMMS_GOTO_WFE] = __stringify(COMMS_GOTO_WFE),
- [COMMS_SKIP_BMC] = __stringify(COMMS_SKIP_BMC),
- [COMMS_PREP_DESC_ELBI] = __stringify(COMMS_PREP_DESC_ELBI),
-};
-
-static char *comms_sts_str_arr[COMMS_STS_INVLD_LAST] = {
- [COMMS_STS_NOOP] = __stringify(COMMS_STS_NOOP),
- [COMMS_STS_ACK] = __stringify(COMMS_STS_ACK),
- [COMMS_STS_OK] = __stringify(COMMS_STS_OK),
- [COMMS_STS_ERR] = __stringify(COMMS_STS_ERR),
- [COMMS_STS_VALID_ERR] = __stringify(COMMS_STS_VALID_ERR),
- [COMMS_STS_TIMEOUT_ERR] = __stringify(COMMS_STS_TIMEOUT_ERR),
-};
-
-static char *extract_fw_ver_from_str(const char *fw_str)
-{
- char *str, *fw_ver, *whitespace;
- u32 ver_offset;
-
- fw_ver = kmalloc(VERSION_MAX_LEN, GFP_KERNEL);
- if (!fw_ver)
- return NULL;
-
- str = strnstr(fw_str, "fw-", VERSION_MAX_LEN);
- if (!str)
- goto free_fw_ver;
-
- /* Skip the fw- part */
- str += 3;
- ver_offset = str - fw_str;
-
- /* Copy until the next whitespace */
- whitespace = strnstr(str, " ", VERSION_MAX_LEN - ver_offset);
- if (!whitespace)
- goto free_fw_ver;
-
- strscpy(fw_ver, str, whitespace - str + 1);
-
- return fw_ver;
-
-free_fw_ver:
- kfree(fw_ver);
- return NULL;
-}
-
-static int extract_fw_sub_versions(struct hl_device *hdev, char *preboot_ver)
-{
- char major[8], minor[8], *first_dot, *second_dot;
- int rc;
-
- first_dot = strnstr(preboot_ver, ".", 10);
- if (first_dot) {
- strscpy(major, preboot_ver, first_dot - preboot_ver + 1);
- rc = kstrtou32(major, 10, &hdev->fw_major_version);
- } else {
- rc = -EINVAL;
- }
-
- if (rc) {
- dev_err(hdev->dev, "Error %d parsing preboot major version\n", rc);
- goto out;
- }
-
- /* skip the first dot */
- first_dot++;
-
- second_dot = strnstr(first_dot, ".", 10);
- if (second_dot) {
- strscpy(minor, first_dot, second_dot - first_dot + 1);
- rc = kstrtou32(minor, 10, &hdev->fw_minor_version);
- } else {
- rc = -EINVAL;
- }
-
- if (rc)
- dev_err(hdev->dev, "Error %d parsing preboot minor version\n", rc);
-
-out:
- kfree(preboot_ver);
- return rc;
-}
-
-static int hl_request_fw(struct hl_device *hdev,
- const struct firmware **firmware_p,
- const char *fw_name)
-{
- size_t fw_size;
- int rc;
-
- rc = request_firmware(firmware_p, fw_name, hdev->dev);
- if (rc) {
- dev_err(hdev->dev, "Firmware file %s is not found! (error %d)\n",
- fw_name, rc);
- goto out;
- }
-
- fw_size = (*firmware_p)->size;
- if ((fw_size % 4) != 0) {
- dev_err(hdev->dev, "Illegal %s firmware size %zu\n",
- fw_name, fw_size);
- rc = -EINVAL;
- goto release_fw;
- }
-
- dev_dbg(hdev->dev, "%s firmware size == %zu\n", fw_name, fw_size);
-
- if (fw_size > FW_FILE_MAX_SIZE) {
- dev_err(hdev->dev,
- "FW file size %zu exceeds maximum of %u bytes\n",
- fw_size, FW_FILE_MAX_SIZE);
- rc = -EINVAL;
- goto release_fw;
- }
-
- return 0;
-
-release_fw:
- release_firmware(*firmware_p);
-out:
- return rc;
-}
-
-/**
- * hl_release_firmware() - release FW
- *
- * @fw: fw descriptor
- *
- * note: this inline function added to serve as a comprehensive mirror for the
- * hl_request_fw function.
- */
-static inline void hl_release_firmware(const struct firmware *fw)
-{
- release_firmware(fw);
-}
-
-/**
- * hl_fw_copy_fw_to_device() - copy FW to device
- *
- * @hdev: pointer to hl_device structure.
- * @fw: fw descriptor
- * @dst: IO memory mapped address space to copy firmware to
- * @src_offset: offset in src FW to copy from
- * @size: amount of bytes to copy (0 to copy the whole binary)
- *
- * actual copy of FW binary data to device, shared by static and dynamic loaders
- */
-static int hl_fw_copy_fw_to_device(struct hl_device *hdev,
- const struct firmware *fw, void __iomem *dst,
- u32 src_offset, u32 size)
-{
- const void *fw_data;
-
- /* size 0 indicates to copy the whole file */
- if (!size)
- size = fw->size;
-
- if (src_offset + size > fw->size) {
- dev_err(hdev->dev,
- "size to copy(%u) and offset(%u) are invalid\n",
- size, src_offset);
- return -EINVAL;
- }
-
- fw_data = (const void *) fw->data;
-
- memcpy_toio(dst, fw_data + src_offset, size);
- return 0;
-}
-
-/**
- * hl_fw_copy_msg_to_device() - copy message to device
- *
- * @hdev: pointer to hl_device structure.
- * @msg: message
- * @dst: IO memory mapped address space to copy firmware to
- * @src_offset: offset in src message to copy from
- * @size: amount of bytes to copy (0 to copy the whole binary)
- *
- * actual copy of message data to device.
- */
-static int hl_fw_copy_msg_to_device(struct hl_device *hdev,
- struct lkd_msg_comms *msg, void __iomem *dst,
- u32 src_offset, u32 size)
-{
- void *msg_data;
-
- /* size 0 indicates to copy the whole file */
- if (!size)
- size = sizeof(struct lkd_msg_comms);
-
- if (src_offset + size > sizeof(struct lkd_msg_comms)) {
- dev_err(hdev->dev,
- "size to copy(%u) and offset(%u) are invalid\n",
- size, src_offset);
- return -EINVAL;
- }
-
- msg_data = (void *) msg;
-
- memcpy_toio(dst, msg_data + src_offset, size);
-
- return 0;
-}
-
-/**
- * hl_fw_load_fw_to_device() - Load F/W code to device's memory.
- *
- * @hdev: pointer to hl_device structure.
- * @fw_name: the firmware image name
- * @dst: IO memory mapped address space to copy firmware to
- * @src_offset: offset in src FW to copy from
- * @size: amount of bytes to copy (0 to copy the whole binary)
- *
- * Copy fw code from firmware file to device memory.
- *
- * Return: 0 on success, non-zero for failure.
- */
-int hl_fw_load_fw_to_device(struct hl_device *hdev, const char *fw_name,
- void __iomem *dst, u32 src_offset, u32 size)
-{
- const struct firmware *fw;
- int rc;
-
- rc = hl_request_fw(hdev, &fw, fw_name);
- if (rc)
- return rc;
-
- rc = hl_fw_copy_fw_to_device(hdev, fw, dst, src_offset, size);
-
- hl_release_firmware(fw);
- return rc;
-}
-
-int hl_fw_send_pci_access_msg(struct hl_device *hdev, u32 opcode, u64 value)
-{
- struct cpucp_packet pkt = {};
-
- pkt.ctl = cpu_to_le32(opcode << CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.value = cpu_to_le64(value);
-
- return hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL);
-}
-
-int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg,
- u16 len, u32 timeout, u64 *result)
-{
- struct hl_hw_queue *queue = &hdev->kernel_queues[hw_queue_id];
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct cpucp_packet *pkt;
- dma_addr_t pkt_dma_addr;
- struct hl_bd *sent_bd;
- u32 tmp, expected_ack_val, pi, opcode;
- int rc;
-
- pkt = hl_cpu_accessible_dma_pool_alloc(hdev, len, &pkt_dma_addr);
- if (!pkt) {
- dev_err(hdev->dev,
- "Failed to allocate DMA memory for packet to CPU\n");
- return -ENOMEM;
- }
-
- memcpy(pkt, msg, len);
-
- mutex_lock(&hdev->send_cpu_message_lock);
-
- /* CPU-CP messages can be sent during soft-reset */
- if (hdev->disabled && !hdev->reset_info.in_compute_reset) {
- rc = 0;
- goto out;
- }
-
- if (hdev->device_cpu_disabled) {
- rc = -EIO;
- goto out;
- }
-
- /* set fence to a non valid value */
- pkt->fence = cpu_to_le32(UINT_MAX);
- pi = queue->pi;
-
- /*
- * The CPU queue is a synchronous queue with an effective depth of
- * a single entry (although it is allocated with room for multiple
- * entries). We lock on it using 'send_cpu_message_lock' which
- * serializes accesses to the CPU queue.
- * Which means that we don't need to lock the access to the entire H/W
- * queues module when submitting a JOB to the CPU queue.
- */
- hl_hw_queue_submit_bd(hdev, queue, hl_queue_inc_ptr(queue->pi), len, pkt_dma_addr);
-
- if (prop->fw_app_cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_PKT_PI_ACK_EN)
- expected_ack_val = queue->pi;
- else
- expected_ack_val = CPUCP_PACKET_FENCE_VAL;
-
- rc = hl_poll_timeout_memory(hdev, &pkt->fence, tmp,
- (tmp == expected_ack_val), 1000,
- timeout, true);
-
- hl_hw_queue_inc_ci_kernel(hdev, hw_queue_id);
-
- if (rc == -ETIMEDOUT) {
- /* If FW performed reset just before sending it a packet, we will get a timeout.
- * This is expected behavior, hence no need for error message.
- */
- if (!hl_device_operational(hdev, NULL) && !hdev->reset_info.in_compute_reset)
- dev_dbg(hdev->dev, "Device CPU packet timeout (0x%x) due to FW reset\n",
- tmp);
- else
- dev_err(hdev->dev, "Device CPU packet timeout (0x%x)\n", tmp);
- hdev->device_cpu_disabled = true;
- goto out;
- }
-
- tmp = le32_to_cpu(pkt->ctl);
-
- rc = (tmp & CPUCP_PKT_CTL_RC_MASK) >> CPUCP_PKT_CTL_RC_SHIFT;
- if (rc) {
- opcode = (tmp & CPUCP_PKT_CTL_OPCODE_MASK) >> CPUCP_PKT_CTL_OPCODE_SHIFT;
-
- if (!prop->supports_advanced_cpucp_rc) {
- dev_dbg(hdev->dev, "F/W ERROR %d for CPU packet %d\n", rc, opcode);
- rc = -EIO;
- goto scrub_descriptor;
- }
-
- switch (rc) {
- case cpucp_packet_invalid:
- dev_err(hdev->dev,
- "CPU packet %d is not supported by F/W\n", opcode);
- break;
- case cpucp_packet_fault:
- dev_err(hdev->dev,
- "F/W failed processing CPU packet %d\n", opcode);
- break;
- case cpucp_packet_invalid_pkt:
- dev_dbg(hdev->dev,
- "CPU packet %d is not supported by F/W\n", opcode);
- break;
- case cpucp_packet_invalid_params:
- dev_err(hdev->dev,
- "F/W reports invalid parameters for CPU packet %d\n", opcode);
- break;
-
- default:
- dev_err(hdev->dev,
- "Unknown F/W ERROR %d for CPU packet %d\n", rc, opcode);
- }
-
- /* propagate the return code from the f/w to the callers who want to check it */
- if (result)
- *result = rc;
-
- rc = -EIO;
-
- } else if (result) {
- *result = le64_to_cpu(pkt->result);
- }
-
-scrub_descriptor:
- /* Scrub previous buffer descriptor 'ctl' field which contains the
- * previous PI value written during packet submission.
- * We must do this or else F/W can read an old value upon queue wraparound.
- */
- sent_bd = queue->kernel_address;
- sent_bd += hl_pi_2_offset(pi);
- sent_bd->ctl = cpu_to_le32(UINT_MAX);
-
-out:
- mutex_unlock(&hdev->send_cpu_message_lock);
-
- hl_cpu_accessible_dma_pool_free(hdev, len, pkt);
-
- return rc;
-}
-
-int hl_fw_unmask_irq(struct hl_device *hdev, u16 event_type)
-{
- struct cpucp_packet pkt;
- u64 result;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_UNMASK_RAZWI_IRQ <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.value = cpu_to_le64(event_type);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, &result);
-
- if (rc)
- dev_err(hdev->dev, "failed to unmask RAZWI IRQ %d", event_type);
-
- return rc;
-}
-
-int hl_fw_unmask_irq_arr(struct hl_device *hdev, const u32 *irq_arr,
- size_t irq_arr_size)
-{
- struct cpucp_unmask_irq_arr_packet *pkt;
- size_t total_pkt_size;
- u64 result;
- int rc;
-
- total_pkt_size = sizeof(struct cpucp_unmask_irq_arr_packet) +
- irq_arr_size;
-
- /* data should be aligned to 8 bytes in order to CPU-CP to copy it */
- total_pkt_size = (total_pkt_size + 0x7) & ~0x7;
-
- /* total_pkt_size is casted to u16 later on */
- if (total_pkt_size > USHRT_MAX) {
- dev_err(hdev->dev, "too many elements in IRQ array\n");
- return -EINVAL;
- }
-
- pkt = kzalloc(total_pkt_size, GFP_KERNEL);
- if (!pkt)
- return -ENOMEM;
-
- pkt->length = cpu_to_le32(irq_arr_size / sizeof(irq_arr[0]));
- memcpy(&pkt->irqs, irq_arr, irq_arr_size);
-
- pkt->cpucp_pkt.ctl = cpu_to_le32(CPUCP_PACKET_UNMASK_RAZWI_IRQ_ARRAY <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) pkt,
- total_pkt_size, 0, &result);
-
- if (rc)
- dev_err(hdev->dev, "failed to unmask IRQ array\n");
-
- kfree(pkt);
-
- return rc;
-}
-
-int hl_fw_test_cpu_queue(struct hl_device *hdev)
-{
- struct cpucp_packet test_pkt = {};
- u64 result;
- int rc;
-
- test_pkt.ctl = cpu_to_le32(CPUCP_PACKET_TEST <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- test_pkt.value = cpu_to_le64(CPUCP_PACKET_FENCE_VAL);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &test_pkt,
- sizeof(test_pkt), 0, &result);
-
- if (!rc) {
- if (result != CPUCP_PACKET_FENCE_VAL)
- dev_err(hdev->dev,
- "CPU queue test failed (%#08llx)\n", result);
- } else {
- dev_err(hdev->dev, "CPU queue test failed, error %d\n", rc);
- }
-
- return rc;
-}
-
-void *hl_fw_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size,
- dma_addr_t *dma_handle)
-{
- u64 kernel_addr;
-
- kernel_addr = gen_pool_alloc(hdev->cpu_accessible_dma_pool, size);
-
- *dma_handle = hdev->cpu_accessible_dma_address +
- (kernel_addr - (u64) (uintptr_t) hdev->cpu_accessible_dma_mem);
-
- return (void *) (uintptr_t) kernel_addr;
-}
-
-void hl_fw_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size,
- void *vaddr)
-{
- gen_pool_free(hdev->cpu_accessible_dma_pool, (u64) (uintptr_t) vaddr,
- size);
-}
-
-int hl_fw_send_device_activity(struct hl_device *hdev, bool open)
-{
- struct cpucp_packet pkt;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_ACTIVE_STATUS_SET << CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.value = cpu_to_le64(open);
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL);
- if (rc)
- dev_err(hdev->dev, "failed to send device activity msg(%u)\n", open);
-
- return rc;
-}
-
-int hl_fw_send_heartbeat(struct hl_device *hdev)
-{
- struct cpucp_packet hb_pkt;
- u64 result;
- int rc;
-
- memset(&hb_pkt, 0, sizeof(hb_pkt));
- hb_pkt.ctl = cpu_to_le32(CPUCP_PACKET_TEST <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- hb_pkt.value = cpu_to_le64(CPUCP_PACKET_FENCE_VAL);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &hb_pkt,
- sizeof(hb_pkt), 0, &result);
-
- if ((rc) || (result != CPUCP_PACKET_FENCE_VAL))
- return -EIO;
-
- if (le32_to_cpu(hb_pkt.status_mask) &
- CPUCP_PKT_HB_STATUS_EQ_FAULT_MASK) {
- dev_warn(hdev->dev, "FW reported EQ fault during heartbeat\n");
- rc = -EIO;
- }
-
- return rc;
-}
-
-static bool fw_report_boot_dev0(struct hl_device *hdev, u32 err_val,
- u32 sts_val)
-{
- bool err_exists = false;
-
- if (!(err_val & CPU_BOOT_ERR0_ENABLED))
- return false;
-
- if (err_val & CPU_BOOT_ERR0_DRAM_INIT_FAIL) {
- dev_err(hdev->dev,
- "Device boot error - DRAM initialization failed\n");
- err_exists = true;
- }
-
- if (err_val & CPU_BOOT_ERR0_FIT_CORRUPTED) {
- dev_err(hdev->dev, "Device boot error - FIT image corrupted\n");
- err_exists = true;
- }
-
- if (err_val & CPU_BOOT_ERR0_TS_INIT_FAIL) {
- dev_err(hdev->dev,
- "Device boot error - Thermal Sensor initialization failed\n");
- err_exists = true;
- }
-
- if (err_val & CPU_BOOT_ERR0_BMC_WAIT_SKIPPED) {
- if (hdev->bmc_enable) {
- dev_err(hdev->dev,
- "Device boot error - Skipped waiting for BMC\n");
- err_exists = true;
- } else {
- dev_info(hdev->dev,
- "Device boot message - Skipped waiting for BMC\n");
- /* This is an info so we don't want it to disable the
- * device
- */
- err_val &= ~CPU_BOOT_ERR0_BMC_WAIT_SKIPPED;
- }
- }
-
- if (err_val & CPU_BOOT_ERR0_NIC_DATA_NOT_RDY) {
- dev_err(hdev->dev,
- "Device boot error - Serdes data from BMC not available\n");
- err_exists = true;
- }
-
- if (err_val & CPU_BOOT_ERR0_NIC_FW_FAIL) {
- dev_err(hdev->dev,
- "Device boot error - NIC F/W initialization failed\n");
- err_exists = true;
- }
-
- if (err_val & CPU_BOOT_ERR0_SECURITY_NOT_RDY) {
- dev_err(hdev->dev,
- "Device boot warning - security not ready\n");
- err_exists = true;
- }
-
- if (err_val & CPU_BOOT_ERR0_SECURITY_FAIL) {
- dev_err(hdev->dev, "Device boot error - security failure\n");
- err_exists = true;
- }
-
- if (err_val & CPU_BOOT_ERR0_EFUSE_FAIL) {
- dev_err(hdev->dev, "Device boot error - eFuse failure\n");
- err_exists = true;
- }
-
- if (err_val & CPU_BOOT_ERR0_SEC_IMG_VER_FAIL) {
- dev_err(hdev->dev, "Device boot error - Failed to load preboot secondary image\n");
- err_exists = true;
- }
-
- if (err_val & CPU_BOOT_ERR0_PLL_FAIL) {
- dev_err(hdev->dev, "Device boot error - PLL failure\n");
- err_exists = true;
- }
-
- if (err_val & CPU_BOOT_ERR0_DEVICE_UNUSABLE_FAIL) {
- /* Ignore this bit, don't prevent driver loading */
- dev_dbg(hdev->dev, "device unusable status is set\n");
- err_val &= ~CPU_BOOT_ERR0_DEVICE_UNUSABLE_FAIL;
- }
-
- if (err_val & CPU_BOOT_ERR0_BINNING_FAIL) {
- dev_err(hdev->dev, "Device boot error - binning failure\n");
- err_exists = true;
- }
-
- if (sts_val & CPU_BOOT_DEV_STS0_ENABLED)
- dev_dbg(hdev->dev, "Device status0 %#x\n", sts_val);
-
- if (err_val & CPU_BOOT_ERR0_EEPROM_FAIL) {
- dev_err(hdev->dev, "Device boot error - EEPROM failure detected\n");
- err_exists = true;
- }
-
- /* All warnings should go here in order not to reach the unknown error validation */
- if (err_val & CPU_BOOT_ERR0_DRAM_SKIPPED) {
- dev_warn(hdev->dev,
- "Device boot warning - Skipped DRAM initialization\n");
- /* This is a warning so we don't want it to disable the
- * device
- */
- err_val &= ~CPU_BOOT_ERR0_DRAM_SKIPPED;
- }
-
- if (err_val & CPU_BOOT_ERR0_PRI_IMG_VER_FAIL) {
- dev_warn(hdev->dev,
- "Device boot warning - Failed to load preboot primary image\n");
- /* This is a warning so we don't want it to disable the
- * device as we have a secondary preboot image
- */
- err_val &= ~CPU_BOOT_ERR0_PRI_IMG_VER_FAIL;
- }
-
- if (err_val & CPU_BOOT_ERR0_TPM_FAIL) {
- dev_warn(hdev->dev,
- "Device boot warning - TPM failure\n");
- /* This is a warning so we don't want it to disable the
- * device
- */
- err_val &= ~CPU_BOOT_ERR0_TPM_FAIL;
- }
-
- if (!err_exists && (err_val & ~CPU_BOOT_ERR0_ENABLED)) {
- dev_err(hdev->dev,
- "Device boot error - unknown ERR0 error 0x%08x\n", err_val);
- err_exists = true;
- }
-
- /* return error only if it's in the predefined mask */
- if (err_exists && ((err_val & ~CPU_BOOT_ERR0_ENABLED) &
- lower_32_bits(hdev->boot_error_status_mask)))
- return true;
-
- return false;
-}
-
-/* placeholder for ERR1 as no errors defined there yet */
-static bool fw_report_boot_dev1(struct hl_device *hdev, u32 err_val,
- u32 sts_val)
-{
- /*
- * keep this variable to preserve the logic of the function.
- * this way it would require less modifications when error will be
- * added to DEV_ERR1
- */
- bool err_exists = false;
-
- if (!(err_val & CPU_BOOT_ERR1_ENABLED))
- return false;
-
- if (sts_val & CPU_BOOT_DEV_STS1_ENABLED)
- dev_dbg(hdev->dev, "Device status1 %#x\n", sts_val);
-
- if (!err_exists && (err_val & ~CPU_BOOT_ERR1_ENABLED)) {
- dev_err(hdev->dev,
- "Device boot error - unknown ERR1 error 0x%08x\n",
- err_val);
- err_exists = true;
- }
-
- /* return error only if it's in the predefined mask */
- if (err_exists && ((err_val & ~CPU_BOOT_ERR1_ENABLED) &
- upper_32_bits(hdev->boot_error_status_mask)))
- return true;
-
- return false;
-}
-
-static int fw_read_errors(struct hl_device *hdev, u32 boot_err0_reg,
- u32 boot_err1_reg, u32 cpu_boot_dev_status0_reg,
- u32 cpu_boot_dev_status1_reg)
-{
- u32 err_val, status_val;
- bool err_exists = false;
-
- /* Some of the firmware status codes are deprecated in newer f/w
- * versions. In those versions, the errors are reported
- * in different registers. Therefore, we need to check those
- * registers and print the exact errors. Moreover, there
- * may be multiple errors, so we need to report on each error
- * separately. Some of the error codes might indicate a state
- * that is not an error per-se, but it is an error in production
- * environment
- */
- err_val = RREG32(boot_err0_reg);
- status_val = RREG32(cpu_boot_dev_status0_reg);
- err_exists = fw_report_boot_dev0(hdev, err_val, status_val);
-
- err_val = RREG32(boot_err1_reg);
- status_val = RREG32(cpu_boot_dev_status1_reg);
- err_exists |= fw_report_boot_dev1(hdev, err_val, status_val);
-
- if (err_exists)
- return -EIO;
-
- return 0;
-}
-
-int hl_fw_cpucp_info_get(struct hl_device *hdev,
- u32 sts_boot_dev_sts0_reg,
- u32 sts_boot_dev_sts1_reg, u32 boot_err0_reg,
- u32 boot_err1_reg)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct cpucp_packet pkt = {};
- dma_addr_t cpucp_info_dma_addr;
- void *cpucp_info_cpu_addr;
- char *kernel_ver;
- u64 result;
- int rc;
-
- cpucp_info_cpu_addr = hl_cpu_accessible_dma_pool_alloc(hdev, sizeof(struct cpucp_info),
- &cpucp_info_dma_addr);
- if (!cpucp_info_cpu_addr) {
- dev_err(hdev->dev,
- "Failed to allocate DMA memory for CPU-CP info packet\n");
- return -ENOMEM;
- }
-
- memset(cpucp_info_cpu_addr, 0, sizeof(struct cpucp_info));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_INFO_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.addr = cpu_to_le64(cpucp_info_dma_addr);
- pkt.data_max_size = cpu_to_le32(sizeof(struct cpucp_info));
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- HL_CPUCP_INFO_TIMEOUT_USEC, &result);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to handle CPU-CP info pkt, error %d\n", rc);
- goto out;
- }
-
- rc = fw_read_errors(hdev, boot_err0_reg, boot_err1_reg,
- sts_boot_dev_sts0_reg, sts_boot_dev_sts1_reg);
- if (rc) {
- dev_err(hdev->dev, "Errors in device boot\n");
- goto out;
- }
-
- memcpy(&prop->cpucp_info, cpucp_info_cpu_addr,
- sizeof(prop->cpucp_info));
-
- rc = hl_build_hwmon_channel_info(hdev, prop->cpucp_info.sensors);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to build hwmon channel info, error %d\n", rc);
- rc = -EFAULT;
- goto out;
- }
-
- kernel_ver = extract_fw_ver_from_str(prop->cpucp_info.kernel_version);
- if (kernel_ver) {
- dev_info(hdev->dev, "Linux version %s", kernel_ver);
- kfree(kernel_ver);
- }
-
- /* assume EQ code doesn't need to check eqe index */
- hdev->event_queue.check_eqe_index = false;
-
- /* Read FW application security bits again */
- if (prop->fw_cpu_boot_dev_sts0_valid) {
- prop->fw_app_cpu_boot_dev_sts0 = RREG32(sts_boot_dev_sts0_reg);
- if (prop->fw_app_cpu_boot_dev_sts0 &
- CPU_BOOT_DEV_STS0_EQ_INDEX_EN)
- hdev->event_queue.check_eqe_index = true;
- }
-
- if (prop->fw_cpu_boot_dev_sts1_valid)
- prop->fw_app_cpu_boot_dev_sts1 = RREG32(sts_boot_dev_sts1_reg);
-
-out:
- hl_cpu_accessible_dma_pool_free(hdev, sizeof(struct cpucp_info), cpucp_info_cpu_addr);
-
- return rc;
-}
-
-static int hl_fw_send_msi_info_msg(struct hl_device *hdev)
-{
- struct cpucp_array_data_packet *pkt;
- size_t total_pkt_size, data_size;
- u64 result;
- int rc;
-
- /* skip sending this info for unsupported ASICs */
- if (!hdev->asic_funcs->get_msi_info)
- return 0;
-
- data_size = CPUCP_NUM_OF_MSI_TYPES * sizeof(u32);
- total_pkt_size = sizeof(struct cpucp_array_data_packet) + data_size;
-
- /* data should be aligned to 8 bytes in order to CPU-CP to copy it */
- total_pkt_size = (total_pkt_size + 0x7) & ~0x7;
-
- /* total_pkt_size is casted to u16 later on */
- if (total_pkt_size > USHRT_MAX) {
- dev_err(hdev->dev, "CPUCP array data is too big\n");
- return -EINVAL;
- }
-
- pkt = kzalloc(total_pkt_size, GFP_KERNEL);
- if (!pkt)
- return -ENOMEM;
-
- pkt->length = cpu_to_le32(CPUCP_NUM_OF_MSI_TYPES);
-
- memset((void *) &pkt->data, 0xFF, data_size);
- hdev->asic_funcs->get_msi_info(pkt->data);
-
- pkt->cpucp_pkt.ctl = cpu_to_le32(CPUCP_PACKET_MSI_INFO_SET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *)pkt,
- total_pkt_size, 0, &result);
-
- /*
- * in case packet result is invalid it means that FW does not support
- * this feature and will use default/hard coded MSI values. no reason
- * to stop the boot
- */
- if (rc && result == cpucp_packet_invalid)
- rc = 0;
-
- if (rc)
- dev_err(hdev->dev, "failed to send CPUCP array data\n");
-
- kfree(pkt);
-
- return rc;
-}
-
-int hl_fw_cpucp_handshake(struct hl_device *hdev,
- u32 sts_boot_dev_sts0_reg,
- u32 sts_boot_dev_sts1_reg, u32 boot_err0_reg,
- u32 boot_err1_reg)
-{
- int rc;
-
- rc = hl_fw_cpucp_info_get(hdev, sts_boot_dev_sts0_reg,
- sts_boot_dev_sts1_reg, boot_err0_reg,
- boot_err1_reg);
- if (rc)
- return rc;
-
- return hl_fw_send_msi_info_msg(hdev);
-}
-
-int hl_fw_get_eeprom_data(struct hl_device *hdev, void *data, size_t max_size)
-{
- struct cpucp_packet pkt = {};
- void *eeprom_info_cpu_addr;
- dma_addr_t eeprom_info_dma_addr;
- u64 result;
- int rc;
-
- eeprom_info_cpu_addr = hl_cpu_accessible_dma_pool_alloc(hdev, max_size,
- &eeprom_info_dma_addr);
- if (!eeprom_info_cpu_addr) {
- dev_err(hdev->dev,
- "Failed to allocate DMA memory for CPU-CP EEPROM packet\n");
- return -ENOMEM;
- }
-
- memset(eeprom_info_cpu_addr, 0, max_size);
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_EEPROM_DATA_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.addr = cpu_to_le64(eeprom_info_dma_addr);
- pkt.data_max_size = cpu_to_le32(max_size);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- HL_CPUCP_EEPROM_TIMEOUT_USEC, &result);
-
- if (rc) {
- dev_err(hdev->dev,
- "Failed to handle CPU-CP EEPROM packet, error %d\n",
- rc);
- goto out;
- }
-
- /* result contains the actual size */
- memcpy(data, eeprom_info_cpu_addr, min((size_t)result, max_size));
-
-out:
- hl_cpu_accessible_dma_pool_free(hdev, max_size, eeprom_info_cpu_addr);
-
- return rc;
-}
-
-int hl_fw_get_monitor_dump(struct hl_device *hdev, void *data)
-{
- struct cpucp_monitor_dump *mon_dump_cpu_addr;
- dma_addr_t mon_dump_dma_addr;
- struct cpucp_packet pkt = {};
- size_t data_size;
- __le32 *src_ptr;
- u32 *dst_ptr;
- u64 result;
- int i, rc;
-
- data_size = sizeof(struct cpucp_monitor_dump);
- mon_dump_cpu_addr = hl_cpu_accessible_dma_pool_alloc(hdev, data_size, &mon_dump_dma_addr);
- if (!mon_dump_cpu_addr) {
- dev_err(hdev->dev,
- "Failed to allocate DMA memory for CPU-CP monitor-dump packet\n");
- return -ENOMEM;
- }
-
- memset(mon_dump_cpu_addr, 0, data_size);
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_MONITOR_DUMP_GET << CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.addr = cpu_to_le64(mon_dump_dma_addr);
- pkt.data_max_size = cpu_to_le32(data_size);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- HL_CPUCP_MON_DUMP_TIMEOUT_USEC, &result);
- if (rc) {
- dev_err(hdev->dev, "Failed to handle CPU-CP monitor-dump packet, error %d\n", rc);
- goto out;
- }
-
- /* result contains the actual size */
- src_ptr = (__le32 *) mon_dump_cpu_addr;
- dst_ptr = data;
- for (i = 0; i < (data_size / sizeof(u32)); i++) {
- *dst_ptr = le32_to_cpu(*src_ptr);
- src_ptr++;
- dst_ptr++;
- }
-
-out:
- hl_cpu_accessible_dma_pool_free(hdev, data_size, mon_dump_cpu_addr);
-
- return rc;
-}
-
-int hl_fw_cpucp_pci_counters_get(struct hl_device *hdev,
- struct hl_info_pci_counters *counters)
-{
- struct cpucp_packet pkt = {};
- u64 result;
- int rc;
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_PCIE_THROUGHPUT_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
-
- /* Fetch PCI rx counter */
- pkt.index = cpu_to_le32(cpucp_pcie_throughput_rx);
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- HL_CPUCP_INFO_TIMEOUT_USEC, &result);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to handle CPU-CP PCI info pkt, error %d\n", rc);
- return rc;
- }
- counters->rx_throughput = result;
-
- memset(&pkt, 0, sizeof(pkt));
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_PCIE_THROUGHPUT_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
-
- /* Fetch PCI tx counter */
- pkt.index = cpu_to_le32(cpucp_pcie_throughput_tx);
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- HL_CPUCP_INFO_TIMEOUT_USEC, &result);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to handle CPU-CP PCI info pkt, error %d\n", rc);
- return rc;
- }
- counters->tx_throughput = result;
-
- /* Fetch PCI replay counter */
- memset(&pkt, 0, sizeof(pkt));
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_PCIE_REPLAY_CNT_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- HL_CPUCP_INFO_TIMEOUT_USEC, &result);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to handle CPU-CP PCI info pkt, error %d\n", rc);
- return rc;
- }
- counters->replay_cnt = (u32) result;
-
- return rc;
-}
-
-int hl_fw_cpucp_total_energy_get(struct hl_device *hdev, u64 *total_energy)
-{
- struct cpucp_packet pkt = {};
- u64 result;
- int rc;
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_TOTAL_ENERGY_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- HL_CPUCP_INFO_TIMEOUT_USEC, &result);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to handle CpuCP total energy pkt, error %d\n",
- rc);
- return rc;
- }
-
- *total_energy = result;
-
- return rc;
-}
-
-int get_used_pll_index(struct hl_device *hdev, u32 input_pll_index,
- enum pll_index *pll_index)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u8 pll_byte, pll_bit_off;
- bool dynamic_pll;
- int fw_pll_idx;
-
- dynamic_pll = !!(prop->fw_app_cpu_boot_dev_sts0 &
- CPU_BOOT_DEV_STS0_DYN_PLL_EN);
-
- if (!dynamic_pll) {
- /*
- * in case we are working with legacy FW (each asic has unique
- * PLL numbering) use the driver based index as they are
- * aligned with fw legacy numbering
- */
- *pll_index = input_pll_index;
- return 0;
- }
-
- /* retrieve a FW compatible PLL index based on
- * ASIC specific user request
- */
- fw_pll_idx = hdev->asic_funcs->map_pll_idx_to_fw_idx(input_pll_index);
- if (fw_pll_idx < 0) {
- dev_err(hdev->dev, "Invalid PLL index (%u) error %d\n",
- input_pll_index, fw_pll_idx);
- return -EINVAL;
- }
-
- /* PLL map is a u8 array */
- pll_byte = prop->cpucp_info.pll_map[fw_pll_idx >> 3];
- pll_bit_off = fw_pll_idx & 0x7;
-
- if (!(pll_byte & BIT(pll_bit_off))) {
- dev_err(hdev->dev, "PLL index %d is not supported\n",
- fw_pll_idx);
- return -EINVAL;
- }
-
- *pll_index = fw_pll_idx;
-
- return 0;
-}
-
-int hl_fw_cpucp_pll_info_get(struct hl_device *hdev, u32 pll_index,
- u16 *pll_freq_arr)
-{
- struct cpucp_packet pkt;
- enum pll_index used_pll_idx;
- u64 result;
- int rc;
-
- rc = get_used_pll_index(hdev, pll_index, &used_pll_idx);
- if (rc)
- return rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_PLL_INFO_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.pll_type = __cpu_to_le16((u16)used_pll_idx);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- HL_CPUCP_INFO_TIMEOUT_USEC, &result);
- if (rc) {
- dev_err(hdev->dev, "Failed to read PLL info, error %d\n", rc);
- return rc;
- }
-
- pll_freq_arr[0] = FIELD_GET(CPUCP_PKT_RES_PLL_OUT0_MASK, result);
- pll_freq_arr[1] = FIELD_GET(CPUCP_PKT_RES_PLL_OUT1_MASK, result);
- pll_freq_arr[2] = FIELD_GET(CPUCP_PKT_RES_PLL_OUT2_MASK, result);
- pll_freq_arr[3] = FIELD_GET(CPUCP_PKT_RES_PLL_OUT3_MASK, result);
-
- return 0;
-}
-
-int hl_fw_cpucp_power_get(struct hl_device *hdev, u64 *power)
-{
- struct cpucp_packet pkt;
- u64 result;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_POWER_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.type = cpu_to_le16(CPUCP_POWER_INPUT);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- HL_CPUCP_INFO_TIMEOUT_USEC, &result);
- if (rc) {
- dev_err(hdev->dev, "Failed to read power, error %d\n", rc);
- return rc;
- }
-
- *power = result;
-
- return rc;
-}
-
-int hl_fw_dram_replaced_row_get(struct hl_device *hdev,
- struct cpucp_hbm_row_info *info)
-{
- struct cpucp_hbm_row_info *cpucp_repl_rows_info_cpu_addr;
- dma_addr_t cpucp_repl_rows_info_dma_addr;
- struct cpucp_packet pkt = {};
- u64 result;
- int rc;
-
- cpucp_repl_rows_info_cpu_addr = hl_cpu_accessible_dma_pool_alloc(hdev,
- sizeof(struct cpucp_hbm_row_info),
- &cpucp_repl_rows_info_dma_addr);
- if (!cpucp_repl_rows_info_cpu_addr) {
- dev_err(hdev->dev,
- "Failed to allocate DMA memory for CPU-CP replaced rows info packet\n");
- return -ENOMEM;
- }
-
- memset(cpucp_repl_rows_info_cpu_addr, 0, sizeof(struct cpucp_hbm_row_info));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_HBM_REPLACED_ROWS_INFO_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.addr = cpu_to_le64(cpucp_repl_rows_info_dma_addr);
- pkt.data_max_size = cpu_to_le32(sizeof(struct cpucp_hbm_row_info));
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- HL_CPUCP_INFO_TIMEOUT_USEC, &result);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to handle CPU-CP replaced rows info pkt, error %d\n", rc);
- goto out;
- }
-
- memcpy(info, cpucp_repl_rows_info_cpu_addr, sizeof(*info));
-
-out:
- hl_cpu_accessible_dma_pool_free(hdev, sizeof(struct cpucp_hbm_row_info),
- cpucp_repl_rows_info_cpu_addr);
-
- return rc;
-}
-
-int hl_fw_dram_pending_row_get(struct hl_device *hdev, u32 *pend_rows_num)
-{
- struct cpucp_packet pkt;
- u64 result;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_HBM_PENDING_ROWS_STATUS << CPUCP_PKT_CTL_OPCODE_SHIFT);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, &result);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to handle CPU-CP pending rows info pkt, error %d\n", rc);
- goto out;
- }
-
- *pend_rows_num = (u32) result;
-out:
- return rc;
-}
-
-int hl_fw_cpucp_engine_core_asid_set(struct hl_device *hdev, u32 asid)
-{
- struct cpucp_packet pkt;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_ENGINE_CORE_ASID_SET << CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.value = cpu_to_le64(asid);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- HL_CPUCP_INFO_TIMEOUT_USEC, NULL);
- if (rc)
- dev_err(hdev->dev,
- "Failed on ASID configuration request for engine core, error %d\n",
- rc);
-
- return rc;
-}
-
-void hl_fw_ask_hard_reset_without_linux(struct hl_device *hdev)
-{
- struct static_fw_load_mgr *static_loader =
- &hdev->fw_loader.static_loader;
- int rc;
-
- if (hdev->asic_prop.dynamic_fw_load) {
- rc = hl_fw_dynamic_send_protocol_cmd(hdev, &hdev->fw_loader,
- COMMS_RST_DEV, 0, false,
- hdev->fw_loader.cpu_timeout);
- if (rc)
- dev_warn(hdev->dev, "Failed sending COMMS_RST_DEV\n");
- } else {
- WREG32(static_loader->kmd_msg_to_cpu_reg, KMD_MSG_RST_DEV);
- }
-}
-
-void hl_fw_ask_halt_machine_without_linux(struct hl_device *hdev)
-{
- struct static_fw_load_mgr *static_loader =
- &hdev->fw_loader.static_loader;
- int rc;
-
- if (hdev->device_cpu_is_halted)
- return;
-
- /* Stop device CPU to make sure nothing bad happens */
- if (hdev->asic_prop.dynamic_fw_load) {
- rc = hl_fw_dynamic_send_protocol_cmd(hdev, &hdev->fw_loader,
- COMMS_GOTO_WFE, 0, true,
- hdev->fw_loader.cpu_timeout);
- if (rc)
- dev_warn(hdev->dev, "Failed sending COMMS_GOTO_WFE\n");
- } else {
- WREG32(static_loader->kmd_msg_to_cpu_reg, KMD_MSG_GOTO_WFE);
- msleep(static_loader->cpu_reset_wait_msec);
-
- /* Must clear this register in order to prevent preboot
- * from reading WFE after reboot
- */
- WREG32(static_loader->kmd_msg_to_cpu_reg, KMD_MSG_NA);
- }
-
- hdev->device_cpu_is_halted = true;
-}
-
-static void detect_cpu_boot_status(struct hl_device *hdev, u32 status)
-{
- /* Some of the status codes below are deprecated in newer f/w
- * versions but we keep them here for backward compatibility
- */
- switch (status) {
- case CPU_BOOT_STATUS_NA:
- dev_err(hdev->dev,
- "Device boot progress - BTL/ROM did NOT run\n");
- break;
- case CPU_BOOT_STATUS_IN_WFE:
- dev_err(hdev->dev,
- "Device boot progress - Stuck inside WFE loop\n");
- break;
- case CPU_BOOT_STATUS_IN_BTL:
- dev_err(hdev->dev,
- "Device boot progress - Stuck in BTL\n");
- break;
- case CPU_BOOT_STATUS_IN_PREBOOT:
- dev_err(hdev->dev,
- "Device boot progress - Stuck in Preboot\n");
- break;
- case CPU_BOOT_STATUS_IN_SPL:
- dev_err(hdev->dev,
- "Device boot progress - Stuck in SPL\n");
- break;
- case CPU_BOOT_STATUS_IN_UBOOT:
- dev_err(hdev->dev,
- "Device boot progress - Stuck in u-boot\n");
- break;
- case CPU_BOOT_STATUS_DRAM_INIT_FAIL:
- dev_err(hdev->dev,
- "Device boot progress - DRAM initialization failed\n");
- break;
- case CPU_BOOT_STATUS_UBOOT_NOT_READY:
- dev_err(hdev->dev,
- "Device boot progress - Cannot boot\n");
- break;
- case CPU_BOOT_STATUS_TS_INIT_FAIL:
- dev_err(hdev->dev,
- "Device boot progress - Thermal Sensor initialization failed\n");
- break;
- case CPU_BOOT_STATUS_SECURITY_READY:
- dev_err(hdev->dev,
- "Device boot progress - Stuck in preboot after security initialization\n");
- break;
- default:
- dev_err(hdev->dev,
- "Device boot progress - Invalid status code %d\n",
- status);
- break;
- }
-}
-
-int hl_fw_wait_preboot_ready(struct hl_device *hdev)
-{
- struct pre_fw_load_props *pre_fw_load = &hdev->fw_loader.pre_fw_load;
- u32 status;
- int rc;
-
- /* Need to check two possible scenarios:
- *
- * CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT - for newer firmwares where
- * the preboot is waiting for the boot fit
- *
- * All other status values - for older firmwares where the uboot was
- * loaded from the FLASH
- */
- rc = hl_poll_timeout(
- hdev,
- pre_fw_load->cpu_boot_status_reg,
- status,
- (status == CPU_BOOT_STATUS_NIC_FW_RDY) ||
- (status == CPU_BOOT_STATUS_READY_TO_BOOT) ||
- (status == CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT),
- hdev->fw_poll_interval_usec,
- pre_fw_load->wait_for_preboot_timeout);
-
- if (rc) {
- dev_err(hdev->dev, "CPU boot ready status timeout\n");
- detect_cpu_boot_status(hdev, status);
-
- /* If we read all FF, then something is totally wrong, no point
- * of reading specific errors
- */
- if (status != -1)
- fw_read_errors(hdev, pre_fw_load->boot_err0_reg,
- pre_fw_load->boot_err1_reg,
- pre_fw_load->sts_boot_dev_sts0_reg,
- pre_fw_load->sts_boot_dev_sts1_reg);
- return -EIO;
- }
-
- hdev->fw_loader.fw_comp_loaded |= FW_TYPE_PREBOOT_CPU;
-
- return 0;
-}
-
-static int hl_fw_read_preboot_caps(struct hl_device *hdev)
-{
- struct pre_fw_load_props *pre_fw_load;
- struct asic_fixed_properties *prop;
- u32 reg_val;
- int rc;
-
- prop = &hdev->asic_prop;
- pre_fw_load = &hdev->fw_loader.pre_fw_load;
-
- rc = hl_fw_wait_preboot_ready(hdev);
- if (rc)
- return rc;
-
- /*
- * the registers DEV_STS* contain FW capabilities/features.
- * We can rely on this registers only if bit CPU_BOOT_DEV_STS*_ENABLED
- * is set.
- * In the first read of this register we store the value of this
- * register ONLY if the register is enabled (which will be propagated
- * to next stages) and also mark the register as valid.
- * In case it is not enabled the stored value will be left 0- all
- * caps/features are off
- */
- reg_val = RREG32(pre_fw_load->sts_boot_dev_sts0_reg);
- if (reg_val & CPU_BOOT_DEV_STS0_ENABLED) {
- prop->fw_cpu_boot_dev_sts0_valid = true;
- prop->fw_preboot_cpu_boot_dev_sts0 = reg_val;
- }
-
- reg_val = RREG32(pre_fw_load->sts_boot_dev_sts1_reg);
- if (reg_val & CPU_BOOT_DEV_STS1_ENABLED) {
- prop->fw_cpu_boot_dev_sts1_valid = true;
- prop->fw_preboot_cpu_boot_dev_sts1 = reg_val;
- }
-
- prop->dynamic_fw_load = !!(prop->fw_preboot_cpu_boot_dev_sts0 &
- CPU_BOOT_DEV_STS0_FW_LD_COM_EN);
-
- /* initialize FW loader once we know what load protocol is used */
- hdev->asic_funcs->init_firmware_loader(hdev);
-
- dev_dbg(hdev->dev, "Attempting %s FW load\n",
- prop->dynamic_fw_load ? "dynamic" : "legacy");
- return 0;
-}
-
-static int hl_fw_static_read_device_fw_version(struct hl_device *hdev,
- enum hl_fw_component fwc)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct fw_load_mgr *fw_loader = &hdev->fw_loader;
- struct static_fw_load_mgr *static_loader;
- char *dest, *boot_ver, *preboot_ver;
- u32 ver_off, limit;
- const char *name;
- char btl_ver[32];
-
- static_loader = &hdev->fw_loader.static_loader;
-
- switch (fwc) {
- case FW_COMP_BOOT_FIT:
- ver_off = RREG32(static_loader->boot_fit_version_offset_reg);
- dest = prop->uboot_ver;
- name = "Boot-fit";
- limit = static_loader->boot_fit_version_max_off;
- break;
- case FW_COMP_PREBOOT:
- ver_off = RREG32(static_loader->preboot_version_offset_reg);
- dest = prop->preboot_ver;
- name = "Preboot";
- limit = static_loader->preboot_version_max_off;
- break;
- default:
- dev_warn(hdev->dev, "Undefined FW component: %d\n", fwc);
- return -EIO;
- }
-
- ver_off &= static_loader->sram_offset_mask;
-
- if (ver_off < limit) {
- memcpy_fromio(dest,
- hdev->pcie_bar[fw_loader->sram_bar_id] + ver_off,
- VERSION_MAX_LEN);
- } else {
- dev_err(hdev->dev, "%s version offset (0x%x) is above SRAM\n",
- name, ver_off);
- strscpy(dest, "unavailable", VERSION_MAX_LEN);
- return -EIO;
- }
-
- if (fwc == FW_COMP_BOOT_FIT) {
- boot_ver = extract_fw_ver_from_str(prop->uboot_ver);
- if (boot_ver) {
- dev_info(hdev->dev, "boot-fit version %s\n", boot_ver);
- kfree(boot_ver);
- }
- } else if (fwc == FW_COMP_PREBOOT) {
- preboot_ver = strnstr(prop->preboot_ver, "Preboot",
- VERSION_MAX_LEN);
- if (preboot_ver && preboot_ver != prop->preboot_ver) {
- strscpy(btl_ver, prop->preboot_ver,
- min((int) (preboot_ver - prop->preboot_ver),
- 31));
- dev_info(hdev->dev, "%s\n", btl_ver);
- }
-
- preboot_ver = extract_fw_ver_from_str(prop->preboot_ver);
- if (preboot_ver) {
- dev_info(hdev->dev, "preboot version %s\n",
- preboot_ver);
- kfree(preboot_ver);
- }
- }
-
- return 0;
-}
-
-/**
- * hl_fw_preboot_update_state - update internal data structures during
- * handshake with preboot
- *
- *
- * @hdev: pointer to the habanalabs device structure
- *
- * @return 0 on success, otherwise non-zero error code
- */
-static void hl_fw_preboot_update_state(struct hl_device *hdev)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u32 cpu_boot_dev_sts0, cpu_boot_dev_sts1;
-
- cpu_boot_dev_sts0 = prop->fw_preboot_cpu_boot_dev_sts0;
- cpu_boot_dev_sts1 = prop->fw_preboot_cpu_boot_dev_sts1;
-
- /* We read boot_dev_sts registers multiple times during boot:
- * 1. preboot - a. Check whether the security status bits are valid
- * b. Check whether fw security is enabled
- * c. Check whether hard reset is done by preboot
- * 2. boot cpu - a. Fetch boot cpu security status
- * b. Check whether hard reset is done by boot cpu
- * 3. FW application - a. Fetch fw application security status
- * b. Check whether hard reset is done by fw app
- */
- prop->hard_reset_done_by_fw = !!(cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_FW_HARD_RST_EN);
-
- prop->fw_security_enabled = !!(cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_SECURITY_EN);
-
- dev_dbg(hdev->dev, "Firmware preboot boot device status0 %#x\n",
- cpu_boot_dev_sts0);
-
- dev_dbg(hdev->dev, "Firmware preboot boot device status1 %#x\n",
- cpu_boot_dev_sts1);
-
- dev_dbg(hdev->dev, "Firmware preboot hard-reset is %s\n",
- prop->hard_reset_done_by_fw ? "enabled" : "disabled");
-
- dev_dbg(hdev->dev, "firmware-level security is %s\n",
- prop->fw_security_enabled ? "enabled" : "disabled");
-
- dev_dbg(hdev->dev, "GIC controller is %s\n",
- prop->gic_interrupts_enable ? "enabled" : "disabled");
-}
-
-static int hl_fw_static_read_preboot_status(struct hl_device *hdev)
-{
- int rc;
-
- rc = hl_fw_static_read_device_fw_version(hdev, FW_COMP_PREBOOT);
- if (rc)
- return rc;
-
- return 0;
-}
-
-int hl_fw_read_preboot_status(struct hl_device *hdev)
-{
- int rc;
-
- if (!(hdev->fw_components & FW_TYPE_PREBOOT_CPU))
- return 0;
-
- /* get FW pre-load parameters */
- hdev->asic_funcs->init_firmware_preload_params(hdev);
-
- /*
- * In order to determine boot method (static VS dynamic) we need to
- * read the boot caps register
- */
- rc = hl_fw_read_preboot_caps(hdev);
- if (rc)
- return rc;
-
- hl_fw_preboot_update_state(hdev);
-
- /* no need to read preboot status in dynamic load */
- if (hdev->asic_prop.dynamic_fw_load)
- return 0;
-
- return hl_fw_static_read_preboot_status(hdev);
-}
-
-/* associate string with COMM status */
-static char *hl_dynamic_fw_status_str[COMMS_STS_INVLD_LAST] = {
- [COMMS_STS_NOOP] = "NOOP",
- [COMMS_STS_ACK] = "ACK",
- [COMMS_STS_OK] = "OK",
- [COMMS_STS_ERR] = "ERR",
- [COMMS_STS_VALID_ERR] = "VALID_ERR",
- [COMMS_STS_TIMEOUT_ERR] = "TIMEOUT_ERR",
-};
-
-/**
- * hl_fw_dynamic_report_error_status - report error status
- *
- * @hdev: pointer to the habanalabs device structure
- * @status: value of FW status register
- * @expected_status: the expected status
- */
-static void hl_fw_dynamic_report_error_status(struct hl_device *hdev,
- u32 status,
- enum comms_sts expected_status)
-{
- enum comms_sts comm_status =
- FIELD_GET(COMMS_STATUS_STATUS_MASK, status);
-
- if (comm_status < COMMS_STS_INVLD_LAST)
- dev_err(hdev->dev, "Device status %s, expected status: %s\n",
- hl_dynamic_fw_status_str[comm_status],
- hl_dynamic_fw_status_str[expected_status]);
- else
- dev_err(hdev->dev, "Device status unknown %d, expected status: %s\n",
- comm_status,
- hl_dynamic_fw_status_str[expected_status]);
-}
-
-/**
- * hl_fw_dynamic_send_cmd - send LKD to FW cmd
- *
- * @hdev: pointer to the habanalabs device structure
- * @fw_loader: managing structure for loading device's FW
- * @cmd: LKD to FW cmd code
- * @size: size of next FW component to be loaded (0 if not necessary)
- *
- * LDK to FW exact command layout is defined at struct comms_command.
- * note: the size argument is used only when the next FW component should be
- * loaded, otherwise it shall be 0. the size is used by the FW in later
- * protocol stages and when sending only indicating the amount of memory
- * to be allocated by the FW to receive the next boot component.
- */
-static void hl_fw_dynamic_send_cmd(struct hl_device *hdev,
- struct fw_load_mgr *fw_loader,
- enum comms_cmd cmd, unsigned int size)
-{
- struct cpu_dyn_regs *dyn_regs;
- u32 val;
-
- dyn_regs = &fw_loader->dynamic_loader.comm_desc.cpu_dyn_regs;
-
- val = FIELD_PREP(COMMS_COMMAND_CMD_MASK, cmd);
- val |= FIELD_PREP(COMMS_COMMAND_SIZE_MASK, size);
-
- trace_habanalabs_comms_send_cmd(hdev->dev, comms_cmd_str_arr[cmd]);
- WREG32(le32_to_cpu(dyn_regs->kmd_msg_to_cpu), val);
-}
-
-/**
- * hl_fw_dynamic_extract_fw_response - update the FW response
- *
- * @hdev: pointer to the habanalabs device structure
- * @fw_loader: managing structure for loading device's FW
- * @response: FW response
- * @status: the status read from CPU status register
- *
- * @return 0 on success, otherwise non-zero error code
- */
-static int hl_fw_dynamic_extract_fw_response(struct hl_device *hdev,
- struct fw_load_mgr *fw_loader,
- struct fw_response *response,
- u32 status)
-{
- response->status = FIELD_GET(COMMS_STATUS_STATUS_MASK, status);
- response->ram_offset = FIELD_GET(COMMS_STATUS_OFFSET_MASK, status) <<
- COMMS_STATUS_OFFSET_ALIGN_SHIFT;
- response->ram_type = FIELD_GET(COMMS_STATUS_RAM_TYPE_MASK, status);
-
- if ((response->ram_type != COMMS_SRAM) &&
- (response->ram_type != COMMS_DRAM)) {
- dev_err(hdev->dev, "FW status: invalid RAM type %u\n",
- response->ram_type);
- return -EIO;
- }
-
- return 0;
-}
-
-/**
- * hl_fw_dynamic_wait_for_status - wait for status in dynamic FW load
- *
- * @hdev: pointer to the habanalabs device structure
- * @fw_loader: managing structure for loading device's FW
- * @expected_status: expected status to wait for
- * @timeout: timeout for status wait
- *
- * @return 0 on success, otherwise non-zero error code
- *
- * waiting for status from FW include polling the FW status register until
- * expected status is received or timeout occurs (whatever occurs first).
- */
-static int hl_fw_dynamic_wait_for_status(struct hl_device *hdev,
- struct fw_load_mgr *fw_loader,
- enum comms_sts expected_status,
- u32 timeout)
-{
- struct cpu_dyn_regs *dyn_regs;
- u32 status;
- int rc;
-
- dyn_regs = &fw_loader->dynamic_loader.comm_desc.cpu_dyn_regs;
-
- trace_habanalabs_comms_wait_status(hdev->dev, comms_sts_str_arr[expected_status]);
-
- /* Wait for expected status */
- rc = hl_poll_timeout(
- hdev,
- le32_to_cpu(dyn_regs->cpu_cmd_status_to_host),
- status,
- FIELD_GET(COMMS_STATUS_STATUS_MASK, status) == expected_status,
- hdev->fw_comms_poll_interval_usec,
- timeout);
-
- if (rc) {
- hl_fw_dynamic_report_error_status(hdev, status,
- expected_status);
- return -EIO;
- }
-
- trace_habanalabs_comms_wait_status_done(hdev->dev, comms_sts_str_arr[expected_status]);
-
- /*
- * skip storing FW response for NOOP to preserve the actual desired
- * FW status
- */
- if (expected_status == COMMS_STS_NOOP)
- return 0;
-
- rc = hl_fw_dynamic_extract_fw_response(hdev, fw_loader,
- &fw_loader->dynamic_loader.response,
- status);
- return rc;
-}
-
-/**
- * hl_fw_dynamic_send_clear_cmd - send clear command to FW
- *
- * @hdev: pointer to the habanalabs device structure
- * @fw_loader: managing structure for loading device's FW
- *
- * @return 0 on success, otherwise non-zero error code
- *
- * after command cycle between LKD to FW CPU (i.e. LKD got an expected status
- * from FW) we need to clear the CPU status register in order to avoid garbage
- * between command cycles.
- * This is done by sending clear command and polling the CPU to LKD status
- * register to hold the status NOOP
- */
-static int hl_fw_dynamic_send_clear_cmd(struct hl_device *hdev,
- struct fw_load_mgr *fw_loader)
-{
- hl_fw_dynamic_send_cmd(hdev, fw_loader, COMMS_CLR_STS, 0);
-
- return hl_fw_dynamic_wait_for_status(hdev, fw_loader, COMMS_STS_NOOP,
- fw_loader->cpu_timeout);
-}
-
-/**
- * hl_fw_dynamic_send_protocol_cmd - send LKD to FW cmd and wait for ACK
- *
- * @hdev: pointer to the habanalabs device structure
- * @fw_loader: managing structure for loading device's FW
- * @cmd: LKD to FW cmd code
- * @size: size of next FW component to be loaded (0 if not necessary)
- * @wait_ok: if true also wait for OK response from FW
- * @timeout: timeout for status wait
- *
- * @return 0 on success, otherwise non-zero error code
- *
- * brief:
- * when sending protocol command we have the following steps:
- * - send clear (clear command and verify clear status register)
- * - send the actual protocol command
- * - wait for ACK on the protocol command
- * - send clear
- * - send NOOP
- * if, in addition, the specific protocol command should wait for OK then:
- * - wait for OK
- * - send clear
- * - send NOOP
- *
- * NOTES:
- * send clear: this is necessary in order to clear the status register to avoid
- * leftovers between command
- * NOOP command: necessary to avoid loop on the clear command by the FW
- */
-int hl_fw_dynamic_send_protocol_cmd(struct hl_device *hdev,
- struct fw_load_mgr *fw_loader,
- enum comms_cmd cmd, unsigned int size,
- bool wait_ok, u32 timeout)
-{
- int rc;
-
- trace_habanalabs_comms_protocol_cmd(hdev->dev, comms_cmd_str_arr[cmd]);
-
- /* first send clear command to clean former commands */
- rc = hl_fw_dynamic_send_clear_cmd(hdev, fw_loader);
- if (rc)
- return rc;
-
- /* send the actual command */
- hl_fw_dynamic_send_cmd(hdev, fw_loader, cmd, size);
-
- /* wait for ACK for the command */
- rc = hl_fw_dynamic_wait_for_status(hdev, fw_loader, COMMS_STS_ACK,
- timeout);
- if (rc)
- return rc;
-
- /* clear command to prepare for NOOP command */
- rc = hl_fw_dynamic_send_clear_cmd(hdev, fw_loader);
- if (rc)
- return rc;
-
- /* send the actual NOOP command */
- hl_fw_dynamic_send_cmd(hdev, fw_loader, COMMS_NOOP, 0);
-
- if (!wait_ok)
- return 0;
-
- rc = hl_fw_dynamic_wait_for_status(hdev, fw_loader, COMMS_STS_OK,
- timeout);
- if (rc)
- return rc;
-
- /* clear command to prepare for NOOP command */
- rc = hl_fw_dynamic_send_clear_cmd(hdev, fw_loader);
- if (rc)
- return rc;
-
- /* send the actual NOOP command */
- hl_fw_dynamic_send_cmd(hdev, fw_loader, COMMS_NOOP, 0);
-
- return 0;
-}
-
-/**
- * hl_fw_compat_crc32 - CRC compatible with FW
- *
- * @data: pointer to the data
- * @size: size of the data
- *
- * @return the CRC32 result
- *
- * NOTE: kernel's CRC32 differs from standard CRC32 calculation.
- * in order to be aligned we need to flip the bits of both the input
- * initial CRC and kernel's CRC32 result.
- * in addition both sides use initial CRC of 0,
- */
-static u32 hl_fw_compat_crc32(u8 *data, size_t size)
-{
- return ~crc32_le(~((u32)0), data, size);
-}
-
-/**
- * hl_fw_dynamic_validate_memory_bound - validate memory bounds for memory
- * transfer (image or descriptor) between
- * host and FW
- *
- * @hdev: pointer to the habanalabs device structure
- * @addr: device address of memory transfer
- * @size: memory transfer size
- * @region: PCI memory region
- *
- * @return 0 on success, otherwise non-zero error code
- */
-static int hl_fw_dynamic_validate_memory_bound(struct hl_device *hdev,
- u64 addr, size_t size,
- struct pci_mem_region *region)
-{
- u64 end_addr;
-
- /* now make sure that the memory transfer is within region's bounds */
- end_addr = addr + size;
- if (end_addr >= region->region_base + region->region_size) {
- dev_err(hdev->dev,
- "dynamic FW load: memory transfer end address out of memory region bounds. addr: %llx\n",
- end_addr);
- return -EIO;
- }
-
- /*
- * now make sure memory transfer is within predefined BAR bounds.
- * this is to make sure we do not need to set the bar (e.g. for DRAM
- * memory transfers)
- */
- if (end_addr >= region->region_base - region->offset_in_bar +
- region->bar_size) {
- dev_err(hdev->dev,
- "FW image beyond PCI BAR bounds\n");
- return -EIO;
- }
-
- return 0;
-}
-
-/**
- * hl_fw_dynamic_validate_descriptor - validate FW descriptor
- *
- * @hdev: pointer to the habanalabs device structure
- * @fw_loader: managing structure for loading device's FW
- * @fw_desc: the descriptor from FW
- *
- * @return 0 on success, otherwise non-zero error code
- */
-static int hl_fw_dynamic_validate_descriptor(struct hl_device *hdev,
- struct fw_load_mgr *fw_loader,
- struct lkd_fw_comms_desc *fw_desc)
-{
- struct pci_mem_region *region;
- enum pci_region region_id;
- size_t data_size;
- u32 data_crc32;
- u8 *data_ptr;
- u64 addr;
- int rc;
-
- if (le32_to_cpu(fw_desc->header.magic) != HL_COMMS_DESC_MAGIC)
- dev_dbg(hdev->dev, "Invalid magic for dynamic FW descriptor (%x)\n",
- fw_desc->header.magic);
-
- if (fw_desc->header.version != HL_COMMS_DESC_VER)
- dev_dbg(hdev->dev, "Invalid version for dynamic FW descriptor (%x)\n",
- fw_desc->header.version);
-
- /*
- * Calc CRC32 of data without header. use the size of the descriptor
- * reported by firmware, without calculating it ourself, to allow adding
- * more fields to the lkd_fw_comms_desc structure.
- * note that no alignment/stride address issues here as all structures
- * are 64 bit padded.
- */
- data_ptr = (u8 *)fw_desc + sizeof(struct comms_desc_header);
- data_size = le16_to_cpu(fw_desc->header.size);
-
- data_crc32 = hl_fw_compat_crc32(data_ptr, data_size);
- if (data_crc32 != le32_to_cpu(fw_desc->header.crc32)) {
- dev_err(hdev->dev, "CRC32 mismatch for dynamic FW descriptor (%x:%x)\n",
- data_crc32, fw_desc->header.crc32);
- return -EIO;
- }
-
- /* find memory region to which to copy the image */
- addr = le64_to_cpu(fw_desc->img_addr);
- region_id = hl_get_pci_memory_region(hdev, addr);
- if ((region_id != PCI_REGION_SRAM) && ((region_id != PCI_REGION_DRAM))) {
- dev_err(hdev->dev, "Invalid region to copy FW image address=%llx\n", addr);
- return -EIO;
- }
-
- region = &hdev->pci_mem_region[region_id];
-
- /* store the region for the copy stage */
- fw_loader->dynamic_loader.image_region = region;
-
- /*
- * here we know that the start address is valid, now make sure that the
- * image is within region's bounds
- */
- rc = hl_fw_dynamic_validate_memory_bound(hdev, addr,
- fw_loader->dynamic_loader.fw_image_size,
- region);
- if (rc) {
- dev_err(hdev->dev, "invalid mem transfer request for FW image\n");
- return rc;
- }
-
- /* here we can mark the descriptor as valid as the content has been validated */
- fw_loader->dynamic_loader.fw_desc_valid = true;
-
- return 0;
-}
-
-static int hl_fw_dynamic_validate_response(struct hl_device *hdev,
- struct fw_response *response,
- struct pci_mem_region *region)
-{
- u64 device_addr;
- int rc;
-
- device_addr = region->region_base + response->ram_offset;
-
- /*
- * validate that the descriptor is within region's bounds
- * Note that as the start address was supplied according to the RAM
- * type- testing only the end address is enough
- */
- rc = hl_fw_dynamic_validate_memory_bound(hdev, device_addr,
- sizeof(struct lkd_fw_comms_desc),
- region);
- return rc;
-}
-
-/*
- * hl_fw_dynamic_read_descriptor_msg - read and show the ascii msg that sent by fw
- *
- * @hdev: pointer to the habanalabs device structure
- * @fw_desc: the descriptor from FW
- */
-static void hl_fw_dynamic_read_descriptor_msg(struct hl_device *hdev,
- struct lkd_fw_comms_desc *fw_desc)
-{
- int i;
- char *msg;
-
- for (i = 0 ; i < LKD_FW_ASCII_MSG_MAX ; i++) {
- if (!fw_desc->ascii_msg[i].valid)
- return;
-
- /* force NULL termination */
- msg = fw_desc->ascii_msg[i].msg;
- msg[LKD_FW_ASCII_MSG_MAX_LEN - 1] = '\0';
-
- switch (fw_desc->ascii_msg[i].msg_lvl) {
- case LKD_FW_ASCII_MSG_ERR:
- dev_err(hdev->dev, "fw: %s", fw_desc->ascii_msg[i].msg);
- break;
- case LKD_FW_ASCII_MSG_WRN:
- dev_warn(hdev->dev, "fw: %s", fw_desc->ascii_msg[i].msg);
- break;
- case LKD_FW_ASCII_MSG_INF:
- dev_info(hdev->dev, "fw: %s", fw_desc->ascii_msg[i].msg);
- break;
- default:
- dev_dbg(hdev->dev, "fw: %s", fw_desc->ascii_msg[i].msg);
- break;
- }
- }
-}
-
-/**
- * hl_fw_dynamic_read_and_validate_descriptor - read and validate FW descriptor
- *
- * @hdev: pointer to the habanalabs device structure
- * @fw_loader: managing structure for loading device's FW
- *
- * @return 0 on success, otherwise non-zero error code
- */
-static int hl_fw_dynamic_read_and_validate_descriptor(struct hl_device *hdev,
- struct fw_load_mgr *fw_loader)
-{
- struct lkd_fw_comms_desc *fw_desc;
- struct pci_mem_region *region;
- struct fw_response *response;
- void *temp_fw_desc;
- void __iomem *src;
- u16 fw_data_size;
- enum pci_region region_id;
- int rc;
-
- fw_desc = &fw_loader->dynamic_loader.comm_desc;
- response = &fw_loader->dynamic_loader.response;
-
- region_id = (response->ram_type == COMMS_SRAM) ?
- PCI_REGION_SRAM : PCI_REGION_DRAM;
-
- region = &hdev->pci_mem_region[region_id];
-
- rc = hl_fw_dynamic_validate_response(hdev, response, region);
- if (rc) {
- dev_err(hdev->dev,
- "invalid mem transfer request for FW descriptor\n");
- return rc;
- }
-
- /*
- * extract address to copy the descriptor from
- * in addition, as the descriptor value is going to be over-ridden by new data- we mark it
- * as invalid.
- * it will be marked again as valid once validated
- */
- fw_loader->dynamic_loader.fw_desc_valid = false;
- src = hdev->pcie_bar[region->bar_id] + region->offset_in_bar +
- response->ram_offset;
-
- /*
- * We do the copy of the fw descriptor in 2 phases:
- * 1. copy the header + data info according to our lkd_fw_comms_desc definition.
- * then we're able to read the actual data size provided by fw.
- * this is needed for cases where data in descriptor was changed(add/remove)
- * in embedded specs header file before updating lkd copy of the header file
- * 2. copy descriptor to temporary buffer with aligned size and send it to validation
- */
- memcpy_fromio(fw_desc, src, sizeof(struct lkd_fw_comms_desc));
- fw_data_size = le16_to_cpu(fw_desc->header.size);
-
- temp_fw_desc = vzalloc(sizeof(struct comms_desc_header) + fw_data_size);
- if (!temp_fw_desc)
- return -ENOMEM;
-
- memcpy_fromio(temp_fw_desc, src, sizeof(struct comms_desc_header) + fw_data_size);
-
- rc = hl_fw_dynamic_validate_descriptor(hdev, fw_loader,
- (struct lkd_fw_comms_desc *) temp_fw_desc);
-
- if (!rc)
- hl_fw_dynamic_read_descriptor_msg(hdev, temp_fw_desc);
-
- vfree(temp_fw_desc);
-
- return rc;
-}
-
-/**
- * hl_fw_dynamic_request_descriptor - handshake with CPU to get FW descriptor
- *
- * @hdev: pointer to the habanalabs device structure
- * @fw_loader: managing structure for loading device's FW
- * @next_image_size: size to allocate for next FW component
- *
- * @return 0 on success, otherwise non-zero error code
- */
-static int hl_fw_dynamic_request_descriptor(struct hl_device *hdev,
- struct fw_load_mgr *fw_loader,
- size_t next_image_size)
-{
- int rc;
-
- rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_PREP_DESC,
- next_image_size, true,
- fw_loader->cpu_timeout);
- if (rc)
- return rc;
-
- return hl_fw_dynamic_read_and_validate_descriptor(hdev, fw_loader);
-}
-
-/**
- * hl_fw_dynamic_read_device_fw_version - read FW version to exposed properties
- *
- * @hdev: pointer to the habanalabs device structure
- * @fwc: the firmware component
- * @fw_version: fw component's version string
- */
-static int hl_fw_dynamic_read_device_fw_version(struct hl_device *hdev,
- enum hl_fw_component fwc,
- const char *fw_version)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- char *preboot_ver, *boot_ver;
- char btl_ver[32];
-
- switch (fwc) {
- case FW_COMP_BOOT_FIT:
- strscpy(prop->uboot_ver, fw_version, VERSION_MAX_LEN);
- boot_ver = extract_fw_ver_from_str(prop->uboot_ver);
- if (boot_ver) {
- dev_info(hdev->dev, "boot-fit version %s\n", boot_ver);
- kfree(boot_ver);
- }
-
- break;
- case FW_COMP_PREBOOT:
- strscpy(prop->preboot_ver, fw_version, VERSION_MAX_LEN);
- preboot_ver = strnstr(prop->preboot_ver, "Preboot",
- VERSION_MAX_LEN);
- if (preboot_ver && preboot_ver != prop->preboot_ver) {
- strscpy(btl_ver, prop->preboot_ver,
- min((int) (preboot_ver - prop->preboot_ver), 31));
- dev_info(hdev->dev, "%s\n", btl_ver);
- }
-
- preboot_ver = extract_fw_ver_from_str(prop->preboot_ver);
- if (preboot_ver) {
- int rc;
-
- dev_info(hdev->dev, "preboot version %s\n", preboot_ver);
-
- /* This function takes care of freeing preboot_ver */
- rc = extract_fw_sub_versions(hdev, preboot_ver);
- if (rc)
- return rc;
- }
-
- break;
- default:
- dev_warn(hdev->dev, "Undefined FW component: %d\n", fwc);
- return -EINVAL;
- }
-
- return 0;
-}
-
-/**
- * hl_fw_dynamic_copy_image - copy image to memory allocated by the FW
- *
- * @hdev: pointer to the habanalabs device structure
- * @fw: fw descriptor
- * @fw_loader: managing structure for loading device's FW
- */
-static int hl_fw_dynamic_copy_image(struct hl_device *hdev,
- const struct firmware *fw,
- struct fw_load_mgr *fw_loader)
-{
- struct lkd_fw_comms_desc *fw_desc;
- struct pci_mem_region *region;
- void __iomem *dest;
- u64 addr;
- int rc;
-
- fw_desc = &fw_loader->dynamic_loader.comm_desc;
- addr = le64_to_cpu(fw_desc->img_addr);
-
- /* find memory region to which to copy the image */
- region = fw_loader->dynamic_loader.image_region;
-
- dest = hdev->pcie_bar[region->bar_id] + region->offset_in_bar +
- (addr - region->region_base);
-
- rc = hl_fw_copy_fw_to_device(hdev, fw, dest,
- fw_loader->boot_fit_img.src_off,
- fw_loader->boot_fit_img.copy_size);
-
- return rc;
-}
-
-/**
- * hl_fw_dynamic_copy_msg - copy msg to memory allocated by the FW
- *
- * @hdev: pointer to the habanalabs device structure
- * @msg: message
- * @fw_loader: managing structure for loading device's FW
- */
-static int hl_fw_dynamic_copy_msg(struct hl_device *hdev,
- struct lkd_msg_comms *msg, struct fw_load_mgr *fw_loader)
-{
- struct lkd_fw_comms_desc *fw_desc;
- struct pci_mem_region *region;
- void __iomem *dest;
- u64 addr;
- int rc;
-
- fw_desc = &fw_loader->dynamic_loader.comm_desc;
- addr = le64_to_cpu(fw_desc->img_addr);
-
- /* find memory region to which to copy the image */
- region = fw_loader->dynamic_loader.image_region;
-
- dest = hdev->pcie_bar[region->bar_id] + region->offset_in_bar +
- (addr - region->region_base);
-
- rc = hl_fw_copy_msg_to_device(hdev, msg, dest, 0, 0);
-
- return rc;
-}
-
-/**
- * hl_fw_boot_fit_update_state - update internal data structures after boot-fit
- * is loaded
- *
- * @hdev: pointer to the habanalabs device structure
- * @cpu_boot_dev_sts0_reg: register holding CPU boot dev status 0
- * @cpu_boot_dev_sts1_reg: register holding CPU boot dev status 1
- *
- * @return 0 on success, otherwise non-zero error code
- */
-static void hl_fw_boot_fit_update_state(struct hl_device *hdev,
- u32 cpu_boot_dev_sts0_reg,
- u32 cpu_boot_dev_sts1_reg)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
-
- hdev->fw_loader.fw_comp_loaded |= FW_TYPE_BOOT_CPU;
-
- /* Read boot_cpu status bits */
- if (prop->fw_preboot_cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_ENABLED) {
- prop->fw_bootfit_cpu_boot_dev_sts0 =
- RREG32(cpu_boot_dev_sts0_reg);
-
- prop->hard_reset_done_by_fw = !!(prop->fw_bootfit_cpu_boot_dev_sts0 &
- CPU_BOOT_DEV_STS0_FW_HARD_RST_EN);
-
- dev_dbg(hdev->dev, "Firmware boot CPU status0 %#x\n",
- prop->fw_bootfit_cpu_boot_dev_sts0);
- }
-
- if (prop->fw_cpu_boot_dev_sts1_valid) {
- prop->fw_bootfit_cpu_boot_dev_sts1 =
- RREG32(cpu_boot_dev_sts1_reg);
-
- dev_dbg(hdev->dev, "Firmware boot CPU status1 %#x\n",
- prop->fw_bootfit_cpu_boot_dev_sts1);
- }
-
- dev_dbg(hdev->dev, "Firmware boot CPU hard-reset is %s\n",
- prop->hard_reset_done_by_fw ? "enabled" : "disabled");
-}
-
-static void hl_fw_dynamic_update_linux_interrupt_if(struct hl_device *hdev)
-{
- struct cpu_dyn_regs *dyn_regs =
- &hdev->fw_loader.dynamic_loader.comm_desc.cpu_dyn_regs;
-
- /* Check whether all 3 interrupt interfaces are set, if not use a
- * single interface
- */
- if (!hdev->asic_prop.gic_interrupts_enable &&
- !(hdev->asic_prop.fw_app_cpu_boot_dev_sts0 &
- CPU_BOOT_DEV_STS0_MULTI_IRQ_POLL_EN)) {
- dyn_regs->gic_host_halt_irq = dyn_regs->gic_host_pi_upd_irq;
- dyn_regs->gic_host_ints_irq = dyn_regs->gic_host_pi_upd_irq;
-
- dev_warn(hdev->dev,
- "Using a single interrupt interface towards cpucp");
- }
-}
-/**
- * hl_fw_dynamic_load_image - load FW image using dynamic protocol
- *
- * @hdev: pointer to the habanalabs device structure
- * @fw_loader: managing structure for loading device's FW
- * @load_fwc: the FW component to be loaded
- * @img_ld_timeout: image load timeout
- *
- * @return 0 on success, otherwise non-zero error code
- */
-static int hl_fw_dynamic_load_image(struct hl_device *hdev,
- struct fw_load_mgr *fw_loader,
- enum hl_fw_component load_fwc,
- u32 img_ld_timeout)
-{
- enum hl_fw_component cur_fwc;
- const struct firmware *fw;
- char *fw_name;
- int rc = 0;
-
- /*
- * when loading image we have one of 2 scenarios:
- * 1. current FW component is preboot and we want to load boot-fit
- * 2. current FW component is boot-fit and we want to load linux
- */
- if (load_fwc == FW_COMP_BOOT_FIT) {
- cur_fwc = FW_COMP_PREBOOT;
- fw_name = fw_loader->boot_fit_img.image_name;
- } else {
- cur_fwc = FW_COMP_BOOT_FIT;
- fw_name = fw_loader->linux_img.image_name;
- }
-
- /* request FW in order to communicate to FW the size to be allocated */
- rc = hl_request_fw(hdev, &fw, fw_name);
- if (rc)
- return rc;
-
- /* store the image size for future validation */
- fw_loader->dynamic_loader.fw_image_size = fw->size;
-
- rc = hl_fw_dynamic_request_descriptor(hdev, fw_loader, fw->size);
- if (rc)
- goto release_fw;
-
- /* read preboot version */
- rc = hl_fw_dynamic_read_device_fw_version(hdev, cur_fwc,
- fw_loader->dynamic_loader.comm_desc.cur_fw_ver);
- if (rc)
- goto release_fw;
-
- /* update state according to boot stage */
- if (cur_fwc == FW_COMP_BOOT_FIT) {
- struct cpu_dyn_regs *dyn_regs;
-
- dyn_regs = &fw_loader->dynamic_loader.comm_desc.cpu_dyn_regs;
- hl_fw_boot_fit_update_state(hdev,
- le32_to_cpu(dyn_regs->cpu_boot_dev_sts0),
- le32_to_cpu(dyn_regs->cpu_boot_dev_sts1));
- }
-
- /* copy boot fit to space allocated by FW */
- rc = hl_fw_dynamic_copy_image(hdev, fw, fw_loader);
- if (rc)
- goto release_fw;
-
- rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_DATA_RDY,
- 0, true,
- fw_loader->cpu_timeout);
- if (rc)
- goto release_fw;
-
- rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_EXEC,
- 0, false,
- img_ld_timeout);
-
-release_fw:
- hl_release_firmware(fw);
- return rc;
-}
-
-static int hl_fw_dynamic_wait_for_boot_fit_active(struct hl_device *hdev,
- struct fw_load_mgr *fw_loader)
-{
- struct dynamic_fw_load_mgr *dyn_loader;
- u32 status;
- int rc;
-
- dyn_loader = &fw_loader->dynamic_loader;
-
- /*
- * Make sure CPU boot-loader is running
- * Note that the CPU_BOOT_STATUS_SRAM_AVAIL is generally set by Linux
- * yet there is a debug scenario in which we loading uboot (without Linux)
- * which at later stage is relocated to DRAM. In this case we expect
- * uboot to set the CPU_BOOT_STATUS_SRAM_AVAIL and so we add it to the
- * poll flags
- */
- rc = hl_poll_timeout(
- hdev,
- le32_to_cpu(dyn_loader->comm_desc.cpu_dyn_regs.cpu_boot_status),
- status,
- (status == CPU_BOOT_STATUS_READY_TO_BOOT) ||
- (status == CPU_BOOT_STATUS_SRAM_AVAIL),
- hdev->fw_poll_interval_usec,
- dyn_loader->wait_for_bl_timeout);
- if (rc) {
- dev_err(hdev->dev, "failed to wait for boot\n");
- return rc;
- }
-
- dev_dbg(hdev->dev, "uboot status = %d\n", status);
- return 0;
-}
-
-static int hl_fw_dynamic_wait_for_linux_active(struct hl_device *hdev,
- struct fw_load_mgr *fw_loader)
-{
- struct dynamic_fw_load_mgr *dyn_loader;
- u32 status;
- int rc;
-
- dyn_loader = &fw_loader->dynamic_loader;
-
- /* Make sure CPU linux is running */
-
- rc = hl_poll_timeout(
- hdev,
- le32_to_cpu(dyn_loader->comm_desc.cpu_dyn_regs.cpu_boot_status),
- status,
- (status == CPU_BOOT_STATUS_SRAM_AVAIL),
- hdev->fw_poll_interval_usec,
- fw_loader->cpu_timeout);
- if (rc) {
- dev_err(hdev->dev, "failed to wait for Linux\n");
- return rc;
- }
-
- dev_dbg(hdev->dev, "Boot status = %d\n", status);
- return 0;
-}
-
-/**
- * hl_fw_linux_update_state - update internal data structures after Linux
- * is loaded.
- * Note: Linux initialization is comprised mainly
- * of two stages - loading kernel (SRAM_AVAIL)
- * & loading ARMCP.
- * Therefore reading boot device status in any of
- * these stages might result in different values.
- *
- * @hdev: pointer to the habanalabs device structure
- * @cpu_boot_dev_sts0_reg: register holding CPU boot dev status 0
- * @cpu_boot_dev_sts1_reg: register holding CPU boot dev status 1
- *
- * @return 0 on success, otherwise non-zero error code
- */
-static void hl_fw_linux_update_state(struct hl_device *hdev,
- u32 cpu_boot_dev_sts0_reg,
- u32 cpu_boot_dev_sts1_reg)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
-
- hdev->fw_loader.fw_comp_loaded |= FW_TYPE_LINUX;
-
- /* Read FW application security bits */
- if (prop->fw_cpu_boot_dev_sts0_valid) {
- prop->fw_app_cpu_boot_dev_sts0 = RREG32(cpu_boot_dev_sts0_reg);
-
- prop->hard_reset_done_by_fw = !!(prop->fw_app_cpu_boot_dev_sts0 &
- CPU_BOOT_DEV_STS0_FW_HARD_RST_EN);
-
- if (prop->fw_app_cpu_boot_dev_sts0 &
- CPU_BOOT_DEV_STS0_GIC_PRIVILEGED_EN)
- prop->gic_interrupts_enable = false;
-
- dev_dbg(hdev->dev,
- "Firmware application CPU status0 %#x\n",
- prop->fw_app_cpu_boot_dev_sts0);
-
- dev_dbg(hdev->dev, "GIC controller is %s\n",
- prop->gic_interrupts_enable ?
- "enabled" : "disabled");
- }
-
- if (prop->fw_cpu_boot_dev_sts1_valid) {
- prop->fw_app_cpu_boot_dev_sts1 = RREG32(cpu_boot_dev_sts1_reg);
-
- dev_dbg(hdev->dev,
- "Firmware application CPU status1 %#x\n",
- prop->fw_app_cpu_boot_dev_sts1);
- }
-
- dev_dbg(hdev->dev, "Firmware application CPU hard-reset is %s\n",
- prop->hard_reset_done_by_fw ? "enabled" : "disabled");
-
- dev_info(hdev->dev, "Successfully loaded firmware to device\n");
-}
-
-/**
- * hl_fw_dynamic_send_msg - send a COMMS message with attached data
- *
- * @hdev: pointer to the habanalabs device structure
- * @fw_loader: managing structure for loading device's FW
- * @msg_type: message type
- * @data: data to be sent
- *
- * @return 0 on success, otherwise non-zero error code
- */
-static int hl_fw_dynamic_send_msg(struct hl_device *hdev,
- struct fw_load_mgr *fw_loader, u8 msg_type, void *data)
-{
- struct lkd_msg_comms *msg;
- int rc;
-
- msg = kzalloc(sizeof(*msg), GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
-
- /* create message to be sent */
- msg->header.type = msg_type;
- msg->header.size = cpu_to_le16(sizeof(struct comms_msg_header));
- msg->header.magic = cpu_to_le32(HL_COMMS_MSG_MAGIC);
-
- switch (msg_type) {
- case HL_COMMS_RESET_CAUSE_TYPE:
- msg->reset_cause = *(__u8 *) data;
- break;
-
- default:
- dev_err(hdev->dev,
- "Send COMMS message - invalid message type %u\n",
- msg_type);
- rc = -EINVAL;
- goto out;
- }
-
- rc = hl_fw_dynamic_request_descriptor(hdev, fw_loader,
- sizeof(struct lkd_msg_comms));
- if (rc)
- goto out;
-
- /* copy message to space allocated by FW */
- rc = hl_fw_dynamic_copy_msg(hdev, msg, fw_loader);
- if (rc)
- goto out;
-
- rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_DATA_RDY,
- 0, true,
- fw_loader->cpu_timeout);
- if (rc)
- goto out;
-
- rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_EXEC,
- 0, true,
- fw_loader->cpu_timeout);
-
-out:
- kfree(msg);
- return rc;
-}
-
-/**
- * hl_fw_dynamic_init_cpu - initialize the device CPU using dynamic protocol
- *
- * @hdev: pointer to the habanalabs device structure
- * @fw_loader: managing structure for loading device's FW
- *
- * @return 0 on success, otherwise non-zero error code
- *
- * brief: the dynamic protocol is master (LKD) slave (FW CPU) protocol.
- * the communication is done using registers:
- * - LKD command register
- * - FW status register
- * the protocol is race free. this goal is achieved by splitting the requests
- * and response to known synchronization points between the LKD and the FW.
- * each response to LKD request is known and bound to a predefined timeout.
- * in case of timeout expiration without the desired status from FW- the
- * protocol (and hence the boot) will fail.
- */
-static int hl_fw_dynamic_init_cpu(struct hl_device *hdev,
- struct fw_load_mgr *fw_loader)
-{
- struct cpu_dyn_regs *dyn_regs;
- int rc, fw_error_rc;
-
- dev_info(hdev->dev,
- "Loading %sfirmware to device, may take some time...\n",
- hdev->asic_prop.fw_security_enabled ? "secured " : "");
-
- /* initialize FW descriptor as invalid */
- fw_loader->dynamic_loader.fw_desc_valid = false;
-
- /*
- * In this stage, "cpu_dyn_regs" contains only LKD's hard coded values!
- * It will be updated from FW after hl_fw_dynamic_request_descriptor().
- */
- dyn_regs = &fw_loader->dynamic_loader.comm_desc.cpu_dyn_regs;
-
- rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader, COMMS_RST_STATE,
- 0, true,
- fw_loader->cpu_timeout);
- if (rc)
- goto protocol_err;
-
- if (hdev->reset_info.curr_reset_cause) {
- rc = hl_fw_dynamic_send_msg(hdev, fw_loader,
- HL_COMMS_RESET_CAUSE_TYPE, &hdev->reset_info.curr_reset_cause);
- if (rc)
- goto protocol_err;
-
- /* Clear current reset cause */
- hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_UNKNOWN;
- }
-
- if (!(hdev->fw_components & FW_TYPE_BOOT_CPU)) {
- struct lkd_fw_binning_info *binning_info;
-
- rc = hl_fw_dynamic_request_descriptor(hdev, fw_loader, 0);
- if (rc)
- goto protocol_err;
-
- /* read preboot version */
- rc = hl_fw_dynamic_read_device_fw_version(hdev, FW_COMP_PREBOOT,
- fw_loader->dynamic_loader.comm_desc.cur_fw_ver);
-
- if (rc)
- goto out;
-
- /* read binning info from preboot */
- if (hdev->support_preboot_binning) {
- binning_info = &fw_loader->dynamic_loader.comm_desc.binning_info;
- hdev->tpc_binning = le64_to_cpu(binning_info->tpc_mask_l);
- hdev->dram_binning = le32_to_cpu(binning_info->dram_mask);
- hdev->edma_binning = le32_to_cpu(binning_info->edma_mask);
- hdev->decoder_binning = le32_to_cpu(binning_info->dec_mask);
- hdev->rotator_binning = le32_to_cpu(binning_info->rot_mask);
-
- rc = hdev->asic_funcs->set_dram_properties(hdev);
- if (rc)
- goto out;
-
- dev_dbg(hdev->dev,
- "Read binning masks: tpc: 0x%llx, dram: 0x%llx, edma: 0x%x, dec: 0x%x, rot:0x%x\n",
- hdev->tpc_binning, hdev->dram_binning, hdev->edma_binning,
- hdev->decoder_binning, hdev->rotator_binning);
- }
-out:
- return rc;
- }
-
- /* load boot fit to FW */
- rc = hl_fw_dynamic_load_image(hdev, fw_loader, FW_COMP_BOOT_FIT,
- fw_loader->boot_fit_timeout);
- if (rc) {
- dev_err(hdev->dev, "failed to load boot fit\n");
- goto protocol_err;
- }
-
- /*
- * when testing FW load (without Linux) on PLDM we don't want to
- * wait until boot fit is active as it may take several hours.
- * instead, we load the bootfit and let it do all initialization in
- * the background.
- */
- if (hdev->pldm && !(hdev->fw_components & FW_TYPE_LINUX))
- return 0;
-
- rc = hl_fw_dynamic_wait_for_boot_fit_active(hdev, fw_loader);
- if (rc)
- goto protocol_err;
-
- /* Enable DRAM scrambling before Linux boot and after successful
- * UBoot
- */
- hdev->asic_funcs->init_cpu_scrambler_dram(hdev);
-
- if (!(hdev->fw_components & FW_TYPE_LINUX)) {
- dev_info(hdev->dev, "Skip loading Linux F/W\n");
- return 0;
- }
-
- if (fw_loader->skip_bmc) {
- rc = hl_fw_dynamic_send_protocol_cmd(hdev, fw_loader,
- COMMS_SKIP_BMC, 0,
- true,
- fw_loader->cpu_timeout);
- if (rc) {
- dev_err(hdev->dev, "failed to load boot fit\n");
- goto protocol_err;
- }
- }
-
- /* load Linux image to FW */
- rc = hl_fw_dynamic_load_image(hdev, fw_loader, FW_COMP_LINUX,
- fw_loader->cpu_timeout);
- if (rc) {
- dev_err(hdev->dev, "failed to load Linux\n");
- goto protocol_err;
- }
-
- rc = hl_fw_dynamic_wait_for_linux_active(hdev, fw_loader);
- if (rc)
- goto protocol_err;
-
- hl_fw_linux_update_state(hdev, le32_to_cpu(dyn_regs->cpu_boot_dev_sts0),
- le32_to_cpu(dyn_regs->cpu_boot_dev_sts1));
-
- hl_fw_dynamic_update_linux_interrupt_if(hdev);
-
-protocol_err:
- if (fw_loader->dynamic_loader.fw_desc_valid) {
- fw_error_rc = fw_read_errors(hdev, le32_to_cpu(dyn_regs->cpu_boot_err0),
- le32_to_cpu(dyn_regs->cpu_boot_err1),
- le32_to_cpu(dyn_regs->cpu_boot_dev_sts0),
- le32_to_cpu(dyn_regs->cpu_boot_dev_sts1));
-
- if (fw_error_rc)
- return fw_error_rc;
- }
-
- return rc;
-}
-
-/**
- * hl_fw_static_init_cpu - initialize the device CPU using static protocol
- *
- * @hdev: pointer to the habanalabs device structure
- * @fw_loader: managing structure for loading device's FW
- *
- * @return 0 on success, otherwise non-zero error code
- */
-static int hl_fw_static_init_cpu(struct hl_device *hdev,
- struct fw_load_mgr *fw_loader)
-{
- u32 cpu_msg_status_reg, cpu_timeout, msg_to_cpu_reg, status;
- u32 cpu_boot_dev_status0_reg, cpu_boot_dev_status1_reg;
- struct static_fw_load_mgr *static_loader;
- u32 cpu_boot_status_reg;
- int rc;
-
- if (!(hdev->fw_components & FW_TYPE_BOOT_CPU))
- return 0;
-
- /* init common loader parameters */
- cpu_timeout = fw_loader->cpu_timeout;
-
- /* init static loader parameters */
- static_loader = &fw_loader->static_loader;
- cpu_msg_status_reg = static_loader->cpu_cmd_status_to_host_reg;
- msg_to_cpu_reg = static_loader->kmd_msg_to_cpu_reg;
- cpu_boot_dev_status0_reg = static_loader->cpu_boot_dev_status0_reg;
- cpu_boot_dev_status1_reg = static_loader->cpu_boot_dev_status1_reg;
- cpu_boot_status_reg = static_loader->cpu_boot_status_reg;
-
- dev_info(hdev->dev, "Going to wait for device boot (up to %lds)\n",
- cpu_timeout / USEC_PER_SEC);
-
- /* Wait for boot FIT request */
- rc = hl_poll_timeout(
- hdev,
- cpu_boot_status_reg,
- status,
- status == CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT,
- hdev->fw_poll_interval_usec,
- fw_loader->boot_fit_timeout);
-
- if (rc) {
- dev_dbg(hdev->dev,
- "No boot fit request received, resuming boot\n");
- } else {
- rc = hdev->asic_funcs->load_boot_fit_to_device(hdev);
- if (rc)
- goto out;
-
- /* Clear device CPU message status */
- WREG32(cpu_msg_status_reg, CPU_MSG_CLR);
-
- /* Signal device CPU that boot loader is ready */
- WREG32(msg_to_cpu_reg, KMD_MSG_FIT_RDY);
-
- /* Poll for CPU device ack */
- rc = hl_poll_timeout(
- hdev,
- cpu_msg_status_reg,
- status,
- status == CPU_MSG_OK,
- hdev->fw_poll_interval_usec,
- fw_loader->boot_fit_timeout);
-
- if (rc) {
- dev_err(hdev->dev,
- "Timeout waiting for boot fit load ack\n");
- goto out;
- }
-
- /* Clear message */
- WREG32(msg_to_cpu_reg, KMD_MSG_NA);
- }
-
- /*
- * Make sure CPU boot-loader is running
- * Note that the CPU_BOOT_STATUS_SRAM_AVAIL is generally set by Linux
- * yet there is a debug scenario in which we loading uboot (without Linux)
- * which at later stage is relocated to DRAM. In this case we expect
- * uboot to set the CPU_BOOT_STATUS_SRAM_AVAIL and so we add it to the
- * poll flags
- */
- rc = hl_poll_timeout(
- hdev,
- cpu_boot_status_reg,
- status,
- (status == CPU_BOOT_STATUS_DRAM_RDY) ||
- (status == CPU_BOOT_STATUS_NIC_FW_RDY) ||
- (status == CPU_BOOT_STATUS_READY_TO_BOOT) ||
- (status == CPU_BOOT_STATUS_SRAM_AVAIL),
- hdev->fw_poll_interval_usec,
- cpu_timeout);
-
- dev_dbg(hdev->dev, "uboot status = %d\n", status);
-
- /* Read U-Boot version now in case we will later fail */
- hl_fw_static_read_device_fw_version(hdev, FW_COMP_BOOT_FIT);
-
- /* update state according to boot stage */
- hl_fw_boot_fit_update_state(hdev, cpu_boot_dev_status0_reg,
- cpu_boot_dev_status1_reg);
-
- if (rc) {
- detect_cpu_boot_status(hdev, status);
- rc = -EIO;
- goto out;
- }
-
- /* Enable DRAM scrambling before Linux boot and after successful
- * UBoot
- */
- hdev->asic_funcs->init_cpu_scrambler_dram(hdev);
-
- if (!(hdev->fw_components & FW_TYPE_LINUX)) {
- dev_info(hdev->dev, "Skip loading Linux F/W\n");
- rc = 0;
- goto out;
- }
-
- if (status == CPU_BOOT_STATUS_SRAM_AVAIL) {
- rc = 0;
- goto out;
- }
-
- dev_info(hdev->dev,
- "Loading firmware to device, may take some time...\n");
-
- rc = hdev->asic_funcs->load_firmware_to_device(hdev);
- if (rc)
- goto out;
-
- if (fw_loader->skip_bmc) {
- WREG32(msg_to_cpu_reg, KMD_MSG_SKIP_BMC);
-
- rc = hl_poll_timeout(
- hdev,
- cpu_boot_status_reg,
- status,
- (status == CPU_BOOT_STATUS_BMC_WAITING_SKIPPED),
- hdev->fw_poll_interval_usec,
- cpu_timeout);
-
- if (rc) {
- dev_err(hdev->dev,
- "Failed to get ACK on skipping BMC, %d\n",
- status);
- WREG32(msg_to_cpu_reg, KMD_MSG_NA);
- rc = -EIO;
- goto out;
- }
- }
-
- WREG32(msg_to_cpu_reg, KMD_MSG_FIT_RDY);
-
- rc = hl_poll_timeout(
- hdev,
- cpu_boot_status_reg,
- status,
- (status == CPU_BOOT_STATUS_SRAM_AVAIL),
- hdev->fw_poll_interval_usec,
- cpu_timeout);
-
- /* Clear message */
- WREG32(msg_to_cpu_reg, KMD_MSG_NA);
-
- if (rc) {
- if (status == CPU_BOOT_STATUS_FIT_CORRUPTED)
- dev_err(hdev->dev,
- "Device reports FIT image is corrupted\n");
- else
- dev_err(hdev->dev,
- "Failed to load firmware to device, %d\n",
- status);
-
- rc = -EIO;
- goto out;
- }
-
- rc = fw_read_errors(hdev, fw_loader->static_loader.boot_err0_reg,
- fw_loader->static_loader.boot_err1_reg,
- cpu_boot_dev_status0_reg,
- cpu_boot_dev_status1_reg);
- if (rc)
- return rc;
-
- hl_fw_linux_update_state(hdev, cpu_boot_dev_status0_reg,
- cpu_boot_dev_status1_reg);
-
- return 0;
-
-out:
- fw_read_errors(hdev, fw_loader->static_loader.boot_err0_reg,
- fw_loader->static_loader.boot_err1_reg,
- cpu_boot_dev_status0_reg,
- cpu_boot_dev_status1_reg);
-
- return rc;
-}
-
-/**
- * hl_fw_init_cpu - initialize the device CPU
- *
- * @hdev: pointer to the habanalabs device structure
- *
- * @return 0 on success, otherwise non-zero error code
- *
- * perform necessary initializations for device's CPU. takes into account if
- * init protocol is static or dynamic.
- */
-int hl_fw_init_cpu(struct hl_device *hdev)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct fw_load_mgr *fw_loader = &hdev->fw_loader;
-
- return prop->dynamic_fw_load ?
- hl_fw_dynamic_init_cpu(hdev, fw_loader) :
- hl_fw_static_init_cpu(hdev, fw_loader);
-}
-
-void hl_fw_set_pll_profile(struct hl_device *hdev)
-{
- hl_fw_set_frequency(hdev, hdev->asic_prop.clk_pll_index,
- hdev->asic_prop.max_freq_value);
-}
-
-int hl_fw_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk)
-{
- long value;
-
- if (!hl_device_operational(hdev, NULL))
- return -ENODEV;
-
- if (!hdev->pdev) {
- *cur_clk = 0;
- *max_clk = 0;
- return 0;
- }
-
- value = hl_fw_get_frequency(hdev, hdev->asic_prop.clk_pll_index, false);
-
- if (value < 0) {
- dev_err(hdev->dev, "Failed to retrieve device max clock %ld\n", value);
- return value;
- }
-
- *max_clk = (value / 1000 / 1000);
-
- value = hl_fw_get_frequency(hdev, hdev->asic_prop.clk_pll_index, true);
-
- if (value < 0) {
- dev_err(hdev->dev, "Failed to retrieve device current clock %ld\n", value);
- return value;
- }
-
- *cur_clk = (value / 1000 / 1000);
-
- return 0;
-}
-
-long hl_fw_get_frequency(struct hl_device *hdev, u32 pll_index, bool curr)
-{
- struct cpucp_packet pkt;
- u32 used_pll_idx;
- u64 result;
- int rc;
-
- rc = get_used_pll_index(hdev, pll_index, &used_pll_idx);
- if (rc)
- return rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- if (curr)
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_CURR_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- else
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_GET << CPUCP_PKT_CTL_OPCODE_SHIFT);
-
- pkt.pll_index = cpu_to_le32((u32)used_pll_idx);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, &result);
-
- if (rc) {
- dev_err(hdev->dev, "Failed to get frequency of PLL %d, error %d\n",
- used_pll_idx, rc);
- return rc;
- }
-
- return (long) result;
-}
-
-void hl_fw_set_frequency(struct hl_device *hdev, u32 pll_index, u64 freq)
-{
- struct cpucp_packet pkt;
- u32 used_pll_idx;
- int rc;
-
- rc = get_used_pll_index(hdev, pll_index, &used_pll_idx);
- if (rc)
- return;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_FREQUENCY_SET << CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.pll_index = cpu_to_le32((u32)used_pll_idx);
- pkt.value = cpu_to_le64(freq);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL);
-
- if (rc)
- dev_err(hdev->dev, "Failed to set frequency to PLL %d, error %d\n",
- used_pll_idx, rc);
-}
-
-long hl_fw_get_max_power(struct hl_device *hdev)
-{
- struct cpucp_packet pkt;
- u64 result;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_MAX_POWER_GET << CPUCP_PKT_CTL_OPCODE_SHIFT);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, &result);
-
- if (rc) {
- dev_err(hdev->dev, "Failed to get max power, error %d\n", rc);
- return rc;
- }
-
- return result;
-}
-
-void hl_fw_set_max_power(struct hl_device *hdev)
-{
- struct cpucp_packet pkt;
- int rc;
-
- /* TODO: remove this after simulator supports this packet */
- if (!hdev->pdev)
- return;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_MAX_POWER_SET << CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.value = cpu_to_le64(hdev->max_power);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, NULL);
-
- if (rc)
- dev_err(hdev->dev, "Failed to set max power, error %d\n", rc);
-}
-
-static int hl_fw_get_sec_attest_data(struct hl_device *hdev, u32 packet_id, void *data, u32 size,
- u32 nonce, u32 timeout)
-{
- struct cpucp_packet pkt = {};
- dma_addr_t req_dma_addr;
- void *req_cpu_addr;
- int rc;
-
- req_cpu_addr = hl_cpu_accessible_dma_pool_alloc(hdev, size, &req_dma_addr);
- if (!req_cpu_addr) {
- dev_err(hdev->dev,
- "Failed to allocate DMA memory for CPU-CP packet %u\n", packet_id);
- return -ENOMEM;
- }
-
- memset(data, 0, size);
-
- pkt.ctl = cpu_to_le32(packet_id << CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.addr = cpu_to_le64(req_dma_addr);
- pkt.data_max_size = cpu_to_le32(size);
- pkt.nonce = cpu_to_le32(nonce);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- timeout, NULL);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to handle CPU-CP pkt %u, error %d\n", packet_id, rc);
- goto out;
- }
-
- memcpy(data, req_cpu_addr, size);
-
-out:
- hl_cpu_accessible_dma_pool_free(hdev, size, req_cpu_addr);
-
- return rc;
-}
-
-int hl_fw_get_sec_attest_info(struct hl_device *hdev, struct cpucp_sec_attest_info *sec_attest_info,
- u32 nonce)
-{
- return hl_fw_get_sec_attest_data(hdev, CPUCP_PACKET_SEC_ATTEST_GET, sec_attest_info,
- sizeof(struct cpucp_sec_attest_info), nonce,
- HL_CPUCP_SEC_ATTEST_INFO_TINEOUT_USEC);
-}
-
-int hl_fw_send_generic_request(struct hl_device *hdev, enum hl_passthrough_type sub_opcode,
- dma_addr_t buff, u32 *size)
-{
- struct cpucp_packet pkt = {0};
- u64 result;
- int rc = 0;
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_GENERIC_PASSTHROUGH << CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.addr = cpu_to_le64(buff);
- pkt.data_max_size = cpu_to_le32(*size);
- pkt.pkt_subidx = cpu_to_le32(sub_opcode);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *)&pkt, sizeof(pkt),
- HL_CPUCP_INFO_TIMEOUT_USEC, &result);
- if (rc)
- dev_err(hdev->dev, "failed to send CPUCP data of generic fw pkt\n");
- else
- dev_dbg(hdev->dev, "generic pkt was successful, result: 0x%llx\n", result);
-
- *size = (u32)result;
-
- return rc;
-}
diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h
deleted file mode 100644
index 7b6f10033ee9..000000000000
--- a/drivers/misc/habanalabs/common/habanalabs.h
+++ /dev/null
@@ -1,4002 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0
- *
- * Copyright 2016-2022 HabanaLabs, Ltd.
- * All Rights Reserved.
- *
- */
-
-#ifndef HABANALABSP_H_
-#define HABANALABSP_H_
-
-#include "../include/common/cpucp_if.h"
-#include "../include/common/qman_if.h"
-#include "../include/hw_ip/mmu/mmu_general.h"
-#include <uapi/drm/habanalabs_accel.h>
-
-#include <linux/cdev.h>
-#include <linux/iopoll.h>
-#include <linux/irqreturn.h>
-#include <linux/dma-direction.h>
-#include <linux/scatterlist.h>
-#include <linux/hashtable.h>
-#include <linux/debugfs.h>
-#include <linux/rwsem.h>
-#include <linux/eventfd.h>
-#include <linux/bitfield.h>
-#include <linux/genalloc.h>
-#include <linux/sched/signal.h>
-#include <linux/io-64-nonatomic-lo-hi.h>
-#include <linux/coresight.h>
-#include <linux/dma-buf.h>
-
-#define HL_NAME "habanalabs"
-
-struct hl_device;
-struct hl_fpriv;
-
-/* Use upper bits of mmap offset to store habana driver specific information.
- * bits[63:59] - Encode mmap type
- * bits[45:0] - mmap offset value
- *
- * NOTE: struct vm_area_struct.vm_pgoff uses offset in pages. Hence, these
- * defines are w.r.t to PAGE_SIZE
- */
-#define HL_MMAP_TYPE_SHIFT (59 - PAGE_SHIFT)
-#define HL_MMAP_TYPE_MASK (0x1full << HL_MMAP_TYPE_SHIFT)
-#define HL_MMAP_TYPE_TS_BUFF (0x10ull << HL_MMAP_TYPE_SHIFT)
-#define HL_MMAP_TYPE_BLOCK (0x4ull << HL_MMAP_TYPE_SHIFT)
-#define HL_MMAP_TYPE_CB (0x2ull << HL_MMAP_TYPE_SHIFT)
-
-#define HL_MMAP_OFFSET_VALUE_MASK (0x1FFFFFFFFFFFull >> PAGE_SHIFT)
-#define HL_MMAP_OFFSET_VALUE_GET(off) (off & HL_MMAP_OFFSET_VALUE_MASK)
-
-#define HL_PENDING_RESET_PER_SEC 10
-#define HL_PENDING_RESET_MAX_TRIALS 60 /* 10 minutes */
-#define HL_PENDING_RESET_LONG_SEC 60
-/*
- * In device fini, wait 10 minutes for user processes to be terminated after we kill them.
- * This is needed to prevent situation of clearing resources while user processes are still alive.
- */
-#define HL_WAIT_PROCESS_KILL_ON_DEVICE_FINI 600
-
-#define HL_HARD_RESET_MAX_TIMEOUT 120
-#define HL_PLDM_HARD_RESET_MAX_TIMEOUT (HL_HARD_RESET_MAX_TIMEOUT * 3)
-
-#define HL_DEVICE_TIMEOUT_USEC 1000000 /* 1 s */
-
-#define HL_HEARTBEAT_PER_USEC 5000000 /* 5 s */
-
-#define HL_PLL_LOW_JOB_FREQ_USEC 5000000 /* 5 s */
-
-#define HL_CPUCP_INFO_TIMEOUT_USEC 10000000 /* 10s */
-#define HL_CPUCP_EEPROM_TIMEOUT_USEC 10000000 /* 10s */
-#define HL_CPUCP_MON_DUMP_TIMEOUT_USEC 10000000 /* 10s */
-#define HL_CPUCP_SEC_ATTEST_INFO_TINEOUT_USEC 10000000 /* 10s */
-
-#define HL_FW_STATUS_POLL_INTERVAL_USEC 10000 /* 10ms */
-#define HL_FW_COMMS_STATUS_PLDM_POLL_INTERVAL_USEC 1000000 /* 1s */
-
-#define HL_PCI_ELBI_TIMEOUT_MSEC 10 /* 10ms */
-
-#define HL_SIM_MAX_TIMEOUT_US 100000000 /* 100s */
-
-#define HL_INVALID_QUEUE UINT_MAX
-
-#define HL_COMMON_USER_CQ_INTERRUPT_ID 0xFFF
-#define HL_COMMON_DEC_INTERRUPT_ID 0xFFE
-
-#define HL_STATE_DUMP_HIST_LEN 5
-
-/* Default value for device reset trigger , an invalid value */
-#define HL_RESET_TRIGGER_DEFAULT 0xFF
-
-#define OBJ_NAMES_HASH_TABLE_BITS 7 /* 1 << 7 buckets */
-#define SYNC_TO_ENGINE_HASH_TABLE_BITS 7 /* 1 << 7 buckets */
-
-/* Memory */
-#define MEM_HASH_TABLE_BITS 7 /* 1 << 7 buckets */
-
-/* MMU */
-#define MMU_HASH_TABLE_BITS 7 /* 1 << 7 buckets */
-
-/**
- * enum hl_mmu_page_table_location - mmu page table location
- * @MMU_DR_PGT: page-table is located on device DRAM.
- * @MMU_HR_PGT: page-table is located on host memory.
- * @MMU_NUM_PGT_LOCATIONS: number of page-table locations currently supported.
- */
-enum hl_mmu_page_table_location {
- MMU_DR_PGT = 0, /* device-dram-resident MMU PGT */
- MMU_HR_PGT, /* host resident MMU PGT */
- MMU_NUM_PGT_LOCATIONS /* num of PGT locations */
-};
-
-/**
- * enum hl_mmu_enablement - what mmu modules to enable
- * @MMU_EN_NONE: mmu disabled.
- * @MMU_EN_ALL: enable all.
- * @MMU_EN_PMMU_ONLY: Enable only the PMMU leaving the DMMU disabled.
- */
-enum hl_mmu_enablement {
- MMU_EN_NONE = 0,
- MMU_EN_ALL = 1,
- MMU_EN_PMMU_ONLY = 3, /* N/A for Goya/Gaudi */
-};
-
-/*
- * HL_RSVD_SOBS 'sync stream' reserved sync objects per QMAN stream
- * HL_RSVD_MONS 'sync stream' reserved monitors per QMAN stream
- */
-#define HL_RSVD_SOBS 2
-#define HL_RSVD_MONS 1
-
-/*
- * HL_COLLECTIVE_RSVD_MSTR_MONS 'collective' reserved monitors per QMAN stream
- */
-#define HL_COLLECTIVE_RSVD_MSTR_MONS 2
-
-#define HL_MAX_SOB_VAL (1 << 15)
-
-#define IS_POWER_OF_2(n) (n != 0 && ((n & (n - 1)) == 0))
-#define IS_MAX_PENDING_CS_VALID(n) (IS_POWER_OF_2(n) && (n > 1))
-
-#define HL_PCI_NUM_BARS 6
-
-/* Completion queue entry relates to completed job */
-#define HL_COMPLETION_MODE_JOB 0
-/* Completion queue entry relates to completed command submission */
-#define HL_COMPLETION_MODE_CS 1
-
-#define HL_MAX_DCORES 8
-
-/* DMA alloc/free wrappers */
-#define hl_asic_dma_alloc_coherent(hdev, size, dma_handle, flags) \
- hl_asic_dma_alloc_coherent_caller(hdev, size, dma_handle, flags, __func__)
-
-#define hl_cpu_accessible_dma_pool_alloc(hdev, size, dma_handle) \
- hl_cpu_accessible_dma_pool_alloc_caller(hdev, size, dma_handle, __func__)
-
-#define hl_asic_dma_pool_zalloc(hdev, size, mem_flags, dma_handle) \
- hl_asic_dma_pool_zalloc_caller(hdev, size, mem_flags, dma_handle, __func__)
-
-#define hl_asic_dma_free_coherent(hdev, size, cpu_addr, dma_handle) \
- hl_asic_dma_free_coherent_caller(hdev, size, cpu_addr, dma_handle, __func__)
-
-#define hl_cpu_accessible_dma_pool_free(hdev, size, vaddr) \
- hl_cpu_accessible_dma_pool_free_caller(hdev, size, vaddr, __func__)
-
-#define hl_asic_dma_pool_free(hdev, vaddr, dma_addr) \
- hl_asic_dma_pool_free_caller(hdev, vaddr, dma_addr, __func__)
-
-/*
- * Reset Flags
- *
- * - HL_DRV_RESET_HARD
- * If set do hard reset to all engines. If not set reset just
- * compute/DMA engines.
- *
- * - HL_DRV_RESET_FROM_RESET_THR
- * Set if the caller is the hard-reset thread
- *
- * - HL_DRV_RESET_HEARTBEAT
- * Set if reset is due to heartbeat
- *
- * - HL_DRV_RESET_TDR
- * Set if reset is due to TDR
- *
- * - HL_DRV_RESET_DEV_RELEASE
- * Set if reset is due to device release
- *
- * - HL_DRV_RESET_BYPASS_REQ_TO_FW
- * F/W will perform the reset. No need to ask it to reset the device. This is relevant
- * only when running with secured f/w
- *
- * - HL_DRV_RESET_FW_FATAL_ERR
- * Set if reset is due to a fatal error from FW
- *
- * - HL_DRV_RESET_DELAY
- * Set if a delay should be added before the reset
- *
- * - HL_DRV_RESET_FROM_WD_THR
- * Set if the caller is the device release watchdog thread
- */
-
-#define HL_DRV_RESET_HARD (1 << 0)
-#define HL_DRV_RESET_FROM_RESET_THR (1 << 1)
-#define HL_DRV_RESET_HEARTBEAT (1 << 2)
-#define HL_DRV_RESET_TDR (1 << 3)
-#define HL_DRV_RESET_DEV_RELEASE (1 << 4)
-#define HL_DRV_RESET_BYPASS_REQ_TO_FW (1 << 5)
-#define HL_DRV_RESET_FW_FATAL_ERR (1 << 6)
-#define HL_DRV_RESET_DELAY (1 << 7)
-#define HL_DRV_RESET_FROM_WD_THR (1 << 8)
-
-/*
- * Security
- */
-
-#define HL_PB_SHARED 1
-#define HL_PB_NA 0
-#define HL_PB_SINGLE_INSTANCE 1
-#define HL_BLOCK_SIZE 0x1000
-#define HL_BLOCK_GLBL_ERR_MASK 0xF40
-#define HL_BLOCK_GLBL_ERR_ADDR 0xF44
-#define HL_BLOCK_GLBL_ERR_CAUSE 0xF48
-#define HL_BLOCK_GLBL_SEC_OFFS 0xF80
-#define HL_BLOCK_GLBL_SEC_SIZE (HL_BLOCK_SIZE - HL_BLOCK_GLBL_SEC_OFFS)
-#define HL_BLOCK_GLBL_SEC_LEN (HL_BLOCK_GLBL_SEC_SIZE / sizeof(u32))
-#define UNSET_GLBL_SEC_BIT(array, b) ((array)[((b) / 32)] |= (1 << ((b) % 32)))
-
-enum hl_protection_levels {
- SECURED_LVL,
- PRIVILEGED_LVL,
- NON_SECURED_LVL
-};
-
-/**
- * struct iterate_module_ctx - HW module iterator
- * @fn: function to apply to each HW module instance
- * @data: optional internal data to the function iterator
- * @rc: return code for optional use of iterator/iterator-caller
- */
-struct iterate_module_ctx {
- /*
- * callback for the HW module iterator
- * @hdev: pointer to the habanalabs device structure
- * @block: block (ASIC specific definition can be dcore/hdcore)
- * @inst: HW module instance within the block
- * @offset: current HW module instance offset from the 1-st HW module instance
- * in the 1-st block
- * @ctx: the iterator context.
- */
- void (*fn)(struct hl_device *hdev, int block, int inst, u32 offset,
- struct iterate_module_ctx *ctx);
- void *data;
- int rc;
-};
-
-struct hl_block_glbl_sec {
- u32 sec_array[HL_BLOCK_GLBL_SEC_LEN];
-};
-
-#define HL_MAX_SOBS_PER_MONITOR 8
-
-/**
- * struct hl_gen_wait_properties - properties for generating a wait CB
- * @data: command buffer
- * @q_idx: queue id is used to extract fence register address
- * @size: offset in command buffer
- * @sob_base: SOB base to use in this wait CB
- * @sob_val: SOB value to wait for
- * @mon_id: monitor to use in this wait CB
- * @sob_mask: each bit represents a SOB offset from sob_base to be used
- */
-struct hl_gen_wait_properties {
- void *data;
- u32 q_idx;
- u32 size;
- u16 sob_base;
- u16 sob_val;
- u16 mon_id;
- u8 sob_mask;
-};
-
-/**
- * struct pgt_info - MMU hop page info.
- * @node: hash linked-list node for the pgts on host (shadow pgts for device resident MMU and
- * actual pgts for host resident MMU).
- * @phys_addr: physical address of the pgt.
- * @virt_addr: host virtual address of the pgt (see above device/host resident).
- * @shadow_addr: shadow hop in the host for device resident MMU.
- * @ctx: pointer to the owner ctx.
- * @num_of_ptes: indicates how many ptes are used in the pgt. used only for dynamically
- * allocated HOPs (all HOPs but HOP0)
- *
- * The MMU page tables hierarchy can be placed either on the device's DRAM (in which case shadow
- * pgts will be stored on host memory) or on host memory (in which case no shadow is required).
- *
- * When a new level (hop) is needed during mapping this structure will be used to describe
- * the newly allocated hop as well as to track number of PTEs in it.
- * During unmapping, if no valid PTEs remained in the page of a newly allocated hop, it is
- * freed with its pgt_info structure.
- */
-struct pgt_info {
- struct hlist_node node;
- u64 phys_addr;
- u64 virt_addr;
- u64 shadow_addr;
- struct hl_ctx *ctx;
- int num_of_ptes;
-};
-
-/**
- * enum hl_pci_match_mode - pci match mode per region
- * @PCI_ADDRESS_MATCH_MODE: address match mode
- * @PCI_BAR_MATCH_MODE: bar match mode
- */
-enum hl_pci_match_mode {
- PCI_ADDRESS_MATCH_MODE,
- PCI_BAR_MATCH_MODE
-};
-
-/**
- * enum hl_fw_component - F/W components to read version through registers.
- * @FW_COMP_BOOT_FIT: boot fit.
- * @FW_COMP_PREBOOT: preboot.
- * @FW_COMP_LINUX: linux.
- */
-enum hl_fw_component {
- FW_COMP_BOOT_FIT,
- FW_COMP_PREBOOT,
- FW_COMP_LINUX,
-};
-
-/**
- * enum hl_fw_types - F/W types present in the system
- * @FW_TYPE_NONE: no FW component indication
- * @FW_TYPE_LINUX: Linux image for device CPU
- * @FW_TYPE_BOOT_CPU: Boot image for device CPU
- * @FW_TYPE_PREBOOT_CPU: Indicates pre-loaded CPUs are present in the system
- * (preboot, ppboot etc...)
- * @FW_TYPE_ALL_TYPES: Mask for all types
- */
-enum hl_fw_types {
- FW_TYPE_NONE = 0x0,
- FW_TYPE_LINUX = 0x1,
- FW_TYPE_BOOT_CPU = 0x2,
- FW_TYPE_PREBOOT_CPU = 0x4,
- FW_TYPE_ALL_TYPES =
- (FW_TYPE_LINUX | FW_TYPE_BOOT_CPU | FW_TYPE_PREBOOT_CPU)
-};
-
-/**
- * enum hl_queue_type - Supported QUEUE types.
- * @QUEUE_TYPE_NA: queue is not available.
- * @QUEUE_TYPE_EXT: external queue which is a DMA channel that may access the
- * host.
- * @QUEUE_TYPE_INT: internal queue that performs DMA inside the device's
- * memories and/or operates the compute engines.
- * @QUEUE_TYPE_CPU: S/W queue for communication with the device's CPU.
- * @QUEUE_TYPE_HW: queue of DMA and compute engines jobs, for which completion
- * notifications are sent by H/W.
- */
-enum hl_queue_type {
- QUEUE_TYPE_NA,
- QUEUE_TYPE_EXT,
- QUEUE_TYPE_INT,
- QUEUE_TYPE_CPU,
- QUEUE_TYPE_HW
-};
-
-enum hl_cs_type {
- CS_TYPE_DEFAULT,
- CS_TYPE_SIGNAL,
- CS_TYPE_WAIT,
- CS_TYPE_COLLECTIVE_WAIT,
- CS_RESERVE_SIGNALS,
- CS_UNRESERVE_SIGNALS,
- CS_TYPE_ENGINE_CORE
-};
-
-/*
- * struct hl_inbound_pci_region - inbound region descriptor
- * @mode: pci match mode for this region
- * @addr: region target address
- * @size: region size in bytes
- * @offset_in_bar: offset within bar (address match mode)
- * @bar: bar id
- */
-struct hl_inbound_pci_region {
- enum hl_pci_match_mode mode;
- u64 addr;
- u64 size;
- u64 offset_in_bar;
- u8 bar;
-};
-
-/*
- * struct hl_outbound_pci_region - outbound region descriptor
- * @addr: region target address
- * @size: region size in bytes
- */
-struct hl_outbound_pci_region {
- u64 addr;
- u64 size;
-};
-
-/*
- * enum queue_cb_alloc_flags - Indicates queue support for CBs that
- * allocated by Kernel or by User
- * @CB_ALLOC_KERNEL: support only CBs that allocated by Kernel
- * @CB_ALLOC_USER: support only CBs that allocated by User
- */
-enum queue_cb_alloc_flags {
- CB_ALLOC_KERNEL = 0x1,
- CB_ALLOC_USER = 0x2
-};
-
-/*
- * struct hl_hw_sob - H/W SOB info.
- * @hdev: habanalabs device structure.
- * @kref: refcount of this SOB. The SOB will reset once the refcount is zero.
- * @sob_id: id of this SOB.
- * @sob_addr: the sob offset from the base address.
- * @q_idx: the H/W queue that uses this SOB.
- * @need_reset: reset indication set when switching to the other sob.
- */
-struct hl_hw_sob {
- struct hl_device *hdev;
- struct kref kref;
- u32 sob_id;
- u32 sob_addr;
- u32 q_idx;
- bool need_reset;
-};
-
-enum hl_collective_mode {
- HL_COLLECTIVE_NOT_SUPPORTED = 0x0,
- HL_COLLECTIVE_MASTER = 0x1,
- HL_COLLECTIVE_SLAVE = 0x2
-};
-
-/**
- * struct hw_queue_properties - queue information.
- * @type: queue type.
- * @cb_alloc_flags: bitmap which indicates if the hw queue supports CB
- * that allocated by the Kernel driver and therefore,
- * a CB handle can be provided for jobs on this queue.
- * Otherwise, a CB address must be provided.
- * @collective_mode: collective mode of current queue
- * @driver_only: true if only the driver is allowed to send a job to this queue,
- * false otherwise.
- * @binned: True if the queue is binned out and should not be used
- * @supports_sync_stream: True if queue supports sync stream
- */
-struct hw_queue_properties {
- enum hl_queue_type type;
- enum queue_cb_alloc_flags cb_alloc_flags;
- enum hl_collective_mode collective_mode;
- u8 driver_only;
- u8 binned;
- u8 supports_sync_stream;
-};
-
-/**
- * enum vm_type - virtual memory mapping request information.
- * @VM_TYPE_USERPTR: mapping of user memory to device virtual address.
- * @VM_TYPE_PHYS_PACK: mapping of DRAM memory to device virtual address.
- */
-enum vm_type {
- VM_TYPE_USERPTR = 0x1,
- VM_TYPE_PHYS_PACK = 0x2
-};
-
-/**
- * enum mmu_op_flags - mmu operation relevant information.
- * @MMU_OP_USERPTR: operation on user memory (host resident).
- * @MMU_OP_PHYS_PACK: operation on DRAM (device resident).
- * @MMU_OP_CLEAR_MEMCACHE: operation has to clear memcache.
- * @MMU_OP_SKIP_LOW_CACHE_INV: operation is allowed to skip parts of cache invalidation.
- */
-enum mmu_op_flags {
- MMU_OP_USERPTR = 0x1,
- MMU_OP_PHYS_PACK = 0x2,
- MMU_OP_CLEAR_MEMCACHE = 0x4,
- MMU_OP_SKIP_LOW_CACHE_INV = 0x8,
-};
-
-
-/**
- * enum hl_device_hw_state - H/W device state. use this to understand whether
- * to do reset before hw_init or not
- * @HL_DEVICE_HW_STATE_CLEAN: H/W state is clean. i.e. after hard reset
- * @HL_DEVICE_HW_STATE_DIRTY: H/W state is dirty. i.e. we started to execute
- * hw_init
- */
-enum hl_device_hw_state {
- HL_DEVICE_HW_STATE_CLEAN = 0,
- HL_DEVICE_HW_STATE_DIRTY
-};
-
-#define HL_MMU_VA_ALIGNMENT_NOT_NEEDED 0
-
-/**
- * struct hl_mmu_properties - ASIC specific MMU address translation properties.
- * @start_addr: virtual start address of the memory region.
- * @end_addr: virtual end address of the memory region.
- * @hop_shifts: array holds HOPs shifts.
- * @hop_masks: array holds HOPs masks.
- * @last_mask: mask to get the bit indicating this is the last hop.
- * @pgt_size: size for page tables.
- * @supported_pages_mask: bitmask for supported page size (relevant only for MMUs
- * supporting multiple page size).
- * @page_size: default page size used to allocate memory.
- * @num_hops: The amount of hops supported by the translation table.
- * @hop_table_size: HOP table size.
- * @hop0_tables_total_size: total size for all HOP0 tables.
- * @host_resident: Should the MMU page table reside in host memory or in the
- * device DRAM.
- */
-struct hl_mmu_properties {
- u64 start_addr;
- u64 end_addr;
- u64 hop_shifts[MMU_HOP_MAX];
- u64 hop_masks[MMU_HOP_MAX];
- u64 last_mask;
- u64 pgt_size;
- u64 supported_pages_mask;
- u32 page_size;
- u32 num_hops;
- u32 hop_table_size;
- u32 hop0_tables_total_size;
- u8 host_resident;
-};
-
-/**
- * struct hl_hints_range - hint addresses reserved va range.
- * @start_addr: start address of the va range.
- * @end_addr: end address of the va range.
- */
-struct hl_hints_range {
- u64 start_addr;
- u64 end_addr;
-};
-
-/**
- * struct asic_fixed_properties - ASIC specific immutable properties.
- * @hw_queues_props: H/W queues properties.
- * @cpucp_info: received various information from CPU-CP regarding the H/W, e.g.
- * available sensors.
- * @uboot_ver: F/W U-boot version.
- * @preboot_ver: F/W Preboot version.
- * @dmmu: DRAM MMU address translation properties.
- * @pmmu: PCI (host) MMU address translation properties.
- * @pmmu_huge: PCI (host) MMU address translation properties for memory
- * allocated with huge pages.
- * @hints_dram_reserved_va_range: dram hint addresses reserved range.
- * @hints_host_reserved_va_range: host hint addresses reserved range.
- * @hints_host_hpage_reserved_va_range: host huge page hint addresses reserved
- * range.
- * @sram_base_address: SRAM physical start address.
- * @sram_end_address: SRAM physical end address.
- * @sram_user_base_address - SRAM physical start address for user access.
- * @dram_base_address: DRAM physical start address.
- * @dram_end_address: DRAM physical end address.
- * @dram_user_base_address: DRAM physical start address for user access.
- * @dram_size: DRAM total size.
- * @dram_pci_bar_size: size of PCI bar towards DRAM.
- * @max_power_default: max power of the device after reset.
- * @dc_power_default: power consumed by the device in mode idle.
- * @dram_size_for_default_page_mapping: DRAM size needed to map to avoid page
- * fault.
- * @pcie_dbi_base_address: Base address of the PCIE_DBI block.
- * @pcie_aux_dbi_reg_addr: Address of the PCIE_AUX DBI register.
- * @mmu_pgt_addr: base physical address in DRAM of MMU page tables.
- * @mmu_dram_default_page_addr: DRAM default page physical address.
- * @tpc_enabled_mask: which TPCs are enabled.
- * @tpc_binning_mask: which TPCs are binned. 0 means usable and 1 means binned.
- * @dram_enabled_mask: which DRAMs are enabled.
- * @dram_binning_mask: which DRAMs are binned. 0 means usable, 1 means binned.
- * @dram_hints_align_mask: dram va hint addresses alignment mask which is used
- * for hints validity check.
- * @cfg_base_address: config space base address.
- * @mmu_cache_mng_addr: address of the MMU cache.
- * @mmu_cache_mng_size: size of the MMU cache.
- * @device_dma_offset_for_host_access: the offset to add to host DMA addresses
- * to enable the device to access them.
- * @host_base_address: host physical start address for host DMA from device
- * @host_end_address: host physical end address for host DMA from device
- * @max_freq_value: current max clk frequency.
- * @clk_pll_index: clock PLL index that specify which PLL determines the clock
- * we display to the user
- * @mmu_pgt_size: MMU page tables total size.
- * @mmu_pte_size: PTE size in MMU page tables.
- * @mmu_hop_table_size: MMU hop table size.
- * @mmu_hop0_tables_total_size: total size of MMU hop0 tables.
- * @dram_page_size: page size for MMU DRAM allocation.
- * @cfg_size: configuration space size on SRAM.
- * @sram_size: total size of SRAM.
- * @max_asid: maximum number of open contexts (ASIDs).
- * @num_of_events: number of possible internal H/W IRQs.
- * @psoc_pci_pll_nr: PCI PLL NR value.
- * @psoc_pci_pll_nf: PCI PLL NF value.
- * @psoc_pci_pll_od: PCI PLL OD value.
- * @psoc_pci_pll_div_factor: PCI PLL DIV FACTOR 1 value.
- * @psoc_timestamp_frequency: frequency of the psoc timestamp clock.
- * @high_pll: high PLL frequency used by the device.
- * @cb_pool_cb_cnt: number of CBs in the CB pool.
- * @cb_pool_cb_size: size of each CB in the CB pool.
- * @decoder_enabled_mask: which decoders are enabled.
- * @decoder_binning_mask: which decoders are binned, 0 means usable and 1
- * means binned (at most one binned decoder per dcore).
- * @edma_enabled_mask: which EDMAs are enabled.
- * @edma_binning_mask: which EDMAs are binned, 0 means usable and 1 means
- * binned (at most one binned DMA).
- * @max_pending_cs: maximum of concurrent pending command submissions
- * @max_queues: maximum amount of queues in the system
- * @fw_preboot_cpu_boot_dev_sts0: bitmap representation of preboot cpu
- * capabilities reported by FW, bit description
- * can be found in CPU_BOOT_DEV_STS0
- * @fw_preboot_cpu_boot_dev_sts1: bitmap representation of preboot cpu
- * capabilities reported by FW, bit description
- * can be found in CPU_BOOT_DEV_STS1
- * @fw_bootfit_cpu_boot_dev_sts0: bitmap representation of boot cpu security
- * status reported by FW, bit description can be
- * found in CPU_BOOT_DEV_STS0
- * @fw_bootfit_cpu_boot_dev_sts1: bitmap representation of boot cpu security
- * status reported by FW, bit description can be
- * found in CPU_BOOT_DEV_STS1
- * @fw_app_cpu_boot_dev_sts0: bitmap representation of application security
- * status reported by FW, bit description can be
- * found in CPU_BOOT_DEV_STS0
- * @fw_app_cpu_boot_dev_sts1: bitmap representation of application security
- * status reported by FW, bit description can be
- * found in CPU_BOOT_DEV_STS1
- * @max_dec: maximum number of decoders
- * @hmmu_hif_enabled_mask: mask of HMMUs/HIFs that are not isolated (enabled)
- * 1- enabled, 0- isolated.
- * @faulty_dram_cluster_map: mask of faulty DRAM cluster.
- * 1- faulty cluster, 0- good cluster.
- * @xbar_edge_enabled_mask: mask of XBAR_EDGEs that are not isolated (enabled)
- * 1- enabled, 0- isolated.
- * @device_mem_alloc_default_page_size: may be different than dram_page_size only for ASICs for
- * which the property supports_user_set_page_size is true
- * (i.e. the DRAM supports multiple page sizes), otherwise
- * it will shall be equal to dram_page_size.
- * @num_engine_cores: number of engine cpu cores
- * @collective_first_sob: first sync object available for collective use
- * @collective_first_mon: first monitor available for collective use
- * @sync_stream_first_sob: first sync object available for sync stream use
- * @sync_stream_first_mon: first monitor available for sync stream use
- * @first_available_user_sob: first sob available for the user
- * @first_available_user_mon: first monitor available for the user
- * @first_available_user_interrupt: first available interrupt reserved for the user
- * @first_available_cq: first available CQ for the user.
- * @user_interrupt_count: number of user interrupts.
- * @user_dec_intr_count: number of decoder interrupts exposed to user.
- * @cache_line_size: device cache line size.
- * @server_type: Server type that the ASIC is currently installed in.
- * The value is according to enum hl_server_type in uapi file.
- * @completion_queues_count: number of completion queues.
- * @completion_mode: 0 - job based completion, 1 - cs based completion
- * @mme_master_slave_mode: 0 - Each MME works independently, 1 - MME works
- * in Master/Slave mode
- * @fw_security_enabled: true if security measures are enabled in firmware,
- * false otherwise
- * @fw_cpu_boot_dev_sts0_valid: status bits are valid and can be fetched from
- * BOOT_DEV_STS0
- * @fw_cpu_boot_dev_sts1_valid: status bits are valid and can be fetched from
- * BOOT_DEV_STS1
- * @dram_supports_virtual_memory: is there an MMU towards the DRAM
- * @hard_reset_done_by_fw: true if firmware is handling hard reset flow
- * @num_functional_hbms: number of functional HBMs in each DCORE.
- * @hints_range_reservation: device support hint addresses range reservation.
- * @iatu_done_by_fw: true if iATU configuration is being done by FW.
- * @dynamic_fw_load: is dynamic FW load is supported.
- * @gic_interrupts_enable: true if FW is not blocking GIC controller,
- * false otherwise.
- * @use_get_power_for_reset_history: To support backward compatibility for Goya
- * and Gaudi
- * @supports_compute_reset: is a reset which is not a hard-reset supported by this asic.
- * @allow_inference_soft_reset: true if the ASIC supports soft reset that is
- * initiated by user or TDR. This is only true
- * in inference ASICs, as there is no real-world
- * use-case of doing soft-reset in training (due
- * to the fact that training runs on multiple
- * devices)
- * @configurable_stop_on_err: is stop-on-error option configurable via debugfs.
- * @set_max_power_on_device_init: true if need to set max power in F/W on device init.
- * @supports_user_set_page_size: true if user can set the allocation page size.
- * @dma_mask: the dma mask to be set for this device
- * @supports_advanced_cpucp_rc: true if new cpucp opcodes are supported.
- */
-struct asic_fixed_properties {
- struct hw_queue_properties *hw_queues_props;
- struct cpucp_info cpucp_info;
- char uboot_ver[VERSION_MAX_LEN];
- char preboot_ver[VERSION_MAX_LEN];
- struct hl_mmu_properties dmmu;
- struct hl_mmu_properties pmmu;
- struct hl_mmu_properties pmmu_huge;
- struct hl_hints_range hints_dram_reserved_va_range;
- struct hl_hints_range hints_host_reserved_va_range;
- struct hl_hints_range hints_host_hpage_reserved_va_range;
- u64 sram_base_address;
- u64 sram_end_address;
- u64 sram_user_base_address;
- u64 dram_base_address;
- u64 dram_end_address;
- u64 dram_user_base_address;
- u64 dram_size;
- u64 dram_pci_bar_size;
- u64 max_power_default;
- u64 dc_power_default;
- u64 dram_size_for_default_page_mapping;
- u64 pcie_dbi_base_address;
- u64 pcie_aux_dbi_reg_addr;
- u64 mmu_pgt_addr;
- u64 mmu_dram_default_page_addr;
- u64 tpc_enabled_mask;
- u64 tpc_binning_mask;
- u64 dram_enabled_mask;
- u64 dram_binning_mask;
- u64 dram_hints_align_mask;
- u64 cfg_base_address;
- u64 mmu_cache_mng_addr;
- u64 mmu_cache_mng_size;
- u64 device_dma_offset_for_host_access;
- u64 host_base_address;
- u64 host_end_address;
- u64 max_freq_value;
- u32 clk_pll_index;
- u32 mmu_pgt_size;
- u32 mmu_pte_size;
- u32 mmu_hop_table_size;
- u32 mmu_hop0_tables_total_size;
- u32 dram_page_size;
- u32 cfg_size;
- u32 sram_size;
- u32 max_asid;
- u32 num_of_events;
- u32 psoc_pci_pll_nr;
- u32 psoc_pci_pll_nf;
- u32 psoc_pci_pll_od;
- u32 psoc_pci_pll_div_factor;
- u32 psoc_timestamp_frequency;
- u32 high_pll;
- u32 cb_pool_cb_cnt;
- u32 cb_pool_cb_size;
- u32 decoder_enabled_mask;
- u32 decoder_binning_mask;
- u32 edma_enabled_mask;
- u32 edma_binning_mask;
- u32 max_pending_cs;
- u32 max_queues;
- u32 fw_preboot_cpu_boot_dev_sts0;
- u32 fw_preboot_cpu_boot_dev_sts1;
- u32 fw_bootfit_cpu_boot_dev_sts0;
- u32 fw_bootfit_cpu_boot_dev_sts1;
- u32 fw_app_cpu_boot_dev_sts0;
- u32 fw_app_cpu_boot_dev_sts1;
- u32 max_dec;
- u32 hmmu_hif_enabled_mask;
- u32 faulty_dram_cluster_map;
- u32 xbar_edge_enabled_mask;
- u32 device_mem_alloc_default_page_size;
- u32 num_engine_cores;
- u16 collective_first_sob;
- u16 collective_first_mon;
- u16 sync_stream_first_sob;
- u16 sync_stream_first_mon;
- u16 first_available_user_sob[HL_MAX_DCORES];
- u16 first_available_user_mon[HL_MAX_DCORES];
- u16 first_available_user_interrupt;
- u16 first_available_cq[HL_MAX_DCORES];
- u16 user_interrupt_count;
- u16 user_dec_intr_count;
- u16 cache_line_size;
- u16 server_type;
- u8 completion_queues_count;
- u8 completion_mode;
- u8 mme_master_slave_mode;
- u8 fw_security_enabled;
- u8 fw_cpu_boot_dev_sts0_valid;
- u8 fw_cpu_boot_dev_sts1_valid;
- u8 dram_supports_virtual_memory;
- u8 hard_reset_done_by_fw;
- u8 num_functional_hbms;
- u8 hints_range_reservation;
- u8 iatu_done_by_fw;
- u8 dynamic_fw_load;
- u8 gic_interrupts_enable;
- u8 use_get_power_for_reset_history;
- u8 supports_compute_reset;
- u8 allow_inference_soft_reset;
- u8 configurable_stop_on_err;
- u8 set_max_power_on_device_init;
- u8 supports_user_set_page_size;
- u8 dma_mask;
- u8 supports_advanced_cpucp_rc;
-};
-
-/**
- * struct hl_fence - software synchronization primitive
- * @completion: fence is implemented using completion
- * @refcount: refcount for this fence
- * @cs_sequence: sequence of the corresponding command submission
- * @stream_master_qid_map: streams masters QID bitmap to represent all streams
- * masters QIDs that multi cs is waiting on
- * @error: mark this fence with error
- * @timestamp: timestamp upon completion
- * @mcs_handling_done: indicates that corresponding command submission has
- * finished msc handling, this does not mean it was part
- * of the mcs
- */
-struct hl_fence {
- struct completion completion;
- struct kref refcount;
- u64 cs_sequence;
- u32 stream_master_qid_map;
- int error;
- ktime_t timestamp;
- u8 mcs_handling_done;
-};
-
-/**
- * struct hl_cs_compl - command submission completion object.
- * @base_fence: hl fence object.
- * @lock: spinlock to protect fence.
- * @hdev: habanalabs device structure.
- * @hw_sob: the H/W SOB used in this signal/wait CS.
- * @encaps_sig_hdl: encaps signals handler.
- * @cs_seq: command submission sequence number.
- * @type: type of the CS - signal/wait.
- * @sob_val: the SOB value that is used in this signal/wait CS.
- * @sob_group: the SOB group that is used in this collective wait CS.
- * @encaps_signals: indication whether it's a completion object of cs with
- * encaps signals or not.
- */
-struct hl_cs_compl {
- struct hl_fence base_fence;
- spinlock_t lock;
- struct hl_device *hdev;
- struct hl_hw_sob *hw_sob;
- struct hl_cs_encaps_sig_handle *encaps_sig_hdl;
- u64 cs_seq;
- enum hl_cs_type type;
- u16 sob_val;
- u16 sob_group;
- bool encaps_signals;
-};
-
-/*
- * Command Buffers
- */
-
-/**
- * struct hl_ts_buff - describes a timestamp buffer.
- * @kernel_buff_address: Holds the internal buffer's kernel virtual address.
- * @user_buff_address: Holds the user buffer's kernel virtual address.
- * @kernel_buff_size: Holds the internal kernel buffer size.
- */
-struct hl_ts_buff {
- void *kernel_buff_address;
- void *user_buff_address;
- u32 kernel_buff_size;
-};
-
-struct hl_mmap_mem_buf;
-
-/**
- * struct hl_mem_mgr - describes unified memory manager for mappable memory chunks.
- * @dev: back pointer to the owning device
- * @lock: protects handles
- * @handles: an idr holding all active handles to the memory buffers in the system.
- * @is_kernel_mem_mgr: indicate whether the memory manager is the per-device kernel memory manager
- */
-struct hl_mem_mgr {
- struct device *dev;
- spinlock_t lock;
- struct idr handles;
- u8 is_kernel_mem_mgr;
-};
-
-/**
- * struct hl_mmap_mem_buf_behavior - describes unified memory manager buffer behavior
- * @topic: string identifier used for logging
- * @mem_id: memory type identifier, embedded in the handle and used to identify
- * the memory type by handle.
- * @alloc: callback executed on buffer allocation, shall allocate the memory,
- * set it under buffer private, and set mappable size.
- * @mmap: callback executed on mmap, must map the buffer to vma
- * @release: callback executed on release, must free the resources used by the buffer
- */
-struct hl_mmap_mem_buf_behavior {
- const char *topic;
- u64 mem_id;
-
- int (*alloc)(struct hl_mmap_mem_buf *buf, gfp_t gfp, void *args);
- int (*mmap)(struct hl_mmap_mem_buf *buf, struct vm_area_struct *vma, void *args);
- void (*release)(struct hl_mmap_mem_buf *buf);
-};
-
-/**
- * struct hl_mmap_mem_buf - describes a single unified memory buffer
- * @behavior: buffer behavior
- * @mmg: back pointer to the unified memory manager
- * @refcount: reference counter for buffer users
- * @private: pointer to buffer behavior private data
- * @mmap: atomic boolean indicating whether or not the buffer is mapped right now
- * @real_mapped_size: the actual size of buffer mapped, after part of it may be released,
- * may change at runtime.
- * @mappable_size: the original mappable size of the buffer, does not change after
- * the allocation.
- * @handle: the buffer id in mmg handles store
- */
-struct hl_mmap_mem_buf {
- struct hl_mmap_mem_buf_behavior *behavior;
- struct hl_mem_mgr *mmg;
- struct kref refcount;
- void *private;
- atomic_t mmap;
- u64 real_mapped_size;
- u64 mappable_size;
- u64 handle;
-};
-
-/**
- * struct hl_cb - describes a Command Buffer.
- * @hdev: pointer to device this CB belongs to.
- * @ctx: pointer to the CB owner's context.
- * @buf: back pointer to the parent mappable memory buffer
- * @debugfs_list: node in debugfs list of command buffers.
- * @pool_list: node in pool list of command buffers.
- * @kernel_address: Holds the CB's kernel virtual address.
- * @virtual_addr: Holds the CB's virtual address.
- * @bus_address: Holds the CB's DMA address.
- * @size: holds the CB's size.
- * @roundup_size: holds the cb size after roundup to page size.
- * @cs_cnt: holds number of CS that this CB participates in.
- * @is_handle_destroyed: atomic boolean indicating whether or not the CB handle was destroyed.
- * @is_pool: true if CB was acquired from the pool, false otherwise.
- * @is_internal: internally allocated
- * @is_mmu_mapped: true if the CB is mapped to the device's MMU.
- */
-struct hl_cb {
- struct hl_device *hdev;
- struct hl_ctx *ctx;
- struct hl_mmap_mem_buf *buf;
- struct list_head debugfs_list;
- struct list_head pool_list;
- void *kernel_address;
- u64 virtual_addr;
- dma_addr_t bus_address;
- u32 size;
- u32 roundup_size;
- atomic_t cs_cnt;
- atomic_t is_handle_destroyed;
- u8 is_pool;
- u8 is_internal;
- u8 is_mmu_mapped;
-};
-
-
-/*
- * QUEUES
- */
-
-struct hl_cs_job;
-
-/* Queue length of external and HW queues */
-#define HL_QUEUE_LENGTH 4096
-#define HL_QUEUE_SIZE_IN_BYTES (HL_QUEUE_LENGTH * HL_BD_SIZE)
-
-#if (HL_MAX_JOBS_PER_CS > HL_QUEUE_LENGTH)
-#error "HL_QUEUE_LENGTH must be greater than HL_MAX_JOBS_PER_CS"
-#endif
-
-/* HL_CQ_LENGTH is in units of struct hl_cq_entry */
-#define HL_CQ_LENGTH HL_QUEUE_LENGTH
-#define HL_CQ_SIZE_IN_BYTES (HL_CQ_LENGTH * HL_CQ_ENTRY_SIZE)
-
-/* Must be power of 2 */
-#define HL_EQ_LENGTH 64
-#define HL_EQ_SIZE_IN_BYTES (HL_EQ_LENGTH * HL_EQ_ENTRY_SIZE)
-
-/* Host <-> CPU-CP shared memory size */
-#define HL_CPU_ACCESSIBLE_MEM_SIZE SZ_2M
-
-/**
- * struct hl_sync_stream_properties -
- * describes a H/W queue sync stream properties
- * @hw_sob: array of the used H/W SOBs by this H/W queue.
- * @next_sob_val: the next value to use for the currently used SOB.
- * @base_sob_id: the base SOB id of the SOBs used by this queue.
- * @base_mon_id: the base MON id of the MONs used by this queue.
- * @collective_mstr_mon_id: the MON ids of the MONs used by this master queue
- * in order to sync with all slave queues.
- * @collective_slave_mon_id: the MON id used by this slave queue in order to
- * sync with its master queue.
- * @collective_sob_id: current SOB id used by this collective slave queue
- * to signal its collective master queue upon completion.
- * @curr_sob_offset: the id offset to the currently used SOB from the
- * HL_RSVD_SOBS that are being used by this queue.
- */
-struct hl_sync_stream_properties {
- struct hl_hw_sob hw_sob[HL_RSVD_SOBS];
- u16 next_sob_val;
- u16 base_sob_id;
- u16 base_mon_id;
- u16 collective_mstr_mon_id[HL_COLLECTIVE_RSVD_MSTR_MONS];
- u16 collective_slave_mon_id;
- u16 collective_sob_id;
- u8 curr_sob_offset;
-};
-
-/**
- * struct hl_encaps_signals_mgr - describes sync stream encapsulated signals
- * handlers manager
- * @lock: protects handles.
- * @handles: an idr to hold all encapsulated signals handles.
- */
-struct hl_encaps_signals_mgr {
- spinlock_t lock;
- struct idr handles;
-};
-
-/**
- * struct hl_hw_queue - describes a H/W transport queue.
- * @shadow_queue: pointer to a shadow queue that holds pointers to jobs.
- * @sync_stream_prop: sync stream queue properties
- * @queue_type: type of queue.
- * @collective_mode: collective mode of current queue
- * @kernel_address: holds the queue's kernel virtual address.
- * @bus_address: holds the queue's DMA address.
- * @pi: holds the queue's pi value.
- * @ci: holds the queue's ci value, AS CALCULATED BY THE DRIVER (not real ci).
- * @hw_queue_id: the id of the H/W queue.
- * @cq_id: the id for the corresponding CQ for this H/W queue.
- * @msi_vec: the IRQ number of the H/W queue.
- * @int_queue_len: length of internal queue (number of entries).
- * @valid: is the queue valid (we have array of 32 queues, not all of them
- * exist).
- * @supports_sync_stream: True if queue supports sync stream
- */
-struct hl_hw_queue {
- struct hl_cs_job **shadow_queue;
- struct hl_sync_stream_properties sync_stream_prop;
- enum hl_queue_type queue_type;
- enum hl_collective_mode collective_mode;
- void *kernel_address;
- dma_addr_t bus_address;
- u32 pi;
- atomic_t ci;
- u32 hw_queue_id;
- u32 cq_id;
- u32 msi_vec;
- u16 int_queue_len;
- u8 valid;
- u8 supports_sync_stream;
-};
-
-/**
- * struct hl_cq - describes a completion queue
- * @hdev: pointer to the device structure
- * @kernel_address: holds the queue's kernel virtual address
- * @bus_address: holds the queue's DMA address
- * @cq_idx: completion queue index in array
- * @hw_queue_id: the id of the matching H/W queue
- * @ci: ci inside the queue
- * @pi: pi inside the queue
- * @free_slots_cnt: counter of free slots in queue
- */
-struct hl_cq {
- struct hl_device *hdev;
- void *kernel_address;
- dma_addr_t bus_address;
- u32 cq_idx;
- u32 hw_queue_id;
- u32 ci;
- u32 pi;
- atomic_t free_slots_cnt;
-};
-
-/**
- * struct hl_user_interrupt - holds user interrupt information
- * @hdev: pointer to the device structure
- * @wait_list_head: head to the list of user threads pending on this interrupt
- * @wait_list_lock: protects wait_list_head
- * @interrupt_id: msix interrupt id
- * @is_decoder: whether this entry represents a decoder interrupt
- */
-struct hl_user_interrupt {
- struct hl_device *hdev;
- struct list_head wait_list_head;
- spinlock_t wait_list_lock;
- u32 interrupt_id;
- bool is_decoder;
-};
-
-/**
- * struct timestamp_reg_free_node - holds the timestamp registration free objects node
- * @free_objects_node: node in the list free_obj_jobs
- * @cq_cb: pointer to cq command buffer to be freed
- * @buf: pointer to timestamp buffer to be freed
- */
-struct timestamp_reg_free_node {
- struct list_head free_objects_node;
- struct hl_cb *cq_cb;
- struct hl_mmap_mem_buf *buf;
-};
-
-/* struct timestamp_reg_work_obj - holds the timestamp registration free objects job
- * the job will be to pass over the free_obj_jobs list and put refcount to objects
- * in each node of the list
- * @free_obj: workqueue object to free timestamp registration node objects
- * @hdev: pointer to the device structure
- * @free_obj_head: list of free jobs nodes (node type timestamp_reg_free_node)
- */
-struct timestamp_reg_work_obj {
- struct work_struct free_obj;
- struct hl_device *hdev;
- struct list_head *free_obj_head;
-};
-
-/* struct timestamp_reg_info - holds the timestamp registration related data.
- * @buf: pointer to the timestamp buffer which include both user/kernel buffers.
- * relevant only when doing timestamps records registration.
- * @cq_cb: pointer to CQ counter CB.
- * @timestamp_kernel_addr: timestamp handle address, where to set timestamp
- * relevant only when doing timestamps records
- * registration.
- * @in_use: indicates if the node already in use. relevant only when doing
- * timestamps records registration, since in this case the driver
- * will have it's own buffer which serve as a records pool instead of
- * allocating records dynamically.
- */
-struct timestamp_reg_info {
- struct hl_mmap_mem_buf *buf;
- struct hl_cb *cq_cb;
- u64 *timestamp_kernel_addr;
- u8 in_use;
-};
-
-/**
- * struct hl_user_pending_interrupt - holds a context to a user thread
- * pending on an interrupt
- * @ts_reg_info: holds the timestamps registration nodes info
- * @wait_list_node: node in the list of user threads pending on an interrupt
- * @fence: hl fence object for interrupt completion
- * @cq_target_value: CQ target value
- * @cq_kernel_addr: CQ kernel address, to be used in the cq interrupt
- * handler for target value comparison
- */
-struct hl_user_pending_interrupt {
- struct timestamp_reg_info ts_reg_info;
- struct list_head wait_list_node;
- struct hl_fence fence;
- u64 cq_target_value;
- u64 *cq_kernel_addr;
-};
-
-/**
- * struct hl_eq - describes the event queue (single one per device)
- * @hdev: pointer to the device structure
- * @kernel_address: holds the queue's kernel virtual address
- * @bus_address: holds the queue's DMA address
- * @ci: ci inside the queue
- * @prev_eqe_index: the index of the previous event queue entry. The index of
- * the current entry's index must be +1 of the previous one.
- * @check_eqe_index: do we need to check the index of the current entry vs. the
- * previous one. This is for backward compatibility with older
- * firmwares
- */
-struct hl_eq {
- struct hl_device *hdev;
- void *kernel_address;
- dma_addr_t bus_address;
- u32 ci;
- u32 prev_eqe_index;
- bool check_eqe_index;
-};
-
-/**
- * struct hl_dec - describes a decoder sw instance.
- * @hdev: pointer to the device structure.
- * @completion_abnrm_work: workqueue object to run when decoder generates an error interrupt
- * @core_id: ID of the decoder.
- * @base_addr: base address of the decoder.
- */
-struct hl_dec {
- struct hl_device *hdev;
- struct work_struct completion_abnrm_work;
- u32 core_id;
- u32 base_addr;
-};
-
-/**
- * enum hl_asic_type - supported ASIC types.
- * @ASIC_INVALID: Invalid ASIC type.
- * @ASIC_GOYA: Goya device (HL-1000).
- * @ASIC_GAUDI: Gaudi device (HL-2000).
- * @ASIC_GAUDI_SEC: Gaudi secured device (HL-2000).
- * @ASIC_GAUDI2: Gaudi2 device.
- * @ASIC_GAUDI2B: Gaudi2B device.
- */
-enum hl_asic_type {
- ASIC_INVALID,
- ASIC_GOYA,
- ASIC_GAUDI,
- ASIC_GAUDI_SEC,
- ASIC_GAUDI2,
- ASIC_GAUDI2B,
-};
-
-struct hl_cs_parser;
-
-/**
- * enum hl_pm_mng_profile - power management profile.
- * @PM_AUTO: internal clock is set by the Linux driver.
- * @PM_MANUAL: internal clock is set by the user.
- * @PM_LAST: last power management type.
- */
-enum hl_pm_mng_profile {
- PM_AUTO = 1,
- PM_MANUAL,
- PM_LAST
-};
-
-/**
- * enum hl_pll_frequency - PLL frequency.
- * @PLL_HIGH: high frequency.
- * @PLL_LOW: low frequency.
- * @PLL_LAST: last frequency values that were configured by the user.
- */
-enum hl_pll_frequency {
- PLL_HIGH = 1,
- PLL_LOW,
- PLL_LAST
-};
-
-#define PLL_REF_CLK 50
-
-enum div_select_defs {
- DIV_SEL_REF_CLK = 0,
- DIV_SEL_PLL_CLK = 1,
- DIV_SEL_DIVIDED_REF = 2,
- DIV_SEL_DIVIDED_PLL = 3,
-};
-
-enum debugfs_access_type {
- DEBUGFS_READ8,
- DEBUGFS_WRITE8,
- DEBUGFS_READ32,
- DEBUGFS_WRITE32,
- DEBUGFS_READ64,
- DEBUGFS_WRITE64,
-};
-
-enum pci_region {
- PCI_REGION_CFG,
- PCI_REGION_SRAM,
- PCI_REGION_DRAM,
- PCI_REGION_SP_SRAM,
- PCI_REGION_NUMBER,
-};
-
-/**
- * struct pci_mem_region - describe memory region in a PCI bar
- * @region_base: region base address
- * @region_size: region size
- * @bar_size: size of the BAR
- * @offset_in_bar: region offset into the bar
- * @bar_id: bar ID of the region
- * @used: if used 1, otherwise 0
- */
-struct pci_mem_region {
- u64 region_base;
- u64 region_size;
- u64 bar_size;
- u64 offset_in_bar;
- u8 bar_id;
- u8 used;
-};
-
-/**
- * struct static_fw_load_mgr - static FW load manager
- * @preboot_version_max_off: max offset to preboot version
- * @boot_fit_version_max_off: max offset to boot fit version
- * @kmd_msg_to_cpu_reg: register address for KDM->CPU messages
- * @cpu_cmd_status_to_host_reg: register address for CPU command status response
- * @cpu_boot_status_reg: boot status register
- * @cpu_boot_dev_status0_reg: boot device status register 0
- * @cpu_boot_dev_status1_reg: boot device status register 1
- * @boot_err0_reg: boot error register 0
- * @boot_err1_reg: boot error register 1
- * @preboot_version_offset_reg: SRAM offset to preboot version register
- * @boot_fit_version_offset_reg: SRAM offset to boot fit version register
- * @sram_offset_mask: mask for getting offset into the SRAM
- * @cpu_reset_wait_msec: used when setting WFE via kmd_msg_to_cpu_reg
- */
-struct static_fw_load_mgr {
- u64 preboot_version_max_off;
- u64 boot_fit_version_max_off;
- u32 kmd_msg_to_cpu_reg;
- u32 cpu_cmd_status_to_host_reg;
- u32 cpu_boot_status_reg;
- u32 cpu_boot_dev_status0_reg;
- u32 cpu_boot_dev_status1_reg;
- u32 boot_err0_reg;
- u32 boot_err1_reg;
- u32 preboot_version_offset_reg;
- u32 boot_fit_version_offset_reg;
- u32 sram_offset_mask;
- u32 cpu_reset_wait_msec;
-};
-
-/**
- * struct fw_response - FW response to LKD command
- * @ram_offset: descriptor offset into the RAM
- * @ram_type: RAM type containing the descriptor (SRAM/DRAM)
- * @status: command status
- */
-struct fw_response {
- u32 ram_offset;
- u8 ram_type;
- u8 status;
-};
-
-/**
- * struct dynamic_fw_load_mgr - dynamic FW load manager
- * @response: FW to LKD response
- * @comm_desc: the communication descriptor with FW
- * @image_region: region to copy the FW image to
- * @fw_image_size: size of FW image to load
- * @wait_for_bl_timeout: timeout for waiting for boot loader to respond
- * @fw_desc_valid: true if FW descriptor has been validated and hence the data can be used
- */
-struct dynamic_fw_load_mgr {
- struct fw_response response;
- struct lkd_fw_comms_desc comm_desc;
- struct pci_mem_region *image_region;
- size_t fw_image_size;
- u32 wait_for_bl_timeout;
- bool fw_desc_valid;
-};
-
-/**
- * struct pre_fw_load_props - needed properties for pre-FW load
- * @cpu_boot_status_reg: cpu_boot_status register address
- * @sts_boot_dev_sts0_reg: sts_boot_dev_sts0 register address
- * @sts_boot_dev_sts1_reg: sts_boot_dev_sts1 register address
- * @boot_err0_reg: boot_err0 register address
- * @boot_err1_reg: boot_err1 register address
- * @wait_for_preboot_timeout: timeout to poll for preboot ready
- */
-struct pre_fw_load_props {
- u32 cpu_boot_status_reg;
- u32 sts_boot_dev_sts0_reg;
- u32 sts_boot_dev_sts1_reg;
- u32 boot_err0_reg;
- u32 boot_err1_reg;
- u32 wait_for_preboot_timeout;
-};
-
-/**
- * struct fw_image_props - properties of FW image
- * @image_name: name of the image
- * @src_off: offset in src FW to copy from
- * @copy_size: amount of bytes to copy (0 to copy the whole binary)
- */
-struct fw_image_props {
- char *image_name;
- u32 src_off;
- u32 copy_size;
-};
-
-/**
- * struct fw_load_mgr - manager FW loading process
- * @dynamic_loader: specific structure for dynamic load
- * @static_loader: specific structure for static load
- * @pre_fw_load_props: parameter for pre FW load
- * @boot_fit_img: boot fit image properties
- * @linux_img: linux image properties
- * @cpu_timeout: CPU response timeout in usec
- * @boot_fit_timeout: Boot fit load timeout in usec
- * @skip_bmc: should BMC be skipped
- * @sram_bar_id: SRAM bar ID
- * @dram_bar_id: DRAM bar ID
- * @fw_comp_loaded: bitmask of loaded FW components. set bit meaning loaded
- * component. values are set according to enum hl_fw_types.
- */
-struct fw_load_mgr {
- union {
- struct dynamic_fw_load_mgr dynamic_loader;
- struct static_fw_load_mgr static_loader;
- };
- struct pre_fw_load_props pre_fw_load;
- struct fw_image_props boot_fit_img;
- struct fw_image_props linux_img;
- u32 cpu_timeout;
- u32 boot_fit_timeout;
- u8 skip_bmc;
- u8 sram_bar_id;
- u8 dram_bar_id;
- u8 fw_comp_loaded;
-};
-
-struct hl_cs;
-
-/**
- * struct engines_data - asic engines data
- * @buf: buffer for engines data in ascii
- * @actual_size: actual size of data that was written by the driver to the allocated buffer
- * @allocated_buf_size: total size of allocated buffer
- */
-struct engines_data {
- char *buf;
- int actual_size;
- u32 allocated_buf_size;
-};
-
-/**
- * struct hl_asic_funcs - ASIC specific functions that are can be called from
- * common code.
- * @early_init: sets up early driver state (pre sw_init), doesn't configure H/W.
- * @early_fini: tears down what was done in early_init.
- * @late_init: sets up late driver/hw state (post hw_init) - Optional.
- * @late_fini: tears down what was done in late_init (pre hw_fini) - Optional.
- * @sw_init: sets up driver state, does not configure H/W.
- * @sw_fini: tears down driver state, does not configure H/W.
- * @hw_init: sets up the H/W state.
- * @hw_fini: tears down the H/W state.
- * @halt_engines: halt engines, needed for reset sequence. This also disables
- * interrupts from the device. Should be called before
- * hw_fini and before CS rollback.
- * @suspend: handles IP specific H/W or SW changes for suspend.
- * @resume: handles IP specific H/W or SW changes for resume.
- * @mmap: maps a memory.
- * @ring_doorbell: increment PI on a given QMAN.
- * @pqe_write: Write the PQ entry to the PQ. This is ASIC-specific
- * function because the PQs are located in different memory areas
- * per ASIC (SRAM, DRAM, Host memory) and therefore, the method of
- * writing the PQE must match the destination memory area
- * properties.
- * @asic_dma_alloc_coherent: Allocate coherent DMA memory by calling
- * dma_alloc_coherent(). This is ASIC function because
- * its implementation is not trivial when the driver
- * is loaded in simulation mode (not upstreamed).
- * @asic_dma_free_coherent: Free coherent DMA memory by calling
- * dma_free_coherent(). This is ASIC function because
- * its implementation is not trivial when the driver
- * is loaded in simulation mode (not upstreamed).
- * @scrub_device_mem: Scrub the entire SRAM and DRAM.
- * @scrub_device_dram: Scrub the dram memory of the device.
- * @get_int_queue_base: get the internal queue base address.
- * @test_queues: run simple test on all queues for sanity check.
- * @asic_dma_pool_zalloc: small DMA allocation of coherent memory from DMA pool.
- * size of allocation is HL_DMA_POOL_BLK_SIZE.
- * @asic_dma_pool_free: free small DMA allocation from pool.
- * @cpu_accessible_dma_pool_alloc: allocate CPU PQ packet from DMA pool.
- * @cpu_accessible_dma_pool_free: free CPU PQ packet from DMA pool.
- * @asic_dma_unmap_single: unmap a single DMA buffer
- * @asic_dma_map_single: map a single buffer to a DMA
- * @hl_dma_unmap_sgtable: DMA unmap scatter-gather table.
- * @cs_parser: parse Command Submission.
- * @asic_dma_map_sgtable: DMA map scatter-gather table.
- * @add_end_of_cb_packets: Add packets to the end of CB, if device requires it.
- * @update_eq_ci: update event queue CI.
- * @context_switch: called upon ASID context switch.
- * @restore_phase_topology: clear all SOBs amd MONs.
- * @debugfs_read_dma: debug interface for reading up to 2MB from the device's
- * internal memory via DMA engine.
- * @add_device_attr: add ASIC specific device attributes.
- * @handle_eqe: handle event queue entry (IRQ) from CPU-CP.
- * @get_events_stat: retrieve event queue entries histogram.
- * @read_pte: read MMU page table entry from DRAM.
- * @write_pte: write MMU page table entry to DRAM.
- * @mmu_invalidate_cache: flush MMU STLB host/DRAM cache, either with soft
- * (L1 only) or hard (L0 & L1) flush.
- * @mmu_invalidate_cache_range: flush specific MMU STLB cache lines with ASID-VA-size mask.
- * @mmu_prefetch_cache_range: pre-fetch specific MMU STLB cache lines with ASID-VA-size mask.
- * @send_heartbeat: send is-alive packet to CPU-CP and verify response.
- * @debug_coresight: perform certain actions on Coresight for debugging.
- * @is_device_idle: return true if device is idle, false otherwise.
- * @compute_reset_late_init: perform certain actions needed after a compute reset
- * @hw_queues_lock: acquire H/W queues lock.
- * @hw_queues_unlock: release H/W queues lock.
- * @get_pci_id: retrieve PCI ID.
- * @get_eeprom_data: retrieve EEPROM data from F/W.
- * @get_monitor_dump: retrieve monitor registers dump from F/W.
- * @send_cpu_message: send message to F/W. If the message is timedout, the
- * driver will eventually reset the device. The timeout can
- * be determined by the calling function or it can be 0 and
- * then the timeout is the default timeout for the specific
- * ASIC
- * @get_hw_state: retrieve the H/W state
- * @pci_bars_map: Map PCI BARs.
- * @init_iatu: Initialize the iATU unit inside the PCI controller.
- * @rreg: Read a register. Needed for simulator support.
- * @wreg: Write a register. Needed for simulator support.
- * @halt_coresight: stop the ETF and ETR traces.
- * @ctx_init: context dependent initialization.
- * @ctx_fini: context dependent cleanup.
- * @pre_schedule_cs: Perform pre-CS-scheduling operations.
- * @get_queue_id_for_cq: Get the H/W queue id related to the given CQ index.
- * @load_firmware_to_device: load the firmware to the device's memory
- * @load_boot_fit_to_device: load boot fit to device's memory
- * @get_signal_cb_size: Get signal CB size.
- * @get_wait_cb_size: Get wait CB size.
- * @gen_signal_cb: Generate a signal CB.
- * @gen_wait_cb: Generate a wait CB.
- * @reset_sob: Reset a SOB.
- * @reset_sob_group: Reset SOB group
- * @get_device_time: Get the device time.
- * @pb_print_security_errors: print security errors according block and cause
- * @collective_wait_init_cs: Generate collective master/slave packets
- * and place them in the relevant cs jobs
- * @collective_wait_create_jobs: allocate collective wait cs jobs
- * @get_dec_base_addr: get the base address of a given decoder.
- * @scramble_addr: Routine to scramble the address prior of mapping it
- * in the MMU.
- * @descramble_addr: Routine to de-scramble the address prior of
- * showing it to users.
- * @ack_protection_bits_errors: ack and dump all security violations
- * @get_hw_block_id: retrieve a HW block id to be used by the user to mmap it.
- * also returns the size of the block if caller supplies
- * a valid pointer for it
- * @hw_block_mmap: mmap a HW block with a given id.
- * @enable_events_from_fw: send interrupt to firmware to notify them the
- * driver is ready to receive asynchronous events. This
- * function should be called during the first init and
- * after every hard-reset of the device
- * @ack_mmu_errors: check and ack mmu errors, page fault, access violation.
- * @get_msi_info: Retrieve asic-specific MSI ID of the f/w async event
- * @map_pll_idx_to_fw_idx: convert driver specific per asic PLL index to
- * generic f/w compatible PLL Indexes
- * @init_firmware_preload_params: initialize pre FW-load parameters.
- * @init_firmware_loader: initialize data for FW loader.
- * @init_cpu_scrambler_dram: Enable CPU specific DRAM scrambling
- * @state_dump_init: initialize constants required for state dump
- * @get_sob_addr: get SOB base address offset.
- * @set_pci_memory_regions: setting properties of PCI memory regions
- * @get_stream_master_qid_arr: get pointer to stream masters QID array
- * @check_if_razwi_happened: check if there was a razwi due to RR violation.
- * @access_dev_mem: access device memory
- * @set_dram_bar_base: set the base of the DRAM BAR
- * @set_engine_cores: set a config command to engine cores
- * @send_device_activity: indication to FW about device availability
- * @set_dram_properties: set DRAM related properties.
- */
-struct hl_asic_funcs {
- int (*early_init)(struct hl_device *hdev);
- int (*early_fini)(struct hl_device *hdev);
- int (*late_init)(struct hl_device *hdev);
- void (*late_fini)(struct hl_device *hdev);
- int (*sw_init)(struct hl_device *hdev);
- int (*sw_fini)(struct hl_device *hdev);
- int (*hw_init)(struct hl_device *hdev);
- void (*hw_fini)(struct hl_device *hdev, bool hard_reset, bool fw_reset);
- void (*halt_engines)(struct hl_device *hdev, bool hard_reset, bool fw_reset);
- int (*suspend)(struct hl_device *hdev);
- int (*resume)(struct hl_device *hdev);
- int (*mmap)(struct hl_device *hdev, struct vm_area_struct *vma,
- void *cpu_addr, dma_addr_t dma_addr, size_t size);
- void (*ring_doorbell)(struct hl_device *hdev, u32 hw_queue_id, u32 pi);
- void (*pqe_write)(struct hl_device *hdev, __le64 *pqe,
- struct hl_bd *bd);
- void* (*asic_dma_alloc_coherent)(struct hl_device *hdev, size_t size,
- dma_addr_t *dma_handle, gfp_t flag);
- void (*asic_dma_free_coherent)(struct hl_device *hdev, size_t size,
- void *cpu_addr, dma_addr_t dma_handle);
- int (*scrub_device_mem)(struct hl_device *hdev);
- int (*scrub_device_dram)(struct hl_device *hdev, u64 val);
- void* (*get_int_queue_base)(struct hl_device *hdev, u32 queue_id,
- dma_addr_t *dma_handle, u16 *queue_len);
- int (*test_queues)(struct hl_device *hdev);
- void* (*asic_dma_pool_zalloc)(struct hl_device *hdev, size_t size,
- gfp_t mem_flags, dma_addr_t *dma_handle);
- void (*asic_dma_pool_free)(struct hl_device *hdev, void *vaddr,
- dma_addr_t dma_addr);
- void* (*cpu_accessible_dma_pool_alloc)(struct hl_device *hdev,
- size_t size, dma_addr_t *dma_handle);
- void (*cpu_accessible_dma_pool_free)(struct hl_device *hdev,
- size_t size, void *vaddr);
- void (*asic_dma_unmap_single)(struct hl_device *hdev,
- dma_addr_t dma_addr, int len,
- enum dma_data_direction dir);
- dma_addr_t (*asic_dma_map_single)(struct hl_device *hdev,
- void *addr, int len,
- enum dma_data_direction dir);
- void (*hl_dma_unmap_sgtable)(struct hl_device *hdev,
- struct sg_table *sgt,
- enum dma_data_direction dir);
- int (*cs_parser)(struct hl_device *hdev, struct hl_cs_parser *parser);
- int (*asic_dma_map_sgtable)(struct hl_device *hdev, struct sg_table *sgt,
- enum dma_data_direction dir);
- void (*add_end_of_cb_packets)(struct hl_device *hdev,
- void *kernel_address, u32 len,
- u32 original_len,
- u64 cq_addr, u32 cq_val, u32 msix_num,
- bool eb);
- void (*update_eq_ci)(struct hl_device *hdev, u32 val);
- int (*context_switch)(struct hl_device *hdev, u32 asid);
- void (*restore_phase_topology)(struct hl_device *hdev);
- int (*debugfs_read_dma)(struct hl_device *hdev, u64 addr, u32 size,
- void *blob_addr);
- void (*add_device_attr)(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp,
- struct attribute_group *dev_vrm_attr_grp);
- void (*handle_eqe)(struct hl_device *hdev,
- struct hl_eq_entry *eq_entry);
- void* (*get_events_stat)(struct hl_device *hdev, bool aggregate,
- u32 *size);
- u64 (*read_pte)(struct hl_device *hdev, u64 addr);
- void (*write_pte)(struct hl_device *hdev, u64 addr, u64 val);
- int (*mmu_invalidate_cache)(struct hl_device *hdev, bool is_hard,
- u32 flags);
- int (*mmu_invalidate_cache_range)(struct hl_device *hdev, bool is_hard,
- u32 flags, u32 asid, u64 va, u64 size);
- int (*mmu_prefetch_cache_range)(struct hl_ctx *ctx, u32 flags, u32 asid, u64 va, u64 size);
- int (*send_heartbeat)(struct hl_device *hdev);
- int (*debug_coresight)(struct hl_device *hdev, struct hl_ctx *ctx, void *data);
- bool (*is_device_idle)(struct hl_device *hdev, u64 *mask_arr, u8 mask_len,
- struct engines_data *e);
- int (*compute_reset_late_init)(struct hl_device *hdev);
- void (*hw_queues_lock)(struct hl_device *hdev);
- void (*hw_queues_unlock)(struct hl_device *hdev);
- u32 (*get_pci_id)(struct hl_device *hdev);
- int (*get_eeprom_data)(struct hl_device *hdev, void *data, size_t max_size);
- int (*get_monitor_dump)(struct hl_device *hdev, void *data);
- int (*send_cpu_message)(struct hl_device *hdev, u32 *msg,
- u16 len, u32 timeout, u64 *result);
- int (*pci_bars_map)(struct hl_device *hdev);
- int (*init_iatu)(struct hl_device *hdev);
- u32 (*rreg)(struct hl_device *hdev, u32 reg);
- void (*wreg)(struct hl_device *hdev, u32 reg, u32 val);
- void (*halt_coresight)(struct hl_device *hdev, struct hl_ctx *ctx);
- int (*ctx_init)(struct hl_ctx *ctx);
- void (*ctx_fini)(struct hl_ctx *ctx);
- int (*pre_schedule_cs)(struct hl_cs *cs);
- u32 (*get_queue_id_for_cq)(struct hl_device *hdev, u32 cq_idx);
- int (*load_firmware_to_device)(struct hl_device *hdev);
- int (*load_boot_fit_to_device)(struct hl_device *hdev);
- u32 (*get_signal_cb_size)(struct hl_device *hdev);
- u32 (*get_wait_cb_size)(struct hl_device *hdev);
- u32 (*gen_signal_cb)(struct hl_device *hdev, void *data, u16 sob_id,
- u32 size, bool eb);
- u32 (*gen_wait_cb)(struct hl_device *hdev,
- struct hl_gen_wait_properties *prop);
- void (*reset_sob)(struct hl_device *hdev, void *data);
- void (*reset_sob_group)(struct hl_device *hdev, u16 sob_group);
- u64 (*get_device_time)(struct hl_device *hdev);
- void (*pb_print_security_errors)(struct hl_device *hdev,
- u32 block_addr, u32 cause, u32 offended_addr);
- int (*collective_wait_init_cs)(struct hl_cs *cs);
- int (*collective_wait_create_jobs)(struct hl_device *hdev,
- struct hl_ctx *ctx, struct hl_cs *cs,
- u32 wait_queue_id, u32 collective_engine_id,
- u32 encaps_signal_offset);
- u32 (*get_dec_base_addr)(struct hl_device *hdev, u32 core_id);
- u64 (*scramble_addr)(struct hl_device *hdev, u64 addr);
- u64 (*descramble_addr)(struct hl_device *hdev, u64 addr);
- void (*ack_protection_bits_errors)(struct hl_device *hdev);
- int (*get_hw_block_id)(struct hl_device *hdev, u64 block_addr,
- u32 *block_size, u32 *block_id);
- int (*hw_block_mmap)(struct hl_device *hdev, struct vm_area_struct *vma,
- u32 block_id, u32 block_size);
- void (*enable_events_from_fw)(struct hl_device *hdev);
- int (*ack_mmu_errors)(struct hl_device *hdev, u64 mmu_cap_mask);
- void (*get_msi_info)(__le32 *table);
- int (*map_pll_idx_to_fw_idx)(u32 pll_idx);
- void (*init_firmware_preload_params)(struct hl_device *hdev);
- void (*init_firmware_loader)(struct hl_device *hdev);
- void (*init_cpu_scrambler_dram)(struct hl_device *hdev);
- void (*state_dump_init)(struct hl_device *hdev);
- u32 (*get_sob_addr)(struct hl_device *hdev, u32 sob_id);
- void (*set_pci_memory_regions)(struct hl_device *hdev);
- u32* (*get_stream_master_qid_arr)(void);
- void (*check_if_razwi_happened)(struct hl_device *hdev);
- int (*mmu_get_real_page_size)(struct hl_device *hdev, struct hl_mmu_properties *mmu_prop,
- u32 page_size, u32 *real_page_size, bool is_dram_addr);
- int (*access_dev_mem)(struct hl_device *hdev, enum pci_region region_type,
- u64 addr, u64 *val, enum debugfs_access_type acc_type);
- u64 (*set_dram_bar_base)(struct hl_device *hdev, u64 addr);
- int (*set_engine_cores)(struct hl_device *hdev, u32 *core_ids,
- u32 num_cores, u32 core_command);
- int (*send_device_activity)(struct hl_device *hdev, bool open);
- int (*set_dram_properties)(struct hl_device *hdev);
-};
-
-
-/*
- * CONTEXTS
- */
-
-#define HL_KERNEL_ASID_ID 0
-
-/**
- * enum hl_va_range_type - virtual address range type.
- * @HL_VA_RANGE_TYPE_HOST: range type of host pages
- * @HL_VA_RANGE_TYPE_HOST_HUGE: range type of host huge pages
- * @HL_VA_RANGE_TYPE_DRAM: range type of dram pages
- */
-enum hl_va_range_type {
- HL_VA_RANGE_TYPE_HOST,
- HL_VA_RANGE_TYPE_HOST_HUGE,
- HL_VA_RANGE_TYPE_DRAM,
- HL_VA_RANGE_TYPE_MAX
-};
-
-/**
- * struct hl_va_range - virtual addresses range.
- * @lock: protects the virtual addresses list.
- * @list: list of virtual addresses blocks available for mappings.
- * @start_addr: range start address.
- * @end_addr: range end address.
- * @page_size: page size of this va range.
- */
-struct hl_va_range {
- struct mutex lock;
- struct list_head list;
- u64 start_addr;
- u64 end_addr;
- u32 page_size;
-};
-
-/**
- * struct hl_cs_counters_atomic - command submission counters
- * @out_of_mem_drop_cnt: dropped due to memory allocation issue
- * @parsing_drop_cnt: dropped due to error in packet parsing
- * @queue_full_drop_cnt: dropped due to queue full
- * @device_in_reset_drop_cnt: dropped due to device in reset
- * @max_cs_in_flight_drop_cnt: dropped due to maximum CS in-flight
- * @validation_drop_cnt: dropped due to error in validation
- */
-struct hl_cs_counters_atomic {
- atomic64_t out_of_mem_drop_cnt;
- atomic64_t parsing_drop_cnt;
- atomic64_t queue_full_drop_cnt;
- atomic64_t device_in_reset_drop_cnt;
- atomic64_t max_cs_in_flight_drop_cnt;
- atomic64_t validation_drop_cnt;
-};
-
-/**
- * struct hl_dmabuf_priv - a dma-buf private object.
- * @dmabuf: pointer to dma-buf object.
- * @ctx: pointer to the dma-buf owner's context.
- * @phys_pg_pack: pointer to physical page pack if the dma-buf was exported
- * where virtual memory is supported.
- * @memhash_hnode: pointer to the memhash node. this object holds the export count.
- * @device_address: physical address of the device's memory. Relevant only
- * if phys_pg_pack is NULL (dma-buf was exported from address).
- * The total size can be taken from the dmabuf object.
- */
-struct hl_dmabuf_priv {
- struct dma_buf *dmabuf;
- struct hl_ctx *ctx;
- struct hl_vm_phys_pg_pack *phys_pg_pack;
- struct hl_vm_hash_node *memhash_hnode;
- uint64_t device_address;
-};
-
-#define HL_CS_OUTCOME_HISTORY_LEN 256
-
-/**
- * struct hl_cs_outcome - represents a single completed CS outcome
- * @list_link: link to either container's used list or free list
- * @map_link: list to the container hash map
- * @ts: completion ts
- * @seq: the original cs sequence
- * @error: error code cs completed with, if any
- */
-struct hl_cs_outcome {
- struct list_head list_link;
- struct hlist_node map_link;
- ktime_t ts;
- u64 seq;
- int error;
-};
-
-/**
- * struct hl_cs_outcome_store - represents a limited store of completed CS outcomes
- * @outcome_map: index of completed CS searchable by sequence number
- * @used_list: list of outcome objects currently in use
- * @free_list: list of outcome objects currently not in use
- * @nodes_pool: a static pool of pre-allocated outcome objects
- * @db_lock: any operation on the store must take this lock
- */
-struct hl_cs_outcome_store {
- DECLARE_HASHTABLE(outcome_map, 8);
- struct list_head used_list;
- struct list_head free_list;
- struct hl_cs_outcome nodes_pool[HL_CS_OUTCOME_HISTORY_LEN];
- spinlock_t db_lock;
-};
-
-/**
- * struct hl_ctx - user/kernel context.
- * @mem_hash: holds mapping from virtual address to virtual memory area
- * descriptor (hl_vm_phys_pg_list or hl_userptr).
- * @mmu_shadow_hash: holds a mapping from shadow address to pgt_info structure.
- * @hr_mmu_phys_hash: if host-resident MMU is used, holds a mapping from
- * MMU-hop-page physical address to its host-resident
- * pgt_info structure.
- * @hpriv: pointer to the private (Kernel Driver) data of the process (fd).
- * @hdev: pointer to the device structure.
- * @refcount: reference counter for the context. Context is released only when
- * this hits 0l. It is incremented on CS and CS_WAIT.
- * @cs_pending: array of hl fence objects representing pending CS.
- * @outcome_store: storage data structure used to remember outcomes of completed
- * command submissions for a long time after CS id wraparound.
- * @va_range: holds available virtual addresses for host and dram mappings.
- * @mem_hash_lock: protects the mem_hash.
- * @hw_block_list_lock: protects the HW block memory list.
- * @debugfs_list: node in debugfs list of contexts.
- * @hw_block_mem_list: list of HW block virtual mapped addresses.
- * @cs_counters: context command submission counters.
- * @cb_va_pool: device VA pool for command buffers which are mapped to the
- * device's MMU.
- * @sig_mgr: encaps signals handle manager.
- * @cb_va_pool_base: the base address for the device VA pool
- * @cs_sequence: sequence number for CS. Value is assigned to a CS and passed
- * to user so user could inquire about CS. It is used as
- * index to cs_pending array.
- * @dram_default_hops: array that holds all hops addresses needed for default
- * DRAM mapping.
- * @cs_lock: spinlock to protect cs_sequence.
- * @dram_phys_mem: amount of used physical DRAM memory by this context.
- * @thread_ctx_switch_token: token to prevent multiple threads of the same
- * context from running the context switch phase.
- * Only a single thread should run it.
- * @thread_ctx_switch_wait_token: token to prevent the threads that didn't run
- * the context switch phase from moving to their
- * execution phase before the context switch phase
- * has finished.
- * @asid: context's unique address space ID in the device's MMU.
- * @handle: context's opaque handle for user
- */
-struct hl_ctx {
- DECLARE_HASHTABLE(mem_hash, MEM_HASH_TABLE_BITS);
- DECLARE_HASHTABLE(mmu_shadow_hash, MMU_HASH_TABLE_BITS);
- DECLARE_HASHTABLE(hr_mmu_phys_hash, MMU_HASH_TABLE_BITS);
- struct hl_fpriv *hpriv;
- struct hl_device *hdev;
- struct kref refcount;
- struct hl_fence **cs_pending;
- struct hl_cs_outcome_store outcome_store;
- struct hl_va_range *va_range[HL_VA_RANGE_TYPE_MAX];
- struct mutex mem_hash_lock;
- struct mutex hw_block_list_lock;
- struct list_head debugfs_list;
- struct list_head hw_block_mem_list;
- struct hl_cs_counters_atomic cs_counters;
- struct gen_pool *cb_va_pool;
- struct hl_encaps_signals_mgr sig_mgr;
- u64 cb_va_pool_base;
- u64 cs_sequence;
- u64 *dram_default_hops;
- spinlock_t cs_lock;
- atomic64_t dram_phys_mem;
- atomic_t thread_ctx_switch_token;
- u32 thread_ctx_switch_wait_token;
- u32 asid;
- u32 handle;
-};
-
-/**
- * struct hl_ctx_mgr - for handling multiple contexts.
- * @lock: protects ctx_handles.
- * @handles: idr to hold all ctx handles.
- */
-struct hl_ctx_mgr {
- struct mutex lock;
- struct idr handles;
-};
-
-
-/*
- * COMMAND SUBMISSIONS
- */
-
-/**
- * struct hl_userptr - memory mapping chunk information
- * @vm_type: type of the VM.
- * @job_node: linked-list node for hanging the object on the Job's list.
- * @pages: pointer to struct page array
- * @npages: size of @pages array
- * @sgt: pointer to the scatter-gather table that holds the pages.
- * @dir: for DMA unmapping, the direction must be supplied, so save it.
- * @debugfs_list: node in debugfs list of command submissions.
- * @pid: the pid of the user process owning the memory
- * @addr: user-space virtual address of the start of the memory area.
- * @size: size of the memory area to pin & map.
- * @dma_mapped: true if the SG was mapped to DMA addresses, false otherwise.
- */
-struct hl_userptr {
- enum vm_type vm_type; /* must be first */
- struct list_head job_node;
- struct page **pages;
- unsigned int npages;
- struct sg_table *sgt;
- enum dma_data_direction dir;
- struct list_head debugfs_list;
- pid_t pid;
- u64 addr;
- u64 size;
- u8 dma_mapped;
-};
-
-/**
- * struct hl_cs - command submission.
- * @jobs_in_queue_cnt: per each queue, maintain counter of submitted jobs.
- * @ctx: the context this CS belongs to.
- * @job_list: list of the CS's jobs in the various queues.
- * @job_lock: spinlock for the CS's jobs list. Needed for free_job.
- * @refcount: reference counter for usage of the CS.
- * @fence: pointer to the fence object of this CS.
- * @signal_fence: pointer to the fence object of the signal CS (used by wait
- * CS only).
- * @finish_work: workqueue object to run when CS is completed by H/W.
- * @work_tdr: delayed work node for TDR.
- * @mirror_node : node in device mirror list of command submissions.
- * @staged_cs_node: node in the staged cs list.
- * @debugfs_list: node in debugfs list of command submissions.
- * @encaps_sig_hdl: holds the encaps signals handle.
- * @sequence: the sequence number of this CS.
- * @staged_sequence: the sequence of the staged submission this CS is part of,
- * relevant only if staged_cs is set.
- * @timeout_jiffies: cs timeout in jiffies.
- * @submission_time_jiffies: submission time of the cs
- * @type: CS_TYPE_*.
- * @jobs_cnt: counter of submitted jobs on all queues.
- * @encaps_sig_hdl_id: encaps signals handle id, set for the first staged cs.
- * @sob_addr_offset: sob offset from the configuration base address.
- * @initial_sob_count: count of completed signals in SOB before current submission of signal or
- * cs with encaps signals.
- * @submitted: true if CS was submitted to H/W.
- * @completed: true if CS was completed by device.
- * @timedout : true if CS was timedout.
- * @tdr_active: true if TDR was activated for this CS (to prevent
- * double TDR activation).
- * @aborted: true if CS was aborted due to some device error.
- * @timestamp: true if a timestamp must be captured upon completion.
- * @staged_last: true if this is the last staged CS and needs completion.
- * @staged_first: true if this is the first staged CS and we need to receive
- * timeout for this CS.
- * @staged_cs: true if this CS is part of a staged submission.
- * @skip_reset_on_timeout: true if we shall not reset the device in case
- * timeout occurs (debug scenario).
- * @encaps_signals: true if this CS has encaps reserved signals.
- */
-struct hl_cs {
- u16 *jobs_in_queue_cnt;
- struct hl_ctx *ctx;
- struct list_head job_list;
- spinlock_t job_lock;
- struct kref refcount;
- struct hl_fence *fence;
- struct hl_fence *signal_fence;
- struct work_struct finish_work;
- struct delayed_work work_tdr;
- struct list_head mirror_node;
- struct list_head staged_cs_node;
- struct list_head debugfs_list;
- struct hl_cs_encaps_sig_handle *encaps_sig_hdl;
- u64 sequence;
- u64 staged_sequence;
- u64 timeout_jiffies;
- u64 submission_time_jiffies;
- enum hl_cs_type type;
- u32 jobs_cnt;
- u32 encaps_sig_hdl_id;
- u32 sob_addr_offset;
- u16 initial_sob_count;
- u8 submitted;
- u8 completed;
- u8 timedout;
- u8 tdr_active;
- u8 aborted;
- u8 timestamp;
- u8 staged_last;
- u8 staged_first;
- u8 staged_cs;
- u8 skip_reset_on_timeout;
- u8 encaps_signals;
-};
-
-/**
- * struct hl_cs_job - command submission job.
- * @cs_node: the node to hang on the CS jobs list.
- * @cs: the CS this job belongs to.
- * @user_cb: the CB we got from the user.
- * @patched_cb: in case of patching, this is internal CB which is submitted on
- * the queue instead of the CB we got from the IOCTL.
- * @finish_work: workqueue object to run when job is completed.
- * @userptr_list: linked-list of userptr mappings that belong to this job and
- * wait for completion.
- * @debugfs_list: node in debugfs list of command submission jobs.
- * @refcount: reference counter for usage of the CS job.
- * @queue_type: the type of the H/W queue this job is submitted to.
- * @id: the id of this job inside a CS.
- * @hw_queue_id: the id of the H/W queue this job is submitted to.
- * @user_cb_size: the actual size of the CB we got from the user.
- * @job_cb_size: the actual size of the CB that we put on the queue.
- * @encaps_sig_wait_offset: encapsulated signals offset, which allow user
- * to wait on part of the reserved signals.
- * @is_kernel_allocated_cb: true if the CB handle we got from the user holds a
- * handle to a kernel-allocated CB object, false
- * otherwise (SRAM/DRAM/host address).
- * @contains_dma_pkt: whether the JOB contains at least one DMA packet. This
- * info is needed later, when adding the 2xMSG_PROT at the
- * end of the JOB, to know which barriers to put in the
- * MSG_PROT packets. Relevant only for GAUDI as GOYA doesn't
- * have streams so the engine can't be busy by another
- * stream.
- */
-struct hl_cs_job {
- struct list_head cs_node;
- struct hl_cs *cs;
- struct hl_cb *user_cb;
- struct hl_cb *patched_cb;
- struct work_struct finish_work;
- struct list_head userptr_list;
- struct list_head debugfs_list;
- struct kref refcount;
- enum hl_queue_type queue_type;
- u32 id;
- u32 hw_queue_id;
- u32 user_cb_size;
- u32 job_cb_size;
- u32 encaps_sig_wait_offset;
- u8 is_kernel_allocated_cb;
- u8 contains_dma_pkt;
-};
-
-/**
- * struct hl_cs_parser - command submission parser properties.
- * @user_cb: the CB we got from the user.
- * @patched_cb: in case of patching, this is internal CB which is submitted on
- * the queue instead of the CB we got from the IOCTL.
- * @job_userptr_list: linked-list of userptr mappings that belong to the related
- * job and wait for completion.
- * @cs_sequence: the sequence number of the related CS.
- * @queue_type: the type of the H/W queue this job is submitted to.
- * @ctx_id: the ID of the context the related CS belongs to.
- * @hw_queue_id: the id of the H/W queue this job is submitted to.
- * @user_cb_size: the actual size of the CB we got from the user.
- * @patched_cb_size: the size of the CB after parsing.
- * @job_id: the id of the related job inside the related CS.
- * @is_kernel_allocated_cb: true if the CB handle we got from the user holds a
- * handle to a kernel-allocated CB object, false
- * otherwise (SRAM/DRAM/host address).
- * @contains_dma_pkt: whether the JOB contains at least one DMA packet. This
- * info is needed later, when adding the 2xMSG_PROT at the
- * end of the JOB, to know which barriers to put in the
- * MSG_PROT packets. Relevant only for GAUDI as GOYA doesn't
- * have streams so the engine can't be busy by another
- * stream.
- * @completion: true if we need completion for this CS.
- */
-struct hl_cs_parser {
- struct hl_cb *user_cb;
- struct hl_cb *patched_cb;
- struct list_head *job_userptr_list;
- u64 cs_sequence;
- enum hl_queue_type queue_type;
- u32 ctx_id;
- u32 hw_queue_id;
- u32 user_cb_size;
- u32 patched_cb_size;
- u8 job_id;
- u8 is_kernel_allocated_cb;
- u8 contains_dma_pkt;
- u8 completion;
-};
-
-/*
- * MEMORY STRUCTURE
- */
-
-/**
- * struct hl_vm_hash_node - hash element from virtual address to virtual
- * memory area descriptor (hl_vm_phys_pg_list or
- * hl_userptr).
- * @node: node to hang on the hash table in context object.
- * @vaddr: key virtual address.
- * @handle: memory handle for device memory allocation.
- * @ptr: value pointer (hl_vm_phys_pg_list or hl_userptr).
- * @export_cnt: number of exports from within the VA block.
- */
-struct hl_vm_hash_node {
- struct hlist_node node;
- u64 vaddr;
- u64 handle;
- void *ptr;
- int export_cnt;
-};
-
-/**
- * struct hl_vm_hw_block_list_node - list element from user virtual address to
- * HW block id.
- * @node: node to hang on the list in context object.
- * @ctx: the context this node belongs to.
- * @vaddr: virtual address of the HW block.
- * @block_size: size of the block.
- * @mapped_size: size of the block which is mapped. May change if partial un-mappings are done.
- * @id: HW block id (handle).
- */
-struct hl_vm_hw_block_list_node {
- struct list_head node;
- struct hl_ctx *ctx;
- unsigned long vaddr;
- u32 block_size;
- u32 mapped_size;
- u32 id;
-};
-
-/**
- * struct hl_vm_phys_pg_pack - physical page pack.
- * @vm_type: describes the type of the virtual area descriptor.
- * @pages: the physical page array.
- * @npages: num physical pages in the pack.
- * @total_size: total size of all the pages in this list.
- * @exported_size: buffer exported size.
- * @node: used to attach to deletion list that is used when all the allocations are cleared
- * at the teardown of the context.
- * @mapping_cnt: number of shared mappings.
- * @asid: the context related to this list.
- * @page_size: size of each page in the pack.
- * @flags: HL_MEM_* flags related to this list.
- * @handle: the provided handle related to this list.
- * @offset: offset from the first page.
- * @contiguous: is contiguous physical memory.
- * @created_from_userptr: is product of host virtual address.
- */
-struct hl_vm_phys_pg_pack {
- enum vm_type vm_type; /* must be first */
- u64 *pages;
- u64 npages;
- u64 total_size;
- u64 exported_size;
- struct list_head node;
- atomic_t mapping_cnt;
- u32 asid;
- u32 page_size;
- u32 flags;
- u32 handle;
- u32 offset;
- u8 contiguous;
- u8 created_from_userptr;
-};
-
-/**
- * struct hl_vm_va_block - virtual range block information.
- * @node: node to hang on the virtual range list in context object.
- * @start: virtual range start address.
- * @end: virtual range end address.
- * @size: virtual range size.
- */
-struct hl_vm_va_block {
- struct list_head node;
- u64 start;
- u64 end;
- u64 size;
-};
-
-/**
- * struct hl_vm - virtual memory manager for MMU.
- * @dram_pg_pool: pool for DRAM physical pages of 2MB.
- * @dram_pg_pool_refcount: reference counter for the pool usage.
- * @idr_lock: protects the phys_pg_list_handles.
- * @phys_pg_pack_handles: idr to hold all device allocations handles.
- * @init_done: whether initialization was done. We need this because VM
- * initialization might be skipped during device initialization.
- */
-struct hl_vm {
- struct gen_pool *dram_pg_pool;
- struct kref dram_pg_pool_refcount;
- spinlock_t idr_lock;
- struct idr phys_pg_pack_handles;
- u8 init_done;
-};
-
-
-/*
- * DEBUG, PROFILING STRUCTURE
- */
-
-/**
- * struct hl_debug_params - Coresight debug parameters.
- * @input: pointer to component specific input parameters.
- * @output: pointer to component specific output parameters.
- * @output_size: size of output buffer.
- * @reg_idx: relevant register ID.
- * @op: component operation to execute.
- * @enable: true if to enable component debugging, false otherwise.
- */
-struct hl_debug_params {
- void *input;
- void *output;
- u32 output_size;
- u32 reg_idx;
- u32 op;
- bool enable;
-};
-
-/**
- * struct hl_notifier_event - holds the notifier data structure
- * @eventfd: the event file descriptor to raise the notifications
- * @lock: mutex lock to protect the notifier data flows
- * @events_mask: indicates the bitmap events
- */
-struct hl_notifier_event {
- struct eventfd_ctx *eventfd;
- struct mutex lock;
- u64 events_mask;
-};
-
-/*
- * FILE PRIVATE STRUCTURE
- */
-
-/**
- * struct hl_fpriv - process information stored in FD private data.
- * @hdev: habanalabs device structure.
- * @filp: pointer to the given file structure.
- * @taskpid: current process ID.
- * @ctx: current executing context. TODO: remove for multiple ctx per process
- * @ctx_mgr: context manager to handle multiple context for this FD.
- * @mem_mgr: manager descriptor for memory exportable via mmap
- * @notifier_event: notifier eventfd towards user process
- * @debugfs_list: list of relevant ASIC debugfs.
- * @dev_node: node in the device list of file private data
- * @refcount: number of related contexts.
- * @restore_phase_mutex: lock for context switch and restore phase.
- * @ctx_lock: protects the pointer to current executing context pointer. TODO: remove for multiple
- * ctx per process.
- */
-struct hl_fpriv {
- struct hl_device *hdev;
- struct file *filp;
- struct pid *taskpid;
- struct hl_ctx *ctx;
- struct hl_ctx_mgr ctx_mgr;
- struct hl_mem_mgr mem_mgr;
- struct hl_notifier_event notifier_event;
- struct list_head debugfs_list;
- struct list_head dev_node;
- struct kref refcount;
- struct mutex restore_phase_mutex;
- struct mutex ctx_lock;
-};
-
-
-/*
- * DebugFS
- */
-
-/**
- * struct hl_info_list - debugfs file ops.
- * @name: file name.
- * @show: function to output information.
- * @write: function to write to the file.
- */
-struct hl_info_list {
- const char *name;
- int (*show)(struct seq_file *s, void *data);
- ssize_t (*write)(struct file *file, const char __user *buf,
- size_t count, loff_t *f_pos);
-};
-
-/**
- * struct hl_debugfs_entry - debugfs dentry wrapper.
- * @info_ent: dentry related ops.
- * @dev_entry: ASIC specific debugfs manager.
- */
-struct hl_debugfs_entry {
- const struct hl_info_list *info_ent;
- struct hl_dbg_device_entry *dev_entry;
-};
-
-/**
- * struct hl_dbg_device_entry - ASIC specific debugfs manager.
- * @root: root dentry.
- * @hdev: habanalabs device structure.
- * @entry_arr: array of available hl_debugfs_entry.
- * @file_list: list of available debugfs files.
- * @file_mutex: protects file_list.
- * @cb_list: list of available CBs.
- * @cb_spinlock: protects cb_list.
- * @cs_list: list of available CSs.
- * @cs_spinlock: protects cs_list.
- * @cs_job_list: list of available CB jobs.
- * @cs_job_spinlock: protects cs_job_list.
- * @userptr_list: list of available userptrs (virtual memory chunk descriptor).
- * @userptr_spinlock: protects userptr_list.
- * @ctx_mem_hash_list: list of available contexts with MMU mappings.
- * @ctx_mem_hash_spinlock: protects cb_list.
- * @data_dma_blob_desc: data DMA descriptor of blob.
- * @mon_dump_blob_desc: monitor dump descriptor of blob.
- * @state_dump: data of the system states in case of a bad cs.
- * @state_dump_sem: protects state_dump.
- * @addr: next address to read/write from/to in read/write32.
- * @mmu_addr: next virtual address to translate to physical address in mmu_show.
- * @mmu_cap_mask: mmu hw capability mask, to be used in mmu_ack_error.
- * @userptr_lookup: the target user ptr to look up for on demand.
- * @mmu_asid: ASID to use while translating in mmu_show.
- * @state_dump_head: index of the latest state dump
- * @i2c_bus: generic u8 debugfs file for bus value to use in i2c_data_read.
- * @i2c_addr: generic u8 debugfs file for address value to use in i2c_data_read.
- * @i2c_reg: generic u8 debugfs file for register value to use in i2c_data_read.
- * @i2c_len: generic u8 debugfs file for length value to use in i2c_data_read.
- */
-struct hl_dbg_device_entry {
- struct dentry *root;
- struct hl_device *hdev;
- struct hl_debugfs_entry *entry_arr;
- struct list_head file_list;
- struct mutex file_mutex;
- struct list_head cb_list;
- spinlock_t cb_spinlock;
- struct list_head cs_list;
- spinlock_t cs_spinlock;
- struct list_head cs_job_list;
- spinlock_t cs_job_spinlock;
- struct list_head userptr_list;
- spinlock_t userptr_spinlock;
- struct list_head ctx_mem_hash_list;
- spinlock_t ctx_mem_hash_spinlock;
- struct debugfs_blob_wrapper data_dma_blob_desc;
- struct debugfs_blob_wrapper mon_dump_blob_desc;
- char *state_dump[HL_STATE_DUMP_HIST_LEN];
- struct rw_semaphore state_dump_sem;
- u64 addr;
- u64 mmu_addr;
- u64 mmu_cap_mask;
- u64 userptr_lookup;
- u32 mmu_asid;
- u32 state_dump_head;
- u8 i2c_bus;
- u8 i2c_addr;
- u8 i2c_reg;
- u8 i2c_len;
-};
-
-/**
- * struct hl_hw_obj_name_entry - single hw object name, member of
- * hl_state_dump_specs
- * @node: link to the containing hash table
- * @name: hw object name
- * @id: object identifier
- */
-struct hl_hw_obj_name_entry {
- struct hlist_node node;
- const char *name;
- u32 id;
-};
-
-enum hl_state_dump_specs_props {
- SP_SYNC_OBJ_BASE_ADDR,
- SP_NEXT_SYNC_OBJ_ADDR,
- SP_SYNC_OBJ_AMOUNT,
- SP_MON_OBJ_WR_ADDR_LOW,
- SP_MON_OBJ_WR_ADDR_HIGH,
- SP_MON_OBJ_WR_DATA,
- SP_MON_OBJ_ARM_DATA,
- SP_MON_OBJ_STATUS,
- SP_MONITORS_AMOUNT,
- SP_TPC0_CMDQ,
- SP_TPC0_CFG_SO,
- SP_NEXT_TPC,
- SP_MME_CMDQ,
- SP_MME_CFG_SO,
- SP_NEXT_MME,
- SP_DMA_CMDQ,
- SP_DMA_CFG_SO,
- SP_DMA_QUEUES_OFFSET,
- SP_NUM_OF_MME_ENGINES,
- SP_SUB_MME_ENG_NUM,
- SP_NUM_OF_DMA_ENGINES,
- SP_NUM_OF_TPC_ENGINES,
- SP_ENGINE_NUM_OF_QUEUES,
- SP_ENGINE_NUM_OF_STREAMS,
- SP_ENGINE_NUM_OF_FENCES,
- SP_FENCE0_CNT_OFFSET,
- SP_FENCE0_RDATA_OFFSET,
- SP_CP_STS_OFFSET,
- SP_NUM_CORES,
-
- SP_MAX
-};
-
-enum hl_sync_engine_type {
- ENGINE_TPC,
- ENGINE_DMA,
- ENGINE_MME,
-};
-
-/**
- * struct hl_mon_state_dump - represents a state dump of a single monitor
- * @id: monitor id
- * @wr_addr_low: address monitor will write to, low bits
- * @wr_addr_high: address monitor will write to, high bits
- * @wr_data: data monitor will write
- * @arm_data: register value containing monitor configuration
- * @status: monitor status
- */
-struct hl_mon_state_dump {
- u32 id;
- u32 wr_addr_low;
- u32 wr_addr_high;
- u32 wr_data;
- u32 arm_data;
- u32 status;
-};
-
-/**
- * struct hl_sync_to_engine_map_entry - sync object id to engine mapping entry
- * @engine_type: type of the engine
- * @engine_id: id of the engine
- * @sync_id: id of the sync object
- */
-struct hl_sync_to_engine_map_entry {
- struct hlist_node node;
- enum hl_sync_engine_type engine_type;
- u32 engine_id;
- u32 sync_id;
-};
-
-/**
- * struct hl_sync_to_engine_map - maps sync object id to associated engine id
- * @tb: hash table containing the mapping, each element is of type
- * struct hl_sync_to_engine_map_entry
- */
-struct hl_sync_to_engine_map {
- DECLARE_HASHTABLE(tb, SYNC_TO_ENGINE_HASH_TABLE_BITS);
-};
-
-/**
- * struct hl_state_dump_specs_funcs - virtual functions used by the state dump
- * @gen_sync_to_engine_map: generate a hash map from sync obj id to its engine
- * @print_single_monitor: format monitor data as string
- * @monitor_valid: return true if given monitor dump is valid
- * @print_fences_single_engine: format fences data as string
- */
-struct hl_state_dump_specs_funcs {
- int (*gen_sync_to_engine_map)(struct hl_device *hdev,
- struct hl_sync_to_engine_map *map);
- int (*print_single_monitor)(char **buf, size_t *size, size_t *offset,
- struct hl_device *hdev,
- struct hl_mon_state_dump *mon);
- int (*monitor_valid)(struct hl_mon_state_dump *mon);
- int (*print_fences_single_engine)(struct hl_device *hdev,
- u64 base_offset,
- u64 status_base_offset,
- enum hl_sync_engine_type engine_type,
- u32 engine_id, char **buf,
- size_t *size, size_t *offset);
-};
-
-/**
- * struct hl_state_dump_specs - defines ASIC known hw objects names
- * @so_id_to_str_tb: sync objects names index table
- * @monitor_id_to_str_tb: monitors names index table
- * @funcs: virtual functions used for state dump
- * @sync_namager_names: readable names for sync manager if available (ex: N_E)
- * @props: pointer to a per asic const props array required for state dump
- */
-struct hl_state_dump_specs {
- DECLARE_HASHTABLE(so_id_to_str_tb, OBJ_NAMES_HASH_TABLE_BITS);
- DECLARE_HASHTABLE(monitor_id_to_str_tb, OBJ_NAMES_HASH_TABLE_BITS);
- struct hl_state_dump_specs_funcs funcs;
- const char * const *sync_namager_names;
- s64 *props;
-};
-
-
-/*
- * DEVICES
- */
-
-#define HL_STR_MAX 32
-
-#define HL_DEV_STS_MAX (HL_DEVICE_STATUS_LAST + 1)
-
-/* Theoretical limit only. A single host can only contain up to 4 or 8 PCIe
- * x16 cards. In extreme cases, there are hosts that can accommodate 16 cards.
- */
-#define HL_MAX_MINORS 256
-
-/*
- * Registers read & write functions.
- */
-
-u32 hl_rreg(struct hl_device *hdev, u32 reg);
-void hl_wreg(struct hl_device *hdev, u32 reg, u32 val);
-
-#define RREG32(reg) hdev->asic_funcs->rreg(hdev, (reg))
-#define WREG32(reg, v) hdev->asic_funcs->wreg(hdev, (reg), (v))
-#define DREG32(reg) pr_info("REGISTER: " #reg " : 0x%08X\n", \
- hdev->asic_funcs->rreg(hdev, (reg)))
-
-#define WREG32_P(reg, val, mask) \
- do { \
- u32 tmp_ = RREG32(reg); \
- tmp_ &= (mask); \
- tmp_ |= ((val) & ~(mask)); \
- WREG32(reg, tmp_); \
- } while (0)
-#define WREG32_AND(reg, and) WREG32_P(reg, 0, and)
-#define WREG32_OR(reg, or) WREG32_P(reg, or, ~(or))
-
-#define RMWREG32_SHIFTED(reg, val, mask) WREG32_P(reg, val, ~(mask))
-
-#define RMWREG32(reg, val, mask) RMWREG32_SHIFTED(reg, (val) << __ffs(mask), mask)
-
-#define RREG32_MASK(reg, mask) ((RREG32(reg) & mask) >> __ffs(mask))
-
-#define REG_FIELD_SHIFT(reg, field) reg##_##field##_SHIFT
-#define REG_FIELD_MASK(reg, field) reg##_##field##_MASK
-#define WREG32_FIELD(reg, offset, field, val) \
- WREG32(mm##reg + offset, (RREG32(mm##reg + offset) & \
- ~REG_FIELD_MASK(reg, field)) | \
- (val) << REG_FIELD_SHIFT(reg, field))
-
-/* Timeout should be longer when working with simulator but cap the
- * increased timeout to some maximum
- */
-#define hl_poll_timeout_common(hdev, addr, val, cond, sleep_us, timeout_us, elbi) \
-({ \
- ktime_t __timeout; \
- u32 __elbi_read; \
- int __rc = 0; \
- if (hdev->pdev) \
- __timeout = ktime_add_us(ktime_get(), timeout_us); \
- else \
- __timeout = ktime_add_us(ktime_get(),\
- min((u64)(timeout_us * 10), \
- (u64) HL_SIM_MAX_TIMEOUT_US)); \
- might_sleep_if(sleep_us); \
- for (;;) { \
- if (elbi) { \
- __rc = hl_pci_elbi_read(hdev, addr, &__elbi_read); \
- if (__rc) \
- break; \
- (val) = __elbi_read; \
- } else {\
- (val) = RREG32(lower_32_bits(addr)); \
- } \
- if (cond) \
- break; \
- if (timeout_us && ktime_compare(ktime_get(), __timeout) > 0) { \
- if (elbi) { \
- __rc = hl_pci_elbi_read(hdev, addr, &__elbi_read); \
- if (__rc) \
- break; \
- (val) = __elbi_read; \
- } else {\
- (val) = RREG32(lower_32_bits(addr)); \
- } \
- break; \
- } \
- if (sleep_us) \
- usleep_range((sleep_us >> 2) + 1, sleep_us); \
- } \
- __rc ? __rc : ((cond) ? 0 : -ETIMEDOUT); \
-})
-
-#define hl_poll_timeout(hdev, addr, val, cond, sleep_us, timeout_us) \
- hl_poll_timeout_common(hdev, addr, val, cond, sleep_us, timeout_us, false)
-
-#define hl_poll_timeout_elbi(hdev, addr, val, cond, sleep_us, timeout_us) \
- hl_poll_timeout_common(hdev, addr, val, cond, sleep_us, timeout_us, true)
-
-/*
- * poll array of register addresses.
- * condition is satisfied if all registers values match the expected value.
- * once some register in the array satisfies the condition it will not be polled again,
- * this is done both for efficiency and due to some registers are "clear on read".
- * TODO: use read from PCI bar in other places in the code (SW-91406)
- */
-#define hl_poll_reg_array_timeout_common(hdev, addr_arr, arr_size, expected_val, sleep_us, \
- timeout_us, elbi) \
-({ \
- ktime_t __timeout; \
- u64 __elem_bitmask; \
- u32 __read_val; \
- u8 __arr_idx; \
- int __rc = 0; \
- \
- if (hdev->pdev) \
- __timeout = ktime_add_us(ktime_get(), timeout_us); \
- else \
- __timeout = ktime_add_us(ktime_get(),\
- min(((u64)timeout_us * 10), \
- (u64) HL_SIM_MAX_TIMEOUT_US)); \
- \
- might_sleep_if(sleep_us); \
- if (arr_size >= 64) \
- __rc = -EINVAL; \
- else \
- __elem_bitmask = BIT_ULL(arr_size) - 1; \
- for (;;) { \
- if (__rc) \
- break; \
- for (__arr_idx = 0; __arr_idx < (arr_size); __arr_idx++) { \
- if (!(__elem_bitmask & BIT_ULL(__arr_idx))) \
- continue; \
- if (elbi) { \
- __rc = hl_pci_elbi_read(hdev, (addr_arr)[__arr_idx], &__read_val); \
- if (__rc) \
- break; \
- } else { \
- __read_val = RREG32(lower_32_bits(addr_arr[__arr_idx])); \
- } \
- if (__read_val == (expected_val)) \
- __elem_bitmask &= ~BIT_ULL(__arr_idx); \
- } \
- if (__rc || (__elem_bitmask == 0)) \
- break; \
- if (timeout_us && ktime_compare(ktime_get(), __timeout) > 0) \
- break; \
- if (sleep_us) \
- usleep_range((sleep_us >> 2) + 1, sleep_us); \
- } \
- __rc ? __rc : ((__elem_bitmask == 0) ? 0 : -ETIMEDOUT); \
-})
-
-#define hl_poll_reg_array_timeout(hdev, addr_arr, arr_size, expected_val, sleep_us, \
- timeout_us) \
- hl_poll_reg_array_timeout_common(hdev, addr_arr, arr_size, expected_val, sleep_us, \
- timeout_us, false)
-
-#define hl_poll_reg_array_timeout_elbi(hdev, addr_arr, arr_size, expected_val, sleep_us, \
- timeout_us) \
- hl_poll_reg_array_timeout_common(hdev, addr_arr, arr_size, expected_val, sleep_us, \
- timeout_us, true)
-
-/*
- * address in this macro points always to a memory location in the
- * host's (server's) memory. That location is updated asynchronously
- * either by the direct access of the device or by another core.
- *
- * To work both in LE and BE architectures, we need to distinguish between the
- * two states (device or another core updates the memory location). Therefore,
- * if mem_written_by_device is true, the host memory being polled will be
- * updated directly by the device. If false, the host memory being polled will
- * be updated by host CPU. Required so host knows whether or not the memory
- * might need to be byte-swapped before returning value to caller.
- */
-#define hl_poll_timeout_memory(hdev, addr, val, cond, sleep_us, timeout_us, \
- mem_written_by_device) \
-({ \
- ktime_t __timeout; \
- if (hdev->pdev) \
- __timeout = ktime_add_us(ktime_get(), timeout_us); \
- else \
- __timeout = ktime_add_us(ktime_get(),\
- min((u64)(timeout_us * 100), \
- (u64) HL_SIM_MAX_TIMEOUT_US)); \
- might_sleep_if(sleep_us); \
- for (;;) { \
- /* Verify we read updates done by other cores or by device */ \
- mb(); \
- (val) = *((u32 *)(addr)); \
- if (mem_written_by_device) \
- (val) = le32_to_cpu(*(__le32 *) &(val)); \
- if (cond) \
- break; \
- if (timeout_us && ktime_compare(ktime_get(), __timeout) > 0) { \
- (val) = *((u32 *)(addr)); \
- if (mem_written_by_device) \
- (val) = le32_to_cpu(*(__le32 *) &(val)); \
- break; \
- } \
- if (sleep_us) \
- usleep_range((sleep_us >> 2) + 1, sleep_us); \
- } \
- (cond) ? 0 : -ETIMEDOUT; \
-})
-
-#define HL_USR_MAPPED_BLK_INIT(blk, base, sz) \
-({ \
- struct user_mapped_block *p = blk; \
-\
- p->address = base; \
- p->size = sz; \
-})
-
-#define HL_USR_INTR_STRUCT_INIT(usr_intr, hdev, intr_id, decoder) \
-({ \
- usr_intr.hdev = hdev; \
- usr_intr.interrupt_id = intr_id; \
- usr_intr.is_decoder = decoder; \
- INIT_LIST_HEAD(&usr_intr.wait_list_head); \
- spin_lock_init(&usr_intr.wait_list_lock); \
-})
-
-struct hwmon_chip_info;
-
-/**
- * struct hl_device_reset_work - reset work wrapper.
- * @reset_work: reset work to be done.
- * @hdev: habanalabs device structure.
- * @flags: reset flags.
- */
-struct hl_device_reset_work {
- struct delayed_work reset_work;
- struct hl_device *hdev;
- u32 flags;
-};
-
-/**
- * struct hl_mmu_hr_pgt_priv - used for holding per-device mmu host-resident
- * page-table internal information.
- * @mmu_pgt_pool: pool of page tables used by a host-resident MMU for
- * allocating hops.
- * @mmu_asid_hop0: per-ASID array of host-resident hop0 tables.
- */
-struct hl_mmu_hr_priv {
- struct gen_pool *mmu_pgt_pool;
- struct pgt_info *mmu_asid_hop0;
-};
-
-/**
- * struct hl_mmu_dr_pgt_priv - used for holding per-device mmu device-resident
- * page-table internal information.
- * @mmu_pgt_pool: pool of page tables used by MMU for allocating hops.
- * @mmu_shadow_hop0: shadow array of hop0 tables.
- */
-struct hl_mmu_dr_priv {
- struct gen_pool *mmu_pgt_pool;
- void *mmu_shadow_hop0;
-};
-
-/**
- * struct hl_mmu_priv - used for holding per-device mmu internal information.
- * @dr: information on the device-resident MMU, when exists.
- * @hr: information on the host-resident MMU, when exists.
- */
-struct hl_mmu_priv {
- struct hl_mmu_dr_priv dr;
- struct hl_mmu_hr_priv hr;
-};
-
-/**
- * struct hl_mmu_per_hop_info - A structure describing one TLB HOP and its entry
- * that was created in order to translate a virtual address to a
- * physical one.
- * @hop_addr: The address of the hop.
- * @hop_pte_addr: The address of the hop entry.
- * @hop_pte_val: The value in the hop entry.
- */
-struct hl_mmu_per_hop_info {
- u64 hop_addr;
- u64 hop_pte_addr;
- u64 hop_pte_val;
-};
-
-/**
- * struct hl_mmu_hop_info - A structure describing the TLB hops and their
- * hop-entries that were created in order to translate a virtual address to a
- * physical one.
- * @scrambled_vaddr: The value of the virtual address after scrambling. This
- * address replaces the original virtual-address when mapped
- * in the MMU tables.
- * @unscrambled_paddr: The un-scrambled physical address.
- * @hop_info: Array holding the per-hop information used for the translation.
- * @used_hops: The number of hops used for the translation.
- * @range_type: virtual address range type.
- */
-struct hl_mmu_hop_info {
- u64 scrambled_vaddr;
- u64 unscrambled_paddr;
- struct hl_mmu_per_hop_info hop_info[MMU_ARCH_6_HOPS];
- u32 used_hops;
- enum hl_va_range_type range_type;
-};
-
-/**
- * struct hl_hr_mmu_funcs - Device related host resident MMU functions.
- * @get_hop0_pgt_info: get page table info structure for HOP0.
- * @get_pgt_info: get page table info structure for HOP other than HOP0.
- * @add_pgt_info: add page table info structure to hash.
- * @get_tlb_mapping_params: get mapping parameters needed for getting TLB info for specific mapping.
- */
-struct hl_hr_mmu_funcs {
- struct pgt_info *(*get_hop0_pgt_info)(struct hl_ctx *ctx);
- struct pgt_info *(*get_pgt_info)(struct hl_ctx *ctx, u64 phys_hop_addr);
- void (*add_pgt_info)(struct hl_ctx *ctx, struct pgt_info *pgt_info, dma_addr_t phys_addr);
- int (*get_tlb_mapping_params)(struct hl_device *hdev, struct hl_mmu_properties **mmu_prop,
- struct hl_mmu_hop_info *hops,
- u64 virt_addr, bool *is_huge);
-};
-
-/**
- * struct hl_mmu_funcs - Device related MMU functions.
- * @init: initialize the MMU module.
- * @fini: release the MMU module.
- * @ctx_init: Initialize a context for using the MMU module.
- * @ctx_fini: disable a ctx from using the mmu module.
- * @map: maps a virtual address to physical address for a context.
- * @unmap: unmap a virtual address of a context.
- * @flush: flush all writes from all cores to reach device MMU.
- * @swap_out: marks all mapping of the given context as swapped out.
- * @swap_in: marks all mapping of the given context as swapped in.
- * @get_tlb_info: returns the list of hops and hop-entries used that were
- * created in order to translate the giver virtual address to a
- * physical one.
- * @hr_funcs: functions specific to host resident MMU.
- */
-struct hl_mmu_funcs {
- int (*init)(struct hl_device *hdev);
- void (*fini)(struct hl_device *hdev);
- int (*ctx_init)(struct hl_ctx *ctx);
- void (*ctx_fini)(struct hl_ctx *ctx);
- int (*map)(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_size,
- bool is_dram_addr);
- int (*unmap)(struct hl_ctx *ctx, u64 virt_addr, bool is_dram_addr);
- void (*flush)(struct hl_ctx *ctx);
- void (*swap_out)(struct hl_ctx *ctx);
- void (*swap_in)(struct hl_ctx *ctx);
- int (*get_tlb_info)(struct hl_ctx *ctx, u64 virt_addr, struct hl_mmu_hop_info *hops);
- struct hl_hr_mmu_funcs hr_funcs;
-};
-
-/**
- * struct hl_prefetch_work - prefetch work structure handler
- * @prefetch_work: actual work struct.
- * @ctx: compute context.
- * @va: virtual address to pre-fetch.
- * @size: pre-fetch size.
- * @flags: operation flags.
- * @asid: ASID for maintenance operation.
- */
-struct hl_prefetch_work {
- struct work_struct prefetch_work;
- struct hl_ctx *ctx;
- u64 va;
- u64 size;
- u32 flags;
- u32 asid;
-};
-
-/*
- * number of user contexts allowed to call wait_for_multi_cs ioctl in
- * parallel
- */
-#define MULTI_CS_MAX_USER_CTX 2
-
-/**
- * struct multi_cs_completion - multi CS wait completion.
- * @completion: completion of any of the CS in the list
- * @lock: spinlock for the completion structure
- * @timestamp: timestamp for the multi-CS completion
- * @stream_master_qid_map: bitmap of all stream masters on which the multi-CS
- * is waiting
- * @used: 1 if in use, otherwise 0
- */
-struct multi_cs_completion {
- struct completion completion;
- spinlock_t lock;
- s64 timestamp;
- u32 stream_master_qid_map;
- u8 used;
-};
-
-/**
- * struct multi_cs_data - internal data for multi CS call
- * @ctx: pointer to the context structure
- * @fence_arr: array of fences of all CSs
- * @seq_arr: array of CS sequence numbers
- * @timeout_jiffies: timeout in jiffies for waiting for CS to complete
- * @timestamp: timestamp of first completed CS
- * @wait_status: wait for CS status
- * @completion_bitmap: bitmap of completed CSs (1- completed, otherwise 0)
- * @arr_len: fence_arr and seq_arr array length
- * @gone_cs: indication of gone CS (1- there was gone CS, otherwise 0)
- * @update_ts: update timestamp. 1- update the timestamp, otherwise 0.
- */
-struct multi_cs_data {
- struct hl_ctx *ctx;
- struct hl_fence **fence_arr;
- u64 *seq_arr;
- s64 timeout_jiffies;
- s64 timestamp;
- long wait_status;
- u32 completion_bitmap;
- u8 arr_len;
- u8 gone_cs;
- u8 update_ts;
-};
-
-/**
- * struct hl_clk_throttle_timestamp - current/last clock throttling timestamp
- * @start: timestamp taken when 'start' event is received in driver
- * @end: timestamp taken when 'end' event is received in driver
- */
-struct hl_clk_throttle_timestamp {
- ktime_t start;
- ktime_t end;
-};
-
-/**
- * struct hl_clk_throttle - keeps current/last clock throttling timestamps
- * @timestamp: timestamp taken by driver and firmware, index 0 refers to POWER
- * index 1 refers to THERMAL
- * @lock: protects this structure as it can be accessed from both event queue
- * context and info_ioctl context
- * @current_reason: bitmask represents the current clk throttling reasons
- * @aggregated_reason: bitmask represents aggregated clk throttling reasons since driver load
- */
-struct hl_clk_throttle {
- struct hl_clk_throttle_timestamp timestamp[HL_CLK_THROTTLE_TYPE_MAX];
- struct mutex lock;
- u32 current_reason;
- u32 aggregated_reason;
-};
-
-/**
- * struct user_mapped_block - describes a hw block allowed to be mmapped by user
- * @address: physical HW block address
- * @size: allowed size for mmap
- */
-struct user_mapped_block {
- u32 address;
- u32 size;
-};
-
-/**
- * struct cs_timeout_info - info of last CS timeout occurred.
- * @timestamp: CS timeout timestamp.
- * @write_enable: if set writing to CS parameters in the structure is enabled. otherwise - disabled,
- * so the first (root cause) CS timeout will not be overwritten.
- * @seq: CS timeout sequence number.
- */
-struct cs_timeout_info {
- ktime_t timestamp;
- atomic_t write_enable;
- u64 seq;
-};
-
-#define MAX_QMAN_STREAMS_INFO 4
-#define OPCODE_INFO_MAX_ADDR_SIZE 8
-/**
- * struct undefined_opcode_info - info about last undefined opcode error
- * @timestamp: timestamp of the undefined opcode error
- * @cb_addr_streams: CB addresses (per stream) that are currently exists in the PQ
- * entries. In case all streams array entries are
- * filled with values, it means the execution was in Lower-CP.
- * @cq_addr: the address of the current handled command buffer
- * @cq_size: the size of the current handled command buffer
- * @cb_addr_streams_len: num of streams - actual len of cb_addr_streams array.
- * should be equal to 1 incase of undefined opcode
- * in Upper-CP (specific stream) and equal to 4 incase
- * of undefined opcode in Lower-CP.
- * @engine_id: engine-id that the error occurred on
- * @stream_id: the stream id the error occurred on. In case the stream equals to
- * MAX_QMAN_STREAMS_INFO it means the error occurred on a Lower-CP.
- * @write_enable: if set, writing to undefined opcode parameters in the structure
- * is enable so the first (root cause) undefined opcode will not be
- * overwritten.
- */
-struct undefined_opcode_info {
- ktime_t timestamp;
- u64 cb_addr_streams[MAX_QMAN_STREAMS_INFO][OPCODE_INFO_MAX_ADDR_SIZE];
- u64 cq_addr;
- u32 cq_size;
- u32 cb_addr_streams_len;
- u32 engine_id;
- u32 stream_id;
- bool write_enable;
-};
-
-/**
- * struct page_fault_info - info about page fault
- * @pgf_info: page fault information.
- * @user_mappings: buffer containing user mappings.
- * @num_of_user_mappings: number of user mappings.
- */
-struct page_fault_info {
- struct hl_page_fault_info pgf;
- struct hl_user_mapping *user_mappings;
- u64 num_of_user_mappings;
-};
-
-/**
- * struct hl_error_info - holds information collected during an error.
- * @cs_timeout: CS timeout error information.
- * @razwi: razwi information.
- * @razwi_info_recorded: if set writing to razwi information is enabled.
- * otherwise - disabled, so the first (root cause) razwi will not be
- * overwritten.
- * @undef_opcode: undefined opcode information
- * @pgf_info: page fault information.
- * @pgf_info_recorded: if set writing to page fault information is enabled.
- * otherwise - disabled, so the first (root cause) page fault will not be
- * overwritten.
- */
-struct hl_error_info {
- struct cs_timeout_info cs_timeout;
- struct hl_info_razwi_event razwi;
- atomic_t razwi_info_recorded;
- struct undefined_opcode_info undef_opcode;
- struct page_fault_info pgf_info;
- atomic_t pgf_info_recorded;
-};
-
-/**
- * struct hl_reset_info - holds current device reset information.
- * @lock: lock to protect critical reset flows.
- * @compute_reset_cnt: number of compute resets since the driver was loaded.
- * @hard_reset_cnt: number of hard resets since the driver was loaded.
- * @hard_reset_schedule_flags: hard reset is scheduled to after current compute reset,
- * here we hold the hard reset flags.
- * @in_reset: is device in reset flow.
- * @in_compute_reset: Device is currently in reset but not in hard-reset.
- * @needs_reset: true if reset_on_lockup is false and device should be reset
- * due to lockup.
- * @hard_reset_pending: is there a hard reset work pending.
- * @curr_reset_cause: saves an enumerated reset cause when a hard reset is
- * triggered, and cleared after it is shared with preboot.
- * @prev_reset_trigger: saves the previous trigger which caused a reset, overridden
- * with a new value on next reset
- * @reset_trigger_repeated: set if device reset is triggered more than once with
- * same cause.
- * @skip_reset_on_timeout: Skip device reset if CS has timed out, wait for it to
- * complete instead.
- * @watchdog_active: true if a device release watchdog work is scheduled.
- */
-struct hl_reset_info {
- spinlock_t lock;
- u32 compute_reset_cnt;
- u32 hard_reset_cnt;
- u32 hard_reset_schedule_flags;
- u8 in_reset;
- u8 in_compute_reset;
- u8 needs_reset;
- u8 hard_reset_pending;
- u8 curr_reset_cause;
- u8 prev_reset_trigger;
- u8 reset_trigger_repeated;
- u8 skip_reset_on_timeout;
- u8 watchdog_active;
-};
-
-/**
- * struct hl_device - habanalabs device structure.
- * @pdev: pointer to PCI device, can be NULL in case of simulator device.
- * @pcie_bar_phys: array of available PCIe bars physical addresses.
- * (required only for PCI address match mode)
- * @pcie_bar: array of available PCIe bars virtual addresses.
- * @rmmio: configuration area address on SRAM.
- * @cdev: related char device.
- * @cdev_ctrl: char device for control operations only (INFO IOCTL)
- * @dev: related kernel basic device structure.
- * @dev_ctrl: related kernel device structure for the control device
- * @work_heartbeat: delayed work for CPU-CP is-alive check.
- * @device_reset_work: delayed work which performs hard reset
- * @device_release_watchdog_work: watchdog work that performs hard reset if user doesn't release
- * device upon certain error cases.
- * @asic_name: ASIC specific name.
- * @asic_type: ASIC specific type.
- * @completion_queue: array of hl_cq.
- * @user_interrupt: array of hl_user_interrupt. upon the corresponding user
- * interrupt, driver will monitor the list of fences
- * registered to this interrupt.
- * @common_user_cq_interrupt: common user CQ interrupt for all user CQ interrupts.
- * upon any user CQ interrupt, driver will monitor the
- * list of fences registered to this common structure.
- * @common_decoder_interrupt: common decoder interrupt for all user decoder interrupts.
- * @shadow_cs_queue: pointer to a shadow queue that holds pointers to
- * outstanding command submissions.
- * @cq_wq: work queues of completion queues for executing work in process
- * context.
- * @eq_wq: work queue of event queue for executing work in process context.
- * @cs_cmplt_wq: work queue of CS completions for executing work in process
- * context.
- * @ts_free_obj_wq: work queue for timestamp registration objects release.
- * @prefetch_wq: work queue for MMU pre-fetch operations.
- * @reset_wq: work queue for device reset procedure.
- * @kernel_ctx: Kernel driver context structure.
- * @kernel_queues: array of hl_hw_queue.
- * @cs_mirror_list: CS mirror list for TDR.
- * @cs_mirror_lock: protects cs_mirror_list.
- * @kernel_mem_mgr: memory manager for memory buffers with lifespan of driver.
- * @event_queue: event queue for IRQ from CPU-CP.
- * @dma_pool: DMA pool for small allocations.
- * @cpu_accessible_dma_mem: Host <-> CPU-CP shared memory CPU address.
- * @cpu_accessible_dma_address: Host <-> CPU-CP shared memory DMA address.
- * @cpu_accessible_dma_pool: Host <-> CPU-CP shared memory pool.
- * @asid_bitmap: holds used/available ASIDs.
- * @asid_mutex: protects asid_bitmap.
- * @send_cpu_message_lock: enforces only one message in Host <-> CPU-CP queue.
- * @debug_lock: protects critical section of setting debug mode for device
- * @mmu_lock: protects the MMU page tables and invalidation h/w. Although the
- * page tables are per context, the invalidation h/w is per MMU.
- * Therefore, we can't allow multiple contexts (we only have two,
- * user and kernel) to access the invalidation h/w at the same time.
- * In addition, any change to the PGT, modifying the MMU hash or
- * walking the PGT requires talking this lock.
- * @asic_prop: ASIC specific immutable properties.
- * @asic_funcs: ASIC specific functions.
- * @asic_specific: ASIC specific information to use only from ASIC files.
- * @vm: virtual memory manager for MMU.
- * @hwmon_dev: H/W monitor device.
- * @hl_chip_info: ASIC's sensors information.
- * @device_status_description: device status description.
- * @hl_debugfs: device's debugfs manager.
- * @cb_pool: list of pre allocated CBs.
- * @cb_pool_lock: protects the CB pool.
- * @internal_cb_pool_virt_addr: internal command buffer pool virtual address.
- * @internal_cb_pool_dma_addr: internal command buffer pool dma address.
- * @internal_cb_pool: internal command buffer memory pool.
- * @internal_cb_va_base: internal cb pool mmu virtual address base
- * @fpriv_list: list of file private data structures. Each structure is created
- * when a user opens the device
- * @fpriv_ctrl_list: list of file private data structures. Each structure is created
- * when a user opens the control device
- * @fpriv_list_lock: protects the fpriv_list
- * @fpriv_ctrl_list_lock: protects the fpriv_ctrl_list
- * @aggregated_cs_counters: aggregated cs counters among all contexts
- * @mmu_priv: device-specific MMU data.
- * @mmu_func: device-related MMU functions.
- * @dec: list of decoder sw instance
- * @fw_loader: FW loader manager.
- * @pci_mem_region: array of memory regions in the PCI
- * @state_dump_specs: constants and dictionaries needed to dump system state.
- * @multi_cs_completion: array of multi-CS completion.
- * @clk_throttling: holds information about current/previous clock throttling events
- * @captured_err_info: holds information about errors.
- * @reset_info: holds current device reset information.
- * @stream_master_qid_arr: pointer to array with QIDs of master streams.
- * @fw_major_version: major version of current loaded preboot.
- * @fw_minor_version: minor version of current loaded preboot.
- * @dram_used_mem: current DRAM memory consumption.
- * @memory_scrub_val: the value to which the dram will be scrubbed to using cb scrub_device_dram
- * @timeout_jiffies: device CS timeout value.
- * @max_power: the max power of the device, as configured by the sysadmin. This
- * value is saved so in case of hard-reset, the driver will restore
- * this value and update the F/W after the re-initialization
- * @boot_error_status_mask: contains a mask of the device boot error status.
- * Each bit represents a different error, according to
- * the defines in hl_boot_if.h. If the bit is cleared,
- * the error will be ignored by the driver during
- * device initialization. Mainly used to debug and
- * workaround firmware bugs
- * @dram_pci_bar_start: start bus address of PCIe bar towards DRAM.
- * @last_successful_open_ktime: timestamp (ktime) of the last successful device open.
- * @last_successful_open_jif: timestamp (jiffies) of the last successful
- * device open.
- * @last_open_session_duration_jif: duration (jiffies) of the last device open
- * session.
- * @open_counter: number of successful device open operations.
- * @fw_poll_interval_usec: FW status poll interval in usec.
- * used for CPU boot status
- * @fw_comms_poll_interval_usec: FW comms/protocol poll interval in usec.
- * used for COMMs protocols cmds(COMMS_STS_*)
- * @dram_binning: contains mask of drams that is received from the f/w which indicates which
- * drams are binned-out
- * @tpc_binning: contains mask of tpc engines that is received from the f/w which indicates which
- * tpc engines are binned-out
- * @card_type: Various ASICs have several card types. This indicates the card
- * type of the current device.
- * @major: habanalabs kernel driver major.
- * @high_pll: high PLL profile frequency.
- * @decoder_binning: contains mask of decoder engines that is received from the f/w which
- * indicates which decoder engines are binned-out
- * @edma_binning: contains mask of edma engines that is received from the f/w which
- * indicates which edma engines are binned-out
- * @device_release_watchdog_timeout_sec: device release watchdog timeout value in seconds.
- * @rotator_binning: contains mask of rotators engines that is received from the f/w
- * which indicates which rotator engines are binned-out(Gaudi3 and above).
- * @id: device minor.
- * @id_control: minor of the control device.
- * @cdev_idx: char device index. Used for setting its name.
- * @cpu_pci_msb_addr: 50-bit extension bits for the device CPU's 40-bit
- * addresses.
- * @is_in_dram_scrub: true if dram scrub operation is on going.
- * @disabled: is device disabled.
- * @late_init_done: is late init stage was done during initialization.
- * @hwmon_initialized: is H/W monitor sensors was initialized.
- * @reset_on_lockup: true if a reset should be done in case of stuck CS, false
- * otherwise.
- * @dram_default_page_mapping: is DRAM default page mapping enabled.
- * @memory_scrub: true to perform device memory scrub in various locations,
- * such as context-switch, context close, page free, etc.
- * @pmmu_huge_range: is a different virtual addresses range used for PMMU with
- * huge pages.
- * @init_done: is the initialization of the device done.
- * @device_cpu_disabled: is the device CPU disabled (due to timeouts)
- * @in_debug: whether the device is in a state where the profiling/tracing infrastructure
- * can be used. This indication is needed because in some ASICs we need to do
- * specific operations to enable that infrastructure.
- * @cdev_sysfs_created: were char devices and sysfs nodes created.
- * @stop_on_err: true if engines should stop on error.
- * @supports_sync_stream: is sync stream supported.
- * @sync_stream_queue_idx: helper index for sync stream queues initialization.
- * @collective_mon_idx: helper index for collective initialization
- * @supports_coresight: is CoreSight supported.
- * @supports_cb_mapping: is mapping a CB to the device's MMU supported.
- * @process_kill_trial_cnt: number of trials reset thread tried killing
- * user processes
- * @device_fini_pending: true if device_fini was called and might be
- * waiting for the reset thread to finish
- * @supports_staged_submission: true if staged submissions are supported
- * @device_cpu_is_halted: Flag to indicate whether the device CPU was already
- * halted. We can't halt it again because the COMMS
- * protocol will throw an error. Relevant only for
- * cases where Linux was not loaded to device CPU
- * @supports_wait_for_multi_cs: true if wait for multi CS is supported
- * @is_compute_ctx_active: Whether there is an active compute context executing.
- * @compute_ctx_in_release: true if the current compute context is being released.
- * @supports_mmu_prefetch: true if prefetch is supported, otherwise false.
- * @reset_upon_device_release: reset the device when the user closes the file descriptor of the
- * device.
- * @nic_ports_mask: Controls which NIC ports are enabled. Used only for testing.
- * @fw_components: Controls which f/w components to load to the device. There are multiple f/w
- * stages and sometimes we want to stop at a certain stage. Used only for testing.
- * @mmu_enable: Whether to enable or disable the device MMU(s). Used only for testing.
- * @cpu_queues_enable: Whether to enable queues communication vs. the f/w. Used only for testing.
- * @pldm: Whether we are running in Palladium environment. Used only for testing.
- * @hard_reset_on_fw_events: Whether to do device hard-reset when a fatal event is received from
- * the f/w. Used only for testing.
- * @bmc_enable: Whether we are running in a box with BMC. Used only for testing.
- * @reset_on_preboot_fail: Whether to reset the device if preboot f/w fails to load.
- * Used only for testing.
- * @heartbeat: Controls if we want to enable the heartbeat mechanism vs. the f/w, which verifies
- * that the f/w is always alive. Used only for testing.
- * @supports_ctx_switch: true if a ctx switch is required upon first submission.
- * @support_preboot_binning: true if we support read binning info from preboot.
- */
-struct hl_device {
- struct pci_dev *pdev;
- u64 pcie_bar_phys[HL_PCI_NUM_BARS];
- void __iomem *pcie_bar[HL_PCI_NUM_BARS];
- void __iomem *rmmio;
- struct cdev cdev;
- struct cdev cdev_ctrl;
- struct device *dev;
- struct device *dev_ctrl;
- struct delayed_work work_heartbeat;
- struct hl_device_reset_work device_reset_work;
- struct hl_device_reset_work device_release_watchdog_work;
- char asic_name[HL_STR_MAX];
- char status[HL_DEV_STS_MAX][HL_STR_MAX];
- enum hl_asic_type asic_type;
- struct hl_cq *completion_queue;
- struct hl_user_interrupt *user_interrupt;
- struct hl_user_interrupt common_user_cq_interrupt;
- struct hl_user_interrupt common_decoder_interrupt;
- struct hl_cs **shadow_cs_queue;
- struct workqueue_struct **cq_wq;
- struct workqueue_struct *eq_wq;
- struct workqueue_struct *cs_cmplt_wq;
- struct workqueue_struct *ts_free_obj_wq;
- struct workqueue_struct *prefetch_wq;
- struct workqueue_struct *reset_wq;
- struct hl_ctx *kernel_ctx;
- struct hl_hw_queue *kernel_queues;
- struct list_head cs_mirror_list;
- spinlock_t cs_mirror_lock;
- struct hl_mem_mgr kernel_mem_mgr;
- struct hl_eq event_queue;
- struct dma_pool *dma_pool;
- void *cpu_accessible_dma_mem;
- dma_addr_t cpu_accessible_dma_address;
- struct gen_pool *cpu_accessible_dma_pool;
- unsigned long *asid_bitmap;
- struct mutex asid_mutex;
- struct mutex send_cpu_message_lock;
- struct mutex debug_lock;
- struct mutex mmu_lock;
- struct asic_fixed_properties asic_prop;
- const struct hl_asic_funcs *asic_funcs;
- void *asic_specific;
- struct hl_vm vm;
- struct device *hwmon_dev;
- struct hwmon_chip_info *hl_chip_info;
-
- struct hl_dbg_device_entry hl_debugfs;
-
- struct list_head cb_pool;
- spinlock_t cb_pool_lock;
-
- void *internal_cb_pool_virt_addr;
- dma_addr_t internal_cb_pool_dma_addr;
- struct gen_pool *internal_cb_pool;
- u64 internal_cb_va_base;
-
- struct list_head fpriv_list;
- struct list_head fpriv_ctrl_list;
- struct mutex fpriv_list_lock;
- struct mutex fpriv_ctrl_list_lock;
-
- struct hl_cs_counters_atomic aggregated_cs_counters;
-
- struct hl_mmu_priv mmu_priv;
- struct hl_mmu_funcs mmu_func[MMU_NUM_PGT_LOCATIONS];
-
- struct hl_dec *dec;
-
- struct fw_load_mgr fw_loader;
-
- struct pci_mem_region pci_mem_region[PCI_REGION_NUMBER];
-
- struct hl_state_dump_specs state_dump_specs;
-
- struct multi_cs_completion multi_cs_completion[
- MULTI_CS_MAX_USER_CTX];
- struct hl_clk_throttle clk_throttling;
- struct hl_error_info captured_err_info;
-
- struct hl_reset_info reset_info;
-
- u32 *stream_master_qid_arr;
- u32 fw_major_version;
- u32 fw_minor_version;
- atomic64_t dram_used_mem;
- u64 memory_scrub_val;
- u64 timeout_jiffies;
- u64 max_power;
- u64 boot_error_status_mask;
- u64 dram_pci_bar_start;
- u64 last_successful_open_jif;
- u64 last_open_session_duration_jif;
- u64 open_counter;
- u64 fw_poll_interval_usec;
- ktime_t last_successful_open_ktime;
- u64 fw_comms_poll_interval_usec;
- u64 dram_binning;
- u64 tpc_binning;
-
- enum cpucp_card_types card_type;
- u32 major;
- u32 high_pll;
- u32 decoder_binning;
- u32 edma_binning;
- u32 device_release_watchdog_timeout_sec;
- u32 rotator_binning;
- u16 id;
- u16 id_control;
- u16 cdev_idx;
- u16 cpu_pci_msb_addr;
- u8 is_in_dram_scrub;
- u8 disabled;
- u8 late_init_done;
- u8 hwmon_initialized;
- u8 reset_on_lockup;
- u8 dram_default_page_mapping;
- u8 memory_scrub;
- u8 pmmu_huge_range;
- u8 init_done;
- u8 device_cpu_disabled;
- u8 in_debug;
- u8 cdev_sysfs_created;
- u8 stop_on_err;
- u8 supports_sync_stream;
- u8 sync_stream_queue_idx;
- u8 collective_mon_idx;
- u8 supports_coresight;
- u8 supports_cb_mapping;
- u8 process_kill_trial_cnt;
- u8 device_fini_pending;
- u8 supports_staged_submission;
- u8 device_cpu_is_halted;
- u8 supports_wait_for_multi_cs;
- u8 stream_master_qid_arr_size;
- u8 is_compute_ctx_active;
- u8 compute_ctx_in_release;
- u8 supports_mmu_prefetch;
- u8 reset_upon_device_release;
- u8 supports_ctx_switch;
- u8 support_preboot_binning;
-
- /* Parameters for bring-up */
- u64 nic_ports_mask;
- u64 fw_components;
- u8 mmu_enable;
- u8 cpu_queues_enable;
- u8 pldm;
- u8 hard_reset_on_fw_events;
- u8 bmc_enable;
- u8 reset_on_preboot_fail;
- u8 heartbeat;
-};
-
-
-/**
- * struct hl_cs_encaps_sig_handle - encapsulated signals handle structure
- * @refcount: refcount used to protect removing this id when several
- * wait cs are used to wait of the reserved encaps signals.
- * @hdev: pointer to habanalabs device structure.
- * @hw_sob: pointer to H/W SOB used in the reservation.
- * @ctx: pointer to the user's context data structure
- * @cs_seq: staged cs sequence which contains encapsulated signals
- * @id: idr handler id to be used to fetch the handler info
- * @q_idx: stream queue index
- * @pre_sob_val: current SOB value before reservation
- * @count: signals number
- */
-struct hl_cs_encaps_sig_handle {
- struct kref refcount;
- struct hl_device *hdev;
- struct hl_hw_sob *hw_sob;
- struct hl_ctx *ctx;
- u64 cs_seq;
- u32 id;
- u32 q_idx;
- u32 pre_sob_val;
- u32 count;
-};
-
-/*
- * IOCTLs
- */
-
-/**
- * typedef hl_ioctl_t - typedef for ioctl function in the driver
- * @hpriv: pointer to the FD's private data, which contains state of
- * user process
- * @data: pointer to the input/output arguments structure of the IOCTL
- *
- * Return: 0 for success, negative value for error
- */
-typedef int hl_ioctl_t(struct hl_fpriv *hpriv, void *data);
-
-/**
- * struct hl_ioctl_desc - describes an IOCTL entry of the driver.
- * @cmd: the IOCTL code as created by the kernel macros.
- * @func: pointer to the driver's function that should be called for this IOCTL.
- */
-struct hl_ioctl_desc {
- unsigned int cmd;
- hl_ioctl_t *func;
-};
-
-
-/*
- * Kernel module functions that can be accessed by entire module
- */
-
-/**
- * hl_get_sg_info() - get number of pages and the DMA address from SG list.
- * @sg: the SG list.
- * @dma_addr: pointer to DMA address to return.
- *
- * Calculate the number of consecutive pages described by the SG list. Take the
- * offset of the address in the first page, add to it the length and round it up
- * to the number of needed pages.
- */
-static inline u32 hl_get_sg_info(struct scatterlist *sg, dma_addr_t *dma_addr)
-{
- *dma_addr = sg_dma_address(sg);
-
- return ((((*dma_addr) & (PAGE_SIZE - 1)) + sg_dma_len(sg)) +
- (PAGE_SIZE - 1)) >> PAGE_SHIFT;
-}
-
-/**
- * hl_mem_area_inside_range() - Checks whether address+size are inside a range.
- * @address: The start address of the area we want to validate.
- * @size: The size in bytes of the area we want to validate.
- * @range_start_address: The start address of the valid range.
- * @range_end_address: The end address of the valid range.
- *
- * Return: true if the area is inside the valid range, false otherwise.
- */
-static inline bool hl_mem_area_inside_range(u64 address, u64 size,
- u64 range_start_address, u64 range_end_address)
-{
- u64 end_address = address + size;
-
- if ((address >= range_start_address) &&
- (end_address <= range_end_address) &&
- (end_address > address))
- return true;
-
- return false;
-}
-
-/**
- * hl_mem_area_crosses_range() - Checks whether address+size crossing a range.
- * @address: The start address of the area we want to validate.
- * @size: The size in bytes of the area we want to validate.
- * @range_start_address: The start address of the valid range.
- * @range_end_address: The end address of the valid range.
- *
- * Return: true if the area overlaps part or all of the valid range,
- * false otherwise.
- */
-static inline bool hl_mem_area_crosses_range(u64 address, u32 size,
- u64 range_start_address, u64 range_end_address)
-{
- u64 end_address = address + size - 1;
-
- return ((address <= range_end_address) && (range_start_address <= end_address));
-}
-
-uint64_t hl_set_dram_bar_default(struct hl_device *hdev, u64 addr);
-void *hl_asic_dma_alloc_coherent_caller(struct hl_device *hdev, size_t size, dma_addr_t *dma_handle,
- gfp_t flag, const char *caller);
-void hl_asic_dma_free_coherent_caller(struct hl_device *hdev, size_t size, void *cpu_addr,
- dma_addr_t dma_handle, const char *caller);
-void *hl_cpu_accessible_dma_pool_alloc_caller(struct hl_device *hdev, size_t size,
- dma_addr_t *dma_handle, const char *caller);
-void hl_cpu_accessible_dma_pool_free_caller(struct hl_device *hdev, size_t size, void *vaddr,
- const char *caller);
-void *hl_asic_dma_pool_zalloc_caller(struct hl_device *hdev, size_t size, gfp_t mem_flags,
- dma_addr_t *dma_handle, const char *caller);
-void hl_asic_dma_pool_free_caller(struct hl_device *hdev, void *vaddr, dma_addr_t dma_addr,
- const char *caller);
-int hl_dma_map_sgtable(struct hl_device *hdev, struct sg_table *sgt, enum dma_data_direction dir);
-void hl_dma_unmap_sgtable(struct hl_device *hdev, struct sg_table *sgt,
- enum dma_data_direction dir);
-int hl_access_sram_dram_region(struct hl_device *hdev, u64 addr, u64 *val,
- enum debugfs_access_type acc_type, enum pci_region region_type, bool set_dram_bar);
-int hl_access_cfg_region(struct hl_device *hdev, u64 addr, u64 *val,
- enum debugfs_access_type acc_type);
-int hl_access_dev_mem(struct hl_device *hdev, enum pci_region region_type,
- u64 addr, u64 *val, enum debugfs_access_type acc_type);
-int hl_device_open(struct inode *inode, struct file *filp);
-int hl_device_open_ctrl(struct inode *inode, struct file *filp);
-bool hl_device_operational(struct hl_device *hdev,
- enum hl_device_status *status);
-bool hl_ctrl_device_operational(struct hl_device *hdev,
- enum hl_device_status *status);
-enum hl_device_status hl_device_status(struct hl_device *hdev);
-int hl_device_set_debug_mode(struct hl_device *hdev, struct hl_ctx *ctx, bool enable);
-int hl_hw_queues_create(struct hl_device *hdev);
-void hl_hw_queues_destroy(struct hl_device *hdev);
-int hl_hw_queue_send_cb_no_cmpl(struct hl_device *hdev, u32 hw_queue_id,
- u32 cb_size, u64 cb_ptr);
-void hl_hw_queue_submit_bd(struct hl_device *hdev, struct hl_hw_queue *q,
- u32 ctl, u32 len, u64 ptr);
-int hl_hw_queue_schedule_cs(struct hl_cs *cs);
-u32 hl_hw_queue_add_ptr(u32 ptr, u16 val);
-void hl_hw_queue_inc_ci_kernel(struct hl_device *hdev, u32 hw_queue_id);
-void hl_hw_queue_update_ci(struct hl_cs *cs);
-void hl_hw_queue_reset(struct hl_device *hdev, bool hard_reset);
-
-#define hl_queue_inc_ptr(p) hl_hw_queue_add_ptr(p, 1)
-#define hl_pi_2_offset(pi) ((pi) & (HL_QUEUE_LENGTH - 1))
-
-int hl_cq_init(struct hl_device *hdev, struct hl_cq *q, u32 hw_queue_id);
-void hl_cq_fini(struct hl_device *hdev, struct hl_cq *q);
-int hl_eq_init(struct hl_device *hdev, struct hl_eq *q);
-void hl_eq_fini(struct hl_device *hdev, struct hl_eq *q);
-void hl_cq_reset(struct hl_device *hdev, struct hl_cq *q);
-void hl_eq_reset(struct hl_device *hdev, struct hl_eq *q);
-irqreturn_t hl_irq_handler_cq(int irq, void *arg);
-irqreturn_t hl_irq_handler_eq(int irq, void *arg);
-irqreturn_t hl_irq_handler_dec_abnrm(int irq, void *arg);
-irqreturn_t hl_irq_handler_user_interrupt(int irq, void *arg);
-irqreturn_t hl_irq_handler_default(int irq, void *arg);
-u32 hl_cq_inc_ptr(u32 ptr);
-
-int hl_asid_init(struct hl_device *hdev);
-void hl_asid_fini(struct hl_device *hdev);
-unsigned long hl_asid_alloc(struct hl_device *hdev);
-void hl_asid_free(struct hl_device *hdev, unsigned long asid);
-
-int hl_ctx_create(struct hl_device *hdev, struct hl_fpriv *hpriv);
-void hl_ctx_free(struct hl_device *hdev, struct hl_ctx *ctx);
-int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx);
-void hl_ctx_do_release(struct kref *ref);
-void hl_ctx_get(struct hl_ctx *ctx);
-int hl_ctx_put(struct hl_ctx *ctx);
-struct hl_ctx *hl_get_compute_ctx(struct hl_device *hdev);
-struct hl_fence *hl_ctx_get_fence(struct hl_ctx *ctx, u64 seq);
-int hl_ctx_get_fences(struct hl_ctx *ctx, u64 *seq_arr,
- struct hl_fence **fence, u32 arr_len);
-void hl_ctx_mgr_init(struct hl_ctx_mgr *mgr);
-void hl_ctx_mgr_fini(struct hl_device *hdev, struct hl_ctx_mgr *mgr);
-
-int hl_device_init(struct hl_device *hdev, struct class *hclass);
-void hl_device_fini(struct hl_device *hdev);
-int hl_device_suspend(struct hl_device *hdev);
-int hl_device_resume(struct hl_device *hdev);
-int hl_device_reset(struct hl_device *hdev, u32 flags);
-int hl_device_cond_reset(struct hl_device *hdev, u32 flags, u64 event_mask);
-void hl_hpriv_get(struct hl_fpriv *hpriv);
-int hl_hpriv_put(struct hl_fpriv *hpriv);
-int hl_device_utilization(struct hl_device *hdev, u32 *utilization);
-
-int hl_build_hwmon_channel_info(struct hl_device *hdev,
- struct cpucp_sensor *sensors_arr);
-
-void hl_notifier_event_send_all(struct hl_device *hdev, u64 event_mask);
-
-int hl_sysfs_init(struct hl_device *hdev);
-void hl_sysfs_fini(struct hl_device *hdev);
-
-int hl_hwmon_init(struct hl_device *hdev);
-void hl_hwmon_fini(struct hl_device *hdev);
-void hl_hwmon_release_resources(struct hl_device *hdev);
-
-int hl_cb_create(struct hl_device *hdev, struct hl_mem_mgr *mmg,
- struct hl_ctx *ctx, u32 cb_size, bool internal_cb,
- bool map_cb, u64 *handle);
-int hl_cb_destroy(struct hl_mem_mgr *mmg, u64 cb_handle);
-int hl_hw_block_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma);
-struct hl_cb *hl_cb_get(struct hl_mem_mgr *mmg, u64 handle);
-void hl_cb_put(struct hl_cb *cb);
-struct hl_cb *hl_cb_kernel_create(struct hl_device *hdev, u32 cb_size,
- bool internal_cb);
-int hl_cb_pool_init(struct hl_device *hdev);
-int hl_cb_pool_fini(struct hl_device *hdev);
-int hl_cb_va_pool_init(struct hl_ctx *ctx);
-void hl_cb_va_pool_fini(struct hl_ctx *ctx);
-
-void hl_cs_rollback_all(struct hl_device *hdev, bool skip_wq_flush);
-struct hl_cs_job *hl_cs_allocate_job(struct hl_device *hdev,
- enum hl_queue_type queue_type, bool is_kernel_allocated_cb);
-void hl_sob_reset_error(struct kref *ref);
-int hl_gen_sob_mask(u16 sob_base, u8 sob_mask, u8 *mask);
-void hl_fence_put(struct hl_fence *fence);
-void hl_fences_put(struct hl_fence **fence, int len);
-void hl_fence_get(struct hl_fence *fence);
-void cs_get(struct hl_cs *cs);
-bool cs_needs_completion(struct hl_cs *cs);
-bool cs_needs_timeout(struct hl_cs *cs);
-bool is_staged_cs_last_exists(struct hl_device *hdev, struct hl_cs *cs);
-struct hl_cs *hl_staged_cs_find_first(struct hl_device *hdev, u64 cs_seq);
-void hl_multi_cs_completion_init(struct hl_device *hdev);
-
-void goya_set_asic_funcs(struct hl_device *hdev);
-void gaudi_set_asic_funcs(struct hl_device *hdev);
-void gaudi2_set_asic_funcs(struct hl_device *hdev);
-
-int hl_vm_ctx_init(struct hl_ctx *ctx);
-void hl_vm_ctx_fini(struct hl_ctx *ctx);
-
-int hl_vm_init(struct hl_device *hdev);
-void hl_vm_fini(struct hl_device *hdev);
-
-void hl_hw_block_mem_init(struct hl_ctx *ctx);
-void hl_hw_block_mem_fini(struct hl_ctx *ctx);
-
-u64 hl_reserve_va_block(struct hl_device *hdev, struct hl_ctx *ctx,
- enum hl_va_range_type type, u64 size, u32 alignment);
-int hl_unreserve_va_block(struct hl_device *hdev, struct hl_ctx *ctx,
- u64 start_addr, u64 size);
-int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size,
- struct hl_userptr *userptr);
-void hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr);
-void hl_userptr_delete_list(struct hl_device *hdev,
- struct list_head *userptr_list);
-bool hl_userptr_is_pinned(struct hl_device *hdev, u64 addr, u32 size,
- struct list_head *userptr_list,
- struct hl_userptr **userptr);
-
-int hl_mmu_init(struct hl_device *hdev);
-void hl_mmu_fini(struct hl_device *hdev);
-int hl_mmu_ctx_init(struct hl_ctx *ctx);
-void hl_mmu_ctx_fini(struct hl_ctx *ctx);
-int hl_mmu_map_page(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr,
- u32 page_size, bool flush_pte);
-int hl_mmu_get_real_page_size(struct hl_device *hdev, struct hl_mmu_properties *mmu_prop,
- u32 page_size, u32 *real_page_size, bool is_dram_addr);
-int hl_mmu_unmap_page(struct hl_ctx *ctx, u64 virt_addr, u32 page_size,
- bool flush_pte);
-int hl_mmu_map_contiguous(struct hl_ctx *ctx, u64 virt_addr,
- u64 phys_addr, u32 size);
-int hl_mmu_unmap_contiguous(struct hl_ctx *ctx, u64 virt_addr, u32 size);
-int hl_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, u32 flags);
-int hl_mmu_invalidate_cache_range(struct hl_device *hdev, bool is_hard,
- u32 flags, u32 asid, u64 va, u64 size);
-int hl_mmu_prefetch_cache_range(struct hl_ctx *ctx, u32 flags, u32 asid, u64 va, u64 size);
-u64 hl_mmu_get_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte);
-u64 hl_mmu_get_hop_pte_phys_addr(struct hl_ctx *ctx, struct hl_mmu_properties *mmu_prop,
- u8 hop_idx, u64 hop_addr, u64 virt_addr);
-void hl_mmu_hr_flush(struct hl_ctx *ctx);
-int hl_mmu_hr_init(struct hl_device *hdev, struct hl_mmu_hr_priv *hr_priv, u32 hop_table_size,
- u64 pgt_size);
-void hl_mmu_hr_fini(struct hl_device *hdev, struct hl_mmu_hr_priv *hr_priv, u32 hop_table_size);
-void hl_mmu_hr_free_hop_remove_pgt(struct pgt_info *pgt_info, struct hl_mmu_hr_priv *hr_priv,
- u32 hop_table_size);
-u64 hl_mmu_hr_pte_phys_to_virt(struct hl_ctx *ctx, struct pgt_info *pgt, u64 phys_pte_addr,
- u32 hop_table_size);
-void hl_mmu_hr_write_pte(struct hl_ctx *ctx, struct pgt_info *pgt_info, u64 phys_pte_addr,
- u64 val, u32 hop_table_size);
-void hl_mmu_hr_clear_pte(struct hl_ctx *ctx, struct pgt_info *pgt_info, u64 phys_pte_addr,
- u32 hop_table_size);
-int hl_mmu_hr_put_pte(struct hl_ctx *ctx, struct pgt_info *pgt_info, struct hl_mmu_hr_priv *hr_priv,
- u32 hop_table_size);
-void hl_mmu_hr_get_pte(struct hl_ctx *ctx, struct hl_hr_mmu_funcs *hr_func, u64 phys_hop_addr);
-struct pgt_info *hl_mmu_hr_get_next_hop_pgt_info(struct hl_ctx *ctx,
- struct hl_hr_mmu_funcs *hr_func,
- u64 curr_pte);
-struct pgt_info *hl_mmu_hr_alloc_hop(struct hl_ctx *ctx, struct hl_mmu_hr_priv *hr_priv,
- struct hl_hr_mmu_funcs *hr_func,
- struct hl_mmu_properties *mmu_prop);
-struct pgt_info *hl_mmu_hr_get_alloc_next_hop(struct hl_ctx *ctx,
- struct hl_mmu_hr_priv *hr_priv,
- struct hl_hr_mmu_funcs *hr_func,
- struct hl_mmu_properties *mmu_prop,
- u64 curr_pte, bool *is_new_hop);
-int hl_mmu_hr_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr, struct hl_mmu_hop_info *hops,
- struct hl_hr_mmu_funcs *hr_func);
-void hl_mmu_swap_out(struct hl_ctx *ctx);
-void hl_mmu_swap_in(struct hl_ctx *ctx);
-int hl_mmu_if_set_funcs(struct hl_device *hdev);
-void hl_mmu_v1_set_funcs(struct hl_device *hdev, struct hl_mmu_funcs *mmu);
-void hl_mmu_v2_hr_set_funcs(struct hl_device *hdev, struct hl_mmu_funcs *mmu);
-int hl_mmu_va_to_pa(struct hl_ctx *ctx, u64 virt_addr, u64 *phys_addr);
-int hl_mmu_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr,
- struct hl_mmu_hop_info *hops);
-u64 hl_mmu_scramble_addr(struct hl_device *hdev, u64 addr);
-u64 hl_mmu_descramble_addr(struct hl_device *hdev, u64 addr);
-bool hl_is_dram_va(struct hl_device *hdev, u64 virt_addr);
-
-int hl_fw_load_fw_to_device(struct hl_device *hdev, const char *fw_name,
- void __iomem *dst, u32 src_offset, u32 size);
-int hl_fw_send_pci_access_msg(struct hl_device *hdev, u32 opcode, u64 value);
-int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg,
- u16 len, u32 timeout, u64 *result);
-int hl_fw_unmask_irq(struct hl_device *hdev, u16 event_type);
-int hl_fw_unmask_irq_arr(struct hl_device *hdev, const u32 *irq_arr,
- size_t irq_arr_size);
-int hl_fw_test_cpu_queue(struct hl_device *hdev);
-void *hl_fw_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size,
- dma_addr_t *dma_handle);
-void hl_fw_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size,
- void *vaddr);
-int hl_fw_send_heartbeat(struct hl_device *hdev);
-int hl_fw_cpucp_info_get(struct hl_device *hdev,
- u32 sts_boot_dev_sts0_reg,
- u32 sts_boot_dev_sts1_reg, u32 boot_err0_reg,
- u32 boot_err1_reg);
-int hl_fw_cpucp_handshake(struct hl_device *hdev,
- u32 sts_boot_dev_sts0_reg,
- u32 sts_boot_dev_sts1_reg, u32 boot_err0_reg,
- u32 boot_err1_reg);
-int hl_fw_get_eeprom_data(struct hl_device *hdev, void *data, size_t max_size);
-int hl_fw_get_monitor_dump(struct hl_device *hdev, void *data);
-int hl_fw_cpucp_pci_counters_get(struct hl_device *hdev,
- struct hl_info_pci_counters *counters);
-int hl_fw_cpucp_total_energy_get(struct hl_device *hdev,
- u64 *total_energy);
-int get_used_pll_index(struct hl_device *hdev, u32 input_pll_index,
- enum pll_index *pll_index);
-int hl_fw_cpucp_pll_info_get(struct hl_device *hdev, u32 pll_index,
- u16 *pll_freq_arr);
-int hl_fw_cpucp_power_get(struct hl_device *hdev, u64 *power);
-void hl_fw_ask_hard_reset_without_linux(struct hl_device *hdev);
-void hl_fw_ask_halt_machine_without_linux(struct hl_device *hdev);
-int hl_fw_init_cpu(struct hl_device *hdev);
-int hl_fw_wait_preboot_ready(struct hl_device *hdev);
-int hl_fw_read_preboot_status(struct hl_device *hdev);
-int hl_fw_dynamic_send_protocol_cmd(struct hl_device *hdev,
- struct fw_load_mgr *fw_loader,
- enum comms_cmd cmd, unsigned int size,
- bool wait_ok, u32 timeout);
-int hl_fw_dram_replaced_row_get(struct hl_device *hdev,
- struct cpucp_hbm_row_info *info);
-int hl_fw_dram_pending_row_get(struct hl_device *hdev, u32 *pend_rows_num);
-int hl_fw_cpucp_engine_core_asid_set(struct hl_device *hdev, u32 asid);
-int hl_fw_send_device_activity(struct hl_device *hdev, bool open);
-int hl_pci_bars_map(struct hl_device *hdev, const char * const name[3],
- bool is_wc[3]);
-int hl_pci_elbi_read(struct hl_device *hdev, u64 addr, u32 *data);
-int hl_pci_iatu_write(struct hl_device *hdev, u32 addr, u32 data);
-int hl_pci_set_inbound_region(struct hl_device *hdev, u8 region,
- struct hl_inbound_pci_region *pci_region);
-int hl_pci_set_outbound_region(struct hl_device *hdev,
- struct hl_outbound_pci_region *pci_region);
-enum pci_region hl_get_pci_memory_region(struct hl_device *hdev, u64 addr);
-int hl_pci_init(struct hl_device *hdev);
-void hl_pci_fini(struct hl_device *hdev);
-
-long hl_fw_get_frequency(struct hl_device *hdev, u32 pll_index, bool curr);
-void hl_fw_set_frequency(struct hl_device *hdev, u32 pll_index, u64 freq);
-int hl_get_temperature(struct hl_device *hdev, int sensor_index, u32 attr, long *value);
-int hl_set_temperature(struct hl_device *hdev, int sensor_index, u32 attr, long value);
-int hl_get_voltage(struct hl_device *hdev, int sensor_index, u32 attr, long *value);
-int hl_get_current(struct hl_device *hdev, int sensor_index, u32 attr, long *value);
-int hl_get_fan_speed(struct hl_device *hdev, int sensor_index, u32 attr, long *value);
-int hl_get_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, long *value);
-void hl_set_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr, long value);
-long hl_fw_get_max_power(struct hl_device *hdev);
-void hl_fw_set_max_power(struct hl_device *hdev);
-int hl_fw_get_sec_attest_info(struct hl_device *hdev, struct cpucp_sec_attest_info *sec_attest_info,
- u32 nonce);
-int hl_set_voltage(struct hl_device *hdev, int sensor_index, u32 attr, long value);
-int hl_set_current(struct hl_device *hdev, int sensor_index, u32 attr, long value);
-int hl_set_power(struct hl_device *hdev, int sensor_index, u32 attr, long value);
-int hl_get_power(struct hl_device *hdev, int sensor_index, u32 attr, long *value);
-int hl_fw_get_clk_rate(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk);
-void hl_fw_set_pll_profile(struct hl_device *hdev);
-void hl_sysfs_add_dev_clk_attr(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp);
-void hl_sysfs_add_dev_vrm_attr(struct hl_device *hdev, struct attribute_group *dev_vrm_attr_grp);
-int hl_fw_send_generic_request(struct hl_device *hdev, enum hl_passthrough_type sub_opcode,
- dma_addr_t buff, u32 *size);
-
-void hw_sob_get(struct hl_hw_sob *hw_sob);
-void hw_sob_put(struct hl_hw_sob *hw_sob);
-void hl_encaps_release_handle_and_put_ctx(struct kref *ref);
-void hl_encaps_release_handle_and_put_sob_ctx(struct kref *ref);
-void hl_hw_queue_encaps_sig_set_sob_info(struct hl_device *hdev,
- struct hl_cs *cs, struct hl_cs_job *job,
- struct hl_cs_compl *cs_cmpl);
-
-int hl_dec_init(struct hl_device *hdev);
-void hl_dec_fini(struct hl_device *hdev);
-void hl_dec_ctx_fini(struct hl_ctx *ctx);
-
-void hl_release_pending_user_interrupts(struct hl_device *hdev);
-void hl_abort_waitings_for_completion(struct hl_device *hdev);
-int hl_cs_signal_sob_wraparound_handler(struct hl_device *hdev, u32 q_idx,
- struct hl_hw_sob **hw_sob, u32 count, bool encaps_sig);
-
-int hl_state_dump(struct hl_device *hdev);
-const char *hl_state_dump_get_sync_name(struct hl_device *hdev, u32 sync_id);
-const char *hl_state_dump_get_monitor_name(struct hl_device *hdev,
- struct hl_mon_state_dump *mon);
-void hl_state_dump_free_sync_to_engine_map(struct hl_sync_to_engine_map *map);
-__printf(4, 5) int hl_snprintf_resize(char **buf, size_t *size, size_t *offset,
- const char *format, ...);
-char *hl_format_as_binary(char *buf, size_t buf_len, u32 n);
-const char *hl_sync_engine_to_string(enum hl_sync_engine_type engine_type);
-
-void hl_mem_mgr_init(struct device *dev, struct hl_mem_mgr *mmg, u8 is_kernel_mem_mgr);
-void hl_mem_mgr_fini(struct hl_mem_mgr *mmg);
-int hl_mem_mgr_mmap(struct hl_mem_mgr *mmg, struct vm_area_struct *vma,
- void *args);
-struct hl_mmap_mem_buf *hl_mmap_mem_buf_get(struct hl_mem_mgr *mmg,
- u64 handle);
-int hl_mmap_mem_buf_put_handle(struct hl_mem_mgr *mmg, u64 handle);
-int hl_mmap_mem_buf_put(struct hl_mmap_mem_buf *buf);
-struct hl_mmap_mem_buf *
-hl_mmap_mem_buf_alloc(struct hl_mem_mgr *mmg,
- struct hl_mmap_mem_buf_behavior *behavior, gfp_t gfp,
- void *args);
-__printf(2, 3) void hl_engine_data_sprintf(struct engines_data *e, const char *fmt, ...);
-void hl_capture_razwi(struct hl_device *hdev, u64 addr, u16 *engine_id, u16 num_of_engines,
- u8 flags);
-void hl_handle_razwi(struct hl_device *hdev, u64 addr, u16 *engine_id, u16 num_of_engines,
- u8 flags, u64 *event_mask);
-void hl_capture_page_fault(struct hl_device *hdev, u64 addr, u16 eng_id, bool is_pmmu);
-void hl_handle_page_fault(struct hl_device *hdev, u64 addr, u16 eng_id, bool is_pmmu,
- u64 *event_mask);
-
-#ifdef CONFIG_DEBUG_FS
-
-void hl_debugfs_init(void);
-void hl_debugfs_fini(void);
-void hl_debugfs_add_device(struct hl_device *hdev);
-void hl_debugfs_remove_device(struct hl_device *hdev);
-void hl_debugfs_add_file(struct hl_fpriv *hpriv);
-void hl_debugfs_remove_file(struct hl_fpriv *hpriv);
-void hl_debugfs_add_cb(struct hl_cb *cb);
-void hl_debugfs_remove_cb(struct hl_cb *cb);
-void hl_debugfs_add_cs(struct hl_cs *cs);
-void hl_debugfs_remove_cs(struct hl_cs *cs);
-void hl_debugfs_add_job(struct hl_device *hdev, struct hl_cs_job *job);
-void hl_debugfs_remove_job(struct hl_device *hdev, struct hl_cs_job *job);
-void hl_debugfs_add_userptr(struct hl_device *hdev, struct hl_userptr *userptr);
-void hl_debugfs_remove_userptr(struct hl_device *hdev,
- struct hl_userptr *userptr);
-void hl_debugfs_add_ctx_mem_hash(struct hl_device *hdev, struct hl_ctx *ctx);
-void hl_debugfs_remove_ctx_mem_hash(struct hl_device *hdev, struct hl_ctx *ctx);
-void hl_debugfs_set_state_dump(struct hl_device *hdev, char *data,
- unsigned long length);
-
-#else
-
-static inline void __init hl_debugfs_init(void)
-{
-}
-
-static inline void hl_debugfs_fini(void)
-{
-}
-
-static inline void hl_debugfs_add_device(struct hl_device *hdev)
-{
-}
-
-static inline void hl_debugfs_remove_device(struct hl_device *hdev)
-{
-}
-
-static inline void hl_debugfs_add_file(struct hl_fpriv *hpriv)
-{
-}
-
-static inline void hl_debugfs_remove_file(struct hl_fpriv *hpriv)
-{
-}
-
-static inline void hl_debugfs_add_cb(struct hl_cb *cb)
-{
-}
-
-static inline void hl_debugfs_remove_cb(struct hl_cb *cb)
-{
-}
-
-static inline void hl_debugfs_add_cs(struct hl_cs *cs)
-{
-}
-
-static inline void hl_debugfs_remove_cs(struct hl_cs *cs)
-{
-}
-
-static inline void hl_debugfs_add_job(struct hl_device *hdev,
- struct hl_cs_job *job)
-{
-}
-
-static inline void hl_debugfs_remove_job(struct hl_device *hdev,
- struct hl_cs_job *job)
-{
-}
-
-static inline void hl_debugfs_add_userptr(struct hl_device *hdev,
- struct hl_userptr *userptr)
-{
-}
-
-static inline void hl_debugfs_remove_userptr(struct hl_device *hdev,
- struct hl_userptr *userptr)
-{
-}
-
-static inline void hl_debugfs_add_ctx_mem_hash(struct hl_device *hdev,
- struct hl_ctx *ctx)
-{
-}
-
-static inline void hl_debugfs_remove_ctx_mem_hash(struct hl_device *hdev,
- struct hl_ctx *ctx)
-{
-}
-
-static inline void hl_debugfs_set_state_dump(struct hl_device *hdev,
- char *data, unsigned long length)
-{
-}
-
-#endif
-
-/* Security */
-int hl_unsecure_register(struct hl_device *hdev, u32 mm_reg_addr, int offset,
- const u32 pb_blocks[], struct hl_block_glbl_sec sgs_array[],
- int array_size);
-int hl_unsecure_registers(struct hl_device *hdev, const u32 mm_reg_array[],
- int mm_array_size, int offset, const u32 pb_blocks[],
- struct hl_block_glbl_sec sgs_array[], int blocks_array_size);
-void hl_config_glbl_sec(struct hl_device *hdev, const u32 pb_blocks[],
- struct hl_block_glbl_sec sgs_array[], u32 block_offset,
- int array_size);
-void hl_secure_block(struct hl_device *hdev,
- struct hl_block_glbl_sec sgs_array[], int array_size);
-int hl_init_pb_with_mask(struct hl_device *hdev, u32 num_dcores,
- u32 dcore_offset, u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size,
- const u32 *regs_array, u32 regs_array_size, u64 mask);
-int hl_init_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset,
- u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size,
- const u32 *regs_array, u32 regs_array_size);
-int hl_init_pb_ranges_with_mask(struct hl_device *hdev, u32 num_dcores,
- u32 dcore_offset, u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size,
- const struct range *regs_range_array, u32 regs_range_array_size,
- u64 mask);
-int hl_init_pb_ranges(struct hl_device *hdev, u32 num_dcores,
- u32 dcore_offset, u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size,
- const struct range *regs_range_array,
- u32 regs_range_array_size);
-int hl_init_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,
- u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size,
- const u32 *regs_array, u32 regs_array_size);
-int hl_init_pb_ranges_single_dcore(struct hl_device *hdev, u32 dcore_offset,
- u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size,
- const struct range *regs_range_array,
- u32 regs_range_array_size);
-void hl_ack_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset,
- u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size);
-void hl_ack_pb_with_mask(struct hl_device *hdev, u32 num_dcores,
- u32 dcore_offset, u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size, u64 mask);
-void hl_ack_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,
- u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size);
-
-/* IOCTLs */
-long hl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
-long hl_ioctl_control(struct file *filep, unsigned int cmd, unsigned long arg);
-int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data);
-int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data);
-int hl_wait_ioctl(struct hl_fpriv *hpriv, void *data);
-int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data);
-
-#endif /* HABANALABSP_H_ */
diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c
deleted file mode 100644
index a2983913d7c0..000000000000
--- a/drivers/misc/habanalabs/common/habanalabs_drv.c
+++ /dev/null
@@ -1,753 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2021 HabanaLabs, Ltd.
- * All Rights Reserved.
- *
- */
-
-#define pr_fmt(fmt) "habanalabs: " fmt
-
-#include "habanalabs.h"
-#include "../include/hw_ip/pci/pci_general.h"
-
-#include <linux/pci.h>
-#include <linux/aer.h>
-#include <linux/module.h>
-
-#define CREATE_TRACE_POINTS
-#include <trace/events/habanalabs.h>
-
-#define HL_DRIVER_AUTHOR "HabanaLabs Kernel Driver Team"
-
-#define HL_DRIVER_DESC "Driver for HabanaLabs's AI Accelerators"
-
-MODULE_AUTHOR(HL_DRIVER_AUTHOR);
-MODULE_DESCRIPTION(HL_DRIVER_DESC);
-MODULE_LICENSE("GPL v2");
-
-static int hl_major;
-static struct class *hl_class;
-static DEFINE_IDR(hl_devs_idr);
-static DEFINE_MUTEX(hl_devs_idr_lock);
-
-#define HL_DEFAULT_TIMEOUT_LOCKED 30 /* 30 seconds */
-#define GAUDI_DEFAULT_TIMEOUT_LOCKED 600 /* 10 minutes */
-
-static int timeout_locked = HL_DEFAULT_TIMEOUT_LOCKED;
-static int reset_on_lockup = 1;
-static int memory_scrub;
-static ulong boot_error_status_mask = ULONG_MAX;
-
-module_param(timeout_locked, int, 0444);
-MODULE_PARM_DESC(timeout_locked,
- "Device lockup timeout in seconds (0 = disabled, default 30s)");
-
-module_param(reset_on_lockup, int, 0444);
-MODULE_PARM_DESC(reset_on_lockup,
- "Do device reset on lockup (0 = no, 1 = yes, default yes)");
-
-module_param(memory_scrub, int, 0444);
-MODULE_PARM_DESC(memory_scrub,
- "Scrub device memory in various states (0 = no, 1 = yes, default no)");
-
-module_param(boot_error_status_mask, ulong, 0444);
-MODULE_PARM_DESC(boot_error_status_mask,
- "Mask of the error status during device CPU boot (If bitX is cleared then error X is masked. Default all 1's)");
-
-#define PCI_VENDOR_ID_HABANALABS 0x1da3
-
-#define PCI_IDS_GOYA 0x0001
-#define PCI_IDS_GAUDI 0x1000
-#define PCI_IDS_GAUDI_SEC 0x1010
-
-#define PCI_IDS_GAUDI2 0x1020
-
-static const struct pci_device_id ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GOYA), },
- { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GAUDI), },
- { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GAUDI_SEC), },
- { PCI_DEVICE(PCI_VENDOR_ID_HABANALABS, PCI_IDS_GAUDI2), },
- { 0, }
-};
-MODULE_DEVICE_TABLE(pci, ids);
-
-/*
- * get_asic_type - translate device id to asic type
- *
- * @hdev: pointer to habanalabs device structure.
- *
- * Translate device id and revision id to asic type.
- * In case of unidentified device, return -1
- */
-static enum hl_asic_type get_asic_type(struct hl_device *hdev)
-{
- struct pci_dev *pdev = hdev->pdev;
- enum hl_asic_type asic_type = ASIC_INVALID;
-
- switch (pdev->device) {
- case PCI_IDS_GOYA:
- asic_type = ASIC_GOYA;
- break;
- case PCI_IDS_GAUDI:
- asic_type = ASIC_GAUDI;
- break;
- case PCI_IDS_GAUDI_SEC:
- asic_type = ASIC_GAUDI_SEC;
- break;
- case PCI_IDS_GAUDI2:
- switch (pdev->revision) {
- case REV_ID_A:
- asic_type = ASIC_GAUDI2;
- break;
- case REV_ID_B:
- asic_type = ASIC_GAUDI2B;
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
-
- return asic_type;
-}
-
-static bool is_asic_secured(enum hl_asic_type asic_type)
-{
- switch (asic_type) {
- case ASIC_GAUDI_SEC:
- return true;
- default:
- return false;
- }
-}
-
-/*
- * hl_device_open - open function for habanalabs device
- *
- * @inode: pointer to inode structure
- * @filp: pointer to file structure
- *
- * Called when process opens an habanalabs device.
- */
-int hl_device_open(struct inode *inode, struct file *filp)
-{
- enum hl_device_status status;
- struct hl_device *hdev;
- struct hl_fpriv *hpriv;
- int rc;
-
- mutex_lock(&hl_devs_idr_lock);
- hdev = idr_find(&hl_devs_idr, iminor(inode));
- mutex_unlock(&hl_devs_idr_lock);
-
- if (!hdev) {
- pr_err("Couldn't find device %d:%d\n",
- imajor(inode), iminor(inode));
- return -ENXIO;
- }
-
- hpriv = kzalloc(sizeof(*hpriv), GFP_KERNEL);
- if (!hpriv)
- return -ENOMEM;
-
- hpriv->hdev = hdev;
- filp->private_data = hpriv;
- hpriv->filp = filp;
-
- mutex_init(&hpriv->notifier_event.lock);
- mutex_init(&hpriv->restore_phase_mutex);
- mutex_init(&hpriv->ctx_lock);
- kref_init(&hpriv->refcount);
- nonseekable_open(inode, filp);
-
- hl_ctx_mgr_init(&hpriv->ctx_mgr);
- hl_mem_mgr_init(hpriv->hdev->dev, &hpriv->mem_mgr, 0);
-
- hpriv->taskpid = get_task_pid(current, PIDTYPE_PID);
-
- mutex_lock(&hdev->fpriv_list_lock);
-
- if (!hl_device_operational(hdev, &status)) {
- dev_dbg_ratelimited(hdev->dev,
- "Can't open %s because it is %s\n",
- dev_name(hdev->dev), hdev->status[status]);
-
- if (status == HL_DEVICE_STATUS_IN_RESET ||
- status == HL_DEVICE_STATUS_IN_RESET_AFTER_DEVICE_RELEASE)
- rc = -EAGAIN;
- else
- rc = -EPERM;
-
- goto out_err;
- }
-
- if (hdev->is_in_dram_scrub) {
- dev_dbg_ratelimited(hdev->dev,
- "Can't open %s during dram scrub\n",
- dev_name(hdev->dev));
- rc = -EAGAIN;
- goto out_err;
- }
-
- if (hdev->compute_ctx_in_release) {
- dev_dbg_ratelimited(hdev->dev,
- "Can't open %s because another user is still releasing it\n",
- dev_name(hdev->dev));
- rc = -EAGAIN;
- goto out_err;
- }
-
- if (hdev->is_compute_ctx_active) {
- dev_dbg_ratelimited(hdev->dev,
- "Can't open %s because another user is working on it\n",
- dev_name(hdev->dev));
- rc = -EBUSY;
- goto out_err;
- }
-
- rc = hl_ctx_create(hdev, hpriv);
- if (rc) {
- dev_err(hdev->dev, "Failed to create context %d\n", rc);
- goto out_err;
- }
-
- list_add(&hpriv->dev_node, &hdev->fpriv_list);
- mutex_unlock(&hdev->fpriv_list_lock);
-
- hdev->asic_funcs->send_device_activity(hdev, true);
-
- hl_debugfs_add_file(hpriv);
-
- atomic_set(&hdev->captured_err_info.cs_timeout.write_enable, 1);
- atomic_set(&hdev->captured_err_info.razwi_info_recorded, 0);
- atomic_set(&hdev->captured_err_info.pgf_info_recorded, 0);
- hdev->captured_err_info.undef_opcode.write_enable = true;
-
- hdev->open_counter++;
- hdev->last_successful_open_jif = jiffies;
- hdev->last_successful_open_ktime = ktime_get();
-
- return 0;
-
-out_err:
- mutex_unlock(&hdev->fpriv_list_lock);
- hl_mem_mgr_fini(&hpriv->mem_mgr);
- hl_ctx_mgr_fini(hpriv->hdev, &hpriv->ctx_mgr);
- filp->private_data = NULL;
- mutex_destroy(&hpriv->ctx_lock);
- mutex_destroy(&hpriv->restore_phase_mutex);
- mutex_destroy(&hpriv->notifier_event.lock);
- put_pid(hpriv->taskpid);
-
- kfree(hpriv);
-
- return rc;
-}
-
-int hl_device_open_ctrl(struct inode *inode, struct file *filp)
-{
- struct hl_device *hdev;
- struct hl_fpriv *hpriv;
- int rc;
-
- mutex_lock(&hl_devs_idr_lock);
- hdev = idr_find(&hl_devs_idr, iminor(inode));
- mutex_unlock(&hl_devs_idr_lock);
-
- if (!hdev) {
- pr_err("Couldn't find device %d:%d\n",
- imajor(inode), iminor(inode));
- return -ENXIO;
- }
-
- hpriv = kzalloc(sizeof(*hpriv), GFP_KERNEL);
- if (!hpriv)
- return -ENOMEM;
-
- /* Prevent other routines from reading partial hpriv data by
- * initializing hpriv fields before inserting it to the list
- */
- hpriv->hdev = hdev;
- filp->private_data = hpriv;
- hpriv->filp = filp;
-
- mutex_init(&hpriv->notifier_event.lock);
- nonseekable_open(inode, filp);
-
- hpriv->taskpid = get_task_pid(current, PIDTYPE_PID);
-
- mutex_lock(&hdev->fpriv_ctrl_list_lock);
-
- if (!hl_ctrl_device_operational(hdev, NULL)) {
- dev_dbg_ratelimited(hdev->dev_ctrl,
- "Can't open %s because it is disabled\n",
- dev_name(hdev->dev_ctrl));
- rc = -EPERM;
- goto out_err;
- }
-
- list_add(&hpriv->dev_node, &hdev->fpriv_ctrl_list);
- mutex_unlock(&hdev->fpriv_ctrl_list_lock);
-
- return 0;
-
-out_err:
- mutex_unlock(&hdev->fpriv_ctrl_list_lock);
- filp->private_data = NULL;
- put_pid(hpriv->taskpid);
-
- kfree(hpriv);
-
- return rc;
-}
-
-static void set_driver_behavior_per_device(struct hl_device *hdev)
-{
- hdev->nic_ports_mask = 0;
- hdev->fw_components = FW_TYPE_ALL_TYPES;
- hdev->mmu_enable = MMU_EN_ALL;
- hdev->cpu_queues_enable = 1;
- hdev->pldm = 0;
- hdev->hard_reset_on_fw_events = 1;
- hdev->bmc_enable = 1;
- hdev->reset_on_preboot_fail = 1;
- hdev->heartbeat = 1;
-}
-
-static void copy_kernel_module_params_to_device(struct hl_device *hdev)
-{
- hdev->asic_prop.fw_security_enabled = is_asic_secured(hdev->asic_type);
-
- hdev->major = hl_major;
- hdev->memory_scrub = memory_scrub;
- hdev->reset_on_lockup = reset_on_lockup;
- hdev->boot_error_status_mask = boot_error_status_mask;
-}
-
-static void fixup_device_params_per_asic(struct hl_device *hdev, int timeout)
-{
- switch (hdev->asic_type) {
- case ASIC_GAUDI:
- case ASIC_GAUDI_SEC:
- /* If user didn't request a different timeout than the default one, we have
- * a different default timeout for Gaudi
- */
- if (timeout == HL_DEFAULT_TIMEOUT_LOCKED)
- hdev->timeout_jiffies = msecs_to_jiffies(GAUDI_DEFAULT_TIMEOUT_LOCKED *
- MSEC_PER_SEC);
-
- hdev->reset_upon_device_release = 0;
- break;
-
- case ASIC_GOYA:
- hdev->reset_upon_device_release = 0;
- break;
-
- default:
- hdev->reset_upon_device_release = 1;
- break;
- }
-}
-
-static int fixup_device_params(struct hl_device *hdev)
-{
- int tmp_timeout;
-
- tmp_timeout = timeout_locked;
-
- hdev->fw_poll_interval_usec = HL_FW_STATUS_POLL_INTERVAL_USEC;
- hdev->fw_comms_poll_interval_usec = HL_FW_STATUS_POLL_INTERVAL_USEC;
-
- if (tmp_timeout)
- hdev->timeout_jiffies = msecs_to_jiffies(tmp_timeout * MSEC_PER_SEC);
- else
- hdev->timeout_jiffies = MAX_SCHEDULE_TIMEOUT;
-
- hdev->stop_on_err = true;
- hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_UNKNOWN;
- hdev->reset_info.prev_reset_trigger = HL_RESET_TRIGGER_DEFAULT;
-
- /* Enable only after the initialization of the device */
- hdev->disabled = true;
-
- if (!(hdev->fw_components & FW_TYPE_PREBOOT_CPU) &&
- (hdev->fw_components & ~FW_TYPE_PREBOOT_CPU)) {
- pr_err("Preboot must be set along with other components");
- return -EINVAL;
- }
-
- /* If CPU queues not enabled, no way to do heartbeat */
- if (!hdev->cpu_queues_enable)
- hdev->heartbeat = 0;
-
- fixup_device_params_per_asic(hdev, tmp_timeout);
-
- return 0;
-}
-
-/**
- * create_hdev - create habanalabs device instance
- *
- * @dev: will hold the pointer to the new habanalabs device structure
- * @pdev: pointer to the pci device
- *
- * Allocate memory for habanalabs device and initialize basic fields
- * Identify the ASIC type
- * Allocate ID (minor) for the device (only for real devices)
- */
-static int create_hdev(struct hl_device **dev, struct pci_dev *pdev)
-{
- int main_id, ctrl_id = 0, rc = 0;
- struct hl_device *hdev;
-
- *dev = NULL;
-
- hdev = kzalloc(sizeof(*hdev), GFP_KERNEL);
- if (!hdev)
- return -ENOMEM;
-
- /* Will be NULL in case of simulator device */
- hdev->pdev = pdev;
-
- /* Assign status description string */
- strncpy(hdev->status[HL_DEVICE_STATUS_OPERATIONAL], "operational", HL_STR_MAX);
- strncpy(hdev->status[HL_DEVICE_STATUS_IN_RESET], "in reset", HL_STR_MAX);
- strncpy(hdev->status[HL_DEVICE_STATUS_MALFUNCTION], "disabled", HL_STR_MAX);
- strncpy(hdev->status[HL_DEVICE_STATUS_NEEDS_RESET], "needs reset", HL_STR_MAX);
- strncpy(hdev->status[HL_DEVICE_STATUS_IN_DEVICE_CREATION],
- "in device creation", HL_STR_MAX);
- strncpy(hdev->status[HL_DEVICE_STATUS_IN_RESET_AFTER_DEVICE_RELEASE],
- "in reset after device release", HL_STR_MAX);
-
-
- /* First, we must find out which ASIC are we handling. This is needed
- * to configure the behavior of the driver (kernel parameters)
- */
- hdev->asic_type = get_asic_type(hdev);
- if (hdev->asic_type == ASIC_INVALID) {
- dev_err(&pdev->dev, "Unsupported ASIC\n");
- rc = -ENODEV;
- goto free_hdev;
- }
-
- copy_kernel_module_params_to_device(hdev);
-
- set_driver_behavior_per_device(hdev);
-
- fixup_device_params(hdev);
-
- mutex_lock(&hl_devs_idr_lock);
-
- /* Always save 2 numbers, 1 for main device and 1 for control.
- * They must be consecutive
- */
- main_id = idr_alloc(&hl_devs_idr, hdev, 0, HL_MAX_MINORS, GFP_KERNEL);
-
- if (main_id >= 0)
- ctrl_id = idr_alloc(&hl_devs_idr, hdev, main_id + 1,
- main_id + 2, GFP_KERNEL);
-
- mutex_unlock(&hl_devs_idr_lock);
-
- if ((main_id < 0) || (ctrl_id < 0)) {
- if ((main_id == -ENOSPC) || (ctrl_id == -ENOSPC))
- pr_err("too many devices in the system\n");
-
- if (main_id >= 0) {
- mutex_lock(&hl_devs_idr_lock);
- idr_remove(&hl_devs_idr, main_id);
- mutex_unlock(&hl_devs_idr_lock);
- }
-
- rc = -EBUSY;
- goto free_hdev;
- }
-
- hdev->id = main_id;
- hdev->id_control = ctrl_id;
-
- *dev = hdev;
-
- return 0;
-
-free_hdev:
- kfree(hdev);
- return rc;
-}
-
-/*
- * destroy_hdev - destroy habanalabs device instance
- *
- * @dev: pointer to the habanalabs device structure
- *
- */
-static void destroy_hdev(struct hl_device *hdev)
-{
- /* Remove device from the device list */
- mutex_lock(&hl_devs_idr_lock);
- idr_remove(&hl_devs_idr, hdev->id);
- idr_remove(&hl_devs_idr, hdev->id_control);
- mutex_unlock(&hl_devs_idr_lock);
-
- kfree(hdev);
-}
-
-static int hl_pmops_suspend(struct device *dev)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- pr_debug("Going to suspend PCI device\n");
-
- if (!hdev) {
- pr_err("device pointer is NULL in suspend\n");
- return 0;
- }
-
- return hl_device_suspend(hdev);
-}
-
-static int hl_pmops_resume(struct device *dev)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- pr_debug("Going to resume PCI device\n");
-
- if (!hdev) {
- pr_err("device pointer is NULL in resume\n");
- return 0;
- }
-
- return hl_device_resume(hdev);
-}
-
-/**
- * hl_pci_probe - probe PCI habanalabs devices
- *
- * @pdev: pointer to pci device
- * @id: pointer to pci device id structure
- *
- * Standard PCI probe function for habanalabs device.
- * Create a new habanalabs device and initialize it according to the
- * device's type
- */
-static int hl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
- struct hl_device *hdev;
- int rc;
-
- dev_info(&pdev->dev, HL_NAME
- " device found [%04x:%04x] (rev %x)\n",
- (int)pdev->vendor, (int)pdev->device, (int)pdev->revision);
-
- rc = create_hdev(&hdev, pdev);
- if (rc)
- return rc;
-
- pci_set_drvdata(pdev, hdev);
-
- pci_enable_pcie_error_reporting(pdev);
-
- rc = hl_device_init(hdev, hl_class);
- if (rc) {
- dev_err(&pdev->dev, "Fatal error during habanalabs device init\n");
- rc = -ENODEV;
- goto disable_device;
- }
-
- return 0;
-
-disable_device:
- pci_disable_pcie_error_reporting(pdev);
- pci_set_drvdata(pdev, NULL);
- destroy_hdev(hdev);
-
- return rc;
-}
-
-/*
- * hl_pci_remove - remove PCI habanalabs devices
- *
- * @pdev: pointer to pci device
- *
- * Standard PCI remove function for habanalabs device
- */
-static void hl_pci_remove(struct pci_dev *pdev)
-{
- struct hl_device *hdev;
-
- hdev = pci_get_drvdata(pdev);
- if (!hdev)
- return;
-
- hl_device_fini(hdev);
- pci_disable_pcie_error_reporting(pdev);
- pci_set_drvdata(pdev, NULL);
- destroy_hdev(hdev);
-}
-
-/**
- * hl_pci_err_detected - a PCI bus error detected on this device
- *
- * @pdev: pointer to pci device
- * @state: PCI error type
- *
- * Called by the PCI subsystem whenever a non-correctable
- * PCI bus error is detected
- */
-static pci_ers_result_t
-hl_pci_err_detected(struct pci_dev *pdev, pci_channel_state_t state)
-{
- struct hl_device *hdev = pci_get_drvdata(pdev);
- enum pci_ers_result result;
-
- switch (state) {
- case pci_channel_io_normal:
- dev_warn(hdev->dev, "PCI normal state error detected\n");
- return PCI_ERS_RESULT_CAN_RECOVER;
-
- case pci_channel_io_frozen:
- dev_warn(hdev->dev, "PCI frozen state error detected\n");
- result = PCI_ERS_RESULT_NEED_RESET;
- break;
-
- case pci_channel_io_perm_failure:
- dev_warn(hdev->dev, "PCI failure state error detected\n");
- result = PCI_ERS_RESULT_DISCONNECT;
- break;
-
- default:
- result = PCI_ERS_RESULT_NONE;
- }
-
- hdev->asic_funcs->halt_engines(hdev, true, false);
-
- return result;
-}
-
-/**
- * hl_pci_err_resume - resume after a PCI slot reset
- *
- * @pdev: pointer to pci device
- *
- */
-static void hl_pci_err_resume(struct pci_dev *pdev)
-{
- struct hl_device *hdev = pci_get_drvdata(pdev);
-
- dev_warn(hdev->dev, "Resuming device after PCI slot reset\n");
- hl_device_resume(hdev);
-}
-
-/**
- * hl_pci_err_slot_reset - a PCI slot reset has just happened
- *
- * @pdev: pointer to pci device
- *
- * Determine if the driver can recover from the PCI slot reset
- */
-static pci_ers_result_t hl_pci_err_slot_reset(struct pci_dev *pdev)
-{
- struct hl_device *hdev = pci_get_drvdata(pdev);
-
- dev_warn(hdev->dev, "PCI slot reset detected\n");
-
- return PCI_ERS_RESULT_RECOVERED;
-}
-
-static const struct dev_pm_ops hl_pm_ops = {
- .suspend = hl_pmops_suspend,
- .resume = hl_pmops_resume,
-};
-
-static const struct pci_error_handlers hl_pci_err_handler = {
- .error_detected = hl_pci_err_detected,
- .slot_reset = hl_pci_err_slot_reset,
- .resume = hl_pci_err_resume,
-};
-
-static struct pci_driver hl_pci_driver = {
- .name = HL_NAME,
- .id_table = ids,
- .probe = hl_pci_probe,
- .remove = hl_pci_remove,
- .shutdown = hl_pci_remove,
- .driver = {
- .name = HL_NAME,
- .pm = &hl_pm_ops,
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- },
- .err_handler = &hl_pci_err_handler,
-};
-
-/*
- * hl_init - Initialize the habanalabs kernel driver
- */
-static int __init hl_init(void)
-{
- int rc;
- dev_t dev;
-
- pr_info("loading driver\n");
-
- rc = alloc_chrdev_region(&dev, 0, HL_MAX_MINORS, HL_NAME);
- if (rc < 0) {
- pr_err("unable to get major\n");
- return rc;
- }
-
- hl_major = MAJOR(dev);
-
- hl_class = class_create(THIS_MODULE, HL_NAME);
- if (IS_ERR(hl_class)) {
- pr_err("failed to allocate class\n");
- rc = PTR_ERR(hl_class);
- goto remove_major;
- }
-
- hl_debugfs_init();
-
- rc = pci_register_driver(&hl_pci_driver);
- if (rc) {
- pr_err("failed to register pci device\n");
- goto remove_debugfs;
- }
-
- pr_debug("driver loaded\n");
-
- return 0;
-
-remove_debugfs:
- hl_debugfs_fini();
- class_destroy(hl_class);
-remove_major:
- unregister_chrdev_region(MKDEV(hl_major, 0), HL_MAX_MINORS);
- return rc;
-}
-
-/*
- * hl_exit - Release all resources of the habanalabs kernel driver
- */
-static void __exit hl_exit(void)
-{
- pci_unregister_driver(&hl_pci_driver);
-
- /*
- * Removing debugfs must be after all devices or simulator devices
- * have been removed because otherwise we get a bug in the
- * debugfs module for referencing NULL objects
- */
- hl_debugfs_fini();
-
- class_destroy(hl_class);
- unregister_chrdev_region(MKDEV(hl_major, 0), HL_MAX_MINORS);
-
- idr_destroy(&hl_devs_idr);
-
- pr_debug("driver removed\n");
-}
-
-module_init(hl_init);
-module_exit(hl_exit);
diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c
deleted file mode 100644
index 079483421e12..000000000000
--- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c
+++ /dev/null
@@ -1,1190 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2022 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#define pr_fmt(fmt) "habanalabs: " fmt
-
-#include <uapi/drm/habanalabs_accel.h>
-#include "habanalabs.h"
-
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/vmalloc.h>
-
-static u32 hl_debug_struct_size[HL_DEBUG_OP_TIMESTAMP + 1] = {
- [HL_DEBUG_OP_ETR] = sizeof(struct hl_debug_params_etr),
- [HL_DEBUG_OP_ETF] = sizeof(struct hl_debug_params_etf),
- [HL_DEBUG_OP_STM] = sizeof(struct hl_debug_params_stm),
- [HL_DEBUG_OP_FUNNEL] = 0,
- [HL_DEBUG_OP_BMON] = sizeof(struct hl_debug_params_bmon),
- [HL_DEBUG_OP_SPMU] = sizeof(struct hl_debug_params_spmu),
- [HL_DEBUG_OP_TIMESTAMP] = 0
-
-};
-
-static int device_status_info(struct hl_device *hdev, struct hl_info_args *args)
-{
- struct hl_info_device_status dev_stat = {0};
- u32 size = args->return_size;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
-
- if ((!size) || (!out))
- return -EINVAL;
-
- dev_stat.status = hl_device_status(hdev);
-
- return copy_to_user(out, &dev_stat,
- min((size_t)size, sizeof(dev_stat))) ? -EFAULT : 0;
-}
-
-static int hw_ip_info(struct hl_device *hdev, struct hl_info_args *args)
-{
- struct hl_info_hw_ip_info hw_ip = {0};
- u32 size = args->return_size;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u64 sram_kmd_size, dram_kmd_size, dram_available_size;
-
- if ((!size) || (!out))
- return -EINVAL;
-
- sram_kmd_size = (prop->sram_user_base_address -
- prop->sram_base_address);
- dram_kmd_size = (prop->dram_user_base_address -
- prop->dram_base_address);
-
- hw_ip.device_id = hdev->asic_funcs->get_pci_id(hdev);
- hw_ip.sram_base_address = prop->sram_user_base_address;
- hw_ip.dram_base_address =
- hdev->mmu_enable && prop->dram_supports_virtual_memory ?
- prop->dmmu.start_addr : prop->dram_user_base_address;
- hw_ip.tpc_enabled_mask = prop->tpc_enabled_mask & 0xFF;
- hw_ip.tpc_enabled_mask_ext = prop->tpc_enabled_mask;
-
- hw_ip.sram_size = prop->sram_size - sram_kmd_size;
-
- dram_available_size = prop->dram_size - dram_kmd_size;
-
- if (hdev->mmu_enable == MMU_EN_ALL)
- hw_ip.dram_size = DIV_ROUND_DOWN_ULL(dram_available_size,
- prop->dram_page_size) * prop->dram_page_size;
- else
- hw_ip.dram_size = dram_available_size;
-
- if (hw_ip.dram_size > PAGE_SIZE)
- hw_ip.dram_enabled = 1;
-
- hw_ip.dram_page_size = prop->dram_page_size;
- hw_ip.device_mem_alloc_default_page_size = prop->device_mem_alloc_default_page_size;
- hw_ip.num_of_events = prop->num_of_events;
-
- memcpy(hw_ip.cpucp_version, prop->cpucp_info.cpucp_version,
- min(VERSION_MAX_LEN, HL_INFO_VERSION_MAX_LEN));
-
- memcpy(hw_ip.card_name, prop->cpucp_info.card_name,
- min(CARD_NAME_MAX_LEN, HL_INFO_CARD_NAME_MAX_LEN));
-
- hw_ip.cpld_version = le32_to_cpu(prop->cpucp_info.cpld_version);
- hw_ip.module_id = le32_to_cpu(prop->cpucp_info.card_location);
-
- hw_ip.psoc_pci_pll_nr = prop->psoc_pci_pll_nr;
- hw_ip.psoc_pci_pll_nf = prop->psoc_pci_pll_nf;
- hw_ip.psoc_pci_pll_od = prop->psoc_pci_pll_od;
- hw_ip.psoc_pci_pll_div_factor = prop->psoc_pci_pll_div_factor;
-
- hw_ip.decoder_enabled_mask = prop->decoder_enabled_mask;
- hw_ip.mme_master_slave_mode = prop->mme_master_slave_mode;
- hw_ip.first_available_interrupt_id = prop->first_available_user_interrupt;
- hw_ip.number_of_user_interrupts = prop->user_interrupt_count;
-
- hw_ip.edma_enabled_mask = prop->edma_enabled_mask;
- hw_ip.server_type = prop->server_type;
- hw_ip.security_enabled = prop->fw_security_enabled;
- hw_ip.revision_id = hdev->pdev->revision;
-
- return copy_to_user(out, &hw_ip,
- min((size_t) size, sizeof(hw_ip))) ? -EFAULT : 0;
-}
-
-static int hw_events_info(struct hl_device *hdev, bool aggregate,
- struct hl_info_args *args)
-{
- u32 size, max_size = args->return_size;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- void *arr;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- arr = hdev->asic_funcs->get_events_stat(hdev, aggregate, &size);
- if (!arr) {
- dev_err(hdev->dev, "Events info not supported\n");
- return -EOPNOTSUPP;
- }
-
- return copy_to_user(out, arr, min(max_size, size)) ? -EFAULT : 0;
-}
-
-static int events_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- u32 max_size = args->return_size;
- u64 events_mask;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
-
- if ((max_size < sizeof(u64)) || (!out))
- return -EINVAL;
-
- mutex_lock(&hpriv->notifier_event.lock);
- events_mask = hpriv->notifier_event.events_mask;
- hpriv->notifier_event.events_mask = 0;
- mutex_unlock(&hpriv->notifier_event.lock);
-
- return copy_to_user(out, &events_mask, sizeof(u64)) ? -EFAULT : 0;
-}
-
-static int dram_usage_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- struct hl_device *hdev = hpriv->hdev;
- struct hl_info_dram_usage dram_usage = {0};
- u32 max_size = args->return_size;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u64 dram_kmd_size;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- dram_kmd_size = (prop->dram_user_base_address -
- prop->dram_base_address);
- dram_usage.dram_free_mem = (prop->dram_size - dram_kmd_size) -
- atomic64_read(&hdev->dram_used_mem);
- if (hpriv->ctx)
- dram_usage.ctx_dram_mem =
- atomic64_read(&hpriv->ctx->dram_phys_mem);
-
- return copy_to_user(out, &dram_usage,
- min((size_t) max_size, sizeof(dram_usage))) ? -EFAULT : 0;
-}
-
-static int hw_idle(struct hl_device *hdev, struct hl_info_args *args)
-{
- struct hl_info_hw_idle hw_idle = {0};
- u32 max_size = args->return_size;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- hw_idle.is_idle = hdev->asic_funcs->is_device_idle(hdev,
- hw_idle.busy_engines_mask_ext,
- HL_BUSY_ENGINES_MASK_EXT_SIZE, NULL);
- hw_idle.busy_engines_mask =
- lower_32_bits(hw_idle.busy_engines_mask_ext[0]);
-
- return copy_to_user(out, &hw_idle,
- min((size_t) max_size, sizeof(hw_idle))) ? -EFAULT : 0;
-}
-
-static int debug_coresight(struct hl_device *hdev, struct hl_ctx *ctx, struct hl_debug_args *args)
-{
- struct hl_debug_params *params;
- void *input = NULL, *output = NULL;
- int rc;
-
- params = kzalloc(sizeof(*params), GFP_KERNEL);
- if (!params)
- return -ENOMEM;
-
- params->reg_idx = args->reg_idx;
- params->enable = args->enable;
- params->op = args->op;
-
- if (args->input_ptr && args->input_size) {
- input = kzalloc(hl_debug_struct_size[args->op], GFP_KERNEL);
- if (!input) {
- rc = -ENOMEM;
- goto out;
- }
-
- if (copy_from_user(input, u64_to_user_ptr(args->input_ptr),
- args->input_size)) {
- rc = -EFAULT;
- dev_err(hdev->dev, "failed to copy input debug data\n");
- goto out;
- }
-
- params->input = input;
- }
-
- if (args->output_ptr && args->output_size) {
- output = kzalloc(args->output_size, GFP_KERNEL);
- if (!output) {
- rc = -ENOMEM;
- goto out;
- }
-
- params->output = output;
- params->output_size = args->output_size;
- }
-
- rc = hdev->asic_funcs->debug_coresight(hdev, ctx, params);
- if (rc) {
- dev_err(hdev->dev,
- "debug coresight operation failed %d\n", rc);
- goto out;
- }
-
- if (output && copy_to_user((void __user *) (uintptr_t) args->output_ptr,
- output, args->output_size)) {
- dev_err(hdev->dev, "copy to user failed in debug ioctl\n");
- rc = -EFAULT;
- goto out;
- }
-
-
-out:
- kfree(params);
- kfree(output);
- kfree(input);
-
- return rc;
-}
-
-static int device_utilization(struct hl_device *hdev, struct hl_info_args *args)
-{
- struct hl_info_device_utilization device_util = {0};
- u32 max_size = args->return_size;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- int rc;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- rc = hl_device_utilization(hdev, &device_util.utilization);
- if (rc)
- return -EINVAL;
-
- return copy_to_user(out, &device_util,
- min((size_t) max_size, sizeof(device_util))) ? -EFAULT : 0;
-}
-
-static int get_clk_rate(struct hl_device *hdev, struct hl_info_args *args)
-{
- struct hl_info_clk_rate clk_rate = {0};
- u32 max_size = args->return_size;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- int rc;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- rc = hl_fw_get_clk_rate(hdev, &clk_rate.cur_clk_rate_mhz, &clk_rate.max_clk_rate_mhz);
- if (rc)
- return rc;
-
- return copy_to_user(out, &clk_rate, min_t(size_t, max_size, sizeof(clk_rate)))
- ? -EFAULT : 0;
-}
-
-static int get_reset_count(struct hl_device *hdev, struct hl_info_args *args)
-{
- struct hl_info_reset_count reset_count = {0};
- u32 max_size = args->return_size;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- reset_count.hard_reset_cnt = hdev->reset_info.hard_reset_cnt;
- reset_count.soft_reset_cnt = hdev->reset_info.compute_reset_cnt;
-
- return copy_to_user(out, &reset_count,
- min((size_t) max_size, sizeof(reset_count))) ? -EFAULT : 0;
-}
-
-static int time_sync_info(struct hl_device *hdev, struct hl_info_args *args)
-{
- struct hl_info_time_sync time_sync = {0};
- u32 max_size = args->return_size;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- time_sync.device_time = hdev->asic_funcs->get_device_time(hdev);
- time_sync.host_time = ktime_get_raw_ns();
-
- return copy_to_user(out, &time_sync,
- min((size_t) max_size, sizeof(time_sync))) ? -EFAULT : 0;
-}
-
-static int pci_counters_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- struct hl_device *hdev = hpriv->hdev;
- struct hl_info_pci_counters pci_counters = {0};
- u32 max_size = args->return_size;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- int rc;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- rc = hl_fw_cpucp_pci_counters_get(hdev, &pci_counters);
- if (rc)
- return rc;
-
- return copy_to_user(out, &pci_counters,
- min((size_t) max_size, sizeof(pci_counters))) ? -EFAULT : 0;
-}
-
-static int clk_throttle_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- struct hl_device *hdev = hpriv->hdev;
- struct hl_info_clk_throttle clk_throttle = {0};
- ktime_t end_time, zero_time = ktime_set(0, 0);
- u32 max_size = args->return_size;
- int i;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- mutex_lock(&hdev->clk_throttling.lock);
-
- clk_throttle.clk_throttling_reason = hdev->clk_throttling.current_reason;
-
- for (i = 0 ; i < HL_CLK_THROTTLE_TYPE_MAX ; i++) {
- if (!(hdev->clk_throttling.aggregated_reason & BIT(i)))
- continue;
-
- clk_throttle.clk_throttling_timestamp_us[i] =
- ktime_to_us(hdev->clk_throttling.timestamp[i].start);
-
- if (ktime_compare(hdev->clk_throttling.timestamp[i].end, zero_time))
- end_time = hdev->clk_throttling.timestamp[i].end;
- else
- end_time = ktime_get();
-
- clk_throttle.clk_throttling_duration_ns[i] =
- ktime_to_ns(ktime_sub(end_time,
- hdev->clk_throttling.timestamp[i].start));
-
- }
- mutex_unlock(&hdev->clk_throttling.lock);
-
- return copy_to_user(out, &clk_throttle,
- min((size_t) max_size, sizeof(clk_throttle))) ? -EFAULT : 0;
-}
-
-static int cs_counters_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- struct hl_info_cs_counters cs_counters = {0};
- struct hl_device *hdev = hpriv->hdev;
- struct hl_cs_counters_atomic *cntr;
- u32 max_size = args->return_size;
-
- cntr = &hdev->aggregated_cs_counters;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- cs_counters.total_out_of_mem_drop_cnt =
- atomic64_read(&cntr->out_of_mem_drop_cnt);
- cs_counters.total_parsing_drop_cnt =
- atomic64_read(&cntr->parsing_drop_cnt);
- cs_counters.total_queue_full_drop_cnt =
- atomic64_read(&cntr->queue_full_drop_cnt);
- cs_counters.total_device_in_reset_drop_cnt =
- atomic64_read(&cntr->device_in_reset_drop_cnt);
- cs_counters.total_max_cs_in_flight_drop_cnt =
- atomic64_read(&cntr->max_cs_in_flight_drop_cnt);
- cs_counters.total_validation_drop_cnt =
- atomic64_read(&cntr->validation_drop_cnt);
-
- if (hpriv->ctx) {
- cs_counters.ctx_out_of_mem_drop_cnt =
- atomic64_read(
- &hpriv->ctx->cs_counters.out_of_mem_drop_cnt);
- cs_counters.ctx_parsing_drop_cnt =
- atomic64_read(
- &hpriv->ctx->cs_counters.parsing_drop_cnt);
- cs_counters.ctx_queue_full_drop_cnt =
- atomic64_read(
- &hpriv->ctx->cs_counters.queue_full_drop_cnt);
- cs_counters.ctx_device_in_reset_drop_cnt =
- atomic64_read(
- &hpriv->ctx->cs_counters.device_in_reset_drop_cnt);
- cs_counters.ctx_max_cs_in_flight_drop_cnt =
- atomic64_read(
- &hpriv->ctx->cs_counters.max_cs_in_flight_drop_cnt);
- cs_counters.ctx_validation_drop_cnt =
- atomic64_read(
- &hpriv->ctx->cs_counters.validation_drop_cnt);
- }
-
- return copy_to_user(out, &cs_counters,
- min((size_t) max_size, sizeof(cs_counters))) ? -EFAULT : 0;
-}
-
-static int sync_manager_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- struct hl_device *hdev = hpriv->hdev;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct hl_info_sync_manager sm_info = {0};
- u32 max_size = args->return_size;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- if (args->dcore_id >= HL_MAX_DCORES)
- return -EINVAL;
-
- sm_info.first_available_sync_object =
- prop->first_available_user_sob[args->dcore_id];
- sm_info.first_available_monitor =
- prop->first_available_user_mon[args->dcore_id];
- sm_info.first_available_cq =
- prop->first_available_cq[args->dcore_id];
-
- return copy_to_user(out, &sm_info, min_t(size_t, (size_t) max_size,
- sizeof(sm_info))) ? -EFAULT : 0;
-}
-
-static int total_energy_consumption_info(struct hl_fpriv *hpriv,
- struct hl_info_args *args)
-{
- struct hl_device *hdev = hpriv->hdev;
- struct hl_info_energy total_energy = {0};
- u32 max_size = args->return_size;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- int rc;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- rc = hl_fw_cpucp_total_energy_get(hdev,
- &total_energy.total_energy_consumption);
- if (rc)
- return rc;
-
- return copy_to_user(out, &total_energy,
- min((size_t) max_size, sizeof(total_energy))) ? -EFAULT : 0;
-}
-
-static int pll_frequency_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- struct hl_device *hdev = hpriv->hdev;
- struct hl_pll_frequency_info freq_info = { {0} };
- u32 max_size = args->return_size;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- int rc;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- rc = hl_fw_cpucp_pll_info_get(hdev, args->pll_index, freq_info.output);
- if (rc)
- return rc;
-
- return copy_to_user(out, &freq_info,
- min((size_t) max_size, sizeof(freq_info))) ? -EFAULT : 0;
-}
-
-static int power_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- struct hl_device *hdev = hpriv->hdev;
- u32 max_size = args->return_size;
- struct hl_power_info power_info = {0};
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- int rc;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- rc = hl_fw_cpucp_power_get(hdev, &power_info.power);
- if (rc)
- return rc;
-
- return copy_to_user(out, &power_info,
- min((size_t) max_size, sizeof(power_info))) ? -EFAULT : 0;
-}
-
-static int open_stats_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- struct hl_device *hdev = hpriv->hdev;
- u32 max_size = args->return_size;
- struct hl_open_stats_info open_stats_info = {0};
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- open_stats_info.last_open_period_ms = jiffies64_to_msecs(
- hdev->last_open_session_duration_jif);
- open_stats_info.open_counter = hdev->open_counter;
- open_stats_info.is_compute_ctx_active = hdev->is_compute_ctx_active;
- open_stats_info.compute_ctx_in_release = hdev->compute_ctx_in_release;
-
- return copy_to_user(out, &open_stats_info,
- min((size_t) max_size, sizeof(open_stats_info))) ? -EFAULT : 0;
-}
-
-static int dram_pending_rows_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- struct hl_device *hdev = hpriv->hdev;
- u32 max_size = args->return_size;
- u32 pend_rows_num = 0;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- int rc;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- rc = hl_fw_dram_pending_row_get(hdev, &pend_rows_num);
- if (rc)
- return rc;
-
- return copy_to_user(out, &pend_rows_num,
- min_t(size_t, max_size, sizeof(pend_rows_num))) ? -EFAULT : 0;
-}
-
-static int dram_replaced_rows_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- struct hl_device *hdev = hpriv->hdev;
- u32 max_size = args->return_size;
- struct cpucp_hbm_row_info info = {0};
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- int rc;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- rc = hl_fw_dram_replaced_row_get(hdev, &info);
- if (rc)
- return rc;
-
- return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0;
-}
-
-static int last_err_open_dev_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- struct hl_info_last_err_open_dev_time info = {0};
- struct hl_device *hdev = hpriv->hdev;
- u32 max_size = args->return_size;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- info.timestamp = ktime_to_ns(hdev->last_successful_open_ktime);
-
- return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0;
-}
-
-static int cs_timeout_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- struct hl_info_cs_timeout_event info = {0};
- struct hl_device *hdev = hpriv->hdev;
- u32 max_size = args->return_size;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- info.seq = hdev->captured_err_info.cs_timeout.seq;
- info.timestamp = ktime_to_ns(hdev->captured_err_info.cs_timeout.timestamp);
-
- return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0;
-}
-
-static int razwi_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- struct hl_device *hdev = hpriv->hdev;
- u32 max_size = args->return_size;
- struct hl_info_razwi_event *info = &hdev->captured_err_info.razwi;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- return copy_to_user(out, info, min_t(size_t, max_size, sizeof(struct hl_info_razwi_event)))
- ? -EFAULT : 0;
-}
-
-static int undefined_opcode_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- struct hl_device *hdev = hpriv->hdev;
- u32 max_size = args->return_size;
- struct hl_info_undefined_opcode_event info = {0};
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- info.timestamp = ktime_to_ns(hdev->captured_err_info.undef_opcode.timestamp);
- info.engine_id = hdev->captured_err_info.undef_opcode.engine_id;
- info.cq_addr = hdev->captured_err_info.undef_opcode.cq_addr;
- info.cq_size = hdev->captured_err_info.undef_opcode.cq_size;
- info.stream_id = hdev->captured_err_info.undef_opcode.stream_id;
- info.cb_addr_streams_len = hdev->captured_err_info.undef_opcode.cb_addr_streams_len;
- memcpy(info.cb_addr_streams, hdev->captured_err_info.undef_opcode.cb_addr_streams,
- sizeof(info.cb_addr_streams));
-
- return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0;
-}
-
-static int dev_mem_alloc_page_sizes_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- struct hl_info_dev_memalloc_page_sizes info = {0};
- struct hl_device *hdev = hpriv->hdev;
- u32 max_size = args->return_size;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- /*
- * Future ASICs that will support multiple DRAM page sizes will support only "powers of 2"
- * pages (unlike some of the ASICs before supporting multiple page sizes).
- * For this reason for all ASICs that not support multiple page size the function will
- * return an empty bitmask indicating that multiple page sizes is not supported.
- */
- info.page_order_bitmask = hdev->asic_prop.dmmu.supported_pages_mask;
-
- return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0;
-}
-
-static int sec_attest_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- struct cpucp_sec_attest_info *sec_attest_info;
- struct hl_info_sec_attest *info;
- u32 max_size = args->return_size;
- int rc;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- sec_attest_info = kmalloc(sizeof(*sec_attest_info), GFP_KERNEL);
- if (!sec_attest_info)
- return -ENOMEM;
-
- info = kmalloc(sizeof(*info), GFP_KERNEL);
- if (!info) {
- rc = -ENOMEM;
- goto free_sec_attest_info;
- }
-
- rc = hl_fw_get_sec_attest_info(hpriv->hdev, sec_attest_info, args->sec_attest_nonce);
- if (rc)
- goto free_info;
-
- info->nonce = le32_to_cpu(sec_attest_info->nonce);
- info->pcr_quote_len = le16_to_cpu(sec_attest_info->pcr_quote_len);
- info->pub_data_len = le16_to_cpu(sec_attest_info->pub_data_len);
- info->certificate_len = le16_to_cpu(sec_attest_info->certificate_len);
- info->pcr_num_reg = sec_attest_info->pcr_num_reg;
- info->pcr_reg_len = sec_attest_info->pcr_reg_len;
- info->quote_sig_len = sec_attest_info->quote_sig_len;
- memcpy(&info->pcr_data, &sec_attest_info->pcr_data, sizeof(info->pcr_data));
- memcpy(&info->pcr_quote, &sec_attest_info->pcr_quote, sizeof(info->pcr_quote));
- memcpy(&info->public_data, &sec_attest_info->public_data, sizeof(info->public_data));
- memcpy(&info->certificate, &sec_attest_info->certificate, sizeof(info->certificate));
- memcpy(&info->quote_sig, &sec_attest_info->quote_sig, sizeof(info->quote_sig));
-
- rc = copy_to_user(out, info,
- min_t(size_t, max_size, sizeof(*info))) ? -EFAULT : 0;
-
-free_info:
- kfree(info);
-free_sec_attest_info:
- kfree(sec_attest_info);
-
- return rc;
-}
-
-static int eventfd_register(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- int rc;
-
- /* check if there is already a registered on that process */
- mutex_lock(&hpriv->notifier_event.lock);
- if (hpriv->notifier_event.eventfd) {
- mutex_unlock(&hpriv->notifier_event.lock);
- return -EINVAL;
- }
-
- hpriv->notifier_event.eventfd = eventfd_ctx_fdget(args->eventfd);
- if (IS_ERR(hpriv->notifier_event.eventfd)) {
- rc = PTR_ERR(hpriv->notifier_event.eventfd);
- hpriv->notifier_event.eventfd = NULL;
- mutex_unlock(&hpriv->notifier_event.lock);
- return rc;
- }
-
- mutex_unlock(&hpriv->notifier_event.lock);
- return 0;
-}
-
-static int eventfd_unregister(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- mutex_lock(&hpriv->notifier_event.lock);
- if (!hpriv->notifier_event.eventfd) {
- mutex_unlock(&hpriv->notifier_event.lock);
- return -EINVAL;
- }
-
- eventfd_ctx_put(hpriv->notifier_event.eventfd);
- hpriv->notifier_event.eventfd = NULL;
- mutex_unlock(&hpriv->notifier_event.lock);
- return 0;
-}
-
-static int engine_status_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- u32 status_buf_size = args->return_size;
- struct hl_device *hdev = hpriv->hdev;
- struct engines_data eng_data;
- int rc;
-
- if ((status_buf_size < SZ_1K) || (status_buf_size > HL_ENGINES_DATA_MAX_SIZE) || (!out))
- return -EINVAL;
-
- eng_data.actual_size = 0;
- eng_data.allocated_buf_size = status_buf_size;
- eng_data.buf = vmalloc(status_buf_size);
- if (!eng_data.buf)
- return -ENOMEM;
-
- hdev->asic_funcs->is_device_idle(hdev, NULL, 0, &eng_data);
-
- if (eng_data.actual_size > eng_data.allocated_buf_size) {
- dev_err(hdev->dev,
- "Engines data size (%d Bytes) is bigger than allocated size (%u Bytes)\n",
- eng_data.actual_size, status_buf_size);
- vfree(eng_data.buf);
- return -ENOMEM;
- }
-
- args->user_buffer_actual_size = eng_data.actual_size;
- rc = copy_to_user(out, eng_data.buf, min_t(size_t, status_buf_size, eng_data.actual_size)) ?
- -EFAULT : 0;
-
- vfree(eng_data.buf);
-
- return rc;
-}
-
-static int page_fault_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- struct hl_device *hdev = hpriv->hdev;
- u32 max_size = args->return_size;
- struct hl_page_fault_info *info = &hdev->captured_err_info.pgf_info.pgf;
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
-
- if ((!max_size) || (!out))
- return -EINVAL;
-
- return copy_to_user(out, info, min_t(size_t, max_size, sizeof(struct hl_page_fault_info)))
- ? -EFAULT : 0;
-}
-
-static int user_mappings_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
-{
- void __user *out = (void __user *) (uintptr_t) args->return_pointer;
- u32 user_buf_size = args->return_size;
- struct hl_device *hdev = hpriv->hdev;
- struct page_fault_info *pgf_info;
- u64 actual_size;
-
- pgf_info = &hdev->captured_err_info.pgf_info;
- args->array_size = pgf_info->num_of_user_mappings;
-
- if (!out)
- return -EINVAL;
-
- actual_size = pgf_info->num_of_user_mappings * sizeof(struct hl_user_mapping);
- if (user_buf_size < actual_size)
- return -ENOMEM;
-
- return copy_to_user(out, pgf_info->user_mappings, min_t(size_t, user_buf_size, actual_size))
- ? -EFAULT : 0;
-}
-
-static int send_fw_generic_request(struct hl_device *hdev, struct hl_info_args *info_args)
-{
- void __user *buff = (void __user *) (uintptr_t) info_args->return_pointer;
- u32 size = info_args->return_size;
- dma_addr_t dma_handle;
- bool need_input_buff;
- void *fw_buff;
- int rc = 0;
-
- switch (info_args->fw_sub_opcode) {
- case HL_PASSTHROUGH_VERSIONS:
- need_input_buff = false;
- break;
- default:
- return -EINVAL;
- }
-
- if (size > SZ_1M) {
- dev_err(hdev->dev, "buffer size cannot exceed 1MB\n");
- return -EINVAL;
- }
-
- fw_buff = hl_cpu_accessible_dma_pool_alloc(hdev, size, &dma_handle);
- if (!fw_buff)
- return -ENOMEM;
-
-
- if (need_input_buff && copy_from_user(fw_buff, buff, size)) {
- dev_dbg(hdev->dev, "Failed to copy from user FW buff\n");
- rc = -EFAULT;
- goto free_buff;
- }
-
- rc = hl_fw_send_generic_request(hdev, info_args->fw_sub_opcode, dma_handle, &size);
- if (rc)
- goto free_buff;
-
- if (copy_to_user(buff, fw_buff, min(size, info_args->return_size))) {
- dev_dbg(hdev->dev, "Failed to copy to user FW generic req output\n");
- rc = -EFAULT;
- }
-
-free_buff:
- hl_cpu_accessible_dma_pool_free(hdev, info_args->return_size, fw_buff);
-
- return rc;
-}
-
-static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data,
- struct device *dev)
-{
- enum hl_device_status status;
- struct hl_info_args *args = data;
- struct hl_device *hdev = hpriv->hdev;
-
- int rc;
-
- /*
- * Information is returned for the following opcodes even if the device
- * is disabled or in reset.
- */
- switch (args->op) {
- case HL_INFO_HW_IP_INFO:
- return hw_ip_info(hdev, args);
-
- case HL_INFO_DEVICE_STATUS:
- return device_status_info(hdev, args);
-
- case HL_INFO_RESET_COUNT:
- return get_reset_count(hdev, args);
-
- case HL_INFO_HW_EVENTS:
- return hw_events_info(hdev, false, args);
-
- case HL_INFO_HW_EVENTS_AGGREGATE:
- return hw_events_info(hdev, true, args);
-
- case HL_INFO_CS_COUNTERS:
- return cs_counters_info(hpriv, args);
-
- case HL_INFO_CLK_THROTTLE_REASON:
- return clk_throttle_info(hpriv, args);
-
- case HL_INFO_SYNC_MANAGER:
- return sync_manager_info(hpriv, args);
-
- case HL_INFO_OPEN_STATS:
- return open_stats_info(hpriv, args);
-
- case HL_INFO_LAST_ERR_OPEN_DEV_TIME:
- return last_err_open_dev_info(hpriv, args);
-
- case HL_INFO_CS_TIMEOUT_EVENT:
- return cs_timeout_info(hpriv, args);
-
- case HL_INFO_RAZWI_EVENT:
- return razwi_info(hpriv, args);
-
- case HL_INFO_UNDEFINED_OPCODE_EVENT:
- return undefined_opcode_info(hpriv, args);
-
- case HL_INFO_DEV_MEM_ALLOC_PAGE_SIZES:
- return dev_mem_alloc_page_sizes_info(hpriv, args);
-
- case HL_INFO_GET_EVENTS:
- return events_info(hpriv, args);
-
- case HL_INFO_PAGE_FAULT_EVENT:
- return page_fault_info(hpriv, args);
-
- case HL_INFO_USER_MAPPINGS:
- return user_mappings_info(hpriv, args);
-
- case HL_INFO_UNREGISTER_EVENTFD:
- return eventfd_unregister(hpriv, args);
-
- default:
- break;
- }
-
- if (!hl_device_operational(hdev, &status)) {
- dev_warn_ratelimited(dev,
- "Device is %s. Can't execute INFO IOCTL\n",
- hdev->status[status]);
- return -EBUSY;
- }
-
- switch (args->op) {
- case HL_INFO_DRAM_USAGE:
- rc = dram_usage_info(hpriv, args);
- break;
-
- case HL_INFO_HW_IDLE:
- rc = hw_idle(hdev, args);
- break;
-
- case HL_INFO_DEVICE_UTILIZATION:
- rc = device_utilization(hdev, args);
- break;
-
- case HL_INFO_CLK_RATE:
- rc = get_clk_rate(hdev, args);
- break;
-
- case HL_INFO_TIME_SYNC:
- return time_sync_info(hdev, args);
-
- case HL_INFO_PCI_COUNTERS:
- return pci_counters_info(hpriv, args);
-
- case HL_INFO_TOTAL_ENERGY:
- return total_energy_consumption_info(hpriv, args);
-
- case HL_INFO_PLL_FREQUENCY:
- return pll_frequency_info(hpriv, args);
-
- case HL_INFO_POWER:
- return power_info(hpriv, args);
-
-
- case HL_INFO_DRAM_REPLACED_ROWS:
- return dram_replaced_rows_info(hpriv, args);
-
- case HL_INFO_DRAM_PENDING_ROWS:
- return dram_pending_rows_info(hpriv, args);
-
- case HL_INFO_SECURED_ATTESTATION:
- return sec_attest_info(hpriv, args);
-
- case HL_INFO_REGISTER_EVENTFD:
- return eventfd_register(hpriv, args);
-
- case HL_INFO_ENGINE_STATUS:
- return engine_status_info(hpriv, args);
-
- case HL_INFO_FW_GENERIC_REQ:
- return send_fw_generic_request(hdev, args);
-
- default:
- dev_err(dev, "Invalid request %d\n", args->op);
- rc = -EINVAL;
- break;
- }
-
- return rc;
-}
-
-static int hl_info_ioctl(struct hl_fpriv *hpriv, void *data)
-{
- return _hl_info_ioctl(hpriv, data, hpriv->hdev->dev);
-}
-
-static int hl_info_ioctl_control(struct hl_fpriv *hpriv, void *data)
-{
- return _hl_info_ioctl(hpriv, data, hpriv->hdev->dev_ctrl);
-}
-
-static int hl_debug_ioctl(struct hl_fpriv *hpriv, void *data)
-{
- struct hl_debug_args *args = data;
- struct hl_device *hdev = hpriv->hdev;
- enum hl_device_status status;
-
- int rc = 0;
-
- if (!hl_device_operational(hdev, &status)) {
- dev_warn_ratelimited(hdev->dev,
- "Device is %s. Can't execute DEBUG IOCTL\n",
- hdev->status[status]);
- return -EBUSY;
- }
-
- switch (args->op) {
- case HL_DEBUG_OP_ETR:
- case HL_DEBUG_OP_ETF:
- case HL_DEBUG_OP_STM:
- case HL_DEBUG_OP_FUNNEL:
- case HL_DEBUG_OP_BMON:
- case HL_DEBUG_OP_SPMU:
- case HL_DEBUG_OP_TIMESTAMP:
- if (!hdev->in_debug) {
- dev_err_ratelimited(hdev->dev,
- "Rejecting debug configuration request because device not in debug mode\n");
- return -EFAULT;
- }
- args->input_size = min(args->input_size, hl_debug_struct_size[args->op]);
- rc = debug_coresight(hdev, hpriv->ctx, args);
- break;
-
- case HL_DEBUG_OP_SET_MODE:
- rc = hl_device_set_debug_mode(hdev, hpriv->ctx, (bool) args->enable);
- break;
-
- default:
- dev_err(hdev->dev, "Invalid request %d\n", args->op);
- rc = -EINVAL;
- break;
- }
-
- return rc;
-}
-
-#define HL_IOCTL_DEF(ioctl, _func) \
- [_IOC_NR(ioctl)] = {.cmd = ioctl, .func = _func}
-
-static const struct hl_ioctl_desc hl_ioctls[] = {
- HL_IOCTL_DEF(HL_IOCTL_INFO, hl_info_ioctl),
- HL_IOCTL_DEF(HL_IOCTL_CB, hl_cb_ioctl),
- HL_IOCTL_DEF(HL_IOCTL_CS, hl_cs_ioctl),
- HL_IOCTL_DEF(HL_IOCTL_WAIT_CS, hl_wait_ioctl),
- HL_IOCTL_DEF(HL_IOCTL_MEMORY, hl_mem_ioctl),
- HL_IOCTL_DEF(HL_IOCTL_DEBUG, hl_debug_ioctl)
-};
-
-static const struct hl_ioctl_desc hl_ioctls_control[] = {
- HL_IOCTL_DEF(HL_IOCTL_INFO, hl_info_ioctl_control)
-};
-
-static long _hl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg,
- const struct hl_ioctl_desc *ioctl, struct device *dev)
-{
- struct hl_fpriv *hpriv = filep->private_data;
- unsigned int nr = _IOC_NR(cmd);
- char stack_kdata[128] = {0};
- char *kdata = NULL;
- unsigned int usize, asize;
- hl_ioctl_t *func;
- u32 hl_size;
- int retcode;
-
- /* Do not trust userspace, use our own definition */
- func = ioctl->func;
-
- if (unlikely(!func)) {
- dev_dbg(dev, "no function\n");
- retcode = -ENOTTY;
- goto out_err;
- }
-
- hl_size = _IOC_SIZE(ioctl->cmd);
- usize = asize = _IOC_SIZE(cmd);
- if (hl_size > asize)
- asize = hl_size;
-
- cmd = ioctl->cmd;
-
- if (cmd & (IOC_IN | IOC_OUT)) {
- if (asize <= sizeof(stack_kdata)) {
- kdata = stack_kdata;
- } else {
- kdata = kzalloc(asize, GFP_KERNEL);
- if (!kdata) {
- retcode = -ENOMEM;
- goto out_err;
- }
- }
- }
-
- if (cmd & IOC_IN) {
- if (copy_from_user(kdata, (void __user *)arg, usize)) {
- retcode = -EFAULT;
- goto out_err;
- }
- } else if (cmd & IOC_OUT) {
- memset(kdata, 0, usize);
- }
-
- retcode = func(hpriv, kdata);
-
- if ((cmd & IOC_OUT) && copy_to_user((void __user *)arg, kdata, usize))
- retcode = -EFAULT;
-
-out_err:
- if (retcode)
- dev_dbg(dev, "error in ioctl: pid=%d, cmd=0x%02x, nr=0x%02x\n",
- task_pid_nr(current), cmd, nr);
-
- if (kdata != stack_kdata)
- kfree(kdata);
-
- return retcode;
-}
-
-long hl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
-{
- struct hl_fpriv *hpriv = filep->private_data;
- struct hl_device *hdev = hpriv->hdev;
- const struct hl_ioctl_desc *ioctl = NULL;
- unsigned int nr = _IOC_NR(cmd);
-
- if (!hdev) {
- pr_err_ratelimited("Sending ioctl after device was removed! Please close FD\n");
- return -ENODEV;
- }
-
- if ((nr >= HL_COMMAND_START) && (nr < HL_COMMAND_END)) {
- ioctl = &hl_ioctls[nr];
- } else {
- dev_err(hdev->dev, "invalid ioctl: pid=%d, nr=0x%02x\n",
- task_pid_nr(current), nr);
- return -ENOTTY;
- }
-
- return _hl_ioctl(filep, cmd, arg, ioctl, hdev->dev);
-}
-
-long hl_ioctl_control(struct file *filep, unsigned int cmd, unsigned long arg)
-{
- struct hl_fpriv *hpriv = filep->private_data;
- struct hl_device *hdev = hpriv->hdev;
- const struct hl_ioctl_desc *ioctl = NULL;
- unsigned int nr = _IOC_NR(cmd);
-
- if (!hdev) {
- pr_err_ratelimited("Sending ioctl after device was removed! Please close FD\n");
- return -ENODEV;
- }
-
- if (nr == _IOC_NR(HL_IOCTL_INFO)) {
- ioctl = &hl_ioctls_control[nr];
- } else {
- dev_err(hdev->dev_ctrl, "invalid ioctl: pid=%d, nr=0x%02x\n",
- task_pid_nr(current), nr);
- return -ENOTTY;
- }
-
- return _hl_ioctl(filep, cmd, arg, ioctl, hdev->dev_ctrl);
-}
diff --git a/drivers/misc/habanalabs/common/hw_queue.c b/drivers/misc/habanalabs/common/hw_queue.c
deleted file mode 100644
index d0087c0ec48c..000000000000
--- a/drivers/misc/habanalabs/common/hw_queue.c
+++ /dev/null
@@ -1,1137 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2019 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include "habanalabs.h"
-
-#include <linux/slab.h>
-
-/*
- * hl_queue_add_ptr - add to pi or ci and checks if it wraps around
- *
- * @ptr: the current pi/ci value
- * @val: the amount to add
- *
- * Add val to ptr. It can go until twice the queue length.
- */
-inline u32 hl_hw_queue_add_ptr(u32 ptr, u16 val)
-{
- ptr += val;
- ptr &= ((HL_QUEUE_LENGTH << 1) - 1);
- return ptr;
-}
-static inline int queue_ci_get(atomic_t *ci, u32 queue_len)
-{
- return atomic_read(ci) & ((queue_len << 1) - 1);
-}
-
-static inline int queue_free_slots(struct hl_hw_queue *q, u32 queue_len)
-{
- int delta = (q->pi - queue_ci_get(&q->ci, queue_len));
-
- if (delta >= 0)
- return (queue_len - delta);
- else
- return (abs(delta) - queue_len);
-}
-
-void hl_hw_queue_update_ci(struct hl_cs *cs)
-{
- struct hl_device *hdev = cs->ctx->hdev;
- struct hl_hw_queue *q;
- int i;
-
- if (hdev->disabled)
- return;
-
- q = &hdev->kernel_queues[0];
-
- /* There are no internal queues if H/W queues are being used */
- if (!hdev->asic_prop.max_queues || q->queue_type == QUEUE_TYPE_HW)
- return;
-
- /* We must increment CI for every queue that will never get a
- * completion, there are 2 scenarios this can happen:
- * 1. All queues of a non completion CS will never get a completion.
- * 2. Internal queues never gets completion.
- */
- for (i = 0 ; i < hdev->asic_prop.max_queues ; i++, q++) {
- if (!cs_needs_completion(cs) || q->queue_type == QUEUE_TYPE_INT)
- atomic_add(cs->jobs_in_queue_cnt[i], &q->ci);
- }
-}
-
-/*
- * hl_hw_queue_submit_bd() - Submit a buffer descriptor to an external or a
- * H/W queue.
- * @hdev: pointer to habanalabs device structure
- * @q: pointer to habanalabs queue structure
- * @ctl: BD's control word
- * @len: BD's length
- * @ptr: BD's pointer
- *
- * This function assumes there is enough space on the queue to submit a new
- * BD to it. It initializes the next BD and calls the device specific
- * function to set the pi (and doorbell)
- *
- * This function must be called when the scheduler mutex is taken
- *
- */
-void hl_hw_queue_submit_bd(struct hl_device *hdev, struct hl_hw_queue *q,
- u32 ctl, u32 len, u64 ptr)
-{
- struct hl_bd *bd;
-
- bd = q->kernel_address;
- bd += hl_pi_2_offset(q->pi);
- bd->ctl = cpu_to_le32(ctl);
- bd->len = cpu_to_le32(len);
- bd->ptr = cpu_to_le64(ptr);
-
- q->pi = hl_queue_inc_ptr(q->pi);
- hdev->asic_funcs->ring_doorbell(hdev, q->hw_queue_id, q->pi);
-}
-
-/*
- * ext_queue_sanity_checks - perform some sanity checks on external queue
- *
- * @hdev : pointer to hl_device structure
- * @q : pointer to hl_hw_queue structure
- * @num_of_entries : how many entries to check for space
- * @reserve_cq_entry : whether to reserve an entry in the cq
- *
- * H/W queues spinlock should be taken before calling this function
- *
- * Perform the following:
- * - Make sure we have enough space in the h/w queue
- * - Make sure we have enough space in the completion queue
- * - Reserve space in the completion queue (needs to be reversed if there
- * is a failure down the road before the actual submission of work). Only
- * do this action if reserve_cq_entry is true
- *
- */
-static int ext_queue_sanity_checks(struct hl_device *hdev,
- struct hl_hw_queue *q, int num_of_entries,
- bool reserve_cq_entry)
-{
- atomic_t *free_slots =
- &hdev->completion_queue[q->cq_id].free_slots_cnt;
- int free_slots_cnt;
-
- /* Check we have enough space in the queue */
- free_slots_cnt = queue_free_slots(q, HL_QUEUE_LENGTH);
-
- if (free_slots_cnt < num_of_entries) {
- dev_dbg(hdev->dev, "Queue %d doesn't have room for %d CBs\n",
- q->hw_queue_id, num_of_entries);
- return -EAGAIN;
- }
-
- if (reserve_cq_entry) {
- /*
- * Check we have enough space in the completion queue
- * Add -1 to counter (decrement) unless counter was already 0
- * In that case, CQ is full so we can't submit a new CB because
- * we won't get ack on its completion
- * atomic_add_unless will return 0 if counter was already 0
- */
- if (atomic_add_negative(num_of_entries * -1, free_slots)) {
- dev_dbg(hdev->dev, "No space for %d on CQ %d\n",
- num_of_entries, q->hw_queue_id);
- atomic_add(num_of_entries, free_slots);
- return -EAGAIN;
- }
- }
-
- return 0;
-}
-
-/*
- * int_queue_sanity_checks - perform some sanity checks on internal queue
- *
- * @hdev : pointer to hl_device structure
- * @q : pointer to hl_hw_queue structure
- * @num_of_entries : how many entries to check for space
- *
- * H/W queues spinlock should be taken before calling this function
- *
- * Perform the following:
- * - Make sure we have enough space in the h/w queue
- *
- */
-static int int_queue_sanity_checks(struct hl_device *hdev,
- struct hl_hw_queue *q,
- int num_of_entries)
-{
- int free_slots_cnt;
-
- if (num_of_entries > q->int_queue_len) {
- dev_err(hdev->dev,
- "Cannot populate queue %u with %u jobs\n",
- q->hw_queue_id, num_of_entries);
- return -ENOMEM;
- }
-
- /* Check we have enough space in the queue */
- free_slots_cnt = queue_free_slots(q, q->int_queue_len);
-
- if (free_slots_cnt < num_of_entries) {
- dev_dbg(hdev->dev, "Queue %d doesn't have room for %d CBs\n",
- q->hw_queue_id, num_of_entries);
- return -EAGAIN;
- }
-
- return 0;
-}
-
-/*
- * hw_queue_sanity_checks() - Make sure we have enough space in the h/w queue
- * @hdev: Pointer to hl_device structure.
- * @q: Pointer to hl_hw_queue structure.
- * @num_of_entries: How many entries to check for space.
- *
- * Notice: We do not reserve queue entries so this function mustn't be called
- * more than once per CS for the same queue
- *
- */
-static int hw_queue_sanity_checks(struct hl_device *hdev, struct hl_hw_queue *q,
- int num_of_entries)
-{
- int free_slots_cnt;
-
- /* Check we have enough space in the queue */
- free_slots_cnt = queue_free_slots(q, HL_QUEUE_LENGTH);
-
- if (free_slots_cnt < num_of_entries) {
- dev_dbg(hdev->dev, "Queue %d doesn't have room for %d CBs\n",
- q->hw_queue_id, num_of_entries);
- return -EAGAIN;
- }
-
- return 0;
-}
-
-/*
- * hl_hw_queue_send_cb_no_cmpl - send a single CB (not a JOB) without completion
- *
- * @hdev: pointer to hl_device structure
- * @hw_queue_id: Queue's type
- * @cb_size: size of CB
- * @cb_ptr: pointer to CB location
- *
- * This function sends a single CB, that must NOT generate a completion entry.
- * Sending CPU messages can be done instead via 'hl_hw_queue_submit_bd()'
- */
-int hl_hw_queue_send_cb_no_cmpl(struct hl_device *hdev, u32 hw_queue_id,
- u32 cb_size, u64 cb_ptr)
-{
- struct hl_hw_queue *q = &hdev->kernel_queues[hw_queue_id];
- int rc = 0;
-
- hdev->asic_funcs->hw_queues_lock(hdev);
-
- if (hdev->disabled) {
- rc = -EPERM;
- goto out;
- }
-
- /*
- * hl_hw_queue_send_cb_no_cmpl() is called for queues of a H/W queue
- * type only on init phase, when the queues are empty and being tested,
- * so there is no need for sanity checks.
- */
- if (q->queue_type != QUEUE_TYPE_HW) {
- rc = ext_queue_sanity_checks(hdev, q, 1, false);
- if (rc)
- goto out;
- }
-
- hl_hw_queue_submit_bd(hdev, q, 0, cb_size, cb_ptr);
-
-out:
- hdev->asic_funcs->hw_queues_unlock(hdev);
-
- return rc;
-}
-
-/*
- * ext_queue_schedule_job - submit a JOB to an external queue
- *
- * @job: pointer to the job that needs to be submitted to the queue
- *
- * This function must be called when the scheduler mutex is taken
- *
- */
-static void ext_queue_schedule_job(struct hl_cs_job *job)
-{
- struct hl_device *hdev = job->cs->ctx->hdev;
- struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id];
- struct hl_cq_entry cq_pkt;
- struct hl_cq *cq;
- u64 cq_addr;
- struct hl_cb *cb;
- u32 ctl;
- u32 len;
- u64 ptr;
-
- /*
- * Update the JOB ID inside the BD CTL so the device would know what
- * to write in the completion queue
- */
- ctl = ((q->pi << BD_CTL_SHADOW_INDEX_SHIFT) & BD_CTL_SHADOW_INDEX_MASK);
-
- cb = job->patched_cb;
- len = job->job_cb_size;
- ptr = cb->bus_address;
-
- /* Skip completion flow in case this is a non completion CS */
- if (!cs_needs_completion(job->cs))
- goto submit_bd;
-
- cq_pkt.data = cpu_to_le32(
- ((q->pi << CQ_ENTRY_SHADOW_INDEX_SHIFT)
- & CQ_ENTRY_SHADOW_INDEX_MASK) |
- FIELD_PREP(CQ_ENTRY_SHADOW_INDEX_VALID_MASK, 1) |
- FIELD_PREP(CQ_ENTRY_READY_MASK, 1));
-
- /*
- * No need to protect pi_offset because scheduling to the
- * H/W queues is done under the scheduler mutex
- *
- * No need to check if CQ is full because it was already
- * checked in ext_queue_sanity_checks
- */
- cq = &hdev->completion_queue[q->cq_id];
- cq_addr = cq->bus_address + cq->pi * sizeof(struct hl_cq_entry);
-
- hdev->asic_funcs->add_end_of_cb_packets(hdev, cb->kernel_address, len,
- job->user_cb_size,
- cq_addr,
- le32_to_cpu(cq_pkt.data),
- q->msi_vec,
- job->contains_dma_pkt);
-
- q->shadow_queue[hl_pi_2_offset(q->pi)] = job;
-
- cq->pi = hl_cq_inc_ptr(cq->pi);
-
-submit_bd:
- hl_hw_queue_submit_bd(hdev, q, ctl, len, ptr);
-}
-
-/*
- * int_queue_schedule_job - submit a JOB to an internal queue
- *
- * @job: pointer to the job that needs to be submitted to the queue
- *
- * This function must be called when the scheduler mutex is taken
- *
- */
-static void int_queue_schedule_job(struct hl_cs_job *job)
-{
- struct hl_device *hdev = job->cs->ctx->hdev;
- struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id];
- struct hl_bd bd;
- __le64 *pi;
-
- bd.ctl = 0;
- bd.len = cpu_to_le32(job->job_cb_size);
-
- if (job->is_kernel_allocated_cb)
- /* bus_address is actually a mmu mapped address
- * allocated from an internal pool
- */
- bd.ptr = cpu_to_le64(job->user_cb->bus_address);
- else
- bd.ptr = cpu_to_le64((u64) (uintptr_t) job->user_cb);
-
- pi = q->kernel_address + (q->pi & (q->int_queue_len - 1)) * sizeof(bd);
-
- q->pi++;
- q->pi &= ((q->int_queue_len << 1) - 1);
-
- hdev->asic_funcs->pqe_write(hdev, pi, &bd);
-
- hdev->asic_funcs->ring_doorbell(hdev, q->hw_queue_id, q->pi);
-}
-
-/*
- * hw_queue_schedule_job - submit a JOB to a H/W queue
- *
- * @job: pointer to the job that needs to be submitted to the queue
- *
- * This function must be called when the scheduler mutex is taken
- *
- */
-static void hw_queue_schedule_job(struct hl_cs_job *job)
-{
- struct hl_device *hdev = job->cs->ctx->hdev;
- struct hl_hw_queue *q = &hdev->kernel_queues[job->hw_queue_id];
- u64 ptr;
- u32 offset, ctl, len;
-
- /*
- * Upon PQE completion, COMP_DATA is used as the write data to the
- * completion queue (QMAN HBW message), and COMP_OFFSET is used as the
- * write address offset in the SM block (QMAN LBW message).
- * The write address offset is calculated as "COMP_OFFSET << 2".
- */
- offset = job->cs->sequence & (hdev->asic_prop.max_pending_cs - 1);
- ctl = ((offset << BD_CTL_COMP_OFFSET_SHIFT) & BD_CTL_COMP_OFFSET_MASK) |
- ((q->pi << BD_CTL_COMP_DATA_SHIFT) & BD_CTL_COMP_DATA_MASK);
-
- len = job->job_cb_size;
-
- /*
- * A patched CB is created only if a user CB was allocated by driver and
- * MMU is disabled. If MMU is enabled, the user CB should be used
- * instead. If the user CB wasn't allocated by driver, assume that it
- * holds an address.
- */
- if (job->patched_cb)
- ptr = job->patched_cb->bus_address;
- else if (job->is_kernel_allocated_cb)
- ptr = job->user_cb->bus_address;
- else
- ptr = (u64) (uintptr_t) job->user_cb;
-
- hl_hw_queue_submit_bd(hdev, q, ctl, len, ptr);
-}
-
-static int init_signal_cs(struct hl_device *hdev,
- struct hl_cs_job *job, struct hl_cs_compl *cs_cmpl)
-{
- struct hl_sync_stream_properties *prop;
- struct hl_hw_sob *hw_sob;
- u32 q_idx;
- int rc = 0;
-
- q_idx = job->hw_queue_id;
- prop = &hdev->kernel_queues[q_idx].sync_stream_prop;
- hw_sob = &prop->hw_sob[prop->curr_sob_offset];
-
- cs_cmpl->hw_sob = hw_sob;
- cs_cmpl->sob_val = prop->next_sob_val;
-
- dev_dbg(hdev->dev,
- "generate signal CB, sob_id: %d, sob val: %u, q_idx: %d, seq: %llu\n",
- cs_cmpl->hw_sob->sob_id, cs_cmpl->sob_val, q_idx,
- cs_cmpl->cs_seq);
-
- /* we set an EB since we must make sure all oeprations are done
- * when sending the signal
- */
- hdev->asic_funcs->gen_signal_cb(hdev, job->patched_cb,
- cs_cmpl->hw_sob->sob_id, 0, true);
-
- rc = hl_cs_signal_sob_wraparound_handler(hdev, q_idx, &hw_sob, 1,
- false);
-
- job->cs->sob_addr_offset = hw_sob->sob_addr;
- job->cs->initial_sob_count = prop->next_sob_val - 1;
-
- return rc;
-}
-
-void hl_hw_queue_encaps_sig_set_sob_info(struct hl_device *hdev,
- struct hl_cs *cs, struct hl_cs_job *job,
- struct hl_cs_compl *cs_cmpl)
-{
- struct hl_cs_encaps_sig_handle *handle = cs->encaps_sig_hdl;
- u32 offset = 0;
-
- cs_cmpl->hw_sob = handle->hw_sob;
-
- /* Note that encaps_sig_wait_offset was validated earlier in the flow
- * for offset value which exceeds the max reserved signal count.
- * always decrement 1 of the offset since when the user
- * set offset 1 for example he mean to wait only for the first
- * signal only, which will be pre_sob_val, and if he set offset 2
- * then the value required is (pre_sob_val + 1) and so on...
- * if user set wait offset to 0, then treat it as legacy wait cs,
- * wait for the next signal.
- */
- if (job->encaps_sig_wait_offset)
- offset = job->encaps_sig_wait_offset - 1;
-
- cs_cmpl->sob_val = handle->pre_sob_val + offset;
-}
-
-static int init_wait_cs(struct hl_device *hdev, struct hl_cs *cs,
- struct hl_cs_job *job, struct hl_cs_compl *cs_cmpl)
-{
- struct hl_gen_wait_properties wait_prop;
- struct hl_sync_stream_properties *prop;
- struct hl_cs_compl *signal_cs_cmpl;
- u32 q_idx;
-
- q_idx = job->hw_queue_id;
- prop = &hdev->kernel_queues[q_idx].sync_stream_prop;
-
- signal_cs_cmpl = container_of(cs->signal_fence,
- struct hl_cs_compl,
- base_fence);
-
- if (cs->encaps_signals) {
- /* use the encaps signal handle stored earlier in the flow
- * and set the SOB information from the encaps
- * signals handle
- */
- hl_hw_queue_encaps_sig_set_sob_info(hdev, cs, job, cs_cmpl);
-
- dev_dbg(hdev->dev, "Wait for encaps signals handle, qidx(%u), CS sequence(%llu), sob val: 0x%x, offset: %u\n",
- cs->encaps_sig_hdl->q_idx,
- cs->encaps_sig_hdl->cs_seq,
- cs_cmpl->sob_val,
- job->encaps_sig_wait_offset);
- } else {
- /* Copy the SOB id and value of the signal CS */
- cs_cmpl->hw_sob = signal_cs_cmpl->hw_sob;
- cs_cmpl->sob_val = signal_cs_cmpl->sob_val;
- }
-
- /* check again if the signal cs already completed.
- * if yes then don't send any wait cs since the hw_sob
- * could be in reset already. if signal is not completed
- * then get refcount to hw_sob to prevent resetting the sob
- * while wait cs is not submitted.
- * note that this check is protected by two locks,
- * hw queue lock and completion object lock,
- * and the same completion object lock also protects
- * the hw_sob reset handler function.
- * The hw_queue lock prevent out of sync of hw_sob
- * refcount value, changed by signal/wait flows.
- */
- spin_lock(&signal_cs_cmpl->lock);
-
- if (completion_done(&cs->signal_fence->completion)) {
- spin_unlock(&signal_cs_cmpl->lock);
- return -EINVAL;
- }
-
- kref_get(&cs_cmpl->hw_sob->kref);
-
- spin_unlock(&signal_cs_cmpl->lock);
-
- dev_dbg(hdev->dev,
- "generate wait CB, sob_id: %d, sob_val: 0x%x, mon_id: %d, q_idx: %d, seq: %llu\n",
- cs_cmpl->hw_sob->sob_id, cs_cmpl->sob_val,
- prop->base_mon_id, q_idx, cs->sequence);
-
- wait_prop.data = (void *) job->patched_cb;
- wait_prop.sob_base = cs_cmpl->hw_sob->sob_id;
- wait_prop.sob_mask = 0x1;
- wait_prop.sob_val = cs_cmpl->sob_val;
- wait_prop.mon_id = prop->base_mon_id;
- wait_prop.q_idx = q_idx;
- wait_prop.size = 0;
-
- hdev->asic_funcs->gen_wait_cb(hdev, &wait_prop);
-
- mb();
- hl_fence_put(cs->signal_fence);
- cs->signal_fence = NULL;
-
- return 0;
-}
-
-/*
- * init_signal_wait_cs - initialize a signal/wait CS
- * @cs: pointer to the signal/wait CS
- *
- * H/W queues spinlock should be taken before calling this function
- */
-static int init_signal_wait_cs(struct hl_cs *cs)
-{
- struct hl_ctx *ctx = cs->ctx;
- struct hl_device *hdev = ctx->hdev;
- struct hl_cs_job *job;
- struct hl_cs_compl *cs_cmpl =
- container_of(cs->fence, struct hl_cs_compl, base_fence);
- int rc = 0;
-
- /* There is only one job in a signal/wait CS */
- job = list_first_entry(&cs->job_list, struct hl_cs_job,
- cs_node);
-
- if (cs->type & CS_TYPE_SIGNAL)
- rc = init_signal_cs(hdev, job, cs_cmpl);
- else if (cs->type & CS_TYPE_WAIT)
- rc = init_wait_cs(hdev, cs, job, cs_cmpl);
-
- return rc;
-}
-
-static int encaps_sig_first_staged_cs_handler
- (struct hl_device *hdev, struct hl_cs *cs)
-{
- struct hl_cs_compl *cs_cmpl =
- container_of(cs->fence,
- struct hl_cs_compl, base_fence);
- struct hl_cs_encaps_sig_handle *encaps_sig_hdl;
- struct hl_encaps_signals_mgr *mgr;
- int rc = 0;
-
- mgr = &cs->ctx->sig_mgr;
-
- spin_lock(&mgr->lock);
- encaps_sig_hdl = idr_find(&mgr->handles, cs->encaps_sig_hdl_id);
- if (encaps_sig_hdl) {
- /*
- * Set handler CS sequence,
- * the CS which contains the encapsulated signals.
- */
- encaps_sig_hdl->cs_seq = cs->sequence;
- /* store the handle and set encaps signal indication,
- * to be used later in cs_do_release to put the last
- * reference to encaps signals handlers.
- */
- cs_cmpl->encaps_signals = true;
- cs_cmpl->encaps_sig_hdl = encaps_sig_hdl;
-
- /* set hw_sob pointer in completion object
- * since it's used in cs_do_release flow to put
- * refcount to sob
- */
- cs_cmpl->hw_sob = encaps_sig_hdl->hw_sob;
- cs_cmpl->sob_val = encaps_sig_hdl->pre_sob_val +
- encaps_sig_hdl->count;
-
- dev_dbg(hdev->dev, "CS seq (%llu) added to encaps signal handler id (%u), count(%u), qidx(%u), sob(%u), val(%u)\n",
- cs->sequence, encaps_sig_hdl->id,
- encaps_sig_hdl->count,
- encaps_sig_hdl->q_idx,
- cs_cmpl->hw_sob->sob_id,
- cs_cmpl->sob_val);
-
- } else {
- dev_err(hdev->dev, "encaps handle id(%u) wasn't found!\n",
- cs->encaps_sig_hdl_id);
- rc = -EINVAL;
- }
-
- spin_unlock(&mgr->lock);
-
- return rc;
-}
-
-/*
- * hl_hw_queue_schedule_cs - schedule a command submission
- * @cs: pointer to the CS
- */
-int hl_hw_queue_schedule_cs(struct hl_cs *cs)
-{
- enum hl_device_status status;
- struct hl_cs_counters_atomic *cntr;
- struct hl_ctx *ctx = cs->ctx;
- struct hl_device *hdev = ctx->hdev;
- struct hl_cs_job *job, *tmp;
- struct hl_hw_queue *q;
- int rc = 0, i, cq_cnt;
- bool first_entry;
- u32 max_queues;
-
- cntr = &hdev->aggregated_cs_counters;
-
- hdev->asic_funcs->hw_queues_lock(hdev);
-
- if (!hl_device_operational(hdev, &status)) {
- atomic64_inc(&cntr->device_in_reset_drop_cnt);
- atomic64_inc(&ctx->cs_counters.device_in_reset_drop_cnt);
- dev_err(hdev->dev,
- "device is %s, CS rejected!\n", hdev->status[status]);
- rc = -EPERM;
- goto out;
- }
-
- max_queues = hdev->asic_prop.max_queues;
-
- q = &hdev->kernel_queues[0];
- for (i = 0, cq_cnt = 0 ; i < max_queues ; i++, q++) {
- if (cs->jobs_in_queue_cnt[i]) {
- switch (q->queue_type) {
- case QUEUE_TYPE_EXT:
- rc = ext_queue_sanity_checks(hdev, q,
- cs->jobs_in_queue_cnt[i],
- cs_needs_completion(cs) ?
- true : false);
- break;
- case QUEUE_TYPE_INT:
- rc = int_queue_sanity_checks(hdev, q,
- cs->jobs_in_queue_cnt[i]);
- break;
- case QUEUE_TYPE_HW:
- rc = hw_queue_sanity_checks(hdev, q,
- cs->jobs_in_queue_cnt[i]);
- break;
- default:
- dev_err(hdev->dev, "Queue type %d is invalid\n",
- q->queue_type);
- rc = -EINVAL;
- break;
- }
-
- if (rc) {
- atomic64_inc(
- &ctx->cs_counters.queue_full_drop_cnt);
- atomic64_inc(&cntr->queue_full_drop_cnt);
- goto unroll_cq_resv;
- }
-
- if (q->queue_type == QUEUE_TYPE_EXT)
- cq_cnt++;
- }
- }
-
- if ((cs->type == CS_TYPE_SIGNAL) || (cs->type == CS_TYPE_WAIT)) {
- rc = init_signal_wait_cs(cs);
- if (rc)
- goto unroll_cq_resv;
- } else if (cs->type == CS_TYPE_COLLECTIVE_WAIT) {
- rc = hdev->asic_funcs->collective_wait_init_cs(cs);
- if (rc)
- goto unroll_cq_resv;
- }
-
- rc = hdev->asic_funcs->pre_schedule_cs(cs);
- if (rc) {
- dev_err(hdev->dev,
- "Failed in pre-submission operations of CS %d.%llu\n",
- ctx->asid, cs->sequence);
- goto unroll_cq_resv;
- }
-
- hdev->shadow_cs_queue[cs->sequence &
- (hdev->asic_prop.max_pending_cs - 1)] = cs;
-
- if (cs->encaps_signals && cs->staged_first) {
- rc = encaps_sig_first_staged_cs_handler(hdev, cs);
- if (rc)
- goto unroll_cq_resv;
- }
-
- spin_lock(&hdev->cs_mirror_lock);
-
- /* Verify staged CS exists and add to the staged list */
- if (cs->staged_cs && !cs->staged_first) {
- struct hl_cs *staged_cs;
-
- staged_cs = hl_staged_cs_find_first(hdev, cs->staged_sequence);
- if (!staged_cs) {
- dev_err(hdev->dev,
- "Cannot find staged submission sequence %llu",
- cs->staged_sequence);
- rc = -EINVAL;
- goto unlock_cs_mirror;
- }
-
- if (is_staged_cs_last_exists(hdev, staged_cs)) {
- dev_err(hdev->dev,
- "Staged submission sequence %llu already submitted",
- cs->staged_sequence);
- rc = -EINVAL;
- goto unlock_cs_mirror;
- }
-
- list_add_tail(&cs->staged_cs_node, &staged_cs->staged_cs_node);
-
- /* update stream map of the first CS */
- if (hdev->supports_wait_for_multi_cs)
- staged_cs->fence->stream_master_qid_map |=
- cs->fence->stream_master_qid_map;
- }
-
- list_add_tail(&cs->mirror_node, &hdev->cs_mirror_list);
-
- /* Queue TDR if the CS is the first entry and if timeout is wanted */
- first_entry = list_first_entry(&hdev->cs_mirror_list,
- struct hl_cs, mirror_node) == cs;
- if ((hdev->timeout_jiffies != MAX_SCHEDULE_TIMEOUT) &&
- first_entry && cs_needs_timeout(cs)) {
- cs->tdr_active = true;
- schedule_delayed_work(&cs->work_tdr, cs->timeout_jiffies);
-
- }
-
- spin_unlock(&hdev->cs_mirror_lock);
-
- list_for_each_entry_safe(job, tmp, &cs->job_list, cs_node)
- switch (job->queue_type) {
- case QUEUE_TYPE_EXT:
- ext_queue_schedule_job(job);
- break;
- case QUEUE_TYPE_INT:
- int_queue_schedule_job(job);
- break;
- case QUEUE_TYPE_HW:
- hw_queue_schedule_job(job);
- break;
- default:
- break;
- }
-
- cs->submitted = true;
-
- goto out;
-
-unlock_cs_mirror:
- spin_unlock(&hdev->cs_mirror_lock);
-unroll_cq_resv:
- q = &hdev->kernel_queues[0];
- for (i = 0 ; (i < max_queues) && (cq_cnt > 0) ; i++, q++) {
- if ((q->queue_type == QUEUE_TYPE_EXT) &&
- (cs->jobs_in_queue_cnt[i])) {
- atomic_t *free_slots =
- &hdev->completion_queue[i].free_slots_cnt;
- atomic_add(cs->jobs_in_queue_cnt[i], free_slots);
- cq_cnt--;
- }
- }
-
-out:
- hdev->asic_funcs->hw_queues_unlock(hdev);
-
- return rc;
-}
-
-/*
- * hl_hw_queue_inc_ci_kernel - increment ci for kernel's queue
- *
- * @hdev: pointer to hl_device structure
- * @hw_queue_id: which queue to increment its ci
- */
-void hl_hw_queue_inc_ci_kernel(struct hl_device *hdev, u32 hw_queue_id)
-{
- struct hl_hw_queue *q = &hdev->kernel_queues[hw_queue_id];
-
- atomic_inc(&q->ci);
-}
-
-static int ext_and_cpu_queue_init(struct hl_device *hdev, struct hl_hw_queue *q,
- bool is_cpu_queue)
-{
- void *p;
- int rc;
-
- if (is_cpu_queue)
- p = hl_cpu_accessible_dma_pool_alloc(hdev, HL_QUEUE_SIZE_IN_BYTES, &q->bus_address);
- else
- p = hl_asic_dma_alloc_coherent(hdev, HL_QUEUE_SIZE_IN_BYTES, &q->bus_address,
- GFP_KERNEL | __GFP_ZERO);
- if (!p)
- return -ENOMEM;
-
- q->kernel_address = p;
-
- q->shadow_queue = kmalloc_array(HL_QUEUE_LENGTH, sizeof(struct hl_cs_job *), GFP_KERNEL);
- if (!q->shadow_queue) {
- dev_err(hdev->dev,
- "Failed to allocate shadow queue for H/W queue %d\n",
- q->hw_queue_id);
- rc = -ENOMEM;
- goto free_queue;
- }
-
- /* Make sure read/write pointers are initialized to start of queue */
- atomic_set(&q->ci, 0);
- q->pi = 0;
-
- return 0;
-
-free_queue:
- if (is_cpu_queue)
- hl_cpu_accessible_dma_pool_free(hdev, HL_QUEUE_SIZE_IN_BYTES, q->kernel_address);
- else
- hl_asic_dma_free_coherent(hdev, HL_QUEUE_SIZE_IN_BYTES, q->kernel_address,
- q->bus_address);
-
- return rc;
-}
-
-static int int_queue_init(struct hl_device *hdev, struct hl_hw_queue *q)
-{
- void *p;
-
- p = hdev->asic_funcs->get_int_queue_base(hdev, q->hw_queue_id,
- &q->bus_address, &q->int_queue_len);
- if (!p) {
- dev_err(hdev->dev,
- "Failed to get base address for internal queue %d\n",
- q->hw_queue_id);
- return -EFAULT;
- }
-
- q->kernel_address = p;
- q->pi = 0;
- atomic_set(&q->ci, 0);
-
- return 0;
-}
-
-static int cpu_queue_init(struct hl_device *hdev, struct hl_hw_queue *q)
-{
- return ext_and_cpu_queue_init(hdev, q, true);
-}
-
-static int ext_queue_init(struct hl_device *hdev, struct hl_hw_queue *q)
-{
- return ext_and_cpu_queue_init(hdev, q, false);
-}
-
-static int hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q)
-{
- void *p;
-
- p = hl_asic_dma_alloc_coherent(hdev, HL_QUEUE_SIZE_IN_BYTES, &q->bus_address,
- GFP_KERNEL | __GFP_ZERO);
- if (!p)
- return -ENOMEM;
-
- q->kernel_address = p;
-
- /* Make sure read/write pointers are initialized to start of queue */
- atomic_set(&q->ci, 0);
- q->pi = 0;
-
- return 0;
-}
-
-static void sync_stream_queue_init(struct hl_device *hdev, u32 q_idx)
-{
- struct hl_sync_stream_properties *sync_stream_prop;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct hl_hw_sob *hw_sob;
- int sob, reserved_mon_idx, queue_idx;
-
- sync_stream_prop = &hdev->kernel_queues[q_idx].sync_stream_prop;
-
- /* We use 'collective_mon_idx' as a running index in order to reserve
- * monitors for collective master/slave queues.
- * collective master queue gets 2 reserved monitors
- * collective slave queue gets 1 reserved monitor
- */
- if (hdev->kernel_queues[q_idx].collective_mode ==
- HL_COLLECTIVE_MASTER) {
- reserved_mon_idx = hdev->collective_mon_idx;
-
- /* reserve the first monitor for collective master queue */
- sync_stream_prop->collective_mstr_mon_id[0] =
- prop->collective_first_mon + reserved_mon_idx;
-
- /* reserve the second monitor for collective master queue */
- sync_stream_prop->collective_mstr_mon_id[1] =
- prop->collective_first_mon + reserved_mon_idx + 1;
-
- hdev->collective_mon_idx += HL_COLLECTIVE_RSVD_MSTR_MONS;
- } else if (hdev->kernel_queues[q_idx].collective_mode ==
- HL_COLLECTIVE_SLAVE) {
- reserved_mon_idx = hdev->collective_mon_idx++;
-
- /* reserve a monitor for collective slave queue */
- sync_stream_prop->collective_slave_mon_id =
- prop->collective_first_mon + reserved_mon_idx;
- }
-
- if (!hdev->kernel_queues[q_idx].supports_sync_stream)
- return;
-
- queue_idx = hdev->sync_stream_queue_idx++;
-
- sync_stream_prop->base_sob_id = prop->sync_stream_first_sob +
- (queue_idx * HL_RSVD_SOBS);
- sync_stream_prop->base_mon_id = prop->sync_stream_first_mon +
- (queue_idx * HL_RSVD_MONS);
- sync_stream_prop->next_sob_val = 1;
- sync_stream_prop->curr_sob_offset = 0;
-
- for (sob = 0 ; sob < HL_RSVD_SOBS ; sob++) {
- hw_sob = &sync_stream_prop->hw_sob[sob];
- hw_sob->hdev = hdev;
- hw_sob->sob_id = sync_stream_prop->base_sob_id + sob;
- hw_sob->sob_addr =
- hdev->asic_funcs->get_sob_addr(hdev, hw_sob->sob_id);
- hw_sob->q_idx = q_idx;
- kref_init(&hw_sob->kref);
- }
-}
-
-static void sync_stream_queue_reset(struct hl_device *hdev, u32 q_idx)
-{
- struct hl_sync_stream_properties *prop =
- &hdev->kernel_queues[q_idx].sync_stream_prop;
-
- /*
- * In case we got here due to a stuck CS, the refcnt might be bigger
- * than 1 and therefore we reset it.
- */
- kref_init(&prop->hw_sob[prop->curr_sob_offset].kref);
- prop->curr_sob_offset = 0;
- prop->next_sob_val = 1;
-}
-
-/*
- * queue_init - main initialization function for H/W queue object
- *
- * @hdev: pointer to hl_device device structure
- * @q: pointer to hl_hw_queue queue structure
- * @hw_queue_id: The id of the H/W queue
- *
- * Allocate dma-able memory for the queue and initialize fields
- * Returns 0 on success
- */
-static int queue_init(struct hl_device *hdev, struct hl_hw_queue *q,
- u32 hw_queue_id)
-{
- int rc;
-
- q->hw_queue_id = hw_queue_id;
-
- switch (q->queue_type) {
- case QUEUE_TYPE_EXT:
- rc = ext_queue_init(hdev, q);
- break;
- case QUEUE_TYPE_INT:
- rc = int_queue_init(hdev, q);
- break;
- case QUEUE_TYPE_CPU:
- rc = cpu_queue_init(hdev, q);
- break;
- case QUEUE_TYPE_HW:
- rc = hw_queue_init(hdev, q);
- break;
- case QUEUE_TYPE_NA:
- q->valid = 0;
- return 0;
- default:
- dev_crit(hdev->dev, "wrong queue type %d during init\n",
- q->queue_type);
- rc = -EINVAL;
- break;
- }
-
- sync_stream_queue_init(hdev, q->hw_queue_id);
-
- if (rc)
- return rc;
-
- q->valid = 1;
-
- return 0;
-}
-
-/*
- * hw_queue_fini - destroy queue
- *
- * @hdev: pointer to hl_device device structure
- * @q: pointer to hl_hw_queue queue structure
- *
- * Free the queue memory
- */
-static void queue_fini(struct hl_device *hdev, struct hl_hw_queue *q)
-{
- if (!q->valid)
- return;
-
- /*
- * If we arrived here, there are no jobs waiting on this queue
- * so we can safely remove it.
- * This is because this function can only called when:
- * 1. Either a context is deleted, which only can occur if all its
- * jobs were finished
- * 2. A context wasn't able to be created due to failure or timeout,
- * which means there are no jobs on the queue yet
- *
- * The only exception are the queues of the kernel context, but
- * if they are being destroyed, it means that the entire module is
- * being removed. If the module is removed, it means there is no open
- * user context. It also means that if a job was submitted by
- * the kernel driver (e.g. context creation), the job itself was
- * released by the kernel driver when a timeout occurred on its
- * Completion. Thus, we don't need to release it again.
- */
-
- if (q->queue_type == QUEUE_TYPE_INT)
- return;
-
- kfree(q->shadow_queue);
-
- if (q->queue_type == QUEUE_TYPE_CPU)
- hl_cpu_accessible_dma_pool_free(hdev, HL_QUEUE_SIZE_IN_BYTES, q->kernel_address);
- else
- hl_asic_dma_free_coherent(hdev, HL_QUEUE_SIZE_IN_BYTES, q->kernel_address,
- q->bus_address);
-}
-
-int hl_hw_queues_create(struct hl_device *hdev)
-{
- struct asic_fixed_properties *asic = &hdev->asic_prop;
- struct hl_hw_queue *q;
- int i, rc, q_ready_cnt;
-
- hdev->kernel_queues = kcalloc(asic->max_queues,
- sizeof(*hdev->kernel_queues), GFP_KERNEL);
-
- if (!hdev->kernel_queues) {
- dev_err(hdev->dev, "Not enough memory for H/W queues\n");
- return -ENOMEM;
- }
-
- /* Initialize the H/W queues */
- for (i = 0, q_ready_cnt = 0, q = hdev->kernel_queues;
- i < asic->max_queues ; i++, q_ready_cnt++, q++) {
-
- q->queue_type = asic->hw_queues_props[i].type;
- q->supports_sync_stream =
- asic->hw_queues_props[i].supports_sync_stream;
- q->collective_mode = asic->hw_queues_props[i].collective_mode;
- rc = queue_init(hdev, q, i);
- if (rc) {
- dev_err(hdev->dev,
- "failed to initialize queue %d\n", i);
- goto release_queues;
- }
- }
-
- return 0;
-
-release_queues:
- for (i = 0, q = hdev->kernel_queues ; i < q_ready_cnt ; i++, q++)
- queue_fini(hdev, q);
-
- kfree(hdev->kernel_queues);
-
- return rc;
-}
-
-void hl_hw_queues_destroy(struct hl_device *hdev)
-{
- struct hl_hw_queue *q;
- u32 max_queues = hdev->asic_prop.max_queues;
- int i;
-
- for (i = 0, q = hdev->kernel_queues ; i < max_queues ; i++, q++)
- queue_fini(hdev, q);
-
- kfree(hdev->kernel_queues);
-}
-
-void hl_hw_queue_reset(struct hl_device *hdev, bool hard_reset)
-{
- struct hl_hw_queue *q;
- u32 max_queues = hdev->asic_prop.max_queues;
- int i;
-
- for (i = 0, q = hdev->kernel_queues ; i < max_queues ; i++, q++) {
- if ((!q->valid) ||
- ((!hard_reset) && (q->queue_type == QUEUE_TYPE_CPU)))
- continue;
- q->pi = 0;
- atomic_set(&q->ci, 0);
-
- if (q->supports_sync_stream)
- sync_stream_queue_reset(hdev, q->hw_queue_id);
- }
-}
diff --git a/drivers/misc/habanalabs/common/hwmon.c b/drivers/misc/habanalabs/common/hwmon.c
deleted file mode 100644
index 55eb0203817f..000000000000
--- a/drivers/misc/habanalabs/common/hwmon.c
+++ /dev/null
@@ -1,934 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2019 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include "habanalabs.h"
-
-#include <linux/pci.h>
-#include <linux/hwmon.h>
-
-#define HWMON_NR_SENSOR_TYPES (hwmon_max)
-
-#ifdef _HAS_HWMON_HWMON_T_ENABLE
-
-static u32 fixup_flags_legacy_fw(struct hl_device *hdev, enum hwmon_sensor_types type,
- u32 cpucp_flags)
-{
- u32 flags;
-
- switch (type) {
- case hwmon_temp:
- flags = (cpucp_flags << 1) | HWMON_T_ENABLE;
- break;
-
- case hwmon_in:
- flags = (cpucp_flags << 1) | HWMON_I_ENABLE;
- break;
-
- case hwmon_curr:
- flags = (cpucp_flags << 1) | HWMON_C_ENABLE;
- break;
-
- case hwmon_fan:
- flags = (cpucp_flags << 1) | HWMON_F_ENABLE;
- break;
-
- case hwmon_power:
- flags = (cpucp_flags << 1) | HWMON_P_ENABLE;
- break;
-
- case hwmon_pwm:
- /* enable bit was here from day 1, so no need to adjust */
- flags = cpucp_flags;
- break;
-
- default:
- dev_err(hdev->dev, "unsupported h/w sensor type %d\n", type);
- flags = cpucp_flags;
- break;
- }
-
- return flags;
-}
-
-static u32 fixup_attr_legacy_fw(u32 attr)
-{
- return (attr - 1);
-}
-
-#else
-
-static u32 fixup_flags_legacy_fw(struct hl_device *hdev, enum hwmon_sensor_types type,
- u32 cpucp_flags)
-{
- return cpucp_flags;
-}
-
-static u32 fixup_attr_legacy_fw(u32 attr)
-{
- return attr;
-}
-
-#endif /* !_HAS_HWMON_HWMON_T_ENABLE */
-
-static u32 adjust_hwmon_flags(struct hl_device *hdev, enum hwmon_sensor_types type, u32 cpucp_flags)
-{
- u32 flags, cpucp_input_val;
- bool use_cpucp_enum;
-
- use_cpucp_enum = (hdev->asic_prop.fw_app_cpu_boot_dev_sts0 &
- CPU_BOOT_DEV_STS0_MAP_HWMON_EN) ? true : false;
-
- /* If f/w is using it's own enum, we need to check if the properties values are aligned.
- * If not, it means we need to adjust the values to the new format that is used in the
- * kernel since 5.6 (enum values were incremented by 1 by adding a new enable value).
- */
- if (use_cpucp_enum) {
- switch (type) {
- case hwmon_temp:
- cpucp_input_val = cpucp_temp_input;
- if (cpucp_input_val == hwmon_temp_input)
- flags = cpucp_flags;
- else
- flags = (cpucp_flags << 1) | HWMON_T_ENABLE;
- break;
-
- case hwmon_in:
- cpucp_input_val = cpucp_in_input;
- if (cpucp_input_val == hwmon_in_input)
- flags = cpucp_flags;
- else
- flags = (cpucp_flags << 1) | HWMON_I_ENABLE;
- break;
-
- case hwmon_curr:
- cpucp_input_val = cpucp_curr_input;
- if (cpucp_input_val == hwmon_curr_input)
- flags = cpucp_flags;
- else
- flags = (cpucp_flags << 1) | HWMON_C_ENABLE;
- break;
-
- case hwmon_fan:
- cpucp_input_val = cpucp_fan_input;
- if (cpucp_input_val == hwmon_fan_input)
- flags = cpucp_flags;
- else
- flags = (cpucp_flags << 1) | HWMON_F_ENABLE;
- break;
-
- case hwmon_pwm:
- /* enable bit was here from day 1, so no need to adjust */
- flags = cpucp_flags;
- break;
-
- case hwmon_power:
- cpucp_input_val = CPUCP_POWER_INPUT;
- if (cpucp_input_val == hwmon_power_input)
- flags = cpucp_flags;
- else
- flags = (cpucp_flags << 1) | HWMON_P_ENABLE;
- break;
-
- default:
- dev_err(hdev->dev, "unsupported h/w sensor type %d\n", type);
- flags = cpucp_flags;
- break;
- }
- } else {
- flags = fixup_flags_legacy_fw(hdev, type, cpucp_flags);
- }
-
- return flags;
-}
-
-int hl_build_hwmon_channel_info(struct hl_device *hdev, struct cpucp_sensor *sensors_arr)
-{
- u32 num_sensors_for_type, flags, num_active_sensor_types = 0, arr_size = 0, *curr_arr;
- u32 sensors_by_type_next_index[HWMON_NR_SENSOR_TYPES] = {0};
- u32 *sensors_by_type[HWMON_NR_SENSOR_TYPES] = {NULL};
- struct hwmon_channel_info **channels_info;
- u32 counts[HWMON_NR_SENSOR_TYPES] = {0};
- enum hwmon_sensor_types type;
- int rc, i, j;
-
- for (i = 0 ; i < CPUCP_MAX_SENSORS ; i++) {
- type = le32_to_cpu(sensors_arr[i].type);
-
- if ((type == 0) && (sensors_arr[i].flags == 0))
- break;
-
- if (type >= HWMON_NR_SENSOR_TYPES) {
- dev_err(hdev->dev, "Got wrong sensor type %d from device\n", type);
- return -EINVAL;
- }
-
- counts[type]++;
- arr_size++;
- }
-
- for (i = 0 ; i < HWMON_NR_SENSOR_TYPES ; i++) {
- if (counts[i] == 0)
- continue;
-
- num_sensors_for_type = counts[i] + 1;
- dev_dbg(hdev->dev, "num_sensors_for_type %d = %d\n", i, num_sensors_for_type);
-
- curr_arr = kcalloc(num_sensors_for_type, sizeof(*curr_arr), GFP_KERNEL);
- if (!curr_arr) {
- rc = -ENOMEM;
- goto sensors_type_err;
- }
-
- num_active_sensor_types++;
- sensors_by_type[i] = curr_arr;
- }
-
- for (i = 0 ; i < arr_size ; i++) {
- type = le32_to_cpu(sensors_arr[i].type);
- curr_arr = sensors_by_type[type];
- flags = adjust_hwmon_flags(hdev, type, le32_to_cpu(sensors_arr[i].flags));
- curr_arr[sensors_by_type_next_index[type]++] = flags;
- }
-
- channels_info = kcalloc(num_active_sensor_types + 1, sizeof(struct hwmon_channel_info *),
- GFP_KERNEL);
- if (!channels_info) {
- rc = -ENOMEM;
- goto channels_info_array_err;
- }
-
- for (i = 0 ; i < num_active_sensor_types ; i++) {
- channels_info[i] = kzalloc(sizeof(*channels_info[i]), GFP_KERNEL);
- if (!channels_info[i]) {
- rc = -ENOMEM;
- goto channel_info_err;
- }
- }
-
- for (i = 0, j = 0 ; i < HWMON_NR_SENSOR_TYPES ; i++) {
- if (!sensors_by_type[i])
- continue;
-
- channels_info[j]->type = i;
- channels_info[j]->config = sensors_by_type[i];
- j++;
- }
-
- hdev->hl_chip_info->info = (const struct hwmon_channel_info **)channels_info;
-
- return 0;
-
-channel_info_err:
- for (i = 0 ; i < num_active_sensor_types ; i++) {
- if (channels_info[i]) {
- kfree(channels_info[i]->config);
- kfree(channels_info[i]);
- }
- }
- kfree(channels_info);
-
-channels_info_array_err:
-sensors_type_err:
- for (i = 0 ; i < HWMON_NR_SENSOR_TYPES ; i++)
- kfree(sensors_by_type[i]);
-
- return rc;
-}
-
-static int hl_read(struct device *dev, enum hwmon_sensor_types type,
- u32 attr, int channel, long *val)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
- bool use_cpucp_enum;
- u32 cpucp_attr;
- int rc;
-
- if (!hl_device_operational(hdev, NULL))
- return -ENODEV;
-
- use_cpucp_enum = (hdev->asic_prop.fw_app_cpu_boot_dev_sts0 &
- CPU_BOOT_DEV_STS0_MAP_HWMON_EN) ? true : false;
-
- switch (type) {
- case hwmon_temp:
- switch (attr) {
- case hwmon_temp_input:
- cpucp_attr = cpucp_temp_input;
- break;
- case hwmon_temp_max:
- cpucp_attr = cpucp_temp_max;
- break;
- case hwmon_temp_crit:
- cpucp_attr = cpucp_temp_crit;
- break;
- case hwmon_temp_max_hyst:
- cpucp_attr = cpucp_temp_max_hyst;
- break;
- case hwmon_temp_crit_hyst:
- cpucp_attr = cpucp_temp_crit_hyst;
- break;
- case hwmon_temp_offset:
- cpucp_attr = cpucp_temp_offset;
- break;
- case hwmon_temp_highest:
- cpucp_attr = cpucp_temp_highest;
- break;
- default:
- return -EINVAL;
- }
-
- if (use_cpucp_enum)
- rc = hl_get_temperature(hdev, channel, cpucp_attr, val);
- else
- rc = hl_get_temperature(hdev, channel, fixup_attr_legacy_fw(attr), val);
- break;
- case hwmon_in:
- switch (attr) {
- case hwmon_in_input:
- cpucp_attr = cpucp_in_input;
- break;
- case hwmon_in_min:
- cpucp_attr = cpucp_in_min;
- break;
- case hwmon_in_max:
- cpucp_attr = cpucp_in_max;
- break;
- case hwmon_in_highest:
- cpucp_attr = cpucp_in_highest;
- break;
- default:
- return -EINVAL;
- }
-
- if (use_cpucp_enum)
- rc = hl_get_voltage(hdev, channel, cpucp_attr, val);
- else
- rc = hl_get_voltage(hdev, channel, fixup_attr_legacy_fw(attr), val);
- break;
- case hwmon_curr:
- switch (attr) {
- case hwmon_curr_input:
- cpucp_attr = cpucp_curr_input;
- break;
- case hwmon_curr_min:
- cpucp_attr = cpucp_curr_min;
- break;
- case hwmon_curr_max:
- cpucp_attr = cpucp_curr_max;
- break;
- case hwmon_curr_highest:
- cpucp_attr = cpucp_curr_highest;
- break;
- default:
- return -EINVAL;
- }
-
- if (use_cpucp_enum)
- rc = hl_get_current(hdev, channel, cpucp_attr, val);
- else
- rc = hl_get_current(hdev, channel, fixup_attr_legacy_fw(attr), val);
- break;
- case hwmon_fan:
- switch (attr) {
- case hwmon_fan_input:
- cpucp_attr = cpucp_fan_input;
- break;
- case hwmon_fan_min:
- cpucp_attr = cpucp_fan_min;
- break;
- case hwmon_fan_max:
- cpucp_attr = cpucp_fan_max;
- break;
- default:
- return -EINVAL;
- }
-
- if (use_cpucp_enum)
- rc = hl_get_fan_speed(hdev, channel, cpucp_attr, val);
- else
- rc = hl_get_fan_speed(hdev, channel, fixup_attr_legacy_fw(attr), val);
- break;
- case hwmon_pwm:
- switch (attr) {
- case hwmon_pwm_input:
- cpucp_attr = cpucp_pwm_input;
- break;
- case hwmon_pwm_enable:
- cpucp_attr = cpucp_pwm_enable;
- break;
- default:
- return -EINVAL;
- }
-
- if (use_cpucp_enum)
- rc = hl_get_pwm_info(hdev, channel, cpucp_attr, val);
- else
- /* no need for fixup as pwm was aligned from day 1 */
- rc = hl_get_pwm_info(hdev, channel, attr, val);
- break;
- case hwmon_power:
- switch (attr) {
- case hwmon_power_input:
- cpucp_attr = CPUCP_POWER_INPUT;
- break;
- case hwmon_power_input_highest:
- cpucp_attr = CPUCP_POWER_INPUT_HIGHEST;
- break;
- default:
- return -EINVAL;
- }
-
- if (use_cpucp_enum)
- rc = hl_get_power(hdev, channel, cpucp_attr, val);
- else
- rc = hl_get_power(hdev, channel, fixup_attr_legacy_fw(attr), val);
- break;
- default:
- return -EINVAL;
- }
- return rc;
-}
-
-static int hl_write(struct device *dev, enum hwmon_sensor_types type,
- u32 attr, int channel, long val)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
- u32 cpucp_attr;
- bool use_cpucp_enum = (hdev->asic_prop.fw_app_cpu_boot_dev_sts0 &
- CPU_BOOT_DEV_STS0_MAP_HWMON_EN) ? true : false;
-
- if (!hl_device_operational(hdev, NULL))
- return -ENODEV;
-
- switch (type) {
- case hwmon_temp:
- switch (attr) {
- case hwmon_temp_offset:
- cpucp_attr = cpucp_temp_offset;
- break;
- case hwmon_temp_reset_history:
- cpucp_attr = cpucp_temp_reset_history;
- break;
- default:
- return -EINVAL;
- }
-
- if (use_cpucp_enum)
- hl_set_temperature(hdev, channel, cpucp_attr, val);
- else
- hl_set_temperature(hdev, channel, fixup_attr_legacy_fw(attr), val);
- break;
- case hwmon_pwm:
- switch (attr) {
- case hwmon_pwm_input:
- cpucp_attr = cpucp_pwm_input;
- break;
- case hwmon_pwm_enable:
- cpucp_attr = cpucp_pwm_enable;
- break;
- default:
- return -EINVAL;
- }
-
- if (use_cpucp_enum)
- hl_set_pwm_info(hdev, channel, cpucp_attr, val);
- else
- /* no need for fixup as pwm was aligned from day 1 */
- hl_set_pwm_info(hdev, channel, attr, val);
- break;
- case hwmon_in:
- switch (attr) {
- case hwmon_in_reset_history:
- cpucp_attr = cpucp_in_reset_history;
- break;
- default:
- return -EINVAL;
- }
-
- if (use_cpucp_enum)
- hl_set_voltage(hdev, channel, cpucp_attr, val);
- else
- hl_set_voltage(hdev, channel, fixup_attr_legacy_fw(attr), val);
- break;
- case hwmon_curr:
- switch (attr) {
- case hwmon_curr_reset_history:
- cpucp_attr = cpucp_curr_reset_history;
- break;
- default:
- return -EINVAL;
- }
-
- if (use_cpucp_enum)
- hl_set_current(hdev, channel, cpucp_attr, val);
- else
- hl_set_current(hdev, channel, fixup_attr_legacy_fw(attr), val);
- break;
- case hwmon_power:
- switch (attr) {
- case hwmon_power_reset_history:
- cpucp_attr = CPUCP_POWER_RESET_INPUT_HISTORY;
- break;
- default:
- return -EINVAL;
- }
-
- if (use_cpucp_enum)
- hl_set_power(hdev, channel, cpucp_attr, val);
- else
- hl_set_power(hdev, channel, fixup_attr_legacy_fw(attr), val);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static umode_t hl_is_visible(const void *data, enum hwmon_sensor_types type,
- u32 attr, int channel)
-{
- switch (type) {
- case hwmon_temp:
- switch (attr) {
- case hwmon_temp_input:
- case hwmon_temp_max:
- case hwmon_temp_max_hyst:
- case hwmon_temp_crit:
- case hwmon_temp_crit_hyst:
- case hwmon_temp_highest:
- return 0444;
- case hwmon_temp_offset:
- return 0644;
- case hwmon_temp_reset_history:
- return 0200;
- }
- break;
- case hwmon_in:
- switch (attr) {
- case hwmon_in_input:
- case hwmon_in_min:
- case hwmon_in_max:
- case hwmon_in_highest:
- return 0444;
- case hwmon_in_reset_history:
- return 0200;
- }
- break;
- case hwmon_curr:
- switch (attr) {
- case hwmon_curr_input:
- case hwmon_curr_min:
- case hwmon_curr_max:
- case hwmon_curr_highest:
- return 0444;
- case hwmon_curr_reset_history:
- return 0200;
- }
- break;
- case hwmon_fan:
- switch (attr) {
- case hwmon_fan_input:
- case hwmon_fan_min:
- case hwmon_fan_max:
- return 0444;
- }
- break;
- case hwmon_pwm:
- switch (attr) {
- case hwmon_pwm_input:
- case hwmon_pwm_enable:
- return 0644;
- }
- break;
- case hwmon_power:
- switch (attr) {
- case hwmon_power_input:
- case hwmon_power_input_highest:
- return 0444;
- case hwmon_power_reset_history:
- return 0200;
- }
- break;
- default:
- break;
- }
- return 0;
-}
-
-static const struct hwmon_ops hl_hwmon_ops = {
- .is_visible = hl_is_visible,
- .read = hl_read,
- .write = hl_write
-};
-
-int hl_get_temperature(struct hl_device *hdev,
- int sensor_index, u32 attr, long *value)
-{
- struct cpucp_packet pkt;
- u64 result;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_TEMPERATURE_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.sensor_index = __cpu_to_le16(sensor_index);
- pkt.type = __cpu_to_le16(attr);
-
- dev_dbg(hdev->dev, "get temp, ctl 0x%x, sensor %d, type %d\n",
- pkt.ctl, pkt.sensor_index, pkt.type);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, &result);
-
- *value = (long) result;
-
- if (rc) {
- dev_err(hdev->dev,
- "Failed to get temperature from sensor %d, error %d\n",
- sensor_index, rc);
- *value = 0;
- }
-
- return rc;
-}
-
-int hl_set_temperature(struct hl_device *hdev,
- int sensor_index, u32 attr, long value)
-{
- struct cpucp_packet pkt;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_TEMPERATURE_SET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.sensor_index = __cpu_to_le16(sensor_index);
- pkt.type = __cpu_to_le16(attr);
- pkt.value = __cpu_to_le64(value);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, NULL);
-
- if (rc)
- dev_err(hdev->dev,
- "Failed to set temperature of sensor %d, error %d\n",
- sensor_index, rc);
-
- return rc;
-}
-
-int hl_get_voltage(struct hl_device *hdev,
- int sensor_index, u32 attr, long *value)
-{
- struct cpucp_packet pkt;
- u64 result;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_VOLTAGE_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.sensor_index = __cpu_to_le16(sensor_index);
- pkt.type = __cpu_to_le16(attr);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, &result);
-
- *value = (long) result;
-
- if (rc) {
- dev_err(hdev->dev,
- "Failed to get voltage from sensor %d, error %d\n",
- sensor_index, rc);
- *value = 0;
- }
-
- return rc;
-}
-
-int hl_get_current(struct hl_device *hdev,
- int sensor_index, u32 attr, long *value)
-{
- struct cpucp_packet pkt;
- u64 result;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_CURRENT_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.sensor_index = __cpu_to_le16(sensor_index);
- pkt.type = __cpu_to_le16(attr);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, &result);
-
- *value = (long) result;
-
- if (rc) {
- dev_err(hdev->dev,
- "Failed to get current from sensor %d, error %d\n",
- sensor_index, rc);
- *value = 0;
- }
-
- return rc;
-}
-
-int hl_get_fan_speed(struct hl_device *hdev,
- int sensor_index, u32 attr, long *value)
-{
- struct cpucp_packet pkt;
- u64 result;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_FAN_SPEED_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.sensor_index = __cpu_to_le16(sensor_index);
- pkt.type = __cpu_to_le16(attr);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, &result);
-
- *value = (long) result;
-
- if (rc) {
- dev_err(hdev->dev,
- "Failed to get fan speed from sensor %d, error %d\n",
- sensor_index, rc);
- *value = 0;
- }
-
- return rc;
-}
-
-int hl_get_pwm_info(struct hl_device *hdev,
- int sensor_index, u32 attr, long *value)
-{
- struct cpucp_packet pkt;
- u64 result;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_PWM_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.sensor_index = __cpu_to_le16(sensor_index);
- pkt.type = __cpu_to_le16(attr);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, &result);
-
- *value = (long) result;
-
- if (rc) {
- dev_err(hdev->dev,
- "Failed to get pwm info from sensor %d, error %d\n",
- sensor_index, rc);
- *value = 0;
- }
-
- return rc;
-}
-
-void hl_set_pwm_info(struct hl_device *hdev, int sensor_index, u32 attr,
- long value)
-{
- struct cpucp_packet pkt;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_PWM_SET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.sensor_index = __cpu_to_le16(sensor_index);
- pkt.type = __cpu_to_le16(attr);
- pkt.value = cpu_to_le64(value);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, NULL);
-
- if (rc)
- dev_err(hdev->dev,
- "Failed to set pwm info to sensor %d, error %d\n",
- sensor_index, rc);
-}
-
-int hl_set_voltage(struct hl_device *hdev,
- int sensor_index, u32 attr, long value)
-{
- struct cpucp_packet pkt;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_VOLTAGE_SET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.sensor_index = __cpu_to_le16(sensor_index);
- pkt.type = __cpu_to_le16(attr);
- pkt.value = __cpu_to_le64(value);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, NULL);
-
- if (rc)
- dev_err(hdev->dev,
- "Failed to set voltage of sensor %d, error %d\n",
- sensor_index, rc);
-
- return rc;
-}
-
-int hl_set_current(struct hl_device *hdev,
- int sensor_index, u32 attr, long value)
-{
- struct cpucp_packet pkt;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_CURRENT_SET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.sensor_index = __cpu_to_le16(sensor_index);
- pkt.type = __cpu_to_le16(attr);
- pkt.value = __cpu_to_le64(value);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, NULL);
-
- if (rc)
- dev_err(hdev->dev,
- "Failed to set current of sensor %d, error %d\n",
- sensor_index, rc);
-
- return rc;
-}
-
-int hl_set_power(struct hl_device *hdev,
- int sensor_index, u32 attr, long value)
-{
- struct cpucp_packet pkt;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- if (prop->use_get_power_for_reset_history)
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_POWER_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- else
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_POWER_SET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
-
- pkt.sensor_index = __cpu_to_le16(sensor_index);
- pkt.type = __cpu_to_le16(attr);
- pkt.value = __cpu_to_le64(value);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, NULL);
-
- if (rc)
- dev_err(hdev->dev,
- "Failed to set power of sensor %d, error %d\n",
- sensor_index, rc);
-
- return rc;
-}
-
-int hl_get_power(struct hl_device *hdev,
- int sensor_index, u32 attr, long *value)
-{
- struct cpucp_packet pkt;
- u64 result;
- int rc;
-
- memset(&pkt, 0, sizeof(pkt));
-
- pkt.ctl = cpu_to_le32(CPUCP_PACKET_POWER_GET <<
- CPUCP_PKT_CTL_OPCODE_SHIFT);
- pkt.sensor_index = __cpu_to_le16(sensor_index);
- pkt.type = __cpu_to_le16(attr);
-
- rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
- 0, &result);
-
- *value = (long) result;
-
- if (rc) {
- dev_err(hdev->dev,
- "Failed to get power of sensor %d, error %d\n",
- sensor_index, rc);
- *value = 0;
- }
-
- return rc;
-}
-
-int hl_hwmon_init(struct hl_device *hdev)
-{
- struct device *dev = hdev->pdev ? &hdev->pdev->dev : hdev->dev;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- int rc;
-
- if ((hdev->hwmon_initialized) || !(hdev->cpu_queues_enable))
- return 0;
-
- if (hdev->hl_chip_info->info) {
- hdev->hl_chip_info->ops = &hl_hwmon_ops;
-
- hdev->hwmon_dev = hwmon_device_register_with_info(dev,
- prop->cpucp_info.card_name, hdev,
- hdev->hl_chip_info, NULL);
- if (IS_ERR(hdev->hwmon_dev)) {
- rc = PTR_ERR(hdev->hwmon_dev);
- dev_err(hdev->dev,
- "Unable to register hwmon device: %d\n", rc);
- return rc;
- }
-
- dev_info(hdev->dev, "%s: add sensors information\n",
- dev_name(hdev->hwmon_dev));
-
- hdev->hwmon_initialized = true;
- } else {
- dev_info(hdev->dev, "no available sensors\n");
- }
-
- return 0;
-}
-
-void hl_hwmon_fini(struct hl_device *hdev)
-{
- if (!hdev->hwmon_initialized)
- return;
-
- hwmon_device_unregister(hdev->hwmon_dev);
-}
-
-void hl_hwmon_release_resources(struct hl_device *hdev)
-{
- const struct hwmon_channel_info **channel_info_arr;
- int i = 0;
-
- if (!hdev->hl_chip_info->info)
- return;
-
- channel_info_arr = hdev->hl_chip_info->info;
-
- while (channel_info_arr[i]) {
- kfree(channel_info_arr[i]->config);
- kfree(channel_info_arr[i]);
- i++;
- }
-
- kfree(channel_info_arr);
-
- hdev->hl_chip_info->info = NULL;
-}
diff --git a/drivers/misc/habanalabs/common/irq.c b/drivers/misc/habanalabs/common/irq.c
deleted file mode 100644
index 8bbcc223df91..000000000000
--- a/drivers/misc/habanalabs/common/irq.c
+++ /dev/null
@@ -1,571 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2022 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include "habanalabs.h"
-
-#include <linux/slab.h>
-
-/**
- * struct hl_eqe_work - This structure is used to schedule work of EQ
- * entry and cpucp_reset event
- *
- * @eq_work: workqueue object to run when EQ entry is received
- * @hdev: pointer to device structure
- * @eq_entry: copy of the EQ entry
- */
-struct hl_eqe_work {
- struct work_struct eq_work;
- struct hl_device *hdev;
- struct hl_eq_entry eq_entry;
-};
-
-/**
- * hl_cq_inc_ptr - increment ci or pi of cq
- *
- * @ptr: the current ci or pi value of the completion queue
- *
- * Increment ptr by 1. If it reaches the number of completion queue
- * entries, set it to 0
- */
-inline u32 hl_cq_inc_ptr(u32 ptr)
-{
- ptr++;
- if (unlikely(ptr == HL_CQ_LENGTH))
- ptr = 0;
- return ptr;
-}
-
-/**
- * hl_eq_inc_ptr - increment ci of eq
- *
- * @ptr: the current ci value of the event queue
- *
- * Increment ptr by 1. If it reaches the number of event queue
- * entries, set it to 0
- */
-static inline u32 hl_eq_inc_ptr(u32 ptr)
-{
- ptr++;
- if (unlikely(ptr == HL_EQ_LENGTH))
- ptr = 0;
- return ptr;
-}
-
-static void irq_handle_eqe(struct work_struct *work)
-{
- struct hl_eqe_work *eqe_work = container_of(work, struct hl_eqe_work,
- eq_work);
- struct hl_device *hdev = eqe_work->hdev;
-
- hdev->asic_funcs->handle_eqe(hdev, &eqe_work->eq_entry);
-
- kfree(eqe_work);
-}
-
-/**
- * job_finish - queue job finish work
- *
- * @hdev: pointer to device structure
- * @cs_seq: command submission sequence
- * @cq: completion queue
- *
- */
-static void job_finish(struct hl_device *hdev, u32 cs_seq, struct hl_cq *cq)
-{
- struct hl_hw_queue *queue;
- struct hl_cs_job *job;
-
- queue = &hdev->kernel_queues[cq->hw_queue_id];
- job = queue->shadow_queue[hl_pi_2_offset(cs_seq)];
- queue_work(hdev->cq_wq[cq->cq_idx], &job->finish_work);
-
- atomic_inc(&queue->ci);
-}
-
-/**
- * cs_finish - queue all cs jobs finish work
- *
- * @hdev: pointer to device structure
- * @cs_seq: command submission sequence
- *
- */
-static void cs_finish(struct hl_device *hdev, u16 cs_seq)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct hl_hw_queue *queue;
- struct hl_cs *cs;
- struct hl_cs_job *job;
-
- cs = hdev->shadow_cs_queue[cs_seq & (prop->max_pending_cs - 1)];
- if (!cs) {
- dev_warn(hdev->dev,
- "No pointer to CS in shadow array at index %d\n",
- cs_seq);
- return;
- }
-
- list_for_each_entry(job, &cs->job_list, cs_node) {
- queue = &hdev->kernel_queues[job->hw_queue_id];
- atomic_inc(&queue->ci);
- }
-
- queue_work(hdev->cs_cmplt_wq, &cs->finish_work);
-}
-
-/**
- * hl_irq_handler_cq - irq handler for completion queue
- *
- * @irq: irq number
- * @arg: pointer to completion queue structure
- *
- */
-irqreturn_t hl_irq_handler_cq(int irq, void *arg)
-{
- struct hl_cq *cq = arg;
- struct hl_device *hdev = cq->hdev;
- bool shadow_index_valid, entry_ready;
- u16 shadow_index;
- struct hl_cq_entry *cq_entry, *cq_base;
-
- if (hdev->disabled) {
- dev_dbg(hdev->dev,
- "Device disabled but received IRQ %d for CQ %d\n",
- irq, cq->hw_queue_id);
- return IRQ_HANDLED;
- }
-
- cq_base = cq->kernel_address;
-
- while (1) {
- cq_entry = (struct hl_cq_entry *) &cq_base[cq->ci];
-
- entry_ready = !!FIELD_GET(CQ_ENTRY_READY_MASK,
- le32_to_cpu(cq_entry->data));
- if (!entry_ready)
- break;
-
- /* Make sure we read CQ entry contents after we've
- * checked the ownership bit.
- */
- dma_rmb();
-
- shadow_index_valid =
- !!FIELD_GET(CQ_ENTRY_SHADOW_INDEX_VALID_MASK,
- le32_to_cpu(cq_entry->data));
-
- shadow_index = FIELD_GET(CQ_ENTRY_SHADOW_INDEX_MASK,
- le32_to_cpu(cq_entry->data));
-
- /*
- * CQ interrupt handler has 2 modes of operation:
- * 1. Interrupt per CS completion: (Single CQ for all queues)
- * CQ entry represents a completed CS
- *
- * 2. Interrupt per CS job completion in queue: (CQ per queue)
- * CQ entry represents a completed job in a certain queue
- */
- if (shadow_index_valid && !hdev->disabled) {
- if (hdev->asic_prop.completion_mode ==
- HL_COMPLETION_MODE_CS)
- cs_finish(hdev, shadow_index);
- else
- job_finish(hdev, shadow_index, cq);
- }
-
- /* Clear CQ entry ready bit */
- cq_entry->data = cpu_to_le32(le32_to_cpu(cq_entry->data) &
- ~CQ_ENTRY_READY_MASK);
-
- cq->ci = hl_cq_inc_ptr(cq->ci);
-
- /* Increment free slots */
- atomic_inc(&cq->free_slots_cnt);
- }
-
- return IRQ_HANDLED;
-}
-
-/*
- * hl_ts_free_objects - handler of the free objects workqueue.
- * This function should put refcount to objects that the registration node
- * took refcount to them.
- * @work: workqueue object pointer
- */
-static void hl_ts_free_objects(struct work_struct *work)
-{
- struct timestamp_reg_work_obj *job =
- container_of(work, struct timestamp_reg_work_obj, free_obj);
- struct timestamp_reg_free_node *free_obj, *temp_free_obj;
- struct list_head *free_list_head = job->free_obj_head;
- struct hl_device *hdev = job->hdev;
-
- list_for_each_entry_safe(free_obj, temp_free_obj, free_list_head, free_objects_node) {
- dev_dbg(hdev->dev, "About to put refcount to buf (%p) cq_cb(%p)\n",
- free_obj->buf,
- free_obj->cq_cb);
-
- hl_mmap_mem_buf_put(free_obj->buf);
- hl_cb_put(free_obj->cq_cb);
- kfree(free_obj);
- }
-
- kfree(free_list_head);
- kfree(job);
-}
-
-/*
- * This function called with spin_lock of wait_list_lock taken
- * This function will set timestamp and delete the registration node from the
- * wait_list_lock.
- * and since we're protected with spin_lock here, so we cannot just put the refcount
- * for the objects here, since the release function may be called and it's also a long
- * logic (which might sleep also) that cannot be handled in irq context.
- * so here we'll be filling a list with nodes of "put" jobs and then will send this
- * list to a dedicated workqueue to do the actual put.
- */
-static int handle_registration_node(struct hl_device *hdev, struct hl_user_pending_interrupt *pend,
- struct list_head **free_list, ktime_t now)
-{
- struct timestamp_reg_free_node *free_node;
- u64 timestamp;
-
- if (!(*free_list)) {
- /* Alloc/Init the timestamp registration free objects list */
- *free_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);
- if (!(*free_list))
- return -ENOMEM;
-
- INIT_LIST_HEAD(*free_list);
- }
-
- free_node = kmalloc(sizeof(*free_node), GFP_ATOMIC);
- if (!free_node)
- return -ENOMEM;
-
- timestamp = ktime_to_ns(now);
-
- *pend->ts_reg_info.timestamp_kernel_addr = timestamp;
-
- dev_dbg(hdev->dev, "Timestamp is set to ts cb address (%p), ts: 0x%llx\n",
- pend->ts_reg_info.timestamp_kernel_addr,
- *(u64 *)pend->ts_reg_info.timestamp_kernel_addr);
-
- list_del(&pend->wait_list_node);
-
- /* Mark kernel CB node as free */
- pend->ts_reg_info.in_use = 0;
-
- /* Putting the refcount for ts_buff and cq_cb objects will be handled
- * in workqueue context, just add job to free_list.
- */
- free_node->buf = pend->ts_reg_info.buf;
- free_node->cq_cb = pend->ts_reg_info.cq_cb;
- list_add(&free_node->free_objects_node, *free_list);
-
- return 0;
-}
-
-static void handle_user_interrupt(struct hl_device *hdev, struct hl_user_interrupt *intr)
-{
- struct hl_user_pending_interrupt *pend, *temp_pend;
- struct list_head *ts_reg_free_list_head = NULL;
- struct timestamp_reg_work_obj *job;
- bool reg_node_handle_fail = false;
- ktime_t now = ktime_get();
- int rc;
-
- /* For registration nodes:
- * As part of handling the registration nodes, we should put refcount to
- * some objects. the problem is that we cannot do that under spinlock
- * or in irq handler context at all (since release functions are long and
- * might sleep), so we will need to handle that part in workqueue context.
- * To avoid handling kmalloc failure which compels us rolling back actions
- * and move nodes hanged on the free list back to the interrupt wait list
- * we always alloc the job of the WQ at the beginning.
- */
- job = kmalloc(sizeof(*job), GFP_ATOMIC);
- if (!job)
- return;
-
- spin_lock(&intr->wait_list_lock);
- list_for_each_entry_safe(pend, temp_pend, &intr->wait_list_head, wait_list_node) {
- if ((pend->cq_kernel_addr && *(pend->cq_kernel_addr) >= pend->cq_target_value) ||
- !pend->cq_kernel_addr) {
- if (pend->ts_reg_info.buf) {
- if (!reg_node_handle_fail) {
- rc = handle_registration_node(hdev, pend,
- &ts_reg_free_list_head, now);
- if (rc)
- reg_node_handle_fail = true;
- }
- } else {
- /* Handle wait target value node */
- pend->fence.timestamp = now;
- complete_all(&pend->fence.completion);
- }
- }
- }
- spin_unlock(&intr->wait_list_lock);
-
- if (ts_reg_free_list_head) {
- INIT_WORK(&job->free_obj, hl_ts_free_objects);
- job->free_obj_head = ts_reg_free_list_head;
- job->hdev = hdev;
- queue_work(hdev->ts_free_obj_wq, &job->free_obj);
- } else {
- kfree(job);
- }
-}
-
-/**
- * hl_irq_handler_user_interrupt - irq handler for user interrupts
- *
- * @irq: irq number
- * @arg: pointer to user interrupt structure
- *
- */
-irqreturn_t hl_irq_handler_user_interrupt(int irq, void *arg)
-{
- struct hl_user_interrupt *user_int = arg;
- struct hl_device *hdev = user_int->hdev;
-
- if (user_int->is_decoder)
- handle_user_interrupt(hdev, &hdev->common_decoder_interrupt);
- else
- handle_user_interrupt(hdev, &hdev->common_user_cq_interrupt);
-
- /* Handle user cq or decoder interrupts registered on this specific irq */
- handle_user_interrupt(hdev, user_int);
-
- return IRQ_HANDLED;
-}
-
-/**
- * hl_irq_handler_default - default irq handler
- *
- * @irq: irq number
- * @arg: pointer to user interrupt structure
- *
- */
-irqreturn_t hl_irq_handler_default(int irq, void *arg)
-{
- struct hl_user_interrupt *user_interrupt = arg;
- struct hl_device *hdev = user_interrupt->hdev;
- u32 interrupt_id = user_interrupt->interrupt_id;
-
- dev_err(hdev->dev, "got invalid user interrupt %u", interrupt_id);
-
- return IRQ_HANDLED;
-}
-
-/**
- * hl_irq_handler_eq - irq handler for event queue
- *
- * @irq: irq number
- * @arg: pointer to event queue structure
- *
- */
-irqreturn_t hl_irq_handler_eq(int irq, void *arg)
-{
- struct hl_eq *eq = arg;
- struct hl_device *hdev = eq->hdev;
- struct hl_eq_entry *eq_entry;
- struct hl_eq_entry *eq_base;
- struct hl_eqe_work *handle_eqe_work;
- bool entry_ready;
- u32 cur_eqe;
- u16 cur_eqe_index;
-
- eq_base = eq->kernel_address;
-
- while (1) {
- cur_eqe = le32_to_cpu(eq_base[eq->ci].hdr.ctl);
- entry_ready = !!FIELD_GET(EQ_CTL_READY_MASK, cur_eqe);
-
- if (!entry_ready)
- break;
-
- cur_eqe_index = FIELD_GET(EQ_CTL_INDEX_MASK, cur_eqe);
- if ((hdev->event_queue.check_eqe_index) &&
- (((eq->prev_eqe_index + 1) & EQ_CTL_INDEX_MASK)
- != cur_eqe_index)) {
- dev_dbg(hdev->dev,
- "EQE 0x%x in queue is ready but index does not match %d!=%d",
- eq_base[eq->ci].hdr.ctl,
- ((eq->prev_eqe_index + 1) & EQ_CTL_INDEX_MASK),
- cur_eqe_index);
- break;
- }
-
- eq->prev_eqe_index++;
-
- eq_entry = &eq_base[eq->ci];
-
- /*
- * Make sure we read EQ entry contents after we've
- * checked the ownership bit.
- */
- dma_rmb();
-
- if (hdev->disabled && !hdev->reset_info.in_compute_reset) {
- dev_warn(hdev->dev, "Device disabled but received an EQ event\n");
- goto skip_irq;
- }
-
- handle_eqe_work = kmalloc(sizeof(*handle_eqe_work), GFP_ATOMIC);
- if (handle_eqe_work) {
- INIT_WORK(&handle_eqe_work->eq_work, irq_handle_eqe);
- handle_eqe_work->hdev = hdev;
-
- memcpy(&handle_eqe_work->eq_entry, eq_entry,
- sizeof(*eq_entry));
-
- queue_work(hdev->eq_wq, &handle_eqe_work->eq_work);
- }
-skip_irq:
- /* Clear EQ entry ready bit */
- eq_entry->hdr.ctl =
- cpu_to_le32(le32_to_cpu(eq_entry->hdr.ctl) &
- ~EQ_CTL_READY_MASK);
-
- eq->ci = hl_eq_inc_ptr(eq->ci);
-
- hdev->asic_funcs->update_eq_ci(hdev, eq->ci);
- }
-
- return IRQ_HANDLED;
-}
-
-/**
- * hl_irq_handler_dec_abnrm - Decoder error interrupt handler
- * @irq: IRQ number
- * @arg: pointer to decoder structure.
- */
-irqreturn_t hl_irq_handler_dec_abnrm(int irq, void *arg)
-{
- struct hl_dec *dec = arg;
-
- schedule_work(&dec->completion_abnrm_work);
-
- return IRQ_HANDLED;
-}
-
-/**
- * hl_cq_init - main initialization function for an cq object
- *
- * @hdev: pointer to device structure
- * @q: pointer to cq structure
- * @hw_queue_id: The H/W queue ID this completion queue belongs to
- * HL_INVALID_QUEUE if cq is not attached to any specific queue
- *
- * Allocate dma-able memory for the completion queue and initialize fields
- * Returns 0 on success
- */
-int hl_cq_init(struct hl_device *hdev, struct hl_cq *q, u32 hw_queue_id)
-{
- void *p;
-
- p = hl_asic_dma_alloc_coherent(hdev, HL_CQ_SIZE_IN_BYTES, &q->bus_address,
- GFP_KERNEL | __GFP_ZERO);
- if (!p)
- return -ENOMEM;
-
- q->hdev = hdev;
- q->kernel_address = p;
- q->hw_queue_id = hw_queue_id;
- q->ci = 0;
- q->pi = 0;
-
- atomic_set(&q->free_slots_cnt, HL_CQ_LENGTH);
-
- return 0;
-}
-
-/**
- * hl_cq_fini - destroy completion queue
- *
- * @hdev: pointer to device structure
- * @q: pointer to cq structure
- *
- * Free the completion queue memory
- */
-void hl_cq_fini(struct hl_device *hdev, struct hl_cq *q)
-{
- hl_asic_dma_free_coherent(hdev, HL_CQ_SIZE_IN_BYTES, q->kernel_address, q->bus_address);
-}
-
-void hl_cq_reset(struct hl_device *hdev, struct hl_cq *q)
-{
- q->ci = 0;
- q->pi = 0;
-
- atomic_set(&q->free_slots_cnt, HL_CQ_LENGTH);
-
- /*
- * It's not enough to just reset the PI/CI because the H/W may have
- * written valid completion entries before it was halted and therefore
- * we need to clean the actual queues so we won't process old entries
- * when the device is operational again
- */
-
- memset(q->kernel_address, 0, HL_CQ_SIZE_IN_BYTES);
-}
-
-/**
- * hl_eq_init - main initialization function for an event queue object
- *
- * @hdev: pointer to device structure
- * @q: pointer to eq structure
- *
- * Allocate dma-able memory for the event queue and initialize fields
- * Returns 0 on success
- */
-int hl_eq_init(struct hl_device *hdev, struct hl_eq *q)
-{
- void *p;
-
- p = hl_cpu_accessible_dma_pool_alloc(hdev, HL_EQ_SIZE_IN_BYTES, &q->bus_address);
- if (!p)
- return -ENOMEM;
-
- q->hdev = hdev;
- q->kernel_address = p;
- q->ci = 0;
- q->prev_eqe_index = 0;
-
- return 0;
-}
-
-/**
- * hl_eq_fini - destroy event queue
- *
- * @hdev: pointer to device structure
- * @q: pointer to eq structure
- *
- * Free the event queue memory
- */
-void hl_eq_fini(struct hl_device *hdev, struct hl_eq *q)
-{
- flush_workqueue(hdev->eq_wq);
-
- hl_cpu_accessible_dma_pool_free(hdev, HL_EQ_SIZE_IN_BYTES, q->kernel_address);
-}
-
-void hl_eq_reset(struct hl_device *hdev, struct hl_eq *q)
-{
- q->ci = 0;
- q->prev_eqe_index = 0;
-
- /*
- * It's not enough to just reset the PI/CI because the H/W may have
- * written valid completion entries before it was halted and therefore
- * we need to clean the actual queues so we won't process old entries
- * when the device is operational again
- */
-
- memset(q->kernel_address, 0, HL_EQ_SIZE_IN_BYTES);
-}
diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c
deleted file mode 100644
index 1c38fab39337..000000000000
--- a/drivers/misc/habanalabs/common/memory.c
+++ /dev/null
@@ -1,3002 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2022 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include <uapi/drm/habanalabs_accel.h>
-#include "habanalabs.h"
-#include "../include/hw_ip/mmu/mmu_general.h"
-
-#include <linux/uaccess.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/pci-p2pdma.h>
-
-MODULE_IMPORT_NS(DMA_BUF);
-
-#define HL_MMU_DEBUG 0
-
-/* use small pages for supporting non-pow2 (32M/40M/48M) DRAM phys page sizes */
-#define DRAM_POOL_PAGE_SIZE SZ_8M
-
-#define MEM_HANDLE_INVALID ULONG_MAX
-
-static int allocate_timestamps_buffers(struct hl_fpriv *hpriv,
- struct hl_mem_in *args, u64 *handle);
-
-static int set_alloc_page_size(struct hl_device *hdev, struct hl_mem_in *args, u32 *page_size)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u64 psize;
-
- /*
- * for ASIC that supports setting the allocation page size by user we will address
- * user's choice only if it is not 0 (as 0 means taking the default page size)
- */
- if (prop->supports_user_set_page_size && args->alloc.page_size) {
- psize = args->alloc.page_size;
-
- if (!is_power_of_2(psize)) {
- dev_err(hdev->dev, "user page size (%#llx) is not power of 2\n", psize);
- return -EINVAL;
- }
- } else {
- psize = prop->device_mem_alloc_default_page_size;
- }
-
- *page_size = psize;
-
- return 0;
-}
-
-/*
- * The va ranges in context object contain a list with the available chunks of
- * device virtual memory.
- * There is one range for host allocations and one for DRAM allocations.
- *
- * On initialization each range contains one chunk of all of its available
- * virtual range which is a half of the total device virtual range.
- *
- * On each mapping of physical pages, a suitable virtual range chunk (with a
- * minimum size) is selected from the list. If the chunk size equals the
- * requested size, the chunk is returned. Otherwise, the chunk is split into
- * two chunks - one to return as result and a remainder to stay in the list.
- *
- * On each Unmapping of a virtual address, the relevant virtual chunk is
- * returned to the list. The chunk is added to the list and if its edges match
- * the edges of the adjacent chunks (means a contiguous chunk can be created),
- * the chunks are merged.
- *
- * On finish, the list is checked to have only one chunk of all the relevant
- * virtual range (which is a half of the device total virtual range).
- * If not (means not all mappings were unmapped), a warning is printed.
- */
-
-/*
- * alloc_device_memory() - allocate device memory.
- * @ctx: pointer to the context structure.
- * @args: host parameters containing the requested size.
- * @ret_handle: result handle.
- *
- * This function does the following:
- * - Allocate the requested size rounded up to 'dram_page_size' pages.
- * - Return unique handle for later map/unmap/free.
- */
-static int alloc_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args,
- u32 *ret_handle)
-{
- struct hl_device *hdev = ctx->hdev;
- struct hl_vm *vm = &hdev->vm;
- struct hl_vm_phys_pg_pack *phys_pg_pack;
- u64 paddr = 0, total_size, num_pgs, i;
- u32 num_curr_pgs, page_size;
- bool contiguous;
- int handle, rc;
-
- num_curr_pgs = 0;
-
- rc = set_alloc_page_size(hdev, args, &page_size);
- if (rc)
- return rc;
-
- num_pgs = DIV_ROUND_UP_ULL(args->alloc.mem_size, page_size);
- total_size = num_pgs * page_size;
-
- if (!total_size) {
- dev_err(hdev->dev, "Cannot allocate 0 bytes\n");
- return -EINVAL;
- }
-
- contiguous = args->flags & HL_MEM_CONTIGUOUS;
-
- if (contiguous) {
- if (is_power_of_2(page_size))
- paddr = (uintptr_t) gen_pool_dma_alloc_align(vm->dram_pg_pool,
- total_size, NULL, page_size);
- else
- paddr = gen_pool_alloc(vm->dram_pg_pool, total_size);
- if (!paddr) {
- dev_err(hdev->dev,
- "Cannot allocate %llu contiguous pages with total size of %llu\n",
- num_pgs, total_size);
- return -ENOMEM;
- }
- }
-
- phys_pg_pack = kzalloc(sizeof(*phys_pg_pack), GFP_KERNEL);
- if (!phys_pg_pack) {
- rc = -ENOMEM;
- goto pages_pack_err;
- }
-
- phys_pg_pack->vm_type = VM_TYPE_PHYS_PACK;
- phys_pg_pack->asid = ctx->asid;
- phys_pg_pack->npages = num_pgs;
- phys_pg_pack->page_size = page_size;
- phys_pg_pack->total_size = total_size;
- phys_pg_pack->flags = args->flags;
- phys_pg_pack->contiguous = contiguous;
-
- phys_pg_pack->pages = kvmalloc_array(num_pgs, sizeof(u64), GFP_KERNEL);
- if (ZERO_OR_NULL_PTR(phys_pg_pack->pages)) {
- rc = -ENOMEM;
- goto pages_arr_err;
- }
-
- if (phys_pg_pack->contiguous) {
- for (i = 0 ; i < num_pgs ; i++)
- phys_pg_pack->pages[i] = paddr + i * page_size;
- } else {
- for (i = 0 ; i < num_pgs ; i++) {
- if (is_power_of_2(page_size))
- phys_pg_pack->pages[i] =
- (uintptr_t)gen_pool_dma_alloc_align(vm->dram_pg_pool,
- page_size, NULL,
- page_size);
- else
- phys_pg_pack->pages[i] = gen_pool_alloc(vm->dram_pg_pool,
- page_size);
-
- if (!phys_pg_pack->pages[i]) {
- dev_err(hdev->dev,
- "Cannot allocate device memory (out of memory)\n");
- rc = -ENOMEM;
- goto page_err;
- }
-
- num_curr_pgs++;
- }
- }
-
- spin_lock(&vm->idr_lock);
- handle = idr_alloc(&vm->phys_pg_pack_handles, phys_pg_pack, 1, 0,
- GFP_ATOMIC);
- spin_unlock(&vm->idr_lock);
-
- if (handle < 0) {
- dev_err(hdev->dev, "Failed to get handle for page\n");
- rc = -EFAULT;
- goto idr_err;
- }
-
- for (i = 0 ; i < num_pgs ; i++)
- kref_get(&vm->dram_pg_pool_refcount);
-
- phys_pg_pack->handle = handle;
-
- atomic64_add(phys_pg_pack->total_size, &ctx->dram_phys_mem);
- atomic64_add(phys_pg_pack->total_size, &hdev->dram_used_mem);
-
- *ret_handle = handle;
-
- return 0;
-
-idr_err:
-page_err:
- if (!phys_pg_pack->contiguous)
- for (i = 0 ; i < num_curr_pgs ; i++)
- gen_pool_free(vm->dram_pg_pool, phys_pg_pack->pages[i],
- page_size);
-
- kvfree(phys_pg_pack->pages);
-pages_arr_err:
- kfree(phys_pg_pack);
-pages_pack_err:
- if (contiguous)
- gen_pool_free(vm->dram_pg_pool, paddr, total_size);
-
- return rc;
-}
-
-/**
- * dma_map_host_va() - DMA mapping of the given host virtual address.
- * @hdev: habanalabs device structure.
- * @addr: the host virtual address of the memory area.
- * @size: the size of the memory area.
- * @p_userptr: pointer to result userptr structure.
- *
- * This function does the following:
- * - Allocate userptr structure.
- * - Pin the given host memory using the userptr structure.
- * - Perform DMA mapping to have the DMA addresses of the pages.
- */
-static int dma_map_host_va(struct hl_device *hdev, u64 addr, u64 size,
- struct hl_userptr **p_userptr)
-{
- struct hl_userptr *userptr;
- int rc;
-
- userptr = kzalloc(sizeof(*userptr), GFP_KERNEL);
- if (!userptr) {
- rc = -ENOMEM;
- goto userptr_err;
- }
-
- rc = hl_pin_host_memory(hdev, addr, size, userptr);
- if (rc) {
- dev_err(hdev->dev, "Failed to pin host memory\n");
- goto pin_err;
- }
-
- userptr->dma_mapped = true;
- userptr->dir = DMA_BIDIRECTIONAL;
- userptr->vm_type = VM_TYPE_USERPTR;
-
- *p_userptr = userptr;
-
- rc = hdev->asic_funcs->asic_dma_map_sgtable(hdev, userptr->sgt, DMA_BIDIRECTIONAL);
- if (rc) {
- dev_err(hdev->dev, "failed to map sgt with DMA region\n");
- goto dma_map_err;
- }
-
- return 0;
-
-dma_map_err:
- hl_unpin_host_memory(hdev, userptr);
-pin_err:
- kfree(userptr);
-userptr_err:
-
- return rc;
-}
-
-/**
- * dma_unmap_host_va() - DMA unmapping of the given host virtual address.
- * @hdev: habanalabs device structure.
- * @userptr: userptr to free.
- *
- * This function does the following:
- * - Unpins the physical pages.
- * - Frees the userptr structure.
- */
-static void dma_unmap_host_va(struct hl_device *hdev,
- struct hl_userptr *userptr)
-{
- hl_unpin_host_memory(hdev, userptr);
- kfree(userptr);
-}
-
-/**
- * dram_pg_pool_do_release() - free DRAM pages pool
- * @ref: pointer to reference object.
- *
- * This function does the following:
- * - Frees the idr structure of physical pages handles.
- * - Frees the generic pool of DRAM physical pages.
- */
-static void dram_pg_pool_do_release(struct kref *ref)
-{
- struct hl_vm *vm = container_of(ref, struct hl_vm,
- dram_pg_pool_refcount);
-
- /*
- * free the idr here as only here we know for sure that there are no
- * allocated physical pages and hence there are no handles in use
- */
- idr_destroy(&vm->phys_pg_pack_handles);
- gen_pool_destroy(vm->dram_pg_pool);
-}
-
-/**
- * free_phys_pg_pack() - free physical page pack.
- * @hdev: habanalabs device structure.
- * @phys_pg_pack: physical page pack to free.
- *
- * This function does the following:
- * - For DRAM memory only
- * - iterate over the pack, free each physical block structure by
- * returning it to the general pool.
- * - Free the hl_vm_phys_pg_pack structure.
- */
-static void free_phys_pg_pack(struct hl_device *hdev,
- struct hl_vm_phys_pg_pack *phys_pg_pack)
-{
- struct hl_vm *vm = &hdev->vm;
- u64 i;
-
- if (phys_pg_pack->created_from_userptr)
- goto end;
-
- if (phys_pg_pack->contiguous) {
- gen_pool_free(vm->dram_pg_pool, phys_pg_pack->pages[0],
- phys_pg_pack->total_size);
-
- for (i = 0; i < phys_pg_pack->npages ; i++)
- kref_put(&vm->dram_pg_pool_refcount,
- dram_pg_pool_do_release);
- } else {
- for (i = 0 ; i < phys_pg_pack->npages ; i++) {
- gen_pool_free(vm->dram_pg_pool,
- phys_pg_pack->pages[i],
- phys_pg_pack->page_size);
- kref_put(&vm->dram_pg_pool_refcount,
- dram_pg_pool_do_release);
- }
- }
-
-end:
- kvfree(phys_pg_pack->pages);
- kfree(phys_pg_pack);
-
- return;
-}
-
-/**
- * free_device_memory() - free device memory.
- * @ctx: pointer to the context structure.
- * @args: host parameters containing the requested size.
- *
- * This function does the following:
- * - Free the device memory related to the given handle.
- */
-static int free_device_memory(struct hl_ctx *ctx, struct hl_mem_in *args)
-{
- struct hl_device *hdev = ctx->hdev;
- struct hl_vm *vm = &hdev->vm;
- struct hl_vm_phys_pg_pack *phys_pg_pack;
- u32 handle = args->free.handle;
-
- spin_lock(&vm->idr_lock);
- phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, handle);
- if (!phys_pg_pack) {
- spin_unlock(&vm->idr_lock);
- dev_err(hdev->dev, "free device memory failed, no match for handle %u\n", handle);
- return -EINVAL;
- }
-
- if (atomic_read(&phys_pg_pack->mapping_cnt) > 0) {
- spin_unlock(&vm->idr_lock);
- dev_err(hdev->dev, "handle %u is mapped, cannot free\n", handle);
- return -EINVAL;
- }
-
- /* must remove from idr before the freeing of the physical pages as the refcount of the pool
- * is also the trigger of the idr destroy
- */
- idr_remove(&vm->phys_pg_pack_handles, handle);
- spin_unlock(&vm->idr_lock);
-
- atomic64_sub(phys_pg_pack->total_size, &ctx->dram_phys_mem);
- atomic64_sub(phys_pg_pack->total_size, &hdev->dram_used_mem);
-
- free_phys_pg_pack(hdev, phys_pg_pack);
-
- return 0;
-}
-
-/**
- * clear_va_list_locked() - free virtual addresses list.
- * @hdev: habanalabs device structure.
- * @va_list: list of virtual addresses to free.
- *
- * This function does the following:
- * - Iterate over the list and free each virtual addresses block.
- *
- * This function should be called only when va_list lock is taken.
- */
-static void clear_va_list_locked(struct hl_device *hdev,
- struct list_head *va_list)
-{
- struct hl_vm_va_block *va_block, *tmp;
-
- list_for_each_entry_safe(va_block, tmp, va_list, node) {
- list_del(&va_block->node);
- kfree(va_block);
- }
-}
-
-/**
- * print_va_list_locked() - print virtual addresses list.
- * @hdev: habanalabs device structure.
- * @va_list: list of virtual addresses to print.
- *
- * This function does the following:
- * - Iterate over the list and print each virtual addresses block.
- *
- * This function should be called only when va_list lock is taken.
- */
-static void print_va_list_locked(struct hl_device *hdev,
- struct list_head *va_list)
-{
-#if HL_MMU_DEBUG
- struct hl_vm_va_block *va_block;
-
- dev_dbg(hdev->dev, "print va list:\n");
-
- list_for_each_entry(va_block, va_list, node)
- dev_dbg(hdev->dev,
- "va block, start: 0x%llx, end: 0x%llx, size: %llu\n",
- va_block->start, va_block->end, va_block->size);
-#endif
-}
-
-/**
- * merge_va_blocks_locked() - merge a virtual block if possible.
- * @hdev: pointer to the habanalabs device structure.
- * @va_list: pointer to the virtual addresses block list.
- * @va_block: virtual block to merge with adjacent blocks.
- *
- * This function does the following:
- * - Merge the given blocks with the adjacent blocks if their virtual ranges
- * create a contiguous virtual range.
- *
- * This Function should be called only when va_list lock is taken.
- */
-static void merge_va_blocks_locked(struct hl_device *hdev,
- struct list_head *va_list, struct hl_vm_va_block *va_block)
-{
- struct hl_vm_va_block *prev, *next;
-
- prev = list_prev_entry(va_block, node);
- if (&prev->node != va_list && prev->end + 1 == va_block->start) {
- prev->end = va_block->end;
- prev->size = prev->end - prev->start + 1;
- list_del(&va_block->node);
- kfree(va_block);
- va_block = prev;
- }
-
- next = list_next_entry(va_block, node);
- if (&next->node != va_list && va_block->end + 1 == next->start) {
- next->start = va_block->start;
- next->size = next->end - next->start + 1;
- list_del(&va_block->node);
- kfree(va_block);
- }
-}
-
-/**
- * add_va_block_locked() - add a virtual block to the virtual addresses list.
- * @hdev: pointer to the habanalabs device structure.
- * @va_list: pointer to the virtual addresses block list.
- * @start: start virtual address.
- * @end: end virtual address.
- *
- * This function does the following:
- * - Add the given block to the virtual blocks list and merge with other blocks
- * if a contiguous virtual block can be created.
- *
- * This Function should be called only when va_list lock is taken.
- */
-static int add_va_block_locked(struct hl_device *hdev,
- struct list_head *va_list, u64 start, u64 end)
-{
- struct hl_vm_va_block *va_block, *res = NULL;
- u64 size = end - start + 1;
-
- print_va_list_locked(hdev, va_list);
-
- list_for_each_entry(va_block, va_list, node) {
- /* TODO: remove upon matureness */
- if (hl_mem_area_crosses_range(start, size, va_block->start,
- va_block->end)) {
- dev_err(hdev->dev,
- "block crossing ranges at start 0x%llx, end 0x%llx\n",
- va_block->start, va_block->end);
- return -EINVAL;
- }
-
- if (va_block->end < start)
- res = va_block;
- }
-
- va_block = kmalloc(sizeof(*va_block), GFP_KERNEL);
- if (!va_block)
- return -ENOMEM;
-
- va_block->start = start;
- va_block->end = end;
- va_block->size = size;
-
- if (!res)
- list_add(&va_block->node, va_list);
- else
- list_add(&va_block->node, &res->node);
-
- merge_va_blocks_locked(hdev, va_list, va_block);
-
- print_va_list_locked(hdev, va_list);
-
- return 0;
-}
-
-/**
- * add_va_block() - wrapper for add_va_block_locked.
- * @hdev: pointer to the habanalabs device structure.
- * @va_range: pointer to the virtual addresses range object.
- * @start: start virtual address.
- * @end: end virtual address.
- *
- * This function does the following:
- * - Takes the list lock and calls add_va_block_locked.
- */
-static inline int add_va_block(struct hl_device *hdev,
- struct hl_va_range *va_range, u64 start, u64 end)
-{
- int rc;
-
- mutex_lock(&va_range->lock);
- rc = add_va_block_locked(hdev, &va_range->list, start, end);
- mutex_unlock(&va_range->lock);
-
- return rc;
-}
-
-/**
- * is_hint_crossing_range() - check if hint address crossing specified reserved.
- * @range_type: virtual space range type.
- * @start_addr: start virtual address.
- * @size: block size.
- * @prop: asic properties structure to retrieve reserved ranges from.
- */
-static inline bool is_hint_crossing_range(enum hl_va_range_type range_type,
- u64 start_addr, u32 size, struct asic_fixed_properties *prop) {
- bool range_cross;
-
- if (range_type == HL_VA_RANGE_TYPE_DRAM)
- range_cross =
- hl_mem_area_crosses_range(start_addr, size,
- prop->hints_dram_reserved_va_range.start_addr,
- prop->hints_dram_reserved_va_range.end_addr);
- else if (range_type == HL_VA_RANGE_TYPE_HOST)
- range_cross =
- hl_mem_area_crosses_range(start_addr, size,
- prop->hints_host_reserved_va_range.start_addr,
- prop->hints_host_reserved_va_range.end_addr);
- else
- range_cross =
- hl_mem_area_crosses_range(start_addr, size,
- prop->hints_host_hpage_reserved_va_range.start_addr,
- prop->hints_host_hpage_reserved_va_range.end_addr);
-
- return range_cross;
-}
-
-/**
- * get_va_block() - get a virtual block for the given size and alignment.
- *
- * @hdev: pointer to the habanalabs device structure.
- * @va_range: pointer to the virtual addresses range.
- * @size: requested block size.
- * @hint_addr: hint for requested address by the user.
- * @va_block_align: required alignment of the virtual block start address.
- * @range_type: va range type (host, dram)
- * @flags: additional memory flags, currently only uses HL_MEM_FORCE_HINT
- *
- * This function does the following:
- * - Iterate on the virtual block list to find a suitable virtual block for the
- * given size, hint address and alignment.
- * - Reserve the requested block and update the list.
- * - Return the start address of the virtual block.
- */
-static u64 get_va_block(struct hl_device *hdev,
- struct hl_va_range *va_range,
- u64 size, u64 hint_addr, u32 va_block_align,
- enum hl_va_range_type range_type,
- u32 flags)
-{
- struct hl_vm_va_block *va_block, *new_va_block = NULL;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u64 tmp_hint_addr, valid_start, valid_size, prev_start, prev_end,
- align_mask, reserved_valid_start = 0, reserved_valid_size = 0,
- dram_hint_mask = prop->dram_hints_align_mask;
- bool add_prev = false;
- bool is_align_pow_2 = is_power_of_2(va_range->page_size);
- bool is_hint_dram_addr = hl_is_dram_va(hdev, hint_addr);
- bool force_hint = flags & HL_MEM_FORCE_HINT;
-
- if (is_align_pow_2)
- align_mask = ~((u64)va_block_align - 1);
- else
- /*
- * with non-power-of-2 range we work only with page granularity
- * and the start address is page aligned,
- * so no need for alignment checking.
- */
- size = DIV_ROUND_UP_ULL(size, va_range->page_size) *
- va_range->page_size;
-
- tmp_hint_addr = hint_addr & ~dram_hint_mask;
-
- /* Check if we need to ignore hint address */
- if ((is_align_pow_2 && (hint_addr & (va_block_align - 1))) ||
- (!is_align_pow_2 && is_hint_dram_addr &&
- do_div(tmp_hint_addr, va_range->page_size))) {
-
- if (force_hint) {
- /* Hint must be respected, so here we just fail */
- dev_err(hdev->dev,
- "Hint address 0x%llx is not page aligned - cannot be respected\n",
- hint_addr);
- return 0;
- }
-
- dev_dbg(hdev->dev,
- "Hint address 0x%llx will be ignored because it is not aligned\n",
- hint_addr);
- hint_addr = 0;
- }
-
- mutex_lock(&va_range->lock);
-
- print_va_list_locked(hdev, &va_range->list);
-
- list_for_each_entry(va_block, &va_range->list, node) {
- /* Calc the first possible aligned addr */
- valid_start = va_block->start;
-
- if (is_align_pow_2 && (valid_start & (va_block_align - 1))) {
- valid_start &= align_mask;
- valid_start += va_block_align;
- if (valid_start > va_block->end)
- continue;
- }
-
- valid_size = va_block->end - valid_start + 1;
- if (valid_size < size)
- continue;
-
- /*
- * In case hint address is 0, and hints_range_reservation
- * property enabled, then avoid allocating va blocks from the
- * range reserved for hint addresses
- */
- if (prop->hints_range_reservation && !hint_addr)
- if (is_hint_crossing_range(range_type, valid_start,
- size, prop))
- continue;
-
- /* Pick the minimal length block which has the required size */
- if (!new_va_block || (valid_size < reserved_valid_size)) {
- new_va_block = va_block;
- reserved_valid_start = valid_start;
- reserved_valid_size = valid_size;
- }
-
- if (hint_addr && hint_addr >= valid_start &&
- (hint_addr + size) <= va_block->end) {
- new_va_block = va_block;
- reserved_valid_start = hint_addr;
- reserved_valid_size = valid_size;
- break;
- }
- }
-
- if (!new_va_block) {
- dev_err(hdev->dev, "no available va block for size %llu\n",
- size);
- goto out;
- }
-
- if (force_hint && reserved_valid_start != hint_addr) {
- /* Hint address must be respected. If we are here - this means
- * we could not respect it.
- */
- dev_err(hdev->dev,
- "Hint address 0x%llx could not be respected\n",
- hint_addr);
- reserved_valid_start = 0;
- goto out;
- }
-
- /*
- * Check if there is some leftover range due to reserving the new
- * va block, then return it to the main virtual addresses list.
- */
- if (reserved_valid_start > new_va_block->start) {
- prev_start = new_va_block->start;
- prev_end = reserved_valid_start - 1;
-
- new_va_block->start = reserved_valid_start;
- new_va_block->size = reserved_valid_size;
-
- add_prev = true;
- }
-
- if (new_va_block->size > size) {
- new_va_block->start += size;
- new_va_block->size = new_va_block->end - new_va_block->start + 1;
- } else {
- list_del(&new_va_block->node);
- kfree(new_va_block);
- }
-
- if (add_prev)
- add_va_block_locked(hdev, &va_range->list, prev_start,
- prev_end);
-
- print_va_list_locked(hdev, &va_range->list);
-out:
- mutex_unlock(&va_range->lock);
-
- return reserved_valid_start;
-}
-
-/*
- * hl_reserve_va_block() - reserve a virtual block of a given size.
- * @hdev: pointer to the habanalabs device structure.
- * @ctx: current context
- * @type: virtual addresses range type.
- * @size: requested block size.
- * @alignment: required alignment in bytes of the virtual block start address,
- * 0 means no alignment.
- *
- * This function does the following:
- * - Iterate on the virtual block list to find a suitable virtual block for the
- * given size and alignment.
- * - Reserve the requested block and update the list.
- * - Return the start address of the virtual block.
- */
-u64 hl_reserve_va_block(struct hl_device *hdev, struct hl_ctx *ctx,
- enum hl_va_range_type type, u64 size, u32 alignment)
-{
- return get_va_block(hdev, ctx->va_range[type], size, 0,
- max(alignment, ctx->va_range[type]->page_size),
- type, 0);
-}
-
-/**
- * hl_get_va_range_type() - get va_range type for the given address and size.
- * @ctx: context to fetch va_range from.
- * @address: the start address of the area we want to validate.
- * @size: the size in bytes of the area we want to validate.
- * @type: returned va_range type.
- *
- * Return: true if the area is inside a valid range, false otherwise.
- */
-static int hl_get_va_range_type(struct hl_ctx *ctx, u64 address, u64 size,
- enum hl_va_range_type *type)
-{
- int i;
-
- for (i = 0 ; i < HL_VA_RANGE_TYPE_MAX; i++) {
- if (hl_mem_area_inside_range(address, size,
- ctx->va_range[i]->start_addr,
- ctx->va_range[i]->end_addr)) {
- *type = i;
- return 0;
- }
- }
-
- return -EINVAL;
-}
-
-/**
- * hl_unreserve_va_block() - wrapper for add_va_block to unreserve a va block.
- * @hdev: pointer to the habanalabs device structure
- * @ctx: pointer to the context structure.
- * @start_addr: start virtual address.
- * @size: number of bytes to unreserve.
- *
- * This function does the following:
- * - Takes the list lock and calls add_va_block_locked.
- */
-int hl_unreserve_va_block(struct hl_device *hdev, struct hl_ctx *ctx,
- u64 start_addr, u64 size)
-{
- enum hl_va_range_type type;
- int rc;
-
- rc = hl_get_va_range_type(ctx, start_addr, size, &type);
- if (rc) {
- dev_err(hdev->dev,
- "cannot find va_range for va %#llx size %llu",
- start_addr, size);
- return rc;
- }
-
- rc = add_va_block(hdev, ctx->va_range[type], start_addr,
- start_addr + size - 1);
- if (rc)
- dev_warn(hdev->dev,
- "add va block failed for vaddr: 0x%llx\n", start_addr);
-
- return rc;
-}
-
-/**
- * init_phys_pg_pack_from_userptr() - initialize physical page pack from host
- * memory
- * @ctx: pointer to the context structure.
- * @userptr: userptr to initialize from.
- * @pphys_pg_pack: result pointer.
- * @force_regular_page: tell the function to ignore huge page optimization,
- * even if possible. Needed for cases where the device VA
- * is allocated before we know the composition of the
- * physical pages
- *
- * This function does the following:
- * - Pin the physical pages related to the given virtual block.
- * - Create a physical page pack from the physical pages related to the given
- * virtual block.
- */
-static int init_phys_pg_pack_from_userptr(struct hl_ctx *ctx,
- struct hl_userptr *userptr,
- struct hl_vm_phys_pg_pack **pphys_pg_pack,
- bool force_regular_page)
-{
- u32 npages, page_size = PAGE_SIZE,
- huge_page_size = ctx->hdev->asic_prop.pmmu_huge.page_size;
- u32 pgs_in_huge_page = huge_page_size >> __ffs(page_size);
- struct hl_vm_phys_pg_pack *phys_pg_pack;
- bool first = true, is_huge_page_opt;
- u64 page_mask, total_npages;
- struct scatterlist *sg;
- dma_addr_t dma_addr;
- int rc, i, j;
-
- phys_pg_pack = kzalloc(sizeof(*phys_pg_pack), GFP_KERNEL);
- if (!phys_pg_pack)
- return -ENOMEM;
-
- phys_pg_pack->vm_type = userptr->vm_type;
- phys_pg_pack->created_from_userptr = true;
- phys_pg_pack->asid = ctx->asid;
- atomic_set(&phys_pg_pack->mapping_cnt, 1);
-
- is_huge_page_opt = (force_regular_page ? false : true);
-
- /* Only if all dma_addrs are aligned to 2MB and their
- * sizes is at least 2MB, we can use huge page mapping.
- * We limit the 2MB optimization to this condition,
- * since later on we acquire the related VA range as one
- * consecutive block.
- */
- total_npages = 0;
- for_each_sgtable_dma_sg(userptr->sgt, sg, i) {
- npages = hl_get_sg_info(sg, &dma_addr);
-
- total_npages += npages;
-
- if ((npages % pgs_in_huge_page) ||
- (dma_addr & (huge_page_size - 1)))
- is_huge_page_opt = false;
- }
-
- if (is_huge_page_opt) {
- page_size = huge_page_size;
- do_div(total_npages, pgs_in_huge_page);
- }
-
- page_mask = ~(((u64) page_size) - 1);
-
- phys_pg_pack->pages = kvmalloc_array(total_npages, sizeof(u64),
- GFP_KERNEL);
- if (ZERO_OR_NULL_PTR(phys_pg_pack->pages)) {
- rc = -ENOMEM;
- goto page_pack_arr_mem_err;
- }
-
- phys_pg_pack->npages = total_npages;
- phys_pg_pack->page_size = page_size;
- phys_pg_pack->total_size = total_npages * page_size;
-
- j = 0;
- for_each_sgtable_dma_sg(userptr->sgt, sg, i) {
- npages = hl_get_sg_info(sg, &dma_addr);
-
- /* align down to physical page size and save the offset */
- if (first) {
- first = false;
- phys_pg_pack->offset = dma_addr & (page_size - 1);
- dma_addr &= page_mask;
- }
-
- while (npages) {
- phys_pg_pack->pages[j++] = dma_addr;
- dma_addr += page_size;
-
- if (is_huge_page_opt)
- npages -= pgs_in_huge_page;
- else
- npages--;
- }
- }
-
- *pphys_pg_pack = phys_pg_pack;
-
- return 0;
-
-page_pack_arr_mem_err:
- kfree(phys_pg_pack);
-
- return rc;
-}
-
-/**
- * map_phys_pg_pack() - maps the physical page pack..
- * @ctx: pointer to the context structure.
- * @vaddr: start address of the virtual area to map from.
- * @phys_pg_pack: the pack of physical pages to map to.
- *
- * This function does the following:
- * - Maps each chunk of virtual memory to matching physical chunk.
- * - Stores number of successful mappings in the given argument.
- * - Returns 0 on success, error code otherwise.
- */
-static int map_phys_pg_pack(struct hl_ctx *ctx, u64 vaddr,
- struct hl_vm_phys_pg_pack *phys_pg_pack)
-{
- struct hl_device *hdev = ctx->hdev;
- u64 next_vaddr = vaddr, paddr, mapped_pg_cnt = 0, i;
- u32 page_size = phys_pg_pack->page_size;
- int rc = 0;
- bool is_host_addr;
-
- for (i = 0 ; i < phys_pg_pack->npages ; i++) {
- paddr = phys_pg_pack->pages[i];
-
- rc = hl_mmu_map_page(ctx, next_vaddr, paddr, page_size,
- (i + 1) == phys_pg_pack->npages);
- if (rc) {
- dev_err(hdev->dev,
- "map failed for handle %u, npages: %llu, mapped: %llu",
- phys_pg_pack->handle, phys_pg_pack->npages,
- mapped_pg_cnt);
- goto err;
- }
-
- mapped_pg_cnt++;
- next_vaddr += page_size;
- }
-
- return 0;
-
-err:
- is_host_addr = !hl_is_dram_va(hdev, vaddr);
-
- next_vaddr = vaddr;
- for (i = 0 ; i < mapped_pg_cnt ; i++) {
- if (hl_mmu_unmap_page(ctx, next_vaddr, page_size,
- (i + 1) == mapped_pg_cnt))
- dev_warn_ratelimited(hdev->dev,
- "failed to unmap handle %u, va: 0x%llx, pa: 0x%llx, page size: %u\n",
- phys_pg_pack->handle, next_vaddr,
- phys_pg_pack->pages[i], page_size);
-
- next_vaddr += page_size;
-
- /*
- * unmapping on Palladium can be really long, so avoid a CPU
- * soft lockup bug by sleeping a little between unmapping pages
- *
- * In addition, on host num of pages could be huge,
- * because page size could be 4KB, so when unmapping host
- * pages sleep every 32K pages to avoid soft lockup
- */
- if (hdev->pldm || (is_host_addr && (i & 0x7FFF) == 0))
- usleep_range(50, 200);
- }
-
- return rc;
-}
-
-/**
- * unmap_phys_pg_pack() - unmaps the physical page pack.
- * @ctx: pointer to the context structure.
- * @vaddr: start address of the virtual area to unmap.
- * @phys_pg_pack: the pack of physical pages to unmap.
- */
-static void unmap_phys_pg_pack(struct hl_ctx *ctx, u64 vaddr,
- struct hl_vm_phys_pg_pack *phys_pg_pack)
-{
- struct hl_device *hdev = ctx->hdev;
- u64 next_vaddr, i;
- bool is_host_addr;
- u32 page_size;
-
- is_host_addr = !hl_is_dram_va(hdev, vaddr);
- page_size = phys_pg_pack->page_size;
- next_vaddr = vaddr;
-
- for (i = 0 ; i < phys_pg_pack->npages ; i++, next_vaddr += page_size) {
- if (hl_mmu_unmap_page(ctx, next_vaddr, page_size,
- (i + 1) == phys_pg_pack->npages))
- dev_warn_ratelimited(hdev->dev,
- "unmap failed for vaddr: 0x%llx\n", next_vaddr);
-
- /*
- * unmapping on Palladium can be really long, so avoid a CPU
- * soft lockup bug by sleeping a little between unmapping pages
- *
- * In addition, on host num of pages could be huge,
- * because page size could be 4KB, so when unmapping host
- * pages sleep every 32K pages to avoid soft lockup
- */
- if (hdev->pldm || (is_host_addr && (i & 0x7FFF) == 0))
- usleep_range(50, 200);
- }
-}
-
-static int get_paddr_from_handle(struct hl_ctx *ctx, struct hl_mem_in *args,
- u64 *paddr)
-{
- struct hl_device *hdev = ctx->hdev;
- struct hl_vm *vm = &hdev->vm;
- struct hl_vm_phys_pg_pack *phys_pg_pack;
- u32 handle;
-
- handle = lower_32_bits(args->map_device.handle);
- spin_lock(&vm->idr_lock);
- phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, handle);
- if (!phys_pg_pack) {
- spin_unlock(&vm->idr_lock);
- dev_err(hdev->dev, "no match for handle %u\n", handle);
- return -EINVAL;
- }
-
- *paddr = phys_pg_pack->pages[0];
-
- spin_unlock(&vm->idr_lock);
-
- return 0;
-}
-
-/**
- * map_device_va() - map the given memory.
- * @ctx: pointer to the context structure.
- * @args: host parameters with handle/host virtual address.
- * @device_addr: pointer to result device virtual address.
- *
- * This function does the following:
- * - If given a physical device memory handle, map to a device virtual block
- * and return the start address of this block.
- * - If given a host virtual address and size, find the related physical pages,
- * map a device virtual block to this pages and return the start address of
- * this block.
- */
-static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, u64 *device_addr)
-{
- struct hl_vm_phys_pg_pack *phys_pg_pack;
- enum hl_va_range_type va_range_type = 0;
- struct hl_device *hdev = ctx->hdev;
- struct hl_userptr *userptr = NULL;
- u32 handle = 0, va_block_align;
- struct hl_vm_hash_node *hnode;
- struct hl_vm *vm = &hdev->vm;
- struct hl_va_range *va_range;
- bool is_userptr, do_prefetch;
- u64 ret_vaddr, hint_addr;
- enum vm_type *vm_type;
- int rc;
-
- /* set map flags */
- is_userptr = args->flags & HL_MEM_USERPTR;
- do_prefetch = hdev->supports_mmu_prefetch && (args->flags & HL_MEM_PREFETCH);
-
- /* Assume failure */
- *device_addr = 0;
-
- if (is_userptr) {
- u64 addr = args->map_host.host_virt_addr,
- size = args->map_host.mem_size;
- u32 page_size = hdev->asic_prop.pmmu.page_size,
- huge_page_size = hdev->asic_prop.pmmu_huge.page_size;
-
- rc = dma_map_host_va(hdev, addr, size, &userptr);
- if (rc) {
- dev_err(hdev->dev, "failed to get userptr from va\n");
- return rc;
- }
-
- rc = init_phys_pg_pack_from_userptr(ctx, userptr,
- &phys_pg_pack, false);
- if (rc) {
- dev_err(hdev->dev,
- "unable to init page pack for vaddr 0x%llx\n",
- addr);
- goto init_page_pack_err;
- }
-
- vm_type = (enum vm_type *) userptr;
- hint_addr = args->map_host.hint_addr;
- handle = phys_pg_pack->handle;
-
- /* get required alignment */
- if (phys_pg_pack->page_size == page_size) {
- va_range = ctx->va_range[HL_VA_RANGE_TYPE_HOST];
- va_range_type = HL_VA_RANGE_TYPE_HOST;
- /*
- * huge page alignment may be needed in case of regular
- * page mapping, depending on the host VA alignment
- */
- if (addr & (huge_page_size - 1))
- va_block_align = page_size;
- else
- va_block_align = huge_page_size;
- } else {
- /*
- * huge page alignment is needed in case of huge page
- * mapping
- */
- va_range = ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE];
- va_range_type = HL_VA_RANGE_TYPE_HOST_HUGE;
- va_block_align = huge_page_size;
- }
- } else {
- handle = lower_32_bits(args->map_device.handle);
-
- spin_lock(&vm->idr_lock);
- phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, handle);
- if (!phys_pg_pack) {
- spin_unlock(&vm->idr_lock);
- dev_err(hdev->dev,
- "no match for handle %u\n", handle);
- return -EINVAL;
- }
-
- /* increment now to avoid freeing device memory while mapping */
- atomic_inc(&phys_pg_pack->mapping_cnt);
-
- spin_unlock(&vm->idr_lock);
-
- vm_type = (enum vm_type *) phys_pg_pack;
-
- hint_addr = args->map_device.hint_addr;
-
- /* DRAM VA alignment is the same as the MMU page size */
- va_range = ctx->va_range[HL_VA_RANGE_TYPE_DRAM];
- va_range_type = HL_VA_RANGE_TYPE_DRAM;
- va_block_align = hdev->asic_prop.dmmu.page_size;
- }
-
- /*
- * relevant for mapping device physical memory only, as host memory is
- * implicitly shared
- */
- if (!is_userptr && !(phys_pg_pack->flags & HL_MEM_SHARED) &&
- phys_pg_pack->asid != ctx->asid) {
- dev_err(hdev->dev,
- "Failed to map memory, handle %u is not shared\n",
- handle);
- rc = -EPERM;
- goto shared_err;
- }
-
- hnode = kzalloc(sizeof(*hnode), GFP_KERNEL);
- if (!hnode) {
- rc = -ENOMEM;
- goto hnode_err;
- }
-
- if (hint_addr && phys_pg_pack->offset) {
- if (args->flags & HL_MEM_FORCE_HINT) {
- /* Fail if hint must be respected but it can't be */
- dev_err(hdev->dev,
- "Hint address 0x%llx cannot be respected because source memory is not aligned 0x%x\n",
- hint_addr, phys_pg_pack->offset);
- rc = -EINVAL;
- goto va_block_err;
- }
- dev_dbg(hdev->dev,
- "Hint address 0x%llx will be ignored because source memory is not aligned 0x%x\n",
- hint_addr, phys_pg_pack->offset);
- }
-
- ret_vaddr = get_va_block(hdev, va_range, phys_pg_pack->total_size,
- hint_addr, va_block_align,
- va_range_type, args->flags);
- if (!ret_vaddr) {
- dev_err(hdev->dev, "no available va block for handle %u\n",
- handle);
- rc = -ENOMEM;
- goto va_block_err;
- }
-
- mutex_lock(&hdev->mmu_lock);
-
- rc = map_phys_pg_pack(ctx, ret_vaddr, phys_pg_pack);
- if (rc) {
- dev_err(hdev->dev, "mapping page pack failed for handle %u\n", handle);
- mutex_unlock(&hdev->mmu_lock);
- goto map_err;
- }
-
- rc = hl_mmu_invalidate_cache_range(hdev, false, *vm_type | MMU_OP_SKIP_LOW_CACHE_INV,
- ctx->asid, ret_vaddr, phys_pg_pack->total_size);
- mutex_unlock(&hdev->mmu_lock);
- if (rc)
- goto map_err;
-
- /*
- * prefetch is done upon user's request. it is performed in WQ as and so can
- * be outside the MMU lock. the operation itself is already protected by the mmu lock
- */
- if (do_prefetch) {
- rc = hl_mmu_prefetch_cache_range(ctx, *vm_type, ctx->asid, ret_vaddr,
- phys_pg_pack->total_size);
- if (rc)
- goto map_err;
- }
-
- ret_vaddr += phys_pg_pack->offset;
-
- hnode->ptr = vm_type;
- hnode->vaddr = ret_vaddr;
- hnode->handle = is_userptr ? MEM_HANDLE_INVALID : handle;
-
- mutex_lock(&ctx->mem_hash_lock);
- hash_add(ctx->mem_hash, &hnode->node, ret_vaddr);
- mutex_unlock(&ctx->mem_hash_lock);
-
- *device_addr = ret_vaddr;
-
- if (is_userptr)
- free_phys_pg_pack(hdev, phys_pg_pack);
-
- return rc;
-
-map_err:
- if (add_va_block(hdev, va_range, ret_vaddr,
- ret_vaddr + phys_pg_pack->total_size - 1))
- dev_warn(hdev->dev,
- "release va block failed for handle 0x%x, vaddr: 0x%llx\n",
- handle, ret_vaddr);
-
-va_block_err:
- kfree(hnode);
-hnode_err:
-shared_err:
- atomic_dec(&phys_pg_pack->mapping_cnt);
- if (is_userptr)
- free_phys_pg_pack(hdev, phys_pg_pack);
-init_page_pack_err:
- if (is_userptr)
- dma_unmap_host_va(hdev, userptr);
-
- return rc;
-}
-
-/**
- * unmap_device_va() - unmap the given device virtual address.
- * @ctx: pointer to the context structure.
- * @args: host parameters with device virtual address to unmap.
- * @ctx_free: true if in context free flow, false otherwise.
- *
- * This function does the following:
- * - unmap the physical pages related to the given virtual address.
- * - return the device virtual block to the virtual block list.
- */
-static int unmap_device_va(struct hl_ctx *ctx, struct hl_mem_in *args,
- bool ctx_free)
-{
- struct hl_vm_phys_pg_pack *phys_pg_pack = NULL;
- u64 vaddr = args->unmap.device_virt_addr;
- struct hl_vm_hash_node *hnode = NULL;
- struct asic_fixed_properties *prop;
- struct hl_device *hdev = ctx->hdev;
- struct hl_userptr *userptr = NULL;
- struct hl_va_range *va_range;
- enum vm_type *vm_type;
- bool is_userptr;
- int rc = 0;
-
- prop = &hdev->asic_prop;
-
- /* protect from double entrance */
- mutex_lock(&ctx->mem_hash_lock);
- hash_for_each_possible(ctx->mem_hash, hnode, node, (unsigned long)vaddr)
- if (vaddr == hnode->vaddr)
- break;
-
- if (!hnode) {
- mutex_unlock(&ctx->mem_hash_lock);
- dev_err(hdev->dev,
- "unmap failed, no mem hnode for vaddr 0x%llx\n",
- vaddr);
- return -EINVAL;
- }
-
- if (hnode->export_cnt) {
- mutex_unlock(&ctx->mem_hash_lock);
- dev_err(hdev->dev, "failed to unmap %#llx, memory is exported\n", vaddr);
- return -EINVAL;
- }
-
- hash_del(&hnode->node);
- mutex_unlock(&ctx->mem_hash_lock);
-
- vm_type = hnode->ptr;
-
- if (*vm_type == VM_TYPE_USERPTR) {
- is_userptr = true;
- userptr = hnode->ptr;
-
- rc = init_phys_pg_pack_from_userptr(ctx, userptr, &phys_pg_pack,
- false);
- if (rc) {
- dev_err(hdev->dev,
- "unable to init page pack for vaddr 0x%llx\n",
- vaddr);
- goto vm_type_err;
- }
-
- if (phys_pg_pack->page_size ==
- hdev->asic_prop.pmmu.page_size)
- va_range = ctx->va_range[HL_VA_RANGE_TYPE_HOST];
- else
- va_range = ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE];
- } else if (*vm_type == VM_TYPE_PHYS_PACK) {
- is_userptr = false;
- va_range = ctx->va_range[HL_VA_RANGE_TYPE_DRAM];
- phys_pg_pack = hnode->ptr;
- } else {
- dev_warn(hdev->dev,
- "unmap failed, unknown vm desc for vaddr 0x%llx\n",
- vaddr);
- rc = -EFAULT;
- goto vm_type_err;
- }
-
- if (atomic_read(&phys_pg_pack->mapping_cnt) == 0) {
- dev_err(hdev->dev, "vaddr 0x%llx is not mapped\n", vaddr);
- rc = -EINVAL;
- goto mapping_cnt_err;
- }
-
- if (!is_userptr && !is_power_of_2(phys_pg_pack->page_size))
- vaddr = prop->dram_base_address +
- DIV_ROUND_DOWN_ULL(vaddr - prop->dram_base_address,
- phys_pg_pack->page_size) *
- phys_pg_pack->page_size;
- else
- vaddr &= ~(((u64) phys_pg_pack->page_size) - 1);
-
- mutex_lock(&hdev->mmu_lock);
-
- unmap_phys_pg_pack(ctx, vaddr, phys_pg_pack);
-
- /*
- * During context free this function is called in a loop to clean all
- * the context mappings. Hence the cache invalidation can be called once
- * at the loop end rather than for each iteration
- */
- if (!ctx_free)
- rc = hl_mmu_invalidate_cache_range(hdev, true, *vm_type, ctx->asid, vaddr,
- phys_pg_pack->total_size);
-
- mutex_unlock(&hdev->mmu_lock);
-
- /*
- * If the context is closing we don't need to check for the MMU cache
- * invalidation return code and update the VA free list as in this flow
- * we invalidate the MMU cache outside of this unmap function and the VA
- * free list will be freed anyway.
- */
- if (!ctx_free) {
- int tmp_rc;
-
- tmp_rc = add_va_block(hdev, va_range, vaddr,
- vaddr + phys_pg_pack->total_size - 1);
- if (tmp_rc) {
- dev_warn(hdev->dev,
- "add va block failed for vaddr: 0x%llx\n",
- vaddr);
- if (!rc)
- rc = tmp_rc;
- }
- }
-
- atomic_dec(&phys_pg_pack->mapping_cnt);
- kfree(hnode);
-
- if (is_userptr) {
- free_phys_pg_pack(hdev, phys_pg_pack);
- dma_unmap_host_va(hdev, userptr);
- }
-
- return rc;
-
-mapping_cnt_err:
- if (is_userptr)
- free_phys_pg_pack(hdev, phys_pg_pack);
-vm_type_err:
- mutex_lock(&ctx->mem_hash_lock);
- hash_add(ctx->mem_hash, &hnode->node, vaddr);
- mutex_unlock(&ctx->mem_hash_lock);
-
- return rc;
-}
-
-static int map_block(struct hl_device *hdev, u64 address, u64 *handle, u32 *size)
-{
- u32 block_id;
- int rc;
-
- *handle = 0;
- if (size)
- *size = 0;
-
- rc = hdev->asic_funcs->get_hw_block_id(hdev, address, size, &block_id);
- if (rc)
- return rc;
-
- *handle = block_id | HL_MMAP_TYPE_BLOCK;
- *handle <<= PAGE_SHIFT;
-
- return 0;
-}
-
-static void hw_block_vm_close(struct vm_area_struct *vma)
-{
- struct hl_vm_hw_block_list_node *lnode =
- (struct hl_vm_hw_block_list_node *) vma->vm_private_data;
- struct hl_ctx *ctx = lnode->ctx;
- long new_mmap_size;
-
- new_mmap_size = lnode->mapped_size - (vma->vm_end - vma->vm_start);
- if (new_mmap_size > 0) {
- lnode->mapped_size = new_mmap_size;
- return;
- }
-
- mutex_lock(&ctx->hw_block_list_lock);
- list_del(&lnode->node);
- mutex_unlock(&ctx->hw_block_list_lock);
- hl_ctx_put(ctx);
- kfree(lnode);
- vma->vm_private_data = NULL;
-}
-
-static const struct vm_operations_struct hw_block_vm_ops = {
- .close = hw_block_vm_close
-};
-
-/**
- * hl_hw_block_mmap() - mmap a hw block to user.
- * @hpriv: pointer to the private data of the fd
- * @vma: pointer to vm_area_struct of the process
- *
- * Driver increments context reference for every HW block mapped in order
- * to prevent user from closing FD without unmapping first
- */
-int hl_hw_block_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma)
-{
- struct hl_vm_hw_block_list_node *lnode;
- struct hl_device *hdev = hpriv->hdev;
- struct hl_ctx *ctx = hpriv->ctx;
- u32 block_id, block_size;
- int rc;
-
- /* We use the page offset to hold the block id and thus we need to clear
- * it before doing the mmap itself
- */
- block_id = vma->vm_pgoff;
- vma->vm_pgoff = 0;
-
- /* Driver only allows mapping of a complete HW block */
- block_size = vma->vm_end - vma->vm_start;
-
- if (!access_ok((void __user *) (uintptr_t) vma->vm_start, block_size)) {
- dev_err(hdev->dev,
- "user pointer is invalid - 0x%lx\n",
- vma->vm_start);
-
- return -EINVAL;
- }
-
- lnode = kzalloc(sizeof(*lnode), GFP_KERNEL);
- if (!lnode)
- return -ENOMEM;
-
- rc = hdev->asic_funcs->hw_block_mmap(hdev, vma, block_id, block_size);
- if (rc) {
- kfree(lnode);
- return rc;
- }
-
- hl_ctx_get(ctx);
-
- lnode->ctx = ctx;
- lnode->vaddr = vma->vm_start;
- lnode->block_size = block_size;
- lnode->mapped_size = lnode->block_size;
- lnode->id = block_id;
-
- vma->vm_private_data = lnode;
- vma->vm_ops = &hw_block_vm_ops;
-
- mutex_lock(&ctx->hw_block_list_lock);
- list_add_tail(&lnode->node, &ctx->hw_block_mem_list);
- mutex_unlock(&ctx->hw_block_list_lock);
-
- vma->vm_pgoff = block_id;
-
- return 0;
-}
-
-static int set_dma_sg(struct scatterlist *sg, u64 bar_address, u64 chunk_size,
- struct device *dev, enum dma_data_direction dir)
-{
- dma_addr_t addr;
- int rc;
-
- addr = dma_map_resource(dev, bar_address, chunk_size, dir,
- DMA_ATTR_SKIP_CPU_SYNC);
- rc = dma_mapping_error(dev, addr);
- if (rc)
- return rc;
-
- sg_set_page(sg, NULL, chunk_size, 0);
- sg_dma_address(sg) = addr;
- sg_dma_len(sg) = chunk_size;
-
- return 0;
-}
-
-static struct sg_table *alloc_sgt_from_device_pages(struct hl_device *hdev, u64 *pages, u64 npages,
- u64 page_size, u64 exported_size,
- struct device *dev, enum dma_data_direction dir)
-{
- u64 chunk_size, bar_address, dma_max_seg_size, cur_size_to_export, cur_npages;
- struct asic_fixed_properties *prop;
- int rc, i, j, nents, cur_page;
- struct scatterlist *sg;
- struct sg_table *sgt;
-
- prop = &hdev->asic_prop;
-
- dma_max_seg_size = dma_get_max_seg_size(dev);
-
- /* We would like to align the max segment size to PAGE_SIZE, so the
- * SGL will contain aligned addresses that can be easily mapped to
- * an MMU
- */
- dma_max_seg_size = ALIGN_DOWN(dma_max_seg_size, PAGE_SIZE);
- if (dma_max_seg_size < PAGE_SIZE) {
- dev_err_ratelimited(hdev->dev,
- "dma_max_seg_size %llu can't be smaller than PAGE_SIZE\n",
- dma_max_seg_size);
- return ERR_PTR(-EINVAL);
- }
-
- sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
- if (!sgt)
- return ERR_PTR(-ENOMEM);
-
- /* remove export size restrictions in case not explicitly defined */
- cur_size_to_export = exported_size ? exported_size : (npages * page_size);
-
- /* If the size of each page is larger than the dma max segment size,
- * then we can't combine pages and the number of entries in the SGL
- * will just be the
- * <number of pages> * <chunks of max segment size in each page>
- */
- if (page_size > dma_max_seg_size) {
- /* we should limit number of pages according to the exported size */
- cur_npages = DIV_ROUND_UP_SECTOR_T(cur_size_to_export, page_size);
- nents = cur_npages * DIV_ROUND_UP_SECTOR_T(page_size, dma_max_seg_size);
- } else {
- cur_npages = npages;
-
- /* Get number of non-contiguous chunks */
- for (i = 1, nents = 1, chunk_size = page_size ; i < cur_npages ; i++) {
- if (pages[i - 1] + page_size != pages[i] ||
- chunk_size + page_size > dma_max_seg_size) {
- nents++;
- chunk_size = page_size;
- continue;
- }
-
- chunk_size += page_size;
- }
- }
-
- rc = sg_alloc_table(sgt, nents, GFP_KERNEL | __GFP_ZERO);
- if (rc)
- goto error_free;
-
- cur_page = 0;
-
- if (page_size > dma_max_seg_size) {
- u64 size_left, cur_device_address = 0;
-
- size_left = page_size;
-
- /* Need to split each page into the number of chunks of
- * dma_max_seg_size
- */
- for_each_sgtable_dma_sg(sgt, sg, i) {
- if (size_left == page_size)
- cur_device_address =
- pages[cur_page] - prop->dram_base_address;
- else
- cur_device_address += dma_max_seg_size;
-
- /* make sure not to export over exported size */
- chunk_size = min3(size_left, dma_max_seg_size, cur_size_to_export);
-
- bar_address = hdev->dram_pci_bar_start + cur_device_address;
-
- rc = set_dma_sg(sg, bar_address, chunk_size, dev, dir);
- if (rc)
- goto error_unmap;
-
- cur_size_to_export -= chunk_size;
-
- if (size_left > dma_max_seg_size) {
- size_left -= dma_max_seg_size;
- } else {
- cur_page++;
- size_left = page_size;
- }
- }
- } else {
- /* Merge pages and put them into the scatterlist */
- for_each_sgtable_dma_sg(sgt, sg, i) {
- chunk_size = page_size;
- for (j = cur_page + 1 ; j < cur_npages ; j++) {
- if (pages[j - 1] + page_size != pages[j] ||
- chunk_size + page_size > dma_max_seg_size)
- break;
-
- chunk_size += page_size;
- }
-
- bar_address = hdev->dram_pci_bar_start +
- (pages[cur_page] - prop->dram_base_address);
-
- /* make sure not to export over exported size */
- chunk_size = min(chunk_size, cur_size_to_export);
- rc = set_dma_sg(sg, bar_address, chunk_size, dev, dir);
- if (rc)
- goto error_unmap;
-
- cur_size_to_export -= chunk_size;
- cur_page = j;
- }
- }
-
- /* Because we are not going to include a CPU list we want to have some
- * chance that other users will detect this by setting the orig_nents
- * to 0 and using only nents (length of DMA list) when going over the
- * sgl
- */
- sgt->orig_nents = 0;
-
- return sgt;
-
-error_unmap:
- for_each_sgtable_dma_sg(sgt, sg, i) {
- if (!sg_dma_len(sg))
- continue;
-
- dma_unmap_resource(dev, sg_dma_address(sg),
- sg_dma_len(sg), dir,
- DMA_ATTR_SKIP_CPU_SYNC);
- }
-
- sg_free_table(sgt);
-
-error_free:
- kfree(sgt);
- return ERR_PTR(rc);
-}
-
-static int hl_dmabuf_attach(struct dma_buf *dmabuf,
- struct dma_buf_attachment *attachment)
-{
- struct hl_dmabuf_priv *hl_dmabuf;
- struct hl_device *hdev;
- int rc;
-
- hl_dmabuf = dmabuf->priv;
- hdev = hl_dmabuf->ctx->hdev;
-
- rc = pci_p2pdma_distance(hdev->pdev, attachment->dev, true);
-
- if (rc < 0)
- attachment->peer2peer = false;
- return 0;
-}
-
-static struct sg_table *hl_map_dmabuf(struct dma_buf_attachment *attachment,
- enum dma_data_direction dir)
-{
- struct dma_buf *dma_buf = attachment->dmabuf;
- struct hl_vm_phys_pg_pack *phys_pg_pack;
- struct hl_dmabuf_priv *hl_dmabuf;
- struct hl_device *hdev;
- struct sg_table *sgt;
-
- hl_dmabuf = dma_buf->priv;
- hdev = hl_dmabuf->ctx->hdev;
- phys_pg_pack = hl_dmabuf->phys_pg_pack;
-
- if (!attachment->peer2peer) {
- dev_dbg(hdev->dev, "Failed to map dmabuf because p2p is disabled\n");
- return ERR_PTR(-EPERM);
- }
-
- if (phys_pg_pack)
- sgt = alloc_sgt_from_device_pages(hdev,
- phys_pg_pack->pages,
- phys_pg_pack->npages,
- phys_pg_pack->page_size,
- phys_pg_pack->exported_size,
- attachment->dev,
- dir);
- else
- sgt = alloc_sgt_from_device_pages(hdev,
- &hl_dmabuf->device_address,
- 1,
- hl_dmabuf->dmabuf->size,
- 0,
- attachment->dev,
- dir);
-
- if (IS_ERR(sgt))
- dev_err(hdev->dev, "failed (%ld) to initialize sgt for dmabuf\n", PTR_ERR(sgt));
-
- return sgt;
-}
-
-static void hl_unmap_dmabuf(struct dma_buf_attachment *attachment,
- struct sg_table *sgt,
- enum dma_data_direction dir)
-{
- struct scatterlist *sg;
- int i;
-
- /* The memory behind the dma-buf has *always* resided on the device itself, i.e. it lives
- * only in the 'device' domain (after all, it maps a PCI bar address which points to the
- * device memory).
- *
- * Therefore, it was never in the 'CPU' domain and hence, there is no need to perform
- * a sync of the memory to the CPU's cache, as it never resided inside that cache.
- */
- for_each_sgtable_dma_sg(sgt, sg, i)
- dma_unmap_resource(attachment->dev, sg_dma_address(sg),
- sg_dma_len(sg), dir,
- DMA_ATTR_SKIP_CPU_SYNC);
-
- /* Need to restore orig_nents because sg_free_table use that field */
- sgt->orig_nents = sgt->nents;
- sg_free_table(sgt);
- kfree(sgt);
-}
-
-static void hl_release_dmabuf(struct dma_buf *dmabuf)
-{
- struct hl_dmabuf_priv *hl_dmabuf = dmabuf->priv;
- struct hl_ctx *ctx;
-
- if (!hl_dmabuf)
- return;
-
- ctx = hl_dmabuf->ctx;
-
- if (hl_dmabuf->memhash_hnode) {
- mutex_lock(&ctx->mem_hash_lock);
- hl_dmabuf->memhash_hnode->export_cnt--;
- mutex_unlock(&ctx->mem_hash_lock);
- }
-
- hl_ctx_put(ctx);
- kfree(hl_dmabuf);
-}
-
-static const struct dma_buf_ops habanalabs_dmabuf_ops = {
- .attach = hl_dmabuf_attach,
- .map_dma_buf = hl_map_dmabuf,
- .unmap_dma_buf = hl_unmap_dmabuf,
- .release = hl_release_dmabuf,
-};
-
-static int export_dmabuf(struct hl_ctx *ctx,
- struct hl_dmabuf_priv *hl_dmabuf,
- u64 total_size, int flags, int *dmabuf_fd)
-{
- DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
- struct hl_device *hdev = ctx->hdev;
- int rc, fd;
-
- exp_info.ops = &habanalabs_dmabuf_ops;
- exp_info.size = total_size;
- exp_info.flags = flags;
- exp_info.priv = hl_dmabuf;
-
- hl_dmabuf->dmabuf = dma_buf_export(&exp_info);
- if (IS_ERR(hl_dmabuf->dmabuf)) {
- dev_err(hdev->dev, "failed to export dma-buf\n");
- return PTR_ERR(hl_dmabuf->dmabuf);
- }
-
- fd = dma_buf_fd(hl_dmabuf->dmabuf, flags);
- if (fd < 0) {
- dev_err(hdev->dev, "failed to get a file descriptor for a dma-buf, %d\n", fd);
- rc = fd;
- goto err_dma_buf_put;
- }
-
- hl_dmabuf->ctx = ctx;
- hl_ctx_get(hl_dmabuf->ctx);
-
- *dmabuf_fd = fd;
-
- return 0;
-
-err_dma_buf_put:
- hl_dmabuf->dmabuf->priv = NULL;
- dma_buf_put(hl_dmabuf->dmabuf);
- return rc;
-}
-
-static int validate_export_params_common(struct hl_device *hdev, u64 device_addr, u64 size)
-{
- if (!IS_ALIGNED(device_addr, PAGE_SIZE)) {
- dev_dbg(hdev->dev,
- "exported device memory address 0x%llx should be aligned to 0x%lx\n",
- device_addr, PAGE_SIZE);
- return -EINVAL;
- }
-
- if (size < PAGE_SIZE) {
- dev_dbg(hdev->dev,
- "exported device memory size %llu should be equal to or greater than %lu\n",
- size, PAGE_SIZE);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int validate_export_params_no_mmu(struct hl_device *hdev, u64 device_addr, u64 size)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u64 bar_address;
- int rc;
-
- rc = validate_export_params_common(hdev, device_addr, size);
- if (rc)
- return rc;
-
- if (device_addr < prop->dram_user_base_address ||
- (device_addr + size) > prop->dram_end_address ||
- (device_addr + size) < device_addr) {
- dev_dbg(hdev->dev,
- "DRAM memory range 0x%llx (+0x%llx) is outside of DRAM boundaries\n",
- device_addr, size);
- return -EINVAL;
- }
-
- bar_address = hdev->dram_pci_bar_start + (device_addr - prop->dram_base_address);
-
- if ((bar_address + size) > (hdev->dram_pci_bar_start + prop->dram_pci_bar_size) ||
- (bar_address + size) < bar_address) {
- dev_dbg(hdev->dev,
- "DRAM memory range 0x%llx (+0x%llx) is outside of PCI BAR boundaries\n",
- device_addr, size);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int validate_export_params(struct hl_device *hdev, u64 device_addr, u64 size, u64 offset,
- struct hl_vm_phys_pg_pack *phys_pg_pack)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u64 bar_address;
- int i, rc;
-
- rc = validate_export_params_common(hdev, device_addr, size);
- if (rc)
- return rc;
-
- if ((offset + size) > phys_pg_pack->total_size) {
- dev_dbg(hdev->dev, "offset %#llx and size %#llx exceed total map size %#llx\n",
- offset, size, phys_pg_pack->total_size);
- return -EINVAL;
- }
-
- for (i = 0 ; i < phys_pg_pack->npages ; i++) {
-
- bar_address = hdev->dram_pci_bar_start +
- (phys_pg_pack->pages[i] - prop->dram_base_address);
-
- if ((bar_address + phys_pg_pack->page_size) >
- (hdev->dram_pci_bar_start + prop->dram_pci_bar_size) ||
- (bar_address + phys_pg_pack->page_size) < bar_address) {
- dev_dbg(hdev->dev,
- "DRAM memory range 0x%llx (+0x%x) is outside of PCI BAR boundaries\n",
- phys_pg_pack->pages[i],
- phys_pg_pack->page_size);
-
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-static struct hl_vm_hash_node *memhash_node_export_get(struct hl_ctx *ctx, u64 addr)
-{
- struct hl_device *hdev = ctx->hdev;
- struct hl_vm_hash_node *hnode;
-
- /* get the memory handle */
- mutex_lock(&ctx->mem_hash_lock);
- hash_for_each_possible(ctx->mem_hash, hnode, node, (unsigned long)addr)
- if (addr == hnode->vaddr)
- break;
-
- if (!hnode) {
- mutex_unlock(&ctx->mem_hash_lock);
- dev_dbg(hdev->dev, "map address %#llx not found\n", addr);
- return ERR_PTR(-EINVAL);
- }
-
- if (upper_32_bits(hnode->handle)) {
- mutex_unlock(&ctx->mem_hash_lock);
- dev_dbg(hdev->dev, "invalid handle %#llx for map address %#llx\n",
- hnode->handle, addr);
- return ERR_PTR(-EINVAL);
- }
-
- /*
- * node found, increase export count so this memory cannot be unmapped
- * and the hash node cannot be deleted.
- */
- hnode->export_cnt++;
- mutex_unlock(&ctx->mem_hash_lock);
-
- return hnode;
-}
-
-static void memhash_node_export_put(struct hl_ctx *ctx, struct hl_vm_hash_node *hnode)
-{
- mutex_lock(&ctx->mem_hash_lock);
- hnode->export_cnt--;
- mutex_unlock(&ctx->mem_hash_lock);
-}
-
-static struct hl_vm_phys_pg_pack *get_phys_pg_pack_from_hash_node(struct hl_device *hdev,
- struct hl_vm_hash_node *hnode)
-{
- struct hl_vm_phys_pg_pack *phys_pg_pack;
- struct hl_vm *vm = &hdev->vm;
-
- spin_lock(&vm->idr_lock);
- phys_pg_pack = idr_find(&vm->phys_pg_pack_handles, (u32) hnode->handle);
- if (!phys_pg_pack) {
- spin_unlock(&vm->idr_lock);
- dev_dbg(hdev->dev, "no match for handle 0x%x\n", (u32) hnode->handle);
- return ERR_PTR(-EINVAL);
- }
-
- spin_unlock(&vm->idr_lock);
-
- if (phys_pg_pack->vm_type != VM_TYPE_PHYS_PACK) {
- dev_dbg(hdev->dev, "handle 0x%llx does not represent DRAM memory\n", hnode->handle);
- return ERR_PTR(-EINVAL);
- }
-
- return phys_pg_pack;
-}
-
-/**
- * export_dmabuf_from_addr() - export a dma-buf object for the given memory
- * address and size.
- * @ctx: pointer to the context structure.
- * @addr: device address.
- * @size: size of device memory to export.
- * @offset: the offset into the buffer from which to start exporting
- * @flags: DMA-BUF file/FD flags.
- * @dmabuf_fd: pointer to result FD that represents the dma-buf object.
- *
- * Create and export a dma-buf object for an existing memory allocation inside
- * the device memory, and return a FD which is associated with the dma-buf
- * object.
- *
- * Return: 0 on success, non-zero for failure.
- */
-static int export_dmabuf_from_addr(struct hl_ctx *ctx, u64 addr, u64 size, u64 offset,
- int flags, int *dmabuf_fd)
-{
- struct hl_vm_phys_pg_pack *phys_pg_pack = NULL;
- struct hl_vm_hash_node *hnode = NULL;
- struct asic_fixed_properties *prop;
- struct hl_dmabuf_priv *hl_dmabuf;
- struct hl_device *hdev;
- u64 export_addr;
- int rc;
-
- hdev = ctx->hdev;
- prop = &hdev->asic_prop;
-
- /* offset must be 0 in devices without virtual memory support */
- if (!prop->dram_supports_virtual_memory && offset) {
- dev_dbg(hdev->dev, "offset is not allowed in device without virtual memory\n");
- return -EINVAL;
- }
-
- export_addr = addr + offset;
-
- hl_dmabuf = kzalloc(sizeof(*hl_dmabuf), GFP_KERNEL);
- if (!hl_dmabuf)
- return -ENOMEM;
-
- if (prop->dram_supports_virtual_memory) {
- hnode = memhash_node_export_get(ctx, addr);
- if (IS_ERR(hnode)) {
- rc = PTR_ERR(hnode);
- goto err_free_dmabuf_wrapper;
- }
- phys_pg_pack = get_phys_pg_pack_from_hash_node(hdev, hnode);
- if (IS_ERR(phys_pg_pack)) {
- rc = PTR_ERR(phys_pg_pack);
- goto dec_memhash_export_cnt;
- }
- rc = validate_export_params(hdev, export_addr, size, offset, phys_pg_pack);
- if (rc)
- goto dec_memhash_export_cnt;
-
- phys_pg_pack->exported_size = size;
- hl_dmabuf->phys_pg_pack = phys_pg_pack;
- hl_dmabuf->memhash_hnode = hnode;
- } else {
- rc = validate_export_params_no_mmu(hdev, export_addr, size);
- if (rc)
- goto err_free_dmabuf_wrapper;
- }
-
- hl_dmabuf->device_address = export_addr;
-
- rc = export_dmabuf(ctx, hl_dmabuf, size, flags, dmabuf_fd);
- if (rc)
- goto dec_memhash_export_cnt;
-
- return 0;
-
-dec_memhash_export_cnt:
- if (prop->dram_supports_virtual_memory)
- memhash_node_export_put(ctx, hnode);
-err_free_dmabuf_wrapper:
- kfree(hl_dmabuf);
- return rc;
-}
-
-static int mem_ioctl_no_mmu(struct hl_fpriv *hpriv, union hl_mem_args *args)
-{
- struct hl_device *hdev = hpriv->hdev;
- u64 block_handle, device_addr = 0;
- struct hl_ctx *ctx = hpriv->ctx;
- u32 handle = 0, block_size;
- int rc;
-
- switch (args->in.op) {
- case HL_MEM_OP_ALLOC:
- if (args->in.alloc.mem_size == 0) {
- dev_err(hdev->dev, "alloc size must be larger than 0\n");
- rc = -EINVAL;
- goto out;
- }
-
- /* Force contiguous as there are no real MMU
- * translations to overcome physical memory gaps
- */
- args->in.flags |= HL_MEM_CONTIGUOUS;
- rc = alloc_device_memory(ctx, &args->in, &handle);
-
- memset(args, 0, sizeof(*args));
- args->out.handle = (__u64) handle;
- break;
-
- case HL_MEM_OP_FREE:
- rc = free_device_memory(ctx, &args->in);
- break;
-
- case HL_MEM_OP_MAP:
- if (args->in.flags & HL_MEM_USERPTR) {
- dev_err(hdev->dev, "Failed to map host memory when MMU is disabled\n");
- rc = -EPERM;
- } else {
- rc = get_paddr_from_handle(ctx, &args->in, &device_addr);
- memset(args, 0, sizeof(*args));
- args->out.device_virt_addr = device_addr;
- }
-
- break;
-
- case HL_MEM_OP_UNMAP:
- rc = 0;
- break;
-
- case HL_MEM_OP_MAP_BLOCK:
- rc = map_block(hdev, args->in.map_block.block_addr, &block_handle, &block_size);
- args->out.block_handle = block_handle;
- args->out.block_size = block_size;
- break;
-
- case HL_MEM_OP_EXPORT_DMABUF_FD:
- dev_err(hdev->dev, "Failed to export dma-buf object when MMU is disabled\n");
- rc = -EPERM;
- break;
-
- case HL_MEM_OP_TS_ALLOC:
- rc = allocate_timestamps_buffers(hpriv, &args->in, &args->out.handle);
- break;
- default:
- dev_err(hdev->dev, "Unknown opcode for memory IOCTL\n");
- rc = -EINVAL;
- break;
- }
-
-out:
- return rc;
-}
-
-static void ts_buff_release(struct hl_mmap_mem_buf *buf)
-{
- struct hl_ts_buff *ts_buff = buf->private;
-
- vfree(ts_buff->kernel_buff_address);
- vfree(ts_buff->user_buff_address);
- kfree(ts_buff);
-}
-
-static int hl_ts_mmap(struct hl_mmap_mem_buf *buf, struct vm_area_struct *vma, void *args)
-{
- struct hl_ts_buff *ts_buff = buf->private;
-
- vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_DONTCOPY | VM_NORESERVE;
- return remap_vmalloc_range(vma, ts_buff->user_buff_address, 0);
-}
-
-static int hl_ts_alloc_buf(struct hl_mmap_mem_buf *buf, gfp_t gfp, void *args)
-{
- struct hl_ts_buff *ts_buff = NULL;
- u32 size, num_elements;
- void *p;
-
- num_elements = *(u32 *)args;
-
- ts_buff = kzalloc(sizeof(*ts_buff), GFP_KERNEL);
- if (!ts_buff)
- return -ENOMEM;
-
- /* Allocate the user buffer */
- size = num_elements * sizeof(u64);
- p = vmalloc_user(size);
- if (!p)
- goto free_mem;
-
- ts_buff->user_buff_address = p;
- buf->mappable_size = size;
-
- /* Allocate the internal kernel buffer */
- size = num_elements * sizeof(struct hl_user_pending_interrupt);
- p = vzalloc(size);
- if (!p)
- goto free_user_buff;
-
- ts_buff->kernel_buff_address = p;
- ts_buff->kernel_buff_size = size;
-
- buf->private = ts_buff;
-
- return 0;
-
-free_user_buff:
- vfree(ts_buff->user_buff_address);
-free_mem:
- kfree(ts_buff);
- return -ENOMEM;
-}
-
-static struct hl_mmap_mem_buf_behavior hl_ts_behavior = {
- .topic = "TS",
- .mem_id = HL_MMAP_TYPE_TS_BUFF,
- .mmap = hl_ts_mmap,
- .alloc = hl_ts_alloc_buf,
- .release = ts_buff_release,
-};
-
-/**
- * allocate_timestamps_buffers() - allocate timestamps buffers
- * This function will allocate ts buffer that will later on be mapped to the user
- * in order to be able to read the timestamp.
- * in additon it'll allocate an extra buffer for registration management.
- * since we cannot fail during registration for out-of-memory situation, so
- * we'll prepare a pool which will be used as user interrupt nodes and instead
- * of dynamically allocating nodes while registration we'll pick the node from
- * this pool. in addtion it'll add node to the mapping hash which will be used
- * to map user ts buffer to the internal kernel ts buffer.
- * @hpriv: pointer to the private data of the fd
- * @args: ioctl input
- * @handle: user timestamp buffer handle as an output
- */
-static int allocate_timestamps_buffers(struct hl_fpriv *hpriv, struct hl_mem_in *args, u64 *handle)
-{
- struct hl_mem_mgr *mmg = &hpriv->mem_mgr;
- struct hl_mmap_mem_buf *buf;
-
- if (args->num_of_elements > TS_MAX_ELEMENTS_NUM) {
- dev_err(mmg->dev, "Num of elements exceeds Max allowed number (0x%x > 0x%x)\n",
- args->num_of_elements, TS_MAX_ELEMENTS_NUM);
- return -EINVAL;
- }
-
- buf = hl_mmap_mem_buf_alloc(mmg, &hl_ts_behavior, GFP_KERNEL, &args->num_of_elements);
- if (!buf)
- return -ENOMEM;
-
- *handle = buf->handle;
-
- return 0;
-}
-
-int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data)
-{
- enum hl_device_status status;
- union hl_mem_args *args = data;
- struct hl_device *hdev = hpriv->hdev;
- struct hl_ctx *ctx = hpriv->ctx;
- u64 block_handle, device_addr = 0;
- u32 handle = 0, block_size;
- int rc, dmabuf_fd = -EBADF;
-
- if (!hl_device_operational(hdev, &status)) {
- dev_warn_ratelimited(hdev->dev,
- "Device is %s. Can't execute MEMORY IOCTL\n",
- hdev->status[status]);
- return -EBUSY;
- }
-
- if (!hdev->mmu_enable)
- return mem_ioctl_no_mmu(hpriv, args);
-
- switch (args->in.op) {
- case HL_MEM_OP_ALLOC:
- if (args->in.alloc.mem_size == 0) {
- dev_err(hdev->dev,
- "alloc size must be larger than 0\n");
- rc = -EINVAL;
- goto out;
- }
-
- /* If DRAM does not support virtual memory the driver won't
- * handle the allocation/freeing of that memory. However, for
- * system administration/monitoring purposes, the driver will
- * keep track of the amount of DRAM memory that is allocated
- * and freed by the user. Because this code totally relies on
- * the user's input, the driver can't ensure the validity
- * of this accounting.
- */
- if (!hdev->asic_prop.dram_supports_virtual_memory) {
- atomic64_add(args->in.alloc.mem_size,
- &ctx->dram_phys_mem);
- atomic64_add(args->in.alloc.mem_size,
- &hdev->dram_used_mem);
-
- dev_dbg(hdev->dev, "DRAM alloc is not supported\n");
- rc = 0;
-
- memset(args, 0, sizeof(*args));
- args->out.handle = 0;
- goto out;
- }
-
- rc = alloc_device_memory(ctx, &args->in, &handle);
-
- memset(args, 0, sizeof(*args));
- args->out.handle = (__u64) handle;
- break;
-
- case HL_MEM_OP_FREE:
- /* If DRAM does not support virtual memory the driver won't
- * handle the allocation/freeing of that memory. However, for
- * system administration/monitoring purposes, the driver will
- * keep track of the amount of DRAM memory that is allocated
- * and freed by the user. Because this code totally relies on
- * the user's input, the driver can't ensure the validity
- * of this accounting.
- */
- if (!hdev->asic_prop.dram_supports_virtual_memory) {
- atomic64_sub(args->in.alloc.mem_size,
- &ctx->dram_phys_mem);
- atomic64_sub(args->in.alloc.mem_size,
- &hdev->dram_used_mem);
-
- dev_dbg(hdev->dev, "DRAM alloc is not supported\n");
- rc = 0;
-
- goto out;
- }
-
- rc = free_device_memory(ctx, &args->in);
- break;
-
- case HL_MEM_OP_MAP:
- rc = map_device_va(ctx, &args->in, &device_addr);
-
- memset(args, 0, sizeof(*args));
- args->out.device_virt_addr = device_addr;
- break;
-
- case HL_MEM_OP_UNMAP:
- rc = unmap_device_va(ctx, &args->in, false);
- break;
-
- case HL_MEM_OP_MAP_BLOCK:
- rc = map_block(hdev, args->in.map_block.block_addr,
- &block_handle, &block_size);
- args->out.block_handle = block_handle;
- args->out.block_size = block_size;
- break;
-
- case HL_MEM_OP_EXPORT_DMABUF_FD:
- rc = export_dmabuf_from_addr(ctx,
- args->in.export_dmabuf_fd.addr,
- args->in.export_dmabuf_fd.mem_size,
- args->in.export_dmabuf_fd.offset,
- args->in.flags,
- &dmabuf_fd);
- memset(args, 0, sizeof(*args));
- args->out.fd = dmabuf_fd;
- break;
-
- case HL_MEM_OP_TS_ALLOC:
- rc = allocate_timestamps_buffers(hpriv, &args->in, &args->out.handle);
- break;
- default:
- dev_err(hdev->dev, "Unknown opcode for memory IOCTL\n");
- rc = -EINVAL;
- break;
- }
-
-out:
- return rc;
-}
-
-static int get_user_memory(struct hl_device *hdev, u64 addr, u64 size,
- u32 npages, u64 start, u32 offset,
- struct hl_userptr *userptr)
-{
- int rc;
-
- if (!access_ok((void __user *) (uintptr_t) addr, size)) {
- dev_err(hdev->dev, "user pointer is invalid - 0x%llx\n", addr);
- return -EFAULT;
- }
-
- userptr->pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
- if (!userptr->pages)
- return -ENOMEM;
-
- rc = pin_user_pages_fast(start, npages, FOLL_WRITE | FOLL_LONGTERM,
- userptr->pages);
-
- if (rc != npages) {
- dev_err(hdev->dev,
- "Failed (%d) to pin host memory with user ptr 0x%llx, size 0x%llx, npages %d\n",
- rc, addr, size, npages);
- if (rc < 0)
- goto destroy_pages;
- npages = rc;
- rc = -EFAULT;
- goto put_pages;
- }
- userptr->npages = npages;
-
- rc = sg_alloc_table_from_pages(userptr->sgt,
- userptr->pages,
- npages, offset, size, GFP_KERNEL);
- if (rc < 0) {
- dev_err(hdev->dev, "failed to create SG table from pages\n");
- goto put_pages;
- }
-
- return 0;
-
-put_pages:
- unpin_user_pages(userptr->pages, npages);
-destroy_pages:
- kvfree(userptr->pages);
- return rc;
-}
-
-/**
- * hl_pin_host_memory() - pins a chunk of host memory.
- * @hdev: pointer to the habanalabs device structure.
- * @addr: the host virtual address of the memory area.
- * @size: the size of the memory area.
- * @userptr: pointer to hl_userptr structure.
- *
- * This function does the following:
- * - Pins the physical pages.
- * - Create an SG list from those pages.
- */
-int hl_pin_host_memory(struct hl_device *hdev, u64 addr, u64 size,
- struct hl_userptr *userptr)
-{
- u64 start, end;
- u32 npages, offset;
- int rc;
-
- if (!size) {
- dev_err(hdev->dev, "size to pin is invalid - %llu\n", size);
- return -EINVAL;
- }
-
- /*
- * If the combination of the address and size requested for this memory
- * region causes an integer overflow, return error.
- */
- if (((addr + size) < addr) ||
- PAGE_ALIGN(addr + size) < (addr + size)) {
- dev_err(hdev->dev,
- "user pointer 0x%llx + %llu causes integer overflow\n",
- addr, size);
- return -EINVAL;
- }
-
- userptr->pid = current->pid;
- userptr->sgt = kzalloc(sizeof(*userptr->sgt), GFP_KERNEL);
- if (!userptr->sgt)
- return -ENOMEM;
-
- start = addr & PAGE_MASK;
- offset = addr & ~PAGE_MASK;
- end = PAGE_ALIGN(addr + size);
- npages = (end - start) >> PAGE_SHIFT;
-
- userptr->size = size;
- userptr->addr = addr;
- userptr->dma_mapped = false;
- INIT_LIST_HEAD(&userptr->job_node);
-
- rc = get_user_memory(hdev, addr, size, npages, start, offset,
- userptr);
- if (rc) {
- dev_err(hdev->dev,
- "failed to get user memory for address 0x%llx\n",
- addr);
- goto free_sgt;
- }
-
- hl_debugfs_add_userptr(hdev, userptr);
-
- return 0;
-
-free_sgt:
- kfree(userptr->sgt);
- return rc;
-}
-
-/*
- * hl_unpin_host_memory - unpins a chunk of host memory.
- * @hdev: pointer to the habanalabs device structure
- * @userptr: pointer to hl_userptr structure
- *
- * This function does the following:
- * - Unpins the physical pages related to the host memory
- * - Free the SG list
- */
-void hl_unpin_host_memory(struct hl_device *hdev, struct hl_userptr *userptr)
-{
- hl_debugfs_remove_userptr(hdev, userptr);
-
- if (userptr->dma_mapped)
- hdev->asic_funcs->hl_dma_unmap_sgtable(hdev, userptr->sgt, userptr->dir);
-
- unpin_user_pages_dirty_lock(userptr->pages, userptr->npages, true);
- kvfree(userptr->pages);
-
- list_del(&userptr->job_node);
-
- sg_free_table(userptr->sgt);
- kfree(userptr->sgt);
-}
-
-/**
- * hl_userptr_delete_list() - clear userptr list.
- * @hdev: pointer to the habanalabs device structure.
- * @userptr_list: pointer to the list to clear.
- *
- * This function does the following:
- * - Iterates over the list and unpins the host memory and frees the userptr
- * structure.
- */
-void hl_userptr_delete_list(struct hl_device *hdev,
- struct list_head *userptr_list)
-{
- struct hl_userptr *userptr, *tmp;
-
- list_for_each_entry_safe(userptr, tmp, userptr_list, job_node) {
- hl_unpin_host_memory(hdev, userptr);
- kfree(userptr);
- }
-
- INIT_LIST_HEAD(userptr_list);
-}
-
-/**
- * hl_userptr_is_pinned() - returns whether the given userptr is pinned.
- * @hdev: pointer to the habanalabs device structure.
- * @addr: user address to check.
- * @size: user block size to check.
- * @userptr_list: pointer to the list to clear.
- * @userptr: pointer to userptr to check.
- *
- * This function does the following:
- * - Iterates over the list and checks if the given userptr is in it, means is
- * pinned. If so, returns true, otherwise returns false.
- */
-bool hl_userptr_is_pinned(struct hl_device *hdev, u64 addr,
- u32 size, struct list_head *userptr_list,
- struct hl_userptr **userptr)
-{
- list_for_each_entry((*userptr), userptr_list, job_node) {
- if ((addr == (*userptr)->addr) && (size == (*userptr)->size))
- return true;
- }
-
- return false;
-}
-
-/**
- * va_range_init() - initialize virtual addresses range.
- * @hdev: pointer to the habanalabs device structure.
- * @va_ranges: pointer to va_ranges array.
- * @range_type: virtual address range type.
- * @start: range start address, inclusive.
- * @end: range end address, inclusive.
- * @page_size: page size for this va_range.
- *
- * This function does the following:
- * - Initializes the virtual addresses list of the given range with the given
- * addresses.
- */
-static int va_range_init(struct hl_device *hdev, struct hl_va_range **va_ranges,
- enum hl_va_range_type range_type, u64 start,
- u64 end, u32 page_size)
-{
- struct hl_va_range *va_range = va_ranges[range_type];
- int rc;
-
- INIT_LIST_HEAD(&va_range->list);
-
- /*
- * PAGE_SIZE alignment
- * it is the caller's responsibility to align the addresses if the
- * page size is not a power of 2
- */
-
- if (is_power_of_2(page_size)) {
- start = round_up(start, page_size);
-
- /*
- * The end of the range is inclusive, hence we need to align it
- * to the end of the last full page in the range. For example if
- * end = 0x3ff5 with page size 0x1000, we need to align it to
- * 0x2fff. The remaining 0xff5 bytes do not form a full page.
- */
- end = round_down(end + 1, page_size) - 1;
- }
-
- if (start >= end) {
- dev_err(hdev->dev, "too small vm range for va list\n");
- return -EFAULT;
- }
-
- rc = add_va_block(hdev, va_range, start, end);
-
- if (rc) {
- dev_err(hdev->dev, "Failed to init host va list\n");
- return rc;
- }
-
- va_range->start_addr = start;
- va_range->end_addr = end;
- va_range->page_size = page_size;
-
- return 0;
-}
-
-/**
- * va_range_fini() - clear a virtual addresses range.
- * @hdev: pointer to the habanalabs structure.
- * @va_range: pointer to virtual addresses range.
- *
- * This function does the following:
- * - Frees the virtual addresses block list and its lock.
- */
-static void va_range_fini(struct hl_device *hdev, struct hl_va_range *va_range)
-{
- mutex_lock(&va_range->lock);
- clear_va_list_locked(hdev, &va_range->list);
- mutex_unlock(&va_range->lock);
-
- mutex_destroy(&va_range->lock);
- kfree(va_range);
-}
-
-/**
- * vm_ctx_init_with_ranges() - initialize virtual memory for context.
- * @ctx: pointer to the habanalabs context structure.
- * @host_range_start: host virtual addresses range start.
- * @host_range_end: host virtual addresses range end.
- * @host_page_size: host page size.
- * @host_huge_range_start: host virtual addresses range start for memory
- * allocated with huge pages.
- * @host_huge_range_end: host virtual addresses range end for memory allocated
- * with huge pages.
- * @host_huge_page_size: host huge page size.
- * @dram_range_start: dram virtual addresses range start.
- * @dram_range_end: dram virtual addresses range end.
- * @dram_page_size: dram page size.
- *
- * This function initializes the following:
- * - MMU for context.
- * - Virtual address to area descriptor hashtable.
- * - Virtual block list of available virtual memory.
- */
-static int vm_ctx_init_with_ranges(struct hl_ctx *ctx,
- u64 host_range_start,
- u64 host_range_end,
- u32 host_page_size,
- u64 host_huge_range_start,
- u64 host_huge_range_end,
- u32 host_huge_page_size,
- u64 dram_range_start,
- u64 dram_range_end,
- u32 dram_page_size)
-{
- struct hl_device *hdev = ctx->hdev;
- int i, rc;
-
- for (i = 0 ; i < HL_VA_RANGE_TYPE_MAX ; i++) {
- ctx->va_range[i] =
- kzalloc(sizeof(struct hl_va_range), GFP_KERNEL);
- if (!ctx->va_range[i]) {
- rc = -ENOMEM;
- goto free_va_range;
- }
- }
-
- rc = hl_mmu_ctx_init(ctx);
- if (rc) {
- dev_err(hdev->dev, "failed to init context %d\n", ctx->asid);
- goto free_va_range;
- }
-
- mutex_init(&ctx->mem_hash_lock);
- hash_init(ctx->mem_hash);
-
- mutex_init(&ctx->va_range[HL_VA_RANGE_TYPE_HOST]->lock);
-
- rc = va_range_init(hdev, ctx->va_range, HL_VA_RANGE_TYPE_HOST,
- host_range_start, host_range_end, host_page_size);
- if (rc) {
- dev_err(hdev->dev, "failed to init host vm range\n");
- goto mmu_ctx_fini;
- }
-
- if (hdev->pmmu_huge_range) {
- mutex_init(&ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE]->lock);
-
- rc = va_range_init(hdev,
- ctx->va_range, HL_VA_RANGE_TYPE_HOST_HUGE,
- host_huge_range_start, host_huge_range_end,
- host_huge_page_size);
- if (rc) {
- dev_err(hdev->dev,
- "failed to init host huge vm range\n");
- goto clear_host_va_range;
- }
- } else {
- kfree(ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE]);
- ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE] =
- ctx->va_range[HL_VA_RANGE_TYPE_HOST];
- }
-
- mutex_init(&ctx->va_range[HL_VA_RANGE_TYPE_DRAM]->lock);
-
- rc = va_range_init(hdev, ctx->va_range, HL_VA_RANGE_TYPE_DRAM,
- dram_range_start, dram_range_end, dram_page_size);
- if (rc) {
- dev_err(hdev->dev, "failed to init dram vm range\n");
- goto clear_host_huge_va_range;
- }
-
- hl_debugfs_add_ctx_mem_hash(hdev, ctx);
-
- return 0;
-
-clear_host_huge_va_range:
- mutex_destroy(&ctx->va_range[HL_VA_RANGE_TYPE_DRAM]->lock);
-
- if (hdev->pmmu_huge_range) {
- mutex_lock(&ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE]->lock);
- clear_va_list_locked(hdev,
- &ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE]->list);
- mutex_unlock(&ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE]->lock);
- }
-clear_host_va_range:
- if (hdev->pmmu_huge_range)
- mutex_destroy(&ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE]->lock);
- mutex_lock(&ctx->va_range[HL_VA_RANGE_TYPE_HOST]->lock);
- clear_va_list_locked(hdev, &ctx->va_range[HL_VA_RANGE_TYPE_HOST]->list);
- mutex_unlock(&ctx->va_range[HL_VA_RANGE_TYPE_HOST]->lock);
-mmu_ctx_fini:
- mutex_destroy(&ctx->va_range[HL_VA_RANGE_TYPE_HOST]->lock);
- mutex_destroy(&ctx->mem_hash_lock);
- hl_mmu_ctx_fini(ctx);
-free_va_range:
- for (i = 0 ; i < HL_VA_RANGE_TYPE_MAX ; i++)
- kfree(ctx->va_range[i]);
-
- return rc;
-}
-
-int hl_vm_ctx_init(struct hl_ctx *ctx)
-{
- struct asic_fixed_properties *prop = &ctx->hdev->asic_prop;
- u64 host_range_start, host_range_end, host_huge_range_start,
- host_huge_range_end, dram_range_start, dram_range_end;
- u32 host_page_size, host_huge_page_size, dram_page_size;
-
- atomic64_set(&ctx->dram_phys_mem, 0);
-
- /*
- * - If MMU is enabled, init the ranges as usual.
- * - If MMU is disabled, in case of host mapping, the returned address
- * is the given one.
- * In case of DRAM mapping, the returned address is the physical
- * address of the memory related to the given handle.
- */
- if (!ctx->hdev->mmu_enable)
- return 0;
-
- dram_range_start = prop->dmmu.start_addr;
- dram_range_end = prop->dmmu.end_addr - 1;
- dram_page_size = prop->dram_page_size ?
- prop->dram_page_size : prop->dmmu.page_size;
- host_range_start = prop->pmmu.start_addr;
- host_range_end = prop->pmmu.end_addr - 1;
- host_page_size = prop->pmmu.page_size;
- host_huge_range_start = prop->pmmu_huge.start_addr;
- host_huge_range_end = prop->pmmu_huge.end_addr - 1;
- host_huge_page_size = prop->pmmu_huge.page_size;
-
- return vm_ctx_init_with_ranges(ctx, host_range_start, host_range_end,
- host_page_size, host_huge_range_start,
- host_huge_range_end, host_huge_page_size,
- dram_range_start, dram_range_end, dram_page_size);
-}
-
-/**
- * hl_vm_ctx_fini() - virtual memory teardown of context.
- * @ctx: pointer to the habanalabs context structure.
- *
- * This function perform teardown the following:
- * - Virtual block list of available virtual memory.
- * - Virtual address to area descriptor hashtable.
- * - MMU for context.
- *
- * In addition this function does the following:
- * - Unmaps the existing hashtable nodes if the hashtable is not empty. The
- * hashtable should be empty as no valid mappings should exist at this
- * point.
- * - Frees any existing physical page list from the idr which relates to the
- * current context asid.
- * - This function checks the virtual block list for correctness. At this point
- * the list should contain one element which describes the whole virtual
- * memory range of the context. Otherwise, a warning is printed.
- */
-void hl_vm_ctx_fini(struct hl_ctx *ctx)
-{
- struct hl_vm_phys_pg_pack *phys_pg_list, *tmp_phys_node;
- struct hl_device *hdev = ctx->hdev;
- struct hl_vm_hash_node *hnode;
- struct hl_vm *vm = &hdev->vm;
- struct hlist_node *tmp_node;
- struct list_head free_list;
- struct hl_mem_in args;
- int i;
-
- if (!hdev->mmu_enable)
- return;
-
- hl_debugfs_remove_ctx_mem_hash(hdev, ctx);
-
- /*
- * Clearly something went wrong on hard reset so no point in printing
- * another side effect error
- */
- if (!hdev->reset_info.hard_reset_pending && !hash_empty(ctx->mem_hash))
- dev_dbg(hdev->dev,
- "user released device without removing its memory mappings\n");
-
- hash_for_each_safe(ctx->mem_hash, i, tmp_node, hnode, node) {
- dev_dbg(hdev->dev,
- "hl_mem_hash_node of vaddr 0x%llx of asid %d is still alive\n",
- hnode->vaddr, ctx->asid);
- args.unmap.device_virt_addr = hnode->vaddr;
- unmap_device_va(ctx, &args, true);
- }
-
- mutex_lock(&hdev->mmu_lock);
-
- /* invalidate the cache once after the unmapping loop */
- hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR);
- hl_mmu_invalidate_cache(hdev, true, MMU_OP_PHYS_PACK);
-
- mutex_unlock(&hdev->mmu_lock);
-
- INIT_LIST_HEAD(&free_list);
-
- spin_lock(&vm->idr_lock);
- idr_for_each_entry(&vm->phys_pg_pack_handles, phys_pg_list, i)
- if (phys_pg_list->asid == ctx->asid) {
- dev_dbg(hdev->dev,
- "page list 0x%px of asid %d is still alive\n",
- phys_pg_list, ctx->asid);
-
- atomic64_sub(phys_pg_list->total_size, &hdev->dram_used_mem);
- idr_remove(&vm->phys_pg_pack_handles, i);
- list_add(&phys_pg_list->node, &free_list);
- }
- spin_unlock(&vm->idr_lock);
-
- list_for_each_entry_safe(phys_pg_list, tmp_phys_node, &free_list, node)
- free_phys_pg_pack(hdev, phys_pg_list);
-
- va_range_fini(hdev, ctx->va_range[HL_VA_RANGE_TYPE_DRAM]);
- va_range_fini(hdev, ctx->va_range[HL_VA_RANGE_TYPE_HOST]);
-
- if (hdev->pmmu_huge_range)
- va_range_fini(hdev, ctx->va_range[HL_VA_RANGE_TYPE_HOST_HUGE]);
-
- mutex_destroy(&ctx->mem_hash_lock);
- hl_mmu_ctx_fini(ctx);
-
- /* In this case we need to clear the global accounting of DRAM usage
- * because the user notifies us on allocations. If the user is no more,
- * all DRAM is available
- */
- if (ctx->asid != HL_KERNEL_ASID_ID &&
- !hdev->asic_prop.dram_supports_virtual_memory)
- atomic64_set(&hdev->dram_used_mem, 0);
-}
-
-/**
- * hl_vm_init() - initialize virtual memory module.
- * @hdev: pointer to the habanalabs device structure.
- *
- * This function initializes the following:
- * - MMU module.
- * - DRAM physical pages pool of 2MB.
- * - Idr for device memory allocation handles.
- */
-int hl_vm_init(struct hl_device *hdev)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct hl_vm *vm = &hdev->vm;
- int rc;
-
- if (is_power_of_2(prop->dram_page_size))
- vm->dram_pg_pool =
- gen_pool_create(__ffs(prop->dram_page_size), -1);
- else
- vm->dram_pg_pool =
- gen_pool_create(__ffs(DRAM_POOL_PAGE_SIZE), -1);
-
- if (!vm->dram_pg_pool) {
- dev_err(hdev->dev, "Failed to create dram page pool\n");
- return -ENOMEM;
- }
-
- kref_init(&vm->dram_pg_pool_refcount);
-
- rc = gen_pool_add(vm->dram_pg_pool, prop->dram_user_base_address,
- prop->dram_end_address - prop->dram_user_base_address,
- -1);
-
- if (rc) {
- dev_err(hdev->dev,
- "Failed to add memory to dram page pool %d\n", rc);
- goto pool_add_err;
- }
-
- spin_lock_init(&vm->idr_lock);
- idr_init(&vm->phys_pg_pack_handles);
-
- atomic64_set(&hdev->dram_used_mem, 0);
-
- vm->init_done = true;
-
- return 0;
-
-pool_add_err:
- gen_pool_destroy(vm->dram_pg_pool);
-
- return rc;
-}
-
-/**
- * hl_vm_fini() - virtual memory module teardown.
- * @hdev: pointer to the habanalabs device structure.
- *
- * This function perform teardown to the following:
- * - Idr for device memory allocation handles.
- * - DRAM physical pages pool of 2MB.
- * - MMU module.
- */
-void hl_vm_fini(struct hl_device *hdev)
-{
- struct hl_vm *vm = &hdev->vm;
-
- if (!vm->init_done)
- return;
-
- /*
- * At this point all the contexts should be freed and hence no DRAM
- * memory should be in use. Hence the DRAM pool should be freed here.
- */
- if (kref_put(&vm->dram_pg_pool_refcount, dram_pg_pool_do_release) != 1)
- dev_warn(hdev->dev, "dram_pg_pool was not destroyed on %s\n",
- __func__);
-
- vm->init_done = false;
-}
-
-/**
- * hl_hw_block_mem_init() - HW block memory initialization.
- * @ctx: pointer to the habanalabs context structure.
- *
- * This function initializes the HW block virtual mapped addresses list and
- * it's lock.
- */
-void hl_hw_block_mem_init(struct hl_ctx *ctx)
-{
- mutex_init(&ctx->hw_block_list_lock);
- INIT_LIST_HEAD(&ctx->hw_block_mem_list);
-}
-
-/**
- * hl_hw_block_mem_fini() - HW block memory teardown.
- * @ctx: pointer to the habanalabs context structure.
- *
- * This function clears the HW block virtual mapped addresses list and destroys
- * it's lock.
- */
-void hl_hw_block_mem_fini(struct hl_ctx *ctx)
-{
- struct hl_vm_hw_block_list_node *lnode, *tmp;
-
- if (!list_empty(&ctx->hw_block_mem_list))
- dev_crit(ctx->hdev->dev, "HW block mem list isn't empty\n");
-
- list_for_each_entry_safe(lnode, tmp, &ctx->hw_block_mem_list, node) {
- list_del(&lnode->node);
- kfree(lnode);
- }
-
- mutex_destroy(&ctx->hw_block_list_lock);
-}
diff --git a/drivers/misc/habanalabs/common/memory_mgr.c b/drivers/misc/habanalabs/common/memory_mgr.c
deleted file mode 100644
index 92d20ed465b4..000000000000
--- a/drivers/misc/habanalabs/common/memory_mgr.c
+++ /dev/null
@@ -1,350 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2022 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include "habanalabs.h"
-
-/**
- * hl_mmap_mem_buf_get - increase the buffer refcount and return a pointer to
- * the buffer descriptor.
- *
- * @mmg: parent unified memory manager
- * @handle: requested buffer handle
- *
- * Find the buffer in the store and return a pointer to its descriptor.
- * Increase buffer refcount. If not found - return NULL.
- */
-struct hl_mmap_mem_buf *hl_mmap_mem_buf_get(struct hl_mem_mgr *mmg, u64 handle)
-{
- struct hl_mmap_mem_buf *buf;
-
- spin_lock(&mmg->lock);
- buf = idr_find(&mmg->handles, lower_32_bits(handle >> PAGE_SHIFT));
- if (!buf) {
- spin_unlock(&mmg->lock);
- dev_dbg(mmg->dev, "Buff get failed, no match to handle %#llx\n", handle);
- return NULL;
- }
- kref_get(&buf->refcount);
- spin_unlock(&mmg->lock);
- return buf;
-}
-
-/**
- * hl_mmap_mem_buf_destroy - destroy the unused buffer
- *
- * @buf: memory manager buffer descriptor
- *
- * Internal function, used as a final step of buffer release. Shall be invoked
- * only when the buffer is no longer in use (removed from idr). Will call the
- * release callback (if applicable), and free the memory.
- */
-static void hl_mmap_mem_buf_destroy(struct hl_mmap_mem_buf *buf)
-{
- if (buf->behavior->release)
- buf->behavior->release(buf);
-
- kfree(buf);
-}
-
-/**
- * hl_mmap_mem_buf_release - release buffer
- *
- * @kref: kref that reached 0.
- *
- * Internal function, used as a kref release callback, when the last user of
- * the buffer is released. Shall be called from an interrupt context.
- */
-static void hl_mmap_mem_buf_release(struct kref *kref)
-{
- struct hl_mmap_mem_buf *buf =
- container_of(kref, struct hl_mmap_mem_buf, refcount);
-
- spin_lock(&buf->mmg->lock);
- idr_remove(&buf->mmg->handles, lower_32_bits(buf->handle >> PAGE_SHIFT));
- spin_unlock(&buf->mmg->lock);
-
- hl_mmap_mem_buf_destroy(buf);
-}
-
-/**
- * hl_mmap_mem_buf_remove_idr_locked - remove handle from idr
- *
- * @kref: kref that reached 0.
- *
- * Internal function, used for kref put by handle. Assumes mmg lock is taken.
- * Will remove the buffer from idr, without destroying it.
- */
-static void hl_mmap_mem_buf_remove_idr_locked(struct kref *kref)
-{
- struct hl_mmap_mem_buf *buf =
- container_of(kref, struct hl_mmap_mem_buf, refcount);
-
- idr_remove(&buf->mmg->handles, lower_32_bits(buf->handle >> PAGE_SHIFT));
-}
-
-/**
- * hl_mmap_mem_buf_put - decrease the reference to the buffer
- *
- * @buf: memory manager buffer descriptor
- *
- * Decrease the reference to the buffer, and release it if it was the last one.
- * Shall be called from an interrupt context.
- */
-int hl_mmap_mem_buf_put(struct hl_mmap_mem_buf *buf)
-{
- return kref_put(&buf->refcount, hl_mmap_mem_buf_release);
-}
-
-/**
- * hl_mmap_mem_buf_put_handle - decrease the reference to the buffer with the
- * given handle.
- *
- * @mmg: parent unified memory manager
- * @handle: requested buffer handle
- *
- * Decrease the reference to the buffer, and release it if it was the last one.
- * Shall not be called from an interrupt context. Return -EINVAL if handle was
- * not found, else return the put outcome (0 or 1).
- */
-int hl_mmap_mem_buf_put_handle(struct hl_mem_mgr *mmg, u64 handle)
-{
- struct hl_mmap_mem_buf *buf;
-
- spin_lock(&mmg->lock);
- buf = idr_find(&mmg->handles, lower_32_bits(handle >> PAGE_SHIFT));
- if (!buf) {
- spin_unlock(&mmg->lock);
- dev_dbg(mmg->dev,
- "Buff put failed, no match to handle %#llx\n", handle);
- return -EINVAL;
- }
-
- if (kref_put(&buf->refcount, hl_mmap_mem_buf_remove_idr_locked)) {
- spin_unlock(&mmg->lock);
- hl_mmap_mem_buf_destroy(buf);
- return 1;
- }
-
- spin_unlock(&mmg->lock);
- return 0;
-}
-
-/**
- * hl_mmap_mem_buf_alloc - allocate a new mappable buffer
- *
- * @mmg: parent unified memory manager
- * @behavior: behavior object describing this buffer polymorphic behavior
- * @gfp: gfp flags to use for the memory allocations
- * @args: additional args passed to behavior->alloc
- *
- * Allocate and register a new memory buffer inside the give memory manager.
- * Return the pointer to the new buffer on success or NULL on failure.
- */
-struct hl_mmap_mem_buf *
-hl_mmap_mem_buf_alloc(struct hl_mem_mgr *mmg,
- struct hl_mmap_mem_buf_behavior *behavior, gfp_t gfp,
- void *args)
-{
- struct hl_mmap_mem_buf *buf;
- int rc;
-
- buf = kzalloc(sizeof(*buf), gfp);
- if (!buf)
- return NULL;
-
- spin_lock(&mmg->lock);
- rc = idr_alloc(&mmg->handles, buf, 1, 0, GFP_ATOMIC);
- spin_unlock(&mmg->lock);
- if (rc < 0) {
- dev_err(mmg->dev,
- "%s: Failed to allocate IDR for a new buffer, rc=%d\n",
- behavior->topic, rc);
- goto free_buf;
- }
-
- buf->mmg = mmg;
- buf->behavior = behavior;
- buf->handle = (((u64)rc | buf->behavior->mem_id) << PAGE_SHIFT);
- kref_init(&buf->refcount);
-
- rc = buf->behavior->alloc(buf, gfp, args);
- if (rc) {
- dev_err(mmg->dev, "%s: Failure in buffer alloc callback %d\n",
- behavior->topic, rc);
- goto remove_idr;
- }
-
- return buf;
-
-remove_idr:
- spin_lock(&mmg->lock);
- idr_remove(&mmg->handles, lower_32_bits(buf->handle >> PAGE_SHIFT));
- spin_unlock(&mmg->lock);
-free_buf:
- kfree(buf);
- return NULL;
-}
-
-/**
- * hl_mmap_mem_buf_vm_close - handle mmap close
- *
- * @vma: the vma object for which mmap was closed.
- *
- * Put the memory buffer if it is no longer mapped.
- */
-static void hl_mmap_mem_buf_vm_close(struct vm_area_struct *vma)
-{
- struct hl_mmap_mem_buf *buf =
- (struct hl_mmap_mem_buf *)vma->vm_private_data;
- long new_mmap_size;
-
- new_mmap_size = buf->real_mapped_size - (vma->vm_end - vma->vm_start);
-
- if (new_mmap_size > 0) {
- buf->real_mapped_size = new_mmap_size;
- return;
- }
-
- atomic_set(&buf->mmap, 0);
- hl_mmap_mem_buf_put(buf);
- vma->vm_private_data = NULL;
-}
-
-static const struct vm_operations_struct hl_mmap_mem_buf_vm_ops = {
- .close = hl_mmap_mem_buf_vm_close
-};
-
-/**
- * hl_mem_mgr_mmap - map the given buffer to the user
- *
- * @mmg: unified memory manager
- * @vma: the vma object for which mmap was closed.
- * @args: additional args passed to behavior->mmap
- *
- * Map the buffer specified by the vma->vm_pgoff to the given vma.
- */
-int hl_mem_mgr_mmap(struct hl_mem_mgr *mmg, struct vm_area_struct *vma,
- void *args)
-{
- struct hl_mmap_mem_buf *buf;
- u64 user_mem_size;
- u64 handle;
- int rc;
-
- /* We use the page offset to hold the idr and thus we need to clear
- * it before doing the mmap itself
- */
- handle = vma->vm_pgoff << PAGE_SHIFT;
- vma->vm_pgoff = 0;
-
- /* Reference was taken here */
- buf = hl_mmap_mem_buf_get(mmg, handle);
- if (!buf) {
- dev_err(mmg->dev,
- "Memory mmap failed, no match to handle %#llx\n", handle);
- return -EINVAL;
- }
-
- /* Validation check */
- user_mem_size = vma->vm_end - vma->vm_start;
- if (user_mem_size != ALIGN(buf->mappable_size, PAGE_SIZE)) {
- dev_err(mmg->dev,
- "%s: Memory mmap failed, mmap VM size 0x%llx != 0x%llx allocated physical mem size\n",
- buf->behavior->topic, user_mem_size, buf->mappable_size);
- rc = -EINVAL;
- goto put_mem;
- }
-
-#ifdef _HAS_TYPE_ARG_IN_ACCESS_OK
- if (!access_ok(VERIFY_WRITE, (void __user *)(uintptr_t)vma->vm_start,
- user_mem_size)) {
-#else
- if (!access_ok((void __user *)(uintptr_t)vma->vm_start,
- user_mem_size)) {
-#endif
- dev_err(mmg->dev, "%s: User pointer is invalid - 0x%lx\n",
- buf->behavior->topic, vma->vm_start);
-
- rc = -EINVAL;
- goto put_mem;
- }
-
- if (atomic_cmpxchg(&buf->mmap, 0, 1)) {
- dev_err(mmg->dev,
- "%s, Memory mmap failed, already mmaped to user\n",
- buf->behavior->topic);
- rc = -EINVAL;
- goto put_mem;
- }
-
- vma->vm_ops = &hl_mmap_mem_buf_vm_ops;
-
- /* Note: We're transferring the memory reference to vma->vm_private_data here. */
-
- vma->vm_private_data = buf;
-
- rc = buf->behavior->mmap(buf, vma, args);
- if (rc) {
- atomic_set(&buf->mmap, 0);
- goto put_mem;
- }
-
- buf->real_mapped_size = buf->mappable_size;
- vma->vm_pgoff = handle >> PAGE_SHIFT;
-
- return 0;
-
-put_mem:
- hl_mmap_mem_buf_put(buf);
- return rc;
-}
-
-/**
- * hl_mem_mgr_init - initialize unified memory manager
- *
- * @dev: owner device pointer
- * @mmg: structure to initialize
- * @is_kernel_mem_mgr: indicate whether the memory manager is the per-device kernel memory manager
- *
- * Initialize an instance of unified memory manager
- */
-void hl_mem_mgr_init(struct device *dev, struct hl_mem_mgr *mmg, u8 is_kernel_mem_mgr)
-{
- mmg->dev = dev;
- spin_lock_init(&mmg->lock);
- idr_init(&mmg->handles);
- mmg->is_kernel_mem_mgr = is_kernel_mem_mgr;
-}
-
-/**
- * hl_mem_mgr_fini - release unified memory manager
- *
- * @mmg: parent unified memory manager
- *
- * Release the unified memory manager. Shall be called from an interrupt context.
- */
-void hl_mem_mgr_fini(struct hl_mem_mgr *mmg)
-{
- struct hl_mmap_mem_buf *buf;
- struct idr *idp;
- const char *topic;
- u32 id;
-
- idp = &mmg->handles;
-
- idr_for_each_entry(idp, buf, id) {
- topic = buf->behavior->topic;
- if (hl_mmap_mem_buf_put(buf) != 1)
- dev_err(mmg->dev,
- "%s: Buff handle %u for CTX is still alive\n",
- topic, id);
- }
-
- /* TODO: can it happen that some buffer is still in use at this point? */
-
- idr_destroy(&mmg->handles);
-}
diff --git a/drivers/misc/habanalabs/common/mmu/Makefile b/drivers/misc/habanalabs/common/mmu/Makefile
deleted file mode 100644
index 1806c524e04a..000000000000
--- a/drivers/misc/habanalabs/common/mmu/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-HL_COMMON_MMU_FILES := common/mmu/mmu.o common/mmu/mmu_v1.o \
- common/mmu/mmu_v2_hr.o
diff --git a/drivers/misc/habanalabs/common/mmu/mmu.c b/drivers/misc/habanalabs/common/mmu/mmu.c
deleted file mode 100644
index 2c1005f74cf4..000000000000
--- a/drivers/misc/habanalabs/common/mmu/mmu.c
+++ /dev/null
@@ -1,1246 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2022 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include <linux/slab.h>
-
-#include "../habanalabs.h"
-
-#include <trace/events/habanalabs.h>
-
-/**
- * hl_mmu_get_funcs() - get MMU functions structure
- * @hdev: habanalabs device structure.
- * @pgt_residency: page table residency.
- * @is_dram_addr: true if we need HMMU functions
- *
- * @return appropriate MMU functions structure
- */
-static struct hl_mmu_funcs *hl_mmu_get_funcs(struct hl_device *hdev, int pgt_residency,
- bool is_dram_addr)
-{
- return &hdev->mmu_func[pgt_residency];
-}
-
-bool hl_is_dram_va(struct hl_device *hdev, u64 virt_addr)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
-
- return hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size,
- prop->dmmu.start_addr,
- prop->dmmu.end_addr);
-}
-
-/**
- * hl_mmu_init() - initialize the MMU module.
- * @hdev: habanalabs device structure.
- *
- * Return: 0 for success, non-zero for failure.
- */
-int hl_mmu_init(struct hl_device *hdev)
-{
- int rc = -EOPNOTSUPP;
-
- if (!hdev->mmu_enable)
- return 0;
-
- mutex_init(&hdev->mmu_lock);
-
- if (hdev->mmu_func[MMU_DR_PGT].init != NULL) {
- rc = hdev->mmu_func[MMU_DR_PGT].init(hdev);
- if (rc)
- return rc;
- }
-
- if (hdev->mmu_func[MMU_HR_PGT].init != NULL) {
- rc = hdev->mmu_func[MMU_HR_PGT].init(hdev);
- if (rc)
- goto fini_dr_mmu;
- }
-
- return 0;
-
-fini_dr_mmu:
- if (hdev->mmu_func[MMU_DR_PGT].fini != NULL)
- hdev->mmu_func[MMU_DR_PGT].fini(hdev);
-
- return rc;
-}
-
-/**
- * hl_mmu_fini() - release the MMU module.
- * @hdev: habanalabs device structure.
- *
- * This function does the following:
- * - Disable MMU in H/W.
- * - Free the pgt_infos pool.
- *
- * All contexts should be freed before calling this function.
- */
-void hl_mmu_fini(struct hl_device *hdev)
-{
- if (!hdev->mmu_enable)
- return;
-
- if (hdev->mmu_func[MMU_DR_PGT].fini != NULL)
- hdev->mmu_func[MMU_DR_PGT].fini(hdev);
-
- if (hdev->mmu_func[MMU_HR_PGT].fini != NULL)
- hdev->mmu_func[MMU_HR_PGT].fini(hdev);
-
- mutex_destroy(&hdev->mmu_lock);
-}
-
-/**
- * hl_mmu_ctx_init() - initialize a context for using the MMU module.
- * @ctx: pointer to the context structure to initialize.
- *
- * Initialize a mutex to protect the concurrent mapping flow, a hash to hold all
- * page tables hops related to this context.
- * Return: 0 on success, non-zero otherwise.
- */
-int hl_mmu_ctx_init(struct hl_ctx *ctx)
-{
- struct hl_device *hdev = ctx->hdev;
- int rc = -EOPNOTSUPP;
-
- if (!hdev->mmu_enable)
- return 0;
-
- if (hdev->mmu_func[MMU_DR_PGT].ctx_init != NULL) {
- rc = hdev->mmu_func[MMU_DR_PGT].ctx_init(ctx);
- if (rc)
- return rc;
- }
-
- if (hdev->mmu_func[MMU_HR_PGT].ctx_init != NULL) {
- rc = hdev->mmu_func[MMU_HR_PGT].ctx_init(ctx);
- if (rc)
- goto fini_dr_ctx;
- }
-
- return 0;
-
-fini_dr_ctx:
- if (hdev->mmu_func[MMU_DR_PGT].fini != NULL)
- hdev->mmu_func[MMU_DR_PGT].fini(hdev);
-
- return rc;
-}
-
-/*
- * hl_mmu_ctx_fini - disable a ctx from using the mmu module
- *
- * @ctx: pointer to the context structure
- *
- * This function does the following:
- * - Free any pgts which were not freed yet
- * - Free the mutex
- * - Free DRAM default page mapping hops
- */
-void hl_mmu_ctx_fini(struct hl_ctx *ctx)
-{
- struct hl_device *hdev = ctx->hdev;
-
- if (!hdev->mmu_enable)
- return;
-
- if (hdev->mmu_func[MMU_DR_PGT].ctx_fini != NULL)
- hdev->mmu_func[MMU_DR_PGT].ctx_fini(ctx);
-
- if (hdev->mmu_func[MMU_HR_PGT].ctx_fini != NULL)
- hdev->mmu_func[MMU_HR_PGT].ctx_fini(ctx);
-}
-
-/*
- * hl_mmu_get_real_page_size - get real page size to use in map/unmap operation
- *
- * @hdev: pointer to device data.
- * @mmu_prop: MMU properties.
- * @page_size: page size
- * @real_page_size: set here the actual page size to use for the operation
- * @is_dram_addr: true if DRAM address, otherwise false.
- *
- * @return 0 on success, otherwise non 0 error code
- *
- * note that this is general implementation that can fit most MMU arch. but as this is used as an
- * MMU function:
- * 1. it shall not be called directly- only from mmu_func structure instance
- * 2. each MMU may modify the implementation internally
- */
-int hl_mmu_get_real_page_size(struct hl_device *hdev, struct hl_mmu_properties *mmu_prop,
- u32 page_size, u32 *real_page_size, bool is_dram_addr)
-{
- /*
- * The H/W handles mapping of specific page sizes. Hence if the page
- * size is bigger, we break it to sub-pages and map them separately.
- */
- if ((page_size % mmu_prop->page_size) == 0) {
- *real_page_size = mmu_prop->page_size;
- return 0;
- }
-
- dev_err(hdev->dev, "page size of %u is not %uKB aligned, can't map\n",
- page_size, mmu_prop->page_size >> 10);
-
- return -EFAULT;
-}
-
-static struct hl_mmu_properties *hl_mmu_get_prop(struct hl_device *hdev, u32 page_size,
- bool is_dram_addr)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
-
- if (is_dram_addr)
- return &prop->dmmu;
- else if ((page_size % prop->pmmu_huge.page_size) == 0)
- return &prop->pmmu_huge;
-
- return &prop->pmmu;
-}
-
-/*
- * hl_mmu_unmap_page - unmaps a virtual addr
- *
- * @ctx: pointer to the context structure
- * @virt_addr: virt addr to map from
- * @page_size: size of the page to unmap
- * @flush_pte: whether to do a PCI flush
- *
- * This function does the following:
- * - Check that the virt addr is mapped
- * - Unmap the virt addr and frees pgts if possible
- * - Returns 0 on success, -EINVAL if the given addr is not mapped
- *
- * Because this function changes the page tables in the device and because it
- * changes the MMU hash, it must be protected by a lock.
- * However, because it maps only a single page, the lock should be implemented
- * in a higher level in order to protect the entire mapping of the memory area
- *
- * For optimization reasons PCI flush may be requested once after unmapping of
- * large area.
- */
-int hl_mmu_unmap_page(struct hl_ctx *ctx, u64 virt_addr, u32 page_size, bool flush_pte)
-{
- struct hl_device *hdev = ctx->hdev;
- struct hl_mmu_properties *mmu_prop;
- struct hl_mmu_funcs *mmu_funcs;
- int i, pgt_residency, rc = 0;
- u32 real_page_size, npages;
- u64 real_virt_addr;
- bool is_dram_addr;
-
- if (!hdev->mmu_enable)
- return 0;
-
- is_dram_addr = hl_is_dram_va(hdev, virt_addr);
- mmu_prop = hl_mmu_get_prop(hdev, page_size, is_dram_addr);
-
- pgt_residency = mmu_prop->host_resident ? MMU_HR_PGT : MMU_DR_PGT;
- mmu_funcs = hl_mmu_get_funcs(hdev, pgt_residency, is_dram_addr);
-
- rc = hdev->asic_funcs->mmu_get_real_page_size(hdev, mmu_prop, page_size, &real_page_size,
- is_dram_addr);
- if (rc)
- return rc;
-
- npages = page_size / real_page_size;
- real_virt_addr = virt_addr;
-
- for (i = 0 ; i < npages ; i++) {
- rc = mmu_funcs->unmap(ctx, real_virt_addr, is_dram_addr);
- if (rc)
- break;
-
- real_virt_addr += real_page_size;
- }
-
- if (flush_pte)
- mmu_funcs->flush(ctx);
-
- if (trace_habanalabs_mmu_unmap_enabled() && !rc)
- trace_habanalabs_mmu_unmap(hdev->dev, virt_addr, 0, page_size, flush_pte);
-
- return rc;
-}
-
-/*
- * hl_mmu_map_page - maps a virtual addr to physical addr
- *
- * @ctx: pointer to the context structure
- * @virt_addr: virt addr to map from
- * @phys_addr: phys addr to map to
- * @page_size: physical page size
- * @flush_pte: whether to do a PCI flush
- *
- * This function does the following:
- * - Check that the virt addr is not mapped
- * - Allocate pgts as necessary in order to map the virt addr to the phys
- * - Returns 0 on success, -EINVAL if addr is already mapped, or -ENOMEM.
- *
- * Because this function changes the page tables in the device and because it
- * changes the MMU hash, it must be protected by a lock.
- * However, because it maps only a single page, the lock should be implemented
- * in a higher level in order to protect the entire mapping of the memory area
- *
- * For optimization reasons PCI flush may be requested once after mapping of
- * large area.
- */
-int hl_mmu_map_page(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_size,
- bool flush_pte)
-{
- int i, rc, pgt_residency, mapped_cnt = 0;
- struct hl_device *hdev = ctx->hdev;
- struct hl_mmu_properties *mmu_prop;
- u64 real_virt_addr, real_phys_addr;
- struct hl_mmu_funcs *mmu_funcs;
- u32 real_page_size, npages;
- bool is_dram_addr;
-
-
- if (!hdev->mmu_enable)
- return 0;
-
- is_dram_addr = hl_is_dram_va(hdev, virt_addr);
- mmu_prop = hl_mmu_get_prop(hdev, page_size, is_dram_addr);
-
- pgt_residency = mmu_prop->host_resident ? MMU_HR_PGT : MMU_DR_PGT;
- mmu_funcs = hl_mmu_get_funcs(hdev, pgt_residency, is_dram_addr);
-
- rc = hdev->asic_funcs->mmu_get_real_page_size(hdev, mmu_prop, page_size, &real_page_size,
- is_dram_addr);
- if (rc)
- return rc;
-
- /*
- * Verify that the phys and virt addresses are aligned with the
- * MMU page size (in dram this means checking the address and MMU
- * after scrambling)
- */
- if ((is_dram_addr &&
- ((hdev->asic_funcs->scramble_addr(hdev, phys_addr) &
- (mmu_prop->page_size - 1)) ||
- (hdev->asic_funcs->scramble_addr(hdev, virt_addr) &
- (mmu_prop->page_size - 1)))) ||
- (!is_dram_addr && ((phys_addr & (real_page_size - 1)) ||
- (virt_addr & (real_page_size - 1)))))
- dev_crit(hdev->dev,
- "Mapping address 0x%llx with virtual address 0x%llx and page size of 0x%x is erroneous! Addresses must be divisible by page size",
- phys_addr, virt_addr, real_page_size);
-
- npages = page_size / real_page_size;
- real_virt_addr = virt_addr;
- real_phys_addr = phys_addr;
-
- for (i = 0 ; i < npages ; i++) {
- rc = mmu_funcs->map(ctx, real_virt_addr, real_phys_addr, real_page_size,
- is_dram_addr);
- if (rc)
- goto err;
-
- real_virt_addr += real_page_size;
- real_phys_addr += real_page_size;
- mapped_cnt++;
- }
-
- if (flush_pte)
- mmu_funcs->flush(ctx);
-
- trace_habanalabs_mmu_map(hdev->dev, virt_addr, phys_addr, page_size, flush_pte);
-
- return 0;
-
-err:
- real_virt_addr = virt_addr;
- for (i = 0 ; i < mapped_cnt ; i++) {
- if (mmu_funcs->unmap(ctx, real_virt_addr, is_dram_addr))
- dev_warn_ratelimited(hdev->dev,
- "failed to unmap va: 0x%llx\n", real_virt_addr);
-
- real_virt_addr += real_page_size;
- }
-
- mmu_funcs->flush(ctx);
-
- return rc;
-}
-
-/*
- * hl_mmu_map_contiguous - implements a wrapper for hl_mmu_map_page
- * for mapping contiguous physical memory
- *
- * @ctx: pointer to the context structure
- * @virt_addr: virt addr to map from
- * @phys_addr: phys addr to map to
- * @size: size to map
- *
- */
-int hl_mmu_map_contiguous(struct hl_ctx *ctx, u64 virt_addr,
- u64 phys_addr, u32 size)
-{
- struct hl_device *hdev = ctx->hdev;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u64 curr_va, curr_pa;
- u32 page_size;
- bool flush_pte;
- int rc = 0, off;
-
- if (hl_mem_area_inside_range(virt_addr, size,
- prop->dmmu.start_addr, prop->dmmu.end_addr))
- page_size = prop->dmmu.page_size;
- else if (hl_mem_area_inside_range(virt_addr, size,
- prop->pmmu.start_addr, prop->pmmu.end_addr))
- page_size = prop->pmmu.page_size;
- else if (hl_mem_area_inside_range(virt_addr, size,
- prop->pmmu_huge.start_addr, prop->pmmu_huge.end_addr))
- page_size = prop->pmmu_huge.page_size;
- else
- return -EINVAL;
-
- for (off = 0 ; off < size ; off += page_size) {
- curr_va = virt_addr + off;
- curr_pa = phys_addr + off;
- flush_pte = (off + page_size) >= size;
- rc = hl_mmu_map_page(ctx, curr_va, curr_pa, page_size,
- flush_pte);
- if (rc) {
- dev_err(hdev->dev,
- "Map failed for va 0x%llx to pa 0x%llx\n",
- curr_va, curr_pa);
- /* last mapping failed so don't try to unmap it - reduce off by page_size */
- off -= page_size;
- goto unmap;
- }
- }
-
- return rc;
-
-unmap:
- for (; off >= 0 ; off -= page_size) {
- curr_va = virt_addr + off;
- flush_pte = (off - (s32) page_size) < 0;
- if (hl_mmu_unmap_page(ctx, curr_va, page_size, flush_pte))
- dev_warn_ratelimited(hdev->dev,
- "failed to unmap va 0x%llx\n", curr_va);
- }
-
- return rc;
-}
-
-/*
- * hl_mmu_unmap_contiguous - implements a wrapper for hl_mmu_unmap_page
- * for unmapping contiguous physical memory
- *
- * @ctx: pointer to the context structure
- * @virt_addr: virt addr to unmap
- * @size: size to unmap
- *
- */
-int hl_mmu_unmap_contiguous(struct hl_ctx *ctx, u64 virt_addr, u32 size)
-{
- struct hl_device *hdev = ctx->hdev;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u64 curr_va;
- u32 page_size;
- bool flush_pte;
- int rc = 0, off;
-
- if (hl_mem_area_inside_range(virt_addr, size,
- prop->dmmu.start_addr, prop->dmmu.end_addr))
- page_size = prop->dmmu.page_size;
- else if (hl_mem_area_inside_range(virt_addr, size,
- prop->pmmu.start_addr, prop->pmmu.end_addr))
- page_size = prop->pmmu.page_size;
- else if (hl_mem_area_inside_range(virt_addr, size,
- prop->pmmu_huge.start_addr, prop->pmmu_huge.end_addr))
- page_size = prop->pmmu_huge.page_size;
- else
- return -EINVAL;
-
- for (off = 0 ; off < size ; off += page_size) {
- curr_va = virt_addr + off;
- flush_pte = (off + page_size) >= size;
- rc = hl_mmu_unmap_page(ctx, curr_va, page_size, flush_pte);
- if (rc)
- dev_warn_ratelimited(hdev->dev,
- "Unmap failed for va 0x%llx\n", curr_va);
- }
-
- return rc;
-}
-
-/*
- * hl_mmu_swap_out - marks all mapping of the given ctx as swapped out
- *
- * @ctx: pointer to the context structure
- *
- */
-void hl_mmu_swap_out(struct hl_ctx *ctx)
-{
- struct hl_device *hdev = ctx->hdev;
-
- if (!hdev->mmu_enable)
- return;
-
- if (hdev->mmu_func[MMU_DR_PGT].swap_out != NULL)
- hdev->mmu_func[MMU_DR_PGT].swap_out(ctx);
-
- if (hdev->mmu_func[MMU_HR_PGT].swap_out != NULL)
- hdev->mmu_func[MMU_HR_PGT].swap_out(ctx);
-}
-
-/*
- * hl_mmu_swap_in - marks all mapping of the given ctx as swapped in
- *
- * @ctx: pointer to the context structure
- *
- */
-void hl_mmu_swap_in(struct hl_ctx *ctx)
-{
- struct hl_device *hdev = ctx->hdev;
-
- if (!hdev->mmu_enable)
- return;
-
- if (hdev->mmu_func[MMU_DR_PGT].swap_in != NULL)
- hdev->mmu_func[MMU_DR_PGT].swap_in(ctx);
-
- if (hdev->mmu_func[MMU_HR_PGT].swap_in != NULL)
- hdev->mmu_func[MMU_HR_PGT].swap_in(ctx);
-}
-
-static void hl_mmu_pa_page_with_offset(struct hl_ctx *ctx, u64 virt_addr,
- struct hl_mmu_hop_info *hops,
- u64 *phys_addr)
-{
- struct asic_fixed_properties *prop = &ctx->hdev->asic_prop;
- u64 offset_mask, addr_mask, hop_shift, tmp_phys_addr;
- struct hl_mmu_properties *mmu_prop;
-
- /* last hop holds the phys address and flags */
- if (hops->unscrambled_paddr)
- tmp_phys_addr = hops->unscrambled_paddr;
- else
- tmp_phys_addr = hops->hop_info[hops->used_hops - 1].hop_pte_val;
-
- if (hops->range_type == HL_VA_RANGE_TYPE_HOST_HUGE)
- mmu_prop = &prop->pmmu_huge;
- else if (hops->range_type == HL_VA_RANGE_TYPE_HOST)
- mmu_prop = &prop->pmmu;
- else /* HL_VA_RANGE_TYPE_DRAM */
- mmu_prop = &prop->dmmu;
-
- if ((hops->range_type == HL_VA_RANGE_TYPE_DRAM) &&
- !is_power_of_2(prop->dram_page_size)) {
- u64 dram_page_size, dram_base, abs_phys_addr, abs_virt_addr,
- page_id, page_start;
- u32 page_off;
-
- /*
- * Bit arithmetics cannot be used for non power of two page
- * sizes. In addition, since bit arithmetics is not used,
- * we cannot ignore dram base. All that shall be considered.
- */
-
- dram_page_size = prop->dram_page_size;
- dram_base = prop->dram_base_address;
- abs_phys_addr = tmp_phys_addr - dram_base;
- abs_virt_addr = virt_addr - dram_base;
- page_id = DIV_ROUND_DOWN_ULL(abs_phys_addr, dram_page_size);
- page_start = page_id * dram_page_size;
- div_u64_rem(abs_virt_addr, dram_page_size, &page_off);
-
- *phys_addr = page_start + page_off + dram_base;
- } else {
- /*
- * find the correct hop shift field in hl_mmu_properties
- * structure in order to determine the right masks
- * for the page offset.
- */
- hop_shift = mmu_prop->hop_shifts[hops->used_hops - 1];
- offset_mask = (1ull << hop_shift) - 1;
- addr_mask = ~(offset_mask);
- *phys_addr = (tmp_phys_addr & addr_mask) |
- (virt_addr & offset_mask);
- }
-}
-
-int hl_mmu_va_to_pa(struct hl_ctx *ctx, u64 virt_addr, u64 *phys_addr)
-{
- struct hl_mmu_hop_info hops;
- int rc;
-
- memset(&hops, 0, sizeof(hops));
-
- rc = hl_mmu_get_tlb_info(ctx, virt_addr, &hops);
- if (rc)
- return rc;
-
- hl_mmu_pa_page_with_offset(ctx, virt_addr, &hops, phys_addr);
-
- return 0;
-}
-
-int hl_mmu_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr,
- struct hl_mmu_hop_info *hops)
-{
- struct hl_device *hdev = ctx->hdev;
- struct asic_fixed_properties *prop;
- struct hl_mmu_properties *mmu_prop;
- struct hl_mmu_funcs *mmu_funcs;
- int pgt_residency, rc;
- bool is_dram_addr;
-
- if (!hdev->mmu_enable)
- return -EOPNOTSUPP;
-
- prop = &hdev->asic_prop;
- hops->scrambled_vaddr = virt_addr; /* assume no scrambling */
-
- is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size,
- prop->dmmu.start_addr,
- prop->dmmu.end_addr);
-
- /* host-residency is the same in PMMU and PMMU huge, no need to distinguish here */
- mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu;
- pgt_residency = mmu_prop->host_resident ? MMU_HR_PGT : MMU_DR_PGT;
- mmu_funcs = hl_mmu_get_funcs(hdev, pgt_residency, is_dram_addr);
-
- mutex_lock(&hdev->mmu_lock);
- rc = mmu_funcs->get_tlb_info(ctx, virt_addr, hops);
- mutex_unlock(&hdev->mmu_lock);
-
- if (rc)
- return rc;
-
- /* add page offset to physical address */
- if (hops->unscrambled_paddr)
- hl_mmu_pa_page_with_offset(ctx, virt_addr, hops, &hops->unscrambled_paddr);
-
- return 0;
-}
-
-int hl_mmu_if_set_funcs(struct hl_device *hdev)
-{
- if (!hdev->mmu_enable)
- return 0;
-
- switch (hdev->asic_type) {
- case ASIC_GOYA:
- case ASIC_GAUDI:
- case ASIC_GAUDI_SEC:
- hl_mmu_v1_set_funcs(hdev, &hdev->mmu_func[MMU_DR_PGT]);
- break;
- case ASIC_GAUDI2:
- case ASIC_GAUDI2B:
- /* MMUs in Gaudi2 are always host resident */
- hl_mmu_v2_hr_set_funcs(hdev, &hdev->mmu_func[MMU_HR_PGT]);
- break;
- default:
- dev_err(hdev->dev, "Unrecognized ASIC type %d\n",
- hdev->asic_type);
- return -EOPNOTSUPP;
- }
-
- return 0;
-}
-
-/**
- * hl_mmu_scramble_addr() - The generic mmu address scrambling routine.
- * @hdev: pointer to device data.
- * @addr: The address to scramble.
- *
- * Return: The scrambled address.
- */
-u64 hl_mmu_scramble_addr(struct hl_device *hdev, u64 addr)
-{
- return addr;
-}
-
-/**
- * hl_mmu_descramble_addr() - The generic mmu address descrambling
- * routine.
- * @hdev: pointer to device data.
- * @addr: The address to descramble.
- *
- * Return: The un-scrambled address.
- */
-u64 hl_mmu_descramble_addr(struct hl_device *hdev, u64 addr)
-{
- return addr;
-}
-
-int hl_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, u32 flags)
-{
- int rc;
-
- rc = hdev->asic_funcs->mmu_invalidate_cache(hdev, is_hard, flags);
- if (rc)
- dev_err_ratelimited(hdev->dev, "MMU cache invalidation failed\n");
-
- return rc;
-}
-
-int hl_mmu_invalidate_cache_range(struct hl_device *hdev, bool is_hard,
- u32 flags, u32 asid, u64 va, u64 size)
-{
- int rc;
-
- rc = hdev->asic_funcs->mmu_invalidate_cache_range(hdev, is_hard, flags,
- asid, va, size);
- if (rc)
- dev_err_ratelimited(hdev->dev, "MMU cache range invalidation failed\n");
-
- return rc;
-}
-
-static void hl_mmu_prefetch_work_function(struct work_struct *work)
-{
- struct hl_prefetch_work *pfw = container_of(work, struct hl_prefetch_work, prefetch_work);
- struct hl_ctx *ctx = pfw->ctx;
- struct hl_device *hdev = ctx->hdev;
-
- if (!hl_device_operational(hdev, NULL))
- goto put_ctx;
-
- mutex_lock(&hdev->mmu_lock);
-
- hdev->asic_funcs->mmu_prefetch_cache_range(ctx, pfw->flags, pfw->asid, pfw->va, pfw->size);
-
- mutex_unlock(&hdev->mmu_lock);
-
-put_ctx:
- /*
- * context was taken in the common mmu prefetch function- see comment there about
- * context handling.
- */
- hl_ctx_put(ctx);
- kfree(pfw);
-}
-
-int hl_mmu_prefetch_cache_range(struct hl_ctx *ctx, u32 flags, u32 asid, u64 va, u64 size)
-{
- struct hl_prefetch_work *handle_prefetch_work;
-
- handle_prefetch_work = kmalloc(sizeof(*handle_prefetch_work), GFP_KERNEL);
- if (!handle_prefetch_work)
- return -ENOMEM;
-
- INIT_WORK(&handle_prefetch_work->prefetch_work, hl_mmu_prefetch_work_function);
- handle_prefetch_work->ctx = ctx;
- handle_prefetch_work->va = va;
- handle_prefetch_work->size = size;
- handle_prefetch_work->flags = flags;
- handle_prefetch_work->asid = asid;
-
- /*
- * as actual prefetch is done in a WQ we must get the context (and put it
- * at the end of the work function)
- */
- hl_ctx_get(ctx);
- queue_work(ctx->hdev->prefetch_wq, &handle_prefetch_work->prefetch_work);
-
- return 0;
-}
-
-u64 hl_mmu_get_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte)
-{
- return (curr_pte & PAGE_PRESENT_MASK) ? (curr_pte & HOP_PHYS_ADDR_MASK) : ULLONG_MAX;
-}
-
-/**
- * hl_mmu_get_hop_pte_phys_addr() - extract PTE address from HOP
- * @ctx: pointer to the context structure to initialize.
- * @mmu_prop: MMU properties.
- * @hop_idx: HOP index.
- * @hop_addr: HOP address.
- * @virt_addr: virtual address fro the translation.
- *
- * @return the matching PTE value on success, otherwise U64_MAX.
- */
-u64 hl_mmu_get_hop_pte_phys_addr(struct hl_ctx *ctx, struct hl_mmu_properties *mmu_prop,
- u8 hop_idx, u64 hop_addr, u64 virt_addr)
-{
- u64 mask, shift;
-
- if (hop_idx >= mmu_prop->num_hops) {
- dev_err_ratelimited(ctx->hdev->dev, "Invalid hop index %d\n", hop_idx);
- return U64_MAX;
- }
-
- shift = mmu_prop->hop_shifts[hop_idx];
- mask = mmu_prop->hop_masks[hop_idx];
-
- return hop_addr + ctx->hdev->asic_prop.mmu_pte_size * ((virt_addr & mask) >> shift);
-}
-
-static void mmu_dma_mem_free_from_chunk(struct gen_pool *pool,
- struct gen_pool_chunk *chunk,
- void *data)
-{
- struct hl_device *hdev = (struct hl_device *)data;
-
- hl_asic_dma_free_coherent(hdev, (chunk->end_addr - chunk->start_addr) + 1,
- (void *)chunk->start_addr, chunk->phys_addr);
-}
-
-void hl_mmu_hr_flush(struct hl_ctx *ctx)
-{
- /* a flush operation requires memory barrier */
- mb();
-}
-
-/**
- * hl_mmu_hr_pool_destroy() - destroy genpool
- * @hdev: habanalabs device structure.
- * @hr_priv: MMU HR private data.
- * @hop_table_size: HOP table size.
- *
- * This function does the following:
- * - free entries allocated for shadow HOP0
- * - free pool chunks
- * - free pool
- */
-static void hl_mmu_hr_pool_destroy(struct hl_device *hdev, struct hl_mmu_hr_priv *hr_priv,
- u32 hop_table_size)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct gen_pool **pool = &hr_priv->mmu_pgt_pool;
- struct pgt_info *hop0_pgt;
- int asid;
-
- if (ZERO_OR_NULL_PTR(*pool))
- return;
-
- /* Free the Fixed allocation of HOPs0 */
- if (hr_priv->mmu_asid_hop0) {
- for (asid = 0 ; asid < prop->max_asid ; asid++) {
- hop0_pgt = &hr_priv->mmu_asid_hop0[asid];
- if (ZERO_OR_NULL_PTR(hop0_pgt->virt_addr))
- continue;
-
- gen_pool_free(*pool, (uintptr_t) hop0_pgt->virt_addr, hop_table_size);
- }
- }
-
- gen_pool_for_each_chunk(*pool, mmu_dma_mem_free_from_chunk, hdev);
- gen_pool_destroy(*pool);
-
- /* Make sure that if we arrive here again without init was called we
- * won't cause kernel panic. This can happen for example if we fail
- * during hard reset code at certain points
- */
- *pool = NULL;
-}
-
-/**
- * hl_mmu_hr_init() - initialize the MMU module.
- * @hdev: habanalabs device structure.
- * @hr_priv: MMU HR private data.
- * @hop_table_size: HOP table size.
- * @pgt_size: memory size allocated for the page table
- *
- * @return 0 on success otherwise non-zero error code
- *
- * This function does the following:
- * - Create a pool of pages for pgt_infos.
- * - Create a shadow table for pgt
- */
-int hl_mmu_hr_init(struct hl_device *hdev, struct hl_mmu_hr_priv *hr_priv, u32 hop_table_size,
- u64 pgt_size)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- size_t pool_chunk_size = SZ_4M;
- struct pgt_info *hop0_pgt;
- dma_addr_t dma_addr;
- u64 virt_addr;
- int i, rc;
-
- /*
- * we set alloc size as PAGE_SIZE (sine dma_alloc_coherent allocation order/size is
- * PAGE_SHIFT/PAGE_SIZE) in order to be able to control the allocations alignment.
- * This way we can call "DMA alloc align" according to dma_alloc granularity and supply
- * allocations with higher-order alignment restrictions
- */
- hr_priv->mmu_pgt_pool = gen_pool_create(PAGE_SHIFT, -1);
- if (ZERO_OR_NULL_PTR(hr_priv->mmu_pgt_pool)) {
- dev_err(hdev->dev, "Failed to create hr page pool\n");
- return -ENOMEM;
- }
-
- hr_priv->mmu_asid_hop0 = kvcalloc(prop->max_asid, sizeof(struct pgt_info), GFP_KERNEL);
- if (ZERO_OR_NULL_PTR(hr_priv->mmu_asid_hop0)) {
- dev_err(hdev->dev, "Failed to allocate hr-mmu hop0 table\n");
- rc = -ENOMEM;
- goto destroy_mmu_pgt_pool;
- }
-
- for (i = 0 ; i < pgt_size ; i += pool_chunk_size) {
- virt_addr = (uintptr_t) hl_asic_dma_alloc_coherent(hdev, pool_chunk_size,
- &dma_addr,
- GFP_KERNEL | __GFP_ZERO);
- if (ZERO_OR_NULL_PTR(virt_addr)) {
- dev_err(hdev->dev,
- "Failed to allocate memory for host-resident page pool\n");
- rc = -ENOMEM;
- goto destroy_mmu_pgt_pool;
- }
-
- rc = gen_pool_add_virt(hr_priv->mmu_pgt_pool, virt_addr, (phys_addr_t) dma_addr,
- pool_chunk_size, -1);
- if (rc) {
- dev_err(hdev->dev, "Failed to fill host-resident page pool\n");
- goto destroy_mmu_pgt_pool;
- }
- }
-
- for (i = 0 ; i < prop->max_asid ; i++) {
- hop0_pgt = &hr_priv->mmu_asid_hop0[i];
- hop0_pgt->virt_addr = (uintptr_t)
- gen_pool_dma_zalloc_align(hr_priv->mmu_pgt_pool,
- hop_table_size,
- (dma_addr_t *) &hop0_pgt->phys_addr,
- hop_table_size);
- if (!hop0_pgt->virt_addr) {
- dev_err(hdev->dev, "Failed to allocate HOP from pgt pool\n");
- rc = -ENOMEM;
- goto destroy_mmu_pgt_pool;
- }
- }
-
- /* MMU H/W init will be done in device hw_init() */
-
- return 0;
-
-destroy_mmu_pgt_pool:
- hl_mmu_hr_pool_destroy(hdev, hr_priv, hop_table_size);
- if (!ZERO_OR_NULL_PTR(hr_priv->mmu_asid_hop0))
- kvfree(hr_priv->mmu_asid_hop0);
-
- return rc;
-}
-
-/**
- * hl_mmu_hr_fini() - release the MMU module.
- * @hdev: habanalabs device structure.
- * @hr_priv: MMU host resident private info.
- * @hop_table_size: HOP table size
- *
- * This function does the following:
- * - Disable MMU in H/W.
- * - Free the pgt_infos pool.
- *
- * All contexts should be freed before calling this function.
- */
-void hl_mmu_hr_fini(struct hl_device *hdev, struct hl_mmu_hr_priv *hr_priv, u32 hop_table_size)
-{
- /* MMU H/W fini was already done in device hw_fini() */
-
- hl_mmu_hr_pool_destroy(hdev, hr_priv, hop_table_size);
-
- if (!ZERO_OR_NULL_PTR(hr_priv->mmu_asid_hop0)) {
- kvfree(hr_priv->mmu_asid_hop0);
-
- /* Make sure that if we arrive here again without init was
- * called we won't cause kernel panic. This can happen for
- * example if we fail during hard reset code at certain points
- */
- hr_priv->mmu_asid_hop0 = NULL;
- }
-}
-
-/**
- * hl_mmu_hr_free_hop_remove_pgt() - free HOP and remove PGT from hash
- * @pgt_info: page table info structure.
- * @hr_priv: MMU HR private data.
- * @hop_table_size: HOP table size.
- */
-void hl_mmu_hr_free_hop_remove_pgt(struct pgt_info *pgt_info, struct hl_mmu_hr_priv *hr_priv,
- u32 hop_table_size)
-{
- gen_pool_free(hr_priv->mmu_pgt_pool, pgt_info->virt_addr, hop_table_size);
- hash_del(&pgt_info->node);
- kfree(pgt_info);
-}
-
-/**
- * hl_mmu_hr_pte_phys_to_virt() - translate PTE phys addr to virt addr
- * @ctx: pointer to the context structure
- * @pgt: pgt_info for the HOP hosting the PTE
- * @phys_pte_addr: phys address of the PTE
- * @hop_table_size: HOP table size
- *
- * @return PTE virtual address
- *
- * The function use the pgt_info to get HOP base virt addr and obtain the PTE's virt addr
- * by adding the PTE offset.
- */
-u64 hl_mmu_hr_pte_phys_to_virt(struct hl_ctx *ctx, struct pgt_info *pgt,
- u64 phys_pte_addr, u32 hop_table_size)
-{
- u64 page_mask = (hop_table_size - 1);
- u64 pte_offset = phys_pte_addr & page_mask;
-
- return pgt->virt_addr + pte_offset;
-}
-
-/**
- * hl_mmu_hr_write_pte() - write HR PTE
- * @ctx: pointer to the context structure
- * @pgt_info: HOP's page table info structure
- * @phys_pte_addr: phys PTE address
- * @val: raw PTE data
- * @hop_table_size: HOP table size
- */
-void hl_mmu_hr_write_pte(struct hl_ctx *ctx, struct pgt_info *pgt_info, u64 phys_pte_addr,
- u64 val, u32 hop_table_size)
-{
- /*
- * The value to write is the phys address of the next hop +
- * flags at the 12 LSBs.
- */
- u64 virt_addr = hl_mmu_hr_pte_phys_to_virt(ctx, pgt_info, phys_pte_addr, hop_table_size);
-
- *((u64 *) (uintptr_t) virt_addr) = val;
-}
-
-/**
- * hl_mmu_hr_clear_pte() - clear HR PTE
- * @ctx: pointer to the context structure
- * @pgt_info: HOP's page table info structure
- * @phys_pte_addr: phys PTE address
- * @hop_table_size: HOP table size
- */
-void hl_mmu_hr_clear_pte(struct hl_ctx *ctx, struct pgt_info *pgt_info, u64 phys_pte_addr,
- u32 hop_table_size)
-{
- /* no need to transform the value to physical address */
- hl_mmu_hr_write_pte(ctx, pgt_info, phys_pte_addr, 0, hop_table_size);
-}
-
-/**
- * hl_mmu_hr_put_pte() - put HR PTE and remove it if necessary (no more PTEs)
- * @ctx: pointer to the context structure
- * @pgt_info: HOP's page table info structure
- * @hr_priv: HR MMU private info
- * @hop_table_size: HOP table size
- *
- * @return number of PTEs still in the HOP
- */
-int hl_mmu_hr_put_pte(struct hl_ctx *ctx, struct pgt_info *pgt_info,
- struct hl_mmu_hr_priv *hr_priv,
- u32 hop_table_size)
-{
- int num_of_ptes_left;
-
- pgt_info->num_of_ptes--;
-
- /*
- * Need to save the number of ptes left because free_hop might free
- * the pgt_info
- */
- num_of_ptes_left = pgt_info->num_of_ptes;
- if (!num_of_ptes_left)
- hl_mmu_hr_free_hop_remove_pgt(pgt_info, hr_priv, hop_table_size);
-
- return num_of_ptes_left;
-}
-
-/**
- * hl_mmu_hr_get_pte() - increase PGT PTE count
- * @ctx: pointer to the context structure
- * @hr_func: host resident functions
- * @phys_hop_addr: HOP phys address
- */
-void hl_mmu_hr_get_pte(struct hl_ctx *ctx, struct hl_hr_mmu_funcs *hr_func, u64 phys_hop_addr)
-{
- hr_func->get_pgt_info(ctx, phys_hop_addr)->num_of_ptes++;
-}
-
-/**
- * hl_mmu_hr_get_next_hop_pgt_info() - get pgt_info structure for the next HOP
- * @ctx: pointer to the context structure.
- * @hr_func: host resident functions.
- * @curr_pte: current PTE value.
- *
- * @return pgt_info structure on success, otherwise NULL.
- */
-struct pgt_info *hl_mmu_hr_get_next_hop_pgt_info(struct hl_ctx *ctx,
- struct hl_hr_mmu_funcs *hr_func,
- u64 curr_pte)
-{
- u64 next_hop_phys_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte);
-
- if (next_hop_phys_addr == ULLONG_MAX)
- return NULL;
-
- return hr_func->get_pgt_info(ctx, next_hop_phys_addr);
-}
-
-/**
- * hl_mmu_hr_alloc_hop() - allocate HOP
- * @ctx: pointer to the context structure.
- * @hr_priv: host resident private info structure.
- * @hr_func: host resident functions.
- * @mmu_prop: MMU properties.
- *
- * @return pgt_info structure associated with the allocated HOP on success, otherwise NULL.
- */
-struct pgt_info *hl_mmu_hr_alloc_hop(struct hl_ctx *ctx, struct hl_mmu_hr_priv *hr_priv,
- struct hl_hr_mmu_funcs *hr_func,
- struct hl_mmu_properties *mmu_prop)
-{
- struct hl_device *hdev = ctx->hdev;
- struct pgt_info *pgt_info;
- dma_addr_t phys_addr;
- void *virt_addr;
- int i, retry = 1;
-
- pgt_info = kmalloc(sizeof(*pgt_info), GFP_KERNEL);
- if (!pgt_info)
- return NULL;
-
- for (i = 0; i <= retry; i++) {
- virt_addr = gen_pool_dma_zalloc_align(hr_priv->mmu_pgt_pool,
- mmu_prop->hop_table_size,
- &phys_addr,
- mmu_prop->hop_table_size);
- if (virt_addr)
- break;
-
- /* No memory in pool - get some and try again */
- virt_addr = hl_asic_dma_alloc_coherent(hdev, SZ_2M, &phys_addr,
- GFP_KERNEL | __GFP_ZERO);
- if (ZERO_OR_NULL_PTR(virt_addr))
- break;
-
- if (gen_pool_add_virt(hr_priv->mmu_pgt_pool, (unsigned long)virt_addr,
- phys_addr, SZ_2M, -1)) {
- hl_asic_dma_free_coherent(hdev, SZ_2M, virt_addr, phys_addr);
- virt_addr = NULL;
- break;
- }
- }
-
- if (ZERO_OR_NULL_PTR(virt_addr)) {
- dev_err(hdev->dev, "failed to allocate page\n");
- goto pool_alloc_err;
- }
-
- pgt_info->phys_addr = phys_addr;
- pgt_info->shadow_addr = (unsigned long) NULL;
- pgt_info->virt_addr = (unsigned long)virt_addr;
- pgt_info->ctx = ctx;
- pgt_info->num_of_ptes = 0;
- hr_func->add_pgt_info(ctx, pgt_info, phys_addr);
-
- return pgt_info;
-
-pool_alloc_err:
- kfree(pgt_info);
-
- return NULL;
-}
-
-/**
- * hl_mmu_hr_get_alloc_next_hop() - get the next HOP, allocate it if it does not exist
- * @ctx: pointer to the context structure.
- * @hr_priv: host resident private info structure.
- * @hr_func: host resident functions.
- * @mmu_prop: MMU properties.
- * @curr_pte: current PTE value.
- * @is_new_hop: set to true if HOP is new (caller responsibility to set it to false).
- *
- * @return pgt_info structure associated with the allocated HOP on success, otherwise NULL.
- */
-struct pgt_info *hl_mmu_hr_get_alloc_next_hop(struct hl_ctx *ctx,
- struct hl_mmu_hr_priv *hr_priv,
- struct hl_hr_mmu_funcs *hr_func,
- struct hl_mmu_properties *mmu_prop,
- u64 curr_pte, bool *is_new_hop)
-{
- u64 hop_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte);
-
- if (hop_addr != ULLONG_MAX)
- return hr_func->get_pgt_info(ctx, hop_addr);
-
- *is_new_hop = true;
- return hl_mmu_hr_alloc_hop(ctx, hr_priv, hr_func, mmu_prop);
-}
-
-/**
- * hl_mmu_hr_get_tlb_info() - get the TLB info (info for a specific mapping)
- * @ctx: pointer to the context structure.
- * @virt_addr: the virt address for which to get info.
- * @hops: HOPs info structure.
- * @hr_func: host resident functions.
- *
- * @return 0 on success, otherwise non 0 error code..
- */
-int hl_mmu_hr_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr, struct hl_mmu_hop_info *hops,
- struct hl_hr_mmu_funcs *hr_func)
-{
- /* using 6 HOPs as this is the maximum number of HOPs */
- struct pgt_info *hops_pgt_info[MMU_ARCH_6_HOPS] = { NULL };
- struct hl_device *hdev = ctx->hdev;
- struct hl_mmu_properties *mmu_prop;
- int rc, i, used_hops;
- bool is_huge;
-
- rc = hr_func->get_tlb_mapping_params(hdev, &mmu_prop, hops, virt_addr, &is_huge);
- if (rc)
- return rc;
-
- used_hops = mmu_prop->num_hops;
-
- /* huge pages use one less hop */
- if (is_huge)
- used_hops--;
-
- hops->scrambled_vaddr = hdev->asic_funcs->scramble_addr(hdev, virt_addr);
-
- for (i = 0 ; i < used_hops ; i++) {
- if (i == 0)
- hops_pgt_info[i] = hr_func->get_hop0_pgt_info(ctx);
- else
- hops_pgt_info[i] = hl_mmu_hr_get_next_hop_pgt_info(ctx, hr_func,
- hops->hop_info[i - 1].hop_pte_val);
-
- if (!hops_pgt_info[i])
- return -EFAULT;
-
- hops->hop_info[i].hop_addr = hops_pgt_info[i]->phys_addr;
- hops->hop_info[i].hop_pte_addr =
- hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, i,
- hops->hop_info[i].hop_addr,
- hops->scrambled_vaddr);
- hops->hop_info[i].hop_pte_val = *(u64 *) (uintptr_t)
- hl_mmu_hr_pte_phys_to_virt(ctx, hops_pgt_info[i],
- hops->hop_info[i].hop_pte_addr,
- mmu_prop->hop_table_size);
-
- if (!(hops->hop_info[i].hop_pte_val & PAGE_PRESENT_MASK))
- return -EFAULT;
-
- if (hops->hop_info[i].hop_pte_val & mmu_prop->last_mask)
- break;
- }
-
- /* if passed over all hops then no last hop was found */
- if (i == mmu_prop->num_hops)
- return -EFAULT;
-
- if (hops->scrambled_vaddr != virt_addr)
- hops->unscrambled_paddr = hdev->asic_funcs->descramble_addr
- (hdev, hops->hop_info[i].hop_pte_val);
- else
- hops->unscrambled_paddr = hops->hop_info[i].hop_pte_val;
-
- hops->used_hops = i + 1;
-
- return 0;
-}
-
diff --git a/drivers/misc/habanalabs/common/mmu/mmu_v1.c b/drivers/misc/habanalabs/common/mmu/mmu_v1.c
deleted file mode 100644
index d925dc4dd097..000000000000
--- a/drivers/misc/habanalabs/common/mmu/mmu_v1.c
+++ /dev/null
@@ -1,814 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2019 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include "../habanalabs.h"
-#include "../../include/hw_ip/mmu/mmu_general.h"
-
-#include <linux/slab.h>
-
-#define MMU_V1_MAX_HOPS (MMU_HOP4 + 1)
-
-static inline u64 get_phys_addr(struct hl_ctx *ctx, u64 shadow_addr);
-
-static struct pgt_info *get_pgt_info(struct hl_ctx *ctx, u64 hop_addr)
-{
- struct pgt_info *pgt_info = NULL;
-
- hash_for_each_possible(ctx->mmu_shadow_hash, pgt_info, node,
- (unsigned long) hop_addr)
- if (hop_addr == pgt_info->shadow_addr)
- break;
-
- return pgt_info;
-}
-
-static void _free_hop(struct hl_ctx *ctx, struct pgt_info *pgt_info)
-{
- struct hl_device *hdev = ctx->hdev;
-
- gen_pool_free(hdev->mmu_priv.dr.mmu_pgt_pool, pgt_info->phys_addr,
- hdev->asic_prop.mmu_hop_table_size);
- hash_del(&pgt_info->node);
- kfree((u64 *) (uintptr_t) pgt_info->shadow_addr);
- kfree(pgt_info);
-}
-
-static void free_hop(struct hl_ctx *ctx, u64 hop_addr)
-{
- struct pgt_info *pgt_info = get_pgt_info(ctx, hop_addr);
-
- _free_hop(ctx, pgt_info);
-}
-
-static u64 alloc_hop(struct hl_ctx *ctx)
-{
- struct hl_device *hdev = ctx->hdev;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct pgt_info *pgt_info;
- u64 phys_addr, shadow_addr;
-
- pgt_info = kmalloc(sizeof(*pgt_info), GFP_KERNEL);
- if (!pgt_info)
- return ULLONG_MAX;
-
- phys_addr = (u64) gen_pool_alloc(hdev->mmu_priv.dr.mmu_pgt_pool,
- prop->mmu_hop_table_size);
- if (!phys_addr) {
- dev_err(hdev->dev, "failed to allocate page\n");
- goto pool_add_err;
- }
-
- shadow_addr = (u64) (uintptr_t) kzalloc(prop->mmu_hop_table_size,
- GFP_KERNEL);
- if (!shadow_addr)
- goto shadow_err;
-
- pgt_info->phys_addr = phys_addr;
- pgt_info->shadow_addr = shadow_addr;
- pgt_info->ctx = ctx;
- pgt_info->num_of_ptes = 0;
- hash_add(ctx->mmu_shadow_hash, &pgt_info->node, shadow_addr);
-
- return shadow_addr;
-
-shadow_err:
- gen_pool_free(hdev->mmu_priv.dr.mmu_pgt_pool, phys_addr,
- prop->mmu_hop_table_size);
-pool_add_err:
- kfree(pgt_info);
-
- return ULLONG_MAX;
-}
-
-static inline u64 get_phys_hop0_addr(struct hl_ctx *ctx)
-{
- return ctx->hdev->asic_prop.mmu_pgt_addr +
- (ctx->asid * ctx->hdev->asic_prop.mmu_hop_table_size);
-}
-
-static inline u64 get_hop0_addr(struct hl_ctx *ctx)
-{
- return (u64) (uintptr_t) ctx->hdev->mmu_priv.dr.mmu_shadow_hop0 +
- (ctx->asid * ctx->hdev->asic_prop.mmu_hop_table_size);
-}
-
-static void flush(struct hl_ctx *ctx)
-{
- /* flush all writes from all cores to reach PCI */
- mb();
- ctx->hdev->asic_funcs->read_pte(ctx->hdev, get_phys_hop0_addr(ctx));
-}
-
-/* transform the value to physical address when writing to H/W */
-static inline void write_pte(struct hl_ctx *ctx, u64 shadow_pte_addr, u64 val)
-{
- /*
- * The value to write is actually the address of the next shadow hop +
- * flags at the 12 LSBs.
- * Hence in order to get the value to write to the physical PTE, we
- * clear the 12 LSBs and translate the shadow hop to its associated
- * physical hop, and add back the original 12 LSBs.
- */
- u64 phys_val = get_phys_addr(ctx, val & HOP_PHYS_ADDR_MASK) |
- (val & FLAGS_MASK);
-
- ctx->hdev->asic_funcs->write_pte(ctx->hdev,
- get_phys_addr(ctx, shadow_pte_addr),
- phys_val);
-
- *(u64 *) (uintptr_t) shadow_pte_addr = val;
-}
-
-/* do not transform the value to physical address when writing to H/W */
-static inline void write_final_pte(struct hl_ctx *ctx, u64 shadow_pte_addr,
- u64 val)
-{
- ctx->hdev->asic_funcs->write_pte(ctx->hdev,
- get_phys_addr(ctx, shadow_pte_addr),
- val);
- *(u64 *) (uintptr_t) shadow_pte_addr = val;
-}
-
-/* clear the last and present bits */
-static inline void clear_pte(struct hl_ctx *ctx, u64 pte_addr)
-{
- /* no need to transform the value to physical address */
- write_final_pte(ctx, pte_addr, 0);
-}
-
-static inline void get_pte(struct hl_ctx *ctx, u64 hop_addr)
-{
- get_pgt_info(ctx, hop_addr)->num_of_ptes++;
-}
-
-/*
- * put_pte - decrement the num of ptes and free the hop if possible
- *
- * @ctx: pointer to the context structure
- * @hop_addr: addr of the hop
- *
- * This function returns the number of ptes left on this hop. If the number is
- * 0, it means the pte was freed.
- */
-static inline int put_pte(struct hl_ctx *ctx, u64 hop_addr)
-{
- struct pgt_info *pgt_info = get_pgt_info(ctx, hop_addr);
- int num_of_ptes_left;
-
- pgt_info->num_of_ptes--;
-
- /*
- * Need to save the number of ptes left because free_hop might free
- * the pgt_info
- */
- num_of_ptes_left = pgt_info->num_of_ptes;
- if (!num_of_ptes_left)
- _free_hop(ctx, pgt_info);
-
- return num_of_ptes_left;
-}
-
-static inline u64 get_hop_pte_addr(struct hl_ctx *ctx, struct hl_mmu_properties *mmu_prop,
- u64 *hop_addr_arr, u64 virt_addr, enum mmu_hop_num hop_idx)
-{
- u64 mask, shift;
-
- mask = mmu_prop->hop_masks[hop_idx];
- shift = mmu_prop->hop_shifts[hop_idx];
- return hop_addr_arr[hop_idx] +
- ctx->hdev->asic_prop.mmu_pte_size * ((virt_addr & mask) >> shift);
-}
-
-static inline u64 get_alloc_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte,
- bool *is_new_hop)
-{
- u64 hop_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte);
-
- if (hop_addr == ULLONG_MAX) {
- hop_addr = alloc_hop(ctx);
- *is_new_hop = (hop_addr != ULLONG_MAX);
- }
-
- return hop_addr;
-}
-
-/* translates shadow address inside hop to a physical address */
-static inline u64 get_phys_addr(struct hl_ctx *ctx, u64 shadow_addr)
-{
- u64 page_mask = (ctx->hdev->asic_prop.mmu_hop_table_size - 1);
- u64 shadow_hop_addr = shadow_addr & ~page_mask;
- u64 pte_offset = shadow_addr & page_mask;
- u64 phys_hop_addr;
-
- if (shadow_hop_addr != get_hop0_addr(ctx))
- phys_hop_addr = get_pgt_info(ctx, shadow_hop_addr)->phys_addr;
- else
- phys_hop_addr = get_phys_hop0_addr(ctx);
-
- return phys_hop_addr + pte_offset;
-}
-
-static int dram_default_mapping_init(struct hl_ctx *ctx)
-{
- struct hl_device *hdev = ctx->hdev;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u64 num_of_hop3, total_hops, hop0_addr, hop1_addr, hop2_addr,
- hop2_pte_addr, hop3_pte_addr, pte_val;
- int rc, i, j, hop3_allocated = 0;
-
- if ((!prop->dram_supports_virtual_memory) ||
- (!hdev->dram_default_page_mapping) ||
- (ctx->asid == HL_KERNEL_ASID_ID))
- return 0;
-
- num_of_hop3 = prop->dram_size_for_default_page_mapping;
- do_div(num_of_hop3, prop->dram_page_size);
- do_div(num_of_hop3, HOP_PTE_ENTRIES_512);
-
- /* add hop1 and hop2 */
- total_hops = num_of_hop3 + 2;
-
- ctx->dram_default_hops = kzalloc(HL_PTE_SIZE * total_hops, GFP_KERNEL);
- if (!ctx->dram_default_hops)
- return -ENOMEM;
-
- hop0_addr = get_hop0_addr(ctx);
-
- hop1_addr = alloc_hop(ctx);
- if (hop1_addr == ULLONG_MAX) {
- dev_err(hdev->dev, "failed to alloc hop 1\n");
- rc = -ENOMEM;
- goto hop1_err;
- }
-
- ctx->dram_default_hops[total_hops - 1] = hop1_addr;
-
- hop2_addr = alloc_hop(ctx);
- if (hop2_addr == ULLONG_MAX) {
- dev_err(hdev->dev, "failed to alloc hop 2\n");
- rc = -ENOMEM;
- goto hop2_err;
- }
-
- ctx->dram_default_hops[total_hops - 2] = hop2_addr;
-
- for (i = 0 ; i < num_of_hop3 ; i++) {
- ctx->dram_default_hops[i] = alloc_hop(ctx);
- if (ctx->dram_default_hops[i] == ULLONG_MAX) {
- dev_err(hdev->dev, "failed to alloc hop 3, i: %d\n", i);
- rc = -ENOMEM;
- goto hop3_err;
- }
- hop3_allocated++;
- }
-
- /* need only pte 0 in hops 0 and 1 */
- pte_val = (hop1_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
- write_pte(ctx, hop0_addr, pte_val);
-
- pte_val = (hop2_addr & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
- write_pte(ctx, hop1_addr, pte_val);
- get_pte(ctx, hop1_addr);
-
- hop2_pte_addr = hop2_addr;
- for (i = 0 ; i < num_of_hop3 ; i++) {
- pte_val = (ctx->dram_default_hops[i] & HOP_PHYS_ADDR_MASK) |
- PAGE_PRESENT_MASK;
- write_pte(ctx, hop2_pte_addr, pte_val);
- get_pte(ctx, hop2_addr);
- hop2_pte_addr += HL_PTE_SIZE;
- }
-
- pte_val = (prop->mmu_dram_default_page_addr & HOP_PHYS_ADDR_MASK) |
- LAST_MASK | PAGE_PRESENT_MASK;
-
- for (i = 0 ; i < num_of_hop3 ; i++) {
- hop3_pte_addr = ctx->dram_default_hops[i];
- for (j = 0 ; j < HOP_PTE_ENTRIES_512 ; j++) {
- write_final_pte(ctx, hop3_pte_addr, pte_val);
- get_pte(ctx, ctx->dram_default_hops[i]);
- hop3_pte_addr += HL_PTE_SIZE;
- }
- }
-
- flush(ctx);
-
- return 0;
-
-hop3_err:
- for (i = 0 ; i < hop3_allocated ; i++)
- free_hop(ctx, ctx->dram_default_hops[i]);
-
- free_hop(ctx, hop2_addr);
-hop2_err:
- free_hop(ctx, hop1_addr);
-hop1_err:
- kfree(ctx->dram_default_hops);
-
- return rc;
-}
-
-static void dram_default_mapping_fini(struct hl_ctx *ctx)
-{
- struct hl_device *hdev = ctx->hdev;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u64 num_of_hop3, total_hops, hop0_addr, hop1_addr, hop2_addr,
- hop2_pte_addr, hop3_pte_addr;
- int i, j;
-
- if ((!prop->dram_supports_virtual_memory) ||
- (!hdev->dram_default_page_mapping) ||
- (ctx->asid == HL_KERNEL_ASID_ID))
- return;
-
- num_of_hop3 = prop->dram_size_for_default_page_mapping;
- do_div(num_of_hop3, prop->dram_page_size);
- do_div(num_of_hop3, HOP_PTE_ENTRIES_512);
-
- hop0_addr = get_hop0_addr(ctx);
- /* add hop1 and hop2 */
- total_hops = num_of_hop3 + 2;
- hop1_addr = ctx->dram_default_hops[total_hops - 1];
- hop2_addr = ctx->dram_default_hops[total_hops - 2];
-
- for (i = 0 ; i < num_of_hop3 ; i++) {
- hop3_pte_addr = ctx->dram_default_hops[i];
- for (j = 0 ; j < HOP_PTE_ENTRIES_512 ; j++) {
- clear_pte(ctx, hop3_pte_addr);
- put_pte(ctx, ctx->dram_default_hops[i]);
- hop3_pte_addr += HL_PTE_SIZE;
- }
- }
-
- hop2_pte_addr = hop2_addr;
- for (i = 0 ; i < num_of_hop3 ; i++) {
- clear_pte(ctx, hop2_pte_addr);
- put_pte(ctx, hop2_addr);
- hop2_pte_addr += HL_PTE_SIZE;
- }
-
- clear_pte(ctx, hop1_addr);
- put_pte(ctx, hop1_addr);
- clear_pte(ctx, hop0_addr);
-
- kfree(ctx->dram_default_hops);
-
- flush(ctx);
-}
-
-/**
- * hl_mmu_v1_init() - initialize the MMU module.
- * @hdev: habanalabs device structure.
- *
- * This function does the following:
- * - Create a pool of pages for pgt_infos.
- * - Create a shadow table for pgt
- *
- * Return: 0 for success, non-zero for failure.
- */
-static int hl_mmu_v1_init(struct hl_device *hdev)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- int rc;
-
- hdev->mmu_priv.dr.mmu_pgt_pool =
- gen_pool_create(__ffs(prop->mmu_hop_table_size), -1);
-
- if (!hdev->mmu_priv.dr.mmu_pgt_pool) {
- dev_err(hdev->dev, "Failed to create page gen pool\n");
- return -ENOMEM;
- }
-
- rc = gen_pool_add(hdev->mmu_priv.dr.mmu_pgt_pool, prop->mmu_pgt_addr +
- prop->mmu_hop0_tables_total_size,
- prop->mmu_pgt_size - prop->mmu_hop0_tables_total_size,
- -1);
- if (rc) {
- dev_err(hdev->dev, "Failed to add memory to page gen pool\n");
- goto err_pool_add;
- }
-
- hdev->mmu_priv.dr.mmu_shadow_hop0 = kvcalloc(prop->max_asid, prop->mmu_hop_table_size,
- GFP_KERNEL);
- if (ZERO_OR_NULL_PTR(hdev->mmu_priv.dr.mmu_shadow_hop0)) {
- rc = -ENOMEM;
- goto err_pool_add;
- }
-
- /* MMU H/W init will be done in device hw_init() */
-
- return 0;
-
-err_pool_add:
- gen_pool_destroy(hdev->mmu_priv.dr.mmu_pgt_pool);
-
- return rc;
-}
-
-/**
- * hl_mmu_v1_fini() - release the MMU module.
- * @hdev: habanalabs device structure.
- *
- * This function does the following:
- * - Disable MMU in H/W.
- * - Free the pgt_infos pool.
- *
- * All contexts should be freed before calling this function.
- */
-static void hl_mmu_v1_fini(struct hl_device *hdev)
-{
- /* MMU H/W fini was already done in device hw_fini() */
-
- if (!ZERO_OR_NULL_PTR(hdev->mmu_priv.dr.mmu_shadow_hop0)) {
- kvfree(hdev->mmu_priv.dr.mmu_shadow_hop0);
- gen_pool_destroy(hdev->mmu_priv.dr.mmu_pgt_pool);
-
- /* Make sure that if we arrive here again without init was
- * called we won't cause kernel panic. This can happen for
- * example if we fail during hard reset code at certain points
- */
- hdev->mmu_priv.dr.mmu_shadow_hop0 = NULL;
- }
-}
-
-/**
- * hl_mmu_v1_ctx_init() - initialize a context for using the MMU module.
- * @ctx: pointer to the context structure to initialize.
- *
- * Initialize a mutex to protect the concurrent mapping flow, a hash to hold all
- * page tables hops related to this context.
- * Return: 0 on success, non-zero otherwise.
- */
-static int hl_mmu_v1_ctx_init(struct hl_ctx *ctx)
-{
- hash_init(ctx->mmu_shadow_hash);
- return dram_default_mapping_init(ctx);
-}
-
-/*
- * hl_mmu_ctx_fini - disable a ctx from using the mmu module
- *
- * @ctx: pointer to the context structure
- *
- * This function does the following:
- * - Free any pgts which were not freed yet
- * - Free the mutex
- * - Free DRAM default page mapping hops
- */
-static void hl_mmu_v1_ctx_fini(struct hl_ctx *ctx)
-{
- struct hl_device *hdev = ctx->hdev;
- struct pgt_info *pgt_info;
- struct hlist_node *tmp;
- int i;
-
- dram_default_mapping_fini(ctx);
-
- if (!hash_empty(ctx->mmu_shadow_hash))
- dev_err(hdev->dev, "ctx %d is freed while it has pgts in use\n",
- ctx->asid);
-
- hash_for_each_safe(ctx->mmu_shadow_hash, i, tmp, pgt_info, node) {
- dev_err_ratelimited(hdev->dev,
- "pgt_info of addr 0x%llx of asid %d was not destroyed, num_ptes: %d\n",
- pgt_info->phys_addr, ctx->asid, pgt_info->num_of_ptes);
- _free_hop(ctx, pgt_info);
- }
-}
-
-static int hl_mmu_v1_unmap(struct hl_ctx *ctx,
- u64 virt_addr, bool is_dram_addr)
-{
- u64 hop_addr[MMU_V1_MAX_HOPS] = {0}, hop_pte_addr[MMU_V1_MAX_HOPS] = {0}, curr_pte = 0;
- struct hl_device *hdev = ctx->hdev;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct hl_mmu_properties *mmu_prop;
- bool is_huge, clear_hop3 = true;
- int hop_idx;
-
- /* shifts and masks are the same in PMMU and HPMMU, use one of them */
- mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu;
-
- for (hop_idx = MMU_HOP0; hop_idx < MMU_HOP4; hop_idx++) {
- if (hop_idx == MMU_HOP0) {
- hop_addr[hop_idx] = get_hop0_addr(ctx);
- } else {
- hop_addr[hop_idx] = hl_mmu_get_next_hop_addr(ctx, curr_pte);
- if (hop_addr[hop_idx] == ULLONG_MAX)
- goto not_mapped;
- }
-
- hop_pte_addr[hop_idx] =
- get_hop_pte_addr(ctx, mmu_prop, hop_addr, virt_addr, hop_idx);
-
- curr_pte = *(u64 *) (uintptr_t) hop_pte_addr[hop_idx];
- }
-
- is_huge = curr_pte & mmu_prop->last_mask;
-
- if (is_dram_addr && !is_huge) {
- dev_err(hdev->dev, "DRAM unmapping should use huge pages only\n");
- return -EFAULT;
- }
-
- if (!is_huge) {
- hop_idx = MMU_HOP4;
- hop_addr[hop_idx] = hl_mmu_get_next_hop_addr(ctx, curr_pte);
- if (hop_addr[hop_idx] == ULLONG_MAX)
- goto not_mapped;
-
- hop_pte_addr[hop_idx] =
- get_hop_pte_addr(ctx, mmu_prop, hop_addr, virt_addr, hop_idx);
- curr_pte = *(u64 *) (uintptr_t) hop_pte_addr[hop_idx];
- clear_hop3 = false;
- }
-
- if (hdev->dram_default_page_mapping && is_dram_addr) {
- u64 default_pte = (prop->mmu_dram_default_page_addr &
- HOP_PHYS_ADDR_MASK) | mmu_prop->last_mask |
- PAGE_PRESENT_MASK;
- if (curr_pte == default_pte) {
- dev_err(hdev->dev,
- "DRAM: hop3 PTE points to zero page, can't unmap, va: 0x%llx\n",
- virt_addr);
- goto not_mapped;
- }
-
- if (!(curr_pte & PAGE_PRESENT_MASK)) {
- dev_err(hdev->dev,
- "DRAM: hop3 PTE is cleared! can't unmap, va: 0x%llx\n",
- virt_addr);
- goto not_mapped;
- }
-
- hop_idx = MMU_HOP3;
- write_final_pte(ctx, hop_pte_addr[hop_idx], default_pte);
- put_pte(ctx, hop_addr[hop_idx]);
- } else {
- if (!(curr_pte & PAGE_PRESENT_MASK))
- goto not_mapped;
-
- if (hop_addr[MMU_HOP4])
- clear_pte(ctx, hop_pte_addr[MMU_HOP4]);
- else
- clear_pte(ctx, hop_pte_addr[MMU_HOP3]);
-
- if (hop_addr[MMU_HOP4] && !put_pte(ctx, hop_addr[MMU_HOP4]))
- clear_hop3 = true;
-
- if (!clear_hop3)
- goto mapped;
-
- for (hop_idx = MMU_HOP3; hop_idx >= 0; hop_idx--) {
- clear_pte(ctx, hop_pte_addr[hop_idx]);
-
- if (hop_idx == MMU_HOP0)
- break;
-
- if (put_pte(ctx, hop_addr[hop_idx]))
- goto mapped;
- }
- }
-
-mapped:
- return 0;
-
-not_mapped:
- dev_err(hdev->dev, "virt addr 0x%llx is not mapped to phys addr\n",
- virt_addr);
-
- return -EINVAL;
-}
-
-static int hl_mmu_v1_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr,
- u32 page_size, bool is_dram_addr)
-{
- u64 hop_addr[MMU_V1_MAX_HOPS] = {0}, hop_pte_addr[MMU_V1_MAX_HOPS] = {0}, curr_pte = 0;
- struct hl_device *hdev = ctx->hdev;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct hl_mmu_properties *mmu_prop;
- bool is_huge, hop_new[MMU_V1_MAX_HOPS] = {false};
- int num_hops, hop_idx, prev_hop, rc = -ENOMEM;
-
- /*
- * This mapping function can map a page or a huge page. For huge page
- * there are only 3 hops rather than 4. Currently the DRAM allocation
- * uses huge pages only but user memory could have been allocated with
- * one of the two page sizes. Since this is a common code for all the
- * three cases, we need this hugs page check.
- */
- if (is_dram_addr) {
- mmu_prop = &prop->dmmu;
- is_huge = true;
- } else if (page_size == prop->pmmu_huge.page_size) {
- mmu_prop = &prop->pmmu_huge;
- is_huge = true;
- } else {
- mmu_prop = &prop->pmmu;
- is_huge = false;
- }
-
- num_hops = is_huge ? (MMU_V1_MAX_HOPS - 1) : MMU_V1_MAX_HOPS;
-
- for (hop_idx = MMU_HOP0; hop_idx < num_hops; hop_idx++) {
- if (hop_idx == MMU_HOP0) {
- hop_addr[hop_idx] = get_hop0_addr(ctx);
- } else {
- hop_addr[hop_idx] =
- get_alloc_next_hop_addr(ctx, curr_pte, &hop_new[hop_idx]);
- if (hop_addr[hop_idx] == ULLONG_MAX)
- goto err;
- }
-
- hop_pte_addr[hop_idx] =
- get_hop_pte_addr(ctx, mmu_prop, hop_addr, virt_addr, hop_idx);
- curr_pte = *(u64 *) (uintptr_t) hop_pte_addr[hop_idx];
- }
-
- if (hdev->dram_default_page_mapping && is_dram_addr) {
- u64 default_pte = (prop->mmu_dram_default_page_addr &
- HOP_PHYS_ADDR_MASK) | mmu_prop->last_mask |
- PAGE_PRESENT_MASK;
-
- if (curr_pte != default_pte) {
- dev_err(hdev->dev,
- "DRAM: mapping already exists for virt_addr 0x%llx\n",
- virt_addr);
- rc = -EINVAL;
- goto err;
- }
-
- for (hop_idx = MMU_HOP1; hop_idx < num_hops; hop_idx++) {
- if (hop_new[hop_idx]) {
- dev_err(hdev->dev, "DRAM mapping should not allocate more hops\n");
- rc = -EFAULT;
- goto err;
- }
- }
- } else if (curr_pte & PAGE_PRESENT_MASK) {
- dev_err(hdev->dev,
- "mapping already exists for virt_addr 0x%llx\n",
- virt_addr);
-
- for (hop_idx = MMU_HOP0; hop_idx < num_hops; hop_idx++)
- dev_dbg(hdev->dev, "hop%d pte: 0x%llx (0x%llx)\n", hop_idx,
- *(u64 *) (uintptr_t) hop_pte_addr[hop_idx],
- hop_pte_addr[hop_idx]);
-
- rc = -EINVAL;
- goto err;
- }
-
- curr_pte = (phys_addr & HOP_PHYS_ADDR_MASK) | mmu_prop->last_mask
- | PAGE_PRESENT_MASK;
-
- write_final_pte(ctx, hop_pte_addr[num_hops - 1], curr_pte);
-
- for (hop_idx = MMU_HOP1; hop_idx < num_hops; hop_idx++) {
- prev_hop = hop_idx - 1;
-
- if (hop_new[hop_idx]) {
- curr_pte = (hop_addr[hop_idx] & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;
- write_pte(ctx, hop_pte_addr[prev_hop], curr_pte);
- if (hop_idx != MMU_HOP1)
- get_pte(ctx, hop_addr[prev_hop]);
- }
- }
-
- get_pte(ctx, hop_addr[num_hops - 1]);
-
- return 0;
-
-err:
- for (hop_idx = num_hops; hop_idx > MMU_HOP0; hop_idx--) {
- if (hop_new[hop_idx])
- free_hop(ctx, hop_addr[hop_idx]);
- }
-
- return rc;
-}
-
-/*
- * hl_mmu_v1_swap_out - marks all mapping of the given ctx as swapped out
- *
- * @ctx: pointer to the context structure
- *
- */
-static void hl_mmu_v1_swap_out(struct hl_ctx *ctx)
-{
-
-}
-
-/*
- * hl_mmu_v1_swap_in - marks all mapping of the given ctx as swapped in
- *
- * @ctx: pointer to the context structure
- *
- */
-static void hl_mmu_v1_swap_in(struct hl_ctx *ctx)
-{
-
-}
-
-static int hl_mmu_v1_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr,
- struct hl_mmu_hop_info *hops)
-{
- struct hl_device *hdev = ctx->hdev;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct hl_mmu_properties *mmu_prop;
- bool is_dram_addr, is_pmmu_addr, is_pmmu_h_addr, is_huge;
- int i, used_hops;
-
- is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size,
- prop->dmmu.start_addr,
- prop->dmmu.end_addr);
- is_pmmu_addr = hl_mem_area_inside_range(virt_addr, prop->pmmu.page_size,
- prop->pmmu.start_addr,
- prop->pmmu.end_addr);
- is_pmmu_h_addr = hl_mem_area_inside_range(virt_addr,
- prop->pmmu_huge.page_size,
- prop->pmmu_huge.start_addr,
- prop->pmmu_huge.end_addr);
- if (is_dram_addr) {
- mmu_prop = &prop->dmmu;
- is_huge = true;
- } else if (is_pmmu_addr) {
- mmu_prop = &prop->pmmu;
- is_huge = false;
- } else if (is_pmmu_h_addr) {
- mmu_prop = &prop->pmmu_huge;
- is_huge = true;
- } else {
- return -EINVAL;
- }
-
- used_hops = mmu_prop->num_hops;
-
- /* huge pages use lesser hops */
- if (is_huge)
- used_hops--;
-
- hops->hop_info[0].hop_addr = get_phys_hop0_addr(ctx);
- hops->hop_info[0].hop_pte_addr =
- hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, 0,
- hops->hop_info[0].hop_addr, virt_addr);
- hops->hop_info[0].hop_pte_val =
- hdev->asic_funcs->read_pte(hdev,
- hops->hop_info[0].hop_pte_addr);
-
- for (i = 1 ; i < used_hops ; i++) {
- hops->hop_info[i].hop_addr =
- hl_mmu_get_next_hop_addr(ctx,
- hops->hop_info[i - 1].hop_pte_val);
- if (hops->hop_info[i].hop_addr == ULLONG_MAX)
- return -EFAULT;
-
- hops->hop_info[i].hop_pte_addr =
- hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, i,
- hops->hop_info[i].hop_addr,
- virt_addr);
- hops->hop_info[i].hop_pte_val =
- hdev->asic_funcs->read_pte(hdev,
- hops->hop_info[i].hop_pte_addr);
-
- if (!(hops->hop_info[i].hop_pte_val & PAGE_PRESENT_MASK))
- return -EFAULT;
-
- if (hops->hop_info[i].hop_pte_val & mmu_prop->last_mask)
- break;
- }
-
- /* if passed over all hops then no last hop was found */
- if (i == mmu_prop->num_hops)
- return -EFAULT;
-
- if (!(hops->hop_info[i].hop_pte_val & PAGE_PRESENT_MASK))
- return -EFAULT;
-
- hops->used_hops = i + 1;
-
- return 0;
-}
-
-/*
- * hl_mmu_v1_prepare - prepare mmu for working with mmu v1
- *
- * @hdev: pointer to the device structure
- */
-void hl_mmu_v1_set_funcs(struct hl_device *hdev, struct hl_mmu_funcs *mmu)
-{
- mmu->init = hl_mmu_v1_init;
- mmu->fini = hl_mmu_v1_fini;
- mmu->ctx_init = hl_mmu_v1_ctx_init;
- mmu->ctx_fini = hl_mmu_v1_ctx_fini;
- mmu->map = hl_mmu_v1_map;
- mmu->unmap = hl_mmu_v1_unmap;
- mmu->flush = flush;
- mmu->swap_out = hl_mmu_v1_swap_out;
- mmu->swap_in = hl_mmu_v1_swap_in;
- mmu->get_tlb_info = hl_mmu_v1_get_tlb_info;
-}
diff --git a/drivers/misc/habanalabs/common/mmu/mmu_v2_hr.c b/drivers/misc/habanalabs/common/mmu/mmu_v2_hr.c
deleted file mode 100644
index afe7ef964f82..000000000000
--- a/drivers/misc/habanalabs/common/mmu/mmu_v2_hr.c
+++ /dev/null
@@ -1,399 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2020-2022 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include "../habanalabs.h"
-#include "../../include/hw_ip/mmu/mmu_general.h"
-
-#include <linux/slab.h>
-
-static struct pgt_info *hl_mmu_v2_hr_get_pgt_info(struct hl_ctx *ctx, u64 phys_hop_addr)
-{
- struct pgt_info *pgt_info = NULL;
-
- hash_for_each_possible(ctx->hr_mmu_phys_hash, pgt_info, node,
- (unsigned long) phys_hop_addr)
- if (phys_hop_addr == pgt_info->phys_addr)
- break;
-
- return pgt_info;
-}
-
-static void hl_mmu_v2_hr_add_pgt_info(struct hl_ctx *ctx, struct pgt_info *pgt_info,
- dma_addr_t phys_addr)
-{
- hash_add(ctx->hr_mmu_phys_hash, &pgt_info->node, phys_addr);
-}
-
-static struct pgt_info *hl_mmu_v2_hr_get_hop0_pgt_info(struct hl_ctx *ctx)
-{
- return &ctx->hdev->mmu_priv.hr.mmu_asid_hop0[ctx->asid];
-}
-
-/**
- * hl_mmu_v2_hr_init() - initialize the MMU module.
- * @hdev: habanalabs device structure.
- *
- * This function does the following:
- * - Create a pool of pages for pgt_infos.
- * - Create a shadow table for pgt
- *
- * Return: 0 for success, non-zero for failure.
- */
-static inline int hl_mmu_v2_hr_init(struct hl_device *hdev)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
-
- return hl_mmu_hr_init(hdev, &hdev->mmu_priv.hr, prop->mmu_hop_table_size,
- prop->mmu_pgt_size);
-}
-
-/**
- * hl_mmu_v2_hr_fini() - release the MMU module.
- * @hdev: habanalabs device structure.
- *
- * This function does the following:
- * - Disable MMU in H/W.
- * - Free the pgt_infos pool.
- *
- * All contexts should be freed before calling this function.
- */
-static inline void hl_mmu_v2_hr_fini(struct hl_device *hdev)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
-
- hl_mmu_hr_fini(hdev, &hdev->mmu_priv.hr, prop->mmu_hop_table_size);
-}
-
-/**
- * hl_mmu_v2_hr_ctx_init() - initialize a context for using the MMU module.
- * @ctx: pointer to the context structure to initialize.
- *
- * Initialize a mutex to protect the concurrent mapping flow, a hash to hold all
- * page tables hops related to this context.
- * Return: 0 on success, non-zero otherwise.
- */
-static int hl_mmu_v2_hr_ctx_init(struct hl_ctx *ctx)
-{
- hash_init(ctx->hr_mmu_phys_hash);
- return 0;
-}
-
-/*
- * hl_mmu_v2_hr_ctx_fini - disable a ctx from using the mmu module
- *
- * @ctx: pointer to the context structure
- *
- * This function does the following:
- * - Free any pgts which were not freed yet
- * - Free the mutex
- * - Free DRAM default page mapping hops
- */
-static void hl_mmu_v2_hr_ctx_fini(struct hl_ctx *ctx)
-{
- struct hl_device *hdev = ctx->hdev;
- struct pgt_info *pgt_info;
- struct hlist_node *tmp;
- int i;
-
- if (!hash_empty(ctx->hr_mmu_phys_hash))
- dev_err(hdev->dev, "ctx %d is freed while it has pgts in use\n",
- ctx->asid);
-
- hash_for_each_safe(ctx->hr_mmu_phys_hash, i, tmp, pgt_info, node) {
- dev_err_ratelimited(hdev->dev,
- "pgt_info of addr 0x%llx of asid %d was not destroyed, num_ptes: %d\n",
- pgt_info->phys_addr, ctx->asid, pgt_info->num_of_ptes);
- hl_mmu_hr_free_hop_remove_pgt(pgt_info, &ctx->hdev->mmu_priv.hr,
- ctx->hdev->asic_prop.mmu_hop_table_size);
- }
-}
-
-static int _hl_mmu_v2_hr_unmap(struct hl_ctx *ctx,
- u64 virt_addr, bool is_dram_addr)
-{
- u64 curr_pte, scrambled_virt_addr, hop_pte_phys_addr[MMU_ARCH_6_HOPS] = { 0 };
- struct pgt_info *hops_pgt_info[MMU_ARCH_6_HOPS] = { NULL };
- struct hl_device *hdev = ctx->hdev;
- struct asic_fixed_properties *prop;
- struct hl_mmu_properties *mmu_prop;
- bool is_huge = false;
- int i, hop_last;
-
- prop = &hdev->asic_prop;
-
- /* shifts and masks are the same in PMMU and HMMU, use one of them */
- mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu;
- hop_last = mmu_prop->num_hops - 1;
-
- scrambled_virt_addr = hdev->asic_funcs->scramble_addr(hdev, virt_addr);
- curr_pte = 0;
-
- for (i = 0 ; i < mmu_prop->num_hops ; i++) {
- /* we get HOP0 differently, it doesn't need curr_pte */
- if (i == 0)
- hops_pgt_info[i] = hl_mmu_v2_hr_get_hop0_pgt_info(ctx);
- else
- hops_pgt_info[i] = hl_mmu_hr_get_next_hop_pgt_info(ctx,
- &ctx->hdev->mmu_func[MMU_HR_PGT].hr_funcs, curr_pte);
- if (!hops_pgt_info[i])
- goto not_mapped;
-
- hop_pte_phys_addr[i] = hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, i,
- hops_pgt_info[i]->phys_addr,
- scrambled_virt_addr);
- if (hop_pte_phys_addr[i] == U64_MAX)
- return -EFAULT;
-
- curr_pte = *(u64 *) (uintptr_t) hl_mmu_hr_pte_phys_to_virt(ctx, hops_pgt_info[i],
- hop_pte_phys_addr[i],
- ctx->hdev->asic_prop.mmu_hop_table_size);
-
- if ((i < hop_last) && (curr_pte & mmu_prop->last_mask)) {
- hop_last = i;
- is_huge = true;
- break;
- }
- }
-
- if (is_dram_addr && !is_huge) {
- dev_err(hdev->dev, "DRAM unmapping should use huge pages only\n");
- return -EFAULT;
- }
-
- if (!(curr_pte & PAGE_PRESENT_MASK))
- goto not_mapped;
-
- for (i = hop_last ; i > 0 ; i--) {
- hl_mmu_hr_clear_pte(ctx, hops_pgt_info[i], hop_pte_phys_addr[i],
- ctx->hdev->asic_prop.mmu_hop_table_size);
-
- if (hl_mmu_hr_put_pte(ctx, hops_pgt_info[i], &ctx->hdev->mmu_priv.hr,
- ctx->hdev->asic_prop.mmu_hop_table_size))
- goto mapped;
- }
- hl_mmu_hr_clear_pte(ctx, hops_pgt_info[0], hop_pte_phys_addr[0],
- ctx->hdev->asic_prop.mmu_hop_table_size);
-
-mapped:
- return 0;
-
-not_mapped:
- dev_err(hdev->dev, "virt addr 0x%llx is not mapped to phys addr\n", virt_addr);
-
- return -EINVAL;
-}
-
-static int hl_mmu_v2_get_last_hop(struct hl_mmu_properties *mmu_prop, u32 page_size)
-{
- int hop;
-
- for (hop = (mmu_prop->num_hops - 1); hop; hop--) {
- if (mmu_prop->hop_shifts[hop] == 0)
- continue;
-
- if (page_size <= (1 << mmu_prop->hop_shifts[hop]))
- break;
- }
-
- return hop;
-}
-
-static int _hl_mmu_v2_hr_map(struct hl_ctx *ctx,
- u64 virt_addr, u64 phys_addr,
- u32 page_size, bool is_dram_addr)
-{
- u64 hop_pte_phys_addr[MMU_ARCH_6_HOPS] = { 0 },
- curr_pte = 0, scrambled_virt_addr, scrambled_phys_addr;
- struct pgt_info *hops_pgt_info[MMU_ARCH_6_HOPS] = { NULL };
- bool hop_new[MMU_ARCH_6_HOPS] = { false };
- struct hl_device *hdev = ctx->hdev;
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct hl_mmu_properties *mmu_prop;
- int i, hop_last, rc = -ENOMEM;
-
- /*
- * This mapping function can map a page or a huge page. For huge page
- * there are only 4 hops rather than 5. Currently the DRAM allocation
- * uses huge pages only but user memory could have been allocated with
- * one of the two page sizes. Since this is a common code for all the
- * three cases, we need this hugs page check.
- */
- if (is_dram_addr)
- mmu_prop = &prop->dmmu;
- else if (page_size == prop->pmmu_huge.page_size)
- mmu_prop = &prop->pmmu_huge;
- else
- mmu_prop = &prop->pmmu;
-
- hop_last = hl_mmu_v2_get_last_hop(mmu_prop, page_size);
- if (hop_last <= 0) {
- dev_err(ctx->hdev->dev, "Invalid last HOP %d\n", hop_last);
- return -EFAULT;
- }
-
- scrambled_virt_addr = hdev->asic_funcs->scramble_addr(hdev, virt_addr);
- scrambled_phys_addr = hdev->asic_funcs->scramble_addr(hdev, phys_addr);
-
- for (i = 0 ; i <= hop_last ; i++) {
-
- if (i == 0)
- hops_pgt_info[i] = hl_mmu_v2_hr_get_hop0_pgt_info(ctx);
- else
- hops_pgt_info[i] = hl_mmu_hr_get_alloc_next_hop(ctx,
- &ctx->hdev->mmu_priv.hr,
- &ctx->hdev->mmu_func[MMU_HR_PGT].hr_funcs,
- mmu_prop, curr_pte, &hop_new[i]);
- if (!hops_pgt_info[i])
- goto err;
-
- hop_pte_phys_addr[i] = hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, i,
- hops_pgt_info[i]->phys_addr,
- scrambled_virt_addr);
- curr_pte = *(u64 *) (uintptr_t) hl_mmu_hr_pte_phys_to_virt(ctx, hops_pgt_info[i],
- hop_pte_phys_addr[i],
- ctx->hdev->asic_prop.mmu_hop_table_size);
- }
-
- if (curr_pte & PAGE_PRESENT_MASK) {
- dev_err(hdev->dev, "mapping already exists for virt_addr 0x%llx\n",
- scrambled_virt_addr);
-
- for (i = 0 ; i <= hop_last ; i++)
- dev_dbg(hdev->dev, "hop%d pte: 0x%llx (0x%llx)\n",
- i,
- *(u64 *) (uintptr_t)
- hl_mmu_hr_pte_phys_to_virt(ctx, hops_pgt_info[i],
- hop_pte_phys_addr[i],
- ctx->hdev->asic_prop.mmu_hop_table_size),
- hop_pte_phys_addr[i]);
- rc = -EINVAL;
- goto err;
- }
-
- curr_pte = (scrambled_phys_addr & HOP_PHYS_ADDR_MASK) | mmu_prop->last_mask
- | PAGE_PRESENT_MASK;
-
- /* Write the PTEs */
- hl_mmu_hr_write_pte(ctx, hops_pgt_info[hop_last], hop_pte_phys_addr[hop_last], curr_pte,
- ctx->hdev->asic_prop.mmu_hop_table_size);
-
- /* for each new hop, add its address to the table of previous-hop */
- for (i = 1 ; i <= hop_last ; i++) {
- if (hop_new[i]) {
- curr_pte = (hops_pgt_info[i]->phys_addr & HOP_PHYS_ADDR_MASK) |
- PAGE_PRESENT_MASK;
- hl_mmu_hr_write_pte(ctx, hops_pgt_info[i - 1], hop_pte_phys_addr[i - 1],
- curr_pte, ctx->hdev->asic_prop.mmu_hop_table_size);
- if (i - 1)
- hl_mmu_hr_get_pte(ctx, &ctx->hdev->mmu_func[MMU_HR_PGT].hr_funcs,
- hops_pgt_info[i - 1]->phys_addr);
- }
- }
-
- hl_mmu_hr_get_pte(ctx, &ctx->hdev->mmu_func[MMU_HR_PGT].hr_funcs,
- hops_pgt_info[hop_last]->phys_addr);
-
- return 0;
-
-err:
- for (i = 1 ; i <= hop_last ; i++)
- if (hop_new[i] && hops_pgt_info[i])
- hl_mmu_hr_free_hop_remove_pgt(hops_pgt_info[i], &ctx->hdev->mmu_priv.hr,
- ctx->hdev->asic_prop.mmu_hop_table_size);
-
- return rc;
-}
-
-/*
- * hl_mmu_v2_swap_out - marks all mapping of the given ctx as swapped out
- *
- * @ctx: pointer to the context structure
- *
- */
-static void hl_mmu_v2_hr_swap_out(struct hl_ctx *ctx)
-{
-
-}
-
-/*
- * hl_mmu_v2_swap_in - marks all mapping of the given ctx as swapped in
- *
- * @ctx: pointer to the context structure
- *
- */
-static void hl_mmu_v2_hr_swap_in(struct hl_ctx *ctx)
-{
-
-}
-
-static int hl_mmu_v2_hr_get_tlb_mapping_params(struct hl_device *hdev,
- struct hl_mmu_properties **mmu_prop,
- struct hl_mmu_hop_info *hops,
- u64 virt_addr, bool *is_huge)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- bool is_dram_addr, is_pmmu_addr, is_pmmu_h_addr;
-
- is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size,
- prop->dmmu.start_addr,
- prop->dmmu.end_addr);
- is_pmmu_addr = hl_mem_area_inside_range(virt_addr, prop->pmmu.page_size,
- prop->pmmu.start_addr,
- prop->pmmu.end_addr);
- is_pmmu_h_addr = hl_mem_area_inside_range(virt_addr,
- prop->pmmu_huge.page_size,
- prop->pmmu_huge.start_addr,
- prop->pmmu_huge.end_addr);
- if (is_dram_addr) {
- *mmu_prop = &prop->dmmu;
- *is_huge = true;
- hops->range_type = HL_VA_RANGE_TYPE_DRAM;
- } else if (is_pmmu_addr) {
- *mmu_prop = &prop->pmmu;
- *is_huge = false;
- hops->range_type = HL_VA_RANGE_TYPE_HOST;
- } else if (is_pmmu_h_addr) {
- *mmu_prop = &prop->pmmu_huge;
- *is_huge = true;
- hops->range_type = HL_VA_RANGE_TYPE_HOST_HUGE;
- } else {
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int hl_mmu_v2_hr_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr,
- struct hl_mmu_hop_info *hops)
-{
- return hl_mmu_hr_get_tlb_info(ctx, virt_addr, hops,
- &ctx->hdev->mmu_func[MMU_HR_PGT].hr_funcs);
-}
-
-/*
- * hl_mmu_v2_prepare - prepare mmu_if for working with mmu v2
- *
- * @hdev: pointer to the device structure
- * @mmu_if: pointer to the mmu interface structure
- */
-void hl_mmu_v2_hr_set_funcs(struct hl_device *hdev, struct hl_mmu_funcs *mmu)
-{
- mmu->init = hl_mmu_v2_hr_init;
- mmu->fini = hl_mmu_v2_hr_fini;
- mmu->ctx_init = hl_mmu_v2_hr_ctx_init;
- mmu->ctx_fini = hl_mmu_v2_hr_ctx_fini;
- mmu->map = _hl_mmu_v2_hr_map;
- mmu->unmap = _hl_mmu_v2_hr_unmap;
- mmu->flush = hl_mmu_hr_flush;
- mmu->swap_out = hl_mmu_v2_hr_swap_out;
- mmu->swap_in = hl_mmu_v2_hr_swap_in;
- mmu->get_tlb_info = hl_mmu_v2_hr_get_tlb_info;
- mmu->hr_funcs.get_hop0_pgt_info = hl_mmu_v2_hr_get_hop0_pgt_info;
- mmu->hr_funcs.get_pgt_info = hl_mmu_v2_hr_get_pgt_info;
- mmu->hr_funcs.add_pgt_info = hl_mmu_v2_hr_add_pgt_info;
- mmu->hr_funcs.get_tlb_mapping_params = hl_mmu_v2_hr_get_tlb_mapping_params;
-}
diff --git a/drivers/misc/habanalabs/common/pci/Makefile b/drivers/misc/habanalabs/common/pci/Makefile
deleted file mode 100644
index dc922a686683..000000000000
--- a/drivers/misc/habanalabs/common/pci/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-HL_COMMON_PCI_FILES := common/pci/pci.o
diff --git a/drivers/misc/habanalabs/common/pci/pci.c b/drivers/misc/habanalabs/common/pci/pci.c
deleted file mode 100644
index 5fe3da5fba30..000000000000
--- a/drivers/misc/habanalabs/common/pci/pci.c
+++ /dev/null
@@ -1,433 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2019 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include "../habanalabs.h"
-#include "../../include/hw_ip/pci/pci_general.h"
-
-#include <linux/pci.h>
-
-#define HL_PLDM_PCI_ELBI_TIMEOUT_MSEC (HL_PCI_ELBI_TIMEOUT_MSEC * 100)
-
-#define IATU_REGION_CTRL_REGION_EN_MASK BIT(31)
-#define IATU_REGION_CTRL_MATCH_MODE_MASK BIT(30)
-#define IATU_REGION_CTRL_NUM_MATCH_EN_MASK BIT(19)
-#define IATU_REGION_CTRL_BAR_NUM_MASK GENMASK(10, 8)
-
-/**
- * hl_pci_bars_map() - Map PCI BARs.
- * @hdev: Pointer to hl_device structure.
- * @name: Array of BAR names.
- * @is_wc: Array with flag per BAR whether a write-combined mapping is needed.
- *
- * Request PCI regions and map them to kernel virtual addresses.
- *
- * Return: 0 on success, non-zero for failure.
- */
-int hl_pci_bars_map(struct hl_device *hdev, const char * const name[3],
- bool is_wc[3])
-{
- struct pci_dev *pdev = hdev->pdev;
- int rc, i, bar;
-
- rc = pci_request_regions(pdev, HL_NAME);
- if (rc) {
- dev_err(hdev->dev, "Cannot obtain PCI resources\n");
- return rc;
- }
-
- for (i = 0 ; i < 3 ; i++) {
- bar = i * 2; /* 64-bit BARs */
- hdev->pcie_bar[bar] = is_wc[i] ?
- pci_ioremap_wc_bar(pdev, bar) :
- pci_ioremap_bar(pdev, bar);
- if (!hdev->pcie_bar[bar]) {
- dev_err(hdev->dev, "pci_ioremap%s_bar failed for %s\n",
- is_wc[i] ? "_wc" : "", name[i]);
- rc = -ENODEV;
- goto err;
- }
- }
-
- return 0;
-
-err:
- for (i = 2 ; i >= 0 ; i--) {
- bar = i * 2; /* 64-bit BARs */
- if (hdev->pcie_bar[bar])
- iounmap(hdev->pcie_bar[bar]);
- }
-
- pci_release_regions(pdev);
-
- return rc;
-}
-
-/**
- * hl_pci_bars_unmap() - Unmap PCI BARS.
- * @hdev: Pointer to hl_device structure.
- *
- * Release all PCI BARs and unmap their virtual addresses.
- */
-static void hl_pci_bars_unmap(struct hl_device *hdev)
-{
- struct pci_dev *pdev = hdev->pdev;
- int i, bar;
-
- for (i = 2 ; i >= 0 ; i--) {
- bar = i * 2; /* 64-bit BARs */
- iounmap(hdev->pcie_bar[bar]);
- }
-
- pci_release_regions(pdev);
-}
-
-int hl_pci_elbi_read(struct hl_device *hdev, u64 addr, u32 *data)
-{
- struct pci_dev *pdev = hdev->pdev;
- ktime_t timeout;
- u64 msec;
- u32 val;
-
- if (hdev->pldm)
- msec = HL_PLDM_PCI_ELBI_TIMEOUT_MSEC;
- else
- msec = HL_PCI_ELBI_TIMEOUT_MSEC;
-
- /* Clear previous status */
- pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_STS, 0);
-
- pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_ADDR, (u32) addr);
- pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_CTRL, 0);
-
- timeout = ktime_add_ms(ktime_get(), msec);
- for (;;) {
- pci_read_config_dword(pdev, mmPCI_CONFIG_ELBI_STS, &val);
- if (val & PCI_CONFIG_ELBI_STS_MASK)
- break;
- if (ktime_compare(ktime_get(), timeout) > 0) {
- pci_read_config_dword(pdev, mmPCI_CONFIG_ELBI_STS,
- &val);
- break;
- }
-
- usleep_range(300, 500);
- }
-
- if ((val & PCI_CONFIG_ELBI_STS_MASK) == PCI_CONFIG_ELBI_STS_DONE) {
- pci_read_config_dword(pdev, mmPCI_CONFIG_ELBI_DATA, data);
-
- return 0;
- }
-
- if (val & PCI_CONFIG_ELBI_STS_ERR) {
- dev_err(hdev->dev, "Error reading from ELBI\n");
- return -EIO;
- }
-
- if (!(val & PCI_CONFIG_ELBI_STS_MASK)) {
- dev_err(hdev->dev, "ELBI read didn't finish in time\n");
- return -EIO;
- }
-
- dev_err(hdev->dev, "ELBI read has undefined bits in status\n");
- return -EIO;
-}
-
-/**
- * hl_pci_elbi_write() - Write through the ELBI interface.
- * @hdev: Pointer to hl_device structure.
- * @addr: Address to write to
- * @data: Data to write
- *
- * Return: 0 on success, negative value for failure.
- */
-static int hl_pci_elbi_write(struct hl_device *hdev, u64 addr, u32 data)
-{
- struct pci_dev *pdev = hdev->pdev;
- ktime_t timeout;
- u64 msec;
- u32 val;
-
- if (hdev->pldm)
- msec = HL_PLDM_PCI_ELBI_TIMEOUT_MSEC;
- else
- msec = HL_PCI_ELBI_TIMEOUT_MSEC;
-
- /* Clear previous status */
- pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_STS, 0);
-
- pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_ADDR, (u32) addr);
- pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_DATA, data);
- pci_write_config_dword(pdev, mmPCI_CONFIG_ELBI_CTRL,
- PCI_CONFIG_ELBI_CTRL_WRITE);
-
- timeout = ktime_add_ms(ktime_get(), msec);
- for (;;) {
- pci_read_config_dword(pdev, mmPCI_CONFIG_ELBI_STS, &val);
- if (val & PCI_CONFIG_ELBI_STS_MASK)
- break;
- if (ktime_compare(ktime_get(), timeout) > 0) {
- pci_read_config_dword(pdev, mmPCI_CONFIG_ELBI_STS,
- &val);
- break;
- }
-
- usleep_range(300, 500);
- }
-
- if ((val & PCI_CONFIG_ELBI_STS_MASK) == PCI_CONFIG_ELBI_STS_DONE)
- return 0;
-
- if (val & PCI_CONFIG_ELBI_STS_ERR)
- return -EIO;
-
- if (!(val & PCI_CONFIG_ELBI_STS_MASK)) {
- dev_err(hdev->dev, "ELBI write didn't finish in time\n");
- return -EIO;
- }
-
- dev_err(hdev->dev, "ELBI write has undefined bits in status\n");
- return -EIO;
-}
-
-/**
- * hl_pci_iatu_write() - iatu write routine.
- * @hdev: Pointer to hl_device structure.
- * @addr: Address to write to
- * @data: Data to write
- *
- * Return: 0 on success, negative value for failure.
- */
-int hl_pci_iatu_write(struct hl_device *hdev, u32 addr, u32 data)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u32 dbi_offset;
- int rc;
-
- dbi_offset = addr & 0xFFF;
-
- /* Ignore result of writing to pcie_aux_dbi_reg_addr as it could fail
- * in case the firmware security is enabled
- */
- hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0x00300000);
-
- rc = hl_pci_elbi_write(hdev, prop->pcie_dbi_base_address + dbi_offset,
- data);
-
- if (rc)
- return -EIO;
-
- return 0;
-}
-
-/**
- * hl_pci_set_inbound_region() - Configure inbound region
- * @hdev: Pointer to hl_device structure.
- * @region: Inbound region number.
- * @pci_region: Inbound region parameters.
- *
- * Configure the iATU inbound region.
- *
- * Return: 0 on success, negative value for failure.
- */
-int hl_pci_set_inbound_region(struct hl_device *hdev, u8 region,
- struct hl_inbound_pci_region *pci_region)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u64 bar_phys_base, region_base, region_end_address;
- u32 offset, ctrl_reg_val;
- int rc = 0;
-
- /* region offset */
- offset = (0x200 * region) + 0x100;
-
- if (pci_region->mode == PCI_ADDRESS_MATCH_MODE) {
- bar_phys_base = hdev->pcie_bar_phys[pci_region->bar];
- region_base = bar_phys_base + pci_region->offset_in_bar;
- region_end_address = region_base + pci_region->size - 1;
-
- rc |= hl_pci_iatu_write(hdev, offset + 0x8,
- lower_32_bits(region_base));
- rc |= hl_pci_iatu_write(hdev, offset + 0xC,
- upper_32_bits(region_base));
- rc |= hl_pci_iatu_write(hdev, offset + 0x10,
- lower_32_bits(region_end_address));
- }
-
- /* Point to the specified address */
- rc |= hl_pci_iatu_write(hdev, offset + 0x14, lower_32_bits(pci_region->addr));
- rc |= hl_pci_iatu_write(hdev, offset + 0x18, upper_32_bits(pci_region->addr));
-
- /* Set bar type as memory */
- rc |= hl_pci_iatu_write(hdev, offset + 0x0, 0);
-
- /* Enable + bar/address match + match enable + bar number */
- ctrl_reg_val = FIELD_PREP(IATU_REGION_CTRL_REGION_EN_MASK, 1);
- ctrl_reg_val |= FIELD_PREP(IATU_REGION_CTRL_MATCH_MODE_MASK, pci_region->mode);
- ctrl_reg_val |= FIELD_PREP(IATU_REGION_CTRL_NUM_MATCH_EN_MASK, 1);
-
- if (pci_region->mode == PCI_BAR_MATCH_MODE)
- ctrl_reg_val |= FIELD_PREP(IATU_REGION_CTRL_BAR_NUM_MASK, pci_region->bar);
-
- rc |= hl_pci_iatu_write(hdev, offset + 0x4, ctrl_reg_val);
-
- /* Return the DBI window to the default location
- * Ignore result of writing to pcie_aux_dbi_reg_addr as it could fail
- * in case the firmware security is enabled
- */
- hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0);
-
- if (rc)
- dev_err(hdev->dev, "failed to map bar %u to 0x%08llx\n",
- pci_region->bar, pci_region->addr);
-
- return rc;
-}
-
-/**
- * hl_pci_set_outbound_region() - Configure outbound region 0
- * @hdev: Pointer to hl_device structure.
- * @pci_region: Outbound region parameters.
- *
- * Configure the iATU outbound region 0.
- *
- * Return: 0 on success, negative value for failure.
- */
-int hl_pci_set_outbound_region(struct hl_device *hdev,
- struct hl_outbound_pci_region *pci_region)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- u64 outbound_region_end_address;
- int rc = 0;
-
- /* Outbound Region 0 */
- outbound_region_end_address =
- pci_region->addr + pci_region->size - 1;
- rc |= hl_pci_iatu_write(hdev, 0x008,
- lower_32_bits(pci_region->addr));
- rc |= hl_pci_iatu_write(hdev, 0x00C,
- upper_32_bits(pci_region->addr));
- rc |= hl_pci_iatu_write(hdev, 0x010,
- lower_32_bits(outbound_region_end_address));
- rc |= hl_pci_iatu_write(hdev, 0x014, 0);
-
- rc |= hl_pci_iatu_write(hdev, 0x018, 0);
-
- rc |= hl_pci_iatu_write(hdev, 0x020,
- upper_32_bits(outbound_region_end_address));
- /* Increase region size */
- rc |= hl_pci_iatu_write(hdev, 0x000, 0x00002000);
- /* Enable */
- rc |= hl_pci_iatu_write(hdev, 0x004, 0x80000000);
-
- /* Return the DBI window to the default location
- * Ignore result of writing to pcie_aux_dbi_reg_addr as it could fail
- * in case the firmware security is enabled
- */
- hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0);
-
- return rc;
-}
-
-/**
- * hl_get_pci_memory_region() - get PCI region for given address
- * @hdev: Pointer to hl_device structure.
- * @addr: device address
- *
- * @return region index on success, otherwise PCI_REGION_NUMBER (invalid
- * region index)
- */
-enum pci_region hl_get_pci_memory_region(struct hl_device *hdev, u64 addr)
-{
- int i;
-
- for (i = 0 ; i < PCI_REGION_NUMBER ; i++) {
- struct pci_mem_region *region = &hdev->pci_mem_region[i];
-
- if (!region->used)
- continue;
-
- if ((addr >= region->region_base) &&
- (addr < region->region_base + region->region_size))
- return i;
- }
-
- return PCI_REGION_NUMBER;
-}
-
-/**
- * hl_pci_init() - PCI initialization code.
- * @hdev: Pointer to hl_device structure.
- *
- * Set DMA masks, initialize the PCI controller and map the PCI BARs.
- *
- * Return: 0 on success, non-zero for failure.
- */
-int hl_pci_init(struct hl_device *hdev)
-{
- struct asic_fixed_properties *prop = &hdev->asic_prop;
- struct pci_dev *pdev = hdev->pdev;
- int rc;
-
- rc = pci_enable_device_mem(pdev);
- if (rc) {
- dev_err(hdev->dev, "can't enable PCI device\n");
- return rc;
- }
-
- pci_set_master(pdev);
-
- rc = hdev->asic_funcs->pci_bars_map(hdev);
- if (rc) {
- dev_err(hdev->dev, "Failed to map PCI BAR addresses\n");
- goto disable_device;
- }
-
- rc = hdev->asic_funcs->init_iatu(hdev);
- if (rc) {
- dev_err(hdev->dev, "PCI controller was not initialized successfully\n");
- goto unmap_pci_bars;
- }
-
- /* Driver must sleep in order for FW to finish the iATU configuration */
- if (hdev->asic_prop.iatu_done_by_fw)
- usleep_range(2000, 3000);
-
- rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(prop->dma_mask));
- if (rc) {
- dev_err(hdev->dev,
- "Failed to set dma mask to %d bits, error %d\n",
- prop->dma_mask, rc);
- goto unmap_pci_bars;
- }
-
- dma_set_max_seg_size(&pdev->dev, U32_MAX);
-
- return 0;
-
-unmap_pci_bars:
- hl_pci_bars_unmap(hdev);
-disable_device:
- pci_clear_master(pdev);
- pci_disable_device(pdev);
-
- return rc;
-}
-
-/**
- * hl_pci_fini() - PCI finalization code.
- * @hdev: Pointer to hl_device structure
- *
- * Unmap PCI bars and disable PCI device.
- */
-void hl_pci_fini(struct hl_device *hdev)
-{
- hl_pci_bars_unmap(hdev);
-
- pci_clear_master(hdev->pdev);
- pci_disable_device(hdev->pdev);
-}
diff --git a/drivers/misc/habanalabs/common/security.c b/drivers/misc/habanalabs/common/security.c
deleted file mode 100644
index 6196c0487c8b..000000000000
--- a/drivers/misc/habanalabs/common/security.c
+++ /dev/null
@@ -1,600 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2020 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include "habanalabs.h"
-
-/**
- * hl_get_pb_block - return the relevant block within the block array
- *
- * @hdev: pointer to hl_device structure
- * @mm_reg_addr: register address in the desired block
- * @pb_blocks: blocks array
- * @array_size: blocks array size
- *
- */
-static int hl_get_pb_block(struct hl_device *hdev, u32 mm_reg_addr,
- const u32 pb_blocks[], int array_size)
-{
- int i;
- u32 start_addr, end_addr;
-
- for (i = 0 ; i < array_size ; i++) {
- start_addr = pb_blocks[i];
- end_addr = start_addr + HL_BLOCK_SIZE;
-
- if ((mm_reg_addr >= start_addr) && (mm_reg_addr < end_addr))
- return i;
- }
-
- dev_err(hdev->dev, "No protection domain was found for 0x%x\n",
- mm_reg_addr);
- return -EDOM;
-}
-
-/**
- * hl_unset_pb_in_block - clear a specific protection bit in a block
- *
- * @hdev: pointer to hl_device structure
- * @reg_offset: register offset will be converted to bit offset in pb block
- * @sgs_entry: pb array
- *
- */
-static int hl_unset_pb_in_block(struct hl_device *hdev, u32 reg_offset,
- struct hl_block_glbl_sec *sgs_entry)
-{
- if ((reg_offset >= HL_BLOCK_SIZE) || (reg_offset & 0x3)) {
- dev_err(hdev->dev,
- "Register offset(%d) is out of range(%d) or invalid\n",
- reg_offset, HL_BLOCK_SIZE);
- return -EINVAL;
- }
-
- UNSET_GLBL_SEC_BIT(sgs_entry->sec_array,
- (reg_offset & (HL_BLOCK_SIZE - 1)) >> 2);
-
- return 0;
-}
-
-/**
- * hl_unsecure_register - locate the relevant block for this register and
- * remove corresponding protection bit
- *
- * @hdev: pointer to hl_device structure
- * @mm_reg_addr: register address to unsecure
- * @offset: additional offset to the register address
- * @pb_blocks: blocks array
- * @sgs_array: pb array
- * @array_size: blocks array size
- *
- */
-int hl_unsecure_register(struct hl_device *hdev, u32 mm_reg_addr, int offset,
- const u32 pb_blocks[], struct hl_block_glbl_sec sgs_array[],
- int array_size)
-{
- u32 reg_offset;
- int block_num;
-
- block_num = hl_get_pb_block(hdev, mm_reg_addr + offset, pb_blocks,
- array_size);
- if (block_num < 0)
- return block_num;
-
- reg_offset = (mm_reg_addr + offset) - pb_blocks[block_num];
-
- return hl_unset_pb_in_block(hdev, reg_offset, &sgs_array[block_num]);
-}
-
-/**
- * hl_unsecure_register_range - locate the relevant block for this register
- * range and remove corresponding protection bit
- *
- * @hdev: pointer to hl_device structure
- * @mm_reg_range: register address range to unsecure
- * @offset: additional offset to the register address
- * @pb_blocks: blocks array
- * @sgs_array: pb array
- * @array_size: blocks array size
- *
- */
-static int hl_unsecure_register_range(struct hl_device *hdev,
- struct range mm_reg_range, int offset, const u32 pb_blocks[],
- struct hl_block_glbl_sec sgs_array[],
- int array_size)
-{
- u32 reg_offset;
- int i, block_num, rc = 0;
-
- block_num = hl_get_pb_block(hdev,
- mm_reg_range.start + offset, pb_blocks,
- array_size);
- if (block_num < 0)
- return block_num;
-
- for (i = mm_reg_range.start ; i <= mm_reg_range.end ; i += 4) {
- reg_offset = (i + offset) - pb_blocks[block_num];
- rc |= hl_unset_pb_in_block(hdev, reg_offset,
- &sgs_array[block_num]);
- }
-
- return rc;
-}
-
-/**
- * hl_unsecure_registers - locate the relevant block for all registers and
- * remove corresponding protection bit
- *
- * @hdev: pointer to hl_device structure
- * @mm_reg_array: register address array to unsecure
- * @mm_array_size: register array size
- * @offset: additional offset to the register address
- * @pb_blocks: blocks array
- * @sgs_array: pb array
- * @blocks_array_size: blocks array size
- *
- */
-int hl_unsecure_registers(struct hl_device *hdev, const u32 mm_reg_array[],
- int mm_array_size, int offset, const u32 pb_blocks[],
- struct hl_block_glbl_sec sgs_array[], int blocks_array_size)
-{
- int i, rc = 0;
-
- for (i = 0 ; i < mm_array_size ; i++) {
- rc = hl_unsecure_register(hdev, mm_reg_array[i], offset,
- pb_blocks, sgs_array, blocks_array_size);
-
- if (rc)
- return rc;
- }
-
- return rc;
-}
-
-/**
- * hl_unsecure_registers_range - locate the relevant block for all register
- * ranges and remove corresponding protection bit
- *
- * @hdev: pointer to hl_device structure
- * @mm_reg_range_array: register address range array to unsecure
- * @mm_array_size: register array size
- * @offset: additional offset to the register address
- * @pb_blocks: blocks array
- * @sgs_array: pb array
- * @blocks_array_size: blocks array size
- *
- */
-static int hl_unsecure_registers_range(struct hl_device *hdev,
- const struct range mm_reg_range_array[], int mm_array_size,
- int offset, const u32 pb_blocks[],
- struct hl_block_glbl_sec sgs_array[], int blocks_array_size)
-{
- int i, rc = 0;
-
- for (i = 0 ; i < mm_array_size ; i++) {
- rc = hl_unsecure_register_range(hdev, mm_reg_range_array[i],
- offset, pb_blocks, sgs_array, blocks_array_size);
-
- if (rc)
- return rc;
- }
-
- return rc;
-}
-
-/**
- * hl_ack_pb_security_violations - Ack security violation
- *
- * @hdev: pointer to hl_device structure
- * @pb_blocks: blocks array
- * @block_offset: additional offset to the block
- * @array_size: blocks array size
- *
- */
-static void hl_ack_pb_security_violations(struct hl_device *hdev,
- const u32 pb_blocks[], u32 block_offset, int array_size)
-{
- int i;
- u32 cause, addr, block_base;
-
- for (i = 0 ; i < array_size ; i++) {
- block_base = pb_blocks[i] + block_offset;
- cause = RREG32(block_base + HL_BLOCK_GLBL_ERR_CAUSE);
- if (cause) {
- addr = RREG32(block_base + HL_BLOCK_GLBL_ERR_ADDR);
- hdev->asic_funcs->pb_print_security_errors(hdev,
- block_base, cause, addr);
- WREG32(block_base + HL_BLOCK_GLBL_ERR_CAUSE, cause);
- }
- }
-}
-
-/**
- * hl_config_glbl_sec - set pb in HW according to given pb array
- *
- * @hdev: pointer to hl_device structure
- * @pb_blocks: blocks array
- * @sgs_array: pb array
- * @block_offset: additional offset to the block
- * @array_size: blocks array size
- *
- */
-void hl_config_glbl_sec(struct hl_device *hdev, const u32 pb_blocks[],
- struct hl_block_glbl_sec sgs_array[], u32 block_offset,
- int array_size)
-{
- int i, j;
- u32 sgs_base;
-
- if (hdev->pldm)
- usleep_range(100, 1000);
-
- for (i = 0 ; i < array_size ; i++) {
- sgs_base = block_offset + pb_blocks[i] +
- HL_BLOCK_GLBL_SEC_OFFS;
-
- for (j = 0 ; j < HL_BLOCK_GLBL_SEC_LEN ; j++)
- WREG32(sgs_base + j * sizeof(u32),
- sgs_array[i].sec_array[j]);
- }
-}
-
-/**
- * hl_secure_block - locally memsets a block to 0
- *
- * @hdev: pointer to hl_device structure
- * @sgs_array: pb array to clear
- * @array_size: blocks array size
- *
- */
-void hl_secure_block(struct hl_device *hdev,
- struct hl_block_glbl_sec sgs_array[], int array_size)
-{
- int i;
-
- for (i = 0 ; i < array_size ; i++)
- memset((char *)(sgs_array[i].sec_array), 0,
- HL_BLOCK_GLBL_SEC_SIZE);
-}
-
-/**
- * hl_init_pb_with_mask - set selected pb instances with mask in HW according
- * to given configuration
- *
- * @hdev: pointer to hl_device structure
- * @num_dcores: number of decores to apply configuration to
- * set to HL_PB_SHARED if need to apply only once
- * @dcore_offset: offset between dcores
- * @num_instances: number of instances to apply configuration to
- * @instance_offset: offset between instances
- * @pb_blocks: blocks array
- * @blocks_array_size: blocks array size
- * @regs_array: register array
- * @regs_array_size: register array size
- * @mask: enabled instances mask: 1- enabled, 0- disabled
- */
-int hl_init_pb_with_mask(struct hl_device *hdev, u32 num_dcores,
- u32 dcore_offset, u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size,
- const u32 *regs_array, u32 regs_array_size, u64 mask)
-{
- int i, j;
- struct hl_block_glbl_sec *glbl_sec;
-
- glbl_sec = kcalloc(blocks_array_size,
- sizeof(struct hl_block_glbl_sec),
- GFP_KERNEL);
- if (!glbl_sec)
- return -ENOMEM;
-
- hl_secure_block(hdev, glbl_sec, blocks_array_size);
- hl_unsecure_registers(hdev, regs_array, regs_array_size, 0, pb_blocks,
- glbl_sec, blocks_array_size);
-
- /* Fill all blocks with the same configuration */
- for (i = 0 ; i < num_dcores ; i++) {
- for (j = 0 ; j < num_instances ; j++) {
- int seq = i * num_instances + j;
-
- if (!(mask & BIT_ULL(seq)))
- continue;
-
- hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
- i * dcore_offset + j * instance_offset,
- blocks_array_size);
- }
- }
-
- kfree(glbl_sec);
-
- return 0;
-}
-
-/**
- * hl_init_pb - set pb in HW according to given configuration
- *
- * @hdev: pointer to hl_device structure
- * @num_dcores: number of decores to apply configuration to
- * set to HL_PB_SHARED if need to apply only once
- * @dcore_offset: offset between dcores
- * @num_instances: number of instances to apply configuration to
- * @instance_offset: offset between instances
- * @pb_blocks: blocks array
- * @blocks_array_size: blocks array size
- * @regs_array: register array
- * @regs_array_size: register array size
- *
- */
-int hl_init_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset,
- u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size,
- const u32 *regs_array, u32 regs_array_size)
-{
- return hl_init_pb_with_mask(hdev, num_dcores, dcore_offset,
- num_instances, instance_offset, pb_blocks,
- blocks_array_size, regs_array, regs_array_size,
- ULLONG_MAX);
-}
-
-/**
- * hl_init_pb_ranges_with_mask - set pb instances using mask in HW according to
- * given configuration unsecurring registers
- * ranges instead of specific registers
- *
- * @hdev: pointer to hl_device structure
- * @num_dcores: number of decores to apply configuration to
- * set to HL_PB_SHARED if need to apply only once
- * @dcore_offset: offset between dcores
- * @num_instances: number of instances to apply configuration to
- * @instance_offset: offset between instances
- * @pb_blocks: blocks array
- * @blocks_array_size: blocks array size
- * @regs_range_array: register range array
- * @regs_range_array_size: register range array size
- * @mask: enabled instances mask: 1- enabled, 0- disabled
- */
-int hl_init_pb_ranges_with_mask(struct hl_device *hdev, u32 num_dcores,
- u32 dcore_offset, u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size,
- const struct range *regs_range_array, u32 regs_range_array_size,
- u64 mask)
-{
- int i, j, rc = 0;
- struct hl_block_glbl_sec *glbl_sec;
-
- glbl_sec = kcalloc(blocks_array_size,
- sizeof(struct hl_block_glbl_sec),
- GFP_KERNEL);
- if (!glbl_sec)
- return -ENOMEM;
-
- hl_secure_block(hdev, glbl_sec, blocks_array_size);
- rc = hl_unsecure_registers_range(hdev, regs_range_array,
- regs_range_array_size, 0, pb_blocks, glbl_sec,
- blocks_array_size);
- if (rc)
- goto free_glbl_sec;
-
- /* Fill all blocks with the same configuration */
- for (i = 0 ; i < num_dcores ; i++) {
- for (j = 0 ; j < num_instances ; j++) {
- int seq = i * num_instances + j;
-
- if (!(mask & BIT_ULL(seq)))
- continue;
-
- hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
- i * dcore_offset + j * instance_offset,
- blocks_array_size);
- }
- }
-
-free_glbl_sec:
- kfree(glbl_sec);
-
- return rc;
-}
-
-/**
- * hl_init_pb_ranges - set pb in HW according to given configuration unsecurring
- * registers ranges instead of specific registers
- *
- * @hdev: pointer to hl_device structure
- * @num_dcores: number of decores to apply configuration to
- * set to HL_PB_SHARED if need to apply only once
- * @dcore_offset: offset between dcores
- * @num_instances: number of instances to apply configuration to
- * @instance_offset: offset between instances
- * @pb_blocks: blocks array
- * @blocks_array_size: blocks array size
- * @regs_range_array: register range array
- * @regs_range_array_size: register range array size
- *
- */
-int hl_init_pb_ranges(struct hl_device *hdev, u32 num_dcores,
- u32 dcore_offset, u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size,
- const struct range *regs_range_array, u32 regs_range_array_size)
-{
- return hl_init_pb_ranges_with_mask(hdev, num_dcores, dcore_offset,
- num_instances, instance_offset, pb_blocks,
- blocks_array_size, regs_range_array,
- regs_range_array_size, ULLONG_MAX);
-}
-
-/**
- * hl_init_pb_single_dcore - set pb for a single docre in HW
- * according to given configuration
- *
- * @hdev: pointer to hl_device structure
- * @dcore_offset: offset from the dcore0
- * @num_instances: number of instances to apply configuration to
- * @instance_offset: offset between instances
- * @pb_blocks: blocks array
- * @blocks_array_size: blocks array size
- * @regs_array: register array
- * @regs_array_size: register array size
- *
- */
-int hl_init_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,
- u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size,
- const u32 *regs_array, u32 regs_array_size)
-{
- int i, rc = 0;
- struct hl_block_glbl_sec *glbl_sec;
-
- glbl_sec = kcalloc(blocks_array_size,
- sizeof(struct hl_block_glbl_sec),
- GFP_KERNEL);
- if (!glbl_sec)
- return -ENOMEM;
-
- hl_secure_block(hdev, glbl_sec, blocks_array_size);
- rc = hl_unsecure_registers(hdev, regs_array, regs_array_size, 0,
- pb_blocks, glbl_sec, blocks_array_size);
- if (rc)
- goto free_glbl_sec;
-
- /* Fill all blocks with the same configuration */
- for (i = 0 ; i < num_instances ; i++)
- hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
- dcore_offset + i * instance_offset,
- blocks_array_size);
-
-free_glbl_sec:
- kfree(glbl_sec);
-
- return rc;
-}
-
-/**
- * hl_init_pb_ranges_single_dcore - set pb for a single docre in HW according
- * to given configuration unsecurring
- * registers ranges instead of specific
- * registers
- *
- * @hdev: pointer to hl_device structure
- * @dcore_offset: offset from the dcore0
- * @num_instances: number of instances to apply configuration to
- * @instance_offset: offset between instances
- * @pb_blocks: blocks array
- * @blocks_array_size: blocks array size
- * @regs_range_array: register range array
- * @regs_range_array_size: register range array size
- *
- */
-int hl_init_pb_ranges_single_dcore(struct hl_device *hdev, u32 dcore_offset,
- u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size,
- const struct range *regs_range_array, u32 regs_range_array_size)
-{
- int i;
- struct hl_block_glbl_sec *glbl_sec;
-
- glbl_sec = kcalloc(blocks_array_size,
- sizeof(struct hl_block_glbl_sec),
- GFP_KERNEL);
- if (!glbl_sec)
- return -ENOMEM;
-
- hl_secure_block(hdev, glbl_sec, blocks_array_size);
- hl_unsecure_registers_range(hdev, regs_range_array,
- regs_range_array_size, 0, pb_blocks, glbl_sec,
- blocks_array_size);
-
- /* Fill all blocks with the same configuration */
- for (i = 0 ; i < num_instances ; i++)
- hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
- dcore_offset + i * instance_offset,
- blocks_array_size);
-
- kfree(glbl_sec);
-
- return 0;
-}
-
-/**
- * hl_ack_pb_with_mask - ack pb with mask in HW according to given configuration
- *
- * @hdev: pointer to hl_device structure
- * @num_dcores: number of decores to apply configuration to
- * set to HL_PB_SHARED if need to apply only once
- * @dcore_offset: offset between dcores
- * @num_instances: number of instances to apply configuration to
- * @instance_offset: offset between instances
- * @pb_blocks: blocks array
- * @blocks_array_size: blocks array size
- * @mask: enabled instances mask: 1- enabled, 0- disabled
- *
- */
-void hl_ack_pb_with_mask(struct hl_device *hdev, u32 num_dcores,
- u32 dcore_offset, u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size, u64 mask)
-{
- int i, j;
-
- /* ack all blocks */
- for (i = 0 ; i < num_dcores ; i++) {
- for (j = 0 ; j < num_instances ; j++) {
- int seq = i * num_instances + j;
-
- if (!(mask & BIT_ULL(seq)))
- continue;
-
- hl_ack_pb_security_violations(hdev, pb_blocks,
- i * dcore_offset + j * instance_offset,
- blocks_array_size);
- }
- }
-}
-
-/**
- * hl_ack_pb - ack pb in HW according to given configuration
- *
- * @hdev: pointer to hl_device structure
- * @num_dcores: number of decores to apply configuration to
- * set to HL_PB_SHARED if need to apply only once
- * @dcore_offset: offset between dcores
- * @num_instances: number of instances to apply configuration to
- * @instance_offset: offset between instances
- * @pb_blocks: blocks array
- * @blocks_array_size: blocks array size
- *
- */
-void hl_ack_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset,
- u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size)
-{
- hl_ack_pb_with_mask(hdev, num_dcores, dcore_offset, num_instances,
- instance_offset, pb_blocks, blocks_array_size,
- ULLONG_MAX);
-}
-
-/**
- * hl_ack_pb_single_dcore - ack pb for single docre in HW
- * according to given configuration
- *
- * @hdev: pointer to hl_device structure
- * @dcore_offset: offset from dcore0
- * @num_instances: number of instances to apply configuration to
- * @instance_offset: offset between instances
- * @pb_blocks: blocks array
- * @blocks_array_size: blocks array size
- *
- */
-void hl_ack_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,
- u32 num_instances, u32 instance_offset,
- const u32 pb_blocks[], u32 blocks_array_size)
-{
- int i;
-
- /* ack all blocks */
- for (i = 0 ; i < num_instances ; i++)
- hl_ack_pb_security_violations(hdev, pb_blocks,
- dcore_offset + i * instance_offset,
- blocks_array_size);
-
-}
diff --git a/drivers/misc/habanalabs/common/state_dump.c b/drivers/misc/habanalabs/common/state_dump.c
deleted file mode 100644
index 3a9931f24259..000000000000
--- a/drivers/misc/habanalabs/common/state_dump.c
+++ /dev/null
@@ -1,718 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2021 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include <linux/vmalloc.h>
-#include <uapi/drm/habanalabs_accel.h>
-#include "habanalabs.h"
-
-/**
- * hl_format_as_binary - helper function, format an integer as binary
- * using supplied scratch buffer
- * @buf: the buffer to use
- * @buf_len: buffer capacity
- * @n: number to format
- *
- * Returns pointer to buffer
- */
-char *hl_format_as_binary(char *buf, size_t buf_len, u32 n)
-{
- int i;
- u32 bit;
- bool leading0 = true;
- char *wrptr = buf;
-
- if (buf_len > 0 && buf_len < 3) {
- *wrptr = '\0';
- return buf;
- }
-
- wrptr[0] = '0';
- wrptr[1] = 'b';
- wrptr += 2;
- /* Remove 3 characters from length for '0b' and '\0' termination */
- buf_len -= 3;
-
- for (i = 0; i < sizeof(n) * BITS_PER_BYTE && buf_len; ++i, n <<= 1) {
- /* Writing bit calculation in one line would cause a false
- * positive static code analysis error, so splitting.
- */
- bit = n & (1 << (sizeof(n) * BITS_PER_BYTE - 1));
- bit = !!bit;
- leading0 &= !bit;
- if (!leading0) {
- *wrptr = '0' + bit;
- ++wrptr;
- }
- }
-
- *wrptr = '\0';
-
- return buf;
-}
-
-/**
- * resize_to_fit - helper function, resize buffer to fit given amount of data
- * @buf: destination buffer double pointer
- * @size: pointer to the size container
- * @desired_size: size the buffer must contain
- *
- * Returns 0 on success or error code on failure.
- * On success, the size of buffer is at least desired_size. Buffer is allocated
- * via vmalloc and must be freed with vfree.
- */
-static int resize_to_fit(char **buf, size_t *size, size_t desired_size)
-{
- char *resized_buf;
- size_t new_size;
-
- if (*size >= desired_size)
- return 0;
-
- /* Not enough space to print all, have to resize */
- new_size = max_t(size_t, PAGE_SIZE, round_up(desired_size, PAGE_SIZE));
- resized_buf = vmalloc(new_size);
- if (!resized_buf)
- return -ENOMEM;
- memcpy(resized_buf, *buf, *size);
- vfree(*buf);
- *buf = resized_buf;
- *size = new_size;
-
- return 1;
-}
-
-/**
- * hl_snprintf_resize() - print formatted data to buffer, resize as needed
- * @buf: buffer double pointer, to be written to and resized, must be either
- * NULL or allocated with vmalloc.
- * @size: current size of the buffer
- * @offset: current offset to write to
- * @format: format of the data
- *
- * This function will write formatted data into the buffer. If buffer is not
- * large enough, it will be resized using vmalloc. Size may be modified if the
- * buffer was resized, offset will be advanced by the number of bytes written
- * not including the terminating character
- *
- * Returns 0 on success or error code on failure
- *
- * Note that the buffer has to be manually released using vfree.
- */
-int hl_snprintf_resize(char **buf, size_t *size, size_t *offset,
- const char *format, ...)
-{
- va_list args;
- size_t length;
- int rc;
-
- if (*buf == NULL && (*size != 0 || *offset != 0))
- return -EINVAL;
-
- va_start(args, format);
- length = vsnprintf(*buf + *offset, *size - *offset, format, args);
- va_end(args);
-
- rc = resize_to_fit(buf, size, *offset + length + 1);
- if (rc < 0)
- return rc;
- else if (rc > 0) {
- /* Resize was needed, write again */
- va_start(args, format);
- length = vsnprintf(*buf + *offset, *size - *offset, format,
- args);
- va_end(args);
- }
-
- *offset += length;
-
- return 0;
-}
-
-/**
- * hl_sync_engine_to_string - convert engine type enum to string literal
- * @engine_type: engine type (TPC/MME/DMA)
- *
- * Return the resolved string literal
- */
-const char *hl_sync_engine_to_string(enum hl_sync_engine_type engine_type)
-{
- switch (engine_type) {
- case ENGINE_DMA:
- return "DMA";
- case ENGINE_MME:
- return "MME";
- case ENGINE_TPC:
- return "TPC";
- }
- return "Invalid Engine Type";
-}
-
-/**
- * hl_print_resize_sync_engine - helper function, format engine name and ID
- * using hl_snprintf_resize
- * @buf: destination buffer double pointer to be used with hl_snprintf_resize
- * @size: pointer to the size container
- * @offset: pointer to the offset container
- * @engine_type: engine type (TPC/MME/DMA)
- * @engine_id: engine numerical id
- *
- * Returns 0 on success or error code on failure
- */
-static int hl_print_resize_sync_engine(char **buf, size_t *size, size_t *offset,
- enum hl_sync_engine_type engine_type,
- u32 engine_id)
-{
- return hl_snprintf_resize(buf, size, offset, "%s%u",
- hl_sync_engine_to_string(engine_type), engine_id);
-}
-
-/**
- * hl_state_dump_get_sync_name - transform sync object id to name if available
- * @hdev: pointer to the device
- * @sync_id: sync object id
- *
- * Returns a name literal or NULL if not resolved.
- * Note: returning NULL shall not be considered as a failure, as not all
- * sync objects are named.
- */
-const char *hl_state_dump_get_sync_name(struct hl_device *hdev, u32 sync_id)
-{
- struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
- struct hl_hw_obj_name_entry *entry;
-
- hash_for_each_possible(sds->so_id_to_str_tb, entry,
- node, sync_id)
- if (sync_id == entry->id)
- return entry->name;
-
- return NULL;
-}
-
-/**
- * hl_state_dump_get_monitor_name - transform monitor object dump to monitor
- * name if available
- * @hdev: pointer to the device
- * @mon: monitor state dump
- *
- * Returns a name literal or NULL if not resolved.
- * Note: returning NULL shall not be considered as a failure, as not all
- * monitors are named.
- */
-const char *hl_state_dump_get_monitor_name(struct hl_device *hdev,
- struct hl_mon_state_dump *mon)
-{
- struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
- struct hl_hw_obj_name_entry *entry;
-
- hash_for_each_possible(sds->monitor_id_to_str_tb,
- entry, node, mon->id)
- if (mon->id == entry->id)
- return entry->name;
-
- return NULL;
-}
-
-/**
- * hl_state_dump_free_sync_to_engine_map - free sync object to engine map
- * @map: sync object to engine map
- *
- * Note: generic free implementation, the allocation is implemented per ASIC.
- */
-void hl_state_dump_free_sync_to_engine_map(struct hl_sync_to_engine_map *map)
-{
- struct hl_sync_to_engine_map_entry *entry;
- struct hlist_node *tmp_node;
- int i;
-
- hash_for_each_safe(map->tb, i, tmp_node, entry, node) {
- hash_del(&entry->node);
- kfree(entry);
- }
-}
-
-/**
- * hl_state_dump_get_sync_to_engine - transform sync_id to
- * hl_sync_to_engine_map_entry if available for current id
- * @map: sync object to engine map
- * @sync_id: sync object id
- *
- * Returns the translation entry if found or NULL if not.
- * Note, returned NULL shall not be considered as a failure as the map
- * does not cover all possible, it is a best effort sync ids.
- */
-static struct hl_sync_to_engine_map_entry *
-hl_state_dump_get_sync_to_engine(struct hl_sync_to_engine_map *map, u32 sync_id)
-{
- struct hl_sync_to_engine_map_entry *entry;
-
- hash_for_each_possible(map->tb, entry, node, sync_id)
- if (entry->sync_id == sync_id)
- return entry;
- return NULL;
-}
-
-/**
- * hl_state_dump_read_sync_objects - read sync objects array
- * @hdev: pointer to the device
- * @index: sync manager block index starting with E_N
- *
- * Returns array of size SP_SYNC_OBJ_AMOUNT on success or NULL on failure
- */
-static u32 *hl_state_dump_read_sync_objects(struct hl_device *hdev, u32 index)
-{
- struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
- u32 *sync_objects;
- s64 base_addr; /* Base addr can be negative */
- int i;
-
- base_addr = sds->props[SP_SYNC_OBJ_BASE_ADDR] +
- sds->props[SP_NEXT_SYNC_OBJ_ADDR] * index;
-
- sync_objects = vmalloc(sds->props[SP_SYNC_OBJ_AMOUNT] * sizeof(u32));
- if (!sync_objects)
- return NULL;
-
- for (i = 0; i < sds->props[SP_SYNC_OBJ_AMOUNT]; ++i)
- sync_objects[i] = RREG32(base_addr + i * sizeof(u32));
-
- return sync_objects;
-}
-
-/**
- * hl_state_dump_free_sync_objects - free sync objects array allocated by
- * hl_state_dump_read_sync_objects
- * @sync_objects: sync objects array
- */
-static void hl_state_dump_free_sync_objects(u32 *sync_objects)
-{
- vfree(sync_objects);
-}
-
-
-/**
- * hl_state_dump_print_syncs_single_block - print active sync objects on a
- * single block
- * @hdev: pointer to the device
- * @index: sync manager block index starting with E_N
- * @buf: destination buffer double pointer to be used with hl_snprintf_resize
- * @size: pointer to the size container
- * @offset: pointer to the offset container
- * @map: sync engines names map
- *
- * Returns 0 on success or error code on failure
- */
-static int
-hl_state_dump_print_syncs_single_block(struct hl_device *hdev, u32 index,
- char **buf, size_t *size, size_t *offset,
- struct hl_sync_to_engine_map *map)
-{
- struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
- const char *sync_name;
- u32 *sync_objects = NULL;
- int rc = 0, i;
-
- if (sds->sync_namager_names) {
- rc = hl_snprintf_resize(
- buf, size, offset, "%s\n",
- sds->sync_namager_names[index]);
- if (rc)
- goto out;
- }
-
- sync_objects = hl_state_dump_read_sync_objects(hdev, index);
- if (!sync_objects) {
- rc = -ENOMEM;
- goto out;
- }
-
- for (i = 0; i < sds->props[SP_SYNC_OBJ_AMOUNT]; ++i) {
- struct hl_sync_to_engine_map_entry *entry;
- u64 sync_object_addr;
-
- if (!sync_objects[i])
- continue;
-
- sync_object_addr = sds->props[SP_SYNC_OBJ_BASE_ADDR] +
- sds->props[SP_NEXT_SYNC_OBJ_ADDR] * index +
- i * sizeof(u32);
-
- rc = hl_snprintf_resize(buf, size, offset, "sync id: %u", i);
- if (rc)
- goto free_sync_objects;
- sync_name = hl_state_dump_get_sync_name(hdev, i);
- if (sync_name) {
- rc = hl_snprintf_resize(buf, size, offset, " %s",
- sync_name);
- if (rc)
- goto free_sync_objects;
- }
- rc = hl_snprintf_resize(buf, size, offset, ", value: %u",
- sync_objects[i]);
- if (rc)
- goto free_sync_objects;
-
- /* Append engine string */
- entry = hl_state_dump_get_sync_to_engine(map,
- (u32)sync_object_addr);
- if (entry) {
- rc = hl_snprintf_resize(buf, size, offset,
- ", Engine: ");
- if (rc)
- goto free_sync_objects;
- rc = hl_print_resize_sync_engine(buf, size, offset,
- entry->engine_type,
- entry->engine_id);
- if (rc)
- goto free_sync_objects;
- }
-
- rc = hl_snprintf_resize(buf, size, offset, "\n");
- if (rc)
- goto free_sync_objects;
- }
-
-free_sync_objects:
- hl_state_dump_free_sync_objects(sync_objects);
-out:
- return rc;
-}
-
-/**
- * hl_state_dump_print_syncs - print active sync objects
- * @hdev: pointer to the device
- * @buf: destination buffer double pointer to be used with hl_snprintf_resize
- * @size: pointer to the size container
- * @offset: pointer to the offset container
- *
- * Returns 0 on success or error code on failure
- */
-static int hl_state_dump_print_syncs(struct hl_device *hdev,
- char **buf, size_t *size,
- size_t *offset)
-
-{
- struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
- struct hl_sync_to_engine_map *map;
- u32 index;
- int rc = 0;
-
- map = kzalloc(sizeof(*map), GFP_KERNEL);
- if (!map)
- return -ENOMEM;
-
- rc = sds->funcs.gen_sync_to_engine_map(hdev, map);
- if (rc)
- goto free_map_mem;
-
- rc = hl_snprintf_resize(buf, size, offset, "Non zero sync objects:\n");
- if (rc)
- goto out;
-
- if (sds->sync_namager_names) {
- for (index = 0; sds->sync_namager_names[index]; ++index) {
- rc = hl_state_dump_print_syncs_single_block(
- hdev, index, buf, size, offset, map);
- if (rc)
- goto out;
- }
- } else {
- for (index = 0; index < sds->props[SP_NUM_CORES]; ++index) {
- rc = hl_state_dump_print_syncs_single_block(
- hdev, index, buf, size, offset, map);
- if (rc)
- goto out;
- }
- }
-
-out:
- hl_state_dump_free_sync_to_engine_map(map);
-free_map_mem:
- kfree(map);
-
- return rc;
-}
-
-/**
- * hl_state_dump_alloc_read_sm_block_monitors - read monitors for a specific
- * block
- * @hdev: pointer to the device
- * @index: sync manager block index starting with E_N
- *
- * Returns an array of monitor data of size SP_MONITORS_AMOUNT or NULL
- * on error
- */
-static struct hl_mon_state_dump *
-hl_state_dump_alloc_read_sm_block_monitors(struct hl_device *hdev, u32 index)
-{
- struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
- struct hl_mon_state_dump *monitors;
- s64 base_addr; /* Base addr can be negative */
- int i;
-
- monitors = vmalloc(sds->props[SP_MONITORS_AMOUNT] *
- sizeof(struct hl_mon_state_dump));
- if (!monitors)
- return NULL;
-
- base_addr = sds->props[SP_NEXT_SYNC_OBJ_ADDR] * index;
-
- for (i = 0; i < sds->props[SP_MONITORS_AMOUNT]; ++i) {
- monitors[i].id = i;
- monitors[i].wr_addr_low =
- RREG32(base_addr + sds->props[SP_MON_OBJ_WR_ADDR_LOW] +
- i * sizeof(u32));
-
- monitors[i].wr_addr_high =
- RREG32(base_addr + sds->props[SP_MON_OBJ_WR_ADDR_HIGH] +
- i * sizeof(u32));
-
- monitors[i].wr_data =
- RREG32(base_addr + sds->props[SP_MON_OBJ_WR_DATA] +
- i * sizeof(u32));
-
- monitors[i].arm_data =
- RREG32(base_addr + sds->props[SP_MON_OBJ_ARM_DATA] +
- i * sizeof(u32));
-
- monitors[i].status =
- RREG32(base_addr + sds->props[SP_MON_OBJ_STATUS] +
- i * sizeof(u32));
- }
-
- return monitors;
-}
-
-/**
- * hl_state_dump_free_monitors - free the monitors structure
- * @monitors: monitors array created with
- * hl_state_dump_alloc_read_sm_block_monitors
- */
-static void hl_state_dump_free_monitors(struct hl_mon_state_dump *monitors)
-{
- vfree(monitors);
-}
-
-/**
- * hl_state_dump_print_monitors_single_block - print active monitors on a
- * single block
- * @hdev: pointer to the device
- * @index: sync manager block index starting with E_N
- * @buf: destination buffer double pointer to be used with hl_snprintf_resize
- * @size: pointer to the size container
- * @offset: pointer to the offset container
- *
- * Returns 0 on success or error code on failure
- */
-static int hl_state_dump_print_monitors_single_block(struct hl_device *hdev,
- u32 index,
- char **buf, size_t *size,
- size_t *offset)
-{
- struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
- struct hl_mon_state_dump *monitors = NULL;
- int rc = 0, i;
-
- if (sds->sync_namager_names) {
- rc = hl_snprintf_resize(
- buf, size, offset, "%s\n",
- sds->sync_namager_names[index]);
- if (rc)
- goto out;
- }
-
- monitors = hl_state_dump_alloc_read_sm_block_monitors(hdev, index);
- if (!monitors) {
- rc = -ENOMEM;
- goto out;
- }
-
- for (i = 0; i < sds->props[SP_MONITORS_AMOUNT]; ++i) {
- if (!(sds->funcs.monitor_valid(&monitors[i])))
- continue;
-
- /* Monitor is valid, dump it */
- rc = sds->funcs.print_single_monitor(buf, size, offset, hdev,
- &monitors[i]);
- if (rc)
- goto free_monitors;
-
- hl_snprintf_resize(buf, size, offset, "\n");
- }
-
-free_monitors:
- hl_state_dump_free_monitors(monitors);
-out:
- return rc;
-}
-
-/**
- * hl_state_dump_print_monitors - print active monitors
- * @hdev: pointer to the device
- * @buf: destination buffer double pointer to be used with hl_snprintf_resize
- * @size: pointer to the size container
- * @offset: pointer to the offset container
- *
- * Returns 0 on success or error code on failure
- */
-static int hl_state_dump_print_monitors(struct hl_device *hdev,
- char **buf, size_t *size,
- size_t *offset)
-{
- struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
- u32 index;
- int rc = 0;
-
- rc = hl_snprintf_resize(buf, size, offset,
- "Valid (armed) monitor objects:\n");
- if (rc)
- goto out;
-
- if (sds->sync_namager_names) {
- for (index = 0; sds->sync_namager_names[index]; ++index) {
- rc = hl_state_dump_print_monitors_single_block(
- hdev, index, buf, size, offset);
- if (rc)
- goto out;
- }
- } else {
- for (index = 0; index < sds->props[SP_NUM_CORES]; ++index) {
- rc = hl_state_dump_print_monitors_single_block(
- hdev, index, buf, size, offset);
- if (rc)
- goto out;
- }
- }
-
-out:
- return rc;
-}
-
-/**
- * hl_state_dump_print_engine_fences - print active fences for a specific
- * engine
- * @hdev: pointer to the device
- * @engine_type: engine type to use
- * @buf: destination buffer double pointer to be used with hl_snprintf_resize
- * @size: pointer to the size container
- * @offset: pointer to the offset container
- */
-static int
-hl_state_dump_print_engine_fences(struct hl_device *hdev,
- enum hl_sync_engine_type engine_type,
- char **buf, size_t *size, size_t *offset)
-{
- struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
- int rc = 0, i, n_fences;
- u64 base_addr, next_fence;
-
- switch (engine_type) {
- case ENGINE_TPC:
- n_fences = sds->props[SP_NUM_OF_TPC_ENGINES];
- base_addr = sds->props[SP_TPC0_CMDQ];
- next_fence = sds->props[SP_NEXT_TPC];
- break;
- case ENGINE_MME:
- n_fences = sds->props[SP_NUM_OF_MME_ENGINES];
- base_addr = sds->props[SP_MME_CMDQ];
- next_fence = sds->props[SP_NEXT_MME];
- break;
- case ENGINE_DMA:
- n_fences = sds->props[SP_NUM_OF_DMA_ENGINES];
- base_addr = sds->props[SP_DMA_CMDQ];
- next_fence = sds->props[SP_DMA_QUEUES_OFFSET];
- break;
- default:
- return -EINVAL;
- }
- for (i = 0; i < n_fences; ++i) {
- rc = sds->funcs.print_fences_single_engine(
- hdev,
- base_addr + next_fence * i +
- sds->props[SP_FENCE0_CNT_OFFSET],
- base_addr + next_fence * i +
- sds->props[SP_CP_STS_OFFSET],
- engine_type, i, buf, size, offset);
- if (rc)
- goto out;
- }
-out:
- return rc;
-}
-
-/**
- * hl_state_dump_print_fences - print active fences
- * @hdev: pointer to the device
- * @buf: destination buffer double pointer to be used with hl_snprintf_resize
- * @size: pointer to the size container
- * @offset: pointer to the offset container
- */
-static int hl_state_dump_print_fences(struct hl_device *hdev, char **buf,
- size_t *size, size_t *offset)
-{
- int rc = 0;
-
- rc = hl_snprintf_resize(buf, size, offset, "Valid (armed) fences:\n");
- if (rc)
- goto out;
-
- rc = hl_state_dump_print_engine_fences(hdev, ENGINE_TPC, buf, size, offset);
- if (rc)
- goto out;
-
- rc = hl_state_dump_print_engine_fences(hdev, ENGINE_MME, buf, size, offset);
- if (rc)
- goto out;
-
- rc = hl_state_dump_print_engine_fences(hdev, ENGINE_DMA, buf, size, offset);
- if (rc)
- goto out;
-
-out:
- return rc;
-}
-
-/**
- * hl_state_dump() - dump system state
- * @hdev: pointer to device structure
- */
-int hl_state_dump(struct hl_device *hdev)
-{
- char *buf = NULL;
- size_t offset = 0, size = 0;
- int rc;
-
- rc = hl_snprintf_resize(&buf, &size, &offset,
- "Timestamp taken on: %llu\n\n",
- ktime_to_ns(ktime_get()));
- if (rc)
- goto err;
-
- rc = hl_state_dump_print_syncs(hdev, &buf, &size, &offset);
- if (rc)
- goto err;
-
- hl_snprintf_resize(&buf, &size, &offset, "\n");
-
- rc = hl_state_dump_print_monitors(hdev, &buf, &size, &offset);
- if (rc)
- goto err;
-
- hl_snprintf_resize(&buf, &size, &offset, "\n");
-
- rc = hl_state_dump_print_fences(hdev, &buf, &size, &offset);
- if (rc)
- goto err;
-
- hl_snprintf_resize(&buf, &size, &offset, "\n");
-
- hl_debugfs_set_state_dump(hdev, buf, size);
-
- return 0;
-err:
- vfree(buf);
- return rc;
-}
diff --git a/drivers/misc/habanalabs/common/sysfs.c b/drivers/misc/habanalabs/common/sysfs.c
deleted file mode 100644
index 735d8bed0066..000000000000
--- a/drivers/misc/habanalabs/common/sysfs.c
+++ /dev/null
@@ -1,514 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * Copyright 2016-2022 HabanaLabs, Ltd.
- * All Rights Reserved.
- */
-
-#include "habanalabs.h"
-
-#include <linux/pci.h>
-
-static ssize_t clk_max_freq_mhz_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
- long value;
-
- if (!hl_device_operational(hdev, NULL))
- return -ENODEV;
-
- value = hl_fw_get_frequency(hdev, hdev->asic_prop.clk_pll_index, false);
- if (value < 0)
- return value;
-
- hdev->asic_prop.max_freq_value = value;
-
- return sprintf(buf, "%lu\n", (value / 1000 / 1000));
-}
-
-static ssize_t clk_max_freq_mhz_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
- int rc;
- u64 value;
-
- if (!hl_device_operational(hdev, NULL)) {
- count = -ENODEV;
- goto fail;
- }
-
- rc = kstrtoull(buf, 0, &value);
- if (rc) {
- count = -EINVAL;
- goto fail;
- }
-
- hdev->asic_prop.max_freq_value = value * 1000 * 1000;
-
- hl_fw_set_frequency(hdev, hdev->asic_prop.clk_pll_index, hdev->asic_prop.max_freq_value);
-
-fail:
- return count;
-}
-
-static ssize_t clk_cur_freq_mhz_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
- long value;
-
- if (!hl_device_operational(hdev, NULL))
- return -ENODEV;
-
- value = hl_fw_get_frequency(hdev, hdev->asic_prop.clk_pll_index, true);
- if (value < 0)
- return value;
-
- return sprintf(buf, "%lu\n", (value / 1000 / 1000));
-}
-
-static DEVICE_ATTR_RW(clk_max_freq_mhz);
-static DEVICE_ATTR_RO(clk_cur_freq_mhz);
-
-static struct attribute *hl_dev_clk_attrs[] = {
- &dev_attr_clk_max_freq_mhz.attr,
- &dev_attr_clk_cur_freq_mhz.attr,
- NULL,
-};
-
-static ssize_t vrm_ver_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
- struct cpucp_info *cpucp_info;
-
- cpucp_info = &hdev->asic_prop.cpucp_info;
-
- if (cpucp_info->infineon_second_stage_version)
- return sprintf(buf, "%#04x %#04x\n", le32_to_cpu(cpucp_info->infineon_version),
- le32_to_cpu(cpucp_info->infineon_second_stage_version));
- else
- return sprintf(buf, "%#04x\n", le32_to_cpu(cpucp_info->infineon_version));
-}
-
-static DEVICE_ATTR_RO(vrm_ver);
-
-static struct attribute *hl_dev_vrm_attrs[] = {
- &dev_attr_vrm_ver.attr,
- NULL,
-};
-
-static ssize_t uboot_ver_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- return sprintf(buf, "%s\n", hdev->asic_prop.uboot_ver);
-}
-
-static ssize_t armcp_kernel_ver_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- return sprintf(buf, "%s", hdev->asic_prop.cpucp_info.kernel_version);
-}
-
-static ssize_t armcp_ver_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- return sprintf(buf, "%s\n", hdev->asic_prop.cpucp_info.cpucp_version);
-}
-
-static ssize_t cpld_ver_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- return sprintf(buf, "0x%08x\n",
- le32_to_cpu(hdev->asic_prop.cpucp_info.cpld_version));
-}
-
-static ssize_t cpucp_kernel_ver_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- return sprintf(buf, "%s", hdev->asic_prop.cpucp_info.kernel_version);
-}
-
-static ssize_t cpucp_ver_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- return sprintf(buf, "%s\n", hdev->asic_prop.cpucp_info.cpucp_version);
-}
-
-static ssize_t fuse_ver_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- return sprintf(buf, "%s\n", hdev->asic_prop.cpucp_info.fuse_version);
-}
-
-static ssize_t thermal_ver_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- return sprintf(buf, "%s", hdev->asic_prop.cpucp_info.thermal_version);
-}
-
-static ssize_t fw_os_ver_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- return sprintf(buf, "%s", hdev->asic_prop.cpucp_info.fw_os_version);
-}
-
-static ssize_t preboot_btl_ver_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- return sprintf(buf, "%s\n", hdev->asic_prop.preboot_ver);
-}
-
-static ssize_t soft_reset_store(struct device *dev,
- struct device_attribute *attr, const char *buf,
- size_t count)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
- long value;
- int rc;
-
- rc = kstrtoul(buf, 0, &value);
-
- if (rc) {
- count = -EINVAL;
- goto out;
- }
-
- if (!hdev->asic_prop.allow_inference_soft_reset) {
- dev_err(hdev->dev, "Device does not support inference soft-reset\n");
- goto out;
- }
-
- dev_warn(hdev->dev, "Inference Soft-Reset requested through sysfs\n");
-
- hl_device_reset(hdev, 0);
-
-out:
- return count;
-}
-
-static ssize_t hard_reset_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
- long value;
- int rc;
-
- rc = kstrtoul(buf, 0, &value);
-
- if (rc) {
- count = -EINVAL;
- goto out;
- }
-
- dev_warn(hdev->dev, "Hard-Reset requested through sysfs\n");
-
- hl_device_reset(hdev, HL_DRV_RESET_HARD);
-
-out:
- return count;
-}
-
-static ssize_t device_type_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
- char *str;
-
- switch (hdev->asic_type) {
- case ASIC_GOYA:
- str = "GOYA";
- break;
- case ASIC_GAUDI:
- str = "GAUDI";
- break;
- case ASIC_GAUDI_SEC:
- str = "GAUDI SEC";
- break;
- case ASIC_GAUDI2:
- str = "GAUDI2";
- break;
- case ASIC_GAUDI2B:
- str = "GAUDI2B";
- break;
- default:
- dev_err(hdev->dev, "Unrecognized ASIC type %d\n",
- hdev->asic_type);
- return -EINVAL;
- }
-
- return sprintf(buf, "%s\n", str);
-}
-
-static ssize_t pci_addr_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- return sprintf(buf, "%04x:%02x:%02x.%x\n",
- pci_domain_nr(hdev->pdev->bus),
- hdev->pdev->bus->number,
- PCI_SLOT(hdev->pdev->devfn),
- PCI_FUNC(hdev->pdev->devfn));
-}
-
-static ssize_t status_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
- char str[HL_STR_MAX];
-
- strscpy(str, hdev->status[hl_device_status(hdev)], HL_STR_MAX);
-
- /* use uppercase for backward compatibility */
- str[0] = 'A' + (str[0] - 'a');
-
- return sprintf(buf, "%s\n", str);
-}
-
-static ssize_t soft_reset_cnt_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- return sprintf(buf, "%d\n", hdev->reset_info.compute_reset_cnt);
-}
-
-static ssize_t hard_reset_cnt_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- return sprintf(buf, "%d\n", hdev->reset_info.hard_reset_cnt);
-}
-
-static ssize_t max_power_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
- long val;
-
- if (!hl_device_operational(hdev, NULL))
- return -ENODEV;
-
- val = hl_fw_get_max_power(hdev);
- if (val < 0)
- return val;
-
- return sprintf(buf, "%lu\n", val);
-}
-
-static ssize_t max_power_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
- unsigned long value;
- int rc;
-
- if (!hl_device_operational(hdev, NULL)) {
- count = -ENODEV;
- goto out;
- }
-
- rc = kstrtoul(buf, 0, &value);
-
- if (rc) {
- count = -EINVAL;
- goto out;
- }
-
- hdev->max_power = value;
- hl_fw_set_max_power(hdev);
-
-out:
- return count;
-}
-
-static ssize_t eeprom_read_handler(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr, char *buf, loff_t offset,
- size_t max_size)
-{
- struct device *dev = kobj_to_dev(kobj);
- struct hl_device *hdev = dev_get_drvdata(dev);
- char *data;
- int rc;
-
- if (!hl_device_operational(hdev, NULL))
- return -ENODEV;
-
- if (!max_size)
- return -EINVAL;
-
- data = kzalloc(max_size, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- rc = hdev->asic_funcs->get_eeprom_data(hdev, data, max_size);
- if (rc)
- goto out;
-
- memcpy(buf, data, max_size);
-
-out:
- kfree(data);
-
- return max_size;
-}
-
-static ssize_t security_enabled_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct hl_device *hdev = dev_get_drvdata(dev);
-
- return sprintf(buf, "%d\n", hdev->asic_prop.fw_security_enabled);
-}
-
-static DEVICE_ATTR_RO(armcp_kernel_ver);
-static DEVICE_ATTR_RO(armcp_ver);
-static DEVICE_ATTR_RO(cpld_ver);
-static DEVICE_ATTR_RO(cpucp_kernel_ver);
-static DEVICE_ATTR_RO(cpucp_ver);
-static DEVICE_ATTR_RO(device_type);
-static DEVICE_ATTR_RO(fuse_ver);
-static DEVICE_ATTR_WO(hard_reset);
-static DEVICE_ATTR_RO(hard_reset_cnt);
-static DEVICE_ATTR_RW(max_power);
-static DEVICE_ATTR_RO(pci_addr);
-static DEVICE_ATTR_RO(preboot_btl_ver);
-static DEVICE_ATTR_WO(soft_reset);
-static DEVICE_ATTR_RO(soft_reset_cnt);
-static DEVICE_ATTR_RO(status);
-static DEVICE_ATTR_RO(thermal_ver);
-static DEVICE_ATTR_RO(uboot_ver);
-static DEVICE_ATTR_RO(fw_os_ver);
-static DEVICE_ATTR_RO(security_enabled);
-
-static struct bin_attribute bin_attr_eeprom = {
- .attr = {.name = "eeprom", .mode = (0444)},
- .size = PAGE_SIZE,
- .read = eeprom_read_handler
-};
-
-static struct attribute *hl_dev_attrs[] = {
- &dev_attr_armcp_kernel_ver.attr,
- &dev_attr_armcp_ver.attr,
- &dev_attr_cpld_ver.attr,
- &dev_attr_cpucp_kernel_ver.attr,
- &dev_attr_cpucp_ver.attr,
- &dev_attr_device_type.attr,
- &dev_attr_fuse_ver.attr,
- &dev_attr_hard_reset.attr,
- &dev_attr_hard_reset_cnt.attr,
- &dev_attr_max_power.attr,
- &dev_attr_pci_addr.attr,
- &dev_attr_preboot_btl_ver.attr,
- &dev_attr_status.attr,
- &dev_attr_thermal_ver.attr,
- &dev_attr_uboot_ver.attr,
- &dev_attr_fw_os_ver.attr,
- &dev_attr_security_enabled.attr,
- NULL,
-};
-
-static struct bin_attribute *hl_dev_bin_attrs[] = {
- &bin_attr_eeprom,
- NULL
-};
-
-static struct attribute_group hl_dev_attr_group = {
- .attrs = hl_dev_attrs,
- .bin_attrs = hl_dev_bin_attrs,
-};
-
-static struct attribute_group hl_dev_clks_attr_group;
-static struct attribute_group hl_dev_vrm_attr_group;
-
-static const struct attribute_group *hl_dev_attr_groups[] = {
- &hl_dev_attr_group,
- &hl_dev_clks_attr_group,
- &hl_dev_vrm_attr_group,
- NULL,
-};
-
-static struct attribute *hl_dev_inference_attrs[] = {
- &dev_attr_soft_reset.attr,
- &dev_attr_soft_reset_cnt.attr,
- NULL,
-};
-
-static struct attribute_group hl_dev_inference_attr_group = {
- .attrs = hl_dev_inference_attrs,
-};
-
-static const struct attribute_group *hl_dev_inference_attr_groups[] = {
- &hl_dev_inference_attr_group,
- NULL,
-};
-
-void hl_sysfs_add_dev_clk_attr(struct hl_device *hdev, struct attribute_group *dev_clk_attr_grp)
-{
- dev_clk_attr_grp->attrs = hl_dev_clk_attrs;
-}
-
-void hl_sysfs_add_dev_vrm_attr(struct hl_device *hdev, struct attribute_group *dev_vrm_attr_grp)
-{
- dev_vrm_attr_grp->attrs = hl_dev_vrm_attrs;
-}
-
-int hl_sysfs_init(struct hl_device *hdev)
-{
- int rc;
-
- hdev->max_power = hdev->asic_prop.max_power_default;
-
- hdev->asic_funcs->add_device_attr(hdev, &hl_dev_clks_attr_group, &hl_dev_vrm_attr_group);
-
- rc = device_add_groups(hdev->dev, hl_dev_attr_groups);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to add groups to device, error %d\n", rc);
- return rc;
- }
-
- if (!hdev->asic_prop.allow_inference_soft_reset)
- return 0;
-
- rc = device_add_groups(hdev->dev, hl_dev_inference_attr_groups);
- if (rc) {
- dev_err(hdev->dev,
- "Failed to add groups to device, error %d\n", rc);
- return rc;
- }
-
- return 0;
-}
-
-void hl_sysfs_fini(struct hl_device *hdev)
-{
- device_remove_groups(hdev->dev, hl_dev_attr_groups);
-
- if (!hdev->asic_prop.allow_inference_soft_reset)
- return;
-
- device_remove_groups(hdev->dev, hl_dev_inference_attr_groups);
-}