msm: npu: Add NPU driver support for kernel 6.1

NPU driver snapshot from msm-5.15 branch
commit f5053f87777d ("msm: npu: Fix OOB issue in
IPC between driver and firmware").

Change-Id: Iea68b912dd7efd9a979969a91fb38d3611e3ff8c
Signed-off-by: Priyanka G Pai <quic_pgpai@quicinc.com>
This commit is contained in:
Priyanka G Pai 2024-08-08 19:30:59 +05:30 committed by Priyanka Pai
parent 9cfdeb74ee
commit 1a6163e69c
15 changed files with 7185 additions and 0 deletions

View File

@ -7,3 +7,14 @@ config VIRTIO_NPU
which provides acceleration for neural network processing.
This driver is based on virtio.
Say Y if you want to support virtual NPU.
config MSM_NPU
tristate "QTI MSM Neural Processing Unit support"
depends on ARCH_QCOM
help
Enable support for Neural Processing Unit
for specific QTI chipsets.
This module serves as the common driver
for npu which provides acceleration for neural
network processing.

View File

@ -1,3 +1,14 @@
# SPDX-License-Identifier: GPL-2.0-only
ifneq ($(CONFIG_VIRTIO_NPU),)
obj-$(CONFIG_VIRTIO_NPU) := virtio_npu.o
else
msm_npu-objs := npu_dbg.o \
npu_dev.o \
npu_debugfs.o \
npu_host_ipc.o \
npu_hw_access.o \
npu_mgr.o
obj-$(CONFIG_MSM_NPU) := msm_npu.o
endif

View File

@ -0,0 +1,277 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _NPU_COMMON_H
#define _NPU_COMMON_H
/* -------------------------------------------------------------------------
* Includes
* -------------------------------------------------------------------------
*/
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/mailbox_controller.h>
#include <linux/mailbox_client.h>
#include <linux/module.h>
#include <linux/msm_npu.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/mailbox/qmp.h>
#include "npu_mgr.h"
/* -------------------------------------------------------------------------
* Defines
* -------------------------------------------------------------------------
*/
#define NPU_MAX_MBOX_NUM 2
#define NPU_MBOX_LOW_PRI 0
#define NPU_MBOX_HIGH_PRI 1
#define DEFAULT_REG_DUMP_NUM 64
#define ROW_BYTES 16
#define GROUP_BYTES 4
#define NUM_MAX_CLK_NUM 24
#define NPU_MAX_REGULATOR_NUM 2
#define NPU_MAX_DT_NAME_LEN 21
#define NPU_MAX_PWRLEVELS 8
#define NPU_MAX_STATS_BUF_SIZE 16384
#define NPU_MAX_PATCH_NUM 160
#define PERF_MODE_DEFAULT 0
enum npu_power_level {
NPU_PWRLEVEL_MINSVS = 0,
NPU_PWRLEVEL_LOWSVS,
NPU_PWRLEVEL_SVS,
NPU_PWRLEVEL_SVS_L1,
NPU_PWRLEVEL_NOM,
NPU_PWRLEVEL_NOM_L1,
NPU_PWRLEVEL_TURBO,
NPU_PWRLEVEL_TURBO_L1,
NPU_PWRLEVEL_OFF = 0xFFFFFFFF,
};
/* -------------------------------------------------------------------------
* Data Structures
* -------------------------------------------------------------------------
*/
struct npu_smmu_ctx {
int domain;
struct dma_iommu_mapping *mmu_mapping;
struct reg_bus_client *reg_bus_clt;
int32_t attach_cnt;
};
struct npu_ion_buf {
int fd;
struct dma_buf *dma_buf;
struct dma_buf_attachment *attachment;
struct sg_table *table;
dma_addr_t iova;
uint32_t size;
void *phys_addr;
void *buf;
struct list_head list;
};
struct npu_clk {
struct clk *clk;
char clk_name[NPU_MAX_DT_NAME_LEN];
};
struct npu_regulator {
struct regulator *regulator;
char regulator_name[NPU_MAX_DT_NAME_LEN];
};
struct npu_debugfs_ctx {
struct dentry *root;
uint32_t reg_off;
uint32_t reg_cnt;
};
struct npu_debugfs_reg_ctx {
char *buf;
size_t buf_len;
struct npu_device *npu_dev;
};
struct npu_mbox {
struct mbox_client client;
struct mbox_chan *chan;
struct npu_device *npu_dev;
uint32_t id;
};
/**
* struct npu_pwrlevel - Struct holding different pwrlevel info obtained
* from dtsi file
* @pwr_level: NPU power level
* @freq[]: NPU frequency vote in Hz
*/
struct npu_pwrlevel {
uint32_t pwr_level;
long clk_freq[NUM_MAX_CLK_NUM];
};
/*
* struct npu_reg - Struct holding npu register information
* @ off - register offset
* @ val - register value
* @ valid - if register value is valid
*/
struct npu_reg {
uint32_t off;
uint32_t val;
bool valid;
};
/**
* struct npu_pwrctrl - Power control settings for a NPU device
* @pwr_vote_num - voting information for power enable
* @pwrlevels - List of supported power levels
* @active_pwrlevel - The currently active power level
* @default_pwrlevel - device wake up power level
* @max_pwrlevel - maximum allowable powerlevel per the user
* @min_pwrlevel - minimum allowable powerlevel per the user
* @num_pwrlevels - number of available power levels
* @cdsprm_pwrlevel - maximum power level from cdsprm
* @fmax_pwrlevel - maximum power level from qfprom fmax setting
* @uc_pwrlevel - power level from user driver setting
* @perf_mode_override - perf mode from sysfs to override perf mode
* settings from user driver
* @dcvs_mode - dcvs mode from sysfs to turn on dcvs mode
* settings from user driver
* @devbw - bw device
*/
struct npu_pwrctrl {
int32_t pwr_vote_num;
struct npu_pwrlevel pwrlevels[NPU_MAX_PWRLEVELS];
uint32_t active_pwrlevel;
uint32_t default_pwrlevel;
uint32_t max_pwrlevel;
uint32_t min_pwrlevel;
uint32_t num_pwrlevels;
struct device *devbw;
uint32_t bwmon_enabled;
uint32_t uc_pwrlevel;
uint32_t cdsprm_pwrlevel;
uint32_t fmax_pwrlevel;
uint32_t perf_mode_override;
uint32_t dcvs_mode;
uint32_t cur_dcvs_activity;
};
/**
* struct npu_thermalctrl - Thermal control settings for a NPU device
* @max_state - maximum thermal mitigation state
* @current_state - current thermal mitigation state
* @pwr_level -power level that thermal control requested
*/
struct npu_thermalctrl {
unsigned long max_state;
unsigned long current_state;
uint32_t pwr_level;
};
#define NPU_MAX_IRQ 3
struct npu_irq {
char *name;
int irq;
int irq_type;
};
struct npu_io_data {
size_t size;
void __iomem *base;
};
struct npu_fw_io_data {
phys_addr_t mem_phys;
phys_addr_t mem_reloc;
void *mem_region;
size_t mem_size;
};
struct npu_device {
struct mutex dev_lock;
struct platform_device *pdev;
dev_t dev_num;
struct cdev cdev;
struct class *class;
struct device *device;
struct npu_io_data core_io;
struct npu_io_data tcm_io;
struct npu_io_data bwmon_io;
struct npu_io_data qfprom_io;
struct npu_fw_io_data fw_io;
uint32_t core_clk_num;
struct npu_clk core_clks[NUM_MAX_CLK_NUM];
uint32_t regulator_num;
struct npu_regulator regulators[NPU_MAX_DT_NAME_LEN];
struct npu_irq irq[NPU_MAX_IRQ];
struct device *cb_device;
struct npu_host_ctx host_ctx;
struct npu_smmu_ctx smmu_ctx;
struct npu_debugfs_ctx debugfs_ctx;
struct npu_mbox mbox_aop;
struct thermal_cooling_device *tcdev;
struct npu_pwrctrl pwrctrl;
struct npu_thermalctrl thermalctrl;
struct llcc_slice_desc *sys_cache;
uint32_t execute_v2_flag;
bool cxlimit_registered;
struct icc_path *icc_npu_cdspmem;
struct icc_path *icc_cpu_imemcfg;
uint32_t hw_version;
};
struct npu_client {
struct npu_device *npu_dev;
struct mutex list_lock;
struct list_head mapped_buffer_list;
};
/* -------------------------------------------------------------------------
* Function Prototypes
* -------------------------------------------------------------------------
*/
int npu_debugfs_init(struct npu_device *npu_dev);
void npu_debugfs_deinit(struct npu_device *npu_dev);
int npu_enable_core_power(struct npu_device *npu_dev);
void npu_disable_core_power(struct npu_device *npu_dev);
int npu_enable_post_pil_clocks(struct npu_device *npu_dev);
void npu_disable_post_pil_clocks(struct npu_device *npu_dev);
irqreturn_t npu_intr_hdler(int irq, void *ptr);
int npu_set_uc_power_level(struct npu_device *npu_dev,
uint32_t pwr_level);
int fw_init(struct npu_device *npu_dev);
void fw_deinit(struct npu_device *npu_dev, bool ssr, bool fw_alive);
int npu_notify_cdsprm_cxlimit_activity(struct npu_device *npu_dev, bool enable);
#endif /* _NPU_COMMON_H */

View File

@ -0,0 +1,33 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
/* -------------------------------------------------------------------------
* Includes
* -------------------------------------------------------------------------
*/
#include "npu_common.h"
#include "npu_firmware.h"
#include "npu_hw.h"
#include "npu_hw_access.h"
#include "npu_mgr.h"
/* -------------------------------------------------------------------------
* Function Definitions - Debug
* -------------------------------------------------------------------------
*/
void npu_dump_debug_timeout_stats(struct npu_device *npu_dev)
{
uint32_t reg_val;
reg_val = REGR(npu_dev, REG_FW_JOB_CNT_START);
pr_info("fw jobs execute started count = %d\n", reg_val);
reg_val = REGR(npu_dev, REG_FW_JOB_CNT_END);
pr_info("fw jobs execute finished count = %d\n", reg_val);
reg_val = REGR(npu_dev, REG_NPU_FW_DEBUG_DATA);
pr_info("fw jobs aco parser debug = %d\n", reg_val);
}

View File

@ -0,0 +1,184 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
/* -------------------------------------------------------------------------
* Includes
* -------------------------------------------------------------------------
*/
#include <linux/debugfs.h>
#include "npu_hw.h"
#include "npu_hw_access.h"
#include "npu_common.h"
/* -------------------------------------------------------------------------
* Defines
* -------------------------------------------------------------------------
*/
#define NPU_LOG_BUF_SIZE 4096
/* -------------------------------------------------------------------------
* Function Prototypes
* -------------------------------------------------------------------------
*/
static int npu_debug_open(struct inode *inode, struct file *file);
static int npu_debug_release(struct inode *inode, struct file *file);
static ssize_t npu_debug_ctrl_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos);
/* -------------------------------------------------------------------------
* Variables
* -------------------------------------------------------------------------
*/
static struct npu_device *g_npu_dev;
static const struct file_operations npu_ctrl_fops = {
.open = npu_debug_open,
.release = npu_debug_release,
.read = NULL,
.write = npu_debug_ctrl_write,
};
/* -------------------------------------------------------------------------
* Function Implementations
* -------------------------------------------------------------------------
*/
static int npu_debug_open(struct inode *inode, struct file *file)
{
/* non-seekable */
file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
file->private_data = inode->i_private;
return 0;
}
static int npu_debug_release(struct inode *inode, struct file *file)
{
return 0;
}
/* -------------------------------------------------------------------------
* Function Implementations - DebugFS Control
* -------------------------------------------------------------------------
*/
static ssize_t npu_debug_ctrl_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
char buf[24];
struct npu_device *npu_dev = file->private_data;
struct npu_debugfs_ctx *debugfs;
int32_t rc = 0;
uint32_t val;
pr_debug("npu_dev %pK %pK\n", npu_dev, g_npu_dev);
npu_dev = g_npu_dev;
debugfs = &npu_dev->debugfs_ctx;
if (count >= sizeof(buf))
return -EINVAL;
if (copy_from_user(buf, user_buf, count))
return -EFAULT;
buf[count] = 0; /* end of string */
if (count >= 2)
buf[count-1] = 0;/* remove line feed */
if (strcmp(buf, "on") == 0) {
pr_info("triggering fw_init\n");
if (fw_init(npu_dev) != 0)
pr_info("error in fw_init\n");
} else if (strcmp(buf, "off") == 0) {
pr_info("triggering fw_deinit\n");
fw_deinit(npu_dev, false, true);
} else if (strcmp(buf, "ssr") == 0) {
pr_info("trigger error irq\n");
if (npu_enable_core_power(npu_dev))
return -EPERM;
REGW(npu_dev, NPU_MASTERn_ERROR_IRQ_SET(1), 2);
REGW(npu_dev, NPU_MASTERn_ERROR_IRQ_SET(0), 2);
npu_disable_core_power(npu_dev);
} else if (strcmp(buf, "ssr_wdt") == 0) {
pr_info("trigger wdt irq\n");
npu_disable_post_pil_clocks(npu_dev);
} else if (strcmp(buf, "loopback") == 0) {
pr_debug("loopback test\n");
rc = npu_host_loopback_test(npu_dev);
pr_debug("loopback test end: %d\n", rc);
} else {
rc = kstrtou32(buf, 10, &val);
if (rc) {
pr_err("Invalid input for power level settings\n");
} else {
val = min(val, npu_dev->pwrctrl.max_pwrlevel);
npu_dev->pwrctrl.active_pwrlevel = val;
pr_info("setting power state to %d\n", val);
}
}
return count;
}
/* -------------------------------------------------------------------------
* Function Implementations - DebugFS
* -------------------------------------------------------------------------
*/
int npu_debugfs_init(struct npu_device *npu_dev)
{
struct npu_debugfs_ctx *debugfs = &npu_dev->debugfs_ctx;
struct npu_host_ctx *host_ctx = &npu_dev->host_ctx;
struct npu_pwrctrl *pwr = &npu_dev->pwrctrl;
g_npu_dev = npu_dev;
debugfs->root = debugfs_create_dir("npu", NULL);
if (IS_ERR_OR_NULL(debugfs->root)) {
pr_err("debugfs_create_dir for npu failed, error %ld\n",
PTR_ERR(debugfs->root));
return -ENODEV;
}
if (!debugfs_create_file("ctrl", 0644, debugfs->root,
npu_dev, &npu_ctrl_fops)) {
pr_err("debugfs_create_file ctrl fail\n");
goto err;
}
debugfs_create_bool("sys_cache_disable", 0644,
debugfs->root, &(host_ctx->sys_cache_disable));
debugfs_create_u32("fw_dbg_mode", 0644,
debugfs->root, &(host_ctx->fw_dbg_mode));
debugfs_create_u32("fw_state", 0444,
debugfs->root, &(host_ctx->fw_state));
debugfs_create_u32("pwr_level", 0444,
debugfs->root, &(pwr->active_pwrlevel));
debugfs_create_u32("exec_flags", 0644,
debugfs->root, &(host_ctx->exec_flags_override));
return 0;
err:
npu_debugfs_deinit(npu_dev);
return -ENODEV;
}
void npu_debugfs_deinit(struct npu_device *npu_dev)
{
struct npu_debugfs_ctx *debugfs = &npu_dev->debugfs_ctx;
if (!IS_ERR_OR_NULL(debugfs->root)) {
debugfs_remove_recursive(debugfs->root);
debugfs->root = NULL;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,176 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _NPU_FIRMWARE_H
#define _NPU_FIRMWARE_H
/* -------------------------------------------------------------------------
* Includes
* -------------------------------------------------------------------------
*/
#include <linux/types.h>
/* -------------------------------------------------------------------------
* Defines
* -------------------------------------------------------------------------
*/
/* NPU Firmware Control/Status Register, written by FW and read HOST */
#define REG_NPU_FW_CTRL_STATUS NPU_GPR0
/* written by HOST and read by FW for control */
#define REG_NPU_HOST_CTRL_STATUS NPU_GPR1
/* Data value for control */
#define REG_NPU_HOST_CTRL_VALUE NPU_GPR2
/* Simulates an interrupt for FW->HOST, used for pre-silicon */
#define REG_FW_TO_HOST_EVENT NPU_GPR3
/* Read/Written by both host and dsp for sync between driver and dsp */
#define REG_HOST_DSP_CTRL_STATUS NPU_GPR4
/* Data value for debug */
#define REG_NPU_FW_DEBUG_DATA NPU_GPR13
/* Started job count */
#define REG_FW_JOB_CNT_START NPU_GPR14
/* Finished job count */
#define REG_FW_JOB_CNT_END NPU_GPR15
/* NPU FW Control/Status Register */
/* bit fields definitions in CTRL STATUS REG */
#define FW_CTRL_STATUS_IPC_READY_BIT 0
#define FW_CTRL_STATUS_LOG_READY_BIT 1
#define FW_CTRL_STATUS_EXECUTE_THREAD_READY_BIT 2
#define FW_CTRL_STATUS_MAIN_THREAD_READY_BIT 3
#define FW_CTRL_STATUS_LOADED_ACO_BIT 4
#define FW_CTRL_STATUS_EXECUTING_ACO_BIT 5
#define FW_CTRL_STATUS_SHUTDOWN_DONE_BIT 12
#define FW_CTRL_STATUS_STACK_CORRUPT_BIT 13
/* 32 bit values of the bit fields above */
#define FW_CTRL_STATUS_IPC_READY_VAL (1 << FW_CTRL_STATUS_IPC_READY_BIT)
#define FW_CTRL_STATUS_LOG_READY_VAL (1 << FW_CTRL_STATUS_LOG_READY_BIT)
#define FW_CTRL_STATUS_EXECUTE_THREAD_READY_VAL \
(1 << FW_CTRL_STATUS_EXECUTE_THREAD_READY_BIT)
#define FW_CTRL_STATUS_MAIN_THREAD_READY_VAL \
(1 << FW_CTRL_STATUS_MAIN_THREAD_READY_BIT)
#define FW_CTRL_STATUS_LOADED_ACO_VAL \
(1 << FW_CTRL_STATUS_LOADED_ACO_BIT)
#define FW_CTRL_STATUS_EXECUTING_ACO_VAL \
(1 << FW_CTRL_STATUS_EXECUTING_ACO_BIT)
#define FW_CTRL_STATUS_SHUTDOWN_DONE_VAL \
(1 << FW_CTRL_STATUS_SHUTDOWN_DONE_BIT)
#define FW_CTRL_STATUS_STACK_CORRUPT_VAL \
(1 << FW_CTRL_STATUS_STACK_CORRUPT_BIT)
/* NPU HOST Control/Status Register */
/* bit fields definitions in CTRL STATUS REG */
/* Host has programmed IPC address into the REG_NPU_HOST_CTRL_VALUE register */
#define HOST_CTRL_STATUS_IPC_ADDRESS_READY_BIT 0
/* Host has enabled logging during boot */
#define HOST_CTRL_STATUS_BOOT_ENABLE_LOGGING_BIT 1
/* Host has enabled the clk gating of CAL during boot */
#define HOST_CTRL_STATUS_BOOT_ENABLE_CLK_GATE_BIT 2
/* Host requests to pause fw during boot up */
#define HOST_CTRL_STATUS_FW_PAUSE 3
/* Host requests to disable watchdog */
#define HOST_CTRL_STATUS_DISABLE_WDOG_BIT 4
/* 32 bit values of the bit fields above */
#define HOST_CTRL_STATUS_IPC_ADDRESS_READY_VAL \
(1 << HOST_CTRL_STATUS_IPC_ADDRESS_READY_BIT)
#define HOST_CTRL_STATUS_BOOT_ENABLE_LOGGING_VAL \
(1 << HOST_CTRL_STATUS_BOOT_ENABLE_LOGGING_BIT)
#define HOST_CTRL_STATUS_BOOT_ENABLE_CLK_GATE_VAL \
(1 << HOST_CTRL_STATUS_BOOT_ENABLE_CLK_GATE_BIT)
#define HOST_CTRL_STATUS_FW_PAUSE_VAL \
(1 << HOST_CTRL_STATUS_FW_PAUSE)
#define HOST_CTRL_STATUS_DISABLE_WDOG_VAL \
(1 << HOST_CTRL_STATUS_DISABLE_WDOG_BIT)
/* NPU HOST DSP Control/Status Register */
/* notification of power up */
/* following bits are set by host and read by dsp */
#define HOST_DSP_CTRL_STATUS_PWR_UP_BIT 0
/* notification of power dwn */
#define HOST_DSP_CTRL_STATUS_PWR_DWN_BIT 1
/* following bits are set by dsp and read by host */
/* notification of power up acknowlegement*/
#define HOST_DSP_CTRL_STATUS_PWR_UP_ACK_BIT 4
/* notification of power down acknowlegement*/
#define HOST_DSP_CTRL_STATUS_PWR_DWN_ACK_BIT 5
/* 32 bit values of the bit fields above */
#define HOST_DSP_CTRL_STATUS_PWR_UP_VAL \
(1 << HOST_DSP_CTRL_STATUS_PWR_UP_BIT)
#define HOST_DSP_CTRL_STATUS_PWR_DWN_VAL \
(1 << HOST_DSP_CTRL_STATUS_PWR_DWN_BIT)
#define HOST_DSP_CTRL_STATUS_PWR_UP_ACK_VAL \
(1 << HOST_DSP_CTRL_STATUS_PWR_UP_ACK_BIT)
#define HOST_DSP_CTRL_STATUS_PWR_DWN_ACK_VAL \
(1 << HOST_DSP_CTRL_STATUS_PWR_DWN_ACK_BIT)
/* Queue table header definition */
struct hfi_queue_tbl_header {
uint32_t qtbl_version; /* queue table version number */
uint32_t qtbl_size; /* total tables+queues size in bytes */
uint32_t qtbl_qhdr0_offset; /* offset of the 1st queue header entry */
uint32_t qtbl_qhdr_size; /* queue header size */
uint32_t qtbl_num_q; /* total number of queues */
uint32_t qtbl_num_active_q; /* number of active queues */
};
/* Queue header definition */
struct hfi_queue_header {
uint32_t qhdr_status; /* 0 == inactive, 1 == active */
/* 4 byte-aligned start offset from start of q table */
uint32_t qhdr_start_offset;
/* queue type */
uint32_t qhdr_type;
/* in bytes, value of 0 means packets are variable size.*/
uint32_t qhdr_q_size;
/* size of the Queue packet entries, in bytes, 0 means variable size */
uint32_t qhdr_pkt_size;
uint32_t qhdr_pkt_drop_cnt;
/* receiver watermark in # of queue packets */
uint32_t qhdr_rx_wm;
/* transmitter watermark in # of queue packets */
uint32_t qhdr_tx_wm;
/*
* set to request an interrupt from transmitter
* if qhdr_tx_wm is reached
*/
uint32_t qhdr_rx_req;
/*
* set to request an interrupt from receiver
* if qhdr_rx_wm is reached
*/
uint32_t qhdr_tx_req;
uint32_t qhdr_rx_irq_status; /* Not used */
uint32_t qhdr_tx_irq_status; /* Not used */
uint32_t qhdr_read_idx; /* read index in bytes */
uint32_t qhdr_write_idx; /* write index in bytes */
};
/* in bytes */
#define HFI_QUEUE_TABLE_HEADER_SIZE (sizeof(struct hfi_queue_tbl_header))
#define HFI_QUEUE_HEADER_SIZE (sizeof(struct hfi_queue_header))
#define HFI_QUEUE_TABLE_SIZE (HFI_QUEUE_TABLE_HEADER_SIZE + \
(NPU_HFI_NUMBER_OF_QS * HFI_QUEUE_HEADER_SIZE))
/* Queue Indexes */
#define IPC_QUEUE_CMD_HIGH_PRIORITY 0 /* High priority Queue APPS->M0 */
#define IPC_QUEUE_APPS_EXEC 1 /* APPS Execute Queue APPS->M0 */
#define IPC_QUEUE_DSP_EXEC 2 /* DSP Execute Queue DSP->M0 */
#define IPC_QUEUE_APPS_RSP 3 /* APPS Message Queue M0->APPS */
#define IPC_QUEUE_DSP_RSP 4 /* DSP Message Queue DSP->APPS */
#define IPC_QUEUE_LOG 5 /* Log Message Queue M0->APPS */
#define NPU_HFI_NUMBER_OF_QS 6
#define NPU_HFI_NUMBER_OF_ACTIVE_QS 6
#define NPU_HFI_QUEUES_PER_CHANNEL 2
#endif /* _NPU_FIRMWARE_H */

View File

@ -0,0 +1,438 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
/* -------------------------------------------------------------------------
* Includes
* -------------------------------------------------------------------------
*/
#include "npu_hw_access.h"
#include "npu_mgr.h"
#include "npu_firmware.h"
#include "npu_hw.h"
#include "npu_host_ipc.h"
/* -------------------------------------------------------------------------
* Defines
* -------------------------------------------------------------------------
*/
/* HFI IPC interface */
#define TX_HDR_TYPE 0x01000000
#define RX_HDR_TYPE 0x00010000
#define HFI_QTBL_STATUS_ENABLED 0x00000001
#define QUEUE_TBL_VERSION 0x87654321
/* -------------------------------------------------------------------------
* Data Structures
* -------------------------------------------------------------------------
*/
struct npu_queue_tuple {
uint32_t size;
uint32_t hdr;
uint32_t start_offset;
};
static struct npu_queue_tuple npu_q_setup[6] = {
{ 1024, IPC_QUEUE_CMD_HIGH_PRIORITY | TX_HDR_TYPE | RX_HDR_TYPE, 0},
{ 4096, IPC_QUEUE_APPS_EXEC | TX_HDR_TYPE | RX_HDR_TYPE, 0},
{ 4096, IPC_QUEUE_DSP_EXEC | TX_HDR_TYPE | RX_HDR_TYPE, 0},
{ 4096, IPC_QUEUE_APPS_RSP | TX_HDR_TYPE | RX_HDR_TYPE, 0},
{ 4096, IPC_QUEUE_DSP_RSP | TX_HDR_TYPE | RX_HDR_TYPE, 0},
{ 1024, IPC_QUEUE_LOG | TX_HDR_TYPE | RX_HDR_TYPE, 0},
};
/* -------------------------------------------------------------------------
* File Scope Function Prototypes
* -------------------------------------------------------------------------
*/
static int npu_host_ipc_init_hfi(struct npu_device *npu_dev);
static int npu_host_ipc_send_cmd_hfi(struct npu_device *npu_dev,
uint32_t q_idx, void *cmd_ptr);
static int npu_host_ipc_read_msg_hfi(struct npu_device *npu_dev,
uint32_t q_idx, uint32_t *msg_ptr);
static int ipc_queue_read(struct npu_device *npu_dev, uint32_t target_que,
uint8_t *packet, uint8_t *is_tx_req_set);
static int ipc_queue_write(struct npu_device *npu_dev, uint32_t target_que,
uint8_t *packet, uint8_t *is_rx_req_set);
/* -------------------------------------------------------------------------
* Function Definitions
* -------------------------------------------------------------------------
*/
static int npu_host_ipc_init_hfi(struct npu_device *npu_dev)
{
int status = 0;
struct hfi_queue_tbl_header *q_tbl_hdr = NULL;
struct hfi_queue_header *q_hdr_arr = NULL;
struct hfi_queue_header *q_hdr = NULL;
void *q_tbl_addr = NULL;
uint32_t reg_val = 0;
uint32_t q_idx = 0;
uint32_t q_tbl_size = sizeof(struct hfi_queue_tbl_header) +
(NPU_HFI_NUMBER_OF_QS * sizeof(struct hfi_queue_header));
uint32_t q_size = 0;
uint32_t cur_start_offset = 0;
reg_val = REGR(npu_dev, REG_NPU_FW_CTRL_STATUS);
/*
* If the firmware is already running and we're just attaching,
* we do not need to do this
*/
if ((reg_val & FW_CTRL_STATUS_LOG_READY_VAL) != 0)
return status;
/* check for valid interface queue table start address */
q_tbl_addr = kzalloc(q_tbl_size, GFP_KERNEL);
if (q_tbl_addr == NULL)
return -ENOMEM;
/* retrieve interface queue table start address */
q_tbl_hdr = q_tbl_addr;
q_hdr_arr = (struct hfi_queue_header *)((uint8_t *)q_tbl_addr +
sizeof(struct hfi_queue_tbl_header));
/* initialize the interface queue table header */
q_tbl_hdr->qtbl_version = QUEUE_TBL_VERSION;
q_tbl_hdr->qtbl_size = q_tbl_size;
q_tbl_hdr->qtbl_qhdr0_offset = sizeof(struct hfi_queue_tbl_header);
q_tbl_hdr->qtbl_qhdr_size = sizeof(struct hfi_queue_header);
q_tbl_hdr->qtbl_num_q = NPU_HFI_NUMBER_OF_QS;
q_tbl_hdr->qtbl_num_active_q = NPU_HFI_NUMBER_OF_ACTIVE_QS;
cur_start_offset = q_tbl_size;
for (q_idx = IPC_QUEUE_CMD_HIGH_PRIORITY;
q_idx <= IPC_QUEUE_LOG; q_idx++) {
q_hdr = &q_hdr_arr[q_idx];
/* queue is active */
q_hdr->qhdr_status = 0x01;
q_hdr->qhdr_start_offset = cur_start_offset;
npu_q_setup[q_idx].start_offset = cur_start_offset;
q_size = npu_q_setup[q_idx].size;
q_hdr->qhdr_type = npu_q_setup[q_idx].hdr;
/* in bytes */
q_hdr->qhdr_q_size = q_size;
/* variable size packets */
q_hdr->qhdr_pkt_size = 0;
q_hdr->qhdr_pkt_drop_cnt = 0;
q_hdr->qhdr_rx_wm = 0x1;
q_hdr->qhdr_tx_wm = 0x1;
/* since queue is initially empty */
q_hdr->qhdr_rx_req = 0x1;
q_hdr->qhdr_tx_req = 0x0;
/* not used */
q_hdr->qhdr_rx_irq_status = 0;
/* not used */
q_hdr->qhdr_tx_irq_status = 0;
q_hdr->qhdr_read_idx = 0;
q_hdr->qhdr_write_idx = 0;
cur_start_offset += q_size;
}
MEMW(npu_dev, IPC_ADDR, (uint8_t *)q_tbl_hdr, q_tbl_size);
kfree(q_tbl_addr);
/* Write in the NPU's address for where IPC starts */
REGW(npu_dev, (uint32_t)REG_NPU_HOST_CTRL_VALUE,
(uint32_t)IPC_MEM_OFFSET_FROM_SSTCM);
/* Set value bit */
reg_val = REGR(npu_dev, (uint32_t)REG_NPU_HOST_CTRL_STATUS);
REGW(npu_dev, (uint32_t)REG_NPU_HOST_CTRL_STATUS, reg_val |
HOST_CTRL_STATUS_IPC_ADDRESS_READY_VAL);
return status;
}
static int npu_host_ipc_send_cmd_hfi(struct npu_device *npu_dev,
uint32_t q_idx, void *cmd_ptr)
{
int status = 0;
uint8_t is_rx_req_set = 0;
uint32_t retry_cnt = 5;
status = ipc_queue_write(npu_dev, q_idx, (uint8_t *)cmd_ptr,
&is_rx_req_set);
if (status == -ENOSPC) {
do {
msleep(20);
status = ipc_queue_write(npu_dev, q_idx,
(uint8_t *)cmd_ptr, &is_rx_req_set);
} while ((status == -ENOSPC) && (--retry_cnt > 0));
}
if (status == 0) {
if (is_rx_req_set == 1)
status = INTERRUPT_RAISE_NPU(npu_dev);
}
if (status == 0)
pr_debug("Cmd Msg put on Command Queue - SUCCESSS\n");
else
pr_err("Cmd Msg put on Command Queue - FAILURE\n");
return status;
}
static int npu_host_ipc_read_msg_hfi(struct npu_device *npu_dev,
uint32_t q_idx, uint32_t *msg_ptr)
{
int status = 0;
uint8_t is_tx_req_set;
status = ipc_queue_read(npu_dev, q_idx, (uint8_t *)msg_ptr,
&is_tx_req_set);
if (status == 0) {
/* raise interrupt if qhdr_tx_req is set */
if (is_tx_req_set == 1)
status = INTERRUPT_RAISE_NPU(npu_dev);
}
return status;
}
static int ipc_queue_read(struct npu_device *npu_dev,
uint32_t target_que, uint8_t *packet,
uint8_t *is_tx_req_set)
{
int status = 0;
struct hfi_queue_header queue;
uint32_t packet_size, new_read_idx;
size_t read_ptr;
size_t offset = 0;
offset = (size_t)IPC_ADDR + sizeof(struct hfi_queue_tbl_header) +
target_que * sizeof(struct hfi_queue_header);
if ((packet == NULL) || (is_tx_req_set == NULL))
return -EINVAL;
/* Read the queue */
MEMR(npu_dev, (void *)((size_t)offset), (uint8_t *)&queue,
HFI_QUEUE_HEADER_SIZE);
if (queue.qhdr_type != npu_q_setup[target_que].hdr ||
queue.qhdr_q_size != npu_q_setup[target_que].size ||
queue.qhdr_read_idx >= queue.qhdr_q_size ||
queue.qhdr_write_idx >= queue.qhdr_q_size ||
queue.qhdr_start_offset !=
npu_q_setup[target_que].start_offset) {
pr_err("Invalid Queue header\n");
status = -EIO;
goto exit;
}
/* check if queue is empty */
if (queue.qhdr_read_idx == queue.qhdr_write_idx) {
/*
* set qhdr_rx_req, to inform the sender that the Interrupt
* needs to be raised with the next packet queued
*/
queue.qhdr_rx_req = 1;
*is_tx_req_set = 0;
status = -EPERM;
goto exit;
}
read_ptr = ((size_t)(size_t)IPC_ADDR +
queue.qhdr_start_offset + queue.qhdr_read_idx);
/* Read packet size */
MEMR(npu_dev, (void *)((size_t)read_ptr), packet, 4);
packet_size = *((uint32_t *)packet);
pr_debug("target_que: %d, packet_size: %d\n",
target_que,
packet_size);
if ((packet_size == 0) ||
(packet_size > NPU_IPC_BUF_LENGTH)) {
pr_err("Invalid packet size %d\n", packet_size);
status = -EINVAL;
goto exit;
}
new_read_idx = queue.qhdr_read_idx + packet_size;
if (new_read_idx < (queue.qhdr_q_size)) {
MEMR(npu_dev, (void *)((size_t)read_ptr), packet, packet_size);
} else {
new_read_idx -= (queue.qhdr_q_size);
MEMR(npu_dev, (void *)((size_t)read_ptr), packet,
packet_size - new_read_idx);
MEMR(npu_dev, (void *)((size_t)IPC_ADDR +
queue.qhdr_start_offset),
(void *)((size_t)packet + (packet_size-new_read_idx)),
new_read_idx);
}
queue.qhdr_read_idx = new_read_idx;
if (queue.qhdr_read_idx == queue.qhdr_write_idx)
/*
* receiver wants an interrupt from transmitter
* (when next item queued) because queue is empty
*/
queue.qhdr_rx_req = 1;
else
/* clear qhdr_rx_req since the queue is not empty */
queue.qhdr_rx_req = 0;
if (queue.qhdr_tx_req == 1)
/* transmitter requested an interrupt */
*is_tx_req_set = 1;
else
*is_tx_req_set = 0;
exit:
/* Update RX interrupt request -- queue.qhdr_rx_req */
MEMW(npu_dev, (void *)((size_t)offset +
(uint32_t)((size_t)&(queue.qhdr_rx_req) -
(size_t)&queue)), (uint8_t *)&queue.qhdr_rx_req,
sizeof(queue.qhdr_rx_req));
/* Update Read pointer -- queue.qhdr_read_idx */
MEMW(npu_dev, (void *)((size_t)offset + (uint32_t)(
(size_t)&(queue.qhdr_read_idx) - (size_t)&queue)),
(uint8_t *)&queue.qhdr_read_idx, sizeof(queue.qhdr_read_idx));
return status;
}
static int ipc_queue_write(struct npu_device *npu_dev,
uint32_t target_que, uint8_t *packet,
uint8_t *is_rx_req_set)
{
int status = 0;
struct hfi_queue_header queue;
uint32_t packet_size, new_write_idx;
uint32_t empty_space;
void *write_ptr;
uint32_t read_idx;
size_t offset = (size_t)IPC_ADDR +
sizeof(struct hfi_queue_tbl_header) +
target_que * sizeof(struct hfi_queue_header);
if ((packet == NULL) || (is_rx_req_set == NULL))
return -EINVAL;
MEMR(npu_dev, (void *)((size_t)offset), (uint8_t *)&queue,
HFI_QUEUE_HEADER_SIZE);
if (queue.qhdr_type != npu_q_setup[target_que].hdr ||
queue.qhdr_q_size != npu_q_setup[target_que].size ||
queue.qhdr_read_idx >= queue.qhdr_q_size ||
queue.qhdr_write_idx >= queue.qhdr_q_size ||
queue.qhdr_start_offset !=
npu_q_setup[target_que].start_offset) {
pr_err("Invalid Queue header\n");
status = -EIO;
goto exit;
}
packet_size = (*(uint32_t *)packet);
if (packet_size == 0) {
/* assign failed status and return */
status = -EPERM;
goto exit;
}
/* sample Read Idx */
read_idx = queue.qhdr_read_idx;
/* Calculate Empty Space(UWord32) in the Queue */
empty_space = (queue.qhdr_write_idx >= read_idx) ?
((queue.qhdr_q_size) - (queue.qhdr_write_idx - read_idx)) :
(read_idx - queue.qhdr_write_idx);
if (empty_space <= packet_size) {
/*
* If Queue is FULL/ no space for message
* set qhdr_tx_req.
*/
queue.qhdr_tx_req = 1;
/*
* Queue is FULL, force raise an interrupt to Receiver
*/
*is_rx_req_set = 1;
status = -ENOSPC;
goto exit;
}
/*
* clear qhdr_tx_req so that receiver does not raise an interrupt
* on reading packets from Queue, since there is space to write
* the next packet
*/
queue.qhdr_tx_req = 0;
new_write_idx = (queue.qhdr_write_idx + packet_size);
write_ptr = (void *)(size_t)((size_t)IPC_ADDR +
queue.qhdr_start_offset + queue.qhdr_write_idx);
if (new_write_idx < queue.qhdr_q_size) {
MEMW(npu_dev, (void *)((size_t)write_ptr), (uint8_t *)packet,
packet_size);
} else {
/* wraparound case */
new_write_idx -= (queue.qhdr_q_size);
MEMW(npu_dev, (void *)((size_t)write_ptr), (uint8_t *)packet,
packet_size - new_write_idx);
MEMW(npu_dev, (void *)((size_t)((size_t)IPC_ADDR +
queue.qhdr_start_offset)), (uint8_t *)(packet +
(packet_size - new_write_idx)), new_write_idx);
}
/* Update qhdr_write_idx */
queue.qhdr_write_idx = new_write_idx;
*is_rx_req_set = (queue.qhdr_rx_req == 1) ? 1 : 0;
/* Update Write pointer -- queue.qhdr_write_idx */
exit:
/* Update TX request -- queue.qhdr_tx_req */
MEMW(npu_dev, (void *)((size_t)(offset + (uint32_t)(
(size_t)&(queue.qhdr_tx_req) - (size_t)&queue))),
&queue.qhdr_tx_req, sizeof(queue.qhdr_tx_req));
MEMW(npu_dev, (void *)((size_t)(offset + (uint32_t)(
(size_t)&(queue.qhdr_write_idx) - (size_t)&queue))),
&queue.qhdr_write_idx, sizeof(queue.qhdr_write_idx));
return status;
}
/* -------------------------------------------------------------------------
* IPC Interface functions
* -------------------------------------------------------------------------
*/
int npu_host_ipc_send_cmd(struct npu_device *npu_dev, uint32_t q_idx,
void *cmd_ptr)
{
return npu_host_ipc_send_cmd_hfi(npu_dev, q_idx, cmd_ptr);
}
int npu_host_ipc_read_msg(struct npu_device *npu_dev, uint32_t q_idx,
uint32_t *msg_ptr)
{
return npu_host_ipc_read_msg_hfi(npu_dev, q_idx, msg_ptr);
}
int npu_host_ipc_pre_init(struct npu_device *npu_dev)
{
return npu_host_ipc_init_hfi(npu_dev);
}
int npu_host_ipc_post_init(struct npu_device *npu_dev)
{
return 0;
}

View File

@ -0,0 +1,464 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef NPU_HOST_IPC_H
#define NPU_HOST_IPC_H
/* -------------------------------------------------------------------------
* Defines
* -------------------------------------------------------------------------
*/
/* Messages sent **to** NPU */
/* IPC Message Commands -- uint32_t */
/* IPC command start base */
#define NPU_IPC_CMD_BASE 0x00000000
/* ipc_cmd_load_pkt */
#define NPU_IPC_CMD_LOAD 0x00000001
/* ipc_cmd_unload_pkt */
#define NPU_IPC_CMD_UNLOAD 0x00000002
/* ipc_cmd_execute_pkt */
#define NPU_IPC_CMD_EXECUTE 0x00000003
/* ipc_cmd_set_logging_state */
#define NPU_IPC_CMD_CONFIG_LOG 0x00000004
#define NPU_IPC_CMD_CONFIG_PERFORMANCE 0x00000005
#define NPU_IPC_CMD_CONFIG_DEBUG 0x00000006
#define NPU_IPC_CMD_SHUTDOWN 0x00000007
/* ipc_cmd_loopback_packet */
#define NPU_IPC_CMD_LOOPBACK 0x00000008
/* ipc_cmd_load_packet_v2_t */
#define NPU_IPC_CMD_LOAD_V2 0x00000009
/* ipc_cmd_execute_packet_v2 */
#define NPU_IPC_CMD_EXECUTE_V2 0x0000000A
/* ipc_cmd_set_property_packet */
#define NPU_IPC_CMD_SET_PROPERTY 0x0000000B
/* ipc_cmd_get_property_packet */
#define NPU_IPC_CMD_GET_PROPERTY 0x0000000C
/* Messages sent **from** NPU */
/* IPC Message Response -- uint32_t */
/* IPC response start base */
#define NPU_IPC_MSG_BASE 0x00010000
/* ipc_msg_load_pkt */
#define NPU_IPC_MSG_LOAD_DONE 0x00010001
/* ipc_msg_header_pkt */
#define NPU_IPC_MSG_UNLOAD_DONE 0x00010002
/* ipc_msg_header_pkt */
#define NPU_IPC_MSG_EXECUTE_DONE 0x00010003
/* ipc_msg_event_notify_pkt */
#define NPU_IPC_MSG_EVENT_NOTIFY 0x00010004
/* ipc_msg_loopback_pkt */
#define NPU_IPC_MSG_LOOPBACK_DONE 0x00010005
/* ipc_msg_execute_pkt_v2 */
#define NPU_IPC_MSG_EXECUTE_V2_DONE 0x00010006
/* ipc_msg_set_property_packet */
#define NPU_IPC_MSG_SET_PROPERTY_DONE 0x00010007
/* ipc_msg_get_property_packet */
#define NPU_IPC_MSG_GET_PROPERTY_DONE 0x00010008
/* ipc_msg_general_notify_pkt */
#define NPU_IPC_MSG_GENERAL_NOTIFY 0x00010010
/* IPC Notify Message Type -- uint32_t */
#define NPU_NOTIFY_DCVS_MODE 0x00002000
/* Logging message size */
/* Number 32-bit elements for the maximum log message size */
#define NPU_LOG_MSG_MAX_SIZE 4
/* Performance */
/* Performance counters for current network layer */
/* Amount of data read from all the DMA read channels */
#define NPU_PERFORMANCE_DMA_DATA_READ 0x01
/* Amount of data written from all the DMA write channels */
#define NPU_PERFORMANCE_DMA_DATA_WRITTEN 0x02
/* Number of blocks read by DMA channels */
#define NPU_PERFORMANCE_DMA_NUM_BLOCKS_READ 0x03
/* Number of blocks written by DMA channels */
#define NPU_PERFORMANCE_DMA_NUM_BLOCKS_WRITTEN 0x04
/* Number of instructions executed by CAL */
#define NPU_PERFORMANCE_INSTRUCTIONS_CAL 0x05
/* Number of instructions executed by CUB */
#define NPU_PERFORMANCE_INSTRUCTIONS_CUB 0x06
/* Timestamp of start of network load */
#define NPU_PERFORMANCE_TIMESTAMP_LOAD_START 0x07
/* Timestamp of end of network load */
#define NPU_PERFORMANCE_TIMESTAMP_LOAD_END 0x08
/* Timestamp of start of network execute */
#define NPU_PERFORMANCE_TIMESTAMP_EXECUTE_START 0x09
/* Timestamp of end of network execute */
#define NPU_PERFORMANCE_TIMESTAMP_EXECUTE_END 0x10
/* Timestamp of CAL start */
#define NPU_PERFORMANCE_TIMESTAMP_CAL_START 0x11
/* Timestamp of CAL end */
#define NPU_PERFORMANCE_TIMESTAMP_CAL_END 0x12
/* Timestamp of CUB start */
#define NPU_PERFORMANCE_TIMESTAMP_CUB_START 0x13
/* Timestamp of CUB end */
#define NPU_PERFORMANCE_TIMESTAMP_CUB_END 0x14
/* Performance enable */
/* Select which counters you want back per layer */
/* Shutdown */
/* Immediate shutdown, discard any state, etc */
#define NPU_SHUTDOWN_IMMEDIATE 0x01
/* Shutdown after current execution (if any) is completed */
#define NPU_SHUTDOWN_WAIT_CURRENT_EXECUTION 0x02
/* Debug stats */
#define NUM_LAYER_STATS_PER_EXE_MSG_MAX 110
/* DCVS */
#define NPU_DCVS_ACTIVITY_MAX_PERF 0x100
/* -------------------------------------------------------------------------
* Data Structures
* -------------------------------------------------------------------------
*/
/* Command Header - Header for all Messages **TO** NPU */
/*
* command header packet definition for
* messages sent from host->NPU
*/
struct ipc_cmd_header_pkt {
uint32_t size;
uint32_t cmd_type;
uint32_t trans_id;
uint32_t flags; /* TDO what flags and why */
};
/* Message Header - Header for all messages **FROM** NPU */
/*
* message header packet definition for
* mesasges sent from NPU->host
*/
struct ipc_msg_header_pkt {
uint32_t size;
uint32_t msg_type;
uint32_t status;
uint32_t trans_id;
uint32_t flags;
};
/* Execute */
/*
* FIRMWARE
* keep lastNetworkIDRan = uint32
* keep wasLastNetworkChunky = BOOLEAN
*/
/*
* ACO Buffer definition
*/
struct npu_aco_buffer {
/*
* used to track if previous network is the same and already loaded,
* we can save a dma
*/
uint32_t network_id;
/*
* size of header + first chunk ACO buffer -
* this saves a dma by dmaing both header and first chunk
*/
uint32_t buf_size;
/*
* SMMU 32-bit mapped address that the DMA engine can read -
* uses lower 32 bits
*/
uint64_t address;
};
/*
* ACO Buffer V2 definition
*/
struct npu_aco_buffer_v2 {
/*
* used to track if previous network is the same and already loaded,
* we can save a dma
*/
uint32_t network_id;
/*
* size of header + first chunk ACO buffer -
* this saves a dma by dmaing both header and first chunk
*/
uint32_t buf_size;
/*
* SMMU 32-bit mapped address that the DMA engine can read -
* uses lower 32 bits
*/
uint32_t address;
/*
* number of layers in the network
*/
uint32_t num_layers;
};
/*
* ACO Patch Parameters
*/
struct npu_patch_tuple {
uint32_t value;
uint32_t chunk_id;
uint16_t instruction_size_in_bytes;
uint16_t variable_size_in_bits;
uint16_t shift_value_in_bits;
uint32_t loc_offset;
};
/*
* ACO Patch Tuple V2
*/
struct npu_patch_tuple_v2 {
uint32_t value;
uint32_t chunk_id;
uint32_t instruction_size_in_bytes;
uint32_t variable_size_in_bits;
uint32_t shift_value_in_bits;
uint32_t loc_offset;
};
struct npu_patch_params {
uint32_t num_params;
struct npu_patch_tuple param[2];
};
/*
* LOAD command packet definition
*/
struct ipc_cmd_load_pkt {
struct ipc_cmd_header_pkt header;
struct npu_aco_buffer buf_pkt;
};
/*
* LOAD command packet V2 definition
*/
struct ipc_cmd_load_pkt_v2 {
struct ipc_cmd_header_pkt header;
struct npu_aco_buffer_v2 buf_pkt;
uint32_t num_patch_params;
struct npu_patch_tuple_v2 patch_params[];
};
/*
* UNLOAD command packet definition
*/
struct ipc_cmd_unload_pkt {
struct ipc_cmd_header_pkt header;
uint32_t network_hdl;
};
/*
* Execute packet definition
*/
struct ipc_cmd_execute_pkt {
struct ipc_cmd_header_pkt header;
struct npu_patch_params patch_params;
uint32_t network_hdl;
};
struct npu_patch_params_v2 {
uint32_t value;
uint32_t id;
};
/*
* Execute packet V2 definition
*/
struct ipc_cmd_execute_pkt_v2 {
struct ipc_cmd_header_pkt header;
uint32_t network_hdl;
uint32_t num_patch_params;
struct npu_patch_params_v2 patch_params[];
};
/*
* Loopback packet definition
*/
struct ipc_cmd_loopback_pkt {
struct ipc_cmd_header_pkt header;
uint32_t loopbackParams;
};
/*
* Generic property definition
*/
struct ipc_cmd_prop_pkt {
struct ipc_cmd_header_pkt header;
uint32_t prop_id;
uint32_t num_params;
uint32_t network_hdl;
uint32_t prop_param[];
};
/*
* Generic property response packet definition
*/
struct ipc_msg_prop_pkt {
struct ipc_msg_header_pkt header;
uint32_t prop_id;
uint32_t num_params;
uint32_t network_hdl;
uint32_t prop_param[];
};
/*
* Generic notify message packet definition
*/
struct ipc_msg_general_notify_pkt {
struct ipc_msg_header_pkt header;
uint32_t notify_id;
uint32_t num_params;
uint32_t network_hdl;
uint32_t notify_param[];
};
/*
* LOAD response packet definition
*/
struct ipc_msg_load_pkt {
struct ipc_msg_header_pkt header;
uint32_t network_hdl;
};
/*
* UNLOAD response packet definition
*/
struct ipc_msg_unload_pkt {
struct ipc_msg_header_pkt header;
uint32_t network_hdl;
};
/*
* Layer Stats information returned back during EXECUTE_DONE response
*/
struct ipc_layer_stats {
/*
* hardware tick count per layer
*/
uint32_t tick_count;
};
struct ipc_execute_layer_stats {
/*
* total number of layers associated with the execution
*/
uint32_t total_num_layers;
/*
* pointer to each layer stats
*/
struct ipc_layer_stats
layer_stats_list[NUM_LAYER_STATS_PER_EXE_MSG_MAX];
};
struct ipc_execute_stats {
/*
* total e2e IPC tick count during EXECUTE cmd
*/
uint32_t e2e_ipc_tick_count;
/*
* tick count on ACO loading
*/
uint32_t aco_load_tick_count;
/*
* tick count on ACO execution
*/
uint32_t aco_execution_tick_count;
/*
* individual layer stats
*/
struct ipc_execute_layer_stats exe_stats;
};
/*
* EXECUTE response packet definition
*/
struct ipc_msg_execute_pkt {
struct ipc_msg_header_pkt header;
struct ipc_execute_stats stats;
uint32_t network_hdl;
};
/*
* EXECUTE V2 response packet definition
*/
struct ipc_msg_execute_pkt_v2 {
struct ipc_msg_header_pkt header;
uint32_t network_hdl;
uint32_t stats_data[];
};
/*
* LOOPBACK response packet definition
*/
struct ipc_msg_loopback_pkt {
struct ipc_msg_header_pkt header;
uint32_t loopbackParams;
};
/* Logging Related */
/*
* ipc_log_state_t - Logging state
*/
struct ipc_log_state {
uint32_t module_msk;
uint32_t level_msk;
};
struct ipc_cmd_log_state_pkt {
struct ipc_cmd_header_pkt header;
struct ipc_log_state log_state;
};
struct ipc_msg_log_state_pkt {
struct ipc_msg_header_pkt header;
struct ipc_log_state log_state;
};
/*
* Logging message
* This is a message from the NPU that contains the
* logging message. The values of part1-4 are not exposed
* the receiver has to refer to the logging implementation to
* intrepret what these mean and how to parse
*/
struct ipc_msg_log_pkt {
struct ipc_msg_header_pkt header;
uint32_t log_msg[NPU_LOG_MSG_MAX_SIZE];
};
/* Performance Related */
/*
* Set counter mask of which counters we want
* This is a message from HOST->NPU Firmware
*/
struct ipc_cmd_set_performance_query {
struct ipc_cmd_header_pkt header;
uint32_t cnt_msk;
};
/*
* Set counter mask of which counters we want
* This is a message from HOST->NPU Firmware
*/
struct ipc_msg_performance_counters {
struct ipc_cmd_header_pkt header;
uint32_t layer_id;
uint32_t num_tulpes;
/* Array of tuples [HEADER,value] */
uint32_t cnt_tulpes[];
};
/*
* ipc_cmd_shutdown - Shutdown command
*/
struct ipc_cmd_shutdown_pkt {
struct ipc_cmd_header_pkt header;
uint32_t shutdown_flags;
};
#endif /* NPU_HOST_IPC_H */

View File

@ -0,0 +1,53 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef NPU_HW_H
#define NPU_HW_H
/* -------------------------------------------------------------------------
* Defines
* -------------------------------------------------------------------------
*/
#define NPU_HW_VERSION (0x00000000)
#define NPU_MASTERn_IPC_IRQ_OUT(n) (0x00001004+0x1000*(n))
#define NPU_CACHE_ATTR_IDn___POR 0x00011100
#define NPU_CACHE_ATTR_IDn(n) (0x00000800+0x4*(n))
#define NPU_MASTERn_IPC_IRQ_IN_CTRL(n) (0x00001008+0x1000*(n))
#define NPU_MASTER0_IPC_IRQ_IN_CTRL__IRQ_SOURCE_SELECT___S 4
#define NPU_MASTERn_IPC_IRQ_OUT_CTRL(n) (0x00001004+0x1000*(n))
#define NPU_MASTER0_IPC_IRQ_OUT_CTRL__IRQ_TYPE_PULSE 4
#define NPU_GPR0 (0x00000100)
#define NPU_MASTERn_ERROR_IRQ_STATUS(n) (0x00001010+0x1000*(n))
#define NPU_MASTERn_ERROR_IRQ_INCLUDE(n) (0x00001014+0x1000*(n))
#define NPU_MASTERn_ERROR_IRQ_ENABLE(n) (0x00001018+0x1000*(n))
#define NPU_MASTERn_ERROR_IRQ_CLEAR(n) (0x0000101C+0x1000*(n))
#define NPU_MASTERn_ERROR_IRQ_SET(n) (0x00001020+0x1000*(n))
#define NPU_MASTERn_ERROR_IRQ_OWNER(n) (0x00007000+4*(n))
#define NPU_ERROR_IRQ_MASK 0x000000E3
#define NPU_MASTERn_WDOG_IRQ_STATUS(n) (0x00001030+0x1000*(n))
#define NPU_WDOG_BITE_IRQ_STATUS (1 << 1)
#define NPU_MASTERn_WDOG_IRQ_INCLUDE(n) (0x00001034+0x1000*(n))
#define NPU_WDOG_BITE_IRQ_INCLUDE (1 << 1)
#define NPU_MASTERn_WDOG_IRQ_OWNER(n) (0x00007010+4*(n))
#define NPU_WDOG_IRQ_MASK 0x00000002
#define NPU_GPR1 (0x00000104)
#define NPU_GPR2 (0x00000108)
#define NPU_GPR3 (0x0000010C)
#define NPU_GPR4 (0x00000110)
#define NPU_GPR13 (0x00000134)
#define NPU_GPR14 (0x00000138)
#define NPU_GPR15 (0x0000013C)
#define BWMON2_SAMPLING_WINDOW (0x000003A8)
#define BWMON2_BYTE_COUNT_THRESHOLD_HIGH (0x000003AC)
#define BWMON2_BYTE_COUNT_THRESHOLD_MEDIUM (0x000003B0)
#define BWMON2_BYTE_COUNT_THRESHOLD_LOW (0x000003B4)
#define BWMON2_ZONE_ACTIONS (0x000003B8)
#define BWMON2_ZONE_COUNT_THRESHOLD (0x000003BC)
#endif /* NPU_HW_H */

View File

@ -0,0 +1,485 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
/* -------------------------------------------------------------------------
* Includes
* -------------------------------------------------------------------------
*/
#include <linux/msm_dma_iommu_mapping.h>
#include <soc/qcom/subsystem_restart.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
#include <linux/qcom_scm.h>
#include <linux/soc/qcom/mdt_loader.h>
#include "npu_hw_access.h"
#include "npu_common.h"
#include "npu_hw.h"
/* -------------------------------------------------------------------------
* Functions - Register
* -------------------------------------------------------------------------
*/
static uint32_t npu_reg_read(void __iomem *base, size_t size, uint32_t off)
{
if (!base) {
pr_err("NULL base address\n");
return 0;
}
if ((off % 4) != 0) {
pr_err("offset %x is not aligned\n", off);
return 0;
}
if (off >= size) {
pr_err("offset exceeds io region %x:%x\n", off, size);
return 0;
}
return readl_relaxed(base + off);
}
static void npu_reg_write(void __iomem *base, size_t size, uint32_t off,
uint32_t val)
{
if (!base) {
pr_err("NULL base address\n");
return;
}
if ((off % 4) != 0) {
pr_err("offset %x is not aligned\n", off);
return;
}
if (off >= size) {
pr_err("offset exceeds io region %x:%x\n", off, size);
return;
}
writel_relaxed(val, base + off);
__iowmb();
}
uint32_t npu_core_reg_read(struct npu_device *npu_dev, uint32_t off)
{
return npu_reg_read(npu_dev->core_io.base, npu_dev->core_io.size, off);
}
void npu_core_reg_write(struct npu_device *npu_dev, uint32_t off, uint32_t val)
{
npu_reg_write(npu_dev->core_io.base, npu_dev->core_io.size,
off, val);
}
uint32_t npu_bwmon_reg_read(struct npu_device *npu_dev, uint32_t off)
{
return npu_reg_read(npu_dev->bwmon_io.base, npu_dev->bwmon_io.size,
off);
}
void npu_bwmon_reg_write(struct npu_device *npu_dev, uint32_t off,
uint32_t val)
{
npu_reg_write(npu_dev->bwmon_io.base, npu_dev->bwmon_io.size,
off, val);
}
uint32_t npu_qfprom_reg_read(struct npu_device *npu_dev, uint32_t off)
{
return npu_reg_read(npu_dev->qfprom_io.base,
npu_dev->qfprom_io.size, off);
}
/* -------------------------------------------------------------------------
* Functions - Memory
* -------------------------------------------------------------------------
*/
void npu_mem_write(struct npu_device *npu_dev, void *dst, void *src,
uint32_t size)
{
size_t dst_off = (size_t)dst;
uint32_t *src_ptr32 = (uint32_t *)src;
uint8_t *src_ptr8 = NULL;
uint32_t i = 0;
uint32_t num = 0;
if (dst_off >= npu_dev->tcm_io.size ||
(npu_dev->tcm_io.size - dst_off) < size) {
pr_err("memory write exceeds io region %x:%x:%x\n",
dst_off, size, npu_dev->tcm_io.size);
return;
}
num = size/4;
for (i = 0; i < num; i++) {
writel_relaxed(src_ptr32[i], npu_dev->tcm_io.base + dst_off);
dst_off += 4;
}
if (size%4 != 0) {
src_ptr8 = (uint8_t *)((size_t)src + (num*4));
num = size%4;
for (i = 0; i < num; i++) {
writeb_relaxed(src_ptr8[i], npu_dev->tcm_io.base +
dst_off);
dst_off += 1;
}
}
__iowmb();
}
int32_t npu_mem_read(struct npu_device *npu_dev, void *src, void *dst,
uint32_t size)
{
size_t src_off = (size_t)src;
uint32_t *out32 = (uint32_t *)dst;
uint8_t *out8 = NULL;
uint32_t i = 0;
uint32_t num = 0;
if (src_off >= npu_dev->tcm_io.size ||
(npu_dev->tcm_io.size - src_off) < size) {
pr_err("memory read exceeds io region %x:%x:%x\n",
src_off, size, npu_dev->tcm_io.size);
return 0;
}
num = size/4;
for (i = 0; i < num; i++) {
out32[i] = readl_relaxed(npu_dev->tcm_io.base + src_off);
src_off += 4;
}
if (size%4 != 0) {
out8 = (uint8_t *)((size_t)dst + (num*4));
num = size%4;
for (i = 0; i < num; i++) {
out8[i] = readb_relaxed(npu_dev->tcm_io.base + src_off);
src_off += 1;
}
}
return 0;
}
void *npu_ipc_addr(void)
{
return (void *)(IPC_MEM_OFFSET_FROM_SSTCM);
}
/* -------------------------------------------------------------------------
* Functions - Interrupt
* -------------------------------------------------------------------------
*/
void npu_interrupt_ack(struct npu_device *npu_dev, uint32_t intr_num)
{
struct npu_host_ctx *host_ctx = &npu_dev->host_ctx;
uint32_t wdg_irq_sts = 0, error_irq_sts = 0;
/* Clear irq state */
REGW(npu_dev, NPU_MASTERn_IPC_IRQ_OUT(0), 0x0);
wdg_irq_sts = REGR(npu_dev, NPU_MASTERn_WDOG_IRQ_STATUS(0));
if (wdg_irq_sts != 0) {
pr_err("wdg irq %x\n", wdg_irq_sts);
host_ctx->wdg_irq_sts |= wdg_irq_sts;
host_ctx->fw_error = true;
}
error_irq_sts = REGR(npu_dev, NPU_MASTERn_ERROR_IRQ_STATUS(0));
error_irq_sts &= REGR(npu_dev, NPU_MASTERn_ERROR_IRQ_ENABLE(0));
if (error_irq_sts != 0) {
REGW(npu_dev, NPU_MASTERn_ERROR_IRQ_CLEAR(0), error_irq_sts);
pr_err("error irq %x\n", error_irq_sts);
host_ctx->err_irq_sts |= error_irq_sts;
host_ctx->fw_error = true;
}
}
int32_t npu_interrupt_raise_m0(struct npu_device *npu_dev)
{
/* Bit 4 is setting IRQ_SOURCE_SELECT to local
* and we're triggering a pulse to NPU_MASTER0_IPC_IN_IRQ0
*/
npu_core_reg_write(npu_dev, NPU_MASTERn_IPC_IRQ_IN_CTRL(0), 0x1
<< NPU_MASTER0_IPC_IRQ_IN_CTRL__IRQ_SOURCE_SELECT___S | 0x1);
return 0;
}
int32_t npu_interrupt_raise_dsp(struct npu_device *npu_dev)
{
npu_core_reg_write(npu_dev, NPU_MASTERn_IPC_IRQ_OUT_CTRL(1), 0x8);
return 0;
}
/* -------------------------------------------------------------------------
* Functions - ION Memory
* -------------------------------------------------------------------------
*/
static struct npu_ion_buf *npu_alloc_npu_ion_buffer(struct npu_client
*client, int buf_hdl, uint32_t size)
{
struct npu_ion_buf *ret_val = NULL, *tmp;
struct list_head *pos = NULL;
mutex_lock(&client->list_lock);
list_for_each(pos, &(client->mapped_buffer_list)) {
tmp = list_entry(pos, struct npu_ion_buf, list);
if (tmp->fd == buf_hdl) {
ret_val = tmp;
break;
}
}
if (ret_val) {
/* mapped already, treat as invalid request */
pr_err("ion buf has been mapped\n");
ret_val = NULL;
} else {
ret_val = kzalloc(sizeof(*ret_val), GFP_KERNEL);
if (ret_val) {
ret_val->fd = buf_hdl;
ret_val->size = size;
ret_val->iova = 0;
list_add(&(ret_val->list),
&(client->mapped_buffer_list));
}
}
mutex_unlock(&client->list_lock);
return ret_val;
}
static struct npu_ion_buf *npu_get_npu_ion_buffer(struct npu_client
*client, int buf_hdl)
{
struct list_head *pos = NULL;
struct npu_ion_buf *ret_val = NULL, *tmp;
mutex_lock(&client->list_lock);
list_for_each(pos, &(client->mapped_buffer_list)) {
tmp = list_entry(pos, struct npu_ion_buf, list);
if (tmp->fd == buf_hdl) {
ret_val = tmp;
break;
}
}
mutex_unlock(&client->list_lock);
return ret_val;
}
static void npu_free_npu_ion_buffer(struct npu_client
*client, int buf_hdl)
{
struct list_head *pos = NULL;
struct npu_ion_buf *npu_ion_buf = NULL;
mutex_lock(&client->list_lock);
list_for_each(pos, &(client->mapped_buffer_list)) {
npu_ion_buf = list_entry(pos, struct npu_ion_buf, list);
if (npu_ion_buf->fd == buf_hdl) {
list_del(&npu_ion_buf->list);
kfree(npu_ion_buf);
break;
}
}
mutex_unlock(&client->list_lock);
}
int npu_mem_map(struct npu_client *client, int buf_hdl, uint32_t size,
uint64_t *addr)
{
MODULE_IMPORT_NS(DMA_BUF);
int ret = 0;
struct npu_device *npu_dev = client->npu_dev;
struct npu_ion_buf *ion_buf = NULL;
struct npu_smmu_ctx *smmu_ctx = &npu_dev->smmu_ctx;
if (buf_hdl == 0)
return -EINVAL;
ion_buf = npu_alloc_npu_ion_buffer(client, buf_hdl, size);
if (!ion_buf) {
pr_err("%s fail to alloc npu_ion_buffer\n", __func__);
ret = -ENOMEM;
return ret;
}
smmu_ctx->attach_cnt++;
ion_buf->dma_buf = dma_buf_get(ion_buf->fd);
if (IS_ERR_OR_NULL(ion_buf->dma_buf)) {
pr_err("dma_buf_get failed %d\n", ion_buf->fd);
ret = -ENOMEM;
ion_buf->dma_buf = NULL;
goto map_end;
}
ion_buf->attachment = dma_buf_attach(ion_buf->dma_buf,
&(npu_dev->pdev->dev));
if (IS_ERR(ion_buf->attachment)) {
ret = -ENOMEM;
ion_buf->attachment = NULL;
goto map_end;
}
ion_buf->attachment->dma_map_attrs = DMA_ATTR_IOMMU_USE_UPSTREAM_HINT;
ion_buf->table = dma_buf_map_attachment(ion_buf->attachment,
DMA_BIDIRECTIONAL);
if (IS_ERR(ion_buf->table)) {
pr_err("npu dma_buf_map_attachment failed\n");
ret = -ENOMEM;
ion_buf->table = NULL;
goto map_end;
}
ion_buf->iova = ion_buf->table->sgl->dma_address;
ion_buf->size = ion_buf->dma_buf->size;
*addr = ion_buf->iova;
pr_debug("mapped mem addr:0x%llx size:0x%x\n", ion_buf->iova,
ion_buf->size);
map_end:
if (ret)
npu_mem_unmap(client, buf_hdl, 0);
return ret;
}
void npu_mem_invalidate(struct npu_client *client, int buf_hdl)
{
struct npu_device *npu_dev = client->npu_dev;
struct npu_ion_buf *ion_buf = npu_get_npu_ion_buffer(client,
buf_hdl);
if (!ion_buf)
pr_err("%s can't find ion buf\n", __func__);
else
dma_sync_sg_for_cpu(&(npu_dev->pdev->dev), ion_buf->table->sgl,
ion_buf->table->nents, DMA_BIDIRECTIONAL);
}
bool npu_mem_verify_addr(struct npu_client *client, uint64_t addr)
{
struct npu_ion_buf *ion_buf = NULL;
struct list_head *pos = NULL;
bool valid = false;
mutex_lock(&client->list_lock);
list_for_each(pos, &(client->mapped_buffer_list)) {
ion_buf = list_entry(pos, struct npu_ion_buf, list);
if (ion_buf->iova == addr) {
valid = true;
break;
}
}
mutex_unlock(&client->list_lock);
return valid;
}
void npu_mem_unmap(struct npu_client *client, int buf_hdl, uint64_t addr)
{
MODULE_IMPORT_NS(DMA_BUF);
struct npu_device *npu_dev = client->npu_dev;
struct npu_ion_buf *ion_buf = NULL;
/* clear entry and retrieve the corresponding buffer */
ion_buf = npu_get_npu_ion_buffer(client, buf_hdl);
if (!ion_buf) {
pr_err("%s could not find buffer\n", __func__);
return;
}
if (ion_buf->iova != addr)
pr_warn("unmap address %llu doesn't match %llu\n", addr,
ion_buf->iova);
if (ion_buf->table)
dma_buf_unmap_attachment(ion_buf->attachment, ion_buf->table,
DMA_BIDIRECTIONAL);
if (ion_buf->dma_buf && ion_buf->attachment)
dma_buf_detach(ion_buf->dma_buf, ion_buf->attachment);
if (ion_buf->dma_buf)
dma_buf_put(ion_buf->dma_buf);
npu_dev->smmu_ctx.attach_cnt--;
pr_debug("unmapped mem addr:0x%llx size:0x%x\n", ion_buf->iova,
ion_buf->size);
npu_free_npu_ion_buffer(client, buf_hdl);
}
/* -------------------------------------------------------------------------
* Functions - Features
* -------------------------------------------------------------------------
*/
uint8_t npu_hw_clk_gating_enabled(void)
{
return 1;
}
uint8_t npu_hw_log_enabled(void)
{
return 1;
}
/* -------------------------------------------------------------------------
* Functions - Subsystem/PIL
* -------------------------------------------------------------------------
*/
#define NPU_PAS_ID (23)
int npu_subsystem_get(struct npu_device *npu_dev, const char *fw_name)
{
struct device *dev = npu_dev->device;
const struct firmware *firmware_p;
ssize_t fw_size;
/* load firmware */
int ret = request_firmware(&firmware_p, fw_name, dev);
if (ret < 0) {
pr_err("request_firmware %s failed: %d\n", fw_name, ret);
return ret;
}
fw_size = qcom_mdt_get_size(firmware_p);
if (fw_size < 0 || fw_size > npu_dev->fw_io.mem_size) {
pr_err("npu fw size invalid, %lld\n", fw_size);
return -EINVAL;
}
/* load the ELF segments to memory */
ret = qcom_mdt_load(dev, firmware_p, fw_name, NPU_PAS_ID,
npu_dev->fw_io.mem_region, npu_dev->fw_io.mem_phys,
npu_dev->fw_io.mem_size, &npu_dev->fw_io.mem_reloc);
release_firmware(firmware_p);
if (ret) {
pr_err("qcom_mdt_load failure, %d\n", ret);
return ret;
}
ret = qcom_scm_pas_auth_and_reset(NPU_PAS_ID);
if (ret) {
pr_err("failed to authenticate image and release reset\n");
return -2;
}
pr_debug("done pas auth\n");
return 0;
}
void npu_subsystem_put(struct npu_device *npu_dev)
{
int ret = qcom_scm_pas_shutdown(NPU_PAS_ID);
if (ret)
pr_err("failed to shutdown: %d\n", ret);
}

View File

@ -0,0 +1,87 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _NPU_HW_ACCESS_H
#define _NPU_HW_ACCESS_H
/* -------------------------------------------------------------------------
* Includes
* -------------------------------------------------------------------------
*/
#include "npu_common.h"
/* -------------------------------------------------------------------------
* Defines
* -------------------------------------------------------------------------
*/
#define IPC_MEM_OFFSET_FROM_SSTCM 0x00010000
#define SYS_CACHE_SCID 23
#define QFPROM_FMAX_REG_OFFSET 0x000001C8
#define QFPROM_FMAX_BITS_MASK 0x0000000C
#define QFPROM_FMAX_BITS_SHIFT 2
#define REGW(npu_dev, off, val) npu_core_reg_write(npu_dev, off, val)
#define REGR(npu_dev, off) npu_core_reg_read(npu_dev, off)
#define MEMW(npu_dev, dst, src, size) npu_mem_write(npu_dev, (void *)(dst),\
(void *)(src), size)
#define MEMR(npu_dev, src, dst, size) npu_mem_read(npu_dev, (void *)(src),\
(void *)(dst), size)
#define IPC_ADDR npu_ipc_addr()
#define INTERRUPT_ACK(npu_dev, num) npu_interrupt_ack(npu_dev, num)
#define INTERRUPT_RAISE_NPU(npu_dev) npu_interrupt_raise_m0(npu_dev)
#define INTERRUPT_RAISE_DSP(npu_dev) npu_interrupt_raise_dsp(npu_dev)
/* -------------------------------------------------------------------------
* Data Structures
* -------------------------------------------------------------------------
*/
struct npu_device;
struct npu_ion_buf_t;
struct npu_host_ctx;
struct npu_client;
typedef irqreturn_t (*intr_hdlr_fn)(int32_t irq, void *ptr);
typedef void (*wq_hdlr_fn) (struct work_struct *work);
/* -------------------------------------------------------------------------
* Function Prototypes
* -------------------------------------------------------------------------
*/
uint32_t npu_core_reg_read(struct npu_device *npu_dev, uint32_t off);
void npu_core_reg_write(struct npu_device *npu_dev, uint32_t off, uint32_t val);
uint32_t npu_bwmon_reg_read(struct npu_device *npu_dev, uint32_t off);
void npu_bwmon_reg_write(struct npu_device *npu_dev, uint32_t off,
uint32_t val);
void npu_mem_write(struct npu_device *npu_dev, void *dst, void *src,
uint32_t size);
int32_t npu_mem_read(struct npu_device *npu_dev, void *src, void *dst,
uint32_t size);
uint32_t npu_qfprom_reg_read(struct npu_device *npu_dev, uint32_t off);
int npu_mem_map(struct npu_client *client, int buf_hdl, uint32_t size,
uint64_t *addr);
void npu_mem_unmap(struct npu_client *client, int buf_hdl, uint64_t addr);
void npu_mem_invalidate(struct npu_client *client, int buf_hdl);
bool npu_mem_verify_addr(struct npu_client *client, uint64_t addr);
void *npu_ipc_addr(void);
void npu_interrupt_ack(struct npu_device *npu_dev, uint32_t intr_num);
int32_t npu_interrupt_raise_m0(struct npu_device *npu_dev);
int32_t npu_interrupt_raise_dsp(struct npu_device *npu_dev);
uint8_t npu_hw_clk_gating_enabled(void);
uint8_t npu_hw_log_enabled(void);
int npu_enable_irq(struct npu_device *npu_dev);
void npu_disable_irq(struct npu_device *npu_dev);
int npu_enable_sys_cache(struct npu_device *npu_dev);
void npu_disable_sys_cache(struct npu_device *npu_dev);
int npu_subsystem_get(struct npu_device *npu_dev, const char *fw_name);
void npu_subsystem_put(struct npu_device *npu_dev);
#endif /* _NPU_HW_ACCESS_H*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,147 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _NPU_MGR_H
#define _NPU_MGR_H
/* -------------------------------------------------------------------------
* Includes
* -------------------------------------------------------------------------
*/
#include <linux/spinlock.h>
#include "npu_hw_access.h"
#include "npu_common.h"
/* -------------------------------------------------------------------------
* Defines
* -------------------------------------------------------------------------
*/
#define NW_CMD_TIMEOUT_MS (1000 * 60 * 5) /* set for 5 minutes */
#define NW_CMD_TIMEOUT msecs_to_jiffies(NW_CMD_TIMEOUT_MS)
#define NW_DEBUG_TIMEOUT_MS (1000 * 60 * 30) /* set for 30 minutes */
#define NW_DEBUG_TIMEOUT msecs_to_jiffies(NW_DEBUG_TIMEOUT_MS)
#define FIRMWARE_VERSION 0x00001000
#define MAX_LOADED_NETWORK 32
#define NPU_IPC_BUF_LENGTH 512
#define FW_DBG_MODE_PAUSE (1 << 0)
#define FW_DBG_MODE_INC_TIMEOUT (1 << 1)
#define FW_DBG_DISABLE_WDOG (1 << 2)
#define FW_DBG_ENABLE_LOGGING (1 << 3)
/* -------------------------------------------------------------------------
* Data Structures
* -------------------------------------------------------------------------
*/
struct npu_network {
uint64_t id;
int buf_hdl;
uint64_t phy_add;
uint32_t size;
uint32_t first_block_size;
uint32_t network_hdl;
uint32_t priority;
uint32_t cur_perf_mode;
uint32_t init_perf_mode;
uint32_t num_layers;
void *stats_buf;
void __user *stats_buf_u;
uint32_t stats_buf_size;
uint32_t trans_id;
atomic_t ref_cnt;
bool is_valid;
bool is_active;
bool fw_error;
bool cmd_pending;
bool cmd_async;
int cmd_ret_status;
struct completion cmd_done;
struct npu_client *client;
};
enum fw_state {
FW_DISABLED = 0,
FW_ENABLED = 1,
};
struct npu_host_ctx {
struct mutex lock;
void *subsystem_handle;
struct npu_device *npu_dev;
enum fw_state fw_state;
int32_t fw_ref_cnt;
int32_t npu_init_cnt;
int32_t power_vote_num;
struct work_struct irq_work;
struct delayed_work fw_deinit_work;
atomic_t fw_deinit_work_cnt;
struct workqueue_struct *wq;
struct completion misc_done;
struct completion fw_deinit_done;
bool misc_pending;
void *prop_buf;
int32_t network_num;
struct npu_network networks[MAX_LOADED_NETWORK];
bool sys_cache_disable;
uint32_t fw_dbg_mode;
uint32_t exec_flags_override;
uint32_t fw_unload_delay_ms;
atomic_t ipc_trans_id;
atomic_t network_execute_cnt;
int cmd_ret_status;
uint32_t err_irq_sts;
uint32_t wdg_irq_sts;
bool fw_error;
};
struct npu_device;
/* -------------------------------------------------------------------------
* Function Prototypes
* -------------------------------------------------------------------------
*/
int npu_host_init(struct npu_device *npu_dev);
void npu_host_deinit(struct npu_device *npu_dev);
/* Host Driver IPC Interface */
int npu_host_ipc_pre_init(struct npu_device *npu_dev);
int npu_host_ipc_post_init(struct npu_device *npu_dev);
void npu_host_ipc_deinit(struct npu_device *npu_dev);
int npu_host_ipc_send_cmd(struct npu_device *npu_dev, uint32_t queueIndex,
void *pCmd);
int npu_host_ipc_read_msg(struct npu_device *npu_dev, uint32_t queueIndex,
uint32_t *pMsg);
int32_t npu_host_get_info(struct npu_device *npu_dev,
struct msm_npu_get_info_ioctl *get_info_ioctl);
int32_t npu_host_map_buf(struct npu_client *client,
struct msm_npu_map_buf_ioctl *map_ioctl);
int32_t npu_host_unmap_buf(struct npu_client *client,
struct msm_npu_unmap_buf_ioctl *unmap_ioctl);
int32_t npu_host_load_network(struct npu_client *client,
struct msm_npu_load_network_ioctl *load_ioctl);
int32_t npu_host_load_network_v2(struct npu_client *client,
struct msm_npu_load_network_ioctl_v2 *load_ioctl,
struct msm_npu_patch_info_v2 *patch_info);
int32_t npu_host_unload_network(struct npu_client *client,
struct msm_npu_unload_network_ioctl *unload);
int32_t npu_host_exec_network(struct npu_client *client,
struct msm_npu_exec_network_ioctl *exec_ioctl);
int32_t npu_host_exec_network_v2(struct npu_client *client,
struct msm_npu_exec_network_ioctl_v2 *exec_ioctl,
struct msm_npu_patch_buf_info *patch_buf_info);
int32_t npu_host_loopback_test(struct npu_device *npu_dev);
int32_t npu_host_set_fw_property(struct npu_device *npu_dev,
struct msm_npu_property *property);
int32_t npu_host_get_fw_property(struct npu_device *npu_dev,
struct msm_npu_property *property);
void npu_host_cleanup_networks(struct npu_client *client);
int32_t npu_host_set_perf_mode(struct npu_client *client, uint32_t network_hdl,
uint32_t perf_mode);
int32_t npu_host_get_perf_mode(struct npu_client *client, uint32_t network_hdl);
void npu_dump_debug_timeout_stats(struct npu_device *npu_dev);
#endif /* _NPU_MGR_H */

