diff mbox series

[RESEND,v2] mailbox: mtk-cmdq: fix gce timeout issue

Message ID 20220919071238.23920-1-yongqiang.niu@mediatek.com (mailing list archive)
State New, archived
Headers show
Series [RESEND,v2] mailbox: mtk-cmdq: fix gce timeout issue | expand

Commit Message

Yongqiang Niu Sept. 19, 2022, 7:12 a.m. UTC
1. enable gce ddr enable(gce reigster offset 0x48, bit 16 to 18) when gce work,
and disable gce ddr enable when gce work job done
2. split cmdq clk enable/disable api, and control gce ddr enable/disable
in clk enable/disable function to make sure it could protect when cmdq
is multiple used by display and mdp

this is only for some SOC which has flag "control_by_sw".
for this kind of gce, there is a handshake flow between gce and ddr
hardware,
if not set ddr enable flag of gce, ddr will fall into idle mode,
then gce instructions will not process done.
we need set this flag of gce to tell ddr when gce is idle or busy
controlled by software flow.

ddr problem is a special case.
when test suspend/resume case, gce sometimes will pull ddr, and ddr can
not go to suspend.
if we set gce register 0x48 to 0x7, will fix this gce pull ddr issue,
as you have referred [1] and [2] (8192 and 8195)
but for mt8186, the gce is more special, except setting of [1] and [2],
we need add more setting set gce register 0x48 to (0x7 << 16 | 0x7)
when gce working to make sure gce could process all instructions ok.
this case just need normal bootup, if we not set this, display cmdq
task will timeout, and chrome homescreen will always black screen.

and with this patch, we have done these test on mt8186:
1.suspend/resume
2.boot up to home screen
3.playback video with youtube.

suspend issue is special gce hardware issue, gce client  driver
command already process done, but gce still pull ddr.

Signed-off-by: Yongqiang Niu <yongqiang.niu@mediatek.com>
---
 drivers/mailbox/mtk-cmdq-mailbox.c | 66 +++++++++++++++++++++++++++---
 1 file changed, 61 insertions(+), 5 deletions(-)

Comments

AngeloGioacchino Del Regno Sept. 20, 2022, 8:48 a.m. UTC | #1
Il 19/09/22 09:12, Yongqiang Niu ha scritto:
> 1. enable gce ddr enable(gce reigster offset 0x48, bit 16 to 18) when gce work,
> and disable gce ddr enable when gce work job done
> 2. split cmdq clk enable/disable api, and control gce ddr enable/disable
> in clk enable/disable function to make sure it could protect when cmdq
> is multiple used by display and mdp
> 
> this is only for some SOC which has flag "control_by_sw".
> for this kind of gce, there is a handshake flow between gce and ddr
> hardware,
> if not set ddr enable flag of gce, ddr will fall into idle mode,
> then gce instructions will not process done.
> we need set this flag of gce to tell ddr when gce is idle or busy
> controlled by software flow.
> 
> ddr problem is a special case.
> when test suspend/resume case, gce sometimes will pull ddr, and ddr can
> not go to suspend.
> if we set gce register 0x48 to 0x7, will fix this gce pull ddr issue,
> as you have referred [1] and [2] (8192 and 8195)
> but for mt8186, the gce is more special, except setting of [1] and [2],
> we need add more setting set gce register 0x48 to (0x7 << 16 | 0x7)
> when gce working to make sure gce could process all instructions ok.
> this case just need normal bootup, if we not set this, display cmdq
> task will timeout, and chrome homescreen will always black screen.
> 
> and with this patch, we have done these test on mt8186:
> 1.suspend/resume
> 2.boot up to home screen
> 3.playback video with youtube.
> 
> suspend issue is special gce hardware issue, gce client  driver
> command already process done, but gce still pull ddr.
> 
> Signed-off-by: Yongqiang Niu <yongqiang.niu@mediatek.com>
> ---
>   drivers/mailbox/mtk-cmdq-mailbox.c | 66 +++++++++++++++++++++++++++---
>   1 file changed, 61 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c
> index 9465f9081515..3a1b11de84be 100644
> --- a/drivers/mailbox/mtk-cmdq-mailbox.c
> +++ b/drivers/mailbox/mtk-cmdq-mailbox.c
> @@ -80,16 +80,60 @@ struct cmdq {
>   	bool			suspended;
>   	u8			shift_pa;
>   	bool			control_by_sw;
> +	bool			sw_ddr_en;
>   	u32			gce_num;
> +	atomic_t		usage;
> +	spinlock_t		lock;
>   };
>   
>   struct gce_plat {
>   	u32 thread_nr;
>   	u8 shift;
>   	bool control_by_sw;
> +	bool sw_ddr_en;
>   	u32 gce_num;
>   };
>   
> +static s32 cmdq_clk_enable(struct cmdq *cmdq)
> +{
> +	s32 usage, ret;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&cmdq->lock, flags);
> +
> +	usage = atomic_inc_return(&cmdq->usage);
> +
> +	ret = clk_bulk_enable(cmdq->gce_num, cmdq->clocks);
> +	if (usage <= 0 || ret < 0) {
> +		dev_err(cmdq->mbox.dev, "ref count %d ret %d suspend %d\n",
> +			usage, ret, cmdq->suspended);
> +	} else if (usage == 1) {
> +		if (cmdq->sw_ddr_en)
> +			writel((0x7 << 16) + 0x7, cmdq->base + GCE_GCTL_VALUE);

Can this be used on MT8192/MT8195?
If yes, you can avoid adding that sw_ddr_en, as you would be able to simply
use `control_by_sw`, which already seems to be doing most of what you're adding
here.

Also, I dislike all this locking: the point of having MTK CMDQ is to improve
performance (I know it's more than that, but there's no ISP upstream) and part
of that happens because locking is greatly reduced.

If you add it back up, we're losing part of the point.

Though, in this driver, we are already tracking the usage of the CMDQ threads
and, when there's no more usage, we're turning off the clocks: this is done
through the usage of `task_busy_list`, so you *do* have a way to avoid adding
more locks around.
Besides that, you could also add a clock notifier for the same, but since
the state is already tracked, I'd avoid using that.

Moreover, (7 << 16) == GENMASK(18, 16)

#define GCE_CMD_SOMETHING GENMASK(18, 16)

Regards,
Angelo
Yongqiang Niu Sept. 26, 2022, 8:26 a.m. UTC | #2
On Tue, 2022-09-20 at 10:48 +0200, AngeloGioacchino Del Regno wrote:
> Il 19/09/22 09:12, Yongqiang Niu ha scritto:
> > 1. enable gce ddr enable(gce reigster offset 0x48, bit 16 to 18)
> > when gce work,
> > and disable gce ddr enable when gce work job done
> > 2. split cmdq clk enable/disable api, and control gce ddr
> > enable/disable
> > in clk enable/disable function to make sure it could protect when
> > cmdq
> > is multiple used by display and mdp
> > 
> > this is only for some SOC which has flag "control_by_sw".
> > for this kind of gce, there is a handshake flow between gce and ddr
> > hardware,
> > if not set ddr enable flag of gce, ddr will fall into idle mode,
> > then gce instructions will not process done.
> > we need set this flag of gce to tell ddr when gce is idle or busy
> > controlled by software flow.
> > 
> > ddr problem is a special case.
> > when test suspend/resume case, gce sometimes will pull ddr, and ddr
> > can
> > not go to suspend.
> > if we set gce register 0x48 to 0x7, will fix this gce pull ddr
> > issue,
> > as you have referred [1] and [2] (8192 and 8195)
> > but for mt8186, the gce is more special, except setting of [1] and
> > [2],
> > we need add more setting set gce register 0x48 to (0x7 << 16 | 0x7)
> > when gce working to make sure gce could process all instructions
> > ok.
> > this case just need normal bootup, if we not set this, display cmdq
> > task will timeout, and chrome homescreen will always black screen.
> > 
> > and with this patch, we have done these test on mt8186:
> > 1.suspend/resume
> > 2.boot up to home screen
> > 3.playback video with youtube.
> > 
> > suspend issue is special gce hardware issue, gce client  driver
> > command already process done, but gce still pull ddr.
> > 
> > Signed-off-by: Yongqiang Niu <yongqiang.niu@mediatek.com>
> > ---
> >   drivers/mailbox/mtk-cmdq-mailbox.c | 66
> > +++++++++++++++++++++++++++---
> >   1 file changed, 61 insertions(+), 5 deletions(-)
> > 
> > diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c
> > b/drivers/mailbox/mtk-cmdq-mailbox.c
> > index 9465f9081515..3a1b11de84be 100644
> > --- a/drivers/mailbox/mtk-cmdq-mailbox.c
> > +++ b/drivers/mailbox/mtk-cmdq-mailbox.c
> > @@ -80,16 +80,60 @@ struct cmdq {
> >   	bool			suspended;
> >   	u8			shift_pa;
> >   	bool			control_by_sw;
> > +	bool			sw_ddr_en;
> >   	u32			gce_num;
> > +	atomic_t		usage;
> > +	spinlock_t		lock;
> >   };
> >   
> >   struct gce_plat {
> >   	u32 thread_nr;
> >   	u8 shift;
> >   	bool control_by_sw;
> > +	bool sw_ddr_en;
> >   	u32 gce_num;
> >   };
> >   
> > +static s32 cmdq_clk_enable(struct cmdq *cmdq)
> > +{
> > +	s32 usage, ret;
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&cmdq->lock, flags);
> > +
> > +	usage = atomic_inc_return(&cmdq->usage);
> > +
> > +	ret = clk_bulk_enable(cmdq->gce_num, cmdq->clocks);
> > +	if (usage <= 0 || ret < 0) {
> > +		dev_err(cmdq->mbox.dev, "ref count %d ret %d suspend
> > %d\n",
> > +			usage, ret, cmdq->suspended);
> > +	} else if (usage == 1) {
> > +		if (cmdq->sw_ddr_en)
> > +			writel((0x7 << 16) + 0x7, cmdq->base +
> > GCE_GCTL_VALUE);
> 
> Can this be used on MT8192/MT8195?
> If yes, you can avoid adding that sw_ddr_en, as you would be able to
> simply
> use `control_by_sw`, which already seems to be doing most of what
> you're adding
> here.

no, this is only for MT8186, MT8192/MT8195 no need this patch

> 
> Also, I dislike all this locking: the point of having MTK CMDQ is to
> improve
> performance (I know it's more than that, but there's no ISP upstream)
> and part
> of that happens because locking is greatly reduced.
> 
> If you add it back up, we're losing part of the point.
> 
> Though, in this driver, we are already tracking the usage of the CMDQ
> threads
> and, when there's no more usage, we're turning off the clocks: this
> is done
> through the usage of `task_busy_list`, so you *do* have a way to
> avoid adding
> more locks around.
> Besides that, you could also add a clock notifier for the same, but
> since
> the state is already tracked, I'd avoid using that.
there are two client of cmdq, both MDP and DISPLAY will use cmdq.
we need this lock to protect MDP and DISPLAY access cmdq at same time.
we need make sure cmdq clock is on when any of it client is work on.
and make sure cmd clock is off when all it's client is work done.

> 
> Moreover, (7 << 16) == GENMASK(18, 16)
> 
> #define GCE_CMD_SOMETHING GENMASK(18, 16)
this will be improve in version3
> 
> Regards,
> Angelo
> 
> 
>
diff mbox series

Patch

diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c
index 9465f9081515..3a1b11de84be 100644
--- a/drivers/mailbox/mtk-cmdq-mailbox.c
+++ b/drivers/mailbox/mtk-cmdq-mailbox.c
@@ -80,16 +80,60 @@  struct cmdq {
 	bool			suspended;
 	u8			shift_pa;
 	bool			control_by_sw;
+	bool			sw_ddr_en;
 	u32			gce_num;
+	atomic_t		usage;
+	spinlock_t		lock;
 };
 
 struct gce_plat {
 	u32 thread_nr;
 	u8 shift;
 	bool control_by_sw;
+	bool sw_ddr_en;
 	u32 gce_num;
 };
 
+static s32 cmdq_clk_enable(struct cmdq *cmdq)
+{
+	s32 usage, ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cmdq->lock, flags);
+
+	usage = atomic_inc_return(&cmdq->usage);
+
+	ret = clk_bulk_enable(cmdq->gce_num, cmdq->clocks);
+	if (usage <= 0 || ret < 0) {
+		dev_err(cmdq->mbox.dev, "ref count %d ret %d suspend %d\n",
+			usage, ret, cmdq->suspended);
+	} else if (usage == 1) {
+		if (cmdq->sw_ddr_en)
+			writel((0x7 << 16) + 0x7, cmdq->base + GCE_GCTL_VALUE);
+	}
+
+	spin_unlock_irqrestore(&cmdq->lock, flags);
+
+	return ret;
+}
+
+static void cmdq_clk_disable(struct cmdq *cmdq)
+{
+	s32 usage;
+
+	usage = atomic_dec_return(&cmdq->usage);
+
+	if (usage < 0) {
+		dev_err(cmdq->mbox.dev, "ref count %d suspend %d\n",
+			usage, cmdq->suspended);
+	} else if (usage == 0) {
+		if (cmdq->sw_ddr_en)
+			writel(0x7, cmdq->base + GCE_GCTL_VALUE);
+	}
+
+	clk_bulk_disable(cmdq->gce_num, cmdq->clocks);
+}
+
 u8 cmdq_get_shift_pa(struct mbox_chan *chan)
 {
 	struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox);
@@ -266,7 +310,8 @@  static void cmdq_thread_irq_handler(struct cmdq *cmdq,
 
 	if (list_empty(&thread->task_busy_list)) {
 		cmdq_thread_disable(cmdq, thread);
-		clk_bulk_disable(cmdq->gce_num, cmdq->clocks);
+
+		cmdq_clk_disable(cmdq);
 	}
 }
 
@@ -355,8 +400,7 @@  static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
 	task->pkt = pkt;
 
 	if (list_empty(&thread->task_busy_list)) {
-		WARN_ON(clk_bulk_enable(cmdq->gce_num, cmdq->clocks));
-
+		WARN_ON(cmdq_clk_enable(cmdq) < 0);
 		/*
 		 * The thread reset will clear thread related register to 0,
 		 * including pc, end, priority, irq, suspend and enable. Thus
@@ -428,7 +472,7 @@  static void cmdq_mbox_shutdown(struct mbox_chan *chan)
 	}
 
 	cmdq_thread_disable(cmdq, thread);
-	clk_bulk_disable(cmdq->gce_num, cmdq->clocks);
+	cmdq_clk_disable(cmdq);
 
 done:
 	/*
@@ -468,7 +512,8 @@  static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout)
 
 	cmdq_thread_resume(thread);
 	cmdq_thread_disable(cmdq, thread);
-	clk_bulk_disable(cmdq->gce_num, cmdq->clocks);
+
+	cmdq_clk_disable(cmdq);
 
 out:
 	spin_unlock_irqrestore(&thread->chan->lock, flags);
@@ -543,6 +588,7 @@  static int cmdq_probe(struct platform_device *pdev)
 	cmdq->thread_nr = plat_data->thread_nr;
 	cmdq->shift_pa = plat_data->shift;
 	cmdq->control_by_sw = plat_data->control_by_sw;
+	cmdq->sw_ddr_en = plat_data->sw_ddr_en;
 	cmdq->gce_num = plat_data->gce_num;
 	cmdq->irq_mask = GENMASK(cmdq->thread_nr - 1, 0);
 	err = devm_request_irq(dev, cmdq->irq, cmdq_irq_handler, IRQF_SHARED,
@@ -615,6 +661,7 @@  static int cmdq_probe(struct platform_device *pdev)
 
 	WARN_ON(clk_bulk_prepare(cmdq->gce_num, cmdq->clocks));
 
+	spin_lock_init(&cmdq->lock);
 	cmdq_init(cmdq);
 
 	return 0;
@@ -660,9 +707,18 @@  static const struct gce_plat gce_plat_v6 = {
 	.gce_num = 2
 };
 
+static const struct gce_plat gce_plat_v7 = {
+	.thread_nr = 24,
+	.shift = 3,
+	.control_by_sw = true,
+	.sw_ddr_en = true,
+	.gce_num = 1
+};
+
 static const struct of_device_id cmdq_of_ids[] = {
 	{.compatible = "mediatek,mt8173-gce", .data = (void *)&gce_plat_v2},
 	{.compatible = "mediatek,mt8183-gce", .data = (void *)&gce_plat_v3},
+	{.compatible = "mediatek,mt8186-gce", .data = (void *)&gce_plat_v7},
 	{.compatible = "mediatek,mt6779-gce", .data = (void *)&gce_plat_v4},
 	{.compatible = "mediatek,mt8192-gce", .data = (void *)&gce_plat_v5},
 	{.compatible = "mediatek,mt8195-gce", .data = (void *)&gce_plat_v6},