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

Commit 2e540ff2 authored by Jilai Wang's avatar Jilai Wang
Browse files

msm: npu: Add mailbox controller support



In order to prevent GLINK and SMP2P driver sending IPCC irq via
ipcc_mproc mailbox to NPUQ6 when itis in sleep mode, a mailbox
controller is implemented to connect them and control if it's
allowed to send data to ipcc_mproc mailbox to trigger IPCC irq.

Change-Id: Id03a884bda605db4e75cb7dd117b04af7d601f8c
Signed-off-by: default avatarJilai Wang <jilaiw@codeaurora.org>
parent 61502d56
Loading
Loading
Loading
Loading
+22 −2
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <linux/uaccess.h>
#include <linux/mailbox/qmp.h>
#include <linux/msm-bus.h>
#include <linux/mailbox_controller.h>

#include "npu_mgr.h"

@@ -30,7 +31,7 @@
 * Defines
 * -------------------------------------------------------------------------
 */
#define NPU_MAX_MBOX_NUM	    2
#define NPU_MAX_MBOX_NUM	    4
#define NPU_MBOX_LOW_PRI	    0
#define NPU_MBOX_HIGH_PRI	    1

@@ -117,6 +118,9 @@ struct npu_mbox {
	struct mbox_chan *chan;
	struct npu_device *npu_dev;
	uint32_t id;
	uint32_t client_id;
	uint32_t signal_id;
	bool send_data_pending;
};

/**
@@ -217,6 +221,12 @@ struct npu_bwctrl {
	uint32_t num_paths;
};

struct mbox_bridge_data {
	struct mbox_controller mbox;
	struct mbox_chan *chans;
	void *priv_data;
};

struct npu_device {
	struct mutex dev_lock;

@@ -249,7 +259,9 @@ struct npu_device {
	struct npu_smmu_ctx smmu_ctx;
	struct npu_debugfs_ctx debugfs_ctx;

	struct npu_mbox mbox_aop;
	struct npu_mbox *mbox_aop;
	struct npu_mbox mbox[NPU_MAX_MBOX_NUM];
	struct mbox_bridge_data mbox_bridge_data;

	struct thermal_cooling_device *tcdev;
	struct npu_pwrctrl pwrctrl;
@@ -278,6 +290,14 @@ struct npu_client {
	struct list_head mapped_buffer_list;
};

struct ipcc_mbox_chan {
	u16 client_id;
	u16 signal_id;
	struct mbox_chan *chan;
	struct npu_mbox *npu_mbox;
	struct npu_device *npu_dev;
};

/* -------------------------------------------------------------------------
 * Function Prototypes
 * -------------------------------------------------------------------------
+209 −16
Original line number Diff line number Diff line
@@ -1727,32 +1727,225 @@ static int npu_irq_init(struct npu_device *npu_dev)
	return ret;
}

/* -------------------------------------------------------------------------
 * Mailbox
 * -------------------------------------------------------------------------
 */
static int npu_ipcc_bridge_mbox_send_data(struct mbox_chan *chan, void *data)
{
	struct ipcc_mbox_chan *ipcc_mbox_chan = chan->con_priv;
	struct npu_device *npu_dev = ipcc_mbox_chan->npu_dev;
	struct npu_host_ctx *host_ctx = &npu_dev->host_ctx;
	unsigned long flags;

	NPU_DBG("Generating IRQ for client_id: %u; signal_id: %u\n",
		ipcc_mbox_chan->client_id, ipcc_mbox_chan->signal_id);

	spin_lock_irqsave(&host_ctx->bridge_mbox_lock, flags);
	ipcc_mbox_chan->npu_mbox->send_data_pending = true;
	queue_work(host_ctx->wq, &host_ctx->bridge_mbox_work);
	spin_unlock_irqrestore(&host_ctx->bridge_mbox_lock, flags);

	return 0;
}

static void npu_ipcc_bridge_mbox_shutdown(struct mbox_chan *chan)
{
	struct ipcc_mbox_chan *ipcc_mbox_chan = chan->con_priv;

	chan->con_priv = NULL;
	kfree(ipcc_mbox_chan);
}

