diff mbox series

ipq40xx: add PCIe magic hack to improve VRX518 compatibility

Message ID 20230130224020.473703-1-jan@3e8.eu
State New
Headers show
Series ipq40xx: add PCIe magic hack to improve VRX518 compatibility | expand

Commit Message

Jan Hoffmann Jan. 30, 2023, 10:40 p.m. UTC
Some VRX518 modems fail to initialize properly with the error message
"dc_ep_clk_on failed". As a result, the DSL data path doesn't work.

This hack, which is based on code from the FRITZ!Box 7530 GPL archive,
fixes the issue. It changes the PCIe vendor/device ID to values matching
a Lantiq SoC. It also appears to emulate a Lantiq CPU ID register for
connected PCIe devices, by remapping the matching address area to a
specially crafted buffer using the address translation unit.

The hack is only active if the "avm,host_magic" property is specified in
the device tree, so this shouldn't affect any devices other than
FRITZ!Box 7530/7520.

Signed-off-by: Jan Hoffmann <jan@3e8.eu>
---
 .../boot/dts/qcom-ipq4019-fritzbox-7530.dts   |   2 +
 .../997-pcie-qcom-host-magic.patch            | 215 ++++++++++++++++++
 2 files changed, 217 insertions(+)
 create mode 100644 target/linux/ipq40xx/patches-5.15/997-pcie-qcom-host-magic.patch

Comments

Robert Marko Jan. 30, 2023, 11:08 p.m. UTC | #1
On Mon, 30 Jan 2023 at 23:41, Jan Hoffmann <jan@3e8.eu> wrote:
>
> Some VRX518 modems fail to initialize properly with the error message
> "dc_ep_clk_on failed". As a result, the DSL data path doesn't work.
>
> This hack, which is based on code from the FRITZ!Box 7530 GPL archive,
> fixes the issue. It changes the PCIe vendor/device ID to values matching
> a Lantiq SoC. It also appears to emulate a Lantiq CPU ID register for
> connected PCIe devices, by remapping the matching address area to a
> specially crafted buffer using the address translation unit.
>
> The hack is only active if the "avm,host_magic" property is specified in
> the device tree, so this shouldn't affect any devices other than
> FRITZ!Box 7530/7520.

Shouldn't it be possible for the modem driver itself to be fixed
instead of faking
the PCI details?

Especially considering that now modem support is not self-contained
and will require
patching the DWC Qualcomm PCI driver forever.

