Devicetree
 help / color / mirror / Atom feed
* RE: [PATCH v2 0/2] char: tpm: add new driver for tpm i2c ptp
From: Benoit HOUYERE @ 2019-07-30  8:39 UTC (permalink / raw)
  To: Alexander Steffen, Eyal.Cohen@nuvoton.com,
	jarkko.sakkinen@linux.intel.com, tmaimon77@gmail.com
  Cc: oshrialkoby85@gmail.com, robh+dt@kernel.org, mark.rutland@arm.com,
	peterhuewe@gmx.de, jgg@ziepe.ca, arnd@arndb.de,
	gregkh@linuxfoundation.org, oshri.alkoby@nuvoton.com,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-integrity@vger.kernel.org, gcwilson@us.ibm.com,
	kgoldman@us.ibm.com, nayna@linux.vnet.ibm.com,
	Dan.Morav@nuvoton.com,
	"oren.tanami@nuvoton.com" <oren.tanam>
In-Reply-To: <548d3727-4a8f-38d4-2193-8a09cbae1e64@infineon.com>

Hi Alexander, Jarkko and Eyal,

A first I2C TCG patch (tpm_tis_i2c.c) has been proposed in the same time as tpm_tis_spi.c by Christophe 3 years ago.

https://patchwork.kernel.org/patch/8628681/

At the time, we have had two concerns :
	1) I2C TPM component number, in the market, compliant with new I2C TCG specification to validate new I2C driver.
	2) Lots changing  was already provided by tpm_tis_spi.c on 4.8.

That's why Tpm_tis_i2c.c has been postponed.

Tpm_tis_spi Linux driver is now robust, if we have several different I2C TPM solutions today to validate a tpm_tis_i2c driver, I 'm ready to contribute to it for validation (STmicro TPM) or propose a solution compatible on 5.1 linux driver if needed under timeframe proposed (second half of august).

Best Regards,

Benoit



-----Original Message-----
From: linux-integrity-owner@vger.kernel.org <linux-integrity-owner@vger.kernel.org> On Behalf Of Alexander Steffen
Sent: jeudi 18 juillet 2019 19:10
To: Eyal.Cohen@nuvoton.com; jarkko.sakkinen@linux.intel.com; tmaimon77@gmail.com
Cc: oshrialkoby85@gmail.com; robh+dt@kernel.org; mark.rutland@arm.com; peterhuewe@gmx.de; jgg@ziepe.ca; arnd@arndb.de; gregkh@linuxfoundation.org; oshri.alkoby@nuvoton.com; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; linux-integrity@vger.kernel.org; gcwilson@us.ibm.com; kgoldman@us.ibm.com; nayna@linux.vnet.ibm.com; Dan.Morav@nuvoton.com; oren.tanami@nuvoton.com
Subject: Re: [PATCH v2 0/2] char: tpm: add new driver for tpm i2c ptp

On 18.07.2019 14:51, Eyal.Cohen@nuvoton.com wrote:
> Hi Jarkko and Alexander,
> 
> We have made an additional code review on the TPM TIS core driver, it looks quite good and we can connect our new I2C driver to this layer.

Great :) In the meantime, I've done some experiments creating an I2C driver based on tpm_tis_core, see https://patchwork.kernel.org/patch/11049363/ Please have a look at that and provide your feedback (and/or use it as a basis for further implementations).

> However, there are several differences between the SPI interface and the I2C interface that will require changes to the TIS core.
> At a minimum we thought of:
> 1. Handling TPM Localities in I2C is different

It turned out not to be that different in the end, see the code mentioned above and my comment here: 
https://patchwork.kernel.org/cover/11049365/

> 2. Handling I2C CRC - relevant only to I2C bus hence not supported 
> today by TIS core

That is completely optional, so there is no need to implement it in the beginning. Also, do you expect a huge benefit from that functionality? 
Are bit flips that much more likely on I2C compared to SPI, which has no CRC at all, but still works fine?

> 3. Handling Chip specific issues, since I2C implementation might be 
> slightly different across the various TPM vendors

