Discussion:
[PATCH v3 2/9] PCI: imx6: enable pcie on imx6qdl sabreauto
Richard Zhu
2014-09-29 05:03:10 UTC
Permalink
- enable pcie on imx6qdl sabreauto boards.

Signed-off-by: Richard Zhu <***@freescale.com>
Reviewed-by: Lucas Stach <***@pengutronix.de>
---
arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
index 009abd6..d6040a5 100644
--- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
@@ -410,6 +410,10 @@
};
};

+&pcie {
+ status = "okay";
+};
+
&pwm3 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pwm3>;
--
1.9.1
Richard Zhu
2014-09-29 05:03:09 UTC
Permalink
For boards without a reset gpio we skip the delay between enabling
the pcie_ref_clk and touching the RC registers for configuration.
System would be hangs when the clocks are not yet settled in the DW
PCIe core. So we need to make sure that there is always an
appropriate delay between those two actions.

Signed-off-by: Richard Zhu <***@freescale.com>
---
drivers/pci/host/pci-imx6.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c
index 233fe8a..eac96fb 100644
--- a/drivers/pci/host/pci-imx6.c
+++ b/drivers/pci/host/pci-imx6.c
@@ -275,15 +275,22 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
goto err_pcie;
}

- /* allow the clocks to stabilize */
- usleep_range(200, 500);
-
/* 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);
+
/* Some boards don't have PCIe reset GPIO. */
if (gpio_is_valid(imx6_pcie->reset_gpio)) {
gpio_set_value(imx6_pcie->reset_gpio, 0);
--
1.9.1
Richard Zhu
2014-09-29 05:03:12 UTC
Permalink
In order to manipulate gpc bits for imx6sx
pcie in driver, add syscon into gpc dts

Signed-off-by: Richard Zhu <***@freescale.com>
---
arch/arm/boot/dts/imx6sx.dtsi | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi
index b4ca94b..76b17e6 100644
--- a/arch/arm/boot/dts/imx6sx.dtsi
+++ b/arch/arm/boot/dts/imx6sx.dtsi
@@ -689,7 +689,8 @@
};

gpc: ***@020dc000 {
- compatible = "fsl,imx6sx-gpc", "fsl,imx6q-gpc";
+ compatible = "fsl,imx6sx-gpc",
+ "fsl,imx6q-gpc", "syscon";
reg = <0x020dc000 0x4000>;
interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
};
--
1.9.1
Richard Zhu
2014-09-29 05:03:13 UTC
Permalink
Signed-off-by: Richard Zhu <***@freescale.com>
---
include/linux/mfd/syscon/imx6q-iomuxc-gpr.h | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
index ff44374..3273b87 100644
--- a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
+++ b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
@@ -301,6 +301,7 @@
#define IMX6Q_GPR12_DEVICE_TYPE (0xf << 12)
#define IMX6Q_GPR12_PCIE_CTL_2 BIT(10)
#define IMX6Q_GPR12_LOS_LEVEL (0x1f << 4)
+#define IMX6Q_GPR12_LOS_LEVEL_9 (0x9 << 4)

#define IMX6Q_GPR13_SDMA_STOP_REQ BIT(30)
#define IMX6Q_GPR13_CAN2_STOP_REQ BIT(29)
@@ -395,4 +396,12 @@
#define IMX6SL_GPR1_FEC_CLOCK_MUX1_SEL_MASK (0x3 << 17)
#define IMX6SL_GPR1_FEC_CLOCK_MUX2_SEL_MASK (0x1 << 14)

+/* For imx6sx iomux gpr register field define */
+#define IMX6SX_GPR5_PCIE_BTNRST BIT(19)
+#define IMX6SX_GPR5_PCIE_PERST BIT(18)
+
+#define IMX6SX_GPR12_PCIE_PM_TURN_OFF BIT(16)
+#define IMX6SX_GPR12_PCIE_TEST_PD BIT(30)
+#define IMX6SX_GPR12_RX_EQ_MASK (0x7 << 0)
+#define IMX6SX_GPR12_RX_EQ_2 (0x2 << 0)
#endif /* __LINUX_IMX6Q_IOMUXC_GPR_H */
--
1.9.1
Richard Zhu
2014-09-29 05:03:17 UTC
Permalink
kernel report one possible dead lock during imx6sx pcie
suspend resume stress tests, after enable Lock Debugging.
platform: imx6sx sdb board + xhci(pcie2usb3.0 ep)

reason: usleep_range used in imx6_pcie_link_up maybe scheduled
out from dw_pcie_valid_config.isra...
About details, please see the following logs.

solution: replace the usleep_range(1000, 2000) by udelay(10) and
enlarge the loop counter.

