qualcommax: add ipq50xx target
authorGeorge Moussalem <george.moussalem@outlook.com>
Mon, 7 Oct 2024 06:28:09 +0000 (10:28 +0400)
committerRobert Marko <robimarko@gmail.com>
Thu, 6 Feb 2025 08:51:13 +0000 (09:51 +0100)
Introduce support for the Qualcomm IPQ50xx SoC.
This series adds support for the following components:
- minimal boot support: GCC/pinctrl/watchdog/CPUFreq/SDI (upstreamed)
- USB2 (upstreamed)
- Thermal/Tsens
- PCIe gen2 1&2-lane PHY and controller
- PWM and PWM LED
- QPIC SPI NAND controller
- CMN PLL Block (provider of fixed rate clocks to GCC/ethernet/more.)
- Ethernet: IPQ5018 Internal GE PHY (1 gbps)
- Remoteproc MPD driver for IPQ5018 (2.4G) & QCN6122 (5/6G) Wifi

Co-developed-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
Link: https://github.com/openwrt/openwrt/pull/17182
Signed-off-by: Robert Marko <robimarko@gmail.com>
84 files changed:
target/linux/qualcommax/Makefile
target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-ess.dtsi [new file with mode: 0644]
target/linux/qualcommax/image/ipq50xx.mk [new file with mode: 0644]
target/linux/qualcommax/ipq50xx/config-default [new file with mode: 0644]
target/linux/qualcommax/ipq50xx/target.mk [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0065-v6.7-arm64-dts-qcom-ipq5018-add-watchdog.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0066-v6.7-dt-bindings-firmware-qcom-scm-support-indicating-SDI-default-state.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0067-v6.7-firmware-qcom-scm-disable-SDI-if-required.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0068-v6.7-dt-bindings-qcom-scm-document-IPQ5018-compatible.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0069-v6.7-arm64-dts-qcom-IPQ5018-indicate-that-SDI-shoud-be-disabled.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0070-v6.7-dt-bindings-phy-qcom-m31-Add-IPQ5018-compatible.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0071-v6.7-phy-qcom-m31-Add-compatible-phy-init-sequence-for-IPQ5018.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0072-v6.7-dt-bindings-usb-dwc3-Add-IPQ5018-compatible.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0073-v6.8-arm64-dts-qcom-ipq5018-Add-USB-related-nodes.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0074-v6.8-arm64-dts-qcom-ipq5018-add-QUP1-SPI-controller.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0075-v6.8-dt-bindings-clock-qcom-a53pll-add-IPQ5018-compatible.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0076-v6.8-clk-qcom-apss-ipq-pll-add-support-for-IPQ5018.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0077-v6.8-arm64-dts-qcom-ipq5018-enable-the-CPUFreq-support.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0078-v6.8-arm64-dts-qcom-ipq5018-add-few-more-reserved-memory-regions.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0080-v6.10-clk-qcom-apss-ipq-pll-use-stromer-ops-for-IPQ5018-to-fix-boot-failure.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0081-v6.10-clk-qcom-apss-ipq-pll-fix-PLL-rate-for-IPQ5018.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0140-dt-bindings-pwm-add-IPQ6018-binding.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0141-pwm-driver-for-qualcomm-ipq6018-pwm-block.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0142-dt-bindings-mfd-qcom-tcsr-Add-simple-mfd-support-for-IPQ6018.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0150-dt-bindings-nvmem-add-IPQ5018-compatible.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0151-dt-bindings-thermal-qcom-tsens-Add-ipq5018-compatible.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0152-thermal-qcom-add-new-feat-for-soc-without-rpm.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0153-thermal-qcom-tsens-add-support-for-IPQ5018-tsens.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0154-dts-qcom-IPQ5018-add-tsens-node.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0155-dt-bindings-phy-qcom-uniphy-pcie-Document-PCIe-uniphy.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0156-phy-qcom-Introduce-PCIe-UNIPHY-28LP-driver.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0157-dt-bindings-phy-qcom-uniphy-pcie-add-IPQ5018-compatible.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0158-phy-qcom-uniphy-pcie-28lp-add-support-for-IPQ5018.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0160-dt-bindings-PCI-qcom-Add-IPQ5018-SoC.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0161-PCI-qcom-add-support-for-IPQ5018.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0162-arm64-dts-qcom-IPQ5018-add-PCIe-related-nodes.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0301-dt-bindings-mfd-qcom-tcsr-add-IPQ5018-compatible.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0302-arm64-dts-qcom-IPQ5018-add-TCSR-node.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0303-arm64-dts-qcom-IPQ5018-enable-the-download-mode-support.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0304-dt-bindings-pwm-add-IPQ5018-compatible.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0305-pinctrl-qcom-IPQ5018-update-pwm-groups.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0306-arm64-dts-qcom-ipq5018-Add-PWM-node.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0324-arm64-dts-qcom-ipq5018-Add-crypto-nodes.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0337-arm64-dts-qcom-ipq5018-Add-PRNG-node.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0339-arm64-dts-qcom-ipq5018-Add-QUP1-UART2-node.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0340-arm64-dts-qcom-ipq5018-Add-QUP3-I2C-node.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0401-spi-dt-bindings-Introduce-qcom-spi-qpic-snand.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0402-mtd-rawnand-qcom-cleanup-qcom_nandc-driver.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0403-mtd-rawnand-qcom-Add-qcom-prefix-to-common-api.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0404-mtd-nand-Add-qpic_common-API-file.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0405-mtd-rawnand-qcom-use-FIELD_PREP-and-GENMASK.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0406-spi-spi-qpic-add-driver-for-QCOM-SPI-NAND-flash-Interface.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0408-spi-spi-qpic-fix-compilation-issues.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0411-spi-spi-qpic-snand-support-BCH8.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0412-mtd-spinand-qpic-only-support-max-4-bytes-ID.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0421-arm64-dts-qcom-ipq5018-Add-SPI-nand-node.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0431-arm64-dts-qcom-ipq5018-Add-more-nand-compatible-for-.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0701-dt-bindings-clock-qcom-Add-CMN-PLL-clock-controller-.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0702-clk-qcom-Add-CMN-PLL-clock-controller-driver-for-IPQ.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0703-clk-qcom-cmn-pll-add-IPQ5018-support.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0704-arm64-dts-qcom-ipq5018-Add-ethernet-cmn-node.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0711-net-phy-qcom-Introduce-IPQ5018-internal-PHY-driver.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0712-arm64-dts-qcom-ipq5018-add-mdio-node.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0713-arm64-dts-qcom-ipq5018-add-ge_phy-node.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0714-net-phy-qcom-IPQ5018-enable-configuration-of-DAC-settings.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0715-net-phy-qcom-add-IPQ5018-initvals-and-CDT-feature.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0721-clk-gcc-ipq5018-remove-the-unsupported-clk-combinati.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0722-clk-gcc-ipq5018-refer-to-ge-phy-rx-and-tx-clk-providers-by-name.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0751-net-dsa-qca8k-always-enable-SGMII-auto-negotiation.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0752-net-dsa-qca8k-support-PHY-to-PHY-CPU-link.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0801-dt-bindings-remoteproc-qcom-Add-support-for-multipd-model.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0802-firmware-qcom_scm-ipq5332-add-support-to-pass-metada.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0803-firmware-qcom_scm-ipq5332-add-msa-lock-unlock-suppor.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0804-remoteproc-qcom-q6v5-Add-multipd-interrupts-support.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0805-remoteproc-qcom-Add-Hexagon-based-multipd-rproc-driv.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0806-rproc-qcom_q6v5_mpd-split-q6_wcss-to-rootpd-and-user.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0807-remoteproc-qcom_q6v5_mpd-fix-incorrent-use-of-rproc-.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0811-firmware-qcom_scm-support-MPD.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0812-soc-qcom-mdt_loader-support-MPD.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0813-remoteproc-qcom_q6v5_mpd-enable-clocks.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0814-remoteproc-qcom_q6v5_mpd-support-ipq5018.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0815-remoteproc-qcom_q6v5_mpd-add-support-for-passing-v1-bootargs.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0816-arm64-dts-qcom-ipq5018-add-wifi-support.patch [new file with mode: 0644]
target/linux/qualcommax/patches-6.6/0817-arm64-dts-qcom-ipq5018-add-tz_apps-reserved-memory-region.patch [new file with mode: 0644]

index 3c9bb840e31ca68caa14d203daf2dad70439e43d..b3b6dc6118aa1dcd1b5203888cbd1ad8442eaf4c 100644 (file)
@@ -6,7 +6,7 @@ BOARDNAME:=Qualcomm Atheros 802.11ax WiSoC-s
 FEATURES:=squashfs ramdisk fpu nand rtc emmc
 KERNELNAME:=Image
 CPU_TYPE:=cortex-a53
-SUBTARGETS:=ipq807x ipq60xx
+SUBTARGETS:=ipq807x ipq60xx ipq50xx
 
 KERNEL_PATCHVER:=6.6
 
diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-ess.dtsi b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-ess.dtsi
new file mode 100644 (file)
index 0000000..a42b7bb
--- /dev/null
@@ -0,0 +1,116 @@
+#include <dt-bindings/net/qcom-ipq-ess.h>
+
+&soc {
+       ess_instance: ess-instance {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               num_devices = <1>;
+
+               switch: ess-switch@39c00000 {
+                       compatible = "qcom,ess-switch-ipq50xx";
+                       device_id = <0>;
+                       cmnblk_clk = "internal_96MHz";
+                       reg = <0x39c00000 0x200000>;
+                       switch_access_mode = "local bus";
+                       clocks = <&gcc GCC_CMN_BLK_AHB_CLK>,
+                                <&gcc GCC_CMN_BLK_SYS_CLK>,
+                                <&gcc GCC_UNIPHY_AHB_CLK>,
+                                <&gcc GCC_UNIPHY_SYS_CLK>,
+                                <&gcc GCC_MDIO0_AHB_CLK>,
+                                <&gcc GCC_MDIO1_AHB_CLK>,
+                                <&gcc GCC_GMAC0_CFG_CLK>,
+                                <&gcc GCC_GMAC0_SYS_CLK>,
+                                <&gcc GCC_GMAC1_CFG_CLK>,
+                                <&gcc GCC_GMAC1_SYS_CLK>,
+                                <&gcc GCC_GEPHY_RX_CLK>,
+                                <&gcc GCC_GEPHY_TX_CLK>,
+                                <&gcc GCC_UNIPHY_RX_CLK>,
+                                <&gcc GCC_UNIPHY_TX_CLK>,
+                                <&gcc GCC_GMAC0_RX_CLK>,
+                                <&gcc GCC_GMAC0_TX_CLK>,
+                                <&gcc GCC_GMAC1_RX_CLK>,
+                                <&gcc GCC_GMAC1_TX_CLK>,
+                                <&gcc GCC_SNOC_GMAC0_AHB_CLK>,
+                                <&gcc GCC_SNOC_GMAC1_AHB_CLK>,
+                                <&gcc GCC_GMAC0_PTP_CLK>,
+                                <&gcc GCC_GMAC1_PTP_CLK>;
+                       clock-names = "cmn_ahb_clk",
+                                     "cmn_sys_clk",
+                                     "uniphy_ahb_clk",
+                                     "uniphy_sys_clk",
+                                     "gcc_mdio0_ahb_clk",
+                                     "gcc_mdio1_ahb_clk",
+                                     "gcc_gmac0_cfg_clk",
+                                     "gcc_gmac0_sys_clk",
+                                     "gcc_gmac1_cfg_clk",
+                                     "gcc_gmac1_sys_clk",
+                                     "uniphy0_port1_rx_clk",
+                                     "uniphy0_port1_tx_clk",
+                                     "uniphy1_port5_rx_clk",
+                                     "uniphy1_port5_tx_clk",
+                                     "nss_port1_rx_clk",
+                                     "nss_port1_tx_clk",
+                                     "nss_port2_rx_clk",
+                                     "nss_port2_tx_clk",
+                                     "gcc_snoc_gmac0_ahb_clk",
+                                     "gcc_snoc_gmac1_ahb_clk",
+                                     "gcc_gmac0_ptp_clk",
+                                     "gcc_gmac1_ptp_clk";
+                       resets = <&gcc GCC_GMAC0_BCR>,
+                                <&gcc GCC_GMAC1_BCR>,
+                                <&gcc GCC_UNIPHY_BCR>,
+                                <&gcc GCC_UNIPHY_SOFT_RESET>;
+                       reset-names = "gmac0_bcr_rst",
+                                     "gmac1_bcr_rst",
+                                     "uniphy_bcr_rst",
+                                     "uniphy1_soft_rst";
+
+                       status = "disabled";
+               };
+       };
+
+       ess-uniphy@98000 {
+               compatible = "qcom,ess-uniphy";
+               reg = <0x98000 0x800>;
+               uniphy_access_mode = "local bus";
+       };
+
+       nss-dp-common {
+               compatible = "qcom,nss-dp-common";
+               qcom,tcsr-base = <0x01937000>;
+       };
+
+       dp1: dp1 {
+               device_type = "network";
+               compatible = "qcom,nss-dp";
+               qcom,id = <1>;
+
+               reg = <0x39C00000 0x10000>;
+               interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&gcc GCC_SNOC_GMAC0_AXI_CLK>;
+               clock-names = "nss-snoc-gmac-axi-clk";
+
+               qcom,mactype = <2>; /* GMAC_HAL_TYPE_SYN_GMAC */
+               local-mac-address = [000000000000];
+               phy-mode = "internal";
+               phy-handle = <&ge_phy>;
+
+               status = "disabled";
+       };
+
+       dp2: dp2 {
+               device_type = "network";
+               compatible = "qcom,nss-dp";
+               qcom,id = <2>;
+
+               reg = <0x39D00000 0x10000>;
+               interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&gcc GCC_SNOC_GMAC1_AXI_CLK>;
+               clock-names = "nss-snoc-gmac-axi-clk";
+
+               qcom,mactype = <2>; /* GMAC_HAL_TYPE_SYN_GMAC */
+               local-mac-address = [000000000000];
+               phy-mode = "sgmii";
+               status = "disabled";
+       };
+};
diff --git a/target/linux/qualcommax/image/ipq50xx.mk b/target/linux/qualcommax/image/ipq50xx.mk
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/target/linux/qualcommax/ipq50xx/config-default b/target/linux/qualcommax/ipq50xx/config-default
new file mode 100644 (file)
index 0000000..63c1d9d
--- /dev/null
@@ -0,0 +1,26 @@
+CONFIG_QCOM_APM=y
+CONFIG_IPQ_GCC_5018=y
+CONFIG_PINCTRL_IPQ5018=y
+
+CONFIG_MTD_SPI_NAND=y
+CONFIG_SPI_QPIC_SNAND=y
+
+CONFIG_IPQ_CMN_PLL=y
+CONFIG_IPQ5018_PHY=y
+CONFIG_NET_DSA=y
+CONFIG_NET_DSA_QCA8K=y
+CONFIG_NET_DSA_TAG_QCA=y
+CONFIG_QCA83XX_PHY=y
+
+CONFIG_QCOM_Q6V5_MPD=y
+CONFIG_QCOM_QMI_HELPERS=y
+
+CONFIG_PHY_QCOM_UNIPHY_PCIE_28LP=y
+CONFIG_PCIE_QCOM=y
+
+CONFIG_PWM=y
+CONFIG_PWM_IPQ=y
+CONFIG_LEDS_PWM=y
+
+CONFIG_PHY_QCOM_M31_USB=y
+CONFIG_USB_DWC3_QCOM=y
diff --git a/target/linux/qualcommax/ipq50xx/target.mk b/target/linux/qualcommax/ipq50xx/target.mk
new file mode 100644 (file)
index 0000000..2e68329
--- /dev/null
@@ -0,0 +1,7 @@
+SUBTARGET:=ipq50xx
+BOARDNAME:=Qualcomm Atheros IPQ50xx
+DEFAULT_PACKAGES += ath11k-firmware-ipq5018
+
+define Target/Description
+       Build firmware images for Qualcomm Atheros IPQ50xx based boards.
+endef
diff --git a/target/linux/qualcommax/patches-6.6/0065-v6.7-arm64-dts-qcom-ipq5018-add-watchdog.patch b/target/linux/qualcommax/patches-6.6/0065-v6.7-arm64-dts-qcom-ipq5018-add-watchdog.patch
new file mode 100644 (file)
index 0000000..8e973e6
--- /dev/null
@@ -0,0 +1,31 @@
+From 9cbaee8379e620f82112002f973adde19679df31 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Wed, 16 Aug 2023 18:14:00 +0200
+Subject: [PATCH] arm64: dts: qcom: ipq5018: add watchdog
+
+Add the required DT node for watchdog operation.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org>
+Link: https://lore.kernel.org/r/20230816161455.3310629-2-robimarko@gmail.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ arch/arm64/boot/dts/qcom/ipq5018.dtsi | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -181,6 +181,13 @@
+                       };
+               };
++              watchdog: watchdog@b017000 {
++                      compatible = "qcom,apss-wdt-ipq5018", "qcom,kpss-wdt";
++                      reg = <0x0b017000 0x40>;
++                      interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>;
++                      clocks = <&sleep_clk>;
++              };
++
+               timer@b120000 {
+                       compatible = "arm,armv7-timer-mem";
+                       reg = <0x0b120000 0x1000>;
diff --git a/target/linux/qualcommax/patches-6.6/0066-v6.7-dt-bindings-firmware-qcom-scm-support-indicating-SDI-default-state.patch b/target/linux/qualcommax/patches-6.6/0066-v6.7-dt-bindings-firmware-qcom-scm-support-indicating-SDI-default-state.patch
new file mode 100644 (file)
index 0000000..46d3c61
--- /dev/null
@@ -0,0 +1,41 @@
+From 92dab9ea5f389c12828283146c60054642453a91 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Wed, 16 Aug 2023 18:45:38 +0200
+Subject: [PATCH] dt-bindings: firmware: qcom,scm: support indicating SDI
+ default state
+
+IPQ5018 has SDI (Secure Debug Image) enabled by TZ by default, and that
+means that WDT being asserted or just trying to reboot will hang the board
+in the debug mode and only pulling the power and repowering will help.
+Some IPQ4019 boards like Google WiFI have it enabled as well.
+
+So, lets add a boolean property to indicate that SDI is enabled by default
+and thus needs to be disabled by the kernel.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+Acked-by: Mukesh Ojha <quic_mojha@quicinc.com>
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Reviewed-by: Brian Norris <computersforpeace@gmail.com>
+Link: https://lore.kernel.org/r/20230816164641.3371878-1-robimarko@gmail.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ Documentation/devicetree/bindings/firmware/qcom,scm.yaml | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml
++++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml
+@@ -89,6 +89,14 @@ properties:
+       protocol to handle sleeping SCM calls.
+     maxItems: 1
++  qcom,sdi-enabled:
++    description:
++      Indicates that the SDI (Secure Debug Image) has been enabled by TZ
++      by default and it needs to be disabled.
++      If not disabled WDT assertion or reboot will cause the board to hang
++      in the debug mode.
++    type: boolean
++
+   qcom,dload-mode:
+     $ref: /schemas/types.yaml#/definitions/phandle-array
+     items:
diff --git a/target/linux/qualcommax/patches-6.6/0067-v6.7-firmware-qcom-scm-disable-SDI-if-required.patch b/target/linux/qualcommax/patches-6.6/0067-v6.7-firmware-qcom-scm-disable-SDI-if-required.patch
new file mode 100644 (file)
index 0000000..9df758a
--- /dev/null
@@ -0,0 +1,83 @@
+From ff4aa3bc98258a240b9bbab53fd8d2fb8184c485 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Wed, 16 Aug 2023 18:45:39 +0200
+Subject: [PATCH] firmware: qcom_scm: disable SDI if required
+
+IPQ5018 has SDI (Secure Debug Image) enabled by TZ by default, and that
+means that WDT being asserted or just trying to reboot will hang the board
+in the debug mode and only pulling the power and repowering will help.
+Some IPQ4019 boards like Google WiFI have it enabled as well.
+
+Luckily, SDI can be disabled via an SCM call.
+
+So, lets use the boolean DT property to identify boards that have SDI
+enabled by default and use the SCM call to disable SDI during SCM probe.
+It is important to disable it as soon as possible as we might have a WDT
+assertion at any time which would then leave the board in debug mode,
+thus disabling it during SCM removal is not enough.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+Reviewed-by: Guru Das Srinagesh <quic_gurus@quicinc.com>
+Link: https://lore.kernel.org/r/20230816164641.3371878-2-robimarko@gmail.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ drivers/firmware/qcom_scm.c | 30 ++++++++++++++++++++++++++++++
+ drivers/firmware/qcom_scm.h |  1 +
+ 2 files changed, 31 insertions(+)
+
+--- a/drivers/firmware/qcom_scm.c
++++ b/drivers/firmware/qcom_scm.c
+@@ -410,6 +410,29 @@ int qcom_scm_set_remote_state(u32 state,
+ }
+ EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state);
++static int qcom_scm_disable_sdi(void)
++{
++      int ret;
++      struct qcom_scm_desc desc = {
++              .svc = QCOM_SCM_SVC_BOOT,
++              .cmd = QCOM_SCM_BOOT_SDI_CONFIG,
++              .args[0] = 1, /* Disable watchdog debug */
++              .args[1] = 0, /* Disable SDI */
++              .arginfo = QCOM_SCM_ARGS(2),
++              .owner = ARM_SMCCC_OWNER_SIP,
++      };
++      struct qcom_scm_res res;
++
++      ret = qcom_scm_clk_enable();
++      if (ret)
++              return ret;
++      ret = qcom_scm_call(__scm->dev, &desc, &res);
++
++      qcom_scm_clk_disable();
++
++      return ret ? : res.result[0];
++}
++
+ static int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
+ {
+       struct qcom_scm_desc desc = {
+@@ -1473,6 +1496,13 @@ static int qcom_scm_probe(struct platfor
+       __get_convention();
++
++      /*
++       * Disable SDI if indicated by DT that it is enabled by default.
++       */
++      if (of_property_read_bool(pdev->dev.of_node, "qcom,sdi-enabled"))
++              qcom_scm_disable_sdi();
++
+       /*
+        * If requested enable "download mode", from this point on warmboot
+        * will cause the boot stages to enter download mode, unless
+--- a/drivers/firmware/qcom_scm.h
++++ b/drivers/firmware/qcom_scm.h
+@@ -80,6 +80,7 @@ extern int scm_legacy_call(struct device
+ #define QCOM_SCM_SVC_BOOT             0x01
+ #define QCOM_SCM_BOOT_SET_ADDR                0x01
+ #define QCOM_SCM_BOOT_TERMINATE_PC    0x02
++#define QCOM_SCM_BOOT_SDI_CONFIG      0x09
+ #define QCOM_SCM_BOOT_SET_DLOAD_MODE  0x10
+ #define QCOM_SCM_BOOT_SET_ADDR_MC     0x11
+ #define QCOM_SCM_BOOT_SET_REMOTE_STATE        0x0a
diff --git a/target/linux/qualcommax/patches-6.6/0068-v6.7-dt-bindings-qcom-scm-document-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0068-v6.7-dt-bindings-qcom-scm-document-IPQ5018-compatible.patch
new file mode 100644 (file)
index 0000000..d36ccdd
--- /dev/null
@@ -0,0 +1,25 @@
+From f6aa7386bc40b552eea8ec1b1d2168afe3b31110 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Wed, 16 Aug 2023 18:45:40 +0200
+Subject: [PATCH] dt-bindings: firmware: qcom,scm: document IPQ5018 compatible
+
+It seems that IPQ5018 compatible was never documented in the bindings.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Link: https://lore.kernel.org/r/20230816164641.3371878-3-robimarko@gmail.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ Documentation/devicetree/bindings/firmware/qcom,scm.yaml | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml
++++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml
+@@ -24,6 +24,7 @@ properties:
+           - qcom,scm-apq8064
+           - qcom,scm-apq8084
+           - qcom,scm-ipq4019
++          - qcom,scm-ipq5018
+           - qcom,scm-ipq5332
+           - qcom,scm-ipq6018
+           - qcom,scm-ipq806x
diff --git a/target/linux/qualcommax/patches-6.6/0069-v6.7-arm64-dts-qcom-IPQ5018-indicate-that-SDI-shoud-be-disabled.patch b/target/linux/qualcommax/patches-6.6/0069-v6.7-arm64-dts-qcom-IPQ5018-indicate-that-SDI-shoud-be-disabled.patch
new file mode 100644 (file)
index 0000000..6d598bf
--- /dev/null
@@ -0,0 +1,26 @@
+From 79796e87215db9587d6c66ec6f6781e091bc6464 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Wed, 16 Aug 2023 18:45:41 +0200
+Subject: [PATCH] arm64: dts: qcom: ipq5018: indicate that SDI should be
+ disabled
+
+Now that SCM has support for indicating that SDI has been enabled by
+default, lets set the property so SCM disables it during probing.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+Link: https://lore.kernel.org/r/20230816164641.3371878-4-robimarko@gmail.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ arch/arm64/boot/dts/qcom/ipq5018.dtsi | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -57,6 +57,7 @@
+       firmware {
+               scm {
+                       compatible = "qcom,scm-ipq5018", "qcom,scm";
++                      qcom,sdi-enabled;
+               };
+       };
diff --git a/target/linux/qualcommax/patches-6.6/0070-v6.7-dt-bindings-phy-qcom-m31-Add-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0070-v6.7-dt-bindings-phy-qcom-m31-Add-IPQ5018-compatible.patch
new file mode 100644 (file)
index 0000000..7772122
--- /dev/null
@@ -0,0 +1,28 @@
+From 1852dfaacd3f4358bbfca134b63a02bbb30c1136 Mon Sep 17 00:00:00 2001
+From: Nitheesh Sekar <quic_nsekar@quicinc.com>
+Date: Mon, 4 Sep 2023 12:06:32 +0530
+Subject: [PATCH] dt-bindings: phy: qcom,m31: Add IPQ5018 compatible
+
+IPQ5332 qcom,m31 phy driver can support IPQ5018.
+
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
+Link: https://lore.kernel.org/r/20230904063635.24975-2-quic_nsekar@quicinc.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+---
+ .../devicetree/bindings/phy/qcom,ipq5332-usb-hsphy.yaml       | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/Documentation/devicetree/bindings/phy/qcom,ipq5332-usb-hsphy.yaml
++++ b/Documentation/devicetree/bindings/phy/qcom,ipq5332-usb-hsphy.yaml
+@@ -17,7 +17,9 @@ description:
+ properties:
+   compatible:
+     items:
+-      - const: qcom,ipq5332-usb-hsphy
++      - enum:
++          - qcom,ipq5018-usb-hsphy
++          - qcom,ipq5332-usb-hsphy
+   "#phy-cells":
+     const: 0
diff --git a/target/linux/qualcommax/patches-6.6/0071-v6.7-phy-qcom-m31-Add-compatible-phy-init-sequence-for-IPQ5018.patch b/target/linux/qualcommax/patches-6.6/0071-v6.7-phy-qcom-m31-Add-compatible-phy-init-sequence-for-IPQ5018.patch
new file mode 100644 (file)
index 0000000..248b47c
--- /dev/null
@@ -0,0 +1,89 @@
+From 68320e35f8cb1987b4ad34347fc7033832da99e3 Mon Sep 17 00:00:00 2001
+From: Nitheesh Sekar <quic_nsekar@quicinc.com>
+Date: Mon, 4 Sep 2023 12:06:33 +0530
+Subject: [PATCH] phy: qcom-m31: Add compatible, phy init sequence for IPQ5018
+
+Add phy init sequence and compatible string for IPQ5018
+chipset.
+
+Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
+Link: https://lore.kernel.org/r/20230904063635.24975-3-quic_nsekar@quicinc.com
+Signed-off-by: Vinod Koul <vkoul@kernel.org>
+---
+ drivers/phy/qualcomm/phy-qcom-m31.c | 51 +++++++++++++++++++++++++++++
+ 1 file changed, 51 insertions(+)
+
+--- a/drivers/phy/qualcomm/phy-qcom-m31.c
++++ b/drivers/phy/qualcomm/phy-qcom-m31.c
+@@ -82,6 +82,50 @@ struct m31_priv_data {
+       unsigned int                    nregs;
+ };
++static const struct m31_phy_regs m31_ipq5018_regs[] = {
++      {
++              .off = USB_PHY_CFG0,
++              .val = UTMI_PHY_OVERRIDE_EN
++      },
++      {
++              .off = USB_PHY_UTMI_CTRL5,
++              .val = POR_EN,
++              .delay = 15
++      },
++      {
++              .off = USB_PHY_FSEL_SEL,
++              .val = FREQ_SEL
++      },
++      {
++              .off = USB_PHY_HS_PHY_CTRL_COMMON0,
++              .val = COMMONONN | FSEL | RETENABLEN
++      },
++      {
++              .off = USB_PHY_REFCLK_CTRL,
++              .val = CLKCORE
++      },
++      {
++              .off = USB_PHY_UTMI_CTRL5,
++              .val = POR_EN
++      },
++      {
++              .off = USB_PHY_HS_PHY_CTRL2,
++              .val = USB2_SUSPEND_N_SEL | USB2_SUSPEND_N | USB2_UTMI_CLK_EN
++      },
++      {
++              .off = USB_PHY_UTMI_CTRL5,
++              .val = 0x0
++      },
++      {
++              .off = USB_PHY_HS_PHY_CTRL2,
++              .val = USB2_SUSPEND_N | USB2_UTMI_CLK_EN
++      },
++      {
++              .off = USB_PHY_CFG0,
++              .val = 0x0
++      },
++};
++
+ static struct m31_phy_regs m31_ipq5332_regs[] = {
+       {
+               USB_PHY_CFG0,
+@@ -267,6 +311,12 @@ static int m31usb_phy_probe(struct platf
+       return PTR_ERR_OR_ZERO(phy_provider);
+ }
++static const struct m31_priv_data m31_ipq5018_data = {
++      .ulpi_mode = false,
++      .regs = m31_ipq5018_regs,
++      .nregs = ARRAY_SIZE(m31_ipq5018_regs),
++};
++
+ static const struct m31_priv_data m31_ipq5332_data = {
+       .ulpi_mode = false,
+       .regs = m31_ipq5332_regs,
+@@ -274,6 +324,7 @@ static const struct m31_priv_data m31_ip
+ };
+ static const struct of_device_id m31usb_phy_id_table[] = {
++      { .compatible = "qcom,ipq5018-usb-hsphy", .data = &m31_ipq5018_data },
+       { .compatible = "qcom,ipq5332-usb-hsphy", .data = &m31_ipq5332_data },
+       { },
+ };
diff --git a/target/linux/qualcommax/patches-6.6/0072-v6.7-dt-bindings-usb-dwc3-Add-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0072-v6.7-dt-bindings-usb-dwc3-Add-IPQ5018-compatible.patch
new file mode 100644 (file)
index 0000000..146fcbf
--- /dev/null
@@ -0,0 +1,41 @@
+From 3865a64284cc4845c61cf3dc6c7246349d80cc49 Mon Sep 17 00:00:00 2001
+From: Nitheesh Sekar <quic_nsekar@quicinc.com>
+Date: Thu, 31 Aug 2023 08:35:03 +0530
+Subject: [PATCH] dt-bindings: usb: dwc3: Add IPQ5018 compatible
+
+Document the IPQ5018 dwc3 compatible.
+
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
+Link: https://lore.kernel.org/r/20230831030503.17100-1-quic_nsekar@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ Documentation/devicetree/bindings/usb/qcom,dwc3.yaml | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml
++++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml
+@@ -14,6 +14,7 @@ properties:
+     items:
+       - enum:
+           - qcom,ipq4019-dwc3
++          - qcom,ipq5018-dwc3
+           - qcom,ipq5332-dwc3
+           - qcom,ipq6018-dwc3
+           - qcom,ipq8064-dwc3
+@@ -238,6 +239,7 @@ allOf:
+         compatible:
+           contains:
+             enum:
++              - qcom,ipq5018-dwc3
+               - qcom,ipq5332-dwc3
+               - qcom,msm8994-dwc3
+               - qcom,qcs404-dwc3
+@@ -411,6 +413,7 @@ allOf:
+         compatible:
+           contains:
+             enum:
++              - qcom,ipq5018-dwc3
+               - qcom,ipq5332-dwc3
+               - qcom,sdm660-dwc3
+     then:
diff --git a/target/linux/qualcommax/patches-6.6/0073-v6.8-arm64-dts-qcom-ipq5018-Add-USB-related-nodes.patch b/target/linux/qualcommax/patches-6.6/0073-v6.8-arm64-dts-qcom-ipq5018-Add-USB-related-nodes.patch
new file mode 100644 (file)
index 0000000..99fdae7
--- /dev/null
@@ -0,0 +1,86 @@
+From e7166f2774aafefd29ff26ffbbb7f6d40ac8ea1c Mon Sep 17 00:00:00 2001
+From: Nitheesh Sekar <quic_nsekar@quicinc.com>
+Date: Mon, 4 Sep 2023 12:06:34 +0530
+Subject: [PATCH] arm64: dts: qcom: ipq5018: Add USB related nodes
+
+Add USB phy and controller nodes.
+
+Co-developed-by: Amandeep Singh <quic_amansing@quicinc.com>
+Signed-off-by: Amandeep Singh <quic_amansing@quicinc.com>
+Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
+Link: https://lore.kernel.org/r/20230904063635.24975-4-quic_nsekar@quicinc.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ arch/arm64/boot/dts/qcom/ipq5018.dtsi | 54 +++++++++++++++++++++++++++
+ 1 file changed, 54 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -94,6 +94,19 @@
+               #size-cells = <1>;
+               ranges = <0 0 0 0xffffffff>;
++              usbphy0: phy@5b000 {
++                      compatible = "qcom,ipq5018-usb-hsphy";
++                      reg = <0x0005b000 0x120>;
++
++                      clocks = <&gcc GCC_USB0_PHY_CFG_AHB_CLK>;
++
++                      resets = <&gcc GCC_QUSB2_0_PHY_BCR>;
++
++                      #phy-cells = <0>;
++
++                      status = "disabled";
++              };
++
+               tlmm: pinctrl@1000000 {
+                       compatible = "qcom,ipq5018-tlmm";
+                       reg = <0x01000000 0x300000>;
+@@ -156,6 +169,47 @@
+                       status = "disabled";
+               };
++              usb: usb@8af8800 {
++                      compatible = "qcom,ipq5018-dwc3", "qcom,dwc3";
++                      reg = <0x08af8800 0x400>;
++
++                      interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
++                      interrupt-names = "hs_phy_irq";
++
++                      clocks = <&gcc GCC_USB0_MASTER_CLK>,
++                               <&gcc GCC_SYS_NOC_USB0_AXI_CLK>,
++                               <&gcc GCC_USB0_SLEEP_CLK>,
++                               <&gcc GCC_USB0_MOCK_UTMI_CLK>;
++                      clock-names = "core",
++                                    "iface",
++                                    "sleep",
++                                    "mock_utmi";
++
++                      resets = <&gcc GCC_USB0_BCR>;
++
++                      qcom,select-utmi-as-pipe-clk;
++                      #address-cells = <1>;
++                      #size-cells = <1>;
++                      ranges;
++
++                      status = "disabled";
++
++                      usb_dwc: usb@8a00000 {
++                              compatible = "snps,dwc3";
++                              reg = <0x08a00000 0xe000>;
++                              clocks = <&gcc GCC_USB0_MOCK_UTMI_CLK>;
++                              clock-names = "ref";
++                              interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
++                              phy-names = "usb2-phy";
++                              phys = <&usbphy0>;
++                              tx-fifo-resize;
++                              snps,is-utmi-l1-suspend;
++                              snps,hird-threshold = /bits/ 8 <0x0>;
++                              snps,dis_u2_susphy_quirk;
++                              snps,dis_u3_susphy_quirk;
++                      };
++              };
++
+               intc: interrupt-controller@b000000 {
+                       compatible = "qcom,msm-qgic2";
+                       reg = <0x0b000000 0x1000>,  /* GICD */
diff --git a/target/linux/qualcommax/patches-6.6/0074-v6.8-arm64-dts-qcom-ipq5018-add-QUP1-SPI-controller.patch b/target/linux/qualcommax/patches-6.6/0074-v6.8-arm64-dts-qcom-ipq5018-add-QUP1-SPI-controller.patch
new file mode 100644 (file)
index 0000000..d0d9e45
--- /dev/null
@@ -0,0 +1,56 @@
+From a1f42e08f0f04b72a6597f080db4bfbb3737910c Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Wed, 4 Oct 2023 21:12:30 +0200
+Subject: [PATCH] arm64: dts: qcom: ipq5018: add QUP1 SPI controller
+
+Add the required BAM and QUP nodes for the QUP1 SPI controller on IPQ5018.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+Reviewed-by: Kathiravan Thirumoorthy <quic_kathirav@quicinc.com>
+Link: https://lore.kernel.org/r/20231004191303.331055-1-robimarko@gmail.com
+[bjorn: Padded address to 8 digits, fixed node sort order]
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ arch/arm64/boot/dts/qcom/ipq5018.dtsi | 24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -159,6 +159,16 @@
+                       status = "disabled";
+               };
++              blsp_dma: dma-controller@7884000 {
++                      compatible = "qcom,bam-v1.7.0";
++                      reg = <0x07884000 0x1d000>;
++                      interrupts = <GIC_SPI 238 IRQ_TYPE_LEVEL_HIGH>;
++                      clocks = <&gcc GCC_BLSP1_AHB_CLK>;
++                      clock-names = "bam_clk";
++                      #dma-cells = <1>;
++                      qcom,ee = <0>;
++              };
++
+               blsp1_uart1: serial@78af000 {
+                       compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
+                       reg = <0x078af000 0x200>;
+@@ -169,6 +179,20 @@
+                       status = "disabled";
+               };
++              blsp1_spi1: spi@78b5000 {
++                      compatible = "qcom,spi-qup-v2.2.1";
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      reg = <0x078b5000 0x600>;
++                      interrupts = <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>;
++                      clocks = <&gcc GCC_BLSP1_QUP1_SPI_APPS_CLK>,
++                               <&gcc GCC_BLSP1_AHB_CLK>;
++                      clock-names = "core", "iface";
++                      dmas = <&blsp_dma 4>, <&blsp_dma 5>;
++                      dma-names = "tx", "rx";
++                      status = "disabled";
++              };
++
+               usb: usb@8af8800 {
+                       compatible = "qcom,ipq5018-dwc3", "qcom,dwc3";
+                       reg = <0x08af8800 0x400>;
diff --git a/target/linux/qualcommax/patches-6.6/0075-v6.8-dt-bindings-clock-qcom-a53pll-add-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0075-v6.8-dt-bindings-clock-qcom-a53pll-add-IPQ5018-compatible.patch
new file mode 100644 (file)
index 0000000..314afa7
--- /dev/null
@@ -0,0 +1,25 @@
+From 4d45d56e17348c6b6bb2bce126a4a5ea97b19900 Mon Sep 17 00:00:00 2001
+From: Gokul Sriram Palanisamy <quic_gokulsri@quicinc.com>
+Date: Mon, 25 Sep 2023 15:58:24 +0530
+Subject: [PATCH] dt-bindings: clock: qcom,a53pll: add IPQ5018 compatible
+
+Add IPQ5018 compatible to A53 PLL bindings.
+
+Signed-off-by: Gokul Sriram Palanisamy <quic_gokulsri@quicinc.com>
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Link: https://lore.kernel.org/r/20230925102826.405446-2-quic_gokulsri@quicinc.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ Documentation/devicetree/bindings/clock/qcom,a53pll.yaml | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/Documentation/devicetree/bindings/clock/qcom,a53pll.yaml
++++ b/Documentation/devicetree/bindings/clock/qcom,a53pll.yaml
+@@ -16,6 +16,7 @@ description:
+ properties:
+   compatible:
+     enum:
++      - qcom,ipq5018-a53pll
+       - qcom,ipq5332-a53pll
+       - qcom,ipq6018-a53pll
+       - qcom,ipq8074-a53pll
diff --git a/target/linux/qualcommax/patches-6.6/0076-v6.8-clk-qcom-apss-ipq-pll-add-support-for-IPQ5018.patch b/target/linux/qualcommax/patches-6.6/0076-v6.8-clk-qcom-apss-ipq-pll-add-support-for-IPQ5018.patch
new file mode 100644 (file)
index 0000000..46dede0
--- /dev/null
@@ -0,0 +1,62 @@
+From 50492f929486c044b43cb3e2c0e040aa9b61ea2b Mon Sep 17 00:00:00 2001
+From: Gokul Sriram Palanisamy <quic_gokulsri@quicinc.com>
+Date: Mon, 25 Sep 2023 15:58:25 +0530
+Subject: [PATCH] clk: qcom: apss-ipq-pll: add support for IPQ5018
+
+IPQ5018 APSS PLL is of type Stromer. Reuse Stromer Plus PLL offsets,
+add configuration values and the compatible.
+
+Co-developed-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
+Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
+Signed-off-by: Gokul Sriram Palanisamy <quic_gokulsri@quicinc.com>
+Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Link: https://lore.kernel.org/r/20230925102826.405446-3-quic_gokulsri@quicinc.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ drivers/clk/qcom/apss-ipq-pll.c | 21 +++++++++++++++++++++
+ 1 file changed, 21 insertions(+)
+
+--- a/drivers/clk/qcom/apss-ipq-pll.c
++++ b/drivers/clk/qcom/apss-ipq-pll.c
+@@ -73,6 +73,20 @@ static struct clk_alpha_pll ipq_pll_stro
+       },
+ };
++static const struct alpha_pll_config ipq5018_pll_config = {
++      .l = 0x32,
++      .config_ctl_val = 0x4001075b,
++      .config_ctl_hi_val = 0x304,
++      .main_output_mask = BIT(0),
++      .aux_output_mask = BIT(1),
++      .early_output_mask = BIT(3),
++      .alpha_en_mask = BIT(24),
++      .status_val = 0x3,
++      .status_mask = GENMASK(10, 8),
++      .lock_det = BIT(2),
++      .test_ctl_hi_val = 0x00400003,
++};
++
+ static const struct alpha_pll_config ipq5332_pll_config = {
+       .l = 0x2d,
+       .config_ctl_val = 0x4001075b,
+@@ -129,6 +143,12 @@ struct apss_pll_data {
+       const struct alpha_pll_config *pll_config;
+ };
++static const struct apss_pll_data ipq5018_pll_data = {
++      .pll_type = CLK_ALPHA_PLL_TYPE_STROMER_PLUS,
++      .pll = &ipq_pll_stromer_plus,
++      .pll_config = &ipq5018_pll_config,
++};
++
+ static struct apss_pll_data ipq5332_pll_data = {
+       .pll_type = CLK_ALPHA_PLL_TYPE_STROMER_PLUS,
+       .pll = &ipq_pll_stromer_plus,
+@@ -195,6 +215,7 @@ static int apss_ipq_pll_probe(struct pla
+ }
+ static const struct of_device_id apss_ipq_pll_match_table[] = {
++      { .compatible = "qcom,ipq5018-a53pll", .data = &ipq5018_pll_data },
+       { .compatible = "qcom,ipq5332-a53pll", .data = &ipq5332_pll_data },
+       { .compatible = "qcom,ipq6018-a53pll", .data = &ipq6018_pll_data },
+       { .compatible = "qcom,ipq8074-a53pll", .data = &ipq8074_pll_data },
diff --git a/target/linux/qualcommax/patches-6.6/0077-v6.8-arm64-dts-qcom-ipq5018-enable-the-CPUFreq-support.patch b/target/linux/qualcommax/patches-6.6/0077-v6.8-arm64-dts-qcom-ipq5018-enable-the-CPUFreq-support.patch
new file mode 100644 (file)
index 0000000..72bec54
--- /dev/null
@@ -0,0 +1,98 @@
+From 3e4b53e04281ed3d9c7a4329c027097265c04d54 Mon Sep 17 00:00:00 2001
+From: Gokul Sriram Palanisamy <quic_gokulsri@quicinc.com>
+Date: Mon, 25 Sep 2023 15:58:26 +0530
+Subject: [PATCH] arm64: dts: qcom: ipq5018: enable the CPUFreq support
+
+Add the APCS, A53 PLL, cpu-opp-table nodes to set
+the CPU frequency at 800MHz (idle) or 1.008GHz.
+
+Co-developed-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
+Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
+Signed-off-by: Gokul Sriram Palanisamy <quic_gokulsri@quicinc.com>
+Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Link: https://lore.kernel.org/r/20230925102826.405446-4-quic_gokulsri@quicinc.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ arch/arm64/boot/dts/qcom/ipq5018.dtsi | 40 +++++++++++++++++++++++++++
+ 1 file changed, 40 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -5,6 +5,7 @@
+  * Copyright (c) 2023 The Linux Foundation. All rights reserved.
+  */
++#include <dt-bindings/clock/qcom,apss-ipq.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/qcom,gcc-ipq5018.h>
+ #include <dt-bindings/reset/qcom,gcc-ipq5018.h>
+@@ -36,6 +37,8 @@
+                       reg = <0x0>;
+                       enable-method = "psci";
+                       next-level-cache = <&L2_0>;
++                      clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>;
++                      operating-points-v2 = <&cpu_opp_table>;
+               };
+               CPU1: cpu@1 {
+@@ -44,6 +47,8 @@
+                       reg = <0x1>;
+                       enable-method = "psci";
+                       next-level-cache = <&L2_0>;
++                      clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>;
++                      operating-points-v2 = <&cpu_opp_table>;
+               };
+               L2_0: l2-cache {
+@@ -54,6 +59,25 @@
+               };
+       };
++      cpu_opp_table: opp-table-cpu {
++              compatible = "operating-points-v2";
++              opp-shared;
++
++              /*
++              opp-800000000 {
++                      opp-hz = /bits/ 64 <800000000>;
++                      opp-microvolt = <1100000>;
++                      clock-latency-ns = <200000>;
++              };
++              */
++
++              opp-1008000000 {
++                      opp-hz = /bits/ 64 <1008000000>;
++                      opp-microvolt = <1100000>;
++                      clock-latency-ns = <200000>;
++              };
++      };
++
+       firmware {
+               scm {
+                       compatible = "qcom,scm-ipq5018", "qcom,scm";
+@@ -267,6 +291,24 @@
+                       clocks = <&sleep_clk>;
+               };
++              apcs_glb: mailbox@b111000 {
++                      compatible = "qcom,ipq5018-apcs-apps-global",
++                                   "qcom,ipq6018-apcs-apps-global";
++                      reg = <0x0b111000 0x1000>;
++                      #clock-cells = <1>;
++                      clocks = <&a53pll>, <&xo_board_clk>, <&gcc GPLL0>;
++                      clock-names = "pll", "xo", "gpll0";
++                      #mbox-cells = <1>;
++              };
++
++              a53pll: clock@b116000 {
++                      compatible = "qcom,ipq5018-a53pll";
++                      reg = <0x0b116000 0x40>;
++                      #clock-cells = <0>;
++                      clocks = <&xo_board_clk>;
++                      clock-names = "xo";
++              };
++
+               timer@b120000 {
+                       compatible = "arm,armv7-timer-mem";
+                       reg = <0x0b120000 0x1000>;
diff --git a/target/linux/qualcommax/patches-6.6/0078-v6.8-arm64-dts-qcom-ipq5018-add-few-more-reserved-memory-regions.patch b/target/linux/qualcommax/patches-6.6/0078-v6.8-arm64-dts-qcom-ipq5018-add-few-more-reserved-memory-regions.patch
new file mode 100644 (file)
index 0000000..97631e7
--- /dev/null
@@ -0,0 +1,66 @@
+From a427dd16e61f3d145bc24f0ed09692fc25931250 Mon Sep 17 00:00:00 2001
+From: Kathiravan Thirumoorthy <quic_kathirav@quicinc.com>
+Date: Wed, 25 Oct 2023 22:12:12 +0530
+Subject: [PATCH] arm64: dts: qcom: ipq5018: add few more reserved memory
+ regions
+
+Like all other IPQ SoCs, bootloader will collect the system RAM contents
+upon crash for the post morterm analysis. If we don't reserve the memory
+region used by bootloader, obviously linux will consume it and upon next
+boot on crash, bootloader will be loaded in the same region, which will
+lead to loose some of the data, sometimes we may miss out critical
+information. So lets reserve the region used by the bootloader.
+
+Similarly SBL copies some data into the reserved region and it will be
+used in the crash scenario. So reserve 1MB for SBL as well.
+
+While at it, enable the SMEM support along with TCSR mutex.
+
+Signed-off-by: Kathiravan Thirumoorthy <quic_kathirav@quicinc.com>
+Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org>
+Link: https://lore.kernel.org/r/20231025-ipq5018-misc-v1-1-7d14fde97fe7@quicinc.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ arch/arm64/boot/dts/qcom/ipq5018.dtsi | 24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -106,6 +106,24 @@
+               #size-cells = <2>;
+               ranges;
++              bootloader@4a800000 {
++                      reg = <0x0 0x4a800000 0x0 0x200000>;
++                      no-map;
++              };
++
++              sbl@4aa00000 {
++                      reg = <0x0 0x4aa00000 0x0 0x100000>;
++                      no-map;
++              };
++
++              smem@4ab00000 {
++                      compatible = "qcom,smem";
++                      reg = <0x0 0x4ab00000 0x0 0x100000>;
++                      no-map;
++
++                      hwlocks = <&tcsr_mutex 3>;
++              };
++
+               tz_region: tz@4ac00000 {
+                       reg = <0x0 0x4ac00000 0x0 0x200000>;
+                       no-map;
+@@ -166,6 +184,12 @@
+                       #power-domain-cells = <1>;
+               };
++              tcsr_mutex: hwlock@1905000 {
++                      compatible = "qcom,tcsr-mutex";
++                      reg = <0x01905000 0x20000>;
++                      #hwlock-cells = <1>;
++              };
++
+               sdhc_1: mmc@7804000 {
+                       compatible = "qcom,ipq5018-sdhci", "qcom,sdhci-msm-v5";
+                       reg = <0x7804000 0x1000>;
diff --git a/target/linux/qualcommax/patches-6.6/0080-v6.10-clk-qcom-apss-ipq-pll-use-stromer-ops-for-IPQ5018-to-fix-boot-failure.patch b/target/linux/qualcommax/patches-6.6/0080-v6.10-clk-qcom-apss-ipq-pll-use-stromer-ops-for-IPQ5018-to-fix-boot-failure.patch
new file mode 100644 (file)
index 0000000..ea6f61b
--- /dev/null
@@ -0,0 +1,83 @@
+From: Gabor Juhos <j4g8y7@gmail.com>
+Subject: [PATCH] clk: qcom: apss-ipq-pll: use stromer ops for IPQ5018 to fix boot failure
+Date: Fri, 15 Mar 2024 17:16:41 +0100
+
+Booting v6.8 results in a hang on various IPQ5018 based boards.
+Investigating the problem showed that the hang happens when the
+clk_alpha_pll_stromer_plus_set_rate() function tries to write
+into the PLL_MODE register of the APSS PLL.
+
+Checking the downstream code revealed that it uses [1] stromer
+specific operations for IPQ5018, whereas in the current code
+the stromer plus specific operations are used.
+
+The ops in the 'ipq_pll_stromer_plus' clock definition can't be
+changed since that is needed for IPQ5332, so add a new alpha pll
+clock declaration which uses the correct stromer ops and use this
+new clock for IPQ5018 to avoid the boot failure.
+
+Also, change pll_type in 'ipq5018_pll_data' to
+CLK_ALPHA_PLL_TYPE_STROMER to better reflect that it is a Stromer
+PLL and change the apss_ipq_pll_probe() function accordingly.
+
+1. https://git.codelinaro.org/clo/qsdk/oss/kernel/linux-ipq-5.4/-/blob/NHSS.QSDK.12.4/drivers/clk/qcom/apss-ipq5018.c#L67
+
+Fixes: 50492f929486 ("clk: qcom: apss-ipq-pll: add support for IPQ5018")
+Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
+---
+ drivers/clk/qcom/apss-ipq-pll.c | 30 +++++++++++++++++++++++++++---
+ 1 file changed, 27 insertions(+), 3 deletions(-)
+
+--- a/drivers/clk/qcom/apss-ipq-pll.c
++++ b/drivers/clk/qcom/apss-ipq-pll.c
+@@ -55,6 +55,29 @@ static struct clk_alpha_pll ipq_pll_huay
+       },
+ };
++static struct clk_alpha_pll ipq_pll_stromer = {
++      .offset = 0x0,
++      /*
++       * Reuse CLK_ALPHA_PLL_TYPE_STROMER_PLUS register offsets.
++       * Although this is a bit confusing, but the offset values
++       * are correct nevertheless.
++       */
++      .regs = ipq_pll_offsets[CLK_ALPHA_PLL_TYPE_STROMER_PLUS],
++      .flags = SUPPORTS_DYNAMIC_UPDATE,
++      .clkr = {
++              .enable_reg = 0x0,
++              .enable_mask = BIT(0),
++              .hw.init = &(const struct clk_init_data) {
++                      .name = "a53pll",
++                      .parent_data = &(const struct clk_parent_data) {
++                              .fw_name = "xo",
++                      },
++                      .num_parents = 1,
++                      .ops = &clk_alpha_pll_stromer_ops,
++              },
++      },
++};
++
+ static struct clk_alpha_pll ipq_pll_stromer_plus = {
+       .offset = 0x0,
+       .regs = ipq_pll_offsets[CLK_ALPHA_PLL_TYPE_STROMER_PLUS],
+@@ -144,8 +167,8 @@ struct apss_pll_data {
+ };
+ static const struct apss_pll_data ipq5018_pll_data = {
+-      .pll_type = CLK_ALPHA_PLL_TYPE_STROMER_PLUS,
+-      .pll = &ipq_pll_stromer_plus,
++      .pll_type = CLK_ALPHA_PLL_TYPE_STROMER,
++      .pll = &ipq_pll_stromer,
+       .pll_config = &ipq5018_pll_config,
+ };
+@@ -203,7 +226,8 @@ static int apss_ipq_pll_probe(struct pla
+       if (data->pll_type == CLK_ALPHA_PLL_TYPE_HUAYRA)
+               clk_alpha_pll_configure(data->pll, regmap, data->pll_config);
+-      else if (data->pll_type == CLK_ALPHA_PLL_TYPE_STROMER_PLUS)
++      else if (data->pll_type == CLK_ALPHA_PLL_TYPE_STROMER ||
++               data->pll_type == CLK_ALPHA_PLL_TYPE_STROMER_PLUS)
+               clk_stromer_pll_configure(data->pll, regmap, data->pll_config);
+       ret = devm_clk_register_regmap(dev, &data->pll->clkr);
diff --git a/target/linux/qualcommax/patches-6.6/0081-v6.10-clk-qcom-apss-ipq-pll-fix-PLL-rate-for-IPQ5018.patch b/target/linux/qualcommax/patches-6.6/0081-v6.10-clk-qcom-apss-ipq-pll-fix-PLL-rate-for-IPQ5018.patch
new file mode 100644 (file)
index 0000000..4bf0598
--- /dev/null
@@ -0,0 +1,32 @@
+From: Gabor Juhos <j4g8y7@gmail.com>
+Subject: [PATCH] clk: qcom: apss-ipq-pll: fix PLL rate for IPQ5018
+Date: Tue, 26 Mar 2024 14:34:11 +0100
+
+According to ipq5018.dtsi, the maximum supported rate by the
+CPU is 1.008 GHz on the IPQ5018 platform, however the current
+configuration of the PLL results in 1.2 GHz rate.
+
+Change the 'L' value in the PLL configuration to limit the
+rate to 1.008 GHz. The downstream kernel also uses the same
+value [1]. Also add a comment to indicate the desired
+frequency.
+
+[1] https://git.codelinaro.org/clo/qsdk/oss/kernel/linux-ipq-5.4/-/blob/NHSS.QSDK.12.4/drivers/clk/qcom/apss-ipq5018.c?ref_type=heads#L151
+
+Fixes: 50492f929486 ("clk: qcom: apss-ipq-pll: add support for IPQ5018")
+Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
+---
+ drivers/clk/qcom/apss-ipq-pll.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/clk/qcom/apss-ipq-pll.c
++++ b/drivers/clk/qcom/apss-ipq-pll.c
+@@ -97,7 +97,7 @@ static struct clk_alpha_pll ipq_pll_stro
+ };
+ static const struct alpha_pll_config ipq5018_pll_config = {
+-      .l = 0x32,
++      .l = 0x2a,
+       .config_ctl_val = 0x4001075b,
+       .config_ctl_hi_val = 0x304,
+       .main_output_mask = BIT(0),
diff --git a/target/linux/qualcommax/patches-6.6/0140-dt-bindings-pwm-add-IPQ6018-binding.patch b/target/linux/qualcommax/patches-6.6/0140-dt-bindings-pwm-add-IPQ6018-binding.patch
new file mode 100644 (file)
index 0000000..bba8b34
--- /dev/null
@@ -0,0 +1,60 @@
+From: Devi Priya <quic_devipriy@quicinc.com>
+Date: Thu, 5 Oct 2023 21:35:48 +0530
+Subject: [PATCH] dt-bindings: pwm: add IPQ6018 binding
+
+DT binding for the PWM block in Qualcomm IPQ6018 SoC.
+
+Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org>
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Co-developed-by: Baruch Siach <baruch.siach@siklu.com>
+Signed-off-by: Baruch Siach <baruch.siach@siklu.com>
+Signed-off-by: Devi Priya <quic_devipriy@quicinc.com>
+---
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml
+@@ -0,0 +1,45 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/pwm/qcom,ipq6018-pwm.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Qualcomm IPQ6018 PWM controller
++
++maintainers:
++  - Baruch Siach <baruch@tkos.co.il>
++
++properties:
++  compatible:
++    const: qcom,ipq6018-pwm
++
++  reg:
++    description: Offset of PWM register in the TCSR block.
++    maxItems: 1
++
++  clocks:
++    maxItems: 1
++
++  "#pwm-cells":
++    const: 2
++
++required:
++  - compatible
++  - reg
++  - clocks
++  - "#pwm-cells"
++
++additionalProperties: false
++
++examples:
++  - |
++    #include <dt-bindings/clock/qcom,gcc-ipq6018.h>
++
++    pwm: pwm@a010 {
++        compatible = "qcom,ipq6018-pwm";
++        reg = <0xa010 0x20>;
++        clocks = <&gcc GCC_ADSS_PWM_CLK>;
++        assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>;
++        assigned-clock-rates = <100000000>;
++        #pwm-cells = <2>;
++    };
diff --git a/target/linux/qualcommax/patches-6.6/0141-pwm-driver-for-qualcomm-ipq6018-pwm-block.patch b/target/linux/qualcommax/patches-6.6/0141-pwm-driver-for-qualcomm-ipq6018-pwm-block.patch
new file mode 100644 (file)
index 0000000..f4f396a
--- /dev/null
@@ -0,0 +1,330 @@
+From: Devi Priya <quic_devipriy@quicinc.com>
+Date: Thu, 5 Oct 2023 21:35:47 +0530
+Subject: [PATCH] pwm: driver for qualcomm ipq6018 pwm block
+
+Driver for the PWM block in Qualcomm IPQ6018 line of SoCs. Based on
+driver from downstream Codeaurora kernel tree. Removed support for older
+(V1) variants because I have no access to that hardware.
+
+Tested on IPQ6010 based hardware.
+
+Co-developed-by: Baruch Siach <baruch.siach@siklu.com>
+Signed-off-by: Baruch Siach <baruch.siach@siklu.com>
+Signed-off-by: Devi Priya <quic_devipriy@quicinc.com>
+---
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -282,6 +282,18 @@ config PWM_INTEL_LGM
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-intel-lgm.
++config PWM_IPQ
++      tristate "IPQ PWM support"
++      depends on ARCH_QCOM || COMPILE_TEST
++      depends on HAVE_CLK && HAS_IOMEM
++      help
++        Generic PWM framework driver for IPQ PWM block which supports
++        4 pwm channels. Each of the these channels can be configured
++        independent of each other.
++
++        To compile this driver as a module, choose M here: the module
++        will be called pwm-ipq.
++
+ config PWM_IQS620A
+       tristate "Azoteq IQS620A PWM support"
+       depends on MFD_IQS62X || COMPILE_TEST
+--- a/drivers/pwm/Makefile
++++ b/drivers/pwm/Makefile
+@@ -24,6 +24,7 @@ obj-$(CONFIG_PWM_IMX1)               += pwm-imx1.o
+ obj-$(CONFIG_PWM_IMX27)               += pwm-imx27.o
+ obj-$(CONFIG_PWM_IMX_TPM)     += pwm-imx-tpm.o
+ obj-$(CONFIG_PWM_INTEL_LGM)   += pwm-intel-lgm.o
++obj-$(CONFIG_PWM_IPQ)         += pwm-ipq.o
+ obj-$(CONFIG_PWM_IQS620A)     += pwm-iqs620a.o
+ obj-$(CONFIG_PWM_JZ4740)      += pwm-jz4740.o
+ obj-$(CONFIG_PWM_KEEMBAY)     += pwm-keembay.o
+--- /dev/null
++++ b/drivers/pwm/pwm-ipq.c
+@@ -0,0 +1,282 @@
++// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
++/*
++ * Copyright (c) 2016-2017, 2020 The Linux Foundation. All rights reserved.
++ */
++
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/pwm.h>
++#include <linux/clk.h>
++#include <linux/io.h>
++#include <linux/of.h>
++#include <linux/math64.h>
++#include <linux/of_device.h>
++#include <linux/bitfield.h>
++#include <linux/units.h>
++
++/* The frequency range supported is 1 Hz to clock rate */
++#define IPQ_PWM_MAX_PERIOD_NS ((u64)NSEC_PER_SEC)
++
++/*
++ * The max value specified for each field is based on the number of bits
++ * in the pwm control register for that field
++ */
++#define IPQ_PWM_MAX_DIV               0xFFFF
++
++/*
++ * Two 32-bit registers for each PWM: REG0, and REG1.
++ * Base offset for PWM #i is at 8 * #i.
++ */
++#define IPQ_PWM_REG0                  0
++#define IPQ_PWM_REG0_PWM_DIV          GENMASK(15, 0)
++#define IPQ_PWM_REG0_HI_DURATION      GENMASK(31, 16)
++
++#define IPQ_PWM_REG1                  4
++#define IPQ_PWM_REG1_PRE_DIV          GENMASK(15, 0)
++/*
++ * Enable bit is set to enable output toggling in pwm device.
++ * Update bit is set to reflect the changed divider and high duration
++ * values in register.
++ */
++#define IPQ_PWM_REG1_UPDATE           BIT(30)
++#define IPQ_PWM_REG1_ENABLE           BIT(31)
++
++struct ipq_pwm_chip {
++      struct pwm_chip chip;
++      struct clk *clk;
++      void __iomem *mem;
++};
++
++static struct ipq_pwm_chip *ipq_pwm_from_chip(struct pwm_chip *chip)
++{
++      return container_of(chip, struct ipq_pwm_chip, chip);
++}
++
++static unsigned int ipq_pwm_reg_read(struct pwm_device *pwm, unsigned int reg)
++{
++      struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(pwm->chip);
++      unsigned int off = 8 * pwm->hwpwm + reg;
++
++      return readl(ipq_chip->mem + off);
++}
++
++static void ipq_pwm_reg_write(struct pwm_device *pwm, unsigned int reg,
++                            unsigned int val)
++{
++      struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(pwm->chip);
++      unsigned int off = 8 * pwm->hwpwm + reg;
++
++      writel(val, ipq_chip->mem + off);
++}
++
++static void config_div_and_duty(struct pwm_device *pwm, unsigned int pre_div,
++                              unsigned int pwm_div, unsigned long rate, u64 duty_ns,
++                              bool enable)
++{
++      unsigned long hi_dur;
++      unsigned long val = 0;
++
++      /*
++       * high duration = pwm duty * (pwm div + 1)
++       * pwm duty = duty_ns / period_ns
++       */
++      hi_dur = div64_u64(duty_ns * rate, (pre_div + 1) * NSEC_PER_SEC);
++
++      val = FIELD_PREP(IPQ_PWM_REG0_HI_DURATION, hi_dur) |
++              FIELD_PREP(IPQ_PWM_REG0_PWM_DIV, pwm_div);
++      ipq_pwm_reg_write(pwm, IPQ_PWM_REG0, val);
++
++      val = FIELD_PREP(IPQ_PWM_REG1_PRE_DIV, pre_div);
++      ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val);
++
++      /* PWM enable toggle needs a separate write to REG1 */
++      val |= IPQ_PWM_REG1_UPDATE;
++      if (enable)
++              val |= IPQ_PWM_REG1_ENABLE;
++      ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val);
++}
++
++static int ipq_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
++                       const struct pwm_state *state)
++{
++      struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip);
++      unsigned int pre_div, pwm_div, best_pre_div, best_pwm_div;
++      unsigned long rate = clk_get_rate(ipq_chip->clk);
++      u64 period_ns, duty_ns, period_rate;
++      u64 min_diff;
++
++      if (state->polarity != PWM_POLARITY_NORMAL)
++              return -EINVAL;
++
++      if (state->period < DIV64_U64_ROUND_UP(NSEC_PER_SEC, rate))
++              return -ERANGE;
++
++      period_ns = min(state->period, IPQ_PWM_MAX_PERIOD_NS);
++      duty_ns = min(state->duty_cycle, period_ns);
++
++      /*
++       * period_ns is 1G or less. As long as rate is less than 16 GHz,
++       * period_rate does not overflow. Make that explicit.
++       */
++      if ((unsigned long long)rate > 16ULL * GIGA)
++              return -EINVAL;
++      period_rate = period_ns * rate;
++      best_pre_div = IPQ_PWM_MAX_DIV;
++      best_pwm_div = IPQ_PWM_MAX_DIV;
++      /*
++       * We don't need to consider pre_div values smaller than
++       *
++       *                              period_rate
++       *  pre_div_min := ------------------------------------
++       *                 NSEC_PER_SEC * (IPQ_PWM_MAX_DIV + 1)
++       *
++       * because pre_div = pre_div_min results in a better
++       * approximation.
++       */
++      pre_div = div64_u64(period_rate,
++                          (u64)NSEC_PER_SEC * (IPQ_PWM_MAX_DIV + 1));
++      min_diff = period_rate;
++
++      for (; pre_div <= IPQ_PWM_MAX_DIV; pre_div++) {
++              u64 remainder;
++
++              pwm_div = div64_u64_rem(period_rate,
++                                      (u64)NSEC_PER_SEC * (pre_div + 1), &remainder);
++              /* pwm_div is unsigned; the check below catches underflow */
++              pwm_div--;
++
++              /*
++               * Swapping values for pre_div and pwm_div produces the same
++               * period length. So we can skip all settings with pre_div >
++               * pwm_div which results in bigger constraints for selecting
++               * the duty_cycle than with the two values swapped.
++               */
++              if (pre_div > pwm_div)
++                      break;
++
++              /*
++               * Make sure we can do 100% duty cycle where
++               * hi_dur == pwm_div + 1
++               */
++              if (pwm_div > IPQ_PWM_MAX_DIV - 1)
++                      continue;
++
++              if (remainder < min_diff) {
++                      best_pre_div = pre_div;
++                      best_pwm_div = pwm_div;
++                      min_diff = remainder;
++
++                      if (min_diff == 0) /* bingo */
++                              break;
++              }
++      }
++
++      /* config divider values for the closest possible frequency */
++      config_div_and_duty(pwm, best_pre_div, best_pwm_div,
++                          rate, duty_ns, state->enabled);
++
++      return 0;
++}
++
++static int ipq_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
++                           struct pwm_state *state)
++{
++      struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip);
++      unsigned long rate = clk_get_rate(ipq_chip->clk);
++      unsigned int pre_div, pwm_div, hi_dur;
++      u64 effective_div, hi_div;
++      u32 reg0, reg1;
++
++      reg0 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG0);
++      reg1 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG1);
++
++      state->polarity = PWM_POLARITY_NORMAL;
++      state->enabled = reg1 & IPQ_PWM_REG1_ENABLE;
++
++      pwm_div = FIELD_GET(IPQ_PWM_REG0_PWM_DIV, reg0);
++      hi_dur = FIELD_GET(IPQ_PWM_REG0_HI_DURATION, reg0);
++      pre_div = FIELD_GET(IPQ_PWM_REG1_PRE_DIV, reg1);
++
++      /* No overflow here, both pre_div and pwm_div <= 0xffff */
++      effective_div = (u64)(pre_div + 1) * (pwm_div + 1);
++      state->period = DIV64_U64_ROUND_UP(effective_div * NSEC_PER_SEC, rate);
++
++      hi_div = hi_dur * (pre_div + 1);
++      state->duty_cycle = DIV64_U64_ROUND_UP(hi_div * NSEC_PER_SEC, rate);
++
++      return 0;
++}
++
++static const struct pwm_ops ipq_pwm_ops = {
++      .apply = ipq_pwm_apply,
++      .get_state = ipq_pwm_get_state,
++      .owner = THIS_MODULE,
++};
++
++static int ipq_pwm_probe(struct platform_device *pdev)
++{
++      struct ipq_pwm_chip *pwm;
++      struct device *dev = &pdev->dev;
++      int ret;
++
++      pwm = devm_kzalloc(dev, sizeof(*pwm), GFP_KERNEL);
++      if (!pwm)
++              return -ENOMEM;
++
++      platform_set_drvdata(pdev, pwm);
++
++      pwm->mem = devm_platform_ioremap_resource(pdev, 0);
++      if (IS_ERR(pwm->mem))
++              return dev_err_probe(dev, PTR_ERR(pwm->mem),
++                              "regs map failed");
++
++      pwm->clk = devm_clk_get(dev, NULL);
++      if (IS_ERR(pwm->clk))
++              return dev_err_probe(dev, PTR_ERR(pwm->clk),
++                              "failed to get clock");
++
++      ret = clk_prepare_enable(pwm->clk);
++      if (ret)
++              return dev_err_probe(dev, ret, "clock enable failed");
++
++      pwm->chip.dev = dev;
++      pwm->chip.ops = &ipq_pwm_ops;
++      pwm->chip.npwm = 4;
++
++      ret = pwmchip_add(&pwm->chip);
++      if (ret < 0) {
++              dev_err_probe(dev, ret, "pwmchip_add() failed\n");
++              clk_disable_unprepare(pwm->clk);
++      }
++
++      return ret;
++}
++
++static int ipq_pwm_remove(struct platform_device *pdev)
++{
++      struct ipq_pwm_chip *pwm = platform_get_drvdata(pdev);
++
++      pwmchip_remove(&pwm->chip);
++      clk_disable_unprepare(pwm->clk);
++
++      return 0;
++}
++
++static const struct of_device_id pwm_ipq_dt_match[] = {
++      { .compatible = "qcom,ipq6018-pwm", },
++      {}
++};
++MODULE_DEVICE_TABLE(of, pwm_ipq_dt_match);
++
++static struct platform_driver ipq_pwm_driver = {
++      .driver = {
++              .name = "ipq-pwm",
++              .of_match_table = pwm_ipq_dt_match,
++      },
++      .probe = ipq_pwm_probe,
++      .remove = ipq_pwm_remove,
++};
++
++module_platform_driver(ipq_pwm_driver);
++
++MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/qualcommax/patches-6.6/0142-dt-bindings-mfd-qcom-tcsr-Add-simple-mfd-support-for-IPQ6018.patch b/target/linux/qualcommax/patches-6.6/0142-dt-bindings-mfd-qcom-tcsr-Add-simple-mfd-support-for-IPQ6018.patch
new file mode 100644 (file)
index 0000000..1714fc4
--- /dev/null
@@ -0,0 +1,148 @@
+From: Devi Priya <quic_devipriy@quicinc.com>
+Subject: [PATCH] dt-bindings: mfd: qcom,tcsr: Add simple-mfd support for IPQ6018
+Date: Thu, 5 Oct 2023 21:35:49 +0530
+
+Update the binding to include pwm as the child node to TCSR block and
+add simple-mfd support for IPQ6018.
+
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Signed-off-by: Devi Priya <quic_devipriy@quicinc.com>
+---
+ .../devicetree/bindings/mfd/qcom,tcsr.yaml    | 112 +++++++++++++-----
+ 1 file changed, 81 insertions(+), 31 deletions(-)
+
+--- a/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml
++++ b/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml
+@@ -15,49 +15,101 @@ description:
+ properties:
+   compatible:
+-    items:
+-      - enum:
+-          - qcom,msm8976-tcsr
+-          - qcom,msm8998-tcsr
+-          - qcom,qcs404-tcsr
+-          - qcom,sc7180-tcsr
+-          - qcom,sc7280-tcsr
+-          - qcom,sc8280xp-tcsr
+-          - qcom,sdm630-tcsr
+-          - qcom,sdm845-tcsr
+-          - qcom,sdx55-tcsr
+-          - qcom,sdx65-tcsr
+-          - qcom,sm8150-tcsr
+-          - qcom,sm8450-tcsr
+-          - qcom,tcsr-apq8064
+-          - qcom,tcsr-apq8084
+-          - qcom,tcsr-ipq5332
+-          - qcom,tcsr-ipq6018
+-          - qcom,tcsr-ipq8064
+-          - qcom,tcsr-ipq8074
+-          - qcom,tcsr-ipq9574
+-          - qcom,tcsr-mdm9615
+-          - qcom,tcsr-msm8226
+-          - qcom,tcsr-msm8660
+-          - qcom,tcsr-msm8916
+-          - qcom,tcsr-msm8953
+-          - qcom,tcsr-msm8960
+-          - qcom,tcsr-msm8974
+-          - qcom,tcsr-msm8996
+-      - const: syscon
++    oneOf:
++      - items:
++          - enum:
++              - qcom,msm8976-tcsr
++              - qcom,msm8998-tcsr
++              - qcom,qcs404-tcsr
++              - qcom,sc7180-tcsr
++              - qcom,sc7280-tcsr
++              - qcom,sc8280xp-tcsr
++              - qcom,sdm630-tcsr
++              - qcom,sdm845-tcsr
++              - qcom,sdx55-tcsr
++              - qcom,sdx65-tcsr
++              - qcom,sm4450-tcsr
++              - qcom,sm8150-tcsr
++              - qcom,sm8450-tcsr
++              - qcom,tcsr-apq8064
++              - qcom,tcsr-apq8084
++              - qcom,tcsr-ipq5332
++              - qcom,tcsr-ipq8064
++              - qcom,tcsr-ipq8074
++              - qcom,tcsr-ipq9574
++              - qcom,tcsr-mdm9615
++              - qcom,tcsr-msm8226
++              - qcom,tcsr-msm8660
++              - qcom,tcsr-msm8916
++              - qcom,tcsr-msm8953
++              - qcom,tcsr-msm8960
++              - qcom,tcsr-msm8974
++              - qcom,tcsr-msm8996
++          - const: syscon
++      - items:
++          - const: qcom,tcsr-ipq6018
++          - const: syscon
++          - const: simple-mfd
+   reg:
+     maxItems: 1
++  ranges: true
++
++  "#address-cells":
++    const: 1
++
++  "#size-cells":
++    const: 1
++
++patternProperties:
++  "pwm@[a-f0-9]+$":
++    type: object
++    $ref: /schemas/pwm/qcom,ipq6018-pwm.yaml
++
++
+ required:
+   - compatible
+   - reg
++allOf:
++  - if:
++      not:
++        properties:
++          compatible:
++            contains:
++              enum:
++                - qcom,tcsr-ipq6018
++    then:
++      patternProperties:
++        "pwm@[a-f0-9]+$": false
++
+ additionalProperties: false
+ examples:
++  # Example 1 - Syscon node found on MSM8960
+   - |
+     syscon@1a400000 {
+         compatible = "qcom,tcsr-msm8960", "syscon";
+         reg = <0x1a400000 0x100>;
+     };
++  # Example 2 - Syscon node found on IPQ6018
++  - |
++    #include <dt-bindings/clock/qcom,gcc-ipq6018.h>
++
++    syscon@1937000 {
++        compatible = "qcom,tcsr-ipq6018", "syscon", "simple-mfd";
++        reg = <0x01937000 0x21000>;
++        ranges = <0 0x1937000 0x21000>;
++        #address-cells = <1>;
++        #size-cells = <1>;
++
++        pwm: pwm@a010 {
++            compatible = "qcom,ipq6018-pwm";
++            reg = <0xa010 0x20>;
++            clocks = <&gcc GCC_ADSS_PWM_CLK>;
++            assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>;
++            assigned-clock-rates = <100000000>;
++            #pwm-cells = <2>;
++        };
++    };
+\ No newline at end of file
diff --git a/target/linux/qualcommax/patches-6.6/0150-dt-bindings-nvmem-add-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0150-dt-bindings-nvmem-add-IPQ5018-compatible.patch
new file mode 100644 (file)
index 0000000..c31b2ba
--- /dev/null
@@ -0,0 +1,22 @@
+From: Sricharan Ramabadhran <quic_srichara@quicinc.com>
+Subject: [PATCH V2 1/1] dt-bindings: nvmem: Add compatible for IPQ5018
+Date: Fri, 15 Sep 2023 17:31:20 +0530
+
+Document the QFPROM on IPQ5018.
+
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
+---
+ Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml
++++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml
+@@ -18,6 +18,7 @@ properties:
+       - enum:
+           - qcom,apq8064-qfprom
+           - qcom,apq8084-qfprom
++          - qcom,ipq5018-qfprom
+           - qcom,ipq5332-qfprom
+           - qcom,ipq6018-qfprom
+           - qcom,ipq8064-qfprom
diff --git a/target/linux/qualcommax/patches-6.6/0151-dt-bindings-thermal-qcom-tsens-Add-ipq5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0151-dt-bindings-thermal-qcom-tsens-Add-ipq5018-compatible.patch
new file mode 100644 (file)
index 0000000..36ab4ab
--- /dev/null
@@ -0,0 +1,26 @@
+From: Sricharan Ramabadhran <quic_srichara@quicinc.com>
+Date: Fri, 22 Sep 2023 17:21:13 +0530
+Subject: [PATCH] dt-bindings: thermal: qcom-tsens: Add ipq5018 compatible
+
+IPQ5018 has tsens v1.0 block with 4 sensors and 1 interrupt.
+
+Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
+---
+--- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
++++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
+@@ -39,6 +39,7 @@ properties:
+       - description: v1 of TSENS
+         items:
+           - enum:
++              - qcom,ipq5018-tsens
+               - qcom,msm8956-tsens
+               - qcom,msm8976-tsens
+               - qcom,qcs404-tsens
+@@ -232,6 +233,7 @@ allOf:
+         compatible:
+           contains:
+             enum:
++              - qcom,ipq5018-tsens
+               - qcom,ipq8064-tsens
+               - qcom,msm8960-tsens
+               - qcom,tsens-v0_1
diff --git a/target/linux/qualcommax/patches-6.6/0152-thermal-qcom-add-new-feat-for-soc-without-rpm.patch b/target/linux/qualcommax/patches-6.6/0152-thermal-qcom-add-new-feat-for-soc-without-rpm.patch
new file mode 100644 (file)
index 0000000..c8f393e
--- /dev/null
@@ -0,0 +1,45 @@
+From: Sricharan Ramabadhran <quic_srichara@quicinc.com>
+Subject: [PATCH] thermal/drivers/qcom: Add new feat for soc without rpm
+Date: Fri, 22 Sep 2023 17:21:14 +0530
+
+In IPQ5018, Tsens IP doesn't have RPM. Hence the early init to
+enable tsens would not be done. So add a flag for that in feat
+and skip enable checks. Without this, tsens probe fails.
+
+Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
+---
+ drivers/thermal/qcom/tsens.c | 2 +-
+ drivers/thermal/qcom/tsens.h | 3 +++
+ 2 files changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/thermal/qcom/tsens.c
++++ b/drivers/thermal/qcom/tsens.c
+@@ -974,7 +974,7 @@ int __init init_common(struct tsens_priv
+       ret = regmap_field_read(priv->rf[TSENS_EN], &enabled);
+       if (ret)
+               goto err_put_device;
+-      if (!enabled) {
++      if (!enabled && !(priv->feat->ignore_enable)) {
+               dev_err(dev, "%s: device not enabled\n", __func__);
+               ret = -ENODEV;
+               goto err_put_device;
+--- a/drivers/thermal/qcom/tsens.h
++++ b/drivers/thermal/qcom/tsens.h
+@@ -505,6 +505,8 @@ enum regfield_ids {
+  * @srot_split: does the IP neatly splits the register space into SROT and TM,
+  *              with SROT only being available to secure boot firmware?
+  * @has_watchdog: does this IP support watchdog functionality?
++ * @ignore_enable: does this IP reside in a soc that does not have rpm to
++ *                 do pre-init.
+  * @max_sensors: maximum sensors supported by this version of the IP
+  * @trip_min_temp: minimum trip temperature supported by this version of the IP
+  * @trip_max_temp: maximum trip temperature supported by this version of the IP
+@@ -516,6 +518,7 @@ struct tsens_features {
+       unsigned int adc:1;
+       unsigned int srot_split:1;
+       unsigned int has_watchdog:1;
++      unsigned int ignore_enable:1;
+       unsigned int max_sensors;
+       int trip_min_temp;
+       int trip_max_temp;
diff --git a/target/linux/qualcommax/patches-6.6/0153-thermal-qcom-tsens-add-support-for-IPQ5018-tsens.patch b/target/linux/qualcommax/patches-6.6/0153-thermal-qcom-tsens-add-support-for-IPQ5018-tsens.patch
new file mode 100644 (file)
index 0000000..0de11da
--- /dev/null
@@ -0,0 +1,118 @@
+From: Sricharan Ramabadhran <quic_srichara@quicinc.com>
+Subject: [PATCH] thermal/drivers/tsens: Add support for IPQ5018 tsens
+Date: Fri, 22 Sep 2023 17:21:15 +0530
+
+IPQ5018 has tsens IP V1.0, 4 sensors and 1 interrupt.
+The soc does not have a RPM, hence tsens has to be reset and
+enabled in the driver init. Adding the driver support for same.
+
+Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
+---
+ drivers/thermal/qcom/tsens-v1.c | 60 +++++++++++++++++++++++++++++++++
+ drivers/thermal/qcom/tsens.c    |  3 ++
+ drivers/thermal/qcom/tsens.h    |  2 +-
+ 3 files changed, 64 insertions(+), 1 deletion(-)
+
+--- a/drivers/thermal/qcom/tsens-v1.c
++++ b/drivers/thermal/qcom/tsens-v1.c
+@@ -79,6 +79,18 @@ static struct tsens_features tsens_v1_fe
+       .trip_max_temp  = 120000,
+ };
++static struct tsens_features tsens_v1_ipq5018_feat = {
++      .ver_major      = VER_1_X,
++      .crit_int       = 0,
++      .combo_int      = 0,
++      .adc            = 1,
++      .srot_split     = 1,
++      .max_sensors    = 11,
++      .trip_min_temp  = -40000,
++      .trip_max_temp  = 120000,
++      .ignore_enable  = 1,
++};
++
+ static const struct reg_field tsens_v1_regfields[MAX_REGFIELDS] = {
+       /* ----- SROT ------ */
+       /* VERSION */
+@@ -150,6 +162,41 @@ static int __init init_8956(struct tsens
+       return init_common(priv);
+ }
++static int __init init_ipq5018(struct tsens_priv *priv)
++{
++      int ret;
++      u32 mask;
++
++      ret = init_common(priv);
++      if (ret < 0) {
++              dev_err(priv->dev, "Init common failed %d\n", ret);
++              return ret;
++      }
++
++      ret = regmap_field_write(priv->rf[TSENS_SW_RST], 1);
++      if (ret) {
++              dev_err(priv->dev, "Reset failed\n");
++              return ret;
++      }
++
++      mask = GENMASK(priv->num_sensors, 0);
++      ret = regmap_field_update_bits(priv->rf[SENSOR_EN], mask, mask);
++      if (ret) {
++              dev_err(priv->dev, "Sensor Enable failed\n");
++              return ret;
++      }
++
++      ret = regmap_field_write(priv->rf[TSENS_EN], 1);
++      if (ret) {
++              dev_err(priv->dev, "Enable failed\n");
++              return ret;
++      }
++
++      ret = regmap_field_write(priv->rf[TSENS_SW_RST], 0);
++
++      return ret;
++}
++
+ static const struct tsens_ops ops_generic_v1 = {
+       .init           = init_common,
+       .calibrate      = calibrate_v1,
+@@ -194,3 +241,16 @@ struct tsens_plat_data data_8976 = {
+       .feat           = &tsens_v1_feat,
+       .fields         = tsens_v1_regfields,
+ };
++
++const struct tsens_ops ops_ipq5018 = {
++      .init           = init_ipq5018,
++      .calibrate      = tsens_calibrate_common,
++      .get_temp       = get_temp_tsens_valid,
++};
++
++struct tsens_plat_data data_ipq5018 = {
++      .num_sensors    = 5,
++      .ops            = &ops_ipq5018,
++      .feat           = &tsens_v1_ipq5018_feat,
++      .fields         = tsens_v1_regfields,
++};
+--- a/drivers/thermal/qcom/tsens.c
++++ b/drivers/thermal/qcom/tsens.c
+@@ -1101,6 +1101,9 @@ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, t
+ static const struct of_device_id tsens_table[] = {
+       {
++              .compatible = "qcom,ipq5018-tsens",
++              .data = &data_ipq5018,
++      }, {
+               .compatible = "qcom,ipq8064-tsens",
+               .data = &data_8960,
+       }, {
+--- a/drivers/thermal/qcom/tsens.h
++++ b/drivers/thermal/qcom/tsens.h
+@@ -645,7 +645,7 @@ extern struct tsens_plat_data data_8960;
+ extern struct tsens_plat_data data_8226, data_8909, data_8916, data_8939, data_8974, data_9607;
+ /* TSENS v1 targets */
+-extern struct tsens_plat_data data_tsens_v1, data_8937, data_8976, data_8956;
++extern struct tsens_plat_data data_tsens_v1, data_8937, data_8976, data_8956, data_ipq5018;
+ /* TSENS v2 targets */
+ extern struct tsens_plat_data data_8996, data_ipq8074, data_tsens_v2;
diff --git a/target/linux/qualcommax/patches-6.6/0154-dts-qcom-IPQ5018-add-tsens-node.patch b/target/linux/qualcommax/patches-6.6/0154-dts-qcom-IPQ5018-add-tsens-node.patch
new file mode 100644 (file)
index 0000000..4f9b8ab
--- /dev/null
@@ -0,0 +1,200 @@
+From: Sricharan Ramabadhran <quic_srichara@quicinc.com>
+Subject: [PATCH] arm64: dts: qcom: ipq5018: Add tsens node
+Date: Fri, 22 Sep 2023 17:21:16 +0530
+
+IPQ5018 has tsens V1.0 IP with 4 sensors.
+There is no RPM, so tsens has to be manually enabled. Adding the tsens
+and nvmem node and IPQ5018 has 4 thermal sensors (zones). With the
+critical temperature being 120'C and action is to reboot. Adding all
+the 4 zones here.
+
+Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
+---
+ arch/arm64/boot/dts/qcom/ipq5018.dtsi | 169 ++++++++++++++++++++++++++
+ 1 file changed, 169 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -149,6 +149,117 @@
+                       status = "disabled";
+               };
++              qfprom: qfprom@a0000 {
++                      compatible = "qcom,ipq5018-qfprom", "qcom,qfprom";
++                      reg = <0xa0000 0x1000>;
++                      #address-cells = <1>;
++                      #size-cells = <1>;
++
++                      tsens_mode: mode@249 {
++                              reg = <0x249 1>;
++                              bits = <0 3>;
++                      };
++
++                      tsens_base1: base1@249 {
++                              reg = <0x249 2>;
++                              bits = <3 8>;
++                      };
++
++                      tsens_base2: base2@24a {
++                              reg = <0x24a 2>;
++                              bits = <3 8>;
++                      };
++
++                      tsens_s0_p1: s0-p1@24b {
++                              reg = <0x24b 0x2>;
++                              bits = <2 6>;
++                      };
++
++                      tsens_s0_p2: s0-p2@24c {
++                              reg = <0x24c 0x1>;
++                              bits = <1 6>;
++                      };
++
++                      tsens_s1_p1: s1-p1@24c {
++                              reg = <0x24c 0x2>;
++                              bits = <7 6>;
++                      };
++
++                      tsens_s1_p2: s1-p2@24d {
++                              reg = <0x24d 0x2>;
++                              bits = <5 6>;
++                      };
++
++                      tsens_s2_p1: s2-p1@24e {
++                              reg = <0x24e 0x2>;
++                              bits = <3 6>;
++                      };
++
++                      tsens_s2_p2: s2-p2@24f {
++                              reg = <0x24f 0x1>;
++                              bits = <1 6>;
++                      };
++
++                      tsens_s3_p1: s3-p1@24f {
++                              reg = <0x24f 0x2>;
++                              bits = <7 6>;
++                      };
++
++                      tsens_s3_p2: s3-p2@250 {
++                              reg = <0x250 0x2>;
++                              bits = <5 6>;
++                      };
++
++                      tsens_s4_p1: s4-p1@251 {
++                              reg = <0x251 0x2>;
++                              bits = <3 6>;
++                      };
++
++                      tsens_s4_p2: s4-p2@254 {
++                              reg = <0x254 0x1>;
++                              bits = <0 6>;
++                      };
++              };
++
++              tsens: thermal-sensor@4a9000 {
++                      compatible = "qcom,ipq5018-tsens";
++                      reg = <0x4a9000 0x1000>, /* TM */
++                            <0x4a8000 0x1000>; /* SROT */
++
++                      nvmem-cells = <&tsens_mode>,
++                                    <&tsens_base1>,
++                                    <&tsens_base2>,
++                                    <&tsens_s0_p1>,
++                                    <&tsens_s0_p2>,
++                                    <&tsens_s1_p1>,
++                                    <&tsens_s1_p2>,
++                                    <&tsens_s2_p1>,
++                                    <&tsens_s2_p2>,
++                                    <&tsens_s3_p1>,
++                                    <&tsens_s3_p2>,
++                                    <&tsens_s4_p1>,
++                                    <&tsens_s4_p2>;
++
++                      nvmem-cell-names = "mode",
++                                         "base1",
++                                         "base2",
++                                         "s0_p1",
++                                         "s0_p2",
++                                         "s1_p1",
++                                         "s1_p2",
++                                         "s2_p1",
++                                         "s2_p2",
++                                         "s3_p1",
++                                         "s3_p2",
++                                         "s4_p1",
++                                         "s4_p2";
++
++                      interrupts = <GIC_SPI 184 IRQ_TYPE_EDGE_RISING>;
++                      interrupt-names = "uplow";
++                      #qcom,sensors = <5>;
++                      #thermal-sensor-cells = <1>;
++              };
++
+               tlmm: pinctrl@1000000 {
+                       compatible = "qcom,ipq5018-tlmm";
+                       reg = <0x01000000 0x300000>;
+@@ -391,6 +502,64 @@
+                       };
+               };
+       };
++
++      thermal-zones {
++              cpu-thermal {
++                      polling-delay-passive = <0>;
++                      polling-delay = <0>;
++                      thermal-sensors = <&tsens 2>;
++
++                      trips {
++                              cpu-critical {
++                                      temperature = <120000>;
++                                      hysteresis = <2>;
++                                      type = "critical";
++                              };
++                      };
++              };
++
++              gephy-thermal {
++                      polling-delay-passive = <0>;
++                      polling-delay = <0>;
++                      thermal-sensors = <&tsens 4>;
++
++                      trips {
++                              gephy-critical {
++                                      temperature = <120000>;
++                                      hysteresis = <2>;
++                                      type = "critical";
++                              };
++                      };
++              };
++
++              top-glue-thermal {
++                      polling-delay-passive = <0>;
++                      polling-delay = <0>;
++                      thermal-sensors = <&tsens 3>;
++
++                      trips {
++                              top_glue-critical {
++                                      temperature = <120000>;
++                                      hysteresis = <2>;
++                                      type = "critical";
++                              };
++                      };
++              };
++
++              ubi32-thermal {
++                      polling-delay-passive = <0>;
++                      polling-delay = <0>;
++                      thermal-sensors = <&tsens 1>;
++
++                      trips {
++                              ubi32-critical {
++                                      temperature = <120000>;
++                                      hysteresis = <2>;
++                                      type = "critical";
++                              };
++                      };
++              };
++      };
+       timer {
+               compatible = "arm,armv8-timer";
diff --git a/target/linux/qualcommax/patches-6.6/0155-dt-bindings-phy-qcom-uniphy-pcie-Document-PCIe-uniphy.patch b/target/linux/qualcommax/patches-6.6/0155-dt-bindings-phy-qcom-uniphy-pcie-Document-PCIe-uniphy.patch
new file mode 100644 (file)
index 0000000..7997976
--- /dev/null
@@ -0,0 +1,85 @@
+From: Varadarajan Narayanan <quic_varada@quicinc.com>
+Date: Thu, 2 Jan 2025 17:00:15 +0530
+Subject: [PATCH] dt-bindings: phy: qcom,uniphy-pcie: Document PCIe uniphy
+
+From: Nitheesh Sekar <quic_nsekar@quicinc.com>
+
+Document the Qualcomm UNIPHY PCIe 28LP present in IPQ5332.
+
+Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
+Signed-off-by: Varadarajan Narayanan <quic_varada@quicinc.com>
+---
+--- /dev/null
++++ b/Documentation/devicetree/bindings/phy/qcom,ipq5332-uniphy-pcie-phy.yaml
+@@ -0,0 +1,71 @@
++# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/phy/qcom,ipq5332-uniphy-pcie-phy.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Qualcomm UNIPHY PCIe 28LP PHY
++
++maintainers:
++  - Nitheesh Sekar <quic_nsekar@quicinc.com>
++  - Varadarajan Narayanan <quic_varada@quicinc.com>
++
++description:
++  PCIe and USB combo PHY found in Qualcomm IPQ5332 SoC
++
++properties:
++  compatible:
++    enum:
++      - qcom,ipq5332-uniphy-pcie-phy
++
++  reg:
++    maxItems: 1
++
++  clocks:
++    items:
++      - description: pcie pipe clock
++      - description: pcie ahb clock
++
++  resets:
++    items:
++      - description: phy reset
++      - description: ahb reset
++      - description: cfg reset
++
++  "#phy-cells":
++    const: 0
++
++  "#clock-cells":
++    const: 0
++
++  num-lanes: true
++
++required:
++  - compatible
++  - reg
++  - clocks
++  - resets
++  - "#phy-cells"
++  - "#clock-cells"
++
++additionalProperties: false
++
++examples:
++  - |
++    #include <dt-bindings/clock/qcom,ipq5332-gcc.h>
++
++    pcie0_phy: phy@4b0000 {
++        compatible = "qcom,ipq5332-uniphy-pcie-phy";
++        reg = <0x004b0000 0x800>;
++
++        clocks = <&gcc GCC_PCIE3X1_0_PIPE_CLK>,
++                 <&gcc GCC_PCIE3X1_PHY_AHB_CLK>;
++
++        resets = <&gcc GCC_PCIE3X1_0_PHY_BCR>,
++                 <&gcc GCC_PCIE3X1_PHY_AHB_CLK_ARES>,
++                 <&gcc GCC_PCIE3X1_0_PHY_PHY_BCR>;
++
++        #clock-cells = <0>;
++
++        #phy-cells = <0>;
++    };
diff --git a/target/linux/qualcommax/patches-6.6/0156-phy-qcom-Introduce-PCIe-UNIPHY-28LP-driver.patch b/target/linux/qualcommax/patches-6.6/0156-phy-qcom-Introduce-PCIe-UNIPHY-28LP-driver.patch
new file mode 100644 (file)
index 0000000..f30db14
--- /dev/null
@@ -0,0 +1,332 @@
+From: Varadarajan Narayanan <quic_varada@quicinc.com>
+Date: Thu, 2 Jan 2025 17:00:16 +0530
+Subject: [PATCH] phy: qcom: Introduce PCIe UNIPHY 28LP driver
+
+From: Nitheesh Sekar <quic_nsekar@quicinc.com>
+
+Add Qualcomm PCIe UNIPHY 28LP driver support present
+in Qualcomm IPQ5332 SoC and the phy init sequence.
+
+Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
+Signed-off-by: Varadarajan Narayanan <quic_varada@quicinc.com>
+---
+--- a/drivers/phy/qualcomm/Kconfig
++++ b/drivers/phy/qualcomm/Kconfig
+@@ -154,6 +154,18 @@ config PHY_QCOM_M31_USB
+         management. This driver is required even for peripheral only or
+         host only mode configurations.
++config PHY_QCOM_UNIPHY_PCIE_28LP
++      bool "PCIE UNIPHY 28LP PHY driver"
++      depends on ARCH_QCOM
++      depends on HAS_IOMEM
++      depends on OF
++      select GENERIC_PHY
++      help
++        Enable this to support the PCIe UNIPHY 28LP phy transceiver that
++        is used with PCIe controllers on Qualcomm IPQ5332 chips. It
++        handles PHY initialization, clock management required after
++        resetting the hardware and power management.
++
+ config PHY_QCOM_USB_HS
+       tristate "Qualcomm USB HS PHY module"
+       depends on USB_ULPI_BUS
+--- a/drivers/phy/qualcomm/Makefile
++++ b/drivers/phy/qualcomm/Makefile
+@@ -17,6 +17,7 @@ obj-$(CONFIG_PHY_QCOM_QMP_USB_LEGACY)        +=
+ obj-$(CONFIG_PHY_QCOM_QUSB2)          += phy-qcom-qusb2.o
+ obj-$(CONFIG_PHY_QCOM_SNPS_EUSB2)     += phy-qcom-snps-eusb2.o
+ obj-$(CONFIG_PHY_QCOM_EUSB2_REPEATER) += phy-qcom-eusb2-repeater.o
++obj-$(CONFIG_PHY_QCOM_UNIPHY_PCIE_28LP)       += phy-qcom-uniphy-pcie-28lp.o
+ obj-$(CONFIG_PHY_QCOM_USB_HS)                 += phy-qcom-usb-hs.o
+ obj-$(CONFIG_PHY_QCOM_USB_HSIC)       += phy-qcom-usb-hsic.o
+ obj-$(CONFIG_PHY_QCOM_USB_HS_28NM)    += phy-qcom-usb-hs-28nm.o
+--- /dev/null
++++ b/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c
+@@ -0,0 +1,285 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (c) 2025, The Linux Foundation. All rights reserved.
++ */
++
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/mfd/syscon.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/of.h>
++#include <linux/phy/phy.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/reset.h>
++
++#define RST_ASSERT_DELAY_MIN_US               100
++#define RST_ASSERT_DELAY_MAX_US               150
++#define PIPE_CLK_DELAY_MIN_US         5000
++#define PIPE_CLK_DELAY_MAX_US         5100
++#define CLK_EN_DELAY_MIN_US           30
++#define CLK_EN_DELAY_MAX_US           50
++#define CDR_CTRL_REG_1                0x80
++#define CDR_CTRL_REG_2                0x84
++#define CDR_CTRL_REG_3                0x88
++#define CDR_CTRL_REG_4                0x8c
++#define CDR_CTRL_REG_5                0x90
++#define CDR_CTRL_REG_6                0x94
++#define CDR_CTRL_REG_7                0x98
++#define SSCG_CTRL_REG_1               0x9c
++#define SSCG_CTRL_REG_2               0xa0
++#define SSCG_CTRL_REG_3               0xa4
++#define SSCG_CTRL_REG_4               0xa8
++#define SSCG_CTRL_REG_5               0xac
++#define SSCG_CTRL_REG_6               0xb0
++#define PCS_INTERNAL_CONTROL_2        0x2d8
++
++#define PHY_CFG_PLLCFG                                0x220
++#define PHY_CFG_EIOS_DTCT_REG                 0x3e4
++#define PHY_CFG_GEN3_ALIGN_HOLDOFF_TIME               0x3e8
++
++#define PHY_MODE_FIXED                0x1
++
++enum qcom_uniphy_pcie_type {
++      PHY_TYPE_PCIE = 1,
++      PHY_TYPE_PCIE_GEN2,
++      PHY_TYPE_PCIE_GEN3,
++};
++
++struct qcom_uniphy_pcie_regs {
++      u32 offset;
++      u32 val;
++};
++
++struct qcom_uniphy_pcie_data {
++      int lane_offset; /* offset between the lane register bases */
++      u32 phy_type;
++      const struct qcom_uniphy_pcie_regs *init_seq;
++      u32 init_seq_num;
++      u32 pipe_clk_rate;
++};
++
++struct qcom_uniphy_pcie {
++      struct phy phy;
++      struct device *dev;
++      const struct qcom_uniphy_pcie_data *data;
++      struct clk_bulk_data *clks;
++      int num_clks;
++      struct reset_control *resets;
++      void __iomem *base;
++      int lanes;
++};
++
++#define phy_to_dw_phy(x)      container_of((x), struct qca_uni_pcie_phy, phy)
++
++static const struct qcom_uniphy_pcie_regs ipq5332_regs[] = {
++      {
++              .offset = PHY_CFG_PLLCFG,
++              .val = 0x30,
++      }, {
++              .offset = PHY_CFG_EIOS_DTCT_REG,
++              .val = 0x53ef,
++      }, {
++              .offset = PHY_CFG_GEN3_ALIGN_HOLDOFF_TIME,
++              .val = 0xcf,
++      },
++};
++
++static const struct qcom_uniphy_pcie_data ipq5332_data = {
++      .lane_offset    = 0x800,
++      .phy_type       = PHY_TYPE_PCIE_GEN3,
++      .init_seq       = ipq5332_regs,
++      .init_seq_num   = ARRAY_SIZE(ipq5332_regs),
++      .pipe_clk_rate  = 250000000,
++};
++
++static void qcom_uniphy_pcie_init(struct qcom_uniphy_pcie *phy)
++{
++      const struct qcom_uniphy_pcie_data *data = phy->data;
++      const struct qcom_uniphy_pcie_regs *init_seq;
++      void __iomem *base = phy->base;
++      int lane, i;
++
++      for (lane = 0; lane < phy->lanes; lane++) {
++              init_seq = data->init_seq;
++
++              for (i = 0; i < data->init_seq_num; i++)
++                      writel(init_seq[i].val, base + init_seq[i].offset);
++
++              base += data->lane_offset;
++      }
++}
++
++static int qcom_uniphy_pcie_power_off(struct phy *x)
++{
++      struct qcom_uniphy_pcie *phy = phy_get_drvdata(x);
++
++      clk_bulk_disable_unprepare(phy->num_clks, phy->clks);
++
++      return reset_control_assert(phy->resets);
++}
++
++static int qcom_uniphy_pcie_power_on(struct phy *x)
++{
++      struct qcom_uniphy_pcie *phy = phy_get_drvdata(x);
++      int ret;
++
++      ret = reset_control_assert(phy->resets);
++      if (ret) {
++              dev_err(phy->dev, "reset assert failed (%d)\n", ret);
++              return ret;
++      }
++
++      usleep_range(RST_ASSERT_DELAY_MIN_US, RST_ASSERT_DELAY_MAX_US);
++
++      ret = reset_control_deassert(phy->resets);
++      if (ret) {
++              dev_err(phy->dev, "reset deassert failed (%d)\n", ret);
++              return ret;
++      }
++
++      usleep_range(PIPE_CLK_DELAY_MIN_US, PIPE_CLK_DELAY_MAX_US);
++
++      ret = clk_bulk_prepare_enable(phy->num_clks, phy->clks);
++      if (ret) {
++              dev_err(phy->dev, "clk prepare and enable failed %d\n", ret);
++              return ret;
++      }
++
++      usleep_range(CLK_EN_DELAY_MIN_US, CLK_EN_DELAY_MAX_US);
++
++      qcom_uniphy_pcie_init(phy);
++      return 0;
++}
++
++static inline int qcom_uniphy_pcie_get_resources(struct platform_device *pdev,
++                                               struct qcom_uniphy_pcie *phy)
++{
++      struct resource *res;
++
++      phy->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
++      if (IS_ERR(phy->base))
++              return PTR_ERR(phy->base);
++
++      phy->num_clks = devm_clk_bulk_get_all(phy->dev, &phy->clks);
++      if (phy->num_clks < 0)
++              return phy->num_clks;
++
++      phy->resets = devm_reset_control_array_get_exclusive(phy->dev);
++      if (IS_ERR(phy->resets))
++              return PTR_ERR(phy->resets);
++
++      return 0;
++}
++
++/*
++ * Register a fixed rate pipe clock.
++ *
++ * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate
++ * controls it. The <s>_pipe_clk coming out of the GCC is requested
++ * by the PHY driver for its operations.
++ * We register the <s>_pipe_clksrc here. The gcc driver takes care
++ * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk.
++ * Below picture shows this relationship.
++ *
++ *         +---------------+
++ *         |   PHY block   |<<---------------------------------------+
++ *         |               |                                         |
++ *         |   +-------+   |                   +-----+               |
++ *   I/P---^-->|  PLL  |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+
++ *    clk  |   +-------+   |                   +-----+
++ *         +---------------+
++ */
++static inline int phy_pipe_clk_register(struct qcom_uniphy_pcie *phy, int id)
++{
++      const struct qcom_uniphy_pcie_data *data = phy->data;
++      struct clk_hw *hw;
++      char name[64];
++
++      snprintf(name, sizeof(name), "phy%d_pipe_clk_src", id);
++      hw = devm_clk_hw_register_fixed_rate(phy->dev, name, NULL, 0,
++                                           data->pipe_clk_rate);
++      if (IS_ERR(hw))
++              return dev_err_probe(phy->dev, PTR_ERR(hw),
++                                   "Unable to register %s\n", name);
++
++      return devm_of_clk_add_hw_provider(phy->dev, of_clk_hw_simple_get, hw);
++}
++
++static const struct of_device_id qcom_uniphy_pcie_id_table[] = {
++      {
++              .compatible = "qcom,ipq5332-uniphy-pcie-phy",
++              .data = &ipq5332_data,
++      }, {
++              /* Sentinel */
++      },
++};
++MODULE_DEVICE_TABLE(of, qcom_uniphy_pcie_id_table);
++
++static const struct phy_ops pcie_ops = {
++      .power_on       = qcom_uniphy_pcie_power_on,
++      .power_off      = qcom_uniphy_pcie_power_off,
++      .owner          = THIS_MODULE,
++};
++
++static int qcom_uniphy_pcie_probe(struct platform_device *pdev)
++{
++      struct phy_provider *phy_provider;
++      struct device *dev = &pdev->dev;
++      struct qcom_uniphy_pcie *phy;
++      struct phy *generic_phy;
++      int ret;
++
++      phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
++      if (!phy)
++              return -ENOMEM;
++
++      platform_set_drvdata(pdev, phy);
++      phy->dev = &pdev->dev;
++
++      phy->data = of_device_get_match_data(dev);
++      if (!phy->data)
++              return -EINVAL;
++
++      phy->lanes = 1;
++      ret = of_property_read_u32(dev->of_node, "num-lanes", &phy->lanes);
++
++      ret = qcom_uniphy_pcie_get_resources(pdev, phy);
++      if (ret < 0)
++              return dev_err_probe(&pdev->dev, ret,
++                                   "failed to get resources: %d\n", ret);
++
++      generic_phy = devm_phy_create(phy->dev, NULL, &pcie_ops);
++      if (IS_ERR(generic_phy))
++              return PTR_ERR(generic_phy);
++
++      phy_set_drvdata(generic_phy, phy);
++
++      ret = phy_pipe_clk_register(phy, generic_phy->id);
++      if (ret)
++              dev_err(&pdev->dev, "failed to register phy pipe clk\n");
++
++      phy_provider = devm_of_phy_provider_register(phy->dev,
++                                                   of_phy_simple_xlate);
++      if (IS_ERR(phy_provider))
++              return PTR_ERR(phy_provider);
++
++      return 0;
++}
++
++static struct platform_driver qcom_uniphy_pcie_driver = {
++      .probe          = qcom_uniphy_pcie_probe,
++      .driver         = {
++              .name   = "qcom-uniphy-pcie",
++              .of_match_table = qcom_uniphy_pcie_id_table,
++      },
++};
++
++module_platform_driver(qcom_uniphy_pcie_driver);
++
++MODULE_DESCRIPTION("PCIE QCOM UNIPHY driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/qualcommax/patches-6.6/0157-dt-bindings-phy-qcom-uniphy-pcie-add-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0157-dt-bindings-phy-qcom-uniphy-pcie-add-IPQ5018-compatible.patch
new file mode 100644 (file)
index 0000000..9951daa
--- /dev/null
@@ -0,0 +1,25 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Date: Tue, 07 Jan 2025 17:34:13 +0400
+Subject: [PATCH] phy: qualcomm: qcom-uniphy-pcie add IPQ5018 compatible
+
+The Qualcomm UNIPHY PCIe PHY 28lp part of the IPQ5332 SoC is also present on
+the IPQ5018 SoC, so adding the compatible for IPQ5018.
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/Documentation/devicetree/bindings/phy/qcom,ipq5332-uniphy-pcie-phy.yaml
++++ b/Documentation/devicetree/bindings/phy/qcom,ipq5332-uniphy-pcie-phy.yaml
+@@ -11,11 +11,12 @@ maintainers:
+   - Varadarajan Narayanan <quic_varada@quicinc.com>
+ description:
+-  PCIe and USB combo PHY found in Qualcomm IPQ5332 SoC
++  PCIe and USB combo PHY found in Qualcomm IPQ5018 and IPQ5332 SoCs
+ properties:
+   compatible:
+     enum:
++      - qcom,ipq5018-uniphy-pcie-phy
+       - qcom,ipq5332-uniphy-pcie-phy
+   reg:
diff --git a/target/linux/qualcommax/patches-6.6/0158-phy-qcom-uniphy-pcie-28lp-add-support-for-IPQ5018.patch b/target/linux/qualcommax/patches-6.6/0158-phy-qcom-uniphy-pcie-28lp-add-support-for-IPQ5018.patch
new file mode 100644 (file)
index 0000000..aedb959
--- /dev/null
@@ -0,0 +1,77 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Date: Tue, 07 Jan 2025 17:34:13 +0400
+Subject: [PATCH] phy: qualcomm: qcom-uniphy-pcie 28lp add support for IPQ5018
+
+The Qualcomm UNIPHY PCIe PHY 28lp is found on both IPQ5332 and IPQ5018.
+Adding the PHY init sequence, pipe clock rate, and compatible for IPQ5018.
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c
++++ b/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c
+@@ -76,6 +76,40 @@ struct qcom_uniphy_pcie {
+ #define phy_to_dw_phy(x)      container_of((x), struct qca_uni_pcie_phy, phy)
++static const struct qcom_uniphy_pcie_regs ipq5018_regs[] = {
++      {
++              .offset = SSCG_CTRL_REG_4,
++              .val = 0x1cb9,
++      }, {
++              .offset = SSCG_CTRL_REG_5,
++              .val = 0x023a,
++      }, {
++              .offset = SSCG_CTRL_REG_3,
++              .val = 0xd360,
++      }, {
++              .offset = SSCG_CTRL_REG_1,
++              .val = 0x1,
++      }, {
++              .offset = SSCG_CTRL_REG_2,
++              .val = 0xeb,
++      }, {
++              .offset = CDR_CTRL_REG_4,
++              .val = 0x3f9,
++      }, {
++              .offset = CDR_CTRL_REG_5,
++              .val = 0x1c9,
++      }, {
++              .offset = CDR_CTRL_REG_2,
++              .val = 0x419,
++      }, {
++              .offset = CDR_CTRL_REG_1,
++              .val = 0x200,
++      }, {
++              .offset = PCS_INTERNAL_CONTROL_2,
++              .val = 0xf101,
++      },
++};
++
+ static const struct qcom_uniphy_pcie_regs ipq5332_regs[] = {
+       {
+               .offset = PHY_CFG_PLLCFG,
+@@ -89,6 +123,14 @@ static const struct qcom_uniphy_pcie_reg
+       },
+ };
++static const struct qcom_uniphy_pcie_data ipq5018_data = {
++      .lane_offset    = 0x800,
++      .phy_type       = PHY_TYPE_PCIE_GEN2,
++      .init_seq       = ipq5018_regs,
++      .init_seq_num   = ARRAY_SIZE(ipq5018_regs),
++      .pipe_clk_rate  = 125000000,
++};
++
+ static const struct qcom_uniphy_pcie_data ipq5332_data = {
+       .lane_offset    = 0x800,
+       .phy_type       = PHY_TYPE_PCIE_GEN3,
+@@ -212,6 +254,9 @@ static inline int phy_pipe_clk_register(
+ static const struct of_device_id qcom_uniphy_pcie_id_table[] = {
+       {
++              .compatible = "qcom,ipq5018-uniphy-pcie-phy",
++              .data = &ipq5018_data,
++      }, {
+               .compatible = "qcom,ipq5332-uniphy-pcie-phy",
+               .data = &ipq5332_data,
+       }, {
diff --git a/target/linux/qualcommax/patches-6.6/0160-dt-bindings-PCI-qcom-Add-IPQ5018-SoC.patch b/target/linux/qualcommax/patches-6.6/0160-dt-bindings-PCI-qcom-Add-IPQ5018-SoC.patch
new file mode 100644 (file)
index 0000000..caa9889
--- /dev/null
@@ -0,0 +1,78 @@
+From: Nitheesh Sekar <quic_nsekar@quicinc.com>
+Date: Tue, 3 Oct 2023 17:38:42 +0530
+Subject: [PATCH] dt-bindings: PCI: qcom: Add IPQ5108 SoC
+
+Add support for the PCIe controller on the Qualcomm
+IPQ5108 SoC to the bindings.
+
+Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
+---
+ .../devicetree/bindings/pci/qcom,pcie.yaml    | 36 +++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+
+--- a/Documentation/devicetree/bindings/pci/qcom,pcie.yaml
++++ b/Documentation/devicetree/bindings/pci/qcom,pcie.yaml
+@@ -21,6 +21,7 @@ properties:
+           - qcom,pcie-apq8064
+           - qcom,pcie-apq8084
+           - qcom,pcie-ipq4019
++          - qcom,pcie-ipq5018
+           - qcom,pcie-ipq6018
+           - qcom,pcie-ipq8064
+           - qcom,pcie-ipq8064-v2
+@@ -170,6 +171,7 @@ allOf:
+         compatible:
+           contains:
+             enum:
++              - qcom,pcie-ipq5018
+               - qcom,pcie-ipq6018
+               - qcom,pcie-ipq8074-gen3
+     then:
+@@ -337,6 +339,39 @@ allOf:
+         compatible:
+           contains:
+             enum:
++              - qcom,pcie-ipq5018
++    then:
++      properties:
++        clocks:
++          minItems: 6
++          maxItems: 6
++        clock-names:
++          items:
++            - const: iface # PCIe to SysNOC BIU clock
++            - const: axi_m # AXI Master clock
++            - const: axi_s # AXI Slave clock
++            - const: ahb # AHB clock
++            - const: aux # Auxiliary clock
++            - const: axi_bridge # AXI bridge clock
++        resets:
++          minItems: 8
++          maxItems: 8
++        reset-names:
++          items:
++            - const: pipe # PIPE reset
++            - const: sleep # Sleep reset
++            - const: sticky # Core sticky reset
++            - const: axi_m # AXI master reset
++            - const: axi_s # AXI slave reset
++            - const: ahb # AHB reset
++            - const: axi_m_sticky # AXI master sticky reset
++            - const: axi_s_sticky # AXI slave sticky reset
++
++  - if:
++      properties:
++        compatible:
++          contains:
++            enum:
+               - qcom,pcie-msm8996
+     then:
+       properties:
+@@ -875,6 +910,7 @@ allOf:
+               - qcom,pcie-apq8064
+               - qcom,pcie-apq8084
+               - qcom,pcie-ipq4019
++              - qcom,pcie-ipq5018
+               - qcom,pcie-ipq6018
+               - qcom,pcie-ipq8064
+               - qcom,pcie-ipq8064-v2
diff --git a/target/linux/qualcommax/patches-6.6/0161-PCI-qcom-add-support-for-IPQ5018.patch b/target/linux/qualcommax/patches-6.6/0161-PCI-qcom-add-support-for-IPQ5018.patch
new file mode 100644 (file)
index 0000000..bd8f51b
--- /dev/null
@@ -0,0 +1,77 @@
+From: Nitheesh Sekar <quic_nsekar@quicinc.com>
+Subject: [PATCH] PCI: qcom: Add support for IPQ5018
+Date: Tue, 3 Oct 2023 17:38:44 +0530
+
+Added a new compatible 'qcom,pcie-ipq5018' and modified
+get_resources of 'ops 2_9_0' to get the clocks from the
+device-tree.
+
+Co-developed-by: Anusha Rao <quic_anusha@quicinc.com>
+Signed-off-by: Anusha Rao <quic_anusha@quicinc.com>
+Co-developed-by: Devi Priya <quic_devipriy@quicinc.com>
+Signed-off-by: Devi Priya <quic_devipriy@quicinc.com>
+Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
+---
+ drivers/pci/controller/dwc/pcie-qcom.c | 22 ++++++++--------------
+ 1 file changed, 8 insertions(+), 14 deletions(-)
+
+--- a/drivers/pci/controller/dwc/pcie-qcom.c
++++ b/drivers/pci/controller/dwc/pcie-qcom.c
+@@ -202,8 +202,9 @@ struct qcom_pcie_resources_2_7_0 {
+ #define QCOM_PCIE_2_9_0_MAX_CLOCKS            5
+ struct qcom_pcie_resources_2_9_0 {
+-      struct clk_bulk_data clks[QCOM_PCIE_2_9_0_MAX_CLOCKS];
++      struct clk_bulk_data *clks;
+       struct reset_control *rst;
++      int num_clks;
+ };
+ union qcom_pcie_resources {
+@@ -1073,17 +1074,10 @@ static int qcom_pcie_get_resources_2_9_0
+       struct qcom_pcie_resources_2_9_0 *res = &pcie->res.v2_9_0;
+       struct dw_pcie *pci = pcie->pci;
+       struct device *dev = pci->dev;
+-      int ret;
+-
+-      res->clks[0].id = "iface";
+-      res->clks[1].id = "axi_m";
+-      res->clks[2].id = "axi_s";
+-      res->clks[3].id = "axi_bridge";
+-      res->clks[4].id = "rchng";
+-      ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks);
+-      if (ret < 0)
+-              return ret;
++      res->num_clks = devm_clk_bulk_get_all(dev, &res->clks);
++      if (res->num_clks < 0)
++              return res->num_clks;
+       res->rst = devm_reset_control_array_get_exclusive(dev);
+       if (IS_ERR(res->rst))
+@@ -1096,7 +1090,7 @@ static void qcom_pcie_deinit_2_9_0(struc
+ {
+       struct qcom_pcie_resources_2_9_0 *res = &pcie->res.v2_9_0;
+-      clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks);
++      clk_bulk_disable_unprepare(res->num_clks, res->clks);
+ }
+ static int qcom_pcie_init_2_9_0(struct qcom_pcie *pcie)
+@@ -1125,7 +1119,7 @@ static int qcom_pcie_init_2_9_0(struct q
+       usleep_range(2000, 2500);
+-      return clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks);
++      return clk_bulk_prepare_enable(res->num_clks, res->clks);
+ }
+ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie)
+@@ -1641,6 +1635,7 @@ static const struct of_device_id qcom_pc
+       { .compatible = "qcom,pcie-apq8064", .data = &cfg_2_1_0 },
+       { .compatible = "qcom,pcie-apq8084", .data = &cfg_1_0_0 },
+       { .compatible = "qcom,pcie-ipq4019", .data = &cfg_2_4_0 },
++      { .compatible = "qcom,pcie-ipq5018", .data = &cfg_2_9_0 },
+       { .compatible = "qcom,pcie-ipq6018", .data = &cfg_2_9_0 },
+       { .compatible = "qcom,pcie-ipq8064", .data = &cfg_2_1_0 },
+       { .compatible = "qcom,pcie-ipq8064-v2", .data = &cfg_2_1_0 },
diff --git a/target/linux/qualcommax/patches-6.6/0162-arm64-dts-qcom-IPQ5018-add-PCIe-related-nodes.patch b/target/linux/qualcommax/patches-6.6/0162-arm64-dts-qcom-IPQ5018-add-PCIe-related-nodes.patch
new file mode 100644 (file)
index 0000000..f51d103
--- /dev/null
@@ -0,0 +1,216 @@
+From: Nitheesh Sekar <quic_nsekar@quicinc.com>
+Subject: [PATCH] arm64: dts: qcom: ipq5018: Add PCIe related nodes
+Date: Tue, 3 Oct 2023 17:38:45 +0530
+
+Add phy and controller nodes for PCIe0 and PCIe1.
+PCIe0 is 2-lane Gen2 and PCIe1 is 1-lane Gen2.
+
+Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+ arch/arm64/boot/dts/qcom/ipq5018.dtsi | 186 +++++++++++++++++++++++++-
+ 1 file changed, 184 insertions(+), 2 deletions(-)
+
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -149,6 +149,42 @@
+                       status = "disabled";
+               };
++              pcie1_phy: phy@7e000{
++                      compatible = "qcom,ipq5018-uniphy-pcie-phy";
++                      reg = <0x0007e000 0x800>;
++
++                      clocks = <&gcc GCC_PCIE1_PIPE_CLK>;
++
++                      resets = <&gcc GCC_PCIE1_PHY_BCR>,
++                                       <&gcc GCC_PCIE1PHY_PHY_BCR>;
++
++                      #clock-cells = <0>;
++
++                      #phy-cells = <0>;
++
++                      num-lanes = <1>;
++
++                      status = "disabled";
++              };
++
++              pcie0_phy: phy@86000{
++                      compatible = "qcom,ipq5018-uniphy-pcie-phy";
++                      reg = <0x00086000 0x800>;
++
++                      clocks = <&gcc GCC_PCIE0_PIPE_CLK>;
++
++                      resets = <&gcc GCC_PCIE0_PHY_BCR>,
++                                       <&gcc GCC_PCIE0PHY_PHY_BCR>;
++
++                      #clock-cells = <0>;
++
++                      #phy-cells = <0>;
++
++                      num-lanes = <2>;
++
++                      status = "disabled";
++              };
++
+               qfprom: qfprom@a0000 {
+                       compatible = "qcom,ipq5018-qfprom", "qcom,qfprom";
+                       reg = <0xa0000 0x1000>;
+@@ -283,8 +319,8 @@
+                       reg = <0x01800000 0x80000>;
+                       clocks = <&xo_board_clk>,
+                                <&sleep_clk>,
+-                               <0>,
+-                               <0>,
++                               <&pcie0_phy>,
++                               <&pcie1_phy>,
+                                <0>,
+                                <0>,
+                                <0>,
+@@ -501,6 +537,146 @@
+                               status = "disabled";
+                       };
+               };
++
++              pcie1: pcie@80000000 {
++                      compatible = "qcom,pcie-ipq5018";
++                      reg = <0x80000000 0xf1d>,
++                                <0x80000f20 0xa8>,
++                                <0x80001000 0x1000>,
++                                <0x00078000 0x3000>,
++                                <0x80100000 0x1000>;
++                      reg-names = "dbi",
++                                              "elbi",
++                                              "atu",
++                                              "parf",
++                                              "config";
++                      device_type = "pci";
++                      linux,pci-domain = <0>;
++                      bus-range = <0x00 0xff>;
++                      num-lanes = <1>;
++                      max-link-speed = <2>;
++                      #address-cells = <3>;
++                      #size-cells = <2>;
++
++                      phys = <&pcie1_phy>;
++                      phy-names ="pciephy";
++
++                      ranges = <0x81000000 0 0x80200000 0x80200000 0 0x00100000>,     /* I/O */
++                                       <0x82000000 0 0x80300000 0x80300000 0 0x10000000>;     /* MEM */
++
++                      #interrupt-cells = <1>;
++                      interrupt-map-mask = <0 0 0 0x7>;
++                      interrupt-map = <0 0 0 1 &intc 0 142 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++                                                      <0 0 0 2 &intc 0 143 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++                                                      <0 0 0 3 &intc 0 144 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++                                                      <0 0 0 4 &intc 0 145 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++
++                      interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
++                      interrupt-names = "global_irq";
++
++                      clocks = <&gcc GCC_SYS_NOC_PCIE1_AXI_CLK>,
++                                       <&gcc GCC_PCIE1_AXI_M_CLK>,
++                                       <&gcc GCC_PCIE1_AXI_S_CLK>,
++                                       <&gcc GCC_PCIE1_AHB_CLK>,
++                                       <&gcc GCC_PCIE1_AUX_CLK>,
++                                       <&gcc GCC_PCIE1_AXI_S_BRIDGE_CLK>;
++                      clock-names = "iface",
++                                                "axi_m",
++                                                "axi_s",
++                                                "ahb",
++                                                "aux",
++                                                "axi_bridge";
++
++                      resets = <&gcc GCC_PCIE1_PIPE_ARES>,
++                                       <&gcc GCC_PCIE1_SLEEP_ARES>,
++                                       <&gcc GCC_PCIE1_CORE_STICKY_ARES>,
++                                       <&gcc GCC_PCIE1_AXI_MASTER_ARES>,
++                                       <&gcc GCC_PCIE1_AXI_SLAVE_ARES>,
++                                       <&gcc GCC_PCIE1_AHB_ARES>,
++                                       <&gcc GCC_PCIE1_AXI_MASTER_STICKY_ARES>,
++                                       <&gcc GCC_PCIE1_AXI_SLAVE_STICKY_ARES>;
++                      reset-names = "pipe",
++                                                "sleep",
++                                                "sticky",
++                                                "axi_m",
++                                                "axi_s",
++                                                "ahb",
++                                                "axi_m_sticky",
++                                                "axi_s_sticky";
++
++                      msi-map = <0x0 &v2m0 0x0 0xff8>;
++                      status = "disabled";
++              };
++
++              pcie0: pcie@a0000000 {
++                      compatible = "qcom,pcie-ipq5018";
++                      reg = <0xa0000000 0xf1d>,
++                                <0xa0000f20 0xa8>,
++                                <0xa0001000 0x1000>,
++                                <0x00080000 0x3000>,
++                                <0xa0100000 0x1000>;
++                      reg-names = "dbi",
++                                              "elbi",
++                                              "atu",
++                                              "parf",
++                                              "config";
++                      device_type = "pci";
++                      linux,pci-domain = <1>;
++                      bus-range = <0x00 0xff>;
++                      num-lanes = <2>;
++                      max-link-speed = <2>;
++                      #address-cells = <3>;
++                      #size-cells = <2>;
++
++                      phys = <&pcie0_phy>;
++                      phy-names ="pciephy";
++
++                      ranges = <0x81000000 0 0xa0200000 0xa0200000 0 0x00100000>,     /* I/O */
++                                       <0x82000000 0 0xa0300000 0xa0300000 0 0x10000000>;     /* MEM */
++
++                      #interrupt-cells = <1>;
++                      interrupt-map-mask = <0 0 0 0x7>;
++                      interrupt-map = <0 0 0 1 &intc 0 75 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
++                                                      <0 0 0 2 &intc 0 78 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
++                                                      <0 0 0 3 &intc 0 79 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
++                                                      <0 0 0 4 &intc 0 83 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
++
++                      interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
++                      interrupt-names = "global_irq";
++
++                      clocks = <&gcc GCC_SYS_NOC_PCIE0_AXI_CLK>,
++                                       <&gcc GCC_PCIE0_AXI_M_CLK>,
++                                       <&gcc GCC_PCIE0_AXI_S_CLK>,
++                                       <&gcc GCC_PCIE0_AHB_CLK>,
++                                       <&gcc GCC_PCIE0_AUX_CLK>,
++                                       <&gcc GCC_PCIE0_AXI_S_BRIDGE_CLK>;
++                      clock-names = "iface",
++                                                "axi_m",
++                                                "axi_s",
++                                                "ahb",
++                                                "aux",
++                                                "axi_bridge";
++
++                      resets = <&gcc GCC_PCIE0_PIPE_ARES>,
++                                       <&gcc GCC_PCIE0_SLEEP_ARES>,
++                                       <&gcc GCC_PCIE0_CORE_STICKY_ARES>,
++                                       <&gcc GCC_PCIE0_AXI_MASTER_ARES>,
++                                       <&gcc GCC_PCIE0_AXI_SLAVE_ARES>,
++                                       <&gcc GCC_PCIE0_AHB_ARES>,
++                                       <&gcc GCC_PCIE0_AXI_MASTER_STICKY_ARES>,
++                                       <&gcc GCC_PCIE0_AXI_SLAVE_STICKY_ARES>;
++                      reset-names = "pipe",
++                                                "sleep",
++                                                "sticky",
++                                                "axi_m",
++                                                "axi_s",
++                                                "ahb",
++                                                "axi_m_sticky",
++                                                "axi_s_sticky";
++
++                      msi-map = <0x0 &v2m0 0x0 0xff8>;
++                      status = "disabled";
++              };
+       };
+       thermal-zones {
diff --git a/target/linux/qualcommax/patches-6.6/0301-dt-bindings-mfd-qcom-tcsr-add-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0301-dt-bindings-mfd-qcom-tcsr-add-IPQ5018-compatible.patch
new file mode 100644 (file)
index 0000000..2faaa1d
--- /dev/null
@@ -0,0 +1,18 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Subject: [PATCH] dt-bindings: mfd: qcom,tcsr: Add IPQ5018 compatible
+Date: Sun, 06 Oct 2024 16:34:11 +0400
+
+Document the qcom,tcsr-ipq5018 compatible.
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml
++++ b/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml
+@@ -33,6 +33,7 @@ properties:
+               - qcom,sm8450-tcsr
+               - qcom,tcsr-apq8064
+               - qcom,tcsr-apq8084
++              - qcom,tcsr-ipq5018
+               - qcom,tcsr-ipq5332
+               - qcom,tcsr-ipq8064
+               - qcom,tcsr-ipq8074
diff --git a/target/linux/qualcommax/patches-6.6/0302-arm64-dts-qcom-IPQ5018-add-TCSR-node.patch b/target/linux/qualcommax/patches-6.6/0302-arm64-dts-qcom-IPQ5018-add-TCSR-node.patch
new file mode 100644 (file)
index 0000000..7a00316
--- /dev/null
@@ -0,0 +1,22 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Subject: [PATCH] arm64: dts: qcom: ipq5018: Add TCSR node
+Date: Sun, 06 Oct 2024 16:34:11 +0400
+
+Add TCSR node.
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -337,6 +337,11 @@
+                       #hwlock-cells = <1>;
+               };
++              tcsr: syscon@1937000 {
++                      compatible = "qcom,tcsr-ipq5018", "syscon", "simple-mfd";
++                      reg = <0x01937000 0x21000>;
++              };
++
+               sdhc_1: mmc@7804000 {
+                       compatible = "qcom,ipq5018-sdhci", "qcom,sdhci-msm-v5";
+                       reg = <0x7804000 0x1000>;
diff --git a/target/linux/qualcommax/patches-6.6/0303-arm64-dts-qcom-IPQ5018-enable-the-download-mode-support.patch b/target/linux/qualcommax/patches-6.6/0303-arm64-dts-qcom-IPQ5018-enable-the-download-mode-support.patch
new file mode 100644 (file)
index 0000000..265695d
--- /dev/null
@@ -0,0 +1,19 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Subject: [PATCH] arm64: dts: qcom: ipq5018: enable the download mode support
+Date: Sun, 06 Oct 2024 16:34:11 +0400
+
+IPQ5018 also supports the download mode to collect the RAM dumps if system crashes, to perform
+the post mortem analysis. Add support for the same.
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -82,6 +82,7 @@
+               scm {
+                       compatible = "qcom,scm-ipq5018", "qcom,scm";
+                       qcom,sdi-enabled;
++                      qcom,dload-mode = <&tcsr 0x6100>;
+               };
+       };
diff --git a/target/linux/qualcommax/patches-6.6/0304-dt-bindings-pwm-add-IPQ5018-compatible.patch b/target/linux/qualcommax/patches-6.6/0304-dt-bindings-pwm-add-IPQ5018-compatible.patch
new file mode 100644 (file)
index 0000000..5705028
--- /dev/null
@@ -0,0 +1,22 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Subject: [PATCH] dt-bindings: mfd: qcom,tcsr: Add IPQ5018 compatible
+Date: Sun, 06 Oct 2024 16:34:11 +0400
+
+Add compatible for IPQ5018.
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml
++++ b/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml
+@@ -11,7 +11,10 @@ maintainers:
+ properties:
+   compatible:
+-    const: qcom,ipq6018-pwm
++    items:
++      - enum:
++          - qcom,ipq5018-pwm
++      - const: qcom,ipq6018-pwm
+   reg:
+     description: Offset of PWM register in the TCSR block.
diff --git a/target/linux/qualcommax/patches-6.6/0305-pinctrl-qcom-IPQ5018-update-pwm-groups.patch b/target/linux/qualcommax/patches-6.6/0305-pinctrl-qcom-IPQ5018-update-pwm-groups.patch
new file mode 100644 (file)
index 0000000..476e5f6
--- /dev/null
@@ -0,0 +1,65 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Subject: [PATCH] pinctrl: qcom: IPQ5018: update pwm groups
+Date: Wed, 27 Nov 2024 09:14:11 +0400
+
+GPIO 1, 30, and 46 are used to control PWM1, PWM3, and PWM0 respectively which
+in turn drive the PWM led, so let's update the pwm# and pingroups accordingly.
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/drivers/pinctrl/qcom/pinctrl-ipq5018.c
++++ b/drivers/pinctrl/qcom/pinctrl-ipq5018.c
+@@ -541,7 +541,7 @@ static const char * const qdss_tracectl_
+ };
+ static const char * const pwm0_groups[] = {
+-      "gpio42",
++      "gpio42", "gpio46",
+ };
+ static const char * const qdss_cti_trig_out_b0_groups[] = {
+@@ -549,7 +549,7 @@ static const char * const qdss_cti_trig_
+ };
+ static const char * const pwm1_groups[] = {
+-      "gpio43",
++      "gpio43", "gpio1",
+ };
+ static const char * const qdss_cti_trig_in_b0_groups[] = {
+@@ -565,7 +565,7 @@ static const char * const qdss_cti_trig_
+ };
+ static const char * const pwm3_groups[] = {
+-      "gpio45",
++      "gpio45", "gpio30",
+ };
+ static const char * const qdss_cti_trig_in_b1_groups[] = {
+@@ -679,7 +679,7 @@ static const struct pinfunction ipq5018_
+ static const struct msm_pingroup ipq5018_groups[] = {
+       PINGROUP(0, atest_char, _, qdss_cti_trig_out_a0, wci_txd, wci_rxd, xfem, _, _, _),
+-      PINGROUP(1, atest_char, _, qdss_cti_trig_in_a0, wci_txd, wci_rxd, xfem, _, _, _),
++      PINGROUP(1, atest_char, pwm1, qdss_cti_trig_in_a0, wci_txd, wci_rxd, xfem, _, _, _),
+       PINGROUP(2, atest_char, _, qdss_cti_trig_out_a1, wci_txd, wci_rxd, xfem, _, _, _),
+       PINGROUP(3, atest_char, _, qdss_cti_trig_in_a1, wci_txd, wci_rxd, xfem, _, _, _),
+       PINGROUP(4, sdc1_data, qspi_data, blsp1_spi1, btss, dbg_out, qdss_traceclk_a, _, burn0, _),
+@@ -708,7 +708,7 @@ static const struct msm_pingroup ipq5018
+       PINGROUP(27, audio_txmclk, wsa_swrm, audio_txmclk, blsp2_spi, btss, _, qdss_tracedata_b, _, _),
+       PINGROUP(28, audio_txbclk, wsa_swrm, blsp0_uart1, btss, qdss_tracedata_b, _, _, _, _),
+       PINGROUP(29, audio_txfsync, _, blsp0_uart1, _, qdss_tracedata_b, _, _, _, _),
+-      PINGROUP(30, audio_txd, led2, led0, _, _, _, _, _, _),
++      PINGROUP(30, audio_txd, led2, led0, pwm3, _, _, _, _, _),
+       PINGROUP(31, blsp2_spi0, blsp1_uart1, _, qdss_tracedata_b, eud_gpio, _, _, _, _),
+       PINGROUP(32, blsp2_spi0, blsp1_uart1, _, qdss_tracedata_b, eud_gpio, _, _, _, _),
+       PINGROUP(33, blsp2_i2c0, blsp2_spi0, blsp1_uart1, _, qdss_tracedata_b, eud_gpio, _, _, _),
+@@ -724,7 +724,7 @@ static const struct msm_pingroup ipq5018
+       PINGROUP(43, pwm1, qdss_cti_trig_in_b0, wci_txd, wci_rxd, xfem, _, _, _, _),
+       PINGROUP(44, pwm2, qdss_cti_trig_out_b1, wci_txd, wci_rxd, xfem, _, _, _, _),
+       PINGROUP(45, pwm3, qdss_cti_trig_in_b1, wci_txd, wci_rxd, xfem, _, _, _, _),
+-      PINGROUP(46, led0, _, _, _, _, _, _, _, _),
++      PINGROUP(46, led0, pwm0, _, _, _, _, _, _, _),
+ };
+ static const struct msm_pinctrl_soc_data ipq5018_pinctrl = {
diff --git a/target/linux/qualcommax/patches-6.6/0306-arm64-dts-qcom-ipq5018-Add-PWM-node.patch b/target/linux/qualcommax/patches-6.6/0306-arm64-dts-qcom-ipq5018-Add-PWM-node.patch
new file mode 100644 (file)
index 0000000..d60e916
--- /dev/null
@@ -0,0 +1,27 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Subject: [PATCH] arm64: dts: qcom: ipq5018: Add PWM node
+Date: Sun, 06 Oct 2024 16:34:11 +0400
+
+Add PWM node.
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -343,6 +343,16 @@
+                       reg = <0x01937000 0x21000>;
+               };
++              pwm: pwm@1941010 {
++                      compatible = "qcom,ipq5018-pwm", "qcom,ipq6018-pwm";
++                      reg = <0x01941010 0x20>;
++                      clocks = <&gcc GCC_ADSS_PWM_CLK>;
++                      assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>;
++                      assigned-clock-rates = <100000000>;
++                      #pwm-cells = <2>;
++                      status = "disabled";
++              };
++
+               sdhc_1: mmc@7804000 {
+                       compatible = "qcom,ipq5018-sdhci", "qcom,sdhci-msm-v5";
+                       reg = <0x7804000 0x1000>;
diff --git a/target/linux/qualcommax/patches-6.6/0324-arm64-dts-qcom-ipq5018-Add-crypto-nodes.patch b/target/linux/qualcommax/patches-6.6/0324-arm64-dts-qcom-ipq5018-Add-crypto-nodes.patch
new file mode 100644 (file)
index 0000000..57d4342
--- /dev/null
@@ -0,0 +1,41 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Subject: [PATCH] arm64: dts: qcom: ipq5018: Add crypto nodes
+Date: Sun, 06 Oct 2024 16:34:11 +0400
+
+Add dma controller and crypto nodes.
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -297,6 +297,30 @@
+                       #thermal-sensor-cells = <1>;
+               };
++              cryptobam: dma-controller@704000 {
++                      compatible = "qcom,bam-v1.7.0";
++                      reg = <0x00704000 0x20000>;
++                      interrupts = <GIC_SPI 207 IRQ_TYPE_LEVEL_HIGH>;
++                      clocks = <&gcc GCC_CRYPTO_AHB_CLK>;
++                      clock-names = "bam_clk";
++                      #dma-cells = <1>;
++                      qcom,ee = <1>;
++                      qcom,controlled-remotely;
++                      status = "disabled";
++              };
++
++              crypto: crypto@73a000 {
++                      compatible = "qcom,crypto-v5.1";
++                      reg = <0x0073a000 0x6000>;
++                      clocks = <&gcc GCC_CRYPTO_AHB_CLK>,
++                               <&gcc GCC_CRYPTO_AXI_CLK>,
++                               <&gcc GCC_CRYPTO_CLK>;
++                      clock-names = "iface", "bus", "core";
++                      dmas = <&cryptobam 2>, <&cryptobam 3>;
++                      dma-names = "rx", "tx";
++                      status = "disabled";
++              };
++
+               tlmm: pinctrl@1000000 {
+                       compatible = "qcom,ipq5018-tlmm";
+                       reg = <0x01000000 0x300000>;
diff --git a/target/linux/qualcommax/patches-6.6/0337-arm64-dts-qcom-ipq5018-Add-PRNG-node.patch b/target/linux/qualcommax/patches-6.6/0337-arm64-dts-qcom-ipq5018-Add-PRNG-node.patch
new file mode 100644 (file)
index 0000000..87d9bdb
--- /dev/null
@@ -0,0 +1,25 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Subject: [PATCH] arm64: dts: qcom: ipq5018: Add PRNG node
+Date: Sun, 06 Oct 2024 16:34:11 +0400
+
+Add PRNG node.
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -258,6 +258,14 @@
+                       };
+               };
++              prng: rng@e3000 {
++                      compatible = "qcom,prng-ee";
++                      reg = <0x000e3000 0x1000>;
++                      clocks = <&gcc GCC_PRNG_AHB_CLK>;
++                      clock-names = "core";
++                      status = "disabled";
++              };
++
+               tsens: thermal-sensor@4a9000 {
+                       compatible = "qcom,ipq5018-tsens";
+                       reg = <0x4a9000 0x1000>, /* TM */
diff --git a/target/linux/qualcommax/patches-6.6/0339-arm64-dts-qcom-ipq5018-Add-QUP1-UART2-node.patch b/target/linux/qualcommax/patches-6.6/0339-arm64-dts-qcom-ipq5018-Add-QUP1-UART2-node.patch
new file mode 100644 (file)
index 0000000..7208506
--- /dev/null
@@ -0,0 +1,27 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Subject: [PATCH] arm64: dts: qcom: ipq5018: Add QUP1-UART2 node
+Date: Sun, 06 Oct 2024 16:34:11 +0400
+
+Add QUP1-UART2 node.
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -422,6 +422,16 @@
+                       status = "disabled";
+               };
++              blsp1_uart2: serial@78b0000 {
++                      compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
++                      reg = <0x078b0000 0x200>;
++                      interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
++                      clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>,
++                                      <&gcc GCC_BLSP1_AHB_CLK>;
++                      clock-names = "core", "iface";
++                      status = "disabled";
++              };
++
+               blsp1_spi1: spi@78b5000 {
+                       compatible = "qcom,spi-qup-v2.2.1";
+                       #address-cells = <1>;
diff --git a/target/linux/qualcommax/patches-6.6/0340-arm64-dts-qcom-ipq5018-Add-QUP3-I2C-node.patch b/target/linux/qualcommax/patches-6.6/0340-arm64-dts-qcom-ipq5018-Add-QUP3-I2C-node.patch
new file mode 100644 (file)
index 0000000..5447e78
--- /dev/null
@@ -0,0 +1,32 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Subject: [PATCH] arm64: dts: qcom: ipq5018: Add QUP3 I2C node
+Date: Sun, 06 Oct 2024 16:34:11 +0400
+
+Add QUP3-I2C node.
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -446,6 +446,21 @@
+                       status = "disabled";
+               };
++              blsp1_i2c3: i2c@78b7000 {
++                      compatible = "qcom,i2c-qup-v2.2.1";
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      reg = <0x078b7000 0x600>;
++                      interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
++                      clocks = <&gcc GCC_BLSP1_QUP3_I2C_APPS_CLK>,
++                               <&gcc GCC_BLSP1_AHB_CLK>;
++                      clock-names = "core", "iface";
++                      clock-frequency = <400000>;
++                      dmas = <&blsp_dma 9>, <&blsp_dma 8>;
++                      dma-names = "tx", "rx";
++                      status = "disabled";
++              };
++
+               usb: usb@8af8800 {
+                       compatible = "qcom,ipq5018-dwc3", "qcom,dwc3";
+                       reg = <0x08af8800 0x400>;
diff --git a/target/linux/qualcommax/patches-6.6/0401-spi-dt-bindings-Introduce-qcom-spi-qpic-snand.patch b/target/linux/qualcommax/patches-6.6/0401-spi-dt-bindings-Introduce-qcom-spi-qpic-snand.patch
new file mode 100644 (file)
index 0000000..433db36
--- /dev/null
@@ -0,0 +1,97 @@
+From: Md Sadre Alam <quic_mdalam@quicinc.com>
+Date: Sun, 22 Sep 2024 17:03:44 +0530
+Subject: [PATCH] spi: dt-bindings: Introduce qcom,spi-qpic-snand
+
+Document the QPIC-SPI-NAND flash controller present in the IPQ SoCs.
+It can work both in serial and parallel mode and supports typical
+SPI-NAND page cache operations.
+
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
+---
+--- /dev/null
++++ b/Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml
+@@ -0,0 +1,83 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/spi/qcom,spi-qpic-snand.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Qualcomm QPIC NAND controller
++
++maintainers:
++  - Md sadre Alam <quic_mdalam@quicinc.com>
++
++description:
++  The QCOM QPIC-SPI-NAND flash controller is an extended version of
++  the QCOM QPIC NAND flash controller. It can work both in serial
++  and parallel mode. It supports typical SPI-NAND page cache
++  operations in single, dual or quad IO mode with pipelined ECC
++  encoding/decoding using the QPIC ECC HW engine.
++
++allOf:
++  - $ref: /schemas/spi/spi-controller.yaml#
++
++properties:
++  compatible:
++    enum:
++      - qcom,spi-qpic-snand
++
++  reg:
++    maxItems: 1
++
++  clocks:
++    maxItems: 3
++
++  clock-names:
++    items:
++      - const: core
++      - const: aon
++      - const: iom
++
++  dmas:
++    items:
++      - description: tx DMA channel
++      - description: rx DMA channel
++      - description: cmd DMA channel
++
++  dma-names:
++    items:
++      - const: tx
++      - const: rx
++      - const: cmd
++
++required:
++  - compatible
++  - reg
++  - clocks
++  - clock-names
++
++unevaluatedProperties: false
++
++examples:
++  - |
++    #include <dt-bindings/clock/qcom,ipq9574-gcc.h>
++    spi@79b0000 {
++        compatible = "qcom,spi-qpic-snand";
++        reg = <0x1ac00000 0x800>;
++
++        clocks = <&gcc GCC_QPIC_CLK>,
++                 <&gcc GCC_QPIC_AHB_CLK>,
++                 <&gcc GCC_QPIC_IO_MACRO_CLK>;
++        clock-names = "core", "aon", "iom";
++
++        #address-cells = <1>;
++        #size-cells = <0>;
++
++        flash@0 {
++            compatible = "spi-nand";
++            reg = <0>;
++            #address-cells = <1>;
++            #size-cells = <1>;
++            nand-ecc-engine = <&qpic_nand>;
++            nand-ecc-strength = <4>;
++            nand-ecc-step-size = <512>;
++        };
++    };
diff --git a/target/linux/qualcommax/patches-6.6/0402-mtd-rawnand-qcom-cleanup-qcom_nandc-driver.patch b/target/linux/qualcommax/patches-6.6/0402-mtd-rawnand-qcom-cleanup-qcom_nandc-driver.patch
new file mode 100644 (file)
index 0000000..91dceaa
--- /dev/null
@@ -0,0 +1,983 @@
+From: Md Sadre Alam <quic_mdalam@quicinc.com>
+Date: Sun, 22 Sep 2024 17:03:45 +0530
+Subject: [PATCH] mtd: rawnand: qcom: cleanup qcom_nandc driver
+
+cleanup qcom_nandc driver as below
+
+- Remove register value indirection api
+
+- Remove set_reg() api
+
+- Convert read_loc_first & read_loc_last macro to function
+
+- Renamed multiple variables
+
+Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
+---
+--- a/drivers/mtd/nand/raw/qcom_nandc.c
++++ b/drivers/mtd/nand/raw/qcom_nandc.c
+@@ -189,17 +189,6 @@
+ #define       ECC_BCH_4BIT    BIT(2)
+ #define       ECC_BCH_8BIT    BIT(3)
+-#define nandc_set_read_loc_first(chip, reg, cw_offset, read_size, is_last_read_loc)   \
+-nandc_set_reg(chip, reg,                      \
+-            ((cw_offset) << READ_LOCATION_OFFSET) |           \
+-            ((read_size) << READ_LOCATION_SIZE) |                     \
+-            ((is_last_read_loc) << READ_LOCATION_LAST))
+-
+-#define nandc_set_read_loc_last(chip, reg, cw_offset, read_size, is_last_read_loc)    \
+-nandc_set_reg(chip, reg,                      \
+-            ((cw_offset) << READ_LOCATION_OFFSET) |           \
+-            ((read_size) << READ_LOCATION_SIZE) |                     \
+-            ((is_last_read_loc) << READ_LOCATION_LAST))
+ /*
+  * Returns the actual register address for all NAND_DEV_ registers
+  * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD)
+@@ -257,8 +246,6 @@ nandc_set_reg(chip, reg,                   \
+  * @tx_sgl_start - start index in data sgl for tx.
+  * @rx_sgl_pos - current index in data sgl for rx.
+  * @rx_sgl_start - start index in data sgl for rx.
+- * @wait_second_completion - wait for second DMA desc completion before making
+- *                         the NAND transfer completion.
+  */
+ struct bam_transaction {
+       struct bam_cmd_element *bam_ce;
+@@ -275,7 +262,6 @@ struct bam_transaction {
+       u32 tx_sgl_start;
+       u32 rx_sgl_pos;
+       u32 rx_sgl_start;
+-      bool wait_second_completion;
+ };
+ /*
+@@ -471,9 +457,9 @@ struct qcom_op {
+       unsigned int data_instr_idx;
+       unsigned int rdy_timeout_ms;
+       unsigned int rdy_delay_ns;
+-      u32 addr1_reg;
+-      u32 addr2_reg;
+-      u32 cmd_reg;
++      __le32 addr1_reg;
++      __le32 addr2_reg;
++      __le32 cmd_reg;
+       u8 flag;
+ };
+@@ -549,17 +535,17 @@ struct qcom_nand_host {
+  * among different NAND controllers.
+  * @ecc_modes - ecc mode for NAND
+  * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
+- * @is_bam - whether NAND controller is using BAM
+- * @is_qpic - whether NAND CTRL is part of qpic IP
+- * @qpic_v2 - flag to indicate QPIC IP version 2
++ * @supports_bam - whether NAND controller is using BAM
++ * @nandc_part_of_qpic - whether NAND controller is part of qpic IP
++ * @qpic_version2 - flag to indicate QPIC IP version 2
+  * @use_codeword_fixup - whether NAND has different layout for boot partitions
+  */
+ struct qcom_nandc_props {
+       u32 ecc_modes;
+       u32 dev_cmd_reg_start;
+-      bool is_bam;
+-      bool is_qpic;
+-      bool qpic_v2;
++      bool supports_bam;
++      bool nandc_part_of_qpic;
++      bool qpic_version2;
+       bool use_codeword_fixup;
+ };
+@@ -613,19 +599,11 @@ static void clear_bam_transaction(struct
+ {
+       struct bam_transaction *bam_txn = nandc->bam_txn;
+-      if (!nandc->props->is_bam)
++      if (!nandc->props->supports_bam)
+               return;
+-      bam_txn->bam_ce_pos = 0;
+-      bam_txn->bam_ce_start = 0;
+-      bam_txn->cmd_sgl_pos = 0;
+-      bam_txn->cmd_sgl_start = 0;
+-      bam_txn->tx_sgl_pos = 0;
+-      bam_txn->tx_sgl_start = 0;
+-      bam_txn->rx_sgl_pos = 0;
+-      bam_txn->rx_sgl_start = 0;
++      memset(&bam_txn->bam_ce_pos, 0, sizeof(u32) * 8);
+       bam_txn->last_data_desc = NULL;
+-      bam_txn->wait_second_completion = false;
+       sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage *
+                     QPIC_PER_CW_CMD_SGL);
+@@ -640,17 +618,7 @@ static void qpic_bam_dma_done(void *data
+ {
+       struct bam_transaction *bam_txn = data;
+-      /*
+-       * In case of data transfer with NAND, 2 callbacks will be generated.
+-       * One for command channel and another one for data channel.
+-       * If current transaction has data descriptors
+-       * (i.e. wait_second_completion is true), then set this to false
+-       * and wait for second DMA descriptor completion.
+-       */
+-      if (bam_txn->wait_second_completion)
+-              bam_txn->wait_second_completion = false;
+-      else
+-              complete(&bam_txn->txn_done);
++      complete(&bam_txn->txn_done);
+ }
+ static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip)
+@@ -676,10 +644,9 @@ static inline void nandc_write(struct qc
+       iowrite32(val, nandc->base + offset);
+ }
+-static inline void nandc_read_buffer_sync(struct qcom_nand_controller *nandc,
+-                                        bool is_cpu)
++static inline void nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu)
+ {
+-      if (!nandc->props->is_bam)
++      if (!nandc->props->supports_bam)
+               return;
+       if (is_cpu)
+@@ -694,93 +661,90 @@ static inline void nandc_read_buffer_syn
+                                          DMA_FROM_DEVICE);
+ }
+-static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset)
+-{
+-      switch (offset) {
+-      case NAND_FLASH_CMD:
+-              return &regs->cmd;
+-      case NAND_ADDR0:
+-              return &regs->addr0;
+-      case NAND_ADDR1:
+-              return &regs->addr1;
+-      case NAND_FLASH_CHIP_SELECT:
+-              return &regs->chip_sel;
+-      case NAND_EXEC_CMD:
+-              return &regs->exec;
+-      case NAND_FLASH_STATUS:
+-              return &regs->clrflashstatus;
+-      case NAND_DEV0_CFG0:
+-              return &regs->cfg0;
+-      case NAND_DEV0_CFG1:
+-              return &regs->cfg1;
+-      case NAND_DEV0_ECC_CFG:
+-              return &regs->ecc_bch_cfg;
+-      case NAND_READ_STATUS:
+-              return &regs->clrreadstatus;
+-      case NAND_DEV_CMD1:
+-              return &regs->cmd1;
+-      case NAND_DEV_CMD1_RESTORE:
+-              return &regs->orig_cmd1;
+-      case NAND_DEV_CMD_VLD:
+-              return &regs->vld;
+-      case NAND_DEV_CMD_VLD_RESTORE:
+-              return &regs->orig_vld;
+-      case NAND_EBI2_ECC_BUF_CFG:
+-              return &regs->ecc_buf_cfg;
+-      case NAND_READ_LOCATION_0:
+-              return &regs->read_location0;
+-      case NAND_READ_LOCATION_1:
+-              return &regs->read_location1;
+-      case NAND_READ_LOCATION_2:
+-              return &regs->read_location2;
+-      case NAND_READ_LOCATION_3:
+-              return &regs->read_location3;
+-      case NAND_READ_LOCATION_LAST_CW_0:
+-              return &regs->read_location_last0;
+-      case NAND_READ_LOCATION_LAST_CW_1:
+-              return &regs->read_location_last1;
+-      case NAND_READ_LOCATION_LAST_CW_2:
+-              return &regs->read_location_last2;
+-      case NAND_READ_LOCATION_LAST_CW_3:
+-              return &regs->read_location_last3;
+-      default:
+-              return NULL;
+-      }
+-}
+-
+-static void nandc_set_reg(struct nand_chip *chip, int offset,
+-                        u32 val)
+-{
+-      struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+-      struct nandc_regs *regs = nandc->regs;
+-      __le32 *reg;
+-
+-      reg = offset_to_nandc_reg(regs, offset);
+-
+-      if (reg)
+-              *reg = cpu_to_le32(val);
+-}
+-
+ /* Helper to check the code word, whether it is last cw or not */
+ static bool qcom_nandc_is_last_cw(struct nand_ecc_ctrl *ecc, int cw)
+ {
+       return cw == (ecc->steps - 1);
+ }
++/**
++ * nandc_set_read_loc_first() - to set read location first register
++ * @chip:             NAND Private Flash Chip Data
++ * @reg_base:         location register base
++ * @cw_offset:                code word offset
++ * @read_size:                code word read length
++ * @is_last_read_loc: is this the last read location
++ *
++ * This function will set location register value
++ */
++static void nandc_set_read_loc_first(struct nand_chip *chip,
++                                   int reg_base, u32 cw_offset,
++                                   u32 read_size, u32 is_last_read_loc)
++{
++      struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
++      __le32 locreg_val;
++      u32 val = (((cw_offset) << READ_LOCATION_OFFSET) |
++                ((read_size) << READ_LOCATION_SIZE) |
++                ((is_last_read_loc) << READ_LOCATION_LAST));
++
++      locreg_val = cpu_to_le32(val);
++
++      if (reg_base == NAND_READ_LOCATION_0)
++              nandc->regs->read_location0 = locreg_val;
++      else if (reg_base == NAND_READ_LOCATION_1)
++              nandc->regs->read_location1 = locreg_val;
++      else if (reg_base == NAND_READ_LOCATION_2)
++              nandc->regs->read_location2 = locreg_val;
++      else if (reg_base == NAND_READ_LOCATION_3)
++              nandc->regs->read_location3 = locreg_val;
++}
++
++/**
++ * nandc_set_read_loc_last - to set read location last register
++ * @chip:             NAND Private Flash Chip Data
++ * @reg_base:         location register base
++ * @cw_offset:                code word offset
++ * @read_size:                code word read length
++ * @is_last_read_loc: is this the last read location
++ *
++ * This function will set location last register value
++ */
++static void nandc_set_read_loc_last(struct nand_chip *chip,
++                                  int reg_base, u32 cw_offset,
++                                  u32 read_size, u32 is_last_read_loc)
++{
++      struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
++      __le32 locreg_val;
++      u32 val = (((cw_offset) << READ_LOCATION_OFFSET) |
++                ((read_size) << READ_LOCATION_SIZE) |
++                ((is_last_read_loc) << READ_LOCATION_LAST));
++
++      locreg_val = cpu_to_le32(val);
++
++      if (reg_base == NAND_READ_LOCATION_LAST_CW_0)
++              nandc->regs->read_location_last0 = locreg_val;
++      else if (reg_base == NAND_READ_LOCATION_LAST_CW_1)
++              nandc->regs->read_location_last1 = locreg_val;
++      else if (reg_base == NAND_READ_LOCATION_LAST_CW_2)
++              nandc->regs->read_location_last2 = locreg_val;
++      else if (reg_base == NAND_READ_LOCATION_LAST_CW_3)
++              nandc->regs->read_location_last3 = locreg_val;
++}
++
+ /* helper to configure location register values */
+ static void nandc_set_read_loc(struct nand_chip *chip, int cw, int reg,
+-                             int cw_offset, int read_size, int is_last_read_loc)
++                             u32 cw_offset, u32 read_size, u32 is_last_read_loc)
+ {
+       struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
+       int reg_base = NAND_READ_LOCATION_0;
+-      if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw))
++      if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw))
+               reg_base = NAND_READ_LOCATION_LAST_CW_0;
+       reg_base += reg * 4;
+-      if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw))
++      if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw))
+               return nandc_set_read_loc_last(chip, reg_base, cw_offset,
+                               read_size, is_last_read_loc);
+       else
+@@ -792,12 +756,13 @@ static void nandc_set_read_loc(struct na
+ static void set_address(struct qcom_nand_host *host, u16 column, int page)
+ {
+       struct nand_chip *chip = &host->chip;
++      struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+       if (chip->options & NAND_BUSWIDTH_16)
+               column >>= 1;
+-      nandc_set_reg(chip, NAND_ADDR0, page << 16 | column);
+-      nandc_set_reg(chip, NAND_ADDR1, page >> 16 & 0xff);
++      nandc->regs->addr0 = cpu_to_le32(page << 16 | column);
++      nandc->regs->addr1 = cpu_to_le32(page >> 16 & 0xff);
+ }
+ /*
+@@ -811,41 +776,43 @@ static void set_address(struct qcom_nand
+ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read, int cw)
+ {
+       struct nand_chip *chip = &host->chip;
+-      u32 cmd, cfg0, cfg1, ecc_bch_cfg;
++      __le32 cmd, cfg0, cfg1, ecc_bch_cfg;
+       struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+       if (read) {
+               if (host->use_ecc)
+-                      cmd = OP_PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE;
++                      cmd = cpu_to_le32(OP_PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE);
+               else
+-                      cmd = OP_PAGE_READ | PAGE_ACC | LAST_PAGE;
++                      cmd = cpu_to_le32(OP_PAGE_READ | PAGE_ACC | LAST_PAGE);
+       } else {
+-              cmd = OP_PROGRAM_PAGE | PAGE_ACC | LAST_PAGE;
++              cmd = cpu_to_le32(OP_PROGRAM_PAGE | PAGE_ACC | LAST_PAGE);
+       }
+       if (host->use_ecc) {
+-              cfg0 = (host->cfg0 & ~(7U << CW_PER_PAGE)) |
+-                              (num_cw - 1) << CW_PER_PAGE;
++              cfg0 = cpu_to_le32((host->cfg0 & ~(7U << CW_PER_PAGE)) |
++                              (num_cw - 1) << CW_PER_PAGE);
+-              cfg1 = host->cfg1;
+-              ecc_bch_cfg = host->ecc_bch_cfg;
++              cfg1 = cpu_to_le32(host->cfg1);
++              ecc_bch_cfg = cpu_to_le32(host->ecc_bch_cfg);
+       } else {
+-              cfg0 = (host->cfg0_raw & ~(7U << CW_PER_PAGE)) |
+-                              (num_cw - 1) << CW_PER_PAGE;
++              cfg0 = cpu_to_le32((host->cfg0_raw & ~(7U << CW_PER_PAGE)) |
++                              (num_cw - 1) << CW_PER_PAGE);
+-              cfg1 = host->cfg1_raw;
+-              ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
++              cfg1 = cpu_to_le32(host->cfg1_raw);
++              ecc_bch_cfg = cpu_to_le32(1 << ECC_CFG_ECC_DISABLE);
+       }
+-      nandc_set_reg(chip, NAND_FLASH_CMD, cmd);
+-      nandc_set_reg(chip, NAND_DEV0_CFG0, cfg0);
+-      nandc_set_reg(chip, NAND_DEV0_CFG1, cfg1);
+-      nandc_set_reg(chip, NAND_DEV0_ECC_CFG, ecc_bch_cfg);
+-      if (!nandc->props->qpic_v2)
+-              nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg);
+-      nandc_set_reg(chip, NAND_FLASH_STATUS, host->clrflashstatus);
+-      nandc_set_reg(chip, NAND_READ_STATUS, host->clrreadstatus);
+-      nandc_set_reg(chip, NAND_EXEC_CMD, 1);
++      nandc->regs->cmd = cmd;
++      nandc->regs->cfg0 = cfg0;
++      nandc->regs->cfg1 = cfg1;
++      nandc->regs->ecc_bch_cfg = ecc_bch_cfg;
++
++      if (!nandc->props->qpic_version2)
++              nandc->regs->ecc_buf_cfg = cpu_to_le32(host->ecc_buf_cfg);
++
++      nandc->regs->clrflashstatus = cpu_to_le32(host->clrflashstatus);
++      nandc->regs->clrreadstatus = cpu_to_le32(host->clrreadstatus);
++      nandc->regs->exec = cpu_to_le32(1);
+       if (read)
+               nandc_set_read_loc(chip, cw, 0, 0, host->use_ecc ?
+@@ -1121,7 +1088,7 @@ static int read_reg_dma(struct qcom_nand
+       if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1)
+               first = dev_cmd_reg_addr(nandc, first);
+-      if (nandc->props->is_bam)
++      if (nandc->props->supports_bam)
+               return prep_bam_dma_desc_cmd(nandc, true, first, vaddr,
+                                            num_regs, flags);
+@@ -1136,25 +1103,16 @@ static int read_reg_dma(struct qcom_nand
+  * write_reg_dma:     prepares a descriptor to write a given number of
+  *                    contiguous registers
+  *
++ * @vaddr:            contnigeous memory from where register value will
++ *                    be written
+  * @first:            offset of the first register in the contiguous block
+  * @num_regs:         number of registers to write
+  * @flags:            flags to control DMA descriptor preparation
+  */
+-static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
+-                       int num_regs, unsigned int flags)
++static int write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr,
++                       int first, int num_regs, unsigned int flags)
+ {
+       bool flow_control = false;
+-      struct nandc_regs *regs = nandc->regs;
+-      void *vaddr;
+-
+-      vaddr = offset_to_nandc_reg(regs, first);
+-
+-      if (first == NAND_ERASED_CW_DETECT_CFG) {
+-              if (flags & NAND_ERASED_CW_SET)
+-                      vaddr = &regs->erased_cw_detect_cfg_set;
+-              else
+-                      vaddr = &regs->erased_cw_detect_cfg_clr;
+-      }
+       if (first == NAND_EXEC_CMD)
+               flags |= NAND_BAM_NWD;
+@@ -1165,7 +1123,7 @@ static int write_reg_dma(struct qcom_nan
+       if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD)
+               first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD);
+-      if (nandc->props->is_bam)
++      if (nandc->props->supports_bam)
+               return prep_bam_dma_desc_cmd(nandc, false, first, vaddr,
+                                            num_regs, flags);
+@@ -1188,7 +1146,7 @@ static int write_reg_dma(struct qcom_nan
+ static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
+                        const u8 *vaddr, int size, unsigned int flags)
+ {
+-      if (nandc->props->is_bam)
++      if (nandc->props->supports_bam)
+               return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags);
+       return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false);
+@@ -1206,7 +1164,7 @@ static int read_data_dma(struct qcom_nan
+ static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
+                         const u8 *vaddr, int size, unsigned int flags)
+ {
+-      if (nandc->props->is_bam)
++      if (nandc->props->supports_bam)
+               return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags);
+       return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false);
+@@ -1220,13 +1178,14 @@ static void config_nand_page_read(struct
+ {
+       struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+-      write_reg_dma(nandc, NAND_ADDR0, 2, 0);
+-      write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
+-      if (!nandc->props->qpic_v2)
+-              write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0);
+-      write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0);
+-      write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1,
+-                    NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
++      write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0);
++      write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
++      if (!nandc->props->qpic_version2)
++              write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0);
++      write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr,
++                    NAND_ERASED_CW_DETECT_CFG, 1, 0);
++      write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set,
++                    NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
+ }
+ /*
+@@ -1239,16 +1198,16 @@ config_nand_cw_read(struct nand_chip *ch
+       struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
+-      int reg = NAND_READ_LOCATION_0;
++      __le32 *reg = &nandc->regs->read_location0;
+-      if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw))
+-              reg = NAND_READ_LOCATION_LAST_CW_0;
++      if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw))
++              reg = &nandc->regs->read_location_last0;
+-      if (nandc->props->is_bam)
+-              write_reg_dma(nandc, reg, 4, NAND_BAM_NEXT_SGL);
++      if (nandc->props->supports_bam)
++              write_reg_dma(nandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL);
+-      write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+-      write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
++      write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
++      write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+       if (use_ecc) {
+               read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0);
+@@ -1279,10 +1238,10 @@ static void config_nand_page_write(struc
+ {
+       struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+-      write_reg_dma(nandc, NAND_ADDR0, 2, 0);
+-      write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
+-      if (!nandc->props->qpic_v2)
+-              write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1,
++      write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0);
++      write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
++      if (!nandc->props->qpic_version2)
++              write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1,
+                             NAND_BAM_NEXT_SGL);
+ }
+@@ -1294,13 +1253,13 @@ static void config_nand_cw_write(struct
+ {
+       struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+-      write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+-      write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
++      write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
++      write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+       read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
+-      write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0);
+-      write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL);
++      write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0);
++      write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL);
+ }
+ /* helpers to submit/free our list of dma descriptors */
+@@ -1311,7 +1270,7 @@ static int submit_descs(struct qcom_nand
+       struct bam_transaction *bam_txn = nandc->bam_txn;
+       int ret = 0;
+-      if (nandc->props->is_bam) {
++      if (nandc->props->supports_bam) {
+               if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) {
+                       ret = prepare_bam_async_desc(nandc, nandc->rx_chan, 0);
+                       if (ret)
+@@ -1336,14 +1295,9 @@ static int submit_descs(struct qcom_nand
+       list_for_each_entry(desc, &nandc->desc_list, node)
+               cookie = dmaengine_submit(desc->dma_desc);
+-      if (nandc->props->is_bam) {
++      if (nandc->props->supports_bam) {
+               bam_txn->last_cmd_desc->callback = qpic_bam_dma_done;
+               bam_txn->last_cmd_desc->callback_param = bam_txn;
+-              if (bam_txn->last_data_desc) {
+-                      bam_txn->last_data_desc->callback = qpic_bam_dma_done;
+-                      bam_txn->last_data_desc->callback_param = bam_txn;
+-                      bam_txn->wait_second_completion = true;
+-              }
+               dma_async_issue_pending(nandc->tx_chan);
+               dma_async_issue_pending(nandc->rx_chan);
+@@ -1365,7 +1319,7 @@ err_unmap_free_desc:
+       list_for_each_entry_safe(desc, n, &nandc->desc_list, node) {
+               list_del(&desc->node);
+-              if (nandc->props->is_bam)
++              if (nandc->props->supports_bam)
+                       dma_unmap_sg(nandc->dev, desc->bam_sgl,
+                                    desc->sgl_cnt, desc->dir);
+               else
+@@ -1382,7 +1336,7 @@ err_unmap_free_desc:
+ static void clear_read_regs(struct qcom_nand_controller *nandc)
+ {
+       nandc->reg_read_pos = 0;
+-      nandc_read_buffer_sync(nandc, false);
++      nandc_dev_to_mem(nandc, false);
+ }
+ /*
+@@ -1446,7 +1400,7 @@ static int check_flash_errors(struct qco
+       struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+       int i;
+-      nandc_read_buffer_sync(nandc, true);
++      nandc_dev_to_mem(nandc, true);
+       for (i = 0; i < cw_cnt; i++) {
+               u32 flash = le32_to_cpu(nandc->reg_read_buf[i]);
+@@ -1476,7 +1430,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *
+       clear_read_regs(nandc);
+       host->use_ecc = false;
+-      if (nandc->props->qpic_v2)
++      if (nandc->props->qpic_version2)
+               raw_cw = ecc->steps - 1;
+       clear_bam_transaction(nandc);
+@@ -1497,7 +1451,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *
+               oob_size2 = host->ecc_bytes_hw + host->spare_bytes;
+       }
+-      if (nandc->props->is_bam) {
++      if (nandc->props->supports_bam) {
+               nandc_set_read_loc(chip, cw, 0, read_loc, data_size1, 0);
+               read_loc += data_size1;
+@@ -1621,7 +1575,7 @@ static int parse_read_errors(struct qcom
+       u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf;
+       buf = (struct read_stats *)nandc->reg_read_buf;
+-      nandc_read_buffer_sync(nandc, true);
++      nandc_dev_to_mem(nandc, true);
+       for (i = 0; i < ecc->steps; i++, buf++) {
+               u32 flash, buffer, erased_cw;
+@@ -1734,7 +1688,7 @@ static int read_page_ecc(struct qcom_nan
+                       oob_size = host->ecc_bytes_hw + host->spare_bytes;
+               }
+-              if (nandc->props->is_bam) {
++              if (nandc->props->supports_bam) {
+                       if (data_buf && oob_buf) {
+                               nandc_set_read_loc(chip, i, 0, 0, data_size, 0);
+                               nandc_set_read_loc(chip, i, 1, data_size,
+@@ -2455,14 +2409,14 @@ static int qcom_nand_attach_chip(struct
+       mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops);
+       /* Free the initially allocated BAM transaction for reading the ONFI params */
+-      if (nandc->props->is_bam)
++      if (nandc->props->supports_bam)
+               free_bam_transaction(nandc);
+       nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage,
+                                    cwperpage);
+       /* Now allocate the BAM transaction based on updated max_cwperpage */
+-      if (nandc->props->is_bam) {
++      if (nandc->props->supports_bam) {
+               nandc->bam_txn = alloc_bam_transaction(nandc);
+               if (!nandc->bam_txn) {
+                       dev_err(nandc->dev,
+@@ -2522,7 +2476,7 @@ static int qcom_nand_attach_chip(struct
+                               | ecc_mode << ECC_MODE
+                               | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_BCH;
+-      if (!nandc->props->qpic_v2)
++      if (!nandc->props->qpic_version2)
+               host->ecc_buf_cfg = 0x203 << NUM_STEPS;
+       host->clrflashstatus = FS_READY_BSY_N;
+@@ -2556,7 +2510,7 @@ static int qcom_op_cmd_mapping(struct na
+               cmd = OP_FETCH_ID;
+               break;
+       case NAND_CMD_PARAM:
+-              if (nandc->props->qpic_v2)
++              if (nandc->props->qpic_version2)
+                       cmd = OP_PAGE_READ_ONFI_READ;
+               else
+                       cmd = OP_PAGE_READ;
+@@ -2609,7 +2563,7 @@ static int qcom_parse_instructions(struc
+                       if (ret < 0)
+                               return ret;
+-                      q_op->cmd_reg = ret;
++                      q_op->cmd_reg = cpu_to_le32(ret);
+                       q_op->rdy_delay_ns = instr->delay_ns;
+                       break;
+@@ -2619,10 +2573,10 @@ static int qcom_parse_instructions(struc
+                       addrs = &instr->ctx.addr.addrs[offset];
+                       for (i = 0; i < min_t(unsigned int, 4, naddrs); i++)
+-                              q_op->addr1_reg |= addrs[i] << (i * 8);
++                              q_op->addr1_reg |= cpu_to_le32(addrs[i] << (i * 8));
+                       if (naddrs > 4)
+-                              q_op->addr2_reg |= addrs[4];
++                              q_op->addr2_reg |= cpu_to_le32(addrs[4]);
+                       q_op->rdy_delay_ns = instr->delay_ns;
+                       break;
+@@ -2663,7 +2617,7 @@ static int qcom_wait_rdy_poll(struct nan
+       unsigned long start = jiffies + msecs_to_jiffies(time_ms);
+       u32 flash;
+-      nandc_read_buffer_sync(nandc, true);
++      nandc_dev_to_mem(nandc, true);
+       do {
+               flash = le32_to_cpu(nandc->reg_read_buf[0]);
+@@ -2706,11 +2660,11 @@ static int qcom_read_status_exec(struct
+       clear_read_regs(nandc);
+       clear_bam_transaction(nandc);
+-      nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg);
+-      nandc_set_reg(chip, NAND_EXEC_CMD, 1);
++      nandc->regs->cmd = q_op.cmd_reg;
++      nandc->regs->exec = cpu_to_le32(1);
+-      write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+-      write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
++      write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
++      write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+       read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
+       ret = submit_descs(nandc);
+@@ -2719,7 +2673,7 @@ static int qcom_read_status_exec(struct
+               goto err_out;
+       }
+-      nandc_read_buffer_sync(nandc, true);
++      nandc_dev_to_mem(nandc, true);
+       for (i = 0; i < num_cw; i++) {
+               flash_status = le32_to_cpu(nandc->reg_read_buf[i]);
+@@ -2763,16 +2717,14 @@ static int qcom_read_id_type_exec(struct
+       clear_read_regs(nandc);
+       clear_bam_transaction(nandc);
+-      nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg);
+-      nandc_set_reg(chip, NAND_ADDR0, q_op.addr1_reg);
+-      nandc_set_reg(chip, NAND_ADDR1, q_op.addr2_reg);
+-      nandc_set_reg(chip, NAND_FLASH_CHIP_SELECT,
+-                    nandc->props->is_bam ? 0 : DM_EN);
++      nandc->regs->cmd = q_op.cmd_reg;
++      nandc->regs->addr0 = q_op.addr1_reg;
++      nandc->regs->addr1 = q_op.addr2_reg;
++      nandc->regs->chip_sel = cpu_to_le32(nandc->props->supports_bam ? 0 : DM_EN);
++      nandc->regs->exec = cpu_to_le32(1);
+-      nandc_set_reg(chip, NAND_EXEC_CMD, 1);
+-
+-      write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
+-      write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
++      write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
++      write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+       read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL);
+@@ -2786,7 +2738,7 @@ static int qcom_read_id_type_exec(struct
+       op_id = q_op.data_instr_idx;
+       len = nand_subop_get_data_len(subop, op_id);
+-      nandc_read_buffer_sync(nandc, true);
++      nandc_dev_to_mem(nandc, true);
+       memcpy(instr->ctx.data.buf.in, nandc->reg_read_buf, len);
+ err_out:
+@@ -2807,15 +2759,14 @@ static int qcom_misc_cmd_type_exec(struc
+       if (q_op.flag == OP_PROGRAM_PAGE) {
+               goto wait_rdy;
+-      } else if (q_op.cmd_reg == OP_BLOCK_ERASE) {
+-              q_op.cmd_reg |= PAGE_ACC | LAST_PAGE;
+-              nandc_set_reg(chip, NAND_ADDR0, q_op.addr1_reg);
+-              nandc_set_reg(chip, NAND_ADDR1, q_op.addr2_reg);
+-              nandc_set_reg(chip, NAND_DEV0_CFG0,
+-                            host->cfg0_raw & ~(7 << CW_PER_PAGE));
+-              nandc_set_reg(chip, NAND_DEV0_CFG1, host->cfg1_raw);
++      } else if (q_op.cmd_reg == cpu_to_le32(OP_BLOCK_ERASE)) {
++              q_op.cmd_reg |= cpu_to_le32(PAGE_ACC | LAST_PAGE);
++              nandc->regs->addr0 = q_op.addr1_reg;
++              nandc->regs->addr1 = q_op.addr2_reg;
++              nandc->regs->cfg0 = cpu_to_le32(host->cfg0_raw & ~(7 << CW_PER_PAGE));
++              nandc->regs->cfg1 = cpu_to_le32(host->cfg1_raw);
+               instrs = 3;
+-      } else if (q_op.cmd_reg != OP_RESET_DEVICE) {
++      } else if (q_op.cmd_reg != cpu_to_le32(OP_RESET_DEVICE)) {
+               return 0;
+       }
+@@ -2826,14 +2777,14 @@ static int qcom_misc_cmd_type_exec(struc
+       clear_read_regs(nandc);
+       clear_bam_transaction(nandc);
+-      nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg);
+-      nandc_set_reg(chip, NAND_EXEC_CMD, 1);
++      nandc->regs->cmd = q_op.cmd_reg;
++      nandc->regs->exec = cpu_to_le32(1);
+-      write_reg_dma(nandc, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL);
+-      if (q_op.cmd_reg == OP_BLOCK_ERASE)
+-              write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL);
++      write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL);
++      if (q_op.cmd_reg == cpu_to_le32(OP_BLOCK_ERASE))
++              write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL);
+-      write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
++      write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+       read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
+       ret = submit_descs(nandc);
+@@ -2864,7 +2815,7 @@ static int qcom_param_page_type_exec(str
+       if (ret)
+               return ret;
+-      q_op.cmd_reg |= PAGE_ACC | LAST_PAGE;
++      q_op.cmd_reg |= cpu_to_le32(PAGE_ACC | LAST_PAGE);
+       nandc->buf_count = 0;
+       nandc->buf_start = 0;
+@@ -2872,38 +2823,38 @@ static int qcom_param_page_type_exec(str
+       clear_read_regs(nandc);
+       clear_bam_transaction(nandc);
+-      nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg);
++      nandc->regs->cmd = q_op.cmd_reg;
++      nandc->regs->addr0 = 0;
++      nandc->regs->addr1 = 0;
++
++      nandc->regs->cfg0 = cpu_to_le32(0 << CW_PER_PAGE
++                          | 512 << UD_SIZE_BYTES
++                          | 5 << NUM_ADDR_CYCLES
++                          | 0 << SPARE_SIZE_BYTES);
++
++      nandc->regs->cfg1 = cpu_to_le32(7 << NAND_RECOVERY_CYCLES
++                          | 0 << CS_ACTIVE_BSY
++                          | 17 << BAD_BLOCK_BYTE_NUM
++                          | 1 << BAD_BLOCK_IN_SPARE_AREA
++                          | 2 << WR_RD_BSY_GAP
++                          | 0 << WIDE_FLASH
++                          | 1 << DEV0_CFG1_ECC_DISABLE);
+-      nandc_set_reg(chip, NAND_ADDR0, 0);
+-      nandc_set_reg(chip, NAND_ADDR1, 0);
+-      nandc_set_reg(chip, NAND_DEV0_CFG0, 0 << CW_PER_PAGE
+-                                      | 512 << UD_SIZE_BYTES
+-                                      | 5 << NUM_ADDR_CYCLES
+-                                      | 0 << SPARE_SIZE_BYTES);
+-      nandc_set_reg(chip, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES
+-                                      | 0 << CS_ACTIVE_BSY
+-                                      | 17 << BAD_BLOCK_BYTE_NUM
+-                                      | 1 << BAD_BLOCK_IN_SPARE_AREA
+-                                      | 2 << WR_RD_BSY_GAP
+-                                      | 0 << WIDE_FLASH
+-                                      | 1 << DEV0_CFG1_ECC_DISABLE);
+-      if (!nandc->props->qpic_v2)
+-              nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE);
++      if (!nandc->props->qpic_version2)
++              nandc->regs->ecc_buf_cfg = cpu_to_le32(1 << ECC_CFG_ECC_DISABLE);
+       /* configure CMD1 and VLD for ONFI param probing in QPIC v1 */
+-      if (!nandc->props->qpic_v2) {
+-              nandc_set_reg(chip, NAND_DEV_CMD_VLD,
+-                            (nandc->vld & ~READ_START_VLD));
+-              nandc_set_reg(chip, NAND_DEV_CMD1,
+-                            (nandc->cmd1 & ~(0xFF << READ_ADDR))
+-                            | NAND_CMD_PARAM << READ_ADDR);
++      if (!nandc->props->qpic_version2) {
++              nandc->regs->vld = cpu_to_le32((nandc->vld & ~READ_START_VLD));
++              nandc->regs->cmd1 = cpu_to_le32((nandc->cmd1 & ~(0xFF << READ_ADDR))
++                                  | NAND_CMD_PARAM << READ_ADDR);
+       }
+-      nandc_set_reg(chip, NAND_EXEC_CMD, 1);
+-
+-      if (!nandc->props->qpic_v2) {
+-              nandc_set_reg(chip, NAND_DEV_CMD1_RESTORE, nandc->cmd1);
+-              nandc_set_reg(chip, NAND_DEV_CMD_VLD_RESTORE, nandc->vld);
++      nandc->regs->exec = cpu_to_le32(1);
++
++      if (!nandc->props->qpic_version2) {
++              nandc->regs->orig_cmd1 = cpu_to_le32(nandc->cmd1);
++              nandc->regs->orig_vld = cpu_to_le32(nandc->vld);
+       }
+       instr = q_op.data_instr;
+@@ -2912,9 +2863,9 @@ static int qcom_param_page_type_exec(str
+       nandc_set_read_loc(chip, 0, 0, 0, len, 1);
+-      if (!nandc->props->qpic_v2) {
+-              write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0);
+-              write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
++      if (!nandc->props->qpic_version2) {
++              write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0);
++              write_reg_dma(nandc, &nandc->regs->cmd1, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
+       }
+       nandc->buf_count = len;
+@@ -2926,9 +2877,10 @@ static int qcom_param_page_type_exec(str
+                     nandc->buf_count, 0);
+       /* restore CMD1 and VLD regs */
+-      if (!nandc->props->qpic_v2) {
+-              write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0);
+-              write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL);
++      if (!nandc->props->qpic_version2) {
++              write_reg_dma(nandc, &nandc->regs->orig_cmd1, NAND_DEV_CMD1_RESTORE, 1, 0);
++              write_reg_dma(nandc, &nandc->regs->orig_vld, NAND_DEV_CMD_VLD_RESTORE, 1,
++                            NAND_BAM_NEXT_SGL);
+       }
+       ret = submit_descs(nandc);
+@@ -3017,7 +2969,7 @@ static const struct nand_controller_ops
+ static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc)
+ {
+-      if (nandc->props->is_bam) {
++      if (nandc->props->supports_bam) {
+               if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma))
+                       dma_unmap_single(nandc->dev, nandc->reg_read_dma,
+                                        MAX_REG_RD *
+@@ -3070,7 +3022,7 @@ static int qcom_nandc_alloc(struct qcom_
+       if (!nandc->reg_read_buf)
+               return -ENOMEM;
+-      if (nandc->props->is_bam) {
++      if (nandc->props->supports_bam) {
+               nandc->reg_read_dma =
+                       dma_map_single(nandc->dev, nandc->reg_read_buf,
+                                      MAX_REG_RD *
+@@ -3151,15 +3103,15 @@ static int qcom_nandc_setup(struct qcom_
+       u32 nand_ctrl;
+       /* kill onenand */
+-      if (!nandc->props->is_qpic)
++      if (!nandc->props->nandc_part_of_qpic)
+               nandc_write(nandc, SFLASHC_BURST_CFG, 0);
+-      if (!nandc->props->qpic_v2)
++      if (!nandc->props->qpic_version2)
+               nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD),
+                           NAND_DEV_CMD_VLD_VAL);
+       /* enable ADM or BAM DMA */
+-      if (nandc->props->is_bam) {
++      if (nandc->props->supports_bam) {
+               nand_ctrl = nandc_read(nandc, NAND_CTRL);
+               /*
+@@ -3176,7 +3128,7 @@ static int qcom_nandc_setup(struct qcom_
+       }
+       /* save the original values of these registers */
+-      if (!nandc->props->qpic_v2) {
++      if (!nandc->props->qpic_version2) {
+               nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1));
+               nandc->vld = NAND_DEV_CMD_VLD_VAL;
+       }
+@@ -3349,7 +3301,7 @@ static int qcom_nandc_parse_dt(struct pl
+       struct device_node *np = nandc->dev->of_node;
+       int ret;
+-      if (!nandc->props->is_bam) {
++      if (!nandc->props->supports_bam) {
+               ret = of_property_read_u32(np, "qcom,cmd-crci",
+                                          &nandc->cmd_crci);
+               if (ret) {
+@@ -3474,30 +3426,30 @@ static void qcom_nandc_remove(struct pla
+ static const struct qcom_nandc_props ipq806x_nandc_props = {
+       .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT),
+-      .is_bam = false,
++      .supports_bam = false,
+       .use_codeword_fixup = true,
+       .dev_cmd_reg_start = 0x0,
+ };
+ static const struct qcom_nandc_props ipq4019_nandc_props = {
+       .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
+-      .is_bam = true,
+-      .is_qpic = true,
++      .supports_bam = true,
++      .nandc_part_of_qpic = true,
+       .dev_cmd_reg_start = 0x0,
+ };
+ static const struct qcom_nandc_props ipq8074_nandc_props = {
+       .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
+-      .is_bam = true,
+-      .is_qpic = true,
++      .supports_bam = true,
++      .nandc_part_of_qpic = true,
+       .dev_cmd_reg_start = 0x7000,
+ };
+ static const struct qcom_nandc_props sdx55_nandc_props = {
+       .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
+-      .is_bam = true,
+-      .is_qpic = true,
+-      .qpic_v2 = true,
++      .supports_bam = true,
++      .nandc_part_of_qpic = true,
++      .qpic_version2 = true,
+       .dev_cmd_reg_start = 0x7000,
+ };
diff --git a/target/linux/qualcommax/patches-6.6/0403-mtd-rawnand-qcom-Add-qcom-prefix-to-common-api.patch b/target/linux/qualcommax/patches-6.6/0403-mtd-rawnand-qcom-Add-qcom-prefix-to-common-api.patch
new file mode 100644 (file)
index 0000000..0bce70c
--- /dev/null
@@ -0,0 +1,874 @@
+From: Md Sadre Alam <quic_mdalam@quicinc.com>
+Date: Sun, 22 Sep 2024 17:03:46 +0530
+Subject: [PATCH] mtd: rawnand: qcom: Add qcom prefix to common api
+
+Add qcom prefix to all the api which will be commonly
+used by spi nand driver and raw nand driver.
+
+Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
+---
+--- a/drivers/mtd/nand/raw/qcom_nandc.c
++++ b/drivers/mtd/nand/raw/qcom_nandc.c
+@@ -53,7 +53,7 @@
+ #define       NAND_READ_LOCATION_LAST_CW_2    0xf48
+ #define       NAND_READ_LOCATION_LAST_CW_3    0xf4c
+-/* dummy register offsets, used by write_reg_dma */
++/* dummy register offsets, used by qcom_write_reg_dma */
+ #define       NAND_DEV_CMD1_RESTORE           0xdead
+ #define       NAND_DEV_CMD_VLD_RESTORE        0xbeef
+@@ -211,7 +211,7 @@
+ /*
+  * Flags used in DMA descriptor preparation helper functions
+- * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma)
++ * (i.e. qcom_read_reg_dma/qcom_write_reg_dma/qcom_read_data_dma/qcom_write_data_dma)
+  */
+ /* Don't set the EOT in current tx BAM sgl */
+ #define NAND_BAM_NO_EOT                       BIT(0)
+@@ -550,7 +550,7 @@ struct qcom_nandc_props {
+ };
+ /* Frees the BAM transaction memory */
+-static void free_bam_transaction(struct qcom_nand_controller *nandc)
++static void qcom_free_bam_transaction(struct qcom_nand_controller *nandc)
+ {
+       struct bam_transaction *bam_txn = nandc->bam_txn;
+@@ -559,7 +559,7 @@ static void free_bam_transaction(struct
+ /* Allocates and Initializes the BAM transaction */
+ static struct bam_transaction *
+-alloc_bam_transaction(struct qcom_nand_controller *nandc)
++qcom_alloc_bam_transaction(struct qcom_nand_controller *nandc)
+ {
+       struct bam_transaction *bam_txn;
+       size_t bam_txn_size;
+@@ -595,7 +595,7 @@ alloc_bam_transaction(struct qcom_nand_c
+ }
+ /* Clears the BAM transaction indexes */
+-static void clear_bam_transaction(struct qcom_nand_controller *nandc)
++static void qcom_clear_bam_transaction(struct qcom_nand_controller *nandc)
+ {
+       struct bam_transaction *bam_txn = nandc->bam_txn;
+@@ -614,7 +614,7 @@ static void clear_bam_transaction(struct
+ }
+ /* Callback for DMA descriptor completion */
+-static void qpic_bam_dma_done(void *data)
++static void qcom_qpic_bam_dma_done(void *data)
+ {
+       struct bam_transaction *bam_txn = data;
+@@ -644,7 +644,7 @@ static inline void nandc_write(struct qc
+       iowrite32(val, nandc->base + offset);
+ }
+-static inline void nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu)
++static inline void qcom_nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu)
+ {
+       if (!nandc->props->supports_bam)
+               return;
+@@ -824,9 +824,9 @@ static void update_rw_regs(struct qcom_n
+  * for BAM. This descriptor will be added in the NAND DMA descriptor queue
+  * which will be submitted to DMA engine.
+  */
+-static int prepare_bam_async_desc(struct qcom_nand_controller *nandc,
+-                                struct dma_chan *chan,
+-                                unsigned long flags)
++static int qcom_prepare_bam_async_desc(struct qcom_nand_controller *nandc,
++                                     struct dma_chan *chan,
++                                     unsigned long flags)
+ {
+       struct desc_info *desc;
+       struct scatterlist *sgl;
+@@ -903,9 +903,9 @@ static int prepare_bam_async_desc(struct
+  * NAND_BAM_NEXT_SGL will be used for starting the separate SGL
+  * after the current command element.
+  */
+-static int prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read,
+-                               int reg_off, const void *vaddr,
+-                               int size, unsigned int flags)
++static int qcom_prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read,
++                                    int reg_off, const void *vaddr,
++                                    int size, unsigned int flags)
+ {
+       int bam_ce_size;
+       int i, ret;
+@@ -943,9 +943,9 @@ static int prep_bam_dma_desc_cmd(struct
+               bam_txn->bam_ce_start = bam_txn->bam_ce_pos;
+               if (flags & NAND_BAM_NWD) {
+-                      ret = prepare_bam_async_desc(nandc, nandc->cmd_chan,
+-                                                   DMA_PREP_FENCE |
+-                                                   DMA_PREP_CMD);
++                      ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan,
++                                                        DMA_PREP_FENCE |
++                                                        DMA_PREP_CMD);
+                       if (ret)
+                               return ret;
+               }
+@@ -958,9 +958,8 @@ static int prep_bam_dma_desc_cmd(struct
+  * Prepares the data descriptor for BAM DMA which will be used for NAND
+  * data reads and writes.
+  */
+-static int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read,
+-                                const void *vaddr,
+-                                int size, unsigned int flags)
++static int qcom_prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read,
++                                     const void *vaddr, int size, unsigned int flags)
+ {
+       int ret;
+       struct bam_transaction *bam_txn = nandc->bam_txn;
+@@ -979,8 +978,8 @@ static int prep_bam_dma_desc_data(struct
+                * is not set, form the DMA descriptor
+                */
+               if (!(flags & NAND_BAM_NO_EOT)) {
+-                      ret = prepare_bam_async_desc(nandc, nandc->tx_chan,
+-                                                   DMA_PREP_INTERRUPT);
++                      ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan,
++                                                        DMA_PREP_INTERRUPT);
+                       if (ret)
+                               return ret;
+               }
+@@ -989,9 +988,9 @@ static int prep_bam_dma_desc_data(struct
+       return 0;
+ }
+-static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read,
+-                           int reg_off, const void *vaddr, int size,
+-                           bool flow_control)
++static int qcom_prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read,
++                                int reg_off, const void *vaddr, int size,
++                                bool flow_control)
+ {
+       struct desc_info *desc;
+       struct dma_async_tx_descriptor *dma_desc;
+@@ -1069,15 +1068,15 @@ err:
+ }
+ /*
+- * read_reg_dma:      prepares a descriptor to read a given number of
++ * qcom_read_reg_dma: prepares a descriptor to read a given number of
+  *                    contiguous registers to the reg_read_buf pointer
+  *
+  * @first:            offset of the first register in the contiguous block
+  * @num_regs:         number of registers to read
+  * @flags:            flags to control DMA descriptor preparation
+  */
+-static int read_reg_dma(struct qcom_nand_controller *nandc, int first,
+-                      int num_regs, unsigned int flags)
++static int qcom_read_reg_dma(struct qcom_nand_controller *nandc, int first,
++                           int num_regs, unsigned int flags)
+ {
+       bool flow_control = false;
+       void *vaddr;
+@@ -1089,18 +1088,18 @@ static int read_reg_dma(struct qcom_nand
+               first = dev_cmd_reg_addr(nandc, first);
+       if (nandc->props->supports_bam)
+-              return prep_bam_dma_desc_cmd(nandc, true, first, vaddr,
++              return qcom_prep_bam_dma_desc_cmd(nandc, true, first, vaddr,
+                                            num_regs, flags);
+       if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
+               flow_control = true;
+-      return prep_adm_dma_desc(nandc, true, first, vaddr,
++      return qcom_prep_adm_dma_desc(nandc, true, first, vaddr,
+                                num_regs * sizeof(u32), flow_control);
+ }
+ /*
+- * write_reg_dma:     prepares a descriptor to write a given number of
++ * qcom_write_reg_dma:        prepares a descriptor to write a given number of
+  *                    contiguous registers
+  *
+  * @vaddr:            contnigeous memory from where register value will
+@@ -1109,8 +1108,8 @@ static int read_reg_dma(struct qcom_nand
+  * @num_regs:         number of registers to write
+  * @flags:            flags to control DMA descriptor preparation
+  */
+-static int write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr,
+-                       int first, int num_regs, unsigned int flags)
++static int qcom_write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr,
++                            int first, int num_regs, unsigned int flags)
+ {
+       bool flow_control = false;
+@@ -1124,18 +1123,18 @@ static int write_reg_dma(struct qcom_nan
+               first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD);
+       if (nandc->props->supports_bam)
+-              return prep_bam_dma_desc_cmd(nandc, false, first, vaddr,
++              return qcom_prep_bam_dma_desc_cmd(nandc, false, first, vaddr,
+                                            num_regs, flags);
+       if (first == NAND_FLASH_CMD)
+               flow_control = true;
+-      return prep_adm_dma_desc(nandc, false, first, vaddr,
++      return qcom_prep_adm_dma_desc(nandc, false, first, vaddr,
+                                num_regs * sizeof(u32), flow_control);
+ }
+ /*
+- * read_data_dma:     prepares a DMA descriptor to transfer data from the
++ * qcom_read_data_dma:        prepares a DMA descriptor to transfer data from the
+  *                    controller's internal buffer to the buffer 'vaddr'
+  *
+  * @reg_off:          offset within the controller's data buffer
+@@ -1143,17 +1142,17 @@ static int write_reg_dma(struct qcom_nan
+  * @size:             DMA transaction size in bytes
+  * @flags:            flags to control DMA descriptor preparation
+  */
+-static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
+-                       const u8 *vaddr, int size, unsigned int flags)
++static int qcom_read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
++                            const u8 *vaddr, int size, unsigned int flags)
+ {
+       if (nandc->props->supports_bam)
+-              return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags);
++              return qcom_prep_bam_dma_desc_data(nandc, true, vaddr, size, flags);
+-      return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false);
++      return qcom_prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false);
+ }
+ /*
+- * write_data_dma:    prepares a DMA descriptor to transfer data from
++ * qcom_write_data_dma:       prepares a DMA descriptor to transfer data from
+  *                    'vaddr' to the controller's internal buffer
+  *
+  * @reg_off:          offset within the controller's data buffer
+@@ -1161,13 +1160,13 @@ static int read_data_dma(struct qcom_nan
+  * @size:             DMA transaction size in bytes
+  * @flags:            flags to control DMA descriptor preparation
+  */
+-static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
+-                        const u8 *vaddr, int size, unsigned int flags)
++static int qcom_write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
++                             const u8 *vaddr, int size, unsigned int flags)
+ {
+       if (nandc->props->supports_bam)
+-              return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags);
++              return qcom_prep_bam_dma_desc_data(nandc, false, vaddr, size, flags);
+-      return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false);
++      return qcom_prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false);
+ }
+ /*
+@@ -1178,14 +1177,14 @@ static void config_nand_page_read(struct
+ {
+       struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+-      write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0);
+-      write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
++      qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0);
++      qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
+       if (!nandc->props->qpic_version2)
+-              write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0);
+-      write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr,
+-                    NAND_ERASED_CW_DETECT_CFG, 1, 0);
+-      write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set,
+-                    NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
++              qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0);
++      qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr,
++                         NAND_ERASED_CW_DETECT_CFG, 1, 0);
++      qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set,
++                         NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
+ }
+ /*
+@@ -1204,17 +1203,17 @@ config_nand_cw_read(struct nand_chip *ch
+               reg = &nandc->regs->read_location_last0;
+       if (nandc->props->supports_bam)
+-              write_reg_dma(nandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL);
++              qcom_write_reg_dma(nandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL);
+-      write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+-      write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+       if (use_ecc) {
+-              read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0);
+-              read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1,
+-                           NAND_BAM_NEXT_SGL);
++              qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0);
++              qcom_read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1,
++                                NAND_BAM_NEXT_SGL);
+       } else {
+-              read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
++              qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
+       }
+ }
+@@ -1238,11 +1237,11 @@ static void config_nand_page_write(struc
+ {
+       struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+-      write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0);
+-      write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
++      qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0);
++      qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
+       if (!nandc->props->qpic_version2)
+-              write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1,
+-                            NAND_BAM_NEXT_SGL);
++              qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1,
++                                 NAND_BAM_NEXT_SGL);
+ }
+ /*
+@@ -1253,17 +1252,18 @@ static void config_nand_cw_write(struct
+ {
+       struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+-      write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+-      write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+-      read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
++      qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
+-      write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0);
+-      write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0);
++      qcom_write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1,
++                         NAND_BAM_NEXT_SGL);
+ }
+ /* helpers to submit/free our list of dma descriptors */
+-static int submit_descs(struct qcom_nand_controller *nandc)
++static int qcom_submit_descs(struct qcom_nand_controller *nandc)
+ {
+       struct desc_info *desc, *n;
+       dma_cookie_t cookie = 0;
+@@ -1272,21 +1272,21 @@ static int submit_descs(struct qcom_nand
+       if (nandc->props->supports_bam) {
+               if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) {
+-                      ret = prepare_bam_async_desc(nandc, nandc->rx_chan, 0);
++                      ret = qcom_prepare_bam_async_desc(nandc, nandc->rx_chan, 0);
+                       if (ret)
+                               goto err_unmap_free_desc;
+               }
+               if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) {
+-                      ret = prepare_bam_async_desc(nandc, nandc->tx_chan,
+-                                                 DMA_PREP_INTERRUPT);
++                      ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan,
++                                                        DMA_PREP_INTERRUPT);
+                       if (ret)
+                               goto err_unmap_free_desc;
+               }
+               if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) {
+-                      ret = prepare_bam_async_desc(nandc, nandc->cmd_chan,
+-                                                 DMA_PREP_CMD);
++                      ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan,
++                                                        DMA_PREP_CMD);
+                       if (ret)
+                               goto err_unmap_free_desc;
+               }
+@@ -1296,7 +1296,7 @@ static int submit_descs(struct qcom_nand
+               cookie = dmaengine_submit(desc->dma_desc);
+       if (nandc->props->supports_bam) {
+-              bam_txn->last_cmd_desc->callback = qpic_bam_dma_done;
++              bam_txn->last_cmd_desc->callback = qcom_qpic_bam_dma_done;
+               bam_txn->last_cmd_desc->callback_param = bam_txn;
+               dma_async_issue_pending(nandc->tx_chan);
+@@ -1314,7 +1314,7 @@ static int submit_descs(struct qcom_nand
+ err_unmap_free_desc:
+       /*
+        * Unmap the dma sg_list and free the desc allocated by both
+-       * prepare_bam_async_desc() and prep_adm_dma_desc() functions.
++       * qcom_prepare_bam_async_desc() and qcom_prep_adm_dma_desc() functions.
+        */
+       list_for_each_entry_safe(desc, n, &nandc->desc_list, node) {
+               list_del(&desc->node);
+@@ -1333,10 +1333,10 @@ err_unmap_free_desc:
+ }
+ /* reset the register read buffer for next NAND operation */
+-static void clear_read_regs(struct qcom_nand_controller *nandc)
++static void qcom_clear_read_regs(struct qcom_nand_controller *nandc)
+ {
+       nandc->reg_read_pos = 0;
+-      nandc_dev_to_mem(nandc, false);
++      qcom_nandc_dev_to_mem(nandc, false);
+ }
+ /*
+@@ -1400,7 +1400,7 @@ static int check_flash_errors(struct qco
+       struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+       int i;
+-      nandc_dev_to_mem(nandc, true);
++      qcom_nandc_dev_to_mem(nandc, true);
+       for (i = 0; i < cw_cnt; i++) {
+               u32 flash = le32_to_cpu(nandc->reg_read_buf[i]);
+@@ -1427,13 +1427,13 @@ qcom_nandc_read_cw_raw(struct mtd_info *
+       nand_read_page_op(chip, page, 0, NULL, 0);
+       nandc->buf_count = 0;
+       nandc->buf_start = 0;
+-      clear_read_regs(nandc);
++      qcom_clear_read_regs(nandc);
+       host->use_ecc = false;
+       if (nandc->props->qpic_version2)
+               raw_cw = ecc->steps - 1;
+-      clear_bam_transaction(nandc);
++      qcom_clear_bam_transaction(nandc);
+       set_address(host, host->cw_size * cw, page);
+       update_rw_regs(host, 1, true, raw_cw);
+       config_nand_page_read(chip);
+@@ -1466,18 +1466,18 @@ qcom_nandc_read_cw_raw(struct mtd_info *
+       config_nand_cw_read(chip, false, raw_cw);
+-      read_data_dma(nandc, reg_off, data_buf, data_size1, 0);
++      qcom_read_data_dma(nandc, reg_off, data_buf, data_size1, 0);
+       reg_off += data_size1;
+-      read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0);
++      qcom_read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0);
+       reg_off += oob_size1;
+-      read_data_dma(nandc, reg_off, data_buf + data_size1, data_size2, 0);
++      qcom_read_data_dma(nandc, reg_off, data_buf + data_size1, data_size2, 0);
+       reg_off += data_size2;
+-      read_data_dma(nandc, reg_off, oob_buf + oob_size1, oob_size2, 0);
++      qcom_read_data_dma(nandc, reg_off, oob_buf + oob_size1, oob_size2, 0);
+-      ret = submit_descs(nandc);
++      ret = qcom_submit_descs(nandc);
+       if (ret) {
+               dev_err(nandc->dev, "failure to read raw cw %d\n", cw);
+               return ret;
+@@ -1575,7 +1575,7 @@ static int parse_read_errors(struct qcom
+       u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf;
+       buf = (struct read_stats *)nandc->reg_read_buf;
+-      nandc_dev_to_mem(nandc, true);
++      qcom_nandc_dev_to_mem(nandc, true);
+       for (i = 0; i < ecc->steps; i++, buf++) {
+               u32 flash, buffer, erased_cw;
+@@ -1704,8 +1704,8 @@ static int read_page_ecc(struct qcom_nan
+               config_nand_cw_read(chip, true, i);
+               if (data_buf)
+-                      read_data_dma(nandc, FLASH_BUF_ACC, data_buf,
+-                                    data_size, 0);
++                      qcom_read_data_dma(nandc, FLASH_BUF_ACC, data_buf,
++                                         data_size, 0);
+               /*
+                * when ecc is enabled, the controller doesn't read the real
+@@ -1720,8 +1720,8 @@ static int read_page_ecc(struct qcom_nan
+                       for (j = 0; j < host->bbm_size; j++)
+                               *oob_buf++ = 0xff;
+-                      read_data_dma(nandc, FLASH_BUF_ACC + data_size,
+-                                    oob_buf, oob_size, 0);
++                      qcom_read_data_dma(nandc, FLASH_BUF_ACC + data_size,
++                                         oob_buf, oob_size, 0);
+               }
+               if (data_buf)
+@@ -1730,7 +1730,7 @@ static int read_page_ecc(struct qcom_nan
+                       oob_buf += oob_size;
+       }
+-      ret = submit_descs(nandc);
++      ret = qcom_submit_descs(nandc);
+       if (ret) {
+               dev_err(nandc->dev, "failure to read page/oob\n");
+               return ret;
+@@ -1751,7 +1751,7 @@ static int copy_last_cw(struct qcom_nand
+       int size;
+       int ret;
+-      clear_read_regs(nandc);
++      qcom_clear_read_regs(nandc);
+       size = host->use_ecc ? host->cw_data : host->cw_size;
+@@ -1763,9 +1763,9 @@ static int copy_last_cw(struct qcom_nand
+       config_nand_single_cw_page_read(chip, host->use_ecc, ecc->steps - 1);
+-      read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0);
++      qcom_read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0);
+-      ret = submit_descs(nandc);
++      ret = qcom_submit_descs(nandc);
+       if (ret)
+               dev_err(nandc->dev, "failed to copy last codeword\n");
+@@ -1851,14 +1851,14 @@ static int qcom_nandc_read_page(struct n
+       nandc->buf_count = 0;
+       nandc->buf_start = 0;
+       host->use_ecc = true;
+-      clear_read_regs(nandc);
++      qcom_clear_read_regs(nandc);
+       set_address(host, 0, page);
+       update_rw_regs(host, ecc->steps, true, 0);
+       data_buf = buf;
+       oob_buf = oob_required ? chip->oob_poi : NULL;
+-      clear_bam_transaction(nandc);
++      qcom_clear_bam_transaction(nandc);
+       return read_page_ecc(host, data_buf, oob_buf, page);
+ }
+@@ -1899,8 +1899,8 @@ static int qcom_nandc_read_oob(struct na
+       if (host->nr_boot_partitions)
+               qcom_nandc_codeword_fixup(host, page);
+-      clear_read_regs(nandc);
+-      clear_bam_transaction(nandc);
++      qcom_clear_read_regs(nandc);
++      qcom_clear_bam_transaction(nandc);
+       host->use_ecc = true;
+       set_address(host, 0, page);
+@@ -1927,8 +1927,8 @@ static int qcom_nandc_write_page(struct
+       set_address(host, 0, page);
+       nandc->buf_count = 0;
+       nandc->buf_start = 0;
+-      clear_read_regs(nandc);
+-      clear_bam_transaction(nandc);
++      qcom_clear_read_regs(nandc);
++      qcom_clear_bam_transaction(nandc);
+       data_buf = (u8 *)buf;
+       oob_buf = chip->oob_poi;
+@@ -1949,8 +1949,8 @@ static int qcom_nandc_write_page(struct
+                       oob_size = ecc->bytes;
+               }
+-              write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size,
+-                             i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0);
++              qcom_write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size,
++                                  i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0);
+               /*
+                * when ECC is enabled, we don't really need to write anything
+@@ -1962,8 +1962,8 @@ static int qcom_nandc_write_page(struct
+               if (qcom_nandc_is_last_cw(ecc, i)) {
+                       oob_buf += host->bbm_size;
+-                      write_data_dma(nandc, FLASH_BUF_ACC + data_size,
+-                                     oob_buf, oob_size, 0);
++                      qcom_write_data_dma(nandc, FLASH_BUF_ACC + data_size,
++                                          oob_buf, oob_size, 0);
+               }
+               config_nand_cw_write(chip);
+@@ -1972,7 +1972,7 @@ static int qcom_nandc_write_page(struct
+               oob_buf += oob_size;
+       }
+-      ret = submit_descs(nandc);
++      ret = qcom_submit_descs(nandc);
+       if (ret) {
+               dev_err(nandc->dev, "failure to write page\n");
+               return ret;
+@@ -1997,8 +1997,8 @@ static int qcom_nandc_write_page_raw(str
+               qcom_nandc_codeword_fixup(host, page);
+       nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+-      clear_read_regs(nandc);
+-      clear_bam_transaction(nandc);
++      qcom_clear_read_regs(nandc);
++      qcom_clear_bam_transaction(nandc);
+       data_buf = (u8 *)buf;
+       oob_buf = chip->oob_poi;
+@@ -2024,28 +2024,28 @@ static int qcom_nandc_write_page_raw(str
+                       oob_size2 = host->ecc_bytes_hw + host->spare_bytes;
+               }
+-              write_data_dma(nandc, reg_off, data_buf, data_size1,
+-                             NAND_BAM_NO_EOT);
++              qcom_write_data_dma(nandc, reg_off, data_buf, data_size1,
++                                  NAND_BAM_NO_EOT);
+               reg_off += data_size1;
+               data_buf += data_size1;
+-              write_data_dma(nandc, reg_off, oob_buf, oob_size1,
+-                             NAND_BAM_NO_EOT);
++              qcom_write_data_dma(nandc, reg_off, oob_buf, oob_size1,
++                                  NAND_BAM_NO_EOT);
+               reg_off += oob_size1;
+               oob_buf += oob_size1;
+-              write_data_dma(nandc, reg_off, data_buf, data_size2,
+-                             NAND_BAM_NO_EOT);
++              qcom_write_data_dma(nandc, reg_off, data_buf, data_size2,
++                                  NAND_BAM_NO_EOT);
+               reg_off += data_size2;
+               data_buf += data_size2;
+-              write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0);
++              qcom_write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0);
+               oob_buf += oob_size2;
+               config_nand_cw_write(chip);
+       }
+-      ret = submit_descs(nandc);
++      ret = qcom_submit_descs(nandc);
+       if (ret) {
+               dev_err(nandc->dev, "failure to write raw page\n");
+               return ret;
+@@ -2075,7 +2075,7 @@ static int qcom_nandc_write_oob(struct n
+               qcom_nandc_codeword_fixup(host, page);
+       host->use_ecc = true;
+-      clear_bam_transaction(nandc);
++      qcom_clear_bam_transaction(nandc);
+       /* calculate the data and oob size for the last codeword/step */
+       data_size = ecc->size - ((ecc->steps - 1) << 2);
+@@ -2090,11 +2090,11 @@ static int qcom_nandc_write_oob(struct n
+       update_rw_regs(host, 1, false, 0);
+       config_nand_page_write(chip);
+-      write_data_dma(nandc, FLASH_BUF_ACC,
+-                     nandc->data_buffer, data_size + oob_size, 0);
++      qcom_write_data_dma(nandc, FLASH_BUF_ACC,
++                          nandc->data_buffer, data_size + oob_size, 0);
+       config_nand_cw_write(chip);
+-      ret = submit_descs(nandc);
++      ret = qcom_submit_descs(nandc);
+       if (ret) {
+               dev_err(nandc->dev, "failure to write oob\n");
+               return ret;
+@@ -2121,7 +2121,7 @@ static int qcom_nandc_block_bad(struct n
+        */
+       host->use_ecc = false;
+-      clear_bam_transaction(nandc);
++      qcom_clear_bam_transaction(nandc);
+       ret = copy_last_cw(host, page);
+       if (ret)
+               goto err;
+@@ -2148,8 +2148,8 @@ static int qcom_nandc_block_markbad(stru
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
+       int page, ret;
+-      clear_read_regs(nandc);
+-      clear_bam_transaction(nandc);
++      qcom_clear_read_regs(nandc);
++      qcom_clear_bam_transaction(nandc);
+       /*
+        * to mark the BBM as bad, we flash the entire last codeword with 0s.
+@@ -2166,11 +2166,11 @@ static int qcom_nandc_block_markbad(stru
+       update_rw_regs(host, 1, false, ecc->steps - 1);
+       config_nand_page_write(chip);
+-      write_data_dma(nandc, FLASH_BUF_ACC,
+-                     nandc->data_buffer, host->cw_size, 0);
++      qcom_write_data_dma(nandc, FLASH_BUF_ACC,
++                          nandc->data_buffer, host->cw_size, 0);
+       config_nand_cw_write(chip);
+-      ret = submit_descs(nandc);
++      ret = qcom_submit_descs(nandc);
+       if (ret) {
+               dev_err(nandc->dev, "failure to update BBM\n");
+               return ret;
+@@ -2410,14 +2410,14 @@ static int qcom_nand_attach_chip(struct
+       mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops);
+       /* Free the initially allocated BAM transaction for reading the ONFI params */
+       if (nandc->props->supports_bam)
+-              free_bam_transaction(nandc);
++              qcom_free_bam_transaction(nandc);
+       nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage,
+                                    cwperpage);
+       /* Now allocate the BAM transaction based on updated max_cwperpage */
+       if (nandc->props->supports_bam) {
+-              nandc->bam_txn = alloc_bam_transaction(nandc);
++              nandc->bam_txn = qcom_alloc_bam_transaction(nandc);
+               if (!nandc->bam_txn) {
+                       dev_err(nandc->dev,
+                               "failed to allocate bam transaction\n");
+@@ -2617,7 +2617,7 @@ static int qcom_wait_rdy_poll(struct nan
+       unsigned long start = jiffies + msecs_to_jiffies(time_ms);
+       u32 flash;
+-      nandc_dev_to_mem(nandc, true);
++      qcom_nandc_dev_to_mem(nandc, true);
+       do {
+               flash = le32_to_cpu(nandc->reg_read_buf[0]);
+@@ -2657,23 +2657,23 @@ static int qcom_read_status_exec(struct
+       nandc->buf_start = 0;
+       host->use_ecc = false;
+-      clear_read_regs(nandc);
+-      clear_bam_transaction(nandc);
++      qcom_clear_read_regs(nandc);
++      qcom_clear_bam_transaction(nandc);
+       nandc->regs->cmd = q_op.cmd_reg;
+       nandc->regs->exec = cpu_to_le32(1);
+-      write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+-      write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+-      read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
++      qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
+-      ret = submit_descs(nandc);
++      ret = qcom_submit_descs(nandc);
+       if (ret) {
+               dev_err(nandc->dev, "failure in submitting status descriptor\n");
+               goto err_out;
+       }
+-      nandc_dev_to_mem(nandc, true);
++      qcom_nandc_dev_to_mem(nandc, true);
+       for (i = 0; i < num_cw; i++) {
+               flash_status = le32_to_cpu(nandc->reg_read_buf[i]);
+@@ -2714,8 +2714,8 @@ static int qcom_read_id_type_exec(struct
+       nandc->buf_start = 0;
+       host->use_ecc = false;
+-      clear_read_regs(nandc);
+-      clear_bam_transaction(nandc);
++      qcom_clear_read_regs(nandc);
++      qcom_clear_bam_transaction(nandc);
+       nandc->regs->cmd = q_op.cmd_reg;
+       nandc->regs->addr0 = q_op.addr1_reg;
+@@ -2723,12 +2723,12 @@ static int qcom_read_id_type_exec(struct
+       nandc->regs->chip_sel = cpu_to_le32(nandc->props->supports_bam ? 0 : DM_EN);
+       nandc->regs->exec = cpu_to_le32(1);
+-      write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
+-      write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+-      read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL);
++      qcom_read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL);
+-      ret = submit_descs(nandc);
++      ret = qcom_submit_descs(nandc);
+       if (ret) {
+               dev_err(nandc->dev, "failure in submitting read id descriptor\n");
+               goto err_out;
+@@ -2738,7 +2738,7 @@ static int qcom_read_id_type_exec(struct
+       op_id = q_op.data_instr_idx;
+       len = nand_subop_get_data_len(subop, op_id);
+-      nandc_dev_to_mem(nandc, true);
++      qcom_nandc_dev_to_mem(nandc, true);
+       memcpy(instr->ctx.data.buf.in, nandc->reg_read_buf, len);
+ err_out:
+@@ -2774,20 +2774,20 @@ static int qcom_misc_cmd_type_exec(struc
+       nandc->buf_start = 0;
+       host->use_ecc = false;
+-      clear_read_regs(nandc);
+-      clear_bam_transaction(nandc);
++      qcom_clear_read_regs(nandc);
++      qcom_clear_bam_transaction(nandc);
+       nandc->regs->cmd = q_op.cmd_reg;
+       nandc->regs->exec = cpu_to_le32(1);
+-      write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL);
+       if (q_op.cmd_reg == cpu_to_le32(OP_BLOCK_ERASE))
+-              write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL);
++              qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL);
+-      write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+-      read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
++      qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
+-      ret = submit_descs(nandc);
++      ret = qcom_submit_descs(nandc);
+       if (ret) {
+               dev_err(nandc->dev, "failure in submitting misc descriptor\n");
+               goto err_out;
+@@ -2820,8 +2820,8 @@ static int qcom_param_page_type_exec(str
+       nandc->buf_count = 0;
+       nandc->buf_start = 0;
+       host->use_ecc = false;
+-      clear_read_regs(nandc);
+-      clear_bam_transaction(nandc);
++      qcom_clear_read_regs(nandc);
++      qcom_clear_bam_transaction(nandc);
+       nandc->regs->cmd = q_op.cmd_reg;
+       nandc->regs->addr0 = 0;
+@@ -2864,8 +2864,8 @@ static int qcom_param_page_type_exec(str
+       nandc_set_read_loc(chip, 0, 0, 0, len, 1);
+       if (!nandc->props->qpic_version2) {
+-              write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0);
+-              write_reg_dma(nandc, &nandc->regs->cmd1, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
++              qcom_write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0);
++              qcom_write_reg_dma(nandc, &nandc->regs->cmd1, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
+       }
+       nandc->buf_count = len;
+@@ -2873,17 +2873,17 @@ static int qcom_param_page_type_exec(str
+       config_nand_single_cw_page_read(chip, false, 0);
+-      read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer,
+-                    nandc->buf_count, 0);
++      qcom_read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer,
++                         nandc->buf_count, 0);
+       /* restore CMD1 and VLD regs */
+       if (!nandc->props->qpic_version2) {
+-              write_reg_dma(nandc, &nandc->regs->orig_cmd1, NAND_DEV_CMD1_RESTORE, 1, 0);
+-              write_reg_dma(nandc, &nandc->regs->orig_vld, NAND_DEV_CMD_VLD_RESTORE, 1,
+-                            NAND_BAM_NEXT_SGL);
++              qcom_write_reg_dma(nandc, &nandc->regs->orig_cmd1, NAND_DEV_CMD1_RESTORE, 1, 0);
++              qcom_write_reg_dma(nandc, &nandc->regs->orig_vld, NAND_DEV_CMD_VLD_RESTORE, 1,
++                                 NAND_BAM_NEXT_SGL);
+       }
+-      ret = submit_descs(nandc);
++      ret = qcom_submit_descs(nandc);
+       if (ret) {
+               dev_err(nandc->dev, "failure in submitting param page descriptor\n");
+               goto err_out;
+@@ -3067,7 +3067,7 @@ static int qcom_nandc_alloc(struct qcom_
+                * maximum codeword size
+                */
+               nandc->max_cwperpage = 1;
+-              nandc->bam_txn = alloc_bam_transaction(nandc);
++              nandc->bam_txn = qcom_alloc_bam_transaction(nandc);
+               if (!nandc->bam_txn) {
+                       dev_err(nandc->dev,
+                               "failed to allocate bam transaction\n");
diff --git a/target/linux/qualcommax/patches-6.6/0404-mtd-nand-Add-qpic_common-API-file.patch b/target/linux/qualcommax/patches-6.6/0404-mtd-nand-Add-qpic_common-API-file.patch
new file mode 100644 (file)
index 0000000..e992e26
--- /dev/null
@@ -0,0 +1,2418 @@
+From: Md Sadre Alam <quic_mdalam@quicinc.com>
+Date: Sun, 22 Sep 2024 17:03:47 +0530
+Subject: [PATCH] mtd: nand: Add qpic_common API file
+
+Add qpic_common.c file which hold all the common
+qpic APIs which will be used by both qpic raw nand
+driver and qpic spi nand driver.
+
+Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
+---
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -5,6 +5,10 @@ obj-$(CONFIG_MTD_NAND_CORE) += nandcore.
+ obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o
+ obj-$(CONFIG_MTD_NAND_MTK_BMT)        += mtk_bmt.o mtk_bmt_v2.o mtk_bmt_bbt.o mtk_bmt_nmbm.o
++ifeq ($(CONFIG_MTD_NAND_QCOM),y)
++obj-y += qpic_common.o
++endif
++
+ obj-y += onenand/
+ obj-y += raw/
+ obj-y += spi/
+--- /dev/null
++++ b/drivers/mtd/nand/qpic_common.c
+@@ -0,0 +1,738 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
++ */
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/dmaengine.h>
++#include <linux/dma-mapping.h>
++#include <linux/dma/qcom_adm.h>
++#include <linux/dma/qcom_bam_dma.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/mtd/nand-qpic-common.h>
++
++/**
++ * qcom_free_bam_transaction() - Frees the BAM transaction memory
++ * @nandc: qpic nand controller
++ *
++ * This function frees the bam transaction memory
++ */
++void qcom_free_bam_transaction(struct qcom_nand_controller *nandc)
++{
++      struct bam_transaction *bam_txn = nandc->bam_txn;
++
++      kfree(bam_txn);
++}
++
++/**
++ * qcom_alloc_bam_transaction() - allocate BAM transaction
++ * @nandc: qpic nand controller
++ *
++ * This function will allocate and initialize the BAM transaction structure
++ */
++struct bam_transaction *
++qcom_alloc_bam_transaction(struct qcom_nand_controller *nandc)
++{
++      struct bam_transaction *bam_txn;
++      size_t bam_txn_size;
++      unsigned int num_cw = nandc->max_cwperpage;
++      void *bam_txn_buf;
++
++      bam_txn_size =
++              sizeof(*bam_txn) + num_cw *
++              ((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) +
++              (sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) +
++              (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL));
++
++      bam_txn_buf = kzalloc(bam_txn_size, GFP_KERNEL);
++      if (!bam_txn_buf)
++              return NULL;
++
++      bam_txn = bam_txn_buf;
++      bam_txn_buf += sizeof(*bam_txn);
++
++      bam_txn->bam_ce = bam_txn_buf;
++      bam_txn_buf +=
++              sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw;
++
++      bam_txn->cmd_sgl = bam_txn_buf;
++      bam_txn_buf +=
++              sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw;
++
++      bam_txn->data_sgl = bam_txn_buf;
++
++      init_completion(&bam_txn->txn_done);
++
++      return bam_txn;
++}
++
++/**
++ * qcom_clear_bam_transaction() - Clears the BAM transaction
++ * @nandc: qpic nand controller
++ *
++ * This function will clear the BAM transaction indexes.
++ */
++void qcom_clear_bam_transaction(struct qcom_nand_controller *nandc)
++{
++      struct bam_transaction *bam_txn = nandc->bam_txn;
++
++      if (!nandc->props->supports_bam)
++              return;
++
++      memset(&bam_txn->bam_ce_pos, 0, sizeof(u32) * 8);
++      bam_txn->last_data_desc = NULL;
++
++      sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage *
++                    QPIC_PER_CW_CMD_SGL);
++      sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage *
++                    QPIC_PER_CW_DATA_SGL);
++
++      reinit_completion(&bam_txn->txn_done);
++}
++
++/**
++ * qcom_qpic_bam_dma_done() - Callback for DMA descriptor completion
++ * @data: data pointer
++ *
++ * This function is a callback for DMA descriptor completion
++ */
++void qcom_qpic_bam_dma_done(void *data)
++{
++      struct bam_transaction *bam_txn = data;
++
++      complete(&bam_txn->txn_done);
++}
++
++/**
++ * qcom_nandc_dev_to_mem() - Check for dma sync for cpu or device
++ * @nandc: qpic nand controller
++ * @is_cpu: cpu or Device
++ *
++ * This function will check for dma sync for cpu or device
++ */
++inline void qcom_nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu)
++{
++      if (!nandc->props->supports_bam)
++              return;
++
++      if (is_cpu)
++              dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma,
++                                      MAX_REG_RD *
++                                      sizeof(*nandc->reg_read_buf),
++                                      DMA_FROM_DEVICE);
++      else
++              dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma,
++                                         MAX_REG_RD *
++                                         sizeof(*nandc->reg_read_buf),
++                                         DMA_FROM_DEVICE);
++}
++
++/**
++ * qcom_prepare_bam_async_desc() - Prepare DMA descriptor
++ * @nandc: qpic nand controller
++ * @chan: dma channel
++ * @flags: flags to control DMA descriptor preparation
++ *
++ * This function maps the scatter gather list for DMA transfer and forms the
++ * DMA descriptor for BAM.This descriptor will be added in the NAND DMA
++ * descriptor queue which will be submitted to DMA engine.
++ */
++int qcom_prepare_bam_async_desc(struct qcom_nand_controller *nandc,
++                              struct dma_chan *chan, unsigned long flags)
++{
++      struct desc_info *desc;
++      struct scatterlist *sgl;
++      unsigned int sgl_cnt;
++      int ret;
++      struct bam_transaction *bam_txn = nandc->bam_txn;
++      enum dma_transfer_direction dir_eng;
++      struct dma_async_tx_descriptor *dma_desc;
++
++      desc = kzalloc(sizeof(*desc), GFP_KERNEL);
++      if (!desc)
++              return -ENOMEM;
++
++      if (chan == nandc->cmd_chan) {
++              sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start];
++              sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start;
++              bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos;
++              dir_eng = DMA_MEM_TO_DEV;
++              desc->dir = DMA_TO_DEVICE;
++      } else if (chan == nandc->tx_chan) {
++              sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start];
++              sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start;
++              bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos;
++              dir_eng = DMA_MEM_TO_DEV;
++              desc->dir = DMA_TO_DEVICE;
++      } else {
++              sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start];
++              sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start;
++              bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos;
++              dir_eng = DMA_DEV_TO_MEM;
++              desc->dir = DMA_FROM_DEVICE;
++      }
++
++      sg_mark_end(sgl + sgl_cnt - 1);
++      ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir);
++      if (ret == 0) {
++              dev_err(nandc->dev, "failure in mapping desc\n");
++              kfree(desc);
++              return -ENOMEM;
++      }
++
++      desc->sgl_cnt = sgl_cnt;
++      desc->bam_sgl = sgl;
++
++      dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng,
++                                         flags);
++
++      if (!dma_desc) {
++              dev_err(nandc->dev, "failure in prep desc\n");
++              dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir);
++              kfree(desc);
++              return -EINVAL;
++      }
++
++      desc->dma_desc = dma_desc;
++
++      /* update last data/command descriptor */
++      if (chan == nandc->cmd_chan)
++              bam_txn->last_cmd_desc = dma_desc;
++      else
++              bam_txn->last_data_desc = dma_desc;
++
++      list_add_tail(&desc->node, &nandc->desc_list);
++
++      return 0;
++}
++
++/**
++ * qcom_prep_bam_dma_desc_cmd() - Prepares the command descriptor for BAM DMA
++ * @nandc: qpic nand controller
++ * @read: read or write type
++ * @reg_off: offset within the controller's data buffer
++ * @vaddr: virtual address of the buffer we want to write to
++ * @size: DMA transaction size in bytes
++ * @flags: flags to control DMA descriptor preparation
++ *
++ * This function will prepares the command descriptor for BAM DMA
++ * which will be used for NAND register reads and writes.
++ */
++int qcom_prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read,
++                             int reg_off, const void *vaddr,
++                             int size, unsigned int flags)
++{
++      int bam_ce_size;
++      int i, ret;
++      struct bam_cmd_element *bam_ce_buffer;
++      struct bam_transaction *bam_txn = nandc->bam_txn;
++
++      bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos];
++
++      /* fill the command desc */
++      for (i = 0; i < size; i++) {
++              if (read)
++                      bam_prep_ce(&bam_ce_buffer[i],
++                                  nandc_reg_phys(nandc, reg_off + 4 * i),
++                                  BAM_READ_COMMAND,
++                                  reg_buf_dma_addr(nandc,
++                                                   (__le32 *)vaddr + i));
++              else
++                      bam_prep_ce_le32(&bam_ce_buffer[i],
++                                       nandc_reg_phys(nandc, reg_off + 4 * i),
++                                       BAM_WRITE_COMMAND,
++                                       *((__le32 *)vaddr + i));
++      }
++
++      bam_txn->bam_ce_pos += size;
++
++      /* use the separate sgl after this command */
++      if (flags & NAND_BAM_NEXT_SGL) {
++              bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start];
++              bam_ce_size = (bam_txn->bam_ce_pos -
++                              bam_txn->bam_ce_start) *
++                              sizeof(struct bam_cmd_element);
++              sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos],
++                         bam_ce_buffer, bam_ce_size);
++              bam_txn->cmd_sgl_pos++;
++              bam_txn->bam_ce_start = bam_txn->bam_ce_pos;
++
++              if (flags & NAND_BAM_NWD) {
++                      ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan,
++                                                        DMA_PREP_FENCE | DMA_PREP_CMD);
++                      if (ret)
++                              return ret;
++              }
++      }
++
++      return 0;
++}
++
++/**
++ * qcom_prep_bam_dma_desc_data() - Prepares the data descriptor for BAM DMA
++ * @nandc: qpic nand controller
++ * @read: read or write type
++ * @vaddr: virtual address of the buffer we want to write to
++ * @size: DMA transaction size in bytes
++ * @flags: flags to control DMA descriptor preparation
++ *
++ * This function will prepares the data descriptor for BAM DMA which
++ * will be used for NAND data reads and writes.
++ */
++int qcom_prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read,
++                              const void *vaddr, int size, unsigned int flags)
++{
++      int ret;
++      struct bam_transaction *bam_txn = nandc->bam_txn;
++
++      if (read) {
++              sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos],
++                         vaddr, size);
++              bam_txn->rx_sgl_pos++;
++      } else {
++              sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos],
++                         vaddr, size);
++              bam_txn->tx_sgl_pos++;
++
++              /*
++               * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag
++               * is not set, form the DMA descriptor
++               */
++              if (!(flags & NAND_BAM_NO_EOT)) {
++                      ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan,
++                                                        DMA_PREP_INTERRUPT);
++                      if (ret)
++                              return ret;
++              }
++      }
++
++      return 0;
++}
++
++/**
++ * qcom_prep_adm_dma_desc() - Prepare descriptor for adma
++ * @nandc: qpic nand controller
++ * @read: read or write type
++ * @reg_off: offset within the controller's data buffer
++ * @vaddr: virtual address of the buffer we want to write to
++ * @size: adm dma transaction size in bytes
++ * @flow_control: flow controller
++ *
++ * This function will prepare descriptor for adma
++ */
++int qcom_prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read,
++                         int reg_off, const void *vaddr, int size,
++                         bool flow_control)
++{
++      struct qcom_adm_peripheral_config periph_conf = {};
++      struct dma_async_tx_descriptor *dma_desc;
++      struct dma_slave_config slave_conf = {0};
++      enum dma_transfer_direction dir_eng;
++      struct desc_info *desc;
++      struct scatterlist *sgl;
++      int ret;
++
++      desc = kzalloc(sizeof(*desc), GFP_KERNEL);
++      if (!desc)
++              return -ENOMEM;
++
++      sgl = &desc->adm_sgl;
++
++      sg_init_one(sgl, vaddr, size);
++
++      if (read) {
++              dir_eng = DMA_DEV_TO_MEM;
++              desc->dir = DMA_FROM_DEVICE;
++      } else {
++              dir_eng = DMA_MEM_TO_DEV;
++              desc->dir = DMA_TO_DEVICE;
++      }
++
++      ret = dma_map_sg(nandc->dev, sgl, 1, desc->dir);
++      if (!ret) {
++              ret = -ENOMEM;
++              goto err;
++      }
++
++      slave_conf.device_fc = flow_control;
++      if (read) {
++              slave_conf.src_maxburst = 16;
++              slave_conf.src_addr = nandc->base_dma + reg_off;
++              if (nandc->data_crci) {
++                      periph_conf.crci = nandc->data_crci;
++                      slave_conf.peripheral_config = &periph_conf;
++                      slave_conf.peripheral_size = sizeof(periph_conf);
++              }
++      } else {
++              slave_conf.dst_maxburst = 16;
++              slave_conf.dst_addr = nandc->base_dma + reg_off;
++              if (nandc->cmd_crci) {
++                      periph_conf.crci = nandc->cmd_crci;
++                      slave_conf.peripheral_config = &periph_conf;
++                      slave_conf.peripheral_size = sizeof(periph_conf);
++              }
++      }
++
++      ret = dmaengine_slave_config(nandc->chan, &slave_conf);
++      if (ret) {
++              dev_err(nandc->dev, "failed to configure dma channel\n");
++              goto err;
++      }
++
++      dma_desc = dmaengine_prep_slave_sg(nandc->chan, sgl, 1, dir_eng, 0);
++      if (!dma_desc) {
++              dev_err(nandc->dev, "failed to prepare desc\n");
++              ret = -EINVAL;
++              goto err;
++      }
++
++      desc->dma_desc = dma_desc;
++
++      list_add_tail(&desc->node, &nandc->desc_list);
++
++      return 0;
++err:
++      kfree(desc);
++
++      return ret;
++}
++
++/**
++ * qcom_read_reg_dma() - read a given number of registers to the reg_read_buf pointer
++ * @nandc: qpic nand controller
++ * @first: offset of the first register in the contiguous block
++ * @num_regs: number of registers to read
++ * @flags: flags to control DMA descriptor preparation
++ *
++ * This function will prepares a descriptor to read a given number of
++ * contiguous registers to the reg_read_buf pointer.
++ */
++int qcom_read_reg_dma(struct qcom_nand_controller *nandc, int first,
++                    int num_regs, unsigned int flags)
++{
++      bool flow_control = false;
++      void *vaddr;
++
++      vaddr = nandc->reg_read_buf + nandc->reg_read_pos;
++      nandc->reg_read_pos += num_regs;
++
++      if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1)
++              first = dev_cmd_reg_addr(nandc, first);
++
++      if (nandc->props->supports_bam)
++              return qcom_prep_bam_dma_desc_cmd(nandc, true, first, vaddr,
++                                           num_regs, flags);
++
++      if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
++              flow_control = true;
++
++      return qcom_prep_adm_dma_desc(nandc, true, first, vaddr,
++                                    num_regs * sizeof(u32), flow_control);
++}
++
++/**
++ * qcom_write_reg_dma() - write a given number of registers
++ * @nandc: qpic nand controller
++ * @vaddr: contnigeous memory from where register value will
++ *       be written
++ * @first: offset of the first register in the contiguous block
++ * @num_regs: number of registers to write
++ * @flags: flags to control DMA descriptor preparation
++ *
++ * This function will prepares a descriptor to write a given number of
++ * contiguous registers
++ */
++int qcom_write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr,
++                     int first, int num_regs, unsigned int flags)
++{
++      bool flow_control = false;
++
++      if (first == NAND_EXEC_CMD)
++              flags |= NAND_BAM_NWD;
++
++      if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1)
++              first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1);
++
++      if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD)
++              first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD);
++
++      if (nandc->props->supports_bam)
++              return qcom_prep_bam_dma_desc_cmd(nandc, false, first, vaddr,
++                                                num_regs, flags);
++
++      if (first == NAND_FLASH_CMD)
++              flow_control = true;
++
++      return qcom_prep_adm_dma_desc(nandc, false, first, vaddr,
++                                    num_regs * sizeof(u32), flow_control);
++}
++
++/**
++ * qcom_read_data_dma() - transfer data
++ * @nandc: qpic nand controller
++ * @reg_off: offset within the controller's data buffer
++ * @vaddr: virtual address of the buffer we want to write to
++ * @size: DMA transaction size in bytes
++ * @flags: flags to control DMA descriptor preparation
++ *
++ * This function will prepares a DMA descriptor to transfer data from the
++ * controller's internal buffer to the buffer 'vaddr'
++ */
++int qcom_read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
++                     const u8 *vaddr, int size, unsigned int flags)
++{
++      if (nandc->props->supports_bam)
++              return qcom_prep_bam_dma_desc_data(nandc, true, vaddr, size, flags);
++
++      return qcom_prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false);
++}
++
++/**
++ * qcom_write_data_dma() - transfer data
++ * @nandc: qpic nand controller
++ * @reg_off: offset within the controller's data buffer
++ * @vaddr: virtual address of the buffer we want to read from
++ * @size: DMA transaction size in bytes
++ * @flags: flags to control DMA descriptor preparation
++ *
++ * This function will prepares a DMA descriptor to transfer data from
++ * 'vaddr' to the controller's internal buffer
++ */
++int qcom_write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
++                      const u8 *vaddr, int size, unsigned int flags)
++{
++      if (nandc->props->supports_bam)
++              return qcom_prep_bam_dma_desc_data(nandc, false, vaddr, size, flags);
++
++      return qcom_prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false);
++}
++
++/**
++ * qcom_submit_descs() - submit dma descriptor
++ * @nandc: qpic nand controller
++ *
++ * This function will submit all the prepared dma descriptor
++ * cmd or data descriptor
++ */
++int qcom_submit_descs(struct qcom_nand_controller *nandc)
++{
++      struct desc_info *desc, *n;
++      dma_cookie_t cookie = 0;
++      struct bam_transaction *bam_txn = nandc->bam_txn;
++      int ret = 0;
++
++      if (nandc->props->supports_bam) {
++              if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) {
++                      ret = qcom_prepare_bam_async_desc(nandc, nandc->rx_chan, 0);
++                      if (ret)
++                              goto err_unmap_free_desc;
++              }
++
++              if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) {
++                      ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan,
++                                                        DMA_PREP_INTERRUPT);
++                      if (ret)
++                              goto err_unmap_free_desc;
++              }
++
++              if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) {
++                      ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan,
++                                                        DMA_PREP_CMD);
++                      if (ret)
++                              goto err_unmap_free_desc;
++              }
++      }
++
++      list_for_each_entry(desc, &nandc->desc_list, node)
++              cookie = dmaengine_submit(desc->dma_desc);
++
++      if (nandc->props->supports_bam) {
++              bam_txn->last_cmd_desc->callback = qcom_qpic_bam_dma_done;
++              bam_txn->last_cmd_desc->callback_param = bam_txn;
++
++              dma_async_issue_pending(nandc->tx_chan);
++              dma_async_issue_pending(nandc->rx_chan);
++              dma_async_issue_pending(nandc->cmd_chan);
++
++              if (!wait_for_completion_timeout(&bam_txn->txn_done,
++                                               QPIC_NAND_COMPLETION_TIMEOUT))
++                      ret = -ETIMEDOUT;
++      } else {
++              if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE)
++                      ret = -ETIMEDOUT;
++      }
++
++err_unmap_free_desc:
++      /*
++       * Unmap the dma sg_list and free the desc allocated by both
++       * qcom_prepare_bam_async_desc() and qcom_prep_adm_dma_desc() functions.
++       */
++      list_for_each_entry_safe(desc, n, &nandc->desc_list, node) {
++              list_del(&desc->node);
++
++              if (nandc->props->supports_bam)
++                      dma_unmap_sg(nandc->dev, desc->bam_sgl,
++                                   desc->sgl_cnt, desc->dir);
++              else
++                      dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1,
++                                   desc->dir);
++
++              kfree(desc);
++      }
++
++      return ret;
++}
++
++/**
++ * qcom_clear_read_regs() - reset the read register buffer
++ * @nandc: qpic nand controller
++ *
++ * This function reset the register read buffer for next NAND operation
++ */
++void qcom_clear_read_regs(struct qcom_nand_controller *nandc)
++{
++      nandc->reg_read_pos = 0;
++      qcom_nandc_dev_to_mem(nandc, false);
++}
++
++/**
++ * qcom_nandc_unalloc() - unallocate qpic nand controller
++ * @nandc: qpic nand controller
++ *
++ * This function will unallocate memory alloacted for qpic nand controller
++ */
++void qcom_nandc_unalloc(struct qcom_nand_controller *nandc)
++{
++      if (nandc->props->supports_bam) {
++              if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma))
++                      dma_unmap_single(nandc->dev, nandc->reg_read_dma,
++                                       MAX_REG_RD *
++                                       sizeof(*nandc->reg_read_buf),
++                                       DMA_FROM_DEVICE);
++
++              if (nandc->tx_chan)
++                      dma_release_channel(nandc->tx_chan);
++
++              if (nandc->rx_chan)
++                      dma_release_channel(nandc->rx_chan);
++
++              if (nandc->cmd_chan)
++                      dma_release_channel(nandc->cmd_chan);
++      } else {
++              if (nandc->chan)
++                      dma_release_channel(nandc->chan);
++      }
++}
++
++/**
++ * qcom_nandc_alloc() - Allocate qpic nand controller
++ * @nandc: qpic nand controller
++ *
++ * This function will allocate memory for qpic nand controller
++ */
++int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
++{
++      int ret;
++
++      ret = dma_set_coherent_mask(nandc->dev, DMA_BIT_MASK(32));
++      if (ret) {
++              dev_err(nandc->dev, "failed to set DMA mask\n");
++              return ret;
++      }
++
++      /*
++       * we use the internal buffer for reading ONFI params, reading small
++       * data like ID and status, and preforming read-copy-write operations
++       * when writing to a codeword partially. 532 is the maximum possible
++       * size of a codeword for our nand controller
++       */
++      nandc->buf_size = 532;
++
++      nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size, GFP_KERNEL);
++      if (!nandc->data_buffer)
++              return -ENOMEM;
++
++      nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs), GFP_KERNEL);
++      if (!nandc->regs)
++              return -ENOMEM;
++
++      nandc->reg_read_buf = devm_kcalloc(nandc->dev, MAX_REG_RD,
++                                         sizeof(*nandc->reg_read_buf),
++                                         GFP_KERNEL);
++      if (!nandc->reg_read_buf)
++              return -ENOMEM;
++
++      if (nandc->props->supports_bam) {
++              nandc->reg_read_dma =
++                      dma_map_single(nandc->dev, nandc->reg_read_buf,
++                                     MAX_REG_RD *
++                                     sizeof(*nandc->reg_read_buf),
++                                     DMA_FROM_DEVICE);
++              if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) {
++                      dev_err(nandc->dev, "failed to DMA MAP reg buffer\n");
++                      return -EIO;
++              }
++
++              nandc->tx_chan = dma_request_chan(nandc->dev, "tx");
++              if (IS_ERR(nandc->tx_chan)) {
++                      ret = PTR_ERR(nandc->tx_chan);
++                      nandc->tx_chan = NULL;
++                      dev_err_probe(nandc->dev, ret,
++                                    "tx DMA channel request failed\n");
++                      goto unalloc;
++              }
++
++              nandc->rx_chan = dma_request_chan(nandc->dev, "rx");
++              if (IS_ERR(nandc->rx_chan)) {
++                      ret = PTR_ERR(nandc->rx_chan);
++                      nandc->rx_chan = NULL;
++                      dev_err_probe(nandc->dev, ret,
++                                    "rx DMA channel request failed\n");
++                      goto unalloc;
++              }
++
++              nandc->cmd_chan = dma_request_chan(nandc->dev, "cmd");
++              if (IS_ERR(nandc->cmd_chan)) {
++                      ret = PTR_ERR(nandc->cmd_chan);
++                      nandc->cmd_chan = NULL;
++                      dev_err_probe(nandc->dev, ret,
++                                    "cmd DMA channel request failed\n");
++                      goto unalloc;
++              }
++
++              /*
++               * Initially allocate BAM transaction to read ONFI param page.
++               * After detecting all the devices, this BAM transaction will
++               * be freed and the next BAM transaction will be allocated with
++               * maximum codeword size
++               */
++              nandc->max_cwperpage = 1;
++              nandc->bam_txn = qcom_alloc_bam_transaction(nandc);
++              if (!nandc->bam_txn) {
++                      dev_err(nandc->dev,
++                              "failed to allocate bam transaction\n");
++                      ret = -ENOMEM;
++                      goto unalloc;
++              }
++      } else {
++              nandc->chan = dma_request_chan(nandc->dev, "rxtx");
++              if (IS_ERR(nandc->chan)) {
++                      ret = PTR_ERR(nandc->chan);
++                      nandc->chan = NULL;
++                      dev_err_probe(nandc->dev, ret,
++                                    "rxtx DMA channel request failed\n");
++                      return ret;
++              }
++      }
++
++      INIT_LIST_HEAD(&nandc->desc_list);
++      INIT_LIST_HEAD(&nandc->host_list);
++
++      return 0;
++unalloc:
++      qcom_nandc_unalloc(nandc);
++      return ret;
++}
+--- a/drivers/mtd/nand/raw/Kconfig
++++ b/drivers/mtd/nand/raw/Kconfig
+@@ -330,7 +330,7 @@ config MTD_NAND_HISI504
+         Enables support for NAND controller on Hisilicon SoC Hip04.
+ config MTD_NAND_QCOM
+-      tristate "QCOM NAND controller"
++      bool "QCOM NAND controller"
+       depends on ARCH_QCOM || COMPILE_TEST
+       depends on HAS_IOMEM
+       help
+--- a/drivers/mtd/nand/raw/qcom_nandc.c
++++ b/drivers/mtd/nand/raw/qcom_nandc.c
+@@ -15,417 +15,7 @@
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/slab.h>
+-
+-/* NANDc reg offsets */
+-#define       NAND_FLASH_CMD                  0x00
+-#define       NAND_ADDR0                      0x04
+-#define       NAND_ADDR1                      0x08
+-#define       NAND_FLASH_CHIP_SELECT          0x0c
+-#define       NAND_EXEC_CMD                   0x10
+-#define       NAND_FLASH_STATUS               0x14
+-#define       NAND_BUFFER_STATUS              0x18
+-#define       NAND_DEV0_CFG0                  0x20
+-#define       NAND_DEV0_CFG1                  0x24
+-#define       NAND_DEV0_ECC_CFG               0x28
+-#define       NAND_AUTO_STATUS_EN             0x2c
+-#define       NAND_DEV1_CFG0                  0x30
+-#define       NAND_DEV1_CFG1                  0x34
+-#define       NAND_READ_ID                    0x40
+-#define       NAND_READ_STATUS                0x44
+-#define       NAND_DEV_CMD0                   0xa0
+-#define       NAND_DEV_CMD1                   0xa4
+-#define       NAND_DEV_CMD2                   0xa8
+-#define       NAND_DEV_CMD_VLD                0xac
+-#define       SFLASHC_BURST_CFG               0xe0
+-#define       NAND_ERASED_CW_DETECT_CFG       0xe8
+-#define       NAND_ERASED_CW_DETECT_STATUS    0xec
+-#define       NAND_EBI2_ECC_BUF_CFG           0xf0
+-#define       FLASH_BUF_ACC                   0x100
+-
+-#define       NAND_CTRL                       0xf00
+-#define       NAND_VERSION                    0xf08
+-#define       NAND_READ_LOCATION_0            0xf20
+-#define       NAND_READ_LOCATION_1            0xf24
+-#define       NAND_READ_LOCATION_2            0xf28
+-#define       NAND_READ_LOCATION_3            0xf2c
+-#define       NAND_READ_LOCATION_LAST_CW_0    0xf40
+-#define       NAND_READ_LOCATION_LAST_CW_1    0xf44
+-#define       NAND_READ_LOCATION_LAST_CW_2    0xf48
+-#define       NAND_READ_LOCATION_LAST_CW_3    0xf4c
+-
+-/* dummy register offsets, used by qcom_write_reg_dma */
+-#define       NAND_DEV_CMD1_RESTORE           0xdead
+-#define       NAND_DEV_CMD_VLD_RESTORE        0xbeef
+-
+-/* NAND_FLASH_CMD bits */
+-#define       PAGE_ACC                        BIT(4)
+-#define       LAST_PAGE                       BIT(5)
+-
+-/* NAND_FLASH_CHIP_SELECT bits */
+-#define       NAND_DEV_SEL                    0
+-#define       DM_EN                           BIT(2)
+-
+-/* NAND_FLASH_STATUS bits */
+-#define       FS_OP_ERR                       BIT(4)
+-#define       FS_READY_BSY_N                  BIT(5)
+-#define       FS_MPU_ERR                      BIT(8)
+-#define       FS_DEVICE_STS_ERR               BIT(16)
+-#define       FS_DEVICE_WP                    BIT(23)
+-
+-/* NAND_BUFFER_STATUS bits */
+-#define       BS_UNCORRECTABLE_BIT            BIT(8)
+-#define       BS_CORRECTABLE_ERR_MSK          0x1f
+-
+-/* NAND_DEVn_CFG0 bits */
+-#define       DISABLE_STATUS_AFTER_WRITE      4
+-#define       CW_PER_PAGE                     6
+-#define       UD_SIZE_BYTES                   9
+-#define       UD_SIZE_BYTES_MASK              GENMASK(18, 9)
+-#define       ECC_PARITY_SIZE_BYTES_RS        19
+-#define       SPARE_SIZE_BYTES                23
+-#define       SPARE_SIZE_BYTES_MASK           GENMASK(26, 23)
+-#define       NUM_ADDR_CYCLES                 27
+-#define       STATUS_BFR_READ                 30
+-#define       SET_RD_MODE_AFTER_STATUS        31
+-
+-/* NAND_DEVn_CFG0 bits */
+-#define       DEV0_CFG1_ECC_DISABLE           0
+-#define       WIDE_FLASH                      1
+-#define       NAND_RECOVERY_CYCLES            2
+-#define       CS_ACTIVE_BSY                   5
+-#define       BAD_BLOCK_BYTE_NUM              6
+-#define       BAD_BLOCK_IN_SPARE_AREA         16
+-#define       WR_RD_BSY_GAP                   17
+-#define       ENABLE_BCH_ECC                  27
+-
+-/* NAND_DEV0_ECC_CFG bits */
+-#define       ECC_CFG_ECC_DISABLE             0
+-#define       ECC_SW_RESET                    1
+-#define       ECC_MODE                        4
+-#define       ECC_PARITY_SIZE_BYTES_BCH       8
+-#define       ECC_NUM_DATA_BYTES              16
+-#define       ECC_NUM_DATA_BYTES_MASK         GENMASK(25, 16)
+-#define       ECC_FORCE_CLK_OPEN              30
+-
+-/* NAND_DEV_CMD1 bits */
+-#define       READ_ADDR                       0
+-
+-/* NAND_DEV_CMD_VLD bits */
+-#define       READ_START_VLD                  BIT(0)
+-#define       READ_STOP_VLD                   BIT(1)
+-#define       WRITE_START_VLD                 BIT(2)
+-#define       ERASE_START_VLD                 BIT(3)
+-#define       SEQ_READ_START_VLD              BIT(4)
+-
+-/* NAND_EBI2_ECC_BUF_CFG bits */
+-#define       NUM_STEPS                       0
+-
+-/* NAND_ERASED_CW_DETECT_CFG bits */
+-#define       ERASED_CW_ECC_MASK              1
+-#define       AUTO_DETECT_RES                 0
+-#define       MASK_ECC                        BIT(ERASED_CW_ECC_MASK)
+-#define       RESET_ERASED_DET                BIT(AUTO_DETECT_RES)
+-#define       ACTIVE_ERASED_DET               (0 << AUTO_DETECT_RES)
+-#define       CLR_ERASED_PAGE_DET             (RESET_ERASED_DET | MASK_ECC)
+-#define       SET_ERASED_PAGE_DET             (ACTIVE_ERASED_DET | MASK_ECC)
+-
+-/* NAND_ERASED_CW_DETECT_STATUS bits */
+-#define       PAGE_ALL_ERASED                 BIT(7)
+-#define       CODEWORD_ALL_ERASED             BIT(6)
+-#define       PAGE_ERASED                     BIT(5)
+-#define       CODEWORD_ERASED                 BIT(4)
+-#define       ERASED_PAGE                     (PAGE_ALL_ERASED | PAGE_ERASED)
+-#define       ERASED_CW                       (CODEWORD_ALL_ERASED | CODEWORD_ERASED)
+-
+-/* NAND_READ_LOCATION_n bits */
+-#define READ_LOCATION_OFFSET          0
+-#define READ_LOCATION_SIZE            16
+-#define READ_LOCATION_LAST            31
+-
+-/* Version Mask */
+-#define       NAND_VERSION_MAJOR_MASK         0xf0000000
+-#define       NAND_VERSION_MAJOR_SHIFT        28
+-#define       NAND_VERSION_MINOR_MASK         0x0fff0000
+-#define       NAND_VERSION_MINOR_SHIFT        16
+-
+-/* NAND OP_CMDs */
+-#define       OP_PAGE_READ                    0x2
+-#define       OP_PAGE_READ_WITH_ECC           0x3
+-#define       OP_PAGE_READ_WITH_ECC_SPARE     0x4
+-#define       OP_PAGE_READ_ONFI_READ          0x5
+-#define       OP_PROGRAM_PAGE                 0x6
+-#define       OP_PAGE_PROGRAM_WITH_ECC        0x7
+-#define       OP_PROGRAM_PAGE_SPARE           0x9
+-#define       OP_BLOCK_ERASE                  0xa
+-#define       OP_CHECK_STATUS                 0xc
+-#define       OP_FETCH_ID                     0xb
+-#define       OP_RESET_DEVICE                 0xd
+-
+-/* Default Value for NAND_DEV_CMD_VLD */
+-#define NAND_DEV_CMD_VLD_VAL          (READ_START_VLD | WRITE_START_VLD | \
+-                                       ERASE_START_VLD | SEQ_READ_START_VLD)
+-
+-/* NAND_CTRL bits */
+-#define       BAM_MODE_EN                     BIT(0)
+-
+-/*
+- * the NAND controller performs reads/writes with ECC in 516 byte chunks.
+- * the driver calls the chunks 'step' or 'codeword' interchangeably
+- */
+-#define       NANDC_STEP_SIZE                 512
+-
+-/*
+- * the largest page size we support is 8K, this will have 16 steps/codewords
+- * of 512 bytes each
+- */
+-#define       MAX_NUM_STEPS                   (SZ_8K / NANDC_STEP_SIZE)
+-
+-/* we read at most 3 registers per codeword scan */
+-#define       MAX_REG_RD                      (3 * MAX_NUM_STEPS)
+-
+-/* ECC modes supported by the controller */
+-#define       ECC_NONE        BIT(0)
+-#define       ECC_RS_4BIT     BIT(1)
+-#define       ECC_BCH_4BIT    BIT(2)
+-#define       ECC_BCH_8BIT    BIT(3)
+-
+-/*
+- * Returns the actual register address for all NAND_DEV_ registers
+- * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD)
+- */
+-#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg))
+-
+-/* Returns the NAND register physical address */
+-#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset))
+-
+-/* Returns the dma address for reg read buffer */
+-#define reg_buf_dma_addr(chip, vaddr) \
+-      ((chip)->reg_read_dma + \
+-      ((u8 *)(vaddr) - (u8 *)(chip)->reg_read_buf))
+-
+-#define QPIC_PER_CW_CMD_ELEMENTS      32
+-#define QPIC_PER_CW_CMD_SGL           32
+-#define QPIC_PER_CW_DATA_SGL          8
+-
+-#define QPIC_NAND_COMPLETION_TIMEOUT  msecs_to_jiffies(2000)
+-
+-/*
+- * Flags used in DMA descriptor preparation helper functions
+- * (i.e. qcom_read_reg_dma/qcom_write_reg_dma/qcom_read_data_dma/qcom_write_data_dma)
+- */
+-/* Don't set the EOT in current tx BAM sgl */
+-#define NAND_BAM_NO_EOT                       BIT(0)
+-/* Set the NWD flag in current BAM sgl */
+-#define NAND_BAM_NWD                  BIT(1)
+-/* Finish writing in the current BAM sgl and start writing in another BAM sgl */
+-#define NAND_BAM_NEXT_SGL             BIT(2)
+-/*
+- * Erased codeword status is being used two times in single transfer so this
+- * flag will determine the current value of erased codeword status register
+- */
+-#define NAND_ERASED_CW_SET            BIT(4)
+-
+-#define MAX_ADDRESS_CYCLE             5
+-
+-/*
+- * This data type corresponds to the BAM transaction which will be used for all
+- * NAND transfers.
+- * @bam_ce - the array of BAM command elements
+- * @cmd_sgl - sgl for NAND BAM command pipe
+- * @data_sgl - sgl for NAND BAM consumer/producer pipe
+- * @last_data_desc - last DMA desc in data channel (tx/rx).
+- * @last_cmd_desc - last DMA desc in command channel.
+- * @txn_done - completion for NAND transfer.
+- * @bam_ce_pos - the index in bam_ce which is available for next sgl
+- * @bam_ce_start - the index in bam_ce which marks the start position ce
+- *               for current sgl. It will be used for size calculation
+- *               for current sgl
+- * @cmd_sgl_pos - current index in command sgl.
+- * @cmd_sgl_start - start index in command sgl.
+- * @tx_sgl_pos - current index in data sgl for tx.
+- * @tx_sgl_start - start index in data sgl for tx.
+- * @rx_sgl_pos - current index in data sgl for rx.
+- * @rx_sgl_start - start index in data sgl for rx.
+- */
+-struct bam_transaction {
+-      struct bam_cmd_element *bam_ce;
+-      struct scatterlist *cmd_sgl;
+-      struct scatterlist *data_sgl;
+-      struct dma_async_tx_descriptor *last_data_desc;
+-      struct dma_async_tx_descriptor *last_cmd_desc;
+-      struct completion txn_done;
+-      u32 bam_ce_pos;
+-      u32 bam_ce_start;
+-      u32 cmd_sgl_pos;
+-      u32 cmd_sgl_start;
+-      u32 tx_sgl_pos;
+-      u32 tx_sgl_start;
+-      u32 rx_sgl_pos;
+-      u32 rx_sgl_start;
+-};
+-
+-/*
+- * This data type corresponds to the nand dma descriptor
+- * @dma_desc - low level DMA engine descriptor
+- * @list - list for desc_info
+- *
+- * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by
+- *          ADM
+- * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM
+- * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM
+- * @dir - DMA transfer direction
+- */
+-struct desc_info {
+-      struct dma_async_tx_descriptor *dma_desc;
+-      struct list_head node;
+-
+-      union {
+-              struct scatterlist adm_sgl;
+-              struct {
+-                      struct scatterlist *bam_sgl;
+-                      int sgl_cnt;
+-              };
+-      };
+-      enum dma_data_direction dir;
+-};
+-
+-/*
+- * holds the current register values that we want to write. acts as a contiguous
+- * chunk of memory which we use to write the controller registers through DMA.
+- */
+-struct nandc_regs {
+-      __le32 cmd;
+-      __le32 addr0;
+-      __le32 addr1;
+-      __le32 chip_sel;
+-      __le32 exec;
+-
+-      __le32 cfg0;
+-      __le32 cfg1;
+-      __le32 ecc_bch_cfg;
+-
+-      __le32 clrflashstatus;
+-      __le32 clrreadstatus;
+-
+-      __le32 cmd1;
+-      __le32 vld;
+-
+-      __le32 orig_cmd1;
+-      __le32 orig_vld;
+-
+-      __le32 ecc_buf_cfg;
+-      __le32 read_location0;
+-      __le32 read_location1;
+-      __le32 read_location2;
+-      __le32 read_location3;
+-      __le32 read_location_last0;
+-      __le32 read_location_last1;
+-      __le32 read_location_last2;
+-      __le32 read_location_last3;
+-
+-      __le32 erased_cw_detect_cfg_clr;
+-      __le32 erased_cw_detect_cfg_set;
+-};
+-
+-/*
+- * NAND controller data struct
+- *
+- * @dev:                      parent device
+- *
+- * @base:                     MMIO base
+- *
+- * @core_clk:                 controller clock
+- * @aon_clk:                  another controller clock
+- *
+- * @regs:                     a contiguous chunk of memory for DMA register
+- *                            writes. contains the register values to be
+- *                            written to controller
+- *
+- * @props:                    properties of current NAND controller,
+- *                            initialized via DT match data
+- *
+- * @controller:                       base controller structure
+- * @host_list:                        list containing all the chips attached to the
+- *                            controller
+- *
+- * @chan:                     dma channel
+- * @cmd_crci:                 ADM DMA CRCI for command flow control
+- * @data_crci:                        ADM DMA CRCI for data flow control
+- *
+- * @desc_list:                        DMA descriptor list (list of desc_infos)
+- *
+- * @data_buffer:              our local DMA buffer for page read/writes,
+- *                            used when we can't use the buffer provided
+- *                            by upper layers directly
+- * @reg_read_buf:             local buffer for reading back registers via DMA
+- *
+- * @base_phys:                        physical base address of controller registers
+- * @base_dma:                 dma base address of controller registers
+- * @reg_read_dma:             contains dma address for register read buffer
+- *
+- * @buf_size/count/start:     markers for chip->legacy.read_buf/write_buf
+- *                            functions
+- * @max_cwperpage:            maximum QPIC codewords required. calculated
+- *                            from all connected NAND devices pagesize
+- *
+- * @reg_read_pos:             marker for data read in reg_read_buf
+- *
+- * @cmd1/vld:                 some fixed controller register values
+- *
+- * @exec_opwrite:             flag to select correct number of code word
+- *                            while reading status
+- */
+-struct qcom_nand_controller {
+-      struct device *dev;
+-
+-      void __iomem *base;
+-
+-      struct clk *core_clk;
+-      struct clk *aon_clk;
+-
+-      struct nandc_regs *regs;
+-      struct bam_transaction *bam_txn;
+-
+-      const struct qcom_nandc_props *props;
+-
+-      struct nand_controller controller;
+-      struct list_head host_list;
+-
+-      union {
+-              /* will be used only by QPIC for BAM DMA */
+-              struct {
+-                      struct dma_chan *tx_chan;
+-                      struct dma_chan *rx_chan;
+-                      struct dma_chan *cmd_chan;
+-              };
+-
+-              /* will be used only by EBI2 for ADM DMA */
+-              struct {
+-                      struct dma_chan *chan;
+-                      unsigned int cmd_crci;
+-                      unsigned int data_crci;
+-              };
+-      };
+-
+-      struct list_head desc_list;
+-
+-      u8              *data_buffer;
+-      __le32          *reg_read_buf;
+-
+-      phys_addr_t base_phys;
+-      dma_addr_t base_dma;
+-      dma_addr_t reg_read_dma;
+-
+-      int             buf_size;
+-      int             buf_count;
+-      int             buf_start;
+-      unsigned int    max_cwperpage;
+-
+-      int reg_read_pos;
+-
+-      u32 cmd1, vld;
+-      bool exec_opwrite;
+-};
++#include <linux/mtd/nand-qpic-common.h>
+ /*
+  * NAND special boot partitions
+@@ -530,97 +120,6 @@ struct qcom_nand_host {
+       bool bch_enabled;
+ };
+-/*
+- * This data type corresponds to the NAND controller properties which varies
+- * among different NAND controllers.
+- * @ecc_modes - ecc mode for NAND
+- * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
+- * @supports_bam - whether NAND controller is using BAM
+- * @nandc_part_of_qpic - whether NAND controller is part of qpic IP
+- * @qpic_version2 - flag to indicate QPIC IP version 2
+- * @use_codeword_fixup - whether NAND has different layout for boot partitions
+- */
+-struct qcom_nandc_props {
+-      u32 ecc_modes;
+-      u32 dev_cmd_reg_start;
+-      bool supports_bam;
+-      bool nandc_part_of_qpic;
+-      bool qpic_version2;
+-      bool use_codeword_fixup;
+-};
+-
+-/* Frees the BAM transaction memory */
+-static void qcom_free_bam_transaction(struct qcom_nand_controller *nandc)
+-{
+-      struct bam_transaction *bam_txn = nandc->bam_txn;
+-
+-      devm_kfree(nandc->dev, bam_txn);
+-}
+-
+-/* Allocates and Initializes the BAM transaction */
+-static struct bam_transaction *
+-qcom_alloc_bam_transaction(struct qcom_nand_controller *nandc)
+-{
+-      struct bam_transaction *bam_txn;
+-      size_t bam_txn_size;
+-      unsigned int num_cw = nandc->max_cwperpage;
+-      void *bam_txn_buf;
+-
+-      bam_txn_size =
+-              sizeof(*bam_txn) + num_cw *
+-              ((sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS) +
+-              (sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL) +
+-              (sizeof(*bam_txn->data_sgl) * QPIC_PER_CW_DATA_SGL));
+-
+-      bam_txn_buf = devm_kzalloc(nandc->dev, bam_txn_size, GFP_KERNEL);
+-      if (!bam_txn_buf)
+-              return NULL;
+-
+-      bam_txn = bam_txn_buf;
+-      bam_txn_buf += sizeof(*bam_txn);
+-
+-      bam_txn->bam_ce = bam_txn_buf;
+-      bam_txn_buf +=
+-              sizeof(*bam_txn->bam_ce) * QPIC_PER_CW_CMD_ELEMENTS * num_cw;
+-
+-      bam_txn->cmd_sgl = bam_txn_buf;
+-      bam_txn_buf +=
+-              sizeof(*bam_txn->cmd_sgl) * QPIC_PER_CW_CMD_SGL * num_cw;
+-
+-      bam_txn->data_sgl = bam_txn_buf;
+-
+-      init_completion(&bam_txn->txn_done);
+-
+-      return bam_txn;
+-}
+-
+-/* Clears the BAM transaction indexes */
+-static void qcom_clear_bam_transaction(struct qcom_nand_controller *nandc)
+-{
+-      struct bam_transaction *bam_txn = nandc->bam_txn;
+-
+-      if (!nandc->props->supports_bam)
+-              return;
+-
+-      memset(&bam_txn->bam_ce_pos, 0, sizeof(u32) * 8);
+-      bam_txn->last_data_desc = NULL;
+-
+-      sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage *
+-                    QPIC_PER_CW_CMD_SGL);
+-      sg_init_table(bam_txn->data_sgl, nandc->max_cwperpage *
+-                    QPIC_PER_CW_DATA_SGL);
+-
+-      reinit_completion(&bam_txn->txn_done);
+-}
+-
+-/* Callback for DMA descriptor completion */
+-static void qcom_qpic_bam_dma_done(void *data)
+-{
+-      struct bam_transaction *bam_txn = data;
+-
+-      complete(&bam_txn->txn_done);
+-}
+-
+ static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip)
+ {
+       return container_of(chip, struct qcom_nand_host, chip);
+@@ -629,8 +128,8 @@ static inline struct qcom_nand_host *to_
+ static inline struct qcom_nand_controller *
+ get_qcom_nand_controller(struct nand_chip *chip)
+ {
+-      return container_of(chip->controller, struct qcom_nand_controller,
+-                          controller);
++      return (struct qcom_nand_controller *)
++              ((u8 *)chip->controller - sizeof(struct qcom_nand_controller));
+ }
+ static inline u32 nandc_read(struct qcom_nand_controller *nandc, int offset)
+@@ -644,23 +143,6 @@ static inline void nandc_write(struct qc
+       iowrite32(val, nandc->base + offset);
+ }
+-static inline void qcom_nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu)
+-{
+-      if (!nandc->props->supports_bam)
+-              return;
+-
+-      if (is_cpu)
+-              dma_sync_single_for_cpu(nandc->dev, nandc->reg_read_dma,
+-                                      MAX_REG_RD *
+-                                      sizeof(*nandc->reg_read_buf),
+-                                      DMA_FROM_DEVICE);
+-      else
+-              dma_sync_single_for_device(nandc->dev, nandc->reg_read_dma,
+-                                         MAX_REG_RD *
+-                                         sizeof(*nandc->reg_read_buf),
+-                                         DMA_FROM_DEVICE);
+-}
+-
+ /* Helper to check the code word, whether it is last cw or not */
+ static bool qcom_nandc_is_last_cw(struct nand_ecc_ctrl *ecc, int cw)
+ {
+@@ -820,356 +302,6 @@ static void update_rw_regs(struct qcom_n
+ }
+ /*
+- * Maps the scatter gather list for DMA transfer and forms the DMA descriptor
+- * for BAM. This descriptor will be added in the NAND DMA descriptor queue
+- * which will be submitted to DMA engine.
+- */
+-static int qcom_prepare_bam_async_desc(struct qcom_nand_controller *nandc,
+-                                     struct dma_chan *chan,
+-                                     unsigned long flags)
+-{
+-      struct desc_info *desc;
+-      struct scatterlist *sgl;
+-      unsigned int sgl_cnt;
+-      int ret;
+-      struct bam_transaction *bam_txn = nandc->bam_txn;
+-      enum dma_transfer_direction dir_eng;
+-      struct dma_async_tx_descriptor *dma_desc;
+-
+-      desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+-      if (!desc)
+-              return -ENOMEM;
+-
+-      if (chan == nandc->cmd_chan) {
+-              sgl = &bam_txn->cmd_sgl[bam_txn->cmd_sgl_start];
+-              sgl_cnt = bam_txn->cmd_sgl_pos - bam_txn->cmd_sgl_start;
+-              bam_txn->cmd_sgl_start = bam_txn->cmd_sgl_pos;
+-              dir_eng = DMA_MEM_TO_DEV;
+-              desc->dir = DMA_TO_DEVICE;
+-      } else if (chan == nandc->tx_chan) {
+-              sgl = &bam_txn->data_sgl[bam_txn->tx_sgl_start];
+-              sgl_cnt = bam_txn->tx_sgl_pos - bam_txn->tx_sgl_start;
+-              bam_txn->tx_sgl_start = bam_txn->tx_sgl_pos;
+-              dir_eng = DMA_MEM_TO_DEV;
+-              desc->dir = DMA_TO_DEVICE;
+-      } else {
+-              sgl = &bam_txn->data_sgl[bam_txn->rx_sgl_start];
+-              sgl_cnt = bam_txn->rx_sgl_pos - bam_txn->rx_sgl_start;
+-              bam_txn->rx_sgl_start = bam_txn->rx_sgl_pos;
+-              dir_eng = DMA_DEV_TO_MEM;
+-              desc->dir = DMA_FROM_DEVICE;
+-      }
+-
+-      sg_mark_end(sgl + sgl_cnt - 1);
+-      ret = dma_map_sg(nandc->dev, sgl, sgl_cnt, desc->dir);
+-      if (ret == 0) {
+-              dev_err(nandc->dev, "failure in mapping desc\n");
+-              kfree(desc);
+-              return -ENOMEM;
+-      }
+-
+-      desc->sgl_cnt = sgl_cnt;
+-      desc->bam_sgl = sgl;
+-
+-      dma_desc = dmaengine_prep_slave_sg(chan, sgl, sgl_cnt, dir_eng,
+-                                         flags);
+-
+-      if (!dma_desc) {
+-              dev_err(nandc->dev, "failure in prep desc\n");
+-              dma_unmap_sg(nandc->dev, sgl, sgl_cnt, desc->dir);
+-              kfree(desc);
+-              return -EINVAL;
+-      }
+-
+-      desc->dma_desc = dma_desc;
+-
+-      /* update last data/command descriptor */
+-      if (chan == nandc->cmd_chan)
+-              bam_txn->last_cmd_desc = dma_desc;
+-      else
+-              bam_txn->last_data_desc = dma_desc;
+-
+-      list_add_tail(&desc->node, &nandc->desc_list);
+-
+-      return 0;
+-}
+-
+-/*
+- * Prepares the command descriptor for BAM DMA which will be used for NAND
+- * register reads and writes. The command descriptor requires the command
+- * to be formed in command element type so this function uses the command
+- * element from bam transaction ce array and fills the same with required
+- * data. A single SGL can contain multiple command elements so
+- * NAND_BAM_NEXT_SGL will be used for starting the separate SGL
+- * after the current command element.
+- */
+-static int qcom_prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read,
+-                                    int reg_off, const void *vaddr,
+-                                    int size, unsigned int flags)
+-{
+-      int bam_ce_size;
+-      int i, ret;
+-      struct bam_cmd_element *bam_ce_buffer;
+-      struct bam_transaction *bam_txn = nandc->bam_txn;
+-
+-      bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_pos];
+-
+-      /* fill the command desc */
+-      for (i = 0; i < size; i++) {
+-              if (read)
+-                      bam_prep_ce(&bam_ce_buffer[i],
+-                                  nandc_reg_phys(nandc, reg_off + 4 * i),
+-                                  BAM_READ_COMMAND,
+-                                  reg_buf_dma_addr(nandc,
+-                                                   (__le32 *)vaddr + i));
+-              else
+-                      bam_prep_ce_le32(&bam_ce_buffer[i],
+-                                       nandc_reg_phys(nandc, reg_off + 4 * i),
+-                                       BAM_WRITE_COMMAND,
+-                                       *((__le32 *)vaddr + i));
+-      }
+-
+-      bam_txn->bam_ce_pos += size;
+-
+-      /* use the separate sgl after this command */
+-      if (flags & NAND_BAM_NEXT_SGL) {
+-              bam_ce_buffer = &bam_txn->bam_ce[bam_txn->bam_ce_start];
+-              bam_ce_size = (bam_txn->bam_ce_pos -
+-                              bam_txn->bam_ce_start) *
+-                              sizeof(struct bam_cmd_element);
+-              sg_set_buf(&bam_txn->cmd_sgl[bam_txn->cmd_sgl_pos],
+-                         bam_ce_buffer, bam_ce_size);
+-              bam_txn->cmd_sgl_pos++;
+-              bam_txn->bam_ce_start = bam_txn->bam_ce_pos;
+-
+-              if (flags & NAND_BAM_NWD) {
+-                      ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan,
+-                                                        DMA_PREP_FENCE |
+-                                                        DMA_PREP_CMD);
+-                      if (ret)
+-                              return ret;
+-              }
+-      }
+-
+-      return 0;
+-}
+-
+-/*
+- * Prepares the data descriptor for BAM DMA which will be used for NAND
+- * data reads and writes.
+- */
+-static int qcom_prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read,
+-                                     const void *vaddr, int size, unsigned int flags)
+-{
+-      int ret;
+-      struct bam_transaction *bam_txn = nandc->bam_txn;
+-
+-      if (read) {
+-              sg_set_buf(&bam_txn->data_sgl[bam_txn->rx_sgl_pos],
+-                         vaddr, size);
+-              bam_txn->rx_sgl_pos++;
+-      } else {
+-              sg_set_buf(&bam_txn->data_sgl[bam_txn->tx_sgl_pos],
+-                         vaddr, size);
+-              bam_txn->tx_sgl_pos++;
+-
+-              /*
+-               * BAM will only set EOT for DMA_PREP_INTERRUPT so if this flag
+-               * is not set, form the DMA descriptor
+-               */
+-              if (!(flags & NAND_BAM_NO_EOT)) {
+-                      ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan,
+-                                                        DMA_PREP_INTERRUPT);
+-                      if (ret)
+-                              return ret;
+-              }
+-      }
+-
+-      return 0;
+-}
+-
+-static int qcom_prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read,
+-                                int reg_off, const void *vaddr, int size,
+-                                bool flow_control)
+-{
+-      struct desc_info *desc;
+-      struct dma_async_tx_descriptor *dma_desc;
+-      struct scatterlist *sgl;
+-      struct dma_slave_config slave_conf;
+-      struct qcom_adm_peripheral_config periph_conf = {};
+-      enum dma_transfer_direction dir_eng;
+-      int ret;
+-
+-      desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+-      if (!desc)
+-              return -ENOMEM;
+-
+-      sgl = &desc->adm_sgl;
+-
+-      sg_init_one(sgl, vaddr, size);
+-
+-      if (read) {
+-              dir_eng = DMA_DEV_TO_MEM;
+-              desc->dir = DMA_FROM_DEVICE;
+-      } else {
+-              dir_eng = DMA_MEM_TO_DEV;
+-              desc->dir = DMA_TO_DEVICE;
+-      }
+-
+-      ret = dma_map_sg(nandc->dev, sgl, 1, desc->dir);
+-      if (ret == 0) {
+-              ret = -ENOMEM;
+-              goto err;
+-      }
+-
+-      memset(&slave_conf, 0x00, sizeof(slave_conf));
+-
+-      slave_conf.device_fc = flow_control;
+-      if (read) {
+-              slave_conf.src_maxburst = 16;
+-              slave_conf.src_addr = nandc->base_dma + reg_off;
+-              if (nandc->data_crci) {
+-                      periph_conf.crci = nandc->data_crci;
+-                      slave_conf.peripheral_config = &periph_conf;
+-                      slave_conf.peripheral_size = sizeof(periph_conf);
+-              }
+-      } else {
+-              slave_conf.dst_maxburst = 16;
+-              slave_conf.dst_addr = nandc->base_dma + reg_off;
+-              if (nandc->cmd_crci) {
+-                      periph_conf.crci = nandc->cmd_crci;
+-                      slave_conf.peripheral_config = &periph_conf;
+-                      slave_conf.peripheral_size = sizeof(periph_conf);
+-              }
+-      }
+-
+-      ret = dmaengine_slave_config(nandc->chan, &slave_conf);
+-      if (ret) {
+-              dev_err(nandc->dev, "failed to configure dma channel\n");
+-              goto err;
+-      }
+-
+-      dma_desc = dmaengine_prep_slave_sg(nandc->chan, sgl, 1, dir_eng, 0);
+-      if (!dma_desc) {
+-              dev_err(nandc->dev, "failed to prepare desc\n");
+-              ret = -EINVAL;
+-              goto err;
+-      }
+-
+-      desc->dma_desc = dma_desc;
+-
+-      list_add_tail(&desc->node, &nandc->desc_list);
+-
+-      return 0;
+-err:
+-      kfree(desc);
+-
+-      return ret;
+-}
+-
+-/*
+- * qcom_read_reg_dma: prepares a descriptor to read a given number of
+- *                    contiguous registers to the reg_read_buf pointer
+- *
+- * @first:            offset of the first register in the contiguous block
+- * @num_regs:         number of registers to read
+- * @flags:            flags to control DMA descriptor preparation
+- */
+-static int qcom_read_reg_dma(struct qcom_nand_controller *nandc, int first,
+-                           int num_regs, unsigned int flags)
+-{
+-      bool flow_control = false;
+-      void *vaddr;
+-
+-      vaddr = nandc->reg_read_buf + nandc->reg_read_pos;
+-      nandc->reg_read_pos += num_regs;
+-
+-      if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1)
+-              first = dev_cmd_reg_addr(nandc, first);
+-
+-      if (nandc->props->supports_bam)
+-              return qcom_prep_bam_dma_desc_cmd(nandc, true, first, vaddr,
+-                                           num_regs, flags);
+-
+-      if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
+-              flow_control = true;
+-
+-      return qcom_prep_adm_dma_desc(nandc, true, first, vaddr,
+-                               num_regs * sizeof(u32), flow_control);
+-}
+-
+-/*
+- * qcom_write_reg_dma:        prepares a descriptor to write a given number of
+- *                    contiguous registers
+- *
+- * @vaddr:            contnigeous memory from where register value will
+- *                    be written
+- * @first:            offset of the first register in the contiguous block
+- * @num_regs:         number of registers to write
+- * @flags:            flags to control DMA descriptor preparation
+- */
+-static int qcom_write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr,
+-                            int first, int num_regs, unsigned int flags)
+-{
+-      bool flow_control = false;
+-
+-      if (first == NAND_EXEC_CMD)
+-              flags |= NAND_BAM_NWD;
+-
+-      if (first == NAND_DEV_CMD1_RESTORE || first == NAND_DEV_CMD1)
+-              first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD1);
+-
+-      if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD)
+-              first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD);
+-
+-      if (nandc->props->supports_bam)
+-              return qcom_prep_bam_dma_desc_cmd(nandc, false, first, vaddr,
+-                                           num_regs, flags);
+-
+-      if (first == NAND_FLASH_CMD)
+-              flow_control = true;
+-
+-      return qcom_prep_adm_dma_desc(nandc, false, first, vaddr,
+-                               num_regs * sizeof(u32), flow_control);
+-}
+-
+-/*
+- * qcom_read_data_dma:        prepares a DMA descriptor to transfer data from the
+- *                    controller's internal buffer to the buffer 'vaddr'
+- *
+- * @reg_off:          offset within the controller's data buffer
+- * @vaddr:            virtual address of the buffer we want to write to
+- * @size:             DMA transaction size in bytes
+- * @flags:            flags to control DMA descriptor preparation
+- */
+-static int qcom_read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
+-                            const u8 *vaddr, int size, unsigned int flags)
+-{
+-      if (nandc->props->supports_bam)
+-              return qcom_prep_bam_dma_desc_data(nandc, true, vaddr, size, flags);
+-
+-      return qcom_prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false);
+-}
+-
+-/*
+- * qcom_write_data_dma:       prepares a DMA descriptor to transfer data from
+- *                    'vaddr' to the controller's internal buffer
+- *
+- * @reg_off:          offset within the controller's data buffer
+- * @vaddr:            virtual address of the buffer we want to read from
+- * @size:             DMA transaction size in bytes
+- * @flags:            flags to control DMA descriptor preparation
+- */
+-static int qcom_write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
+-                             const u8 *vaddr, int size, unsigned int flags)
+-{
+-      if (nandc->props->supports_bam)
+-              return qcom_prep_bam_dma_desc_data(nandc, false, vaddr, size, flags);
+-
+-      return qcom_prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false);
+-}
+-
+-/*
+  * Helper to prepare DMA descriptors for configuring registers
+  * before reading a NAND page.
+  */
+@@ -1262,83 +394,6 @@ static void config_nand_cw_write(struct
+                          NAND_BAM_NEXT_SGL);
+ }
+-/* helpers to submit/free our list of dma descriptors */
+-static int qcom_submit_descs(struct qcom_nand_controller *nandc)
+-{
+-      struct desc_info *desc, *n;
+-      dma_cookie_t cookie = 0;
+-      struct bam_transaction *bam_txn = nandc->bam_txn;
+-      int ret = 0;
+-
+-      if (nandc->props->supports_bam) {
+-              if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) {
+-                      ret = qcom_prepare_bam_async_desc(nandc, nandc->rx_chan, 0);
+-                      if (ret)
+-                              goto err_unmap_free_desc;
+-              }
+-
+-              if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) {
+-                      ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan,
+-                                                        DMA_PREP_INTERRUPT);
+-                      if (ret)
+-                              goto err_unmap_free_desc;
+-              }
+-
+-              if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) {
+-                      ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan,
+-                                                        DMA_PREP_CMD);
+-                      if (ret)
+-                              goto err_unmap_free_desc;
+-              }
+-      }
+-
+-      list_for_each_entry(desc, &nandc->desc_list, node)
+-              cookie = dmaengine_submit(desc->dma_desc);
+-
+-      if (nandc->props->supports_bam) {
+-              bam_txn->last_cmd_desc->callback = qcom_qpic_bam_dma_done;
+-              bam_txn->last_cmd_desc->callback_param = bam_txn;
+-
+-              dma_async_issue_pending(nandc->tx_chan);
+-              dma_async_issue_pending(nandc->rx_chan);
+-              dma_async_issue_pending(nandc->cmd_chan);
+-
+-              if (!wait_for_completion_timeout(&bam_txn->txn_done,
+-                                               QPIC_NAND_COMPLETION_TIMEOUT))
+-                      ret = -ETIMEDOUT;
+-      } else {
+-              if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE)
+-                      ret = -ETIMEDOUT;
+-      }
+-
+-err_unmap_free_desc:
+-      /*
+-       * Unmap the dma sg_list and free the desc allocated by both
+-       * qcom_prepare_bam_async_desc() and qcom_prep_adm_dma_desc() functions.
+-       */
+-      list_for_each_entry_safe(desc, n, &nandc->desc_list, node) {
+-              list_del(&desc->node);
+-
+-              if (nandc->props->supports_bam)
+-                      dma_unmap_sg(nandc->dev, desc->bam_sgl,
+-                                   desc->sgl_cnt, desc->dir);
+-              else
+-                      dma_unmap_sg(nandc->dev, &desc->adm_sgl, 1,
+-                                   desc->dir);
+-
+-              kfree(desc);
+-      }
+-
+-      return ret;
+-}
+-
+-/* reset the register read buffer for next NAND operation */
+-static void qcom_clear_read_regs(struct qcom_nand_controller *nandc)
+-{
+-      nandc->reg_read_pos = 0;
+-      qcom_nandc_dev_to_mem(nandc, false);
+-}
+-
+ /*
+  * when using BCH ECC, the HW flags an error in NAND_FLASH_STATUS if it read
+  * an erased CW, and reports an erased CW in NAND_ERASED_CW_DETECT_STATUS.
+@@ -2967,141 +2022,14 @@ static const struct nand_controller_ops
+       .exec_op = qcom_nand_exec_op,
+ };
+-static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc)
+-{
+-      if (nandc->props->supports_bam) {
+-              if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma))
+-                      dma_unmap_single(nandc->dev, nandc->reg_read_dma,
+-                                       MAX_REG_RD *
+-                                       sizeof(*nandc->reg_read_buf),
+-                                       DMA_FROM_DEVICE);
+-
+-              if (nandc->tx_chan)
+-                      dma_release_channel(nandc->tx_chan);
+-
+-              if (nandc->rx_chan)
+-                      dma_release_channel(nandc->rx_chan);
+-
+-              if (nandc->cmd_chan)
+-                      dma_release_channel(nandc->cmd_chan);
+-      } else {
+-              if (nandc->chan)
+-                      dma_release_channel(nandc->chan);
+-      }
+-}
+-
+-static int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
+-{
+-      int ret;
+-
+-      ret = dma_set_coherent_mask(nandc->dev, DMA_BIT_MASK(32));
+-      if (ret) {
+-              dev_err(nandc->dev, "failed to set DMA mask\n");
+-              return ret;
+-      }
+-
+-      /*
+-       * we use the internal buffer for reading ONFI params, reading small
+-       * data like ID and status, and preforming read-copy-write operations
+-       * when writing to a codeword partially. 532 is the maximum possible
+-       * size of a codeword for our nand controller
+-       */
+-      nandc->buf_size = 532;
+-
+-      nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size, GFP_KERNEL);
+-      if (!nandc->data_buffer)
+-              return -ENOMEM;
+-
+-      nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs), GFP_KERNEL);
+-      if (!nandc->regs)
+-              return -ENOMEM;
+-
+-      nandc->reg_read_buf = devm_kcalloc(nandc->dev, MAX_REG_RD,
+-                                         sizeof(*nandc->reg_read_buf),
+-                                         GFP_KERNEL);
+-      if (!nandc->reg_read_buf)
+-              return -ENOMEM;
+-
+-      if (nandc->props->supports_bam) {
+-              nandc->reg_read_dma =
+-                      dma_map_single(nandc->dev, nandc->reg_read_buf,
+-                                     MAX_REG_RD *
+-                                     sizeof(*nandc->reg_read_buf),
+-                                     DMA_FROM_DEVICE);
+-              if (dma_mapping_error(nandc->dev, nandc->reg_read_dma)) {
+-                      dev_err(nandc->dev, "failed to DMA MAP reg buffer\n");
+-                      return -EIO;
+-              }
+-
+-              nandc->tx_chan = dma_request_chan(nandc->dev, "tx");
+-              if (IS_ERR(nandc->tx_chan)) {
+-                      ret = PTR_ERR(nandc->tx_chan);
+-                      nandc->tx_chan = NULL;
+-                      dev_err_probe(nandc->dev, ret,
+-                                    "tx DMA channel request failed\n");
+-                      goto unalloc;
+-              }
+-
+-              nandc->rx_chan = dma_request_chan(nandc->dev, "rx");
+-              if (IS_ERR(nandc->rx_chan)) {
+-                      ret = PTR_ERR(nandc->rx_chan);
+-                      nandc->rx_chan = NULL;
+-                      dev_err_probe(nandc->dev, ret,
+-                                    "rx DMA channel request failed\n");
+-                      goto unalloc;
+-              }
+-
+-              nandc->cmd_chan = dma_request_chan(nandc->dev, "cmd");
+-              if (IS_ERR(nandc->cmd_chan)) {
+-                      ret = PTR_ERR(nandc->cmd_chan);
+-                      nandc->cmd_chan = NULL;
+-                      dev_err_probe(nandc->dev, ret,
+-                                    "cmd DMA channel request failed\n");
+-                      goto unalloc;
+-              }
+-
+-              /*
+-               * Initially allocate BAM transaction to read ONFI param page.
+-               * After detecting all the devices, this BAM transaction will
+-               * be freed and the next BAM transaction will be allocated with
+-               * maximum codeword size
+-               */
+-              nandc->max_cwperpage = 1;
+-              nandc->bam_txn = qcom_alloc_bam_transaction(nandc);
+-              if (!nandc->bam_txn) {
+-                      dev_err(nandc->dev,
+-                              "failed to allocate bam transaction\n");
+-                      ret = -ENOMEM;
+-                      goto unalloc;
+-              }
+-      } else {
+-              nandc->chan = dma_request_chan(nandc->dev, "rxtx");
+-              if (IS_ERR(nandc->chan)) {
+-                      ret = PTR_ERR(nandc->chan);
+-                      nandc->chan = NULL;
+-                      dev_err_probe(nandc->dev, ret,
+-                                    "rxtx DMA channel request failed\n");
+-                      return ret;
+-              }
+-      }
+-
+-      INIT_LIST_HEAD(&nandc->desc_list);
+-      INIT_LIST_HEAD(&nandc->host_list);
+-
+-      nand_controller_init(&nandc->controller);
+-      nandc->controller.ops = &qcom_nandc_ops;
+-
+-      return 0;
+-unalloc:
+-      qcom_nandc_unalloc(nandc);
+-      return ret;
+-}
+-
+ /* one time setup of a few nand controller registers */
+ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
+ {
+       u32 nand_ctrl;
++      nand_controller_init(nandc->controller);
++      nandc->controller->ops = &qcom_nandc_ops;
++
+       /* kill onenand */
+       if (!nandc->props->nandc_part_of_qpic)
+               nandc_write(nandc, SFLASHC_BURST_CFG, 0);
+@@ -3240,7 +2168,7 @@ static int qcom_nand_host_init_and_regis
+       chip->legacy.block_bad          = qcom_nandc_block_bad;
+       chip->legacy.block_markbad      = qcom_nandc_block_markbad;
+-      chip->controller = &nandc->controller;
++      chip->controller = nandc->controller;
+       chip->options |= NAND_NO_SUBPAGE_WRITE | NAND_USES_DMA |
+                        NAND_SKIP_BBTSCAN;
+@@ -3323,17 +2251,21 @@ static int qcom_nandc_parse_dt(struct pl
+ static int qcom_nandc_probe(struct platform_device *pdev)
+ {
+       struct qcom_nand_controller *nandc;
++      struct nand_controller *controller;
+       const void *dev_data;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       int ret;
+-      nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc), GFP_KERNEL);
++      nandc = devm_kzalloc(&pdev->dev, sizeof(*nandc) + sizeof(*controller),
++                           GFP_KERNEL);
+       if (!nandc)
+               return -ENOMEM;
++      controller = (struct nand_controller *)&nandc[1];
+       platform_set_drvdata(pdev, nandc);
+       nandc->dev = dev;
++      nandc->controller = controller;
+       dev_data = of_device_get_match_data(dev);
+       if (!dev_data) {
+--- /dev/null
++++ b/include/linux/mtd/nand-qpic-common.h
+@@ -0,0 +1,468 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * QCOM QPIC common APIs header file
++ *
++ * Copyright (c) 2023 Qualcomm Inc.
++ * Authors:   Md sadre Alam   <quic_mdalam@quicinc.com>
++ *
++ */
++#ifndef __MTD_NAND_QPIC_COMMON_H__
++#define __MTD_NAND_QPIC_COMMON_H__
++
++/* NANDc reg offsets */
++#define       NAND_FLASH_CMD                  0x00
++#define       NAND_ADDR0                      0x04
++#define       NAND_ADDR1                      0x08
++#define       NAND_FLASH_CHIP_SELECT          0x0c
++#define       NAND_EXEC_CMD                   0x10
++#define       NAND_FLASH_STATUS               0x14
++#define       NAND_BUFFER_STATUS              0x18
++#define       NAND_DEV0_CFG0                  0x20
++#define       NAND_DEV0_CFG1                  0x24
++#define       NAND_DEV0_ECC_CFG               0x28
++#define       NAND_AUTO_STATUS_EN             0x2c
++#define       NAND_DEV1_CFG0                  0x30
++#define       NAND_DEV1_CFG1                  0x34
++#define       NAND_READ_ID                    0x40
++#define       NAND_READ_STATUS                0x44
++#define       NAND_DEV_CMD0                   0xa0
++#define       NAND_DEV_CMD1                   0xa4
++#define       NAND_DEV_CMD2                   0xa8
++#define       NAND_DEV_CMD_VLD                0xac
++#define       SFLASHC_BURST_CFG               0xe0
++#define       NAND_ERASED_CW_DETECT_CFG       0xe8
++#define       NAND_ERASED_CW_DETECT_STATUS    0xec
++#define       NAND_EBI2_ECC_BUF_CFG           0xf0
++#define       FLASH_BUF_ACC                   0x100
++
++#define       NAND_CTRL                       0xf00
++#define       NAND_VERSION                    0xf08
++#define       NAND_READ_LOCATION_0            0xf20
++#define       NAND_READ_LOCATION_1            0xf24
++#define       NAND_READ_LOCATION_2            0xf28
++#define       NAND_READ_LOCATION_3            0xf2c
++#define       NAND_READ_LOCATION_LAST_CW_0    0xf40
++#define       NAND_READ_LOCATION_LAST_CW_1    0xf44
++#define       NAND_READ_LOCATION_LAST_CW_2    0xf48
++#define       NAND_READ_LOCATION_LAST_CW_3    0xf4c
++
++/* dummy register offsets, used by qcom_write_reg_dma */
++#define       NAND_DEV_CMD1_RESTORE           0xdead
++#define       NAND_DEV_CMD_VLD_RESTORE        0xbeef
++
++/* NAND_FLASH_CMD bits */
++#define       PAGE_ACC                        BIT(4)
++#define       LAST_PAGE                       BIT(5)
++
++/* NAND_FLASH_CHIP_SELECT bits */
++#define       NAND_DEV_SEL                    0
++#define       DM_EN                           BIT(2)
++
++/* NAND_FLASH_STATUS bits */
++#define       FS_OP_ERR                       BIT(4)
++#define       FS_READY_BSY_N                  BIT(5)
++#define       FS_MPU_ERR                      BIT(8)
++#define       FS_DEVICE_STS_ERR               BIT(16)
++#define       FS_DEVICE_WP                    BIT(23)
++
++/* NAND_BUFFER_STATUS bits */
++#define       BS_UNCORRECTABLE_BIT            BIT(8)
++#define       BS_CORRECTABLE_ERR_MSK          0x1f
++
++/* NAND_DEVn_CFG0 bits */
++#define       DISABLE_STATUS_AFTER_WRITE      4
++#define       CW_PER_PAGE                     6
++#define       UD_SIZE_BYTES                   9
++#define       UD_SIZE_BYTES_MASK              GENMASK(18, 9)
++#define       ECC_PARITY_SIZE_BYTES_RS        19
++#define       SPARE_SIZE_BYTES                23
++#define       SPARE_SIZE_BYTES_MASK           GENMASK(26, 23)
++#define       NUM_ADDR_CYCLES                 27
++#define       STATUS_BFR_READ                 30
++#define       SET_RD_MODE_AFTER_STATUS        31
++
++/* NAND_DEVn_CFG0 bits */
++#define       DEV0_CFG1_ECC_DISABLE           0
++#define       WIDE_FLASH                      1
++#define       NAND_RECOVERY_CYCLES            2
++#define       CS_ACTIVE_BSY                   5
++#define       BAD_BLOCK_BYTE_NUM              6
++#define       BAD_BLOCK_IN_SPARE_AREA         16
++#define       WR_RD_BSY_GAP                   17
++#define       ENABLE_BCH_ECC                  27
++
++/* NAND_DEV0_ECC_CFG bits */
++#define       ECC_CFG_ECC_DISABLE             0
++#define       ECC_SW_RESET                    1
++#define       ECC_MODE                        4
++#define       ECC_PARITY_SIZE_BYTES_BCH       8
++#define       ECC_NUM_DATA_BYTES              16
++#define       ECC_NUM_DATA_BYTES_MASK         GENMASK(25, 16)
++#define       ECC_FORCE_CLK_OPEN              30
++
++/* NAND_DEV_CMD1 bits */
++#define       READ_ADDR                       0
++
++/* NAND_DEV_CMD_VLD bits */
++#define       READ_START_VLD                  BIT(0)
++#define       READ_STOP_VLD                   BIT(1)
++#define       WRITE_START_VLD                 BIT(2)
++#define       ERASE_START_VLD                 BIT(3)
++#define       SEQ_READ_START_VLD              BIT(4)
++
++/* NAND_EBI2_ECC_BUF_CFG bits */
++#define       NUM_STEPS                       0
++
++/* NAND_ERASED_CW_DETECT_CFG bits */
++#define       ERASED_CW_ECC_MASK              1
++#define       AUTO_DETECT_RES                 0
++#define       MASK_ECC                        BIT(ERASED_CW_ECC_MASK)
++#define       RESET_ERASED_DET                BIT(AUTO_DETECT_RES)
++#define       ACTIVE_ERASED_DET               (0 << AUTO_DETECT_RES)
++#define       CLR_ERASED_PAGE_DET             (RESET_ERASED_DET | MASK_ECC)
++#define       SET_ERASED_PAGE_DET             (ACTIVE_ERASED_DET | MASK_ECC)
++
++/* NAND_ERASED_CW_DETECT_STATUS bits */
++#define       PAGE_ALL_ERASED                 BIT(7)
++#define       CODEWORD_ALL_ERASED             BIT(6)
++#define       PAGE_ERASED                     BIT(5)
++#define       CODEWORD_ERASED                 BIT(4)
++#define       ERASED_PAGE                     (PAGE_ALL_ERASED | PAGE_ERASED)
++#define       ERASED_CW                       (CODEWORD_ALL_ERASED | CODEWORD_ERASED)
++
++/* NAND_READ_LOCATION_n bits */
++#define READ_LOCATION_OFFSET          0
++#define READ_LOCATION_SIZE            16
++#define READ_LOCATION_LAST            31
++
++/* Version Mask */
++#define       NAND_VERSION_MAJOR_MASK         0xf0000000
++#define       NAND_VERSION_MAJOR_SHIFT        28
++#define       NAND_VERSION_MINOR_MASK         0x0fff0000
++#define       NAND_VERSION_MINOR_SHIFT        16
++
++/* NAND OP_CMDs */
++#define       OP_PAGE_READ                    0x2
++#define       OP_PAGE_READ_WITH_ECC           0x3
++#define       OP_PAGE_READ_WITH_ECC_SPARE     0x4
++#define       OP_PAGE_READ_ONFI_READ          0x5
++#define       OP_PROGRAM_PAGE                 0x6
++#define       OP_PAGE_PROGRAM_WITH_ECC        0x7
++#define       OP_PROGRAM_PAGE_SPARE           0x9
++#define       OP_BLOCK_ERASE                  0xa
++#define       OP_CHECK_STATUS                 0xc
++#define       OP_FETCH_ID                     0xb
++#define       OP_RESET_DEVICE                 0xd
++
++/* Default Value for NAND_DEV_CMD_VLD */
++#define NAND_DEV_CMD_VLD_VAL          (READ_START_VLD | WRITE_START_VLD | \
++                                       ERASE_START_VLD | SEQ_READ_START_VLD)
++
++/* NAND_CTRL bits */
++#define       BAM_MODE_EN                     BIT(0)
++
++/*
++ * the NAND controller performs reads/writes with ECC in 516 byte chunks.
++ * the driver calls the chunks 'step' or 'codeword' interchangeably
++ */
++#define       NANDC_STEP_SIZE                 512
++
++/*
++ * the largest page size we support is 8K, this will have 16 steps/codewords
++ * of 512 bytes each
++ */
++#define       MAX_NUM_STEPS                   (SZ_8K / NANDC_STEP_SIZE)
++
++/* we read at most 3 registers per codeword scan */
++#define       MAX_REG_RD                      (3 * MAX_NUM_STEPS)
++
++/* ECC modes supported by the controller */
++#define       ECC_NONE        BIT(0)
++#define       ECC_RS_4BIT     BIT(1)
++#define       ECC_BCH_4BIT    BIT(2)
++#define       ECC_BCH_8BIT    BIT(3)
++
++/*
++ * Returns the actual register address for all NAND_DEV_ registers
++ * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD)
++ */
++#define dev_cmd_reg_addr(nandc, reg) ((nandc)->props->dev_cmd_reg_start + (reg))
++
++/* Returns the NAND register physical address */
++#define nandc_reg_phys(chip, offset) ((chip)->base_phys + (offset))
++
++/* Returns the dma address for reg read buffer */
++#define reg_buf_dma_addr(chip, vaddr) \
++      ((chip)->reg_read_dma + \
++      ((u8 *)(vaddr) - (u8 *)(chip)->reg_read_buf))
++
++#define QPIC_PER_CW_CMD_ELEMENTS      32
++#define QPIC_PER_CW_CMD_SGL           32
++#define QPIC_PER_CW_DATA_SGL          8
++
++#define QPIC_NAND_COMPLETION_TIMEOUT  msecs_to_jiffies(2000)
++
++/*
++ * Flags used in DMA descriptor preparation helper functions
++ * (i.e. qcom_read_reg_dma/qcom_write_reg_dma/qcom_read_data_dma/qcom_write_data_dma)
++ */
++/* Don't set the EOT in current tx BAM sgl */
++#define NAND_BAM_NO_EOT                       BIT(0)
++/* Set the NWD flag in current BAM sgl */
++#define NAND_BAM_NWD                  BIT(1)
++/* Finish writing in the current BAM sgl and start writing in another BAM sgl */
++#define NAND_BAM_NEXT_SGL             BIT(2)
++/*
++ * Erased codeword status is being used two times in single transfer so this
++ * flag will determine the current value of erased codeword status register
++ */
++#define NAND_ERASED_CW_SET            BIT(4)
++
++#define MAX_ADDRESS_CYCLE             5
++
++/*
++ * This data type corresponds to the BAM transaction which will be used for all
++ * NAND transfers.
++ * @bam_ce - the array of BAM command elements
++ * @cmd_sgl - sgl for NAND BAM command pipe
++ * @data_sgl - sgl for NAND BAM consumer/producer pipe
++ * @last_data_desc - last DMA desc in data channel (tx/rx).
++ * @last_cmd_desc - last DMA desc in command channel.
++ * @txn_done - completion for NAND transfer.
++ * @bam_ce_pos - the index in bam_ce which is available for next sgl
++ * @bam_ce_start - the index in bam_ce which marks the start position ce
++ *               for current sgl. It will be used for size calculation
++ *               for current sgl
++ * @cmd_sgl_pos - current index in command sgl.
++ * @cmd_sgl_start - start index in command sgl.
++ * @tx_sgl_pos - current index in data sgl for tx.
++ * @tx_sgl_start - start index in data sgl for tx.
++ * @rx_sgl_pos - current index in data sgl for rx.
++ * @rx_sgl_start - start index in data sgl for rx.
++ */
++struct bam_transaction {
++      struct bam_cmd_element *bam_ce;
++      struct scatterlist *cmd_sgl;
++      struct scatterlist *data_sgl;
++      struct dma_async_tx_descriptor *last_data_desc;
++      struct dma_async_tx_descriptor *last_cmd_desc;
++      struct completion txn_done;
++      u32 bam_ce_pos;
++      u32 bam_ce_start;
++      u32 cmd_sgl_pos;
++      u32 cmd_sgl_start;
++      u32 tx_sgl_pos;
++      u32 tx_sgl_start;
++      u32 rx_sgl_pos;
++      u32 rx_sgl_start;
++};
++
++/*
++ * This data type corresponds to the nand dma descriptor
++ * @dma_desc - low level DMA engine descriptor
++ * @list - list for desc_info
++ *
++ * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by
++ *          ADM
++ * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM
++ * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM
++ * @dir - DMA transfer direction
++ */
++struct desc_info {
++      struct dma_async_tx_descriptor *dma_desc;
++      struct list_head node;
++
++      union {
++              struct scatterlist adm_sgl;
++              struct {
++                      struct scatterlist *bam_sgl;
++                      int sgl_cnt;
++              };
++      };
++      enum dma_data_direction dir;
++};
++
++/*
++ * holds the current register values that we want to write. acts as a contiguous
++ * chunk of memory which we use to write the controller registers through DMA.
++ */
++struct nandc_regs {
++      __le32 cmd;
++      __le32 addr0;
++      __le32 addr1;
++      __le32 chip_sel;
++      __le32 exec;
++
++      __le32 cfg0;
++      __le32 cfg1;
++      __le32 ecc_bch_cfg;
++
++      __le32 clrflashstatus;
++      __le32 clrreadstatus;
++
++      __le32 cmd1;
++      __le32 vld;
++
++      __le32 orig_cmd1;
++      __le32 orig_vld;
++
++      __le32 ecc_buf_cfg;
++      __le32 read_location0;
++      __le32 read_location1;
++      __le32 read_location2;
++      __le32 read_location3;
++      __le32 read_location_last0;
++      __le32 read_location_last1;
++      __le32 read_location_last2;
++      __le32 read_location_last3;
++
++      __le32 erased_cw_detect_cfg_clr;
++      __le32 erased_cw_detect_cfg_set;
++};
++
++/*
++ * NAND controller data struct
++ *
++ * @dev:                      parent device
++ *
++ * @base:                     MMIO base
++ *
++ * @core_clk:                 controller clock
++ * @aon_clk:                  another controller clock
++ *
++ * @regs:                     a contiguous chunk of memory for DMA register
++ *                            writes. contains the register values to be
++ *                            written to controller
++ *
++ * @props:                    properties of current NAND controller,
++ *                            initialized via DT match data
++ *
++ * @controller:                       base controller structure
++ * @host_list:                        list containing all the chips attached to the
++ *                            controller
++ *
++ * @chan:                     dma channel
++ * @cmd_crci:                 ADM DMA CRCI for command flow control
++ * @data_crci:                        ADM DMA CRCI for data flow control
++ *
++ * @desc_list:                        DMA descriptor list (list of desc_infos)
++ *
++ * @data_buffer:              our local DMA buffer for page read/writes,
++ *                            used when we can't use the buffer provided
++ *                            by upper layers directly
++ * @reg_read_buf:             local buffer for reading back registers via DMA
++ *
++ * @base_phys:                        physical base address of controller registers
++ * @base_dma:                 dma base address of controller registers
++ * @reg_read_dma:             contains dma address for register read buffer
++ *
++ * @buf_size/count/start:     markers for chip->legacy.read_buf/write_buf
++ *                            functions
++ * @max_cwperpage:            maximum QPIC codewords required. calculated
++ *                            from all connected NAND devices pagesize
++ *
++ * @reg_read_pos:             marker for data read in reg_read_buf
++ *
++ * @cmd1/vld:                 some fixed controller register values
++ *
++ * @exec_opwrite:             flag to select correct number of code word
++ *                            while reading status
++ */
++struct qcom_nand_controller {
++      struct device *dev;
++
++      void __iomem *base;
++
++      struct clk *core_clk;
++      struct clk *aon_clk;
++
++      struct nandc_regs *regs;
++      struct bam_transaction *bam_txn;
++
++      const struct qcom_nandc_props *props;
++
++      struct nand_controller *controller;
++      struct list_head host_list;
++
++      union {
++              /* will be used only by QPIC for BAM DMA */
++              struct {
++                      struct dma_chan *tx_chan;
++                      struct dma_chan *rx_chan;
++                      struct dma_chan *cmd_chan;
++              };
++
++              /* will be used only by EBI2 for ADM DMA */
++              struct {
++                      struct dma_chan *chan;
++                      unsigned int cmd_crci;
++                      unsigned int data_crci;
++              };
++      };
++
++      struct list_head desc_list;
++
++      u8              *data_buffer;
++      __le32          *reg_read_buf;
++
++      phys_addr_t base_phys;
++      dma_addr_t base_dma;
++      dma_addr_t reg_read_dma;
++
++      int             buf_size;
++      int             buf_count;
++      int             buf_start;
++      unsigned int    max_cwperpage;
++
++      int reg_read_pos;
++
++      u32 cmd1, vld;
++      bool exec_opwrite;
++};
++
++/*
++ * This data type corresponds to the NAND controller properties which varies
++ * among different NAND controllers.
++ * @ecc_modes - ecc mode for NAND
++ * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
++ * @supports_bam - whether NAND controller is using BAM
++ * @nandc_part_of_qpic - whether NAND controller is part of qpic IP
++ * @qpic_version2 - flag to indicate QPIC IP version 2
++ * @use_codeword_fixup - whether NAND has different layout for boot partitions
++ */
++struct qcom_nandc_props {
++      u32 ecc_modes;
++      u32 dev_cmd_reg_start;
++      bool supports_bam;
++      bool nandc_part_of_qpic;
++      bool qpic_version2;
++      bool use_codeword_fixup;
++};
++
++void qcom_free_bam_transaction(struct qcom_nand_controller *nandc);
++struct bam_transaction *qcom_alloc_bam_transaction(struct qcom_nand_controller *nandc);
++void qcom_clear_bam_transaction(struct qcom_nand_controller *nandc);
++void qcom_qpic_bam_dma_done(void *data);
++void qcom_nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu);
++int qcom_prepare_bam_async_desc(struct qcom_nand_controller *nandc,
++                              struct dma_chan *chan, unsigned long flags);
++int qcom_prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read,
++                             int reg_off, const void *vaddr, int size, unsigned int flags);
++int qcom_prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read,
++                              const void *vaddr, int size, unsigned int flags);
++int qcom_prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read, int reg_off,
++                         const void *vaddr, int size, bool flow_control);
++int qcom_read_reg_dma(struct qcom_nand_controller *nandc, int first, int num_regs,
++                    unsigned int flags);
++int qcom_write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr, int first,
++                     int num_regs, unsigned int flags);
++int qcom_read_data_dma(struct qcom_nand_controller *nandc, int reg_off, const u8 *vaddr,
++                     int size, unsigned int flags);
++int qcom_write_data_dma(struct qcom_nand_controller *nandc, int reg_off, const u8 *vaddr,
++                      int size, unsigned int flags);
++int qcom_submit_descs(struct qcom_nand_controller *nandc);
++void qcom_clear_read_regs(struct qcom_nand_controller *nandc);
++void qcom_nandc_unalloc(struct qcom_nand_controller *nandc);
++int qcom_nandc_alloc(struct qcom_nand_controller *nandc);
++#endif
++
diff --git a/target/linux/qualcommax/patches-6.6/0405-mtd-rawnand-qcom-use-FIELD_PREP-and-GENMASK.patch b/target/linux/qualcommax/patches-6.6/0405-mtd-rawnand-qcom-use-FIELD_PREP-and-GENMASK.patch
new file mode 100644 (file)
index 0000000..5f8ff3e
--- /dev/null
@@ -0,0 +1,191 @@
+From: Md Sadre Alam <quic_mdalam@quicinc.com>
+Date: Sun, 22 Sep 2024 17:03:48 +0530
+Subject: [PATCH] mtd: rawnand: qcom: use FIELD_PREP and GENMASK
+
+Use the bitfield macro FIELD_PREP, and GENMASK to
+do the shift and mask in one go. This makes the code
+more readable.
+
+Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
+---
+--- a/drivers/mtd/nand/raw/qcom_nandc.c
++++ b/drivers/mtd/nand/raw/qcom_nandc.c
+@@ -281,7 +281,7 @@ static void update_rw_regs(struct qcom_n
+                               (num_cw - 1) << CW_PER_PAGE);
+               cfg1 = cpu_to_le32(host->cfg1_raw);
+-              ecc_bch_cfg = cpu_to_le32(1 << ECC_CFG_ECC_DISABLE);
++              ecc_bch_cfg = cpu_to_le32(ECC_CFG_ECC_DISABLE);
+       }
+       nandc->regs->cmd = cmd;
+@@ -1494,42 +1494,41 @@ static int qcom_nand_attach_chip(struct
+       host->cw_size = host->cw_data + ecc->bytes;
+       bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1;
+-      host->cfg0 = (cwperpage - 1) << CW_PER_PAGE
+-                              | host->cw_data << UD_SIZE_BYTES
+-                              | 0 << DISABLE_STATUS_AFTER_WRITE
+-                              | 5 << NUM_ADDR_CYCLES
+-                              | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_RS
+-                              | 0 << STATUS_BFR_READ
+-                              | 1 << SET_RD_MODE_AFTER_STATUS
+-                              | host->spare_bytes << SPARE_SIZE_BYTES;
+-
+-      host->cfg1 = 7 << NAND_RECOVERY_CYCLES
+-                              | 0 <<  CS_ACTIVE_BSY
+-                              | bad_block_byte << BAD_BLOCK_BYTE_NUM
+-                              | 0 << BAD_BLOCK_IN_SPARE_AREA
+-                              | 2 << WR_RD_BSY_GAP
+-                              | wide_bus << WIDE_FLASH
+-                              | host->bch_enabled << ENABLE_BCH_ECC;
+-
+-      host->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE
+-                              | host->cw_size << UD_SIZE_BYTES
+-                              | 5 << NUM_ADDR_CYCLES
+-                              | 0 << SPARE_SIZE_BYTES;
+-
+-      host->cfg1_raw = 7 << NAND_RECOVERY_CYCLES
+-                              | 0 << CS_ACTIVE_BSY
+-                              | 17 << BAD_BLOCK_BYTE_NUM
+-                              | 1 << BAD_BLOCK_IN_SPARE_AREA
+-                              | 2 << WR_RD_BSY_GAP
+-                              | wide_bus << WIDE_FLASH
+-                              | 1 << DEV0_CFG1_ECC_DISABLE;
+-
+-      host->ecc_bch_cfg = !host->bch_enabled << ECC_CFG_ECC_DISABLE
+-                              | 0 << ECC_SW_RESET
+-                              | host->cw_data << ECC_NUM_DATA_BYTES
+-                              | 1 << ECC_FORCE_CLK_OPEN
+-                              | ecc_mode << ECC_MODE
+-                              | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_BCH;
++      host->cfg0 = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) |
++                   FIELD_PREP(UD_SIZE_BYTES_MASK, host->cw_data) |
++                   FIELD_PREP(DISABLE_STATUS_AFTER_WRITE, 0) |
++                   FIELD_PREP(NUM_ADDR_CYCLES_MASK, 5) |
++                   FIELD_PREP(ECC_PARITY_SIZE_BYTES_RS, host->ecc_bytes_hw) |
++                   FIELD_PREP(STATUS_BFR_READ, 0) |
++                   FIELD_PREP(SET_RD_MODE_AFTER_STATUS, 1) |
++                   FIELD_PREP(SPARE_SIZE_BYTES_MASK, host->spare_bytes);
++
++      host->cfg1 = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 7) |
++                   FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, bad_block_byte) |
++                   FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 0) |
++                   FIELD_PREP(WR_RD_BSY_GAP_MASK, 2) |
++                   FIELD_PREP(WIDE_FLASH, wide_bus) |
++                   FIELD_PREP(ENABLE_BCH_ECC, host->bch_enabled);
++
++      host->cfg0_raw = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) |
++                       FIELD_PREP(UD_SIZE_BYTES_MASK, host->cw_size) |
++                       FIELD_PREP(NUM_ADDR_CYCLES_MASK, 5) |
++                       FIELD_PREP(SPARE_SIZE_BYTES_MASK, 0);
++
++      host->cfg1_raw = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 7) |
++                       FIELD_PREP(CS_ACTIVE_BSY, 0) |
++                       FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, 17) |
++                       FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 1) |
++                       FIELD_PREP(WR_RD_BSY_GAP_MASK, 2) |
++                       FIELD_PREP(WIDE_FLASH, wide_bus) |
++                       FIELD_PREP(DEV0_CFG1_ECC_DISABLE, 1);
++
++      host->ecc_bch_cfg = FIELD_PREP(ECC_CFG_ECC_DISABLE, !host->bch_enabled) |
++                          FIELD_PREP(ECC_SW_RESET, 0) |
++                          FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, host->cw_data) |
++                          FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) |
++                          FIELD_PREP(ECC_MODE_MASK, ecc_mode) |
++                          FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, host->ecc_bytes_hw);
+       if (!nandc->props->qpic_version2)
+               host->ecc_buf_cfg = 0x203 << NUM_STEPS;
+@@ -1882,21 +1881,21 @@ static int qcom_param_page_type_exec(str
+       nandc->regs->addr0 = 0;
+       nandc->regs->addr1 = 0;
+-      nandc->regs->cfg0 = cpu_to_le32(0 << CW_PER_PAGE
+-                          | 512 << UD_SIZE_BYTES
+-                          | 5 << NUM_ADDR_CYCLES
+-                          | 0 << SPARE_SIZE_BYTES);
+-
+-      nandc->regs->cfg1 = cpu_to_le32(7 << NAND_RECOVERY_CYCLES
+-                          | 0 << CS_ACTIVE_BSY
+-                          | 17 << BAD_BLOCK_BYTE_NUM
+-                          | 1 << BAD_BLOCK_IN_SPARE_AREA
+-                          | 2 << WR_RD_BSY_GAP
+-                          | 0 << WIDE_FLASH
+-                          | 1 << DEV0_CFG1_ECC_DISABLE);
++      host->cfg0 = FIELD_PREP(CW_PER_PAGE_MASK, 0) |
++                   FIELD_PREP(UD_SIZE_BYTES_MASK, 512) |
++                   FIELD_PREP(NUM_ADDR_CYCLES_MASK, 5) |
++                   FIELD_PREP(SPARE_SIZE_BYTES_MASK, 0);
++
++      host->cfg1 = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 7) |
++                   FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, 17) |
++                   FIELD_PREP(CS_ACTIVE_BSY, 0) |
++                   FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 1) |
++                   FIELD_PREP(WR_RD_BSY_GAP_MASK, 2) |
++                   FIELD_PREP(WIDE_FLASH, 0) |
++                   FIELD_PREP(DEV0_CFG1_ECC_DISABLE, 1);
+       if (!nandc->props->qpic_version2)
+-              nandc->regs->ecc_buf_cfg = cpu_to_le32(1 << ECC_CFG_ECC_DISABLE);
++              nandc->regs->ecc_buf_cfg = cpu_to_le32(ECC_CFG_ECC_DISABLE);
+       /* configure CMD1 and VLD for ONFI param probing in QPIC v1 */
+       if (!nandc->props->qpic_version2) {
+--- a/include/linux/mtd/nand-qpic-common.h
++++ b/include/linux/mtd/nand-qpic-common.h
+@@ -70,35 +70,42 @@
+ #define       BS_CORRECTABLE_ERR_MSK          0x1f
+ /* NAND_DEVn_CFG0 bits */
+-#define       DISABLE_STATUS_AFTER_WRITE      4
++#define       DISABLE_STATUS_AFTER_WRITE      BIT(4)
+ #define       CW_PER_PAGE                     6
++#define       CW_PER_PAGE_MASK                GENMASK(8, 6)
+ #define       UD_SIZE_BYTES                   9
+ #define       UD_SIZE_BYTES_MASK              GENMASK(18, 9)
+-#define       ECC_PARITY_SIZE_BYTES_RS        19
++#define       ECC_PARITY_SIZE_BYTES_RS        GENMASK(22, 19)
+ #define       SPARE_SIZE_BYTES                23
+ #define       SPARE_SIZE_BYTES_MASK           GENMASK(26, 23)
+ #define       NUM_ADDR_CYCLES                 27
+-#define       STATUS_BFR_READ                 30
+-#define       SET_RD_MODE_AFTER_STATUS        31
++#define       NUM_ADDR_CYCLES_MASK            GENMASK(29, 27)
++#define       STATUS_BFR_READ                 BIT(30)
++#define       SET_RD_MODE_AFTER_STATUS        BIT(31)
+ /* NAND_DEVn_CFG0 bits */
+-#define       DEV0_CFG1_ECC_DISABLE           0
+-#define       WIDE_FLASH                      1
++#define       DEV0_CFG1_ECC_DISABLE           BIT(0)
++#define       WIDE_FLASH                      BIT(1)
+ #define       NAND_RECOVERY_CYCLES            2
+-#define       CS_ACTIVE_BSY                   5
++#define       NAND_RECOVERY_CYCLES_MASK       GENMASK(4, 2)
++#define       CS_ACTIVE_BSY                   BIT(5)
+ #define       BAD_BLOCK_BYTE_NUM              6
+-#define       BAD_BLOCK_IN_SPARE_AREA         16
++#define       BAD_BLOCK_BYTE_NUM_MASK         GENMASK(15, 6)
++#define       BAD_BLOCK_IN_SPARE_AREA         BIT(16)
+ #define       WR_RD_BSY_GAP                   17
+-#define       ENABLE_BCH_ECC                  27
++#define       WR_RD_BSY_GAP_MASK              GENMASK(22, 17)
++#define       ENABLE_BCH_ECC                  BIT(27)
+ /* NAND_DEV0_ECC_CFG bits */
+-#define       ECC_CFG_ECC_DISABLE             0
+-#define       ECC_SW_RESET                    1
++#define       ECC_CFG_ECC_DISABLE             BIT(0)
++#define       ECC_SW_RESET                    BIT(1)
+ #define       ECC_MODE                        4
++#define       ECC_MODE_MASK                   GENMASK(5, 4)
+ #define       ECC_PARITY_SIZE_BYTES_BCH       8
++#define       ECC_PARITY_SIZE_BYTES_BCH_MASK  GENMASK(12, 8)
+ #define       ECC_NUM_DATA_BYTES              16
+ #define       ECC_NUM_DATA_BYTES_MASK         GENMASK(25, 16)
+-#define       ECC_FORCE_CLK_OPEN              30
++#define       ECC_FORCE_CLK_OPEN              BIT(30)
+ /* NAND_DEV_CMD1 bits */
+ #define       READ_ADDR                       0
diff --git a/target/linux/qualcommax/patches-6.6/0406-spi-spi-qpic-add-driver-for-QCOM-SPI-NAND-flash-Interface.patch b/target/linux/qualcommax/patches-6.6/0406-spi-spi-qpic-add-driver-for-QCOM-SPI-NAND-flash-Interface.patch
new file mode 100644 (file)
index 0000000..9518852
--- /dev/null
@@ -0,0 +1,1729 @@
+From: Md Sadre Alam <quic_mdalam@quicinc.com>
+Date: Sun, 22 Sep 2024 17:03:49 +0530
+Subject: [PATCH] spi: spi-qpic: add driver for QCOM SPI NAND flash Interface
+
+This driver implements support for the SPI-NAND mode of QCOM NAND Flash
+Interface as a SPI-MEM controller with pipelined ECC capability.
+
+Co-developed-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
+Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
+Co-developed-by: Varadarajan Narayanan <quic_varada@quicinc.com>
+Signed-off-by: Varadarajan Narayanan <quic_varada@quicinc.com>
+Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
+---
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -7,8 +7,11 @@ obj-$(CONFIG_MTD_NAND_MTK_BMT)        += mtk_bm
+ ifeq ($(CONFIG_MTD_NAND_QCOM),y)
+ obj-y += qpic_common.o
++else
++ifeq ($(CONFIG_SPI_QPIC_SNAND),y)
++obj-y   += qpic_common.o
++endif
+ endif
+-
+ obj-y += onenand/
+ obj-y += raw/
+ obj-y += spi/
+--- a/drivers/spi/Kconfig
++++ b/drivers/spi/Kconfig
+@@ -870,6 +870,14 @@ config SPI_QCOM_QSPI
+       help
+         QSPI(Quad SPI) driver for Qualcomm QSPI controller.
++config SPI_QPIC_SNAND
++      bool "QPIC SNAND controller"
++      depends on ARCH_QCOM || COMPILE_TEST
++      help
++        QPIC_SNAND (QPIC SPI NAND) driver for Qualcomm QPIC controller.
++        QPIC controller supports both parallel nand and serial nand.
++        This config will enable serial nand driver for QPIC controller.
++
+ config SPI_QUP
+       tristate "Qualcomm SPI controller with QUP interface"
+       depends on ARCH_QCOM || COMPILE_TEST
+--- a/drivers/spi/Makefile
++++ b/drivers/spi/Makefile
+@@ -110,6 +110,7 @@ obj-$(CONFIG_SPI_PXA2XX)           += spi-pxa2xx-
+ obj-$(CONFIG_SPI_PXA2XX_PCI)          += spi-pxa2xx-pci.o
+ obj-$(CONFIG_SPI_QCOM_GENI)           += spi-geni-qcom.o
+ obj-$(CONFIG_SPI_QCOM_QSPI)           += spi-qcom-qspi.o
++obj-$(CONFIG_SPI_QPIC_SNAND)            += spi-qpic-snand.o
+ obj-$(CONFIG_SPI_QUP)                 += spi-qup.o
+ obj-$(CONFIG_SPI_ROCKCHIP)            += spi-rockchip.o
+ obj-$(CONFIG_SPI_ROCKCHIP_SFC)                += spi-rockchip-sfc.o
+--- /dev/null
++++ b/drivers/spi/spi-qpic-snand.c
+@@ -0,0 +1,1634 @@
++/*
++ * SPDX-License-Identifier: GPL-2.0
++ *
++ * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
++ *
++ * Authors:
++ *    Md Sadre Alam <quic_mdalam@quicinc.com>
++ *    Sricharan R <quic_srichara@quicinc.com>
++ *    Varadarajan Narayanan <quic_varada@quicinc.com>
++ */
++#include <linux/bitops.h>
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/dmaengine.h>
++#include <linux/dma-mapping.h>
++#include <linux/dma/qcom_adm.h>
++#include <linux/dma/qcom_bam_dma.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/mtd/nand-qpic-common.h>
++#include <linux/mtd/spinand.h>
++#include <linux/bitfield.h>
++
++#define NAND_FLASH_SPI_CFG            0xc0
++#define NAND_NUM_ADDR_CYCLES          0xc4
++#define NAND_BUSY_CHECK_WAIT_CNT      0xc8
++#define NAND_FLASH_FEATURES           0xf64
++
++/* QSPI NAND config reg bits */
++#define LOAD_CLK_CNTR_INIT_EN         BIT(28)
++#define CLK_CNTR_INIT_VAL_VEC         0x924
++#define CLK_CNTR_INIT_VAL_VEC_MASK    GENMASK(27, 16)
++#define FEA_STATUS_DEV_ADDR           0xc0
++#define FEA_STATUS_DEV_ADDR_MASK      GENMASK(15, 8)
++#define SPI_CFG                               BIT(0)
++#define SPI_NUM_ADDR                  0xDA4DB
++#define SPI_WAIT_CNT                  0x10
++#define QPIC_QSPI_NUM_CS              1
++#define SPI_TRANSFER_MODE_x1          BIT(29)
++#define SPI_TRANSFER_MODE_x4          (3 << 29)
++#define SPI_WP                                BIT(28)
++#define SPI_HOLD                      BIT(27)
++#define QPIC_SET_FEATURE              BIT(31)
++
++#define SPINAND_RESET                 0xff
++#define SPINAND_READID                        0x9f
++#define SPINAND_GET_FEATURE           0x0f
++#define SPINAND_SET_FEATURE           0x1f
++#define SPINAND_READ                  0x13
++#define SPINAND_ERASE                 0xd8
++#define SPINAND_WRITE_EN              0x06
++#define SPINAND_PROGRAM_EXECUTE               0x10
++#define SPINAND_PROGRAM_LOAD          0x84
++
++#define ACC_FEATURE                   0xe
++#define BAD_BLOCK_MARKER_SIZE         0x2
++#define OOB_BUF_SIZE                  128
++#define ecceng_to_qspi(eng)           container_of(eng, struct qpic_spi_nand, ecc_eng)
++struct qpic_snand_op {
++      u32 cmd_reg;
++      u32 addr1_reg;
++      u32 addr2_reg;
++};
++
++struct snandc_read_status {
++      __le32 snandc_flash;
++      __le32 snandc_buffer;
++      __le32 snandc_erased_cw;
++};
++
++/*
++ * ECC state struct
++ * @corrected:                ECC corrected
++ * @bitflips:         Max bit flip
++ * @failed:           ECC failed
++ */
++struct qcom_ecc_stats {
++      u32 corrected;
++      u32 bitflips;
++      u32 failed;
++};
++
++struct qpic_ecc {
++      struct device *dev;
++      int ecc_bytes_hw;
++      int spare_bytes;
++      int bbm_size;
++      int ecc_mode;
++      int bytes;
++      int steps;
++      int step_size;
++      int strength;
++      int cw_size;
++      int cw_data;
++      u32 cfg0;
++      u32 cfg1;
++      u32 cfg0_raw;
++      u32 cfg1_raw;
++      u32 ecc_buf_cfg;
++      u32 ecc_bch_cfg;
++      u32 clrflashstatus;
++      u32 clrreadstatus;
++      bool bch_enabled;
++};
++
++struct qpic_spi_nand {
++      struct qcom_nand_controller *snandc;
++      struct spi_controller *ctlr;
++      struct mtd_info *mtd;
++      struct clk *iomacro_clk;
++      struct qpic_ecc *ecc;
++      struct qcom_ecc_stats ecc_stats;
++      struct nand_ecc_engine ecc_eng;
++      u8 *data_buf;
++      u8 *oob_buf;
++      u32 wlen;
++      __le32 addr1;
++      __le32 addr2;
++      __le32 cmd;
++      u32 num_cw;
++      bool oob_rw;
++      bool page_rw;
++      bool raw_rw;
++};
++
++static void qcom_spi_set_read_loc_first(struct qcom_nand_controller *snandc,
++                                      int reg, int cw_offset, int read_size,
++                                      int is_last_read_loc)
++{
++      __le32 locreg_val;
++      u32 val = (((cw_offset) << READ_LOCATION_OFFSET) |
++                ((read_size) << READ_LOCATION_SIZE) | ((is_last_read_loc)
++                << READ_LOCATION_LAST));
++
++      locreg_val = cpu_to_le32(val);
++
++      if (reg == NAND_READ_LOCATION_0)
++              snandc->regs->read_location0 = locreg_val;
++      else if (reg == NAND_READ_LOCATION_1)
++              snandc->regs->read_location1 = locreg_val;
++      else if (reg == NAND_READ_LOCATION_2)
++              snandc->regs->read_location1 = locreg_val;
++      else if (reg == NAND_READ_LOCATION_3)
++              snandc->regs->read_location3 = locreg_val;
++}
++
++static void qcom_spi_set_read_loc_last(struct qcom_nand_controller *snandc,
++                                     int reg, int cw_offset, int read_size,
++                                     int is_last_read_loc)
++{
++      __le32 locreg_val;
++      u32 val = (((cw_offset) << READ_LOCATION_OFFSET) |
++                ((read_size) << READ_LOCATION_SIZE) | ((is_last_read_loc)
++                << READ_LOCATION_LAST));
++
++      locreg_val = cpu_to_le32(val);
++
++      if (reg == NAND_READ_LOCATION_LAST_CW_0)
++              snandc->regs->read_location_last0 = locreg_val;
++      else if (reg == NAND_READ_LOCATION_LAST_CW_1)
++              snandc->regs->read_location_last1 = locreg_val;
++      else if (reg == NAND_READ_LOCATION_LAST_CW_2)
++              snandc->regs->read_location_last2 = locreg_val;
++      else if (reg == NAND_READ_LOCATION_LAST_CW_3)
++              snandc->regs->read_location_last3 = locreg_val;
++}
++
++static struct qcom_nand_controller *nand_to_qcom_snand(struct nand_device *nand)
++{
++      struct nand_ecc_engine *eng = nand->ecc.engine;
++      struct qpic_spi_nand *qspi = ecceng_to_qspi(eng);
++
++      return qspi->snandc;
++}
++
++static int qcom_spi_init(struct qcom_nand_controller *snandc)
++{
++      u32 snand_cfg_val = 0x0;
++      int ret;
++
++      snand_cfg_val = FIELD_PREP(CLK_CNTR_INIT_VAL_VEC_MASK, CLK_CNTR_INIT_VAL_VEC) |
++                      FIELD_PREP(LOAD_CLK_CNTR_INIT_EN, 0) |
++                      FIELD_PREP(FEA_STATUS_DEV_ADDR_MASK, FEA_STATUS_DEV_ADDR) |
++                      FIELD_PREP(SPI_CFG, 0);
++
++      snandc->regs->spi_cfg = cpu_to_le32(snand_cfg_val);
++      snandc->regs->num_addr_cycle = cpu_to_le32(SPI_NUM_ADDR);
++      snandc->regs->busy_wait_cnt = cpu_to_le32(SPI_WAIT_CNT);
++
++      qcom_write_reg_dma(snandc, &snandc->regs->spi_cfg, NAND_FLASH_SPI_CFG, 1, 0);
++
++      snand_cfg_val &= ~LOAD_CLK_CNTR_INIT_EN;
++      snandc->regs->spi_cfg = cpu_to_le32(snand_cfg_val);
++
++      qcom_write_reg_dma(snandc, &snandc->regs->spi_cfg, NAND_FLASH_SPI_CFG, 1, 0);
++
++      qcom_write_reg_dma(snandc, &snandc->regs->num_addr_cycle, NAND_NUM_ADDR_CYCLES, 1, 0);
++      qcom_write_reg_dma(snandc, &snandc->regs->busy_wait_cnt, NAND_BUSY_CHECK_WAIT_CNT, 1,
++                         NAND_BAM_NEXT_SGL);
++
++      ret = qcom_submit_descs(snandc);
++      if (ret) {
++              dev_err(snandc->dev, "failure in submitting spi init descriptor\n");
++              return ret;
++      }
++
++      return ret;
++}
++
++static int qcom_spi_ooblayout_ecc(struct mtd_info *mtd, int section,
++                                struct mtd_oob_region *oobregion)
++{
++      struct nand_device *nand = mtd_to_nanddev(mtd);
++      struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand);
++      struct qpic_ecc *qecc = snandc->qspi->ecc;
++
++      if (section > 1)
++              return -ERANGE;
++
++      oobregion->length = qecc->ecc_bytes_hw + qecc->spare_bytes;
++      oobregion->offset = mtd->oobsize - oobregion->length;
++
++      return 0;
++}
++
++static int qcom_spi_ooblayout_free(struct mtd_info *mtd, int section,
++                                 struct mtd_oob_region *oobregion)
++{
++      struct nand_device *nand = mtd_to_nanddev(mtd);
++      struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand);
++      struct qpic_ecc *qecc = snandc->qspi->ecc;
++
++      if (section)
++              return -ERANGE;
++
++      oobregion->length = qecc->steps * 4;
++      oobregion->offset = ((qecc->steps - 1) * qecc->bytes) + qecc->bbm_size;
++
++      return 0;
++}
++
++static const struct mtd_ooblayout_ops qcom_spi_ooblayout = {
++      .ecc = qcom_spi_ooblayout_ecc,
++      .free = qcom_spi_ooblayout_free,
++};
++
++static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
++{
++      struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand);
++      struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
++      struct mtd_info *mtd = nanddev_to_mtd(nand);
++      int cwperpage, bad_block_byte;
++      struct qpic_ecc *ecc_cfg;
++
++      cwperpage = mtd->writesize / NANDC_STEP_SIZE;
++      snandc->qspi->num_cw = cwperpage;
++
++      ecc_cfg = kzalloc(sizeof(*ecc_cfg), GFP_KERNEL);
++      if (!ecc_cfg)
++              return -ENOMEM;
++      snandc->qspi->oob_buf = kzalloc(mtd->writesize + mtd->oobsize,
++                                      GFP_KERNEL);
++      if (!snandc->qspi->oob_buf)
++              return -ENOMEM;
++
++      memset(snandc->qspi->oob_buf, 0xff, mtd->writesize + mtd->oobsize);
++
++      nand->ecc.ctx.priv = ecc_cfg;
++      snandc->qspi->mtd = mtd;
++
++      ecc_cfg->ecc_bytes_hw = 7;
++      ecc_cfg->spare_bytes = 4;
++      ecc_cfg->bbm_size = 1;
++      ecc_cfg->bch_enabled = true;
++      ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size;
++
++      ecc_cfg->steps = 4;
++      ecc_cfg->strength = 4;
++      ecc_cfg->step_size = 512;
++      ecc_cfg->cw_data = 516;
++      ecc_cfg->cw_size = ecc_cfg->cw_data + ecc_cfg->bytes;
++      bad_block_byte = mtd->writesize - ecc_cfg->cw_size * (cwperpage - 1) + 1;
++
++      mtd_set_ooblayout(mtd, &qcom_spi_ooblayout);
++
++      ecc_cfg->cfg0 = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) |
++                      FIELD_PREP(UD_SIZE_BYTES_MASK, ecc_cfg->cw_data) |
++                      FIELD_PREP(DISABLE_STATUS_AFTER_WRITE, 1) |
++                      FIELD_PREP(NUM_ADDR_CYCLES_MASK, 3) |
++                      FIELD_PREP(ECC_PARITY_SIZE_BYTES_RS, ecc_cfg->ecc_bytes_hw) |
++                      FIELD_PREP(STATUS_BFR_READ, 0) |
++                      FIELD_PREP(SET_RD_MODE_AFTER_STATUS, 1) |
++                      FIELD_PREP(SPARE_SIZE_BYTES_MASK, ecc_cfg->spare_bytes);
++
++      ecc_cfg->cfg1 = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 0) |
++                      FIELD_PREP(CS_ACTIVE_BSY, 0) |
++                      FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, bad_block_byte) |
++                      FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 0) |
++                      FIELD_PREP(WR_RD_BSY_GAP_MASK, 20) |
++                      FIELD_PREP(WIDE_FLASH, 0) |
++                      FIELD_PREP(ENABLE_BCH_ECC, ecc_cfg->bch_enabled);
++
++      ecc_cfg->cfg0_raw = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) |
++                          FIELD_PREP(NUM_ADDR_CYCLES_MASK, 3) |
++                          FIELD_PREP(UD_SIZE_BYTES_MASK, ecc_cfg->cw_size) |
++                          FIELD_PREP(SPARE_SIZE_BYTES_MASK, 0);
++
++      ecc_cfg->cfg1_raw = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 0) |
++                          FIELD_PREP(CS_ACTIVE_BSY, 0) |
++                          FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, 17) |
++                          FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 1) |
++                          FIELD_PREP(WR_RD_BSY_GAP_MASK, 20) |
++                          FIELD_PREP(WIDE_FLASH, 0) |
++                          FIELD_PREP(DEV0_CFG1_ECC_DISABLE, 1);
++
++      ecc_cfg->ecc_bch_cfg = FIELD_PREP(ECC_CFG_ECC_DISABLE, !ecc_cfg->bch_enabled) |
++                             FIELD_PREP(ECC_SW_RESET, 0) |
++                             FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) |
++                             FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) |
++                             FIELD_PREP(ECC_MODE_MASK, 0) |
++                             FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw);
++
++      ecc_cfg->ecc_buf_cfg = 0x203 << NUM_STEPS;
++      ecc_cfg->clrflashstatus = FS_READY_BSY_N;
++      ecc_cfg->clrreadstatus = 0xc0;
++
++      conf->step_size = ecc_cfg->step_size;
++      conf->strength = ecc_cfg->strength;
++
++      snandc->regs->erased_cw_detect_cfg_clr = cpu_to_le32(CLR_ERASED_PAGE_DET);
++      snandc->regs->erased_cw_detect_cfg_set = cpu_to_le32(SET_ERASED_PAGE_DET);
++
++      dev_dbg(snandc->dev, "ECC strength: %u bits per %u bytes\n",
++              ecc_cfg->strength, ecc_cfg->step_size);
++
++      return 0;
++}
++
++static void qcom_spi_ecc_cleanup_ctx_pipelined(struct nand_device *nand)
++{
++      struct qpic_ecc *ecc_cfg = nand_to_ecc_ctx(nand);
++
++      kfree(ecc_cfg);
++}
++
++static int qcom_spi_ecc_prepare_io_req_pipelined(struct nand_device *nand,
++                                               struct nand_page_io_req *req)
++{
++      struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand);
++      struct qpic_ecc *ecc_cfg = nand_to_ecc_ctx(nand);
++
++      snandc->qspi->ecc = ecc_cfg;
++      snandc->qspi->raw_rw = false;
++      snandc->qspi->oob_rw = false;
++      snandc->qspi->page_rw = false;
++
++      if (req->datalen)
++              snandc->qspi->page_rw = true;
++
++      if (req->ooblen)
++              snandc->qspi->oob_rw = true;
++
++      if (req->mode == MTD_OPS_RAW)
++              snandc->qspi->raw_rw = true;
++
++      return 0;
++}
++
++static int qcom_spi_ecc_finish_io_req_pipelined(struct nand_device *nand,
++                                              struct nand_page_io_req *req)
++{
++      struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand);
++      struct mtd_info *mtd = nanddev_to_mtd(nand);
++
++      if (req->mode == MTD_OPS_RAW || req->type != NAND_PAGE_READ)
++              return 0;
++
++      if (snandc->qspi->ecc_stats.failed)
++              mtd->ecc_stats.failed += snandc->qspi->ecc_stats.failed;
++      else
++              mtd->ecc_stats.corrected += snandc->qspi->ecc_stats.corrected;
++
++      if (snandc->qspi->ecc_stats.failed)
++              return -EBADMSG;
++      else
++              return snandc->qspi->ecc_stats.bitflips;
++}
++
++static struct nand_ecc_engine_ops qcom_spi_ecc_engine_ops_pipelined = {
++      .init_ctx = qcom_spi_ecc_init_ctx_pipelined,
++      .cleanup_ctx = qcom_spi_ecc_cleanup_ctx_pipelined,
++      .prepare_io_req = qcom_spi_ecc_prepare_io_req_pipelined,
++      .finish_io_req = qcom_spi_ecc_finish_io_req_pipelined,
++};
++
++/* helper to configure location register values */
++static void qcom_spi_set_read_loc(struct qcom_nand_controller *snandc, int cw, int reg,
++                                int cw_offset, int read_size, int is_last_read_loc)
++{
++      int reg_base = NAND_READ_LOCATION_0;
++      int num_cw = snandc->qspi->num_cw;
++
++      if (cw == (num_cw - 1))
++              reg_base = NAND_READ_LOCATION_LAST_CW_0;
++
++      reg_base += reg * 4;
++
++      if (cw == (num_cw - 1))
++              return qcom_spi_set_read_loc_last(snandc, reg_base, cw_offset,
++                                                read_size, is_last_read_loc);
++      else
++              return qcom_spi_set_read_loc_first(snandc, reg_base, cw_offset,
++                                                 read_size, is_last_read_loc);
++}
++
++static void
++qcom_spi_config_cw_read(struct qcom_nand_controller *snandc, bool use_ecc, int cw)
++{
++      __le32 *reg = &snandc->regs->read_location0;
++      int num_cw = snandc->qspi->num_cw;
++
++      qcom_write_reg_dma(snandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL);
++      if (cw == (num_cw - 1)) {
++              reg = &snandc->regs->read_location_last0;
++              qcom_write_reg_dma(snandc, reg, NAND_READ_LOCATION_LAST_CW_0, 4,
++                                 NAND_BAM_NEXT_SGL);
++      }
++
++      qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
++
++      qcom_read_reg_dma(snandc, NAND_FLASH_STATUS, 2, 0);
++      qcom_read_reg_dma(snandc, NAND_ERASED_CW_DETECT_STATUS, 1,
++                        NAND_BAM_NEXT_SGL);
++}
++
++static int qcom_spi_block_erase(struct qcom_nand_controller *snandc)
++{
++      struct qpic_ecc *ecc_cfg = snandc->qspi->ecc;
++      int ret;
++
++      snandc->buf_count = 0;
++      snandc->buf_start = 0;
++      qcom_clear_read_regs(snandc);
++      qcom_clear_bam_transaction(snandc);
++
++      snandc->regs->cmd = snandc->qspi->cmd;
++      snandc->regs->addr0 = snandc->qspi->addr1;
++      snandc->regs->addr1 = snandc->qspi->addr2;
++      snandc->regs->cfg0 = cpu_to_le32(ecc_cfg->cfg0_raw & ~(7 << CW_PER_PAGE));
++      snandc->regs->cfg1 = cpu_to_le32(ecc_cfg->cfg1_raw);
++      snandc->regs->exec = cpu_to_le32(1);
++
++      qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
++
++      ret = qcom_submit_descs(snandc);
++      if (ret) {
++              dev_err(snandc->dev, "failure to erase block\n");
++              return ret;
++      }
++
++      return 0;
++}
++
++static void qcom_spi_config_single_cw_page_read(struct qcom_nand_controller *snandc,
++                                              bool use_ecc, int cw)
++{
++      __le32 *reg = &snandc->regs->read_location0;
++      int num_cw = snandc->qspi->num_cw;
++
++      qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0);
++      qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
++      qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr,
++                         NAND_ERASED_CW_DETECT_CFG, 1, 0);
++      qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set,
++                         NAND_ERASED_CW_DETECT_CFG, 1,
++                         NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
++
++      if (cw == (num_cw - 1)) {
++              reg = &snandc->regs->read_location_last0;
++              qcom_write_reg_dma(snandc, reg, NAND_READ_LOCATION_LAST_CW_0, 4, NAND_BAM_NEXT_SGL);
++      }
++      qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
++
++      qcom_read_reg_dma(snandc, NAND_FLASH_STATUS, 1, 0);
++}
++
++static int qcom_spi_read_last_cw(struct qcom_nand_controller *snandc,
++                               const struct spi_mem_op *op)
++{
++      struct qpic_ecc *ecc_cfg = snandc->qspi->ecc;
++      struct mtd_info *mtd = snandc->qspi->mtd;
++      int size, ret = 0;
++      int col,  bbpos;
++      u32 cfg0, cfg1, ecc_bch_cfg;
++      u32 num_cw = snandc->qspi->num_cw;
++
++      qcom_clear_bam_transaction(snandc);
++      qcom_clear_read_regs(snandc);
++
++      size = ecc_cfg->cw_size;
++      col = ecc_cfg->cw_size * (num_cw - 1);
++
++      memset(snandc->data_buffer, 0xff, size);
++      snandc->regs->addr0 = (snandc->qspi->addr1 | cpu_to_le32(col));
++      snandc->regs->addr1 = snandc->qspi->addr2;
++
++      cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) |
++              0 << CW_PER_PAGE;
++      cfg1 = ecc_cfg->cfg1_raw;
++      ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
++
++      snandc->regs->cmd = snandc->qspi->cmd;
++      snandc->regs->cfg0 = cpu_to_le32(cfg0);
++      snandc->regs->cfg1 = cpu_to_le32(cfg1);
++      snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg);
++      snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus);
++      snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus);
++      snandc->regs->exec = cpu_to_le32(1);
++
++      qcom_spi_set_read_loc(snandc, num_cw - 1, 0, 0, ecc_cfg->cw_size, 1);
++
++      qcom_spi_config_single_cw_page_read(snandc, false, num_cw - 1);
++
++      qcom_read_data_dma(snandc, FLASH_BUF_ACC, snandc->data_buffer, size, 0);
++
++      ret = qcom_submit_descs(snandc);
++      if (ret) {
++              dev_err(snandc->dev, "failed to read last cw\n");
++              return ret;
++      }
++
++      qcom_nandc_dev_to_mem(snandc, true);
++      u32 flash = le32_to_cpu(snandc->reg_read_buf[0]);
++
++      if (flash & (FS_OP_ERR | FS_MPU_ERR))
++              return -EIO;
++
++      bbpos = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1);
++
++      if (snandc->data_buffer[bbpos] == 0xff)
++              snandc->data_buffer[bbpos + 1] = 0xff;
++      if (snandc->data_buffer[bbpos] != 0xff)
++              snandc->data_buffer[bbpos + 1] = snandc->data_buffer[bbpos];
++
++      memcpy(op->data.buf.in, snandc->data_buffer + bbpos, op->data.nbytes);
++
++      return ret;
++}
++
++static int qcom_spi_check_error(struct qcom_nand_controller *snandc, u8 *data_buf, u8 *oob_buf)
++{
++      struct snandc_read_status *buf;
++      struct qpic_ecc *ecc_cfg = snandc->qspi->ecc;
++      int i, num_cw = snandc->qspi->num_cw;
++      bool flash_op_err = false, erased;
++      unsigned int max_bitflips = 0;
++      unsigned int uncorrectable_cws = 0;
++
++      snandc->qspi->ecc_stats.failed = 0;
++      snandc->qspi->ecc_stats.corrected = 0;
++
++      qcom_nandc_dev_to_mem(snandc, true);
++      buf = (struct snandc_read_status *)snandc->reg_read_buf;
++
++      for (i = 0; i < num_cw; i++, buf++) {
++              u32 flash, buffer, erased_cw;
++              int data_len, oob_len;
++
++              if (i == (num_cw - 1)) {
++                      data_len = NANDC_STEP_SIZE - ((num_cw - 1) << 2);
++                      oob_len = num_cw << 2;
++              } else {
++                      data_len = ecc_cfg->cw_data;
++                      oob_len = 0;
++              }
++
++              flash = le32_to_cpu(buf->snandc_flash);
++              buffer = le32_to_cpu(buf->snandc_buffer);
++              erased_cw = le32_to_cpu(buf->snandc_erased_cw);
++
++              if ((flash & FS_OP_ERR) && (buffer & BS_UNCORRECTABLE_BIT)) {
++                      if (ecc_cfg->bch_enabled)
++                              erased = (erased_cw & ERASED_CW) == ERASED_CW;
++                      else
++                              erased = false;
++
++                      if (!erased)
++                              uncorrectable_cws |= BIT(i);
++
++              } else if (flash & (FS_OP_ERR | FS_MPU_ERR)) {
++                      flash_op_err = true;
++              } else {
++                      unsigned int stat;
++
++                      stat = buffer & BS_CORRECTABLE_ERR_MSK;
++                      snandc->qspi->ecc_stats.corrected += stat;
++                      max_bitflips = max(max_bitflips, stat);
++              }
++
++              if (data_buf)
++                      data_buf += data_len;
++              if (oob_buf)
++                      oob_buf += oob_len + ecc_cfg->bytes;
++      }
++
++      if (flash_op_err)
++              return -EIO;
++
++      if (!uncorrectable_cws)
++              snandc->qspi->ecc_stats.bitflips = max_bitflips;
++      else
++              snandc->qspi->ecc_stats.failed++;
++
++      return 0;
++}
++
++static int qcom_spi_check_raw_flash_errors(struct qcom_nand_controller *snandc, int cw_cnt)
++{
++      int i;
++
++      qcom_nandc_dev_to_mem(snandc, true);
++
++      for (i = 0; i < cw_cnt; i++) {
++              u32 flash = le32_to_cpu(snandc->reg_read_buf[i]);
++
++              if (flash & (FS_OP_ERR | FS_MPU_ERR))
++                      return -EIO;
++      }
++
++      return 0;
++}
++
++static int qcom_spi_read_cw_raw(struct qcom_nand_controller *snandc, u8 *data_buf,
++                              u8 *oob_buf, int cw)
++{
++      struct qpic_ecc *ecc_cfg = snandc->qspi->ecc;
++      struct mtd_info *mtd = snandc->qspi->mtd;
++      int data_size1, data_size2, oob_size1, oob_size2;
++      int ret, reg_off = FLASH_BUF_ACC, read_loc = 0;
++      int raw_cw = cw;
++      u32 cfg0, cfg1, ecc_bch_cfg, num_cw = snandc->qspi->num_cw;
++      int col;
++
++      snandc->buf_count = 0;
++      snandc->buf_start = 0;
++      qcom_clear_read_regs(snandc);
++      qcom_clear_bam_transaction(snandc);
++      raw_cw = num_cw - 1;
++
++      cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) |
++                              0 << CW_PER_PAGE;
++      cfg1 = ecc_cfg->cfg1_raw;
++      ecc_bch_cfg = ECC_CFG_ECC_DISABLE;
++
++      col = ecc_cfg->cw_size * cw;
++
++      snandc->regs->addr0 = (snandc->qspi->addr1 | cpu_to_le32(col));
++      snandc->regs->addr1 = snandc->qspi->addr2;
++      snandc->regs->cmd = snandc->qspi->cmd;
++      snandc->regs->cfg0 = cpu_to_le32(cfg0);
++      snandc->regs->cfg1 = cpu_to_le32(cfg1);
++      snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg);
++      snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus);
++      snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus);
++      snandc->regs->exec = cpu_to_le32(1);
++
++      qcom_spi_set_read_loc(snandc, raw_cw, 0, 0, ecc_cfg->cw_size, 1);
++
++      qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0);
++      qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
++      qcom_write_reg_dma(snandc, &snandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0);
++
++      qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr,
++                         NAND_ERASED_CW_DETECT_CFG, 1, 0);
++      qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set,
++                         NAND_ERASED_CW_DETECT_CFG, 1,
++                         NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
++
++      data_size1 = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1);
++      oob_size1 = ecc_cfg->bbm_size;
++
++      if (cw == (num_cw - 1)) {
++              data_size2 = NANDC_STEP_SIZE - data_size1 -
++                           ((num_cw - 1) * 4);
++              oob_size2 = (num_cw * 4) + ecc_cfg->ecc_bytes_hw +
++                          ecc_cfg->spare_bytes;
++      } else {
++              data_size2 = ecc_cfg->cw_data - data_size1;
++              oob_size2 = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes;
++      }
++
++      qcom_spi_set_read_loc(snandc, cw, 0, read_loc, data_size1, 0);
++      read_loc += data_size1;
++
++      qcom_spi_set_read_loc(snandc, cw, 1, read_loc, oob_size1, 0);
++      read_loc += oob_size1;
++
++      qcom_spi_set_read_loc(snandc, cw, 2, read_loc, data_size2, 0);
++      read_loc += data_size2;
++
++      qcom_spi_set_read_loc(snandc, cw, 3, read_loc, oob_size2, 1);
++
++      qcom_spi_config_cw_read(snandc, false, raw_cw);
++
++      qcom_read_data_dma(snandc, reg_off, data_buf, data_size1, 0);
++      reg_off += data_size1;
++
++      qcom_read_data_dma(snandc, reg_off, oob_buf, oob_size1, 0);
++      reg_off += oob_size1;
++
++      qcom_read_data_dma(snandc, reg_off, data_buf + data_size1, data_size2, 0);
++      reg_off += data_size2;
++
++      qcom_read_data_dma(snandc, reg_off, oob_buf + oob_size1, oob_size2, 0);
++
++      ret = qcom_submit_descs(snandc);
++      if (ret) {
++              dev_err(snandc->dev, "failure to read raw cw %d\n", cw);
++              return ret;
++      }
++
++      return qcom_spi_check_raw_flash_errors(snandc, 1);
++}
++
++static int qcom_spi_read_page_raw(struct qcom_nand_controller *snandc,
++                                const struct spi_mem_op *op)
++{
++      struct qpic_ecc *ecc_cfg = snandc->qspi->ecc;
++      u8 *data_buf = NULL, *oob_buf = NULL;
++      int ret, cw;
++      u32 num_cw = snandc->qspi->num_cw;
++
++      if (snandc->qspi->page_rw)
++              data_buf = op->data.buf.in;
++
++      oob_buf = snandc->qspi->oob_buf;
++      memset(oob_buf, 0xff, OOB_BUF_SIZE);
++
++      for (cw = 0; cw < num_cw; cw++) {
++              ret = qcom_spi_read_cw_raw(snandc, data_buf, oob_buf, cw);
++              if (ret)
++                      return ret;
++
++              if (data_buf)
++                      data_buf += ecc_cfg->cw_data;
++              if (oob_buf)
++                      oob_buf += ecc_cfg->bytes;
++      }
++
++      return 0;
++}
++
++static int qcom_spi_read_page_ecc(struct qcom_nand_controller *snandc,
++                                const struct spi_mem_op *op)
++{
++      struct qpic_ecc *ecc_cfg = snandc->qspi->ecc;
++      u8 *data_buf = NULL, *data_buf_start, *oob_buf = NULL, *oob_buf_start;
++      int ret, i;
++      u32 cfg0, cfg1, ecc_bch_cfg, num_cw = snandc->qspi->num_cw;
++
++      data_buf = op->data.buf.in;
++      data_buf_start = data_buf;
++
++      oob_buf = snandc->qspi->oob_buf;
++      oob_buf_start = oob_buf;
++
++      snandc->buf_count = 0;
++      snandc->buf_start = 0;
++      qcom_clear_read_regs(snandc);
++
++      cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) |
++                              (num_cw - 1) << CW_PER_PAGE;
++      cfg1 = ecc_cfg->cfg1;
++      ecc_bch_cfg = ecc_cfg->ecc_bch_cfg;
++
++      snandc->regs->addr0 = snandc->qspi->addr1;
++      snandc->regs->addr1 = snandc->qspi->addr2;
++      snandc->regs->cmd = snandc->qspi->cmd;
++      snandc->regs->cfg0 = cpu_to_le32(cfg0);
++      snandc->regs->cfg1 = cpu_to_le32(cfg1);
++      snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg);
++      snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus);
++      snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus);
++      snandc->regs->exec = cpu_to_le32(1);
++
++      qcom_spi_set_read_loc(snandc, 0, 0, 0, ecc_cfg->cw_data, 1);
++
++      qcom_clear_bam_transaction(snandc);
++
++      qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0);
++      qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
++      qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr,
++                         NAND_ERASED_CW_DETECT_CFG, 1, 0);
++      qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set,
++                         NAND_ERASED_CW_DETECT_CFG, 1,
++                         NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
++
++      for (i = 0; i < num_cw; i++) {
++              int data_size, oob_size;
++
++              if (i == (num_cw - 1)) {
++                      data_size = 512 - ((num_cw - 1) << 2);
++                      oob_size = (num_cw << 2) + ecc_cfg->ecc_bytes_hw +
++                                  ecc_cfg->spare_bytes;
++              } else {
++                      data_size = ecc_cfg->cw_data;
++                      oob_size = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes;
++              }
++
++              if (data_buf && oob_buf) {
++                      qcom_spi_set_read_loc(snandc, i, 0, 0, data_size, 0);
++                      qcom_spi_set_read_loc(snandc, i, 1, data_size, oob_size, 1);
++              } else if (data_buf) {
++                      qcom_spi_set_read_loc(snandc, i, 0, 0, data_size, 1);
++              } else {
++                      qcom_spi_set_read_loc(snandc, i, 0, data_size, oob_size, 1);
++              }
++
++              qcom_spi_config_cw_read(snandc, true, i);
++
++              if (data_buf)
++                      qcom_read_data_dma(snandc, FLASH_BUF_ACC, data_buf,
++                                         data_size, 0);
++              if (oob_buf) {
++                      int j;
++
++                      for (j = 0; j < ecc_cfg->bbm_size; j++)
++                              *oob_buf++ = 0xff;
++
++                      qcom_read_data_dma(snandc, FLASH_BUF_ACC + data_size,
++                                         oob_buf, oob_size, 0);
++              }
++
++              if (data_buf)
++                      data_buf += data_size;
++              if (oob_buf)
++                      oob_buf += oob_size;
++      }
++
++      ret = qcom_submit_descs(snandc);
++      if (ret) {
++              dev_err(snandc->dev, "failure to read page\n");
++              return ret;
++      }
++
++      return qcom_spi_check_error(snandc, data_buf_start, oob_buf_start);
++}
++
++static int qcom_spi_read_page_oob(struct qcom_nand_controller *snandc,
++                                const struct spi_mem_op *op)
++{
++      struct qpic_ecc *ecc_cfg = snandc->qspi->ecc;
++      u8 *data_buf = NULL, *data_buf_start, *oob_buf = NULL, *oob_buf_start;
++      int ret, i;
++      u32 cfg0, cfg1, ecc_bch_cfg, num_cw = snandc->qspi->num_cw;
++
++      oob_buf = op->data.buf.in;
++      oob_buf_start = oob_buf;
++
++      data_buf_start = data_buf;
++
++      snandc->buf_count = 0;
++      snandc->buf_start = 0;
++      qcom_clear_read_regs(snandc);
++      qcom_clear_bam_transaction(snandc);
++
++      cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) |
++                              (num_cw - 1) << CW_PER_PAGE;
++      cfg1 = ecc_cfg->cfg1;
++      ecc_bch_cfg = ecc_cfg->ecc_bch_cfg;
++
++      snandc->regs->addr0 = snandc->qspi->addr1;
++      snandc->regs->addr1 = snandc->qspi->addr2;
++      snandc->regs->cmd = snandc->qspi->cmd;
++      snandc->regs->cfg0 = cpu_to_le32(cfg0);
++      snandc->regs->cfg1 = cpu_to_le32(cfg1);
++      snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg);
++      snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus);
++      snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus);
++      snandc->regs->exec = cpu_to_le32(1);
++
++      qcom_spi_set_read_loc(snandc, 0, 0, 0, ecc_cfg->cw_data, 1);
++
++      qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0);
++      qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
++      qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr,
++                         NAND_ERASED_CW_DETECT_CFG, 1, 0);
++      qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set,
++                         NAND_ERASED_CW_DETECT_CFG, 1,
++                         NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
++
++      for (i = 0; i < num_cw; i++) {
++              int data_size, oob_size;
++
++              if (i == (num_cw - 1)) {
++                      data_size = NANDC_STEP_SIZE - ((num_cw - 1) << 2);
++                      oob_size = (num_cw << 2) + ecc_cfg->ecc_bytes_hw +
++                                  ecc_cfg->spare_bytes;
++              } else {
++                      data_size = ecc_cfg->cw_data;
++                      oob_size = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes;
++              }
++
++              qcom_spi_set_read_loc(snandc, i, 0, data_size, oob_size, 1);
++
++              qcom_spi_config_cw_read(snandc, true, i);
++
++              if (oob_buf) {
++                      int j;
++
++                      for (j = 0; j < ecc_cfg->bbm_size; j++)
++                              *oob_buf++ = 0xff;
++
++                      qcom_read_data_dma(snandc, FLASH_BUF_ACC + data_size,
++                                         oob_buf, oob_size, 0);
++              }
++
++              if (oob_buf)
++                      oob_buf += oob_size;
++      }
++
++      ret = qcom_submit_descs(snandc);
++      if (ret) {
++              dev_err(snandc->dev, "failure to read oob\n");
++              return ret;
++      }
++
++      return qcom_spi_check_error(snandc, data_buf_start, oob_buf_start);
++}
++
++static int qcom_spi_read_page(struct qcom_nand_controller *snandc,
++                            const struct spi_mem_op *op)
++{
++      if (snandc->qspi->page_rw && snandc->qspi->raw_rw)
++              return qcom_spi_read_page_raw(snandc, op);
++
++      if (snandc->qspi->page_rw)
++              return qcom_spi_read_page_ecc(snandc, op);
++
++      if (snandc->qspi->oob_rw && snandc->qspi->raw_rw)
++              return qcom_spi_read_last_cw(snandc, op);
++
++      if (snandc->qspi->oob_rw)
++              return qcom_spi_read_page_oob(snandc, op);
++
++      return 0;
++}
++
++static void qcom_spi_config_page_write(struct qcom_nand_controller *snandc)
++{
++      qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0);
++      qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
++      qcom_write_reg_dma(snandc, &snandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG,
++                         1, NAND_BAM_NEXT_SGL);
++}
++
++static void qcom_spi_config_cw_write(struct qcom_nand_controller *snandc)
++{
++      qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
++      qcom_read_reg_dma(snandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
++
++      qcom_write_reg_dma(snandc, &snandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0);
++      qcom_write_reg_dma(snandc, &snandc->regs->clrreadstatus, NAND_READ_STATUS, 1,
++                         NAND_BAM_NEXT_SGL);
++}
++
++static int qcom_spi_program_raw(struct qcom_nand_controller *snandc,
++                              const struct spi_mem_op *op)
++{
++      struct qpic_ecc *ecc_cfg = snandc->qspi->ecc;
++      struct mtd_info *mtd = snandc->qspi->mtd;
++      u8 *data_buf = NULL, *oob_buf = NULL;
++      int i, ret;
++      int num_cw = snandc->qspi->num_cw;
++      u32 cfg0, cfg1, ecc_bch_cfg;
++
++      cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) |
++                      (num_cw - 1) << CW_PER_PAGE;
++      cfg1 = ecc_cfg->cfg1_raw;
++      ecc_bch_cfg = ECC_CFG_ECC_DISABLE;
++
++      data_buf = snandc->qspi->data_buf;
++
++      oob_buf = snandc->qspi->oob_buf;
++      memset(oob_buf, 0xff, OOB_BUF_SIZE);
++
++      snandc->buf_count = 0;
++      snandc->buf_start = 0;
++      qcom_clear_read_regs(snandc);
++      qcom_clear_bam_transaction(snandc);
++
++      snandc->regs->addr0 = snandc->qspi->addr1;
++      snandc->regs->addr1 = snandc->qspi->addr2;
++      snandc->regs->cmd = snandc->qspi->cmd;
++      snandc->regs->cfg0 = cpu_to_le32(cfg0);
++      snandc->regs->cfg1 = cpu_to_le32(cfg1);
++      snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg);
++      snandc->regs->clrflashstatus = cpu_to_le32(ecc_cfg->clrflashstatus);
++      snandc->regs->clrreadstatus = cpu_to_le32(ecc_cfg->clrreadstatus);
++      snandc->regs->exec = cpu_to_le32(1);
++
++      qcom_spi_config_page_write(snandc);
++
++      for (i = 0; i < num_cw; i++) {
++              int data_size1, data_size2, oob_size1, oob_size2;
++              int reg_off = FLASH_BUF_ACC;
++
++              data_size1 = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1);
++              oob_size1 = ecc_cfg->bbm_size;
++
++              if ((i == (num_cw - 1))) {
++                      data_size2 = NANDC_STEP_SIZE - data_size1 -
++                                   ((num_cw - 1) << 2);
++                      oob_size2 = (num_cw << 2) + ecc_cfg->ecc_bytes_hw +
++                                  ecc_cfg->spare_bytes;
++              } else {
++                      data_size2 = ecc_cfg->cw_data - data_size1;
++                      oob_size2 = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes;
++              }
++
++              qcom_write_data_dma(snandc, reg_off, data_buf, data_size1,
++                                  NAND_BAM_NO_EOT);
++              reg_off += data_size1;
++              data_buf += data_size1;
++
++              qcom_write_data_dma(snandc, reg_off, oob_buf, oob_size1,
++                                  NAND_BAM_NO_EOT);
++              oob_buf += oob_size1;
++              reg_off += oob_size1;
++
++              qcom_write_data_dma(snandc, reg_off, data_buf, data_size2,
++                                  NAND_BAM_NO_EOT);
++              reg_off += data_size2;
++              data_buf += data_size2;
++
++              qcom_write_data_dma(snandc, reg_off, oob_buf, oob_size2, 0);
++              oob_buf += oob_size2;
++
++              qcom_spi_config_cw_write(snandc);
++      }
++
++      ret = qcom_submit_descs(snandc);
++      if (ret) {
++              dev_err(snandc->dev, "failure to write raw page\n");
++              return ret;
++      }
++
++      return 0;
++}
++
++static int qcom_spi_program_ecc(struct qcom_nand_controller *snandc,
++                              const struct spi_mem_op *op)
++{
++      struct qpic_ecc *ecc_cfg = snandc->qspi->ecc;
++      u8 *data_buf = NULL, *oob_buf = NULL;
++      int i, ret;
++      int num_cw = snandc->qspi->num_cw;
++      u32 cfg0, cfg1, ecc_bch_cfg, ecc_buf_cfg;
++
++      cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) |
++                              (num_cw - 1) << CW_PER_PAGE;
++      cfg1 = ecc_cfg->cfg1;
++      ecc_bch_cfg = ecc_cfg->ecc_bch_cfg;
++      ecc_buf_cfg = ecc_cfg->ecc_buf_cfg;
++
++      if (snandc->qspi->data_buf)
++              data_buf = snandc->qspi->data_buf;
++
++      oob_buf = snandc->qspi->oob_buf;
++
++      snandc->buf_count = 0;
++      snandc->buf_start = 0;
++      qcom_clear_read_regs(snandc);
++      qcom_clear_bam_transaction(snandc);
++
++      snandc->regs->addr0 = snandc->qspi->addr1;
++      snandc->regs->addr1 = snandc->qspi->addr2;
++      snandc->regs->cmd = snandc->qspi->cmd;
++      snandc->regs->cfg0 = cpu_to_le32(cfg0);
++      snandc->regs->cfg1 = cpu_to_le32(cfg1);
++      snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg);
++      snandc->regs->ecc_buf_cfg = cpu_to_le32(ecc_buf_cfg);
++      snandc->regs->exec = cpu_to_le32(1);
++
++      qcom_spi_config_page_write(snandc);
++
++      for (i = 0; i < num_cw; i++) {
++              int data_size, oob_size;
++
++              if (i == (num_cw - 1)) {
++                      data_size = NANDC_STEP_SIZE - ((num_cw - 1) << 2);
++                      oob_size = (num_cw << 2) + ecc_cfg->ecc_bytes_hw +
++                                  ecc_cfg->spare_bytes;
++              } else {
++                      data_size = ecc_cfg->cw_data;
++                      oob_size = ecc_cfg->bytes;
++              }
++
++              if (data_buf)
++                      qcom_write_data_dma(snandc, FLASH_BUF_ACC, data_buf, data_size,
++                                          i == (num_cw - 1) ? NAND_BAM_NO_EOT : 0);
++
++              if (i == (num_cw - 1)) {
++                      if (oob_buf) {
++                              oob_buf += ecc_cfg->bbm_size;
++                              qcom_write_data_dma(snandc, FLASH_BUF_ACC + data_size,
++                                                  oob_buf, oob_size, 0);
++                      }
++              }
++
++              qcom_spi_config_cw_write(snandc);
++
++              if (data_buf)
++                      data_buf += data_size;
++              if (oob_buf)
++                      oob_buf += oob_size;
++      }
++
++      ret = qcom_submit_descs(snandc);
++      if (ret) {
++              dev_err(snandc->dev, "failure to write page\n");
++              return ret;
++      }
++
++      return 0;
++}
++
++static int qcom_spi_program_oob(struct qcom_nand_controller *snandc,
++                              const struct spi_mem_op *op)
++{
++      struct qpic_ecc *ecc_cfg = snandc->qspi->ecc;
++      u8 *oob_buf = NULL;
++      int ret, col, data_size, oob_size;
++      int num_cw = snandc->qspi->num_cw;
++      u32 cfg0, cfg1, ecc_bch_cfg, ecc_buf_cfg;
++
++      cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) |
++                              (num_cw - 1) << CW_PER_PAGE;
++      cfg1 = ecc_cfg->cfg1;
++      ecc_bch_cfg = ecc_cfg->ecc_bch_cfg;
++      ecc_buf_cfg = ecc_cfg->ecc_buf_cfg;
++
++      col = ecc_cfg->cw_size * (num_cw - 1);
++
++      oob_buf = snandc->qspi->data_buf;
++
++      snandc->buf_count = 0;
++      snandc->buf_start = 0;
++      qcom_clear_read_regs(snandc);
++      qcom_clear_bam_transaction(snandc);
++      snandc->regs->addr0 = (snandc->qspi->addr1 | cpu_to_le32(col));
++      snandc->regs->addr1 = snandc->qspi->addr2;
++      snandc->regs->cmd = snandc->qspi->cmd;
++      snandc->regs->cfg0 = cpu_to_le32(cfg0);
++      snandc->regs->cfg1 = cpu_to_le32(cfg1);
++      snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg);
++      snandc->regs->ecc_buf_cfg = cpu_to_le32(ecc_buf_cfg);
++      snandc->regs->exec = cpu_to_le32(1);
++
++      /* calculate the data and oob size for the last codeword/step */
++      data_size = NANDC_STEP_SIZE - ((num_cw - 1) << 2);
++      oob_size = snandc->qspi->mtd->oobavail;
++
++      memset(snandc->data_buffer, 0xff, ecc_cfg->cw_data);
++      /* override new oob content to last codeword */
++      mtd_ooblayout_get_databytes(snandc->qspi->mtd, snandc->data_buffer + data_size,
++                                  oob_buf, 0, snandc->qspi->mtd->oobavail);
++      qcom_spi_config_page_write(snandc);
++      qcom_write_data_dma(snandc, FLASH_BUF_ACC, snandc->data_buffer, data_size + oob_size, 0);
++      qcom_spi_config_cw_write(snandc);
++
++      ret = qcom_submit_descs(snandc);
++      if (ret) {
++              dev_err(snandc->dev, "failure to write oob\n");
++              return ret;
++      }
++
++      return 0;
++}
++
++static int qcom_spi_program_execute(struct qcom_nand_controller *snandc,
++                                  const struct spi_mem_op *op)
++{
++      if (snandc->qspi->page_rw && snandc->qspi->raw_rw)
++              return qcom_spi_program_raw(snandc, op);
++
++      if (snandc->qspi->page_rw)
++              return qcom_spi_program_ecc(snandc, op);
++
++      if (snandc->qspi->oob_rw)
++              return qcom_spi_program_oob(snandc, op);
++
++      return 0;
++}
++
++static u32 qcom_spi_cmd_mapping(struct qcom_nand_controller *snandc, u32 opcode)
++{
++      u32 cmd = 0x0;
++
++      switch (opcode) {
++      case SPINAND_RESET:
++              cmd = (SPI_WP | SPI_HOLD | SPI_TRANSFER_MODE_x1 | OP_RESET_DEVICE);
++              break;
++      case SPINAND_READID:
++              cmd = (SPI_WP | SPI_HOLD | SPI_TRANSFER_MODE_x1 | OP_FETCH_ID);
++              break;
++      case SPINAND_GET_FEATURE:
++              cmd = (SPI_TRANSFER_MODE_x1 | SPI_WP | SPI_HOLD | ACC_FEATURE);
++              break;
++      case SPINAND_SET_FEATURE:
++              cmd = (SPI_TRANSFER_MODE_x1 | SPI_WP | SPI_HOLD | ACC_FEATURE |
++                      QPIC_SET_FEATURE);
++              break;
++      case SPINAND_READ:
++              if (snandc->qspi->raw_rw) {
++                      cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 |
++                                      SPI_WP | SPI_HOLD | OP_PAGE_READ);
++              } else {
++                      cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 |
++                                      SPI_WP | SPI_HOLD | OP_PAGE_READ_WITH_ECC);
++              }
++
++              break;
++      case SPINAND_ERASE:
++              cmd = OP_BLOCK_ERASE | PAGE_ACC | LAST_PAGE | SPI_WP |
++                      SPI_HOLD | SPI_TRANSFER_MODE_x1;
++              break;
++      case SPINAND_WRITE_EN:
++              cmd = SPINAND_WRITE_EN;
++              break;
++      case SPINAND_PROGRAM_EXECUTE:
++              cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 |
++                              SPI_WP | SPI_HOLD | OP_PROGRAM_PAGE);
++              break;
++      case SPINAND_PROGRAM_LOAD:
++              cmd = SPINAND_PROGRAM_LOAD;
++              break;
++      default:
++              dev_err(snandc->dev, "Opcode not supported: %u\n", opcode);
++              return -EOPNOTSUPP;
++      }
++
++      return cmd;
++}
++
++static int qcom_spi_write_page(struct qcom_nand_controller *snandc,
++                             const struct spi_mem_op *op)
++{
++      struct qpic_snand_op s_op = {};
++      u32 cmd;
++
++      cmd = qcom_spi_cmd_mapping(snandc, op->cmd.opcode);
++      if (cmd < 0)
++              return cmd;
++
++      s_op.cmd_reg = cmd;
++
++      if (op->cmd.opcode == SPINAND_PROGRAM_LOAD)
++              snandc->qspi->data_buf = (u8 *)op->data.buf.out;
++
++      return 0;
++}
++
++static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc,
++                               const struct spi_mem_op *op)
++{
++      struct qpic_snand_op s_op = {};
++      u32 cmd;
++      int ret, opcode;
++
++      cmd = qcom_spi_cmd_mapping(snandc, op->cmd.opcode);
++      if (cmd < 0)
++              return cmd;
++
++      s_op.cmd_reg = cmd;
++      s_op.addr1_reg = op->addr.val;
++      s_op.addr2_reg = 0;
++
++      opcode = op->cmd.opcode;
++
++      switch (opcode) {
++      case SPINAND_WRITE_EN:
++              return 0;
++      case SPINAND_PROGRAM_EXECUTE:
++              s_op.addr1_reg = op->addr.val << 16;
++              s_op.addr2_reg = op->addr.val >> 16 & 0xff;
++              snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg);
++              snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg);
++              snandc->qspi->cmd = cpu_to_le32(cmd);
++              return qcom_spi_program_execute(snandc, op);
++      case SPINAND_READ:
++              s_op.addr1_reg = (op->addr.val << 16);
++              s_op.addr2_reg = op->addr.val >> 16 & 0xff;
++              snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg);
++              snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg);
++              snandc->qspi->cmd = cpu_to_le32(cmd);
++              return 0;
++      case SPINAND_ERASE:
++              s_op.addr2_reg = (op->addr.val >> 16) & 0xffff;
++              s_op.addr1_reg = op->addr.val;
++              snandc->qspi->addr1 = cpu_to_le32(s_op.addr1_reg << 16);
++              snandc->qspi->addr2 = cpu_to_le32(s_op.addr2_reg);
++              snandc->qspi->cmd = cpu_to_le32(cmd);
++              qcom_spi_block_erase(snandc);
++              return 0;
++      default:
++              break;
++      }
++
++      snandc->buf_count = 0;
++      snandc->buf_start = 0;
++      qcom_clear_read_regs(snandc);
++      qcom_clear_bam_transaction(snandc);
++
++      snandc->regs->cmd = cpu_to_le32(s_op.cmd_reg);
++      snandc->regs->exec = cpu_to_le32(1);
++      snandc->regs->addr0 = cpu_to_le32(s_op.addr1_reg);
++      snandc->regs->addr1 = cpu_to_le32(s_op.addr2_reg);
++
++      qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL);
++      qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
++
++      ret = qcom_submit_descs(snandc);
++      if (ret)
++              dev_err(snandc->dev, "failure in submitting cmd descriptor\n");
++
++      return ret;
++}
++
++static int qcom_spi_io_op(struct qcom_nand_controller *snandc, const struct spi_mem_op *op)
++{
++      int ret, val, opcode;
++      bool copy = false, copy_ftr = false;
++
++      ret = qcom_spi_send_cmdaddr(snandc, op);
++      if (ret)
++              return ret;
++
++      snandc->buf_count = 0;
++      snandc->buf_start = 0;
++      qcom_clear_read_regs(snandc);
++      qcom_clear_bam_transaction(snandc);
++      opcode = op->cmd.opcode;
++
++      switch (opcode) {
++      case SPINAND_READID:
++              snandc->buf_count = 4;
++              qcom_read_reg_dma(snandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL);
++              copy = true;
++              break;
++      case SPINAND_GET_FEATURE:
++              snandc->buf_count = 4;
++              qcom_read_reg_dma(snandc, NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL);
++              copy_ftr = true;
++              break;
++      case SPINAND_SET_FEATURE:
++              snandc->regs->flash_feature = cpu_to_le32(*(u32 *)op->data.buf.out);
++              qcom_write_reg_dma(snandc, &snandc->regs->flash_feature,
++                                 NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL);
++              break;
++      case SPINAND_PROGRAM_EXECUTE:
++      case SPINAND_WRITE_EN:
++      case SPINAND_RESET:
++      case SPINAND_ERASE:
++      case SPINAND_READ:
++              return 0;
++      default:
++              return -EOPNOTSUPP;
++      }
++
++      ret = qcom_submit_descs(snandc);
++      if (ret)
++              dev_err(snandc->dev, "failure in submitting descriptor for:%d\n", opcode);
++
++      if (copy) {
++              qcom_nandc_dev_to_mem(snandc, true);
++              memcpy(op->data.buf.in, snandc->reg_read_buf, snandc->buf_count);
++      }
++
++      if (copy_ftr) {
++              qcom_nandc_dev_to_mem(snandc, true);
++              val = le32_to_cpu(*(__le32 *)snandc->reg_read_buf);
++              val >>= 8;
++              memcpy(op->data.buf.in, &val, snandc->buf_count);
++      }
++
++      return ret;
++}
++
++static bool qcom_spi_is_page_op(const struct spi_mem_op *op)
++{
++      if (op->addr.buswidth != 1 && op->addr.buswidth != 2 && op->addr.buswidth != 4)
++              return false;
++
++      if (op->data.dir == SPI_MEM_DATA_IN) {
++              if (op->addr.buswidth == 4 && op->data.buswidth == 4)
++                      return true;
++
++              if (op->addr.nbytes == 2 && op->addr.buswidth == 1)
++                      return true;
++
++      } else if (op->data.dir == SPI_MEM_DATA_OUT) {
++              if (op->data.buswidth == 4)
++                      return true;
++              if (op->addr.nbytes == 2 && op->addr.buswidth == 1)
++                      return true;
++      }
++
++      return false;
++}
++
++static bool qcom_spi_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
++{
++      if (!spi_mem_default_supports_op(mem, op))
++              return false;
++
++      if (op->cmd.nbytes != 1 || op->cmd.buswidth != 1)
++              return false;
++
++      if (qcom_spi_is_page_op(op))
++              return true;
++
++      return ((!op->addr.nbytes || op->addr.buswidth == 1) &&
++              (!op->dummy.nbytes || op->dummy.buswidth == 1) &&
++              (!op->data.nbytes || op->data.buswidth == 1));
++}
++
++static int qcom_spi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
++{
++      struct qcom_nand_controller *snandc = spi_controller_get_devdata(mem->spi->controller);
++
++      dev_dbg(snandc->dev, "OP %02x ADDR %08llX@%d:%u DATA %d:%u", op->cmd.opcode,
++              op->addr.val, op->addr.buswidth, op->addr.nbytes,
++              op->data.buswidth, op->data.nbytes);
++
++      if (qcom_spi_is_page_op(op)) {
++              if (op->data.dir == SPI_MEM_DATA_IN)
++                      return qcom_spi_read_page(snandc, op);
++              if (op->data.dir == SPI_MEM_DATA_OUT)
++                      return qcom_spi_write_page(snandc, op);
++      } else {
++              return qcom_spi_io_op(snandc, op);
++      }
++
++      return 0;
++}
++
++static const struct spi_controller_mem_ops qcom_spi_mem_ops = {
++      .supports_op = qcom_spi_supports_op,
++      .exec_op = qcom_spi_exec_op,
++};
++
++static const struct spi_controller_mem_caps qcom_spi_mem_caps = {
++      .ecc = true,
++};
++
++static int qcom_spi_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct spi_controller *ctlr;
++      struct qcom_nand_controller *snandc;
++      struct qpic_spi_nand *qspi;
++      struct qpic_ecc *ecc;
++      struct resource *res;
++      const void *dev_data;
++      int ret;
++
++      ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL);
++      if (!ecc)
++              return -ENOMEM;
++
++      qspi = devm_kzalloc(dev, sizeof(*qspi), GFP_KERNEL);
++      if (!qspi)
++              return -ENOMEM;
++
++      ctlr = __devm_spi_alloc_controller(dev, sizeof(*snandc), false);
++      if (!ctlr)
++              return -ENOMEM;
++
++      platform_set_drvdata(pdev, ctlr);
++
++      snandc = spi_controller_get_devdata(ctlr);
++      qspi->snandc = snandc;
++
++      snandc->dev = dev;
++      snandc->qspi = qspi;
++      snandc->qspi->ctlr = ctlr;
++      snandc->qspi->ecc = ecc;
++
++      dev_data = of_device_get_match_data(dev);
++      if (!dev_data) {
++              dev_err(&pdev->dev, "failed to get device data\n");
++              return -ENODEV;
++      }
++
++      snandc->props = dev_data;
++      snandc->dev = &pdev->dev;
++
++      snandc->core_clk = devm_clk_get(dev, "core");
++      if (IS_ERR(snandc->core_clk))
++              return PTR_ERR(snandc->core_clk);
++
++      snandc->aon_clk = devm_clk_get(dev, "aon");
++      if (IS_ERR(snandc->aon_clk))
++              return PTR_ERR(snandc->aon_clk);
++
++      snandc->qspi->iomacro_clk = devm_clk_get(dev, "iom");
++      if (IS_ERR(snandc->qspi->iomacro_clk))
++              return PTR_ERR(snandc->qspi->iomacro_clk);
++
++      snandc->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
++      if (IS_ERR(snandc->base))
++              return PTR_ERR(snandc->base);
++
++      snandc->base_phys = res->start;
++      snandc->base_dma = dma_map_resource(dev, res->start, resource_size(res),
++                                          DMA_BIDIRECTIONAL, 0);
++      if (dma_mapping_error(dev, snandc->base_dma))
++              return -ENXIO;
++
++      ret = clk_prepare_enable(snandc->core_clk);
++      if (ret)
++              goto err_dis_core_clk;
++
++      ret = clk_prepare_enable(snandc->aon_clk);
++      if (ret)
++              goto err_dis_aon_clk;
++
++      ret = clk_prepare_enable(snandc->qspi->iomacro_clk);
++      if (ret)
++              goto err_dis_iom_clk;
++
++      ret = qcom_nandc_alloc(snandc);
++      if (ret)
++              goto err_snand_alloc;
++
++      ret = qcom_spi_init(snandc);
++      if (ret)
++              goto err_spi_init;
++
++      /* setup ECC engine */
++      snandc->qspi->ecc_eng.dev = &pdev->dev;
++      snandc->qspi->ecc_eng.integration = NAND_ECC_ENGINE_INTEGRATION_PIPELINED;
++      snandc->qspi->ecc_eng.ops = &qcom_spi_ecc_engine_ops_pipelined;
++      snandc->qspi->ecc_eng.priv = snandc;
++
++      ret = nand_ecc_register_on_host_hw_engine(&snandc->qspi->ecc_eng);
++      if (ret) {
++              dev_err(&pdev->dev, "failed to register ecc engine:%d\n", ret);
++              goto err_spi_init;
++      }
++
++      ctlr->num_chipselect = QPIC_QSPI_NUM_CS;
++      ctlr->mem_ops = &qcom_spi_mem_ops;
++      ctlr->mem_caps = &qcom_spi_mem_caps;
++      ctlr->dev.of_node = pdev->dev.of_node;
++      ctlr->mode_bits = SPI_TX_DUAL | SPI_RX_DUAL |
++                          SPI_TX_QUAD | SPI_RX_QUAD;
++
++      ret = spi_register_controller(ctlr);
++      if (ret) {
++              dev_err(&pdev->dev, "spi_register_controller failed.\n");
++              goto err_spi_init;
++      }
++
++      return 0;
++
++err_spi_init:
++      qcom_nandc_unalloc(snandc);
++err_snand_alloc:
++      clk_disable_unprepare(snandc->qspi->iomacro_clk);
++err_dis_iom_clk:
++      clk_disable_unprepare(snandc->aon_clk);
++err_dis_aon_clk:
++      clk_disable_unprepare(snandc->core_clk);
++err_dis_core_clk:
++      dma_unmap_resource(dev, res->start, resource_size(res),
++                         DMA_BIDIRECTIONAL, 0);
++      return ret;
++}
++
++static void qcom_spi_remove(struct platform_device *pdev)
++{
++      struct spi_controller *ctlr = platform_get_drvdata(pdev);
++      struct qcom_nand_controller *snandc = spi_controller_get_devdata(ctlr);
++      struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++
++      spi_unregister_controller(ctlr);
++
++      qcom_nandc_unalloc(snandc);
++
++      clk_disable_unprepare(snandc->aon_clk);
++      clk_disable_unprepare(snandc->core_clk);
++      clk_disable_unprepare(snandc->qspi->iomacro_clk);
++
++      dma_unmap_resource(&pdev->dev, snandc->base_dma, resource_size(res),
++                         DMA_BIDIRECTIONAL, 0);
++}
++
++static const struct qcom_nandc_props ipq9574_snandc_props = {
++      .dev_cmd_reg_start = 0x7000,
++      .supports_bam = true,
++};
++
++static const struct of_device_id qcom_snandc_of_match[] = {
++      {
++              .compatible = "qcom,spi-qpic-snand",
++              .data = &ipq9574_snandc_props,
++      },
++      {}
++}
++MODULE_DEVICE_TABLE(of, qcom_snandc_of_match);
++
++static struct platform_driver qcom_spi_driver = {
++      .driver = {
++              .name           = "qcom_snand",
++              .of_match_table = qcom_snandc_of_match,
++      },
++      .probe = qcom_spi_probe,
++      .remove = qcom_spi_remove,
++};
++module_platform_driver(qcom_spi_driver);
++
++MODULE_DESCRIPTION("SPI driver for QPIC QSPI cores");
++MODULE_AUTHOR("Md Sadre Alam <quic_mdalam@quicinc.com>");
++MODULE_LICENSE("GPL");
++
+--- a/include/linux/mtd/nand-qpic-common.h
++++ b/include/linux/mtd/nand-qpic-common.h
+@@ -322,6 +322,10 @@ struct nandc_regs {
+       __le32 read_location_last1;
+       __le32 read_location_last2;
+       __le32 read_location_last3;
++      __le32 spi_cfg;
++      __le32 num_addr_cycle;
++      __le32 busy_wait_cnt;
++      __le32 flash_feature;
+       __le32 erased_cw_detect_cfg_clr;
+       __le32 erased_cw_detect_cfg_set;
+@@ -336,6 +340,7 @@ struct nandc_regs {
+  *
+  * @core_clk:                 controller clock
+  * @aon_clk:                  another controller clock
++ * @iomacro_clk:              io macro clock
+  *
+  * @regs:                     a contiguous chunk of memory for DMA register
+  *                            writes. contains the register values to be
+@@ -345,6 +350,7 @@ struct nandc_regs {
+  *                            initialized via DT match data
+  *
+  * @controller:                       base controller structure
++ * @qspi:                     qpic spi structure
+  * @host_list:                        list containing all the chips attached to the
+  *                            controller
+  *
+@@ -389,6 +395,7 @@ struct qcom_nand_controller {
+       const struct qcom_nandc_props *props;
+       struct nand_controller *controller;
++      struct qpic_spi_nand *qspi;
+       struct list_head host_list;
+       union {
diff --git a/target/linux/qualcommax/patches-6.6/0408-spi-spi-qpic-fix-compilation-issues.patch b/target/linux/qualcommax/patches-6.6/0408-spi-spi-qpic-fix-compilation-issues.patch
new file mode 100644 (file)
index 0000000..03b18e0
--- /dev/null
@@ -0,0 +1,45 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Subject: [PATCH] spi: spi-qpic: fix compilation issues
+Date: Sun, 06 Oct 2024 16:34:11 +0400
+
+The compiler will throw a warning when freeing a variable, setting values 
+of u32 to zero using memset, when the number of bytes is greater than the 
+size of the variable passed, so let's set each of the 8 variables 
+contiguously set in memory as part of the structure to zero.
+
+The output type of the remove function is void while it should return an 
+integer indicating success (0) or a negative number as an error. So let's
+switch to use the new .remove_new function which expects nothing to be 
+returned
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/drivers/mtd/nand/qpic_common.c
++++ b/drivers/mtd/nand/qpic_common.c
+@@ -82,7 +82,14 @@ void qcom_clear_bam_transaction(struct q
+       if (!nandc->props->supports_bam)
+               return;
+-      memset(&bam_txn->bam_ce_pos, 0, sizeof(u32) * 8);
++      bam_txn->bam_ce_pos = 0;
++      bam_txn->bam_ce_start = 0;
++      bam_txn->cmd_sgl_pos = 0;
++      bam_txn->cmd_sgl_start = 0;
++      bam_txn->tx_sgl_pos = 0;
++      bam_txn->tx_sgl_start = 0;
++      bam_txn->rx_sgl_pos = 0;
++      bam_txn->rx_sgl_start = 0;
+       bam_txn->last_data_desc = NULL;
+       sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage *
+--- a/drivers/spi/spi-qpic-snand.c
++++ b/drivers/spi/spi-qpic-snand.c
+@@ -1624,7 +1624,7 @@ static struct platform_driver qcom_spi_d
+               .of_match_table = qcom_snandc_of_match,
+       },
+       .probe = qcom_spi_probe,
+-      .remove = qcom_spi_remove,
++      .remove_new = qcom_spi_remove,
+ };
+ module_platform_driver(qcom_spi_driver);
diff --git a/target/linux/qualcommax/patches-6.6/0411-spi-spi-qpic-snand-support-BCH8.patch b/target/linux/qualcommax/patches-6.6/0411-spi-spi-qpic-snand-support-BCH8.patch
new file mode 100644 (file)
index 0000000..6442361
--- /dev/null
@@ -0,0 +1,53 @@
+From 396886e8644d5b601126b97e0b36c40c5fb5cecf Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:11 +0800
+Subject: [PATCH 1/2] spi: spi-qpic-snand: support BCH8
+
+Add BCH8 error-correcting code support for QPIC SPI Nand controller.
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+ drivers/spi/spi-qpic-snand.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+--- a/drivers/spi/spi-qpic-snand.c
++++ b/drivers/spi/spi-qpic-snand.c
+@@ -252,6 +252,7 @@ static int qcom_spi_ecc_init_ctx_pipelin
+       struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
+       struct mtd_info *mtd = nanddev_to_mtd(nand);
+       int cwperpage, bad_block_byte;
++      int ecc_mode;
+       struct qpic_ecc *ecc_cfg;
+       cwperpage = mtd->writesize / NANDC_STEP_SIZE;
+@@ -270,14 +271,17 @@ static int qcom_spi_ecc_init_ctx_pipelin
+       nand->ecc.ctx.priv = ecc_cfg;
+       snandc->qspi->mtd = mtd;
+-      ecc_cfg->ecc_bytes_hw = 7;
+-      ecc_cfg->spare_bytes = 4;
++      /* BCH8 or BCH4 */
++      ecc_mode = mtd->oobsize > 64 ? 1 : 0;
++
++      ecc_cfg->ecc_bytes_hw = ecc_mode ? 13 : 7;
++      ecc_cfg->spare_bytes = ecc_mode ? 2 : 4;
+       ecc_cfg->bbm_size = 1;
+       ecc_cfg->bch_enabled = true;
+       ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size;
+       ecc_cfg->steps = 4;
+-      ecc_cfg->strength = 4;
++      ecc_cfg->strength = ecc_mode ? 8 : 4;
+       ecc_cfg->step_size = 512;
+       ecc_cfg->cw_data = 516;
+       ecc_cfg->cw_size = ecc_cfg->cw_data + ecc_cfg->bytes;
+@@ -319,7 +323,7 @@ static int qcom_spi_ecc_init_ctx_pipelin
+                              FIELD_PREP(ECC_SW_RESET, 0) |
+                              FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) |
+                              FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) |
+-                             FIELD_PREP(ECC_MODE_MASK, 0) |
++                             FIELD_PREP(ECC_MODE_MASK, ecc_mode) |
+                              FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw);
+       ecc_cfg->ecc_buf_cfg = 0x203 << NUM_STEPS;
diff --git a/target/linux/qualcommax/patches-6.6/0412-mtd-spinand-qpic-only-support-max-4-bytes-ID.patch b/target/linux/qualcommax/patches-6.6/0412-mtd-spinand-qpic-only-support-max-4-bytes-ID.patch
new file mode 100644 (file)
index 0000000..3f50be2
--- /dev/null
@@ -0,0 +1,25 @@
+From 3d550dc3eb4eaa2fe1d0668ed67e835c91487d61 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:11 +0800
+Subject: [PATCH 2/2] mtd: spinand: qpic only support max 4 bytes ID
+
+The QPIC SPI NAND controller supports a max of 4 bytes of device ID.
+As such, set a maximum of 4 bytes.
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+ drivers/mtd/nand/spi/core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -1087,7 +1087,7 @@ int spinand_match_and_init(struct spinan
+               if (rdid_method != info->devid.method)
+                       continue;
+-              if (memcmp(id + 1, info->devid.id, info->devid.len))
++              if (memcmp(id + 1, info->devid.id, min(3, info->devid.len)))
+                       continue;
+               nand->memorg = table[i].memorg;
diff --git a/target/linux/qualcommax/patches-6.6/0421-arm64-dts-qcom-ipq5018-Add-SPI-nand-node.patch b/target/linux/qualcommax/patches-6.6/0421-arm64-dts-qcom-ipq5018-Add-SPI-nand-node.patch
new file mode 100644 (file)
index 0000000..2de1556
--- /dev/null
@@ -0,0 +1,52 @@
+From c2019f64539dd24e6e0da3cea2219d6f9e6b03e4 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:11 +0800
+Subject: [PATCH] arm64: dts: qcom: ipq5018: Add SPI nand node
+
+Add SPI NAND support for IPQ5018 SoC.
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+ arch/arm64/boot/dts/qcom/ipq5018.dtsi | 40 +++++++++++++++++++++++++++
+ 1 file changed, 40 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -461,6 +461,36 @@
+                       status = "disabled";
+               };
++              qpic_bam: dma@7984000 {
++                      compatible = "qcom,bam-v1.7.0";
++                      reg = <0x07984000 0x1c000>;
++                      interrupts = <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>;
++                      clocks = <&gcc GCC_QPIC_AHB_CLK>;
++                      clock-names = "bam_clk";
++                      #dma-cells = <1>;
++                      qcom,ee = <0>;
++                      status = "disabled";
++              };
++
++              qpic_nand: qpic-nand@79b0000 {
++                      compatible = "qcom,spi-qpic-snand";
++                      reg = <0x079b0000 0x10000>;
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      clocks = <&gcc GCC_QPIC_CLK>,
++                               <&gcc GCC_QPIC_AHB_CLK>,
++                               <&gcc GCC_QPIC_IO_MACRO_CLK>;
++                      clock-names = "core", "aon", "iom";
++
++                      dmas = <&qpic_bam 0>,
++                             <&qpic_bam 1>,
++                             <&qpic_bam 2>,
++                             <&qpic_bam 3>;
++                      dma-names = "tx", "rx", "cmd", "status";
++
++                      status = "disabled";
++              };
++
+               usb: usb@8af8800 {
+                       compatible = "qcom,ipq5018-dwc3", "qcom,dwc3";
+                       reg = <0x08af8800 0x400>;
diff --git a/target/linux/qualcommax/patches-6.6/0431-arm64-dts-qcom-ipq5018-Add-more-nand-compatible-for-.patch b/target/linux/qualcommax/patches-6.6/0431-arm64-dts-qcom-ipq5018-Add-more-nand-compatible-for-.patch
new file mode 100644 (file)
index 0000000..fdbea3c
--- /dev/null
@@ -0,0 +1,23 @@
+From b76a7649402d3eb1245ab463832133fc7efda194 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:11 +0800
+Subject: [PATCH] arm64: dts: qcom: ipq5018: Add more nand compatible for
+ uboot to fix partitions
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+ arch/arm64/boot/dts/qcom/ipq5018.dtsi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -473,7 +473,7 @@
+               };
+               qpic_nand: qpic-nand@79b0000 {
+-                      compatible = "qcom,spi-qpic-snand";
++                      compatible = "qcom,spi-qpic-snand", "qcom,ebi2-nandc-bam-v2.1.1";
+                       reg = <0x079b0000 0x10000>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
diff --git a/target/linux/qualcommax/patches-6.6/0701-dt-bindings-clock-qcom-Add-CMN-PLL-clock-controller-.patch b/target/linux/qualcommax/patches-6.6/0701-dt-bindings-clock-qcom-Add-CMN-PLL-clock-controller-.patch
new file mode 100644 (file)
index 0000000..ad7e08c
--- /dev/null
@@ -0,0 +1,113 @@
+From 7b89dbf5c7dcd8a9c131721e93c1292e5993968b Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Tue, 20 Aug 2024 22:02:42 +0800
+Subject: [PATCH] dt-bindings: clock: qcom: Add CMN PLL clock controller
+ for IPQ SoC
+
+The CMN PLL controller provides clocks to networking hardware blocks
+on Qualcomm IPQ9574 SoC. It receives input clock from the on-chip Wi-Fi,
+and produces output clocks at fixed rates. These output rates are
+predetermined, and are unrelated to the input clock rate. The output
+clocks are supplied to the Ethernet hardware such as PPE (packet
+process engine) and the externally connected switch or PHY device.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+---
+ .../bindings/clock/qcom,ipq9574-cmn-pll.yaml  | 70 +++++++++++++++++++
+ include/dt-bindings/clock/qcom,ipq-cmn-pll.h  | 15 ++++
+ 2 files changed, 85 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml
+ create mode 100644 include/dt-bindings/clock/qcom,ipq-cmn-pll.h
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml
+@@ -0,0 +1,70 @@
++# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/clock/qcom,ipq9574-cmn-pll.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Qualcomm CMN PLL Clock Controller on IPQ SoC
++
++maintainers:
++  - Bjorn Andersson <andersson@kernel.org>
++  - Luo Jie <quic_luoj@quicinc.com>
++
++description:
++  The CMN PLL clock controller expects a reference input clock.
++  This reference clock is from the on-board Wi-Fi. The CMN PLL
++  supplies a number of fixed rate output clocks to the Ethernet
++  devices including PPE (packet process engine) and the connected
++  switch or PHY device.
++
++properties:
++  compatible:
++    enum:
++      - qcom,ipq9574-cmn-pll
++
++  reg:
++    maxItems: 1
++
++  clocks:
++    items:
++      - description: The reference clock. The supported clock rates include
++          25000000, 31250000, 40000000, 48000000, 50000000 and 96000000 HZ.
++      - description: The AHB clock
++      - description: The SYS clock
++    description:
++      The reference clock is the source clock of CMN PLL, which is from the
++      Wi-Fi. The AHB and SYS clocks must be enabled to access CMN PLL
++      clock registers.
++
++  clock-names:
++    items:
++      - const: ref
++      - const: ahb
++      - const: sys
++
++  "#clock-cells":
++    const: 1
++
++required:
++  - compatible
++  - reg
++  - clocks
++  - clock-names
++  - "#clock-cells"
++
++additionalProperties: false
++
++examples:
++  - |
++    #include <dt-bindings/clock/qcom,ipq9574-gcc.h>
++
++    clock-controller@9b000 {
++        compatible = "qcom,ipq9574-cmn-pll";
++        reg = <0x0009b000 0x800>;
++        clocks = <&cmn_pll_ref_clk>,
++                 <&gcc GCC_CMN_12GPLL_AHB_CLK>,
++                 <&gcc GCC_CMN_12GPLL_SYS_CLK>;
++        clock-names = "ref", "ahb", "sys";
++        #clock-cells = <1>;
++    };
++...
+--- /dev/null
++++ b/include/dt-bindings/clock/qcom,ipq-cmn-pll.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
++/*
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef _DT_BINDINGS_CLK_QCOM_IPQ_CMN_PLL_H
++#define _DT_BINDINGS_CLK_QCOM_IPQ_CMN_PLL_H
++
++/* The output clocks from CMN PLL of IPQ9574. */
++#define PPE_353MHZ_CLK                        0
++#define ETH0_50MHZ_CLK                        1
++#define ETH1_50MHZ_CLK                        2
++#define ETH2_50MHZ_CLK                        3
++#define ETH_25MHZ_CLK                 4
++#endif
diff --git a/target/linux/qualcommax/patches-6.6/0702-clk-qcom-Add-CMN-PLL-clock-controller-driver-for-IPQ.patch b/target/linux/qualcommax/patches-6.6/0702-clk-qcom-Add-CMN-PLL-clock-controller-driver-for-IPQ.patch
new file mode 100644 (file)
index 0000000..afccffa
--- /dev/null
@@ -0,0 +1,288 @@
+From a7e8397e2db6133e3435054a3f312dbd9cab05ed Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Tue, 20 Aug 2024 22:02:43 +0800
+Subject: [PATCH] clk: qcom: Add CMN PLL clock controller driver for IPQ
+ SoC
+
+The CMN PLL clock controller supplies clocks to the hardware
+blocks that together make up the Ethernet function on Qualcomm
+IPQ SoCs. The driver is initially supported for IPQ9574 SoC.
+
+The CMN PLL clock controller expects a reference input clock
+from the on-board Wi-Fi block acting as clock source. The input
+reference clock needs to be configured to one of the supported
+clock rates.
+
+The controller supplies a number of fixed-rate output clocks.
+For the IPQ9574, there is one output clock of 353 MHZ to PPE
+(Packet Process Engine) hardware block, three 50 MHZ output
+clocks and an additional 25 MHZ output clock supplied to the
+connected Ethernet devices.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ drivers/clk/qcom/Kconfig           |  10 ++
+ drivers/clk/qcom/Makefile          |   1 +
+ drivers/clk/qcom/clk-ipq-cmn-pll.c | 227 +++++++++++++++++++++++++++++
+ 3 files changed, 238 insertions(+)
+ create mode 100644 drivers/clk/qcom/clk-ipq-cmn-pll.c
+
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -139,6 +139,16 @@ config IPQ_APSS_6018
+         Say Y if you want to support CPU frequency scaling on
+         ipq based devices.
++config IPQ_CMN_PLL
++      tristate "IPQ CMN PLL Clock Controller"
++      depends on IPQ_GCC_9574
++      help
++        Support for CMN PLL clock controller on IPQ platform. The
++        CMN PLL feeds the reference clocks to the Ethernet devices
++        based on IPQ SoC.
++        Say Y or M if you want to support CMN PLL clock on the IPQ
++        based devices.
++
+ config IPQ_GCC_4019
+       tristate "IPQ4019 Global Clock Controller"
+       help
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -23,6 +23,7 @@ obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8
+ obj-$(CONFIG_CLK_GFM_LPASS_SM8250) += lpass-gfm-sm8250.o
+ obj-$(CONFIG_IPQ_APSS_PLL) += apss-ipq-pll.o
+ obj-$(CONFIG_IPQ_APSS_6018) += apss-ipq6018.o
++obj-$(CONFIG_IPQ_CMN_PLL) += clk-ipq-cmn-pll.o
+ obj-$(CONFIG_IPQ_GCC_4019) += gcc-ipq4019.o
+ obj-$(CONFIG_IPQ_GCC_5018) += gcc-ipq5018.o
+ obj-$(CONFIG_IPQ_GCC_5332) += gcc-ipq5332.o
+--- /dev/null
++++ b/drivers/clk/qcom/clk-ipq-cmn-pll.c
+@@ -0,0 +1,227 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/*
++ * CMN PLL block expects the reference clock from on-board Wi-Fi block, and
++ * supplies fixed rate clocks as output to the Ethernet hardware blocks.
++ * The Ethernet related blocks include PPE (packet process engine) and the
++ * external connected PHY (or switch) chip receiving clocks from the CMN PLL.
++ *
++ * On the IPQ9574 SoC, There are three clocks with 50 MHZ, one clock with
++ * 25 MHZ which are output from the CMN PLL to Ethernet PHY (or switch),
++ * and one clock with 353 MHZ to PPE.
++ *
++ *               +---------+
++ *               |   GCC   |
++ *               +--+---+--+
++ *           AHB CLK|   |SYS CLK
++ *                  V   V
++ *          +-------+---+------+
++ *          |                  +-------------> eth0-50mhz
++ * REF CLK  |     IPQ9574      |
++ * -------->+                  +-------------> eth1-50mhz
++ *          |  CMN PLL block   |
++ *          |                  +-------------> eth2-50mhz
++ *          |                  |
++ *          +---------+--------+-------------> eth-25mhz
++ *                    |
++ *                    V
++ *                    ppe-353mhz
++ */
++
++#include <dt-bindings/clock/qcom,ipq-cmn-pll.h>
++#include <linux/bitfield.h>
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/delay.h>
++#include <linux/io.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++
++#define CMN_PLL_REFCLK_SRC_SELECTION          0x28
++#define CMN_PLL_REFCLK_SRC_DIV                        GENMASK(9, 8)
++
++#define CMN_PLL_REFCLK_CONFIG                 0x784
++#define CMN_PLL_REFCLK_EXTERNAL                       BIT(9)
++#define CMN_PLL_REFCLK_DIV                    GENMASK(8, 4)
++#define CMN_PLL_REFCLK_INDEX                  GENMASK(3, 0)
++
++#define CMN_PLL_POWER_ON_AND_RESET            0x780
++#define CMN_ANA_EN_SW_RSTN                    BIT(6)
++
++/**
++ * struct cmn_pll_fixed_output_clk - CMN PLL output clocks information
++ * @id:       Clock specifier to be supplied
++ * @name: Clock name to be registered
++ * @rate: Clock rate
++ */
++struct cmn_pll_fixed_output_clk {
++      unsigned int            id;
++      const char              *name;
++      const unsigned long     rate;
++};
++
++#define CLK_PLL_OUTPUT(_id, _name, _rate) {           \
++      .id = _id,                                      \
++      .name = _name,                                  \
++      .rate = _rate,                                  \
++}
++
++static const struct cmn_pll_fixed_output_clk ipq9574_output_clks[] = {
++      CLK_PLL_OUTPUT(PPE_353MHZ_CLK, "ppe-353mhz", 353000000UL),
++      CLK_PLL_OUTPUT(ETH0_50MHZ_CLK, "eth0-50mhz", 50000000UL),
++      CLK_PLL_OUTPUT(ETH1_50MHZ_CLK, "eth1-50mhz", 50000000UL),
++      CLK_PLL_OUTPUT(ETH2_50MHZ_CLK, "eth2-50mhz", 50000000UL),
++      CLK_PLL_OUTPUT(ETH_25MHZ_CLK, "eth-25mhz", 25000000UL),
++};
++
++static int ipq_cmn_pll_config(struct device *dev, unsigned long parent_rate)
++{
++      void __iomem *base;
++      u32 val;
++
++      base = devm_of_iomap(dev, dev->of_node, 0, NULL);
++      if (IS_ERR(base))
++              return PTR_ERR(base);
++
++      val = readl(base + CMN_PLL_REFCLK_CONFIG);
++      val &= ~(CMN_PLL_REFCLK_EXTERNAL | CMN_PLL_REFCLK_INDEX);
++
++      /*
++       * Configure the reference input clock selection as per the given rate.
++       * The output clock rates are always of fixed value.
++       */
++      switch (parent_rate) {
++      case 25000000:
++              val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 3);
++              break;
++      case 31250000:
++              val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 4);
++              break;
++      case 40000000:
++              val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 6);
++              break;
++      case 48000000:
++              val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 7);
++              break;
++      case 50000000:
++              val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 8);
++              break;
++      case 96000000:
++              val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 7);
++              val &= ~CMN_PLL_REFCLK_DIV;
++              val |= FIELD_PREP(CMN_PLL_REFCLK_DIV, 2);
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      writel(val, base + CMN_PLL_REFCLK_CONFIG);
++
++      /* Update the source clock rate selection. Only 96 MHZ uses 0. */
++      val = readl(base + CMN_PLL_REFCLK_SRC_SELECTION);
++      val &= ~CMN_PLL_REFCLK_SRC_DIV;
++      if (parent_rate != 96000000)
++              val |= FIELD_PREP(CMN_PLL_REFCLK_SRC_DIV, 1);
++
++      writel(val, base + CMN_PLL_REFCLK_SRC_SELECTION);
++
++      /*
++       * Reset the CMN PLL block by asserting/de-asserting for 100 ms
++       * each, to ensure the updated configurations take effect.
++       */
++      val = readl(base + CMN_PLL_POWER_ON_AND_RESET);
++      val &= ~CMN_ANA_EN_SW_RSTN;
++      writel(val, base);
++      msleep(100);
++
++      val |= CMN_ANA_EN_SW_RSTN;
++      writel(val, base + CMN_PLL_POWER_ON_AND_RESET);
++      msleep(100);
++
++      return 0;
++}
++
++static int ipq_cmn_pll_clk_register(struct device *dev, const char *parent)
++{
++      const struct cmn_pll_fixed_output_clk *fixed_clk;
++      struct clk_hw_onecell_data *data;
++      unsigned int num_clks;
++      struct clk_hw *hw;
++      int i;
++
++      num_clks = ARRAY_SIZE(ipq9574_output_clks);
++      fixed_clk = ipq9574_output_clks;
++
++      data = devm_kzalloc(dev, struct_size(data, hws, num_clks), GFP_KERNEL);
++      if (!data)
++              return -ENOMEM;
++
++      for (i = 0; i < num_clks; i++) {
++              hw = devm_clk_hw_register_fixed_rate(dev, fixed_clk[i].name,
++                                                   parent, 0,
++                                                   fixed_clk[i].rate);
++              if (IS_ERR(hw))
++                      return PTR_ERR(hw);
++
++              data->hws[fixed_clk[i].id] = hw;
++      }
++      data->num = num_clks;
++
++      return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data);
++}
++
++static int ipq_cmn_pll_clk_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct clk *clk;
++      int ret;
++
++      /*
++       * To access the CMN PLL registers, the GCC AHB & SYSY clocks
++       * for CMN PLL block need to be enabled.
++       */
++      clk = devm_clk_get_enabled(dev, "ahb");
++      if (IS_ERR(clk))
++              return dev_err_probe(dev, PTR_ERR(clk),
++                                   "Enable AHB clock failed\n");
++
++      clk = devm_clk_get_enabled(dev, "sys");
++      if (IS_ERR(clk))
++              return dev_err_probe(dev, PTR_ERR(clk),
++                                   "Enable SYS clock failed\n");
++
++      clk = devm_clk_get(dev, "ref");
++      if (IS_ERR(clk))
++              return dev_err_probe(dev, PTR_ERR(clk),
++                                   "Get reference clock failed\n");
++
++      /* Configure CMN PLL to apply the reference clock. */
++      ret = ipq_cmn_pll_config(dev, clk_get_rate(clk));
++      if (ret)
++              return dev_err_probe(dev, ret, "Configure CMN PLL failed\n");
++
++      return ipq_cmn_pll_clk_register(dev, __clk_get_name(clk));
++}
++
++static const struct of_device_id ipq_cmn_pll_clk_ids[] = {
++      { .compatible = "qcom,ipq9574-cmn-pll", },
++      { }
++};
++
++static struct platform_driver ipq_cmn_pll_clk_driver = {
++      .probe = ipq_cmn_pll_clk_probe,
++      .driver = {
++              .name = "ipq_cmn_pll",
++              .of_match_table = ipq_cmn_pll_clk_ids,
++      },
++};
++
++module_platform_driver(ipq_cmn_pll_clk_driver);
++
++MODULE_DESCRIPTION("Qualcomm Technologies, Inc. IPQ CMN PLL Driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/qualcommax/patches-6.6/0703-clk-qcom-cmn-pll-add-IPQ5018-support.patch b/target/linux/qualcommax/patches-6.6/0703-clk-qcom-cmn-pll-add-IPQ5018-support.patch
new file mode 100644 (file)
index 0000000..9d0d52b
--- /dev/null
@@ -0,0 +1,78 @@
+From a28797563b8c97c9abced82e0cf89302fcd2bf37 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:11 +0800
+Subject: [PATCH 1/2] clk: qcom: cmn-pll: add IPQ5018 support
+
+Add support for IPQ5018 (and removing dependency on the IPQ9574 platform).
+The common network block in IPQ5018 must be enabled first through a 
+specific register at a fixed offset in the TCSR area, set in the DTS.
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+ drivers/clk/qcom/Kconfig           |  1 -
+ drivers/clk/qcom/clk-ipq-cmn-pll.c | 29 +++++++++++++++++++++++++++++
+ 2 files changed, 29 insertions(+), 1 deletion(-)
+
+--- a/drivers/clk/qcom/Kconfig
++++ b/drivers/clk/qcom/Kconfig
+@@ -141,7 +141,6 @@ config IPQ_APSS_6018
+ config IPQ_CMN_PLL
+       tristate "IPQ CMN PLL Clock Controller"
+-      depends on IPQ_GCC_9574
+       help
+         Support for CMN PLL clock controller on IPQ platform. The
+         CMN PLL feeds the reference clocks to the Ethernet devices
+--- a/drivers/clk/qcom/clk-ipq-cmn-pll.c
++++ b/drivers/clk/qcom/clk-ipq-cmn-pll.c
+@@ -42,6 +42,9 @@
+ #include <linux/platform_device.h>
+ #include <linux/slab.h>
++#define TCSR_ETH_CMN                          0x0
++#define  TCSR_ETH_CMN_ENABLE                  BIT(0)
++
+ #define CMN_PLL_REFCLK_SRC_SELECTION          0x28
+ #define CMN_PLL_REFCLK_SRC_DIV                        GENMASK(9, 8)
+@@ -79,6 +82,28 @@ static const struct cmn_pll_fixed_output
+       CLK_PLL_OUTPUT(ETH_25MHZ_CLK, "eth-25mhz", 25000000UL),
+ };
++static int ipq_cmn_pll_tcsr_enable(struct platform_device *pdev)
++{
++      struct resource *res;
++      void __iomem *tcsr_base;
++      u32 val;
++
++      /* For IPQ50xx, tcsr is necessary to enable cmn block */
++      res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tcsr");
++      if (!res)
++              return 0;
++
++      tcsr_base = devm_ioremap_resource(&pdev->dev, res);
++      if (IS_ERR_OR_NULL(tcsr_base))
++              return PTR_ERR(tcsr_base);
++
++      val = readl(tcsr_base + TCSR_ETH_CMN);
++      val |= TCSR_ETH_CMN_ENABLE;
++      writel(val, (tcsr_base + TCSR_ETH_CMN));
++
++      return 0;
++}
++
+ static int ipq_cmn_pll_config(struct device *dev, unsigned long parent_rate)
+ {
+       void __iomem *base;
+@@ -181,6 +206,10 @@ static int ipq_cmn_pll_clk_probe(struct
+       struct clk *clk;
+       int ret;
++      ret = ipq_cmn_pll_tcsr_enable(pdev);
++      if (ret)
++              return dev_err_probe(dev, ret, "Enable CMN PLL failed\n");
++
+       /*
+        * To access the CMN PLL registers, the GCC AHB & SYSY clocks
+        * for CMN PLL block need to be enabled.
diff --git a/target/linux/qualcommax/patches-6.6/0704-arm64-dts-qcom-ipq5018-Add-ethernet-cmn-node.patch b/target/linux/qualcommax/patches-6.6/0704-arm64-dts-qcom-ipq5018-Add-ethernet-cmn-node.patch
new file mode 100644 (file)
index 0000000..8127656
--- /dev/null
@@ -0,0 +1,45 @@
+From 1b625a37b96b0448aac126d7720eec38de8e5956 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:11 +0800
+Subject: [PATCH 2/2] arm64: dts: qcom: ipq5018: Add ethernet cmn node
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+---
+ arch/arm64/boot/dts/qcom/ipq5018.dtsi | 19 +++++++++++++++++++
+ 1 file changed, 19 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -16,6 +16,12 @@
+       #size-cells = <2>;
+       clocks {
++              cmn_pll_ref_clk: cmn-pll-ref-clk {
++                      compatible = "fixed-clock";
++                      clock-frequency = <96000000>;
++                      #clock-cells = <0>;
++              };
++
+               sleep_clk: sleep-clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+@@ -186,6 +192,19 @@
+                       status = "disabled";
+               };
++              cmn_pll: clock-controller@9b000 {
++                      compatible = "qcom,ipq9574-cmn-pll";
++                      reg = <0x0009b000 0x800>,
++                            <0x19475c4 0x4>;
++                      reg-names = "cmn",
++                                  "tcsr";
++                      clocks = <&cmn_pll_ref_clk>,
++                               <&gcc GCC_CMN_BLK_AHB_CLK>,
++                               <&gcc GCC_CMN_BLK_SYS_CLK>;
++                      clock-names = "ref", "ahb", "sys";
++                      #clock-cells = <1>;
++              };
++
+               qfprom: qfprom@a0000 {
+                       compatible = "qcom,ipq5018-qfprom", "qcom,qfprom";
+                       reg = <0xa0000 0x1000>;
diff --git a/target/linux/qualcommax/patches-6.6/0711-net-phy-qcom-Introduce-IPQ5018-internal-PHY-driver.patch b/target/linux/qualcommax/patches-6.6/0711-net-phy-qcom-Introduce-IPQ5018-internal-PHY-driver.patch
new file mode 100644 (file)
index 0000000..7764df4
--- /dev/null
@@ -0,0 +1,184 @@
+From 77ad12b3a5e21cae859247c0b82cf9a5b661e531 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:11 +0800
+Subject: [PATCH 1/3] net: phy: qcom: Introduce IPQ5018 internal PHY driver
+
+Introduce the internal GE PHY driver, part of the Qualcomm IPQ50xx SoC.
+The driver registers two clock providers needed and referenced by the GCC 
+using DT properties and phandles.
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+ drivers/net/phy/qcom/Kconfig   |   6 ++
+ drivers/net/phy/qcom/Makefile  |   1 +
+ drivers/net/phy/qcom/ipq5018.c | 138 +++++++++++++++++++++++++++++++++
+ 3 files changed, 145 insertions(+)
+ create mode 100644 drivers/net/phy/qcom/ipq5018.c
+
+--- a/drivers/net/phy/qcom/Kconfig
++++ b/drivers/net/phy/qcom/Kconfig
+@@ -9,6 +9,12 @@ config AT803X_PHY
+       help
+         Currently supports the AR8030, AR8031, AR8033, AR8035 model
++config IPQ5018_PHY
++      tristate "Qualcomm IPQ5018 internal PHYs"
++      select QCOM_NET_PHYLIB
++      help
++        Currently supports the Qualcomm IPQ5018 internal PHY
++
+ config QCA83XX_PHY
+       tristate "Qualcomm Atheros QCA833x PHYs"
+       select QCOM_NET_PHYLIB
+--- a/drivers/net/phy/qcom/Makefile
++++ b/drivers/net/phy/qcom/Makefile
+@@ -1,6 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0
+ obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o
+ obj-$(CONFIG_AT803X_PHY)      += at803x.o
++obj-$(CONFIG_IPQ5018_PHY)     += ipq5018.o
+ obj-$(CONFIG_QCA83XX_PHY)     += qca83xx.o
+ obj-$(CONFIG_QCA808X_PHY)     += qca808x.o
+ obj-$(CONFIG_QCA807X_PHY)     += qca807x.o
+--- /dev/null
++++ b/drivers/net/phy/qcom/ipq5018.c
+@@ -0,0 +1,138 @@
++#include <linux/bitfield.h>
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/phy.h>
++#include <linux/reset.h>
++
++#include "qcom.h"
++
++#define IPQ5018_PHY_ID                        0x004dd0c0
++
++#define TX_RX_CLK_RATE                        125000000 /* 125M */
++
++#define IPQ5018_PHY_FIFO_CONTROL      0x19
++#define  IPQ5018_PHY_FIFO_RESET               GENMASK(1, 0)
++
++struct ipq5018_phy {
++      int num_clks;
++      struct clk_bulk_data *clks;
++      struct reset_control *rst;
++
++      struct clk_hw *clk_rx, *clk_tx;
++      struct clk_hw_onecell_data *clk_data;
++};
++
++static int ipq5018_probe(struct phy_device *phydev)
++{
++      struct ipq5018_phy *priv;
++      struct device *dev = &phydev->mdio.dev;
++      char name[64];
++      int ret;
++
++      priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++      if (!priv)
++              return dev_err_probe(dev, -ENOMEM,
++                                   "failed to allocate priv\n");
++
++      priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks);
++      if (priv->num_clks < 0)
++              return dev_err_probe(dev, priv->num_clks,
++                                   "failed to acquire clocks\n");
++
++      ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks);
++      if (ret)
++              return dev_err_probe(dev, ret,
++                                   "failed to enable clocks\n");
++
++      priv->rst = devm_reset_control_array_get_exclusive(dev);
++      if (IS_ERR_OR_NULL(priv->rst))
++              return dev_err_probe(dev, PTR_ERR(priv->rst),
++                                   "failed to acquire reset\n");
++
++      ret = reset_control_reset(priv->rst);
++      if (ret)
++              return dev_err_probe(dev, ret,
++                                   "failed to reset\n");
++
++      snprintf(name, sizeof(name), "%s#rx", dev_name(dev));
++      priv->clk_rx = clk_hw_register_fixed_rate(dev, name, NULL, 0,
++                                                TX_RX_CLK_RATE);
++      if (IS_ERR_OR_NULL(priv->clk_rx))
++              return dev_err_probe(dev, PTR_ERR(priv->clk_rx),
++                                   "failed to register rx clock\n");
++
++      snprintf(name, sizeof(name), "%s#tx", dev_name(dev));
++      priv->clk_tx = clk_hw_register_fixed_rate(dev, name, NULL, 0,
++                                                TX_RX_CLK_RATE);
++      if (IS_ERR_OR_NULL(priv->clk_tx))
++              return dev_err_probe(dev, PTR_ERR(priv->clk_tx),
++                                   "failed to register tx clock\n");
++
++      priv->clk_data = devm_kzalloc(dev,
++                                    struct_size(priv->clk_data, hws, 2),
++                                    GFP_KERNEL);
++      if (!priv->clk_data)
++              return dev_err_probe(dev, -ENOMEM,
++                                   "failed to allocate clk_data\n");
++
++      priv->clk_data->num = 2;
++      priv->clk_data->hws[0] = priv->clk_rx;
++      priv->clk_data->hws[1] = priv->clk_tx;
++      ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
++                                   priv->clk_data);
++      if (ret)
++              return dev_err_probe(dev, ret,
++                                   "fail to register clock provider\n");
++
++      return 0;
++}
++
++static int ipq5018_soft_reset(struct phy_device *phydev)
++{
++      int ret;
++
++      ret = phy_modify(phydev, IPQ5018_PHY_FIFO_CONTROL,
++                       IPQ5018_PHY_FIFO_RESET, 0);
++      if (ret < 0)
++              return ret;
++
++      msleep(50);
++
++      ret = phy_modify(phydev, IPQ5018_PHY_FIFO_CONTROL,
++                       IPQ5018_PHY_FIFO_RESET, IPQ5018_PHY_FIFO_RESET);
++      if (ret < 0)
++              return ret;
++
++      return 0;
++}
++
++static int ipq5018_cable_test_start(struct phy_device *phydev)
++{
++      /* we do all the (time consuming) work later */
++      return 0;
++}
++
++static struct phy_driver ipq5018_internal_phy_driver[] = {
++      {
++              PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID),
++              .name           = "Qualcomm IPQ5018 internal PHY",
++              .flags          = PHY_IS_INTERNAL | PHY_POLL_CABLE_TEST,
++              .probe          = ipq5018_probe,
++              .soft_reset     = ipq5018_soft_reset,
++              .read_status    = at803x_read_status,
++              .config_intr    = at803x_config_intr,
++              .handle_interrupt = at803x_handle_interrupt,
++              .cable_test_start       = ipq5018_cable_test_start,
++              .cable_test_get_status  = qca808x_cable_test_get_status,
++      },
++};
++module_phy_driver(ipq5018_internal_phy_driver);
++
++static struct mdio_device_id __maybe_unused ipq5018_internal_phy_ids[] = {
++      { PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID) },
++      { }
++};
++MODULE_DEVICE_TABLE(mdio, ipq5018_internal_phy_ids);
++
++MODULE_DESCRIPTION("Qualcomm IPQ5018 internal PHY driver");
++MODULE_AUTHOR("Ziyang Huang <hzyitc@outlook.com>");
diff --git a/target/linux/qualcommax/patches-6.6/0712-arm64-dts-qcom-ipq5018-add-mdio-node.patch b/target/linux/qualcommax/patches-6.6/0712-arm64-dts-qcom-ipq5018-add-mdio-node.patch
new file mode 100644 (file)
index 0000000..ce5367a
--- /dev/null
@@ -0,0 +1,43 @@
+From d2cdc83fb2c7360856e598810b88211d815fc851 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:12 +0800
+Subject: [PATCH 2/3] arm64: dts: qcom: ipq5018: add mdio node
+
+The IPQ5018 SoC contains two MDIO controllers. MDIO0 is used to control 
+its internal GE Phy, while MDIO1 is wired to external PHYs/switch.
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+ arch/arm64/boot/dts/qcom/ipq5018.dtsi | 20 ++++++++++++++++++++
+ 1 file changed, 20 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -192,6 +192,26 @@
+                       status = "disabled";
+               };
++              mdio0: mdio@88000 {
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      compatible = "qcom,ipq5018-mdio", "qcom,qca-mdio";
++                      reg = <0x88000 0x64>;
++                      clocks = <&gcc GCC_MDIO0_AHB_CLK>;
++                      clock-names = "gcc_mdio_ahb_clk";
++                      status = "disabled";
++              };
++
++              mdio1: mdio@90000 {
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      compatible = "qcom,ipq5018-mdio";
++                      reg = <0x90000 0x64>;
++                      clocks = <&gcc GCC_MDIO1_AHB_CLK>;
++                      clock-names = "gcc_mdio_ahb_clk";
++                      status = "disabled";
++              };
++
+               cmn_pll: clock-controller@9b000 {
+                       compatible = "qcom,ipq9574-cmn-pll";
+                       reg = <0x0009b000 0x800>,
diff --git a/target/linux/qualcommax/patches-6.6/0713-arm64-dts-qcom-ipq5018-add-ge_phy-node.patch b/target/linux/qualcommax/patches-6.6/0713-arm64-dts-qcom-ipq5018-add-ge_phy-node.patch
new file mode 100644 (file)
index 0000000..9ac1098
--- /dev/null
@@ -0,0 +1,45 @@
+From 28490d95fe9e059c5ce74b2289d66e0d7ede2d50 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:12 +0800
+Subject: [PATCH 3/3] arm64: dts: qcom: ipq5018: add ge_phy node
+
+Add the GE PHY node and register the output clocks in the GCC node.
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+ arch/arm64/boot/dts/qcom/ipq5018.dtsi | 16 ++++++++++++++--
+ 1 file changed, 14 insertions(+), 2 deletions(-)
+
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -200,6 +200,18 @@
+                       clocks = <&gcc GCC_MDIO0_AHB_CLK>;
+                       clock-names = "gcc_mdio_ahb_clk";
+                       status = "disabled";
++
++                      ge_phy: ethernet-phy@7 {
++                              compatible = "ethernet-phy-id004d.d0c0";
++                              reg = <7>;
++                              resets = <&gcc GCC_GEPHY_BCR>,
++                                       <&gcc GCC_GEPHY_MDC_SW_ARES>,
++                                       <&gcc GCC_GEPHY_DSP_HW_ARES>,
++                                       <&gcc GCC_GEPHY_RX_ARES>,
++                                       <&gcc GCC_GEPHY_TX_ARES>;
++                              clocks = <&gcc GCC_GEPHY_RX_CLK>,
++                                       <&gcc GCC_GEPHY_TX_CLK>;
++                      };
+               };
+               mdio1: mdio@90000 {
+@@ -394,8 +406,8 @@
+                                <&pcie0_phy>,
+                                <&pcie1_phy>,
+                                <0>,
+-                               <0>,
+-                               <0>,
++                               <&ge_phy 0>,
++                               <&ge_phy 1>,
+                                <0>,
+                                <0>;
+                       #clock-cells = <1>;
diff --git a/target/linux/qualcommax/patches-6.6/0714-net-phy-qcom-IPQ5018-enable-configuration-of-DAC-settings.patch b/target/linux/qualcommax/patches-6.6/0714-net-phy-qcom-IPQ5018-enable-configuration-of-DAC-settings.patch
new file mode 100644 (file)
index 0000000..0c773ca
--- /dev/null
@@ -0,0 +1,111 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Date: Sun, 19 Jan 2025 11:25:27 +0400
+Subject: [PATCH] net: phy: qcom: ipq5018 enable configuration of DAC settings
+
+Allow setting amplitude and bias current as needed on the IPQ5018 Internal
+GE PHY. When the "qcom,dac" property is set in the DTS, the driver expects
+a pair of u32 values:
+
+(from QCA8337 datasheet)
+11: follow DSP setting
+10: bypass half amplitude and follow DSP half bias current
+01: half amplitude follow DSP and bypass half bias current
+00: full amplitude and full bias current
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/drivers/net/phy/qcom/ipq5018.c
++++ b/drivers/net/phy/qcom/ipq5018.c
+@@ -13,6 +13,10 @@
+ #define IPQ5018_PHY_FIFO_CONTROL      0x19
+ #define  IPQ5018_PHY_FIFO_RESET               GENMASK(1, 0)
++#define IPQ5018_PHY_DEBUG_EDAC                        0x4380
++#define IPQ5018_PHY_MMD1_MDAC                 0x8100
++#define  IPQ5018_PHY_DAC_MASK                 GENMASK(15,8)
++
+ struct ipq5018_phy {
+       int num_clks;
+       struct clk_bulk_data *clks;
+@@ -20,20 +24,35 @@ struct ipq5018_phy {
+       struct clk_hw *clk_rx, *clk_tx;
+       struct clk_hw_onecell_data *clk_data;
++
++      u32 mdac;
++      u32 edac;
+ };
+ static int ipq5018_probe(struct phy_device *phydev)
+ {
+-      struct ipq5018_phy *priv;
+       struct device *dev = &phydev->mdio.dev;
++      struct ipq5018_phy *priv;
++      u32 mdac, edac = 0;
+       char name[64];
+-      int ret;
++      int ret, cnt;
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return dev_err_probe(dev, -ENOMEM,
+                                    "failed to allocate priv\n");
++      cnt = of_property_count_u32_elems(dev->of_node, "qcom,dac");
++      if (cnt == 2) {
++              ret = of_property_read_u32_index(dev->of_node, "qcom,dac", 0, &mdac);
++              if (!ret)
++                      priv->mdac = mdac;
++
++              ret = of_property_read_u32_index(dev->of_node, "qcom,dac", 1, &edac);
++              if (!ret)
++                      priv->edac = edac;
++      }
++
+       priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks);
+       if (priv->num_clks < 0)
+               return dev_err_probe(dev, priv->num_clks,
+@@ -84,6 +103,8 @@ static int ipq5018_probe(struct phy_devi
+               return dev_err_probe(dev, ret,
+                                    "fail to register clock provider\n");
++      phydev->priv = priv;
++
+       return 0;
+ }
+@@ -112,12 +133,34 @@ static int ipq5018_cable_test_start(stru
+       return 0;
+ }
++static int ipq5018_config_init(struct phy_device *phydev)
++{
++      struct ipq5018_phy *priv = phydev->priv;
++      int ret;
++
++      /* setting mdac in MMD1 */
++      if (priv->mdac) {
++              ret = phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC,
++                                                      IPQ5018_PHY_DAC_MASK, priv->mdac);
++              if (ret)
++                      return ret;
++      }
++
++      /* setting edac in debug register */
++      if (priv->edac)
++              return at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_EDAC,
++                                                      IPQ5018_PHY_DAC_MASK, priv->edac);
++
++      return 0;
++}
++
+ static struct phy_driver ipq5018_internal_phy_driver[] = {
+       {
+               PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID),
+               .name           = "Qualcomm IPQ5018 internal PHY",
+               .flags          = PHY_IS_INTERNAL | PHY_POLL_CABLE_TEST,
+               .probe          = ipq5018_probe,
++              .config_init    = ipq5018_config_init,
+               .soft_reset     = ipq5018_soft_reset,
+               .read_status    = at803x_read_status,
+               .config_intr    = at803x_config_intr,
diff --git a/target/linux/qualcommax/patches-6.6/0715-net-phy-qcom-add-IPQ5018-initvals-and-CDT-feature.patch b/target/linux/qualcommax/patches-6.6/0715-net-phy-qcom-add-IPQ5018-initvals-and-CDT-feature.patch
new file mode 100644 (file)
index 0000000..ae4214b
--- /dev/null
@@ -0,0 +1,108 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Date: Fri, 24 Jan 2025 17:18:12 +0400
+Subject: [PATCH] net: phy: qcom: add IPQ5018 initvals and CDT feature
+
+The Cable Diagnostics Test for IPQ5018 follows the same logic as qca808x.
+However, the IPQ5018 GE PHY has its own threshold values. So let's set the
+CDT thresholds for the IPQ5018 internal GE PHY. While add it, add and set
+thesholds for MSE for signal quality measurement and 8023az for EEE.
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/drivers/net/phy/qcom/ipq5018.c
++++ b/drivers/net/phy/qcom/ipq5018.c
+@@ -17,6 +17,38 @@
+ #define IPQ5018_PHY_MMD1_MDAC                 0x8100
+ #define  IPQ5018_PHY_DAC_MASK                 GENMASK(15,8)
++#define IPQ5018_PHY_MMD1_MSE_THRESH1                  0x1000
++#define IPQ5018_PHY_MMD1_MSE_THRESH2                  0x1001
++#define IPQ5018_PHY_MMD3_AZ_CTRL1                             0x8008
++#define IPQ5018_PHY_MMD3_AZ_CTRL2                             0x8009
++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL3             0x8074
++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL4             0x8075
++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL5             0x8076
++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL6             0x8077
++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL7             0x8078
++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL9             0x807a
++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL13            0x807e
++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL14            0x807f
++
++#define IPQ5018_PHY_MMD1_MSE_THRESH1_VAL              0xf1
++#define IPQ5018_PHY_MMD1_MSE_THRESH2_VAL              0x1f6
++#define IPQ5018_PHY_MMD3_AZ_CTRL1_VAL                 0x7880
++#define IPQ5018_PHY_MMD3_AZ_CTRL2_VAL                 0xc8
++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL3_VAL 0xc040
++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL4_VAL 0xa060
++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL5_VAL 0xc040
++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL6_VAL 0xa060
++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL7_VAL 0xc24c
++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL9_VAL 0xc060
++#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL13_VAL        0xb060
++#define IPQ5018_PHY_MMD3_NEAR_ECHO_THRESH_VAL 0x90b0
++
++#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE                               0x1
++#define  IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK         GENMASK(7,4)
++#define  IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT      0x50
++
++#define IPQ5018_PHY_DEBUG_ANA_DAC_FILTER              0xa080
++
+ struct ipq5018_phy {
+       int num_clks;
+       struct clk_bulk_data *clks;
+@@ -129,6 +161,24 @@ static int ipq5018_soft_reset(struct phy
+ static int ipq5018_cable_test_start(struct phy_device *phydev)
+ {
++      phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL3,
++                                IPQ5018_PHY_MMD3_CDT_THRESH_CTRL3_VAL);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL4,
++                                IPQ5018_PHY_MMD3_CDT_THRESH_CTRL4_VAL);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL5,
++                                IPQ5018_PHY_MMD3_CDT_THRESH_CTRL5_VAL);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL6,
++                                IPQ5018_PHY_MMD3_CDT_THRESH_CTRL6_VAL);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL7,
++                                IPQ5018_PHY_MMD3_CDT_THRESH_CTRL7_VAL);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL9,
++                                IPQ5018_PHY_MMD3_CDT_THRESH_CTRL9_VAL);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, 
++                                IPQ5018_PHY_MMD3_CDT_THRESH_CTRL13,
++                                IPQ5018_PHY_MMD3_CDT_THRESH_CTRL13_VAL);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL3,
++                                IPQ5018_PHY_MMD3_NEAR_ECHO_THRESH_VAL);
++
+       /* we do all the (time consuming) work later */
+       return 0;
+ }
+@@ -136,8 +186,30 @@ static int ipq5018_cable_test_start(stru
+ static int ipq5018_config_init(struct phy_device *phydev)
+ {
+       struct ipq5018_phy *priv = phydev->priv;
++      u16 val = 0;
+       int ret;
++      /* set LDO efuse: first temporarily store ANA_DAC_FILTER value from 
++         debug register as it will be reset once the ANA_LDO_EFUSE register
++         is written to */
++      val = at803x_debug_reg_read(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER);
++      at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE,
++                                                IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK,
++                                                IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT);
++      at803x_debug_reg_write(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER, val);
++
++      /* set 8023AZ CTRL values */
++      phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_AZ_CTRL1,
++                                IPQ5018_PHY_MMD3_AZ_CTRL1_VAL);
++      phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_AZ_CTRL2,
++                                IPQ5018_PHY_MMD3_AZ_CTRL2_VAL);
++
++      /* set MSE threshold values */
++      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH1,
++                                IPQ5018_PHY_MMD1_MSE_THRESH1_VAL);
++      phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH2,
++                                IPQ5018_PHY_MMD1_MSE_THRESH2_VAL);
++
+       /* setting mdac in MMD1 */
+       if (priv->mdac) {
+               ret = phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC,
diff --git a/target/linux/qualcommax/patches-6.6/0721-clk-gcc-ipq5018-remove-the-unsupported-clk-combinati.patch b/target/linux/qualcommax/patches-6.6/0721-clk-gcc-ipq5018-remove-the-unsupported-clk-combinati.patch
new file mode 100644 (file)
index 0000000..0a22289
--- /dev/null
@@ -0,0 +1,35 @@
+From f71366e0530db2c5cecbbbb6edfbf7344bd6f83b Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:12 +0800
+Subject: [PATCH 1/2] clk: gcc-ipq5018: remove the unsupported clk
+ combination for gmac
+
+Comment out the unsupported clock combination in the frequency table 
+for GMAC1.
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+ drivers/clk/qcom/gcc-ipq5018.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/clk/qcom/gcc-ipq5018.c
++++ b/drivers/clk/qcom/gcc-ipq5018.c
+@@ -677,7 +677,7 @@ static const struct freq_tbl ftbl_gmac1_
+       F(2500000, P_UNIPHY_RX, 12.5, 0, 0),
+       F(24000000, P_XO, 1, 0, 0),
+       F(25000000, P_UNIPHY_RX, 2.5, 0, 0),
+-      F(125000000, P_UNIPHY_RX, 2.5, 0, 0),
++      /* F(125000000, P_UNIPHY_RX, 2.5, 0, 0), */
+       F(125000000, P_UNIPHY_RX, 1, 0, 0),
+       F(312500000, P_UNIPHY_RX, 1, 0, 0),
+       { }
+@@ -717,7 +717,7 @@ static const struct freq_tbl ftbl_gmac1_
+       F(2500000, P_UNIPHY_TX, 12.5, 0, 0),
+       F(24000000, P_XO, 1, 0, 0),
+       F(25000000, P_UNIPHY_TX, 2.5, 0, 0),
+-      F(125000000, P_UNIPHY_TX, 2.5, 0, 0),
++      /* F(125000000, P_UNIPHY_TX, 2.5, 0, 0), */
+       F(125000000, P_UNIPHY_TX, 1, 0, 0),
+       F(312500000, P_UNIPHY_TX, 1, 0, 0),
+       { }
diff --git a/target/linux/qualcommax/patches-6.6/0722-clk-gcc-ipq5018-refer-to-ge-phy-rx-and-tx-clk-providers-by-name.patch b/target/linux/qualcommax/patches-6.6/0722-clk-gcc-ipq5018-refer-to-ge-phy-rx-and-tx-clk-providers-by-name.patch
new file mode 100644 (file)
index 0000000..6cd1a2a
--- /dev/null
@@ -0,0 +1,62 @@
+From ce9e56a436e486690097cfbdda2d0c11b60db4c2 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:12 +0800
+Subject: [PATCH] clk: gcc-ipq5018: refer to GE PHY rx and tx clk providers by name
+
+QCA-SSDK does not register the output clocks of the onboard GE Phy and 
+uniphy so the GCC and DTS can't reference them by their index.
+The SSDK references them by name, so let's change the GCC driver 
+accordingly.
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+ drivers/clk/qcom/gcc-ipq5018.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+--- a/drivers/clk/qcom/gcc-ipq5018.c
++++ b/drivers/clk/qcom/gcc-ipq5018.c
+@@ -335,8 +335,8 @@ static const struct parent_map gcc_xo_gp
+ static const struct clk_parent_data gcc_xo_gephy_gcc_rx_gephy_gcc_tx_ubi32_pll_gpll0[] = {
+       { .index = DT_XO },
+-      { .index = DT_GEPHY_RX_CLK },
+-      { .index = DT_GEPHY_TX_CLK },
++      { .name =  "gephy_gcc_rx", .index = -1 },
++      { .name =  "gephy_gcc_tx", .index = -1 },
+       { .hw = &ubi32_pll.clkr.hw },
+       { .hw = &gpll0.clkr.hw },
+ };
+@@ -351,8 +351,8 @@ static const struct parent_map gcc_xo_ge
+ static const struct clk_parent_data gcc_xo_gephy_gcc_tx_gephy_gcc_rx_ubi32_pll_gpll0[] = {
+       { .index = DT_XO },
+-      { .index = DT_GEPHY_TX_CLK },
+-      { .index = DT_GEPHY_RX_CLK },
++      { .name =  "gephy_gcc_tx", .index = -1 },
++      { .name =  "gephy_gcc_rx", .index = -1 },
+       { .hw = &ubi32_pll.clkr.hw },
+       { .hw = &gpll0.clkr.hw },
+ };
+@@ -367,8 +367,8 @@ static const struct parent_map gcc_xo_ge
+ static const struct clk_parent_data gcc_xo_uniphy_gcc_rx_uniphy_gcc_tx_ubi32_pll_gpll0[] = {
+       { .index = DT_XO },
+-      { .index = DT_UNIPHY_RX_CLK },
+-      { .index = DT_UNIPHY_TX_CLK },
++      { .name =  "uniphy_gcc_rx", .index = -1 },
++      { .name =  "uniphy_gcc_tx", .index = -1 },
+       { .hw = &ubi32_pll.clkr.hw },
+       { .hw = &gpll0.clkr.hw },
+ };
+@@ -383,8 +383,8 @@ static const struct parent_map gcc_xo_un
+ static const struct clk_parent_data gcc_xo_uniphy_gcc_tx_uniphy_gcc_rx_ubi32_pll_gpll0[] = {
+       { .index = DT_XO },
+-      { .index = DT_UNIPHY_TX_CLK },
+-      { .index = DT_UNIPHY_RX_CLK },
++      { .name =  "uniphy_gcc_tx", .index = -1 },
++      { .name =  "uniphy_gcc_rx", .index = -1 },
+       { .hw = &ubi32_pll.clkr.hw },
+       { .hw = &gpll0.clkr.hw },
+ };
diff --git a/target/linux/qualcommax/patches-6.6/0751-net-dsa-qca8k-always-enable-SGMII-auto-negotiation.patch b/target/linux/qualcommax/patches-6.6/0751-net-dsa-qca8k-always-enable-SGMII-auto-negotiation.patch
new file mode 100644 (file)
index 0000000..bccac8a
--- /dev/null
@@ -0,0 +1,30 @@
+From d7a41a3ab6b8e3a3158997cda13f1fe28a37268c Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:12 +0800
+Subject: [PATCH] net: dsa: qca8k: always enable SGMII auto-negotiation
+
+fixed-link can't work well without this
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1545,11 +1545,10 @@ static int qca8k_pcs_config(struct phyli
+               return -EINVAL;
+       }
+-      /* Enable/disable SerDes auto-negotiation as necessary */
+-      val = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED ?
+-              0 : QCA8K_PWS_SERDES_AEN_DIS;
+-
+-      ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8K_PWS_SERDES_AEN_DIS, val);
++      /* Enable SerDes auto-negotiation always.
++       * So fixed-link can work.
++       */
++      ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8K_PWS_SERDES_AEN_DIS, 0);
+       if (ret)
+               return ret;
diff --git a/target/linux/qualcommax/patches-6.6/0752-net-dsa-qca8k-support-PHY-to-PHY-CPU-link.patch b/target/linux/qualcommax/patches-6.6/0752-net-dsa-qca8k-support-PHY-to-PHY-CPU-link.patch
new file mode 100644 (file)
index 0000000..e664a71
--- /dev/null
@@ -0,0 +1,49 @@
+From 8a56ac86c2eed13024413aa23a6cda85613d60f9 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sat, 18 Jan 2025 16:18:40 +0800
+Subject: [PATCH 1/2] net: dsa: qca8k: support PHY-to-PHY CPU link
+
+PHY-to-PHY CPU link is a common/demo design in IPQ50xx platform, since it only has a SGMII/SGMII+ link and a MDI link.
+
+For DSA, CPU tag is the only requirement. Fortunately, qca8337 can enable it on any port. So it's ok to trust a PHY-to-PHY link as a CPU link.
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+---
+ drivers/net/dsa/qca/qca8k-8xxx.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/dsa/qca/qca8k-8xxx.c
++++ b/drivers/net/dsa/qca/qca8k-8xxx.c
+@@ -1013,7 +1013,7 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
+                       return err;
+               }
+-              if (!dsa_is_user_port(priv->ds, reg))
++              if (reg == 0 || reg == 6)
+                       continue;
+               of_get_phy_mode(port, &mode);
+@@ -1088,17 +1088,19 @@ qca8k_setup_mac_pwr_sel(struct qca8k_pri
+ static int qca8k_find_cpu_port(struct dsa_switch *ds)
+ {
+-      struct qca8k_priv *priv = ds->priv;
++      int i;
+-      /* Find the connected cpu port. Valid port are 0 or 6 */
+       if (dsa_is_cpu_port(ds, 0))
+               return 0;
+-      dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6");
+-
+       if (dsa_is_cpu_port(ds, 6))
+               return 6;
++      /* PHY-to-PHY link */
++      for (i = 1; i <= 5; i++)
++              if (dsa_is_cpu_port(ds, i))
++                      return i;
++
+       return -EINVAL;
+ }
diff --git a/target/linux/qualcommax/patches-6.6/0801-dt-bindings-remoteproc-qcom-Add-support-for-multipd-model.patch b/target/linux/qualcommax/patches-6.6/0801-dt-bindings-remoteproc-qcom-Add-support-for-multipd-model.patch
new file mode 100644 (file)
index 0000000..81248e5
--- /dev/null
@@ -0,0 +1,202 @@
+From: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
+Date: Fri, 10 Nov 2023 14:49:29 +0530
+Subject: [PATCH] dt-bindings: remoteproc: qcom: Add support for multipd model
+
+Add new binding document for multipd model remoteproc.
+IPQ5332, IPQ9574 follows multipd model.
+
+Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+---
+--- /dev/null
++++ b/Documentation/devicetree/bindings/remoteproc/qcom,multipd-pil.yaml
+@@ -0,0 +1,189 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/remoteproc/qcom,multipd-pil.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Qualcomm Multipd Secure Peripheral Image Loader
++
++maintainers:
++  - Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
++
++description:
++  Multipd Peripheral Image Loader loads firmware and boots Q6 protection domain,
++  WCSS protection domain remoteproc's on the Qualcomm IPQ9574, IPQ5332 SoC.
++  Protection domain is similar to process in Linux. Here QDSP6 processor runs
++  each wifi radio functionality on a separate process. One process can't access
++  other process resources, so this is termed as PD i.e protection domain.
++
++properties:
++  compatible:
++    enum:
++      - qcom,ipq5332-q6-mpd
++      - qcom,ipq9574-q6-mpd
++
++  reg:
++    maxItems: 1
++
++  firmware-name:
++    maxItems: 2
++
++  interrupts:
++    items:
++      - description: Watchdog interrupt
++      - description: Fatal interrupt
++      - description: Ready interrupt
++      - description: Handover interrupt
++      - description: Stop acknowledge interrupt
++
++  interrupt-names:
++    items:
++      - const: wdog
++      - const: fatal
++      - const: ready
++      - const: handover
++      - const: stop-ack
++
++  qcom,smem-states:
++    $ref: /schemas/types.yaml#/definitions/phandle-array
++    description: States used by the AP to signal the remote processor
++    items:
++      - description: Shutdown Q6
++      - description: Stop Q6
++
++  qcom,smem-state-names:
++    description:
++      Names of the states used by the AP to signal the remote processor
++    items:
++      - const: shutdown
++      - const: stop
++
++  memory-region:
++    items:
++      - description: Q6 reserved region
++
++  glink-edge:
++    $ref: /schemas/remoteproc/qcom,glink-edge.yaml#
++    description:
++      Qualcomm G-Link subnode which represents communication edge, channels
++      and devices related to the Modem.
++    unevaluatedProperties: false
++
++patternProperties:
++  "^pd-1|pd-2|pd-3":
++    type: object
++    description:
++      WCSS means 'wireless connectivity sub system', in simple words it's a
++      wifi radio block. In multipd model, Q6 root protection domain will
++      provide services to WCSS user protection domain. In other words WCSS
++      protection domains depends on Q6 protection domain i.e  Q6 should be up
++      before WCSS, similarly Q6 should shut down after all WCSS domains are
++      down. It can be achieved by building parent-child topology between Q6
++      and WCSS domain's i.e keeping wcss node as sub node of Q6 device node.
++
++    properties:
++      firmware-name:
++        maxItems: 1
++
++      interrupts:
++        items:
++          - description: Fatal interrupt
++          - description: Ready interrupt
++          - description: Spawn acknowledge interrupt
++          - description: Stop acknowledge interrupt
++
++      interrupt-names:
++        items:
++          - const: fatal
++          - const: ready
++          - const: spawn-ack
++          - const: stop-ack
++
++      qcom,smem-states:
++        $ref: /schemas/types.yaml#/definitions/phandle-array
++        description: States used by the AP to signal the remote processor
++        items:
++          - description: Shutdown WCSS pd
++          - description: Stop WCSS pd
++          - description: Spawn WCSS pd
++
++      qcom,smem-state-names:
++        description:
++          Names of the states used by the AP to signal the remote processor
++        items:
++          - const: shutdown
++          - const: stop
++          - const: spawn
++
++    required:
++      - firmware-name
++      - interrupts
++      - interrupt-names
++      - qcom,smem-states
++      - qcom,smem-state-names
++
++    additionalProperties: false
++
++required:
++  - compatible
++  - firmware-name
++  - reg
++  - interrupts
++  - interrupt-names
++  - qcom,smem-states
++  - qcom,smem-state-names
++  - memory-region
++
++additionalProperties: false
++
++examples:
++  - |
++    #include <dt-bindings/interrupt-controller/arm-gic.h>
++    q6v5_wcss: remoteproc@d100000 {
++      compatible = "qcom,ipq5332-q6-mpd";
++      reg = <0xd100000 0x4040>;
++      firmware-name = "ath11k/IPQ5332/hw1.0/q6_fw0.mdt",
++                      "ath11k/IPQ5332/hw1.0/iu_fw.mdt";
++      interrupts-extended = <&intc GIC_SPI 291 IRQ_TYPE_EDGE_RISING>,
++                            <&wcss_smp2p_in 0 IRQ_TYPE_NONE>,
++                            <&wcss_smp2p_in 1 IRQ_TYPE_NONE>,
++                            <&wcss_smp2p_in 2 IRQ_TYPE_NONE>,
++                            <&wcss_smp2p_in 3 IRQ_TYPE_NONE>;
++      interrupt-names = "wdog",
++                        "fatal",
++                        "ready",
++                        "handover",
++                        "stop-ack";
++
++      qcom,smem-states = <&wcss_smp2p_out 0>,
++                         <&wcss_smp2p_out 1>;
++      qcom,smem-state-names = "shutdown",
++                              "stop";
++
++      memory-region = <&q6_region>;
++
++      glink-edge {
++        interrupts = <GIC_SPI 417 IRQ_TYPE_EDGE_RISING>;
++        label = "rtr";
++        qcom,remote-pid = <1>;
++        mboxes = <&apcs_glb 8>;
++      };
++
++      pd-1 {
++        firmware-name = "ath11k/IPQ5332/hw1.0/q6_fw1.mdt";
++        interrupts-extended = <&wcss_smp2p_in 8 IRQ_TYPE_NONE>,
++                              <&wcss_smp2p_in 9 IRQ_TYPE_NONE>,
++                              <&wcss_smp2p_in 12 IRQ_TYPE_NONE>,
++                              <&wcss_smp2p_in 11 IRQ_TYPE_NONE>;
++        interrupt-names = "fatal",
++                          "ready",
++                          "spawn-ack",
++                          "stop-ack";
++        qcom,smem-states = <&wcss_smp2p_out 8>,
++                           <&wcss_smp2p_out 9>,
++                           <&wcss_smp2p_out 10>;
++        qcom,smem-state-names = "shutdown",
++                                "stop",
++                                "spawn";
++      };
++    };
diff --git a/target/linux/qualcommax/patches-6.6/0802-firmware-qcom_scm-ipq5332-add-support-to-pass-metada.patch b/target/linux/qualcommax/patches-6.6/0802-firmware-qcom_scm-ipq5332-add-support-to-pass-metada.patch
new file mode 100644 (file)
index 0000000..677c61f
--- /dev/null
@@ -0,0 +1,43 @@
+From 50799703c6c8ec0860e19b102dd7cca3d29028e1 Mon Sep 17 00:00:00 2001
+From: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
+Date: Fri, 10 Nov 2023 14:49:34 +0530
+Subject: [PATCH] firmware: qcom_scm: ipq5332: add support to pass
+ metadata size
+
+IPQ5332 security software running under trustzone
+requires metadata size. With V2 cmd, pass metadata
+size as well.
+
+Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
+---
+ drivers/firmware/qcom_scm.c | 8 ++++++++
+ drivers/firmware/qcom_scm.h | 1 +
+ 2 files changed, 9 insertions(+)
+
+--- a/drivers/firmware/qcom_scm.c
++++ b/drivers/firmware/qcom_scm.c
+@@ -592,6 +592,14 @@ int qcom_scm_pas_mem_setup(u32 periphera
+       if (ret)
+               goto disable_clk;
++      if (__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
++                                       QCOM_SCM_PAS_INIT_IMAGE_V2)) {
++              desc.cmd = QCOM_SCM_PAS_INIT_IMAGE_V2;
++              desc.arginfo =
++                      QCOM_SCM_ARGS(3, QCOM_SCM_VAL, QCOM_SCM_RW, QCOM_SCM_VAL);
++              desc.args[2] = size;
++      }
++
+       ret = qcom_scm_call(__scm->dev, &desc, &res);
+       qcom_scm_bw_disable();
+--- a/drivers/firmware/qcom_scm.h
++++ b/drivers/firmware/qcom_scm.h
+@@ -92,6 +92,7 @@ extern int scm_legacy_call(struct device
+ #define QCOM_SCM_SVC_PIL              0x02
+ #define QCOM_SCM_PIL_PAS_INIT_IMAGE   0x01
++#define QCOM_SCM_PAS_INIT_IMAGE_V2    0x1a
+ #define QCOM_SCM_PIL_PAS_MEM_SETUP    0x02
+ #define QCOM_SCM_PIL_PAS_AUTH_AND_RESET       0x05
+ #define QCOM_SCM_PIL_PAS_SHUTDOWN     0x06
diff --git a/target/linux/qualcommax/patches-6.6/0803-firmware-qcom_scm-ipq5332-add-msa-lock-unlock-suppor.patch b/target/linux/qualcommax/patches-6.6/0803-firmware-qcom_scm-ipq5332-add-msa-lock-unlock-suppor.patch
new file mode 100644 (file)
index 0000000..06c6b04
--- /dev/null
@@ -0,0 +1,126 @@
+From 217fbbc122663c5a3dac752cebef44fb3e0cc179 Mon Sep 17 00:00:00 2001
+From: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
+Date: Fri, 10 Nov 2023 14:49:35 +0530
+Subject: [PATCH] firmware: qcom_scm: ipq5332: add msa lock/unlock
+ support
+
+IPQ5332 user pd remoteproc firmwares need to be locked
+with MSA(modem secure access) features. This patch add
+support to lock/unlock MSA features.
+
+Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
+---
+ drivers/firmware/qcom_scm.c            | 78 ++++++++++++++++++++++++++
+ drivers/firmware/qcom_scm.h            |  2 +
+ include/linux/firmware/qcom/qcom_scm.h |  2 +
+ 3 files changed, 82 insertions(+)
+
+--- a/drivers/firmware/qcom_scm.c
++++ b/drivers/firmware/qcom_scm.c
+@@ -712,6 +712,84 @@ bool qcom_scm_pas_supported(u32 peripher
+ }
+ EXPORT_SYMBOL_GPL(qcom_scm_pas_supported);
++/**
++ * qcom_scm_msa_lock() - Lock given peripheral firmware region as MSA
++ *
++ * @peripheral:       peripheral id
++ *
++ * Return 0 on success.
++ */
++int qcom_scm_msa_lock(u32 peripheral)
++{
++      int ret;
++      struct qcom_scm_desc desc = {
++              .svc = QCOM_SCM_SVC_PIL,
++              .cmd = QCOM_SCM_MSA_LOCK,
++              .arginfo = QCOM_SCM_ARGS(1),
++              .args[0] = peripheral,
++              .owner = ARM_SMCCC_OWNER_SIP,
++      };
++      struct qcom_scm_res res;
++
++      if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
++                                        QCOM_SCM_MSA_LOCK))
++              return 0;
++
++      ret = qcom_scm_clk_enable();
++      if (ret)
++              return ret;
++
++      ret = qcom_scm_bw_enable();
++      if (ret)
++              return ret;
++
++      ret = qcom_scm_call(__scm->dev, &desc, &res);
++      qcom_scm_bw_disable();
++      qcom_scm_clk_disable();
++
++      return ret ? : res.result[0];
++}
++EXPORT_SYMBOL_GPL(qcom_scm_msa_lock);
++
++/**
++ * qcom_scm_msa_unlock() - Unlock given peripheral MSA firmware region
++ *
++ * @peripheral:       peripheral id
++ *
++ * Return 0 on success.
++ */
++int qcom_scm_msa_unlock(u32 peripheral)
++{
++      int ret;
++      struct qcom_scm_desc desc = {
++              .svc = QCOM_SCM_SVC_PIL,
++              .cmd = QCOM_SCM_MSA_UNLOCK,
++              .arginfo = QCOM_SCM_ARGS(1),
++              .args[0] = peripheral,
++              .owner = ARM_SMCCC_OWNER_SIP,
++      };
++      struct qcom_scm_res res;
++
++      if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
++                                        QCOM_SCM_MSA_UNLOCK))
++              return 0;
++
++      ret = qcom_scm_clk_enable();
++      if (ret)
++              return ret;
++
++      ret = qcom_scm_bw_enable();
++      if (ret)
++              return ret;
++
++      ret = qcom_scm_call(__scm->dev, &desc, &res);
++      qcom_scm_bw_disable();
++      qcom_scm_clk_disable();
++
++      return ret ? : res.result[0];
++}
++EXPORT_SYMBOL_GPL(qcom_scm_msa_unlock);
++
+ static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
+ {
+       struct qcom_scm_desc desc = {
+--- a/drivers/firmware/qcom_scm.h
++++ b/drivers/firmware/qcom_scm.h
+@@ -98,6 +98,8 @@ extern int scm_legacy_call(struct device
+ #define QCOM_SCM_PIL_PAS_SHUTDOWN     0x06
+ #define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07
+ #define QCOM_SCM_PIL_PAS_MSS_RESET    0x0a
++#define QCOM_SCM_MSA_LOCK             0x24
++#define QCOM_SCM_MSA_UNLOCK           0x25
+ #define QCOM_SCM_SVC_IO                       0x05
+ #define QCOM_SCM_IO_READ              0x01
+--- a/include/linux/firmware/qcom/qcom_scm.h
++++ b/include/linux/firmware/qcom/qcom_scm.h
+@@ -81,6 +81,8 @@ extern int qcom_scm_pas_mem_setup(u32 pe
+ extern int qcom_scm_pas_auth_and_reset(u32 peripheral);
+ extern int qcom_scm_pas_shutdown(u32 peripheral);
+ extern bool qcom_scm_pas_supported(u32 peripheral);
++extern int qcom_scm_msa_lock(u32 peripheral);
++extern int qcom_scm_msa_unlock(u32 peripheral);
+ extern int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val);
+ extern int qcom_scm_io_writel(phys_addr_t addr, unsigned int val);
diff --git a/target/linux/qualcommax/patches-6.6/0804-remoteproc-qcom-q6v5-Add-multipd-interrupts-support.patch b/target/linux/qualcommax/patches-6.6/0804-remoteproc-qcom-q6v5-Add-multipd-interrupts-support.patch
new file mode 100644 (file)
index 0000000..4e95543
--- /dev/null
@@ -0,0 +1,148 @@
+From cae691d32306966065df869fa7424728d1b16b14 Mon Sep 17 00:00:00 2001
+From: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
+Date: Fri, 10 Nov 2023 14:49:36 +0530
+Subject: [PATCH] remoteproc: qcom: q6v5: Add multipd interrupts support
+
+In multipd model, root & user pd remoteproc's interrupts are
+different. User pd needs additional interrupts like spawn.
+Instead of going with qcom_q6v5_init(), we defined a new
+function to register userpd rproc interrupts in mpd driver.
+Since userpd rproc uses some of common interrupts like fatal,
+ready, static is removed from ISR handler and used in userpd
+interrupt registration.
+
+Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
+---
+ drivers/remoteproc/qcom_q6v5.c | 41 +++++++++++++++++++++++++++++++---
+ drivers/remoteproc/qcom_q6v5.h | 11 +++++++++
+ 2 files changed, 49 insertions(+), 3 deletions(-)
+
+--- a/drivers/remoteproc/qcom_q6v5.c
++++ b/drivers/remoteproc/qcom_q6v5.c
+@@ -112,7 +112,7 @@ static irqreturn_t q6v5_wdog_interrupt(i
+       return IRQ_HANDLED;
+ }
+-static irqreturn_t q6v5_fatal_interrupt(int irq, void *data)
++irqreturn_t q6v5_fatal_interrupt(int irq, void *data)
+ {
+       struct qcom_q6v5 *q6v5 = data;
+       size_t len;
+@@ -132,8 +132,9 @@ static irqreturn_t q6v5_fatal_interrupt(
+       return IRQ_HANDLED;
+ }
++EXPORT_SYMBOL_GPL(q6v5_fatal_interrupt);
+-static irqreturn_t q6v5_ready_interrupt(int irq, void *data)
++irqreturn_t q6v5_ready_interrupt(int irq, void *data)
+ {
+       struct qcom_q6v5 *q6v5 = data;
+@@ -141,6 +142,7 @@ static irqreturn_t q6v5_ready_interrupt(
+       return IRQ_HANDLED;
+ }
++EXPORT_SYMBOL_GPL(q6v5_ready_interrupt);
+ /**
+  * qcom_q6v5_wait_for_start() - wait for remote processor start signal
+@@ -177,7 +179,17 @@ static irqreturn_t q6v5_handover_interru
+       return IRQ_HANDLED;
+ }
+-static irqreturn_t q6v5_stop_interrupt(int irq, void *data)
++irqreturn_t q6v5_spawn_interrupt(int irq, void *data)
++{
++      struct qcom_q6v5 *q6v5 = data;
++
++      complete(&q6v5->spawn_done);
++
++      return IRQ_HANDLED;
++}
++EXPORT_SYMBOL_GPL(q6v5_spawn_interrupt);
++
++irqreturn_t q6v5_stop_interrupt(int irq, void *data)
+ {
+       struct qcom_q6v5 *q6v5 = data;
+@@ -185,6 +197,7 @@ static irqreturn_t q6v5_stop_interrupt(i
+       return IRQ_HANDLED;
+ }
++EXPORT_SYMBOL_GPL(q6v5_stop_interrupt);
+ /**
+  * qcom_q6v5_request_stop() - request the remote processor to stop
+@@ -215,6 +228,28 @@ int qcom_q6v5_request_stop(struct qcom_q
+ EXPORT_SYMBOL_GPL(qcom_q6v5_request_stop);
+ /**
++ * qcom_q6v5_request_spawn() - request the remote processor to spawn
++ * @q6v5:      reference to qcom_q6v5 context
++ *
++ * Return: 0 on success, negative errno on failure
++ */
++int qcom_q6v5_request_spawn(struct qcom_q6v5 *q6v5)
++{
++      int ret;
++
++      ret = qcom_smem_state_update_bits(q6v5->spawn_state,
++                                        BIT(q6v5->spawn_bit), BIT(q6v5->spawn_bit));
++
++      ret = wait_for_completion_timeout(&q6v5->spawn_done, 5 * HZ);
++
++      qcom_smem_state_update_bits(q6v5->spawn_state,
++                                  BIT(q6v5->spawn_bit), 0);
++
++      return ret == 0 ? -ETIMEDOUT : 0;
++}
++EXPORT_SYMBOL_GPL(qcom_q6v5_request_spawn);
++
++/**
+  * qcom_q6v5_panic() - panic handler to invoke a stop on the remote
+  * @q6v5:     reference to qcom_q6v5 context
+  *
+--- a/drivers/remoteproc/qcom_q6v5.h
++++ b/drivers/remoteproc/qcom_q6v5.h
+@@ -18,21 +18,27 @@ struct qcom_q6v5 {
+       struct qcom_smem_state *state;
+       struct qmp *qmp;
++      struct qcom_smem_state *shutdown_state;
++      struct qcom_smem_state *spawn_state;
+       struct icc_path *path;
+       unsigned stop_bit;
++      unsigned shutdown_bit;
++      unsigned spawn_bit;
+       int wdog_irq;
+       int fatal_irq;
+       int ready_irq;
+       int handover_irq;
+       int stop_irq;
++      int spawn_irq;
+       bool handover_issued;
+       struct completion start_done;
+       struct completion stop_done;
++      struct completion spawn_done;
+       int crash_reason;
+@@ -50,7 +56,12 @@ void qcom_q6v5_deinit(struct qcom_q6v5 *
+ int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5);
+ int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5);
+ int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct qcom_sysmon *sysmon);
++int qcom_q6v5_request_spawn(struct qcom_q6v5 *q6v5);
+ int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout);
+ unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5);
++irqreturn_t q6v5_fatal_interrupt(int irq, void *data);
++irqreturn_t q6v5_ready_interrupt(int irq, void *data);
++irqreturn_t q6v5_spawn_interrupt(int irq, void *data);
++irqreturn_t q6v5_stop_interrupt(int irq, void *data);
+ #endif
diff --git a/target/linux/qualcommax/patches-6.6/0805-remoteproc-qcom-Add-Hexagon-based-multipd-rproc-driv.patch b/target/linux/qualcommax/patches-6.6/0805-remoteproc-qcom-Add-Hexagon-based-multipd-rproc-driv.patch
new file mode 100644 (file)
index 0000000..14371cc
--- /dev/null
@@ -0,0 +1,901 @@
+From 3f61ff1fb5c90f8b6c28a3a2b4a29203000ee585 Mon Sep 17 00:00:00 2001
+From: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
+Date: Fri, 10 Nov 2023 14:49:37 +0530
+Subject: [PATCH] remoteproc: qcom: Add Hexagon based multipd rproc
+ driver
+
+It adds support to bring up remoteproc's on multipd model.
+Pd means protection domain. It's similar to process in Linux.
+Here QDSP6 processor runs each wifi radio functionality on a
+separate process. One process can't access other process
+resources, so this is termed as PD i.e protection domain.
+
+Here we have two pd's called root and user pd. We can correlate
+Root pd as root and user pd as user in linux. Root pd has more
+privileges than user pd. Root will provide services to user pd.
+
+>From remoteproc driver perspective, root pd corresponds to QDSP6
+processor bring up and user pd corresponds to Wifi radio (WCSS)
+bring up.
+
+Here WCSS(user) PD is dependent on Q6(root) PD, so first
+q6 pd should be up before wcss pd. After wcss pd goes down,
+q6 pd should be turned off.
+
+ APPS                          QDSP6
+-------                      -------------
+|     |         Crash notification  |          |       ------
+|     |<---------------------|----------|-------|User|
+|     |                             |          |    |->|PD1 |
+|     |                             |  ------- |    |  ------
+|     |                             |  |     | |    |
+|Root |         Start/stop  Q6      |  |  R  | |    |
+|PD   |<---------------------|->|     | |    |
+|rproc|  Crash notification  | |  O  | |    |
+|     |                             |  |     | |    |
+|User |Start/stop UserPD1    |  |  O  | |    |
+|PD1  |----------------------|->|     |-|----|
+|rproc|                             |  |  T  | |    |
+|     |                             |  |     | |    |
+|User |Start/stop UserPD2    | |  P  | |    |
+|PD2  |----------------------|->|     |-|----|
+|rproc|                             |  |  D  | |    |
+|     |                             |  ------- |    |  ------
+|     |        Crash notification   |          |    |->|User|
+|     |<---------------------|----------|-------|PD2 |
+-------                             |          |       ------
+                             ------------
+
+IPQ5332, IPQ9574 supports multipd remoteproc driver.
+
+Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
+---
+ drivers/remoteproc/Kconfig         |  19 +
+ drivers/remoteproc/Makefile        |   1 +
+ drivers/remoteproc/qcom_q6v5_mpd.c | 802 +++++++++++++++++++++++++++++
+ 3 files changed, 822 insertions(+)
+ create mode 100644 drivers/remoteproc/qcom_q6v5_mpd.c
+
+--- a/drivers/remoteproc/Kconfig
++++ b/drivers/remoteproc/Kconfig
+@@ -234,6 +234,25 @@ config QCOM_Q6V5_PAS
+         CDSP (Compute DSP), MPSS (Modem Peripheral SubSystem), and
+         SLPI (Sensor Low Power Island).
++config QCOM_Q6V5_MPD
++      tristate "Qualcomm Hexagon based MPD model Peripheral Image Loader"
++      depends on OF && ARCH_QCOM
++      depends on QCOM_SMEM
++      depends on RPMSG_QCOM_SMD || RPMSG_QCOM_SMD=n
++      depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
++      depends on QCOM_SYSMON || QCOM_SYSMON=n
++      depends on RPMSG_QCOM_GLINK || RPMSG_QCOM_GLINK=n
++      depends on QCOM_AOSS_QMP || QCOM_AOSS_QMP=n
++      select QCOM_MDT_LOADER
++      select QCOM_PIL_INFO
++      select QCOM_Q6V5_COMMON
++      select QCOM_RPROC_COMMON
++      select QCOM_SCM
++      help
++        Say y here to support the Qualcomm Secure Peripheral Image Loader
++        for the Hexagon based MultiPD model remote processors on e.g. IPQ5018.
++        This is trustZone wireless subsystem.
++
+ config QCOM_Q6V5_WCSS
+       tristate "Qualcomm Hexagon based WCSS Peripheral Image Loader"
+       depends on OF && ARCH_QCOM
+--- a/drivers/remoteproc/Makefile
++++ b/drivers/remoteproc/Makefile
+@@ -25,6 +25,7 @@ obj-$(CONFIG_QCOM_PIL_INFO)          += qcom_pil
+ obj-$(CONFIG_QCOM_RPROC_COMMON)               += qcom_common.o
+ obj-$(CONFIG_QCOM_Q6V5_COMMON)                += qcom_q6v5.o
+ obj-$(CONFIG_QCOM_Q6V5_ADSP)          += qcom_q6v5_adsp.o
++obj-$(CONFIG_QCOM_Q6V5_MPD)           += qcom_q6v5_mpd.o
+ obj-$(CONFIG_QCOM_Q6V5_MSS)           += qcom_q6v5_mss.o
+ obj-$(CONFIG_QCOM_Q6V5_PAS)           += qcom_q6v5_pas.o
+ obj-$(CONFIG_QCOM_Q6V5_WCSS)          += qcom_q6v5_wcss.o
+--- /dev/null
++++ b/drivers/remoteproc/qcom_q6v5_mpd.c
+@@ -0,0 +1,802 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2016-2018 Linaro Ltd.
++ * Copyright (C) 2014 Sony Mobile Communications AB
++ * Copyright (c) 2012-2018, 2021 The Linux Foundation. All rights reserved.
++ */
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/firmware/qcom/qcom_scm.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/iopoll.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of_address.h>
++#include <linux/of_device.h>
++#include <linux/of_reserved_mem.h>
++#include <linux/platform_device.h>
++#include <linux/reset.h>
++#include <linux/soc/qcom/mdt_loader.h>
++#include <linux/soc/qcom/smem.h>
++#include <linux/soc/qcom/smem_state.h>
++#include "qcom_common.h"
++#include "qcom_q6v5.h"
++
++#include "remoteproc_internal.h"
++
++#define WCSS_CRASH_REASON             421
++#define WCSS_SMEM_HOST                        1
++
++#define WCNSS_PAS_ID                  6
++#define MPD_WCNSS_PAS_ID        0xD
++
++#define BUF_SIZE                      35
++
++#define MAX_FIRMWARE                  3
++
++#define RPD_SWID              MPD_WCNSS_PAS_ID
++#define UPD_SWID              0x12
++#define REMOTE_PID                    1
++#define UPD_BOOT_INFO_SMEM_SIZE               4096
++#define UPD_BOOT_INFO_HEADER_TYPE     0x2
++#define UPD_BOOT_INFO_SMEM_ID         507
++#define VERSION2                      2
++
++static LIST_HEAD(upd_rproc_list);
++enum {
++      Q6_IPQ,
++      WCSS_IPQ,
++};
++
++/**
++ * struct userpd_boot_info_header - header of user pd bootinfo
++ * @type:             type of bootinfo passing over smem
++ * @length:           length of header in bytes
++ */
++struct userpd_boot_info_header {
++      u8 type;
++      u8 length;
++};
++
++/**
++ * struct userpd_boot_info - holds info required to boot user pd
++ * @header:           pointer to header
++ * @pid:              unique id represents each user pd process
++ * @bootaddr:         load address of user pd firmware
++ * @data_size:                user pd firmware memory size
++ */
++struct userpd_boot_info {
++      struct userpd_boot_info_header header;
++      u8 pid;
++      u32 bootaddr;
++      u32 data_size;
++} __packed;
++
++struct q6_wcss {
++      struct device *dev;
++      struct qcom_rproc_glink glink_subdev;
++      struct qcom_rproc_ssr ssr_subdev;
++      struct qcom_q6v5 q6;
++      phys_addr_t mem_phys;
++      phys_addr_t mem_reloc;
++      void *mem_region;
++      size_t mem_size;
++      u8 pd_asid;
++      const struct wcss_data *desc;
++      const char **firmware;
++      u32 version;
++};
++
++struct wcss_data {
++      u32 pasid;
++      bool share_upd_info_to_q6;
++};
++
++/**
++ * qcom_get_pd_asid() - get the pd asid number from PD spawn bit
++ * @rproc:    rproc handle
++ *
++ * Returns asid on success
++ */
++static u8 qcom_get_pd_asid(struct rproc *rproc)
++{
++      struct q6_wcss *wcss = rproc->priv;
++      u8 bit = wcss->q6.spawn_bit;
++
++      return bit / 8;
++}
++
++static int q6_wcss_start(struct rproc *rproc)
++{
++      struct q6_wcss *wcss = rproc->priv;
++      int ret;
++      const struct wcss_data *desc = wcss->desc;
++
++      qcom_q6v5_prepare(&wcss->q6);
++
++      ret = qcom_scm_pas_auth_and_reset(desc->pasid);
++      if (ret) {
++              dev_err(wcss->dev, "wcss_reset failed\n");
++              return ret;
++      }
++
++      ret = qcom_q6v5_wait_for_start(&wcss->q6, 5 * HZ);
++      if (ret == -ETIMEDOUT)
++              dev_err(wcss->dev, "start timed out\n");
++
++      return ret;
++}
++
++static int q6_wcss_spawn_pd(struct rproc *rproc)
++{
++      int ret;
++      struct q6_wcss *wcss = rproc->priv;
++
++      ret = qcom_q6v5_request_spawn(&wcss->q6);
++      if (ret == -ETIMEDOUT) {
++              dev_err(wcss->dev, "%s spawn timedout\n", rproc->name);
++              return ret;
++      }
++
++      ret = qcom_q6v5_wait_for_start(&wcss->q6, msecs_to_jiffies(10000));
++      if (ret == -ETIMEDOUT) {
++              dev_err(wcss->dev, "%s start timedout\n", rproc->name);
++              wcss->q6.running = false;
++              return ret;
++      }
++      wcss->q6.running = true;
++      return ret;
++}
++
++static int wcss_pd_start(struct rproc *rproc)
++{
++      struct q6_wcss *wcss = rproc->priv;
++      u32 pasid = (wcss->pd_asid << 8) | UPD_SWID;
++      int ret;
++
++      ret = qcom_scm_msa_lock(pasid);
++      if (ret) {
++              dev_err(wcss->dev, "failed to power up pd\n");
++              return ret;
++      }
++
++      if (wcss->q6.spawn_bit) {
++              ret = q6_wcss_spawn_pd(rproc);
++              if (ret)
++                      return ret;
++      }
++
++      return ret;
++}
++
++static int q6_wcss_stop(struct rproc *rproc)
++{
++      struct q6_wcss *wcss = rproc->priv;
++      const struct wcss_data *desc = wcss->desc;
++      int ret;
++
++      ret = qcom_scm_pas_shutdown(desc->pasid);
++      if (ret) {
++              dev_err(wcss->dev, "not able to shutdown\n");
++              return ret;
++      }
++      qcom_q6v5_unprepare(&wcss->q6);
++
++      return 0;
++}
++
++/**
++ * wcss_pd_stop() - Stop WCSS user pd
++ * @rproc:    rproc handle
++ *
++ * Stop root pd after user pd down. Root pd
++ * is used to provide services to user pd, so
++ * keeping root pd alive when user pd is down
++ * is invalid.
++ * ---------------------------------------------
++ *
++ *                            -----------
++ *                 |-------->| User PD1 |
++ *                 |          -----------
++ *                 |
++ *                 |
++ *    -----        |          -----------
++ *    | Q6 |---------------->| User Pd2 |
++ *    -----        |          -----------
++ *                 |
++ *                 |
++ *                 |          -----------
++ *                 |--------->| User Pd3 |
++ *                            -----------
++ * ----------------------------------------------
++ */
++static int wcss_pd_stop(struct rproc *rproc)
++{
++      struct q6_wcss *wcss = rproc->priv;
++      struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent);
++      u32 pasid = (wcss->pd_asid << 8) | UPD_SWID;
++      int ret;
++
++      if (rproc->state != RPROC_CRASHED && wcss->q6.stop_bit) {
++              ret = qcom_q6v5_request_stop(&wcss->q6, NULL);
++              if (ret) {
++                      dev_err(&rproc->dev, "pd not stopped\n");
++                      return ret;
++              }
++      }
++
++      ret = qcom_scm_msa_unlock(pasid);
++      if (ret) {
++              dev_err(wcss->dev, "failed to power down pd\n");
++              return ret;
++      }
++
++      rproc_shutdown(rpd_rproc);
++
++      return 0;
++}
++
++static void *q6_wcss_da_to_va(struct rproc *rproc, u64 da, size_t len,
++                            bool *is_iomem)
++{
++      struct q6_wcss *wcss = rproc->priv;
++      int offset;
++
++      offset = da - wcss->mem_reloc;
++      if (offset < 0 || offset + len > wcss->mem_size)
++              return NULL;
++
++      return wcss->mem_region + offset;
++}
++
++/**
++ * share_upd_bootinfo_to_q6() - Share userpd boot info to Q6 root pd
++ * @rproc:    rproc handle
++ *
++ * Q6 needs user pd parameters like loadaddress and
++ * PIL size to authenticate user pd with underlying
++ * security software. If authenticatoin success then
++ * only Q6 spawns user pd and sends spawn ack to rproc
++ * driver. This API is passing userpd boot info to Q6
++ * over SMEM.
++ *
++ * User pd boot-info format mentioned below
++ * <Version> <No of elements passing over smem> <Header type> <Header Length>
++ * <Process Id> <Load address> <firmware mem Size>
++ *
++ * Returns 0 on success else negative value on failure.
++ */
++static int share_upd_bootinfo_to_q6(struct rproc *rproc)
++{
++      int ret;
++      size_t size;
++      u16 cnt = 0, version;
++      void *ptr;
++      struct q6_wcss *wcss = rproc->priv, *upd_wcss;
++      struct rproc *upd_rproc;
++      struct userpd_boot_info upd_bootinfo = {0};
++      const struct firmware *fw;
++
++      ret = qcom_smem_alloc(REMOTE_PID, UPD_BOOT_INFO_SMEM_ID,
++                            UPD_BOOT_INFO_SMEM_SIZE);
++      if (ret && ret != -EEXIST) {
++              dev_err(wcss->dev,
++                      "failed to allocate q6 bootinfo smem segment\n");
++              return ret;
++      }
++
++      ptr = qcom_smem_get(REMOTE_PID, UPD_BOOT_INFO_SMEM_ID, &size);
++      if (IS_ERR(ptr) || size != UPD_BOOT_INFO_SMEM_SIZE) {
++              dev_err(wcss->dev,
++                      "Unable to acquire smp2p item(%d) ret:%ld\n",
++                      UPD_BOOT_INFO_SMEM_ID, PTR_ERR(ptr));
++              return PTR_ERR(ptr);
++      }
++
++      /*Version*/
++      version = VERSION2;
++      memcpy_toio(ptr, &version, sizeof(version));
++      ptr += sizeof(version);
++
++      list_for_each_entry(upd_rproc, &upd_rproc_list, node)
++              cnt++;
++
++      /* No of elements */
++      cnt = (sizeof(upd_bootinfo) * cnt);
++      memcpy_toio(ptr, &cnt, sizeof(u16));
++      ptr += sizeof(u16);
++
++      list_for_each_entry(upd_rproc, &upd_rproc_list, node) {
++              upd_wcss = upd_rproc->priv;
++
++              /* TYPE */
++              upd_bootinfo.header.type = UPD_BOOT_INFO_HEADER_TYPE;
++
++              /* LENGTH */
++              upd_bootinfo.header.length =
++                      sizeof(upd_bootinfo) - sizeof(upd_bootinfo.header);
++
++              /* Process ID */
++              upd_bootinfo.pid = upd_wcss->pd_asid + 1;
++
++              ret = request_firmware(&fw, upd_rproc->firmware, upd_wcss->dev);
++              if (ret < 0) {
++                      dev_err(upd_wcss->dev, "request_firmware failed: %d\n", ret);
++                      return ret;
++              }
++
++              /* Load address */
++              upd_bootinfo.bootaddr = rproc_get_boot_addr(upd_rproc, fw);
++
++              /* Firmware mem size */
++              upd_bootinfo.data_size = qcom_mdt_get_size(fw);
++
++              release_firmware(fw);
++
++              /* copy into smem */
++              memcpy_toio(ptr, &upd_bootinfo, sizeof(upd_bootinfo));
++              ptr += sizeof(upd_bootinfo);
++      }
++      return 0;
++}
++
++static int q6_wcss_load(struct rproc *rproc, const struct firmware *fw)
++{
++      struct q6_wcss *wcss = rproc->priv;
++      const struct firmware *fw_hdl;
++      int ret;
++      const struct wcss_data *desc = wcss->desc;
++      int loop;
++
++      /* Share user pd boot info to Q6 remote processor */
++      if (desc->share_upd_info_to_q6) {
++              ret = share_upd_bootinfo_to_q6(rproc);
++              if (ret) {
++                      dev_err(wcss->dev,
++                              "user pd boot info sharing with q6 failed %d\n",
++                              ret);
++                      return ret;
++              }
++      }
++
++      ret = qcom_mdt_load(wcss->dev, fw, rproc->firmware,
++                          desc->pasid, wcss->mem_region,
++                          wcss->mem_phys, wcss->mem_size,
++                          &wcss->mem_reloc);
++      if (ret)
++              return ret;
++
++      for (loop = 1; loop < MAX_FIRMWARE; loop++) {
++              if (!wcss->firmware[loop])
++                      continue;
++
++              ret = request_firmware(&fw_hdl, wcss->firmware[loop],
++                                     wcss->dev);
++              if (ret)
++                      continue;
++
++              ret = qcom_mdt_load_no_init(wcss->dev, fw_hdl,
++                                          wcss->firmware[loop], 0,
++                                          wcss->mem_region,
++                                          wcss->mem_phys,
++                                          wcss->mem_size,
++                                          &wcss->mem_reloc);
++
++              release_firmware(fw_hdl);
++
++              if (ret) {
++                      dev_err(wcss->dev,
++                              "can't load %s ret:%d\n", wcss->firmware[loop], ret);
++                      return ret;
++              }
++      }
++      return 0;
++}
++
++/**
++ * wcss_pd_load() - Load WCSS user pd firmware
++ * @rproc:    rproc handle
++ * @fw:               firmware handle
++ *
++ * User pd get services from root pd. So first
++ * bring up root pd and then load userpd firmware.
++ * ---------------------------------------------
++ *
++ *                            -----------
++ *                 |-------->| User PD1 |
++ *                 |          -----------
++ *                 |
++ *                 |
++ *    -----        |          -----------
++ *    | Q6 |---------------->| User Pd2 |
++ *    -----        |          -----------
++ *                 |
++ *                 |
++ *                 |          -----------
++ *                 |--------->| User Pd3 |
++ *                            -----------
++ * ----------------------------------------------
++ *
++ */
++static int wcss_pd_load(struct rproc *rproc, const struct firmware *fw)
++{
++      struct q6_wcss *wcss = rproc->priv;
++      struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent);
++      u32 pasid = (wcss->pd_asid << 8) | UPD_SWID;
++      int ret;
++
++      ret = rproc_boot(rpd_rproc);
++      if (ret)
++              return ret;
++
++      return qcom_mdt_load(wcss->dev, fw, rproc->firmware,
++                           pasid, wcss->mem_region,
++                           wcss->mem_phys, wcss->mem_size,
++                           &wcss->mem_reloc);
++}
++
++static unsigned long q6_wcss_panic(struct rproc *rproc)
++{
++      struct q6_wcss *wcss = rproc->priv;
++
++      return qcom_q6v5_panic(&wcss->q6);
++}
++
++static const struct rproc_ops wcss_ops = {
++      .start = wcss_pd_start,
++      .stop = wcss_pd_stop,
++      .load = wcss_pd_load,
++      .get_boot_addr = rproc_elf_get_boot_addr,
++};
++
++static const struct rproc_ops q6_wcss_ops = {
++      .start = q6_wcss_start,
++      .stop = q6_wcss_stop,
++      .da_to_va = q6_wcss_da_to_va,
++      .load = q6_wcss_load,
++      .get_boot_addr = rproc_elf_get_boot_addr,
++      .panic = q6_wcss_panic,
++};
++
++static int q6_alloc_memory_region(struct q6_wcss *wcss)
++{
++      struct reserved_mem *rmem = NULL;
++      struct device_node *node;
++      struct device *dev = wcss->dev;
++
++      if (wcss->version == Q6_IPQ) {
++              node = of_parse_phandle(dev->of_node, "memory-region", 0);
++              if (node)
++                      rmem = of_reserved_mem_lookup(node);
++
++              of_node_put(node);
++
++              if (!rmem) {
++                      dev_err(dev, "unable to acquire memory-region\n");
++                      return -EINVAL;
++              }
++      } else {
++              struct rproc *rpd_rproc = dev_get_drvdata(dev->parent);
++              struct q6_wcss *rpd_wcss = rpd_rproc->priv;
++
++              wcss->mem_phys = rpd_wcss->mem_phys;
++              wcss->mem_reloc = rpd_wcss->mem_reloc;
++              wcss->mem_size = rpd_wcss->mem_size;
++              wcss->mem_region = rpd_wcss->mem_region;
++              return 0;
++      }
++
++      wcss->mem_phys = rmem->base;
++      wcss->mem_reloc = rmem->base;
++      wcss->mem_size = rmem->size;
++      wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size);
++      if (!wcss->mem_region) {
++              dev_err(dev, "unable to map memory region: %pa+%pa\n",
++                      &rmem->base, &rmem->size);
++              return -EBUSY;
++      }
++
++      return 0;
++}
++
++static int q6_get_inbound_irq(struct qcom_q6v5 *q6,
++                            struct platform_device *pdev,
++                            const char *int_name,
++                            int index, int *pirq,
++                            irqreturn_t (*handler)(int irq, void *data))
++{
++      int ret, irq;
++      char *interrupt, *tmp = (char *)int_name;
++      struct q6_wcss *wcss = q6->rproc->priv;
++
++      irq = platform_get_irq(pdev, index);
++      if (irq < 0)
++              return irq;
++
++      *pirq = irq;
++
++      interrupt = devm_kzalloc(&pdev->dev, BUF_SIZE, GFP_KERNEL);
++      if (!interrupt)
++              return -ENOMEM;
++
++      snprintf(interrupt, BUF_SIZE, "q6v5_wcss_userpd%d_%s", wcss->pd_asid, tmp);
++
++      ret = devm_request_threaded_irq(&pdev->dev, *pirq,
++                                      NULL, handler,
++                                      IRQF_TRIGGER_RISING | IRQF_ONESHOT,
++                                      interrupt, q6);
++      if (ret)
++              return dev_err_probe(&pdev->dev, ret,
++                                   "failed to acquire %s irq\n", interrupt);
++      return 0;
++}
++
++static int q6_get_outbound_irq(struct qcom_q6v5 *q6,
++                             struct platform_device *pdev,
++                             const char *int_name)
++{
++      struct qcom_smem_state *tmp_state;
++      unsigned  bit;
++
++      tmp_state = qcom_smem_state_get(&pdev->dev, int_name, &bit);
++      if (IS_ERR(tmp_state))
++              return dev_err_probe(&pdev->dev, PTR_ERR(tmp_state),
++                                   "failed to acquire %s state\n", int_name);
++
++      if (!strcmp(int_name, "stop")) {
++              q6->state = tmp_state;
++              q6->stop_bit = bit;
++      } else if (!strcmp(int_name, "spawn")) {
++              q6->spawn_state = tmp_state;
++              q6->spawn_bit = bit;
++      }
++
++      return 0;
++}
++
++static int init_irq(struct qcom_q6v5 *q6,
++                  struct platform_device *pdev, struct rproc *rproc,
++                  int crash_reason, const char *load_state,
++                  void (*handover)(struct qcom_q6v5 *q6))
++{
++      int ret;
++      struct q6_wcss *wcss = rproc->priv;
++
++      q6->rproc = rproc;
++      q6->dev = &pdev->dev;
++      q6->crash_reason = crash_reason;
++      q6->handover = handover;
++
++      init_completion(&q6->start_done);
++      init_completion(&q6->stop_done);
++      init_completion(&q6->spawn_done);
++
++      ret = q6_get_outbound_irq(q6, pdev, "stop");
++      if (ret)
++              return ret;
++
++      ret = q6_get_outbound_irq(q6, pdev, "spawn");
++      if (ret)
++              return ret;
++
++      /* Get pd_asid to prepare interrupt names */
++      wcss->pd_asid = qcom_get_pd_asid(rproc);
++
++      ret = q6_get_inbound_irq(q6, pdev, "fatal", 0, &q6->fatal_irq,
++                               q6v5_fatal_interrupt);
++      if (ret)
++              return ret;
++
++      ret = q6_get_inbound_irq(q6, pdev, "ready", 1, &q6->ready_irq,
++                               q6v5_ready_interrupt);
++      if (ret)
++              return ret;
++
++      ret = q6_get_inbound_irq(q6, pdev, "stop-ack", 3, &q6->stop_irq,
++                               q6v5_stop_interrupt);
++      if (ret)
++              return ret;
++
++      ret = q6_get_inbound_irq(q6, pdev, "spawn-ack", 2, &q6->spawn_irq,
++                               q6v5_spawn_interrupt);
++      if (ret)
++              return ret;
++      return 0;
++}
++
++static void q6_release_resources(void)
++{
++      struct rproc *upd_rproc;
++
++      /* Release userpd resources */
++      list_for_each_entry(upd_rproc, &upd_rproc_list, node) {
++              rproc_del(upd_rproc);
++              rproc_free(upd_rproc);
++      }
++}
++
++static int q6_register_userpd(struct platform_device *pdev,
++                            struct device_node *userpd_np)
++{
++      struct q6_wcss *wcss;
++      struct rproc *rproc = NULL;
++      int ret;
++      struct platform_device *userpd_pdev;
++      const char *firmware_name = NULL;
++      const char *label = NULL;
++
++      ret = of_property_read_string(userpd_np, "firmware-name",
++                                    &firmware_name);
++      if (ret < 0) {
++              /* All userpd's who want to register as rproc must have firmware.
++               * Other than userpd like glink they don't need any firmware.
++               * So for glink child simply return success.
++               */
++              if (ret == -EINVAL) {
++                      /* Confirming userpd_np is glink node or not */
++                      if (!of_property_read_string(userpd_np, "label", &label))
++                              return 0;
++              }
++              return ret;
++      }
++
++      dev_info(&pdev->dev, "%s node found\n", userpd_np->name);
++
++      userpd_pdev = of_platform_device_create(userpd_np, userpd_np->name,
++                                              &pdev->dev);
++      if (!userpd_pdev)
++              return dev_err_probe(&pdev->dev, -ENODEV,
++                                   "failed to create %s platform device\n",
++                                   userpd_np->name);
++
++      userpd_pdev->dev.driver = pdev->dev.driver;
++      rproc = rproc_alloc(&userpd_pdev->dev, userpd_pdev->name, &wcss_ops,
++                          firmware_name, sizeof(*wcss));
++      if (!rproc) {
++              ret = -ENOMEM;
++              goto free_rproc;
++      }
++
++      wcss = rproc->priv;
++      wcss->dev = &userpd_pdev->dev;
++      wcss->version = WCSS_IPQ;
++
++      ret = q6_alloc_memory_region(wcss);
++      if (ret)
++              goto free_rproc;
++
++      ret = init_irq(&wcss->q6, userpd_pdev, rproc,
++                     WCSS_CRASH_REASON, NULL, NULL);
++      if (ret)
++              goto free_rproc;
++
++      rproc->auto_boot = false;
++      ret = rproc_add(rproc);
++      if (ret)
++              goto free_rproc;
++
++      list_add(&rproc->node, &upd_rproc_list);
++      platform_set_drvdata(userpd_pdev, rproc);
++      qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, userpd_pdev->name);
++      return 0;
++
++free_rproc:
++      kfree(rproc);
++      return ret;
++}
++
++static int q6_wcss_probe(struct platform_device *pdev)
++{
++      const struct wcss_data *desc;
++      struct q6_wcss *wcss;
++      struct rproc *rproc;
++      int ret;
++      const char **firmware;
++      struct device_node *userpd_np;
++      const struct rproc_ops *ops = &q6_wcss_ops;
++
++      desc = of_device_get_match_data(&pdev->dev);
++      if (!desc)
++              return -EINVAL;
++
++      firmware = devm_kcalloc(&pdev->dev, MAX_FIRMWARE,
++                              sizeof(*firmware), GFP_KERNEL);
++      if (!firmware)
++              return -ENOMEM;
++
++      ret = of_property_read_string_array(pdev->dev.of_node, "firmware-name",
++                                          firmware, MAX_FIRMWARE);
++      if (ret < 0)
++              return ret;
++
++      rproc = rproc_alloc(&pdev->dev, pdev->name, ops,
++                          firmware[0], sizeof(*wcss));
++      if (!rproc)
++              return -ENOMEM;
++
++      wcss = rproc->priv;
++      wcss->dev = &pdev->dev;
++      wcss->desc = desc;
++      wcss->firmware = firmware;
++      wcss->version = Q6_IPQ;
++
++      ret = q6_alloc_memory_region(wcss);
++      if (ret)
++              goto free_rproc;
++
++      ret = qcom_q6v5_init(&wcss->q6, pdev, rproc,
++                           WCSS_CRASH_REASON, NULL, NULL);
++      if (ret)
++              goto free_rproc;
++
++      qcom_add_glink_subdev(rproc, &wcss->glink_subdev, "q6wcss");
++      qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, "q6wcss");
++
++      rproc->auto_boot = false;
++      ret = rproc_add(rproc);
++      if (ret)
++              goto free_rproc;
++
++      platform_set_drvdata(pdev, rproc);
++
++      /* Iterate over userpd child's and register with rproc */
++      for_each_available_child_of_node(pdev->dev.of_node, userpd_np) {
++              ret = q6_register_userpd(pdev, userpd_np);
++              if (ret) {
++                      /* release resources of successfully allocated userpd rproc's */
++                      q6_release_resources();
++                      return dev_err_probe(&pdev->dev, ret,
++                                           "Failed to register userpd(%s)\n",
++                                           userpd_np->name);
++              }
++      }
++      return 0;
++
++free_rproc:
++      rproc_free(rproc);
++
++      return ret;
++}
++
++static int q6_wcss_remove(struct platform_device *pdev)
++{
++      struct rproc *rproc = platform_get_drvdata(pdev);
++      struct q6_wcss *wcss = rproc->priv;
++
++      qcom_q6v5_deinit(&wcss->q6);
++
++      rproc_del(rproc);
++      rproc_free(rproc);
++
++      return 0;
++}
++
++static const struct wcss_data q6_ipq5332_res_init = {
++      .pasid = MPD_WCNSS_PAS_ID,
++      .share_upd_info_to_q6 = true,
++};
++
++static const struct wcss_data q6_ipq9574_res_init = {
++      .pasid = WCNSS_PAS_ID,
++};
++
++static const struct of_device_id q6_wcss_of_match[] = {
++      { .compatible = "qcom,ipq5332-q6-mpd", .data = &q6_ipq5332_res_init },
++      { .compatible = "qcom,ipq9574-q6-mpd", .data = &q6_ipq9574_res_init },
++      { },
++};
++MODULE_DEVICE_TABLE(of, q6_wcss_of_match);
++
++static struct platform_driver q6_wcss_driver = {
++      .probe = q6_wcss_probe,
++      .remove = q6_wcss_remove,
++      .driver = {
++              .name = "qcom-q6-mpd",
++              .of_match_table = q6_wcss_of_match,
++      },
++};
++module_platform_driver(q6_wcss_driver);
++
++MODULE_DESCRIPTION("Hexagon WCSS Multipd Peripheral Image Loader");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/qualcommax/patches-6.6/0806-rproc-qcom_q6v5_mpd-split-q6_wcss-to-rootpd-and-user.patch b/target/linux/qualcommax/patches-6.6/0806-rproc-qcom_q6v5_mpd-split-q6_wcss-to-rootpd-and-user.patch
new file mode 100644 (file)
index 0000000..b3cd492
--- /dev/null
@@ -0,0 +1,321 @@
+From 6c66dff196cbba8515380110dd3599cde31dd896 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:12 +0800
+Subject: [PATCH 1/2] rproc: qcom_q6v5_mpd: split q6_wcss to rootpd and
+ userpd
+
+Split the q6_wcss structure and create a separate userpd struct to clearly
+differentiate between the process to bring up the QDSP6 processor vs 
+process(es) to bring up the Wifi radio(s) (WCSS) for better readability.
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+ drivers/remoteproc/qcom_q6v5_mpd.c | 126 +++++++++++++----------------
+ 1 file changed, 56 insertions(+), 70 deletions(-)
+
+--- a/drivers/remoteproc/qcom_q6v5_mpd.c
++++ b/drivers/remoteproc/qcom_q6v5_mpd.c
+@@ -44,10 +44,6 @@
+ #define VERSION2                      2
+ static LIST_HEAD(upd_rproc_list);
+-enum {
+-      Q6_IPQ,
+-      WCSS_IPQ,
+-};
+ /**
+  * struct userpd_boot_info_header - header of user pd bootinfo
+@@ -82,10 +78,15 @@ struct q6_wcss {
+       phys_addr_t mem_reloc;
+       void *mem_region;
+       size_t mem_size;
+-      u8 pd_asid;
+       const struct wcss_data *desc;
+       const char **firmware;
+-      u32 version;
++};
++
++struct userpd {
++      u8 pd_asid;
++      struct device *dev;
++      struct qcom_rproc_ssr ssr_subdev;
++      struct qcom_q6v5 q6;
+ };
+ struct wcss_data {
+@@ -101,8 +102,8 @@ struct wcss_data {
+  */
+ static u8 qcom_get_pd_asid(struct rproc *rproc)
+ {
+-      struct q6_wcss *wcss = rproc->priv;
+-      u8 bit = wcss->q6.spawn_bit;
++      struct userpd *upd = rproc->priv;
++      u8 bit = upd->q6.spawn_bit;
+       return bit / 8;
+ }
+@@ -131,37 +132,37 @@ static int q6_wcss_start(struct rproc *r
+ static int q6_wcss_spawn_pd(struct rproc *rproc)
+ {
+       int ret;
+-      struct q6_wcss *wcss = rproc->priv;
++      struct userpd *upd = rproc->priv;
+-      ret = qcom_q6v5_request_spawn(&wcss->q6);
++      ret = qcom_q6v5_request_spawn(&upd->q6);
+       if (ret == -ETIMEDOUT) {
+-              dev_err(wcss->dev, "%s spawn timedout\n", rproc->name);
++              dev_err(upd->dev, "%s spawn timedout\n", rproc->name);
+               return ret;
+       }
+-      ret = qcom_q6v5_wait_for_start(&wcss->q6, msecs_to_jiffies(10000));
++      ret = qcom_q6v5_wait_for_start(&upd->q6, msecs_to_jiffies(10000));
+       if (ret == -ETIMEDOUT) {
+-              dev_err(wcss->dev, "%s start timedout\n", rproc->name);
+-              wcss->q6.running = false;
++              dev_err(upd->dev, "%s start timedout\n", rproc->name);
++              upd->q6.running = false;
+               return ret;
+       }
+-      wcss->q6.running = true;
++      upd->q6.running = true;
+       return ret;
+ }
+ static int wcss_pd_start(struct rproc *rproc)
+ {
+-      struct q6_wcss *wcss = rproc->priv;
+-      u32 pasid = (wcss->pd_asid << 8) | UPD_SWID;
++      struct userpd *upd = rproc->priv;
++      u32 pasid = (upd->pd_asid << 8) | UPD_SWID;
+       int ret;
+       ret = qcom_scm_msa_lock(pasid);
+       if (ret) {
+-              dev_err(wcss->dev, "failed to power up pd\n");
++              dev_err(upd->dev, "failed to power up pd\n");
+               return ret;
+       }
+-      if (wcss->q6.spawn_bit) {
++      if (upd->q6.spawn_bit) {
+               ret = q6_wcss_spawn_pd(rproc);
+               if (ret)
+                       return ret;
+@@ -213,22 +214,22 @@ static int q6_wcss_stop(struct rproc *rp
+  */
+ static int wcss_pd_stop(struct rproc *rproc)
+ {
+-      struct q6_wcss *wcss = rproc->priv;
+-      struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent);
+-      u32 pasid = (wcss->pd_asid << 8) | UPD_SWID;
++      struct userpd *upd = rproc->priv;
++      struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent);
++      u32 pasid = (upd->pd_asid << 8) | UPD_SWID;
+       int ret;
+-      if (rproc->state != RPROC_CRASHED && wcss->q6.stop_bit) {
+-              ret = qcom_q6v5_request_stop(&wcss->q6, NULL);
++      if (rproc->state != RPROC_CRASHED && upd->q6.stop_bit) {
++              ret = qcom_q6v5_request_stop(&upd->q6, NULL);
+               if (ret) {
+-                      dev_err(&rproc->dev, "pd not stopped\n");
++                      dev_err(upd->dev, "pd not stopped\n");
+                       return ret;
+               }
+       }
+       ret = qcom_scm_msa_unlock(pasid);
+       if (ret) {
+-              dev_err(wcss->dev, "failed to power down pd\n");
++              dev_err(upd->dev, "failed to power down pd\n");
+               return ret;
+       }
+@@ -273,7 +274,8 @@ static int share_upd_bootinfo_to_q6(stru
+       size_t size;
+       u16 cnt = 0, version;
+       void *ptr;
+-      struct q6_wcss *wcss = rproc->priv, *upd_wcss;
++      struct q6_wcss *wcss = rproc->priv;
++      struct userpd *upd;
+       struct rproc *upd_rproc;
+       struct userpd_boot_info upd_bootinfo = {0};
+       const struct firmware *fw;
+@@ -308,7 +310,7 @@ static int share_upd_bootinfo_to_q6(stru
+       ptr += sizeof(u16);
+       list_for_each_entry(upd_rproc, &upd_rproc_list, node) {
+-              upd_wcss = upd_rproc->priv;
++              upd = upd_rproc->priv;
+               /* TYPE */
+               upd_bootinfo.header.type = UPD_BOOT_INFO_HEADER_TYPE;
+@@ -318,11 +320,11 @@ static int share_upd_bootinfo_to_q6(stru
+                       sizeof(upd_bootinfo) - sizeof(upd_bootinfo.header);
+               /* Process ID */
+-              upd_bootinfo.pid = upd_wcss->pd_asid + 1;
++              upd_bootinfo.pid = upd->pd_asid + 1;
+-              ret = request_firmware(&fw, upd_rproc->firmware, upd_wcss->dev);
++              ret = request_firmware(&fw, upd_rproc->firmware, upd->dev);
+               if (ret < 0) {
+-                      dev_err(upd_wcss->dev, "request_firmware failed: %d\n", ret);
++                      dev_err(upd->dev, "request_firmware failed: %d\n",      ret);
+                       return ret;
+               }
+@@ -421,19 +423,20 @@ static int q6_wcss_load(struct rproc *rp
+  */
+ static int wcss_pd_load(struct rproc *rproc, const struct firmware *fw)
+ {
+-      struct q6_wcss *wcss = rproc->priv;
+-      struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent);
+-      u32 pasid = (wcss->pd_asid << 8) | UPD_SWID;
++      struct userpd *upd = rproc->priv;
++      struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent);
++      struct q6_wcss *wcss = rpd_rproc->priv;
++      u32 pasid = (upd->pd_asid << 8) | UPD_SWID;
+       int ret;
+       ret = rproc_boot(rpd_rproc);
+       if (ret)
+               return ret;
+-      return qcom_mdt_load(wcss->dev, fw, rproc->firmware,
++      return qcom_mdt_load(upd->dev, fw, rproc->firmware,
+                            pasid, wcss->mem_region,
+                            wcss->mem_phys, wcss->mem_size,
+-                           &wcss->mem_reloc);
++                           NULL);
+ }
+ static unsigned long q6_wcss_panic(struct rproc *rproc)
+@@ -465,26 +468,15 @@ static int q6_alloc_memory_region(struct
+       struct device_node *node;
+       struct device *dev = wcss->dev;
+-      if (wcss->version == Q6_IPQ) {
+-              node = of_parse_phandle(dev->of_node, "memory-region", 0);
+-              if (node)
+-                      rmem = of_reserved_mem_lookup(node);
+-
+-              of_node_put(node);
+-
+-              if (!rmem) {
+-                      dev_err(dev, "unable to acquire memory-region\n");
+-                      return -EINVAL;
+-              }
+-      } else {
+-              struct rproc *rpd_rproc = dev_get_drvdata(dev->parent);
+-              struct q6_wcss *rpd_wcss = rpd_rproc->priv;
+-
+-              wcss->mem_phys = rpd_wcss->mem_phys;
+-              wcss->mem_reloc = rpd_wcss->mem_reloc;
+-              wcss->mem_size = rpd_wcss->mem_size;
+-              wcss->mem_region = rpd_wcss->mem_region;
+-              return 0;
++      node = of_parse_phandle(dev->of_node, "memory-region", 0);
++      if (node)
++              rmem = of_reserved_mem_lookup(node);
++
++      of_node_put(node);
++
++      if (!rmem) {
++              dev_err(dev, "unable to acquire memory-region\n");
++              return -EINVAL;
+       }
+       wcss->mem_phys = rmem->base;
+@@ -508,7 +500,7 @@ static int q6_get_inbound_irq(struct qco
+ {
+       int ret, irq;
+       char *interrupt, *tmp = (char *)int_name;
+-      struct q6_wcss *wcss = q6->rproc->priv;
++      struct userpd *upd = q6->rproc->priv;
+       irq = platform_get_irq(pdev, index);
+       if (irq < 0)
+@@ -520,7 +512,7 @@ static int q6_get_inbound_irq(struct qco
+       if (!interrupt)
+               return -ENOMEM;
+-      snprintf(interrupt, BUF_SIZE, "q6v5_wcss_userpd%d_%s", wcss->pd_asid, tmp);
++      snprintf(interrupt, BUF_SIZE, "q6v5_wcss_userpd%d_%s", upd->pd_asid, tmp);
+       ret = devm_request_threaded_irq(&pdev->dev, *pirq,
+                                       NULL, handler,
+@@ -561,7 +553,7 @@ static int init_irq(struct qcom_q6v5 *q6
+                   void (*handover)(struct qcom_q6v5 *q6))
+ {
+       int ret;
+-      struct q6_wcss *wcss = rproc->priv;
++      struct userpd *upd = rproc->priv;
+       q6->rproc = rproc;
+       q6->dev = &pdev->dev;
+@@ -581,7 +573,7 @@ static int init_irq(struct qcom_q6v5 *q6
+               return ret;
+       /* Get pd_asid to prepare interrupt names */
+-      wcss->pd_asid = qcom_get_pd_asid(rproc);
++      upd->pd_asid = qcom_get_pd_asid(rproc);
+       ret = q6_get_inbound_irq(q6, pdev, "fatal", 0, &q6->fatal_irq,
+                                q6v5_fatal_interrupt);
+@@ -619,7 +611,7 @@ static void q6_release_resources(void)
+ static int q6_register_userpd(struct platform_device *pdev,
+                             struct device_node *userpd_np)
+ {
+-      struct q6_wcss *wcss;
++      struct userpd *upd;
+       struct rproc *rproc = NULL;
+       int ret;
+       struct platform_device *userpd_pdev;
+@@ -652,21 +644,16 @@ static int q6_register_userpd(struct pla
+       userpd_pdev->dev.driver = pdev->dev.driver;
+       rproc = rproc_alloc(&userpd_pdev->dev, userpd_pdev->name, &wcss_ops,
+-                          firmware_name, sizeof(*wcss));
++                          firmware_name, sizeof(*upd));
+       if (!rproc) {
+               ret = -ENOMEM;
+               goto free_rproc;
+       }
+-      wcss = rproc->priv;
+-      wcss->dev = &userpd_pdev->dev;
+-      wcss->version = WCSS_IPQ;
+-
+-      ret = q6_alloc_memory_region(wcss);
+-      if (ret)
+-              goto free_rproc;
++      upd = rproc->priv;
++      upd->dev = &userpd_pdev->dev;
+-      ret = init_irq(&wcss->q6, userpd_pdev, rproc,
++      ret = init_irq(&upd->q6, userpd_pdev, rproc,
+                      WCSS_CRASH_REASON, NULL, NULL);
+       if (ret)
+               goto free_rproc;
+@@ -678,7 +665,7 @@ static int q6_register_userpd(struct pla
+       list_add(&rproc->node, &upd_rproc_list);
+       platform_set_drvdata(userpd_pdev, rproc);
+-      qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, userpd_pdev->name);
++      qcom_add_ssr_subdev(rproc, &upd->ssr_subdev, userpd_pdev->name);
+       return 0;
+ free_rproc:
+@@ -719,7 +706,6 @@ static int q6_wcss_probe(struct platform
+       wcss->dev = &pdev->dev;
+       wcss->desc = desc;
+       wcss->firmware = firmware;
+-      wcss->version = Q6_IPQ;
+       ret = q6_alloc_memory_region(wcss);
+       if (ret)
diff --git a/target/linux/qualcommax/patches-6.6/0807-remoteproc-qcom_q6v5_mpd-fix-incorrent-use-of-rproc-.patch b/target/linux/qualcommax/patches-6.6/0807-remoteproc-qcom_q6v5_mpd-fix-incorrent-use-of-rproc-.patch
new file mode 100644 (file)
index 0000000..59f49ad
--- /dev/null
@@ -0,0 +1,206 @@
+From 0fa7bdb855247b738d1d227d6f4b3417ebdf21a8 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:12 +0800
+Subject: [PATCH 2/2] remoteproc: qcom_q6v5_mpd: fix incorrent use of
+ rproc->node
+
+    1.817524] list_add corruption. next->prev should be prev (ffffffc0814bbfc8), but was ffffffc0814bc358. (next=ffffff8003b56800).
+[    1.822435] WARNING: CPU: 1 PID: 24 at lib/list_debug.c:29 __list_add_valid_or_report+0x8c/0xdc
+[    1.833923] Modules linked in:
+[    1.842425] CPU: 1 PID: 24 Comm: kworker/u4:1 Not tainted 6.6.47 #0
+[    1.845552] Hardware name: Qualcomm MP03 (DT)
+[    1.851716] Workqueue: events_unbound deferred_probe_work_func
+[    1.856229] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
+[    1.861959] pc : __list_add_valid_or_report+0x8c/0xdc
+[    1.868816] lr : __list_add_valid_or_report+0x8c/0xdc
+[    1.874022] sp : ffffffc081603a50
+[    1.879055] x29: ffffffc081603a50 x28: ffffff8000fa2810 x27: ffffff8003cba800
+[    1.882358] x26: ffffff8000fa2800 x25: ffffff8003cbac80 x24: 0000000000000000
+[    1.889476] x23: ffffffc08088b968 x22: ffffffc0814bb000 x21: ffffffc0814bbfc8
+[    1.896593] x20: ffffffc08088b8e8 x19: ffffff8003cba800 x18: 00000000000000b1
+[    1.903713] x17: 3863666262343138 x16: 3063666666666666 x15: ffffffc081416e20
+[    1.910830] x14: 0000000000000213 x13: 00000000000000b1 x12: 00000000ffffffea
+[    1.917948] x11: 00000000ffffefff x10: ffffffc08146ee20 x9 : ffffffc081416dc8
+[    1.925066] x8 : 0000000000017fe8 x7 : c0000000ffffefff x6 : 0000000000057fa8
+[    1.932184] x5 : 0000000000000fff x4 : 0000000000000000 x3 : ffffffc081603850
+[    1.939302] x2 : ffffffc081416d60 x1 : ffffffc081416d60 x0 : 0000000000000075
+[    1.946422] Call trace:
+[    1.953535]  __list_add_valid_or_report+0x8c/0xdc
+[    1.955793]  rproc_add+0x1f4/0x25c
+[    1.960653]  q6_wcss_probe+0x510/0x634
+[    1.963950]  platform_probe+0x68/0xc4
+[    1.967684]  really_probe+0x148/0x2b0
+[    1.971417]  __driver_probe_device+0x78/0x128
+[    1.975063]  driver_probe_device+0x40/0xdc
+[    1.979402]  __device_attach_driver+0xb8/0xf8
+[    1.983397]  bus_for_each_drv+0x70/0xb8
+[    1.987823]  __device_attach+0xa0/0x184
+[    1.991468]  device_initial_probe+0x14/0x20
+[    1.995289]  bus_probe_device+0xac/0xb0
+[    1.999455]  deferred_probe_work_func+0xa4/0xec
+[    2.003275]  process_one_work+0x178/0x2d4
+[    2.007788]  worker_thread+0x2ec/0x4d8
+[    2.011954]  kthread+0xdc/0xe0
+[    2.015600]  ret_from_fork+0x10/0x20
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+---
+ drivers/remoteproc/qcom_q6v5_mpd.c | 53 +++++++++++++++++-------------
+ 1 file changed, 30 insertions(+), 23 deletions(-)
+
+--- a/drivers/remoteproc/qcom_q6v5_mpd.c
++++ b/drivers/remoteproc/qcom_q6v5_mpd.c
+@@ -33,6 +33,7 @@
+ #define BUF_SIZE                      35
++#define MAX_UPD                               3
+ #define MAX_FIRMWARE                  3
+ #define RPD_SWID              MPD_WCNSS_PAS_ID
+@@ -43,8 +44,6 @@
+ #define UPD_BOOT_INFO_SMEM_ID         507
+ #define VERSION2                      2
+-static LIST_HEAD(upd_rproc_list);
+-
+ /**
+  * struct userpd_boot_info_header - header of user pd bootinfo
+  * @type:             type of bootinfo passing over smem
+@@ -80,6 +79,7 @@ struct q6_wcss {
+       size_t mem_size;
+       const struct wcss_data *desc;
+       const char **firmware;
++      struct userpd *upd[MAX_UPD];
+ };
+ struct userpd {
+@@ -270,13 +270,12 @@ static void *q6_wcss_da_to_va(struct rpr
+  */
+ static int share_upd_bootinfo_to_q6(struct rproc *rproc)
+ {
+-      int ret;
++      int i, ret;
+       size_t size;
+       u16 cnt = 0, version;
+       void *ptr;
+       struct q6_wcss *wcss = rproc->priv;
+       struct userpd *upd;
+-      struct rproc *upd_rproc;
+       struct userpd_boot_info upd_bootinfo = {0};
+       const struct firmware *fw;
+@@ -301,16 +300,19 @@ static int share_upd_bootinfo_to_q6(stru
+       memcpy_toio(ptr, &version, sizeof(version));
+       ptr += sizeof(version);
+-      list_for_each_entry(upd_rproc, &upd_rproc_list, node)
+-              cnt++;
++      for (i = 0; i < ARRAY_SIZE(wcss->upd); i++)
++              if (wcss->upd[i])
++                      cnt++;
+       /* No of elements */
+       cnt = (sizeof(upd_bootinfo) * cnt);
+       memcpy_toio(ptr, &cnt, sizeof(u16));
+       ptr += sizeof(u16);
+-      list_for_each_entry(upd_rproc, &upd_rproc_list, node) {
+-              upd = upd_rproc->priv;
++      for (i = 0; i < ARRAY_SIZE(wcss->upd); i++) {
++              upd = wcss->upd[i];
++              if (!upd)
++                      continue;
+               /* TYPE */
+               upd_bootinfo.header.type = UPD_BOOT_INFO_HEADER_TYPE;
+@@ -322,14 +324,14 @@ static int share_upd_bootinfo_to_q6(stru
+               /* Process ID */
+               upd_bootinfo.pid = upd->pd_asid + 1;
+-              ret = request_firmware(&fw, upd_rproc->firmware, upd->dev);
++              ret = request_firmware(&fw, upd->q6.rproc->firmware, upd->dev);
+               if (ret < 0) {
+                       dev_err(upd->dev, "request_firmware failed: %d\n",      ret);
+                       return ret;
+               }
+               /* Load address */
+-              upd_bootinfo.bootaddr = rproc_get_boot_addr(upd_rproc, fw);
++              upd_bootinfo.bootaddr = rproc_get_boot_addr(upd->q6.rproc, fw);
+               /* Firmware mem size */
+               upd_bootinfo.data_size = qcom_mdt_get_size(fw);
+@@ -597,18 +599,23 @@ static int init_irq(struct qcom_q6v5 *q6
+       return 0;
+ }
+-static void q6_release_resources(void)
++static void q6_release_resources(struct q6_wcss *wcss)
+ {
+-      struct rproc *upd_rproc;
++      struct userpd *upd;
++      int i;
+       /* Release userpd resources */
+-      list_for_each_entry(upd_rproc, &upd_rproc_list, node) {
+-              rproc_del(upd_rproc);
+-              rproc_free(upd_rproc);
++      for (i = 0; i < ARRAY_SIZE(wcss->upd); i++) {
++              upd = wcss->upd[i];
++              if (!upd)
++                      continue;
++
++              rproc_del(upd->q6.rproc);
++              rproc_free(upd->q6.rproc);
+       }
+ }
+-static int q6_register_userpd(struct platform_device *pdev,
++static int q6_register_userpd(struct q6_wcss *wcss,
+                             struct device_node *userpd_np)
+ {
+       struct userpd *upd;
+@@ -633,16 +640,16 @@ static int q6_register_userpd(struct pla
+               return ret;
+       }
+-      dev_info(&pdev->dev, "%s node found\n", userpd_np->name);
++      dev_info(wcss->dev, "%s node found\n", userpd_np->name);
+       userpd_pdev = of_platform_device_create(userpd_np, userpd_np->name,
+-                                              &pdev->dev);
++                                              wcss->dev);
+       if (!userpd_pdev)
+-              return dev_err_probe(&pdev->dev, -ENODEV,
++              return dev_err_probe(wcss->dev, -ENODEV,
+                                    "failed to create %s platform device\n",
+                                    userpd_np->name);
+-      userpd_pdev->dev.driver = pdev->dev.driver;
++      userpd_pdev->dev.driver = wcss->dev->driver;
+       rproc = rproc_alloc(&userpd_pdev->dev, userpd_pdev->name, &wcss_ops,
+                           firmware_name, sizeof(*upd));
+       if (!rproc) {
+@@ -663,7 +670,7 @@ static int q6_register_userpd(struct pla
+       if (ret)
+               goto free_rproc;
+-      list_add(&rproc->node, &upd_rproc_list);
++      wcss->upd[upd->pd_asid] = upd;
+       platform_set_drvdata(userpd_pdev, rproc);
+       qcom_add_ssr_subdev(rproc, &upd->ssr_subdev, userpd_pdev->name);
+       return 0;
+@@ -728,10 +735,10 @@ static int q6_wcss_probe(struct platform
+       /* Iterate over userpd child's and register with rproc */
+       for_each_available_child_of_node(pdev->dev.of_node, userpd_np) {
+-              ret = q6_register_userpd(pdev, userpd_np);
++              ret = q6_register_userpd(wcss, userpd_np);
+               if (ret) {
+                       /* release resources of successfully allocated userpd rproc's */
+-                      q6_release_resources();
++                      q6_release_resources(wcss);
+                       return dev_err_probe(&pdev->dev, ret,
+                                            "Failed to register userpd(%s)\n",
+                                            userpd_np->name);
diff --git a/target/linux/qualcommax/patches-6.6/0811-firmware-qcom_scm-support-MPD.patch b/target/linux/qualcommax/patches-6.6/0811-firmware-qcom_scm-support-MPD.patch
new file mode 100644 (file)
index 0000000..afcc15e
--- /dev/null
@@ -0,0 +1,128 @@
+From 6553d598cdb507f7ede020f25da646ba084a23c6 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:12 +0800
+Subject: [PATCH] firmware: qcom_scm: support MPD
+
+Add SCM calls to power up / down the SoC's internal WiFi radio and to 
+load PIL segments to support the MPD architecture.
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+ drivers/firmware/qcom_scm.c            | 79 ++++++++++++++++++++++++++
+ drivers/firmware/qcom_scm.h            |  3 +
+ include/linux/firmware/qcom/qcom_scm.h |  3 +
+ 3 files changed, 85 insertions(+)
+
+--- a/drivers/firmware/qcom_scm.c
++++ b/drivers/firmware/qcom_scm.c
+@@ -713,6 +713,85 @@ bool qcom_scm_pas_supported(u32 peripher
+ EXPORT_SYMBOL_GPL(qcom_scm_pas_supported);
+ /**
++ * qcom_scm_internal_wifi_powerup() - Bring up internal wifi
++ * @peripheral:       peripheral id
++ *
++ * Return 0 on success.
++ */
++int qcom_scm_internal_wifi_powerup(u32 peripheral)
++{
++      struct qcom_scm_desc desc = {
++              .svc = QCOM_SCM_SVC_PIL,
++              .cmd = QCOM_SCM_INTERNAL_WIFI_POWERUP,
++              .arginfo = QCOM_SCM_ARGS(1),
++              .args[0] = peripheral,
++              .owner = ARM_SMCCC_OWNER_SIP,
++      };
++      struct qcom_scm_res res;
++      int ret;
++
++      ret = qcom_scm_call(__scm->dev, &desc, &res);
++
++      return ret ? : res.result[0];
++}
++EXPORT_SYMBOL(qcom_scm_internal_wifi_powerup);
++
++/**
++ * qcom_scm_internal_wifi_shutdown() - Shut down internal wifi
++ * @peripheral: peripheral id
++ *
++ * Returns 0 on success.
++ */
++int qcom_scm_internal_wifi_shutdown(u32 peripheral)
++{
++      struct qcom_scm_desc desc = {
++              .svc = QCOM_SCM_SVC_PIL,
++              .cmd = QCOM_SCM_INTERNAL_WIFI_SHUTDOWN,
++              .arginfo = QCOM_SCM_ARGS(1),
++              .args[0] = peripheral,
++              .owner = ARM_SMCCC_OWNER_SIP,
++      };
++      struct qcom_scm_res res;
++      int ret;
++
++      ret = qcom_scm_call(__scm->dev, &desc, &res);
++
++      return ret ? : res.result[0];
++}
++EXPORT_SYMBOL(qcom_scm_internal_wifi_shutdown);
++
++/**
++ * qcom_scm_pas_load_segment() - copy userpd PIL segments data to dma blocks
++ * @peripheral:               peripheral id
++ * @segment:          segment id
++ * @dma:              handle of dma region
++ * @seg_cnt:          no of dma blocks
++ *
++ * Returns 0 if trustzone successfully loads userpd PIL segments from dma
++ * blocks to DDR
++ */
++int qcom_scm_pas_load_segment(u32 peripheral, int segment, dma_addr_t dma, int seg_cnt)
++{
++      struct qcom_scm_desc desc = {
++              .svc = QCOM_SCM_SVC_PIL,
++              .cmd = QCOM_SCM_PIL_PAS_LOAD_SEG,
++              .arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_VAL, QCOM_SCM_VAL, QCOM_SCM_RW, QCOM_SCM_VAL),
++              .args[0] = peripheral,
++              .args[1] = segment,
++              .args[2] = dma,
++              .args[3] = seg_cnt,
++              .owner = ARM_SMCCC_OWNER_SIP,
++      };
++      struct qcom_scm_res res;
++      int ret;
++
++      ret = qcom_scm_call(__scm->dev, &desc, &res);
++
++      return ret ? : res.result[0];
++}
++EXPORT_SYMBOL(qcom_scm_pas_load_segment);
++
++/**
+  * qcom_scm_msa_lock() - Lock given peripheral firmware region as MSA
+  *
+  * @peripheral:       peripheral id
+--- a/drivers/firmware/qcom_scm.h
++++ b/drivers/firmware/qcom_scm.h
+@@ -98,6 +98,9 @@ extern int scm_legacy_call(struct device
+ #define QCOM_SCM_PIL_PAS_SHUTDOWN     0x06
+ #define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07
+ #define QCOM_SCM_PIL_PAS_MSS_RESET    0x0a
++#define QCOM_SCM_INTERNAL_WIFI_POWERUP        0x17
++#define QCOM_SCM_INTERNAL_WIFI_SHUTDOWN       0x18
++#define QCOM_SCM_PIL_PAS_LOAD_SEG     0x19
+ #define QCOM_SCM_MSA_LOCK             0x24
+ #define QCOM_SCM_MSA_UNLOCK           0x25
+--- a/include/linux/firmware/qcom/qcom_scm.h
++++ b/include/linux/firmware/qcom/qcom_scm.h
+@@ -81,6 +81,9 @@ extern int qcom_scm_pas_mem_setup(u32 pe
+ extern int qcom_scm_pas_auth_and_reset(u32 peripheral);
+ extern int qcom_scm_pas_shutdown(u32 peripheral);
+ extern bool qcom_scm_pas_supported(u32 peripheral);
++extern int qcom_scm_internal_wifi_powerup(u32 peripheral);
++extern int qcom_scm_internal_wifi_shutdown(u32 peripheral);
++extern int qcom_scm_pas_load_segment(u32 peripheral, int segment, dma_addr_t dma, int seg_cnt);
+ extern int qcom_scm_msa_lock(u32 peripheral);
+ extern int qcom_scm_msa_unlock(u32 peripheral);
diff --git a/target/linux/qualcommax/patches-6.6/0812-soc-qcom-mdt_loader-support-MPD.patch b/target/linux/qualcommax/patches-6.6/0812-soc-qcom-mdt_loader-support-MPD.patch
new file mode 100644 (file)
index 0000000..935a943
--- /dev/null
@@ -0,0 +1,199 @@
+From bf42d84868bc82a9cb334a33930f2d1da24f7070 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:12 +0800
+Subject: [PATCH] soc: qcom: mdt_loader: support MPD
+
+Add support for loading user PD specific PIL segments as required by the 
+MPD architecture.
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+ drivers/soc/qcom/mdt_loader.c       | 110 ++++++++++++++++++++++++++--
+ include/linux/soc/qcom/mdt_loader.h |   5 ++
+ 2 files changed, 110 insertions(+), 5 deletions(-)
+
+--- a/drivers/soc/qcom/mdt_loader.c
++++ b/drivers/soc/qcom/mdt_loader.c
+@@ -16,6 +16,16 @@
+ #include <linux/sizes.h>
+ #include <linux/slab.h>
+ #include <linux/soc/qcom/mdt_loader.h>
++#include <linux/dma-mapping.h>
++
++#include "../../remoteproc/qcom_common.h"
++
++#define QCOM_MDT_PF_ASID_MASK GENMASK(19, 16)
++
++struct segment_load_args {
++      __le64 addr;
++      __le64 blk_size;
++};
+ static bool mdt_phdr_valid(const struct elf32_phdr *phdr)
+ {
+@@ -69,6 +79,56 @@ static ssize_t mdt_load_split_segment(vo
+       return ret;
+ }
++static int mdt_load_split_segment_dma(int pas_id, unsigned int segment,
++                                    const struct elf32_phdr *phdrs,
++                                    const char *fw_name,
++                                    struct device *dev)
++{
++      const struct elf32_phdr *phdr = &phdrs[segment];
++      struct segment_load_args *args;
++      dma_addr_t *addrs;
++      void *ptr;
++      dma_addr_t dma_args, dma_addrs, dma_ptr;
++      int ret;
++
++      args = dma_alloc_coherent(dev, sizeof(*args) + sizeof(*addrs), &dma_args, GFP_DMA);
++      if (!args) {
++              dev_err(dev, "Error in dma alloc regin: %ld\n", sizeof(*args));
++              return -ENOMEM;
++      }
++
++      addrs = (void *) args + sizeof(*args);
++      dma_addrs = dma_args + sizeof(*args);
++
++      ptr = dma_alloc_coherent(dev, phdr->p_filesz, &dma_ptr, GFP_DMA);
++      if (!ptr) {
++              dev_err(dev, "Error in dma alloc ptr: %d\n", phdr->p_filesz);
++              return -ENOMEM;
++      }
++
++      args->addr = dma_addrs;
++      args->blk_size = phdr->p_filesz;
++
++      addrs[0] = dma_ptr;
++
++      ret = mdt_load_split_segment(ptr, phdrs, segment, fw_name, dev);
++      if (ret < 0) {
++              dev_err(dev, "Error in mdt_load_split_segment: %d\n", ret);
++              return ret;
++      }
++
++      ret = qcom_scm_pas_load_segment(pas_id, segment, dma_args, 1);
++      if (ret < 0) {
++              dev_err(dev, "Error in qcom_scm_pas_load_segment: %d\n", ret);
++              return ret;
++      }
++
++      dma_free_coherent(dev, phdr->p_filesz, ptr, dma_ptr);
++      dma_free_coherent(dev, sizeof(*args) + sizeof(*addrs), args, dma_args);
++
++      return 0;
++}
++
+ /**
+  * qcom_mdt_get_size() - acquire size of the memory region needed to load mdt
+  * @fw:               firmware object for the mdt file
+@@ -295,7 +355,8 @@ static bool qcom_mdt_bins_are_split(cons
+ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
+                          const char *fw_name, int pas_id, void *mem_region,
+                          phys_addr_t mem_phys, size_t mem_size,
+-                         phys_addr_t *reloc_base, bool pas_init)
++                         phys_addr_t *reloc_base, bool pas_init,
++                         bool dma_require, int pd_asid)
+ {
+       const struct elf32_phdr *phdrs;
+       const struct elf32_phdr *phdr;
+@@ -349,6 +410,14 @@ static int __qcom_mdt_load(struct device
+               if (!mdt_phdr_valid(phdr))
+                       continue;
++              /*
++               * While doing PD specific reloading, load only that PD
++               * specific writeable entries. Skip others
++               */
++              if (pd_asid && (FIELD_GET(QCOM_MDT_PF_ASID_MASK, phdr->p_flags) != pd_asid ||
++                              (phdr->p_flags & PF_W) == 0))
++                      continue;
++
+               offset = phdr->p_paddr - mem_reloc;
+               if (offset < 0 || offset + phdr->p_memsz > mem_size) {
+                       dev_err(dev, "segment outside memory range\n");
+@@ -366,7 +435,11 @@ static int __qcom_mdt_load(struct device
+               ptr = mem_region + offset;
+-              if (phdr->p_filesz && !is_split) {
++              if (dma_require && phdr->p_filesz) {
++                      ret = mdt_load_split_segment_dma(pas_id, i, phdrs, fw_name, dev);
++                      if (ret)
++                              break;
++              } else if (phdr->p_filesz && !is_split) {
+                       /* Firmware is large enough to be non-split */
+                       if (phdr->p_offset + phdr->p_filesz > fw->size) {
+                               dev_err(dev, "file %s segment %d would be truncated\n",
+@@ -383,7 +456,7 @@ static int __qcom_mdt_load(struct device
+                               break;
+               }
+-              if (phdr->p_memsz > phdr->p_filesz)
++              if (!dma_require && phdr->p_memsz > phdr->p_filesz)
+                       memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);
+       }
+@@ -418,7 +491,7 @@ int qcom_mdt_load(struct device *dev, co
+               return ret;
+       return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys,
+-                             mem_size, reloc_base, true);
++                             mem_size, reloc_base, true, false, 0);
+ }
+ EXPORT_SYMBOL_GPL(qcom_mdt_load);
+@@ -441,9 +514,36 @@ int qcom_mdt_load_no_init(struct device
+                         size_t mem_size, phys_addr_t *reloc_base)
+ {
+       return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys,
+-                             mem_size, reloc_base, false);
++                             mem_size, reloc_base, false, false, 0);
+ }
+ EXPORT_SYMBOL_GPL(qcom_mdt_load_no_init);
++/**
++ * qcom_mdt_load_pd_seg() - load userpd specific PIL segements
++ * @dev:      device handle to associate resources with
++ * @fw:               firmware object for the mdt file
++ * @firmware: name of the firmware, for construction of segment file names
++ * @pas_id:   PAS identifier
++ * @mem_region:       allocated memory region to load firmware into
++ * @mem_phys: physical address of allocated memory region
++ * @mem_size: size of the allocated memory region
++ * @reloc_base:       adjusted physical address after relocation
++ *
++ * Here userpd PIL segements are stitched with rootpd firmware.
++ * This function reloads userpd specific PIL segments during SSR
++ * of userpd.
++ *
++ * Returns 0 on success, negative errno otherwise.
++ */
++int qcom_mdt_load_pd_seg(struct device *dev, const struct firmware *fw,
++                       const char *firmware, int pas_id, int pd_asid, void *mem_region,
++                       phys_addr_t mem_phys, size_t mem_size,
++                       phys_addr_t *reloc_base)
++{
++      return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys,
++                             mem_size, reloc_base, false, true, pd_asid);
++}
++EXPORT_SYMBOL_GPL(qcom_mdt_load_pd_seg);
++
+ MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format");
+ MODULE_LICENSE("GPL v2");
+--- a/include/linux/soc/qcom/mdt_loader.h
++++ b/include/linux/soc/qcom/mdt_loader.h
+@@ -30,6 +30,11 @@ int qcom_mdt_load_no_init(struct device
+ void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len,
+                            const char *fw_name, struct device *dev);
++int qcom_mdt_load_pd_seg(struct device *dev, const struct firmware *fw,
++                       const char *firmware, int pas_id, int pd_asid, void *mem_region,
++                       phys_addr_t mem_phys, size_t mem_size,
++                       phys_addr_t *reloc_base);
++
+ #else /* !IS_ENABLED(CONFIG_QCOM_MDT_LOADER) */
+ static inline ssize_t qcom_mdt_get_size(const struct firmware *fw)
diff --git a/target/linux/qualcommax/patches-6.6/0813-remoteproc-qcom_q6v5_mpd-enable-clocks.patch b/target/linux/qualcommax/patches-6.6/0813-remoteproc-qcom_q6v5_mpd-enable-clocks.patch
new file mode 100644 (file)
index 0000000..f549580
--- /dev/null
@@ -0,0 +1,38 @@
+From e83215d5d22946885fa388d375b12f1b991a43c1 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:12 +0800
+Subject: [PATCH 3/5] remoteproc: qcom_q6v5_mpd: enable clocks
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+---
+ drivers/remoteproc/qcom_q6v5_mpd.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/drivers/remoteproc/qcom_q6v5_mpd.c
++++ b/drivers/remoteproc/qcom_q6v5_mpd.c
+@@ -77,6 +77,8 @@ struct q6_wcss {
+       phys_addr_t mem_reloc;
+       void *mem_region;
+       size_t mem_size;
++      struct clk_bulk_data *clks;
++      int num_clks;
+       const struct wcss_data *desc;
+       const char **firmware;
+       struct userpd *upd[MAX_UPD];
+@@ -718,6 +720,16 @@ static int q6_wcss_probe(struct platform
+       if (ret)
+               goto free_rproc;
++      wcss->num_clks = devm_clk_bulk_get_all(wcss->dev, &wcss->clks);
++      if (wcss->num_clks < 0)
++              return dev_err_probe(wcss->dev, wcss->num_clks,
++                                   "failed to acquire clocks\n");
++
++      ret = clk_bulk_prepare_enable(wcss->num_clks, wcss->clks);
++      if (ret)
++              return dev_err_probe(wcss->dev, ret,
++                                   "failed to enable clocks\n");
++
+       ret = qcom_q6v5_init(&wcss->q6, pdev, rproc,
+                            WCSS_CRASH_REASON, NULL, NULL);
+       if (ret)
diff --git a/target/linux/qualcommax/patches-6.6/0814-remoteproc-qcom_q6v5_mpd-support-ipq5018.patch b/target/linux/qualcommax/patches-6.6/0814-remoteproc-qcom_q6v5_mpd-support-ipq5018.patch
new file mode 100644 (file)
index 0000000..9316458
--- /dev/null
@@ -0,0 +1,111 @@
+From 4ae334127f073aa5f7c9209c9f0a17fd9e331db1 Mon Sep 17 00:00:00 2001
+From: Ziyang Huang <hzyitc@outlook.com>
+Date: Sun, 8 Sep 2024 16:40:12 +0800
+Subject: [PATCH] remoteproc: qcom_q6v5_mpd: support ipq5018
+
+Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
+---
+ drivers/remoteproc/qcom_q6v5_mpd.c | 37 +++++++++++++++++++++++++++---
+ 1 file changed, 34 insertions(+), 3 deletions(-)
+
+--- a/drivers/remoteproc/qcom_q6v5_mpd.c
++++ b/drivers/remoteproc/qcom_q6v5_mpd.c
+@@ -155,6 +155,8 @@ static int q6_wcss_spawn_pd(struct rproc
+ static int wcss_pd_start(struct rproc *rproc)
+ {
+       struct userpd *upd = rproc->priv;
++      struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent);
++      struct q6_wcss *wcss = rpd_rproc->priv;
+       u32 pasid = (upd->pd_asid << 8) | UPD_SWID;
+       int ret;
+@@ -170,6 +172,14 @@ static int wcss_pd_start(struct rproc *r
+                       return ret;
+       }
++      if (upd->pd_asid == 1) {
++              ret = qcom_scm_internal_wifi_powerup(wcss->desc->pasid);
++              if (ret) {
++                      dev_err(upd->dev, "failed to power up internal radio\n");
++                      return ret;
++              }
++      }
++
+       return ret;
+ }
+@@ -179,6 +189,12 @@ static int q6_wcss_stop(struct rproc *rp
+       const struct wcss_data *desc = wcss->desc;
+       int ret;
++      ret = qcom_q6v5_request_stop(&wcss->q6, NULL);
++      if (ret) {
++              dev_err(wcss->dev, "pd not stopped\n");
++              return ret;
++      }
++
+       ret = qcom_scm_pas_shutdown(desc->pasid);
+       if (ret) {
+               dev_err(wcss->dev, "not able to shutdown\n");
+@@ -218,6 +234,7 @@ static int wcss_pd_stop(struct rproc *rp
+ {
+       struct userpd *upd = rproc->priv;
+       struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent);
++      struct q6_wcss *wcss = rpd_rproc->priv;
+       u32 pasid = (upd->pd_asid << 8) | UPD_SWID;
+       int ret;
+@@ -229,6 +246,14 @@ static int wcss_pd_stop(struct rproc *rp
+               }
+       }
++      if (upd->pd_asid == 1) {
++              ret = qcom_scm_internal_wifi_shutdown(wcss->desc->pasid);
++              if (ret) {
++                      dev_err(upd->dev, "failed to power down internal radio\n");
++                      return ret;
++              }
++      }
++
+       ret = qcom_scm_msa_unlock(pasid);
+       if (ret) {
+               dev_err(upd->dev, "failed to power down pd\n");
+@@ -430,15 +455,14 @@ static int wcss_pd_load(struct rproc *rp
+       struct userpd *upd = rproc->priv;
+       struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent);
+       struct q6_wcss *wcss = rpd_rproc->priv;
+-      u32 pasid = (upd->pd_asid << 8) | UPD_SWID;
+       int ret;
+       ret = rproc_boot(rpd_rproc);
+       if (ret)
+               return ret;
+-      return qcom_mdt_load(upd->dev, fw, rproc->firmware,
+-                           pasid, wcss->mem_region,
++      return qcom_mdt_load_pd_seg(upd->dev, fw, rproc->firmware,
++                           wcss->desc->pasid, upd->pd_asid, wcss->mem_region,
+                            wcss->mem_phys, wcss->mem_size,
+                            NULL);
+ }
+@@ -777,6 +801,12 @@ static int q6_wcss_remove(struct platfor
+       return 0;
+ }
++static const struct wcss_data q6_ipq5018_res_init = {
++      .pasid = MPD_WCNSS_PAS_ID,
++      // .share_upd_info_to_q6 = true, /* Version 1 */
++      // .mdt_load_sec = qcom_mdt_load_pd_seg,
++};
++
+ static const struct wcss_data q6_ipq5332_res_init = {
+       .pasid = MPD_WCNSS_PAS_ID,
+       .share_upd_info_to_q6 = true,
+@@ -787,6 +817,7 @@ static const struct wcss_data q6_ipq9574
+ };
+ static const struct of_device_id q6_wcss_of_match[] = {
++      { .compatible = "qcom,ipq5018-q6-mpd", .data = &q6_ipq5018_res_init },
+       { .compatible = "qcom,ipq5332-q6-mpd", .data = &q6_ipq5332_res_init },
+       { .compatible = "qcom,ipq9574-q6-mpd", .data = &q6_ipq9574_res_init },
+       { },
diff --git a/target/linux/qualcommax/patches-6.6/0815-remoteproc-qcom_q6v5_mpd-add-support-for-passing-v1-bootargs.patch b/target/linux/qualcommax/patches-6.6/0815-remoteproc-qcom_q6v5_mpd-add-support-for-passing-v1-bootargs.patch
new file mode 100644 (file)
index 0000000..fdc2879
--- /dev/null
@@ -0,0 +1,163 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Date: Mon, 09 Dec 2024 09:59:38 +0400
+Subject: [PATCH] remoteproc: qcom_q6v5_mpd: add support for passing v1 bootargs
+
+On multi-PD platforms such as IPQ5018, boot args are passed to the root PD
+run on the Q6 processor which in turn boots the user PDs for internal 
+(IPQ5018) and external wifi radios (such as QCN6122). These boot args 
+let the user PD process know details like what PCIE index, user PD ID, and
+reset GPIO is used. These are otherwise hardcoded in the firmware.
+
+Below is the structure expected of the version 1 boot args including the 
+default values hardcoded in the firmware for IPQ5018:
+
++------------+------+--------------+--------------+
+| Argument   | type | def val UPD2 | def val UPD3 |
++------------+------+--------------+--------------+
+| PCIE Index | u32  | 0x02 (PCIE1) | 0x01 (PCIE0) |
+| Length     | u32  | 0x04         | 0x04         |
+| User PD ID | u32  | 0x02         | 0x03         |
+| Reset GPIO | u32  | 0x12         | 0x0f         |
+| Reserved 1 | u32  | 0x00         | 0x00         |
+| Reserved 2 | u32  | 0x00         | 0x00         |
++------------+------+--------------+--------------+
+
+On IPQ5018/QCN6122 boards, the default mapping is as follows:
+
+          +-> UPD1 ----> IPQ5018 Internal 2.4G Radio
+         / 
+        /
+Root PD +---> UPD2 ----> QCN6122 6G Radio on PCIE1 (if available)
+        \
+         \
+          +-> UPD3 ----> QCN6102 5G Radio on PCIE0
+
+To support (future) boards with other mappings or control what UPD ID is 
+used, let's add support for passing boot args for more flexibility.
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/drivers/remoteproc/qcom_q6v5_mpd.c
++++ b/drivers/remoteproc/qcom_q6v5_mpd.c
+@@ -42,7 +42,11 @@
+ #define UPD_BOOT_INFO_SMEM_SIZE               4096
+ #define UPD_BOOT_INFO_HEADER_TYPE     0x2
+ #define UPD_BOOT_INFO_SMEM_ID         507
+-#define VERSION2                      2
++
++enum q6_bootargs_version {
++      VERSION1 = 1,
++      VERSION2,
++};
+ /**
+  * struct userpd_boot_info_header - header of user pd bootinfo
+@@ -94,6 +98,7 @@ struct userpd {
+ struct wcss_data {
+       u32 pasid;
+       bool share_upd_info_to_q6;
++      u8 bootargs_version;
+ };
+ /**
+@@ -298,10 +303,13 @@ static void *q6_wcss_da_to_va(struct rpr
+ static int share_upd_bootinfo_to_q6(struct rproc *rproc)
+ {
+       int i, ret;
++      u32 rd_val;
+       size_t size;
+       u16 cnt = 0, version;
+       void *ptr;
++      u8 *bootargs_arr;
+       struct q6_wcss *wcss = rproc->priv;
++      struct device_node *np = wcss->dev->of_node;
+       struct userpd *upd;
+       struct userpd_boot_info upd_bootinfo = {0};
+       const struct firmware *fw;
+@@ -323,10 +331,47 @@ static int share_upd_bootinfo_to_q6(stru
+       }
+       /*Version*/
+-      version = VERSION2;
++      version = (wcss->desc->bootargs_version) ? wcss->desc->bootargs_version : VERSION2;
+       memcpy_toio(ptr, &version, sizeof(version));
+       ptr += sizeof(version);
++      cnt = ret = of_property_count_u32_elems(np, "boot-args");
++      if (ret < 0) {
++              if (ret == -ENODATA) {
++                      dev_err(wcss->dev, "failed to read boot args ret:%d\n", ret);
++                      return ret;
++              }
++              cnt = 0;
++      }
++
++      /* No of elements */
++      memcpy_toio(ptr, &cnt, sizeof(u16));
++      ptr += sizeof(u16);
++
++      bootargs_arr = kzalloc(cnt, GFP_KERNEL);
++      if (!bootargs_arr) {
++              dev_err(wcss->dev, "failed to allocate memory\n");
++              return PTR_ERR(bootargs_arr);
++      }
++
++      for (i = 0; i < cnt; i++) {
++              ret = of_property_read_u32_index(np, "boot-args", i, &rd_val);
++              if (ret) {
++                      dev_err(wcss->dev, "failed to read boot args\n");
++                      kfree(bootargs_arr);
++                      return ret;
++              }
++              bootargs_arr[i] = (u8)rd_val;
++      }
++
++      /* Copy bootargs */
++      memcpy_toio(ptr, bootargs_arr, cnt);
++      ptr += (cnt);
++
++      of_node_put(np);
++      kfree(bootargs_arr);
++      cnt = 0;
++
+       for (i = 0; i < ARRAY_SIZE(wcss->upd); i++)
+               if (wcss->upd[i])
+                       cnt++;
+@@ -382,12 +427,14 @@ static int q6_wcss_load(struct rproc *rp
+       /* Share user pd boot info to Q6 remote processor */
+       if (desc->share_upd_info_to_q6) {
+-              ret = share_upd_bootinfo_to_q6(rproc);
+-              if (ret) {
+-                      dev_err(wcss->dev,
+-                              "user pd boot info sharing with q6 failed %d\n",
+-                              ret);
+-                      return ret;
++              if (of_property_present(wcss->dev->of_node, "boot-args")) {
++                      ret = share_upd_bootinfo_to_q6(rproc);
++                      if (ret) {
++                              dev_err(wcss->dev,
++                                      "user pd boot info sharing with q6 failed %d\n",
++                                      ret);
++                              return ret;
++                      }
+               }
+       }
+@@ -803,13 +850,15 @@ static int q6_wcss_remove(struct platfor
+ static const struct wcss_data q6_ipq5018_res_init = {
+       .pasid = MPD_WCNSS_PAS_ID,
+-      // .share_upd_info_to_q6 = true, /* Version 1 */
++      .share_upd_info_to_q6 = true,
++      .bootargs_version = VERSION1,
+       // .mdt_load_sec = qcom_mdt_load_pd_seg,
+ };
+ static const struct wcss_data q6_ipq5332_res_init = {
+       .pasid = MPD_WCNSS_PAS_ID,
+       .share_upd_info_to_q6 = true,
++      .bootargs_version = VERSION2,
+ };
+ static const struct wcss_data q6_ipq9574_res_init = {
diff --git a/target/linux/qualcommax/patches-6.6/0816-arm64-dts-qcom-ipq5018-add-wifi-support.patch b/target/linux/qualcommax/patches-6.6/0816-arm64-dts-qcom-ipq5018-add-wifi-support.patch
new file mode 100644 (file)
index 0000000..786345f
--- /dev/null
@@ -0,0 +1,241 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Date: Wed, 27 Oct 2024 16:34:11 +0400
+Subject: [PATCH] arm64: dts: qcom: ipq5018: add wifi support
+
+The IPQ5018 SoC comes with an internal 2x2 2.4Ghz wifi radio.
+QCN6122 is a PCIe based wifi solution specific to the IPQ5018 platform which 
+comes optinally packed with 1 or 2 QCN6122 chips or with an external 
+PCIe based wifi solution (such as QCN9074) for 5/6 Ghz support.
+
+As such, add wifi nodes for both IPQ5018 and QCN6122.
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -692,6 +692,225 @@
+                       };
+               };
++              wifi0: wifi@c000000 {
++                      compatible = "qcom,ipq5018-wifi";
++                      reg = <0xc000000 0x1000000>;
++
++                      interrupts = <GIC_SPI 288 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 289 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 290 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 291 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 292 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 293 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 294 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 295 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 296 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 297 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 298 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 299 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 300 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 301 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 302 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 303 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 304 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 305 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 306 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 307 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 308 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 309 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 310 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 311 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 312 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 313 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 314 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 315 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 316 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 317 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 318 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 319 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 320 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 321 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 322 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 323 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 324 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 325 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 326 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 327 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 328 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 329 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 330 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 331 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 332 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 333 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 334 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 335 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 336 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 337 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 338 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 339 IRQ_TYPE_EDGE_RISING>;
++
++                      interrupt-names = "misc-pulse1",
++                                        "misc-latch",
++                                        "sw-exception",
++                                        "watchdog",
++                                        "ce0",
++                                        "ce1",
++                                        "ce2",
++                                        "ce3",
++                                        "ce4",
++                                        "ce5",
++                                        "ce6",
++                                        "ce7",
++                                        "ce8",
++                                        "ce9",
++                                        "ce10",
++                                        "ce11",
++                                        "host2wbm-desc-feed",
++                                        "host2reo-re-injection",
++                                        "host2reo-command",
++                                        "host2rxdma-monitor-ring3",
++                                        "host2rxdma-monitor-ring2",
++                                        "host2rxdma-monitor-ring1",
++                                        "reo2ost-exception",
++                                        "wbm2host-rx-release",
++                                        "reo2host-status",
++                                        "reo2host-destination-ring4",
++                                        "reo2host-destination-ring3",
++                                        "reo2host-destination-ring2",
++                                        "reo2host-destination-ring1",
++                                        "rxdma2host-monitor-destination-mac3",
++                                        "rxdma2host-monitor-destination-mac2",
++                                        "rxdma2host-monitor-destination-mac1",
++                                        "ppdu-end-interrupts-mac3",
++                                        "ppdu-end-interrupts-mac2",
++                                        "ppdu-end-interrupts-mac1",
++                                        "rxdma2host-monitor-status-ring-mac3",
++                                        "rxdma2host-monitor-status-ring-mac2",
++                                        "rxdma2host-monitor-status-ring-mac1",
++                                        "host2rxdma-host-buf-ring-mac3",
++                                        "host2rxdma-host-buf-ring-mac2",
++                                        "host2rxdma-host-buf-ring-mac1",
++                                        "rxdma2host-destination-ring-mac3",
++                                        "rxdma2host-destination-ring-mac2",
++                                        "rxdma2host-destination-ring-mac1",
++                                        "host2tcl-input-ring4",
++                                        "host2tcl-input-ring3",
++                                        "host2tcl-input-ring2",
++                                        "host2tcl-input-ring1",
++                                        "wbm2host-tx-completions-ring3",
++                                        "wbm2host-tx-completions-ring2",
++                                        "wbm2host-tx-completions-ring1",
++                                        "tcl2host-status-ring";
++
++                      status = "disabled";
++              };
++
++              //QCN6102 5G
++              wifi1: wifi1@c000000 {
++                      reg = <0x0b00a040 0x0>;
++                      compatible = "qcom,qcn6122-wifi";
++                      interrupts = <GIC_SPI 416 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 417 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 418 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 419 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 420 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 421 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 422 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 423 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 424 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 425 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 426 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 427 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 428 IRQ_TYPE_EDGE_RISING>;
++                      status = "disabled";
++              };
++
++              //QCN6122 5G/6G
++              wifi2: wifi2@c000000 {
++                      reg = <0x0b00a040 0x0>;
++                      compatible = "qcom,qcn6122-wifi";
++                      interrupts = <GIC_SPI 448 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 449 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 450 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 451 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 452 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 453 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 454 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 455 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 456 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 457 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 458 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 459 IRQ_TYPE_EDGE_RISING>,
++                              <GIC_SPI 460 IRQ_TYPE_EDGE_RISING>;
++                      status = "disabled";
++              };
++
++              q6v5_wcss: remoteproc@cd00000 {
++                      compatible = "qcom,ipq5018-q6-mpd";
++                      reg = <0x0cd00000 0x4040>;
++                      #address-cells = <1>;
++                      #size-cells = <1>;
++                      ranges;
++
++                      clocks = <&gcc GCC_XO_CLK>,
++                               <&gcc GCC_SLEEP_CLK_SRC>,
++                               <&gcc GCC_SYS_NOC_WCSS_AHB_CLK>;
++
++                      interrupts-extended = <&intc GIC_SPI 291 IRQ_TYPE_EDGE_RISING>,
++                                            <&wcss_smp2p_in 0 IRQ_TYPE_NONE>,
++                                            <&wcss_smp2p_in 1 IRQ_TYPE_NONE>,
++                                            <&wcss_smp2p_in 2 IRQ_TYPE_NONE>,
++                                            <&wcss_smp2p_in 3 IRQ_TYPE_NONE>;
++                      interrupt-names = "wdog",
++                                        "fatal",
++                                        "ready",
++                                        "handover",
++                                        "stop-ack";
++
++                      qcom,smem-states = <&wcss_smp2p_out 0>,
++                                         <&wcss_smp2p_out 1>;
++                      qcom,smem-state-names = "shutdown",
++                                              "stop";
++
++                      status = "disabled";
++
++                      glink-edge {
++                              interrupts = <GIC_SPI 179 IRQ_TYPE_EDGE_RISING>;
++                              label = "rtr";
++                              qcom,remote-pid = <1>;
++                              mboxes = <&apcs_glb 8>;
++
++                              qrtr_requests {
++                                      qcom,glink-channels = "IPCRTR";
++                              };
++                      };
++              };
++
++              wcss: smp2p-wcss {
++                      compatible = "qcom,smp2p";
++                      qcom,smem = <435>, <428>;
++
++                      interrupt-parent = <&intc>;
++                      interrupts = <GIC_SPI 177 IRQ_TYPE_EDGE_RISING>;
++
++                      mboxes = <&apcs_glb 9>;
++
++                      qcom,local-pid = <0>;
++                      qcom,remote-pid = <1>;
++
++                      wcss_smp2p_out: master-kernel {
++                              qcom,entry-name = "master-kernel";
++                              qcom,smp2p-feature-ssr-ack;
++                              #qcom,smem-state-cells = <1>;
++                      };
++
++                      wcss_smp2p_in: slave-kernel {
++                              qcom,entry-name = "slave-kernel";
++                              interrupt-controller;
++                              #interrupt-cells = <2>;
++                      };
++              };
++
+               pcie1: pcie@80000000 {
+                       compatible = "qcom,pcie-ipq5018";
+                       reg = <0x80000000 0xf1d>,
diff --git a/target/linux/qualcommax/patches-6.6/0817-arm64-dts-qcom-ipq5018-add-tz_apps-reserved-memory-region.patch b/target/linux/qualcommax/patches-6.6/0817-arm64-dts-qcom-ipq5018-add-tz_apps-reserved-memory-region.patch
new file mode 100644 (file)
index 0000000..03a9c1c
--- /dev/null
@@ -0,0 +1,22 @@
+From: George Moussalem <george.moussalem@outlook.com>
+Date: Wed, 05 Feb 2025 12:12:47 +0400
+Subject: [PATCH] arm64: dts: qcom: ipq5018: add tz_apps reserved memory region
+
+Add tz_apps memory region needed for wifi to work.
+
+Signed-off-by: George Moussalem <george.moussalem@outlook.com>
+---
+--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+@@ -113,6 +113,11 @@
+               #size-cells = <2>;
+               ranges;
++              tz_apps@4a400000 {
++                      reg = <0x0 0x4a400000 0x0 0x400000>;
++                      no-map;
++              };
++
+               bootloader@4a800000 {
+                       reg = <0x0 0x4a800000 0x0 0x200000>;
+                       no-map;