Right, that seems similar to the cr50 issues (https://lkml.org/lkml/2019/7/17/677), so there should probably be a similar way to do it.

> 4. Modify tpm_tis_send_data and tpm_tis_recv_data to work according 
> the TCG Device Driver Guide (optimization on TPM_STS access and 
> send/recv retry)

Optimizations are always welcome, but I'd expect basic communication to work already with the current code (though maybe not as efficiently as possible).

> Besides this, during development we might encounter additional differences between SPI and I2C.
> 
> We currently target to allocate an eng. to work on this on the second half of August with a goal to have the driver ready for the next kernel merge window.
> 
> Regards,
> Eyal.

^ permalink raw reply

* Re: Re: [PATCH 4/6] pwm: sun4i: Add support for H6 PWM
From: Chen-Yu Tsai @ 2019-07-30  8:32 UTC (permalink / raw)
  To: Uwe Kleine-König, Rob Herring, Frank Rowand
  Cc: linux-sunxi, Jernej Škrabec, Mark Rutland,
	linux-pwm-u79uwXL29TY76Z2rM5mHXA, devicetree, linux-kernel,
	Maxime Ripard, Thierry Reding, Sascha Hauer, linux-arm-kernel
In-Reply-To: <20190730080900.hhxrqun7vk4nsj2h-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>

On Tue, Jul 30, 2019 at 4:09 PM Uwe Kleine-König
<u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> wrote:
>
> Hello Rob and Frank,
>
> Maxime and Jernej on one side and me on the other cannot agree about a
> detail in the change to the bindings here. I'm trying to objectively
> summarize the situation for you to help deciding what is the right thing
> to do here.
>
> TLDR: The sun4i pwm driver is extended to support a new variant of that
> device on the H6 SoC. Compared to the earlier supported variants
> allwinner,sun50i-h6-pwm on H6 needs to handle a reset controller and an
> additional clock.
>
> The two positions are:
>
>  - We need a new compatible because only then the driver and/or the dt
>    schema checker can check that each "allwinner,sun50i-h6-pwm" device
>    has a reset property and a "bus" clock; and the earlier variants
>    don't.
>
>  - The driver can be simpler and the device specific knowledge is only
>    in a single place (the dt) if the device tree is considered valid and
>    a reset property and "bus" clock is used iff it's provided in the
>    device tree without additional comparison for the compatible.
>
> Now our arguments seem to go in circles and Jernej was interested in
> your position. That's something I agree with ;-) Can you please share
> your view?
>
> Find below some context about the arguments.

A bit more context on the failure modes:

If the reset control is missing, anything done to hardware will be
silently ignored, since any writes to the registers are ignored.

On the other hand, if the bus clock is missing and otherwise not enabled,
accessing the device's registers could actually stall the whole system.

ChenYu

> Best regards
> Uwe
>
> On Tue, Jul 30, 2019 at 12:04:47AM +0200, Jernej Škrabec wrote:
> > Dne ponedeljek, 29. julij 2019 ob 20:51:08 CEST je Uwe Kleine-König
> > napisal(a):
> > > On Mon, Jul 29, 2019 at 08:46:25PM +0200, Jernej Škrabec wrote:
> > > > Dne ponedeljek, 29. julij 2019 ob 20:40:41 CEST je Uwe Kleine-König
> > > > napisal(a):
> > > > > On Mon, Jul 29, 2019 at 06:40:15PM +0200, Jernej Škrabec wrote:
> > > > > > Dne ponedeljek, 29. julij 2019 ob 18:24:28 CEST je Uwe Kleine-König
> > > > > > napisal(a):
> > > > > > > On Tue, Jul 30, 2019 at 12:09:40AM +0800, Chen-Yu Tsai wrote:
> > > > > > > > On Tue, Jul 30, 2019 at 12:07 AM Uwe Kleine-König
> > > > > > > > <u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> wrote:
> > > > > > > > > On Mon, Jul 29, 2019 at 05:55:52PM +0200, Jernej Škrabec wrote:
> > > > > > > > > > Dne ponedeljek, 29. julij 2019 ob 08:40:30 CEST je Uwe Kleine-König
> > > > > > > > > > napisal(a):
> > > > > > > > > > > On Fri, Jul 26, 2019 at 08:40:43PM +0200, Jernej Skrabec wrote:
> > > > > > > > > > > > --- a/drivers/pwm/pwm-sun4i.c
> > > > > > > > > > > > +++ b/drivers/pwm/pwm-sun4i.c
> > > > > > > > > > > > @@ -331,6 +331,13 @@ static const struct sun4i_pwm_data
> > > > > > > > > > > > sun4i_pwm_single_bypass = {>
> > > > > > > > > > > >
> > > > > > > > > > > >   .npwm = 1,
> > > > > > > > > > > >
> > > > > > > > > > > >  };
> > > > > > > > > > > >
> > > > > > > > > > > > +static const struct sun4i_pwm_data
> > > > > > > > > > > > sun50i_pwm_dual_bypass_clk_rst
> > > > > > > > > > > > = {
> > > > > > > > > > > > + .has_bus_clock = true,
> > > > > > > > > > > > + .has_prescaler_bypass = true,
> > > > > > > > > > > > + .has_reset = true,
> > > > > > > > > > > > + .npwm = 2,
> > > > > > > > > > > > +};
> > > > > > > > > > > > +
> > > > > > > > > > > >
> > > > > > > > > > > >  static const struct of_device_id sun4i_pwm_dt_ids[] = {
> > > > > > > > > > > >
> > > > > > > > > > > >   {
> > > > > > > > > > > >
> > > > > > > > > > > >           .compatible = "allwinner,sun4i-a10-pwm",
> > > > > > > > > > > >
> > > > > > > > > > > > @@ -347,6 +354,9 @@ static const struct of_device_id
> > > > > > > > > > > > sun4i_pwm_dt_ids[] =
> > > > > > > > > > > > {
> > > > > > > > > > > >
> > > > > > > > > > > >   }, {
> > > > > > > > > > > >
> > > > > > > > > > > >           .compatible = "allwinner,sun8i-h3-pwm",
> > > > > > > > > > > >           .data = &sun4i_pwm_single_bypass,
> > > > > > > > > > > >
> > > > > > > > > > > > + }, {
> > > > > > > > > > > > +         .compatible = "allwinner,sun50i-h6-pwm",
> > > > > > > > > > > > +         .data = &sun50i_pwm_dual_bypass_clk_rst,
> > > > > > > > > > >
> > > > > > > > > > > If you follow my suggestion for the two previous patches,
>
> (i.e. use devm_clk_get_optional instead of using devm_clk_get iff the
> compatible is allwinner,sun50i-h6-pwm; analogous for the reset
> controller.)
>
> > > > > > > > > > > you can just use:
> > > > > > > > > > >
> > > > > > > > > > >     compatible = "allwinner,sun50i-h6-pwm", "allwinner,sun5i-a10s-pwm";
> > > > > > > > > > >
> > > > > > > > > > > and drop this patch.
> > > > > > > > > >
> > > > > > > > > > Maxime found out that it's not compatible with A10s due to difference
> > > > > > > > > > in bypass bit, but yes, I know what you mean.
> > > > > > > > > >
> > > > > > > > > > Since H6 requires reset line and bus clock to be specified, it's not
> > > > > > > > > > compatible from DT binding side. New yaml based binding must somehow
> > > > > > > > > > know that in order to be able to validate DT node, so it needs
> > > > > > > > > > standalone compatible. However, depending on conclusions of other
> > > > > > > > > > discussions, this new compatible can be associated with already
> > > > > > > > > > available quirks structure or have it's own.
> > > > > > > > >
> > > > > > > > > I cannot follow. You should be able to specify in the binding that the
> > > > > > > > > reset line and bus clock is optional. Then allwinner,sun50i-h6-pwm
> > > > > > > > > without a reset line and bus clock also verifies, but this doesn't
> > > > > > > > > really hurt (and who knows, maybe the next allwinner chip needs exactly this).
> > > > > > > >
> > > > > > > > It is not optional. It will not work if either the clocks or reset controls
> > > > > > > > are missing. How would these be optional anyway? Either it's connected and
> > > > > > > > thus required, or it's not and therefore should be omitted from the description.
> > > > > > >
> > > > > > > [Just arguing about the clock here, the argumentation is analogous for
> > > > > > > the reset control.]
> > > > > > >
> > > > > > > From the driver's perspective it's optional: There are devices with and
> > > > > > > without a bus clock. This doesn't mean that you can just ignore this
> > > > > > > clock if it's specified. It's optional in the sense "If dt doesn't
> > > > > > > specify it, then assume this is a device that doesn't have it and so you
> > > > > > > don't need to handle it." but not in the sense "it doesn't matter if
> > > > > > > you handle it or not.".
> > > > > > >
> > > > > > > Other than that I'm on your side. So for example I think it's not
> > > > > > > optimal that gpiod_get_optional returns NULL if GPIOLIB=n or that
> > > > > > > devm_reset_control_get_optional returns NULL if RESET_CONTROLLER=n
> > > > > > > because this hides exactly the kind of problem you point out here.
> > > > > >
> > > > > > I think there's misunderstanding. I only argued that we can't use
> > > > > >
> > > > > > compatible = "allwinner,sun50i-h6-pwm", "allwinner,sun5i-a10s-pwm";
> > > > > >
> > > > > > as you suggested and only
> > > > > >
> > > > > > compatible = "allwinner,sun50i-h6-pwm";
> > > > > >
> > > > > > will work. Not because of driver itself (it can still use _optional()
> > > > > > variants), but because of DT binding, which should be able to validate H6
> > > > > > PWM node - reset and bus clock references are required in this case.
> > > > >
> > > > > I think I understood. In my eyes there is no need to let validation of
> > > > > the DT bindings catch a missing "optional" property that is needed on
> > > > > H6.
> > > > >
> > > > > You have to draw the line somewhere which information the driver has
> > > > > hard-coded and what is only provided by the device tree and just assumed
> > > > > to be correct by the driver. You argue the driver should know that
> > > >
> > > > No, in this thread I argue that DT validation tool, executed by
> > > >
> > > > make ARCH=arm64 dtbs_check
> > > >
> > > > should catch that. This is not a driver, but DT binding described in YAML.
> > >
> > > The argumentation is the same. dtbs_check doesn't notice if the base
> > > address of your "allwinner,sun50i-h6-pwm" device is wrong. So why should
> > > it catch a missing reset controller phandle?
> >
> > Of course checking actual values of node properties doesn't make sense in
> > dtbs_check, otherwise we would have million DT bindings. If you have 10 copies
> > of the same IP core, of course you would use same compatible, but actual
> > register ranges, interrupts, etc. would be different in DT nodes.
> >
> > At this point I would make same argument as were made before, but there is no
> > point going in circles. I'm interested what have DT maintainers to say.
>
> --
> Pengutronix e.K.                           | Uwe Kleine-König            |
> Industrial Linux Solutions                 | http://www.pengutronix.de/  |
>
> --
> You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
> To view this discussion on the web, visit https://groups.google.com/d/msgid/linux-sunxi/20190730080900.hhxrqun7vk4nsj2h%40pengutronix.de.

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
To view this discussion on the web, visit https://groups.google.com/d/msgid/linux-sunxi/CAGb2v65jFdFZGLti4_B%3D2QPbtrj1b8wh63R5G3NpY_ndpJoV5g%40mail.gmail.com.

^ permalink raw reply

* Re: [PATCH v3 2/2] ARM: dts: imx6ul-kontron-n6310: Add Kontron i.MX6UL N6310 SoM and boards
From: Schrempf Frieder @ 2019-07-30  8:27 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Rob Herring, Mark Rutland, Shawn Guo,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	NXP Linux Team, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org
In-Reply-To: <20190729172007.3275-2-krzk@kernel.org>

On 29.07.19 19:20, Krzysztof Kozlowski wrote:
> Add support for i.MX6UL modules from Kontron Electronics GmbH (before
> acquisition: Exceet Electronics) and evalkit boards based on it:
> 
> 1. N6310 SOM: i.MX6 UL System-on-Module, a 25x25 mm solderable module
>     (LGA pads and pin castellations) with 256 MB RAM, 1 MB NOR-Flash,
>     256 MB NAND and other interfaces,
> 2. N6310 S: evalkit, w/wo eMMC, without display,
> 3. N6310 S 43: evalkit with 4.3" display,
> 4. N6310 S 50: evalkit with 5.0" display.
> 
> This includes device nodes for unsupported displays (Admatec
> T043C004800272T2A and T070P133T0S301).
> 
> The work is based on Exceet/Kontron source code (GPLv2) with numerous
> changes:
> 1. Reorganize files,
> 2. Rename Exceet -> Kontron,
> 3. Rename models/compatibles to match newest Kontron product naming,
> 4. Fix coding style errors and adjust to device tree coding guidelines,
> 5. Fix DTC warnings,
> 6. Extend compatibles so eval boards inherit the SoM compatible,
> 7. Use defines instead of GPIO and interrupt flag values,
> 8. Use proper vendor compatible for Macronix SPI NOR,
> 9. Sort nodes alphabetically.
> 
> Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
> 
> ---
> 
> Changes since v2, after Fabio's review:
> 1. Add "imx6ul" compatible to board name (that's what I understood from
>     review),
> 2. Add vendor/device prefix to eeprom and document the compatible,
> 3. Use "admatecde" as vendor compatible to avoid confusion with Admatec
>     AG in Switzerland (also making LCD panels),
> 4. Use generic names for nodes,
> 5. Use IRQ_TYPE_LEVEL_LOW,
> 6. Move iomux to the end of files,
> 7. Remove regulators node (include regulators in top level),
> 8. Remove cpu clock-frequency,
> 9. Other minor fixes pointed by Fabio.
> 
> Changes since v1, after Frieder's review:
> 1. Remove unneeded license notes,
> 2. Add Kontron copyright (2018),
> 3. Rename the files/models/compatibles to new naming - N6310,
> 4. Remove unneeded CPU operating points override,
> 5. Switch regulator nodes into simple children nodes without addresses
>     (so not simple bus),
> 6. Use proper vendor compatible for Macronix SPI NOR.
> ---
>   .../devicetree/bindings/arm/fsl.yaml          |   4 +
>   .../devicetree/bindings/eeprom/at25.txt       |   1 +
>   arch/arm/boot/dts/Makefile                    |   3 +
>   .../boot/dts/imx6ul-kontron-n6310-s-43.dts    | 119 +++++
>   .../boot/dts/imx6ul-kontron-n6310-s-50.dts    | 119 +++++
>   arch/arm/boot/dts/imx6ul-kontron-n6310-s.dts  | 420 ++++++++++++++++++
>   .../boot/dts/imx6ul-kontron-n6310-som.dtsi    | 134 ++++++
>   7 files changed, 800 insertions(+)
>   create mode 100644 arch/arm/boot/dts/imx6ul-kontron-n6310-s-43.dts
>   create mode 100644 arch/arm/boot/dts/imx6ul-kontron-n6310-s-50.dts
>   create mode 100644 arch/arm/boot/dts/imx6ul-kontron-n6310-s.dts
>   create mode 100644 arch/arm/boot/dts/imx6ul-kontron-n6310-som.dtsi

Reviewed-by: Frieder Schrempf <frieder.schrempf@kontron.de>

^ permalink raw reply

* Re: Re: [PATCH 4/6] pwm: sun4i: Add support for H6 PWM
From: Uwe Kleine-König @ 2019-07-30  8:09 UTC (permalink / raw)
  To: Rob Herring, Frank Rowand
  Cc: linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Chen-Yu Tsai,
	Jernej Škrabec, Mark Rutland,
	linux-pwm-u79uwXL29TY76Z2rM5mHXA, devicetree, linux-kernel,
	Maxime Ripard, Thierry Reding, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
	linux-arm-kernel
In-Reply-To: <2452836.v7ux4bnEjb@jernej-laptop>

Hello Rob and Frank,

Maxime and Jernej on one side and me on the other cannot agree about a
detail in the change to the bindings here. I'm trying to objectively
summarize the situation for you to help deciding what is the right thing
to do here.

TLDR: The sun4i pwm driver is extended to support a new variant of that
device on the H6 SoC. Compared to the earlier supported variants
allwinner,sun50i-h6-pwm on H6 needs to handle a reset controller and an
additional clock. 

The two positions are:

 - We need a new compatible because only then the driver and/or the dt
   schema checker can check that each "allwinner,sun50i-h6-pwm" device
   has a reset property and a "bus" clock; and the earlier variants
   don't.

 - The driver can be simpler and the device specific knowledge is only
   in a single place (the dt) if the device tree is considered valid and
   a reset property and "bus" clock is used iff it's provided in the
   device tree without additional comparison for the compatible.

Now our arguments seem to go in circles and Jernej was interested in
your position. That's something I agree with ;-) Can you please share
your view?

Find below some context about the arguments.

Best regards
Uwe

On Tue, Jul 30, 2019 at 12:04:47AM +0200, Jernej Škrabec wrote:
> Dne ponedeljek, 29. julij 2019 ob 20:51:08 CEST je Uwe Kleine-König 
> napisal(a):
> > On Mon, Jul 29, 2019 at 08:46:25PM +0200, Jernej Škrabec wrote:
> > > Dne ponedeljek, 29. julij 2019 ob 20:40:41 CEST je Uwe Kleine-König
> > > napisal(a):
> > > > On Mon, Jul 29, 2019 at 06:40:15PM +0200, Jernej Škrabec wrote:
> > > > > Dne ponedeljek, 29. julij 2019 ob 18:24:28 CEST je Uwe Kleine-König
> > > > > napisal(a):
> > > > > > On Tue, Jul 30, 2019 at 12:09:40AM +0800, Chen-Yu Tsai wrote:
> > > > > > > On Tue, Jul 30, 2019 at 12:07 AM Uwe Kleine-König
> > > > > > > <u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> wrote:
> > > > > > > > On Mon, Jul 29, 2019 at 05:55:52PM +0200, Jernej Škrabec wrote:
> > > > > > > > > Dne ponedeljek, 29. julij 2019 ob 08:40:30 CEST je Uwe Kleine-König
> > > > > > > > > napisal(a):
> > > > > > > > > > On Fri, Jul 26, 2019 at 08:40:43PM +0200, Jernej Skrabec wrote:
> > > > > > > > > > > --- a/drivers/pwm/pwm-sun4i.c
> > > > > > > > > > > +++ b/drivers/pwm/pwm-sun4i.c
> > > > > > > > > > > @@ -331,6 +331,13 @@ static const struct sun4i_pwm_data
> > > > > > > > > > > sun4i_pwm_single_bypass = {>
> > > > > > > > > > > 
> > > > > > > > > > >   .npwm = 1,
> > > > > > > > > > >  
> > > > > > > > > > >  };
> > > > > > > > > > > 
> > > > > > > > > > > +static const struct sun4i_pwm_data
> > > > > > > > > > > sun50i_pwm_dual_bypass_clk_rst
> > > > > > > > > > > = {
> > > > > > > > > > > + .has_bus_clock = true,
> > > > > > > > > > > + .has_prescaler_bypass = true,
> > > > > > > > > > > + .has_reset = true,
> > > > > > > > > > > + .npwm = 2,
> > > > > > > > > > > +};
> > > > > > > > > > > +
> > > > > > > > > > > 
> > > > > > > > > > >  static const struct of_device_id sun4i_pwm_dt_ids[] = {
> > > > > > > > > > >  
> > > > > > > > > > >   {
> > > > > > > > > > >   
> > > > > > > > > > >           .compatible = "allwinner,sun4i-a10-pwm",
> > > > > > > > > > > 
> > > > > > > > > > > @@ -347,6 +354,9 @@ static const struct of_device_id
> > > > > > > > > > > sun4i_pwm_dt_ids[] =
> > > > > > > > > > > {
> > > > > > > > > > > 
> > > > > > > > > > >   }, {
> > > > > > > > > > >   
> > > > > > > > > > >           .compatible = "allwinner,sun8i-h3-pwm",
> > > > > > > > > > >           .data = &sun4i_pwm_single_bypass,
> > > > > > > > > > > 
> > > > > > > > > > > + }, {
> > > > > > > > > > > +         .compatible = "allwinner,sun50i-h6-pwm",
> > > > > > > > > > > +         .data = &sun50i_pwm_dual_bypass_clk_rst,
> > > > > > > > > > 
> > > > > > > > > > If you follow my suggestion for the two previous patches,

(i.e. use devm_clk_get_optional instead of using devm_clk_get iff the
compatible is allwinner,sun50i-h6-pwm; analogous for the reset
controller.)

> > > > > > > > > > you can just use:
> > > > > > > > > >
> > > > > > > > > >     compatible = "allwinner,sun50i-h6-pwm", "allwinner,sun5i-a10s-pwm";
> > > > > > > > > > 
> > > > > > > > > > and drop this patch.
> > > > > > > > > 
> > > > > > > > > Maxime found out that it's not compatible with A10s due to difference
> > > > > > > > > in bypass bit, but yes, I know what you mean.
> > > > > > > > > 
> > > > > > > > > Since H6 requires reset line and bus clock to be specified, it's not
> > > > > > > > > compatible from DT binding side. New yaml based binding must somehow
> > > > > > > > > know that in order to be able to validate DT node, so it needs
> > > > > > > > > standalone compatible. However, depending on conclusions of other
> > > > > > > > > discussions, this new compatible can be associated with already
> > > > > > > > > available quirks structure or have it's own.
> > > > > > > > 
> > > > > > > > I cannot follow. You should be able to specify in the binding that the
> > > > > > > > reset line and bus clock is optional. Then allwinner,sun50i-h6-pwm
> > > > > > > > without a reset line and bus clock also verifies, but this doesn't
> > > > > > > > really hurt (and who knows, maybe the next allwinner chip needs exactly this).
> > > > > > > 
> > > > > > > It is not optional. It will not work if either the clocks or reset controls
> > > > > > > are missing. How would these be optional anyway? Either it's connected and
> > > > > > > thus required, or it's not and therefore should be omitted from the description.
> > > > > > 
> > > > > > [Just arguing about the clock here, the argumentation is analogous for
> > > > > > the reset control.]
> > > > > > 
> > > > > > From the driver's perspective it's optional: There are devices with and
> > > > > > without a bus clock. This doesn't mean that you can just ignore this
> > > > > > clock if it's specified. It's optional in the sense "If dt doesn't
> > > > > > specify it, then assume this is a device that doesn't have it and so you
> > > > > > don't need to handle it." but not in the sense "it doesn't matter if
> > > > > > you handle it or not.".
> > > > > > 
> > > > > > Other than that I'm on your side. So for example I think it's not
> > > > > > optimal that gpiod_get_optional returns NULL if GPIOLIB=n or that
> > > > > > devm_reset_control_get_optional returns NULL if RESET_CONTROLLER=n
> > > > > > because this hides exactly the kind of problem you point out here.
> > > > > 
> > > > > I think there's misunderstanding. I only argued that we can't use
> > > > > 
> > > > > compatible = "allwinner,sun50i-h6-pwm", "allwinner,sun5i-a10s-pwm";
> > > > > 
> > > > > as you suggested and only
> > > > > 
> > > > > compatible = "allwinner,sun50i-h6-pwm";
> > > > > 
> > > > > will work. Not because of driver itself (it can still use _optional()
> > > > > variants), but because of DT binding, which should be able to validate H6
> > > > > PWM node - reset and bus clock references are required in this case.
> > > > 
> > > > I think I understood. In my eyes there is no need to let validation of
> > > > the DT bindings catch a missing "optional" property that is needed on
> > > > H6.
> > > > 
> > > > You have to draw the line somewhere which information the driver has
> > > > hard-coded and what is only provided by the device tree and just assumed
> > > > to be correct by the driver. You argue the driver should know that
> > > 
> > > No, in this thread I argue that DT validation tool, executed by
> > > 
> > > make ARCH=arm64 dtbs_check
> > > 
> > > should catch that. This is not a driver, but DT binding described in YAML.
> > 
> > The argumentation is the same. dtbs_check doesn't notice if the base
> > address of your "allwinner,sun50i-h6-pwm" device is wrong. So why should
> > it catch a missing reset controller phandle?
> 
> Of course checking actual values of node properties doesn't make sense in 
> dtbs_check, otherwise we would have million DT bindings. If you have 10 copies 
> of the same IP core, of course you would use same compatible, but actual 
> register ranges, interrupts, etc. would be different in DT nodes.
> 
> At this point I would make same argument as were made before, but there is no 
> point going in circles. I'm interested what have DT maintainers to say.

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
To view this discussion on the web, visit https://groups.google.com/d/msgid/linux-sunxi/20190730080900.hhxrqun7vk4nsj2h%40pengutronix.de.

^ permalink raw reply

* Re: [PATCH v2 6/7] ASoC: fsl_sai: Add support for imx7ulp/imx8mq
From: Nicolin Chen @ 2019-07-30  8:05 UTC (permalink / raw)
  To: Daniel Baluta
  Cc: broonie, l.stach, mihai.serban, alsa-devel, viorel.suman, timur,
	shengjiu.wang, angus, tiwai, linux-imx, kernel, festevam,
	linux-kernel, devicetree, robh
In-Reply-To: <20190728192429.1514-7-daniel.baluta@nxp.com>

On Sun, Jul 28, 2019 at 10:24:28PM +0300, Daniel Baluta wrote:
> SAI module on imx7ulp/imx8m features 2 new registers (VERID and PARAM)
> at the beginning of register address space.
> 
> On imx7ulp FIFOs can held up to 16 x 32 bit samples.
> On imx8mq FIFOs can held up to 128 x 32 bit samples.
> 
> Signed-off-by: Daniel Baluta <daniel.baluta@nxp.com>

Acked-by: Nicolin Chen <nicoleotsuka@gmail.com>

> ---
>  sound/soc/fsl/fsl_sai.c | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
> 
> diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
> index 54e5e9abae01..0fb6750fefd5 100644
> --- a/sound/soc/fsl/fsl_sai.c
> +++ b/sound/soc/fsl/fsl_sai.c
> @@ -1030,10 +1030,24 @@ static const struct fsl_sai_soc_data fsl_sai_imx6sx_data = {
>  	.reg_offset = 0,
>  };
>  
> +static const struct fsl_sai_soc_data fsl_sai_imx7ulp_data = {
> +	.use_imx_pcm = true,
> +	.fifo_depth = 16,
> +	.reg_offset = 8,
> +};
> +
> +static const struct fsl_sai_soc_data fsl_sai_imx8mq_data = {
> +	.use_imx_pcm = true,
> +	.fifo_depth = 128,
> +	.reg_offset = 8,
> +};
> +
>  static const struct of_device_id fsl_sai_ids[] = {
>  	{ .compatible = "fsl,vf610-sai", .data = &fsl_sai_vf610_data },
>  	{ .compatible = "fsl,imx6sx-sai", .data = &fsl_sai_imx6sx_data },
>  	{ .compatible = "fsl,imx6ul-sai", .data = &fsl_sai_imx6sx_data },
> +	{ .compatible = "fsl,imx7ulp-sai", .data = &fsl_sai_imx7ulp_data },
> +	{ .compatible = "fsl,imx8mq-sai", .data = &fsl_sai_imx8mq_data },
>  	{ /* sentinel */ }
>  };
>  MODULE_DEVICE_TABLE(of, fsl_sai_ids);
> -- 
> 2.17.1
> 

^ permalink raw reply

* Re: [PATCH v2 7/7] ASoC: dt-bindings: Introduce compatible strings for 7ULP and 8MQ
From: Nicolin Chen @ 2019-07-30  8:01 UTC (permalink / raw)
  To: Daniel Baluta
  Cc: broonie, l.stach, mihai.serban, alsa-devel, viorel.suman, timur,
	shengjiu.wang, angus, tiwai, linux-imx, kernel, festevam,
	linux-kernel, devicetree, robh
In-Reply-To: <20190728192429.1514-8-daniel.baluta@nxp.com>

On Sun, Jul 28, 2019 at 10:24:29PM +0300, Daniel Baluta wrote:
> For i.MX7ULP and i.MX8MQ register map is changed. Add two new compatbile
> strings to differentiate this.
> 
> Signed-off-by: Daniel Baluta <daniel.baluta@nxp.com>
> ---
>  Documentation/devicetree/bindings/sound/fsl-sai.txt | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/sound/fsl-sai.txt b/Documentation/devicetree/bindings/sound/fsl-sai.txt
> index 2b38036a4883..b008e9cfedc1 100644
> --- a/Documentation/devicetree/bindings/sound/fsl-sai.txt
> +++ b/Documentation/devicetree/bindings/sound/fsl-sai.txt
> @@ -8,7 +8,8 @@ codec/DSP interfaces.
>  Required properties:
>  
>    - compatible		: Compatible list, contains "fsl,vf610-sai",
> -			  "fsl,imx6sx-sai" or "fsl,imx6ul-sai"
> +			  "fsl,imx6sx-sai", "fsl,imx6ul-sai",
> +			  "fsl,imx7ulp-sai", "fsl,imx8mq-sai".

A nit, could have that 'or' :)

>  
>    - reg			: Offset and length of the register set for the device.
>  
> -- 
> 2.17.1
> 

^ permalink raw reply

* Re: [alsa-devel] [PATCH v2 1/7] ASoC: fsl_sai: Add registers definition for multiple datalines
From: Nicolin Chen @ 2019-07-30  7:59 UTC (permalink / raw)
  To: Mark Brown
  Cc: Daniel Baluta, Daniel Baluta, Devicetree List, Linux-ALSA,
	Pengutronix Kernel Team, Timur Tabi, Rob Herring, S.j. Wang,
	Angus Ainslie (Purism), Takashi Iwai, Linux Kernel Mailing List,
	dl-linux-imx, Viorel Suman, Fabio Estevam, Mihai Serban,
	Lucas Stach
In-Reply-To: <20190729202001.GC4787@sirena.org.uk>

On Mon, Jul 29, 2019 at 09:20:01PM +0100, Mark Brown wrote:
> On Mon, Jul 29, 2019 at 10:57:43PM +0300, Daniel Baluta wrote:
> > On Mon, Jul 29, 2019 at 10:42 PM Nicolin Chen <nicoleotsuka@gmail.com> wrote:
> > > On Sun, Jul 28, 2019 at 10:24:23PM +0300, Daniel Baluta wrote:
> 
> > > > @@ -704,7 +711,14 @@ static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
> > > >       case FSL_SAI_TCR3:
> > > >       case FSL_SAI_TCR4:
> > > >       case FSL_SAI_TCR5:
> > > > -     case FSL_SAI_TFR:
> > > > +     case FSL_SAI_TFR0:
> 
> > > A tricky thing here is that those SAI instances on older SoC don't
> > > support multi data lines physically, while seemly having registers
> > > pre-defined. So your change doesn't sound doing anything wrong to
> > > them at all, I am still wondering if it is necessary to apply them
> > > to newer compatible only though, as for older compatibles of SAI,
> > > these registers would be useless and confusing if being exposed.
> 
> > > What do you think?
> 
> > Yes, I thought about this too. But, I tried to keep the code as short
> > as possible and technically it is not wrong. When 1 data line is supported
> > for example application will only care about TDR0, TFR0, etc.
> 
> So long as it's safe to read the registers (you don't get a bus error or
> anything) I'd say it's more trouble than it's worth to have separate
> regmap configuations just for this.  The main reasons for restricting
> readability are where there's physical problems with doing the reads or
> to keep the size of the debugfs files under control for usability and
> performance reasons.

Thanks for the input, Mark.

Daniel, did you get a chance to test it on older SoCs? At least
nothing breaks like bus errors?

^ permalink raw reply

* Re: [PATCH 1/4 v4] irqchip: Ingenic: Change interrupt handling form cascade to chained_irq.
From: Zhou Yanjie @ 2019-07-30  6:41 UTC (permalink / raw)
  To: Paul Cercueil
  Cc: linux-mips, linux-kernel, devicetree, robh+dt, paul.burton, tglx,
	mark.rutland, jason, marc.zyngier
In-Reply-To: <1564420754.6633.0@crapouillou.net>

Hi Paul,
Thanks for your suggestion, and after receiving Marc's comments,
if this patch can be continued, I'll use for_each_set_bit() to simplify
code in v5.

On 2019年07月30日 01:19, Paul Cercueil wrote:
> Hi Zhou,
>
>
>
> Le dim. 28 juil. 2019 à 13:34, Zhou Yanjie <zhouyanjie@zoho.com> a 
> écrit :
>> The interrupt handling method is changed from old-style cascade to
>> chained_irq which is more appropriate. Also, it can process the
>> corner situation that more than one irq is coming to a single
>> chip at the same time.
>>
>> Signed-off-by: Zhou Yanjie <zhouyanjie@zoho.com>
>> ---
>>  drivers/irqchip/irq-ingenic.c | 37 
>> +++++++++++++++++++++++--------------
>>  1 file changed, 23 insertions(+), 14 deletions(-)
>>
>> diff --git a/drivers/irqchip/irq-ingenic.c 
>> b/drivers/irqchip/irq-ingenic.c
>> index f126255..49f7685 100644
>> --- a/drivers/irqchip/irq-ingenic.c
>> +++ b/drivers/irqchip/irq-ingenic.c
>> @@ -1,7 +1,7 @@
>>  // SPDX-License-Identifier: GPL-2.0-or-later
>>  /*
>>   *  Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
>> - *  JZ4740 platform IRQ support
>> + *  Ingenic XBurst platform IRQ support
>>   */
>>
>>  #include <linux/errno.h>
>> @@ -10,6 +10,7 @@
>>  #include <linux/interrupt.h>
>>  #include <linux/ioport.h>
>>  #include <linux/irqchip.h>
>> +#include <linux/irqchip/chained_irq.h>
>>  #include <linux/irqchip/ingenic.h>
>>  #include <linux/of_address.h>
>>  #include <linux/of_irq.h>
>> @@ -32,22 +33,34 @@ struct ingenic_intc_data {
>>  #define JZ_REG_INTC_PENDING    0x10
>>  #define CHIP_SIZE        0x20
>>
>> -static irqreturn_t intc_cascade(int irq, void *data)
>> +static void ingenic_chained_handle_irq(struct irq_desc *desc)
>>  {
>> -    struct ingenic_intc_data *intc = irq_get_handler_data(irq);
>> -    uint32_t irq_reg;
>> +    struct ingenic_intc_data *intc = irq_desc_get_handler_data(desc);
>> +    struct irq_chip *chip = irq_desc_get_chip(desc);
>> +    bool have_irq = false;
>> +    uint32_t pending;
>>      unsigned i;
>>
>> +    chained_irq_enter(chip, desc);
>>      for (i = 0; i < intc->num_chips; i++) {
>> -        irq_reg = readl(intc->base + (i * CHIP_SIZE) +
>> +        pending = readl(intc->base + (i * CHIP_SIZE) +
>>                  JZ_REG_INTC_PENDING);
>> -        if (!irq_reg)
>> +        if (!pending)
>>              continue;
>>
>> -        generic_handle_irq(__fls(irq_reg) + (i * 32) + 
>> JZ4740_IRQ_BASE);
>> +        have_irq = true;
>> +        while (pending) {
>> +            int bit = __fls(pending);
>
> Use the for_each_set_bit() macro here, that will be simpler.
>
>
>> +
>> +            generic_handle_irq(bit + (i * 32) + JZ4740_IRQ_BASE);
>> +            pending &= ~BIT(bit);
>> +        }
>>      }
>>
>> -    return IRQ_HANDLED;
>> +    if (!have_irq)
>> +        spurious_interrupt();
>> +
>> +    chained_irq_exit(chip, desc);
>>  }
>>
>>  static void intc_irq_set_mask(struct irq_chip_generic *gc, uint32_t 
>> mask)
>> @@ -70,11 +83,6 @@ void ingenic_intc_irq_resume(struct irq_data *data)
>>      intc_irq_set_mask(gc, gc->mask_cache);
>>  }
>>
>> -static struct irqaction intc_cascade_action = {
>> -    .handler = intc_cascade,
>> -    .name = "SoC intc cascade interrupt",
>> -};
>> -
>>  static int __init ingenic_intc_of_init(struct device_node *node,
>>                         unsigned num_chips)
>>  {
>> @@ -139,7 +147,8 @@ static int __init ingenic_intc_of_init(struct 
>> device_node *node,
>>      if (!domain)
>>          pr_warn("unable to register IRQ domain\n");
>>
>> -    setup_irq(parent_irq, &intc_cascade_action);
>> +    irq_set_chained_handler_and_data(parent_irq,
>> +                    ingenic_chained_handle_irq, intc);
>>      return 0;
>>
>>  out_unmap_irq:
>> -- 
>> 2.7.4
>
>

^ permalink raw reply

* Re: [PATCH 4/4 v4] irqchip: Ingenic: Add support for new Ingenic Socs.
From: Zhou Yanjie @ 2019-07-30  6:26 UTC (permalink / raw)
  To: Paul Cercueil
  Cc: linux-mips, linux-kernel, devicetree, robh+dt, paul.burton, tglx,
	mark.rutland, jason, marc.zyngier
In-Reply-To: <1564421133.6633.1@crapouillou.net>

Hi Paul,
It seems very strange to appear another soc model in one soc's devicetree,
and before submitting this patch I referred to some other irq drivers. 
Similar
usage was found in irq-sunxi-nmi.c and irq-omap-intc.c, and the original
irq-ingenic.c was declare jz4770, jz4775, jz4780 in the same way. So I 
followed
the same method to add the declare of jz4760/x1000/x1500, this may be a 
little
better.

On 2019年07月30日 01:25, Paul Cercueil wrote:
>
>
> Le dim. 28 juil. 2019 à 13:34, Zhou Yanjie <zhouyanjie@zoho.com> a 
> écrit :
>> Add support for probing the irq-ingenic driver on the JZ4760/JZ4760B
>> and the X1000/X1000E and the X1500 Socs from Ingenic.
>>
>> Signed-off-by: Zhou Yanjie <zhouyanjie@zoho.com>
>> ---
>>  drivers/irqchip/irq-ingenic.c | 5 +++++
>>  1 file changed, 5 insertions(+)
>>
>> diff --git a/drivers/irqchip/irq-ingenic.c 
>> b/drivers/irqchip/irq-ingenic.c
>> index 8430f5a..b72430c 100644
>> --- a/drivers/irqchip/irq-ingenic.c
>> +++ b/drivers/irqchip/irq-ingenic.c
>> @@ -173,6 +173,11 @@ static int __init intc_2chip_of_init(struct 
>> device_node *node,
>>  {
>>      return ingenic_intc_of_init(node, 2);
>>  }
>> +IRQCHIP_DECLARE(jz4760_intc, "ingenic,jz4760-intc", 
>> intc_2chip_of_init);
>> +IRQCHIP_DECLARE(jz4760b_intc, "ingenic,jz4760b-intc", 
>> intc_2chip_of_init);
>>  IRQCHIP_DECLARE(jz4770_intc, "ingenic,jz4770-intc", 
>> intc_2chip_of_init);
>>  IRQCHIP_DECLARE(jz4775_intc, "ingenic,jz4775-intc", 
>> intc_2chip_of_init);
>>  IRQCHIP_DECLARE(jz4780_intc, "ingenic,jz4780-intc", 
>> intc_2chip_of_init);
>> +IRQCHIP_DECLARE(x1000_intc, "ingenic,x1000-intc", intc_2chip_of_init);
>> +IRQCHIP_DECLARE(x1000e_intc, "ingenic,x1000e-intc", 
>> intc_2chip_of_init);
>> +IRQCHIP_DECLARE(x1500_intc, "ingenic,x1500-intc", intc_2chip_of_init);
>
> All these compatible strings point to the exact same behaviour. It was
> already a mistake to have the three "ingenic,jz47[70,75,80]-intc" here;
> there should have been only one, e.g. "ingenic,jz4770-intc" and the other
> two SoCs using it as a fallback compatible.
>
> I think you don't need to add these, and in your devicetree just use
> "ingenic,jz4780-intc" as a fallback compatible.
>
> Cheers,
> -Paul
>
>

^ permalink raw reply

* [PATCH v3 2/2] mmc: Add support for the ASPEED SD controller
From: Andrew Jeffery @ 2019-07-30  6:23 UTC (permalink / raw)
  To: linux-mmc
  Cc: Andrew Jeffery, ulf.hansson, robh+dt, mark.rutland, joel,
	adrian.hunter, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, ryanchen.aspeed
In-Reply-To: <20190730062316.32037-1-andrew@aj.id.au>

Add a minimal driver for ASPEED's SD controller, which exposes two
SDHCIs.

The ASPEED design implements a common register set for the SDHCIs, and
moves some of the standard configuration elements out to this common
area (e.g. 8-bit mode, and card detect configuration which is not
currently supported).

The SD controller has a dedicated hardware interrupt that is shared
between the slots. The common register set exposes information on which
slot triggered the interrupt; early revisions of the patch introduced an
irqchip for the register, but reality is it doesn't behave as an
irqchip, and the result fits awkwardly into the irqchip APIs. Instead
I've taken the simple approach of using the IRQ as a shared IRQ with
some minor performance impact for the second slot.

Ryan was the original author of the patch - I've taken his work and
massaged it to drop the irqchip support and rework the devicetree
integration. The driver has been smoke tested under qemu against a
minimal SD controller model and lightly tested on an ast2500-evb.

Signed-off-by: Ryan Chen <ryanchen.aspeed@gmail.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>

---
v3:
* Add AST2600 compatible
* Drop SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN
* Ensure slot number is valid
* Fix build with CONFIG_MODULES
* Fix module license string
* Non-PCI devices won't die
* Rename aspeed_sdc_configure_8bit_mode()
* Rename aspeed_sdhci_pdata
* Switch to sdhci_enable_clk()
* Use PTR_ERR() on the right `struct platform_device *`
---
 drivers/mmc/host/Kconfig           |  12 ++
 drivers/mmc/host/Makefile          |   1 +
 drivers/mmc/host/sdhci-of-aspeed.c | 328 +++++++++++++++++++++++++++++
 3 files changed, 341 insertions(+)
 create mode 100644 drivers/mmc/host/sdhci-of-aspeed.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 14d89a108edd..0f8a230de2f3 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -154,6 +154,18 @@ config MMC_SDHCI_OF_ARASAN
 
 	  If unsure, say N.
 
+config MMC_SDHCI_OF_ASPEED
+	tristate "SDHCI OF support for the ASPEED SDHCI controller"
+	depends on MMC_SDHCI_PLTFM
+	depends on OF
+	help
+	  This selects the ASPEED Secure Digital Host Controller Interface.
+
+	  If you have a controller with this interface, say Y or M here. You
+	  also need to enable an appropriate bus interface.
+
+	  If unsure, say N.
+
 config MMC_SDHCI_OF_AT91
 	tristate "SDHCI OF support for the Atmel SDMMC controller"
 	depends on MMC_SDHCI_PLTFM
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 73578718f119..390ee162fe71 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -84,6 +84,7 @@ obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX)	+= sdhci-esdhc-imx.o
 obj-$(CONFIG_MMC_SDHCI_DOVE)		+= sdhci-dove.o
 obj-$(CONFIG_MMC_SDHCI_TEGRA)		+= sdhci-tegra.o
 obj-$(CONFIG_MMC_SDHCI_OF_ARASAN)	+= sdhci-of-arasan.o
+obj-$(CONFIG_MMC_SDHCI_OF_ASPEED)	+= sdhci-of-aspeed.o
 obj-$(CONFIG_MMC_SDHCI_OF_AT91)		+= sdhci-of-at91.o
 obj-$(CONFIG_MMC_SDHCI_OF_ESDHC)	+= sdhci-of-esdhc.o
 obj-$(CONFIG_MMC_SDHCI_OF_HLWD)		+= sdhci-of-hlwd.o
diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c
new file mode 100644
index 000000000000..d31785ec90d7
--- /dev/null
+++ b/drivers/mmc/host/sdhci-of-aspeed.c
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (C) 2019 ASPEED Technology Inc. */
+/* Copyright (C) 2019 IBM Corp. */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/mmc/host.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#include "sdhci-pltfm.h"
+
+#define ASPEED_SDC_INFO		0x00
+#define   ASPEED_SDC_S1MMC8	BIT(25)
+#define   ASPEED_SDC_S0MMC8	BIT(24)
+
+struct aspeed_sdc {
+	struct clk *clk;
+	struct resource *res;
+
+	spinlock_t lock;
+	void __iomem *regs;
+};
+
+struct aspeed_sdhci {
+	struct aspeed_sdc *parent;
+	u32 width_mask;
+};
+
+static void aspeed_sdc_configure_8bit_mode(struct aspeed_sdc *sdc,
+					   struct aspeed_sdhci *sdhci,
+					   bool bus8)
+{
+	u32 info;
+
+	/* Set/clear 8 bit mode */
+	spin_lock(&sdc->lock);
+	info = readl(sdc->regs + ASPEED_SDC_INFO);
+	if (bus8)
+		info |= sdhci->width_mask;
+	else
+		info &= ~sdhci->width_mask;
+	writel(info, sdc->regs + ASPEED_SDC_INFO);
+	spin_unlock(&sdc->lock);
+}
+
+static void aspeed_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+	int div;
+	u16 clk;
+
+	if (clock == host->clock)
+		return;
+
+	sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+
+	if (clock == 0)
+		goto out;
+
+	for (div = 1; div < 256; div *= 2) {
+		if ((host->max_clk / div) <= clock)
+			break;
+	}
+	div >>= 1;
+
+	clk = div << SDHCI_DIVIDER_SHIFT;
+
+	sdhci_enable_clk(host, clk);
+
+out:
+	host->clock = clock;
+}
+
+static void aspeed_sdhci_set_bus_width(struct sdhci_host *host, int width)
+{
+	struct sdhci_pltfm_host *pltfm_priv;
+	struct aspeed_sdhci *aspeed_sdhci;
+	struct aspeed_sdc *aspeed_sdc;
+	u8 ctrl;
+
+	pltfm_priv = sdhci_priv(host);
+	aspeed_sdhci = sdhci_pltfm_priv(pltfm_priv);
+	aspeed_sdc = aspeed_sdhci->parent;
+
+	/* Set/clear 8-bit mode */
+	aspeed_sdc_configure_8bit_mode(aspeed_sdc, aspeed_sdhci,
+				       width == MMC_BUS_WIDTH_8);
+
+	/* Set/clear 1 or 4 bit mode */
+	ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
+	if (width == MMC_BUS_WIDTH_4)
+		ctrl |= SDHCI_CTRL_4BITBUS;
+	else
+		ctrl &= ~SDHCI_CTRL_4BITBUS;
+	sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+}
+
+static const struct sdhci_ops aspeed_sdhci_ops = {
+	.set_clock = aspeed_sdhci_set_clock,
+	.get_max_clock = sdhci_pltfm_clk_get_max_clock,
+	.set_bus_width = aspeed_sdhci_set_bus_width,
+	.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
+	.reset = sdhci_reset,
+	.set_uhs_signaling = sdhci_set_uhs_signaling,
+};
+
+static const struct sdhci_pltfm_data aspeed_sdhci_pdata = {
+	.ops = &aspeed_sdhci_ops,
+	.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+};
+
+static inline int aspeed_sdhci_calculate_slot(struct aspeed_sdhci *dev,
+					      struct resource *res)
+{
+	resource_size_t delta;
+
+	if (!res || resource_type(res) != IORESOURCE_MEM)
+		return -EINVAL;
+
+	if (res->start < dev->parent->res->start)
+		return -EINVAL;
+
+	delta = res->start - dev->parent->res->start;
+	if (delta & (0x100 - 1))
+		return -EINVAL;
+
+	return (delta / 0x100) - 1;
+}
+
+static int aspeed_sdhci_probe(struct platform_device *pdev)
+{
+	struct sdhci_pltfm_host *pltfm_host;
+	struct aspeed_sdhci *dev;
+	struct sdhci_host *host;
+	struct resource *res;
+	int slot;
+	int ret;
+
+	host = sdhci_pltfm_init(pdev, &aspeed_sdhci_pdata, sizeof(*dev));
+	if (IS_ERR(host))
+		return PTR_ERR(host);
+
+	pltfm_host = sdhci_priv(host);
+	dev = sdhci_pltfm_priv(pltfm_host);
+	dev->parent = dev_get_drvdata(pdev->dev.parent);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	slot = aspeed_sdhci_calculate_slot(dev, res);
+
+	if (slot < 0)
+		return slot;
+	else if (slot >= 2)
+		return -EINVAL;
+
+	dev_info(&pdev->dev, "Configuring for slot %d\n", slot);
+	dev->width_mask = !slot ? ASPEED_SDC_S0MMC8 : ASPEED_SDC_S1MMC8;
+
+	sdhci_get_of_property(pdev);
+
+	pltfm_host->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(pltfm_host->clk))
+		return PTR_ERR(pltfm_host->clk);
+
+	ret = clk_prepare_enable(pltfm_host->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to enable SDIO clock\n");
+		goto err_pltfm_free;
+	}
+
+	ret = mmc_of_parse(host->mmc);
+	if (ret)
+		goto err_sdhci_add;
+
+	ret = sdhci_add_host(host);
+	if (ret)
+		goto err_sdhci_add;
+
+	return 0;
+
+err_sdhci_add:
+	clk_disable_unprepare(pltfm_host->clk);
+err_pltfm_free:
+	sdhci_pltfm_free(pdev);
+	return ret;
+}
+
+static int aspeed_sdhci_remove(struct platform_device *pdev)
+{
+	struct sdhci_pltfm_host *pltfm_host;
+	struct sdhci_host *host;
+	int dead = 0;
+
+	host = platform_get_drvdata(pdev);
+	pltfm_host = sdhci_priv(host);
+
+	sdhci_remove_host(host, dead);
+
+	clk_disable_unprepare(pltfm_host->clk);
+
+	sdhci_pltfm_free(pdev);
+
+	return 0;
+}
+
+static const struct of_device_id aspeed_sdhci_of_match[] = {
+	{ .compatible = "aspeed,ast2400-sdhci", },
+	{ .compatible = "aspeed,ast2500-sdhci", },
+	{ .compatible = "aspeed,ast2600-sdhci", },
+	{ }
+};
+
+static struct platform_driver aspeed_sdhci_driver = {
+	.driver		= {
+		.name	= "sdhci-aspeed",
+		.of_match_table = aspeed_sdhci_of_match,
+	},
+	.probe		= aspeed_sdhci_probe,
+	.remove		= aspeed_sdhci_remove,
+};
+
+static int aspeed_sdc_probe(struct platform_device *pdev)
+
+{
+	struct device_node *parent, *child;
+	struct aspeed_sdc *sdc;
+	int ret;
+
+	sdc = devm_kzalloc(&pdev->dev, sizeof(*sdc), GFP_KERNEL);
+	if (!sdc)
+		return -ENOMEM;
+
+	spin_lock_init(&sdc->lock);
+
+	sdc->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(sdc->clk))
+		return PTR_ERR(sdc->clk);
+
+	ret = clk_prepare_enable(sdc->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to enable SDCLK\n");
+		return ret;
+	}
+
+	sdc->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	sdc->regs = devm_ioremap_resource(&pdev->dev, sdc->res);
+	if (IS_ERR(sdc->regs)) {
+		ret = PTR_ERR(sdc->regs);
+		goto err_clk;
+	}
+
+	dev_set_drvdata(&pdev->dev, sdc);
+
+	parent = pdev->dev.of_node;
+	for_each_available_child_of_node(parent, child) {
+		struct platform_device *cpdev;
+
+		cpdev = of_platform_device_create(child, NULL, &pdev->dev);
+		if (IS_ERR(cpdev)) {
+			of_node_put(child);
+			ret = PTR_ERR(cpdev);
+			goto err_clk;
+		}
+	}
+
+	return 0;
+
+err_clk:
+	clk_disable_unprepare(sdc->clk);
+	return ret;
+}
+
+static int aspeed_sdc_remove(struct platform_device *pdev)
+{
+	struct aspeed_sdc *sdc = dev_get_drvdata(&pdev->dev);
+
+	clk_disable_unprepare(sdc->clk);
+
+	return 0;
+}
+
+static const struct of_device_id aspeed_sdc_of_match[] = {
+	{ .compatible = "aspeed,ast2400-sd-controller", },
+	{ .compatible = "aspeed,ast2500-sd-controller", },
+	{ .compatible = "aspeed,ast2600-sd-controller", },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, aspeed_sdc_of_match);
+
+static struct platform_driver aspeed_sdc_driver = {
+	.driver		= {
+		.name	= "sd-controller-aspeed",
+		.pm	= &sdhci_pltfm_pmops,
+		.of_match_table = aspeed_sdc_of_match,
+	},
+	.probe		= aspeed_sdc_probe,
+	.remove		= aspeed_sdc_remove,
+};
+
+static int __init aspeed_sdc_init(void)
+{
+	int rc;
+
+	rc = platform_driver_register(&aspeed_sdhci_driver);
+	if (rc < 0)
+		return rc;
+
+	return platform_driver_register(&aspeed_sdc_driver);
+}
+module_init(aspeed_sdc_init);
+
+static void __exit aspeed_sdc_exit(void)
+{
+	platform_driver_unregister(&aspeed_sdc_driver);
+	platform_driver_unregister(&aspeed_sdhci_driver);
+}
+module_exit(aspeed_sdc_exit);
+
+MODULE_DESCRIPTION("Driver for the ASPEED SD/SDIO/SDHCI Controllers");
+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
+MODULE_LICENSE("GPL");
-- 
2.20.1

^ permalink raw reply related

* [PATCH v3 1/2] dt-bindings: mmc: Document Aspeed SD controller
From: Andrew Jeffery @ 2019-07-30  6:23 UTC (permalink / raw)
  To: linux-mmc
  Cc: Andrew Jeffery, ulf.hansson, robh+dt, mark.rutland, joel,
	adrian.hunter, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, ryanchen.aspeed
In-Reply-To: <20190730062316.32037-1-andrew@aj.id.au>

The ASPEED SD/SDIO/eMMC controller exposes two slots implementing the
SDIO Host Specification v2.00, with 1 or 4 bit data buses, or an 8 bit
data bus if only a single slot is enabled.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>

---
v3:
* Fix compatible enums
* Add AST2600 compatibles
* Describe #address-cells / #size-cells
---
 .../devicetree/bindings/mmc/aspeed,sdhci.yaml | 100 ++++++++++++++++++
 1 file changed, 100 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml

diff --git a/Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml b/Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml
new file mode 100644
index 000000000000..dd2a00c59641
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml
@@ -0,0 +1,100 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mmc/aspeed,sdhci.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ASPEED SD/SDIO/eMMC Controller
+
+maintainers:
+  - Andrew Jeffery <andrew@aj.id.au>
+  - Ryan Chen <ryanchen.aspeed@gmail.com>
+
+description: |+
+  The ASPEED SD/SDIO/eMMC controller exposes two slots implementing the SDIO
+  Host Specification v2.00, with 1 or 4 bit data buses, or an 8 bit data bus if
+  only a single slot is enabled.
+
+  The two slots are supported by a common configuration area. As the SDHCIs for
+  the slots are dependent on the common configuration area, they are described
+  as child nodes.
+
+properties:
+  compatible:
+    enum:
+      - aspeed,ast2400-sd-controller
+      - aspeed,ast2500-sd-controller
+      - aspeed,ast2600-sd-controller
+  reg:
+    maxItems: 1
+    description: Common configuration registers
+  "#address-cells":
+    const: 1
+  "#size-cells":
+    const: 1
+  ranges: true
+  clocks:
+    maxItems: 1
+    description: The SD/SDIO controller clock gate
+
+patternProperties:
+  "^sdhci@[0-9a-f]+$":
+    type: object
+    properties:
+      compatible:
+        enum:
+          - aspeed,ast2400-sdhci
+          - aspeed,ast2500-sdhci
+          - aspeed,ast2600-sdhci
+      reg:
+        maxItems: 1
+        description: The SDHCI registers
+      clocks:
+        maxItems: 1
+        description: The SD bus clock
+      interrupts:
+        maxItems: 1
+        description: The SD interrupt shared between both slots
+    required:
+      - compatible
+      - reg
+      - clocks
+      - interrupts
+
+additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - "#address-cells"
+  - "#size-cells"
+  - ranges
+  - clocks
+
+examples:
+  - |
+    #include <dt-bindings/clock/aspeed-clock.h>
+    sdc@1e740000 {
+            compatible = "aspeed,ast2500-sd-controller";
+            reg = <0x1e740000 0x100>;
+            #address-cells = <1>;
+            #size-cells = <1>;
+            ranges = <0 0x1e740000 0x10000>;
+            clocks = <&syscon ASPEED_CLK_GATE_SDCLK>;
+
+            sdhci0: sdhci@100 {
+                    compatible = "aspeed,ast2500-sdhci";
+                    reg = <0x100 0x100>;
+                    interrupts = <26>;
+                    sdhci,auto-cmd12;
+                    clocks = <&syscon ASPEED_CLK_SDIO>;
+            };
+
+            sdhci1: sdhci@200 {
+                    compatible = "aspeed,ast2500-sdhci";
+                    reg = <0x200 0x100>;
+                    interrupts = <26>;
+                    sdhci,auto-cmd12;
+                    clocks = <&syscon ASPEED_CLK_SDIO>;
+            };
+    };
-- 
2.20.1

^ permalink raw reply related

* [PATCH v3 0/2] mmc: Add support for the ASPEED SD controller
From: Andrew Jeffery @ 2019-07-30  6:23 UTC (permalink / raw)
  To: linux-mmc
  Cc: Andrew Jeffery, ulf.hansson, robh+dt, mark.rutland, joel,
	adrian.hunter, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, ryanchen.aspeed

Hello,

v3 of the ASPEED SDHCI driver makes a bunch of fixes to the driver and the
devicetree binding, including the addition of the AST2600 compatible string.

v2 can be found here:

https://lists.ozlabs.org/pipermail/linux-aspeed/2019-July/002013.html

Please review!

Andrew

Andrew Jeffery (2):
  dt-bindings: mmc: Document Aspeed SD controller
  mmc: Add support for the ASPEED SD controller

 .../devicetree/bindings/mmc/aspeed,sdhci.yaml | 100 ++++++
 drivers/mmc/host/Kconfig                      |  12 +
 drivers/mmc/host/Makefile                     |   1 +
 drivers/mmc/host/sdhci-of-aspeed.c            | 328 ++++++++++++++++++
 4 files changed, 441 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml
 create mode 100644 drivers/mmc/host/sdhci-of-aspeed.c

-- 
2.20.1

^ permalink raw reply

* [PATCH] ARM: dts: aspeed: Add Mihawk BMC platform
From: Ben Pai @ 2019-07-30  6:00 UTC (permalink / raw)
  To: robh+dt, mark.rutland, joel, andrew, devicetree, linux-arm-kernel,
	linux-aspeed, linux-kernel
  Cc: wangat, Ben Pai

The Mihawk BMC is an ASPEED ast2500 based BMC that is part of an
OpenPower Power9 server.

This adds the device tree description for most upstream components. It
is a squashed commit from the OpenBMC kernel tree.

Signed-off-by: Ben Pai <Ben_Pai@wistron.com>
---
 arch/arm/boot/dts/Makefile                  |   1 +
 arch/arm/boot/dts/aspeed-bmc-opp-mihawk.dts | 922 ++++++++++++++++++++
 2 files changed, 923 insertions(+)
 create mode 100755 arch/arm/boot/dts/aspeed-bmc-opp-mihawk.dts

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index eb6de52c1936..262345544359 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -1281,5 +1281,6 @@ dtb-$(CONFIG_ARCH_ASPEED) += \
 	aspeed-bmc-opp-vesnin.dtb \
 	aspeed-bmc-opp-witherspoon.dtb \
 	aspeed-bmc-opp-zaius.dtb \
+	aspeed-bmc-opp-mihawk.dtb \
 	aspeed-bmc-portwell-neptune.dtb \
 	aspeed-bmc-quanta-q71l.dtb
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-mihawk.dts b/arch/arm/boot/dts/aspeed-bmc-opp-mihawk.dts
new file mode 100755
index 000000000000..cfa20e0b2939
--- /dev/null
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-mihawk.dts
@@ -0,0 +1,922 @@
+/dts-v1/;
+
+#include "aspeed-g5.dtsi"
+#include <dt-bindings/gpio/aspeed-gpio.h>
+#include <dt-bindings/leds/leds-pca955x.h>
+
+/ {
+	model = "Mihawk BMC";
+	compatible = "ibm,mihawk-bmc", "aspeed,ast2500";
+
+
+	chosen {
+		stdout-path = &uart5;
+		bootargs = "console=ttyS4,115200 earlyprintk";
+	};
+
+	memory@80000000 {
+		reg = <0x80000000 0x20000000>; /* address and size of RAM(512MB) */
+	};
+
+	reserved-memory {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		flash_memory: region@98000000 {
+			no-map;
+			reg = <0x98000000 0x04000000>; /* 64M */
+		};
+
+		gfx_memory: framebuffer {
+			size = <0x01000000>;
+			alignment = <0x01000000>;
+			compatible = "shared-dma-pool";
+			reusable;
+		};
+
+		video_engine_memory: jpegbuffer {
+			size = <0x02000000>;	/* 32MM */
+			alignment = <0x01000000>;
+			compatible = "shared-dma-pool";
+			reusable;
+		};
+	};
+
+	gpio-keys {
+		compatible = "gpio-keys";
+
+		air-water {
+			label = "air-water";
+			gpios = <&gpio ASPEED_GPIO(F, 6) GPIO_ACTIVE_LOW>;
+			linux,code = <ASPEED_GPIO(F, 6)>;
+		};
+
+		checkstop {
+			label = "checkstop";
+			gpios = <&gpio ASPEED_GPIO(J, 2) GPIO_ACTIVE_LOW>;
+			linux,code = <ASPEED_GPIO(J, 2)>;
+		};
+
+		ps0-presence {
+			label = "ps0-presence";
+			gpios = <&gpio ASPEED_GPIO(Z, 2) GPIO_ACTIVE_LOW>;
+			linux,code = <ASPEED_GPIO(Z, 2)>;
+		};
+
+		ps1-presence {
+			label = "ps1-presence";
+			gpios = <&gpio ASPEED_GPIO(Z, 0) GPIO_ACTIVE_LOW>;
+			linux,code = <ASPEED_GPIO(Z, 0)>;
+		};
+		id-button {
+			label = "id-button";
+			gpios = <&gpio ASPEED_GPIO(F, 1) GPIO_ACTIVE_LOW>;
+			linux,code = <ASPEED_GPIO(F, 1)>;
+		};
+	};
+
+	gpio-keys-polled {
+		compatible = "gpio-keys-polled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		poll-interval = <1000>;
+
+		fan0-presence {
+			label = "fan0-presence";
+			gpios = <&pca9552 9 GPIO_ACTIVE_LOW>;
+			linux,code = <9>;
+		};
+
+		fan1-presence {
+			label = "fan1-presence";
+			gpios = <&pca9552 10 GPIO_ACTIVE_LOW>;
+			linux,code = <10>;
+		};
+
+		fan2-presence {
+			label = "fan2-presence";
+			gpios = <&pca9552 11 GPIO_ACTIVE_LOW>;
+			linux,code = <11>;
+		};
+
+		fan3-presence {
+			label = "fan3-presence";
+			gpios = <&pca9552 12 GPIO_ACTIVE_LOW>;
+			linux,code = <12>;
+		};
+
+		fan4-presence {
+			label = "fan4-presence";
+			gpios = <&pca9552 13 GPIO_ACTIVE_LOW>;
+			linux,code = <13>;
+		};
+
+		fan5-presence {
+			label = "fan5-presence";
+			gpios = <&pca9552 14 GPIO_ACTIVE_LOW>;
+			linux,code = <14>;
+		};
+	};
+
+	leds {
+		compatible = "gpio-leds";
+
+		fault {
+			retain-state-shutdown;
+			default-state = "keep";
+			gpios = <&gpio ASPEED_GPIO(AA, 0) GPIO_ACTIVE_LOW>;
+		};
+
+		power {
+			retain-state-shutdown;
+			default-state = "keep";
+			gpios = <&gpio ASPEED_GPIO(AA, 1) GPIO_ACTIVE_LOW>;
+		};
+
+		rear-id {
+			retain-state-shutdown;
+			default-state = "keep";
+			gpios = <&gpio ASPEED_GPIO(AA, 2) GPIO_ACTIVE_LOW>;
+		};
+
+		rear-g {
+			retain-state-shutdown;
+			default-state = "keep";
+			gpios = <&gpio ASPEED_GPIO(AA, 4) GPIO_ACTIVE_LOW>;
+		};
+
+		rear-ok {
+			retain-state-shutdown;
+			default-state = "keep";
+			gpios = <&gpio ASPEED_GPIO(Y, 0) GPIO_ACTIVE_LOW>;
+		};
+
+		fan0 {
+			retain-state-shutdown;
+			default-state = "keep";
+			gpios = <&pca9552 0 GPIO_ACTIVE_LOW>;
+		};
+
+		fan1 {
+			retain-state-shutdown;
+			default-state = "keep";
+			gpios = <&pca9552 1 GPIO_ACTIVE_LOW>;
+		};
+
+		fan2 {
+			retain-state-shutdown;
+			default-state = "keep";
+			gpios = <&pca9552 2 GPIO_ACTIVE_LOW>;
+		};
+
+		fan3 {
+			retain-state-shutdown;
+			default-state = "keep";
+			gpios = <&pca9552 3 GPIO_ACTIVE_LOW>;
+		};
+
+		fan4 {
+			retain-state-shutdown;
+			default-state = "keep";
+			gpios = <&pca9552 4 GPIO_ACTIVE_LOW>;
+		};
+
+		fan5 {
+			retain-state-shutdown;
+			default-state = "keep";
+			gpios = <&pca9552 5 GPIO_ACTIVE_LOW>;
+		};
+	};
+
+	fsi: gpio-fsi {
+		compatible = "fsi-master-gpio", "fsi-master";
+		#address-cells = <2>;
+		#size-cells = <0>;
+		no-gpio-delays;
+
+		clock-gpios = <&gpio ASPEED_GPIO(E, 6) GPIO_ACTIVE_HIGH>;
+		data-gpios = <&gpio ASPEED_GPIO(E, 7) GPIO_ACTIVE_HIGH>;
+		mux-gpios = <&gpio ASPEED_GPIO(E, 5) GPIO_ACTIVE_HIGH>;
+		enable-gpios = <&gpio ASPEED_GPIO(D, 0) GPIO_ACTIVE_HIGH>;
+		trans-gpios = <&gpio ASPEED_GPIO(R, 2) GPIO_ACTIVE_HIGH>;
+	};
+	iio-hwmon-12v {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 0>;
+	};
+	
+	iio-hwmon-5v {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 1>;
+	};
+	
+	iio-hwmon-3v {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 2>;
+	};
+		
+	iio-hwmon-vdd0 {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 3>;
+	};
+	
+	iio-hwmon-vdd1 {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 4>;
+	};
+	
+	iio-hwmon-vcs0 {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 5>;
+	};
+	
+	iio-hwmon-vcs1 {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 6>;
+	};
+
+	iio-hwmon-vdn0 {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 7>;
+	};
+	
+	iio-hwmon-vdn1 {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 8>;
+	};
+	
+	iio-hwmon-vio0 {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 9>;
+	};
+	
+	iio-hwmon-vio1 {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 10>;
+	};
+	
+	iio-hwmon-vddra {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 11>;
+	};
+	
+	iio-hwmon-vddrb {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 13>;
+	};
+	
+	iio-hwmon-vddrc {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 14>;
+	};
+	
+	iio-hwmon-vddrd {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 15>;
+	};
+	
+	iio-hwmon-battery {
+		compatible = "iio-hwmon";
+		io-channels = <&adc 12>;
+	};
+};
+
+&pwm_tacho {
+	status = "okay";
+	/*compatible = "aspeed,ast2500-pwm-tacho";
+	#address-cells = <1>;
+	#size-cells = <1>;
+	reg = <0x1e786000 0x1000>;
+	clocks = <&pwm_tacho_fixed_clk>;*/
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_pwm0_default &pinctrl_pwm1_default
+		&pinctrl_pwm2_default &pinctrl_pwm3_default
+		&pinctrl_pwm4_default &pinctrl_pwm5_default>;
+
+	fan@0 {
+		reg = <0x00>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x00>;
+	};
+
+	fan@1 {
+		reg = <0x01>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x01>;
+	};
+
+	fan@2 {
+		reg = <0x02>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x02>;
+	};
+
+	fan@3 {
+		reg = <0x03>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x03>;
+	};
+
+	fan@4 {
+		reg = <0x04>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x04>;
+	};
+
+	fan@5 {
+		reg = <0x05>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x05>;
+	};
+
+	fan@6 {
+		reg = <0x00>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x06>;
+	};
+
+	fan@7 {
+		reg = <0x01>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x07>;
+	};
+
+	fan@8 {
+		reg = <0x02>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x08>;
+	};
+
+	fan@9 {
+		reg = <0x03>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x09>;
+	};
+
+	fan@10 {
+		reg = <0x04>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x0a>;
+	};
+
+	fan@11 {
+		reg = <0x05>;
+		aspeed,fan-tach-ch = /bits/ 8 <0x0b>;
+	};
+};
+
+&fmc {
+	status = "okay";
+	flash@0 {
+		status = "okay";
+		label = "bmc";
+		m25p,fast-read;
+		spi-max-frequency = <50000000>;
+		partitions {
+			#address-cells = < 1 >;
+			#size-cells = < 1 >;
+			compatible = "fixed-partitions";
+			u-boot@0 {
+				reg = < 0 0x60000 >;
+				label = "u-boot";
+			};
+			u-boot-env@60000 {
+				reg = < 0x60000 0x20000 >;
+				label = "u-boot-env";
+			};
+			obmc-ubi@80000 {
+				reg = < 0x80000 0x1F80000 >;
+				label = "obmc-ubi";
+			};
+		};
+	};
+	flash@1 {
+		status = "okay";
+		label = "alt-bmc";
+		m25p,fast-read;
+		spi-max-frequency = <50000000>;
+		partitions {
+			#address-cells = < 1 >;
+			#size-cells = < 1 >;
+			compatible = "fixed-partitions";
+			u-boot@0 {
+				reg = < 0 0x60000 >;
+				label = "alt-u-boot";
+			};
+			u-boot-env@60000 {
+				reg = < 0x60000 0x20000 >;
+				label = "alt-u-boot-env";
+			};
+			obmc-ubi@80000 {
+				reg = < 0x80000 0x1F80000 >;
+				label = "alt-obmc-ubi";
+			};
+		};
+	};
+};
+
+&spi1 {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_spi1_default>;
+
+	flash@0 {
+		status = "okay";
+		label = "pnor";
+		m25p,fast-read;
+		spi-max-frequency = <100000000>;
+	};
+};
+
+&lpc_ctrl {
+	status = "okay";
+	memory-region = <&flash_memory>;
+	flash = <&spi1>;
+};
+
+&uart1 {
+	/* Rear RS-232 connector */
+	status = "okay";
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_txd1_default
+			&pinctrl_rxd1_default
+			&pinctrl_nrts1_default
+			&pinctrl_ndtr1_default
+			&pinctrl_ndsr1_default
+			&pinctrl_ncts1_default
+			&pinctrl_ndcd1_default
+			&pinctrl_nri1_default>;
+};
+
+&uart2 {
+	/* APSS */
+	status = "okay";
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_txd2_default &pinctrl_rxd2_default>;
+};
+
+&uart5 {
+	status = "okay";
+};
+
+&mac0 {
+	status = "okay";
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_rmii1_default>;
+	use-ncsi;
+};
+
+&mac1 {
+	status = "okay";
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_rgmii2_default &pinctrl_mdio2_default>;
+};
+
+&i2c0 {
+	status = "disabled";
+};
+
+&i2c1 {
+	status = "disabled";
+};
+
+&i2c2 {
+	status = "okay";
+
+	/* SAMTEC P0 */
+	/* SAMTEC P1 */
+	
+};
+
+&i2c3 {
+	status = "okay";
+
+	/* APSS */
+	/* CPLD */
+
+	/* PCA9516 (repeater) ->
+	 *    CLK Buffer 9FGS9092
+	 *    CLK Buffer 9DBL0651BKILFT
+	 *    CLK Buffer 9DBL0651BKILFT
+	 *    Power Supply 0
+	 *    Power Supply 1
+	 *    PCA 9552 LED
+	 */
+	 
+	power-supply@58 {
+		compatible = "ibm,cffps1";
+		reg = <0x58>;
+	};
+
+	power-supply@5b {
+		compatible = "ibm,cffps1";
+		reg = <0x5b>;
+	};
+
+	pca9552: pca9552@60 {
+		compatible = "nxp,pca9552";
+		reg = <0x60>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		gpio-controller;
+		#gpio-cells = <2>;
+
+		gpio@0 {
+			reg = <0>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@1 {
+			reg = <1>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@2 {
+			reg = <2>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@3 {
+			reg = <3>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@4 {
+			reg = <4>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@5 {
+			reg = <5>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@6 {
+			reg = <6>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@7 {
+			reg = <7>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@8 {
+			reg = <8>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@9 {
+			reg = <9>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@10 {
+			reg = <10>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@11 {
+			reg = <11>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@12 {
+			reg = <12>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@13 {
+			reg = <13>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@14 {
+			reg = <14>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+		gpio@15 {
+			reg = <15>;
+			type = <PCA955X_TYPE_GPIO>;
+		};
+
+	};
+
+};
+
+&i2c4 {
+	status = "okay";
+
+	/* CP0 VDD & VCS : IR35221 */
+	/* CP0 VDN : IR35221 */
+	/* CP0 VIO : IR38064 */
+        /* CP0 VDDR : PXM1330 */
+
+	ir35221@70 {
+		compatible = "infineon,ir35221";
+		reg = <0x70>;
+	};
+
+	ir35221@72 {
+		compatible = "infineon,ir35221";
+		reg = <0x72>;
+	};
+
+};
+
+&i2c5 {
+	status = "okay";
+	
+	/* CP0 VDD & VCS : IR35221 */
+	/* CP0 VDN : IR35221 */
+	/* CP0 VIO : IR38064 */
+        /* CP0 VDDR : PXM1330 */
+
+	ir35221@70 {
+		compatible = "infineon,ir35221";
+		reg = <0x70>;
+	};
+
+	ir35221@72 {
+		compatible = "infineon,ir35221";
+		reg = <0x72>;
+	};
+	
+};
+
+&i2c6 {
+	status = "okay";
+	
+	/* pca9548 -> NVMe1 to 8 */
+	
+	pca9548@70 {
+		compatible = "nxp,pca9548";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x70>;
+	};
+	
+};
+
+&i2c7 {
+	status = "okay";
+	
+	/* pca9548 -> NVMe9 to 16 */
+	
+	pca9548@70 {
+		compatible = "nxp,pca9548";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x70>;
+	};
+	
+};
+
+&i2c8 {
+	status = "okay";
+
+	/* FSI CLK/DAT */
+	eeprom@50 {
+		compatible = "atmel,24c64";
+		reg = <0x50>;
+	};
+};
+
+&i2c9 {
+	status = "okay";
+	
+	/* pca9545 Riser -> 
+	* 	PCIe x8  Slot3 
+	* 	PCIe x16 slot4 
+	* 	PCIe x8  slot5 
+	* 	I2C BMC RISER PCA9554
+	* 	BMC SCL/SDA PCA9554 
+	* 	PCA9554
+	*/
+	
+	/* pca9545 -> 
+	* 	PCIe x16 Slot1 
+	* 	PCIe x8  slot2 
+	* 	PEX8748 
+	*/
+
+	pca9545riser@70 {
+		compatible = "nxp,pca9545";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x70>;
+
+		/*interrupt-parent = <&ipic>;*/
+		/*interrupts = <17 IRQ_TYPE_LEVEL_LOW>;*/
+		i2c-mux-idle-disconnect;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+	};
+	
+	pca9545@71 {
+		compatible = "nxp,pca9545";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x71>;
+
+		/*interrupt-parent = <&ipic>;*/
+		/*interrupts = <17 IRQ_TYPE_LEVEL_LOW>;*/
+		i2c-mux-idle-disconnect;
+		interrupt-controller;
+		#interrupt-cells = <2>;	
+	};
+};
+
+&i2c10 {
+	status = "okay";
+	
+	/* pca9545 Riser -> 
+	* 	PCIe x8  Slot8 
+	* 	PCIe x16 slot9 
+	* 	PCIe x8  slot10 
+	* 	I2C BMC RISER PCA9554
+	* 	BMC SCL/SDA PCA9554 
+	* 	PCA9554
+	*/
+	
+	/* pca9545 -> 
+	* 	PCIe x16 Slot1 
+	* 	PCIe x8  slot2 
+	* 	PEX8748 
+	*/
+	
+	pca9545riser@70 {
+		compatible = "nxp,pca9545";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x70>;
+
+		/*interrupt-parent = <&ipic>;*/
+		/*interrupts = <17 IRQ_TYPE_LEVEL_LOW>;*/
+		i2c-mux-idle-disconnect;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+	};
+	
+	pca9545@71 {
+		compatible = "nxp,pca9545";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x71>;
+
+		/*interrupt-parent = <&ipic>;*/
+		/*interrupts = <17 IRQ_TYPE_LEVEL_LOW>;*/
+		i2c-mux-idle-disconnect;
+		interrupt-controller;
+		#interrupt-cells = <2>;	
+	};
+};
+
+&i2c11 {
+	status = "okay";
+	
+	/* TPM */
+	/* RTC RX8900CE */
+	/* FPGA for power sequence */
+	/* TMP275A */
+	/* TMP275A */
+	/* EMC1462 */
+
+	tpm@57 {
+		compatible = "infineon,slb9645tt";
+		reg = <0x57>;
+	};
+	
+	rtc@32 {
+		compatible = "epson,rx8900";
+		reg = <0x32>;
+	};
+	
+	tmp275@48 {
+		compatible = "ti,tmp275";
+		reg = <0x48>;
+	};
+	
+	tmp275@49 {
+		compatible = "ti,tmp275";
+		reg = <0x49>;
+	};
+
+    /* chip emc1462 use emc1403 driver */
+    emc1403@4c {
+        compatible = "smsc,emc1403";
+        reg = <0x4c>;
+    };
+
+};
+
+&i2c12 {
+	status = "okay";
+
+	/* pca9545 ->
+	*	SAS BP1
+	*	SAS BP2
+	*	NVMe BP
+	*	M.2 riser
+	*/
+	
+	pca9545@70 {
+		compatible = "nxp,pca9545";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x70>;
+
+		/*interrupt-parent = <&ipic>;*/
+		/*interrupts = <17 IRQ_TYPE_LEVEL_LOW>;*/
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		
+		i2c@0 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0>;
+			
+			eeprom@50 {
+				compatible = "atmel,24c64";
+				reg = <0x50>;
+			};
+		};
+		
+		i2c@1 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <1>;
+			
+			eeprom@50 {
+				compatible = "atmel,24c64";
+				reg = <0x50>;
+			};
+		};
+		
+		i2c@2 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <2>;
+			
+			eeprom@50 {
+				compatible = "atmel,24c64";
+				reg = <0x50>;
+			};
+		};
+		
+		i2c@3 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <3>;
+			
+			tmp275@48 {
+				compatible = "ti,tmp275";
+				reg = <0x48>;
+			};
+		};
+		
+	};
+	
+};
+
+&i2c13 {
+	status = "okay";
+	
+	/* pca9548 ->
+	*	NVMe BP
+	*	NVMe HDD17 to 24
+	*/
+	
+	pca9548@70 {
+		compatible = "nxp,pca9548";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x70>;
+	};	
+};
+
+&vuart {
+	status = "okay";
+};
+
+&gfx {
+	status = "okay";
+	memory-region = <&gfx_memory>;
+};
+
+&pinctrl {
+	aspeed,external-nodes = <&gfx &lhc>;
+};
+
+&adc {
+	status = "okay";
+};
+
+&wdt1 {
+	aspeed,reset-type = "none";
+	aspeed,external-signal;
+	aspeed,ext-push-pull;
+	aspeed,ext-active-high;
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_wdtrst1_default>;
+};
+
+&wdt2 {
+	aspeed,alt-boot;
+};
+
+&ibt {
+	status = "okay";
+};
+
+&vhub {
+	status = "okay";
+};
+
+&video {
+	status = "okay";
+	memory-region = <&video_engine_memory>;
+};
+
+#include "ibm-power9-dual.dtsi"
\ No newline at end of file
-- 
2.17.1

^ permalink raw reply related

* Re: [PATCH v4 0/3] Introduce Bandwidth OPPs for interconnects
From: Saravana Kannan @ 2019-07-30  5:53 UTC (permalink / raw)
  To: Sibi Sankar
  Cc: Viresh Kumar, Georgi Djakov, Rob Herring, Mark Rutland,
	Viresh Kumar, Nishanth Menon, Stephen Boyd, Rafael J. Wysocki,
	Vincent Guittot, Sweeney, Sean, David Dai, adharmap,
	Rajendra Nayak, Bjorn Andersson, Evan Green, Android Kernel Team,
	Linux PM,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS, LKML
In-Reply-To: <361effba-4433-24d9-243c-201af39214cc@codeaurora.org>

On Mon, Jul 29, 2019 at 10:28 PM Sibi Sankar <sibis@codeaurora.org> wrote:
>
> Hey Viresh,
>
> On 7/30/19 8:16 AM, Viresh Kumar wrote:
> > On 29-07-19, 13:16, Saravana Kannan wrote:
> >> Sibi might be working on doing that for the SDM845 CPUfreq driver.
> >> Georgi could also change his GPU driver use case to use this BW OPP
> >> table and required-opps.
> >>
> >> The problem is that people don't want to start using this until we
> >> decide on the DT representation. So it's like a chicken and egg
> >> situation.
> >
> > Yeah, I agree to that.
> >
> > @Georgi and @Sibi: This is your chance to speak up about the proposal
> > from Saravana and if you find anything wrong with them. And specially
> > that it is mostly about interconnects here, I would like to have an
> > explicit Ack from Georgi on this.
> >
> > And if you guys are all okay about this then please at least commit
> > that you will convert your stuff based on this in coming days.
>
> I've been using both Saravana's and Georgi's series for a while
> now to scale DDR and L3 on SDM845. There is currently no consensus
> as to where the votes are to be actuated from, hence couldn't post
> anything out.
>
> DCVS based on Saravana's series + passive governor:
> https://github.com/QuinAsura/linux/tree/lnext-072619-SK-series

Thanks Sibi! You might want to convert your patches so that until the
passive governor is ready, you just look up the required opps and vote
for BW directly from the cpufreq driver. Once devfreq governor is
ready, you can switch to it.

-Saravana

>
> DCVS based on Georgi's series: (I had already posted this out)
> https://github.com/QuinAsura/linux/tree/lnext-072619-GJ-series
>
> --
> Qualcomm Innovation Center, Inc.
> Qualcomm Innovation Center, Inc, is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project

^ permalink raw reply

* Re: [PATCH v4 0/3] Introduce Bandwidth OPPs for interconnects
From: Sibi Sankar @ 2019-07-30  5:28 UTC (permalink / raw)
  To: Viresh Kumar, Saravana Kannan, Georgi Djakov
  Cc: Rob Herring, Mark Rutland, Viresh Kumar, Nishanth Menon,
	Stephen Boyd, Rafael J. Wysocki, Vincent Guittot, Sweeney, Sean,
	David Dai, adharmap, Rajendra Nayak, Bjorn Andersson, Evan Green,
	Android Kernel Team, Linux PM,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS, LKML
In-Reply-To: <20190730024640.xk27jgdfl2j6ucx7@vireshk-i7>

Hey Viresh,

On 7/30/19 8:16 AM, Viresh Kumar wrote:
> On 29-07-19, 13:16, Saravana Kannan wrote:
>> Sibi might be working on doing that for the SDM845 CPUfreq driver.
>> Georgi could also change his GPU driver use case to use this BW OPP
>> table and required-opps.
>>
>> The problem is that people don't want to start using this until we
>> decide on the DT representation. So it's like a chicken and egg
>> situation.
> 
> Yeah, I agree to that.
> 
> @Georgi and @Sibi: This is your chance to speak up about the proposal
> from Saravana and if you find anything wrong with them. And specially
> that it is mostly about interconnects here, I would like to have an
> explicit Ack from Georgi on this.
> 
> And if you guys are all okay about this then please at least commit
> that you will convert your stuff based on this in coming days.

I've been using both Saravana's and Georgi's series for a while
now to scale DDR and L3 on SDM845. There is currently no consensus
as to where the votes are to be actuated from, hence couldn't post
anything out.

DCVS based on Saravana's series + passive governor:
https://github.com/QuinAsura/linux/tree/lnext-072619-SK-series

DCVS based on Georgi's series: (I had already posted this out)
https://github.com/QuinAsura/linux/tree/lnext-072619-GJ-series

-- 
Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc, is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply

* Re: [PATCH 0/4] new driver for TI eQEP
From: William Breathitt Gray @ 2019-07-30  4:45 UTC (permalink / raw)
  To: David Lechner
  Cc: linux-iio, linux-omap, devicetree, Rob Herring, Mark Rutland,
	Benoît Cousson, Tony Lindgren, Thierry Reding, linux-kernel,
	linux-pwm
In-Reply-To: <4616508c-d753-586d-0d3b-5a003e86f582@lechnology.com>

On Thu, Jul 25, 2019 at 05:52:21PM -0500, David Lechner wrote:
> On 7/25/19 7:40 AM, William Breathitt Gray wrote:
> > On Mon, Jul 22, 2019 at 10:45:34AM -0500, David Lechner wrote:
> >> This series adds device tree bindings and a new counter driver for the Texas
> >> Instruments Enhanced Quadrature Encoder Pulse (eQEP).
> >>
> >> As mentioned in one of the commit messages, to start with, the driver only
> >> supports reading the current counter value and setting the min/max values.
> >> Other features can be added on an as-needed basis.
> >>
> >> The only other feature I am interested in is adding is getting time data in
> >> order to calculate the rotational speed of a motor. However, there probably
> >> needs to be a higher level discussion of how this can fit into the counter
> >> subsystem in general first.
> > 
> > I believe exposing some sort of time data has merit. Quadrature counter
> > devices in particular are commonly used for position tracking of
> > automation systems, and such systems would benefit from velocity/speed
> > information. So let's try to introduce that sort of functionality in this
> > driver if possible.
> > 
> > First, let's discuss your specific use case and requirements, and hopefully we
> > can generalize it enough to be of use for future drivers. From your description,
> > it sounds like you're attaching some sort of rotary encoder to the eQEP device.
> > Is that correct? What sort of time data are you hoping to use; does the eQEP
> > device provide a clock value, or would you be grabbing a timestamp from the
> > system?
> 
> My use case is robotics using LEGO MINDSTORMS. More specifically, I am using
> motors that have a cheap optical rotary encoder (plastic wheel and infrared
> LED/detectors) that give 360 counts per 1 rotation of the motor shaft. One count
> is defined as the rising edge or falling edge of the A signal. We are looking at
> anywhere from 0 to around 2000 counts per second. We use the speed as feedback in
> a control algorithm to drive the motor at a constant speed. The control loop
> updates on the order of 1 to 10 ms.
> 
> Because the encoder resolution and speeds are relatively low, we are currently
> logging a timestamp for each count. If no count occurs for 50ms, then we log the
> same count again with a new timestamp (otherwise we would never see 0 speed). To
> get the actual speed, we find the first timestamp > 20 ms before the current
> timestamp then compute the speed as the change in position divided by the change
> in time between these two samples. This give a fairly accurate speed across most
> of the range, but does get a bit noisy once we get below 100 counts per second.
> It also means that we need a ring buffer that holds about 50 samples.
> 
> The timestamp itself comes from the eQEP, not the system. There are latching
> registers to ensure that the timestamp read is from exactly the moment when
> the count register was read.

So if I understand correctly, there are two registers you're reading: a
count register and a timestamp register. The count register is updated
by the rotation of the motor shaft, while the timestamp register is
updated by reading the count register (thus logging the time associated
with the read count value).

> > I'm not sure yet if it would make sense to expose rotational speed directly as
> > an attribute. If we were to expose just the count value and timestamp since the
> > last read, that should be enough for a user to compute the delta and derive
> > speed. I'll think more about this since some devices may simplify that case if
> > the hardware is able to compute the speed for us.
> > 
> 
> I agree that it probably doesn't make sense to expect drivers to compute the
> speed. There isn't really a general way to do that works for an arbitrary
> speed. For example at high speeds, it is better to just look at the change
> in counts over a fixed interval rather than triggering a timestamp based on
> a certain number of counts.

This is a good point. Depending on the resolution the user cares about,
they may be more interested in the speed over a short time interval
versus a long time interval. It doesn't seem practical to have the driver
try to handle all possible speed calculations when the user can decide
themselves how best to use the data.

> I also don't think having a timestamp sysfs attribute would be very useful.
> To make it work at all, I think it would have to be implemented such that
> it returns the timestamp for the count that was most recently read via sysfs.
> And it would require 4 syscalls (2 seeks and 2 reads) to get a single count/
> timestamp pair in a control loop. On a 300MHz ARM9 processor, this is not
> a negligible amount of time.

This is a concern I've had as well. The sysfs interface is useful in
that it provides an intuitive and human-friendly way to expose data
about devices. But as you note, there is considerable overhead in the
amount of syscalls we have to make to interact with multiple attributes.

One solution that may work is providing a character device interface in
addition to the sysfs interface. I believe that should reduce the
syscall overhead since a user can pass in a data structure with a
configuration defining what data/actions they want, and receive back
all data in a single syscall.

I think concern over latency was one of the reasons the GPIO subsystem
gained a character device interface as well. It's an addition to the
Counter subsystem that is worth considering, but the possible downsides
to such an interface should also be investigated.
 
> I noticed that several of the other counter drivers also register an IIO
> device. So this got me thinking that perhaps the counter subsystem should
> just be for configuring a counter device an then the IIO subsystem should
> be used for triggers and ring buffers.
> 
> For the general case a counter device could have two possible triggers.
> One that triggers an interrupt after X counts and another that triggers
> with a period of T nanoseconds (or microseconds). Both triggers would add
> a count/timestamp pair to an IIO ring buffer.
> 
> To fully reproduce our current methodology the first trigger would actually
> need two configurable settings, the count X that triggers every X counts and
> a watchdog time setting (using terminology from eQEP docs) that will also
> trigger if and only if the count does not change before the time has elapsed.
> Note, this is different from the other proposed time based trigger which
> would cause a trigger interrupt at a fixed period regardless of whether
> the count changed or not.

The counter drivers in the kernel right now are registering IIO devices
in order to keep the preexisting (but now deprecated) IIO Counter
interface working for these devices -- some users may be using this
older interface so we don't want to remove it cold turkey. Regardless,
there's nothing the prevents incorporating the IIO interface with your
Counter drivers; in fact, in some circumstances it's better that you do
just that.

The key idea to recognize is how the Counter subsystem differs from the
IIO subsystem on a conceptual level: the IIO subsystem provides an
interface for your device by describing it on a hardware level, whereas
the Counter subsystem provides an interface for your device by
describing it on a more abstract level.

What I mean is that every interface interaction in the Counter subsystem
relates to the abstract concept of an ideal "counter device" (Counts,
Synapses, Signals); if a device functionality or data does not relate
directly to those ideal counter device components, then the Counter
subsystem isn't that right interface for it.

For example, it makes sense to have an "enable" attribute or "present"
attribute, because these functionalities/data are directly related to
the Count, Synapse, and Signal components conceptually. However, in the
Counter subsystem you will likely not see something like the IIO
"in_voltageY_supply_raw" attribute -- not because that data is not
useful to know about for the operation of the counter device hardware,
but because it is outside the scope of the Counter subsystem paradigm
(i.e. it does not directly related to Counts, Synapses, or Signals).
As such, this would be a case where the counter driver should register
both a Counter device and IIO device, one to handle the counter device
on an abstract level while the other provides an interface for control
of the more specific hardware details.

> ---
> 
> Thinking more generally though, I think what I would propose is adding a new
> component to the existing list of Count, Signal and Synapse. The new component
> could be called Event. Event would be more general than the trigger conditions
> I have just discussed. In addition to those two, it could be any event
> generated by the hardware, such as an error condition or a change in direction.
> 
> Drivers could register an arbitrary number of events for each Count, so we
> would have /sys/bus/counter/devices/counterX/eventY/*. There should be a few
> standard attributes, like "name" and "enable". Configurable events would need
> ext attributes to allow configuration.
> 
> However, I see that there are already preset and error_noise "events" for
> count objects, so maybe we don't do the eventY thing and keep it flatter (or
> is the counter subsystem still considered in "staging" where breaking ABI
> changes could be made?).

The components for handling events already exist in the Counter
interface paradigm: Signals and Synapses. Although, the Counter
subsystem is currently lacking the implementation (I still need to code
in support for interrupts and such), the paradigm itself supports the
concept of events and triggers.

Recall that the Counter subsystem represents hardware via the
abstraction of an idealized "counter device". This is important to
understand because it means that Signals are not necessarily limited to
the physical wires of the hardware. To summarize the Counter interface
paradigm:

    * A Signal is a stream of data to be evaluated.
    * A Synapse is a trigger condition based on the evaluation of the
      data streams (i.e. the Signals).
    * A Count is the accumulation of the effects of Synapses (i.e. the
      triggers).

As such, in order to represent an event, you would add in a Signal to
represent the stream of device events, and a Synapse defining the
specific event that will trigger the action. I'll give an example in
order to demonstrate what I mean.

A simple clock can be conceptualize as a proper counter device: an
oscillation is a Signal, a rising edge from that oscillation line can be
the Synapse, and the current clock value is the Count.

                Count                Synapse          Signal
                -----                -------          ------
        +---------------------+
        | Data: Clock ticks   |    Rising Edge     _____________
        | Function: Increase  |  <-------------   / Oscillation \
        |                     |                  _________________
        +---------------------+

Now, in order to represent your timestamping clock we need two Signals:
a simple clock and an event stream. The simple clock is the source of
the current clock ticks we will store, while the event stream provides
the rotation count register read notification that will trigger the
timestamp.

                   Count                       Synapse      Signal
                   -----                       -------      ------
        +-------------------------------+
        | Data: Timestamp               |       None        _______
        | Function: Current clock ticks |  <------------   / Clock \
        |                               |                 ___________
        |                               |
        |                               |    Read event     ________
        |                               |  <------------   / Events \
        |                               |                 ____________
        +-------------------------------+

Note that in this case both Signals either do not exist in or are not
exposed by the hardware (maybe the simple clock is exposed, but it's not
necessary to be) -- they are meant to be abstract representations of the
components of the timestamp clock as an idealized "counter device".

By organizing the timestamp clock in this way, we can control and
configure the components using the standard Counter interface: common
attributes such as "name", "preset", "enable", etc. can now be exposed
to users like every other counter device component.

In theory we can sleep on the timestamp count attribute read (or
character device equivalent if we go down that route), and be woken when
an event triggers updating the timestamp value. However, the current
Counter subsystem implementation is lacking the code for this so it
needs to be added to the core functionality first.

> When thinking about what events would actually do when enabled though, it
> seems like we should be using IIO events and triggers (we have found reading
> sysfs attributes to be insufficient performance-wise). It seems like unnecessary
> work to reproduce all of this in the counter subsystem. Which makes me wonder if
> it would be better to have counter devices just be a different device type (i.e.
> different struct device_type for dev->type) in the IIO subsystem instead of
> creating a completely new subsystem.

I plan on adding interrupt support for the 104-QUAD-8 counter driver
since this device has some useful interrupts on configured threshold
conditions and such, so having the ability to handle an event rather
than constantly read and loop is something I want to have in the Counter
subsystem.

It's possible that I can reuse some code from the IIO subsystem, as
Jonathan pointed out, but overall I believe these should be separate
subsystems. From the reasons described above, the IIO subsystem and
Counter subsystem have different goals and thus different
implementations. I don't think that's a bad thing, and we can share code
in the few cases where the two may overlap.

Regarding whether to use IIO events and triggers within the TI eQEP
counter driver, I think we should wait for a proper Counter subsystem
implementation to be added first. My fear is that we'll have a similar
situation as what happened with IIO_COUNT, where we'll have to keep a
IIO interface present with a newer Counter interface. If adding in event
support to the Counter subsystem will take too long, we can add this TI
eQEP driver as-is now and later add in the timestamp support.

William Breathitt Gray

^ permalink raw reply

* RE: [PATCH V2 4/4] thermal: qoriq: Add clock operations
From: Anson Huang @ 2019-07-30  4:33 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: rui.zhang, Eduardo Valentin, Daniel Lezcano, Rob Herring,
	Mark Rutland, linux-pm@vger.kernel.org,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS  <devicetree@vger.kernel.org>, linux-kernel,
	dl-linux-imx
In-Reply-To: <CAOMZO5D33zPEu8zkq-KYvDv4Xzd8OoshSbzOCrBw1hwwJr-qNg@mail.gmail.com>

Hi, Fabio

> On Tue, Jul 30, 2019 at 12:00 AM Anson Huang <anson.huang@nxp.com>
> wrote:
> 
> > Shawn already applied the patch, and Abel has the AHB clock patch to
> > fix that, so just wait for the AHB clock patch in instead of revert the TMU
> clock patch?
> 
> Sorry, I don't understand Abel's patch as there is not a proper description of
> what it tries to fix.
> 
> If I understand correctly Abel's patch is not the proper fix and the real fix for
> the kernel TMU hang in linux-next is to manage the TMU clock inside the
> TMU driver, like you did in this patch.
> 
> To avoid a revert one possible solution would be to send only this one as a
> fix for 5.3. You can point that it Fixes the commit that adds i.MX8M support
> for the TMU driver.

I replied in the patch https://patchwork.kernel.org/patch/11032781/ , I pasted it as below,
The TMU clock is NOT the root cause, but it just accidently trigger this issue:

I can explain why Abel's patch can fix this issue, the AHB clock is a MUST always ON for system bus, while it does NOT have CLK_IS_CRITICAL flag set for now, when SDMA1 is probed, it will enable its clock, and the clk path is sdma1_clk->ipg_root->ahb, after SDMA1 probed done, it will disable its clock since no one use it, and it will trigger the ahb clock to be OFF, as its usecount is added by 1 when probe and decreased by 1 after
SDMA1 probe done, so usecount=0 will trigger AHB clock to be OFF.

So I think the best solution should be applying Abel's patch as you mentioned upper, the TMU clock patch is NOT the root cause, it just triggers this issue accidently☹

But I saw Abel's AHB patch is still under discussion, so I think we need to speed it up and make kernel boot up work for development. AHB/IPG are always critical for i.MX SoCs.

Thanks,
Anson.


^ permalink raw reply

* Re: [PATCH 0/3] Add IMX290 CMOS image sensor support
From: Manivannan Sadhasivam @ 2019-07-30  4:12 UTC (permalink / raw)
  To: mchehab
  Cc: robh+dt, linux-media, linux-kernel, devicetree, linux-arm-kernel,
	c.barrett, a.brela
In-Reply-To: <20190703190230.12392-1-manivannan.sadhasivam@linaro.org>

On Thu, Jul 04, 2019 at 12:32:27AM +0530, Manivannan Sadhasivam wrote:
> Hello,
> 
> This patchset adds support for IMX290 CMOS image sensor from Sony.
> Sensor can be programmed through I2C and 4-wire interface but the
> current driver only supports I2C interface. Also, the sensor is
> capable of outputting frames in following 3 interfaces:
> 
> * CMOS logic parallel SDR output
> * Low voltage LVDS serial DDR output
> * CSI-2 serial data output
> 
> But the current driver only supports CSI-2 output available from 4 lanes.
> In the case of sensor resolution, driver only supports 1920x1080 and
> 1280x720 at mid data rate of 445.5 Mpbs.
> 
> The driver has been validated using Framos IMX290 module interfaced to
> 96Boards Dragonboard410c.
> 

Ping on the patchset!

Thanks,
Mani

> Thanks,
> Mani
> 
> Manivannan Sadhasivam (3):
>   dt-bindings: media: i2c: Add IMX290 CMOS sensor binding
>   media: i2c: Add IMX290 CMOS image sensor driver
>   MAINTAINERS: Add entry for IMX290 CMOS image sensor driver
> 
>  .../devicetree/bindings/media/i2c/imx290.txt  |  51 ++
>  MAINTAINERS                                   |   8 +
>  drivers/media/i2c/Kconfig                     |  11 +
>  drivers/media/i2c/Makefile                    |   1 +
>  drivers/media/i2c/imx290.c                    | 845 ++++++++++++++++++
>  5 files changed, 916 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/imx290.txt
>  create mode 100644 drivers/media/i2c/imx290.c
> 
> -- 
> 2.17.1
> 

^ permalink raw reply

* Re: [PATCH V14 13/13] PCI: tegra: Add Tegra194 PCIe support
From: Vidya Sagar @ 2019-07-30  4:00 UTC (permalink / raw)
  To: lorenzo.pieralisi, bhelgaas
  Cc: robh+dt, mark.rutland, thierry.reding, jonathanh, kishon,
	catalin.marinas, will.deacon, jingoohan1, gustavo.pimentel,
	digetx, mperttunen, linux-pci, devicetree, linux-tegra,
	linux-kernel, linux-arm-kernel, kthota, mmaddireddy, sagar.tv
In-Reply-To: <20190724174824.20933-14-vidyas@nvidia.com>

On 7/24/2019 11:18 PM, Vidya Sagar wrote:

Bjorn / Lorenzo,
Can you please review this change?

Thanks,
Vidya Sagar

> Add support for Synopsys DesignWare core IP based PCIe host controller
> present in Tegra194 SoC.
> 
> Signed-off-by: Vidya Sagar <vidyas@nvidia.com>
> Acked-by: Thierry Reding <treding@nvidia.com>
> ---
> V14:
> * Addressed Lorenzo's review comments
> * Removed unused header files
> * Gathered all ASPM related programming under one define
> * Refactored tegra_pcie_dw_host_init() API to avoid using upward goto statement
> * Started using dw_pcie_wait_for_link() API for link up check
> * Modified condition to call tegra_bpmp_transfer_atomic()/tegra_bpmp_transfer() APIs
> 
> V13:
> * Modified according to modifications in PATCH V13 01/12
> 
> V12:
> * None
> 
> V11:
> * None
> 
> V10:
> * Used _relaxed() versions of readl() & writel()
> 
> V9:
> * Made it dependent on ARCH_TEGRA_194_SOC directly
> 
> V8:
> * Addressed review comments from Thierry
> 
> V7:
> * Removed code around "nvidia,disable-aspm-states" DT property
> * Refactored code to remove code duplication
> 
> V6:
> * Addressed review comments from Thierry
> 
> V5:
> * None
> 
> V4:
> * None
> 
> V3:
> * Changed 'nvidia,init-speed' to 'nvidia,init-link-speed'
> * Changed 'nvidia,pex-wake' to 'nvidia,wake-gpios'
> * Removed .runtime_suspend() & .runtime_resume() implementations
> 
> V2:
> * Made CONFIG_PCIE_TEGRA194 as 'm' by default from its previous 'y' state
> * Modified code as per changes made to DT documentation
> * Refactored code to address Bjorn & Thierry's review comments
> * Added goto to avoid recursion in tegra_pcie_dw_host_init() API
> * Merged .scan_bus() of dw_pcie_host_ops implementation to tegra_pcie_dw_host_init() API
> 
>   drivers/pci/controller/dwc/Kconfig         |   10 +
>   drivers/pci/controller/dwc/Makefile        |    1 +
>   drivers/pci/controller/dwc/pcie-tegra194.c | 1630 ++++++++++++++++++++
>   3 files changed, 1641 insertions(+)
>   create mode 100644 drivers/pci/controller/dwc/pcie-tegra194.c
> 
> diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
> index 6ea778ae4877..49475f5c42c3 100644
> --- a/drivers/pci/controller/dwc/Kconfig
> +++ b/drivers/pci/controller/dwc/Kconfig
> @@ -220,6 +220,16 @@ config PCI_MESON
>   	  and therefore the driver re-uses the DesignWare core functions to
>   	  implement the driver.
>   
> +config PCIE_TEGRA194
> +	tristate "NVIDIA Tegra194 (and later) PCIe controller"
> +	depends on ARCH_TEGRA_194_SOC || COMPILE_TEST
> +	depends on PCI_MSI_IRQ_DOMAIN
> +	select PCIE_DW_HOST
> +	select PHY_TEGRA194_P2U
> +	help
> +	  Say Y here if you want support for DesignWare core based PCIe host
> +	  controller found in NVIDIA Tegra194 SoC.
> +
>   config PCIE_UNIPHIER
>   	bool "Socionext UniPhier PCIe controllers"
>   	depends on ARCH_UNIPHIER || COMPILE_TEST
> diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile
> index b085dfd4fab7..b30336181d46 100644
> --- a/drivers/pci/controller/dwc/Makefile
> +++ b/drivers/pci/controller/dwc/Makefile
> @@ -15,6 +15,7 @@ obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
>   obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o
>   obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o
>   obj-$(CONFIG_PCI_MESON) += pci-meson.o
> +obj-$(CONFIG_PCIE_TEGRA194) += pcie-tegra194.o
>   obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o
>   
>   # The following drivers are for devices that use the generic ACPI
> diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
> new file mode 100644
> index 000000000000..f7d9ecf9e019
> --- /dev/null
> +++ b/drivers/pci/controller/dwc/pcie-tegra194.c
> @@ -0,0 +1,1630 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * PCIe host controller driver for Tegra194 SoC
> + *
> + * Copyright (C) 2019 NVIDIA Corporation.
> + *
> + * Author: Vidya Sagar <vidyas@nvidia.com>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/debugfs.h>
> +#include <linux/delay.h>
> +#include <linux/gpio.h>
> +#include <linux/interrupt.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_pci.h>
> +#include <linux/pci.h>
> +#include <linux/pci-aspm.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/random.h>
> +#include <linux/reset.h>
> +#include <linux/resource.h>
> +#include <linux/types.h>
> +#include "pcie-designware.h"
> +#include <soc/tegra/bpmp.h>
> +#include <soc/tegra/bpmp-abi.h>
> +#include "../../pci.h"
> +
> +#define APPL_PINMUX				0x0
> +#define APPL_PINMUX_PEX_RST			BIT(0)
> +#define APPL_PINMUX_CLKREQ_OVERRIDE_EN		BIT(2)
> +#define APPL_PINMUX_CLKREQ_OVERRIDE		BIT(3)
> +#define APPL_PINMUX_CLK_OUTPUT_IN_OVERRIDE_EN	BIT(4)
> +#define APPL_PINMUX_CLK_OUTPUT_IN_OVERRIDE	BIT(5)
> +#define APPL_PINMUX_CLKREQ_OUT_OVRD_EN		BIT(9)
> +#define APPL_PINMUX_CLKREQ_OUT_OVRD		BIT(10)
> +
> +#define APPL_CTRL				0x4
> +#define APPL_CTRL_SYS_PRE_DET_STATE		BIT(6)
> +#define APPL_CTRL_LTSSM_EN			BIT(7)
> +#define APPL_CTRL_HW_HOT_RST_EN			BIT(20)
> +#define APPL_CTRL_HW_HOT_RST_MODE_MASK		GENMASK(1, 0)
> +#define APPL_CTRL_HW_HOT_RST_MODE_SHIFT		22
> +#define APPL_CTRL_HW_HOT_RST_MODE_IMDT_RST	0x1
> +
> +#define APPL_INTR_EN_L0_0			0x8
> +#define APPL_INTR_EN_L0_0_LINK_STATE_INT_EN	BIT(0)
> +#define APPL_INTR_EN_L0_0_MSI_RCV_INT_EN	BIT(4)
> +#define APPL_INTR_EN_L0_0_INT_INT_EN		BIT(8)
> +#define APPL_INTR_EN_L0_0_CDM_REG_CHK_INT_EN	BIT(19)
> +#define APPL_INTR_EN_L0_0_SYS_INTR_EN		BIT(30)
> +#define APPL_INTR_EN_L0_0_SYS_MSI_INTR_EN	BIT(31)
> +
> +#define APPL_INTR_STATUS_L0			0xC
> +#define APPL_INTR_STATUS_L0_LINK_STATE_INT	BIT(0)
> +#define APPL_INTR_STATUS_L0_INT_INT		BIT(8)
> +#define APPL_INTR_STATUS_L0_CDM_REG_CHK_INT	BIT(18)
> +
> +#define APPL_INTR_EN_L1_0_0				0x1C
> +#define APPL_INTR_EN_L1_0_0_LINK_REQ_RST_NOT_INT_EN	BIT(1)
> +
> +#define APPL_INTR_STATUS_L1_0_0				0x20
> +#define APPL_INTR_STATUS_L1_0_0_LINK_REQ_RST_NOT_CHGED	BIT(1)
> +
> +#define APPL_INTR_STATUS_L1_1			0x2C
> +#define APPL_INTR_STATUS_L1_2			0x30
> +#define APPL_INTR_STATUS_L1_3			0x34
> +#define APPL_INTR_STATUS_L1_6			0x3C
> +#define APPL_INTR_STATUS_L1_7			0x40
> +
> +#define APPL_INTR_EN_L1_8_0			0x44
> +#define APPL_INTR_EN_L1_8_BW_MGT_INT_EN		BIT(2)
> +#define APPL_INTR_EN_L1_8_AUTO_BW_INT_EN	BIT(3)
> +#define APPL_INTR_EN_L1_8_INTX_EN		BIT(11)
> +#define APPL_INTR_EN_L1_8_AER_INT_EN		BIT(15)
> +
> +#define APPL_INTR_STATUS_L1_8_0			0x4C
> +#define APPL_INTR_STATUS_L1_8_0_EDMA_INT_MASK	GENMASK(11, 6)
> +#define APPL_INTR_STATUS_L1_8_0_BW_MGT_INT_STS	BIT(2)
> +#define APPL_INTR_STATUS_L1_8_0_AUTO_BW_INT_STS	BIT(3)
> +
> +#define APPL_INTR_STATUS_L1_9			0x54
> +#define APPL_INTR_STATUS_L1_10			0x58
> +#define APPL_INTR_STATUS_L1_11			0x64
> +#define APPL_INTR_STATUS_L1_13			0x74
> +#define APPL_INTR_STATUS_L1_14			0x78
> +#define APPL_INTR_STATUS_L1_15			0x7C
> +#define APPL_INTR_STATUS_L1_17			0x88
> +
> +#define APPL_INTR_EN_L1_18				0x90
> +#define APPL_INTR_EN_L1_18_CDM_REG_CHK_CMPLT		BIT(2)
> +#define APPL_INTR_EN_L1_18_CDM_REG_CHK_CMP_ERR		BIT(1)
> +#define APPL_INTR_EN_L1_18_CDM_REG_CHK_LOGIC_ERR	BIT(0)
> +
> +#define APPL_INTR_STATUS_L1_18				0x94
> +#define APPL_INTR_STATUS_L1_18_CDM_REG_CHK_CMPLT	BIT(2)
> +#define APPL_INTR_STATUS_L1_18_CDM_REG_CHK_CMP_ERR	BIT(1)
> +#define APPL_INTR_STATUS_L1_18_CDM_REG_CHK_LOGIC_ERR	BIT(0)
> +
> +#define APPL_MSI_CTRL_2				0xB0
> +
> +#define APPL_LTR_MSG_1				0xC4
> +#define LTR_MSG_REQ				BIT(15)
> +#define LTR_MST_NO_SNOOP_SHIFT			16
> +
> +#define APPL_LTR_MSG_2				0xC8
> +#define APPL_LTR_MSG_2_LTR_MSG_REQ_STATE	BIT(3)
> +
> +#define APPL_LINK_STATUS			0xCC
> +#define APPL_LINK_STATUS_RDLH_LINK_UP		BIT(0)
> +
> +#define APPL_DEBUG				0xD0
> +#define APPL_DEBUG_PM_LINKST_IN_L2_LAT		BIT(21)
> +#define APPL_DEBUG_PM_LINKST_IN_L0		0x11
> +#define APPL_DEBUG_LTSSM_STATE_MASK		GENMASK(8, 3)
> +#define APPL_DEBUG_LTSSM_STATE_SHIFT		3
> +#define LTSSM_STATE_PRE_DETECT			5
> +
> +#define APPL_RADM_STATUS			0xE4
> +#define APPL_PM_XMT_TURNOFF_STATE		BIT(0)
> +
> +#define APPL_DM_TYPE				0x100
> +#define APPL_DM_TYPE_MASK			GENMASK(3, 0)
> +#define APPL_DM_TYPE_RP				0x4
> +#define APPL_DM_TYPE_EP				0x0
> +
> +#define APPL_CFG_BASE_ADDR			0x104
> +#define APPL_CFG_BASE_ADDR_MASK			GENMASK(31, 12)
> +
> +#define APPL_CFG_IATU_DMA_BASE_ADDR		0x108
> +#define APPL_CFG_IATU_DMA_BASE_ADDR_MASK	GENMASK(31, 18)
> +
> +#define APPL_CFG_MISC				0x110
> +#define APPL_CFG_MISC_SLV_EP_MODE		BIT(14)
> +#define APPL_CFG_MISC_ARCACHE_MASK		GENMASK(13, 10)
> +#define APPL_CFG_MISC_ARCACHE_SHIFT		10
> +#define APPL_CFG_MISC_ARCACHE_VAL		3
> +
> +#define APPL_CFG_SLCG_OVERRIDE			0x114
> +#define APPL_CFG_SLCG_OVERRIDE_SLCG_EN_MASTER	BIT(0)
> +
> +#define APPL_CAR_RESET_OVRD				0x12C
> +#define APPL_CAR_RESET_OVRD_CYA_OVERRIDE_CORE_RST_N	BIT(0)
> +
> +#define IO_BASE_IO_DECODE				BIT(0)
> +#define IO_BASE_IO_DECODE_BIT8				BIT(8)
> +
> +#define CFG_PREF_MEM_LIMIT_BASE_MEM_DECODE		BIT(0)
> +#define CFG_PREF_MEM_LIMIT_BASE_MEM_LIMIT_DECODE	BIT(16)
> +
> +#define CFG_TIMER_CTRL_MAX_FUNC_NUM_OFF	0x718
> +#define CFG_TIMER_CTRL_ACK_NAK_SHIFT	(19)
> +
> +#define EVENT_COUNTER_ALL_CLEAR		0x3
> +#define EVENT_COUNTER_ENABLE_ALL	0x7
> +#define EVENT_COUNTER_ENABLE_SHIFT	2
> +#define EVENT_COUNTER_EVENT_SEL_MASK	GENMASK(7, 0)
> +#define EVENT_COUNTER_EVENT_SEL_SHIFT	16
> +#define EVENT_COUNTER_EVENT_Tx_L0S	0x2
> +#define EVENT_COUNTER_EVENT_Rx_L0S	0x3
> +#define EVENT_COUNTER_EVENT_L1		0x5
> +#define EVENT_COUNTER_EVENT_L1_1	0x7
> +#define EVENT_COUNTER_EVENT_L1_2	0x8
> +#define EVENT_COUNTER_GROUP_SEL_SHIFT	24
> +#define EVENT_COUNTER_GROUP_5		0x5
> +
> +#define PORT_LOGIC_ACK_F_ASPM_CTRL			0x70C
> +#define ENTER_ASPM					BIT(30)
> +#define L0S_ENTRANCE_LAT_SHIFT				24
> +#define L0S_ENTRANCE_LAT_MASK				GENMASK(26, 24)
> +#define L1_ENTRANCE_LAT_SHIFT				27
> +#define L1_ENTRANCE_LAT_MASK				GENMASK(29, 27)
> +#define N_FTS_SHIFT					8
> +#define N_FTS_MASK					GENMASK(7, 0)
> +#define N_FTS_VAL					52
> +
> +#define PORT_LOGIC_GEN2_CTRL				0x80C
> +#define PORT_LOGIC_GEN2_CTRL_DIRECT_SPEED_CHANGE	BIT(17)
> +#define FTS_MASK					GENMASK(7, 0)
> +#define FTS_VAL						52
> +
> +#define PORT_LOGIC_MSI_CTRL_INT_0_EN		0x828
> +
> +#define GEN3_EQ_CONTROL_OFF			0x8a8
> +#define GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC_SHIFT	8
> +#define GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC_MASK	GENMASK(23, 8)
> +#define GEN3_EQ_CONTROL_OFF_FB_MODE_MASK	GENMASK(3, 0)
> +
> +#define GEN3_RELATED_OFF			0x890
> +#define GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL	BIT(0)
> +#define GEN3_RELATED_OFF_GEN3_EQ_DISABLE	BIT(16)
> +#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT	24
> +#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK	GENMASK(25, 24)
> +
> +#define PORT_LOGIC_AMBA_ERROR_RESPONSE_DEFAULT	0x8D0
> +#define AMBA_ERROR_RESPONSE_CRS_SHIFT		3
> +#define AMBA_ERROR_RESPONSE_CRS_MASK		GENMASK(1, 0)
> +#define AMBA_ERROR_RESPONSE_CRS_OKAY		0
> +#define AMBA_ERROR_RESPONSE_CRS_OKAY_FFFFFFFF	1
> +#define AMBA_ERROR_RESPONSE_CRS_OKAY_FFFF0001	2
> +
> +#define PORT_LOGIC_MSIX_DOORBELL			0x948
> +
> +#define CAP_SPCIE_CAP_OFF			0x154
> +#define CAP_SPCIE_CAP_OFF_DSP_TX_PRESET0_MASK	GENMASK(3, 0)
> +#define CAP_SPCIE_CAP_OFF_USP_TX_PRESET0_MASK	GENMASK(11, 8)
> +#define CAP_SPCIE_CAP_OFF_USP_TX_PRESET0_SHIFT	8
> +
> +#define PME_ACK_TIMEOUT 10000
> +
> +#define LTSSM_TIMEOUT 50000	/* 50ms */
> +
> +#define GEN3_GEN4_EQ_PRESET_INIT	5
> +
> +#define GEN1_CORE_CLK_FREQ	62500000
> +#define GEN2_CORE_CLK_FREQ	125000000
> +#define GEN3_CORE_CLK_FREQ	250000000
> +#define GEN4_CORE_CLK_FREQ	500000000
> +
> +static const unsigned int pcie_gen_freq[] = {
> +	GEN1_CORE_CLK_FREQ,
> +	GEN2_CORE_CLK_FREQ,
> +	GEN3_CORE_CLK_FREQ,
> +	GEN4_CORE_CLK_FREQ
> +};
> +
> +static const u32 event_cntr_ctrl_offset[] = {
> +	0x1d8,
> +	0x1a8,
> +	0x1a8,
> +	0x1a8,
> +	0x1c4,
> +	0x1d8
> +};
> +
> +static const u32 event_cntr_data_offset[] = {
> +	0x1dc,
> +	0x1ac,
> +	0x1ac,
> +	0x1ac,
> +	0x1c8,
> +	0x1dc
> +};
> +
> +struct tegra_pcie_dw {
> +	struct device *dev;
> +	struct resource *appl_res;
> +	struct resource *dbi_res;
> +	struct resource *atu_dma_res;
> +	void __iomem *appl_base;
> +	struct clk *core_clk;
> +	struct reset_control *core_apb_rst;
> +	struct reset_control *core_rst;
> +	struct dw_pcie pci;
> +	struct tegra_bpmp *bpmp;
> +
> +	bool supports_clkreq;
> +	bool enable_cdm_check;
> +	bool link_state;
> +	bool update_fc_fixup;
> +	u8 init_link_width;
> +	u32 msi_ctrl_int;
> +	u32 num_lanes;
> +	u32 max_speed;
> +	u32 cid;
> +	u32 cfg_link_cap_l1sub;
> +	u32 pcie_cap_base;
> +	u32 aspm_cmrt;
> +	u32 aspm_pwr_on_t;
> +	u32 aspm_l0s_enter_lat;
> +
> +	struct regulator *pex_ctl_supply;
> +
> +	unsigned int phy_count;
> +	struct phy **phys;
> +
> +	struct dentry *debugfs;
> +};
> +
> +static inline struct tegra_pcie_dw *to_tegra_pcie(struct dw_pcie *pci)
> +{
> +	return container_of(pci, struct tegra_pcie_dw, pci);
> +}
> +
> +static inline void appl_writel(struct tegra_pcie_dw *pcie, const u32 value,
> +			       const u32 reg)
> +{
> +	writel_relaxed(value, pcie->appl_base + reg);
> +}
> +
> +static inline u32 appl_readl(struct tegra_pcie_dw *pcie, const u32 reg)
> +{
> +	return readl_relaxed(pcie->appl_base + reg);
> +}
> +
> +struct tegra_pcie_soc {
> +	enum dw_pcie_device_mode mode;
> +};
> +
> +static void apply_bad_link_workaround(struct pcie_port *pp)
> +{
> +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
> +	struct tegra_pcie_dw *pcie = to_tegra_pcie(pci);
> +	u32 current_link_width;
> +	u16 val;
> +
> +	/*
> +	 * NOTE:- Since this scenario is uncommon and link as such is not
> +	 * stable anyway, not waiting to confirm if link is really
> +	 * transitioning to Gen-2 speed
> +	 */
> +	val = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKSTA);
> +	if (val & PCI_EXP_LNKSTA_LBMS) {
> +		current_link_width = (val & PCI_EXP_LNKSTA_NLW) >>
> +				     PCI_EXP_LNKSTA_NLW_SHIFT;
> +		if (pcie->init_link_width > current_link_width) {
> +			dev_warn(pci->dev, "PCIe link is bad, width reduced\n");
> +			val = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base +
> +						PCI_EXP_LNKCTL2);
> +			val &= ~PCI_EXP_LNKCTL2_TLS;
> +			val |= PCI_EXP_LNKCTL2_TLS_2_5GT;
> +			dw_pcie_writew_dbi(pci, pcie->pcie_cap_base +
> +					   PCI_EXP_LNKCTL2, val);
> +
> +			val = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base +
> +						PCI_EXP_LNKCTL);
> +			val |= PCI_EXP_LNKCTL_RL;
> +			dw_pcie_writew_dbi(pci, pcie->pcie_cap_base +
> +					   PCI_EXP_LNKCTL, val);
> +		}
> +	}
> +}
> +
> +static irqreturn_t tegra_pcie_rp_irq_handler(struct tegra_pcie_dw *pcie)
> +{
> +	struct dw_pcie *pci = &pcie->pci;
> +	struct pcie_port *pp = &pci->pp;
> +	u32 val, tmp;
> +	u16 val_w;
> +
> +	val = appl_readl(pcie, APPL_INTR_STATUS_L0);
> +	if (val & APPL_INTR_STATUS_L0_LINK_STATE_INT) {
> +		val = appl_readl(pcie, APPL_INTR_STATUS_L1_0_0);
> +		if (val & APPL_INTR_STATUS_L1_0_0_LINK_REQ_RST_NOT_CHGED) {
> +			appl_writel(pcie, val, APPL_INTR_STATUS_L1_0_0);
> +
> +			/* SBR & Surprise Link Down WAR */
> +			val = appl_readl(pcie, APPL_CAR_RESET_OVRD);
> +			val &= ~APPL_CAR_RESET_OVRD_CYA_OVERRIDE_CORE_RST_N;
> +			appl_writel(pcie, val, APPL_CAR_RESET_OVRD);
> +			udelay(1);
> +			val = appl_readl(pcie, APPL_CAR_RESET_OVRD);
> +			val |= APPL_CAR_RESET_OVRD_CYA_OVERRIDE_CORE_RST_N;
> +			appl_writel(pcie, val, APPL_CAR_RESET_OVRD);
> +
> +			val = dw_pcie_readl_dbi(pci, PORT_LOGIC_GEN2_CTRL);
> +			val |= PORT_LOGIC_GEN2_CTRL_DIRECT_SPEED_CHANGE;
> +			dw_pcie_writel_dbi(pci, PORT_LOGIC_GEN2_CTRL, val);
> +		}
> +	}
> +
> +	if (val & APPL_INTR_STATUS_L0_INT_INT) {
> +		val = appl_readl(pcie, APPL_INTR_STATUS_L1_8_0);
> +		if (val & APPL_INTR_STATUS_L1_8_0_AUTO_BW_INT_STS) {
> +			appl_writel(pcie,
> +				    APPL_INTR_STATUS_L1_8_0_AUTO_BW_INT_STS,
> +				    APPL_INTR_STATUS_L1_8_0);
> +			apply_bad_link_workaround(pp);
> +		}
> +		if (val & APPL_INTR_STATUS_L1_8_0_BW_MGT_INT_STS) {
> +			appl_writel(pcie,
> +				    APPL_INTR_STATUS_L1_8_0_BW_MGT_INT_STS,
> +				    APPL_INTR_STATUS_L1_8_0);
> +
> +			val_w = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base +
> +						  PCI_EXP_LNKSTA);
> +			dev_dbg(pci->dev, "Link Speed : Gen-%u\n", val_w &
> +				PCI_EXP_LNKSTA_CLS);
> +		}
> +	}
> +
> +	val = appl_readl(pcie, APPL_INTR_STATUS_L0);
> +	if (val & APPL_INTR_STATUS_L0_CDM_REG_CHK_INT) {
> +		val = appl_readl(pcie, APPL_INTR_STATUS_L1_18);
> +		tmp = dw_pcie_readl_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS);
> +		if (val & APPL_INTR_STATUS_L1_18_CDM_REG_CHK_CMPLT) {
> +			dev_info(pci->dev, "CDM check complete\n");
> +			tmp |= PCIE_PL_CHK_REG_CHK_REG_COMPLETE;
> +		}
> +		if (val & APPL_INTR_STATUS_L1_18_CDM_REG_CHK_CMP_ERR) {
> +			dev_err(pci->dev, "CDM comparison mismatch\n");
> +			tmp |= PCIE_PL_CHK_REG_CHK_REG_COMPARISON_ERROR;
> +		}
> +		if (val & APPL_INTR_STATUS_L1_18_CDM_REG_CHK_LOGIC_ERR) {
> +			dev_err(pci->dev, "CDM Logic error\n");
> +			tmp |= PCIE_PL_CHK_REG_CHK_REG_LOGIC_ERROR;
> +		}
> +		dw_pcie_writel_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS, tmp);
> +		tmp = dw_pcie_readl_dbi(pci, PCIE_PL_CHK_REG_ERR_ADDR);
> +		dev_err(pci->dev, "CDM Error Address Offset = 0x%08X\n", tmp);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t tegra_pcie_irq_handler(int irq, void *arg)
> +{
> +	struct tegra_pcie_dw *pcie = arg;
> +
> +	return tegra_pcie_rp_irq_handler(pcie);
> +}
> +
> +static int tegra_pcie_dw_rd_own_conf(struct pcie_port *pp, int where, int size,
> +				     u32 *val)
> +{
> +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
> +
> +	/*
> +	 * This is an endpoint mode specific register happen to appear even
> +	 * when controller is operating in root port mode and system hangs
> +	 * when it is accessed with link being in ASPM-L1 state.
> +	 * So skip accessing it altogether
> +	 */
> +	if (where == PORT_LOGIC_MSIX_DOORBELL) {
> +		*val = 0x00000000;
> +		return PCIBIOS_SUCCESSFUL;
> +	}
> +
> +	return dw_pcie_read(pci->dbi_base + where, size, val);
> +}
> +
> +static int tegra_pcie_dw_wr_own_conf(struct pcie_port *pp, int where, int size,
> +				     u32 val)
> +{
> +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
> +
> +	/*
> +	 * This is an endpoint mode specific register happen to appear even
> +	 * when controller is operating in root port mode and system hangs
> +	 * when it is accessed with link being in ASPM-L1 state.
> +	 * So skip accessing it altogether
> +	 */
> +	if (where == PORT_LOGIC_MSIX_DOORBELL)
> +		return PCIBIOS_SUCCESSFUL;
> +
> +	return dw_pcie_write(pci->dbi_base + where, size, val);
> +}
> +
> +#if defined(CONFIG_PCIEASPM)
> +static void disable_aspm_l11(struct tegra_pcie_dw *pcie)
> +{
> +	u32 val;
> +
> +	val = dw_pcie_readl_dbi(&pcie->pci, pcie->cfg_link_cap_l1sub);
> +	val &= ~PCI_L1SS_CAP_ASPM_L1_1;
> +	dw_pcie_writel_dbi(&pcie->pci, pcie->cfg_link_cap_l1sub, val);
> +}
> +
> +static void disable_aspm_l12(struct tegra_pcie_dw *pcie)
> +{
> +	u32 val;
> +
> +	val = dw_pcie_readl_dbi(&pcie->pci, pcie->cfg_link_cap_l1sub);
> +	val &= ~PCI_L1SS_CAP_ASPM_L1_2;
> +	dw_pcie_writel_dbi(&pcie->pci, pcie->cfg_link_cap_l1sub, val);
> +}
> +
> +static inline u32 event_counter_prog(struct tegra_pcie_dw *pcie, u32 event)
> +{
> +	u32 val;
> +
> +	val = dw_pcie_readl_dbi(&pcie->pci, event_cntr_ctrl_offset[pcie->cid]);
> +	val &= ~(EVENT_COUNTER_EVENT_SEL_MASK << EVENT_COUNTER_EVENT_SEL_SHIFT);
> +	val |= EVENT_COUNTER_GROUP_5 << EVENT_COUNTER_GROUP_SEL_SHIFT;
> +	val |= event << EVENT_COUNTER_EVENT_SEL_SHIFT;
> +	val |= EVENT_COUNTER_ENABLE_ALL << EVENT_COUNTER_ENABLE_SHIFT;
> +	dw_pcie_writel_dbi(&pcie->pci, event_cntr_ctrl_offset[pcie->cid], val);
> +	val = dw_pcie_readl_dbi(&pcie->pci, event_cntr_data_offset[pcie->cid]);
> +	return val;
> +}
> +
> +static int aspm_state_cnt(struct seq_file *s, void *data)
> +{
> +	struct tegra_pcie_dw *pcie = (struct tegra_pcie_dw *)
> +				     dev_get_drvdata(s->private);
> +	u32 val;
> +
> +	seq_printf(s, "Tx L0s entry count : %u\n",
> +		   event_counter_prog(pcie, EVENT_COUNTER_EVENT_Tx_L0S));
> +
> +	seq_printf(s, "Rx L0s entry count : %u\n",
> +		   event_counter_prog(pcie, EVENT_COUNTER_EVENT_Rx_L0S));
> +
> +	seq_printf(s, "Link L1 entry count : %u\n",
> +		   event_counter_prog(pcie, EVENT_COUNTER_EVENT_L1));
> +
> +	seq_printf(s, "Link L1.1 entry count : %u\n",
> +		   event_counter_prog(pcie, EVENT_COUNTER_EVENT_L1_1));
> +
> +	seq_printf(s, "Link L1.2 entry count : %u\n",
> +		   event_counter_prog(pcie, EVENT_COUNTER_EVENT_L1_2));
> +
> +	/* Clear all counters */
> +	dw_pcie_writel_dbi(&pcie->pci, event_cntr_ctrl_offset[pcie->cid],
> +			   EVENT_COUNTER_ALL_CLEAR);
> +
> +	/* Re-enable counting */
> +	val = EVENT_COUNTER_ENABLE_ALL << EVENT_COUNTER_ENABLE_SHIFT;
> +	val |= EVENT_COUNTER_GROUP_5 << EVENT_COUNTER_GROUP_SEL_SHIFT;
> +	dw_pcie_writel_dbi(&pcie->pci, event_cntr_ctrl_offset[pcie->cid], val);
> +
> +	return 0;
> +}
> +
> +static void init_host_aspm(struct tegra_pcie_dw *pcie)
> +{
> +	struct dw_pcie *pci = &pcie->pci;
> +	u32 val;
> +
> +	val = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS);
> +	pcie->cfg_link_cap_l1sub = val + PCI_L1SS_CAP;
> +
> +	/* Enable ASPM counters */
> +	val = EVENT_COUNTER_ENABLE_ALL << EVENT_COUNTER_ENABLE_SHIFT;
> +	val |= EVENT_COUNTER_GROUP_5 << EVENT_COUNTER_GROUP_SEL_SHIFT;
> +	dw_pcie_writel_dbi(pci, event_cntr_ctrl_offset[pcie->cid], val);
> +
> +	/* Program T_cmrt and T_pwr_on values */
> +	val = dw_pcie_readl_dbi(pci, pcie->cfg_link_cap_l1sub);
> +	val &= ~(PCI_L1SS_CAP_CM_RESTORE_TIME | PCI_L1SS_CAP_P_PWR_ON_VALUE);
> +	val |= (pcie->aspm_cmrt << 8);
> +	val |= (pcie->aspm_pwr_on_t << 19);
> +	dw_pcie_writel_dbi(pci, pcie->cfg_link_cap_l1sub, val);
> +
> +	/* Program L0s and L1 entrance latencies */
> +	val = dw_pcie_readl_dbi(pci, PORT_LOGIC_ACK_F_ASPM_CTRL);
> +	val &= ~L0S_ENTRANCE_LAT_MASK;
> +	val |= (pcie->aspm_l0s_enter_lat << L0S_ENTRANCE_LAT_SHIFT);
> +	val |= ENTER_ASPM;
> +	dw_pcie_writel_dbi(pci, PORT_LOGIC_ACK_F_ASPM_CTRL, val);
> +}
> +
> +static int init_debugfs(struct tegra_pcie_dw *pcie)
> +{
> +	struct dentry *d;
> +
> +	d = debugfs_create_devm_seqfile(pcie->dev, "aspm_state_cnt",
> +					pcie->debugfs, aspm_state_cnt);
> +	if (IS_ERR_OR_NULL(d))
> +		dev_err(pcie->dev,
> +			"Failed to create debugfs file \"aspm_state_cnt\"\n");
> +
> +	return 0;
> +}
> +#else
> +static inline void disable_aspm_l12(struct tegra_pcie_dw *pcie) { return; }
> +static inline void disable_aspm_l11(struct tegra_pcie_dw *pcie) { return; }
> +static inline void init_host_aspm(struct tegra_pcie_dw *pcie) { return; }
> +static inline int init_debugfs(struct tegra_pcie_dw *pcie) { return 0; }
> +#endif
> +
> +static void tegra_pcie_enable_system_interrupts(struct pcie_port *pp)
> +{
> +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
> +	struct tegra_pcie_dw *pcie = to_tegra_pcie(pci);
> +	u32 val;
> +	u16 val_w;
> +
> +	val = appl_readl(pcie, APPL_INTR_EN_L0_0);
> +	val |= APPL_INTR_EN_L0_0_LINK_STATE_INT_EN;
> +	appl_writel(pcie, val, APPL_INTR_EN_L0_0);
> +
> +	val = appl_readl(pcie, APPL_INTR_EN_L1_0_0);
> +	val |= APPL_INTR_EN_L1_0_0_LINK_REQ_RST_NOT_INT_EN;
> +	appl_writel(pcie, val, APPL_INTR_EN_L1_0_0);
> +
> +	if (pcie->enable_cdm_check) {
> +		val = appl_readl(pcie, APPL_INTR_EN_L0_0);
> +		val |= APPL_INTR_EN_L0_0_CDM_REG_CHK_INT_EN;
> +		appl_writel(pcie, val, APPL_INTR_EN_L0_0);
> +
> +		val = appl_readl(pcie, APPL_INTR_EN_L1_18);
> +		val |= APPL_INTR_EN_L1_18_CDM_REG_CHK_CMP_ERR;
> +		val |= APPL_INTR_EN_L1_18_CDM_REG_CHK_LOGIC_ERR;
> +		appl_writel(pcie, val, APPL_INTR_EN_L1_18);
> +	}
> +
> +	val_w = dw_pcie_readw_dbi(&pcie->pci, pcie->pcie_cap_base +
> +				  PCI_EXP_LNKSTA);
> +	pcie->init_link_width = (val_w & PCI_EXP_LNKSTA_NLW) >>
> +				PCI_EXP_LNKSTA_NLW_SHIFT;
> +
> +	val_w = dw_pcie_readw_dbi(&pcie->pci, pcie->pcie_cap_base +
> +				  PCI_EXP_LNKCTL);
> +	val_w |= PCI_EXP_LNKCTL_LBMIE;
> +	dw_pcie_writew_dbi(&pcie->pci, pcie->pcie_cap_base + PCI_EXP_LNKCTL,
> +			   val_w);
> +}
> +
> +static void tegra_pcie_enable_legacy_interrupts(struct pcie_port *pp)
> +{
> +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
> +	struct tegra_pcie_dw *pcie = to_tegra_pcie(pci);
> +	u32 val;
> +
> +	/* Enable legacy interrupt generation */
> +	val = appl_readl(pcie, APPL_INTR_EN_L0_0);
> +	val |= APPL_INTR_EN_L0_0_SYS_INTR_EN;
> +	val |= APPL_INTR_EN_L0_0_INT_INT_EN;
> +	appl_writel(pcie, val, APPL_INTR_EN_L0_0);
> +
> +	val = appl_readl(pcie, APPL_INTR_EN_L1_8_0);
> +	val |= APPL_INTR_EN_L1_8_INTX_EN;
> +	val |= APPL_INTR_EN_L1_8_AUTO_BW_INT_EN;
> +	val |= APPL_INTR_EN_L1_8_BW_MGT_INT_EN;
> +	if (IS_ENABLED(CONFIG_PCIEAER))
> +		val |= APPL_INTR_EN_L1_8_AER_INT_EN;
> +	appl_writel(pcie, val, APPL_INTR_EN_L1_8_0);
> +}
> +
> +static void tegra_pcie_enable_msi_interrupts(struct pcie_port *pp)
> +{
> +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
> +	struct tegra_pcie_dw *pcie = to_tegra_pcie(pci);
> +	u32 val;
> +
> +	dw_pcie_msi_init(pp);
> +
> +	/* Enable MSI interrupt generation */
> +	val = appl_readl(pcie, APPL_INTR_EN_L0_0);
> +	val |= APPL_INTR_EN_L0_0_SYS_MSI_INTR_EN;
> +	val |= APPL_INTR_EN_L0_0_MSI_RCV_INT_EN;
> +	appl_writel(pcie, val, APPL_INTR_EN_L0_0);
> +}
> +
> +static void tegra_pcie_enable_interrupts(struct pcie_port *pp)
> +{
> +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
> +	struct tegra_pcie_dw *pcie = to_tegra_pcie(pci);
> +
> +	/* Clear interrupt statuses before enabling interrupts */
> +	appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L0);
> +	appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_0_0);
> +	appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_1);
> +	appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_2);
> +	appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_3);
> +	appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_6);
> +	appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_7);
> +	appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_8_0);
> +	appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_9);
> +	appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_10);
> +	appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_11);
> +	appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_13);
> +	appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_14);
> +	appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_15);
> +	appl_writel(pcie, 0xFFFFFFFF, APPL_INTR_STATUS_L1_17);
> +
> +	tegra_pcie_enable_system_interrupts(pp);
> +	tegra_pcie_enable_legacy_interrupts(pp);
> +	if (IS_ENABLED(CONFIG_PCI_MSI))
> +		tegra_pcie_enable_msi_interrupts(pp);
> +}
> +
> +static void config_gen3_gen4_eq_presets(struct tegra_pcie_dw *pcie)
> +{
> +	struct dw_pcie *pci = &pcie->pci;
> +	u32 val, offset, i;
> +
> +	/* Program init preset */
> +	for (i = 0; i < pcie->num_lanes; i++) {
> +		dw_pcie_read(pci->dbi_base + CAP_SPCIE_CAP_OFF
> +				 + (i * 2), 2, &val);
> +		val &= ~CAP_SPCIE_CAP_OFF_DSP_TX_PRESET0_MASK;
> +		val |= GEN3_GEN4_EQ_PRESET_INIT;
> +		val &= ~CAP_SPCIE_CAP_OFF_USP_TX_PRESET0_MASK;
> +		val |= (GEN3_GEN4_EQ_PRESET_INIT <<
> +			   CAP_SPCIE_CAP_OFF_USP_TX_PRESET0_SHIFT);
> +		dw_pcie_write(pci->dbi_base + CAP_SPCIE_CAP_OFF
> +				 + (i * 2), 2, val);
> +
> +		offset = dw_pcie_find_ext_capability(pci,
> +						     PCI_EXT_CAP_ID_PL_16GT) +
> +				PCI_PL_16GT_LE_CTRL;
> +		dw_pcie_read(pci->dbi_base + offset + i, 1, &val);
> +		val &= ~PCI_PL_16GT_LE_CTRL_DSP_TX_PRESET_MASK;
> +		val |= GEN3_GEN4_EQ_PRESET_INIT;
> +		val &= ~PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_MASK;
> +		val |= (GEN3_GEN4_EQ_PRESET_INIT <<
> +			PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_SHIFT);
> +		dw_pcie_write(pci->dbi_base + offset + i, 1, val);
> +	}
> +
> +	val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
> +	val &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK;
> +	dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
> +
> +	val = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF);
> +	val &= ~GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC_MASK;
> +	val |= (0x3ff << GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC_SHIFT);
> +	val &= ~GEN3_EQ_CONTROL_OFF_FB_MODE_MASK;
> +	dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, val);
> +
> +	val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
> +	val &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK;
> +	val |= (0x1 << GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT);
> +	dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
> +
> +	val = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF);
> +	val &= ~GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC_MASK;
> +	val |= (0x360 << GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC_SHIFT);
> +	val &= ~GEN3_EQ_CONTROL_OFF_FB_MODE_MASK;
> +	dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, val);
> +
> +	val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
> +	val &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK;
> +	dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
> +}
> +
> +static void tegra_pcie_prepare_host(struct pcie_port *pp)
> +{
> +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
> +	struct tegra_pcie_dw *pcie = to_tegra_pcie(pci);
> +	u32 val;
> +
> +	val = dw_pcie_readl_dbi(pci, PCI_IO_BASE);
> +	val &= ~(IO_BASE_IO_DECODE | IO_BASE_IO_DECODE_BIT8);
> +	dw_pcie_writel_dbi(pci, PCI_IO_BASE, val);
> +
> +	val = dw_pcie_readl_dbi(pci, PCI_PREF_MEMORY_BASE);
> +	val |= CFG_PREF_MEM_LIMIT_BASE_MEM_DECODE;
> +	val |= CFG_PREF_MEM_LIMIT_BASE_MEM_LIMIT_DECODE;
> +	dw_pcie_writel_dbi(pci, PCI_PREF_MEMORY_BASE, val);
> +
> +	dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0);
> +
> +	/* Configure FTS */
> +	val = dw_pcie_readl_dbi(pci, PORT_LOGIC_ACK_F_ASPM_CTRL);
> +	val &= ~(N_FTS_MASK << N_FTS_SHIFT);
> +	val |= N_FTS_VAL << N_FTS_SHIFT;
> +	dw_pcie_writel_dbi(pci, PORT_LOGIC_ACK_F_ASPM_CTRL, val);
> +
> +	val = dw_pcie_readl_dbi(pci, PORT_LOGIC_GEN2_CTRL);
> +	val &= ~FTS_MASK;
> +	val |= FTS_VAL;
> +	dw_pcie_writel_dbi(pci, PORT_LOGIC_GEN2_CTRL, val);
> +
> +	/* Enable as 0xFFFF0001 response for CRS */
> +	val = dw_pcie_readl_dbi(pci, PORT_LOGIC_AMBA_ERROR_RESPONSE_DEFAULT);
> +	val &= ~(AMBA_ERROR_RESPONSE_CRS_MASK << AMBA_ERROR_RESPONSE_CRS_SHIFT);
> +	val |= (AMBA_ERROR_RESPONSE_CRS_OKAY_FFFF0001 <<
> +		AMBA_ERROR_RESPONSE_CRS_SHIFT);
> +	dw_pcie_writel_dbi(pci, PORT_LOGIC_AMBA_ERROR_RESPONSE_DEFAULT, val);
> +
> +	/* Configure Max Speed from DT */
> +	if (pcie->max_speed && pcie->max_speed != -EINVAL) {
> +		val = dw_pcie_readl_dbi(pci, pcie->pcie_cap_base +
> +					PCI_EXP_LNKCAP);
> +		val &= ~PCI_EXP_LNKCAP_SLS;
> +		val |= pcie->max_speed;
> +		dw_pcie_writel_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKCAP,
> +				   val);
> +	}
> +
> +	/* Configure Max lane width from DT */
> +	val = dw_pcie_readl_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKCAP);
> +	val &= ~PCI_EXP_LNKCAP_MLW;
> +	val |= (pcie->num_lanes << PCI_EXP_LNKSTA_NLW_SHIFT);
> +	dw_pcie_writel_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKCAP, val);
> +
> +	config_gen3_gen4_eq_presets(pcie);
> +
> +	init_host_aspm(pcie);
> +
> +	val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
> +	val &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
> +	dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
> +
> +	if (pcie->update_fc_fixup) {
> +		val = dw_pcie_readl_dbi(pci, CFG_TIMER_CTRL_MAX_FUNC_NUM_OFF);
> +		val |= 0x1 << CFG_TIMER_CTRL_ACK_NAK_SHIFT;
> +		dw_pcie_writel_dbi(pci, CFG_TIMER_CTRL_MAX_FUNC_NUM_OFF, val);
> +	}
> +
> +	dw_pcie_setup_rc(pp);
> +
> +	clk_set_rate(pcie->core_clk, GEN4_CORE_CLK_FREQ);
> +
> +	/* Assert RST */
> +	val = appl_readl(pcie, APPL_PINMUX);
> +	val &= ~APPL_PINMUX_PEX_RST;
> +	appl_writel(pcie, val, APPL_PINMUX);
> +
> +	usleep_range(100, 200);
> +
> +	/* Enable LTSSM */
> +	val = appl_readl(pcie, APPL_CTRL);
> +	val |= APPL_CTRL_LTSSM_EN;
> +	appl_writel(pcie, val, APPL_CTRL);
> +
> +	/* De-assert RST */
> +	val = appl_readl(pcie, APPL_PINMUX);
> +	val |= APPL_PINMUX_PEX_RST;
> +	appl_writel(pcie, val, APPL_PINMUX);
> +
> +	msleep(100);
> +}
> +
> +static int tegra_pcie_dw_host_init(struct pcie_port *pp)
> +{
> +	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
> +	struct tegra_pcie_dw *pcie = to_tegra_pcie(pci);
> +	u32 val, tmp, offset, speed;
> +
> +	tegra_pcie_prepare_host(pp);
> +
> +	if (dw_pcie_wait_for_link(pci)) {
> +		/*
> +		 * There are some endpoints which can't get the link up if
> +		 * root port has Data Link Feature (DLF) enabled.
> +		 * Refer Spec rev 4.0 ver 1.0 sec 3.4.2 & 7.7.4 for more info
> +		 * on Scaled Flow Control and DLF.
> +		 * So, need to confirm that is indeed the case here and attempt
> +		 * link up once again with DLF disabled.
> +		 */
> +		val = appl_readl(pcie, APPL_DEBUG);
> +		val &= APPL_DEBUG_LTSSM_STATE_MASK;
> +		val >>= APPL_DEBUG_LTSSM_STATE_SHIFT;
> +		tmp = appl_readl(pcie, APPL_LINK_STATUS);
> +		tmp &= APPL_LINK_STATUS_RDLH_LINK_UP;
> +		if (!(val == 0x11 && !tmp)) {
> +			/* Link is down for all good reasons */
> +			return 0;
> +		}
> +
> +		dev_info(pci->dev, "Link is down in DLL");
> +		dev_info(pci->dev, "Trying again with DLFE disabled\n");
> +		/* Disable LTSSM */
> +		val = appl_readl(pcie, APPL_CTRL);
> +		val &= ~APPL_CTRL_LTSSM_EN;
> +		appl_writel(pcie, val, APPL_CTRL);
> +
> +		reset_control_assert(pcie->core_rst);
> +		reset_control_deassert(pcie->core_rst);
> +
> +		offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_DLF);
> +		val = dw_pcie_readl_dbi(pci, offset + PCI_DLF_CAP);
> +		val &= ~PCI_DLF_EXCHANGE_ENABLE;
> +		dw_pcie_writel_dbi(pci, offset, val);
> +
> +		tegra_pcie_prepare_host(pp);
> +
> +		if (dw_pcie_wait_for_link(pci))
> +			return 0;
> +	}
> +
> +	speed = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKSTA) &
> +		PCI_EXP_LNKSTA_CLS;
> +	clk_set_rate(pcie->core_clk, pcie_gen_freq[speed - 1]);
> +
> +	tegra_pcie_enable_interrupts(pp);
> +
> +	return 0;
> +}
> +
> +static int tegra_pcie_dw_link_up(struct dw_pcie *pci)
> +{
> +	struct tegra_pcie_dw *pcie = to_tegra_pcie(pci);
> +	u32 val = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKSTA);
> +
> +	return !!(val & PCI_EXP_LNKSTA_DLLLA);
> +}
> +
> +static void tegra_pcie_set_msi_vec_num(struct pcie_port *pp)
> +{
> +	pp->num_vectors = MAX_MSI_IRQS;
> +}
> +
> +static const struct dw_pcie_ops tegra_dw_pcie_ops = {
> +	.link_up = tegra_pcie_dw_link_up,
> +};
> +
> +static struct dw_pcie_host_ops tegra_pcie_dw_host_ops = {
> +	.rd_own_conf = tegra_pcie_dw_rd_own_conf,
> +	.wr_own_conf = tegra_pcie_dw_wr_own_conf,
> +	.host_init = tegra_pcie_dw_host_init,
> +	.set_num_vectors = tegra_pcie_set_msi_vec_num,
> +};
> +
> +static void tegra_pcie_disable_phy(struct tegra_pcie_dw *pcie)
> +{
> +	unsigned int phy_count = pcie->phy_count;
> +
> +	while (phy_count--) {
> +		phy_power_off(pcie->phys[phy_count]);
> +		phy_exit(pcie->phys[phy_count]);
> +	}
> +}
> +
> +static int tegra_pcie_enable_phy(struct tegra_pcie_dw *pcie)
> +{
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < pcie->phy_count; i++) {
> +		ret = phy_init(pcie->phys[i]);
> +		if (ret < 0)
> +			goto phy_power_off;
> +
> +		ret = phy_power_on(pcie->phys[i]);
> +		if (ret < 0)
> +			goto phy_exit;
> +	}
> +
> +	return 0;
> +
> +phy_power_off:
> +	while (i--) {
> +		phy_power_off(pcie->phys[i]);
> +phy_exit:
> +		phy_exit(pcie->phys[i]);
> +	}
> +
> +	return ret;
> +}
> +
> +static int tegra_pcie_dw_parse_dt(struct tegra_pcie_dw *pcie)
> +{
> +	struct device_node *np = pcie->dev->of_node;
> +	int ret;
> +
> +	ret = of_property_read_u32(np, "nvidia,aspm-cmrt-us", &pcie->aspm_cmrt);
> +	if (ret < 0) {
> +		dev_info(pcie->dev, "Failed to read ASPM T_cmrt: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = of_property_read_u32(np, "nvidia,aspm-pwr-on-t-us",
> +				   &pcie->aspm_pwr_on_t);
> +	if (ret < 0)
> +		dev_info(pcie->dev, "Failed to read ASPM Power On time: %d\n",
> +			 ret);
> +
> +	ret = of_property_read_u32(np, "nvidia,aspm-l0s-entrance-latency-us",
> +				   &pcie->aspm_l0s_enter_lat);
> +	if (ret < 0)
> +		dev_info(pcie->dev,
> +			 "Failed to read ASPM L0s Entrance latency: %d\n", ret);
> +
> +	ret = of_property_read_u32(np, "num-lanes", &pcie->num_lanes);
> +	if (ret < 0) {
> +		dev_err(pcie->dev, "Failed to read num-lanes: %d\n", ret);
> +		return ret;
> +	}
> +
> +	pcie->max_speed = of_pci_get_max_link_speed(np);
> +
> +	ret = of_property_read_u32_index(np, "nvidia,bpmp", 1, &pcie->cid);
> +	if (ret) {
> +		dev_err(pcie->dev, "Failed to read Controller-ID: %d\n", ret);
> +		return ret;
> +	}
> +
> +	pcie->phy_count = of_property_count_strings(np, "phy-names");
> +	if (pcie->phy_count < 0) {
> +		dev_err(pcie->dev, "Failed to find PHY entries: %d\n",
> +			pcie->phy_count);
> +		return pcie->phy_count;
> +	}
> +
> +	if (of_property_read_bool(np, "nvidia,update-fc-fixup"))
> +		pcie->update_fc_fixup = true;
> +
> +	pcie->supports_clkreq =
> +		of_property_read_bool(pcie->dev->of_node, "supports-clkreq");
> +
> +	pcie->enable_cdm_check =
> +		of_property_read_bool(np, "snps,enable-cdm-check");
> +
> +	return 0;
> +}
> +
> +static int tegra_pcie_bpmp_set_ctrl_state(struct tegra_pcie_dw *pcie,
> +					  bool enable, bool is_noirq)
> +{
> +	struct mrq_uphy_response resp;
> +	struct tegra_bpmp_message msg;
> +	struct mrq_uphy_request req;
> +	unsigned long flags;
> +	int err;
> +
> +	/* Controller-5 doesn't need to have its state set by BPMP-FW */
> +	if (pcie->cid == 5)
> +		return 0;
> +
> +	memset(&req, 0, sizeof(req));
> +	memset(&resp, 0, sizeof(resp));
> +
> +	req.cmd = CMD_UPHY_PCIE_CONTROLLER_STATE;
> +	req.controller_state.pcie_controller = pcie->cid;
> +	req.controller_state.enable = enable;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.mrq = MRQ_UPHY;
> +	msg.tx.data = &req;
> +	msg.tx.size = sizeof(req);
> +	msg.rx.data = &resp;
> +	msg.rx.size = sizeof(resp);
> +
> +	if (is_noirq) {
> +		local_irq_save(flags);
> +		err = tegra_bpmp_transfer_atomic(pcie->bpmp, &msg);
> +		local_irq_restore(flags);
> +	} else {
> +		err = tegra_bpmp_transfer(pcie->bpmp, &msg);
> +	}
> +
> +	return err;
> +}
> +
> +static void tegra_pcie_downstream_dev_to_D0(struct tegra_pcie_dw *pcie)
> +{
> +	struct pcie_port *pp = &pcie->pci.pp;
> +	struct pci_bus *child;
> +	struct pci_dev *pdev;
> +
> +	/*
> +	 * link doesn't go into L2 state with some of the endpoints with Tegra
> +	 * if they are not in D0 state. So, need to make sure that immediate
> +	 * downstream devices are in D0 state before sending PME_TurnOff to put
> +	 * link into L2 state.
> +	 * This is as per PCI Express Base r4.0 v1.0 September 27-2017,
> +	 * 5.2 Link State Power Management (Page #428).
> +	 */
> +
> +	list_for_each_entry(child, &pp->root_bus->children, node) {
> +		/* Bring downstream devices to D0 if they are not already in */
> +		if (child->parent == pp->root_bus)
> +			break;
> +	}
> +	list_for_each_entry(pdev, &child->devices, bus_list) {
> +		if (PCI_SLOT(pdev->devfn) == 0) {
> +			if (pci_set_power_state(pdev, PCI_D0))
> +				dev_err(pcie->dev,
> +					"Failed to transition %s to D0 state\n",
> +					dev_name(&pdev->dev));
> +		}
> +	}
> +}
> +
> +static int tegra_pcie_config_controller(struct tegra_pcie_dw *pcie,
> +					bool en_hw_hot_rst)
> +{
> +	int ret;
> +	u32 val;
> +
> +	ret = tegra_pcie_bpmp_set_ctrl_state(pcie, true, false);
> +	if (ret) {
> +		dev_err(pcie->dev,
> +			"Failed to enable controller %u: %d\n", pcie->cid, ret);
> +		return ret;
> +	}
> +
> +	ret = regulator_enable(pcie->pex_ctl_supply);
> +	if (ret < 0) {
> +		dev_err(pcie->dev, "Failed to enable regulator: %d\n", ret);
> +		goto fail_reg_en;
> +	}
> +
> +	ret = clk_prepare_enable(pcie->core_clk);
> +	if (ret) {
> +		dev_err(pcie->dev, "Failed to enable core clock: %d\n", ret);
> +		goto fail_core_clk;
> +	}
> +
> +	ret = reset_control_deassert(pcie->core_apb_rst);
> +	if (ret) {
> +		dev_err(pcie->dev, "Failed to deassert core APB reset: %d\n",
> +			ret);
> +		goto fail_core_apb_rst;
> +	}
> +
> +	if (en_hw_hot_rst) {
> +		/* Enable HW_HOT_RST mode */
> +		val = appl_readl(pcie, APPL_CTRL);
> +		val &= ~(APPL_CTRL_HW_HOT_RST_MODE_MASK <<
> +			 APPL_CTRL_HW_HOT_RST_MODE_SHIFT);
> +		val |= APPL_CTRL_HW_HOT_RST_EN;
> +		appl_writel(pcie, val, APPL_CTRL);
> +	}
> +
> +	ret = tegra_pcie_enable_phy(pcie);
> +	if (ret) {
> +		dev_err(pcie->dev, "Failed to enable PHY: %d\n", ret);
> +		goto fail_phy;
> +	}
> +
> +	/* Update CFG base address */
> +	appl_writel(pcie, pcie->dbi_res->start & APPL_CFG_BASE_ADDR_MASK,
> +		    APPL_CFG_BASE_ADDR);
> +
> +	/* Configure this core for RP mode operation */
> +	appl_writel(pcie, APPL_DM_TYPE_RP, APPL_DM_TYPE);
> +
> +	appl_writel(pcie, 0x0, APPL_CFG_SLCG_OVERRIDE);
> +
> +	val = appl_readl(pcie, APPL_CTRL);
> +	appl_writel(pcie, val | APPL_CTRL_SYS_PRE_DET_STATE, APPL_CTRL);
> +
> +	val = appl_readl(pcie, APPL_CFG_MISC);
> +	val |= (APPL_CFG_MISC_ARCACHE_VAL << APPL_CFG_MISC_ARCACHE_SHIFT);
> +	appl_writel(pcie, val, APPL_CFG_MISC);
> +
> +	if (!pcie->supports_clkreq) {
> +		val = appl_readl(pcie, APPL_PINMUX);
> +		val |= APPL_PINMUX_CLKREQ_OUT_OVRD_EN;
> +		val |= APPL_PINMUX_CLKREQ_OUT_OVRD;
> +		appl_writel(pcie, val, APPL_PINMUX);
> +	}
> +
> +	/* Update iATU_DMA base address */
> +	appl_writel(pcie,
> +		    pcie->atu_dma_res->start & APPL_CFG_IATU_DMA_BASE_ADDR_MASK,
> +		    APPL_CFG_IATU_DMA_BASE_ADDR);
> +
> +	reset_control_deassert(pcie->core_rst);
> +
> +	pcie->pcie_cap_base = dw_pcie_find_capability(&pcie->pci,
> +						      PCI_CAP_ID_EXP);
> +
> +	/* Disable ASPM-L1SS advertisement as there is no CLKREQ routing */
> +	if (!pcie->supports_clkreq) {
> +		disable_aspm_l11(pcie);
> +		disable_aspm_l12(pcie);
> +	}
> +
> +	return ret;
> +
> +fail_phy:
> +	reset_control_assert(pcie->core_apb_rst);
> +fail_core_apb_rst:
> +	clk_disable_unprepare(pcie->core_clk);
> +fail_core_clk:
> +	regulator_disable(pcie->pex_ctl_supply);
> +fail_reg_en:
> +	tegra_pcie_bpmp_set_ctrl_state(pcie, false, false);
> +
> +	return ret;
> +}
> +
> +static int __deinit_controller(struct tegra_pcie_dw *pcie, bool is_noirq)
> +{
> +	int ret;
> +
> +	ret = reset_control_assert(pcie->core_rst);
> +	if (ret) {
> +		dev_err(pcie->dev, "Failed to assert \"core\" reset: %d\n",
> +			ret);
> +		return ret;
> +	}
> +	tegra_pcie_disable_phy(pcie);
> +	ret = reset_control_assert(pcie->core_apb_rst);
> +	if (ret) {
> +		dev_err(pcie->dev, "Failed to assert APB reset: %d\n", ret);
> +		return ret;
> +	}
> +	clk_disable_unprepare(pcie->core_clk);
> +	ret = regulator_disable(pcie->pex_ctl_supply);
> +	if (ret) {
> +		dev_err(pcie->dev, "Failed to disable regulator: %d\n", ret);
> +		return ret;
> +	}
> +	ret = tegra_pcie_bpmp_set_ctrl_state(pcie, false, is_noirq);
> +	if (ret) {
> +		dev_err(pcie->dev, "Failed to disable controller %d: %d\n",
> +			pcie->cid, ret);
> +		return ret;
> +	}
> +	return ret;
> +}
> +
> +static int tegra_pcie_init_controller(struct tegra_pcie_dw *pcie)
> +{
> +	struct dw_pcie *pci = &pcie->pci;
> +	struct pcie_port *pp = &pci->pp;
> +	int ret;
> +
> +	ret = tegra_pcie_config_controller(pcie, false);
> +	if (ret < 0)
> +		return ret;
> +
> +	pp->ops = &tegra_pcie_dw_host_ops;
> +
> +	ret = dw_pcie_host_init(pp);
> +	if (ret < 0) {
> +		dev_err(pcie->dev, "Failed to add PCIe port: %d\n", ret);
> +		goto fail_host_init;
> +	}
> +
> +	return 0;
> +
> +fail_host_init:
> +	return __deinit_controller(pcie, false);
> +}
> +
> +static int tegra_pcie_try_link_l2(struct tegra_pcie_dw *pcie)
> +{
> +	u32 val;
> +
> +	if (!tegra_pcie_dw_link_up(&pcie->pci))
> +		return 0;
> +
> +	val = appl_readl(pcie, APPL_RADM_STATUS);
> +	val |= APPL_PM_XMT_TURNOFF_STATE;
> +	appl_writel(pcie, val, APPL_RADM_STATUS);
> +
> +	return readl_poll_timeout_atomic(pcie->appl_base + APPL_DEBUG, val,
> +				 val & APPL_DEBUG_PM_LINKST_IN_L2_LAT,
> +				 1, PME_ACK_TIMEOUT);
> +}
> +
> +static void tegra_pcie_dw_pme_turnoff(struct tegra_pcie_dw *pcie)
> +{
> +	u32 data;
> +	int err;
> +
> +	if (!tegra_pcie_dw_link_up(&pcie->pci)) {
> +		dev_dbg(pcie->dev, "PCIe link is not up...!\n");
> +		return;
> +	}
> +
> +	if (tegra_pcie_try_link_l2(pcie)) {
> +		dev_info(pcie->dev, "Link didn't transition to L2 state\n");
> +		/*
> +		 * TX lane clock freq will reset to Gen1 only if link is in L2
> +		 * or detect state.
> +		 * So apply pex_rst to end point to force RP to go into detect
> +		 * state
> +		 */
> +		data = appl_readl(pcie, APPL_PINMUX);
> +		data &= ~APPL_PINMUX_PEX_RST;
> +		appl_writel(pcie, data, APPL_PINMUX);
> +
> +		err = readl_poll_timeout_atomic(pcie->appl_base + APPL_DEBUG,
> +						data,
> +						((data &
> +						APPL_DEBUG_LTSSM_STATE_MASK) >>
> +						APPL_DEBUG_LTSSM_STATE_SHIFT) ==
> +						LTSSM_STATE_PRE_DETECT,
> +						1, LTSSM_TIMEOUT);
> +		if (err) {
> +			dev_info(pcie->dev, "Link didn't go to detect state\n");
> +		} else {
> +			/* Disable LTSSM after link is in detect state */
> +			data = appl_readl(pcie, APPL_CTRL);
> +			data &= ~APPL_CTRL_LTSSM_EN;
> +			appl_writel(pcie, data, APPL_CTRL);
> +		}
> +	}
> +	/*
> +	 * DBI registers may not be accessible after this as PLL-E would be
> +	 * down depending on how CLKREQ is pulled by end point
> +	 */
> +	data = appl_readl(pcie, APPL_PINMUX);
> +	data |= (APPL_PINMUX_CLKREQ_OVERRIDE_EN | APPL_PINMUX_CLKREQ_OVERRIDE);
> +	/* Cut REFCLK to slot */
> +	data |= APPL_PINMUX_CLK_OUTPUT_IN_OVERRIDE_EN;
> +	data &= ~APPL_PINMUX_CLK_OUTPUT_IN_OVERRIDE;
> +	appl_writel(pcie, data, APPL_PINMUX);
> +}
> +
> +static int tegra_pcie_deinit_controller(struct tegra_pcie_dw *pcie)
> +{
> +	tegra_pcie_downstream_dev_to_D0(pcie);
> +	dw_pcie_host_deinit(&pcie->pci.pp);
> +	tegra_pcie_dw_pme_turnoff(pcie);
> +	return __deinit_controller(pcie, false);
> +}
> +
> +static int tegra_pcie_config_rp(struct tegra_pcie_dw *pcie)
> +{
> +	struct pcie_port *pp = &pcie->pci.pp;
> +	struct device *dev = pcie->dev;
> +	char *name;
> +	int ret;
> +
> +	if (IS_ENABLED(CONFIG_PCI_MSI)) {
> +		pp->msi_irq = of_irq_get_byname(dev->of_node, "msi");
> +		if (!pp->msi_irq) {
> +			dev_err(dev, "Failed to get MSI interrupt\n");
> +			return -ENODEV;
> +		}
> +	}
> +
> +	pm_runtime_enable(dev);
> +	ret = pm_runtime_get_sync(dev);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to get runtime sync for PCIe dev: %d\n",
> +			ret);
> +		pm_runtime_disable(dev);
> +		return ret;
> +	}
> +
> +	tegra_pcie_init_controller(pcie);
> +
> +	pcie->link_state = tegra_pcie_dw_link_up(&pcie->pci);
> +
> +	if (!pcie->link_state) {
> +		ret = -ENOMEDIUM;
> +		goto fail_host_init;
> +	}
> +
> +	name = devm_kasprintf(dev, GFP_KERNEL, "%pOFP", dev->of_node);
> +	if (!name) {
> +		ret = -ENOMEM;
> +		goto fail_host_init;
> +	}
> +
> +	pcie->debugfs = debugfs_create_dir(name, NULL);
> +	if (!pcie->debugfs)
> +		dev_err(dev, "Failed to create debugfs\n");
> +	else
> +		init_debugfs(pcie);
> +
> +	return ret;
> +
> +fail_host_init:
> +	tegra_pcie_deinit_controller(pcie);
> +	pm_runtime_put_sync(dev);
> +	pm_runtime_disable(dev);
> +	return ret;
> +}
> +
> +static int tegra_pcie_dw_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct resource *atu_dma_res;
> +	struct tegra_pcie_dw *pcie;
> +	struct resource *dbi_res;
> +	struct pcie_port *pp;
> +	struct dw_pcie *pci;
> +	struct phy **phys;
> +	char *name;
> +	int ret;
> +	u32 i;
> +
> +	pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
> +	if (!pcie)
> +		return -ENOMEM;
> +
> +	pci = &pcie->pci;
> +	pci->dev = &pdev->dev;
> +	pci->ops = &tegra_dw_pcie_ops;
> +	pp = &pci->pp;
> +	pcie->dev = &pdev->dev;
> +
> +	ret = tegra_pcie_dw_parse_dt(pcie);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to parse device tree: %d\n", ret);
> +		return ret;
> +	}
> +
> +	pcie->pex_ctl_supply = devm_regulator_get(dev, "vddio-pex-ctl");
> +	if (IS_ERR(pcie->pex_ctl_supply)) {
> +		dev_err(dev, "Failed to get regulator: %ld\n",
> +			PTR_ERR(pcie->pex_ctl_supply));
> +		return PTR_ERR(pcie->pex_ctl_supply);
> +	}
> +
> +	pcie->core_clk = devm_clk_get(dev, "core");
> +	if (IS_ERR(pcie->core_clk)) {
> +		dev_err(dev, "Failed to get core clock: %ld\n",
> +			PTR_ERR(pcie->core_clk));
> +		return PTR_ERR(pcie->core_clk);
> +	}
> +
> +	pcie->appl_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +						      "appl");
> +	if (!pcie->appl_res) {
> +		dev_err(dev, "Failed to find \"appl\" region\n");
> +		return PTR_ERR(pcie->appl_res);
> +	}
> +
> +	pcie->appl_base = devm_ioremap_resource(dev, pcie->appl_res);
> +	if (IS_ERR(pcie->appl_base))
> +		return PTR_ERR(pcie->appl_base);
> +
> +	pcie->core_apb_rst = devm_reset_control_get(dev, "apb");
> +	if (IS_ERR(pcie->core_apb_rst)) {
> +		dev_err(dev, "Failed to get APB reset: %ld\n",
> +			PTR_ERR(pcie->core_apb_rst));
> +		return PTR_ERR(pcie->core_apb_rst);
> +	}
> +
> +	phys = devm_kcalloc(dev, pcie->phy_count, sizeof(*phys), GFP_KERNEL);
> +	if (!phys)
> +		return PTR_ERR(phys);
> +
> +	for (i = 0; i < pcie->phy_count; i++) {
> +		name = kasprintf(GFP_KERNEL, "p2u-%u", i);
> +		if (!name) {
> +			dev_err(dev, "Failed to create P2U string\n");
> +			return -ENOMEM;
> +		}
> +		phys[i] = devm_phy_get(dev, name);
> +		kfree(name);
> +		if (IS_ERR(phys[i])) {
> +			ret = PTR_ERR(phys[i]);
> +			dev_err(dev, "Failed to get PHY: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	pcie->phys = phys;
> +
> +	dbi_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
> +	if (!dbi_res) {
> +		dev_err(dev, "Failed to find \"dbi\" region\n");
> +		return PTR_ERR(dbi_res);
> +	}
> +	pcie->dbi_res = dbi_res;
> +
> +	pci->dbi_base = devm_ioremap_resource(dev, dbi_res);
> +	if (IS_ERR(pci->dbi_base))
> +		return PTR_ERR(pci->dbi_base);
> +
> +	/* Tegra HW locates DBI2 at a fixed offset from DBI */
> +	pci->dbi_base2 = pci->dbi_base + 0x1000;
> +
> +	atu_dma_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +						   "atu_dma");
> +	if (!atu_dma_res) {
> +		dev_err(dev, "Failed to find \"atu_dma\" region\n");
> +		return PTR_ERR(atu_dma_res);
> +	}
> +	pcie->atu_dma_res = atu_dma_res;
> +
> +	pci->atu_base = devm_ioremap_resource(dev, atu_dma_res);
> +	if (IS_ERR(pci->atu_base))
> +		return PTR_ERR(pci->atu_base);
> +
> +	pcie->core_rst = devm_reset_control_get(dev, "core");
> +	if (IS_ERR(pcie->core_rst)) {
> +		dev_err(dev, "Failed to get core reset: %ld\n",
> +			PTR_ERR(pcie->core_rst));
> +		return PTR_ERR(pcie->core_rst);
> +	}
> +
> +	pp->irq = platform_get_irq_byname(pdev, "intr");
> +	if (!pp->irq) {
> +		dev_err(dev, "Failed to get \"intr\" interrupt\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = devm_request_irq(dev, pp->irq, tegra_pcie_irq_handler,
> +			       IRQF_SHARED, "tegra-pcie-intr", pcie);
> +	if (ret) {
> +		dev_err(dev, "Failed to request IRQ %d: %d\n", pp->irq, ret);
> +		return ret;
> +	}
> +
> +	pcie->bpmp = tegra_bpmp_get(dev);
> +	if (IS_ERR(pcie->bpmp))
> +		return PTR_ERR(pcie->bpmp);
> +
> +	platform_set_drvdata(pdev, pcie);
> +
> +	ret = tegra_pcie_config_rp(pcie);
> +	if (ret && ret != -ENOMEDIUM)
> +		goto fail;
> +	else
> +		return 0;
> +
> +fail:
> +	tegra_bpmp_put(pcie->bpmp);
> +	return ret;
> +}
> +
> +static int tegra_pcie_dw_remove(struct platform_device *pdev)
> +{
> +	struct tegra_pcie_dw *pcie = platform_get_drvdata(pdev);
> +
> +	if (!pcie->link_state)
> +		return 0;
> +
> +	debugfs_remove_recursive(pcie->debugfs);
> +	tegra_pcie_deinit_controller(pcie);
> +	pm_runtime_put_sync(pcie->dev);
> +	pm_runtime_disable(pcie->dev);
> +	tegra_bpmp_put(pcie->bpmp);
> +
> +	return 0;
> +}
> +
> +static int tegra_pcie_dw_suspend_late(struct device *dev)
> +{
> +	struct tegra_pcie_dw *pcie = dev_get_drvdata(dev);
> +	u32 val;
> +
> +	if (!pcie->link_state)
> +		return 0;
> +
> +	/* Enable HW_HOT_RST mode */
> +	val = appl_readl(pcie, APPL_CTRL);
> +	val &= ~(APPL_CTRL_HW_HOT_RST_MODE_MASK <<
> +		 APPL_CTRL_HW_HOT_RST_MODE_SHIFT);
> +	val |= APPL_CTRL_HW_HOT_RST_EN;
> +	appl_writel(pcie, val, APPL_CTRL);
> +
> +	return 0;
> +}
> +
> +static int tegra_pcie_dw_suspend_noirq(struct device *dev)
> +{
> +	struct tegra_pcie_dw *pcie = dev_get_drvdata(dev);
> +
> +	if (!pcie->link_state)
> +		return 0;
> +
> +	/* Save MSI interrupt vector */
> +	pcie->msi_ctrl_int = dw_pcie_readl_dbi(&pcie->pci,
> +					       PORT_LOGIC_MSI_CTRL_INT_0_EN);
> +	tegra_pcie_downstream_dev_to_D0(pcie);
> +	tegra_pcie_dw_pme_turnoff(pcie);
> +	return __deinit_controller(pcie, true);
> +}
> +
> +static int tegra_pcie_dw_resume_noirq(struct device *dev)
> +{
> +	struct tegra_pcie_dw *pcie = dev_get_drvdata(dev);
> +	int ret;
> +
> +	if (!pcie->link_state)
> +		return 0;
> +
> +	ret = tegra_pcie_config_controller(pcie, true);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = tegra_pcie_dw_host_init(&pcie->pci.pp);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to init host: %d\n", ret);
> +		goto fail_host_init;
> +	}
> +
> +	/* Restore MSI interrupt vector */
> +	dw_pcie_writel_dbi(&pcie->pci, PORT_LOGIC_MSI_CTRL_INT_0_EN,
> +			   pcie->msi_ctrl_int);
> +
> +	return 0;
> +fail_host_init:
> +	return __deinit_controller(pcie, true);
> +}
> +
> +static int tegra_pcie_dw_resume_early(struct device *dev)
> +{
> +	struct tegra_pcie_dw *pcie = dev_get_drvdata(dev);
> +	u32 val;
> +
> +	if (!pcie->link_state)
> +		return 0;
> +
> +	/* Disable HW_HOT_RST mode */
> +	val = appl_readl(pcie, APPL_CTRL);
> +	val &= ~(APPL_CTRL_HW_HOT_RST_MODE_MASK <<
> +		 APPL_CTRL_HW_HOT_RST_MODE_SHIFT);
> +	val |= APPL_CTRL_HW_HOT_RST_MODE_IMDT_RST <<
> +	       APPL_CTRL_HW_HOT_RST_MODE_SHIFT;
> +	val &= ~APPL_CTRL_HW_HOT_RST_EN;
> +	appl_writel(pcie, val, APPL_CTRL);
> +
> +	return 0;
> +}
> +
> +static void tegra_pcie_dw_shutdown(struct platform_device *pdev)
> +{
> +	struct tegra_pcie_dw *pcie = platform_get_drvdata(pdev);
> +
> +	if (!pcie->link_state)
> +		return;
> +
> +	debugfs_remove_recursive(pcie->debugfs);
> +	tegra_pcie_downstream_dev_to_D0(pcie);
> +
> +	disable_irq(pcie->pci.pp.irq);
> +	if (IS_ENABLED(CONFIG_PCI_MSI))
> +		disable_irq(pcie->pci.pp.msi_irq);
> +
> +	tegra_pcie_dw_pme_turnoff(pcie);
> +	__deinit_controller(pcie, false);
> +}
> +
> +static const struct of_device_id tegra_pcie_dw_of_match[] = {
> +	{
> +		.compatible = "nvidia,tegra194-pcie",
> +	},
> +	{},
> +};
> +
> +static const struct dev_pm_ops tegra_pcie_dw_pm_ops = {
> +	.suspend_late = tegra_pcie_dw_suspend_late,
> +	.suspend_noirq = tegra_pcie_dw_suspend_noirq,
> +	.resume_noirq = tegra_pcie_dw_resume_noirq,
> +	.resume_early = tegra_pcie_dw_resume_early,
> +};
> +
> +static struct platform_driver tegra_pcie_dw_driver = {
> +	.probe = tegra_pcie_dw_probe,
> +	.remove = tegra_pcie_dw_remove,
> +	.shutdown = tegra_pcie_dw_shutdown,
> +	.driver = {
> +		.name	= "tegra194-pcie",
> +		.pm = &tegra_pcie_dw_pm_ops,
> +		.of_match_table = tegra_pcie_dw_of_match,
> +	},
> +};
> +module_platform_driver(tegra_pcie_dw_driver);
> +
> +MODULE_DEVICE_TABLE(of, tegra_pcie_dw_of_match);
> +
> +MODULE_AUTHOR("Vidya Sagar <vidyas@nvidia.com>");
> +MODULE_DESCRIPTION("NVIDIA PCIe host controller driver");
> +MODULE_LICENSE("GPL v2");
> 

^ permalink raw reply

* Re: [PATCH V2 4/4] thermal: qoriq: Add clock operations
From: Fabio Estevam @ 2019-07-30  3:56 UTC (permalink / raw)
  To: Anson Huang
  Cc: rui.zhang, Eduardo Valentin, Daniel Lezcano, Rob Herring,
	Mark Rutland, linux-pm@vger.kernel.org,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	linux-kernel, dl-linux-imx
In-Reply-To: <DB3PR0402MB39161F938C25012015B9739CF5DC0@DB3PR0402MB3916.eurprd04.prod.outlook.com>

Hi Anson,

On Tue, Jul 30, 2019 at 12:00 AM Anson Huang <anson.huang@nxp.com> wrote:

> Shawn already applied the patch, and Abel has the AHB clock patch to fix that,
> so just wait for the AHB clock patch in instead of revert the TMU clock patch?

Sorry, I don't understand Abel's patch as there is not a proper
description of what it tries to fix.

If I understand correctly Abel's patch is not the proper fix and the
real fix for the kernel TMU hang in linux-next is to manage the TMU
clock inside the TMU driver, like you did in this patch.

To avoid a revert one possible solution would be to send only this one
as a fix for 5.3. You can point that it Fixes the commit that adds
i.MX8M support for the TMU driver.

^ permalink raw reply

* Re: [PATCH v4 13/13] ARM: dts: sunxi: Switch from phy to phy-handle
From: Chen-Yu Tsai @ 2019-07-30  3:51 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Mark Rutland, Rob Herring, Frank Rowand, David S . Miller,
	Maxime Coquelin, Alexandre Torgue, netdev, linux-arm-kernel,
	devicetree, linux-stm32, Maxime Chevallier, Antoine Ténart,
	Andrew Lunn, Florian Fainelli, Heiner Kallweit
In-Reply-To: <a1a33392c64c71099021fb49cc811a30790d40a8.1561649505.git-series.maxime.ripard@bootlin.com>

On Thu, Jun 27, 2019 at 11:32 PM Maxime Ripard
<maxime.ripard@bootlin.com> wrote:
>
> The phy device tree property has been deprecated in favor of phy-handle,
> let's replace it.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>

This patch breaks Ethernet on all my dwmac-sunxi, i.e. old GMAC, boards, with
the following error messages:

    sun7i-dwmac 1c50000.ethernet eth0: no phy at addr -1
    sun7i-dwmac 1c50000.ethernet eth0: stmmac_open: Cannot attach to
PHY (error: -19)

Reverting this patch fixes it.

It also breaks the A10/A10s, but that's probably because the sun4i-emac
driver doesn't recognize the "phy-handle" property.

ChenYu

^ permalink raw reply

* Re: [PATCH v3 0/6] Introduce Bandwidth OPPs for interconnect paths
From: Viresh Kumar @ 2019-07-30  3:01 UTC (permalink / raw)
  To: Saravana Kannan
  Cc: Georgi Djakov, Rob Herring, Mark Rutland, Viresh Kumar,
	Nishanth Menon, Stephen Boyd, Rafael J. Wysocki, Vincent Guittot,
	Sweeney, Sean, David Dai, Rajendra Nayak, Sibi Sankar,
	Bjorn Andersson, Evan Green, Android Kernel Team, Linux PM,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS, LKML
In-Reply-To: <CAGETcx_7fK20VZ6Zn07Z+Ran1_O7gSPohck_tg-aEr5oONQ5iA@mail.gmail.com>

On 29-07-19, 13:12, Saravana Kannan wrote:
> On Mon, Jul 29, 2019 at 2:24 AM Viresh Kumar <viresh.kumar@linaro.org> wrote:
> >
> > On 18-07-19, 21:12, Saravana Kannan wrote:
> > > On Wed, Jul 17, 2019 at 10:37 PM Viresh Kumar <viresh.kumar@linaro.org> wrote:
> > > > I would like
> > > > to put this data in the GPU OPP table only. What about putting a
> > > > range in the GPU OPP table for the Bandwidth if it can change so much
> > > > for the same frequency.
> > >
> > > I don't think the range is going to work.
> >
> > Any specific reason for that ?
> 
> The next sentence was literally explaining this :) Fine to debate
> that, but ignoring that and asking this question is kinda funny.

