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

Commit a4a8d06e authored by Sujeev Dias's avatar Sujeev Dias
Browse files

mhi: dev: satellite: send early SYS_ERROR event to subsystems



MHI driver sends out early notifications when device enters an
error state. Register for the callback to notify subsystems in
parallel and in advance by sending the SYS_ERROR state change
event.

CRs-Fixed: 2461030
Change-Id: I30daf30d925dbe53f5643b686dfad09f0e125ec4
Acked-by: default avatarBhaumik Vasav Bhatt <bbhatt@qti.qualcomm.com>
Signed-off-by: default avatarSujeev Dias <sdias@codeaurora.org>
parent b0a149a1
Loading
Loading
Loading
Loading
+159 −49
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2019, The Linux Foundation. All rights reserved.*/

#include <linux/async.h>
#include <linux/device.h>
#include <linux/dma-direction.h>
#include <linux/dma-mapping.h>
@@ -228,6 +229,21 @@ struct mhi_sat_packet {
	void *msg; /* incoming message */
};

enum mhi_sat_state {
	SAT_READY, /* initial state when device is presented to driver */
	SAT_RUNNING, /* subsystem can communicate with the device */
	SAT_DISCONNECTED, /* rpmsg link is down */
	SAT_FATAL_DETECT, /* device is down as fatal error was detected early */
	SAT_ERROR, /* device is down after error or graceful shutdown */
	SAT_DISABLED, /* set if rpmsg link goes down after device is down */
};

#define MHI_SAT_ACTIVE(cntrl) (cntrl->state == SAT_RUNNING)
#define MHI_SAT_FATAL_DETECT(cntrl) (cntrl->state == SAT_FATAL_DETECT)
#define MHI_SAT_ALLOW_CONNECTION(cntrl) (cntrl->state == SAT_READY || \
					 cntrl->state == SAT_DISCONNECTED)
#define MHI_SAT_IN_ERROR_STATE(cntrl) (cntrl->state >= SAT_FATAL_DETECT)

struct mhi_sat_cntrl {
	struct list_head node;

@@ -243,6 +259,7 @@ struct mhi_sat_cntrl {

	struct work_struct connect_work; /* subsystem connection worker */
	struct work_struct process_work; /* incoming packets processor */
	async_cookie_t error_cookie; /* synchronize device error handling */

	/* mhi core/controller configurations */
	u32 dev_id; /* unique device ID with BDF as per connection topology */
@@ -254,7 +271,8 @@ struct mhi_sat_cntrl {
	int num_devices; /* mhi devices current count */
	int max_devices; /* count of maximum devices for subsys/controller */
	u16 seq; /* internal sequence number for all outgoing packets */
	bool active; /* flag set if hello packet/MHI_CFG event was sent */
	enum mhi_sat_state state; /* controller state manager */
	spinlock_t state_lock; /* lock to change controller state */

	/* command completion variables */
	u16 last_cmd_seq; /* sequence number of last sent command packet */
@@ -556,6 +574,83 @@ static void mhi_sat_process_cmds(struct mhi_sat_cntrl *sat_cntrl,
	}
}

/* send sys_err command to subsystem if device asserts or is powered off */
static void mhi_sat_send_sys_err(struct mhi_sat_cntrl *sat_cntrl)
{
	struct mhi_sat_subsys *subsys = sat_cntrl->subsys;
	struct sat_tre *pkt;
	void *msg;
	int ret;

	/* flush all pending work */
	flush_work(&sat_cntrl->connect_work);
	flush_work(&sat_cntrl->process_work);

	msg = kmalloc(SAT_MSG_SIZE(1), GFP_KERNEL);

	MHI_SAT_ASSERT(!msg, "Unable to malloc for SYS_ERR message!\n");
	if (!msg)
		return;

	pkt = SAT_TRE_OFFSET(msg);
	pkt->ptr = MHI_TRE_CMD_SYS_ERR_PTR;
	pkt->dword[0] = MHI_TRE_CMD_SYS_ERR_D0;
	pkt->dword[1] = MHI_TRE_CMD_SYS_ERR_D1;

	mutex_lock(&sat_cntrl->cmd_wait_mutex);

	ret = mhi_sat_send_msg(sat_cntrl, SAT_MSG_ID_CMD,
			       SAT_RESERVED_SEQ_NUM, msg,
			       SAT_MSG_SIZE(1));
	kfree(msg);
	if (ret) {
		MHI_SAT_ERR("Failed to notify SYS_ERR cmd\n");
		mutex_unlock(&sat_cntrl->cmd_wait_mutex);
		return;
	}

	MHI_SAT_LOG("SYS_ERR command sent\n");

	/* blocking call to wait for command completion event */
	mhi_sat_wait_cmd_completion(sat_cntrl);

	mutex_unlock(&sat_cntrl->cmd_wait_mutex);
}

static void mhi_sat_error_worker(void *data, async_cookie_t cookie)
{
	struct mhi_sat_cntrl *sat_cntrl = data;
	struct mhi_sat_subsys *subsys = sat_cntrl->subsys;
	struct sat_tre *pkt;
	void *msg;
	int ret;

	MHI_SAT_LOG("Entered\n");

	/* flush all pending work */
	flush_work(&sat_cntrl->connect_work);
	flush_work(&sat_cntrl->process_work);

	msg = kmalloc(SAT_MSG_SIZE(1), GFP_KERNEL);

	MHI_SAT_ASSERT(!msg, "Unable to malloc for SYS_ERR message!\n");
	if (!msg)
		return;

	pkt = SAT_TRE_OFFSET(msg);
	pkt->ptr = MHI_TRE_EVT_MHI_STATE_PTR;
	pkt->dword[0] = MHI_TRE_EVT_MHI_STATE_D0(MHI_STATE_SYS_ERR);
	pkt->dword[1] = MHI_TRE_EVT_MHI_STATE_D1;

	ret = mhi_sat_send_msg(sat_cntrl, SAT_MSG_ID_EVT,
			       SAT_RESERVED_SEQ_NUM, msg,
			       SAT_MSG_SIZE(1));
	kfree(msg);

	MHI_SAT_LOG("SYS_ERROR state change event send %s!\n", ret ? "failure" :
		    "success");
}

static void mhi_sat_process_worker(struct work_struct *work)
{
	struct mhi_sat_cntrl *sat_cntrl = container_of(work,
@@ -578,6 +673,9 @@ static void mhi_sat_process_worker(struct work_struct *work)

		list_del(&packet->node);

		if (!MHI_SAT_ACTIVE(sat_cntrl))
			goto process_next;

		mhi_sat_process_cmds(sat_cntrl, hdr, pkt);

		/* send response event(s) */
@@ -586,6 +684,7 @@ static void mhi_sat_process_worker(struct work_struct *work)
				 SAT_MSG_SIZE(SAT_TRE_NUM_PKTS(
					      hdr->payload_size)));

process_next:
		kfree(packet);
	}

@@ -597,21 +696,26 @@ static void mhi_sat_connect_worker(struct work_struct *work)
	struct mhi_sat_cntrl *sat_cntrl = container_of(work,
					struct mhi_sat_cntrl, connect_work);
	struct mhi_sat_subsys *subsys = sat_cntrl->subsys;
	enum mhi_sat_state prev_state;
	struct sat_tre *pkt;
	void *msg;
	int ret;

	spin_lock_irq(&sat_cntrl->state_lock);
	if (!subsys->rpdev || sat_cntrl->max_devices != sat_cntrl->num_devices
	    || sat_cntrl->active)
	    || !(MHI_SAT_ALLOW_CONNECTION(sat_cntrl))) {
		spin_unlock_irq(&sat_cntrl->state_lock);
		return;
	}
	prev_state = sat_cntrl->state;
	sat_cntrl->state = SAT_RUNNING;
	spin_unlock_irq(&sat_cntrl->state_lock);

	MHI_SAT_LOG("Entered\n");

	msg = kmalloc(SAT_MSG_SIZE(3), GFP_ATOMIC);
	if (!msg)
		return;

	sat_cntrl->active = true;
		goto error_connect_work;

	pkt = SAT_TRE_OFFSET(msg);

@@ -638,11 +742,18 @@ static void mhi_sat_connect_worker(struct work_struct *work)
	kfree(msg);
	if (ret) {
		MHI_SAT_ERR("Failed to send hello packet:%d\n", ret);
		sat_cntrl->active = false;
		return;
		goto error_connect_work;
	}

	MHI_SAT_LOG("Device 0x%x sent hello packet\n", sat_cntrl->dev_id);

	return;

error_connect_work:
	spin_lock_irq(&sat_cntrl->state_lock);
	if (MHI_SAT_ACTIVE(sat_cntrl))
		sat_cntrl->state = prev_state;
	spin_unlock_irq(&sat_cntrl->state_lock);
}

static void mhi_sat_process_events(struct mhi_sat_cntrl *sat_cntrl,
@@ -687,7 +798,7 @@ static int mhi_sat_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len,
	}

	/* Inactive controller cannot process incoming commands */
	if (unlikely(!sat_cntrl->active)) {
	if (unlikely(!MHI_SAT_ACTIVE(sat_cntrl))) {
		MHI_SAT_ERR("Message for inactive controller!\n");
		return 0;
	}
@@ -722,10 +833,21 @@ static void mhi_sat_rpmsg_remove(struct rpmsg_device *rpdev)
	/* unprepare each controller/device from transfer */
	mutex_lock(&subsys->cntrl_mutex);
	list_for_each_entry(sat_cntrl, &subsys->cntrl_list, node) {
		if (!sat_cntrl->active)
			continue;
		async_synchronize_cookie(sat_cntrl->error_cookie + 1);

		sat_cntrl->active = false;
		spin_lock_irq(&sat_cntrl->state_lock);
		/*
		 * move to disabled state if early error fatal is detected
		 * and rpmsg link goes down before device remove call from
		 * mhi is received
		 */
		if (MHI_SAT_IN_ERROR_STATE(sat_cntrl)) {
			sat_cntrl->state = SAT_DISABLED;
			spin_unlock_irq(&sat_cntrl->state_lock);
			continue;
		}
		sat_cntrl->state = SAT_DISCONNECTED;
		spin_unlock_irq(&sat_cntrl->state_lock);

		flush_work(&sat_cntrl->connect_work);
		flush_work(&sat_cntrl->process_work);
@@ -804,6 +926,21 @@ static struct rpmsg_driver mhi_sat_rpmsg_driver = {
static void mhi_sat_dev_status_cb(struct mhi_device *mhi_dev,
				  enum MHI_CB mhi_cb)
{
	struct mhi_sat_device *sat_dev = mhi_device_get_devdata(mhi_dev);
	struct mhi_sat_cntrl *sat_cntrl = sat_dev->cntrl;
	struct mhi_sat_subsys *subsys = sat_cntrl->subsys;
	unsigned long flags;

	if (mhi_cb != MHI_CB_FATAL_ERROR)
		return;

	MHI_SAT_LOG("Device fatal error detected\n");
	spin_lock_irqsave(&sat_cntrl->state_lock, flags);
	if (MHI_SAT_ACTIVE(sat_cntrl))
		sat_cntrl->error_cookie = async_schedule(mhi_sat_error_worker,
							 sat_cntrl);
	sat_cntrl->state = SAT_FATAL_DETECT;
	spin_unlock_irqrestore(&sat_cntrl->state_lock, flags);
}

static void mhi_sat_dev_remove(struct mhi_device *mhi_dev)
@@ -812,9 +949,7 @@ static void mhi_sat_dev_remove(struct mhi_device *mhi_dev)
	struct mhi_sat_cntrl *sat_cntrl = sat_dev->cntrl;
	struct mhi_sat_subsys *subsys = sat_cntrl->subsys;
	struct mhi_buf *buf, *tmp;
	struct sat_tre *pkt;
	void *msg;
	int ret;
	bool send_sys_err = false;

	/* remove device node from probed list */
	mutex_lock(&sat_cntrl->list_mutex);
@@ -824,45 +959,19 @@ static void mhi_sat_dev_remove(struct mhi_device *mhi_dev)
	sat_cntrl->num_devices--;

	mutex_lock(&subsys->cntrl_mutex);
	/* prepare SYS_ERR command if first device is being removed */
	if (sat_cntrl->active) {
		sat_cntrl->active = false;

		/* flush all pending work */
		flush_work(&sat_cntrl->connect_work);
		flush_work(&sat_cntrl->process_work);

		msg = kmalloc(SAT_MSG_SIZE(1), GFP_KERNEL);

		MHI_SAT_ASSERT(!msg, "Unable to malloc for SYS_ERR message!\n");

		pkt = SAT_TRE_OFFSET(msg);
		pkt->ptr = MHI_TRE_CMD_SYS_ERR_PTR;
		pkt->dword[0] = MHI_TRE_CMD_SYS_ERR_D0;
		pkt->dword[1] = MHI_TRE_CMD_SYS_ERR_D1;

		/* acquire cmd_wait_mutex before sending command */
		mutex_lock(&sat_cntrl->cmd_wait_mutex);
	async_synchronize_cookie(sat_cntrl->error_cookie + 1);

		ret = mhi_sat_send_msg(sat_cntrl, SAT_MSG_ID_CMD,
				       SAT_RESERVED_SEQ_NUM, msg,
				       SAT_MSG_SIZE(1));
		kfree(msg);
		if (ret) {
			MHI_SAT_ERR("Failed to notify SYS_ERR\n");
			mutex_unlock(&sat_cntrl->cmd_wait_mutex);
			goto exit_sys_err_send;
		}
	/* send sys_err if first device is removed */
	spin_lock_irq(&sat_cntrl->state_lock);
	if (MHI_SAT_ACTIVE(sat_cntrl) || MHI_SAT_FATAL_DETECT(sat_cntrl))
		send_sys_err = true;
	sat_cntrl->state = SAT_ERROR;
	spin_unlock_irq(&sat_cntrl->state_lock);

		MHI_SAT_LOG("SYS_ERR command sent\n");

		/* blocking call to wait for command completion event */
		mhi_sat_wait_cmd_completion(sat_cntrl);

		mutex_unlock(&sat_cntrl->cmd_wait_mutex);
	}
	if (send_sys_err)
		mhi_sat_send_sys_err(sat_cntrl);

exit_sys_err_send:
	/* exit if some devices are still present */
	if (sat_cntrl->num_devices) {
		mutex_unlock(&subsys->cntrl_mutex);
@@ -927,6 +1036,7 @@ static int mhi_sat_dev_probe(struct mhi_device *mhi_dev,
		mutex_init(&sat_cntrl->list_mutex);
		mutex_init(&sat_cntrl->cmd_wait_mutex);
		spin_lock_init(&sat_cntrl->pkt_lock);
		spin_lock_init(&sat_cntrl->state_lock);
		INIT_WORK(&sat_cntrl->connect_work, mhi_sat_connect_worker);
		INIT_WORK(&sat_cntrl->process_work, mhi_sat_process_worker);
		INIT_LIST_HEAD(&sat_cntrl->dev_list);