PCI: imx6: support MPLL reconfiguration for 100MHz and 200MHz refclock

Message ID 20180731102149.8537-1-l.stach@pengutronix.de
State Accepted
Delegated to: Lorenzo Pieralisi
Headers show
Series
  • PCI: imx6: support MPLL reconfiguration for 100MHz and 200MHz refclock
Related show

Commit Message

Lucas Stach July 31, 2018, 10:21 a.m.
The power up defaults of the MPLL are designed for the standard 125MHz
refclock derived from the ENET PLL. As this clock has a jitter that
violates the PCIe Gen2 timing requirements, some board designs use
an external reference clock generator. Those clock generators may
output a clock at a different rate than what the MPLL expects
(usually a 100MHz clock, to re-use the PCIe bus clock).

In that case the MPLL must be reconfigured via overrides to use
different refclock dividers and loop multipliers. The i.MX6
reference manual lists both 100MHz and 200MHz as supported refclock
rates and the associated mult and div values.

Only the 100MHz setup has been tested on a real board, but since the
200MHz setup only differs in the used pre-divider it seems safe to
add it now.

Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
 drivers/pci/controller/dwc/pci-imx6.c | 55 +++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

Comments

Richard Zhu Aug. 1, 2018, 8:20 a.m. | #1
> -----Original Message-----
> From: Lucas Stach [mailto:l.stach@pengutronix.de]
> Sent: Tuesday, July 31, 2018 6:22 PM
> To: Richard Zhu <hongxing.zhu@nxp.com>; Lorenzo Pieralisi
> <lorenzo.pieralisi@arm.com>
> Cc: linux-pci@vger.kernel.org; linux-arm-kernel@lists.infradead.org;
> kernel@pengutronix.de; patchwork-lst@pengutronix.de
> Subject: [PATCH] PCI: imx6: support MPLL reconfiguration for 100MHz and
> 200MHz refclock
> 
> The power up defaults of the MPLL are designed for the standard 125MHz
> refclock derived from the ENET PLL. As this clock has a jitter that violates the
> PCIe Gen2 timing requirements, some board designs use an external reference
> clock generator. Those clock generators may output a clock at a different rate
> than what the MPLL expects (usually a 100MHz clock, to re-use the PCIe bus
> clock).
> 
> In that case the MPLL must be reconfigured via overrides to use different
> refclock dividers and loop multipliers. The i.MX6 reference manual lists both
> 100MHz and 200MHz as supported refclock rates and the associated mult and
> div values.
> 
> Only the 100MHz setup has been tested on a real board, but since the 200MHz
> setup only differs in the used pre-divider it seems safe to add it now.
> 
> Signed-off-by: Lucas Stach <l.stach@pengutronix.de>

Reviewed-by: Richard Zhu <hongxing.zhu@nxp.com>
Richard Zhu
Best Regards!