logs:
[ 50.643062] xhci_hcd 0000:01:00.0: Refused to change power state, currently in D3
[ 50.653390]
[ 50.654898] =========================================================
[ 50.661343] [ INFO: possible irq lock inversion dependency detected ]
[ 50.667792] 3.17.0-rc2-01341-gfc43ff7-dirty #101 Not tainted
[ 50.673454] ---------------------------------------------------------
[ 50.679898] kworker/u2:2/48 just changed the state of lock:
[ 50.685477] (pci_lock){+.....}, at: [<802d650c>] pci_bus_read_config_dword+0x44/0x94
[ 50.693394] but this lock was taken by another, HARDIRQ-safe lock in the past:
[ 50.700619] (&irq_desc_lock_class){-.-...}

and interrupts could create inverse lock ordering between them.

[ 50.710843]
[ 50.710843] other info that might help us debug this:
[ 50.717377] Possible interrupt unsafe locking scenario:
[ 50.717377]
[ 50.724169] CPU0 CPU1
[ 50.728702] ---- ----
[ 50.733234] lock(pci_lock);
[ 50.736232] local_irq_disable();
[ 50.742154] lock(&irq_desc_lock_class);
[ 50.748713] lock(pci_lock);
[ 50.754229] <Interrupt>
[ 50.756852] lock(&irq_desc_lock_class);
[ 50.761065]
[ 50.761065] *** DEADLOCK ***
...

[ 52.119515] [<806e8ad0>] (schedule_hrtimeout_range) from [<80077e90>] (usleep_range+0x50/0x58)
[ 52.128141] [<80077e40>] (usleep_range) from [<802f3694>] (imx6_pcie_link_up+0x48/0x16c)
[ 52.136242] [<802f364c>] (imx6_pcie_link_up) from [<802f1b74>] (dw_pcie_valid_config.isra.10+0x40/0x7c)
[ 52.145637] r6:ae72606d r5:ae72606c r4:ae711228
[ 52.150311] [<802f1b34>] (dw_pcie_valid_config.isra.10) from [<802f1ca8>] (dw_pcie_rd_conf+0x4c/0x154)
[ 52.159620] r7:00000000 r6:00000000 r5:ae711228 r4:ae726000
[ 52.165355] [<802f1c5c>] (dw_pcie_rd_conf) from [<802d6534>] (pci_bus_read_config_dword+0x6c/0x94)
[ 52.174316] r9:adf4e910 r8:809bc51c r7:00000000 r6:60000153 r5:adc3fd5c r4:ae726000
[ 52.182152] [<802d64c8>] (pci_bus_read_config_dword) from [<802db588>] (pci_restore_config_dword+0x54/0xa4)
[ 52.191894] r6:00000024 r5:0000000a r4:ae61e000
[ 52.196570] [<802db534>] (pci_restore_config_dword) from [<802dd150>] (pci_restore_state.part.37+0x7c/0x1f8)
[ 52.206399] r8:802e086c r7:ae61dfe8 r6:519e2024 r5:ae61e000 r4:ae61dffc
[ 52.213187] [<802dd0d4>] (pci_restore_state.part.37) from [<802dd2e8>] (pci_restore_state+0x1c/0x20)
[ 52.222322] r7:adc3fea8 r6:809d90e0 r5:ae61e000 r4:ae61e068
[ 52.228056] [<802dd2cc>] (pci_restore_state) from [<802e0894>] (pci_pm_resume_noirq+0x28/0xa4)
[ 52.236683] [<802e086c>] (pci_pm_resume_noirq) from [<8037ea1c>] (dpm_run_callback.isra.12+0x34/0x7c)

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

diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c
index be953aa..e60c195 100644
--- a/drivers/pci/host/pci-imx6.c
+++ b/drivers/pci/host/pci-imx6.c
@@ -521,7 +521,7 @@ static void imx6_pcie_reset_phy(struct pcie_port *pp)
static int imx6_pcie_link_up(struct pcie_port *pp)
{
u32 rc, debug_r0, rx_valid;
- int count = 5;
+ int count = 500;

/*
* Test if the PHY reports that the link is up and also that the LTSSM
@@ -552,7 +552,7 @@ static int imx6_pcie_link_up(struct pcie_port *pp)
* Wait a little bit, then re-check if the link finished
* the training.
*/
- usleep_range(1000, 2000);
+ udelay(10);
}
/*
* From L0, initiate MAC entry to gen2 if EP/RC supports gen2.
--
1.9.1
Lucas Stach
2014-09-29 10:38:42 UTC
Permalink
Post by Richard Zhu
kernel report one possible dead lock during imx6sx pcie
suspend resume stress tests, after enable Lock Debugging.
platform: imx6sx sdb board + xhci(pcie2usb3.0 ep)
reason: usleep_range used in imx6_pcie_link_up maybe scheduled
out from dw_pcie_valid_config.isra...
About details, please see the following logs.
solution: replace the usleep_range(1000, 2000) by udelay(10) and
enlarge the loop counter.
I don't like this change, as we eat one CPU just to work around a
slightly wrong behavior of the link-up signaling. I had a look at the
code in question and it seems there are more things that need different
handling in there. I'll post a patch to resolve the deadlock without
using a udelay shortly.

Regards,
Lucas
Post by Richard Zhu
[ 50.643062] xhci_hcd 0000:01:00.0: Refused to change power state, currently in D3
[ 50.653390]
[ 50.654898] =========================================================
[ 50.661343] [ INFO: possible irq lock inversion dependency detected ]
[ 50.667792] 3.17.0-rc2-01341-gfc43ff7-dirty #101 Not tainted
[ 50.673454] ---------------------------------------------------------
[ 50.685477] (pci_lock){+.....}, at: [<802d650c>] pci_bus_read_config_dword+0x44/0x94
[ 50.700619] (&irq_desc_lock_class){-.-...}
and interrupts could create inverse lock ordering between them.
[ 50.710843]
[ 50.717377]
[ 50.724169] CPU0 CPU1
[ 50.728702] ---- ----
[ 50.733234] lock(pci_lock);
[ 50.736232] local_irq_disable();
[ 50.742154] lock(&irq_desc_lock_class);
[ 50.748713] lock(pci_lock);
[ 50.754229] <Interrupt>
[ 50.756852] lock(&irq_desc_lock_class);
[ 50.761065]
[ 50.761065] *** DEADLOCK ***
...
[ 52.119515] [<806e8ad0>] (schedule_hrtimeout_range) from [<80077e90>] (usleep_range+0x50/0x58)
[ 52.128141] [<80077e40>] (usleep_range) from [<802f3694>] (imx6_pcie_link_up+0x48/0x16c)
[ 52.136242] [<802f364c>] (imx6_pcie_link_up) from [<802f1b74>] (dw_pcie_valid_config.isra.10+0x40/0x7c)
[ 52.145637] r6:ae72606d r5:ae72606c r4:ae711228
[ 52.150311] [<802f1b34>] (dw_pcie_valid_config.isra.10) from [<802f1ca8>] (dw_pcie_rd_conf+0x4c/0x154)
[ 52.159620] r7:00000000 r6:00000000 r5:ae711228 r4:ae726000
[ 52.165355] [<802f1c5c>] (dw_pcie_rd_conf) from [<802d6534>] (pci_bus_read_config_dword+0x6c/0x94)
[ 52.174316] r9:adf4e910 r8:809bc51c r7:00000000 r6:60000153 r5:adc3fd5c r4:ae726000
[ 52.182152] [<802d64c8>] (pci_bus_read_config_dword) from [<802db588>] (pci_restore_config_dword+0x54/0xa4)
[ 52.191894] r6:00000024 r5:0000000a r4:ae61e000
[ 52.196570] [<802db534>] (pci_restore_config_dword) from [<802dd150>] (pci_restore_state.part.37+0x7c/0x1f8)
[ 52.206399] r8:802e086c r7:ae61dfe8 r6:519e2024 r5:ae61e000 r4:ae61dffc
[ 52.213187] [<802dd0d4>] (pci_restore_state.part.37) from [<802dd2e8>] (pci_restore_state+0x1c/0x20)
[ 52.222322] r7:adc3fea8 r6:809d90e0 r5:ae61e000 r4:ae61e068
[ 52.228056] [<802dd2cc>] (pci_restore_state) from [<802e0894>] (pci_pm_resume_noirq+0x28/0xa4)
[ 52.236683] [<802e086c>] (pci_pm_resume_noirq) from [<8037ea1c>] (dpm_run_callback.isra.12+0x34/0x7c)
---
drivers/pci/host/pci-imx6.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c
index be953aa..e60c195 100644
--- a/drivers/pci/host/pci-imx6.c
+++ b/drivers/pci/host/pci-imx6.c
@@ -521,7 +521,7 @@ static void imx6_pcie_reset_phy(struct pcie_port *pp)
static int imx6_pcie_link_up(struct pcie_port *pp)
{
u32 rc, debug_r0, rx_valid;
- int count = 5;
+ int count = 500;
/*
* Test if the PHY reports that the link is up and also that the LTSSM
@@ -552,7 +552,7 @@ static int imx6_pcie_link_up(struct pcie_port *pp)
* Wait a little bit, then re-check if the link finished
* the training.
*/
- usleep_range(1000, 2000);
+ udelay(10);
}
/*
* From L0, initiate MAC entry to gen2 if EP/RC supports gen2.
--
Pengutronix e.K. | Lucas Stach |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Richard Zhu
2014-09-29 05:03:16 UTC
Permalink
- move "program correct class for RC" from dw_pcie_host_init()
to dw_pcie_setup_rc(). since this is RC setup, it's
better to contained in dw_pcie_setup_rc function.
Then, RC can be re-setup really by dw_pcie_setup_rc().
- add one re-store msi data function. Because that
pcie controller maybe powered off during system suspend,
and the msi data configuration would be lost.
this functions can be used to restore the msi data
during the resume callback.

Signed-off-by: Richard Zhu <***@freescale.com>
---
drivers/pci/host/pcie-designware.c | 15 ++++++++++++---
drivers/pci/host/pcie-designware.h | 1 +
2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
index 538bbf3..ae1e6c5 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/host/pcie-designware.c
@@ -194,6 +194,13 @@ void dw_pcie_msi_init(struct pcie_port *pp)
dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0);
}

+void dw_pcie_msi_cfg_restore(struct pcie_port *pp)
+{
+ dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4,
+ virt_to_phys((void *)pp->msi_data));
+ dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0);
+}
+
static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0)
{
int flag = 1;
@@ -570,9 +577,6 @@ int __init dw_pcie_host_init(struct pcie_port *pp)

dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);

- /* program correct class for RC */
- dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI);
-
dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val);
val |= PORT_LOGIC_SPEED_CHANGE;
dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
@@ -917,6 +921,11 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
val = memlimit | membase;
dw_pcie_writel_rc(pp, val, PCI_MEMORY_BASE);

+ /* program correct class for RC */
+ dw_pcie_readl_rc(pp, PCI_CLASS_REVISION, &val);
+ val |= PCI_CLASS_BRIDGE_PCI << 16;
+ dw_pcie_writel_rc(pp, val, PCI_CLASS_REVISION);
+
/* setup command register */
dw_pcie_readl_rc(pp, PCI_COMMAND, &val);
val &= 0xffff0000;
diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h
index a476e60..bb75715 100644
--- a/drivers/pci/host/pcie-designware.h
+++ b/drivers/pci/host/pcie-designware.h
@@ -83,6 +83,7 @@ int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val);
int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val);
irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
void dw_pcie_msi_init(struct pcie_port *pp);
+void dw_pcie_msi_cfg_restore(struct pcie_port *pp);
int dw_pcie_link_up(struct pcie_port *pp);
void dw_pcie_setup_rc(struct pcie_port *pp);
int dw_pcie_host_init(struct pcie_port *pp);
--
1.9.1
Lucas Stach
2014-09-29 10:26:44 UTC
Permalink
Post by Richard Zhu
- move "program correct class for RC" from dw_pcie_host_init()
to dw_pcie_setup_rc(). since this is RC setup, it's
better to contained in dw_pcie_setup_rc function.
Then, RC can be re-setup really by dw_pcie_setup_rc().
- add one re-store msi data function. Because that
pcie controller maybe powered off during system suspend,
and the msi data configuration would be lost.
this functions can be used to restore the msi data
during the resume callback.
Those are two independent changes. So split into two patches. Also you
are using the msi restore function in your imx6sx enable patch, so this
needs to earlier in the series in order to not break compilation.
Post by Richard Zhu
---
drivers/pci/host/pcie-designware.c | 15 ++++++++++++---
drivers/pci/host/pcie-designware.h | 1 +
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
index 538bbf3..ae1e6c5 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/host/pcie-designware.c
@@ -194,6 +194,13 @@ void dw_pcie_msi_init(struct pcie_port *pp)
dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0);
}
+void dw_pcie_msi_cfg_restore(struct pcie_port *pp)
+{
+ dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4,
+ virt_to_phys((void *)pp->msi_data));
+ dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0);
+}
+
static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0)
{
int flag = 1;
@@ -570,9 +577,6 @@ int __init dw_pcie_host_init(struct pcie_port *pp)
dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);
- /* program correct class for RC */
- dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI);
-
dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val);
val |= PORT_LOGIC_SPEED_CHANGE;
dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
@@ -917,6 +921,11 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
val = memlimit | membase;
dw_pcie_writel_rc(pp, val, PCI_MEMORY_BASE);
+ /* program correct class for RC */
+ dw_pcie_readl_rc(pp, PCI_CLASS_REVISION, &val);
+ val |= PCI_CLASS_BRIDGE_PCI << 16;
+ dw_pcie_writel_rc(pp, val, PCI_CLASS_REVISION);
+
You don't need this read-modify-write dance and the shift if you only
access the upper word of the register. The call you are removing does
exactly the right thing, please don't change it, just move it.
Post by Richard Zhu
/* setup command register */
dw_pcie_readl_rc(pp, PCI_COMMAND, &val);
val &= 0xffff0000;
diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h
index a476e60..bb75715 100644
--- a/drivers/pci/host/pcie-designware.h
+++ b/drivers/pci/host/pcie-designware.h
@@ -83,6 +83,7 @@ int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val);
int dw_pcie_cfg_write(void __iomem *addr, int where, int size, u32 val);
irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
void dw_pcie_msi_init(struct pcie_port *pp);
+void dw_pcie_msi_cfg_restore(struct pcie_port *pp);
int dw_pcie_link_up(struct pcie_port *pp);
void dw_pcie_setup_rc(struct pcie_port *pp);
int dw_pcie_host_init(struct pcie_port *pp);
--
Pengutronix e.K. | Lucas Stach |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Richard Zhu
2014-09-29 05:03:15 UTC
Permalink
- 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 <***@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>
+#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);
--
1.9.1
Lucas Stach
2014-09-29 10:18:41 UTC
Permalink
Post by 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.
---
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?
Post by Richard Zhu
+#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.
Post by Richard Zhu
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;
+ clk_disable_unprepare(imx6_pcie->pcie);
clk_disable_unprepare(imx6_pcie->pcie_bus);
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.
Post by Richard Zhu
+ }
+
+ 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);
--
Pengutronix e.K. | Lucas Stach |
Industrial Linux Solutions | http://www.pengutronix.de/ |
H***@freescale.com
2014-09-30 02:37:20 UTC
Permalink
Hi Lucas:
Thanks for your review comments.
-----Original Message-----
Sent: Monday, September 29, 2014 6:19 PM
To: Zhu Richard-R65037
Subject: Re: [PATCH v3 7/9] PCI: imx6: add imx6sx pcie support
Post by 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.
---
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?
[Richard]It's not required anymore, would be removed later.
Post by Richard Zhu
+#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.
[Richard]how about like "pcie_axi_inbound"?
Post by Richard Zhu
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)
Post by Richard Zhu
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)
Post by Richard Zhu
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;
+ clk_disable_unprepare(imx6_pcie->pcie);
clk_disable_unprepare(imx6_pcie->pcie_bus);
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,
int
Post by Richard Zhu
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,
Post by Richard Zhu
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.
[Richard] Ok, all these shifts wouldn't be removed.
Post by Richard Zhu
+ }
+
+ 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)
Post by Richard Zhu
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");
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);
--
Pengutronix e.K. | Lucas Stach |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Best Regards
Richard Z
Richard Zhu
2014-09-29 05:03:14 UTC
Permalink
Signed-off-by: Richard Zhu <***@freescale.com>
---
arch/arm/boot/dts/imx6sx-sdb.dts | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/arch/arm/boot/dts/imx6sx-sdb.dts b/arch/arm/boot/dts/imx6sx-sdb.dts
index a3980d9..2976913 100644
--- a/arch/arm/boot/dts/imx6sx-sdb.dts
+++ b/arch/arm/boot/dts/imx6sx-sdb.dts
@@ -251,6 +251,13 @@
};
};

