diff mbox series

[2/2] PCI: imx6: limit DBI register length

Message ID 20181119094144.4127-2-stefan@agner.ch
State Superseded
Delegated to: Lorenzo Pieralisi
Headers show
Series [1/2] PCI: dwc: allow to limit registers set length | expand

Commit Message

Stefan Agner Nov. 19, 2018, 9:41 a.m. UTC
Define the length of the DBI registers. This makes sure that
the kernel does not access registers beyond that point, avoiding
the following abort on a i.MX 6Quad:
  # cat /sys/devices/soc0/soc/1ffc000.pcie/pci0000\:00/0000\:00\:00.0/config
  [  100.021433] Unhandled fault: imprecise external abort (0x1406) at 0xb6ea7000
  ...
  [  100.056423] PC is at dw_pcie_read+0x50/0x84
  [  100.060790] LR is at dw_pcie_rd_own_conf+0x44/0x48
  ...

Signed-off-by: Stefan Agner <stefan@agner.ch>
---
 drivers/pci/controller/dwc/pci-imx6.c | 51 +++++++++++++++++++--------
 1 file changed, 37 insertions(+), 14 deletions(-)

Comments

Trent Piepho Nov. 20, 2018, 1:06 a.m. UTC | #1
On Mon, 2018-11-19 at 10:41 +0100, Stefan Agner wrote:
> Define the length of the DBI registers. This makes sure that
> the kernel does not access registers beyond that point, avoiding
> the following abort on a i.MX 6Quad:
>   # cat
> /sys/devices/soc0/soc/1ffc000.pcie/pci0000\:00/0000\:00\:00.0/config
>   [  100.021433] Unhandled fault: imprecise external abort (0x1406)
> at 0xb6ea7000
>   ...
>   [  100.056423] PC is at dw_pcie_read+0x50/0x84
>   [  100.060790] LR is at dw_pcie_rd_own_conf+0x44/0x48
>   ...
> 
> Signed-off-by: Stefan Agner <stefan@agner.ch>

After updating this for v4.20-rc2, tested on IMX 7d, config space
access works as before.

Tested-by: Trent Piepho <tpiepho@impinj.com>
Trent Piepho Nov. 20, 2018, 1:33 a.m. UTC | #2
On Mon, 2018-11-19 at 10:41 +0100, Stefan Agner wrote:
> 
>  
> +static const struct imx6_pcie_drvdata imx6q_pcie_drvdata = {
> +	.variant = IMX6Q,
> +	.dbi_length = 0x15c,
> +};
> +
> +static const struct imx6_pcie_drvdata imx6sx_pcie_drvdata = {
> +	.variant = IMX6SX,
> +};
> +
> +static const struct imx6_pcie_drvdata imx6qp_pcie_drvdata = {
> +	.variant = IMX6QP,
> +};
> +
> +static const struct imx6_pcie_drvdata imx7d_pcie_drvdata = {
> +	.variant = IMX7D,
> +};
> +
>  static const struct of_device_id imx6_pcie_of_match[] = {
> -	{ .compatible = "fsl,imx6q-pcie",  .data = (void *)IMX6Q,  },
> -	{ .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, },
> -	{ .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, },
> -	{ .compatible = "fsl,imx7d-pcie",  .data = (void *)IMX7D,  },
> +	{ .compatible = "fsl,imx6q-pcie",  .data = &imx6q_pcie_drvdata,  },
> +	{ .compatible = "fsl,imx6sx-pcie", .data = &imx6sx_pcie_drvdata, },
> +	{ .compatible = "fsl,imx6qp-pcie", .data = &imx6qp_pcie_drvdata, },
> +	{ .compatible = "fsl,imx7d-pcie",  .data = &imx7d_pcie_drvdata,  },
>  	{},
>  };

Instead of making a single drvdata struct for each type, this could use
an array:

static const struct imx6_pcie_drvdata drvdata[] = {
	[IMX6Q]  = { .variant = IMX6Q, .dbi_length = 0x15c },
	[IMX6SX] = { .variant = IMX6SX },
	[...]
};