Okay, but ...
 
> > > If a GPU is doing purely
> > > computational work, it's not unreasonable for it to vote for the
> > > lowest bandwidth for any GPU frequency.

... it wasn't clear to me even after reading this sentence again now
:)

I understand that you may have to vote for the lowest bandwidth but
that doesn't explain why a range can't work (sorry if it was just me
who doesn't understood it :)).

> > I think that is fine, but if the GPU is able to find how much
> > bandwidth it needs why can't it just pass that value without needing
> > to have another OPP table for the path ?
> 
> You were asking this question in the context of "can the GPU OPP just
> list all the range of bandwidth it might use per GPU frequency". My point
> is that the range would be useless because it would the entire
> available bandwidth range (because purely compute work might not need
> any bandwidth).

If it is useless to have entire range here, then why bother providing
one ? Why can't the GPU request what it needs in exact terms, based on
its calculations ? And then based on these requests, let the
interconnect find what's the best/stable values it really wants to
program the path for (and for that the interconnect can use its own
OPP table, which would be fine).

> Whereas, what the GPU's algorithm actually needs might be the list of
> "useful" bandwidth levels to use.

Hmm, I am not sure GPU's algorithm needs this table AFAIU based on all
the conversations we had until now. It is very capable of finding how
much bandwidth it needs, you just want the GPU driver to finally align
that with a stable bandwidth for the platform later on. And what I am
asking is that it is not required for the end driver to look for
stable values, it just requests what it wants and let the interconnect
core/driver decide the stable values.

Very much like the clock framework, most of the user drivers just ask
for a clk value to be programmed and it is the clock driver which
keeps a table of the stable values and then aligns the requested value
to one of those.

> Also, as we add more ICC request properties, this range idea will not scale.

I am not sure about what kind of properties there can be and where
should they land. My feedback is completely based on what you have
presented in this patchset.

-- 
viresh

^ permalink raw reply

* RE: [PATCH V2 4/4] thermal: qoriq: Add clock operations
From: Anson Huang @ 2019-07-30  3:00 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: rui.zhang, Eduardo Valentin, Daniel Lezcano, Rob Herring,
	Mark Rutland, linux-pm@vger.kernel.org,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS  <devicetree@vger.kernel.org>, linux-kernel,
	dl-linux-imx
In-Reply-To: <CAOMZO5AgP0BrHyFKz78rsEz1XQMgSNzMmtTV6Q+GYtCGBiFMEQ@mail.gmail.com>

Hi, Fabio

> On Mon, Jul 29, 2019 at 6:04 AM <Anson.Huang@nxp.com> wrote:
> >
> > From: Anson Huang <Anson.Huang@nxp.com>
> >
> > Some platforms like i.MX8MQ has clock control for this module, need to
> > add clock operations to make sure the driver is working properly.
> 
> I haven't seen this series earlier, and I have sent a similar patch for Guido to
> test.
> 
> Since this patch solves a hang problem, I would suggest that this one
> becomes the first of the series.

Done in V3.

> 
> Also, the "clk: imx8mq: Remove CLK_IS_CRITICAL flag for
> IMX8MQ_CLK_TMU_ROOT" should only be applied after this one in order to
> avoid the hang.

Shawn already applied the patch, and Abel has the AHB clock patch to fix that,
so just wait for the AHB clock patch in instead of revert the TMU clock patch? 

> 
> >
> > Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
> > Reviewed-by: Guido Günther <agx@sigxcpu.org>
> > ---
> > Changes since V1:
> >         - use devm_clk_get_optional() instead of devm_clk_get().
> > ---
> >  drivers/thermal/qoriq_thermal.c | 21 +++++++++++++++++++++
> >  1 file changed, 21 insertions(+)
> >
> > diff --git a/drivers/thermal/qoriq_thermal.c
> > b/drivers/thermal/qoriq_thermal.c index 2b2f79b..0ae45c0 100644
> > --- a/drivers/thermal/qoriq_thermal.c
> > +++ b/drivers/thermal/qoriq_thermal.c
> > @@ -2,6 +2,7 @@
> >  //
> >  // Copyright 2016 Freescale Semiconductor, Inc.
> >
> > +#include <linux/clk.h>
> >  #include <linux/module.h>
> >  #include <linux/platform_device.h>
> >  #include <linux/err.h>
> > @@ -72,6 +73,7 @@ struct qoriq_sensor {
> >
> >  struct qoriq_tmu_data {
> >         struct qoriq_tmu_regs __iomem *regs;
> > +       struct clk *clk;
> >         bool little_endian;
> >         struct qoriq_sensor     *sensor[SITES_MAX];
> >  };
> > @@ -208,6 +210,16 @@ static int qoriq_tmu_probe(struct platform_device
> *pdev)
> >                 return PTR_ERR(data->regs);
> >         }
> >
> > +       data->clk = devm_clk_get_optional(&pdev->dev, NULL);
> > +       if (IS_ERR(data->clk))
> > +               return PTR_ERR(data->clk);
> > +
> > +       ret = clk_prepare_enable(data->clk);
> > +       if (ret) {
> > +               dev_err(&pdev->dev, "Failed to enable clock\n");
> > +               return ret;
> > +       }
> > +
> >         qoriq_tmu_init_device(data);    /* TMU initialization */
> >
> >         ret = qoriq_tmu_calibration(pdev);      /* TMU calibration */
> 
> In case of failure the TMU clock should be disabled in the error path.

Done in V3.

Thanks,
Anson.

^ permalink raw reply

* Re: [PATCH v4 0/3] Introduce Bandwidth OPPs for interconnects
From: Viresh Kumar @ 2019-07-30  2:46 UTC (permalink / raw)
  To: Saravana Kannan, Georgi Djakov, Sibi Sankar
  Cc: Rob Herring, Mark Rutland, Viresh Kumar, Nishanth Menon,
	Stephen Boyd, Rafael J. Wysocki, Vincent Guittot, Sweeney, Sean,
	David Dai, adharmap, Rajendra Nayak, Bjorn Andersson, Evan Green,
	Android Kernel Team, Linux PM,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS, LKML
In-Reply-To: <CAGETcx8OBFGgP1-hj717Sk-_N95-kacVsz0yb288n3pej12n1Q@mail.gmail.com>

On 29-07-19, 13:16, Saravana Kannan wrote:
> Sibi might be working on doing that for the SDM845 CPUfreq driver.
> Georgi could also change his GPU driver use case to use this BW OPP
> table and required-opps.
> 
> The problem is that people don't want to start using this until we
> decide on the DT representation. So it's like a chicken and egg
> situation.

Yeah, I agree to that.

@Georgi and @Sibi: This is your chance to speak up about the proposal
from Saravana and if you find anything wrong with them. And specially
that it is mostly about interconnects here, I would like to have an
explicit Ack from Georgi on this.

And if you guys are all okay about this then please at least commit
that you will convert your stuff based on this in coming days.

-- 
viresh

^ permalink raw reply

* Re: [PATCH 2/4] net: phy: Add mdio-aspeed
From: Andrew Jeffery @ 2019-07-30  2:23 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: netdev, David Miller, Rob Herring, mark.rutland, Joel Stanley,
	Florian Fainelli, Heiner Kallweit, devicetree, linux-arm-kernel,
	linux-aspeed, linux-kernel
In-Reply-To: <20190729133250.GB4110@lunn.ch>



On Mon, 29 Jul 2019, at 23:03, Andrew Lunn wrote:
> On Mon, Jul 29, 2019 at 02:09:24PM +0930, Andrew Jeffery wrote:
> > The AST2600 design separates the MDIO controllers from the MAC, which is
> > where they were placed in the AST2400 and AST2500. Further, the register
> > interface is reworked again, so now we have three possible different
> > interface implementations, however this driver only supports the
> > interface provided by the AST2600. The AST2400 and AST2500 will continue
> > to be supported by the MDIO support embedded in the FTGMAC100 driver.
> > 
> > Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> > ---
> >  drivers/net/phy/Kconfig       |  13 +++
> >  drivers/net/phy/Makefile      |   1 +
> >  drivers/net/phy/mdio-aspeed.c | 159 ++++++++++++++++++++++++++++++++++
> >  3 files changed, 173 insertions(+)
> >  create mode 100644 drivers/net/phy/mdio-aspeed.c
> > 
> > diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
> > index 20f14c5fbb7e..206d8650ee7f 100644
> > --- a/drivers/net/phy/Kconfig
> > +++ b/drivers/net/phy/Kconfig
> > @@ -21,6 +21,19 @@ config MDIO_BUS
> >  
> >  if MDIO_BUS
> >  
> > +config MDIO_ASPEED
> > +	tristate "ASPEED MDIO bus controller"
> > +	depends on ARCH_ASPEED || COMPILE_TEST
> > +	depends on OF_MDIO && HAS_IOMEM
> > +	help
> > +	  This module provides a driver for the independent MDIO bus
> > +	  controllers found in the ASPEED AST2600 SoC. This is a driver for the
> > +	  third revision of the ASPEED MDIO register interface - the first two
> > +	  revisions are the "old" and "new" interfaces found in the AST2400 and
> > +	  AST2500, embedded in the MAC. For legacy reasons, FTGMAC100 driver
> > +	  continues to drive the embedded MDIO controller for the AST2400 and
> > +	  AST2500 SoCs, so say N if AST2600 support is not required.
> > +
> >  config MDIO_BCM_IPROC
> >  	tristate "Broadcom iProc MDIO bus controller"
> >  	depends on ARCH_BCM_IPROC || COMPILE_TEST
> > diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
> > index 839acb292c38..ba07c27e4208 100644
> > --- a/drivers/net/phy/Makefile
> > +++ b/drivers/net/phy/Makefile
> > @@ -22,6 +22,7 @@ libphy-$(CONFIG_LED_TRIGGER_PHY)	+= phy_led_triggers.o
> >  obj-$(CONFIG_PHYLINK)		+= phylink.o
> >  obj-$(CONFIG_PHYLIB)		+= libphy.o
> >  
> > +obj-$(CONFIG_MDIO_ASPEED)	+= mdio-aspeed.o
> >  obj-$(CONFIG_MDIO_BCM_IPROC)	+= mdio-bcm-iproc.o
> >  obj-$(CONFIG_MDIO_BCM_UNIMAC)	+= mdio-bcm-unimac.o
> >  obj-$(CONFIG_MDIO_BITBANG)	+= mdio-bitbang.o
> > diff --git a/drivers/net/phy/mdio-aspeed.c b/drivers/net/phy/mdio-aspeed.c
> > new file mode 100644
> > index 000000000000..71496a9ff54a
> > --- /dev/null
> > +++ b/drivers/net/phy/mdio-aspeed.c
> > @@ -0,0 +1,159 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/* Copyright (C) 2019 IBM Corp. */
> > +
> > +#include <linux/bitfield.h>
> > +#include <linux/delay.h>
> > +#include <linux/mdio.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_mdio.h>
> > +#include <linux/phy.h>
> > +#include <linux/platform_device.h>
> > +
> > +#define DRV_NAME "mdio-aspeed"
> > +
> > +#define ASPEED_MDIO_CTRL		0x0
> > +#define   ASPEED_MDIO_CTRL_FIRE		BIT(31)
> > +#define   ASPEED_MDIO_CTRL_ST		BIT(28)
> > +#define     ASPEED_MDIO_CTRL_ST_C45	0
> > +#define     ASPEED_MDIO_CTRL_ST_C22	1
> > +#define   ASPEED_MDIO_CTRL_OP		GENMASK(27, 26)
> > +#define     MDIO_C22_OP_WRITE		0b01
> > +#define     MDIO_C22_OP_READ		0b10
> > +#define   ASPEED_MDIO_CTRL_PHYAD	GENMASK(25, 21)
> > +#define   ASPEED_MDIO_CTRL_REGAD	GENMASK(20, 16)
> > +#define   ASPEED_MDIO_CTRL_MIIWDATA	GENMASK(15, 0)
> > +
> > +#define ASPEED_MDIO_DATA		0x4
> > +#define   ASPEED_MDIO_DATA_MDC_THRES	GENMASK(31, 24)
> > +#define   ASPEED_MDIO_DATA_MDIO_EDGE	BIT(23)
> > +#define   ASPEED_MDIO_DATA_MDIO_LATCH	GENMASK(22, 20)
> > +#define   ASPEED_MDIO_DATA_IDLE		BIT(16)
> > +#define   ASPEED_MDIO_DATA_MIIRDATA	GENMASK(15, 0)
> > +
> > +#define ASPEED_MDIO_RETRIES		10
> > +
> > +struct aspeed_mdio {
> > +	void __iomem *base;
> > +};
> > +
> > +static int aspeed_mdio_read(struct mii_bus *bus, int addr, int regnum)
> > +{
> > +	struct aspeed_mdio *ctx = bus->priv;
> > +	u32 ctrl;
> > +	int i;
> > +
> > +	dev_dbg(&bus->dev, "%s: addr: %d, regnum: %d\n", __func__, addr,
> > +		regnum);
> > +
> > +	/* Just clause 22 for the moment */
> > +	ctrl = ASPEED_MDIO_CTRL_FIRE
> 
> Hi Andrew
> 
> In the binding, you say C45 is supported. Here you don't. It would be
> nice to be consistent.

Right - but the bindings describe the hardware, and the hardware is capable.
Just that the driver as it stands can't drive it that way.

Having said that I do need to do more digging to understand how to cater to
C45 PHYs wrt the read/write calls.

> 
> 
> > +		| FIELD_PREP(ASPEED_MDIO_CTRL_ST, ASPEED_MDIO_CTRL_ST_C22)
> > +		| FIELD_PREP(ASPEED_MDIO_CTRL_OP, MDIO_C22_OP_READ)
> > +		| FIELD_PREP(ASPEED_MDIO_CTRL_PHYAD, addr)
> > +		| FIELD_PREP(ASPEED_MDIO_CTRL_REGAD, regnum);
> > +
> > +	iowrite32(ctrl, ctx->base + ASPEED_MDIO_CTRL);
> > +
> > +	for (i = 0; i < ASPEED_MDIO_RETRIES; i++) {
> > +		u32 data;
> > +
> > +		data = ioread32(ctx->base + ASPEED_MDIO_DATA);
> > +		if (data & ASPEED_MDIO_DATA_IDLE)
> > +			return FIELD_GET(ASPEED_MDIO_DATA_MIIRDATA, data);
> > +
> > +		udelay(100);
> > +	}
> 
> One of the readx_poll_timeout functions could be used.

Thanks, I'll take a look.

> 
> > +
> > +	dev_err(&bus->dev, "MDIO read failed\n");
> > +	return -EIO;
> > +}
> > +
> > +static int aspeed_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val)
> > +{
> > +	struct aspeed_mdio *ctx = bus->priv;
> > +	u32 ctrl;
> > +	int i;
> > +
> > +	dev_dbg(&bus->dev, "%s: addr: %d, regnum: %d, val: 0x%x\n",
> > +		__func__, addr, regnum, val);
> > +
> > +	/* Just clause 22 for the moment */
> > +	ctrl = ASPEED_MDIO_CTRL_FIRE
> > +		| FIELD_PREP(ASPEED_MDIO_CTRL_ST, ASPEED_MDIO_CTRL_ST_C22)
> > +		| FIELD_PREP(ASPEED_MDIO_CTRL_OP, MDIO_C22_OP_WRITE)
> > +		| FIELD_PREP(ASPEED_MDIO_CTRL_PHYAD, addr)
> > +		| FIELD_PREP(ASPEED_MDIO_CTRL_REGAD, regnum)
> > +		| FIELD_PREP(ASPEED_MDIO_CTRL_MIIWDATA, val);
> > +
> > +	iowrite32(ctrl, ctx->base + ASPEED_MDIO_CTRL);
> > +
> > +	for (i = 0; i < ASPEED_MDIO_RETRIES; i++) {
> > +		ctrl = ioread32(ctx->base + ASPEED_MDIO_CTRL);
> > +		if (!(ctrl & ASPEED_MDIO_CTRL_FIRE))
> > +			return 0;
> > +
> > +		udelay(100);
> > +	}
> 
> readx_poll_timeout() here as well.
> 
> Otherwise this looks good.

Thanks for the review!

Andrew

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox