diff mbox

[v3,7/9] PCI: imx6: add imx6sx pcie support

Message ID 1411966997-27118-8-git-send-email-r65037@freescale.com (mailing list archive)
State New, archived
Delegated to: Bjorn Helgaas
Headers show

Commit Message

Richard Zhu Sept. 29, 2014, 5:03 a.m. UTC
- imx6sx pcie has its own standalone pcie power supply.
In order to turn on the imx6sx pcie power during
initialization. Add the pcie regulator and the gpc regmap
into the imx6sx pcie structure.
- imx6sx pcie has the new added reset mechanism, add the
reset operations into the initialization.
- Register one PM call-back, enter/exit L2 state of the ASPM
during system suspend/resume.
- disp_axi clock is required by pcie inbound axi port actually.
Add one more clock for imx6sx pcie.

Signed-off-by: Richard Zhu <r65037@freescale.com>
---
 drivers/pci/host/pci-imx6.c | 162 +++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 144 insertions(+), 18 deletions(-)

Comments

Lucas Stach Sept. 29, 2014, 10:18 a.m. UTC | #1
Am Montag, den 29.09.2014, 13:03 +0800 schrieb Richard Zhu:
> - imx6sx pcie has its own standalone pcie power supply.
> In order to turn on the imx6sx pcie power during
> initialization. Add the pcie regulator and the gpc regmap
> into the imx6sx pcie structure.
> - imx6sx pcie has the new added reset mechanism, add the
> reset operations into the initialization.
> - Register one PM call-back, enter/exit L2 state of the ASPM
> during system suspend/resume.
> - disp_axi clock is required by pcie inbound axi port actually.
> Add one more clock for imx6sx pcie.
> 
> Signed-off-by: Richard Zhu <r65037@freescale.com>
> ---
>  drivers/pci/host/pci-imx6.c | 162 +++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 144 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c
> index eac96fb..be953aa 100644
> --- a/drivers/pci/host/pci-imx6.c
> +++ b/drivers/pci/host/pci-imx6.c
> @@ -18,12 +18,16 @@
>  #include <linux/mfd/syscon.h>
>  #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
>  #include <linux/module.h>
> +#include <linux/of_address.h>

Why do you need this include?

> +#include <linux/of_device.h>
>  #include <linux/of_gpio.h>
>  #include <linux/pci.h>
>  #include <linux/platform_device.h>
>  #include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
>  #include <linux/resource.h>
>  #include <linux/signal.h>
> +#include <linux/syscore_ops.h>
>  #include <linux/types.h>
>  #include <linux/interrupt.h>
>  
> @@ -35,11 +39,15 @@ struct imx6_pcie {
>  	int			reset_gpio;
>  	struct clk		*pcie_bus;
>  	struct clk		*pcie_phy;
> +	struct clk		*disp_axi;

This needs a more descriptive name, just like the binding.

>  	struct clk		*pcie;
>  	struct pcie_port	pp;
>  	struct regmap		*iomuxc_gpr;
> +	struct regmap		*gpc_ips_reg;
> +	struct regulator	*pcie_regulator;
>  	void __iomem		*mem_base;
>  };
> +static struct imx6_pcie *imx6_pcie;
>  
>  /* PCIe Root Complex registers (memory-mapped) */
>  #define PCIE_RC_LCR				0x7c
> @@ -77,6 +85,18 @@ struct imx6_pcie {
>  #define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5)
>  #define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3)
>  
> +/* GPC PCIE PHY bit definitions */
> +#define GPC_CNTR			0
> +#define GPC_CNTR_PCIE_PHY_PUP_REQ	BIT(7)
> +
> +static inline bool is_imx6sx_pcie(struct imx6_pcie *imx6_pcie)
> +{
> +	struct pcie_port *pp = &imx6_pcie->pp;
> +	struct device_node *np = pp->dev->of_node;
> +
> +	return of_device_is_compatible(np, "fsl,imx6sx-pcie");
> +}
> +
>  static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val)
>  {
>  	u32 val;
> @@ -275,18 +295,29 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
>  		goto err_pcie;
>  	}
>  
> -	/* power up core phy and enable ref clock */
> -	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
> -			IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
> -	/*
> -	 * the async reset input need ref clock to sync internally,
> -	 * when the ref clock comes after reset, internal synced
> -	 * reset time is too short , cannot meet the requirement.
> -	 * add one ~10us delay here.
> -	 */
> -	udelay(10);
> -	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
> -			IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
> +	if (is_imx6sx_pcie(imx6_pcie)) {
> +		ret = clk_prepare_enable(imx6_pcie->disp_axi);
> +		if (ret) {
> +			dev_err(pp->dev, "unable to enable pcie clock\n");
> +			goto err_disp;
> +		}
> +
> +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
> +				IMX6SX_GPR12_PCIE_TEST_PD, 0 << 30);
> +	} else {
> +		/* power up core phy and enable ref clock */
> +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
> +				IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
> +		/*
> +		 * the async reset input need ref clock to sync internally,
> +		 * when the ref clock comes after reset, internal synced
> +		 * reset time is too short , cannot meet the requirement.
> +		 * add one ~10us delay here.
> +		 */
> +		udelay(10);
> +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
> +				IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
> +	}
>  
>  	/* allow the clocks to stabilize */
>  	usleep_range(200, 500);
> @@ -297,8 +328,19 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
>  		msleep(100);
>  		gpio_set_value(imx6_pcie->reset_gpio, 1);
>  	}
> +
> +	/*
> +	 * Release the PCIe PHY reset here, that we have set in
> +	 * imx6_pcie_init_phy() now
> +	 */
> +	if (is_imx6sx_pcie(imx6_pcie))
> +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
> +				IMX6SX_GPR5_PCIE_BTNRST, 0 << 19);
> +
>  	return 0;
>  
> +err_disp:
> +	clk_disable_unprepare(imx6_pcie->pcie);
>  err_pcie:
>  	clk_disable_unprepare(imx6_pcie->pcie_bus);
>  err_pcie_bus:
> @@ -311,6 +353,26 @@ err_pcie_phy:
>  static void imx6_pcie_init_phy(struct pcie_port *pp)
>  {
>  	struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
> +	int ret;
> +
> +	/* Power up the separate domain available on i.MX6SX */
> +	if (is_imx6sx_pcie(imx6_pcie)) {
> +		/* Force PCIe PHY reset */
> +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
> +				IMX6SX_GPR5_PCIE_BTNRST,
> +				IMX6SX_GPR5_PCIE_BTNRST);
> +
> +		regmap_update_bits(imx6_pcie->gpc_ips_reg, GPC_CNTR,
> +				GPC_CNTR_PCIE_PHY_PUP_REQ,
> +				GPC_CNTR_PCIE_PHY_PUP_REQ);
> +		regulator_set_voltage(imx6_pcie->pcie_regulator,
> +				1100000, 1100000);
> +		ret = regulator_enable(imx6_pcie->pcie_regulator);
> +		if (ret)
> +			dev_info(pp->dev, "failed to enable pcie regulator.\n");
> +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
> +				IMX6SX_GPR12_RX_EQ_MASK, IMX6SX_GPR12_RX_EQ_2);
> +	}
>  
>  	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
>  			IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
> @@ -319,7 +381,7 @@ static void imx6_pcie_init_phy(struct pcie_port *pp)
>  	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
>  			IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12);
>  	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
> -			IMX6Q_GPR12_LOS_LEVEL, 9 << 4);
> +			IMX6Q_GPR12_LOS_LEVEL, IMX6Q_GPR12_LOS_LEVEL_9);
>  
>  	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
>  			IMX6Q_GPR8_TX_DEEMPH_GEN1, 0 << 0);
> @@ -377,7 +439,8 @@ static int imx6_pcie_start_link(struct pcie_port *pp)
>  
>  	/* Start LTSSM. */
>  	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
> -			IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
> +			IMX6Q_GPR12_PCIE_CTL_2,
> +			IMX6Q_GPR12_PCIE_CTL_2);
>  
>  	ret = imx6_pcie_wait_for_link(pp);
>  	if (ret)
> @@ -553,9 +616,50 @@ static int __init imx6_add_pcie_port(struct pcie_port *pp,
>  	return 0;
>  }
>  
> +#ifdef CONFIG_PM_SLEEP
> +static int pci_imx_suspend(void)
> +{
> +	if (is_imx6sx_pcie(imx6_pcie)) {
> +		/* PM_TURN_OFF */
> +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
> +				IMX6SX_GPR12_PCIE_PM_TURN_OFF,
> +				IMX6SX_GPR12_PCIE_PM_TURN_OFF);
> +		udelay(10);
> +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
> +				IMX6SX_GPR12_PCIE_PM_TURN_OFF, 0 << 16);

Just use 0 as the last argument. Those shifts aren't adding anything and
I would like to get rid of them long term, so please don't introduce new
ones.

> +	}
> +
> +	return 0;
> +}
> +
> +static void pci_imx_resume(void)
> +{
> +	struct pcie_port *pp = &imx6_pcie->pp;
> +
> +	if (is_imx6sx_pcie(imx6_pcie)) {
> +		/* Reset iMX6SX PCIe */
> +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
> +				IMX6SX_GPR5_PCIE_PERST, IMX6SX_GPR5_PCIE_PERST);
> +		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
> +				IMX6SX_GPR5_PCIE_PERST, 0 << 18);
> +		/*
> +		 * controller maybe turn off, re-configure again
> +		 */
> +		dw_pcie_setup_rc(pp);
> +
> +		if (IS_ENABLED(CONFIG_PCI_MSI))
> +			dw_pcie_msi_cfg_restore(pp);
> +	}
> +}
> +
> +static struct syscore_ops pci_imx_syscore_ops = {
> +	.suspend = pci_imx_suspend,
> +	.resume = pci_imx_resume,
> +};
> +#endif
> +
>  static int __init imx6_pcie_probe(struct platform_device *pdev)
>  {
> -	struct imx6_pcie *imx6_pcie;
>  	struct pcie_port *pp;
>  	struct device_node *np = pdev->dev.of_node;
>  	struct resource *dbi_base;
> @@ -610,9 +714,27 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
>  		return PTR_ERR(imx6_pcie->pcie);
>  	}
>  
> -	/* Grab GPR config register range */
> -	imx6_pcie->iomuxc_gpr =
> -		 syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
> +	if (is_imx6sx_pcie(imx6_pcie)) {
> +		imx6_pcie->disp_axi = devm_clk_get(&pdev->dev, "disp_axi");
> +		if (IS_ERR(imx6_pcie->disp_axi)) {
> +			dev_err(&pdev->dev,
> +				"pcie clock source missing or invalid\n");
> +			return PTR_ERR(imx6_pcie->disp_axi);
> +		}
> +
> +		imx6_pcie->pcie_regulator = devm_regulator_get(pp->dev,
> +				"pcie_phy");
> +
> +		imx6_pcie->iomuxc_gpr =
> +			 syscon_regmap_lookup_by_compatible
> +			 ("fsl,imx6sx-iomuxc-gpr");
> +		imx6_pcie->gpc_ips_reg =
> +			 syscon_regmap_lookup_by_compatible("fsl,imx6sx-gpc");
> +	} else {
> +		imx6_pcie->iomuxc_gpr =
> +			syscon_regmap_lookup_by_compatible
> +			("fsl,imx6q-iomuxc-gpr");
> +	}
>  	if (IS_ERR(imx6_pcie->iomuxc_gpr)) {
>  		dev_err(&pdev->dev, "unable to find iomuxc registers\n");
>  		return PTR_ERR(imx6_pcie->iomuxc_gpr);
> @@ -623,6 +745,9 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
>  		return ret;
>  
>  	platform_set_drvdata(pdev, imx6_pcie);
> +#ifdef CONFIG_PM_SLEEP
> +	register_syscore_ops(&pci_imx_syscore_ops);
> +#endif
>  	return 0;
>  }
>  
> @@ -636,6 +761,7 @@ static void imx6_pcie_shutdown(struct platform_device *pdev)
>  
>  static const struct of_device_id imx6_pcie_of_match[] = {
>  	{ .compatible = "fsl,imx6q-pcie", },
> +	{ .compatible = "fsl,imx6sx-pcie", },
>  	{},
>  };
>  MODULE_DEVICE_TABLE(of, imx6_pcie_of_match);
Richard Zhu Sept. 30, 2014, 2:37 a.m. UTC | #2
SGkgTHVjYXM6DQpUaGFua3MgZm9yIHlvdXIgcmV2aWV3IGNvbW1lbnRzLg0KDQo+IC0tLS0tT3Jp
Z2luYWwgTWVzc2FnZS0tLS0tDQo+IEZyb206IEx1Y2FzIFN0YWNoIFttYWlsdG86bC5zdGFjaEBw
ZW5ndXRyb25peC5kZV0NCj4gU2VudDogTW9uZGF5LCBTZXB0ZW1iZXIgMjksIDIwMTQgNjoxOSBQ
TQ0KPiBUbzogWmh1IFJpY2hhcmQtUjY1MDM3DQo+IENjOiBsaW51eC1wY2ktb3duZXJAdmdlci5r
ZXJuZWwub3JnOyBsaW51eC1wY2lAdmdlci5rZXJuZWwub3JnOyBHdW8gU2hhd24tDQo+IFI2NTA3
MzsgZmVzdGV2YW1AZ21haWwuY29tOyB0aGFydmV5QGdhdGV3b3Jrcy5jb20NCj4gU3ViamVjdDog
UmU6IFtQQVRDSCB2MyA3LzldIFBDSTogaW14NjogYWRkIGlteDZzeCBwY2llIHN1cHBvcnQNCj4g
DQo+IEFtIE1vbnRhZywgZGVuIDI5LjA5LjIwMTQsIDEzOjAzICswODAwIHNjaHJpZWIgUmljaGFy
ZCBaaHU6DQo+ID4gLSBpbXg2c3ggcGNpZSBoYXMgaXRzIG93biBzdGFuZGFsb25lIHBjaWUgcG93
ZXIgc3VwcGx5Lg0KPiA+IEluIG9yZGVyIHRvIHR1cm4gb24gdGhlIGlteDZzeCBwY2llIHBvd2Vy
IGR1cmluZyBpbml0aWFsaXphdGlvbi4gQWRkDQo+ID4gdGhlIHBjaWUgcmVndWxhdG9yIGFuZCB0
aGUgZ3BjIHJlZ21hcCBpbnRvIHRoZSBpbXg2c3ggcGNpZSBzdHJ1Y3R1cmUuDQo+ID4gLSBpbXg2
c3ggcGNpZSBoYXMgdGhlIG5ldyBhZGRlZCByZXNldCBtZWNoYW5pc20sIGFkZCB0aGUgcmVzZXQN
Cj4gPiBvcGVyYXRpb25zIGludG8gdGhlIGluaXRpYWxpemF0aW9uLg0KPiA+IC0gUmVnaXN0ZXIg
b25lIFBNIGNhbGwtYmFjaywgZW50ZXIvZXhpdCBMMiBzdGF0ZSBvZiB0aGUgQVNQTSBkdXJpbmcN
Cj4gPiBzeXN0ZW0gc3VzcGVuZC9yZXN1bWUuDQo+ID4gLSBkaXNwX2F4aSBjbG9jayBpcyByZXF1
aXJlZCBieSBwY2llIGluYm91bmQgYXhpIHBvcnQgYWN0dWFsbHkuDQo+ID4gQWRkIG9uZSBtb3Jl
IGNsb2NrIGZvciBpbXg2c3ggcGNpZS4NCj4gPg0KPiA+IFNpZ25lZC1vZmYtYnk6IFJpY2hhcmQg
Wmh1IDxyNjUwMzdAZnJlZXNjYWxlLmNvbT4NCj4gPiAtLS0NCj4gPiAgZHJpdmVycy9wY2kvaG9z
dC9wY2ktaW14Ni5jIHwgMTYyDQo+ID4gKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr
KysrKysrLS0tLS0NCj4gPiAgMSBmaWxlIGNoYW5nZWQsIDE0NCBpbnNlcnRpb25zKCspLCAxOCBk
ZWxldGlvbnMoLSkNCj4gPg0KPiA+IGRpZmYgLS1naXQgYS9kcml2ZXJzL3BjaS9ob3N0L3BjaS1p
bXg2LmMgYi9kcml2ZXJzL3BjaS9ob3N0L3BjaS1pbXg2LmMNCj4gPiBpbmRleCBlYWM5NmZiLi5i
ZTk1M2FhIDEwMDY0NA0KPiA+IC0tLSBhL2RyaXZlcnMvcGNpL2hvc3QvcGNpLWlteDYuYw0KPiA+
ICsrKyBiL2RyaXZlcnMvcGNpL2hvc3QvcGNpLWlteDYuYw0KPiA+IEBAIC0xOCwxMiArMTgsMTYg
QEANCj4gPiAgI2luY2x1ZGUgPGxpbnV4L21mZC9zeXNjb24uaD4NCj4gPiAgI2luY2x1ZGUgPGxp
bnV4L21mZC9zeXNjb24vaW14NnEtaW9tdXhjLWdwci5oPg0KPiA+ICAjaW5jbHVkZSA8bGludXgv
bW9kdWxlLmg+DQo+ID4gKyNpbmNsdWRlIDxsaW51eC9vZl9hZGRyZXNzLmg+DQo+IA0KPiBXaHkg
ZG8geW91IG5lZWQgdGhpcyBpbmNsdWRlPw0KW1JpY2hhcmRdSXQncyBub3QgcmVxdWlyZWQgYW55
bW9yZSwgd291bGQgYmUgcmVtb3ZlZCBsYXRlci4NCj4gDQo+ID4gKyNpbmNsdWRlIDxsaW51eC9v
Zl9kZXZpY2UuaD4NCj4gPiAgI2luY2x1ZGUgPGxpbnV4L29mX2dwaW8uaD4NCj4gPiAgI2luY2x1
ZGUgPGxpbnV4L3BjaS5oPg0KPiA+ICAjaW5jbHVkZSA8bGludXgvcGxhdGZvcm1fZGV2aWNlLmg+
DQo+ID4gICNpbmNsdWRlIDxsaW51eC9yZWdtYXAuaD4NCj4gPiArI2luY2x1ZGUgPGxpbnV4L3Jl
Z3VsYXRvci9jb25zdW1lci5oPg0KPiA+ICAjaW5jbHVkZSA8bGludXgvcmVzb3VyY2UuaD4NCj4g
PiAgI2luY2x1ZGUgPGxpbnV4L3NpZ25hbC5oPg0KPiA+ICsjaW5jbHVkZSA8bGludXgvc3lzY29y
ZV9vcHMuaD4NCj4gPiAgI2luY2x1ZGUgPGxpbnV4L3R5cGVzLmg+DQo+ID4gICNpbmNsdWRlIDxs
aW51eC9pbnRlcnJ1cHQuaD4NCj4gPg0KPiA+IEBAIC0zNSwxMSArMzksMTUgQEAgc3RydWN0IGlt
eDZfcGNpZSB7DQo+ID4gIAlpbnQJCQlyZXNldF9ncGlvOw0KPiA+ICAJc3RydWN0IGNsawkJKnBj
aWVfYnVzOw0KPiA+ICAJc3RydWN0IGNsawkJKnBjaWVfcGh5Ow0KPiA+ICsJc3RydWN0IGNsawkJ
KmRpc3BfYXhpOw0KPiANCj4gVGhpcyBuZWVkcyBhIG1vcmUgZGVzY3JpcHRpdmUgbmFtZSwganVz
dCBsaWtlIHRoZSBiaW5kaW5nLg0KW1JpY2hhcmRdaG93IGFib3V0IGxpa2UgInBjaWVfYXhpX2lu
Ym91bmQiPw0KPiANCj4gPiAgCXN0cnVjdCBjbGsJCSpwY2llOw0KPiA+ICAJc3RydWN0IHBjaWVf
cG9ydAlwcDsNCj4gPiAgCXN0cnVjdCByZWdtYXAJCSppb211eGNfZ3ByOw0KPiA+ICsJc3RydWN0
IHJlZ21hcAkJKmdwY19pcHNfcmVnOw0KPiA+ICsJc3RydWN0IHJlZ3VsYXRvcgkqcGNpZV9yZWd1
bGF0b3I7DQo+ID4gIAl2b2lkIF9faW9tZW0JCSptZW1fYmFzZTsNCj4gPiAgfTsNCj4gPiArc3Rh
dGljIHN0cnVjdCBpbXg2X3BjaWUgKmlteDZfcGNpZTsNCj4gPg0KPiA+ICAvKiBQQ0llIFJvb3Qg
Q29tcGxleCByZWdpc3RlcnMgKG1lbW9yeS1tYXBwZWQpICovDQo+ID4gICNkZWZpbmUgUENJRV9S
Q19MQ1IJCQkJMHg3Yw0KPiA+IEBAIC03Nyw2ICs4NSwxOCBAQCBzdHJ1Y3QgaW14Nl9wY2llIHsN
Cj4gPiAgI2RlZmluZSBQSFlfUlhfT1ZSRF9JTl9MT19SWF9EQVRBX0VOICgxIDw8IDUpICAjZGVm
aW5lDQo+ID4gUEhZX1JYX09WUkRfSU5fTE9fUlhfUExMX0VOICgxIDw8IDMpDQo+ID4NCj4gPiAr
LyogR1BDIFBDSUUgUEhZIGJpdCBkZWZpbml0aW9ucyAqLw0KPiA+ICsjZGVmaW5lIEdQQ19DTlRS
CQkJMA0KPiA+ICsjZGVmaW5lIEdQQ19DTlRSX1BDSUVfUEhZX1BVUF9SRVEJQklUKDcpDQo+ID4g
Kw0KPiA+ICtzdGF0aWMgaW5saW5lIGJvb2wgaXNfaW14NnN4X3BjaWUoc3RydWN0IGlteDZfcGNp
ZSAqaW14Nl9wY2llKSB7DQo+ID4gKwlzdHJ1Y3QgcGNpZV9wb3J0ICpwcCA9ICZpbXg2X3BjaWUt
PnBwOw0KPiA+ICsJc3RydWN0IGRldmljZV9ub2RlICpucCA9IHBwLT5kZXYtPm9mX25vZGU7DQo+
ID4gKw0KPiA+ICsJcmV0dXJuIG9mX2RldmljZV9pc19jb21wYXRpYmxlKG5wLCAiZnNsLGlteDZz
eC1wY2llIik7IH0NCj4gPiArDQo+ID4gIHN0YXRpYyBpbnQgcGNpZV9waHlfcG9sbF9hY2sodm9p
ZCBfX2lvbWVtICpkYmlfYmFzZSwgaW50IGV4cF92YWwpICB7DQo+ID4gIAl1MzIgdmFsOw0KPiA+
IEBAIC0yNzUsMTggKzI5NSwyOSBAQCBzdGF0aWMgaW50IGlteDZfcGNpZV9kZWFzc2VydF9jb3Jl
X3Jlc2V0KHN0cnVjdA0KPiBwY2llX3BvcnQgKnBwKQ0KPiA+ICAJCWdvdG8gZXJyX3BjaWU7DQo+
ID4gIAl9DQo+ID4NCj4gPiAtCS8qIHBvd2VyIHVwIGNvcmUgcGh5IGFuZCBlbmFibGUgcmVmIGNs
b2NrICovDQo+ID4gLQlyZWdtYXBfdXBkYXRlX2JpdHMoaW14Nl9wY2llLT5pb211eGNfZ3ByLCBJ
T01VWENfR1BSMSwNCj4gPiAtCQkJSU1YNlFfR1BSMV9QQ0lFX1RFU1RfUEQsIDAgPDwgMTgpOw0K
PiA+IC0JLyoNCj4gPiAtCSAqIHRoZSBhc3luYyByZXNldCBpbnB1dCBuZWVkIHJlZiBjbG9jayB0
byBzeW5jIGludGVybmFsbHksDQo+ID4gLQkgKiB3aGVuIHRoZSByZWYgY2xvY2sgY29tZXMgYWZ0
ZXIgcmVzZXQsIGludGVybmFsIHN5bmNlZA0KPiA+IC0JICogcmVzZXQgdGltZSBpcyB0b28gc2hv
cnQgLCBjYW5ub3QgbWVldCB0aGUgcmVxdWlyZW1lbnQuDQo+ID4gLQkgKiBhZGQgb25lIH4xMHVz
IGRlbGF5IGhlcmUuDQo+ID4gLQkgKi8NCj4gPiAtCXVkZWxheSgxMCk7DQo+ID4gLQlyZWdtYXBf
dXBkYXRlX2JpdHMoaW14Nl9wY2llLT5pb211eGNfZ3ByLCBJT01VWENfR1BSMSwNCj4gPiAtCQkJ
SU1YNlFfR1BSMV9QQ0lFX1JFRl9DTEtfRU4sIDEgPDwgMTYpOw0KPiA+ICsJaWYgKGlzX2lteDZz
eF9wY2llKGlteDZfcGNpZSkpIHsNCj4gPiArCQlyZXQgPSBjbGtfcHJlcGFyZV9lbmFibGUoaW14
Nl9wY2llLT5kaXNwX2F4aSk7DQo+ID4gKwkJaWYgKHJldCkgew0KPiA+ICsJCQlkZXZfZXJyKHBw
LT5kZXYsICJ1bmFibGUgdG8gZW5hYmxlIHBjaWUgY2xvY2tcbiIpOw0KPiA+ICsJCQlnb3RvIGVy
cl9kaXNwOw0KPiA+ICsJCX0NCj4gPiArDQo+ID4gKwkJcmVnbWFwX3VwZGF0ZV9iaXRzKGlteDZf
cGNpZS0+aW9tdXhjX2dwciwgSU9NVVhDX0dQUjEyLA0KPiA+ICsJCQkJSU1YNlNYX0dQUjEyX1BD
SUVfVEVTVF9QRCwgMCA8PCAzMCk7DQo+ID4gKwl9IGVsc2Ugew0KPiA+ICsJCS8qIHBvd2VyIHVw
IGNvcmUgcGh5IGFuZCBlbmFibGUgcmVmIGNsb2NrICovDQo+ID4gKwkJcmVnbWFwX3VwZGF0ZV9i
aXRzKGlteDZfcGNpZS0+aW9tdXhjX2dwciwgSU9NVVhDX0dQUjEsDQo+ID4gKwkJCQlJTVg2UV9H
UFIxX1BDSUVfVEVTVF9QRCwgMCA8PCAxOCk7DQo+ID4gKwkJLyoNCj4gPiArCQkgKiB0aGUgYXN5
bmMgcmVzZXQgaW5wdXQgbmVlZCByZWYgY2xvY2sgdG8gc3luYyBpbnRlcm5hbGx5LA0KPiA+ICsJ
CSAqIHdoZW4gdGhlIHJlZiBjbG9jayBjb21lcyBhZnRlciByZXNldCwgaW50ZXJuYWwgc3luY2Vk
DQo+ID4gKwkJICogcmVzZXQgdGltZSBpcyB0b28gc2hvcnQgLCBjYW5ub3QgbWVldCB0aGUgcmVx
dWlyZW1lbnQuDQo+ID4gKwkJICogYWRkIG9uZSB+MTB1cyBkZWxheSBoZXJlLg0KPiA+ICsJCSAq
Lw0KPiA+ICsJCXVkZWxheSgxMCk7DQo+ID4gKwkJcmVnbWFwX3VwZGF0ZV9iaXRzKGlteDZfcGNp
ZS0+aW9tdXhjX2dwciwgSU9NVVhDX0dQUjEsDQo+ID4gKwkJCQlJTVg2UV9HUFIxX1BDSUVfUkVG
X0NMS19FTiwgMSA8PCAxNik7DQo+ID4gKwl9DQo+ID4NCj4gPiAgCS8qIGFsbG93IHRoZSBjbG9j
a3MgdG8gc3RhYmlsaXplICovDQo+ID4gIAl1c2xlZXBfcmFuZ2UoMjAwLCA1MDApOw0KPiA+IEBA
IC0yOTcsOCArMzI4LDE5IEBAIHN0YXRpYyBpbnQgaW14Nl9wY2llX2RlYXNzZXJ0X2NvcmVfcmVz
ZXQoc3RydWN0DQo+IHBjaWVfcG9ydCAqcHApDQo+ID4gIAkJbXNsZWVwKDEwMCk7DQo+ID4gIAkJ
Z3Bpb19zZXRfdmFsdWUoaW14Nl9wY2llLT5yZXNldF9ncGlvLCAxKTsNCj4gPiAgCX0NCj4gPiAr
DQo+ID4gKwkvKg0KPiA+ICsJICogUmVsZWFzZSB0aGUgUENJZSBQSFkgcmVzZXQgaGVyZSwgdGhh
dCB3ZSBoYXZlIHNldCBpbg0KPiA+ICsJICogaW14Nl9wY2llX2luaXRfcGh5KCkgbm93DQo+ID4g
KwkgKi8NCj4gPiArCWlmIChpc19pbXg2c3hfcGNpZShpbXg2X3BjaWUpKQ0KPiA+ICsJCXJlZ21h
cF91cGRhdGVfYml0cyhpbXg2X3BjaWUtPmlvbXV4Y19ncHIsIElPTVVYQ19HUFI1LA0KPiA+ICsJ
CQkJSU1YNlNYX0dQUjVfUENJRV9CVE5SU1QsIDAgPDwgMTkpOw0KPiA+ICsNCj4gPiAgCXJldHVy
biAwOw0KPiA+DQo+ID4gK2Vycl9kaXNwOg0KPiA+ICsJY2xrX2Rpc2FibGVfdW5wcmVwYXJlKGlt
eDZfcGNpZS0+cGNpZSk7DQo+ID4gIGVycl9wY2llOg0KPiA+ICAJY2xrX2Rpc2FibGVfdW5wcmVw
YXJlKGlteDZfcGNpZS0+cGNpZV9idXMpOw0KPiA+ICBlcnJfcGNpZV9idXM6DQo+ID4gQEAgLTMx
MSw2ICszNTMsMjYgQEAgZXJyX3BjaWVfcGh5Og0KPiA+ICBzdGF0aWMgdm9pZCBpbXg2X3BjaWVf
aW5pdF9waHkoc3RydWN0IHBjaWVfcG9ydCAqcHApICB7DQo+ID4gIAlzdHJ1Y3QgaW14Nl9wY2ll
ICppbXg2X3BjaWUgPSB0b19pbXg2X3BjaWUocHApOw0KPiA+ICsJaW50IHJldDsNCj4gPiArDQo+
ID4gKwkvKiBQb3dlciB1cCB0aGUgc2VwYXJhdGUgZG9tYWluIGF2YWlsYWJsZSBvbiBpLk1YNlNY
ICovDQo+ID4gKwlpZiAoaXNfaW14NnN4X3BjaWUoaW14Nl9wY2llKSkgew0KPiA+ICsJCS8qIEZv
cmNlIFBDSWUgUEhZIHJlc2V0ICovDQo+ID4gKwkJcmVnbWFwX3VwZGF0ZV9iaXRzKGlteDZfcGNp
ZS0+aW9tdXhjX2dwciwgSU9NVVhDX0dQUjUsDQo+ID4gKwkJCQlJTVg2U1hfR1BSNV9QQ0lFX0JU
TlJTVCwNCj4gPiArCQkJCUlNWDZTWF9HUFI1X1BDSUVfQlROUlNUKTsNCj4gPiArDQo+ID4gKwkJ
cmVnbWFwX3VwZGF0ZV9iaXRzKGlteDZfcGNpZS0+Z3BjX2lwc19yZWcsIEdQQ19DTlRSLA0KPiA+
ICsJCQkJR1BDX0NOVFJfUENJRV9QSFlfUFVQX1JFUSwNCj4gPiArCQkJCUdQQ19DTlRSX1BDSUVf
UEhZX1BVUF9SRVEpOw0KPiA+ICsJCXJlZ3VsYXRvcl9zZXRfdm9sdGFnZShpbXg2X3BjaWUtPnBj
aWVfcmVndWxhdG9yLA0KPiA+ICsJCQkJMTEwMDAwMCwgMTEwMDAwMCk7DQo+ID4gKwkJcmV0ID0g
cmVndWxhdG9yX2VuYWJsZShpbXg2X3BjaWUtPnBjaWVfcmVndWxhdG9yKTsNCj4gPiArCQlpZiAo
cmV0KQ0KPiA+ICsJCQlkZXZfaW5mbyhwcC0+ZGV2LCAiZmFpbGVkIHRvIGVuYWJsZSBwY2llIHJl
Z3VsYXRvci5cbiIpOw0KPiA+ICsJCXJlZ21hcF91cGRhdGVfYml0cyhpbXg2X3BjaWUtPmlvbXV4
Y19ncHIsIElPTVVYQ19HUFIxMiwNCj4gPiArCQkJCUlNWDZTWF9HUFIxMl9SWF9FUV9NQVNLLCBJ
TVg2U1hfR1BSMTJfUlhfRVFfMik7DQo+ID4gKwl9DQo+ID4NCj4gPiAgCXJlZ21hcF91cGRhdGVf
Yml0cyhpbXg2X3BjaWUtPmlvbXV4Y19ncHIsIElPTVVYQ19HUFIxMiwNCj4gPiAgCQkJSU1YNlFf
R1BSMTJfUENJRV9DVExfMiwgMCA8PCAxMCk7DQo+ID4gQEAgLTMxOSw3ICszODEsNyBAQCBzdGF0
aWMgdm9pZCBpbXg2X3BjaWVfaW5pdF9waHkoc3RydWN0IHBjaWVfcG9ydCAqcHApDQo+ID4gIAly
ZWdtYXBfdXBkYXRlX2JpdHMoaW14Nl9wY2llLT5pb211eGNfZ3ByLCBJT01VWENfR1BSMTIsDQo+
ID4gIAkJCUlNWDZRX0dQUjEyX0RFVklDRV9UWVBFLCBQQ0lfRVhQX1RZUEVfUk9PVF9QT1JUIDw8
IDEyKTsNCj4gPiAgCXJlZ21hcF91cGRhdGVfYml0cyhpbXg2X3BjaWUtPmlvbXV4Y19ncHIsIElP
TVVYQ19HUFIxMiwNCj4gPiAtCQkJSU1YNlFfR1BSMTJfTE9TX0xFVkVMLCA5IDw8IDQpOw0KPiA+
ICsJCQlJTVg2UV9HUFIxMl9MT1NfTEVWRUwsIElNWDZRX0dQUjEyX0xPU19MRVZFTF85KTsNCj4g
Pg0KPiA+ICAJcmVnbWFwX3VwZGF0ZV9iaXRzKGlteDZfcGNpZS0+aW9tdXhjX2dwciwgSU9NVVhD
X0dQUjgsDQo+ID4gIAkJCUlNWDZRX0dQUjhfVFhfREVFTVBIX0dFTjEsIDAgPDwgMCk7IEBAIC0z
NzcsNyArNDM5LDggQEAgc3RhdGljDQo+IGludA0KPiA+IGlteDZfcGNpZV9zdGFydF9saW5rKHN0
cnVjdCBwY2llX3BvcnQgKnBwKQ0KPiA+DQo+ID4gIAkvKiBTdGFydCBMVFNTTS4gKi8NCj4gPiAg
CXJlZ21hcF91cGRhdGVfYml0cyhpbXg2X3BjaWUtPmlvbXV4Y19ncHIsIElPTVVYQ19HUFIxMiwN
Cj4gPiAtCQkJSU1YNlFfR1BSMTJfUENJRV9DVExfMiwgMSA8PCAxMCk7DQo+ID4gKwkJCUlNWDZR
X0dQUjEyX1BDSUVfQ1RMXzIsDQo+ID4gKwkJCUlNWDZRX0dQUjEyX1BDSUVfQ1RMXzIpOw0KPiA+
DQo+ID4gIAlyZXQgPSBpbXg2X3BjaWVfd2FpdF9mb3JfbGluayhwcCk7DQo+ID4gIAlpZiAocmV0
KQ0KPiA+IEBAIC01NTMsOSArNjE2LDUwIEBAIHN0YXRpYyBpbnQgX19pbml0IGlteDZfYWRkX3Bj
aWVfcG9ydChzdHJ1Y3QgcGNpZV9wb3J0DQo+ICpwcCwNCj4gPiAgCXJldHVybiAwOw0KPiA+ICB9
DQo+ID4NCj4gPiArI2lmZGVmIENPTkZJR19QTV9TTEVFUA0KPiA+ICtzdGF0aWMgaW50IHBjaV9p
bXhfc3VzcGVuZCh2b2lkKQ0KPiA+ICt7DQo+ID4gKwlpZiAoaXNfaW14NnN4X3BjaWUoaW14Nl9w
Y2llKSkgew0KPiA+ICsJCS8qIFBNX1RVUk5fT0ZGICovDQo+ID4gKwkJcmVnbWFwX3VwZGF0ZV9i
aXRzKGlteDZfcGNpZS0+aW9tdXhjX2dwciwgSU9NVVhDX0dQUjEyLA0KPiA+ICsJCQkJSU1YNlNY
X0dQUjEyX1BDSUVfUE1fVFVSTl9PRkYsDQo+ID4gKwkJCQlJTVg2U1hfR1BSMTJfUENJRV9QTV9U
VVJOX09GRik7DQo+ID4gKwkJdWRlbGF5KDEwKTsNCj4gPiArCQlyZWdtYXBfdXBkYXRlX2JpdHMo
aW14Nl9wY2llLT5pb211eGNfZ3ByLCBJT01VWENfR1BSMTIsDQo+ID4gKwkJCQlJTVg2U1hfR1BS
MTJfUENJRV9QTV9UVVJOX09GRiwgMCA8PCAxNik7DQo+IA0KPiBKdXN0IHVzZSAwIGFzIHRoZSBs
YXN0IGFyZ3VtZW50LiBUaG9zZSBzaGlmdHMgYXJlbid0IGFkZGluZyBhbnl0aGluZyBhbmQgSQ0K
PiB3b3VsZCBsaWtlIHRvIGdldCByaWQgb2YgdGhlbSBsb25nIHRlcm0sIHNvIHBsZWFzZSBkb24n
dCBpbnRyb2R1Y2UgbmV3IG9uZXMuDQo+IA0KW1JpY2hhcmRdIE9rLCBhbGwgdGhlc2Ugc2hpZnRz
IHdvdWxkbid0IGJlIHJlbW92ZWQuDQo+ID4gKwl9DQo+ID4gKw0KPiA+ICsJcmV0dXJuIDA7DQo+
ID4gK30NCj4gPiArDQo+ID4gK3N0YXRpYyB2b2lkIHBjaV9pbXhfcmVzdW1lKHZvaWQpDQo+ID4g
K3sNCj4gPiArCXN0cnVjdCBwY2llX3BvcnQgKnBwID0gJmlteDZfcGNpZS0+cHA7DQo+ID4gKw0K
PiA+ICsJaWYgKGlzX2lteDZzeF9wY2llKGlteDZfcGNpZSkpIHsNCj4gPiArCQkvKiBSZXNldCBp
TVg2U1ggUENJZSAqLw0KPiA+ICsJCXJlZ21hcF91cGRhdGVfYml0cyhpbXg2X3BjaWUtPmlvbXV4
Y19ncHIsIElPTVVYQ19HUFI1LA0KPiA+ICsJCQkJSU1YNlNYX0dQUjVfUENJRV9QRVJTVCwgSU1Y
NlNYX0dQUjVfUENJRV9QRVJTVCk7DQo+ID4gKwkJcmVnbWFwX3VwZGF0ZV9iaXRzKGlteDZfcGNp
ZS0+aW9tdXhjX2dwciwgSU9NVVhDX0dQUjUsDQo+ID4gKwkJCQlJTVg2U1hfR1BSNV9QQ0lFX1BF
UlNULCAwIDw8IDE4KTsNCj4gPiArCQkvKg0KPiA+ICsJCSAqIGNvbnRyb2xsZXIgbWF5YmUgdHVy
biBvZmYsIHJlLWNvbmZpZ3VyZSBhZ2Fpbg0KPiA+ICsJCSAqLw0KPiA+ICsJCWR3X3BjaWVfc2V0
dXBfcmMocHApOw0KPiA+ICsNCj4gPiArCQlpZiAoSVNfRU5BQkxFRChDT05GSUdfUENJX01TSSkp
DQo+ID4gKwkJCWR3X3BjaWVfbXNpX2NmZ19yZXN0b3JlKHBwKTsNCj4gPiArCX0NCj4gPiArfQ0K
PiA+ICsNCj4gPiArc3RhdGljIHN0cnVjdCBzeXNjb3JlX29wcyBwY2lfaW14X3N5c2NvcmVfb3Bz
ID0gew0KPiA+ICsJLnN1c3BlbmQgPSBwY2lfaW14X3N1c3BlbmQsDQo+ID4gKwkucmVzdW1lID0g
cGNpX2lteF9yZXN1bWUsDQo+ID4gK307DQo+ID4gKyNlbmRpZg0KPiA+ICsNCj4gPiAgc3RhdGlj
IGludCBfX2luaXQgaW14Nl9wY2llX3Byb2JlKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYp
ICB7DQo+ID4gLQlzdHJ1Y3QgaW14Nl9wY2llICppbXg2X3BjaWU7DQo+ID4gIAlzdHJ1Y3QgcGNp
ZV9wb3J0ICpwcDsNCj4gPiAgCXN0cnVjdCBkZXZpY2Vfbm9kZSAqbnAgPSBwZGV2LT5kZXYub2Zf
bm9kZTsNCj4gPiAgCXN0cnVjdCByZXNvdXJjZSAqZGJpX2Jhc2U7DQo+ID4gQEAgLTYxMCw5ICs3
MTQsMjcgQEAgc3RhdGljIGludCBfX2luaXQgaW14Nl9wY2llX3Byb2JlKHN0cnVjdA0KPiBwbGF0
Zm9ybV9kZXZpY2UgKnBkZXYpDQo+ID4gIAkJcmV0dXJuIFBUUl9FUlIoaW14Nl9wY2llLT5wY2ll
KTsNCj4gPiAgCX0NCj4gPg0KPiA+IC0JLyogR3JhYiBHUFIgY29uZmlnIHJlZ2lzdGVyIHJhbmdl
ICovDQo+ID4gLQlpbXg2X3BjaWUtPmlvbXV4Y19ncHIgPQ0KPiA+IC0JCSBzeXNjb25fcmVnbWFw
X2xvb2t1cF9ieV9jb21wYXRpYmxlKCJmc2wsaW14NnEtaW9tdXhjLWdwciIpOw0KPiA+ICsJaWYg
KGlzX2lteDZzeF9wY2llKGlteDZfcGNpZSkpIHsNCj4gPiArCQlpbXg2X3BjaWUtPmRpc3BfYXhp
ID0gZGV2bV9jbGtfZ2V0KCZwZGV2LT5kZXYsICJkaXNwX2F4aSIpOw0KPiA+ICsJCWlmIChJU19F
UlIoaW14Nl9wY2llLT5kaXNwX2F4aSkpIHsNCj4gPiArCQkJZGV2X2VycigmcGRldi0+ZGV2LA0K
PiA+ICsJCQkJInBjaWUgY2xvY2sgc291cmNlIG1pc3Npbmcgb3IgaW52YWxpZFxuIik7DQo+ID4g
KwkJCXJldHVybiBQVFJfRVJSKGlteDZfcGNpZS0+ZGlzcF9heGkpOw0KPiA+ICsJCX0NCj4gPiAr
DQo+ID4gKwkJaW14Nl9wY2llLT5wY2llX3JlZ3VsYXRvciA9IGRldm1fcmVndWxhdG9yX2dldChw
cC0+ZGV2LA0KPiA+ICsJCQkJInBjaWVfcGh5Iik7DQo+ID4gKw0KPiA+ICsJCWlteDZfcGNpZS0+
aW9tdXhjX2dwciA9DQo+ID4gKwkJCSBzeXNjb25fcmVnbWFwX2xvb2t1cF9ieV9jb21wYXRpYmxl
DQo+ID4gKwkJCSAoImZzbCxpbXg2c3gtaW9tdXhjLWdwciIpOw0KPiA+ICsJCWlteDZfcGNpZS0+
Z3BjX2lwc19yZWcgPQ0KPiA+ICsJCQkgc3lzY29uX3JlZ21hcF9sb29rdXBfYnlfY29tcGF0aWJs
ZSgiZnNsLGlteDZzeC1ncGMiKTsNCj4gPiArCX0gZWxzZSB7DQo+ID4gKwkJaW14Nl9wY2llLT5p
b211eGNfZ3ByID0NCj4gPiArCQkJc3lzY29uX3JlZ21hcF9sb29rdXBfYnlfY29tcGF0aWJsZQ0K
PiA+ICsJCQkoImZzbCxpbXg2cS1pb211eGMtZ3ByIik7DQo+ID4gKwl9DQo+ID4gIAlpZiAoSVNf
RVJSKGlteDZfcGNpZS0+aW9tdXhjX2dwcikpIHsNCj4gPiAgCQlkZXZfZXJyKCZwZGV2LT5kZXYs
ICJ1bmFibGUgdG8gZmluZCBpb211eGMgcmVnaXN0ZXJzXG4iKTsNCj4gPiAgCQlyZXR1cm4gUFRS
X0VSUihpbXg2X3BjaWUtPmlvbXV4Y19ncHIpOyBAQCAtNjIzLDYgKzc0NSw5IEBAIHN0YXRpYw0K
PiA+IGludCBfX2luaXQgaW14Nl9wY2llX3Byb2JlKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBk
ZXYpDQo+ID4gIAkJcmV0dXJuIHJldDsNCj4gPg0KPiA+ICAJcGxhdGZvcm1fc2V0X2RydmRhdGEo
cGRldiwgaW14Nl9wY2llKTsNCj4gPiArI2lmZGVmIENPTkZJR19QTV9TTEVFUA0KPiA+ICsJcmVn
aXN0ZXJfc3lzY29yZV9vcHMoJnBjaV9pbXhfc3lzY29yZV9vcHMpOw0KPiA+ICsjZW5kaWYNCj4g
PiAgCXJldHVybiAwOw0KPiA+ICB9DQo+ID4NCj4gPiBAQCAtNjM2LDYgKzc2MSw3IEBAIHN0YXRp
YyB2b2lkIGlteDZfcGNpZV9zaHV0ZG93bihzdHJ1Y3QNCj4gPiBwbGF0Zm9ybV9kZXZpY2UgKnBk
ZXYpDQo+ID4NCj4gPiAgc3RhdGljIGNvbnN0IHN0cnVjdCBvZl9kZXZpY2VfaWQgaW14Nl9wY2ll
X29mX21hdGNoW10gPSB7DQo+ID4gIAl7IC5jb21wYXRpYmxlID0gImZzbCxpbXg2cS1wY2llIiwg
fSwNCj4gPiArCXsgLmNvbXBhdGlibGUgPSAiZnNsLGlteDZzeC1wY2llIiwgfSwNCj4gPiAgCXt9
LA0KPiA+ICB9Ow0KPiA+ICBNT0RVTEVfREVWSUNFX1RBQkxFKG9mLCBpbXg2X3BjaWVfb2ZfbWF0
Y2gpOw0KPiANCj4gLS0NCj4gUGVuZ3V0cm9uaXggZS5LLiAgICAgICAgICAgICB8IEx1Y2FzIFN0
YWNoICAgICAgICAgICAgICAgICB8DQo+IEluZHVzdHJpYWwgTGludXggU29sdXRpb25zICAgfCBo
dHRwOi8vd3d3LnBlbmd1dHJvbml4LmRlLyAgfA0KDQoNCkJlc3QgUmVnYXJkcw0KUmljaGFyZCBa
aHUNCg0K
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c
index eac96fb..be953aa 100644
--- a/drivers/pci/host/pci-imx6.c
+++ b/drivers/pci/host/pci-imx6.c
@@ -18,12 +18,16 @@ 
 #include <linux/mfd/syscon.h>
 #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
 #include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
 #include <linux/of_gpio.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
 #include <linux/resource.h>
 #include <linux/signal.h>
