On Mon, 30 Jan 2023 at 23:41, Jan Hoffmann <j...@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 <j...@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 _______________________________________________ openwrt-devel mailing list openwrt-devel@lists.openwrt.org https://lists.openwrt.org/mailman/listinfo/openwrt-devel