static const struct of_device_id imx6_pcie_of_match[] = {
	{ .compatible = "fsl,imx6q-pcie",  .data = &drvdata[IMX6Q], },
	{ .compatible = "fsl,imx6sx-pcie", .data = &drvdata[IMX6SX], },
	...
};
Leonard Crestez Nov. 20, 2018, 10:22 a.m. UTC | #3
On Mon, 2018-11-19 at 10:41 +0100, Stefan Agner wrote:
> Define the length of the DBI registers. This makes sure that
> the kernel does not access registers beyond that point, avoiding
> the following abort on a i.MX 6Quad:
>   # cat /sys/devices/soc0/soc/1ffc000.pcie/pci0000\:00/0000\:00\:00.0/config
>   [  100.021433] Unhandled fault: imprecise external abort (0x1406) at 0xb6ea7000
>   ...
>   [  100.056423] PC is at dw_pcie_read+0x50/0x84
>   [  100.060790] LR is at dw_pcie_rd_own_conf+0x44/0x48
>   ...
> 
> Signed-off-by: Stefan Agner <stefan@agner.ch>
> 
> diff --git a/drivers/pci/controller/dwc/pci-imx6.c

> +struct imx6_pcie_drvdata {
> +	enum imx6_pcie_variants variant;
> +	int			dbi_length;
> +};

Turning imx6_pcie drvdata into a struct is very nice, maybe in the
future some of the long case statements in this driver could be split
into per-soc functions called via drvdata.
Stefan Agner Nov. 20, 2018, 10:30 a.m. UTC | #4
On 20.11.2018 11:22, Leonard Crestez wrote:
> On Mon, 2018-11-19 at 10:41 +0100, Stefan Agner wrote:
>> Define the length of the DBI registers. This makes sure that
>> the kernel does not access registers beyond that point, avoiding
>> the following abort on a i.MX 6Quad:
>>   # cat /sys/devices/soc0/soc/1ffc000.pcie/pci0000\:00/0000\:00\:00.0/config
>>   [  100.021433] Unhandled fault: imprecise external abort (0x1406) at 0xb6ea7000
>>   ...
>>   [  100.056423] PC is at dw_pcie_read+0x50/0x84
>>   [  100.060790] LR is at dw_pcie_rd_own_conf+0x44/0x48
>>   ...
>>
>> Signed-off-by: Stefan Agner <stefan@agner.ch>
>>
>> diff --git a/drivers/pci/controller/dwc/pci-imx6.c
> 
>> +struct imx6_pcie_drvdata {
>> +	enum imx6_pcie_variants variant;
>> +	int			dbi_length;
>> +};
> 
> Turning imx6_pcie drvdata into a struct is very nice, maybe in the
> future some of the long case statements in this driver could be split
> into per-soc functions called via drvdata.

Yeah I thought that too. At a quick glance I did not saw an obvious
contender. Should certainly help for similar cases in the future.

--
Stefan
Leonard Crestez Nov. 20, 2018, 1:03 p.m. UTC | #5
From: Stefan Agner <stefan@agner.ch>
> On 20.11.2018 11:22, Leonard Crestez wrote:
> > On Mon, 2018-11-19 at 10:41 +0100, Stefan Agner wrote:
> >> Define the length of the DBI registers. This makes sure that
> >> the kernel does not access registers beyond that point, avoiding
> >> the following abort on a i.MX 6Quad:
> >>   # cat
> /sys/devices/soc0/soc/1ffc000.pcie/pci0000\:00/0000\:00\:00.0/config
> >>   [  100.021433] Unhandled fault: imprecise external abort (0x1406) at
> 0xb6ea7000
> >>   ...
> >>   [  100.056423] PC is at dw_pcie_read+0x50/0x84
> >>   [  100.060790] LR is at dw_pcie_rd_own_conf+0x44/0x48
> >>   ...
> >>
> >> Signed-off-by: Stefan Agner <stefan@agner.ch>
> >>
> >> diff --git a/drivers/pci/controller/dwc/pci-imx6.c
> >
> >> +struct imx6_pcie_drvdata {
> >> +	enum imx6_pcie_variants variant;
> >> +	int			dbi_length;
> >> +};
> >
> > Turning imx6_pcie drvdata into a struct is very nice, maybe in the
> > future some of the long case statements in this driver could be split
> > into per-soc functions called via drvdata.
> 
> Yeah I thought that too. At a quick glance I did not saw an obvious
> contender. Should certainly help for similar cases in the future.