+&pcie {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pcie>;
+ reset-gpio = <&gpio2 0 0>;
+ status = "okay";
+};
+
&ssi2 {
status = "okay";
};
@@ -365,6 +372,12 @@
;
};

+ pinctrl_pcie: pciegrp {
+ fsl,pins = <
+ MX6SX_PAD_ENET1_COL__GPIO2_IO_0 0x17059
+ >;
+ };
+
pinctrl_vcc_sd3: vccsd3grp {
fsl,pins = <
MX6SX_PAD_KEY_COL1__GPIO2_IO_11 0x17059
--
1.9.1
Richard Zhu
2014-09-29 05:03:11 UTC
Permalink
- imx6sx pcie phy has its own power regulator. Add the
pcie phy power suppy into im6sx pcie dts and binding.
- in order to align with imx6qdl's pcie dts, re-format
imx6sx pcie dts.
- in order to align with imx6qdl pcie dts format and
keep clean of imx6 pcie driver, keep the pcie phy clock
in imx6sx pcie dts, although it's the parent clk of the
pcie bus clock now, and would be enabled automatically
when pcie bus clock is enabled. secondly, it's
possible that the external osc maybe used as source
of the pcie_bus clk in board design in future.
- disp_axi clock is required by pcie inbound axi port.
Add one more clock for imx6sx pcie.

Signed-off-by: Richard Zhu <***@freescale.com>
---
.../devicetree/bindings/pci/fsl,imx6q-pcie.txt | 5 +++-
arch/arm/boot/dts/imx6sx.dtsi | 30 ++++++++++++----------
2 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
index 9455fd0..981e41d 100644
--- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
@@ -4,7 +4,7 @@ This PCIe host controller is based on the Synopsis Designware PCIe IP
and thus inherits all the common properties defined in designware-pcie.txt.

Required properties:
-- compatible: "fsl,imx6q-pcie"
+- compatible: "fsl,imx6q-pcie", "fsl,imx6sx-pcie"
- reg: base addresse and length of the pcie controller
- interrupts: A list of interrupt outputs of the controller. Must contain an
entry for each entry in the interrupt-names property.
@@ -13,6 +13,9 @@ Required properties:
- clock-names: Must include the following additional entries:
- "pcie_phy"

+Power supplies for imx6sx:
+- pcie_phy-supply: regulator used by imx6sx pcie phy.
+
Example:

***@0x01000000 {
diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi
index f4b9da6..b4ca94b 100644
--- a/arch/arm/boot/dts/imx6sx.dtsi
+++ b/arch/arm/boot/dts/imx6sx.dtsi
@@ -599,9 +599,9 @@
anatop-max-voltage = <1450000>;
};

- reg_pcie: regulator-***@140 {
+ reg_pcie_phy: regulator-***@140 {
compatible = "fsl,anatop-regulator";
- regulator-name = "vddpcie";
+ regulator-name = "vddpcie_phy";
regulator-min-microvolt = <725000>;
regulator-max-microvolt = <1450000>;
anatop-reg-offset = <0x140>;
@@ -1188,20 +1188,24 @@
#address-cells = <3>;
#size-cells = <2>;
device_type = "pci";
- /* configuration space */
- ranges = <0x00000800 0 0x08f00000 0x08f00000 0 0x00080000
- /* downstream I/O */
- 0x81000000 0 0 0x08f80000 0 0x00010000
- /* non-prefetchable memory */
- 0x82000000 0 0x08000000 0x08000000 0 0x00f00000>;
+ ranges = <0x00000800 0 0x08f00000 0x08f00000 0 0x00080000 /* configuration space */
+ 0x81000000 0 0 0x08f80000 0 0x00010000 /* downstream I/O */
+ 0x82000000 0 0x08000000 0x08000000 0 0x00f00000>; /* non-prefetchable memory */
num-lanes = <1>;
- interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&clks IMX6SX_CLK_PCIE_REF_125M>,
- <&clks IMX6SX_CLK_PCIE_AXI>,
+ interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 2 &intc GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 3 &intc GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 4 &intc GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks IMX6SX_CLK_PCIE_AXI>,
<&clks IMX6SX_CLK_LVDS1_OUT>,
+ <&clks IMX6SX_CLK_PCIE_REF_125M>,
<&clks IMX6SX_CLK_DISPLAY_AXI>;
- clock-names = "pcie_ref_125m", "pcie_axi",
- "lvds_gate", "display_axi";
+ clock-names = "pcie", "pcie_bus", "pcie_phy", "disp_axi";
+ pcie_phy-supply = <&reg_pcie_phy>;
status = "disabled";
};
};
--
1.9.1
Lucas Stach
2014-09-29 10:13:20 UTC
Permalink
Post by Richard Zhu
- imx6sx pcie phy has its own power regulator. Add the
pcie phy power suppy into im6sx pcie dts and binding.
- in order to align with imx6qdl's pcie dts, re-format
imx6sx pcie dts.
- in order to align with imx6qdl pcie dts format and
keep clean of imx6 pcie driver, keep the pcie phy clock
in imx6sx pcie dts, although it's the parent clk of the
pcie bus clock now, and would be enabled automatically
when pcie bus clock is enabled. secondly, it's
possible that the external osc maybe used as source
of the pcie_bus clk in board design in future.
- disp_axi clock is required by pcie inbound axi port.
Add one more clock for imx6sx pcie.
---
.../devicetree/bindings/pci/fsl,imx6q-pcie.txt | 5 +++-
arch/arm/boot/dts/imx6sx.dtsi | 30 ++++++++++++----------
2 files changed, 21 insertions(+), 14 deletions(-)
diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
index 9455fd0..981e41d 100644
--- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
@@ -4,7 +4,7 @@ This PCIe host controller is based on the Synopsis Designware PCIe IP
and thus inherits all the common properties defined in designware-pcie.txt.
-- compatible: "fsl,imx6q-pcie"
+- compatible: "fsl,imx6q-pcie", "fsl,imx6sx-pcie"
- reg: base addresse and length of the pcie controller
- interrupts: A list of interrupt outputs of the controller. Must contain an
entry for each entry in the interrupt-names property.
- "pcie_phy"
+- pcie_phy-supply: regulator used by imx6sx pcie phy.
+
To align with the previous practice of naming regulator supplies please
don't use underscores in the name. Also you aren't documenting the
additional clock (which also needs a descriptive name, NOT "disp_axi").
I would recommend the documentation change to look like this:

Additional required properties for imx6sx-pcie:
- clock names: Must include the following additional entries:
- "pcie_inbound_axi"
- power supplies:
- pcie-phy-supply: regulator used to power the PCIe PHY

Please update the DTS and driver accordingly.
Post by Richard Zhu
diff --git a/arch/arm/boot/dts/imx6sx.dtsi b/arch/arm/boot/dts/imx6sx.dtsi
index f4b9da6..b4ca94b 100644
--- a/arch/arm/boot/dts/imx6sx.dtsi
+++ b/arch/arm/boot/dts/imx6sx.dtsi
@@ -599,9 +599,9 @@
anatop-max-voltage = <1450000>;
};
compatible = "fsl,anatop-regulator";
- regulator-name = "vddpcie";
+ regulator-name = "vddpcie_phy";
regulator-min-microvolt = <725000>;
regulator-max-microvolt = <1450000>;
anatop-reg-offset = <0x140>;
@@ -1188,20 +1188,24 @@
#address-cells = <3>;
#size-cells = <2>;
device_type = "pci";
- /* configuration space */
- ranges = <0x00000800 0 0x08f00000 0x08f00000 0 0x00080000
- /* downstream I/O */
- 0x81000000 0 0 0x08f80000 0 0x00010000
- /* non-prefetchable memory */
- 0x82000000 0 0x08000000 0x08000000 0 0x00f00000>;
+ ranges = <0x00000800 0 0x08f00000 0x08f00000 0 0x00080000 /* configuration space */
+ 0x81000000 0 0 0x08f80000 0 0x00010000 /* downstream I/O */
+ 0x82000000 0 0x08000000 0x08000000 0 0x00f00000>; /* non-prefetchable memory */
num-lanes = <1>;
- interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&clks IMX6SX_CLK_PCIE_REF_125M>,
- <&clks IMX6SX_CLK_PCIE_AXI>,
+ interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 2 &intc GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 3 &intc GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 4 &intc GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks IMX6SX_CLK_PCIE_AXI>,
<&clks IMX6SX_CLK_LVDS1_OUT>,
+ <&clks IMX6SX_CLK_PCIE_REF_125M>,
<&clks IMX6SX_CLK_DISPLAY_AXI>;
- clock-names = "pcie_ref_125m", "pcie_axi",
- "lvds_gate", "display_axi";
+ clock-names = "pcie", "pcie_bus", "pcie_phy", "disp_axi";
+ pcie_phy-supply = <&reg_pcie_phy>;
status = "disabled";
};
};
You are still missing the "config" reg space. Please look at the
designware-pcie.txt base binding, it's a required property now. Also
please be more careful to address my review comments.

Regards,
Lucas
--
Pengutronix e.K. | Lucas Stach |
Industrial Linux Solutions | http://www.pengutronix.de/ |
H***@freescale.com
2014-09-30 02:58:19 UTC
Permalink
Hi Lucas:
Thanks for your review comments.
-----Original Message-----
Sent: Monday, September 29, 2014 6:13 PM
To: Zhu Richard-R65037
Subject: Re: [PATCH v3 3/9] PCI: imx6: update dts and binding for imx6sx pcie
- imx6sx pcie phy has its own power regulator. Add the pcie phy power
suppy into im6sx pcie dts and binding.
- in order to align with imx6qdl's pcie dts, re-format imx6sx pcie
dts.
- in order to align with imx6qdl pcie dts format and keep clean of
imx6 pcie driver, keep the pcie phy clock in imx6sx pcie dts, although
it's the parent clk of the pcie bus clock now, and would be enabled
automatically when pcie bus clock is enabled. secondly, it's possible
that the external osc maybe used as source of the pcie_bus clk in
board design in future.
- disp_axi clock is required by pcie inbound axi port.
Add one more clock for imx6sx pcie.
---
.../devicetree/bindings/pci/fsl,imx6q-pcie.txt | 5 +++-
arch/arm/boot/dts/imx6sx.dtsi | 30 ++++++++++++-------
---
2 files changed, 21 insertions(+), 14 deletions(-)
diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
index 9455fd0..981e41d 100644
--- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
@@ -4,7 +4,7 @@ This PCIe host controller is based on the Synopsis
Designware PCIe IP and thus inherits all the common properties defined in
designware-pcie.txt.
-- compatible: "fsl,imx6q-pcie"
+- compatible: "fsl,imx6q-pcie", "fsl,imx6sx-pcie"
- reg: base addresse and length of the pcie controller
- interrupts: A list of interrupt outputs of the controller. Must contain
an
entry for each entry in the interrupt-names property.
- "pcie_phy"
+- pcie_phy-supply: regulator used by imx6sx pcie phy.
+
To align with the previous practice of naming regulator supplies please don't
use underscores in the name. Also you aren't documenting the additional clock
(which also needs a descriptive name, NOT "disp_axi").
- "pcie_inbound_axi"
- pcie-phy-supply: regulator used to power the PCIe PHY
Please update the DTS and driver accordingly.
[Richard] Accepted. pcie_inbound_axi is ok.
diff --git a/arch/arm/boot/dts/imx6sx.dtsi
b/arch/arm/boot/dts/imx6sx.dtsi index f4b9da6..b4ca94b 100644
--- a/arch/arm/boot/dts/imx6sx.dtsi
+++ b/arch/arm/boot/dts/imx6sx.dtsi
@@ -599,9 +599,9 @@
anatop-max-voltage = <1450000>;
};
compatible = "fsl,anatop-regulator";
- regulator-name = "vddpcie";
+ regulator-name = "vddpcie_phy";
regulator-min-microvolt = <725000>;
regulator-max-microvolt = <1450000>;
anatop-reg-offset = <0x140>;
@@ -1188,20 +1188,24 @@
#address-cells = <3>;
#size-cells = <2>;
device_type = "pci";
- /* configuration space */
- ranges = <0x00000800 0 0x08f00000 0x08f00000 0 0x00080000
- /* downstream I/O */
- 0x81000000 0 0 0x08f80000 0 0x00010000
- /* non-prefetchable memory */
- 0x82000000 0 0x08000000 0x08000000 0 0x00f00000>;
+ ranges = <0x00000800 0 0x08f00000 0x08f00000 0 0x00080000 /*
configuration space */
+ 0x81000000 0 0 0x08f80000 0 0x00010000 /*
downstream I/O */
+ 0x82000000 0 0x08000000 0x08000000 0 0x00f00000>; /*
+non-prefetchable memory */
num-lanes = <1>;
- interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&clks IMX6SX_CLK_PCIE_REF_125M>,
- <&clks IMX6SX_CLK_PCIE_AXI>,
+ interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 2 &intc GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 3 &intc GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 4 &intc GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks IMX6SX_CLK_PCIE_AXI>,
<&clks IMX6SX_CLK_LVDS1_OUT>,
+ <&clks IMX6SX_CLK_PCIE_REF_125M>,
<&clks IMX6SX_CLK_DISPLAY_AXI>;
- clock-names = "pcie_ref_125m", "pcie_axi",
- "lvds_gate", "display_axi";
+ clock-names = "pcie", "pcie_bus", "pcie_phy", "disp_axi";
+ pcie_phy-supply = <&reg_pcie_phy>;
status = "disabled";
};
};
You are still missing the "config" reg space. Please look at the designware-
pcie.txt base binding, it's a required property now. Also please be more
careful to address my review comments.
[Richard]Sorry, mis-understand your previous comment.
Catch your point now, would add the new "config" reg space later.
Regards,
Lucas
--
Pengutronix e.K. | Lucas Stach |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Best Regards
Ric