> ---
>  drivers/pci/controller/dwc/pci-imx6.c | 55 +++++++++++++++++++++++++++
>  1 file changed, 55 insertions(+)
> 
> diff --git a/drivers/pci/controller/dwc/pci-imx6.c
> b/drivers/pci/controller/dwc/pci-imx6.c
> index 80f604602783..c481c61a41b8 100644
> --- a/drivers/pci/controller/dwc/pci-imx6.c
> +++ b/drivers/pci/controller/dwc/pci-imx6.c
> @@ -97,6 +97,16 @@ struct imx6_pcie {
>  #define PORT_LOGIC_SPEED_CHANGE		(0x1 << 17)
> 
>  /* PHY registers (not memory-mapped) */
> +#define PCIE_PHY_ATEOVRD			0x10
> +#define  PCIE_PHY_ATEOVRD_EN			(0x1 << 2)
> +#define  PCIE_PHY_ATEOVRD_REF_CLKDIV_SHIFT	0
> +#define  PCIE_PHY_ATEOVRD_REF_CLKDIV_MASK	0x1
> +
> +#define PCIE_PHY_MPLL_OVRD_IN_LO		0x11
> +#define  PCIE_PHY_MPLL_MULTIPLIER_SHIFT		2
> +#define  PCIE_PHY_MPLL_MULTIPLIER_MASK		0x7f
> +#define  PCIE_PHY_MPLL_MULTIPLIER_OVRD		(0x1 << 9)
> +
>  #define PCIE_PHY_RX_ASIC_OUT 0x100D
>  #define PCIE_PHY_RX_ASIC_OUT_VALID	(1 << 0)
> 
> @@ -508,6 +518,50 @@ static void imx6_pcie_init_phy(struct imx6_pcie
> *imx6_pcie)
>  			IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT <<
> 12);  }
> 
> +static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie) {
> +	unsigned long phy_rate = clk_get_rate(imx6_pcie->pcie_phy);
> +	int mult, div;
> +	u32 val;
> +
> +	switch (phy_rate) {
> +	case 125000000:
> +		/*
> +		 * The default settings of the MPLL are for a 125MHz input
> +		 * clock, so no need to reconfigure anything in that case.
> +		 */
> +		return 0;
> +	case 100000000:
> +		mult = 25;
> +		div = 0;
> +		break;
> +	case 200000000:
> +		mult = 25;
> +		div = 1;
> +		break;
> +	default:
> +		dev_err(imx6_pcie->pci->dev,
> +			"Unsupported PHY reference clock rate %lu\n", phy_rate);
> +		return -EINVAL;
> +	}
> +
> +	pcie_phy_read(imx6_pcie, PCIE_PHY_MPLL_OVRD_IN_LO, &val);
> +	val &= ~(PCIE_PHY_MPLL_MULTIPLIER_MASK <<
> +		 PCIE_PHY_MPLL_MULTIPLIER_SHIFT);
> +	val |= mult << PCIE_PHY_MPLL_MULTIPLIER_SHIFT;
> +	val |= PCIE_PHY_MPLL_MULTIPLIER_OVRD;
> +	pcie_phy_write(imx6_pcie, PCIE_PHY_MPLL_OVRD_IN_LO, val);
> +
> +	pcie_phy_read(imx6_pcie, PCIE_PHY_ATEOVRD, &val);
> +	val &= ~(PCIE_PHY_ATEOVRD_REF_CLKDIV_MASK <<
> +		 PCIE_PHY_ATEOVRD_REF_CLKDIV_SHIFT);
> +	val |= div << PCIE_PHY_ATEOVRD_REF_CLKDIV_SHIFT;
> +	val |= PCIE_PHY_ATEOVRD_EN;
> +	pcie_phy_write(imx6_pcie, PCIE_PHY_ATEOVRD, val);
> +
> +	return 0;
> +}
> +
>  static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie)  {
>  	struct dw_pcie *pci = imx6_pcie->pci;
> @@ -632,6 +686,7 @@ static int imx6_pcie_host_init(struct pcie_port *pp)
>  	imx6_pcie_assert_core_reset(imx6_pcie);
>  	imx6_pcie_init_phy(imx6_pcie);
>  	imx6_pcie_deassert_core_reset(imx6_pcie);
> +	imx6_setup_phy_mpll(imx6_pcie);
>  	dw_pcie_setup_rc(pp);
>  	imx6_pcie_establish_link(imx6_pcie);
> 
> --
> 2.18.0
Lorenzo Pieralisi Sept. 17, 2018, 10:31 a.m. | #2
On Tue, Jul 31, 2018 at 12:21:49PM +0200, Lucas Stach wrote:
> The power up defaults of the MPLL are designed for the standard 125MHz
> refclock derived from the ENET PLL. As this clock has a jitter that
> violates the PCIe Gen2 timing requirements, some board designs use
> an external reference clock generator. Those clock generators may
> output a clock at a different rate than what the MPLL expects
> (usually a 100MHz clock, to re-use the PCIe bus clock).
> 
> In that case the MPLL must be reconfigured via overrides to use
> different refclock dividers and loop multipliers. The i.MX6
> reference manual lists both 100MHz and 200MHz as supported refclock
> rates and the associated mult and div values.
> 
> Only the 100MHz setup has been tested on a real board, but since the
> 200MHz setup only differs in the used pre-divider it seems safe to
> add it now.
> 
> Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
> ---
>  drivers/pci/controller/dwc/pci-imx6.c | 55 +++++++++++++++++++++++++++
>  1 file changed, 55 insertions(+)

Applied to pci/dwc for v4.20, thanks.

Lorenzo

> diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
> index 80f604602783..c481c61a41b8 100644
> --- a/drivers/pci/controller/dwc/pci-imx6.c
> +++ b/drivers/pci/controller/dwc/pci-imx6.c
> @@ -97,6 +97,16 @@ struct imx6_pcie {
>  #define PORT_LOGIC_SPEED_CHANGE		(0x1 << 17)
>  
>  /* PHY registers (not memory-mapped) */
> +#define PCIE_PHY_ATEOVRD			0x10
> +#define  PCIE_PHY_ATEOVRD_EN			(0x1 << 2)
> +#define  PCIE_PHY_ATEOVRD_REF_CLKDIV_SHIFT	0
> +#define  PCIE_PHY_ATEOVRD_REF_CLKDIV_MASK	0x1
> +
> +#define PCIE_PHY_MPLL_OVRD_IN_LO		0x11
> +#define  PCIE_PHY_MPLL_MULTIPLIER_SHIFT		2
> +#define  PCIE_PHY_MPLL_MULTIPLIER_MASK		0x7f
> +#define  PCIE_PHY_MPLL_MULTIPLIER_OVRD		(0x1 << 9)
> +
>  #define PCIE_PHY_RX_ASIC_OUT 0x100D
>  #define PCIE_PHY_RX_ASIC_OUT_VALID	(1 << 0)
>  
> @@ -508,6 +518,50 @@ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
>  			IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12);
>  }
>  
> +static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie)
> +{
> +	unsigned long phy_rate = clk_get_rate(imx6_pcie->pcie_phy);
> +	int mult, div;
> +	u32 val;
> +
> +	switch (phy_rate) {
> +	case 125000000:
> +		/*
> +		 * The default settings of the MPLL are for a 125MHz input
> +		 * clock, so no need to reconfigure anything in that case.
> +		 */
> +		return 0;
> +	case 100000000:
> +		mult = 25;
> +		div = 0;
> +		break;
> +	case 200000000:
> +		mult = 25;
> +		div = 1;
> +		break;
> +	default:
> +		dev_err(imx6_pcie->pci->dev,
> +			"Unsupported PHY reference clock rate %lu\n", phy_rate);
> +		return -EINVAL;
> +	}
> +
> +	pcie_phy_read(imx6_pcie, PCIE_PHY_MPLL_OVRD_IN_LO, &val);
> +	val &= ~(PCIE_PHY_MPLL_MULTIPLIER_MASK <<
> +		 PCIE_PHY_MPLL_MULTIPLIER_SHIFT);
> +	val |= mult << PCIE_PHY_MPLL_MULTIPLIER_SHIFT;
> +	val |= PCIE_PHY_MPLL_MULTIPLIER_OVRD;
> +	pcie_phy_write(imx6_pcie, PCIE_PHY_MPLL_OVRD_IN_LO, val);
> +
> +	pcie_phy_read(imx6_pcie, PCIE_PHY_ATEOVRD, &val);
> +	val &= ~(PCIE_PHY_ATEOVRD_REF_CLKDIV_MASK <<
> +		 PCIE_PHY_ATEOVRD_REF_CLKDIV_SHIFT);
> +	val |= div << PCIE_PHY_ATEOVRD_REF_CLKDIV_SHIFT;
> +	val |= PCIE_PHY_ATEOVRD_EN;
> +	pcie_phy_write(imx6_pcie, PCIE_PHY_ATEOVRD, val);
> +
> +	return 0;
> +}
> +
>  static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie)
>  {
>  	struct dw_pcie *pci = imx6_pcie->pci;
> @@ -632,6 +686,7 @@ static int imx6_pcie_host_init(struct pcie_port *pp)
>  	imx6_pcie_assert_core_reset(imx6_pcie);
>  	imx6_pcie_init_phy(imx6_pcie);
>  	imx6_pcie_deassert_core_reset(imx6_pcie);
> +	imx6_setup_phy_mpll(imx6_pcie);
>  	dw_pcie_setup_rc(pp);
>  	imx6_pcie_establish_link(imx6_pcie);
>  
> -- 
> 2.18.0
>

Patch

diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 80f604602783..c481c61a41b8 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -97,6 +97,16 @@  struct imx6_pcie {
 #define PORT_LOGIC_SPEED_CHANGE		(0x1 << 17)
 
 /* PHY registers (not memory-mapped) */
+#define PCIE_PHY_ATEOVRD			0x10
+#define  PCIE_PHY_ATEOVRD_EN			(0x1 << 2)
+#define  PCIE_PHY_ATEOVRD_REF_CLKDIV_SHIFT	0
+#define  PCIE_PHY_ATEOVRD_REF_CLKDIV_MASK	0x1
+
+#define PCIE_PHY_MPLL_OVRD_IN_LO		0x11
+#define  PCIE_PHY_MPLL_MULTIPLIER_SHIFT		2
+#define  PCIE_PHY_MPLL_MULTIPLIER_MASK		0x7f
+#define  PCIE_PHY_MPLL_MULTIPLIER_OVRD		(0x1 << 9)
+
 #define PCIE_PHY_RX_ASIC_OUT 0x100D
 #define PCIE_PHY_RX_ASIC_OUT_VALID	(1 << 0)
 
@@ -508,6 +518,50 @@  static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
 			IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12);
 }
 
+static int imx6_setup_phy_mpll(struct imx6_pcie *imx6_pcie)
+{
+	unsigned long phy_rate = clk_get_rate(imx6_pcie->pcie_phy);
+	int mult, div;
+	u32 val;
+
+	switch (phy_rate) {
+	case 125000000:
+		/*
+		 * The default settings of the MPLL are for a 125MHz input
+		 * clock, so no need to reconfigure anything in that case.
+		 */
+		return 0;
+	case 100000000:
+		mult = 25;
+		div = 0;
+		break;
+	case 200000000:
+		mult = 25;
+		div = 1;
+		break;
+	default:
+		dev_err(imx6_pcie->pci->dev,
+			"Unsupported PHY reference clock rate %lu\n", phy_rate);
+		return -EINVAL;
+	}
+
+	pcie_phy_read(imx6_pcie, PCIE_PHY_MPLL_OVRD_IN_LO, &val);
+	val &= ~(PCIE_PHY_MPLL_MULTIPLIER_MASK <<
+		 PCIE_PHY_MPLL_MULTIPLIER_SHIFT);
+	val |= mult << PCIE_PHY_MPLL_MULTIPLIER_SHIFT;
+	val |= PCIE_PHY_MPLL_MULTIPLIER_OVRD;
+	pcie_phy_write(imx6_pcie, PCIE_PHY_MPLL_OVRD_IN_LO, val);
+
+	pcie_phy_read(imx6_pcie, PCIE_PHY_ATEOVRD, &val);
+	val &= ~(PCIE_PHY_ATEOVRD_REF_CLKDIV_MASK <<
+		 PCIE_PHY_ATEOVRD_REF_CLKDIV_SHIFT);
+	val |= div << PCIE_PHY_ATEOVRD_REF_CLKDIV_SHIFT;
+	val |= PCIE_PHY_ATEOVRD_EN;
+	pcie_phy_write(imx6_pcie, PCIE_PHY_ATEOVRD, val);
+
+	return 0;
+}
+
 static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie)
 {
 	struct dw_pcie *pci = imx6_pcie->pci;
@@ -632,6 +686,7 @@  static int imx6_pcie_host_init(struct pcie_port *pp)
 	imx6_pcie_assert_core_reset(imx6_pcie);
 	imx6_pcie_init_phy(imx6_pcie);
 	imx6_pcie_deassert_core_reset(imx6_pcie);
+	imx6_setup_phy_mpll(imx6_pcie);
 	dw_pcie_setup_rc(pp);
 	imx6_pcie_establish_link(imx6_pcie);