Yes, there are other cases in which it would be useful.

But I think it makes more sense to split introducing drvdata to a separate patch.
It's nice to separate functional and code cleanup changes.

For example maybe dbi_length causes issues and has to be reverted?

--
Regards,
Leonard
Stefan Agner Nov. 20, 2018, 1:12 p.m. UTC | #6
On 20.11.2018 14:03, Leonard Crestez wrote:
> From: Stefan Agner <stefan@agner.ch>
>> On 20.11.2018 11:22, Leonard Crestez wrote:
>> > On Mon, 2018-11-19 at 10:41 +0100, Stefan Agner wrote:
>> >> Define the length of the DBI registers. This makes sure that
>> >> the kernel does not access registers beyond that point, avoiding
>> >> the following abort on a i.MX 6Quad:
>> >>   # cat
>> /sys/devices/soc0/soc/1ffc000.pcie/pci0000\:00/0000\:00\:00.0/config
>> >>   [  100.021433] Unhandled fault: imprecise external abort (0x1406) at
>> 0xb6ea7000
>> >>   ...
>> >>   [  100.056423] PC is at dw_pcie_read+0x50/0x84
>> >>   [  100.060790] LR is at dw_pcie_rd_own_conf+0x44/0x48
>> >>   ...
>> >>
>> >> Signed-off-by: Stefan Agner <stefan@agner.ch>
>> >>
>> >> diff --git a/drivers/pci/controller/dwc/pci-imx6.c
>> >
>> >> +struct imx6_pcie_drvdata {
>> >> +	enum imx6_pcie_variants variant;
>> >> +	int			dbi_length;
>> >> +};
>> >
>> > Turning imx6_pcie drvdata into a struct is very nice, maybe in the
>> > future some of the long case statements in this driver could be split
>> > into per-soc functions called via drvdata.
>>
>> Yeah I thought that too. At a quick glance I did not saw an obvious
>> contender. Should certainly help for similar cases in the future.
> 
> Yes, there are other cases in which it would be useful.
> 
> But I think it makes more sense to split introducing drvdata to a
> separate patch.
> It's nice to separate functional and code cleanup changes.
> 
> For example maybe dbi_length causes issues and has to be reverted?

Ok, makes sense, will split drvdata introduction in its own patch.

--
Stefan
diff mbox series

Patch

diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 4a9a673b4777..8c96af414dac 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -39,6 +39,11 @@  enum imx6_pcie_variants {
 	IMX7D,
 };
 