Lucas Stach
2014-09-29 09:56:53 UTC
Permalink
Post by Richard Zhu
- enable pcie on imx6qdl sabreauto boards.
---
arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
index 009abd6..d6040a5 100644
--- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
@@ -410,6 +410,10 @@
};
};
+&pcie {
+ status = "okay";
+};
+
&pwm3 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pwm3>;
The dts changes have a wrong prefix. They are not PCI subsystem patches,
but arch patches for i.MX and should be applied by Shawn. So prefix
needs to be ARM: imx6XX: ...

Also I would recommend that you sort them to the end of the series so
make it easy for Bjorn and Shawn to see where the slit between the two
is. Don't intermix those changes in the series, it certainly isn't
needed here.

Regards,
Lucas
--
Pengutronix e.K. | Lucas Stach |
Industrial Linux Solutions | http://www.pengutronix.de/ |
H***@freescale.com
2014-09-30 02:18:30 UTC
Permalink
Best Regards
Richard Zhu
-----Original Message-----
Sent: Monday, September 29, 2014 5:57 PM
To: Zhu Richard-R65037
Subject: Re: [PATCH v3 2/9] PCI: imx6: enable pcie on imx6qdl sabreauto
Post by Richard Zhu
- enable pcie on imx6qdl sabreauto boards.
---
arch/arm/boot/dts/imx6qdl-sabreauto.dtsi | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
index 009abd6..d6040a5 100644
--- a/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-sabreauto.dtsi
@@ -410,6 +410,10 @@
};
};
+&pcie {
+ status = "okay";
+};
+
&pwm3 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pwm3>;
The dts changes have a wrong prefix. They are not PCI subsystem patches, but
arch patches for i.MX and should be applied by Shawn. So prefix needs to be
ARM: imx6XX: ...
Also I would recommend that you sort them to the end of the series so make it
easy for Bjorn and Shawn to see where the slit between the two is. Don't
intermix those changes in the series, it certainly isn't needed here.
[Richard] Ok, no problem. Thanks.
Regards,
Lucas
--
Pengutronix e.K. | Lucas Stach |
Industrial Linux Solutions | http://www.pengutronix.
Loading...