Skip to content
Snippets Groups Projects
Commit e70e616a authored by Joy Zou's avatar Joy Zou Committed by Michele Cirinei
Browse files

[PATCH] LF-5352: dmaengine: imx-sdma: fix the potential access registers without clock


The issue can be triggered with the sdma pm_runtime false.

If the dma client try to use sdma in boot phase, it may cause
system hang. The sdma driver really enable clk after sdma load
firmware, the clk_disable_unused will disable the SDMA1_ROOT_CLK
before the sdma driver enable clk. This issue will comes if access
the sdma register when the SDMA1_ROOT_CLK is disable.

This issue is very easy to reproduce with hdmi connected displayer
on imx7d-sbd board which the i2c will use sdma early before sdma
firmware loaded and clock enabled.

The client i2c calls dmaengine_prep_slave_single->device_prep_slave_sg
->sdma_prep_slave_sg->sdma_config_write->sdma_config_channel to get
descriptor in boot phase. Then, the function sdma_config_channel will
access sdma registers. If clk is disable will cause system hang.

This patch adds clk_enable and clk_disable to make sure the clk
enable before access sdma hardware.

Signed-off-by: default avatarJoy Zou <joy.zou@nxp.com>
Reviewed-by: default avatarShengjiu Wang <shengjiu.wang@nxp.com>
Reviewed-by: default avatarDong Aisheng <aisheng.dong@nxp.com>
Acked-by: default avatarJason Liu <jason.hui.liu@nxp.com>
parent 0455c5b5
No related branches found
No related tags found
1 merge request!152[PATCH] LF-5352: dmaengine: imx-sdma: fix the potential access registers without clock
...@@ -708,6 +708,30 @@ MODULE_DEVICE_TABLE(of, sdma_dt_ids); ...@@ -708,6 +708,30 @@ MODULE_DEVICE_TABLE(of, sdma_dt_ids);
#define SDMA_H_CONFIG_ACR BIT(4) /* indicates if AHB freq /core freq = 2 or 1 */ #define SDMA_H_CONFIG_ACR BIT(4) /* indicates if AHB freq /core freq = 2 or 1 */
#define SDMA_H_CONFIG_CSM (3) /* indicates which context switch mode is selected*/ #define SDMA_H_CONFIG_CSM (3) /* indicates which context switch mode is selected*/
static void sdma_pm_clk_enable(struct sdma_engine *sdma, bool direct, bool enable)
{
if (enable) {
if (sdma->drvdata->pm_runtime)
pm_runtime_get_sync(sdma->dev);
else {
clk_enable(sdma->clk_ipg);
clk_enable(sdma->clk_ahb);
}
} else {
if (sdma->drvdata->pm_runtime) {
if (direct) {
pm_runtime_put_sync_suspend(sdma->dev);
} else {
pm_runtime_mark_last_busy(sdma->dev);
pm_runtime_put_autosuspend(sdma->dev);
}
} else {
clk_disable(sdma->clk_ipg);
clk_disable(sdma->clk_ahb);
}
}
}
static inline u32 chnenbl_ofs(struct sdma_engine *sdma, unsigned int event) static inline u32 chnenbl_ofs(struct sdma_engine *sdma, unsigned int event)
{ {
u32 chnenbl0 = sdma->drvdata->chnenbl0; u32 chnenbl0 = sdma->drvdata->chnenbl0;
...@@ -1037,13 +1061,7 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id) ...@@ -1037,13 +1061,7 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id)
struct sdma_engine *sdma = dev_id; struct sdma_engine *sdma = dev_id;
unsigned long stat; unsigned long stat;
if (sdma->drvdata->pm_runtime) sdma_pm_clk_enable(sdma, false, true);
pm_runtime_get_sync(sdma->dev);
else {
clk_enable(sdma->clk_ipg);
clk_enable(sdma->clk_ahb);
}
stat = readl_relaxed(sdma->regs + SDMA_H_INTR); stat = readl_relaxed(sdma->regs + SDMA_H_INTR);
writel_relaxed(stat, sdma->regs + SDMA_H_INTR); writel_relaxed(stat, sdma->regs + SDMA_H_INTR);
/* channel 0 is special and not handled here, see run_channel0() */ /* channel 0 is special and not handled here, see run_channel0() */
...@@ -1073,13 +1091,7 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id) ...@@ -1073,13 +1091,7 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id)
__clear_bit(channel, &stat); __clear_bit(channel, &stat);
} }
if (sdma->drvdata->pm_runtime) { sdma_pm_clk_enable(sdma, false, false);
pm_runtime_mark_last_busy(sdma->dev);
pm_runtime_put_autosuspend(sdma->dev);
} else {
clk_disable(sdma->clk_ipg);
clk_disable(sdma->clk_ahb);
}
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -1322,9 +1334,7 @@ static int sdma_terminate_all(struct dma_chan *chan) ...@@ -1322,9 +1334,7 @@ static int sdma_terminate_all(struct dma_chan *chan)
struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_channel *sdmac = to_sdma_chan(chan);
unsigned long flags; unsigned long flags;
if (sdmac->sdma->drvdata->pm_runtime) sdma_pm_clk_enable(sdmac->sdma, false, true);
pm_runtime_get_sync(sdmac->sdma->dev);
spin_lock_irqsave(&sdmac->vc.lock, flags); spin_lock_irqsave(&sdmac->vc.lock, flags);
sdma_disable_channel(chan); sdma_disable_channel(chan);
...@@ -1344,10 +1354,7 @@ static int sdma_terminate_all(struct dma_chan *chan) ...@@ -1344,10 +1354,7 @@ static int sdma_terminate_all(struct dma_chan *chan)
spin_unlock_irqrestore(&sdmac->vc.lock, flags); spin_unlock_irqrestore(&sdmac->vc.lock, flags);
if (sdmac->sdma->drvdata->pm_runtime) { sdma_pm_clk_enable(sdmac->sdma, false, false);
pm_runtime_mark_last_busy(sdmac->sdma->dev);
pm_runtime_put_autosuspend(sdmac->sdma->dev);
}
return 0; return 0;
} }
...@@ -1753,9 +1760,7 @@ static void sdma_free_chan_resources(struct dma_chan *chan) ...@@ -1753,9 +1760,7 @@ static void sdma_free_chan_resources(struct dma_chan *chan)
if (unlikely(!sdmac->sdma->fw_data)) if (unlikely(!sdmac->sdma->fw_data))
return; return;
if (sdmac->sdma->drvdata->pm_runtime) sdma_pm_clk_enable(sdmac->sdma, false, true);
pm_runtime_get_sync(sdmac->sdma->dev);
sdma_terminate_all(chan); sdma_terminate_all(chan);
sdma_channel_synchronize(chan); sdma_channel_synchronize(chan);
...@@ -1771,11 +1776,7 @@ static void sdma_free_chan_resources(struct dma_chan *chan) ...@@ -1771,11 +1776,7 @@ static void sdma_free_chan_resources(struct dma_chan *chan)
kfree(sdmac->audio_config); kfree(sdmac->audio_config);
sdmac->audio_config = NULL; sdmac->audio_config = NULL;
sdma_pm_clk_enable(sdmac->sdma, false, false);
if (sdmac->sdma->drvdata->pm_runtime) {
pm_runtime_mark_last_busy(sdmac->sdma->dev);
pm_runtime_put_autosuspend(sdmac->sdma->dev);
}
} }
static struct sdma_desc *sdma_transfer_init(struct sdma_channel *sdmac, static struct sdma_desc *sdma_transfer_init(struct sdma_channel *sdmac,
...@@ -1839,14 +1840,11 @@ static struct dma_async_tx_descriptor *sdma_prep_memcpy( ...@@ -1839,14 +1840,11 @@ static struct dma_async_tx_descriptor *sdma_prep_memcpy(
dev_dbg(sdma->dev, "memcpy: %pad->%pad, len=%zu, channel=%d.\n", dev_dbg(sdma->dev, "memcpy: %pad->%pad, len=%zu, channel=%d.\n",
&dma_src, &dma_dst, len, channel); &dma_src, &dma_dst, len, channel);
if (sdma->drvdata->pm_runtime) sdma_pm_clk_enable(sdmac->sdma, false, true);
pm_runtime_get_sync(sdmac->sdma->dev);
desc = sdma_transfer_init(sdmac, DMA_MEM_TO_MEM, desc = sdma_transfer_init(sdmac, DMA_MEM_TO_MEM,
len / SDMA_BD_MAX_CNT + 1); len / SDMA_BD_MAX_CNT + 1);
if (!desc) { if (!desc) {
if (sdma->drvdata->pm_runtime) sdma_pm_clk_enable(sdmac->sdma, true, false);
pm_runtime_put_sync_suspend(sdmac->sdma->dev);
return NULL; return NULL;
} }
...@@ -1880,10 +1878,7 @@ static struct dma_async_tx_descriptor *sdma_prep_memcpy( ...@@ -1880,10 +1878,7 @@ static struct dma_async_tx_descriptor *sdma_prep_memcpy(
bd->mode.status = param; bd->mode.status = param;
} while (len); } while (len);
if (sdmac->sdma->drvdata->pm_runtime) { sdma_pm_clk_enable(sdmac->sdma, false, false);
pm_runtime_mark_last_busy(sdmac->sdma->dev);
pm_runtime_put_autosuspend(sdmac->sdma->dev);
}
return vchan_tx_prep(&sdmac->vc, &desc->vd, flags); return vchan_tx_prep(&sdmac->vc, &desc->vd, flags);
} }
...@@ -1900,9 +1895,7 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg( ...@@ -1900,9 +1895,7 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg(
struct scatterlist *sg; struct scatterlist *sg;
struct sdma_desc *desc; struct sdma_desc *desc;
if (sdma->drvdata->pm_runtime) sdma_pm_clk_enable(sdmac->sdma, false, true);
pm_runtime_get_sync(sdmac->sdma->dev);
sdma_config_write(chan, &sdmac->slave_config, direction); sdma_config_write(chan, &sdmac->slave_config, direction);
desc = sdma_transfer_init(sdmac, direction, sg_len); desc = sdma_transfer_init(sdmac, direction, sg_len);
...@@ -1969,10 +1962,7 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg( ...@@ -1969,10 +1962,7 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg(
bd->mode.status = param; bd->mode.status = param;
} }
if (sdmac->sdma->drvdata->pm_runtime) { sdma_pm_clk_enable(sdmac->sdma, false, false);
pm_runtime_mark_last_busy(sdmac->sdma->dev);
pm_runtime_put_autosuspend(sdmac->sdma->dev);
}
return vchan_tx_prep(&sdmac->vc, &desc->vd, flags); return vchan_tx_prep(&sdmac->vc, &desc->vd, flags);
err_bd_out: err_bd_out:
...@@ -1980,8 +1970,8 @@ err_bd_out: ...@@ -1980,8 +1970,8 @@ err_bd_out:
kfree(desc); kfree(desc);
err_out: err_out:
sdmac->status = DMA_ERROR; sdmac->status = DMA_ERROR;
if (sdma->drvdata->pm_runtime) sdma_pm_clk_enable(sdmac->sdma, true, false);
pm_runtime_put_sync_suspend(sdmac->sdma->dev);
return NULL; return NULL;
} }
...@@ -1999,8 +1989,7 @@ static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic( ...@@ -1999,8 +1989,7 @@ static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic(
dev_dbg(sdma->dev, "%s channel: %d\n", __func__, channel); dev_dbg(sdma->dev, "%s channel: %d\n", __func__, channel);
if (sdma->drvdata->pm_runtime) sdma_pm_clk_enable(sdmac->sdma, false, true);
pm_runtime_get_sync(sdmac->sdma->dev);
if (sdmac->peripheral_type != IMX_DMATYPE_HDMI) if (sdmac->peripheral_type != IMX_DMATYPE_HDMI)
num_periods = buf_len / period_len; num_periods = buf_len / period_len;
...@@ -2056,10 +2045,7 @@ static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic( ...@@ -2056,10 +2045,7 @@ static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic(
i++; i++;
} }
if (sdmac->sdma->drvdata->pm_runtime) { sdma_pm_clk_enable(sdmac->sdma, false, false);
pm_runtime_mark_last_busy(sdmac->sdma->dev);
pm_runtime_put_autosuspend(sdmac->sdma->dev);
}
return vchan_tx_prep(&sdmac->vc, &desc->vd, flags); return vchan_tx_prep(&sdmac->vc, &desc->vd, flags);
err_bd_out: err_bd_out:
...@@ -2067,8 +2053,8 @@ err_bd_out: ...@@ -2067,8 +2053,8 @@ err_bd_out:
kfree(desc); kfree(desc);
err_out: err_out:
sdmac->status = DMA_ERROR; sdmac->status = DMA_ERROR;
if (sdma->drvdata->pm_runtime) sdma_pm_clk_enable(sdmac->sdma, true, false);
pm_runtime_put_sync_suspend(sdmac->sdma->dev);
return NULL; return NULL;
} }
...@@ -2187,18 +2173,13 @@ static void sdma_issue_pending(struct dma_chan *chan) ...@@ -2187,18 +2173,13 @@ static void sdma_issue_pending(struct dma_chan *chan)
struct sdma_channel *sdmac = to_sdma_chan(chan); struct sdma_channel *sdmac = to_sdma_chan(chan);
unsigned long flags; unsigned long flags;
if (sdmac->sdma->drvdata->pm_runtime) sdma_pm_clk_enable(sdmac->sdma, false, true);
pm_runtime_get_sync(sdmac->sdma->dev);
spin_lock_irqsave(&sdmac->vc.lock, flags); spin_lock_irqsave(&sdmac->vc.lock, flags);
if (vchan_issue_pending(&sdmac->vc) && !sdmac->desc) if (vchan_issue_pending(&sdmac->vc) && !sdmac->desc)
sdma_start_desc(sdmac); sdma_start_desc(sdmac);
spin_unlock_irqrestore(&sdmac->vc.lock, flags); spin_unlock_irqrestore(&sdmac->vc.lock, flags);
if (sdmac->sdma->drvdata->pm_runtime) { sdma_pm_clk_enable(sdmac->sdma, false, false);
pm_runtime_mark_last_busy(sdmac->sdma->dev);
pm_runtime_put_autosuspend(sdmac->sdma->dev);
}
} }
static void sdma_load_firmware(const struct firmware *fw, void *context) static void sdma_load_firmware(const struct firmware *fw, void *context)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment