mbox series

[00/14] UFS: Add OPP and interconnect support

Message ID 20230712103213.101770-1-manivannan.sadhasivam@linaro.org
Headers show
Series UFS: Add OPP and interconnect support | expand

Message

Manivannan Sadhasivam July 12, 2023, 10:31 a.m. UTC
Hi,

This series adds OPP (Operating Points) support to UFSHCD driver and
interconnect support to Qcom UFS driver.

Motivation behind adding OPP support is to scale both clocks as well as
regulators/performance state dynamically. Currently, UFSHCD just scales
clock frequency during runtime with the help of "freq-table-hz" property
defined in devicetree. With the addition of OPP tables in devicetree (as
done for Qcom SDM845 and SM8250 SoCs in this series) UFSHCD can now scale
both clocks and performance state of power domain which helps in power
saving.

For the addition of OPP support to UFSHCD, there are changes required to
the OPP framework and devfreq drivers which are also added in this series.

Finally, interconnect support is added to Qcom UFS driver for scaling the
interconnect path dynamically. This is required to avoid boot crash in
recent SoCs and also to save power during runtime. More information is
available in patch 13/13.

Credits
=======

This series is a continuation of previous work by Krzysztof Kozlowski [1]
and Brian Masney [2]. Ideally, this could've split into two series (OPP
and interconnect) but since there will be a dependency in the devicetree,
I decided to keep them in a single series.

Testing
=======

This series is tested on 96Boards RB3 (SDM845 SoC) and RB5 (SM8250 SoC)
development boards.

Merging Strategy
================

An immutable branch might be required between OPP and SCSI trees because of
the API dependency (devfreq too). And I leave it up to the maintainers to
decide.

Thanks,
Mani

[1] https://lore.kernel.org/all/20220513061347.46480-1-krzysztof.kozlowski@linaro.org/
[2] https://lore.kernel.org/all/20221117104957.254648-1-bmasney@redhat.com/

Krzysztof Kozlowski (2):
  dt-bindings: ufs: common: add OPP table
  arm64: dts: qcom: sdm845: Add OPP table support to UFSHC

Manivannan Sadhasivam (12):
  dt-bindings: opp: Increase maxItems for opp-hz property
  arm64: dts: qcom: sdm845: Add missing RPMh power domain to GCC
  arm64: dts: qcom: sdm845: Fix the min frequency of "ice_core_clk"
  arm64: dts: qcom: sm8250: Add OPP table support to UFSHC
  OPP: Introduce dev_pm_opp_find_freq_{ceil/floor}_indexed() APIs
  OPP: Introduce dev_pm_opp_get_freq_indexed() API
  PM / devfreq: Switch to dev_pm_opp_find_freq_{ceil/floor}_indexed()
    APIs
  scsi: ufs: core: Add OPP support for scaling clocks and regulators
  scsi: ufs: host: Add support for parsing OPP
  arm64: dts: qcom: sdm845: Add interconnect paths to UFSHC
  arm64: dts: qcom: sm8250: Add interconnect paths to UFSHC
  scsi: ufs: qcom: Add support for scaling interconnects

 .../devicetree/bindings/opp/opp-v2-base.yaml  |   2 +-
 .../devicetree/bindings/ufs/ufs-common.yaml   |  34 ++++-
 arch/arm64/boot/dts/qcom/sdm845.dtsi          |  47 ++++--
 arch/arm64/boot/dts/qcom/sm8250.dtsi          |  43 ++++--
 drivers/devfreq/devfreq.c                     |  14 +-
 drivers/opp/core.c                            |  76 ++++++++++
 drivers/ufs/core/ufshcd.c                     | 142 ++++++++++++++----
 drivers/ufs/host/ufs-qcom.c                   | 131 +++++++++++++++-
 drivers/ufs/host/ufs-qcom.h                   |   3 +
 drivers/ufs/host/ufshcd-pltfrm.c              | 116 ++++++++++++++
 include/linux/pm_opp.h                        |  26 ++++
 include/ufs/ufshcd.h                          |   4 +
 12 files changed, 574 insertions(+), 64 deletions(-)

Comments

Konrad Dybcio July 12, 2023, 10:39 a.m. UTC | #1
On 12.07.2023 12:31, Manivannan Sadhasivam wrote:
> GCC and it's GDSCs are under the RPMh CX power domain. So let's add the
> missing RPMh power domain to the GCC node.
> 
> Fixes: 6d4cf750d03a ("arm64: dts: sdm845: Add minimal dts/dtsi files for sdm845 SoC and MTP")
> Co-developed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> ---
The ratio of developers to lines of code changed is sus, but okay

Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org>

Konrad
>  arch/arm64/boot/dts/qcom/sdm845.dtsi | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> index 02a6ea0b8b2c..9ed74bf72d05 100644
> --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
> +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> @@ -1207,6 +1207,7 @@ gcc: clock-controller@100000 {
>  			#clock-cells = <1>;
>  			#reset-cells = <1>;
>  			#power-domain-cells = <1>;
> +			power-domains = <&rpmhpd SDM845_CX>;
>  		};
>  
>  		qfprom@784000 {
Manivannan Sadhasivam July 12, 2023, 10:40 a.m. UTC | #2
On Wed, Jul 12, 2023 at 04:01:55PM +0530, Manivannan Sadhasivam wrote:
> Hi,
> 
> This series adds OPP (Operating Points) support to UFSHCD driver and
> interconnect support to Qcom UFS driver.
> 

Missed to cc SCSI folks. Will be resending this series. Sorry for the noise.

- Mani

> Motivation behind adding OPP support is to scale both clocks as well as
> regulators/performance state dynamically. Currently, UFSHCD just scales
> clock frequency during runtime with the help of "freq-table-hz" property
> defined in devicetree. With the addition of OPP tables in devicetree (as
> done for Qcom SDM845 and SM8250 SoCs in this series) UFSHCD can now scale
> both clocks and performance state of power domain which helps in power
> saving.
> 
> For the addition of OPP support to UFSHCD, there are changes required to
> the OPP framework and devfreq drivers which are also added in this series.
> 
> Finally, interconnect support is added to Qcom UFS driver for scaling the
> interconnect path dynamically. This is required to avoid boot crash in
> recent SoCs and also to save power during runtime. More information is
> available in patch 13/13.
> 
> Credits
> =======
> 
> This series is a continuation of previous work by Krzysztof Kozlowski [1]
> and Brian Masney [2]. Ideally, this could've split into two series (OPP
> and interconnect) but since there will be a dependency in the devicetree,
> I decided to keep them in a single series.
> 
> Testing
> =======
> 
> This series is tested on 96Boards RB3 (SDM845 SoC) and RB5 (SM8250 SoC)
> development boards.
> 
> Merging Strategy
> ================
> 
> An immutable branch might be required between OPP and SCSI trees because of
> the API dependency (devfreq too). And I leave it up to the maintainers to
> decide.
> 
> Thanks,
> Mani
> 
> [1] https://lore.kernel.org/all/20220513061347.46480-1-krzysztof.kozlowski@linaro.org/
> [2] https://lore.kernel.org/all/20221117104957.254648-1-bmasney@redhat.com/
> 
> Krzysztof Kozlowski (2):
>   dt-bindings: ufs: common: add OPP table
>   arm64: dts: qcom: sdm845: Add OPP table support to UFSHC
> 
> Manivannan Sadhasivam (12):
>   dt-bindings: opp: Increase maxItems for opp-hz property
>   arm64: dts: qcom: sdm845: Add missing RPMh power domain to GCC
>   arm64: dts: qcom: sdm845: Fix the min frequency of "ice_core_clk"
>   arm64: dts: qcom: sm8250: Add OPP table support to UFSHC
>   OPP: Introduce dev_pm_opp_find_freq_{ceil/floor}_indexed() APIs
>   OPP: Introduce dev_pm_opp_get_freq_indexed() API
>   PM / devfreq: Switch to dev_pm_opp_find_freq_{ceil/floor}_indexed()
>     APIs
>   scsi: ufs: core: Add OPP support for scaling clocks and regulators
>   scsi: ufs: host: Add support for parsing OPP
>   arm64: dts: qcom: sdm845: Add interconnect paths to UFSHC
>   arm64: dts: qcom: sm8250: Add interconnect paths to UFSHC
>   scsi: ufs: qcom: Add support for scaling interconnects
> 
>  .../devicetree/bindings/opp/opp-v2-base.yaml  |   2 +-
>  .../devicetree/bindings/ufs/ufs-common.yaml   |  34 ++++-
>  arch/arm64/boot/dts/qcom/sdm845.dtsi          |  47 ++++--
>  arch/arm64/boot/dts/qcom/sm8250.dtsi          |  43 ++++--
>  drivers/devfreq/devfreq.c                     |  14 +-
>  drivers/opp/core.c                            |  76 ++++++++++
>  drivers/ufs/core/ufshcd.c                     | 142 ++++++++++++++----
>  drivers/ufs/host/ufs-qcom.c                   | 131 +++++++++++++++-
>  drivers/ufs/host/ufs-qcom.h                   |   3 +
>  drivers/ufs/host/ufshcd-pltfrm.c              | 116 ++++++++++++++
>  include/linux/pm_opp.h                        |  26 ++++
>  include/ufs/ufshcd.h                          |   4 +
>  12 files changed, 574 insertions(+), 64 deletions(-)
> 
> -- 
> 2.25.1
>
Konrad Dybcio July 12, 2023, 10:45 a.m. UTC | #3
On 12.07.2023 12:31, Manivannan Sadhasivam wrote:
> Minimum frequency of the "ice_core_clk" should be 75MHz as specified in the
> downstream vendor devicetree. So fix it!
> 
> https://git.codelinaro.org/clo/la/kernel/msm-4.9/-/blob/LA.UM.7.3.r1-09300-sdm845.0/arch/arm64/boot/dts/qcom/sdm845.dtsi
> 
> Fixes: cc16687fbd74 ("arm64: dts: qcom: sdm845: add UFS controller")
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> ---
Would it make sense to move the ICE to the new bindings instead?

Can sdm845's ICE also work with the sdcard slot?

Konrad
>  arch/arm64/boot/dts/qcom/sdm845.dtsi | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> index 9ed74bf72d05..89520a9fe1e3 100644
> --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
> +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> @@ -2614,7 +2614,7 @@ ufs_mem_hc: ufshc@1d84000 {
>  				<0 0>,
>  				<0 0>,
>  				<0 0>,
> -				<0 300000000>;
> +				<75000000 300000000>;
>  
>  			status = "disabled";
>  		};
John Garry July 12, 2023, 11:02 a.m. UTC | #4
Did you bcc linux-scsi on this series?

I am just wondering why it came directly to my inbox and I am not the 
to: or cc: list. I also notice replies in the archives which I have not 
received...

Thanks,
John
Manivannan Sadhasivam July 12, 2023, 11:04 a.m. UTC | #5
On Wed, Jul 12, 2023 at 12:45:40PM +0200, Konrad Dybcio wrote:
> On 12.07.2023 12:31, Manivannan Sadhasivam wrote:
> > Minimum frequency of the "ice_core_clk" should be 75MHz as specified in the
> > downstream vendor devicetree. So fix it!
> > 
> > https://git.codelinaro.org/clo/la/kernel/msm-4.9/-/blob/LA.UM.7.3.r1-09300-sdm845.0/arch/arm64/boot/dts/qcom/sdm845.dtsi
> > 
> > Fixes: cc16687fbd74 ("arm64: dts: qcom: sdm845: add UFS controller")
> > Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> > ---
> Would it make sense to move the ICE to the new bindings instead?
> 

That can be done later if required. Fixing the frequency just in this patch
allows it to get backported to stable.

> Can sdm845's ICE also work with the sdcard slot?
> 

I do not have any info on this.

- Mani

> Konrad
> >  arch/arm64/boot/dts/qcom/sdm845.dtsi | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> > index 9ed74bf72d05..89520a9fe1e3 100644
> > --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
> > +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> > @@ -2614,7 +2614,7 @@ ufs_mem_hc: ufshc@1d84000 {
> >  				<0 0>,
> >  				<0 0>,
> >  				<0 0>,
> > -				<0 300000000>;
> > +				<75000000 300000000>;
> >  
> >  			status = "disabled";
> >  		};
Manivannan Sadhasivam July 12, 2023, 11:12 a.m. UTC | #6
On Wed, Jul 12, 2023 at 12:02:37PM +0100, John Garry wrote:
> Did you bcc linux-scsi on this series?
> 
> I am just wondering why it came directly to my inbox and I am not the to: or
> cc: list. I also notice replies in the archives which I have not received...
> 

I initially missed CCing scsi list and maintainers. So instead of resending the
series (since it has too many people CCed), I just bounced the patches to scsi
list and maintainers using Mutt.

From the next iteration, I will make sure to add everyone. Sorry for the
trouble.

- Mani

> Thanks,
> John
Dmitry Baryshkov July 12, 2023, 12:18 p.m. UTC | #7
On 12/07/2023 13:40, Manivannan Sadhasivam wrote:
> On Wed, Jul 12, 2023 at 04:01:55PM +0530, Manivannan Sadhasivam wrote:
>> Hi,
>>
>> This series adds OPP (Operating Points) support to UFSHCD driver and
>> interconnect support to Qcom UFS driver.
>>
> 
> Missed to cc SCSI folks. Will be resending this series. Sorry for the noise.

I'd say, there is a need for the resend anyway, the series got duplicate 
patch indices.

> 
> - Mani
> 
>> Motivation behind adding OPP support is to scale both clocks as well as
>> regulators/performance state dynamically. Currently, UFSHCD just scales
>> clock frequency during runtime with the help of "freq-table-hz" property
>> defined in devicetree. With the addition of OPP tables in devicetree (as
>> done for Qcom SDM845 and SM8250 SoCs in this series) UFSHCD can now scale
>> both clocks and performance state of power domain which helps in power
>> saving.
>>
>> For the addition of OPP support to UFSHCD, there are changes required to
>> the OPP framework and devfreq drivers which are also added in this series.
>>
>> Finally, interconnect support is added to Qcom UFS driver for scaling the
>> interconnect path dynamically. This is required to avoid boot crash in
>> recent SoCs and also to save power during runtime. More information is
>> available in patch 13/13.
>>
>> Credits
>> =======
>>
>> This series is a continuation of previous work by Krzysztof Kozlowski [1]
>> and Brian Masney [2]. Ideally, this could've split into two series (OPP
>> and interconnect) but since there will be a dependency in the devicetree,
>> I decided to keep them in a single series.
>>
>> Testing
>> =======
>>
>> This series is tested on 96Boards RB3 (SDM845 SoC) and RB5 (SM8250 SoC)
>> development boards.
>>
>> Merging Strategy
>> ================
>>
>> An immutable branch might be required between OPP and SCSI trees because of
>> the API dependency (devfreq too). And I leave it up to the maintainers to
>> decide.
>>
>> Thanks,
>> Mani
>>
>> [1] https://lore.kernel.org/all/20220513061347.46480-1-krzysztof.kozlowski@linaro.org/
>> [2] https://lore.kernel.org/all/20221117104957.254648-1-bmasney@redhat.com/
>>
>> Krzysztof Kozlowski (2):
>>    dt-bindings: ufs: common: add OPP table
>>    arm64: dts: qcom: sdm845: Add OPP table support to UFSHC
>>
>> Manivannan Sadhasivam (12):
>>    dt-bindings: opp: Increase maxItems for opp-hz property
>>    arm64: dts: qcom: sdm845: Add missing RPMh power domain to GCC
>>    arm64: dts: qcom: sdm845: Fix the min frequency of "ice_core_clk"
>>    arm64: dts: qcom: sm8250: Add OPP table support to UFSHC
>>    OPP: Introduce dev_pm_opp_find_freq_{ceil/floor}_indexed() APIs
>>    OPP: Introduce dev_pm_opp_get_freq_indexed() API
>>    PM / devfreq: Switch to dev_pm_opp_find_freq_{ceil/floor}_indexed()
>>      APIs
>>    scsi: ufs: core: Add OPP support for scaling clocks and regulators
>>    scsi: ufs: host: Add support for parsing OPP
>>    arm64: dts: qcom: sdm845: Add interconnect paths to UFSHC
>>    arm64: dts: qcom: sm8250: Add interconnect paths to UFSHC
>>    scsi: ufs: qcom: Add support for scaling interconnects
>>
>>   .../devicetree/bindings/opp/opp-v2-base.yaml  |   2 +-
>>   .../devicetree/bindings/ufs/ufs-common.yaml   |  34 ++++-
>>   arch/arm64/boot/dts/qcom/sdm845.dtsi          |  47 ++++--
>>   arch/arm64/boot/dts/qcom/sm8250.dtsi          |  43 ++++--
>>   drivers/devfreq/devfreq.c                     |  14 +-
>>   drivers/opp/core.c                            |  76 ++++++++++
>>   drivers/ufs/core/ufshcd.c                     | 142 ++++++++++++++----
>>   drivers/ufs/host/ufs-qcom.c                   | 131 +++++++++++++++-
>>   drivers/ufs/host/ufs-qcom.h                   |   3 +
>>   drivers/ufs/host/ufshcd-pltfrm.c              | 116 ++++++++++++++
>>   include/linux/pm_opp.h                        |  26 ++++
>>   include/ufs/ufshcd.h                          |   4 +
>>   12 files changed, 574 insertions(+), 64 deletions(-)
>>
>> -- 
>> 2.25.1
>>
>
Manivannan Sadhasivam July 12, 2023, 12:27 p.m. UTC | #8
On Wed, Jul 12, 2023 at 03:18:38PM +0300, Dmitry Baryshkov wrote:
> On 12/07/2023 13:40, Manivannan Sadhasivam wrote:
> > On Wed, Jul 12, 2023 at 04:01:55PM +0530, Manivannan Sadhasivam wrote:
> > > Hi,
> > > 
> > > This series adds OPP (Operating Points) support to UFSHCD driver and
> > > interconnect support to Qcom UFS driver.
> > > 
> > 
> > Missed to cc SCSI folks. Will be resending this series. Sorry for the noise.
> 
> I'd say, there is a need for the resend anyway, the series got duplicate
> patch indices.
> 

Yeah, my script screwed up. Will wait for some time to get comments before
resending though.

- Mani

> > 
> > - Mani
> > 
> > > Motivation behind adding OPP support is to scale both clocks as well as
> > > regulators/performance state dynamically. Currently, UFSHCD just scales
> > > clock frequency during runtime with the help of "freq-table-hz" property
> > > defined in devicetree. With the addition of OPP tables in devicetree (as
> > > done for Qcom SDM845 and SM8250 SoCs in this series) UFSHCD can now scale
> > > both clocks and performance state of power domain which helps in power
> > > saving.
> > > 
> > > For the addition of OPP support to UFSHCD, there are changes required to
> > > the OPP framework and devfreq drivers which are also added in this series.
> > > 
> > > Finally, interconnect support is added to Qcom UFS driver for scaling the
> > > interconnect path dynamically. This is required to avoid boot crash in
> > > recent SoCs and also to save power during runtime. More information is
> > > available in patch 13/13.
> > > 
> > > Credits
> > > =======
> > > 
> > > This series is a continuation of previous work by Krzysztof Kozlowski [1]
> > > and Brian Masney [2]. Ideally, this could've split into two series (OPP
> > > and interconnect) but since there will be a dependency in the devicetree,
> > > I decided to keep them in a single series.
> > > 
> > > Testing
> > > =======
> > > 
> > > This series is tested on 96Boards RB3 (SDM845 SoC) and RB5 (SM8250 SoC)
> > > development boards.
> > > 
> > > Merging Strategy
> > > ================
> > > 
> > > An immutable branch might be required between OPP and SCSI trees because of
> > > the API dependency (devfreq too). And I leave it up to the maintainers to
> > > decide.
> > > 
> > > Thanks,
> > > Mani
> > > 
> > > [1] https://lore.kernel.org/all/20220513061347.46480-1-krzysztof.kozlowski@linaro.org/
> > > [2] https://lore.kernel.org/all/20221117104957.254648-1-bmasney@redhat.com/
> > > 
> > > Krzysztof Kozlowski (2):
> > >    dt-bindings: ufs: common: add OPP table
> > >    arm64: dts: qcom: sdm845: Add OPP table support to UFSHC
> > > 
> > > Manivannan Sadhasivam (12):
> > >    dt-bindings: opp: Increase maxItems for opp-hz property
> > >    arm64: dts: qcom: sdm845: Add missing RPMh power domain to GCC
> > >    arm64: dts: qcom: sdm845: Fix the min frequency of "ice_core_clk"
> > >    arm64: dts: qcom: sm8250: Add OPP table support to UFSHC
> > >    OPP: Introduce dev_pm_opp_find_freq_{ceil/floor}_indexed() APIs
> > >    OPP: Introduce dev_pm_opp_get_freq_indexed() API
> > >    PM / devfreq: Switch to dev_pm_opp_find_freq_{ceil/floor}_indexed()
> > >      APIs
> > >    scsi: ufs: core: Add OPP support for scaling clocks and regulators
> > >    scsi: ufs: host: Add support for parsing OPP
> > >    arm64: dts: qcom: sdm845: Add interconnect paths to UFSHC
> > >    arm64: dts: qcom: sm8250: Add interconnect paths to UFSHC
> > >    scsi: ufs: qcom: Add support for scaling interconnects
> > > 
> > >   .../devicetree/bindings/opp/opp-v2-base.yaml  |   2 +-
> > >   .../devicetree/bindings/ufs/ufs-common.yaml   |  34 ++++-
> > >   arch/arm64/boot/dts/qcom/sdm845.dtsi          |  47 ++++--
> > >   arch/arm64/boot/dts/qcom/sm8250.dtsi          |  43 ++++--
> > >   drivers/devfreq/devfreq.c                     |  14 +-
> > >   drivers/opp/core.c                            |  76 ++++++++++
> > >   drivers/ufs/core/ufshcd.c                     | 142 ++++++++++++++----
> > >   drivers/ufs/host/ufs-qcom.c                   | 131 +++++++++++++++-
> > >   drivers/ufs/host/ufs-qcom.h                   |   3 +
> > >   drivers/ufs/host/ufshcd-pltfrm.c              | 116 ++++++++++++++
> > >   include/linux/pm_opp.h                        |  26 ++++
> > >   include/ufs/ufshcd.h                          |   4 +
> > >   12 files changed, 574 insertions(+), 64 deletions(-)
> > > 
> > > -- 
> > > 2.25.1
> > > 
> > 
> 
> -- 
> With best wishes
> Dmitry
>
Dmitry Baryshkov July 12, 2023, 1:15 p.m. UTC | #9
On 12/07/2023 13:32, Manivannan Sadhasivam wrote:
> OPP framework can be used to scale the clocks along with other entities
> such as regulators, performance state etc... So let's add support for
> parsing OPP from devicetree. OPP support in devicetree is added through
> the "operating-points-v2" property which accepts the OPP table defining
> clock frequency, regulator voltage, power domain performance state etc...
> 
> Since the UFS controller requires multiple clocks to be controlled for
> proper working, devm_pm_opp_set_config() has been used which supports
> scaling multiple clocks through custom ufshcd_opp_config_clks() callback.
> 
> It should be noted that the OPP support is not compatible with the old
> "freq-table-hz" property. So only one can be used at a time even though
> the UFS core supports both.
> 
> Co-developed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> ---
>   drivers/ufs/host/ufshcd-pltfrm.c | 116 +++++++++++++++++++++++++++++++
>   1 file changed, 116 insertions(+)
> 
> diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c
> index 0b7430033047..068c22378c88 100644
> --- a/drivers/ufs/host/ufshcd-pltfrm.c
> +++ b/drivers/ufs/host/ufshcd-pltfrm.c
> @@ -8,8 +8,10 @@
>    *	Vinayak Holikatti <h.vinayak@samsung.com>
>    */
>   
> +#include <linux/clk.h>
>   #include <linux/module.h>
>   #include <linux/platform_device.h>
> +#include <linux/pm_opp.h>
>   #include <linux/pm_runtime.h>
>   #include <linux/of.h>
>   
> @@ -17,6 +19,8 @@
>   #include "ufshcd-pltfrm.h"
>   #include <ufs/unipro.h>
>   
> +#include <trace/events/ufs.h>
> +
>   #define UFSHCD_DEFAULT_LANES_PER_DIRECTION		2
>   
>   static int ufshcd_parse_clock_info(struct ufs_hba *hba)
> @@ -205,6 +209,112 @@ static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
>   	}
>   }
>   
> +static int ufshcd_opp_config_clks(struct device *dev, struct opp_table *opp_table,
> +				  struct dev_pm_opp *opp, void *data,
> +				  bool scaling_down)
> +{
> +	struct ufs_hba *hba = dev_get_drvdata(dev);
> +	struct list_head *head = &hba->clk_list_head;
> +	struct ufs_clk_info *clki;
> +	unsigned long freq;
> +	u8 idx = 0;
> +	int ret;
> +
> +	list_for_each_entry(clki, head, list) {
> +		if (!IS_ERR_OR_NULL(clki->clk)) {
> +			freq = dev_pm_opp_get_freq_indexed(opp, idx++);
> +
> +			/* Do not set rate for clocks having frequency as 0 */
> +			if (!freq)
> +				continue;

Can we omit these clocks from the opp table? I don't think they serve 
any purpose.

Maybe it would even make sense to move this function to drivers/opp 
then, as it will be generic enough.

> +
> +			ret = clk_set_rate(clki->clk, freq);
> +			if (ret) {
> +				dev_err(dev, "%s: %s clk set rate(%ldHz) failed, %d\n",
> +					__func__, clki->name, freq, ret);
> +				return ret;
> +			}
> +
> +			trace_ufshcd_clk_scaling(dev_name(dev),
> +				(scaling_down ? "scaled down" : "scaled up"),
> +				clki->name, hba->clk_scaling.target_freq, freq);
> +		}
> +	}
> +
> +	return 0;
> +} > +
> +static int ufshcd_parse_operating_points(struct ufs_hba *hba)
> +{
> +	struct device *dev = hba->dev;
> +	struct device_node *np = dev->of_node;
> +	struct dev_pm_opp_config config = {};
> +	struct ufs_clk_info *clki;
> +	const char **clk_names;
> +	int cnt, i, ret;
> +
> +	if (!of_find_property(np, "operating-points-v2", NULL))
> +		return 0;
> +
> +	if (of_find_property(np, "freq-table-hz", NULL)) {
> +		dev_err(dev, "%s: operating-points and freq-table-hz are incompatible\n",
> +			 __func__);
> +		return -EINVAL;
> +	}
> +
> +	cnt = of_property_count_strings(np, "clock-names");
> +	if (cnt <= 0) {
> +		dev_err(dev, "%s: Missing clock-names\n",  __func__);
> +		return -ENODEV;
> +	}
> +
> +	/* OPP expects clk_names to be NULL terminated */
> +	clk_names = devm_kcalloc(dev, cnt + 1, sizeof(*clk_names), GFP_KERNEL);
> +	if (!clk_names)
> +		return -ENOMEM;
> +
> +	/*
> +	 * We still need to get reference to all clocks as the UFS core uses
> +	 * them separately.
> +	 */
> +	for (i = 0; i < cnt; i++) {
> +		ret = of_property_read_string_index(np, "clock-names", i,
> +						    &clk_names[i]);
> +		if (ret)
> +			return ret;
> +
> +		clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
> +		if (!clki)
> +			return -ENOMEM;
> +
> +		clki->name = devm_kstrdup(dev, clk_names[i], GFP_KERNEL);
> +		if (!clki->name)
> +			return -ENOMEM;
> +
> +		if (!strcmp(clk_names[i], "ref_clk"))
> +			clki->keep_link_active = true;
> +
> +		list_add_tail(&clki->list, &hba->clk_list_head);
> +	}
> +
> +	config.clk_names = clk_names,
> +	config.config_clks = ufshcd_opp_config_clks;
> +
> +	ret = devm_pm_opp_set_config(dev, &config);
> +	if (ret)
> +		return ret;
> +
> +	ret = devm_pm_opp_of_add_table(dev);
> +	if (ret) {
> +		dev_err(dev, "Failed to add OPP table: %d\n", ret);
> +		return ret;
> +	}
> +
> +	hba->use_pm_opp = true;
> +
> +	return 0;
> +}
> +
>   /**
>    * ufshcd_get_pwr_dev_param - get finally agreed attributes for
>    *                            power mode change
> @@ -371,6 +481,12 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
>   
>   	ufshcd_init_lanes_per_dir(hba);
>   
> +	err = ufshcd_parse_operating_points(hba);
> +	if (err) {
> +		dev_err(dev, "%s: OPP parse failed %d\n", __func__, err);
> +		goto dealloc_host;
> +	}
> +
>   	err = ufshcd_init(hba, mmio_base, irq);
>   	if (err) {
>   		dev_err(dev, "Initialization failed\n");
Dmitry Baryshkov July 12, 2023, 1:22 p.m. UTC | #10
On 12/07/2023 13:32, Manivannan Sadhasivam wrote:
> Qcom SoCs require scaling the interconnect paths for proper working of the
> peripherals connected through interconnects. Even for accessing the UFS
> controller, someone should setup the interconnect paths. So far, the
> bootloaders used to setup the interconnect paths before booting linux as
> they need to access the UFS storage for things like fetching boot firmware.
> But with the advent of multi boot options, bootloader nowadays like in
> SA8540p SoC do not setup the interconnect paths at all.
> 
> So trying to configure UFS in the absence of the interconnect path
> configuration, results in boot crash.
> 
> To fix this issue and also to dynamically scale the interconnects (UFS-DDR
> and CPU-UFS), interconnect API support is added to the Qcom UFS driver.
> With this support, the interconnect paths are scaled dynamically based on
> the gear configuration.
> 
> During the early stage of ufs_qcom_init(), ufs_qcom_icc_init() will setup
> the paths to max bandwidth to allow configuring the UFS registers. Touching
> the registers without configuring the icc paths would result in a crash.
> However, we don't really need to set max vote for the icc paths as any
> minimal vote would suffice. But the max value would allow initialization to
> be done faster. After init, the bandwidth will get updated using
> ufs_qcom_icc_update_bw() based on the gear and lane configuration.
> 
> The bandwidth values defined in ufs_qcom_bw_table struct are taken from
> Qcom downstream vendor devicetree source and are calculated as per the
> UFS3.1 Spec, Section 6.4.1, HS Gear Rates. So it is fixed across platforms.
> 
> Cc: Brian Masney <bmasney@redhat.com>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> ---
>   drivers/ufs/host/ufs-qcom.c | 131 +++++++++++++++++++++++++++++++++++-
>   drivers/ufs/host/ufs-qcom.h |   3 +
>   2 files changed, 133 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
> index 8d6fd4c3324f..8a3132d45a65 100644
> --- a/drivers/ufs/host/ufs-qcom.c
> +++ b/drivers/ufs/host/ufs-qcom.c
> @@ -7,6 +7,7 @@
>   #include <linux/time.h>
>   #include <linux/clk.h>
>   #include <linux/delay.h>
> +#include <linux/interconnect.h>
>   #include <linux/module.h>
>   #include <linux/of.h>
>   #include <linux/platform_device.h>
> @@ -46,6 +47,49 @@ enum {
>   	TSTBUS_MAX,
>   };
>   
> +#define QCOM_UFS_MAX_GEAR 4
> +#define QCOM_UFS_MAX_LANE 2
> +
> +enum {
> +	MODE_MIN,
> +	MODE_PWM,
> +	MODE_HS_RA,
> +	MODE_HS_RB,
> +	MODE_MAX,

MODE_MIN and MODE_MAX seem to be unused

> +};
> +
> +struct __ufs_qcom_bw_table {
> +	u32 bw1;
> +	u32 bw2;

Please consider:

s/bw1/mem_bw/
s/bw2/cfg_bw/

> +} ufs_qcom_bw_table[MODE_MAX + 1][QCOM_UFS_MAX_GEAR + 1][QCOM_UFS_MAX_LANE + 1] = {

I'd say, these +1's are slightly confusing and unnecessary.

> +	[MODE_MIN][0][0] = { 0,		0 },	/* Bandwidth values are in KB/s */

