From c1342f2c521415f41aa343b383b2bea1dffe9a7d Mon Sep 17 00:00:00 2001 From: Jan Hoffmann Date: Mon, 30 Jan 2023 23:40:20 +0100 Subject: [PATCH] ipq40xx: add PCIe magic hack to improve VRX518 compatibility 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 --- .../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 a118bdf26b..6558f86a53 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 @@ -310,6 +310,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 0000000000..f427bccd2f --- /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 + #include + #include ++#include + + #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.30.2