+struct imx6_pcie_drvdata {
+	enum imx6_pcie_variants variant;
+	int			dbi_length;
+};
+
 struct imx6_pcie {
 	struct dw_pcie		*pci;
 	int			reset_gpio;
@@ -50,7 +55,6 @@  struct imx6_pcie {
 	struct regmap		*iomuxc_gpr;
 	struct reset_control	*pciephy_reset;
 	struct reset_control	*apps_reset;
-	enum imx6_pcie_variants variant;
 	u32			tx_deemph_gen1;
 	u32			tx_deemph_gen2_3p5db;
 	u32			tx_deemph_gen2_6db;
@@ -58,6 +62,7 @@  struct imx6_pcie {
 	u32			tx_swing_low;
 	int			link_gen;
 	struct regulator	*vpcie;
+	const struct imx6_pcie_drvdata *drvdata;
 };
 
 /* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */
@@ -285,7 +290,7 @@  static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
 {
 	struct device *dev = imx6_pcie->pci->dev;
 
-	switch (imx6_pcie->variant) {
+	switch (imx6_pcie->drvdata->variant) {
 	case IMX7D:
 		reset_control_assert(imx6_pcie->pciephy_reset);
 		reset_control_assert(imx6_pcie->apps_reset);
@@ -327,7 +332,7 @@  static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
 	struct device *dev = pci->dev;
 	int ret = 0;
 
-	switch (imx6_pcie->variant) {
+	switch (imx6_pcie->drvdata->variant) {
 	case IMX6SX:
 		ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi);
 		if (ret) {
@@ -430,7 +435,7 @@  static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
 					!imx6_pcie->gpio_active_high);
 	}
 
-	switch (imx6_pcie->variant) {
+	switch (imx6_pcie->drvdata->variant) {
 	case IMX7D:
 		reset_control_deassert(imx6_pcie->pciephy_reset);
 		imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie);
@@ -468,7 +473,7 @@  static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
 
 static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
 {
-	switch (imx6_pcie->variant) {
+	switch (imx6_pcie->drvdata->variant) {
 	case IMX7D:
 		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
 				   IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0);
@@ -560,7 +565,7 @@  static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
 	dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp);
 
 	/* Start LTSSM. */
-	if (imx6_pcie->variant == IMX7D)
+	if (imx6_pcie->drvdata->variant == IMX7D)
 		reset_control_deassert(imx6_pcie->apps_reset);
 	else
 		regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
@@ -585,7 +590,7 @@  static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
 		tmp |= PORT_LOGIC_SPEED_CHANGE;
 		dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
 
-		if (imx6_pcie->variant != IMX7D) {
+		if (imx6_pcie->drvdata->variant != IMX7D) {
 			/*
 			 * On i.MX7, DIRECT_SPEED_CHANGE behaves differently
 			 * from i.MX6 family when no link speed transition
@@ -703,8 +708,7 @@  static int imx6_pcie_probe(struct platform_device *pdev)
 	pci->ops = &dw_pcie_ops;
 
 	imx6_pcie->pci = pci;
-	imx6_pcie->variant =
-		(enum imx6_pcie_variants)of_device_get_match_data(dev);
+	imx6_pcie->drvdata = of_device_get_match_data(dev);
 
 	dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	pci->dbi_base = devm_ioremap_resource(dev, dbi_base);
@@ -748,7 +752,7 @@  static int imx6_pcie_probe(struct platform_device *pdev)
 		return PTR_ERR(imx6_pcie->pcie);
 	}
 
-	switch (imx6_pcie->variant) {
+	switch (imx6_pcie->drvdata->variant) {
 	case IMX6SX:
 		imx6_pcie->pcie_inbound_axi = devm_clk_get(dev,
 							   "pcie_inbound_axi");
@@ -776,6 +780,8 @@  static int imx6_pcie_probe(struct platform_device *pdev)
 		break;
 	}
 
+	pci->dbi_length = imx6_pcie->drvdata->dbi_length;
+
 	/* Grab GPR config register range */
 	imx6_pcie->iomuxc_gpr =
 		 syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
@@ -835,11 +841,28 @@  static void imx6_pcie_shutdown(struct platform_device *pdev)
 	imx6_pcie_assert_core_reset(imx6_pcie);
 }
 
+static const struct imx6_pcie_drvdata imx6q_pcie_drvdata = {
+	.variant = IMX6Q,
+	.dbi_length = 0x15c,
+};
+
+static const struct imx6_pcie_drvdata imx6sx_pcie_drvdata = {
+	.variant = IMX6SX,
+};
+
+static const struct imx6_pcie_drvdata imx6qp_pcie_drvdata = {
+	.variant = IMX6QP,
+};
+
+static const struct imx6_pcie_drvdata imx7d_pcie_drvdata = {
+	.variant = IMX7D,
+};
+
 static const struct of_device_id imx6_pcie_of_match[] = {
-	{ .compatible = "fsl,imx6q-pcie",  .data = (void *)IMX6Q,  },
-	{ .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, },
-	{ .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, },
-	{ .compatible = "fsl,imx7d-pcie",  .data = (void *)IMX7D,  },
+	{ .compatible = "fsl,imx6q-pcie",  .data = &imx6q_pcie_drvdata,  },
+	{ .compatible = "fsl,imx6sx-pcie", .data = &imx6sx_pcie_drvdata, },
+	{ .compatible = "fsl,imx6qp-pcie", .data = &imx6qp_pcie_drvdata, },
+	{ .compatible = "fsl,imx7d-pcie",  .data = &imx7d_pcie_drvdata,  },
 	{},
 };