static struct mbox_chan *npu_ipcc_bridge_mbox_xlate(
	struct mbox_controller *mbox, const struct of_phandle_args *ph)
{
	int chan_id, i;
	struct npu_device *npu_dev;
	struct mbox_bridge_data *bridge_data;
	struct ipcc_mbox_chan *ipcc_mbox_chan;

	bridge_data = container_of(mbox, struct mbox_bridge_data, mbox);
	if (WARN_ON(!bridge_data))
		return ERR_PTR(-EINVAL);

	npu_dev = bridge_data->priv_data;

	if (ph->args_count != 2)
		return ERR_PTR(-EINVAL);

	for (chan_id = 0; chan_id < mbox->num_chans; chan_id++) {
		ipcc_mbox_chan = bridge_data->chans[chan_id].con_priv;

		if (!ipcc_mbox_chan)
			break;
		else if (ipcc_mbox_chan->client_id == ph->args[0] &&
				ipcc_mbox_chan->signal_id == ph->args[1])
			return ERR_PTR(-EBUSY);
	}

	if (chan_id >= mbox->num_chans)
		return ERR_PTR(-EBUSY);

	/* search for target mailbox */
	for (i = 0; i < NPU_MAX_MBOX_NUM; i++) {
		if (npu_dev->mbox[i].chan &&
			(npu_dev->mbox[i].client_id == ph->args[0]) &&
			(npu_dev->mbox[i].signal_id == ph->args[1])) {
			NPU_DBG("Find matched target mailbox %d\n", i);
			break;
		}
	}

	if (i == NPU_MAX_MBOX_NUM) {
		NPU_ERR("Can't find matched target mailbox %d:%d\n",
			ph->args[0], ph->args[1]);
		return ERR_PTR(-EINVAL);
	}

	ipcc_mbox_chan = kzalloc(sizeof(*ipcc_mbox_chan), GFP_KERNEL);
	if (!ipcc_mbox_chan)
		return ERR_PTR(-ENOMEM);

	ipcc_mbox_chan->client_id = ph->args[0];
	ipcc_mbox_chan->signal_id = ph->args[1];
	ipcc_mbox_chan->chan = &bridge_data->chans[chan_id];
	ipcc_mbox_chan->npu_dev = npu_dev;
	ipcc_mbox_chan->chan->con_priv = ipcc_mbox_chan;
	ipcc_mbox_chan->npu_mbox = &npu_dev->mbox[i];

	NPU_DBG("New mailbox channel: %u for client_id: %u; signal_id: %u\n",
		chan_id, ipcc_mbox_chan->client_id,
		ipcc_mbox_chan->signal_id);

	return ipcc_mbox_chan->chan;
}

static const struct mbox_chan_ops ipcc_mbox_chan_ops = {
	.send_data = npu_ipcc_bridge_mbox_send_data,
	.shutdown = npu_ipcc_bridge_mbox_shutdown
};

static int npu_setup_ipcc_bridge_mbox(struct npu_device *npu_dev)
{
	int i, j, ret;
	int num_chans = 0;
	struct mbox_controller *mbox;
	struct device_node *client_dn;
	struct of_phandle_args curr_ph;
	struct device *dev = &npu_dev->pdev->dev;
	struct device_node *controller_dn = dev->of_node;
	struct mbox_bridge_data *mbox_data = &npu_dev->mbox_bridge_data;

	NPU_DBG("Setup ipcc brige mbox\n");
	/*
	 * Find out the number of clients interested in this mailbox
	 * and create channels accordingly.
	 */
	for_each_node_with_property(client_dn, "mboxes") {
		if (!of_device_is_available(client_dn)) {
			NPU_DBG("No node available\n");
			continue;
		}
		i = of_count_phandle_with_args(client_dn,
						"mboxes", "#mbox-cells");
		for (j = 0; j < i; j++) {
			ret = of_parse_phandle_with_args(client_dn, "mboxes",
						"#mbox-cells", j, &curr_ph);
			of_node_put(curr_ph.np);
			if (!ret && curr_ph.np == controller_dn) {
				NPU_DBG("Found a client\n");
				num_chans++;
				break;
			}
		}
	}

	/* If no clients are found, skip registering as a mbox controller */
	if (!num_chans) {
		NPU_WARN("Can't find ipcc bridge mbox client\n");
		return 0;
	}

	mbox_data->chans = devm_kcalloc(dev, num_chans,
					sizeof(struct mbox_chan), GFP_KERNEL);
	if (!mbox_data->chans)
		return -ENOMEM;

	mbox_data->priv_data = npu_dev;
	mbox = &mbox_data->mbox;
	mbox->dev = dev;
	mbox->num_chans = num_chans;
	mbox->chans = mbox_data->chans;
	mbox->ops = &ipcc_mbox_chan_ops;
	mbox->of_xlate = npu_ipcc_bridge_mbox_xlate;
	mbox->txdone_irq = false;
	mbox->txdone_poll = false;

	return mbox_controller_register(mbox);
}

