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

Commit b68814fb authored by Lina Iyer's avatar Lina Iyer
Browse files

drivers: mailbox: qti-tcs: mark TCS busy until an IRQ response



A TCS may be marked idle as soon as the request is sent from the TCS
even though the response interrupt is pending. This may lead to
overwriting existing TCS requests even before the request is done.

Use an atomic variable to mark the TCS as busy until the interrupt
is received for the request.

Change-Id: I3d313fd18f5de620a0aa1daa366959a3fa396ddc
Signed-off-by: default avatarLina Iyer <ilina@codeaurora.org>
parent 68e85fd7
Loading
Loading
Loading
Loading
+25 −10
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@

#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__

#include <linux/atomic.h>
#include <linux/bitmap.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
@@ -132,6 +133,7 @@ struct tcs_drv {
	int num_tcs;
	struct workqueue_struct *wq;
	struct tcs_response_pool *resp_pool;
	atomic_t tcs_in_use[TCS_TYPE_NR * MAX_TCS_PER_TYPE];
};

static void tcs_notify_tx_done(unsigned long data);
@@ -228,9 +230,12 @@ static inline void write_tcs_reg_sync(void __iomem *base, int reg, int m, int n,
	} while (1);
}

static inline bool tcs_is_free(void __iomem *base, int m)
static inline bool tcs_is_free(struct tcs_drv *drv, int m)
{
	return read_tcs_reg(base, TCS_DRV_STATUS, m, 0);
	void __iomem *base = drv->reg_base;

	return read_tcs_reg(base, TCS_DRV_STATUS, m, 0) &&
			!atomic_read(&drv->tcs_in_use[m]);
}

static inline struct tcs_mbox *get_tcs_from_index(struct tcs_drv *drv, int m)
@@ -400,6 +405,13 @@ static irqreturn_t tcs_irq_handler(int irq, void *p)
	/* Clear the TCS IRQ status */
	write_tcs_reg(base, TCS_DRV_IRQ_CLEAR, 0, 0, irq_clear);

	/* Mark the TCS as free */
	for (m = 0; irq_status >= BIT(m); m++) {
		if (!(irq_status & BIT(m)))
			continue;
		atomic_set(&drv->tcs_in_use[m], 0);
	}

	return IRQ_HANDLED;
}

@@ -446,7 +458,7 @@ static void tcs_notify_timeout(struct work_struct *work)
	 * request, the TCS would be blocked forever waiting on the response.
	 * There is no way to recover from this case.
	 */
	if (!tcs_is_free(drv->reg_base, m)) {
	if (!tcs_is_free(drv, m)) {
		bool pending = false;
		struct tcs_cmd *cmd;
		int i;
@@ -513,8 +525,8 @@ static void __tcs_buffer_write(struct tcs_drv *drv, int d, int m, int n,
	write_tcs_reg(base, TCS_DRV_CMD_ENABLE, m, 0, cmd_enable);

	if (trigger) {
		/* Clear pending interrupt bits for this TCS, OK to not lock */
		write_tcs_reg(base, TCS_DRV_IRQ_CLEAR, 0, 0, BIT(m));
		/* Mark the TCS as busy */
		atomic_set(&drv->tcs_in_use[m], 1);
		/* HW req: Clear the DRV_CONTROL and enable TCS again */
		write_tcs_reg_sync(base, TCS_DRV_CONTROL, m, 0, 0);
		write_tcs_reg_sync(base, TCS_DRV_CONTROL, m, 0, enable);
@@ -542,7 +554,7 @@ static bool tcs_drv_is_idle(struct mbox_controller *mbox)
		tcs = get_tcs_of_type(drv, WAKE_TCS);

	for (m = tcs->tcs_offset; m < tcs->tcs_offset + tcs->num_tcs; m++)
		if (!tcs_is_free(drv->reg_base, m))
		if (!tcs_is_free(drv, m))
			return false;

	return true;
@@ -560,7 +572,7 @@ static void wait_for_req_inflight(struct tcs_drv *drv, struct tcs_mbox *tcs,
		for (i = 1; i > tcs->tcs_mask; i = i << 1) {
			if (!(tcs->tcs_mask & i))
				continue;
			if (tcs_is_free(drv->reg_base, i))
			if (tcs_is_free(drv, i))
				continue;
			curr_enabled = read_tcs_reg(drv->reg_base,
						TCS_DRV_CMD_ENABLE, i, 0);
@@ -588,7 +600,7 @@ static int find_free_tcs(struct tcs_mbox *tcs)

	/* Loop until we find a free AMC */
	do {
		if (tcs_is_free(tcs->drv->reg_base, tcs->tcs_offset + m)) {
		if (tcs_is_free(tcs->drv, tcs->tcs_offset + m)) {
			slot = m * tcs->ncpt;
			break;
		}
@@ -720,7 +732,7 @@ static int tcs_mbox_write(struct mbox_chan *chan, struct tcs_mbox_msg *msg,
		/* Block, if we have an address from the msg in flight */
		wait_for_req_inflight(drv, tcs, msg);
		/* If the TCS is busy there is nothing to do but spin wait */
		while (!tcs_is_free(drv->reg_base, m))
		while (!tcs_is_free(drv, m))
			cpu_relax();
	}

@@ -758,7 +770,7 @@ static int tcs_mbox_invalidate(struct mbox_chan *chan)
		for (i = 0; i < tcs->num_tcs; i++) {
			m = i + tcs->tcs_offset;
			spin_lock(&tcs->tcs_m_lock[i]);
			while (!tcs_is_free(drv->reg_base, m))
			while (!tcs_is_free(drv, m))
				cpu_relax();
			__tcs_buffer_invalidate(drv->reg_base, m);
			spin_unlock(&tcs->tcs_m_lock[i]);
@@ -1119,6 +1131,9 @@ static int tcs_drv_probe(struct platform_device *pdev)
				drv->tcs[WAKE_TCS].tcs_mask;
	write_tcs_reg(drv->reg_base, TCS_DRV_IRQ_ENABLE, 0, 0, irq_mask);

	for (i = 0; i < ARRAY_SIZE(drv->tcs_in_use); i++)
		atomic_set(&drv->tcs_in_use[i], 0);

	ret = mbox_controller_register(&drv->mbox);
	if (ret)
		return ret;