+#include <linux/syscore_ops.h>
 #include <linux/types.h>
 #include <linux/interrupt.h>
 
@@ -35,11 +39,15 @@  struct imx6_pcie {
 	int			reset_gpio;
 	struct clk		*pcie_bus;
 	struct clk		*pcie_phy;
+	struct clk		*disp_axi;
 	struct clk		*pcie;
 	struct pcie_port	pp;
 	struct regmap		*iomuxc_gpr;
+	struct regmap		*gpc_ips_reg;
+	struct regulator	*pcie_regulator;
 	void __iomem		*mem_base;
 };
+static struct imx6_pcie *imx6_pcie;
 
 /* PCIe Root Complex registers (memory-mapped) */
 #define PCIE_RC_LCR				0x7c
@@ -77,6 +85,18 @@  struct imx6_pcie {
 #define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5)
 #define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3)
 
+/* GPC PCIE PHY bit definitions */
+#define GPC_CNTR			0
+#define GPC_CNTR_PCIE_PHY_PUP_REQ	BIT(7)
+
+static inline bool is_imx6sx_pcie(struct imx6_pcie *imx6_pcie)
+{
+	struct pcie_port *pp = &imx6_pcie->pp;
+	struct device_node *np = pp->dev->of_node;
+
+	return of_device_is_compatible(np, "fsl,imx6sx-pcie");
+}
+
 static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val)
 {
 	u32 val;
@@ -275,18 +295,29 @@  static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
 		goto err_pcie;
 	}
 
-	/* power up core phy and enable ref clock */
-	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
-			IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
-	/*
-	 * the async reset input need ref clock to sync internally,
-	 * when the ref clock comes after reset, internal synced
-	 * reset time is too short , cannot meet the requirement.
-	 * add one ~10us delay here.
-	 */
-	udelay(10);
-	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
-			IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
+	if (is_imx6sx_pcie(imx6_pcie)) {
+		ret = clk_prepare_enable(imx6_pcie->disp_axi);
+		if (ret) {
+			dev_err(pp->dev, "unable to enable pcie clock\n");
+			goto err_disp;
+		}
+
+		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+				IMX6SX_GPR12_PCIE_TEST_PD, 0 << 30);
+	} else {
+		/* power up core phy and enable ref clock */
+		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+				IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
+		/*
+		 * the async reset input need ref clock to sync internally,
+		 * when the ref clock comes after reset, internal synced
+		 * reset time is too short , cannot meet the requirement.
+		 * add one ~10us delay here.
+		 */
+		udelay(10);
+		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
+				IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
+	}
 
 	/* allow the clocks to stabilize */
 	usleep_range(200, 500);
@@ -297,8 +328,19 @@  static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
 		msleep(100);
 		gpio_set_value(imx6_pcie->reset_gpio, 1);
 	}
+
+	/*
+	 * Release the PCIe PHY reset here, that we have set in
+	 * imx6_pcie_init_phy() now
+	 */
+	if (is_imx6sx_pcie(imx6_pcie))
+		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
+				IMX6SX_GPR5_PCIE_BTNRST, 0 << 19);
+
 	return 0;
 
+err_disp:
+	clk_disable_unprepare(imx6_pcie->pcie);
 err_pcie:
 	clk_disable_unprepare(imx6_pcie->pcie_bus);
 err_pcie_bus:
@@ -311,6 +353,26 @@  err_pcie_phy:
 static void imx6_pcie_init_phy(struct pcie_port *pp)
 {
 	struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
+	int ret;
+
+	/* Power up the separate domain available on i.MX6SX */
+	if (is_imx6sx_pcie(imx6_pcie)) {
+		/* Force PCIe PHY reset */
+		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
+				IMX6SX_GPR5_PCIE_BTNRST,
+				IMX6SX_GPR5_PCIE_BTNRST);
+
+		regmap_update_bits(imx6_pcie->gpc_ips_reg, GPC_CNTR,
+				GPC_CNTR_PCIE_PHY_PUP_REQ,
+				GPC_CNTR_PCIE_PHY_PUP_REQ);
+		regulator_set_voltage(imx6_pcie->pcie_regulator,
+				1100000, 1100000);
+		ret = regulator_enable(imx6_pcie->pcie_regulator);
+		if (ret)
+			dev_info(pp->dev, "failed to enable pcie regulator.\n");
+		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+				IMX6SX_GPR12_RX_EQ_MASK, IMX6SX_GPR12_RX_EQ_2);
+	}
 
 	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
 			IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
@@ -319,7 +381,7 @@  static void imx6_pcie_init_phy(struct pcie_port *pp)
 	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
 			IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12);
 	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
-			IMX6Q_GPR12_LOS_LEVEL, 9 << 4);
+			IMX6Q_GPR12_LOS_LEVEL, IMX6Q_GPR12_LOS_LEVEL_9);
 
 	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
 			IMX6Q_GPR8_TX_DEEMPH_GEN1, 0 << 0);
@@ -377,7 +439,8 @@  static int imx6_pcie_start_link(struct pcie_port *pp)
 
 	/* Start LTSSM. */
 	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
-			IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
+			IMX6Q_GPR12_PCIE_CTL_2,
+			IMX6Q_GPR12_PCIE_CTL_2);
 
 	ret = imx6_pcie_wait_for_link(pp);
 	if (ret)
@@ -553,9 +616,50 @@  static int __init imx6_add_pcie_port(struct pcie_port *pp,
 	return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int pci_imx_suspend(void)
+{
+	if (is_imx6sx_pcie(imx6_pcie)) {
+		/* PM_TURN_OFF */
+		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+				IMX6SX_GPR12_PCIE_PM_TURN_OFF,
+				IMX6SX_GPR12_PCIE_PM_TURN_OFF);
+		udelay(10);
+		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+				IMX6SX_GPR12_PCIE_PM_TURN_OFF, 0 << 16);
+	}
+
+	return 0;
+}
+
+static void pci_imx_resume(void)
+{
+	struct pcie_port *pp = &imx6_pcie->pp;
+
+	if (is_imx6sx_pcie(imx6_pcie)) {
+		/* Reset iMX6SX PCIe */
+		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
+				IMX6SX_GPR5_PCIE_PERST, IMX6SX_GPR5_PCIE_PERST);
+		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
+				IMX6SX_GPR5_PCIE_PERST, 0 << 18);
+		/*
+		 * controller maybe turn off, re-configure again
+		 */
+		dw_pcie_setup_rc(pp);
+
+		if (IS_ENABLED(CONFIG_PCI_MSI))
+			dw_pcie_msi_cfg_restore(pp);
+	}
+}
+
+static struct syscore_ops pci_imx_syscore_ops = {
+	.suspend = pci_imx_suspend,
+	.resume = pci_imx_resume,
+};
+#endif
+
 static int __init imx6_pcie_probe(struct platform_device *pdev)
 {
-	struct imx6_pcie *imx6_pcie;
 	struct pcie_port *pp;
 	struct device_node *np = pdev->dev.of_node;
 	struct resource *dbi_base;
@@ -610,9 +714,27 @@  static int __init imx6_pcie_probe(struct platform_device *pdev)
 		return PTR_ERR(imx6_pcie->pcie);
 	}
 
-	/* Grab GPR config register range */
-	imx6_pcie->iomuxc_gpr =
-		 syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
+	if (is_imx6sx_pcie(imx6_pcie)) {
+		imx6_pcie->disp_axi = devm_clk_get(&pdev->dev, "disp_axi");
+		if (IS_ERR(imx6_pcie->disp_axi)) {
+			dev_err(&pdev->dev,
+				"pcie clock source missing or invalid\n");
+			return PTR_ERR(imx6_pcie->disp_axi);
+		}
+
+		imx6_pcie->pcie_regulator = devm_regulator_get(pp->dev,
+				"pcie_phy");
+
+		imx6_pcie->iomuxc_gpr =
+			 syscon_regmap_lookup_by_compatible
+			 ("fsl,imx6sx-iomuxc-gpr");
+		imx6_pcie->gpc_ips_reg =
+			 syscon_regmap_lookup_by_compatible("fsl,imx6sx-gpc");
+	} else {
+		imx6_pcie->iomuxc_gpr =
+			syscon_regmap_lookup_by_compatible
+			("fsl,imx6q-iomuxc-gpr");
+	}
 	if (IS_ERR(imx6_pcie->iomuxc_gpr)) {
 		dev_err(&pdev->dev, "unable to find iomuxc registers\n");
 		return PTR_ERR(imx6_pcie->iomuxc_gpr);
@@ -623,6 +745,9 @@  static int __init imx6_pcie_probe(struct platform_device *pdev)
 		return ret;
 
 	platform_set_drvdata(pdev, imx6_pcie);
+#ifdef CONFIG_PM_SLEEP
+	register_syscore_ops(&pci_imx_syscore_ops);
+#endif
 	return 0;
 }
 
@@ -636,6 +761,7 @@  static void imx6_pcie_shutdown(struct platform_device *pdev)
 
 static const struct of_device_id imx6_pcie_of_match[] = {
 	{ .compatible = "fsl,imx6q-pcie", },
+	{ .compatible = "fsl,imx6sx-pcie", },
 	{},
 };
 MODULE_DEVICE_TABLE(of, imx6_pcie_of_match);