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

Commit 95442682 authored by Arnaud Pouliquen's avatar Arnaud Pouliquen Committed by Greg Kroah-Hartman
Browse files

mailbox: stm32_ipcc: add spinlock to fix channels concurrent access



commit dba9a3dfe912dc47c9dbc9ba1f5f65adbf9aea0f upstream.

Add spinlock protection on IPCC register update to avoid race condition.
Without this fix, stm32_ipcc_set_bits and stm32_ipcc_clr_bits can be
called in parallel for different channels. This results in register
corruptions.

Signed-off-by: default avatarArnaud Pouliquen <arnaud.pouliquen@st.com>
Reviewed-by: default avatarFabien Dessenne <fabien.dessenne@st.com>
Signed-off-by: default avatarJassi Brar <jaswinder.singh@linaro.org>
Signed-off-by: default avatarMathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent f9367f94
Loading
Loading
Loading
Loading
+27 −10
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ struct stm32_ipcc {
	void __iomem *reg_base;
	void __iomem *reg_proc;
	struct clk *clk;
	spinlock_t lock; /* protect access to IPCC registers */
	int irqs[IPCC_IRQ_NUM];
	int wkp;
	u32 proc_id;
@@ -58,14 +59,24 @@ struct stm32_ipcc {
	u32 xmr;
};

static inline void stm32_ipcc_set_bits(void __iomem *reg, u32 mask)
static inline void stm32_ipcc_set_bits(spinlock_t *lock, void __iomem *reg,
				       u32 mask)
{
	unsigned long flags;

	spin_lock_irqsave(lock, flags);
	writel_relaxed(readl_relaxed(reg) | mask, reg);
	spin_unlock_irqrestore(lock, flags);
}

static inline void stm32_ipcc_clr_bits(void __iomem *reg, u32 mask)
static inline void stm32_ipcc_clr_bits(spinlock_t *lock, void __iomem *reg,
				       u32 mask)
{
	unsigned long flags;

	spin_lock_irqsave(lock, flags);
	writel_relaxed(readl_relaxed(reg) & ~mask, reg);
	spin_unlock_irqrestore(lock, flags);
}

static irqreturn_t stm32_ipcc_rx_irq(int irq, void *data)
@@ -92,7 +103,7 @@ static irqreturn_t stm32_ipcc_rx_irq(int irq, void *data)

		mbox_chan_received_data(&ipcc->controller.chans[chan], NULL);

		stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XSCR,
		stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XSCR,
				    RX_BIT_CHAN(chan));

		ret = IRQ_HANDLED;
@@ -121,7 +132,7 @@ static irqreturn_t stm32_ipcc_tx_irq(int irq, void *data)
		dev_dbg(dev, "%s: chan:%d tx\n", __func__, chan);

		/* mask 'tx channel free' interrupt */
		stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XMR,
		stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR,
				    TX_BIT_CHAN(chan));

		mbox_chan_txdone(&ipcc->controller.chans[chan], 0);
@@ -141,10 +152,12 @@ static int stm32_ipcc_send_data(struct mbox_chan *link, void *data)
	dev_dbg(ipcc->controller.dev, "%s: chan:%d\n", __func__, chan);

	/* set channel n occupied */
	stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XSCR, TX_BIT_CHAN(chan));
	stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XSCR,
			    TX_BIT_CHAN(chan));

	/* unmask 'tx channel free' interrupt */
	stm32_ipcc_clr_bits(ipcc->reg_proc + IPCC_XMR, TX_BIT_CHAN(chan));
	stm32_ipcc_clr_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR,
			    TX_BIT_CHAN(chan));

	return 0;
}
@@ -163,7 +176,8 @@ static int stm32_ipcc_startup(struct mbox_chan *link)
	}

	/* unmask 'rx channel occupied' interrupt */
	stm32_ipcc_clr_bits(ipcc->reg_proc + IPCC_XMR, RX_BIT_CHAN(chan));
	stm32_ipcc_clr_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR,
			    RX_BIT_CHAN(chan));

	return 0;
}
@@ -175,7 +189,7 @@ static void stm32_ipcc_shutdown(struct mbox_chan *link)
					       controller);

	/* mask rx/tx interrupt */
	stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XMR,
	stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR,
			    RX_BIT_CHAN(chan) | TX_BIT_CHAN(chan));

	clk_disable_unprepare(ipcc->clk);
@@ -208,6 +222,8 @@ static int stm32_ipcc_probe(struct platform_device *pdev)
	if (!ipcc)
		return -ENOMEM;

	spin_lock_init(&ipcc->lock);

	/* proc_id */
	if (of_property_read_u32(np, "st,proc-id", &ipcc->proc_id)) {
		dev_err(dev, "Missing st,proc-id\n");
@@ -259,9 +275,10 @@ static int stm32_ipcc_probe(struct platform_device *pdev)
	}

	/* mask and enable rx/tx irq */
	stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XMR,
	stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XMR,
			    RX_BIT_MASK | TX_BIT_MASK);
	stm32_ipcc_set_bits(ipcc->reg_proc + IPCC_XCR, XCR_RXOIE | XCR_TXOIE);
	stm32_ipcc_set_bits(&ipcc->lock, ipcc->reg_proc + IPCC_XCR,
			    XCR_RXOIE | XCR_TXOIE);

	/* wakeup */
	if (of_property_read_bool(np, "wakeup-source")) {