diff mbox series

[04/11] soc: imx: gpcv2: wait for ADB400 handshake

Message ID 20200930155006.535712-5-l.stach@pengutronix.de (mailing list archive)
State New, archived
Headers show
Series i.MX8MM power domain support | expand

Commit Message

Lucas Stach Sept. 30, 2020, 3:49 p.m. UTC
New reference manuals show that there is actually a status bit for
the ADB400 handshake. Add a poll loop to wait for the ADB400 to
acknowledge our request.

Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
 drivers/soc/imx/gpcv2.c | 43 +++++++++++++++++++++++++++++++++--------
 1 file changed, 35 insertions(+), 8 deletions(-)

Comments

Marek Vasut Sept. 30, 2020, 4:11 p.m. UTC | #1
On 9/30/20 5:49 PM, Lucas Stach wrote:

[...]

> @@ -176,9 +180,19 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
>  			   GPC_PGC_CTRL_PCR, 0);
>  
>  	/* request the ADB400 to power up */
> -	if (domain->bits.hsk)
> +	if (domain->bits.hskreq) {
>  		regmap_update_bits(domain->regmap, GPC_PU_PWRHSK,
> -				   domain->bits.hsk, domain->bits.hsk);
> +				   domain->bits.hskreq, domain->bits.hskreq);
> +
> +		ret = regmap_read_poll_timeout(domain->regmap, GPC_PU_PWRHSK,
> +					       reg_val,
> +					       (reg_val & domain->bits.hskack),
> +					       0, USEC_PER_MSEC);
> +		if (ret) {
> +			dev_err(domain->dev, "failed to power up ADB400\n");

The ADB400 is a bus bridge, so the bus is being attached here, not
powered up, right ?
Lucas Stach Sept. 30, 2020, 4:19 p.m. UTC | #2
On Mi, 2020-09-30 at 18:11 +0200, Marek Vasut wrote:
> On 9/30/20 5:49 PM, Lucas Stach wrote:
> 
> [...]
> 
> > @@ -176,9 +180,19 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
> >  			   GPC_PGC_CTRL_PCR, 0);
> >  
> >  	/* request the ADB400 to power up */
> > -	if (domain->bits.hsk)
> > +	if (domain->bits.hskreq) {
> >  		regmap_update_bits(domain->regmap, GPC_PU_PWRHSK,
> > -				   domain->bits.hsk, domain->bits.hsk);
> > +				   domain->bits.hskreq, domain->bits.hskreq);
> > +
> > +		ret = regmap_read_poll_timeout(domain->regmap, GPC_PU_PWRHSK,
> > +					       reg_val,
> > +					       (reg_val & domain->bits.hskack),
> > +					       0, USEC_PER_MSEC);
> > +		if (ret) {
> > +			dev_err(domain->dev, "failed to power up ADB400\n");
> 
> The ADB400 is a bus bridge, so the bus is being attached here, not
> powered up, right ?

The bits in the PWRHSK register are called "power down" bits, so I kept
this nomenclature. Also I think the ADB400 is mostly isolating the bus
in the power domains from the rest of the NoC, "attaching" of the bus
is really disabling the isolation.

As there are multiple valid naming choices I kept the naming from the
RM.

Regards,
Lucas
Marek Vasut Sept. 30, 2020, 4:23 p.m. UTC | #3
On 9/30/20 6:19 PM, Lucas Stach wrote:
> On Mi, 2020-09-30 at 18:11 +0200, Marek Vasut wrote:
>> On 9/30/20 5:49 PM, Lucas Stach wrote:
>>
>> [...]
>>
>>> @@ -176,9 +180,19 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
>>>  			   GPC_PGC_CTRL_PCR, 0);
>>>  
>>>  	/* request the ADB400 to power up */
>>> -	if (domain->bits.hsk)
>>> +	if (domain->bits.hskreq) {
>>>  		regmap_update_bits(domain->regmap, GPC_PU_PWRHSK,
>>> -				   domain->bits.hsk, domain->bits.hsk);
>>> +				   domain->bits.hskreq, domain->bits.hskreq);
>>> +
>>> +		ret = regmap_read_poll_timeout(domain->regmap, GPC_PU_PWRHSK,
>>> +					       reg_val,
>>> +					       (reg_val & domain->bits.hskack),
>>> +					       0, USEC_PER_MSEC);
>>> +		if (ret) {
>>> +			dev_err(domain->dev, "failed to power up ADB400\n");
>>
>> The ADB400 is a bus bridge, so the bus is being attached here, not
>> powered up, right ?
> 
> The bits in the PWRHSK register are called "power down" bits, so I kept
> this nomenclature. Also I think the ADB400 is mostly isolating the bus
> in the power domains from the rest of the NoC, "attaching" of the bus
> is really disabling the isolation.
> 
> As there are multiple valid naming choices I kept the naming from the
> RM.

Maybe NXP can finally explain what these bits really do ?
Jacky Bai Oct. 9, 2020, 3:05 a.m. UTC | #4
> -----Original Message-----
> From: Marek Vasut [mailto:marex@denx.de]
> Sent: Thursday, October 1, 2020 12:23 AM
> To: Lucas Stach <l.stach@pengutronix.de>; Shawn Guo
> <shawnguo@kernel.org>; Rob Herring <robh+dt@kernel.org>
> Cc: devicetree@vger.kernel.org; Frieder Schrempf
> <frieder.schrempf@kontron.de>; patchwork-lst@pengutronix.de; dl-linux-imx
> <linux-imx@nxp.com>; kernel@pengutronix.de; Fabio Estevam
> <festevam@gmail.com>; linux-arm-kernel@lists.infradead.org
> Subject: Re: [PATCH 04/11] soc: imx: gpcv2: wait for ADB400 handshake
> 
> On 9/30/20 6:19 PM, Lucas Stach wrote:
> > On Mi, 2020-09-30 at 18:11 +0200, Marek Vasut wrote:
> >> On 9/30/20 5:49 PM, Lucas Stach wrote:
> >>
> >> [...]
> >>
> >>> @@ -176,9 +180,19 @@ static int imx_pgc_power_up(struct
> generic_pm_domain *genpd)
> >>>  			   GPC_PGC_CTRL_PCR, 0);
> >>>
> >>>  	/* request the ADB400 to power up */
> >>> -	if (domain->bits.hsk)
> >>> +	if (domain->bits.hskreq) {
> >>>  		regmap_update_bits(domain->regmap, GPC_PU_PWRHSK,
> >>> -				   domain->bits.hsk, domain->bits.hsk);
> >>> +				   domain->bits.hskreq, domain->bits.hskreq);
> >>> +
> >>> +		ret = regmap_read_poll_timeout(domain->regmap,
> GPC_PU_PWRHSK,
> >>> +					       reg_val,
> >>> +					       (reg_val & domain->bits.hskack),
> >>> +					       0, USEC_PER_MSEC);
> >>> +		if (ret) {
> >>> +			dev_err(domain->dev, "failed to power up ADB400\n");
> >>
> >> The ADB400 is a bus bridge, so the bus is being attached here, not
> >> powered up, right ?
> >
> > The bits in the PWRHSK register are called "power down" bits, so I
> > kept this nomenclature. Also I think the ADB400 is mostly isolating
> > the bus in the power domains from the rest of the NoC, "attaching" of
> > the bus is really disabling the isolation.
> >
> > As there are multiple valid naming choices I kept the naming from the
> > RM.
> 
> Maybe NXP can finally explain what these bits really do ?

This bit is used to sync the ADB400 bridge to a known status before MIX side power down & isolation.
Detailed info can be find in ARM's ADB400 TRM.

BR
Jacky Bai
Marek Vasut Oct. 9, 2020, 7:27 a.m. UTC | #5
On 10/9/20 5:05 AM, Jacky Bai wrote:
[...]

>>>>> @@ -176,9 +180,19 @@ static int imx_pgc_power_up(struct
>> generic_pm_domain *genpd)
>>>>>  			   GPC_PGC_CTRL_PCR, 0);
>>>>>
>>>>>  	/* request the ADB400 to power up */
>>>>> -	if (domain->bits.hsk)
>>>>> +	if (domain->bits.hskreq) {
>>>>>  		regmap_update_bits(domain->regmap, GPC_PU_PWRHSK,
>>>>> -				   domain->bits.hsk, domain->bits.hsk);
>>>>> +				   domain->bits.hskreq, domain->bits.hskreq);
>>>>> +
>>>>> +		ret = regmap_read_poll_timeout(domain->regmap,
>> GPC_PU_PWRHSK,
>>>>> +					       reg_val,
>>>>> +					       (reg_val & domain->bits.hskack),
>>>>> +					       0, USEC_PER_MSEC);
>>>>> +		if (ret) {
>>>>> +			dev_err(domain->dev, "failed to power up ADB400\n");
>>>>
>>>> The ADB400 is a bus bridge, so the bus is being attached here, not
>>>> powered up, right ?
>>>
>>> The bits in the PWRHSK register are called "power down" bits, so I
>>> kept this nomenclature. Also I think the ADB400 is mostly isolating
>>> the bus in the power domains from the rest of the NoC, "attaching" of
>>> the bus is really disabling the isolation.
>>>
>>> As there are multiple valid naming choices I kept the naming from the
>>> RM.
>>
>> Maybe NXP can finally explain what these bits really do ?
> 
> This bit is used to sync the ADB400 bridge to a known status before MIX side power down & isolation.
> Detailed info can be find in ARM's ADB400 TRM.

Is this documentation publicly available ?
Jacky Bai Oct. 9, 2020, 7:51 a.m. UTC | #6
> -----Original Message-----
> From: Marek Vasut [mailto:marex@denx.de]
> Sent: Friday, October 9, 2020 3:27 PM
> To: Jacky Bai <ping.bai@nxp.com>; Lucas Stach <l.stach@pengutronix.de>;
> Shawn Guo <shawnguo@kernel.org>; Rob Herring <robh+dt@kernel.org>
> Cc: devicetree@vger.kernel.org; Frieder Schrempf
> <frieder.schrempf@kontron.de>; patchwork-lst@pengutronix.de; dl-linux-imx
> <linux-imx@nxp.com>; kernel@pengutronix.de; Fabio Estevam
> <festevam@gmail.com>; linux-arm-kernel@lists.infradead.org
> Subject: Re: [PATCH 04/11] soc: imx: gpcv2: wait for ADB400 handshake
> 
> On 10/9/20 5:05 AM, Jacky Bai wrote:
> [...]
> 
> >>>>> @@ -176,9 +180,19 @@ static int imx_pgc_power_up(struct
> >> generic_pm_domain *genpd)
> >>>>>  			   GPC_PGC_CTRL_PCR, 0);
> >>>>>
> >>>>>  	/* request the ADB400 to power up */
> >>>>> -	if (domain->bits.hsk)
> >>>>> +	if (domain->bits.hskreq) {
> >>>>>  		regmap_update_bits(domain->regmap, GPC_PU_PWRHSK,
> >>>>> -				   domain->bits.hsk, domain->bits.hsk);
> >>>>> +				   domain->bits.hskreq, domain->bits.hskreq);
> >>>>> +
> >>>>> +		ret = regmap_read_poll_timeout(domain->regmap,
> >> GPC_PU_PWRHSK,
> >>>>> +					       reg_val,
> >>>>> +					       (reg_val & domain->bits.hskack),
> >>>>> +					       0, USEC_PER_MSEC);
> >>>>> +		if (ret) {
> >>>>> +			dev_err(domain->dev, "failed to power up ADB400\n");
> >>>>
> >>>> The ADB400 is a bus bridge, so the bus is being attached here, not
> >>>> powered up, right ?
> >>>
> >>> The bits in the PWRHSK register are called "power down" bits, so I
> >>> kept this nomenclature. Also I think the ADB400 is mostly isolating
> >>> the bus in the power domains from the rest of the NoC, "attaching"
> >>> of the bus is really disabling the isolation.
> >>>
> >>> As there are multiple valid naming choices I kept the naming from
> >>> the RM.
> >>
> >> Maybe NXP can finally explain what these bits really do ?
> >
> > This bit is used to sync the ADB400 bridge to a known status before MIX side
> power down & isolation.
> > Detailed info can be find in ARM's ADB400 TRM.
> 
> Is this documentation publicly available ?

Ooh, Sorry, It seems confidential. Some info shared below:

When the powerdown interface is used then the bridge must enter the idle state before either
domain can be reset or powered down:
1. The ADB-400 must be quiescent before a powerdown request. It is a system responsibility
to ensure that all transactions are completed and no new transactions are sent to ADB-400.
2. When the ADB-400 receives the powerdown request signal, pwrdnreqn, LOW it ensures
that all FIFOs are empty, pointers are reset to zero, and no false transactions can be
generated.
3. When the ADB-400 completes the internal shutdown process, it sets the pwrdnackn
signal LOW. The interface is in idle state and powerdown can commence.

BR
Jacky Bai
Marek Vasut Oct. 9, 2020, 8:19 a.m. UTC | #7
On 10/9/20 9:51 AM, Jacky Bai wrote:

[...]

>>>>>>> @@ -176,9 +180,19 @@ static int imx_pgc_power_up(struct
>>>> generic_pm_domain *genpd)
>>>>>>>  			   GPC_PGC_CTRL_PCR, 0);
>>>>>>>
>>>>>>>  	/* request the ADB400 to power up */
>>>>>>> -	if (domain->bits.hsk)
>>>>>>> +	if (domain->bits.hskreq) {
>>>>>>>  		regmap_update_bits(domain->regmap, GPC_PU_PWRHSK,
>>>>>>> -				   domain->bits.hsk, domain->bits.hsk);
>>>>>>> +				   domain->bits.hskreq, domain->bits.hskreq);
>>>>>>> +
>>>>>>> +		ret = regmap_read_poll_timeout(domain->regmap,
>>>> GPC_PU_PWRHSK,
>>>>>>> +					       reg_val,
>>>>>>> +					       (reg_val & domain->bits.hskack),
>>>>>>> +					       0, USEC_PER_MSEC);
>>>>>>> +		if (ret) {
>>>>>>> +			dev_err(domain->dev, "failed to power up ADB400\n");
>>>>>>
>>>>>> The ADB400 is a bus bridge, so the bus is being attached here, not
>>>>>> powered up, right ?
>>>>>
>>>>> The bits in the PWRHSK register are called "power down" bits, so I
>>>>> kept this nomenclature. Also I think the ADB400 is mostly isolating
>>>>> the bus in the power domains from the rest of the NoC, "attaching"
>>>>> of the bus is really disabling the isolation.
>>>>>
>>>>> As there are multiple valid naming choices I kept the naming from
>>>>> the RM.
>>>>
>>>> Maybe NXP can finally explain what these bits really do ?
>>>
>>> This bit is used to sync the ADB400 bridge to a known status before MIX side
>> power down & isolation.
>>> Detailed info can be find in ARM's ADB400 TRM.
>>
>> Is this documentation publicly available ?
> 
> Ooh, Sorry, It seems confidential. Some info shared below:
> 
> When the powerdown interface is used then the bridge must enter the idle state before either
> domain can be reset or powered down:
> 1. The ADB-400 must be quiescent before a powerdown request. It is a system responsibility
> to ensure that all transactions are completed and no new transactions are sent to ADB-400.
> 2. When the ADB-400 receives the powerdown request signal, pwrdnreqn, LOW it ensures
> that all FIFOs are empty, pointers are reset to zero, and no false transactions can be
> generated.
> 3. When the ADB-400 completes the internal shutdown process, it sets the pwrdnackn
> signal LOW. The interface is in idle state and powerdown can commence.

Nice, this was useful, thanks !
diff mbox series

Patch

diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c
index f91063c9fb92..3cfb8b51c23e 100644
--- a/drivers/soc/imx/gpcv2.c
+++ b/drivers/soc/imx/gpcv2.c
@@ -69,6 +69,9 @@ 
 
 #define GPC_PU_PWRHSK			0x1fc
 
+#define IMX8M_GPU_HSK_PWRDNACKN			BIT(26)
+#define IMX8M_VPU_HSK_PWRDNACKN			BIT(25)
+#define IMX8M_DISP_HSK_PWRDNACKN		BIT(24)
 #define IMX8M_GPU_HSK_PWRDNREQN			BIT(6)
 #define IMX8M_VPU_HSK_PWRDNREQN			BIT(5)
 #define IMX8M_DISP_HSK_PWRDNREQN		BIT(4)
@@ -114,7 +117,8 @@  struct imx_pgc_domain {
 	const struct {
 		u32 pxx;
 		u32 map;
-		u32 hsk;
+		u32 hskreq;
+		u32 hskack;
 	} bits;
 
 	const int voltage;
@@ -176,9 +180,19 @@  static int imx_pgc_power_up(struct generic_pm_domain *genpd)
 			   GPC_PGC_CTRL_PCR, 0);
 
 	/* request the ADB400 to power up */
-	if (domain->bits.hsk)
+	if (domain->bits.hskreq) {
 		regmap_update_bits(domain->regmap, GPC_PU_PWRHSK,
-				   domain->bits.hsk, domain->bits.hsk);
+				   domain->bits.hskreq, domain->bits.hskreq);
+
+		ret = regmap_read_poll_timeout(domain->regmap, GPC_PU_PWRHSK,
+					       reg_val,
+					       (reg_val & domain->bits.hskack),
+					       0, USEC_PER_MSEC);
+		if (ret) {
+			dev_err(domain->dev, "failed to power up ADB400\n");
+			goto out_clk_disable;
+		}
+	}
 
 	/* Disable reset clocks for all devices in the domain */
 	for (i = 0; i < domain->num_clks; i++)
@@ -211,9 +225,19 @@  static int imx_pgc_power_down(struct generic_pm_domain *genpd)
 	}
 
 	/* request the ADB400 to power down */
-	if (domain->bits.hsk)
+	if (domain->bits.hskreq) {
 		regmap_update_bits(domain->regmap, GPC_PU_PWRHSK,
-				   domain->bits.hsk, 0);
+				   domain->bits.hskreq, 0);
+
+		ret = regmap_read_poll_timeout(domain->regmap, GPC_PU_PWRHSK,
+					       reg_val,
+					       !(reg_val & domain->bits.hskack),
+					       0, USEC_PER_MSEC);
+		if (ret) {
+			dev_err(domain->dev, "failed to power down ADB400\n");
+			goto out_clk_disable;
+		}
+	}
 
 	/* enable power control */
 	regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc),
@@ -378,7 +402,8 @@  static const struct imx_pgc_domain imx8m_pgc_domains[] = {
 		.bits  = {
 			.pxx = IMX8M_GPU_SW_Pxx_REQ,
 			.map = IMX8M_GPU_A53_DOMAIN,
-			.hsk = IMX8M_GPU_HSK_PWRDNREQN,
+			.hskreq = IMX8M_GPU_HSK_PWRDNREQN,
+			.hskack = IMX8M_GPU_HSK_PWRDNACKN,
 		},
 		.pgc   = IMX8M_PGC_GPU,
 	},
@@ -390,7 +415,8 @@  static const struct imx_pgc_domain imx8m_pgc_domains[] = {
 		.bits  = {
 			.pxx = IMX8M_VPU_SW_Pxx_REQ,
 			.map = IMX8M_VPU_A53_DOMAIN,
-			.hsk = IMX8M_VPU_HSK_PWRDNREQN,
+			.hskreq = IMX8M_VPU_HSK_PWRDNREQN,
+			.hskack = IMX8M_VPU_HSK_PWRDNACKN,
 		},
 		.pgc   = IMX8M_PGC_VPU,
 	},
@@ -402,7 +428,8 @@  static const struct imx_pgc_domain imx8m_pgc_domains[] = {
 		.bits  = {
 			.pxx = IMX8M_DISP_SW_Pxx_REQ,
 			.map = IMX8M_DISP_A53_DOMAIN,
-			.hsk = IMX8M_DISP_HSK_PWRDNREQN,
+			.hskreq = IMX8M_DISP_HSK_PWRDNREQN,
+			.hskack = IMX8M_DISP_HSK_PWRDNACKN,
 		},
 		.pgc   = IMX8M_PGC_DISP,
 	},