]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
mailbox: mtk-cmdq: Refine DMA address handling for the command buffer
authorJason-JH Lin <jason-jh.lin@mediatek.com>
Wed, 22 Oct 2025 17:16:30 +0000 (01:16 +0800)
committerJassi Brar <jassisinghbrar@gmail.com>
Fri, 28 Nov 2025 15:31:53 +0000 (09:31 -0600)
GCE can only fetch the command buffer address from a 32-bit register.
Some SoCs support a 35-bit command buffer address for GCE, which
requires a right shift of 3 bits before setting the address into
the 32-bit register. A comment has been added to the header of
cmdq_get_shift_pa() to explain this requirement.

To prevent the GCE command buffer address from being DMA mapped beyond
its supported bit range, the DMA bit mask for the device is set during
initialization.

Additionally, to ensure the correct shift is applied when setting or
reading the register that stores the GCE command buffer address,
new APIs, cmdq_convert_gce_addr() and cmdq_revert_gce_addr(), have
been introduced for consistent operations on this register.

The variable type for the command buffer address has been standardized
to dma_addr_t to prevent handling issues caused by type mismatches.

Fixes: 0858fde496f8 ("mailbox: cmdq: variablize address shift in platform")
Signed-off-by: Jason-JH Lin <jason-jh.lin@mediatek.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Jassi Brar <jassisinghbrar@gmail.com>
drivers/mailbox/mtk-cmdq-mailbox.c
include/linux/mailbox/mtk-cmdq-mailbox.h

index 654a60f63756a47a8c1e660c02f6f84e548e9bf7..5791f80f995ab98f751cd3e1a5c1dee7b6b1a786 100644 (file)
@@ -92,6 +92,18 @@ struct gce_plat {
        u32 gce_num;
 };
 
+static inline u32 cmdq_convert_gce_addr(dma_addr_t addr, const struct gce_plat *pdata)
+{
+       /* Convert DMA addr (PA or IOVA) to GCE readable addr */
+       return addr >> pdata->shift;
+}
+
+static inline dma_addr_t cmdq_revert_gce_addr(u32 addr, const struct gce_plat *pdata)
+{
+       /* Revert GCE readable addr to DMA addr (PA or IOVA) */
+       return (dma_addr_t)addr << pdata->shift;
+}
+
 u8 cmdq_get_shift_pa(struct mbox_chan *chan)
 {
        struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox);
@@ -188,13 +200,12 @@ static void cmdq_task_insert_into_thread(struct cmdq_task *task)
        struct cmdq_task *prev_task = list_last_entry(
                        &thread->task_busy_list, typeof(*task), list_entry);
        u64 *prev_task_base = prev_task->pkt->va_base;
+       u32 gce_addr = cmdq_convert_gce_addr(task->pa_base, task->cmdq->pdata);
 
        /* let previous task jump to this task */
        dma_sync_single_for_cpu(dev, prev_task->pa_base,
                                prev_task->pkt->cmd_buf_size, DMA_TO_DEVICE);
-       prev_task_base[CMDQ_NUM_CMD(prev_task->pkt) - 1] =
-               (u64)CMDQ_JUMP_BY_PA << 32 |
-               (task->pa_base >> task->cmdq->pdata->shift);
+       prev_task_base[CMDQ_NUM_CMD(prev_task->pkt) - 1] = (u64)CMDQ_JUMP_BY_PA << 32 | gce_addr;
        dma_sync_single_for_device(dev, prev_task->pa_base,
                                   prev_task->pkt->cmd_buf_size, DMA_TO_DEVICE);
 