static int npu_mbox_init(struct npu_device *npu_dev)
{
	struct platform_device *pdev = npu_dev->pdev;
	struct npu_mbox *mbox_aop = &npu_dev->mbox_aop;
	struct npu_mbox *mbox = NULL;
	struct property *prop;
	const char *mbox_name;
	uint32_t index = 0;
	int ret = 0;
	struct of_phandle_args curr_ph;

	if (of_find_property(pdev->dev.of_node, "mboxes", NULL)) {
		mbox_aop->client.dev = &pdev->dev;
		mbox_aop->client.tx_block = true;
		mbox_aop->client.tx_tout = MBOX_OP_TIMEOUTMS;
		mbox_aop->client.knows_txdone = false;
	if (!of_get_property(pdev->dev.of_node, "mbox-names", NULL)  ||
		!of_find_property(pdev->dev.of_node, "mboxes", NULL)) {
		NPU_WARN("requires mbox-names and mboxes property\n");
		return 0;
	}

		mbox_aop->chan = mbox_request_channel(&mbox_aop->client, 0);
		if (IS_ERR(mbox_aop->chan)) {
			NPU_WARN("aop mailbox is not available\n");
			mbox_aop->chan = NULL;
	of_property_for_each_string(pdev->dev.of_node,
		"mbox-names", prop, mbox_name) {
		NPU_DBG("setup mbox[%d] %s\n", index, mbox_name);
		mbox = &npu_dev->mbox[index];
		mbox->client.dev = &pdev->dev;
		mbox->client.knows_txdone = true;
		mbox->chan = mbox_request_channel(&mbox->client, index);
		if (IS_ERR(mbox->chan)) {
			NPU_WARN("mailbox %s is not available\n", mbox_name);
			mbox->chan = NULL;
		} else if (!strcmp(mbox_name, "aop")) {
			npu_dev->mbox_aop = mbox;
		} else {
			ret = of_parse_phandle_with_args(pdev->dev.of_node,
				"mboxes", "#mbox-cells", index, &curr_ph);
			of_node_put(curr_ph.np);
			if (ret) {
				NPU_WARN("can't get mailbox %s args\n",
					mbox_name);
			} else {
				mbox->client_id = curr_ph.args[0];
				mbox->signal_id = curr_ph.args[1];
				NPU_DBG("argument for mailbox %x is %x %x\n",
					mbox_name, curr_ph.args[0],
					curr_ph.args[1]);
			}
		}
		index++;
	}

	return 0;
	return npu_setup_ipcc_bridge_mbox(npu_dev);
}

static void npu_mbox_deinit(struct npu_device *npu_dev)
{
	if (npu_dev->mbox_aop.chan) {
		mbox_free_channel(npu_dev->mbox_aop.chan);
		npu_dev->mbox_aop.chan = NULL;
	int i;

	mbox_controller_unregister(&npu_dev->mbox_bridge_data.mbox);

	for (i = 0; i < NPU_MAX_MBOX_NUM; i++) {
		if (!npu_dev->mbox[i].chan)
			continue;

		mbox_free_channel(npu_dev->mbox[i].chan);
		npu_dev->mbox[i].chan = NULL;
	}
}

+95 −9
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@
 */
static void npu_ipc_irq_work(struct work_struct *work);
static void npu_wdg_err_irq_work(struct work_struct *work);
static void npu_bridge_mbox_work(struct work_struct *work);
static void npu_disable_fw_work(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);
@@ -193,19 +195,16 @@ int unload_fw(struct npu_device *npu_dev)
	return 0;
}

int enable_fw(struct npu_device *npu_dev)
static int enable_fw_nolock(struct npu_device *npu_dev)
{
	struct npu_host_ctx *host_ctx = &npu_dev->host_ctx;
	int ret = 0;

	mutex_lock(&host_ctx->lock);

	if (host_ctx->fw_state == FW_UNLOADED) {
		ret = load_fw_nolock(npu_dev,
			host_ctx->auto_pil_disable ? true : false);
		if (ret) {
			NPU_ERR("load fw failed\n");
			mutex_unlock(&host_ctx->lock);
			return ret;
		}

@@ -220,7 +219,6 @@ int enable_fw(struct npu_device *npu_dev)
	if (host_ctx->fw_state == FW_ENABLED) {
		host_ctx->fw_ref_cnt++;
		NPU_DBG("fw_ref_cnt %d\n", host_ctx->fw_ref_cnt);
		mutex_unlock(&host_ctx->lock);
		return 0;
	}

@@ -275,7 +273,7 @@ int enable_fw(struct npu_device *npu_dev)

	host_ctx->fw_error = false;
	host_ctx->fw_ref_cnt++;
	mutex_unlock(&host_ctx->lock);


enable_log:
	/* Set logging state */
@@ -294,7 +292,18 @@ int enable_fw(struct npu_device *npu_dev)
enable_sys_cache_fail:
	npu_disable_core_power(npu_dev);
enable_pw_fail:
	return ret;
}

int enable_fw(struct npu_device *npu_dev)
{
	struct npu_host_ctx *host_ctx = &npu_dev->host_ctx;
	int ret;

	mutex_lock(&host_ctx->lock);
	ret = enable_fw_nolock(npu_dev);
	mutex_unlock(&host_ctx->lock);

	return ret;
}

@@ -449,7 +458,7 @@ static int npu_notifier_cb(struct notifier_block *this, unsigned long code,
			break;
		}

		npu_cc_reg_write(npu_dev, NPU_CC_NPU_CPC_RSC_CTRL, 0);
		npu_cc_reg_write(npu_dev, NPU_CC_NPU_CPC_RSC_CTRL, 3);

		/* Clear control/status registers */
		REGW(npu_dev, REG_NPU_FW_CTRL_STATUS, 0x0);
@@ -516,6 +525,7 @@ int npu_host_init(struct npu_device *npu_dev)
	init_completion(&host_ctx->fw_bringup_done);
	init_completion(&host_ctx->fw_shutdown_done);
	mutex_init(&host_ctx->lock);
	spin_lock_init(&host_ctx->bridge_mbox_lock);
	atomic_set(&host_ctx->ipc_trans_id, 1);

	host_ctx->npu_dev = npu_dev;
@@ -534,6 +544,9 @@ int npu_host_init(struct npu_device *npu_dev)
	} else {
		INIT_WORK(&host_ctx->ipc_irq_work, npu_ipc_irq_work);
		INIT_WORK(&host_ctx->wdg_err_irq_work, npu_wdg_err_irq_work);
		INIT_WORK(&host_ctx->bridge_mbox_work, npu_bridge_mbox_work);
		INIT_DELAYED_WORK(&host_ctx->disable_fw_work,
			npu_disable_fw_work);
	}

	if (npu_dev->hw_version != 0x20000000)
@@ -768,6 +781,79 @@ static void npu_wdg_err_irq_work(struct work_struct *work)
	host_error_hdlr(npu_dev, false);
}

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

	NPU_DBG("Enter disable fw work\n");
	host_ctx = container_of(work, struct npu_host_ctx,
		disable_fw_work.work);
	npu_dev = container_of(host_ctx, struct npu_device, host_ctx);

	mutex_lock(&host_ctx->lock);
	disable_fw_nolock(npu_dev);
	host_ctx->bridge_mbox_pwr_on = false;
	mutex_unlock(&host_ctx->lock);
	NPU_DBG("Exit disable fw work\n");
}

