Re: [PATCH 07/15] habanalabs: add h/w queues module
From: Mike Rapoport
Date: Fri Jan 25 2019 - 02:51:41 EST
On Wed, Jan 23, 2019 at 02:00:49AM +0200, Oded Gabbay wrote:
> This patch adds the H/W queues module and the code to initialize Goya's
> various compute and DMA engines and their queues.
>
> Goya has 5 DMA channels, 8 TPC engines and a single MME engine. For each
> channel/engine, there is a H/W queue logic which is used to pass commands
> from the user to the H/W. That logic is called QMAN.
>
> There are two types of QMANs: external and internal. The DMA QMANs are
> considered external while the TPC and MME QMANs are considered internal.
> For each external queue there is a completion queue, which is located on
> the Host memory.
>
> The differences between external and internal QMANs are:
>
> 1. The location of the queue's memory. External QMANs are located on the
> Host memory while internal QMANs are located on the on-chip memory.
>
> 2. The external QMAN write an entry to a completion queue and sends an
> MSI-X interrupt upon completion of a command buffer that was given to
> it. The internal QMAN doesn't do that.
>
> Signed-off-by: Oded Gabbay <oded.gabbay@xxxxxxxxx>
> ---
> drivers/misc/habanalabs/Makefile | 2 +-
> drivers/misc/habanalabs/device.c | 74 +-
> drivers/misc/habanalabs/goya/goya.c | 1518 +++++++++++++++--
> drivers/misc/habanalabs/goya/goyaP.h | 6 +
> drivers/misc/habanalabs/habanalabs.h | 176 +-
> drivers/misc/habanalabs/habanalabs_drv.c | 6 +
> drivers/misc/habanalabs/hw_queue.c | 404 +++++
> .../habanalabs/include/goya/goya_packets.h | 234 +++
> .../habanalabs/include/habanalabs_device_if.h | 272 +++
> drivers/misc/habanalabs/irq.c | 150 ++
> 10 files changed, 2721 insertions(+), 121 deletions(-)
> create mode 100644 drivers/misc/habanalabs/hw_queue.c
> create mode 100644 drivers/misc/habanalabs/include/goya/goya_packets.h
> create mode 100644 drivers/misc/habanalabs/irq.c
>
> diff --git a/drivers/misc/habanalabs/Makefile b/drivers/misc/habanalabs/Makefile
> index 2530c9b78ca4..c07f3ccb57dc 100644
> --- a/drivers/misc/habanalabs/Makefile
> +++ b/drivers/misc/habanalabs/Makefile
> @@ -5,7 +5,7 @@
> obj-m := habanalabs.o
>
> habanalabs-y := habanalabs_drv.o device.o context.o asid.o habanalabs_ioctl.o \
> - command_buffer.o
> + command_buffer.o hw_queue.o irq.o
>
> include $(src)/goya/Makefile
> habanalabs-y += $(HL_GOYA_FILES)
> diff --git a/drivers/misc/habanalabs/device.c b/drivers/misc/habanalabs/device.c
> index 9fc7218a973c..98220628a467 100644
> --- a/drivers/misc/habanalabs/device.c
> +++ b/drivers/misc/habanalabs/device.c
> @@ -170,13 +170,22 @@ static int device_early_init(struct hl_device *hdev)
> if (rc)
> goto early_fini;
>
> + hdev->cq_wq = alloc_workqueue("hl-free-jobs", WQ_UNBOUND, 0);
> + if (hdev->cq_wq == NULL) {
> + dev_err(hdev->dev, "Failed to allocate CQ workqueue\n");
> + goto asid_fini;
> + }
> +
> hl_cb_mgr_init(&hdev->kernel_cb_mgr);
>
> mutex_init(&hdev->device_open);
> + mutex_init(&hdev->send_cpu_message_lock);
> atomic_set(&hdev->fd_open_cnt, 0);
>
> return 0;
>
> +asid_fini:
> + hl_asid_fini(hdev);
> early_fini:
> if (hdev->asic_funcs->early_fini)
> hdev->asic_funcs->early_fini(hdev);
> @@ -192,9 +201,12 @@ static int device_early_init(struct hl_device *hdev)
> */
> static void device_early_fini(struct hl_device *hdev)
> {
> + mutex_destroy(&hdev->send_cpu_message_lock);
>
> hl_cb_mgr_fini(hdev, &hdev->kernel_cb_mgr);
>
> + destroy_workqueue(hdev->cq_wq);
> +
> hl_asid_fini(hdev);
>
> if (hdev->asic_funcs->early_fini)
> @@ -273,7 +285,7 @@ int hl_device_resume(struct hl_device *hdev)
> */
> int hl_device_init(struct hl_device *hdev, struct class *hclass)
> {
> - int rc;
> + int i, rc, cq_ready_cnt;
>
> /* Create device */
> rc = device_setup_cdev(hdev, hclass, hdev->id, &hl_ops);
> @@ -294,11 +306,48 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass)
> if (rc)
> goto early_fini;
>
> + /*
> + * 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;
> + }
> +
> + /*
> + * 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
> + */
> + hdev->completion_queue =
> + kcalloc(hdev->asic_prop.completion_queues_count,
> + 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 < hdev->asic_prop.completion_queues_count;
> + i++, cq_ready_cnt++) {
> + rc = hl_cq_init(hdev, &hdev->completion_queue[i], i);
> + if (rc) {
> + dev_err(hdev->dev,
> + "failed to initialize completion queue\n");
> + goto cq_fini;
> + }
> + }
> +
> /* Allocate the kernel context */
> hdev->kernel_ctx = kzalloc(sizeof(*hdev->kernel_ctx), GFP_KERNEL);
> if (!hdev->kernel_ctx) {
> rc = -ENOMEM;
> - goto sw_fini;
> + goto cq_fini;
> }
>
> hdev->user_ctx = NULL;
> @@ -324,6 +373,14 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass)
>
> hdev->disabled = false;
>
> + /* 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;
Why rc is 0 here?
> + goto out_disabled;
> + }
> +
> dev_notice(hdev->dev,
> "Successfully added device to habanalabs driver\n");
>
> @@ -335,6 +392,12 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass)
> "kernel ctx is still alive on initialization failure\n");
> free_ctx:
> kfree(hdev->kernel_ctx);
> +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);
> early_fini:
> @@ -364,6 +427,7 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass)
> */
> void hl_device_fini(struct hl_device *hdev)
> {
> + int i;
> dev_info(hdev->dev, "Removing device\n");
>
> /* Mark device as disabled */
> @@ -378,6 +442,12 @@ void hl_device_fini(struct hl_device *hdev)
> /* Reset the H/W. It will be in idle state after this returns */
> hdev->asic_funcs->hw_fini(hdev, true);
>
> + for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++)
> + hl_cq_fini(hdev, &hdev->completion_queue[i]);
> + kfree(hdev->completion_queue);
> +
> + hl_hw_queues_destroy(hdev);
> +
> /* Call ASIC S/W finalize function */
> hdev->asic_funcs->sw_fini(hdev);
>
> diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c
> index f715e01838b3..08d5227eaf1d 100644
> --- a/drivers/misc/habanalabs/goya/goya.c
> +++ b/drivers/misc/habanalabs/goya/goya.c
> @@ -98,6 +98,26 @@
> static void goya_get_fixed_properties(struct hl_device *hdev)
> {
> struct asic_fixed_properties *prop = &hdev->asic_prop;
> + int i;
> +
> + for (i = 0 ; i < NUMBER_OF_EXT_HW_QUEUES ; i++) {
> + prop->hw_queues_props[i].type = QUEUE_TYPE_EXT;
> + prop->hw_queues_props[i].kmd_only = 0;
> + }
> +
> + for (; i < NUMBER_OF_EXT_HW_QUEUES + NUMBER_OF_CPU_HW_QUEUES ; i++) {
> + prop->hw_queues_props[i].type = QUEUE_TYPE_CPU;
> + prop->hw_queues_props[i].kmd_only = 1;
> + }
> +
> + for (; i < NUMBER_OF_EXT_HW_QUEUES + NUMBER_OF_CPU_HW_QUEUES +
> + NUMBER_OF_INT_HW_QUEUES; i++) {
> + prop->hw_queues_props[i].type = QUEUE_TYPE_INT;
> + prop->hw_queues_props[i].kmd_only = 0;
> + }
> +
> + for (; i < HL_MAX_QUEUES; i++)
> + prop->hw_queues_props[i].type = QUEUE_TYPE_NA;
>
> prop->completion_queues_count = NUMBER_OF_CMPLT_QUEUES;
>
> @@ -126,6 +146,18 @@ static void goya_get_fixed_properties(struct hl_device *hdev)
> prop->high_pll = PLL_HIGH_DEFAULT;
> }
>
> +int goya_send_pci_access_msg(struct hl_device *hdev, u32 opcode)
> +{
> + struct armcp_packet pkt;
> +
> + memset(&pkt, 0, sizeof(pkt));
> +
> + pkt.opcode = opcode;
> +
> + return hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt,
> + sizeof(pkt), HL_DEVICE_TIMEOUT_USEC, NULL);
> +}
> +
> /**
> * goya_pci_bars_map - Map PCI BARS of Goya device
> *
> @@ -509,6 +541,8 @@ static int goya_sw_init(struct hl_device *hdev)
> if (!goya)
> return -ENOMEM;
>
> + goya->test_cpu_queue = goya_test_cpu_queue;
> +
> /* according to goya_init_iatu */
> goya->ddr_bar_cur_addr = DRAM_PHYS_BASE;
> hdev->asic_specific = goya;
> @@ -595,6 +629,299 @@ int goya_sw_fini(struct hl_device *hdev)
> return 0;
> }
>
> +static void goya_init_dma_qman(struct hl_device *hdev, int dma_id,
> + dma_addr_t bus_address)
> +{
> + struct goya_device *goya = hdev->asic_specific;
> + u32 mtr_base_lo, mtr_base_hi;
> + u32 so_base_lo, so_base_hi;
> + u32 gic_base_lo, gic_base_hi;
> + u32 reg_off = dma_id * (mmDMA_QM_1_PQ_PI - mmDMA_QM_0_PQ_PI);
> +
> + mtr_base_lo = lower_32_bits(CFG_BASE + mmSYNC_MNGR_MON_PAY_ADDRL_0);
> + mtr_base_hi = upper_32_bits(CFG_BASE + mmSYNC_MNGR_MON_PAY_ADDRL_0);
> + so_base_lo = lower_32_bits(CFG_BASE + mmSYNC_MNGR_SOB_OBJ_0);
> + so_base_hi = upper_32_bits(CFG_BASE + mmSYNC_MNGR_SOB_OBJ_0);
> +
> + gic_base_lo =
> + lower_32_bits(CFG_BASE + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR);
> + gic_base_hi =
> + upper_32_bits(CFG_BASE + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR);
> +
> + WREG32(mmDMA_QM_0_PQ_BASE_LO + reg_off, lower_32_bits(bus_address));
> + WREG32(mmDMA_QM_0_PQ_BASE_HI + reg_off, upper_32_bits(bus_address));
> +
> + WREG32(mmDMA_QM_0_PQ_SIZE + reg_off, ilog2(HL_QUEUE_LENGTH));
> + WREG32(mmDMA_QM_0_PQ_PI + reg_off, 0);
> + WREG32(mmDMA_QM_0_PQ_CI + reg_off, 0);
> +
> + WREG32(mmDMA_QM_0_CP_MSG_BASE0_ADDR_LO + reg_off, mtr_base_lo);
> + WREG32(mmDMA_QM_0_CP_MSG_BASE0_ADDR_HI + reg_off, mtr_base_hi);
> + WREG32(mmDMA_QM_0_CP_MSG_BASE1_ADDR_LO + reg_off, so_base_lo);
> + WREG32(mmDMA_QM_0_CP_MSG_BASE1_ADDR_HI + reg_off, so_base_hi);
> + WREG32(mmDMA_QM_0_GLBL_ERR_ADDR_LO + reg_off, gic_base_lo);
> + WREG32(mmDMA_QM_0_GLBL_ERR_ADDR_HI + reg_off, gic_base_hi);
> + WREG32(mmDMA_QM_0_GLBL_ERR_WDATA + reg_off,
> + GOYA_ASYNC_EVENT_ID_DMA0_QM + dma_id);
> +
> + /* PQ has buffer of 2 cache lines, while CQ has 8 lines */
> + WREG32(mmDMA_QM_0_PQ_CFG1 + reg_off, 0x00020002);
> + WREG32(mmDMA_QM_0_CQ_CFG1 + reg_off, 0x00080008);
> +
> + if (dma_id == 0)
> + WREG32(mmDMA_QM_0_GLBL_PROT + reg_off, QMAN_DMA_FULLY_TRUSTED);
> + else
> + if (goya->hw_cap_initialized & HW_CAP_MMU)
> + WREG32(mmDMA_QM_0_GLBL_PROT + reg_off,
> + QMAN_DMA_PARTLY_TRUSTED);
> + else
> + WREG32(mmDMA_QM_0_GLBL_PROT + reg_off,
> + QMAN_DMA_FULLY_TRUSTED);
> +
> + WREG32(mmDMA_QM_0_GLBL_ERR_CFG + reg_off, QMAN_DMA_ERR_MSG_EN);
> + WREG32(mmDMA_QM_0_GLBL_CFG0 + reg_off, QMAN_DMA_ENABLE);
> +}
> +
> +static void goya_init_dma_ch(struct hl_device *hdev, int dma_id)
> +{
> + u32 gic_base_lo, gic_base_hi;
> + u64 sob_addr;
> + u32 reg_off = dma_id * (mmDMA_CH_1_CFG1 - mmDMA_CH_0_CFG1);
> +
> + gic_base_lo =
> + lower_32_bits(CFG_BASE + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR);
> + gic_base_hi =
> + upper_32_bits(CFG_BASE + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR);
> +
> + WREG32(mmDMA_CH_0_ERRMSG_ADDR_LO + reg_off, gic_base_lo);
> + WREG32(mmDMA_CH_0_ERRMSG_ADDR_HI + reg_off, gic_base_hi);
> + WREG32(mmDMA_CH_0_ERRMSG_WDATA + reg_off,
> + GOYA_ASYNC_EVENT_ID_DMA0_CH + dma_id);
> +
> + if (dma_id) {
> + sob_addr = CFG_BASE + mmSYNC_MNGR_SOB_OBJ_1000 +
> + (dma_id - 1) * 4;
> + WREG32(mmDMA_CH_0_WR_COMP_ADDR_LO + reg_off,
> + lower_32_bits(sob_addr));
> + WREG32(mmDMA_CH_0_WR_COMP_ADDR_HI + reg_off,
> + upper_32_bits(sob_addr));
> + WREG32(mmDMA_CH_0_WR_COMP_WDATA + reg_off, 0x80000001);
> + }
> +}
> +
> +/**
> + * goya_init_dma_qmans - Initialize QMAN DMA registers
> + *
> + * @hdev: pointer to hl_device structure
> + *
> + * Initialize the H/W registers of the QMAN DMA channels
> + *
> + */
> +static void goya_init_dma_qmans(struct hl_device *hdev)
> +{
> + struct goya_device *goya = hdev->asic_specific;
> + struct hl_hw_queue *q;
> + dma_addr_t bus_address;
> + int i;
> +
> + if (goya->hw_cap_initialized & HW_CAP_DMA)
> + return;
> +
> + q = &hdev->kernel_queues[0];
> +
> + for (i = 0 ; i < NUMBER_OF_EXT_HW_QUEUES ; i++, q++) {
> + bus_address = q->bus_address +
> + hdev->asic_prop.host_phys_base_address;
> +
> + goya_init_dma_qman(hdev, i, bus_address);
> + goya_init_dma_ch(hdev, i);
> + }
> +
> + goya->hw_cap_initialized |= HW_CAP_DMA;
> +}
> +
> +/**
> + * goya_disable_external_queues - Disable external queues
> + *
> + * @hdev: pointer to hl_device structure
> + *
> + */
> +static void goya_disable_external_queues(struct hl_device *hdev)
> +{
> + WREG32(mmDMA_QM_0_GLBL_CFG0, 0);
> + WREG32(mmDMA_QM_1_GLBL_CFG0, 0);
> + WREG32(mmDMA_QM_2_GLBL_CFG0, 0);
> + WREG32(mmDMA_QM_3_GLBL_CFG0, 0);
> + WREG32(mmDMA_QM_4_GLBL_CFG0, 0);
> +}
> +
> +static int goya_stop_queue(struct hl_device *hdev, u32 cfg_reg,
> + u32 cp_sts_reg, u32 glbl_sts0_reg)
> +{
> + int rc;
> + u32 status;
> +
> + /* use the values of TPC0 as they are all the same*/
> +
> + WREG32(cfg_reg, 1 << TPC0_QM_GLBL_CFG1_CP_STOP_SHIFT);
> +
> + status = RREG32(cp_sts_reg);
> + if (status & TPC0_QM_CP_STS_FENCE_IN_PROGRESS_MASK) {
> + rc = hl_poll_timeout(
> + hdev,
> + cp_sts_reg,
> + status,
> + !(status & TPC0_QM_CP_STS_FENCE_IN_PROGRESS_MASK),
> + 1000,
> + QMAN_FENCE_TIMEOUT_USEC);
> +
> + /* if QMAN is stuck in fence no need to check for stop */
> + if (rc)
> + return 0;
Isn't it an error?
> + }
> +
> + rc = hl_poll_timeout(
> + hdev,
> + glbl_sts0_reg,
> + status,
> + (status & TPC0_QM_GLBL_STS0_CP_IS_STOP_MASK),
> + 1000,
> + QMAN_STOP_TIMEOUT_USEC);
> +
> + if (rc) {
> + dev_err(hdev->dev,
> + "Timeout while waiting for QMAN to stop\n");
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * goya_stop_external_queues - Stop external queues
> + *
> + * @hdev: pointer to hl_device structure
> + *
> + * Returns 0 on success
> + *
> + */
> +static int goya_stop_external_queues(struct hl_device *hdev)
> +{
> + int rc = goya_stop_queue(hdev,
> + mmDMA_QM_0_GLBL_CFG1,
> + mmDMA_QM_0_CP_STS,
> + mmDMA_QM_0_GLBL_STS0);
> +
> + if (rc)
> + dev_err(hdev->dev, "failed to stop DMA QMAN 0\n");
> +
> + rc = goya_stop_queue(hdev,
> + mmDMA_QM_1_GLBL_CFG1,
> + mmDMA_QM_1_CP_STS,
> + mmDMA_QM_1_GLBL_STS0);
> +
> + if (rc)
> + dev_err(hdev->dev, "failed to stop DMA QMAN 1\n");
> +
> + rc = goya_stop_queue(hdev,
> + mmDMA_QM_2_GLBL_CFG1,
> + mmDMA_QM_2_CP_STS,
> + mmDMA_QM_2_GLBL_STS0);
> +
> + if (rc)
> + dev_err(hdev->dev, "failed to stop DMA QMAN 2\n");
> +
> + rc = goya_stop_queue(hdev,
> + mmDMA_QM_3_GLBL_CFG1,
> + mmDMA_QM_3_CP_STS,
> + mmDMA_QM_3_GLBL_STS0);
> +
> + if (rc)
> + dev_err(hdev->dev, "failed to stop DMA QMAN 3\n");
> +
> + rc = goya_stop_queue(hdev,
> + mmDMA_QM_4_GLBL_CFG1,
> + mmDMA_QM_4_CP_STS,
> + mmDMA_QM_4_GLBL_STS0);
> +
> + if (rc)
> + dev_err(hdev->dev, "failed to stop DMA QMAN 4\n");
> +
> + return rc;
> +}
> +
> +static void goya_resume_external_queues(struct hl_device *hdev)
> +{
> + WREG32(mmDMA_QM_0_GLBL_CFG1, 0);
> + WREG32(mmDMA_QM_1_GLBL_CFG1, 0);
> + WREG32(mmDMA_QM_2_GLBL_CFG1, 0);
> + WREG32(mmDMA_QM_3_GLBL_CFG1, 0);
> + WREG32(mmDMA_QM_4_GLBL_CFG1, 0);
> +}
> +
> +/**
> + * goya_init_cpu_queues - Initialize PQ/CQ/EQ of CPU
> + *
> + * @hdev: pointer to hl_device structure
> + *
> + * Returns 0 on success
> + *
> + */
> +int goya_init_cpu_queues(struct hl_device *hdev)
> +{
> + struct goya_device *goya = hdev->asic_specific;
> + dma_addr_t bus_address;
> + u32 status;
> + struct hl_hw_queue *cpu_pq = &hdev->kernel_queues[GOYA_QUEUE_ID_CPU_PQ];
> + int err;
> +
> + if (!hdev->cpu_queues_enable)
> + return 0;
> +
> + if (goya->hw_cap_initialized & HW_CAP_CPU_Q)
> + return 0;
> +
> + bus_address = cpu_pq->bus_address +
> + hdev->asic_prop.host_phys_base_address;
> + WREG32(mmPSOC_GLOBAL_CONF_SCRATCHPAD_0, lower_32_bits(bus_address));
> + WREG32(mmPSOC_GLOBAL_CONF_SCRATCHPAD_1, upper_32_bits(bus_address));
> +
> + bus_address = hdev->cpu_accessible_dma_address +
> + hdev->asic_prop.host_phys_base_address;
> + WREG32(mmPSOC_GLOBAL_CONF_SCRATCHPAD_8, lower_32_bits(bus_address));
> + WREG32(mmPSOC_GLOBAL_CONF_SCRATCHPAD_9, upper_32_bits(bus_address));
> +
> + WREG32(mmPSOC_GLOBAL_CONF_SCRATCHPAD_5, HL_QUEUE_SIZE_IN_BYTES);
> + WREG32(mmPSOC_GLOBAL_CONF_SCRATCHPAD_10, CPU_ACCESSIBLE_MEM_SIZE);
> +
> + /* Used for EQ CI */
> + WREG32(mmPSOC_GLOBAL_CONF_SCRATCHPAD_6, 0);
> +
> + WREG32(mmCPU_IF_PF_PQ_PI, 0);
> +
> + WREG32(mmPSOC_GLOBAL_CONF_SCRATCHPAD_7, PQ_INIT_STATUS_READY_FOR_CP);
> +
> + WREG32(mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR,
> + GOYA_ASYNC_EVENT_ID_PI_UPDATE);
> +
> + err = hl_poll_timeout(
> + hdev,
> + mmPSOC_GLOBAL_CONF_SCRATCHPAD_7,
> + status,
> + (status == PQ_INIT_STATUS_READY_FOR_HOST),
> + 1000,
> + GOYA_CPU_TIMEOUT_USEC);
> +
> + if (err) {
> + dev_err(hdev->dev,
> + "Failed to communicate with ARM CPU (ArmCP timeout)\n");
> + return -EIO;
> + }
> +
> + goya->hw_cap_initialized |= HW_CAP_CPU_Q;
> + return 0;
> +}
> +
> /**
> * goya_init_pll - Initialize pll registers
> *
> @@ -1960,152 +2287,646 @@ static void goya_init_golden_registers(struct hl_device *hdev)
> goya->hw_cap_initialized |= HW_CAP_GOLDEN;
> }
>
> -
> -/**
> - * goya_push_uboot_to_device - Push u-boot FW code to device
> - *
> - * @hdev: pointer to hl_device structure
> - *
> - * Copy u-boot fw code from firmware file to SRAM BAR.
> - * Returns 0 on success
> - *
> - */
> -static int goya_push_uboot_to_device(struct hl_device *hdev)
> +static void goya_init_mme_qman(struct hl_device *hdev)
> {
> - char fw_name[200];
> - const u64 *fw_data;
> - void __iomem *dst;
> - size_t fw_size, i;
> - int rc;
> + u32 mtr_base_lo, mtr_base_hi;
> + u32 so_base_lo, so_base_hi;
> + u32 gic_base_lo, gic_base_hi;
> + u64 qman_base_addr;
>
> - snprintf(fw_name, sizeof(fw_name), "habanalabs/goya/goya-u-boot.bin");
> + mtr_base_lo = lower_32_bits(CFG_BASE + mmSYNC_MNGR_MON_PAY_ADDRL_0);
> + mtr_base_hi = upper_32_bits(CFG_BASE + mmSYNC_MNGR_MON_PAY_ADDRL_0);
> + so_base_lo = lower_32_bits(CFG_BASE + mmSYNC_MNGR_SOB_OBJ_0);
> + so_base_hi = upper_32_bits(CFG_BASE + mmSYNC_MNGR_SOB_OBJ_0);
>
> - rc = request_firmware(&hdev->spl_fw, fw_name, hdev->dev);
> + gic_base_lo =
> + lower_32_bits(CFG_BASE + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR);
> + gic_base_hi =
> + upper_32_bits(CFG_BASE + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR);
>
> - if (rc) {
> - dev_err(hdev->dev, "Failed to request u-boot fw image\n");
> - goto out;
> - }
> + qman_base_addr = hdev->asic_prop.sram_base_address +
> + MME_QMAN_BASE_OFFSET;
>
> - fw_size = hdev->spl_fw->size;
> - if ((fw_size % 4) != 0) {
> - dev_err(hdev->dev, "illegal u-boot firmware size %lu\n",
> - fw_size);
> - rc = -EINVAL;
> - goto out;
> - }
> + WREG32(mmMME_QM_PQ_BASE_LO, lower_32_bits(qman_base_addr));
> + WREG32(mmMME_QM_PQ_BASE_HI, upper_32_bits(qman_base_addr));
> + WREG32(mmMME_QM_PQ_SIZE, ilog2(MME_QMAN_LENGTH));
> + WREG32(mmMME_QM_PQ_PI, 0);
> + WREG32(mmMME_QM_PQ_CI, 0);
> + WREG32(mmMME_QM_CP_LDMA_SRC_BASE_LO_OFFSET, 0x10C0);
> + WREG32(mmMME_QM_CP_LDMA_SRC_BASE_HI_OFFSET, 0x10C4);
> + WREG32(mmMME_QM_CP_LDMA_TSIZE_OFFSET, 0x10C8);
> + WREG32(mmMME_QM_CP_LDMA_COMMIT_OFFSET, 0x10CC);
>
> - dev_dbg(hdev->dev, "u-boot firmware size == %lu\n", fw_size);
> + WREG32(mmMME_QM_CP_MSG_BASE0_ADDR_LO, mtr_base_lo);
> + WREG32(mmMME_QM_CP_MSG_BASE0_ADDR_HI, mtr_base_hi);
> + WREG32(mmMME_QM_CP_MSG_BASE1_ADDR_LO, so_base_lo);
> + WREG32(mmMME_QM_CP_MSG_BASE1_ADDR_HI, so_base_hi);
>
> - fw_data = (const u64 *) hdev->spl_fw->data;
> - dst = hdev->pcie_bar[SRAM_CFG_BAR_ID] + UBOOT_FW_OFFSET;
> + /* QMAN CQ has 8 cache lines */
> + WREG32(mmMME_QM_CQ_CFG1, 0x00080008);
>
> - if ((hdev->spl_fw->size % 8) != 0)
> - fw_size -= 8;
> + WREG32(mmMME_QM_GLBL_ERR_ADDR_LO, gic_base_lo);
> + WREG32(mmMME_QM_GLBL_ERR_ADDR_HI, gic_base_hi);
>
> - for (i = 0 ; i < fw_size ; i += 8, fw_data++, dst += 8) {
> - if (!(i & (0x80000 - 1)))
> - dev_dbg(hdev->dev,
> - "u-boot copied so far %lu out of %lu",
> - i, fw_size);
> + WREG32(mmMME_QM_GLBL_ERR_WDATA, GOYA_ASYNC_EVENT_ID_MME_QM);
>
> - writeq(*fw_data, dst);
> - }
> + WREG32(mmMME_QM_GLBL_ERR_CFG, QMAN_MME_ERR_MSG_EN);
>
> - if ((hdev->spl_fw->size % 8) != 0)
> - writel(*(const u32 *) fw_data, dst);
> + WREG32(mmMME_QM_GLBL_PROT, QMAN_MME_ERR_PROT);
>
> -out:
> - release_firmware(hdev->spl_fw);
> - return rc;
> + WREG32(mmMME_QM_GLBL_CFG0, QMAN_MME_ENABLE);
> }
>
> -/**
> - * goya_push_linux_to_device - Push LINUX FW code to device
> - *
> - * @hdev: pointer to hl_device structure
> - *
> - * Copy LINXU fw code from firmware file to DDR BAR.
> - * Returns 0 on success
> - *
> - */
> -static int goya_push_linux_to_device(struct hl_device *hdev)
> +static void goya_init_mme_cmdq(struct hl_device *hdev)
> {
> - char fw_name[200];
> - const u64 *fw_data;
> - void __iomem *dst;
> - size_t fw_size, i;
> - int rc;
> + u32 mtr_base_lo, mtr_base_hi;
> + u32 so_base_lo, so_base_hi;
> + u32 gic_base_lo, gic_base_hi;
> + u64 qman_base_addr;
>
> - snprintf(fw_name, sizeof(fw_name), "habanalabs/goya/goya-fit.itb");
> + mtr_base_lo = lower_32_bits(CFG_BASE + mmSYNC_MNGR_MON_PAY_ADDRL_0);
> + mtr_base_hi = upper_32_bits(CFG_BASE + mmSYNC_MNGR_MON_PAY_ADDRL_0);
> + so_base_lo = lower_32_bits(CFG_BASE + mmSYNC_MNGR_SOB_OBJ_0);
> + so_base_hi = upper_32_bits(CFG_BASE + mmSYNC_MNGR_SOB_OBJ_0);
>
> - rc = request_firmware(&hdev->spl_fw, fw_name, hdev->dev);
> + gic_base_lo =
> + lower_32_bits(CFG_BASE + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR);
> + gic_base_hi =
> + upper_32_bits(CFG_BASE + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR);
>
> - if (rc) {
> - dev_err(hdev->dev, "Failed to request Linux fw image\n");
> - goto out;
> - }
> + qman_base_addr = hdev->asic_prop.sram_base_address +
> + MME_QMAN_BASE_OFFSET;
>
> - fw_size = hdev->spl_fw->size;
> - if ((fw_size % 4) != 0) {
> - dev_err(hdev->dev, "illegal Linux firmware size %lu\n",
> - fw_size);
> - rc = -EINVAL;
> - goto out;
> - }
> + WREG32(mmMME_CMDQ_CP_MSG_BASE0_ADDR_LO, mtr_base_lo);
> + WREG32(mmMME_CMDQ_CP_MSG_BASE0_ADDR_HI, mtr_base_hi);
> + WREG32(mmMME_CMDQ_CP_MSG_BASE1_ADDR_LO, so_base_lo);
> + WREG32(mmMME_CMDQ_CP_MSG_BASE1_ADDR_HI, so_base_hi);
>
> - dev_dbg(hdev->dev, "Linux firmware size == %lu\n", fw_size);
> + /* CMDQ CQ has 20 cache lines */
> + WREG32(mmMME_CMDQ_CQ_CFG1, 0x00140014);
>
> - fw_data = (const u64 *) hdev->spl_fw->data;
> - dst = hdev->pcie_bar[DDR_BAR_ID] + LINUX_FW_OFFSET;
> + WREG32(mmMME_CMDQ_GLBL_ERR_ADDR_LO, gic_base_lo);
> + WREG32(mmMME_CMDQ_GLBL_ERR_ADDR_HI, gic_base_hi);
>
> - if ((hdev->spl_fw->size % 8) != 0)
> - fw_size -= 8;
> + WREG32(mmMME_CMDQ_GLBL_ERR_WDATA, GOYA_ASYNC_EVENT_ID_MME_CMDQ);
>
> - for (i = 0 ; i < fw_size ; i += 8, fw_data++, dst += 8) {
> - if (!(i & (0x80000 - 1))) {
> - dev_dbg(hdev->dev,
> - "Linux copied so far %lu out of %lu",
> - i, fw_size);
> - usleep_range(20, 100);
> - }
> - writeq(*fw_data, dst);
> - }
> + WREG32(mmMME_CMDQ_GLBL_ERR_CFG, CMDQ_MME_ERR_MSG_EN);
>
> - if ((hdev->spl_fw->size % 8) != 0)
> - writel(*(const u32 *) fw_data, dst);
> + WREG32(mmMME_CMDQ_GLBL_PROT, CMDQ_MME_ERR_PROT);
>
> -out:
> - release_firmware(hdev->spl_fw);
> - return rc;
> + WREG32(mmMME_CMDQ_GLBL_CFG0, CMDQ_MME_ENABLE);
> }
>
> -static int goya_pldm_init_cpu(struct hl_device *hdev)
> +static void goya_init_mme_qmans(struct hl_device *hdev)
> {
> - u32 val, unit_rst_val;
> - int rc;
> + struct goya_device *goya = hdev->asic_specific;
> + u32 so_base_lo, so_base_hi;
>
> - /* Must initialize SRAM scrambler before pushing u-boot to SRAM */
> - goya_init_golden_registers(hdev);
> + if (goya->hw_cap_initialized & HW_CAP_MME)
> + return;
>
> - /* Put ARM cores into reset */
> - WREG32(mmCPU_CA53_CFG_ARM_RST_CONTROL, CPU_RESET_ASSERT);
> - val = RREG32(mmCPU_CA53_CFG_ARM_RST_CONTROL);
> + so_base_lo = lower_32_bits(CFG_BASE + mmSYNC_MNGR_SOB_OBJ_0);
> + so_base_hi = upper_32_bits(CFG_BASE + mmSYNC_MNGR_SOB_OBJ_0);
>
> - /* Reset the CA53 MACRO */
> - unit_rst_val = RREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N);
> - WREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N, CA53_RESET);
> - val = RREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N);
> - WREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N, unit_rst_val);
> - val = RREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N);
> + WREG32(mmMME_SM_BASE_ADDRESS_LOW, so_base_lo);
> + WREG32(mmMME_SM_BASE_ADDRESS_HIGH, so_base_hi);
>
> - rc = goya_push_uboot_to_device(hdev);
> - if (rc)
> - return rc;
> + goya_init_mme_qman(hdev);
> + goya_init_mme_cmdq(hdev);
>
> - rc = goya_push_linux_to_device(hdev);
> - if (rc)
> - return rc;
> + goya->hw_cap_initialized |= HW_CAP_MME;
> +}
> +
> +static void goya_init_tpc_qman(struct hl_device *hdev, u32 base_off, int tpc_id)
> +{
> + u32 mtr_base_lo, mtr_base_hi;
> + u32 so_base_lo, so_base_hi;
> + u32 gic_base_lo, gic_base_hi;
> + u64 qman_base_addr;
> + u32 reg_off = tpc_id * (mmTPC1_QM_PQ_PI - mmTPC0_QM_PQ_PI);
> +
> + mtr_base_lo = lower_32_bits(CFG_BASE + mmSYNC_MNGR_MON_PAY_ADDRL_0);
> + mtr_base_hi = upper_32_bits(CFG_BASE + mmSYNC_MNGR_MON_PAY_ADDRL_0);
> + so_base_lo = lower_32_bits(CFG_BASE + mmSYNC_MNGR_SOB_OBJ_0);
> + so_base_hi = upper_32_bits(CFG_BASE + mmSYNC_MNGR_SOB_OBJ_0);
> +
> + gic_base_lo =
> + lower_32_bits(CFG_BASE + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR);
> + gic_base_hi =
> + upper_32_bits(CFG_BASE + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR);
> +
> + qman_base_addr = hdev->asic_prop.sram_base_address + base_off;
> +
> + WREG32(mmTPC0_QM_PQ_BASE_LO + reg_off, lower_32_bits(qman_base_addr));
> + WREG32(mmTPC0_QM_PQ_BASE_HI + reg_off, upper_32_bits(qman_base_addr));
> + WREG32(mmTPC0_QM_PQ_SIZE + reg_off, ilog2(TPC_QMAN_LENGTH));
> + WREG32(mmTPC0_QM_PQ_PI + reg_off, 0);
> + WREG32(mmTPC0_QM_PQ_CI + reg_off, 0);
> + WREG32(mmTPC0_QM_CP_LDMA_SRC_BASE_LO_OFFSET + reg_off, 0x10C0);
> + WREG32(mmTPC0_QM_CP_LDMA_SRC_BASE_HI_OFFSET + reg_off, 0x10C4);
> + WREG32(mmTPC0_QM_CP_LDMA_TSIZE_OFFSET + reg_off, 0x10C8);
> + WREG32(mmTPC0_QM_CP_LDMA_COMMIT_OFFSET + reg_off, 0x10CC);
> +
> + WREG32(mmTPC0_QM_CP_MSG_BASE0_ADDR_LO + reg_off, mtr_base_lo);
> + WREG32(mmTPC0_QM_CP_MSG_BASE0_ADDR_HI + reg_off, mtr_base_hi);
> + WREG32(mmTPC0_QM_CP_MSG_BASE1_ADDR_LO + reg_off, so_base_lo);
> + WREG32(mmTPC0_QM_CP_MSG_BASE1_ADDR_HI + reg_off, so_base_hi);
> +
> + WREG32(mmTPC0_QM_CQ_CFG1 + reg_off, 0x00080008);
> +
> + WREG32(mmTPC0_QM_GLBL_ERR_ADDR_LO + reg_off, gic_base_lo);
> + WREG32(mmTPC0_QM_GLBL_ERR_ADDR_HI + reg_off, gic_base_hi);
> +
> + WREG32(mmTPC0_QM_GLBL_ERR_WDATA + reg_off,
> + GOYA_ASYNC_EVENT_ID_TPC0_QM + tpc_id);
> +
> + WREG32(mmTPC0_QM_GLBL_ERR_CFG + reg_off, QMAN_TPC_ERR_MSG_EN);
> +
> + WREG32(mmTPC0_QM_GLBL_PROT + reg_off, QMAN_TPC_ERR_PROT);
> +
> + WREG32(mmTPC0_QM_GLBL_CFG0 + reg_off, QMAN_TPC_ENABLE);
> +}
> +
> +static void goya_init_tpc_cmdq(struct hl_device *hdev, int tpc_id)
> +{
> + u32 mtr_base_lo, mtr_base_hi;
> + u32 so_base_lo, so_base_hi;
> + u32 gic_base_lo, gic_base_hi;
> + u32 reg_off = tpc_id * (mmTPC1_CMDQ_CQ_CFG1 - mmTPC0_CMDQ_CQ_CFG1);
> +
> + mtr_base_lo = lower_32_bits(CFG_BASE + mmSYNC_MNGR_MON_PAY_ADDRL_0);
> + mtr_base_hi = upper_32_bits(CFG_BASE + mmSYNC_MNGR_MON_PAY_ADDRL_0);
> + so_base_lo = lower_32_bits(CFG_BASE + mmSYNC_MNGR_SOB_OBJ_0);
> + so_base_hi = upper_32_bits(CFG_BASE + mmSYNC_MNGR_SOB_OBJ_0);
> +
> + gic_base_lo =
> + lower_32_bits(CFG_BASE + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR);
> + gic_base_hi =
> + upper_32_bits(CFG_BASE + mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR);
> +
> + WREG32(mmTPC0_CMDQ_CP_MSG_BASE0_ADDR_LO + reg_off, mtr_base_lo);
> + WREG32(mmTPC0_CMDQ_CP_MSG_BASE0_ADDR_HI + reg_off, mtr_base_hi);
> + WREG32(mmTPC0_CMDQ_CP_MSG_BASE1_ADDR_LO + reg_off, so_base_lo);
> + WREG32(mmTPC0_CMDQ_CP_MSG_BASE1_ADDR_HI + reg_off, so_base_hi);
> +
> + WREG32(mmTPC0_CMDQ_CQ_CFG1 + reg_off, 0x00140014);
> +
> + WREG32(mmTPC0_CMDQ_GLBL_ERR_ADDR_LO + reg_off, gic_base_lo);
> + WREG32(mmTPC0_CMDQ_GLBL_ERR_ADDR_HI + reg_off, gic_base_hi);
> +
> + WREG32(mmTPC0_CMDQ_GLBL_ERR_WDATA + reg_off,
> + GOYA_ASYNC_EVENT_ID_TPC0_CMDQ + tpc_id);
> +
> + WREG32(mmTPC0_CMDQ_GLBL_ERR_CFG + reg_off, CMDQ_TPC_ERR_MSG_EN);
> +
> + WREG32(mmTPC0_CMDQ_GLBL_PROT + reg_off, CMDQ_TPC_ERR_PROT);
> +
> + WREG32(mmTPC0_CMDQ_GLBL_CFG0 + reg_off, CMDQ_TPC_ENABLE);
> +}
> +
> +static void goya_init_tpc_qmans(struct hl_device *hdev)
> +{
> + struct goya_device *goya = hdev->asic_specific;
> + u32 so_base_lo, so_base_hi;
> + u32 cfg_off = mmTPC1_CFG_SM_BASE_ADDRESS_LOW -
> + mmTPC0_CFG_SM_BASE_ADDRESS_LOW;
> + int i;
> +
> + if (goya->hw_cap_initialized & HW_CAP_TPC)
> + return;
> +
> + so_base_lo = lower_32_bits(CFG_BASE + mmSYNC_MNGR_SOB_OBJ_0);
> + so_base_hi = upper_32_bits(CFG_BASE + mmSYNC_MNGR_SOB_OBJ_0);
> +
> + for (i = 0 ; i < TPC_MAX_NUM ; i++) {
> + WREG32(mmTPC0_CFG_SM_BASE_ADDRESS_LOW + i * cfg_off,
> + so_base_lo);
> + WREG32(mmTPC0_CFG_SM_BASE_ADDRESS_HIGH + i * cfg_off,
> + so_base_hi);
> + }
> +
> + goya_init_tpc_qman(hdev, TPC0_QMAN_BASE_OFFSET, 0);
> + goya_init_tpc_qman(hdev, TPC1_QMAN_BASE_OFFSET, 1);
> + goya_init_tpc_qman(hdev, TPC2_QMAN_BASE_OFFSET, 2);
> + goya_init_tpc_qman(hdev, TPC3_QMAN_BASE_OFFSET, 3);
> + goya_init_tpc_qman(hdev, TPC4_QMAN_BASE_OFFSET, 4);
> + goya_init_tpc_qman(hdev, TPC5_QMAN_BASE_OFFSET, 5);
> + goya_init_tpc_qman(hdev, TPC6_QMAN_BASE_OFFSET, 6);
> + goya_init_tpc_qman(hdev, TPC7_QMAN_BASE_OFFSET, 7);
> +
> + for (i = 0 ; i < TPC_MAX_NUM ; i++)
> + goya_init_tpc_cmdq(hdev, i);
> +
> + goya->hw_cap_initialized |= HW_CAP_TPC;
> +}
> +
> +/**
> + * goya_disable_internal_queues - Disable internal queues
> + *
> + * @hdev: pointer to hl_device structure
> + *
> + */
> +static void goya_disable_internal_queues(struct hl_device *hdev)
> +{
> + WREG32(mmMME_QM_GLBL_CFG0, 0);
> + WREG32(mmMME_CMDQ_GLBL_CFG0, 0);
> +
> + WREG32(mmTPC0_QM_GLBL_CFG0, 0);
> + WREG32(mmTPC0_CMDQ_GLBL_CFG0, 0);
> +
> + WREG32(mmTPC1_QM_GLBL_CFG0, 0);
> + WREG32(mmTPC1_CMDQ_GLBL_CFG0, 0);
> +
> + WREG32(mmTPC2_QM_GLBL_CFG0, 0);
> + WREG32(mmTPC2_CMDQ_GLBL_CFG0, 0);
> +
> + WREG32(mmTPC3_QM_GLBL_CFG0, 0);
> + WREG32(mmTPC3_CMDQ_GLBL_CFG0, 0);
> +
> + WREG32(mmTPC4_QM_GLBL_CFG0, 0);
> + WREG32(mmTPC4_CMDQ_GLBL_CFG0, 0);
> +
> + WREG32(mmTPC5_QM_GLBL_CFG0, 0);
> + WREG32(mmTPC5_CMDQ_GLBL_CFG0, 0);
> +
> + WREG32(mmTPC6_QM_GLBL_CFG0, 0);
> + WREG32(mmTPC6_CMDQ_GLBL_CFG0, 0);
> +
> + WREG32(mmTPC7_QM_GLBL_CFG0, 0);
> + WREG32(mmTPC7_CMDQ_GLBL_CFG0, 0);
> +}
> +
> +/**
> + * goya_stop_internal_queues - Stop internal queues
> + *
> + * @hdev: pointer to hl_device structure
> + *
> + * Returns 0 on success
> + *
> + */
> +static int goya_stop_internal_queues(struct hl_device *hdev)
> +{
> + int rc, retval = 0;
> +
> + rc = goya_stop_queue(hdev,
> + mmMME_QM_GLBL_CFG1,
> + mmMME_QM_CP_STS,
> + mmMME_QM_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop MME QMAN\n");
> + retval = -EIO;
> + }
> +
> + rc = goya_stop_queue(hdev,
> + mmMME_CMDQ_GLBL_CFG1,
> + mmMME_CMDQ_CP_STS,
> + mmMME_CMDQ_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop MME CMDQ\n");
> + retval = -EIO;
> + }
If I understand correctly, the queues can be and should be stopped independently and
failure to stop one of them wouldn't prevent stopping the others.
If that's the case a comment explaining that would be nice.
> + rc = goya_stop_queue(hdev,
> + mmTPC0_QM_GLBL_CFG1,
> + mmTPC0_QM_CP_STS,
> + mmTPC0_QM_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop TPC 0 QMAN\n");
> + retval = -EIO;
> + }
> +
> + rc = goya_stop_queue(hdev,
> + mmTPC0_CMDQ_GLBL_CFG1,
> + mmTPC0_CMDQ_CP_STS,
> + mmTPC0_CMDQ_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop TPC 0 CMDQ\n");
> + retval = -EIO;
> + }
> +
> + rc = goya_stop_queue(hdev,
> + mmTPC1_QM_GLBL_CFG1,
> + mmTPC1_QM_CP_STS,
> + mmTPC1_QM_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop TPC 1 QMAN\n");
> + retval = -EIO;
> + }
> +
> + rc = goya_stop_queue(hdev,
> + mmTPC1_CMDQ_GLBL_CFG1,
> + mmTPC1_CMDQ_CP_STS,
> + mmTPC1_CMDQ_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop TPC 1 CMDQ\n");
> + retval = -EIO;
> + }
> +
> + rc = goya_stop_queue(hdev,
> + mmTPC2_QM_GLBL_CFG1,
> + mmTPC2_QM_CP_STS,
> + mmTPC2_QM_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop TPC 2 QMAN\n");
> + retval = -EIO;
> + }
> +
> + rc = goya_stop_queue(hdev,
> + mmTPC2_CMDQ_GLBL_CFG1,
> + mmTPC2_CMDQ_CP_STS,
> + mmTPC2_CMDQ_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop TPC 2 CMDQ\n");
> + retval = -EIO;
> + }
> +
> + rc = goya_stop_queue(hdev,
> + mmTPC3_QM_GLBL_CFG1,
> + mmTPC3_QM_CP_STS,
> + mmTPC3_QM_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop TPC 3 QMAN\n");
> + retval = -EIO;
> + }
> +
> + rc = goya_stop_queue(hdev,
> + mmTPC3_CMDQ_GLBL_CFG1,
> + mmTPC3_CMDQ_CP_STS,
> + mmTPC3_CMDQ_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop TPC 3 CMDQ\n");
> + retval = -EIO;
> + }
> +
> + rc = goya_stop_queue(hdev,
> + mmTPC4_QM_GLBL_CFG1,
> + mmTPC4_QM_CP_STS,
> + mmTPC4_QM_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop TPC 4 QMAN\n");
> + retval = -EIO;
> + }
> +
> + rc = goya_stop_queue(hdev,
> + mmTPC4_CMDQ_GLBL_CFG1,
> + mmTPC4_CMDQ_CP_STS,
> + mmTPC4_CMDQ_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop TPC 4 CMDQ\n");
> + retval = -EIO;
> + }
> +
> + rc = goya_stop_queue(hdev,
> + mmTPC5_QM_GLBL_CFG1,
> + mmTPC5_QM_CP_STS,
> + mmTPC5_QM_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop TPC 5 QMAN\n");
> + retval = -EIO;
> + }
> +
> + rc = goya_stop_queue(hdev,
> + mmTPC5_CMDQ_GLBL_CFG1,
> + mmTPC5_CMDQ_CP_STS,
> + mmTPC5_CMDQ_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop TPC 5 CMDQ\n");
> + retval = -EIO;
> + }
> +
> + rc = goya_stop_queue(hdev,
> + mmTPC6_QM_GLBL_CFG1,
> + mmTPC6_QM_CP_STS,
> + mmTPC6_QM_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop TPC 6 QMAN\n");
> + retval = -EIO;
> + }
> +
> + rc = goya_stop_queue(hdev,
> + mmTPC6_CMDQ_GLBL_CFG1,
> + mmTPC6_CMDQ_CP_STS,
> + mmTPC6_CMDQ_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop TPC 6 CMDQ\n");
> + retval = -EIO;
> + }
> +
> + rc = goya_stop_queue(hdev,
> + mmTPC7_QM_GLBL_CFG1,
> + mmTPC7_QM_CP_STS,
> + mmTPC7_QM_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop TPC 7 QMAN\n");
> + retval = -EIO;
> + }
> +
> + rc = goya_stop_queue(hdev,
> + mmTPC7_CMDQ_GLBL_CFG1,
> + mmTPC7_CMDQ_CP_STS,
> + mmTPC7_CMDQ_GLBL_STS0);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop TPC 7 CMDQ\n");
> + retval = -EIO;
> + }
> +
> + return rc;
> +}
> +
> +static void goya_resume_internal_queues(struct hl_device *hdev)
> +{
> + WREG32(mmMME_QM_GLBL_CFG1, 0);
> + WREG32(mmMME_CMDQ_GLBL_CFG1, 0);
> +
> + WREG32(mmTPC0_QM_GLBL_CFG1, 0);
> + WREG32(mmTPC0_CMDQ_GLBL_CFG1, 0);
> +
> + WREG32(mmTPC1_QM_GLBL_CFG1, 0);
> + WREG32(mmTPC1_CMDQ_GLBL_CFG1, 0);
> +
> + WREG32(mmTPC2_QM_GLBL_CFG1, 0);
> + WREG32(mmTPC2_CMDQ_GLBL_CFG1, 0);
> +
> + WREG32(mmTPC3_QM_GLBL_CFG1, 0);
> + WREG32(mmTPC3_CMDQ_GLBL_CFG1, 0);
> +
> + WREG32(mmTPC4_QM_GLBL_CFG1, 0);
> + WREG32(mmTPC4_CMDQ_GLBL_CFG1, 0);
> +
> + WREG32(mmTPC5_QM_GLBL_CFG1, 0);
> + WREG32(mmTPC5_CMDQ_GLBL_CFG1, 0);
> +
> + WREG32(mmTPC6_QM_GLBL_CFG1, 0);
> + WREG32(mmTPC6_CMDQ_GLBL_CFG1, 0);
> +
> + WREG32(mmTPC7_QM_GLBL_CFG1, 0);
> + WREG32(mmTPC7_CMDQ_GLBL_CFG1, 0);
> +}
> +
> +
> +/**
> + * goya_push_uboot_to_device - Push u-boot FW code to device
> + *
> + * @hdev: pointer to hl_device structure
> + *
> + * Copy u-boot fw code from firmware file to SRAM BAR.
> + * Returns 0 on success
> + *
> + */
> +static int goya_push_uboot_to_device(struct hl_device *hdev)
> +{
> + char fw_name[200];
> + const u64 *fw_data;
> + void __iomem *dst;
> + size_t fw_size, i;
> + int rc;
> +
> + snprintf(fw_name, sizeof(fw_name), "habanalabs/goya/goya-u-boot.bin");
> +
> + rc = request_firmware(&hdev->spl_fw, fw_name, hdev->dev);
> +
> + if (rc) {
> + dev_err(hdev->dev, "Failed to request u-boot fw image\n");
> + goto out;
> + }
> +
> + fw_size = hdev->spl_fw->size;
> + if ((fw_size % 4) != 0) {
> + dev_err(hdev->dev, "illegal u-boot firmware size %lu\n",
> + fw_size);
> + rc = -EINVAL;
> + goto out;
> + }
> +
> + dev_dbg(hdev->dev, "u-boot firmware size == %lu\n", fw_size);
> +
> + fw_data = (const u64 *) hdev->spl_fw->data;
> + dst = hdev->pcie_bar[SRAM_CFG_BAR_ID] + UBOOT_FW_OFFSET;
> +
> + if ((hdev->spl_fw->size % 8) != 0)
> + fw_size -= 8;
> +
> + for (i = 0 ; i < fw_size ; i += 8, fw_data++, dst += 8) {
> + if (!(i & (0x80000 - 1)))
> + dev_dbg(hdev->dev,
> + "u-boot copied so far %lu out of %lu",
> + i, fw_size);
> +
> + writeq(*fw_data, dst);
> + }
> +
> + if ((hdev->spl_fw->size % 8) != 0)
> + writel(*(const u32 *) fw_data, dst);
> +
> +out:
> + release_firmware(hdev->spl_fw);
> + return rc;
> +}
> +
> +/**
> + * goya_push_linux_to_device - Push LINUX FW code to device
> + *
> + * @hdev: pointer to hl_device structure
> + *
> + * Copy LINXU fw code from firmware file to DDR BAR.
> + * Returns 0 on success
> + *
> + */
> +static int goya_push_linux_to_device(struct hl_device *hdev)
> +{
> + char fw_name[200];
> + const u64 *fw_data;
> + void __iomem *dst;
> + size_t fw_size, i;
> + int rc;
> +
> + snprintf(fw_name, sizeof(fw_name), "habanalabs/goya/goya-fit.itb");
> +
> + rc = request_firmware(&hdev->spl_fw, fw_name, hdev->dev);
> +
> + if (rc) {
> + dev_err(hdev->dev, "Failed to request Linux fw image\n");
> + goto out;
> + }
> +
> + fw_size = hdev->spl_fw->size;
> + if ((fw_size % 4) != 0) {
> + dev_err(hdev->dev, "illegal Linux firmware size %lu\n",
> + fw_size);
> + rc = -EINVAL;
> + goto out;
> + }
> +
> + dev_dbg(hdev->dev, "Linux firmware size == %lu\n", fw_size);
> +
> + fw_data = (const u64 *) hdev->spl_fw->data;
> + dst = hdev->pcie_bar[DDR_BAR_ID] + LINUX_FW_OFFSET;
> +
> + if ((hdev->spl_fw->size % 8) != 0)
> + fw_size -= 8;
> +
> + for (i = 0 ; i < fw_size ; i += 8, fw_data++, dst += 8) {
> + if (!(i & (0x80000 - 1))) {
> + dev_dbg(hdev->dev,
> + "Linux copied so far %lu out of %lu",
> + i, fw_size);
> + usleep_range(20, 100);
> + }
> + writeq(*fw_data, dst);
> + }
> +
> + if ((hdev->spl_fw->size % 8) != 0)
> + writel(*(const u32 *) fw_data, dst);
> +
> +out:
> + release_firmware(hdev->spl_fw);
> + return rc;
> +}
> +
> +static int goya_pldm_init_cpu(struct hl_device *hdev)
> +{
> + u32 val, unit_rst_val;
> + int rc;
> +
> + /* Must initialize SRAM scrambler before pushing u-boot to SRAM */
> + goya_init_golden_registers(hdev);
> +
> + /* Put ARM cores into reset */
> + WREG32(mmCPU_CA53_CFG_ARM_RST_CONTROL, CPU_RESET_ASSERT);
> + val = RREG32(mmCPU_CA53_CFG_ARM_RST_CONTROL);
> +
> + /* Reset the CA53 MACRO */
> + unit_rst_val = RREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N);
> + WREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N, CA53_RESET);
> + val = RREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N);
> + WREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N, unit_rst_val);
> + val = RREG32(mmPSOC_GLOBAL_CONF_UNIT_RST_N);
> +
> + rc = goya_push_uboot_to_device(hdev);
> + if (rc)
> + return rc;
> +
> + rc = goya_push_linux_to_device(hdev);
> + if (rc)
> + return rc;
>
> WREG32(mmPSOC_GLOBAL_CONF_UBOOT_MAGIC, KMD_MSG_FIT_RDY);
> WREG32(mmPSOC_GLOBAL_CONF_WARM_REBOOT, CPU_BOOT_STATUS_NA);
> @@ -2339,6 +3160,19 @@ static int goya_hw_init(struct hl_device *hdev)
>
> goya_init_security(hdev);
>
> + goya_init_dma_qmans(hdev);
> +
> + goya_init_mme_qmans(hdev);
> +
> + goya_init_tpc_qmans(hdev);
> +
> + rc = goya_init_cpu_queues(hdev);
> + if (rc) {
> + dev_err(hdev->dev, "failed to initialize CPU H/W queues %d\n",
> + rc);
> + goto disable_queues;
> + }
> +
> /* CPU initialization is finished, we can now move to 48 bit DMA mask */
> rc = pci_set_dma_mask(hdev->pdev, DMA_BIT_MASK(48));
> if (rc) {
> @@ -2347,7 +3181,7 @@ static int goya_hw_init(struct hl_device *hdev)
> if (rc) {
> dev_err(hdev->dev,
> "Unable to set pci dma mask to 32 bits\n");
> - return rc;
> + goto disable_pci_access;
> }
> }
>
> @@ -2359,7 +3193,7 @@ static int goya_hw_init(struct hl_device *hdev)
> if (rc) {
> dev_err(hdev->dev,
> "Unable to set pci consistent dma mask to 32 bits\n");
> - return rc;
> + goto disable_pci_access;
> }
> }
>
> @@ -2367,6 +3201,14 @@ static int goya_hw_init(struct hl_device *hdev)
> val = RREG32(mmPCIE_DBI_DEVICE_ID_VENDOR_ID_REG);
>
> return 0;
> +
> +disable_pci_access:
> + goya_send_pci_access_msg(hdev, ARMCP_PACKET_DISABLE_PCI_ACCESS);
> +disable_queues:
> + goya_disable_internal_queues(hdev);
> + goya_disable_external_queues(hdev);
> +
> + return rc;
> }
>
> /**
> @@ -2473,12 +3315,40 @@ static void goya_hw_fini(struct hl_device *hdev, bool hard_reset)
>
> int goya_suspend(struct hl_device *hdev)
> {
> - return 0;
> + int rc;
> +
> + rc = goya_stop_internal_queues(hdev);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop internal queues\n");
> + return rc;
> + }
> +
> + rc = goya_stop_external_queues(hdev);
> +
> + if (rc) {
> + dev_err(hdev->dev, "failed to stop external queues\n");
> + return rc;
> + }
> +
> + rc = goya_send_pci_access_msg(hdev, ARMCP_PACKET_DISABLE_PCI_ACCESS);
> + if (rc)
> + dev_err(hdev->dev, "Failed to disable PCI access from CPU\n");
> +
> + return rc;
> }
>
> int goya_resume(struct hl_device *hdev)
> {
> - return 0;
> + int rc;
> +
> + goya_resume_external_queues(hdev);
> + goya_resume_internal_queues(hdev);
> +
> + rc = goya_send_pci_access_msg(hdev, ARMCP_PACKET_ENABLE_PCI_ACCESS);
> + if (rc)
> + dev_err(hdev->dev, "Failed to enable PCI access from CPU\n");
> + return rc;
> }
>
> int goya_mmap(struct hl_fpriv *hpriv, struct vm_area_struct *vma)
> @@ -2502,6 +3372,104 @@ int goya_cb_mmap(struct hl_device *hdev, struct vm_area_struct *vma,
> return rc;
> }
>
> +void goya_ring_doorbell(struct hl_device *hdev, u32 hw_queue_id, u32 pi)
> +{
> + u32 db_reg_offset, db_value;
> + bool invalid_queue = false;
> +
> + switch (hw_queue_id) {
> + case GOYA_QUEUE_ID_DMA_0:
> + db_reg_offset = mmDMA_QM_0_PQ_PI;
> + break;
> +
> + case GOYA_QUEUE_ID_DMA_1:
> + db_reg_offset = mmDMA_QM_1_PQ_PI;
> + break;
> +
> + case GOYA_QUEUE_ID_DMA_2:
> + db_reg_offset = mmDMA_QM_2_PQ_PI;
> + break;
> +
> + case GOYA_QUEUE_ID_DMA_3:
> + db_reg_offset = mmDMA_QM_3_PQ_PI;
> + break;
> +
> + case GOYA_QUEUE_ID_DMA_4:
> + db_reg_offset = mmDMA_QM_4_PQ_PI;
> + break;
> +
> + case GOYA_QUEUE_ID_CPU_PQ:
> + if (hdev->cpu_queues_enable)
> + db_reg_offset = mmCPU_IF_PF_PQ_PI;
> + else
> + invalid_queue = true;
> + break;
> +
> + case GOYA_QUEUE_ID_MME:
> + db_reg_offset = mmMME_QM_PQ_PI;
> + break;
> +
> + case GOYA_QUEUE_ID_TPC0:
> + db_reg_offset = mmTPC0_QM_PQ_PI;
> + break;
> +
> + case GOYA_QUEUE_ID_TPC1:
> + db_reg_offset = mmTPC1_QM_PQ_PI;
> + break;
> +
> + case GOYA_QUEUE_ID_TPC2:
> + db_reg_offset = mmTPC2_QM_PQ_PI;
> + break;
> +
> + case GOYA_QUEUE_ID_TPC3:
> + db_reg_offset = mmTPC3_QM_PQ_PI;
> + break;
> +
> + case GOYA_QUEUE_ID_TPC4:
> + db_reg_offset = mmTPC4_QM_PQ_PI;
> + break;
> +
> + case GOYA_QUEUE_ID_TPC5:
> + db_reg_offset = mmTPC5_QM_PQ_PI;
> + break;
> +
> + case GOYA_QUEUE_ID_TPC6:
> + db_reg_offset = mmTPC6_QM_PQ_PI;
> + break;
> +
> + case GOYA_QUEUE_ID_TPC7:
> + db_reg_offset = mmTPC7_QM_PQ_PI;
> + break;
> +
> + default:
> + invalid_queue = true;
> + }
> +
> + if (invalid_queue) {
> + /* Should never get here */
> + dev_err(hdev->dev, "h/w queue %d is invalid. Can't set pi\n",
> + hw_queue_id);
> + return;
> + }
> +
> + db_value = pi;
> +
> + if (hdev->ifh)
> + return;
> +
> + /* ring the doorbell */
> + WREG32(db_reg_offset, db_value);
> +
> + if (hw_queue_id == GOYA_QUEUE_ID_CPU_PQ)
> + WREG32(mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR,
> + GOYA_ASYNC_EVENT_ID_PI_UPDATE);
> +}
> +
> +void goya_flush_pq_write(struct hl_device *hdev, u64 *pq, u64 exp_val)
> +{
> + /* Not needed in Goya */
> +}
> +
> void *goya_dma_alloc_coherent(struct hl_device *hdev, size_t size,
> dma_addr_t *dma_handle, gfp_t flags)
> {
> @@ -2514,6 +3482,311 @@ void goya_dma_free_coherent(struct hl_device *hdev, size_t size, void *cpu_addr,
> dma_free_coherent(&hdev->pdev->dev, size, cpu_addr, dma_handle);
> }
>
> +void *goya_get_int_queue_base(struct hl_device *hdev, u32 queue_id,
> + dma_addr_t *dma_handle, u16 *queue_len)
> +{
> + void *base;
> + u32 offset;
> +
> + *dma_handle = hdev->asic_prop.sram_base_address;
> +
> + base = hdev->pcie_bar[SRAM_CFG_BAR_ID];
> +
> + switch (queue_id) {
> + case GOYA_QUEUE_ID_MME:
> + offset = MME_QMAN_BASE_OFFSET;
> + *queue_len = MME_QMAN_LENGTH;
> + break;
> + case GOYA_QUEUE_ID_TPC0:
> + offset = TPC0_QMAN_BASE_OFFSET;
> + *queue_len = TPC_QMAN_LENGTH;
> + break;
> + case GOYA_QUEUE_ID_TPC1:
> + offset = TPC1_QMAN_BASE_OFFSET;
> + *queue_len = TPC_QMAN_LENGTH;
> + break;
> + case GOYA_QUEUE_ID_TPC2:
> + offset = TPC2_QMAN_BASE_OFFSET;
> + *queue_len = TPC_QMAN_LENGTH;
> + break;
> + case GOYA_QUEUE_ID_TPC3:
> + offset = TPC3_QMAN_BASE_OFFSET;
> + *queue_len = TPC_QMAN_LENGTH;
> + break;
> + case GOYA_QUEUE_ID_TPC4:
> + offset = TPC4_QMAN_BASE_OFFSET;
> + *queue_len = TPC_QMAN_LENGTH;
> + break;
> + case GOYA_QUEUE_ID_TPC5:
> + offset = TPC5_QMAN_BASE_OFFSET;
> + *queue_len = TPC_QMAN_LENGTH;
> + break;
> + case GOYA_QUEUE_ID_TPC6:
> + offset = TPC6_QMAN_BASE_OFFSET;
> + *queue_len = TPC_QMAN_LENGTH;
> + break;
> + case GOYA_QUEUE_ID_TPC7:
> + offset = TPC7_QMAN_BASE_OFFSET;
> + *queue_len = TPC_QMAN_LENGTH;
> + break;
> + default:
> + dev_err(hdev->dev, "Got invalid queue id %d\n", queue_id);
> + return NULL;
> + }
> +
> + base += offset;
> + *dma_handle += offset;
> +
> + return base;
> +}
> +
> +int goya_send_cpu_message(struct hl_device *hdev, u32 *msg, u16 len,
> + u32 timeout, long *result)
> +{
> + struct goya_device *goya = hdev->asic_specific;
> + struct armcp_packet *pkt;
> + dma_addr_t pkt_dma_addr;
> + u32 tmp;
> + int rc = 0;
> +
> + if (!(goya->hw_cap_initialized & HW_CAP_CPU_Q)) {
> + if (result)
> + *result = 0;
> + return 0;
> + }
> +
> + if (len > CPU_CB_SIZE) {
> + dev_err(hdev->dev, "Invalid CPU message size of %d bytes\n",
> + len);
> + return -ENOMEM;
> + }
> +
> + pkt = hdev->asic_funcs->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);
> +
> + if (hdev->disabled)
> + goto out;
> +
> + rc = hl_hw_queue_send_cb_no_cmpl(hdev, GOYA_QUEUE_ID_CPU_PQ, len,
> + pkt_dma_addr);
> + if (rc) {
> + dev_err(hdev->dev, "Failed to send CB on CPU PQ (%d)\n", rc);
> + goto out;
> + }
> +
> + rc = hl_poll_timeout_memory(hdev, (u64) &pkt->fence, timeout, &tmp);
> +
> + hl_hw_queue_inc_ci_kernel(hdev, GOYA_QUEUE_ID_CPU_PQ);
> +
> + if (rc == -ETIMEDOUT) {
> + dev_err(hdev->dev,
> + "Timeout while waiting for CPU packet fence\n");
> + goto out;
> + }
> +
> + if (tmp == ARMCP_PACKET_FENCE_VAL) {
> + if (pkt->rc) {
> + dev_err(hdev->dev,
> + "failed to execute CPU packet, rc: %d\n",
> + pkt->rc);
> + rc = -EINVAL;
> + } else if (result) {
> + *result = pkt->result;
For some error cases above the *result is not initialized.
> + }
> + } else {
> + dev_err(hdev->dev, "CPU packet wrong fence value\n");
> + rc = -EINVAL;
> + }
> +
> +out:
> + mutex_unlock(&hdev->send_cpu_message_lock);
> +
> + hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, len, pkt);
> +
> + return rc;
> +}
> +
> +int goya_test_queue(struct hl_device *hdev, u32 hw_queue_id)
> +{
> + struct packet_msg_prot *fence_pkt;
> + dma_addr_t pkt_dma_addr;
> + u32 fence_val, tmp;
> + dma_addr_t fence_dma_addr;
> + u32 *fence_ptr;
> + int rc;
> +
> + fence_val = GOYA_QMAN0_FENCE_VAL;
> +
> + fence_ptr = hdev->asic_funcs->dma_pool_zalloc(hdev, 4, GFP_KERNEL,
> + &fence_dma_addr);
> + if (!fence_ptr) {
> + dev_err(hdev->dev,
> + "Failed to allocate memory for queue testing\n");
> + return -ENOMEM;
> + }
> +
> + *fence_ptr = 0;
> +
> + fence_pkt = hdev->asic_funcs->dma_pool_zalloc(hdev,
> + sizeof(struct packet_msg_prot),
> + GFP_KERNEL, &pkt_dma_addr);
> + if (!fence_pkt) {
> + dev_err(hdev->dev,
> + "Failed to allocate packet for queue testing\n");
> + rc = -ENOMEM;
> + goto free_fence_ptr;
> + }
> +
> + fence_pkt->opcode = PACKET_MSG_PROT;
> + fence_pkt->value = fence_val;
> + fence_pkt->addr = fence_dma_addr +
> + hdev->asic_prop.host_phys_base_address;
> +
> + rc = hl_hw_queue_send_cb_no_cmpl(hdev, hw_queue_id,
> + sizeof(struct packet_msg_prot),
> + pkt_dma_addr);
> + if (rc) {
> + dev_err(hdev->dev,
> + "Failed to send fence packet\n");
> + goto free_pkt;
> + }
> +
> + rc = hl_poll_timeout_memory(hdev, (u64) fence_ptr,
> + GOYA_TEST_QUEUE_WAIT_USEC, &tmp);
> +
> + hl_hw_queue_inc_ci_kernel(hdev, hw_queue_id);
> +
> + if ((!rc) && (tmp == fence_val)) {
> + dev_info(hdev->dev,
> + "queue test on H/W queue %d succeeded\n",
> + hw_queue_id);
> + } else {
> + dev_err(hdev->dev,
> + "H/W queue %d test failed (scratch(0x%08llX) == 0x%08X)\n",
> + hw_queue_id, fence_dma_addr, tmp);
> + rc = -EINVAL;
> + }
> +
> +free_pkt:
> + hdev->asic_funcs->dma_pool_free(hdev, (void *) fence_pkt,
> + pkt_dma_addr);
> +free_fence_ptr:
> + hdev->asic_funcs->dma_pool_free(hdev, (void *) fence_ptr,
> + fence_dma_addr);
> + return rc;
> +}
> +
> +int goya_test_cpu_queue(struct hl_device *hdev)
> +{
> + struct armcp_packet test_pkt;
> + long result;
> + int rc;
> +
> + /* cpu_queues_enable flag is always checked in send cpu message */
> +
> + memset(&test_pkt, 0, sizeof(test_pkt));
> +
> + test_pkt.opcode = ARMCP_PACKET_TEST;
> + test_pkt.value = ARMCP_PACKET_FENCE_VAL;
> +
> + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &test_pkt,
> + sizeof(test_pkt), HL_DEVICE_TIMEOUT_USEC, &result);
> +
> + if (!rc)
> + dev_info(hdev->dev, "queue test on CPU queue succeeded\n");
> + else
> + dev_err(hdev->dev, "CPU queue test failed (0x%08lX)\n", result);
> +
> + return rc;
> +}
> +
> +static int goya_test_queues(struct hl_device *hdev)
> +{
> + struct goya_device *goya = hdev->asic_specific;
> + int i, rc, ret_val = 0;
> +
> + if (hdev->ifh)
> + return 0;
> +
> + for (i = 0 ; i < NUMBER_OF_EXT_HW_QUEUES ; i++) {
> + rc = goya_test_queue(hdev, i);
> + if (rc)
> + ret_val = -EINVAL;
> + }
> +
> + if (hdev->cpu_queues_enable) {
> + rc = goya->test_cpu_queue(hdev);
> + if (rc)
> + ret_val = -EINVAL;
> + }
> +
> + return ret_val;
> +}
> +
> +void *goya_dma_pool_zalloc(struct hl_device *hdev, size_t size, gfp_t mem_flags,
> + dma_addr_t *dma_handle)
> +{
> + if (size > GOYA_DMA_POOL_BLK_SIZE)
> + return NULL;
> +
> + return dma_pool_zalloc(hdev->dma_pool, mem_flags, dma_handle);
> +}
> +
> +void goya_dma_pool_free(struct hl_device *hdev, void *vaddr,
> + dma_addr_t dma_addr)
> +{
> + dma_pool_free(hdev->dma_pool, vaddr, dma_addr);
> +}
> +
> +void *goya_cpu_accessible_dma_pool_alloc(struct hl_device *hdev, size_t size,
> + dma_addr_t *dma_handle)
> +{
> + u64 kernel_addr;
> +
> + /* roundup to CPU_PKT_SIZE */
> + size = (size + (CPU_PKT_SIZE - 1)) & CPU_PKT_MASK;
> +
> + kernel_addr = gen_pool_alloc(hdev->cpu_accessible_dma_pool, size);
> +
> + *dma_handle = hdev->cpu_accessible_dma_address +
> + (kernel_addr - (u64) hdev->cpu_accessible_dma_mem);
> +
> + return (void *) kernel_addr;
> +}
> +
> +void goya_cpu_accessible_dma_pool_free(struct hl_device *hdev, size_t size,
> + void *vaddr)
> +{
> + /* roundup to CPU_PKT_SIZE */
> + size = (size + (CPU_PKT_SIZE - 1)) & CPU_PKT_MASK;
> +
> + gen_pool_free(hdev->cpu_accessible_dma_pool, (u64) vaddr, size);
> +}
> +
> +
> +static void goya_hw_queues_lock(struct hl_device *hdev)
> +{
> + struct goya_device *goya = hdev->asic_specific;
> +
> + spin_lock(&goya->hw_queues_lock);
> +}
> +
> +static void goya_hw_queues_unlock(struct hl_device *hdev)
> +{
> + struct goya_device *goya = hdev->asic_specific;
> +
> + spin_unlock(&goya->hw_queues_lock);
> +}
> +
> static const struct hl_asic_funcs goya_funcs = {
> .early_init = goya_early_init,
> .early_fini = goya_early_fini,
> @@ -2525,8 +3798,19 @@ static const struct hl_asic_funcs goya_funcs = {
> .resume = goya_resume,
> .mmap = goya_mmap,
> .cb_mmap = goya_cb_mmap,
> + .ring_doorbell = goya_ring_doorbell,
> + .flush_pq_write = goya_flush_pq_write,
> .dma_alloc_coherent = goya_dma_alloc_coherent,
> .dma_free_coherent = goya_dma_free_coherent,
> + .get_int_queue_base = goya_get_int_queue_base,
> + .test_queues = goya_test_queues,
> + .dma_pool_zalloc = goya_dma_pool_zalloc,
> + .dma_pool_free = goya_dma_pool_free,
> + .cpu_accessible_dma_pool_alloc = goya_cpu_accessible_dma_pool_alloc,
> + .cpu_accessible_dma_pool_free = goya_cpu_accessible_dma_pool_free,
> + .hw_queues_lock = goya_hw_queues_lock,
> + .hw_queues_unlock = goya_hw_queues_unlock,
> + .send_cpu_message = goya_send_cpu_message
> };
>
> /**
> diff --git a/drivers/misc/habanalabs/goya/goyaP.h b/drivers/misc/habanalabs/goya/goyaP.h
> index 45a6d2ca2752..598a718d3df1 100644
> --- a/drivers/misc/habanalabs/goya/goyaP.h
> +++ b/drivers/misc/habanalabs/goya/goyaP.h
> @@ -9,6 +9,7 @@
> #define GOYAP_H_
>
> #include "habanalabs.h"
> +#include "include/goya/goya_packets.h"
> #include "include/goya/goya_boot_if.h"
> #include "include/goya/goya.h"
>
> @@ -117,12 +118,17 @@ enum goya_fw_component {
> };
>
> struct goya_device {
> + int (*test_cpu_queue)(struct hl_device *hdev);
> +
> /* TODO: remove hw_queues_lock after moving to scheduler code */
> spinlock_t hw_queues_lock;
> u64 ddr_bar_cur_addr;
> u32 hw_cap_initialized;
> };
>
> +int goya_test_cpu_queue(struct hl_device *hdev);
> +int goya_send_cpu_message(struct hl_device *hdev, u32 *msg, u16 len,
> + u32 timeout, long *result);
> void goya_init_security(struct hl_device *hdev);
>
> #endif /* GOYAP_H_ */
> diff --git a/drivers/misc/habanalabs/habanalabs.h b/drivers/misc/habanalabs/habanalabs.h
> index adda281ec2af..8232e2259463 100644
> --- a/drivers/misc/habanalabs/habanalabs.h
> +++ b/drivers/misc/habanalabs/habanalabs.h
> @@ -30,10 +30,36 @@
> struct hl_device;
> struct hl_fpriv;
>
> +/**
> + * 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.
> + */
> +enum hl_queue_type {
> + QUEUE_TYPE_NA,
> + QUEUE_TYPE_EXT,
> + QUEUE_TYPE_INT,
> + QUEUE_TYPE_CPU
> +};
>
> +/**
> + * struct hw_queue_properties - queue information.
> + * @type: queue type.
> + * @kmd_only: true if only KMD is allowed to send a job to this queue, false
> + * otherwise.
> + */
> +struct hw_queue_properties {
> + enum hl_queue_type type;
> + u8 kmd_only;
> +};
>
> /**
> * struct asic_fixed_properties - ASIC specific immutable properties.
> + * @hw_queues_props: H/W queues properties.
> * @uboot_ver: F/W U-boot version.
> * @preboot_ver: F/W Preboot version.
> * @sram_base_address: SRAM physical start address.
> @@ -64,6 +90,7 @@ struct hl_fpriv;
> * @tpc_enabled_mask: which TPCs are enabled.
> */
> struct asic_fixed_properties {
> + struct hw_queue_properties hw_queues_props[HL_MAX_QUEUES];
> char uboot_ver[VERSION_MAX_LEN];
> char preboot_ver[VERSION_MAX_LEN];
> u64 sram_base_address;
> @@ -145,7 +172,92 @@ struct hl_cb {
>
>
>
> +/*
> + * QUEUES
> + */
> +
> +struct hl_cs_job;
> +
> +/*
> + * Currently, there are two limitations on the maximum length of a queue:
> + *
> + * 1. The memory footprint of the queue. The current allocated space for the
> + * queue is PAGE_SIZE. Because each entry in the queue is HL_BD_SIZE,
> + * the maximum length of the queue can be PAGE_SIZE / HL_BD_SIZE,
> + * which currently is 4096/16 = 256 entries.
> + *
> + * To increase that, we need either to decrease the size of the
> + * BD (difficult), or allocate more than a single page (easier).
> + *
> + * 2. Because the size of the JOB handle field in the BD CTL / completion queue
> + * is 10-bit, we can have up to 1024 open jobs per hardware queue.
> + * Therefore, each queue can hold up to 1024 entries.
> + *
> + * HL_QUEUE_LENGTH is in units of struct hl_bd.
> + * HL_QUEUE_LENGTH * sizeof(struct hl_bd) should be <= HL_PAGE_SIZE
> + */
> +
> +#define HL_PAGE_SIZE 4096 /* minimum page size */
> +/* Must be power of 2 (HL_PAGE_SIZE / HL_BD_SIZE) */
> #define HL_QUEUE_LENGTH 256
> +#define HL_QUEUE_SIZE_IN_BYTES (HL_QUEUE_LENGTH * HL_BD_SIZE)
> +
> +/*
> + * HL_CQ_LENGTH is in units of struct hl_cq_entry.
> + * HL_CQ_LENGTH should be <= HL_PAGE_SIZE
> + */
> +#define HL_CQ_LENGTH HL_QUEUE_LENGTH
> +#define HL_CQ_SIZE_IN_BYTES (HL_CQ_LENGTH * HL_CQ_ENTRY_SIZE)
> +
> +
> +
> +/**
> + * struct hl_hw_queue - describes a H/W transport queue.
> + * @shadow_queue: pointer to a shadow queue that holds pointers to jobs.
> + * @queue_type: type of 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.
> + * @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
> + * exists).
> + */
> +struct hl_hw_queue {
> + struct hl_cs_job **shadow_queue;
> + enum hl_queue_type queue_type;
> + u64 kernel_address;
> + dma_addr_t bus_address;
> + u32 pi;
> + u32 ci;
> + u32 hw_queue_id;
> + u16 int_queue_len;
> + u8 valid;
> +};
> +
> +/**
> + * 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
> + * @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;
> + u64 kernel_address;
> + dma_addr_t bus_address;
> + u32 hw_queue_id;
> + u32 ci;
> + u32 pi;
> + atomic_t free_slots_cnt;
> +};
> +
> +
> +
>
>
> /*
> @@ -180,8 +292,20 @@ enum hl_asic_type {
> * @resume: handles IP specific H/W or SW changes for resume.
> * @mmap: mmap function, does nothing.
> * @cb_mmap: maps a CB.
> + * @ring_doorbell: increment PI on a given QMAN.
> + * @flush_pq_write: flush PQ entry write if necessary, WARN if flushing failed.
> * @dma_alloc_coherent: DMA allocate coherent memory.
> * @dma_free_coherent: free DMA allocation.
> + * @get_int_queue_base: get the internal queue base address.
> + * @test_queues: run simple test on all queues for sanity check.
> + * @dma_pool_zalloc: small DMA allocation of coherent memory from DMA pool.
> + * size of allocation is HL_DMA_POOL_BLK_SIZE.
> + * @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.
> + * @hw_queues_lock: acquire H/W queues lock.
> + * @hw_queues_unlock: release H/W queues lock.
> + * @send_cpu_message: send buffer to ArmCP.
> */
> struct hl_asic_funcs {
> int (*early_init)(struct hl_device *hdev);
> @@ -195,10 +319,27 @@ struct hl_asic_funcs {
> int (*mmap)(struct hl_fpriv *hpriv, struct vm_area_struct *vma);
> int (*cb_mmap)(struct hl_device *hdev, struct vm_area_struct *vma,
> u64 kaddress, phys_addr_t paddress, u32 size);
> + void (*ring_doorbell)(struct hl_device *hdev, u32 hw_queue_id, u32 pi);
> + void (*flush_pq_write)(struct hl_device *hdev, u64 *pq, u64 exp_val);
> void* (*dma_alloc_coherent)(struct hl_device *hdev, size_t size,
> dma_addr_t *dma_handle, gfp_t flag);
> void (*dma_free_coherent)(struct hl_device *hdev, size_t size,
> void *cpu_addr, dma_addr_t dma_handle);
> + 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* (*dma_pool_zalloc)(struct hl_device *hdev, size_t size,
> + gfp_t mem_flags, dma_addr_t *dma_handle);
> + void (*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 (*hw_queues_lock)(struct hl_device *hdev);
> + void (*hw_queues_unlock)(struct hl_device *hdev);
> + int (*send_cpu_message)(struct hl_device *hdev, u32 *msg,
> + u16 len, u32 timeout, long *result);
> };
>
>
> @@ -240,6 +381,17 @@ struct hl_ctx_mgr {
>
>
>
> +/**
> + * struct hl_cs_job - command submission job.
> + * @finish_work: workqueue object to run when job is completed.
> + * @id: the id of this job inside a CS.
> + */
> +struct hl_cs_job {
> + struct work_struct finish_work;
> + u32 id;
> +};
> +
> +
> /*
> * FILE PRIVATE STRUCTURE
> */
> @@ -316,7 +468,11 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val);
> * @dev: realted kernel basic device structure.
> * @asic_name: ASIC specific nmae.
> * @asic_type: ASIC specific type.
> + * @completion_queue: array of hl_cq.
> + * @cq_wq: work queue of completion queues for executing work in process context
> + * @eq_wq: work queue of event queue for executing work in process context.
> * @kernel_ctx: KMD context structure.
> + * @kernel_queues: array of hl_hw_queue.
> * @kernel_cb_mgr: command buffer manager for creating/destroying/handling CGs.
> * @dma_pool: DMA pool for small allocations.
> * @cpu_accessible_dma_mem: KMD <-> ArmCP shared memory CPU address.
> @@ -326,6 +482,7 @@ void hl_wreg(struct hl_device *hdev, u32 reg, u32 val);
> * @asid_bitmap: holds used/available ASIDs.
> * @asid_mutex: protects asid_bitmap.
> * @device_open: lock for sanity checks upon FD open.
> + * @send_cpu_message_lock: enforces only one message in KMD <-> ArmCP queue.
> * @asic_prop: ASIC specific immutable properties.
> * @asic_funcs: ASIC specific functions.
> * @asic_specific: ASIC specific information to use only from ASIC files.
> @@ -345,7 +502,10 @@ struct hl_device {
> struct device *dev;
> char asic_name[16];
> enum hl_asic_type asic_type;
> + struct hl_cq *completion_queue;
> + struct workqueue_struct *cq_wq;
> struct hl_ctx *kernel_ctx;
> + struct hl_hw_queue *kernel_queues;
> struct hl_cb_mgr kernel_cb_mgr;
> struct dma_pool *dma_pool;
> void *cpu_accessible_dma_mem;
> @@ -356,6 +516,7 @@ struct hl_device {
> struct mutex asid_mutex;
> /* TODO: change to rw_sem for multiple contexts (same as other IOCTL) */
> struct mutex device_open;
> + struct mutex send_cpu_message_lock;
> struct asic_fixed_properties asic_prop;
> const struct hl_asic_funcs *asic_funcs;
> void *asic_specific;
> @@ -374,7 +535,9 @@ struct hl_device {
> u8 cpu_enable;
> u8 reset_pcilink;
> u8 config_pll;
> + u8 cpu_queues_enable;
> u8 fw_loading;
> + u8 ifh;
> u8 pldm;
> };
>
> @@ -418,7 +581,18 @@ int hl_poll_timeout_memory(struct hl_device *hdev, u64 addr, u32 timeout_us,
> u32 *val);
> int hl_poll_timeout_device_memory(struct hl_device *hdev, void __iomem *addr,
> u32 timeout_us, u32 *val);
> -
> +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);
> +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);
> +
> +#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_asid_init(struct hl_device *hdev);
> void hl_asid_fini(struct hl_device *hdev);
> unsigned long hl_asid_alloc(struct hl_device *hdev);
> diff --git a/drivers/misc/habanalabs/habanalabs_drv.c b/drivers/misc/habanalabs/habanalabs_drv.c
> index bd80683118d3..b64f58ad0f5d 100644
> --- a/drivers/misc/habanalabs/habanalabs_drv.c
> +++ b/drivers/misc/habanalabs/habanalabs_drv.c
> @@ -184,13 +184,19 @@ int create_hdev(struct hl_device **dev, struct pci_dev *pdev,
> hdev->cpu_enable = 1;
> hdev->reset_pcilink = 0;
> hdev->config_pll = 0;
> + hdev->cpu_queues_enable = 1;
> hdev->fw_loading = 1;
> + hdev->ifh = 0;
> hdev->pldm = 0;
>
> /* If CPU is disabled, no point in loading FW */
> if (!hdev->cpu_enable)
> hdev->fw_loading = 0;
>
> + /* If we don't load FW, no need to initialize CPU queues */
> + if (!hdev->fw_loading)
> + hdev->cpu_queues_enable = 0;
> +
> hdev->disabled = true;
> hdev->pdev = pdev; /* can be NULL in case of simulator device */
>
> diff --git a/drivers/misc/habanalabs/hw_queue.c b/drivers/misc/habanalabs/hw_queue.c
> new file mode 100644
> index 000000000000..65102a5bc2ca
> --- /dev/null
> +++ b/drivers/misc/habanalabs/hw_queue.c
> @@ -0,0 +1,404 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * Copyright 2016-2018 HabanaLabs, Ltd.
> + * All Rights Reserved.
> + */
> +
> +#include "habanalabs.h"
> +
> +#include <linux/dma-mapping.h>
> +#include <linux/sched.h>
> +#include <linux/wait.h>
> +#include <linux/delay.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_free_slots(struct hl_hw_queue *q, u32 queue_len)
> +{
> + int delta = (q->pi - q->ci);
> +
> + if (delta >= 0)
> + return (queue_len - delta);
> + else
> + return (abs(delta) - queue_len);
> +}
> +
> +/**
> + * ext_queue_submit_bd - Submit a buffer descriptor to an external 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
> + *
> + */
> +static void ext_queue_submit_bd(struct hl_device *hdev, struct hl_hw_queue *q,
> + u32 ctl, u32 len, u64 ptr)
> +{
> + struct hl_bd *bd;
> +
> + bd = (struct hl_bd *) q->kernel_address;
> + bd += hl_pi_2_offset(q->pi);
> + bd->ctl = ctl;
> + bd->len = len;
> + bd->ptr = ptr + hdev->asic_prop.host_phys_base_address;
> +
> + 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->hw_queue_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;
> +}
> +
> +/**
> + * 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
> + *
> + */
> +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;
> +
> + /*
> + * The CPU queue is a synchronous queue with an effective depth of
> + * a single entry (although it is allocated with room for multiple
> + * entries). Therefore, there is a different lock, called
> + * send_cpu_message_lock, that serializes accesses to the CPU queue.
> + * As a result, we don't need to lock the access to the entire H/W
> + * queues module when submitting a JOB to the CPU queue
> + */
> + if (q->queue_type != QUEUE_TYPE_CPU)
> + hdev->asic_funcs->hw_queues_lock(hdev);
> +
> + if (hdev->disabled) {
> + rc = -EPERM;
> + goto out;
> + }
> +
> + rc = ext_queue_sanity_checks(hdev, q, 1, false);
> + if (rc)
> + goto out;
> +
> + ext_queue_submit_bd(hdev, q, 0, cb_size, cb_ptr);
> +
> +out:
> + if (q->queue_type != QUEUE_TYPE_CPU)
> + 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];
> +
> + q->ci = hl_queue_inc_ptr(q->ci);
> +}
> +
> +static int ext_and_cpu_hw_queue_init(struct hl_device *hdev,
> + struct hl_hw_queue *q)
> +{
> + void *p;
> + int rc;
> +
> + p = hdev->asic_funcs->dma_alloc_coherent(hdev,
> + HL_QUEUE_SIZE_IN_BYTES,
> + &q->bus_address, GFP_KERNEL | __GFP_ZERO);
> + if (!p)
> + return -ENOMEM;
> +
> + q->kernel_address = (u64) p;
> +
> + q->shadow_queue = kmalloc_array(HL_QUEUE_LENGTH,
> + sizeof(*q->shadow_queue),
> + 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 */
> + q->ci = 0;
> + q->pi = 0;
> +
> + return 0;
> +
> +free_queue:
> + hdev->asic_funcs->dma_free_coherent(hdev, HL_QUEUE_SIZE_IN_BYTES,
> + (void *) q->kernel_address, q->bus_address);
> +
> + return rc;
> +}
> +
> +static int int_hw_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 = (u64) p;
> + q->pi = 0;
> + q->ci = 0;
> +
> + return 0;
> +}
> +
> +static int cpu_hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q)
> +{
> + return ext_and_cpu_hw_queue_init(hdev, q);
> +}
> +
> +static int ext_hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q)
> +{
> + return ext_and_cpu_hw_queue_init(hdev, q);
> +}
> +
> +/**
> + * hw_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 hw_queue_init(struct hl_device *hdev, struct hl_hw_queue *q,
> + u32 hw_queue_id)
> +{
> + int rc;
> +
> + BUILD_BUG_ON(HL_QUEUE_SIZE_IN_BYTES > HL_PAGE_SIZE);
> +
> + q->hw_queue_id = hw_queue_id;
> +
> + switch (q->queue_type) {
> + case QUEUE_TYPE_EXT:
> + rc = ext_hw_queue_init(hdev, q);
> + break;
> +
> + case QUEUE_TYPE_INT:
> + rc = int_hw_queue_init(hdev, q);
> + break;
> +
> + case QUEUE_TYPE_CPU:
> + rc = cpu_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;
> + }
> +
> + 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 hw_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);
> +
> + hdev->asic_funcs->dma_free_coherent(hdev,
> + HL_QUEUE_SIZE_IN_BYTES,
> + (void *) 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(HL_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 < HL_MAX_QUEUES ; i++, q_ready_cnt++, q++) {
> +
> + q->queue_type = asic->hw_queues_props[i].type;
> + rc = hw_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++)
> + hw_queue_fini(hdev, q);
> +
> + kfree(hdev->kernel_queues);
> +
> + return rc;
> +}
> +
> +void hl_hw_queues_destroy(struct hl_device *hdev)
> +{
> + struct hl_hw_queue *q;
> + int i;
> +
> + for (i = 0, q = hdev->kernel_queues ; i < HL_MAX_QUEUES ; i++, q++)
> + hw_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;
> + int i;
> +
> + for (i = 0, q = hdev->kernel_queues ; i < HL_MAX_QUEUES ; i++, q++) {
> + if ((!q->valid) ||
> + ((!hard_reset) && (q->queue_type == QUEUE_TYPE_CPU)))
> + continue;
> + q->pi = q->ci = 0;
> + }
> +}
> diff --git a/drivers/misc/habanalabs/include/goya/goya_packets.h b/drivers/misc/habanalabs/include/goya/goya_packets.h
> new file mode 100644
> index 000000000000..669a3f37ccb7
> --- /dev/null
> +++ b/drivers/misc/habanalabs/include/goya/goya_packets.h
> @@ -0,0 +1,234 @@
> +/* SPDX-License-Identifier: GPL-2.0
> + *
> + * Copyright 2017-2018 HabanaLabs, Ltd.
> + * All Rights Reserved.
> + *
> + * Authors:
> + *
> + * Oded Gabbay <oded.gabbay@xxxxxxxxx>
> + * Guy Eilat <geilat@xxxxxxxxx>
> + *
> + */
> +
> +#ifndef GOYA_PACKETS_H
> +#define GOYA_PACKETS_H
> +
> +#include <linux/types.h>
> +
> +#define PACKET_HEADER_PACKET_ID_SHIFT 56
> +#define PACKET_HEADER_PACKET_ID_MASK 0x1F00000000000000ull
> +
> +enum packet_id {
> + PACKET_WREG_32 = 0x1,
> + PACKET_WREG_BULK = 0x2,
> + PACKET_MSG_LONG = 0x3,
> + PACKET_MSG_SHORT = 0x4,
> + PACKET_CP_DMA = 0x5,
> + PACKET_MSG_PROT = 0x7,
> + PACKET_FENCE = 0x8,
> + PACKET_LIN_DMA = 0x9,
> + PACKET_NOP = 0xA,
> + PACKET_STOP = 0xB,
> + MAX_PACKET_ID = (PACKET_HEADER_PACKET_ID_MASK >>
> + PACKET_HEADER_PACKET_ID_SHIFT) + 1
> +};
> +
> +enum goya_dma_direction {
> + DMA_HOST_TO_DRAM,
> + DMA_HOST_TO_SRAM,
> + DMA_DRAM_TO_SRAM,
> + DMA_SRAM_TO_DRAM,
> + DMA_SRAM_TO_HOST,
> + DMA_DRAM_TO_HOST,
> + DMA_DRAM_TO_DRAM,
> + DMA_SRAM_TO_SRAM,
> + DMA_ENUM_MAX
> +};
> +
> +struct packet_nop {
> + __u32 reserved;
> + union {
> + struct {
> + __u32:24;
> + __u32 opcode :5;
> + __u32 eng_barrier :1;
> + __u32 reg_barrier :1;
> + __u32 msg_barrier :1;
> + };
> + __u32 ctl;
> + };
> +};
> +
> +struct packet_stop {
> + __u32 reserved;
> + union {
> + struct {
> + __u32:24;
> + __u32 opcode :5;
> + __u32 eng_barrier :1;
> + __u32 reg_barrier :1; /* must be 0 */
> + __u32 msg_barrier :1; /* must be 0 */
> + };
> + __u32 ctl;
> + };
> +};
> +
> +struct packet_wreg32 {
> + __u32 value;
> + union {
> + struct {
> + __u32 reg_offset :16;
> + __u32:7;
> + __u32 local :1; /* 0: write to TCL regs,
> + * 1: write to CMDQ regs
> + */
> + __u32 opcode :5;
> + __u32 eng_barrier :1;
> + __u32 reg_barrier :1; /* must be 1 */
> + __u32 msg_barrier :1;
> + };
> + __u32 ctl;
> + };
> +};
> +
> +struct packet_wreg_bulk {
> + __u32 size64 :16;
> + __u32:16;
> + __u32 reg_offset :16;
> + __u32:8;
> + __u32 opcode :5;
> + __u32 eng_barrier :1;
> + __u32 reg_barrier :1; /* must be 1 */
> + __u32 msg_barrier :1;
> + __u64 values[0]; /* data starts here */
> +};
> +
> +struct packet_msg_long {
> + __u32 value;
> + union {
> + struct {
> + __u32:16;
> + __u32 weakly_ordered :1;
> + __u32 no_snoop :1;
> + __u32:2;
> + __u32 op :2; /* 0: write <value>. 1: write timestamp. */
> + __u32:2;
> + __u32 opcode :5;
> + __u32 eng_barrier :1;
> + __u32 reg_barrier :1;
> + __u32 msg_barrier :1;
> + };
> + __u32 ctl;
> + };
> + __u64 addr;
> +};
> +
> +struct packet_msg_short {
> + union {
> + struct {
> + __u32 sync_id :10;
> + __u32:5;
> + __u32 mode : 1;
> + __u32 sync_value :16;
> + } mon_arm_register;
> + struct {
> + __u32 sync_value :16;
> + __u32:15;
> + __u32 mode :1;
> + } so_upd;
> + __u32 value;
> + };
> + union {
> + struct {
> + __u32 msg_addr_offset :16;
> + __u32 weakly_ordered :1;
> + __u32 no_snoop :1;
> + __u32:2;
> + __u32 op :2;
> + __u32 base :2;
> + __u32 opcode :5;
> + __u32 eng_barrier :1;
> + __u32 reg_barrier :1;
> + __u32 msg_barrier :1;
> + };
> + __u32 ctl;
> + };
> +};
> +
> +struct packet_msg_prot {
> + __u32 value;
> + union {
> + struct {
> + __u32:16;
> + __u32 weakly_ordered :1;
> + __u32 no_snoop :1;
> + __u32:2;
> + __u32 op :2; /* 0: write <value>. 1: write timestamp. */
> + __u32:2;
> + __u32 opcode :5;
> + __u32 eng_barrier :1;
> + __u32 reg_barrier :1;
> + __u32 msg_barrier :1;
> + };
> + __u32 ctl;
> + };
> + __u64 addr;
> +};
> +
> +struct packet_fence {
> + __u32 dec_val :4;
> + __u32:12;
> + __u32 gate_val :8;
> + __u32:6;
> + __u32 id :2;
> + __u32:24;
> + __u32 opcode :5;
> + __u32 eng_barrier :1;
> + __u32 reg_barrier :1;
> + __u32 msg_barrier :1;
> +};
> +
> +struct packet_lin_dma {
> + __u32 tsize;
> + union {
> + struct {
> + __u32 weakly_ordered :1; /* H/W bug, must be 1 */
> + __u32 rdcomp :1;
> + __u32 wrcomp :1;
> + __u32 no_snoop :1;
> + __u32 src_disable :1;
> + __u32 dst_disable :1;
> + __u32 memset_mode :1;
> + __u32 tensor_dma :1; /* N/A, must be 0 */
> + __u32 cntrl :12;
> + __u32 dma_dir :3; /* S/W only, no effect on HW */
> + __u32:1;
> + __u32 opcode :5;
> + __u32 eng_barrier :1;
> + __u32 reg_barrier :1; /* must be 1 */
> + __u32 msg_barrier :1;
> + };
> + __u32 ctl;
> + };
> + __u64 src_addr;
> + __u64 dst_addr;
> +};
> +
> +struct packet_cp_dma {
> + __u32 tsize;
> + union {
> + struct {
> + __u32 weakly_ordered :1;
> + __u32 no_snoop :1;
> + __u32:22;
> + __u32 opcode :5;
> + __u32 eng_barrier :1;
> + __u32 reg_barrier :1; /* must be 1 */
> + __u32 msg_barrier :1;
> + };
> + __u32 ctl;
> + };
> + __u64 src_addr;
> +};
> +
> +#endif /* GOYA_PACKETS_H */
> diff --git a/drivers/misc/habanalabs/include/habanalabs_device_if.h b/drivers/misc/habanalabs/include/habanalabs_device_if.h
> index 9dbb7077eabd..62df9981f68a 100644
> --- a/drivers/misc/habanalabs/include/habanalabs_device_if.h
> +++ b/drivers/misc/habanalabs/include/habanalabs_device_if.h
> @@ -97,6 +97,278 @@ enum pq_init_status {
> PQ_INIT_STATUS_READY_FOR_HOST
> };
>
> +/*
> + * ArmCP Primary Queue Packets
> + *
> + * During normal operation, KMD needs to send various messages to ArmCP,
> + * usually either to SET some value into a H/W periphery or to GET the current
> + * value of some H/W periphery. For example, SET the frequency of MME/TPC and
> + * GET the value of the thermal sensor.
> + *
> + * These messages can be initiated either by the User application or by KMD
> + * itself, e.g. power management code. In either case, the communication from
> + * KMD to ArmCP will *always* be in synchronous mode, meaning that KMD will
> + * send a single message and poll until the message was acknowledged and the
> + * results are ready (if results are needed).
> + *
> + * This means that only a single message can be sent at a time and KMD must
> + * wait for its result before sending the next message. Having said that,
> + * because these are control messages which are sent in a relatively low
> + * frequency, this limitation seems acceptable. It's important to note that
> + * in case of multiple devices, messages to different devices *can* be sent
> + * at the same time.
> + *
> + * The message, inputs/outputs (if relevant) and fence object will be located
> + * on the device DDR at an address that will be determined by KMD. During
> + * device initialization phase, KMD will pass to ArmCP that address. Most of
> + * the message types will contain inputs/outputs inside the message itself.
> + * The common part of each message will contain the opcode of the message (its
> + * type) and a field representing a fence object.
> + *
> + * When KMD wishes to send a message to ArmCP, it will write the message
> + * contents to the device DDR, clear the fence object and then write the
> + * value 484 to the mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR register to issue
> + * the 484 interrupt-id to the ARM core.
> + *
> + * Upon receiving the 484 interrupt-id, ArmCP will read the message from the
> + * DDR. In case the message is a SET operation, ArmCP will first perform the
> + * operation and then write to the fence object on the device DDR. In case the
> + * message is a GET operation, ArmCP will first fill the results section on the
> + * device DDR and then write to the fence object. If an error occurred, ArmCP
> + * will fill the rc field with the right error code.
> + *
> + * In the meantime, KMD will poll on the fence object. Once KMD sees that the
> + * fence object is signaled, it will read the results from the device DDR
> + * (if relevant) and resume the code execution in KMD.
> + *
> + * To use QMAN packets, the opcode must be the QMAN opcode, shifted by 8
> + * so the value being put by the KMD matches the value read by ArmCP
> + *
> + * Non-QMAN packets should be limited to values 1 through (2^8 - 1)
> + *
> + * Detailed description:
> + *
> + * ARMCP_PACKET_DISABLE_PCI_ACCESS -
> + * After receiving this packet the embedded CPU must NOT issue PCI
> + * transactions (read/write) towards the Host CPU. This also include
> + * sending MSI-X interrupts.
> + * This packet is usually sent before the device is moved to D3Hot state.
> + *
> + * ARMCP_PACKET_ENABLE_PCI_ACCESS -
> + * After receiving this packet the embedded CPU is allowed to issue PCI
> + * transactions towards the Host CPU, including sending MSI-X interrupts.
> + * This packet is usually send after the device is moved to D0 state.
> + *
> + * ARMCP_PACKET_TEMPERATURE_GET -
> + * Fetch the current temperature / Max / Max Hyst / Critical /
> + * Critical Hyst of a specified thermal sensor. The packet's
> + * arguments specify the desired sensor and the field to get.
> + *
> + * ARMCP_PACKET_VOLTAGE_GET -
> + * Fetch the voltage / Max / Min of a specified sensor. The packet's
> + * arguments specify the sensor and type.
> + *
> + * ARMCP_PACKET_CURRENT_GET -
> + * Fetch the current / Max / Min of a specified sensor. The packet's
> + * arguments specify the sensor and type.
> + *
> + * ARMCP_PACKET_FAN_SPEED_GET -
> + * Fetch the speed / Max / Min of a specified fan. The packet's
> + * arguments specify the sensor and type.
> + *
> + * ARMCP_PACKET_PWM_GET -
> + * Fetch the pwm value / mode of a specified pwm. The packet's
> + * arguments specify the sensor and type.
> + *
> + * ARMCP_PACKET_PWM_SET -
> + * Set the pwm value / mode of a specified pwm. The packet's
> + * arguments specify the sensor, type and value.
> + *
> + * ARMCP_PACKET_FREQUENCY_SET -
> + * Set the frequency of a specified PLL. The packet's arguments specify
> + * the PLL and the desired frequency. The actual frequency in the device
> + * might differ from the requested frequency.
> + *
> + * ARMCP_PACKET_FREQUENCY_GET -
> + * Fetch the frequency of a specified PLL. The packet's arguments specify
> + * the PLL.
> + *
> + * ARMCP_PACKET_LED_SET -
> + * Set the state of a specified led. The packet's arguments
> + * specify the led and the desired state.
> + *
> + * ARMCP_PACKET_I2C_WR -
> + * Write 32-bit value to I2C device. The packet's arguments specify the
> + * I2C bus, address and value.
> + *
> + * ARMCP_PACKET_I2C_RD -
> + * Read 32-bit value from I2C device. The packet's arguments specify the
> + * I2C bus and address.
> + *
> + * ARMCP_PACKET_INFO_GET -
> + * Fetch information from the device as specified in the packet's
> + * structure. KMD passes the max size it allows the ArmCP to write to
> + * the structure, to prevent data corruption in case of mismatched
> + * KMD/FW versions.
> + *
> + * ARMCP_PACKET_FLASH_PROGRAM_REMOVED - this packet was removed
> + *
> + * ARMCP_PACKET_UNMASK_RAZWI_IRQ -
> + * Unmask the given IRQ. The IRQ number is specified in the value field.
> + * The packet is sent after receiving an interrupt and printing its
> + * relevant information.
> + *
> + * ARMCP_PACKET_UNMASK_RAZWI_IRQ_ARRAY -
> + * Unmask the given IRQs. The IRQs numbers are specified in an array right
> + * after the armcp_packet structure, where its first element is the array
> + * length. The packet is sent after a soft reset was done in order to
> + * handle any interrupts that were sent during the reset process.
> + *
> + * ARMCP_PACKET_TEST -
> + * Test packet for ArmCP connectivity. The CPU will put the fence value
> + * in the result field.
> + *
> + * ARMCP_PACKET_FREQUENCY_CURR_GET -
> + * Fetch the current frequency of a specified PLL. The packet's arguments
> + * specify the PLL.
> + *
> + * ARMCP_PACKET_MAX_POWER_GET -
> + * Fetch the maximal power of the device.
> + *
> + * ARMCP_PACKET_MAX_POWER_SET -
> + * Set the maximal power of the device. The packet's arguments specify
> + * the power.
> + *
> + * ARMCP_PACKET_EEPROM_DATA_GET -
> + * Get EEPROM data from the ArmCP kernel. The buffer is specified in the
> + * addr field. The CPU will put the returned data size in the result
> + * field. In addition, KMD passes the max size it allows the ArmCP to
> + * write to the structure, to prevent data corruption in case of
> + * mismatched KMD/FW versions.
> + *
> + */
> +
> +enum armcp_packet_id {
> + ARMCP_PACKET_DISABLE_PCI_ACCESS = 1, /* internal */
> + ARMCP_PACKET_ENABLE_PCI_ACCESS, /* internal */
> + ARMCP_PACKET_TEMPERATURE_GET, /* sysfs */
> + ARMCP_PACKET_VOLTAGE_GET, /* sysfs */
> + ARMCP_PACKET_CURRENT_GET, /* sysfs */
> + ARMCP_PACKET_FAN_SPEED_GET, /* sysfs */
> + ARMCP_PACKET_PWM_GET, /* sysfs */
> + ARMCP_PACKET_PWM_SET, /* sysfs */
> + ARMCP_PACKET_FREQUENCY_SET, /* sysfs */
> + ARMCP_PACKET_FREQUENCY_GET, /* sysfs */
> + ARMCP_PACKET_LED_SET, /* debugfs */
> + ARMCP_PACKET_I2C_WR, /* debugfs */
> + ARMCP_PACKET_I2C_RD, /* debugfs */
> + ARMCP_PACKET_INFO_GET, /* IOCTL */
> + ARMCP_PACKET_FLASH_PROGRAM_REMOVED,
> + ARMCP_PACKET_UNMASK_RAZWI_IRQ, /* internal */
> + ARMCP_PACKET_UNMASK_RAZWI_IRQ_ARRAY, /* internal */
> + ARMCP_PACKET_TEST, /* internal */
> + ARMCP_PACKET_FREQUENCY_CURR_GET, /* sysfs */
> + ARMCP_PACKET_MAX_POWER_GET, /* sysfs */
> + ARMCP_PACKET_MAX_POWER_SET, /* sysfs */
> + ARMCP_PACKET_EEPROM_DATA_GET, /* sysfs */
> +};
> +
> +#define ARMCP_PACKET_FENCE_VAL 0xFE8CE7A5
> +
> +struct armcp_packet {
> + union {
> + __u64 value; /* For SET packets */
> + __u64 result; /* For GET packets */
> + __u64 addr; /* For PQ */
> + };
> +
> + union {
> + struct {
> + __u32:12;
> + __u32 rc :4;
> + __u32 opcode :13;
> + __u32 eng_barrier :1;
> + __u32 reg_barrier :1;
> + __u32 msg_barrier :1;
> + };
> + __u32 ctl;
> + };
> +
> + __u32 fence; /* Signal to KMD that message is completed */
> +
> + union {
> + struct {/* For temperature/current/voltage/fan/pwm get/set */
> + __u16 sensor_index;
> + __u16 type;
> + };
> +
> + struct { /* For I2C read/write */
> + __u8 i2c_bus;
> + __u8 i2c_addr;
> + __u8 i2c_reg;
> + __u8 pad; /* unused */
> + };
> +
> + /* For frequency get/set */
> + __u32 pll_index;
> +
> + /* For led set */
> + __u32 led_index;
> +
> + /* For get Armcp info/EEPROM data */
> + __u32 data_max_size;
> + };
> +};
> +
> +struct armcp_unmask_irq_arr_packet {
> + struct armcp_packet armcp_pkt;
> + __u32 length;
> + __u32 irqs[0];
> +};
> +
> +enum armcp_packet_rc {
> + armcp_packet_success,
> + armcp_packet_invalid,
> + armcp_packet_fault
> +};
> +
> +enum armcp_temp_type {
> + armcp_temp_input,
> + armcp_temp_max = 6,
> + armcp_temp_max_hyst,
> + armcp_temp_crit,
> + armcp_temp_crit_hyst
> +};
> +
> +enum armcp_in_attributes {
> + armcp_in_input,
> + armcp_in_min,
> + armcp_in_max
> +};
> +
> +enum armcp_curr_attributes {
> + armcp_curr_input,
> + armcp_curr_min,
> + armcp_curr_max
> +};
> +
> +enum armcp_fan_attributes {
> + armcp_fan_input,
> + armcp_fan_min = 2,
> + armcp_fan_max
> +};
> +
> +enum armcp_pwm_attributes {
> + armcp_pwm_input,
> + armcp_pwm_enable
> +};
> +
> +/* Event Queue Packets */
> +
> +struct eq_generic_event {
> + __u64 data[7];
> +};
> +
> /*
> * ArmCP info
> */
> diff --git a/drivers/misc/habanalabs/irq.c b/drivers/misc/habanalabs/irq.c
> new file mode 100644
> index 000000000000..97b0de7ea5c2
> --- /dev/null
> +++ b/drivers/misc/habanalabs/irq.c
> @@ -0,0 +1,150 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * Copyright 2016-2018 HabanaLabs, Ltd.
> + * All Rights Reserved.
> + */
> +
> +#include "habanalabs.h"
> +
> +#include <linux/dma-mapping.h>
> +
> +
> +/**
> + * 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_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;
> + struct hl_hw_queue *queue;
> + struct hl_cs_job *job;
> + bool shadow_index_valid;
> + u16 shadow_index;
> + u32 *cq_entry;
> + u32 *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 = (u32 *) cq->kernel_address;
> +
> + while (1) {
> + bool entry_ready = ((cq_base[cq->ci] & CQ_ENTRY_READY_MASK)
> + >> CQ_ENTRY_READY_SHIFT);
> +
> + if (!entry_ready)
> + break;
> +
> + cq_entry = (u32 *) &cq_base[cq->ci];
> +
> + /*
> + * Make sure we read CQ entry contents after we've
> + * checked the ownership bit.
> + */
> + dma_rmb();
> +
> + shadow_index_valid =
> + ((*cq_entry & CQ_ENTRY_SHADOW_INDEX_VALID_MASK)
> + >> CQ_ENTRY_SHADOW_INDEX_VALID_SHIFT);
> +
> + shadow_index = (u16)
> + ((*cq_entry & CQ_ENTRY_SHADOW_INDEX_MASK)
> + >> CQ_ENTRY_SHADOW_INDEX_SHIFT);
> +
> + queue = &hdev->kernel_queues[cq->hw_queue_id];
> +
> + if ((shadow_index_valid) && (!hdev->disabled)) {
> + job = queue->shadow_queue[hl_pi_2_offset(shadow_index)];
> + queue_work(hdev->cq_wq, &job->finish_work);
> + }
> +
> + /*
> + * Update ci of the context's queue. There is no
> + * need to protect it with spinlock because this update is
> + * done only inside IRQ and there is a different IRQ per
> + * queue
> + */
> + queue->ci = hl_queue_inc_ptr(queue->ci);
> +
> + /* Clear CQ entry ready bit */
> + cq_base[cq->ci] &= ~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_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
> + *
> + * 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;
> +
> + BUILD_BUG_ON(HL_CQ_SIZE_IN_BYTES > HL_PAGE_SIZE);
> +
> + p = hdev->asic_funcs->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 = (u64) 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)
> +{
> + hdev->asic_funcs->dma_free_coherent(hdev, HL_CQ_SIZE_IN_BYTES,
> + (void *) q->kernel_address, q->bus_address);
> +}
> --
> 2.17.1
>
--
Sincerely yours,
Mike.