I'd say, this becomes impossible to check. Can you please structure it?
Either by inclusion:

[MODE_PWM] = {
   [1] = {
      [1] = { .. },
      [2] = { .. },
   },
   // etc.
};

Also, do we have defines for gears? Can we use them instead of indices?

> +	[MODE_PWM][1][1] = { 922,	1000 },
> +	[MODE_PWM][2][1] = { 1844,	1000 },
> +	[MODE_PWM][3][1] = { 3688,	1000 },
> +	[MODE_PWM][4][1] = { 7376,	1000 },
> +	[MODE_PWM][1][2] = { 1844,	1000 },
> +	[MODE_PWM][2][2] = { 3688,	1000 },
> +	[MODE_PWM][3][2] = { 7376,	1000 },
> +	[MODE_PWM][4][2] = { 14752,	1000 },
> +	[MODE_HS_RA][1][1] = { 127796,	1000 },
> +	[MODE_HS_RA][2][1] = { 255591,	1000 },
> +	[MODE_HS_RA][3][1] = { 1492582,	102400 },
> +	[MODE_HS_RA][4][1] = { 2915200,	204800 },
> +	[MODE_HS_RA][1][2] = { 255591,	1000 },
> +	[MODE_HS_RA][2][2] = { 511181,	1000 },
> +	[MODE_HS_RA][3][2] = { 1492582,	204800 },
> +	[MODE_HS_RA][4][2] = { 2915200,	409600 },
> +	[MODE_HS_RB][1][1] = { 149422,	1000 },
> +	[MODE_HS_RB][2][1] = { 298189,	1000 },
> +	[MODE_HS_RB][3][1] = { 1492582,	102400 },
> +	[MODE_HS_RB][4][1] = { 2915200,	204800 },
> +	[MODE_HS_RB][1][2] = { 298189,	1000 },
> +	[MODE_HS_RB][2][2] = { 596378,	1000 },
> +	[MODE_HS_RB][3][2] = { 1492582,	204800 },
> +	[MODE_HS_RB][4][2] = { 2915200,	409600 },
> +	[MODE_MAX][0][0] = { 7643136, 307200 },
> +};
> +
>   static struct ufs_qcom_host *ufs_qcom_hosts[MAX_UFS_QCOM_HOSTS];
>   
>   static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host);
> @@ -789,6 +833,51 @@ static void ufs_qcom_dev_ref_clk_ctrl(struct ufs_qcom_host *host, bool enable)
>   	}
>   }
>   
> +static int ufs_qcom_icc_set_bw(struct ufs_qcom_host *host, u32 bw1, u32 bw2)
> +{
> +	struct device *dev = host->hba->dev;
> +	int ret;
> +
> +	ret = icc_set_bw(host->icc_ddr, 0, bw1);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to set bandwidth request: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = icc_set_bw(host->icc_cpu, 0, bw2);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to set bandwidth request: %d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct __ufs_qcom_bw_table ufs_qcom_get_bw_table(struct ufs_qcom_host *host)
> +{
> +	struct ufs_pa_layer_attr *p = &host->dev_req_params;
> +	int gear = max_t(u32, p->gear_rx, p->gear_tx);
> +	int lane = max_t(u32, p->lane_rx, p->lane_tx);
> +
> +	if (ufshcd_is_hs_mode(p)) {
> +		if (p->hs_rate == PA_HS_MODE_B)
> +			return ufs_qcom_bw_table[MODE_HS_RB][gear][lane];
> +		else
> +			return ufs_qcom_bw_table[MODE_HS_RA][gear][lane];
> +	} else {
> +		return ufs_qcom_bw_table[MODE_PWM][gear][lane];
> +	}
> +}
> +
> +static int ufs_qcom_icc_update_bw(struct ufs_qcom_host *host)
> +{
> +	struct __ufs_qcom_bw_table bw_table;
> +
> +	bw_table = ufs_qcom_get_bw_table(host);
> +
> +	return ufs_qcom_icc_set_bw(host, bw_table.bw1, bw_table.bw2);
> +}
> +
>   static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
>   				enum ufs_notify_change_status status,
>   				struct ufs_pa_layer_attr *dev_max_params,
> @@ -852,6 +941,8 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
>   		memcpy(&host->dev_req_params,
>   				dev_req_params, sizeof(*dev_req_params));
>   
> +		ufs_qcom_icc_update_bw(host);
> +
>   		/* disable the device ref clock if entered PWM mode */
>   		if (ufshcd_is_hs_mode(&hba->pwr_info) &&
>   			!ufshcd_is_hs_mode(dev_req_params))
> @@ -981,7 +1072,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
>   
>   	switch (status) {
>   	case PRE_CHANGE:
> -		if (!on) {
> +		if (on) {
> +			ufs_qcom_icc_update_bw(host);
> +		} else {
>   			if (!ufs_qcom_is_link_active(hba)) {
>   				/* disable device ref_clk */
>   				ufs_qcom_dev_ref_clk_ctrl(host, false);
> @@ -993,6 +1086,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
>   			/* enable the device ref clock for HS mode*/
>   			if (ufshcd_is_hs_mode(&hba->pwr_info))
>   				ufs_qcom_dev_ref_clk_ctrl(host, true);
> +		} else {
> +			ufs_qcom_icc_set_bw(host, ufs_qcom_bw_table[MODE_MIN][0][0].bw1,
> +					    ufs_qcom_bw_table[MODE_MIN][0][0].bw2);
>   		}
>   		break;
>   	}
> @@ -1031,6 +1127,34 @@ static const struct reset_control_ops ufs_qcom_reset_ops = {
>   	.deassert = ufs_qcom_reset_deassert,
>   };
>   
> +static int ufs_qcom_icc_init(struct ufs_qcom_host *host)
> +{
> +	struct device *dev = host->hba->dev;
> +	int ret;
> +
> +	host->icc_ddr = devm_of_icc_get(dev, "ufs-ddr");
> +	if (IS_ERR(host->icc_ddr))
> +		return dev_err_probe(dev, PTR_ERR(host->icc_ddr),
> +				    "failed to acquire interconnect path\n");
> +
> +	host->icc_cpu = devm_of_icc_get(dev, "cpu-ufs");
> +	if (IS_ERR(host->icc_cpu))
> +		return dev_err_probe(dev, PTR_ERR(host->icc_cpu),
> +				    "failed to acquire interconnect path\n");
> +
> +	/*
> +	 * Set Maximum bandwidth vote before initializing the UFS controller and
> +	 * device. Ideally, a minimal interconnect vote would suffice for the
> +	 * initialization, but a max vote would allow faster initialization.
> +	 */
> +	ret = ufs_qcom_icc_set_bw(host, ufs_qcom_bw_table[MODE_MAX][0][0].bw1,
> +				  ufs_qcom_bw_table[MODE_MAX][0][0].bw2);
> +	if (ret < 0)
> +		return dev_err_probe(dev, ret, "failed to set bandwidth request\n");
> +
> +	return 0;
> +}
> +
>   /**
>    * ufs_qcom_init - bind phy with controller
>    * @hba: host controller instance
> @@ -1085,6 +1209,10 @@ static int ufs_qcom_init(struct ufs_hba *hba)
>   		}
>   	}
>   
> +	err = ufs_qcom_icc_init(host);
> +	if (err)
> +		goto out_variant_clear;
> +
>   	host->device_reset = devm_gpiod_get_optional(dev, "reset",
>   						     GPIOD_OUT_HIGH);
>   	if (IS_ERR(host->device_reset)) {
> @@ -1282,6 +1410,7 @@ static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba,
>   				    dev_req_params->pwr_rx,
>   				    dev_req_params->hs_rate,
>   				    false);
> +		ufs_qcom_icc_update_bw(host);
>   		ufshcd_uic_hibern8_exit(hba);
>   	}
>   
> diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h
> index 6289ad5a42d0..dc27395ecba1 100644
> --- a/drivers/ufs/host/ufs-qcom.h
> +++ b/drivers/ufs/host/ufs-qcom.h
> @@ -206,6 +206,9 @@ struct ufs_qcom_host {
>   	struct clk *tx_l1_sync_clk;
>   	bool is_lane_clks_enabled;
>   
> +	struct icc_path *icc_ddr;
> +	struct icc_path *icc_cpu;
> +
>   #ifdef CONFIG_SCSI_UFS_CRYPTO
>   	struct qcom_ice *ice;
>   #endif
Manivannan Sadhasivam July 12, 2023, 4:34 p.m. UTC | #11
On Wed, Jul 12, 2023 at 04:15:12PM +0300, Dmitry Baryshkov wrote:
> On 12/07/2023 13:32, Manivannan Sadhasivam wrote:
> > OPP framework can be used to scale the clocks along with other entities
> > such as regulators, performance state etc... So let's add support for
> > parsing OPP from devicetree. OPP support in devicetree is added through
> > the "operating-points-v2" property which accepts the OPP table defining
> > clock frequency, regulator voltage, power domain performance state etc...
> > 
> > Since the UFS controller requires multiple clocks to be controlled for
> > proper working, devm_pm_opp_set_config() has been used which supports
> > scaling multiple clocks through custom ufshcd_opp_config_clks() callback.
> > 
> > It should be noted that the OPP support is not compatible with the old
> > "freq-table-hz" property. So only one can be used at a time even though
> > the UFS core supports both.
> > 
> > Co-developed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> > Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> > Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> > ---
> >   drivers/ufs/host/ufshcd-pltfrm.c | 116 +++++++++++++++++++++++++++++++
> >   1 file changed, 116 insertions(+)
> > 
> > diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c
> > index 0b7430033047..068c22378c88 100644
> > --- a/drivers/ufs/host/ufshcd-pltfrm.c
> > +++ b/drivers/ufs/host/ufshcd-pltfrm.c
> > @@ -8,8 +8,10 @@
> >    *	Vinayak Holikatti <h.vinayak@samsung.com>
> >    */
> > +#include <linux/clk.h>
> >   #include <linux/module.h>
> >   #include <linux/platform_device.h>
> > +#include <linux/pm_opp.h>
> >   #include <linux/pm_runtime.h>
> >   #include <linux/of.h>
> > @@ -17,6 +19,8 @@
> >   #include "ufshcd-pltfrm.h"
> >   #include <ufs/unipro.h>
> > +#include <trace/events/ufs.h>
> > +
> >   #define UFSHCD_DEFAULT_LANES_PER_DIRECTION		2
> >   static int ufshcd_parse_clock_info(struct ufs_hba *hba)
> > @@ -205,6 +209,112 @@ static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
> >   	}
> >   }
> > +static int ufshcd_opp_config_clks(struct device *dev, struct opp_table *opp_table,
> > +				  struct dev_pm_opp *opp, void *data,
> > +				  bool scaling_down)
> > +{
> > +	struct ufs_hba *hba = dev_get_drvdata(dev);
> > +	struct list_head *head = &hba->clk_list_head;
> > +	struct ufs_clk_info *clki;
> > +	unsigned long freq;
> > +	u8 idx = 0;
> > +	int ret;
> > +
> > +	list_for_each_entry(clki, head, list) {
> > +		if (!IS_ERR_OR_NULL(clki->clk)) {
> > +			freq = dev_pm_opp_get_freq_indexed(opp, idx++);
> > +
> > +			/* Do not set rate for clocks having frequency as 0 */
> > +			if (!freq)
> > +				continue;
> 
> Can we omit these clocks from the opp table? I don't think they serve any
> purpose.
> 

No, we cannot. OPP requires the clocks and opp-hz to be of same length. And we
cannot omit those clocks as well since linux needs to gate control them.

> Maybe it would even make sense to move this function to drivers/opp then, as
> it will be generic enough.
> 

There is already a generic function available in OPP core. But we cannot use it
as we need to skip setting 0 freq and that's not applicable in OPP core as
discussed with Viresh offline.

- Mani

> > +
> > +			ret = clk_set_rate(clki->clk, freq);
> > +			if (ret) {
> > +				dev_err(dev, "%s: %s clk set rate(%ldHz) failed, %d\n",
> > +					__func__, clki->name, freq, ret);
> > +				return ret;
> > +			}
> > +
> > +			trace_ufshcd_clk_scaling(dev_name(dev),
> > +				(scaling_down ? "scaled down" : "scaled up"),
> > +				clki->name, hba->clk_scaling.target_freq, freq);
> > +		}
> > +	}
> > +
> > +	return 0;
> > +} > +
> > +static int ufshcd_parse_operating_points(struct ufs_hba *hba)
> > +{
> > +	struct device *dev = hba->dev;
> > +	struct device_node *np = dev->of_node;
> > +	struct dev_pm_opp_config config = {};
> > +	struct ufs_clk_info *clki;
> > +	const char **clk_names;
> > +	int cnt, i, ret;
> > +
> > +	if (!of_find_property(np, "operating-points-v2", NULL))
> > +		return 0;
> > +
> > +	if (of_find_property(np, "freq-table-hz", NULL)) {
> > +		dev_err(dev, "%s: operating-points and freq-table-hz are incompatible\n",
> > +			 __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	cnt = of_property_count_strings(np, "clock-names");
> > +	if (cnt <= 0) {
> > +		dev_err(dev, "%s: Missing clock-names\n",  __func__);
> > +		return -ENODEV;
> > +	}
> > +
> > +	/* OPP expects clk_names to be NULL terminated */
> > +	clk_names = devm_kcalloc(dev, cnt + 1, sizeof(*clk_names), GFP_KERNEL);
> > +	if (!clk_names)
> > +		return -ENOMEM;
> > +
> > +	/*
> > +	 * We still need to get reference to all clocks as the UFS core uses
> > +	 * them separately.
> > +	 */
> > +	for (i = 0; i < cnt; i++) {
> > +		ret = of_property_read_string_index(np, "clock-names", i,
> > +						    &clk_names[i]);
> > +		if (ret)
> > +			return ret;
> > +
> > +		clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
> > +		if (!clki)
> > +			return -ENOMEM;
> > +
> > +		clki->name = devm_kstrdup(dev, clk_names[i], GFP_KERNEL);
> > +		if (!clki->name)
> > +			return -ENOMEM;
> > +
> > +		if (!strcmp(clk_names[i], "ref_clk"))
> > +			clki->keep_link_active = true;
> > +
> > +		list_add_tail(&clki->list, &hba->clk_list_head);
> > +	}
> > +
> > +	config.clk_names = clk_names,
> > +	config.config_clks = ufshcd_opp_config_clks;
> > +
> > +	ret = devm_pm_opp_set_config(dev, &config);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = devm_pm_opp_of_add_table(dev);
> > +	if (ret) {
> > +		dev_err(dev, "Failed to add OPP table: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	hba->use_pm_opp = true;
> > +
> > +	return 0;
> > +}
> > +
> >   /**
> >    * ufshcd_get_pwr_dev_param - get finally agreed attributes for
> >    *                            power mode change
> > @@ -371,6 +481,12 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
> >   	ufshcd_init_lanes_per_dir(hba);
> > +	err = ufshcd_parse_operating_points(hba);
> > +	if (err) {
> > +		dev_err(dev, "%s: OPP parse failed %d\n", __func__, err);
> > +		goto dealloc_host;
> > +	}
> > +
> >   	err = ufshcd_init(hba, mmio_base, irq);
> >   	if (err) {
> >   		dev_err(dev, "Initialization failed\n");
> 
> -- 
> With best wishes
> Dmitry
>
Manivannan Sadhasivam July 12, 2023, 4:41 p.m. UTC | #12
On Wed, Jul 12, 2023 at 04:22:51PM +0300, Dmitry Baryshkov wrote:
> On 12/07/2023 13:32, Manivannan Sadhasivam wrote:
> > Qcom SoCs require scaling the interconnect paths for proper working of the
> > peripherals connected through interconnects. Even for accessing the UFS
> > controller, someone should setup the interconnect paths. So far, the
> > bootloaders used to setup the interconnect paths before booting linux as
> > they need to access the UFS storage for things like fetching boot firmware.
> > But with the advent of multi boot options, bootloader nowadays like in
> > SA8540p SoC do not setup the interconnect paths at all.
> > 
> > So trying to configure UFS in the absence of the interconnect path
> > configuration, results in boot crash.
> > 
> > To fix this issue and also to dynamically scale the interconnects (UFS-DDR
> > and CPU-UFS), interconnect API support is added to the Qcom UFS driver.
> > With this support, the interconnect paths are scaled dynamically based on
> > the gear configuration.
> > 
> > During the early stage of ufs_qcom_init(), ufs_qcom_icc_init() will setup
> > the paths to max bandwidth to allow configuring the UFS registers. Touching
> > the registers without configuring the icc paths would result in a crash.
> > However, we don't really need to set max vote for the icc paths as any
> > minimal vote would suffice. But the max value would allow initialization to
> > be done faster. After init, the bandwidth will get updated using
> > ufs_qcom_icc_update_bw() based on the gear and lane configuration.
> > 
> > The bandwidth values defined in ufs_qcom_bw_table struct are taken from
> > Qcom downstream vendor devicetree source and are calculated as per the
> > UFS3.1 Spec, Section 6.4.1, HS Gear Rates. So it is fixed across platforms.
> > 
> > Cc: Brian Masney <bmasney@redhat.com>
> > Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> > ---
> >   drivers/ufs/host/ufs-qcom.c | 131 +++++++++++++++++++++++++++++++++++-
> >   drivers/ufs/host/ufs-qcom.h |   3 +
> >   2 files changed, 133 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
> > index 8d6fd4c3324f..8a3132d45a65 100644
> > --- a/drivers/ufs/host/ufs-qcom.c
> > +++ b/drivers/ufs/host/ufs-qcom.c
> > @@ -7,6 +7,7 @@
> >   #include <linux/time.h>
> >   #include <linux/clk.h>
> >   #include <linux/delay.h>
> > +#include <linux/interconnect.h>
> >   #include <linux/module.h>
> >   #include <linux/of.h>
> >   #include <linux/platform_device.h>
> > @@ -46,6 +47,49 @@ enum {
> >   	TSTBUS_MAX,
> >   };
> > +#define QCOM_UFS_MAX_GEAR 4
> > +#define QCOM_UFS_MAX_LANE 2
> > +
> > +enum {
> > +	MODE_MIN,
> > +	MODE_PWM,
> > +	MODE_HS_RA,
> > +	MODE_HS_RB,
> > +	MODE_MAX,
> 
> MODE_MIN and MODE_MAX seem to be unused
> 

No, they are used in the driver.

> > +};
> > +
> > +struct __ufs_qcom_bw_table {
> > +	u32 bw1;
> > +	u32 bw2;
> 
> Please consider:
> 
> s/bw1/mem_bw/
> s/bw2/cfg_bw/
> 

Ok.

> > +} ufs_qcom_bw_table[MODE_MAX + 1][QCOM_UFS_MAX_GEAR + 1][QCOM_UFS_MAX_LANE + 1] = {
> 
> I'd say, these +1's are slightly confusing and unnecessary.
> 

These max values itself are used in the table. Without adding 1 to them, we will
end up with out of bounds access.

> > +	[MODE_MIN][0][0] = { 0,		0 },	/* Bandwidth values are in KB/s */
> 
> I'd say, this becomes impossible to check. Can you please structure it?

What do you mean by "impossible to check"?

> Either by inclusion:
> 
> [MODE_PWM] = {
>   [1] = {
>      [1] = { .. },
>      [2] = { .. },
>   },
>   // etc.

Now, this seems confusion to me. The existing table looks fine IMO.

> };
> 
> Also, do we have defines for gears? Can we use them instead of indices?
> 

There are defines for the gears but not for lanes. So I ended up using numbers
for simplicity.

- Mani

> > +	[MODE_PWM][1][1] = { 922,	1000 },
> > +	[MODE_PWM][2][1] = { 1844,	1000 },
> > +	[MODE_PWM][3][1] = { 3688,	1000 },
> > +	[MODE_PWM][4][1] = { 7376,	1000 },
> > +	[MODE_PWM][1][2] = { 1844,	1000 },
> > +	[MODE_PWM][2][2] = { 3688,	1000 },
> > +	[MODE_PWM][3][2] = { 7376,	1000 },
> > +	[MODE_PWM][4][2] = { 14752,	1000 },
> > +	[MODE_HS_RA][1][1] = { 127796,	1000 },
> > +	[MODE_HS_RA][2][1] = { 255591,	1000 },
> > +	[MODE_HS_RA][3][1] = { 1492582,	102400 },
> > +	[MODE_HS_RA][4][1] = { 2915200,	204800 },
> > +	[MODE_HS_RA][1][2] = { 255591,	1000 },
> > +	[MODE_HS_RA][2][2] = { 511181,	1000 },
> > +	[MODE_HS_RA][3][2] = { 1492582,	204800 },
> > +	[MODE_HS_RA][4][2] = { 2915200,	409600 },
> > +	[MODE_HS_RB][1][1] = { 149422,	1000 },
> > +	[MODE_HS_RB][2][1] = { 298189,	1000 },
> > +	[MODE_HS_RB][3][1] = { 1492582,	102400 },
> > +	[MODE_HS_RB][4][1] = { 2915200,	204800 },
> > +	[MODE_HS_RB][1][2] = { 298189,	1000 },
> > +	[MODE_HS_RB][2][2] = { 596378,	1000 },
> > +	[MODE_HS_RB][3][2] = { 1492582,	204800 },
> > +	[MODE_HS_RB][4][2] = { 2915200,	409600 },
> > +	[MODE_MAX][0][0] = { 7643136, 307200 },
> > +};
> > +
> >   static struct ufs_qcom_host *ufs_qcom_hosts[MAX_UFS_QCOM_HOSTS];
> >   static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host);
> > @@ -789,6 +833,51 @@ static void ufs_qcom_dev_ref_clk_ctrl(struct ufs_qcom_host *host, bool enable)
> >   	}
> >   }
> > +static int ufs_qcom_icc_set_bw(struct ufs_qcom_host *host, u32 bw1, u32 bw2)
> > +{
> > +	struct device *dev = host->hba->dev;
> > +	int ret;
> > +
> > +	ret = icc_set_bw(host->icc_ddr, 0, bw1);
> > +	if (ret < 0) {
> > +		dev_err(dev, "failed to set bandwidth request: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = icc_set_bw(host->icc_cpu, 0, bw2);
> > +	if (ret < 0) {
> > +		dev_err(dev, "failed to set bandwidth request: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static struct __ufs_qcom_bw_table ufs_qcom_get_bw_table(struct ufs_qcom_host *host)
> > +{
> > +	struct ufs_pa_layer_attr *p = &host->dev_req_params;
> > +	int gear = max_t(u32, p->gear_rx, p->gear_tx);
> > +	int lane = max_t(u32, p->lane_rx, p->lane_tx);
> > +
> > +	if (ufshcd_is_hs_mode(p)) {
> > +		if (p->hs_rate == PA_HS_MODE_B)
> > +			return ufs_qcom_bw_table[MODE_HS_RB][gear][lane];
> > +		else
> > +			return ufs_qcom_bw_table[MODE_HS_RA][gear][lane];
> > +	} else {
> > +		return ufs_qcom_bw_table[MODE_PWM][gear][lane];
> > +	}
> > +}
> > +
> > +static int ufs_qcom_icc_update_bw(struct ufs_qcom_host *host)
> > +{
> > +	struct __ufs_qcom_bw_table bw_table;
> > +
> > +	bw_table = ufs_qcom_get_bw_table(host);
> > +
> > +	return ufs_qcom_icc_set_bw(host, bw_table.bw1, bw_table.bw2);
> > +}
> > +
> >   static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
> >   				enum ufs_notify_change_status status,
> >   				struct ufs_pa_layer_attr *dev_max_params,
> > @@ -852,6 +941,8 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
> >   		memcpy(&host->dev_req_params,
> >   				dev_req_params, sizeof(*dev_req_params));
> > +		ufs_qcom_icc_update_bw(host);
> > +
> >   		/* disable the device ref clock if entered PWM mode */
> >   		if (ufshcd_is_hs_mode(&hba->pwr_info) &&
> >   			!ufshcd_is_hs_mode(dev_req_params))
> > @@ -981,7 +1072,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
> >   	switch (status) {
> >   	case PRE_CHANGE:
> > -		if (!on) {
> > +		if (on) {
> > +			ufs_qcom_icc_update_bw(host);
> > +		} else {
> >   			if (!ufs_qcom_is_link_active(hba)) {
> >   				/* disable device ref_clk */
> >   				ufs_qcom_dev_ref_clk_ctrl(host, false);
> > @@ -993,6 +1086,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
> >   			/* enable the device ref clock for HS mode*/
> >   			if (ufshcd_is_hs_mode(&hba->pwr_info))
> >   				ufs_qcom_dev_ref_clk_ctrl(host, true);
> > +		} else {
> > +			ufs_qcom_icc_set_bw(host, ufs_qcom_bw_table[MODE_MIN][0][0].bw1,
> > +					    ufs_qcom_bw_table[MODE_MIN][0][0].bw2);
> >   		}
> >   		break;
> >   	}
> > @@ -1031,6 +1127,34 @@ static const struct reset_control_ops ufs_qcom_reset_ops = {
> >   	.deassert = ufs_qcom_reset_deassert,
> >   };
> > +static int ufs_qcom_icc_init(struct ufs_qcom_host *host)
> > +{
> > +	struct device *dev = host->hba->dev;
> > +	int ret;
> > +
> > +	host->icc_ddr = devm_of_icc_get(dev, "ufs-ddr");
> > +	if (IS_ERR(host->icc_ddr))
> > +		return dev_err_probe(dev, PTR_ERR(host->icc_ddr),
> > +				    "failed to acquire interconnect path\n");
> > +
> > +	host->icc_cpu = devm_of_icc_get(dev, "cpu-ufs");
> > +	if (IS_ERR(host->icc_cpu))
> > +		return dev_err_probe(dev, PTR_ERR(host->icc_cpu),
> > +				    "failed to acquire interconnect path\n");
> > +
> > +	/*
> > +	 * Set Maximum bandwidth vote before initializing the UFS controller and
> > +	 * device. Ideally, a minimal interconnect vote would suffice for the
> > +	 * initialization, but a max vote would allow faster initialization.
> > +	 */
> > +	ret = ufs_qcom_icc_set_bw(host, ufs_qcom_bw_table[MODE_MAX][0][0].bw1,
> > +				  ufs_qcom_bw_table[MODE_MAX][0][0].bw2);
> > +	if (ret < 0)
> > +		return dev_err_probe(dev, ret, "failed to set bandwidth request\n");
> > +
> > +	return 0;
> > +}
> > +
> >   /**
> >    * ufs_qcom_init - bind phy with controller
> >    * @hba: host controller instance
> > @@ -1085,6 +1209,10 @@ static int ufs_qcom_init(struct ufs_hba *hba)
> >   		}
> >   	}
> > +	err = ufs_qcom_icc_init(host);
> > +	if (err)
> > +		goto out_variant_clear;
> > +
> >   	host->device_reset = devm_gpiod_get_optional(dev, "reset",
> >   						     GPIOD_OUT_HIGH);
> >   	if (IS_ERR(host->device_reset)) {
> > @@ -1282,6 +1410,7 @@ static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba,
> >   				    dev_req_params->pwr_rx,
> >   				    dev_req_params->hs_rate,
> >   				    false);
> > +		ufs_qcom_icc_update_bw(host);
> >   		ufshcd_uic_hibern8_exit(hba);
> >   	}
> > diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h
> > index 6289ad5a42d0..dc27395ecba1 100644
> > --- a/drivers/ufs/host/ufs-qcom.h
> > +++ b/drivers/ufs/host/ufs-qcom.h
> > @@ -206,6 +206,9 @@ struct ufs_qcom_host {
> >   	struct clk *tx_l1_sync_clk;
> >   	bool is_lane_clks_enabled;
> > +	struct icc_path *icc_ddr;
> > +	struct icc_path *icc_cpu;
> > +
> >   #ifdef CONFIG_SCSI_UFS_CRYPTO
> >   	struct qcom_ice *ice;
> >   #endif
> 
> -- 
> With best wishes
> Dmitry
>
Dmitry Baryshkov July 12, 2023, 4:48 p.m. UTC | #13
On Wed, 12 Jul 2023 at 19:34, Manivannan Sadhasivam
<manivannan.sadhasivam@linaro.org> wrote:
>
> On Wed, Jul 12, 2023 at 04:15:12PM +0300, Dmitry Baryshkov wrote:
> > On 12/07/2023 13:32, Manivannan Sadhasivam wrote:
> > > OPP framework can be used to scale the clocks along with other entities
> > > such as regulators, performance state etc... So let's add support for
> > > parsing OPP from devicetree. OPP support in devicetree is added through
> > > the "operating-points-v2" property which accepts the OPP table defining
> > > clock frequency, regulator voltage, power domain performance state etc...
> > >
> > > Since the UFS controller requires multiple clocks to be controlled for
> > > proper working, devm_pm_opp_set_config() has been used which supports
> > > scaling multiple clocks through custom ufshcd_opp_config_clks() callback.
> > >
> > > It should be noted that the OPP support is not compatible with the old
> > > "freq-table-hz" property. So only one can be used at a time even though
> > > the UFS core supports both.
> > >
> > > Co-developed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> > > Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> > > Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> > > ---
> > >   drivers/ufs/host/ufshcd-pltfrm.c | 116 +++++++++++++++++++++++++++++++
> > >   1 file changed, 116 insertions(+)
> > >
> > > diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c
> > > index 0b7430033047..068c22378c88 100644
> > > --- a/drivers/ufs/host/ufshcd-pltfrm.c
> > > +++ b/drivers/ufs/host/ufshcd-pltfrm.c
> > > @@ -8,8 +8,10 @@
> > >    *        Vinayak Holikatti <h.vinayak@samsung.com>
> > >    */
> > > +#include <linux/clk.h>
> > >   #include <linux/module.h>
> > >   #include <linux/platform_device.h>
> > > +#include <linux/pm_opp.h>
> > >   #include <linux/pm_runtime.h>
> > >   #include <linux/of.h>
> > > @@ -17,6 +19,8 @@
> > >   #include "ufshcd-pltfrm.h"
> > >   #include <ufs/unipro.h>
> > > +#include <trace/events/ufs.h>
> > > +
> > >   #define UFSHCD_DEFAULT_LANES_PER_DIRECTION                2
> > >   static int ufshcd_parse_clock_info(struct ufs_hba *hba)
> > > @@ -205,6 +209,112 @@ static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
> > >     }
> > >   }
> > > +static int ufshcd_opp_config_clks(struct device *dev, struct opp_table *opp_table,
> > > +                             struct dev_pm_opp *opp, void *data,
> > > +                             bool scaling_down)
> > > +{
> > > +   struct ufs_hba *hba = dev_get_drvdata(dev);
> > > +   struct list_head *head = &hba->clk_list_head;
> > > +   struct ufs_clk_info *clki;
> > > +   unsigned long freq;
> > > +   u8 idx = 0;
> > > +   int ret;
> > > +
> > > +   list_for_each_entry(clki, head, list) {
> > > +           if (!IS_ERR_OR_NULL(clki->clk)) {
> > > +                   freq = dev_pm_opp_get_freq_indexed(opp, idx++);
> > > +
> > > +                   /* Do not set rate for clocks having frequency as 0 */
> > > +                   if (!freq)
> > > +                           continue;
> >
> > Can we omit these clocks from the opp table? I don't think they serve any
> > purpose.
> >
>
> No, we cannot. OPP requires the clocks and opp-hz to be of same length. And we
> cannot omit those clocks as well since linux needs to gate control them.

Hmm, I thought we push the list of "interesting" clocks through
config->clock_names.

>
> > Maybe it would even make sense to move this function to drivers/opp then, as
> > it will be generic enough.
> >
>
> There is already a generic function available in OPP core. But we cannot use it
> as we need to skip setting 0 freq and that's not applicable in OPP core as
> discussed with Viresh offline.

Ack.

>
> - Mani
>
> > > +
> > > +                   ret = clk_set_rate(clki->clk, freq);
> > > +                   if (ret) {
> > > +                           dev_err(dev, "%s: %s clk set rate(%ldHz) failed, %d\n",
> > > +                                   __func__, clki->name, freq, ret);
> > > +                           return ret;
> > > +                   }
> > > +
> > > +                   trace_ufshcd_clk_scaling(dev_name(dev),
> > > +                           (scaling_down ? "scaled down" : "scaled up"),
> > > +                           clki->name, hba->clk_scaling.target_freq, freq);
> > > +           }
> > > +   }
> > > +
> > > +   return 0;
> > > +} > +
> > > +static int ufshcd_parse_operating_points(struct ufs_hba *hba)
> > > +{
> > > +   struct device *dev = hba->dev;
> > > +   struct device_node *np = dev->of_node;
> > > +   struct dev_pm_opp_config config = {};
> > > +   struct ufs_clk_info *clki;
> > > +   const char **clk_names;
> > > +   int cnt, i, ret;
> > > +
> > > +   if (!of_find_property(np, "operating-points-v2", NULL))
> > > +           return 0;
> > > +
> > > +   if (of_find_property(np, "freq-table-hz", NULL)) {
> > > +           dev_err(dev, "%s: operating-points and freq-table-hz are incompatible\n",
> > > +                    __func__);
> > > +           return -EINVAL;
> > > +   }
> > > +
> > > +   cnt = of_property_count_strings(np, "clock-names");
> > > +   if (cnt <= 0) {
> > > +           dev_err(dev, "%s: Missing clock-names\n",  __func__);
> > > +           return -ENODEV;
> > > +   }
> > > +
> > > +   /* OPP expects clk_names to be NULL terminated */
> > > +   clk_names = devm_kcalloc(dev, cnt + 1, sizeof(*clk_names), GFP_KERNEL);
> > > +   if (!clk_names)
> > > +           return -ENOMEM;
> > > +
> > > +   /*
> > > +    * We still need to get reference to all clocks as the UFS core uses
> > > +    * them separately.
> > > +    */
> > > +   for (i = 0; i < cnt; i++) {
> > > +           ret = of_property_read_string_index(np, "clock-names", i,
> > > +                                               &clk_names[i]);
> > > +           if (ret)
> > > +                   return ret;
> > > +
> > > +           clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
> > > +           if (!clki)
> > > +                   return -ENOMEM;
> > > +
> > > +           clki->name = devm_kstrdup(dev, clk_names[i], GFP_KERNEL);
> > > +           if (!clki->name)
> > > +                   return -ENOMEM;
> > > +
> > > +           if (!strcmp(clk_names[i], "ref_clk"))
> > > +                   clki->keep_link_active = true;
> > > +
> > > +           list_add_tail(&clki->list, &hba->clk_list_head);
> > > +   }
> > > +
> > > +   config.clk_names = clk_names,
> > > +   config.config_clks = ufshcd_opp_config_clks;
> > > +
> > > +   ret = devm_pm_opp_set_config(dev, &config);
> > > +   if (ret)
> > > +           return ret;
> > > +
> > > +   ret = devm_pm_opp_of_add_table(dev);
> > > +   if (ret) {
> > > +           dev_err(dev, "Failed to add OPP table: %d\n", ret);
> > > +           return ret;
> > > +   }
> > > +
> > > +   hba->use_pm_opp = true;
> > > +
> > > +   return 0;
> > > +}
> > > +
> > >   /**
> > >    * ufshcd_get_pwr_dev_param - get finally agreed attributes for
> > >    *                            power mode change
> > > @@ -371,6 +481,12 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
> > >     ufshcd_init_lanes_per_dir(hba);
> > > +   err = ufshcd_parse_operating_points(hba);
> > > +   if (err) {
> > > +           dev_err(dev, "%s: OPP parse failed %d\n", __func__, err);
> > > +           goto dealloc_host;
> > > +   }
> > > +
> > >     err = ufshcd_init(hba, mmio_base, irq);
> > >     if (err) {
> > >             dev_err(dev, "Initialization failed\n");
> >
> > --
> > With best wishes
> > Dmitry
> >
>
> --
> மணிவண்ணன் சதாசிவம்
Dmitry Baryshkov July 12, 2023, 5:23 p.m. UTC | #14
On 12/07/2023 19:41, Manivannan Sadhasivam wrote:
> On Wed, Jul 12, 2023 at 04:22:51PM +0300, Dmitry Baryshkov wrote:
>> On 12/07/2023 13:32, Manivannan Sadhasivam wrote:
>>> Qcom SoCs require scaling the interconnect paths for proper working of the
>>> peripherals connected through interconnects. Even for accessing the UFS
>>> controller, someone should setup the interconnect paths. So far, the
>>> bootloaders used to setup the interconnect paths before booting linux as
>>> they need to access the UFS storage for things like fetching boot firmware.
>>> But with the advent of multi boot options, bootloader nowadays like in
>>> SA8540p SoC do not setup the interconnect paths at all.
>>>
>>> So trying to configure UFS in the absence of the interconnect path
>>> configuration, results in boot crash.
>>>
>>> To fix this issue and also to dynamically scale the interconnects (UFS-DDR
>>> and CPU-UFS), interconnect API support is added to the Qcom UFS driver.
>>> With this support, the interconnect paths are scaled dynamically based on
>>> the gear configuration.
>>>
>>> During the early stage of ufs_qcom_init(), ufs_qcom_icc_init() will setup
>>> the paths to max bandwidth to allow configuring the UFS registers. Touching
>>> the registers without configuring the icc paths would result in a crash.
>>> However, we don't really need to set max vote for the icc paths as any
>>> minimal vote would suffice. But the max value would allow initialization to
>>> be done faster. After init, the bandwidth will get updated using
>>> ufs_qcom_icc_update_bw() based on the gear and lane configuration.
>>>
>>> The bandwidth values defined in ufs_qcom_bw_table struct are taken from
>>> Qcom downstream vendor devicetree source and are calculated as per the
>>> UFS3.1 Spec, Section 6.4.1, HS Gear Rates. So it is fixed across platforms.
>>>
>>> Cc: Brian Masney <bmasney@redhat.com>
>>> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
>>> ---
>>>    drivers/ufs/host/ufs-qcom.c | 131 +++++++++++++++++++++++++++++++++++-
>>>    drivers/ufs/host/ufs-qcom.h |   3 +
>>>    2 files changed, 133 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
>>> index 8d6fd4c3324f..8a3132d45a65 100644
>>> --- a/drivers/ufs/host/ufs-qcom.c
>>> +++ b/drivers/ufs/host/ufs-qcom.c
>>> @@ -7,6 +7,7 @@
>>>    #include <linux/time.h>
>>>    #include <linux/clk.h>
>>>    #include <linux/delay.h>
>>> +#include <linux/interconnect.h>
>>>    #include <linux/module.h>
>>>    #include <linux/of.h>
>>>    #include <linux/platform_device.h>
>>> @@ -46,6 +47,49 @@ enum {
>>>    	TSTBUS_MAX,
>>>    };
>>> +#define QCOM_UFS_MAX_GEAR 4
>>> +#define QCOM_UFS_MAX_LANE 2
>>> +
>>> +enum {
>>> +	MODE_MIN,
>>> +	MODE_PWM,
>>> +	MODE_HS_RA,
>>> +	MODE_HS_RB,
>>> +	MODE_MAX,
>>
>> MODE_MIN and MODE_MAX seem to be unused
>>
> 
> No, they are used in the driver.

Ack, missed that. Will comment inline.

> 
>>> +};
>>> +
>>> +struct __ufs_qcom_bw_table {
>>> +	u32 bw1;
>>> +	u32 bw2;
>>
>> Please consider:
>>
>> s/bw1/mem_bw/
>> s/bw2/cfg_bw/
>>
> 
> Ok.
> 
>>> +} ufs_qcom_bw_table[MODE_MAX + 1][QCOM_UFS_MAX_GEAR + 1][QCOM_UFS_MAX_LANE + 1] = {
>>
>> I'd say, these +1's are slightly confusing and unnecessary.
>>
> 
> These max values itself are used in the table. Without adding 1 to them, we will
> end up with out of bounds access.



> 
>>> +	[MODE_MIN][0][0] = { 0,		0 },	/* Bandwidth values are in KB/s */
>>
>> I'd say, this becomes impossible to check. Can you please structure it?
> 
> What do you mean by "impossible to check"?
> 
>> Either by inclusion:
>>
>> [MODE_PWM] = {
>>    [1] = {
>>       [1] = { .. },
>>       [2] = { .. },
>>    },
>>    // etc.
> 
> Now, this seems confusion to me. The existing table looks fine IMO.

Fine with me then, your call!

> 
>> };
>>
>> Also, do we have defines for gears? Can we use them instead of indices?
>>
> 
> There are defines for the gears but not for lanes. So I ended up using numbers
> for simplicity.

My suggestion would be to use them for gears at least. Then it becomes 
cleaner (and maybe will solve some of my other comments).

> 
> - Mani
> 
>>> +	[MODE_PWM][1][1] = { 922,	1000 },
>>> +	[MODE_PWM][2][1] = { 1844,	1000 },
>>> +	[MODE_PWM][3][1] = { 3688,	1000 },
>>> +	[MODE_PWM][4][1] = { 7376,	1000 },
>>> +	[MODE_PWM][1][2] = { 1844,	1000 },
>>> +	[MODE_PWM][2][2] = { 3688,	1000 },
>>> +	[MODE_PWM][3][2] = { 7376,	1000 },
>>> +	[MODE_PWM][4][2] = { 14752,	1000 },
>>> +	[MODE_HS_RA][1][1] = { 127796,	1000 },
>>> +	[MODE_HS_RA][2][1] = { 255591,	1000 },
>>> +	[MODE_HS_RA][3][1] = { 1492582,	102400 },
>>> +	[MODE_HS_RA][4][1] = { 2915200,	204800 },
>>> +	[MODE_HS_RA][1][2] = { 255591,	1000 },
>>> +	[MODE_HS_RA][2][2] = { 511181,	1000 },
>>> +	[MODE_HS_RA][3][2] = { 1492582,	204800 },
>>> +	[MODE_HS_RA][4][2] = { 2915200,	409600 },
>>> +	[MODE_HS_RB][1][1] = { 149422,	1000 },
>>> +	[MODE_HS_RB][2][1] = { 298189,	1000 },
>>> +	[MODE_HS_RB][3][1] = { 1492582,	102400 },
>>> +	[MODE_HS_RB][4][1] = { 2915200,	204800 },
>>> +	[MODE_HS_RB][1][2] = { 298189,	1000 },
>>> +	[MODE_HS_RB][2][2] = { 596378,	1000 },
>>> +	[MODE_HS_RB][3][2] = { 1492582,	204800 },
>>> +	[MODE_HS_RB][4][2] = { 2915200,	409600 },
>>> +	[MODE_MAX][0][0] = { 7643136, 307200 },
>>> +};
>>> +
>>>    static struct ufs_qcom_host *ufs_qcom_hosts[MAX_UFS_QCOM_HOSTS];
>>>    static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host);
>>> @@ -789,6 +833,51 @@ static void ufs_qcom_dev_ref_clk_ctrl(struct ufs_qcom_host *host, bool enable)
>>>    	}
>>>    }
>>> +static int ufs_qcom_icc_set_bw(struct ufs_qcom_host *host, u32 bw1, u32 bw2)
>>> +{
>>> +	struct device *dev = host->hba->dev;
>>> +	int ret;
>>> +
>>> +	ret = icc_set_bw(host->icc_ddr, 0, bw1);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "failed to set bandwidth request: %d\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = icc_set_bw(host->icc_cpu, 0, bw2);
>>> +	if (ret < 0) {
>>> +		dev_err(dev, "failed to set bandwidth request: %d\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static struct __ufs_qcom_bw_table ufs_qcom_get_bw_table(struct ufs_qcom_host *host)
>>> +{
>>> +	struct ufs_pa_layer_attr *p = &host->dev_req_params;
>>> +	int gear = max_t(u32, p->gear_rx, p->gear_tx);
>>> +	int lane = max_t(u32, p->lane_rx, p->lane_tx);
>>> +
>>> +	if (ufshcd_is_hs_mode(p)) {
>>> +		if (p->hs_rate == PA_HS_MODE_B)
>>> +			return ufs_qcom_bw_table[MODE_HS_RB][gear][lane];
>>> +		else
>>> +			return ufs_qcom_bw_table[MODE_HS_RA][gear][lane];
>>> +	} else {
>>> +		return ufs_qcom_bw_table[MODE_PWM][gear][lane];
>>> +	}
>>> +}
>>> +
>>> +static int ufs_qcom_icc_update_bw(struct ufs_qcom_host *host)
>>> +{
>>> +	struct __ufs_qcom_bw_table bw_table;
>>> +
>>> +	bw_table = ufs_qcom_get_bw_table(host);
>>> +
>>> +	return ufs_qcom_icc_set_bw(host, bw_table.bw1, bw_table.bw2);
>>> +}
>>> +
>>>    static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
>>>    				enum ufs_notify_change_status status,
>>>    				struct ufs_pa_layer_attr *dev_max_params,
>>> @@ -852,6 +941,8 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
>>>    		memcpy(&host->dev_req_params,
>>>    				dev_req_params, sizeof(*dev_req_params));
>>> +		ufs_qcom_icc_update_bw(host);
>>> +
>>>    		/* disable the device ref clock if entered PWM mode */
>>>    		if (ufshcd_is_hs_mode(&hba->pwr_info) &&
>>>    			!ufshcd_is_hs_mode(dev_req_params))
>>> @@ -981,7 +1072,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
>>>    	switch (status) {
>>>    	case PRE_CHANGE:
>>> -		if (!on) {
>>> +		if (on) {
>>> +			ufs_qcom_icc_update_bw(host);
>>> +		} else {
>>>    			if (!ufs_qcom_is_link_active(hba)) {
>>>    				/* disable device ref_clk */
>>>    				ufs_qcom_dev_ref_clk_ctrl(host, false);
>>> @@ -993,6 +1086,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
>>>    			/* enable the device ref clock for HS mode*/
>>>    			if (ufshcd_is_hs_mode(&hba->pwr_info))
>>>    				ufs_qcom_dev_ref_clk_ctrl(host, true);
>>> +		} else {
>>> +			ufs_qcom_icc_set_bw(host, ufs_qcom_bw_table[MODE_MIN][0][0].bw1,
>>> +					    ufs_qcom_bw_table[MODE_MIN][0][0].bw2);

With MODE_MIN values being initialised to 0, can we use the value 
directly instead? You are not defining the whole table for MODE_MIN anyway.

>>>    		}
>>>    		break;
>>>    	}
>>> @@ -1031,6 +1127,34 @@ static const struct reset_control_ops ufs_qcom_reset_ops = {
>>>    	.deassert = ufs_qcom_reset_deassert,
>>>    };
>>> +static int ufs_qcom_icc_init(struct ufs_qcom_host *host)
>>> +{
>>> +	struct device *dev = host->hba->dev;
>>> +	int ret;
>>> +
>>> +	host->icc_ddr = devm_of_icc_get(dev, "ufs-ddr");
>>> +	if (IS_ERR(host->icc_ddr))
>>> +		return dev_err_probe(dev, PTR_ERR(host->icc_ddr),
>>> +				    "failed to acquire interconnect path\n");
>>> +
>>> +	host->icc_cpu = devm_of_icc_get(dev, "cpu-ufs");
>>> +	if (IS_ERR(host->icc_cpu))
>>> +		return dev_err_probe(dev, PTR_ERR(host->icc_cpu),
>>> +				    "failed to acquire interconnect path\n");
>>> +
>>> +	/*
>>> +	 * Set Maximum bandwidth vote before initializing the UFS controller and
>>> +	 * device. Ideally, a minimal interconnect vote would suffice for the
>>> +	 * initialization, but a max vote would allow faster initialization.
>>> +	 */
>>> +	ret = ufs_qcom_icc_set_bw(host, ufs_qcom_bw_table[MODE_MAX][0][0].bw1,
>>> +				  ufs_qcom_bw_table[MODE_MAX][0][0].bw2);

If this is a static value, can we define it separately? You don't have a 
full gear/lanes enumeration anyway.

>>> +	if (ret < 0)
>>> +		return dev_err_probe(dev, ret, "failed to set bandwidth request\n");
>>> +
>>> +	return 0;
>>> +}
>>> +
>>>    /**
>>>     * ufs_qcom_init - bind phy with controller
>>>     * @hba: host controller instance
>>> @@ -1085,6 +1209,10 @@ static int ufs_qcom_init(struct ufs_hba *hba)
>>>    		}
>>>    	}
>>> +	err = ufs_qcom_icc_init(host);
>>> +	if (err)
>>> +		goto out_variant_clear;
>>> +
>>>    	host->device_reset = devm_gpiod_get_optional(dev, "reset",
>>>    						     GPIOD_OUT_HIGH);
>>>    	if (IS_ERR(host->device_reset)) {
>>> @@ -1282,6 +1410,7 @@ static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba,
>>>    				    dev_req_params->pwr_rx,
>>>    				    dev_req_params->hs_rate,
>>>    				    false);
>>> +		ufs_qcom_icc_update_bw(host);
>>>    		ufshcd_uic_hibern8_exit(hba);
>>>    	}
>>> diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h
>>> index 6289ad5a42d0..dc27395ecba1 100644
>>> --- a/drivers/ufs/host/ufs-qcom.h
>>> +++ b/drivers/ufs/host/ufs-qcom.h
>>> @@ -206,6 +206,9 @@ struct ufs_qcom_host {
>>>    	struct clk *tx_l1_sync_clk;
>>>    	bool is_lane_clks_enabled;
>>> +	struct icc_path *icc_ddr;
>>> +	struct icc_path *icc_cpu;
>>> +
>>>    #ifdef CONFIG_SCSI_UFS_CRYPTO
>>>    	struct qcom_ice *ice;
>>>    #endif
>>
>> -- 
>> With best wishes
>> Dmitry
>>
>
Viresh Kumar July 13, 2023, 4:01 a.m. UTC | #15
On 12-07-23, 16:02, Manivannan Sadhasivam wrote:
> +static int ufshcd_opp_set_rate(struct ufs_hba *hba, unsigned long freq)
> +{
> +	struct dev_pm_opp *opp;
> +
> +	opp = dev_pm_opp_find_freq_floor_indexed(hba->dev,
> +						 &freq, 0);
> +	if (IS_ERR(opp))
> +		return PTR_ERR(opp);
> +
> +	dev_pm_opp_put(opp);

You can't free the OPP while you are still using it :)

> +
> +	return dev_pm_opp_set_opp(hba->dev, opp);
> +}
Viresh Kumar July 13, 2023, 4:09 a.m. UTC | #16
On 12-07-23, 19:48, Dmitry Baryshkov wrote:
> On Wed, 12 Jul 2023 at 19:34, Manivannan Sadhasivam
> <manivannan.sadhasivam@linaro.org> wrote:
> > On Wed, Jul 12, 2023 at 04:15:12PM +0300, Dmitry Baryshkov wrote:
> > > On 12/07/2023 13:32, Manivannan Sadhasivam wrote:

> > > > +static int ufshcd_opp_config_clks(struct device *dev, struct opp_table *opp_table,
> > > > +                             struct dev_pm_opp *opp, void *data,
> > > > +                             bool scaling_down)
> > > > +{
> > > > +   struct ufs_hba *hba = dev_get_drvdata(dev);
> > > > +   struct list_head *head = &hba->clk_list_head;
> > > > +   struct ufs_clk_info *clki;
> > > > +   unsigned long freq;
> > > > +   u8 idx = 0;
> > > > +   int ret;
> > > > +
> > > > +   list_for_each_entry(clki, head, list) {
> > > > +           if (!IS_ERR_OR_NULL(clki->clk)) {
> > > > +                   freq = dev_pm_opp_get_freq_indexed(opp, idx++);
> > > > +
> > > > +                   /* Do not set rate for clocks having frequency as 0 */
> > > > +                   if (!freq)
> > > > +                           continue;
> > >
> > > Can we omit these clocks from the opp table? I don't think they serve any
> > > purpose.
> > >
> >
> > No, we cannot. OPP requires the clocks and opp-hz to be of same length.

I am okay with having a patch for the OPP core to modify this
behavior, as I told privately earlier.

> > And we
> > cannot omit those clocks as well since linux needs to gate control them.
> 
> Hmm, I thought we push the list of "interesting" clocks through
> config->clock_names.

Yes, another way to solve this would be keep the interesting clocks in
the beginning in "clock-names" field and let the platform pass only
those to the OPP core.

> >
> > > Maybe it would even make sense to move this function to drivers/opp then, as
> > > it will be generic enough.
> > >
> >
> > There is already a generic function available in OPP core. But we cannot use it
> > as we need to skip setting 0 freq and that's not applicable in OPP core as
> > discussed with Viresh offline.
> 
> Ack.

I am okay with either of the solutions, it is for you guys to decide
what works better for your platform.
Manivannan Sadhasivam July 13, 2023, 5:05 a.m. UTC | #17
On Thu, Jul 13, 2023 at 09:39:18AM +0530, Viresh Kumar wrote:
> On 12-07-23, 19:48, Dmitry Baryshkov wrote:
> > On Wed, 12 Jul 2023 at 19:34, Manivannan Sadhasivam
> > <manivannan.sadhasivam@linaro.org> wrote:
> > > On Wed, Jul 12, 2023 at 04:15:12PM +0300, Dmitry Baryshkov wrote:
> > > > On 12/07/2023 13:32, Manivannan Sadhasivam wrote:
> 
> > > > > +static int ufshcd_opp_config_clks(struct device *dev, struct opp_table *opp_table,
> > > > > +                             struct dev_pm_opp *opp, void *data,
> > > > > +                             bool scaling_down)
> > > > > +{
> > > > > +   struct ufs_hba *hba = dev_get_drvdata(dev);
> > > > > +   struct list_head *head = &hba->clk_list_head;
> > > > > +   struct ufs_clk_info *clki;
> > > > > +   unsigned long freq;
> > > > > +   u8 idx = 0;
> > > > > +   int ret;
> > > > > +
> > > > > +   list_for_each_entry(clki, head, list) {
> > > > > +           if (!IS_ERR_OR_NULL(clki->clk)) {
> > > > > +                   freq = dev_pm_opp_get_freq_indexed(opp, idx++);
> > > > > +
> > > > > +                   /* Do not set rate for clocks having frequency as 0 */
> > > > > +                   if (!freq)
> > > > > +                           continue;
> > > >
> > > > Can we omit these clocks from the opp table? I don't think they serve any
> > > > purpose.
> > > >
> > >
> > > No, we cannot. OPP requires the clocks and opp-hz to be of same length.
> 
> I am okay with having a patch for the OPP core to modify this
> behavior, as I told privately earlier.
> 
> > > And we
> > > cannot omit those clocks as well since linux needs to gate control them.
> > 
> > Hmm, I thought we push the list of "interesting" clocks through
> > config->clock_names.
> 
> Yes, another way to solve this would be keep the interesting clocks in
> the beginning in "clock-names" field and let the platform pass only
> those to the OPP core.
> 
> > >
> > > > Maybe it would even make sense to move this function to drivers/opp then, as
> > > > it will be generic enough.
> > > >
> > >
> > > There is already a generic function available in OPP core. But we cannot use it
> > > as we need to skip setting 0 freq and that's not applicable in OPP core as
> > > discussed with Viresh offline.
> > 
> > Ack.
> 
> I am okay with either of the solutions, it is for you guys to decide
> what works better for your platform.
> 

We can settle with this custom callback for now. If there are drivers in the
future trying to do the same (skipping 0 freq) then we can generalize.

- Mani

> -- 
> viresh
Manivannan Sadhasivam July 13, 2023, 5:07 a.m. UTC | #18
On Thu, Jul 13, 2023 at 09:31:42AM +0530, Viresh Kumar wrote:
> On 12-07-23, 16:02, Manivannan Sadhasivam wrote:
> > +static int ufshcd_opp_set_rate(struct ufs_hba *hba, unsigned long freq)
> > +{
> > +	struct dev_pm_opp *opp;
> > +
> > +	opp = dev_pm_opp_find_freq_floor_indexed(hba->dev,
> > +						 &freq, 0);
> > +	if (IS_ERR(opp))
> > +		return PTR_ERR(opp);
> > +
> > +	dev_pm_opp_put(opp);
> 
> You can't free the OPP while you are still using it :)
> 

Ah... will fix it!

- Mani

> > +
> > +	return dev_pm_opp_set_opp(hba->dev, opp);
> > +}
> 
> -- 
> viresh
Viresh Kumar July 13, 2023, 5:12 a.m. UTC | #19
On 13-07-23, 10:35, Manivannan Sadhasivam wrote:
> We can settle with this custom callback for now. If there are drivers in the
> future trying to do the same (skipping 0 freq) then we can generalize.

Just for completeness, there isn't much to generalize here apart from
changing the DT order of clocks. Isn't it ?

The change require for the OPP core makes sense, I will probably just
push it anyway.
Manivannan Sadhasivam July 13, 2023, 5:20 a.m. UTC | #20
On Wed, Jul 12, 2023 at 08:23:02PM +0300, Dmitry Baryshkov wrote:
> On 12/07/2023 19:41, Manivannan Sadhasivam wrote:
> > On Wed, Jul 12, 2023 at 04:22:51PM +0300, Dmitry Baryshkov wrote:
> > > On 12/07/2023 13:32, Manivannan Sadhasivam wrote:
> > > > Qcom SoCs require scaling the interconnect paths for proper working of the
> > > > peripherals connected through interconnects. Even for accessing the UFS
> > > > controller, someone should setup the interconnect paths. So far, the
> > > > bootloaders used to setup the interconnect paths before booting linux as
> > > > they need to access the UFS storage for things like fetching boot firmware.
> > > > But with the advent of multi boot options, bootloader nowadays like in
> > > > SA8540p SoC do not setup the interconnect paths at all.
> > > > 
> > > > So trying to configure UFS in the absence of the interconnect path
> > > > configuration, results in boot crash.
> > > > 
> > > > To fix this issue and also to dynamically scale the interconnects (UFS-DDR
> > > > and CPU-UFS), interconnect API support is added to the Qcom UFS driver.
> > > > With this support, the interconnect paths are scaled dynamically based on
> > > > the gear configuration.
> > > > 
> > > > During the early stage of ufs_qcom_init(), ufs_qcom_icc_init() will setup
> > > > the paths to max bandwidth to allow configuring the UFS registers. Touching
> > > > the registers without configuring the icc paths would result in a crash.
> > > > However, we don't really need to set max vote for the icc paths as any
> > > > minimal vote would suffice. But the max value would allow initialization to
> > > > be done faster. After init, the bandwidth will get updated using
> > > > ufs_qcom_icc_update_bw() based on the gear and lane configuration.
> > > > 
> > > > The bandwidth values defined in ufs_qcom_bw_table struct are taken from
> > > > Qcom downstream vendor devicetree source and are calculated as per the
> > > > UFS3.1 Spec, Section 6.4.1, HS Gear Rates. So it is fixed across platforms.
> > > > 
> > > > Cc: Brian Masney <bmasney@redhat.com>
> > > > Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> > > > ---
> > > >    drivers/ufs/host/ufs-qcom.c | 131 +++++++++++++++++++++++++++++++++++-
> > > >    drivers/ufs/host/ufs-qcom.h |   3 +
> > > >    2 files changed, 133 insertions(+), 1 deletion(-)
> > > > 
> > > > diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
> > > > index 8d6fd4c3324f..8a3132d45a65 100644
> > > > --- a/drivers/ufs/host/ufs-qcom.c
> > > > +++ b/drivers/ufs/host/ufs-qcom.c
> > > > @@ -7,6 +7,7 @@
> > > >    #include <linux/time.h>
> > > >    #include <linux/clk.h>
> > > >    #include <linux/delay.h>
> > > > +#include <linux/interconnect.h>
> > > >    #include <linux/module.h>
> > > >    #include <linux/of.h>
> > > >    #include <linux/platform_device.h>
> > > > @@ -46,6 +47,49 @@ enum {
> > > >    	TSTBUS_MAX,
> > > >    };
> > 

[...]

> > > };
> > > 
> > > Also, do we have defines for gears? Can we use them instead of indices?
> > > 
> > 
> > There are defines for the gears but not for lanes. So I ended up using numbers
> > for simplicity.
> 
> My suggestion would be to use them for gears at least. Then it becomes
> cleaner (and maybe will solve some of my other comments).
> 

I think it'd better to add enums for lanes as well (in unipro.h) and use both.

> > 
> > - Mani
> > 
> > > > +	[MODE_PWM][1][1] = { 922,	1000 },
> > > > +	[MODE_PWM][2][1] = { 1844,	1000 },
> > > > +	[MODE_PWM][3][1] = { 3688,	1000 },
> > > > +	[MODE_PWM][4][1] = { 7376,	1000 },
> > > > +	[MODE_PWM][1][2] = { 1844,	1000 },
> > > > +	[MODE_PWM][2][2] = { 3688,	1000 },
> > > > +	[MODE_PWM][3][2] = { 7376,	1000 },
> > > > +	[MODE_PWM][4][2] = { 14752,	1000 },
> > > > +	[MODE_HS_RA][1][1] = { 127796,	1000 },
> > > > +	[MODE_HS_RA][2][1] = { 255591,	1000 },
> > > > +	[MODE_HS_RA][3][1] = { 1492582,	102400 },
> > > > +	[MODE_HS_RA][4][1] = { 2915200,	204800 },
> > > > +	[MODE_HS_RA][1][2] = { 255591,	1000 },
> > > > +	[MODE_HS_RA][2][2] = { 511181,	1000 },
> > > > +	[MODE_HS_RA][3][2] = { 1492582,	204800 },
> > > > +	[MODE_HS_RA][4][2] = { 2915200,	409600 },
> > > > +	[MODE_HS_RB][1][1] = { 149422,	1000 },
> > > > +	[MODE_HS_RB][2][1] = { 298189,	1000 },
> > > > +	[MODE_HS_RB][3][1] = { 1492582,	102400 },
> > > > +	[MODE_HS_RB][4][1] = { 2915200,	204800 },
> > > > +	[MODE_HS_RB][1][2] = { 298189,	1000 },
> > > > +	[MODE_HS_RB][2][2] = { 596378,	1000 },
> > > > +	[MODE_HS_RB][3][2] = { 1492582,	204800 },
> > > > +	[MODE_HS_RB][4][2] = { 2915200,	409600 },
> > > > +	[MODE_MAX][0][0] = { 7643136, 307200 },
> > > > +};
> > > > +

[...]

> > > > +static int ufs_qcom_icc_update_bw(struct ufs_qcom_host *host)
> > > > +{
> > > > +	struct __ufs_qcom_bw_table bw_table;
> > > > +
> > > > +	bw_table = ufs_qcom_get_bw_table(host);
> > > > +
> > > > +	return ufs_qcom_icc_set_bw(host, bw_table.bw1, bw_table.bw2);
> > > > +}
> > > > +
> > > >    static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
> > > >    				enum ufs_notify_change_status status,
> > > >    				struct ufs_pa_layer_attr *dev_max_params,
> > > > @@ -852,6 +941,8 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba,
> > > >    		memcpy(&host->dev_req_params,
> > > >    				dev_req_params, sizeof(*dev_req_params));
> > > > +		ufs_qcom_icc_update_bw(host);
> > > > +
> > > >    		/* disable the device ref clock if entered PWM mode */
> > > >    		if (ufshcd_is_hs_mode(&hba->pwr_info) &&
> > > >    			!ufshcd_is_hs_mode(dev_req_params))
> > > > @@ -981,7 +1072,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
> > > >    	switch (status) {
> > > >    	case PRE_CHANGE:
> > > > -		if (!on) {
> > > > +		if (on) {
> > > > +			ufs_qcom_icc_update_bw(host);
> > > > +		} else {
> > > >    			if (!ufs_qcom_is_link_active(hba)) {
> > > >    				/* disable device ref_clk */
> > > >    				ufs_qcom_dev_ref_clk_ctrl(host, false);
> > > > @@ -993,6 +1086,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
> > > >    			/* enable the device ref clock for HS mode*/
> > > >    			if (ufshcd_is_hs_mode(&hba->pwr_info))
> > > >    				ufs_qcom_dev_ref_clk_ctrl(host, true);
> > > > +		} else {
> > > > +			ufs_qcom_icc_set_bw(host, ufs_qcom_bw_table[MODE_MIN][0][0].bw1,
> > > > +					    ufs_qcom_bw_table[MODE_MIN][0][0].bw2);
> 
> With MODE_MIN values being initialised to 0, can we use the value directly
> instead? You are not defining the whole table for MODE_MIN anyway.
> 

I initially thought about it, but having all the values in the table gives
better visibility IMO. Otherwise, one has to look into the actual call to
determine what is being set for min and max.

- Mani
Manivannan Sadhasivam July 13, 2023, 5:28 a.m. UTC | #21
On Thu, Jul 13, 2023 at 10:42:35AM +0530, Viresh Kumar wrote:
> On 13-07-23, 10:35, Manivannan Sadhasivam wrote:
> > We can settle with this custom callback for now. If there are drivers in the
> > future trying to do the same (skipping 0 freq) then we can generalize.
> 
> Just for completeness, there isn't much to generalize here apart from
> changing the DT order of clocks. Isn't it ?
> 

Even with changing the order, driver has to know the "interesting" clocks
beforehand. But that varies between platforms (this is a generic driver for
ufshc platforms).

And I do not know if clocks have any dependency between them, atleast not in
Qcom platforms. But not sure about others.

- Mani

> The change require for the OPP core makes sense, I will probably just
> push it anyway.
> 
> -- 
> viresh
Viresh Kumar July 13, 2023, 5:43 a.m. UTC | #22
Okay, sorry about missing one point first. I thought we are adding the
clk config callback (which neglects 0 frequencies) to a Qcom only
driver and so was okay-ish with that. But now that I realize that this
is a generic driver instead (my mistake here), I wonder if it is the
right thing to do anymore.

On 13-07-23, 10:58, Manivannan Sadhasivam wrote:
> On Thu, Jul 13, 2023 at 10:42:35AM +0530, Viresh Kumar wrote:
> > On 13-07-23, 10:35, Manivannan Sadhasivam wrote:
> > > We can settle with this custom callback for now. If there are drivers in the
> > > future trying to do the same (skipping 0 freq) then we can generalize.
> > 
> > Just for completeness, there isn't much to generalize here apart from
> > changing the DT order of clocks. Isn't it ?
> > 
> 
> Even with changing the order, driver has to know the "interesting" clocks
> beforehand. But that varies between platforms (this is a generic driver for
> ufshc platforms).
> 
> And I do not know if clocks have any dependency between them, atleast not in
> Qcom platforms. But not sure about others.

Maybe this requires some sort of callback, per-platform, which gets
you these details or the struct dev_pm_opp_config itself (so platforms
can choose the callback too, in case order is important).

> > The change require for the OPP core makes sense, I will probably just
> > push it anyway.

I tried to look at this code and I think it is doing the right thing
currently, i.e. it matches clk-count with the number of frequencies in
opp-hz, which should turn out to be the same in your case. So nothing
to change here I guess.
Manivannan Sadhasivam July 13, 2023, 5:53 a.m. UTC | #23
On Thu, Jul 13, 2023 at 11:13:02AM +0530, Viresh Kumar wrote:
> Okay, sorry about missing one point first. I thought we are adding the
> clk config callback (which neglects 0 frequencies) to a Qcom only
> driver and so was okay-ish with that. But now that I realize that this
> is a generic driver instead (my mistake here), I wonder if it is the
> right thing to do anymore.
> 

That's the pre-opp behavior as well. Reason is, most of the platforms have only
gate clocks supplied to the ufs controller and cannot change the frequency. Only
Qcom requires changing the frequency of _some_ clocks, so that's why we have to
use this hack of skipping 0 freq clocks.

> On 13-07-23, 10:58, Manivannan Sadhasivam wrote:
> > On Thu, Jul 13, 2023 at 10:42:35AM +0530, Viresh Kumar wrote:
> > > On 13-07-23, 10:35, Manivannan Sadhasivam wrote:
> > > > We can settle with this custom callback for now. If there are drivers in the
> > > > future trying to do the same (skipping 0 freq) then we can generalize.
> > > 
> > > Just for completeness, there isn't much to generalize here apart from
> > > changing the DT order of clocks. Isn't it ?
> > > 
> > 
> > Even with changing the order, driver has to know the "interesting" clocks
> > beforehand. But that varies between platforms (this is a generic driver for
> > ufshc platforms).
> > 
> > And I do not know if clocks have any dependency between them, atleast not in
> > Qcom platforms. But not sure about others.
> 
> Maybe this requires some sort of callback, per-platform, which gets
> you these details or the struct dev_pm_opp_config itself (so platforms
> can choose the callback too, in case order is important).
> 

Yeah but that seems overkill since the current config_clks helper satisfies the
requirement.

- Mani

> > > The change require for the OPP core makes sense, I will probably just
> > > push it anyway.
> 
> I tried to look at this code and I think it is doing the right thing
> currently, i.e. it matches clk-count with the number of frequencies in
> opp-hz, which should turn out to be the same in your case. So nothing
> to change here I guess.
> 
> -- 
> viresh
Eric Biggers July 13, 2023, 7:30 a.m. UTC | #24
On Wed, Jul 12, 2023 at 04:01:59PM +0530, Manivannan Sadhasivam wrote:
> Minimum frequency of the "ice_core_clk" should be 75MHz as specified in the
> downstream vendor devicetree. So fix it!
> 
> https://git.codelinaro.org/clo/la/kernel/msm-4.9/-/blob/LA.UM.7.3.r1-09300-sdm845.0/arch/arm64/boot/dts/qcom/sdm845.dtsi
> 
> Fixes: cc16687fbd74 ("arm64: dts: qcom: sdm845: add UFS controller")
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> ---
>  arch/arm64/boot/dts/qcom/sdm845.dtsi | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> index 9ed74bf72d05..89520a9fe1e3 100644
> --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
> +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> @@ -2614,7 +2614,7 @@ ufs_mem_hc: ufshc@1d84000 {
>  				<0 0>,
>  				<0 0>,
>  				<0 0>,
> -				<0 300000000>;
> +				<75000000 300000000>;

Thanks for fixing this!  What was the visible effect of this bug, if any?
Do you know why the minimum has that particular value?

The Fixes tag is wrong; it should be:

Fixes: 433f9a57298f ("arm64: dts: sdm845: add Inline Crypto Engine registers and clock")

- Eric
Manivannan Sadhasivam July 13, 2023, 7:37 a.m. UTC | #25
On Thu, Jul 13, 2023 at 12:30:03AM -0700, Eric Biggers wrote:
> On Wed, Jul 12, 2023 at 04:01:59PM +0530, Manivannan Sadhasivam wrote:
> > Minimum frequency of the "ice_core_clk" should be 75MHz as specified in the
> > downstream vendor devicetree. So fix it!
> > 
> > https://git.codelinaro.org/clo/la/kernel/msm-4.9/-/blob/LA.UM.7.3.r1-09300-sdm845.0/arch/arm64/boot/dts/qcom/sdm845.dtsi
> > 
> > Fixes: cc16687fbd74 ("arm64: dts: qcom: sdm845: add UFS controller")
> > Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> > ---
> >  arch/arm64/boot/dts/qcom/sdm845.dtsi | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> > index 9ed74bf72d05..89520a9fe1e3 100644
> > --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
> > +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> > @@ -2614,7 +2614,7 @@ ufs_mem_hc: ufshc@1d84000 {
> >  				<0 0>,
> >  				<0 0>,
> >  				<0 0>,
> > -				<0 300000000>;
> > +				<75000000 300000000>;
> 
> Thanks for fixing this!  What was the visible effect of this bug, if any?

No, this was just based on inspection.

> Do you know why the minimum has that particular value?
> 

Min and max frequencies come from the Qcom's internal documentation where they
have calculated the freq range based on RPMh performance states.

> The Fixes tag is wrong; it should be:
> 
> Fixes: 433f9a57298f ("arm64: dts: sdm845: add Inline Crypto Engine registers and clock")
> 

Yikes! Thanks for noticing, will fix it.

- Mani

> - Eric
Konrad Dybcio July 15, 2023, 1:12 p.m. UTC | #26
On 12.07.2023 12:32, Manivannan Sadhasivam wrote:
> UFS host controller requires interconnect path configuration for proper
> working. So let's specify them for SDM845 SoC.
> 
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> ---
Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org>

Konrad
>  arch/arm64/boot/dts/qcom/sdm845.dtsi | 5 +++++
>  1 file changed, 5 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> index e04a3cbb1017..2ea6eb44953e 100644
> --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
> +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> @@ -2607,6 +2607,11 @@ ufs_mem_hc: ufshc@1d84000 {
>  				<&gcc GCC_UFS_PHY_ICE_CORE_CLK>;
>  
>  			operating-points-v2 = <&ufs_opp_table>;
> +
> +			interconnects = <&aggre1_noc MASTER_UFS_MEM 0 &mem_noc SLAVE_EBI1 0>,
> +					<&gladiator_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_UFS_MEM_CFG 0>;
> +			interconnect-names = "ufs-ddr", "cpu-ufs";
> +
>  			status = "disabled";
>  
>  			ufs_opp_table: opp-table {
Konrad Dybcio July 15, 2023, 1:12 p.m. UTC | #27
On 12.07.2023 12:32, Manivannan Sadhasivam wrote:
> UFS host controller requires interconnect path configuration for proper
> working. So let's specify them for SDM845 SoC.
> 
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> ---
Duplicate of patch 11

Konrad
>  arch/arm64/boot/dts/qcom/sdm845.dtsi | 5 +++++
>  1 file changed, 5 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> index e04a3cbb1017..2ea6eb44953e 100644
> --- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
> +++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
> @@ -2607,6 +2607,11 @@ ufs_mem_hc: ufshc@1d84000 {
>  				<&gcc GCC_UFS_PHY_ICE_CORE_CLK>;
>  
>  			operating-points-v2 = <&ufs_opp_table>;
> +
> +			interconnects = <&aggre1_noc MASTER_UFS_MEM 0 &mem_noc SLAVE_EBI1 0>,
> +					<&gladiator_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_UFS_MEM_CFG 0>;
> +			interconnect-names = "ufs-ddr", "cpu-ufs";
> +
>  			status = "disabled";
>  
>  			ufs_opp_table: opp-table {
Konrad Dybcio July 15, 2023, 1:13 p.m. UTC | #28
On 12.07.2023 12:32, Manivannan Sadhasivam wrote:
> UFS host controller requires interconnect path configuration for proper
> working. So let's specify them for SM8250 SoC.
> 
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> ---
Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org>

Konrad
>  arch/arm64/boot/dts/qcom/sm8250.dtsi | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/qcom/sm8250.dtsi b/arch/arm64/boot/dts/qcom/sm8250.dtsi
> index 72fd66db9c51..7a495ff7512f 100644
> --- a/arch/arm64/boot/dts/qcom/sm8250.dtsi
> +++ b/arch/arm64/boot/dts/qcom/sm8250.dtsi
> @@ -2197,6 +2197,10 @@ ufs_mem_hc: ufshc@1d84000 {
>  
>  			operating-points-v2 = <&ufs_opp_table>;
>  
> +			interconnects = <&aggre1_noc MASTER_UFS_MEM 0 &mc_virt SLAVE_EBI_CH0 0>,
> +					<&gem_noc MASTER_AMPSS_M0 0 &config_noc SLAVE_UFS_MEM_CFG 0>;
> +			interconnect-names = "ufs-ddr", "cpu-ufs";
> +
>  			status = "disabled";
>  
>  			ufs_opp_table: opp-table {
Konrad Dybcio July 15, 2023, 1:14 p.m. UTC | #29
On 12.07.2023 12:32, Manivannan Sadhasivam wrote:
> UFS host controller requires interconnect path configuration for proper
> working. So let's specify them for SM8250 SoC.
> 
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> ---
Duplicate

Konrad
>  arch/arm64/boot/dts/qcom/sm8250.dtsi | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/qcom/sm8250.dtsi b/arch/arm64/boot/dts/qcom/sm8250.dtsi
> index 72fd66db9c51..7a495ff7512f 100644
> --- a/arch/arm64/boot/dts/qcom/sm8250.dtsi
> +++ b/arch/arm64/boot/dts/qcom/sm8250.dtsi
> @@ -2197,6 +2197,10 @@ ufs_mem_hc: ufshc@1d84000 {
>  
>  			operating-points-v2 = <&ufs_opp_table>;
>  
> +			interconnects = <&aggre1_noc MASTER_UFS_MEM 0 &mc_virt SLAVE_EBI_CH0 0>,
> +					<&gem_noc MASTER_AMPSS_M0 0 &config_noc SLAVE_UFS_MEM_CFG 0>;
> +			interconnect-names = "ufs-ddr", "cpu-ufs";
> +
>  			status = "disabled";
>  
>  			ufs_opp_table: opp-table {