static int npu_bridge_mbox_send_data(struct npu_host_ctx *host_ctx,
	struct npu_mbox *mbox, void *data)
{
	NPU_DBG("Generating IRQ for client_id: %u; signal_id: %u\n",
		mbox->client_id, mbox->signal_id);
	mbox_send_message(mbox->chan, NULL);
	mbox_client_txdone(mbox->chan, 0);
	mbox->send_data_pending = false;

	return 0;
}

static void npu_bridge_mbox_work(struct work_struct *work)
{
	int i, ret;
	struct npu_host_ctx *host_ctx;
	struct npu_device *npu_dev;
	unsigned long flags;

	NPU_DBG("Enter bridge mbox work\n");
	host_ctx = container_of(work, struct npu_host_ctx, bridge_mbox_work);
	npu_dev = container_of(host_ctx, struct npu_device, host_ctx);

	/* queue or modify delayed work to disable fw */
	mod_delayed_work(host_ctx->wq, &host_ctx->disable_fw_work,
		NPU_MBOX_IDLE_TIMEOUT);

	mutex_lock(&host_ctx->lock);
	if (host_ctx->fw_state == FW_UNLOADED) {
		NPU_WARN("NPU fw is not loaded\n");
		mutex_unlock(&host_ctx->lock);
		return;
	}

	if (!host_ctx->bridge_mbox_pwr_on) {
		ret = enable_fw_nolock(npu_dev);
		if (ret) {
			mutex_unlock(&host_ctx->lock);
			NPU_ERR("Enable fw failed\n");
			return;
		}
		host_ctx->bridge_mbox_pwr_on = true;
		NPU_DBG("Fw is enabled by mbox\n");
	}

	spin_lock_irqsave(&host_ctx->bridge_mbox_lock, flags);
	for (i = 0; i < NPU_MAX_MBOX_NUM; i++)
		if (npu_dev->mbox[i].send_data_pending)
			npu_bridge_mbox_send_data(host_ctx,
				&npu_dev->mbox[i], NULL);

	spin_unlock_irqrestore(&host_ctx->bridge_mbox_lock, flags);
	mutex_unlock(&host_ctx->lock);
	NPU_DBG("Exit bridge mbox work\n");
}