View File

@ -0,0 +1,279 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2014-2019 2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef __SUBSYS_RESTART_H
#define __SUBSYS_RESTART_H
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/device.h>
struct subsys_device;
extern struct bus_type subsys_bus_type;
enum {
RESET_SOC = 0,
RESET_SUBSYS_COUPLED,
RESET_LEVEL_MAX
};
enum crash_status {
CRASH_STATUS_NO_CRASH = 0,
CRASH_STATUS_ERR_FATAL,
CRASH_STATUS_WDOG_BITE,
};
struct device;
struct module;
enum ssr_comm {
SUBSYS_TO_SUBSYS_SYSMON,
SUBSYS_TO_HLOS,
HLOS_TO_SUBSYS_SYSMON_SHUTDOWN,
HLOS_TO_SUBSYS_SYSMON_DSENTER,
NUM_SSR_COMMS,
};
/**
* struct subsys_notif_timeout - timeout data used by notification timeout hdlr
* @comm_type: Specifies if the type of communication being tracked is
* through sysmon between two subsystems, subsystem notifier call chain, or
* sysmon shutdown.
* @dest_name: subsystem to which sysmon notification is being sent to
* @source_name: subsystem which generated event that notification is being sent
* for
* @timer: timer for scheduling timeout
*/
struct subsys_notif_timeout {
enum ssr_comm comm_type;
const char *dest_name;
const char *source_name;
struct timer_list timer;
};
/**
* struct subsys_desc - subsystem descriptor
* @name: name of subsystem
* @fw_name: firmware name
* @pon_depends_on: subsystem this subsystem wants to power-on first. If the
* dependednt subsystem is already powered-on, the framework won't try to power
* it back up again.
* @poff_depends_on: subsystem this subsystem wants to power-off first. If the
* dependednt subsystem is already powered-off, the framework won't try to power
* it off again.
* @dev: parent device
* @owner: module the descriptor belongs to
* @shutdown: Stop a subsystem
* @powerup_notify: Notify about start of a subsystem
* @powerup: Start a subsystem
* @crash_shutdown: Shutdown a subsystem when the system crashes (can't sleep)
* @ramdump: Collect a ramdump of the subsystem
* @free_memory: Free the memory associated with this subsystem
* @no_auth: Set if subsystem does not rely on PIL to authenticate and bring
* it out of reset
* @ssctl_instance_id: Instance id used to connect with SSCTL service
* @sysmon_pid: pdev id that sysmon is probed with for the subsystem
* @sysmon_shutdown_ret: Return value for the call to sysmon_send_shutdown
* @system_debug: If "set", triggers a device restart when the
* subsystem's wdog bite handler is invoked.
* @ignore_ssr_failure: SSR failures are usually fatal and results in panic. If
* set will ignore failure.
* @edge: GLINK logical name of the subsystem
*/
struct subsys_desc {
const char *name;
char fw_name[256];
const char *pon_depends_on;
const char *poff_depends_on;
struct device *dev;
struct module *owner;
int (*shutdown)(const struct subsys_desc *desc, bool force_stop);
int (*enter_ds)(const struct subsys_desc *desc);
int (*powerup_notify)(const struct subsys_desc *desc);
int (*powerup)(const struct subsys_desc *desc);
void (*crash_shutdown)(const struct subsys_desc *desc);
int (*ramdump)(int need_dumps, const struct subsys_desc *desc);
void (*free_memory)(const struct subsys_desc *desc);
struct completion shutdown_ack;
struct completion dsentry_ack;
int err_fatal_gpio;
int force_stop_bit;
int ramdump_disable_irq;
int shutdown_ack_irq;
int dsentry_ack_irq;
int ramdump_disable;
bool no_auth;
bool pil_mss_memsetup;
int ssctl_instance_id;
u32 sysmon_pid;
int sysmon_shutdown_ret;
int sysmon_dsentry_ret;
bool system_debug;
bool ignore_ssr_failure;
const char *edge;
struct qcom_smem_state *state;
#ifdef CONFIG_SETUP_SSR_NOTIF_TIMEOUTS
struct subsys_notif_timeout timeout_data;
#endif /* CONFIG_SETUP_SSR_NOTIF_TIMEOUTS */
};
/**
* struct notif_data - additional notif information
* @crashed: indicates if subsystem has crashed due to wdog bite or err fatal
* @enable_ramdump: ramdumps disabled if set to 0
* @enable_mini_ramdumps: enable flag for minimized critical-memory-only
* ramdumps
* @no_auth: set if subsystem does not use PIL to bring it out of reset
* @pdev: subsystem platform device pointer
*/
struct notif_data {
enum crash_status crashed;
int enable_ramdump;
int enable_mini_ramdumps;
bool no_auth;
struct platform_device *pdev;
};
#if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART)
extern int subsys_get_restart_level(struct subsys_device *dev);
extern int subsystem_restart_dev(struct subsys_device *dev);
extern int subsystem_restart(const char *name);
extern int subsystem_crashed(const char *name);
extern int subsystem_start_notify(const char *name);
extern int subsystem_stop_notify(const char *subsystem);
extern int subsystem_ds_entry(const char *subsystem);
extern int subsystem_ds_exit(const char *name);
extern int subsystem_s2d_entry(const char *subsystem);
extern int subsystem_s2d_exit(const char *name);
extern void *subsystem_get(const char *name);
extern void *subsystem_get_with_fwname(const char *name, const char *fw_name);
extern int subsystem_set_fwname(const char *name, const char *fw_name);
extern void subsystem_put(void *subsystem);
extern struct subsys_device *subsys_register(struct subsys_desc *desc);
extern void subsys_unregister(struct subsys_device *dev);
extern void subsys_set_crash_status(struct subsys_device *dev,
enum crash_status crashed);
extern enum crash_status subsys_get_crash_status(struct subsys_device *dev);
void notify_proxy_vote(struct device *device);
void notify_proxy_unvote(struct device *device);
void notify_before_auth_and_reset(struct device *device);
static inline void complete_shutdown_ack(struct subsys_desc *desc)
{
complete(&desc->shutdown_ack);
}
static inline void complete_dsentry_ack(struct subsys_desc *desc)
{
complete(&desc->dsentry_ack);
}
struct subsys_device *find_subsys_device(const char *str);
#else
static inline int subsys_get_restart_level(struct subsys_device *dev)
{
return 0;
}
static inline int subsystem_restart_dev(struct subsys_device *dev)
{
return 0;
}
static inline int subsystem_restart(const char *name)
{
return 0;
}
static inline int subsystem_crashed(const char *name)
{
return 0;
}
extern int subsystem_start_notify(const char *name)
{
return 0;
}
extern int subsystem_stop_notify(const char *subsystem)
{
return 0;
}
/**
* static int subsystem_ds_entry(const char *subsystem)
* {
* return 0;
* }
*
* static int subsystem_ds_exit(const char *name)
* {
* return 0;
* }
*
* static int subsystem_s2d_exit(const char *name)
* {
* return 0;
* }
*
* static int subsystem_s2d_entry(const char *name)
* {
* return 0;
* }
*/
static inline void *subsystem_get(const char *name)
{
return NULL;
}
static inline void *subsystem_get_with_fwname(const char *name,
const char *fw_name) {
return NULL;
}
static inline int subsystem_set_fwname(const char *name,
const char *fw_name) {
return 0;
}
static inline void subsystem_put(void *subsystem) { }
static inline
struct subsys_device *subsys_register(struct subsys_desc *desc)
{
return NULL;
}
static inline void subsys_unregister(struct subsys_device *dev) { }
static inline void subsys_set_crash_status(struct subsys_device *dev,
enum crash_status crashed) { }
static inline
enum crash_status subsys_get_crash_status(struct subsys_device *dev)
{
return false;
}
static inline void notify_proxy_vote(struct device *device) { }
static inline void notify_proxy_unvote(struct device *device) { }
static inline void notify_before_auth_and_reset(struct device *device) { }
#endif
/* CONFIG_MSM_SUBSYSTEM_RESTART */
/* Helper wrappers */
static inline void wakeup_source_trash(struct wakeup_source *ws)
{
if (!ws)
return;
wakeup_source_remove(ws);
__pm_relax(ws);
}
#endif