lkml.org 
[lkml]   [2019]   [Dec]   [3]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.19 302/321] mailbox: stm32_ipcc: add spinlock to fix channels concurrent access
    Date
    From: Arnaud Pouliquen <arnaud.pouliquen@st.com>

    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: Arnaud Pouliquen <arnaud.pouliquen@st.com>
    Reviewed-by: Fabien Dessenne <fabien.dessenne@st.com>
    Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
    Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    drivers/mailbox/stm32-ipcc.c | 37 +++++++++++++++++++++++++++----------
    1 file changed, 27 insertions(+), 10 deletions(-)

    --- a/drivers/mailbox/stm32-ipcc.c
    +++ b/drivers/mailbox/stm32-ipcc.c
    @@ -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

    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
    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 m
    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 mbo
    }

    /* 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 m
    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 platf
    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 platf
    }

    /* 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")) {

    \
     
     \ /
      Last update: 2019-12-04 00:00    [W:7.816 / U:0.132 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site