diff --git a/package/boot/imx-bootlets/Makefile b/package/boot/imx-bootlets/Makefile index de976c249a..0ecb0f2dfd 100644 --- a/package/boot/imx-bootlets/Makefile +++ b/package/boot/imx-bootlets/Makefile @@ -7,11 +7,11 @@ include $(TOPDIR)/rules.mk PKG_NAME:=imx-bootlets -PKG_VERSION:=10.05.02 +PKG_VERSION:=10.12.01 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=http://trabant.uid0.hu/openwrt/ -PKG_HASH:=09ecd81a64db5166a235932146faf08d0689bfc7ac04ac9fcc3a5bd809fba74a +PKG_HASH:=f7c98cbc41e15184cad61c56115e840e34ac3ebb4a162fadeea905e5038fd65b PKG_FLAGS:=nonshared @@ -37,7 +37,7 @@ define Package/imx-bootlets/install $(INSTALL_BIN) $(PKG_BUILD_DIR)/boot_prep/boot_prep $(STAGING_DIR)/boot_prep $(INSTALL_BIN) $(PKG_BUILD_DIR)/linux_prep/output-target/linux_prep $(STAGING_DIR)/linux_prep $(INSTALL_BIN) $(PKG_BUILD_DIR)/power_prep/power_prep $(STAGING_DIR)/power_prep - $(INSTALL_BIN) $(PKG_BUILD_DIR)/linux_prebuilt.db $(STAGING_DIR)/linux_prebuilt.db + $(INSTALL_BIN) $(PKG_BUILD_DIR)/linux_ivt.bd $(STAGING_DIR)/linux_ivt.bd endef $(eval $(call BuildPackage,imx-bootlets)) diff --git a/package/boot/imx-bootlets/patches/001-skip_sb_generation.patch b/package/boot/imx-bootlets/patches/001-skip_sb_generation.patch index ad65265850..d1829aa16c 100644 --- a/package/boot/imx-bootlets/patches/001-skip_sb_generation.patch +++ b/package/boot/imx-bootlets/patches/001-skip_sb_generation.patch @@ -1,18 +1,22 @@ --- a/Makefile +++ b/Makefile -@@ -32,10 +32,11 @@ ifeq "$(DFT_IMAGE)" "$(wildcard $(DFT_IM - sed -i 's,[^ *]image.*;,\timage="$(DFT_UBOOT)";,' uboot.db - elftosb2 -z -c ./uboot.db -o i$(ARCH)_uboot.sb +@@ -37,13 +37,13 @@ ifeq "$(DFT_IMAGE)" "$(wildcard $(DFT_IM + elftosb -z -c ./uboot.bd -o i$(ARCH)_uboot.sb + elftosb -z -f imx28 -c ./uboot_ivt.bd -o i$(ARCH)_ivt_uboot.sb else - @echo "by using the pre-built kernel" -- elftosb2 -z -c ./linux_prebuilt.db -o i$(ARCH)_linux.sb -- @echo "generating U-Boot boot stream image" -- elftosb2 -z -c ./uboot_prebuilt.db -o i$(ARCH)_uboot.sb +- elftosb -z -c ./linux.bd -o i$(ARCH)_linux.sb +- elftosb -z -f imx28 -c ./linux_ivt.bd -o i$(ARCH)_ivt_linux.sb + @echo "... not generating any image for now." -+ #@echo "by using the pre-built kernel" -+ #elftosb2 -z -c ./linux_prebuilt.db -o i$(ARCH)_linux.sb ++ #elftosb -z -c ./linux.bd -o i$(ARCH)_linux.sb ++ #elftosb -z -f imx28 -c ./linux_ivt.bd -o i$(ARCH)_ivt_linux.sb + +- @echo "generating U-Boot boot stream image" +- elftosb -z -c ./uboot.bd -o i$(ARCH)_uboot.sb +- elftosb -z -f imx28 -c ./uboot_ivt.bd -o i$(ARCH)_ivt_uboot.sb + #@echo "generating U-Boot boot stream image" -+ #elftosb2 -z -c ./uboot_prebuilt.db -o i$(ARCH)_uboot.sb ++ #elftosb -z -c ./uboot.bd -o i$(ARCH)_uboot.sb ++ #elftosb -z -f imx28 -c ./uboot_ivt.bd -o i$(ARCH)_ivt_uboot.sb endif #@echo "generating kernel bootstream file sd_mmc_bootstream.raw" #Please use cfimager to burn xxx_linux.sb. The below way will no diff --git a/package/boot/imx-bootlets/patches/002-set_elftosb_config.patch b/package/boot/imx-bootlets/patches/002-set_elftosb_config.patch index 5cf48fd6fc..afd4150248 100644 --- a/package/boot/imx-bootlets/patches/002-set_elftosb_config.patch +++ b/package/boot/imx-bootlets/patches/002-set_elftosb_config.patch @@ -1,5 +1,5 @@ ---- a/linux_prebuilt.db -+++ b/linux_prebuilt.db +--- a/linux_ivt.bd ++++ b/linux_ivt.bd @@ -4,10 +4,10 @@ options { flags = 0x01; } @@ -7,11 +7,11 @@ - power_prep="./power_prep/power_prep"; - sdram_prep="./boot_prep/boot_prep"; - linux_prep="./linux_prep/output-target/linux_prep"; -- zImage = "./zImage"; +- zImage="./zImage"; + power_prep="./power_prep"; + sdram_prep="./boot_prep"; + linux_prep="./linux_prep"; -+ zImage = "./zImage_dtb"; ++ zImage="./zImage_dtb"; } section (0) { diff --git a/package/boot/imx-bootlets/patches/003-add-olinuxino.patch b/package/boot/imx-bootlets/patches/003-add-olinuxino.patch index 6ed5786344..dd0a5ce478 100644 --- a/package/boot/imx-bootlets/patches/003-add-olinuxino.patch +++ b/package/boot/imx-bootlets/patches/003-add-olinuxino.patch @@ -121,7 +121,7 @@ all: build_prep gen_bootstream -@@ -94,6 +97,8 @@ distclean: clean +@@ -101,6 +104,8 @@ distclean: clean clean: -rm -rf *.sb rm -f sd_mmc_bootstream.raw @@ -130,14 +130,3 @@ $(MAKE) -C linux_prep clean ARCH=$(ARCH) $(MAKE) -C boot_prep clean ARCH=$(ARCH) $(MAKE) -C power_prep clean ARCH=$(ARCH) ---- a/uboot.db -+++ b/uboot.db -@@ -3,7 +3,7 @@ - sources { - power_prep="./power_prep/power_prep"; - sdram_prep="./boot_prep/boot_prep"; -- image="/home/b18647/repos/ltib_latest/rootfs/boot/u-boot"; -+ image="../boot/u-boot"; - } - - section (0) { diff --git a/package/boot/imx-bootlets/patches/004-fix-ARM-8933_1.patch b/package/boot/imx-bootlets/patches/004-fix-ARM-8933_1.patch new file mode 100644 index 0000000000..3b896b2889 --- /dev/null +++ b/package/boot/imx-bootlets/patches/004-fix-ARM-8933_1.patch @@ -0,0 +1,11 @@ +--- a/linux_prep/core/cmdlines.S ++++ b/linux_prep/core/cmdlines.S +@@ -14,7 +14,7 @@ + #define CMDLINES_FILE "output-target/command_lines_stripped.txt" + #endif + +- .section .cmdlines, #alloc ++ .section .cmdlines, "a" + .globl cmdlines_start + cmdlines_start: + .incbin CMDLINES_FILE diff --git a/package/boot/uboot-envtools/files/qualcommax_ipq50xx b/package/boot/uboot-envtools/files/qualcommax_ipq50xx new file mode 100644 index 0000000000..0e78bb8060 --- /dev/null +++ b/package/boot/uboot-envtools/files/qualcommax_ipq50xx @@ -0,0 +1,22 @@ +[ -e /etc/config/ubootenv ] && exit 0 + +touch /etc/config/ubootenv + +. /lib/uboot-envtools.sh +. /lib/functions.sh + +board=$(board_name) + +case "$board" in +linksys,mx2000|\ +linksys,mx5500) + idx="$(find_mtd_index u_env)" + [ -n "$idx" ] && \ + ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x40000" "0x20000" + ;; +esac + +config_load ubootenv +config_foreach ubootenv_add_app_config + +exit 0 diff --git a/package/firmware/ath11k-firmware/Makefile b/package/firmware/ath11k-firmware/Makefile index 0acc3f9f1a..a333d1b154 100644 --- a/package/firmware/ath11k-firmware/Makefile +++ b/package/firmware/ath11k-firmware/Makefile @@ -25,6 +25,21 @@ include $(INCLUDE_DIR)/package.mk RSTRIP:=: STRIP:=: +ATH11K_LEGACY_FW_NAME:=ath11k-legacy-firmware +ATH11K_LEGACY_FW_DATE:=2024-03-14 +ATH11K_LEGACY_FW_RELEASE:=795809c7 +ATH11K_LEGACY_FW_SUBDIR:=$(ATH11K_LEGACY_FW_NAME)-$(ATH11K_LEGACY_FW_DATE)~$(ATH11K_LEGACY_FW_RELEASE) +ATH11K_LEGACY_FW_SOURCE=$(ATH11K_LEGACY_FW_SUBDIR).tar.zst + +define Download/$(ATH11K_LEGACY_FW_NAME) + FILE:=$(ATH11K_LEGACY_FW_SOURCE) + PROTO:=git + URL:=https://github.com/quic/upstream-wifi-fw.git + SOURCE_VERSION:=795809c7041582bd51bdfaa1f548b916ae8d4382 + MIRROR_HASH:=e042024b6762a5b4fe56f1a5bf8870df2e7abcca90ec22103cd46a609bee7cec + SUBDIR:=$(ATH11K_LEGACY_FW_SUBDIR) +endef + define Package/ath11k-firmware-default SECTION:=firmware CATEGORY:=Firmware @@ -32,6 +47,11 @@ define Package/ath11k-firmware-default DEPENDS:= endef +define Package/ath11k-firmware-ipq5018 +$(Package/ath11k-firmware-default) + TITLE:=IPQ5018 ath11k firmware +endef + define Package/ath11k-firmware-ipq6018 $(Package/ath11k-firmware-default) TITLE:=IPQ6018 ath11k firmware @@ -42,15 +62,43 @@ $(Package/ath11k-firmware-default) TITLE:=IPQ8074 ath11k firmware endef +define Package/ath11k-firmware-qcn6122 +$(Package/ath11k-firmware-default) + TITLE:=QCN6122 ath11k firmware + DEPENDS:=ath11k-firmware-ipq5018 +endef + define Package/ath11k-firmware-qcn9074 $(Package/ath11k-firmware-default) TITLE:=QCN9074 ath11k firmware endef +define Build/Clean + $(call Build/Clean/Default,) + + rm -rf \ + $(BUILD_DIR)/$(ATH11K_LEGACY_FW_SUBDIR) +endef + +define Build/Prepare + $(eval $(call Download,ath11k-legacy-firmware)) + + $(call Build/Prepare/Default,) + + $(TAR) -C $(BUILD_DIR) -xf $(DL_DIR)/$(ATH11K_LEGACY_FW_SOURCE) +endef + define Build/Compile endef +define Package/ath11k-firmware-ipq5018/install + $(INSTALL_DIR) $(1)/lib/firmware/ath11k/IPQ5018/hw1.0 + $(INSTALL_DATA) \ + $(BUILD_DIR)/$(ATH11K_LEGACY_FW_SUBDIR)/ath11k-firmware/IPQ5018_QCN6122_QCN6122/hw1.0/2.7.0.1/WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1/*.* \ + $(1)/lib/firmware/ath11k/IPQ5018/hw1.0/ +endef + define Package/ath11k-firmware-ipq6018/install $(INSTALL_DIR) $(1)/lib/firmware/IPQ6018 $(INSTALL_DATA) \ @@ -65,6 +113,13 @@ define Package/ath11k-firmware-ipq8074/install $(1)/lib/firmware/IPQ8074/ endef +define Package/ath11k-firmware-qcn6122/install + $(INSTALL_DIR) $(1)/lib/firmware/ath11k/QCN6122/hw1.0 + $(INSTALL_DATA) \ + $(BUILD_DIR)/$(ATH11K_LEGACY_FW_SUBDIR)/ath11k-firmware/IPQ5018_QCN6122_QCN6122/hw1.0/2.7.0.1/WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1/qcn6122/* \ + $(1)/lib/firmware/ath11k/QCN6122/hw1.0/ +endef + define Package/ath11k-firmware-qcn9074/install $(INSTALL_DIR) $(1)/lib/firmware/ath11k/QCN9074/hw1.0 $(INSTALL_DATA) \ @@ -74,6 +129,8 @@ define Package/ath11k-firmware-qcn9074/install $(PKG_BUILD_DIR)/QCN9074/hw1.0/board-2.bin $(1)/lib/firmware/ath11k/QCN9074/hw1.0/board-2.bin endef +$(eval $(call BuildPackage,ath11k-firmware-ipq5018)) $(eval $(call BuildPackage,ath11k-firmware-ipq6018)) $(eval $(call BuildPackage,ath11k-firmware-ipq8074)) +$(eval $(call BuildPackage,ath11k-firmware-qcn6122)) $(eval $(call BuildPackage,ath11k-firmware-qcn9074)) diff --git a/package/firmware/ipq-wifi/Makefile b/package/firmware/ipq-wifi/Makefile index da5fd5e23e..408c821035 100644 --- a/package/firmware/ipq-wifi/Makefile +++ b/package/firmware/ipq-wifi/Makefile @@ -39,8 +39,10 @@ ALLWIFIBOARDS:= \ edimax_cax1800 \ linksys_homewrk \ linksys_mr7350 \ + linksys_mx2000 \ linksys_mx4200 \ linksys_mx5300 \ + linksys_mx5500 \ linksys_mx8500 \ linksys_whw03 \ netgear_lbr20 \ @@ -102,14 +104,18 @@ define ipq-wifi-install-one $(call ipq-wifi-install-one-to,$(1),$(2),QCA9984/hw1.0),\ $(if $(filter $(suffix $(1)),.QCA99X0 .qca99x0),\ $(call ipq-wifi-install-one-to,$(1),$(2),QCA99X0/hw2.0),\ + $(if $(filter $(suffix $(1)),.IPQ5018 .ipq5018),\ + $(call ipq-wifi-install-ath11-one-to,$(1),$(2),IPQ5018/hw1.0),\ $(if $(filter $(suffix $(1)),.IPQ6018 .ipq6018),\ $(call ipq-wifi-install-ath11-one-to,$(1),$(2),IPQ6018/hw1.0),\ $(if $(filter $(suffix $(1)),.IPQ8074 .ipq8074),\ $(call ipq-wifi-install-ath11-one-to,$(1),$(2),IPQ8074/hw2.0),\ + $(if $(filter $(suffix $(1)),.QCN6122 .qcn6122),\ + $(call ipq-wifi-install-ath11-one-to,$(1),$(2),QCN6122/hw1.0),\ $(if $(filter $(suffix $(1)),.QCN9074 .qcn9074),\ $(call ipq-wifi-install-ath11-one-to,$(1),$(2),QCN9074/hw1.0),\ $(error Unrecognized board-file suffix '$(suffix $(1))' for '$(1)')\ - )))))))) + )))))))))) endef # Blank line required at end of above define due to foreach context @@ -171,8 +177,10 @@ $(eval $(call generate-ipq-wifi-package,edgecore_eap102,Edgecore EAP102)) $(eval $(call generate-ipq-wifi-package,edimax_cax1800,Edimax CAX1800)) $(eval $(call generate-ipq-wifi-package,linksys_homewrk,Linksys HomeWRK)) $(eval $(call generate-ipq-wifi-package,linksys_mr7350,Linksys MR7350)) +$(eval $(call generate-ipq-wifi-package,linksys_mx2000,Linksys MX2000)) $(eval $(call generate-ipq-wifi-package,linksys_mx4200,Linksys MX4200)) $(eval $(call generate-ipq-wifi-package,linksys_mx5300,Linksys MX5300)) +$(eval $(call generate-ipq-wifi-package,linksys_mx5500,Linksys MX5500)) $(eval $(call generate-ipq-wifi-package,linksys_mx8500,Linksys MX8500)) $(eval $(call generate-ipq-wifi-package,linksys_whw03,Linksys WHW03)) $(eval $(call generate-ipq-wifi-package,netgear_lbr20,Netgear LBR20)) diff --git a/package/kernel/linux/modules/netdevices.mk b/package/kernel/linux/modules/netdevices.mk index 2d2cc1dade..ac3687f7d7 100644 --- a/package/kernel/linux/modules/netdevices.mk +++ b/package/kernel/linux/modules/netdevices.mk @@ -637,6 +637,65 @@ endef $(eval $(call KernelPackage,dsa-qca8k)) + +define KernelPackage/dsa-realtek + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Realtek common module RTL83xx DSA switch family + DEPENDS:=+kmod-dsa +kmod-phy-realtek +kmod-regmap-core @!TARGET_x86 @!TARGET_bcm47xx @!TARGET_uml + KCONFIG:= \ + CONFIG_NET_DSA_REALTEK \ + CONFIG_NET_DSA_REALTEK_MDIO=y \ + CONFIG_NET_DSA_REALTEK_SMI=y + FILES:= $(LINUX_DIR)/drivers/net/dsa/realtek/realtek_dsa.ko +endef + +define KernelPackage/dsa-realtek/description + Common kernel module for Realtek RTL83xx DSA switch family +endef + +$(eval $(call KernelPackage,dsa-realtek)) + + +define KernelPackage/dsa-rtl8366rb + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Realtek RTL8365MB switch DSA support + DEPENDS:=+kmod-dsa-realtek @!TARGET_x86 @!TARGET_bcm47xx @!TARGET_uml + KCONFIG:= \ + CONFIG_NET_DSA_REALTEK_RTL8366RB \ + CONFIG_NET_DSA_TAG_RTL4_A + FILES:= \ + $(LINUX_DIR)/drivers/net/dsa/realtek/rtl8366.ko \ + $(LINUX_DIR)/net/dsa/tag_rtl4_a.ko + AUTOLOAD:=$(call AutoLoad,42,rtl8366,1) +endef + +define KernelPackage/dsa-rtl8366rb/description + DSA based kernel modules for the Realtek RTL8366RB switch family +endef + +$(eval $(call KernelPackage,dsa-rtl8366rb)) + + +define KernelPackage/dsa-rtl8365mb + SUBMENU:=$(NETWORK_DEVICES_MENU) + TITLE:=Realtek RTL8365MB switch DSA support + DEPENDS:=+kmod-dsa-realtek @!TARGET_x86 @!TARGET_bcm47xx @!TARGET_uml + KCONFIG:= \ + CONFIG_NET_DSA_REALTEK_RTL8365MB \ + CONFIG_NET_DSA_TAG_RTL8_4 + FILES:= \ + $(LINUX_DIR)/drivers/net/dsa/realtek/rtl8365mb.ko \ + $(LINUX_DIR)/net/dsa/tag_rtl8_4.ko + AUTOLOAD:=$(call AutoLoad,42,rtl8365mb,1) +endef + +define KernelPackage/dsa-rtl8365mb/description + DSA based kernel modules for the Realtek RTL8365MB switch family +endef + +$(eval $(call KernelPackage,dsa-rtl8365mb)) + + define KernelPackage/swconfig SUBMENU:=$(NETWORK_DEVICES_MENU) TITLE:=switch configuration API diff --git a/package/kernel/mac80211/patches/ath11k/201-wifi-ath11k-Support-setting-bdf-addr-and-caldb-addr-.patch b/package/kernel/mac80211/patches/ath11k/201-wifi-ath11k-Support-setting-bdf-addr-and-caldb-addr-.patch new file mode 100644 index 0000000000..109daf02f2 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/201-wifi-ath11k-Support-setting-bdf-addr-and-caldb-addr-.patch @@ -0,0 +1,62 @@ +From 824dde8652815aa67b4e2bf2d8a9455a8ef82b8f Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Thu, 29 Jun 2023 06:12:45 +0000 +Subject: [PATCH] wifi: ath11k: Support setting bdf-addr and caldb-addr via DT + +Signed-off-by: Ziyang Huang +--- + drivers/net/wireless/ath/ath11k/qmi.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2028,6 +2028,7 @@ static int ath11k_qmi_assign_target_mem_ + struct device_node *hremote_node = NULL; + struct resource res; + u32 host_ddr_sz; ++ u32 addr; + int i, idx, ret; + + for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) { +@@ -2067,7 +2068,9 @@ static int ath11k_qmi_assign_target_mem_ + idx++; + break; + case BDF_MEM_REGION_TYPE: +- ab->qmi.target_mem[idx].paddr = ab->hw_params.bdf_addr; ++ if(of_property_read_u32(dev->of_node, "qcom,bdf-addr", &addr)) ++ addr = ab->hw_params.bdf_addr; ++ ab->qmi.target_mem[idx].paddr = addr; + ab->qmi.target_mem[idx].vaddr = NULL; + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; +@@ -2089,8 +2092,9 @@ static int ath11k_qmi_assign_target_mem_ + if (!ab->qmi.target_mem[idx].iaddr) + return -EIO; + } else { +- ab->qmi.target_mem[idx].paddr = +- ATH11K_QMI_CALDB_ADDRESS; ++ if(of_property_read_u32(dev->of_node, "qcom,caldb-addr", &addr)) ++ addr = ATH11K_QMI_CALDB_ADDRESS; ++ ab->qmi.target_mem[idx].paddr = addr; + } + } else { + ab->qmi.target_mem[idx].paddr = 0; +@@ -2292,6 +2296,7 @@ static int ath11k_qmi_load_file_target_m + struct qmi_wlanfw_bdf_download_resp_msg_v01 resp; + struct qmi_txn txn; + const u8 *temp = data; ++ u32 addr; + void __iomem *bdf_addr = NULL; + int ret = 0; + u32 remaining = len; +@@ -2303,7 +2308,9 @@ static int ath11k_qmi_load_file_target_m + memset(&resp, 0, sizeof(resp)); + + if (ab->hw_params.fixed_bdf_addr) { +- bdf_addr = ioremap(ab->hw_params.bdf_addr, ab->hw_params.fw.board_size); ++ if(of_property_read_u32(ab->dev->of_node, "qcom,bdf-addr", &addr)) ++ addr = ab->hw_params.bdf_addr; ++ bdf_addr = ioremap(addr, ab->hw_params.fw.board_size); + if (!bdf_addr) { + ath11k_warn(ab, "qmi ioremap error for bdf_addr\n"); + ret = -EIO; diff --git a/package/kernel/mac80211/patches/ath11k/910-ath11k-fix-remapped-ce-accessing-issue-on-64bit-OS.patch b/package/kernel/mac80211/patches/ath11k/910-ath11k-fix-remapped-ce-accessing-issue-on-64bit-OS.patch new file mode 100644 index 0000000000..5878fa08df --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/910-ath11k-fix-remapped-ce-accessing-issue-on-64bit-OS.patch @@ -0,0 +1,153 @@ +From: Ziyang Huang +Date: Thu, 2 May 2024 00:14:31 +0800 +Subject: [PATCH] wifi: ath11k: fix remapped ce accessing issue on 64bit OS + +On 64bit OS, when ab->mem_ce is lower than or 4G far away from ab->mem, +u32 is not enough to store the offsets, which makes ath11k_ahb_read32() +and ath11k_ahb_write32() access incorrect address and causes Data Abort +Exception. + +Let's use the high bits of offsets to decide where to access, which is +similar as ath11k_pci_get_window_start() done. In the future, we can merge +these functions for unified regs accessing. + +Signed-off-by: Ziyang Huang +--- + +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -198,12 +198,18 @@ static const struct ath11k_pci_ops ath11 + + static inline u32 ath11k_ahb_read32(struct ath11k_base *ab, u32 offset) + { +- return ioread32(ab->mem + offset); ++ if ((offset & ATH11K_REG_TYPE_MASK) == ATH11K_REG_TYPE_CE) ++ return ioread32(ab->mem_ce + FIELD_GET(ATH11K_REG_OFFSET_MASK, offset)); ++ else ++ return ioread32(ab->mem + FIELD_GET(ATH11K_REG_OFFSET_MASK, offset)); + } + + static inline void ath11k_ahb_write32(struct ath11k_base *ab, u32 offset, u32 value) + { +- iowrite32(value, ab->mem + offset); ++ if ((offset & ATH11K_REG_TYPE_MASK) == ATH11K_REG_TYPE_CE) ++ iowrite32(value, ab->mem_ce + FIELD_GET(ATH11K_REG_OFFSET_MASK, offset)); ++ else ++ iowrite32(value, ab->mem + FIELD_GET(ATH11K_REG_OFFSET_MASK, offset)); + } + + static void ath11k_ahb_kill_tasklets(struct ath11k_base *ab) +@@ -275,9 +281,9 @@ static void ath11k_ahb_ce_irq_enable(str + const struct ce_ie_addr *ce_ie_addr = ab->hw_params.ce_ie_addr; + u32 ie1_reg_addr, ie2_reg_addr, ie3_reg_addr; + +- ie1_reg_addr = ce_ie_addr->ie1_reg_addr + ATH11K_CE_OFFSET(ab); +- ie2_reg_addr = ce_ie_addr->ie2_reg_addr + ATH11K_CE_OFFSET(ab); +- ie3_reg_addr = ce_ie_addr->ie3_reg_addr + ATH11K_CE_OFFSET(ab); ++ ie1_reg_addr = ce_ie_addr->ie1_reg_addr; ++ ie2_reg_addr = ce_ie_addr->ie2_reg_addr; ++ ie3_reg_addr = ce_ie_addr->ie3_reg_addr; + + ce_attr = &ab->hw_params.host_ce_config[ce_id]; + if (ce_attr->src_nentries) +@@ -296,9 +302,9 @@ static void ath11k_ahb_ce_irq_disable(st + const struct ce_ie_addr *ce_ie_addr = ab->hw_params.ce_ie_addr; + u32 ie1_reg_addr, ie2_reg_addr, ie3_reg_addr; + +- ie1_reg_addr = ce_ie_addr->ie1_reg_addr + ATH11K_CE_OFFSET(ab); +- ie2_reg_addr = ce_ie_addr->ie2_reg_addr + ATH11K_CE_OFFSET(ab); +- ie3_reg_addr = ce_ie_addr->ie3_reg_addr + ATH11K_CE_OFFSET(ab); ++ ie1_reg_addr = ce_ie_addr->ie1_reg_addr; ++ ie2_reg_addr = ce_ie_addr->ie2_reg_addr; ++ ie3_reg_addr = ce_ie_addr->ie3_reg_addr; + + ce_attr = &ab->hw_params.host_ce_config[ce_id]; + if (ce_attr->src_nentries) +--- a/drivers/net/wireless/ath/ath11k/hal.c ++++ b/drivers/net/wireless/ath/ath11k/hal.c +@@ -1247,20 +1247,16 @@ static int ath11k_hal_srng_create_config + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_STATUS_RING_HP; + + s = &hal->srng_config[HAL_CE_SRC]; +- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_BASE_LSB + +- ATH11K_CE_OFFSET(ab); +- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_HP + +- ATH11K_CE_OFFSET(ab); ++ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_BASE_LSB; ++ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_HP; + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab); + + s = &hal->srng_config[HAL_CE_DST]; +- s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_BASE_LSB + +- ATH11K_CE_OFFSET(ab); +- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_HP + +- ATH11K_CE_OFFSET(ab); ++ s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_BASE_LSB; ++ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_HP; + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - +@@ -1268,9 +1264,8 @@ static int ath11k_hal_srng_create_config + + s = &hal->srng_config[HAL_CE_DST_STATUS]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + +- HAL_CE_DST_STATUS_RING_BASE_LSB + ATH11K_CE_OFFSET(ab); +- s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_STATUS_RING_HP + +- ATH11K_CE_OFFSET(ab); ++ HAL_CE_DST_STATUS_RING_BASE_LSB; ++ s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_STATUS_RING_HP; + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -2268,9 +2268,9 @@ const struct ce_ie_addr ath11k_ce_ie_add + }; + + const struct ce_ie_addr ath11k_ce_ie_addr_ipq5018 = { +- .ie1_reg_addr = CE_HOST_IPQ5018_IE_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE, +- .ie2_reg_addr = CE_HOST_IPQ5018_IE_2_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE, +- .ie3_reg_addr = CE_HOST_IPQ5018_IE_3_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE, ++ .ie1_reg_addr = ATH11K_REG_TYPE_CE + CE_HOST_IPQ5018_IE_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE, ++ .ie2_reg_addr = ATH11K_REG_TYPE_CE + CE_HOST_IPQ5018_IE_2_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE, ++ .ie3_reg_addr = ATH11K_REG_TYPE_CE + CE_HOST_IPQ5018_IE_3_ADDRESS - HAL_IPQ5018_CE_WFSS_REG_BASE, + }; + + const struct ce_remap ath11k_ce_remap_ipq5018 = { +@@ -2801,13 +2801,13 @@ const struct ath11k_hw_regs ipq5018_regs + .hal_reo_status_hp = 0x00003070, + + /* WCSS relative address */ +- .hal_seq_wcss_umac_ce0_src_reg = 0x08400000 ++ .hal_seq_wcss_umac_ce0_src_reg = ATH11K_REG_TYPE_CE + 0x08400000 + - HAL_IPQ5018_CE_WFSS_REG_BASE, +- .hal_seq_wcss_umac_ce0_dst_reg = 0x08401000 ++ .hal_seq_wcss_umac_ce0_dst_reg = ATH11K_REG_TYPE_CE + 0x08401000 + - HAL_IPQ5018_CE_WFSS_REG_BASE, +- .hal_seq_wcss_umac_ce1_src_reg = 0x08402000 ++ .hal_seq_wcss_umac_ce1_src_reg = ATH11K_REG_TYPE_CE + 0x08402000 + - HAL_IPQ5018_CE_WFSS_REG_BASE, +- .hal_seq_wcss_umac_ce1_dst_reg = 0x08403000 ++ .hal_seq_wcss_umac_ce1_dst_reg = ATH11K_REG_TYPE_CE + 0x08403000 + - HAL_IPQ5018_CE_WFSS_REG_BASE, + + /* WBM Idle address */ +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -81,7 +81,12 @@ + #define ATH11K_M3_FILE "m3.bin" + #define ATH11K_REGDB_FILE_NAME "regdb.bin" + +-#define ATH11K_CE_OFFSET(ab) (ab->mem_ce - ab->mem) ++#define ATH11K_REG_TYPE_MASK GENMASK(31, 28) ++#define ATH11K_REG_TYPE(x) FIELD_PREP_CONST(ATH11K_REG_TYPE_MASK, x) ++#define ATH11K_REG_TYPE_NORMAL ATH11K_REG_TYPE(0) ++#define ATH11K_REG_TYPE_DP ATH11K_REG_TYPE(1) ++#define ATH11K_REG_TYPE_CE ATH11K_REG_TYPE(2) ++#define ATH11K_REG_OFFSET_MASK GENMASK(27, 0) + + enum ath11k_hw_rate_cck { + ATH11K_HW_RATE_CCK_LP_11M = 0, diff --git a/package/kernel/mac80211/patches/ath11k/920-wifi-ath11k-add-hw-params-for-QCN6122.patch b/package/kernel/mac80211/patches/ath11k/920-wifi-ath11k-add-hw-params-for-QCN6122.patch new file mode 100644 index 0000000000..6d5aa7e2c1 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/920-wifi-ath11k-add-hw-params-for-QCN6122.patch @@ -0,0 +1,115 @@ +From: George Moussalem +Date: Wed, 27 Oct 2024 16:34:11 +0400 +Subject: [PATCH] wifi: ath11k: add hw params for QCN6122 + +Add QCN6122 platform support. + +QCN6122 is a PCIe based solution that is attached to and enumerated +by the WPSS (Wireless Processor SubSystem) Q6 processor. + +Though it is a PCIe device, since it is not attached to APSS processor +(Application Processor SubSystem), APSS will be unaware of such a decice +and hence it is registered to the APSS processor as a platform device(AHB). +Because of this hybrid nature, it is called as a hybrid bus device. + +As such, QCN6122 is a hybrid bus type device and follows the same codepath +as for WCN6750. + +This is a heavily simplified version of below downstream patch: +Download from https://git.codelinaro.org/clo/qsdk/oss/system/feeds/wlan-open/-/blob/NHSS.QSDK.12.4.5.r2/mac80211/patches/232-ath11k-qcn6122-support.patch + +Co-developed-by: George Moussalem +Signed-off-by: Sowmiya Sree Elavalagan +Signed-off-by: George Moussalem +--- + +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -809,6 +809,67 @@ static struct ath11k_hw_params ath11k_hw + .support_fw_mac_sequence = true, + .support_dual_stations = true, + }, ++ { ++ .hw_rev = ATH11K_HW_QCN6122_HW10, ++ .name = "qcn6122 hw1.0", ++ .fw = { ++ .dir = "QCN6122/hw1.0", ++ .board_size = 256 * 1024, ++ .cal_offset = 128 * 1024, ++ }, ++ .hal_params = &ath11k_hw_hal_params_ipq8074, ++ .max_radios = MAX_RADIOS_5018, ++ .bdf_addr = 0x4D200000, ++ .hw_ops = &ipq5018_ops, ++ .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), ++ .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122, ++ .interface_modes = BIT(NL80211_IFTYPE_STATION) | ++ BIT(NL80211_IFTYPE_AP) | ++ BIT(NL80211_IFTYPE_MESH_POINT), ++ .spectral = { ++ .fft_sz = 2, ++ .fft_pad_sz = 0, ++ .summary_pad_sz = 16, ++ .fft_hdr_len = 24, ++ .max_fft_bins = 1024, ++ }, ++ .credit_flow = false, ++ .max_tx_ring = 1, ++ .supports_monitor = true, ++ .supports_shadow_regs = false, ++ .idle_ps = false, ++ .supports_suspend = false, ++ .host_ce_config = ath11k_host_ce_config_qcn9074, ++ .ce_count = CE_CNT_5018, ++ .target_ce_config = ath11k_target_ce_config_wlan_ipq5018, ++ .target_ce_count = TARGET_CE_CNT_5018, ++ .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq5018, ++ .svc_to_ce_map_len = SVC_CE_MAP_LEN_5018, ++ .single_pdev_only = false, ++ .rxdma1_enable = true, ++ .num_rxdma_per_pdev = RXDMA_PER_PDEV_5018, ++ .rx_mac_buf_ring = false, ++ .vdev_start_delay = false, ++ .htt_peer_map_v2 = true, ++ .coldboot_cal_mm = true, ++ .coldboot_cal_ftm = true, ++ .cbcal_restart_fw = true, ++ .fix_l1ss = true, ++ .alloc_cacheable_memory = true, ++ .m3_fw_support = false, ++ .fixed_bdf_addr = true, ++ .fixed_mem_region = true, ++ .static_window_map = true, ++ .hybrid_bus_type = true, ++ .fw_mem_mode = 1, ++ .supports_sta_ps = false, ++ .dbr_debug_support = true, ++ .bios_sar_capa = NULL, ++ .fixed_fw_mem = false, ++ .support_off_channel_tx = false, ++ .tcl_ring_retry = true, ++ .tx_ring_size = DP_TCL_DATA_RING_SIZE, ++ }, + }; + + static inline struct ath11k_pdev *ath11k_core_get_single_pdev(struct ath11k_base *ab) +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -148,6 +148,7 @@ enum ath11k_hw_rev { + ATH11K_HW_WCN6750_HW10, + ATH11K_HW_IPQ5018_HW10, + ATH11K_HW_QCA2066_HW21, ++ ATH11K_HW_QCN6122_HW10, + }; + + enum ath11k_firmware_mode { +--- a/drivers/net/wireless/ath/ath11k/qmi.h ++++ b/drivers/net/wireless/ath/ath11k/qmi.h +@@ -22,6 +22,7 @@ + #define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ8074 0x02 + #define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN9074 0x07 + #define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_WCN6750 0x03 ++#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122 0x40 + #define ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 32 + #define ATH11K_QMI_RESP_LEN_MAX 8192 + #define ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 52 diff --git a/package/kernel/mac80211/patches/ath11k/921-wifi-ath11k-add-hal-regs-for-QCN6122.patch b/package/kernel/mac80211/patches/ath11k/921-wifi-ath11k-add-hal-regs-for-QCN6122.patch new file mode 100644 index 0000000000..eae73170b3 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/921-wifi-ath11k-add-hal-regs-for-QCN6122.patch @@ -0,0 +1,114 @@ +From: George Moussalem +Date: Wed, 27 Oct 2024 16:34:11 +0400 +Subject: [PATCH] wifi: ath11k: add hal regs for QCN6122 + +Add HAL changes required to support QCN6122. Offsets are similar to those of +WCN6750 but QCN6122 does not use the hal_shadow_base_addr, so add platform +specific ath11k_hw_regs and register them in hw params. + +Signed-off-by: George Moussalem +--- +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -822,6 +822,7 @@ static struct ath11k_hw_params ath11k_hw + .bdf_addr = 0x4D200000, + .hw_ops = &ipq5018_ops, + .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), ++ .regs = &qcn6122_regs, + .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122, + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -2822,6 +2822,81 @@ const struct ath11k_hw_regs ipq5018_regs + .hal_wbm1_release_ring_base_lsb = 0x0000097c, + }; + ++const struct ath11k_hw_regs qcn6122_regs = { ++ /* SW2TCL(x) R0 ring configuration address */ ++ .hal_tcl1_ring_base_lsb = 0x00000694, ++ .hal_tcl1_ring_base_msb = 0x00000698, ++ .hal_tcl1_ring_id = 0x0000069c, ++ .hal_tcl1_ring_misc = 0x000006a4, ++ .hal_tcl1_ring_tp_addr_lsb = 0x000006b0, ++ .hal_tcl1_ring_tp_addr_msb = 0x000006b4, ++ .hal_tcl1_ring_consumer_int_setup_ix0 = 0x000006c4, ++ .hal_tcl1_ring_consumer_int_setup_ix1 = 0x000006c8, ++ .hal_tcl1_ring_msi1_base_lsb = 0x000006dc, ++ .hal_tcl1_ring_msi1_base_msb = 0x000006e0, ++ .hal_tcl1_ring_msi1_data = 0x000006e4, ++ .hal_tcl2_ring_base_lsb = 0x000006ec, ++ .hal_tcl_ring_base_lsb = 0x0000079c, ++ ++ /* TCL STATUS ring address */ ++ .hal_tcl_status_ring_base_lsb = 0x000008a4, ++ ++ /* REO2SW(x) R0 ring configuration address */ ++ .hal_reo1_ring_base_lsb = 0x000001ec, ++ .hal_reo1_ring_base_msb = 0x000001f0, ++ .hal_reo1_ring_id = 0x000001f4, ++ .hal_reo1_ring_misc = 0x000001fc, ++ .hal_reo1_ring_hp_addr_lsb = 0x00000200, ++ .hal_reo1_ring_hp_addr_msb = 0x00000204, ++ .hal_reo1_ring_producer_int_setup = 0x00000210, ++ .hal_reo1_ring_msi1_base_lsb = 0x00000234, ++ .hal_reo1_ring_msi1_base_msb = 0x00000238, ++ .hal_reo1_ring_msi1_data = 0x0000023c, ++ .hal_reo2_ring_base_lsb = 0x00000244, ++ .hal_reo1_aging_thresh_ix_0 = 0x00000564, ++ .hal_reo1_aging_thresh_ix_1 = 0x00000568, ++ .hal_reo1_aging_thresh_ix_2 = 0x0000056c, ++ .hal_reo1_aging_thresh_ix_3 = 0x00000570, ++ ++ /* REO2SW(x) R2 ring pointers (head/tail) address */ ++ .hal_reo1_ring_hp = 0x00003028, ++ .hal_reo1_ring_tp = 0x0000302c, ++ .hal_reo2_ring_hp = 0x00003030, ++ ++ /* REO2TCL R0 ring configuration address */ ++ .hal_reo_tcl_ring_base_lsb = 0x000003fc, ++ .hal_reo_tcl_ring_hp = 0x00003058, ++ ++ /* SW2REO ring address */ ++ .hal_sw2reo_ring_base_lsb = 0x0000013c, ++ .hal_sw2reo_ring_hp = 0x00003018, ++ ++ /* REO CMD ring address */ ++ .hal_reo_cmd_ring_base_lsb = 0x000000e4, ++ .hal_reo_cmd_ring_hp = 0x00003010, ++ ++ /* REO status address */ ++ .hal_reo_status_ring_base_lsb = 0x00000504, ++ .hal_reo_status_hp = 0x00003070, ++ ++ /* WCSS relative address */ ++ .hal_seq_wcss_umac_ce0_src_reg = 0x01b80000, ++ .hal_seq_wcss_umac_ce0_dst_reg = 0x01b81000, ++ .hal_seq_wcss_umac_ce1_src_reg = 0x01b82000, ++ .hal_seq_wcss_umac_ce1_dst_reg = 0x01b83000, ++ ++ /* WBM Idle address */ ++ .hal_wbm_idle_link_ring_base_lsb = 0x00000874, ++ .hal_wbm_idle_link_ring_misc = 0x00000884, ++ ++ /* SW2WBM release address */ ++ .hal_wbm_release_ring_base_lsb = 0x000001ec, ++ ++ /* WBM2SW release address */ ++ .hal_wbm0_release_ring_base_lsb = 0x00000924, ++ .hal_wbm1_release_ring_base_lsb = 0x0000097c, ++}; ++ + const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq8074 = { + .rx_buf_rbm = HAL_RX_BUF_RBM_SW3_BM, + .tcl2wbm_rbm_map = ath11k_hw_tcl2wbm_rbm_map_ipq8074, +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -425,6 +425,7 @@ extern const struct ath11k_hw_regs qcn90 + extern const struct ath11k_hw_regs wcn6855_regs; + extern const struct ath11k_hw_regs wcn6750_regs; + extern const struct ath11k_hw_regs ipq5018_regs; ++extern const struct ath11k_hw_regs qcn6122_regs; + + static inline const char *ath11k_bd_ie_type_str(enum ath11k_bd_ie_type type) + { diff --git a/package/kernel/mac80211/patches/ath11k/922-wifi-ath11k-add-hw-ring-mask-for-QCN6122.patch b/package/kernel/mac80211/patches/ath11k/922-wifi-ath11k-add-hw-ring-mask-for-QCN6122.patch new file mode 100644 index 0000000000..f93a3bc60a --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/922-wifi-ath11k-add-hw-ring-mask-for-QCN6122.patch @@ -0,0 +1,74 @@ +From: George Moussalem +Date: Wed, 27 Oct 2024 16:34:11 +0400 +Subject: [PATCH] wifi: ath11k: add hw ring mask for QCN6122 + +Add ring mask for QCN6122 and register them in hw params. + +Signed-off-by: George Moussalem +--- +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -821,6 +821,7 @@ static struct ath11k_hw_params ath11k_hw + .max_radios = MAX_RADIOS_5018, + .bdf_addr = 0x4D200000, + .hw_ops = &ipq5018_ops, ++ .ring_mask = &ath11k_hw_ring_mask_qcn6122, + .hal_desc_sz = sizeof(struct hal_rx_desc_qcn9074), + .regs = &qcn6122_regs, + .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCN6122, +--- a/drivers/net/wireless/ath/ath11k/hw.c ++++ b/drivers/net/wireless/ath/ath11k/hw.c +@@ -2070,6 +2070,43 @@ const struct ath11k_hw_ring_mask ath11k_ + }, + }; + ++const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn6122 = { ++ .tx = { ++ ATH11K_TX_RING_MASK_0, ++ ATH11K_TX_RING_MASK_1, ++ ATH11K_TX_RING_MASK_2, ++ }, ++ .rx_mon_status = { ++ 0, 0, 0, ++ ATH11K_RX_MON_STATUS_RING_MASK_0, ++ }, ++ .rx = { ++ 0, 0, 0, 0, ++ ATH11K_RX_RING_MASK_0, ++ ATH11K_RX_RING_MASK_1, ++ ATH11K_RX_RING_MASK_2, ++ ATH11K_RX_RING_MASK_3, ++ }, ++ .rx_err = { ++ 0, 0, 0, 0, 0, 0, 0, 0, ++ ATH11K_RX_ERR_RING_MASK_0, ++ }, ++ .rx_wbm_rel = { ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ ATH11K_RX_WBM_REL_RING_MASK_0, ++ }, ++ .reo_status = { ++ 0, 0, 0, ++ ATH11K_REO_STATUS_RING_MASK_0, ++ }, ++ .rxdma2host = { ++ ATH11K_RXDMA2HOST_RING_MASK_0, ++ }, ++ .host2rxdma = { ++ ATH11K_HOST2RXDMA_RING_MASK_0, ++ }, ++}; ++ + /* Target firmware's Copy Engine configuration for IPQ5018 */ + const struct ce_pipe_config ath11k_target_ce_config_wlan_ipq5018[] = { + /* CE0: host->target HTC control and raw streams */ +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -289,6 +289,7 @@ extern const struct ath11k_hw_ring_mask + extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qca6390; + extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn9074; + extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_wcn6750; ++extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qcn6122; + + extern const struct ce_ie_addr ath11k_ce_ie_addr_ipq8074; + extern const struct ce_ie_addr ath11k_ce_ie_addr_ipq5018; diff --git a/package/kernel/mac80211/patches/ath11k/923-wifi-ath11k-update-hif_and-pci_ops-for-QCN6122.patch b/package/kernel/mac80211/patches/ath11k/923-wifi-ath11k-update-hif_and-pci_ops-for-QCN6122.patch new file mode 100644 index 0000000000..9c9caef4f2 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/923-wifi-ath11k-update-hif_and-pci_ops-for-QCN6122.patch @@ -0,0 +1,102 @@ +From: George Moussalem +Date: Wed, 27 Oct 2024 16:34:11 +0400 +Subject: [PATCH] wifi: ath11k: update hif and pci ops for QCN6122 + +Add HIF and PCI ops for QCN6122. QCN6122 by default uses DP window 3. +However, this is configurable, so let's introduce a function to do that and +follow the existing register access code for (hybrid)AHB devices and use +DP window 1. + +Signed-off-by: George Moussalem +--- +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -768,6 +768,18 @@ static int ath11k_ahb_hif_resume(struct + return 0; + } + ++static void ath11k_ahb_config_static_window_qcn6122(struct ath11k_base *ab) ++{ ++ u32 umac_window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, HAL_SEQ_WCSS_UMAC_OFFSET); ++ u32 ce_window = FIELD_GET(ATH11K_PCI_WINDOW_VALUE_MASK, HAL_CE_WFSS_CE_REG_BASE); ++ u32 window; ++ ++ window = (umac_window) | (ce_window << 6); ++ ++ iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | window, ++ ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS); ++} ++ + static const struct ath11k_hif_ops ath11k_ahb_hif_ops_ipq8074 = { + .start = ath11k_ahb_start, + .stop = ath11k_ahb_stop, +@@ -800,6 +812,24 @@ static const struct ath11k_hif_ops ath11 + .ce_irq_disable = ath11k_pci_disable_ce_irqs_except_wake_irq, + }; + ++static const struct ath11k_hif_ops ath11k_ahb_hif_ops_qcn6122 = { ++ .start = ath11k_pcic_start, ++ .stop = ath11k_pcic_stop, ++ .read32 = ath11k_pcic_read32, ++ .write32 = ath11k_pcic_write32, ++ .read = NULL, ++ .irq_enable = ath11k_pcic_ext_irq_enable, ++ .irq_disable = ath11k_pcic_ext_irq_disable, ++ .get_msi_address = ath11k_pcic_get_msi_address, ++ .get_user_msi_vector = ath11k_pcic_get_user_msi_assignment, ++ .map_service_to_pipe = ath11k_pcic_map_service_to_pipe, ++ .power_down = ath11k_ahb_power_down, ++ .power_up = ath11k_ahb_power_up, ++ .ce_irq_enable = ath11k_pci_enable_ce_irqs_except_wake_irq, ++ .ce_irq_disable = ath11k_pci_disable_ce_irqs_except_wake_irq, ++ .config_static_window = ath11k_ahb_config_static_window_qcn6122, ++}; ++ + static int ath11k_core_get_rproc(struct ath11k_base *ab) + { + struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); +@@ -1144,6 +1174,10 @@ static int ath11k_ahb_probe(struct platf + hif_ops = &ath11k_ahb_hif_ops_wcn6750; + pci_ops = &ath11k_ahb_pci_ops_wcn6750; + break; ++ case ATH11K_HW_QCN6122_HW10: ++ hif_ops = &ath11k_ahb_hif_ops_qcn6122; ++ pci_ops = &ath11k_ahb_pci_ops_wcn6750; ++ break; + default: + dev_err(&pdev->dev, "unsupported device type %d\n", hw_rev); + return -EOPNOTSUPP; +--- a/drivers/net/wireless/ath/ath11k/hif.h ++++ b/drivers/net/wireless/ath/ath11k/hif.h +@@ -31,6 +31,7 @@ struct ath11k_hif_ops { + void (*ce_irq_enable)(struct ath11k_base *ab); + void (*ce_irq_disable)(struct ath11k_base *ab); + void (*get_ce_msi_idx)(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx); ++ void (*config_static_window)(struct ath11k_base *ab); + }; + + static inline void ath11k_hif_ce_irq_enable(struct ath11k_base *ab) +@@ -146,4 +147,12 @@ static inline void ath11k_get_ce_msi_idx + *msi_data_idx = ce_id; + } + ++static inline void ath11k_hif_config_static_window(struct ath11k_base *ab) ++{ ++ if (!ab->hw_params.static_window_map || !ab->hif.ops->config_static_window) ++ return; ++ ++ ab->hif.ops->config_static_window(ab); ++} ++ + #endif /* _HIF_H_ */ +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2184,6 +2184,8 @@ static int ath11k_qmi_request_device_inf + ab->mem = bar_addr_va; + ab->mem_len = resp.bar_size; + ++ ath11k_hif_config_static_window(ab); ++ + return 0; + out: + return ret; diff --git a/package/kernel/mac80211/patches/ath11k/924-wifi-ath11k-add-multipd-support-for-QCN6122.patch b/package/kernel/mac80211/patches/ath11k/924-wifi-ath11k-add-multipd-support-for-QCN6122.patch new file mode 100644 index 0000000000..e6170a74e4 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/924-wifi-ath11k-add-multipd-support-for-QCN6122.patch @@ -0,0 +1,110 @@ +From: George Moussalem +Date: Wed, 27 Oct 2024 16:34:11 +0400 +Subject: [PATCH] wifi: ath11k: add multipd support for QCN6122 + +IPQ5018/QCN6122 platforms use multi PD (protection domains) to avoid having +one instance of the running Q6 firmware crashing resulting in crashing the +others. See below patch for more info: +https://lore.kernel.org/all/20231110091939.3025413-1-quic_mmanikan@quicinc.com/ + +The IPQ5018 platform can have multiple (2) QCN6122 wifi cards. To differentiate +the two, the PD instance number (1 or 2) is added to the QMI service instance +ID, which the QCN6122 firmware also expects. IPQ5018 is always the first PD, so +the QCN6122 cards should be the second or third. + +Signed-off-by: George Moussalem +--- +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -435,6 +435,7 @@ static void ath11k_ahb_init_qmi_ce_confi + cfg->svc_to_ce_map_len = ab->hw_params.svc_to_ce_map_len; + cfg->svc_to_ce_map = ab->hw_params.svc_to_ce_map; + ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id; ++ ab->qmi.service_ins_id += ab->userpd_id; + } + + static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab) +@@ -1118,6 +1119,27 @@ err_unregister: + return ret; + } + ++static int ath11k_get_userpd_id(struct device *dev) ++{ ++ int ret; ++ int userpd_id = 0; ++ const char *subsys_name; ++ ++ ret = of_property_read_string(dev->of_node, ++ "qcom,userpd-subsys-name", ++ &subsys_name); ++ if (ret) ++ return 0; ++ ++ if (strcmp(subsys_name, "q6v5_wcss_userpd2") == 0) ++ userpd_id = ATH11K_QCN6122_USERPD_2; ++ else if (strcmp(subsys_name, "q6v5_wcss_userpd3") == 0) ++ userpd_id = ATH11K_QCN6122_USERPD_3; ++ dev_info(dev, "Multipd architecture - userpd: %d\n", userpd_id + 1); ++ ++ return userpd_id; ++} ++ + static int ath11k_ahb_fw_resource_deinit(struct ath11k_base *ab) + { + struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); +@@ -1159,7 +1181,7 @@ static int ath11k_ahb_probe(struct platf + const struct ath11k_hif_ops *hif_ops; + const struct ath11k_pci_ops *pci_ops; + enum ath11k_hw_rev hw_rev; +- int ret; ++ int ret, userpd_id; + + hw_rev = (uintptr_t)device_get_match_data(&pdev->dev); + +@@ -1183,6 +1205,7 @@ static int ath11k_ahb_probe(struct platf + return -EOPNOTSUPP; + } + ++ userpd_id = ath11k_get_userpd_id(&pdev->dev); + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "failed to set 32-bit consistent dma\n"); +@@ -1199,6 +1222,7 @@ static int ath11k_ahb_probe(struct platf + ab->hif.ops = hif_ops; + ab->pdev = pdev; + ab->hw_rev = hw_rev; ++ ab->userpd_id = userpd_id; + ab->fw_mode = ATH11K_FIRMWARE_MODE_NORMAL; + platform_set_drvdata(pdev, ab); + +--- a/drivers/net/wireless/ath/ath11k/core.h ++++ b/drivers/net/wireless/ath/ath11k/core.h +@@ -45,6 +45,9 @@ + #define ATH11K_INVALID_HW_MAC_ID 0xFF + #define ATH11K_CONNECTION_LOSS_HZ (3 * HZ) + ++#define ATH11K_QCN6122_USERPD_2 1 ++#define ATH11K_QCN6122_USERPD_3 2 ++ + /* SMBIOS type containing Board Data File Name Extension */ + #define ATH11K_SMBIOS_BDF_EXT_TYPE 0xF8 + +@@ -945,6 +948,7 @@ struct ath11k_base { + struct list_head peers; + wait_queue_head_t peer_mapping_wq; + u8 mac_addr[ETH_ALEN]; ++ int userpd_id; + int irq_num[ATH11K_IRQ_NUM_MAX]; + struct ath11k_ext_irq_grp ext_irq_grp[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + struct ath11k_targ_cap target_caps; +--- a/drivers/net/wireless/ath/ath11k/pci.c ++++ b/drivers/net/wireless/ath/ath11k/pci.c +@@ -389,6 +389,8 @@ static void ath11k_pci_init_qmi_ce_confi + } else + ab->qmi.service_ins_id = ab->hw_params.qmi_service_ins_id; + ++ ab->qmi.service_ins_id += ab->userpd_id; ++ + ath11k_ce_get_shadow_config(ab, &cfg->shadow_reg_v2, + &cfg->shadow_reg_v2_len); + } diff --git a/package/kernel/mac80211/patches/ath11k/925-wifi-ath11k-add-QCN6122-device-support.patch b/package/kernel/mac80211/patches/ath11k/925-wifi-ath11k-add-QCN6122-device-support.patch new file mode 100644 index 0000000000..58627ce07e --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/925-wifi-ath11k-add-QCN6122-device-support.patch @@ -0,0 +1,55 @@ +From: George Moussalem +Date: Wed, 27 Oct 2024 16:34:11 +0400 +Subject: [PATCH] wifi: ath11k: add QCN6122 device support + +QCN6122 is a 2x2 11AX PCIe based chipset, but it is attached to the WPSS +(Wireless Processor SubSystem) Q6 processor, hence it is enumerated +by the Q6 processor. It is registered to the APSS processor +(Application Processor SubSystem) as a platform device (AHB) and remoteproc +APIs are used to boot up or shutdown the device like other AHB devices. + +Also, device information like BAR and its size is not known to the +APSS processor as the chip is enumerated by WPSS Q6. These details +are fetched over QMI. + +STA, AP, and MESH modes are supported. +Tested on: Linksys MX2000 and GLiNET B3000 access points for prolonged duration +tests spanning multiple days with multiple clients connected with firmware +WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1 + +An important point to note is that though QCN6122 is a PCIe device, +it is an IPQ5018 SoC specific solution and cannot be attached to any other +platform. + +Signed-off-by: George Moussalem +--- +--- a/drivers/net/wireless/ath/ath11k/ahb.c ++++ b/drivers/net/wireless/ath/ath11k/ahb.c +@@ -37,6 +37,9 @@ static const struct of_device_id ath11k_ + { .compatible = "qcom,ipq5018-wifi", + .data = (void *)ATH11K_HW_IPQ5018_HW10, + }, ++ { .compatible = "qcom,qcn6122-wifi", ++ .data = (void *)ATH11K_HW_QCN6122_HW10, ++ }, + { } + }; + +--- a/drivers/net/wireless/ath/ath11k/pcic.c ++++ b/drivers/net/wireless/ath/ath11k/pcic.c +@@ -126,6 +126,15 @@ static const struct ath11k_msi_config at + }, + .hw_rev = ATH11K_HW_QCA2066_HW21, + }, ++ { ++ .total_vectors = 13, ++ .total_users = 2, ++ .users = (struct ath11k_msi_user[]) { ++ { .name = "CE", .num_vectors = 5, .base_vector = 0 }, ++ { .name = "DP", .num_vectors = 8, .base_vector = 5 }, ++ }, ++ .hw_rev = ATH11K_HW_QCN6122_HW10, ++ }, + }; + + int ath11k_pcic_init_msi_config(struct ath11k_base *ab) diff --git a/package/kernel/mac80211/patches/ath11k/931-wifi-ath11k-Support-to-assign-m3-dump-memory.patch b/package/kernel/mac80211/patches/ath11k/931-wifi-ath11k-Support-to-assign-m3-dump-memory.patch new file mode 100644 index 0000000000..834a4b5797 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/931-wifi-ath11k-Support-to-assign-m3-dump-memory.patch @@ -0,0 +1,31 @@ +From 64f6f6cdde0b6b763181145a698207fad4536c06 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Wed, 9 Aug 2023 17:44:49 +0000 +Subject: [PATCH] wifi: ath11k: Support to assign m3 dump memory + +Signed-off-by: Ziyang Huang +--- + drivers/net/wireless/ath/ath11k/qmi.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +--- a/drivers/net/wireless/ath/ath11k/qmi.c ++++ b/drivers/net/wireless/ath/ath11k/qmi.c +@@ -2104,6 +2104,18 @@ static int ath11k_qmi_assign_target_mem_ + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + idx++; + break; ++ case M3_DUMP_REGION_TYPE: ++ if (of_property_read_u32(dev->of_node, "qcom,m3-dump-addr", &addr)) { ++ ath11k_warn(ab, "qmi fail to get qcom,m3-dump-addr, ignore m3 dump mem req\n"); ++ break; ++ } ++ ++ ab->qmi.target_mem[idx].paddr = (phys_addr_t) addr; ++ ab->qmi.target_mem[idx].vaddr = NULL; ++ ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; ++ ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; ++ idx++; ++ break; + default: + ath11k_warn(ab, "qmi ignore invalid mem req type %d\n", + ab->qmi.target_mem[i].type); diff --git a/package/kernel/mac80211/patches/ath11k/932-wifi-ath11k-poll-reo-status-ipq5018.patch b/package/kernel/mac80211/patches/ath11k/932-wifi-ath11k-poll-reo-status-ipq5018.patch new file mode 100644 index 0000000000..d89fb92e22 --- /dev/null +++ b/package/kernel/mac80211/patches/ath11k/932-wifi-ath11k-poll-reo-status-ipq5018.patch @@ -0,0 +1,164 @@ +From d890c6d602307c9297df12c7d0287f9ffd26208b Mon Sep 17 00:00:00 2001 +From: Sriram R +Date: Wed, 12 May 2021 19:21:09 +0530 +Subject: [PATCH] ath11k: poll reo status ring for IPQ5018 + +Currently reo status interrupts are not received +due to wrong mapping of the reo status interrupt +line in IPQ5018. + +Hence, until the mapping is resolved in HW, use +polling to reap the reo status ring. Rather than +a period timer to reap the ring, the timer is +triggered only on sending a reo command with +status request. + +Without proper reaping of the ring, backpressure +and ring full issues are seen in multi client test +setups which leads to flooding the console with +error messages reporting failure to send reo cmds. + +Can be reverted once HW solution is available. + +Signed-off-by: Sriram R +--- +--- a/drivers/net/wireless/ath/ath11k/core.c ++++ b/drivers/net/wireless/ath/ath11k/core.c +@@ -719,6 +719,7 @@ static struct ath11k_hw_params ath11k_hw + .smp2p_wow_exit = false, + .support_fw_mac_sequence = false, + .support_dual_stations = false, ++ .reo_status_poll = true, + }, + { + .name = "qca2066 hw2.1", +--- a/drivers/net/wireless/ath/ath11k/dp.c ++++ b/drivers/net/wireless/ath/ath11k/dp.c +@@ -361,12 +361,66 @@ void ath11k_dp_stop_shadow_timers(struct + ath11k_dp_shadow_stop_timer(ab, &ab->dp.reo_cmd_timer); + } + ++static void ath11k_dp_handle_reo_status_timer(struct timer_list *timer) ++{ ++ struct ath11k_dp *dp = from_timer(dp, timer, reo_status_timer); ++ struct ath11k_base *ab = dp->ab; ++ ++ spin_lock_bh(&dp->reo_cmd_lock); ++ dp->reo_status_timer_running = false; ++ spin_unlock_bh(&dp->reo_cmd_lock); ++ ++ ath11k_dp_process_reo_status(ab); ++} ++ ++void ath11k_dp_start_reo_status_timer(struct ath11k_base *ab) ++{ ++ struct ath11k_dp *dp = &ab->dp; ++ ++ if (!ab->hw_params.reo_status_poll) ++ return; ++ ++ spin_lock_bh(&dp->reo_cmd_lock); ++ if (dp->reo_status_timer_running) { ++ spin_unlock_bh(&dp->reo_cmd_lock); ++ return; ++ } ++ dp->reo_status_timer_running = true; ++ spin_unlock_bh(&dp->reo_cmd_lock); ++ ++ mod_timer(&dp->reo_status_timer, jiffies + ++ msecs_to_jiffies(ATH11K_REO_STATUS_POLL_TIMEOUT_MS)); ++} ++ ++static void ath11k_dp_stop_reo_status_timer(struct ath11k_base *ab) ++{ ++ struct ath11k_dp *dp = &ab->dp; ++ ++ if (!ab->hw_params.reo_status_poll) ++ return; ++ ++ del_timer_sync(&dp->reo_status_timer); ++ dp->reo_status_timer_running = false; ++} ++ ++static void ath11k_dp_init_reo_status_timer(struct ath11k_base *ab) ++{ ++ struct ath11k_dp *dp = &ab->dp; ++ ++ if (!ab->hw_params.reo_status_poll) ++ return; ++ ++ timer_setup(&dp->reo_status_timer, ++ ath11k_dp_handle_reo_status_timer, 0); ++} ++ + static void ath11k_dp_srng_common_cleanup(struct ath11k_base *ab) + { + struct ath11k_dp *dp = &ab->dp; + int i; + + ath11k_dp_stop_shadow_timers(ab); ++ ath11k_dp_stop_reo_status_timer(ab); + ath11k_dp_srng_cleanup(ab, &dp->wbm_desc_rel_ring); + ath11k_dp_srng_cleanup(ab, &dp->tcl_cmd_ring); + ath11k_dp_srng_cleanup(ab, &dp->tcl_status_ring); +@@ -388,6 +442,8 @@ static int ath11k_dp_srng_common_setup(s + int i, ret; + u8 tcl_num, wbm_num; + ++ ath11k_dp_init_reo_status_timer(ab); ++ + ret = ath11k_dp_srng_setup(ab, &dp->wbm_desc_rel_ring, + HAL_SW2WBM_RELEASE, 0, 0, + DP_WBM_RELEASE_RING_SIZE); +--- a/drivers/net/wireless/ath/ath11k/dp.h ++++ b/drivers/net/wireless/ath/ath11k/dp.h +@@ -44,6 +44,8 @@ struct dp_rx_tid { + #define DP_MON_PURGE_TIMEOUT_MS 100 + #define DP_MON_SERVICE_BUDGET 128 + ++#define ATH11K_REO_STATUS_POLL_TIMEOUT_MS 10 ++ + struct dp_reo_cache_flush_elem { + struct list_head list; + struct dp_rx_tid data; +@@ -286,6 +288,10 @@ struct ath11k_dp { + spinlock_t reo_cmd_lock; + struct ath11k_hp_update_timer reo_cmd_timer; + struct ath11k_hp_update_timer tx_ring_timer[DP_TCL_NUM_RING_MAX]; ++ ++ /* reo status timer and flags */ ++ struct timer_list reo_status_timer; ++ bool reo_status_timer_running; + }; + + /* HTT definitions */ +@@ -1712,5 +1718,6 @@ void ath11k_dp_shadow_init_timer(struct + struct ath11k_hp_update_timer *update_timer, + u32 interval, u32 ring_id); + void ath11k_dp_stop_shadow_timers(struct ath11k_base *ab); ++void ath11k_dp_start_reo_status_timer(struct ath11k_base *ab); + + #endif +--- a/drivers/net/wireless/ath/ath11k/dp_tx.c ++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c +@@ -787,6 +787,10 @@ int ath11k_dp_tx_send_reo_cmd(struct ath + if (cmd_num == 0) + return -EINVAL; + ++ /* Trigger reo status polling if required */ ++ if (cmd->flag & HAL_REO_CMD_FLG_NEED_STATUS) ++ ath11k_dp_start_reo_status_timer(ab); ++ + if (!cb) + return 0; + +--- a/drivers/net/wireless/ath/ath11k/hw.h ++++ b/drivers/net/wireless/ath/ath11k/hw.h +@@ -232,6 +232,7 @@ struct ath11k_hw_params { + bool smp2p_wow_exit; + bool support_fw_mac_sequence; + bool support_dual_stations; ++ bool reo_status_poll; + }; + + struct ath11k_hw_ops { diff --git a/package/kernel/qca-nss-dp/patches/0012-01-syn-gmac-use-standard-DMA-api.patch b/package/kernel/qca-nss-dp/patches/0012-01-syn-gmac-use-standard-DMA-api.patch new file mode 100644 index 0000000000..882bc8cad9 --- /dev/null +++ b/package/kernel/qca-nss-dp/patches/0012-01-syn-gmac-use-standard-DMA-api.patch @@ -0,0 +1,245 @@ +From 5ad8cf24897ff903112967a9662cb13ed4cbbf57 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Mon, 22 Apr 2024 21:47:58 +0800 +Subject: [PATCH] syn-gmac: use standard DMA api + +The SSDK uses several old DMA API calls based on raw assembly which have +been deprecated. So let's convert to and use the standard Linux DMA API +instead. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + hal/dp_ops/syn_gmac_dp/syn_dp_cfg_rx.c | 14 ++++++-- + hal/dp_ops/syn_gmac_dp/syn_dp_cfg_tx.c | 2 ++ + hal/dp_ops/syn_gmac_dp/syn_dp_rx.c | 47 +++++++++++++------------- + hal/dp_ops/syn_gmac_dp/syn_dp_tx.c | 23 ++++--------- + 4 files changed, 42 insertions(+), 44 deletions(-) + +diff --git a/hal/dp_ops/syn_gmac_dp/syn_dp_cfg_rx.c b/hal/dp_ops/syn_gmac_dp/syn_dp_cfg_rx.c +index 8cbbcaaf..1c9006c7 100644 +--- a/hal/dp_ops/syn_gmac_dp/syn_dp_cfg_rx.c ++++ b/hal/dp_ops/syn_gmac_dp/syn_dp_cfg_rx.c +@@ -26,6 +26,7 @@ static int syn_dp_cfg_rx_setup_desc_queue(struct syn_dp_info *dev_info) + { + struct syn_dp_info_rx *rx_info = &dev_info->dp_info_rx; + struct dma_desc_rx *first_desc = NULL; ++ dma_addr_t dma_addr; + struct net_device *netdev = rx_info->netdev; + + netdev_dbg(netdev, "Total size of memory required for Rx Descriptors in Ring Mode = %u\n", (uint32_t)((sizeof(struct dma_desc_rx) * SYN_DP_RX_DESC_SIZE))); +@@ -33,13 +34,15 @@ static int syn_dp_cfg_rx_setup_desc_queue(struct syn_dp_info *dev_info) + /* + * Allocate cacheable descriptors for Rx + */ +- first_desc = kzalloc(sizeof(struct dma_desc_rx) * SYN_DP_RX_DESC_SIZE, GFP_KERNEL); ++ first_desc = dma_alloc_coherent(rx_info->dev, ++ sizeof(struct dma_desc_rx) * SYN_DP_RX_DESC_SIZE, ++ &dma_addr, GFP_KERNEL); + if (!first_desc) { + netdev_dbg(netdev, "Error in Rx Descriptor Memory allocation in Ring mode\n"); + return -ENOMEM; + } + +- dev_info->rx_desc_dma_addr = (dma_addr_t)virt_to_phys(first_desc); ++ dev_info->rx_desc_dma_addr = dma_addr; + rx_info->rx_desc = first_desc; + syn_dp_gmac_rx_desc_init_ring(rx_info->rx_desc, SYN_DP_RX_DESC_SIZE); + +@@ -98,6 +101,10 @@ void syn_dp_cfg_rx_cleanup_rings(struct syn_dp_info *dev_info) + for (i = 0; i < rx_info->busy_rx_desc_cnt; i++) { + rx_skb_index = (rx_skb_index + i) & SYN_DP_RX_DESC_MAX_INDEX; + rxdesc = rx_info->rx_desc; ++ ++ dma_unmap_single(rx_info->dev, rxdesc->buffer1, ++ rxdesc->length, DMA_FROM_DEVICE); ++ + skb = rx_info->rx_buf_pool[rx_skb_index].skb; + if (unlikely(skb != NULL)) { + dev_kfree_skb_any(skb); +@@ -105,7 +112,8 @@ void syn_dp_cfg_rx_cleanup_rings(struct syn_dp_info *dev_info) + } + } + +- kfree(rx_info->rx_desc); ++ dma_free_coherent(rx_info->dev, (sizeof(struct dma_desc_rx) * SYN_DP_RX_DESC_SIZE), ++ rx_info->rx_desc, dev_info->rx_desc_dma_addr); + rx_info->rx_desc = NULL; + dev_info->rx_desc_dma_addr = (dma_addr_t)0; + } +diff --git a/hal/dp_ops/syn_gmac_dp/syn_dp_cfg_tx.c b/hal/dp_ops/syn_gmac_dp/syn_dp_cfg_tx.c +index bf5e19a0..284e8880 100644 +--- a/hal/dp_ops/syn_gmac_dp/syn_dp_cfg_tx.c ++++ b/hal/dp_ops/syn_gmac_dp/syn_dp_cfg_tx.c +@@ -91,6 +91,8 @@ void syn_dp_cfg_tx_cleanup_rings(struct syn_dp_info *dev_info) + tx_skb_index = syn_dp_tx_inc_index(tx_skb_index, i); + txdesc = tx_info->tx_desc; + ++ dma_unmap_single(tx_info->dev, txdesc->buffer1, txdesc->length, DMA_TO_DEVICE); ++ + skb = tx_info->tx_buf_pool[tx_skb_index].skb; + if (unlikely(skb != NULL)) { + dev_kfree_skb_any(skb); +diff --git a/hal/dp_ops/syn_gmac_dp/syn_dp_rx.c b/hal/dp_ops/syn_gmac_dp/syn_dp_rx.c +index 1ddeb7d6..1798d4e7 100644 +--- a/hal/dp_ops/syn_gmac_dp/syn_dp_rx.c ++++ b/hal/dp_ops/syn_gmac_dp/syn_dp_rx.c +@@ -73,16 +73,6 @@ static inline void syn_dp_rx_refill_one_desc(struct dma_desc_rx *rx_desc, + */ + static inline void syn_dp_rx_inval_and_flush(struct syn_dp_info_rx *rx_info, uint32_t start, uint32_t end) + { +- /* +- * Batched flush and invalidation of the rx descriptors +- */ +- if (end > start) { +- dmac_flush_range_no_dsb((void *)&rx_info->rx_desc[start], (void *)&rx_info->rx_desc[end] + sizeof(struct dma_desc_rx)); +- } else { +- dmac_flush_range_no_dsb((void *)&rx_info->rx_desc[start], (void *)&rx_info->rx_desc[SYN_DP_RX_DESC_MAX_INDEX] + sizeof(struct dma_desc_rx)); +- dmac_flush_range_no_dsb((void *)&rx_info->rx_desc[0], (void *)&rx_info->rx_desc[end] + sizeof(struct dma_desc_rx)); +- } +- + dsb(st); + } + +@@ -124,15 +114,19 @@ int syn_dp_rx_refill_page_mode(struct syn_dp_info_rx *rx_info) + break; + } + ++ skb_fill_page_desc(skb, 0, pg, 0, PAGE_SIZE); ++ + /* + * Get virtual address of allocated page. + */ + page_addr = page_address(pg); +- dma_addr = (dma_addr_t)virt_to_phys(page_addr); +- +- skb_fill_page_desc(skb, 0, pg, 0, PAGE_SIZE); ++ dma_addr = dma_map_page(rx_info->dev, pg, 0, PAGE_SIZE, DMA_FROM_DEVICE); ++ if (unlikely(dma_mapping_error(rx_info->dev, dma_addr))) { ++ dev_kfree_skb(skb); ++ netdev_dbg(netdev, "DMA mapping failed for empty buffer\n"); ++ break; ++ } + +- dmac_inv_range_no_dsb(page_addr, (page_addr + PAGE_SIZE)); + rx_refill_idx = rx_info->rx_refill_idx; + rx_desc = rx_info->rx_desc + rx_refill_idx; + +@@ -181,8 +175,15 @@ int syn_dp_rx_refill(struct syn_dp_info_rx *rx_info) + + skb_reserve(skb, SYN_DP_SKB_HEADROOM + NET_IP_ALIGN); + +- dma_addr = (dma_addr_t)virt_to_phys(skb->data); +- dmac_inv_range_no_dsb((void *)skb->data, (void *)(skb->data + inval_len)); ++ dma_addr = dma_map_single(rx_info->dev, skb->data, ++ inval_len, ++ DMA_FROM_DEVICE); ++ if (unlikely(dma_mapping_error(rx_info->dev, dma_addr))) { ++ dev_kfree_skb(skb); ++ netdev_dbg(netdev, "DMA mapping failed for empty buffer\n"); ++ break; ++ } ++ + rx_refill_idx = rx_info->rx_refill_idx; + rx_desc = rx_info->rx_desc + rx_refill_idx; + +@@ -407,12 +408,6 @@ int syn_dp_rx(struct syn_dp_info_rx *rx_info, int budget) + * this code is executing. + */ + end = syn_dp_rx_inc_index(rx_info->rx_idx, busy); +- if (end > start) { +- dmac_inv_range_no_dsb((void *)&rx_info->rx_desc[start], (void *)&rx_info->rx_desc[end] + sizeof(struct dma_desc_rx)); +- } else { +- dmac_inv_range_no_dsb((void *)&rx_info->rx_desc[start], (void *)&rx_info->rx_desc[SYN_DP_RX_DESC_MAX_INDEX] + sizeof(struct dma_desc_rx)); +- dmac_inv_range_no_dsb((void *)&rx_info->rx_desc[0], (void *)&rx_info->rx_desc[end] + sizeof(struct dma_desc_rx)); +- } + + dsb(st); + +@@ -439,8 +434,12 @@ int syn_dp_rx(struct syn_dp_info_rx *rx_info, int budget) + * speculative prefetch by CPU may have occurred. + */ + frame_length = syn_dp_gmac_get_rx_desc_frame_length(status); +- dmac_inv_range((void *)rx_buf->map_addr_virt, +- (void *)(((uint8_t *)rx_buf->map_addr_virt) + frame_length)); ++ if (likely(!rx_info->page_mode)) ++ dma_unmap_single(rx_info->dev, rx_desc->buffer1, ++ rx_info->alloc_buf_len, DMA_FROM_DEVICE); ++ else ++ dma_unmap_page(rx_info->dev, rx_desc->buffer1, ++ PAGE_SIZE, DMA_FROM_DEVICE); + prefetch((void *)rx_buf->map_addr_virt); + + rx_next_idx = syn_dp_rx_inc_index(rx_idx, 1); +diff --git a/hal/dp_ops/syn_gmac_dp/syn_dp_tx.c b/hal/dp_ops/syn_gmac_dp/syn_dp_tx.c +index c97e252b..6d4adb3f 100644 +--- a/hal/dp_ops/syn_gmac_dp/syn_dp_tx.c ++++ b/hal/dp_ops/syn_gmac_dp/syn_dp_tx.c +@@ -104,9 +104,7 @@ static inline struct dma_desc_tx *syn_dp_tx_process_nr_frags(struct syn_dp_info_ + BUG_ON(!length); + #endif + +- dma_addr = (dma_addr_t)virt_to_phys(frag_addr); +- +- dmac_clean_range_no_dsb(frag_addr, frag_addr + length); ++ dma_addr = dma_map_single(tx_info->dev, frag_addr, length, DMA_TO_DEVICE); + + *total_length += length; + tx_desc = syn_dp_tx_set_desc_sg(tx_info, dma_addr, length, DESC_OWN_BY_DMA); +@@ -150,8 +148,7 @@ int syn_dp_tx_nr_frags(struct syn_dp_info_tx *tx_info, struct sk_buff *skb) + /* + * Flush the dma for non-paged skb data + */ +- dma_addr = (dma_addr_t)virt_to_phys(skb->data); +- dmac_clean_range_no_dsb((void *)skb->data, (void *)(skb->data + length)); ++ dma_addr = dma_map_single(tx_info->dev, skb->data, length, DMA_TO_DEVICE); + + total_len = length; + +@@ -256,12 +253,7 @@ int syn_dp_tx_frag_list(struct syn_dp_info_tx *tx_info, struct sk_buff *skb) + return NETDEV_TX_BUSY; + } + +- dma_addr = (dma_addr_t)virt_to_phys(skb->data); +- +- /* +- * Flush the data area of the head skb +- */ +- dmac_clean_range_no_dsb((void *)skb->data, (void *)(skb->data + length)); ++ dma_addr = dma_map_single(tx_info->dev, skb->data, length, DMA_TO_DEVICE); + + total_len = length; + +@@ -290,9 +282,7 @@ int syn_dp_tx_frag_list(struct syn_dp_info_tx *tx_info, struct sk_buff *skb) + BUG_ON(!length); + #endif + +- dma_addr = (dma_addr_t)virt_to_phys(iter_skb->data); +- +- dmac_clean_range_no_dsb((void *)iter_skb->data, (void *)(iter_skb->data + length)); ++ dma_addr = dma_map_single(tx_info->dev, iter_skb->data, length, DMA_TO_DEVICE); + + total_len += length; + +@@ -445,6 +435,7 @@ int syn_dp_tx_complete(struct syn_dp_info_tx *tx_info, int budget) + break; + } + ++ dma_unmap_single(tx_info->dev, desc->buffer1, desc->length, DMA_TO_DEVICE); + + if (likely(status & DESC_TX_LAST)) { + tx_skb_index = syn_dp_tx_comp_index_get(tx_info); +@@ -571,9 +562,7 @@ int syn_dp_tx(struct syn_dp_info_tx *tx_info, struct sk_buff *skb) + return NETDEV_TX_BUSY; + } + +- dma_addr = (dma_addr_t)virt_to_phys(skb->data); +- +- dmac_clean_range_no_dsb((void *)skb->data, (void *)(skb->data + skb->len)); ++ dma_addr = dma_map_single(tx_info->dev, skb->data, skb->len, DMA_TO_DEVICE); + + /* + * Queue packet to the GMAC rings +-- +2.40.1 + diff --git a/package/kernel/qca-nss-dp/patches/0012-02-ipq50xx-use-corrent-scm-function-to-write-tcsr.patch b/package/kernel/qca-nss-dp/patches/0012-02-ipq50xx-use-corrent-scm-function-to-write-tcsr.patch new file mode 100644 index 0000000000..10a8eef6da --- /dev/null +++ b/package/kernel/qca-nss-dp/patches/0012-02-ipq50xx-use-corrent-scm-function-to-write-tcsr.patch @@ -0,0 +1,46 @@ +From ba430b1a512dc1972807a1dd5a8d31a78ac572ff Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Mon, 22 Apr 2024 21:49:18 +0800 +Subject: [PATCH] ipq50xx: use corrent scm function to write tcsr + +QCA leverages its own API function to write to the TCSR register space, +not available upstream. As such, convert to the upstream qcom SCM IO +write function. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + hal/soc_ops/ipq50xx/nss_ipq50xx.c | 9 ++------- + 1 file changed, 2 insertions(+), 7 deletions(-) + +diff --git a/hal/soc_ops/ipq50xx/nss_ipq50xx.c b/hal/soc_ops/ipq50xx/nss_ipq50xx.c +index 3e4491c0..e56de1cc 100644 +--- a/hal/soc_ops/ipq50xx/nss_ipq50xx.c ++++ b/hal/soc_ops/ipq50xx/nss_ipq50xx.c +@@ -18,7 +18,7 @@ + + #include + #include +-#include ++#include + #include "nss_dp_hal.h" + + /* +@@ -78,13 +78,8 @@ static void nss_dp_hal_tcsr_set(void) + * If TZ is not enabled, we can write to the register directly. + */ + if (qcom_scm_is_available()) { +-#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) +- err = qcom_scm_tcsr_reg_write((tcsr_base + TCSR_GMAC_AXI_CACHE_OVERRIDE_OFFSET), ++ err = qcom_scm_io_writel((tcsr_base + TCSR_GMAC_AXI_CACHE_OVERRIDE_OFFSET), + TCSR_GMAC_AXI_CACHE_OVERRIDE_VALUE); +-#else +- err = qti_scm_tcsr_reg_write((tcsr_base + TCSR_GMAC_AXI_CACHE_OVERRIDE_OFFSET), +- TCSR_GMAC_AXI_CACHE_OVERRIDE_VALUE); +-#endif + if (err) { + pr_err("%s: SCM TCSR write error: %d\n", __func__, err); + } +-- +2.40.1 + diff --git a/package/kernel/qca-nss-dp/patches/0013-nss_dp_main-support-fixed-link.patch b/package/kernel/qca-nss-dp/patches/0013-nss_dp_main-support-fixed-link.patch new file mode 100644 index 0000000000..668046881a --- /dev/null +++ b/package/kernel/qca-nss-dp/patches/0013-nss_dp_main-support-fixed-link.patch @@ -0,0 +1,49 @@ +From 309a1a330ccaa103a7648e944d97a0032116b338 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Mon, 22 Apr 2024 21:50:39 +0800 +Subject: [PATCH] nss_dp_main: support fixed-link + +Add support for "fixed link" to support ethernet MACs which are not +connected to an MDIO managed PHY. For example, IPQ5018 has two internal +MACs, one connected to a GE PHY and the other connected to a Uniphy to +support connections to an external switch over a fixed link. As such, +check for a fixed link in absence of a phy-handle and return an error +when no phy-handle and fixed link is found. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + nss_dp_main.c | 15 ++++++++++++--- + 1 file changed, 12 insertions(+), 3 deletions(-) + +diff --git a/nss_dp_main.c b/nss_dp_main.c +index 9a09edd5..204063bf 100644 +--- a/nss_dp_main.c ++++ b/nss_dp_main.c +@@ -619,11 +619,20 @@ static int32_t nss_dp_of_get_pdata(struct device_node *np, + } + + dp_priv->phy_node = of_parse_phandle(np, "phy-handle", 0); +- if (!dp_priv->phy_node) { +- pr_err("%s: error parsing phy-handle\n", np->name); +- return -EFAULT; ++ if(!dp_priv->phy_node) { ++ if(of_phy_is_fixed_link(np)) { ++ int ret = of_phy_register_fixed_link(np); ++ if(ret < 0) { ++ pr_err("%s: fail to register fixed-link: %d\n", np->name, ret); ++ return -EFAULT; ++ } ++ } ++ dp_priv->phy_node = of_node_get(np); + } + ++ if(!dp_priv->phy_node) ++ pr_err("%s: no phy-handle or fixed-link found\n", np->name); ++ + if (of_property_read_u32(np, "qcom,mactype", &hal_pdata->mactype)) { + pr_err("%s: error reading mactype\n", np->name); + return -EFAULT; +-- +2.40.1 + diff --git a/package/kernel/qca-ssdk/Makefile b/package/kernel/qca-ssdk/Makefile index 16b2b4477d..032f0de331 100644 --- a/package/kernel/qca-ssdk/Makefile +++ b/package/kernel/qca-ssdk/Makefile @@ -49,7 +49,7 @@ MAKE_FLAGS+= \ PTP_FEATURE=disable SWCONFIG_FEATURE=disable \ ISISC_ENABLE=disable MHT_ENABLE=disable \ IN_QCA803X_PHY=FALSE IN_QCA808X_PHY=FALSE \ - IN_MALIBU_PHY=FALSE \ + IN_MALIBU_PHY=FALSE IN_MP_PHY=FALSE \ $(LNX_CONFIG_OPTS) ifeq ($(CONFIG_TARGET_SUBTARGET), "ipq807x") @@ -60,6 +60,9 @@ ifeq ($(CONFIG_TARGET_SUBTARGET), "ipq60xx") MAKE_FLAGS+= CHIP_TYPE=CPPE endif +ifeq ($(CONFIG_TARGET_SUBTARGET), "ipq50xx") + MAKE_FLAGS+= CHIP_TYPE=MP +endif define Build/Compile +$(MAKE) $(PKG_JOBS) $(MAKE_FLAGS) -C $(PKG_BUILD_DIR) $(LNX_CONFIG_OPTS) diff --git a/package/kernel/qca-ssdk/patches/111-hsl_phy-split-MP_PHY-config.patch b/package/kernel/qca-ssdk/patches/111-hsl_phy-split-MP_PHY-config.patch new file mode 100644 index 0000000000..8dd18c08e0 --- /dev/null +++ b/package/kernel/qca-ssdk/patches/111-hsl_phy-split-MP_PHY-config.patch @@ -0,0 +1,127 @@ +From 15847e1f56b7f9423095cd96fd9d524a41bee814 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 15:24:07 +0800 +Subject: [PATCH] hsl_phy: split MP_PHY config + +Compiling the MP_PHY driver for ipq50xx is disabled in the Makefile in +favor of a native driver being upstreamed. As such, conditionally disable +unneeded flags and code associated to initializing the MP GE PHY that +would otherwise conflict with the native driver. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + config | 1 + + make/linux_opt.mk | 5 +++++ + src/adpt/mp/adpt_mp_portctrl.c | 4 ++++ + src/hsl/phy/Makefile | 8 +------- + src/hsl/phy/hsl_phy.c | 4 ++-- + 5 files changed, 13 insertions(+), 9 deletions(-) + +diff --git a/config b/config +index 99d99dff..1f74e4f8 100644 +--- a/config ++++ b/config +@@ -299,6 +299,7 @@ else ifeq (DESS, $(CHIP_TYPE)) + else ifeq (MP, $(CHIP_TYPE)) + IN_QCA803X_PHY=TRUE + IN_QCA808X_PHY=TRUE ++ IN_MP_PHY=TRUE + IN_SFP_PHY=TRUE + IN_SFP=TRUE + else ifeq (APPE, $(CHIP_TYPE)) +diff --git a/make/linux_opt.mk b/make/linux_opt.mk +index 6936b754..66b08ef5 100644 +--- a/make/linux_opt.mk ++++ b/make/linux_opt.mk +@@ -183,6 +183,11 @@ endif + ifeq (TRUE, $(IN_QCA808X_PHY)) + MODULE_CFLAG += -DIN_QCA808X_PHY + endif ++ ++ifeq (TRUE, $(IN_MP_PHY)) ++ MODULE_CFLAG += -DIN_MP_PHY ++endif ++ + ifeq (TRUE, $(IN_SFP_PHY)) + MODULE_CFLAG += -DIN_SFP_PHY + endif +diff --git a/src/adpt/mp/adpt_mp_portctrl.c b/src/adpt/mp/adpt_mp_portctrl.c +index 2c983fff..db60fc72 100644 +--- a/src/adpt/mp/adpt_mp_portctrl.c ++++ b/src/adpt/mp/adpt_mp_portctrl.c +@@ -92,12 +92,15 @@ static sw_error_t + adpt_mp_port_reset_set(a_uint32_t dev_id, a_uint32_t port_id) + { + sw_error_t rv = 0; ++#ifdef IN_MP_PHY + a_uint32_t phy_addr; + hsl_phy_ops_t *phy_drv; ++#endif + + ADPT_DEV_ID_CHECK(dev_id); + + if (port_id == SSDK_PHYSICAL_PORT1) { ++#ifdef IN_MP_PHY + /*internal gephy reset*/ + SW_RTN_ON_NULL (phy_drv = hsl_phy_api_ops_get(dev_id, + port_id)); +@@ -107,6 +110,7 @@ adpt_mp_port_reset_set(a_uint32_t dev_id, a_uint32_t port_id) + SW_RTN_ON_ERROR (rv); + rv = phy_drv->phy_function_reset(dev_id, phy_addr, PHY_FIFO_RESET); + SW_RTN_ON_ERROR (rv); ++#endif + } else if (port_id == SSDK_PHYSICAL_PORT2) { + rv = adpt_mp_uniphy_adapter_port_reset(dev_id, port_id); + } else { +diff --git a/src/hsl/phy/Makefile b/src/hsl/phy/Makefile +index 68d0679f..0eae9377 100755 +--- a/src/hsl/phy/Makefile ++++ b/src/hsl/phy/Makefile +@@ -23,7 +23,7 @@ ifeq (ISIS, $(CHIP_TYPE)) + SRC_LIST = f1_phy.c + endif + +-ifeq (MP, $(CHIP_TYPE)) ++ifeq (TRUE, $(IN_MP_PHY)) + SRC_LIST = mpge_phy.c + ifeq (TRUE, $(IN_LED)) + SRC_LIST += mpge_led.c +@@ -40,12 +40,6 @@ endif + + ifeq (ALL_CHIP, $(CHIP_TYPE)) + SRC_LIST = f1_phy.c f2_phy.c malibu_phy.c +-ifneq (,$(filter MP, $(SUPPORT_CHIP))) +- SRC_LIST += mpge_phy.c +-ifeq (TRUE, $(IN_LED)) +- SRC_LIST += mpge_led.c +-endif +-endif + endif + + ifeq (NONHK_CHIP, $(CHIP_TYPE)) +diff --git a/src/hsl/phy/hsl_phy.c b/src/hsl/phy/hsl_phy.c +index f2cf90e2..efab2343 100644 +--- a/src/hsl/phy/hsl_phy.c ++++ b/src/hsl/phy/hsl_phy.c +@@ -28,7 +28,7 @@ + #if defined(ATHENA) ||defined(SHIVA) ||defined(HORUS) + #include + #endif +-#ifdef MP ++#ifdef IN_MP_PHY + #include "mpge_phy.h" + #endif + #ifdef IN_MALIBU_PHY +@@ -94,7 +94,7 @@ phy_driver_instance_t ssdk_phy_driver[] = + #else + {SFP_PHY_CHIP, {0}, NULL, NULL, NULL}, + #endif +- #ifdef MP ++ #ifdef IN_MP_PHY + {MPGE_PHY_CHIP, {0}, NULL, mpge_phy_init, NULL}, + #else + {MPGE_PHY_CHIP, {0}, NULL, NULL, NULL}, +-- +2.40.1 + diff --git a/package/kernel/qca-ssdk/patches/112-init-MP-allow-to-ignore-reset-controlls.patch b/package/kernel/qca-ssdk/patches/112-init-MP-allow-to-ignore-reset-controlls.patch new file mode 100644 index 0000000000..616ccea252 --- /dev/null +++ b/package/kernel/qca-ssdk/patches/112-init-MP-allow-to-ignore-reset-controlls.patch @@ -0,0 +1,40 @@ +From 01fb404dbda1872ad99cea88bf43313bed30200a Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 15:24:07 +0800 +Subject: [PATCH] init: MP: allow to ignore reset controlls + +The SSDK is not used anymore to initialize the internal IPQ5018 GE PHY as +there is a separate driver pending upstream review/approval: +https://lore.kernel.org/all/TYZPR01MB5556D5568546D6DA4313209EC9762@ \ +TYZPR01MB5556.apcprd01.prod.exchangelabs.com/ + +As such, change the code to not error out when the reset controls aren't +found in the DTS where the SSDK expects them. These resets are now defined +under the definition based on the new driver mentioned above. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + src/init/ssdk_clk.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/src/init/ssdk_clk.c b/src/init/ssdk_clk.c +index 71e59452..bc244c6e 100644 +--- a/src/init/ssdk_clk.c ++++ b/src/init/ssdk_clk.c +@@ -1282,10 +1282,8 @@ ssdk_mp_reset_init(void) + + for (i = 0; i < MP_BCR_RST_MAX; i++) { + rst = of_reset_control_get(rst_node, mp_rst_ids[i]); +- if (IS_ERR(rst)) { +- SSDK_ERROR("%s not exist!\n", mp_rst_ids[i]); +- return; +- } ++ if (IS_ERR(rst)) ++ continue; + ssdk_gcc_reset(rst, SSDK_RESET_ASSERT); + msleep(200); + ssdk_gcc_reset(rst, SSDK_RESET_DEASSERT); +-- +2.40.1 + diff --git a/package/kernel/qca-ssdk/patches/121-MP-fix-build-issues.patch b/package/kernel/qca-ssdk/patches/121-MP-fix-build-issues.patch new file mode 100644 index 0000000000..14fe42bc73 --- /dev/null +++ b/package/kernel/qca-ssdk/patches/121-MP-fix-build-issues.patch @@ -0,0 +1,82 @@ +From a4378eb29c7b9dd95601d20f507a2220457f8ede Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 15:24:07 +0800 +Subject: [PATCH] MP: fix build issues + +Enable the IN_VSI make flag which enables macro definitions needed to +successfully compile the SSDK for the ipq50xx target. In addition, fix an +incorrect return type by expanding the macro called and return a boolean +instead of an integer. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + config | 30 +----------------------------- + src/adpt/mp/adpt_mp_portctrl.c | 3 ++- + 2 files changed, 3 insertions(+), 30 deletions(-) + +diff --git a/config b/config +index 1f74e4f8..58d67648 100644 +--- a/config ++++ b/config +@@ -374,6 +374,7 @@ ifneq (, $(filter MPPE APPE HPPE CPPE ALL_CHIP, $(CHIP_TYPE))) + endif + + ifneq (, $(filter MP, $(CHIP_TYPE))) ++ IN_VSI=TRUE + IN_UNIPHY=TRUE + endif + +@@ -436,35 +437,6 @@ endif + # SDK Features According To Specfic Switch # + ############################################# + ifeq (MP, $(CHIP_TYPE)) +- ifeq (disable, $(ISISC_ENABLE)) +- IN_ACL=FALSE +- IN_FDB=FALSE +- IN_IGMP=FALSE +- IN_LEAKY=FALSE +- IN_LED=FALSE +- IN_MIRROR=FALSE +- IN_MISC=FALSE +- IN_PORTVLAN=FALSE +- IN_QOS=FALSE +- IN_RATE=FALSE +- IN_STP=FALSE +- IN_VLAN=FALSE +- IN_REDUCED_ACL=FALSE +- IN_COSMAP=FALSE +- IN_IP=FALSE +- IN_NAT=FALSE +- IN_FLOW=FALSE +- IN_TRUNK=FALSE +- IN_RSS_HASH=FALSE +- IN_SEC=FALSE +- IN_QM=FALSE +- IN_PPPOE=FALSE +- IN_VSI=FALSE +- IN_SERVCODE=FALSE +- IN_BM=FALSE +- IN_SHAPER=FALSE +- IN_POLICER=FALSE +- endif + IN_CTRLPKT=TRUE + endif + +diff --git a/src/adpt/mp/adpt_mp_portctrl.c b/src/adpt/mp/adpt_mp_portctrl.c +index db60fc72..c230e214 100644 +--- a/src/adpt/mp/adpt_mp_portctrl.c ++++ b/src/adpt/mp/adpt_mp_portctrl.c +@@ -45,7 +45,8 @@ _adpt_mp_gcc_mac_clock_set(a_uint32_t dev_id, + static a_bool_t + _adpt_mp_port_phy_connected (a_uint32_t dev_id, fal_port_t port_id) + { +- ADPT_DEV_ID_CHECK(dev_id); ++ if (dev_id >= SW_MAX_NR_DEV) ++ return A_FALSE; + + /* force port which connect s17c or other device chip*/ + if (hsl_port_feature_get(dev_id, port_id, PHY_F_FORCE | PHY_F_SFP)) { +-- +2.40.1 + diff --git a/package/kernel/qca-ssdk/patches/122-init-replace-ioremap_nocache-with-ioremap.patch b/package/kernel/qca-ssdk/patches/122-init-replace-ioremap_nocache-with-ioremap.patch new file mode 100644 index 0000000000..1e1e909bdb --- /dev/null +++ b/package/kernel/qca-ssdk/patches/122-init-replace-ioremap_nocache-with-ioremap.patch @@ -0,0 +1,57 @@ +From a90a9f3e2a21cb87c2cbf2ddb999846aa614e88a Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 15:24:07 +0800 +Subject: [PATCH 2/2] init: replace ioremap_nocache() with ioremap() + +As per https://lore.kernel.org/linux-mips/20191209194819.GA28157@lst.de/T/, +ioremap_nocache is deprecated so let's replace all calls by ioremap instead. + +Signed-off-by: Ziyang Huang +Signed-off-by: George Moussalem +--- + src/init/ssdk_clk.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/init/ssdk_clk.c b/src/init/ssdk_clk.c +index bc244c6e..dc45691e 100644 +--- a/src/init/ssdk_clk.c ++++ b/src/init/ssdk_clk.c +@@ -1183,7 +1183,7 @@ ssdk_mp_tcsr_get(a_uint32_t tcsr_offset, a_uint32_t *tcsr_val) + { + void __iomem *tcsr_base = NULL; + +- tcsr_base = ioremap_nocache(TCSR_ETH_ADDR, TCSR_ETH_SIZE); ++ tcsr_base = ioremap(TCSR_ETH_ADDR, TCSR_ETH_SIZE); + if (!tcsr_base) + { + SSDK_ERROR("Failed to map tcsr eth address!\n"); +@@ -1200,7 +1200,7 @@ ssdk_mp_tcsr_set(a_uint32_t tcsr_offset, a_uint32_t tcsr_val) + { + void __iomem *tcsr_base = NULL; + +- tcsr_base = ioremap_nocache(TCSR_ETH_ADDR, TCSR_ETH_SIZE); ++ tcsr_base = ioremap(TCSR_ETH_ADDR, TCSR_ETH_SIZE); + if (!tcsr_base) + { + SSDK_ERROR("Failed to map tcsr eth address!\n"); +@@ -1248,7 +1248,7 @@ ssdk_mp_cmnblk_stable_check(void) + a_uint32_t reg_val; + int i, loops = 20; + +- pll_lock = ioremap_nocache(CMN_PLL_LOCKED_ADDR, CMN_PLL_LOCKED_SIZE); ++ pll_lock = ioremap(CMN_PLL_LOCKED_ADDR, CMN_PLL_LOCKED_SIZE); + if (!pll_lock) { + SSDK_ERROR("Failed to map CMN PLL LOCK register!\n"); + return A_FALSE; +@@ -1303,7 +1303,7 @@ static void ssdk_cmnblk_pll_src_set(enum cmnblk_pll_src_type pll_source) + void __iomem *cmn_pll_src_base = NULL; + a_uint32_t reg_val; + +- cmn_pll_src_base = ioremap_nocache(CMN_BLK_PLL_SRC_ADDR, CMN_BLK_SIZE); ++ cmn_pll_src_base = ioremap(CMN_BLK_PLL_SRC_ADDR, CMN_BLK_SIZE); + if (!cmn_pll_src_base) { + SSDK_ERROR("Failed to map cmn pll source address!\n"); + return; +-- +2.40.1 + diff --git a/package/kernel/r8168/Makefile b/package/kernel/r8168/Makefile index 5897b92e56..1564873fd3 100644 --- a/package/kernel/r8168/Makefile +++ b/package/kernel/r8168/Makefile @@ -1,12 +1,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=r8168 -PKG_VERSION:=8.054.00 +PKG_VERSION:=8.055.00 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 PKG_SOURCE_URL:=https://github.com/openwrt/rtl8168/releases/download/$(PKG_VERSION) -PKG_HASH:=5480120cf823e991e8cbd325118c1ec0c57d8f42760ba1a7334bd07d291d235d +PKG_HASH:=61deb2a9cb7d6b08748ad51734b108da95d629712b64b204e2e6bd3f16d0a48f PKG_BUILD_PARALLEL:=1 PKG_LICENSE:=GPLv2 diff --git a/package/kernel/r8168/patches/001-r8168_n-fix-proc_dump_rx_desc_2-on-32-bits.patch b/package/kernel/r8168/patches/001-r8168_n-fix-proc_dump_rx_desc_2-on-32-bits.patch index b53819a0cb..bb00411e06 100644 --- a/package/kernel/r8168/patches/001-r8168_n-fix-proc_dump_rx_desc_2-on-32-bits.patch +++ b/package/kernel/r8168/patches/001-r8168_n-fix-proc_dump_rx_desc_2-on-32-bits.patch @@ -13,7 +13,7 @@ Signed-off-by: Álvaro Fernández Rojas --- a/src/r8168_n.c +++ b/src/r8168_n.c -@@ -1655,9 +1655,9 @@ static int proc_dump_rx_desc_2(struct se +@@ -1668,9 +1668,9 @@ static int proc_dump_rx_desc_2(struct se j, k); for (i=0; i<(tp->RxDescLength/4); i++) { if (!(i % 4)) diff --git a/package/kernel/r8168/patches/110-r8168-print-link-speed-and-duplex-mode.patch b/package/kernel/r8168/patches/110-r8168-print-link-speed-and-duplex-mode.patch index 41162b4d9b..150c41d632 100644 --- a/package/kernel/r8168/patches/110-r8168-print-link-speed-and-duplex-mode.patch +++ b/package/kernel/r8168/patches/110-r8168-print-link-speed-and-duplex-mode.patch @@ -18,7 +18,7 @@ Signed-off-by: Chukun Pan --- a/src/r8168.h +++ b/src/r8168.h -@@ -1468,6 +1468,8 @@ enum RTL8168_register_content { +@@ -1480,6 +1480,8 @@ enum RTL8168_register_content { LinkStatus = 0x02, FullDup = 0x01, @@ -37,7 +37,7 @@ Signed-off-by: Chukun Pan #include #include #include -@@ -5369,6 +5370,36 @@ rtl8168_link_down_patch(struct net_devic +@@ -5396,6 +5397,38 @@ rtl8168_link_down_patch(struct net_devic #endif } @@ -66,6 +66,8 @@ Signed-off-by: Chukun Pan + speed = SPEED_100; + else if (status & _10bps) + speed = SPEED_10; ++ else if (eee_giga_lite) ++ speed = SPEED_1000; + } + + return speed; @@ -74,7 +76,7 @@ Signed-off-by: Chukun Pan static void rtl8168_check_link_status(struct net_device *dev) { -@@ -5388,11 +5419,18 @@ rtl8168_check_link_status(struct net_dev +@@ -5415,11 +5448,18 @@ rtl8168_check_link_status(struct net_dev if (link_status_on) { rtl8168_link_on_patch(dev); diff --git a/target/linux/generic/backport-6.6/894-v6.7-net-dsa-realtek-Convert-to-platform-remove-callback.patch b/target/linux/generic/backport-6.6/894-v6.7-net-dsa-realtek-Convert-to-platform-remove-callback.patch new file mode 100644 index 0000000000..d19338e7df --- /dev/null +++ b/target/linux/generic/backport-6.6/894-v6.7-net-dsa-realtek-Convert-to-platform-remove-callback.patch @@ -0,0 +1,66 @@ +From d48a5472b8f2b29800bb25913f9403765005f1bc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= +Date: Mon, 18 Sep 2023 21:19:14 +0200 +Subject: [PATCH] net: dsa: realtek: Convert to platform remove callback + returning void +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The .remove() callback for a platform driver returns an int which makes +many driver authors wrongly assume it's possible to do error handling by +returning an error code. However the value returned is ignored (apart +from emitting a warning) and this typically results in resource leaks. +To improve here there is a quest to make the remove callback return +void. In the first step of this quest all drivers are converted to +.remove_new() which already returns void. Eventually after all drivers +are converted, .remove_new() is renamed to .remove(). + +Trivially convert this driver from always returning zero in the remove +callback to the void returning variant. + +Signed-off-by: Uwe Kleine-König +Reviewed-by: Andrew Lunn +Reviewed-by: Florian Fainelli +Reviewed-by: Alvin Šipraga +Signed-off-by: David S. Miller +Signed-off-by: Luiz Angelo Daros de Luca +--- + drivers/net/dsa/realtek/realtek-smi.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -506,12 +506,12 @@ static int realtek_smi_probe(struct plat + return 0; + } + +-static int realtek_smi_remove(struct platform_device *pdev) ++static void realtek_smi_remove(struct platform_device *pdev) + { + struct realtek_priv *priv = platform_get_drvdata(pdev); + + if (!priv) +- return 0; ++ return; + + dsa_unregister_switch(priv->ds); + if (priv->slave_mii_bus) +@@ -520,8 +520,6 @@ static int realtek_smi_remove(struct pla + /* leave the device reset asserted */ + if (priv->reset) + gpiod_set_value(priv->reset, 1); +- +- return 0; + } + + static void realtek_smi_shutdown(struct platform_device *pdev) +@@ -559,7 +557,7 @@ static struct platform_driver realtek_sm + .of_match_table = realtek_smi_of_match, + }, + .probe = realtek_smi_probe, +- .remove = realtek_smi_remove, ++ .remove_new = realtek_smi_remove, + .shutdown = realtek_smi_shutdown, + }; + module_platform_driver(realtek_smi_driver); diff --git a/target/linux/generic/backport-6.6/896-v6.9-0001-net-dsa-realtek-drop-cleanup-from-realtek_ops.patch b/target/linux/generic/backport-6.6/896-v6.9-0001-net-dsa-realtek-drop-cleanup-from-realtek_ops.patch new file mode 100644 index 0000000000..82958ecff8 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0001-net-dsa-realtek-drop-cleanup-from-realtek_ops.patch @@ -0,0 +1,30 @@ +From 33f4336cbd32c21717b60d013693a0bd51a27db6 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:37 -0300 +Subject: net: dsa: realtek: drop cleanup from realtek_ops +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It was never used and never referenced. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Alvin Šipraga +Reviewed-by: Florian Fainelli +Reviewed-by: Linus Walleij +Reviewed-by: Vladimir Oltean +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek.h | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/net/dsa/realtek/realtek.h ++++ b/drivers/net/dsa/realtek/realtek.h +@@ -91,7 +91,6 @@ struct realtek_ops { + int (*detect)(struct realtek_priv *priv); + int (*reset_chip)(struct realtek_priv *priv); + int (*setup)(struct realtek_priv *priv); +- void (*cleanup)(struct realtek_priv *priv); + int (*get_mib_counter)(struct realtek_priv *priv, + int port, + struct rtl8366_mib_counter *mib, diff --git a/target/linux/generic/backport-6.6/896-v6.9-0002-net-dsa-realtek-introduce-REALTEK_DSA-namespace.patch b/target/linux/generic/backport-6.6/896-v6.9-0002-net-dsa-realtek-introduce-REALTEK_DSA-namespace.patch new file mode 100644 index 0000000000..d50ea1bb30 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0002-net-dsa-realtek-introduce-REALTEK_DSA-namespace.patch @@ -0,0 +1,146 @@ +From ded3813b44fe11a3bbd2c9d7df8870e8c19a7ccd Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:38 -0300 +Subject: net: dsa: realtek: introduce REALTEK_DSA namespace + +Create a namespace to group the exported symbols. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek-mdio.c | 1 + + drivers/net/dsa/realtek/realtek-smi.c | 1 + + drivers/net/dsa/realtek/rtl8365mb.c | 1 + + drivers/net/dsa/realtek/rtl8366-core.c | 22 +++++++++++----------- + drivers/net/dsa/realtek/rtl8366rb.c | 1 + + 5 files changed, 15 insertions(+), 11 deletions(-) + +--- a/drivers/net/dsa/realtek/realtek-mdio.c ++++ b/drivers/net/dsa/realtek/realtek-mdio.c +@@ -288,3 +288,4 @@ mdio_module_driver(realtek_mdio_driver); + MODULE_AUTHOR("Luiz Angelo Daros de Luca "); + MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface"); + MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS(REALTEK_DSA); +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -565,3 +565,4 @@ module_platform_driver(realtek_smi_drive + MODULE_AUTHOR("Linus Walleij "); + MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface"); + MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS(REALTEK_DSA); +--- a/drivers/net/dsa/realtek/rtl8365mb.c ++++ b/drivers/net/dsa/realtek/rtl8365mb.c +@@ -2178,3 +2178,4 @@ EXPORT_SYMBOL_GPL(rtl8365mb_variant); + MODULE_AUTHOR("Alvin Šipraga "); + MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch"); + MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS(REALTEK_DSA); +--- a/drivers/net/dsa/realtek/rtl8366-core.c ++++ b/drivers/net/dsa/realtek/rtl8366-core.c +@@ -34,7 +34,7 @@ int rtl8366_mc_is_used(struct realtek_pr + + return 0; + } +-EXPORT_SYMBOL_GPL(rtl8366_mc_is_used); ++EXPORT_SYMBOL_NS_GPL(rtl8366_mc_is_used, REALTEK_DSA); + + /** + * rtl8366_obtain_mc() - retrieve or allocate a VLAN member configuration +@@ -187,7 +187,7 @@ int rtl8366_set_vlan(struct realtek_priv + + return ret; + } +-EXPORT_SYMBOL_GPL(rtl8366_set_vlan); ++EXPORT_SYMBOL_NS_GPL(rtl8366_set_vlan, REALTEK_DSA); + + int rtl8366_set_pvid(struct realtek_priv *priv, unsigned int port, + unsigned int vid) +@@ -217,7 +217,7 @@ int rtl8366_set_pvid(struct realtek_priv + + return 0; + } +-EXPORT_SYMBOL_GPL(rtl8366_set_pvid); ++EXPORT_SYMBOL_NS_GPL(rtl8366_set_pvid, REALTEK_DSA); + + int rtl8366_enable_vlan4k(struct realtek_priv *priv, bool enable) + { +@@ -243,7 +243,7 @@ int rtl8366_enable_vlan4k(struct realtek + priv->vlan4k_enabled = enable; + return 0; + } +-EXPORT_SYMBOL_GPL(rtl8366_enable_vlan4k); ++EXPORT_SYMBOL_NS_GPL(rtl8366_enable_vlan4k, REALTEK_DSA); + + int rtl8366_enable_vlan(struct realtek_priv *priv, bool enable) + { +@@ -265,7 +265,7 @@ int rtl8366_enable_vlan(struct realtek_p + + return ret; + } +-EXPORT_SYMBOL_GPL(rtl8366_enable_vlan); ++EXPORT_SYMBOL_NS_GPL(rtl8366_enable_vlan, REALTEK_DSA); + + int rtl8366_reset_vlan(struct realtek_priv *priv) + { +@@ -290,7 +290,7 @@ int rtl8366_reset_vlan(struct realtek_pr + + return 0; + } +-EXPORT_SYMBOL_GPL(rtl8366_reset_vlan); ++EXPORT_SYMBOL_NS_GPL(rtl8366_reset_vlan, REALTEK_DSA); + + int rtl8366_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, +@@ -345,7 +345,7 @@ int rtl8366_vlan_add(struct dsa_switch * + + return 0; + } +-EXPORT_SYMBOL_GPL(rtl8366_vlan_add); ++EXPORT_SYMBOL_NS_GPL(rtl8366_vlan_add, REALTEK_DSA); + + int rtl8366_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +@@ -389,7 +389,7 @@ int rtl8366_vlan_del(struct dsa_switch * + + return 0; + } +-EXPORT_SYMBOL_GPL(rtl8366_vlan_del); ++EXPORT_SYMBOL_NS_GPL(rtl8366_vlan_del, REALTEK_DSA); + + void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset, + uint8_t *data) +@@ -407,7 +407,7 @@ void rtl8366_get_strings(struct dsa_swit + mib->name, ETH_GSTRING_LEN); + } + } +-EXPORT_SYMBOL_GPL(rtl8366_get_strings); ++EXPORT_SYMBOL_NS_GPL(rtl8366_get_strings, REALTEK_DSA); + + int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset) + { +@@ -421,7 +421,7 @@ int rtl8366_get_sset_count(struct dsa_sw + + return priv->num_mib_counters; + } +-EXPORT_SYMBOL_GPL(rtl8366_get_sset_count); ++EXPORT_SYMBOL_NS_GPL(rtl8366_get_sset_count, REALTEK_DSA); + + void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) + { +@@ -445,4 +445,4 @@ void rtl8366_get_ethtool_stats(struct ds + data[i] = mibvalue; + } + } +-EXPORT_SYMBOL_GPL(rtl8366_get_ethtool_stats); ++EXPORT_SYMBOL_NS_GPL(rtl8366_get_ethtool_stats, REALTEK_DSA); +--- a/drivers/net/dsa/realtek/rtl8366rb.c ++++ b/drivers/net/dsa/realtek/rtl8366rb.c +@@ -1852,3 +1852,4 @@ EXPORT_SYMBOL_GPL(rtl8366rb_variant); + MODULE_AUTHOR("Linus Walleij "); + MODULE_DESCRIPTION("Driver for RTL8366RB ethernet switch"); + MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS(REALTEK_DSA); diff --git a/target/linux/generic/backport-6.6/896-v6.9-0003-net-dsa-realtek-convert-variants-into-real-drivers.patch b/target/linux/generic/backport-6.6/896-v6.9-0003-net-dsa-realtek-convert-variants-into-real-drivers.patch new file mode 100644 index 0000000000..aa6dfcf7e6 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0003-net-dsa-realtek-convert-variants-into-real-drivers.patch @@ -0,0 +1,539 @@ +From bce254b839abe67577bebdef0838796af409c229 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:39 -0300 +Subject: net: dsa: realtek: convert variants into real drivers + +Previously, the interface modules realtek-smi and realtek-mdio served as +a platform and an MDIO driver, respectively. Each interface module +redundantly specified the same compatible strings for both variants and +referenced symbols from the variants. + +Now, each variant module has been transformed into a unified driver +serving both as a platform and an MDIO driver. This modification +reverses the relationship between the interface and variant modules, +with the variant module now utilizing symbols from the interface +modules. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Vladimir Oltean +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/Kconfig | 20 +++---- + drivers/net/dsa/realtek/realtek-mdio.c | 68 +++++++++++++++--------- + drivers/net/dsa/realtek/realtek-mdio.h | 48 +++++++++++++++++ + drivers/net/dsa/realtek/realtek-smi.c | 73 +++++++++++++++----------- + drivers/net/dsa/realtek/realtek-smi.h | 48 +++++++++++++++++ + drivers/net/dsa/realtek/rtl8365mb.c | 54 ++++++++++++++++++- + drivers/net/dsa/realtek/rtl8366rb.c | 54 ++++++++++++++++++- + 7 files changed, 292 insertions(+), 73 deletions(-) + create mode 100644 drivers/net/dsa/realtek/realtek-mdio.h + create mode 100644 drivers/net/dsa/realtek/realtek-smi.h + +--- a/drivers/net/dsa/realtek/Kconfig ++++ b/drivers/net/dsa/realtek/Kconfig +@@ -16,37 +16,29 @@ menuconfig NET_DSA_REALTEK + if NET_DSA_REALTEK + + config NET_DSA_REALTEK_MDIO +- tristate "Realtek MDIO interface driver" ++ tristate "Realtek MDIO interface support" + depends on OF +- depends on NET_DSA_REALTEK_RTL8365MB || NET_DSA_REALTEK_RTL8366RB +- depends on NET_DSA_REALTEK_RTL8365MB || !NET_DSA_REALTEK_RTL8365MB +- depends on NET_DSA_REALTEK_RTL8366RB || !NET_DSA_REALTEK_RTL8366RB + help + Select to enable support for registering switches configured + through MDIO. + + config NET_DSA_REALTEK_SMI +- tristate "Realtek SMI interface driver" ++ tristate "Realtek SMI interface support" + depends on OF +- depends on NET_DSA_REALTEK_RTL8365MB || NET_DSA_REALTEK_RTL8366RB +- depends on NET_DSA_REALTEK_RTL8365MB || !NET_DSA_REALTEK_RTL8365MB +- depends on NET_DSA_REALTEK_RTL8366RB || !NET_DSA_REALTEK_RTL8366RB + help + Select to enable support for registering switches connected + through SMI. + + config NET_DSA_REALTEK_RTL8365MB +- tristate "Realtek RTL8365MB switch subdriver" +- imply NET_DSA_REALTEK_SMI +- imply NET_DSA_REALTEK_MDIO ++ tristate "Realtek RTL8365MB switch driver" ++ depends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO + select NET_DSA_TAG_RTL8_4 + help + Select to enable support for Realtek RTL8365MB-VC and RTL8367S. + + config NET_DSA_REALTEK_RTL8366RB +- tristate "Realtek RTL8366RB switch subdriver" +- imply NET_DSA_REALTEK_SMI +- imply NET_DSA_REALTEK_MDIO ++ tristate "Realtek RTL8366RB switch driver" ++ depends on NET_DSA_REALTEK_SMI || NET_DSA_REALTEK_MDIO + select NET_DSA_TAG_RTL4_A + help + Select to enable support for Realtek RTL8366RB. +--- a/drivers/net/dsa/realtek/realtek-mdio.c ++++ b/drivers/net/dsa/realtek/realtek-mdio.c +@@ -25,6 +25,7 @@ + #include + + #include "realtek.h" ++#include "realtek-mdio.h" + + /* Read/write via mdiobus */ + #define REALTEK_MDIO_CTRL0_REG 31 +@@ -140,7 +141,19 @@ static const struct regmap_config realte + .disable_locking = true, + }; + +-static int realtek_mdio_probe(struct mdio_device *mdiodev) ++/** ++ * realtek_mdio_probe() - Probe a platform device for an MDIO-connected switch ++ * @mdiodev: mdio_device to probe on. ++ * ++ * This function should be used as the .probe in an mdio_driver. It ++ * initializes realtek_priv and read data from the device-tree node. The switch ++ * is hard reset if a method is provided. It checks the switch chip ID and, ++ * finally, a DSA switch is registered. ++ * ++ * Context: Can sleep. Takes and releases priv->map_lock. ++ * Return: Returns 0 on success, a negative error on failure. ++ */ ++int realtek_mdio_probe(struct mdio_device *mdiodev) + { + struct realtek_priv *priv; + struct device *dev = &mdiodev->dev; +@@ -235,8 +248,20 @@ static int realtek_mdio_probe(struct mdi + + return 0; + } ++EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe, REALTEK_DSA); + +-static void realtek_mdio_remove(struct mdio_device *mdiodev) ++/** ++ * realtek_mdio_remove() - Remove the driver of an MDIO-connected switch ++ * @mdiodev: mdio_device to be removed. ++ * ++ * This function should be used as the .remove_new in an mdio_driver. First ++ * it unregisters the DSA switch and cleans internal data. If a method is ++ * provided, the hard reset is asserted to avoid traffic leakage. ++ * ++ * Context: Can sleep. ++ * Return: Nothing. ++ */ ++void realtek_mdio_remove(struct mdio_device *mdiodev) + { + struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev); + +@@ -249,8 +274,21 @@ static void realtek_mdio_remove(struct m + if (priv->reset) + gpiod_set_value(priv->reset, 1); + } ++EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, REALTEK_DSA); + +-static void realtek_mdio_shutdown(struct mdio_device *mdiodev) ++/** ++ * realtek_mdio_shutdown() - Shutdown the driver of a MDIO-connected switch ++ * @mdiodev: mdio_device shutting down. ++ * ++ * This function should be used as the .shutdown in an mdio_driver. It shuts ++ * down the DSA switch and cleans the platform driver data, to prevent ++ * realtek_mdio_remove() from running afterwards, which is possible if the ++ * parent bus implements its own .shutdown() as .remove(). ++ * ++ * Context: Can sleep. ++ * Return: Nothing. ++ */ ++void realtek_mdio_shutdown(struct mdio_device *mdiodev) + { + struct realtek_priv *priv = dev_get_drvdata(&mdiodev->dev); + +@@ -261,29 +299,7 @@ static void realtek_mdio_shutdown(struct + + dev_set_drvdata(&mdiodev->dev, NULL); + } +- +-static const struct of_device_id realtek_mdio_of_match[] = { +-#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB) +- { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, }, +-#endif +-#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB) +- { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, }, +-#endif +- { /* sentinel */ }, +-}; +-MODULE_DEVICE_TABLE(of, realtek_mdio_of_match); +- +-static struct mdio_driver realtek_mdio_driver = { +- .mdiodrv.driver = { +- .name = "realtek-mdio", +- .of_match_table = realtek_mdio_of_match, +- }, +- .probe = realtek_mdio_probe, +- .remove = realtek_mdio_remove, +- .shutdown = realtek_mdio_shutdown, +-}; +- +-mdio_module_driver(realtek_mdio_driver); ++EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA); + + MODULE_AUTHOR("Luiz Angelo Daros de Luca "); + MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface"); +--- /dev/null ++++ b/drivers/net/dsa/realtek/realtek-mdio.h +@@ -0,0 +1,48 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++ ++#ifndef _REALTEK_MDIO_H ++#define _REALTEK_MDIO_H ++ ++#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_MDIO) ++ ++static inline int realtek_mdio_driver_register(struct mdio_driver *drv) ++{ ++ return mdio_driver_register(drv); ++} ++ ++static inline void realtek_mdio_driver_unregister(struct mdio_driver *drv) ++{ ++ mdio_driver_unregister(drv); ++} ++ ++int realtek_mdio_probe(struct mdio_device *mdiodev); ++void realtek_mdio_remove(struct mdio_device *mdiodev); ++void realtek_mdio_shutdown(struct mdio_device *mdiodev); ++ ++#else /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_MDIO) */ ++ ++static inline int realtek_mdio_driver_register(struct mdio_driver *drv) ++{ ++ return 0; ++} ++ ++static inline void realtek_mdio_driver_unregister(struct mdio_driver *drv) ++{ ++} ++ ++static inline int realtek_mdio_probe(struct mdio_device *mdiodev) ++{ ++ return -ENOENT; ++} ++ ++static inline void realtek_mdio_remove(struct mdio_device *mdiodev) ++{ ++} ++ ++static inline void realtek_mdio_shutdown(struct mdio_device *mdiodev) ++{ ++} ++ ++#endif /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_MDIO) */ ++ ++#endif /* _REALTEK_MDIO_H */ +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -40,6 +40,7 @@ + #include + + #include "realtek.h" ++#include "realtek-smi.h" + + #define REALTEK_SMI_ACK_RETRY_COUNT 5 + +@@ -408,7 +409,19 @@ err_put_node: + return ret; + } + +-static int realtek_smi_probe(struct platform_device *pdev) ++/** ++ * realtek_smi_probe() - Probe a platform device for an SMI-connected switch ++ * @pdev: platform_device to probe on. ++ * ++ * This function should be used as the .probe in a platform_driver. It ++ * initializes realtek_priv and read data from the device-tree node. The switch ++ * is hard reset if a method is provided. It checks the switch chip ID and, ++ * finally, a DSA switch is registered. ++ * ++ * Context: Can sleep. Takes and releases priv->map_lock. ++ * Return: Returns 0 on success, a negative error on failure. ++ */ ++int realtek_smi_probe(struct platform_device *pdev) + { + const struct realtek_variant *var; + struct device *dev = &pdev->dev; +@@ -505,8 +518,20 @@ static int realtek_smi_probe(struct plat + } + return 0; + } ++EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, REALTEK_DSA); + +-static void realtek_smi_remove(struct platform_device *pdev) ++/** ++ * realtek_smi_remove() - Remove the driver of a SMI-connected switch ++ * @pdev: platform_device to be removed. ++ * ++ * This function should be used as the .remove_new in a platform_driver. First ++ * it unregisters the DSA switch and cleans internal data. If a method is ++ * provided, the hard reset is asserted to avoid traffic leakage. ++ * ++ * Context: Can sleep. ++ * Return: Nothing. ++ */ ++void realtek_smi_remove(struct platform_device *pdev) + { + struct realtek_priv *priv = platform_get_drvdata(pdev); + +@@ -521,8 +546,21 @@ static void realtek_smi_remove(struct pl + if (priv->reset) + gpiod_set_value(priv->reset, 1); + } ++EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA); + +-static void realtek_smi_shutdown(struct platform_device *pdev) ++/** ++ * realtek_smi_shutdown() - Shutdown the driver of a SMI-connected switch ++ * @pdev: platform_device shutting down. ++ * ++ * This function should be used as the .shutdown in a platform_driver. It shuts ++ * down the DSA switch and cleans the platform driver data, to prevent ++ * realtek_smi_remove() from running afterwards, which is possible if the ++ * parent bus implements its own .shutdown() as .remove(). ++ * ++ * Context: Can sleep. ++ * Return: Nothing. ++ */ ++void realtek_smi_shutdown(struct platform_device *pdev) + { + struct realtek_priv *priv = platform_get_drvdata(pdev); + +@@ -533,34 +571,7 @@ static void realtek_smi_shutdown(struct + + platform_set_drvdata(pdev, NULL); + } +- +-static const struct of_device_id realtek_smi_of_match[] = { +-#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB) +- { +- .compatible = "realtek,rtl8366rb", +- .data = &rtl8366rb_variant, +- }, +-#endif +-#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB) +- { +- .compatible = "realtek,rtl8365mb", +- .data = &rtl8365mb_variant, +- }, +-#endif +- { /* sentinel */ }, +-}; +-MODULE_DEVICE_TABLE(of, realtek_smi_of_match); +- +-static struct platform_driver realtek_smi_driver = { +- .driver = { +- .name = "realtek-smi", +- .of_match_table = realtek_smi_of_match, +- }, +- .probe = realtek_smi_probe, +- .remove_new = realtek_smi_remove, +- .shutdown = realtek_smi_shutdown, +-}; +-module_platform_driver(realtek_smi_driver); ++EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, REALTEK_DSA); + + MODULE_AUTHOR("Linus Walleij "); + MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface"); +--- /dev/null ++++ b/drivers/net/dsa/realtek/realtek-smi.h +@@ -0,0 +1,48 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++ ++#ifndef _REALTEK_SMI_H ++#define _REALTEK_SMI_H ++ ++#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_SMI) ++ ++static inline int realtek_smi_driver_register(struct platform_driver *drv) ++{ ++ return platform_driver_register(drv); ++} ++ ++static inline void realtek_smi_driver_unregister(struct platform_driver *drv) ++{ ++ platform_driver_unregister(drv); ++} ++ ++int realtek_smi_probe(struct platform_device *pdev); ++void realtek_smi_remove(struct platform_device *pdev); ++void realtek_smi_shutdown(struct platform_device *pdev); ++ ++#else /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_SMI) */ ++ ++static inline int realtek_smi_driver_register(struct platform_driver *drv) ++{ ++ return 0; ++} ++ ++static inline void realtek_smi_driver_unregister(struct platform_driver *drv) ++{ ++} ++ ++static inline int realtek_smi_probe(struct platform_device *pdev) ++{ ++ return -ENOENT; ++} ++ ++static inline void realtek_smi_remove(struct platform_device *pdev) ++{ ++} ++ ++static inline void realtek_smi_shutdown(struct platform_device *pdev) ++{ ++} ++ ++#endif /* IS_ENABLED(CONFIG_NET_DSA_REALTEK_SMI) */ ++ ++#endif /* _REALTEK_SMI_H */ +--- a/drivers/net/dsa/realtek/rtl8365mb.c ++++ b/drivers/net/dsa/realtek/rtl8365mb.c +@@ -101,6 +101,8 @@ + #include + + #include "realtek.h" ++#include "realtek-smi.h" ++#include "realtek-mdio.h" + + /* Family-specific data and limits */ + #define RTL8365MB_PHYADDRMAX 7 +@@ -2173,7 +2175,57 @@ const struct realtek_variant rtl8365mb_v + .cmd_write = 0xb8, + .chip_data_sz = sizeof(struct rtl8365mb), + }; +-EXPORT_SYMBOL_GPL(rtl8365mb_variant); ++ ++static const struct of_device_id rtl8365mb_of_match[] = { ++ { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, rtl8365mb_of_match); ++ ++static struct platform_driver rtl8365mb_smi_driver = { ++ .driver = { ++ .name = "rtl8365mb-smi", ++ .of_match_table = rtl8365mb_of_match, ++ }, ++ .probe = realtek_smi_probe, ++ .remove_new = realtek_smi_remove, ++ .shutdown = realtek_smi_shutdown, ++}; ++ ++static struct mdio_driver rtl8365mb_mdio_driver = { ++ .mdiodrv.driver = { ++ .name = "rtl8365mb-mdio", ++ .of_match_table = rtl8365mb_of_match, ++ }, ++ .probe = realtek_mdio_probe, ++ .remove = realtek_mdio_remove, ++ .shutdown = realtek_mdio_shutdown, ++}; ++ ++static int rtl8365mb_init(void) ++{ ++ int ret; ++ ++ ret = realtek_mdio_driver_register(&rtl8365mb_mdio_driver); ++ if (ret) ++ return ret; ++ ++ ret = realtek_smi_driver_register(&rtl8365mb_smi_driver); ++ if (ret) { ++ realtek_mdio_driver_unregister(&rtl8365mb_mdio_driver); ++ return ret; ++ } ++ ++ return 0; ++} ++module_init(rtl8365mb_init); ++ ++static void __exit rtl8365mb_exit(void) ++{ ++ realtek_smi_driver_unregister(&rtl8365mb_smi_driver); ++ realtek_mdio_driver_unregister(&rtl8365mb_mdio_driver); ++} ++module_exit(rtl8365mb_exit); + + MODULE_AUTHOR("Alvin Šipraga "); + MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch"); +--- a/drivers/net/dsa/realtek/rtl8366rb.c ++++ b/drivers/net/dsa/realtek/rtl8366rb.c +@@ -22,6 +22,8 @@ + #include + + #include "realtek.h" ++#include "realtek-smi.h" ++#include "realtek-mdio.h" + + #define RTL8366RB_PORT_NUM_CPU 5 + #define RTL8366RB_NUM_PORTS 6 +@@ -1847,7 +1849,57 @@ const struct realtek_variant rtl8366rb_v + .cmd_write = 0xa8, + .chip_data_sz = sizeof(struct rtl8366rb), + }; +-EXPORT_SYMBOL_GPL(rtl8366rb_variant); ++ ++static const struct of_device_id rtl8366rb_of_match[] = { ++ { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, rtl8366rb_of_match); ++ ++static struct platform_driver rtl8366rb_smi_driver = { ++ .driver = { ++ .name = "rtl8366rb-smi", ++ .of_match_table = rtl8366rb_of_match, ++ }, ++ .probe = realtek_smi_probe, ++ .remove_new = realtek_smi_remove, ++ .shutdown = realtek_smi_shutdown, ++}; ++ ++static struct mdio_driver rtl8366rb_mdio_driver = { ++ .mdiodrv.driver = { ++ .name = "rtl8366rb-mdio", ++ .of_match_table = rtl8366rb_of_match, ++ }, ++ .probe = realtek_mdio_probe, ++ .remove = realtek_mdio_remove, ++ .shutdown = realtek_mdio_shutdown, ++}; ++ ++static int rtl8366rb_init(void) ++{ ++ int ret; ++ ++ ret = realtek_mdio_driver_register(&rtl8366rb_mdio_driver); ++ if (ret) ++ return ret; ++ ++ ret = realtek_smi_driver_register(&rtl8366rb_smi_driver); ++ if (ret) { ++ realtek_mdio_driver_unregister(&rtl8366rb_mdio_driver); ++ return ret; ++ } ++ ++ return 0; ++} ++module_init(rtl8366rb_init); ++ ++static void __exit rtl8366rb_exit(void) ++{ ++ realtek_smi_driver_unregister(&rtl8366rb_smi_driver); ++ realtek_mdio_driver_unregister(&rtl8366rb_mdio_driver); ++} ++module_exit(rtl8366rb_exit); + + MODULE_AUTHOR("Linus Walleij "); + MODULE_DESCRIPTION("Driver for RTL8366RB ethernet switch"); diff --git a/target/linux/generic/backport-6.6/896-v6.9-0004-net-dsa-realtek-keep-variant-reference-in-realtek_pr.patch b/target/linux/generic/backport-6.6/896-v6.9-0004-net-dsa-realtek-keep-variant-reference-in-realtek_pr.patch new file mode 100644 index 0000000000..b0e074b2c9 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0004-net-dsa-realtek-keep-variant-reference-in-realtek_pr.patch @@ -0,0 +1,91 @@ +From 4667a1db2f550d23e01ba655fce331196ead6e92 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:40 -0300 +Subject: net: dsa: realtek: keep variant reference in + realtek_priv + +Instead of copying values from the variant, we can keep a reference in +realtek_priv. + +This is a preliminary change for sharing code betwen interfaces. It will +allow to move most of the probe into a common module while still allow +code specific to each interface to read variant fields. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek-mdio.c | 4 +--- + drivers/net/dsa/realtek/realtek-smi.c | 10 ++++------ + drivers/net/dsa/realtek/realtek.h | 5 ++--- + 3 files changed, 7 insertions(+), 12 deletions(-) + +--- a/drivers/net/dsa/realtek/realtek-mdio.c ++++ b/drivers/net/dsa/realtek/realtek-mdio.c +@@ -196,9 +196,7 @@ int realtek_mdio_probe(struct mdio_devic + priv->dev = &mdiodev->dev; + priv->chip_data = (void *)priv + sizeof(*priv); + +- priv->clk_delay = var->clk_delay; +- priv->cmd_read = var->cmd_read; +- priv->cmd_write = var->cmd_write; ++ priv->variant = var; + priv->ops = var->ops; + + priv->write_reg_noack = realtek_mdio_write; +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -46,7 +46,7 @@ + + static inline void realtek_smi_clk_delay(struct realtek_priv *priv) + { +- ndelay(priv->clk_delay); ++ ndelay(priv->variant->clk_delay); + } + + static void realtek_smi_start(struct realtek_priv *priv) +@@ -209,7 +209,7 @@ static int realtek_smi_read_reg(struct r + realtek_smi_start(priv); + + /* Send READ command */ +- ret = realtek_smi_write_byte(priv, priv->cmd_read); ++ ret = realtek_smi_write_byte(priv, priv->variant->cmd_read); + if (ret) + goto out; + +@@ -250,7 +250,7 @@ static int realtek_smi_write_reg(struct + realtek_smi_start(priv); + + /* Send WRITE command */ +- ret = realtek_smi_write_byte(priv, priv->cmd_write); ++ ret = realtek_smi_write_byte(priv, priv->variant->cmd_write); + if (ret) + goto out; + +@@ -459,9 +459,7 @@ int realtek_smi_probe(struct platform_de + + /* Link forward and backward */ + priv->dev = dev; +- priv->clk_delay = var->clk_delay; +- priv->cmd_read = var->cmd_read; +- priv->cmd_write = var->cmd_write; ++ priv->variant = var; + priv->ops = var->ops; + + priv->setup_interface = realtek_smi_setup_mdio; +--- a/drivers/net/dsa/realtek/realtek.h ++++ b/drivers/net/dsa/realtek/realtek.h +@@ -58,9 +58,8 @@ struct realtek_priv { + struct mii_bus *bus; + int mdio_addr; + +- unsigned int clk_delay; +- u8 cmd_read; +- u8 cmd_write; ++ const struct realtek_variant *variant; ++ + spinlock_t lock; /* Locks around command writes */ + struct dsa_switch *ds; + struct irq_domain *irqdomain; diff --git a/target/linux/generic/backport-6.6/896-v6.9-0005-net-dsa-realtek-common-rtl83xx-module.patch b/target/linux/generic/backport-6.6/896-v6.9-0005-net-dsa-realtek-common-rtl83xx-module.patch new file mode 100644 index 0000000000..544b1b2992 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0005-net-dsa-realtek-common-rtl83xx-module.patch @@ -0,0 +1,875 @@ +From 8be040ecd94c1a9a137927d18534edfae0a9b68a Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:41 -0300 +Subject: net: dsa: realtek: common rtl83xx module + +Some code can be shared between both interface modules (MDIO and SMI) +and among variants. These interface functions migrated to a common +module: + +- rtl83xx_lock +- rtl83xx_unlock +- rtl83xx_probe +- rtl83xx_register_switch +- rtl83xx_unregister_switch +- rtl83xx_shutdown +- rtl83xx_remove + +The reset during probe was moved to the end of the common probe. This way, +we avoid a reset if anything else fails. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/Makefile | 2 + + drivers/net/dsa/realtek/realtek-mdio.c | 152 +++------------- + drivers/net/dsa/realtek/realtek-smi.c | 166 ++++------------- + drivers/net/dsa/realtek/realtek.h | 1 + + drivers/net/dsa/realtek/rtl8365mb.c | 9 +- + drivers/net/dsa/realtek/rtl8366rb.c | 9 +- + drivers/net/dsa/realtek/rtl83xx.c | 235 +++++++++++++++++++++++++ + drivers/net/dsa/realtek/rtl83xx.h | 21 +++ + 8 files changed, 322 insertions(+), 273 deletions(-) + create mode 100644 drivers/net/dsa/realtek/rtl83xx.c + create mode 100644 drivers/net/dsa/realtek/rtl83xx.h + +--- a/drivers/net/dsa/realtek/Makefile ++++ b/drivers/net/dsa/realtek/Makefile +@@ -1,4 +1,6 @@ + # SPDX-License-Identifier: GPL-2.0 ++obj-$(CONFIG_NET_DSA_REALTEK) += realtek_dsa.o ++realtek_dsa-objs := rtl83xx.o + obj-$(CONFIG_NET_DSA_REALTEK_MDIO) += realtek-mdio.o + obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o + obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366.o +--- a/drivers/net/dsa/realtek/realtek-mdio.c ++++ b/drivers/net/dsa/realtek/realtek-mdio.c +@@ -26,6 +26,7 @@ + + #include "realtek.h" + #include "realtek-mdio.h" ++#include "rtl83xx.h" + + /* Read/write via mdiobus */ + #define REALTEK_MDIO_CTRL0_REG 31 +@@ -100,147 +101,41 @@ out_unlock: + return ret; + } + +-static void realtek_mdio_lock(void *ctx) +-{ +- struct realtek_priv *priv = ctx; +- +- mutex_lock(&priv->map_lock); +-} +- +-static void realtek_mdio_unlock(void *ctx) +-{ +- struct realtek_priv *priv = ctx; +- +- mutex_unlock(&priv->map_lock); +-} +- +-static const struct regmap_config realtek_mdio_regmap_config = { +- .reg_bits = 10, /* A4..A0 R4..R0 */ +- .val_bits = 16, +- .reg_stride = 1, +- /* PHY regs are at 0x8000 */ +- .max_register = 0xffff, +- .reg_format_endian = REGMAP_ENDIAN_BIG, ++static const struct realtek_interface_info realtek_mdio_info = { + .reg_read = realtek_mdio_read, + .reg_write = realtek_mdio_write, +- .cache_type = REGCACHE_NONE, +- .lock = realtek_mdio_lock, +- .unlock = realtek_mdio_unlock, +-}; +- +-static const struct regmap_config realtek_mdio_nolock_regmap_config = { +- .reg_bits = 10, /* A4..A0 R4..R0 */ +- .val_bits = 16, +- .reg_stride = 1, +- /* PHY regs are at 0x8000 */ +- .max_register = 0xffff, +- .reg_format_endian = REGMAP_ENDIAN_BIG, +- .reg_read = realtek_mdio_read, +- .reg_write = realtek_mdio_write, +- .cache_type = REGCACHE_NONE, +- .disable_locking = true, + }; + + /** + * realtek_mdio_probe() - Probe a platform device for an MDIO-connected switch + * @mdiodev: mdio_device to probe on. + * +- * This function should be used as the .probe in an mdio_driver. It +- * initializes realtek_priv and read data from the device-tree node. The switch +- * is hard reset if a method is provided. It checks the switch chip ID and, +- * finally, a DSA switch is registered. ++ * This function should be used as the .probe in an mdio_driver. After ++ * calling the common probe function for both interfaces, it initializes the ++ * values specific for MDIO-connected devices. Finally, it calls a common ++ * function to register the DSA switch. + * + * Context: Can sleep. Takes and releases priv->map_lock. + * Return: Returns 0 on success, a negative error on failure. + */ + int realtek_mdio_probe(struct mdio_device *mdiodev) + { +- struct realtek_priv *priv; + struct device *dev = &mdiodev->dev; +- const struct realtek_variant *var; +- struct regmap_config rc; +- struct device_node *np; ++ struct realtek_priv *priv; + int ret; + +- var = of_device_get_match_data(dev); +- if (!var) +- return -EINVAL; +- +- priv = devm_kzalloc(&mdiodev->dev, +- size_add(sizeof(*priv), var->chip_data_sz), +- GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- mutex_init(&priv->map_lock); +- +- rc = realtek_mdio_regmap_config; +- rc.lock_arg = priv; +- priv->map = devm_regmap_init(dev, NULL, priv, &rc); +- if (IS_ERR(priv->map)) { +- ret = PTR_ERR(priv->map); +- dev_err(dev, "regmap init failed: %d\n", ret); +- return ret; +- } ++ priv = rtl83xx_probe(dev, &realtek_mdio_info); ++ if (IS_ERR(priv)) ++ return PTR_ERR(priv); + +- rc = realtek_mdio_nolock_regmap_config; +- priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); +- if (IS_ERR(priv->map_nolock)) { +- ret = PTR_ERR(priv->map_nolock); +- dev_err(dev, "regmap init failed: %d\n", ret); +- return ret; +- } +- +- priv->mdio_addr = mdiodev->addr; + priv->bus = mdiodev->bus; +- priv->dev = &mdiodev->dev; +- priv->chip_data = (void *)priv + sizeof(*priv); +- +- priv->variant = var; +- priv->ops = var->ops; +- ++ priv->mdio_addr = mdiodev->addr; + priv->write_reg_noack = realtek_mdio_write; ++ priv->ds_ops = priv->variant->ds_ops_mdio; + +- np = dev->of_node; +- +- dev_set_drvdata(dev, priv); +- +- /* TODO: if power is software controlled, set up any regulators here */ +- priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); +- +- priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); +- if (IS_ERR(priv->reset)) { +- dev_err(dev, "failed to get RESET GPIO\n"); +- return PTR_ERR(priv->reset); +- } +- +- if (priv->reset) { +- gpiod_set_value(priv->reset, 1); +- dev_dbg(dev, "asserted RESET\n"); +- msleep(REALTEK_HW_STOP_DELAY); +- gpiod_set_value(priv->reset, 0); +- msleep(REALTEK_HW_START_DELAY); +- dev_dbg(dev, "deasserted RESET\n"); +- } +- +- ret = priv->ops->detect(priv); +- if (ret) { +- dev_err(dev, "unable to detect switch\n"); +- return ret; +- } +- +- priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); +- if (!priv->ds) +- return -ENOMEM; +- +- priv->ds->dev = dev; +- priv->ds->num_ports = priv->num_ports; +- priv->ds->priv = priv; +- priv->ds->ops = var->ds_ops_mdio; +- +- ret = dsa_register_switch(priv->ds); ++ ret = rtl83xx_register_switch(priv); + if (ret) { +- dev_err(priv->dev, "unable to register switch ret = %d\n", ret); ++ rtl83xx_remove(priv); + return ret; + } + +@@ -253,8 +148,7 @@ EXPORT_SYMBOL_NS_GPL(realtek_mdio_probe, + * @mdiodev: mdio_device to be removed. + * + * This function should be used as the .remove_new in an mdio_driver. First +- * it unregisters the DSA switch and cleans internal data. If a method is +- * provided, the hard reset is asserted to avoid traffic leakage. ++ * it unregisters the DSA switch and then it calls the common remove function. + * + * Context: Can sleep. + * Return: Nothing. +@@ -266,11 +160,9 @@ void realtek_mdio_remove(struct mdio_dev + if (!priv) + return; + +- dsa_unregister_switch(priv->ds); ++ rtl83xx_unregister_switch(priv); + +- /* leave the device reset asserted */ +- if (priv->reset) +- gpiod_set_value(priv->reset, 1); ++ rtl83xx_remove(priv); + } + EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove, REALTEK_DSA); + +@@ -278,10 +170,8 @@ EXPORT_SYMBOL_NS_GPL(realtek_mdio_remove + * realtek_mdio_shutdown() - Shutdown the driver of a MDIO-connected switch + * @mdiodev: mdio_device shutting down. + * +- * This function should be used as the .shutdown in an mdio_driver. It shuts +- * down the DSA switch and cleans the platform driver data, to prevent +- * realtek_mdio_remove() from running afterwards, which is possible if the +- * parent bus implements its own .shutdown() as .remove(). ++ * This function should be used as the .shutdown in a platform_driver. It calls ++ * the common shutdown function. + * + * Context: Can sleep. + * Return: Nothing. +@@ -293,9 +183,7 @@ void realtek_mdio_shutdown(struct mdio_d + if (!priv) + return; + +- dsa_switch_shutdown(priv->ds); +- +- dev_set_drvdata(&mdiodev->dev, NULL); ++ rtl83xx_shutdown(priv); + } + EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA); + +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -41,6 +41,7 @@ + + #include "realtek.h" + #include "realtek-smi.h" ++#include "rtl83xx.h" + + #define REALTEK_SMI_ACK_RETRY_COUNT 5 + +@@ -311,47 +312,6 @@ static int realtek_smi_read(void *ctx, u + return realtek_smi_read_reg(priv, reg, val); + } + +-static void realtek_smi_lock(void *ctx) +-{ +- struct realtek_priv *priv = ctx; +- +- mutex_lock(&priv->map_lock); +-} +- +-static void realtek_smi_unlock(void *ctx) +-{ +- struct realtek_priv *priv = ctx; +- +- mutex_unlock(&priv->map_lock); +-} +- +-static const struct regmap_config realtek_smi_regmap_config = { +- .reg_bits = 10, /* A4..A0 R4..R0 */ +- .val_bits = 16, +- .reg_stride = 1, +- /* PHY regs are at 0x8000 */ +- .max_register = 0xffff, +- .reg_format_endian = REGMAP_ENDIAN_BIG, +- .reg_read = realtek_smi_read, +- .reg_write = realtek_smi_write, +- .cache_type = REGCACHE_NONE, +- .lock = realtek_smi_lock, +- .unlock = realtek_smi_unlock, +-}; +- +-static const struct regmap_config realtek_smi_nolock_regmap_config = { +- .reg_bits = 10, /* A4..A0 R4..R0 */ +- .val_bits = 16, +- .reg_stride = 1, +- /* PHY regs are at 0x8000 */ +- .max_register = 0xffff, +- .reg_format_endian = REGMAP_ENDIAN_BIG, +- .reg_read = realtek_smi_read, +- .reg_write = realtek_smi_write, +- .cache_type = REGCACHE_NONE, +- .disable_locking = true, +-}; +- + static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum) + { + struct realtek_priv *priv = bus->priv; +@@ -409,111 +369,56 @@ err_put_node: + return ret; + } + ++static const struct realtek_interface_info realtek_smi_info = { ++ .reg_read = realtek_smi_read, ++ .reg_write = realtek_smi_write, ++}; ++ + /** + * realtek_smi_probe() - Probe a platform device for an SMI-connected switch + * @pdev: platform_device to probe on. + * +- * This function should be used as the .probe in a platform_driver. It +- * initializes realtek_priv and read data from the device-tree node. The switch +- * is hard reset if a method is provided. It checks the switch chip ID and, +- * finally, a DSA switch is registered. ++ * This function should be used as the .probe in a platform_driver. After ++ * calling the common probe function for both interfaces, it initializes the ++ * values specific for SMI-connected devices. Finally, it calls a common ++ * function to register the DSA switch. + * + * Context: Can sleep. Takes and releases priv->map_lock. + * Return: Returns 0 on success, a negative error on failure. + */ + int realtek_smi_probe(struct platform_device *pdev) + { +- const struct realtek_variant *var; + struct device *dev = &pdev->dev; + struct realtek_priv *priv; +- struct regmap_config rc; +- struct device_node *np; + int ret; + +- var = of_device_get_match_data(dev); +- np = dev->of_node; +- +- priv = devm_kzalloc(dev, sizeof(*priv) + var->chip_data_sz, GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- priv->chip_data = (void *)priv + sizeof(*priv); +- +- mutex_init(&priv->map_lock); +- +- rc = realtek_smi_regmap_config; +- rc.lock_arg = priv; +- priv->map = devm_regmap_init(dev, NULL, priv, &rc); +- if (IS_ERR(priv->map)) { +- ret = PTR_ERR(priv->map); +- dev_err(dev, "regmap init failed: %d\n", ret); +- return ret; +- } +- +- rc = realtek_smi_nolock_regmap_config; +- priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); +- if (IS_ERR(priv->map_nolock)) { +- ret = PTR_ERR(priv->map_nolock); +- dev_err(dev, "regmap init failed: %d\n", ret); +- return ret; +- } +- +- /* Link forward and backward */ +- priv->dev = dev; +- priv->variant = var; +- priv->ops = var->ops; +- +- priv->setup_interface = realtek_smi_setup_mdio; +- priv->write_reg_noack = realtek_smi_write_reg_noack; +- +- dev_set_drvdata(dev, priv); +- spin_lock_init(&priv->lock); +- +- /* TODO: if power is software controlled, set up any regulators here */ +- +- priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); +- if (IS_ERR(priv->reset)) { +- dev_err(dev, "failed to get RESET GPIO\n"); +- return PTR_ERR(priv->reset); +- } +- if (priv->reset) { +- gpiod_set_value(priv->reset, 1); +- dev_dbg(dev, "asserted RESET\n"); +- msleep(REALTEK_HW_STOP_DELAY); +- gpiod_set_value(priv->reset, 0); +- msleep(REALTEK_HW_START_DELAY); +- dev_dbg(dev, "deasserted RESET\n"); +- } ++ priv = rtl83xx_probe(dev, &realtek_smi_info); ++ if (IS_ERR(priv)) ++ return PTR_ERR(priv); + + /* Fetch MDIO pins */ + priv->mdc = devm_gpiod_get_optional(dev, "mdc", GPIOD_OUT_LOW); +- if (IS_ERR(priv->mdc)) ++ if (IS_ERR(priv->mdc)) { ++ rtl83xx_remove(priv); + return PTR_ERR(priv->mdc); ++ } ++ + priv->mdio = devm_gpiod_get_optional(dev, "mdio", GPIOD_OUT_LOW); +- if (IS_ERR(priv->mdio)) ++ if (IS_ERR(priv->mdio)) { ++ rtl83xx_remove(priv); + return PTR_ERR(priv->mdio); +- +- priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); +- +- ret = priv->ops->detect(priv); +- if (ret) { +- dev_err(dev, "unable to detect switch\n"); +- return ret; + } + +- priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); +- if (!priv->ds) +- return -ENOMEM; +- +- priv->ds->dev = dev; +- priv->ds->num_ports = priv->num_ports; +- priv->ds->priv = priv; ++ priv->write_reg_noack = realtek_smi_write_reg_noack; ++ priv->setup_interface = realtek_smi_setup_mdio; ++ priv->ds_ops = priv->variant->ds_ops_smi; + +- priv->ds->ops = var->ds_ops_smi; +- ret = dsa_register_switch(priv->ds); ++ ret = rtl83xx_register_switch(priv); + if (ret) { +- dev_err_probe(dev, ret, "unable to register switch\n"); ++ rtl83xx_remove(priv); + return ret; + } ++ + return 0; + } + EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, REALTEK_DSA); +@@ -523,8 +428,8 @@ EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, + * @pdev: platform_device to be removed. + * + * This function should be used as the .remove_new in a platform_driver. First +- * it unregisters the DSA switch and cleans internal data. If a method is +- * provided, the hard reset is asserted to avoid traffic leakage. ++ * it unregisters the DSA switch and cleans internal data. Finally, it calls ++ * the common remove function. + * + * Context: Can sleep. + * Return: Nothing. +@@ -536,13 +441,12 @@ void realtek_smi_remove(struct platform_ + if (!priv) + return; + +- dsa_unregister_switch(priv->ds); ++ rtl83xx_unregister_switch(priv); ++ + if (priv->slave_mii_bus) + of_node_put(priv->slave_mii_bus->dev.of_node); + +- /* leave the device reset asserted */ +- if (priv->reset) +- gpiod_set_value(priv->reset, 1); ++ rtl83xx_remove(priv); + } + EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA); + +@@ -550,10 +454,8 @@ EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, + * realtek_smi_shutdown() - Shutdown the driver of a SMI-connected switch + * @pdev: platform_device shutting down. + * +- * This function should be used as the .shutdown in a platform_driver. It shuts +- * down the DSA switch and cleans the platform driver data, to prevent +- * realtek_smi_remove() from running afterwards, which is possible if the +- * parent bus implements its own .shutdown() as .remove(). ++ * This function should be used as the .shutdown in a platform_driver. It calls ++ * the common shutdown function. + * + * Context: Can sleep. + * Return: Nothing. +@@ -565,9 +467,7 @@ void realtek_smi_shutdown(struct platfor + if (!priv) + return; + +- dsa_switch_shutdown(priv->ds); +- +- platform_set_drvdata(pdev, NULL); ++ rtl83xx_shutdown(priv); + } + EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, REALTEK_DSA); + +--- a/drivers/net/dsa/realtek/realtek.h ++++ b/drivers/net/dsa/realtek/realtek.h +@@ -62,6 +62,7 @@ struct realtek_priv { + + spinlock_t lock; /* Locks around command writes */ + struct dsa_switch *ds; ++ const struct dsa_switch_ops *ds_ops; + struct irq_domain *irqdomain; + bool leds_disabled; + +--- a/drivers/net/dsa/realtek/rtl8365mb.c ++++ b/drivers/net/dsa/realtek/rtl8365mb.c +@@ -103,6 +103,7 @@ + #include "realtek.h" + #include "realtek-smi.h" + #include "realtek-mdio.h" ++#include "rtl83xx.h" + + /* Family-specific data and limits */ + #define RTL8365MB_PHYADDRMAX 7 +@@ -691,7 +692,7 @@ static int rtl8365mb_phy_ocp_read(struct + u32 val; + int ret; + +- mutex_lock(&priv->map_lock); ++ rtl83xx_lock(priv); + + ret = rtl8365mb_phy_poll_busy(priv); + if (ret) +@@ -724,7 +725,7 @@ static int rtl8365mb_phy_ocp_read(struct + *data = val & 0xFFFF; + + out: +- mutex_unlock(&priv->map_lock); ++ rtl83xx_unlock(priv); + + return ret; + } +@@ -735,7 +736,7 @@ static int rtl8365mb_phy_ocp_write(struc + u32 val; + int ret; + +- mutex_lock(&priv->map_lock); ++ rtl83xx_lock(priv); + + ret = rtl8365mb_phy_poll_busy(priv); + if (ret) +@@ -766,7 +767,7 @@ static int rtl8365mb_phy_ocp_write(struc + goto out; + + out: +- mutex_unlock(&priv->map_lock); ++ rtl83xx_unlock(priv); + + return 0; + } +--- a/drivers/net/dsa/realtek/rtl8366rb.c ++++ b/drivers/net/dsa/realtek/rtl8366rb.c +@@ -24,6 +24,7 @@ + #include "realtek.h" + #include "realtek-smi.h" + #include "realtek-mdio.h" ++#include "rtl83xx.h" + + #define RTL8366RB_PORT_NUM_CPU 5 + #define RTL8366RB_NUM_PORTS 6 +@@ -1634,7 +1635,7 @@ static int rtl8366rb_phy_read(struct rea + if (phy > RTL8366RB_PHY_NO_MAX) + return -EINVAL; + +- mutex_lock(&priv->map_lock); ++ rtl83xx_lock(priv); + + ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG, + RTL8366RB_PHY_CTRL_READ); +@@ -1662,7 +1663,7 @@ static int rtl8366rb_phy_read(struct rea + phy, regnum, reg, val); + + out: +- mutex_unlock(&priv->map_lock); ++ rtl83xx_unlock(priv); + + return ret; + } +@@ -1676,7 +1677,7 @@ static int rtl8366rb_phy_write(struct re + if (phy > RTL8366RB_PHY_NO_MAX) + return -EINVAL; + +- mutex_lock(&priv->map_lock); ++ rtl83xx_lock(priv); + + ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG, + RTL8366RB_PHY_CTRL_WRITE); +@@ -1693,7 +1694,7 @@ static int rtl8366rb_phy_write(struct re + goto out; + + out: +- mutex_unlock(&priv->map_lock); ++ rtl83xx_unlock(priv); + + return ret; + } +--- /dev/null ++++ b/drivers/net/dsa/realtek/rtl83xx.c +@@ -0,0 +1,236 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++ ++#include ++#include ++#include /* krnl 6.1 only */ ++ ++#include "realtek.h" ++#include "rtl83xx.h" ++ ++/** ++ * rtl83xx_lock() - Locks the mutex used by regmaps ++ * @ctx: realtek_priv pointer ++ * ++ * This function is passed to regmap to be used as the lock function. ++ * It is also used externally to block regmap before executing multiple ++ * operations that must happen in sequence (which will use ++ * realtek_priv.map_nolock instead). ++ * ++ * Context: Can sleep. Holds priv->map_lock lock. ++ * Return: nothing ++ */ ++void rtl83xx_lock(void *ctx) ++{ ++ struct realtek_priv *priv = ctx; ++ ++ mutex_lock(&priv->map_lock); ++} ++EXPORT_SYMBOL_NS_GPL(rtl83xx_lock, REALTEK_DSA); ++ ++/** ++ * rtl83xx_unlock() - Unlocks the mutex used by regmaps ++ * @ctx: realtek_priv pointer ++ * ++ * This function unlocks the lock acquired by rtl83xx_lock. ++ * ++ * Context: Releases priv->map_lock lock. ++ * Return: nothing ++ */ ++void rtl83xx_unlock(void *ctx) ++{ ++ struct realtek_priv *priv = ctx; ++ ++ mutex_unlock(&priv->map_lock); ++} ++EXPORT_SYMBOL_NS_GPL(rtl83xx_unlock, REALTEK_DSA); ++ ++/** ++ * rtl83xx_probe() - probe a Realtek switch ++ * @dev: the device being probed ++ * @interface_info: specific management interface info. ++ * ++ * This function initializes realtek_priv and reads data from the device tree ++ * node. The switch is hard resetted if a method is provided. ++ * ++ * Context: Can sleep. ++ * Return: Pointer to the realtek_priv or ERR_PTR() in case of failure. ++ * ++ * The realtek_priv pointer does not need to be freed as it is controlled by ++ * devres. ++ */ ++struct realtek_priv * ++rtl83xx_probe(struct device *dev, ++ const struct realtek_interface_info *interface_info) ++{ ++ const struct realtek_variant *var; ++ struct realtek_priv *priv; ++ struct regmap_config rc = { ++ .reg_bits = 10, /* A4..A0 R4..R0 */ ++ .val_bits = 16, ++ .reg_stride = 1, ++ .max_register = 0xffff, ++ .reg_format_endian = REGMAP_ENDIAN_BIG, ++ .reg_read = interface_info->reg_read, ++ .reg_write = interface_info->reg_write, ++ .cache_type = REGCACHE_NONE, ++ .lock = rtl83xx_lock, ++ .unlock = rtl83xx_unlock, ++ }; ++ int ret; ++ ++ var = of_device_get_match_data(dev); ++ if (!var) ++ return ERR_PTR(-EINVAL); ++ ++ priv = devm_kzalloc(dev, size_add(sizeof(*priv), var->chip_data_sz), ++ GFP_KERNEL); ++ if (!priv) ++ return ERR_PTR(-ENOMEM); ++ ++ mutex_init(&priv->map_lock); ++ ++ rc.lock_arg = priv; ++ priv->map = devm_regmap_init(dev, NULL, priv, &rc); ++ if (IS_ERR(priv->map)) { ++ ret = PTR_ERR(priv->map); ++ dev_err(dev, "regmap init failed: %d\n", ret); ++ return ERR_PTR(ret); ++ } ++ ++ rc.disable_locking = true; ++ priv->map_nolock = devm_regmap_init(dev, NULL, priv, &rc); ++ if (IS_ERR(priv->map_nolock)) { ++ ret = PTR_ERR(priv->map_nolock); ++ dev_err(dev, "regmap init failed: %d\n", ret); ++ return ERR_PTR(ret); ++ } ++ ++ /* Link forward and backward */ ++ priv->dev = dev; ++ priv->variant = var; ++ priv->ops = var->ops; ++ priv->chip_data = (void *)priv + sizeof(*priv); ++ ++ spin_lock_init(&priv->lock); ++ ++ priv->leds_disabled = of_property_read_bool(dev->of_node, ++ "realtek,disable-leds"); ++ ++ /* TODO: if power is software controlled, set up any regulators here */ ++ priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); ++ if (IS_ERR(priv->reset)) { ++ dev_err(dev, "failed to get RESET GPIO\n"); ++ return ERR_CAST(priv->reset); ++ } ++ ++ dev_set_drvdata(dev, priv); ++ ++ if (priv->reset) { ++ gpiod_set_value(priv->reset, 1); ++ dev_dbg(dev, "asserted RESET\n"); ++ msleep(REALTEK_HW_STOP_DELAY); ++ gpiod_set_value(priv->reset, 0); ++ msleep(REALTEK_HW_START_DELAY); ++ dev_dbg(dev, "deasserted RESET\n"); ++ } ++ ++ return priv; ++} ++EXPORT_SYMBOL_NS_GPL(rtl83xx_probe, REALTEK_DSA); ++ ++/** ++ * rtl83xx_register_switch() - detects and register a switch ++ * @priv: realtek_priv pointer ++ * ++ * This function first checks the switch chip ID and register a DSA ++ * switch. ++ * ++ * Context: Can sleep. Takes and releases priv->map_lock. ++ * Return: 0 on success, negative value for failure. ++ */ ++int rtl83xx_register_switch(struct realtek_priv *priv) ++{ ++ struct dsa_switch *ds; ++ int ret; ++ ++ ret = priv->ops->detect(priv); ++ if (ret) { ++ dev_err_probe(priv->dev, ret, "unable to detect switch\n"); ++ return ret; ++ } ++ ++ ds = devm_kzalloc(priv->dev, sizeof(*ds), GFP_KERNEL); ++ if (!ds) ++ return -ENOMEM; ++ ++ ds->priv = priv; ++ ds->dev = priv->dev; ++ ds->ops = priv->ds_ops; ++ ds->num_ports = priv->num_ports; ++ priv->ds = ds; ++ ++ ret = dsa_register_switch(ds); ++ if (ret) { ++ dev_err_probe(priv->dev, ret, "unable to register switch\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_NS_GPL(rtl83xx_register_switch, REALTEK_DSA); ++ ++/** ++ * rtl83xx_unregister_switch() - unregister a switch ++ * @priv: realtek_priv pointer ++ * ++ * This function unregister a DSA switch. ++ * ++ * Context: Can sleep. ++ * Return: Nothing. ++ */ ++void rtl83xx_unregister_switch(struct realtek_priv *priv) ++{ ++ dsa_unregister_switch(priv->ds); ++} ++EXPORT_SYMBOL_NS_GPL(rtl83xx_unregister_switch, REALTEK_DSA); ++ ++/** ++ * rtl83xx_shutdown() - shutdown a switch ++ * @priv: realtek_priv pointer ++ * ++ * This function shuts down the DSA switch and cleans the platform driver data, ++ * to prevent realtek_{smi,mdio}_remove() from running afterwards, which is ++ * possible if the parent bus implements its own .shutdown() as .remove(). ++ * ++ * Context: Can sleep. ++ * Return: Nothing. ++ */ ++void rtl83xx_shutdown(struct realtek_priv *priv) ++{ ++ dsa_switch_shutdown(priv->ds); ++ ++ dev_set_drvdata(priv->dev, NULL); ++} ++EXPORT_SYMBOL_NS_GPL(rtl83xx_shutdown, REALTEK_DSA); ++ ++/** ++ * rtl83xx_remove() - Cleanup a realtek switch driver ++ * @priv: realtek_priv pointer ++ * ++ * If a method is provided, this function asserts the hard reset of the switch ++ * in order to avoid leaking traffic when the driver is gone. ++ * ++ * Context: Might sleep if priv->gdev->chip->can_sleep. ++ * Return: nothing ++ */ ++void rtl83xx_remove(struct realtek_priv *priv) ++{ ++ /* leave the device reset asserted */ ++ if (priv->reset) ++ gpiod_set_value(priv->reset, 1); ++} ++EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA); ++ ++MODULE_AUTHOR("Luiz Angelo Daros de Luca "); ++MODULE_DESCRIPTION("Realtek DSA switches common module"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/net/dsa/realtek/rtl83xx.h +@@ -0,0 +1,21 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++ ++#ifndef _RTL83XX_H ++#define _RTL83XX_H ++ ++struct realtek_interface_info { ++ int (*reg_read)(void *ctx, u32 reg, u32 *val); ++ int (*reg_write)(void *ctx, u32 reg, u32 val); ++}; ++ ++void rtl83xx_lock(void *ctx); ++void rtl83xx_unlock(void *ctx); ++struct realtek_priv * ++rtl83xx_probe(struct device *dev, ++ const struct realtek_interface_info *interface_info); ++int rtl83xx_register_switch(struct realtek_priv *priv); ++void rtl83xx_unregister_switch(struct realtek_priv *priv); ++void rtl83xx_shutdown(struct realtek_priv *priv); ++void rtl83xx_remove(struct realtek_priv *priv); ++ ++#endif /* _RTL83XX_H */ diff --git a/target/linux/generic/backport-6.6/896-v6.9-0006-net-dsa-realtek-merge-rtl83xx-and-interface-modules-.patch b/target/linux/generic/backport-6.6/896-v6.9-0006-net-dsa-realtek-merge-rtl83xx-and-interface-modules-.patch new file mode 100644 index 0000000000..2754b62319 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0006-net-dsa-realtek-merge-rtl83xx-and-interface-modules-.patch @@ -0,0 +1,93 @@ +From 98b75c1c149c653ad11a440636213eb070325158 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:42 -0300 +Subject: net: dsa: realtek: merge rtl83xx and interface + modules into realtek_dsa + +Since rtl83xx and realtek-{smi,mdio} are always loaded together, +we can optimize resource usage by consolidating them into a single +module. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/Kconfig | 4 ++-- + drivers/net/dsa/realtek/Makefile | 11 +++++++++-- + drivers/net/dsa/realtek/realtek-mdio.c | 5 ----- + drivers/net/dsa/realtek/realtek-smi.c | 5 ----- + drivers/net/dsa/realtek/rtl83xx.c | 1 + + 5 files changed, 12 insertions(+), 14 deletions(-) + +--- a/drivers/net/dsa/realtek/Kconfig ++++ b/drivers/net/dsa/realtek/Kconfig +@@ -16,14 +16,14 @@ menuconfig NET_DSA_REALTEK + if NET_DSA_REALTEK + + config NET_DSA_REALTEK_MDIO +- tristate "Realtek MDIO interface support" ++ bool "Realtek MDIO interface support" + depends on OF + help + Select to enable support for registering switches configured + through MDIO. + + config NET_DSA_REALTEK_SMI +- tristate "Realtek SMI interface support" ++ bool "Realtek SMI interface support" + depends on OF + help + Select to enable support for registering switches connected +--- a/drivers/net/dsa/realtek/Makefile ++++ b/drivers/net/dsa/realtek/Makefile +@@ -1,8 +1,15 @@ + # SPDX-License-Identifier: GPL-2.0 + obj-$(CONFIG_NET_DSA_REALTEK) += realtek_dsa.o + realtek_dsa-objs := rtl83xx.o +-obj-$(CONFIG_NET_DSA_REALTEK_MDIO) += realtek-mdio.o +-obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o ++ ++ifdef CONFIG_NET_DSA_REALTEK_MDIO ++realtek_dsa-objs += realtek-mdio.o ++endif ++ ++ifdef CONFIG_NET_DSA_REALTEK_SMI ++realtek_dsa-objs += realtek-smi.o ++endif ++ + obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366.o + rtl8366-objs := rtl8366-core.o rtl8366rb.o + obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o +--- a/drivers/net/dsa/realtek/realtek-mdio.c ++++ b/drivers/net/dsa/realtek/realtek-mdio.c +@@ -186,8 +186,3 @@ void realtek_mdio_shutdown(struct mdio_d + rtl83xx_shutdown(priv); + } + EXPORT_SYMBOL_NS_GPL(realtek_mdio_shutdown, REALTEK_DSA); +- +-MODULE_AUTHOR("Luiz Angelo Daros de Luca "); +-MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface"); +-MODULE_LICENSE("GPL"); +-MODULE_IMPORT_NS(REALTEK_DSA); +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -470,8 +470,3 @@ void realtek_smi_shutdown(struct platfor + rtl83xx_shutdown(priv); + } + EXPORT_SYMBOL_NS_GPL(realtek_smi_shutdown, REALTEK_DSA); +- +-MODULE_AUTHOR("Linus Walleij "); +-MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface"); +-MODULE_LICENSE("GPL"); +-MODULE_IMPORT_NS(REALTEK_DSA); +--- a/drivers/net/dsa/realtek/rtl83xx.c ++++ b/drivers/net/dsa/realtek/rtl83xx.c +@@ -232,5 +232,6 @@ void rtl83xx_remove(struct realtek_priv + EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA); + + MODULE_AUTHOR("Luiz Angelo Daros de Luca "); ++MODULE_AUTHOR("Linus Walleij "); + MODULE_DESCRIPTION("Realtek DSA switches common module"); + MODULE_LICENSE("GPL"); diff --git a/target/linux/generic/backport-6.6/896-v6.9-0007-net-dsa-realtek-get-internal-MDIO-node-by-name.patch b/target/linux/generic/backport-6.6/896-v6.9-0007-net-dsa-realtek-get-internal-MDIO-node-by-name.patch new file mode 100644 index 0000000000..423e7500d7 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0007-net-dsa-realtek-get-internal-MDIO-node-by-name.patch @@ -0,0 +1,34 @@ +From 8685c98d45c54346caf005de69988e13c731c533 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:43 -0300 +Subject: net: dsa: realtek: get internal MDIO node by name + +The binding docs requires for SMI-connected devices that the switch +must have a child node named "mdio" and with a compatible string of +"realtek,smi-mdio". Meanwile, for MDIO-connected switches, the binding +docs only requires a child node named "mdio". + +This patch changes the driver to use the common denominator for both +interfaces, looking for the MDIO node by name, ignoring the compatible +string. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Vladimir Oltean +Reviewed-by: Florian Fainelli +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek-smi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -333,7 +333,7 @@ static int realtek_smi_setup_mdio(struct + struct device_node *mdio_np; + int ret; + +- mdio_np = of_get_compatible_child(priv->dev->of_node, "realtek,smi-mdio"); ++ mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio"); + if (!mdio_np) { + dev_err(priv->dev, "no MDIO bus node\n"); + return -ENODEV; diff --git a/target/linux/generic/backport-6.6/896-v6.9-0008-net-dsa-realtek-clean-user_mii_bus-setup.patch b/target/linux/generic/backport-6.6/896-v6.9-0008-net-dsa-realtek-clean-user_mii_bus-setup.patch new file mode 100644 index 0000000000..1958c7feb4 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0008-net-dsa-realtek-clean-user_mii_bus-setup.patch @@ -0,0 +1,83 @@ +From 68c66d8d8a19088967a0ab6bb98cb5ecc80ca0be Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:44 -0300 +Subject: net: dsa: realtek: clean slave_mii_bus setup + +Remove the line assigning dev.of_node in mdio_bus as subsequent +of_mdiobus_register will always overwrite it. + +As discussed in [1], allow the DSA core to be simplified, by not +assigning ds->slave_mii_bus when the MDIO bus is described in OF, as it +is unnecessary. + +Since commit 3b73a7b8ec38 ("net: mdio_bus: add refcounting for fwnodes +to mdiobus"), we can put the "mdio" node just after the MDIO bus +registration. + +[1] https://lkml.kernel.org/netdev/20231213120656.x46fyad6ls7sqyzv@skbuf/T/#u + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Vladimir Oltean +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek-smi.c | 13 +++---------- + 1 file changed, 3 insertions(+), 10 deletions(-) + +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -331,7 +331,7 @@ static int realtek_smi_setup_mdio(struct + { + struct realtek_priv *priv = ds->priv; + struct device_node *mdio_np; +- int ret; ++ int ret = 0; + + mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio"); + if (!mdio_np) { +@@ -344,15 +344,14 @@ static int realtek_smi_setup_mdio(struct + ret = -ENOMEM; + goto err_put_node; + } ++ + priv->slave_mii_bus->priv = priv; + priv->slave_mii_bus->name = "SMI slave MII"; + priv->slave_mii_bus->read = realtek_smi_mdio_read; + priv->slave_mii_bus->write = realtek_smi_mdio_write; + snprintf(priv->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d", + ds->index); +- priv->slave_mii_bus->dev.of_node = mdio_np; + priv->slave_mii_bus->parent = priv->dev; +- ds->slave_mii_bus = priv->slave_mii_bus; + + ret = devm_of_mdiobus_register(priv->dev, priv->slave_mii_bus, mdio_np); + if (ret) { +@@ -361,8 +360,6 @@ static int realtek_smi_setup_mdio(struct + goto err_put_node; + } + +- return 0; +- + err_put_node: + of_node_put(mdio_np); + +@@ -428,8 +425,7 @@ EXPORT_SYMBOL_NS_GPL(realtek_smi_probe, + * @pdev: platform_device to be removed. + * + * This function should be used as the .remove_new in a platform_driver. First +- * it unregisters the DSA switch and cleans internal data. Finally, it calls +- * the common remove function. ++ * it unregisters the DSA switch and then it calls the common remove function. + * + * Context: Can sleep. + * Return: Nothing. +@@ -443,9 +439,6 @@ void realtek_smi_remove(struct platform_ + + rtl83xx_unregister_switch(priv); + +- if (priv->slave_mii_bus) +- of_node_put(priv->slave_mii_bus->dev.of_node); +- + rtl83xx_remove(priv); + } + EXPORT_SYMBOL_NS_GPL(realtek_smi_remove, REALTEK_DSA); diff --git a/target/linux/generic/backport-6.6/896-v6.9-0009-net-dsa-realtek-migrate-user_mii_bus-setup-to-realte.patch b/target/linux/generic/backport-6.6/896-v6.9-0009-net-dsa-realtek-migrate-user_mii_bus-setup-to-realte.patch new file mode 100644 index 0000000000..50e96d7eef --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0009-net-dsa-realtek-migrate-user_mii_bus-setup-to-realte.patch @@ -0,0 +1,198 @@ +From b4bd77971f3c290c4694ed710cc6967593b10bc2 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:45 -0300 +Subject: net: dsa: realtek: migrate slave_mii_bus setup to + realtek_dsa + +In the user MDIO driver, despite numerous references to SMI, including +its compatible string, there's nothing inherently specific about the SMI +interface in the user MDIO bus. Consequently, the code has been migrated +to the rtl83xx module. All references to SMI have been eliminated. + +The MDIO bus id was changed from Realtek- to the switch +devname suffixed with :slave_mii, giving more information about the bus +it is referencing. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek-smi.c | 57 +--------------------- + drivers/net/dsa/realtek/rtl83xx.c | 68 +++++++++++++++++++++++++++ + drivers/net/dsa/realtek/rtl83xx.h | 1 + + 3 files changed, 70 insertions(+), 56 deletions(-) + +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -31,7 +31,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -312,60 +311,6 @@ static int realtek_smi_read(void *ctx, u + return realtek_smi_read_reg(priv, reg, val); + } + +-static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum) +-{ +- struct realtek_priv *priv = bus->priv; +- +- return priv->ops->phy_read(priv, addr, regnum); +-} +- +-static int realtek_smi_mdio_write(struct mii_bus *bus, int addr, int regnum, +- u16 val) +-{ +- struct realtek_priv *priv = bus->priv; +- +- return priv->ops->phy_write(priv, addr, regnum, val); +-} +- +-static int realtek_smi_setup_mdio(struct dsa_switch *ds) +-{ +- struct realtek_priv *priv = ds->priv; +- struct device_node *mdio_np; +- int ret = 0; +- +- mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio"); +- if (!mdio_np) { +- dev_err(priv->dev, "no MDIO bus node\n"); +- return -ENODEV; +- } +- +- priv->slave_mii_bus = devm_mdiobus_alloc(priv->dev); +- if (!priv->slave_mii_bus) { +- ret = -ENOMEM; +- goto err_put_node; +- } +- +- priv->slave_mii_bus->priv = priv; +- priv->slave_mii_bus->name = "SMI slave MII"; +- priv->slave_mii_bus->read = realtek_smi_mdio_read; +- priv->slave_mii_bus->write = realtek_smi_mdio_write; +- snprintf(priv->slave_mii_bus->id, MII_BUS_ID_SIZE, "SMI-%d", +- ds->index); +- priv->slave_mii_bus->parent = priv->dev; +- +- ret = devm_of_mdiobus_register(priv->dev, priv->slave_mii_bus, mdio_np); +- if (ret) { +- dev_err(priv->dev, "unable to register MDIO bus %s\n", +- priv->slave_mii_bus->id); +- goto err_put_node; +- } +- +-err_put_node: +- of_node_put(mdio_np); +- +- return ret; +-} +- + static const struct realtek_interface_info realtek_smi_info = { + .reg_read = realtek_smi_read, + .reg_write = realtek_smi_write, +@@ -407,7 +352,7 @@ int realtek_smi_probe(struct platform_de + } + + priv->write_reg_noack = realtek_smi_write_reg_noack; +- priv->setup_interface = realtek_smi_setup_mdio; ++ priv->setup_interface = rtl83xx_setup_user_mdio; + priv->ds_ops = priv->variant->ds_ops_smi; + + ret = rtl83xx_register_switch(priv); +--- a/drivers/net/dsa/realtek/rtl83xx.c ++++ b/drivers/net/dsa/realtek/rtl83xx.c +@@ -3,6 +3,7 @@ + #include + #include + #include /* krnl 6.1 only */ ++#include + + #include "realtek.h" + #include "rtl83xx.h" +@@ -44,6 +45,73 @@ void rtl83xx_unlock(void *ctx) + } + EXPORT_SYMBOL_NS_GPL(rtl83xx_unlock, REALTEK_DSA); + ++static int rtl83xx_user_mdio_read(struct mii_bus *bus, int addr, int regnum) ++{ ++ struct realtek_priv *priv = bus->priv; ++ ++ return priv->ops->phy_read(priv, addr, regnum); ++} ++ ++static int rtl83xx_user_mdio_write(struct mii_bus *bus, int addr, int regnum, ++ u16 val) ++{ ++ struct realtek_priv *priv = bus->priv; ++ ++ return priv->ops->phy_write(priv, addr, regnum, val); ++} ++ ++/** ++ * rtl83xx_setup_user_mdio() - register the user mii bus driver ++ * @ds: DSA switch associated with this slave_mii_bus ++ * ++ * Registers the MDIO bus for built-in Ethernet PHYs, and associates it with ++ * the mandatory 'mdio' child OF node of the switch. ++ * ++ * Context: Can sleep. ++ * Return: 0 on success, negative value for failure. ++ */ ++int rtl83xx_setup_user_mdio(struct dsa_switch *ds) ++{ ++ struct realtek_priv *priv = ds->priv; ++ struct device_node *mdio_np; ++ struct mii_bus *bus; ++ int ret = 0; ++ ++ mdio_np = of_get_child_by_name(priv->dev->of_node, "mdio"); ++ if (!mdio_np) { ++ dev_err(priv->dev, "no MDIO bus node\n"); ++ return -ENODEV; ++ } ++ ++ bus = devm_mdiobus_alloc(priv->dev); ++ if (!bus) { ++ ret = -ENOMEM; ++ goto err_put_node; ++ } ++ ++ bus->priv = priv; ++ bus->name = "Realtek user MII"; ++ bus->read = rtl83xx_user_mdio_read; ++ bus->write = rtl83xx_user_mdio_write; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "%s:slave_mii", dev_name(priv->dev)); ++ bus->parent = priv->dev; ++ ++ ret = devm_of_mdiobus_register(priv->dev, bus, mdio_np); ++ if (ret) { ++ dev_err(priv->dev, "unable to register MDIO bus %s\n", ++ bus->id); ++ goto err_put_node; ++ } ++ ++ priv->slave_mii_bus = bus; ++ ++err_put_node: ++ of_node_put(mdio_np); ++ ++ return ret; ++} ++EXPORT_SYMBOL_NS_GPL(rtl83xx_setup_user_mdio, REALTEK_DSA); ++ + /** + * rtl83xx_probe() - probe a Realtek switch + * @dev: the device being probed +--- a/drivers/net/dsa/realtek/rtl83xx.h ++++ b/drivers/net/dsa/realtek/rtl83xx.h +@@ -10,6 +10,7 @@ struct realtek_interface_info { + + void rtl83xx_lock(void *ctx); + void rtl83xx_unlock(void *ctx); ++int rtl83xx_setup_user_mdio(struct dsa_switch *ds); + struct realtek_priv * + rtl83xx_probe(struct device *dev, + const struct realtek_interface_info *interface_info); diff --git a/target/linux/generic/backport-6.6/896-v6.9-0010-net-dsa-realtek-use-the-same-mii-bus-driver-for-both.patch b/target/linux/generic/backport-6.6/896-v6.9-0010-net-dsa-realtek-use-the-same-mii-bus-driver-for-both.patch new file mode 100644 index 0000000000..b50a33590b --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0010-net-dsa-realtek-use-the-same-mii-bus-driver-for-both.patch @@ -0,0 +1,261 @@ +From bba140a566ed075304c49c52ab32c0016cab624a Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:46 -0300 +Subject: net: dsa: realtek: use the same mii bus driver for + both interfaces + +The realtek-mdio will now use this driver instead of the generic DSA +driver ("dsa user smi"), which should not be used with OF[1]. + +With a single ds_ops for both interfaces, the ds_ops in realtek_priv is +no longer necessary. Now, the realtek_variant.ds_ops can be used +directly. + +The realtek_priv.setup_interface() has been removed as we can directly +call the new common function. + +[1] https://lkml.kernel.org/netdev/20220630200423.tieprdu5fpabflj7@bang-olufsen.dk/T/ + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Vladimir Oltean +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek-mdio.c | 1 - + drivers/net/dsa/realtek/realtek-smi.c | 2 - + drivers/net/dsa/realtek/realtek.h | 5 +-- + drivers/net/dsa/realtek/rtl8365mb.c | 49 +++--------------------- + drivers/net/dsa/realtek/rtl8366rb.c | 52 +++----------------------- + drivers/net/dsa/realtek/rtl83xx.c | 2 +- + 6 files changed, 14 insertions(+), 97 deletions(-) + +--- a/drivers/net/dsa/realtek/realtek-mdio.c ++++ b/drivers/net/dsa/realtek/realtek-mdio.c +@@ -131,7 +131,6 @@ int realtek_mdio_probe(struct mdio_devic + priv->bus = mdiodev->bus; + priv->mdio_addr = mdiodev->addr; + priv->write_reg_noack = realtek_mdio_write; +- priv->ds_ops = priv->variant->ds_ops_mdio; + + ret = rtl83xx_register_switch(priv); + if (ret) { +--- a/drivers/net/dsa/realtek/realtek-smi.c ++++ b/drivers/net/dsa/realtek/realtek-smi.c +@@ -352,8 +352,6 @@ int realtek_smi_probe(struct platform_de + } + + priv->write_reg_noack = realtek_smi_write_reg_noack; +- priv->setup_interface = rtl83xx_setup_user_mdio; +- priv->ds_ops = priv->variant->ds_ops_smi; + + ret = rtl83xx_register_switch(priv); + if (ret) { +--- a/drivers/net/dsa/realtek/realtek.h ++++ b/drivers/net/dsa/realtek/realtek.h +@@ -62,7 +62,6 @@ struct realtek_priv { + + spinlock_t lock; /* Locks around command writes */ + struct dsa_switch *ds; +- const struct dsa_switch_ops *ds_ops; + struct irq_domain *irqdomain; + bool leds_disabled; + +@@ -73,7 +72,6 @@ struct realtek_priv { + struct rtl8366_mib_counter *mib_counters; + + const struct realtek_ops *ops; +- int (*setup_interface)(struct dsa_switch *ds); + int (*write_reg_noack)(void *ctx, u32 addr, u32 data); + + int vlan_enabled; +@@ -115,8 +113,7 @@ struct realtek_ops { + }; + + struct realtek_variant { +- const struct dsa_switch_ops *ds_ops_smi; +- const struct dsa_switch_ops *ds_ops_mdio; ++ const struct dsa_switch_ops *ds_ops; + const struct realtek_ops *ops; + unsigned int clk_delay; + u8 cmd_read; +--- a/drivers/net/dsa/realtek/rtl8365mb.c ++++ b/drivers/net/dsa/realtek/rtl8365mb.c +@@ -828,17 +828,6 @@ static int rtl8365mb_phy_write(struct re + return 0; + } + +-static int rtl8365mb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum) +-{ +- return rtl8365mb_phy_read(ds->priv, phy, regnum); +-} +- +-static int rtl8365mb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum, +- u16 val) +-{ +- return rtl8365mb_phy_write(ds->priv, phy, regnum, val); +-} +- + static const struct rtl8365mb_extint * + rtl8365mb_get_port_extint(struct realtek_priv *priv, int port) + { +@@ -2018,12 +2007,10 @@ static int rtl8365mb_setup(struct dsa_sw + if (ret) + goto out_teardown_irq; + +- if (priv->setup_interface) { +- ret = priv->setup_interface(ds); +- if (ret) { +- dev_err(priv->dev, "could not set up MDIO bus\n"); +- goto out_teardown_irq; +- } ++ ret = rtl83xx_setup_user_mdio(ds); ++ if (ret) { ++ dev_err(priv->dev, "could not set up MDIO bus\n"); ++ goto out_teardown_irq; + } + + /* Start statistics counter polling */ +@@ -2117,28 +2104,7 @@ static int rtl8365mb_detect(struct realt + return 0; + } + +-static const struct dsa_switch_ops rtl8365mb_switch_ops_smi = { +- .get_tag_protocol = rtl8365mb_get_tag_protocol, +- .change_tag_protocol = rtl8365mb_change_tag_protocol, +- .setup = rtl8365mb_setup, +- .teardown = rtl8365mb_teardown, +- .phylink_get_caps = rtl8365mb_phylink_get_caps, +- .phylink_mac_config = rtl8365mb_phylink_mac_config, +- .phylink_mac_link_down = rtl8365mb_phylink_mac_link_down, +- .phylink_mac_link_up = rtl8365mb_phylink_mac_link_up, +- .port_stp_state_set = rtl8365mb_port_stp_state_set, +- .get_strings = rtl8365mb_get_strings, +- .get_ethtool_stats = rtl8365mb_get_ethtool_stats, +- .get_sset_count = rtl8365mb_get_sset_count, +- .get_eth_phy_stats = rtl8365mb_get_phy_stats, +- .get_eth_mac_stats = rtl8365mb_get_mac_stats, +- .get_eth_ctrl_stats = rtl8365mb_get_ctrl_stats, +- .get_stats64 = rtl8365mb_get_stats64, +- .port_change_mtu = rtl8365mb_port_change_mtu, +- .port_max_mtu = rtl8365mb_port_max_mtu, +-}; +- +-static const struct dsa_switch_ops rtl8365mb_switch_ops_mdio = { ++static const struct dsa_switch_ops rtl8365mb_switch_ops = { + .get_tag_protocol = rtl8365mb_get_tag_protocol, + .change_tag_protocol = rtl8365mb_change_tag_protocol, + .setup = rtl8365mb_setup, +@@ -2147,8 +2113,6 @@ static const struct dsa_switch_ops rtl83 + .phylink_mac_config = rtl8365mb_phylink_mac_config, + .phylink_mac_link_down = rtl8365mb_phylink_mac_link_down, + .phylink_mac_link_up = rtl8365mb_phylink_mac_link_up, +- .phy_read = rtl8365mb_dsa_phy_read, +- .phy_write = rtl8365mb_dsa_phy_write, + .port_stp_state_set = rtl8365mb_port_stp_state_set, + .get_strings = rtl8365mb_get_strings, + .get_ethtool_stats = rtl8365mb_get_ethtool_stats, +@@ -2168,8 +2132,7 @@ static const struct realtek_ops rtl8365m + }; + + const struct realtek_variant rtl8365mb_variant = { +- .ds_ops_smi = &rtl8365mb_switch_ops_smi, +- .ds_ops_mdio = &rtl8365mb_switch_ops_mdio, ++ .ds_ops = &rtl8365mb_switch_ops, + .ops = &rtl8365mb_ops, + .clk_delay = 10, + .cmd_read = 0xb9, +--- a/drivers/net/dsa/realtek/rtl8366rb.c ++++ b/drivers/net/dsa/realtek/rtl8366rb.c +@@ -1035,12 +1035,10 @@ static int rtl8366rb_setup(struct dsa_sw + if (ret) + dev_info(priv->dev, "no interrupt support\n"); + +- if (priv->setup_interface) { +- ret = priv->setup_interface(ds); +- if (ret) { +- dev_err(priv->dev, "could not set up MDIO bus\n"); +- return -ENODEV; +- } ++ ret = rtl83xx_setup_user_mdio(ds); ++ if (ret) { ++ dev_err(priv->dev, "could not set up MDIO bus\n"); ++ return -ENODEV; + } + + return 0; +@@ -1699,17 +1697,6 @@ out: + return ret; + } + +-static int rtl8366rb_dsa_phy_read(struct dsa_switch *ds, int phy, int regnum) +-{ +- return rtl8366rb_phy_read(ds->priv, phy, regnum); +-} +- +-static int rtl8366rb_dsa_phy_write(struct dsa_switch *ds, int phy, int regnum, +- u16 val) +-{ +- return rtl8366rb_phy_write(ds->priv, phy, regnum, val); +-} +- + static int rtl8366rb_reset_chip(struct realtek_priv *priv) + { + int timeout = 10; +@@ -1775,35 +1762,9 @@ static int rtl8366rb_detect(struct realt + return 0; + } + +-static const struct dsa_switch_ops rtl8366rb_switch_ops_smi = { +- .get_tag_protocol = rtl8366_get_tag_protocol, +- .setup = rtl8366rb_setup, +- .phylink_get_caps = rtl8366rb_phylink_get_caps, +- .phylink_mac_link_up = rtl8366rb_mac_link_up, +- .phylink_mac_link_down = rtl8366rb_mac_link_down, +- .get_strings = rtl8366_get_strings, +- .get_ethtool_stats = rtl8366_get_ethtool_stats, +- .get_sset_count = rtl8366_get_sset_count, +- .port_bridge_join = rtl8366rb_port_bridge_join, +- .port_bridge_leave = rtl8366rb_port_bridge_leave, +- .port_vlan_filtering = rtl8366rb_vlan_filtering, +- .port_vlan_add = rtl8366_vlan_add, +- .port_vlan_del = rtl8366_vlan_del, +- .port_enable = rtl8366rb_port_enable, +- .port_disable = rtl8366rb_port_disable, +- .port_pre_bridge_flags = rtl8366rb_port_pre_bridge_flags, +- .port_bridge_flags = rtl8366rb_port_bridge_flags, +- .port_stp_state_set = rtl8366rb_port_stp_state_set, +- .port_fast_age = rtl8366rb_port_fast_age, +- .port_change_mtu = rtl8366rb_change_mtu, +- .port_max_mtu = rtl8366rb_max_mtu, +-}; +- +-static const struct dsa_switch_ops rtl8366rb_switch_ops_mdio = { ++static const struct dsa_switch_ops rtl8366rb_switch_ops = { + .get_tag_protocol = rtl8366_get_tag_protocol, + .setup = rtl8366rb_setup, +- .phy_read = rtl8366rb_dsa_phy_read, +- .phy_write = rtl8366rb_dsa_phy_write, + .phylink_get_caps = rtl8366rb_phylink_get_caps, + .phylink_mac_link_up = rtl8366rb_mac_link_up, + .phylink_mac_link_down = rtl8366rb_mac_link_down, +@@ -1842,8 +1803,7 @@ static const struct realtek_ops rtl8366r + }; + + const struct realtek_variant rtl8366rb_variant = { +- .ds_ops_smi = &rtl8366rb_switch_ops_smi, +- .ds_ops_mdio = &rtl8366rb_switch_ops_mdio, ++ .ds_ops = &rtl8366rb_switch_ops, + .ops = &rtl8366rb_ops, + .clk_delay = 10, + .cmd_read = 0xa9, +--- a/drivers/net/dsa/realtek/rtl83xx.c ++++ b/drivers/net/dsa/realtek/rtl83xx.c +@@ -233,7 +233,7 @@ int rtl83xx_register_switch(struct realt + + ds->priv = priv; + ds->dev = priv->dev; +- ds->ops = priv->ds_ops; ++ ds->ops = priv->variant->ds_ops; + ds->num_ports = priv->num_ports; + priv->ds = ds; + diff --git a/target/linux/generic/backport-6.6/896-v6.9-0011-net-dsa-realtek-embed-dsa_switch-into-realtek_priv.patch b/target/linux/generic/backport-6.6/896-v6.9-0011-net-dsa-realtek-embed-dsa_switch-into-realtek_priv.patch new file mode 100644 index 0000000000..4cb55f2da9 --- /dev/null +++ b/target/linux/generic/backport-6.6/896-v6.9-0011-net-dsa-realtek-embed-dsa_switch-into-realtek_priv.patch @@ -0,0 +1,180 @@ +From 9fc469b2943d9b1ff2a7800f823e7cd7a5cac0ca Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Fri, 9 Feb 2024 02:03:47 -0300 +Subject: net: dsa: realtek: embed dsa_switch into realtek_priv +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Embed dsa_switch within realtek_priv to eliminate the need for a second +memory allocation. + +Suggested-by: Alvin Šipraga +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Vladimir Oltean +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek.h | 2 +- + drivers/net/dsa/realtek/rtl8365mb.c | 15 +++++++++------ + drivers/net/dsa/realtek/rtl8366rb.c | 3 ++- + drivers/net/dsa/realtek/rtl83xx.c | 15 +++++++-------- + 4 files changed, 19 insertions(+), 16 deletions(-) + +--- a/drivers/net/dsa/realtek/realtek.h ++++ b/drivers/net/dsa/realtek/realtek.h +@@ -61,7 +61,7 @@ struct realtek_priv { + const struct realtek_variant *variant; + + spinlock_t lock; /* Locks around command writes */ +- struct dsa_switch *ds; ++ struct dsa_switch ds; + struct irq_domain *irqdomain; + bool leds_disabled; + +--- a/drivers/net/dsa/realtek/rtl8365mb.c ++++ b/drivers/net/dsa/realtek/rtl8365mb.c +@@ -870,6 +870,7 @@ static int rtl8365mb_ext_config_rgmii(st + { + const struct rtl8365mb_extint *extint = + rtl8365mb_get_port_extint(priv, port); ++ struct dsa_switch *ds = &priv->ds; + struct device_node *dn; + struct dsa_port *dp; + int tx_delay = 0; +@@ -880,7 +881,7 @@ static int rtl8365mb_ext_config_rgmii(st + if (!extint) + return -ENODEV; + +- dp = dsa_to_port(priv->ds, port); ++ dp = dsa_to_port(ds, port); + dn = dp->dn; + + /* Set the RGMII TX/RX delay +@@ -1534,6 +1535,7 @@ static void rtl8365mb_get_stats64(struct + static void rtl8365mb_stats_setup(struct realtek_priv *priv) + { + struct rtl8365mb *mb = priv->chip_data; ++ struct dsa_switch *ds = &priv->ds; + int i; + + /* Per-chip global mutex to protect MIB counter access, since doing +@@ -1544,7 +1546,7 @@ static void rtl8365mb_stats_setup(struct + for (i = 0; i < priv->num_ports; i++) { + struct rtl8365mb_port *p = &mb->ports[i]; + +- if (dsa_is_unused_port(priv->ds, i)) ++ if (dsa_is_unused_port(ds, i)) + continue; + + /* Per-port spinlock to protect the stats64 data */ +@@ -1560,12 +1562,13 @@ static void rtl8365mb_stats_setup(struct + static void rtl8365mb_stats_teardown(struct realtek_priv *priv) + { + struct rtl8365mb *mb = priv->chip_data; ++ struct dsa_switch *ds = &priv->ds; + int i; + + for (i = 0; i < priv->num_ports; i++) { + struct rtl8365mb_port *p = &mb->ports[i]; + +- if (dsa_is_unused_port(priv->ds, i)) ++ if (dsa_is_unused_port(ds, i)) + continue; + + cancel_delayed_work_sync(&p->mib_work); +@@ -1964,7 +1967,7 @@ static int rtl8365mb_setup(struct dsa_sw + dev_info(priv->dev, "no interrupt support\n"); + + /* Configure CPU tagging */ +- dsa_switch_for_each_cpu_port(cpu_dp, priv->ds) { ++ dsa_switch_for_each_cpu_port(cpu_dp, ds) { + cpu->mask |= BIT(cpu_dp->index); + + if (cpu->trap_port == RTL8365MB_MAX_NUM_PORTS) +@@ -1979,7 +1982,7 @@ static int rtl8365mb_setup(struct dsa_sw + for (i = 0; i < priv->num_ports; i++) { + struct rtl8365mb_port *p = &mb->ports[i]; + +- if (dsa_is_unused_port(priv->ds, i)) ++ if (dsa_is_unused_port(ds, i)) + continue; + + /* Forward only to the CPU */ +@@ -1996,7 +1999,7 @@ static int rtl8365mb_setup(struct dsa_sw + * ports will still forward frames to the CPU despite being + * administratively down by default. + */ +- rtl8365mb_port_stp_state_set(priv->ds, i, BR_STATE_DISABLED); ++ rtl8365mb_port_stp_state_set(ds, i, BR_STATE_DISABLED); + + /* Set up per-port private data */ + p->priv = priv; +--- a/drivers/net/dsa/realtek/rtl8366rb.c ++++ b/drivers/net/dsa/realtek/rtl8366rb.c +@@ -1565,6 +1565,7 @@ static int rtl8366rb_get_mc_index(struct + + static int rtl8366rb_set_mc_index(struct realtek_priv *priv, int port, int index) + { ++ struct dsa_switch *ds = &priv->ds; + struct rtl8366rb *rb; + bool pvid_enabled; + int ret; +@@ -1589,7 +1590,7 @@ static int rtl8366rb_set_mc_index(struct + * not drop any untagged or C-tagged frames. Make sure to update the + * filtering setting. + */ +- if (dsa_port_is_vlan_filtering(dsa_to_port(priv->ds, port))) ++ if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) + ret = rtl8366rb_drop_untagged(priv, port, !pvid_enabled); + + return ret; +--- a/drivers/net/dsa/realtek/rtl83xx.c ++++ b/drivers/net/dsa/realtek/rtl83xx.c +@@ -218,7 +218,7 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_probe, REAL + */ + int rtl83xx_register_switch(struct realtek_priv *priv) + { +- struct dsa_switch *ds; ++ struct dsa_switch *ds = &priv->ds; + int ret; + + ret = priv->ops->detect(priv); +@@ -227,15 +227,10 @@ int rtl83xx_register_switch(struct realt + return ret; + } + +- ds = devm_kzalloc(priv->dev, sizeof(*ds), GFP_KERNEL); +- if (!ds) +- return -ENOMEM; +- + ds->priv = priv; + ds->dev = priv->dev; + ds->ops = priv->variant->ds_ops; + ds->num_ports = priv->num_ports; +- priv->ds = ds; + + ret = dsa_register_switch(ds); + if (ret) { +@@ -258,7 +253,9 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_register_sw + */ + void rtl83xx_unregister_switch(struct realtek_priv *priv) + { +- dsa_unregister_switch(priv->ds); ++ struct dsa_switch *ds = &priv->ds; ++ ++ dsa_unregister_switch(ds); + } + EXPORT_SYMBOL_NS_GPL(rtl83xx_unregister_switch, REALTEK_DSA); + +@@ -275,7 +272,9 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_unregister_ + */ + void rtl83xx_shutdown(struct realtek_priv *priv) + { +- dsa_switch_shutdown(priv->ds); ++ struct dsa_switch *ds = &priv->ds; ++ ++ dsa_switch_shutdown(ds); + + dev_set_drvdata(priv->dev, NULL); + } diff --git a/target/linux/generic/backport-6.6/897-v6.9-net-dsa-realtek-fix-digital-interface-select-macro-f.patch b/target/linux/generic/backport-6.6/897-v6.9-net-dsa-realtek-fix-digital-interface-select-macro-f.patch new file mode 100644 index 0000000000..a3ccf5d4ad --- /dev/null +++ b/target/linux/generic/backport-6.6/897-v6.9-net-dsa-realtek-fix-digital-interface-select-macro-f.patch @@ -0,0 +1,38 @@ +From 32e4a5447ed9fa904a2dfcf4609c64bce053b4e8 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Mon, 12 Feb 2024 18:34:33 -0300 +Subject: [PATCH] net: dsa: realtek: fix digital interface select macro for + EXT0 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +While no supported devices currently utilize EXT0, the register reserves +the bits for an EXT0. EXT0 is utilized by devices from the generation +prior to rtl8365mb, such as those supported by the driver library +rtl8367b. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Alvin Šipraga +Reviewed-by: Linus Walleij +Link: https://lore.kernel.org/r/20240212-realtek-fix_ext0-v1-1-f3d2536d191a@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/realtek/rtl8365mb.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/realtek/rtl8365mb.c ++++ b/drivers/net/dsa/realtek/rtl8365mb.c +@@ -209,10 +209,10 @@ + #define RTL8365MB_EXT_PORT_MODE_100FX 13 + + /* External interface mode configuration registers 0~1 */ +-#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 /* EXT1 */ ++#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 /* EXT0,EXT1 */ + #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 0x13C3 /* EXT2 */ + #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(_extint) \ +- ((_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \ ++ ((_extint) <= 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \ + (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 : \ + 0x0) + #define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(_extint) \ diff --git a/target/linux/generic/backport-6.6/898-v6.9-0001-dt-bindings-net-dsa-realtek-reset-gpios-is-not-requi.patch b/target/linux/generic/backport-6.6/898-v6.9-0001-dt-bindings-net-dsa-realtek-reset-gpios-is-not-requi.patch new file mode 100644 index 0000000000..e42dfc5d4d --- /dev/null +++ b/target/linux/generic/backport-6.6/898-v6.9-0001-dt-bindings-net-dsa-realtek-reset-gpios-is-not-requi.patch @@ -0,0 +1,34 @@ +From 28001bb1955fcfa63e535848c4289fcd7bb88daf Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Sun, 25 Feb 2024 13:29:53 -0300 +Subject: [PATCH 1/3] dt-bindings: net: dsa: realtek: reset-gpios is not + required +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The 'reset-gpios' should not be mandatory. although they might be +required for some devices if the switch reset was left asserted by a +previous driver, such as the bootloader. + +Signed-off-by: Luiz Angelo Daros de Luca +Cc: devicetree@vger.kernel.org +Acked-by: Arınç ÜNAL +Acked-by: Rob Herring +Reviewed-by: Linus Walleij +Reviewed-by: Alvin Šipraga +Signed-off-by: David S. Miller +--- + Documentation/devicetree/bindings/net/dsa/realtek.yaml | 1 - + 1 file changed, 1 deletion(-) + +--- a/Documentation/devicetree/bindings/net/dsa/realtek.yaml ++++ b/Documentation/devicetree/bindings/net/dsa/realtek.yaml +@@ -125,7 +125,6 @@ else: + - mdc-gpios + - mdio-gpios + - mdio +- - reset-gpios + + required: + - compatible diff --git a/target/linux/generic/backport-6.6/898-v6.9-0002-dt-bindings-net-dsa-realtek-add-reset-controller.patch b/target/linux/generic/backport-6.6/898-v6.9-0002-dt-bindings-net-dsa-realtek-add-reset-controller.patch new file mode 100644 index 0000000000..7e8a240626 --- /dev/null +++ b/target/linux/generic/backport-6.6/898-v6.9-0002-dt-bindings-net-dsa-realtek-add-reset-controller.patch @@ -0,0 +1,33 @@ +From 5fc2d68fc81801162188995e4d3dc0b26747dd76 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Sun, 25 Feb 2024 13:29:54 -0300 +Subject: [PATCH 2/3] dt-bindings: net: dsa: realtek: add reset controller +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Realtek switches can use a reset controller instead of reset-gpios. + +Signed-off-by: Luiz Angelo Daros de Luca +Cc: devicetree@vger.kernel.org +Acked-by: Arınç ÜNAL +Reviewed-by: Linus Walleij +Reviewed-by: Alvin Šipraga +Acked-by: Rob Herring +Signed-off-by: David S. Miller +--- + Documentation/devicetree/bindings/net/dsa/realtek.yaml | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/Documentation/devicetree/bindings/net/dsa/realtek.yaml ++++ b/Documentation/devicetree/bindings/net/dsa/realtek.yaml +@@ -59,6 +59,9 @@ properties: + description: GPIO to be used to reset the whole device + maxItems: 1 + ++ resets: ++ maxItems: 1 ++ + realtek,disable-leds: + type: boolean + description: | diff --git a/target/linux/generic/backport-6.6/898-v6.9-0003-net-dsa-realtek-support-reset-controller.patch b/target/linux/generic/backport-6.6/898-v6.9-0003-net-dsa-realtek-support-reset-controller.patch new file mode 100644 index 0000000000..7a280bf7ad --- /dev/null +++ b/target/linux/generic/backport-6.6/898-v6.9-0003-net-dsa-realtek-support-reset-controller.patch @@ -0,0 +1,123 @@ +From 56998aa6b7f0f31ce8df23c00701af2d8e8a1f1a Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Sun, 25 Feb 2024 13:29:55 -0300 +Subject: [PATCH 3/3] net: dsa: realtek: support reset controller +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add support for resetting the device using a reset controller, +complementing the existing GPIO reset functionality (reset-gpios). + +Although the reset is optional and the driver performs a soft reset +during setup, if the initial reset pin state was asserted, the driver +will not detect the device until the reset is deasserted. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Linus Walleij +Reviewed-by: Alvin Šipraga +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/realtek.h | 2 ++ + drivers/net/dsa/realtek/rtl83xx.c | 42 +++++++++++++++++++++++++++---- + drivers/net/dsa/realtek/rtl83xx.h | 2 ++ + 3 files changed, 41 insertions(+), 5 deletions(-) + +--- a/drivers/net/dsa/realtek/realtek.h ++++ b/drivers/net/dsa/realtek/realtek.h +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + + #define REALTEK_HW_STOP_DELAY 25 /* msecs */ + #define REALTEK_HW_START_DELAY 100 /* msecs */ +@@ -48,6 +49,7 @@ struct rtl8366_vlan_4k { + + struct realtek_priv { + struct device *dev; ++ struct reset_control *reset_ctl; + struct gpio_desc *reset; + struct gpio_desc *mdc; + struct gpio_desc *mdio; +--- a/drivers/net/dsa/realtek/rtl83xx.c ++++ b/drivers/net/dsa/realtek/rtl83xx.c +@@ -185,6 +185,13 @@ rtl83xx_probe(struct device *dev, + "realtek,disable-leds"); + + /* TODO: if power is software controlled, set up any regulators here */ ++ priv->reset_ctl = devm_reset_control_get_optional(dev, NULL); ++ if (IS_ERR(priv->reset_ctl)) { ++ ret = PTR_ERR(priv->reset_ctl); ++ dev_err_probe(dev, ret, "failed to get reset control\n"); ++ return ERR_CAST(priv->reset_ctl); ++ } ++ + priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(priv->reset)) { + dev_err(dev, "failed to get RESET GPIO\n"); +@@ -193,11 +200,11 @@ rtl83xx_probe(struct device *dev, + + dev_set_drvdata(dev, priv); + +- if (priv->reset) { +- gpiod_set_value(priv->reset, 1); ++ if (priv->reset_ctl || priv->reset) { ++ rtl83xx_reset_assert(priv); + dev_dbg(dev, "asserted RESET\n"); + msleep(REALTEK_HW_STOP_DELAY); +- gpiod_set_value(priv->reset, 0); ++ rtl83xx_reset_deassert(priv); + msleep(REALTEK_HW_START_DELAY); + dev_dbg(dev, "deasserted RESET\n"); + } +@@ -293,11 +300,36 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_shutdown, R + void rtl83xx_remove(struct realtek_priv *priv) + { + /* leave the device reset asserted */ +- if (priv->reset) +- gpiod_set_value(priv->reset, 1); ++ rtl83xx_reset_assert(priv); + } + EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA); + ++void rtl83xx_reset_assert(struct realtek_priv *priv) ++{ ++ int ret; ++ ++ ret = reset_control_assert(priv->reset_ctl); ++ if (ret) ++ dev_warn(priv->dev, ++ "Failed to assert the switch reset control: %pe\n", ++ ERR_PTR(ret)); ++ ++ gpiod_set_value(priv->reset, true); ++} ++ ++void rtl83xx_reset_deassert(struct realtek_priv *priv) ++{ ++ int ret; ++ ++ ret = reset_control_deassert(priv->reset_ctl); ++ if (ret) ++ dev_warn(priv->dev, ++ "Failed to deassert the switch reset control: %pe\n", ++ ERR_PTR(ret)); ++ ++ gpiod_set_value(priv->reset, false); ++} ++ + MODULE_AUTHOR("Luiz Angelo Daros de Luca "); + MODULE_AUTHOR("Linus Walleij "); + MODULE_DESCRIPTION("Realtek DSA switches common module"); +--- a/drivers/net/dsa/realtek/rtl83xx.h ++++ b/drivers/net/dsa/realtek/rtl83xx.h +@@ -18,5 +18,7 @@ int rtl83xx_register_switch(struct realt + void rtl83xx_unregister_switch(struct realtek_priv *priv); + void rtl83xx_shutdown(struct realtek_priv *priv); + void rtl83xx_remove(struct realtek_priv *priv); ++void rtl83xx_reset_assert(struct realtek_priv *priv); ++void rtl83xx_reset_deassert(struct realtek_priv *priv); + + #endif /* _RTL83XX_H */ diff --git a/target/linux/generic/backport-6.6/899-v6.10-0002-net-dsa-realtek-do-not-assert-reset-on-remove.patch b/target/linux/generic/backport-6.6/899-v6.10-0002-net-dsa-realtek-do-not-assert-reset-on-remove.patch new file mode 100644 index 0000000000..7299a25e37 --- /dev/null +++ b/target/linux/generic/backport-6.6/899-v6.10-0002-net-dsa-realtek-do-not-assert-reset-on-remove.patch @@ -0,0 +1,45 @@ +From 4f580e9aced1816398c1c64f178302a22b8ea6e2 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Sat, 27 Apr 2024 02:11:29 -0300 +Subject: [PATCH 2/3] net: dsa: realtek: do not assert reset on remove + +The necessity of asserting the reset on removal was previously +questioned, as DSA's own cleanup methods should suffice to prevent +traffic leakage[1]. + +When a driver has subdrivers controlled by devres, they will be +unregistered after the main driver's .remove is executed. If it asserts +a reset, the subdrivers will be unable to communicate with the hardware +during their cleanup. For LEDs, this means that they will fail to turn +off, resulting in a timeout error. + +[1] https://lore.kernel.org/r/20240123215606.26716-9-luizluca@gmail.com/ + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/rtl83xx.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +--- a/drivers/net/dsa/realtek/rtl83xx.c ++++ b/drivers/net/dsa/realtek/rtl83xx.c +@@ -291,16 +291,13 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_shutdown, R + * rtl83xx_remove() - Cleanup a realtek switch driver + * @priv: realtek_priv pointer + * +- * If a method is provided, this function asserts the hard reset of the switch +- * in order to avoid leaking traffic when the driver is gone. ++ * Placehold for common cleanup procedures. + * +- * Context: Might sleep if priv->gdev->chip->can_sleep. ++ * Context: Any + * Return: nothing + */ + void rtl83xx_remove(struct realtek_priv *priv) + { +- /* leave the device reset asserted */ +- rtl83xx_reset_assert(priv); + } + EXPORT_SYMBOL_NS_GPL(rtl83xx_remove, REALTEK_DSA); + diff --git a/target/linux/generic/backport-6.6/899-v6.10-0003-net-dsa-realtek-add-LED-drivers-for-rtl8366rb.patch b/target/linux/generic/backport-6.6/899-v6.10-0003-net-dsa-realtek-add-LED-drivers-for-rtl8366rb.patch new file mode 100644 index 0000000000..4c9ea7fe12 --- /dev/null +++ b/target/linux/generic/backport-6.6/899-v6.10-0003-net-dsa-realtek-add-LED-drivers-for-rtl8366rb.patch @@ -0,0 +1,396 @@ +From 32d617005475a71ebcc4ec8b2791e8d1481e9a10 Mon Sep 17 00:00:00 2001 +From: Luiz Angelo Daros de Luca +Date: Sat, 27 Apr 2024 02:11:30 -0300 +Subject: [PATCH 3/3] net: dsa: realtek: add LED drivers for rtl8366rb + +This commit introduces LED drivers for rtl8366rb, enabling LEDs to be +described in the device tree using the same format as qca8k. Each port +can configure up to 4 LEDs. + +If all LEDs in a group use the default state "keep", they will use the +default behavior after a reset. Changing the brightness of one LED, +either manually or by a trigger, will disable the default hardware +trigger and switch the entire LED group to manually controlled LEDs. +Once in this mode, there is no way to revert to hardware-controlled LEDs +(except by resetting the switch). + +Software triggers function as expected with manually controlled LEDs. + +Signed-off-by: Luiz Angelo Daros de Luca +Reviewed-by: Linus Walleij +Signed-off-by: David S. Miller +--- + drivers/net/dsa/realtek/rtl8366rb.c | 304 ++++++++++++++++++++++++---- + 1 file changed, 265 insertions(+), 39 deletions(-) + +--- a/drivers/net/dsa/realtek/rtl8366rb.c ++++ b/drivers/net/dsa/realtek/rtl8366rb.c +@@ -180,6 +180,7 @@ + #define RTL8366RB_VLAN_INGRESS_CTRL2_REG 0x037f + + /* LED control registers */ ++/* The LED blink rate is global; it is used by all triggers in all groups. */ + #define RTL8366RB_LED_BLINKRATE_REG 0x0430 + #define RTL8366RB_LED_BLINKRATE_MASK 0x0007 + #define RTL8366RB_LED_BLINKRATE_28MS 0x0000 +@@ -195,31 +196,21 @@ + (4 * (led_group)) + #define RTL8366RB_LED_CTRL_MASK(led_group) \ + (0xf << RTL8366RB_LED_CTRL_OFFSET(led_group)) +-#define RTL8366RB_LED_OFF 0x0 +-#define RTL8366RB_LED_DUP_COL 0x1 +-#define RTL8366RB_LED_LINK_ACT 0x2 +-#define RTL8366RB_LED_SPD1000 0x3 +-#define RTL8366RB_LED_SPD100 0x4 +-#define RTL8366RB_LED_SPD10 0x5 +-#define RTL8366RB_LED_SPD1000_ACT 0x6 +-#define RTL8366RB_LED_SPD100_ACT 0x7 +-#define RTL8366RB_LED_SPD10_ACT 0x8 +-#define RTL8366RB_LED_SPD100_10_ACT 0x9 +-#define RTL8366RB_LED_FIBER 0xa +-#define RTL8366RB_LED_AN_FAULT 0xb +-#define RTL8366RB_LED_LINK_RX 0xc +-#define RTL8366RB_LED_LINK_TX 0xd +-#define RTL8366RB_LED_MASTER 0xe +-#define RTL8366RB_LED_FORCE 0xf + + /* The RTL8366RB_LED_X_X registers are used to manually set the LED state only + * when the corresponding LED group in RTL8366RB_LED_CTRL_REG is +- * RTL8366RB_LED_FORCE. Otherwise, it is ignored. ++ * RTL8366RB_LEDGROUP_FORCE. Otherwise, it is ignored. + */ + #define RTL8366RB_LED_0_1_CTRL_REG 0x0432 +-#define RTL8366RB_LED_1_OFFSET 6 + #define RTL8366RB_LED_2_3_CTRL_REG 0x0433 +-#define RTL8366RB_LED_3_OFFSET 6 ++#define RTL8366RB_LED_X_X_CTRL_REG(led_group) \ ++ ((led_group) <= 1 ? \ ++ RTL8366RB_LED_0_1_CTRL_REG : \ ++ RTL8366RB_LED_2_3_CTRL_REG) ++#define RTL8366RB_LED_0_X_CTRL_MASK GENMASK(5, 0) ++#define RTL8366RB_LED_X_1_CTRL_MASK GENMASK(11, 6) ++#define RTL8366RB_LED_2_X_CTRL_MASK GENMASK(5, 0) ++#define RTL8366RB_LED_X_3_CTRL_MASK GENMASK(11, 6) + + #define RTL8366RB_MIB_COUNT 33 + #define RTL8366RB_GLOBAL_MIB_COUNT 1 +@@ -363,14 +354,44 @@ + #define RTL8366RB_GREEN_FEATURE_TX BIT(0) + #define RTL8366RB_GREEN_FEATURE_RX BIT(2) + ++enum rtl8366_ledgroup_mode { ++ RTL8366RB_LEDGROUP_OFF = 0x0, ++ RTL8366RB_LEDGROUP_DUP_COL = 0x1, ++ RTL8366RB_LEDGROUP_LINK_ACT = 0x2, ++ RTL8366RB_LEDGROUP_SPD1000 = 0x3, ++ RTL8366RB_LEDGROUP_SPD100 = 0x4, ++ RTL8366RB_LEDGROUP_SPD10 = 0x5, ++ RTL8366RB_LEDGROUP_SPD1000_ACT = 0x6, ++ RTL8366RB_LEDGROUP_SPD100_ACT = 0x7, ++ RTL8366RB_LEDGROUP_SPD10_ACT = 0x8, ++ RTL8366RB_LEDGROUP_SPD100_10_ACT = 0x9, ++ RTL8366RB_LEDGROUP_FIBER = 0xa, ++ RTL8366RB_LEDGROUP_AN_FAULT = 0xb, ++ RTL8366RB_LEDGROUP_LINK_RX = 0xc, ++ RTL8366RB_LEDGROUP_LINK_TX = 0xd, ++ RTL8366RB_LEDGROUP_MASTER = 0xe, ++ RTL8366RB_LEDGROUP_FORCE = 0xf, ++ ++ __RTL8366RB_LEDGROUP_MODE_MAX ++}; ++ ++struct rtl8366rb_led { ++ u8 port_num; ++ u8 led_group; ++ struct realtek_priv *priv; ++ struct led_classdev cdev; ++}; ++ + /** + * struct rtl8366rb - RTL8366RB-specific data + * @max_mtu: per-port max MTU setting + * @pvid_enabled: if PVID is set for respective port ++ * @leds: per-port and per-ledgroup led info + */ + struct rtl8366rb { + unsigned int max_mtu[RTL8366RB_NUM_PORTS]; + bool pvid_enabled[RTL8366RB_NUM_PORTS]; ++ struct rtl8366rb_led leds[RTL8366RB_NUM_PORTS][RTL8366RB_NUM_LEDGROUPS]; + }; + + static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = { +@@ -813,6 +834,217 @@ static int rtl8366rb_jam_table(const str + return 0; + } + ++static int rb8366rb_set_ledgroup_mode(struct realtek_priv *priv, ++ u8 led_group, ++ enum rtl8366_ledgroup_mode mode) ++{ ++ int ret; ++ u32 val; ++ ++ val = mode << RTL8366RB_LED_CTRL_OFFSET(led_group); ++ ++ ret = regmap_update_bits(priv->map, ++ RTL8366RB_LED_CTRL_REG, ++ RTL8366RB_LED_CTRL_MASK(led_group), ++ val); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static inline u32 rtl8366rb_led_group_port_mask(u8 led_group, u8 port) ++{ ++ switch (led_group) { ++ case 0: ++ return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); ++ case 1: ++ return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); ++ case 2: ++ return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); ++ case 3: ++ return FIELD_PREP(RTL8366RB_LED_0_X_CTRL_MASK, BIT(port)); ++ default: ++ return 0; ++ } ++} ++ ++static int rb8366rb_get_port_led(struct rtl8366rb_led *led) ++{ ++ struct realtek_priv *priv = led->priv; ++ u8 led_group = led->led_group; ++ u8 port_num = led->port_num; ++ int ret; ++ u32 val; ++ ++ ret = regmap_read(priv->map, RTL8366RB_LED_X_X_CTRL_REG(led_group), ++ &val); ++ if (ret) { ++ dev_err(priv->dev, "error reading LED on port %d group %d\n", ++ led_group, port_num); ++ return ret; ++ } ++ ++ return !!(val & rtl8366rb_led_group_port_mask(led_group, port_num)); ++} ++ ++static int rb8366rb_set_port_led(struct rtl8366rb_led *led, bool enable) ++{ ++ struct realtek_priv *priv = led->priv; ++ u8 led_group = led->led_group; ++ u8 port_num = led->port_num; ++ int ret; ++ ++ ret = regmap_update_bits(priv->map, ++ RTL8366RB_LED_X_X_CTRL_REG(led_group), ++ rtl8366rb_led_group_port_mask(led_group, ++ port_num), ++ enable ? 0xffff : 0); ++ if (ret) { ++ dev_err(priv->dev, "error updating LED on port %d group %d\n", ++ led_group, port_num); ++ return ret; ++ } ++ ++ /* Change the LED group to manual controlled LEDs if required */ ++ ret = rb8366rb_set_ledgroup_mode(priv, led_group, ++ RTL8366RB_LEDGROUP_FORCE); ++ ++ if (ret) { ++ dev_err(priv->dev, "error updating LED GROUP group %d\n", ++ led_group); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ++rtl8366rb_cled_brightness_set_blocking(struct led_classdev *ldev, ++ enum led_brightness brightness) ++{ ++ struct rtl8366rb_led *led = container_of(ldev, struct rtl8366rb_led, ++ cdev); ++ ++ return rb8366rb_set_port_led(led, brightness == LED_ON); ++} ++ ++static int rtl8366rb_setup_led(struct realtek_priv *priv, struct dsa_port *dp, ++ struct fwnode_handle *led_fwnode) ++{ ++ struct rtl8366rb *rb = priv->chip_data; ++ struct led_init_data init_data = { }; ++ enum led_default_state state; ++ struct rtl8366rb_led *led; ++ u32 led_group; ++ int ret; ++ ++ ret = fwnode_property_read_u32(led_fwnode, "reg", &led_group); ++ if (ret) ++ return ret; ++ ++ if (led_group >= RTL8366RB_NUM_LEDGROUPS) { ++ dev_warn(priv->dev, "Invalid LED reg %d defined for port %d", ++ led_group, dp->index); ++ return -EINVAL; ++ } ++ ++ led = &rb->leds[dp->index][led_group]; ++ led->port_num = dp->index; ++ led->led_group = led_group; ++ led->priv = priv; ++ ++ state = led_init_default_state_get(led_fwnode); ++ switch (state) { ++ case LEDS_DEFSTATE_ON: ++ led->cdev.brightness = 1; ++ rb8366rb_set_port_led(led, 1); ++ break; ++ case LEDS_DEFSTATE_KEEP: ++ led->cdev.brightness = ++ rb8366rb_get_port_led(led); ++ break; ++ case LEDS_DEFSTATE_OFF: ++ default: ++ led->cdev.brightness = 0; ++ rb8366rb_set_port_led(led, 0); ++ } ++ ++ led->cdev.max_brightness = 1; ++ led->cdev.brightness_set_blocking = ++ rtl8366rb_cled_brightness_set_blocking; ++ init_data.fwnode = led_fwnode; ++ init_data.devname_mandatory = true; ++ ++ init_data.devicename = kasprintf(GFP_KERNEL, "Realtek-%d:0%d:%d", ++ dp->ds->index, dp->index, led_group); ++ if (!init_data.devicename) ++ return -ENOMEM; ++ ++ ret = devm_led_classdev_register_ext(priv->dev, &led->cdev, &init_data); ++ if (ret) { ++ dev_warn(priv->dev, "Failed to init LED %d for port %d", ++ led_group, dp->index); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int rtl8366rb_setup_all_leds_off(struct realtek_priv *priv) ++{ ++ int ret = 0; ++ int i; ++ ++ regmap_update_bits(priv->map, ++ RTL8366RB_INTERRUPT_CONTROL_REG, ++ RTL8366RB_P4_RGMII_LED, ++ 0); ++ ++ for (i = 0; i < RTL8366RB_NUM_LEDGROUPS; i++) { ++ ret = rb8366rb_set_ledgroup_mode(priv, i, ++ RTL8366RB_LEDGROUP_OFF); ++ if (ret) ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static int rtl8366rb_setup_leds(struct realtek_priv *priv) ++{ ++ struct device_node *leds_np, *led_np; ++ struct dsa_switch *ds = &priv->ds; ++ struct dsa_port *dp; ++ int ret = 0; ++ ++ dsa_switch_for_each_port(dp, ds) { ++ if (!dp->dn) ++ continue; ++ ++ leds_np = of_get_child_by_name(dp->dn, "leds"); ++ if (!leds_np) { ++ dev_dbg(priv->dev, "No leds defined for port %d", ++ dp->index); ++ continue; ++ } ++ ++ for_each_child_of_node(leds_np, led_np) { ++ ret = rtl8366rb_setup_led(priv, dp, ++ of_fwnode_handle(led_np)); ++ if (ret) { ++ of_node_put(led_np); ++ break; ++ } ++ } ++ ++ of_node_put(leds_np); ++ if (ret) ++ return ret; ++ } ++ return 0; ++} ++ + static int rtl8366rb_setup(struct dsa_switch *ds) + { + struct realtek_priv *priv = ds->priv; +@@ -821,7 +1053,6 @@ static int rtl8366rb_setup(struct dsa_sw + u32 chip_ver = 0; + u32 chip_id = 0; + int jam_size; +- u32 val; + int ret; + int i; + +@@ -997,7 +1228,9 @@ static int rtl8366rb_setup(struct dsa_sw + if (ret) + return ret; + +- /* Set blinking, TODO: make this configurable */ ++ /* Set blinking, used by all LED groups using HW triggers. ++ * TODO: make this configurable ++ */ + ret = regmap_update_bits(priv->map, RTL8366RB_LED_BLINKRATE_REG, + RTL8366RB_LED_BLINKRATE_MASK, + RTL8366RB_LED_BLINKRATE_56MS); +@@ -1005,26 +1238,19 @@ static int rtl8366rb_setup(struct dsa_sw + return ret; + + /* Set up LED activity: +- * Each port has 4 LEDs, we configure all ports to the same +- * behaviour (no individual config) but we can set up each +- * LED separately. ++ * Each port has 4 LEDs on fixed groups. Each group shares the same ++ * hardware trigger across all ports. LEDs can only be indiviually ++ * controlled setting the LED group to fixed mode and using the driver ++ * to toggle them LEDs on/off. + */ + if (priv->leds_disabled) { +- /* Turn everything off */ +- regmap_update_bits(priv->map, +- RTL8366RB_INTERRUPT_CONTROL_REG, +- RTL8366RB_P4_RGMII_LED, +- 0); +- +- for (i = 0; i < RTL8366RB_NUM_LEDGROUPS; i++) { +- val = RTL8366RB_LED_OFF << RTL8366RB_LED_CTRL_OFFSET(i); +- ret = regmap_update_bits(priv->map, +- RTL8366RB_LED_CTRL_REG, +- RTL8366RB_LED_CTRL_MASK(i), +- val); +- if (ret) +- return ret; +- } ++ ret = rtl8366rb_setup_all_leds_off(priv); ++ if (ret) ++ return ret; ++ } else { ++ ret = rtl8366rb_setup_leds(priv); ++ if (ret) ++ return ret; + } + + ret = rtl8366_reset_vlan(priv); diff --git a/target/linux/mxs/image/Makefile b/target/linux/mxs/image/Makefile index b95409741a..12e7136144 100644 --- a/target/linux/mxs/image/Makefile +++ b/target/linux/mxs/image/Makefile @@ -5,16 +5,38 @@ include $(TOPDIR)/rules.mk include $(INCLUDE_DIR)/image.mk -FAT32_BLOCK_SIZE=1024 -FAT32_BLOCKS=$(shell echo $$(($(CONFIG_MXS_SD_BOOT_PARTSIZE)*1024*1024/$(FAT32_BLOCK_SIZE)))) +FAT32_BLOCK_SIZE = 1024 +FAT32_BLOCKS = $(shell echo $$(($(CONFIG_MXS_SD_BOOT_PARTSIZE)*1024*1024/$(FAT32_BLOCK_SIZE)))) -KERNEL_LOADADDR:=0x40008000 +KERNEL_LOADADDR := 0x40008000 + +define Build/mxs-ext4-rootfs-with-boot + rm -rf $(call mkfs_target_dir,$(1))/boot + mkdir -p $(call mkfs_target_dir,$(1))/boot + $(CP) --no-preserve=mode $(KDIR)/$(KERNEL_NAME) $(call mkfs_target_dir,$(1))/boot/ + $(foreach dts,$(DEVICE_DTS), \ + $(CP) \ + $(DTS_DIR)/$(dts).dtb \ + $(call mkfs_target_dir,$(1))/boot/; \ + ) + + rm -rf $@.rootfs + $(STAGING_DIR_HOST)/bin/make_ext4fs -L rootfs \ + -l $(ROOTFS_PARTSIZE) -b $(CONFIG_TARGET_EXT4_BLOCKSIZE) \ + $(if $(CONFIG_TARGET_EXT4_RESERVED_PCT),-m $(CONFIG_TARGET_EXT4_RESERVED_PCT)) \ + $(if $(CONFIG_TARGET_EXT4_JOURNAL),,-J) \ + $(if $(SOURCE_DATE_EPOCH),-T $(SOURCE_DATE_EPOCH)) \ + $@.rootfs $(call mkfs_target_dir,$(1))/ + mv $@.rootfs $@ +endef define Build/mxs-sdcard-ext4-ext4 + mv $@ $@.rootfs + ./gen_sdcard_ext4_ext4.sh \ $@ \ $(STAGING_DIR_IMAGE)/$(DEVICE_NAME)/u-boot.sb \ - $(IMAGE_ROOTFS) \ + $@.rootfs \ $(CONFIG_TARGET_ROOTFS_PARTSIZE) endef @@ -46,12 +68,13 @@ endef define Device/i2se_duckbill DEVICE_VENDOR := I2SE DEVICE_MODEL := Duckbill - DEVICE_PACKAGES := -dnsmasq -ppp -ip6tables -iptables -mtd \ - uboot-envtools kmod-leds-gpio -kmod-nf-nathelper - SUPPORTED_DEVICES:=i2se,duckbill - SOC:=imx28 - DEVICE_DTS:=imx28-duckbill - IMAGE/sdcard.img.gz = mxs-sdcard-ext4-ext4 | append-metadata | gzip + DEVICE_PACKAGES := -dnsmasq -firewall4 -mtd -nftables -odhcpd-ipv6only -ppp -kmod-nft-offload \ + uboot-envtools kmod-leds-gpio + SUPPORTED_DEVICES := i2se,duckbill + SOC := imx28 + KERNEL := kernel-bin + DEVICE_DTS := imx28-duckbill imx28-duckbill-2 imx28-duckbill-2-485 imx28-duckbill-2-spi + IMAGE/sdcard.img.gz = mxs-ext4-rootfs-with-boot | mxs-sdcard-ext4-ext4 | append-metadata | gzip endef TARGET_DEVICES += i2se_duckbill @@ -59,10 +82,10 @@ define Device/olinuxino_maxi DEVICE_VENDOR := Olimex DEVICE_MODEL := OLinuXino Maxi DEVICE_PACKAGES := kmod-usb-net-smsc95xx kmod-pinctrl-mcp23s08-i2c \ - kmod-pinctrl-mcp23s08-spi kmod-leds-gpio kmod-sound-core - SUPPORTED_DEVICES:=olimex,imx23-olinuxino - SOC:=imx23 - DEVICE_DTS:=imx23-olinuxino + kmod-pinctrl-mcp23s08-spi kmod-leds-gpio kmod-sound-core + SUPPORTED_DEVICES := olimex,imx23-olinuxino + SOC := imx23 + DEVICE_DTS := imx23-olinuxino IMAGE/sdcard.img.gz = mxs-sdcard-vfat-ext4 | append-metadata | gzip endef TARGET_DEVICES += olinuxino_maxi @@ -71,10 +94,10 @@ define Device/olinuxino_micro DEVICE_VENDOR := Olimex DEVICE_MODEL := OLinuXino Micro DEVICE_PACKAGES := kmod-pinctrl-mcp23s08-spi kmod-pinctrl-mcp23s08-i2c \ - kmod-leds-gpio - SUPPORTED_DEVICES:=olimex,imx23-olinuxino - SOC:=imx23 - DEVICE_DTS:=imx23-olinuxino + kmod-leds-gpio + SUPPORTED_DEVICES := olimex,imx23-olinuxino + SOC := imx23 + DEVICE_DTS := imx23-olinuxino IMAGE/sdcard.img.gz = mxs-sdcard-vfat-ext4 | append-metadata | gzip endef TARGET_DEVICES += olinuxino_micro diff --git a/target/linux/qualcommax/Makefile b/target/linux/qualcommax/Makefile index 3a211d1dea..7f923bab41 100644 --- a/target/linux/qualcommax/Makefile +++ b/target/linux/qualcommax/Makefile @@ -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 @@ -14,8 +14,7 @@ include $(INCLUDE_DIR)/target.mk DEFAULT_PACKAGES += \ kmod-usb3 kmod-usb-dwc3 kmod-usb-dwc3-qcom \ kmod-leds-gpio kmod-gpio-button-hotplug \ - kmod-qca-nss-dp \ - kmod-ath11k-ahb wpad-openssl \ + kmod-qca-nss-dp kmod-ath11k-ahb wpad-openssl \ uboot-envtools losetup \ automount cpufreq 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 index 0000000000..a42b7bbeaa --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-ess.dtsi @@ -0,0 +1,116 @@ +#include + +&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 = ; + 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 = ; + 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/files/arch/arm64/boot/dts/qcom/ipq5018-mx-base.dtsi b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx-base.dtsi new file mode 100644 index 0000000000..57fe5c53a9 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx-base.dtsi @@ -0,0 +1,325 @@ +#include "ipq5018.dtsi" +#include "ipq5018-ess.dtsi" + +#include +#include +#include + +/ { + + aliases { + ethernet0 = &dp1; + ethernet1 = &dp2; + led-boot = &led_system_blue; + led-failsafe = &led_system_red; + led-running = &led_system_blue; + led-upgrade = &led_system_red; + serial0 = &blsp1_uart1; + }; + + chosen { + bootargs-append = " root=/dev/ubiblock0_0 coherent_pool=2M"; + stdout-path = "serial0:115200n8"; + }; + + keys { + compatible = "gpio-keys"; + pinctrl-0 = <&button_pins>; + pinctrl-names = "default"; + + wps-button { + label = "wps"; + gpios = <&tlmm 27 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + reset-button { + label = "reset"; + gpios = <&tlmm 28 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "pwm-leds"; + + led_system_red: red { + color = ; + function = LED_FUNCTION_POWER; + pwms = <&pwm 3 1250000>; + max-brightness = <255>; + }; + + green { + color = ; + function = LED_FUNCTION_POWER; + pwms = <&pwm 0 1250000>; + max-brightness = <255>; + }; + + led_system_blue: blue { + color = ; + function = LED_FUNCTION_POWER; + pwms = <&pwm 1 1250000>; + max-brightness = <255>; + }; + }; + + reserved-memory { + q6_mem_regions: q6_mem_regions@4b000000 { + no-map; + reg = <0x0 0x4b000000 0x0 0x3000000>; + }; + }; +}; + +&sleep_clk { + clock-frequency = <32000>; +}; + +&xo_board_clk { + clock-frequency = <24000000>; +}; + +&blsp1_uart1 { + status = "okay"; + + pinctrl-0 = <&serial_0_pins>; + pinctrl-names = "default"; +}; + +&crypto { + status = "okay"; +}; + +&cryptobam { + status = "okay"; +}; + +&prng { + status = "okay"; +}; + +&pwm { + status = "okay"; + + #pwm-cells = <2>; + pinctrl-0 = <&pwm_pins>; + pinctrl-names = "default"; +}; + +&qfprom { + status = "okay"; +}; + +&qpic_bam { + status = "okay"; +}; + +&qpic_nand { + pinctrl-0 = <&qpic_pins>; + pinctrl-names = "default"; + status = "okay"; + + partitions { + status = "disabled"; + }; + + nand@0 { + compatible = "spi-nand"; + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + + nand-ecc-engine = <&qpic_nand>; + + nand-ecc-strength = <8>; + nand-ecc-step-size = <512>; + nand-bus-width = <8>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "0:SBL1"; + reg = <0x00000000 0x80000>; + read-only; + }; + + partition@80000 { + label = "0:MIBIB"; + reg = <0x00080000 0x80000>; + read-only; + }; + + partition@100000 { + label = "0:QSEE"; + reg = <0x00100000 0x100000>; + read-only; + }; + + partition@200000 { + label = "0:DEVCFG"; + reg = <0x00200000 0x40000>; + read-only; + }; + + partition@240000 { + label = "0:CDT"; + reg = <0x00240000 0x40000>; + read-only; + }; + + partition@280000 { + label = "0:APPSBLENV"; + reg = <0x00280000 0x80000>; + }; + + partition@300000 { + label = "0:APPSBL"; + reg = <0x00300000 0x140000>; + read-only; + }; + + partition@440000 { + label = "0:ART"; + reg = <0x00440000 0x100000>; + read-only; + }; + + partition@540000 { + label = "0:TRAINING"; + reg = <0x00540000 0x80000>; + read-only; + }; + + partition@5c0000 { + label = "u_env"; + reg = <0x005c0000 0x80000>; + }; + + partition@640000 { + label = "s_env"; + reg = <0x00640000 0x40000>; + }; + + partition@680000 { + label = "devinfo"; + reg = <0x00680000 0x40000>; + read-only; + }; + + partition@6c0000 { + label = "kernel"; + reg = <0x006c0000 0x5200000>; + }; + + partition@ec0000 { + label = "rootfs"; + reg = <0x0ec0000 0x4a00000>; + }; + + partition@58c0000 { + label = "alt_kernel"; + reg = <0x058c0000 0x5200000>; + }; + + partition@60c0000 { + label = "alt_rootfs"; + reg = <0x060c0000 0x4a00000>; + }; + + partition@aac0000 { + label = "sysdiag"; + reg = <0x0aac0000 0x200000>; + read-only; + }; + + partition@acc0000 { + label = "syscfg"; + reg = <0x0acc0000 0x4400000>; + read-only; + }; + }; + }; +}; + +&tlmm { + button_pins: button-state { + pins = "gpio27", "gpio28"; + function = "gpio"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio1_pins: mdio-state { + mdc-pins { + pins = "gpio36"; + function = "mdc"; + drive-strength = <8>; + bias-pull-up; + }; + + mdio-pins { + pins = "gpio37"; + function = "mdio"; + drive-strength = <8>; + bias-pull-up; + }; + }; + + pwm_pins: pwm-state { + mux_1 { + pins = "gpio1"; + function = "pwm1"; + drive-strength = <8>; + }; + + mux_2 { + pins = "gpio30"; + function = "pwm3"; + drive-strength = <8>; + }; + + mux_3 { + pins = "gpio46"; + function = "pwm0"; + drive-strength = <8>; + }; + }; + + qpic_pins: qpic-state { + clock-pins { + pins = "gpio9"; + function = "qspi_clk"; + drive-strength = <8>; + bias-disable; + }; + + cs-pins { + pins = "gpio8"; + function = "qspi_cs"; + drive-strength = <8>; + bias-disable; + }; + + data-pins { + pins = "gpio4", "gpio5", "gpio6", "gpio7"; + function = "qspi_data"; + drive-strength = <8>; + bias-disable; + }; + }; + + serial_0_pins: uart0-state { + pins = "gpio20", "gpio21"; + function = "blsp0_uart0"; + bias-disable; + }; +}; + +&tsens { + status = "okay"; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx2000.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx2000.dts new file mode 100644 index 0000000000..108b26f8d1 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx2000.dts @@ -0,0 +1,262 @@ +/dts-v1/; + +#include "ipq5018.dtsi" +#include "ipq5018-mx-base.dtsi" + +/ { + model = "Linksys MX2000"; + compatible = "linksys,mx2000", "qcom,ipq5018"; +}; + +/* + * =============================================================== + * _______________________ _______________________ + * | IPQ5018 | | QCA8337 | + * | +------+ +--------+ | | +--------+ +------+ | + * | | MAC0 |---| GE Phy | | | | Phy0 |---| MAC1 | | + * | +------+ +--------+ | | +--------+ +------+ | + * | +------+ +--------+ | | +--------+ +------+ | + * | | MAC1 |---| Uniphy |-+-SGMII-+-| SerDes |---| MAC6 | | + * | +------+ +--------+ | | +--------+ +------+ | + * |_______________________| |_______________________| + * + * =============================================================== + */ + +&switch { + status = "okay"; + + switch_mac_mode = ; + + qcom,port_phyinfo { + // MAC0 -> GE Phy -> QCA8337 Phy4 + port@0 { + port_id = <1>; + mdiobus = <&mdio0>; + phy_address = <7>; + }; + + // MAC1 ---SGMII---> QCA8337 SerDes + port@1 { + port_id = <2>; + forced-speed = <1000>; + forced-duplex = <1>; + }; + }; +}; + +// MAC1 ---SGMII---> QCA8337 SerDes +&dp2 { + status = "okay"; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; +}; + +&mdio0 { + status = "okay"; +}; + +/* IPQ5018 GE Phy -> Not connected + * needs to be enabled for QSDK to identify the IPQ5018 dummy switch + */ +&ge_phy { + status = "okay"; +}; + +&mdio1 { + status = "okay"; + + pinctrl-0 = <&mdio1_pins>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 39 GPIO_ACTIVE_LOW>; + + // QCA8337 Phy0 not connected + qca8337_0: ethernet-phy@0 { + reg = <0>; + }; + + // QCA8337 Phy1 -> WAN + qca8337_1: ethernet-phy@1 { + reg = <1>; + }; + + // QCA8337 Phy2 -> LAN1 + qca8337_2: ethernet-phy@2 { + reg = <2>; + }; + + // QCA8337 Phy3 -> LAN2 + qca8337_3: ethernet-phy@3 { + reg = <3>; + }; + + // QCA8337 Phy4 -> LAN3 + qca8337_4: ethernet-phy@4 { + reg = <4>; + }; + + // QCA8337 switch + switch1: ethernet-switch@17 { + compatible = "qca,qca8337"; + reg = <17>; + #address-cells = <1>; + #size-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@2 { + reg = <2>; + label = "wan"; + phy-handle = <&qca8337_1>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-handle = <&qca8337_2>; + }; + + port@4 { + reg = <4>; + label = "lan2"; + phy-handle = <&qca8337_3>; + }; + + port@5 { + reg = <5>; + label = "lan1"; + phy-handle = <&qca8337_4>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + phy-mode = "sgmii"; + ethernet = <&dp2>; + qca,sgmii-enable-pll; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; +}; + +&q6v5_wcss { + status = "okay"; + + memory-region = <&q6_mem_regions>; + firmware-name = "ath11k/IPQ5018/hw1.0/q6_fw.mdt", + "ath11k/IPQ5018/hw1.0/m3_fw.mdt", + "ath11k/QCN6122/hw1.0/m3_fw.mdt"; + + /* The QCN6102 radio should map to UPD ID 2. Without */ + /* bootargs, the firmware will expect it to be on UPD ID 3 */ + boot-args = < + /* type: */ 0x1 /* PCIE0 */ + /* length: */ 4 + /* UPD ID: */ 2 + /* reset GPIO: */ 15 + /* reserved: */ 0 0>; + + // IPQ5018 + q6_wcss_pd1: pd-1 { + firmware-name = "ath11k/IPQ5018/hw1.0/q6_fw.mdt"; + + resets = + <&gcc GCC_WCSSAON_RESET>, + <&gcc GCC_WCSS_BCR>, + <&gcc GCC_CE_BCR>; + reset-names = + "wcss_aon_reset", + "wcss_reset", + "ce_reset"; + + clocks = + <&gcc GCC_WCSS_AHB_S_CLK>, + <&gcc GCC_WCSS_ACMT_CLK>, + <&gcc GCC_WCSS_AXI_M_CLK>; + clock-names = + "gcc_wcss_ahb_s_clk", + "gcc_wcss_acmt_clk", + "gcc_wcss_axi_m_clk"; + + // qcom,halt-regs = <&tcsr_q6_block 0xa000 0xd000 0x0>; + 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"; + status = "okay"; + }; + + // QCN6102 5G + q6_wcss_pd2: pd-2 { + firmware-name = "ath11k/IPQ5018/hw1.0/q6_fw.mdt"; + + interrupts-extended = + <&wcss_smp2p_in 16 IRQ_TYPE_NONE>, + <&wcss_smp2p_in 17 IRQ_TYPE_NONE>, + <&wcss_smp2p_in 20 IRQ_TYPE_NONE>, + <&wcss_smp2p_in 19 IRQ_TYPE_NONE>; + interrupt-names = + "fatal", + "ready", + "spawn-ack", + "stop-ack"; + + qcom,smem-states = + <&wcss_smp2p_out 16>, + <&wcss_smp2p_out 17>, + <&wcss_smp2p_out 18>; + qcom,smem-state-names = + "shutdown", + "stop", + "spawn"; + status = "okay"; + }; +}; + +&wifi0 { + // IPQ5018 + qcom,rproc = <&q6_wcss_pd1>; + qcom,ath11k-calibration-variant = "Linksys-MX2000"; + qcom,ath11k-fw-memory-mode = <2>; + qcom,bdf-addr = <0x4c400000>; + + status = "okay"; +}; + +&wifi1 { + // QCN6102 5G + qcom,rproc = <&q6_wcss_pd2>; + qcom,userpd-subsys-name = "q6v5_wcss_userpd2"; + qcom,ath11k-calibration-variant = "Linksys-MX2000"; + qcom,ath11k-fw-memory-mode = <2>; + qcom,bdf-addr = <0x4d100000>; + qcom,m3-dump-addr = <0x4df00000>; + + status = "okay"; +}; diff --git a/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx5500.dts b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx5500.dts new file mode 100644 index 0000000000..630e8ba310 --- /dev/null +++ b/target/linux/qualcommax/files/arch/arm64/boot/dts/qcom/ipq5018-mx5500.dts @@ -0,0 +1,240 @@ +/dts-v1/; + +#include "ipq5018.dtsi" +#include "ipq5018-mx-base.dtsi" + +/ { + model = "Linksys MX5500"; + compatible = "linksys,mx5500", "qcom,ipq5018"; +}; + +/* + * =============================================================== + * _______________________ _______________________ + * | IPQ5018 | | QCA8337 | + * | +------+ +--------+ | | +--------+ +------+ | + * | | MAC0 |---| GE Phy | | | | Phy0 |---| MAC1 | | + * | +------+ +--------+ | | +--------+ +------+ | + * | +------+ +--------+ | | +--------+ +------+ | + * | | MAC1 |---| Uniphy |-+-SGMII-+-| SerDes |---| MAC6 | | + * | +------+ +--------+ | | +--------+ +------+ | + * |_______________________| |_______________________| + * + * =============================================================== + */ + +&switch { + status = "okay"; + + switch_mac_mode = ; + + qcom,port_phyinfo { + // MAC0 -> GE Phy -> QCA8337 Phy4 + port@0 { + port_id = <1>; + mdiobus = <&mdio0>; + phy_address = <7>; + }; + + // MAC1 ---SGMII---> QCA8337 SerDes + port@1 { + port_id = <2>; + forced-speed = <1000>; + forced-duplex = <1>; + }; + }; +}; + +// MAC1 ---SGMII---> QCA8337 SerDes +&dp2 { + status = "okay"; + phy-mode = "sgmii"; + + fixed-link { + speed = <1000>; + full-duplex; + }; +}; + +&mdio0 { + status = "okay"; +}; + +/* IPQ5018 GE Phy -> Not connected + * needs to be enabled for QSDK to identify the IPQ5018 dummy switch + */ +&ge_phy { + status = "okay"; +}; + +&mdio1 { + status = "okay"; + + pinctrl-0 = <&mdio1_pins>; + pinctrl-names = "default"; + reset-gpios = <&tlmm 39 GPIO_ACTIVE_LOW>; + + // QCA8337 Phy0 not connected + qca8337_0: ethernet-phy@0 { + reg = <0>; + }; + + // QCA8337 Phy1 -> WAN + qca8337_1: ethernet-phy@1 { + reg = <1>; + }; + + // QCA8337 Phy2 -> LAN1 + qca8337_2: ethernet-phy@2 { + reg = <2>; + }; + + // QCA8337 Phy3 -> LAN2 + qca8337_3: ethernet-phy@3 { + reg = <3>; + }; + + // QCA8337 Phy4 -> LAN3 + qca8337_4: ethernet-phy@4 { + reg = <4>; + }; + + // QCA8337 switch + switch1: ethernet-switch@17 { + compatible = "qca,qca8337"; + reg = <17>; + #address-cells = <1>; + #size-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@2 { + reg = <2>; + label = "wan"; + phy-handle = <&qca8337_1>; + }; + + port@3 { + reg = <3>; + label = "lan3"; + phy-handle = <&qca8337_2>; + }; + + port@4 { + reg = <4>; + label = "lan2"; + phy-handle = <&qca8337_3>; + }; + + port@5 { + reg = <5>; + label = "lan1"; + phy-handle = <&qca8337_4>; + }; + + port@6 { + reg = <6>; + label = "cpu"; + phy-mode = "sgmii"; + ethernet = <&dp2>; + qca,sgmii-enable-pll; + + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; +}; + +&pcie0_phy { + status = "okay"; +}; + +&pcie0 { + status = "okay"; + + perst-gpios = <&tlmm 15 GPIO_ACTIVE_LOW>; + + bridge@0,0 { + reg = <0x00000000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + + wifi@1,0 { + status = "okay"; + + /* QCN9074: ath11k lacks DT compatible for PCI cards */ + compatible = "pci17cb,1104"; + reg = <0x00010000 0 0 0 0>; + + qcom,ath11k-calibration-variant = "Linksys-MX5500"; + }; + }; +}; + +&q6v5_wcss { + status = "okay"; + + memory-region = <&q6_mem_regions>; + firmware-name = "ath11k/IPQ5018/hw1.0/q6_fw.mdt", + "ath11k/IPQ5018/hw1.0/m3_fw.mdt"; + + // IPQ5018 + q6_wcss_pd1: pd-1 { + firmware-name = "ath11k/IPQ5018/hw1.0/q6_fw.mdt"; + + resets = + <&gcc GCC_WCSSAON_RESET>, + <&gcc GCC_WCSS_BCR>, + <&gcc GCC_CE_BCR>; + reset-names = + "wcss_aon_reset", + "wcss_reset", + "ce_reset"; + + clocks = + <&gcc GCC_WCSS_AHB_S_CLK>, + <&gcc GCC_WCSS_ACMT_CLK>, + <&gcc GCC_WCSS_AXI_M_CLK>; + clock-names = + "gcc_wcss_ahb_s_clk", + "gcc_wcss_acmt_clk", + "gcc_wcss_axi_m_clk"; + + interrupts-extended = + <&wcss_smp2p_in 8 0>, + <&wcss_smp2p_in 9 0>, + <&wcss_smp2p_in 12 0>, + <&wcss_smp2p_in 11 0>; + 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"; + status = "okay"; + }; +}; + +&wifi0 { + // IPQ5018 + qcom,rproc = <&q6_wcss_pd1>; + qcom,ath11k-calibration-variant = "Linksys-MX5500"; + qcom,ath11k-fw-memory-mode = <2>; + qcom,bdf-addr = <0x4c400000>; + + status = "okay"; +}; diff --git a/target/linux/qualcommax/image/ipq50xx.mk b/target/linux/qualcommax/image/ipq50xx.mk new file mode 100644 index 0000000000..affd5e6345 --- /dev/null +++ b/target/linux/qualcommax/image/ipq50xx.mk @@ -0,0 +1,31 @@ +define Device/linksys_ipq50xx_mx_base + $(call Device/FitImageLzma) + DEVICE_VENDOR := Linksys + BLOCKSIZE := 128k + PAGESIZE := 2048 + KERNEL_SIZE := 8192k + IMAGE_SIZE := 83968k + NAND_SIZE := 256m + SOC := ipq5018 + IMAGES += factory.bin + IMAGE/factory.bin := append-kernel | pad-to $$$$(KERNEL_SIZE) | append-ubi | linksys-image type=$$$$(DEVICE_MODEL) +endef + +define Device/linksys_mx2000 + $(call Device/linksys_ipq50xx_mx_base) + DEVICE_MODEL := MX2000 + DEVICE_DTS_CONFIG := config@mp03.5-c1 + DEVICE_PACKAGES := ath11k-firmware-qcn6122 \ + ipq-wifi-linksys_mx2000 +endef +TARGET_DEVICES += linksys_mx2000 + +define Device/linksys_mx5500 + $(call Device/linksys_ipq50xx_mx_base) + DEVICE_MODEL := MX5500 + DEVICE_DTS_CONFIG := config@mp03.1 + DEVICE_PACKAGES := kmod-ath11k-pci \ + ath11k-firmware-qcn9074 \ + ipq-wifi-linksys_mx5500 +endef +TARGET_DEVICES += linksys_mx5500 diff --git a/target/linux/qualcommax/ipq50xx/base-files/etc/board.d/02_network b/target/linux/qualcommax/ipq50xx/base-files/etc/board.d/02_network new file mode 100644 index 0000000000..57f11bad84 --- /dev/null +++ b/target/linux/qualcommax/ipq50xx/base-files/etc/board.d/02_network @@ -0,0 +1,45 @@ +#!/bin/sh + +. /lib/functions/uci-defaults.sh +. /lib/functions/system.sh + +ipq50xx_setup_interfaces() +{ + local board="$1" + case $board in + linksys,mx2000|\ + linksys,mx5500) + ucidef_set_interfaces_lan_wan "lan1 lan2 lan3" "wan" + ;; + esac +} + +ipq50xx_setup_macs() +{ + local board="$1" + local lan_mac="" + local wan_mac="" + local label_mac="" + + case "$board" in + linksys,mx2000|\ + linksys,mx5500) + label_mac=$(mtd_get_mac_ascii devinfo hw_mac_addr) + lan_mac=$label_mac + wan_mac=$label_mac + ucidef_set_network_device_mac eth0 $label_mac + ;; + esac + + [ -n "$lan_mac" ] && ucidef_set_interface_macaddr "lan" $lan_mac + [ -n "$wan_mac" ] && ucidef_set_interface_macaddr "wan" $wan_mac + [ -n "$label_mac" ] && ucidef_set_label_macaddr $label_mac +} + +board_config_update +board=$(board_name) +ipq50xx_setup_interfaces $board +ipq50xx_setup_macs $board +board_config_flush + +exit 0 diff --git a/target/linux/qualcommax/ipq50xx/base-files/etc/hotplug.d/firmware/11-ath11k-caldata b/target/linux/qualcommax/ipq50xx/base-files/etc/hotplug.d/firmware/11-ath11k-caldata new file mode 100644 index 0000000000..7c8cc99ee3 --- /dev/null +++ b/target/linux/qualcommax/ipq50xx/base-files/etc/hotplug.d/firmware/11-ath11k-caldata @@ -0,0 +1,47 @@ +#!/bin/sh + +[ -e /lib/firmware/$FIRMWARE ] && exit 0 + +. /lib/functions/caldata.sh + +board=$(board_name) + +case "$FIRMWARE" in +"ath11k/IPQ5018/hw1.0/cal-ahb-c000000.wifi.bin") + case "$board" in + linksys,mx2000|\ + linksys,mx5500) + caldata_extract "0:ART" 0x1000 0x20000 + label_mac=$(mtd_get_mac_ascii devinfo hw_mac_addr) + ath11k_patch_mac $(macaddr_add $label_mac 1) 0 + ath11k_remove_regdomain + ath11k_set_macflag + ;; + esac + ;; +"ath11k/QCN6122/hw1.0/cal-ahb-b00a040.wifi1.bin") + case "$board" in + linksys,mx2000) + caldata_extract "0:ART" 0x26800 0x20000 + label_mac=$(mtd_get_mac_ascii devinfo hw_mac_addr) + ath11k_patch_mac $(macaddr_add $label_mac 2) 0 + ath11k_remove_regdomain + ath11k_set_macflag + ;; + esac + ;; +"ath11k/QCN9074/hw1.0/cal-pci-0001:01:00.0.bin") + case "$board" in + linksys,mx5500) + caldata_extract "0:ART" 0x26800 0x20000 + label_mac=$(mtd_get_mac_ascii devinfo hw_mac_addr) + ath11k_patch_mac $(macaddr_add $label_mac 2) 0 + ath11k_remove_regdomain + ath11k_set_macflag + ;; + esac + ;; +*) + exit 1 + ;; +esac diff --git a/target/linux/qualcommax/ipq50xx/base-files/etc/init.d/bootcount b/target/linux/qualcommax/ipq50xx/base-files/etc/init.d/bootcount new file mode 100755 index 0000000000..b570428aef --- /dev/null +++ b/target/linux/qualcommax/ipq50xx/base-files/etc/init.d/bootcount @@ -0,0 +1,12 @@ +#!/bin/sh /etc/rc.common + +START=99 + +boot() { + case $(board_name) in + linksys,mx2000|\ + linksys,mx5500) + mtd resetbc s_env || true + ;; + esac +} diff --git a/target/linux/qualcommax/ipq50xx/base-files/lib/upgrade/platform.sh b/target/linux/qualcommax/ipq50xx/base-files/lib/upgrade/platform.sh new file mode 100644 index 0000000000..a75b6798d2 --- /dev/null +++ b/target/linux/qualcommax/ipq50xx/base-files/lib/upgrade/platform.sh @@ -0,0 +1,84 @@ +PART_NAME=firmware +REQUIRE_IMAGE_METADATA=1 + +RAMFS_COPY_BIN='fw_printenv fw_setenv head' +RAMFS_COPY_DATA='/etc/fw_env.config /var/lock/fw_printenv.lock' + +remove_oem_ubi_volume() { + local oem_volume_name="$1" + local oem_ubivol + local mtdnum + local ubidev + + mtdnum=$(find_mtd_index "$CI_UBIPART") + if [ ! "$mtdnum" ]; then + return + fi + + ubidev=$(nand_find_ubi "$CI_UBIPART") + if [ ! "$ubidev" ]; then + ubiattach --mtdn="$mtdnum" + ubidev=$(nand_find_ubi "$CI_UBIPART") + fi + + if [ "$ubidev" ]; then + oem_ubivol=$(nand_find_volume "$ubidev" "$oem_volume_name") + [ "$oem_ubivol" ] && ubirmvol "/dev/$ubidev" --name="$oem_volume_name" + fi +} + +linksys_mx_do_upgrade() { + local setenv_script="/tmp/fw_env_upgrade" + + CI_UBIPART="rootfs" + boot_part="$(fw_printenv -n boot_part)" + if [ -n "$UPGRADE_OPT_USE_CURR_PART" ]; then + if [ "$boot_part" -eq "2" ]; then + CI_KERNPART="alt_kernel" + CI_UBIPART="alt_rootfs" + fi + else + if [ "$boot_part" -eq "1" ]; then + echo "boot_part 2" >> $setenv_script + CI_KERNPART="alt_kernel" + CI_UBIPART="alt_rootfs" + else + echo "boot_part 1" >> $setenv_script + fi + fi + + boot_part_ready="$(fw_printenv -n boot_part_ready)" + if [ "$boot_part_ready" -ne "3" ]; then + echo "boot_part_ready 3" >> $setenv_script + fi + + auto_recovery="$(fw_printenv -n auto_recovery)" + if [ "$auto_recovery" != "yes" ]; then + echo "auto_recovery yes" >> $setenv_script + fi + + if [ -f "$setenv_script" ]; then + fw_setenv -s $setenv_script || { + echo "failed to update U-Boot environment" + return 1 + } + fi + nand_do_upgrade "$1" +} + +platform_check_image() { + return 0; +} + +platform_do_upgrade() { + case "$(board_name)" in + linksys,mx2000|\ + linksys,mx5500) + remove_oem_ubi_volume rootfs + linksys_mx_do_upgrade "$1" + ;; + *) + default_do_upgrade "$1" + ;; + esac +} diff --git a/target/linux/qualcommax/ipq50xx/config-default b/target/linux/qualcommax/ipq50xx/config-default new file mode 100644 index 0000000000..63c1d9dd97 --- /dev/null +++ b/target/linux/qualcommax/ipq50xx/config-default @@ -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 index 0000000000..2e68329265 --- /dev/null +++ b/target/linux/qualcommax/ipq50xx/target.mk @@ -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 index 0000000000..8e973e63f2 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0065-v6.7-arm64-dts-qcom-ipq5018-add-watchdog.patch @@ -0,0 +1,31 @@ +From 9cbaee8379e620f82112002f973adde19679df31 Mon Sep 17 00:00:00 2001 +From: Robert Marko +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 +Reviewed-by: Konrad Dybcio +Link: https://lore.kernel.org/r/20230816161455.3310629-2-robimarko@gmail.com +Signed-off-by: Bjorn Andersson +--- + 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 = ; ++ 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 index 0000000000..46d3c61398 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0066-v6.7-dt-bindings-firmware-qcom-scm-support-indicating-SDI-default-state.patch @@ -0,0 +1,41 @@ +From 92dab9ea5f389c12828283146c60054642453a91 Mon Sep 17 00:00:00 2001 +From: Robert Marko +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 +Acked-by: Mukesh Ojha +Reviewed-by: Krzysztof Kozlowski +Reviewed-by: Brian Norris +Link: https://lore.kernel.org/r/20230816164641.3371878-1-robimarko@gmail.com +Signed-off-by: Bjorn Andersson +--- + 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 index 0000000000..9df758ae1b --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0067-v6.7-firmware-qcom-scm-disable-SDI-if-required.patch @@ -0,0 +1,83 @@ +From ff4aa3bc98258a240b9bbab53fd8d2fb8184c485 Mon Sep 17 00:00:00 2001 +From: Robert Marko +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 +Reviewed-by: Guru Das Srinagesh +Link: https://lore.kernel.org/r/20230816164641.3371878-2-robimarko@gmail.com +Signed-off-by: Bjorn Andersson +--- + 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 index 0000000000..d36ccddd27 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0068-v6.7-dt-bindings-qcom-scm-document-IPQ5018-compatible.patch @@ -0,0 +1,25 @@ +From f6aa7386bc40b552eea8ec1b1d2168afe3b31110 Mon Sep 17 00:00:00 2001 +From: Robert Marko +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 +Reviewed-by: Krzysztof Kozlowski +Link: https://lore.kernel.org/r/20230816164641.3371878-3-robimarko@gmail.com +Signed-off-by: Bjorn Andersson +--- + 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 index 0000000000..6d598bf50f --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0069-v6.7-arm64-dts-qcom-IPQ5018-indicate-that-SDI-shoud-be-disabled.patch @@ -0,0 +1,26 @@ +From 79796e87215db9587d6c66ec6f6781e091bc6464 Mon Sep 17 00:00:00 2001 +From: Robert Marko +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 +Link: https://lore.kernel.org/r/20230816164641.3371878-4-robimarko@gmail.com +Signed-off-by: Bjorn Andersson +--- + 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 index 0000000000..777212249e --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0070-v6.7-dt-bindings-phy-qcom-m31-Add-IPQ5018-compatible.patch @@ -0,0 +1,28 @@ +From 1852dfaacd3f4358bbfca134b63a02bbb30c1136 Mon Sep 17 00:00:00 2001 +From: Nitheesh Sekar +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 +Signed-off-by: Nitheesh Sekar +Link: https://lore.kernel.org/r/20230904063635.24975-2-quic_nsekar@quicinc.com +Signed-off-by: Vinod Koul +--- + .../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 index 0000000000..248b47c694 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0071-v6.7-phy-qcom-m31-Add-compatible-phy-init-sequence-for-IPQ5018.patch @@ -0,0 +1,89 @@ +From 68320e35f8cb1987b4ad34347fc7033832da99e3 Mon Sep 17 00:00:00 2001 +From: Nitheesh Sekar +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 +Link: https://lore.kernel.org/r/20230904063635.24975-3-quic_nsekar@quicinc.com +Signed-off-by: Vinod Koul +--- + 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 index 0000000000..146fcbff09 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0072-v6.7-dt-bindings-usb-dwc3-Add-IPQ5018-compatible.patch @@ -0,0 +1,41 @@ +From 3865a64284cc4845c61cf3dc6c7246349d80cc49 Mon Sep 17 00:00:00 2001 +From: Nitheesh Sekar +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 +Signed-off-by: Nitheesh Sekar +Link: https://lore.kernel.org/r/20230831030503.17100-1-quic_nsekar@quicinc.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..99fdae732a --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0073-v6.8-arm64-dts-qcom-ipq5018-Add-USB-related-nodes.patch @@ -0,0 +1,86 @@ +From e7166f2774aafefd29ff26ffbbb7f6d40ac8ea1c Mon Sep 17 00:00:00 2001 +From: Nitheesh Sekar +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 +Signed-off-by: Amandeep Singh +Signed-off-by: Nitheesh Sekar +Link: https://lore.kernel.org/r/20230904063635.24975-4-quic_nsekar@quicinc.com +Signed-off-by: Bjorn Andersson +--- + 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 = ; ++ 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 = ; ++ 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 index 0000000000..d0d9e45dec --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0074-v6.8-arm64-dts-qcom-ipq5018-add-QUP1-SPI-controller.patch @@ -0,0 +1,56 @@ +From a1f42e08f0f04b72a6597f080db4bfbb3737910c Mon Sep 17 00:00:00 2001 +From: Robert Marko +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 +Reviewed-by: Kathiravan Thirumoorthy +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 +--- + 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 = ; ++ 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 = ; ++ 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 index 0000000000..314afa76d2 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0075-v6.8-dt-bindings-clock-qcom-a53pll-add-IPQ5018-compatible.patch @@ -0,0 +1,25 @@ +From 4d45d56e17348c6b6bb2bce126a4a5ea97b19900 Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +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 +Reviewed-by: Krzysztof Kozlowski +Link: https://lore.kernel.org/r/20230925102826.405446-2-quic_gokulsri@quicinc.com +Signed-off-by: Bjorn Andersson +--- + 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 index 0000000000..46dede07ca --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0076-v6.8-clk-qcom-apss-ipq-pll-add-support-for-IPQ5018.patch @@ -0,0 +1,62 @@ +From 50492f929486c044b43cb3e2c0e040aa9b61ea2b Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +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 +Signed-off-by: Sricharan Ramabadhran +Signed-off-by: Gokul Sriram Palanisamy +Reviewed-by: Dmitry Baryshkov +Link: https://lore.kernel.org/r/20230925102826.405446-3-quic_gokulsri@quicinc.com +Signed-off-by: Bjorn Andersson +--- + 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 index 0000000000..72bec54cb3 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0077-v6.8-arm64-dts-qcom-ipq5018-enable-the-CPUFreq-support.patch @@ -0,0 +1,98 @@ +From 3e4b53e04281ed3d9c7a4329c027097265c04d54 Mon Sep 17 00:00:00 2001 +From: Gokul Sriram Palanisamy +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 +Signed-off-by: Sricharan Ramabadhran +Signed-off-by: Gokul Sriram Palanisamy +Reviewed-by: Dmitry Baryshkov +Reviewed-by: Krzysztof Kozlowski +Link: https://lore.kernel.org/r/20230925102826.405446-4-quic_gokulsri@quicinc.com +Signed-off-by: Bjorn Andersson +--- + 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 + #include + #include + #include +@@ -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 index 0000000000..97631e715a --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0078-v6.8-arm64-dts-qcom-ipq5018-add-few-more-reserved-memory-regions.patch @@ -0,0 +1,66 @@ +From a427dd16e61f3d145bc24f0ed09692fc25931250 Mon Sep 17 00:00:00 2001 +From: Kathiravan Thirumoorthy +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 +Reviewed-by: Konrad Dybcio +Link: https://lore.kernel.org/r/20231025-ipq5018-misc-v1-1-7d14fde97fe7@quicinc.com +Signed-off-by: Bjorn Andersson +--- + 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 index 0000000000..ea6f61b57f --- /dev/null +++ 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 @@ -0,0 +1,83 @@ +From: Gabor Juhos +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 +--- + 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 index 0000000000..4bf0598c36 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0081-v6.10-clk-qcom-apss-ipq-pll-fix-PLL-rate-for-IPQ5018.patch @@ -0,0 +1,32 @@ +From: Gabor Juhos +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 +--- + 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 index 0000000000..bba8b3457a --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0140-dt-bindings-pwm-add-IPQ6018-binding.patch @@ -0,0 +1,60 @@ +From: Devi Priya +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 +Reviewed-by: Krzysztof Kozlowski +Co-developed-by: Baruch Siach +Signed-off-by: Baruch Siach +Signed-off-by: Devi Priya +--- +--- /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 ++ ++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 ++ ++ 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 index 0000000000..f4f396a2c1 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0141-pwm-driver-for-qualcomm-ipq6018-pwm-block.patch @@ -0,0 +1,330 @@ +From: Devi Priya +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 +Signed-off-by: Baruch Siach +Signed-off-by: Devi Priya +--- +--- 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* 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 index 0000000000..1714fc4e6f --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0142-dt-bindings-mfd-qcom-tcsr-Add-simple-mfd-support-for-IPQ6018.patch @@ -0,0 +1,148 @@ +From: Devi Priya +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 +Signed-off-by: Devi Priya +--- + .../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 ++ ++ 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 index 0000000000..c31b2ba497 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0150-dt-bindings-nvmem-add-IPQ5018-compatible.patch @@ -0,0 +1,22 @@ +From: Sricharan Ramabadhran +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 +Signed-off-by: Sricharan Ramabadhran +--- + 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 index 0000000000..36ab4abbdf --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0151-dt-bindings-thermal-qcom-tsens-Add-ipq5018-compatible.patch @@ -0,0 +1,26 @@ +From: Sricharan Ramabadhran +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 +--- +--- 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 index 0000000000..c8f393e2b6 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0152-thermal-qcom-add-new-feat-for-soc-without-rpm.patch @@ -0,0 +1,45 @@ +From: Sricharan Ramabadhran +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 +Signed-off-by: Sricharan Ramabadhran +--- + 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 index 0000000000..0de11daf72 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0153-thermal-qcom-tsens-add-support-for-IPQ5018-tsens.patch @@ -0,0 +1,118 @@ +From: Sricharan Ramabadhran +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 +--- + 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 index 0000000000..4f9b8abfa5 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0154-dts-qcom-IPQ5018-add-tsens-node.patch @@ -0,0 +1,200 @@ +From: Sricharan Ramabadhran +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 +--- + 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 = ; ++ 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 index 0000000000..7997976d7c --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0155-dt-bindings-phy-qcom-uniphy-pcie-Document-PCIe-uniphy.patch @@ -0,0 +1,85 @@ +From: Varadarajan Narayanan +Date: Thu, 2 Jan 2025 17:00:15 +0530 +Subject: [PATCH] dt-bindings: phy: qcom,uniphy-pcie: Document PCIe uniphy + +From: Nitheesh Sekar + +Document the Qualcomm UNIPHY PCIe 28LP present in IPQ5332. + +Signed-off-by: Nitheesh Sekar +Signed-off-by: Varadarajan Narayanan +--- +--- /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 ++ - Varadarajan Narayanan ++ ++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 ++ ++ 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 index 0000000000..f30db1463f --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0156-phy-qcom-Introduce-PCIe-UNIPHY-28LP-driver.patch @@ -0,0 +1,332 @@ +From: Varadarajan Narayanan +Date: Thu, 2 Jan 2025 17:00:16 +0530 +Subject: [PATCH] phy: qcom: Introduce PCIe UNIPHY 28LP driver + +From: Nitheesh Sekar + +Add Qualcomm PCIe UNIPHY 28LP driver support present +in Qualcomm IPQ5332 SoC and the phy init sequence. + +Reviewed-by: Dmitry Baryshkov +Signed-off-by: Nitheesh Sekar +Signed-off-by: Varadarajan Narayanan +--- +--- 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 _pipe_clksrc generated by PHY goes to the GCC that gate ++ * controls it. The _pipe_clk coming out of the GCC is requested ++ * by the PHY driver for its operations. ++ * We register the _pipe_clksrc here. The gcc driver takes care ++ * of assigning this _pipe_clksrc as parent to _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 index 0000000000..9951daa8f7 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0157-dt-bindings-phy-qcom-uniphy-pcie-add-IPQ5018-compatible.patch @@ -0,0 +1,25 @@ +From: George Moussalem +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 +--- +--- 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 + + 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 index 0000000000..aedb959873 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0158-phy-qcom-uniphy-pcie-28lp-add-support-for-IPQ5018.patch @@ -0,0 +1,77 @@ +From: George Moussalem +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 +--- +--- 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 index 0000000000..caa9889109 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0160-dt-bindings-PCI-qcom-Add-IPQ5018-SoC.patch @@ -0,0 +1,78 @@ +From: Nitheesh Sekar +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 +--- + .../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 index 0000000000..bd8f51b84c --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0161-PCI-qcom-add-support-for-IPQ5018.patch @@ -0,0 +1,77 @@ +From: Nitheesh Sekar +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 +Signed-off-by: Anusha Rao +Co-developed-by: Devi Priya +Signed-off-by: Devi Priya +Signed-off-by: Nitheesh Sekar +--- + 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 index 0000000000..f51d103508 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0162-arm64-dts-qcom-IPQ5018-add-PCIe-related-nodes.patch @@ -0,0 +1,216 @@ +From: Nitheesh Sekar +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 +Signed-off-by: George Moussalem +--- + 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 = ; ++ 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 = ; ++ 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 index 0000000000..2faaa1d10e --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0301-dt-bindings-mfd-qcom-tcsr-add-IPQ5018-compatible.patch @@ -0,0 +1,18 @@ +From: George Moussalem +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 +--- +--- 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 index 0000000000..7a0031666d --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0302-arm64-dts-qcom-IPQ5018-add-TCSR-node.patch @@ -0,0 +1,22 @@ +From: George Moussalem +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 +--- +--- 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 index 0000000000..265695d1c4 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0303-arm64-dts-qcom-IPQ5018-enable-the-download-mode-support.patch @@ -0,0 +1,19 @@ +From: George Moussalem +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 +--- +--- 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 index 0000000000..5705028931 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0304-dt-bindings-pwm-add-IPQ5018-compatible.patch @@ -0,0 +1,22 @@ +From: George Moussalem +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 +--- +--- 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 index 0000000000..476e5f6ba1 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0305-pinctrl-qcom-IPQ5018-update-pwm-groups.patch @@ -0,0 +1,65 @@ +From: George Moussalem +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 +--- +--- 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 index 0000000000..d60e916971 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0306-arm64-dts-qcom-ipq5018-Add-PWM-node.patch @@ -0,0 +1,27 @@ +From: George Moussalem +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 +--- +--- 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 index 0000000000..57d434271f --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0324-arm64-dts-qcom-ipq5018-Add-crypto-nodes.patch @@ -0,0 +1,41 @@ +From: George Moussalem +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 +--- +--- 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 = ; ++ 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 index 0000000000..87d9bdb270 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0337-arm64-dts-qcom-ipq5018-Add-PRNG-node.patch @@ -0,0 +1,25 @@ +From: George Moussalem +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 +--- +--- 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 index 0000000000..72085061f8 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0339-arm64-dts-qcom-ipq5018-Add-QUP1-UART2-node.patch @@ -0,0 +1,27 @@ +From: George Moussalem +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 +--- +--- 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 = ; ++ 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 index 0000000000..5447e78fab --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0340-arm64-dts-qcom-ipq5018-Add-QUP3-I2C-node.patch @@ -0,0 +1,32 @@ +From: George Moussalem +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 +--- +--- 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 = ; ++ 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 index 0000000000..433db36871 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0401-spi-dt-bindings-Introduce-qcom-spi-qpic-snand.patch @@ -0,0 +1,97 @@ +From: Md Sadre Alam +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 +Signed-off-by: Md Sadre Alam +--- +--- /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 ++ ++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 ++ 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 index 0000000000..91dceaa3a6 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0402-mtd-rawnand-qcom-cleanup-qcom_nandc-driver.patch @@ -0,0 +1,983 @@ +From: Md Sadre Alam +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 +--- +--- 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 ®s->cmd; +- case NAND_ADDR0: +- return ®s->addr0; +- case NAND_ADDR1: +- return ®s->addr1; +- case NAND_FLASH_CHIP_SELECT: +- return ®s->chip_sel; +- case NAND_EXEC_CMD: +- return ®s->exec; +- case NAND_FLASH_STATUS: +- return ®s->clrflashstatus; +- case NAND_DEV0_CFG0: +- return ®s->cfg0; +- case NAND_DEV0_CFG1: +- return ®s->cfg1; +- case NAND_DEV0_ECC_CFG: +- return ®s->ecc_bch_cfg; +- case NAND_READ_STATUS: +- return ®s->clrreadstatus; +- case NAND_DEV_CMD1: +- return ®s->cmd1; +- case NAND_DEV_CMD1_RESTORE: +- return ®s->orig_cmd1; +- case NAND_DEV_CMD_VLD: +- return ®s->vld; +- case NAND_DEV_CMD_VLD_RESTORE: +- return ®s->orig_vld; +- case NAND_EBI2_ECC_BUF_CFG: +- return ®s->ecc_buf_cfg; +- case NAND_READ_LOCATION_0: +- return ®s->read_location0; +- case NAND_READ_LOCATION_1: +- return ®s->read_location1; +- case NAND_READ_LOCATION_2: +- return ®s->read_location2; +- case NAND_READ_LOCATION_3: +- return ®s->read_location3; +- case NAND_READ_LOCATION_LAST_CW_0: +- return ®s->read_location_last0; +- case NAND_READ_LOCATION_LAST_CW_1: +- return ®s->read_location_last1; +- case NAND_READ_LOCATION_LAST_CW_2: +- return ®s->read_location_last2; +- case NAND_READ_LOCATION_LAST_CW_3: +- return ®s->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 = ®s->erased_cw_detect_cfg_set; +- else +- vaddr = ®s->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 index 0000000000..0bce70c175 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0403-mtd-rawnand-qcom-Add-qcom-prefix-to-common-api.patch @@ -0,0 +1,874 @@ +From: Md Sadre Alam +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 +--- +--- 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 index 0000000000..e992e261af --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0404-mtd-nand-Add-qpic_common-API-file.patch @@ -0,0 +1,2418 @@ +From: Md Sadre Alam +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 +--- +--- 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/** ++ * 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 + #include + #include +- +-/* 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 + + /* + * 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 ++ * ++ */ ++#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 index 0000000000..5f8ff3ed56 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0405-mtd-rawnand-qcom-use-FIELD_PREP-and-GENMASK.patch @@ -0,0 +1,191 @@ +From: Md Sadre Alam +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 +--- +--- 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 index 0000000000..95188529c4 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0406-spi-spi-qpic-add-driver-for-QCOM-SPI-NAND-flash-Interface.patch @@ -0,0 +1,1729 @@ +From: Md Sadre Alam +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 +Signed-off-by: Sricharan Ramabadhran +Co-developed-by: Varadarajan Narayanan +Signed-off-by: Varadarajan Narayanan +Signed-off-by: Md Sadre Alam +--- +--- 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 ++ * Sricharan R ++ * Varadarajan Narayanan ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++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 index 0000000000..03b18e0829 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0408-spi-spi-qpic-fix-compilation-issues.patch @@ -0,0 +1,45 @@ +From: George Moussalem +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 +--- +--- 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 index 0000000000..6442361b0a --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0411-spi-spi-qpic-snand-support-BCH8.patch @@ -0,0 +1,53 @@ +From 396886e8644d5b601126b97e0b36c40c5fb5cecf Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +Signed-off-by: George Moussalem +--- + 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 index 0000000000..3f50be2574 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0412-mtd-spinand-qpic-only-support-max-4-bytes-ID.patch @@ -0,0 +1,25 @@ +From 3d550dc3eb4eaa2fe1d0668ed67e835c91487d61 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +Signed-off-by: George Moussalem +--- + 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 index 0000000000..2de155637f --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0421-arm64-dts-qcom-ipq5018-Add-SPI-nand-node.patch @@ -0,0 +1,52 @@ +From c2019f64539dd24e6e0da3cea2219d6f9e6b03e4 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +Signed-off-by: George Moussalem +--- + 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 = ; ++ 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 index 0000000000..fdbea3cf62 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0431-arm64-dts-qcom-ipq5018-Add-more-nand-compatible-for-.patch @@ -0,0 +1,23 @@ +From b76a7649402d3eb1245ab463832133fc7efda194 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +Signed-off-by: George Moussalem +--- + 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 index 0000000000..ad7e08c895 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0701-dt-bindings-clock-qcom-Add-CMN-PLL-clock-controller-.patch @@ -0,0 +1,113 @@ +From 7b89dbf5c7dcd8a9c131721e93c1292e5993968b Mon Sep 17 00:00:00 2001 +From: Luo Jie +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 +Reviewed-by: Krzysztof Kozlowski +--- + .../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 ++ - Luo Jie ++ ++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 ++ ++ 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 index 0000000000..afccffa67e --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0702-clk-qcom-Add-CMN-PLL-clock-controller-driver-for-IPQ.patch @@ -0,0 +1,288 @@ +From a7e8397e2db6133e3435054a3f312dbd9cab05ed Mon Sep 17 00:00:00 2001 +From: Luo Jie +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 index 0000000000..9d0d52b8eb --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0703-clk-qcom-cmn-pll-add-IPQ5018-support.patch @@ -0,0 +1,78 @@ +From a28797563b8c97c9abced82e0cf89302fcd2bf37 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +Signed-off-by: George Moussalem +--- + 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 + #include + ++#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 index 0000000000..8127656ddd --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0704-arm64-dts-qcom-ipq5018-Add-ethernet-cmn-node.patch @@ -0,0 +1,45 @@ +From 1b625a37b96b0448aac126d7720eec38de8e5956 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +--- + 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 index 0000000000..7764df4219 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0711-net-phy-qcom-Introduce-IPQ5018-internal-PHY-driver.patch @@ -0,0 +1,184 @@ +From 77ad12b3a5e21cae859247c0b82cf9a5b661e531 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +Signed-off-by: George Moussalem +--- + 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 ++#include ++#include ++#include ++#include ++ ++#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 "); 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 index 0000000000..ce5367a085 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0712-arm64-dts-qcom-ipq5018-add-mdio-node.patch @@ -0,0 +1,43 @@ +From d2cdc83fb2c7360856e598810b88211d815fc851 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +Signed-off-by: George Moussalem +--- + 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 index 0000000000..9ac109858d --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0713-arm64-dts-qcom-ipq5018-add-ge_phy-node.patch @@ -0,0 +1,45 @@ +From 28490d95fe9e059c5ce74b2289d66e0d7ede2d50 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +Signed-off-by: George Moussalem +--- + 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 index 0000000000..0c773caf38 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0714-net-phy-qcom-IPQ5018-enable-configuration-of-DAC-settings.patch @@ -0,0 +1,111 @@ +From: George Moussalem +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 +--- +--- 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 index 0000000000..ae4214b0cb --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0715-net-phy-qcom-add-IPQ5018-initvals-and-CDT-feature.patch @@ -0,0 +1,108 @@ +From: George Moussalem +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 +--- +--- 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 index 0000000000..0a22289d39 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0721-clk-gcc-ipq5018-remove-the-unsupported-clk-combinati.patch @@ -0,0 +1,35 @@ +From f71366e0530db2c5cecbbbb6edfbf7344bd6f83b Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +Signed-off-by: George Moussalem +--- + 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 index 0000000000..6cd1a2a16b --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0722-clk-gcc-ipq5018-refer-to-ge-phy-rx-and-tx-clk-providers-by-name.patch @@ -0,0 +1,62 @@ +From ce9e56a436e486690097cfbdda2d0c11b60db4c2 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +Signed-off-by: George Moussalem +--- + 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 index 0000000000..bccac8a4eb --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0751-net-dsa-qca8k-always-enable-SGMII-auto-negotiation.patch @@ -0,0 +1,30 @@ +From d7a41a3ab6b8e3a3158997cda13f1fe28a37268c Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +--- + 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 index 0000000000..e664a717a3 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0752-net-dsa-qca8k-support-PHY-to-PHY-CPU-link.patch @@ -0,0 +1,49 @@ +From 8a56ac86c2eed13024413aa23a6cda85613d60f9 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +--- + 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 index 0000000000..81248e517a --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0801-dt-bindings-remoteproc-qcom-Add-support-for-multipd-model.patch @@ -0,0 +1,202 @@ +From: Manikanta Mylavarapu +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 +Reviewed-by: Krzysztof Kozlowski +--- +--- /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 ++ ++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 ++ 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 = ; ++ 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 index 0000000000..677c61f7ac --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0802-firmware-qcom_scm-ipq5332-add-support-to-pass-metada.patch @@ -0,0 +1,43 @@ +From 50799703c6c8ec0860e19b102dd7cca3d29028e1 Mon Sep 17 00:00:00 2001 +From: Manikanta Mylavarapu +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 +--- + 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 index 0000000000..06c6b04756 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0803-firmware-qcom_scm-ipq5332-add-msa-lock-unlock-suppor.patch @@ -0,0 +1,126 @@ +From 217fbbc122663c5a3dac752cebef44fb3e0cc179 Mon Sep 17 00:00:00 2001 +From: Manikanta Mylavarapu +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 +--- + 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 index 0000000000..4e955435b2 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0804-remoteproc-qcom-q6v5-Add-multipd-interrupts-support.patch @@ -0,0 +1,148 @@ +From cae691d32306966065df869fa7424728d1b16b14 Mon Sep 17 00:00:00 2001 +From: Manikanta Mylavarapu +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 +--- + 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 index 0000000000..14371cc0fd --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0805-remoteproc-qcom-Add-Hexagon-based-multipd-rproc-driv.patch @@ -0,0 +1,901 @@ +From 3f61ff1fb5c90f8b6c28a3a2b4a29203000ee585 Mon Sep 17 00:00:00 2001 +From: Manikanta Mylavarapu +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 ++ *
++ * ++ * ++ * 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 index 0000000000..b3cd492d05 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0806-rproc-qcom_q6v5_mpd-split-q6_wcss-to-rootpd-and-user.patch @@ -0,0 +1,321 @@ +From 6c66dff196cbba8515380110dd3599cde31dd896 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +Signed-off-by: George Moussalem +--- + 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 index 0000000000..59f49ad66e --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0807-remoteproc-qcom_q6v5_mpd-fix-incorrent-use-of-rproc-.patch @@ -0,0 +1,206 @@ +From 0fa7bdb855247b738d1d227d6f4b3417ebdf21a8 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +--- + 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 index 0000000000..afcc15e69f --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0811-firmware-qcom_scm-support-MPD.patch @@ -0,0 +1,128 @@ +From 6553d598cdb507f7ede020f25da646ba084a23c6 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +Signed-off-by: George Moussalem +--- + 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 index 0000000000..935a943858 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0812-soc-qcom-mdt_loader-support-MPD.patch @@ -0,0 +1,199 @@ +From bf42d84868bc82a9cb334a33930f2d1da24f7070 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +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 +Signed-off-by: George Moussalem +--- + 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 + #include + #include ++#include ++ ++#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 index 0000000000..f549580f3d --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0813-remoteproc-qcom_q6v5_mpd-enable-clocks.patch @@ -0,0 +1,38 @@ +From e83215d5d22946885fa388d375b12f1b991a43c1 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:12 +0800 +Subject: [PATCH 3/5] remoteproc: qcom_q6v5_mpd: enable clocks + +Signed-off-by: Ziyang Huang +--- + 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 index 0000000000..9316458cbd --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0814-remoteproc-qcom_q6v5_mpd-support-ipq5018.patch @@ -0,0 +1,111 @@ +From 4ae334127f073aa5f7c9209c9f0a17fd9e331db1 Mon Sep 17 00:00:00 2001 +From: Ziyang Huang +Date: Sun, 8 Sep 2024 16:40:12 +0800 +Subject: [PATCH] remoteproc: qcom_q6v5_mpd: support ipq5018 + +Signed-off-by: Ziyang Huang +--- + 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 index 0000000000..fdc2879f03 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0815-remoteproc-qcom_q6v5_mpd-add-support-for-passing-v1-bootargs.patch @@ -0,0 +1,163 @@ +From: George Moussalem +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 +--- +--- 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 index 0000000000..786345f640 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0816-arm64-dts-qcom-ipq5018-add-wifi-support.patch @@ -0,0 +1,241 @@ +From: George Moussalem +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 +--- +--- 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 = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ ++ 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 = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ status = "disabled"; ++ }; ++ ++ //QCN6122 5G/6G ++ wifi2: wifi2@c000000 { ++ reg = <0x0b00a040 0x0>; ++ compatible = "qcom,qcn6122-wifi"; ++ interrupts = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ 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 = ; ++ 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 = ; ++ ++ 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 index 0000000000..03a9c1c981 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0817-arm64-dts-qcom-ipq5018-add-tz_apps-reserved-memory-region.patch @@ -0,0 +1,22 @@ +From: George Moussalem +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 +--- +--- 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; diff --git a/target/linux/qualcommax/patches-6.6/0820-dt-bindings-net-wireless-ath11k-add-bindings-for-QCN6122.patch b/target/linux/qualcommax/patches-6.6/0820-dt-bindings-net-wireless-ath11k-add-bindings-for-QCN6122.patch new file mode 100644 index 0000000000..d9eaf582c7 --- /dev/null +++ b/target/linux/qualcommax/patches-6.6/0820-dt-bindings-net-wireless-ath11k-add-bindings-for-QCN6122.patch @@ -0,0 +1,62 @@ +From: George Moussalem +Date: Wed, 27 Oct 2024 16:34:11 +0400 +Subject: [PATCH] dt: bindings: net: add bindings for QCN6122 + +QCN6122 is a PCIe based solution that is attached to and enumerated +by the WPSS (Wireless Processor SubSystem) Q6 processor. + +Though it is a PCIe device, since it is not attached to APSS processor +(Application Processor SubSystem), APSS will be unaware of such a decice +and hence it is registered to the APSS processor as a platform device(AHB). +Because of this hybrid nature, it is called as a hybrid bus device. + +As such, QCN6122 is a hybrid bus type device and follows the same codepath +as for WCN6750. + +This is a reversed engineered and heavily simplified version of below +downstream patch: +https://git.codelinaro.org/clo/qsdk/oss/system/feeds/wlan-open/-/ \ +blob/NHSS.QSDK.12.4.5.r2/mac80211/patches/232-ath11k-qcn6122-support.patch + +Signed-off-by: George Moussalem +--- +--- a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml ++++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.yaml +@@ -21,6 +21,7 @@ properties: + - qcom,ipq6018-wifi + - qcom,wcn6750-wifi + - qcom,ipq5018-wifi ++ - qcom,qcn6122-wifi + + reg: + maxItems: 1 +@@ -258,6 +259,29 @@ allOf: + - description: interrupt event for ring DP20 + - description: interrupt event for ring DP21 + - description: interrupt event for ring DP22 ++ - if: ++ properties: ++ compatible: ++ contains: ++ enum: ++ - qcom,qcn6122-wifi ++ then: ++ properties: ++ interrupts: ++ items: ++ - description: interrupt event for ring CE1 ++ - description: interrupt event for ring CE2 ++ - description: interrupt event for ring CE3 ++ - description: interrupt event for ring CE4 ++ - description: interrupt event for ring CE5 ++ - description: interrupt event for ring DP1 ++ - description: interrupt event for ring DP2 ++ - description: interrupt event for ring DP3 ++ - description: interrupt event for ring DP4 ++ - description: interrupt event for ring DP5 ++ - description: interrupt event for ring DP6 ++ - description: interrupt event for ring DP7 ++ - description: interrupt event for ring DP8 + + examples: + - | diff --git a/target/linux/realtek/files-6.6/drivers/gpio/gpio-rtl8231.c b/target/linux/realtek/files-6.6/drivers/gpio/gpio-rtl8231.c deleted file mode 100644 index 2821591a97..0000000000 --- a/target/linux/realtek/files-6.6/drivers/gpio/gpio-rtl8231.c +++ /dev/null @@ -1,364 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -#include -#include -#include -#include -#include - -/* RTL8231 registers for LED control */ -#define RTL8231_LED_FUNC0 0x0000 -#define RTL8231_LED_FUNC1 0x0001 -#define RTL8231_READY_MASK 0x03f0 -#define RTL8231_READY_VALUE 0x0370 -#define RTL8231_GPIO_PIN_SEL(gpio) ((0x0002) + ((gpio) >> 4)) -#define RTL8231_GPIO_DIR(gpio) ((0x0005) + ((gpio) >> 4)) -#define RTL8231_GPIO_DATA(gpio) ((0x001C) + ((gpio) >> 4)) - -#define USEC_TIMEOUT 5000 - -#define RTL8231_SMI_BUS_ID_MAX 0x1F - -struct rtl8231_gpios { - struct gpio_chip gc; - struct device *dev; - u32 id; - u32 smi_bus_id; - u16 reg_shadow[0x20]; - u32 reg_cached; - int ext_gpio_indrt_access; -}; - -extern struct rtl83xx_soc_info soc_info; - -DEFINE_MUTEX(miim_lock); - -static u32 rtl8231_read(struct rtl8231_gpios *gpios, u32 reg) -{ - u32 t = 0, n = 0; - - reg &= 0x1f; - - /* Calculate read register address */ - t = (gpios->smi_bus_id << 2) | (reg << 7); - - /* Set execution bit: cleared when operation completed */ - t |= 1; - - /* Start execution */ - sw_w32(t, gpios->ext_gpio_indrt_access); - do { - udelay(1); - t = sw_r32(gpios->ext_gpio_indrt_access); - n++; - } while ((t & 1) && (n < USEC_TIMEOUT)); - - if (n >= USEC_TIMEOUT) - return 0x80000000; - - pr_debug("%s: %x, %x, %x\n", __func__, gpios->smi_bus_id, - reg, (t & 0xffff0000) >> 16); - - return (t & 0xffff0000) >> 16; -} - -static int rtl8231_write(struct rtl8231_gpios *gpios, u32 reg, u32 data) -{ - u32 t = 0, n = 0; - - pr_debug("%s: %x, %x, %x\n", __func__, gpios->smi_bus_id, reg, data); - reg &= 0x1f; - - t = (gpios->smi_bus_id << 2) | (reg << 7) | (data << 16); - /* Set write bit */ - t |= 2; - - /* Set execution bit: cleared when operation completed */ - t |= 1; - - /* Start execution */ - sw_w32(t, gpios->ext_gpio_indrt_access); - do { - udelay(1); - t = sw_r32(gpios->ext_gpio_indrt_access); - } while ((t & 1) && (n < USEC_TIMEOUT)); - - if (n >= USEC_TIMEOUT) - return -1; - - return 0; -} - -static u32 rtl8231_read_cached(struct rtl8231_gpios *gpios, u32 reg) -{ - if (reg > 0x1f) - return 0; - - if (gpios->reg_cached & (1 << reg)) - return gpios->reg_shadow[reg]; - - return rtl8231_read(gpios, reg); -} - -/* Set Direction of the RTL8231 pin: - * dir 1: input - * dir 0: output - */ -static int rtl8231_pin_dir(struct rtl8231_gpios *gpios, u32 gpio, u32 dir) -{ - u32 v; - int pin_sel_addr = RTL8231_GPIO_PIN_SEL(gpio); - int pin_dir_addr = RTL8231_GPIO_DIR(gpio); - int dpin = gpio % 16; - - if (gpio > 31) { - pr_debug("WARNING: HIGH pin\n"); - dpin += 5; - pin_dir_addr = pin_sel_addr; - } - - v = rtl8231_read_cached(gpios, pin_dir_addr); - if (v & 0x80000000) { - pr_err("Error reading RTL8231\n"); - return -1; - } - - v = (v & ~(1 << dpin)) | (dir << dpin); - rtl8231_write(gpios, pin_dir_addr, v); - gpios->reg_shadow[pin_dir_addr] = v; - gpios->reg_cached |= 1 << pin_dir_addr; - - return 0; -} - -static int rtl8231_pin_dir_get(struct rtl8231_gpios *gpios, u32 gpio, u32 *dir) -{ - /* dir 1: input - * dir 0: output - */ - - u32 v; - int pin_dir_addr = RTL8231_GPIO_DIR(gpio); - int pin = gpio % 16; - - if (gpio > 31) { - pin_dir_addr = RTL8231_GPIO_PIN_SEL(gpio); - pin += 5; - } - - v = rtl8231_read(gpios, pin_dir_addr); - if (v & (1 << pin)) - *dir = 1; - else - *dir = 0; - - return 0; -} - -static int rtl8231_pin_set(struct rtl8231_gpios *gpios, u32 gpio, u32 data) -{ - u32 v = rtl8231_read(gpios, RTL8231_GPIO_DATA(gpio)); - - pr_debug("%s: %d to %d\n", __func__, gpio, data); - if (v & 0x80000000) { - pr_err("Error reading RTL8231\n"); - return -1; - } - v = (v & ~(1 << (gpio % 16))) | (data << (gpio % 16)); - rtl8231_write(gpios, RTL8231_GPIO_DATA(gpio), v); - gpios->reg_shadow[RTL8231_GPIO_DATA(gpio)] = v; - gpios->reg_cached |= 1 << RTL8231_GPIO_DATA(gpio); - - return 0; -} - -static int rtl8231_pin_get(struct rtl8231_gpios *gpios, u32 gpio, u16 *state) -{ - u32 v = rtl8231_read(gpios, RTL8231_GPIO_DATA(gpio)); - - if (v & 0x80000000) { - pr_err("Error reading RTL8231\n"); - return -1; - } - - *state = v & 0xffff; - - return 0; -} - -static int rtl8231_direction_input(struct gpio_chip *gc, unsigned int offset) -{ - int err; - struct rtl8231_gpios *gpios = gpiochip_get_data(gc); - - pr_debug("%s: %d\n", __func__, offset); - mutex_lock(&miim_lock); - err = rtl8231_pin_dir(gpios, offset, 1); - mutex_unlock(&miim_lock); - - return err; -} - -static int rtl8231_direction_output(struct gpio_chip *gc, unsigned int offset, int value) -{ - int err; - struct rtl8231_gpios *gpios = gpiochip_get_data(gc); - - pr_debug("%s: %d\n", __func__, offset); - mutex_lock(&miim_lock); - err = rtl8231_pin_dir(gpios, offset, 0); - mutex_unlock(&miim_lock); - - if (!err) - err = rtl8231_pin_set(gpios, offset, value); - - return err; -} - -static int rtl8231_get_direction(struct gpio_chip *gc, unsigned int offset) -{ - u32 v = 0; - struct rtl8231_gpios *gpios = gpiochip_get_data(gc); - - pr_debug("%s: %d\n", __func__, offset); - mutex_lock(&miim_lock); - rtl8231_pin_dir_get(gpios, offset, &v); - mutex_unlock(&miim_lock); - - return v; -} - -static int rtl8231_gpio_get(struct gpio_chip *gc, unsigned int offset) -{ - u16 state = 0; - struct rtl8231_gpios *gpios = gpiochip_get_data(gc); - - mutex_lock(&miim_lock); - rtl8231_pin_get(gpios, offset, &state); - mutex_unlock(&miim_lock); - - if (state & (1 << (offset % 16))) - return 1; - - return 0; -} - -void rtl8231_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) -{ - struct rtl8231_gpios *gpios = gpiochip_get_data(gc); - - rtl8231_pin_set(gpios, offset, value); -} - -int rtl8231_init(struct rtl8231_gpios *gpios) -{ - u32 ret; - - pr_info("%s called, MDIO bus ID: %d\n", __func__, gpios->smi_bus_id); - - gpios->reg_cached = 0; - - if (soc_info.family == RTL8390_FAMILY_ID) { - /* RTL8390: Enable external gpio in global led control register */ - sw_w32_mask(0x7 << 18, 0x4 << 18, RTL839X_LED_GLB_CTRL); - } else if (soc_info.family == RTL8380_FAMILY_ID) { - /* RTL8380: Enable RTL8231 indirect access mode */ - sw_w32_mask(0, 1, RTL838X_EXTRA_GPIO_CTRL); - sw_w32_mask(3, 1, RTL838X_DMY_REG5); - } - - ret = rtl8231_read(gpios, RTL8231_LED_FUNC1); - if ((ret & 0x80000000) || ((ret & RTL8231_READY_MASK) != RTL8231_READY_VALUE)) - return -ENXIO; - - /* Select GPIO functionality and force input direction for pins 0-36 */ - rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(0), 0xffff); - rtl8231_write(gpios, RTL8231_GPIO_DIR(0), 0xffff); - rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(16), 0xffff); - rtl8231_write(gpios, RTL8231_GPIO_DIR(16), 0xffff); - rtl8231_write(gpios, RTL8231_GPIO_PIN_SEL(32), 0x03ff); - - /* Set LED_Start to enable drivers for output mode */ - rtl8231_write(gpios, RTL8231_LED_FUNC0, 1 << 1); - - return 0; -} - -static const struct of_device_id rtl8231_gpio_of_match[] = { - { .compatible = "realtek,rtl8231-gpio" }, - {}, -}; - -MODULE_DEVICE_TABLE(of, rtl8231_gpio_of_match); - -static int rtl8231_gpio_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct rtl8231_gpios *gpios; - int err; - - pr_info("Probing RTL8231 GPIOs\n"); - - if (!np) { - dev_err(&pdev->dev, "No DT found\n"); - return -EINVAL; - } - - gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL); - if (!gpios) - return -ENOMEM; - - gpios->id = soc_info.id; - if (soc_info.family == RTL8380_FAMILY_ID) { - gpios->ext_gpio_indrt_access = RTL838X_EXT_GPIO_INDRT_ACCESS; - } - - if (soc_info.family == RTL8390_FAMILY_ID) { - gpios->ext_gpio_indrt_access = RTL839X_EXT_GPIO_INDRT_ACCESS; - } - - err = of_property_read_u32(np, "indirect-access-bus-id", &gpios->smi_bus_id); - if (!err && gpios->smi_bus_id > RTL8231_SMI_BUS_ID_MAX) - err = -EINVAL; - - if (err) { - dev_err(dev, "invalid or missing indirect-access-bus-id\n"); - return err; - } - - err = rtl8231_init(gpios); - if (err) { - dev_err(dev, "no device found at bus address %d\n", gpios->smi_bus_id); - return err; - } - - gpios->dev = dev; - gpios->gc.base = -1; - gpios->gc.ngpio = 37; - gpios->gc.label = "rtl8231"; - gpios->gc.parent = dev; - gpios->gc.owner = THIS_MODULE; - gpios->gc.can_sleep = true; - - gpios->gc.direction_input = rtl8231_direction_input; - gpios->gc.direction_output = rtl8231_direction_output; - gpios->gc.set = rtl8231_gpio_set; - gpios->gc.get = rtl8231_gpio_get; - gpios->gc.get_direction = rtl8231_get_direction; - - return devm_gpiochip_add_data(dev, &gpios->gc, gpios); -} - -static struct platform_driver rtl8231_gpio_driver = { - .driver = { - .name = "rtl8231-gpio", - .of_match_table = rtl8231_gpio_of_match, - }, - .probe = rtl8231_gpio_probe, -}; - -module_platform_driver(rtl8231_gpio_driver); - -MODULE_DESCRIPTION("Realtek RTL8231 GPIO expansion chip support"); -MODULE_LICENSE("GPL v2"); diff --git a/target/linux/realtek/patches-6.6/301-gpio-add-rtl8231-driver.patch b/target/linux/realtek/patches-6.6/301-gpio-add-rtl8231-driver.patch deleted file mode 100644 index f64e2cf94e..0000000000 --- a/target/linux/realtek/patches-6.6/301-gpio-add-rtl8231-driver.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 2b88563ee5aafd9571d965b7f2093a0f58d98a31 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Thu, 26 Nov 2020 12:02:21 +0100 -Subject: [PATCH] realtek: update the tree to the latest refactored version -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -* rename the target to realtek -* add refactored DSA driver -* add latest gpio driver -* lots of arch cleanups -* new irq driver -* additional boards - -Submitted-by: Bert Vermeulen -Submitted-by: Birger Koblitz -Submitted-by: Sander Vanheule -Submitted-by: Bjørn Mork -Submitted-by: John Crispin ---- - drivers/gpio/Kconfig | 6 ++++++ - drivers/gpio/Makefile | 1 + - 2 files changed, 7 insertions(+) - ---- a/drivers/gpio/Kconfig -+++ b/drivers/gpio/Kconfig -@@ -553,6 +553,12 @@ config GPIO_ROCKCHIP - help - Say yes here to support GPIO on Rockchip SoCs. - -+config GPIO_RTL8231 -+ tristate "RTL8231 GPIO" -+ depends on MACH_REALTEK_RTL -+ help -+ Say yes here to support Realtek RTL8231 GPIO expansion chips. -+ - config GPIO_SAMA5D2_PIOBU - tristate "SAMA5D2 PIOBU GPIO support" - depends on MFD_SYSCON ---- a/drivers/gpio/Makefile -+++ b/drivers/gpio/Makefile -@@ -138,6 +138,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc3 - obj-$(CONFIG_GPIO_REALTEK_OTTO) += gpio-realtek-otto.o - obj-$(CONFIG_GPIO_REG) += gpio-reg.o - obj-$(CONFIG_GPIO_ROCKCHIP) += gpio-rockchip.o -+obj-$(CONFIG_GPIO_RTL8231) += gpio-rtl8231.o - obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o - obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o - obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o diff --git a/target/linux/realtek/rtl838x/config-6.6 b/target/linux/realtek/rtl838x/config-6.6 index 3a2dbae8c1..3020e58a68 100644 --- a/target/linux/realtek/rtl838x/config-6.6 +++ b/target/linux/realtek/rtl838x/config-6.6 @@ -96,7 +96,6 @@ CONFIG_GPIO_PCA953X=y CONFIG_GPIO_PCA953X_IRQ=y CONFIG_GPIO_REALTEK_OTTO=y CONFIG_GPIO_REGMAP=y -CONFIG_GPIO_RTL8231=y CONFIG_GPIO_WATCHDOG=y # CONFIG_GPIO_WATCHDOG_ARCH_INITCALL is not set CONFIG_GRO_CELLS=y diff --git a/target/linux/realtek/rtl839x/config-6.6 b/target/linux/realtek/rtl839x/config-6.6 index b64c46b613..9b9bcae6df 100644 --- a/target/linux/realtek/rtl839x/config-6.6 +++ b/target/linux/realtek/rtl839x/config-6.6 @@ -97,7 +97,6 @@ CONFIG_GPIO_PCA953X=y CONFIG_GPIO_PCA953X_IRQ=y CONFIG_GPIO_REALTEK_OTTO=y CONFIG_GPIO_REGMAP=y -CONFIG_GPIO_RTL8231=y CONFIG_GPIO_WATCHDOG=y # CONFIG_GPIO_WATCHDOG_ARCH_INITCALL is not set CONFIG_GRO_CELLS=y diff --git a/target/linux/realtek/rtl930x/config-6.6 b/target/linux/realtek/rtl930x/config-6.6 index 4c53682b1b..a381e88824 100644 --- a/target/linux/realtek/rtl930x/config-6.6 +++ b/target/linux/realtek/rtl930x/config-6.6 @@ -78,7 +78,6 @@ CONFIG_GPIO_CDEV=y CONFIG_GPIO_GENERIC=y CONFIG_GPIO_PCA953X=y CONFIG_GPIO_REALTEK_OTTO=y -CONFIG_GPIO_RTL8231=y CONFIG_GRO_CELLS=y CONFIG_HARDWARE_WATCHPOINTS=y CONFIG_HAS_DMA=y diff --git a/target/linux/realtek/rtl931x/config-6.6 b/target/linux/realtek/rtl931x/config-6.6 index 716d805e51..c9cd3ddfb4 100644 --- a/target/linux/realtek/rtl931x/config-6.6 +++ b/target/linux/realtek/rtl931x/config-6.6 @@ -83,7 +83,6 @@ CONFIG_GPIOLIB_IRQCHIP=y CONFIG_GPIO_CDEV=y CONFIG_GPIO_GENERIC=y CONFIG_GPIO_REALTEK_OTTO=y -CONFIG_GPIO_RTL8231=y CONFIG_GRO_CELLS=y CONFIG_HARDWARE_WATCHPOINTS=y CONFIG_HAS_DMA=y