Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4bbb4701 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: npu: Support delayed unloading NPU fw"

parents b4da3bde d151c07f
Loading
Loading
Loading
Loading
+92 −0
Original line number Diff line number Diff line
@@ -68,6 +68,18 @@ static ssize_t npu_show_perf_mode_override(struct device *dev,
static ssize_t npu_store_perf_mode_override(struct device *dev,
					  struct device_attribute *attr,
					  const char *buf, size_t count);
static ssize_t npu_show_fw_unload_delay_ms(struct device *dev,
					 struct device_attribute *attr,
					 char *buf);
static ssize_t npu_store_fw_unload_delay_ms(struct device *dev,
					  struct device_attribute *attr,
					  const char *buf, size_t count);
static ssize_t npu_show_fw_state(struct device *dev,
					 struct device_attribute *attr,
					 char *buf);
static ssize_t npu_store_fw_state(struct device *dev,
					  struct device_attribute *attr,
					  const char *buf, size_t count);
static void npu_suspend_devbw(struct npu_device *npu_dev);
static void npu_resume_devbw(struct npu_device *npu_dev);
static bool npu_is_post_clock(const char *clk_name);
@@ -166,11 +178,16 @@ static DEVICE_ATTR(caps, 0444, npu_show_capabilities, NULL);
static DEVICE_ATTR(pwr, 0644, npu_show_pwr_state, npu_store_pwr_state);
static DEVICE_ATTR(perf_mode_override, 0644,
	npu_show_perf_mode_override, npu_store_perf_mode_override);
static DEVICE_ATTR(fw_unload_delay_ms, 0644,
	npu_show_fw_unload_delay_ms, npu_store_fw_unload_delay_ms);
static DEVICE_ATTR(fw_state, 0644, npu_show_fw_state, npu_store_fw_state);

static struct attribute *npu_fs_attrs[] = {
	&dev_attr_caps.attr,
	&dev_attr_pwr.attr,
	&dev_attr_perf_mode_override.attr,
	&dev_attr_fw_state.attr,
	&dev_attr_fw_unload_delay_ms.attr,
	NULL
};

@@ -307,6 +324,81 @@ static ssize_t npu_store_perf_mode_override(struct device *dev,
	return count;
}

/* -------------------------------------------------------------------------
 * SysFS - Delayed FW unload
 * -------------------------------------------------------------------------
 */
static ssize_t npu_show_fw_unload_delay_ms(struct device *dev,
					 struct device_attribute *attr,
					 char *buf)
{
	struct npu_device *npu_dev = dev_get_drvdata(dev);

	return scnprintf(buf, PAGE_SIZE, "%d\n",
		npu_dev->host_ctx.fw_unload_delay_ms);
}

static ssize_t npu_store_fw_unload_delay_ms(struct device *dev,
					  struct device_attribute *attr,
					  const char *buf, size_t count)
{
	struct npu_device *npu_dev = dev_get_drvdata(dev);
	uint32_t val;
	int rc;

	rc = kstrtou32(buf, 10, &val);
	if (rc) {
		pr_err("Invalid input for fw unload delay setting\n");
		return -EINVAL;
	}

	npu_dev->host_ctx.fw_unload_delay_ms = val;
	pr_debug("setting fw_unload_delay_ms to %d\n", val);

	return count;
}

/* -------------------------------------------------------------------------
 * SysFS - firmware state
 * -------------------------------------------------------------------------
 */
static ssize_t npu_show_fw_state(struct device *dev,
					 struct device_attribute *attr,
					 char *buf)
{
	struct npu_device *npu_dev = dev_get_drvdata(dev);

	return scnprintf(buf, PAGE_SIZE, "%s\n",
			(npu_dev->host_ctx.fw_state == FW_ENABLED) ?
			"on" : "off");
}

static ssize_t npu_store_fw_state(struct device *dev,
					  struct device_attribute *attr,
					  const char *buf, size_t count)
{
	struct npu_device *npu_dev = dev_get_drvdata(dev);
	bool enable = false;
	int rc;

	if (strtobool(buf, &enable) < 0)
		return -EINVAL;

	if (enable) {
		pr_debug("%s: fw init\n", __func__);
		rc = fw_init(npu_dev);
		if (rc) {
			pr_err("fw init failed\n");
			return rc;
		}
	} else {
		pr_debug("%s: fw deinit\n", __func__);
		fw_deinit(npu_dev, false, true);
	}

	return count;
}

/* -------------------------------------------------------------------------
 * Power Related
 * -------------------------------------------------------------------------
+1 −20
Original line number Diff line number Diff line
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -378,25 +378,6 @@ void npu_mem_unmap(struct npu_client *client, int buf_hdl, uint64_t addr)
	npu_free_npu_ion_buffer(client, buf_hdl);
}

/* -------------------------------------------------------------------------
 * Functions - Work Queue
 * -------------------------------------------------------------------------
 */
void npu_destroy_wq(struct workqueue_struct *wq)
{
	destroy_workqueue(wq);
}

struct workqueue_struct *npu_create_wq(struct npu_host_ctx *host_ctx,
	const char *name, wq_hdlr_fn hdlr, struct work_struct *irq_work)
{
	struct workqueue_struct *wq = create_workqueue(name);

	INIT_WORK(irq_work, hdlr);

	return wq;
}

/* -------------------------------------------------------------------------
 * Functions - Features
 * -------------------------------------------------------------------------
+1 −5
Original line number Diff line number Diff line
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -78,10 +78,6 @@ 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);

struct workqueue_struct *npu_create_wq(struct npu_host_ctx *host_ctx,
	const char *name, wq_hdlr_fn hdlr, struct work_struct *irq_work);
void npu_destroy_wq(struct workqueue_struct *wq);

uint8_t npu_hw_clk_gating_enabled(void);
uint8_t npu_hw_log_enabled(void);

+65 −7
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@
 * -------------------------------------------------------------------------
 */
static void host_irq_wq(struct work_struct *work);
static void fw_deinit_wq(struct work_struct *work);
static void turn_off_fw_logging(struct npu_device *npu_dev);
static int wait_for_status_ready(struct npu_device *npu_dev,
	uint32_t status_reg, uint32_t status_bits);
@@ -65,6 +66,9 @@ static int npu_send_misc_cmd(struct npu_device *npu_dev, uint32_t q_idx,
static int npu_queue_event(struct npu_client *client, struct npu_kevent *evt);
static int npu_notify_dsp(struct npu_device *npu_dev, bool pwr_up);
static int npu_notify_aop(struct npu_device *npu_dev, bool on);
static void npu_destroy_wq(struct npu_host_ctx *host_ctx);
static struct workqueue_struct *npu_create_wq(struct npu_host_ctx *host_ctx,
	const char *name);

/* -------------------------------------------------------------------------
 * Function Definitions - Init / Deinit
@@ -74,7 +78,8 @@ int fw_init(struct npu_device *npu_dev)
{
	uint32_t reg_val;
	struct npu_host_ctx *host_ctx = &npu_dev->host_ctx;
	int ret = 0;
	int ret = 0, retry_cnt = 3;
	bool need_retry;

	mutex_lock(&host_ctx->lock);
	if (host_ctx->fw_state == FW_ENABLED) {
@@ -83,6 +88,8 @@ int fw_init(struct npu_device *npu_dev)
		return 0;
	}

retry:
	need_retry = false;
	npu_notify_aop(npu_dev, true);

	if (npu_enable_core_power(npu_dev)) {
@@ -139,14 +146,19 @@ int fw_init(struct npu_device *npu_dev)
	REGW(npu_dev, REG_NPU_HOST_CTRL_STATUS, reg_val);

	/* Initialize the host side IPC */
	npu_host_ipc_pre_init(npu_dev);
	ret = npu_host_ipc_pre_init(npu_dev);
	if (ret) {
		pr_err("npu_host_ipc_pre_init failed %d\n", ret);
		goto enable_post_clk_fail;
	}

	/* Keep reading ctrl status until NPU is ready */
	pr_debug("waiting for status ready from fw\n");

	if (wait_for_status_ready(npu_dev, REG_NPU_FW_CTRL_STATUS,
		FW_CTRL_STATUS_MAIN_THREAD_READY_BIT)) {
		FW_CTRL_STATUS_MAIN_THREAD_READY_VAL)) {
		ret = -EPERM;
		need_retry = true;
		goto wait_fw_ready_fail;
	}

@@ -183,7 +195,13 @@ int fw_init(struct npu_device *npu_dev)
enable_sys_cache_fail:
	npu_disable_core_power(npu_dev);
enable_pw_fail:
	npu_notify_aop(npu_dev, false);
	host_ctx->fw_state = FW_DISABLED;
	if (need_retry && (retry_cnt > 0)) {
		retry_cnt--;
		pr_warn("retry fw init %d\n", retry_cnt);
		goto retry;
	}
	mutex_unlock(&host_ctx->lock);
	return ret;
}
@@ -284,8 +302,7 @@ int npu_host_init(struct npu_device *npu_dev)
	mutex_init(&host_ctx->lock);
	atomic_set(&host_ctx->ipc_trans_id, 1);

	host_ctx->wq = npu_create_wq(host_ctx, "irq_hdl", host_irq_wq,
		&host_ctx->irq_work);
	host_ctx->wq = npu_create_wq(host_ctx, "npu_wq");
	if (!host_ctx->wq)
		sts = -EPERM;