Regards,
Robert
>
> Signed-off-by: Jan Hoffmann <jan@3e8.eu>
> ---
>  .../boot/dts/qcom-ipq4019-fritzbox-7530.dts   |   2 +
>  .../997-pcie-qcom-host-magic.patch            | 215 ++++++++++++++++++
>  2 files changed, 217 insertions(+)
>  create mode 100644 target/linux/ipq40xx/patches-5.15/997-pcie-qcom-host-magic.patch
>
> diff --git a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-fritzbox-7530.dts b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-fritzbox-7530.dts
> index 336da52f2724..bc167616d3dc 100644
> --- a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-fritzbox-7530.dts
> +++ b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-fritzbox-7530.dts
> @@ -306,6 +306,8 @@
>         perst-gpio = <&tlmm 38 GPIO_ACTIVE_LOW>;
>         wake-gpio = <&tlmm 50 GPIO_ACTIVE_LOW>;
>
> +       avm,host_magic;
> +
>         bridge@0,0 {
>                 reg = <0x00000000 0 0 0 0>;
>                 #address-cells = <3>;
> diff --git a/target/linux/ipq40xx/patches-5.15/997-pcie-qcom-host-magic.patch b/target/linux/ipq40xx/patches-5.15/997-pcie-qcom-host-magic.patch
> new file mode 100644
> index 000000000000..f427bccd2f4a
> --- /dev/null
> +++ b/target/linux/ipq40xx/patches-5.15/997-pcie-qcom-host-magic.patch
> @@ -0,0 +1,215 @@
> +This hack is based on code from the FRITZ!Box 7530 GPL archive for
> +firmware version 07.50.
> +
> +If the device tree contains the "avm,host_magic" property, it changes
> +the PCIe vendor/device ID to the values from Lantiq GRX500 SoCs. It also
> +programs the ATU to present a buffer containing a magic value to PCIe
> +devices. This appears to emulate a Lantiq CPU ID register.
> +
> +Without this hack, some VRX518 modems fail to initialize properly (error
> +"dc_ep_clk_on failed"), and the DSL data path doesn't work.
> +--- a/drivers/pci/controller/dwc/pcie-qcom.c
> ++++ b/drivers/pci/controller/dwc/pcie-qcom.c
> +@@ -27,6 +27,7 @@
> + #include <linux/reset.h>
> + #include <linux/slab.h>
> + #include <linux/types.h>
> ++#include <linux/version.h>
> +
> + #include "../../pci.h"
> + #include "pcie-designware.h"
> +@@ -102,6 +103,8 @@
> +
> + #define QCOM_PCIE_CRC8_POLYNOMIAL (BIT(2) | BIT(1) | BIT(0))
> +
> ++#define PCIE_MAGIC_SIZE               0x10000
> ++
> + struct qcom_pcie_resources_2_1_0 {
> +       struct clk_bulk_data clks[QCOM_PCIE_2_1_0_MAX_CLOCKS];
> +       struct reset_control *pci_reset;
> +@@ -197,6 +200,8 @@ struct qcom_pcie {
> +       struct phy *phy;
> +       struct gpio_desc *reset;
> +       const struct qcom_pcie_ops *ops;
> ++      void *magic_cpu_addr;
> ++      dma_addr_t magic_dma_handle;
> + };
> +
> + #define to_qcom_pcie(x)               dev_get_drvdata((x)->dev)
> +@@ -1388,8 +1393,141 @@ err_deinit:
> +       return ret;
> + }
> +
> ++static int qcom_pcie_magic_prog_atu(struct qcom_pcie *pcie,
> ++                                   u32 addr, u32 limit, u32 phys)
> ++{
> ++      struct dw_pcie *pci = pcie->pci;
> ++      struct device *dev = pci->dev;
> ++      u32 retries, val;
> ++      int index;
> ++
> ++      if (!pci->num_ib_windows) {
> ++              dev_err(dev, "No inbound ATU window available for magic\n");
> ++              return -1;
> ++      }
> ++
> ++      /*
> ++       * Use highest window index and reduce window count so the driver
> ++       * won't overwrite the entry later.
> ++       */
> ++      index = --pci->num_ib_windows;
> ++
> ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6,0,0)
> ++      if (pci->iatu_unroll_enabled) {
> ++              dev_err(dev, "Programming ATU for magic not implemented for this hardware\n");
> ++              return -1;
> ++      }
> ++
> ++      dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT,
> ++                         PCIE_ATU_REGION_INBOUND | index);
> ++
> ++      dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE, addr);
> ++      dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE, 0);
> ++      dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT, limit);
> ++      dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, phys);
> ++      dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, 0);
> ++
> ++      dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, PCIE_ATU_TYPE_MEM);
> ++      dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
> ++
> ++      for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
> ++              val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2);
> ++              if (val & PCIE_ATU_ENABLE)
> ++                      return 0;
> ++
> ++              mdelay(LINK_WAIT_IATU);
> ++      }
> ++#else
> ++      dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_BASE, addr);
> ++      dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_BASE, 0);
> ++      dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LIMIT, limit);
> ++      dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET, phys);
> ++      dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET, 0);
> ++
> ++      dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1,
> ++                            PCIE_ATU_TYPE_MEM);
> ++      dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2,
> ++                            PCIE_ATU_ENABLE);
> ++
> ++      for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
> ++              val = dw_pcie_readl_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2);
> ++              if (val & PCIE_ATU_ENABLE)
> ++                      return 0;
> ++
> ++              mdelay(LINK_WAIT_IATU);
> ++      }
> ++#endif
> ++
> ++      dev_err(dev, "Failed to program ATU for magic\n");
> ++      return -1;
> ++}
> ++
> ++static void qcom_pcie_magic_deinit(struct qcom_pcie *pcie)
> ++{
> ++      struct dw_pcie *pci = pcie->pci;
> ++      struct device *dev = pci->dev;
> ++
> ++      if (pcie->magic_cpu_addr) {
> ++              dma_free_coherent(dev, PCIE_MAGIC_SIZE,
> ++                                pcie->magic_cpu_addr,
> ++                                pcie->magic_dma_handle);
> ++
> ++              pcie->magic_cpu_addr = NULL;
> ++      }
> ++}
> ++
> ++static void qcom_pcie_magic_init(struct qcom_pcie *pcie)
> ++{
> ++      struct dw_pcie *pci = pcie->pci;
> ++      struct device *dev = pci->dev;
> ++      u32 *virt;
> ++      u32 phys;
> ++      int ret;
> ++
> ++      if (!of_property_read_bool(dev->of_node, "avm,host_magic"))
> ++              return;
> ++
> ++      dev_info(dev, "Applying PCIe host magic\n");
> ++
> ++      virt = dma_alloc_coherent(dev, PCIE_MAGIC_SIZE, &phys, GFP_ATOMIC);
> ++      BUG_ON(virt == NULL);
> ++
> ++      pcie->magic_cpu_addr = virt;
> ++      pcie->magic_dma_handle = phys;
> ++
> ++      /*
> ++       * This value is the manufacturer ID of Lantiq. The address where
> ++       * it will be visible for the PCIe device matches the location of
> ++       * CPU ID registers on Lantiq SocS (MPS base address is 0x1f107000).
> ++       */
> ++      virt[0x7340/4] = 0x389 << 5;
> ++
> ++      /* Make it visible to PCIe devices using address translation unit */
> ++      ret = qcom_pcie_magic_prog_atu(pcie, 0x1f100000, 0x1f10ffff, phys);
> ++
> ++      dw_pcie_dbi_ro_wr_en(pci);
> ++
> ++      /* Set vendor/device ID of GRX500 PCIe host */
> ++      dw_pcie_writew_dbi(pci, PCI_VENDOR_ID, 0x1bef);
> ++      dw_pcie_writew_dbi(pci, PCI_DEVICE_ID, 0x0030);
> ++
> ++      dw_pcie_dbi_ro_wr_dis(pci);
> ++
> ++      if (ret)
> ++              qcom_pcie_magic_deinit(pcie);
> ++}
> ++
> ++static void qcom_pcie_atu_hack(struct pcie_port *pp)
> ++{
> ++      struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
> ++      struct qcom_pcie *pcie = to_qcom_pcie(pci);
> ++
> ++      qcom_pcie_magic_init(pcie);
> ++}
> ++
> + static const struct dw_pcie_host_ops qcom_pcie_dw_ops = {
> +       .host_init = qcom_pcie_host_init,
> ++      .atu_hack = qcom_pcie_atu_hack,
> + };
> +
> + /* Qcom IP rev.: 2.1.0        Synopsys IP rev.: 4.01a */
> +@@ -1536,6 +1674,7 @@ static int qcom_pcie_probe(struct platfo
> +
> + err_phy_exit:
> +       phy_exit(pcie->phy);
> ++      qcom_pcie_magic_deinit(pcie);
> + err_pm_runtime_put:
> +       pm_runtime_put(dev);
> +       pm_runtime_disable(dev);
> +--- a/drivers/pci/controller/dwc/pcie-designware-host.c
> ++++ b/drivers/pci/controller/dwc/pcie-designware-host.c
> +@@ -400,6 +400,14 @@ int dw_pcie_host_init(struct pcie_port *
> +       }
> +       dw_pcie_iatu_detect(pci);
> +
> ++      /*
> ++       * This needs to be called after ATU detection, but before the driver
> ++       * sets up any ATU entries, to avoid any ATU entry programmed in the
> ++       * hack being overwritten by the driver later.
> ++       */
> ++      if (pp->ops->atu_hack)
> ++              pp->ops->atu_hack(pp);
> ++
> +       dw_pcie_setup_rc(pp);
> +
> +       if (!dw_pcie_link_up(pci) && pci->ops && pci->ops->start_link) {
> +--- a/drivers/pci/controller/dwc/pcie-designware.h
> ++++ b/drivers/pci/controller/dwc/pcie-designware.h
> +@@ -174,6 +174,7 @@ enum dw_pcie_device_mode {
> +
> + struct dw_pcie_host_ops {
> +       int (*host_init)(struct pcie_port *pp);
> ++      void (*atu_hack)(struct pcie_port *pp);
> +       int (*msi_host_init)(struct pcie_port *pp);
> + };
> +
> --
> 2.39.1
>
>
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/mailman/listinfo/openwrt-devel
Jan Hoffmann Jan. 31, 2023, 10:52 p.m. UTC | #2
Hi Robert,


On 2023-01-30 at 00:08, Robert Marko wrote:
> Shouldn't it be possible for the modem driver itself to be fixed
> instead of faking
> the PCI details?

This hack is definitely far from ideal, but I'm not sure if there is a 
better way to fix this.

Here are a few more details about the issue:

On the affected devices (so far, three users reported it on the VRX518 
thread in the forum [0]), the function call to turn on the "EMA" 
hardware unit in the vrx518_tc driver [1] fails with the error mentioned 
in the commit message.

I don't have any details about it, but this EMA unit is part of the data 
path (it is also referenced in the ltq-atm and ltq-ptm data path drivers 
for older Lantiq modems). If the EMA unit is not running, then at least 
the transmit data path doesn't work, and any packets the driver writes 
to the TX ring are not actually sent out by the device.

This is also reproducible on non-affected devices by calling tc_clkoff 
instead of tc_clkon in the vrx518_tc driver (i.e. disabling the hardware 
unit). The same issue also occurs on affected devices running vendor 
firmware, if the "magic" in the PCIe driver is disabled in the device 
tree. So this is not just a bug in the data path driver.


Regards,
Jan


> 
> Especially considering that now modem support is not self-contained
> and will require
> patching the DWC Qualcomm PCI driver forever.
> 
> Regards,
> Robert


[0] 
https://forum.openwrt.org/t/adding-support-for-vrx518-and-maybe-vrx320/55160
[1] 
https://gitlab.com/prpl-foundation/intel/vrx518_tc_drv/-/blob/ugw-8.5.2/dcdp/tc_main.c#L112
Robert Marko Feb. 2, 2023, 10:54 a.m. UTC | #3
On Tue, 31 Jan 2023 at 23:52, Jan Hoffmann <jan@3e8.eu> wrote:
>
> Hi Robert,
>
>
> On 2023-01-30 at 00:08, Robert Marko wrote:
> > Shouldn't it be possible for the modem driver itself to be fixed
> > instead of faking
> > the PCI details?
>
> This hack is definitely far from ideal, but I'm not sure if there is a
> better way to fix this.
>
> Here are a few more details about the issue:
>
> On the affected devices (so far, three users reported it on the VRX518
> thread in the forum [0]), the function call to turn on the "EMA"
> hardware unit in the vrx518_tc driver [1] fails with the error mentioned
> in the commit message.
>
> I don't have any details about it, but this EMA unit is part of the data
> path (it is also referenced in the ltq-atm and ltq-ptm data path drivers
> for older Lantiq modems). If the EMA unit is not running, then at least
> the transmit data path doesn't work, and any packets the driver writes
> to the TX ring are not actually sent out by the device.
>
> This is also reproducible on non-affected devices by calling tc_clkoff
> instead of tc_clkon in the vrx518_tc driver (i.e. disabling the hardware
> unit). The same issue also occurs on affected devices running vendor
> firmware, if the "magic" in the PCIe driver is disabled in the device
> tree. So this is not just a bug in the data path driver.

I get the issue, however, I am failing to see how faking the PCI ID for the
root adaptor is magically solving that?
If that works, why can't you just patch the driver to stop looking for
the ancient
Lantiq ID?

Regards,
Robert
>
>
> Regards,
> Jan
>
>
> >
> > Especially considering that now modem support is not self-contained
> > and will require
> > patching the DWC Qualcomm PCI driver forever.
> >
> > Regards,
> > Robert
>
>
> [0]
> https://forum.openwrt.org/t/adding-support-for-vrx518-and-maybe-vrx320/55160
> [1]
> https://gitlab.com/prpl-foundation/intel/vrx518_tc_drv/-/blob/ugw-8.5.2/dcdp/tc_main.c#L112
Jan Hoffmann Feb. 5, 2023, 12:10 a.m. UTC | #4
Am 02.02.23 um 11:54 schrieb Robert Marko:
> On Tue, 31 Jan 2023 at 23:52, Jan Hoffmann <jan@3e8.eu> wrote:
>>
>> Hi Robert,
>>
>>
>> On 2023-01-30 at 00:08, Robert Marko wrote:
>>> Shouldn't it be possible for the modem driver itself to be fixed
>>> instead of faking
>>> the PCI details?
>>
>> This hack is definitely far from ideal, but I'm not sure if there is a
>> better way to fix this.
>>
>> Here are a few more details about the issue:
>>
>> On the affected devices (so far, three users reported it on the VRX518
>> thread in the forum [0]), the function call to turn on the "EMA"
>> hardware unit in the vrx518_tc driver [1] fails with the error mentioned
>> in the commit message.
>>
>> I don't have any details about it, but this EMA unit is part of the data
>> path (it is also referenced in the ltq-atm and ltq-ptm data path drivers
>> for older Lantiq modems). If the EMA unit is not running, then at least
>> the transmit data path doesn't work, and any packets the driver writes
>> to the TX ring are not actually sent out by the device.
>>
>> This is also reproducible on non-affected devices by calling tc_clkoff
>> instead of tc_clkon in the vrx518_tc driver (i.e. disabling the hardware
>> unit). The same issue also occurs on affected devices running vendor
>> firmware, if the "magic" in the PCIe driver is disabled in the device
>> tree. So this is not just a bug in the data path driver.
> 
> I get the issue, however, I am failing to see how faking the PCI ID for the
> root adaptor is magically solving that?
> If that works, why can't you just patch the driver to stop looking for
> the ancient
> Lantiq ID?

As far as I can see, the magic values don't appear anywhere in the DSL 
drivers. So it doesn't seem like there is an easy fix like that.

To me this looks like whatever access to these values is being done, 
seems to happen in hardware (or firmware). Maybe there are some 
revisions or variants of the modem that don't like to cooperate with 
non-Lantiq SoCs.

But in the end, I don't know with certainty what exactly is happening 
here, as about the only public information on these modems are the 
open-source drivers (including the magic hack in the PCIe driver which 
in its original form contains comments like "do some magic" without 
really explaining what it actually does).

Regards,
Jan


> 
> Regards,
> Robert
>>
>>
>> Regards,
>> Jan
>>
>>
>>>
>>> Especially considering that now modem support is not self-contained
>>> and will require
>>> patching the DWC Qualcomm PCI driver forever.
>>>
>>> Regards,
>>> Robert
>>
>>
>> [0]
>> https://forum.openwrt.org/t/adding-support-for-vrx518-and-maybe-vrx320/55160
>> [1]
>> https://gitlab.com/prpl-foundation/intel/vrx518_tc_drv/-/blob/ugw-8.5.2/dcdp/tc_main.c#L112
Robert Marko Feb. 8, 2023, 10:44 a.m. UTC | #5
On Sun, 5 Feb 2023 at 01:10, Jan Hoffmann <jan@3e8.eu> wrote:
>
>
> Am 02.02.23 um 11:54 schrieb Robert Marko:
> > On Tue, 31 Jan 2023 at 23:52, Jan Hoffmann <jan@3e8.eu> wrote:
> >>
> >> Hi Robert,
> >>
> >>
> >> On 2023-01-30 at 00:08, Robert Marko wrote:
> >>> Shouldn't it be possible for the modem driver itself to be fixed
> >>> instead of faking
> >>> the PCI details?
> >>
> >> This hack is definitely far from ideal, but I'm not sure if there is a
> >> better way to fix this.
> >>
> >> Here are a few more details about the issue:
> >>
> >> On the affected devices (so far, three users reported it on the VRX518
> >> thread in the forum [0]), the function call to turn on the "EMA"
> >> hardware unit in the vrx518_tc driver [1] fails with the error mentioned
> >> in the commit message.
> >>
> >> I don't have any details about it, but this EMA unit is part of the data
> >> path (it is also referenced in the ltq-atm and ltq-ptm data path drivers
> >> for older Lantiq modems). If the EMA unit is not running, then at least
> >> the transmit data path doesn't work, and any packets the driver writes
> >> to the TX ring are not actually sent out by the device.
> >>
> >> This is also reproducible on non-affected devices by calling tc_clkoff
> >> instead of tc_clkon in the vrx518_tc driver (i.e. disabling the hardware
> >> unit). The same issue also occurs on affected devices running vendor
> >> firmware, if the "magic" in the PCIe driver is disabled in the device
> >> tree. So this is not just a bug in the data path driver.
> >
> > I get the issue, however, I am failing to see how faking the PCI ID for the
> > root adaptor is magically solving that?
> > If that works, why can't you just patch the driver to stop looking for
> > the ancient
> > Lantiq ID?
>
> As far as I can see, the magic values don't appear anywhere in the DSL
> drivers. So it doesn't seem like there is an easy fix like that.
>
> To me this looks like whatever access to these values is being done,
> seems to happen in hardware (or firmware). Maybe there are some
> revisions or variants of the modem that don't like to cooperate with
> non-Lantiq SoCs.
>
> But in the end, I don't know with certainty what exactly is happening
> here, as about the only public information on these modems are the
> open-source drivers (including the magic hack in the PCIe driver which
> in its original form contains comments like "do some magic" without
> really explaining what it actually does).

Ok, I am now getting the issue, it's probably in the damn firmware.
I still really dont like the hack that we are gonna need to carry forever.

I would like for somebody else to chime in as well.

Regards,
Robert
>
> Regards,
> Jan
>
>
> >
> > Regards,
> > Robert
> >>
> >>
> >> Regards,
> >> Jan
> >>
> >>
> >>>
> >>> Especially considering that now modem support is not self-contained
> >>> and will require
> >>> patching the DWC Qualcomm PCI driver forever.
> >>>
> >>> Regards,
> >>> Robert
> >>
> >>
> >> [0]
> >> https://forum.openwrt.org/t/adding-support-for-vrx518-and-maybe-vrx320/55160
> >> [1]
> >> https://gitlab.com/prpl-foundation/intel/vrx518_tc_drv/-/blob/ugw-8.5.2/dcdp/tc_main.c#L112
>
>
Paul D Feb. 14, 2023, 1:22 p.m. UTC | #6
On 2023-02-08 11:44, Robert Marko wrote:
> On Sun, 5 Feb 2023 at 01:10, Jan Hoffmann <jan@3e8.eu> wrote:
>>
>>
>> Am 02.02.23 um 11:54 schrieb Robert Marko:
>>> On Tue, 31 Jan 2023 at 23:52, Jan Hoffmann <jan@3e8.eu> wrote:
>>>>
>>>> Hi Robert,
>>>>
>>>>
>>>> On 2023-01-30 at 00:08, Robert Marko wrote:
>>>>> Shouldn't it be possible for the modem driver itself to be fixed
>>>>> instead of faking
>>>>> the PCI details?
>>>>
>>>> This hack is definitely far from ideal, but I'm not sure if there is a
>>>> better way to fix this.
>>>>
>>>> Here are a few more details about the issue:
>>>>
>>>> On the affected devices (so far, three users reported it on the VRX518
>>>> thread in the forum [0]), the function call to turn on the "EMA"
>>>> hardware unit in the vrx518_tc driver [1] fails with the error mentioned
>>>> in the commit message.
>>>>
>>>> I don't have any details about it, but this EMA unit is part of the data
>>>> path (it is also referenced in the ltq-atm and ltq-ptm data path drivers
>>>> for older Lantiq modems). If the EMA unit is not running, then at least
>>>> the transmit data path doesn't work, and any packets the driver writes
>>>> to the TX ring are not actually sent out by the device.
>>>>
>>>> This is also reproducible on non-affected devices by calling tc_clkoff
>>>> instead of tc_clkon in the vrx518_tc driver (i.e. disabling the hardware
>>>> unit). The same issue also occurs on affected devices running vendor
>>>> firmware, if the "magic" in the PCIe driver is disabled in the device
>>>> tree. So this is not just a bug in the data path driver.
>>>
>>> I get the issue, however, I am failing to see how faking the PCI ID for the
>>> root adaptor is magically solving that?
>>> If that works, why can't you just patch the driver to stop looking for
>>> the ancient
>>> Lantiq ID?
>>
>> As far as I can see, the magic values don't appear anywhere in the DSL
>> drivers. So it doesn't seem like there is an easy fix like that.
>>
>> To me this looks like whatever access to these values is being done,
>> seems to happen in hardware (or firmware). Maybe there are some
>> revisions or variants of the modem that don't like to cooperate with
>> non-Lantiq SoCs.
>>
>> But in the end, I don't know with certainty what exactly is happening
>> here, as about the only public information on these modems are the
>> open-source drivers (including the magic hack in the PCIe driver which
>> in its original form contains comments like "do some magic" without
>> really explaining what it actually does).
> 
> Ok, I am now getting the issue, it's probably in the damn firmware.
> I still really dont like the hack that we are gonna need to carry forever.
> 
> I would like for somebody else to chime in as well.
> 

If the OEM does it, unless we can find another way, we should carry the 
hack, if that's what it takes to better support a chipset. DSL will 
still be with us for a while to come. There was a huge amount of work 
and guesswork to even get these chipsets usable.
Jan Hoffmann April 25, 2023, 1:12 p.m. UTC | #7
On 2023-01-30 at 23:40, Jan Hoffmann wrote:
> Some VRX518 modems fail to initialize properly with the error message
> "dc_ep_clk_on failed". As a result, the DSL data path doesn't work.
> 
> This hack, which is based on code from the FRITZ!Box 7530 GPL archive,
> fixes the issue. It changes the PCIe vendor/device ID to values matching
> a Lantiq SoC. It also appears to emulate a Lantiq CPU ID register for
> connected PCIe devices, by remapping the matching address area to a
> specially crafted buffer using the address translation unit.
> 
> The hack is only active if the "avm,host_magic" property is specified in
> the device tree, so this shouldn't affect any devices other than
> FRITZ!Box 7530/7520.
> 
> Signed-off-by: Jan Hoffmann <jan@3e8.eu>


Is there any chance for this patch to get accepted?

I'm aware that this hack is not a particularly nice solution. And the 
additional maintenance burden of adding another kernel patch is also an 
obvious downside.

However, we don't know any better fix, and the modem just won't work 
without it on some devices. As there is no way to distinguish the 
affected devices externally, users will only find out if the modem on 
their device actually works after they installed OpenWrt on it. 
Recently, another user with this issue turned up on the forum [0].


Thanks,
Jan


[0] 
https://forum.openwrt.org/t/fritzbox-7520-wan-dsl0-7-device-is-missing/156424
Paul D April 26, 2023, 4:50 p.m. UTC | #8
On 2023-04-25 16:12, Jan Hoffmann wrote:
> On 2023-01-30 at 23:40, Jan Hoffmann wrote:
>> Some VRX518 modems fail to initialize properly with the error message
>> "dc_ep_clk_on failed". As a result, the DSL data path doesn't work.
>>
>> This hack, which is based on code from the FRITZ!Box 7530 GPL archive,
>> fixes the issue. It changes the PCIe vendor/device ID to values matching
>> a Lantiq SoC. It also appears to emulate a Lantiq CPU ID register for
>> connected PCIe devices, by remapping the matching address area to a
>> specially crafted buffer using the address translation unit.
>>
>> The hack is only active if the "avm,host_magic" property is specified in
>> the device tree, so this shouldn't affect any devices other than
>> FRITZ!Box 7530/7520.
>>
>> Signed-off-by: Jan Hoffmann <jan@3e8.eu>
> 
> 
> Is there any chance for this patch to get accepted?
> 
> I'm aware that this hack is not a particularly nice solution. And the 
> additional maintenance burden of adding another kernel patch is also an 
> obvious downside.
> 
> However, we don't know any better fix, and the modem just won't work 
> without it on some devices. As there is no way to distinguish the 
> affected devices externally, users will only find out if the modem on 
> their device actually works after they installed OpenWrt on it. 
> Recently, another user with this issue turned up on the forum [0].
> 
> 
> Thanks,
> Jan
> 
> 
> [0] 
> https://forum.openwrt.org/t/fritzbox-7520-wan-dsl0-7-device-is-missing/156424
> 
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Sometimes fixing silicon bugs is analogous to hacks. Seems like making 
the datapath work is... desirable.

/P
Felix Maurer Nov. 21, 2023, 11:37 p.m. UTC | #9
On 30.01.23 23:40, Jan Hoffmann wrote:
> Some VRX518 modems fail to initialize properly with the error message
> "dc_ep_clk_on failed". As a result, the DSL data path doesn't work.
> 
> This hack, which is based on code from the FRITZ!Box 7530 GPL archive,
> fixes the issue. It changes the PCIe vendor/device ID to values matching
> a Lantiq SoC. It also appears to emulate a Lantiq CPU ID register for
> connected PCIe devices, by remapping the matching address area to a
> specially crafted buffer using the address translation unit.
> 
> The hack is only active if the "avm,host_magic" property is specified in
> the device tree, so this shouldn't affect any devices other than
> FRITZ!Box 7530/7520.
> 
> Signed-off-by: Jan Hoffmann <jan@3e8.eu>

Sorry for digging up an old thread/patch. As I own one of these devices 
where the DSL modem can only work with this hack, I applied this patch 
on top of v23.05.0 and built an image with it. I had this running for 
the last three weeks and the DSL is stable with this patch.

I agree that it is very much a hack, but given that the OEM does the 
same thing and we have no other way of getting those versions of the DSL 
modem to work, I think it's worthwhile adding the patch.

Tested-by: Felix Maurer <felix@felix-maurer.de>


> ---
>   .../boot/dts/qcom-ipq4019-fritzbox-7530.dts   |   2 +
>   .../997-pcie-qcom-host-magic.patch            | 215 ++++++++++++++++++
>   2 files changed, 217 insertions(+)
>   create mode 100644 target/linux/ipq40xx/patches-5.15/997-pcie-qcom-host-magic.patch
> 
> diff --git a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-fritzbox-7530.dts b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-fritzbox-7530.dts
> index 336da52f2724..bc167616d3dc 100644
> --- a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-fritzbox-7530.dts
> +++ b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-fritzbox-7530.dts
> @@ -306,6 +306,8 @@
>   	perst-gpio = <&tlmm 38 GPIO_ACTIVE_LOW>;
>   	wake-gpio = <&tlmm 50 GPIO_ACTIVE_LOW>;
>   
> +	avm,host_magic;
> +
>   	bridge@0,0 {
>   		reg = <0x00000000 0 0 0 0>;
>   		#address-cells = <3>;
> diff --git a/target/linux/ipq40xx/patches-5.15/997-pcie-qcom-host-magic.patch b/target/linux/ipq40xx/patches-5.15/997-pcie-qcom-host-magic.patch
> new file mode 100644
> index 000000000000..f427bccd2f4a
> --- /dev/null
> +++ b/target/linux/ipq40xx/patches-5.15/997-pcie-qcom-host-magic.patch
> @@ -0,0 +1,215 @@
> +This hack is based on code from the FRITZ!Box 7530 GPL archive for
> +firmware version 07.50.
> +
> +If the device tree contains the "avm,host_magic" property, it changes
> +the PCIe vendor/device ID to the values from Lantiq GRX500 SoCs. It also
> +programs the ATU to present a buffer containing a magic value to PCIe
> +devices. This appears to emulate a Lantiq CPU ID register.
> +
> +Without this hack, some VRX518 modems fail to initialize properly (error
> +"dc_ep_clk_on failed"), and the DSL data path doesn't work.
> +--- a/drivers/pci/controller/dwc/pcie-qcom.c
> ++++ b/drivers/pci/controller/dwc/pcie-qcom.c
> +@@ -27,6 +27,7 @@
> + #include <linux/reset.h>
> + #include <linux/slab.h>
> + #include <linux/types.h>
> ++#include <linux/version.h>
> +
> + #include "../../pci.h"
> + #include "pcie-designware.h"
> +@@ -102,6 +103,8 @@
> +
> + #define QCOM_PCIE_CRC8_POLYNOMIAL (BIT(2) | BIT(1) | BIT(0))
> +
> ++#define PCIE_MAGIC_SIZE		0x10000
> ++
> + struct qcom_pcie_resources_2_1_0 {
> + 	struct clk_bulk_data clks[QCOM_PCIE_2_1_0_MAX_CLOCKS];
> + 	struct reset_control *pci_reset;
> +@@ -197,6 +200,8 @@ struct qcom_pcie {
> + 	struct phy *phy;
> + 	struct gpio_desc *reset;
> + 	const struct qcom_pcie_ops *ops;
> ++	void *magic_cpu_addr;
> ++	dma_addr_t magic_dma_handle;
> + };
> +
> + #define to_qcom_pcie(x)		dev_get_drvdata((x)->dev)
> +@@ -1388,8 +1393,141 @@ err_deinit:
> + 	return ret;
> + }
> +
> ++static int qcom_pcie_magic_prog_atu(struct qcom_pcie *pcie,
> ++				     u32 addr, u32 limit, u32 phys)
> ++{
> ++	struct dw_pcie *pci = pcie->pci;
> ++	struct device *dev = pci->dev;
> ++	u32 retries, val;
> ++	int index;
> ++
> ++	if (!pci->num_ib_windows) {
> ++		dev_err(dev, "No inbound ATU window available for magic\n");
> ++		return -1;
> ++	}
> ++
> ++	/*
> ++	 * Use highest window index and reduce window count so the driver
> ++	 * won't overwrite the entry later.
> ++	 */
> ++	index = --pci->num_ib_windows;
> ++
> ++#if LINUX_VERSION_CODE < KERNEL_VERSION(6,0,0)
> ++	if (pci->iatu_unroll_enabled) {
> ++		dev_err(dev, "Programming ATU for magic not implemented for this hardware\n");
> ++		return -1;
> ++	}
> ++
> ++	dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT,
> ++			   PCIE_ATU_REGION_INBOUND | index);
> ++
> ++	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE, addr);
> ++	dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE, 0);
> ++	dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT, limit);
> ++	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, phys);
> ++	dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, 0);
> ++
> ++	dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, PCIE_ATU_TYPE_MEM);
> ++	dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
> ++
> ++	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
> ++		val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2);
> ++		if (val & PCIE_ATU_ENABLE)
> ++			return 0;
> ++
> ++		mdelay(LINK_WAIT_IATU);
> ++	}
> ++#else
> ++	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_BASE, addr);
> ++	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_BASE, 0);
> ++	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LIMIT, limit);
> ++	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET, phys);
> ++	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET, 0);
> ++
> ++	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1,
> ++			      PCIE_ATU_TYPE_MEM);
> ++	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2,
> ++			      PCIE_ATU_ENABLE);
> ++
> ++	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
> ++		val = dw_pcie_readl_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2);
> ++		if (val & PCIE_ATU_ENABLE)
> ++			return 0;
> ++
> ++		mdelay(LINK_WAIT_IATU);
> ++	}
> ++#endif
> ++
> ++	dev_err(dev, "Failed to program ATU for magic\n");
> ++	return -1;
> ++}
> ++
> ++static void qcom_pcie_magic_deinit(struct qcom_pcie *pcie)
> ++{
> ++	struct dw_pcie *pci = pcie->pci;
> ++	struct device *dev = pci->dev;
> ++
> ++	if (pcie->magic_cpu_addr) {
> ++		dma_free_coherent(dev, PCIE_MAGIC_SIZE,
> ++				  pcie->magic_cpu_addr,
> ++				  pcie->magic_dma_handle);
> ++
> ++		pcie->magic_cpu_addr = NULL;
> ++	}
> ++}
> ++
> ++static void qcom_pcie_magic_init(struct qcom_pcie *pcie)
> ++{
> ++	struct dw_pcie *pci = pcie->pci;
> ++	struct device *dev = pci->dev;
> ++	u32 *virt;
> ++	u32 phys;
> ++	int ret;
> ++
> ++	if (!of_property_read_bool(dev->of_node, "avm,host_magic"))
> ++		return;
> ++
> ++	dev_info(dev, "Applying PCIe host magic\n");
> ++
> ++	virt = dma_alloc_coherent(dev, PCIE_MAGIC_SIZE, &phys, GFP_ATOMIC);
> ++	BUG_ON(virt == NULL);
> ++
> ++	pcie->magic_cpu_addr = virt;
> ++	pcie->magic_dma_handle = phys;
> ++
> ++	/*
> ++	 * This value is the manufacturer ID of Lantiq. The address where
> ++	 * it will be visible for the PCIe device matches the location of
> ++	 * CPU ID registers on Lantiq SocS (MPS base address is 0x1f107000).
> ++	 */
> ++	virt[0x7340/4] = 0x389 << 5;
> ++
> ++	/* Make it visible to PCIe devices using address translation unit */
> ++	ret = qcom_pcie_magic_prog_atu(pcie, 0x1f100000, 0x1f10ffff, phys);
> ++
> ++	dw_pcie_dbi_ro_wr_en(pci);
> ++
> ++	/* Set vendor/device ID of GRX500 PCIe host */
> ++	dw_pcie_writew_dbi(pci, PCI_VENDOR_ID, 0x1bef);
> ++	dw_pcie_writew_dbi(pci, PCI_DEVICE_ID, 0x0030);
> ++
> ++	dw_pcie_dbi_ro_wr_dis(pci);
> ++
> ++	if (ret)
> ++		qcom_pcie_magic_deinit(pcie);
> ++}
> ++
> ++static void qcom_pcie_atu_hack(struct pcie_port *pp)
> ++{
> ++	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
> ++	struct qcom_pcie *pcie = to_qcom_pcie(pci);
> ++
> ++	qcom_pcie_magic_init(pcie);
> ++}
> ++
> + static const struct dw_pcie_host_ops qcom_pcie_dw_ops = {
> + 	.host_init = qcom_pcie_host_init,
> ++	.atu_hack = qcom_pcie_atu_hack,
> + };
> +
> + /* Qcom IP rev.: 2.1.0	Synopsys IP rev.: 4.01a */
> +@@ -1536,6 +1674,7 @@ static int qcom_pcie_probe(struct platfo
> +
> + err_phy_exit:
> + 	phy_exit(pcie->phy);
> ++	qcom_pcie_magic_deinit(pcie);
> + err_pm_runtime_put:
> + 	pm_runtime_put(dev);
> + 	pm_runtime_disable(dev);
> +--- a/drivers/pci/controller/dwc/pcie-designware-host.c
> ++++ b/drivers/pci/controller/dwc/pcie-designware-host.c
> +@@ -400,6 +400,14 @@ int dw_pcie_host_init(struct pcie_port *
> + 	}
> + 	dw_pcie_iatu_detect(pci);
> +
> ++	/*
> ++	 * This needs to be called after ATU detection, but before the driver
> ++	 * sets up any ATU entries, to avoid any ATU entry programmed in the
> ++	 * hack being overwritten by the driver later.
> ++	 */
> ++	if (pp->ops->atu_hack)
> ++		pp->ops->atu_hack(pp);
> ++
> + 	dw_pcie_setup_rc(pp);
> +
> + 	if (!dw_pcie_link_up(pci) && pci->ops && pci->ops->start_link) {
> +--- a/drivers/pci/controller/dwc/pcie-designware.h
> ++++ b/drivers/pci/controller/dwc/pcie-designware.h
> +@@ -174,6 +174,7 @@ enum dw_pcie_device_mode {
> +
> + struct dw_pcie_host_ops {
> + 	int (*host_init)(struct pcie_port *pp);
> ++	void (*atu_hack)(struct pcie_port *pp);
> + 	int (*msi_host_init)(struct pcie_port *pp);
> + };
> +
diff mbox series

Patch

diff --git a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-fritzbox-7530.dts b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-fritzbox-7530.dts
index 336da52f2724..bc167616d3dc 100644
--- a/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-fritzbox-7530.dts
+++ b/target/linux/ipq40xx/files/arch/arm/boot/dts/qcom-ipq4019-fritzbox-7530.dts
@@ -306,6 +306,8 @@ 
 	perst-gpio = <&tlmm 38 GPIO_ACTIVE_LOW>;
 	wake-gpio = <&tlmm 50 GPIO_ACTIVE_LOW>;
 
+	avm,host_magic;
+
 	bridge@0,0 {
 		reg = <0x00000000 0 0 0 0>;
 		#address-cells = <3>;
diff --git a/target/linux/ipq40xx/patches-5.15/997-pcie-qcom-host-magic.patch b/target/linux/ipq40xx/patches-5.15/997-pcie-qcom-host-magic.patch
new file mode 100644
index 000000000000..f427bccd2f4a
--- /dev/null
+++ b/target/linux/ipq40xx/patches-5.15/997-pcie-qcom-host-magic.patch
@@ -0,0 +1,215 @@ 
+This hack is based on code from the FRITZ!Box 7530 GPL archive for
+firmware version 07.50.
+
+If the device tree contains the "avm,host_magic" property, it changes
+the PCIe vendor/device ID to the values from Lantiq GRX500 SoCs. It also
+programs the ATU to present a buffer containing a magic value to PCIe
+devices. This appears to emulate a Lantiq CPU ID register.
+
+Without this hack, some VRX518 modems fail to initialize properly (error
+"dc_ep_clk_on failed"), and the DSL data path doesn't work.
+--- a/drivers/pci/controller/dwc/pcie-qcom.c
++++ b/drivers/pci/controller/dwc/pcie-qcom.c
+@@ -27,6 +27,7 @@
+ #include <linux/reset.h>
+ #include <linux/slab.h>
+ #include <linux/types.h>
++#include <linux/version.h>
+ 
+ #include "../../pci.h"
+ #include "pcie-designware.h"
+@@ -102,6 +103,8 @@
+ 
+ #define QCOM_PCIE_CRC8_POLYNOMIAL (BIT(2) | BIT(1) | BIT(0))
+ 
++#define PCIE_MAGIC_SIZE		0x10000
++
+ struct qcom_pcie_resources_2_1_0 {
+ 	struct clk_bulk_data clks[QCOM_PCIE_2_1_0_MAX_CLOCKS];
+ 	struct reset_control *pci_reset;
+@@ -197,6 +200,8 @@ struct qcom_pcie {
+ 	struct phy *phy;
+ 	struct gpio_desc *reset;
+ 	const struct qcom_pcie_ops *ops;
++	void *magic_cpu_addr;
++	dma_addr_t magic_dma_handle;
+ };
+ 
+ #define to_qcom_pcie(x)		dev_get_drvdata((x)->dev)
+@@ -1388,8 +1393,141 @@ err_deinit:
+ 	return ret;
+ }
+ 
++static int qcom_pcie_magic_prog_atu(struct qcom_pcie *pcie,
++				     u32 addr, u32 limit, u32 phys)
++{
++	struct dw_pcie *pci = pcie->pci;
++	struct device *dev = pci->dev;
++	u32 retries, val;
++	int index;
++
++	if (!pci->num_ib_windows) {
++		dev_err(dev, "No inbound ATU window available for magic\n");
++		return -1;
++	}
++
++	/*
++	 * Use highest window index and reduce window count so the driver
++	 * won't overwrite the entry later.
++	 */
++	index = --pci->num_ib_windows;
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(6,0,0)
++	if (pci->iatu_unroll_enabled) {
++		dev_err(dev, "Programming ATU for magic not implemented for this hardware\n");
++		return -1;
++	}
++
++	dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT,
++			   PCIE_ATU_REGION_INBOUND | index);
++
++	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE, addr);
++	dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE, 0);
++	dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT, limit);
++	dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, phys);
++	dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, 0);
++
++	dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, PCIE_ATU_TYPE_MEM);
++	dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
++
++	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
++		val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2);
++		if (val & PCIE_ATU_ENABLE)
++			return 0;
++
++		mdelay(LINK_WAIT_IATU);
++	}
++#else
++	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_BASE, addr);
++	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_BASE, 0);
++	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LIMIT, limit);
++	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET, phys);
++	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET, 0);
++
++	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1,
++			      PCIE_ATU_TYPE_MEM);
++	dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2,
++			      PCIE_ATU_ENABLE);
++
++	for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
++		val = dw_pcie_readl_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2);
++		if (val & PCIE_ATU_ENABLE)
++			return 0;
++
++		mdelay(LINK_WAIT_IATU);
++	}
++#endif
++
++	dev_err(dev, "Failed to program ATU for magic\n");
++	return -1;
++}
++
++static void qcom_pcie_magic_deinit(struct qcom_pcie *pcie)
++{
++	struct dw_pcie *pci = pcie->pci;
++	struct device *dev = pci->dev;
++
++	if (pcie->magic_cpu_addr) {
++		dma_free_coherent(dev, PCIE_MAGIC_SIZE,
++				  pcie->magic_cpu_addr,
++				  pcie->magic_dma_handle);
++
++		pcie->magic_cpu_addr = NULL;
++	}
++}
++
++static void qcom_pcie_magic_init(struct qcom_pcie *pcie)
++{
++	struct dw_pcie *pci = pcie->pci;
++	struct device *dev = pci->dev;
++	u32 *virt;
++	u32 phys;
++	int ret;
++
++	if (!of_property_read_bool(dev->of_node, "avm,host_magic"))
++		return;
++
++	dev_info(dev, "Applying PCIe host magic\n");
++
++	virt = dma_alloc_coherent(dev, PCIE_MAGIC_SIZE, &phys, GFP_ATOMIC);
++	BUG_ON(virt == NULL);
++
++	pcie->magic_cpu_addr = virt;
++	pcie->magic_dma_handle = phys;
++
++	/*
++	 * This value is the manufacturer ID of Lantiq. The address where
++	 * it will be visible for the PCIe device matches the location of
++	 * CPU ID registers on Lantiq SocS (MPS base address is 0x1f107000).
++	 */
++	virt[0x7340/4] = 0x389 << 5;
++
++	/* Make it visible to PCIe devices using address translation unit */
++	ret = qcom_pcie_magic_prog_atu(pcie, 0x1f100000, 0x1f10ffff, phys);
++
++	dw_pcie_dbi_ro_wr_en(pci);
++
++	/* Set vendor/device ID of GRX500 PCIe host */
++	dw_pcie_writew_dbi(pci, PCI_VENDOR_ID, 0x1bef);
++	dw_pcie_writew_dbi(pci, PCI_DEVICE_ID, 0x0030);
++
++	dw_pcie_dbi_ro_wr_dis(pci);
++
++	if (ret)
++		qcom_pcie_magic_deinit(pcie);
++}
++
++static void qcom_pcie_atu_hack(struct pcie_port *pp)
++{
++	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
++	struct qcom_pcie *pcie = to_qcom_pcie(pci);
++
++	qcom_pcie_magic_init(pcie);
++}
++
+ static const struct dw_pcie_host_ops qcom_pcie_dw_ops = {
+ 	.host_init = qcom_pcie_host_init,
++	.atu_hack = qcom_pcie_atu_hack,
+ };
+ 
+ /* Qcom IP rev.: 2.1.0	Synopsys IP rev.: 4.01a */
+@@ -1536,6 +1674,7 @@ static int qcom_pcie_probe(struct platfo
+ 
+ err_phy_exit:
+ 	phy_exit(pcie->phy);
++	qcom_pcie_magic_deinit(pcie);
+ err_pm_runtime_put:
+ 	pm_runtime_put(dev);
+ 	pm_runtime_disable(dev);
+--- a/drivers/pci/controller/dwc/pcie-designware-host.c
++++ b/drivers/pci/controller/dwc/pcie-designware-host.c
+@@ -400,6 +400,14 @@ int dw_pcie_host_init(struct pcie_port *
+ 	}
+ 	dw_pcie_iatu_detect(pci);
+ 
++	/*
++	 * This needs to be called after ATU detection, but before the driver
++	 * sets up any ATU entries, to avoid any ATU entry programmed in the
++	 * hack being overwritten by the driver later.
++	 */
++	if (pp->ops->atu_hack)
++		pp->ops->atu_hack(pp);
++
+ 	dw_pcie_setup_rc(pp);
+ 
+ 	if (!dw_pcie_link_up(pci) && pci->ops && pci->ops->start_link) {
+--- a/drivers/pci/controller/dwc/pcie-designware.h
++++ b/drivers/pci/controller/dwc/pcie-designware.h
+@@ -174,6 +174,7 @@ enum dw_pcie_device_mode {
+ 
+ struct dw_pcie_host_ops {
+ 	int (*host_init)(struct pcie_port *pp);
++	void (*atu_hack)(struct pcie_port *pp);
+ 	int (*msi_host_init)(struct pcie_port *pp);
+ };
+