static void turn_off_fw_logging(struct npu_device *npu_dev)
{
	struct ipc_cmd_log_state_pkt log_packet;
@@ -828,7 +914,7 @@ static int npu_notify_aop(struct npu_device *npu_dev, bool on)
	struct qmp_pkt pkt;
	int buf_size, rc = 0;

	if (!npu_dev->mbox_aop.chan) {
	if (!npu_dev->mbox_aop || !npu_dev->mbox_aop->chan) {
		NPU_WARN("aop mailbox channel is not available\n");
		return 0;
	}
@@ -845,7 +931,7 @@ static int npu_notify_aop(struct npu_device *npu_dev, bool on)
	pkt.size = (buf_size + 3) & ~0x3;
	pkt.data = buf;

	rc = mbox_send_message(npu_dev->mbox_aop.chan, &pkt);
	rc = mbox_send_message(npu_dev->mbox_aop->chan, &pkt);
	if (rc < 0)
		NPU_ERR("qmp message send failed, ret=%d\n", rc);

+6 −0
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@
#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 NPU_MBOX_IDLE_TIMEOUT_MS 500 /* set for 500ms */
#define NPU_MBOX_IDLE_TIMEOUT msecs_to_jiffies(NPU_MBOX_IDLE_TIMEOUT_MS)
#define FIRMWARE_VERSION 0x00001000
#define MAX_LOADED_NETWORK 32
#define NPU_IPC_BUF_LENGTH 512
@@ -74,6 +76,8 @@ struct npu_host_ctx {
	int32_t power_vote_num;
	struct work_struct ipc_irq_work;
	struct work_struct wdg_err_irq_work;
	struct work_struct bridge_mbox_work;
	struct delayed_work disable_fw_work;
	struct workqueue_struct *wq;
	struct completion misc_cmd_done;
	struct completion fw_deinit_done;
@@ -96,6 +100,8 @@ struct npu_host_ctx {
	uint32_t misc_cmd_result;
	struct notifier_block nb;
	void *notif_hdle;
	spinlock_t bridge_mbox_lock;
	bool bridge_mbox_pwr_on;
};

struct npu_device;