@@ -237,7 +248,8 @@ static void cmdq_thread_irq_handler(struct cmdq *cmdq,
                                    struct cmdq_thread *thread)
 {
        struct cmdq_task *task, *tmp, *curr_task = NULL;
-       u32 curr_pa, irq_flag, task_end_pa;
+       u32 irq_flag, gce_addr;
+       dma_addr_t curr_pa, task_end_pa;
        bool err;
 
        irq_flag = readl(thread->base + CMDQ_THR_IRQ_STATUS);
@@ -259,7 +271,8 @@ static void cmdq_thread_irq_handler(struct cmdq *cmdq,
        else
                return;
 
-       curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR) << cmdq->pdata->shift;
+       gce_addr = readl(thread->base + CMDQ_THR_CURR_ADDR);
+       curr_pa = cmdq_revert_gce_addr(gce_addr, cmdq->pdata);
 
        list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
                                 list_entry) {
@@ -378,7 +391,8 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
        struct cmdq_thread *thread = (struct cmdq_thread *)chan->con_priv;
        struct cmdq *cmdq = dev_get_drvdata(chan->mbox->dev);
        struct cmdq_task *task;
-       unsigned long curr_pa, end_pa;
+       u32 gce_addr;
+       dma_addr_t curr_pa, end_pa;
 
        /* Client should not flush new tasks if suspended. */
        WARN_ON(cmdq->suspended);
@@ -402,20 +416,20 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
                 */
                WARN_ON(cmdq_thread_reset(cmdq, thread) < 0);
 
-               writel(task->pa_base >> cmdq->pdata->shift,
-                      thread->base + CMDQ_THR_CURR_ADDR);
-               writel((task->pa_base + pkt->cmd_buf_size) >> cmdq->pdata->shift,
-                      thread->base + CMDQ_THR_END_ADDR);
+               gce_addr = cmdq_convert_gce_addr(task->pa_base, cmdq->pdata);
+               writel(gce_addr, thread->base + CMDQ_THR_CURR_ADDR);
+               gce_addr = cmdq_convert_gce_addr(task->pa_base + pkt->cmd_buf_size, cmdq->pdata);
+               writel(gce_addr, thread->base + CMDQ_THR_END_ADDR);
 
                writel(thread->priority, thread->base + CMDQ_THR_PRIORITY);
                writel(CMDQ_THR_IRQ_EN, thread->base + CMDQ_THR_IRQ_ENABLE);
                writel(CMDQ_THR_ENABLED, thread->base + CMDQ_THR_ENABLE_TASK);
        } else {
                WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
-               curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR) <<
-                       cmdq->pdata->shift;
-               end_pa = readl(thread->base + CMDQ_THR_END_ADDR) <<
-                       cmdq->pdata->shift;
+               gce_addr = readl(thread->base + CMDQ_THR_CURR_ADDR);
+               curr_pa = cmdq_revert_gce_addr(gce_addr, cmdq->pdata);
+               gce_addr = readl(thread->base + CMDQ_THR_END_ADDR);
+               end_pa = cmdq_revert_gce_addr(gce_addr, cmdq->pdata);
                /* check boundary */
                if (curr_pa == end_pa - CMDQ_INST_SIZE ||
                    curr_pa == end_pa) {
@@ -646,6 +660,9 @@ static int cmdq_probe(struct platform_device *pdev)
        if (err)
                return err;
 
+       dma_set_coherent_mask(dev,
+                             DMA_BIT_MASK(sizeof(u32) * BITS_PER_BYTE + cmdq->pdata->shift));
+
        cmdq->mbox.dev = dev;
        cmdq->mbox.chans = devm_kcalloc(dev, cmdq->pdata->thread_nr,
                                        sizeof(*cmdq->mbox.chans), GFP_KERNEL);
index 4c1a91b07de394585b3940b9ab5eeb128a140ba2..e1555e06e7e55e9d74fa583669ef7d8cf00b571d 100644 (file)
@@ -77,6 +77,16 @@ struct cmdq_pkt {
        size_t                  buf_size; /* real buffer size */
 };
 
+/**
+ * cmdq_get_shift_pa() - get the shift bits of physical address
+ * @chan: mailbox channel
+ *
+ * GCE can only fetch the command buffer address from a 32-bit register.
+ * Some SOCs support more than 32-bit command buffer address for GCE, which
+ * requires some shift bits to make the address fit into the 32-bit register.
+ *
+ * Return: the shift bits of physical address
+ */
 u8 cmdq_get_shift_pa(struct mbox_chan *chan);
 
 #endif /* __MTK_CMDQ_MAILBOX_H__ */