@@ -296,7 +313,7 @@ void npu_host_deinit(struct npu_device *npu_dev)
{
	struct npu_host_ctx *host_ctx = &npu_dev->host_ctx;

	npu_destroy_wq(host_ctx->wq);
	npu_destroy_wq(host_ctx);
	mutex_destroy(&host_ctx->lock);
}

@@ -383,6 +400,40 @@ static void host_irq_wq(struct work_struct *work)
	host_session_msg_hdlr(npu_dev);
}

static void fw_deinit_wq(struct work_struct *work)
{
	struct npu_host_ctx *host_ctx;
	struct npu_device *npu_dev;

	pr_debug("%s: deinit fw\n", __func__);
	host_ctx = container_of(work, struct npu_host_ctx, fw_deinit_work.work);
	npu_dev = container_of(host_ctx, struct npu_device, host_ctx);

	if (atomic_read(&host_ctx->fw_deinit_work_cnt) == 0)
		return;

	do {
		fw_deinit(npu_dev, false, true);
	} while (!atomic_dec_and_test(&host_ctx->fw_deinit_work_cnt));
}

static void npu_destroy_wq(struct npu_host_ctx *host_ctx)
{
	flush_delayed_work(&host_ctx->fw_deinit_work);
	destroy_workqueue(host_ctx->wq);
}

static struct workqueue_struct *npu_create_wq(struct npu_host_ctx *host_ctx,
	const char *name)
{
	struct workqueue_struct *wq = create_workqueue(name);

	INIT_WORK(&host_ctx->irq_work, host_irq_wq);
	INIT_DELAYED_WORK(&host_ctx->fw_deinit_work, fw_deinit_wq);

	return wq;
}

static void turn_off_fw_logging(struct npu_device *npu_dev)
{
	struct ipc_cmd_log_state_pkt log_packet;
@@ -1419,7 +1470,14 @@ int32_t npu_host_unload_network(struct npu_client *client,
	if (ret)
		pr_err("network unload failed to set power level\n");
	mutex_unlock(&host_ctx->lock);
	if (host_ctx->fw_unload_delay_ms) {
		flush_delayed_work(&host_ctx->fw_deinit_work);
		atomic_inc(&host_ctx->fw_deinit_work_cnt);
		queue_delayed_work(host_ctx->wq, &host_ctx->fw_deinit_work,
			msecs_to_jiffies(host_ctx->fw_unload_delay_ms));
	} else {
		fw_deinit(npu_dev, false, true);
	}
	return ret;
}

+4 −1
Original line number Diff line number Diff line
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -78,6 +78,8 @@ struct npu_host_ctx {
	int32_t fw_ref_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 loopback_done;
	struct completion fw_deinit_done;
@@ -86,6 +88,7 @@ struct npu_host_ctx {
	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_exeute_cnt;