]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
qualcommbe: create files for v6.18 from v6.12
authorAlexandru Gagniuc <mr.nuke.me@gmail.com>
Sat, 10 Jan 2026 21:21:08 +0000 (15:21 -0600)
committerRobert Marko <robimarko@gmail.com>
Thu, 28 May 2026 08:15:20 +0000 (10:15 +0200)
Create the config and relevant patches for 6.18 from 6.12. The
"standard" openwrt devel process seems to be to move the files and
restore the old ones. I find this process confusing, and I don't see
any git benefits for doing things this way. So just copy the files.

Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/21506
Signed-off-by: Robert Marko <robimarko@gmail.com>
73 files changed:
target/linux/qualcommbe/config-6.18 [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0100-arm64-dts-qcom-ipq9574-Add-nsscc-node.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0101-arm64-dts-qcom-ipq9574-fix-the-msi-interrupt-numbers.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0103-arm64-dts-qcom-ipq9574-Add-SPI-nand-support.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0104-arm64-dts-qcom-ipq9574-Enable-SPI-NAND-for-ipq9574.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0105-arm64-dts-qcom-ipq9574-Remove-eMMC-node.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0301-arm64-dts-qcom-Add-IPQ9574-MDIO-device-node.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0302-arm64-dts-qcom-ipq9574-Use-usb-phy-for-node-names.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0304-arm64-dts-qcom-ipq9574-add-QPIC-SPI-NAND-default-par.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0305-arm64-dts-qcom-add-partition-table-for-ipq9574-rdp-c.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0306-dt-bindings-net-Document-Qualcomm-QCA8084-PHY-packag.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0307-net-phy-qca808x-Add-QCA8084-ethernet-phy-support.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0308-net-phy-qca808x-Add-config_init-function-for-QCA8084.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0309-net-phy-qca808x-Add-link_change_notify-function-for-.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0310-net-phy-qca808x-Add-register-access-support-routines.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0311-net-phy-qca808x-Add-QCA8084-probe-function.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0312-net-phy-qca808x-Add-package-clocks-and-resets-for-QC.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0313-net-phy-qca808x-Add-QCA8084-package-init-function.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0314-dt-bindings-net-pcs-Add-Ethernet-PCS-for-Qualcomm-IP.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0315-net-pcs-Add-PCS-driver-for-Qualcomm-IPQ9574-SoC.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0316-net-pcs-qcom-ipq9574-Add-PCS-instantiation-and-phyli.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0317-net-pcs-qcom-ipq9574-Add-USXGMII-interface-mode-supp.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0318-MAINTAINERS-Add-maintainer-for-Qualcomm-IPQ9574-PCS-.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0322-arm64-dts-qcom-ipq9574-add-PCS-uniphy-nodes.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0323-dt-bindings-net-Add-PPE-for-Qualcomm-IPQ9574-SoC.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0324-docs-networking-Add-PPE-driver-documentation-for-Qua.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0325-net-ethernet-qualcomm-Add-PPE-driver-for-IPQ9574-SoC.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0326-net-ethernet-qualcomm-Initialize-PPE-buffer-manageme.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0327-net-ethernet-qualcomm-Initialize-PPE-queue-managemen.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0328-net-ethernet-qualcomm-Initialize-the-PPE-scheduler-s.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0329-net-ethernet-qualcomm-Initialize-PPE-queue-settings.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0330-net-ethernet-qualcomm-Initialize-PPE-service-code-se.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0331-net-ethernet-qualcomm-Initialize-PPE-port-control-se.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0332-net-ethernet-qualcomm-Initialize-PPE-RSS-hash-settin.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0333-net-ethernet-qualcomm-Initialize-PPE-queue-to-Ethern.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0334-net-ethernet-qualcomm-Initialize-PPE-L2-bridge-setti.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0335-net-ethernet-qualcomm-Add-PPE-debugfs-support-for-PP.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0336-MAINTAINERS-Add-maintainer-for-Qualcomm-PPE-driver.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0337-net-ethernet-qualcomm-Add-PPE-scheduler-config.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0338-net-ethernet-qualcomm-Add-phylink-support-for-PPE-MA.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0339-net-ethernet-qualcomm-Add-PPE-port-MAC-MIB-statistic.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0340-net-ethernet-qualcomm-Add-PPE-port-MAC-address-and-E.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0341-net-ethernet-qualcomm-Add-API-to-configure-PPE-port-.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0342-net-ethernet-qualcomm-Add-EDMA-support-for-QCOM-IPQ9.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0343-net-ethernet-qualcomm-Add-netdevice-support-for-QCOM.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0344-net-ethernet-qualcomm-Add-Rx-Ethernet-DMA-support.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0345-net-ethernet-qualcomm-Add-Tx-Ethernet-DMA-support.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0346-net-ethernet-qualcomm-Add-miscellaneous-error-interr.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0347-net-ethernet-qualcomm-Add-ethtool-support-for-EDMA.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0348-net-ethernet-qualcomm-Add-module-parameters-for-driv.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0349-net-ethernet-qualcomm-Add-sysctl-for-RPS-bitmap.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0350-net-ethernet-qualcomm-Add-support-for-label-property.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0351-net-ethernet-qualcomm-ppe-Fix-unmet-dependency-with-.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0352-net-ethernet-qualcomm-ppe-select-correct-PCS-depende.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0353-arm64-dts-qcom-Add-IPQ9574-PPE-base-device-node.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0354-arm64-dts-qcom-Add-EDMA-node-for-IPQ9574.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0355-arm64-dts-qcom-Add-IPQ9574-RDP433-port-node.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0356-arm64-dts-qcom-add-AQR-NVMEM-node-for-IPQ9574-RDP433.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0357-arm64-dts-qcom-Add-label-to-EDMA-port-for-IPQ9574-RD.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0358-clk-qcom-nsscc-Attach-required-NSSNOC-clock-to-PM-do.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0359-arm64-dts-qcom-ipq9574-add-NSSNOC-clock-to-nss-node.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0360-clk-qcom-nsscc-ipq9574-fix-port5-clock-config.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0361-net-pcs-Add-10GBASER-interface-mode-support-to-IPQ-U.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0362-net-pcs-Add-2500BASEX-interface-mode-support-to-IPQ-.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0363-net-pcs-Add-1000BASEX-interface-mode-support-to-IPQ-.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0364-net-pcs-Add-10G_QXGMII-interface-mode-support-to-IPQ.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0365-net-pcs-ipq-uniphy-control-MISC2-register-for-2.5G-s.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0367-net-pcs-ipq-uniphy-fix-USXGMII-link-up-failure.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0368-net-pcs-qcom-ipq9574-Update-IPQ9574-PCS-driver.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0370-net-phy-Add-phy_package_remove_once-helper.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0371-net-phy-qca808x-Add-QCA8084-SerDes-probe-and-remove-.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0372-net-phy-qca808x-Add-QCA8084-SerDes-init-function.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.18/0373-net-phy-qca808x-Add-QCA8084-SerDes-speed-config.patch [new file with mode: 0644]

diff --git a/target/linux/qualcommbe/config-6.18 b/target/linux/qualcommbe/config-6.18
new file mode 100644 (file)
index 0000000..360b2ab
--- /dev/null
@@ -0,0 +1,622 @@
+CONFIG_64BIT=y
+CONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y
+CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y
+CONFIG_ARCH_DEFAULT_KEXEC_IMAGE_VERIFY_SIG=y
+CONFIG_ARCH_DMA_ADDR_T_64BIT=y
+CONFIG_ARCH_FORCE_MAX_ORDER=10
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_ARCH_KEEP_MEMBLOCK=y
+CONFIG_ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE=y
+CONFIG_ARCH_MMAP_RND_BITS=18
+CONFIG_ARCH_MMAP_RND_BITS_MAX=24
+CONFIG_ARCH_MMAP_RND_BITS_MIN=18
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11
+CONFIG_ARCH_PKEY_BITS=3
+CONFIG_ARCH_PROC_KCORE_TEXT=y
+CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_STACKWALK=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARCH_WANTS_EXECMEM_LATE=y
+CONFIG_ARCH_WANTS_NO_INSTR=y
+CONFIG_ARCH_WANTS_THP_SWAP=y
+CONFIG_ARM64=y
+CONFIG_ARM64_4K_PAGES=y
+CONFIG_ARM64_ERRATUM_1165522=y
+CONFIG_ARM64_ERRATUM_1286807=y
+CONFIG_ARM64_ERRATUM_2051678=y
+CONFIG_ARM64_ERRATUM_2054223=y
+CONFIG_ARM64_ERRATUM_2067961=y
+CONFIG_ARM64_ERRATUM_2077057=y
+CONFIG_ARM64_ERRATUM_2658417=y
+CONFIG_ARM64_LD_HAS_FIX_ERRATUM_843419=y
+CONFIG_ARM64_PA_BITS=48
+CONFIG_ARM64_PA_BITS_48=y
+CONFIG_ARM64_PLATFORM_DEVICES=y
+CONFIG_ARM64_PTR_AUTH=y
+CONFIG_ARM64_PTR_AUTH_KERNEL=y
+CONFIG_ARM64_SVE=y
+CONFIG_ARM64_TAGGED_ADDR_ABI=y
+CONFIG_ARM64_VA_BITS=39
+CONFIG_ARM64_VA_BITS_39=y
+# CONFIG_ARM64_VA_BITS_52 is not set
+CONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y
+CONFIG_ARM64_WORKAROUND_SPECULATIVE_AT=y
+CONFIG_ARM64_WORKAROUND_TSB_FLUSH_FAILURE=y
+CONFIG_ARM_AMBA=y
+CONFIG_ARM_ARCH_TIMER=y
+CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y
+CONFIG_ARM_GIC=y
+CONFIG_ARM_GIC_V2M=y
+CONFIG_ARM_GIC_V3=y
+CONFIG_ARM_GIC_V3_ITS=y
+# CONFIG_ARM_MHU_V2 is not set
+# CONFIG_ARM_MHU_V3 is not set
+CONFIG_ARM_PSCI_CPUIDLE=y
+CONFIG_ARM_PSCI_FW=y
+# CONFIG_ARM_QCOM_CPUFREQ_HW is not set
+CONFIG_ARM_QCOM_CPUFREQ_NVMEM=y
+CONFIG_AT803X_PHY=y
+CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y
+CONFIG_AUXILIARY_BUS=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_MQ_PCI=y
+CONFIG_BLK_MQ_VIRTIO=y
+CONFIG_BLK_PM=y
+CONFIG_BUILTIN_RETURN_ADDRESS_STRIPS_PAC=y
+CONFIG_CC_HAVE_SHADOW_CALL_STACK=y
+CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y
+# CONFIG_CLK_QCM2290_GPUCC is not set
+# CONFIG_CLK_X1E80100_CAMCC is not set
+# CONFIG_CLK_X1E80100_DISPCC is not set
+# CONFIG_CLK_X1E80100_GCC is not set
+# CONFIG_CLK_X1E80100_GPUCC is not set
+# CONFIG_CLK_X1E80100_TCSRCC is not set
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_COMMON_CLK=y
+CONFIG_COMMON_CLK_QCOM=y
+CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1
+# CONFIG_COMPAT_32BIT_TIME is not set
+CONFIG_CONTEXT_TRACKING=y
+CONFIG_CONTEXT_TRACKING_IDLE=y
+CONFIG_COREDUMP=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_CPUFREQ_DT_PLATDEV=y
+CONFIG_CPU_FREQ=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y
+CONFIG_CPU_FREQ_GOV_ATTR_SET=y
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
+# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+# CONFIG_CPU_FREQ_GOV_USERSPACE is not set
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_THERMAL=y
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_MENU=y
+CONFIG_CPU_IDLE_MULTIPLE_DRIVERS=y
+CONFIG_CPU_LITTLE_ENDIAN=y
+CONFIG_CPU_MITIGATIONS=y
+CONFIG_CPU_PM=y
+CONFIG_CPU_RMAP=y
+CONFIG_CPU_THERMAL=y
+CONFIG_CRC16=y
+CONFIG_CRC8=y
+CONFIG_CRYPTO_AUTHENC=y
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_DEV_QCE=y
+CONFIG_CRYPTO_DEV_QCE_AEAD=y
+# CONFIG_CRYPTO_DEV_QCE_ENABLE_AEAD is not set
+CONFIG_CRYPTO_DEV_QCE_ENABLE_ALL=y
+# CONFIG_CRYPTO_DEV_QCE_ENABLE_SHA is not set
+# CONFIG_CRYPTO_DEV_QCE_ENABLE_SKCIPHER is not set
+CONFIG_CRYPTO_DEV_QCE_SHA=y
+CONFIG_CRYPTO_DEV_QCE_SKCIPHER=y
+CONFIG_CRYPTO_DEV_QCE_SW_MAX_LEN=512
+CONFIG_CRYPTO_DEV_QCOM_RNG=y
+CONFIG_CRYPTO_ECB=y
+CONFIG_CRYPTO_HASH_INFO=y
+CONFIG_CRYPTO_HW=y
+CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
+CONFIG_CRYPTO_LIB_DES=y
+CONFIG_CRYPTO_LIB_GF128MUL=y
+CONFIG_CRYPTO_LIB_SHA1=y
+CONFIG_CRYPTO_LIB_SHA256=y
+CONFIG_CRYPTO_LIB_UTILS=y
+CONFIG_CRYPTO_LZO=y
+CONFIG_CRYPTO_RNG=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_XTS=y
+CONFIG_CRYPTO_ZSTD=y
+CONFIG_DCACHE_WORD_ACCESS=y
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEV_COREDUMP=y
+CONFIG_DMADEVICES=y
+CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC=y
+CONFIG_DMA_DIRECT_REMAP=y
+CONFIG_DMA_ENGINE=y
+CONFIG_DMA_NEED_SYNC=y
+CONFIG_DMA_OF=y
+CONFIG_DMA_VIRTUAL_CHANNELS=y
+CONFIG_DTC=y
+CONFIG_DT_IDLE_STATES=y
+CONFIG_EDAC_SUPPORT=y
+CONFIG_EXCLUSIVE_SYSTEM_RAM=y
+CONFIG_FIXED_PHY=y
+CONFIG_FIX_EARLYCON_MEM=y
+CONFIG_FRAME_POINTER=y
+CONFIG_FS_IOMAP=y
+CONFIG_FUNCTION_ALIGNMENT=4
+CONFIG_FUNCTION_ALIGNMENT_4B=y
+CONFIG_FWNODE_MDIO=y
+CONFIG_FW_LOADER_PAGED_BUF=y
+CONFIG_FW_LOADER_SYSFS=y
+CONFIG_GCC_SUPPORTS_DYNAMIC_FTRACE_WITH_ARGS=y
+CONFIG_GENERIC_ALLOCATOR=y
+CONFIG_GENERIC_ARCH_TOPOLOGY=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_GENERIC_CPU_AUTOPROBE=y
+CONFIG_GENERIC_CPU_DEVICES=y
+CONFIG_GENERIC_CPU_VULNERABILITIES=y
+CONFIG_GENERIC_CSUM=y
+CONFIG_GENERIC_EARLY_IOREMAP=y
+CONFIG_GENERIC_GETTIMEOFDAY=y
+CONFIG_GENERIC_IDLE_POLL_SETUP=y
+CONFIG_GENERIC_IOREMAP=y
+CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
+CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y
+CONFIG_GENERIC_MSI_IRQ=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_PHY=y
+CONFIG_GENERIC_PINCONF=y
+CONFIG_GENERIC_PINCTRL_GROUPS=y
+CONFIG_GENERIC_PINMUX_FUNCTIONS=y
+CONFIG_GENERIC_SCHED_CLOCK=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GENERIC_STRNCPY_FROM_USER=y
+CONFIG_GENERIC_STRNLEN_USER=y
+CONFIG_GENERIC_TIME_VSYSCALL=y
+CONFIG_GLOB=y
+CONFIG_GPIOLIB_IRQCHIP=y
+CONFIG_GPIO_CDEV=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_HAS_DMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_IOPORT_MAP=y
+CONFIG_HWMON=y
+CONFIG_HWSPINLOCK=y
+CONFIG_HWSPINLOCK_QCOM=y
+CONFIG_HW_RANDOM=y
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_HELPER_AUTO=y
+# CONFIG_I2C_QCOM_CCI is not set
+CONFIG_I2C_QUP=y
+# CONFIG_IDPF is not set
+CONFIG_IIO=y
+CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_INTERCONNECT=y
+CONFIG_INTERCONNECT_CLK=y
+# CONFIG_INTERCONNECT_QCOM is not set
+CONFIG_IPQ_APSS_6018=y
+CONFIG_IPQ_APSS_PLL=y
+# CONFIG_IPQ_CMN_PLL is not set
+# CONFIG_IPQ_GCC_4019 is not set
+# CONFIG_IPQ_GCC_5018 is not set
+# CONFIG_IPQ_GCC_5332 is not set
+# CONFIG_IPQ_GCC_6018 is not set
+# CONFIG_IPQ_GCC_8074 is not set
+# CONFIG_IPQ_GCC_9574 is not set
+# CONFIG_IPQ_NSSCC_QCA8K is not set
+CONFIG_IRQCHIP=y
+CONFIG_IRQ_DOMAIN=y
+CONFIG_IRQ_DOMAIN_HIERARCHY=y
+CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_IRQ_MSI_LIB=y
+CONFIG_IRQ_WORK=y
+# CONFIG_KPSS_XCC is not set
+CONFIG_LEDS_TLC591XX=y
+CONFIG_LIBFDT=y
+CONFIG_LOCK_DEBUGGING_SUPPORT=y
+CONFIG_LOCK_SPIN_ON_OWNER=y
+CONFIG_LRU_GEN_WALKS_MMU=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_MAILBOX=y
+# CONFIG_MAILBOX_TEST is not set
+CONFIG_MDIO_BUS=y
+CONFIG_MDIO_DEVICE=y
+CONFIG_MDIO_DEVRES=y
+CONFIG_MDIO_IPQ4019=y
+# CONFIG_MFD_QCOM_RPM is not set
+CONFIG_MFD_SYSCON=y
+CONFIG_MIGRATION=y
+# CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY is not set
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_CQHCI=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_IO_ACCESSORS=y
+CONFIG_MMC_SDHCI_MSM=y
+# CONFIG_MMC_SDHCI_PCI is not set
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMU_LAZY_TLB_REFCOUNT=y
+CONFIG_MODULES_USE_ELF_RELA=y
+# CONFIG_MSM_GCC_8916 is not set
+# CONFIG_MSM_GCC_8917 is not set
+# CONFIG_MSM_GCC_8939 is not set
+# CONFIG_MSM_GCC_8976 is not set
+# CONFIG_MSM_GCC_8994 is not set
+# CONFIG_MSM_GCC_8996 is not set
+# CONFIG_MSM_GCC_8998 is not set
+# CONFIG_MSM_GPUCC_8998 is not set
+# CONFIG_MSM_MMCC_8996 is not set
+# CONFIG_MSM_MMCC_8998 is not set
+CONFIG_MTD_NAND_CORE=y
+CONFIG_MTD_NAND_ECC=y
+CONFIG_MTD_NAND_ECC_SW_HAMMING=y
+CONFIG_MTD_NAND_QCOM=y
+CONFIG_MTD_QCOMSMEM_PARTS=y
+CONFIG_MTD_RAW_NAND=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_BEB_LIMIT=20
+CONFIG_MTD_UBI_BLOCK=y
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MUTEX_SPIN_ON_OWNER=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_NEED_SG_DMA_LENGTH=y
+CONFIG_NET_EGRESS=y
+CONFIG_NET_FLOW_LIMIT=y
+CONFIG_NET_INGRESS=y
+CONFIG_NET_SELFTESTS=y
+CONFIG_NET_XGRESS=y
+CONFIG_NLS=y
+CONFIG_NO_HZ_COMMON=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_NR_CPUS=4
+# CONFIG_NSM is not set
+CONFIG_NVMEM=y
+CONFIG_NVMEM_LAYOUTS=y
+CONFIG_NVMEM_LAYOUT_U_BOOT_ENV=y
+CONFIG_NVMEM_QCOM_QFPROM=y
+# CONFIG_NVMEM_QCOM_SEC_QFPROM is not set
+CONFIG_NVMEM_SYSFS=y
+CONFIG_OF=y
+CONFIG_OF_ADDRESS=y
+CONFIG_OF_EARLY_FLATTREE=y
+CONFIG_OF_FLATTREE=y
+CONFIG_OF_GPIO=y
+CONFIG_OF_IRQ=y
+CONFIG_OF_KOBJ=y
+CONFIG_OF_MDIO=y
+CONFIG_PADATA=y
+CONFIG_PAGE_POOL=y
+CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
+CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
+CONFIG_PARTITION_PERCPU=y
+CONFIG_PCI=y
+CONFIG_PCIEAER=y
+CONFIG_PCIEASPM=y
+CONFIG_PCIEASPM_DEFAULT=y
+# CONFIG_PCIEASPM_PERFORMANCE is not set
+# CONFIG_PCIEASPM_POWERSAVE is not set
+# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCIE_DW=y
+CONFIG_PCIE_DW_HOST=y
+CONFIG_PCIE_PME=y
+CONFIG_PCIE_QCOM=y
+CONFIG_PCIE_QCOM_COMMON=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCI_DOMAINS_GENERIC=y
+CONFIG_PCI_MSI=y
+# CONFIG_PCS_QCOM_IPQ9574 is not set
+CONFIG_PER_VMA_LOCK=y
+CONFIG_PGTABLE_LEVELS=3
+CONFIG_PHYLIB=y
+CONFIG_PHYLIB_LEDS=y
+CONFIG_PHYS_ADDR_T_64BIT=y
+# CONFIG_PHY_QCOM_APQ8064_SATA is not set
+# CONFIG_PHY_QCOM_EDP is not set
+# CONFIG_PHY_QCOM_EUSB2_REPEATER is not set
+# CONFIG_PHY_QCOM_IPQ4019_USB is not set
+# CONFIG_PHY_QCOM_IPQ806X_SATA is not set
+# CONFIG_PHY_QCOM_IPQ806X_USB is not set
+# CONFIG_PHY_QCOM_M31_USB is not set
+# CONFIG_PHY_QCOM_PCIE2 is not set
+CONFIG_PHY_QCOM_QMP=y
+CONFIG_PHY_QCOM_QMP_COMBO=y
+CONFIG_PHY_QCOM_QMP_PCIE=y
+CONFIG_PHY_QCOM_QMP_PCIE_8996=y
+CONFIG_PHY_QCOM_QMP_UFS=y
+CONFIG_PHY_QCOM_QMP_USB=y
+# CONFIG_PHY_QCOM_QMP_USB_LEGACY is not set
+CONFIG_PHY_QCOM_QUSB2=y
+# CONFIG_PHY_QCOM_SGMII_ETH is not set
+# CONFIG_PHY_QCOM_SNPS_EUSB2 is not set
+# CONFIG_PHY_QCOM_USB_HS_28NM is not set
+# CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2 is not set
+# CONFIG_PHY_QCOM_USB_SS is not set
+CONFIG_PINCTRL=y
+# CONFIG_PINCTRL_IPQ5018 is not set
+# CONFIG_PINCTRL_IPQ5332 is not set
+# CONFIG_PINCTRL_IPQ6018 is not set
+# CONFIG_PINCTRL_IPQ8074 is not set
+# CONFIG_PINCTRL_IPQ9574 is not set
+CONFIG_PINCTRL_MSM=y
+# CONFIG_PINCTRL_MSM8916 is not set
+# CONFIG_PINCTRL_MSM8976 is not set
+# CONFIG_PINCTRL_MSM8994 is not set
+# CONFIG_PINCTRL_MSM8996 is not set
+# CONFIG_PINCTRL_MSM8998 is not set
+# CONFIG_PINCTRL_QCM2290 is not set
+# CONFIG_PINCTRL_QCOM_SSBI_PMIC is not set
+# CONFIG_PINCTRL_QCS404 is not set
+# CONFIG_PINCTRL_QDU1000 is not set
+# CONFIG_PINCTRL_SA8775P is not set
+# CONFIG_PINCTRL_SC7180 is not set
+# CONFIG_PINCTRL_SC8280XP is not set
+# CONFIG_PINCTRL_SDM660 is not set
+# CONFIG_PINCTRL_SDM670 is not set
+# CONFIG_PINCTRL_SDM845 is not set
+# CONFIG_PINCTRL_SDX75 is not set
+# CONFIG_PINCTRL_SM4450 is not set
+# CONFIG_PINCTRL_SM6350 is not set
+# CONFIG_PINCTRL_SM6375 is not set
+# CONFIG_PINCTRL_SM7150 is not set
+# CONFIG_PINCTRL_SM8150 is not set
+# CONFIG_PINCTRL_SM8250 is not set
+# CONFIG_PINCTRL_SM8450 is not set
+# CONFIG_PINCTRL_SM8550 is not set
+# CONFIG_PINCTRL_SM8650 is not set
+# CONFIG_PINCTRL_X1E80100 is not set
+CONFIG_PM=y
+CONFIG_PM_CLK=y
+CONFIG_PM_OPP=y
+CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y
+CONFIG_POWER_RESET=y
+# CONFIG_POWER_RESET_MSM is not set
+CONFIG_POWER_SUPPLY=y
+CONFIG_PRINTK_TIME=y
+CONFIG_PTP_1588_CLOCK_OPTIONAL=y
+CONFIG_QCA807X_PHY=y
+CONFIG_QCA808X_PHY=y
+# CONFIG_QCM_DISPCC_2290 is not set
+# CONFIG_QCM_GCC_2290 is not set
+# CONFIG_QCOM_A53PLL is not set
+# CONFIG_QCOM_AOSS_QMP is not set
+CONFIG_QCOM_APCS_IPC=y
+# CONFIG_QCOM_APR is not set
+CONFIG_QCOM_BAM_DMA=y
+# CONFIG_QCOM_CLK_APCC_MSM8996 is not set
+# CONFIG_QCOM_CLK_APCS_MSM8916 is not set
+# CONFIG_QCOM_COMMAND_DB is not set
+# CONFIG_QCOM_CPR is not set
+# CONFIG_QCOM_CPUCP_MBOX is not set
+# CONFIG_QCOM_EBI2 is not set
+# CONFIG_QCOM_FASTRPC is not set
+# CONFIG_QCOM_GENI_SE is not set
+# CONFIG_QCOM_GSBI is not set
+# CONFIG_QCOM_HFPLL is not set
+# CONFIG_QCOM_ICC_BWMON is not set
+# CONFIG_QCOM_IPA is not set
+# CONFIG_QCOM_IPCC is not set
+# CONFIG_QCOM_LLCC is not set
+CONFIG_QCOM_MDT_LOADER=y
+# CONFIG_QCOM_MPM is not set
+CONFIG_QCOM_NET_PHYLIB=y
+# CONFIG_QCOM_OCMEM is not set
+# CONFIG_QCOM_PDC is not set
+CONFIG_QCOM_PIL_INFO=y
+# CONFIG_QCOM_PPE is not set
+# CONFIG_QCOM_Q6V5_ADSP is not set
+CONFIG_QCOM_Q6V5_COMMON=y
+# CONFIG_QCOM_Q6V5_MSS is not set
+# CONFIG_QCOM_Q6V5_PAS is not set
+CONFIG_QCOM_Q6V5_WCSS=y
+# CONFIG_QCOM_QSEECOM is not set
+# CONFIG_QCOM_RAMP_CTRL is not set
+# CONFIG_QCOM_RMTFS_MEM is not set
+# CONFIG_QCOM_RPMH is not set
+# CONFIG_QCOM_RPM_MASTER_STATS is not set
+CONFIG_QCOM_RPROC_COMMON=y
+CONFIG_QCOM_SCM=y
+# CONFIG_QCOM_SMD_RPM is not set
+CONFIG_QCOM_SMEM=y
+CONFIG_QCOM_SMEM_STATE=y
+CONFIG_QCOM_SMP2P=y
+# CONFIG_QCOM_SMSM is not set
+CONFIG_QCOM_SOCINFO=y
+# CONFIG_QCOM_SPM is not set
+# CONFIG_QCOM_STATS is not set
+# CONFIG_QCOM_SYSMON is not set
+CONFIG_QCOM_TSENS=y
+CONFIG_QCOM_TZMEM=y
+CONFIG_QCOM_TZMEM_MODE_GENERIC=y
+# CONFIG_QCOM_TZMEM_MODE_SHMBRIDGE is not set
+# CONFIG_QCOM_WCNSS_CTRL is not set
+# CONFIG_QCOM_WCNSS_PIL is not set
+CONFIG_QCOM_WDT=y
+# CONFIG_QCS_GCC_404 is not set
+# CONFIG_QCS_Q6SSTOP_404 is not set
+# CONFIG_QCS_TURING_404 is not set
+# CONFIG_QDU_ECPRICC_1000 is not set
+# CONFIG_QDU_GCC_1000 is not set
+CONFIG_QUEUED_RWLOCKS=y
+CONFIG_QUEUED_SPINLOCKS=y
+CONFIG_RANDSTRUCT_NONE=y
+CONFIG_RAS=y
+CONFIG_RATIONAL=y
+CONFIG_REGMAP=y
+CONFIG_REGMAP_I2C=y
+CONFIG_REGMAP_MMIO=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+# CONFIG_REGULATOR_VQMMC_IPQ4019 is not set
+CONFIG_RELOCATABLE=y
+CONFIG_REMOTEPROC=y
+CONFIG_REMOTEPROC_CDEV=y
+CONFIG_RESET_CONTROLLER=y
+# CONFIG_RESET_QCOM_AOSS is not set
+# CONFIG_RESET_QCOM_PDC is not set
+CONFIG_RFS_ACCEL=y
+CONFIG_RODATA_FULL_DEFAULT_ENABLED=y
+CONFIG_RPMSG=y
+CONFIG_RPMSG_CHAR=y
+# CONFIG_RPMSG_CTRL is not set
+# CONFIG_RPMSG_NS is not set
+CONFIG_RPMSG_QCOM_GLINK=y
+CONFIG_RPMSG_QCOM_GLINK_RPM=y
+CONFIG_RPMSG_QCOM_GLINK_SMEM=y
+CONFIG_RPMSG_QCOM_SMD=y
+# CONFIG_RPMSG_TTY is not set
+CONFIG_RPS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_I2C_AND_SPI=y
+CONFIG_RWSEM_SPIN_ON_OWNER=y
+# CONFIG_SA_GCC_8775P is not set
+# CONFIG_SA_GPUCC_8775P is not set
+# CONFIG_SCHED_CORE is not set
+CONFIG_SCHED_HW_PRESSURE=y
+CONFIG_SCHED_MC=y
+CONFIG_SCHED_SMT=y
+CONFIG_SCSI=y
+CONFIG_SCSI_COMMON=y
+# CONFIG_SCSI_LOWLEVEL is not set
+# CONFIG_SCSI_PROC_FS is not set
+# CONFIG_SC_CAMCC_7280 is not set
+# CONFIG_SC_CAMCC_8280XP is not set
+# CONFIG_SC_DISPCC_7180 is not set
+# CONFIG_SC_DISPCC_8280XP is not set
+# CONFIG_SC_GCC_7180 is not set
+# CONFIG_SC_GCC_8280XP is not set
+# CONFIG_SC_GPUCC_7180 is not set
+# CONFIG_SC_LPASSCC_7280 is not set
+# CONFIG_SC_LPASSCC_8280XP is not set
+# CONFIG_SC_LPASS_CORECC_7180 is not set
+# CONFIG_SC_LPASS_CORECC_7280 is not set
+# CONFIG_SC_VIDEOCC_7180 is not set
+# CONFIG_SDM_CAMCC_845 is not set
+# CONFIG_SDM_DISPCC_845 is not set
+# CONFIG_SDM_GCC_660 is not set
+# CONFIG_SDM_GCC_845 is not set
+# CONFIG_SDM_GPUCC_845 is not set
+# CONFIG_SDM_LPASSCC_845 is not set
+# CONFIG_SDM_VIDEOCC_845 is not set
+# CONFIG_SDX_GCC_75 is not set
+CONFIG_SERIAL_8250_FSL=y
+CONFIG_SERIAL_MCTRL_GPIO=y
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+CONFIG_SGL_ALLOC=y
+CONFIG_SG_POOL=y
+CONFIG_SMP=y
+# CONFIG_SM_CAMCC_4450 is not set
+# CONFIG_SM_CAMCC_6350 is not set
+# CONFIG_SM_CAMCC_7150 is not set
+# CONFIG_SM_CAMCC_8150 is not set
+# CONFIG_SM_CAMCC_8450 is not set
+# CONFIG_SM_CAMCC_8550 is not set
+# CONFIG_SM_CAMCC_8650 is not set
+# CONFIG_SM_GCC_4450 is not set
+# CONFIG_SM_GCC_7150 is not set
+# CONFIG_SM_GCC_8150 is not set
+# CONFIG_SM_GCC_8250 is not set
+# CONFIG_SM_GCC_8450 is not set
+# CONFIG_SM_GCC_8550 is not set
+# CONFIG_SM_GCC_8650 is not set
+# CONFIG_SM_GPUCC_4450 is not set
+# CONFIG_SM_GPUCC_6115 is not set
+# CONFIG_SM_GPUCC_6125 is not set
+# CONFIG_SM_GPUCC_6350 is not set
+# CONFIG_SM_GPUCC_6375 is not set
+# CONFIG_SM_GPUCC_8150 is not set
+# CONFIG_SM_GPUCC_8250 is not set
+# CONFIG_SM_GPUCC_8350 is not set
+# CONFIG_SM_GPUCC_8450 is not set
+# CONFIG_SM_GPUCC_8550 is not set
+# CONFIG_SM_GPUCC_8650 is not set
+# CONFIG_SM_TCSRCC_8550 is not set
+# CONFIG_SM_TCSRCC_8650 is not set
+# CONFIG_SM_VIDEOCC_7150 is not set
+# CONFIG_SM_VIDEOCC_8150 is not set
+# CONFIG_SM_VIDEOCC_8250 is not set
+# CONFIG_SM_VIDEOCC_8350 is not set
+# CONFIG_SM_VIDEOCC_8450 is not set
+CONFIG_SOCK_RX_QUEUE_MAPPING=y
+CONFIG_SOC_BUS=y
+CONFIG_SOFTIRQ_ON_OWN_STACK=y
+CONFIG_SPARSEMEM=y
+CONFIG_SPARSEMEM_EXTREME=y
+CONFIG_SPARSEMEM_VMEMMAP=y
+CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
+CONFIG_SPARSE_IRQ=y
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+CONFIG_SPI_MEM=y
+# CONFIG_SPI_QPIC_SNAND is not set
+CONFIG_SPI_QUP=y
+CONFIG_SPLIT_PMD_PTLOCKS=y
+CONFIG_SPLIT_PTE_PTLOCKS=y
+CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU=y
+CONFIG_SWIOTLB=y
+CONFIG_SWPHY=y
+CONFIG_SYSCTL_EXCEPTION_TRACE=y
+# CONFIG_TEST_FPU is not set
+CONFIG_THERMAL=y
+CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
+CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
+CONFIG_THERMAL_GOV_STEP_WISE=y
+CONFIG_THERMAL_HWMON=y
+CONFIG_THERMAL_OF=y
+CONFIG_THREAD_INFO_IN_TASK=y
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_TIMER_OF=y
+CONFIG_TIMER_PROBE=y
+CONFIG_TRACE_IRQFLAGS_NMI_SUPPORT=y
+CONFIG_TREE_RCU=y
+CONFIG_TREE_SRCU=y
+CONFIG_UBIFS_FS=y
+CONFIG_UBIFS_FS_ADVANCED_COMPR=y
+# CONFIG_UCLAMP_TASK is not set
+CONFIG_UNMAP_KERNEL_AT_EL0=y
+CONFIG_USB=y
+CONFIG_USB_COMMON=y
+CONFIG_USB_SUPPORT=y
+CONFIG_USER_STACKTRACE_SUPPORT=y
+CONFIG_VDSO_GETRANDOM=y
+CONFIG_VIRTIO=y
+CONFIG_VIRTIO_ANCHOR=y
+# CONFIG_VIRTIO_BLK is not set
+# CONFIG_VIRTIO_DEBUG is not set
+# CONFIG_VIRTIO_NET is not set
+CONFIG_VMAP_STACK=y
+CONFIG_WANT_DEV_COREDUMP=y
+CONFIG_WATCHDOG_CORE=y
+CONFIG_WATCHDOG_SYSFS=y
+CONFIG_XPS=y
+CONFIG_XXHASH=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZONE_DMA32=y
+CONFIG_ZSTD_COMMON=y
+CONFIG_ZSTD_COMPRESS=y
+CONFIG_ZSTD_DECOMPRESS=y
diff --git a/target/linux/qualcommbe/patches-6.18/0100-arm64-dts-qcom-ipq9574-Add-nsscc-node.patch b/target/linux/qualcommbe/patches-6.18/0100-arm64-dts-qcom-ipq9574-Add-nsscc-node.patch
new file mode 100644 (file)
index 0000000..c38a7eb
--- /dev/null
@@ -0,0 +1,54 @@
+From 52ebd52aa1906961142a2aba55d47a53b956847c Mon Sep 17 00:00:00 2001
+From: Devi Priya <quic_devipriy@quicinc.com>
+Date: Thu, 13 Mar 2025 16:33:58 +0530
+Subject: [PATCH] arm64: dts: qcom: ipq9574: Add nsscc node
+
+Add a node for the nss clock controller found on ipq9574 based devices.
+
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Signed-off-by: Devi Priya <quic_devipriy@quicinc.com>
+Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
+Link: https://lore.kernel.org/r/20250313110359.242491-6-quic_mmanikan@quicinc.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ arch/arm64/boot/dts/qcom/ipq9574.dtsi | 29 +++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq9574.dtsi
+@@ -1195,6 +1195,35 @@
+                       status = "disabled";
+               };
++              nsscc: clock-controller@39b00000 {
++                      compatible = "qcom,ipq9574-nsscc";
++                      reg = <0x39b00000 0x80000>;
++                      clocks = <&xo_board_clk>,
++                               <&cmn_pll NSS_1200MHZ_CLK>,
++                               <&cmn_pll PPE_353MHZ_CLK>,
++                               <&gcc GPLL0_OUT_AUX>,
++                               <0>,
++                               <0>,
++                               <0>,
++                               <0>,
++                               <0>,
++                               <0>,
++                               <&gcc GCC_NSSCC_CLK>;
++                      clock-names = "xo",
++                                    "nss_1200",
++                                    "ppe_353",
++                                    "gpll0_out",
++                                    "uniphy0_rx",
++                                    "uniphy0_tx",
++                                    "uniphy1_rx",
++                                    "uniphy1_tx",
++                                    "uniphy2_rx",
++                                    "uniphy2_tx",
++                                    "bus";
++                      #clock-cells = <1>;
++                      #reset-cells = <1>;
++                      #interconnect-cells = <1>;
++              };
+       };
+       thermal-zones {
diff --git a/target/linux/qualcommbe/patches-6.18/0101-arm64-dts-qcom-ipq9574-fix-the-msi-interrupt-numbers.patch b/target/linux/qualcommbe/patches-6.18/0101-arm64-dts-qcom-ipq9574-fix-the-msi-interrupt-numbers.patch
new file mode 100644 (file)
index 0000000..b09fccf
--- /dev/null
@@ -0,0 +1,44 @@
+From 2f2f5ae4d52ea882ba58f6b2fa6373a3d3db2bce Mon Sep 17 00:00:00 2001
+From: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
+Date: Thu, 13 Mar 2025 12:44:22 +0530
+Subject: [PATCH] arm64: dts: qcom: ipq9574: fix the msi interrupt numbers of
+ pcie3
+
+The MSI interrupt numbers of the PCIe3 controller are incorrect. Due
+to this, the functional bring up of the QDSP6 processor on the PCIe
+endpoint has failed. Correct the MSI interrupt numbers to properly
+bring up the QDSP6 processor on the PCIe endpoint.
+
+Fixes: d80c7fbfa908 ("arm64: dts: qcom: ipq9574: Add PCIe PHYs and controller nodes")
+Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
+Link: https://lore.kernel.org/r/20250313071422.510-1-quic_mmanikan@quicinc.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ arch/arm64/boot/dts/qcom/ipq9574.dtsi | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq9574.dtsi
+@@ -974,14 +974,14 @@
+                       ranges = <0x01000000 0x0 0x00000000 0x18200000 0x0 0x100000>,
+                                <0x02000000 0x0 0x18300000 0x18300000 0x0 0x7d00000>;
+-                      interrupts = <GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>,
+-                                   <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>,
+-                                   <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>,
+-                                   <GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>,
+-                                   <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>,
+-                                   <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>,
+-                                   <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>,
+-                                   <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>;
++                      interrupts = <GIC_SPI 221 IRQ_TYPE_LEVEL_HIGH>,
++                                   <GIC_SPI 222 IRQ_TYPE_LEVEL_HIGH>,
++                                   <GIC_SPI 225 IRQ_TYPE_LEVEL_HIGH>,
++                                   <GIC_SPI 312 IRQ_TYPE_LEVEL_HIGH>,
++                                   <GIC_SPI 326 IRQ_TYPE_LEVEL_HIGH>,
++                                   <GIC_SPI 415 IRQ_TYPE_LEVEL_HIGH>,
++                                   <GIC_SPI 494 IRQ_TYPE_LEVEL_HIGH>,
++                                   <GIC_SPI 495 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "msi0",
+                                         "msi1",
+                                         "msi2",
diff --git a/target/linux/qualcommbe/patches-6.18/0103-arm64-dts-qcom-ipq9574-Add-SPI-nand-support.patch b/target/linux/qualcommbe/patches-6.18/0103-arm64-dts-qcom-ipq9574-Add-SPI-nand-support.patch
new file mode 100644 (file)
index 0000000..7bec20d
--- /dev/null
@@ -0,0 +1,50 @@
+From 583299efa34c4a484b211f84c63aee78b6c2b469 Mon Sep 17 00:00:00 2001
+From: Md Sadre Alam <quic_mdalam@quicinc.com>
+Date: Thu, 6 Mar 2025 17:03:55 +0530
+Subject: [PATCH] arm64: dts: qcom: ipq9574: Add SPI nand support
+
+Add SPI NAND support for ipq9574 SoC.
+
+Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
+Link: https://lore.kernel.org/r/20250306113357.126602-2-quic_mdalam@quicinc.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ arch/arm64/boot/dts/qcom/ipq9574.dtsi | 27 +++++++++++++++++++++++++++
+ 1 file changed, 27 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq9574.dtsi
+@@ -675,6 +675,33 @@
+                       status = "disabled";
+               };
++              qpic_bam: dma-controller@7984000 {
++                      compatible = "qcom,bam-v1.7.4", "qcom,bam-v1.7.0";
++                      reg = <0x07984000 0x1c000>;
++                      interrupts = <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>;
++                      clocks = <&gcc GCC_QPIC_AHB_CLK>;
++                      clock-names = "bam_clk";
++                      #dma-cells = <1>;
++                      qcom,ee = <0>;
++                      status = "disabled";
++              };
++
++              qpic_nand: spi@79b0000 {
++                      compatible = "qcom,ipq9574-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>;
++                      dma-names = "tx", "rx", "cmd";
++                      status = "disabled";
++              };
++
+               usb_0_qusbphy: phy@7b000 {
+                       compatible = "qcom,ipq9574-qusb2-phy";
+                       reg = <0x0007b000 0x180>;
diff --git a/target/linux/qualcommbe/patches-6.18/0104-arm64-dts-qcom-ipq9574-Enable-SPI-NAND-for-ipq9574.patch b/target/linux/qualcommbe/patches-6.18/0104-arm64-dts-qcom-ipq9574-Enable-SPI-NAND-for-ipq9574.patch
new file mode 100644 (file)
index 0000000..393923a
--- /dev/null
@@ -0,0 +1,68 @@
+From a7c88bc81632974c0708308493aefb1f871b65fa Mon Sep 17 00:00:00 2001
+From: Md Sadre Alam <quic_mdalam@quicinc.com>
+Date: Thu, 6 Mar 2025 17:03:56 +0530
+Subject: [PATCH] arm64: dts: qcom: ipq9574: Enable SPI NAND for ipq9574
+
+Enable SPI NAND support for ipq9574 SoC.
+
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
+Link: https://lore.kernel.org/r/20250306113357.126602-3-quic_mdalam@quicinc.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ .../boot/dts/qcom/ipq9574-rdp-common.dtsi     | 44 +++++++++++++++++++
+ 1 file changed, 44 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574-rdp-common.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq9574-rdp-common.dtsi
+@@ -146,6 +146,50 @@
+               drive-strength = <8>;
+               bias-pull-up;
+       };
++
++      qpic_snand_default_state: qpic-snand-default-state {
++              clock-pins {
++                      pins = "gpio5";
++                      function = "qspi_clk";
++                      drive-strength = <8>;
++                      bias-disable;
++              };
++
++              cs-pins {
++                      pins = "gpio4";
++                      function = "qspi_cs";
++                      drive-strength = <8>;
++                      bias-disable;
++              };
++
++              data-pins {
++                      pins = "gpio0", "gpio1", "gpio2", "gpio3";
++                      function = "qspi_data";
++                      drive-strength = <8>;
++                      bias-disable;
++              };
++      };
++};
++
++&qpic_bam {
++      status = "okay";
++};
++
++&qpic_nand {
++      pinctrl-0 = <&qpic_snand_default_state>;
++      pinctrl-names = "default";
++
++      status = "okay";
++
++      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>;
++      };
+ };
+ &usb_0_dwc3 {
diff --git a/target/linux/qualcommbe/patches-6.18/0105-arm64-dts-qcom-ipq9574-Remove-eMMC-node.patch b/target/linux/qualcommbe/patches-6.18/0105-arm64-dts-qcom-ipq9574-Remove-eMMC-node.patch
new file mode 100644 (file)
index 0000000..d188539
--- /dev/null
@@ -0,0 +1,37 @@
+From 0156e327aa854be5eb9cbec9d020be1026b5b446 Mon Sep 17 00:00:00 2001
+From: Md Sadre Alam <quic_mdalam@quicinc.com>
+Date: Thu, 6 Mar 2025 17:03:57 +0530
+Subject: [PATCH] arm64: dts: qcom: ipq9574: Remove eMMC node
+
+Remove eMMC node for rdp433, since rdp433
+default boot mode is norplusnand
+
+Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
+Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
+Link: https://lore.kernel.org/r/20250306113357.126602-4-quic_mdalam@quicinc.com
+Signed-off-by: Bjorn Andersson <andersson@kernel.org>
+---
+ arch/arm64/boot/dts/qcom/ipq9574-rdp433.dts | 12 ------------
+ 1 file changed, 12 deletions(-)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574-rdp433.dts
++++ b/arch/arm64/boot/dts/qcom/ipq9574-rdp433.dts
+@@ -55,18 +55,6 @@
+       status = "okay";
+ };
+-&sdhc_1 {
+-      pinctrl-0 = <&sdc_default_state>;
+-      pinctrl-names = "default";
+-      mmc-ddr-1_8v;
+-      mmc-hs200-1_8v;
+-      mmc-hs400-1_8v;
+-      mmc-hs400-enhanced-strobe;
+-      max-frequency = <384000000>;
+-      bus-width = <8>;
+-      status = "okay";
+-};
+-
+ &tlmm {
+       pcie1_default: pcie1-default-state {
diff --git a/target/linux/qualcommbe/patches-6.18/0301-arm64-dts-qcom-Add-IPQ9574-MDIO-device-node.patch b/target/linux/qualcommbe/patches-6.18/0301-arm64-dts-qcom-Add-IPQ9574-MDIO-device-node.patch
new file mode 100644 (file)
index 0000000..c094e6e
--- /dev/null
@@ -0,0 +1,49 @@
+From 657833a74f532262d415fa2ca354b69f4a97353c Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Thu, 23 Nov 2023 15:41:20 +0800
+Subject: [PATCH] arm64: dts: qcom: Add IPQ9574 MDIO device node
+
+The MDIO bus master block is used to accessing the MDIO slave
+device (such as PHY device), the dedicated MDIO PINs needs to
+be configured.
+
+Change-Id: Ia64083529e693256dbd8f8af4071c02afdded8f9
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ arch/arm64/boot/dts/qcom/ipq9574.dtsi | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq9574.dtsi
+@@ -295,6 +295,8 @@
+               mdio: mdio@90000 {
+                       compatible =  "qcom,ipq9574-mdio", "qcom,ipq4019-mdio";
+                       reg = <0x00090000 0x64>;
++                      pinctrl-0 = <&mdio_pins>;
++                      pinctrl-names = "default";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       clocks = <&gcc GCC_MDIO_AHB_CLK>;
+@@ -414,6 +416,22 @@
+                       interrupt-controller;
+                       #interrupt-cells = <2>;
++                      mdio_pins: mdio-pins {
++                              mdc-state {
++                                      pins = "gpio38";
++                                      function = "mdc";
++                                      drive-strength = <8>;
++                                      bias-disable;
++                              };
++
++                              mdio-state {
++                                      pins = "gpio39";
++                                      function = "mdio";
++                                      drive-strength = <8>;
++                                      bias-pull-up;
++                              };
++                      };
++
+                       uart2_pins: uart2-state {
+                               pins = "gpio34", "gpio35";
+                               function = "blsp2_uart";
diff --git a/target/linux/qualcommbe/patches-6.18/0302-arm64-dts-qcom-ipq9574-Use-usb-phy-for-node-names.patch b/target/linux/qualcommbe/patches-6.18/0302-arm64-dts-qcom-ipq9574-Use-usb-phy-for-node-names.patch
new file mode 100644 (file)
index 0000000..cc71921
--- /dev/null
@@ -0,0 +1,34 @@
+From 91467ca0db1654644b2168f882f223d47dcfb9c1 Mon Sep 17 00:00:00 2001
+From: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+Date: Sat, 30 Mar 2024 20:03:30 -0500
+Subject: [PATCH] arm64: dts: qcom: ipq9574: Use 'usb-phy' for node names
+
+The devicetree spec allows node names of "usb-phy". So be more
+specific for the USB PHYs, and name the nodes "usb-phy" instead of
+just "phy".
+
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ arch/arm64/boot/dts/qcom/ipq9574.dtsi | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq9574.dtsi
+@@ -720,7 +720,7 @@
+                       status = "disabled";
+               };
+-              usb_0_qusbphy: phy@7b000 {
++              usb_0_qusbphy: usb-phy@7b000 {
+                       compatible = "qcom,ipq9574-qusb2-phy";
+                       reg = <0x0007b000 0x180>;
+                       #phy-cells = <0>;
+@@ -734,7 +734,7 @@
+                       status = "disabled";
+               };
+-              usb_0_qmpphy: phy@7d000 {
++              usb_0_qmpphy: usb-phy@7d000 {
+                       compatible = "qcom,ipq9574-qmp-usb3-phy";
+                       reg = <0x0007d000 0xa00>;
+                       #phy-cells = <0>;
diff --git a/target/linux/qualcommbe/patches-6.18/0304-arm64-dts-qcom-ipq9574-add-QPIC-SPI-NAND-default-par.patch b/target/linux/qualcommbe/patches-6.18/0304-arm64-dts-qcom-ipq9574-add-QPIC-SPI-NAND-default-par.patch
new file mode 100644 (file)
index 0000000..e0b84b1
--- /dev/null
@@ -0,0 +1,50 @@
+From be44d0251a2540f3b8d7205e0bc6659704366711 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 30 Jan 2025 00:39:30 +0100
+Subject: [PATCH] arm64: dts: qcom: ipq9574: add QPIC SPI NAND default
+ partition nodes
+
+Add QPIC SPI NAND default partition nodes for RDP reference board.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ .../boot/dts/qcom/ipq9574-rdp-common.dtsi     | 28 +++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574-rdp-common.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq9574-rdp-common.dtsi
+@@ -189,6 +189,34 @@
+               nand-ecc-engine = <&qpic_nand>;
+               nand-ecc-strength = <4>;
+               nand-ecc-step-size = <512>;
++
++              partitions {
++                      compatible = "fixed-partitions";
++                      #address-cells = <1>;
++                      #size-cells = <1>;
++
++                      partition@0 {
++                              label = "0:training";
++                              reg = <0x0 0x80000>;
++                              read-only;
++                      };
++
++                      partition@80000 {
++                              label = "0:license";
++                              reg = <0x80000 0x40000>;
++                              read-only;
++                      };
++
++                      partition@c0000 {
++                              label = "rootfs";
++                              reg = <0xc0000 0x3c00000>;
++                      };
++
++                      partition@3cc0000 {
++                              label = "rootfs_1";
++                              reg = <0x3cc0000 0x3c00000>;
++                      };
++              };
+       };
+ };
diff --git a/target/linux/qualcommbe/patches-6.18/0305-arm64-dts-qcom-add-partition-table-for-ipq9574-rdp-c.patch b/target/linux/qualcommbe/patches-6.18/0305-arm64-dts-qcom-add-partition-table-for-ipq9574-rdp-c.patch
new file mode 100644 (file)
index 0000000..04314f5
--- /dev/null
@@ -0,0 +1,174 @@
+From 47c7ae9715d76054d98e8407dbb8ca1cf42fd587 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 9 Dec 2024 17:50:31 +0100
+Subject: [PATCH] arm64: dts: qcom: add partition table for ipq9574 rdp common
+
+Add partition table for ipq9574 SoC common to every RDB board.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ .../boot/dts/qcom/ipq9574-rdp-common.dtsi     | 146 +++++++++++++++++-
+ 1 file changed, 145 insertions(+), 1 deletion(-)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574-rdp-common.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq9574-rdp-common.dtsi
+@@ -74,11 +74,158 @@
+       status = "okay";
+       flash@0 {
+-              compatible = "micron,n25q128a11", "jedec,spi-nor";
++              compatible = "jedec,spi-nor";
+               reg = <0>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+               spi-max-frequency = <50000000>;
++
++              partitions {
++                      compatible = "fixed-partitions";
++                      #address-cells = <1>;
++                      #size-cells = <1>;
++
++                      partition@0 {
++                              label = "0:sbl1";
++                              reg = <0x0 0xc0000>;
++                              read-only;
++                      };
++
++                      partition@c0000 {
++                              label = "0:mibib";
++                              reg = <0xc0000 0x10000>;
++                              read-only;
++                      };
++
++                      partition@d0000 {
++                              label = "0:bootconfig";
++                              reg = <0xd0000 0x20000>;
++                              read-only;
++                      };
++
++                      partition@f0000 {
++                              label = "0:bootconfig1";
++                              reg = <0xf0000 0x20000>;
++                              read-only;
++                      };
++
++                      partition@110000 {
++                              label = "0:qsee";
++                              reg = <0x110000 0x180000>;
++                              read-only;
++                      };
++
++                      partition@290000 {
++                              label = "0:qsee_1";
++                              reg = <0x290000 0x180000>;
++                              read-only;
++                      };
++
++                      partition@410000 {
++                              label = "0:devcfg";
++                              reg = <0x410000 0x10000>;
++                              read-only;
++                      };
++
++                      partition@420000 {
++                              label = "0:devcfg_1";
++                              reg = <0x420000 0x10000>;
++                              read-only;
++                      };
++
++                      partition@430000 {
++                              label = "0:apdp";
++                              reg = <0x430000 0x10000>;
++                              read-only;
++                      };
++
++                      partition@440000 {
++                              label = "0:apdp_1";
++                              reg = <0x440000 0x10000>;
++                              read-only;
++                      };
++
++                      partition@450000 {
++                              label = "0:tme";
++                              reg = <0x450000 0x40000>;
++                              read-only;
++                      };
++
++                      partition@490000 {
++                              label = "0:tme_1";
++                              reg = <0x490000 0x40000>;
++                              read-only;
++                      };
++
++                      partition@4d0000 {
++                              label = "0:rpm";
++                              reg = <0x4d0000 0x20000>;
++                              read-only;
++                      };
++
++                      partition@4f0000 {
++                              label = "0:rpm_1";
++                              reg = <0x4f0000 0x20000>;
++                              read-only;
++                      };
++
++                      partition@510000 {
++                              label = "0:cdt";
++                              reg = <0x510000 0x10000>;
++                              read-only;
++                      };
++
++                      partition@520000 {
++                              label = "0:cdt_1";
++                              reg = <0x520000 0x10000>;
++                              read-only;
++                      };
++
++                      partition@530000 {
++                              label = "0:appsblenv";
++                              reg = <0x530000 0x10000>;
++
++                              nvmem-layout {
++                                      compatible = "u-boot,env";
++
++                                      macaddr_lan: ethaddr {
++                                              #nvmem-cell-cells = <1>;
++                                      };
++                              };
++                      };
++
++                      partition@540000 {
++                              label = "0:appsbl";
++                              reg = <0x540000 0xa0000>;
++                              read-only;
++                      };
++
++                      partition@5e0000 {
++                              label = "0:appsbl_1";
++                              reg = <0x5e0000 0xa0000>;
++                              read-only;
++                      };
++
++                      partition@680000 {
++                              label = "0:art";
++                              reg = <0x680000 0x100000>;
++                              read-only;
++                      };
++
++                      partition@780000 {
++                              label = "0:ethphyfw";
++                              reg = <0x780000 0x80000>;
++                              read-only;
++
++                              nvmem-layout {
++                                      compatible = "fixed-layout";
++
++                                      aqr_fw: aqr-fw@0 {
++                                              reg = <0x0 0x5fc02>;
++                                      };
++                              };
++                      };
++              };
+       };
+ };
diff --git a/target/linux/qualcommbe/patches-6.18/0306-dt-bindings-net-Document-Qualcomm-QCA8084-PHY-packag.patch b/target/linux/qualcommbe/patches-6.18/0306-dt-bindings-net-Document-Qualcomm-QCA8084-PHY-packag.patch
new file mode 100644 (file)
index 0000000..a9223c5
--- /dev/null
@@ -0,0 +1,536 @@
+From 7b1c4e22532ded6b20ee41936fa38b5ca1e61ff9 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Mon, 29 Jan 2024 17:57:20 +0800
+Subject: [PATCH] dt-bindings: net: Document Qualcomm QCA8084 PHY package
+
+QCA8084 is quad PHY chip, which integrates 4 PHYs, 2 PCS
+interfaces (PCS0 and PCS1) and clock controller, which can
+also be integrated to the switch chip named as QCA8386.
+
+1. MDIO address of 4 PHYs, 2 PCS and 1 XPCS (PCS1 includes
+   PCS and XPCS, PCS0 includes PCS) can be configured.
+2. The package mode of PHY is optionally configured for the
+   interface mode of two PCSes working correctly.
+3. The package level clock and reset need to be initialized.
+4. The clock and reset per PHY device need to be initialized
+   so that the PHY register can be accessed.
+
+Change-Id: Idb2338d2673152cbd3c57e95968faa59e9d4a80f
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Alex G: Update to match the patches that will be upstream.
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ .../devicetree/bindings/net/qcom,qca8084.yaml | 488 ++++++++++++++++++
+ include/dt-bindings/net/qcom,qca808x.h        |  14 +
+ 2 files changed, 502 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/net/qcom,qca8084.yaml
+ create mode 100644 include/dt-bindings/net/qcom,qca808x.h
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/net/qcom,qca8084.yaml
+@@ -0,0 +1,488 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/net/qcom,qca8084.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Qualcomm QCA8084 Ethernet Quad PHY
++
++maintainers:
++  - Luo Jie <quic_luoj@quicinc.com>
++
++description:
++  Qualcomm QCA8084 is PHY package of four-port Ethernet transceiver,
++  the Ethernet port supports link speed 10/100/1000/2500 Mbps.
++  There are two PCSes (PCS0 and PCS1) integrated in the PHY
++  package, PCS1 includes XPCS and PCS to support the interface
++  mode 10G-QXGMII and SGMII, PCS0 includes a PCS to support the
++  interface mode SGMII only. There is also a clock controller
++  integrated in the PHY package. This four-port Ethernet
++  transceiver can also be integrated to the switch chip named
++  as QCA8386. The PHY package mode needs to be configured as the
++  correct value to apply the interface mode of two PCSes as
++  mentioned below.
++
++  QCA8084 expects an input reference clock 50 MHZ as the clock
++  source of the integrated clock controller, the integrated
++  clock controller supplies the clocks and resets to the
++  integrated PHY, PCS and PHY package.
++
++  - |
++                 +--| |--+-------------------+--| |--+
++                 | PCS1  |<------------+---->| PCS0  |
++                 +-------+             |     +-------+
++                 |                     |             |
++    Ref 50M clk  +--------+            |             |
++    ------------>|        | clk & rst  |             |
++    GPIO Reset   |QCA8K-CC+------------+             |
++    ------------>|        |            |             |
++                 +--------+            |             |
++                 |                     V             |
++                 +--------+--------+--------+--------+
++                 |  PHY0  |  PHY1  |  PHY2  |  PHY3  |
++                 +--------+--------+--------+--------+
++
++properties:
++  compatible:
++    const: qcom,qca8084-package
++
++  clocks:
++    description:
++      PHY package level initial common clocks, which are needed to
++      be enabled after GPIO reset on the PHY package, these clocks
++      are supplied from the PHY integrated clock controller (QCA8K-CC).
++    items:
++      - description: APB bridge clock
++      - description: AHB clock
++      - description: Security control clock
++      - description: TLMM clock
++      - description: TLMM AHB clock
++      - description: CNOC AHB clock
++      - description: MDIO AHB clock
++
++  clock-names:
++    items:
++      - const: apb_bridge
++      - const: ahb
++      - const: sec_ctrl_ahb
++      - const: tlmm
++      - const: tlmm_ahb
++      - const: cnoc_ahb
++      - const: mdio_ahb
++
++  resets:
++    description:
++      PHY package level initial common reset, which are needed to
++      be deasserted after GPIO reset on the PHY package, this reset
++      is provided by the PHY integrated clock controller to do PHY
++      DSP reset.
++    maxItems: 1
++
++  qcom,package-mode:
++    description: |
++      The package mode of PHY supports to be configured as 3 modes
++      to apply the combinations of interface mode of two PCSes
++      correctly. This value should use one of the values defined in
++      dt-bindings/net/qcom,qca808x.h. The package mode 10G-QXGMII of
++      Quad PHY is used by default.
++
++      package mode             PCS1             PCS0
++      phy mode (0)             10G-QXGMII for   not used
++                               PHY0-PHY3
++
++      switch mode (1)          SGMII for        SGMII for
++                               switch MAC0      switch MAC5 (optional)
++
++      switch bypass MAC5 (2)   SGMII for        SGMII for
++                               switch MAC0      PHY3
++    $ref: /schemas/types.yaml#/definitions/uint32
++    enum: [0, 1, 2]
++    default: 0
++
++  qcom,phy-addr-fixup:
++    description:
++      MDIO address for PHY0-PHY3, PCS0 and PCS1 including PCS and XPCS,
++      which can be optionally customized by programming the security
++      control register of PHY package. The hardware default MDIO address
++      of PHY0-PHY3, PCS0 and PCS1 including PCS and XPCS is 0-6.
++    $ref: /schemas/types.yaml#/definitions/uint32-array
++    minItems: 7
++    maxItems: 7
++
++patternProperties:
++  ^ethernet-phy@[a-f0-9]+$:
++    unevaluatedProperties: false
++    $ref: ethernet-phy.yaml#
++
++    properties:
++      compatible:
++        const: ethernet-phy-id004d.d180
++
++      qcom,xpcs-channel:
++        description:
++          When PCS1 works on the interface mode 10G-QXGMII, the integrated
++          XPCS including 4 channels is used to connected with the Quad PHYs,
++          each PHY needs to be specified the XPCS channel ID to deliver the
++          PHY link status to the XPCS.
++        $ref: /schemas/types.yaml#/definitions/uint32
++        enum: [0, 1, 2, 3]
++
++    required:
++      - compatible
++      - reg
++      - clocks
++      - resets
++
++  ^pcs-phy@[a-f0-9]+$:
++    type: object
++    additionalProperties: false
++    description:
++      PCS device has the independent MDIO address, which controls
++      the interface mode used and provides the clocks such as
++      312.5 MHZ as RX and TX root clocks to the integrated clock
++      controller.
++    properties:
++      compatible:
++        const: qcom,qca8k-pcs-phy
++
++      reg:
++        items:
++          - description: PCS MDIO address.
++
++      clocks:
++        items:
++          - description: PCS clock.
++          - description: PCS RX root clock.
++          - description: PCS TX root clock.
++
++      clock-names:
++        items:
++          - const: pcs
++          - const: pcs_rx_root
++          - const: pcs_tx_root
++
++      resets:
++        items:
++          - description: PCS reset.
++
++    required:
++      - compatible
++      - reg
++      - clocks
++      - resets
++
++  ^xpcs-phy@[a-f0-9]+$:
++    type: object
++    additionalProperties: false
++    description:
++      XPCS device has the independent MDIO address, which includes 4
++      channels to connect with Quad PHYs.
++    properties:
++      compatible:
++        const: qcom,qca8k-xpcs-phy
++
++      reg:
++        items:
++          - description: XPCS MDIO address.
++
++      resets:
++        items:
++          - description: XPCS reset.
++
++      '#address-cells':
++        const: 1
++
++      '#size-cells':
++        const: 0
++
++    required:
++      - compatible
++      - reg
++      - resets
++      - '#address-cells'
++      - '#size-cells'
++
++    patternProperties:
++      "^channel@[0-3]+$":
++        type: object
++        additionalProperties: false
++        description:
++          XPCS is used to support 10G-QXGMII mode, which inlcudes 4 channels
++          to be connected with Quad PHYs, each channels has the dedicated
++          clocks and resets from the integrated clock controller of QCA8084.
++
++        properties:
++          reg:
++            items:
++              - description: XPCS channel ID
++
++          clocks:
++            items:
++              - description: XPCS XGMII RX clock
++              - description: XPCS XGMII TX clock
++              - description: XPCS RX clock
++              - description: XPCS TX clock
++              - description: Port RX clock
++              - description: Port TX clock
++              - description: RX source clock
++              - description: TX source clock
++
++          clock-names:
++            items:
++              - const: xgmii_rx
++              - const: xgmii_tx
++              - const: xpcs_rx
++              - const: xpcs_tx
++              - const: port_rx
++              - const: port_tx
++              - const: rx_src
++              - const: tx_src
++
++          resets:
++            items:
++              - description: XPCS XGMII RX reset
++              - description: XPCS XGMII TX reset
++              - description: XPCS RX reset
++              - description: XPCS TX reset
++              - description: Port RX reset
++              - description: Port TX reset
++
++          reset-names:
++            items:
++              - const: xgmii_rx
++              - const: xgmii_tx
++              - const: xpcs_rx
++              - const: xpcs_tx
++              - const: port_rx
++              - const: port_tx
++
++        required:
++          - reg
++          - clocks
++          - clock-names
++          - resets
++          - reset-names
++
++required:
++  - compatible
++  - clocks
++  - clock-names
++  - resets
++
++allOf:
++  - $ref: ethernet-phy-package.yaml#
++
++unevaluatedProperties: false
++
++examples:
++  - |
++    #include <dt-bindings/clock/qcom,qca8k-nsscc.h>
++    #include <dt-bindings/net/qcom,qca808x.h>
++    #include <dt-bindings/reset/qcom,qca8k-nsscc.h>
++
++    mdio {
++        #address-cells = <1>;
++        #size-cells = <0>;
++
++        ethernet-phy-package@1 {
++            #address-cells = <1>;
++            #size-cells = <0>;
++            compatible = "qcom,qca8084-package";
++            reg = <1>;
++            clocks = <&qca8k_nsscc NSS_CC_APB_BRIDGE_CLK>,
++                     <&qca8k_nsscc NSS_CC_AHB_CLK>,
++                     <&qca8k_nsscc NSS_CC_SEC_CTRL_AHB_CLK>,
++                     <&qca8k_nsscc NSS_CC_TLMM_CLK>,
++                     <&qca8k_nsscc NSS_CC_TLMM_AHB_CLK>,
++                     <&qca8k_nsscc NSS_CC_CNOC_AHB_CLK>,
++                     <&qca8k_nsscc NSS_CC_MDIO_AHB_CLK>;
++            clock-names = "apb_bridge",
++                          "ahb",
++                          "sec_ctrl_ahb",
++                          "tlmm",
++                          "tlmm_ahb",
++                          "cnoc_ahb",
++                          "mdio_ahb";
++            resets = <&qca8k_nsscc NSS_CC_GEPHY_FULL_ARES>;
++            qcom,package-mode = <QCA808X_PCS1_10G_QXGMII_PCS0_UNUNSED>;
++            qcom,phy-addr-fixup = <1 2 3 4 5 6 7>;
++
++            ethernet-phy@1 {
++                compatible = "ethernet-phy-id004d.d180";
++                reg = <1>;
++                clocks = <&qca8k_nsscc NSS_CC_GEPHY0_SYS_CLK>;
++                resets = <&qca8k_nsscc NSS_CC_GEPHY0_SYS_ARES>;
++                qcom,xpcs-channel = <0>;
++            };
++
++            ethernet-phy@2 {
++                compatible = "ethernet-phy-id004d.d180";
++                reg = <2>;
++                clocks = <&qca8k_nsscc NSS_CC_GEPHY1_SYS_CLK>;
++                resets = <&qca8k_nsscc NSS_CC_GEPHY1_SYS_ARES>;
++                qcom,xpcs-channel = <1>;
++            };
++
++            ethernet-phy@3 {
++                compatible = "ethernet-phy-id004d.d180";
++                reg = <3>;
++                clocks = <&qca8k_nsscc NSS_CC_GEPHY2_SYS_CLK>;
++                resets = <&qca8k_nsscc NSS_CC_GEPHY2_SYS_ARES>;
++                qcom,xpcs-channel = <2>;
++            };
++
++            ethernet-phy@4 {
++                compatible = "ethernet-phy-id004d.d180";
++                reg = <4>;
++                clocks = <&qca8k_nsscc NSS_CC_GEPHY3_SYS_CLK>;
++                resets = <&qca8k_nsscc NSS_CC_GEPHY3_SYS_ARES>;
++                qcom,xpcs-channel = <3>;
++            };
++
++            pcs-phy@6 {
++                compatible = "qcom,qca8k-pcs-phy";
++                reg = <6>;
++                clocks = <&qca8k_nsscc NSS_CC_SRDS1_SYS_CLK>,
++                         <&qca8k_uniphy1_tx312p5m>,
++                         <&qca8k_uniphy1_rx312p5m>;
++                clock-names = "pcs", "pcs_rx_root", "pcs_tx_root";
++                resets = <&qca8k_nsscc NSS_CC_SRDS1_SYS_ARES>;
++            };
++
++            xpcs-phy@7 {
++                compatible = "qcom,qca8k-xpcs-phy";
++                reg = <7>;
++                #address-cells = <1>;
++                #size-cells = <0>;
++                resets = <&qca8k_nsscc NSS_CC_XPCS_ARES>;
++
++                channel@0 {
++                    reg = <0>;
++                    clocks = <&qca8k_nsscc NSS_CC_MAC1_SRDS1_CH0_XGMII_TX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC1_SRDS1_CH0_XGMII_RX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC1_SRDS1_CH0_TX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC1_SRDS1_CH0_RX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC1_GEPHY0_RX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC1_GEPHY0_TX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC1_RX_CLK_SRC>,
++                             <&qca8k_nsscc NSS_CC_MAC1_TX_CLK_SRC>;
++                    clock-names = "xgmii_rx",
++                                  "xgmii_tx",
++                                  "xpcs_rx",
++                                  "xpcs_tx",
++                                  "port_rx",
++                                  "port_tx",
++                                  "rx_src",
++                                  "tx_src";
++                    resets = <&qca8k_nsscc NSS_CC_MAC1_SRDS1_CH0_XGMII_TX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC1_SRDS1_CH0_XGMII_RX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC1_SRDS1_CH0_TX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC1_SRDS1_CH0_RX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC1_GEPHY0_RX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC1_GEPHY0_TX_ARES>;
++                    reset-names = "xgmii_rx",
++                                  "xgmii_tx",
++                                  "xpcs_rx",
++                                  "xpcs_tx",
++                                  "port_rx",
++                                  "port_tx";
++                };
++
++                channel@1 {
++                    reg = <1>;
++                    clocks = <&qca8k_nsscc NSS_CC_MAC2_SRDS1_CH1_XGMII_TX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC2_SRDS1_CH1_XGMII_RX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC2_SRDS1_CH1_TX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC2_SRDS1_CH1_RX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC2_GEPHY1_RX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC2_GEPHY1_TX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC2_RX_CLK_SRC>,
++                             <&qca8k_nsscc NSS_CC_MAC2_TX_CLK_SRC>;
++                    clock-names = "xgmii_rx",
++                                  "xgmii_tx",
++                                  "xpcs_rx",
++                                  "xpcs_tx",
++                                  "port_rx",
++                                  "port_tx",
++                                  "rx_src",
++                                  "tx_src";
++                    resets = <&qca8k_nsscc NSS_CC_MAC2_SRDS1_CH1_XGMII_TX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC2_SRDS1_CH1_XGMII_RX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC2_SRDS1_CH1_TX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC2_SRDS1_CH1_RX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC2_GEPHY1_RX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC2_GEPHY1_TX_ARES>;
++                    reset-names = "xgmii_rx",
++                                  "xgmii_tx",
++                                  "xpcs_rx",
++                                  "xpcs_tx",
++                                  "port_rx",
++                                  "port_tx";
++                };
++
++                channel@2 {
++                    reg = <2>;
++                    clocks = <&qca8k_nsscc NSS_CC_MAC3_SRDS1_CH2_XGMII_TX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC3_SRDS1_CH2_XGMII_RX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC3_SRDS1_CH2_TX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC3_SRDS1_CH2_RX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC3_GEPHY2_RX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC3_GEPHY2_TX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC3_RX_CLK_SRC>,
++                             <&qca8k_nsscc NSS_CC_MAC3_TX_CLK_SRC>;
++                    clock-names = "xgmii_rx",
++                                  "xgmii_tx",
++                                  "xpcs_rx",
++                                  "xpcs_tx",
++                                  "port_rx",
++                                  "port_tx",
++                                  "rx_src",
++                                  "tx_src";
++                    resets = <&qca8k_nsscc NSS_CC_MAC3_SRDS1_CH2_XGMII_TX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC3_SRDS1_CH2_XGMII_RX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC3_SRDS1_CH2_TX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC3_SRDS1_CH2_RX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC3_GEPHY2_RX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC3_GEPHY2_TX_ARES>;
++                    reset-names = "xgmii_rx",
++                                  "xgmii_tx",
++                                  "xpcs_rx",
++                                  "xpcs_tx",
++                                  "port_rx",
++                                  "port_tx";
++                };
++
++                channel@3 {
++                    reg = <3>;
++                    clocks = <&qca8k_nsscc NSS_CC_MAC4_SRDS1_CH3_XGMII_TX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC4_SRDS1_CH3_XGMII_RX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC4_SRDS1_CH3_TX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC4_SRDS1_CH3_RX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC4_GEPHY3_RX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC4_GEPHY3_TX_CLK>,
++                             <&qca8k_nsscc NSS_CC_MAC4_RX_CLK_SRC>,
++                             <&qca8k_nsscc NSS_CC_MAC4_TX_CLK_SRC>;
++                    clock-names = "xgmii_rx",
++                                  "xgmii_tx",
++                                  "xpcs_rx",
++                                  "xpcs_tx",
++                                  "port_rx",
++                                  "port_tx",
++                                  "rx_src",
++                                  "tx_src";
++                    resets = <&qca8k_nsscc NSS_CC_MAC4_SRDS1_CH3_XGMII_TX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC4_SRDS1_CH3_XGMII_RX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC4_SRDS1_CH3_TX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC4_SRDS1_CH3_RX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC4_GEPHY3_RX_ARES>,
++                             <&qca8k_nsscc NSS_CC_MAC4_GEPHY3_TX_ARES>;
++                    reset-names = "xgmii_rx",
++                                  "xgmii_tx",
++                                  "xpcs_rx",
++                                  "xpcs_tx",
++                                  "port_rx",
++                                  "port_tx";
++                };
++            };
++        };
++    };
+--- /dev/null
++++ b/include/dt-bindings/net/qcom,qca808x.h
+@@ -0,0 +1,14 @@
++/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
++/*
++ * Device Tree constants for the Qualcomm QCA808X PHYs
++ */
++
++#ifndef _DT_BINDINGS_QCOM_QCA808X_H
++#define _DT_BINDINGS_QCOM_QCA808X_H
++
++/* PHY package modes of QCA8084 to apply the interface modes of two PCSes. */
++#define QCA808X_PCS1_10G_QXGMII_PCS0_UNUNSED          0
++#define QCA808X_PCS1_SGMII_MAC_PCS0_SGMII_MAC         1
++#define QCA808X_PCS1_SGMII_MAC_PCS0_SGMII_PHY         2
++
++#endif
diff --git a/target/linux/qualcommbe/patches-6.18/0307-net-phy-qca808x-Add-QCA8084-ethernet-phy-support.patch b/target/linux/qualcommbe/patches-6.18/0307-net-phy-qca808x-Add-QCA8084-ethernet-phy-support.patch
new file mode 100644 (file)
index 0000000..360517f
--- /dev/null
@@ -0,0 +1,144 @@
+From 60c44842f9611be237ab3f68afe8ebf2d9595fb2 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Thu, 6 Apr 2023 18:09:07 +0800
+Subject: [PATCH] net: phy: qca808x: Add QCA8084 ethernet phy support
+
+Add QCA8084 Quad-PHY support, which is a four-port PHY with
+maximum link capability of 2.5 Gbps. The features of each port
+are almost same as QCA8081. The slave seed and fast retrain
+configs are not needed for QCA8084. It includes two PCSes.
+
+PCS0 of QCA8084 supports the interface modes:
+PHY_INTERFACE_MODE_2500BASEX and PHY_INTERFACE_MODE_SGMII.
+
+PCS1 of QCA8084 supports the interface modes:
+PHY_INTERFACE_MODE_10G_QXGMII, PHY_INTERFACE_MODE_2500BASEX and
+PHY_INTERFACE_MODE_SGMII.
+
+The additional CDT configurations needed for QCA8084 compared
+with QCA8081.
+
+Change-Id: I12555fa70662682474ab4432204405b5e752fef6
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Alex G: Update to match the patches that will be upstream.
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/phy/qcom/qca808x.c | 65 ++++++++++++++++++++++++++++++++--
+ 1 file changed, 63 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -86,9 +86,16 @@
+ #define QCA8081_PHY_FIFO_RSTN                 BIT(11)
+ #define QCA8081_PHY_ID                                0x004dd101
++#define QCA8084_PHY_ID                                0x004dd180
++
++#define QCA8084_MMD3_CDT_PULSE_CTRL           0x8075
++#define QCA8084_CDT_PULSE_THRESH_VAL          0xa060
++
++#define QCA8084_MMD3_CDT_NEAR_CTRL            0x807f
++#define QCA8084_CDT_NEAR_BYPASS                       BIT(15)
+ MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver");
+-MODULE_AUTHOR("Matus Ujhelyi");
++MODULE_AUTHOR("Matus Ujhelyi, Luo Jie");
+ MODULE_LICENSE("GPL");
+ struct qca808x_priv {
+@@ -153,13 +160,18 @@ static bool qca808x_is_prefer_master(str
+ static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
+ {
+-      return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
++      return phydev_id_compare(phydev, QCA8081_PHY_ID) &&
++              linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
++                                phydev->supported);
+ }
+ static bool qca808x_is_1g_only(struct phy_device *phydev)
+ {
+       int ret;
++      if (!phydev_id_compare(phydev, QCA8081_PHY_ID))
++              return false;
++
+       ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
+       if (ret < 0)
+               return true;
+@@ -273,6 +285,23 @@ static int qca808x_read_status(struct ph
+               return ret;
+       if (phydev->link) {
++              /* There are two PCSes available for QCA8084, which support
++               * the following interface modes.
++               *
++               * 1. PHY_INTERFACE_MODE_10G_QXGMII utilizes PCS1 for all
++               * available 4 ports, which is for all link speeds.
++               *
++               * 2. PHY_INTERFACE_MODE_2500BASEX utilizes PCS0 for the
++               * fourth port, which is only for the link speed 2500M same
++               * as QCA8081.
++               *
++               * 3. PHY_INTERFACE_MODE_SGMII utilizes PCS0 for the fourth
++               * port, which is for the link speed 10M, 100M and 1000M same
++               * as QCA8081.
++               */
++              if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII)
++                      return 0;
++
+               if (phydev->speed == SPEED_2500)
+                       phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
+               else
+@@ -352,6 +381,18 @@ static int qca808x_cable_test_start(stru
+       phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060);
+       phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060);
++      if (phydev_id_compare(phydev, QCA8084_PHY_ID)) {
++              /* Adjust the positive and negative pulse thereshold of CDT. */
++              phy_write_mmd(phydev, MDIO_MMD_PCS,
++                            QCA8084_MMD3_CDT_PULSE_CTRL,
++                            QCA8084_CDT_PULSE_THRESH_VAL);
++
++              /* Disable the near bypass of CDT. */
++              phy_modify_mmd(phydev, MDIO_MMD_PCS,
++                             QCA8084_MMD3_CDT_NEAR_CTRL,
++                             QCA8084_CDT_NEAR_BYPASS, 0);
++      }
++
+       return 0;
+ }
+@@ -651,12 +692,32 @@ static struct phy_driver qca808x_driver[
+       .led_hw_control_set     = qca808x_led_hw_control_set,
+       .led_hw_control_get     = qca808x_led_hw_control_get,
+       .led_polarity_set       = qca808x_led_polarity_set,
++}, {
++      /* Qualcomm QCA8084 */
++      PHY_ID_MATCH_MODEL(QCA8084_PHY_ID),
++      .name                   = "Qualcomm QCA8084",
++      .flags                  = PHY_POLL_CABLE_TEST,
++      .config_intr            = at803x_config_intr,
++      .handle_interrupt       = at803x_handle_interrupt,
++      .get_tunable            = at803x_get_tunable,
++      .set_tunable            = at803x_set_tunable,
++      .set_wol                = at803x_set_wol,
++      .get_wol                = at803x_get_wol,
++      .get_features           = qca808x_get_features,
++      .config_aneg            = qca808x_config_aneg,
++      .suspend                = genphy_suspend,
++      .resume                 = genphy_resume,
++      .read_status            = qca808x_read_status,
++      .soft_reset             = qca808x_soft_reset,
++      .cable_test_start       = qca808x_cable_test_start,
++      .cable_test_get_status  = qca808x_cable_test_get_status,
+ }, };
+ module_phy_driver(qca808x_driver);
+ static const struct mdio_device_id __maybe_unused qca808x_tbl[] = {
+       { PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) },
++      { PHY_ID_MATCH_MODEL(QCA8084_PHY_ID) },
+       { }
+ };
diff --git a/target/linux/qualcommbe/patches-6.18/0308-net-phy-qca808x-Add-config_init-function-for-QCA8084.patch b/target/linux/qualcommbe/patches-6.18/0308-net-phy-qca808x-Add-config_init-function-for-QCA8084.patch
new file mode 100644 (file)
index 0000000..8ac94c8
--- /dev/null
@@ -0,0 +1,85 @@
+From c052b9a4ab869cc54976402b3f9dbdef5bdb9f27 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Wed, 8 Nov 2023 16:18:02 +0800
+Subject: [PATCH] net: phy: qca808x: Add config_init function for QCA8084
+
+1. The ADC of QCA8084 PHY must be configured as edge inverted
+and falling whenever it is initialized or reset. In addition,
+the default MSE (Mean square error) threshold value is adjusted,
+which comes into play during link partner detection to detect
+the valid link signal.
+
+2. Add the possible interface modes.
+   When QCA8084 works on the interface mode SGMII or 2500BASE-X, the
+   interface mode can be switched according to the PHY link speed.
+
+   When QCA8084 works on the 10G-QXGMII mode, which will be the only
+   possible interface mode.
+
+Change-Id: I832c0d0b069e95cc411a8a7b680a5f60e1d6041a
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ drivers/net/phy/qcom/qca808x.c | 38 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 38 insertions(+)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -94,6 +94,15 @@
+ #define QCA8084_MMD3_CDT_NEAR_CTRL            0x807f
+ #define QCA8084_CDT_NEAR_BYPASS                       BIT(15)
++/* QCA8084 ADC clock edge */
++#define QCA8084_ADC_CLK_SEL                   0x8b80
++#define QCA8084_ADC_CLK_SEL_ACLK              GENMASK(7, 4)
++#define QCA8084_ADC_CLK_SEL_ACLK_FALL         0xf
++#define QCA8084_ADC_CLK_SEL_ACLK_RISE         0x0
++
++#define QCA8084_MSE_THRESHOLD                 0x800a
++#define QCA8084_MSE_THRESHOLD_2P5G_VAL                0x51c6
++
+ MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi, Luo Jie");
+ MODULE_LICENSE("GPL");
+@@ -663,6 +672,34 @@ static int qca808x_led_polarity_set(stru
+                             active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
+ }
++static int qca8084_config_init(struct phy_device *phydev)
++{
++      int ret;
++
++      if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII)
++              __set_bit(PHY_INTERFACE_MODE_10G_QXGMII,
++                        phydev->possible_interfaces);
++      else
++              qca808x_fill_possible_interfaces(phydev);
++
++      /* Configure the ADC to convert the signal using falling edge
++       * instead of the default rising edge.
++       */
++      ret = at803x_debug_reg_mask(phydev, QCA8084_ADC_CLK_SEL,
++                                  QCA8084_ADC_CLK_SEL_ACLK,
++                                  FIELD_PREP(QCA8084_ADC_CLK_SEL_ACLK,
++                                             QCA8084_ADC_CLK_SEL_ACLK_FALL));
++      if (ret < 0)
++              return ret;
++
++      /* Adjust MSE threshold value to avoid link issue with
++       * some link partner.
++       */
++      return phy_write_mmd(phydev, MDIO_MMD_PMAPMD,
++                           QCA8084_MSE_THRESHOLD,
++                           QCA8084_MSE_THRESHOLD_2P5G_VAL);
++}
++
+ static struct phy_driver qca808x_driver[] = {
+ {
+       /* Qualcomm QCA8081 */
+@@ -711,6 +748,7 @@ static struct phy_driver qca808x_driver[
+       .soft_reset             = qca808x_soft_reset,
+       .cable_test_start       = qca808x_cable_test_start,
+       .cable_test_get_status  = qca808x_cable_test_get_status,
++      .config_init            = qca8084_config_init,
+ }, };
+ module_phy_driver(qca808x_driver);
diff --git a/target/linux/qualcommbe/patches-6.18/0309-net-phy-qca808x-Add-link_change_notify-function-for-.patch b/target/linux/qualcommbe/patches-6.18/0309-net-phy-qca808x-Add-link_change_notify-function-for-.patch
new file mode 100644 (file)
index 0000000..e00a582
--- /dev/null
@@ -0,0 +1,90 @@
+From aec49c172cd9c739c1d97ff2d42b9718bb20b609 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Wed, 8 Nov 2023 18:01:14 +0800
+Subject: [PATCH] net: phy: qca808x: Add link_change_notify function for
+ QCA8084
+
+When the link is changed, QCA8084 needs to do the fifo reset and
+adjust the IPG level for the 10G-QXGMII link on the speed 1000M.
+
+Change-Id: I21de802c78496fb95f1c5119fe3894c9fdebbd65
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ drivers/net/phy/qcom/qca808x.c | 52 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 52 insertions(+)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -103,6 +103,14 @@
+ #define QCA8084_MSE_THRESHOLD                 0x800a
+ #define QCA8084_MSE_THRESHOLD_2P5G_VAL                0x51c6
++/* QCA8084 FIFO reset control */
++#define QCA8084_FIFO_CONTROL                  0x19
++#define QCA8084_FIFO_MAC_2_PHY                        BIT(1)
++#define QCA8084_FIFO_PHY_2_MAC                        BIT(0)
++
++#define QCA8084_MMD7_IPG_OP                   0x901d
++#define QCA8084_IPG_10_TO_11_EN                       BIT(0)
++
+ MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi, Luo Jie");
+ MODULE_LICENSE("GPL");
+@@ -700,6 +708,49 @@ static int qca8084_config_init(struct ph
+                            QCA8084_MSE_THRESHOLD_2P5G_VAL);
+ }
++static void qca8084_link_change_notify(struct phy_device *phydev)
++{
++      int ret;
++
++      /* Assert the FIFO between PHY and MAC. */
++      ret = phy_modify(phydev, QCA8084_FIFO_CONTROL,
++                       QCA8084_FIFO_MAC_2_PHY | QCA8084_FIFO_PHY_2_MAC,
++                       0);
++      if (ret) {
++              phydev_err(phydev, "Asserting PHY FIFO failed\n");
++              return;
++      }
++
++      /* If the PHY is in 10G_QXGMII mode, the FIFO needs to be kept in
++       * reset state when link is down, otherwise the FIFO needs to be
++       * de-asserted after waiting 50 ms to make the assert completed.
++       */
++      if (phydev->interface != PHY_INTERFACE_MODE_10G_QXGMII ||
++          phydev->link) {
++              msleep(50);
++
++              /* Deassert the FIFO between PHY and MAC. */
++              ret = phy_modify(phydev, QCA8084_FIFO_CONTROL,
++                               QCA8084_FIFO_MAC_2_PHY |
++                               QCA8084_FIFO_PHY_2_MAC,
++                               QCA8084_FIFO_MAC_2_PHY |
++                               QCA8084_FIFO_PHY_2_MAC);
++              if (ret) {
++                      phydev_err(phydev, "De-asserting PHY FIFO failed\n");
++                      return;
++              }
++      }
++
++      /* Enable IPG level 10 to 11 tuning for link speed 1000M in the
++       * 10G_QXGMII mode.
++       */
++      if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII)
++              phy_modify_mmd(phydev, MDIO_MMD_AN, QCA8084_MMD7_IPG_OP,
++                             QCA8084_IPG_10_TO_11_EN,
++                             phydev->speed == SPEED_1000 ?
++                             QCA8084_IPG_10_TO_11_EN : 0);
++}
++
+ static struct phy_driver qca808x_driver[] = {
+ {
+       /* Qualcomm QCA8081 */
+@@ -749,6 +800,7 @@ static struct phy_driver qca808x_driver[
+       .cable_test_start       = qca808x_cable_test_start,
+       .cable_test_get_status  = qca808x_cable_test_get_status,
+       .config_init            = qca8084_config_init,
++      .link_change_notify     = qca8084_link_change_notify,
+ }, };
+ module_phy_driver(qca808x_driver);
diff --git a/target/linux/qualcommbe/patches-6.18/0310-net-phy-qca808x-Add-register-access-support-routines.patch b/target/linux/qualcommbe/patches-6.18/0310-net-phy-qca808x-Add-register-access-support-routines.patch
new file mode 100644 (file)
index 0000000..c1673ae
--- /dev/null
@@ -0,0 +1,125 @@
+From cea8043def0c0867370c2efd5a1cd73bf4d3e5ba Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Wed, 29 Nov 2023 15:21:22 +0800
+Subject: [PATCH] net: phy: qca808x: Add register access support routines for
+ QCA8084
+
+QCA8084 integrates clock controller and security control modules
+besides of the PHY and PCS. The 32bit registers in these modules
+are accessed using special MDIO sequences to read or write these
+registers.
+
+The MDIO address of PHY and PCS are configured by writing to the
+security control register. The package mode for QCA8084 is also
+configured in a similar manner.
+
+Change-Id: I9317307ef9bbc738a6adcbc3ea1be8e6528d711e
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ drivers/net/phy/qcom/qca808x.c | 88 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 88 insertions(+)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -111,6 +111,22 @@
+ #define QCA8084_MMD7_IPG_OP                   0x901d
+ #define QCA8084_IPG_10_TO_11_EN                       BIT(0)
++/* QCA8084 includes secure control module, which supports customizing the
++ * MDIO address of PHY device and PCS device and configuring package mode
++ * for the interface mode of PCS. The register of secure control is accessed
++ * by MDIO bus with the special MDIO sequences, where the 32 bits register
++ * address is split into 3 MDIO operations with 16 bits address.
++ */
++#define QCA8084_HIGH_ADDR_PREFIX              0x18
++#define QCA8084_LOW_ADDR_PREFIX                       0x10
++
++/* Bottom two bits of REG must be zero */
++#define QCA8084_MII_REG_MASK                  GENMASK(4, 0)
++#define QCA8084_MII_PHY_ADDR_MASK             GENMASK(7, 5)
++#define QCA8084_MII_PAGE_MASK                 GENMASK(23, 8)
++#define QCA8084_MII_SW_ADDR_MASK              GENMASK(31, 24)
++#define QCA8084_MII_REG_DATA_UPPER_16_BITS    BIT(1)
++
+ MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi, Luo Jie");
+ MODULE_LICENSE("GPL");
+@@ -119,6 +135,78 @@ struct qca808x_priv {
+       int led_polarity_mode;
+ };
++static int __qca8084_set_page(struct mii_bus *bus, u16 sw_addr, u16 page)
++{
++      return __mdiobus_write(bus, QCA8084_HIGH_ADDR_PREFIX | (sw_addr >> 5),
++                             sw_addr & 0x1f, page);
++}
++
++static int __qca8084_mii_read(struct mii_bus *bus, u16 addr, u16 reg, u32 *val)
++{
++      int ret, data;
++
++      ret = __mdiobus_read(bus, addr, reg);
++      if (ret < 0)
++              return ret;
++
++      data = ret;
++      ret = __mdiobus_read(bus, addr,
++                           reg | QCA8084_MII_REG_DATA_UPPER_16_BITS);
++      if (ret < 0)
++              return ret;
++
++      *val =  data | ret << 16;
++
++      return 0;
++}
++
++static int __qca8084_mii_write(struct mii_bus *bus, u16 addr, u16 reg, u32 val)
++{
++      int ret;
++
++      ret = __mdiobus_write(bus, addr, reg, lower_16_bits(val));
++      if (!ret)
++              ret = __mdiobus_write(bus, addr,
++                                    reg | QCA8084_MII_REG_DATA_UPPER_16_BITS,
++                                    upper_16_bits(val));
++
++      return ret;
++}
++
++static int qca8084_mii_modify(struct phy_device *phydev, u32 regaddr,
++                            u32 clear, u32 set)
++{
++      u16 reg, addr, page, sw_addr;
++      struct mii_bus *bus;
++      u32 val;
++      int ret;
++
++      bus = phydev->mdio.bus;
++      mutex_lock(&bus->mdio_lock);
++
++      reg = FIELD_GET(QCA8084_MII_REG_MASK, regaddr);
++      addr = FIELD_GET(QCA8084_MII_PHY_ADDR_MASK, regaddr);
++      page = FIELD_GET(QCA8084_MII_PAGE_MASK, regaddr);
++      sw_addr = FIELD_GET(QCA8084_MII_SW_ADDR_MASK, regaddr);
++
++      ret = __qca8084_set_page(bus, sw_addr, page);
++      if (ret < 0)
++              goto qca8084_mii_modify_exit;
++
++      ret = __qca8084_mii_read(bus, QCA8084_LOW_ADDR_PREFIX | addr,
++                               reg, &val);
++      if (ret < 0)
++              goto qca8084_mii_modify_exit;
++
++      val &= ~clear;
++      val |= set;
++      ret = __qca8084_mii_write(bus, QCA8084_LOW_ADDR_PREFIX | addr,
++                                reg, val);
++qca8084_mii_modify_exit:
++      mutex_unlock(&bus->mdio_lock);
++      return ret;
++};
++
+ static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
+ {
+       int ret;
diff --git a/target/linux/qualcommbe/patches-6.18/0311-net-phy-qca808x-Add-QCA8084-probe-function.patch b/target/linux/qualcommbe/patches-6.18/0311-net-phy-qca808x-Add-QCA8084-probe-function.patch
new file mode 100644 (file)
index 0000000..e74fc0b
--- /dev/null
@@ -0,0 +1,162 @@
+From a7fe2c13f3188bf01b60fb15063d028c76dd2f1a Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Mon, 29 Jan 2024 10:51:38 +0800
+Subject: [PATCH] net: phy: qca808x: Add QCA8084 probe function
+
+Add the PHY package probe function. The MDIO slave address of
+PHY, PCS and XPCS can be optionally customized by configuring
+the PHY package level register.
+
+In addition, enable system clock of PHY and de-assert PHY in
+the probe function so that the register of PHY device can be
+accessed, and the features of PHY can be acquired.
+
+Change-Id: I2251b9c5c398a21a4ef547a727189a934ad3a44c
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Alex G: include <linux/reset.h>
+        include "phylib.h" for phy_package_*() declarations
+        select PHY_PACKAGE in Kconfig
+        use phy_package_get_node() instead of phylib->shared->np
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+
+freckup c89414adf2ec7c
+
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/phy/qcom/Kconfig   |  1 +
+ drivers/net/phy/qcom/qca808x.c | 92 ++++++++++++++++++++++++++++++++++
+ 2 files changed, 93 insertions(+)
+
+--- a/drivers/net/phy/qcom/Kconfig
++++ b/drivers/net/phy/qcom/Kconfig
+@@ -18,6 +18,7 @@ config QCA83XX_PHY
+ config QCA808X_PHY
+       tristate "Qualcomm QCA808x PHYs"
+       select QCOM_NET_PHYLIB
++      select PHY_PACKAGE
+       help
+         Currently supports the QCA8081 model
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -2,7 +2,11 @@
+ #include <linux/phy.h>
+ #include <linux/module.h>
++#include <linux/of.h>
++#include <linux/reset.h>
++#include <linux/clk.h>
++#include "../phylib.h"
+ #include "qcom.h"
+ /* ADC threshold */
+@@ -127,6 +131,21 @@
+ #define QCA8084_MII_SW_ADDR_MASK              GENMASK(31, 24)
+ #define QCA8084_MII_REG_DATA_UPPER_16_BITS    BIT(1)
++/* QCA8084 integrates 4 PHYs, PCS0 and PCS1(includes PCS and XPCS). */
++#define QCA8084_MDIO_DEVICE_NUM                       7
++
++#define QCA8084_PCS_CFG                               0xc90f014
++#define QCA8084_PCS_ADDR0_MASK                        GENMASK(4, 0)
++#define QCA8084_PCS_ADDR1_MASK                        GENMASK(9, 5)
++#define QCA8084_PCS_ADDR2_MASK                        GENMASK(14, 10)
++
++#define QCA8084_EPHY_CFG                      0xc90f018
++#define QCA8084_EPHY_ADDR0_MASK                       GENMASK(4, 0)
++#define QCA8084_EPHY_ADDR1_MASK                       GENMASK(9, 5)
++#define QCA8084_EPHY_ADDR2_MASK                       GENMASK(14, 10)
++#define QCA8084_EPHY_ADDR3_MASK                       GENMASK(19, 15)
++#define QCA8084_EPHY_LDO_EN                   GENMASK(21, 20)
++
+ MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi, Luo Jie");
+ MODULE_LICENSE("GPL");
+@@ -839,6 +858,78 @@ static void qca8084_link_change_notify(s
+                              QCA8084_IPG_10_TO_11_EN : 0);
+ }
++static int qca8084_phy_package_probe_once(struct phy_device *phydev)
++{
++      int addr[QCA8084_MDIO_DEVICE_NUM] = {0, 1, 2, 3, 4, 5, 6};
++      struct device_node *np = phy_package_get_node(phydev);
++      int ret, clear, set;
++
++      /* Program the MDIO address of PHY and PCS optionally, the MDIO
++       * address 0-6 is used for PHY and PCS MDIO devices by default.
++       */
++      ret = of_property_read_u32_array(np, "qcom,phy-addr-fixup",
++                                       addr, ARRAY_SIZE(addr));
++      if (ret && ret != -EINVAL)
++              return ret;
++
++      /* Configure the MDIO addresses for the four PHY devices. */
++      clear = QCA8084_EPHY_ADDR0_MASK | QCA8084_EPHY_ADDR1_MASK |
++              QCA8084_EPHY_ADDR2_MASK | QCA8084_EPHY_ADDR3_MASK;
++      set = FIELD_PREP(QCA8084_EPHY_ADDR0_MASK, addr[0]);
++      set |= FIELD_PREP(QCA8084_EPHY_ADDR1_MASK, addr[1]);
++      set |= FIELD_PREP(QCA8084_EPHY_ADDR2_MASK, addr[2]);
++      set |= FIELD_PREP(QCA8084_EPHY_ADDR3_MASK, addr[3]);
++
++      ret = qca8084_mii_modify(phydev, QCA8084_EPHY_CFG, clear, set);
++      if (ret)
++              return ret;
++
++      /* Configure the MDIO addresses for PCS0 and PCS1 including
++       * PCS and XPCS.
++       */
++      clear = QCA8084_PCS_ADDR0_MASK | QCA8084_PCS_ADDR1_MASK |
++              QCA8084_PCS_ADDR2_MASK;
++      set = FIELD_PREP(QCA8084_PCS_ADDR0_MASK, addr[4]);
++      set |= FIELD_PREP(QCA8084_PCS_ADDR1_MASK, addr[5]);
++      set |= FIELD_PREP(QCA8084_PCS_ADDR2_MASK, addr[6]);
++
++      return qca8084_mii_modify(phydev, QCA8084_PCS_CFG, clear, set);
++}
++
++static int qca8084_probe(struct phy_device *phydev)
++{
++      struct device *dev = &phydev->mdio.dev;
++      struct reset_control *rstc;
++      struct clk *clk;
++      int ret;
++
++      ret = devm_of_phy_package_join(dev, phydev, 0);
++      if (ret)
++              return ret;
++
++      if (phy_package_probe_once(phydev)) {
++              ret = qca8084_phy_package_probe_once(phydev);
++              if (ret)
++                      return ret;
++      }
++
++      /* Enable clock of PHY device, so that the PHY register
++       * can be accessed to get PHY features.
++       */
++      clk = devm_clk_get_enabled(dev, NULL);
++      if (IS_ERR(clk))
++              return dev_err_probe(dev, PTR_ERR(clk),
++                                   "Enable PHY clock failed\n");
++
++      /* De-assert PHY reset after the clock of PHY enabled. */
++      rstc = devm_reset_control_get_exclusive(dev, NULL);
++      if (IS_ERR(rstc))
++              return dev_err_probe(dev, PTR_ERR(rstc),
++                                   "Get PHY reset failed\n");
++
++      return reset_control_deassert(rstc);
++}
++
+ static struct phy_driver qca808x_driver[] = {
+ {
+       /* Qualcomm QCA8081 */
+@@ -889,6 +980,7 @@ static struct phy_driver qca808x_driver[
+       .cable_test_get_status  = qca808x_cable_test_get_status,
+       .config_init            = qca8084_config_init,
+       .link_change_notify     = qca8084_link_change_notify,
++      .probe                  = qca8084_probe,
+ }, };
+ module_phy_driver(qca808x_driver);
diff --git a/target/linux/qualcommbe/patches-6.18/0312-net-phy-qca808x-Add-package-clocks-and-resets-for-QC.patch b/target/linux/qualcommbe/patches-6.18/0312-net-phy-qca808x-Add-package-clocks-and-resets-for-QC.patch
new file mode 100644 (file)
index 0000000..5034652
--- /dev/null
@@ -0,0 +1,135 @@
+From 57379fe257895b374d35ce6578ecd62ce1cc1a4d Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Tue, 9 Apr 2024 16:30:55 +0800
+Subject: [PATCH] net: phy: qca808x: Add package clocks and resets for QCA8084
+
+Parse the PHY package clocks from the PHY package DTS node.
+These package level clocks will be enabled in the PHY package
+init function.
+
+Deassert PHY package reset, which is necessary for accessing
+the PHY registers.
+
+Change-Id: I254d0aa0a1155d3618c6f1fc7d7a5b6ecadccbaa
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Alex G: Use accessors for struct phy_package_shared
+        Update to match the patches that will be upstream.
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/phy/qcom/qca808x.c | 74 ++++++++++++++++++++++++++++++++--
+ 1 file changed, 71 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -150,10 +150,39 @@ MODULE_DESCRIPTION("Qualcomm Atheros QCA
+ MODULE_AUTHOR("Matus Ujhelyi, Luo Jie");
+ MODULE_LICENSE("GPL");
++enum {
++      APB_BRIDGE_CLK,
++      AHB_CLK,
++      SEC_CTRL_AHB_CLK,
++      TLMM_CLK,
++      TLMM_AHB_CLK,
++      CNOC_AHB_CLK,
++      MDIO_AHB_CLK,
++      MDIO_MASTER_AHB_CLK,
++      SWITCH_CORE_CLK,
++      PACKAGE_CLK_MAX
++};
++
+ struct qca808x_priv {
+       int led_polarity_mode;
+ };
++struct qca808x_shared_priv {
++      struct clk *clk[PACKAGE_CLK_MAX];
++};
++
++static const char *const qca8084_package_clk_name[PACKAGE_CLK_MAX] = {
++      [APB_BRIDGE_CLK] =      "apb_bridge",
++      [AHB_CLK] =             "ahb",
++      [SEC_CTRL_AHB_CLK] =    "sec_ctrl_ahb",
++      [TLMM_CLK] =            "tlmm",
++      [TLMM_AHB_CLK] =        "tlmm_ahb",
++      [CNOC_AHB_CLK] =        "cnoc_ahb",
++      [MDIO_AHB_CLK] =        "mdio_ahb",
++      [MDIO_MASTER_AHB_CLK] = "mdio_master_ahb",
++      [SWITCH_CORE_CLK] =     "switch_core",
++};
++
+ static int __qca8084_set_page(struct mii_bus *bus, u16 sw_addr, u16 page)
+ {
+       return __mdiobus_write(bus, QCA8084_HIGH_ADDR_PREFIX | (sw_addr >> 5),
+@@ -858,11 +887,24 @@ static void qca8084_link_change_notify(s
+                              QCA8084_IPG_10_TO_11_EN : 0);
+ }
++/* QCA8084 is a four-port PHY, which integrates the clock controller,
++ * 4 PHY devices and 2 PCS interfaces (PCS0 and PCS1). PCS1 includes
++ * XPCS and PCS to support 10G-QXGMII and SGMII. PCS0 includes one PCS
++ * to support SGMII.
++ *
++ * The clocks and resets are sourced from the integrated clock controller
++ * of the PHY package. This integrated clock controller is driven by a
++ * QCA8K clock provider that supplies the clocks and resets to the four
++ * PHYs, PCS and PHY package.
++ */
+ static int qca8084_phy_package_probe_once(struct phy_device *phydev)
+ {
+       int addr[QCA8084_MDIO_DEVICE_NUM] = {0, 1, 2, 3, 4, 5, 6};
+       struct device_node *np = phy_package_get_node(phydev);
+-      int ret, clear, set;
++      struct qca808x_shared_priv *shared_priv;
++      struct reset_control *rstc;
++      int i, ret, clear, set;
++      struct clk *clk;
+       /* Program the MDIO address of PHY and PCS optionally, the MDIO
+        * address 0-6 is used for PHY and PCS MDIO devices by default.
+@@ -893,17 +935,43 @@ static int qca8084_phy_package_probe_onc
+       set |= FIELD_PREP(QCA8084_PCS_ADDR1_MASK, addr[5]);
+       set |= FIELD_PREP(QCA8084_PCS_ADDR2_MASK, addr[6]);
+-      return qca8084_mii_modify(phydev, QCA8084_PCS_CFG, clear, set);
++      ret =  qca8084_mii_modify(phydev, QCA8084_PCS_CFG, clear, set);
++      if (ret)
++              return ret;
++
++      shared_priv = phy_package_get_priv(phydev);
++      for (i = 0; i < ARRAY_SIZE(qca8084_package_clk_name); i++) {
++              clk = of_clk_get_by_name(np, qca8084_package_clk_name[i]);
++              if (IS_ERR(clk)) {
++                      if (PTR_ERR(clk) == -EINVAL)
++                              clk = NULL;
++                      else
++                              return dev_err_probe(&phydev->mdio.dev, PTR_ERR(clk),
++                                                   "package clock %s not ready\n",
++                                                   qca8084_package_clk_name[i]);
++              }
++
++              shared_priv->clk[i] = clk;
++      }
++
++      rstc = of_reset_control_get_exclusive(np, NULL);
++      if (IS_ERR(rstc))
++              return dev_err_probe(&phydev->mdio.dev, PTR_ERR(rstc),
++                                   "package reset not ready\n");
++
++      /* Deassert PHY package. */
++      return reset_control_deassert(rstc);
+ }
+ static int qca8084_probe(struct phy_device *phydev)
+ {
++      struct qca808x_shared_priv *shared_priv;
+       struct device *dev = &phydev->mdio.dev;
+       struct reset_control *rstc;
+       struct clk *clk;
+       int ret;
+-      ret = devm_of_phy_package_join(dev, phydev, 0);
++      ret = devm_of_phy_package_join(dev, phydev, sizeof(*shared_priv));
+       if (ret)
+               return ret;
diff --git a/target/linux/qualcommbe/patches-6.18/0313-net-phy-qca808x-Add-QCA8084-package-init-function.patch b/target/linux/qualcommbe/patches-6.18/0313-net-phy-qca808x-Add-QCA8084-package-init-function.patch
new file mode 100644 (file)
index 0000000..5af66c2
--- /dev/null
@@ -0,0 +1,177 @@
+From d39dc53424bcc778f1e468015490577e7bf0c7b6 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Thu, 25 Jan 2024 17:13:24 +0800
+Subject: [PATCH] net: phy: qca808x: Add QCA8084 package init function
+
+The package mode of PHY is configured for the interface mode of two
+PCSes working correctly.
+
+The PHY package level clocks are enabled and their rates configured.
+
+Change-Id: I63d4b22d2a70ee713cc6a6818b0f3c7aa098a5f5
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Alex G: Use phy_package_get_*() accessors
+        Update to match the patches that will be upstream.
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/phy/qcom/qca808x.c | 118 +++++++++++++++++++++++++++++++++
+ 1 file changed, 118 insertions(+)
+
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -1,5 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0+
++#include <dt-bindings/net/qcom,qca808x.h>
+ #include <linux/phy.h>
+ #include <linux/module.h>
+ #include <linux/of.h>
+@@ -146,6 +147,12 @@
+ #define QCA8084_EPHY_ADDR3_MASK                       GENMASK(19, 15)
+ #define QCA8084_EPHY_LDO_EN                   GENMASK(21, 20)
++#define QCA8084_WORK_MODE_CFG                 0xc90f030
++#define QCA8084_WORK_MODE_MASK                        GENMASK(5, 0)
++#define QCA8084_WORK_MODE_QXGMII              (BIT(5) | GENMASK(3, 0))
++#define QCA8084_WORK_MODE_SWITCH              BIT(4)
++#define QCA8084_WORK_MODE_SWITCH_PORT4_SGMII  BIT(5)
++
+ MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver");
+ MODULE_AUTHOR("Matus Ujhelyi, Luo Jie");
+ MODULE_LICENSE("GPL");
+@@ -168,6 +175,7 @@ struct qca808x_priv {
+ };
+ struct qca808x_shared_priv {
++      int package_mode;
+       struct clk *clk[PACKAGE_CLK_MAX];
+ };
+@@ -816,10 +824,111 @@ static int qca808x_led_polarity_set(stru
+                             active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
+ }
++static int qca8084_package_clock_init(struct qca808x_shared_priv *shared_priv)
++{
++      int ret;
++
++      /* Configure clock rate 312.5MHZ for the PHY package
++       * APB bridge clock tree.
++       */
++      ret = clk_set_rate(shared_priv->clk[APB_BRIDGE_CLK], 312500000);
++      if (ret)
++              return ret;
++
++      ret = clk_prepare_enable(shared_priv->clk[SWITCH_CORE_CLK]);
++      if (ret)
++              return ret;
++
++      ret = clk_prepare_enable(shared_priv->clk[APB_BRIDGE_CLK]);
++      if (ret)
++              return ret;
++
++      /* Configure clock rate 104.17MHZ for the PHY package
++       * AHB clock tree.
++       */
++      ret = clk_set_rate(shared_priv->clk[AHB_CLK], 104170000);
++      if (ret)
++              return ret;
++
++      ret = clk_prepare_enable(shared_priv->clk[AHB_CLK]);
++      if (ret)
++              return ret;
++
++      ret = clk_prepare_enable(shared_priv->clk[SEC_CTRL_AHB_CLK]);
++      if (ret)
++              return ret;
++
++      ret = clk_prepare_enable(shared_priv->clk[TLMM_CLK]);
++      if (ret)
++              return ret;
++
++      ret = clk_prepare_enable(shared_priv->clk[TLMM_AHB_CLK]);
++      if (ret)
++              return ret;
++
++      ret = clk_prepare_enable(shared_priv->clk[CNOC_AHB_CLK]);
++      if (ret)
++              return ret;
++
++      ret = clk_prepare_enable(shared_priv->clk[MDIO_MASTER_AHB_CLK]);
++      if (ret)
++              return ret;
++
++      return clk_prepare_enable(shared_priv->clk[MDIO_AHB_CLK]);
++}
++
++static int qca8084_phy_package_config_init_once(struct phy_device *phydev)
++{
++      struct qca808x_shared_priv *shared_priv;
++      int ret, mode;
++
++      shared_priv = phy_package_get_priv(phydev);
++      switch (shared_priv->package_mode) {
++      case QCA808X_PCS1_10G_QXGMII_PCS0_UNUNSED:
++              mode = QCA8084_WORK_MODE_QXGMII;
++              break;
++      case QCA808X_PCS1_SGMII_MAC_PCS0_SGMII_MAC:
++              mode = QCA8084_WORK_MODE_SWITCH;
++              break;
++      case QCA808X_PCS1_SGMII_MAC_PCS0_SGMII_PHY:
++              mode = QCA8084_WORK_MODE_SWITCH_PORT4_SGMII;
++              break;
++      default:
++              phydev_err(phydev, "Invalid qcom,package-mode %d\n",
++                         shared_priv->package_mode);
++              return -EINVAL;
++      }
++
++      ret = qca8084_mii_modify(phydev, QCA8084_WORK_MODE_CFG,
++                               QCA8084_WORK_MODE_MASK,
++                               FIELD_PREP(QCA8084_WORK_MODE_MASK, mode));
++      if (ret)
++              return ret;
++
++      /* Enable efuse loading into analog circuit */
++      ret = qca8084_mii_modify(phydev, QCA8084_EPHY_CFG,
++                               QCA8084_EPHY_LDO_EN, 0);
++      if (ret)
++              return ret;
++
++      usleep_range(10000, 11000);
++
++      /* Initialize the PHY package clock and reset, which is the
++       * necessary config sequence after GPIO reset on the PHY package.
++       */
++      return qca8084_package_clock_init(shared_priv);
++}
++
+ static int qca8084_config_init(struct phy_device *phydev)
+ {
+       int ret;
++      if (phy_package_init_once(phydev)) {
++              ret = qca8084_phy_package_config_init_once(phydev);
++              if (ret)
++                      return ret;
++      }
++
+       if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII)
+               __set_bit(PHY_INTERFACE_MODE_10G_QXGMII,
+                         phydev->possible_interfaces);
+@@ -954,6 +1063,15 @@ static int qca8084_phy_package_probe_onc
+               shared_priv->clk[i] = clk;
+       }
++      /* The package mode 10G-QXGMII of PCS1 is used for Quad PHY and
++       * PCS0 is unused by default.
++       */
++      shared_priv->package_mode = QCA808X_PCS1_10G_QXGMII_PCS0_UNUNSED;
++      ret = of_property_read_u32(np, "qcom,package-mode",
++                                 &shared_priv->package_mode);
++      if (ret && ret != -EINVAL)
++              return ret;
++
+       rstc = of_reset_control_get_exclusive(np, NULL);
+       if (IS_ERR(rstc))
+               return dev_err_probe(&phydev->mdio.dev, PTR_ERR(rstc),
diff --git a/target/linux/qualcommbe/patches-6.18/0314-dt-bindings-net-pcs-Add-Ethernet-PCS-for-Qualcomm-IP.patch b/target/linux/qualcommbe/patches-6.18/0314-dt-bindings-net-pcs-Add-Ethernet-PCS-for-Qualcomm-IP.patch
new file mode 100644 (file)
index 0000000..1112d4e
--- /dev/null
@@ -0,0 +1,234 @@
+From 5f650721c4b232a14a1a3e25b686f2234faee961 Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Fri, 7 Feb 2025 23:53:12 +0800
+Subject: [PATCH] dt-bindings: net: pcs: Add Ethernet PCS for Qualcomm IPQ9574
+ SoC
+
+The 'UNIPHY' PCS block in the IPQ9574 SoC includes PCS and SerDes
+functions. It supports different interface modes to enable Ethernet
+MAC connections to different types of external PHYs/switch. It includes
+PCS functions for 1Gbps and 2.5Gbps interface modes and XPCS functions
+for 10Gbps interface modes. There are three UNIPHY (PCS) instances
+in IPQ9574 SoC which provide PCS/XPCS functions to the six Ethernet
+ports.
+
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+---
+ .../bindings/net/pcs/qcom,ipq9574-pcs.yaml    | 190 ++++++++++++++++++
+ include/dt-bindings/net/qcom,ipq9574-pcs.h    |  15 ++
+ 2 files changed, 205 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/net/pcs/qcom,ipq9574-pcs.yaml
+ create mode 100644 include/dt-bindings/net/qcom,ipq9574-pcs.h
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/net/pcs/qcom,ipq9574-pcs.yaml
+@@ -0,0 +1,190 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/net/pcs/qcom,ipq9574-pcs.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Ethernet PCS for Qualcomm IPQ9574 SoC
++
++maintainers:
++  - Lei Wei <quic_leiwei@quicinc.com>
++
++description:
++  The UNIPHY hardware blocks in the Qualcomm IPQ SoC include PCS and SerDes
++  functions. They enable connectivity between the Ethernet MAC inside the
++  PPE (packet processing engine) and external Ethernet PHY/switch. There are
++  three UNIPHY instances in IPQ9574 SoC which provide PCS functions to the
++  six Ethernet ports.
++
++  For SGMII (1Gbps PHY) or 2500BASE-X (2.5Gbps PHY) interface modes, the PCS
++  function is enabled by using the PCS block inside UNIPHY. For USXGMII (10Gbps
++  PHY), the XPCS block in UNIPHY is used.
++
++  The SerDes provides 125M (1Gbps mode) or 312.5M (2.5Gbps and 10Gbps modes)
++  RX and TX clocks to the NSSCC (Networking Sub System Clock Controller). The
++  NSSCC divides these clocks and generates the MII RX and TX clocks to each
++  of the MII interfaces between the PCS and MAC, as per the link speeds and
++  interface modes.
++
++  Different IPQ SoC may support different number of UNIPHYs (PCSes) since the
++  number of ports and their capabilities can be different between these SoCs
++
++  Below diagram depicts the UNIPHY (PCS) connections for an IPQ9574 SoC based
++  board. In this example, the PCS0 has four GMIIs/XGMIIs, which can connect
++  with four MACs to support QSGMII (4 x 1Gbps) or 10G_QXGMII (4 x 2.5Gbps)
++  interface modes.
++
++  -           +-------+ +---------+  +-------------------------+
++    +---------+CMN PLL| |  GCC    |  |   NSSCC (Divider)       |
++    |         +----+--+ +----+----+  +--+-------+--------------+
++    |              |         |          ^       |
++    |       31.25M |  SYS/AHB|clk  RX/TX|clk    +------------+
++    |       ref clk|         |          |       |            |
++    |              |         v          | MII RX|TX clk   MAC| RX/TX clk
++    |25/50M     +--+---------+----------+-------+---+      +-+---------+
++    |ref clk    |  |   +----------------+       |   |      | |     PPE |
++    v           |  |   |     UNIPHY0            V   |      | V         |
++  +-------+     |  v   |       +-----------+ (X)GMII|      |           |
++  |       |     |  +---+---+   |           |--------|------|-- MAC0    |
++  |       |     |  |       |   |           | (X)GMII|      |           |
++  |  Quad |     |  |SerDes |   | PCS/XPCS  |--------|------|-- MAC1    |
++  |       +<----+  |       |   |           | (X)GMII|      |           |
++  |(X)GPHY|     |  |       |   |           |--------|------|-- MAC2    |
++  |       |     |  |       |   |           | (X)GMII|      |           |
++  |       |     |  +-------+   |           |--------|------|-- MAC3    |
++  +-------+     |              |           |        |      |           |
++                |              +-----------+        |      |           |
++                +-----------------------------------+      |           |
++                +--+---------+----------+-------+---+      |           |
++  +-------+     |            UNIPHY1                |      |           |
++  |       |     |              +-----------+        |      |           |
++  |(X)GPHY|     | +-------+    |           | (X)GMII|      |           |
++  |       +<----+ |SerDes |    | PCS/XPCS  |--------|------|- MAC4     |
++  |       |     | |       |    |           |        |      |           |
++  +-------+     | +-------+    |           |        |      |           |
++                |              +-----------+        |      |           |
++                +-----------------------------------+      |           |
++                +--+---------+----------+-------+---+      |           |
++  +-------+     |           UNIPHY2                 |      |           |
++  |       |     |              +-----------+        |      |           |
++  |(X)GPHY|     | +-------+    |           | (X)GMII|      |           |
++  |       +<----+ |SerDes |    | PCS/XPCS  |--------|------|- MAC5     |
++  |       |     | |       |    |           |        |      |           |
++  +-------+     | +-------+    |           |        |      |           |
++                |              +-----------+        |      |           |
++                +-----------------------------------+      +-----------+
++
++properties:
++  compatible:
++    enum:
++      - qcom,ipq9574-pcs
++
++  reg:
++    maxItems: 1
++
++  '#address-cells':
++    const: 1
++
++  '#size-cells':
++    const: 0
++
++  clocks:
++    items:
++      - description: System clock
++      - description: AHB clock needed for register interface access
++
++  clock-names:
++    items:
++      - const: sys
++      - const: ahb
++
++  '#clock-cells':
++    const: 1
++    description: See include/dt-bindings/net/qcom,ipq9574-pcs.h for constants
++
++patternProperties:
++  '^pcs-mii@[0-4]$':
++    type: object
++    description: PCS MII interface.
++
++    properties:
++      reg:
++        minimum: 0
++        maximum: 4
++        description: MII index
++
++      clocks:
++        items:
++          - description: PCS MII RX clock
++          - description: PCS MII TX clock
++
++      clock-names:
++        items:
++          - const: rx
++          - const: tx
++
++    required:
++      - reg
++      - clocks
++      - clock-names
++
++    additionalProperties: false
++
++required:
++  - compatible
++  - reg
++  - '#address-cells'
++  - '#size-cells'
++  - clocks
++  - clock-names
++  - '#clock-cells'
++
++additionalProperties: false
++
++examples:
++  - |
++    #include <dt-bindings/clock/qcom,ipq9574-gcc.h>
++
++    ethernet-pcs@7a00000 {
++        compatible = "qcom,ipq9574-pcs";
++        reg = <0x7a00000 0x10000>;
++        #address-cells = <1>;
++        #size-cells = <0>;
++        clocks = <&gcc GCC_UNIPHY0_SYS_CLK>,
++                 <&gcc GCC_UNIPHY0_AHB_CLK>;
++        clock-names = "sys",
++                      "ahb";
++        #clock-cells = <1>;
++
++        pcs-mii@0 {
++            reg = <0>;
++            clocks = <&nsscc 116>,
++                     <&nsscc 117>;
++            clock-names = "rx",
++                          "tx";
++        };
++
++        pcs-mii@1 {
++            reg = <1>;
++            clocks = <&nsscc 118>,
++                     <&nsscc 119>;
++            clock-names = "rx",
++                          "tx";
++        };
++
++        pcs-mii@2 {
++            reg = <2>;
++            clocks = <&nsscc 120>,
++                     <&nsscc 121>;
++            clock-names = "rx",
++                          "tx";
++        };
++
++        pcs-mii@3 {
++            reg = <3>;
++            clocks = <&nsscc 122>,
++                     <&nsscc 123>;
++            clock-names = "rx",
++                          "tx";
++        };
++    };
+--- /dev/null
++++ b/include/dt-bindings/net/qcom,ipq9574-pcs.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.
++ *
++ * Device Tree constants for the Qualcomm IPQ9574 PCS
++ */
++
++#ifndef _DT_BINDINGS_PCS_QCOM_IPQ9574_H
++#define _DT_BINDINGS_PCS_QCOM_IPQ9574_H
++
++/* The RX and TX clocks which are provided from the SerDes to NSSCC. */
++#define PCS_RX_CLK            0
++#define PCS_TX_CLK            1
++
++#endif /* _DT_BINDINGS_PCS_QCOM_IPQ9574_H */
diff --git a/target/linux/qualcommbe/patches-6.18/0315-net-pcs-Add-PCS-driver-for-Qualcomm-IPQ9574-SoC.patch b/target/linux/qualcommbe/patches-6.18/0315-net-pcs-Add-PCS-driver-for-Qualcomm-IPQ9574-SoC.patch
new file mode 100644 (file)
index 0000000..bae262a
--- /dev/null
@@ -0,0 +1,301 @@
+From e404519d9f3e5e7d661cb105d3766d87e37e4ef5 Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Fri, 7 Feb 2025 23:53:13 +0800
+Subject: [PATCH] net: pcs: Add PCS driver for Qualcomm IPQ9574 SoC
+
+The 'UNIPHY' PCS hardware block in Qualcomm's IPQ SoC supports
+different interface modes to enable Ethernet MAC connections
+for different types of external PHYs/switch. Each UNIPHY block
+includes a SerDes and PCS/XPCS blocks, and can operate in either
+PCS or XPCS modes. It supports 1Gbps and 2.5Gbps interface modes
+(Ex: SGMII) using the PCS, and 10Gbps interface modes (Ex: USXGMII)
+using the XPCS. There are three UNIPHY (PCS) instances in IPQ9574
+SoC which support the six Ethernet ports in the SoC.
+
+This patch adds support for the platform driver, probe and clock
+registrations for the PCS driver. The platform driver creates an
+'ipq_pcs' instance for each of the UNIPHY used on the given board.
+
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+---
+ drivers/net/pcs/Kconfig            |   9 ++
+ drivers/net/pcs/Makefile           |   1 +
+ drivers/net/pcs/pcs-qcom-ipq9574.c | 245 +++++++++++++++++++++++++++++
+ 3 files changed, 255 insertions(+)
+ create mode 100644 drivers/net/pcs/pcs-qcom-ipq9574.c
+
+--- a/drivers/net/pcs/Kconfig
++++ b/drivers/net/pcs/Kconfig
+@@ -36,6 +36,15 @@ config PCS_MTK_USXGMII
+         1000Base-X, 2500Base-X and Cisco SGMII are supported on the same
+         differential pairs via an embedded LynxI PHY.
++config PCS_QCOM_IPQ9574
++      tristate "Qualcomm IPQ9574 PCS"
++      depends on OF && (ARCH_QCOM || COMPILE_TEST)
++      depends on HAS_IOMEM && COMMON_CLK
++      help
++        This module provides driver for UNIPHY PCS available on Qualcomm
++        IPQ9574 SoC. The UNIPHY PCS supports both PCS and XPCS functions
++        to support different interface modes for MAC to PHY connections.
++
+ config PCS_RZN1_MIIC
+       tristate "Renesas RZ/N1 MII converter"
+       depends on OF && (ARCH_RZN1 || COMPILE_TEST)
+--- a/drivers/net/pcs/Makefile
++++ b/drivers/net/pcs/Makefile
+@@ -7,5 +7,6 @@ pcs_xpcs-$(CONFIG_PCS_XPCS)    := pcs-xpcs.
+ obj-$(CONFIG_PCS_XPCS)                += pcs_xpcs.o
+ obj-$(CONFIG_PCS_LYNX)                += pcs-lynx.o
+ obj-$(CONFIG_PCS_MTK_LYNXI)   += pcs-mtk-lynxi.o
++obj-$(CONFIG_PCS_QCOM_IPQ9574)        += pcs-qcom-ipq9574.o
+ obj-$(CONFIG_PCS_RZN1_MIIC)   += pcs-rzn1-miic.o
+ obj-$(CONFIG_PCS_MTK_USXGMII) += pcs-mtk-usxgmii.o
+--- /dev/null
++++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
+@@ -0,0 +1,245 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/device.h>
++#include <linux/phy.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++
++#include <dt-bindings/net/qcom,ipq9574-pcs.h>
++
++#define XPCS_INDIRECT_ADDR            0x8000
++#define XPCS_INDIRECT_AHB_ADDR                0x83fc
++#define XPCS_INDIRECT_ADDR_H          GENMASK(20, 8)
++#define XPCS_INDIRECT_ADDR_L          GENMASK(7, 0)
++#define XPCS_INDIRECT_DATA_ADDR(reg)  (FIELD_PREP(GENMASK(15, 10), 0x20) | \
++                                       FIELD_PREP(GENMASK(9, 2), \
++                                       FIELD_GET(XPCS_INDIRECT_ADDR_L, reg)))
++
++/* PCS private data */
++struct ipq_pcs {
++      struct device *dev;
++      void __iomem *base;
++      struct regmap *regmap;
++      phy_interface_t interface;
++
++      /* RX clock supplied to NSSCC */
++      struct clk_hw rx_hw;
++      /* TX clock supplied to NSSCC */
++      struct clk_hw tx_hw;
++};
++
++static unsigned long ipq_pcs_clk_rate_get(struct ipq_pcs *qpcs)
++{
++      switch (qpcs->interface) {
++      case PHY_INTERFACE_MODE_USXGMII:
++              return 312500000;
++      default:
++              return 125000000;
++      }
++}
++
++/* Return clock rate for the RX clock supplied to NSSCC
++ * as per the interface mode.
++ */
++static unsigned long ipq_pcs_rx_clk_recalc_rate(struct clk_hw *hw,
++                                              unsigned long parent_rate)
++{
++      struct ipq_pcs *qpcs = container_of(hw, struct ipq_pcs, rx_hw);
++
++      return ipq_pcs_clk_rate_get(qpcs);
++}
++
++/* Return clock rate for the TX clock supplied to NSSCC
++ * as per the interface mode.
++ */
++static unsigned long ipq_pcs_tx_clk_recalc_rate(struct clk_hw *hw,
++                                              unsigned long parent_rate)
++{
++      struct ipq_pcs *qpcs = container_of(hw, struct ipq_pcs, tx_hw);
++
++      return ipq_pcs_clk_rate_get(qpcs);
++}
++
++static int ipq_pcs_clk_determine_rate(struct clk_hw *hw,
++                                    struct clk_rate_request *req)
++{
++      switch (req->rate) {
++      case 125000000:
++      case 312500000:
++              return 0;
++      default:
++              return -EINVAL;
++      }
++}
++
++/* Clock ops for the RX clock supplied to NSSCC */
++static const struct clk_ops ipq_pcs_rx_clk_ops = {
++      .determine_rate = ipq_pcs_clk_determine_rate,
++      .recalc_rate = ipq_pcs_rx_clk_recalc_rate,
++};
++
++/* Clock ops for the TX clock supplied to NSSCC */
++static const struct clk_ops ipq_pcs_tx_clk_ops = {
++      .determine_rate = ipq_pcs_clk_determine_rate,
++      .recalc_rate = ipq_pcs_tx_clk_recalc_rate,
++};
++
++static struct clk_hw *ipq_pcs_clk_hw_get(struct of_phandle_args *clkspec,
++                                       void *data)
++{
++      struct ipq_pcs *qpcs = data;
++
++      switch (clkspec->args[0]) {
++      case PCS_RX_CLK:
++              return &qpcs->rx_hw;
++      case PCS_TX_CLK:
++              return &qpcs->tx_hw;
++      }
++
++      return ERR_PTR(-EINVAL);
++}
++
++/* Register the RX and TX clock which are output from SerDes to
++ * the NSSCC. The NSSCC driver assigns the RX and TX clock as
++ * parent, divides them to generate the MII RX and TX clock to
++ * each MII interface of the PCS as per the link speeds and
++ * interface modes.
++ */
++static int ipq_pcs_clk_register(struct ipq_pcs *qpcs)
++{
++      struct clk_init_data init = { };
++      int ret;
++
++      init.ops = &ipq_pcs_rx_clk_ops;
++      init.name = devm_kasprintf(qpcs->dev, GFP_KERNEL, "%s::rx_clk",
++                                 dev_name(qpcs->dev));
++      if (!init.name)
++              return -ENOMEM;
++
++      qpcs->rx_hw.init = &init;
++      ret = devm_clk_hw_register(qpcs->dev, &qpcs->rx_hw);
++      if (ret)
++              return ret;
++
++      init.ops = &ipq_pcs_tx_clk_ops;
++      init.name = devm_kasprintf(qpcs->dev, GFP_KERNEL, "%s::tx_clk",
++                                 dev_name(qpcs->dev));
++      if (!init.name)
++              return -ENOMEM;
++
++      qpcs->tx_hw.init = &init;
++      ret = devm_clk_hw_register(qpcs->dev, &qpcs->tx_hw);
++      if (ret)
++              return ret;
++
++      return devm_of_clk_add_hw_provider(qpcs->dev, ipq_pcs_clk_hw_get, qpcs);
++}
++
++static int ipq_pcs_regmap_read(void *context, unsigned int reg,
++                             unsigned int *val)
++{
++      struct ipq_pcs *qpcs = context;
++
++      /* PCS uses direct AHB access while XPCS uses indirect AHB access */
++      if (reg >= XPCS_INDIRECT_ADDR) {
++              writel(FIELD_GET(XPCS_INDIRECT_ADDR_H, reg),
++                     qpcs->base + XPCS_INDIRECT_AHB_ADDR);
++              *val = readl(qpcs->base + XPCS_INDIRECT_DATA_ADDR(reg));
++      } else {
++              *val = readl(qpcs->base + reg);
++      }
++
++      return 0;
++}
++
++static int ipq_pcs_regmap_write(void *context, unsigned int reg,
++                              unsigned int val)
++{
++      struct ipq_pcs *qpcs = context;
++
++      /* PCS uses direct AHB access while XPCS uses indirect AHB access */
++      if (reg >= XPCS_INDIRECT_ADDR) {
++              writel(FIELD_GET(XPCS_INDIRECT_ADDR_H, reg),
++                     qpcs->base + XPCS_INDIRECT_AHB_ADDR);
++              writel(val, qpcs->base + XPCS_INDIRECT_DATA_ADDR(reg));
++      } else {
++              writel(val, qpcs->base + reg);
++      }
++
++      return 0;
++}
++
++static const struct regmap_config ipq_pcs_regmap_cfg = {
++      .reg_bits = 32,
++      .val_bits = 32,
++      .reg_read = ipq_pcs_regmap_read,
++      .reg_write = ipq_pcs_regmap_write,
++      .fast_io = true,
++};
++
++static int ipq9574_pcs_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct ipq_pcs *qpcs;
++      struct clk *clk;
++      int ret;
++
++      qpcs = devm_kzalloc(dev, sizeof(*qpcs), GFP_KERNEL);
++      if (!qpcs)
++              return -ENOMEM;
++
++      qpcs->dev = dev;
++
++      qpcs->base = devm_platform_ioremap_resource(pdev, 0);
++      if (IS_ERR(qpcs->base))
++              return dev_err_probe(dev, PTR_ERR(qpcs->base),
++                                   "Failed to ioremap resource\n");
++
++      qpcs->regmap = devm_regmap_init(dev, NULL, qpcs, &ipq_pcs_regmap_cfg);
++      if (IS_ERR(qpcs->regmap))
++              return dev_err_probe(dev, PTR_ERR(qpcs->regmap),
++                                   "Failed to allocate register map\n");
++
++      clk = devm_clk_get_enabled(dev, "sys");
++      if (IS_ERR(clk))
++              return dev_err_probe(dev, PTR_ERR(clk),
++                                   "Failed to enable SYS clock\n");
++
++      clk = devm_clk_get_enabled(dev, "ahb");
++      if (IS_ERR(clk))
++              return dev_err_probe(dev, PTR_ERR(clk),
++                                   "Failed to enable AHB clock\n");
++
++      ret = ipq_pcs_clk_register(qpcs);
++      if (ret)
++              return ret;
++
++      platform_set_drvdata(pdev, qpcs);
++
++      return 0;
++}
++
++static const struct of_device_id ipq9574_pcs_of_mtable[] = {
++      { .compatible = "qcom,ipq9574-pcs" },
++      { /* sentinel */ },
++};
++MODULE_DEVICE_TABLE(of, ipq9574_pcs_of_mtable);
++
++static struct platform_driver ipq9574_pcs_driver = {
++      .driver = {
++              .name = "ipq9574_pcs",
++              .suppress_bind_attrs = true,
++              .of_match_table = ipq9574_pcs_of_mtable,
++      },
++      .probe = ipq9574_pcs_probe,
++};
++module_platform_driver(ipq9574_pcs_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Qualcomm IPQ9574 PCS driver");
++MODULE_AUTHOR("Lei Wei <quic_leiwei@quicinc.com>");
diff --git a/target/linux/qualcommbe/patches-6.18/0316-net-pcs-qcom-ipq9574-Add-PCS-instantiation-and-phyli.patch b/target/linux/qualcommbe/patches-6.18/0316-net-pcs-qcom-ipq9574-Add-PCS-instantiation-and-phyli.patch
new file mode 100644 (file)
index 0000000..7d071c2
--- /dev/null
@@ -0,0 +1,555 @@
+From 10b609ddbf4d369c80098efa39451ef3973759b5 Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Fri, 7 Feb 2025 23:53:14 +0800
+Subject: [PATCH] net: pcs: qcom-ipq9574: Add PCS instantiation and phylink
+ operations
+
+This patch adds the following PCS functionality for the PCS driver
+for IPQ9574 SoC:
+
+a.) Parses PCS MII DT nodes and instantiate each MII PCS instance.
+b.) Exports PCS instance get and put APIs. The network driver calls
+the PCS get API to get and associate the PCS instance with the port
+MAC.
+c.) PCS phylink operations for SGMII/QSGMII interface modes.
+
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+Alex G: remove phylink_pcs .neg_mode boolean
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/pcs/pcs-qcom-ipq9574.c   | 468 +++++++++++++++++++++++++++
+ include/linux/pcs/pcs-qcom-ipq9574.h |  15 +
+ 2 files changed, 483 insertions(+)
+ create mode 100644 include/linux/pcs/pcs-qcom-ipq9574.h
+
+--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
++++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
+@@ -6,12 +6,46 @@
+ #include <linux/clk.h>
+ #include <linux/clk-provider.h>
+ #include <linux/device.h>
++#include <linux/of.h>
++#include <linux/of_platform.h>
++#include <linux/pcs/pcs-qcom-ipq9574.h>
+ #include <linux/phy.h>
++#include <linux/phylink.h>
+ #include <linux/platform_device.h>
+ #include <linux/regmap.h>
+ #include <dt-bindings/net/qcom,ipq9574-pcs.h>
++/* Maximum number of MIIs per PCS instance. There are 5 MIIs for PSGMII. */
++#define PCS_MAX_MII_NRS                       5
++
++#define PCS_CALIBRATION                       0x1e0
++#define PCS_CALIBRATION_DONE          BIT(7)
++
++#define PCS_MODE_CTRL                 0x46c
++#define PCS_MODE_SEL_MASK             GENMASK(12, 8)
++#define PCS_MODE_SGMII                        FIELD_PREP(PCS_MODE_SEL_MASK, 0x4)
++#define PCS_MODE_QSGMII                       FIELD_PREP(PCS_MODE_SEL_MASK, 0x1)
++
++#define PCS_MII_CTRL(x)                       (0x480 + 0x18 * (x))
++#define PCS_MII_ADPT_RESET            BIT(11)
++#define PCS_MII_FORCE_MODE            BIT(3)
++#define PCS_MII_SPEED_MASK            GENMASK(2, 1)
++#define PCS_MII_SPEED_1000            FIELD_PREP(PCS_MII_SPEED_MASK, 0x2)
++#define PCS_MII_SPEED_100             FIELD_PREP(PCS_MII_SPEED_MASK, 0x1)
++#define PCS_MII_SPEED_10              FIELD_PREP(PCS_MII_SPEED_MASK, 0x0)
++
++#define PCS_MII_STS(x)                        (0x488 + 0x18 * (x))
++#define PCS_MII_LINK_STS              BIT(7)
++#define PCS_MII_STS_DUPLEX_FULL               BIT(6)
++#define PCS_MII_STS_SPEED_MASK                GENMASK(5, 4)
++#define PCS_MII_STS_SPEED_10          0
++#define PCS_MII_STS_SPEED_100         1
++#define PCS_MII_STS_SPEED_1000                2
++
++#define PCS_PLL_RESET                 0x780
++#define PCS_ANA_SW_RESET              BIT(6)
++
+ #define XPCS_INDIRECT_ADDR            0x8000
+ #define XPCS_INDIRECT_AHB_ADDR                0x83fc
+ #define XPCS_INDIRECT_ADDR_H          GENMASK(20, 8)
+@@ -20,6 +54,18 @@
+                                        FIELD_PREP(GENMASK(9, 2), \
+                                        FIELD_GET(XPCS_INDIRECT_ADDR_L, reg)))
++/* Per PCS MII private data */
++struct ipq_pcs_mii {
++      struct ipq_pcs *qpcs;
++      struct phylink_pcs pcs;
++      int index;
++
++      /* RX clock from NSSCC to PCS MII */
++      struct clk *rx_clk;
++      /* TX clock from NSSCC to PCS MII */
++      struct clk *tx_clk;
++};
++
+ /* PCS private data */
+ struct ipq_pcs {
+       struct device *dev;
+@@ -31,8 +77,358 @@ struct ipq_pcs {
+       struct clk_hw rx_hw;
+       /* TX clock supplied to NSSCC */
+       struct clk_hw tx_hw;
++
++      struct ipq_pcs_mii *qpcs_mii[PCS_MAX_MII_NRS];
+ };
++#define phylink_pcs_to_qpcs_mii(_pcs) \
++      container_of(_pcs, struct ipq_pcs_mii, pcs)
++
++static void ipq_pcs_get_state_sgmii(struct ipq_pcs *qpcs,
++                                  int index,
++                                  struct phylink_link_state *state)
++{
++      unsigned int val;
++      int ret;
++
++      ret = regmap_read(qpcs->regmap, PCS_MII_STS(index), &val);
++      if (ret) {
++              state->link = 0;
++              return;
++      }
++
++      state->link = !!(val & PCS_MII_LINK_STS);
++
++      if (!state->link)
++              return;
++
++      switch (FIELD_GET(PCS_MII_STS_SPEED_MASK, val)) {
++      case PCS_MII_STS_SPEED_1000:
++              state->speed = SPEED_1000;
++              break;
++      case PCS_MII_STS_SPEED_100:
++              state->speed = SPEED_100;
++              break;
++      case PCS_MII_STS_SPEED_10:
++              state->speed = SPEED_10;
++              break;
++      default:
++              state->link = false;
++              return;
++      }
++
++      if (val & PCS_MII_STS_DUPLEX_FULL)
++              state->duplex = DUPLEX_FULL;
++      else
++              state->duplex = DUPLEX_HALF;
++}
++
++static int ipq_pcs_config_mode(struct ipq_pcs *qpcs,
++                             phy_interface_t interface)
++{
++      unsigned int val;
++      int ret;
++
++      /* Configure PCS interface mode */
++      switch (interface) {
++      case PHY_INTERFACE_MODE_SGMII:
++              val = PCS_MODE_SGMII;
++              break;
++      case PHY_INTERFACE_MODE_QSGMII:
++              val = PCS_MODE_QSGMII;
++              break;
++      default:
++              return -EOPNOTSUPP;
++      }
++
++      ret = regmap_update_bits(qpcs->regmap, PCS_MODE_CTRL,
++                               PCS_MODE_SEL_MASK, val);
++      if (ret)
++              return ret;
++
++      /* PCS PLL reset */
++      ret = regmap_clear_bits(qpcs->regmap, PCS_PLL_RESET, PCS_ANA_SW_RESET);
++      if (ret)
++              return ret;
++
++      fsleep(1000);
++      ret = regmap_set_bits(qpcs->regmap, PCS_PLL_RESET, PCS_ANA_SW_RESET);
++      if (ret)
++              return ret;
++
++      /* Wait for calibration completion */
++      ret = regmap_read_poll_timeout(qpcs->regmap, PCS_CALIBRATION,
++                                     val, val & PCS_CALIBRATION_DONE,
++                                     1000, 100000);
++      if (ret) {
++              dev_err(qpcs->dev, "PCS calibration timed-out\n");
++              return ret;
++      }
++
++      qpcs->interface = interface;
++
++      return 0;
++}
++
++static int ipq_pcs_config_sgmii(struct ipq_pcs *qpcs,
++                              int index,
++                              unsigned int neg_mode,
++                              phy_interface_t interface)
++{
++      int ret;
++
++      /* Configure the PCS mode if required */
++      if (qpcs->interface != interface) {
++              ret = ipq_pcs_config_mode(qpcs, interface);
++              if (ret)
++                      return ret;
++      }
++
++      /* Nothing to do here as in-band autoneg mode is enabled
++       * by default for each PCS MII port.
++       */
++      if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
++              return 0;
++
++      /* Set force speed mode */
++      return regmap_set_bits(qpcs->regmap,
++                             PCS_MII_CTRL(index), PCS_MII_FORCE_MODE);
++}
++
++static int ipq_pcs_link_up_config_sgmii(struct ipq_pcs *qpcs,
++                                      int index,
++                                      unsigned int neg_mode,
++                                      int speed)
++{
++      unsigned int val;
++      int ret;
++
++      /* PCS speed need not be configured if in-band autoneg is enabled */
++      if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) {
++              /* PCS speed set for force mode */
++              switch (speed) {
++              case SPEED_1000:
++                      val = PCS_MII_SPEED_1000;
++                      break;
++              case SPEED_100:
++                      val = PCS_MII_SPEED_100;
++                      break;
++              case SPEED_10:
++                      val = PCS_MII_SPEED_10;
++                      break;
++              default:
++                      dev_err(qpcs->dev, "Invalid SGMII speed %d\n", speed);
++                      return -EINVAL;
++              }
++
++              ret = regmap_update_bits(qpcs->regmap, PCS_MII_CTRL(index),
++                                       PCS_MII_SPEED_MASK, val);
++              if (ret)
++                      return ret;
++      }
++
++      /* PCS adapter reset */
++      ret = regmap_clear_bits(qpcs->regmap,
++                              PCS_MII_CTRL(index), PCS_MII_ADPT_RESET);
++      if (ret)
++              return ret;
++
++      return regmap_set_bits(qpcs->regmap,
++                             PCS_MII_CTRL(index), PCS_MII_ADPT_RESET);
++}
++
++static int ipq_pcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
++                          const struct phylink_link_state *state)
++{
++      switch (state->interface) {
++      case PHY_INTERFACE_MODE_SGMII:
++      case PHY_INTERFACE_MODE_QSGMII:
++              return 0;
++      default:
++              return -EINVAL;
++      }
++}
++
++static unsigned int ipq_pcs_inband_caps(struct phylink_pcs *pcs,
++                                      phy_interface_t interface)
++{
++      switch (interface) {
++      case PHY_INTERFACE_MODE_SGMII:
++      case PHY_INTERFACE_MODE_QSGMII:
++              return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
++      default:
++              return 0;
++      }
++}
++
++static int ipq_pcs_enable(struct phylink_pcs *pcs)
++{
++      struct ipq_pcs_mii *qpcs_mii = phylink_pcs_to_qpcs_mii(pcs);
++      struct ipq_pcs *qpcs = qpcs_mii->qpcs;
++      int index = qpcs_mii->index;
++      int ret;
++
++      ret = clk_prepare_enable(qpcs_mii->rx_clk);
++      if (ret) {
++              dev_err(qpcs->dev, "Failed to enable MII %d RX clock\n", index);
++              return ret;
++      }
++
++      ret = clk_prepare_enable(qpcs_mii->tx_clk);
++      if (ret) {
++              /* This is a fatal event since phylink does not support unwinding
++               * the state back for this error. So, we only report the error
++               * and do not disable the clocks.
++               */
++              dev_err(qpcs->dev, "Failed to enable MII %d TX clock\n", index);
++              return ret;
++      }
++
++      return 0;
++}
++
++static void ipq_pcs_disable(struct phylink_pcs *pcs)
++{
++      struct ipq_pcs_mii *qpcs_mii = phylink_pcs_to_qpcs_mii(pcs);
++
++      clk_disable_unprepare(qpcs_mii->rx_clk);
++      clk_disable_unprepare(qpcs_mii->tx_clk);
++}
++
++static void ipq_pcs_get_state(struct phylink_pcs *pcs,
++                            struct phylink_link_state *state)
++{
++      struct ipq_pcs_mii *qpcs_mii = phylink_pcs_to_qpcs_mii(pcs);
++      struct ipq_pcs *qpcs = qpcs_mii->qpcs;
++      int index = qpcs_mii->index;
++
++      switch (state->interface) {
++      case PHY_INTERFACE_MODE_SGMII:
++      case PHY_INTERFACE_MODE_QSGMII:
++              ipq_pcs_get_state_sgmii(qpcs, index, state);
++              break;
++      default:
++              break;
++      }
++
++      dev_dbg_ratelimited(qpcs->dev,
++                          "mode=%s/%s/%s link=%u\n",
++                          phy_modes(state->interface),
++                          phy_speed_to_str(state->speed),
++                          phy_duplex_to_str(state->duplex),
++                          state->link);
++}
++
++static int ipq_pcs_config(struct phylink_pcs *pcs,
++                        unsigned int neg_mode,
++                        phy_interface_t interface,
++                        const unsigned long *advertising,
++                        bool permit)
++{
++      struct ipq_pcs_mii *qpcs_mii = phylink_pcs_to_qpcs_mii(pcs);
++      struct ipq_pcs *qpcs = qpcs_mii->qpcs;
++      int index = qpcs_mii->index;
++
++      switch (interface) {
++      case PHY_INTERFACE_MODE_SGMII:
++      case PHY_INTERFACE_MODE_QSGMII:
++              return ipq_pcs_config_sgmii(qpcs, index, neg_mode, interface);
++      default:
++              return -EOPNOTSUPP;
++      };
++}
++
++static void ipq_pcs_link_up(struct phylink_pcs *pcs,
++                          unsigned int neg_mode,
++                          phy_interface_t interface,
++                          int speed, int duplex)
++{
++      struct ipq_pcs_mii *qpcs_mii = phylink_pcs_to_qpcs_mii(pcs);
++      struct ipq_pcs *qpcs = qpcs_mii->qpcs;
++      int index = qpcs_mii->index;
++      int ret;
++
++      switch (interface) {
++      case PHY_INTERFACE_MODE_SGMII:
++      case PHY_INTERFACE_MODE_QSGMII:
++              ret = ipq_pcs_link_up_config_sgmii(qpcs, index,
++                                                 neg_mode, speed);
++              break;
++      default:
++              return;
++      }
++
++      if (ret)
++              dev_err(qpcs->dev, "PCS link up fail for interface %s\n",
++                      phy_modes(interface));
++}
++
++static const struct phylink_pcs_ops ipq_pcs_phylink_ops = {
++      .pcs_validate = ipq_pcs_validate,
++      .pcs_inband_caps = ipq_pcs_inband_caps,
++      .pcs_enable = ipq_pcs_enable,
++      .pcs_disable = ipq_pcs_disable,
++      .pcs_get_state = ipq_pcs_get_state,
++      .pcs_config = ipq_pcs_config,
++      .pcs_link_up = ipq_pcs_link_up,
++};
++
++/* Parse the PCS MII DT nodes which are child nodes of the PCS node,
++ * and instantiate each MII PCS instance.
++ */
++static int ipq_pcs_create_miis(struct ipq_pcs *qpcs)
++{
++      struct device *dev = qpcs->dev;
++      struct ipq_pcs_mii *qpcs_mii;
++      struct device_node *mii_np;
++      u32 index;
++      int ret;
++
++      for_each_available_child_of_node(dev->of_node, mii_np) {
++              ret = of_property_read_u32(mii_np, "reg", &index);
++              if (ret) {
++                      dev_err(dev, "Failed to read MII index\n");
++                      of_node_put(mii_np);
++                      return ret;
++              }
++
++              if (index >= PCS_MAX_MII_NRS) {
++                      dev_err(dev, "Invalid MII index\n");
++                      of_node_put(mii_np);
++                      return -EINVAL;
++              }
++
++              qpcs_mii = devm_kzalloc(dev, sizeof(*qpcs_mii), GFP_KERNEL);
++              if (!qpcs_mii) {
++                      of_node_put(mii_np);
++                      return -ENOMEM;
++              }
++
++              qpcs_mii->qpcs = qpcs;
++              qpcs_mii->index = index;
++              qpcs_mii->pcs.ops = &ipq_pcs_phylink_ops;
++              qpcs_mii->pcs.poll = true;
++
++              qpcs_mii->rx_clk = devm_get_clk_from_child(dev, mii_np, "rx");
++              if (IS_ERR(qpcs_mii->rx_clk)) {
++                      of_node_put(mii_np);
++                      return dev_err_probe(dev, PTR_ERR(qpcs_mii->rx_clk),
++                                           "Failed to get MII %d RX clock\n", index);
++              }
++
++              qpcs_mii->tx_clk = devm_get_clk_from_child(dev, mii_np, "tx");
++              if (IS_ERR(qpcs_mii->tx_clk)) {
++                      of_node_put(mii_np);
++                      return dev_err_probe(dev, PTR_ERR(qpcs_mii->tx_clk),
++                                           "Failed to get MII %d TX clock\n", index);
++              }
++
++              qpcs->qpcs_mii[index] = qpcs_mii;
++      }
++
++      return 0;
++}
++
+ static unsigned long ipq_pcs_clk_rate_get(struct ipq_pcs *qpcs)
+ {
+       switch (qpcs->interface) {
+@@ -219,6 +615,10 @@ static int ipq9574_pcs_probe(struct plat
+       if (ret)
+               return ret;
++      ret = ipq_pcs_create_miis(qpcs);
++      if (ret)
++              return ret;
++
+       platform_set_drvdata(pdev, qpcs);
+       return 0;
+@@ -230,6 +630,74 @@ static const struct of_device_id ipq9574
+ };
+ MODULE_DEVICE_TABLE(of, ipq9574_pcs_of_mtable);
++/**
++ * ipq_pcs_get() - Get the IPQ PCS MII instance
++ * @np: Device tree node to the PCS MII
++ *
++ * Description: Get the phylink PCS instance for the given PCS MII node @np.
++ * This instance is associated with the specific MII of the PCS and the
++ * corresponding Ethernet netdevice.
++ *
++ * Return: A pointer to the phylink PCS instance or an error-pointer value.
++ */
++struct phylink_pcs *ipq_pcs_get(struct device_node *np)
++{
++      struct platform_device *pdev;
++      struct ipq_pcs_mii *qpcs_mii;
++      struct ipq_pcs *qpcs;
++      u32 index;
++
++      if (of_property_read_u32(np, "reg", &index))
++              return ERR_PTR(-EINVAL);
++
++      if (index >= PCS_MAX_MII_NRS)
++              return ERR_PTR(-EINVAL);
++
++      if (!of_match_node(ipq9574_pcs_of_mtable, np->parent))
++              return ERR_PTR(-EINVAL);
++
++      /* Get the parent device */
++      pdev = of_find_device_by_node(np->parent);
++      if (!pdev)
++              return ERR_PTR(-ENODEV);
++
++      qpcs = platform_get_drvdata(pdev);
++      if (!qpcs) {
++              put_device(&pdev->dev);
++
++              /* If probe is not yet completed, return DEFER to
++               * the dependent driver.
++               */
++              return ERR_PTR(-EPROBE_DEFER);
++      }
++
++      qpcs_mii = qpcs->qpcs_mii[index];
++      if (!qpcs_mii) {
++              put_device(&pdev->dev);
++              return ERR_PTR(-ENOENT);
++      }
++
++      return &qpcs_mii->pcs;
++}
++EXPORT_SYMBOL(ipq_pcs_get);
++
++/**
++ * ipq_pcs_put() - Release the IPQ PCS MII instance
++ * @pcs: PCS instance
++ *
++ * Description: Release a phylink PCS instance.
++ */
++void ipq_pcs_put(struct phylink_pcs *pcs)
++{
++      struct ipq_pcs_mii *qpcs_mii = phylink_pcs_to_qpcs_mii(pcs);
++
++      /* Put reference taken by of_find_device_by_node() in
++       * ipq_pcs_get().
++       */
++      put_device(qpcs_mii->qpcs->dev);
++}
++EXPORT_SYMBOL(ipq_pcs_put);
++
+ static struct platform_driver ipq9574_pcs_driver = {
+       .driver = {
+               .name = "ipq9574_pcs",
+--- /dev/null
++++ b/include/linux/pcs/pcs-qcom-ipq9574.h
+@@ -0,0 +1,15 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef __LINUX_PCS_QCOM_IPQ9574_H
++#define __LINUX_PCS_QCOM_IPQ9574_H
++
++struct device_node;
++struct phylink_pcs;
++
++struct phylink_pcs *ipq_pcs_get(struct device_node *np);
++void ipq_pcs_put(struct phylink_pcs *pcs);
++
++#endif /* __LINUX_PCS_QCOM_IPQ9574_H */
diff --git a/target/linux/qualcommbe/patches-6.18/0317-net-pcs-qcom-ipq9574-Add-USXGMII-interface-mode-supp.patch b/target/linux/qualcommbe/patches-6.18/0317-net-pcs-qcom-ipq9574-Add-USXGMII-interface-mode-supp.patch
new file mode 100644 (file)
index 0000000..b1cddff
--- /dev/null
@@ -0,0 +1,272 @@
+From 4923ca63214a4e6bbee1b3f8f6b9b79f0fd3a3be Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Fri, 7 Feb 2025 23:53:15 +0800
+Subject: [PATCH] net: pcs: qcom-ipq9574: Add USXGMII interface mode support
+
+USXGMII mode is enabled by PCS when 10Gbps PHYs are connected, such as
+Aquantia 10Gbps PHY.
+
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+---
+ drivers/net/pcs/pcs-qcom-ipq9574.c | 170 +++++++++++++++++++++++++++++
+ 1 file changed, 170 insertions(+)
+
+--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
++++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
+@@ -26,6 +26,7 @@
+ #define PCS_MODE_SEL_MASK             GENMASK(12, 8)
+ #define PCS_MODE_SGMII                        FIELD_PREP(PCS_MODE_SEL_MASK, 0x4)
+ #define PCS_MODE_QSGMII                       FIELD_PREP(PCS_MODE_SEL_MASK, 0x1)
++#define PCS_MODE_XPCS                 FIELD_PREP(PCS_MODE_SEL_MASK, 0x10)
+ #define PCS_MII_CTRL(x)                       (0x480 + 0x18 * (x))
+ #define PCS_MII_ADPT_RESET            BIT(11)
+@@ -54,6 +55,34 @@
+                                        FIELD_PREP(GENMASK(9, 2), \
+                                        FIELD_GET(XPCS_INDIRECT_ADDR_L, reg)))
++#define XPCS_DIG_CTRL                 0x38000
++#define XPCS_USXG_ADPT_RESET          BIT(10)
++#define XPCS_USXG_EN                  BIT(9)
++
++#define XPCS_MII_CTRL                 0x1f0000
++#define XPCS_MII_AN_EN                        BIT(12)
++#define XPCS_DUPLEX_FULL              BIT(8)
++#define XPCS_SPEED_MASK                       (BIT(13) | BIT(6) | BIT(5))
++#define XPCS_SPEED_10000              (BIT(13) | BIT(6))
++#define XPCS_SPEED_5000                       (BIT(13) | BIT(5))
++#define XPCS_SPEED_2500                       BIT(5)
++#define XPCS_SPEED_1000                       BIT(6)
++#define XPCS_SPEED_100                        BIT(13)
++#define XPCS_SPEED_10                 0
++
++#define XPCS_MII_AN_CTRL              0x1f8001
++#define XPCS_MII_AN_8BIT              BIT(8)
++
++#define XPCS_MII_AN_INTR_STS          0x1f8002
++#define XPCS_USXG_AN_LINK_STS         BIT(14)
++#define XPCS_USXG_AN_SPEED_MASK               GENMASK(12, 10)
++#define XPCS_USXG_AN_SPEED_10         0
++#define XPCS_USXG_AN_SPEED_100                1
++#define XPCS_USXG_AN_SPEED_1000               2
++#define XPCS_USXG_AN_SPEED_2500               4
++#define XPCS_USXG_AN_SPEED_5000               5
++#define XPCS_USXG_AN_SPEED_10000      3
++
+ /* Per PCS MII private data */
+ struct ipq_pcs_mii {
+       struct ipq_pcs *qpcs;
+@@ -123,9 +152,54 @@ static void ipq_pcs_get_state_sgmii(stru
+               state->duplex = DUPLEX_HALF;
+ }
++static void ipq_pcs_get_state_usxgmii(struct ipq_pcs *qpcs,
++                                    struct phylink_link_state *state)
++{
++      unsigned int val;
++      int ret;
++
++      ret = regmap_read(qpcs->regmap, XPCS_MII_AN_INTR_STS, &val);
++      if (ret) {
++              state->link = 0;
++              return;
++      }
++
++      state->link = !!(val & XPCS_USXG_AN_LINK_STS);
++
++      if (!state->link)
++              return;
++
++      switch (FIELD_GET(XPCS_USXG_AN_SPEED_MASK, val)) {
++      case XPCS_USXG_AN_SPEED_10000:
++              state->speed = SPEED_10000;
++              break;
++      case XPCS_USXG_AN_SPEED_5000:
++              state->speed = SPEED_5000;
++              break;
++      case XPCS_USXG_AN_SPEED_2500:
++              state->speed = SPEED_2500;
++              break;
++      case XPCS_USXG_AN_SPEED_1000:
++              state->speed = SPEED_1000;
++              break;
++      case XPCS_USXG_AN_SPEED_100:
++              state->speed = SPEED_100;
++              break;
++      case XPCS_USXG_AN_SPEED_10:
++              state->speed = SPEED_10;
++              break;
++      default:
++              state->link = false;
++              return;
++      }
++
++      state->duplex = DUPLEX_FULL;
++}
++
+ static int ipq_pcs_config_mode(struct ipq_pcs *qpcs,
+                              phy_interface_t interface)
+ {
++      unsigned long rate = 125000000;
+       unsigned int val;
+       int ret;
+@@ -137,6 +211,10 @@ static int ipq_pcs_config_mode(struct ip
+       case PHY_INTERFACE_MODE_QSGMII:
+               val = PCS_MODE_QSGMII;
+               break;
++      case PHY_INTERFACE_MODE_USXGMII:
++              val = PCS_MODE_XPCS;
++              rate = 312500000;
++              break;
+       default:
+               return -EOPNOTSUPP;
+       }
+@@ -167,6 +245,21 @@ static int ipq_pcs_config_mode(struct ip
+       qpcs->interface = interface;
++      /* Configure the RX and TX clock to NSSCC as 125M or 312.5M based
++       * on current interface mode.
++       */
++      ret = clk_set_rate(qpcs->rx_hw.clk, rate);
++      if (ret) {
++              dev_err(qpcs->dev, "Failed to set RX clock rate\n");
++              return ret;
++      }
++
++      ret = clk_set_rate(qpcs->tx_hw.clk, rate);
++      if (ret) {
++              dev_err(qpcs->dev, "Failed to set TX clock rate\n");
++              return ret;
++      }
++
+       return 0;
+ }
+@@ -195,6 +288,29 @@ static int ipq_pcs_config_sgmii(struct i
+                              PCS_MII_CTRL(index), PCS_MII_FORCE_MODE);
+ }
++static int ipq_pcs_config_usxgmii(struct ipq_pcs *qpcs)
++{
++      int ret;
++
++      /* Configure the XPCS for USXGMII mode if required */
++      if (qpcs->interface == PHY_INTERFACE_MODE_USXGMII)
++              return 0;
++
++      ret = ipq_pcs_config_mode(qpcs, PHY_INTERFACE_MODE_USXGMII);
++      if (ret)
++              return ret;
++
++      ret = regmap_set_bits(qpcs->regmap, XPCS_DIG_CTRL, XPCS_USXG_EN);
++      if (ret)
++              return ret;
++
++      ret = regmap_set_bits(qpcs->regmap, XPCS_MII_AN_CTRL, XPCS_MII_AN_8BIT);
++      if (ret)
++              return ret;
++
++      return regmap_set_bits(qpcs->regmap, XPCS_MII_CTRL, XPCS_MII_AN_EN);
++}
++
+ static int ipq_pcs_link_up_config_sgmii(struct ipq_pcs *qpcs,
+                                       int index,
+                                       unsigned int neg_mode,
+@@ -237,6 +353,46 @@ static int ipq_pcs_link_up_config_sgmii(
+                              PCS_MII_CTRL(index), PCS_MII_ADPT_RESET);
+ }
++static int ipq_pcs_link_up_config_usxgmii(struct ipq_pcs *qpcs, int speed)
++{
++      unsigned int val;
++      int ret;
++
++      switch (speed) {
++      case SPEED_10000:
++              val = XPCS_SPEED_10000;
++              break;
++      case SPEED_5000:
++              val = XPCS_SPEED_5000;
++              break;
++      case SPEED_2500:
++              val = XPCS_SPEED_2500;
++              break;
++      case SPEED_1000:
++              val = XPCS_SPEED_1000;
++              break;
++      case SPEED_100:
++              val = XPCS_SPEED_100;
++              break;
++      case SPEED_10:
++              val = XPCS_SPEED_10;
++              break;
++      default:
++              dev_err(qpcs->dev, "Invalid USXGMII speed %d\n", speed);
++              return -EINVAL;
++      }
++
++      /* Configure XPCS speed */
++      ret = regmap_update_bits(qpcs->regmap, XPCS_MII_CTRL,
++                               XPCS_SPEED_MASK, val | XPCS_DUPLEX_FULL);
++      if (ret)
++              return ret;
++
++      /* XPCS adapter reset */
++      return regmap_set_bits(qpcs->regmap,
++                             XPCS_DIG_CTRL, XPCS_USXG_ADPT_RESET);
++}
++
+ static int ipq_pcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
+                           const struct phylink_link_state *state)
+ {
+@@ -244,6 +400,11 @@ static int ipq_pcs_validate(struct phyli
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
+               return 0;
++      case PHY_INTERFACE_MODE_USXGMII:
++              /* USXGMII only supports full duplex mode */
++              phylink_clear(supported, 100baseT_Half);
++              phylink_clear(supported, 10baseT_Half);
++              return 0;
+       default:
+               return -EINVAL;
+       }
+@@ -255,6 +416,7 @@ static unsigned int ipq_pcs_inband_caps(
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
++      case PHY_INTERFACE_MODE_USXGMII:
+               return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+       default:
+               return 0;
+@@ -307,6 +469,9 @@ static void ipq_pcs_get_state(struct phy
+       case PHY_INTERFACE_MODE_QSGMII:
+               ipq_pcs_get_state_sgmii(qpcs, index, state);
+               break;
++      case PHY_INTERFACE_MODE_USXGMII:
++              ipq_pcs_get_state_usxgmii(qpcs, state);
++              break;
+       default:
+               break;
+       }
+@@ -333,6 +498,8 @@ static int ipq_pcs_config(struct phylink
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
+               return ipq_pcs_config_sgmii(qpcs, index, neg_mode, interface);
++      case PHY_INTERFACE_MODE_USXGMII:
++              return ipq_pcs_config_usxgmii(qpcs);
+       default:
+               return -EOPNOTSUPP;
+       };
+@@ -354,6 +521,9 @@ static void ipq_pcs_link_up(struct phyli
+               ret = ipq_pcs_link_up_config_sgmii(qpcs, index,
+                                                  neg_mode, speed);
+               break;
++      case PHY_INTERFACE_MODE_USXGMII:
++              ret = ipq_pcs_link_up_config_usxgmii(qpcs, speed);
++              break;
+       default:
+               return;
+       }
diff --git a/target/linux/qualcommbe/patches-6.18/0318-MAINTAINERS-Add-maintainer-for-Qualcomm-IPQ9574-PCS-.patch b/target/linux/qualcommbe/patches-6.18/0318-MAINTAINERS-Add-maintainer-for-Qualcomm-IPQ9574-PCS-.patch
new file mode 100644 (file)
index 0000000..ccdeb9d
--- /dev/null
@@ -0,0 +1,31 @@
+From 34d10a4eb8fea32bb79e3012dc9d8bd2dffb0df3 Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Fri, 7 Feb 2025 23:53:16 +0800
+Subject: [PATCH] MAINTAINERS: Add maintainer for Qualcomm IPQ9574 PCS driver
+
+Add maintainer for the Ethernet PCS driver supported for Qualcomm
+IPQ9574 SoC.
+
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+---
+ MAINTAINERS | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -19116,6 +19116,15 @@ S:    Maintained
+ F:    Documentation/devicetree/bindings/regulator/vqmmc-ipq4019-regulator.yaml
+ F:    drivers/regulator/vqmmc-ipq4019-regulator.c
++QUALCOMM IPQ9574 Ethernet PCS DRIVER
++M:    Lei Wei <quic_leiwei@quicinc.com>
++L:    netdev@vger.kernel.org
++S:    Supported
++F:    Documentation/devicetree/bindings/net/pcs/qcom,ipq9574-pcs.yaml
++F:    drivers/net/pcs/pcs-qcom-ipq9574.c
++F:    include/dt-bindings/net/qcom,ipq9574-pcs.h
++F:    include/linux/pcs/pcs-qcom-ipq9574.h
++
+ QUALCOMM NAND CONTROLLER DRIVER
+ M:    Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ L:    linux-mtd@lists.infradead.org
diff --git a/target/linux/qualcommbe/patches-6.18/0322-arm64-dts-qcom-ipq9574-add-PCS-uniphy-nodes.patch b/target/linux/qualcommbe/patches-6.18/0322-arm64-dts-qcom-ipq9574-add-PCS-uniphy-nodes.patch
new file mode 100644 (file)
index 0000000..8196ff6
--- /dev/null
@@ -0,0 +1,139 @@
+From d6f184181b076cbb54f152994f5bc73ce524a67e Mon Sep 17 00:00:00 2001
+From: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+Date: Sun, 11 May 2025 18:21:00 -0500
+Subject: [PATCH] arm64: dts: qcom: ipq9574: add PCS uniphy nodes
+
+IPQ9574 has three uniphy blocks. IPQ9554 lacks uniphy1. They take
+their system and AHB clocks from NSSCC, and also feed NSSCC with
+the clocks that are intended for the PHYs. This is not a cirular
+dependency. Add nodes for these uniphy blocks, and the clocks they
+feed back to the NSSCC node.
+
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ arch/arm64/boot/dts/qcom/ipq9574.dtsi | 100 ++++++++++++++++++++++++--
+ 1 file changed, 94 insertions(+), 6 deletions(-)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq9574.dtsi
+@@ -9,6 +9,7 @@
+ #include <dt-bindings/clock/qcom,apss-ipq.h>
+ #include <dt-bindings/clock/qcom,ipq-cmn-pll.h>
+ #include <dt-bindings/clock/qcom,ipq9574-gcc.h>
++#include <dt-bindings/clock/qcom,ipq9574-nsscc.h>
+ #include <dt-bindings/interconnect/qcom,ipq9574.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/reset/qcom,ipq9574-gcc.h>
+@@ -1247,12 +1248,12 @@
+                                <&cmn_pll NSS_1200MHZ_CLK>,
+                                <&cmn_pll PPE_353MHZ_CLK>,
+                                <&gcc GPLL0_OUT_AUX>,
+-                               <0>,
+-                               <0>,
+-                               <0>,
+-                               <0>,
+-                               <0>,
+-                               <0>,
++                               <&pcs0 0>,
++                               <&pcs0 1>,
++                               <&pcs1 0>,
++                               <&pcs1 1>,
++                               <&pcs2 0>,
++                               <&pcs2 1>,
+                                <&gcc GCC_NSSCC_CLK>;
+                       clock-names = "xo",
+                                     "nss_1200",
+@@ -1269,6 +1270,93 @@
+                       #reset-cells = <1>;
+                       #interconnect-cells = <1>;
+               };
++
++              pcs0: ethernet-pcs@7a00000 {
++                      compatible = "qcom,ipq9574-pcs";
++                      reg = <0x7a00000 0x10000>;
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      clocks = <&gcc GCC_UNIPHY0_SYS_CLK>,
++                               <&gcc GCC_UNIPHY0_AHB_CLK>;
++                      clock-names = "sys",
++                                    "ahb";
++                      resets = <&gcc GCC_UNIPHY0_XPCS_RESET>;
++                      #clock-cells = <1>;
++
++                      pcs0_ch0: pcs-mii@0 {
++                              reg = <0>;
++                              clocks = <&nsscc NSS_CC_UNIPHY_PORT1_RX_CLK>,
++                                       <&nsscc NSS_CC_UNIPHY_PORT1_TX_CLK>;
++                              clock-names = "rx",
++                                            "tx";
++                      };
++
++                      pcs0_ch1: pcs-mii@1 {
++                              reg = <1>;
++                              clocks = <&nsscc NSS_CC_UNIPHY_PORT2_RX_CLK>,
++                                       <&nsscc NSS_CC_UNIPHY_PORT2_TX_CLK>;
++                              clock-names = "rx",
++                                            "tx";
++                      };
++
++                      pcs0_ch2: pcs-mii@2 {
++                              reg = <2>;
++                              clocks = <&nsscc NSS_CC_UNIPHY_PORT3_RX_CLK>,
++                                       <&nsscc NSS_CC_UNIPHY_PORT3_TX_CLK>;
++                              clock-names = "rx",
++                                            "tx";
++                      };
++
++                      pcs0_ch3: pcs-mii@3 {
++                              reg = <3>;
++                              clocks = <&nsscc NSS_CC_UNIPHY_PORT4_RX_CLK>,
++                                       <&nsscc NSS_CC_UNIPHY_PORT4_TX_CLK>;
++                              clock-names = "rx",
++                                            "tx";
++                      };
++              };
++
++              pcs1: ethernet-pcs@7a10000 {
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      compatible = "qcom,ipq9574-pcs";
++                      reg = <0x7a10000 0x10000>;
++                      clocks = <&gcc GCC_UNIPHY1_SYS_CLK>,
++                               <&gcc GCC_UNIPHY1_AHB_CLK>;
++                      clock-names = "sys",
++                                    "ahb";
++                      resets = <&gcc GCC_UNIPHY1_XPCS_RESET>;
++                      #clock-cells = <1>;
++
++                      pcs1_ch0: pcs-mii@0 {
++                              reg = <0>;
++                              clocks = <&nsscc NSS_CC_UNIPHY_PORT5_RX_CLK>,
++                                       <&nsscc NSS_CC_UNIPHY_PORT5_TX_CLK>;
++                              clock-names = "rx",
++                                            "tx";
++                      };
++              };
++
++              pcs2: ethernet-pcs@7a20000 {
++                      compatible = "qcom,ipq9574-pcs";
++                      reg = <0x7a20000 0x10000>;
++                      #address-cells = <1>;
++                      #size-cells = <0>;
++                      clocks = <&gcc GCC_UNIPHY2_SYS_CLK>,
++                               <&gcc GCC_UNIPHY2_AHB_CLK>;
++                      clock-names = "sys",
++                                    "ahb";
++                      resets = <&gcc GCC_UNIPHY2_XPCS_RESET>;
++                      #clock-cells = <1>;
++
++                      pcs2_ch0: pcs-mii@0 {
++                              reg = <0>;
++                              clocks = <&nsscc NSS_CC_UNIPHY_PORT6_RX_CLK>,
++                                       <&nsscc NSS_CC_UNIPHY_PORT6_TX_CLK>;
++                              clock-names = "rx",
++                                            "tx";
++                      };
++              };
+       };
+       thermal-zones {
diff --git a/target/linux/qualcommbe/patches-6.18/0323-dt-bindings-net-Add-PPE-for-Qualcomm-IPQ9574-SoC.patch b/target/linux/qualcommbe/patches-6.18/0323-dt-bindings-net-Add-PPE-for-Qualcomm-IPQ9574-SoC.patch
new file mode 100644 (file)
index 0000000..89c09ff
--- /dev/null
@@ -0,0 +1,432 @@
+From 48dc6d2fe28865a5c3d271aeb966b984a8085e7c Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 9 Feb 2025 22:29:35 +0800
+Subject: [PATCH] dt-bindings: net: Add PPE for Qualcomm IPQ9574 SoC
+
+The PPE (packet process engine) hardware block is available in Qualcomm
+IPQ chipsets that support PPE architecture, such as IPQ9574. The PPE in
+the IPQ9574 SoC includes six ethernet ports (6 GMAC and 6 XGMAC), which
+are used to connect with external PHY devices by PCS. It includes an L2
+switch function for bridging packets among the 6 ethernet ports and the
+CPU port. The CPU port enables packet transfer between the ethernet
+ports and the ARM cores in the SoC, using the ethernet DMA.
+
+The PPE also includes packet processing offload capabilities for various
+networking functions such as route and bridge flows, VLANs, different
+tunnel protocols and VPN.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ .../bindings/net/qcom,ipq9574-ppe.yaml        | 406 ++++++++++++++++++
+ 1 file changed, 406 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/net/qcom,ipq9574-ppe.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/net/qcom,ipq9574-ppe.yaml
+@@ -0,0 +1,406 @@
++# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/net/qcom,ipq9574-ppe.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Qualcomm IPQ packet process engine (PPE)
++
++maintainers:
++  - Luo Jie <quic_luoj@quicinc.com>
++  - Lei Wei <quic_leiwei@quicinc.com>
++  - Suruchi Agarwal <quic_suruchia@quicinc.com>
++  - Pavithra R <quic_pavir@quicinc.com>>
++
++description:
++  The Ethernet functionality in the PPE (Packet Process Engine) is comprised
++  of three components, the switch core, port wrapper and Ethernet DMA.
++
++  The Switch core in the IPQ9574 PPE has maximum of 6 front panel ports and
++  two FIFO interfaces. One of the two FIFO interfaces is used for Ethernet
++  port to host CPU communication using Ethernet DMA. The other is used
++  communicating to the EIP engine which is used for IPsec offload. On the
++  IPQ9574, the PPE includes 6 GMAC/XGMACs that can be connected with external
++  Ethernet PHY. Switch core also includes BM (Buffer Management), QM (Queue
++  Management) and SCH (Scheduler) modules for supporting the packet processing.
++
++  The port wrapper provides connections from the 6 GMAC/XGMACS to UNIPHY (PCS)
++  supporting various modes such as SGMII/QSGMII/PSGMII/USXGMII/10G-BASER. There
++  are 3 UNIPHY (PCS) instances supported on the IPQ9574.
++
++  Ethernet DMA is used to transmit and receive packets between the six Ethernet
++  ports and ARM host CPU.
++
++  The follow diagram shows the PPE hardware block along with its connectivity
++  to the external hardware blocks such clock hardware blocks (CMNPLL, GCC,
++  NSS clock controller) and ethernet PCS/PHY blocks. For depicting the PHY
++  connectivity, one 4x1 Gbps PHY (QCA8075) and two 10 GBps PHYs are used as an
++  example.
++  - |
++         +---------+
++         |  48 MHZ |
++         +----+----+
++              |(clock)
++              v
++         +----+----+
++  +------| CMN PLL |
++  |      +----+----+
++  |           |(clock)
++  |           v
++  |      +----+----+           +----+----+  (clock) +----+----+
++  |  +---|  NSSCC  |           |   GCC   |--------->|   MDIO  |
++  |  |   +----+----+           +----+----+          +----+----+
++  |  |        |(clock & reset)      |(clock)
++  |  |        v                     v
++  |  |   +-----------------------------+----------+----------+---------+
++  |  |   |       +-----+               |EDMA FIFO |          | EIP FIFO|
++  |  |   |       | SCH |               +----------+          +---------+
++  |  |   |       +-----+                        |              |       |
++  |  |   |  +------+   +------+               +-------------------+    |
++  |  |   |  |  BM  |   |  QM  |  IPQ9574-PPE  |    L2/L3 Process  |    |
++  |  |   |  +------+   +------+               +-------------------+    |
++  |  |   |                                             |               |
++  |  |   | +-------+ +-------+ +-------+ +-------+ +-------+ +-------+ |
++  |  |   | |  MAC0 | |  MAC1 | |  MAC2 | |  MAC3 | | XGMAC4| |XGMAC5 | |
++  |  |   | +---+---+ +---+---+ +---+---+ +---+---+ +---+---+ +---+---+ |
++  |  |   |     |         |         |         |         |         |     |
++  |  |   +-----+---------+---------+---------+---------+---------+-----+
++  |  |         |         |         |         |         |         |
++  |  |     +---+---------+---------+---------+---+ +---+---+ +---+---+
++  +--+---->|                PCS0                 | |  PCS1 | | PCS2  |
++  |(clock) +---+---------+---------+---------+---+ +---+---+ +---+---+
++  |            |         |         |         |         |         |
++  |        +---+---------+---------+---------+---+ +---+---+ +---+---+
++  +------->|             QCA8075 PHY             | | PHY4  | | PHY5  |
++   (clock) +-------------------------------------+ +-------+ +-------+
++
++properties:
++  compatible:
++    enum:
++      - qcom,ipq9574-ppe
++
++  reg:
++    maxItems: 1
++
++  clocks:
++    items:
++      - description: PPE core clock from NSS clock controller
++      - description: PPE APB (Advanced Peripheral Bus) clock from NSS clock controller
++      - description: PPE ingress process engine clock from NSS clock controller
++      - description: PPE BM, QM and scheduler clock from NSS clock controller
++
++  clock-names:
++    items:
++      - const: ppe
++      - const: apb
++      - const: ipe
++      - const: btq
++
++  resets:
++    maxItems: 1
++    description: PPE reset, which is necessary before configuring PPE hardware
++
++  interconnects:
++    items:
++      - description: Clock path leading to PPE switch core function
++      - description: Clock path leading to PPE register access
++      - description: Clock path leading to QoS generation
++      - description: Clock path leading to timeout reference
++      - description: Clock path leading to NSS NOC from memory NOC
++      - description: Clock path leading to memory NOC from NSS NOC
++      - description: Clock path leading to enhanced memory NOC from NSS NOC
++
++  interconnect-names:
++    items:
++      - const: ppe
++      - const: ppe_cfg
++      - const: qos_gen
++      - const: timeout_ref
++      - const: nssnoc_memnoc
++      - const: memnoc_nssnoc
++      - const: memnoc_nssnoc_1
++
++  ethernet-dma:
++    type: object
++    additionalProperties: false
++    description:
++      EDMA (Ethernet DMA) is used to transmit packets between PPE and ARM
++      host CPU. There are 32 TX descriptor rings, 32 TX completion rings,
++      24 RX descriptor rings and 8 RX fill rings supported.
++
++    properties:
++      clocks:
++        items:
++          - description: EDMA system clock from NSS Clock Controller
++          - description: EDMA APB (Advanced Peripheral Bus) clock from
++              NSS Clock Controller
++
++      clock-names:
++        items:
++          - const: sys
++          - const: apb
++
++      resets:
++        maxItems: 1
++        description: EDMA reset from NSS clock controller
++
++      interrupts:
++        minItems: 29
++        maxItems: 57
++
++      interrupt-names:
++        minItems: 29
++        maxItems: 57
++        items:
++          pattern: '^(txcmpl_([0-9]|[1-2][0-9]|3[0-1])|rxdesc_([0-9]|1[0-9]|2[0-3])|misc)$'
++        description:
++          Interrupts "txcmpl_[0-31]" are the Ethernet DMA Tx completion ring interrupts.
++          Interrupts "rxdesc_[0-23]" are the Ethernet DMA Rx Descriptor ring interrupts.
++          Interrupt "misc" is the Ethernet DMA miscellaneous error interrupt.
++
++    required:
++      - clocks
++      - clock-names
++      - resets
++      - interrupts
++      - interrupt-names
++
++required:
++  - compatible
++  - reg
++  - clocks
++  - clock-names
++  - resets
++  - interconnects
++  - interconnect-names
++  - ethernet-dma
++
++allOf:
++  - $ref: ethernet-switch.yaml
++
++unevaluatedProperties: false
++
++examples:
++  - |
++    #include <dt-bindings/clock/qcom,ipq9574-gcc.h>
++    #include <dt-bindings/interconnect/qcom,ipq9574.h>
++    #include <dt-bindings/interrupt-controller/arm-gic.h>
++
++    ethernet-switch@3a000000 {
++        compatible = "qcom,ipq9574-ppe";
++        reg = <0x3a000000 0xbef800>;
++        clocks = <&nsscc 80>,
++                 <&nsscc 79>,
++                 <&nsscc 81>,
++                 <&nsscc 78>;
++        clock-names = "ppe",
++                      "apb",
++                      "ipe",
++                      "btq";
++        resets = <&nsscc 108>;
++        interconnects = <&nsscc MASTER_NSSNOC_PPE &nsscc SLAVE_NSSNOC_PPE>,
++                        <&nsscc MASTER_NSSNOC_PPE_CFG &nsscc SLAVE_NSSNOC_PPE_CFG>,
++                        <&gcc MASTER_NSSNOC_QOSGEN_REF &gcc SLAVE_NSSNOC_QOSGEN_REF>,
++                        <&gcc MASTER_NSSNOC_TIMEOUT_REF &gcc SLAVE_NSSNOC_TIMEOUT_REF>,
++                        <&gcc MASTER_MEM_NOC_NSSNOC &gcc SLAVE_MEM_NOC_NSSNOC>,
++                        <&gcc MASTER_NSSNOC_MEMNOC &gcc SLAVE_NSSNOC_MEMNOC>,
++                        <&gcc MASTER_NSSNOC_MEM_NOC_1 &gcc SLAVE_NSSNOC_MEM_NOC_1>;
++        interconnect-names = "ppe",
++                             "ppe_cfg",
++                             "qos_gen",
++                             "timeout_ref",
++                             "nssnoc_memnoc",
++                             "memnoc_nssnoc",
++                             "memnoc_nssnoc_1";
++
++        ethernet-dma {
++            clocks = <&nsscc 77>,
++                     <&nsscc 76>;
++            clock-names = "sys",
++                          "apb";
++            resets = <&nsscc 0>;
++            interrupts = <GIC_SPI 371 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 372 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 373 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 374 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 375 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 376 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 377 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 378 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 379 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 380 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 381 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 382 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 383 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 384 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 509 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 508 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 507 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 506 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 505 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 504 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 503 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 502 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 501 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 500 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 351 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 352 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 353 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 354 IRQ_TYPE_LEVEL_HIGH>,
++                         <GIC_SPI 499 IRQ_TYPE_LEVEL_HIGH>;
++            interrupt-names = "txcmpl_8",
++                              "txcmpl_9",
++                              "txcmpl_10",
++                              "txcmpl_11",
++                              "txcmpl_12",
++                              "txcmpl_13",
++                              "txcmpl_14",
++                              "txcmpl_15",
++                              "txcmpl_16",
++                              "txcmpl_17",
++                              "txcmpl_18",
++                              "txcmpl_19",
++                              "txcmpl_20",
++                              "txcmpl_21",
++                              "txcmpl_22",
++                              "txcmpl_23",
++                              "txcmpl_24",
++                              "txcmpl_25",
++                              "txcmpl_26",
++                              "txcmpl_27",
++                              "txcmpl_28",
++                              "txcmpl_29",
++                              "txcmpl_30",
++                              "txcmpl_31",
++                              "rxdesc_20",
++                              "rxdesc_21",
++                              "rxdesc_22",
++                              "rxdesc_23",
++                              "misc";
++        };
++
++        ethernet-ports {
++            #address-cells = <1>;
++            #size-cells = <0>;
++
++            port@1 {
++                reg = <1>;
++                phy-mode = "qsgmii";
++                managed = "in-band-status";
++                phy-handle = <&phy0>;
++                pcs-handle = <&pcs0_mii0>;
++                clocks = <&nsscc 33>,
++                         <&nsscc 34>,
++                         <&nsscc 37>;
++                clock-names = "mac",
++                              "rx",
++                              "tx";
++                resets = <&nsscc 29>,
++                         <&nsscc 96>,
++                         <&nsscc 97>;
++                reset-names = "mac",
++                              "rx",
++                              "tx";
++            };
++
++            port@2 {
++                reg = <2>;
++                phy-mode = "qsgmii";
++                managed = "in-band-status";
++                phy-handle = <&phy1>;
++                pcs-handle = <&pcs0_mii1>;
++                clocks = <&nsscc 40>,
++                         <&nsscc 41>,
++                         <&nsscc 44>;
++                clock-names = "mac",
++                              "rx",
++                              "tx";
++                resets = <&nsscc 30>,
++                         <&nsscc 98>,
++                         <&nsscc 99>;
++                reset-names = "mac",
++                              "rx",
++                              "tx";
++            };
++
++            port@3 {
++                reg = <3>;
++                phy-mode = "qsgmii";
++                managed = "in-band-status";
++                phy-handle = <&phy2>;
++                pcs-handle = <&pcs0_mii2>;
++                clocks = <&nsscc 47>,
++                         <&nsscc 48>,
++                         <&nsscc 51>;
++                clock-names = "mac",
++                              "rx",
++                              "tx";
++                resets = <&nsscc 31>,
++                         <&nsscc 100>,
++                         <&nsscc 101>;
++                reset-names = "mac",
++                              "rx",
++                              "tx";
++            };
++
++            port@4 {
++                reg = <4>;
++                phy-mode = "qsgmii";
++                managed = "in-band-status";
++                phy-handle = <&phy3>;
++                pcs-handle = <&pcs0_mii3>;
++                clocks = <&nsscc 54>,
++                         <&nsscc 55>,
++                         <&nsscc 58>;
++                clock-names = "mac",
++                              "rx",
++                              "tx";
++                resets = <&nsscc 32>,
++                         <&nsscc 102>,
++                         <&nsscc 103>;
++                reset-names = "mac",
++                              "rx",
++                              "tx";
++            };
++
++            port@5 {
++                reg = <5>;
++                phy-mode = "usxgmii";
++                managed = "in-band-status";
++                phy-handle = <&phy4>;
++                pcs-handle = <&pcs1_mii0>;
++                clocks = <&nsscc 61>,
++                         <&nsscc 62>,
++                         <&nsscc 65>;
++                clock-names = "mac",
++                              "rx",
++                              "tx";
++                resets = <&nsscc 33>,
++                         <&nsscc 104>,
++                         <&nsscc 105>;
++                reset-names = "mac",
++                              "rx",
++                              "tx";
++            };
++
++            port@6 {
++                reg = <6>;
++                phy-mode = "usxgmii";
++                managed = "in-band-status";
++                phy-handle = <&phy5>;
++                pcs-handle = <&pcs2_mii0>;
++                clocks = <&nsscc 68>,
++                         <&nsscc 69>,
++                         <&nsscc 72>;
++                clock-names = "mac",
++                              "rx",
++                              "tx";
++                resets = <&nsscc 34>,
++                         <&nsscc 106>,
++                         <&nsscc 107>;
++                reset-names = "mac",
++                              "rx",
++                              "tx";
++            };
++        };
++    };
diff --git a/target/linux/qualcommbe/patches-6.18/0324-docs-networking-Add-PPE-driver-documentation-for-Qua.patch b/target/linux/qualcommbe/patches-6.18/0324-docs-networking-Add-PPE-driver-documentation-for-Qua.patch
new file mode 100644 (file)
index 0000000..429006c
--- /dev/null
@@ -0,0 +1,227 @@
+From 9973b6610830146af1a12fe02d2d6440eb80b0f9 Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Sun, 9 Feb 2025 22:29:36 +0800
+Subject: [PATCH] docs: networking: Add PPE driver documentation for Qualcomm
+ IPQ9574 SoC
+
+Add description and high-level diagram for PPE, driver overview and
+module enable/debug information.
+
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ .../device_drivers/ethernet/index.rst         |   1 +
+ .../ethernet/qualcomm/ppe/ppe.rst             | 197 ++++++++++++++++++
+ 2 files changed, 198 insertions(+)
+ create mode 100644 Documentation/networking/device_drivers/ethernet/qualcomm/ppe/ppe.rst
+
+--- a/Documentation/networking/device_drivers/ethernet/index.rst
++++ b/Documentation/networking/device_drivers/ethernet/index.rst
+@@ -49,6 +49,7 @@ Contents:
+    neterion/s2io
+    netronome/nfp
+    pensando/ionic
++   qualcomm/ppe/ppe
+    smsc/smc9
+    stmicro/stmmac
+    ti/cpsw
+--- /dev/null
++++ b/Documentation/networking/device_drivers/ethernet/qualcomm/ppe/ppe.rst
+@@ -0,0 +1,197 @@
++.. SPDX-License-Identifier: GPL-2.0
++
++===============================================
++PPE Ethernet Driver for Qualcomm IPQ SoC Family
++===============================================
++
++Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
++
++Author: Lei Wei <quic_leiwei@quicinc.com>
++
++
++Contents
++========
++
++- `PPE Overview`_
++- `PPE Driver Overview`_
++- `PPE Driver Supported SoCs`_
++- `Enabling the Driver`_
++- `Debugging`_
++
++
++PPE Overview
++============
++
++IPQ (Qualcomm Internet Processor) SoC (System-on-Chip) series is Qualcomm's series of
++networking SoC for Wi-Fi access points. The PPE (Packet Process Engine) is the Ethernet
++packet process engine in the IPQ SoC.
++
++Below is a simplified hardware diagram of IPQ9574 SoC which includes the PPE engine and
++other blocks which are in the SoC but outside the PPE engine. These blocks work together
++to enable the Ethernet for the IPQ SoC::
++
++             +------+ +------+ +------+ +------+ +------+  +------+ start +-------+
++             |netdev| |netdev| |netdev| |netdev| |netdev|  |netdev|<------|PHYLINK|
++             +------+ +------+ +------+ +------+ +------+  +------+ stop  +-+-+-+-+
++                                           |                                | | ^
++ +-------+   +-------------------------+--------+----------------------+    | | |
++ | GCC   |   |                         |  EDMA  |                      |    | | |
++ +---+---+   |  PPE                    +---+----+                      |    | | |
++     | clk   |                             |                           |    | | |
++     +------>| +-----------------------+------+-----+---------------+  |    | | |
++             | |   Switch Core         |Port0 |     |Port7(EIP FIFO)|  |    | | |
++             | |                       +---+--+     +------+--------+  |    | | |
++             | |                           |               |        |  |    | | |
++ +-------+   | |                    +------+---------------+----+   |  |    | | |
++ |CMN PLL|   | | +---+ +---+ +----+ | +--------+                |   |  |    | | |
++ +---+---+   | | |BM | |QM | |SCH | | | L2/L3  |  .......       |   |  |    | | |
++ |   |       | | +---+ +---+ +----+ | +--------+                |   |  |    | | |
++ |   |       | |                    +------+--------------------+   |  |    | | |
++ |   |       | |                           |                        |  |    | | |
++ |   v       | | +-----+-+-----+-+-----+-+-+---+--+-----+-+-----+   |  |    | | |
++ | +------+  | | |Port1| |Port2| |Port3| |Port4|  |Port5| |Port6|   |  |    | | |
++ | |NSSCC |  | | +-----+ +-----+ +-----+ +-----+  +-----+ +-----+   |  | mac| | |
++ | +-+-+--+  | | |MAC0 | |MAC1 | |MAC2 | |MAC3 |  |MAC4 | |MAC5 |   |  |<---+ | |
++ | ^ | |clk  | | +-----+-+-----+-+-----+-+-----+--+-----+-+-----+   |  | ops  | |
++ | | | +---->| +----|------|-------|-------|---------|--------|-----+  |      | |
++ | | |       +---------------------------------------------------------+      | |
++ | | |              |      |       |       |         |        |               | |
++ | | |   MII clk    |      QSGMII               USXGMII   USXGMII             | |
++ | | +------------->|      |       |       |         |        |               | |
++ | |              +-------------------------+ +---------+ +---------+         | |
++ | |125/312.5M clk|       (PCS0)            | | (PCS1)  | | (PCS2)  | pcs ops | |
++ | +--------------+       UNIPHY0           | | UNIPHY1 | | UNIPHY2 |<--------+ |
++ +--------------->|                         | |         | |         |           |
++ | 31.25M ref clk +-------------------------+ +---------+ +---------+           |
++ |                   |     |      |      |          |          |                |
++ |              +-----------------------------------------------------+         |
++ |25/50M ref clk| +-------------------------+    +------+   +------+  | link    |
++ +------------->| |      QUAD PHY           |    | PHY4 |   | PHY5 |  |---------+
++                | +-------------------------+    +------+   +------+  | change
++                |                                                     |
++                |                       MDIO bus                      |
++                +-----------------------------------------------------+
++
++The CMN (Common) PLL, NSSCC (Networking Sub System Clock Controller) and GCC (Global
++Clock Controller) blocks are in the SoC and act as clock providers.
++
++The UNIPHY block is in the SoC and provides the PCS (Physical Coding Sublayer) and
++XPCS (10-Gigabit Physical Coding Sublayer) functions to support different interface
++modes between the PPE MAC and the external PHY.
++
++This documentation focuses on the descriptions of PPE engine and the PPE driver.
++
++The Ethernet functionality in the PPE (Packet Process Engine) is comprised of three
++components: the switch core, port wrapper and Ethernet DMA.
++
++The Switch core in the IPQ9574 PPE has maximum of 6 front panel ports and two FIFO
++interfaces. One of the two FIFO interfaces is used for Ethernet port to host CPU
++communication using Ethernet DMA. The other is used communicating to the EIP engine
++which is used for IPsec offload. On the IPQ9574, the PPE includes 6 GMAC/XGMACs that
++can be connected with external Ethernet PHY. Switch core also includes BM (Buffer
++Management), QM (Queue Management) and SCH (Scheduler) modules for supporting the
++packet processing.
++
++The port wrapper provides connections from the 6 GMAC/XGMACS to UNIPHY (PCS) supporting
++various modes such as SGMII/QSGMII/PSGMII/USXGMII/10G-BASER. There are 3 UNIPHY (PCS)
++instances supported on the IPQ9574.
++
++Ethernet DMA is used to transmit and receive packets between the Ethernet subsystem
++and ARM host CPU.
++
++The following lists the main blocks in the PPE engine which will be driven by this
++PPE driver:
++
++- BM
++    BM is the hardware buffer manager for the PPE switch ports.
++- QM
++    Queue Manager for managing the egress hardware queues of the PPE switch ports.
++- SCH
++    The scheduler which manages the hardware traffic scheduling for the PPE switch ports.
++- L2
++    The L2 block performs the packet bridging in the switch core. The bridge domain is
++    represented by the VSI (Virtual Switch Instance) domain in PPE. FDB learning can be
++    enabled based on the VSI domain and bridge forwarding occurs within the VSI domain.
++- MAC
++    The PPE in the IPQ9574 supports up to six MACs (MAC0 to MAC5) which are corresponding
++    to six switch ports (port1 to port6). The MAC block is connected with external PHY
++    through the UNIPHY PCS block. Each MAC block includes the GMAC and XGMAC blocks and
++    the switch port can select to use GMAC or XMAC through a MUX selection according to
++    the external PHY's capability.
++- EDMA (Ethernet DMA)
++    The Ethernet DMA is used to transmit and receive Ethernet packets between the PPE
++    ports and the ARM cores.
++
++The received packet on a PPE MAC port can be forwarded to another PPE MAC port. It can
++be also forwarded to internal switch port0 so that the packet can be delivered to the
++ARM cores using the Ethernet DMA (EDMA) engine. The Ethernet DMA driver will deliver the
++packet to the corresponding 'netdevice' interface.
++
++The software instantiations of the PPE MAC (netdevice), PCS and external PHYs interact
++with the Linux PHYLINK framework to manage the connectivity between the PPE ports and
++the connected PHYs, and the port link states. This is also illustrated in above diagram.
++
++
++PPE Driver Overview
++===================
++PPE driver is Ethernet driver for the Qualcomm IPQ SoC. It is a single platform driver
++which includes the PPE part and Ethernet DMA part. The PPE part initializes and drives the
++various blocks in PPE switch core such as BM/QM/L2 blocks and the PPE MACs. The EDMA part
++drives the Ethernet DMA for packet transfer between PPE ports and ARM cores, and enables
++the netdevice driver for the PPE ports.
++
++The PPE driver files in drivers/net/ethernet/qualcomm/ppe/ are listed as below:
++
++- Makefile
++- ppe.c
++- ppe.h
++- ppe_config.c
++- ppe_config.h
++- ppe_debugfs.c
++- ppe_debugfs.h
++- ppe_regs.h
++
++The ppe.c file contains the main PPE platform driver and undertakes the initialization of
++PPE switch core blocks such as QM, BM and L2. The configuration APIs for these hardware
++blocks are provided in the ppe_config.c file.
++
++The ppe.h defines the PPE device data structure which will be used by PPE driver functions.
++
++The ppe_debugfs.c enables the PPE statistics counters such as PPE port Rx and Tx counters,
++CPU code counters and queue counters.
++
++
++PPE Driver Supported SoCs
++=========================
++
++The PPE driver supports the following IPQ SoC:
++
++- IPQ9574
++
++
++Enabling the Driver
++===================
++
++The driver is located in the menu structure at:
++
++  -> Device Drivers
++    -> Network device support (NETDEVICES [=y])
++      -> Ethernet driver support
++        -> Qualcomm devices
++          -> Qualcomm Technologies, Inc. PPE Ethernet support
++
++If this driver is built as a module, we can use below commands to install and remove it:
++
++- insmod qcom-ppe.ko
++- rmmod qcom-ppe.ko
++
++The PPE driver functionally depends on the CMN PLL and NSSCC clock controller drivers.
++Please make sure the dependent modules are installed before installing the PPE driver
++module.
++
++
++Debugging
++=========
++
++The PPE hardware counters are available in the debugfs and can be checked by the command
++``cat /sys/kernel/debug/ppe/packet_counters``.
diff --git a/target/linux/qualcommbe/patches-6.18/0325-net-ethernet-qualcomm-Add-PPE-driver-for-IPQ9574-SoC.patch b/target/linux/qualcommbe/patches-6.18/0325-net-ethernet-qualcomm-Add-PPE-driver-for-IPQ9574-SoC.patch
new file mode 100644 (file)
index 0000000..f55879a
--- /dev/null
@@ -0,0 +1,339 @@
+From d1158f0282304c89217894aa346fc45364b95542 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 9 Feb 2025 22:29:37 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Add PPE driver for IPQ9574 SoC
+
+The PPE (Packet Process Engine) hardware block is available
+on Qualcomm IPQ SoC that support PPE architecture, such as
+IPQ9574.
+
+The PPE in IPQ9574 includes six integrated ethernet MAC
+(for 6 PPE ports), buffer management, queue management and
+scheduler functions. The MACs can connect with the external
+PHY or switch devices using the UNIPHY PCS block available
+in the SoC.
+
+The PPE also includes various packet processing offload
+capabilities such as L3 routing and L2 bridging, VLAN and
+tunnel processing offload. It also includes Ethernet DMA
+function for transferring packets between ARM cores and
+PPE ethernet ports.
+
+This patch adds the base source files and Makefiles for
+the PPE driver such as platform driver registration,
+clock initialization, and PPE reset routines.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ drivers/net/ethernet/qualcomm/Kconfig      |  15 ++
+ drivers/net/ethernet/qualcomm/Makefile     |   1 +
+ drivers/net/ethernet/qualcomm/ppe/Makefile |   7 +
+ drivers/net/ethernet/qualcomm/ppe/ppe.c    | 218 +++++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/ppe.h    |  36 ++++
+ 5 files changed, 277 insertions(+)
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/Makefile
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/ppe.c
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/ppe.h
+
+--- a/drivers/net/ethernet/qualcomm/Kconfig
++++ b/drivers/net/ethernet/qualcomm/Kconfig
+@@ -61,6 +61,21 @@ config QCOM_EMAC
+         low power, Receive-Side Scaling (RSS), and IEEE 1588-2008
+         Precision Clock Synchronization Protocol.
++config QCOM_PPE
++      tristate "Qualcomm Technologies, Inc. PPE Ethernet support"
++      depends on HAS_IOMEM && OF
++      depends on COMMON_CLK
++      select REGMAP_MMIO
++      help
++        This driver supports the Qualcomm Technologies, Inc. packet
++        process engine (PPE) available with IPQ SoC. The PPE includes
++        the ethernet MACs, Ethernet DMA (EDMA) and switch core that
++        supports L3 flow offload, L2 switch function, RSS and tunnel
++        offload.
++
++        To compile this driver as a module, choose M here. The module
++        will be called qcom-ppe.
++
+ source "drivers/net/ethernet/qualcomm/rmnet/Kconfig"
+ endif # NET_VENDOR_QUALCOMM
+--- a/drivers/net/ethernet/qualcomm/Makefile
++++ b/drivers/net/ethernet/qualcomm/Makefile
+@@ -11,4 +11,5 @@ qcauart-objs := qca_uart.o
+ obj-y += emac/
++obj-$(CONFIG_QCOM_PPE) += ppe/
+ obj-$(CONFIG_RMNET) += rmnet/
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/Makefile
+@@ -0,0 +1,7 @@
++# SPDX-License-Identifier: GPL-2.0-only
++#
++# Makefile for the device driver of PPE (Packet Process Engine) in IPQ SoC
++#
++
++obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o
++qcom-ppe-objs := ppe.o
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe.c
+@@ -0,0 +1,218 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* PPE platform device probe, DTSI parser and PPE clock initializations. */
++
++#include <linux/clk.h>
++#include <linux/interconnect.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/regmap.h>
++#include <linux/reset.h>
++
++#include "ppe.h"
++
++#define PPE_PORT_MAX          8
++#define PPE_CLK_RATE          353000000
++
++/* ICC clocks for enabling PPE device. The avg_bw and peak_bw with value 0
++ * will be updated by the clock rate of PPE.
++ */
++static const struct icc_bulk_data ppe_icc_data[] = {
++      {
++              .name = "ppe",
++              .avg_bw = 0,
++              .peak_bw = 0,
++      },
++      {
++              .name = "ppe_cfg",
++              .avg_bw = 0,
++              .peak_bw = 0,
++      },
++      {
++              .name = "qos_gen",
++              .avg_bw = 6000,
++              .peak_bw = 6000,
++      },
++      {
++              .name = "timeout_ref",
++              .avg_bw = 6000,
++              .peak_bw = 6000,
++      },
++      {
++              .name = "nssnoc_memnoc",
++              .avg_bw = 533333,
++              .peak_bw = 533333,
++      },
++      {
++              .name = "memnoc_nssnoc",
++              .avg_bw = 533333,
++              .peak_bw = 533333,
++      },
++      {
++              .name = "memnoc_nssnoc_1",
++              .avg_bw = 533333,
++              .peak_bw = 533333,
++      },
++};
++
++static const struct regmap_range ppe_readable_ranges[] = {
++      regmap_reg_range(0x0, 0x1ff),           /* Global */
++      regmap_reg_range(0x400, 0x5ff),         /* LPI CSR */
++      regmap_reg_range(0x1000, 0x11ff),       /* GMAC0 */
++      regmap_reg_range(0x1200, 0x13ff),       /* GMAC1 */
++      regmap_reg_range(0x1400, 0x15ff),       /* GMAC2 */
++      regmap_reg_range(0x1600, 0x17ff),       /* GMAC3 */
++      regmap_reg_range(0x1800, 0x19ff),       /* GMAC4 */
++      regmap_reg_range(0x1a00, 0x1bff),       /* GMAC5 */
++      regmap_reg_range(0xb000, 0xefff),       /* PRX CSR */
++      regmap_reg_range(0xf000, 0x1efff),      /* IPE */
++      regmap_reg_range(0x20000, 0x5ffff),     /* PTX CSR */
++      regmap_reg_range(0x60000, 0x9ffff),     /* IPE L2 CSR */
++      regmap_reg_range(0xb0000, 0xeffff),     /* IPO CSR */
++      regmap_reg_range(0x100000, 0x17ffff),   /* IPE PC */
++      regmap_reg_range(0x180000, 0x1bffff),   /* PRE IPO CSR */
++      regmap_reg_range(0x1d0000, 0x1dffff),   /* Tunnel parser */
++      regmap_reg_range(0x1e0000, 0x1effff),   /* Ingress parse */
++      regmap_reg_range(0x200000, 0x2fffff),   /* IPE L3 */
++      regmap_reg_range(0x300000, 0x3fffff),   /* IPE tunnel */
++      regmap_reg_range(0x400000, 0x4fffff),   /* Scheduler */
++      regmap_reg_range(0x500000, 0x503fff),   /* XGMAC0 */
++      regmap_reg_range(0x504000, 0x507fff),   /* XGMAC1 */
++      regmap_reg_range(0x508000, 0x50bfff),   /* XGMAC2 */
++      regmap_reg_range(0x50c000, 0x50ffff),   /* XGMAC3 */
++      regmap_reg_range(0x510000, 0x513fff),   /* XGMAC4 */
++      regmap_reg_range(0x514000, 0x517fff),   /* XGMAC5 */
++      regmap_reg_range(0x600000, 0x6fffff),   /* BM */
++      regmap_reg_range(0x800000, 0x9fffff),   /* QM */
++      regmap_reg_range(0xb00000, 0xbef800),   /* EDMA */
++};
++
++static const struct regmap_access_table ppe_reg_table = {
++      .yes_ranges = ppe_readable_ranges,
++      .n_yes_ranges = ARRAY_SIZE(ppe_readable_ranges),
++};
++
++static const struct regmap_config regmap_config_ipq9574 = {
++      .reg_bits = 32,
++      .reg_stride = 4,
++      .val_bits = 32,
++      .rd_table = &ppe_reg_table,
++      .wr_table = &ppe_reg_table,
++      .max_register = 0xbef800,
++      .fast_io = true,
++};
++
++static int ppe_clock_init_and_reset(struct ppe_device *ppe_dev)
++{
++      unsigned long ppe_rate = ppe_dev->clk_rate;
++      struct device *dev = ppe_dev->dev;
++      struct reset_control *rstc;
++      struct clk_bulk_data *clks;
++      struct clk *clk;
++      int ret, i;
++
++      for (i = 0; i < ppe_dev->num_icc_paths; i++) {
++              ppe_dev->icc_paths[i].name = ppe_icc_data[i].name;
++              ppe_dev->icc_paths[i].avg_bw = ppe_icc_data[i].avg_bw ? :
++                                             Bps_to_icc(ppe_rate);
++              ppe_dev->icc_paths[i].peak_bw = ppe_icc_data[i].peak_bw ? :
++                                              Bps_to_icc(ppe_rate);
++      }
++
++      ret = devm_of_icc_bulk_get(dev, ppe_dev->num_icc_paths,
++                                 ppe_dev->icc_paths);
++      if (ret)
++              return ret;
++
++      ret = icc_bulk_set_bw(ppe_dev->num_icc_paths, ppe_dev->icc_paths);
++      if (ret)
++              return ret;
++
++      /* The PPE clocks have a common parent clock. Setting the clock
++       * rate of "ppe" ensures the clock rate of all PPE clocks is
++       * configured to the same rate.
++       */
++      clk = devm_clk_get(dev, "ppe");
++      if (IS_ERR(clk))
++              return PTR_ERR(clk);
++
++      ret = clk_set_rate(clk, ppe_rate);
++      if (ret)
++              return ret;
++
++      ret = devm_clk_bulk_get_all_enable(dev, &clks);
++      if (ret < 0)
++              return ret;
++
++      /* Reset the PPE. */
++      rstc = devm_reset_control_get_exclusive(dev, NULL);
++      if (IS_ERR(rstc))
++              return PTR_ERR(rstc);
++
++      ret = reset_control_assert(rstc);
++      if (ret)
++              return ret;
++
++      /* The delay 10 ms of assert is necessary for resetting PPE. */
++      usleep_range(10000, 11000);
++
++      return reset_control_deassert(rstc);
++}
++
++static int qcom_ppe_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct ppe_device *ppe_dev;
++      void __iomem *base;
++      int ret, num_icc;
++
++      num_icc = ARRAY_SIZE(ppe_icc_data);
++      ppe_dev = devm_kzalloc(dev, struct_size(ppe_dev, icc_paths, num_icc),
++                             GFP_KERNEL);
++      if (!ppe_dev)
++              return -ENOMEM;
++
++      base = devm_platform_ioremap_resource(pdev, 0);
++      if (IS_ERR(base))
++              return dev_err_probe(dev, PTR_ERR(base), "PPE ioremap failed\n");
++
++      ppe_dev->regmap = devm_regmap_init_mmio(dev, base, &regmap_config_ipq9574);
++      if (IS_ERR(ppe_dev->regmap))
++              return dev_err_probe(dev, PTR_ERR(ppe_dev->regmap),
++                                   "PPE initialize regmap failed\n");
++      ppe_dev->dev = dev;
++      ppe_dev->clk_rate = PPE_CLK_RATE;
++      ppe_dev->num_ports = PPE_PORT_MAX;
++      ppe_dev->num_icc_paths = num_icc;
++
++      ret = ppe_clock_init_and_reset(ppe_dev);
++      if (ret)
++              return dev_err_probe(dev, ret, "PPE clock config failed\n");
++
++      platform_set_drvdata(pdev, ppe_dev);
++
++      return 0;
++}
++
++static const struct of_device_id qcom_ppe_of_match[] = {
++      { .compatible = "qcom,ipq9574-ppe" },
++      {}
++};
++MODULE_DEVICE_TABLE(of, qcom_ppe_of_match);
++
++static struct platform_driver qcom_ppe_driver = {
++      .driver = {
++              .name = "qcom_ppe",
++              .of_match_table = qcom_ppe_of_match,
++      },
++      .probe  = qcom_ppe_probe,
++};
++module_platform_driver(qcom_ppe_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Qualcomm Technologies, Inc. IPQ PPE driver");
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe.h
+@@ -0,0 +1,36 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ *
++ * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef __PPE_H__
++#define __PPE_H__
++
++#include <linux/compiler.h>
++#include <linux/interconnect.h>
++
++struct device;
++struct regmap;
++
++/**
++ * struct ppe_device - PPE device private data.
++ * @dev: PPE device structure.
++ * @regmap: PPE register map.
++ * @clk_rate: PPE clock rate.
++ * @num_ports: Number of PPE ports.
++ * @num_icc_paths: Number of interconnect paths.
++ * @icc_paths: Interconnect path array.
++ *
++ * PPE device is the instance of PPE hardware, which is used to
++ * configure PPE packet process modules such as BM (buffer management),
++ * QM (queue management), and scheduler.
++ */
++struct ppe_device {
++      struct device *dev;
++      struct regmap *regmap;
++      unsigned long clk_rate;
++      unsigned int num_ports;
++      unsigned int num_icc_paths;
++      struct icc_bulk_data icc_paths[] __counted_by(num_icc_paths);
++};
++#endif
diff --git a/target/linux/qualcommbe/patches-6.18/0326-net-ethernet-qualcomm-Initialize-PPE-buffer-manageme.patch b/target/linux/qualcommbe/patches-6.18/0326-net-ethernet-qualcomm-Initialize-PPE-buffer-manageme.patch
new file mode 100644 (file)
index 0000000..5f38e25
--- /dev/null
@@ -0,0 +1,328 @@
+From 6e639ab45348ee7a697db8b481fa6f8555280f58 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 9 Feb 2025 22:29:38 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Initialize PPE buffer management for
+ IPQ9574
+
+The BM (Buffer Management) config controls the pause frame generated
+on the PPE port. There are maximum 15 BM ports and 4 groups supported,
+all BM ports are assigned to group 0 by default. The number of hardware
+buffers configured for the port influence the threshold of the flow
+control for that port.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/Makefile    |   2 +-
+ drivers/net/ethernet/qualcomm/ppe/ppe.c       |   5 +
+ .../net/ethernet/qualcomm/ppe/ppe_config.c    | 195 ++++++++++++++++++
+ .../net/ethernet/qualcomm/ppe/ppe_config.h    |  12 ++
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h  |  59 ++++++
+ 5 files changed, 272 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/ppe_config.c
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/ppe_config.h
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+
+--- a/drivers/net/ethernet/qualcomm/ppe/Makefile
++++ b/drivers/net/ethernet/qualcomm/ppe/Makefile
+@@ -4,4 +4,4 @@
+ #
+ obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o
+-qcom-ppe-objs := ppe.o
++qcom-ppe-objs := ppe.o ppe_config.o
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe.c
+@@ -15,6 +15,7 @@
+ #include <linux/reset.h>
+ #include "ppe.h"
++#include "ppe_config.h"
+ #define PPE_PORT_MAX          8
+ #define PPE_CLK_RATE          353000000
+@@ -194,6 +195,10 @@ static int qcom_ppe_probe(struct platfor
+       if (ret)
+               return dev_err_probe(dev, ret, "PPE clock config failed\n");
++      ret = ppe_hw_config(ppe_dev);
++      if (ret)
++              return dev_err_probe(dev, ret, "PPE HW config failed\n");
++
+       platform_set_drvdata(pdev, ppe_dev);
+       return 0;
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
+@@ -0,0 +1,195 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* PPE HW initialization configs such as BM(buffer management),
++ * QM(queue management) and scheduler configs.
++ */
++
++#include <linux/bitfield.h>
++#include <linux/bits.h>
++#include <linux/device.h>
++#include <linux/regmap.h>
++
++#include "ppe.h"
++#include "ppe_config.h"
++#include "ppe_regs.h"
++
++/**
++ * struct ppe_bm_port_config - PPE BM port configuration.
++ * @port_id_start: The fist BM port ID to configure.
++ * @port_id_end: The last BM port ID to configure.
++ * @pre_alloc: BM port dedicated buffer number.
++ * @in_fly_buf: Buffer number for receiving the packet after pause frame sent.
++ * @ceil: Ceil to generate the back pressure.
++ * @weight: Weight value.
++ * @resume_offset: Resume offset from the threshold value.
++ * @resume_ceil: Ceil to resume from the back pressure state.
++ * @dynamic: Dynamic threshold used or not.
++ *
++ * The is for configuring the threshold that impacts the port
++ * flow control.
++ */
++struct ppe_bm_port_config {
++      unsigned int port_id_start;
++      unsigned int port_id_end;
++      unsigned int pre_alloc;
++      unsigned int in_fly_buf;
++      unsigned int ceil;
++      unsigned int weight;
++      unsigned int resume_offset;
++      unsigned int resume_ceil;
++      bool dynamic;
++};
++
++/* Assign the share buffer number 1550 to group 0 by default. */
++static const int ipq9574_ppe_bm_group_config = 1550;
++
++/* The buffer configurations per PPE port. There are 15 BM ports and
++ * 4 BM groups supported by PPE. BM port (0-7) is for EDMA port 0,
++ * BM port (8-13) is for PPE physical port 1-6 and BM port 14 is for
++ * EIP port.
++ */
++static const struct ppe_bm_port_config ipq9574_ppe_bm_port_config[] = {
++      {
++              /* Buffer configuration for the BM port ID 0 of EDMA. */
++              .port_id_start  = 0,
++              .port_id_end    = 0,
++              .pre_alloc      = 0,
++              .in_fly_buf     = 100,
++              .ceil           = 1146,
++              .weight         = 7,
++              .resume_offset  = 8,
++              .resume_ceil    = 0,
++              .dynamic        = true,
++      },
++      {
++              /* Buffer configuration for the BM port ID 1-7 of EDMA. */
++              .port_id_start  = 1,
++              .port_id_end    = 7,
++              .pre_alloc      = 0,
++              .in_fly_buf     = 100,
++              .ceil           = 250,
++              .weight         = 4,
++              .resume_offset  = 36,
++              .resume_ceil    = 0,
++              .dynamic        = true,
++      },
++      {
++              /* Buffer configuration for the BM port ID 8-13 of PPE ports. */
++              .port_id_start  = 8,
++              .port_id_end    = 13,
++              .pre_alloc      = 0,
++              .in_fly_buf     = 128,
++              .ceil           = 250,
++              .weight         = 4,
++              .resume_offset  = 36,
++              .resume_ceil    = 0,
++              .dynamic        = true,
++      },
++      {
++              /* Buffer configuration for the BM port ID 14 of EIP. */
++              .port_id_start  = 14,
++              .port_id_end    = 14,
++              .pre_alloc      = 0,
++              .in_fly_buf     = 40,
++              .ceil           = 250,
++              .weight         = 4,
++              .resume_offset  = 36,
++              .resume_ceil    = 0,
++              .dynamic        = true,
++      },
++};
++
++static int ppe_config_bm_threshold(struct ppe_device *ppe_dev, int bm_port_id,
++                                 const struct ppe_bm_port_config port_cfg)
++{
++      u32 reg, val, bm_fc_val[2];
++      int ret;
++
++      reg = PPE_BM_PORT_FC_CFG_TBL_ADDR + PPE_BM_PORT_FC_CFG_TBL_INC * bm_port_id;
++      ret = regmap_bulk_read(ppe_dev->regmap, reg,
++                             bm_fc_val, ARRAY_SIZE(bm_fc_val));
++      if (ret)
++              return ret;
++
++      /* Configure BM flow control related threshold. */
++      PPE_BM_PORT_FC_SET_WEIGHT(bm_fc_val, port_cfg.weight);
++      PPE_BM_PORT_FC_SET_RESUME_OFFSET(bm_fc_val, port_cfg.resume_offset);
++      PPE_BM_PORT_FC_SET_RESUME_THRESHOLD(bm_fc_val, port_cfg.resume_ceil);
++      PPE_BM_PORT_FC_SET_DYNAMIC(bm_fc_val, port_cfg.dynamic);
++      PPE_BM_PORT_FC_SET_REACT_LIMIT(bm_fc_val, port_cfg.in_fly_buf);
++      PPE_BM_PORT_FC_SET_PRE_ALLOC(bm_fc_val, port_cfg.pre_alloc);
++
++      /* Configure low/high bits of the ceiling for the BM port. */
++      val = FIELD_GET(GENMASK(2, 0), port_cfg.ceil);
++      PPE_BM_PORT_FC_SET_CEILING_LOW(bm_fc_val, val);
++      val = FIELD_GET(GENMASK(10, 3), port_cfg.ceil);
++      PPE_BM_PORT_FC_SET_CEILING_HIGH(bm_fc_val, val);
++
++      ret = regmap_bulk_write(ppe_dev->regmap, reg,
++                              bm_fc_val, ARRAY_SIZE(bm_fc_val));
++      if (ret)
++              return ret;
++
++      /* Assign the default group ID 0 to the BM port. */
++      val = FIELD_PREP(PPE_BM_PORT_GROUP_ID_SHARED_GROUP_ID, 0);
++      reg = PPE_BM_PORT_GROUP_ID_ADDR + PPE_BM_PORT_GROUP_ID_INC * bm_port_id;
++      ret = regmap_update_bits(ppe_dev->regmap, reg,
++                               PPE_BM_PORT_GROUP_ID_SHARED_GROUP_ID,
++                               val);
++      if (ret)
++              return ret;
++
++      /* Enable BM port flow control. */
++      reg = PPE_BM_PORT_FC_MODE_ADDR + PPE_BM_PORT_FC_MODE_INC * bm_port_id;
++
++      return regmap_set_bits(ppe_dev->regmap, reg, PPE_BM_PORT_FC_MODE_EN);
++}
++
++/* Configure the buffer threshold for the port flow control function. */
++static int ppe_config_bm(struct ppe_device *ppe_dev)
++{
++      const struct ppe_bm_port_config *port_cfg;
++      unsigned int i, bm_port_id, port_cfg_cnt;
++      u32 reg, val;
++      int ret;
++
++      /* Configure the allocated buffer number only for group 0.
++       * The buffer number of group 1-3 is already cleared to 0
++       * after PPE reset during the probe of PPE driver.
++       */
++      reg = PPE_BM_SHARED_GROUP_CFG_ADDR;
++      val = FIELD_PREP(PPE_BM_SHARED_GROUP_CFG_SHARED_LIMIT,
++                       ipq9574_ppe_bm_group_config);
++      ret = regmap_update_bits(ppe_dev->regmap, reg,
++                               PPE_BM_SHARED_GROUP_CFG_SHARED_LIMIT,
++                               val);
++      if (ret)
++              goto bm_config_fail;
++
++      /* Configure buffer thresholds for the BM ports. */
++      port_cfg = ipq9574_ppe_bm_port_config;
++      port_cfg_cnt = ARRAY_SIZE(ipq9574_ppe_bm_port_config);
++      for (i = 0; i < port_cfg_cnt; i++) {
++              for (bm_port_id = port_cfg[i].port_id_start;
++                   bm_port_id <= port_cfg[i].port_id_end; bm_port_id++) {
++                      ret = ppe_config_bm_threshold(ppe_dev, bm_port_id,
++                                                    port_cfg[i]);
++                      if (ret)
++                              goto bm_config_fail;
++              }
++      }
++
++      return 0;
++
++bm_config_fail:
++      dev_err(ppe_dev->dev, "PPE BM config error %d\n", ret);
++      return ret;
++}
++
++int ppe_hw_config(struct ppe_device *ppe_dev)
++{
++      return ppe_config_bm(ppe_dev);
++}
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
+@@ -0,0 +1,12 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ *
++ * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef __PPE_CONFIG_H__
++#define __PPE_CONFIG_H__
++
++#include "ppe.h"
++
++int ppe_hw_config(struct ppe_device *ppe_dev);
++#endif
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -0,0 +1,59 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ *
++ * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* PPE hardware register and table declarations. */
++#ifndef __PPE_REGS_H__
++#define __PPE_REGS_H__
++
++#include <linux/bitfield.h>
++
++/* There are 15 BM ports and 4 BM groups supported by PPE.
++ * BM port (0-7) is for EDMA port 0, BM port (8-13) is for
++ * PPE physical port 1-6 and BM port 14 is for EIP port.
++ */
++#define PPE_BM_PORT_FC_MODE_ADDR              0x600100
++#define PPE_BM_PORT_FC_MODE_ENTRIES           15
++#define PPE_BM_PORT_FC_MODE_INC                       0x4
++#define PPE_BM_PORT_FC_MODE_EN                        BIT(0)
++
++#define PPE_BM_PORT_GROUP_ID_ADDR             0x600180
++#define PPE_BM_PORT_GROUP_ID_ENTRIES          15
++#define PPE_BM_PORT_GROUP_ID_INC              0x4
++#define PPE_BM_PORT_GROUP_ID_SHARED_GROUP_ID  GENMASK(1, 0)
++
++#define PPE_BM_SHARED_GROUP_CFG_ADDR          0x600290
++#define PPE_BM_SHARED_GROUP_CFG_ENTRIES               4
++#define PPE_BM_SHARED_GROUP_CFG_INC           0x4
++#define PPE_BM_SHARED_GROUP_CFG_SHARED_LIMIT  GENMASK(10, 0)
++
++#define PPE_BM_PORT_FC_CFG_TBL_ADDR           0x601000
++#define PPE_BM_PORT_FC_CFG_TBL_ENTRIES                15
++#define PPE_BM_PORT_FC_CFG_TBL_INC            0x10
++#define PPE_BM_PORT_FC_W0_REACT_LIMIT         GENMASK(8, 0)
++#define PPE_BM_PORT_FC_W0_RESUME_THRESHOLD    GENMASK(17, 9)
++#define PPE_BM_PORT_FC_W0_RESUME_OFFSET               GENMASK(28, 18)
++#define PPE_BM_PORT_FC_W0_CEILING_LOW         GENMASK(31, 29)
++#define PPE_BM_PORT_FC_W1_CEILING_HIGH                GENMASK(7, 0)
++#define PPE_BM_PORT_FC_W1_WEIGHT              GENMASK(10, 8)
++#define PPE_BM_PORT_FC_W1_DYNAMIC             BIT(11)
++#define PPE_BM_PORT_FC_W1_PRE_ALLOC           GENMASK(22, 12)
++
++#define PPE_BM_PORT_FC_SET_REACT_LIMIT(tbl_cfg, value)        \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_BM_PORT_FC_W0_REACT_LIMIT)
++#define PPE_BM_PORT_FC_SET_RESUME_THRESHOLD(tbl_cfg, value)   \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_BM_PORT_FC_W0_RESUME_THRESHOLD)
++#define PPE_BM_PORT_FC_SET_RESUME_OFFSET(tbl_cfg, value)      \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_BM_PORT_FC_W0_RESUME_OFFSET)
++#define PPE_BM_PORT_FC_SET_CEILING_LOW(tbl_cfg, value)        \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_BM_PORT_FC_W0_CEILING_LOW)
++#define PPE_BM_PORT_FC_SET_CEILING_HIGH(tbl_cfg, value)       \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_BM_PORT_FC_W1_CEILING_HIGH)
++#define PPE_BM_PORT_FC_SET_WEIGHT(tbl_cfg, value)     \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_BM_PORT_FC_W1_WEIGHT)
++#define PPE_BM_PORT_FC_SET_DYNAMIC(tbl_cfg, value)    \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_BM_PORT_FC_W1_DYNAMIC)
++#define PPE_BM_PORT_FC_SET_PRE_ALLOC(tbl_cfg, value)  \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_BM_PORT_FC_W1_PRE_ALLOC)
++#endif
diff --git a/target/linux/qualcommbe/patches-6.18/0327-net-ethernet-qualcomm-Initialize-PPE-queue-managemen.patch b/target/linux/qualcommbe/patches-6.18/0327-net-ethernet-qualcomm-Initialize-PPE-queue-managemen.patch
new file mode 100644 (file)
index 0000000..be16222
--- /dev/null
@@ -0,0 +1,320 @@
+From 9be6c3590ef3c241e6a3cfd05291304a1f973bcf Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 9 Feb 2025 22:29:39 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Initialize PPE queue management for
+ IPQ9574
+
+QM (queue management) configurations decide the length of PPE
+queues and the queue depth for these queues which are used to
+drop packets in events of congestion.
+
+There are two types of PPE queues - unicast queues (0-255) and
+multicast queues (256-299). These queue types are used to forward
+different types of traffic, and are configured with different
+lengths.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ .../net/ethernet/qualcomm/ppe/ppe_config.c    | 177 +++++++++++++++++-
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h  |  85 +++++++++
+ 2 files changed, 261 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
+@@ -43,6 +43,29 @@ struct ppe_bm_port_config {
+       bool dynamic;
+ };
++/**
++ * struct ppe_qm_queue_config - PPE queue config.
++ * @queue_start: PPE start of queue ID.
++ * @queue_end: PPE end of queue ID.
++ * @prealloc_buf: Queue dedicated buffer number.
++ * @ceil: Ceil to start drop packet from queue.
++ * @weight: Weight value.
++ * @resume_offset: Resume offset from the threshold.
++ * @dynamic: Threshold value is decided dynamically or statically.
++ *
++ * Queue configuration decides the threshold to drop packet from PPE
++ * hardware queue.
++ */
++struct ppe_qm_queue_config {
++      unsigned int queue_start;
++      unsigned int queue_end;
++      unsigned int prealloc_buf;
++      unsigned int ceil;
++      unsigned int weight;
++      unsigned int resume_offset;
++      bool dynamic;
++};
++
+ /* Assign the share buffer number 1550 to group 0 by default. */
+ static const int ipq9574_ppe_bm_group_config = 1550;
+@@ -102,6 +125,33 @@ static const struct ppe_bm_port_config i
+       },
+ };
++/* Default QM group settings for IPQ9754. */
++static const int ipq9574_ppe_qm_group_config = 2000;
++
++/* Default QM settings for unicast and multicast queues for IPQ9754. */
++static const struct ppe_qm_queue_config ipq9574_ppe_qm_queue_config[] = {
++      {
++              /* QM settings for unicast queues 0 to 255. */
++              .queue_start    = 0,
++              .queue_end      = 255,
++              .prealloc_buf   = 0,
++              .ceil           = 1200,
++              .weight         = 7,
++              .resume_offset  = 36,
++              .dynamic        = true,
++      },
++      {
++              /* QM settings for multicast queues 256 to 299. */
++              .queue_start    = 256,
++              .queue_end      = 299,
++              .prealloc_buf   = 0,
++              .ceil           = 250,
++              .weight         = 0,
++              .resume_offset  = 36,
++              .dynamic        = false,
++      },
++};
++
+ static int ppe_config_bm_threshold(struct ppe_device *ppe_dev, int bm_port_id,
+                                  const struct ppe_bm_port_config port_cfg)
+ {
+@@ -189,7 +239,132 @@ bm_config_fail:
+       return ret;
+ }
++/* Configure PPE hardware queue depth, which is decided by the threshold
++ * of queue.
++ */
++static int ppe_config_qm(struct ppe_device *ppe_dev)
++{
++      const struct ppe_qm_queue_config *queue_cfg;
++      int ret, i, queue_id, queue_cfg_count;
++      u32 reg, multicast_queue_cfg[5];
++      u32 unicast_queue_cfg[4];
++      u32 group_cfg[3];
++
++      /* Assign the buffer number to the group 0 by default. */
++      reg = PPE_AC_GRP_CFG_TBL_ADDR;
++      ret = regmap_bulk_read(ppe_dev->regmap, reg,
++                             group_cfg, ARRAY_SIZE(group_cfg));
++      if (ret)
++              goto qm_config_fail;
++
++      PPE_AC_GRP_SET_BUF_LIMIT(group_cfg, ipq9574_ppe_qm_group_config);
++
++      ret = regmap_bulk_write(ppe_dev->regmap, reg,
++                              group_cfg, ARRAY_SIZE(group_cfg));
++      if (ret)
++              goto qm_config_fail;
++
++      queue_cfg = ipq9574_ppe_qm_queue_config;
++      queue_cfg_count = ARRAY_SIZE(ipq9574_ppe_qm_queue_config);
++      for (i = 0; i < queue_cfg_count; i++) {
++              queue_id = queue_cfg[i].queue_start;
++
++              /* Configure threshold for dropping packets separately for
++               * unicast and multicast PPE queues.
++               */
++              while (queue_id <= queue_cfg[i].queue_end) {
++                      if (queue_id < PPE_AC_UNICAST_QUEUE_CFG_TBL_ENTRIES) {
++                              reg = PPE_AC_UNICAST_QUEUE_CFG_TBL_ADDR +
++                                    PPE_AC_UNICAST_QUEUE_CFG_TBL_INC * queue_id;
++
++                              ret = regmap_bulk_read(ppe_dev->regmap, reg,
++                                                     unicast_queue_cfg,
++                                                     ARRAY_SIZE(unicast_queue_cfg));
++                              if (ret)
++                                      goto qm_config_fail;
++
++                              PPE_AC_UNICAST_QUEUE_SET_EN(unicast_queue_cfg, true);
++                              PPE_AC_UNICAST_QUEUE_SET_GRP_ID(unicast_queue_cfg, 0);
++                              PPE_AC_UNICAST_QUEUE_SET_PRE_LIMIT(unicast_queue_cfg,
++                                                                 queue_cfg[i].prealloc_buf);
++                              PPE_AC_UNICAST_QUEUE_SET_DYNAMIC(unicast_queue_cfg,
++                                                               queue_cfg[i].dynamic);
++                              PPE_AC_UNICAST_QUEUE_SET_WEIGHT(unicast_queue_cfg,
++                                                              queue_cfg[i].weight);
++                              PPE_AC_UNICAST_QUEUE_SET_THRESHOLD(unicast_queue_cfg,
++                                                                 queue_cfg[i].ceil);
++                              PPE_AC_UNICAST_QUEUE_SET_GRN_RESUME(unicast_queue_cfg,
++                                                                  queue_cfg[i].resume_offset);
++
++                              ret = regmap_bulk_write(ppe_dev->regmap, reg,
++                                                      unicast_queue_cfg,
++                                                      ARRAY_SIZE(unicast_queue_cfg));
++                              if (ret)
++                                      goto qm_config_fail;
++                      } else {
++                              reg = PPE_AC_MULTICAST_QUEUE_CFG_TBL_ADDR +
++                                    PPE_AC_MULTICAST_QUEUE_CFG_TBL_INC * queue_id;
++
++                              ret = regmap_bulk_read(ppe_dev->regmap, reg,
++                                                     multicast_queue_cfg,
++                                                     ARRAY_SIZE(multicast_queue_cfg));
++                              if (ret)
++                                      goto qm_config_fail;
++
++                              PPE_AC_MULTICAST_QUEUE_SET_EN(multicast_queue_cfg, true);
++                              PPE_AC_MULTICAST_QUEUE_SET_GRN_GRP_ID(multicast_queue_cfg, 0);
++                              PPE_AC_MULTICAST_QUEUE_SET_GRN_PRE_LIMIT(multicast_queue_cfg,
++                                                                       queue_cfg[i].prealloc_buf);
++                              PPE_AC_MULTICAST_QUEUE_SET_GRN_THRESHOLD(multicast_queue_cfg,
++                                                                       queue_cfg[i].ceil);
++                              PPE_AC_MULTICAST_QUEUE_SET_GRN_RESUME(multicast_queue_cfg,
++                                                                    queue_cfg[i].resume_offset);
++
++                              ret = regmap_bulk_write(ppe_dev->regmap, reg,
++                                                      multicast_queue_cfg,
++                                                      ARRAY_SIZE(multicast_queue_cfg));
++                              if (ret)
++                                      goto qm_config_fail;
++                      }
++
++                      /* Enable enqueue. */
++                      reg = PPE_ENQ_OPR_TBL_ADDR + PPE_ENQ_OPR_TBL_INC * queue_id;
++                      ret = regmap_clear_bits(ppe_dev->regmap, reg,
++                                              PPE_ENQ_OPR_TBL_ENQ_DISABLE);
++                      if (ret)
++                              goto qm_config_fail;
++
++                      /* Enable dequeue. */
++                      reg = PPE_DEQ_OPR_TBL_ADDR + PPE_DEQ_OPR_TBL_INC * queue_id;
++                      ret = regmap_clear_bits(ppe_dev->regmap, reg,
++                                              PPE_DEQ_OPR_TBL_DEQ_DISABLE);
++                      if (ret)
++                              goto qm_config_fail;
++
++                      queue_id++;
++              }
++      }
++
++      /* Enable queue counter for all PPE hardware queues. */
++      ret = regmap_set_bits(ppe_dev->regmap, PPE_EG_BRIDGE_CONFIG_ADDR,
++                            PPE_EG_BRIDGE_CONFIG_QUEUE_CNT_EN);
++      if (ret)
++              goto qm_config_fail;
++
++      return 0;
++
++qm_config_fail:
++      dev_err(ppe_dev->dev, "PPE QM config error %d\n", ret);
++      return ret;
++}
++
+ int ppe_hw_config(struct ppe_device *ppe_dev)
+ {
+-      return ppe_config_bm(ppe_dev);
++      int ret;
++
++      ret = ppe_config_bm(ppe_dev);
++      if (ret)
++              return ret;
++
++      return ppe_config_qm(ppe_dev);
+ }
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -9,6 +9,16 @@
+ #include <linux/bitfield.h>
++/* PPE queue counters enable/disable control. */
++#define PPE_EG_BRIDGE_CONFIG_ADDR             0x20044
++#define PPE_EG_BRIDGE_CONFIG_QUEUE_CNT_EN     BIT(2)
++
++/* Table addresses for per-queue dequeue setting. */
++#define PPE_DEQ_OPR_TBL_ADDR                  0x430000
++#define PPE_DEQ_OPR_TBL_ENTRIES                       300
++#define PPE_DEQ_OPR_TBL_INC                   0x10
++#define PPE_DEQ_OPR_TBL_DEQ_DISABLE           BIT(0)
++
+ /* There are 15 BM ports and 4 BM groups supported by PPE.
+  * BM port (0-7) is for EDMA port 0, BM port (8-13) is for
+  * PPE physical port 1-6 and BM port 14 is for EIP port.
+@@ -56,4 +66,79 @@
+       u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_BM_PORT_FC_W1_DYNAMIC)
+ #define PPE_BM_PORT_FC_SET_PRE_ALLOC(tbl_cfg, value)  \
+       u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_BM_PORT_FC_W1_PRE_ALLOC)
++
++/* PPE unicast queue (0-255) configurations. */
++#define PPE_AC_UNICAST_QUEUE_CFG_TBL_ADDR     0x848000
++#define PPE_AC_UNICAST_QUEUE_CFG_TBL_ENTRIES  256
++#define PPE_AC_UNICAST_QUEUE_CFG_TBL_INC      0x10
++#define PPE_AC_UNICAST_QUEUE_CFG_W0_EN                BIT(0)
++#define PPE_AC_UNICAST_QUEUE_CFG_W0_WRED_EN   BIT(1)
++#define PPE_AC_UNICAST_QUEUE_CFG_W0_FC_EN     BIT(2)
++#define PPE_AC_UNICAST_QUEUE_CFG_W0_CLR_AWARE BIT(3)
++#define PPE_AC_UNICAST_QUEUE_CFG_W0_GRP_ID    GENMASK(5, 4)
++#define PPE_AC_UNICAST_QUEUE_CFG_W0_PRE_LIMIT GENMASK(16, 6)
++#define PPE_AC_UNICAST_QUEUE_CFG_W0_DYNAMIC   BIT(17)
++#define PPE_AC_UNICAST_QUEUE_CFG_W0_WEIGHT    GENMASK(20, 18)
++#define PPE_AC_UNICAST_QUEUE_CFG_W0_THRESHOLD GENMASK(31, 21)
++#define PPE_AC_UNICAST_QUEUE_CFG_W3_GRN_RESUME        GENMASK(23, 13)
++
++#define PPE_AC_UNICAST_QUEUE_SET_EN(tbl_cfg, value)   \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_AC_UNICAST_QUEUE_CFG_W0_EN)
++#define PPE_AC_UNICAST_QUEUE_SET_GRP_ID(tbl_cfg, value)       \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_AC_UNICAST_QUEUE_CFG_W0_GRP_ID)
++#define PPE_AC_UNICAST_QUEUE_SET_PRE_LIMIT(tbl_cfg, value)    \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_AC_UNICAST_QUEUE_CFG_W0_PRE_LIMIT)
++#define PPE_AC_UNICAST_QUEUE_SET_DYNAMIC(tbl_cfg, value)      \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_AC_UNICAST_QUEUE_CFG_W0_DYNAMIC)
++#define PPE_AC_UNICAST_QUEUE_SET_WEIGHT(tbl_cfg, value)       \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_AC_UNICAST_QUEUE_CFG_W0_WEIGHT)
++#define PPE_AC_UNICAST_QUEUE_SET_THRESHOLD(tbl_cfg, value)    \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_AC_UNICAST_QUEUE_CFG_W0_THRESHOLD)
++#define PPE_AC_UNICAST_QUEUE_SET_GRN_RESUME(tbl_cfg, value)   \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x3, value, PPE_AC_UNICAST_QUEUE_CFG_W3_GRN_RESUME)
++
++/* PPE multicast queue (256-299) configurations. */
++#define PPE_AC_MULTICAST_QUEUE_CFG_TBL_ADDR   0x84a000
++#define PPE_AC_MULTICAST_QUEUE_CFG_TBL_ENTRIES        44
++#define PPE_AC_MULTICAST_QUEUE_CFG_TBL_INC    0x10
++#define PPE_AC_MULTICAST_QUEUE_CFG_W0_EN      BIT(0)
++#define PPE_AC_MULTICAST_QUEUE_CFG_W0_FC_EN   BIT(1)
++#define PPE_AC_MULTICAST_QUEUE_CFG_W0_CLR_AWARE       BIT(2)
++#define PPE_AC_MULTICAST_QUEUE_CFG_W0_GRP_ID  GENMASK(4, 3)
++#define PPE_AC_MULTICAST_QUEUE_CFG_W0_PRE_LIMIT       GENMASK(15, 5)
++#define PPE_AC_MULTICAST_QUEUE_CFG_W0_THRESHOLD       GENMASK(26, 16)
++#define PPE_AC_MULTICAST_QUEUE_CFG_W2_RESUME  GENMASK(17, 7)
++
++#define PPE_AC_MULTICAST_QUEUE_SET_EN(tbl_cfg, value) \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_AC_MULTICAST_QUEUE_CFG_W0_EN)
++#define PPE_AC_MULTICAST_QUEUE_SET_GRN_GRP_ID(tbl_cfg, value) \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_AC_MULTICAST_QUEUE_CFG_W0_GRP_ID)
++#define PPE_AC_MULTICAST_QUEUE_SET_GRN_PRE_LIMIT(tbl_cfg, value)      \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_AC_MULTICAST_QUEUE_CFG_W0_PRE_LIMIT)
++#define PPE_AC_MULTICAST_QUEUE_SET_GRN_THRESHOLD(tbl_cfg, value)      \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_AC_MULTICAST_QUEUE_CFG_W0_THRESHOLD)
++#define PPE_AC_MULTICAST_QUEUE_SET_GRN_RESUME(tbl_cfg, value) \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x2, value, PPE_AC_MULTICAST_QUEUE_CFG_W2_RESUME)
++
++/* PPE admission control group (0-3) configurations */
++#define PPE_AC_GRP_CFG_TBL_ADDR                       0x84c000
++#define PPE_AC_GRP_CFG_TBL_ENTRIES            0x4
++#define PPE_AC_GRP_CFG_TBL_INC                        0x10
++#define PPE_AC_GRP_W0_AC_EN                   BIT(0)
++#define PPE_AC_GRP_W0_AC_FC_EN                        BIT(1)
++#define PPE_AC_GRP_W0_CLR_AWARE                       BIT(2)
++#define PPE_AC_GRP_W0_THRESHOLD_LOW           GENMASK(31, 25)
++#define PPE_AC_GRP_W1_THRESHOLD_HIGH          GENMASK(3, 0)
++#define PPE_AC_GRP_W1_BUF_LIMIT                       GENMASK(14, 4)
++#define PPE_AC_GRP_W2_RESUME_GRN              GENMASK(15, 5)
++#define PPE_AC_GRP_W2_PRE_ALLOC                       GENMASK(26, 16)
++
++#define PPE_AC_GRP_SET_BUF_LIMIT(tbl_cfg, value)      \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_AC_GRP_W1_BUF_LIMIT)
++
++/* Table addresses for per-queue enqueue setting. */
++#define PPE_ENQ_OPR_TBL_ADDR                  0x85c000
++#define PPE_ENQ_OPR_TBL_ENTRIES                       300
++#define PPE_ENQ_OPR_TBL_INC                   0x10
++#define PPE_ENQ_OPR_TBL_ENQ_DISABLE           BIT(0)
+ #endif
diff --git a/target/linux/qualcommbe/patches-6.18/0328-net-ethernet-qualcomm-Initialize-the-PPE-scheduler-s.patch b/target/linux/qualcommbe/patches-6.18/0328-net-ethernet-qualcomm-Initialize-the-PPE-scheduler-s.patch
new file mode 100644 (file)
index 0000000..86949a2
--- /dev/null
@@ -0,0 +1,1000 @@
+From 333edaf474cd707b0a04c57f255b56bc3c015789 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 9 Feb 2025 22:29:40 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Initialize the PPE scheduler
+ settings
+
+The PPE scheduler settings determine the priority of scheduling the
+packet across the different hardware queues per PPE port.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ .../net/ethernet/qualcomm/ppe/ppe_config.c    | 788 +++++++++++++++++-
+ .../net/ethernet/qualcomm/ppe/ppe_config.h    |  37 +
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h  |  97 +++
+ 3 files changed, 921 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
+@@ -16,6 +16,8 @@
+ #include "ppe_config.h"
+ #include "ppe_regs.h"
++#define PPE_QUEUE_SCH_PRI_NUM         8
++
+ /**
+  * struct ppe_bm_port_config - PPE BM port configuration.
+  * @port_id_start: The fist BM port ID to configure.
+@@ -66,6 +68,66 @@ struct ppe_qm_queue_config {
+       bool dynamic;
+ };
++/**
++ * struct ppe_scheduler_bm_config - PPE arbitration for buffer config.
++ * @valid: Arbitration entry valid or not.
++ * @is_egress: Arbitration entry for egress or not.
++ * @port: Port ID to use arbitration entry.
++ * @second_valid: Second port valid or not.
++ * @second_port: Second port to use.
++ *
++ * Configure the scheduler settings for accessing and releasing the PPE buffers.
++ */
++struct ppe_scheduler_bm_config {
++      bool valid;
++      bool is_egress;
++      unsigned int port;
++      bool second_valid;
++      unsigned int second_port;
++};
++
++/**
++ * struct ppe_scheduler_qm_config - PPE arbitration for scheduler config.
++ * @ensch_port_bmp: Port bit map for enqueue scheduler.
++ * @ensch_port: Port ID to enqueue scheduler.
++ * @desch_port: Port ID to dequeue scheduler.
++ * @desch_second_valid: Dequeue for the second port valid or not.
++ * @desch_second_port: Second port ID to dequeue scheduler.
++ *
++ * Configure the scheduler settings for enqueuing and dequeuing packets on
++ * the PPE port.
++ */
++struct ppe_scheduler_qm_config {
++      unsigned int ensch_port_bmp;
++      unsigned int ensch_port;
++      unsigned int desch_port;
++      bool desch_second_valid;
++      unsigned int desch_second_port;
++};
++
++/**
++ * struct ppe_scheduler_port_config - PPE port scheduler config.
++ * @port: Port ID to be scheduled.
++ * @flow_level: Scheduler flow level or not.
++ * @node_id: Node ID, for level 0, queue ID is used.
++ * @loop_num: Loop number of scheduler config.
++ * @pri_max: Max priority configured.
++ * @flow_id: Strict priority ID.
++ * @drr_node_id: Node ID for scheduler.
++ *
++ * PPE port scheduler configuration which decides the priority in the
++ * packet scheduler for the egress port.
++ */
++struct ppe_scheduler_port_config {
++      unsigned int port;
++      bool flow_level;
++      unsigned int node_id;
++      unsigned int loop_num;
++      unsigned int pri_max;
++      unsigned int flow_id;
++      unsigned int drr_node_id;
++};
++
+ /* Assign the share buffer number 1550 to group 0 by default. */
+ static const int ipq9574_ppe_bm_group_config = 1550;
+@@ -152,6 +214,599 @@ static const struct ppe_qm_queue_config
+       },
+ };
++/* Scheduler configuration for the assigning and releasing buffers for the
++ * packet passing through PPE, which is different per SoC.
++ */
++static const struct ppe_scheduler_bm_config ipq9574_ppe_sch_bm_config[] = {
++      {1, 0, 0, 0, 0},
++      {1, 1, 0, 0, 0},
++      {1, 0, 5, 0, 0},
++      {1, 1, 5, 0, 0},
++      {1, 0, 6, 0, 0},
++      {1, 1, 6, 0, 0},
++      {1, 0, 1, 0, 0},
++      {1, 1, 1, 0, 0},
++      {1, 0, 0, 0, 0},
++      {1, 1, 0, 0, 0},
++      {1, 0, 5, 0, 0},
++      {1, 1, 5, 0, 0},
++      {1, 0, 6, 0, 0},
++      {1, 1, 6, 0, 0},
++      {1, 0, 7, 0, 0},
++      {1, 1, 7, 0, 0},
++      {1, 0, 0, 0, 0},
++      {1, 1, 0, 0, 0},
++      {1, 0, 1, 0, 0},
++      {1, 1, 1, 0, 0},
++      {1, 0, 5, 0, 0},
++      {1, 1, 5, 0, 0},
++      {1, 0, 6, 0, 0},
++      {1, 1, 6, 0, 0},
++      {1, 0, 2, 0, 0},
++      {1, 1, 2, 0, 0},
++      {1, 0, 0, 0, 0},
++      {1, 1, 0, 0, 0},
++      {1, 0, 5, 0, 0},
++      {1, 1, 5, 0, 0},
++      {1, 0, 6, 0, 0},
++      {1, 1, 6, 0, 0},
++      {1, 0, 1, 0, 0},
++      {1, 1, 1, 0, 0},
++      {1, 0, 3, 0, 0},
++      {1, 1, 3, 0, 0},
++      {1, 0, 0, 0, 0},
++      {1, 1, 0, 0, 0},
++      {1, 0, 5, 0, 0},
++      {1, 1, 5, 0, 0},
++      {1, 0, 6, 0, 0},
++      {1, 1, 6, 0, 0},
++      {1, 0, 7, 0, 0},
++      {1, 1, 7, 0, 0},
++      {1, 0, 0, 0, 0},
++      {1, 1, 0, 0, 0},
++      {1, 0, 1, 0, 0},
++      {1, 1, 1, 0, 0},
++      {1, 0, 5, 0, 0},
++      {1, 1, 5, 0, 0},
++      {1, 0, 6, 0, 0},
++      {1, 1, 6, 0, 0},
++      {1, 0, 4, 0, 0},
++      {1, 1, 4, 0, 0},
++      {1, 0, 0, 0, 0},
++      {1, 1, 0, 0, 0},
++      {1, 0, 5, 0, 0},
++      {1, 1, 5, 0, 0},
++      {1, 0, 6, 0, 0},
++      {1, 1, 6, 0, 0},
++      {1, 0, 1, 0, 0},
++      {1, 1, 1, 0, 0},
++      {1, 0, 0, 0, 0},
++      {1, 1, 0, 0, 0},
++      {1, 0, 5, 0, 0},
++      {1, 1, 5, 0, 0},
++      {1, 0, 6, 0, 0},
++      {1, 1, 6, 0, 0},
++      {1, 0, 2, 0, 0},
++      {1, 1, 2, 0, 0},
++      {1, 0, 0, 0, 0},
++      {1, 1, 0, 0, 0},
++      {1, 0, 7, 0, 0},
++      {1, 1, 7, 0, 0},
++      {1, 0, 5, 0, 0},
++      {1, 1, 5, 0, 0},
++      {1, 0, 6, 0, 0},
++      {1, 1, 6, 0, 0},
++      {1, 0, 1, 0, 0},
++      {1, 1, 1, 0, 0},
++      {1, 0, 0, 0, 0},
++      {1, 1, 0, 0, 0},
++      {1, 0, 5, 0, 0},
++      {1, 1, 5, 0, 0},
++      {1, 0, 6, 0, 0},
++      {1, 1, 6, 0, 0},
++      {1, 0, 3, 0, 0},
++      {1, 1, 3, 0, 0},
++      {1, 0, 1, 0, 0},
++      {1, 1, 1, 0, 0},
++      {1, 0, 0, 0, 0},
++      {1, 1, 0, 0, 0},
++      {1, 0, 5, 0, 0},
++      {1, 1, 5, 0, 0},
++      {1, 0, 6, 0, 0},
++      {1, 1, 6, 0, 0},
++      {1, 0, 4, 0, 0},
++      {1, 1, 4, 0, 0},
++      {1, 0, 7, 0, 0},
++      {1, 1, 7, 0, 0},
++};
++
++/* Scheduler configuration for dispatching packet on PPE queues, which
++ * is different per SoC.
++ */
++static const struct ppe_scheduler_qm_config ipq9574_ppe_sch_qm_config[] = {
++      {0x98, 6, 0, 1, 1},
++      {0x94, 5, 6, 1, 3},
++      {0x86, 0, 5, 1, 4},
++      {0x8C, 1, 6, 1, 0},
++      {0x1C, 7, 5, 1, 1},
++      {0x98, 2, 6, 1, 0},
++      {0x1C, 5, 7, 1, 1},
++      {0x34, 3, 6, 1, 0},
++      {0x8C, 4, 5, 1, 1},
++      {0x98, 2, 6, 1, 0},
++      {0x8C, 5, 4, 1, 1},
++      {0xA8, 0, 6, 1, 2},
++      {0x98, 5, 1, 1, 0},
++      {0x98, 6, 5, 1, 2},
++      {0x89, 1, 6, 1, 4},
++      {0xA4, 3, 0, 1, 1},
++      {0x8C, 5, 6, 1, 4},
++      {0xA8, 0, 2, 1, 1},
++      {0x98, 6, 5, 1, 0},
++      {0xC4, 4, 3, 1, 1},
++      {0x94, 6, 5, 1, 0},
++      {0x1C, 7, 6, 1, 1},
++      {0x98, 2, 5, 1, 0},
++      {0x1C, 6, 7, 1, 1},
++      {0x1C, 5, 6, 1, 0},
++      {0x94, 3, 5, 1, 1},
++      {0x8C, 4, 6, 1, 0},
++      {0x94, 1, 5, 1, 3},
++      {0x94, 6, 1, 1, 0},
++      {0xD0, 3, 5, 1, 2},
++      {0x98, 6, 0, 1, 1},
++      {0x94, 5, 6, 1, 3},
++      {0x94, 1, 5, 1, 0},
++      {0x98, 2, 6, 1, 1},
++      {0x8C, 4, 5, 1, 0},
++      {0x1C, 7, 6, 1, 1},
++      {0x8C, 0, 5, 1, 4},
++      {0x89, 1, 6, 1, 2},
++      {0x98, 5, 0, 1, 1},
++      {0x94, 6, 5, 1, 3},
++      {0x92, 0, 6, 1, 2},
++      {0x98, 1, 5, 1, 0},
++      {0x98, 6, 2, 1, 1},
++      {0xD0, 0, 5, 1, 3},
++      {0x94, 6, 0, 1, 1},
++      {0x8C, 5, 6, 1, 4},
++      {0x8C, 1, 5, 1, 0},
++      {0x1C, 6, 7, 1, 1},
++      {0x1C, 5, 6, 1, 0},
++      {0xB0, 2, 3, 1, 1},
++      {0xC4, 4, 5, 1, 0},
++      {0x8C, 6, 4, 1, 1},
++      {0xA4, 3, 6, 1, 0},
++      {0x1C, 5, 7, 1, 1},
++      {0x4C, 0, 5, 1, 4},
++      {0x8C, 6, 0, 1, 1},
++      {0x34, 7, 6, 1, 3},
++      {0x94, 5, 0, 1, 1},
++      {0x98, 6, 5, 1, 2},
++};
++
++static const struct ppe_scheduler_port_config ppe_port_sch_config[] = {
++      {
++              .port           = 0,
++              .flow_level     = true,
++              .node_id        = 0,
++              .loop_num       = 1,
++              .pri_max        = 1,
++              .flow_id        = 0,
++              .drr_node_id    = 0,
++      },
++      {
++              .port           = 0,
++              .flow_level     = false,
++              .node_id        = 0,
++              .loop_num       = 8,
++              .pri_max        = 8,
++              .flow_id        = 0,
++              .drr_node_id    = 0,
++      },
++      {
++              .port           = 0,
++              .flow_level     = false,
++              .node_id        = 8,
++              .loop_num       = 8,
++              .pri_max        = 8,
++              .flow_id        = 0,
++              .drr_node_id    = 0,
++      },
++      {
++              .port           = 0,
++              .flow_level     = false,
++              .node_id        = 16,
++              .loop_num       = 8,
++              .pri_max        = 8,
++              .flow_id        = 0,
++              .drr_node_id    = 0,
++      },
++      {
++              .port           = 0,
++              .flow_level     = false,
++              .node_id        = 24,
++              .loop_num       = 8,
++              .pri_max        = 8,
++              .flow_id        = 0,
++              .drr_node_id    = 0,
++      },
++      {
++              .port           = 0,
++              .flow_level     = false,
++              .node_id        = 32,
++              .loop_num       = 8,
++              .pri_max        = 8,
++              .flow_id        = 0,
++              .drr_node_id    = 0,
++      },
++      {
++              .port           = 0,
++              .flow_level     = false,
++              .node_id        = 40,
++              .loop_num       = 8,
++              .pri_max        = 8,
++              .flow_id        = 0,
++              .drr_node_id    = 0,
++      },
++      {
++              .port           = 0,
++              .flow_level     = false,
++              .node_id        = 48,
++              .loop_num       = 8,
++              .pri_max        = 8,
++              .flow_id        = 0,
++              .drr_node_id    = 0,
++      },
++      {
++              .port           = 0,
++              .flow_level     = false,
++              .node_id        = 56,
++              .loop_num       = 8,
++              .pri_max        = 8,
++              .flow_id        = 0,
++              .drr_node_id    = 0,
++      },
++      {
++              .port           = 0,
++              .flow_level     = false,
++              .node_id        = 256,
++              .loop_num       = 8,
++              .pri_max        = 8,
++              .flow_id        = 0,
++              .drr_node_id    = 0,
++      },
++      {
++              .port           = 0,
++              .flow_level     = false,
++              .node_id        = 264,
++              .loop_num       = 8,
++              .pri_max        = 8,
++              .flow_id        = 0,
++              .drr_node_id    = 0,
++      },
++      {
++              .port           = 1,
++              .flow_level     = true,
++              .node_id        = 36,
++              .loop_num       = 2,
++              .pri_max        = 0,
++              .flow_id        = 1,
++              .drr_node_id    = 8,
++      },
++      {
++              .port           = 1,
++              .flow_level     = false,
++              .node_id        = 144,
++              .loop_num       = 16,
++              .pri_max        = 8,
++              .flow_id        = 36,
++              .drr_node_id    = 48,
++      },
++      {
++              .port           = 1,
++              .flow_level     = false,
++              .node_id        = 272,
++              .loop_num       = 4,
++              .pri_max        = 4,
++              .flow_id        = 36,
++              .drr_node_id    = 48,
++      },
++      {
++              .port           = 2,
++              .flow_level     = true,
++              .node_id        = 40,
++              .loop_num       = 2,
++              .pri_max        = 0,
++              .flow_id        = 2,
++              .drr_node_id    = 12,
++      },
++      {
++              .port           = 2,
++              .flow_level     = false,
++              .node_id        = 160,
++              .loop_num       = 16,
++              .pri_max        = 8,
++              .flow_id        = 40,
++              .drr_node_id    = 64,
++      },
++      {
++              .port           = 2,
++              .flow_level     = false,
++              .node_id        = 276,
++              .loop_num       = 4,
++              .pri_max        = 4,
++              .flow_id        = 40,
++              .drr_node_id    = 64,
++      },
++      {
++              .port           = 3,
++              .flow_level     = true,
++              .node_id        = 44,
++              .loop_num       = 2,
++              .pri_max        = 0,
++              .flow_id        = 3,
++              .drr_node_id    = 16,
++      },
++      {
++              .port           = 3,
++              .flow_level     = false,
++              .node_id        = 176,
++              .loop_num       = 16,
++              .pri_max        = 8,
++              .flow_id        = 44,
++              .drr_node_id    = 80,
++      },
++      {
++              .port           = 3,
++              .flow_level     = false,
++              .node_id        = 280,
++              .loop_num       = 4,
++              .pri_max        = 4,
++              .flow_id        = 44,
++              .drr_node_id    = 80,
++      },
++      {
++              .port           = 4,
++              .flow_level     = true,
++              .node_id        = 48,
++              .loop_num       = 2,
++              .pri_max        = 0,
++              .flow_id        = 4,
++              .drr_node_id    = 20,
++      },
++      {
++              .port           = 4,
++              .flow_level     = false,
++              .node_id        = 192,
++              .loop_num       = 16,
++              .pri_max        = 8,
++              .flow_id        = 48,
++              .drr_node_id    = 96,
++      },
++      {
++              .port           = 4,
++              .flow_level     = false,
++              .node_id        = 284,
++              .loop_num       = 4,
++              .pri_max        = 4,
++              .flow_id        = 48,
++              .drr_node_id    = 96,
++      },
++      {
++              .port           = 5,
++              .flow_level     = true,
++              .node_id        = 52,
++              .loop_num       = 2,
++              .pri_max        = 0,
++              .flow_id        = 5,
++              .drr_node_id    = 24,
++      },
++      {
++              .port           = 5,
++              .flow_level     = false,
++              .node_id        = 208,
++              .loop_num       = 16,
++              .pri_max        = 8,
++              .flow_id        = 52,
++              .drr_node_id    = 112,
++      },
++      {
++              .port           = 5,
++              .flow_level     = false,
++              .node_id        = 288,
++              .loop_num       = 4,
++              .pri_max        = 4,
++              .flow_id        = 52,
++              .drr_node_id    = 112,
++      },
++      {
++              .port           = 6,
++              .flow_level     = true,
++              .node_id        = 56,
++              .loop_num       = 2,
++              .pri_max        = 0,
++              .flow_id        = 6,
++              .drr_node_id    = 28,
++      },
++      {
++              .port           = 6,
++              .flow_level     = false,
++              .node_id        = 224,
++              .loop_num       = 16,
++              .pri_max        = 8,
++              .flow_id        = 56,
++              .drr_node_id    = 128,
++      },
++      {
++              .port           = 6,
++              .flow_level     = false,
++              .node_id        = 292,
++              .loop_num       = 4,
++              .pri_max        = 4,
++              .flow_id        = 56,
++              .drr_node_id    = 128,
++      },
++      {
++              .port           = 7,
++              .flow_level     = true,
++              .node_id        = 60,
++              .loop_num       = 2,
++              .pri_max        = 0,
++              .flow_id        = 7,
++              .drr_node_id    = 32,
++      },
++      {
++              .port           = 7,
++              .flow_level     = false,
++              .node_id        = 240,
++              .loop_num       = 16,
++              .pri_max        = 8,
++              .flow_id        = 60,
++              .drr_node_id    = 144,
++      },
++      {
++              .port           = 7,
++              .flow_level     = false,
++              .node_id        = 296,
++              .loop_num       = 4,
++              .pri_max        = 4,
++              .flow_id        = 60,
++              .drr_node_id    = 144,
++      },
++};
++
++/* Set the PPE queue level scheduler configuration. */
++static int ppe_scheduler_l0_queue_map_set(struct ppe_device *ppe_dev,
++                                        int node_id, int port,
++                                        struct ppe_scheduler_cfg scheduler_cfg)
++{
++      u32 val, reg;
++      int ret;
++
++      reg = PPE_L0_FLOW_MAP_TBL_ADDR + node_id * PPE_L0_FLOW_MAP_TBL_INC;
++      val = FIELD_PREP(PPE_L0_FLOW_MAP_TBL_FLOW_ID, scheduler_cfg.flow_id);
++      val |= FIELD_PREP(PPE_L0_FLOW_MAP_TBL_C_PRI, scheduler_cfg.pri);
++      val |= FIELD_PREP(PPE_L0_FLOW_MAP_TBL_E_PRI, scheduler_cfg.pri);
++      val |= FIELD_PREP(PPE_L0_FLOW_MAP_TBL_C_NODE_WT, scheduler_cfg.drr_node_wt);
++      val |= FIELD_PREP(PPE_L0_FLOW_MAP_TBL_E_NODE_WT, scheduler_cfg.drr_node_wt);
++
++      ret = regmap_write(ppe_dev->regmap, reg, val);
++      if (ret)
++              return ret;
++
++      reg = PPE_L0_C_FLOW_CFG_TBL_ADDR +
++            (scheduler_cfg.flow_id * PPE_QUEUE_SCH_PRI_NUM + scheduler_cfg.pri) *
++            PPE_L0_C_FLOW_CFG_TBL_INC;
++      val = FIELD_PREP(PPE_L0_C_FLOW_CFG_TBL_NODE_ID, scheduler_cfg.drr_node_id);
++      val |= FIELD_PREP(PPE_L0_C_FLOW_CFG_TBL_NODE_CREDIT_UNIT, scheduler_cfg.unit_is_packet);
++
++      ret = regmap_write(ppe_dev->regmap, reg, val);
++      if (ret)
++              return ret;
++
++      reg = PPE_L0_E_FLOW_CFG_TBL_ADDR +
++            (scheduler_cfg.flow_id * PPE_QUEUE_SCH_PRI_NUM + scheduler_cfg.pri) *
++            PPE_L0_E_FLOW_CFG_TBL_INC;
++      val = FIELD_PREP(PPE_L0_E_FLOW_CFG_TBL_NODE_ID, scheduler_cfg.drr_node_id);
++      val |= FIELD_PREP(PPE_L0_E_FLOW_CFG_TBL_NODE_CREDIT_UNIT, scheduler_cfg.unit_is_packet);
++
++      ret = regmap_write(ppe_dev->regmap, reg, val);
++      if (ret)
++              return ret;
++
++      reg = PPE_L0_FLOW_PORT_MAP_TBL_ADDR + node_id * PPE_L0_FLOW_PORT_MAP_TBL_INC;
++      val = FIELD_PREP(PPE_L0_FLOW_PORT_MAP_TBL_PORT_NUM, port);
++
++      ret = regmap_write(ppe_dev->regmap, reg, val);
++      if (ret)
++              return ret;
++
++      reg = PPE_L0_COMP_CFG_TBL_ADDR + node_id * PPE_L0_COMP_CFG_TBL_INC;
++      val = FIELD_PREP(PPE_L0_COMP_CFG_TBL_NODE_METER_LEN, scheduler_cfg.frame_mode);
++
++      return regmap_update_bits(ppe_dev->regmap, reg,
++                                PPE_L0_COMP_CFG_TBL_NODE_METER_LEN,
++                                val);
++}
++
++/* Set the PPE flow level scheduler configuration. */
++static int ppe_scheduler_l1_queue_map_set(struct ppe_device *ppe_dev,
++                                        int node_id, int port,
++                                        struct ppe_scheduler_cfg scheduler_cfg)
++{
++      u32 val, reg;
++      int ret;
++
++      val = FIELD_PREP(PPE_L1_FLOW_MAP_TBL_FLOW_ID, scheduler_cfg.flow_id);
++      val |= FIELD_PREP(PPE_L1_FLOW_MAP_TBL_C_PRI, scheduler_cfg.pri);
++      val |= FIELD_PREP(PPE_L1_FLOW_MAP_TBL_E_PRI, scheduler_cfg.pri);
++      val |= FIELD_PREP(PPE_L1_FLOW_MAP_TBL_C_NODE_WT, scheduler_cfg.drr_node_wt);
++      val |= FIELD_PREP(PPE_L1_FLOW_MAP_TBL_E_NODE_WT, scheduler_cfg.drr_node_wt);
++      reg = PPE_L1_FLOW_MAP_TBL_ADDR + node_id * PPE_L1_FLOW_MAP_TBL_INC;
++
++      ret = regmap_write(ppe_dev->regmap, reg, val);
++      if (ret)
++              return ret;
++
++      val = FIELD_PREP(PPE_L1_C_FLOW_CFG_TBL_NODE_ID, scheduler_cfg.drr_node_id);
++      val |= FIELD_PREP(PPE_L1_C_FLOW_CFG_TBL_NODE_CREDIT_UNIT, scheduler_cfg.unit_is_packet);
++      reg = PPE_L1_C_FLOW_CFG_TBL_ADDR +
++            (scheduler_cfg.flow_id * PPE_QUEUE_SCH_PRI_NUM + scheduler_cfg.pri) *
++            PPE_L1_C_FLOW_CFG_TBL_INC;
++
++      ret = regmap_write(ppe_dev->regmap, reg, val);
++      if (ret)
++              return ret;
++
++      val = FIELD_PREP(PPE_L1_E_FLOW_CFG_TBL_NODE_ID, scheduler_cfg.drr_node_id);
++      val |= FIELD_PREP(PPE_L1_E_FLOW_CFG_TBL_NODE_CREDIT_UNIT, scheduler_cfg.unit_is_packet);
++      reg = PPE_L1_E_FLOW_CFG_TBL_ADDR +
++              (scheduler_cfg.flow_id * PPE_QUEUE_SCH_PRI_NUM + scheduler_cfg.pri) *
++              PPE_L1_E_FLOW_CFG_TBL_INC;
++
++      ret = regmap_write(ppe_dev->regmap, reg, val);
++      if (ret)
++              return ret;
++
++      val = FIELD_PREP(PPE_L1_FLOW_PORT_MAP_TBL_PORT_NUM, port);
++      reg = PPE_L1_FLOW_PORT_MAP_TBL_ADDR + node_id * PPE_L1_FLOW_PORT_MAP_TBL_INC;
++
++      ret = regmap_write(ppe_dev->regmap, reg, val);
++      if (ret)
++              return ret;
++
++      reg = PPE_L1_COMP_CFG_TBL_ADDR + node_id * PPE_L1_COMP_CFG_TBL_INC;
++      val = FIELD_PREP(PPE_L1_COMP_CFG_TBL_NODE_METER_LEN, scheduler_cfg.frame_mode);
++
++      return regmap_update_bits(ppe_dev->regmap, reg, PPE_L1_COMP_CFG_TBL_NODE_METER_LEN, val);
++}
++
++/**
++ * ppe_queue_scheduler_set - Configure scheduler for PPE hardware queue
++ * @ppe_dev: PPE device
++ * @node_id: PPE queue ID or flow ID
++ * @flow_level: Flow level scheduler or queue level scheduler
++ * @port: PPE port ID set scheduler configuration
++ * @scheduler_cfg: PPE scheduler configuration
++ *
++ * PPE scheduler configuration supports queue level and flow level on
++ * the PPE egress port.
++ *
++ * Return: 0 on success, negative error code on failure.
++ */
++int ppe_queue_scheduler_set(struct ppe_device *ppe_dev,
++                          int node_id, bool flow_level, int port,
++                          struct ppe_scheduler_cfg scheduler_cfg)
++{
++      if (flow_level)
++              return ppe_scheduler_l1_queue_map_set(ppe_dev, node_id,
++                                                    port, scheduler_cfg);
++
++      return ppe_scheduler_l0_queue_map_set(ppe_dev, node_id,
++                                            port, scheduler_cfg);
++}
++
+ static int ppe_config_bm_threshold(struct ppe_device *ppe_dev, int bm_port_id,
+                                  const struct ppe_bm_port_config port_cfg)
+ {
+@@ -358,6 +1013,133 @@ qm_config_fail:
+       return ret;
+ }
++static int ppe_node_scheduler_config(struct ppe_device *ppe_dev,
++                                   const struct ppe_scheduler_port_config config)
++{
++      struct ppe_scheduler_cfg sch_cfg;
++      int ret, i;
++
++      for (i = 0; i < config.loop_num; i++) {
++              if (!config.pri_max) {
++                      /* Round robin scheduler without priority. */
++                      sch_cfg.flow_id = config.flow_id;
++                      sch_cfg.pri = 0;
++                      sch_cfg.drr_node_id = config.drr_node_id;
++              } else {
++                      sch_cfg.flow_id = config.flow_id + (i / config.pri_max);
++                      sch_cfg.pri = i % config.pri_max;
++                      sch_cfg.drr_node_id = config.drr_node_id + i;
++              }
++
++              /* Scheduler weight, must be more than 0. */
++              sch_cfg.drr_node_wt = 1;
++              /* Byte based to be scheduled. */
++              sch_cfg.unit_is_packet = false;
++              /* Frame + CRC calculated. */
++              sch_cfg.frame_mode = PPE_SCH_WITH_FRAME_CRC;
++
++              ret = ppe_queue_scheduler_set(ppe_dev, config.node_id + i,
++                                            config.flow_level,
++                                            config.port,
++                                            sch_cfg);
++              if (ret)
++                      return ret;
++      }
++
++      return 0;
++}
++
++/* Initialize scheduler settings for PPE buffer utilization and dispatching
++ * packet on PPE queue.
++ */
++static int ppe_config_scheduler(struct ppe_device *ppe_dev)
++{
++      const struct ppe_scheduler_port_config *port_cfg;
++      const struct ppe_scheduler_qm_config *qm_cfg;
++      const struct ppe_scheduler_bm_config *bm_cfg;
++      int ret, i, count;
++      u32 val, reg;
++
++      count = ARRAY_SIZE(ipq9574_ppe_sch_bm_config);
++      bm_cfg = ipq9574_ppe_sch_bm_config;
++
++      /* Configure the depth of BM scheduler entries. */
++      val = FIELD_PREP(PPE_BM_SCH_CTRL_SCH_DEPTH, count);
++      val |= FIELD_PREP(PPE_BM_SCH_CTRL_SCH_OFFSET, 0);
++      val |= FIELD_PREP(PPE_BM_SCH_CTRL_SCH_EN, 1);
++
++      ret = regmap_write(ppe_dev->regmap, PPE_BM_SCH_CTRL_ADDR, val);
++      if (ret)
++              goto sch_config_fail;
++
++      /* Configure each BM scheduler entry with the valid ingress port and
++       * egress port, the second port takes effect when the specified port
++       * is in the inactive state.
++       */
++      for (i = 0; i < count; i++) {
++              val = FIELD_PREP(PPE_BM_SCH_CFG_TBL_VALID, bm_cfg[i].valid);
++              val |= FIELD_PREP(PPE_BM_SCH_CFG_TBL_DIR, bm_cfg[i].is_egress);
++              val |= FIELD_PREP(PPE_BM_SCH_CFG_TBL_PORT_NUM, bm_cfg[i].port);
++              val |= FIELD_PREP(PPE_BM_SCH_CFG_TBL_SECOND_PORT_VALID, bm_cfg[i].second_valid);
++              val |= FIELD_PREP(PPE_BM_SCH_CFG_TBL_SECOND_PORT, bm_cfg[i].second_port);
++
++              reg = PPE_BM_SCH_CFG_TBL_ADDR + i * PPE_BM_SCH_CFG_TBL_INC;
++              ret = regmap_write(ppe_dev->regmap, reg, val);
++              if (ret)
++                      goto sch_config_fail;
++      }
++
++      count = ARRAY_SIZE(ipq9574_ppe_sch_qm_config);
++      qm_cfg = ipq9574_ppe_sch_qm_config;
++
++      /* Configure the depth of QM scheduler entries. */
++      val = FIELD_PREP(PPE_PSCH_SCH_DEPTH_CFG_SCH_DEPTH, count);
++      ret = regmap_write(ppe_dev->regmap, PPE_PSCH_SCH_DEPTH_CFG_ADDR, val);
++      if (ret)
++              goto sch_config_fail;
++
++      /* Configure each QM scheduler entry with enqueue port and dequeue
++       * port, the second port takes effect when the specified dequeue
++       * port is in the inactive port.
++       */
++      for (i = 0; i < count; i++) {
++              val = FIELD_PREP(PPE_PSCH_SCH_CFG_TBL_ENS_PORT_BITMAP,
++                               qm_cfg[i].ensch_port_bmp);
++              val |= FIELD_PREP(PPE_PSCH_SCH_CFG_TBL_ENS_PORT,
++                                qm_cfg[i].ensch_port);
++              val |= FIELD_PREP(PPE_PSCH_SCH_CFG_TBL_DES_PORT,
++                                qm_cfg[i].desch_port);
++              val |= FIELD_PREP(PPE_PSCH_SCH_CFG_TBL_DES_SECOND_PORT_EN,
++                                qm_cfg[i].desch_second_valid);
++              val |= FIELD_PREP(PPE_PSCH_SCH_CFG_TBL_DES_SECOND_PORT,
++                                qm_cfg[i].desch_second_port);
++
++              reg = PPE_PSCH_SCH_CFG_TBL_ADDR + i * PPE_PSCH_SCH_CFG_TBL_INC;
++              ret = regmap_write(ppe_dev->regmap, reg, val);
++              if (ret)
++                      goto sch_config_fail;
++      }
++
++      count = ARRAY_SIZE(ppe_port_sch_config);
++      port_cfg = ppe_port_sch_config;
++
++      /* Configure scheduler per PPE queue or flow. */
++      for (i = 0; i < count; i++) {
++              if (port_cfg[i].port >= ppe_dev->num_ports)
++                      break;
++
++              ret = ppe_node_scheduler_config(ppe_dev, port_cfg[i]);
++              if (ret)
++                      goto sch_config_fail;
++      }
++
++      return 0;
++
++sch_config_fail:
++      dev_err(ppe_dev->dev, "PPE scheduler arbitration config error %d\n", ret);
++      return ret;
++};
++
+ int ppe_hw_config(struct ppe_device *ppe_dev)
+ {
+       int ret;
+@@ -366,5 +1148,9 @@ int ppe_hw_config(struct ppe_device *ppe
+       if (ret)
+               return ret;
+-      return ppe_config_qm(ppe_dev);
++      ret = ppe_config_qm(ppe_dev);
++      if (ret)
++              return ret;
++
++      return ppe_config_scheduler(ppe_dev);
+ }
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
+@@ -8,5 +8,42 @@
+ #include "ppe.h"
++/**
++ * enum ppe_scheduler_frame_mode - PPE scheduler frame mode.
++ * @PPE_SCH_WITH_IPG_PREAMBLE_FRAME_CRC: The scheduled frame includes IPG,
++ * preamble, Ethernet packet and CRC.
++ * @PPE_SCH_WITH_FRAME_CRC: The scheduled frame includes Ethernet frame and CRC
++ * excluding IPG and preamble.
++ * @PPE_SCH_WITH_L3_PAYLOAD: The scheduled frame includes layer 3 packet data.
++ */
++enum ppe_scheduler_frame_mode {
++      PPE_SCH_WITH_IPG_PREAMBLE_FRAME_CRC = 0,
++      PPE_SCH_WITH_FRAME_CRC = 1,
++      PPE_SCH_WITH_L3_PAYLOAD = 2,
++};
++
++/**
++ * struct ppe_scheduler_cfg - PPE scheduler configuration.
++ * @flow_id: PPE flow ID.
++ * @pri: Scheduler priority.
++ * @drr_node_id: Node ID for scheduled traffic.
++ * @drr_node_wt: Weight for scheduled traffic.
++ * @unit_is_packet: Packet based or byte based unit for scheduled traffic.
++ * @frame_mode: Packet mode to be scheduled.
++ *
++ * PPE scheduler supports commit rate and exceed rate configurations.
++ */
++struct ppe_scheduler_cfg {
++      int flow_id;
++      int pri;
++      int drr_node_id;
++      int drr_node_wt;
++      bool unit_is_packet;
++      enum ppe_scheduler_frame_mode frame_mode;
++};
++
+ int ppe_hw_config(struct ppe_device *ppe_dev);
++int ppe_queue_scheduler_set(struct ppe_device *ppe_dev,
++                          int node_id, bool flow_level, int port,
++                          struct ppe_scheduler_cfg scheduler_cfg);
+ #endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -9,16 +9,113 @@
+ #include <linux/bitfield.h>
++/* PPE scheduler configurations for buffer manager block. */
++#define PPE_BM_SCH_CTRL_ADDR                  0xb000
++#define PPE_BM_SCH_CTRL_INC                   4
++#define PPE_BM_SCH_CTRL_SCH_DEPTH             GENMASK(7, 0)
++#define PPE_BM_SCH_CTRL_SCH_OFFSET            GENMASK(14, 8)
++#define PPE_BM_SCH_CTRL_SCH_EN                        BIT(31)
++
++#define PPE_BM_SCH_CFG_TBL_ADDR                       0xc000
++#define PPE_BM_SCH_CFG_TBL_ENTRIES            128
++#define PPE_BM_SCH_CFG_TBL_INC                        0x10
++#define PPE_BM_SCH_CFG_TBL_PORT_NUM           GENMASK(3, 0)
++#define PPE_BM_SCH_CFG_TBL_DIR                        BIT(4)
++#define PPE_BM_SCH_CFG_TBL_VALID              BIT(5)
++#define PPE_BM_SCH_CFG_TBL_SECOND_PORT_VALID  BIT(6)
++#define PPE_BM_SCH_CFG_TBL_SECOND_PORT                GENMASK(11, 8)
++
+ /* PPE queue counters enable/disable control. */
+ #define PPE_EG_BRIDGE_CONFIG_ADDR             0x20044
+ #define PPE_EG_BRIDGE_CONFIG_QUEUE_CNT_EN     BIT(2)
++/* Port scheduler global config. */
++#define PPE_PSCH_SCH_DEPTH_CFG_ADDR           0x400000
++#define PPE_PSCH_SCH_DEPTH_CFG_INC            4
++#define PPE_PSCH_SCH_DEPTH_CFG_SCH_DEPTH      GENMASK(7, 0)
++
++/* PPE queue level scheduler configurations. */
++#define PPE_L0_FLOW_MAP_TBL_ADDR              0x402000
++#define PPE_L0_FLOW_MAP_TBL_ENTRIES           300
++#define PPE_L0_FLOW_MAP_TBL_INC                       0x10
++#define PPE_L0_FLOW_MAP_TBL_FLOW_ID           GENMASK(5, 0)
++#define PPE_L0_FLOW_MAP_TBL_C_PRI             GENMASK(8, 6)
++#define PPE_L0_FLOW_MAP_TBL_E_PRI             GENMASK(11, 9)
++#define PPE_L0_FLOW_MAP_TBL_C_NODE_WT         GENMASK(21, 12)
++#define PPE_L0_FLOW_MAP_TBL_E_NODE_WT         GENMASK(31, 22)
++
++#define PPE_L0_C_FLOW_CFG_TBL_ADDR            0x404000
++#define PPE_L0_C_FLOW_CFG_TBL_ENTRIES         512
++#define PPE_L0_C_FLOW_CFG_TBL_INC             0x10
++#define PPE_L0_C_FLOW_CFG_TBL_NODE_ID         GENMASK(7, 0)
++#define PPE_L0_C_FLOW_CFG_TBL_NODE_CREDIT_UNIT        BIT(8)
++
++#define PPE_L0_E_FLOW_CFG_TBL_ADDR            0x406000
++#define PPE_L0_E_FLOW_CFG_TBL_ENTRIES         512
++#define PPE_L0_E_FLOW_CFG_TBL_INC             0x10
++#define PPE_L0_E_FLOW_CFG_TBL_NODE_ID         GENMASK(7, 0)
++#define PPE_L0_E_FLOW_CFG_TBL_NODE_CREDIT_UNIT        BIT(8)
++
++#define PPE_L0_FLOW_PORT_MAP_TBL_ADDR         0x408000
++#define PPE_L0_FLOW_PORT_MAP_TBL_ENTRIES      300
++#define PPE_L0_FLOW_PORT_MAP_TBL_INC          0x10
++#define PPE_L0_FLOW_PORT_MAP_TBL_PORT_NUM     GENMASK(3, 0)
++
++#define PPE_L0_COMP_CFG_TBL_ADDR              0x428000
++#define PPE_L0_COMP_CFG_TBL_ENTRIES           300
++#define PPE_L0_COMP_CFG_TBL_INC                       0x10
++#define PPE_L0_COMP_CFG_TBL_SHAPER_METER_LEN  GENMASK(1, 0)
++#define PPE_L0_COMP_CFG_TBL_NODE_METER_LEN    GENMASK(3, 2)
++
+ /* Table addresses for per-queue dequeue setting. */
+ #define PPE_DEQ_OPR_TBL_ADDR                  0x430000
+ #define PPE_DEQ_OPR_TBL_ENTRIES                       300
+ #define PPE_DEQ_OPR_TBL_INC                   0x10
+ #define PPE_DEQ_OPR_TBL_DEQ_DISABLE           BIT(0)
++/* PPE flow level scheduler configurations. */
++#define PPE_L1_FLOW_MAP_TBL_ADDR              0x440000
++#define PPE_L1_FLOW_MAP_TBL_ENTRIES           64
++#define PPE_L1_FLOW_MAP_TBL_INC                       0x10
++#define PPE_L1_FLOW_MAP_TBL_FLOW_ID           GENMASK(3, 0)
++#define PPE_L1_FLOW_MAP_TBL_C_PRI             GENMASK(6, 4)
++#define PPE_L1_FLOW_MAP_TBL_E_PRI             GENMASK(9, 7)
++#define PPE_L1_FLOW_MAP_TBL_C_NODE_WT         GENMASK(19, 10)
++#define PPE_L1_FLOW_MAP_TBL_E_NODE_WT         GENMASK(29, 20)
++
++#define PPE_L1_C_FLOW_CFG_TBL_ADDR            0x442000
++#define PPE_L1_C_FLOW_CFG_TBL_ENTRIES         64
++#define PPE_L1_C_FLOW_CFG_TBL_INC             0x10
++#define PPE_L1_C_FLOW_CFG_TBL_NODE_ID         GENMASK(5, 0)
++#define PPE_L1_C_FLOW_CFG_TBL_NODE_CREDIT_UNIT        BIT(6)
++
++#define PPE_L1_E_FLOW_CFG_TBL_ADDR            0x444000
++#define PPE_L1_E_FLOW_CFG_TBL_ENTRIES         64
++#define PPE_L1_E_FLOW_CFG_TBL_INC             0x10
++#define PPE_L1_E_FLOW_CFG_TBL_NODE_ID         GENMASK(5, 0)
++#define PPE_L1_E_FLOW_CFG_TBL_NODE_CREDIT_UNIT        BIT(6)
++
++#define PPE_L1_FLOW_PORT_MAP_TBL_ADDR         0x446000
++#define PPE_L1_FLOW_PORT_MAP_TBL_ENTRIES      64
++#define PPE_L1_FLOW_PORT_MAP_TBL_INC          0x10
++#define PPE_L1_FLOW_PORT_MAP_TBL_PORT_NUM     GENMASK(3, 0)
++
++#define PPE_L1_COMP_CFG_TBL_ADDR              0x46a000
++#define PPE_L1_COMP_CFG_TBL_ENTRIES           64
++#define PPE_L1_COMP_CFG_TBL_INC                       0x10
++#define PPE_L1_COMP_CFG_TBL_SHAPER_METER_LEN  GENMASK(1, 0)
++#define PPE_L1_COMP_CFG_TBL_NODE_METER_LEN    GENMASK(3, 2)
++
++/* PPE port scheduler configurations for egress. */
++#define PPE_PSCH_SCH_CFG_TBL_ADDR             0x47a000
++#define PPE_PSCH_SCH_CFG_TBL_ENTRIES          128
++#define PPE_PSCH_SCH_CFG_TBL_INC              0x10
++#define PPE_PSCH_SCH_CFG_TBL_DES_PORT         GENMASK(3, 0)
++#define PPE_PSCH_SCH_CFG_TBL_ENS_PORT         GENMASK(7, 4)
++#define PPE_PSCH_SCH_CFG_TBL_ENS_PORT_BITMAP  GENMASK(15, 8)
++#define PPE_PSCH_SCH_CFG_TBL_DES_SECOND_PORT_EN       BIT(16)
++#define PPE_PSCH_SCH_CFG_TBL_DES_SECOND_PORT  GENMASK(20, 17)
++
+ /* There are 15 BM ports and 4 BM groups supported by PPE.
+  * BM port (0-7) is for EDMA port 0, BM port (8-13) is for
+  * PPE physical port 1-6 and BM port 14 is for EIP port.
diff --git a/target/linux/qualcommbe/patches-6.18/0329-net-ethernet-qualcomm-Initialize-PPE-queue-settings.patch b/target/linux/qualcommbe/patches-6.18/0329-net-ethernet-qualcomm-Initialize-PPE-queue-settings.patch
new file mode 100644 (file)
index 0000000..fe29d76
--- /dev/null
@@ -0,0 +1,522 @@
+From 63874f7c2e46f192e43e6214d66236372e36396c Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 9 Feb 2025 22:29:41 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Initialize PPE queue settings
+
+Configure unicast and multicast hardware queues for the PPE
+ports to enable packet forwarding between the ports.
+
+Each PPE port is assigned with a range of queues. The queue ID
+selection for a packet is decided by the queue base and queue
+offset that is configured based on the internal priority and
+the RSS hash value of the packet.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ .../net/ethernet/qualcomm/ppe/ppe_config.c    | 356 +++++++++++++++++-
+ .../net/ethernet/qualcomm/ppe/ppe_config.h    |  63 ++++
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h  |  21 ++
+ 3 files changed, 439 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
+@@ -128,6 +128,34 @@ struct ppe_scheduler_port_config {
+       unsigned int drr_node_id;
+ };
++/**
++ * struct ppe_port_schedule_resource - PPE port scheduler resource.
++ * @ucastq_start: Unicast queue start ID.
++ * @ucastq_end: Unicast queue end ID.
++ * @mcastq_start: Multicast queue start ID.
++ * @mcastq_end: Multicast queue end ID.
++ * @flow_id_start: Flow start ID.
++ * @flow_id_end: Flow end ID.
++ * @l0node_start: Scheduler node start ID for queue level.
++ * @l0node_end: Scheduler node end ID for queue level.
++ * @l1node_start: Scheduler node start ID for flow level.
++ * @l1node_end: Scheduler node end ID for flow level.
++ *
++ * PPE scheduler resource allocated among the PPE ports.
++ */
++struct ppe_port_schedule_resource {
++      unsigned int ucastq_start;
++      unsigned int ucastq_end;
++      unsigned int mcastq_start;
++      unsigned int mcastq_end;
++      unsigned int flow_id_start;
++      unsigned int flow_id_end;
++      unsigned int l0node_start;
++      unsigned int l0node_end;
++      unsigned int l1node_start;
++      unsigned int l1node_end;
++};
++
+ /* Assign the share buffer number 1550 to group 0 by default. */
+ static const int ipq9574_ppe_bm_group_config = 1550;
+@@ -676,6 +704,111 @@ static const struct ppe_scheduler_port_c
+       },
+ };
++/* The scheduler resource is applied to each PPE port, The resource
++ * includes the unicast & multicast queues, flow nodes and DRR nodes.
++ */
++static const struct ppe_port_schedule_resource ppe_scheduler_res[] = {
++      {       .ucastq_start   = 0,
++              .ucastq_end     = 63,
++              .mcastq_start   = 256,
++              .mcastq_end     = 271,
++              .flow_id_start  = 0,
++              .flow_id_end    = 0,
++              .l0node_start   = 0,
++              .l0node_end     = 7,
++              .l1node_start   = 0,
++              .l1node_end     = 0,
++      },
++      {       .ucastq_start   = 144,
++              .ucastq_end     = 159,
++              .mcastq_start   = 272,
++              .mcastq_end     = 275,
++              .flow_id_start  = 36,
++              .flow_id_end    = 39,
++              .l0node_start   = 48,
++              .l0node_end     = 63,
++              .l1node_start   = 8,
++              .l1node_end     = 11,
++      },
++      {       .ucastq_start   = 160,
++              .ucastq_end     = 175,
++              .mcastq_start   = 276,
++              .mcastq_end     = 279,
++              .flow_id_start  = 40,
++              .flow_id_end    = 43,
++              .l0node_start   = 64,
++              .l0node_end     = 79,
++              .l1node_start   = 12,
++              .l1node_end     = 15,
++      },
++      {       .ucastq_start   = 176,
++              .ucastq_end     = 191,
++              .mcastq_start   = 280,
++              .mcastq_end     = 283,
++              .flow_id_start  = 44,
++              .flow_id_end    = 47,
++              .l0node_start   = 80,
++              .l0node_end     = 95,
++              .l1node_start   = 16,
++              .l1node_end     = 19,
++      },
++      {       .ucastq_start   = 192,
++              .ucastq_end     = 207,
++              .mcastq_start   = 284,
++              .mcastq_end     = 287,
++              .flow_id_start  = 48,
++              .flow_id_end    = 51,
++              .l0node_start   = 96,
++              .l0node_end     = 111,
++              .l1node_start   = 20,
++              .l1node_end     = 23,
++      },
++      {       .ucastq_start   = 208,
++              .ucastq_end     = 223,
++              .mcastq_start   = 288,
++              .mcastq_end     = 291,
++              .flow_id_start  = 52,
++              .flow_id_end    = 55,
++              .l0node_start   = 112,
++              .l0node_end     = 127,
++              .l1node_start   = 24,
++              .l1node_end     = 27,
++      },
++      {       .ucastq_start   = 224,
++              .ucastq_end     = 239,
++              .mcastq_start   = 292,
++              .mcastq_end     = 295,
++              .flow_id_start  = 56,
++              .flow_id_end    = 59,
++              .l0node_start   = 128,
++              .l0node_end     = 143,
++              .l1node_start   = 28,
++              .l1node_end     = 31,
++      },
++      {       .ucastq_start   = 240,
++              .ucastq_end     = 255,
++              .mcastq_start   = 296,
++              .mcastq_end     = 299,
++              .flow_id_start  = 60,
++              .flow_id_end    = 63,
++              .l0node_start   = 144,
++              .l0node_end     = 159,
++              .l1node_start   = 32,
++              .l1node_end     = 35,
++      },
++      {       .ucastq_start   = 64,
++              .ucastq_end     = 143,
++              .mcastq_start   = 0,
++              .mcastq_end     = 0,
++              .flow_id_start  = 1,
++              .flow_id_end    = 35,
++              .l0node_start   = 8,
++              .l0node_end     = 47,
++              .l1node_start   = 1,
++              .l1node_end     = 7,
++      },
++};
++
+ /* Set the PPE queue level scheduler configuration. */
+ static int ppe_scheduler_l0_queue_map_set(struct ppe_device *ppe_dev,
+                                         int node_id, int port,
+@@ -807,6 +940,149 @@ int ppe_queue_scheduler_set(struct ppe_d
+                                             port, scheduler_cfg);
+ }
++/**
++ * ppe_queue_ucast_base_set - Set PPE unicast queue base ID and profile ID
++ * @ppe_dev: PPE device
++ * @queue_dst: PPE queue destination configuration
++ * @queue_base: PPE queue base ID
++ * @profile_id: Profile ID
++ *
++ * The PPE unicast queue base ID and profile ID are configured based on the
++ * destination port information that can be service code or CPU code or the
++ * destination port.
++ *
++ * Return: 0 on success, negative error code on failure.
++ */
++int ppe_queue_ucast_base_set(struct ppe_device *ppe_dev,
++                           struct ppe_queue_ucast_dest queue_dst,
++                           int queue_base, int profile_id)
++{
++      int index, profile_size;
++      u32 val, reg;
++
++      profile_size = queue_dst.src_profile << 8;
++      if (queue_dst.service_code_en)
++              index = PPE_QUEUE_BASE_SERVICE_CODE + profile_size +
++                      queue_dst.service_code;
++      else if (queue_dst.cpu_code_en)
++              index = PPE_QUEUE_BASE_CPU_CODE + profile_size +
++                      queue_dst.cpu_code;
++      else
++              index = profile_size + queue_dst.dest_port;
++
++      val = FIELD_PREP(PPE_UCAST_QUEUE_MAP_TBL_PROFILE_ID, profile_id);
++      val |= FIELD_PREP(PPE_UCAST_QUEUE_MAP_TBL_QUEUE_ID, queue_base);
++      reg = PPE_UCAST_QUEUE_MAP_TBL_ADDR + index * PPE_UCAST_QUEUE_MAP_TBL_INC;
++
++      return regmap_write(ppe_dev->regmap, reg, val);
++}
++
++/**
++ * ppe_queue_ucast_offset_pri_set - Set PPE unicast queue offset based on priority
++ * @ppe_dev: PPE device
++ * @profile_id: Profile ID
++ * @priority: PPE internal priority to be used to set queue offset
++ * @queue_offset: Queue offset used for calculating the destination queue ID
++ *
++ * The PPE unicast queue offset is configured based on the PPE
++ * internal priority.
++ *
++ * Return: 0 on success, negative error code on failure.
++ */
++int ppe_queue_ucast_offset_pri_set(struct ppe_device *ppe_dev,
++                                 int profile_id,
++                                 int priority,
++                                 int queue_offset)
++{
++      u32 val, reg;
++      int index;
++
++      index = (profile_id << 4) + priority;
++      val = FIELD_PREP(PPE_UCAST_PRIORITY_MAP_TBL_CLASS, queue_offset);
++      reg = PPE_UCAST_PRIORITY_MAP_TBL_ADDR + index * PPE_UCAST_PRIORITY_MAP_TBL_INC;
++
++      return regmap_write(ppe_dev->regmap, reg, val);
++}
++
++/**
++ * ppe_queue_ucast_offset_hash_set - Set PPE unicast queue offset based on hash
++ * @ppe_dev: PPE device
++ * @profile_id: Profile ID
++ * @rss_hash: Packet hash value to be used to set queue offset
++ * @queue_offset: Queue offset used for calculating the destination queue ID
++ *
++ * The PPE unicast queue offset is configured based on the RSS hash value.
++ *
++ * Return: 0 on success, negative error code on failure.
++ */
++int ppe_queue_ucast_offset_hash_set(struct ppe_device *ppe_dev,
++                                  int profile_id,
++                                  int rss_hash,
++                                  int queue_offset)
++{
++      u32 val, reg;
++      int index;
++
++      index = (profile_id << 8) + rss_hash;
++      val = FIELD_PREP(PPE_UCAST_HASH_MAP_TBL_HASH, queue_offset);
++      reg = PPE_UCAST_HASH_MAP_TBL_ADDR + index * PPE_UCAST_HASH_MAP_TBL_INC;
++
++      return regmap_write(ppe_dev->regmap, reg, val);
++}
++
++/**
++ * ppe_port_resource_get - Get PPE resource per port
++ * @ppe_dev: PPE device
++ * @port: PPE port
++ * @type: Resource type
++ * @res_start: Resource start ID returned
++ * @res_end: Resource end ID returned
++ *
++ * PPE resource is assigned per PPE port, which is acquired for QoS scheduler.
++ *
++ * Return: 0 on success, negative error code on failure.
++ */
++int ppe_port_resource_get(struct ppe_device *ppe_dev, int port,
++                        enum ppe_resource_type type,
++                        int *res_start, int *res_end)
++{
++      struct ppe_port_schedule_resource res;
++
++      /* The reserved resource with the maximum port ID of PPE is
++       * also allowed to be acquired.
++       */
++      if (port > ppe_dev->num_ports)
++              return -EINVAL;
++
++      res = ppe_scheduler_res[port];
++      switch (type) {
++      case PPE_RES_UCAST:
++              *res_start = res.ucastq_start;
++              *res_end = res.ucastq_end;
++              break;
++      case PPE_RES_MCAST:
++              *res_start = res.mcastq_start;
++              *res_end = res.mcastq_end;
++              break;
++      case PPE_RES_FLOW_ID:
++              *res_start = res.flow_id_start;
++              *res_end = res.flow_id_end;
++              break;
++      case PPE_RES_L0_NODE:
++              *res_start = res.l0node_start;
++              *res_end = res.l0node_end;
++              break;
++      case PPE_RES_L1_NODE:
++              *res_start = res.l1node_start;
++              *res_end = res.l1node_end;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
+ static int ppe_config_bm_threshold(struct ppe_device *ppe_dev, int bm_port_id,
+                                  const struct ppe_bm_port_config port_cfg)
+ {
+@@ -1140,6 +1416,80 @@ sch_config_fail:
+       return ret;
+ };
++/* Configure PPE queue destination of each PPE port. */
++static int ppe_queue_dest_init(struct ppe_device *ppe_dev)
++{
++      int ret, port_id, index, q_base, q_offset, res_start, res_end, pri_max;
++      struct ppe_queue_ucast_dest queue_dst;
++
++      for (port_id = 0; port_id < ppe_dev->num_ports; port_id++) {
++              memset(&queue_dst, 0, sizeof(queue_dst));
++
++              ret = ppe_port_resource_get(ppe_dev, port_id, PPE_RES_UCAST,
++                                          &res_start, &res_end);
++              if (ret)
++                      return ret;
++
++              q_base = res_start;
++              queue_dst.dest_port = port_id;
++
++              /* Configure queue base ID and profile ID that is same as
++               * physical port ID.
++               */
++              ret = ppe_queue_ucast_base_set(ppe_dev, queue_dst,
++                                             q_base, port_id);
++              if (ret)
++                      return ret;
++
++              /* Queue priority range supported by each PPE port */
++              ret = ppe_port_resource_get(ppe_dev, port_id, PPE_RES_L0_NODE,
++                                          &res_start, &res_end);
++              if (ret)
++                      return ret;
++
++              pri_max = res_end - res_start;
++
++              /* Redirect ARP reply packet with the max priority on CPU port,
++               * which keeps the ARP reply directed to CPU (CPU code is 101)
++               * with highest priority queue of EDMA.
++               */
++              if (port_id == 0) {
++                      memset(&queue_dst, 0, sizeof(queue_dst));
++
++                      queue_dst.cpu_code_en = true;
++                      queue_dst.cpu_code = 101;
++                      ret = ppe_queue_ucast_base_set(ppe_dev, queue_dst,
++                                                     q_base + pri_max,
++                                                     0);
++                      if (ret)
++                              return ret;
++              }
++
++              /* Initialize the queue offset of internal priority. */
++              for (index = 0; index < PPE_QUEUE_INTER_PRI_NUM; index++) {
++                      q_offset = index > pri_max ? pri_max : index;
++
++                      ret = ppe_queue_ucast_offset_pri_set(ppe_dev, port_id,
++                                                           index, q_offset);
++                      if (ret)
++                              return ret;
++              }
++
++              /* Initialize the queue offset of RSS hash as 0 to avoid the
++               * random hardware value that will lead to the unexpected
++               * destination queue generated.
++               */
++              for (index = 0; index < PPE_QUEUE_HASH_NUM; index++) {
++                      ret = ppe_queue_ucast_offset_hash_set(ppe_dev, port_id,
++                                                            index, 0);
++                      if (ret)
++                              return ret;
++              }
++      }
++
++      return 0;
++}
++
+ int ppe_hw_config(struct ppe_device *ppe_dev)
+ {
+       int ret;
+@@ -1152,5 +1502,9 @@ int ppe_hw_config(struct ppe_device *ppe
+       if (ret)
+               return ret;
+-      return ppe_config_scheduler(ppe_dev);
++      ret = ppe_config_scheduler(ppe_dev);
++      if (ret)
++              return ret;
++
++      return ppe_queue_dest_init(ppe_dev);
+ }
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
+@@ -8,6 +8,16 @@
+ #include "ppe.h"
++/* There are different table index ranges for configuring queue base ID of
++ * the destination port, CPU code and service code.
++ */
++#define PPE_QUEUE_BASE_DEST_PORT              0
++#define PPE_QUEUE_BASE_CPU_CODE                       1024
++#define PPE_QUEUE_BASE_SERVICE_CODE           2048
++
++#define PPE_QUEUE_INTER_PRI_NUM                       16
++#define PPE_QUEUE_HASH_NUM                    256
++
+ /**
+  * enum ppe_scheduler_frame_mode - PPE scheduler frame mode.
+  * @PPE_SCH_WITH_IPG_PREAMBLE_FRAME_CRC: The scheduled frame includes IPG,
+@@ -42,8 +52,61 @@ struct ppe_scheduler_cfg {
+       enum ppe_scheduler_frame_mode frame_mode;
+ };
++/**
++ * enum ppe_resource_type - PPE resource type.
++ * @PPE_RES_UCAST: Unicast queue resource.
++ * @PPE_RES_MCAST: Multicast queue resource.
++ * @PPE_RES_L0_NODE: Level 0 for queue based node resource.
++ * @PPE_RES_L1_NODE: Level 1 for flow based node resource.
++ * @PPE_RES_FLOW_ID: Flow based node resource.
++ */
++enum ppe_resource_type {
++      PPE_RES_UCAST,
++      PPE_RES_MCAST,
++      PPE_RES_L0_NODE,
++      PPE_RES_L1_NODE,
++      PPE_RES_FLOW_ID,
++};
++
++/**
++ * struct ppe_queue_ucast_dest - PPE unicast queue destination.
++ * @src_profile: Source profile.
++ * @service_code_en: Enable service code to map the queue base ID.
++ * @service_code: Service code.
++ * @cpu_code_en: Enable CPU code to map the queue base ID.
++ * @cpu_code: CPU code.
++ * @dest_port: destination port.
++ *
++ * PPE egress queue ID is decided by the service code if enabled, otherwise
++ * by the CPU code if enabled, or by destination port if both service code
++ * and CPU code are disabled.
++ */
++struct ppe_queue_ucast_dest {
++      int src_profile;
++      bool service_code_en;
++      int service_code;
++      bool cpu_code_en;
++      int cpu_code;
++      int dest_port;
++};
++
+ int ppe_hw_config(struct ppe_device *ppe_dev);
+ int ppe_queue_scheduler_set(struct ppe_device *ppe_dev,
+                           int node_id, bool flow_level, int port,
+                           struct ppe_scheduler_cfg scheduler_cfg);
++int ppe_queue_ucast_base_set(struct ppe_device *ppe_dev,
++                           struct ppe_queue_ucast_dest queue_dst,
++                           int queue_base,
++                           int profile_id);
++int ppe_queue_ucast_offset_pri_set(struct ppe_device *ppe_dev,
++                                 int profile_id,
++                                 int priority,
++                                 int queue_offset);
++int ppe_queue_ucast_offset_hash_set(struct ppe_device *ppe_dev,
++                                  int profile_id,
++                                  int rss_hash,
++                                  int queue_offset);
++int ppe_port_resource_get(struct ppe_device *ppe_dev, int port,
++                        enum ppe_resource_type type,
++                        int *res_start, int *res_end);
+ #endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -164,6 +164,27 @@
+ #define PPE_BM_PORT_FC_SET_PRE_ALLOC(tbl_cfg, value)  \
+       u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_BM_PORT_FC_W1_PRE_ALLOC)
++/* The queue base configurations based on destination port,
++ * service code or CPU code.
++ */
++#define PPE_UCAST_QUEUE_MAP_TBL_ADDR          0x810000
++#define PPE_UCAST_QUEUE_MAP_TBL_ENTRIES               3072
++#define PPE_UCAST_QUEUE_MAP_TBL_INC           0x10
++#define PPE_UCAST_QUEUE_MAP_TBL_PROFILE_ID    GENMASK(3, 0)
++#define PPE_UCAST_QUEUE_MAP_TBL_QUEUE_ID      GENMASK(11, 4)
++
++/* The queue offset configurations based on RSS hash value. */
++#define PPE_UCAST_HASH_MAP_TBL_ADDR           0x830000
++#define PPE_UCAST_HASH_MAP_TBL_ENTRIES                4096
++#define PPE_UCAST_HASH_MAP_TBL_INC            0x10
++#define PPE_UCAST_HASH_MAP_TBL_HASH           GENMASK(7, 0)
++
++/* The queue offset configurations based on PPE internal priority. */
++#define PPE_UCAST_PRIORITY_MAP_TBL_ADDR               0x842000
++#define PPE_UCAST_PRIORITY_MAP_TBL_ENTRIES    256
++#define PPE_UCAST_PRIORITY_MAP_TBL_INC                0x10
++#define PPE_UCAST_PRIORITY_MAP_TBL_CLASS      GENMASK(3, 0)
++
+ /* PPE unicast queue (0-255) configurations. */
+ #define PPE_AC_UNICAST_QUEUE_CFG_TBL_ADDR     0x848000
+ #define PPE_AC_UNICAST_QUEUE_CFG_TBL_ENTRIES  256
diff --git a/target/linux/qualcommbe/patches-6.18/0330-net-ethernet-qualcomm-Initialize-PPE-service-code-se.patch b/target/linux/qualcommbe/patches-6.18/0330-net-ethernet-qualcomm-Initialize-PPE-service-code-se.patch
new file mode 100644 (file)
index 0000000..176b7d6
--- /dev/null
@@ -0,0 +1,384 @@
+From 4147ce0d95816bded5c5e6cb276b1aa9f2620045 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 9 Feb 2025 22:29:42 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Initialize PPE service code settings
+
+PPE service code is a special code (0-255) that is defined by PPE for
+PPE's packet processing stages, as per the network functions required
+for the packet.
+
+For packet being sent out by ARM cores on Ethernet ports, The service
+code 1 is used as the default service code. This service code is used
+to bypass most of packet processing stages of the PPE before the packet
+transmitted out PPE port, since the software network stack has already
+processed the packet.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ .../net/ethernet/qualcomm/ppe/ppe_config.c    |  95 +++++++++++-
+ .../net/ethernet/qualcomm/ppe/ppe_config.h    | 145 ++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h  |  53 +++++++
+ 3 files changed, 292 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
+@@ -8,6 +8,7 @@
+  */
+ #include <linux/bitfield.h>
++#include <linux/bitmap.h>
+ #include <linux/bits.h>
+ #include <linux/device.h>
+ #include <linux/regmap.h>
+@@ -1083,6 +1084,75 @@ int ppe_port_resource_get(struct ppe_dev
+       return 0;
+ }
++/**
++ * ppe_sc_config_set - Set PPE service code configuration
++ * @ppe_dev: PPE device
++ * @sc: Service ID, 0-255 supported by PPE
++ * @cfg: Service code configuration
++ *
++ * PPE service code is used by the PPE during its packet processing stages,
++ * to perform or bypass certain selected packet operations on the packet.
++ *
++ * Return: 0 on success, negative error code on failure.
++ */
++int ppe_sc_config_set(struct ppe_device *ppe_dev, int sc, struct ppe_sc_cfg cfg)
++{
++      u32 val, reg, servcode_val[2] = {};
++      unsigned long bitmap_value;
++      int ret;
++
++      val = FIELD_PREP(PPE_IN_L2_SERVICE_TBL_DST_PORT_ID_VALID, cfg.dest_port_valid);
++      val |= FIELD_PREP(PPE_IN_L2_SERVICE_TBL_DST_PORT_ID, cfg.dest_port);
++      val |= FIELD_PREP(PPE_IN_L2_SERVICE_TBL_DST_DIRECTION, cfg.is_src);
++
++      bitmap_value = bitmap_read(cfg.bitmaps.egress, 0, PPE_SC_BYPASS_EGRESS_SIZE);
++      val |= FIELD_PREP(PPE_IN_L2_SERVICE_TBL_DST_BYPASS_BITMAP, bitmap_value);
++      val |= FIELD_PREP(PPE_IN_L2_SERVICE_TBL_RX_CNT_EN,
++                        test_bit(PPE_SC_BYPASS_COUNTER_RX, cfg.bitmaps.counter));
++      val |= FIELD_PREP(PPE_IN_L2_SERVICE_TBL_TX_CNT_EN,
++                        test_bit(PPE_SC_BYPASS_COUNTER_TX, cfg.bitmaps.counter));
++      reg = PPE_IN_L2_SERVICE_TBL_ADDR + PPE_IN_L2_SERVICE_TBL_INC * sc;
++
++      ret = regmap_write(ppe_dev->regmap, reg, val);
++      if (ret)
++              return ret;
++
++      bitmap_value = bitmap_read(cfg.bitmaps.ingress, 0, PPE_SC_BYPASS_INGRESS_SIZE);
++      PPE_SERVICE_SET_BYPASS_BITMAP(servcode_val, bitmap_value);
++      PPE_SERVICE_SET_RX_CNT_EN(servcode_val,
++                                test_bit(PPE_SC_BYPASS_COUNTER_RX_VLAN, cfg.bitmaps.counter));
++      reg = PPE_SERVICE_TBL_ADDR + PPE_SERVICE_TBL_INC * sc;
++
++      ret = regmap_bulk_write(ppe_dev->regmap, reg,
++                              servcode_val, ARRAY_SIZE(servcode_val));
++      if (ret)
++              return ret;
++
++      reg = PPE_EG_SERVICE_TBL_ADDR + PPE_EG_SERVICE_TBL_INC * sc;
++      ret = regmap_bulk_read(ppe_dev->regmap, reg,
++                             servcode_val, ARRAY_SIZE(servcode_val));
++      if (ret)
++              return ret;
++
++      PPE_EG_SERVICE_SET_NEXT_SERVCODE(servcode_val, cfg.next_service_code);
++      PPE_EG_SERVICE_SET_UPDATE_ACTION(servcode_val, cfg.eip_field_update_bitmap);
++      PPE_EG_SERVICE_SET_HW_SERVICE(servcode_val, cfg.eip_hw_service);
++      PPE_EG_SERVICE_SET_OFFSET_SEL(servcode_val, cfg.eip_offset_sel);
++      PPE_EG_SERVICE_SET_TX_CNT_EN(servcode_val,
++                                   test_bit(PPE_SC_BYPASS_COUNTER_TX_VLAN, cfg.bitmaps.counter));
++
++      ret = regmap_bulk_write(ppe_dev->regmap, reg,
++                              servcode_val, ARRAY_SIZE(servcode_val));
++      if (ret)
++              return ret;
++
++      bitmap_value = bitmap_read(cfg.bitmaps.tunnel, 0, PPE_SC_BYPASS_TUNNEL_SIZE);
++      val = FIELD_PREP(PPE_TL_SERVICE_TBL_BYPASS_BITMAP, bitmap_value);
++      reg = PPE_TL_SERVICE_TBL_ADDR + PPE_TL_SERVICE_TBL_INC * sc;
++
++      return regmap_write(ppe_dev->regmap, reg, val);
++}
++
+ static int ppe_config_bm_threshold(struct ppe_device *ppe_dev, int bm_port_id,
+                                  const struct ppe_bm_port_config port_cfg)
+ {
+@@ -1490,6 +1560,25 @@ static int ppe_queue_dest_init(struct pp
+       return 0;
+ }
++/* Initialize the service code 1 used by CPU port. */
++static int ppe_servcode_init(struct ppe_device *ppe_dev)
++{
++      struct ppe_sc_cfg sc_cfg = {};
++
++      bitmap_zero(sc_cfg.bitmaps.counter, PPE_SC_BYPASS_COUNTER_SIZE);
++      bitmap_zero(sc_cfg.bitmaps.tunnel, PPE_SC_BYPASS_TUNNEL_SIZE);
++
++      bitmap_fill(sc_cfg.bitmaps.ingress, PPE_SC_BYPASS_INGRESS_SIZE);
++      clear_bit(PPE_SC_BYPASS_INGRESS_FAKE_MAC_HEADER, sc_cfg.bitmaps.ingress);
++      clear_bit(PPE_SC_BYPASS_INGRESS_SERVICE_CODE, sc_cfg.bitmaps.ingress);
++      clear_bit(PPE_SC_BYPASS_INGRESS_FAKE_L2_PROTO, sc_cfg.bitmaps.ingress);
++
++      bitmap_fill(sc_cfg.bitmaps.egress, PPE_SC_BYPASS_EGRESS_SIZE);
++      clear_bit(PPE_SC_BYPASS_EGRESS_ACL_POST_ROUTING_CHECK, sc_cfg.bitmaps.egress);
++
++      return ppe_sc_config_set(ppe_dev, PPE_EDMA_SC_BYPASS_ID, sc_cfg);
++}
++
+ int ppe_hw_config(struct ppe_device *ppe_dev)
+ {
+       int ret;
+@@ -1506,5 +1595,9 @@ int ppe_hw_config(struct ppe_device *ppe
+       if (ret)
+               return ret;
+-      return ppe_queue_dest_init(ppe_dev);
++      ret = ppe_queue_dest_init(ppe_dev);
++      if (ret)
++              return ret;
++
++      return ppe_servcode_init(ppe_dev);
+ }
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
+@@ -6,6 +6,8 @@
+ #ifndef __PPE_CONFIG_H__
+ #define __PPE_CONFIG_H__
++#include <linux/types.h>
++
+ #include "ppe.h"
+ /* There are different table index ranges for configuring queue base ID of
+@@ -18,6 +20,9 @@
+ #define PPE_QUEUE_INTER_PRI_NUM                       16
+ #define PPE_QUEUE_HASH_NUM                    256
++/* The service code is used by EDMA port to transmit packet to PPE. */
++#define PPE_EDMA_SC_BYPASS_ID                 1
++
+ /**
+  * enum ppe_scheduler_frame_mode - PPE scheduler frame mode.
+  * @PPE_SCH_WITH_IPG_PREAMBLE_FRAME_CRC: The scheduled frame includes IPG,
+@@ -90,6 +95,144 @@ struct ppe_queue_ucast_dest {
+       int dest_port;
+ };
++/* Hardware bitmaps for bypassing features of the ingress packet. */
++enum ppe_sc_ingress_type {
++      PPE_SC_BYPASS_INGRESS_VLAN_TAG_FMT_CHECK = 0,
++      PPE_SC_BYPASS_INGRESS_VLAN_MEMBER_CHECK = 1,
++      PPE_SC_BYPASS_INGRESS_VLAN_TRANSLATE = 2,
++      PPE_SC_BYPASS_INGRESS_MY_MAC_CHECK = 3,
++      PPE_SC_BYPASS_INGRESS_DIP_LOOKUP = 4,
++      PPE_SC_BYPASS_INGRESS_FLOW_LOOKUP = 5,
++      PPE_SC_BYPASS_INGRESS_FLOW_ACTION = 6,
++      PPE_SC_BYPASS_INGRESS_ACL = 7,
++      PPE_SC_BYPASS_INGRESS_FAKE_MAC_HEADER = 8,
++      PPE_SC_BYPASS_INGRESS_SERVICE_CODE = 9,
++      PPE_SC_BYPASS_INGRESS_WRONG_PKT_FMT_L2 = 10,
++      PPE_SC_BYPASS_INGRESS_WRONG_PKT_FMT_L3_IPV4 = 11,
++      PPE_SC_BYPASS_INGRESS_WRONG_PKT_FMT_L3_IPV6 = 12,
++      PPE_SC_BYPASS_INGRESS_WRONG_PKT_FMT_L4 = 13,
++      PPE_SC_BYPASS_INGRESS_FLOW_SERVICE_CODE = 14,
++      PPE_SC_BYPASS_INGRESS_ACL_SERVICE_CODE = 15,
++      PPE_SC_BYPASS_INGRESS_FAKE_L2_PROTO = 16,
++      PPE_SC_BYPASS_INGRESS_PPPOE_TERMINATION = 17,
++      PPE_SC_BYPASS_INGRESS_DEFAULT_VLAN = 18,
++      PPE_SC_BYPASS_INGRESS_DEFAULT_PCP = 19,
++      PPE_SC_BYPASS_INGRESS_VSI_ASSIGN = 20,
++      /* Values 21-23 are not specified by hardware. */
++      PPE_SC_BYPASS_INGRESS_VLAN_ASSIGN_FAIL = 24,
++      PPE_SC_BYPASS_INGRESS_SOURCE_GUARD = 25,
++      PPE_SC_BYPASS_INGRESS_MRU_MTU_CHECK = 26,
++      PPE_SC_BYPASS_INGRESS_FLOW_SRC_CHECK = 27,
++      PPE_SC_BYPASS_INGRESS_FLOW_QOS = 28,
++      /* This must be last as it determines the size of the BITMAP. */
++      PPE_SC_BYPASS_INGRESS_SIZE,
++};
++
++/* Hardware bitmaps for bypassing features of the egress packet. */
++enum ppe_sc_egress_type {
++      PPE_SC_BYPASS_EGRESS_VLAN_MEMBER_CHECK = 0,
++      PPE_SC_BYPASS_EGRESS_VLAN_TRANSLATE = 1,
++      PPE_SC_BYPASS_EGRESS_VLAN_TAG_FMT_CTRL = 2,
++      PPE_SC_BYPASS_EGRESS_FDB_LEARN = 3,
++      PPE_SC_BYPASS_EGRESS_FDB_REFRESH = 4,
++      PPE_SC_BYPASS_EGRESS_L2_SOURCE_SECURITY = 5,
++      PPE_SC_BYPASS_EGRESS_MANAGEMENT_FWD = 6,
++      PPE_SC_BYPASS_EGRESS_BRIDGING_FWD = 7,
++      PPE_SC_BYPASS_EGRESS_IN_STP_FLTR = 8,
++      PPE_SC_BYPASS_EGRESS_EG_STP_FLTR = 9,
++      PPE_SC_BYPASS_EGRESS_SOURCE_FLTR = 10,
++      PPE_SC_BYPASS_EGRESS_POLICER = 11,
++      PPE_SC_BYPASS_EGRESS_L2_PKT_EDIT = 12,
++      PPE_SC_BYPASS_EGRESS_L3_PKT_EDIT = 13,
++      PPE_SC_BYPASS_EGRESS_ACL_POST_ROUTING_CHECK = 14,
++      PPE_SC_BYPASS_EGRESS_PORT_ISOLATION = 15,
++      PPE_SC_BYPASS_EGRESS_PRE_ACL_QOS = 16,
++      PPE_SC_BYPASS_EGRESS_POST_ACL_QOS = 17,
++      PPE_SC_BYPASS_EGRESS_DSCP_QOS = 18,
++      PPE_SC_BYPASS_EGRESS_PCP_QOS = 19,
++      PPE_SC_BYPASS_EGRESS_PREHEADER_QOS = 20,
++      PPE_SC_BYPASS_EGRESS_FAKE_MAC_DROP = 21,
++      PPE_SC_BYPASS_EGRESS_TUNL_CONTEXT = 22,
++      PPE_SC_BYPASS_EGRESS_FLOW_POLICER = 23,
++      /* This must be last as it determines the size of the BITMAP. */
++      PPE_SC_BYPASS_EGRESS_SIZE,
++};
++
++/* Hardware bitmaps for bypassing counter of packet. */
++enum ppe_sc_counter_type {
++      PPE_SC_BYPASS_COUNTER_RX_VLAN = 0,
++      PPE_SC_BYPASS_COUNTER_RX = 1,
++      PPE_SC_BYPASS_COUNTER_TX_VLAN = 2,
++      PPE_SC_BYPASS_COUNTER_TX = 3,
++      /* This must be last as it determines the size of the BITMAP. */
++      PPE_SC_BYPASS_COUNTER_SIZE,
++};
++
++/* Hardware bitmaps for bypassing features of tunnel packet. */
++enum ppe_sc_tunnel_type {
++      PPE_SC_BYPASS_TUNNEL_SERVICE_CODE = 0,
++      PPE_SC_BYPASS_TUNNEL_TUNNEL_HANDLE = 1,
++      PPE_SC_BYPASS_TUNNEL_L3_IF_CHECK = 2,
++      PPE_SC_BYPASS_TUNNEL_VLAN_CHECK = 3,
++      PPE_SC_BYPASS_TUNNEL_DMAC_CHECK = 4,
++      PPE_SC_BYPASS_TUNNEL_UDP_CSUM_0_CHECK = 5,
++      PPE_SC_BYPASS_TUNNEL_TBL_DE_ACCE_CHECK = 6,
++      PPE_SC_BYPASS_TUNNEL_PPPOE_MC_TERM_CHECK = 7,
++      PPE_SC_BYPASS_TUNNEL_TTL_EXCEED_CHECK = 8,
++      PPE_SC_BYPASS_TUNNEL_MAP_SRC_CHECK = 9,
++      PPE_SC_BYPASS_TUNNEL_MAP_DST_CHECK = 10,
++      PPE_SC_BYPASS_TUNNEL_LPM_DST_LOOKUP = 11,
++      PPE_SC_BYPASS_TUNNEL_LPM_LOOKUP = 12,
++      PPE_SC_BYPASS_TUNNEL_WRONG_PKT_FMT_L2 = 13,
++      PPE_SC_BYPASS_TUNNEL_WRONG_PKT_FMT_L3_IPV4 = 14,
++      PPE_SC_BYPASS_TUNNEL_WRONG_PKT_FMT_L3_IPV6 = 15,
++      PPE_SC_BYPASS_TUNNEL_WRONG_PKT_FMT_L4 = 16,
++      PPE_SC_BYPASS_TUNNEL_WRONG_PKT_FMT_TUNNEL = 17,
++      /* Values 18-19 are not specified by hardware. */
++      PPE_SC_BYPASS_TUNNEL_PRE_IPO = 20,
++      /* This must be last as it determines the size of the BITMAP. */
++      PPE_SC_BYPASS_TUNNEL_SIZE,
++};
++
++/**
++ * struct ppe_sc_bypass - PPE service bypass bitmaps
++ * @ingress: Bitmap of features that can be bypassed on the ingress packet.
++ * @egress: Bitmap of features that can be bypassed on the egress packet.
++ * @counter: Bitmap of features that can be bypassed on the counter type.
++ * @tunnel: Bitmap of features that can be bypassed on the tunnel packet.
++ */
++struct ppe_sc_bypass {
++      DECLARE_BITMAP(ingress, PPE_SC_BYPASS_INGRESS_SIZE);
++      DECLARE_BITMAP(egress, PPE_SC_BYPASS_EGRESS_SIZE);
++      DECLARE_BITMAP(counter, PPE_SC_BYPASS_COUNTER_SIZE);
++      DECLARE_BITMAP(tunnel, PPE_SC_BYPASS_TUNNEL_SIZE);
++};
++
++/**
++ * struct ppe_sc_cfg - PPE service code configuration.
++ * @dest_port_valid: Generate destination port or not.
++ * @dest_port: Destination port ID.
++ * @bitmaps: Bitmap of bypass features.
++ * @is_src: Destination port acts as source port, packet sent to CPU.
++ * @next_service_code: New service code generated.
++ * @eip_field_update_bitmap: Fields updated as actions taken for EIP.
++ * @eip_hw_service: Selected hardware functions for EIP.
++ * @eip_offset_sel: Packet offset selection, using packet's layer 4 offset
++ * or using packet's layer 3 offset for EIP.
++ *
++ * Service code is generated during the packet passing through PPE.
++ */
++struct ppe_sc_cfg {
++      bool dest_port_valid;
++      int dest_port;
++      struct ppe_sc_bypass bitmaps;
++      bool is_src;
++      int next_service_code;
++      int eip_field_update_bitmap;
++      int eip_hw_service;
++      int eip_offset_sel;
++};
++
+ int ppe_hw_config(struct ppe_device *ppe_dev);
+ int ppe_queue_scheduler_set(struct ppe_device *ppe_dev,
+                           int node_id, bool flow_level, int port,
+@@ -109,4 +252,6 @@ int ppe_queue_ucast_offset_hash_set(stru
+ int ppe_port_resource_get(struct ppe_device *ppe_dev, int port,
+                         enum ppe_resource_type type,
+                         int *res_start, int *res_end);
++int ppe_sc_config_set(struct ppe_device *ppe_dev, int sc,
++                    struct ppe_sc_cfg cfg);
+ #endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -25,10 +25,63 @@
+ #define PPE_BM_SCH_CFG_TBL_SECOND_PORT_VALID  BIT(6)
+ #define PPE_BM_SCH_CFG_TBL_SECOND_PORT                GENMASK(11, 8)
++/* PPE service code configuration for the ingress direction functions,
++ * including bypass configuration for relevant PPE switch core functions
++ * such as flow entry lookup bypass.
++ */
++#define PPE_SERVICE_TBL_ADDR                  0x15000
++#define PPE_SERVICE_TBL_ENTRIES                       256
++#define PPE_SERVICE_TBL_INC                   0x10
++#define PPE_SERVICE_W0_BYPASS_BITMAP          GENMASK(31, 0)
++#define PPE_SERVICE_W1_RX_CNT_EN              BIT(0)
++
++#define PPE_SERVICE_SET_BYPASS_BITMAP(tbl_cfg, value) \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_SERVICE_W0_BYPASS_BITMAP)
++#define PPE_SERVICE_SET_RX_CNT_EN(tbl_cfg, value)     \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_SERVICE_W1_RX_CNT_EN)
++
+ /* PPE queue counters enable/disable control. */
+ #define PPE_EG_BRIDGE_CONFIG_ADDR             0x20044
+ #define PPE_EG_BRIDGE_CONFIG_QUEUE_CNT_EN     BIT(2)
++/* PPE service code configuration on the egress direction. */
++#define PPE_EG_SERVICE_TBL_ADDR                       0x43000
++#define PPE_EG_SERVICE_TBL_ENTRIES            256
++#define PPE_EG_SERVICE_TBL_INC                        0x10
++#define PPE_EG_SERVICE_W0_UPDATE_ACTION               GENMASK(31, 0)
++#define PPE_EG_SERVICE_W1_NEXT_SERVCODE               GENMASK(7, 0)
++#define PPE_EG_SERVICE_W1_HW_SERVICE          GENMASK(13, 8)
++#define PPE_EG_SERVICE_W1_OFFSET_SEL          BIT(14)
++#define PPE_EG_SERVICE_W1_TX_CNT_EN           BIT(15)
++
++#define PPE_EG_SERVICE_SET_UPDATE_ACTION(tbl_cfg, value)      \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_EG_SERVICE_W0_UPDATE_ACTION)
++#define PPE_EG_SERVICE_SET_NEXT_SERVCODE(tbl_cfg, value)      \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_EG_SERVICE_W1_NEXT_SERVCODE)
++#define PPE_EG_SERVICE_SET_HW_SERVICE(tbl_cfg, value) \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_EG_SERVICE_W1_HW_SERVICE)
++#define PPE_EG_SERVICE_SET_OFFSET_SEL(tbl_cfg, value) \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_EG_SERVICE_W1_OFFSET_SEL)
++#define PPE_EG_SERVICE_SET_TX_CNT_EN(tbl_cfg, value)  \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_EG_SERVICE_W1_TX_CNT_EN)
++
++/* PPE service code configuration for destination port and counter. */
++#define PPE_IN_L2_SERVICE_TBL_ADDR            0x66000
++#define PPE_IN_L2_SERVICE_TBL_ENTRIES         256
++#define PPE_IN_L2_SERVICE_TBL_INC             0x10
++#define PPE_IN_L2_SERVICE_TBL_DST_PORT_ID_VALID       BIT(0)
++#define PPE_IN_L2_SERVICE_TBL_DST_PORT_ID     GENMASK(4, 1)
++#define PPE_IN_L2_SERVICE_TBL_DST_DIRECTION   BIT(5)
++#define PPE_IN_L2_SERVICE_TBL_DST_BYPASS_BITMAP       GENMASK(29, 6)
++#define PPE_IN_L2_SERVICE_TBL_RX_CNT_EN               BIT(30)
++#define PPE_IN_L2_SERVICE_TBL_TX_CNT_EN               BIT(31)
++
++/* PPE service code configuration for the tunnel packet. */
++#define PPE_TL_SERVICE_TBL_ADDR                       0x306000
++#define PPE_TL_SERVICE_TBL_ENTRIES            256
++#define PPE_TL_SERVICE_TBL_INC                        4
++#define PPE_TL_SERVICE_TBL_BYPASS_BITMAP      GENMASK(31, 0)
++
+ /* Port scheduler global config. */
+ #define PPE_PSCH_SCH_DEPTH_CFG_ADDR           0x400000
+ #define PPE_PSCH_SCH_DEPTH_CFG_INC            4
diff --git a/target/linux/qualcommbe/patches-6.18/0331-net-ethernet-qualcomm-Initialize-PPE-port-control-se.patch b/target/linux/qualcommbe/patches-6.18/0331-net-ethernet-qualcomm-Initialize-PPE-port-control-se.patch
new file mode 100644 (file)
index 0000000..f1dcb51
--- /dev/null
@@ -0,0 +1,215 @@
+From 63af46200da794acda25cf8083bde0c1576b0859 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 9 Feb 2025 22:29:43 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Initialize PPE port control settings
+
+1. Enable port specific counters in PPE.
+2. Configure the default action as drop when the packet size
+   is more than the configured MTU of physical port.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ .../net/ethernet/qualcomm/ppe/ppe_config.c    | 86 ++++++++++++++++++-
+ .../net/ethernet/qualcomm/ppe/ppe_config.h    | 15 ++++
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h  | 47 ++++++++++
+ 3 files changed, 147 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
+@@ -1153,6 +1153,44 @@ int ppe_sc_config_set(struct ppe_device
+       return regmap_write(ppe_dev->regmap, reg, val);
+ }
++/**
++ * ppe_counter_enable_set - Set PPE port counter enabled
++ * @ppe_dev: PPE device
++ * @port: PPE port ID
++ *
++ * Enable PPE counters on the given port for the unicast packet, multicast
++ * packet and VLAN packet received and transmitted by PPE.
++ *
++ * Return: 0 on success, negative error code on failure.
++ */
++int ppe_counter_enable_set(struct ppe_device *ppe_dev, int port)
++{
++      u32 reg, mru_mtu_val[3];
++      int ret;
++
++      reg = PPE_MRU_MTU_CTRL_TBL_ADDR + PPE_MRU_MTU_CTRL_TBL_INC * port;
++      ret = regmap_bulk_read(ppe_dev->regmap, reg,
++                             mru_mtu_val, ARRAY_SIZE(mru_mtu_val));
++      if (ret)
++              return ret;
++
++      PPE_MRU_MTU_CTRL_SET_RX_CNT_EN(mru_mtu_val, true);
++      PPE_MRU_MTU_CTRL_SET_TX_CNT_EN(mru_mtu_val, true);
++      ret = regmap_bulk_write(ppe_dev->regmap, reg,
++                              mru_mtu_val, ARRAY_SIZE(mru_mtu_val));
++      if (ret)
++              return ret;
++
++      reg = PPE_MC_MTU_CTRL_TBL_ADDR + PPE_MC_MTU_CTRL_TBL_INC * port;
++      ret = regmap_set_bits(ppe_dev->regmap, reg, PPE_MC_MTU_CTRL_TBL_TX_CNT_EN);
++      if (ret)
++              return ret;
++
++      reg = PPE_PORT_EG_VLAN_TBL_ADDR + PPE_PORT_EG_VLAN_TBL_INC * port;
++
++      return regmap_set_bits(ppe_dev->regmap, reg, PPE_PORT_EG_VLAN_TBL_TX_COUNTING_EN);
++}
++
+ static int ppe_config_bm_threshold(struct ppe_device *ppe_dev, int bm_port_id,
+                                  const struct ppe_bm_port_config port_cfg)
+ {
+@@ -1579,6 +1617,48 @@ static int ppe_servcode_init(struct ppe_
+       return ppe_sc_config_set(ppe_dev, PPE_EDMA_SC_BYPASS_ID, sc_cfg);
+ }
++/* Initialize PPE port configurations. */
++static int ppe_port_config_init(struct ppe_device *ppe_dev)
++{
++      u32 reg, val, mru_mtu_val[3];
++      int i, ret;
++
++      /* MTU and MRU settings are not required for CPU port 0. */
++      for (i = 1; i < ppe_dev->num_ports; i++) {
++              /* Enable Ethernet port counter */
++              ret = ppe_counter_enable_set(ppe_dev, i);
++              if (ret)
++                      return ret;
++
++              reg = PPE_MRU_MTU_CTRL_TBL_ADDR + PPE_MRU_MTU_CTRL_TBL_INC * i;
++              ret = regmap_bulk_read(ppe_dev->regmap, reg,
++                                     mru_mtu_val, ARRAY_SIZE(mru_mtu_val));
++              if (ret)
++                      return ret;
++
++              /* Drop the packet when the packet size is more than
++               * the MTU or MRU of the physical interface.
++               */
++              PPE_MRU_MTU_CTRL_SET_MRU_CMD(mru_mtu_val, PPE_ACTION_DROP);
++              PPE_MRU_MTU_CTRL_SET_MTU_CMD(mru_mtu_val, PPE_ACTION_DROP);
++              ret = regmap_bulk_write(ppe_dev->regmap, reg,
++                                      mru_mtu_val, ARRAY_SIZE(mru_mtu_val));
++              if (ret)
++                      return ret;
++
++              reg = PPE_MC_MTU_CTRL_TBL_ADDR + PPE_MC_MTU_CTRL_TBL_INC * i;
++              val = FIELD_PREP(PPE_MC_MTU_CTRL_TBL_MTU_CMD, PPE_ACTION_DROP);
++              ret = regmap_update_bits(ppe_dev->regmap, reg,
++                                       PPE_MC_MTU_CTRL_TBL_MTU_CMD,
++                                       val);
++              if (ret)
++                      return ret;
++      }
++
++      /* Enable CPU port counters. */
++      return ppe_counter_enable_set(ppe_dev, 0);
++}
++
+ int ppe_hw_config(struct ppe_device *ppe_dev)
+ {
+       int ret;
+@@ -1599,5 +1679,9 @@ int ppe_hw_config(struct ppe_device *ppe
+       if (ret)
+               return ret;
+-      return ppe_servcode_init(ppe_dev);
++      ret = ppe_servcode_init(ppe_dev);
++      if (ret)
++              return ret;
++
++      return ppe_port_config_init(ppe_dev);
+ }
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
+@@ -233,6 +233,20 @@ struct ppe_sc_cfg {
+       int eip_offset_sel;
+ };
++/**
++ * enum ppe_action_type - PPE action of the received packet.
++ * @PPE_ACTION_FORWARD: Packet forwarded per L2/L3 process.
++ * @PPE_ACTION_DROP: Packet dropped by PPE.
++ * @PPE_ACTION_COPY_TO_CPU: Packet copied to CPU port per multicast queue.
++ * @PPE_ACTION_REDIRECT_TO_CPU: Packet redirected to CPU port per unicast queue.
++ */
++enum ppe_action_type {
++      PPE_ACTION_FORWARD = 0,
++      PPE_ACTION_DROP = 1,
++      PPE_ACTION_COPY_TO_CPU = 2,
++      PPE_ACTION_REDIRECT_TO_CPU = 3,
++};
++
+ int ppe_hw_config(struct ppe_device *ppe_dev);
+ int ppe_queue_scheduler_set(struct ppe_device *ppe_dev,
+                           int node_id, bool flow_level, int port,
+@@ -254,4 +268,5 @@ int ppe_port_resource_get(struct ppe_dev
+                         int *res_start, int *res_end);
+ int ppe_sc_config_set(struct ppe_device *ppe_dev, int sc,
+                     struct ppe_sc_cfg cfg);
++int ppe_counter_enable_set(struct ppe_device *ppe_dev, int port);
+ #endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -40,6 +40,18 @@
+ #define PPE_SERVICE_SET_RX_CNT_EN(tbl_cfg, value)     \
+       u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_SERVICE_W1_RX_CNT_EN)
++/* PPE port egress VLAN configurations. */
++#define PPE_PORT_EG_VLAN_TBL_ADDR             0x20020
++#define PPE_PORT_EG_VLAN_TBL_ENTRIES          8
++#define PPE_PORT_EG_VLAN_TBL_INC              4
++#define PPE_PORT_EG_VLAN_TBL_VLAN_TYPE                BIT(0)
++#define PPE_PORT_EG_VLAN_TBL_CTAG_MODE                GENMASK(2, 1)
++#define PPE_PORT_EG_VLAN_TBL_STAG_MODE                GENMASK(4, 3)
++#define PPE_PORT_EG_VLAN_TBL_VSI_TAG_MODE_EN  BIT(5)
++#define PPE_PORT_EG_VLAN_TBL_PCP_PROP_CMD     BIT(6)
++#define PPE_PORT_EG_VLAN_TBL_DEI_PROP_CMD     BIT(7)
++#define PPE_PORT_EG_VLAN_TBL_TX_COUNTING_EN   BIT(8)
++
+ /* PPE queue counters enable/disable control. */
+ #define PPE_EG_BRIDGE_CONFIG_ADDR             0x20044
+ #define PPE_EG_BRIDGE_CONFIG_QUEUE_CNT_EN     BIT(2)
+@@ -65,6 +77,41 @@
+ #define PPE_EG_SERVICE_SET_TX_CNT_EN(tbl_cfg, value)  \
+       u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_EG_SERVICE_W1_TX_CNT_EN)
++/* PPE port control configurations for the traffic to the multicast queues. */
++#define PPE_MC_MTU_CTRL_TBL_ADDR              0x60a00
++#define PPE_MC_MTU_CTRL_TBL_ENTRIES           8
++#define PPE_MC_MTU_CTRL_TBL_INC                       4
++#define PPE_MC_MTU_CTRL_TBL_MTU                       GENMASK(13, 0)
++#define PPE_MC_MTU_CTRL_TBL_MTU_CMD           GENMASK(15, 14)
++#define PPE_MC_MTU_CTRL_TBL_TX_CNT_EN         BIT(16)
++
++/* PPE port control configurations for the traffic to the unicast queues. */
++#define PPE_MRU_MTU_CTRL_TBL_ADDR             0x65000
++#define PPE_MRU_MTU_CTRL_TBL_ENTRIES          256
++#define PPE_MRU_MTU_CTRL_TBL_INC              0x10
++#define PPE_MRU_MTU_CTRL_W0_MRU                       GENMASK(13, 0)
++#define PPE_MRU_MTU_CTRL_W0_MRU_CMD           GENMASK(15, 14)
++#define PPE_MRU_MTU_CTRL_W0_MTU                       GENMASK(29, 16)
++#define PPE_MRU_MTU_CTRL_W0_MTU_CMD           GENMASK(31, 30)
++#define PPE_MRU_MTU_CTRL_W1_RX_CNT_EN         BIT(0)
++#define PPE_MRU_MTU_CTRL_W1_TX_CNT_EN         BIT(1)
++#define PPE_MRU_MTU_CTRL_W1_SRC_PROFILE               GENMASK(3, 2)
++#define PPE_MRU_MTU_CTRL_W1_INNER_PREC_LOW    BIT(31)
++#define PPE_MRU_MTU_CTRL_W2_INNER_PREC_HIGH   GENMASK(1, 0)
++
++#define PPE_MRU_MTU_CTRL_SET_MRU(tbl_cfg, value)      \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_MRU_MTU_CTRL_W0_MRU)
++#define PPE_MRU_MTU_CTRL_SET_MRU_CMD(tbl_cfg, value)  \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_MRU_MTU_CTRL_W0_MRU_CMD)
++#define PPE_MRU_MTU_CTRL_SET_MTU(tbl_cfg, value)      \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_MRU_MTU_CTRL_W0_MTU)
++#define PPE_MRU_MTU_CTRL_SET_MTU_CMD(tbl_cfg, value)  \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_MRU_MTU_CTRL_W0_MTU_CMD)
++#define PPE_MRU_MTU_CTRL_SET_RX_CNT_EN(tbl_cfg, value)        \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_MRU_MTU_CTRL_W1_RX_CNT_EN)
++#define PPE_MRU_MTU_CTRL_SET_TX_CNT_EN(tbl_cfg, value)        \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_MRU_MTU_CTRL_W1_TX_CNT_EN)
++
+ /* PPE service code configuration for destination port and counter. */
+ #define PPE_IN_L2_SERVICE_TBL_ADDR            0x66000
+ #define PPE_IN_L2_SERVICE_TBL_ENTRIES         256
diff --git a/target/linux/qualcommbe/patches-6.18/0332-net-ethernet-qualcomm-Initialize-PPE-RSS-hash-settin.patch b/target/linux/qualcommbe/patches-6.18/0332-net-ethernet-qualcomm-Initialize-PPE-RSS-hash-settin.patch
new file mode 100644 (file)
index 0000000..fc07642
--- /dev/null
@@ -0,0 +1,344 @@
+From 796be78fffeebe77237a6464da7ebe9807d670f0 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 9 Feb 2025 22:29:44 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Initialize PPE RSS hash settings
+
+PPE RSS hash is generated during PPE receive, based on the packet
+content (3 tuples or 5 tuples) and as per the configured RSS seed.
+The hash is then used to select the queue to transmit the packet
+to the ARM CPU.
+
+This patch initializes the RSS hash settings that are used to
+generate the hash for the packet during PPE packet receive.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ .../net/ethernet/qualcomm/ppe/ppe_config.c    | 194 +++++++++++++++++-
+ .../net/ethernet/qualcomm/ppe/ppe_config.h    |  39 ++++
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h  |  40 ++++
+ 3 files changed, 272 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
+@@ -1191,6 +1191,143 @@ int ppe_counter_enable_set(struct ppe_de
+       return regmap_set_bits(ppe_dev->regmap, reg, PPE_PORT_EG_VLAN_TBL_TX_COUNTING_EN);
+ }
++static int ppe_rss_hash_ipv4_config(struct ppe_device *ppe_dev, int index,
++                                  struct ppe_rss_hash_cfg cfg)
++{
++      u32 reg, val;
++
++      switch (index) {
++      case 0:
++              val = FIELD_PREP(PPE_RSS_HASH_MIX_IPV4_VAL, cfg.hash_sip_mix[0]);
++              break;
++      case 1:
++              val = FIELD_PREP(PPE_RSS_HASH_MIX_IPV4_VAL, cfg.hash_dip_mix[0]);
++              break;
++      case 2:
++              val = FIELD_PREP(PPE_RSS_HASH_MIX_IPV4_VAL, cfg.hash_protocol_mix);
++              break;
++      case 3:
++              val = FIELD_PREP(PPE_RSS_HASH_MIX_IPV4_VAL, cfg.hash_dport_mix);
++              break;
++      case 4:
++              val = FIELD_PREP(PPE_RSS_HASH_MIX_IPV4_VAL, cfg.hash_sport_mix);
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      reg = PPE_RSS_HASH_MIX_IPV4_ADDR + index * PPE_RSS_HASH_MIX_IPV4_INC;
++
++      return regmap_write(ppe_dev->regmap, reg, val);
++}
++
++static int ppe_rss_hash_ipv6_config(struct ppe_device *ppe_dev, int index,
++                                  struct ppe_rss_hash_cfg cfg)
++{
++      u32 reg, val;
++
++      switch (index) {
++      case 0 ... 3:
++              val = FIELD_PREP(PPE_RSS_HASH_MIX_VAL, cfg.hash_sip_mix[index]);
++              break;
++      case 4 ... 7:
++              val = FIELD_PREP(PPE_RSS_HASH_MIX_VAL, cfg.hash_dip_mix[index - 4]);
++              break;
++      case 8:
++              val = FIELD_PREP(PPE_RSS_HASH_MIX_VAL, cfg.hash_protocol_mix);
++              break;
++      case 9:
++              val = FIELD_PREP(PPE_RSS_HASH_MIX_VAL, cfg.hash_dport_mix);
++              break;
++      case 10:
++              val = FIELD_PREP(PPE_RSS_HASH_MIX_VAL, cfg.hash_sport_mix);
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      reg = PPE_RSS_HASH_MIX_ADDR + index * PPE_RSS_HASH_MIX_INC;
++
++      return regmap_write(ppe_dev->regmap, reg, val);
++}
++
++/**
++ * ppe_rss_hash_config_set - Configure the PPE hash settings for the packet received.
++ * @ppe_dev: PPE device.
++ * @mode: Configure RSS hash for the packet type IPv4 and IPv6.
++ * @cfg: RSS hash configuration.
++ *
++ * PPE RSS hash settings are configured for the packet type IPv4 and IPv6.
++ *
++ * Return: 0 on success, negative error code on failure.
++ */
++int ppe_rss_hash_config_set(struct ppe_device *ppe_dev, int mode,
++                          struct ppe_rss_hash_cfg cfg)
++{
++      u32 val, reg;
++      int i, ret;
++
++      if (mode & PPE_RSS_HASH_MODE_IPV4) {
++              val = FIELD_PREP(PPE_RSS_HASH_MASK_IPV4_HASH_MASK, cfg.hash_mask);
++              val |= FIELD_PREP(PPE_RSS_HASH_MASK_IPV4_FRAGMENT, cfg.hash_fragment_mode);
++              ret = regmap_write(ppe_dev->regmap, PPE_RSS_HASH_MASK_IPV4_ADDR, val);
++              if (ret)
++                      return ret;
++
++              val = FIELD_PREP(PPE_RSS_HASH_SEED_IPV4_VAL, cfg.hash_seed);
++              ret = regmap_write(ppe_dev->regmap, PPE_RSS_HASH_SEED_IPV4_ADDR, val);
++              if (ret)
++                      return ret;
++
++              for (i = 0; i < PPE_RSS_HASH_MIX_IPV4_ENTRIES; i++) {
++                      ret = ppe_rss_hash_ipv4_config(ppe_dev, i, cfg);
++                      if (ret)
++                              return ret;
++              }
++
++              for (i = 0; i < PPE_RSS_HASH_FIN_IPV4_ENTRIES; i++) {
++                      val = FIELD_PREP(PPE_RSS_HASH_FIN_IPV4_INNER, cfg.hash_fin_inner[i]);
++                      val |= FIELD_PREP(PPE_RSS_HASH_FIN_IPV4_OUTER, cfg.hash_fin_outer[i]);
++                      reg = PPE_RSS_HASH_FIN_IPV4_ADDR + i * PPE_RSS_HASH_FIN_IPV4_INC;
++
++                      ret = regmap_write(ppe_dev->regmap, reg, val);
++                      if (ret)
++                              return ret;
++              }
++      }
++
++      if (mode & PPE_RSS_HASH_MODE_IPV6) {
++              val = FIELD_PREP(PPE_RSS_HASH_MASK_HASH_MASK, cfg.hash_mask);
++              val |= FIELD_PREP(PPE_RSS_HASH_MASK_FRAGMENT, cfg.hash_fragment_mode);
++              ret = regmap_write(ppe_dev->regmap, PPE_RSS_HASH_MASK_ADDR, val);
++              if (ret)
++                      return ret;
++
++              val = FIELD_PREP(PPE_RSS_HASH_SEED_VAL, cfg.hash_seed);
++              ret = regmap_write(ppe_dev->regmap, PPE_RSS_HASH_SEED_ADDR, val);
++              if (ret)
++                      return ret;
++
++              for (i = 0; i < PPE_RSS_HASH_MIX_ENTRIES; i++) {
++                      ret = ppe_rss_hash_ipv6_config(ppe_dev, i, cfg);
++                      if (ret)
++                              return ret;
++              }
++
++              for (i = 0; i < PPE_RSS_HASH_FIN_ENTRIES; i++) {
++                      val = FIELD_PREP(PPE_RSS_HASH_FIN_INNER, cfg.hash_fin_inner[i]);
++                      val |= FIELD_PREP(PPE_RSS_HASH_FIN_OUTER, cfg.hash_fin_outer[i]);
++                      reg = PPE_RSS_HASH_FIN_ADDR + i * PPE_RSS_HASH_FIN_INC;
++
++                      ret = regmap_write(ppe_dev->regmap, reg, val);
++                      if (ret)
++                              return ret;
++              }
++      }
++
++      return 0;
++}
++
+ static int ppe_config_bm_threshold(struct ppe_device *ppe_dev, int bm_port_id,
+                                  const struct ppe_bm_port_config port_cfg)
+ {
+@@ -1659,6 +1796,57 @@ static int ppe_port_config_init(struct p
+       return ppe_counter_enable_set(ppe_dev, 0);
+ }
++/* Initialize the PPE RSS configuration for IPv4 and IPv6 packet receive.
++ * RSS settings are to calculate the random RSS hash value generated during
++ * packet receive. This hash is then used to generate the queue offset used
++ * to determine the queue used to transmit the packet.
++ */
++static int ppe_rss_hash_init(struct ppe_device *ppe_dev)
++{
++      u16 fins[PPE_RSS_HASH_TUPLES] = { 0x205, 0x264, 0x227, 0x245, 0x201 };
++      u8 ips[PPE_RSS_HASH_IP_LENGTH] = { 0x13, 0xb, 0x13, 0xb };
++      struct ppe_rss_hash_cfg hash_cfg;
++      int i, ret;
++
++      hash_cfg.hash_seed = get_random_u32();
++      hash_cfg.hash_mask = 0xfff;
++
++      /* Use 5 tuple as RSS hash key for the first fragment of TCP, UDP
++       * and UDP-Lite packets.
++       */
++      hash_cfg.hash_fragment_mode = false;
++
++      /* The final common seed configs used to calculate the RSS has value,
++       * which is available for both IPv4 and IPv6 packet.
++       */
++      for (i = 0; i < ARRAY_SIZE(fins); i++) {
++              hash_cfg.hash_fin_inner[i] = fins[i] & 0x1f;
++              hash_cfg.hash_fin_outer[i] = fins[i] >> 5;
++      }
++
++      /* RSS seeds for IP protocol, L4 destination & source port and
++       * destination & source IP used to calculate the RSS hash value.
++       */
++      hash_cfg.hash_protocol_mix = 0x13;
++      hash_cfg.hash_dport_mix = 0xb;
++      hash_cfg.hash_sport_mix = 0x13;
++      hash_cfg.hash_dip_mix[0] = 0xb;
++      hash_cfg.hash_sip_mix[0] = 0x13;
++
++      /* Configure RSS seed configs for IPv4 packet. */
++      ret = ppe_rss_hash_config_set(ppe_dev, PPE_RSS_HASH_MODE_IPV4, hash_cfg);
++      if (ret)
++              return ret;
++
++      for (i = 0; i < ARRAY_SIZE(ips); i++) {
++              hash_cfg.hash_sip_mix[i] = ips[i];
++              hash_cfg.hash_dip_mix[i] = ips[i];
++      }
++
++      /* Configure RSS seed configs for IPv6 packet. */
++      return ppe_rss_hash_config_set(ppe_dev, PPE_RSS_HASH_MODE_IPV6, hash_cfg);
++}
++
+ int ppe_hw_config(struct ppe_device *ppe_dev)
+ {
+       int ret;
+@@ -1683,5 +1871,9 @@ int ppe_hw_config(struct ppe_device *ppe
+       if (ret)
+               return ret;
+-      return ppe_port_config_init(ppe_dev);
++      ret = ppe_port_config_init(ppe_dev);
++      if (ret)
++              return ret;
++
++      return ppe_rss_hash_init(ppe_dev);
+ }
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
+@@ -23,6 +23,12 @@
+ /* The service code is used by EDMA port to transmit packet to PPE. */
+ #define PPE_EDMA_SC_BYPASS_ID                 1
++/* The PPE RSS hash configured for IPv4 and IPv6 packet separately. */
++#define PPE_RSS_HASH_MODE_IPV4                        BIT(0)
++#define PPE_RSS_HASH_MODE_IPV6                        BIT(1)
++#define PPE_RSS_HASH_IP_LENGTH                        4
++#define PPE_RSS_HASH_TUPLES                   5
++
+ /**
+  * enum ppe_scheduler_frame_mode - PPE scheduler frame mode.
+  * @PPE_SCH_WITH_IPG_PREAMBLE_FRAME_CRC: The scheduled frame includes IPG,
+@@ -247,6 +253,37 @@ enum ppe_action_type {
+       PPE_ACTION_REDIRECT_TO_CPU = 3,
+ };
++/**
++ * struct ppe_rss_hash_cfg - PPE RSS hash configuration.
++ * @hash_mask: Mask of the generated hash value.
++ * @hash_fragment_mode: Hash generation mode for the first fragment of TCP,
++ * UDP and UDP-Lite packets, to use either 3 tuple or 5 tuple for RSS hash
++ * key computation.
++ * @hash_seed: Seed to generate RSS hash.
++ * @hash_sip_mix: Source IP selection.
++ * @hash_dip_mix: Destination IP selection.
++ * @hash_protocol_mix: Protocol selection.
++ * @hash_sport_mix: Source L4 port selection.
++ * @hash_dport_mix: Destination L4 port selection.
++ * @hash_fin_inner: RSS hash value first selection.
++ * @hash_fin_outer: RSS hash value second selection.
++ *
++ * PPE RSS hash value is generated for the packet based on the RSS hash
++ * configured.
++ */
++struct ppe_rss_hash_cfg {
++      u32 hash_mask;
++      bool hash_fragment_mode;
++      u32 hash_seed;
++      u8 hash_sip_mix[PPE_RSS_HASH_IP_LENGTH];
++      u8 hash_dip_mix[PPE_RSS_HASH_IP_LENGTH];
++      u8 hash_protocol_mix;
++      u8 hash_sport_mix;
++      u8 hash_dport_mix;
++      u8 hash_fin_inner[PPE_RSS_HASH_TUPLES];
++      u8 hash_fin_outer[PPE_RSS_HASH_TUPLES];
++};
++
+ int ppe_hw_config(struct ppe_device *ppe_dev);
+ int ppe_queue_scheduler_set(struct ppe_device *ppe_dev,
+                           int node_id, bool flow_level, int port,
+@@ -269,4 +306,6 @@ int ppe_port_resource_get(struct ppe_dev
+ int ppe_sc_config_set(struct ppe_device *ppe_dev, int sc,
+                     struct ppe_sc_cfg cfg);
+ int ppe_counter_enable_set(struct ppe_device *ppe_dev, int port);
++int ppe_rss_hash_config_set(struct ppe_device *ppe_dev, int mode,
++                          struct ppe_rss_hash_cfg hash_cfg);
+ #endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -16,6 +16,46 @@
+ #define PPE_BM_SCH_CTRL_SCH_OFFSET            GENMASK(14, 8)
+ #define PPE_BM_SCH_CTRL_SCH_EN                        BIT(31)
++/* RSS settings are to calculate the random RSS hash value generated during
++ * packet receive to ARM cores. This hash is then used to generate the queue
++ * offset used to determine the queue used to transmit the packet to ARM cores.
++ */
++#define PPE_RSS_HASH_MASK_ADDR                        0xb4318
++#define PPE_RSS_HASH_MASK_HASH_MASK           GENMASK(20, 0)
++#define PPE_RSS_HASH_MASK_FRAGMENT            BIT(28)
++
++#define PPE_RSS_HASH_SEED_ADDR                        0xb431c
++#define PPE_RSS_HASH_SEED_VAL                 GENMASK(31, 0)
++
++#define PPE_RSS_HASH_MIX_ADDR                 0xb4320
++#define PPE_RSS_HASH_MIX_ENTRIES              11
++#define PPE_RSS_HASH_MIX_INC                  4
++#define PPE_RSS_HASH_MIX_VAL                  GENMASK(4, 0)
++
++#define PPE_RSS_HASH_FIN_ADDR                 0xb4350
++#define PPE_RSS_HASH_FIN_ENTRIES              5
++#define PPE_RSS_HASH_FIN_INC                  4
++#define PPE_RSS_HASH_FIN_INNER                        GENMASK(4, 0)
++#define PPE_RSS_HASH_FIN_OUTER                        GENMASK(9, 5)
++
++#define PPE_RSS_HASH_MASK_IPV4_ADDR           0xb4380
++#define PPE_RSS_HASH_MASK_IPV4_HASH_MASK      GENMASK(20, 0)
++#define PPE_RSS_HASH_MASK_IPV4_FRAGMENT               BIT(28)
++
++#define PPE_RSS_HASH_SEED_IPV4_ADDR           0xb4384
++#define PPE_RSS_HASH_SEED_IPV4_VAL            GENMASK(31, 0)
++
++#define PPE_RSS_HASH_MIX_IPV4_ADDR            0xb4390
++#define PPE_RSS_HASH_MIX_IPV4_ENTRIES         5
++#define PPE_RSS_HASH_MIX_IPV4_INC             4
++#define PPE_RSS_HASH_MIX_IPV4_VAL             GENMASK(4, 0)
++
++#define PPE_RSS_HASH_FIN_IPV4_ADDR            0xb43b0
++#define PPE_RSS_HASH_FIN_IPV4_ENTRIES         5
++#define PPE_RSS_HASH_FIN_IPV4_INC             4
++#define PPE_RSS_HASH_FIN_IPV4_INNER           GENMASK(4, 0)
++#define PPE_RSS_HASH_FIN_IPV4_OUTER           GENMASK(9, 5)
++
+ #define PPE_BM_SCH_CFG_TBL_ADDR                       0xc000
+ #define PPE_BM_SCH_CFG_TBL_ENTRIES            128
+ #define PPE_BM_SCH_CFG_TBL_INC                        0x10
diff --git a/target/linux/qualcommbe/patches-6.18/0333-net-ethernet-qualcomm-Initialize-PPE-queue-to-Ethern.patch b/target/linux/qualcommbe/patches-6.18/0333-net-ethernet-qualcomm-Initialize-PPE-queue-to-Ethern.patch
new file mode 100644 (file)
index 0000000..e05748f
--- /dev/null
@@ -0,0 +1,122 @@
+From c4a321bc120fabc318df165a7fcdeddfcf052253 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 9 Feb 2025 22:29:45 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Initialize PPE queue to Ethernet DMA
+ ring mapping
+
+Configure the selected queues to map with an Ethernet DMA ring for the
+packet to receive on ARM cores.
+
+As default initialization, all queues assigned to CPU port 0 are mapped
+to the EDMA ring 0. This configuration is later updated during Ethernet
+DMA initialization.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ .../net/ethernet/qualcomm/ppe/ppe_config.c    | 47 ++++++++++++++++++-
+ .../net/ethernet/qualcomm/ppe/ppe_config.h    |  6 +++
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h  |  5 ++
+ 3 files changed, 57 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
+@@ -1328,6 +1328,28 @@ int ppe_rss_hash_config_set(struct ppe_d
+       return 0;
+ }
++/**
++ * ppe_ring_queue_map_set - Set the PPE queue to Ethernet DMA ring mapping
++ * @ppe_dev: PPE device
++ * @ring_id: Ethernet DMA ring ID
++ * @queue_map: Bit map of queue IDs to given Ethernet DMA ring
++ *
++ * Configure the mapping from a set of PPE queues to a given Ethernet DMA ring.
++ *
++ * Return: 0 on success, negative error code on failure.
++ */
++int ppe_ring_queue_map_set(struct ppe_device *ppe_dev, int ring_id, u32 *queue_map)
++{
++      u32 reg, queue_bitmap_val[PPE_RING_TO_QUEUE_BITMAP_WORD_CNT];
++
++      memcpy(queue_bitmap_val, queue_map, sizeof(queue_bitmap_val));
++      reg = PPE_RING_Q_MAP_TBL_ADDR + PPE_RING_Q_MAP_TBL_INC * ring_id;
++
++      return regmap_bulk_write(ppe_dev->regmap, reg,
++                               queue_bitmap_val,
++                               ARRAY_SIZE(queue_bitmap_val));
++}
++
+ static int ppe_config_bm_threshold(struct ppe_device *ppe_dev, int bm_port_id,
+                                  const struct ppe_bm_port_config port_cfg)
+ {
+@@ -1847,6 +1869,25 @@ static int ppe_rss_hash_init(struct ppe_
+       return ppe_rss_hash_config_set(ppe_dev, PPE_RSS_HASH_MODE_IPV6, hash_cfg);
+ }
++/* Initialize mapping between PPE queues assigned to CPU port 0
++ * to Ethernet DMA ring 0.
++ */
++static int ppe_queues_to_ring_init(struct ppe_device *ppe_dev)
++{
++      u32 queue_bmap[PPE_RING_TO_QUEUE_BITMAP_WORD_CNT] = {};
++      int ret, queue_id, queue_max;
++
++      ret = ppe_port_resource_get(ppe_dev, 0, PPE_RES_UCAST,
++                                  &queue_id, &queue_max);
++      if (ret)
++              return ret;
++
++      for (; queue_id <= queue_max; queue_id++)
++              queue_bmap[queue_id / 32] |= BIT_MASK(queue_id % 32);
++
++      return ppe_ring_queue_map_set(ppe_dev, 0, queue_bmap);
++}
++
+ int ppe_hw_config(struct ppe_device *ppe_dev)
+ {
+       int ret;
+@@ -1875,5 +1916,9 @@ int ppe_hw_config(struct ppe_device *ppe
+       if (ret)
+               return ret;
+-      return ppe_rss_hash_init(ppe_dev);
++      ret = ppe_rss_hash_init(ppe_dev);
++      if (ret)
++              return ret;
++
++      return ppe_queues_to_ring_init(ppe_dev);
+ }
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
+@@ -29,6 +29,9 @@
+ #define PPE_RSS_HASH_IP_LENGTH                        4
+ #define PPE_RSS_HASH_TUPLES                   5
++/* PPE supports 300 queues, each bit presents as one queue. */
++#define PPE_RING_TO_QUEUE_BITMAP_WORD_CNT     10
++
+ /**
+  * enum ppe_scheduler_frame_mode - PPE scheduler frame mode.
+  * @PPE_SCH_WITH_IPG_PREAMBLE_FRAME_CRC: The scheduled frame includes IPG,
+@@ -308,4 +311,7 @@ int ppe_sc_config_set(struct ppe_device
+ int ppe_counter_enable_set(struct ppe_device *ppe_dev, int port);
+ int ppe_rss_hash_config_set(struct ppe_device *ppe_dev, int mode,
+                           struct ppe_rss_hash_cfg hash_cfg);
++int ppe_ring_queue_map_set(struct ppe_device *ppe_dev,
++                         int ring_id,
++                         u32 *queue_map);
+ #endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -207,6 +207,11 @@
+ #define PPE_L0_COMP_CFG_TBL_SHAPER_METER_LEN  GENMASK(1, 0)
+ #define PPE_L0_COMP_CFG_TBL_NODE_METER_LEN    GENMASK(3, 2)
++/* PPE queue to Ethernet DMA ring mapping table. */
++#define PPE_RING_Q_MAP_TBL_ADDR                       0x42a000
++#define PPE_RING_Q_MAP_TBL_ENTRIES            24
++#define PPE_RING_Q_MAP_TBL_INC                        0x40
++
+ /* Table addresses for per-queue dequeue setting. */
+ #define PPE_DEQ_OPR_TBL_ADDR                  0x430000
+ #define PPE_DEQ_OPR_TBL_ENTRIES                       300
diff --git a/target/linux/qualcommbe/patches-6.18/0334-net-ethernet-qualcomm-Initialize-PPE-L2-bridge-setti.patch b/target/linux/qualcommbe/patches-6.18/0334-net-ethernet-qualcomm-Initialize-PPE-L2-bridge-setti.patch
new file mode 100644 (file)
index 0000000..28a4816
--- /dev/null
@@ -0,0 +1,193 @@
+From cf7282d1e5712953516fa1cc0ffaae405491b3ca Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Sun, 9 Feb 2025 22:29:46 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Initialize PPE L2 bridge settings
+
+Initialize the L2 bridge settings for the PPE ports to only enable
+L2 frame forwarding between CPU port and PPE Ethernet ports.
+
+The per-port L2 bridge settings are initialized as follows:
+For PPE CPU port, the PPE bridge TX is enabled and FDB learning is
+disabled. For PPE physical ports, the default L2 forwarding action
+is initialized to forward to CPU port only.
+
+L2/FDB learning and forwarding will not be enabled for PPE physical
+ports yet, since the port's VSI (Virtual Switch Instance) and VSI
+membership are not yet configured, which are required for FDB
+forwarding. The VSI and FDB forwarding will later be enabled when
+switchdev is enabled.
+
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ .../net/ethernet/qualcomm/ppe/ppe_config.c    | 80 ++++++++++++++++++-
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h  | 50 ++++++++++++
+ 2 files changed, 129 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
+@@ -1888,6 +1888,80 @@ static int ppe_queues_to_ring_init(struc
+       return ppe_ring_queue_map_set(ppe_dev, 0, queue_bmap);
+ }
++/* Initialize PPE bridge settings to only enable L2 frame receive and
++ * transmit between CPU port and PPE Ethernet ports.
++ */
++static int ppe_bridge_init(struct ppe_device *ppe_dev)
++{
++      u32 reg, mask, port_cfg[4], vsi_cfg[2];
++      int ret, i;
++
++      /* Configure the following settings for CPU port0:
++       * a.) Enable Bridge TX
++       * b.) Disable FDB new address learning
++       * c.) Disable station move address learning
++       */
++      mask = PPE_PORT_BRIDGE_TXMAC_EN;
++      mask |= PPE_PORT_BRIDGE_NEW_LRN_EN;
++      mask |= PPE_PORT_BRIDGE_STA_MOVE_LRN_EN;
++      ret = regmap_update_bits(ppe_dev->regmap,
++                               PPE_PORT_BRIDGE_CTRL_ADDR,
++                               mask,
++                               PPE_PORT_BRIDGE_TXMAC_EN);
++      if (ret)
++              return ret;
++
++      for (i = 1; i < ppe_dev->num_ports; i++) {
++              /* Enable invalid VSI forwarding for all the physical ports
++               * to CPU port0, in case no VSI is assigned to the physical
++               * port.
++               */
++              reg = PPE_L2_VP_PORT_TBL_ADDR + PPE_L2_VP_PORT_TBL_INC * i;
++              ret = regmap_bulk_read(ppe_dev->regmap, reg,
++                                     port_cfg, ARRAY_SIZE(port_cfg));
++
++              if (ret)
++                      return ret;
++
++              PPE_L2_PORT_SET_INVALID_VSI_FWD_EN(port_cfg, true);
++              PPE_L2_PORT_SET_DST_INFO(port_cfg, 0);
++
++              ret = regmap_bulk_write(ppe_dev->regmap, reg,
++                                      port_cfg, ARRAY_SIZE(port_cfg));
++              if (ret)
++                      return ret;
++      }
++
++      for (i = 0; i < PPE_VSI_TBL_ENTRIES; i++) {
++              /* Set the VSI forward membership to include only CPU port0.
++               * FDB learning and forwarding take place only after switchdev
++               * is supported later to create the VSI and join the physical
++               * ports to the VSI port member.
++               */
++              reg = PPE_VSI_TBL_ADDR + PPE_VSI_TBL_INC * i;
++              ret = regmap_bulk_read(ppe_dev->regmap, reg,
++                                     vsi_cfg, ARRAY_SIZE(vsi_cfg));
++              if (ret)
++                      return ret;
++
++              PPE_VSI_SET_MEMBER_PORT_BITMAP(vsi_cfg, BIT(0));
++              PPE_VSI_SET_UUC_BITMAP(vsi_cfg, BIT(0));
++              PPE_VSI_SET_UMC_BITMAP(vsi_cfg, BIT(0));
++              PPE_VSI_SET_BC_BITMAP(vsi_cfg, BIT(0));
++              PPE_VSI_SET_NEW_ADDR_LRN_EN(vsi_cfg, true);
++              PPE_VSI_SET_NEW_ADDR_FWD_CMD(vsi_cfg, PPE_ACTION_FORWARD);
++              PPE_VSI_SET_STATION_MOVE_LRN_EN(vsi_cfg, true);
++              PPE_VSI_SET_STATION_MOVE_FWD_CMD(vsi_cfg, PPE_ACTION_FORWARD);
++
++              ret = regmap_bulk_write(ppe_dev->regmap, reg,
++                                      vsi_cfg, ARRAY_SIZE(vsi_cfg));
++              if (ret)
++                      return ret;
++      }
++
++      return 0;
++}
++
+ int ppe_hw_config(struct ppe_device *ppe_dev)
+ {
+       int ret;
+@@ -1920,5 +1994,9 @@ int ppe_hw_config(struct ppe_device *ppe
+       if (ret)
+               return ret;
+-      return ppe_queues_to_ring_init(ppe_dev);
++      ret = ppe_queues_to_ring_init(ppe_dev);
++      if (ret)
++              return ret;
++
++      return ppe_bridge_init(ppe_dev);
+ }
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -117,6 +117,14 @@
+ #define PPE_EG_SERVICE_SET_TX_CNT_EN(tbl_cfg, value)  \
+       u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_EG_SERVICE_W1_TX_CNT_EN)
++/* PPE port bridge configuration */
++#define PPE_PORT_BRIDGE_CTRL_ADDR             0x60300
++#define PPE_PORT_BRIDGE_CTRL_ENTRIES          8
++#define PPE_PORT_BRIDGE_CTRL_INC              4
++#define PPE_PORT_BRIDGE_NEW_LRN_EN            BIT(0)
++#define PPE_PORT_BRIDGE_STA_MOVE_LRN_EN               BIT(3)
++#define PPE_PORT_BRIDGE_TXMAC_EN              BIT(16)
++
+ /* PPE port control configurations for the traffic to the multicast queues. */
+ #define PPE_MC_MTU_CTRL_TBL_ADDR              0x60a00
+ #define PPE_MC_MTU_CTRL_TBL_ENTRIES           8
+@@ -125,6 +133,36 @@
+ #define PPE_MC_MTU_CTRL_TBL_MTU_CMD           GENMASK(15, 14)
+ #define PPE_MC_MTU_CTRL_TBL_TX_CNT_EN         BIT(16)
++/* PPE VSI configurations */
++#define PPE_VSI_TBL_ADDR                      0x63800
++#define PPE_VSI_TBL_ENTRIES                   64
++#define PPE_VSI_TBL_INC                               0x10
++#define PPE_VSI_W0_MEMBER_PORT_BITMAP         GENMASK(7, 0)
++#define PPE_VSI_W0_UUC_BITMAP                 GENMASK(15, 8)
++#define PPE_VSI_W0_UMC_BITMAP                 GENMASK(23, 16)
++#define PPE_VSI_W0_BC_BITMAP                  GENMASK(31, 24)
++#define PPE_VSI_W1_NEW_ADDR_LRN_EN            BIT(0)
++#define PPE_VSI_W1_NEW_ADDR_FWD_CMD           GENMASK(2, 1)
++#define PPE_VSI_W1_STATION_MOVE_LRN_EN                BIT(3)
++#define PPE_VSI_W1_STATION_MOVE_FWD_CMD               GENMASK(5, 4)
++
++#define PPE_VSI_SET_MEMBER_PORT_BITMAP(tbl_cfg, value)                \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_VSI_W0_MEMBER_PORT_BITMAP)
++#define PPE_VSI_SET_UUC_BITMAP(tbl_cfg, value)                        \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_VSI_W0_UUC_BITMAP)
++#define PPE_VSI_SET_UMC_BITMAP(tbl_cfg, value)                        \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_VSI_W0_UMC_BITMAP)
++#define PPE_VSI_SET_BC_BITMAP(tbl_cfg, value)                 \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_VSI_W0_BC_BITMAP)
++#define PPE_VSI_SET_NEW_ADDR_LRN_EN(tbl_cfg, value)           \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_VSI_W1_NEW_ADDR_LRN_EN)
++#define PPE_VSI_SET_NEW_ADDR_FWD_CMD(tbl_cfg, value)          \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_VSI_W1_NEW_ADDR_FWD_CMD)
++#define PPE_VSI_SET_STATION_MOVE_LRN_EN(tbl_cfg, value)               \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_VSI_W1_STATION_MOVE_LRN_EN)
++#define PPE_VSI_SET_STATION_MOVE_FWD_CMD(tbl_cfg, value)      \
++      u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_VSI_W1_STATION_MOVE_FWD_CMD)
++
+ /* PPE port control configurations for the traffic to the unicast queues. */
+ #define PPE_MRU_MTU_CTRL_TBL_ADDR             0x65000
+ #define PPE_MRU_MTU_CTRL_TBL_ENTRIES          256
+@@ -163,6 +201,18 @@
+ #define PPE_IN_L2_SERVICE_TBL_RX_CNT_EN               BIT(30)
+ #define PPE_IN_L2_SERVICE_TBL_TX_CNT_EN               BIT(31)
++/* L2 Port configurations */
++#define PPE_L2_VP_PORT_TBL_ADDR                       0x98000
++#define PPE_L2_VP_PORT_TBL_ENTRIES            256
++#define PPE_L2_VP_PORT_TBL_INC                        0x10
++#define PPE_L2_VP_PORT_W0_INVALID_VSI_FWD_EN  BIT(0)
++#define PPE_L2_VP_PORT_W0_DST_INFO            GENMASK(9, 2)
++
++#define PPE_L2_PORT_SET_INVALID_VSI_FWD_EN(tbl_cfg, value)    \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_L2_VP_PORT_W0_INVALID_VSI_FWD_EN)
++#define PPE_L2_PORT_SET_DST_INFO(tbl_cfg, value)              \
++      u32p_replace_bits((u32 *)tbl_cfg, value, PPE_L2_VP_PORT_W0_DST_INFO)
++
+ /* PPE service code configuration for the tunnel packet. */
+ #define PPE_TL_SERVICE_TBL_ADDR                       0x306000
+ #define PPE_TL_SERVICE_TBL_ENTRIES            256
diff --git a/target/linux/qualcommbe/patches-6.18/0335-net-ethernet-qualcomm-Add-PPE-debugfs-support-for-PP.patch b/target/linux/qualcommbe/patches-6.18/0335-net-ethernet-qualcomm-Add-PPE-debugfs-support-for-PP.patch
new file mode 100644 (file)
index 0000000..e748bca
--- /dev/null
@@ -0,0 +1,950 @@
+From fc25088f79cccb934d69e563221068589565926f Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 9 Feb 2025 22:29:47 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Add PPE debugfs support for PPE
+ counters
+
+The PPE hardware counters maintain counters for packets handled by
+the various functional blocks of PPE. They help in tracing the packets
+passed through PPE and debugging any packet drops.
+
+The counters displayed by this debugfs file are ones that are common
+for all Ethernet ports, and they do not include the counters that are
+specific for a MAC port. Hence they cannot be displayed using ethtool.
+The per-MAC counters will be supported using "ethtool -S" along with
+the netdevice driver.
+
+The PPE hardware packet counters are made available through
+the debugfs entry "/sys/kernel/debug/ppe/packet_counters".
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/Makefile    |   2 +-
+ drivers/net/ethernet/qualcomm/ppe/ppe.c       |  11 +
+ drivers/net/ethernet/qualcomm/ppe/ppe.h       |   3 +
+ .../net/ethernet/qualcomm/ppe/ppe_debugfs.c   | 692 ++++++++++++++++++
+ .../net/ethernet/qualcomm/ppe/ppe_debugfs.h   |  16 +
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h  | 102 +++
+ 6 files changed, 825 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/ppe_debugfs.c
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/ppe_debugfs.h
+
+--- a/drivers/net/ethernet/qualcomm/ppe/Makefile
++++ b/drivers/net/ethernet/qualcomm/ppe/Makefile
+@@ -4,4 +4,4 @@
+ #
+ obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o
+-qcom-ppe-objs := ppe.o ppe_config.o
++qcom-ppe-objs := ppe.o ppe_config.o ppe_debugfs.o
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe.c
+@@ -16,6 +16,7 @@
+ #include "ppe.h"
+ #include "ppe_config.h"
++#include "ppe_debugfs.h"
+ #define PPE_PORT_MAX          8
+ #define PPE_CLK_RATE          353000000
+@@ -199,11 +200,20 @@ static int qcom_ppe_probe(struct platfor
+       if (ret)
+               return dev_err_probe(dev, ret, "PPE HW config failed\n");
++      ppe_debugfs_setup(ppe_dev);
+       platform_set_drvdata(pdev, ppe_dev);
+       return 0;
+ }
++static void qcom_ppe_remove(struct platform_device *pdev)
++{
++      struct ppe_device *ppe_dev;
++
++      ppe_dev = platform_get_drvdata(pdev);
++      ppe_debugfs_teardown(ppe_dev);
++}
++
+ static const struct of_device_id qcom_ppe_of_match[] = {
+       { .compatible = "qcom,ipq9574-ppe" },
+       {}
+@@ -216,6 +226,7 @@ static struct platform_driver qcom_ppe_d
+               .of_match_table = qcom_ppe_of_match,
+       },
+       .probe  = qcom_ppe_probe,
++      .remove = qcom_ppe_remove,
+ };
+ module_platform_driver(qcom_ppe_driver);
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe.h
+@@ -11,6 +11,7 @@
+ struct device;
+ struct regmap;
++struct dentry;
+ /**
+  * struct ppe_device - PPE device private data.
+@@ -18,6 +19,7 @@ struct regmap;
+  * @regmap: PPE register map.
+  * @clk_rate: PPE clock rate.
+  * @num_ports: Number of PPE ports.
++ * @debugfs_root: Debugfs root entry.
+  * @num_icc_paths: Number of interconnect paths.
+  * @icc_paths: Interconnect path array.
+  *
+@@ -30,6 +32,7 @@ struct ppe_device {
+       struct regmap *regmap;
+       unsigned long clk_rate;
+       unsigned int num_ports;
++      struct dentry *debugfs_root;
+       unsigned int num_icc_paths;
+       struct icc_bulk_data icc_paths[] __counted_by(num_icc_paths);
+ };
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_debugfs.c
+@@ -0,0 +1,692 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* PPE debugfs routines for display of PPE counters useful for debug. */
++
++#include <linux/bitfield.h>
++#include <linux/debugfs.h>
++#include <linux/regmap.h>
++#include <linux/seq_file.h>
++
++#include "ppe.h"
++#include "ppe_config.h"
++#include "ppe_debugfs.h"
++#include "ppe_regs.h"
++
++#define PPE_PKT_CNT_TBL_SIZE                          3
++#define PPE_DROP_PKT_CNT_TBL_SIZE                     5
++
++#define PPE_W0_PKT_CNT                                        GENMASK(31, 0)
++#define PPE_W2_DROP_PKT_CNT_LOW                               GENMASK(31, 8)
++#define PPE_W3_DROP_PKT_CNT_HIGH                      GENMASK(7, 0)
++
++#define PPE_GET_PKT_CNT(tbl_cnt)                      \
++      u32_get_bits(*((u32 *)(tbl_cnt)), PPE_W0_PKT_CNT)
++#define PPE_GET_DROP_PKT_CNT_LOW(tbl_cnt)             \
++      u32_get_bits(*((u32 *)(tbl_cnt) + 0x2), PPE_W2_DROP_PKT_CNT_LOW)
++#define PPE_GET_DROP_PKT_CNT_HIGH(tbl_cnt)            \
++      u32_get_bits(*((u32 *)(tbl_cnt) + 0x3), PPE_W3_DROP_PKT_CNT_HIGH)
++
++#define PRINT_COUNTER_PREFIX(desc, cnt_type)          \
++      seq_printf(seq, "%-16s %16s", desc, cnt_type)
++
++#define PRINT_CPU_CODE_COUNTER(cnt, code)             \
++      seq_printf(seq, "%10u(cpucode:%d)", cnt, code)
++
++#define PRINT_DROP_CODE_COUNTER(cnt, port, code)      \
++      seq_printf(seq, "%10u(port=%d),dropcode:%d", cnt, port, code)
++
++#define PRINT_SINGLE_COUNTER(tag, cnt, str, index)                    \
++do {                                                                  \
++      if (!((tag) % 4))                                                       \
++              seq_printf(seq, "\n%-16s %16s", "", "");                \
++      seq_printf(seq, "%10u(%s=%04d)", cnt, str, index);              \
++} while (0)
++
++#define PRINT_TWO_COUNTERS(tag, cnt0, cnt1, str, index)                       \
++do {                                                                  \
++      if (!((tag) % 4))                                                       \
++              seq_printf(seq, "\n%-16s %16s", "", "");                \
++      seq_printf(seq, "%10u/%u(%s=%04d)", cnt0, cnt1, str, index);    \
++} while (0)
++
++/**
++ * enum ppe_cnt_size_type - PPE counter size type
++ * @PPE_PKT_CNT_SIZE_1WORD: Counter size with single register
++ * @PPE_PKT_CNT_SIZE_3WORD: Counter size with table of 3 words
++ * @PPE_PKT_CNT_SIZE_5WORD: Counter size with table of 5 words
++ *
++ * PPE takes the different register size to record the packet counters.
++ * It uses single register, or register table with 3 words or 5 words.
++ * The counter with table size 5 words also records the drop counter.
++ * There are also some other counter types occupying sizes less than 32
++ * bits, which is not covered by this enumeration type.
++ */
++enum ppe_cnt_size_type {
++      PPE_PKT_CNT_SIZE_1WORD,
++      PPE_PKT_CNT_SIZE_3WORD,
++      PPE_PKT_CNT_SIZE_5WORD,
++};
++
++static int ppe_pkt_cnt_get(struct ppe_device *ppe_dev, u32 reg,
++                         enum ppe_cnt_size_type cnt_type,
++                         u32 *cnt, u32 *drop_cnt)
++{
++      u32 drop_pkt_cnt[PPE_DROP_PKT_CNT_TBL_SIZE];
++      u32 pkt_cnt[PPE_PKT_CNT_TBL_SIZE];
++      u32 value;
++      int ret;
++
++      switch (cnt_type) {
++      case PPE_PKT_CNT_SIZE_1WORD:
++              ret = regmap_read(ppe_dev->regmap, reg, &value);
++              if (ret)
++                      return ret;
++
++              *cnt = value;
++              break;
++      case PPE_PKT_CNT_SIZE_3WORD:
++              ret = regmap_bulk_read(ppe_dev->regmap, reg,
++                                     pkt_cnt, ARRAY_SIZE(pkt_cnt));
++              if (ret)
++                      return ret;
++
++              *cnt = PPE_GET_PKT_CNT(pkt_cnt);
++              break;
++      case PPE_PKT_CNT_SIZE_5WORD:
++              ret = regmap_bulk_read(ppe_dev->regmap, reg,
++                                     drop_pkt_cnt, ARRAY_SIZE(drop_pkt_cnt));
++              if (ret)
++                      return ret;
++
++              *cnt = PPE_GET_PKT_CNT(drop_pkt_cnt);
++
++              /* Drop counter with low 24 bits. */
++              value  = PPE_GET_DROP_PKT_CNT_LOW(drop_pkt_cnt);
++              *drop_cnt = FIELD_PREP(GENMASK(23, 0), value);
++
++              /* Drop counter with high 8 bits. */
++              value  = PPE_GET_DROP_PKT_CNT_HIGH(drop_pkt_cnt);
++              *drop_cnt |= FIELD_PREP(GENMASK(31, 24), value);
++              break;
++      }
++
++      return 0;
++}
++
++static void ppe_tbl_pkt_cnt_clear(struct ppe_device *ppe_dev, u32 reg,
++                                enum ppe_cnt_size_type cnt_type)
++{
++      u32 drop_pkt_cnt[PPE_DROP_PKT_CNT_TBL_SIZE] = {};
++      u32 pkt_cnt[PPE_PKT_CNT_TBL_SIZE] = {};
++
++      switch (cnt_type) {
++      case PPE_PKT_CNT_SIZE_1WORD:
++              regmap_write(ppe_dev->regmap, reg, 0);
++              break;
++      case PPE_PKT_CNT_SIZE_3WORD:
++              regmap_bulk_write(ppe_dev->regmap, reg,
++                                pkt_cnt, ARRAY_SIZE(pkt_cnt));
++              break;
++      case PPE_PKT_CNT_SIZE_5WORD:
++              regmap_bulk_write(ppe_dev->regmap, reg,
++                                drop_pkt_cnt, ARRAY_SIZE(drop_pkt_cnt));
++              break;
++      }
++}
++
++/* The number of packets dropped because of no buffer available, no PPE
++ * buffer assigned to these packets.
++ */
++static void ppe_port_rx_drop_counter_get(struct ppe_device *ppe_dev,
++                                       struct seq_file *seq)
++{
++      u32 reg, drop_cnt = 0;
++      int ret, i, tag = 0;
++
++      PRINT_COUNTER_PREFIX("PRX_DROP_CNT", "SILENT_DROP:");
++      for (i = 0; i < PPE_DROP_CNT_TBL_ENTRIES; i++) {
++              reg = PPE_DROP_CNT_TBL_ADDR + i * PPE_DROP_CNT_TBL_INC;
++              ret = ppe_pkt_cnt_get(ppe_dev, reg, PPE_PKT_CNT_SIZE_1WORD,
++                                    &drop_cnt, NULL);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              if (drop_cnt > 0) {
++                      tag++;
++                      PRINT_SINGLE_COUNTER(tag, drop_cnt, "port", i);
++              }
++      }
++
++      seq_putc(seq, '\n');
++}
++
++/* The number of packets dropped because hardware buffers were available
++ * only partially for the packet.
++ */
++static void ppe_port_rx_bm_drop_counter_get(struct ppe_device *ppe_dev,
++                                          struct seq_file *seq)
++{
++      u32 reg, pkt_cnt = 0;
++      int ret, i, tag = 0;
++
++      PRINT_COUNTER_PREFIX("PRX_BM_DROP_CNT", "OVERFLOW_DROP:");
++      for (i = 0; i < PPE_DROP_STAT_TBL_ENTRIES; i++) {
++              reg = PPE_DROP_STAT_TBL_ADDR + PPE_DROP_STAT_TBL_INC * i;
++
++              ret = ppe_pkt_cnt_get(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD,
++                                    &pkt_cnt, NULL);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              if (pkt_cnt > 0) {
++                      tag++;
++                      PRINT_SINGLE_COUNTER(tag, pkt_cnt, "port", i);
++              }
++      }
++
++      seq_putc(seq, '\n');
++}
++
++/* The number of currently occupied buffers, that can't be flushed. */
++static void ppe_port_rx_bm_port_counter_get(struct ppe_device *ppe_dev,
++                                          struct seq_file *seq)
++{
++      int used_cnt, react_cnt;
++      int ret, i, tag = 0;
++      u32 reg, val;
++
++      PRINT_COUNTER_PREFIX("PRX_BM_PORT_CNT", "USED/REACT:");
++      for (i = 0; i < PPE_BM_USED_CNT_TBL_ENTRIES; i++) {
++              reg = PPE_BM_USED_CNT_TBL_ADDR + i * PPE_BM_USED_CNT_TBL_INC;
++              ret = regmap_read(ppe_dev->regmap, reg, &val);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              /* The number of PPE buffers used for caching the received
++               * packets before the pause frame sent.
++               */
++              used_cnt = FIELD_GET(PPE_BM_USED_CNT_VAL, val);
++
++              reg = PPE_BM_REACT_CNT_TBL_ADDR + i * PPE_BM_REACT_CNT_TBL_INC;
++              ret = regmap_read(ppe_dev->regmap, reg, &val);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              /* The number of PPE buffers used for caching the received
++               * packets after pause frame sent out.
++               */
++              react_cnt = FIELD_GET(PPE_BM_REACT_CNT_VAL, val);
++
++              if (used_cnt > 0 || react_cnt > 0) {
++                      tag++;
++                      PRINT_TWO_COUNTERS(tag, used_cnt, react_cnt, "port", i);
++              }
++      }
++
++      seq_putc(seq, '\n');
++}
++
++/* The number of packets processed by the ingress parser module of PPE. */
++static void ppe_parse_pkt_counter_get(struct ppe_device *ppe_dev,
++                                    struct seq_file *seq)
++{
++      u32 reg, cnt = 0, tunnel_cnt = 0;
++      int i, ret, tag = 0;
++
++      PRINT_COUNTER_PREFIX("IPR_PKT_CNT", "TPRX/IPRX:");
++      for (i = 0; i < PPE_IPR_PKT_CNT_TBL_ENTRIES; i++) {
++              reg = PPE_TPR_PKT_CNT_TBL_ADDR + i * PPE_TPR_PKT_CNT_TBL_INC;
++              ret = ppe_pkt_cnt_get(ppe_dev, reg, PPE_PKT_CNT_SIZE_1WORD,
++                                    &tunnel_cnt, NULL);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              reg = PPE_IPR_PKT_CNT_TBL_ADDR + i * PPE_IPR_PKT_CNT_TBL_INC;
++              ret = ppe_pkt_cnt_get(ppe_dev, reg, PPE_PKT_CNT_SIZE_1WORD,
++                                    &cnt, NULL);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              if (tunnel_cnt > 0 || cnt > 0) {
++                      tag++;
++                      PRINT_TWO_COUNTERS(tag, tunnel_cnt, cnt, "port", i);
++              }
++      }
++
++      seq_putc(seq, '\n');
++}
++
++/* The number of packets received or dropped on the ingress direction. */
++static void ppe_port_rx_counter_get(struct ppe_device *ppe_dev,
++                                  struct seq_file *seq)
++{
++      u32 reg, pkt_cnt = 0, drop_cnt = 0;
++      int ret, i, tag = 0;
++
++      PRINT_COUNTER_PREFIX("PORT_RX_CNT", "RX/RX_DROP:");
++      for (i = 0; i < PPE_PHY_PORT_RX_CNT_TBL_ENTRIES; i++) {
++              reg = PPE_PHY_PORT_RX_CNT_TBL_ADDR + PPE_PHY_PORT_RX_CNT_TBL_INC * i;
++              ret = ppe_pkt_cnt_get(ppe_dev, reg, PPE_PKT_CNT_SIZE_5WORD,
++                                    &pkt_cnt, &drop_cnt);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              if (pkt_cnt > 0) {
++                      tag++;
++                      PRINT_TWO_COUNTERS(tag, pkt_cnt, drop_cnt, "port", i);
++              }
++      }
++
++      seq_putc(seq, '\n');
++}
++
++/* The number of packets received or dropped by the port. */
++static void ppe_vp_rx_counter_get(struct ppe_device *ppe_dev,
++                                struct seq_file *seq)
++{
++      u32 reg, pkt_cnt = 0, drop_cnt = 0;
++      int ret, i, tag = 0;
++
++      PRINT_COUNTER_PREFIX("VPORT_RX_CNT", "RX/RX_DROP:");
++      for (i = 0; i < PPE_PORT_RX_CNT_TBL_ENTRIES; i++) {
++              reg = PPE_PORT_RX_CNT_TBL_ADDR + PPE_PORT_RX_CNT_TBL_INC * i;
++              ret = ppe_pkt_cnt_get(ppe_dev, reg, PPE_PKT_CNT_SIZE_5WORD,
++                                    &pkt_cnt, &drop_cnt);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              if (pkt_cnt > 0) {
++                      tag++;
++                      PRINT_TWO_COUNTERS(tag, pkt_cnt, drop_cnt, "port", i);
++              }
++      }
++
++      seq_putc(seq, '\n');
++}
++
++/* The number of packets received or dropped by layer 2 processing. */
++static void ppe_pre_l2_counter_get(struct ppe_device *ppe_dev,
++                                 struct seq_file *seq)
++{
++      u32 reg, pkt_cnt = 0, drop_cnt = 0;
++      int ret, i, tag = 0;
++
++      PRINT_COUNTER_PREFIX("PRE_L2_CNT", "RX/RX_DROP:");
++      for (i = 0; i < PPE_PRE_L2_CNT_TBL_ENTRIES; i++) {
++              reg = PPE_PRE_L2_CNT_TBL_ADDR + PPE_PRE_L2_CNT_TBL_INC * i;
++              ret = ppe_pkt_cnt_get(ppe_dev, reg, PPE_PKT_CNT_SIZE_5WORD,
++                                    &pkt_cnt, &drop_cnt);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              if (pkt_cnt > 0) {
++                      tag++;
++                      PRINT_TWO_COUNTERS(tag, pkt_cnt, drop_cnt, "vsi", i);
++              }
++      }
++
++      seq_putc(seq, '\n');
++}
++
++/* The number of VLAN packets received by PPE. */
++static void ppe_vlan_counter_get(struct ppe_device *ppe_dev,
++                               struct seq_file *seq)
++{
++      u32 reg, pkt_cnt = 0;
++      int ret, i, tag = 0;
++
++      PRINT_COUNTER_PREFIX("VLAN_CNT", "RX:");
++      for (i = 0; i < PPE_VLAN_CNT_TBL_ENTRIES; i++) {
++              reg = PPE_VLAN_CNT_TBL_ADDR + PPE_VLAN_CNT_TBL_INC * i;
++
++              ret = ppe_pkt_cnt_get(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD,
++                                    &pkt_cnt, NULL);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              if (pkt_cnt > 0) {
++                      tag++;
++                      PRINT_SINGLE_COUNTER(tag, pkt_cnt, "vsi", i);
++              }
++      }
++
++      seq_putc(seq, '\n');
++}
++
++/* The number of packets handed to CPU by PPE. */
++static void ppe_cpu_code_counter_get(struct ppe_device *ppe_dev,
++                                   struct seq_file *seq)
++{
++      u32 reg, pkt_cnt = 0;
++      int ret, i;
++
++      PRINT_COUNTER_PREFIX("CPU_CODE_CNT", "CODE:");
++      for (i = 0; i < PPE_DROP_CPU_CNT_TBL_ENTRIES; i++) {
++              reg = PPE_DROP_CPU_CNT_TBL_ADDR + PPE_DROP_CPU_CNT_TBL_INC * i;
++
++              ret = ppe_pkt_cnt_get(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD,
++                                    &pkt_cnt, NULL);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              if (!pkt_cnt)
++                      continue;
++
++              /* There are 256 CPU codes saved in the first 256 entries
++               * of register table, and 128 drop codes for each PPE port
++               * (0-7), the total entries is 256 + 8 * 128.
++               */
++              if (i < 256)
++                      PRINT_CPU_CODE_COUNTER(pkt_cnt, i);
++              else
++                      PRINT_DROP_CODE_COUNTER(pkt_cnt, (i - 256) % 8,
++                                              (i - 256) / 8);
++              seq_putc(seq, '\n');
++              PRINT_COUNTER_PREFIX("", "");
++      }
++
++      seq_putc(seq, '\n');
++}
++
++/* The number of packets forwarded by VLAN on the egress direction. */
++static void ppe_eg_vsi_counter_get(struct ppe_device *ppe_dev,
++                                 struct seq_file *seq)
++{
++      u32 reg, pkt_cnt = 0;
++      int ret, i, tag = 0;
++
++      PRINT_COUNTER_PREFIX("EG_VSI_CNT", "TX:");
++      for (i = 0; i < PPE_EG_VSI_COUNTER_TBL_ENTRIES; i++) {
++              reg = PPE_EG_VSI_COUNTER_TBL_ADDR + PPE_EG_VSI_COUNTER_TBL_INC * i;
++
++              ret = ppe_pkt_cnt_get(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD,
++                                    &pkt_cnt, NULL);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              if (pkt_cnt > 0) {
++                      tag++;
++                      PRINT_SINGLE_COUNTER(tag, pkt_cnt, "vsi", i);
++              }
++      }
++
++      seq_putc(seq, '\n');
++}
++
++/* The number of packets trasmitted or dropped by port. */
++static void ppe_vp_tx_counter_get(struct ppe_device *ppe_dev,
++                                struct seq_file *seq)
++{
++      u32 reg, pkt_cnt = 0, drop_cnt = 0;
++      int ret, i, tag = 0;
++
++      PRINT_COUNTER_PREFIX("VPORT_TX_CNT", "TX/TX_DROP:");
++      for (i = 0; i < PPE_VPORT_TX_COUNTER_TBL_ENTRIES; i++) {
++              reg = PPE_VPORT_TX_COUNTER_TBL_ADDR + PPE_VPORT_TX_COUNTER_TBL_INC * i;
++              ret = ppe_pkt_cnt_get(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD,
++                                    &pkt_cnt, NULL);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              reg = PPE_VPORT_TX_DROP_CNT_TBL_ADDR + PPE_VPORT_TX_DROP_CNT_TBL_INC * i;
++              ret = ppe_pkt_cnt_get(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD,
++                                    &drop_cnt, NULL);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              if (pkt_cnt > 0 || drop_cnt > 0) {
++                      tag++;
++                      PRINT_TWO_COUNTERS(tag, pkt_cnt, drop_cnt, "port", i);
++              }
++      }
++
++      seq_putc(seq, '\n');
++}
++
++/* The number of packets trasmitted or dropped on the egress direction. */
++static void ppe_port_tx_counter_get(struct ppe_device *ppe_dev,
++                                  struct seq_file *seq)
++{
++      u32 reg, pkt_cnt = 0, drop_cnt = 0;
++      int ret, i, tag = 0;
++
++      PRINT_COUNTER_PREFIX("PORT_TX_CNT", "TX/TX_DROP:");
++      for (i = 0; i < PPE_PORT_TX_COUNTER_TBL_ENTRIES; i++) {
++              reg = PPE_PORT_TX_COUNTER_TBL_ADDR + PPE_PORT_TX_COUNTER_TBL_INC * i;
++              ret = ppe_pkt_cnt_get(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD,
++                                    &pkt_cnt, NULL);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              reg = PPE_PORT_TX_DROP_CNT_TBL_ADDR + PPE_PORT_TX_DROP_CNT_TBL_INC * i;
++              ret = ppe_pkt_cnt_get(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD,
++                                    &drop_cnt, NULL);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              if (pkt_cnt > 0 || drop_cnt > 0) {
++                      tag++;
++                      PRINT_TWO_COUNTERS(tag, pkt_cnt, drop_cnt, "port", i);
++              }
++      }
++
++      seq_putc(seq, '\n');
++}
++
++/* The number of packets transmitted or pending by the PPE queue. */
++static void ppe_queue_tx_counter_get(struct ppe_device *ppe_dev,
++                                   struct seq_file *seq)
++{
++      u32 reg, val, pkt_cnt = 0, pend_cnt = 0;
++      int ret, i, tag = 0;
++
++      PRINT_COUNTER_PREFIX("QUEUE_TX_CNT", "TX/PEND:");
++      for (i = 0; i < PPE_QUEUE_TX_COUNTER_TBL_ENTRIES; i++) {
++              reg = PPE_QUEUE_TX_COUNTER_TBL_ADDR + PPE_QUEUE_TX_COUNTER_TBL_INC * i;
++              ret = ppe_pkt_cnt_get(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD,
++                                    &pkt_cnt, NULL);
++              if (ret) {
++                      seq_printf(seq, "ERROR %d\n", ret);
++                      return;
++              }
++
++              if (i < PPE_AC_UNICAST_QUEUE_CFG_TBL_ENTRIES) {
++                      reg = PPE_AC_UNICAST_QUEUE_CNT_TBL_ADDR +
++                            PPE_AC_UNICAST_QUEUE_CNT_TBL_INC * i;
++                      ret = regmap_read(ppe_dev->regmap, reg, &val);
++                      if (ret) {
++                              seq_printf(seq, "ERROR %d\n", ret);
++                              return;
++                      }
++
++                      pend_cnt = FIELD_GET(PPE_AC_UNICAST_QUEUE_CNT_TBL_PEND_CNT, val);
++              } else {
++                      reg = PPE_AC_MULTICAST_QUEUE_CNT_TBL_ADDR +
++                            PPE_AC_MULTICAST_QUEUE_CNT_TBL_INC *
++                            (i - PPE_AC_UNICAST_QUEUE_CFG_TBL_ENTRIES);
++                      ret = regmap_read(ppe_dev->regmap, reg, &val);
++                      if (ret) {
++                              seq_printf(seq, "ERROR %d\n", ret);
++                              return;
++                      }
++
++                      pend_cnt = FIELD_GET(PPE_AC_MULTICAST_QUEUE_CNT_TBL_PEND_CNT, val);
++              }
++
++              if (pkt_cnt > 0 || pend_cnt > 0) {
++                      tag++;
++                      PRINT_TWO_COUNTERS(tag, pkt_cnt, pend_cnt, "queue", i);
++              }
++      }
++
++      seq_putc(seq, '\n');
++}
++
++/* Display the various packet counters of PPE. */
++static int ppe_packet_counter_show(struct seq_file *seq, void *v)
++{
++      struct ppe_device *ppe_dev = seq->private;
++
++      ppe_port_rx_drop_counter_get(ppe_dev, seq);
++      ppe_port_rx_bm_drop_counter_get(ppe_dev, seq);
++      ppe_port_rx_bm_port_counter_get(ppe_dev, seq);
++      ppe_parse_pkt_counter_get(ppe_dev, seq);
++      ppe_port_rx_counter_get(ppe_dev, seq);
++      ppe_vp_rx_counter_get(ppe_dev, seq);
++      ppe_pre_l2_counter_get(ppe_dev, seq);
++      ppe_vlan_counter_get(ppe_dev, seq);
++      ppe_cpu_code_counter_get(ppe_dev, seq);
++      ppe_eg_vsi_counter_get(ppe_dev, seq);
++      ppe_vp_tx_counter_get(ppe_dev, seq);
++      ppe_port_tx_counter_get(ppe_dev, seq);
++      ppe_queue_tx_counter_get(ppe_dev, seq);
++
++      return 0;
++}
++
++static int ppe_packet_counter_open(struct inode *inode, struct file *file)
++{
++      return single_open(file, ppe_packet_counter_show, inode->i_private);
++}
++
++static ssize_t ppe_packet_counter_clear(struct file *file,
++                                      const char __user *buf,
++                                      size_t count, loff_t *pos)
++{
++      struct ppe_device *ppe_dev = file_inode(file)->i_private;
++      u32 reg;
++      int i;
++
++      for (i = 0; i < PPE_DROP_CNT_TBL_ENTRIES; i++) {
++              reg = PPE_DROP_CNT_TBL_ADDR + i * PPE_DROP_CNT_TBL_INC;
++              ppe_tbl_pkt_cnt_clear(ppe_dev, reg, PPE_PKT_CNT_SIZE_1WORD);
++      }
++
++      for (i = 0; i < PPE_DROP_STAT_TBL_ENTRIES; i++) {
++              reg = PPE_DROP_STAT_TBL_ADDR + PPE_DROP_STAT_TBL_INC * i;
++              ppe_tbl_pkt_cnt_clear(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD);
++      }
++
++      for (i = 0; i < PPE_IPR_PKT_CNT_TBL_ENTRIES; i++) {
++              reg = PPE_IPR_PKT_CNT_TBL_ADDR + i * PPE_IPR_PKT_CNT_TBL_INC;
++              ppe_tbl_pkt_cnt_clear(ppe_dev, reg, PPE_PKT_CNT_SIZE_1WORD);
++
++              reg = PPE_TPR_PKT_CNT_TBL_ADDR + i * PPE_TPR_PKT_CNT_TBL_INC;
++              ppe_tbl_pkt_cnt_clear(ppe_dev, reg, PPE_PKT_CNT_SIZE_1WORD);
++      }
++
++      for (i = 0; i < PPE_VLAN_CNT_TBL_ENTRIES; i++) {
++              reg = PPE_VLAN_CNT_TBL_ADDR + PPE_VLAN_CNT_TBL_INC * i;
++              ppe_tbl_pkt_cnt_clear(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD);
++      }
++
++      for (i = 0; i < PPE_PRE_L2_CNT_TBL_ENTRIES; i++) {
++              reg = PPE_PRE_L2_CNT_TBL_ADDR + PPE_PRE_L2_CNT_TBL_INC * i;
++              ppe_tbl_pkt_cnt_clear(ppe_dev, reg, PPE_PKT_CNT_SIZE_5WORD);
++      }
++
++      for (i = 0; i < PPE_PORT_TX_COUNTER_TBL_ENTRIES; i++) {
++              reg = PPE_PORT_TX_DROP_CNT_TBL_ADDR + PPE_PORT_TX_DROP_CNT_TBL_INC * i;
++              ppe_tbl_pkt_cnt_clear(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD);
++
++              reg = PPE_PORT_TX_COUNTER_TBL_ADDR + PPE_PORT_TX_COUNTER_TBL_INC * i;
++              ppe_tbl_pkt_cnt_clear(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD);
++      }
++
++      for (i = 0; i < PPE_EG_VSI_COUNTER_TBL_ENTRIES; i++) {
++              reg = PPE_EG_VSI_COUNTER_TBL_ADDR + PPE_EG_VSI_COUNTER_TBL_INC * i;
++              ppe_tbl_pkt_cnt_clear(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD);
++      }
++
++      for (i = 0; i < PPE_VPORT_TX_COUNTER_TBL_ENTRIES; i++) {
++              reg = PPE_VPORT_TX_COUNTER_TBL_ADDR + PPE_VPORT_TX_COUNTER_TBL_INC * i;
++              ppe_tbl_pkt_cnt_clear(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD);
++
++              reg = PPE_VPORT_TX_DROP_CNT_TBL_ADDR + PPE_VPORT_TX_DROP_CNT_TBL_INC * i;
++              ppe_tbl_pkt_cnt_clear(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD);
++      }
++
++      for (i = 0; i < PPE_QUEUE_TX_COUNTER_TBL_ENTRIES; i++) {
++              reg = PPE_QUEUE_TX_COUNTER_TBL_ADDR + PPE_QUEUE_TX_COUNTER_TBL_INC * i;
++              ppe_tbl_pkt_cnt_clear(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD);
++      }
++
++      ppe_tbl_pkt_cnt_clear(ppe_dev, PPE_EPE_DBG_IN_CNT_ADDR, PPE_PKT_CNT_SIZE_1WORD);
++      ppe_tbl_pkt_cnt_clear(ppe_dev, PPE_EPE_DBG_OUT_CNT_ADDR, PPE_PKT_CNT_SIZE_1WORD);
++
++      for (i = 0; i < PPE_DROP_CPU_CNT_TBL_ENTRIES; i++) {
++              reg = PPE_DROP_CPU_CNT_TBL_ADDR + PPE_DROP_CPU_CNT_TBL_INC * i;
++              ppe_tbl_pkt_cnt_clear(ppe_dev, reg, PPE_PKT_CNT_SIZE_3WORD);
++      }
++
++      for (i = 0; i < PPE_PORT_RX_CNT_TBL_ENTRIES; i++) {
++              reg = PPE_PORT_RX_CNT_TBL_ADDR + PPE_PORT_RX_CNT_TBL_INC * i;
++              ppe_tbl_pkt_cnt_clear(ppe_dev, reg, PPE_PKT_CNT_SIZE_5WORD);
++      }
++
++      for (i = 0; i < PPE_PHY_PORT_RX_CNT_TBL_ENTRIES; i++) {
++              reg = PPE_PHY_PORT_RX_CNT_TBL_ADDR + PPE_PHY_PORT_RX_CNT_TBL_INC * i;
++              ppe_tbl_pkt_cnt_clear(ppe_dev, reg, PPE_PKT_CNT_SIZE_5WORD);
++      }
++
++      return count;
++}
++
++static const struct file_operations ppe_debugfs_packet_counter_fops = {
++      .owner   = THIS_MODULE,
++      .open    = ppe_packet_counter_open,
++      .read    = seq_read,
++      .llseek  = seq_lseek,
++      .release = single_release,
++      .write   = ppe_packet_counter_clear,
++};
++
++void ppe_debugfs_setup(struct ppe_device *ppe_dev)
++{
++      ppe_dev->debugfs_root = debugfs_create_dir("ppe", NULL);
++      debugfs_create_file("packet_counters", 0444,
++                          ppe_dev->debugfs_root,
++                          ppe_dev,
++                          &ppe_debugfs_packet_counter_fops);
++}
++
++void ppe_debugfs_teardown(struct ppe_device *ppe_dev)
++{
++      debugfs_remove_recursive(ppe_dev->debugfs_root);
++      ppe_dev->debugfs_root = NULL;
++}
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_debugfs.h
+@@ -0,0 +1,16 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ *
++ * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* PPE debugfs counters setup. */
++
++#ifndef __PPE_DEBUGFS_H__
++#define __PPE_DEBUGFS_H__
++
++#include "ppe.h"
++
++void ppe_debugfs_setup(struct ppe_device *ppe_dev);
++void ppe_debugfs_teardown(struct ppe_device *ppe_dev);
++
++#endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -16,6 +16,39 @@
+ #define PPE_BM_SCH_CTRL_SCH_OFFSET            GENMASK(14, 8)
+ #define PPE_BM_SCH_CTRL_SCH_EN                        BIT(31)
++/* PPE drop counters. */
++#define PPE_DROP_CNT_TBL_ADDR                 0xb024
++#define PPE_DROP_CNT_TBL_ENTRIES              8
++#define PPE_DROP_CNT_TBL_INC                  4
++
++/* BM port drop counters. */
++#define PPE_DROP_STAT_TBL_ADDR                        0xe000
++#define PPE_DROP_STAT_TBL_ENTRIES             30
++#define PPE_DROP_STAT_TBL_INC                 0x10
++
++#define PPE_EPE_DBG_IN_CNT_ADDR                       0x26054
++#define PPE_EPE_DBG_OUT_CNT_ADDR              0x26070
++
++/* Egress VLAN counters. */
++#define PPE_EG_VSI_COUNTER_TBL_ADDR           0x41000
++#define PPE_EG_VSI_COUNTER_TBL_ENTRIES                64
++#define PPE_EG_VSI_COUNTER_TBL_INC            0x10
++
++/* Port TX counters. */
++#define PPE_PORT_TX_COUNTER_TBL_ADDR          0x45000
++#define PPE_PORT_TX_COUNTER_TBL_ENTRIES               8
++#define PPE_PORT_TX_COUNTER_TBL_INC           0x10
++
++/* Virtual port TX counters. */
++#define PPE_VPORT_TX_COUNTER_TBL_ADDR         0x47000
++#define PPE_VPORT_TX_COUNTER_TBL_ENTRIES      256
++#define PPE_VPORT_TX_COUNTER_TBL_INC          0x10
++
++/* Queue counters. */
++#define PPE_QUEUE_TX_COUNTER_TBL_ADDR         0x4a000
++#define PPE_QUEUE_TX_COUNTER_TBL_ENTRIES      300
++#define PPE_QUEUE_TX_COUNTER_TBL_INC          0x10
++
+ /* RSS settings are to calculate the random RSS hash value generated during
+  * packet receive to ARM cores. This hash is then used to generate the queue
+  * offset used to determine the queue used to transmit the packet to ARM cores.
+@@ -213,6 +246,51 @@
+ #define PPE_L2_PORT_SET_DST_INFO(tbl_cfg, value)              \
+       u32p_replace_bits((u32 *)tbl_cfg, value, PPE_L2_VP_PORT_W0_DST_INFO)
++/* Port RX and RX drop counters. */
++#define PPE_PORT_RX_CNT_TBL_ADDR              0x150000
++#define PPE_PORT_RX_CNT_TBL_ENTRIES           256
++#define PPE_PORT_RX_CNT_TBL_INC                       0x20
++
++/* Physical port RX and RX drop counters. */
++#define PPE_PHY_PORT_RX_CNT_TBL_ADDR          0x156000
++#define PPE_PHY_PORT_RX_CNT_TBL_ENTRIES               8
++#define PPE_PHY_PORT_RX_CNT_TBL_INC           0x20
++
++/* Counters for the packet to CPU port. */
++#define PPE_DROP_CPU_CNT_TBL_ADDR             0x160000
++#define PPE_DROP_CPU_CNT_TBL_ENTRIES          1280
++#define PPE_DROP_CPU_CNT_TBL_INC              0x10
++
++/* VLAN counters. */
++#define PPE_VLAN_CNT_TBL_ADDR                 0x178000
++#define PPE_VLAN_CNT_TBL_ENTRIES              64
++#define PPE_VLAN_CNT_TBL_INC                  0x10
++
++/* PPE L2 counters. */
++#define PPE_PRE_L2_CNT_TBL_ADDR                       0x17c000
++#define PPE_PRE_L2_CNT_TBL_ENTRIES            64
++#define PPE_PRE_L2_CNT_TBL_INC                        0x20
++
++/* Port TX drop counters. */
++#define PPE_PORT_TX_DROP_CNT_TBL_ADDR         0x17d000
++#define PPE_PORT_TX_DROP_CNT_TBL_ENTRIES      8
++#define PPE_PORT_TX_DROP_CNT_TBL_INC          0x10
++
++/* Virtual port TX counters. */
++#define PPE_VPORT_TX_DROP_CNT_TBL_ADDR                0x17e000
++#define PPE_VPORT_TX_DROP_CNT_TBL_ENTRIES     256
++#define PPE_VPORT_TX_DROP_CNT_TBL_INC         0x10
++
++/* Counters for the tunnel packet. */
++#define PPE_TPR_PKT_CNT_TBL_ADDR              0x1d0080
++#define PPE_TPR_PKT_CNT_TBL_ENTRIES           8
++#define PPE_TPR_PKT_CNT_TBL_INC                       4
++
++/* Counters for the all packet received. */
++#define PPE_IPR_PKT_CNT_TBL_ADDR              0x1e0080
++#define PPE_IPR_PKT_CNT_TBL_ENTRIES           8
++#define PPE_IPR_PKT_CNT_TBL_INC                       4
++
+ /* PPE service code configuration for the tunnel packet. */
+ #define PPE_TL_SERVICE_TBL_ADDR                       0x306000
+ #define PPE_TL_SERVICE_TBL_ENTRIES            256
+@@ -325,6 +403,18 @@
+ #define PPE_BM_PORT_GROUP_ID_INC              0x4
+ #define PPE_BM_PORT_GROUP_ID_SHARED_GROUP_ID  GENMASK(1, 0)
++/* Counters for PPE buffers used for packets cached. */
++#define PPE_BM_USED_CNT_TBL_ADDR              0x6001c0
++#define PPE_BM_USED_CNT_TBL_ENTRIES           15
++#define PPE_BM_USED_CNT_TBL_INC                       0x4
++#define PPE_BM_USED_CNT_VAL                   GENMASK(10, 0)
++
++/* Counters for PPE buffers used for packets received after pause frame sent. */
++#define PPE_BM_REACT_CNT_TBL_ADDR             0x600240
++#define PPE_BM_REACT_CNT_TBL_ENTRIES          15
++#define PPE_BM_REACT_CNT_TBL_INC              0x4
++#define PPE_BM_REACT_CNT_VAL                  GENMASK(8, 0)
++
+ #define PPE_BM_SHARED_GROUP_CFG_ADDR          0x600290
+ #define PPE_BM_SHARED_GROUP_CFG_ENTRIES               4
+ #define PPE_BM_SHARED_GROUP_CFG_INC           0x4
+@@ -449,6 +539,18 @@
+ #define PPE_AC_GRP_SET_BUF_LIMIT(tbl_cfg, value)      \
+       u32p_replace_bits((u32 *)(tbl_cfg) + 0x1, value, PPE_AC_GRP_W1_BUF_LIMIT)
++/* Counters for packets handled by unicast queues (0-255). */
++#define PPE_AC_UNICAST_QUEUE_CNT_TBL_ADDR     0x84e000
++#define PPE_AC_UNICAST_QUEUE_CNT_TBL_ENTRIES  256
++#define PPE_AC_UNICAST_QUEUE_CNT_TBL_INC      0x10
++#define PPE_AC_UNICAST_QUEUE_CNT_TBL_PEND_CNT GENMASK(12, 0)
++
++/* Counters for packets handled by multicast queues (256-299). */
++#define PPE_AC_MULTICAST_QUEUE_CNT_TBL_ADDR   0x852000
++#define PPE_AC_MULTICAST_QUEUE_CNT_TBL_ENTRIES        44
++#define PPE_AC_MULTICAST_QUEUE_CNT_TBL_INC    0x10
++#define PPE_AC_MULTICAST_QUEUE_CNT_TBL_PEND_CNT       GENMASK(12, 0)
++
+ /* Table addresses for per-queue enqueue setting. */
+ #define PPE_ENQ_OPR_TBL_ADDR                  0x85c000
+ #define PPE_ENQ_OPR_TBL_ENTRIES                       300
diff --git a/target/linux/qualcommbe/patches-6.18/0336-MAINTAINERS-Add-maintainer-for-Qualcomm-PPE-driver.patch b/target/linux/qualcommbe/patches-6.18/0336-MAINTAINERS-Add-maintainer-for-Qualcomm-PPE-driver.patch
new file mode 100644 (file)
index 0000000..3517ded
--- /dev/null
@@ -0,0 +1,30 @@
+From 28098c348414fa97531449d4e27ba1587e67c2d9 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Sun, 9 Feb 2025 22:29:48 +0800
+Subject: [PATCH] MAINTAINERS: Add maintainer for Qualcomm PPE driver
+
+Add maintainer entry for PPE (Packet Process Engine) driver
+supported for Qualcomm IPQ SoCs.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ MAINTAINERS | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -19133,6 +19133,14 @@ S:    Maintained
+ F:    Documentation/devicetree/bindings/mtd/qcom,nandc.yaml
+ F:    drivers/mtd/nand/raw/qcom_nandc.c
++QUALCOMM PPE DRIVER
++M:    Luo Jie <quic_luoj@quicinc.com>
++L:    netdev@vger.kernel.org
++S:    Supported
++F:    Documentation/devicetree/bindings/net/qcom,ipq9574-ppe.yaml
++F:    Documentation/networking/device_drivers/ethernet/qualcomm/ppe/ppe.rst
++F:    drivers/net/ethernet/qualcomm/ppe/
++
+ QUALCOMM QSEECOM DRIVER
+ M:    Maximilian Luz <luzmaximilian@gmail.com>
+ L:    linux-arm-msm@vger.kernel.org
diff --git a/target/linux/qualcommbe/patches-6.18/0337-net-ethernet-qualcomm-Add-PPE-scheduler-config.patch b/target/linux/qualcommbe/patches-6.18/0337-net-ethernet-qualcomm-Add-PPE-scheduler-config.patch
new file mode 100644 (file)
index 0000000..d6292f8
--- /dev/null
@@ -0,0 +1,201 @@
+From 93cf3297818ee61607f0a8d1d34e4fb7fcde3cdf Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Tue, 26 Dec 2023 20:18:09 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Add PPE scheduler config
+
+PPE scheduler config determines the priority of scheduling the
+packet. The scheduler config is used for supporting the QoS
+offload in PPE hardware.
+
+Change-Id: I4811bd133074757371775a6a69a1cc3cfaa8d0d0
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Alex G: rebase patch on top of PPE driver submission from 20250209.
+        Add the ppe_queue_priority_set() function and its
+        dependencies. They will be used in the edma support in
+        susequent changes.
+        ppe_queue_priority_set() used to be part of ppe_api.c, and
+        is hereby moved to ppe_config.c .
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ .../net/ethernet/qualcomm/ppe/ppe_config.c    | 141 ++++++++++++++++++
+ .../net/ethernet/qualcomm/ppe/ppe_config.h    |   5 +
+ 2 files changed, 146 insertions(+)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
+@@ -864,6 +864,51 @@ static int ppe_scheduler_l0_queue_map_se
+                                 val);
+ }
++/* Get the first level scheduler configuration. */
++static int ppe_scheduler_l0_queue_map_get(struct ppe_device *ppe_dev,
++                                        int node_id, int *port,
++                                        struct ppe_scheduler_cfg *scheduler_cfg)
++{
++      u32 val, reg;
++      int ret;
++
++      reg = PPE_L0_FLOW_MAP_TBL_ADDR + node_id * PPE_L0_FLOW_MAP_TBL_INC;
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret)
++              return ret;
++
++      scheduler_cfg->flow_id = FIELD_GET(PPE_L0_FLOW_MAP_TBL_FLOW_ID, val);
++      scheduler_cfg->pri = FIELD_GET(PPE_L0_FLOW_MAP_TBL_C_PRI, val);
++      scheduler_cfg->drr_node_wt = FIELD_GET(PPE_L0_FLOW_MAP_TBL_C_NODE_WT, val);
++
++      reg = PPE_L0_C_FLOW_CFG_TBL_ADDR +
++            (scheduler_cfg->flow_id * PPE_QUEUE_SCH_PRI_NUM + scheduler_cfg->pri) *
++            PPE_L0_C_FLOW_CFG_TBL_INC;
++
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret)
++              return ret;
++
++      scheduler_cfg->drr_node_id = FIELD_GET(PPE_L0_C_FLOW_CFG_TBL_NODE_ID, val);
++      scheduler_cfg->unit_is_packet = FIELD_GET(PPE_L0_C_FLOW_CFG_TBL_NODE_CREDIT_UNIT, val);
++
++      reg = PPE_L0_FLOW_PORT_MAP_TBL_ADDR + node_id * PPE_L0_FLOW_PORT_MAP_TBL_INC;
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret)
++              return ret;
++
++      *port = FIELD_GET(PPE_L0_FLOW_PORT_MAP_TBL_PORT_NUM, val);
++
++      reg = PPE_L0_COMP_CFG_TBL_ADDR + node_id * PPE_L0_COMP_CFG_TBL_INC;
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret)
++              return ret;
++
++      scheduler_cfg->frame_mode = FIELD_GET(PPE_L0_COMP_CFG_TBL_NODE_METER_LEN, val);
++
++      return 0;
++}
++
+ /* Set the PPE flow level scheduler configuration. */
+ static int ppe_scheduler_l1_queue_map_set(struct ppe_device *ppe_dev,
+                                         int node_id, int port,
+@@ -916,6 +961,50 @@ static int ppe_scheduler_l1_queue_map_se
+       return regmap_update_bits(ppe_dev->regmap, reg, PPE_L1_COMP_CFG_TBL_NODE_METER_LEN, val);
+ }
++/* Get the second level scheduler configuration. */
++static int ppe_scheduler_l1_queue_map_get(struct ppe_device *ppe_dev,
++                                        int node_id, int *port,
++                                        struct ppe_scheduler_cfg *scheduler_cfg)
++{
++      u32 val, reg;
++      int ret;
++
++      reg = PPE_L1_FLOW_MAP_TBL_ADDR + node_id * PPE_L1_FLOW_MAP_TBL_INC;
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret)
++              return ret;
++
++      scheduler_cfg->flow_id = FIELD_GET(PPE_L1_FLOW_MAP_TBL_FLOW_ID, val);
++      scheduler_cfg->pri = FIELD_GET(PPE_L1_FLOW_MAP_TBL_C_PRI, val);
++      scheduler_cfg->drr_node_wt = FIELD_GET(PPE_L1_FLOW_MAP_TBL_C_NODE_WT, val);
++
++      reg = PPE_L1_C_FLOW_CFG_TBL_ADDR +
++            (scheduler_cfg->flow_id * PPE_QUEUE_SCH_PRI_NUM + scheduler_cfg->pri) *
++            PPE_L1_C_FLOW_CFG_TBL_INC;
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret)
++              return ret;
++
++      scheduler_cfg->drr_node_id = FIELD_GET(PPE_L1_C_FLOW_CFG_TBL_NODE_ID, val);
++      scheduler_cfg->unit_is_packet = FIELD_GET(PPE_L1_C_FLOW_CFG_TBL_NODE_CREDIT_UNIT, val);
++
++      reg = PPE_L1_FLOW_PORT_MAP_TBL_ADDR + node_id * PPE_L1_FLOW_PORT_MAP_TBL_INC;
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret)
++              return ret;
++
++      *port = FIELD_GET(PPE_L1_FLOW_PORT_MAP_TBL_PORT_NUM, val);
++
++      reg = PPE_L1_COMP_CFG_TBL_ADDR + node_id * PPE_L1_COMP_CFG_TBL_INC;
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret)
++              return ret;
++
++      scheduler_cfg->frame_mode = FIELD_GET(PPE_L1_COMP_CFG_TBL_NODE_METER_LEN, val);
++
++      return 0;
++}
++
+ /**
+  * ppe_queue_scheduler_set - Configure scheduler for PPE hardware queue
+  * @ppe_dev: PPE device
+@@ -942,6 +1031,58 @@ int ppe_queue_scheduler_set(struct ppe_d
+ }
+ /**
++ * ppe_queue_scheduler_get - get QoS scheduler of PPE hardware queue
++ * @ppe_dev: PPE device
++ * @node_id: PPE node ID
++ * @flow_level: Flow level scheduler or queue level scheduler
++ * @port: PPE port ID to get scheduler config
++ * @scheduler_cfg: QoS scheduler configuration
++ *
++ * The hardware QoS function is supported by PPE, the current scheduler
++ * configuration can be acquired based on the queue ID of PPE port.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++int ppe_queue_scheduler_get(struct ppe_device *ppe_dev,
++                          int node_id, bool flow_level, int *port,
++                          struct ppe_scheduler_cfg *scheduler_cfg)
++{
++      if (flow_level)
++              return ppe_scheduler_l1_queue_map_get(ppe_dev, node_id,
++                                                    port, scheduler_cfg);
++
++      return ppe_scheduler_l0_queue_map_get(ppe_dev, node_id,
++                                            port, scheduler_cfg);
++}
++
++
++/**
++ * ppe_queue_priority_set - set scheduler priority of PPE hardware queue
++ * @ppe_dev: PPE device
++ * @node_id: PPE hardware node ID, which is either queue ID or flow ID
++ * @priority: Qos scheduler priority
++ *
++ * Configure scheduler priority of PPE hardware queque, the maximum node
++ * ID supported is PPE_QUEUE_ID_NUM added by PPE_FLOW_ID_NUM, queue ID
++ * belongs to level 0, flow ID belongs to level 1 in the packet pipeline.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++int ppe_queue_priority_set(struct ppe_device *ppe_dev,
++                         int node_id, int priority)
++{
++      struct ppe_scheduler_cfg sch_cfg;
++      int ret, port, level = 0;
++
++      ret = ppe_queue_scheduler_get(ppe_dev, node_id, level, &port, &sch_cfg);
++      if (ret)
++              return ret;
++
++      sch_cfg.pri = priority;
++      return ppe_queue_scheduler_set(ppe_dev, node_id, level, port, sch_cfg);
++}
++
++/**
+  * ppe_queue_ucast_base_set - Set PPE unicast queue base ID and profile ID
+  * @ppe_dev: PPE device
+  * @queue_dst: PPE queue destination configuration
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
+@@ -291,6 +291,11 @@ int ppe_hw_config(struct ppe_device *ppe
+ int ppe_queue_scheduler_set(struct ppe_device *ppe_dev,
+                           int node_id, bool flow_level, int port,
+                           struct ppe_scheduler_cfg scheduler_cfg);
++int ppe_queue_scheduler_get(struct ppe_device *ppe_dev,
++                          int node_id, bool flow_level, int *port,
++                          struct ppe_scheduler_cfg *scheduler_cfg);
++int ppe_queue_priority_set(struct ppe_device *ppe_dev,
++                         int queue_id, int priority);
+ int ppe_queue_ucast_base_set(struct ppe_device *ppe_dev,
+                            struct ppe_queue_ucast_dest queue_dst,
+                            int queue_base,
diff --git a/target/linux/qualcommbe/patches-6.18/0338-net-ethernet-qualcomm-Add-phylink-support-for-PPE-MA.patch b/target/linux/qualcommbe/patches-6.18/0338-net-ethernet-qualcomm-Add-phylink-support-for-PPE-MA.patch
new file mode 100644 (file)
index 0000000..b108366
--- /dev/null
@@ -0,0 +1,1040 @@
+From dbb3711ab25ea410ad5286b2f39dccd954cda225 Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Thu, 29 Feb 2024 16:59:53 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Add phylink support for PPE MAC
+ ports
+
+Add MAC initialization and phylink functions for PPE MAC ports.
+
+Change-Id: I39dcba671732392bcfa2e734473fd083989bfbec
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+---
+ drivers/net/ethernet/qualcomm/Kconfig        |   3 +
+ drivers/net/ethernet/qualcomm/ppe/Makefile   |   2 +-
+ drivers/net/ethernet/qualcomm/ppe/ppe.c      |   9 +
+ drivers/net/ethernet/qualcomm/ppe/ppe.h      |   2 +
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.c | 728 +++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.h |  76 ++
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h | 124 ++++
+ 7 files changed, 943 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/ppe_port.c
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/ppe_port.h
+
+--- a/drivers/net/ethernet/qualcomm/Kconfig
++++ b/drivers/net/ethernet/qualcomm/Kconfig
+@@ -66,6 +66,9 @@ config QCOM_PPE
+       depends on HAS_IOMEM && OF
+       depends on COMMON_CLK
+       select REGMAP_MMIO
++      select PHYLINK
++      select PCS_QCOM_IPQ_UNIPHY
++      select SFP
+       help
+         This driver supports the Qualcomm Technologies, Inc. packet
+         process engine (PPE) available with IPQ SoC. The PPE includes
+--- a/drivers/net/ethernet/qualcomm/ppe/Makefile
++++ b/drivers/net/ethernet/qualcomm/ppe/Makefile
+@@ -4,4 +4,4 @@
+ #
+ obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o
+-qcom-ppe-objs := ppe.o ppe_config.o ppe_debugfs.o
++qcom-ppe-objs := ppe.o ppe_config.o ppe_debugfs.o ppe_port.o
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe.c
+@@ -17,6 +17,7 @@
+ #include "ppe.h"
+ #include "ppe_config.h"
+ #include "ppe_debugfs.h"
++#include "ppe_port.h"
+ #define PPE_PORT_MAX          8
+ #define PPE_CLK_RATE          353000000
+@@ -200,6 +201,11 @@ static int qcom_ppe_probe(struct platfor
+       if (ret)
+               return dev_err_probe(dev, ret, "PPE HW config failed\n");
++      ret = ppe_port_mac_init(ppe_dev);
++      if (ret)
++              return dev_err_probe(dev, ret,
++                                   "PPE Port MAC initialization failed\n");
++
+       ppe_debugfs_setup(ppe_dev);
+       platform_set_drvdata(pdev, ppe_dev);
+@@ -212,6 +218,9 @@ static void qcom_ppe_remove(struct platf
+       ppe_dev = platform_get_drvdata(pdev);
+       ppe_debugfs_teardown(ppe_dev);
++      ppe_port_mac_deinit(ppe_dev);
++
++      platform_set_drvdata(pdev, NULL);
+ }
+ static const struct of_device_id qcom_ppe_of_match[] = {
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe.h
+@@ -20,6 +20,7 @@ struct dentry;
+  * @clk_rate: PPE clock rate.
+  * @num_ports: Number of PPE ports.
+  * @debugfs_root: Debugfs root entry.
++ * @ports: PPE MAC ports.
+  * @num_icc_paths: Number of interconnect paths.
+  * @icc_paths: Interconnect path array.
+  *
+@@ -33,6 +34,7 @@ struct ppe_device {
+       unsigned long clk_rate;
+       unsigned int num_ports;
+       struct dentry *debugfs_root;
++      struct ppe_ports *ports;
+       unsigned int num_icc_paths;
+       struct icc_bulk_data icc_paths[] __counted_by(num_icc_paths);
+ };
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
+@@ -0,0 +1,728 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* PPE Port MAC initialization and PPE port MAC functions. */
++
++#include <linux/clk.h>
++#include <linux/of_net.h>
++#include <linux/pcs/pcs-qcom-ipq9574.h>
++#include <linux/phylink.h>
++#include <linux/reset.h>
++#include <linux/regmap.h>
++#include <linux/rtnetlink.h>
++
++#include "ppe.h"
++#include "ppe_port.h"
++#include "ppe_regs.h"
++
++/* PPE MAC max frame size which including 4bytes FCS */
++#define PPE_PORT_MAC_MAX_FRAME_SIZE           0x3000
++
++/* PPE BM port start for PPE MAC ports */
++#define PPE_BM_PORT_MAC_START                 7
++
++/* PPE port clock and reset name */
++static const char * const ppe_port_clk_rst_name[] = {
++      [PPE_PORT_CLK_RST_MAC] = "port_mac",
++      [PPE_PORT_CLK_RST_RX] = "port_rx",
++      [PPE_PORT_CLK_RST_TX] = "port_tx",
++};
++
++/* PPE port and MAC reset */
++static int ppe_port_mac_reset(struct ppe_port *ppe_port)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int ret;
++
++      ret = reset_control_assert(ppe_port->rstcs[PPE_PORT_CLK_RST_MAC]);
++      if (ret)
++              goto error;
++
++      ret = reset_control_assert(ppe_port->rstcs[PPE_PORT_CLK_RST_RX]);
++      if (ret)
++              goto error;
++
++      ret = reset_control_assert(ppe_port->rstcs[PPE_PORT_CLK_RST_TX]);
++      if (ret)
++              goto error;
++
++      /* 150ms delay is required by hardware to reset PPE port and MAC */
++      msleep(150);
++
++      ret = reset_control_deassert(ppe_port->rstcs[PPE_PORT_CLK_RST_MAC]);
++      if (ret)
++              goto error;
++
++      ret = reset_control_deassert(ppe_port->rstcs[PPE_PORT_CLK_RST_RX]);
++      if (ret)
++              goto error;
++
++      ret = reset_control_deassert(ppe_port->rstcs[PPE_PORT_CLK_RST_TX]);
++      if (ret)
++              goto error;
++
++      return ret;
++
++error:
++      dev_err(ppe_dev->dev, "%s: port %d reset fail %d\n",
++              __func__, ppe_port->port_id, ret);
++      return ret;
++}
++
++/* PPE port MAC configuration for phylink */
++static void ppe_port_mac_config(struct phylink_config *config,
++                              unsigned int mode,
++                              const struct phylink_link_state *state)
++{
++      struct ppe_port *ppe_port = container_of(config, struct ppe_port,
++                                               phylink_config);
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int port = ppe_port->port_id;
++      enum ppe_mac_type mac_type;
++      u32 val, mask;
++      int ret;
++
++      switch (state->interface) {
++      case PHY_INTERFACE_MODE_2500BASEX:
++      case PHY_INTERFACE_MODE_USXGMII:
++      case PHY_INTERFACE_MODE_10GBASER:
++      case PHY_INTERFACE_MODE_10G_QXGMII:
++              mac_type = PPE_MAC_TYPE_XGMAC;
++              break;
++      case PHY_INTERFACE_MODE_QSGMII:
++      case PHY_INTERFACE_MODE_PSGMII:
++      case PHY_INTERFACE_MODE_SGMII:
++      case PHY_INTERFACE_MODE_1000BASEX:
++              mac_type = PPE_MAC_TYPE_GMAC;
++              break;
++      default:
++              dev_err(ppe_dev->dev, "%s: Unsupport interface %s\n",
++                      __func__, phy_modes(state->interface));
++              return;
++      }
++
++      /* Reset Port MAC for GMAC */
++      if (mac_type == PPE_MAC_TYPE_GMAC) {
++              ret = ppe_port_mac_reset(ppe_port);
++              if (ret)
++                      goto err_mac_config;
++      }
++
++      /* Port mux to select GMAC or XGMAC */
++      mask = PPE_PORT_SEL_XGMAC(port);
++      val = mac_type == PPE_MAC_TYPE_GMAC ? 0 : mask;
++      ret = regmap_update_bits(ppe_dev->regmap,
++                               PPE_PORT_MUX_CTRL_ADDR,
++                               mask, val);
++      if (ret)
++              goto err_mac_config;
++
++      ppe_port->mac_type = mac_type;
++
++      return;
++
++err_mac_config:
++      dev_err(ppe_dev->dev, "%s: port %d MAC config fail %d\n",
++              __func__, port, ret);
++}
++
++/* PPE port GMAC link up configuration */
++static int ppe_port_gmac_link_up(struct ppe_port *ppe_port, int speed,
++                               int duplex, bool tx_pause, bool rx_pause)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int ret, port = ppe_port->port_id;
++      u32 reg, val;
++
++      /* Set GMAC speed */
++      switch (speed) {
++      case SPEED_1000:
++              val = GMAC_SPEED_1000;
++              break;
++      case SPEED_100:
++              val = GMAC_SPEED_100;
++              break;
++      case SPEED_10:
++              val = GMAC_SPEED_10;
++              break;
++      default:
++              dev_err(ppe_dev->dev, "%s: Invalid GMAC speed %s\n",
++                      __func__, phy_speed_to_str(speed));
++              return -EINVAL;
++      }
++
++      reg = PPE_PORT_GMAC_ADDR(port);
++      ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_SPEED_ADDR,
++                               GMAC_SPEED_M, val);
++      if (ret)
++              return ret;
++
++      /* Set duplex, flow control and enable GMAC */
++      val = GMAC_TRXEN;
++      if (duplex == DUPLEX_FULL)
++              val |= GMAC_DUPLEX_FULL;
++      if (tx_pause)
++              val |= GMAC_TXFCEN;
++      if (rx_pause)
++              val |= GMAC_RXFCEN;
++
++      ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_ENABLE_ADDR,
++                               GMAC_ENABLE_ALL, val);
++
++      return ret;
++}
++
++/* PPE port XGMAC link up configuration */
++static int ppe_port_xgmac_link_up(struct ppe_port *ppe_port,
++                                phy_interface_t interface,
++                                int speed, int duplex,
++                                bool tx_pause, bool rx_pause)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int ret, port = ppe_port->port_id;
++      u32 reg, val;
++
++      /* Set XGMAC TX speed and enable TX */
++      switch (speed) {
++      case SPEED_10000:
++              if (interface == PHY_INTERFACE_MODE_USXGMII)
++                      val = XGMAC_SPEED_10000_USXGMII;
++              else
++                      val = XGMAC_SPEED_10000;
++              break;
++      case SPEED_5000:
++              val = XGMAC_SPEED_5000;
++              break;
++      case SPEED_2500:
++              if (interface == PHY_INTERFACE_MODE_USXGMII ||
++                  interface == PHY_INTERFACE_MODE_10G_QXGMII)
++                      val = XGMAC_SPEED_2500_USXGMII;
++              else
++                      val = XGMAC_SPEED_2500;
++              break;
++      case SPEED_1000:
++              val = XGMAC_SPEED_1000;
++              break;
++      case SPEED_100:
++              val = XGMAC_SPEED_100;
++              break;
++      case SPEED_10:
++              val = XGMAC_SPEED_10;
++              break;
++      default:
++              dev_err(ppe_dev->dev, "%s: Invalid XGMAC speed %s\n",
++                      __func__, phy_speed_to_str(speed));
++              return -EINVAL;
++      }
++
++      reg = PPE_PORT_XGMAC_ADDR(port);
++      val |= XGMAC_TXEN;
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_TX_CONFIG_ADDR,
++                               XGMAC_SPEED_M | XGMAC_TXEN, val);
++      if (ret)
++              return ret;
++
++      /* Set XGMAC TX flow control */
++      val = FIELD_PREP(XGMAC_PAUSE_TIME_M, FIELD_MAX(XGMAC_PAUSE_TIME_M));
++      val |= tx_pause ? XGMAC_TXFCEN : 0;
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_TX_FLOW_CTRL_ADDR,
++                               XGMAC_PAUSE_TIME_M | XGMAC_TXFCEN, val);
++      if (ret)
++              return ret;
++
++      /* Set XGMAC RX flow control */
++      val = rx_pause ? XGMAC_RXFCEN : 0;
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_RX_FLOW_CTRL_ADDR,
++                               XGMAC_RXFCEN, val);
++      if (ret)
++              return ret;
++
++      /* Enable XGMAC RX*/
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_RX_CONFIG_ADDR,
++                               XGMAC_RXEN, XGMAC_RXEN);
++
++      return ret;
++}
++
++/* PPE port MAC link up configuration for phylink */
++static void ppe_port_mac_link_up(struct phylink_config *config,
++                               struct phy_device *phy,
++                               unsigned int mode,
++                               phy_interface_t interface,
++                               int speed, int duplex,
++                               bool tx_pause, bool rx_pause)
++{
++      struct ppe_port *ppe_port = container_of(config, struct ppe_port,
++                                               phylink_config);
++      enum ppe_mac_type mac_type = ppe_port->mac_type;
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int ret, port = ppe_port->port_id;
++      u32 reg, val;
++
++      if (mac_type == PPE_MAC_TYPE_GMAC)
++              ret = ppe_port_gmac_link_up(ppe_port,
++                                          speed, duplex, tx_pause, rx_pause);
++      else
++              ret = ppe_port_xgmac_link_up(ppe_port, interface,
++                                           speed, duplex, tx_pause, rx_pause);
++      if (ret)
++              goto err_port_mac_link_up;
++
++      /* Set PPE port BM flow control */
++      reg = PPE_BM_PORT_FC_MODE_ADDR +
++              PPE_BM_PORT_FC_MODE_INC * (port + PPE_BM_PORT_MAC_START);
++      val = tx_pause ? PPE_BM_PORT_FC_MODE_EN : 0;
++      ret = regmap_update_bits(ppe_dev->regmap, reg,
++                               PPE_BM_PORT_FC_MODE_EN, val);
++      if (ret)
++              goto err_port_mac_link_up;
++
++      /* Enable PPE port TX */
++      reg = PPE_PORT_BRIDGE_CTRL_ADDR + PPE_PORT_BRIDGE_CTRL_INC * port;
++      ret = regmap_update_bits(ppe_dev->regmap, reg,
++                               PPE_PORT_BRIDGE_TXMAC_EN,
++                               PPE_PORT_BRIDGE_TXMAC_EN);
++      if (ret)
++              goto err_port_mac_link_up;
++
++      return;
++
++err_port_mac_link_up:
++      dev_err(ppe_dev->dev, "%s: port %d link up fail %d\n",
++              __func__, port, ret);
++}
++
++/* PPE port MAC link down configuration for phylink */
++static void ppe_port_mac_link_down(struct phylink_config *config,
++                                 unsigned int mode,
++                                 phy_interface_t interface)
++{
++      struct ppe_port *ppe_port = container_of(config, struct ppe_port,
++                                               phylink_config);
++      enum ppe_mac_type mac_type = ppe_port->mac_type;
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int ret, port = ppe_port->port_id;
++      u32 reg;
++
++      /* Disable PPE port TX */
++      reg = PPE_PORT_BRIDGE_CTRL_ADDR + PPE_PORT_BRIDGE_CTRL_INC * port;
++      ret = regmap_update_bits(ppe_dev->regmap, reg,
++                               PPE_PORT_BRIDGE_TXMAC_EN, 0);
++      if (ret)
++              goto err_port_mac_link_down;
++
++      /* Disable PPE MAC */
++      if (mac_type == PPE_MAC_TYPE_GMAC) {
++              reg = PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE_ADDR;
++              ret = regmap_update_bits(ppe_dev->regmap, reg, GMAC_TRXEN, 0);
++              if (ret)
++                      goto err_port_mac_link_down;
++      } else {
++              reg = PPE_PORT_XGMAC_ADDR(port);
++              ret = regmap_update_bits(ppe_dev->regmap,
++                                       reg + XGMAC_RX_CONFIG_ADDR,
++                                       XGMAC_RXEN, 0);
++              if (ret)
++                      goto err_port_mac_link_down;
++
++              ret = regmap_update_bits(ppe_dev->regmap,
++                                       reg + XGMAC_TX_CONFIG_ADDR,
++                                       XGMAC_TXEN, 0);
++              if (ret)
++                      goto err_port_mac_link_down;
++      }
++
++      return;
++
++err_port_mac_link_down:
++      dev_err(ppe_dev->dev, "%s: port %d link down fail %d\n",
++              __func__, port, ret);
++}
++
++/* PPE port MAC PCS selection for phylink */
++static
++struct phylink_pcs *ppe_port_mac_select_pcs(struct phylink_config *config,
++                                          phy_interface_t interface)
++{
++      struct ppe_port *ppe_port = container_of(config, struct ppe_port,
++                                               phylink_config);
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int ret, port = ppe_port->port_id;
++      u32 val;
++
++      /* PPE port5 can connects with PCS0 or PCS1. In PSGMII
++       * mode, it selects PCS0; otherwise, it selects PCS1.
++       */
++      if (port == 5) {
++              val = interface == PHY_INTERFACE_MODE_PSGMII ?
++                      0 : PPE_PORT5_SEL_PCS1;
++              ret = regmap_update_bits(ppe_dev->regmap,
++                                       PPE_PORT_MUX_CTRL_ADDR,
++                                       PPE_PORT5_SEL_PCS1, val);
++              if (ret) {
++                      dev_err(ppe_dev->dev, "%s: port5 select PCS fail %d\n",
++                              __func__, ret);
++                      return NULL;
++              }
++      }
++
++      return ppe_port->pcs;
++}
++
++static const struct phylink_mac_ops ppe_phylink_ops = {
++      .mac_config = ppe_port_mac_config,
++      .mac_link_up = ppe_port_mac_link_up,
++      .mac_link_down = ppe_port_mac_link_down,
++      .mac_select_pcs = ppe_port_mac_select_pcs,
++};
++
++/**
++ * ppe_port_phylink_setup() - Set phylink instance for the given PPE port
++ * @ppe_port: PPE port
++ * @netdev: Netdevice
++ *
++ * Description: Wrapper function to help setup phylink for the PPE port
++ * specified by @ppe_port and associated with the net device @netdev.
++ *
++ * Return: 0 upon success or a negative error upon failure.
++ */
++int ppe_port_phylink_setup(struct ppe_port *ppe_port, struct net_device *netdev)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      struct device_node *pcs_node;
++      int ret;
++
++      /* Create PCS */
++      pcs_node = of_parse_phandle(ppe_port->np, "pcs-handle", 0);
++      if (!pcs_node)
++              return -ENODEV;
++
++      ppe_port->pcs = ipq_pcs_get(pcs_node);
++      of_node_put(pcs_node);
++      if (IS_ERR(ppe_port->pcs)) {
++              dev_err(ppe_dev->dev, "%s: port %d failed to create PCS\n",
++                      __func__, ppe_port->port_id);
++              return PTR_ERR(ppe_port->pcs);
++      }
++
++      /* Port phylink capability */
++      ppe_port->phylink_config.dev = &netdev->dev;
++      ppe_port->phylink_config.type = PHYLINK_NETDEV;
++      ppe_port->phylink_config.mac_capabilities = MAC_ASYM_PAUSE |
++              MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000 |
++              MAC_2500FD | MAC_5000FD | MAC_10000FD;
++      __set_bit(PHY_INTERFACE_MODE_QSGMII,
++                ppe_port->phylink_config.supported_interfaces);
++      __set_bit(PHY_INTERFACE_MODE_PSGMII,
++                ppe_port->phylink_config.supported_interfaces);
++      __set_bit(PHY_INTERFACE_MODE_SGMII,
++                ppe_port->phylink_config.supported_interfaces);
++      __set_bit(PHY_INTERFACE_MODE_1000BASEX,
++                ppe_port->phylink_config.supported_interfaces);
++      __set_bit(PHY_INTERFACE_MODE_2500BASEX,
++                ppe_port->phylink_config.supported_interfaces);
++      __set_bit(PHY_INTERFACE_MODE_USXGMII,
++                ppe_port->phylink_config.supported_interfaces);
++      __set_bit(PHY_INTERFACE_MODE_10GBASER,
++                ppe_port->phylink_config.supported_interfaces);
++      __set_bit(PHY_INTERFACE_MODE_10G_QXGMII,
++                ppe_port->phylink_config.supported_interfaces);
++
++      /* Create phylink */
++      ppe_port->phylink = phylink_create(&ppe_port->phylink_config,
++                                         of_fwnode_handle(ppe_port->np),
++                                         ppe_port->interface,
++                                         &ppe_phylink_ops);
++      if (IS_ERR(ppe_port->phylink)) {
++              dev_err(ppe_dev->dev, "%s: port %d failed to create phylink\n",
++                      __func__, ppe_port->port_id);
++              ret = PTR_ERR(ppe_port->phylink);
++              goto err_free_pcs;
++      }
++
++      /* Connect phylink */
++      ret = phylink_of_phy_connect(ppe_port->phylink, ppe_port->np, 0);
++      if (ret) {
++              dev_err(ppe_dev->dev, "%s: port %d failed to connect phylink\n",
++                      __func__, ppe_port->port_id);
++              goto err_free_phylink;
++      }
++
++      return 0;
++
++err_free_phylink:
++      phylink_destroy(ppe_port->phylink);
++      ppe_port->phylink = NULL;
++err_free_pcs:
++      ipq_pcs_put(ppe_port->pcs);
++      ppe_port->pcs = NULL;
++      return ret;
++}
++
++/**
++ * ppe_port_phylink_destroy() - Destroy phylink instance for the given PPE port
++ * @ppe_port: PPE port
++ *
++ * Description: Wrapper function to help destroy phylink for the PPE port
++ * specified by @ppe_port.
++ */
++void ppe_port_phylink_destroy(struct ppe_port *ppe_port)
++{
++      /* Destroy phylink */
++      if (ppe_port->phylink) {
++              rtnl_lock();
++              phylink_disconnect_phy(ppe_port->phylink);
++              rtnl_unlock();
++              phylink_destroy(ppe_port->phylink);
++              ppe_port->phylink = NULL;
++      }
++
++      /* Destroy PCS */
++      if (ppe_port->pcs) {
++              ipq_pcs_put(ppe_port->pcs);
++              ppe_port->pcs = NULL;
++      }
++}
++
++/* PPE port clock initialization */
++static int ppe_port_clock_init(struct ppe_port *ppe_port)
++{
++      struct device_node *port_node = ppe_port->np;
++      struct reset_control *rstc;
++      struct clk *clk;
++      int i, j, ret;
++
++      for (i = 0; i < PPE_PORT_CLK_RST_MAX; i++) {
++              /* Get PPE port resets which will be used to reset PPE
++               * port and MAC.
++               */
++              rstc = of_reset_control_get_exclusive(port_node,
++                                                    ppe_port_clk_rst_name[i]);
++              if (IS_ERR(rstc)) {
++                      ret =  PTR_ERR(rstc);
++                      goto err_rst;
++              }
++
++              clk = of_clk_get_by_name(port_node, ppe_port_clk_rst_name[i]);
++              if (IS_ERR(clk)) {
++                      ret = PTR_ERR(clk);
++                      goto err_clk_get;
++              }
++
++              ret = clk_prepare_enable(clk);
++              if (ret)
++                      goto err_clk_en;
++
++              ppe_port->clks[i] = clk;
++              ppe_port->rstcs[i] = rstc;
++      }
++
++      return 0;
++
++err_clk_en:
++      clk_put(clk);
++err_clk_get:
++      reset_control_put(rstc);
++err_rst:
++      for (j = 0; j < i; j++) {
++              clk_disable_unprepare(ppe_port->clks[j]);
++              clk_put(ppe_port->clks[j]);
++              reset_control_put(ppe_port->rstcs[j]);
++      }
++
++      return ret;
++}
++
++/* PPE port clock deinitialization */
++static void ppe_port_clock_deinit(struct ppe_port *ppe_port)
++{
++      int i;
++
++      for (i = 0; i < PPE_PORT_CLK_RST_MAX; i++) {
++              clk_disable_unprepare(ppe_port->clks[i]);
++              clk_put(ppe_port->clks[i]);
++              reset_control_put(ppe_port->rstcs[i]);
++      }
++}
++
++/* PPE port MAC hardware init configuration */
++static int ppe_port_mac_hw_init(struct ppe_port *ppe_port)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int ret, port = ppe_port->port_id;
++      u32 reg, val;
++
++      /* GMAC RX and TX are initialized as disabled */
++      reg = PPE_PORT_GMAC_ADDR(port);
++      ret = regmap_update_bits(ppe_dev->regmap,
++                               reg + GMAC_ENABLE_ADDR, GMAC_TRXEN, 0);
++      if (ret)
++              return ret;
++
++      /* GMAC max frame size configuration */
++      val = FIELD_PREP(GMAC_JUMBO_SIZE_M, PPE_PORT_MAC_MAX_FRAME_SIZE);
++      ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_JUMBO_SIZE_ADDR,
++                               GMAC_JUMBO_SIZE_M, val);
++      if (ret)
++              return ret;
++
++      val = FIELD_PREP(GMAC_MAXFRAME_SIZE_M, PPE_PORT_MAC_MAX_FRAME_SIZE);
++      val |= FIELD_PREP(GMAC_TX_THD_M, 0x1);
++      ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_CTRL_ADDR,
++                               GMAC_CTRL_MASK, val);
++      if (ret)
++              return ret;
++
++      val = FIELD_PREP(GMAC_HIGH_IPG_M, 0xc);
++      ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_DBG_CTRL_ADDR,
++                               GMAC_HIGH_IPG_M, val);
++      if (ret)
++              return ret;
++
++      /* Enable and reset GMAC MIB counters and set as read clear
++       * mode, the GMAC MIB counters will be cleared after reading.
++       */
++      ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_MIB_CTRL_ADDR,
++                               GMAC_MIB_CTRL_MASK, GMAC_MIB_CTRL_MASK);
++      if (ret)
++              return ret;
++
++      ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_MIB_CTRL_ADDR,
++                               GMAC_MIB_RST, 0);
++      if (ret)
++              return ret;
++
++      /* XGMAC RX and TX disabled and max frame size configuration */
++      reg = PPE_PORT_XGMAC_ADDR(port);
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_TX_CONFIG_ADDR,
++                               XGMAC_TXEN | XGMAC_JD, XGMAC_JD);
++      if (ret)
++              return ret;
++
++      val = FIELD_PREP(XGMAC_GPSL_M, PPE_PORT_MAC_MAX_FRAME_SIZE);
++      val |= XGMAC_GPSLEN;
++      val |= XGMAC_CST;
++      val |= XGMAC_ACS;
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_RX_CONFIG_ADDR,
++                               XGMAC_RX_CONFIG_MASK, val);
++      if (ret)
++              return ret;
++
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_WD_TIMEOUT_ADDR,
++                               XGMAC_WD_TIMEOUT_MASK, XGMAC_WD_TIMEOUT_VAL);
++      if (ret)
++              return ret;
++
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_PKT_FILTER_ADDR,
++                               XGMAC_PKT_FILTER_MASK, XGMAC_PKT_FILTER_VAL);
++      if (ret)
++              return ret;
++
++      /* Enable and reset XGMAC MIB counters */
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_MMC_CTRL_ADDR,
++                               XGMAC_MCF | XGMAC_CNTRST, XGMAC_CNTRST);
++
++      return ret;
++}
++
++/**
++ * ppe_port_mac_init() - Initialization of PPE ports for the PPE device
++ * @ppe_dev: PPE device
++ *
++ * Description: Initialize the PPE MAC ports on the PPE device specified
++ * by @ppe_dev.
++ *
++ * Return: 0 upon success or a negative error upon failure.
++ */
++int ppe_port_mac_init(struct ppe_device *ppe_dev)
++{
++      struct device_node *ports_node, *port_node;
++      int port, num, ret, j, i = 0;
++      struct ppe_ports *ppe_ports;
++      phy_interface_t phy_mode;
++
++      ports_node = of_get_child_by_name(ppe_dev->dev->of_node,
++                                        "ethernet-ports");
++      if (!ports_node) {
++              dev_err(ppe_dev->dev, "Failed to get ports node\n");
++              return -ENODEV;
++      }
++
++      num = of_get_available_child_count(ports_node);
++
++      ppe_ports = devm_kzalloc(ppe_dev->dev,
++                               struct_size(ppe_ports, port, num),
++                               GFP_KERNEL);
++      if (!ppe_ports) {
++              ret = -ENOMEM;
++              goto err_ports_node;
++      }
++
++      ppe_dev->ports = ppe_ports;
++      ppe_ports->num = num;
++
++      for_each_available_child_of_node(ports_node, port_node) {
++              ret = of_property_read_u32(port_node, "reg", &port);
++              if (ret) {
++                      dev_err(ppe_dev->dev, "Failed to get port id\n");
++                      goto err_port_node;
++              }
++
++              ret = of_get_phy_mode(port_node, &phy_mode);
++              if (ret) {
++                      dev_err(ppe_dev->dev, "Failed to get phy mode\n");
++                      goto err_port_node;
++              }
++
++              ppe_ports->port[i].ppe_dev = ppe_dev;
++              ppe_ports->port[i].port_id = port;
++              ppe_ports->port[i].np = port_node;
++              ppe_ports->port[i].interface = phy_mode;
++
++              ret = ppe_port_clock_init(&ppe_ports->port[i]);
++              if (ret) {
++                      dev_err(ppe_dev->dev, "Failed to initialize port clocks\n");
++                      goto err_port_clk;
++              }
++
++              ret = ppe_port_mac_hw_init(&ppe_ports->port[i]);
++              if (ret) {
++                      dev_err(ppe_dev->dev, "Failed to initialize MAC hardware\n");
++                      goto err_port_node;
++              }
++
++              i++;
++      }
++
++      of_node_put(ports_node);
++      return 0;
++
++err_port_clk:
++      for (j = 0; j < i; j++)
++              ppe_port_clock_deinit(&ppe_ports->port[j]);
++err_port_node:
++      of_node_put(port_node);
++err_ports_node:
++      of_node_put(ports_node);
++      return ret;
++}
++
++/**
++ * ppe_port_mac_deinit() - Deinitialization of PPE ports for the PPE device
++ * @ppe_dev: PPE device
++ *
++ * Description: Deinitialize the PPE MAC ports on the PPE device specified
++ * by @ppe_dev.
++ */
++void ppe_port_mac_deinit(struct ppe_device *ppe_dev)
++{
++      struct ppe_port *ppe_port;
++      int i;
++
++      for (i = 0; i < ppe_dev->ports->num; i++) {
++              ppe_port = &ppe_dev->ports->port[i];
++              ppe_port_clock_deinit(ppe_port);
++      }
++}
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.h
+@@ -0,0 +1,76 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ *
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef __PPE_PORT_H__
++#define __PPE_PORT_H__
++
++#include <linux/phylink.h>
++
++/**
++ * enum ppe_port_clk_rst_type - PPE port clock and reset ID type
++ * @PPE_PORT_CLK_RST_MAC: The clock and reset ID for port MAC
++ * @PPE_PORT_CLK_RST_RX: The clock and reset ID for port receive path
++ * @PPE_PORT_CLK_RST_TX: The clock and reset for port transmit path
++ * @PPE_PORT_CLK_RST_MAX: The maximum of port clock and reset
++ */
++enum ppe_port_clk_rst_type {
++      PPE_PORT_CLK_RST_MAC,
++      PPE_PORT_CLK_RST_RX,
++      PPE_PORT_CLK_RST_TX,
++      PPE_PORT_CLK_RST_MAX,
++};
++
++/**
++ * enum ppe_mac_type - PPE MAC type
++ * @PPE_MAC_TYPE_GMAC: GMAC type
++ * @PPE_MAC_TYPE_XGMAC: XGMAC type
++ */
++enum ppe_mac_type {
++      PPE_MAC_TYPE_GMAC,
++      PPE_MAC_TYPE_XGMAC,
++};
++
++/**
++ * struct ppe_port - Private data for each PPE port
++ * @phylink: Linux phylink instance
++ * @phylink_config: Linux phylink configurations
++ * @pcs: Linux phylink PCS instance
++ * @np: Port device tree node
++ * @ppe_dev: Back pointer to PPE device private data
++ * @interface: Port interface mode
++ * @mac_type: Port MAC type, GMAC or XGMAC
++ * @port_id: Port ID
++ * @clks: Port clocks
++ * @rstcs: Port resets
++ */
++struct ppe_port {
++      struct phylink *phylink;
++      struct phylink_config phylink_config;
++      struct phylink_pcs *pcs;
++      struct device_node *np;
++      struct ppe_device *ppe_dev;
++      phy_interface_t interface;
++      enum ppe_mac_type mac_type;
++      int port_id;
++      struct clk *clks[PPE_PORT_CLK_RST_MAX];
++      struct reset_control *rstcs[PPE_PORT_CLK_RST_MAX];
++};
++
++/**
++ * struct ppe_ports - Array of PPE ports
++ * @num: Number of PPE ports
++ * @port: Each PPE port private data
++ */
++struct ppe_ports {
++      unsigned int num;
++      struct ppe_port port[] __counted_by(num);
++};
++
++int ppe_port_mac_init(struct ppe_device *ppe_dev);
++void ppe_port_mac_deinit(struct ppe_device *ppe_dev);
++int ppe_port_phylink_setup(struct ppe_port *ppe_port,
++                         struct net_device *netdev);
++void ppe_port_phylink_destroy(struct ppe_port *ppe_port);
++#endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -9,6 +9,17 @@
+ #include <linux/bitfield.h>
++/* PPE port mux select control register */
++#define PPE_PORT_MUX_CTRL_ADDR                        0x10
++#define PPE_PORT6_SEL_XGMAC                   BIT(13)
++#define PPE_PORT5_SEL_XGMAC                   BIT(12)
++#define PPE_PORT4_SEL_XGMAC                   BIT(11)
++#define PPE_PORT3_SEL_XGMAC                   BIT(10)
++#define PPE_PORT2_SEL_XGMAC                   BIT(9)
++#define PPE_PORT1_SEL_XGMAC                   BIT(8)
++#define PPE_PORT5_SEL_PCS1                    BIT(4)
++#define PPE_PORT_SEL_XGMAC(x)                 (BIT(8) << ((x) - 1))
++
+ /* PPE scheduler configurations for buffer manager block. */
+ #define PPE_BM_SCH_CTRL_ADDR                  0xb000
+ #define PPE_BM_SCH_CTRL_INC                   4
+@@ -556,4 +567,117 @@
+ #define PPE_ENQ_OPR_TBL_ENTRIES                       300
+ #define PPE_ENQ_OPR_TBL_INC                   0x10
+ #define PPE_ENQ_OPR_TBL_ENQ_DISABLE           BIT(0)
++
++/* PPE GMAC and XGMAC register base address */
++#define PPE_PORT_GMAC_ADDR(x)                 (0x001000 + ((x) - 1) * 0x200)
++#define PPE_PORT_XGMAC_ADDR(x)                        (0x500000 + ((x) - 1) * 0x4000)
++
++/* GMAC enable register */
++#define GMAC_ENABLE_ADDR                      0x0
++#define GMAC_TXFCEN                           BIT(6)
++#define GMAC_RXFCEN                           BIT(5)
++#define GMAC_DUPLEX_FULL                      BIT(4)
++#define GMAC_TXEN                             BIT(1)
++#define GMAC_RXEN                             BIT(0)
++
++#define GMAC_TRXEN                            \
++      (GMAC_TXEN | GMAC_RXEN)
++#define GMAC_ENABLE_ALL                               \
++      (GMAC_TXFCEN | GMAC_RXFCEN | GMAC_DUPLEX_FULL | GMAC_TXEN | GMAC_RXEN)
++
++/* GMAC speed register */
++#define GMAC_SPEED_ADDR                               0x4
++#define GMAC_SPEED_M                          GENMASK(1, 0)
++#define GMAC_SPEED_10                         0
++#define GMAC_SPEED_100                                1
++#define GMAC_SPEED_1000                               2
++
++/* GMAC control register */
++#define GMAC_CTRL_ADDR                                0x18
++#define GMAC_TX_THD_M                         GENMASK(27, 24)
++#define GMAC_MAXFRAME_SIZE_M                  GENMASK(21, 8)
++#define GMAC_CRS_SEL                          BIT(6)
++
++#define GMAC_CTRL_MASK                                \
++      (GMAC_TX_THD_M | GMAC_MAXFRAME_SIZE_M | GMAC_CRS_SEL)
++
++/* GMAC debug control register */
++#define GMAC_DBG_CTRL_ADDR                    0x1c
++#define GMAC_HIGH_IPG_M                               GENMASK(15, 8)
++
++/* GMAC jumbo size register */
++#define GMAC_JUMBO_SIZE_ADDR                  0x30
++#define GMAC_JUMBO_SIZE_M                     GENMASK(13, 0)
++
++/* GMAC MIB control register */
++#define GMAC_MIB_CTRL_ADDR                    0x34
++#define GMAC_MIB_RD_CLR                               BIT(2)
++#define GMAC_MIB_RST                          BIT(1)
++#define GMAC_MIB_EN                           BIT(0)
++
++#define GMAC_MIB_CTRL_MASK                    \
++      (GMAC_MIB_RD_CLR | GMAC_MIB_RST | GMAC_MIB_EN)
++
++/* XGMAC TX configuration register */
++#define XGMAC_TX_CONFIG_ADDR                  0x0
++#define XGMAC_SPEED_M                         GENMASK(31, 29)
++#define XGMAC_SPEED_10000_USXGMII             FIELD_PREP(XGMAC_SPEED_M, 4)
++#define XGMAC_SPEED_10000                     FIELD_PREP(XGMAC_SPEED_M, 0)
++#define XGMAC_SPEED_5000                      FIELD_PREP(XGMAC_SPEED_M, 5)
++#define XGMAC_SPEED_2500_USXGMII              FIELD_PREP(XGMAC_SPEED_M, 6)
++#define XGMAC_SPEED_2500                      FIELD_PREP(XGMAC_SPEED_M, 2)
++#define XGMAC_SPEED_1000                      FIELD_PREP(XGMAC_SPEED_M, 3)
++#define XGMAC_SPEED_100                               XGMAC_SPEED_1000
++#define XGMAC_SPEED_10                                XGMAC_SPEED_1000
++#define XGMAC_JD                              BIT(16)
++#define XGMAC_TXEN                            BIT(0)
++
++/* XGMAC RX configuration register */
++#define XGMAC_RX_CONFIG_ADDR                  0x4
++#define XGMAC_GPSL_M                          GENMASK(29, 16)
++#define XGMAC_WD                              BIT(7)
++#define XGMAC_GPSLEN                          BIT(6)
++#define XGMAC_CST                             BIT(2)
++#define XGMAC_ACS                             BIT(1)
++#define XGMAC_RXEN                            BIT(0)
++
++#define XGMAC_RX_CONFIG_MASK                  \
++      (XGMAC_GPSL_M | XGMAC_WD | XGMAC_GPSLEN | XGMAC_CST | \
++       XGMAC_ACS | XGMAC_RXEN)
++
++/* XGMAC packet filter register */
++#define XGMAC_PKT_FILTER_ADDR                 0x8
++#define XGMAC_RA                              BIT(31)
++#define XGMAC_PCF_M                           GENMASK(7, 6)
++#define XGMAC_PR                              BIT(0)
++
++#define XGMAC_PKT_FILTER_MASK                 \
++      (XGMAC_RA | XGMAC_PCF_M | XGMAC_PR)
++#define XGMAC_PKT_FILTER_VAL                  \
++      (XGMAC_RA | XGMAC_PR | FIELD_PREP(XGMAC_PCF_M, 0x2))
++
++/* XGMAC watchdog timeout register */
++#define XGMAC_WD_TIMEOUT_ADDR                 0xc
++#define XGMAC_PWE                             BIT(8)
++#define XGMAC_WTO_M                           GENMASK(3, 0)
++
++#define XGMAC_WD_TIMEOUT_MASK                 \
++      (XGMAC_PWE | XGMAC_WTO_M)
++#define XGMAC_WD_TIMEOUT_VAL                  \
++      (XGMAC_PWE | FIELD_PREP(XGMAC_WTO_M, 0xb))
++
++/* XGMAC TX flow control register */
++#define XGMAC_TX_FLOW_CTRL_ADDR                       0x70
++#define XGMAC_PAUSE_TIME_M                    GENMASK(31, 16)
++#define XGMAC_TXFCEN                          BIT(1)
++
++/* XGMAC RX flow control register */
++#define XGMAC_RX_FLOW_CTRL_ADDR                       0x90
++#define XGMAC_RXFCEN                          BIT(0)
++
++/* XGMAC management counters control register */
++#define XGMAC_MMC_CTRL_ADDR                   0x800
++#define XGMAC_MCF                             BIT(3)
++#define XGMAC_CNTRST                          BIT(0)
++
+ #endif
diff --git a/target/linux/qualcommbe/patches-6.18/0339-net-ethernet-qualcomm-Add-PPE-port-MAC-MIB-statistic.patch b/target/linux/qualcommbe/patches-6.18/0339-net-ethernet-qualcomm-Add-PPE-port-MAC-MIB-statistic.patch
new file mode 100644 (file)
index 0000000..1430692
--- /dev/null
@@ -0,0 +1,673 @@
+From dbcc0d01241a1353d8e11e764cf7fcd390ae3f1f Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Thu, 29 Feb 2024 20:16:14 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Add PPE port MAC MIB statistics
+ functions
+
+Add PPE port MAC MIB statistics functions which are used by netdev
+ops and ethtool. For GMAC, a polling task is scheduled to read the
+MIB counters periodically to avoid 32bit register counter overflow.
+
+Change-Id: Ic20e240061278f77d703f652e1f7d959db8fac37
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.c | 465 +++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.h |  13 +
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h |  91 ++++
+ 3 files changed, 569 insertions(+)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
+@@ -23,6 +23,122 @@
+ /* PPE BM port start for PPE MAC ports */
+ #define PPE_BM_PORT_MAC_START                 7
++/* Poll interval time to poll GMAC MIBs for overflow protection,
++ * the time should ensure that the 32bit GMAC packet counter
++ * register would not overflow within this time at line rate
++ * speed for 64B packet size.
++ */
++#define PPE_GMIB_POLL_INTERVAL_MS             120000
++
++#define PPE_MAC_MIB_DESC(_s, _o, _n)          \
++      {                                       \
++              .size = (_s),                   \
++              .offset = (_o),                 \
++              .name = (_n),                   \
++      }
++
++/* PPE MAC MIB description */
++struct ppe_mac_mib_info {
++      u32 size;
++      u32 offset;
++      const char *name;
++};
++
++/* PPE GMAC MIB statistics type */
++enum ppe_gmib_stats_type {
++      gmib_rx_broadcast,
++      gmib_rx_pause,
++      gmib_rx_multicast,
++      gmib_rx_fcserr,
++      gmib_rx_alignerr,
++      gmib_rx_runt,
++      gmib_rx_frag,
++      gmib_rx_jumbofcserr,
++      gmib_rx_jumboalignerr,
++      gmib_rx_pkt64,
++      gmib_rx_pkt65to127,
++      gmib_rx_pkt128to255,
++      gmib_rx_pkt256to511,
++      gmib_rx_pkt512to1023,
++      gmib_rx_pkt1024to1518,
++      gmib_rx_pkt1519tomax,
++      gmib_rx_toolong,
++      gmib_rx_bytes_g,
++      gmib_rx_bytes_b,
++      gmib_rx_unicast,
++      gmib_tx_broadcast,
++      gmib_tx_pause,
++      gmib_tx_multicast,
++      gmib_tx_underrun,
++      gmib_tx_pkt64,
++      gmib_tx_pkt65to127,
++      gmib_tx_pkt128to255,
++      gmib_tx_pkt256to511,
++      gmib_tx_pkt512to1023,
++      gmib_tx_pkt1024to1518,
++      gmib_tx_pkt1519tomax,
++      gmib_tx_bytes,
++      gmib_tx_collisions,
++      gmib_tx_abortcol,
++      gmib_tx_multicol,
++      gmib_tx_singlecol,
++      gmib_tx_excdeffer,
++      gmib_tx_deffer,
++      gmib_tx_latecol,
++      gmib_tx_unicast,
++};
++
++/* PPE XGMAC MIB statistics type */
++enum ppe_xgmib_stats_type {
++      xgmib_tx_bytes,
++      xgmib_tx_frames,
++      xgmib_tx_broadcast_g,
++      xgmib_tx_multicast_g,
++      xgmib_tx_pkt64,
++      xgmib_tx_pkt65to127,
++      xgmib_tx_pkt128to255,
++      xgmib_tx_pkt256to511,
++      xgmib_tx_pkt512to1023,
++      xgmib_tx_pkt1024tomax,
++      xgmib_tx_unicast,
++      xgmib_tx_multicast,
++      xgmib_tx_broadcast,
++      xgmib_tx_underflow_err,
++      xgmib_tx_bytes_g,
++      xgmib_tx_frames_g,
++      xgmib_tx_pause,
++      xgmib_tx_vlan_g,
++      xgmib_tx_lpi_usec,
++      xgmib_tx_lpi_tran,
++      xgmib_rx_frames,
++      xgmib_rx_bytes,
++      xgmib_rx_bytes_g,
++      xgmib_rx_broadcast_g,
++      xgmib_rx_multicast_g,
++      xgmib_rx_crc_err,
++      xgmib_rx_runt_err,
++      xgmib_rx_jabber_err,
++      xgmib_rx_undersize_g,
++      xgmib_rx_oversize_g,
++      xgmib_rx_pkt64,
++      xgmib_rx_pkt65to127,
++      xgmib_rx_pkt128to255,
++      xgmib_rx_pkt256to511,
++      xgmib_rx_pkt512to1023,
++      xgmib_rx_pkt1024tomax,
++      xgmib_rx_unicast_g,
++      xgmib_rx_len_err,
++      xgmib_rx_outofrange_err,
++      xgmib_rx_pause,
++      xgmib_rx_fifo_overflow,
++      xgmib_rx_vlan,
++      xgmib_rx_wdog_err,
++      xgmib_rx_lpi_usec,
++      xgmib_rx_lpi_tran,
++      xgmib_rx_drop_frames,
++      xgmib_rx_drop_bytes,
++};
++
+ /* PPE port clock and reset name */
+ static const char * const ppe_port_clk_rst_name[] = {
+       [PPE_PORT_CLK_RST_MAC] = "port_mac",
+@@ -30,6 +146,322 @@ static const char * const ppe_port_clk_r
+       [PPE_PORT_CLK_RST_TX] = "port_tx",
+ };
++/* PPE GMAC MIB statistics description information */
++static const struct ppe_mac_mib_info gmib_info[] = {
++      PPE_MAC_MIB_DESC(4, GMAC_RXBROAD_ADDR, "rx_broadcast"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXPAUSE_ADDR, "rx_pause"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXMULTI_ADDR, "rx_multicast"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXFCSERR_ADDR, "rx_fcserr"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXALIGNERR_ADDR, "rx_alignerr"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXRUNT_ADDR, "rx_runt"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXFRAG_ADDR, "rx_frag"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXJUMBOFCSERR_ADDR, "rx_jumbofcserr"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXJUMBOALIGNERR_ADDR, "rx_jumboalignerr"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXPKT64_ADDR, "rx_pkt64"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXPKT65TO127_ADDR, "rx_pkt65to127"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXPKT128TO255_ADDR, "rx_pkt128to255"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXPKT256TO511_ADDR, "rx_pkt256to511"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXPKT512TO1023_ADDR, "rx_pkt512to1023"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXPKT1024TO1518_ADDR, "rx_pkt1024to1518"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXPKT1519TOX_ADDR, "rx_pkt1519tomax"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXTOOLONG_ADDR, "rx_toolong"),
++      PPE_MAC_MIB_DESC(8, GMAC_RXBYTE_G_ADDR, "rx_bytes_g"),
++      PPE_MAC_MIB_DESC(8, GMAC_RXBYTE_B_ADDR, "rx_bytes_b"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXUNI_ADDR, "rx_unicast"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXBROAD_ADDR, "tx_broadcast"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXPAUSE_ADDR, "tx_pause"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXMULTI_ADDR, "tx_multicast"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXUNDERRUN_ADDR, "tx_underrun"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXPKT64_ADDR, "tx_pkt64"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXPKT65TO127_ADDR, "tx_pkt65to127"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXPKT128TO255_ADDR, "tx_pkt128to255"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXPKT256TO511_ADDR, "tx_pkt256to511"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXPKT512TO1023_ADDR, "tx_pkt512to1023"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXPKT1024TO1518_ADDR, "tx_pkt1024to1518"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXPKT1519TOX_ADDR, "tx_pkt1519tomax"),
++      PPE_MAC_MIB_DESC(8, GMAC_TXBYTE_ADDR, "tx_bytes"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXCOLLISIONS_ADDR, "tx_collisions"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXABORTCOL_ADDR, "tx_abortcol"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXMULTICOL_ADDR, "tx_multicol"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXSINGLECOL_ADDR, "tx_singlecol"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXEXCESSIVEDEFER_ADDR, "tx_excdeffer"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXDEFER_ADDR, "tx_deffer"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXLATECOL_ADDR, "tx_latecol"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXUNI_ADDR, "tx_unicast"),
++};
++
++/* PPE XGMAC MIB statistics description information */
++static const struct ppe_mac_mib_info xgmib_info[] = {
++      PPE_MAC_MIB_DESC(8, XGMAC_TXBYTE_GB_ADDR, "tx_bytes"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPKT_GB_ADDR, "tx_frames"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXBROAD_G_ADDR, "tx_broadcast_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXMULTI_G_ADDR, "tx_multicast_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPKT64_GB_ADDR, "tx_pkt64"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPKT65TO127_GB_ADDR, "tx_pkt65to127"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPKT128TO255_GB_ADDR, "tx_pkt128to255"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPKT256TO511_GB_ADDR, "tx_pkt256to511"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPKT512TO1023_GB_ADDR, "tx_pkt512to1023"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPKT1024TOMAX_GB_ADDR, "tx_pkt1024tomax"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXUNI_GB_ADDR, "tx_unicast"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXMULTI_GB_ADDR, "tx_multicast"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXBROAD_GB_ADDR, "tx_broadcast"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXUNDERFLOW_ERR_ADDR, "tx_underflow_err"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXBYTE_G_ADDR, "tx_bytes_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPKT_G_ADDR, "tx_frames_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPAUSE_ADDR, "tx_pause"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXVLAN_G_ADDR, "tx_vlan_g"),
++      PPE_MAC_MIB_DESC(4, XGMAC_TXLPI_USEC_ADDR, "tx_lpi_usec"),
++      PPE_MAC_MIB_DESC(4, XGMAC_TXLPI_TRAN_ADDR, "tx_lpi_tran"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXPKT_GB_ADDR, "rx_frames"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXBYTE_GB_ADDR, "rx_bytes"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXBYTE_G_ADDR, "rx_bytes_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXBROAD_G_ADDR, "rx_broadcast_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXMULTI_G_ADDR, "rx_multicast_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXCRC_ERR_ADDR, "rx_crc_err"),
++      PPE_MAC_MIB_DESC(4, XGMAC_RXRUNT_ERR_ADDR, "rx_runt_err"),
++      PPE_MAC_MIB_DESC(4, XGMAC_RXJABBER_ERR_ADDR, "rx_jabber_err"),
++      PPE_MAC_MIB_DESC(4, XGMAC_RXUNDERSIZE_G_ADDR, "rx_undersize_g"),
++      PPE_MAC_MIB_DESC(4, XGMAC_RXOVERSIZE_G_ADDR, "rx_oversize_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXPKT64_GB_ADDR, "rx_pkt64"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXPKT65TO127_GB_ADDR, "rx_pkt65to127"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXPKT128TO255_GB_ADDR, "rx_pkt128to255"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXPKT256TO511_GB_ADDR, "rx_pkt256to511"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXPKT512TO1023_GB_ADDR, "rx_pkt512to1023"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXPKT1024TOMAX_GB_ADDR, "rx_pkt1024tomax"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXUNI_G_ADDR, "rx_unicast_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXLEN_ERR_ADDR, "rx_len_err"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXOUTOFRANGE_ADDR, "rx_outofrange_err"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXPAUSE_ADDR, "rx_pause"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXFIFOOVERFLOW_ADDR, "rx_fifo_overflow"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXVLAN_GB_ADDR, "rx_vlan"),
++      PPE_MAC_MIB_DESC(4, XGMAC_RXWATCHDOG_ERR_ADDR, "rx_wdog_err"),
++      PPE_MAC_MIB_DESC(4, XGMAC_RXLPI_USEC_ADDR, "rx_lpi_usec"),
++      PPE_MAC_MIB_DESC(4, XGMAC_RXLPI_TRAN_ADDR, "rx_lpi_tran"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXDISCARD_GB_ADDR, "rx_drop_frames"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXDISCARDBYTE_GB_ADDR, "rx_drop_bytes"),
++};
++
++/* Get GMAC MIBs from registers and accumulate to PPE port GMIB stats array */
++static void ppe_port_gmib_update(struct ppe_port *ppe_port)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      const struct ppe_mac_mib_info *mib;
++      int port = ppe_port->port_id;
++      u32 reg, val;
++      int i, ret;
++
++      for (i = 0; i < ARRAY_SIZE(gmib_info); i++) {
++              mib = &gmib_info[i];
++              reg = PPE_PORT_GMAC_ADDR(port) + mib->offset;
++
++              ret = regmap_read(ppe_dev->regmap, reg, &val);
++              if (ret) {
++                      dev_warn(ppe_dev->dev, "%s: %d\n", __func__, ret);
++                      continue;
++              }
++
++              ppe_port->gmib_stats[i] += val;
++              if (mib->size == 8) {
++                      ret = regmap_read(ppe_dev->regmap, reg + 4, &val);
++                      if (ret) {
++                              dev_warn(ppe_dev->dev, "%s: %d\n",
++                                       __func__, ret);
++                              continue;
++                      }
++
++                      ppe_port->gmib_stats[i] += (u64)val << 32;
++              }
++      }
++}
++
++/* Polling task to read GMIB statistics to avoid GMIB 32bit register overflow */
++static void ppe_port_gmib_stats_poll(struct work_struct *work)
++{
++      struct ppe_port *ppe_port = container_of(work, struct ppe_port,
++                                               gmib_read.work);
++      spin_lock(&ppe_port->gmib_stats_lock);
++      ppe_port_gmib_update(ppe_port);
++      spin_unlock(&ppe_port->gmib_stats_lock);
++
++      schedule_delayed_work(&ppe_port->gmib_read,
++                            msecs_to_jiffies(PPE_GMIB_POLL_INTERVAL_MS));
++}
++
++/* Get the XGMAC MIB counter based on the specific MIB stats type */
++static u64 ppe_port_xgmib_get(struct ppe_port *ppe_port,
++                            enum ppe_xgmib_stats_type xgmib_type)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      const struct ppe_mac_mib_info *mib;
++      int port = ppe_port->port_id;
++      u32 reg, val;
++      u64 data = 0;
++      int ret;
++
++      mib = &xgmib_info[xgmib_type];
++      reg = PPE_PORT_XGMAC_ADDR(port) + mib->offset;
++
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret) {
++              dev_warn(ppe_dev->dev, "%s: %d\n", __func__, ret);
++              goto data_return;
++      }
++
++      data = val;
++      if (mib->size == 8) {
++              ret = regmap_read(ppe_dev->regmap, reg + 4, &val);
++              if (ret) {
++                      dev_warn(ppe_dev->dev, "%s: %d\n", __func__, ret);
++                      goto data_return;
++              }
++
++              data |= (u64)val << 32;
++      }
++
++data_return:
++      return data;
++}
++
++/**
++ * ppe_port_get_sset_count() - Get PPE port statistics string count
++ * @ppe_port: PPE port
++ * @sset: string set ID
++ *
++ * Description: Get the MAC statistics string count for the PPE port
++ * specified by @ppe_port.
++ *
++ * Return: The count of the statistics string.
++ */
++int ppe_port_get_sset_count(struct ppe_port *ppe_port, int sset)
++{
++      if (sset != ETH_SS_STATS)
++              return 0;
++
++      if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC)
++              return ARRAY_SIZE(gmib_info);
++      else
++              return ARRAY_SIZE(xgmib_info);
++}
++
++/**
++ * ppe_port_get_strings() - Get PPE port statistics strings
++ * @ppe_port: PPE port
++ * @stringset: string set ID
++ * @data: pointer to statistics strings
++ *
++ * Description: Get the MAC statistics stings for the PPE port
++ * specified by @ppe_port. The strings are stored in the buffer
++ * indicated by @data which used in the ethtool ops.
++ */
++void ppe_port_get_strings(struct ppe_port *ppe_port, u32 stringset, u8 *data)
++{
++      int i;
++
++      if (stringset != ETH_SS_STATS)
++              return;
++
++      if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
++              for (i = 0; i < ARRAY_SIZE(gmib_info); i++)
++                      strscpy(data + i * ETH_GSTRING_LEN, gmib_info[i].name,
++                              ETH_GSTRING_LEN);
++      } else {
++              for (i = 0; i < ARRAY_SIZE(xgmib_info); i++)
++                      strscpy(data + i * ETH_GSTRING_LEN, xgmib_info[i].name,
++                              ETH_GSTRING_LEN);
++      }
++}
++
++/**
++ * ppe_port_get_ethtool_stats() - Get PPE port ethtool statistics
++ * @ppe_port: PPE port
++ * @data: pointer to statistics data
++ *
++ * Description: Get the MAC statistics for the PPE port specified
++ * by @ppe_port. The statistics are stored in the buffer indicated
++ * by @data which used in the ethtool ops.
++ */
++void ppe_port_get_ethtool_stats(struct ppe_port *ppe_port, u64 *data)
++{
++      int i;
++
++      if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
++              spin_lock(&ppe_port->gmib_stats_lock);
++
++              ppe_port_gmib_update(ppe_port);
++              for (i = 0; i < ARRAY_SIZE(gmib_info); i++)
++                      data[i] = ppe_port->gmib_stats[i];
++
++              spin_unlock(&ppe_port->gmib_stats_lock);
++      } else {
++              for (i = 0; i < ARRAY_SIZE(xgmib_info); i++)
++                      data[i] = ppe_port_xgmib_get(ppe_port, i);
++      }
++}
++
++/**
++ * ppe_port_get_stats64() - Get PPE port statistics
++ * @ppe_port: PPE port
++ * @s: statistics pointer
++ *
++ * Description: Get the MAC statistics for the PPE port specified
++ * by @ppe_port.
++ */
++void ppe_port_get_stats64(struct ppe_port *ppe_port,
++                        struct rtnl_link_stats64 *s)
++{
++      if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
++              u64 *src = ppe_port->gmib_stats;
++
++              spin_lock(&ppe_port->gmib_stats_lock);
++
++              ppe_port_gmib_update(ppe_port);
++
++              s->rx_packets = src[gmib_rx_unicast] +
++                      src[gmib_rx_broadcast] + src[gmib_rx_multicast];
++
++              s->tx_packets = src[gmib_tx_unicast] +
++                      src[gmib_tx_broadcast] + src[gmib_tx_multicast];
++
++              s->rx_bytes = src[gmib_rx_bytes_g];
++              s->tx_bytes = src[gmib_tx_bytes];
++              s->multicast = src[gmib_rx_multicast];
++
++              s->rx_crc_errors = src[gmib_rx_fcserr] + src[gmib_rx_frag];
++              s->rx_frame_errors = src[gmib_rx_alignerr];
++              s->rx_errors = s->rx_crc_errors + s->rx_frame_errors;
++              s->rx_dropped = src[gmib_rx_toolong] + s->rx_errors;
++
++              s->tx_fifo_errors = src[gmib_tx_underrun];
++              s->tx_aborted_errors = src[gmib_tx_abortcol];
++              s->tx_errors = s->tx_fifo_errors + s->tx_aborted_errors;
++              s->collisions = src[gmib_tx_collisions];
++
++              spin_unlock(&ppe_port->gmib_stats_lock);
++      } else {
++              s->multicast = ppe_port_xgmib_get(ppe_port, xgmib_rx_multicast_g);
++
++              s->rx_packets = s->multicast;
++              s->rx_packets += ppe_port_xgmib_get(ppe_port, xgmib_rx_unicast_g);
++              s->rx_packets += ppe_port_xgmib_get(ppe_port, xgmib_rx_broadcast_g);
++
++              s->tx_packets = ppe_port_xgmib_get(ppe_port, xgmib_tx_frames);
++              s->rx_bytes = ppe_port_xgmib_get(ppe_port, xgmib_rx_bytes);
++              s->tx_bytes = ppe_port_xgmib_get(ppe_port, xgmib_tx_bytes);
++
++              s->rx_crc_errors = ppe_port_xgmib_get(ppe_port, xgmib_rx_crc_err);
++              s->rx_fifo_errors = ppe_port_xgmib_get(ppe_port, xgmib_rx_fifo_overflow);
++
++              s->rx_length_errors = ppe_port_xgmib_get(ppe_port, xgmib_rx_len_err);
++              s->rx_errors = s->rx_crc_errors +
++                      s->rx_fifo_errors + s->rx_length_errors;
++              s->rx_dropped = s->rx_errors;
++
++              s->tx_fifo_errors = ppe_port_xgmib_get(ppe_port, xgmib_tx_underflow_err);
++              s->tx_errors = s->tx_packets -
++                      ppe_port_xgmib_get(ppe_port, xgmib_tx_frames_g);
++      }
++}
++
+ /* PPE port and MAC reset */
+ static int ppe_port_mac_reset(struct ppe_port *ppe_port)
+ {
+@@ -261,6 +693,9 @@ static void ppe_port_mac_link_up(struct
+       int ret, port = ppe_port->port_id;
+       u32 reg, val;
++      /* Start GMIB statistics polling */
++      schedule_delayed_work(&ppe_port->gmib_read, 0);
++
+       if (mac_type == PPE_MAC_TYPE_GMAC)
+               ret = ppe_port_gmac_link_up(ppe_port,
+                                           speed, duplex, tx_pause, rx_pause);
+@@ -306,6 +741,9 @@ static void ppe_port_mac_link_down(struc
+       int ret, port = ppe_port->port_id;
+       u32 reg;
++      /* Stop GMIB statistics polling */
++      cancel_delayed_work_sync(&ppe_port->gmib_read);
++
+       /* Disable PPE port TX */
+       reg = PPE_PORT_BRIDGE_CTRL_ADDR + PPE_PORT_BRIDGE_CTRL_INC * port;
+       ret = regmap_update_bits(ppe_dev->regmap, reg,
+@@ -627,6 +1065,27 @@ static int ppe_port_mac_hw_init(struct p
+       return ret;
+ }
++/* PPE port MAC MIB work task initialization */
++static int ppe_port_mac_mib_work_init(struct ppe_port *ppe_port)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      u64 *gstats;
++
++      gstats = devm_kzalloc(ppe_dev->dev,
++                            sizeof(*gstats) * ARRAY_SIZE(gmib_info),
++                            GFP_KERNEL);
++      if (!gstats)
++              return -ENOMEM;
++
++      ppe_port->gmib_stats = gstats;
++
++      spin_lock_init(&ppe_port->gmib_stats_lock);
++      INIT_DELAYED_WORK(&ppe_port->gmib_read,
++                        ppe_port_gmib_stats_poll);
++
++      return 0;
++}
++
+ /**
+  * ppe_port_mac_init() - Initialization of PPE ports for the PPE device
+  * @ppe_dev: PPE device
+@@ -693,6 +1152,12 @@ int ppe_port_mac_init(struct ppe_device
+                       goto err_port_node;
+               }
++              ret = ppe_port_mac_mib_work_init(&ppe_ports->port[i]);
++              if (ret) {
++                      dev_err(ppe_dev->dev, "Failed to initialize MAC MIB work\n");
++                      goto err_port_node;
++              }
++
+               i++;
+       }
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.h
+@@ -8,6 +8,8 @@
+ #include <linux/phylink.h>
++struct rtnl_link_stats64;
++
+ /**
+  * enum ppe_port_clk_rst_type - PPE port clock and reset ID type
+  * @PPE_PORT_CLK_RST_MAC: The clock and reset ID for port MAC
+@@ -44,6 +46,9 @@ enum ppe_mac_type {
+  * @port_id: Port ID
+  * @clks: Port clocks
+  * @rstcs: Port resets
++ * @gmib_read: Delay work task for GMAC MIB statistics polling function
++ * @gmib_stats: GMAC MIB statistics array
++ * @gmib_stats_lock: Lock to protect GMAC MIB statistics
+  */
+ struct ppe_port {
+       struct phylink *phylink;
+@@ -56,6 +61,9 @@ struct ppe_port {
+       int port_id;
+       struct clk *clks[PPE_PORT_CLK_RST_MAX];
+       struct reset_control *rstcs[PPE_PORT_CLK_RST_MAX];
++      struct delayed_work gmib_read;
++      u64 *gmib_stats;
++      spinlock_t gmib_stats_lock; /* Protects GMIB stats */
+ };
+ /**
+@@ -73,4 +81,9 @@ void ppe_port_mac_deinit(struct ppe_devi
+ int ppe_port_phylink_setup(struct ppe_port *ppe_port,
+                          struct net_device *netdev);
+ void ppe_port_phylink_destroy(struct ppe_port *ppe_port);
++int ppe_port_get_sset_count(struct ppe_port *ppe_port, int sset);
++void ppe_port_get_strings(struct ppe_port *ppe_port, u32 stringset, u8 *data);
++void ppe_port_get_ethtool_stats(struct ppe_port *ppe_port, u64 *data);
++void ppe_port_get_stats64(struct ppe_port *ppe_port,
++                        struct rtnl_link_stats64 *s);
+ #endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -618,6 +618,48 @@
+ #define GMAC_MIB_CTRL_MASK                    \
+       (GMAC_MIB_RD_CLR | GMAC_MIB_RST | GMAC_MIB_EN)
++/* GMAC MIB counter registers */
++#define GMAC_RXBROAD_ADDR                     0x40
++#define GMAC_RXPAUSE_ADDR                     0x44
++#define GMAC_RXMULTI_ADDR                     0x48
++#define GMAC_RXFCSERR_ADDR                    0x4C
++#define GMAC_RXALIGNERR_ADDR                  0x50
++#define GMAC_RXRUNT_ADDR                      0x54
++#define GMAC_RXFRAG_ADDR                      0x58
++#define GMAC_RXJUMBOFCSERR_ADDR                       0x5C
++#define GMAC_RXJUMBOALIGNERR_ADDR             0x60
++#define GMAC_RXPKT64_ADDR                     0x64
++#define GMAC_RXPKT65TO127_ADDR                        0x68
++#define GMAC_RXPKT128TO255_ADDR                       0x6C
++#define GMAC_RXPKT256TO511_ADDR                       0x70
++#define GMAC_RXPKT512TO1023_ADDR              0x74
++#define GMAC_RXPKT1024TO1518_ADDR             0x78
++#define GMAC_RXPKT1519TOX_ADDR                        0x7C
++#define GMAC_RXTOOLONG_ADDR                   0x80
++#define GMAC_RXBYTE_G_ADDR                    0x84
++#define GMAC_RXBYTE_B_ADDR                    0x8C
++#define GMAC_RXUNI_ADDR                               0x94
++#define GMAC_TXBROAD_ADDR                     0xA0
++#define GMAC_TXPAUSE_ADDR                     0xA4
++#define GMAC_TXMULTI_ADDR                     0xA8
++#define GMAC_TXUNDERRUN_ADDR                  0xAC
++#define GMAC_TXPKT64_ADDR                     0xB0
++#define GMAC_TXPKT65TO127_ADDR                        0xB4
++#define GMAC_TXPKT128TO255_ADDR                       0xB8
++#define GMAC_TXPKT256TO511_ADDR                       0xBC
++#define GMAC_TXPKT512TO1023_ADDR              0xC0
++#define GMAC_TXPKT1024TO1518_ADDR             0xC4
++#define GMAC_TXPKT1519TOX_ADDR                        0xC8
++#define GMAC_TXBYTE_ADDR                      0xCC
++#define GMAC_TXCOLLISIONS_ADDR                        0xD4
++#define GMAC_TXABORTCOL_ADDR                  0xD8
++#define GMAC_TXMULTICOL_ADDR                  0xDC
++#define GMAC_TXSINGLECOL_ADDR                 0xE0
++#define GMAC_TXEXCESSIVEDEFER_ADDR            0xE4
++#define GMAC_TXDEFER_ADDR                     0xE8
++#define GMAC_TXLATECOL_ADDR                   0xEC
++#define GMAC_TXUNI_ADDR                               0xF0
++
+ /* XGMAC TX configuration register */
+ #define XGMAC_TX_CONFIG_ADDR                  0x0
+ #define XGMAC_SPEED_M                         GENMASK(31, 29)
+@@ -680,4 +722,53 @@
+ #define XGMAC_MCF                             BIT(3)
+ #define XGMAC_CNTRST                          BIT(0)
++/* XGMAC MIB counter registers */
++#define XGMAC_TXBYTE_GB_ADDR                  0x814
++#define XGMAC_TXPKT_GB_ADDR                   0x81C
++#define XGMAC_TXBROAD_G_ADDR                  0x824
++#define XGMAC_TXMULTI_G_ADDR                  0x82C
++#define XGMAC_TXPKT64_GB_ADDR                 0x834
++#define XGMAC_TXPKT65TO127_GB_ADDR            0x83C
++#define XGMAC_TXPKT128TO255_GB_ADDR           0x844
++#define XGMAC_TXPKT256TO511_GB_ADDR           0x84C
++#define XGMAC_TXPKT512TO1023_GB_ADDR          0x854
++#define XGMAC_TXPKT1024TOMAX_GB_ADDR          0x85C
++#define XGMAC_TXUNI_GB_ADDR                   0x864
++#define XGMAC_TXMULTI_GB_ADDR                 0x86C
++#define XGMAC_TXBROAD_GB_ADDR                 0x874
++#define XGMAC_TXUNDERFLOW_ERR_ADDR            0x87C
++#define XGMAC_TXBYTE_G_ADDR                   0x884
++#define XGMAC_TXPKT_G_ADDR                    0x88C
++#define XGMAC_TXPAUSE_ADDR                    0x894
++#define XGMAC_TXVLAN_G_ADDR                   0x89C
++#define XGMAC_TXLPI_USEC_ADDR                 0x8A4
++#define XGMAC_TXLPI_TRAN_ADDR                 0x8A8
++#define XGMAC_RXPKT_GB_ADDR                   0x900
++#define XGMAC_RXBYTE_GB_ADDR                  0x908
++#define XGMAC_RXBYTE_G_ADDR                   0x910
++#define XGMAC_RXBROAD_G_ADDR                  0x918
++#define XGMAC_RXMULTI_G_ADDR                  0x920
++#define XGMAC_RXCRC_ERR_ADDR                  0x928
++#define XGMAC_RXRUNT_ERR_ADDR                 0x930
++#define XGMAC_RXJABBER_ERR_ADDR                       0x934
++#define XGMAC_RXUNDERSIZE_G_ADDR              0x938
++#define XGMAC_RXOVERSIZE_G_ADDR                       0x93C
++#define XGMAC_RXPKT64_GB_ADDR                 0x940
++#define XGMAC_RXPKT65TO127_GB_ADDR            0x948
++#define XGMAC_RXPKT128TO255_GB_ADDR           0x950
++#define XGMAC_RXPKT256TO511_GB_ADDR           0x958
++#define XGMAC_RXPKT512TO1023_GB_ADDR          0x960
++#define XGMAC_RXPKT1024TOMAX_GB_ADDR          0x968
++#define XGMAC_RXUNI_G_ADDR                    0x970
++#define XGMAC_RXLEN_ERR_ADDR                  0x978
++#define XGMAC_RXOUTOFRANGE_ADDR                       0x980
++#define XGMAC_RXPAUSE_ADDR                    0x988
++#define XGMAC_RXFIFOOVERFLOW_ADDR             0x990
++#define XGMAC_RXVLAN_GB_ADDR                  0x998
++#define XGMAC_RXWATCHDOG_ERR_ADDR             0x9A0
++#define XGMAC_RXLPI_USEC_ADDR                 0x9A4
++#define XGMAC_RXLPI_TRAN_ADDR                 0x9A8
++#define XGMAC_RXDISCARD_GB_ADDR                       0x9AC
++#define XGMAC_RXDISCARDBYTE_GB_ADDR           0x9B4
++
+ #endif
diff --git a/target/linux/qualcommbe/patches-6.18/0340-net-ethernet-qualcomm-Add-PPE-port-MAC-address-and-E.patch b/target/linux/qualcommbe/patches-6.18/0340-net-ethernet-qualcomm-Add-PPE-port-MAC-address-and-E.patch
new file mode 100644 (file)
index 0000000..856a1ed
--- /dev/null
@@ -0,0 +1,172 @@
+From 55fbbc8ef90df27a16bca1613a793a578b79a384 Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Fri, 1 Mar 2024 13:36:26 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Add PPE port MAC address and EEE
+ functions
+
+Add PPE port MAC address set and EEE set API functions which
+will be used by netdev ops and ethtool.
+
+Change-Id: Id2b3b06ae940b3b6f5227d927316329cdf3caeaa
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+Alex G: use struct ethtool_keee instead of ethtool_eee
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.c | 75 ++++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.h |  3 +
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h | 29 ++++++++
+ 3 files changed, 107 insertions(+)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
+@@ -462,6 +462,81 @@ void ppe_port_get_stats64(struct ppe_por
+       }
+ }
++/**
++ * ppe_port_set_mac_address() - Set PPE port MAC address
++ * @ppe_port: PPE port
++ * @addr: MAC address
++ *
++ * Description: Set MAC address for the given PPE port.
++ *
++ * Return: 0 upon success or a negative error upon failure.
++ */
++int ppe_port_set_mac_address(struct ppe_port *ppe_port, const u8 *addr)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int port = ppe_port->port_id;
++      u32 reg, val;
++      int ret;
++
++      if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
++              reg = PPE_PORT_GMAC_ADDR(port);
++              val = (addr[5] << 8) | addr[4];
++              ret = regmap_write(ppe_dev->regmap, reg + GMAC_GOL_ADDR0_ADDR, val);
++              if (ret)
++                      return ret;
++
++              val = (addr[0] << 24) | (addr[1] << 16) |
++                    (addr[2] << 8) | addr[3];
++              ret = regmap_write(ppe_dev->regmap, reg + GMAC_GOL_ADDR1_ADDR, val);
++              if (ret)
++                      return ret;
++      } else {
++              reg = PPE_PORT_XGMAC_ADDR(port);
++              val = (addr[5] << 8) | addr[4] | XGMAC_ADDR_EN;
++              ret = regmap_write(ppe_dev->regmap, reg + XGMAC_ADDR0_H_ADDR, val);
++              if (ret)
++                      return ret;
++
++              val = (addr[3] << 24) | (addr[2] << 16) |
++                    (addr[1] << 8) | addr[0];
++              ret = regmap_write(ppe_dev->regmap, reg + XGMAC_ADDR0_L_ADDR, val);
++              if (ret)
++                      return ret;
++      }
++
++      return 0;
++}
++
++/**
++ * ppe_port_set_mac_eee() - Set EEE configuration for PPE port MAC
++ * @ppe_port: PPE port
++ * @eee: EEE settings
++ *
++ * Description: Set port MAC EEE settings for the given PPE port.
++ *
++ * Return: 0 upon success or a negative error upon failure.
++ */
++int ppe_port_set_mac_eee(struct ppe_port *ppe_port, struct ethtool_keee *eee)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int port = ppe_port->port_id;
++      u32 val;
++      int ret;
++
++      ret = regmap_read(ppe_dev->regmap, PPE_LPI_EN_ADDR, &val);
++      if (ret)
++              return ret;
++
++      if (eee->tx_lpi_enabled)
++              val |= PPE_LPI_PORT_EN(port);
++      else
++              val &= ~PPE_LPI_PORT_EN(port);
++
++      ret = regmap_write(ppe_dev->regmap, PPE_LPI_EN_ADDR, val);
++
++      return ret;
++}
++
+ /* PPE port and MAC reset */
+ static int ppe_port_mac_reset(struct ppe_port *ppe_port)
+ {
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.h
+@@ -8,6 +8,7 @@
+ #include <linux/phylink.h>
++struct ethtool_keee;
+ struct rtnl_link_stats64;
+ /**
+@@ -86,4 +87,6 @@ void ppe_port_get_strings(struct ppe_por
+ void ppe_port_get_ethtool_stats(struct ppe_port *ppe_port, u64 *data);
+ void ppe_port_get_stats64(struct ppe_port *ppe_port,
+                         struct rtnl_link_stats64 *s);
++int ppe_port_set_mac_address(struct ppe_port *ppe_port, const u8 *addr);
++int ppe_port_set_mac_eee(struct ppe_port *ppe_port, struct ethtool_keee *eee);
+ #endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -20,6 +20,16 @@
+ #define PPE_PORT5_SEL_PCS1                    BIT(4)
+ #define PPE_PORT_SEL_XGMAC(x)                 (BIT(8) << ((x) - 1))
++/* PPE port LPI enable register */
++#define PPE_LPI_EN_ADDR                               0x400
++#define PPE_LPI_PORT1_EN                      BIT(0)
++#define PPE_LPI_PORT2_EN                      BIT(1)
++#define PPE_LPI_PORT3_EN                      BIT(2)
++#define PPE_LPI_PORT4_EN                      BIT(3)
++#define PPE_LPI_PORT5_EN                      BIT(4)
++#define PPE_LPI_PORT6_EN                      BIT(5)
++#define PPE_LPI_PORT_EN(x)                    (BIT(0) << ((x) - 1))
++
+ /* PPE scheduler configurations for buffer manager block. */
+ #define PPE_BM_SCH_CTRL_ADDR                  0xb000
+ #define PPE_BM_SCH_CTRL_INC                   4
+@@ -592,6 +602,17 @@
+ #define GMAC_SPEED_100                                1
+ #define GMAC_SPEED_1000                               2
++/* GMAC MAC address register */
++#define GMAC_GOL_ADDR0_ADDR                   0x8
++#define GMAC_ADDR_BYTE5                               GENMASK(15, 8)
++#define GMAC_ADDR_BYTE4                               GENMASK(7, 0)
++
++#define GMAC_GOL_ADDR1_ADDR                   0xC
++#define GMAC_ADDR_BYTE0                               GENMASK(31, 24)
++#define GMAC_ADDR_BYTE1                               GENMASK(23, 16)
++#define GMAC_ADDR_BYTE2                               GENMASK(15, 8)
++#define GMAC_ADDR_BYTE3                               GENMASK(7, 0)
++
+ /* GMAC control register */
+ #define GMAC_CTRL_ADDR                                0x18
+ #define GMAC_TX_THD_M                         GENMASK(27, 24)
+@@ -717,6 +738,14 @@
+ #define XGMAC_RX_FLOW_CTRL_ADDR                       0x90
+ #define XGMAC_RXFCEN                          BIT(0)
++/* XGMAC MAC address register */
++#define XGMAC_ADDR0_H_ADDR                    0x300
++#define XGMAC_ADDR_EN                         BIT(31)
++#define XGMAC_ADDRH                           GENMASK(15, 0)
++
++#define XGMAC_ADDR0_L_ADDR                    0x304
++#define XGMAC_ADDRL                           GENMASK(31, 0)
++
+ /* XGMAC management counters control register */
+ #define XGMAC_MMC_CTRL_ADDR                   0x800
+ #define XGMAC_MCF                             BIT(3)
diff --git a/target/linux/qualcommbe/patches-6.18/0341-net-ethernet-qualcomm-Add-API-to-configure-PPE-port-.patch b/target/linux/qualcommbe/patches-6.18/0341-net-ethernet-qualcomm-Add-API-to-configure-PPE-port-.patch
new file mode 100644 (file)
index 0000000..ae81ddb
--- /dev/null
@@ -0,0 +1,78 @@
+From 3981aeae5dd43dea94a0ec10f0b2977ebd102560 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Tue, 5 Mar 2024 16:42:56 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Add API to configure PPE port max
+ frame size
+
+This function is called when the MTU of an ethernet port is
+configured. It limits the size of packet passed through the
+ethernet port.
+
+Change-Id: I2a4dcd04407156d73770d2becbb7cbc0d56b3754
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.c | 44 ++++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.h |  1 +
+ 2 files changed, 45 insertions(+)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
+@@ -537,6 +537,50 @@ int ppe_port_set_mac_eee(struct ppe_port
+       return ret;
+ }
++/**
++ * ppe_port_set_maxframe() - Set port maximum frame size
++ * @ppe_port: PPE port structure
++ * @maxframe_size: Maximum frame size supported by PPE port
++ *
++ * Description: Set MTU of network interface specified by @ppe_port.
++ *
++ * Return: 0 upon success or a negative error upon failure.
++ */
++int ppe_port_set_maxframe(struct ppe_port *ppe_port, int maxframe_size)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      u32 reg, val, mru_mtu_val[3];
++      int port = ppe_port->port_id;
++      int ret;
++
++      /* The max frame size should be MTU added by ETH_HLEN in PPE. */
++      maxframe_size += ETH_HLEN;
++
++      /* MAC takes cover the FCS for the calculation of frame size. */
++      if (maxframe_size > PPE_PORT_MAC_MAX_FRAME_SIZE - ETH_FCS_LEN)
++              return -EINVAL;
++
++      reg = PPE_MC_MTU_CTRL_TBL_ADDR + PPE_MC_MTU_CTRL_TBL_INC * port;
++      val = FIELD_PREP(PPE_MC_MTU_CTRL_TBL_MTU, maxframe_size);
++      ret = regmap_update_bits(ppe_dev->regmap, reg,
++                               PPE_MC_MTU_CTRL_TBL_MTU,
++                               val);
++      if (ret)
++              return ret;
++
++      reg = PPE_MRU_MTU_CTRL_TBL_ADDR + PPE_MRU_MTU_CTRL_TBL_INC * port;
++      ret = regmap_bulk_read(ppe_dev->regmap, reg,
++                             mru_mtu_val, ARRAY_SIZE(mru_mtu_val));
++      if (ret)
++              return ret;
++
++      PPE_MRU_MTU_CTRL_SET_MRU(mru_mtu_val, maxframe_size);
++      PPE_MRU_MTU_CTRL_SET_MTU(mru_mtu_val, maxframe_size);
++
++      return regmap_bulk_write(ppe_dev->regmap, reg,
++                               mru_mtu_val, ARRAY_SIZE(mru_mtu_val));
++}
++
+ /* PPE port and MAC reset */
+ static int ppe_port_mac_reset(struct ppe_port *ppe_port)
+ {
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.h
+@@ -89,4 +89,5 @@ void ppe_port_get_stats64(struct ppe_por
+                         struct rtnl_link_stats64 *s);
+ int ppe_port_set_mac_address(struct ppe_port *ppe_port, const u8 *addr);
+ int ppe_port_set_mac_eee(struct ppe_port *ppe_port, struct ethtool_keee *eee);
++int ppe_port_set_maxframe(struct ppe_port *ppe_port, int maxframe_size);
+ #endif
diff --git a/target/linux/qualcommbe/patches-6.18/0342-net-ethernet-qualcomm-Add-EDMA-support-for-QCOM-IPQ9.patch b/target/linux/qualcommbe/patches-6.18/0342-net-ethernet-qualcomm-Add-EDMA-support-for-QCOM-IPQ9.patch
new file mode 100644 (file)
index 0000000..0160efd
--- /dev/null
@@ -0,0 +1,932 @@
+From 00d4f3cb4f5d1e6924151a4551f06b6a82bf0146 Mon Sep 17 00:00:00 2001
+From: Pavithra R <quic_pavir@quicinc.com>
+Date: Wed, 28 Feb 2024 11:25:15 +0530
+Subject: [PATCH] net: ethernet: qualcomm: Add EDMA support for QCOM IPQ9574
+ chipset.
+
+Add the infrastructure functions such as Makefile,
+EDMA hardware configuration, clock and IRQ initializations.
+
+Change-Id: I64f65e554e70e9095b0cf3636fec421569ae6895
+Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
+Co-developed-by: Suruchi Agarwal <quic_suruchia@quicinc.com>
+Signed-off-by: Suruchi Agarwal <quic_suruchia@quicinc.com>
+Alex G: use "ppe_config.h" header instead of "ppe_api.h"
+        add missing definitions and functions from ppe_api:
+        - enum ppe_queue_class_type {}
+        - ppe_edma_queue_offset_config()
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/Makefile   |   3 +
+ drivers/net/ethernet/qualcomm/ppe/edma.c     | 480 +++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/edma.h     | 113 +++++
+ drivers/net/ethernet/qualcomm/ppe/ppe.c      |  10 +-
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h | 253 ++++++++++
+ 5 files changed, 858 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma.c
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma.h
+
+--- a/drivers/net/ethernet/qualcomm/ppe/Makefile
++++ b/drivers/net/ethernet/qualcomm/ppe/Makefile
+@@ -5,3 +5,6 @@
+ obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o
+ qcom-ppe-objs := ppe.o ppe_config.o ppe_debugfs.o ppe_port.o
++
++#EDMA
++qcom-ppe-objs += edma.o
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.c
+@@ -0,0 +1,480 @@
++// SPDX-License-Identifier: GPL-2.0-only
++ /* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++  */
++
++ /* Qualcomm Ethernet DMA driver setup, HW configuration, clocks and
++  * interrupt initializations.
++  */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/netdevice.h>
++#include <linux/of_irq.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <linux/regmap.h>
++#include <linux/reset.h>
++
++#include "edma.h"
++#include "ppe_regs.h"
++
++#define EDMA_IRQ_NAME_SIZE            32
++
++/* Global EDMA context. */
++struct edma_context *edma_ctx;
++
++/* Priority to multi-queue mapping. */
++static u8 edma_pri_map[PPE_QUEUE_INTER_PRI_NUM] = {
++      0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7};
++
++enum edma_clk_id {
++      EDMA_CLK,
++      EDMA_CFG_CLK,
++      EDMA_CLK_MAX
++};
++
++static const char * const clock_name[EDMA_CLK_MAX] = {
++      [EDMA_CLK] = "edma",
++      [EDMA_CFG_CLK] = "edma-cfg",
++};
++
++/* Rx Fill ring info for IPQ9574. */
++static struct edma_ring_info ipq9574_rxfill_ring_info = {
++      .max_rings = 8,
++      .ring_start = 4,
++      .num_rings = 4,
++};
++
++/* Rx ring info for IPQ9574. */
++static struct edma_ring_info ipq9574_rx_ring_info = {
++      .max_rings = 24,
++      .ring_start = 20,
++      .num_rings = 4,
++};
++
++/* Tx ring info for IPQ9574. */
++static struct edma_ring_info ipq9574_tx_ring_info = {
++      .max_rings = 32,
++      .ring_start = 8,
++      .num_rings = 24,
++};
++
++/* Tx complete ring info for IPQ9574. */
++static struct edma_ring_info ipq9574_txcmpl_ring_info = {
++      .max_rings = 32,
++      .ring_start = 8,
++      .num_rings = 24,
++};
++
++/* HW info for IPQ9574. */
++static struct edma_hw_info ipq9574_hw_info = {
++      .rxfill = &ipq9574_rxfill_ring_info,
++      .rx = &ipq9574_rx_ring_info,
++      .tx = &ipq9574_tx_ring_info,
++      .txcmpl = &ipq9574_txcmpl_ring_info,
++      .max_ports = 6,
++      .napi_budget_rx = 128,
++      .napi_budget_tx = 512,
++};
++
++static int edma_clock_set_and_enable(struct device *dev,
++                                   const char *id, unsigned long rate)
++{
++      struct device_node *edma_np;
++      struct clk *clk = NULL;
++      int ret;
++
++      edma_np = of_get_child_by_name(dev->of_node, "edma");
++
++      clk = devm_get_clk_from_child(dev, edma_np, id);
++      if (IS_ERR(clk)) {
++              dev_err(dev, "clk %s get failed\n", id);
++              of_node_put(edma_np);
++              return PTR_ERR(clk);
++      }
++
++      ret = clk_set_rate(clk, rate);
++      if (ret) {
++              dev_err(dev, "set %lu rate for %s failed\n", rate, id);
++              of_node_put(edma_np);
++              return ret;
++      }
++
++      ret = clk_prepare_enable(clk);
++      if (ret) {
++              dev_err(dev, "clk %s enable failed\n", id);
++              of_node_put(edma_np);
++              return ret;
++      }
++
++      of_node_put(edma_np);
++
++      dev_dbg(dev, "set %lu rate for %s\n", rate, id);
++
++      return 0;
++}
++
++static int edma_clock_init(void)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device *dev = ppe_dev->dev;
++      unsigned long ppe_rate;
++      int ret;
++
++      ppe_rate = ppe_dev->clk_rate;
++
++      ret = edma_clock_set_and_enable(dev, clock_name[EDMA_CLK],
++                                      ppe_rate);
++      if (ret)
++              return ret;
++
++      ret = edma_clock_set_and_enable(dev, clock_name[EDMA_CFG_CLK],
++                                      ppe_rate);
++      if (ret)
++              return ret;
++
++      return 0;
++}
++
++/**
++ * edma_configure_ucast_prio_map_tbl - Configure unicast priority map table.
++ *
++ * Map int_priority values to priority class and initialize
++ * unicast priority map table for default profile_id.
++ */
++static int edma_configure_ucast_prio_map_tbl(void)
++{
++      u8 pri_class, int_pri;
++      int ret = 0;
++
++      /* Set the priority class value for every possible priority. */
++      for (int_pri = 0; int_pri < PPE_QUEUE_INTER_PRI_NUM; int_pri++) {
++              pri_class = edma_pri_map[int_pri];
++
++              /* Priority offset should be less than maximum supported
++               * queue priority.
++               */
++              if (pri_class > EDMA_PRI_MAX_PER_CORE - 1) {
++                      pr_err("Configured incorrect priority offset: %d\n",
++                             pri_class);
++                      return -EINVAL;
++              }
++
++              ret = ppe_edma_queue_offset_config(edma_ctx->ppe_dev,
++                                                 PPE_QUEUE_CLASS_PRIORITY, int_pri, pri_class);
++
++              if (ret) {
++                      pr_err("Failed with error: %d to set queue priority class for int_pri: %d for profile_id: %d\n",
++                             ret, int_pri, 0);
++                      return ret;
++              }
++
++              pr_debug("profile_id: %d, int_priority: %d, pri_class: %d\n",
++                       0, int_pri, pri_class);
++      }
++
++      return ret;
++}
++
++static int edma_irq_init(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct edma_ring_info *rx = hw_info->rx;
++      char edma_irq_name[EDMA_IRQ_NAME_SIZE];
++      struct device *dev = ppe_dev->dev;
++      struct platform_device *pdev;
++      struct device_node *edma_np;
++      u32 i;
++
++      pdev = to_platform_device(dev);
++      edma_np = of_get_child_by_name(dev->of_node, "edma");
++      edma_ctx->intr_info.intr_txcmpl = kzalloc((sizeof(*edma_ctx->intr_info.intr_txcmpl) *
++                                                txcmpl->num_rings), GFP_KERNEL);
++      if (!edma_ctx->intr_info.intr_txcmpl) {
++              of_node_put(edma_np);
++              return -ENOMEM;
++      }
++
++      /* Get TXCMPL rings IRQ numbers. */
++      for (i = 0; i < txcmpl->num_rings; i++) {
++              snprintf(edma_irq_name, sizeof(edma_irq_name), "edma_txcmpl_%d",
++                       txcmpl->ring_start + i);
++              edma_ctx->intr_info.intr_txcmpl[i] = of_irq_get_byname(edma_np, edma_irq_name);
++              if (edma_ctx->intr_info.intr_txcmpl[i] < 0) {
++                      dev_err(dev, "%s: txcmpl_info.intr[%u] irq get failed\n",
++                              edma_np->name, i);
++                      of_node_put(edma_np);
++                      kfree(edma_ctx->intr_info.intr_txcmpl);
++                      return edma_ctx->intr_info.intr_txcmpl[i];
++              }
++
++              dev_dbg(dev, "%s: intr_info.intr_txcmpl[%u] = %u\n",
++                      edma_np->name, i, edma_ctx->intr_info.intr_txcmpl[i]);
++      }
++
++      edma_ctx->intr_info.intr_rx = kzalloc((sizeof(*edma_ctx->intr_info.intr_rx) *
++                                            rx->num_rings), GFP_KERNEL);
++      if (!edma_ctx->intr_info.intr_rx) {
++              of_node_put(edma_np);
++              kfree(edma_ctx->intr_info.intr_txcmpl);
++              return -ENOMEM;
++      }
++
++      /* Get RXDESC rings IRQ numbers. */
++      for (i = 0; i < rx->num_rings; i++) {
++              snprintf(edma_irq_name, sizeof(edma_irq_name), "edma_rxdesc_%d",
++                       rx->ring_start + i);
++              edma_ctx->intr_info.intr_rx[i] = of_irq_get_byname(edma_np, edma_irq_name);
++              if (edma_ctx->intr_info.intr_rx[i] < 0) {
++                      dev_err(dev, "%s: rx_queue_map_info.intr[%u] irq get failed\n",
++                              edma_np->name, i);
++                      of_node_put(edma_np);
++                      kfree(edma_ctx->intr_info.intr_rx);
++                      kfree(edma_ctx->intr_info.intr_txcmpl);
++                      return edma_ctx->intr_info.intr_rx[i];
++              }
++
++              dev_dbg(dev, "%s: intr_info.intr_rx[%u] = %u\n",
++                      edma_np->name, i, edma_ctx->intr_info.intr_rx[i]);
++      }
++
++      /* Get misc IRQ number. */
++      edma_ctx->intr_info.intr_misc = of_irq_get_byname(edma_np, "edma_misc");
++      if (edma_ctx->intr_info.intr_misc < 0) {
++              dev_err(dev, "%s: misc_intr irq get failed\n", edma_np->name);
++              of_node_put(edma_np);
++              kfree(edma_ctx->intr_info.intr_rx);
++              kfree(edma_ctx->intr_info.intr_txcmpl);
++              return edma_ctx->intr_info.intr_misc;
++      }
++
++      of_node_put(edma_np);
++
++      dev_dbg(dev, "%s: misc IRQ:%u\n", edma_np->name,
++              edma_ctx->intr_info.intr_misc);
++
++      return 0;
++}
++
++static int edma_hw_reset(void)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device *dev = ppe_dev->dev;
++      struct reset_control *edma_hw_rst;
++      struct device_node *edma_np;
++      const char *reset_string;
++      u32 count, i;
++      int ret;
++
++      /* Count and parse reset names from DTSI. */
++      edma_np = of_get_child_by_name(dev->of_node, "edma");
++      count = of_property_count_strings(edma_np, "reset-names");
++      if (count < 0) {
++              dev_err(dev, "EDMA reset entry not found\n");
++              of_node_put(edma_np);
++              return -EINVAL;
++      }
++
++      for (i = 0; i < count; i++) {
++              ret = of_property_read_string_index(edma_np, "reset-names",
++                                                  i, &reset_string);
++              if (ret) {
++                      dev_err(dev, "Error reading reset-names");
++                      of_node_put(edma_np);
++                      return -EINVAL;
++              }
++
++              edma_hw_rst = of_reset_control_get_exclusive(edma_np, reset_string);
++              if (IS_ERR(edma_hw_rst)) {
++                      of_node_put(edma_np);
++                      return PTR_ERR(edma_hw_rst);
++              }
++
++              /* 100ms delay is required by hardware to reset EDMA. */
++              reset_control_assert(edma_hw_rst);
++              fsleep(100);
++
++              reset_control_deassert(edma_hw_rst);
++              fsleep(100);
++
++              reset_control_put(edma_hw_rst);
++              dev_dbg(dev, "EDMA HW reset, i:%d reset_string:%s\n", i, reset_string);
++      }
++
++      of_node_put(edma_np);
++
++      return 0;
++}
++
++static int edma_hw_configure(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 data, reg;
++      int ret;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_MAS_CTRL_ADDR;
++      ret = regmap_read(regmap, reg, &data);
++      if (ret)
++              return ret;
++
++      pr_debug("EDMA ver %d hw init\n", data);
++
++      /* Setup private data structure. */
++      edma_ctx->intr_info.intr_mask_rx = EDMA_RXDESC_INT_MASK_PKT_INT;
++      edma_ctx->intr_info.intr_mask_txcmpl = EDMA_TX_INT_MASK_PKT_INT;
++
++      /* Reset EDMA. */
++      ret = edma_hw_reset();
++      if (ret) {
++              pr_err("Error in resetting the hardware. ret: %d\n", ret);
++              return ret;
++      }
++
++      /* Allocate memory for netdevices. */
++      edma_ctx->netdev_arr = kzalloc((sizeof(**edma_ctx->netdev_arr) *
++                                       hw_info->max_ports),
++                                       GFP_KERNEL);
++      if (!edma_ctx->netdev_arr)
++              return -ENOMEM;
++
++      /* Configure DMA request priority, DMA read burst length,
++       * and AXI write size.
++       */
++      data = FIELD_PREP(EDMA_DMAR_BURST_LEN_MASK, EDMA_BURST_LEN_ENABLE);
++      data |= FIELD_PREP(EDMA_DMAR_REQ_PRI_MASK, 0);
++      data |= FIELD_PREP(EDMA_DMAR_TXDATA_OUTSTANDING_NUM_MASK, 31);
++      data |= FIELD_PREP(EDMA_DMAR_TXDESC_OUTSTANDING_NUM_MASK, 7);
++      data |= FIELD_PREP(EDMA_DMAR_RXFILL_OUTSTANDING_NUM_MASK, 7);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_DMAR_CTRL_ADDR;
++      ret = regmap_write(regmap, reg, data);
++      if (ret)
++              return ret;
++
++      /* Configure Tx Timeout Threshold. */
++      data = EDMA_TX_TIMEOUT_THRESH_VAL;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TX_TIMEOUT_THRESH_ADDR;
++      ret = regmap_write(regmap, reg, data);
++      if (ret)
++              return ret;
++
++      /* Set Miscellaneous error mask. */
++      data = EDMA_MISC_AXI_RD_ERR_MASK |
++              EDMA_MISC_AXI_WR_ERR_MASK |
++              EDMA_MISC_RX_DESC_FIFO_FULL_MASK |
++              EDMA_MISC_RX_ERR_BUF_SIZE_MASK |
++              EDMA_MISC_TX_SRAM_FULL_MASK |
++              EDMA_MISC_TX_CMPL_BUF_FULL_MASK |
++              EDMA_MISC_DATA_LEN_ERR_MASK;
++      data |= EDMA_MISC_TX_TIMEOUT_MASK;
++      edma_ctx->intr_info.intr_mask_misc = data;
++
++      /* Global EDMA enable and padding enable. */
++      data = EDMA_PORT_PAD_EN | EDMA_PORT_EDMA_EN;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_PORT_CTRL_ADDR;
++      ret = regmap_write(regmap, reg, data);
++      if (ret)
++              return ret;
++
++      /* Initialize unicast priority map table. */
++      ret = (int)edma_configure_ucast_prio_map_tbl();
++      if (ret) {
++              pr_err("Failed to initialize unicast priority map table: %d\n",
++                     ret);
++              kfree(edma_ctx->netdev_arr);
++              return ret;
++      }
++
++      return 0;
++}
++
++/**
++ * edma_destroy - EDMA Destroy.
++ * @ppe_dev: PPE device
++ *
++ * Free the memory allocated during setup.
++ */
++void edma_destroy(struct ppe_device *ppe_dev)
++{
++      kfree(edma_ctx->intr_info.intr_rx);
++      kfree(edma_ctx->intr_info.intr_txcmpl);
++      kfree(edma_ctx->netdev_arr);
++}
++
++/**
++ * edma_setup - EDMA Setup.
++ * @ppe_dev: PPE device
++ *
++ * Configure Ethernet global ctx, clocks, hardware and interrupts.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++int edma_setup(struct ppe_device *ppe_dev)
++{
++      struct device *dev = ppe_dev->dev;
++      int ret;
++
++      edma_ctx = devm_kzalloc(dev, sizeof(*edma_ctx), GFP_KERNEL);
++      if (!edma_ctx)
++              return -ENOMEM;
++
++      edma_ctx->hw_info = &ipq9574_hw_info;
++      edma_ctx->ppe_dev = ppe_dev;
++
++      /* Configure the EDMA common clocks. */
++      ret = edma_clock_init();
++      if (ret) {
++              dev_err(dev, "Error in configuring the EDMA clocks\n");
++              return ret;
++      }
++
++      dev_dbg(dev, "QCOM EDMA common clocks are configured\n");
++
++      ret = edma_hw_configure();
++      if (ret) {
++              dev_err(dev, "Error in edma configuration\n");
++              return ret;
++      }
++
++      ret = edma_irq_init();
++      if (ret) {
++              dev_err(dev, "Error in irq initialization\n");
++              return ret;
++      }
++
++      dev_info(dev, "EDMA configuration successful\n");
++
++      return 0;
++}
++
++/**
++ * ppe_edma_queue_offset_config - Configure queue offset for EDMA interface
++ * @ppe_dev: PPE device
++ * @class: The class to configure queue offset
++ * @index: Class index, internal priority or hash value
++ * @queue_offset: Queue offset value
++ *
++ * PPE EDMA queue offset is configured based on the PPE internal priority or
++ * RSS hash value, the profile ID is fixed to 0 for EDMA interface.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++int ppe_edma_queue_offset_config(struct ppe_device *ppe_dev,
++                               enum ppe_queue_class_type class,
++                               int index, int queue_offset)
++{
++      if (class == PPE_QUEUE_CLASS_PRIORITY)
++              return ppe_queue_ucast_offset_pri_set(ppe_dev, 0,
++                                                    index, queue_offset);
++
++      return ppe_queue_ucast_offset_hash_set(ppe_dev, 0,
++                                             index, queue_offset);
++}
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.h
+@@ -0,0 +1,113 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef __EDMA_MAIN__
++#define __EDMA_MAIN__
++
++#include "ppe_config.h"
++
++/* One clock cycle = 1/(EDMA clock frequency in Mhz) micro seconds.
++ *
++ * One timer unit is 128 clock cycles.
++ *
++ * So, therefore the microsecond to timer unit calculation is:
++ * Timer unit = time in microseconds / (one clock cycle in microsecond * cycles in 1 timer unit)
++ *            = ('x' microsecond * EDMA clock frequency in MHz ('y') / 128).
++ *
++ */
++#define EDMA_CYCLE_PER_TIMER_UNIT     128
++#define EDMA_MICROSEC_TO_TIMER_UNIT(x, y)     ((x) * (y) / EDMA_CYCLE_PER_TIMER_UNIT)
++#define MHZ                   1000000UL
++
++/* EDMA profile ID. */
++#define EDMA_CPU_PORT_PROFILE_ID  0
++
++/* Number of PPE queue priorities supported per ARM core. */
++#define EDMA_PRI_MAX_PER_CORE 8
++
++/**
++ * enum ppe_queue_class_type - PPE queue class type
++ * @PPE_QUEUE_CLASS_PRIORITY: Queue offset configured from internal priority
++ * @PPE_QUEUE_CLASS_HASH: Queue offset configured from RSS hash.
++ */
++enum ppe_queue_class_type {
++      PPE_QUEUE_CLASS_PRIORITY,
++      PPE_QUEUE_CLASS_HASH,
++};
++
++/**
++ * struct edma_ring_info - EDMA ring data structure.
++ * @max_rings: Maximum number of rings
++ * @ring_start: Ring start ID
++ * @num_rings: Number of rings
++ */
++struct edma_ring_info {
++      u32 max_rings;
++      u32 ring_start;
++      u32 num_rings;
++};
++
++/**
++ * struct edma_hw_info - EDMA hardware data structure.
++ * @rxfill: Rx Fill ring information
++ * @rx: Rx Desc ring information
++ * @tx: Tx Desc ring information
++ * @txcmpl: Tx complete ring information
++ * @max_ports: Maximum number of ports
++ * @napi_budget_rx: Rx NAPI budget
++ * @napi_budget_tx: Tx NAPI budget
++ */
++struct edma_hw_info {
++      struct edma_ring_info *rxfill;
++      struct edma_ring_info *rx;
++      struct edma_ring_info *tx;
++      struct edma_ring_info *txcmpl;
++      u32 max_ports;
++      u32 napi_budget_rx;
++      u32 napi_budget_tx;
++};
++
++/**
++ * struct edma_intr_info - EDMA interrupt data structure.
++ * @intr_mask_rx: RX interrupt mask
++ * @intr_rx: Rx interrupts
++ * @intr_mask_txcmpl: Tx completion interrupt mask
++ * @intr_txcmpl: Tx completion interrupts
++ * @intr_mask_misc: Miscellaneous interrupt mask
++ * @intr_misc: Miscellaneous interrupts
++ */
++struct edma_intr_info {
++      u32 intr_mask_rx;
++      u32 *intr_rx;
++      u32 intr_mask_txcmpl;
++      u32 *intr_txcmpl;
++      u32 intr_mask_misc;
++      u32 intr_misc;
++};
++
++/**
++ * struct edma_context - EDMA context.
++ * @netdev_arr: Net device for each EDMA port
++ * @ppe_dev: PPE device
++ * @hw_info: EDMA Hardware info
++ * @intr_info: EDMA Interrupt info
++ */
++struct edma_context {
++      struct net_device **netdev_arr;
++      struct ppe_device *ppe_dev;
++      struct edma_hw_info *hw_info;
++      struct edma_intr_info intr_info;
++};
++
++/* Global EDMA context. */
++extern struct edma_context *edma_ctx;
++
++void edma_destroy(struct ppe_device *ppe_dev);
++int edma_setup(struct ppe_device *ppe_dev);
++int ppe_edma_queue_offset_config(struct ppe_device *ppe_dev,
++                               enum ppe_queue_class_type class,
++                               int index, int queue_offset);
++
++
++#endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe.c
+@@ -14,6 +14,7 @@
+ #include <linux/regmap.h>
+ #include <linux/reset.h>
++#include "edma.h"
+ #include "ppe.h"
+ #include "ppe_config.h"
+ #include "ppe_debugfs.h"
+@@ -201,10 +202,16 @@ static int qcom_ppe_probe(struct platfor
+       if (ret)
+               return dev_err_probe(dev, ret, "PPE HW config failed\n");
+-      ret = ppe_port_mac_init(ppe_dev);
++      ret = edma_setup(ppe_dev);
+       if (ret)
++              return dev_err_probe(dev, ret, "EDMA setup failed\n");
++
++      ret = ppe_port_mac_init(ppe_dev);
++      if (ret) {
++              edma_destroy(ppe_dev);
+               return dev_err_probe(dev, ret,
+                                    "PPE Port MAC initialization failed\n");
++      }
+       ppe_debugfs_setup(ppe_dev);
+       platform_set_drvdata(pdev, ppe_dev);
+@@ -219,6 +226,7 @@ static void qcom_ppe_remove(struct platf
+       ppe_dev = platform_get_drvdata(pdev);
+       ppe_debugfs_teardown(ppe_dev);
+       ppe_port_mac_deinit(ppe_dev);
++      edma_destroy(ppe_dev);
+       platform_set_drvdata(pdev, NULL);
+ }
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -800,4 +800,257 @@
+ #define XGMAC_RXDISCARD_GB_ADDR                       0x9AC
+ #define XGMAC_RXDISCARDBYTE_GB_ADDR           0x9B4
++#define EDMA_BASE_OFFSET                      0xb00000
++
++/* EDMA register offsets */
++#define EDMA_REG_MAS_CTRL_ADDR                        0x0
++#define EDMA_REG_PORT_CTRL_ADDR                       0x4
++#define EDMA_REG_VLAN_CTRL_ADDR                       0x8
++#define EDMA_REG_RXDESC2FILL_MAP_0_ADDR               0x14
++#define EDMA_REG_RXDESC2FILL_MAP_1_ADDR               0x18
++#define EDMA_REG_RXDESC2FILL_MAP_2_ADDR               0x1c
++#define EDMA_REG_TXQ_CTRL_ADDR                        0x20
++#define EDMA_REG_TXQ_CTRL_2_ADDR              0x24
++#define EDMA_REG_TXQ_FC_0_ADDR                        0x28
++#define EDMA_REG_TXQ_FC_1_ADDR                        0x30
++#define EDMA_REG_TXQ_FC_2_ADDR                        0x34
++#define EDMA_REG_TXQ_FC_3_ADDR                        0x38
++#define EDMA_REG_RXQ_CTRL_ADDR                        0x3c
++#define EDMA_REG_MISC_ERR_QID_ADDR            0x40
++#define EDMA_REG_RXQ_FC_THRE_ADDR             0x44
++#define EDMA_REG_DMAR_CTRL_ADDR                       0x48
++#define EDMA_REG_AXIR_CTRL_ADDR                       0x4c
++#define EDMA_REG_AXIW_CTRL_ADDR                       0x50
++#define EDMA_REG_MIN_MSS_ADDR                 0x54
++#define EDMA_REG_LOOPBACK_CTRL_ADDR           0x58
++#define EDMA_REG_MISC_INT_STAT_ADDR           0x5c
++#define EDMA_REG_MISC_INT_MASK_ADDR           0x60
++#define EDMA_REG_DBG_CTRL_ADDR                        0x64
++#define EDMA_REG_DBG_DATA_ADDR                        0x68
++#define EDMA_REG_TX_TIMEOUT_THRESH_ADDR               0x6c
++#define EDMA_REG_REQ0_FIFO_THRESH_ADDR                0x80
++#define EDMA_REG_WB_OS_THRESH_ADDR            0x84
++#define EDMA_REG_MISC_ERR_QID_REG2_ADDR               0x88
++#define EDMA_REG_TXDESC2CMPL_MAP_0_ADDR               0x8c
++#define EDMA_REG_TXDESC2CMPL_MAP_1_ADDR               0x90
++#define EDMA_REG_TXDESC2CMPL_MAP_2_ADDR               0x94
++#define EDMA_REG_TXDESC2CMPL_MAP_3_ADDR               0x98
++#define EDMA_REG_TXDESC2CMPL_MAP_4_ADDR               0x9c
++#define EDMA_REG_TXDESC2CMPL_MAP_5_ADDR               0xa0
++
++/* Tx descriptor ring configuration register addresses */
++#define EDMA_REG_TXDESC_BA(n)         (0x1000 + (0x1000 * (n)))
++#define EDMA_REG_TXDESC_PROD_IDX(n)   (0x1004 + (0x1000 * (n)))
++#define EDMA_REG_TXDESC_CONS_IDX(n)   (0x1008 + (0x1000 * (n)))
++#define EDMA_REG_TXDESC_RING_SIZE(n)  (0x100c + (0x1000 * (n)))
++#define EDMA_REG_TXDESC_CTRL(n)               (0x1010 + (0x1000 * (n)))
++#define EDMA_REG_TXDESC_BA2(n)                (0x1014 + (0x1000 * (n)))
++
++/* RxFill ring configuration register addresses */
++#define EDMA_REG_RXFILL_BA(n)         (0x29000 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_PROD_IDX(n)   (0x29004 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_CONS_IDX(n)   (0x29008 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_RING_SIZE(n)  (0x2900c + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_BUFFER1_SIZE(n)       (0x29010 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_FC_THRE(n)    (0x29014 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_UGT_THRE(n)   (0x29018 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_RING_EN(n)    (0x2901c + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_DISABLE(n)    (0x29020 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_DISABLE_DONE(n)       (0x29024 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_INT_STAT(n)   (0x31000 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_INT_MASK(n)   (0x31004 + (0x1000 * (n)))
++
++/* Rx descriptor ring configuration register addresses */
++#define EDMA_REG_RXDESC_BA(n)         (0x39000 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_PROD_IDX(n)   (0x39004 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_CONS_IDX(n)   (0x39008 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_RING_SIZE(n)  (0x3900c + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_FC_THRE(n)    (0x39010 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_UGT_THRE(n)   (0x39014 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_CTRL(n)               (0x39018 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_BPC(n)                (0x3901c + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_DISABLE(n)    (0x39020 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_DISABLE_DONE(n)       (0x39024 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_PREHEADER_BA(n)       (0x39028 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_INT_STAT(n)   (0x59000 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_INT_MASK(n)   (0x59004 + (0x1000 * (n)))
++
++#define EDMA_REG_RX_MOD_TIMER(n)      (0x59008 + (0x1000 * (n)))
++#define EDMA_REG_RX_INT_CTRL(n)               (0x5900c + (0x1000 * (n)))
++
++/* Tx completion ring configuration register addresses */
++#define EDMA_REG_TXCMPL_BA(n)         (0x79000 + (0x1000 * (n)))
++#define EDMA_REG_TXCMPL_PROD_IDX(n)   (0x79004 + (0x1000 * (n)))
++#define EDMA_REG_TXCMPL_CONS_IDX(n)   (0x79008 + (0x1000 * (n)))
++#define EDMA_REG_TXCMPL_RING_SIZE(n)  (0x7900c + (0x1000 * (n)))
++#define EDMA_REG_TXCMPL_UGT_THRE(n)   (0x79010 + (0x1000 * (n)))
++#define EDMA_REG_TXCMPL_CTRL(n)               (0x79014 + (0x1000 * (n)))
++#define EDMA_REG_TXCMPL_BPC(n)                (0x79018 + (0x1000 * (n)))
++
++#define EDMA_REG_TX_INT_STAT(n)               (0x99000 + (0x1000 * (n)))
++#define EDMA_REG_TX_INT_MASK(n)               (0x99004 + (0x1000 * (n)))
++#define EDMA_REG_TX_MOD_TIMER(n)      (0x99008 + (0x1000 * (n)))
++#define EDMA_REG_TX_INT_CTRL(n)               (0x9900c + (0x1000 * (n)))
++
++/* EDMA_QID2RID_TABLE_MEM register field masks */
++#define EDMA_RX_RING_ID_QUEUE0_MASK   GENMASK(7, 0)
++#define EDMA_RX_RING_ID_QUEUE1_MASK   GENMASK(15, 8)
++#define EDMA_RX_RING_ID_QUEUE2_MASK   GENMASK(23, 16)
++#define EDMA_RX_RING_ID_QUEUE3_MASK   GENMASK(31, 24)
++
++/* EDMA_REG_PORT_CTRL register bit definitions */
++#define EDMA_PORT_PAD_EN                      0x1
++#define EDMA_PORT_EDMA_EN                     0x2
++
++/* EDMA_REG_DMAR_CTRL register field masks */
++#define EDMA_DMAR_REQ_PRI_MASK                        GENMASK(2, 0)
++#define EDMA_DMAR_BURST_LEN_MASK              BIT(3)
++#define EDMA_DMAR_TXDATA_OUTSTANDING_NUM_MASK GENMASK(8, 4)
++#define EDMA_DMAR_TXDESC_OUTSTANDING_NUM_MASK GENMASK(11, 9)
++#define EDMA_DMAR_RXFILL_OUTSTANDING_NUM_MASK GENMASK(14, 12)
++
++#define EDMA_BURST_LEN_ENABLE                 0
++
++/* Tx timeout threshold */
++#define EDMA_TX_TIMEOUT_THRESH_VAL            0xFFFF
++
++/* Rx descriptor ring base address mask */
++#define EDMA_RXDESC_BA_MASK                   0xffffffff
++
++/* Rx Descriptor ring pre-header base address mask */
++#define EDMA_RXDESC_PREHEADER_BA_MASK         0xffffffff
++
++/* Tx descriptor prod ring index mask */
++#define EDMA_TXDESC_PROD_IDX_MASK             0xffff
++
++/* Tx descriptor consumer ring index mask */
++#define EDMA_TXDESC_CONS_IDX_MASK             0xffff
++
++/* Tx descriptor ring size mask */
++#define EDMA_TXDESC_RING_SIZE_MASK            0xffff
++
++/* Tx descriptor ring enable */
++#define EDMA_TXDESC_TX_ENABLE                 0x1
++
++#define EDMA_TXDESC_CTRL_TXEN_MASK            BIT(0)
++#define EDMA_TXDESC_CTRL_FC_GRP_ID_MASK               GENMASK(3, 1)
++
++/* Tx completion ring prod index mask */
++#define EDMA_TXCMPL_PROD_IDX_MASK             0xffff
++
++/* Tx completion ring urgent threshold mask */
++#define EDMA_TXCMPL_LOW_THRE_MASK             0xffff
++#define EDMA_TXCMPL_LOW_THRE_SHIFT            0
++
++/* EDMA_REG_TX_MOD_TIMER mask */
++#define EDMA_TX_MOD_TIMER_INIT_MASK           0xffff
++#define EDMA_TX_MOD_TIMER_INIT_SHIFT          0
++
++/* Rx fill ring prod index mask */
++#define EDMA_RXFILL_PROD_IDX_MASK             0xffff
++
++/* Rx fill ring consumer index mask */
++#define EDMA_RXFILL_CONS_IDX_MASK             0xffff
++
++/* Rx fill ring size mask */
++#define EDMA_RXFILL_RING_SIZE_MASK            0xffff
++
++/* Rx fill ring flow control threshold masks */
++#define EDMA_RXFILL_FC_XON_THRE_MASK          0x7ff
++#define EDMA_RXFILL_FC_XON_THRE_SHIFT         12
++#define EDMA_RXFILL_FC_XOFF_THRE_MASK         0x7ff
++#define EDMA_RXFILL_FC_XOFF_THRE_SHIFT                0
++
++/* Rx fill ring enable bit */
++#define EDMA_RXFILL_RING_EN                   0x1
++
++/* Rx desc ring prod index mask */
++#define EDMA_RXDESC_PROD_IDX_MASK             0xffff
++
++/* Rx descriptor ring cons index mask */
++#define EDMA_RXDESC_CONS_IDX_MASK             0xffff
++
++/* Rx descriptor ring size masks */
++#define EDMA_RXDESC_RING_SIZE_MASK            0xffff
++#define EDMA_RXDESC_PL_OFFSET_MASK            0x1ff
++#define EDMA_RXDESC_PL_OFFSET_SHIFT           16
++#define EDMA_RXDESC_PL_DEFAULT_VALUE          0
++
++/* Rx descriptor ring flow control threshold masks */
++#define EDMA_RXDESC_FC_XON_THRE_MASK          0x7ff
++#define EDMA_RXDESC_FC_XON_THRE_SHIFT         12
++#define EDMA_RXDESC_FC_XOFF_THRE_MASK         0x7ff
++#define EDMA_RXDESC_FC_XOFF_THRE_SHIFT                0
++
++/* Rx descriptor ring urgent threshold mask */
++#define EDMA_RXDESC_LOW_THRE_MASK             0xffff
++#define EDMA_RXDESC_LOW_THRE_SHIFT            0
++
++/* Rx descriptor ring enable bit */
++#define EDMA_RXDESC_RX_EN                     0x1
++
++/* Tx interrupt status bit */
++#define EDMA_TX_INT_MASK_PKT_INT              0x1
++
++/* Rx interrupt mask */
++#define EDMA_RXDESC_INT_MASK_PKT_INT          0x1
++
++#define EDMA_MASK_INT_DISABLE                 0x0
++#define EDMA_MASK_INT_CLEAR                   0x0
++
++/* EDMA_REG_RX_MOD_TIMER register field masks */
++#define EDMA_RX_MOD_TIMER_INIT_MASK           0xffff
++#define EDMA_RX_MOD_TIMER_INIT_SHIFT          0
++
++/* EDMA Ring mask */
++#define EDMA_RING_DMA_MASK                    0xffffffff
++
++/* RXDESC threshold interrupt. */
++#define EDMA_RXDESC_UGT_INT_STAT              0x2
++
++/* RXDESC timer interrupt */
++#define EDMA_RXDESC_PKT_INT_STAT              0x1
++
++/* RXDESC Interrupt status mask */
++#define EDMA_RXDESC_RING_INT_STATUS_MASK \
++      (EDMA_RXDESC_UGT_INT_STAT | EDMA_RXDESC_PKT_INT_STAT)
++
++/* TXCMPL threshold interrupt. */
++#define EDMA_TXCMPL_UGT_INT_STAT              0x2
++
++/* TXCMPL timer interrupt */
++#define EDMA_TXCMPL_PKT_INT_STAT              0x1
++
++/* TXCMPL Interrupt status mask */
++#define EDMA_TXCMPL_RING_INT_STATUS_MASK \
++      (EDMA_TXCMPL_UGT_INT_STAT | EDMA_TXCMPL_PKT_INT_STAT)
++
++#define EDMA_TXCMPL_RETMODE_OPAQUE            0x0
++
++#define EDMA_RXDESC_LOW_THRE                  0
++#define EDMA_RX_MOD_TIMER_INIT                        1000
++#define EDMA_RX_NE_INT_EN                     0x2
++
++#define EDMA_TX_MOD_TIMER                     150
++
++#define EDMA_TX_INITIAL_PROD_IDX              0x0
++#define EDMA_TX_NE_INT_EN                     0x2
++
++/* EDMA misc error mask */
++#define EDMA_MISC_AXI_RD_ERR_MASK             BIT(0)
++#define EDMA_MISC_AXI_WR_ERR_MASK             BIT(1)
++#define EDMA_MISC_RX_DESC_FIFO_FULL_MASK      BIT(2)
++#define EDMA_MISC_RX_ERR_BUF_SIZE_MASK                BIT(3)
++#define EDMA_MISC_TX_SRAM_FULL_MASK           BIT(4)
++#define EDMA_MISC_TX_CMPL_BUF_FULL_MASK               BIT(5)
++
++#define EDMA_MISC_DATA_LEN_ERR_MASK           BIT(6)
++#define EDMA_MISC_TX_TIMEOUT_MASK             BIT(7)
++
++/* EDMA txdesc2cmpl map */
++#define EDMA_TXDESC2CMPL_MAP_TXDESC_MASK              0x1F
++
++/* EDMA rxdesc2fill map */
++#define EDMA_RXDESC2FILL_MAP_RXDESC_MASK      0x7
++
+ #endif
diff --git a/target/linux/qualcommbe/patches-6.18/0343-net-ethernet-qualcomm-Add-netdevice-support-for-QCOM.patch b/target/linux/qualcommbe/patches-6.18/0343-net-ethernet-qualcomm-Add-netdevice-support-for-QCOM.patch
new file mode 100644 (file)
index 0000000..55de486
--- /dev/null
@@ -0,0 +1,397 @@
+From 5dc80c468c668d855d76b323f09bbadb95cc3147 Mon Sep 17 00:00:00 2001
+From: Suruchi Agarwal <quic_suruchia@quicinc.com>
+Date: Thu, 21 Mar 2024 16:14:46 -0700
+Subject: [PATCH] net: ethernet: qualcomm: Add netdevice support for QCOM
+ IPQ9574 chipset.
+
+Add EDMA ports and netdevice operations for QCOM IPQ9574 chipset.
+
+Change-Id: I08b2eff52b4ef0d6d428c1c416f5580ef010973f
+Co-developed-by: Pavithra R <quic_pavir@quicinc.com>
+Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
+Signed-off-by: Suruchi Agarwal <quic_suruchia@quicinc.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/Makefile    |   2 +-
+ drivers/net/ethernet/qualcomm/ppe/edma.h      |   3 +
+ drivers/net/ethernet/qualcomm/ppe/edma_port.c | 270 ++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/edma_port.h |  31 ++
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.c  |  19 ++
+ 5 files changed, 324 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_port.c
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_port.h
+
+--- a/drivers/net/ethernet/qualcomm/ppe/Makefile
++++ b/drivers/net/ethernet/qualcomm/ppe/Makefile
+@@ -7,4 +7,4 @@ obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o
+ qcom-ppe-objs := ppe.o ppe_config.o ppe_debugfs.o ppe_port.o
+ #EDMA
+-qcom-ppe-objs += edma.o
++qcom-ppe-objs += edma.o edma_port.o
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.h
+@@ -26,6 +26,9 @@
+ /* Number of PPE queue priorities supported per ARM core. */
+ #define EDMA_PRI_MAX_PER_CORE 8
++/* Interface ID start. */
++#define EDMA_START_IFNUM   1
++
+ /**
+  * enum ppe_queue_class_type - PPE queue class type
+  * @PPE_QUEUE_CLASS_PRIORITY: Queue offset configured from internal priority
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.c
+@@ -0,0 +1,270 @@
++// SPDX-License-Identifier: GPL-2.0-only
++ /* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++  */
++
++/* EDMA port initialization, configuration and netdevice ops handling */
++
++#include <linux/etherdevice.h>
++#include <linux/net.h>
++#include <linux/netdevice.h>
++#include <linux/of_net.h>
++#include <linux/phylink.h>
++#include <linux/printk.h>
++
++#include "edma.h"
++#include "edma_port.h"
++#include "ppe_regs.h"
++
++/* Number of netdev queues. */
++#define EDMA_NETDEV_QUEUE_NUM 4
++
++static u16 __maybe_unused edma_port_select_queue(__maybe_unused struct net_device *netdev,
++                                               __maybe_unused struct sk_buff *skb,
++                              __maybe_unused struct net_device *sb_dev)
++{
++      int cpu = get_cpu();
++
++      put_cpu();
++
++      return cpu;
++}
++
++static int edma_port_open(struct net_device *netdev)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *ppe_port;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      /* Inform the Linux Networking stack about the hardware capability of
++       * checksum offloading and other features. Each port is
++       * responsible to maintain the feature set it supports.
++       */
++      netdev->features |= EDMA_NETDEV_FEATURES;
++      netdev->hw_features |= EDMA_NETDEV_FEATURES;
++      netdev->vlan_features |= EDMA_NETDEV_FEATURES;
++      netdev->wanted_features |= EDMA_NETDEV_FEATURES;
++
++      ppe_port  = port_priv->ppe_port;
++
++      if (ppe_port->phylink)
++              phylink_start(ppe_port->phylink);
++
++      netif_start_queue(netdev);
++
++      return 0;
++}
++
++static int edma_port_close(struct net_device *netdev)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *ppe_port;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      netif_stop_queue(netdev);
++
++      ppe_port  = port_priv->ppe_port;
++
++      /* Phylink close. */
++      if (ppe_port->phylink)
++              phylink_stop(ppe_port->phylink);
++
++      return 0;
++}
++
++static int edma_port_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *ppe_port;
++      int ret = -EINVAL;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      ppe_port = port_priv->ppe_port;
++      if (ppe_port->phylink)
++              return phylink_mii_ioctl(ppe_port->phylink, ifr, cmd);
++
++      return ret;
++}
++
++static int edma_port_change_mtu(struct net_device *netdev, int mtu)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++
++      if (!port_priv)
++              return -EINVAL;
++
++      netdev->mtu = mtu;
++
++      return ppe_port_set_maxframe(port_priv->ppe_port, mtu);
++}
++
++static netdev_features_t edma_port_feature_check(__maybe_unused struct sk_buff *skb,
++                                               __maybe_unused struct net_device *netdev,
++                                               netdev_features_t features)
++{
++      return features;
++}
++
++static void edma_port_get_stats64(struct net_device *netdev,
++                                struct rtnl_link_stats64 *stats)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++
++      if (!port_priv)
++              return;
++
++      ppe_port_get_stats64(port_priv->ppe_port, stats);
++}
++
++static int edma_port_set_mac_address(struct net_device *netdev, void *macaddr)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct sockaddr *addr = (struct sockaddr *)macaddr;
++      int ret;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      netdev_dbg(netdev, "AddrFamily: %d, %0x:%0x:%0x:%0x:%0x:%0x\n",
++                 addr->sa_family, addr->sa_data[0], addr->sa_data[1],
++                 addr->sa_data[2], addr->sa_data[3], addr->sa_data[4],
++                 addr->sa_data[5]);
++
++      ret = eth_prepare_mac_addr_change(netdev, addr);
++      if (ret)
++              return ret;
++
++      if (ppe_port_set_mac_address(port_priv->ppe_port, (u8 *)addr)) {
++              netdev_err(netdev, "set mac address failed for dev: %s\n", netdev->name);
++              return -EINVAL;
++      }
++
++      eth_commit_mac_addr_change(netdev, addr);
++
++      return 0;
++}
++
++static const struct net_device_ops edma_port_netdev_ops = {
++      .ndo_open = edma_port_open,
++      .ndo_stop = edma_port_close,
++      .ndo_get_stats64 = edma_port_get_stats64,
++      .ndo_set_mac_address = edma_port_set_mac_address,
++      .ndo_validate_addr = eth_validate_addr,
++      .ndo_change_mtu = edma_port_change_mtu,
++      .ndo_eth_ioctl = edma_port_ioctl,
++      .ndo_features_check = edma_port_feature_check,
++      .ndo_select_queue = edma_port_select_queue,
++};
++
++/**
++ * edma_port_destroy - EDMA port destroy.
++ * @port: PPE port
++ *
++ * Unregister and free the netdevice.
++ */
++void edma_port_destroy(struct ppe_port *port)
++{
++      int port_id = port->port_id;
++      struct net_device *netdev = edma_ctx->netdev_arr[port_id - 1];
++
++      unregister_netdev(netdev);
++      free_netdev(netdev);
++      ppe_port_phylink_destroy(port);
++      edma_ctx->netdev_arr[port_id - 1] = NULL;
++}
++
++/**
++ * edma_port_setup - EDMA port Setup.
++ * @port: PPE port
++ *
++ * Initialize and register the netdevice.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++int edma_port_setup(struct ppe_port *port)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device_node *np = port->np;
++      struct edma_port_priv *port_priv;
++      int port_id = port->port_id;
++      struct net_device *netdev;
++      u8 mac_addr[ETH_ALEN];
++      int ret = 0;
++      u8 *maddr;
++
++      netdev = alloc_etherdev_mqs(sizeof(struct edma_port_priv),
++                                  EDMA_NETDEV_QUEUE_NUM, EDMA_NETDEV_QUEUE_NUM);
++      if (!netdev) {
++              pr_err("alloc_etherdev() failed\n");
++              return -ENOMEM;
++      }
++
++      SET_NETDEV_DEV(netdev, ppe_dev->dev);
++      netdev->dev.of_node = np;
++
++      /* max_mtu is set to 1500 in ether_setup(). */
++      netdev->max_mtu = ETH_MAX_MTU;
++
++      port_priv = netdev_priv(netdev);
++      memset((void *)port_priv, 0, sizeof(struct edma_port_priv));
++
++      port_priv->ppe_port = port;
++      port_priv->netdev = netdev;
++      netdev->watchdog_timeo = 5 * HZ;
++      netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
++      netdev->netdev_ops = &edma_port_netdev_ops;
++      netdev->gso_max_segs = GSO_MAX_SEGS;
++
++      maddr = mac_addr;
++      if (of_get_mac_address(np, maddr))
++              maddr = NULL;
++
++      if (maddr && is_valid_ether_addr(maddr)) {
++              eth_hw_addr_set(netdev, maddr);
++      } else {
++              eth_hw_addr_random(netdev);
++              netdev_info(netdev, "GMAC%d Using random MAC address - %pM\n",
++                          port_id, netdev->dev_addr);
++      }
++
++      netdev_dbg(netdev, "Configuring the port %s(qcom-id:%d)\n",
++                 netdev->name, port_id);
++
++      /* We expect 'port_id' to correspond to ports numbers on SoC.
++       * These begin from '1' and hence we subtract
++       * one when using it as an array index.
++       */
++      edma_ctx->netdev_arr[port_id - 1] = netdev;
++
++      /* Setup phylink. */
++      ret = ppe_port_phylink_setup(port, netdev);
++      if (ret) {
++              netdev_dbg(netdev, "EDMA port phylink setup for netdevice %s\n",
++                         netdev->name);
++              goto port_phylink_setup_fail;
++      }
++
++      /* Register the network interface. */
++      ret = register_netdev(netdev);
++      if (ret) {
++              netdev_dbg(netdev, "Error registering netdevice %s\n",
++                         netdev->name);
++              goto register_netdev_fail;
++      }
++
++      netdev_dbg(netdev, "Setup EDMA port GMAC%d done\n", port_id);
++      return ret;
++
++register_netdev_fail:
++      ppe_port_phylink_destroy(port);
++port_phylink_setup_fail:
++      free_netdev(netdev);
++      edma_ctx->netdev_arr[port_id - 1] = NULL;
++
++      return ret;
++}
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.h
+@@ -0,0 +1,31 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef __EDMA_PORTS__
++#define __EDMA_PORTS__
++
++#include "ppe_port.h"
++
++#define EDMA_NETDEV_FEATURES          (NETIF_F_FRAGLIST \
++                                      | NETIF_F_SG \
++                                      | NETIF_F_RXCSUM \
++                                      | NETIF_F_HW_CSUM \
++                                      | NETIF_F_TSO \
++                                      | NETIF_F_TSO6)
++
++/**
++ * struct edma_port_priv - EDMA port priv structure.
++ * @ppe_port: Pointer to PPE port
++ * @netdev: Corresponding netdevice
++ * @flags: Feature flags
++ */
++struct edma_port_priv {
++      struct ppe_port *ppe_port;
++      struct net_device *netdev;
++      unsigned long flags;
++};
++
++void edma_port_destroy(struct ppe_port *port);
++int edma_port_setup(struct ppe_port *port);
++#endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
+@@ -13,6 +13,7 @@
+ #include <linux/regmap.h>
+ #include <linux/rtnetlink.h>
++#include "edma_port.h"
+ #include "ppe.h"
+ #include "ppe_port.h"
+ #include "ppe_regs.h"
+@@ -1277,12 +1278,26 @@ int ppe_port_mac_init(struct ppe_device
+                       goto err_port_node;
+               }
++              ret = edma_port_setup(&ppe_ports->port[i]);
++              if (ret) {
++                      dev_err(ppe_dev->dev, "QCOM EDMA port setup failed\n");
++                      i--;
++                      goto err_port_setup;
++              }
++
+               i++;
+       }
+       of_node_put(ports_node);
+       return 0;
++err_port_setup:
++      /* Destroy edma ports created till now */
++      while (i >= 0) {
++              edma_port_destroy(&ppe_ports->port[i]);
++              i--;
++      }
++
+ err_port_clk:
+       for (j = 0; j < i; j++)
+               ppe_port_clock_deinit(&ppe_ports->port[j]);
+@@ -1307,6 +1322,10 @@ void ppe_port_mac_deinit(struct ppe_devi
+       for (i = 0; i < ppe_dev->ports->num; i++) {
+               ppe_port = &ppe_dev->ports->port[i];
++
++              /* Destroy all phylinks and edma ports */
++              edma_port_destroy(ppe_port);
++
+               ppe_port_clock_deinit(ppe_port);
+       }
+ }
diff --git a/target/linux/qualcommbe/patches-6.18/0344-net-ethernet-qualcomm-Add-Rx-Ethernet-DMA-support.patch b/target/linux/qualcommbe/patches-6.18/0344-net-ethernet-qualcomm-Add-Rx-Ethernet-DMA-support.patch
new file mode 100644 (file)
index 0000000..eb10a00
--- /dev/null
@@ -0,0 +1,2454 @@
+From b5c8c5d3888328321e8be1db50b75dff8f514e51 Mon Sep 17 00:00:00 2001
+From: Suruchi Agarwal <quic_suruchia@quicinc.com>
+Date: Thu, 21 Mar 2024 16:21:19 -0700
+Subject: [PATCH] net: ethernet: qualcomm: Add Rx Ethernet DMA support
+
+Add Rx queues, rings, descriptors configurations and
+DMA support for the EDMA.
+
+Change-Id: I612bcd661e74d5bf3ecb33de10fd5298d18ff7e9
+Co-developed-by: Pavithra R <quic_pavir@quicinc.com>
+Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
+Signed-off-by: Suruchi Agarwal <quic_suruchia@quicinc.com>
+Alex G: add missing functions that were previously in ppe_api.c:
+        - ppe_edma_queue_resource_get()
+        - ppe_edma_ring_to_queues_config()
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/Makefile    |   2 +-
+ drivers/net/ethernet/qualcomm/ppe/edma.c      | 214 +++-
+ drivers/net/ethernet/qualcomm/ppe/edma.h      |  22 +-
+ .../net/ethernet/qualcomm/ppe/edma_cfg_rx.c   | 964 ++++++++++++++++++
+ .../net/ethernet/qualcomm/ppe/edma_cfg_rx.h   |  48 +
+ drivers/net/ethernet/qualcomm/ppe/edma_port.c |  39 +-
+ drivers/net/ethernet/qualcomm/ppe/edma_port.h |  31 +
+ drivers/net/ethernet/qualcomm/ppe/edma_rx.c   | 622 +++++++++++
+ drivers/net/ethernet/qualcomm/ppe/edma_rx.h   | 287 ++++++
+ 9 files changed, 2224 insertions(+), 5 deletions(-)
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.c
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.h
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_rx.c
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_rx.h
+
+--- a/drivers/net/ethernet/qualcomm/ppe/Makefile
++++ b/drivers/net/ethernet/qualcomm/ppe/Makefile
+@@ -7,4 +7,4 @@ obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o
+ qcom-ppe-objs := ppe.o ppe_config.o ppe_debugfs.o ppe_port.o
+ #EDMA
+-qcom-ppe-objs += edma.o edma_port.o
++qcom-ppe-objs += edma.o edma_cfg_rx.o edma_port.o edma_rx.o
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.c
+@@ -18,12 +18,23 @@
+ #include <linux/reset.h>
+ #include "edma.h"
++#include "edma_cfg_rx.h"
+ #include "ppe_regs.h"
+ #define EDMA_IRQ_NAME_SIZE            32
+ /* Global EDMA context. */
+ struct edma_context *edma_ctx;
++static char **edma_rxdesc_irq_name;
++
++/* Module params. */
++static int page_mode;
++module_param(page_mode, int, 0);
++MODULE_PARM_DESC(page_mode, "Enable page mode (default:0)");
++
++static int rx_buff_size;
++module_param(rx_buff_size, int, 0640);
++MODULE_PARM_DESC(rx_buff_size, "Rx Buffer size for Jumbo MRU value (default:0)");
+ /* Priority to multi-queue mapping. */
+ static u8 edma_pri_map[PPE_QUEUE_INTER_PRI_NUM] = {
+@@ -178,6 +189,59 @@ static int edma_configure_ucast_prio_map
+       return ret;
+ }
++static int edma_irq_register(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rx = hw_info->rx;
++      int ret;
++      u32 i;
++
++      /* Request IRQ for RXDESC rings. */
++      edma_rxdesc_irq_name = kzalloc((sizeof(char *) * rx->num_rings),
++                                     GFP_KERNEL);
++      if (!edma_rxdesc_irq_name)
++              return -ENOMEM;
++
++      for (i = 0; i < rx->num_rings; i++) {
++              edma_rxdesc_irq_name[i] = kzalloc((sizeof(char *) * EDMA_IRQ_NAME_SIZE),
++                                                GFP_KERNEL);
++              if (!edma_rxdesc_irq_name[i]) {
++                      ret = -ENOMEM;
++                      goto rxdesc_irq_name_alloc_fail;
++              }
++
++              snprintf(edma_rxdesc_irq_name[i], 20, "edma_rxdesc_%d",
++                       rx->ring_start + i);
++
++              irq_set_status_flags(edma_ctx->intr_info.intr_rx[i], IRQ_DISABLE_UNLAZY);
++
++              ret = request_irq(edma_ctx->intr_info.intr_rx[i],
++                                edma_rx_handle_irq, IRQF_SHARED,
++                                edma_rxdesc_irq_name[i],
++                                (void *)&edma_ctx->rx_rings[i]);
++              if (ret) {
++                      pr_err("RXDESC ring IRQ:%d request failed\n",
++                             edma_ctx->intr_info.intr_rx[i]);
++                      goto rx_desc_ring_intr_req_fail;
++              }
++
++              pr_debug("RXDESC ring: %d IRQ:%d request success: %s\n",
++                       rx->ring_start + i,
++                       edma_ctx->intr_info.intr_rx[i],
++                       edma_rxdesc_irq_name[i]);
++      }
++
++      return 0;
++
++rx_desc_ring_intr_req_fail:
++      for (i = 0; i < rx->num_rings; i++)
++              kfree(edma_rxdesc_irq_name[i]);
++rxdesc_irq_name_alloc_fail:
++      kfree(edma_rxdesc_irq_name);
++
++      return ret;
++}
++
+ static int edma_irq_init(void)
+ {
+       struct edma_hw_info *hw_info = edma_ctx->hw_info;
+@@ -260,6 +324,16 @@ static int edma_irq_init(void)
+       return 0;
+ }
++static int edma_alloc_rings(void)
++{
++      if (edma_cfg_rx_rings_alloc()) {
++              pr_err("Error in allocating Rx rings\n");
++              return -ENOMEM;
++      }
++
++      return 0;
++}
++
+ static int edma_hw_reset(void)
+ {
+       struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+@@ -343,6 +417,40 @@ static int edma_hw_configure(void)
+       if (!edma_ctx->netdev_arr)
+               return -ENOMEM;
++      edma_ctx->dummy_dev = alloc_netdev_dummy(0);
++      if (!edma_ctx->dummy_dev) {
++              ret = -ENOMEM;
++              pr_err("Failed to allocate dummy device. ret: %d\n", ret);
++              goto dummy_dev_alloc_failed;
++      }
++
++      /* Set EDMA jumbo MRU if enabled or set page mode. */
++      if (edma_ctx->rx_buf_size) {
++              edma_ctx->rx_page_mode = false;
++              pr_debug("Rx Jumbo mru is enabled: %d\n", edma_ctx->rx_buf_size);
++      } else {
++              edma_ctx->rx_page_mode = page_mode;
++      }
++
++      ret = edma_alloc_rings();
++      if (ret) {
++              pr_err("Error in initializaing the rings. ret: %d\n", ret);
++              goto edma_alloc_rings_failed;
++      }
++
++      /* Disable interrupts. */
++      edma_cfg_rx_disable_interrupts();
++
++      edma_cfg_rx_rings_disable();
++
++      edma_cfg_rx_ring_mappings();
++
++      ret = edma_cfg_rx_rings();
++      if (ret) {
++              pr_err("Error in configuring Rx rings. ret: %d\n", ret);
++              goto edma_cfg_rx_rings_failed;
++      }
++
+       /* Configure DMA request priority, DMA read burst length,
+        * and AXI write size.
+        */
+@@ -376,6 +484,10 @@ static int edma_hw_configure(void)
+       data |= EDMA_MISC_TX_TIMEOUT_MASK;
+       edma_ctx->intr_info.intr_mask_misc = data;
++      edma_cfg_rx_rings_enable();
++      edma_cfg_rx_napi_add();
++      edma_cfg_rx_napi_enable();
++
+       /* Global EDMA enable and padding enable. */
+       data = EDMA_PORT_PAD_EN | EDMA_PORT_EDMA_EN;
+@@ -389,11 +501,32 @@ static int edma_hw_configure(void)
+       if (ret) {
+               pr_err("Failed to initialize unicast priority map table: %d\n",
+                      ret);
+-              kfree(edma_ctx->netdev_arr);
+-              return ret;
++              goto configure_ucast_prio_map_tbl_failed;
++      }
++
++      /* Initialize RPS hash map table. */
++      ret = edma_cfg_rx_rps_hash_map();
++      if (ret) {
++              pr_err("Failed to configure rps hash table: %d\n",
++                     ret);
++              goto edma_cfg_rx_rps_hash_map_failed;
+       }
+       return 0;
++
++edma_cfg_rx_rps_hash_map_failed:
++configure_ucast_prio_map_tbl_failed:
++      edma_cfg_rx_napi_disable();
++      edma_cfg_rx_napi_delete();
++      edma_cfg_rx_rings_disable();
++edma_cfg_rx_rings_failed:
++      edma_cfg_rx_rings_cleanup();
++edma_alloc_rings_failed:
++      free_netdev(edma_ctx->dummy_dev);
++dummy_dev_alloc_failed:
++      kfree(edma_ctx->netdev_arr);
++
++      return ret;
+ }
+ /**
+@@ -404,8 +537,31 @@ static int edma_hw_configure(void)
+  */
+ void edma_destroy(struct ppe_device *ppe_dev)
+ {
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 i;
++
++      /* Disable interrupts. */
++      edma_cfg_rx_disable_interrupts();
++
++      /* Free IRQ for RXDESC rings. */
++      for (i = 0; i < rx->num_rings; i++) {
++              synchronize_irq(edma_ctx->intr_info.intr_rx[i]);
++              free_irq(edma_ctx->intr_info.intr_rx[i],
++                       (void *)&edma_ctx->rx_rings[i]);
++              kfree(edma_rxdesc_irq_name[i]);
++      }
++      kfree(edma_rxdesc_irq_name);
++
+       kfree(edma_ctx->intr_info.intr_rx);
+       kfree(edma_ctx->intr_info.intr_txcmpl);
++
++      edma_cfg_rx_napi_disable();
++      edma_cfg_rx_napi_delete();
++      edma_cfg_rx_rings_disable();
++      edma_cfg_rx_rings_cleanup();
++
++      free_netdev(edma_ctx->dummy_dev);
+       kfree(edma_ctx->netdev_arr);
+ }
+@@ -428,6 +584,7 @@ int edma_setup(struct ppe_device *ppe_de
+       edma_ctx->hw_info = &ipq9574_hw_info;
+       edma_ctx->ppe_dev = ppe_dev;
++      edma_ctx->rx_buf_size = rx_buff_size;
+       /* Configure the EDMA common clocks. */
+       ret = edma_clock_init();
+@@ -450,6 +607,16 @@ int edma_setup(struct ppe_device *ppe_de
+               return ret;
+       }
++      ret = edma_irq_register();
++      if (ret) {
++              dev_err(dev, "Error in irq registration\n");
++              kfree(edma_ctx->intr_info.intr_rx);
++              kfree(edma_ctx->intr_info.intr_txcmpl);
++              return ret;
++      }
++
++      edma_cfg_rx_enable_interrupts();
++
+       dev_info(dev, "EDMA configuration successful\n");
+       return 0;
+@@ -478,3 +645,46 @@ int ppe_edma_queue_offset_config(struct
+       return ppe_queue_ucast_offset_hash_set(ppe_dev, 0,
+                                              index, queue_offset);
+ }
++
++/**
++ * ppe_edma_queue_resource_get - Get EDMA queue resource
++ * @ppe_dev: PPE device
++ * @type: Resource type
++ * @res_start: Resource start ID returned
++ * @res_end: Resource end ID returned
++ *
++ * PPE EDMA queue resource includes unicast queue and multicast queue.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++int ppe_edma_queue_resource_get(struct ppe_device *ppe_dev, int type,
++                              int *res_start, int *res_end)
++{
++      if (type != PPE_RES_UCAST && type != PPE_RES_MCAST)
++              return -EINVAL;
++
++      return ppe_port_resource_get(ppe_dev, 0, type, res_start, res_end);
++};
++
++/**
++ * ppe_edma_ring_to_queues_config - Map EDMA ring to PPE queues
++ * @ppe_dev: PPE device
++ * @ring_id: EDMA ring ID
++ * @num: Number of queues mapped to EDMA ring
++ * @queues: PPE queue IDs
++ *
++ * PPE queues are configured to map with the special EDMA ring ID.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++int ppe_edma_ring_to_queues_config(struct ppe_device *ppe_dev, int ring_id,
++                                 int num, int queues[] __counted_by(num))
++{
++      u32 queue_bmap[PPE_RING_TO_QUEUE_BITMAP_WORD_CNT] = {};
++      int index;
++
++      for (index = 0; index < num; index++)
++              queue_bmap[queues[index] / 32] |= BIT_MASK(queues[index] % 32);
++
++      return ppe_ring_queue_map_set(ppe_dev, ring_id, queue_bmap);
++}
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.h
+@@ -6,6 +6,7 @@
+ #define __EDMA_MAIN__
+ #include "ppe_config.h"
++#include "edma_rx.h"
+ /* One clock cycle = 1/(EDMA clock frequency in Mhz) micro seconds.
+  *
+@@ -29,6 +30,11 @@
+ /* Interface ID start. */
+ #define EDMA_START_IFNUM   1
++#define EDMA_DESC_AVAIL_COUNT(head, tail, _max) ({ \
++                      typeof(_max) (max) = (_max); \
++                      ((((head) - (tail)) + \
++                      (max)) & ((max) - 1)); })
++
+ /**
+  * enum ppe_queue_class_type - PPE queue class type
+  * @PPE_QUEUE_CLASS_PRIORITY: Queue offset configured from internal priority
+@@ -92,18 +98,28 @@ struct edma_intr_info {
+ /**
+  * struct edma_context - EDMA context.
+  * @netdev_arr: Net device for each EDMA port
++ * @dummy_dev: Dummy netdevice for RX DMA
+  * @ppe_dev: PPE device
+  * @hw_info: EDMA Hardware info
+  * @intr_info: EDMA Interrupt info
++ * @rxfill_rings: Rx fill Rings, SW is producer
++ * @rx_rings: Rx Desc Rings, SW is consumer
++ * @rx_page_mode: Page mode enabled or disabled
++ * @rx_buf_size: Rx buffer size for Jumbo MRU
+  */
+ struct edma_context {
+       struct net_device **netdev_arr;
++      struct net_device *dummy_dev;
+       struct ppe_device *ppe_dev;
+       struct edma_hw_info *hw_info;
+       struct edma_intr_info intr_info;
++      struct edma_rxfill_ring *rxfill_rings;
++      struct edma_rxdesc_ring *rx_rings;
++      u32 rx_page_mode;
++      u32 rx_buf_size;
+ };
+-/* Global EDMA context. */
++/* Global EDMA context */
+ extern struct edma_context *edma_ctx;
+ void edma_destroy(struct ppe_device *ppe_dev);
+@@ -111,6 +127,10 @@ int edma_setup(struct ppe_device *ppe_de
+ int ppe_edma_queue_offset_config(struct ppe_device *ppe_dev,
+                                enum ppe_queue_class_type class,
+                                int index, int queue_offset);
++int ppe_edma_queue_resource_get(struct ppe_device *ppe_dev, int type,
++                              int *res_start, int *res_end);
++int ppe_edma_ring_to_queues_config(struct ppe_device *ppe_dev, int ring_id,
++                                 int num, int queues[] __counted_by(num));
+ #endif
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.c
+@@ -0,0 +1,964 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* Configure rings, Buffers and NAPI for receive path along with
++ * providing APIs to enable, disable, clean and map the Rx rings.
++ */
++
++#include <linux/cpumask.h>
++#include <linux/dma-mapping.h>
++#include <linux/kernel.h>
++#include <linux/netdevice.h>
++#include <linux/printk.h>
++#include <linux/regmap.h>
++#include <linux/skbuff.h>
++
++#include "edma.h"
++#include "edma_cfg_rx.h"
++#include "ppe.h"
++#include "ppe_regs.h"
++
++/* EDMA Queue ID to Ring ID Table. */
++#define EDMA_QID2RID_TABLE_MEM(q)     (0xb9000 + (0x4 * (q)))
++
++/* Rx ring queue offset. */
++#define EDMA_QUEUE_OFFSET(q_id)       ((q_id) / EDMA_MAX_PRI_PER_CORE)
++
++/* Rx EDMA maximum queue supported. */
++#define EDMA_CPU_PORT_QUEUE_MAX(queue_start)  \
++                      ((queue_start) + (EDMA_MAX_PRI_PER_CORE * num_possible_cpus()) - 1)
++
++/* EDMA Queue ID to Ring ID configuration. */
++#define EDMA_QID2RID_NUM_PER_REG      4
++
++int rx_queues[] = {0, 8, 16, 24};
++
++static u32 edma_rx_ring_queue_map[][EDMA_MAX_CORE] = {{ 0, 8, 16, 24 },
++                                              { 1, 9, 17, 25 },
++                                              { 2, 10, 18, 26 },
++                                              { 3, 11, 19, 27 },
++                                              { 4, 12, 20, 28 },
++                                              { 5, 13, 21, 29 },
++                                              { 6, 14, 22, 30 },
++                                              { 7, 15, 23, 31 }};
++
++static int edma_cfg_rx_desc_rings_reset_queue_mapping(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 i, ret;
++
++      for (i = 0; i < rx->num_rings; i++) {
++              struct edma_rxdesc_ring *rxdesc_ring;
++
++              rxdesc_ring = &edma_ctx->rx_rings[i];
++
++              ret = ppe_edma_ring_to_queues_config(edma_ctx->ppe_dev, rxdesc_ring->ring_id,
++                                                   ARRAY_SIZE(rx_queues), rx_queues);
++              if (ret) {
++                      pr_err("Error in unmapping rxdesc ring %d to PPE queue mapping to disable its backpressure configuration\n",
++                             i);
++                      return ret;
++              }
++      }
++
++      return 0;
++}
++
++static int edma_cfg_rx_desc_ring_reset_queue_priority(u32 rxdesc_ring_idx)
++{
++      u32 i, queue_id, ret;
++
++      for (i = 0; i < EDMA_MAX_PRI_PER_CORE; i++) {
++              queue_id = edma_rx_ring_queue_map[i][rxdesc_ring_idx];
++
++              ret = ppe_queue_priority_set(edma_ctx->ppe_dev, queue_id, i);
++              if (ret) {
++                      pr_err("Error in resetting %u queue's priority\n",
++                             queue_id);
++                      return ret;
++              }
++      }
++
++      return 0;
++}
++
++static int edma_cfg_rx_desc_ring_reset_queue_config(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 i, ret;
++
++      if (unlikely(rx->num_rings > num_possible_cpus())) {
++              pr_err("Invalid count of rxdesc rings: %d\n",
++                     rx->num_rings);
++              return -EINVAL;
++      }
++
++      /* Unmap Rxdesc ring to PPE queue mapping */
++      ret = edma_cfg_rx_desc_rings_reset_queue_mapping();
++      if (ret)        {
++              pr_err("Error in resetting Rx desc ring backpressure config\n");
++              return ret;
++      }
++
++      /* Reset the priority for PPE queues mapped to Rx rings */
++      for (i = 0; i < rx->num_rings; i++) {
++              ret =  edma_cfg_rx_desc_ring_reset_queue_priority(i);
++              if (ret)        {
++                      pr_err("Error in resetting ring:%d queue's priority\n",
++                             i + rx->ring_start);
++                      return ret;
++              }
++      }
++
++      return 0;
++}
++
++static int edma_cfg_rx_desc_ring_to_queue_mapping(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 i;
++      int ret;
++
++      /* Rxdesc ring to PPE queue mapping */
++      for (i = 0; i < rx->num_rings; i++) {
++              struct edma_rxdesc_ring *rxdesc_ring;
++
++              rxdesc_ring = &edma_ctx->rx_rings[i];
++
++              ret = ppe_edma_ring_to_queues_config(edma_ctx->ppe_dev,
++                                                   rxdesc_ring->ring_id,
++                                              ARRAY_SIZE(rx_queues), rx_queues);
++              if (ret) {
++                      pr_err("Error in configuring Rx ring to PPE queue mapping, ret: %d, id: %d\n",
++                             ret, rxdesc_ring->ring_id);
++                      if (!edma_cfg_rx_desc_rings_reset_queue_mapping())
++                              pr_err("Error in resetting Rx desc ringbackpressure configurations\n");
++
++                      return ret;
++              }
++
++              pr_debug("Rx desc ring %d to PPE queue mapping for backpressure:\n",
++                       rxdesc_ring->ring_id);
++      }
++
++      return 0;
++}
++
++static void edma_cfg_rx_desc_ring_configure(struct edma_rxdesc_ring *rxdesc_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 data, reg;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_BA(rxdesc_ring->ring_id);
++      regmap_write(regmap, reg, (u32)(rxdesc_ring->pdma & EDMA_RXDESC_BA_MASK));
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_PREHEADER_BA(rxdesc_ring->ring_id);
++      regmap_write(regmap, reg, (u32)(rxdesc_ring->sdma & EDMA_RXDESC_PREHEADER_BA_MASK));
++
++      data = rxdesc_ring->count & EDMA_RXDESC_RING_SIZE_MASK;
++      data |= (EDMA_RXDESC_PL_DEFAULT_VALUE & EDMA_RXDESC_PL_OFFSET_MASK)
++               << EDMA_RXDESC_PL_OFFSET_SHIFT;
++      reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_RING_SIZE(rxdesc_ring->ring_id);
++      regmap_write(regmap, reg, data);
++
++      /* Configure the Mitigation timer */
++      data = EDMA_MICROSEC_TO_TIMER_UNIT(EDMA_RX_MITIGATION_TIMER_DEF,
++                                         ppe_dev->clk_rate / MHZ);
++      data = ((data & EDMA_RX_MOD_TIMER_INIT_MASK)
++                      << EDMA_RX_MOD_TIMER_INIT_SHIFT);
++      pr_debug("EDMA Rx mitigation timer value: %d\n", data);
++      reg = EDMA_BASE_OFFSET + EDMA_REG_RX_MOD_TIMER(rxdesc_ring->ring_id);
++      regmap_write(regmap, reg, data);
++
++      /* Configure the Mitigation packet count */
++      data = (EDMA_RX_MITIGATION_PKT_CNT_DEF & EDMA_RXDESC_LOW_THRE_MASK)
++                      << EDMA_RXDESC_LOW_THRE_SHIFT;
++      pr_debug("EDMA Rx mitigation packet count value: %d\n", data);
++      reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_UGT_THRE(rxdesc_ring->ring_id);
++      regmap_write(regmap, reg, data);
++
++      /* Enable ring. Set ret mode to 'opaque'. */
++      reg = EDMA_BASE_OFFSET + EDMA_REG_RX_INT_CTRL(rxdesc_ring->ring_id);
++      regmap_write(regmap, reg, EDMA_RX_NE_INT_EN);
++}
++
++static void edma_cfg_rx_qid_to_rx_desc_ring_mapping(void)
++{
++      u32 desc_index, ring_index, reg_index, data, q_id;
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 mcast_start, mcast_end, reg;
++      int ret;
++
++      desc_index = (rx->ring_start & EDMA_RX_RING_ID_MASK);
++
++      /* Here map all the queues to ring. */
++      for (q_id = EDMA_RX_QUEUE_START;
++              q_id <= EDMA_CPU_PORT_QUEUE_MAX(EDMA_RX_QUEUE_START);
++                      q_id += EDMA_QID2RID_NUM_PER_REG) {
++              reg_index = q_id / EDMA_QID2RID_NUM_PER_REG;
++              ring_index = desc_index + EDMA_QUEUE_OFFSET(q_id);
++
++              data = FIELD_PREP(EDMA_RX_RING_ID_QUEUE0_MASK, ring_index);
++              data |= FIELD_PREP(EDMA_RX_RING_ID_QUEUE1_MASK, ring_index);
++              data |= FIELD_PREP(EDMA_RX_RING_ID_QUEUE2_MASK, ring_index);
++              data |= FIELD_PREP(EDMA_RX_RING_ID_QUEUE3_MASK, ring_index);
++
++              reg = EDMA_BASE_OFFSET + EDMA_QID2RID_TABLE_MEM(reg_index);
++              regmap_write(regmap, reg, data);
++              pr_debug("Configure QID2RID: %d reg:0x%x to 0x%x, desc_index: %d, reg_index: %d\n",
++                       q_id, EDMA_QID2RID_TABLE_MEM(reg_index), data, desc_index, reg_index);
++      }
++
++      ret = ppe_edma_queue_resource_get(edma_ctx->ppe_dev, PPE_RES_MCAST,
++                                        &mcast_start, &mcast_end);
++      if (ret < 0) {
++              pr_err("Error in extracting multicast queue values\n");
++              return;
++      }
++
++      /* Map multicast queues to the first Rx ring. */
++      desc_index = (rx->ring_start & EDMA_RX_RING_ID_MASK);
++      for (q_id = mcast_start; q_id <= mcast_end;
++                      q_id += EDMA_QID2RID_NUM_PER_REG) {
++              reg_index = q_id / EDMA_QID2RID_NUM_PER_REG;
++
++              data = FIELD_PREP(EDMA_RX_RING_ID_QUEUE0_MASK, desc_index);
++              data |= FIELD_PREP(EDMA_RX_RING_ID_QUEUE1_MASK, desc_index);
++              data |= FIELD_PREP(EDMA_RX_RING_ID_QUEUE2_MASK, desc_index);
++              data |= FIELD_PREP(EDMA_RX_RING_ID_QUEUE3_MASK, desc_index);
++
++              reg = EDMA_BASE_OFFSET + EDMA_QID2RID_TABLE_MEM(reg_index);
++              regmap_write(regmap, reg, data);
++
++              pr_debug("Configure QID2RID: %d reg:0x%x to 0x%x\n",
++                       q_id, EDMA_QID2RID_TABLE_MEM(reg_index), data);
++      }
++}
++
++static void edma_cfg_rx_rings_to_rx_fill_mapping(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 i, data, reg;
++
++      regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_0_ADDR, 0);
++      regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_1_ADDR, 0);
++      regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_2_ADDR, 0);
++
++      for (i = 0; i < rx->num_rings; i++) {
++              struct edma_rxdesc_ring *rxdesc_ring = &edma_ctx->rx_rings[i];
++              u32 data, reg, ring_id;
++
++              ring_id = rxdesc_ring->ring_id;
++              if (ring_id >= 0 && ring_id <= 9)
++                      reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_0_ADDR;
++              else if (ring_id >= 10 && ring_id <= 19)
++                      reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_1_ADDR;
++              else
++                      reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_2_ADDR;
++
++              pr_debug("Configure RXDESC:%u to use RXFILL:%u\n",
++                       ring_id,
++                       rxdesc_ring->rxfill->ring_id);
++
++               /* Set the Rx fill ring number in the mapping register. */
++              regmap_read(regmap, reg, &data);
++              data |= (rxdesc_ring->rxfill->ring_id &
++                       EDMA_RXDESC2FILL_MAP_RXDESC_MASK) <<
++                       ((ring_id % 10) * 3);
++              regmap_write(regmap, reg, data);
++      }
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_0_ADDR;
++      regmap_read(regmap, reg, &data);
++      pr_debug("EDMA_REG_RXDESC2FILL_MAP_0_ADDR: 0x%x\n", data);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_1_ADDR;
++      regmap_read(regmap, reg, &data);
++      pr_debug("EDMA_REG_RXDESC2FILL_MAP_1_ADDR: 0x%x\n", data);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_2_ADDR;
++      regmap_read(regmap, reg, &data);
++      pr_debug("EDMA_REG_RXDESC2FILL_MAP_2_ADDR: 0x%x\n", data);
++}
++
++/**
++ * edma_cfg_rx_rings_enable - Enable Rx and Rxfill rings
++ *
++ * Enable Rx and Rxfill rings.
++ */
++void edma_cfg_rx_rings_enable(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rxfill = hw_info->rxfill;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 i, reg;
++
++      /* Enable Rx rings */
++      for (i = rx->ring_start; i < rx->ring_start + rx->num_rings; i++) {
++              u32 data;
++
++              reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_CTRL(i);
++              regmap_read(regmap, reg, &data);
++              data |= EDMA_RXDESC_RX_EN;
++              regmap_write(regmap, reg, data);
++      }
++
++      for (i = rxfill->ring_start; i < rxfill->ring_start + rxfill->num_rings; i++) {
++              u32 data;
++
++              reg = EDMA_BASE_OFFSET + EDMA_REG_RXFILL_RING_EN(i);
++              regmap_read(regmap, reg, &data);
++              data |= EDMA_RXFILL_RING_EN;
++              regmap_write(regmap, reg, data);
++      }
++}
++
++/**
++ * edma_cfg_rx_rings_disable - Disable Rx and Rxfill rings
++ *
++ * Disable Rx and Rxfill rings.
++ */
++void edma_cfg_rx_rings_disable(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rxfill = hw_info->rxfill;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 i, reg;
++
++      /* Disable Rx rings */
++      for (i = 0; i < rx->num_rings; i++) {
++              struct edma_rxdesc_ring *rxdesc_ring = NULL;
++              u32 data;
++
++              rxdesc_ring = &edma_ctx->rx_rings[i];
++              reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_CTRL(rxdesc_ring->ring_id);
++              regmap_read(regmap, reg, &data);
++              data &= ~EDMA_RXDESC_RX_EN;
++              regmap_write(regmap, reg, data);
++      }
++
++      /* Disable RxFill Rings */
++      for (i = 0; i < rxfill->num_rings; i++) {
++              struct edma_rxfill_ring *rxfill_ring = NULL;
++              u32 data;
++
++              rxfill_ring = &edma_ctx->rxfill_rings[i];
++              reg = EDMA_BASE_OFFSET + EDMA_REG_RXFILL_RING_EN(rxfill_ring->ring_id);
++              regmap_read(regmap, reg, &data);
++              data &= ~EDMA_RXFILL_RING_EN;
++              regmap_write(regmap, reg, data);
++      }
++}
++
++/**
++ * edma_cfg_rx_mappings - Setup RX ring mapping
++ *
++ * Setup queue ID to Rx desc ring mapping.
++ */
++void edma_cfg_rx_ring_mappings(void)
++{
++      edma_cfg_rx_qid_to_rx_desc_ring_mapping();
++      edma_cfg_rx_rings_to_rx_fill_mapping();
++}
++
++static void edma_cfg_rx_fill_ring_cleanup(struct edma_rxfill_ring *rxfill_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct device *dev = ppe_dev->dev;
++      u16 cons_idx, curr_idx;
++      u32 data, reg;
++
++      /* Get RxFill ring producer index */
++      curr_idx = rxfill_ring->prod_idx & EDMA_RXFILL_PROD_IDX_MASK;
++
++      /* Get RxFill ring consumer index */
++      reg = EDMA_BASE_OFFSET + EDMA_REG_RXFILL_CONS_IDX(rxfill_ring->ring_id);
++      regmap_read(regmap, reg, &data);
++      cons_idx = data & EDMA_RXFILL_CONS_IDX_MASK;
++
++      while (curr_idx != cons_idx) {
++              struct edma_rxfill_desc *rxfill_desc;
++              struct sk_buff *skb;
++
++              /* Get RxFill descriptor */
++              rxfill_desc = EDMA_RXFILL_DESC(rxfill_ring, cons_idx);
++
++              cons_idx = (cons_idx + 1) & EDMA_RX_RING_SIZE_MASK;
++
++              /* Get skb from opaque */
++              skb = (struct sk_buff *)EDMA_RXFILL_OPAQUE_GET(rxfill_desc);
++              if (unlikely(!skb)) {
++                      pr_err("Empty skb reference at index:%d\n",
++                             cons_idx);
++                      continue;
++              }
++
++              dev_kfree_skb_any(skb);
++      }
++
++      /* Free RxFill ring descriptors */
++      dma_free_coherent(dev, (sizeof(struct edma_rxfill_desc)
++                         * rxfill_ring->count),
++                         rxfill_ring->desc, rxfill_ring->dma);
++      rxfill_ring->desc = NULL;
++      rxfill_ring->dma = (dma_addr_t)0;
++}
++
++static int edma_cfg_rx_fill_ring_dma_alloc(struct edma_rxfill_ring *rxfill_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device *dev = ppe_dev->dev;
++
++      /* Allocate RxFill ring descriptors */
++      rxfill_ring->desc = dma_alloc_coherent(dev, (sizeof(struct edma_rxfill_desc)
++                                             * rxfill_ring->count),
++                                             &rxfill_ring->dma,
++                                             GFP_KERNEL | __GFP_ZERO);
++      if (unlikely(!rxfill_ring->desc))
++              return -ENOMEM;
++
++      return 0;
++}
++
++static int edma_cfg_rx_desc_ring_dma_alloc(struct edma_rxdesc_ring *rxdesc_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device *dev = ppe_dev->dev;
++
++      rxdesc_ring->pdesc = dma_alloc_coherent(dev, (sizeof(struct edma_rxdesc_pri)
++                                              * rxdesc_ring->count),
++                              &rxdesc_ring->pdma, GFP_KERNEL | __GFP_ZERO);
++      if (unlikely(!rxdesc_ring->pdesc))
++              return -ENOMEM;
++
++      rxdesc_ring->sdesc = dma_alloc_coherent(dev, (sizeof(struct edma_rxdesc_sec)
++                              * rxdesc_ring->count),
++                              &rxdesc_ring->sdma, GFP_KERNEL | __GFP_ZERO);
++      if (unlikely(!rxdesc_ring->sdesc)) {
++              dma_free_coherent(dev, (sizeof(struct edma_rxdesc_pri)
++                                * rxdesc_ring->count),
++                                rxdesc_ring->pdesc,
++                                rxdesc_ring->pdma);
++              rxdesc_ring->pdesc = NULL;
++              rxdesc_ring->pdma = (dma_addr_t)0;
++              return -ENOMEM;
++      }
++
++      return 0;
++}
++
++static void edma_cfg_rx_desc_ring_cleanup(struct edma_rxdesc_ring *rxdesc_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct device *dev = ppe_dev->dev;
++      u32 prod_idx, cons_idx, reg;
++
++      /* Get Rxdesc consumer & producer indices */
++      cons_idx = rxdesc_ring->cons_idx & EDMA_RXDESC_CONS_IDX_MASK;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_PROD_IDX(rxdesc_ring->ring_id);
++      regmap_read(regmap, reg, &prod_idx);
++      prod_idx = prod_idx & EDMA_RXDESC_PROD_IDX_MASK;
++
++      /* Free any buffers assigned to any descriptors */
++      while (cons_idx != prod_idx) {
++              struct edma_rxdesc_pri *rxdesc_pri =
++                      EDMA_RXDESC_PRI_DESC(rxdesc_ring, cons_idx);
++              struct sk_buff *skb;
++
++              /* Update consumer index */
++              cons_idx = (cons_idx + 1) & EDMA_RX_RING_SIZE_MASK;
++
++              /* Get opaque from Rxdesc */
++              skb = (struct sk_buff *)EDMA_RXDESC_OPAQUE_GET(rxdesc_pri);
++              if (unlikely(!skb)) {
++                      pr_warn("Empty skb reference at index:%d\n",
++                              cons_idx);
++                      continue;
++              }
++
++              dev_kfree_skb_any(skb);
++      }
++
++      /* Update the consumer index */
++      reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_CONS_IDX(rxdesc_ring->ring_id);
++      regmap_write(regmap, reg, cons_idx);
++
++      /* Free Rxdesc ring descriptor */
++      dma_free_coherent(dev, (sizeof(struct edma_rxdesc_pri)
++                        * rxdesc_ring->count), rxdesc_ring->pdesc,
++                        rxdesc_ring->pdma);
++      rxdesc_ring->pdesc = NULL;
++      rxdesc_ring->pdma = (dma_addr_t)0;
++
++      /* Free any buffers assigned to any secondary ring descriptors */
++      dma_free_coherent(dev, (sizeof(struct edma_rxdesc_sec)
++                        * rxdesc_ring->count), rxdesc_ring->sdesc,
++                        rxdesc_ring->sdma);
++      rxdesc_ring->sdesc = NULL;
++      rxdesc_ring->sdma = (dma_addr_t)0;
++}
++
++static int edma_cfg_rx_rings_setup(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rxfill = hw_info->rxfill;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 ring_idx, alloc_size, buf_len;
++
++      /* Set buffer allocation size */
++      if (edma_ctx->rx_buf_size) {
++              alloc_size = edma_ctx->rx_buf_size +
++                              EDMA_RX_SKB_HEADROOM + NET_IP_ALIGN;
++              buf_len = alloc_size - EDMA_RX_SKB_HEADROOM - NET_IP_ALIGN;
++      } else if (edma_ctx->rx_page_mode) {
++              alloc_size = EDMA_RX_PAGE_MODE_SKB_SIZE +
++                              EDMA_RX_SKB_HEADROOM + NET_IP_ALIGN;
++              buf_len = PAGE_SIZE;
++      } else {
++              alloc_size = EDMA_RX_BUFFER_SIZE;
++              buf_len = alloc_size - EDMA_RX_SKB_HEADROOM - NET_IP_ALIGN;
++      }
++
++      pr_debug("EDMA ctx:%p rx_ring alloc_size=%d, buf_len=%d\n",
++               edma_ctx, alloc_size, buf_len);
++
++      /* Allocate Rx fill ring descriptors */
++      for (ring_idx = 0; ring_idx < rxfill->num_rings; ring_idx++) {
++              u32 ret;
++              struct edma_rxfill_ring *rxfill_ring = NULL;
++
++              rxfill_ring = &edma_ctx->rxfill_rings[ring_idx];
++              rxfill_ring->count = EDMA_RX_RING_SIZE;
++              rxfill_ring->ring_id = rxfill->ring_start + ring_idx;
++              rxfill_ring->alloc_size = alloc_size;
++              rxfill_ring->buf_len = buf_len;
++              rxfill_ring->page_mode = edma_ctx->rx_page_mode;
++
++              ret = edma_cfg_rx_fill_ring_dma_alloc(rxfill_ring);
++              if (ret) {
++                      pr_err("Error in setting up %d rxfill ring. ret: %d",
++                             rxfill_ring->ring_id, ret);
++                      while (--ring_idx >= 0)
++                              edma_cfg_rx_fill_ring_cleanup(&edma_ctx->rxfill_rings[ring_idx]);
++
++                      return -ENOMEM;
++              }
++      }
++
++      /* Allocate RxDesc ring descriptors */
++      for (ring_idx = 0; ring_idx < rx->num_rings; ring_idx++) {
++              u32 index, queue_id = EDMA_RX_QUEUE_START;
++              struct edma_rxdesc_ring *rxdesc_ring = NULL;
++              u32 ret;
++
++              rxdesc_ring = &edma_ctx->rx_rings[ring_idx];
++              rxdesc_ring->count = EDMA_RX_RING_SIZE;
++              rxdesc_ring->ring_id = rx->ring_start + ring_idx;
++
++              if (queue_id > EDMA_CPU_PORT_QUEUE_MAX(EDMA_RX_QUEUE_START)) {
++                      pr_err("Invalid queue_id: %d\n", queue_id);
++                      while (--ring_idx >= 0)
++                              edma_cfg_rx_desc_ring_cleanup(&edma_ctx->rx_rings[ring_idx]);
++
++                      goto rxdesc_mem_alloc_fail;
++              }
++
++              /* Create a mapping between RX Desc ring and Rx fill ring.
++               * Number of fill rings are lesser than the descriptor rings
++               * Share the fill rings across descriptor rings.
++               */
++              index = rxfill->ring_start +
++                              (ring_idx % rxfill->num_rings);
++              rxdesc_ring->rxfill = &edma_ctx->rxfill_rings[index
++                                              - rxfill->ring_start];
++
++              ret = edma_cfg_rx_desc_ring_dma_alloc(rxdesc_ring);
++              if (ret) {
++                      pr_err("Error in setting up %d rxdesc ring. ret: %d",
++                             rxdesc_ring->ring_id, ret);
++                      while (--ring_idx >= 0)
++                              edma_cfg_rx_desc_ring_cleanup(&edma_ctx->rx_rings[ring_idx]);
++
++                      goto rxdesc_mem_alloc_fail;
++              }
++      }
++
++      pr_debug("Rx descriptor count for Rx desc and Rx fill rings : %d\n",
++               EDMA_RX_RING_SIZE);
++
++      return 0;
++
++rxdesc_mem_alloc_fail:
++      for (ring_idx = 0; ring_idx < rxfill->num_rings; ring_idx++)
++              edma_cfg_rx_fill_ring_cleanup(&edma_ctx->rxfill_rings[ring_idx]);
++
++      return -ENOMEM;
++}
++
++/**
++ * edma_cfg_rx_buff_size_setup - Configure EDMA Rx jumbo buffer
++ *
++ * Configure EDMA Rx jumbo buffer
++ */
++void edma_cfg_rx_buff_size_setup(void)
++{
++      if (edma_ctx->rx_buf_size) {
++              edma_ctx->rx_page_mode = false;
++              pr_debug("Rx Jumbo mru is enabled: %d\n", edma_ctx->rx_buf_size);
++      }
++}
++
++/**
++ * edma_cfg_rx_rings_alloc - Allocate EDMA Rx rings
++ *
++ * Allocate EDMA Rx rings.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++int edma_cfg_rx_rings_alloc(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rxfill = hw_info->rxfill;
++      struct edma_ring_info *rx = hw_info->rx;
++      int ret;
++
++      edma_ctx->rxfill_rings = kzalloc((sizeof(*edma_ctx->rxfill_rings) *
++                                       rxfill->num_rings),
++                                       GFP_KERNEL);
++      if (!edma_ctx->rxfill_rings)
++              return -ENOMEM;
++
++      edma_ctx->rx_rings = kzalloc((sizeof(*edma_ctx->rx_rings) *
++                                       rx->num_rings),
++                                       GFP_KERNEL);
++      if (!edma_ctx->rx_rings)
++              goto rxdesc_ring_alloc_fail;
++
++      pr_debug("RxDesc:%u rx (%u-%u) RxFill:%u (%u-%u)\n",
++               rx->num_rings, rx->ring_start,
++               (rx->ring_start + rx->num_rings - 1),
++               rxfill->num_rings, rxfill->ring_start,
++               (rxfill->ring_start + rxfill->num_rings - 1));
++
++      if (edma_cfg_rx_rings_setup()) {
++              pr_err("Error in setting up Rx rings\n");
++              goto rx_rings_setup_fail;
++      }
++
++      /* Reset Rx descriptor ring mapped queue's configurations */
++      ret = edma_cfg_rx_desc_ring_reset_queue_config();
++      if (ret) {
++              pr_err("Error in resetting the Rx descriptor rings configurations\n");
++              edma_cfg_rx_rings_cleanup();
++              return ret;
++      }
++
++      return 0;
++
++rx_rings_setup_fail:
++      kfree(edma_ctx->rx_rings);
++      edma_ctx->rx_rings = NULL;
++rxdesc_ring_alloc_fail:
++      kfree(edma_ctx->rxfill_rings);
++      edma_ctx->rxfill_rings = NULL;
++
++      return -ENOMEM;
++}
++
++/**
++ * edma_cfg_rx_rings_cleanup - Cleanup EDMA Rx rings
++ *
++ * Cleanup EDMA Rx rings
++ */
++void edma_cfg_rx_rings_cleanup(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rxfill = hw_info->rxfill;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 i;
++
++      /* Free RxFill ring descriptors */
++      for (i = 0; i < rxfill->num_rings; i++)
++              edma_cfg_rx_fill_ring_cleanup(&edma_ctx->rxfill_rings[i]);
++
++      /* Free Rx completion ring descriptors */
++      for (i = 0; i < rx->num_rings; i++)
++              edma_cfg_rx_desc_ring_cleanup(&edma_ctx->rx_rings[i]);
++
++      kfree(edma_ctx->rxfill_rings);
++      kfree(edma_ctx->rx_rings);
++      edma_ctx->rxfill_rings = NULL;
++      edma_ctx->rx_rings = NULL;
++}
++
++static void edma_cfg_rx_fill_ring_configure(struct edma_rxfill_ring *rxfill_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 ring_sz, reg;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_RXFILL_BA(rxfill_ring->ring_id);
++      regmap_write(regmap, reg, (u32)(rxfill_ring->dma & EDMA_RING_DMA_MASK));
++
++      ring_sz = rxfill_ring->count & EDMA_RXFILL_RING_SIZE_MASK;
++      reg = EDMA_BASE_OFFSET + EDMA_REG_RXFILL_RING_SIZE(rxfill_ring->ring_id);
++      regmap_write(regmap, reg, ring_sz);
++
++      edma_rx_alloc_buffer(rxfill_ring, rxfill_ring->count - 1);
++}
++
++static void edma_cfg_rx_desc_ring_flow_control(u32 threshold_xoff, u32 threshold_xon)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 data, i, reg;
++
++      data = (threshold_xoff & EDMA_RXDESC_FC_XOFF_THRE_MASK) << EDMA_RXDESC_FC_XOFF_THRE_SHIFT;
++      data |= ((threshold_xon & EDMA_RXDESC_FC_XON_THRE_MASK) << EDMA_RXDESC_FC_XON_THRE_SHIFT);
++
++      for (i = 0; i < rx->num_rings; i++) {
++              struct edma_rxdesc_ring *rxdesc_ring;
++
++              rxdesc_ring = &edma_ctx->rx_rings[i];
++              reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_FC_THRE(rxdesc_ring->ring_id);
++              regmap_write(regmap, reg, data);
++      }
++}
++
++static void edma_cfg_rx_fill_ring_flow_control(int threshold_xoff, int threshold_xon)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rxfill = hw_info->rxfill;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 data, i, reg;
++
++      data = (threshold_xoff & EDMA_RXFILL_FC_XOFF_THRE_MASK) << EDMA_RXFILL_FC_XOFF_THRE_SHIFT;
++      data |= ((threshold_xon & EDMA_RXFILL_FC_XON_THRE_MASK) << EDMA_RXFILL_FC_XON_THRE_SHIFT);
++
++      for (i = 0; i < rxfill->num_rings; i++) {
++              struct edma_rxfill_ring *rxfill_ring;
++
++              rxfill_ring = &edma_ctx->rxfill_rings[i];
++              reg = EDMA_BASE_OFFSET + EDMA_REG_RXFILL_FC_THRE(rxfill_ring->ring_id);
++              regmap_write(regmap, reg, data);
++      }
++}
++
++/**
++ * edma_cfg_rx_rings - Configure EDMA Rx rings.
++ *
++ * Configure EDMA Rx rings.
++ */
++int edma_cfg_rx_rings(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rxfill = hw_info->rxfill;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 i;
++
++      for (i = 0; i < rxfill->num_rings; i++)
++              edma_cfg_rx_fill_ring_configure(&edma_ctx->rxfill_rings[i]);
++
++      for (i = 0; i < rx->num_rings; i++)
++              edma_cfg_rx_desc_ring_configure(&edma_ctx->rx_rings[i]);
++
++      /* Configure Rx flow control configurations */
++      edma_cfg_rx_desc_ring_flow_control(EDMA_RX_FC_XOFF_DEF, EDMA_RX_FC_XON_DEF);
++      edma_cfg_rx_fill_ring_flow_control(EDMA_RX_FC_XOFF_DEF, EDMA_RX_FC_XON_DEF);
++
++      return edma_cfg_rx_desc_ring_to_queue_mapping();
++}
++
++/**
++ * edma_cfg_rx_disable_interrupts - EDMA disable RX interrupts
++ *
++ * Disable RX interrupt masks
++ */
++void edma_cfg_rx_disable_interrupts(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 i, reg;
++
++      for (i = 0; i < rx->num_rings; i++) {
++              struct edma_rxdesc_ring *rxdesc_ring =
++                              &edma_ctx->rx_rings[i];
++              reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_INT_MASK(rxdesc_ring->ring_id);
++              regmap_write(regmap, reg, EDMA_MASK_INT_CLEAR);
++      }
++}
++
++/**
++ * edma_cfg_rx_enable_interrupts - EDMA enable RX interrupts
++ *
++ * Enable RX interrupt masks
++ */
++void edma_cfg_rx_enable_interrupts(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 i, reg;
++
++      for (i = 0; i < rx->num_rings; i++) {
++              struct edma_rxdesc_ring *rxdesc_ring =
++                              &edma_ctx->rx_rings[i];
++              reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_INT_MASK(rxdesc_ring->ring_id);
++              regmap_write(regmap, reg, edma_ctx->intr_info.intr_mask_rx);
++      }
++}
++
++/**
++ * edma_cfg_rx_napi_disable - Disable NAPI for Rx
++ *
++ * Disable NAPI for Rx
++ */
++void edma_cfg_rx_napi_disable(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 i;
++
++      for (i = 0; i < rx->num_rings; i++) {
++              struct edma_rxdesc_ring *rxdesc_ring;
++
++              rxdesc_ring = &edma_ctx->rx_rings[i];
++
++              if (!rxdesc_ring->napi_added)
++                      continue;
++
++              napi_disable(&rxdesc_ring->napi);
++      }
++}
++
++/**
++ * edma_cfg_rx_napi_enable - Enable NAPI for Rx
++ *
++ * Enable NAPI for Rx
++ */
++void edma_cfg_rx_napi_enable(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 i;
++
++      for (i = 0; i < rx->num_rings; i++) {
++              struct edma_rxdesc_ring *rxdesc_ring;
++
++              rxdesc_ring = &edma_ctx->rx_rings[i];
++
++              if (!rxdesc_ring->napi_added)
++                      continue;
++
++              napi_enable(&rxdesc_ring->napi);
++      }
++}
++
++/**
++ * edma_cfg_rx_napi_delete - Delete Rx NAPI
++ *
++ * Delete RX NAPI
++ */
++void edma_cfg_rx_napi_delete(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 i;
++
++      for (i = 0; i < rx->num_rings; i++) {
++              struct edma_rxdesc_ring *rxdesc_ring;
++
++              rxdesc_ring = &edma_ctx->rx_rings[i];
++
++              if (!rxdesc_ring->napi_added)
++                      continue;
++
++              netif_napi_del(&rxdesc_ring->napi);
++              rxdesc_ring->napi_added = false;
++      }
++}
++
++/* Add Rx NAPI */
++/**
++ * edma_cfg_rx_napi_add - Add Rx NAPI
++ * @netdev: Netdevice
++ *
++ * Add RX NAPI
++ */
++void edma_cfg_rx_napi_add(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rx = hw_info->rx;
++      u32 i;
++
++      for (i = 0; i < rx->num_rings; i++) {
++              struct edma_rxdesc_ring *rxdesc_ring = &edma_ctx->rx_rings[i];
++
++              netif_napi_add_weight(edma_ctx->dummy_dev, &rxdesc_ring->napi,
++                                    edma_rx_napi_poll, hw_info->napi_budget_rx);
++              rxdesc_ring->napi_added = true;
++      }
++
++      netdev_dbg(edma_ctx->dummy_dev, "Rx NAPI budget: %d\n", hw_info->napi_budget_rx);
++}
++
++/**
++ * edma_cfg_rx_rps_hash_map - Configure rx rps hash map.
++ *
++ * Initialize and configure RPS hash map for queues
++ */
++int edma_cfg_rx_rps_hash_map(void)
++{
++      cpumask_t edma_rps_cpumask = {{EDMA_RX_DEFAULT_BITMAP}};
++      int map_len = 0, idx = 0, ret = 0;
++      u32 q_off = EDMA_RX_QUEUE_START;
++      u32 q_map[EDMA_MAX_CORE] = {0};
++      u32 hash, cpu;
++
++      /* Map all possible hash values to queues used by the EDMA Rx
++       * rings based on a bitmask, which represents the cores to be mapped.
++       * These queues are expected to be mapped to different Rx rings
++       * which are assigned to different cores using IRQ affinity configuration.
++       */
++      for_each_cpu(cpu, &edma_rps_cpumask) {
++              q_map[map_len] = q_off + (cpu * EDMA_MAX_PRI_PER_CORE);
++              map_len++;
++      }
++
++      for (hash = 0; hash < PPE_QUEUE_HASH_NUM; hash++) {
++              ret = ppe_edma_queue_offset_config(edma_ctx->ppe_dev,
++                                                 PPE_QUEUE_CLASS_HASH, hash, q_map[idx]);
++              if (ret)
++                      return ret;
++
++              pr_debug("profile_id: %u, hash: %u, q_off: %u\n",
++                       EDMA_CPU_PORT_PROFILE_ID, hash, q_map[idx]);
++              idx = (idx + 1) % map_len;
++      }
++
++      return 0;
++}
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.h
+@@ -0,0 +1,48 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef __EDMA_CFG_RX__
++#define __EDMA_CFG_RX__
++
++/* SKB payload size used in page mode */
++#define EDMA_RX_PAGE_MODE_SKB_SIZE    256
++
++/* Rx flow control X-OFF default value */
++#define EDMA_RX_FC_XOFF_DEF   32
++
++/* Rx flow control X-ON default value */
++#define EDMA_RX_FC_XON_DEF    64
++
++/* Rx AC flow control original threshold */
++#define EDMA_RX_AC_FC_THRE_ORIG               0x190
++
++/* Rx AC flow control default threshold */
++#define EDMA_RX_AC_FC_THRES_DEF               0x104
++/* Rx mitigation timer's default value in microseconds */
++#define EDMA_RX_MITIGATION_TIMER_DEF  25
++
++/* Rx mitigation packet count's default value */
++#define EDMA_RX_MITIGATION_PKT_CNT_DEF        16
++
++/* Default bitmap of cores for RPS to ARM cores */
++#define EDMA_RX_DEFAULT_BITMAP        ((1 << EDMA_MAX_CORE) - 1)
++
++int edma_cfg_rx_rings(void);
++int edma_cfg_rx_rings_alloc(void);
++void edma_cfg_rx_ring_mappings(void);
++void edma_cfg_rx_rings_cleanup(void);
++void edma_cfg_rx_disable_interrupts(void);
++void edma_cfg_rx_enable_interrupts(void);
++void edma_cfg_rx_napi_disable(void);
++void edma_cfg_rx_napi_enable(void);
++void edma_cfg_rx_napi_delete(void);
++void edma_cfg_rx_napi_add(void);
++void edma_cfg_rx_mapping(void);
++void edma_cfg_rx_rings_enable(void);
++void edma_cfg_rx_rings_disable(void);
++void edma_cfg_rx_buff_size_setup(void);
++int edma_cfg_rx_rps_hash_map(void);
++int edma_cfg_rx_rps(struct ctl_table *table, int write,
++                  void *buffer, size_t *lenp, loff_t *ppos);
++#endif
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_port.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.c
+@@ -12,12 +12,39 @@
+ #include <linux/printk.h>
+ #include "edma.h"
++#include "edma_cfg_rx.h"
+ #include "edma_port.h"
+ #include "ppe_regs.h"
+ /* Number of netdev queues. */
+ #define EDMA_NETDEV_QUEUE_NUM 4
++static int edma_port_stats_alloc(struct net_device *netdev)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++
++      if (!port_priv)
++              return -EINVAL;
++
++      /* Allocate per-cpu stats memory. */
++      port_priv->pcpu_stats.rx_stats =
++              netdev_alloc_pcpu_stats(struct edma_port_rx_stats);
++      if (!port_priv->pcpu_stats.rx_stats) {
++              netdev_err(netdev, "Per-cpu EDMA Rx stats alloc failed for %s\n",
++                         netdev->name);
++              return -ENOMEM;
++      }
++
++      return 0;
++}
++
++static void edma_port_stats_free(struct net_device *netdev)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++
++      free_percpu(port_priv->pcpu_stats.rx_stats);
++}
++
+ static u16 __maybe_unused edma_port_select_queue(__maybe_unused struct net_device *netdev,
+                                                __maybe_unused struct sk_buff *skb,
+                               __maybe_unused struct net_device *sb_dev)
+@@ -172,6 +199,7 @@ void edma_port_destroy(struct ppe_port *
+       int port_id = port->port_id;
+       struct net_device *netdev = edma_ctx->netdev_arr[port_id - 1];
++      edma_port_stats_free(netdev);
+       unregister_netdev(netdev);
+       free_netdev(netdev);
+       ppe_port_phylink_destroy(port);
+@@ -232,6 +260,13 @@ int edma_port_setup(struct ppe_port *por
+                           port_id, netdev->dev_addr);
+       }
++      /* Allocate memory for EDMA port statistics. */
++      ret = edma_port_stats_alloc(netdev);
++      if (ret) {
++              netdev_dbg(netdev, "EDMA port stats alloc failed\n");
++              goto stats_alloc_fail;
++      }
++
+       netdev_dbg(netdev, "Configuring the port %s(qcom-id:%d)\n",
+                  netdev->name, port_id);
+@@ -263,8 +298,10 @@ int edma_port_setup(struct ppe_port *por
+ register_netdev_fail:
+       ppe_port_phylink_destroy(port);
+ port_phylink_setup_fail:
+-      free_netdev(netdev);
+       edma_ctx->netdev_arr[port_id - 1] = NULL;
++      edma_port_stats_free(netdev);
++stats_alloc_fail:
++      free_netdev(netdev);
+       return ret;
+ }
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_port.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.h
+@@ -15,14 +15,45 @@
+                                       | NETIF_F_TSO6)
+ /**
++ * struct edma_port_rx_stats - EDMA RX per CPU stats for the port.
++ * @rx_pkts: Number of Rx packets
++ * @rx_bytes: Number of Rx bytes
++ * @rx_drops: Number of Rx drops
++ * @rx_nr_frag_pkts: Number of Rx nr_frags packets
++ * @rx_fraglist_pkts: Number of Rx fraglist packets
++ * @rx_nr_frag_headroom_err: nr_frags headroom error packets
++ * @syncp: Synchronization pointer
++ */
++struct edma_port_rx_stats {
++      u64 rx_pkts;
++      u64 rx_bytes;
++      u64 rx_drops;
++      u64 rx_nr_frag_pkts;
++      u64 rx_fraglist_pkts;
++      u64 rx_nr_frag_headroom_err;
++      struct u64_stats_sync syncp;
++};
++
++/**
++ * struct edma_port_pcpu_stats - EDMA per cpu stats data structure for the port.
++ * @rx_stats: Per CPU Rx statistics
++ */
++struct edma_port_pcpu_stats {
++      struct edma_port_rx_stats __percpu *rx_stats;
++};
++
++/**
+  * struct edma_port_priv - EDMA port priv structure.
+  * @ppe_port: Pointer to PPE port
+  * @netdev: Corresponding netdevice
++ * @pcpu_stats: Per CPU netdev statistics
++ * @txr_map: Tx ring per-core mapping
+  * @flags: Feature flags
+  */
+ struct edma_port_priv {
+       struct ppe_port *ppe_port;
+       struct net_device *netdev;
++      struct edma_port_pcpu_stats pcpu_stats;
+       unsigned long flags;
+ };
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_rx.c
+@@ -0,0 +1,622 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* Provides APIs to alloc Rx Buffers, reap the buffers, receive and
++ * process linear and Scatter Gather packets.
++ */
++
++#include <linux/dma-mapping.h>
++#include <linux/etherdevice.h>
++#include <linux/irqreturn.h>
++#include <linux/kernel.h>
++#include <linux/netdevice.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <linux/regmap.h>
++
++#include "edma.h"
++#include "edma_cfg_rx.h"
++#include "edma_port.h"
++#include "ppe.h"
++#include "ppe_regs.h"
++
++static int edma_rx_alloc_buffer_list(struct edma_rxfill_ring *rxfill_ring, int alloc_count)
++{
++      struct edma_rxfill_stats *rxfill_stats = &rxfill_ring->rxfill_stats;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      u32 rx_alloc_size = rxfill_ring->alloc_size;
++      struct regmap *regmap = ppe_dev->regmap;
++      bool page_mode = rxfill_ring->page_mode;
++      struct edma_rxfill_desc *rxfill_desc;
++      u32 buf_len = rxfill_ring->buf_len;
++      struct device *dev = ppe_dev->dev;
++      u16 prod_idx, start_idx;
++      u16 num_alloc = 0;
++      u32 reg;
++
++      prod_idx = rxfill_ring->prod_idx;
++      start_idx = prod_idx;
++
++      while (likely(alloc_count--)) {
++              dma_addr_t buff_addr;
++              struct sk_buff *skb;
++              struct page *pg;
++
++              rxfill_desc = EDMA_RXFILL_DESC(rxfill_ring, prod_idx);
++
++              skb = dev_alloc_skb(rx_alloc_size);
++              if (unlikely(!skb)) {
++                      u64_stats_update_begin(&rxfill_stats->syncp);
++                      ++rxfill_stats->alloc_failed;
++                      u64_stats_update_end(&rxfill_stats->syncp);
++                      break;
++              }
++
++              skb_reserve(skb, EDMA_RX_SKB_HEADROOM + NET_IP_ALIGN);
++
++              if (likely(!page_mode)) {
++                      buff_addr = dma_map_single(dev, skb->data, rx_alloc_size, DMA_FROM_DEVICE);
++                      if (dma_mapping_error(dev, buff_addr)) {
++                              dev_dbg(dev, "edma_context:%p Unable to dma for non page mode",
++                                      edma_ctx);
++                              dev_kfree_skb_any(skb);
++                              break;
++                      }
++              } else {
++                      pg = alloc_page(GFP_ATOMIC);
++                      if (unlikely(!pg)) {
++                              u64_stats_update_begin(&rxfill_stats->syncp);
++                              ++rxfill_stats->page_alloc_failed;
++                              u64_stats_update_end(&rxfill_stats->syncp);
++                              dev_kfree_skb_any(skb);
++                              dev_dbg(dev, "edma_context:%p Unable to allocate page",
++                                      edma_ctx);
++                              break;
++                      }
++
++                      buff_addr = dma_map_page(dev, pg, 0, PAGE_SIZE, DMA_FROM_DEVICE);
++                      if (dma_mapping_error(dev, buff_addr)) {
++                              dev_dbg(dev, "edma_context:%p Mapping error for page mode",
++                                      edma_ctx);
++                              __free_page(pg);
++                              dev_kfree_skb_any(skb);
++                              break;
++                      }
++
++                      skb_fill_page_desc(skb, 0, pg, 0, PAGE_SIZE);
++              }
++
++              EDMA_RXFILL_BUFFER_ADDR_SET(rxfill_desc, buff_addr);
++
++              EDMA_RXFILL_OPAQUE_LO_SET(rxfill_desc, skb);
++#ifdef __LP64__
++              EDMA_RXFILL_OPAQUE_HI_SET(rxfill_desc, skb);
++#endif
++              EDMA_RXFILL_PACKET_LEN_SET(rxfill_desc,
++                                         (u32)(buf_len) & EDMA_RXFILL_BUF_SIZE_MASK);
++              prod_idx = (prod_idx + 1) & EDMA_RX_RING_SIZE_MASK;
++              num_alloc++;
++      }
++
++      if (likely(num_alloc)) {
++              reg = EDMA_BASE_OFFSET + EDMA_REG_RXFILL_PROD_IDX(rxfill_ring->ring_id);
++              regmap_write(regmap, reg, prod_idx);
++              rxfill_ring->prod_idx = prod_idx;
++      }
++
++      return num_alloc;
++}
++
++/**
++ * edma_rx_alloc_buffer - EDMA Rx alloc buffer.
++ * @rxfill_ring: EDMA Rxfill ring
++ * @alloc_count: Number of rings to alloc
++ *
++ * Alloc Rx buffers for RxFill ring.
++ *
++ * Return the number of rings allocated.
++ */
++int edma_rx_alloc_buffer(struct edma_rxfill_ring *rxfill_ring, int alloc_count)
++{
++      return edma_rx_alloc_buffer_list(rxfill_ring, alloc_count);
++}
++
++/* Mark ip_summed appropriately in the skb as per the L3/L4 checksum
++ * status in descriptor.
++ */
++static void edma_rx_checksum_verify(struct edma_rxdesc_pri *rxdesc_pri,
++                                  struct sk_buff *skb)
++{
++      u8 pid = EDMA_RXDESC_PID_GET(rxdesc_pri);
++
++      skb_checksum_none_assert(skb);
++
++      if (likely(EDMA_RX_PID_IS_IPV4(pid))) {
++              if (likely(EDMA_RXDESC_L3CSUM_STATUS_GET(rxdesc_pri)) &&
++                  likely(EDMA_RXDESC_L4CSUM_STATUS_GET(rxdesc_pri)))
++                      skb->ip_summed = CHECKSUM_UNNECESSARY;
++      } else if (likely(EDMA_RX_PID_IS_IPV6(pid))) {
++              if (likely(EDMA_RXDESC_L4CSUM_STATUS_GET(rxdesc_pri)))
++                      skb->ip_summed = CHECKSUM_UNNECESSARY;
++      }
++}
++
++static void edma_rx_process_last_segment(struct edma_rxdesc_ring *rxdesc_ring,
++                                       struct edma_rxdesc_pri *rxdesc_pri,
++                                       struct sk_buff *skb)
++{
++      bool page_mode = rxdesc_ring->rxfill->page_mode;
++      struct edma_port_pcpu_stats *pcpu_stats;
++      struct edma_port_rx_stats *rx_stats;
++      struct edma_port_priv *port_dev;
++      struct sk_buff *skb_head;
++      struct net_device *dev;
++      u32 pkt_length;
++
++      /* Get packet length. */
++      pkt_length = EDMA_RXDESC_PACKET_LEN_GET(rxdesc_pri);
++
++      skb_head = rxdesc_ring->head;
++      dev = skb_head->dev;
++
++      /* Check Rx checksum offload status. */
++      if (likely(dev->features & NETIF_F_RXCSUM))
++              edma_rx_checksum_verify(rxdesc_pri, skb_head);
++
++      /* Get stats for the netdevice. */
++      port_dev = netdev_priv(dev);
++      pcpu_stats = &port_dev->pcpu_stats;
++      rx_stats = this_cpu_ptr(pcpu_stats->rx_stats);
++
++      if (unlikely(page_mode)) {
++              if (unlikely(!pskb_may_pull(skb_head, ETH_HLEN))) {
++                      /* Discard the SKB that we have been building,
++                       * in addition to the SKB linked to current descriptor.
++                       */
++                      dev_kfree_skb_any(skb_head);
++                      rxdesc_ring->head = NULL;
++                      rxdesc_ring->last = NULL;
++                      rxdesc_ring->pdesc_head = NULL;
++
++                      u64_stats_update_begin(&rx_stats->syncp);
++                      rx_stats->rx_nr_frag_headroom_err++;
++                      u64_stats_update_end(&rx_stats->syncp);
++
++                      return;
++              }
++      }
++
++      if (unlikely(!pskb_pull(skb_head, EDMA_RXDESC_DATA_OFFSET_GET(rxdesc_ring->pdesc_head)))) {
++              dev_kfree_skb_any(skb_head);
++              rxdesc_ring->head = NULL;
++              rxdesc_ring->last = NULL;
++              rxdesc_ring->pdesc_head = NULL;
++
++              u64_stats_update_begin(&rx_stats->syncp);
++              rx_stats->rx_nr_frag_headroom_err++;
++              u64_stats_update_end(&rx_stats->syncp);
++
++              return;
++      }
++
++      u64_stats_update_begin(&rx_stats->syncp);
++      rx_stats->rx_pkts++;
++      rx_stats->rx_bytes += skb_head->len;
++      rx_stats->rx_nr_frag_pkts += (u64)page_mode;
++      rx_stats->rx_fraglist_pkts += (u64)(!page_mode);
++      u64_stats_update_end(&rx_stats->syncp);
++
++      pr_debug("edma_context:%p skb:%p Jumbo pkt_length:%u\n",
++               edma_ctx, skb_head, skb_head->len);
++
++      skb_head->protocol = eth_type_trans(skb_head, dev);
++
++      /* Send packet up the stack. */
++      if (dev->features & NETIF_F_GRO)
++              napi_gro_receive(&rxdesc_ring->napi, skb_head);
++      else
++              netif_receive_skb(skb_head);
++
++      rxdesc_ring->head = NULL;
++      rxdesc_ring->last = NULL;
++      rxdesc_ring->pdesc_head = NULL;
++}
++
++static void edma_rx_handle_frag_list(struct edma_rxdesc_ring *rxdesc_ring,
++                                   struct edma_rxdesc_pri *rxdesc_pri,
++                                   struct sk_buff *skb)
++{
++      u32 pkt_length;
++
++      /* Get packet length. */
++      pkt_length = EDMA_RXDESC_PACKET_LEN_GET(rxdesc_pri);
++      pr_debug("edma_context:%p skb:%p fragment pkt_length:%u\n",
++               edma_ctx, skb, pkt_length);
++
++      if (!(rxdesc_ring->head)) {
++              skb_put(skb, pkt_length);
++              rxdesc_ring->head = skb;
++              rxdesc_ring->last = NULL;
++              rxdesc_ring->pdesc_head = rxdesc_pri;
++
++              return;
++      }
++
++      /* Append it to the fraglist of head if this is second frame
++       * If not second frame append to tail.
++       */
++      skb_put(skb, pkt_length);
++      if (!skb_has_frag_list(rxdesc_ring->head))
++              skb_shinfo(rxdesc_ring->head)->frag_list = skb;
++      else
++              rxdesc_ring->last->next = skb;
++
++      rxdesc_ring->last = skb;
++      rxdesc_ring->last->next = NULL;
++      rxdesc_ring->head->len += pkt_length;
++      rxdesc_ring->head->data_len += pkt_length;
++      rxdesc_ring->head->truesize += skb->truesize;
++
++      /* If there are more segments for this packet,
++       * then we have nothing to do. Otherwise process
++       * last segment and send packet to stack.
++       */
++      if (EDMA_RXDESC_MORE_BIT_GET(rxdesc_pri))
++              return;
++
++      edma_rx_process_last_segment(rxdesc_ring, rxdesc_pri, skb);
++}
++
++static void edma_rx_handle_nr_frags(struct edma_rxdesc_ring *rxdesc_ring,
++                                  struct edma_rxdesc_pri *rxdesc_pri,
++                                  struct sk_buff *skb)
++{
++      skb_frag_t *frag = NULL;
++      u32 pkt_length;
++
++      /* Get packet length. */
++      pkt_length = EDMA_RXDESC_PACKET_LEN_GET(rxdesc_pri);
++      pr_debug("edma_context:%p skb:%p fragment pkt_length:%u\n",
++               edma_ctx, skb, pkt_length);
++
++      if (!(rxdesc_ring->head)) {
++              skb->len = pkt_length;
++              skb->data_len = pkt_length;
++              skb->truesize = SKB_TRUESIZE(PAGE_SIZE);
++              rxdesc_ring->head = skb;
++              rxdesc_ring->last = NULL;
++              rxdesc_ring->pdesc_head = rxdesc_pri;
++
++              return;
++      }
++
++      frag = &skb_shinfo(skb)->frags[0];
++
++      /* Append current frag at correct index as nr_frag of parent. */
++      skb_add_rx_frag(rxdesc_ring->head, skb_shinfo(rxdesc_ring->head)->nr_frags,
++                      skb_frag_page(frag), 0, pkt_length, PAGE_SIZE);
++      skb_shinfo(skb)->nr_frags = 0;
++
++      /* Free the SKB after we have appended its frag page to the head skb. */
++      dev_kfree_skb_any(skb);
++
++      /* If there are more segments for this packet,
++       * then we have nothing to do. Otherwise process
++       * last segment and send packet to stack.
++       */
++      if (EDMA_RXDESC_MORE_BIT_GET(rxdesc_pri))
++              return;
++
++      edma_rx_process_last_segment(rxdesc_ring, rxdesc_pri, skb);
++}
++
++static bool edma_rx_handle_linear_packets(struct edma_rxdesc_ring *rxdesc_ring,
++                                        struct edma_rxdesc_pri *rxdesc_pri,
++                                        struct sk_buff *skb)
++{
++      bool page_mode = rxdesc_ring->rxfill->page_mode;
++      struct edma_port_pcpu_stats *pcpu_stats;
++      struct edma_port_rx_stats *rx_stats;
++      struct edma_port_priv *port_dev;
++      skb_frag_t *frag = NULL;
++      u32 pkt_length;
++
++      /* Get stats for the netdevice. */
++      port_dev = netdev_priv(skb->dev);
++      pcpu_stats = &port_dev->pcpu_stats;
++      rx_stats = this_cpu_ptr(pcpu_stats->rx_stats);
++
++      /* Get packet length. */
++      pkt_length = EDMA_RXDESC_PACKET_LEN_GET(rxdesc_pri);
++
++      if (likely(!page_mode)) {
++              skb_put(skb, pkt_length);
++              goto send_to_stack;
++      }
++
++      /* Handle linear packet in page mode. */
++      frag = &skb_shinfo(skb)->frags[0];
++      skb_add_rx_frag(skb, 0, skb_frag_page(frag), 0, pkt_length, PAGE_SIZE);
++
++      /* Pull ethernet header into SKB data area for header processing. */
++      if (unlikely(!pskb_may_pull(skb, ETH_HLEN))) {
++              u64_stats_update_begin(&rx_stats->syncp);
++              rx_stats->rx_nr_frag_headroom_err++;
++              u64_stats_update_end(&rx_stats->syncp);
++              dev_kfree_skb_any(skb);
++
++              return false;
++      }
++
++send_to_stack:
++
++      __skb_pull(skb, EDMA_RXDESC_DATA_OFFSET_GET(rxdesc_pri));
++
++      /* Check Rx checksum offload status. */
++      if (likely(skb->dev->features & NETIF_F_RXCSUM))
++              edma_rx_checksum_verify(rxdesc_pri, skb);
++
++      u64_stats_update_begin(&rx_stats->syncp);
++      rx_stats->rx_pkts++;
++      rx_stats->rx_bytes += pkt_length;
++      rx_stats->rx_nr_frag_pkts += (u64)page_mode;
++      u64_stats_update_end(&rx_stats->syncp);
++
++      skb->protocol = eth_type_trans(skb, skb->dev);
++      if (skb->dev->features & NETIF_F_GRO)
++              napi_gro_receive(&rxdesc_ring->napi, skb);
++      else
++              netif_receive_skb(skb);
++
++      netdev_dbg(skb->dev, "edma_context:%p, skb:%p pkt_length:%u\n",
++                 edma_ctx, skb, skb->len);
++
++      return true;
++}
++
++static struct net_device *edma_rx_get_src_dev(struct edma_rxdesc_stats *rxdesc_stats,
++                                            struct edma_rxdesc_pri *rxdesc_pri,
++                                            struct sk_buff *skb)
++{
++      u32 src_info = EDMA_RXDESC_SRC_INFO_GET(rxdesc_pri);
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct net_device *ndev = NULL;
++      u8 src_port_num;
++
++      /* Check src_info. */
++      if (likely((src_info & EDMA_RXDESC_SRCINFO_TYPE_MASK)
++          == EDMA_RXDESC_SRCINFO_TYPE_PORTID)) {
++              src_port_num = src_info & EDMA_RXDESC_PORTNUM_BITS;
++      } else {
++              if (net_ratelimit()) {
++                      pr_warn("Invalid src info_type:0x%x. Drop skb:%p\n",
++                              (src_info & EDMA_RXDESC_SRCINFO_TYPE_MASK), skb);
++              }
++
++              u64_stats_update_begin(&rxdesc_stats->syncp);
++              ++rxdesc_stats->src_port_inval_type;
++              u64_stats_update_end(&rxdesc_stats->syncp);
++
++              return NULL;
++      }
++
++      /* Packet with PP source. */
++      if (likely(src_port_num <= hw_info->max_ports)) {
++              if (unlikely(src_port_num < EDMA_START_IFNUM)) {
++                      if (net_ratelimit())
++                              pr_warn("Port number error :%d. Drop skb:%p\n",
++                                      src_port_num, skb);
++
++                      u64_stats_update_begin(&rxdesc_stats->syncp);
++                      ++rxdesc_stats->src_port_inval;
++                      u64_stats_update_end(&rxdesc_stats->syncp);
++
++                      return NULL;
++              }
++
++              /* Get netdev for this port using the source port
++               * number as index into the netdev array. We need to
++               * subtract one since the indices start form '0' and
++               * port numbers start from '1'.
++               */
++              ndev = edma_ctx->netdev_arr[src_port_num - 1];
++      }
++
++      if (likely(ndev))
++              return ndev;
++
++      if (net_ratelimit())
++              pr_warn("Netdev Null src_info_type:0x%x src port num:%d Drop skb:%p\n",
++                      (src_info & EDMA_RXDESC_SRCINFO_TYPE_MASK),
++                      src_port_num, skb);
++
++      u64_stats_update_begin(&rxdesc_stats->syncp);
++      ++rxdesc_stats->src_port_inval_netdev;
++      u64_stats_update_end(&rxdesc_stats->syncp);
++
++      return NULL;
++}
++
++static int edma_rx_reap(struct edma_rxdesc_ring *rxdesc_ring, int budget)
++{
++      struct edma_rxdesc_stats *rxdesc_stats = &rxdesc_ring->rxdesc_stats;
++      u32 alloc_size = rxdesc_ring->rxfill->alloc_size;
++      bool page_mode = rxdesc_ring->rxfill->page_mode;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct edma_rxdesc_pri *next_rxdesc_pri;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct device *dev = ppe_dev->dev;
++      u32 prod_idx, cons_idx, end_idx;
++      u32 work_to_do, work_done = 0;
++      struct sk_buff *next_skb;
++      u32 work_leftover, reg;
++
++      /* Get Rx ring producer and consumer indices. */
++      cons_idx = rxdesc_ring->cons_idx;
++
++      if (likely(rxdesc_ring->work_leftover > EDMA_RX_MAX_PROCESS)) {
++              work_to_do = rxdesc_ring->work_leftover;
++      } else {
++              reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_PROD_IDX(rxdesc_ring->ring_id);
++              regmap_read(regmap, reg, &prod_idx);
++              prod_idx = prod_idx & EDMA_RXDESC_PROD_IDX_MASK;
++              work_to_do = EDMA_DESC_AVAIL_COUNT(prod_idx,
++                                                 cons_idx, EDMA_RX_RING_SIZE);
++              rxdesc_ring->work_leftover = work_to_do;
++      }
++
++      if (work_to_do > budget)
++              work_to_do = budget;
++
++      rxdesc_ring->work_leftover -= work_to_do;
++      end_idx = (cons_idx + work_to_do) & EDMA_RX_RING_SIZE_MASK;
++      next_rxdesc_pri = EDMA_RXDESC_PRI_DESC(rxdesc_ring, cons_idx);
++
++      /* Get opaque from RXDESC. */
++      next_skb = (struct sk_buff *)EDMA_RXDESC_OPAQUE_GET(next_rxdesc_pri);
++
++      work_leftover = work_to_do & (EDMA_RX_MAX_PROCESS - 1);
++      while (likely(work_to_do--)) {
++              struct edma_rxdesc_pri *rxdesc_pri;
++              struct net_device *ndev;
++              struct sk_buff *skb;
++              dma_addr_t dma_addr;
++
++              skb = next_skb;
++              rxdesc_pri = next_rxdesc_pri;
++              dma_addr = EDMA_RXDESC_BUFFER_ADDR_GET(rxdesc_pri);
++
++              if (!page_mode)
++                      dma_unmap_single(dev, dma_addr, alloc_size,
++                                       DMA_TO_DEVICE);
++              else
++                      dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_TO_DEVICE);
++
++              /* Update consumer index. */
++              cons_idx = (cons_idx + 1) & EDMA_RX_RING_SIZE_MASK;
++
++              /* Get the next Rx descriptor. */
++              next_rxdesc_pri = EDMA_RXDESC_PRI_DESC(rxdesc_ring, cons_idx);
++
++              /* Handle linear packets or initial segments first. */
++              if (likely(!(rxdesc_ring->head))) {
++                      ndev = edma_rx_get_src_dev(rxdesc_stats, rxdesc_pri, skb);
++                      if (unlikely(!ndev)) {
++                              dev_kfree_skb_any(skb);
++                              goto next_rx_desc;
++                      }
++
++                      /* Update skb fields for head skb. */
++                      skb->dev = ndev;
++                      skb->skb_iif = ndev->ifindex;
++
++                      /* Handle linear packets. */
++                      if (likely(!EDMA_RXDESC_MORE_BIT_GET(rxdesc_pri))) {
++                              next_skb =
++                                      (struct sk_buff *)EDMA_RXDESC_OPAQUE_GET(next_rxdesc_pri);
++
++                              if (unlikely(!
++                                           edma_rx_handle_linear_packets(rxdesc_ring,
++                                                                         rxdesc_pri, skb)))
++                                      dev_kfree_skb_any(skb);
++
++                              goto next_rx_desc;
++                      }
++              }
++
++              next_skb = (struct sk_buff *)EDMA_RXDESC_OPAQUE_GET(next_rxdesc_pri);
++
++              /* Handle scatter frame processing for first/middle/last segments. */
++              page_mode ? edma_rx_handle_nr_frags(rxdesc_ring, rxdesc_pri, skb) :
++                      edma_rx_handle_frag_list(rxdesc_ring, rxdesc_pri, skb);
++
++next_rx_desc:
++              /* Update work done. */
++              work_done++;
++
++              /* Check if we can refill EDMA_RX_MAX_PROCESS worth buffers,
++               * if yes, refill and update index before continuing.
++               */
++              if (unlikely(!(work_done & (EDMA_RX_MAX_PROCESS - 1)))) {
++                      reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_CONS_IDX(rxdesc_ring->ring_id);
++                      regmap_write(regmap, reg, cons_idx);
++                      rxdesc_ring->cons_idx = cons_idx;
++                      edma_rx_alloc_buffer_list(rxdesc_ring->rxfill, EDMA_RX_MAX_PROCESS);
++              }
++      }
++
++      /* Check if we need to refill and update
++       * index for any buffers before exit.
++       */
++      if (unlikely(work_leftover)) {
++              reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_CONS_IDX(rxdesc_ring->ring_id);
++              regmap_write(regmap, reg, cons_idx);
++              rxdesc_ring->cons_idx = cons_idx;
++              edma_rx_alloc_buffer_list(rxdesc_ring->rxfill, work_leftover);
++      }
++
++      return work_done;
++}
++
++/**
++ * edma_rx_napi_poll - EDMA Rx napi poll.
++ * @napi: NAPI structure
++ * @budget: Rx NAPI budget
++ *
++ * EDMA RX NAPI handler to handle the NAPI poll.
++ *
++ * Return the number of packets processed.
++ */
++int edma_rx_napi_poll(struct napi_struct *napi, int budget)
++{
++      struct edma_rxdesc_ring *rxdesc_ring = (struct edma_rxdesc_ring *)napi;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      int work_done = 0;
++      u32 status, reg;
++
++      do {
++              work_done += edma_rx_reap(rxdesc_ring, budget - work_done);
++              if (likely(work_done >= budget))
++                      return work_done;
++
++              /* Check if there are more packets to process. */
++              reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_INT_STAT(rxdesc_ring->ring_id);
++              regmap_read(regmap, reg, &status);
++              status = status & EDMA_RXDESC_RING_INT_STATUS_MASK;
++      } while (likely(status));
++
++      napi_complete(napi);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_INT_MASK(rxdesc_ring->ring_id);
++      regmap_write(regmap, reg, edma_ctx->intr_info.intr_mask_rx);
++
++      return work_done;
++}
++
++/**
++ * edma_rx_handle_irq - EDMA Rx handle irq.
++ * @irq: Interrupt to handle
++ * @ctx: Context
++ *
++ * Process RX IRQ and schedule NAPI.
++ *
++ * Return IRQ_HANDLED(1) on success.
++ */
++irqreturn_t edma_rx_handle_irq(int irq, void *ctx)
++{
++      struct edma_rxdesc_ring *rxdesc_ring = (struct edma_rxdesc_ring *)ctx;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 reg;
++
++      if (likely(napi_schedule_prep(&rxdesc_ring->napi))) {
++              /* Disable RxDesc interrupt. */
++              reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_INT_MASK(rxdesc_ring->ring_id);
++              regmap_write(regmap, reg, EDMA_MASK_INT_DISABLE);
++              __napi_schedule(&rxdesc_ring->napi);
++      }
++
++      return IRQ_HANDLED;
++}
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_rx.h
+@@ -0,0 +1,287 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef __EDMA_RX__
++#define __EDMA_RX__
++
++#include <linux/netdevice.h>
++
++#define EDMA_RXFILL_RING_PER_CORE_MAX 1
++#define EDMA_RXDESC_RING_PER_CORE_MAX 1
++
++/* Max Rx processing without replenishing RxFill ring. */
++#define EDMA_RX_MAX_PROCESS           32
++
++#define EDMA_RX_SKB_HEADROOM          128
++#define EDMA_RX_QUEUE_START           0
++#define EDMA_RX_BUFFER_SIZE           1984
++#define EDMA_MAX_CORE                 4
++
++#define EDMA_GET_DESC(R, i, type)     (&(((type *)((R)->desc))[(i)]))
++#define EDMA_GET_PDESC(R, i, type)    (&(((type *)((R)->pdesc))[(i)]))
++#define EDMA_GET_SDESC(R, i, type)    (&(((type *)((R)->sdesc))[(i)]))
++#define EDMA_RXFILL_DESC(R, i)                EDMA_GET_DESC(R, i, \
++                                              struct edma_rxfill_desc)
++#define EDMA_RXDESC_PRI_DESC(R, i)    EDMA_GET_PDESC(R, i, \
++                                              struct edma_rxdesc_pri)
++#define EDMA_RXDESC_SEC_DESC(R, i)    EDMA_GET_SDESC(R, i, \
++                                              struct edma_rxdesc_sec)
++
++#define EDMA_RX_RING_SIZE     2048
++
++#define EDMA_RX_RING_SIZE_MASK        (EDMA_RX_RING_SIZE - 1)
++#define EDMA_RX_RING_ID_MASK          0x1F
++
++#define EDMA_MAX_PRI_PER_CORE      8
++#define EDMA_RX_PID_IPV4_MAX          0x3
++#define EDMA_RX_PID_IPV6              0x4
++#define EDMA_RX_PID_IS_IPV4(pid)      (!((pid) & (~EDMA_RX_PID_IPV4_MAX)))
++#define EDMA_RX_PID_IS_IPV6(pid)      (!(!((pid) & EDMA_RX_PID_IPV6)))
++
++#define EDMA_RXDESC_BUFFER_ADDR_GET(desc)     \
++                              ((u32)(le32_to_cpu((__force __le32)((desc)->word0))))
++#define EDMA_RXDESC_OPAQUE_GET(_desc) ({ \
++                      typeof(_desc) (desc) = (_desc); \
++                      ((uintptr_t)((u64)((desc)->word2) | \
++                      ((u64)((desc)->word3) << 0x20))); })
++
++#define EDMA_RXDESC_SRCINFO_TYPE_PORTID               0x2000
++#define EDMA_RXDESC_SRCINFO_TYPE_MASK         0xF000
++#define EDMA_RXDESC_L3CSUM_STATUS_MASK                BIT(13)
++#define EDMA_RXDESC_L4CSUM_STATUS_MASK                BIT(12)
++#define EDMA_RXDESC_PORTNUM_BITS              0x0FFF
++
++#define EDMA_RXDESC_PACKET_LEN_MASK           0x3FFFF
++#define EDMA_RXDESC_PACKET_LEN_GET(_desc) ({ \
++                      typeof(_desc) (desc) = (_desc); \
++                      ((le32_to_cpu((__force __le32)((desc)->word5))) & \
++                      EDMA_RXDESC_PACKET_LEN_MASK); })
++
++#define EDMA_RXDESC_MORE_BIT_MASK             0x40000000
++#define EDMA_RXDESC_MORE_BIT_GET(desc)                ((le32_to_cpu((__force __le32)((desc)->word1))) & \
++                                              EDMA_RXDESC_MORE_BIT_MASK)
++#define EDMA_RXDESC_SRC_DST_INFO_GET(desc)    \
++                              ((u32)((le32_to_cpu((__force __le32)((desc)->word4)))))
++
++#define EDMA_RXDESC_L3_OFFSET_MASK    GENMASK(23, 16)
++#define EDMA_RXDESC_L3_OFFSET_GET(desc)       FIELD_GET(EDMA_RXDESC_L3_OFFSET_MASK, \
++                                      le32_to_cpu((__force __le32)((desc)->word7)))
++
++#define EDMA_RXDESC_PID_MASK          GENMASK(15, 12)
++#define EDMA_RXDESC_PID_GET(desc)     FIELD_GET(EDMA_RXDESC_PID_MASK, \
++                                      le32_to_cpu((__force __le32)((desc)->word7)))
++
++#define EDMA_RXDESC_DST_INFO_MASK     GENMASK(31, 16)
++#define EDMA_RXDESC_DST_INFO_GET(desc)        FIELD_GET(EDMA_RXDESC_DST_INFO_MASK, \
++                                      le32_to_cpu((__force __le32)((desc)->word4)))
++
++#define EDMA_RXDESC_SRC_INFO_MASK     GENMASK(15, 0)
++#define EDMA_RXDESC_SRC_INFO_GET(desc)        FIELD_GET(EDMA_RXDESC_SRC_INFO_MASK, \
++                                      le32_to_cpu((__force __le32)((desc)->word4)))
++
++#define EDMA_RXDESC_PORT_ID_MASK      GENMASK(11, 0)
++#define EDMA_RXDESC_PORT_ID_GET(x)    FIELD_GET(EDMA_RXDESC_PORT_ID_MASK, x)
++
++#define EDMA_RXDESC_SRC_PORT_ID_GET(desc)     (EDMA_RXDESC_PORT_ID_GET \
++                                              (EDMA_RXDESC_SRC_INFO_GET(desc)))
++#define EDMA_RXDESC_DST_PORT_ID_GET(desc)     (EDMA_RXDESC_PORT_ID_GET \
++                                              (EDMA_RXDESC_DST_INFO_GET(desc)))
++
++#define EDMA_RXDESC_DST_PORT          (0x2 << EDMA_RXDESC_PID_SHIFT)
++
++#define EDMA_RXDESC_L3CSUM_STATUS_GET(desc)   FIELD_GET(EDMA_RXDESC_L3CSUM_STATUS_MASK, \
++                                              le32_to_cpu((__force __le32)(desc)->word6))
++#define EDMA_RXDESC_L4CSUM_STATUS_GET(desc)   FIELD_GET(EDMA_RXDESC_L4CSUM_STATUS_MASK, \
++                                              le32_to_cpu((__force __le32)(desc)->word6))
++
++#define EDMA_RXDESC_DATA_OFFSET_MASK          GENMASK(11, 0)
++#define EDMA_RXDESC_DATA_OFFSET_GET(desc)     FIELD_GET(EDMA_RXDESC_DATA_OFFSET_MASK, \
++                                              le32_to_cpu((__force __le32)(desc)->word6))
++
++#define EDMA_RXFILL_BUF_SIZE_MASK             0xFFFF
++#define EDMA_RXFILL_BUF_SIZE_SHIFT            16
++
++/* Opaque values are not accessed by the EDMA HW,
++ * so endianness conversion is not needed.
++ */
++
++#define EDMA_RXFILL_OPAQUE_LO_SET(desc, ptr)  (((desc)->word2) = \
++                                              (u32)(uintptr_t)(ptr))
++#ifdef __LP64__
++#define EDMA_RXFILL_OPAQUE_HI_SET(desc, ptr)  (((desc)->word3) = \
++                                              (u32)((u64)(ptr) >> 0x20))
++#endif
++
++#define EDMA_RXFILL_OPAQUE_GET(_desc) ({ \
++                      typeof(_desc) (desc) = (_desc); \
++                      ((uintptr_t)((u64)((desc)->word2) | \
++                      ((u64)((desc)->word3) << 0x20))); })
++
++#define EDMA_RXFILL_PACKET_LEN_SET(desc, len) { \
++      (((desc)->word1) = (u32)((((u32)len) << EDMA_RXFILL_BUF_SIZE_SHIFT) & \
++                                              0xFFFF0000)); \
++}
++
++#define EDMA_RXFILL_BUFFER_ADDR_SET(desc, addr)       (((desc)->word0) = (u32)(addr))
++
++/* Opaque values are set in word2 and word3, they are not accessed by the EDMA HW,
++ * so endianness conversion is not needed.
++ */
++#define EDMA_RXFILL_ENDIAN_SET(_desc) ({ \
++      typeof(_desc) (desc) = (_desc); \
++      cpu_to_le32s(&((desc)->word0)); \
++      cpu_to_le32s(&((desc)->word1)); \
++})
++
++/* RX DESC size shift to obtain index from descriptor pointer. */
++#define EDMA_RXDESC_SIZE_SHIFT                5
++
++/**
++ * struct edma_rxdesc_stats - RX descriptor ring stats.
++ * @src_port_inval: Invalid source port number
++ * @src_port_inval_type: Source type is not PORT ID
++ * @src_port_inval_netdev: Invalid net device for the source port
++ * @syncp: Synchronization pointer
++ */
++struct edma_rxdesc_stats {
++      u64 src_port_inval;
++      u64 src_port_inval_type;
++      u64 src_port_inval_netdev;
++      struct u64_stats_sync syncp;
++};
++
++/**
++ * struct edma_rxfill_stats - Rx fill descriptor ring stats.
++ * @alloc_failed: Buffer allocation failure count
++ * @page_alloc_failed: Page allocation failure count for page mode
++ * @syncp: Synchronization pointer
++ */
++struct edma_rxfill_stats {
++      u64 alloc_failed;
++      u64 page_alloc_failed;
++      struct u64_stats_sync syncp;
++};
++
++/**
++ * struct edma_rxdesc_pri - Rx descriptor.
++ * @word0: Buffer address
++ * @word1: More bit, priority bit, service code
++ * @word2: Opaque low bits
++ * @word3: Opaque high bits
++ * @word4: Destination and source information
++ * @word5: WiFi QoS, data length
++ * @word6: Hash value, check sum status
++ * @word7: DSCP, packet offsets
++ */
++struct edma_rxdesc_pri {
++      u32 word0;
++      u32 word1;
++      u32 word2;
++      u32 word3;
++      u32 word4;
++      u32 word5;
++      u32 word6;
++      u32 word7;
++};
++
++ /**
++  * struct edma_rxdesc_sec - Rx secondary descriptor.
++  * @word0: Timestamp
++  * @word1: Secondary checksum status
++  * @word2: QoS tag
++  * @word3: Flow index details
++  * @word4: Secondary packet offsets
++  * @word5: Multicast bit, checksum
++  * @word6: SVLAN, CVLAN
++  * @word7: Secondary SVLAN, CVLAN
++  */
++struct edma_rxdesc_sec {
++      u32 word0;
++      u32 word1;
++      u32 word2;
++      u32 word3;
++      u32 word4;
++      u32 word5;
++      u32 word6;
++      u32 word7;
++};
++
++/**
++ * struct edma_rxfill_desc - RxFill descriptor.
++ * @word0: Buffer address
++ * @word1: Buffer size
++ * @word2: Opaque low bits
++ * @word3: Opaque high bits
++ */
++struct edma_rxfill_desc {
++      u32 word0;
++      u32 word1;
++      u32 word2;
++      u32 word3;
++};
++
++/**
++ * struct edma_rxfill_ring - RxFill ring
++ * @ring_id: RxFill ring number
++ * @count: Number of descriptors in the ring
++ * @prod_idx: Ring producer index
++ * @alloc_size: Buffer size to allocate
++ * @desc: Descriptor ring virtual address
++ * @dma: Descriptor ring physical address
++ * @buf_len: Buffer length for rxfill descriptor
++ * @page_mode: Page mode for Rx processing
++ * @rx_fill_stats: Rx fill ring statistics
++ */
++struct edma_rxfill_ring {
++      u32 ring_id;
++      u32 count;
++      u32 prod_idx;
++      u32 alloc_size;
++      struct edma_rxfill_desc *desc;
++      dma_addr_t dma;
++      u32 buf_len;
++      bool page_mode;
++      struct edma_rxfill_stats rxfill_stats;
++};
++
++/**
++ * struct edma_rxdesc_ring - RxDesc ring
++ * @napi: Pointer to napi
++ * @ring_id: Rxdesc ring number
++ * @count: Number of descriptors in the ring
++ * @work_leftover: Leftover descriptors to be processed
++ * @cons_idx: Ring consumer index
++ * @pdesc: Primary descriptor ring virtual address
++ * @pdesc_head: Primary descriptor head in case of scatter-gather frame
++ * @sdesc: Secondary descriptor ring virtual address
++ * @rxdesc_stats: Rx descriptor ring statistics
++ * @rxfill: RxFill ring used
++ * @napi_added: Flag to indicate NAPI add status
++ * @pdma: Primary descriptor ring physical address
++ * @sdma: Secondary descriptor ring physical address
++ * @head: Head of the skb list in case of scatter-gather frame
++ * @last: Last skb of the skb list in case of scatter-gather frame
++ */
++struct edma_rxdesc_ring {
++      struct napi_struct napi;
++      u32 ring_id;
++      u32 count;
++      u32 work_leftover;
++      u32 cons_idx;
++      struct edma_rxdesc_pri *pdesc;
++      struct edma_rxdesc_pri *pdesc_head;
++      struct edma_rxdesc_sec *sdesc;
++      struct edma_rxdesc_stats rxdesc_stats;
++      struct edma_rxfill_ring *rxfill;
++      bool napi_added;
++      dma_addr_t pdma;
++      dma_addr_t sdma;
++      struct sk_buff *head;
++      struct sk_buff *last;
++};
++
++irqreturn_t edma_rx_handle_irq(int irq, void *ctx);
++int edma_rx_alloc_buffer(struct edma_rxfill_ring *rxfill_ring, int alloc_count);
++int edma_rx_napi_poll(struct napi_struct *napi, int budget);
++#endif
diff --git a/target/linux/qualcommbe/patches-6.18/0345-net-ethernet-qualcomm-Add-Tx-Ethernet-DMA-support.patch b/target/linux/qualcommbe/patches-6.18/0345-net-ethernet-qualcomm-Add-Tx-Ethernet-DMA-support.patch
new file mode 100644 (file)
index 0000000..39d300d
--- /dev/null
@@ -0,0 +1,2363 @@
+From 339d3a5365f150a78ed405684e379fee3acdbe90 Mon Sep 17 00:00:00 2001
+From: Suruchi Agarwal <quic_suruchia@quicinc.com>
+Date: Thu, 21 Mar 2024 16:26:29 -0700
+Subject: [PATCH] net: ethernet: qualcomm: Add Tx Ethernet DMA support
+
+Add Tx queues, rings, descriptors configurations and
+DMA support for the EDMA.
+
+Change-Id: Idfb0e1fe5ac494d614097d6c97dd15d63bbce8e6
+Co-developed-by: Pavithra R <quic_pavir@quicinc.com>
+Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
+Signed-off-by: Suruchi Agarwal <quic_suruchia@quicinc.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/Makefile    |   2 +-
+ drivers/net/ethernet/qualcomm/ppe/edma.c      |  97 ++-
+ drivers/net/ethernet/qualcomm/ppe/edma.h      |   7 +
+ .../net/ethernet/qualcomm/ppe/edma_cfg_tx.c   | 648 ++++++++++++++
+ .../net/ethernet/qualcomm/ppe/edma_cfg_tx.h   |  28 +
+ drivers/net/ethernet/qualcomm/ppe/edma_port.c | 136 +++
+ drivers/net/ethernet/qualcomm/ppe/edma_port.h |  35 +
+ drivers/net/ethernet/qualcomm/ppe/edma_tx.c   | 808 ++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/edma_tx.h   | 302 +++++++
+ 9 files changed, 2055 insertions(+), 8 deletions(-)
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_cfg_tx.c
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_cfg_tx.h
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_tx.c
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_tx.h
+
+--- a/drivers/net/ethernet/qualcomm/ppe/Makefile
++++ b/drivers/net/ethernet/qualcomm/ppe/Makefile
+@@ -7,4 +7,4 @@ obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o
+ qcom-ppe-objs := ppe.o ppe_config.o ppe_debugfs.o ppe_port.o
+ #EDMA
+-qcom-ppe-objs += edma.o edma_cfg_rx.o edma_port.o edma_rx.o
++qcom-ppe-objs += edma.o edma_cfg_rx.o edma_cfg_tx.o edma_port.o edma_rx.o edma_tx.o
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.c
+@@ -18,6 +18,7 @@
+ #include <linux/reset.h>
+ #include "edma.h"
++#include "edma_cfg_tx.h"
+ #include "edma_cfg_rx.h"
+ #include "ppe_regs.h"
+@@ -25,6 +26,7 @@
+ /* Global EDMA context. */
+ struct edma_context *edma_ctx;
++static char **edma_txcmpl_irq_name;
+ static char **edma_rxdesc_irq_name;
+ /* Module params. */
+@@ -192,22 +194,59 @@ static int edma_configure_ucast_prio_map
+ static int edma_irq_register(void)
+ {
+       struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
+       struct edma_ring_info *rx = hw_info->rx;
+       int ret;
+       u32 i;
++      /* Request IRQ for TXCMPL rings. */
++      edma_txcmpl_irq_name = kzalloc((sizeof(char *) * txcmpl->num_rings), GFP_KERNEL);
++      if (!edma_txcmpl_irq_name)
++              return -ENOMEM;
++
++      for (i = 0; i < txcmpl->num_rings; i++) {
++              edma_txcmpl_irq_name[i] = kzalloc((sizeof(char *) * EDMA_IRQ_NAME_SIZE),
++                                                GFP_KERNEL);
++              if (!edma_txcmpl_irq_name[i]) {
++                      ret = -ENOMEM;
++                      goto txcmpl_ring_irq_name_alloc_fail;
++              }
++
++              snprintf(edma_txcmpl_irq_name[i], EDMA_IRQ_NAME_SIZE, "edma_txcmpl_%d",
++                       txcmpl->ring_start + i);
++
++              irq_set_status_flags(edma_ctx->intr_info.intr_txcmpl[i], IRQ_DISABLE_UNLAZY);
++
++              ret = request_irq(edma_ctx->intr_info.intr_txcmpl[i],
++                                edma_tx_handle_irq, IRQF_SHARED,
++                                edma_txcmpl_irq_name[i],
++                                (void *)&edma_ctx->txcmpl_rings[i]);
++              if (ret) {
++                      pr_err("TXCMPL ring IRQ:%d request %d failed\n",
++                             edma_ctx->intr_info.intr_txcmpl[i], i);
++                      goto txcmpl_ring_intr_req_fail;
++              }
++
++              pr_debug("TXCMPL ring: %d IRQ:%d request success: %s\n",
++                       txcmpl->ring_start + i,
++                       edma_ctx->intr_info.intr_txcmpl[i],
++                       edma_txcmpl_irq_name[i]);
++      }
++
+       /* Request IRQ for RXDESC rings. */
+       edma_rxdesc_irq_name = kzalloc((sizeof(char *) * rx->num_rings),
+                                      GFP_KERNEL);
+-      if (!edma_rxdesc_irq_name)
+-              return -ENOMEM;
++      if (!edma_rxdesc_irq_name) {
++              ret = -ENOMEM;
++              goto rxdesc_irq_name_alloc_fail;
++      }
+       for (i = 0; i < rx->num_rings; i++) {
+               edma_rxdesc_irq_name[i] = kzalloc((sizeof(char *) * EDMA_IRQ_NAME_SIZE),
+                                                 GFP_KERNEL);
+               if (!edma_rxdesc_irq_name[i]) {
+                       ret = -ENOMEM;
+-                      goto rxdesc_irq_name_alloc_fail;
++                      goto rxdesc_ring_irq_name_alloc_fail;
+               }
+               snprintf(edma_rxdesc_irq_name[i], 20, "edma_rxdesc_%d",
+@@ -236,8 +275,19 @@ static int edma_irq_register(void)
+ rx_desc_ring_intr_req_fail:
+       for (i = 0; i < rx->num_rings; i++)
+               kfree(edma_rxdesc_irq_name[i]);
+-rxdesc_irq_name_alloc_fail:
++rxdesc_ring_irq_name_alloc_fail:
+       kfree(edma_rxdesc_irq_name);
++rxdesc_irq_name_alloc_fail:
++      for (i = 0; i < txcmpl->num_rings; i++) {
++              synchronize_irq(edma_ctx->intr_info.intr_txcmpl[i]);
++              free_irq(edma_ctx->intr_info.intr_txcmpl[i],
++                       (void *)&edma_ctx->txcmpl_rings[i]);
++      }
++txcmpl_ring_intr_req_fail:
++      for (i = 0; i < txcmpl->num_rings; i++)
++              kfree(edma_txcmpl_irq_name[i]);
++txcmpl_ring_irq_name_alloc_fail:
++      kfree(edma_txcmpl_irq_name);
+       return ret;
+ }
+@@ -326,12 +376,22 @@ static int edma_irq_init(void)
+ static int edma_alloc_rings(void)
+ {
++      if (edma_cfg_tx_rings_alloc()) {
++              pr_err("Error in allocating Tx rings\n");
++              return -ENOMEM;
++      }
++
+       if (edma_cfg_rx_rings_alloc()) {
+               pr_err("Error in allocating Rx rings\n");
+-              return -ENOMEM;
++              goto rx_rings_alloc_fail;
+       }
+       return 0;
++
++rx_rings_alloc_fail:
++      edma_cfg_tx_rings_cleanup();
++
++      return -ENOMEM;
+ }
+ static int edma_hw_reset(void)
+@@ -389,7 +449,7 @@ static int edma_hw_configure(void)
+       struct edma_hw_info *hw_info = edma_ctx->hw_info;
+       struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+       struct regmap *regmap = ppe_dev->regmap;
+-      u32 data, reg;
++      u32 data, reg, i;
+       int ret;
+       reg = EDMA_BASE_OFFSET + EDMA_REG_MAS_CTRL_ADDR;
+@@ -439,11 +499,17 @@ static int edma_hw_configure(void)
+       }
+       /* Disable interrupts. */
++      for (i = 1; i <= hw_info->max_ports; i++)
++              edma_cfg_tx_disable_interrupts(i);
++
+       edma_cfg_rx_disable_interrupts();
+       edma_cfg_rx_rings_disable();
+       edma_cfg_rx_ring_mappings();
++      edma_cfg_tx_ring_mappings();
++
++      edma_cfg_tx_rings();
+       ret = edma_cfg_rx_rings();
+       if (ret) {
+@@ -520,6 +586,7 @@ configure_ucast_prio_map_tbl_failed:
+       edma_cfg_rx_napi_delete();
+       edma_cfg_rx_rings_disable();
+ edma_cfg_rx_rings_failed:
++      edma_cfg_tx_rings_cleanup();
+       edma_cfg_rx_rings_cleanup();
+ edma_alloc_rings_failed:
+       free_netdev(edma_ctx->dummy_dev);
+@@ -538,13 +605,27 @@ dummy_dev_alloc_failed:
+ void edma_destroy(struct ppe_device *ppe_dev)
+ {
+       struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
+       struct edma_ring_info *rx = hw_info->rx;
+       u32 i;
+       /* Disable interrupts. */
++      for (i = 1; i <= hw_info->max_ports; i++)
++              edma_cfg_tx_disable_interrupts(i);
++
+       edma_cfg_rx_disable_interrupts();
+-      /* Free IRQ for RXDESC rings. */
++      /* Free IRQ for TXCMPL rings. */
++      for (i = 0; i < txcmpl->num_rings; i++) {
++              synchronize_irq(edma_ctx->intr_info.intr_txcmpl[i]);
++
++              free_irq(edma_ctx->intr_info.intr_txcmpl[i],
++                       (void *)&edma_ctx->txcmpl_rings[i]);
++              kfree(edma_txcmpl_irq_name[i]);
++      }
++      kfree(edma_txcmpl_irq_name);
++
++      /* Free IRQ for RXDESC rings */
+       for (i = 0; i < rx->num_rings; i++) {
+               synchronize_irq(edma_ctx->intr_info.intr_rx[i]);
+               free_irq(edma_ctx->intr_info.intr_rx[i],
+@@ -560,6 +641,7 @@ void edma_destroy(struct ppe_device *ppe
+       edma_cfg_rx_napi_delete();
+       edma_cfg_rx_rings_disable();
+       edma_cfg_rx_rings_cleanup();
++      edma_cfg_tx_rings_cleanup();
+       free_netdev(edma_ctx->dummy_dev);
+       kfree(edma_ctx->netdev_arr);
+@@ -585,6 +667,7 @@ int edma_setup(struct ppe_device *ppe_de
+       edma_ctx->hw_info = &ipq9574_hw_info;
+       edma_ctx->ppe_dev = ppe_dev;
+       edma_ctx->rx_buf_size = rx_buff_size;
++      edma_ctx->tx_requeue_stop = false;
+       /* Configure the EDMA common clocks. */
+       ret = edma_clock_init();
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.h
+@@ -7,6 +7,7 @@
+ #include "ppe_config.h"
+ #include "edma_rx.h"
++#include "edma_tx.h"
+ /* One clock cycle = 1/(EDMA clock frequency in Mhz) micro seconds.
+  *
+@@ -104,8 +105,11 @@ struct edma_intr_info {
+  * @intr_info: EDMA Interrupt info
+  * @rxfill_rings: Rx fill Rings, SW is producer
+  * @rx_rings: Rx Desc Rings, SW is consumer
++ * @tx_rings: Tx Descriptor Ring, SW is producer
++ * @txcmpl_rings: Tx complete Ring, SW is consumer
+  * @rx_page_mode: Page mode enabled or disabled
+  * @rx_buf_size: Rx buffer size for Jumbo MRU
++ * @tx_requeue_stop: Tx requeue stop enabled or disabled
+  */
+ struct edma_context {
+       struct net_device **netdev_arr;
+@@ -115,8 +119,11 @@ struct edma_context {
+       struct edma_intr_info intr_info;
+       struct edma_rxfill_ring *rxfill_rings;
+       struct edma_rxdesc_ring *rx_rings;
++      struct edma_txdesc_ring *tx_rings;
++      struct edma_txcmpl_ring *txcmpl_rings;
+       u32 rx_page_mode;
+       u32 rx_buf_size;
++      bool tx_requeue_stop;
+ };
+ /* Global EDMA context */
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_tx.c
+@@ -0,0 +1,648 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* Configure rings, Buffers and NAPI for transmit path along with
++ * providing APIs to enable, disable, clean and map the Tx rings.
++ */
++
++#include <linux/dma-mapping.h>
++#include <linux/kernel.h>
++#include <linux/netdevice.h>
++#include <linux/printk.h>
++#include <linux/regmap.h>
++#include <linux/skbuff.h>
++
++#include "edma.h"
++#include "edma_cfg_tx.h"
++#include "edma_port.h"
++#include "ppe.h"
++#include "ppe_regs.h"
++
++static void edma_cfg_txcmpl_ring_cleanup(struct edma_txcmpl_ring *txcmpl_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device *dev =  ppe_dev->dev;
++
++      /* Free any buffers assigned to any descriptors. */
++      edma_tx_complete(EDMA_TX_RING_SIZE - 1, txcmpl_ring);
++
++      /* Free TxCmpl ring descriptors. */
++      dma_free_coherent(dev, sizeof(struct edma_txcmpl_desc)
++                        * txcmpl_ring->count, txcmpl_ring->desc,
++                        txcmpl_ring->dma);
++      txcmpl_ring->desc = NULL;
++      txcmpl_ring->dma = (dma_addr_t)0;
++}
++
++static int edma_cfg_txcmpl_ring_setup(struct edma_txcmpl_ring *txcmpl_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device *dev =  ppe_dev->dev;
++
++      /* Allocate RxFill ring descriptors. */
++      txcmpl_ring->desc = dma_alloc_coherent(dev, sizeof(struct edma_txcmpl_desc)
++                                             * txcmpl_ring->count,
++                                             &txcmpl_ring->dma,
++                                             GFP_KERNEL | __GFP_ZERO);
++
++      if (unlikely(!txcmpl_ring->desc))
++              return -ENOMEM;
++
++      return 0;
++}
++
++static void edma_cfg_tx_desc_ring_cleanup(struct edma_txdesc_ring *txdesc_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_txdesc_pri *txdesc = NULL;
++      struct device *dev =  ppe_dev->dev;
++      u32 prod_idx, cons_idx, data, reg;
++      struct sk_buff *skb = NULL;
++
++      /* Free any buffers assigned to any descriptors. */
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_PROD_IDX(txdesc_ring->id);
++      regmap_read(regmap, reg, &data);
++      prod_idx = data & EDMA_TXDESC_PROD_IDX_MASK;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_CONS_IDX(txdesc_ring->id);
++      regmap_read(regmap, reg, &data);
++      cons_idx = data & EDMA_TXDESC_CONS_IDX_MASK;
++
++      /* Walk active list, obtain skb from descriptor and free it. */
++      while (cons_idx != prod_idx) {
++              txdesc = EDMA_TXDESC_PRI_DESC(txdesc_ring, cons_idx);
++              skb = (struct sk_buff *)EDMA_TXDESC_OPAQUE_GET(txdesc);
++              dev_kfree_skb_any(skb);
++
++              cons_idx = ((cons_idx + 1) & EDMA_TX_RING_SIZE_MASK);
++      }
++
++      /* Free Tx ring descriptors. */
++      dma_free_coherent(dev, (sizeof(struct edma_txdesc_pri)
++                        * txdesc_ring->count),
++                        txdesc_ring->pdesc,
++                        txdesc_ring->pdma);
++      txdesc_ring->pdesc = NULL;
++      txdesc_ring->pdma = (dma_addr_t)0;
++
++      /* Free any buffers assigned to any secondary descriptors. */
++      dma_free_coherent(dev, (sizeof(struct edma_txdesc_sec)
++                        * txdesc_ring->count),
++                        txdesc_ring->sdesc,
++                        txdesc_ring->sdma);
++      txdesc_ring->sdesc = NULL;
++      txdesc_ring->sdma = (dma_addr_t)0;
++}
++
++static int edma_cfg_tx_desc_ring_setup(struct edma_txdesc_ring *txdesc_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device *dev = ppe_dev->dev;
++
++      /* Allocate RxFill ring descriptors. */
++      txdesc_ring->pdesc = dma_alloc_coherent(dev, sizeof(struct edma_txdesc_pri)
++                                              * txdesc_ring->count,
++                                              &txdesc_ring->pdma,
++                                              GFP_KERNEL | __GFP_ZERO);
++
++      if (unlikely(!txdesc_ring->pdesc))
++              return -ENOMEM;
++
++      txdesc_ring->sdesc = dma_alloc_coherent(dev, sizeof(struct edma_txdesc_sec)
++                                              * txdesc_ring->count,
++                                              &txdesc_ring->sdma,
++                                              GFP_KERNEL | __GFP_ZERO);
++
++      if (unlikely(!txdesc_ring->sdesc)) {
++              dma_free_coherent(dev, (sizeof(struct edma_txdesc_pri)
++                                * txdesc_ring->count),
++                                txdesc_ring->pdesc,
++                                txdesc_ring->pdma);
++              txdesc_ring->pdesc = NULL;
++              txdesc_ring->pdma = (dma_addr_t)0;
++              return -ENOMEM;
++      }
++
++      return 0;
++}
++
++static void edma_cfg_tx_desc_ring_configure(struct edma_txdesc_ring *txdesc_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 data, reg;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_BA(txdesc_ring->id);
++      regmap_write(regmap, reg, (u32)(txdesc_ring->pdma & EDMA_RING_DMA_MASK));
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_BA2(txdesc_ring->id);
++      regmap_write(regmap, reg, (u32)(txdesc_ring->sdma & EDMA_RING_DMA_MASK));
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_RING_SIZE(txdesc_ring->id);
++      regmap_write(regmap, reg, (u32)(txdesc_ring->count & EDMA_TXDESC_RING_SIZE_MASK));
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_PROD_IDX(txdesc_ring->id);
++      regmap_write(regmap, reg, (u32)EDMA_TX_INITIAL_PROD_IDX);
++
++      data = FIELD_PREP(EDMA_TXDESC_CTRL_FC_GRP_ID_MASK, txdesc_ring->fc_grp_id);
++
++      /* Configure group ID for flow control for this Tx ring. */
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_CTRL(txdesc_ring->id);
++      regmap_write(regmap, reg, data);
++}
++
++static void edma_cfg_txcmpl_ring_configure(struct edma_txcmpl_ring *txcmpl_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 data, reg;
++
++      /* Configure TxCmpl ring base address. */
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXCMPL_BA(txcmpl_ring->id);
++      regmap_write(regmap, reg, (u32)(txcmpl_ring->dma & EDMA_RING_DMA_MASK));
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXCMPL_RING_SIZE(txcmpl_ring->id);
++      regmap_write(regmap, reg, (u32)(txcmpl_ring->count & EDMA_TXDESC_RING_SIZE_MASK));
++
++      /* Set TxCmpl ret mode to opaque. */
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXCMPL_CTRL(txcmpl_ring->id);
++      regmap_write(regmap, reg, EDMA_TXCMPL_RETMODE_OPAQUE);
++
++      /* Configure the Mitigation timer. */
++      data = EDMA_MICROSEC_TO_TIMER_UNIT(EDMA_TX_MITIGATION_TIMER_DEF,
++                                         ppe_dev->clk_rate / MHZ);
++      data = ((data & EDMA_TX_MOD_TIMER_INIT_MASK)
++              << EDMA_TX_MOD_TIMER_INIT_SHIFT);
++      pr_debug("EDMA Tx mitigation timer value: %d\n", data);
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TX_MOD_TIMER(txcmpl_ring->id);
++      regmap_write(regmap, reg, data);
++
++      /* Configure the Mitigation packet count. */
++      data = (EDMA_TX_MITIGATION_PKT_CNT_DEF & EDMA_TXCMPL_LOW_THRE_MASK)
++              << EDMA_TXCMPL_LOW_THRE_SHIFT;
++      pr_debug("EDMA Tx mitigation packet count value: %d\n", data);
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXCMPL_UGT_THRE(txcmpl_ring->id);
++      regmap_write(regmap, reg, data);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TX_INT_CTRL(txcmpl_ring->id);
++      regmap_write(regmap, reg, EDMA_TX_NE_INT_EN);
++}
++
++/**
++ * edma_cfg_tx_fill_per_port_tx_map - Fill Tx ring mapping.
++ * @netdev: Netdevice.
++ * @port_id: Port ID.
++ *
++ * Fill per-port Tx ring mapping in net device private area.
++ */
++void edma_cfg_tx_fill_per_port_tx_map(struct net_device *netdev, u32 port_id)
++{
++      u32 i;
++
++      /* Ring to core mapping is done in order starting from 0 for port 1. */
++      for_each_possible_cpu(i) {
++              struct edma_port_priv *port_dev = (struct edma_port_priv *)netdev_priv(netdev);
++              struct edma_txdesc_ring *txdesc_ring;
++              u32 txdesc_ring_id;
++
++              txdesc_ring_id = ((port_id - 1) * num_possible_cpus()) + i;
++              txdesc_ring = &edma_ctx->tx_rings[txdesc_ring_id];
++              port_dev->txr_map[i] = txdesc_ring;
++      }
++}
++
++/**
++ * edma_cfg_tx_rings_enable - Enable Tx rings.
++ *
++ * Enable Tx rings.
++ */
++void edma_cfg_tx_rings_enable(u32 port_id)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_txdesc_ring *txdesc_ring;
++      u32 i, ring_idx, reg;
++
++      for_each_possible_cpu(i) {
++              ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
++              txdesc_ring = &edma_ctx->tx_rings[ring_idx];
++              u32 data;
++
++              reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_CTRL(txdesc_ring->id);
++              regmap_read(regmap, reg, &data);
++              data |= FIELD_PREP(EDMA_TXDESC_CTRL_TXEN_MASK, EDMA_TXDESC_TX_ENABLE);
++
++              regmap_write(regmap, reg, data);
++      }
++}
++
++/**
++ * edma_cfg_tx_rings_disable - Disable Tx rings.
++ *
++ * Disable Tx rings.
++ */
++void edma_cfg_tx_rings_disable(u32 port_id)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_txdesc_ring *txdesc_ring;
++      u32 i, ring_idx, reg;
++
++      for_each_possible_cpu(i) {
++              ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
++              txdesc_ring = &edma_ctx->tx_rings[ring_idx];
++              u32 data;
++
++              txdesc_ring = &edma_ctx->tx_rings[i];
++              reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_CTRL(txdesc_ring->id);
++              regmap_read(regmap, reg, &data);
++              data &= ~EDMA_TXDESC_TX_ENABLE;
++              regmap_write(regmap, reg, data);
++      }
++}
++
++/**
++ * edma_cfg_tx_ring_mappings - Map Tx to Tx complete rings.
++ *
++ * Map Tx to Tx complete rings.
++ */
++void edma_cfg_tx_ring_mappings(void)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_ring_info *tx = hw_info->tx;
++      u32 desc_index, i, data, reg;
++
++      /* Clear the TXDESC2CMPL_MAP_xx reg before setting up
++       * the mapping. This register holds TXDESC to TXFILL ring
++       * mapping.
++       */
++      regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_0_ADDR, 0);
++      regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_1_ADDR, 0);
++      regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_2_ADDR, 0);
++      regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_3_ADDR, 0);
++      regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_4_ADDR, 0);
++      regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_5_ADDR, 0);
++      desc_index = txcmpl->ring_start;
++
++      /* 6 registers to hold the completion mapping for total 32
++       * TX desc rings (0-5, 6-11, 12-17, 18-23, 24-29 and rest).
++       * In each entry 5 bits hold the mapping for a particular TX desc ring.
++       */
++      for (i = tx->ring_start; i < tx->ring_start + tx->num_rings; i++) {
++              u32 reg, data;
++
++              if (i >= 0 && i <= 5)
++                      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_0_ADDR;
++              else if (i >= 6 && i <= 11)
++                      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_1_ADDR;
++              else if (i >= 12 && i <= 17)
++                      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_2_ADDR;
++              else if (i >= 18 && i <= 23)
++                      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_3_ADDR;
++              else if (i >= 24 && i <= 29)
++                      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_4_ADDR;
++              else
++                      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_5_ADDR;
++
++              pr_debug("Configure Tx desc:%u to use TxCmpl:%u\n", i, desc_index);
++
++              /* Set the Tx complete descriptor ring number in the mapping register.
++               * E.g. If (txcmpl ring)desc_index = 31, (txdesc ring)i = 28.
++               *      reg = EDMA_REG_TXDESC2CMPL_MAP_4_ADDR
++               *      data |= (desc_index & 0x1F) << ((i % 6) * 5);
++               *      data |= (0x1F << 20); -
++               *      This sets 11111 at 20th bit of register EDMA_REG_TXDESC2CMPL_MAP_4_ADDR.
++               */
++              regmap_read(regmap, reg, &data);
++              data |= (desc_index & EDMA_TXDESC2CMPL_MAP_TXDESC_MASK) << ((i % 6) * 5);
++              regmap_write(regmap, reg, data);
++
++              desc_index++;
++              if (desc_index == txcmpl->ring_start + txcmpl->num_rings)
++                      desc_index = txcmpl->ring_start;
++      }
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_0_ADDR;
++      regmap_read(regmap, reg, &data);
++      pr_debug("EDMA_REG_TXDESC2CMPL_MAP_0_ADDR: 0x%x\n", data);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_1_ADDR;
++      regmap_read(regmap, reg, &data);
++      pr_debug("EDMA_REG_TXDESC2CMPL_MAP_1_ADDR: 0x%x\n", data);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_2_ADDR;
++      regmap_read(regmap, reg, &data);
++      pr_debug("EDMA_REG_TXDESC2CMPL_MAP_2_ADDR: 0x%x\n", data);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_3_ADDR;
++      regmap_read(regmap, reg, &data);
++      pr_debug("EDMA_REG_TXDESC2CMPL_MAP_3_ADDR: 0x%x\n", data);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_4_ADDR;
++      regmap_read(regmap, reg, &data);
++      pr_debug("EDMA_REG_TXDESC2CMPL_MAP_4_ADDR: 0x%x\n", data);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_5_ADDR;
++      regmap_read(regmap, reg, &data);
++      pr_debug("EDMA_REG_TXDESC2CMPL_MAP_5_ADDR: 0x%x\n", data);
++}
++
++static int edma_cfg_tx_rings_setup(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
++      struct edma_ring_info *tx = hw_info->tx;
++      u32 i, j = 0;
++
++      /* Set Txdesc flow control group id, same as port number. */
++      for (i = 0; i < hw_info->max_ports; i++) {
++              for_each_possible_cpu(j) {
++                      struct edma_txdesc_ring *txdesc_ring = NULL;
++                      u32 txdesc_idx = (i * num_possible_cpus()) + j;
++
++                      txdesc_ring = &edma_ctx->tx_rings[txdesc_idx];
++                      txdesc_ring->fc_grp_id = i + 1;
++              }
++      }
++
++      /* Allocate TxDesc ring descriptors. */
++      for (i = 0; i < tx->num_rings; i++) {
++              struct edma_txdesc_ring *txdesc_ring = NULL;
++              int ret;
++
++              txdesc_ring = &edma_ctx->tx_rings[i];
++              txdesc_ring->count = EDMA_TX_RING_SIZE;
++              txdesc_ring->id = tx->ring_start + i;
++
++              ret = edma_cfg_tx_desc_ring_setup(txdesc_ring);
++              if (ret) {
++                      pr_err("Error in setting up %d txdesc ring. ret: %d",
++                             txdesc_ring->id, ret);
++                      while (i-- >= 0)
++                              edma_cfg_tx_desc_ring_cleanup(&edma_ctx->tx_rings[i]);
++
++                      return -ENOMEM;
++              }
++      }
++
++      /* Allocate TxCmpl ring descriptors. */
++      for (i = 0; i < txcmpl->num_rings; i++) {
++              struct edma_txcmpl_ring *txcmpl_ring = NULL;
++              int ret;
++
++              txcmpl_ring = &edma_ctx->txcmpl_rings[i];
++              txcmpl_ring->count = EDMA_TX_RING_SIZE;
++              txcmpl_ring->id = txcmpl->ring_start + i;
++
++              ret = edma_cfg_txcmpl_ring_setup(txcmpl_ring);
++              if (ret != 0) {
++                      pr_err("Error in setting up %d TxCmpl ring. ret: %d",
++                             txcmpl_ring->id, ret);
++                      while (i-- >= 0)
++                              edma_cfg_txcmpl_ring_cleanup(&edma_ctx->txcmpl_rings[i]);
++
++                      goto txcmpl_mem_alloc_fail;
++              }
++      }
++
++      pr_debug("Tx descriptor count for Tx desc and Tx complete rings: %d\n",
++               EDMA_TX_RING_SIZE);
++
++      return 0;
++
++txcmpl_mem_alloc_fail:
++      for (i = 0; i < tx->num_rings; i++)
++              edma_cfg_tx_desc_ring_cleanup(&edma_ctx->tx_rings[i]);
++
++      return -ENOMEM;
++}
++
++/**
++ * edma_cfg_tx_rings_alloc - Allocate EDMA Tx rings.
++ *
++ * Allocate EDMA Tx rings.
++ */
++int edma_cfg_tx_rings_alloc(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
++      struct edma_ring_info *tx = hw_info->tx;
++
++      edma_ctx->tx_rings = kzalloc((sizeof(*edma_ctx->tx_rings) * tx->num_rings),
++                                   GFP_KERNEL);
++      if (!edma_ctx->tx_rings)
++              return -ENOMEM;
++
++      edma_ctx->txcmpl_rings = kzalloc((sizeof(*edma_ctx->txcmpl_rings) * txcmpl->num_rings),
++                                       GFP_KERNEL);
++      if (!edma_ctx->txcmpl_rings)
++              goto txcmpl_ring_alloc_fail;
++
++      pr_debug("Num rings - TxDesc:%u (%u-%u) TxCmpl:%u (%u-%u)\n",
++               tx->num_rings, tx->ring_start,
++              (tx->ring_start + tx->num_rings - 1),
++              txcmpl->num_rings, txcmpl->ring_start,
++              (txcmpl->ring_start + txcmpl->num_rings - 1));
++
++      if (edma_cfg_tx_rings_setup()) {
++              pr_err("Error in setting up tx rings\n");
++              goto tx_rings_setup_fail;
++      }
++
++      return 0;
++
++tx_rings_setup_fail:
++      kfree(edma_ctx->txcmpl_rings);
++      edma_ctx->txcmpl_rings = NULL;
++
++txcmpl_ring_alloc_fail:
++      kfree(edma_ctx->tx_rings);
++      edma_ctx->tx_rings = NULL;
++
++      return -ENOMEM;
++}
++
++/**
++ * edma_cfg_tx_rings_cleanup - Cleanup EDMA Tx rings.
++ *
++ * Cleanup EDMA Tx rings.
++ */
++void edma_cfg_tx_rings_cleanup(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
++      struct edma_ring_info *tx = hw_info->tx;
++      u32 i;
++
++      /* Free any buffers assigned to any descriptors. */
++      for (i = 0; i < tx->num_rings; i++)
++              edma_cfg_tx_desc_ring_cleanup(&edma_ctx->tx_rings[i]);
++
++      /* Free Tx completion descriptors. */
++      for (i = 0; i < txcmpl->num_rings; i++)
++              edma_cfg_txcmpl_ring_cleanup(&edma_ctx->txcmpl_rings[i]);
++
++      kfree(edma_ctx->tx_rings);
++      kfree(edma_ctx->txcmpl_rings);
++      edma_ctx->tx_rings = NULL;
++      edma_ctx->txcmpl_rings = NULL;
++}
++
++/**
++ * edma_cfg_tx_rings - Configure EDMA Tx rings.
++ *
++ * Configure EDMA Tx rings.
++ */
++void edma_cfg_tx_rings(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
++      struct edma_ring_info *tx = hw_info->tx;
++      u32 i;
++
++      /* Configure Tx desc ring. */
++      for (i = 0; i < tx->num_rings; i++)
++              edma_cfg_tx_desc_ring_configure(&edma_ctx->tx_rings[i]);
++
++      /* Configure TxCmpl ring. */
++      for (i = 0; i < txcmpl->num_rings; i++)
++              edma_cfg_txcmpl_ring_configure(&edma_ctx->txcmpl_rings[i]);
++}
++
++/**
++ * edma_cfg_tx_disable_interrupts - EDMA disable TX interrupts.
++ *
++ * Disable TX interrupt masks.
++ */
++void edma_cfg_tx_disable_interrupts(u32 port_id)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_txcmpl_ring *txcmpl_ring;
++      u32 i, ring_idx, reg;
++
++      for_each_possible_cpu(i) {
++              ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
++              txcmpl_ring = &edma_ctx->txcmpl_rings[ring_idx];
++              reg = EDMA_BASE_OFFSET + EDMA_REG_TX_INT_MASK(txcmpl_ring->id);
++              regmap_write(regmap, reg, EDMA_MASK_INT_CLEAR);
++      }
++}
++
++/**
++ * edma_cfg_tx_enable_interrupts - EDMA enable TX interrupts.
++ *
++ * Enable TX interrupt masks.
++ */
++void edma_cfg_tx_enable_interrupts(u32 port_id)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_txcmpl_ring *txcmpl_ring;
++      u32 i, ring_idx, reg;
++
++      for_each_possible_cpu(i) {
++              ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
++              txcmpl_ring = &edma_ctx->txcmpl_rings[ring_idx];
++              reg = EDMA_BASE_OFFSET + EDMA_REG_TX_INT_MASK(txcmpl_ring->id);
++              regmap_write(regmap, reg, edma_ctx->intr_info.intr_mask_txcmpl);
++      }
++}
++
++/**
++ * edma_cfg_tx_napi_enable - EDMA Tx NAPI.
++ * @port_id: Port ID.
++ *
++ * Enable Tx NAPI.
++ */
++void edma_cfg_tx_napi_enable(u32 port_id)
++{
++      struct edma_txcmpl_ring *txcmpl_ring;
++      u32 i, ring_idx;
++
++      /* Enabling Tx napi for a interface with each queue. */
++      for_each_possible_cpu(i) {
++              ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
++              txcmpl_ring = &edma_ctx->txcmpl_rings[ring_idx];
++              if (!txcmpl_ring->napi_added)
++                      continue;
++
++              napi_enable(&txcmpl_ring->napi);
++      }
++}
++
++/**
++ * edma_cfg_tx_napi_disable - Disable Tx NAPI.
++ * @port_id: Port ID.
++ *
++ * Disable Tx NAPI.
++ */
++void edma_cfg_tx_napi_disable(u32 port_id)
++{
++      struct edma_txcmpl_ring *txcmpl_ring;
++      u32 i, ring_idx;
++
++      /* Disabling Tx napi for a interface with each queue. */
++      for_each_possible_cpu(i) {
++              ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
++              txcmpl_ring = &edma_ctx->txcmpl_rings[ring_idx];
++              if (!txcmpl_ring->napi_added)
++                      continue;
++
++              napi_disable(&txcmpl_ring->napi);
++      }
++}
++
++/**
++ * edma_cfg_tx_napi_delete - Delete Tx NAPI.
++ * @port_id: Port ID.
++ *
++ * Delete Tx NAPI.
++ */
++void edma_cfg_tx_napi_delete(u32 port_id)
++{
++      struct edma_txcmpl_ring *txcmpl_ring;
++      u32 i, ring_idx;
++
++      /* Disabling Tx napi for a interface with each queue. */
++      for_each_possible_cpu(i) {
++              ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
++              txcmpl_ring = &edma_ctx->txcmpl_rings[ring_idx];
++              if (!txcmpl_ring->napi_added)
++                      continue;
++
++              netif_napi_del(&txcmpl_ring->napi);
++              txcmpl_ring->napi_added = false;
++      }
++}
++
++/**
++ * edma_cfg_tx_napi_add - TX NAPI add.
++ * @netdev: Netdevice.
++ * @port_id: Port ID.
++ *
++ * TX NAPI add.
++ */
++void edma_cfg_tx_napi_add(struct net_device *netdev, u32 port_id)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_txcmpl_ring *txcmpl_ring;
++      u32 i, ring_idx;
++
++      /* Adding tx napi for a interface with each queue. */
++      for_each_possible_cpu(i) {
++              ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
++              txcmpl_ring = &edma_ctx->txcmpl_rings[ring_idx];
++              netif_napi_add_weight(netdev, &txcmpl_ring->napi,
++                                    edma_tx_napi_poll, hw_info->napi_budget_tx);
++              txcmpl_ring->napi_added = true;
++              netdev_dbg(netdev, "Napi added for txcmpl ring: %u\n", txcmpl_ring->id);
++      }
++
++      netdev_dbg(netdev, "Tx NAPI budget: %d\n", hw_info->napi_budget_tx);
++}
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_tx.h
+@@ -0,0 +1,28 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef __EDMA_CFG_TX__
++#define __EDMA_CFG_TX__
++
++/* Tx mitigation timer's default value. */
++#define EDMA_TX_MITIGATION_TIMER_DEF  250
++
++/* Tx mitigation packet count default value. */
++#define EDMA_TX_MITIGATION_PKT_CNT_DEF        16
++
++void edma_cfg_tx_rings(void);
++int edma_cfg_tx_rings_alloc(void);
++void edma_cfg_tx_rings_cleanup(void);
++void edma_cfg_tx_disable_interrupts(u32 port_id);
++void edma_cfg_tx_enable_interrupts(u32 port_id);
++void edma_cfg_tx_napi_enable(u32 port_id);
++void edma_cfg_tx_napi_disable(u32 port_id);
++void edma_cfg_tx_napi_delete(u32 port_id);
++void edma_cfg_tx_napi_add(struct net_device *netdevice, u32 macid);
++void edma_cfg_tx_ring_mappings(void);
++void edma_cfg_txcmpl_mapping_fill(void);
++void edma_cfg_tx_rings_enable(u32 port_id);
++void edma_cfg_tx_rings_disable(u32 port_id);
++void edma_cfg_tx_fill_per_port_tx_map(struct net_device *netdev, u32 macid);
++#endif
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_port.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.c
+@@ -13,6 +13,7 @@
+ #include "edma.h"
+ #include "edma_cfg_rx.h"
++#include "edma_cfg_tx.h"
+ #include "edma_port.h"
+ #include "ppe_regs.h"
+@@ -35,6 +36,15 @@ static int edma_port_stats_alloc(struct
+               return -ENOMEM;
+       }
++      port_priv->pcpu_stats.tx_stats =
++              netdev_alloc_pcpu_stats(struct edma_port_tx_stats);
++      if (!port_priv->pcpu_stats.tx_stats) {
++              netdev_err(netdev, "Per-cpu EDMA Tx stats alloc failed for %s\n",
++                         netdev->name);
++              free_percpu(port_priv->pcpu_stats.rx_stats);
++              return -ENOMEM;
++      }
++
+       return 0;
+ }
+@@ -43,6 +53,28 @@ static void edma_port_stats_free(struct
+       struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
+       free_percpu(port_priv->pcpu_stats.rx_stats);
++      free_percpu(port_priv->pcpu_stats.tx_stats);
++}
++
++static void edma_port_configure(struct net_device *netdev)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *port =  port_priv->ppe_port;
++      int port_id = port->port_id;
++
++      edma_cfg_tx_fill_per_port_tx_map(netdev, port_id);
++      edma_cfg_tx_rings_enable(port_id);
++      edma_cfg_tx_napi_add(netdev, port_id);
++}
++
++static void edma_port_deconfigure(struct net_device *netdev)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *port =  port_priv->ppe_port;
++      int port_id = port->port_id;
++
++      edma_cfg_tx_napi_delete(port_id);
++      edma_cfg_tx_rings_disable(port_id);
+ }
+ static u16 __maybe_unused edma_port_select_queue(__maybe_unused struct net_device *netdev,
+@@ -60,6 +92,7 @@ static int edma_port_open(struct net_dev
+ {
+       struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
+       struct ppe_port *ppe_port;
++      int port_id;
+       if (!port_priv)
+               return -EINVAL;
+@@ -74,10 +107,14 @@ static int edma_port_open(struct net_dev
+       netdev->wanted_features |= EDMA_NETDEV_FEATURES;
+       ppe_port  = port_priv->ppe_port;
++      port_id = ppe_port->port_id;
+       if (ppe_port->phylink)
+               phylink_start(ppe_port->phylink);
++      edma_cfg_tx_napi_enable(port_id);
++      edma_cfg_tx_enable_interrupts(port_id);
++
+       netif_start_queue(netdev);
+       return 0;
+@@ -87,13 +124,21 @@ static int edma_port_close(struct net_de
+ {
+       struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
+       struct ppe_port *ppe_port;
++      int port_id;
+       if (!port_priv)
+               return -EINVAL;
+       netif_stop_queue(netdev);
++      /* 20ms delay would provide a plenty of margin to take care of in-flight packets. */
++      msleep(20);
++
+       ppe_port  = port_priv->ppe_port;
++      port_id = ppe_port->port_id;
++
++      edma_cfg_tx_disable_interrupts(port_id);
++      edma_cfg_tx_napi_disable(port_id);
+       /* Phylink close. */
+       if (ppe_port->phylink)
+@@ -137,6 +182,92 @@ static netdev_features_t edma_port_featu
+       return features;
+ }
++static netdev_tx_t edma_port_xmit(struct sk_buff *skb,
++                                struct net_device *dev)
++{
++      struct edma_port_priv *port_priv = NULL;
++      struct edma_port_pcpu_stats *pcpu_stats;
++      struct edma_txdesc_ring *txdesc_ring;
++      struct edma_port_tx_stats *stats;
++      enum edma_tx_gso_status result;
++      struct sk_buff *segs = NULL;
++      u8 cpu_id;
++      u32 skbq;
++      int ret;
++
++      if (!skb || !dev)
++              return NETDEV_TX_OK;
++
++      port_priv = netdev_priv(dev);
++
++      /* Select a TX ring. */
++      skbq = (skb_get_queue_mapping(skb) & (num_possible_cpus() - 1));
++
++      txdesc_ring = (struct edma_txdesc_ring *)port_priv->txr_map[skbq];
++
++      pcpu_stats = &port_priv->pcpu_stats;
++      stats = this_cpu_ptr(pcpu_stats->tx_stats);
++
++      /* HW does not support TSO for packets with more than or equal to
++       * 32 segments. Perform SW GSO for such packets.
++       */
++      result = edma_tx_gso_segment(skb, dev, &segs);
++      if (likely(result == EDMA_TX_GSO_NOT_NEEDED)) {
++              /* Transmit the packet. */
++              ret = edma_tx_ring_xmit(dev, skb, txdesc_ring, stats);
++
++              if (unlikely(ret == EDMA_TX_FAIL_NO_DESC)) {
++                      if (likely(!edma_ctx->tx_requeue_stop)) {
++                              cpu_id = smp_processor_id();
++                              netdev_dbg(dev, "Stopping tx queue due to lack oftx descriptors\n");
++                              u64_stats_update_begin(&stats->syncp);
++                              ++stats->tx_queue_stopped[cpu_id];
++                              u64_stats_update_end(&stats->syncp);
++                              netif_tx_stop_queue(netdev_get_tx_queue(dev, skbq));
++                              return NETDEV_TX_BUSY;
++                      }
++              }
++
++              if (unlikely(ret != EDMA_TX_OK)) {
++                      dev_kfree_skb_any(skb);
++                      u64_stats_update_begin(&stats->syncp);
++                      ++stats->tx_drops;
++                      u64_stats_update_end(&stats->syncp);
++              }
++
++              return NETDEV_TX_OK;
++      } else if (unlikely(result == EDMA_TX_GSO_FAIL)) {
++              netdev_dbg(dev, "%p: SW GSO failed for segment size: %d\n",
++                         skb, skb_shinfo(skb)->gso_segs);
++              dev_kfree_skb_any(skb);
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->tx_gso_drop_pkts;
++              u64_stats_update_end(&stats->syncp);
++              return NETDEV_TX_OK;
++      }
++
++      u64_stats_update_begin(&stats->syncp);
++      ++stats->tx_gso_pkts;
++      u64_stats_update_end(&stats->syncp);
++
++      dev_kfree_skb_any(skb);
++      while (segs) {
++              skb = segs;
++              segs = segs->next;
++
++              /* Transmit the packet. */
++              ret = edma_tx_ring_xmit(dev, skb, txdesc_ring, stats);
++              if (unlikely(ret != EDMA_TX_OK)) {
++                      dev_kfree_skb_any(skb);
++                      u64_stats_update_begin(&stats->syncp);
++                      ++stats->tx_drops;
++                      u64_stats_update_end(&stats->syncp);
++              }
++      }
++
++      return NETDEV_TX_OK;
++}
++
+ static void edma_port_get_stats64(struct net_device *netdev,
+                                 struct rtnl_link_stats64 *stats)
+ {
+@@ -179,6 +310,7 @@ static int edma_port_set_mac_address(str
+ static const struct net_device_ops edma_port_netdev_ops = {
+       .ndo_open = edma_port_open,
+       .ndo_stop = edma_port_close,
++      .ndo_start_xmit = edma_port_xmit,
+       .ndo_get_stats64 = edma_port_get_stats64,
+       .ndo_set_mac_address = edma_port_set_mac_address,
+       .ndo_validate_addr = eth_validate_addr,
+@@ -199,6 +331,7 @@ void edma_port_destroy(struct ppe_port *
+       int port_id = port->port_id;
+       struct net_device *netdev = edma_ctx->netdev_arr[port_id - 1];
++      edma_port_deconfigure(netdev);
+       edma_port_stats_free(netdev);
+       unregister_netdev(netdev);
+       free_netdev(netdev);
+@@ -276,6 +409,8 @@ int edma_port_setup(struct ppe_port *por
+        */
+       edma_ctx->netdev_arr[port_id - 1] = netdev;
++      edma_port_configure(netdev);
++
+       /* Setup phylink. */
+       ret = ppe_port_phylink_setup(port, netdev);
+       if (ret) {
+@@ -298,6 +433,7 @@ int edma_port_setup(struct ppe_port *por
+ register_netdev_fail:
+       ppe_port_phylink_destroy(port);
+ port_phylink_setup_fail:
++      edma_port_deconfigure(netdev);
+       edma_ctx->netdev_arr[port_id - 1] = NULL;
+       edma_port_stats_free(netdev);
+ stats_alloc_fail:
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_port.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.h
+@@ -7,6 +7,8 @@
+ #include "ppe_port.h"
++#define EDMA_PORT_MAX_CORE            4
++
+ #define EDMA_NETDEV_FEATURES          (NETIF_F_FRAGLIST \
+                                       | NETIF_F_SG \
+                                       | NETIF_F_RXCSUM \
+@@ -35,11 +37,43 @@ struct edma_port_rx_stats {
+ };
+ /**
++ * struct edma_port_tx_stats - EDMA TX port per CPU stats for the port.
++ * @tx_pkts: Number of Tx packets
++ * @tx_bytes: Number of Tx bytes
++ * @tx_drops: Number of Tx drops
++ * @tx_nr_frag_pkts: Number of Tx nr_frag packets
++ * @tx_fraglist_pkts: Number of Tx fraglist packets
++ * @tx_fraglist_with_nr_frags_pkts:  Number of Tx packets with fraglist and nr_frags
++ * @tx_tso_pkts: Number of Tx TSO packets
++ * @tx_tso_drop_pkts: Number of Tx TSO drop packets
++ * @tx_gso_pkts: Number of Tx GSO packets
++ * @tx_gso_drop_pkts: Number of Tx GSO drop packets
++ * @tx_queue_stopped: Number of Tx queue stopped packets
++ * @syncp: Synchronization pointer
++ */
++struct edma_port_tx_stats {
++      u64 tx_pkts;
++      u64 tx_bytes;
++      u64 tx_drops;
++      u64 tx_nr_frag_pkts;
++      u64 tx_fraglist_pkts;
++      u64 tx_fraglist_with_nr_frags_pkts;
++      u64 tx_tso_pkts;
++      u64 tx_tso_drop_pkts;
++      u64 tx_gso_pkts;
++      u64 tx_gso_drop_pkts;
++      u64 tx_queue_stopped[EDMA_PORT_MAX_CORE];
++      struct u64_stats_sync syncp;
++};
++
++/**
+  * struct edma_port_pcpu_stats - EDMA per cpu stats data structure for the port.
+  * @rx_stats: Per CPU Rx statistics
++ * @tx_stats: Per CPU Tx statistics
+  */
+ struct edma_port_pcpu_stats {
+       struct edma_port_rx_stats __percpu *rx_stats;
++      struct edma_port_tx_stats __percpu *tx_stats;
+ };
+ /**
+@@ -54,6 +88,7 @@ struct edma_port_priv {
+       struct ppe_port *ppe_port;
+       struct net_device *netdev;
+       struct edma_port_pcpu_stats pcpu_stats;
++      struct edma_txdesc_ring *txr_map[EDMA_PORT_MAX_CORE];
+       unsigned long flags;
+ };
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_tx.c
+@@ -0,0 +1,808 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* Provide APIs to alloc Tx Buffers, fill the Tx descriptors and transmit
++ * Scatter Gather and linear packets, Tx complete to free the skb after transmit.
++ */
++
++#include <linux/dma-mapping.h>
++#include <linux/kernel.h>
++#include <linux/netdevice.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <net/gso.h>
++#include <linux/regmap.h>
++
++#include "edma.h"
++#include "edma_cfg_tx.h"
++#include "edma_port.h"
++#include "ppe.h"
++#include "ppe_regs.h"
++
++static u32 edma_tx_num_descs_for_sg(struct sk_buff *skb)
++{
++      u32 nr_frags_first = 0, num_tx_desc_needed = 0;
++
++      /* Check if we have enough Tx descriptors for SG. */
++      if (unlikely(skb_shinfo(skb)->nr_frags)) {
++              nr_frags_first = skb_shinfo(skb)->nr_frags;
++              WARN_ON_ONCE(nr_frags_first > MAX_SKB_FRAGS);
++              num_tx_desc_needed += nr_frags_first;
++      }
++
++      /* Walk through fraglist skbs making a note of nr_frags
++       * One Tx desc for fraglist skb. Fraglist skb may have
++       * further nr_frags.
++       */
++      if (unlikely(skb_has_frag_list(skb))) {
++              struct sk_buff *iter_skb;
++
++              skb_walk_frags(skb, iter_skb) {
++                      u32 nr_frags = skb_shinfo(iter_skb)->nr_frags;
++
++                      WARN_ON_ONCE(nr_frags > MAX_SKB_FRAGS);
++                      num_tx_desc_needed += (1 + nr_frags);
++              }
++      }
++
++      return (num_tx_desc_needed + 1);
++}
++
++/**
++ * edma_tx_gso_segment - Tx GSO.
++ * @skb: Socket Buffer.
++ * @netdev: Netdevice.
++ * @segs: SKB segments from GSO.
++ *
++ * Format skbs into GSOs.
++ *
++ * Return 1 on success, error code on failure.
++ */
++enum edma_tx_gso_status edma_tx_gso_segment(struct sk_buff *skb,
++                                          struct net_device *netdev, struct sk_buff **segs)
++{
++      u32 num_tx_desc_needed;
++
++      /* Check is skb is non-linear to proceed. */
++      if (likely(!skb_is_nonlinear(skb)))
++              return EDMA_TX_GSO_NOT_NEEDED;
++
++      /* Check if TSO is enabled. If so, return as skb doesn't
++       * need to be segmented by linux.
++       */
++      if (netdev->features & (NETIF_F_TSO | NETIF_F_TSO6)) {
++              num_tx_desc_needed = edma_tx_num_descs_for_sg(skb);
++              if (likely(num_tx_desc_needed <= EDMA_TX_TSO_SEG_MAX))
++                      return EDMA_TX_GSO_NOT_NEEDED;
++      }
++
++      /* GSO segmentation of the skb into multiple segments. */
++      *segs = skb_gso_segment(skb, netdev->features
++              & ~(NETIF_F_TSO | NETIF_F_TSO6));
++
++      /* Check for error in GSO segmentation. */
++      if (IS_ERR_OR_NULL(*segs)) {
++              netdev_info(netdev, "Tx gso fail\n");
++              return EDMA_TX_GSO_FAIL;
++      }
++
++      return EDMA_TX_GSO_SUCCEED;
++}
++
++/**
++ * edma_tx_complete - Reap Tx completion descriptors.
++ * @work_to_do: Work to do.
++ * @txcmpl_ring: Tx Completion ring.
++ *
++ * Reap Tx completion descriptors of the transmitted
++ * packets and free the corresponding SKBs.
++ *
++ * Return the number descriptors for which Tx complete is done.
++ */
++u32 edma_tx_complete(u32 work_to_do, struct edma_txcmpl_ring *txcmpl_ring)
++{
++      struct edma_txcmpl_stats *txcmpl_stats = &txcmpl_ring->txcmpl_stats;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 cons_idx, end_idx, data, cpu_id;
++      struct device *dev = ppe_dev->dev;
++      u32 avail, count, txcmpl_errors;
++      struct edma_txcmpl_desc *txcmpl;
++      u32 prod_idx = 0, more_bit = 0;
++      struct netdev_queue *nq;
++      struct sk_buff *skb;
++      u32 reg;
++
++      cons_idx = txcmpl_ring->cons_idx;
++
++      if (likely(txcmpl_ring->avail_pkt >= work_to_do)) {
++              avail = work_to_do;
++      } else {
++              /* Get TXCMPL ring producer index. */
++              reg = EDMA_BASE_OFFSET + EDMA_REG_TXCMPL_PROD_IDX(txcmpl_ring->id);
++              regmap_read(regmap, reg, &data);
++              prod_idx = data & EDMA_TXCMPL_PROD_IDX_MASK;
++
++              avail = EDMA_DESC_AVAIL_COUNT(prod_idx, cons_idx, EDMA_TX_RING_SIZE);
++              txcmpl_ring->avail_pkt = avail;
++
++              if (unlikely(!avail)) {
++                      dev_dbg(dev, "No available descriptors are pending for %d txcmpl ring\n",
++                              txcmpl_ring->id);
++                      u64_stats_update_begin(&txcmpl_stats->syncp);
++                      ++txcmpl_stats->no_pending_desc;
++                      u64_stats_update_end(&txcmpl_stats->syncp);
++                      return 0;
++              }
++
++              avail = min(avail, work_to_do);
++      }
++
++      count = avail;
++
++      end_idx = (cons_idx + avail) & EDMA_TX_RING_SIZE_MASK;
++      txcmpl = EDMA_TXCMPL_DESC(txcmpl_ring, cons_idx);
++
++      /* Instead of freeing the skb, it might be better to save and use
++       * for Rxfill.
++       */
++      while (likely(avail--)) {
++              /* The last descriptor holds the SKB pointer for scattered frames.
++               * So skip the descriptors with more bit set.
++               */
++              more_bit = EDMA_TXCMPL_MORE_BIT_GET(txcmpl);
++              if (unlikely(more_bit)) {
++                      u64_stats_update_begin(&txcmpl_stats->syncp);
++                      ++txcmpl_stats->desc_with_more_bit;
++                      u64_stats_update_end(&txcmpl_stats->syncp);
++                      cons_idx = ((cons_idx + 1) & EDMA_TX_RING_SIZE_MASK);
++                      txcmpl = EDMA_TXCMPL_DESC(txcmpl_ring, cons_idx);
++                      continue;
++              }
++
++              /* Find and free the skb for Tx completion. */
++              skb = (struct sk_buff *)EDMA_TXCMPL_OPAQUE_GET(txcmpl);
++              if (unlikely(!skb)) {
++                      if (net_ratelimit())
++                              dev_warn(dev, "Invalid cons_idx:%u prod_idx:%u word2:%x word3:%x\n",
++                                       cons_idx, prod_idx, txcmpl->word2, txcmpl->word3);
++
++                      u64_stats_update_begin(&txcmpl_stats->syncp);
++                      ++txcmpl_stats->invalid_buffer;
++                      u64_stats_update_end(&txcmpl_stats->syncp);
++              } else {
++                      dev_dbg(dev, "TXCMPL: skb:%p, skb->len %d, skb->data_len %d, cons_idx:%d prod_idx:%d word2:0x%x word3:0x%x\n",
++                              skb, skb->len, skb->data_len, cons_idx, prod_idx,
++                              txcmpl->word2, txcmpl->word3);
++
++                      txcmpl_errors = EDMA_TXCOMP_RING_ERROR_GET(txcmpl->word3);
++                      if (unlikely(txcmpl_errors)) {
++                              if (net_ratelimit())
++                                      dev_err(dev, "Error 0x%0x observed in tx complete %d ring\n",
++                                              txcmpl_errors, txcmpl_ring->id);
++
++                              u64_stats_update_begin(&txcmpl_stats->syncp);
++                              ++txcmpl_stats->errors;
++                              u64_stats_update_end(&txcmpl_stats->syncp);
++                      }
++
++                      /* Retrieve pool id for unmapping.
++                       * 0 for linear skb and (pool id - 1) represents nr_frag index.
++                       */
++                      if (!EDMA_TXCOMP_POOL_ID_GET(txcmpl)) {
++                              dma_unmap_single(dev, virt_to_phys(skb->data),
++                                               skb->len, DMA_TO_DEVICE);
++                      } else {
++                              u8 frag_index = (EDMA_TXCOMP_POOL_ID_GET(txcmpl) - 1);
++                              skb_frag_t *frag = &skb_shinfo(skb)->frags[frag_index];
++
++                              dma_unmap_page(dev, virt_to_phys(frag),
++                                             PAGE_SIZE, DMA_TO_DEVICE);
++                      }
++
++                      dev_kfree_skb(skb);
++              }
++
++              cons_idx = ((cons_idx + 1) & EDMA_TX_RING_SIZE_MASK);
++              txcmpl = EDMA_TXCMPL_DESC(txcmpl_ring, cons_idx);
++      }
++
++      txcmpl_ring->cons_idx = cons_idx;
++      txcmpl_ring->avail_pkt -= count;
++
++      dev_dbg(dev, "TXCMPL:%u count:%u prod_idx:%u cons_idx:%u\n",
++              txcmpl_ring->id, count, prod_idx, cons_idx);
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXCMPL_CONS_IDX(txcmpl_ring->id);
++      regmap_write(regmap, reg, cons_idx);
++
++      /* If tx_requeue_stop disabled (tx_requeue_stop = 0)
++       * Fetch the tx queue of interface and check if it is stopped.
++       * if queue is stopped and interface is up, wake up this queue.
++       */
++      if (unlikely(!edma_ctx->tx_requeue_stop)) {
++              cpu_id = smp_processor_id();
++              nq = netdev_get_tx_queue(txcmpl_ring->napi.dev, cpu_id);
++              if (unlikely(netif_tx_queue_stopped(nq)) &&
++                  netif_carrier_ok(txcmpl_ring->napi.dev)) {
++                      dev_dbg(dev, "Waking queue number %d, for interface %s\n",
++                              cpu_id, txcmpl_ring->napi.dev->name);
++                      __netif_tx_lock(nq, cpu_id);
++                      netif_tx_wake_queue(nq);
++                      __netif_tx_unlock(nq);
++              }
++      }
++
++      return count;
++}
++
++/**
++ * edma_tx_napi_poll - EDMA TX NAPI handler.
++ * @napi: NAPI structure.
++ * @budget: Tx NAPI Budget.
++ *
++ * EDMA TX NAPI handler.
++ */
++int edma_tx_napi_poll(struct napi_struct *napi, int budget)
++{
++      struct edma_txcmpl_ring *txcmpl_ring = (struct edma_txcmpl_ring *)napi;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 txcmpl_intr_status;
++      int work_done = 0;
++      u32 data, reg;
++
++      do {
++              work_done += edma_tx_complete(budget - work_done, txcmpl_ring);
++              if (work_done >= budget)
++                      return work_done;
++
++              reg = EDMA_BASE_OFFSET + EDMA_REG_TX_INT_STAT(txcmpl_ring->id);
++              regmap_read(regmap, reg, &data);
++              txcmpl_intr_status = data & EDMA_TXCMPL_RING_INT_STATUS_MASK;
++      } while (txcmpl_intr_status);
++
++      /* No more packets to process. Finish NAPI processing. */
++      napi_complete(napi);
++
++      /* Set TXCMPL ring interrupt mask. */
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TX_INT_MASK(txcmpl_ring->id);
++      regmap_write(regmap, reg, edma_ctx->intr_info.intr_mask_txcmpl);
++
++      return work_done;
++}
++
++/**
++ * edma_tx_handle_irq - Tx IRQ Handler.
++ * @irq: Interrupt request.
++ * @ctx: Context.
++ *
++ * Process TX IRQ and schedule NAPI.
++ *
++ * Return IRQ handler code.
++ */
++irqreturn_t edma_tx_handle_irq(int irq, void *ctx)
++{
++      struct edma_txcmpl_ring *txcmpl_ring = (struct edma_txcmpl_ring *)ctx;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 reg;
++
++      pr_debug("irq: irq=%d txcmpl_ring_id=%u\n", irq, txcmpl_ring->id);
++      if (likely(napi_schedule_prep(&txcmpl_ring->napi))) {
++              /* Disable TxCmpl intr. */
++              reg = EDMA_BASE_OFFSET + EDMA_REG_TX_INT_MASK(txcmpl_ring->id);
++              regmap_write(regmap, reg, EDMA_MASK_INT_DISABLE);
++              __napi_schedule(&txcmpl_ring->napi);
++      }
++
++      return IRQ_HANDLED;
++}
++
++static void edma_tx_dma_unmap_frags(struct sk_buff *skb, u32 nr_frags)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device *dev = ppe_dev->dev;
++      u32 buf_len = 0;
++      u8 i = 0;
++
++      for (i = 0; i < skb_shinfo(skb)->nr_frags - nr_frags; i++) {
++              skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
++
++              /* DMA mapping was not done for zero size segments. */
++              buf_len = skb_frag_size(frag);
++              if (unlikely(buf_len == 0))
++                      continue;
++
++              dma_unmap_page(dev, virt_to_phys(frag), PAGE_SIZE,
++                             DMA_TO_DEVICE);
++      }
++}
++
++static u32 edma_tx_skb_nr_frags(struct edma_txdesc_ring *txdesc_ring,
++                              struct edma_txdesc_pri **txdesc, struct sk_buff *skb,
++                              u32 *hw_next_to_use, u32 *invalid_frag)
++{
++      u32 nr_frags = 0, buf_len = 0, num_descs = 0, start_idx = 0, end_idx = 0;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      u32 start_hw_next_to_use = *hw_next_to_use;
++      struct edma_txdesc_pri *txd = *txdesc;
++      struct device *dev = ppe_dev->dev;
++      u8 i = 0;
++
++      /* Hold onto the index mapped to *txdesc.
++       * This will be the index previous to that of current *hw_next_to_use.
++       */
++      start_idx = (((*hw_next_to_use) + EDMA_TX_RING_SIZE_MASK)
++              & EDMA_TX_RING_SIZE_MASK);
++
++      /* Handle if the skb has nr_frags. */
++      nr_frags = skb_shinfo(skb)->nr_frags;
++      num_descs = nr_frags;
++      i = 0;
++
++      while (nr_frags--) {
++              skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
++              dma_addr_t buff_addr;
++
++              buf_len = skb_frag_size(frag);
++
++              /* Zero size segment can lead EDMA HW to hang so, we don't want to
++               * process them. Zero size segment can happen during TSO operation
++               * if there is nothing but header in the primary segment.
++               */
++              if (unlikely(buf_len == 0)) {
++                      num_descs--;
++                      i++;
++                      continue;
++              }
++
++              /* Setting the MORE bit on the previous Tx descriptor.
++               * Note: We will flush this descriptor as well later.
++               */
++              EDMA_TXDESC_MORE_BIT_SET(txd, 1);
++              EDMA_TXDESC_ENDIAN_SET(txd);
++
++              txd = EDMA_TXDESC_PRI_DESC(txdesc_ring, *hw_next_to_use);
++              memset(txd, 0, sizeof(struct edma_txdesc_pri));
++              buff_addr = skb_frag_dma_map(dev, frag, 0, buf_len,
++                                           DMA_TO_DEVICE);
++              if (dma_mapping_error(dev, buff_addr)) {
++                      dev_dbg(dev, "Unable to dma first descriptor for nr_frags tx\n");
++                      *hw_next_to_use = start_hw_next_to_use;
++                      *invalid_frag = nr_frags;
++                      return 0;
++              }
++
++              EDMA_TXDESC_BUFFER_ADDR_SET(txd, buff_addr);
++              EDMA_TXDESC_DATA_LEN_SET(txd, buf_len);
++              EDMA_TXDESC_POOL_ID_SET(txd, (i + 1));
++
++              *hw_next_to_use = ((*hw_next_to_use + 1) & EDMA_TX_RING_SIZE_MASK);
++              i++;
++      }
++
++      EDMA_TXDESC_ENDIAN_SET(txd);
++
++      /* This will be the index previous to that of current *hw_next_to_use. */
++      end_idx = (((*hw_next_to_use) + EDMA_TX_RING_SIZE_MASK) & EDMA_TX_RING_SIZE_MASK);
++
++      *txdesc = txd;
++
++      return num_descs;
++}
++
++static void edma_tx_fill_pp_desc(struct edma_port_priv *port_priv,
++                               struct edma_txdesc_pri *txd, struct sk_buff *skb,
++      struct edma_port_tx_stats *stats)
++{
++      struct ppe_port *port = port_priv->ppe_port;
++      int port_id = port->port_id;
++
++      /* Offload L3/L4 checksum computation. */
++      if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
++              EDMA_TXDESC_ADV_OFFLOAD_SET(txd);
++              EDMA_TXDESC_IP_CSUM_SET(txd);
++              EDMA_TXDESC_L4_CSUM_SET(txd);
++      }
++
++      /* Check if the packet needs TSO
++       * This will be mostly true for SG packets.
++       */
++      if (unlikely(skb_is_gso(skb))) {
++              if ((skb_shinfo(skb)->gso_type == SKB_GSO_TCPV4) ||
++                  (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6)) {
++                      u32 mss = skb_shinfo(skb)->gso_size;
++
++                      /* If MSS<256, HW will do TSO using MSS=256,
++                       * if MSS>10K, HW will do TSO using MSS=10K,
++                       * else HW will report error 0x200000 in Tx Cmpl.
++                       */
++                      if (mss < EDMA_TX_TSO_MSS_MIN)
++                              mss = EDMA_TX_TSO_MSS_MIN;
++                      else if (mss > EDMA_TX_TSO_MSS_MAX)
++                              mss = EDMA_TX_TSO_MSS_MAX;
++
++                      EDMA_TXDESC_TSO_ENABLE_SET(txd, 1);
++                      EDMA_TXDESC_MSS_SET(txd, mss);
++
++                      /* Update tso stats. */
++                      u64_stats_update_begin(&stats->syncp);
++                      stats->tx_tso_pkts++;
++                      u64_stats_update_end(&stats->syncp);
++              }
++      }
++
++      /* Set destination information in the descriptor. */
++      EDMA_TXDESC_SERVICE_CODE_SET(txd, PPE_EDMA_SC_BYPASS_ID);
++      EDMA_DST_INFO_SET(txd, port_id);
++}
++
++static struct edma_txdesc_pri *edma_tx_skb_first_desc(struct edma_port_priv *port_priv,
++                                                    struct edma_txdesc_ring *txdesc_ring,
++                                                    struct sk_buff *skb, u32 *hw_next_to_use,
++                                                    struct edma_port_tx_stats *stats)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct edma_txdesc_pri *txd = NULL;
++      struct device *dev = ppe_dev->dev;
++      dma_addr_t buff_addr;
++      u32 buf_len = 0;
++
++      /* Get the packet length. */
++      buf_len = skb_headlen(skb);
++      txd = EDMA_TXDESC_PRI_DESC(txdesc_ring, *hw_next_to_use);
++      memset(txd, 0, sizeof(struct edma_txdesc_pri));
++
++      /* Set the data pointer as the buffer address in the descriptor. */
++      buff_addr = dma_map_single(dev, skb->data, buf_len, DMA_TO_DEVICE);
++      if (dma_mapping_error(dev, buff_addr)) {
++              dev_dbg(dev, "Unable to dma first descriptor for tx\n");
++              return NULL;
++      }
++
++      EDMA_TXDESC_BUFFER_ADDR_SET(txd, buff_addr);
++      EDMA_TXDESC_POOL_ID_SET(txd, 0);
++      edma_tx_fill_pp_desc(port_priv, txd, skb, stats);
++
++      /* Set packet length in the descriptor. */
++      EDMA_TXDESC_DATA_LEN_SET(txd, buf_len);
++      *hw_next_to_use = (*hw_next_to_use + 1) & EDMA_TX_RING_SIZE_MASK;
++
++      return txd;
++}
++
++static void edma_tx_handle_dma_err(struct sk_buff *skb, u32 num_sg_frag_list)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device *dev = ppe_dev->dev;
++      struct sk_buff *iter_skb = NULL;
++      u32 cnt_sg_frag_list = 0;
++
++      /* Walk through all fraglist skbs. */
++      skb_walk_frags(skb, iter_skb) {
++              if (skb_headlen(iter_skb)) {
++                      dma_unmap_single(dev, virt_to_phys(iter_skb->data),
++                                       skb_headlen(iter_skb), DMA_TO_DEVICE);
++                      cnt_sg_frag_list += 1;
++              }
++
++              if (cnt_sg_frag_list == num_sg_frag_list)
++                      return;
++
++              /* skb fraglist skb had nr_frags, unmap that memory. */
++              u32 nr_frags = skb_shinfo(iter_skb)->nr_frags;
++
++              if (nr_frags == 0)
++                      continue;
++
++              for (int i = 0; i < nr_frags; i++) {
++                      skb_frag_t *frag = &skb_shinfo(iter_skb)->frags[i];
++
++                      /* DMA mapping was not done for zero size segments. */
++                      if (unlikely(skb_frag_size(frag) == 0))
++                              continue;
++
++                      dma_unmap_page(dev, virt_to_phys(frag),
++                                     PAGE_SIZE, DMA_TO_DEVICE);
++                      cnt_sg_frag_list += 1;
++                      if (cnt_sg_frag_list == num_sg_frag_list)
++                              return;
++              }
++      }
++}
++
++static u32 edma_tx_skb_sg_fill_desc(struct edma_txdesc_ring *txdesc_ring,
++                                  struct edma_txdesc_pri **txdesc,
++                                  struct sk_buff *skb, u32 *hw_next_to_use,
++                                  struct edma_port_tx_stats *stats)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      u32 start_hw_next_to_use = 0, invalid_frag = 0;
++      struct edma_txdesc_pri *txd = *txdesc;
++      struct device *dev = ppe_dev->dev;
++      struct sk_buff *iter_skb = NULL;
++      u32 buf_len = 0, num_descs = 0;
++      u32 num_sg_frag_list = 0;
++
++      /* Head skb processed already. */
++      num_descs++;
++
++      if (unlikely(skb_has_frag_list(skb))) {
++              struct edma_txdesc_pri *start_desc = NULL;
++              u32 start_idx = 0, end_idx = 0;
++
++              /* Hold onto the index mapped to txd.
++               * This will be the index previous to that of current *hw_next_to_use.
++               */
++              start_idx = (((*hw_next_to_use) + EDMA_TX_RING_SIZE_MASK)
++                           & EDMA_TX_RING_SIZE_MASK);
++              start_desc = txd;
++              start_hw_next_to_use = *hw_next_to_use;
++
++              /* Walk through all fraglist skbs. */
++              skb_walk_frags(skb, iter_skb) {
++                      dma_addr_t buff_addr;
++                      u32 num_nr_frag = 0;
++
++                      /* This case could happen during the packet decapsulation.
++                       * All header content might be removed.
++                       */
++                      buf_len = skb_headlen(iter_skb);
++                      if (unlikely(buf_len == 0))
++                              goto skip_primary;
++
++                      /* We make sure to flush this descriptor later. */
++                      EDMA_TXDESC_MORE_BIT_SET(txd, 1);
++                      EDMA_TXDESC_ENDIAN_SET(txd);
++
++                      txd = EDMA_TXDESC_PRI_DESC(txdesc_ring, *hw_next_to_use);
++                      memset(txd, 0, sizeof(struct edma_txdesc_pri));
++                      buff_addr = dma_map_single(dev, iter_skb->data,
++                                                 buf_len, DMA_TO_DEVICE);
++                      if (dma_mapping_error(dev, buff_addr)) {
++                              dev_dbg(dev, "Unable to dma for fraglist\n");
++                              goto dma_err;
++                      }
++
++                      EDMA_TXDESC_BUFFER_ADDR_SET(txd, buff_addr);
++                      EDMA_TXDESC_DATA_LEN_SET(txd, buf_len);
++                      EDMA_TXDESC_POOL_ID_SET(txd, 0);
++
++                      *hw_next_to_use = (*hw_next_to_use + 1) & EDMA_TX_RING_SIZE_MASK;
++                      num_descs += 1;
++                      num_sg_frag_list += 1;
++
++                      /* skb fraglist skb can have nr_frags. */
++skip_primary:
++                      if (unlikely(skb_shinfo(iter_skb)->nr_frags)) {
++                              num_nr_frag = edma_tx_skb_nr_frags(txdesc_ring, &txd,
++                                                                 iter_skb, hw_next_to_use,
++                                                                 &invalid_frag);
++                              if (unlikely(!num_nr_frag)) {
++                                      dev_dbg(dev, "No descriptor available for ring %d\n",
++                                              txdesc_ring->id);
++                                      edma_tx_dma_unmap_frags(iter_skb, invalid_frag);
++                                      goto dma_err;
++                              }
++
++                              num_descs += num_nr_frag;
++                              num_sg_frag_list += num_nr_frag;
++
++                              /* Update fraglist with nr_frag stats. */
++                              u64_stats_update_begin(&stats->syncp);
++                              stats->tx_fraglist_with_nr_frags_pkts++;
++                              u64_stats_update_end(&stats->syncp);
++                      }
++              }
++
++              EDMA_TXDESC_ENDIAN_SET(txd);
++
++              /* This will be the index previous to
++               * that of current *hw_next_to_use.
++               */
++              end_idx = (((*hw_next_to_use) + EDMA_TX_RING_SIZE_MASK) &
++                         EDMA_TX_RING_SIZE_MASK);
++
++              /* Update frag_list stats. */
++              u64_stats_update_begin(&stats->syncp);
++              stats->tx_fraglist_pkts++;
++              u64_stats_update_end(&stats->syncp);
++      } else {
++              /* Process skb with nr_frags. */
++              num_descs += edma_tx_skb_nr_frags(txdesc_ring, &txd, skb,
++                                                hw_next_to_use, &invalid_frag);
++              if (unlikely(!num_descs)) {
++                      dev_dbg(dev, "No descriptor available for ring %d\n", txdesc_ring->id);
++                      edma_tx_dma_unmap_frags(skb, invalid_frag);
++                      *txdesc = NULL;
++                      return num_descs;
++              }
++
++              u64_stats_update_begin(&stats->syncp);
++              stats->tx_nr_frag_pkts++;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      dev_dbg(dev, "skb:%p num_descs_filled: %u, nr_frags %u, frag_list fragments %u\n",
++              skb, num_descs, skb_shinfo(skb)->nr_frags, num_sg_frag_list);
++
++      *txdesc = txd;
++
++      return num_descs;
++
++dma_err:
++      if (!num_sg_frag_list)
++              goto reset_state;
++
++      edma_tx_handle_dma_err(skb, num_sg_frag_list);
++
++reset_state:
++      *hw_next_to_use = start_hw_next_to_use;
++      *txdesc = NULL;
++
++      return 0;
++}
++
++static u32 edma_tx_avail_desc(struct edma_txdesc_ring *txdesc_ring,
++                            u32 hw_next_to_use)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      u32 data = 0, avail = 0, hw_next_to_clean = 0;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 reg;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_CONS_IDX(txdesc_ring->id);
++      regmap_read(regmap, reg, &data);
++      hw_next_to_clean = data & EDMA_TXDESC_CONS_IDX_MASK;
++
++      avail = EDMA_DESC_AVAIL_COUNT(hw_next_to_clean - 1,
++                                    hw_next_to_use, EDMA_TX_RING_SIZE);
++
++      return avail;
++}
++
++/**
++ * edma_tx_ring_xmit - Transmit a packet.
++ * @netdev: Netdevice.
++ * @skb: Socket Buffer.
++ * @txdesc_ring: Tx Descriptor ring.
++ * @stats: EDMA Tx Statistics.
++ *
++ * Check for available descriptors, fill the descriptors
++ * and transmit both linear and non linear packets.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++enum edma_tx_status edma_tx_ring_xmit(struct net_device *netdev,
++                                    struct sk_buff *skb, struct edma_txdesc_ring *txdesc_ring,
++                              struct edma_port_tx_stats *stats)
++{
++      struct edma_txdesc_stats *txdesc_stats = &txdesc_ring->txdesc_stats;
++      struct edma_port_priv *port_priv = netdev_priv(netdev);
++      u32 num_tx_desc_needed = 0, num_desc_filled = 0;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct ppe_port *port = port_priv->ppe_port;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_txdesc_pri *txdesc = NULL;
++      struct device *dev = ppe_dev->dev;
++      int port_id = port->port_id;
++      u32 hw_next_to_use = 0;
++      u32 reg;
++
++      hw_next_to_use = txdesc_ring->prod_idx;
++
++      if (unlikely(!(txdesc_ring->avail_desc)))  {
++              txdesc_ring->avail_desc = edma_tx_avail_desc(txdesc_ring,
++                                                           hw_next_to_use);
++              if (unlikely(!txdesc_ring->avail_desc)) {
++                      netdev_dbg(netdev, "No available descriptors are present at %d ring\n",
++                                 txdesc_ring->id);
++
++                      u64_stats_update_begin(&txdesc_stats->syncp);
++                      ++txdesc_stats->no_desc_avail;
++                      u64_stats_update_end(&txdesc_stats->syncp);
++                      return EDMA_TX_FAIL_NO_DESC;
++              }
++      }
++
++      /* Process head skb for linear skb.
++       * Process head skb + nr_frags + fraglist for non linear skb.
++       */
++      if (likely(!skb_is_nonlinear(skb))) {
++              txdesc = edma_tx_skb_first_desc(port_priv, txdesc_ring, skb,
++                                              &hw_next_to_use, stats);
++              if (unlikely(!txdesc)) {
++                      netdev_dbg(netdev, "No descriptor available for ring %d\n",
++                                 txdesc_ring->id);
++                      u64_stats_update_begin(&txdesc_stats->syncp);
++                      ++txdesc_stats->no_desc_avail;
++                      u64_stats_update_end(&txdesc_stats->syncp);
++                      return EDMA_TX_FAIL_NO_DESC;
++              }
++
++              EDMA_TXDESC_ENDIAN_SET(txdesc);
++              num_desc_filled++;
++      } else {
++              num_tx_desc_needed = edma_tx_num_descs_for_sg(skb);
++
++              /* HW does not support TSO for packets with more than 32 segments.
++               * HW hangs up if it sees more than 32 segments. Kernel Perform GSO
++               * for such packets with netdev gso_max_segs set to 32.
++               */
++              if (unlikely(num_tx_desc_needed > EDMA_TX_TSO_SEG_MAX)) {
++                      netdev_dbg(netdev, "Number of segments %u more than %u for %d ring\n",
++                                 num_tx_desc_needed, EDMA_TX_TSO_SEG_MAX, txdesc_ring->id);
++                      u64_stats_update_begin(&txdesc_stats->syncp);
++                      ++txdesc_stats->tso_max_seg_exceed;
++                      u64_stats_update_end(&txdesc_stats->syncp);
++
++                      u64_stats_update_begin(&stats->syncp);
++                      stats->tx_tso_drop_pkts++;
++                      u64_stats_update_end(&stats->syncp);
++
++                      return EDMA_TX_FAIL;
++              }
++
++              if (unlikely(num_tx_desc_needed > txdesc_ring->avail_desc)) {
++                      txdesc_ring->avail_desc = edma_tx_avail_desc(txdesc_ring,
++                                                                   hw_next_to_use);
++                      if (num_tx_desc_needed > txdesc_ring->avail_desc) {
++                              u64_stats_update_begin(&txdesc_stats->syncp);
++                              ++txdesc_stats->no_desc_avail;
++                              u64_stats_update_end(&txdesc_stats->syncp);
++                              netdev_dbg(netdev, "Not enough available descriptors are present at %d ring for SG packet. Needed %d, currently available %d\n",
++                                         txdesc_ring->id, num_tx_desc_needed,
++                                         txdesc_ring->avail_desc);
++                              return EDMA_TX_FAIL_NO_DESC;
++                      }
++              }
++
++              txdesc = edma_tx_skb_first_desc(port_priv, txdesc_ring, skb,
++                                              &hw_next_to_use, stats);
++              if (unlikely(!txdesc)) {
++                      netdev_dbg(netdev, "No non-linear descriptor available for ring %d\n",
++                                 txdesc_ring->id);
++                      u64_stats_update_begin(&txdesc_stats->syncp);
++                      ++txdesc_stats->no_desc_avail;
++                      u64_stats_update_end(&txdesc_stats->syncp);
++                      return EDMA_TX_FAIL_NO_DESC;
++              }
++
++              num_desc_filled = edma_tx_skb_sg_fill_desc(txdesc_ring,
++                                                         &txdesc, skb, &hw_next_to_use, stats);
++              if (unlikely(!txdesc)) {
++                      netdev_dbg(netdev, "No descriptor available for ring %d\n",
++                                 txdesc_ring->id);
++                      dma_unmap_single(dev, virt_to_phys(skb->data),
++                                       skb->len, DMA_TO_DEVICE);
++                      u64_stats_update_begin(&txdesc_stats->syncp);
++                      ++txdesc_stats->no_desc_avail;
++                      u64_stats_update_end(&txdesc_stats->syncp);
++                      return EDMA_TX_FAIL_NO_DESC;
++              }
++      }
++
++      /* Set the skb pointer to the descriptor's opaque field/s
++       * on the last descriptor of the packet/SG packet.
++       */
++      EDMA_TXDESC_OPAQUE_SET(txdesc, skb);
++
++      /* Update producer index. */
++      txdesc_ring->prod_idx = hw_next_to_use & EDMA_TXDESC_PROD_IDX_MASK;
++      txdesc_ring->avail_desc -= num_desc_filled;
++
++      netdev_dbg(netdev, "%s: skb:%p tx_ring:%u proto:0x%x skb->len:%d\n port:%u prod_idx:%u ip_summed:0x%x\n",
++                 netdev->name, skb, txdesc_ring->id, ntohs(skb->protocol),
++               skb->len, port_id, hw_next_to_use, skb->ip_summed);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_PROD_IDX(txdesc_ring->id);
++      regmap_write(regmap, reg, txdesc_ring->prod_idx);
++
++      u64_stats_update_begin(&stats->syncp);
++      stats->tx_pkts++;
++      stats->tx_bytes += skb->len;
++      u64_stats_update_end(&stats->syncp);
++
++      return EDMA_TX_OK;
++}
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_tx.h
+@@ -0,0 +1,302 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef __EDMA_TX__
++#define __EDMA_TX__
++
++#include "edma_port.h"
++
++#define EDMA_GET_DESC(R, i, type)     (&(((type *)((R)->desc))[(i)]))
++#define EDMA_GET_PDESC(R, i, type)    (&(((type *)((R)->pdesc))[(i)]))
++#define EDMA_GET_SDESC(R, i, type)    (&(((type *)((R)->sdesc))[(i)]))
++#define EDMA_TXCMPL_DESC(R, i)                EDMA_GET_DESC(R, i, \
++                                              struct edma_txcmpl_desc)
++#define EDMA_TXDESC_PRI_DESC(R, i)    EDMA_GET_PDESC(R, i, \
++                                              struct edma_txdesc_pri)
++#define EDMA_TXDESC_SEC_DESC(R, i)    EDMA_GET_SDESC(R, i, \
++                                              struct edma_txdesc_sec)
++
++#define EDMA_DESC_AVAIL_COUNT(head, tail, _max) ({ \
++                      typeof(_max) (max) = (_max); \
++                      ((((head) - (tail)) + \
++                      (max)) & ((max) - 1)); })
++
++#define EDMA_TX_RING_SIZE               2048
++#define EDMA_TX_RING_SIZE_MASK                (EDMA_TX_RING_SIZE - 1)
++
++/* Max segment processing capacity of HW for TSO. */
++#define EDMA_TX_TSO_SEG_MAX           32
++
++/* HW defined low and high MSS size. */
++#define EDMA_TX_TSO_MSS_MIN           256
++#define EDMA_TX_TSO_MSS_MAX           10240
++
++#define EDMA_DST_PORT_TYPE            2
++#define EDMA_DST_PORT_TYPE_SHIFT      28
++#define EDMA_DST_PORT_TYPE_MASK               (0xf << EDMA_DST_PORT_TYPE_SHIFT)
++#define EDMA_DST_PORT_ID_SHIFT                16
++#define EDMA_DST_PORT_ID_MASK         (0xfff << EDMA_DST_PORT_ID_SHIFT)
++
++#define EDMA_DST_PORT_TYPE_SET(x)     (((x) << EDMA_DST_PORT_TYPE_SHIFT) & \
++                                                      EDMA_DST_PORT_TYPE_MASK)
++#define EDMA_DST_PORT_ID_SET(x)               (((x) << EDMA_DST_PORT_ID_SHIFT) & \
++                                                      EDMA_DST_PORT_ID_MASK)
++#define EDMA_DST_INFO_SET(desc, x)    ((desc)->word4 |= \
++      (EDMA_DST_PORT_TYPE_SET(EDMA_DST_PORT_TYPE) | EDMA_DST_PORT_ID_SET(x)))
++
++#define EDMA_TXDESC_TSO_ENABLE_MASK           BIT(24)
++#define EDMA_TXDESC_TSO_ENABLE_SET(desc, x)   ((desc)->word5 |= \
++                              FIELD_PREP(EDMA_TXDESC_TSO_ENABLE_MASK, x))
++#define EDMA_TXDESC_MSS_MASK                  GENMASK(31, 16)
++#define EDMA_TXDESC_MSS_SET(desc, x)          ((desc)->word6 |= \
++                                      FIELD_PREP(EDMA_TXDESC_MSS_MASK, x))
++#define EDMA_TXDESC_MORE_BIT_MASK     BIT(30)
++#define EDMA_TXDESC_MORE_BIT_SET(desc, x)     ((desc)->word1 |= \
++                              FIELD_PREP(EDMA_TXDESC_MORE_BIT_MASK, x))
++
++#define EDMA_TXDESC_ADV_OFFSET_BIT    BIT(31)
++#define EDMA_TXDESC_ADV_OFFLOAD_SET(desc)     ((desc)->word5 |= \
++                                      FIELD_PREP(EDMA_TXDESC_ADV_OFFSET_BIT, 1))
++#define EDMA_TXDESC_IP_CSUM_BIT               BIT(25)
++#define EDMA_TXDESC_IP_CSUM_SET(desc)         ((desc)->word5 |= \
++                                      FIELD_PREP(EDMA_TXDESC_IP_CSUM_BIT, 1))
++
++#define EDMA_TXDESC_L4_CSUM_SET_MASK   GENMASK(27, 26)
++#define EDMA_TXDESC_L4_CSUM_SET(desc)  ((desc)->word5 |= \
++                             (FIELD_PREP(EDMA_TXDESC_L4_CSUM_SET_MASK, 1)))
++
++#define EDMA_TXDESC_POOL_ID_SET_MASK  GENMASK(24, 18)
++#define EDMA_TXDESC_POOL_ID_SET(desc, x)      ((desc)->word5 |= \
++                              (FIELD_PREP(EDMA_TXDESC_POOL_ID_SET_MASK, x)))
++
++#define EDMA_TXDESC_DATA_LEN_SET(desc, x)     ((desc)->word5 |= ((x) & 0x1ffff))
++#define EDMA_TXDESC_SERVICE_CODE_MASK GENMASK(24, 16)
++#define EDMA_TXDESC_SERVICE_CODE_SET(desc, x) ((desc)->word1 |= \
++                              (FIELD_PREP(EDMA_TXDESC_SERVICE_CODE_MASK, x)))
++#define EDMA_TXDESC_BUFFER_ADDR_SET(desc, addr)       (((desc)->word0) = (addr))
++
++#ifdef __LP64__
++#define EDMA_TXDESC_OPAQUE_GET(_desc) ({ \
++                      typeof(_desc) (desc) = (_desc); \
++                      (((u64)(desc)->word3 << 32) | (desc)->word2); })
++
++#define EDMA_TXCMPL_OPAQUE_GET(_desc) ({ \
++                      typeof(_desc) (desc) = (_desc); \
++                      (((u64)(desc)->word1 << 32) | \
++                      (desc)->word0); })
++
++#define EDMA_TXDESC_OPAQUE_LO_SET(desc, ptr)  ((desc)->word2 = \
++                                              (u32)(uintptr_t)(ptr))
++
++#define EDMA_TXDESC_OPAQUE_HI_SET(desc, ptr)  ((desc)->word3 = \
++                                              (u32)((u64)(ptr) >> 32))
++
++#define EDMA_TXDESC_OPAQUE_SET(_desc, _ptr)   do { \
++      typeof(_desc) (desc) = (_desc); \
++      typeof(_ptr) (ptr) = (_ptr); \
++      EDMA_TXDESC_OPAQUE_LO_SET(desc, ptr); \
++      EDMA_TXDESC_OPAQUE_HI_SET(desc, ptr); \
++} while (0)
++#else
++#define EDMA_TXCMPL_OPAQUE_GET(desc)          ((desc)->word0)
++#define EDMA_TXDESC_OPAQUE_GET(desc)          ((desc)->word2)
++#define EDMA_TXDESC_OPAQUE_LO_SET(desc, ptr)  ((desc)->word2 = (u32)(uintptr_t)ptr)
++
++#define EDMA_TXDESC_OPAQUE_SET(desc, ptr)     \
++                                      EDMA_TXDESC_OPAQUE_LO_SET(desc, ptr)
++#endif
++#define EDMA_TXCMPL_MORE_BIT_MASK     BIT(30)
++
++#define EDMA_TXCMPL_MORE_BIT_GET(desc)        ((le32_to_cpu((__force __le32)((desc)->word2))) & \
++                                      EDMA_TXCMPL_MORE_BIT_MASK)
++
++#define EDMA_TXCOMP_RING_ERROR_MASK   GENMASK(22, 0)
++
++#define EDMA_TXCOMP_RING_ERROR_GET(x) ((le32_to_cpu((__force __le32)x)) & \
++                                      EDMA_TXCOMP_RING_ERROR_MASK)
++
++#define EDMA_TXCOMP_POOL_ID_MASK      GENMASK(5, 0)
++
++#define EDMA_TXCOMP_POOL_ID_GET(desc) ((le32_to_cpu((__force __le32)((desc)->word2))) & \
++                                      EDMA_TXCOMP_POOL_ID_MASK)
++
++/* Opaque values are set in word2 and word3,
++ * they are not accessed by the EDMA HW,
++ * so endianness conversion is not needed.
++ */
++#define EDMA_TXDESC_ENDIAN_SET(_desc) ({ \
++      typeof(_desc) (desc) = (_desc); \
++      cpu_to_le32s(&((desc)->word0)); \
++      cpu_to_le32s(&((desc)->word1)); \
++      cpu_to_le32s(&((desc)->word4)); \
++      cpu_to_le32s(&((desc)->word5)); \
++      cpu_to_le32s(&((desc)->word6)); \
++      cpu_to_le32s(&((desc)->word7)); \
++})
++
++/* EDMA Tx GSO status */
++enum edma_tx_status {
++      EDMA_TX_OK = 0,                 /* Tx success. */
++      EDMA_TX_FAIL_NO_DESC = 1,       /* Not enough descriptors. */
++      EDMA_TX_FAIL = 2,               /* Tx failure. */
++};
++
++/* EDMA TX GSO status */
++enum edma_tx_gso_status {
++      EDMA_TX_GSO_NOT_NEEDED = 0,
++              /* Packet has segment count less than TX_TSO_SEG_MAX. */
++      EDMA_TX_GSO_SUCCEED = 1,
++              /* GSO Succeed. */
++      EDMA_TX_GSO_FAIL = 2,
++              /* GSO failed, drop the packet. */
++};
++
++/**
++ * struct edma_txcmpl_stats - EDMA TX complete ring statistics.
++ * @invalid_buffer: Invalid buffer address received.
++ * @errors: Other Tx complete descriptor errors indicated by the hardware.
++ * @desc_with_more_bit: Packet's segment transmit count.
++ * @no_pending_desc: No descriptor is pending for processing.
++ * @syncp: Synchronization pointer.
++ */
++struct edma_txcmpl_stats {
++      u64 invalid_buffer;
++      u64 errors;
++      u64 desc_with_more_bit;
++      u64 no_pending_desc;
++      struct u64_stats_sync syncp;
++};
++
++/**
++ * struct edma_txdesc_stats - EDMA Tx descriptor ring statistics.
++ * @no_desc_avail: No descriptor available to transmit.
++ * @tso_max_seg_exceed: Packets extending EDMA_TX_TSO_SEG_MAX segments.
++ * @syncp: Synchronization pointer.
++ */
++struct edma_txdesc_stats {
++      u64 no_desc_avail;
++      u64 tso_max_seg_exceed;
++      struct u64_stats_sync syncp;
++};
++
++/**
++ * struct edma_txdesc_pri - EDMA primary TX descriptor.
++ * @word0: Low 32-bit of buffer address.
++ * @word1: Buffer recycling, PTP tag flag, PRI valid flag.
++ * @word2: Low 32-bit of opaque value.
++ * @word3: High 32-bit of opaque value.
++ * @word4: Source/Destination port info.
++ * @word5: VLAN offload, csum mode, ip_csum_en, tso_en, data len.
++ * @word6: MSS/hash_value/PTP tag, data offset.
++ * @word7: L4/L3 offset, PROT type, L2 type, CVLAN/SVLAN tag, service code.
++ */
++struct edma_txdesc_pri {
++      u32 word0;
++      u32 word1;
++      u32 word2;
++      u32 word3;
++      u32 word4;
++      u32 word5;
++      u32 word6;
++      u32 word7;
++};
++
++/**
++ * struct edma_txdesc_sec - EDMA secondary TX descriptor.
++ * @word0: Reserved.
++ * @word1: Custom csum offset, payload offset, TTL/NAT action.
++ * @word2: NAPT translated port, DSCP value, TTL value.
++ * @word3: Flow index value and valid flag.
++ * @word4: Reserved.
++ * @word5: Reserved.
++ * @word6: CVLAN/SVLAN command.
++ * @word7: CVLAN/SVLAN tag value.
++ */
++struct edma_txdesc_sec {
++      u32 word0;
++      u32 word1;
++      u32 word2;
++      u32 word3;
++      u32 word4;
++      u32 word5;
++      u32 word6;
++      u32 word7;
++};
++
++/**
++ * struct edma_txcmpl_desc - EDMA TX complete descriptor.
++ * @word0: Low 32-bit opaque value.
++ * @word1: High 32-bit opaque value.
++ * @word2: More fragment, transmit ring id, pool id.
++ * @word3: Error indications.
++ */
++struct edma_txcmpl_desc {
++      u32 word0;
++      u32 word1;
++      u32 word2;
++      u32 word3;
++};
++
++/**
++ * struct edma_txdesc_ring - EDMA TX descriptor ring
++ * @prod_idx: Producer index
++ * @id: Tx ring number
++ * @avail_desc: Number of available descriptor to process
++ * @pdesc: Primary descriptor ring virtual address
++ * @pdma: Primary descriptor ring physical address
++ * @sdesc: Secondary descriptor ring virtual address
++ * @tx_desc_stats: Tx descriptor ring statistics
++ * @sdma: Secondary descriptor ring physical address
++ * @count: Number of descriptors
++ * @fc_grp_id: Flow control group ID
++ */
++struct edma_txdesc_ring {
++      u32 prod_idx;
++      u32 id;
++      u32 avail_desc;
++      struct edma_txdesc_pri *pdesc;
++      dma_addr_t pdma;
++      struct edma_txdesc_sec *sdesc;
++      struct edma_txdesc_stats txdesc_stats;
++      dma_addr_t sdma;
++      u32 count;
++      u8 fc_grp_id;
++};
++
++/**
++ * struct edma_txcmpl_ring - EDMA TX complete ring
++ * @napi: NAPI
++ * @cons_idx: Consumer index
++ * @avail_pkt: Number of available packets to process
++ * @desc: Descriptor ring virtual address
++ * @id: Txcmpl ring number
++ * @tx_cmpl_stats: Tx complete ring statistics
++ * @dma: Descriptor ring physical address
++ * @count: Number of descriptors in the ring
++ * @napi_added: Flag to indicate NAPI add status
++ */
++struct edma_txcmpl_ring {
++      struct napi_struct napi;
++      u32 cons_idx;
++      u32 avail_pkt;
++      struct edma_txcmpl_desc *desc;
++      u32 id;
++      struct edma_txcmpl_stats txcmpl_stats;
++      dma_addr_t dma;
++      u32 count;
++      bool napi_added;
++};
++
++enum edma_tx_status edma_tx_ring_xmit(struct net_device *netdev,
++                                    struct sk_buff *skb,
++                             struct edma_txdesc_ring *txdesc_ring,
++                             struct edma_port_tx_stats *stats);
++u32 edma_tx_complete(u32 work_to_do,
++                   struct edma_txcmpl_ring *txcmpl_ring);
++irqreturn_t edma_tx_handle_irq(int irq, void *ctx);
++int edma_tx_napi_poll(struct napi_struct *napi, int budget);
++enum edma_tx_gso_status edma_tx_gso_segment(struct sk_buff *skb,
++                                          struct net_device *netdev, struct sk_buff **segs);
++
++#endif
diff --git a/target/linux/qualcommbe/patches-6.18/0346-net-ethernet-qualcomm-Add-miscellaneous-error-interr.patch b/target/linux/qualcommbe/patches-6.18/0346-net-ethernet-qualcomm-Add-miscellaneous-error-interr.patch
new file mode 100644 (file)
index 0000000..0bdfb0c
--- /dev/null
@@ -0,0 +1,730 @@
+From 8a924457c0b71acee96c8f78ef386e2a354a2aca Mon Sep 17 00:00:00 2001
+From: Suruchi Agarwal <quic_suruchia@quicinc.com>
+Date: Thu, 21 Mar 2024 16:31:04 -0700
+Subject: [PATCH] net: ethernet: qualcomm: Add miscellaneous error interrupts
+ and counters
+
+Miscellaneous error interrupts, EDMA Tx/Rx and error counters are supported
+using debugfs framework.
+
+Change-Id: I7da8b978a7e93947b03a45269a81b401f35da31c
+Co-developed-by: Pavithra R <quic_pavir@quicinc.com>
+Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
+Signed-off-by: Suruchi Agarwal <quic_suruchia@quicinc.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/Makefile    |   2 +-
+ drivers/net/ethernet/qualcomm/ppe/edma.c      | 162 ++++++++
+ drivers/net/ethernet/qualcomm/ppe/edma.h      |  30 ++
+ .../net/ethernet/qualcomm/ppe/edma_debugfs.c  | 370 ++++++++++++++++++
+ .../net/ethernet/qualcomm/ppe/ppe_debugfs.c   |  17 +
+ 5 files changed, 580 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_debugfs.c
+
+--- a/drivers/net/ethernet/qualcomm/ppe/Makefile
++++ b/drivers/net/ethernet/qualcomm/ppe/Makefile
+@@ -7,4 +7,4 @@ obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o
+ qcom-ppe-objs := ppe.o ppe_config.o ppe_debugfs.o ppe_port.o
+ #EDMA
+-qcom-ppe-objs += edma.o edma_cfg_rx.o edma_cfg_tx.o edma_port.o edma_rx.o edma_tx.o
++qcom-ppe-objs += edma.o edma_cfg_rx.o edma_cfg_tx.o edma_debugfs.o edma_port.o edma_rx.o edma_tx.o
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.c
+@@ -152,6 +152,42 @@ static int edma_clock_init(void)
+ }
+ /**
++ * edma_err_stats_alloc - Allocate stats memory
++ *
++ * Allocate memory for per-CPU error stats.
++ */
++int edma_err_stats_alloc(void)
++{
++      u32 i;
++
++      edma_ctx->err_stats = alloc_percpu(*edma_ctx->err_stats);
++      if (!edma_ctx->err_stats)
++              return -ENOMEM;
++
++      for_each_possible_cpu(i) {
++              struct edma_err_stats *stats;
++
++              stats = per_cpu_ptr(edma_ctx->err_stats, i);
++              u64_stats_init(&stats->syncp);
++      }
++
++      return 0;
++}
++
++/**
++ * edma_err_stats_free - Free stats memory
++ *
++ * Free memory of per-CPU error stats.
++ */
++void edma_err_stats_free(void)
++{
++      if (edma_ctx->err_stats) {
++              free_percpu(edma_ctx->err_stats);
++              edma_ctx->err_stats = NULL;
++      }
++}
++
++/**
+  * edma_configure_ucast_prio_map_tbl - Configure unicast priority map table.
+  *
+  * Map int_priority values to priority class and initialize
+@@ -191,11 +227,113 @@ static int edma_configure_ucast_prio_map
+       return ret;
+ }
++static void edma_disable_misc_interrupt(void)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 reg;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_MISC_INT_MASK_ADDR;
++      regmap_write(regmap, reg, EDMA_MASK_INT_CLEAR);
++}
++
++static void edma_enable_misc_interrupt(void)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 reg;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_MISC_INT_MASK_ADDR;
++      regmap_write(regmap, reg, edma_ctx->intr_info.intr_mask_misc);
++}
++
++static irqreturn_t edma_misc_handle_irq(int irq,
++                                      __maybe_unused void *ctx)
++{
++      struct edma_err_stats *stats = this_cpu_ptr(edma_ctx->err_stats);
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 misc_intr_status, data, reg;
++
++      /* Read Misc intr status */
++      reg = EDMA_BASE_OFFSET + EDMA_REG_MISC_INT_STAT_ADDR;
++      regmap_read(regmap, reg, &data);
++      misc_intr_status = data & edma_ctx->intr_info.intr_mask_misc;
++
++      pr_debug("Received misc irq %d, status: %d\n", irq, misc_intr_status);
++
++      if (FIELD_GET(EDMA_MISC_AXI_RD_ERR_MASK, misc_intr_status)) {
++              pr_err("MISC AXI read error received\n");
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->edma_axi_read_err;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      if (FIELD_GET(EDMA_MISC_AXI_WR_ERR_MASK, misc_intr_status)) {
++              pr_err("MISC AXI write error received\n");
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->edma_axi_write_err;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      if (FIELD_GET(EDMA_MISC_RX_DESC_FIFO_FULL_MASK, misc_intr_status)) {
++              if (net_ratelimit())
++                      pr_err("MISC Rx descriptor fifo full error received\n");
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->edma_rxdesc_fifo_full;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      if (FIELD_GET(EDMA_MISC_RX_ERR_BUF_SIZE_MASK, misc_intr_status)) {
++              if (net_ratelimit())
++                      pr_err("MISC Rx buffer size error received\n");
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->edma_rx_buf_size_err;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      if (FIELD_GET(EDMA_MISC_TX_SRAM_FULL_MASK, misc_intr_status)) {
++              if (net_ratelimit())
++                      pr_err("MISC Tx SRAM full error received\n");
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->edma_tx_sram_full;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      if (FIELD_GET(EDMA_MISC_TX_CMPL_BUF_FULL_MASK, misc_intr_status)) {
++              if (net_ratelimit())
++                      pr_err("MISC Tx complete buffer full error received\n");
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->edma_txcmpl_buf_full;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      if (FIELD_GET(EDMA_MISC_DATA_LEN_ERR_MASK, misc_intr_status)) {
++              if (net_ratelimit())
++                      pr_err("MISC data length error received\n");
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->edma_tx_data_len_err;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      if (FIELD_GET(EDMA_MISC_TX_TIMEOUT_MASK, misc_intr_status)) {
++              if (net_ratelimit())
++                      pr_err("MISC Tx timeout error received\n");
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->edma_tx_timeout;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      return IRQ_HANDLED;
++}
++
+ static int edma_irq_register(void)
+ {
+       struct edma_hw_info *hw_info = edma_ctx->hw_info;
+       struct edma_ring_info *txcmpl = hw_info->txcmpl;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+       struct edma_ring_info *rx = hw_info->rx;
++      struct device *dev = ppe_dev->dev;
+       int ret;
+       u32 i;
+@@ -270,8 +408,25 @@ static int edma_irq_register(void)
+                        edma_rxdesc_irq_name[i]);
+       }
++      /* Request Misc IRQ */
++      ret = request_irq(edma_ctx->intr_info.intr_misc, edma_misc_handle_irq,
++                        IRQF_SHARED, "edma_misc",
++                        (void *)dev);
++      if (ret) {
++              pr_err("MISC IRQ:%d request failed\n",
++                     edma_ctx->intr_info.intr_misc);
++              goto misc_intr_req_fail;
++      }
++
+       return 0;
++misc_intr_req_fail:
++      /* Free IRQ for RXDESC rings */
++      for (i = 0; i < rx->num_rings; i++) {
++              synchronize_irq(edma_ctx->intr_info.intr_rx[i]);
++              free_irq(edma_ctx->intr_info.intr_rx[i],
++                       (void *)&edma_ctx->rx_rings[i]);
++      }
+ rx_desc_ring_intr_req_fail:
+       for (i = 0; i < rx->num_rings; i++)
+               kfree(edma_rxdesc_irq_name[i]);
+@@ -503,6 +658,7 @@ static int edma_hw_configure(void)
+               edma_cfg_tx_disable_interrupts(i);
+       edma_cfg_rx_disable_interrupts();
++      edma_disable_misc_interrupt();
+       edma_cfg_rx_rings_disable();
+@@ -614,6 +770,7 @@ void edma_destroy(struct ppe_device *ppe
+               edma_cfg_tx_disable_interrupts(i);
+       edma_cfg_rx_disable_interrupts();
++      edma_disable_misc_interrupt();
+       /* Free IRQ for TXCMPL rings. */
+       for (i = 0; i < txcmpl->num_rings; i++) {
+@@ -634,6 +791,10 @@ void edma_destroy(struct ppe_device *ppe
+       }
+       kfree(edma_rxdesc_irq_name);
++      /* Free Misc IRQ */
++      synchronize_irq(edma_ctx->intr_info.intr_misc);
++      free_irq(edma_ctx->intr_info.intr_misc, (void *)(ppe_dev->dev));
++
+       kfree(edma_ctx->intr_info.intr_rx);
+       kfree(edma_ctx->intr_info.intr_txcmpl);
+@@ -699,6 +860,7 @@ int edma_setup(struct ppe_device *ppe_de
+       }
+       edma_cfg_rx_enable_interrupts();
++      edma_enable_misc_interrupt();
+       dev_info(dev, "EDMA configuration successful\n");
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.h
+@@ -47,6 +47,30 @@ enum ppe_queue_class_type {
+ };
+ /**
++ * struct edma_err_stats - EDMA error stats
++ * @edma_axi_read_err: AXI read error
++ * @edma_axi_write_err: AXI write error
++ * @edma_rxdesc_fifo_full: Rx desc FIFO full error
++ * @edma_rx_buf_size_err: Rx buffer size too small error
++ * @edma_tx_sram_full: Tx packet SRAM buffer full error
++ * @edma_tx_data_len_err: Tx data length error
++ * @edma_tx_timeout: Tx timeout error
++ * @edma_txcmpl_buf_full: Tx completion buffer full error
++ * @syncp: Synchronization pointer
++ */
++struct edma_err_stats {
++      u64 edma_axi_read_err;
++      u64 edma_axi_write_err;
++      u64 edma_rxdesc_fifo_full;
++      u64 edma_rx_buf_size_err;
++      u64 edma_tx_sram_full;
++      u64 edma_tx_data_len_err;
++      u64 edma_tx_timeout;
++      u64 edma_txcmpl_buf_full;
++      struct u64_stats_sync syncp;
++};
++
++/**
+  * struct edma_ring_info - EDMA ring data structure.
+  * @max_rings: Maximum number of rings
+  * @ring_start: Ring start ID
+@@ -107,6 +131,7 @@ struct edma_intr_info {
+  * @rx_rings: Rx Desc Rings, SW is consumer
+  * @tx_rings: Tx Descriptor Ring, SW is producer
+  * @txcmpl_rings: Tx complete Ring, SW is consumer
++ * @err_stats: Per CPU error statistics
+  * @rx_page_mode: Page mode enabled or disabled
+  * @rx_buf_size: Rx buffer size for Jumbo MRU
+  * @tx_requeue_stop: Tx requeue stop enabled or disabled
+@@ -121,6 +146,7 @@ struct edma_context {
+       struct edma_rxdesc_ring *rx_rings;
+       struct edma_txdesc_ring *tx_rings;
+       struct edma_txcmpl_ring *txcmpl_rings;
++      struct edma_err_stats __percpu *err_stats;
+       u32 rx_page_mode;
+       u32 rx_buf_size;
+       bool tx_requeue_stop;
+@@ -129,8 +155,12 @@ struct edma_context {
+ /* Global EDMA context */
+ extern struct edma_context *edma_ctx;
++int edma_err_stats_alloc(void);
++void edma_err_stats_free(void);
+ void edma_destroy(struct ppe_device *ppe_dev);
+ int edma_setup(struct ppe_device *ppe_dev);
++void edma_debugfs_teardown(void);
++int edma_debugfs_setup(struct ppe_device *ppe_dev);
+ int ppe_edma_queue_offset_config(struct ppe_device *ppe_dev,
+                                enum ppe_queue_class_type class,
+                                int index, int queue_offset);
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_debugfs.c
+@@ -0,0 +1,370 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* EDMA debugfs routines for display of Tx/Rx counters. */
++
++#include <linux/cpumask.h>
++#include <linux/debugfs.h>
++#include <linux/kernel.h>
++#include <linux/netdevice.h>
++#include <linux/printk.h>
++
++#include "edma.h"
++
++#define EDMA_STATS_BANNER_MAX_LEN       80
++#define EDMA_RX_RING_STATS_NODE_NAME    "EDMA_RX"
++#define EDMA_TX_RING_STATS_NODE_NAME    "EDMA_TX"
++#define EDMA_ERR_STATS_NODE_NAME        "EDMA_ERR"
++
++static struct dentry *edma_dentry;
++static struct dentry *stats_dentry;
++
++static void edma_debugfs_print_banner(struct seq_file *m, char *node)
++{
++      u32 banner_char_len, i;
++
++      for (i = 0; i < EDMA_STATS_BANNER_MAX_LEN; i++)
++              seq_puts(m, "_");
++      banner_char_len = (EDMA_STATS_BANNER_MAX_LEN - (strlen(node) + 2)) / 2;
++      seq_puts(m, "\n\n");
++
++      for (i = 0; i < banner_char_len; i++)
++              seq_puts(m, "<");
++      seq_printf(m, " %s ", node);
++
++      for (i = 0; i < banner_char_len; i++)
++              seq_puts(m, ">");
++      seq_puts(m, "\n");
++
++      for (i = 0; i < EDMA_STATS_BANNER_MAX_LEN; i++)
++              seq_puts(m, "_");
++      seq_puts(m, "\n\n");
++}
++
++static int edma_debugfs_rx_rings_stats_show(struct seq_file *m,
++                                          void __maybe_unused *p)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rxfill = hw_info->rxfill;
++      struct edma_rxfill_stats *rxfill_stats;
++      struct edma_rxdesc_stats *rxdesc_stats;
++      struct edma_ring_info *rx = hw_info->rx;
++      unsigned int start;
++      u32 i;
++
++      rxfill_stats = kcalloc(rxfill->num_rings, sizeof(*rxfill_stats), GFP_KERNEL);
++      if (!rxfill_stats)
++              return -ENOMEM;
++
++      rxdesc_stats = kcalloc(rx->num_rings, sizeof(*rxdesc_stats), GFP_KERNEL);
++      if (!rxdesc_stats) {
++              kfree(rxfill_stats);
++              return -ENOMEM;
++      }
++
++      /* Get stats for Rx fill rings. */
++      for (i = 0; i < rxfill->num_rings; i++) {
++              struct edma_rxfill_ring *rxfill_ring;
++              struct edma_rxfill_stats *stats;
++
++              rxfill_ring = &edma_ctx->rxfill_rings[i];
++              stats = &rxfill_ring->rxfill_stats;
++              do {
++                      start = u64_stats_fetch_begin(&stats->syncp);
++                      rxfill_stats[i].alloc_failed = stats->alloc_failed;
++                      rxfill_stats[i].page_alloc_failed = stats->page_alloc_failed;
++              } while (u64_stats_fetch_retry(&stats->syncp, start));
++      }
++
++      /* Get stats for Rx Desc rings. */
++      for (i = 0; i < rx->num_rings; i++) {
++              struct edma_rxdesc_ring *rxdesc_ring;
++              struct edma_rxdesc_stats *stats;
++
++              rxdesc_ring = &edma_ctx->rx_rings[i];
++              stats = &rxdesc_ring->rxdesc_stats;
++              do {
++                      start = u64_stats_fetch_begin(&stats->syncp);
++                      rxdesc_stats[i].src_port_inval = stats->src_port_inval;
++                      rxdesc_stats[i].src_port_inval_type = stats->src_port_inval_type;
++                      rxdesc_stats[i].src_port_inval_netdev = stats->src_port_inval_netdev;
++              } while (u64_stats_fetch_retry(&stats->syncp, start));
++      }
++
++      edma_debugfs_print_banner(m, EDMA_RX_RING_STATS_NODE_NAME);
++
++      seq_puts(m, "\n#EDMA RX descriptor rings stats:\n\n");
++      for (i = 0; i < rx->num_rings; i++) {
++              seq_printf(m, "\t\tEDMA RX descriptor %d ring stats:\n", i + rx->ring_start);
++              seq_printf(m, "\t\t rxdesc[%d]:src_port_inval = %llu\n",
++                         i + rx->ring_start, rxdesc_stats[i].src_port_inval);
++              seq_printf(m, "\t\t rxdesc[%d]:src_port_inval_type = %llu\n",
++                         i + rx->ring_start, rxdesc_stats[i].src_port_inval_type);
++              seq_printf(m, "\t\t rxdesc[%d]:src_port_inval_netdev = %llu\n",
++                         i + rx->ring_start,
++                         rxdesc_stats[i].src_port_inval_netdev);
++              seq_puts(m, "\n");
++      }
++
++      seq_puts(m, "\n#EDMA RX fill rings stats:\n\n");
++      for (i = 0; i < rxfill->num_rings; i++) {
++              seq_printf(m, "\t\tEDMA RX fill %d ring stats:\n", i + rxfill->ring_start);
++              seq_printf(m, "\t\t rxfill[%d]:alloc_failed = %llu\n",
++                         i + rxfill->ring_start, rxfill_stats[i].alloc_failed);
++              seq_printf(m, "\t\t rxfill[%d]:page_alloc_failed = %llu\n",
++                         i + rxfill->ring_start, rxfill_stats[i].page_alloc_failed);
++              seq_puts(m, "\n");
++      }
++
++      kfree(rxfill_stats);
++      kfree(rxdesc_stats);
++      return 0;
++}
++
++static int edma_debugfs_tx_rings_stats_show(struct seq_file *m,
++                                          void __maybe_unused *p)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
++      struct edma_ring_info *tx = hw_info->tx;
++      struct edma_txcmpl_stats *txcmpl_stats;
++      struct edma_txdesc_stats *txdesc_stats;
++      unsigned int start;
++      u32 i;
++
++      txcmpl_stats = kcalloc(txcmpl->num_rings, sizeof(*txcmpl_stats), GFP_KERNEL);
++      if (!txcmpl_stats)
++              return -ENOMEM;
++
++      txdesc_stats = kcalloc(tx->num_rings, sizeof(*txdesc_stats), GFP_KERNEL);
++      if (!txdesc_stats) {
++              kfree(txcmpl_stats);
++              return -ENOMEM;
++      }
++
++      /* Get stats for Tx desc rings. */
++      for (i = 0; i < tx->num_rings; i++) {
++              struct edma_txdesc_ring *txdesc_ring;
++              struct edma_txdesc_stats *stats;
++
++              txdesc_ring = &edma_ctx->tx_rings[i];
++              stats = &txdesc_ring->txdesc_stats;
++              do {
++                      start = u64_stats_fetch_begin(&stats->syncp);
++                      txdesc_stats[i].no_desc_avail = stats->no_desc_avail;
++                      txdesc_stats[i].tso_max_seg_exceed = stats->tso_max_seg_exceed;
++              } while (u64_stats_fetch_retry(&stats->syncp, start));
++      }
++
++      /* Get stats for Tx Complete rings. */
++      for (i = 0; i < txcmpl->num_rings; i++) {
++              struct edma_txcmpl_ring *txcmpl_ring;
++              struct edma_txcmpl_stats *stats;
++
++              txcmpl_ring = &edma_ctx->txcmpl_rings[i];
++              stats = &txcmpl_ring->txcmpl_stats;
++              do {
++                      start = u64_stats_fetch_begin(&stats->syncp);
++                      txcmpl_stats[i].invalid_buffer = stats->invalid_buffer;
++                      txcmpl_stats[i].errors = stats->errors;
++                      txcmpl_stats[i].desc_with_more_bit = stats->desc_with_more_bit;
++                      txcmpl_stats[i].no_pending_desc = stats->no_pending_desc;
++              } while (u64_stats_fetch_retry(&stats->syncp, start));
++      }
++
++      edma_debugfs_print_banner(m, EDMA_TX_RING_STATS_NODE_NAME);
++
++      seq_puts(m, "\n#EDMA TX complete rings stats:\n\n");
++      for (i = 0; i < txcmpl->num_rings; i++) {
++              seq_printf(m, "\t\tEDMA TX complete %d ring stats:\n", i + txcmpl->ring_start);
++              seq_printf(m, "\t\t txcmpl[%d]:invalid_buffer = %llu\n",
++                         i + txcmpl->ring_start, txcmpl_stats[i].invalid_buffer);
++              seq_printf(m, "\t\t txcmpl[%d]:errors = %llu\n",
++                         i + txcmpl->ring_start, txcmpl_stats[i].errors);
++              seq_printf(m, "\t\t txcmpl[%d]:desc_with_more_bit = %llu\n",
++                         i + txcmpl->ring_start, txcmpl_stats[i].desc_with_more_bit);
++              seq_printf(m, "\t\t txcmpl[%d]:no_pending_desc = %llu\n",
++                         i + txcmpl->ring_start, txcmpl_stats[i].no_pending_desc);
++              seq_puts(m, "\n");
++      }
++
++      seq_puts(m, "\n#EDMA TX descriptor rings stats:\n\n");
++      for (i = 0; i < tx->num_rings; i++) {
++              seq_printf(m, "\t\tEDMA TX descriptor %d ring stats:\n", i + tx->ring_start);
++              seq_printf(m, "\t\t txdesc[%d]:no_desc_avail = %llu\n",
++                         i + tx->ring_start, txdesc_stats[i].no_desc_avail);
++              seq_printf(m, "\t\t txdesc[%d]:tso_max_seg_exceed = %llu\n",
++                         i + tx->ring_start, txdesc_stats[i].tso_max_seg_exceed);
++              seq_puts(m, "\n");
++      }
++
++      kfree(txcmpl_stats);
++      kfree(txdesc_stats);
++      return 0;
++}
++
++static int edma_debugfs_err_stats_show(struct seq_file *m,
++                                     void __maybe_unused *p)
++{
++      struct edma_err_stats *err_stats, *pcpu_err_stats;
++      unsigned int start;
++      u32 cpu;
++
++      err_stats = kzalloc(sizeof(*err_stats), GFP_KERNEL);
++      if (!err_stats)
++              return -ENOMEM;
++
++      /* Get percpu EDMA miscellaneous stats. */
++      for_each_possible_cpu(cpu) {
++              pcpu_err_stats = per_cpu_ptr(edma_ctx->err_stats, cpu);
++              do {
++                      start = u64_stats_fetch_begin(&pcpu_err_stats->syncp);
++                      err_stats->edma_axi_read_err +=
++                              pcpu_err_stats->edma_axi_read_err;
++                      err_stats->edma_axi_write_err +=
++                              pcpu_err_stats->edma_axi_write_err;
++                      err_stats->edma_rxdesc_fifo_full +=
++                              pcpu_err_stats->edma_rxdesc_fifo_full;
++                      err_stats->edma_rx_buf_size_err +=
++                              pcpu_err_stats->edma_rx_buf_size_err;
++                      err_stats->edma_tx_sram_full +=
++                              pcpu_err_stats->edma_tx_sram_full;
++                      err_stats->edma_tx_data_len_err +=
++                              pcpu_err_stats->edma_tx_data_len_err;
++                      err_stats->edma_tx_timeout +=
++                              pcpu_err_stats->edma_tx_timeout;
++                      err_stats->edma_txcmpl_buf_full +=
++                              pcpu_err_stats->edma_txcmpl_buf_full;
++              } while (u64_stats_fetch_retry(&pcpu_err_stats->syncp, start));
++      }
++
++      edma_debugfs_print_banner(m, EDMA_ERR_STATS_NODE_NAME);
++
++      seq_puts(m, "\n#EDMA error stats:\n\n");
++      seq_printf(m, "\t\t axi read error = %llu\n",
++                 err_stats->edma_axi_read_err);
++      seq_printf(m, "\t\t axi write error = %llu\n",
++                 err_stats->edma_axi_write_err);
++      seq_printf(m, "\t\t Rx descriptor fifo full = %llu\n",
++                 err_stats->edma_rxdesc_fifo_full);
++      seq_printf(m, "\t\t Rx buffer size error = %llu\n",
++                 err_stats->edma_rx_buf_size_err);
++      seq_printf(m, "\t\t Tx SRAM full = %llu\n",
++                 err_stats->edma_tx_sram_full);
++      seq_printf(m, "\t\t Tx data length error = %llu\n",
++                 err_stats->edma_tx_data_len_err);
++      seq_printf(m, "\t\t Tx timeout = %llu\n",
++                 err_stats->edma_tx_timeout);
++      seq_printf(m, "\t\t Tx completion buffer full = %llu\n",
++                 err_stats->edma_txcmpl_buf_full);
++
++      kfree(err_stats);
++      return 0;
++}
++
++static int edma_debugs_rx_rings_stats_open(struct inode *inode,
++                                         struct file *file)
++{
++      return single_open(file, edma_debugfs_rx_rings_stats_show,
++                         inode->i_private);
++}
++
++static const struct file_operations edma_debugfs_rx_rings_file_ops = {
++      .open = edma_debugs_rx_rings_stats_open,
++      .read = seq_read,
++      .llseek = seq_lseek,
++      .release = seq_release
++};
++
++static int edma_debugs_tx_rings_stats_open(struct inode *inode, struct file *file)
++{
++      return single_open(file, edma_debugfs_tx_rings_stats_show, inode->i_private);
++}
++
++static const struct file_operations edma_debugfs_tx_rings_file_ops = {
++      .open = edma_debugs_tx_rings_stats_open,
++      .read = seq_read,
++      .llseek = seq_lseek,
++      .release = seq_release
++};
++
++static int edma_debugs_err_stats_open(struct inode *inode, struct file *file)
++{
++      return single_open(file, edma_debugfs_err_stats_show, inode->i_private);
++}
++
++static const struct file_operations edma_debugfs_misc_file_ops = {
++      .open = edma_debugs_err_stats_open,
++      .read = seq_read,
++      .llseek = seq_lseek,
++      .release = seq_release
++};
++
++/**
++ * edma_debugfs_teardown - EDMA debugfs teardown.
++ *
++ * EDMA debugfs teardown and free stats memory.
++ */
++void edma_debugfs_teardown(void)
++{
++      /* Free EDMA miscellaneous stats memory */
++      edma_err_stats_free();
++
++      debugfs_remove_recursive(edma_dentry);
++      edma_dentry = NULL;
++      stats_dentry = NULL;
++}
++
++/**
++ * edma_debugfs_setup - EDMA debugfs setup.
++ * @ppe_dev: PPE Device
++ *
++ * EDMA debugfs setup.
++ */
++int edma_debugfs_setup(struct ppe_device *ppe_dev)
++{
++      edma_dentry = debugfs_create_dir("edma", ppe_dev->debugfs_root);
++      if (!edma_dentry) {
++              pr_err("Unable to create debugfs edma directory in debugfs\n");
++              goto debugfs_dir_failed;
++      }
++
++      stats_dentry = debugfs_create_dir("stats", edma_dentry);
++      if (!stats_dentry) {
++              pr_err("Unable to create debugfs stats directory in debugfs\n");
++              goto debugfs_dir_failed;
++      }
++
++      if (!debugfs_create_file("rx_ring_stats", 0444, stats_dentry,
++                               NULL, &edma_debugfs_rx_rings_file_ops)) {
++              pr_err("Unable to create Rx rings statistics file entry in debugfs\n");
++              goto debugfs_dir_failed;
++      }
++
++      if (!debugfs_create_file("tx_ring_stats", 0444, stats_dentry,
++                               NULL, &edma_debugfs_tx_rings_file_ops)) {
++              pr_err("Unable to create Tx rings statistics file entry in debugfs\n");
++              goto debugfs_dir_failed;
++      }
++
++      /* Allocate memory for EDMA miscellaneous stats */
++      if (edma_err_stats_alloc() < 0) {
++              pr_err("Unable to allocate miscellaneous percpu stats\n");
++              goto debugfs_dir_failed;
++      }
++
++      if (!debugfs_create_file("err_stats", 0444, stats_dentry,
++                               NULL, &edma_debugfs_misc_file_ops)) {
++              pr_err("Unable to create EDMA miscellaneous statistics file entry in debugfs\n");
++              goto debugfs_dir_failed;
++      }
++
++      return 0;
++
++debugfs_dir_failed:
++      debugfs_remove_recursive(edma_dentry);
++      edma_dentry = NULL;
++      stats_dentry = NULL;
++      return -ENOMEM;
++}
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_debugfs.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_debugfs.c
+@@ -7,9 +7,11 @@
+ #include <linux/bitfield.h>
+ #include <linux/debugfs.h>
++#include <linux/netdevice.h>
+ #include <linux/regmap.h>
+ #include <linux/seq_file.h>
++#include "edma.h"
+ #include "ppe.h"
+ #include "ppe_config.h"
+ #include "ppe_debugfs.h"
+@@ -678,15 +680,30 @@ static const struct file_operations ppe_
+ void ppe_debugfs_setup(struct ppe_device *ppe_dev)
+ {
++      int ret;
++
+       ppe_dev->debugfs_root = debugfs_create_dir("ppe", NULL);
+       debugfs_create_file("packet_counters", 0444,
+                           ppe_dev->debugfs_root,
+                           ppe_dev,
+                           &ppe_debugfs_packet_counter_fops);
++
++      if (!ppe_dev->debugfs_root) {
++              dev_err(ppe_dev->dev, "Error in PPE debugfs setup\n");
++              return;
++      }
++
++      ret = edma_debugfs_setup(ppe_dev);
++      if (ret) {
++              dev_err(ppe_dev->dev, "Error in EDMA debugfs setup API. ret: %d\n", ret);
++              debugfs_remove_recursive(ppe_dev->debugfs_root);
++              ppe_dev->debugfs_root = NULL;
++      }
+ }
+ void ppe_debugfs_teardown(struct ppe_device *ppe_dev)
+ {
++      edma_debugfs_teardown();
+       debugfs_remove_recursive(ppe_dev->debugfs_root);
+       ppe_dev->debugfs_root = NULL;
+ }
diff --git a/target/linux/qualcommbe/patches-6.18/0347-net-ethernet-qualcomm-Add-ethtool-support-for-EDMA.patch b/target/linux/qualcommbe/patches-6.18/0347-net-ethernet-qualcomm-Add-ethtool-support-for-EDMA.patch
new file mode 100644 (file)
index 0000000..4e0103d
--- /dev/null
@@ -0,0 +1,344 @@
+From bd61a680fb657eb65272225f18c93fe338c700da Mon Sep 17 00:00:00 2001
+From: Pavithra R <quic_pavir@quicinc.com>
+Date: Thu, 30 May 2024 20:46:36 +0530
+Subject: [PATCH] net: ethernet: qualcomm: Add ethtool support for EDMA
+
+ethtool ops can be used for EDMA netdevice configuration and statistics.
+
+Change-Id: I57fc19415dacbe51fed000520336463938220609
+Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
+Alex G: use struct ethtool_keee instead of ethtool_eee
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/Makefile    |   2 +-
+ drivers/net/ethernet/qualcomm/ppe/edma.h      |   1 +
+ .../net/ethernet/qualcomm/ppe/edma_ethtool.c  | 294 ++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/edma_port.c |   1 +
+ 4 files changed, 297 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_ethtool.c
+
+--- a/drivers/net/ethernet/qualcomm/ppe/Makefile
++++ b/drivers/net/ethernet/qualcomm/ppe/Makefile
+@@ -7,4 +7,4 @@ obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o
+ qcom-ppe-objs := ppe.o ppe_config.o ppe_debugfs.o ppe_port.o
+ #EDMA
+-qcom-ppe-objs += edma.o edma_cfg_rx.o edma_cfg_tx.o edma_debugfs.o edma_port.o edma_rx.o edma_tx.o
++qcom-ppe-objs += edma.o edma_cfg_rx.o edma_cfg_tx.o edma_debugfs.o edma_port.o edma_rx.o edma_tx.o edma_ethtool.o
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.h
+@@ -161,6 +161,7 @@ void edma_destroy(struct ppe_device *ppe
+ int edma_setup(struct ppe_device *ppe_dev);
+ void edma_debugfs_teardown(void);
+ int edma_debugfs_setup(struct ppe_device *ppe_dev);
++void edma_set_ethtool_ops(struct net_device *netdev);
+ int ppe_edma_queue_offset_config(struct ppe_device *ppe_dev,
+                                enum ppe_queue_class_type class,
+                                int index, int queue_offset);
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_ethtool.c
+@@ -0,0 +1,294 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* ethtool support for EDMA */
++
++#include <linux/cpumask.h>
++#include <linux/ethtool.h>
++#include <linux/kernel.h>
++#include <linux/netdevice.h>
++#include <linux/phylink.h>
++
++#include "edma.h"
++#include "edma_port.h"
++
++struct edma_ethtool_stats {
++      u8 stat_string[ETH_GSTRING_LEN];
++      u32 stat_offset;
++};
++
++/**
++ * struct edma_gmac_stats - Per-GMAC statistics.
++ * @rx_packets: Number of RX packets
++ * @rx_bytes: Number of RX bytes
++ * @rx_dropped: Number of RX dropped packets
++ * @rx_fraglist_packets: Number of RX fraglist packets
++ * @rx_nr_frag_packets: Number of RX nr fragment packets
++ * @rx_nr_frag_headroom_err: Number of RX nr fragment packets with headroom error
++ * @tx_packets: Number of TX packets
++ * @tx_bytes: Number of TX bytes
++ * @tx_dropped: Number of TX dropped packets
++ * @tx_nr_frag_packets: Number of TX nr fragment packets
++ * @tx_fraglist_packets: Number of TX fraglist packets
++ * @tx_fraglist_with_nr_frags_packets: Number of TX fraglist packets with nr fragments
++ * @tx_tso_packets: Number of TX TCP segmentation offload packets
++ * @tx_tso_drop_packets: Number of TX TCP segmentation dropped packets
++ * @tx_gso_packets: Number of TX SW GSO packets
++ * @tx_gso_drop_packets: Number of TX SW GSO dropped packets
++ * @tx_queue_stopped: Number of times Queue got stopped
++ */
++struct edma_gmac_stats {
++      u64 rx_packets;
++      u64 rx_bytes;
++      u64 rx_dropped;
++      u64 rx_fraglist_packets;
++      u64 rx_nr_frag_packets;
++      u64 rx_nr_frag_headroom_err;
++      u64 tx_packets;
++      u64 tx_bytes;
++      u64 tx_dropped;
++      u64 tx_nr_frag_packets;
++      u64 tx_fraglist_packets;
++      u64 tx_fraglist_with_nr_frags_packets;
++      u64 tx_tso_packets;
++      u64 tx_tso_drop_packets;
++      u64 tx_gso_packets;
++      u64 tx_gso_drop_packets;
++      u64 tx_queue_stopped[EDMA_MAX_CORE];
++};
++
++#define EDMA_STAT(m)          offsetof(struct edma_gmac_stats, m)
++
++static const struct edma_ethtool_stats edma_gstrings_stats[] = {
++      {"rx_bytes", EDMA_STAT(rx_bytes)},
++      {"rx_packets", EDMA_STAT(rx_packets)},
++      {"rx_dropped", EDMA_STAT(rx_dropped)},
++      {"rx_fraglist_packets", EDMA_STAT(rx_fraglist_packets)},
++      {"rx_nr_frag_packets", EDMA_STAT(rx_nr_frag_packets)},
++      {"rx_nr_frag_headroom_err", EDMA_STAT(rx_nr_frag_headroom_err)},
++      {"tx_bytes", EDMA_STAT(tx_bytes)},
++      {"tx_packets", EDMA_STAT(tx_packets)},
++      {"tx_dropped", EDMA_STAT(tx_dropped)},
++      {"tx_nr_frag_packets", EDMA_STAT(tx_nr_frag_packets)},
++      {"tx_fraglist_packets", EDMA_STAT(tx_fraglist_packets)},
++      {"tx_fraglist_nr_frags_packets", EDMA_STAT(tx_fraglist_with_nr_frags_packets)},
++      {"tx_tso_packets", EDMA_STAT(tx_tso_packets)},
++      {"tx_tso_drop_packets", EDMA_STAT(tx_tso_drop_packets)},
++      {"tx_gso_packets", EDMA_STAT(tx_gso_packets)},
++      {"tx_gso_drop_packets", EDMA_STAT(tx_gso_drop_packets)},
++      {"tx_queue_stopped_cpu0", EDMA_STAT(tx_queue_stopped[0])},
++      {"tx_queue_stopped_cpu1", EDMA_STAT(tx_queue_stopped[1])},
++      {"tx_queue_stopped_cpu2", EDMA_STAT(tx_queue_stopped[2])},
++      {"tx_queue_stopped_cpu3", EDMA_STAT(tx_queue_stopped[3])},
++};
++
++#define EDMA_STATS_LEN                ARRAY_SIZE(edma_gstrings_stats)
++
++static void edma_port_get_stats(struct net_device *netdev,
++                              struct edma_gmac_stats *stats)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct edma_port_rx_stats *pcpu_rx_stats;
++      struct edma_port_tx_stats *pcpu_tx_stats;
++      int i;
++
++      memset(stats, 0, sizeof(struct edma_port_pcpu_stats));
++
++      for_each_possible_cpu(i) {
++              struct edma_port_rx_stats rxp;
++              struct edma_port_tx_stats txp;
++              unsigned int start;
++
++              pcpu_rx_stats = per_cpu_ptr(port_priv->pcpu_stats.rx_stats, i);
++
++              do {
++                      start = u64_stats_fetch_begin(&pcpu_rx_stats->syncp);
++                      memcpy(&rxp, pcpu_rx_stats, sizeof(*pcpu_rx_stats));
++              } while (u64_stats_fetch_retry(&pcpu_rx_stats->syncp, start));
++
++              stats->rx_packets += rxp.rx_pkts;
++              stats->rx_bytes += rxp.rx_bytes;
++              stats->rx_dropped += rxp.rx_drops;
++              stats->rx_nr_frag_packets += rxp.rx_nr_frag_pkts;
++              stats->rx_fraglist_packets += rxp.rx_fraglist_pkts;
++              stats->rx_nr_frag_headroom_err += rxp.rx_nr_frag_headroom_err;
++
++              pcpu_tx_stats = per_cpu_ptr(port_priv->pcpu_stats.tx_stats, i);
++
++              do {
++                      start = u64_stats_fetch_begin(&pcpu_tx_stats->syncp);
++                      memcpy(&txp, pcpu_tx_stats, sizeof(*pcpu_tx_stats));
++              } while (u64_stats_fetch_retry(&pcpu_tx_stats->syncp, start));
++
++              stats->tx_packets += txp.tx_pkts;
++              stats->tx_bytes += txp.tx_bytes;
++              stats->tx_dropped += txp.tx_drops;
++              stats->tx_nr_frag_packets += txp.tx_nr_frag_pkts;
++              stats->tx_fraglist_packets += txp.tx_fraglist_pkts;
++              stats->tx_fraglist_with_nr_frags_packets += txp.tx_fraglist_with_nr_frags_pkts;
++              stats->tx_tso_packets += txp.tx_tso_pkts;
++              stats->tx_tso_drop_packets += txp.tx_tso_drop_pkts;
++              stats->tx_gso_packets += txp.tx_gso_pkts;
++              stats->tx_gso_drop_packets += txp.tx_gso_drop_pkts;
++              stats->tx_queue_stopped[i] += txp.tx_queue_stopped[i];
++      }
++}
++
++static void edma_get_ethtool_stats(struct net_device *netdev,
++                                 __maybe_unused struct ethtool_stats *stats,
++                                 u64 *data)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct edma_gmac_stats edma_stats;
++      u64 *mib_data;
++      int i;
++      u8 *p;
++
++      if (!port_priv)
++              return;
++
++      /* Get the DMA Driver statistics from the data plane if available. */
++      memset(&edma_stats, 0, sizeof(struct edma_gmac_stats));
++      edma_port_get_stats(netdev, &edma_stats);
++
++       /* Populate data plane statistics. */
++      for (i = 0; i < EDMA_STATS_LEN; i++) {
++              p = ((u8 *)(&edma_stats) + edma_gstrings_stats[i].stat_offset);
++              data[i] = *(u64 *)p;
++      }
++
++       /* Get the GMAC MIB statistics along with the DMA driver statistics. */
++      mib_data = &data[EDMA_STATS_LEN];
++      ppe_port_get_ethtool_stats(port_priv->ppe_port, mib_data);
++}
++
++static int edma_get_strset_count(struct net_device *netdev, int sset)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      int sset_count = 0;
++
++      if (!port_priv || sset != ETH_SS_STATS)
++              return 0;
++
++      sset_count = ppe_port_get_sset_count(port_priv->ppe_port, sset);
++
++      return (EDMA_STATS_LEN + sset_count);
++}
++
++static void edma_get_strings(struct net_device *netdev, u32 stringset,
++                           u8 *data)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      int i;
++
++      if (!port_priv || stringset != ETH_SS_STATS)
++              return;
++
++      for (i = 0; i < EDMA_STATS_LEN; i++) {
++              memcpy(data, edma_gstrings_stats[i].stat_string,
++                     strlen(edma_gstrings_stats[i].stat_string));
++              data += ETH_GSTRING_LEN;
++      }
++
++      ppe_port_get_strings(port_priv->ppe_port, stringset, data);
++}
++
++static int edma_get_link_ksettings(struct net_device *netdev,
++                                 struct ethtool_link_ksettings *cmd)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *port =  port_priv->ppe_port;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      return phylink_ethtool_ksettings_get(port->phylink, cmd);
++}
++
++static int edma_set_link_ksettings(struct net_device *netdev,
++                                 const struct ethtool_link_ksettings *cmd)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *port =  port_priv->ppe_port;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      return phylink_ethtool_ksettings_set(port->phylink, cmd);
++}
++
++static void edma_get_pauseparam(struct net_device *netdev,
++                              struct ethtool_pauseparam *pause)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *port =  port_priv->ppe_port;
++
++      if (!port_priv)
++              return;
++
++      phylink_ethtool_get_pauseparam(port->phylink, pause);
++}
++
++static int edma_set_pauseparam(struct net_device *netdev,
++                             struct ethtool_pauseparam *pause)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *port =  port_priv->ppe_port;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      return phylink_ethtool_set_pauseparam(port->phylink, pause);
++}
++
++static int edma_get_eee(struct net_device *netdev, struct ethtool_keee *eee)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *port =  port_priv->ppe_port;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      return phylink_ethtool_get_eee(port->phylink, eee);
++}
++
++static int edma_set_eee(struct net_device *netdev, struct ethtool_keee *eee)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *port =  port_priv->ppe_port;
++      int ret;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      ret = ppe_port_set_mac_eee(port_priv->ppe_port, eee);
++      if (ret)
++              return ret;
++
++      return phylink_ethtool_set_eee(port->phylink, eee);
++}
++
++static const struct ethtool_ops edma_ethtool_ops = {
++      .get_strings = &edma_get_strings,
++      .get_sset_count = &edma_get_strset_count,
++      .get_ethtool_stats = &edma_get_ethtool_stats,
++      .get_link = &ethtool_op_get_link,
++      .get_link_ksettings = edma_get_link_ksettings,
++      .set_link_ksettings = edma_set_link_ksettings,
++      .get_pauseparam = &edma_get_pauseparam,
++      .set_pauseparam = &edma_set_pauseparam,
++      .get_eee = &edma_get_eee,
++      .set_eee = &edma_set_eee,
++};
++
++/**
++ * edma_set_ethtool_ops - Set ethtool operations
++ * @netdev: Netdevice
++ *
++ * Set ethtool operations.
++ */
++void edma_set_ethtool_ops(struct net_device *netdev)
++{
++      netdev->ethtool_ops = &edma_ethtool_ops;
++}
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_port.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.c
+@@ -380,6 +380,7 @@ int edma_port_setup(struct ppe_port *por
+       netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+       netdev->netdev_ops = &edma_port_netdev_ops;
+       netdev->gso_max_segs = GSO_MAX_SEGS;
++      edma_set_ethtool_ops(netdev);
+       maddr = mac_addr;
+       if (of_get_mac_address(np, maddr))
diff --git a/target/linux/qualcommbe/patches-6.18/0348-net-ethernet-qualcomm-Add-module-parameters-for-driv.patch b/target/linux/qualcommbe/patches-6.18/0348-net-ethernet-qualcomm-Add-module-parameters-for-driv.patch
new file mode 100644 (file)
index 0000000..65eb3c6
--- /dev/null
@@ -0,0 +1,286 @@
+From 2ecec7e47e269e05cdd393c34aae51d4866070c6 Mon Sep 17 00:00:00 2001
+From: Pavithra R <quic_pavir@quicinc.com>
+Date: Tue, 11 Jun 2024 00:00:46 +0530
+Subject: [PATCH] net: ethernet: qualcomm: Add module parameters for driver
+ tunings
+
+Add module params and corresponding functionality for Tx/Rx
+mitigation timer/packet count, napi budget and tx requeue stop.
+
+Change-Id: I1717559c931bba4f355ee06ab89f289818400ca2
+Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/edma.c      | 35 +++++++++++++++++++
+ .../net/ethernet/qualcomm/ppe/edma_cfg_rx.c   | 29 +++++++++++++--
+ .../net/ethernet/qualcomm/ppe/edma_cfg_rx.h   | 21 +++++++++++
+ .../net/ethernet/qualcomm/ppe/edma_cfg_tx.c   | 29 +++++++++++++--
+ .../net/ethernet/qualcomm/ppe/edma_cfg_tx.h   | 16 +++++++++
+ drivers/net/ethernet/qualcomm/ppe/edma_rx.h   |  4 +++
+ drivers/net/ethernet/qualcomm/ppe/edma_tx.h   |  4 +++
+ 7 files changed, 134 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.c
+@@ -38,6 +38,38 @@ static int rx_buff_size;
+ module_param(rx_buff_size, int, 0640);
+ MODULE_PARM_DESC(rx_buff_size, "Rx Buffer size for Jumbo MRU value (default:0)");
++int edma_rx_napi_budget = EDMA_RX_NAPI_WORK_DEF;
++module_param(edma_rx_napi_budget, int, 0444);
++MODULE_PARM_DESC(edma_rx_napi_budget, "Rx NAPI budget (default:128, min:16, max:512)");
++
++int edma_tx_napi_budget = EDMA_TX_NAPI_WORK_DEF;
++module_param(edma_tx_napi_budget, int, 0444);
++MODULE_PARM_DESC(edma_tx_napi_budget, "Tx NAPI budget (default:512 for ipq95xx, min:16, max:512)");
++
++int edma_rx_mitigation_pkt_cnt = EDMA_RX_MITIGATION_PKT_CNT_DEF;
++module_param(edma_rx_mitigation_pkt_cnt, int, 0444);
++MODULE_PARM_DESC(edma_rx_mitigation_pkt_cnt,
++               "Rx mitigation packet count value (default:16, min:0, max: 256)");
++
++s32 edma_rx_mitigation_timer = EDMA_RX_MITIGATION_TIMER_DEF;
++module_param(edma_rx_mitigation_timer, int, 0444);
++MODULE_PARM_DESC(edma_dp_rx_mitigation_timer,
++               "Rx mitigation timer value in microseconds (default:25, min:0, max: 1000)");
++
++int edma_tx_mitigation_timer = EDMA_TX_MITIGATION_TIMER_DEF;
++module_param(edma_tx_mitigation_timer, int, 0444);
++MODULE_PARM_DESC(edma_tx_mitigation_timer,
++               "Tx mitigation timer value in microseconds (default:250, min:0, max: 1000)");
++
++int edma_tx_mitigation_pkt_cnt = EDMA_TX_MITIGATION_PKT_CNT_DEF;
++module_param(edma_tx_mitigation_pkt_cnt, int, 0444);
++MODULE_PARM_DESC(edma_tx_mitigation_pkt_cnt,
++               "Tx mitigation packet count value (default:16, min:0, max: 256)");
++
++static int tx_requeue_stop;
++module_param(tx_requeue_stop, int, 0640);
++MODULE_PARM_DESC(tx_requeue_stop, "Disable Tx requeue function (default:0)");
++
+ /* Priority to multi-queue mapping. */
+ static u8 edma_pri_map[PPE_QUEUE_INTER_PRI_NUM] = {
+       0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7};
+@@ -828,7 +860,10 @@ int edma_setup(struct ppe_device *ppe_de
+       edma_ctx->hw_info = &ipq9574_hw_info;
+       edma_ctx->ppe_dev = ppe_dev;
+       edma_ctx->rx_buf_size = rx_buff_size;
++
+       edma_ctx->tx_requeue_stop = false;
++      if (tx_requeue_stop != 0)
++              edma_ctx->tx_requeue_stop = true;
+       /* Configure the EDMA common clocks. */
+       ret = edma_clock_init();
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.c
+@@ -166,6 +166,24 @@ static void edma_cfg_rx_desc_ring_config
+       reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_RING_SIZE(rxdesc_ring->ring_id);
+       regmap_write(regmap, reg, data);
++      /* Validate mitigation timer value */
++      if (edma_rx_mitigation_timer < EDMA_RX_MITIGATION_TIMER_MIN ||
++          edma_rx_mitigation_timer > EDMA_RX_MITIGATION_TIMER_MAX) {
++              pr_err("Invalid Rx mitigation timer configured:%d for ring:%d. Using the default timer value:%d\n",
++                     edma_rx_mitigation_timer, rxdesc_ring->ring_id,
++                      EDMA_RX_MITIGATION_TIMER_DEF);
++              edma_rx_mitigation_timer = EDMA_RX_MITIGATION_TIMER_DEF;
++      }
++
++      /* Validate mitigation packet count value */
++      if (edma_rx_mitigation_pkt_cnt < EDMA_RX_MITIGATION_PKT_CNT_MIN ||
++          edma_rx_mitigation_pkt_cnt > EDMA_RX_MITIGATION_PKT_CNT_MAX) {
++              pr_err("Invalid Rx mitigation packet count configured:%d for ring:%d. Using the default packet counter value:%d\n",
++                     edma_rx_mitigation_timer, rxdesc_ring->ring_id,
++                      EDMA_RX_MITIGATION_PKT_CNT_DEF);
++              edma_rx_mitigation_pkt_cnt = EDMA_RX_MITIGATION_PKT_CNT_DEF;
++      }
++
+       /* Configure the Mitigation timer */
+       data = EDMA_MICROSEC_TO_TIMER_UNIT(EDMA_RX_MITIGATION_TIMER_DEF,
+                                          ppe_dev->clk_rate / MHZ);
+@@ -176,7 +194,7 @@ static void edma_cfg_rx_desc_ring_config
+       regmap_write(regmap, reg, data);
+       /* Configure the Mitigation packet count */
+-      data = (EDMA_RX_MITIGATION_PKT_CNT_DEF & EDMA_RXDESC_LOW_THRE_MASK)
++      data = (edma_rx_mitigation_pkt_cnt & EDMA_RXDESC_LOW_THRE_MASK)
+                       << EDMA_RXDESC_LOW_THRE_SHIFT;
+       pr_debug("EDMA Rx mitigation packet count value: %d\n", data);
+       reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_UGT_THRE(rxdesc_ring->ring_id);
+@@ -915,6 +933,13 @@ void edma_cfg_rx_napi_add(void)
+       struct edma_ring_info *rx = hw_info->rx;
+       u32 i;
++      if (edma_rx_napi_budget < EDMA_RX_NAPI_WORK_MIN ||
++          edma_rx_napi_budget > EDMA_RX_NAPI_WORK_MAX) {
++              pr_err("Incorrect Rx NAPI budget: %d, setting to default: %d",
++                     edma_rx_napi_budget, hw_info->napi_budget_rx);
++              edma_rx_napi_budget = hw_info->napi_budget_rx;
++      }
++
+       for (i = 0; i < rx->num_rings; i++) {
+               struct edma_rxdesc_ring *rxdesc_ring = &edma_ctx->rx_rings[i];
+@@ -923,7 +948,7 @@ void edma_cfg_rx_napi_add(void)
+               rxdesc_ring->napi_added = true;
+       }
+-      netdev_dbg(edma_ctx->dummy_dev, "Rx NAPI budget: %d\n", hw_info->napi_budget_rx);
++      netdev_dbg(edma_ctx->dummy_dev, "Rx NAPI budget: %d\n", edma_rx_napi_budget);
+ }
+ /**
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.h
+@@ -5,6 +5,15 @@
+ #ifndef __EDMA_CFG_RX__
+ #define __EDMA_CFG_RX__
++/* Rx default NAPI budget */
++#define EDMA_RX_NAPI_WORK_DEF         128
++
++/* RX minimum NAPI budget */
++#define EDMA_RX_NAPI_WORK_MIN         16
++
++/* Rx maximum NAPI budget */
++#define EDMA_RX_NAPI_WORK_MAX         512
++
+ /* SKB payload size used in page mode */
+ #define EDMA_RX_PAGE_MODE_SKB_SIZE    256
+@@ -22,9 +31,21 @@
+ /* Rx mitigation timer's default value in microseconds */
+ #define EDMA_RX_MITIGATION_TIMER_DEF  25
++/* Rx mitigation timer's minimum value in microseconds */
++#define EDMA_RX_MITIGATION_TIMER_MIN  0
++
++/* Rx mitigation timer's maximum value in microseconds */
++#define EDMA_RX_MITIGATION_TIMER_MAX  1000
++
+ /* Rx mitigation packet count's default value */
+ #define EDMA_RX_MITIGATION_PKT_CNT_DEF        16
++/* Rx mitigation packet count's minimum value */
++#define EDMA_RX_MITIGATION_PKT_CNT_MIN        0
++
++/* Rx mitigation packet count's maximum value */
++#define EDMA_RX_MITIGATION_PKT_CNT_MAX        256
++
+ /* Default bitmap of cores for RPS to ARM cores */
+ #define EDMA_RX_DEFAULT_BITMAP        ((1 << EDMA_MAX_CORE) - 1)
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_cfg_tx.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_tx.c
+@@ -170,6 +170,24 @@ static void edma_cfg_txcmpl_ring_configu
+       reg = EDMA_BASE_OFFSET + EDMA_REG_TXCMPL_CTRL(txcmpl_ring->id);
+       regmap_write(regmap, reg, EDMA_TXCMPL_RETMODE_OPAQUE);
++      /* Validate mitigation timer value */
++      if (edma_tx_mitigation_timer < EDMA_TX_MITIGATION_TIMER_MIN ||
++          edma_tx_mitigation_timer > EDMA_TX_MITIGATION_TIMER_MAX) {
++              pr_err("Invalid Tx mitigation timer configured:%d for ring:%d. Using the default timer value:%d\n",
++                     edma_tx_mitigation_timer, txcmpl_ring->id,
++                     EDMA_TX_MITIGATION_TIMER_DEF);
++              edma_tx_mitigation_timer = EDMA_TX_MITIGATION_TIMER_DEF;
++      }
++
++      /* Validate mitigation packet count value */
++      if (edma_tx_mitigation_pkt_cnt < EDMA_TX_MITIGATION_PKT_CNT_MIN ||
++          edma_tx_mitigation_pkt_cnt > EDMA_TX_MITIGATION_PKT_CNT_MAX) {
++              pr_err("Invalid Tx mitigation packet count configured:%d for ring:%d. Using the default packet counter value:%d\n",
++                     edma_tx_mitigation_timer, txcmpl_ring->id,
++                     EDMA_TX_MITIGATION_PKT_CNT_DEF);
++              edma_tx_mitigation_pkt_cnt = EDMA_TX_MITIGATION_PKT_CNT_DEF;
++      }
++
+       /* Configure the Mitigation timer. */
+       data = EDMA_MICROSEC_TO_TIMER_UNIT(EDMA_TX_MITIGATION_TIMER_DEF,
+                                          ppe_dev->clk_rate / MHZ);
+@@ -180,7 +198,7 @@ static void edma_cfg_txcmpl_ring_configu
+       regmap_write(regmap, reg, data);
+       /* Configure the Mitigation packet count. */
+-      data = (EDMA_TX_MITIGATION_PKT_CNT_DEF & EDMA_TXCMPL_LOW_THRE_MASK)
++      data = (edma_tx_mitigation_pkt_cnt & EDMA_TXCMPL_LOW_THRE_MASK)
+               << EDMA_TXCMPL_LOW_THRE_SHIFT;
+       pr_debug("EDMA Tx mitigation packet count value: %d\n", data);
+       reg = EDMA_BASE_OFFSET + EDMA_REG_TXCMPL_UGT_THRE(txcmpl_ring->id);
+@@ -634,6 +652,13 @@ void edma_cfg_tx_napi_add(struct net_dev
+       struct edma_txcmpl_ring *txcmpl_ring;
+       u32 i, ring_idx;
++      if (edma_tx_napi_budget < EDMA_TX_NAPI_WORK_MIN ||
++          edma_tx_napi_budget > EDMA_TX_NAPI_WORK_MAX) {
++              pr_err("Incorrect Tx NAPI budget: %d, setting to default: %d",
++                     edma_tx_napi_budget, hw_info->napi_budget_tx);
++              edma_tx_napi_budget = hw_info->napi_budget_tx;
++      }
++
+       /* Adding tx napi for a interface with each queue. */
+       for_each_possible_cpu(i) {
+               ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
+@@ -644,5 +669,5 @@ void edma_cfg_tx_napi_add(struct net_dev
+               netdev_dbg(netdev, "Napi added for txcmpl ring: %u\n", txcmpl_ring->id);
+       }
+-      netdev_dbg(netdev, "Tx NAPI budget: %d\n", hw_info->napi_budget_tx);
++      netdev_dbg(netdev, "Tx NAPI budget: %d\n", edma_tx_napi_budget);
+ }
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_cfg_tx.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_tx.h
+@@ -5,12 +5,28 @@
+ #ifndef __EDMA_CFG_TX__
+ #define __EDMA_CFG_TX__
++#define EDMA_TX_NAPI_WORK_DEF 512
++#define EDMA_TX_NAPI_WORK_MIN 16
++#define EDMA_TX_NAPI_WORK_MAX 512
++
+ /* Tx mitigation timer's default value. */
+ #define EDMA_TX_MITIGATION_TIMER_DEF  250
++/* Tx mitigation timer's minimum value in microseconds */
++#define EDMA_TX_MITIGATION_TIMER_MIN  0
++
++/* Tx mitigation timer's maximum value in microseconds */
++#define EDMA_TX_MITIGATION_TIMER_MAX  1000
++
+ /* Tx mitigation packet count default value. */
+ #define EDMA_TX_MITIGATION_PKT_CNT_DEF        16
++/* Tx mitigation packet count's minimum value */
++#define EDMA_TX_MITIGATION_PKT_CNT_MIN        0
++
++/* Tx mitigation packet count's maximum value */
++#define EDMA_TX_MITIGATION_PKT_CNT_MAX        256
++
+ void edma_cfg_tx_rings(void);
+ int edma_cfg_tx_rings_alloc(void);
+ void edma_cfg_tx_rings_cleanup(void);
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_rx.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_rx.h
+@@ -281,6 +281,10 @@ struct edma_rxdesc_ring {
+       struct sk_buff *last;
+ };
++extern int edma_rx_napi_budget;
++extern int edma_rx_mitigation_timer;
++extern int edma_rx_mitigation_pkt_cnt;
++
+ irqreturn_t edma_rx_handle_irq(int irq, void *ctx);
+ int edma_rx_alloc_buffer(struct edma_rxfill_ring *rxfill_ring, int alloc_count);
+ int edma_rx_napi_poll(struct napi_struct *napi, int budget);
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_tx.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_tx.h
+@@ -288,6 +288,10 @@ struct edma_txcmpl_ring {
+       bool napi_added;
+ };
++extern int edma_tx_napi_budget;
++extern int edma_tx_mitigation_timer;
++extern int edma_tx_mitigation_pkt_cnt;
++
+ enum edma_tx_status edma_tx_ring_xmit(struct net_device *netdev,
+                                     struct sk_buff *skb,
+                              struct edma_txdesc_ring *txdesc_ring,
diff --git a/target/linux/qualcommbe/patches-6.18/0349-net-ethernet-qualcomm-Add-sysctl-for-RPS-bitmap.patch b/target/linux/qualcommbe/patches-6.18/0349-net-ethernet-qualcomm-Add-sysctl-for-RPS-bitmap.patch
new file mode 100644 (file)
index 0000000..c697001
--- /dev/null
@@ -0,0 +1,145 @@
+From dcac735a715c13a817d65ae371564cf2793330b2 Mon Sep 17 00:00:00 2001
+From: Pavithra R <quic_pavir@quicinc.com>
+Date: Tue, 11 Jun 2024 01:43:22 +0530
+Subject: [PATCH] net: ethernet: qualcomm: Add sysctl for RPS bitmap
+
+Add sysctl to configure RPS bitmap for EDMA receive.
+This bitmap is used to configure the set of ARM cores
+used to receive packets from EDMA.
+
+Change-Id: Ie0e7d5971db93ea1494608a9e79c4abb13ce69b6
+Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
+Alex G: Use **const** ctl_table argument for .proc_handler
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/edma.c      | 23 ++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/edma.h      |  2 ++
+ .../net/ethernet/qualcomm/ppe/edma_cfg_rx.c   | 27 +++++++++++++++++++
+ .../net/ethernet/qualcomm/ppe/edma_cfg_rx.h   |  6 ++++-
+ 4 files changed, 57 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.c
+@@ -797,6 +797,11 @@ void edma_destroy(struct ppe_device *ppe
+       struct edma_ring_info *rx = hw_info->rx;
+       u32 i;
++      if (edma_ctx->rx_rps_ctl_table_hdr) {
++              unregister_sysctl_table(edma_ctx->rx_rps_ctl_table_hdr);
++              edma_ctx->rx_rps_ctl_table_hdr = NULL;
++      }
++
+       /* Disable interrupts. */
+       for (i = 1; i <= hw_info->max_ports; i++)
+               edma_cfg_tx_disable_interrupts(i);
+@@ -840,6 +845,17 @@ void edma_destroy(struct ppe_device *ppe
+       kfree(edma_ctx->netdev_arr);
+ }
++/* EDMA Rx RPS core sysctl table */
++static struct ctl_table edma_rx_rps_core_table[] = {
++      {
++              .procname       =       "rps_bitmap_cores",
++              .data           =       &edma_cfg_rx_rps_bitmap_cores,
++              .maxlen         =       sizeof(int),
++              .mode           =       0644,
++              .proc_handler   =       edma_cfg_rx_rps_bitmap
++      },
++};
++
+ /**
+  * edma_setup - EDMA Setup.
+  * @ppe_dev: PPE device
+@@ -865,6 +881,13 @@ int edma_setup(struct ppe_device *ppe_de
+       if (tx_requeue_stop != 0)
+               edma_ctx->tx_requeue_stop = true;
++      edma_ctx->rx_rps_ctl_table_hdr = register_sysctl("net/edma",
++                                                       edma_rx_rps_core_table);
++      if (!edma_ctx->rx_rps_ctl_table_hdr) {
++              pr_err("Rx rps sysctl table configuration failed\n");
++              return -EINVAL;
++      }
++
+       /* Configure the EDMA common clocks. */
+       ret = edma_clock_init();
+       if (ret) {
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.h
+@@ -132,6 +132,7 @@ struct edma_intr_info {
+  * @tx_rings: Tx Descriptor Ring, SW is producer
+  * @txcmpl_rings: Tx complete Ring, SW is consumer
+  * @err_stats: Per CPU error statistics
++ * @rx_rps_ctl_table_hdr: Rx RPS sysctl table
+  * @rx_page_mode: Page mode enabled or disabled
+  * @rx_buf_size: Rx buffer size for Jumbo MRU
+  * @tx_requeue_stop: Tx requeue stop enabled or disabled
+@@ -147,6 +148,7 @@ struct edma_context {
+       struct edma_txdesc_ring *tx_rings;
+       struct edma_txcmpl_ring *txcmpl_rings;
+       struct edma_err_stats __percpu *err_stats;
++      struct ctl_table_header *rx_rps_ctl_table_hdr;
+       u32 rx_page_mode;
+       u32 rx_buf_size;
+       bool tx_requeue_stop;
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.c
+@@ -43,6 +43,8 @@ static u32 edma_rx_ring_queue_map[][EDMA
+                                               { 6, 14, 22, 30 },
+                                               { 7, 15, 23, 31 }};
++u32 edma_cfg_rx_rps_bitmap_cores = EDMA_RX_DEFAULT_BITMAP;
++
+ static int edma_cfg_rx_desc_rings_reset_queue_mapping(void)
+ {
+       struct edma_hw_info *hw_info = edma_ctx->hw_info;
+@@ -987,3 +989,28 @@ int edma_cfg_rx_rps_hash_map(void)
+       return 0;
+ }
++
++/* Configure RPS hash mapping based on bitmap */
++int edma_cfg_rx_rps_bitmap(const struct ctl_table *table, int write,
++                         void *buffer, size_t *lenp, loff_t *ppos)
++{
++      int ret;
++
++      ret = proc_dointvec(table, write, buffer, lenp, ppos);
++
++      if (!write)
++              return ret;
++
++      if (!edma_cfg_rx_rps_bitmap_cores ||
++          edma_cfg_rx_rps_bitmap_cores > EDMA_RX_DEFAULT_BITMAP) {
++              pr_warn("Incorrect CPU bitmap: %x. Setting it to default value: %d",
++                      edma_cfg_rx_rps_bitmap_cores, EDMA_RX_DEFAULT_BITMAP);
++              edma_cfg_rx_rps_bitmap_cores = EDMA_RX_DEFAULT_BITMAP;
++      }
++
++      ret = edma_cfg_rx_rps_hash_map();
++
++      pr_info("EDMA RPS bitmap value: %d\n", edma_cfg_rx_rps_bitmap_cores);
++
++      return ret;
++}
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.h
+@@ -49,6 +49,8 @@
+ /* Default bitmap of cores for RPS to ARM cores */
+ #define EDMA_RX_DEFAULT_BITMAP        ((1 << EDMA_MAX_CORE) - 1)
++extern u32 edma_cfg_rx_rps_bitmap_cores;
++
+ int edma_cfg_rx_rings(void);
+ int edma_cfg_rx_rings_alloc(void);
+ void edma_cfg_rx_ring_mappings(void);
+@@ -64,6 +66,8 @@ void edma_cfg_rx_rings_enable(void);
+ void edma_cfg_rx_rings_disable(void);
+ void edma_cfg_rx_buff_size_setup(void);
+ int edma_cfg_rx_rps_hash_map(void);
+-int edma_cfg_rx_rps(struct ctl_table *table, int write,
++int edma_cfg_rx_rps(const struct ctl_table *table, int write,
+                   void *buffer, size_t *lenp, loff_t *ppos);
++int edma_cfg_rx_rps_bitmap(const struct ctl_table *table, int write,
++                         void *buffer, size_t *lenp, loff_t *ppos);
+ #endif
diff --git a/target/linux/qualcommbe/patches-6.18/0350-net-ethernet-qualcomm-Add-support-for-label-property.patch b/target/linux/qualcommbe/patches-6.18/0350-net-ethernet-qualcomm-Add-support-for-label-property.patch
new file mode 100644 (file)
index 0000000..79af169
--- /dev/null
@@ -0,0 +1,48 @@
+From a809433c9b6a418dd886f12a5dcb3376f73bf2a7 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 4 Dec 2024 01:37:05 +0100
+Subject: [PATCH] net: ethernet: qualcomm: Add support for label property for
+ EDMA port
+
+Add support for label property for EDMA port. This is useful to define
+custom name in DTS for specific ethernet port instead of assigning a
+dynamic name at runtime.
+
+This also improve the log output by using modern APIs.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/edma_port.c | 18 +++++++++++++++---
+ 1 file changed, 15 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_port.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.c
+@@ -355,13 +355,25 @@ int edma_port_setup(struct ppe_port *por
+       int port_id = port->port_id;
+       struct net_device *netdev;
+       u8 mac_addr[ETH_ALEN];
++      const char *name;
++      int assign_type;
+       int ret = 0;
+       u8 *maddr;
+-      netdev = alloc_etherdev_mqs(sizeof(struct edma_port_priv),
+-                                  EDMA_NETDEV_QUEUE_NUM, EDMA_NETDEV_QUEUE_NUM);
++      name = of_get_property(np, "label", NULL);
++      if (name) {
++              assign_type = NET_NAME_PREDICTABLE;
++      } else {
++              name = "eth%d";
++              assign_type = NET_NAME_ENUM;
++      }
++
++      netdev = alloc_netdev_mqs(sizeof(struct edma_port_priv),
++                                name, assign_type,
++                                ether_setup,
++                                EDMA_NETDEV_QUEUE_NUM, EDMA_NETDEV_QUEUE_NUM);
+       if (!netdev) {
+-              pr_err("alloc_etherdev() failed\n");
++              dev_err(ppe_dev->dev, "alloc_netdev_mqs() failed\n");
+               return -ENOMEM;
+       }
diff --git a/target/linux/qualcommbe/patches-6.18/0351-net-ethernet-qualcomm-ppe-Fix-unmet-dependency-with-.patch b/target/linux/qualcommbe/patches-6.18/0351-net-ethernet-qualcomm-ppe-Fix-unmet-dependency-with-.patch
new file mode 100644 (file)
index 0000000..a0d15cf
--- /dev/null
@@ -0,0 +1,30 @@
+From 9c4ad75f17788a64c1e37d0b9e19ca157e01c80a Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 9 Dec 2024 18:19:06 +0100
+Subject: [PATCH] net: ethernet: qualcomm: ppe: Fix unmet dependency with
+ QCOM_PPE
+
+Fix unmet dependency with QCOM_PPE on selecting SFP.
+
+WARNING: unmet direct dependencies detected for SFP
+  Depends on [m]: NETDEVICES [=y] && PHYLIB [=y] && I2C [=y] && PHYLINK [=y] && (HWMON [=m] || HWMON [=m]=n [=n])
+  Selected by [y]:
+  - QCOM_PPE [=y] && NETDEVICES [=y] && ETHERNET [=y] && NET_VENDOR_QUALCOMM [=y] && HAS_IOMEM [=y] && OF [=y] && COMMON_CLK [=y]
+
+This permit correct compilation of the modules with SFP enabled.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/net/ethernet/qualcomm/Kconfig | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/net/ethernet/qualcomm/Kconfig
++++ b/drivers/net/ethernet/qualcomm/Kconfig
+@@ -68,7 +68,6 @@ config QCOM_PPE
+       select REGMAP_MMIO
+       select PHYLINK
+       select PCS_QCOM_IPQ_UNIPHY
+-      select SFP
+       help
+         This driver supports the Qualcomm Technologies, Inc. packet
+         process engine (PPE) available with IPQ SoC. The PPE includes
diff --git a/target/linux/qualcommbe/patches-6.18/0352-net-ethernet-qualcomm-ppe-select-correct-PCS-depende.patch b/target/linux/qualcommbe/patches-6.18/0352-net-ethernet-qualcomm-ppe-select-correct-PCS-depende.patch
new file mode 100644 (file)
index 0000000..3893c5c
--- /dev/null
@@ -0,0 +1,24 @@
+From ac41b401d274a4004027fa4000d801cd28c51f4c Mon Sep 17 00:00:00 2001
+From: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+Date: Tue, 13 May 2025 13:41:37 -0500
+Subject: [PATCH] net: ethernet: qualcomm: ppe: select correct PCS dependency
+
+The config symbol for the PCS driver has changed to PCS_QCOM_IPQ9574,
+since the original submission. Update Kconfig accordingly.
+
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/ethernet/qualcomm/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/qualcomm/Kconfig
++++ b/drivers/net/ethernet/qualcomm/Kconfig
+@@ -67,7 +67,7 @@ config QCOM_PPE
+       depends on COMMON_CLK
+       select REGMAP_MMIO
+       select PHYLINK
+-      select PCS_QCOM_IPQ_UNIPHY
++      select PCS_QCOM_IPQ9574
+       help
+         This driver supports the Qualcomm Technologies, Inc. packet
+         process engine (PPE) available with IPQ SoC. The PPE includes
diff --git a/target/linux/qualcommbe/patches-6.18/0353-arm64-dts-qcom-Add-IPQ9574-PPE-base-device-node.patch b/target/linux/qualcommbe/patches-6.18/0353-arm64-dts-qcom-Add-IPQ9574-PPE-base-device-node.patch
new file mode 100644 (file)
index 0000000..640a2a9
--- /dev/null
@@ -0,0 +1,72 @@
+From bbf706ecfd4295d73c8217d5220573dd51d7a081 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Fri, 1 Mar 2024 14:46:45 +0800
+Subject: [PATCH] arm64: dts: qcom: Add IPQ9574 PPE base device node
+
+PPE is the packet process engine on the Qualcomm IPQ platform,
+which is connected with the external switch or PHY device via
+the UNIPHY (PCS).
+
+Change-Id: I254bd48c218aa4eab54f697a2ad149f5a93b682c
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Alex G: Add "qcom_ppe" label to PPE node
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ arch/arm64/boot/dts/qcom/ipq9574.dtsi | 39 +++++++++++++++++++++++++++
+ 1 file changed, 39 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq9574.dtsi
+@@ -13,6 +13,7 @@
+ #include <dt-bindings/interconnect/qcom,ipq9574.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/reset/qcom,ipq9574-gcc.h>
++#include <dt-bindings/reset/qcom,ipq9574-nsscc.h>
+ #include <dt-bindings/thermal/thermal.h>
+ / {
+@@ -1271,6 +1272,44 @@
+                       #interconnect-cells = <1>;
+               };
++              qcom_ppe: ethernet@3a000000 {
++                      compatible = "qcom,ipq9574-ppe";
++                      reg = <0x3a000000 0xbef800>;
++                      ranges;
++                      #address-cells = <1>;
++                      #size-cells = <1>;
++                      clocks = <&nsscc NSS_CC_PPE_SWITCH_CLK>,
++                               <&nsscc NSS_CC_PPE_SWITCH_CFG_CLK>,
++                               <&nsscc NSS_CC_PPE_SWITCH_IPE_CLK>,
++                               <&nsscc NSS_CC_PPE_SWITCH_BTQ_CLK>;
++                      clock-names = "ppe",
++                                    "ppe_cfg",
++                                    "ppe_ipe",
++                                    "ppe_btq";
++                      resets = <&nsscc PPE_FULL_RESET>;
++                      interconnects = <&nsscc MASTER_NSSNOC_PPE
++                                       &nsscc SLAVE_NSSNOC_PPE>,
++                                      <&nsscc MASTER_NSSNOC_PPE_CFG
++                                       &nsscc SLAVE_NSSNOC_PPE_CFG>,
++                                      <&gcc MASTER_NSSNOC_QOSGEN_REF
++                                       &gcc SLAVE_NSSNOC_QOSGEN_REF>,
++                                      <&gcc MASTER_NSSNOC_TIMEOUT_REF
++                                       &gcc SLAVE_NSSNOC_TIMEOUT_REF>,
++                                      <&gcc MASTER_MEM_NOC_NSSNOC
++                                       &gcc SLAVE_MEM_NOC_NSSNOC>,
++                                      <&gcc MASTER_NSSNOC_MEMNOC
++                                       &gcc SLAVE_NSSNOC_MEMNOC>,
++                                      <&gcc MASTER_NSSNOC_MEM_NOC_1
++                                       &gcc SLAVE_NSSNOC_MEM_NOC_1>;
++                      interconnect-names = "ppe",
++                                           "ppe_cfg",
++                                           "qos_gen",
++                                           "timeout_ref",
++                                           "nssnoc_memnoc",
++                                           "memnoc_nssnoc",
++                                           "memnoc_nssnoc_1";
++              };
++
+               pcs0: ethernet-pcs@7a00000 {
+                       compatible = "qcom,ipq9574-pcs";
+                       reg = <0x7a00000 0x10000>;
diff --git a/target/linux/qualcommbe/patches-6.18/0354-arm64-dts-qcom-Add-EDMA-node-for-IPQ9574.patch b/target/linux/qualcommbe/patches-6.18/0354-arm64-dts-qcom-Add-EDMA-node-for-IPQ9574.patch
new file mode 100644 (file)
index 0000000..f93ed0c
--- /dev/null
@@ -0,0 +1,91 @@
+From bd50babc7db2a35d98236a0386173dccd6c6374b Mon Sep 17 00:00:00 2001
+From: Pavithra R <quic_pavir@quicinc.com>
+Date: Wed, 6 Mar 2024 22:29:41 +0530
+Subject: [PATCH] arm64: dts: qcom: Add EDMA node for IPQ9574
+
+Add EDMA (Ethernet DMA) device tree node for IPQ9574 to
+enable ethernet support.
+
+Change-Id: I87d7c50f2485c8670948dce305000337f6499f8b
+Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
+---
+ arch/arm64/boot/dts/qcom/ipq9574.dtsi | 68 +++++++++++++++++++++++++++
+ 1 file changed, 68 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq9574.dtsi
+@@ -1308,6 +1308,74 @@
+                                            "nssnoc_memnoc",
+                                            "memnoc_nssnoc",
+                                            "memnoc_nssnoc_1";
++
++                      edma {
++                              compatible = "qcom,ipq9574-edma";
++                              clocks = <&nsscc NSS_CC_PPE_EDMA_CLK>,
++                                       <&nsscc NSS_CC_PPE_EDMA_CFG_CLK>;
++                              clock-names = "edma",
++                                            "edma-cfg";
++                              resets = <&nsscc EDMA_HW_RESET>;
++                              reset-names = "edma_rst";
++                              interrupts = <GIC_SPI 371 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 372 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 373 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 374 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 375 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 376 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 377 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 378 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 379 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 380 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 381 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 382 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 383 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 384 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 509 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 508 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 507 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 506 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 505 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 504 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 503 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 502 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 501 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 500 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 351 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 352 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 353 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 354 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 499 IRQ_TYPE_LEVEL_HIGH>;
++                              interrupt-names = "edma_txcmpl_8",
++                                                "edma_txcmpl_9",
++                                                "edma_txcmpl_10",
++                                                "edma_txcmpl_11",
++                                                "edma_txcmpl_12",
++                                                "edma_txcmpl_13",
++                                                "edma_txcmpl_14",
++                                                "edma_txcmpl_15",
++                                                "edma_txcmpl_16",
++                                                "edma_txcmpl_17",
++                                                "edma_txcmpl_18",
++                                                "edma_txcmpl_19",
++                                                "edma_txcmpl_20",
++                                                "edma_txcmpl_21",
++                                                "edma_txcmpl_22",
++                                                "edma_txcmpl_23",
++                                                "edma_txcmpl_24",
++                                                "edma_txcmpl_25",
++                                                "edma_txcmpl_26",
++                                                "edma_txcmpl_27",
++                                                "edma_txcmpl_28",
++                                                "edma_txcmpl_29",
++                                                "edma_txcmpl_30",
++                                                "edma_txcmpl_31",
++                                                "edma_rxdesc_20",
++                                                "edma_rxdesc_21",
++                                                "edma_rxdesc_22",
++                                                "edma_rxdesc_23",
++                                                "edma_misc";
++                      };
+               };
+               pcs0: ethernet-pcs@7a00000 {
diff --git a/target/linux/qualcommbe/patches-6.18/0355-arm64-dts-qcom-Add-IPQ9574-RDP433-port-node.patch b/target/linux/qualcommbe/patches-6.18/0355-arm64-dts-qcom-Add-IPQ9574-RDP433-port-node.patch
new file mode 100644 (file)
index 0000000..b7a7035
--- /dev/null
@@ -0,0 +1,197 @@
+From 001b663ecc5f838dac143623badae0e472749d8a Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Tue, 14 May 2024 10:53:27 +0800
+Subject: [PATCH] arm64: dts: qcom: Add IPQ9574 RDP433 port node
+
+There are 6 PPE MAC ports available on RDP433. The port1-port4 are
+connected with QCA8075 QUAD PHYs through UNIPHY0 PCS channel0-channel3.
+The port5 is connected with Aquantia PHY through UNIPHY1 PCS channel0
+and the port6 is connected with Aquantia PHY through UNIPHY2 PCS
+channel0.
+
+Change-Id: Ic16efdef2fe2cff7b1e80245619c0f82afb24cb9
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+---
+ arch/arm64/boot/dts/qcom/ipq9574-rdp433.dts | 167 ++++++++++++++++++++
+ 1 file changed, 167 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574-rdp433.dts
++++ b/arch/arm64/boot/dts/qcom/ipq9574-rdp433.dts
+@@ -55,6 +55,46 @@
+       status = "okay";
+ };
++&mdio {
++      reset-gpios = <&tlmm 60 GPIO_ACTIVE_LOW>;
++      clock-frequency = <6250000>;
++      status = "okay";
++
++      ethernet-phy-package@0 {
++              compatible = "qcom,qca8075-package";
++              #address-cells = <1>;
++              #size-cells = <0>;
++              reg = <0x10>;
++              qcom,package-mode = "qsgmii";
++
++              phy0: ethernet-phy@10 {
++                      reg = <0x10>;
++              };
++
++              phy1: ethernet-phy@11 {
++                      reg = <0x11>;
++              };
++
++              phy2: ethernet-phy@12 {
++                      reg = <0x12>;
++              };
++
++              phy3: ethernet-phy@13 {
++                      reg = <0x13>;
++              };
++      };
++
++      phy4: ethernet-phy@8 {
++              compatible ="ethernet-phy-ieee802.3-c45";
++              reg = <8>;
++      };
++
++      phy5: ethernet-phy@0 {
++              compatible ="ethernet-phy-ieee802.3-c45";
++              reg = <0>;
++      };
++};
++
+ &tlmm {
+       pcie1_default: pcie1-default-state {
+@@ -161,3 +201,130 @@
+               };
+       };
+ };
++
++&qcom_ppe {
++      ethernet-ports {
++              #address-cells = <1>;
++              #size-cells = <0>;
++
++              port@1 {
++                      reg = <1>;
++                      phy-mode = "qsgmii";
++                      managed = "in-band-status";
++                      phy-handle = <&phy0>;
++                      pcs-handle = <&pcs0_ch0>;
++                      clocks = <&nsscc NSS_CC_PORT1_MAC_CLK>,
++                               <&nsscc NSS_CC_PORT1_RX_CLK>,
++                               <&nsscc NSS_CC_PORT1_TX_CLK>;
++                      clock-names = "port_mac",
++                                    "port_rx",
++                                    "port_tx";
++                      resets = <&nsscc PORT1_MAC_ARES>,
++                               <&nsscc PORT1_RX_ARES>,
++                               <&nsscc PORT1_TX_ARES>;
++                      reset-names = "port_mac",
++                                    "port_rx",
++                                    "port_tx";
++              };
++
++              port@2 {
++                      reg = <2>;
++                      phy-mode = "qsgmii";
++                      managed = "in-band-status";
++                      phy-handle = <&phy1>;
++                      pcs-handle = <&pcs0_ch1>;
++                      clocks = <&nsscc NSS_CC_PORT2_MAC_CLK>,
++                               <&nsscc NSS_CC_PORT2_RX_CLK>,
++                               <&nsscc NSS_CC_PORT2_TX_CLK>;
++                      clock-names = "port_mac",
++                                    "port_rx",
++                                    "port_tx";
++                      resets = <&nsscc PORT2_MAC_ARES>,
++                               <&nsscc PORT2_RX_ARES>,
++                               <&nsscc PORT2_TX_ARES>;
++                      reset-names = "port_mac",
++                                    "port_rx",
++                                    "port_tx";
++              };
++
++              port@3 {
++                      reg = <3>;
++                      phy-mode = "qsgmii";
++                      managed = "in-band-status";
++                      phy-handle = <&phy2>;
++                      pcs-handle = <&pcs0_ch2>;
++                      clocks = <&nsscc NSS_CC_PORT3_MAC_CLK>,
++                               <&nsscc NSS_CC_PORT3_RX_CLK>,
++                               <&nsscc NSS_CC_PORT3_TX_CLK>;
++                      clock-names = "port_mac",
++                                    "port_rx",
++                                    "port_tx";
++                      resets = <&nsscc PORT3_MAC_ARES>,
++                               <&nsscc PORT3_RX_ARES>,
++                               <&nsscc PORT3_TX_ARES>;
++                      reset-names = "port_mac",
++                                    "port_rx",
++                                    "port_tx";
++              };
++
++              port@4 {
++                      reg = <4>;
++                      phy-mode = "qsgmii";
++                      managed = "in-band-status";
++                      phy-handle = <&phy3>;
++                      pcs-handle = <&pcs0_ch3>;
++                      clocks = <&nsscc NSS_CC_PORT4_MAC_CLK>,
++                               <&nsscc NSS_CC_PORT4_RX_CLK>,
++                               <&nsscc NSS_CC_PORT4_TX_CLK>;
++                      clock-names = "port_mac",
++                                    "port_rx",
++                                    "port_tx";
++                      resets = <&nsscc PORT4_MAC_ARES>,
++                               <&nsscc PORT4_RX_ARES>,
++                               <&nsscc PORT4_TX_ARES>;
++                      reset-names = "port_mac",
++                                    "port_rx",
++                                    "port_tx";
++              };
++
++              port@5 {
++                      reg = <5>;
++                      phy-mode = "usxgmii";
++                      managed = "in-band-status";
++                      phy-handle = <&phy4>;
++                      pcs-handle = <&pcs1_ch0>;
++                      clocks = <&nsscc NSS_CC_PORT5_MAC_CLK>,
++                               <&nsscc NSS_CC_PORT5_RX_CLK>,
++                               <&nsscc NSS_CC_PORT5_TX_CLK>;
++                      clock-names = "port_mac",
++                                    "port_rx",
++                                    "port_tx";
++                      resets = <&nsscc PORT5_MAC_ARES>,
++                               <&nsscc PORT5_RX_ARES>,
++                               <&nsscc PORT5_TX_ARES>;
++                      reset-names = "port_mac",
++                                    "port_rx",
++                                    "port_tx";
++              };
++
++              port@6 {
++                      reg = <6>;
++                      phy-mode = "usxgmii";
++                      managed = "in-band-status";
++                      phy-handle = <&phy5>;
++                      pcs-handle = <&pcs2_ch0>;
++                      clocks = <&nsscc NSS_CC_PORT6_MAC_CLK>,
++                               <&nsscc NSS_CC_PORT6_RX_CLK>,
++                               <&nsscc NSS_CC_PORT6_TX_CLK>;
++                      clock-names = "port_mac",
++                                    "port_rx",
++                                    "port_tx";
++                      resets = <&nsscc PORT6_MAC_ARES>,
++                               <&nsscc PORT6_RX_ARES>,
++                               <&nsscc PORT6_TX_ARES>;
++                      reset-names = "port_mac",
++                                    "port_rx",
++                                    "port_tx";
++              };
++      };
++};
diff --git a/target/linux/qualcommbe/patches-6.18/0356-arm64-dts-qcom-add-AQR-NVMEM-node-for-IPQ9574-RDP433.patch b/target/linux/qualcommbe/patches-6.18/0356-arm64-dts-qcom-add-AQR-NVMEM-node-for-IPQ9574-RDP433.patch
new file mode 100644 (file)
index 0000000..e508bad
--- /dev/null
@@ -0,0 +1,33 @@
+From 30b751f5984e295f0b5e7a2308b6103fae3322d2 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 9 Dec 2024 18:10:43 +0100
+Subject: [PATCH] arm64: dts: qcom: add AQR NVMEM node for IPQ9574 RDP433 board
+
+Add Aquantia NVMEM node for IPQ9574 RDP433 board to load the firmware
+for the Aquantia PHY.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ arch/arm64/boot/dts/qcom/ipq9574-rdp433.dts | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574-rdp433.dts
++++ b/arch/arm64/boot/dts/qcom/ipq9574-rdp433.dts
+@@ -87,11 +87,17 @@
+       phy4: ethernet-phy@8 {
+               compatible ="ethernet-phy-ieee802.3-c45";
+               reg = <8>;
++
++              nvmem-cells = <&aqr_fw>;
++              nvmem-cell-names = "firmware";
+       };
+       phy5: ethernet-phy@0 {
+               compatible ="ethernet-phy-ieee802.3-c45";
+               reg = <0>;
++
++              nvmem-cells = <&aqr_fw>;
++              nvmem-cell-names = "firmware";
+       };
+ };
diff --git a/target/linux/qualcommbe/patches-6.18/0357-arm64-dts-qcom-Add-label-to-EDMA-port-for-IPQ9574-RD.patch b/target/linux/qualcommbe/patches-6.18/0357-arm64-dts-qcom-Add-label-to-EDMA-port-for-IPQ9574-RD.patch
new file mode 100644 (file)
index 0000000..950c9fa
--- /dev/null
@@ -0,0 +1,62 @@
+From b297d12d434191845cf8ae359466dcd8312ed21d Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 4 Dec 2024 01:49:09 +0100
+Subject: [PATCH] arm64: dts: qcom: Add label to EDMA port for IPQ9574 RDP433
+
+Add label to EDMA port for IPQ9574 RDP433 board.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ arch/arm64/boot/dts/qcom/ipq9574-rdp433.dts | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574-rdp433.dts
++++ b/arch/arm64/boot/dts/qcom/ipq9574-rdp433.dts
+@@ -217,6 +217,7 @@
+                       reg = <1>;
+                       phy-mode = "qsgmii";
+                       managed = "in-band-status";
++                      label = "lan1";
+                       phy-handle = <&phy0>;
+                       pcs-handle = <&pcs0_ch0>;
+                       clocks = <&nsscc NSS_CC_PORT1_MAC_CLK>,
+@@ -237,6 +238,7 @@
+                       reg = <2>;
+                       phy-mode = "qsgmii";
+                       managed = "in-band-status";
++                      label = "lan2";
+                       phy-handle = <&phy1>;
+                       pcs-handle = <&pcs0_ch1>;
+                       clocks = <&nsscc NSS_CC_PORT2_MAC_CLK>,
+@@ -257,6 +259,7 @@
+                       reg = <3>;
+                       phy-mode = "qsgmii";
+                       managed = "in-band-status";
++                      label = "lan3";
+                       phy-handle = <&phy2>;
+                       pcs-handle = <&pcs0_ch2>;
+                       clocks = <&nsscc NSS_CC_PORT3_MAC_CLK>,
+@@ -277,6 +280,7 @@
+                       reg = <4>;
+                       phy-mode = "qsgmii";
+                       managed = "in-band-status";
++                      label = "lan4";
+                       phy-handle = <&phy3>;
+                       pcs-handle = <&pcs0_ch3>;
+                       clocks = <&nsscc NSS_CC_PORT4_MAC_CLK>,
+@@ -297,6 +301,7 @@
+                       reg = <5>;
+                       phy-mode = "usxgmii";
+                       managed = "in-band-status";
++                      label = "lan5";
+                       phy-handle = <&phy4>;
+                       pcs-handle = <&pcs1_ch0>;
+                       clocks = <&nsscc NSS_CC_PORT5_MAC_CLK>,
+@@ -317,6 +322,7 @@
+                       reg = <6>;
+                       phy-mode = "usxgmii";
+                       managed = "in-band-status";
++                      label = "wan";
+                       phy-handle = <&phy5>;
+                       pcs-handle = <&pcs2_ch0>;
+                       clocks = <&nsscc NSS_CC_PORT6_MAC_CLK>,
diff --git a/target/linux/qualcommbe/patches-6.18/0358-clk-qcom-nsscc-Attach-required-NSSNOC-clock-to-PM-do.patch b/target/linux/qualcommbe/patches-6.18/0358-clk-qcom-nsscc-Attach-required-NSSNOC-clock-to-PM-do.patch
new file mode 100644 (file)
index 0000000..372572a
--- /dev/null
@@ -0,0 +1,73 @@
+From 6417cb20e854194a845d4ab092b92fd753c0e405 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 30 Jan 2025 16:11:14 +0100
+Subject: [PATCH] clk: qcom: nsscc: Attach required NSSNOC clock to PM domain
+
+There is currently a problem with ICC clock disabling the NSSNOC clock
+as there isn't any user for them on calling sync_state.
+This cause the kernel to stall if NSS is enabled and reboot with the watchdog.
+
+This is caused by the fact that the NSSNOC clock nsscc, snoc and snoc_1
+are actually required to make the NSS work and make the system continue
+booting.
+
+To attach these clock, setup pm-clk in nsscc and setup the correct
+resume/suspend OPs.
+
+With this change, the clock gets correctly attached and are not disabled
+when ICC call the sync_state.
+
+Suggested-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Alex G: Retrieve clocks by name rather than index.
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/clk/qcom/nsscc-ipq9574.c | 21 +++++++++++++++++++++
+ 1 file changed, 21 insertions(+)
+
+--- a/drivers/clk/qcom/nsscc-ipq9574.c
++++ b/drivers/clk/qcom/nsscc-ipq9574.c
+@@ -3060,6 +3060,7 @@ MODULE_DEVICE_TABLE(of, nss_cc_ipq9574_m
+ static int nss_cc_ipq9574_probe(struct platform_device *pdev)
+ {
++      struct device *dev = &pdev->dev;
+       struct regmap *regmap;
+       int ret;
+@@ -3075,6 +3076,18 @@ static int nss_cc_ipq9574_probe(struct p
+       if (ret)
+               return dev_err_probe(&pdev->dev, ret, "Fail to add bus clock\n");
++      ret = pm_clk_add(&pdev->dev, "nssnoc");
++      if (ret)
++              return dev_err_probe(dev, ret,"failed to acquire nssnoc clock\n");
++
++      ret = pm_clk_add(&pdev->dev, "snoc");
++      if (ret)
++              return dev_err_probe(dev, ret,"failed to acquire snoc clock\n");
++
++      ret = pm_clk_add(&pdev->dev, "snoc_1");
++      if (ret)
++              return dev_err_probe(dev, ret,"failed to acquire snoc_1 clock\n");
++
+       ret = pm_runtime_resume_and_get(&pdev->dev);
+       if (ret)
+               return dev_err_probe(&pdev->dev, ret, "Fail to resume\n");
+@@ -3089,8 +3102,16 @@ static int nss_cc_ipq9574_probe(struct p
+       clk_alpha_pll_configure(&ubi32_pll_main, regmap, &ubi32_pll_config);
+       ret = qcom_cc_really_probe(&pdev->dev, &nss_cc_ipq9574_desc, regmap);
++      if (ret)
++              goto err_put_pm;
++
+       pm_runtime_put(&pdev->dev);
++      return 0;
++
++err_put_pm:
++      pm_runtime_put_sync(dev);
++
+       return ret;
+ }
diff --git a/target/linux/qualcommbe/patches-6.18/0359-arm64-dts-qcom-ipq9574-add-NSSNOC-clock-to-nss-node.patch b/target/linux/qualcommbe/patches-6.18/0359-arm64-dts-qcom-ipq9574-add-NSSNOC-clock-to-nss-node.patch
new file mode 100644 (file)
index 0000000..c32f077
--- /dev/null
@@ -0,0 +1,42 @@
+From 372bbae100ffe14908bfd8448143c6cdbea17e8d Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Thu, 30 Jan 2025 16:23:03 +0100
+Subject: [PATCH] arm64: dts: qcom: ipq9574: add NSSNOC clock to nss node
+
+Add NSSNOC clock to nss node to attach the clock with PM clock and fix
+the boot stall after ICC sync_state.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+Alex G: Do not remove GCC_NSSCC_CLK ("bus") clock
+        Add clock-names for the new clocks
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ arch/arm64/boot/dts/qcom/ipq9574.dtsi | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq9574.dtsi
+@@ -1255,7 +1255,10 @@
+                                <&pcs1 1>,
+                                <&pcs2 0>,
+                                <&pcs2 1>,
+-                               <&gcc GCC_NSSCC_CLK>;
++                               <&gcc GCC_NSSCC_CLK>,
++                               <&gcc GCC_NSSNOC_NSSCC_CLK>,
++                               <&gcc GCC_NSSNOC_SNOC_CLK>,
++                               <&gcc GCC_NSSNOC_SNOC_1_CLK>;
+                       clock-names = "xo",
+                                     "nss_1200",
+                                     "ppe_353",
+@@ -1266,7 +1269,10 @@
+                                     "uniphy1_tx",
+                                     "uniphy2_rx",
+                                     "uniphy2_tx",
+-                                    "bus";
++                                    "bus",
++                                    "nssnoc",
++                                    "snoc",
++                                    "snoc_1";
+                       #clock-cells = <1>;
+                       #reset-cells = <1>;
+                       #interconnect-cells = <1>;
diff --git a/target/linux/qualcommbe/patches-6.18/0360-clk-qcom-nsscc-ipq9574-fix-port5-clock-config.patch b/target/linux/qualcommbe/patches-6.18/0360-clk-qcom-nsscc-ipq9574-fix-port5-clock-config.patch
new file mode 100644 (file)
index 0000000..725079c
--- /dev/null
@@ -0,0 +1,46 @@
+From fa691ff57c72a8f0bfeff1a9e86ae2d78765b0da Mon Sep 17 00:00:00 2001
+From: Mantas Pucka <mantas@8devices.com>
+Date: Mon, 31 Mar 2025 15:39:59 +0300
+Subject: [PATCH] clk: qcom: nsscc-ipq9574: fix port5 clock config
+
+Currently there is no configuration to derive 25/125MHz port5 clock
+from uniphy1 running at 125MHz. This is needed for SGMII mode when
+port5 is using uniphy1.
+
+Fix this by adding option such clock config option.
+
+Signed-off-by: Mantas Pucka <mantas@8devices.com>
+---
+ drivers/clk/qcom/nsscc-ipq9574.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/clk/qcom/nsscc-ipq9574.c
++++ b/drivers/clk/qcom/nsscc-ipq9574.c
+@@ -383,11 +383,13 @@ static const struct freq_multi_tbl ftbl_
+ static const struct freq_conf ftbl_nss_cc_port5_rx_clk_src_25[] = {
+       C(P_UNIPHY1_NSS_RX_CLK, 12.5, 0, 0),
++      C(P_UNIPHY1_NSS_RX_CLK, 5, 0, 0),
+       C(P_UNIPHY0_NSS_RX_CLK, 5, 0, 0),
+ };
+ static const struct freq_conf ftbl_nss_cc_port5_rx_clk_src_125[] = {
+       C(P_UNIPHY1_NSS_RX_CLK, 2.5, 0, 0),
++      C(P_UNIPHY1_NSS_RX_CLK, 1, 0, 0),
+       C(P_UNIPHY0_NSS_RX_CLK, 1, 0, 0),
+ };
+@@ -408,11 +410,13 @@ static const struct freq_multi_tbl ftbl_
+ static const struct freq_conf ftbl_nss_cc_port5_tx_clk_src_25[] = {
+       C(P_UNIPHY1_NSS_TX_CLK, 12.5, 0, 0),
++      C(P_UNIPHY1_NSS_TX_CLK, 5, 0, 0),
+       C(P_UNIPHY0_NSS_TX_CLK, 5, 0, 0),
+ };
+ static const struct freq_conf ftbl_nss_cc_port5_tx_clk_src_125[] = {
+       C(P_UNIPHY1_NSS_TX_CLK, 2.5, 0, 0),
++      C(P_UNIPHY1_NSS_TX_CLK, 1, 0, 0),
+       C(P_UNIPHY0_NSS_TX_CLK, 1, 0, 0),
+ };
diff --git a/target/linux/qualcommbe/patches-6.18/0361-net-pcs-Add-10GBASER-interface-mode-support-to-IPQ-U.patch b/target/linux/qualcommbe/patches-6.18/0361-net-pcs-Add-10GBASER-interface-mode-support-to-IPQ-U.patch
new file mode 100644 (file)
index 0000000..5bdf3e9
--- /dev/null
@@ -0,0 +1,136 @@
+From 432c2a2da1e0f4a8e2c0fea191361832a7f90f36 Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Wed, 6 Mar 2024 17:40:52 +0800
+Subject: [PATCH] net: pcs: Add 10GBASER interface mode support to IPQ UNIPHY
+ PCS driver
+
+10GBASER mode is used when PCS connects with a 10G SFP module.
+
+Change-Id: Ifc3c3bb23811807a9b34e88771aab2c830c2327c
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+Alex G: Use regmap to read/write registers
+        Remove xpcs_reset deassert logic (to be implemented later)
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/pcs/pcs-qcom-ipq9574.c | 47 ++++++++++++++++++++++++++++++
+ 1 file changed, 47 insertions(+)
+
+--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
++++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
+@@ -55,6 +55,9 @@
+                                        FIELD_PREP(GENMASK(9, 2), \
+                                        FIELD_GET(XPCS_INDIRECT_ADDR_L, reg)))
++#define XPCS_KR_STS                   0x30020
++#define XPCS_KR_LINK_STS              BIT(12)
++
+ #define XPCS_DIG_CTRL                 0x38000
+ #define XPCS_USXG_ADPT_RESET          BIT(10)
+ #define XPCS_USXG_EN                  BIT(9)
+@@ -196,6 +199,28 @@ static void ipq_pcs_get_state_usxgmii(st
+       state->duplex = DUPLEX_FULL;
+ }
++static void ipq_pcs_get_state_10gbaser(struct ipq_pcs *qpcs,
++                                     struct phylink_link_state *state)
++{
++      unsigned int val;
++      int ret;
++
++      ret = regmap_read(qpcs->regmap, XPCS_KR_STS, &val);
++      if (ret) {
++              state->link = 0;
++              return;
++      }
++
++      state->link = !!(val & XPCS_KR_LINK_STS);
++
++      if (!state->link)
++              return;
++
++      state->speed = SPEED_10000;
++      state->duplex = DUPLEX_FULL;
++      state->pause |= MLO_PAUSE_TXRX_MASK;
++}
++
+ static int ipq_pcs_config_mode(struct ipq_pcs *qpcs,
+                              phy_interface_t interface)
+ {
+@@ -212,6 +237,7 @@ static int ipq_pcs_config_mode(struct ip
+               val = PCS_MODE_QSGMII;
+               break;
+       case PHY_INTERFACE_MODE_USXGMII:
++      case PHY_INTERFACE_MODE_10GBASER:
+               val = PCS_MODE_XPCS;
+               rate = 312500000;
+               break;
+@@ -311,6 +337,15 @@ static int ipq_pcs_config_usxgmii(struct
+       return regmap_set_bits(qpcs->regmap, XPCS_MII_CTRL, XPCS_MII_AN_EN);
+ }
++static int ipq_pcs_config_10gbaser(struct ipq_pcs *qpcs)
++{
++      /* Configure 10GBASER mode if required */
++      if (qpcs->interface == PHY_INTERFACE_MODE_10GBASER)
++              return 0;
++
++      return ipq_pcs_config_mode(qpcs, PHY_INTERFACE_MODE_10GBASER);
++}
++
+ static int ipq_pcs_link_up_config_sgmii(struct ipq_pcs *qpcs,
+                                       int index,
+                                       unsigned int neg_mode,
+@@ -399,6 +434,7 @@ static int ipq_pcs_validate(struct phyli
+       switch (state->interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
++      case PHY_INTERFACE_MODE_10GBASER:
+               return 0;
+       case PHY_INTERFACE_MODE_USXGMII:
+               /* USXGMII only supports full duplex mode */
+@@ -418,6 +454,8 @@ static unsigned int ipq_pcs_inband_caps(
+       case PHY_INTERFACE_MODE_QSGMII:
+       case PHY_INTERFACE_MODE_USXGMII:
+               return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
++      case PHY_INTERFACE_MODE_10GBASER:
++              return LINK_INBAND_DISABLE;
+       default:
+               return 0;
+       }
+@@ -472,6 +510,9 @@ static void ipq_pcs_get_state(struct phy
+       case PHY_INTERFACE_MODE_USXGMII:
+               ipq_pcs_get_state_usxgmii(qpcs, state);
+               break;
++      case PHY_INTERFACE_MODE_10GBASER:
++              ipq_pcs_get_state_10gbaser(qpcs, state);
++              break;
+       default:
+               break;
+       }
+@@ -500,6 +541,8 @@ static int ipq_pcs_config(struct phylink
+               return ipq_pcs_config_sgmii(qpcs, index, neg_mode, interface);
+       case PHY_INTERFACE_MODE_USXGMII:
+               return ipq_pcs_config_usxgmii(qpcs);
++      case PHY_INTERFACE_MODE_10GBASER:
++              return ipq_pcs_config_10gbaser(qpcs);
+       default:
+               return -EOPNOTSUPP;
+       };
+@@ -524,6 +567,9 @@ static void ipq_pcs_link_up(struct phyli
+       case PHY_INTERFACE_MODE_USXGMII:
+               ret = ipq_pcs_link_up_config_usxgmii(qpcs, speed);
+               break;
++      case PHY_INTERFACE_MODE_10GBASER:
++              /* Nothing to do here */
++              return;
+       default:
+               return;
+       }
+@@ -603,6 +649,7 @@ static unsigned long ipq_pcs_clk_rate_ge
+ {
+       switch (qpcs->interface) {
+       case PHY_INTERFACE_MODE_USXGMII:
++      case PHY_INTERFACE_MODE_10GBASER:
+               return 312500000;
+       default:
+               return 125000000;
diff --git a/target/linux/qualcommbe/patches-6.18/0362-net-pcs-Add-2500BASEX-interface-mode-support-to-IPQ-.patch b/target/linux/qualcommbe/patches-6.18/0362-net-pcs-Add-2500BASEX-interface-mode-support-to-IPQ-.patch
new file mode 100644 (file)
index 0000000..fbdebec
--- /dev/null
@@ -0,0 +1,166 @@
+From 0d3a93e3a5544daec59d8f10ac5ccab39849536e Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Tue, 2 Apr 2024 18:28:42 +0800
+Subject: [PATCH] net: pcs: Add 2500BASEX interface mode support to IPQ UNIPHY
+ PCS driver
+
+2500BASEX mode is used when PCS connects with QCA8386 switch in a fixed
+2500M link. It is also used when PCS connectes with QCA8081 PHY which
+works at 2500M link speed. In addition, it can be also used when PCS
+connects with a 2.5G SFP module.
+
+Change-Id: I3fe61113c1b3685debc20659736a9488216a029d
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+Alex G: use regmap to read/write registers
+        's/ipq_unipcs/ipq_pcs/' in function names as suggested by Luo Jie
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/pcs/pcs-qcom-ipq9574.c | 67 ++++++++++++++++++++++++++++++
+ 1 file changed, 67 insertions(+)
+
+--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
++++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
+@@ -26,6 +26,7 @@
+ #define PCS_MODE_SEL_MASK             GENMASK(12, 8)
+ #define PCS_MODE_SGMII                        FIELD_PREP(PCS_MODE_SEL_MASK, 0x4)
+ #define PCS_MODE_QSGMII                       FIELD_PREP(PCS_MODE_SEL_MASK, 0x1)
++#define PCS_MODE_2500BASEX            FIELD_PREP(PCS_MODE_SEL_MASK, 0x8)
+ #define PCS_MODE_XPCS                 FIELD_PREP(PCS_MODE_SEL_MASK, 0x10)
+ #define PCS_MII_CTRL(x)                       (0x480 + 0x18 * (x))
+@@ -155,6 +156,29 @@ static void ipq_pcs_get_state_sgmii(stru
+               state->duplex = DUPLEX_HALF;
+ }
++static void ipq_pcs_get_state_2500basex(struct ipq_pcs *qpcs,
++                                      struct phylink_link_state *state)
++{
++      unsigned int val;
++      int ret;
++
++      ret = regmap_read(qpcs->regmap, PCS_MII_STS(0), &val);
++      if (ret) {
++              state->link = 0;
++              return;
++      }
++
++
++      state->link = !!(val & PCS_MII_LINK_STS);
++
++      if (!state->link)
++              return;
++
++      state->speed = SPEED_2500;
++      state->duplex = DUPLEX_FULL;
++      state->pause |= MLO_PAUSE_TXRX_MASK;
++}
++
+ static void ipq_pcs_get_state_usxgmii(struct ipq_pcs *qpcs,
+                                     struct phylink_link_state *state)
+ {
+@@ -236,6 +260,10 @@ static int ipq_pcs_config_mode(struct ip
+       case PHY_INTERFACE_MODE_QSGMII:
+               val = PCS_MODE_QSGMII;
+               break;
++      case PHY_INTERFACE_MODE_2500BASEX:
++              val = PCS_MODE_2500BASEX;
++              rate = 312500000;
++              break;
+       case PHY_INTERFACE_MODE_USXGMII:
+       case PHY_INTERFACE_MODE_10GBASER:
+               val = PCS_MODE_XPCS;
+@@ -314,6 +342,15 @@ static int ipq_pcs_config_sgmii(struct i
+                              PCS_MII_CTRL(index), PCS_MII_FORCE_MODE);
+ }
++static int ipq_pcs_config_2500basex(struct ipq_pcs *qpcs)
++{
++      /* Configure PCS for 2500BASEX mode if required */
++      if (qpcs->interface == PHY_INTERFACE_MODE_2500BASEX)
++              return 0;
++
++      return ipq_pcs_config_mode(qpcs, PHY_INTERFACE_MODE_2500BASEX);
++}
++
+ static int ipq_pcs_config_usxgmii(struct ipq_pcs *qpcs)
+ {
+       int ret;
+@@ -388,6 +425,22 @@ static int ipq_pcs_link_up_config_sgmii(
+                              PCS_MII_CTRL(index), PCS_MII_ADPT_RESET);
+ }
++static int ipq_pcs_link_up_config_2500basex(struct ipq_pcs *qpcs, int speed)
++{
++      int ret;
++
++      /* 2500BASEX does not support autoneg and does not need to
++       * configure PCS speed. Only reset PCS adapter here.
++       */
++      ret = regmap_clear_bits(qpcs->regmap,
++                              PCS_MII_CTRL(0), PCS_MII_ADPT_RESET);
++      if (ret)
++              return ret;
++
++      return regmap_set_bits(qpcs->regmap,
++                             PCS_MII_CTRL(0), PCS_MII_ADPT_RESET);
++}
++
+ static int ipq_pcs_link_up_config_usxgmii(struct ipq_pcs *qpcs, int speed)
+ {
+       unsigned int val;
+@@ -436,6 +489,10 @@ static int ipq_pcs_validate(struct phyli
+       case PHY_INTERFACE_MODE_QSGMII:
+       case PHY_INTERFACE_MODE_10GBASER:
+               return 0;
++      case PHY_INTERFACE_MODE_2500BASEX:
++              /* In-band autoneg is not supported for 2500BASEX */
++              phylink_clear(supported, Autoneg);
++              return 0;
+       case PHY_INTERFACE_MODE_USXGMII:
+               /* USXGMII only supports full duplex mode */
+               phylink_clear(supported, 100baseT_Half);
+@@ -454,6 +511,7 @@ static unsigned int ipq_pcs_inband_caps(
+       case PHY_INTERFACE_MODE_QSGMII:
+       case PHY_INTERFACE_MODE_USXGMII:
+               return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
++      case PHY_INTERFACE_MODE_2500BASEX:
+       case PHY_INTERFACE_MODE_10GBASER:
+               return LINK_INBAND_DISABLE;
+       default:
+@@ -507,6 +565,9 @@ static void ipq_pcs_get_state(struct phy
+       case PHY_INTERFACE_MODE_QSGMII:
+               ipq_pcs_get_state_sgmii(qpcs, index, state);
+               break;
++      case PHY_INTERFACE_MODE_2500BASEX:
++              ipq_pcs_get_state_2500basex(qpcs, state);
++              break;
+       case PHY_INTERFACE_MODE_USXGMII:
+               ipq_pcs_get_state_usxgmii(qpcs, state);
+               break;
+@@ -539,6 +600,8 @@ static int ipq_pcs_config(struct phylink
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
+               return ipq_pcs_config_sgmii(qpcs, index, neg_mode, interface);
++      case PHY_INTERFACE_MODE_2500BASEX:
++              return ipq_pcs_config_2500basex(qpcs);
+       case PHY_INTERFACE_MODE_USXGMII:
+               return ipq_pcs_config_usxgmii(qpcs);
+       case PHY_INTERFACE_MODE_10GBASER:
+@@ -564,6 +627,9 @@ static void ipq_pcs_link_up(struct phyli
+               ret = ipq_pcs_link_up_config_sgmii(qpcs, index,
+                                                  neg_mode, speed);
+               break;
++      case PHY_INTERFACE_MODE_2500BASEX:
++              ret = ipq_pcs_link_up_config_2500basex(qpcs, speed);
++              break;
+       case PHY_INTERFACE_MODE_USXGMII:
+               ret = ipq_pcs_link_up_config_usxgmii(qpcs, speed);
+               break;
+@@ -648,6 +714,7 @@ static int ipq_pcs_create_miis(struct ip
+ static unsigned long ipq_pcs_clk_rate_get(struct ipq_pcs *qpcs)
+ {
+       switch (qpcs->interface) {
++      case PHY_INTERFACE_MODE_2500BASEX:
+       case PHY_INTERFACE_MODE_USXGMII:
+       case PHY_INTERFACE_MODE_10GBASER:
+               return 312500000;
diff --git a/target/linux/qualcommbe/patches-6.18/0363-net-pcs-Add-1000BASEX-interface-mode-support-to-IPQ-.patch b/target/linux/qualcommbe/patches-6.18/0363-net-pcs-Add-1000BASEX-interface-mode-support-to-IPQ-.patch
new file mode 100644 (file)
index 0000000..37aa60d
--- /dev/null
@@ -0,0 +1,104 @@
+From d82953614a4f09dd7479e1d3904351ff85d1d088 Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Tue, 9 Apr 2024 01:07:22 +0800
+Subject: [PATCH] net: pcs: Add 1000BASEX interface mode support to IPQ UNIPHY
+ PCS driver
+
+1000BASEX is used when PCS connects with a 1G SFP module.
+
+Change-Id: Ied7298de3c1ecba74e6457a07fdd6b3ceab79728
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/pcs/pcs-qcom-ipq9574.c | 21 ++++++++++++++++++---
+ 1 file changed, 18 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
++++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
+@@ -28,6 +28,9 @@
+ #define PCS_MODE_QSGMII                       FIELD_PREP(PCS_MODE_SEL_MASK, 0x1)
+ #define PCS_MODE_2500BASEX            FIELD_PREP(PCS_MODE_SEL_MASK, 0x8)
+ #define PCS_MODE_XPCS                 FIELD_PREP(PCS_MODE_SEL_MASK, 0x10)
++#define PCS_MODE_SGMII_MODE_MASK      GENMASK(6, 4)
++#define PCS_MODE_SGMII_MODE_1000BASEX FIELD_PREP(PCS_MODE_SGMII_MODE_MASK, \
++                                                 0x0)
+ #define PCS_MII_CTRL(x)                       (0x480 + 0x18 * (x))
+ #define PCS_MII_ADPT_RESET            BIT(11)
+@@ -249,10 +252,11 @@ static int ipq_pcs_config_mode(struct ip
+                              phy_interface_t interface)
+ {
+       unsigned long rate = 125000000;
+-      unsigned int val;
++      unsigned int val, mask;
+       int ret;
+       /* Configure PCS interface mode */
++      mask = PCS_MODE_SEL_MASK;
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+               val = PCS_MODE_SGMII;
+@@ -260,6 +264,10 @@ static int ipq_pcs_config_mode(struct ip
+       case PHY_INTERFACE_MODE_QSGMII:
+               val = PCS_MODE_QSGMII;
+               break;
++      case PHY_INTERFACE_MODE_1000BASEX:
++              mask |= PCS_MODE_SGMII_MODE_MASK;
++              val = PCS_MODE_SGMII | PCS_MODE_SGMII_MODE_1000BASEX;
++              break;
+       case PHY_INTERFACE_MODE_2500BASEX:
+               val = PCS_MODE_2500BASEX;
+               rate = 312500000;
+@@ -273,8 +281,7 @@ static int ipq_pcs_config_mode(struct ip
+               return -EOPNOTSUPP;
+       }
+-      ret = regmap_update_bits(qpcs->regmap, PCS_MODE_CTRL,
+-                               PCS_MODE_SEL_MASK, val);
++      ret = regmap_update_bits(qpcs->regmap, PCS_MODE_CTRL, mask, val);
+       if (ret)
+               return ret;
+@@ -487,6 +494,7 @@ static int ipq_pcs_validate(struct phyli
+       switch (state->interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
++      case PHY_INTERFACE_MODE_1000BASEX:
+       case PHY_INTERFACE_MODE_10GBASER:
+               return 0;
+       case PHY_INTERFACE_MODE_2500BASEX:
+@@ -509,6 +517,7 @@ static unsigned int ipq_pcs_inband_caps(
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
++      case PHY_INTERFACE_MODE_1000BASEX:
+       case PHY_INTERFACE_MODE_USXGMII:
+               return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+       case PHY_INTERFACE_MODE_2500BASEX:
+@@ -563,6 +572,10 @@ static void ipq_pcs_get_state(struct phy
+       switch (state->interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
++      case PHY_INTERFACE_MODE_1000BASEX:
++              /* SGMII and 1000BASEX in-band autoneg word format are decoded
++               * by PCS hardware and both placed to the same status register.
++               */
+               ipq_pcs_get_state_sgmii(qpcs, index, state);
+               break;
+       case PHY_INTERFACE_MODE_2500BASEX:
+@@ -599,6 +612,7 @@ static int ipq_pcs_config(struct phylink
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
++      case PHY_INTERFACE_MODE_1000BASEX:
+               return ipq_pcs_config_sgmii(qpcs, index, neg_mode, interface);
+       case PHY_INTERFACE_MODE_2500BASEX:
+               return ipq_pcs_config_2500basex(qpcs);
+@@ -624,6 +638,7 @@ static void ipq_pcs_link_up(struct phyli
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
++      case PHY_INTERFACE_MODE_1000BASEX:
+               ret = ipq_pcs_link_up_config_sgmii(qpcs, index,
+                                                  neg_mode, speed);
+               break;
diff --git a/target/linux/qualcommbe/patches-6.18/0364-net-pcs-Add-10G_QXGMII-interface-mode-support-to-IPQ.patch b/target/linux/qualcommbe/patches-6.18/0364-net-pcs-Add-10G_QXGMII-interface-mode-support-to-IPQ.patch
new file mode 100644 (file)
index 0000000..2563ac8
--- /dev/null
@@ -0,0 +1,267 @@
+From fc26c6f6c69149ce87c88d6878ae929b2a138063 Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Mon, 15 Apr 2024 11:06:02 +0800
+Subject: [PATCH] net: pcs: Add 10G_QXGMII interface mode support to IPQ UNIPHY
+ PCS driver
+
+10G_QXGMII is used when PCS connectes with QCA8084 four ports
+2.5G PHYs.
+
+Change-Id: If3dc92a07ac3e51f7c9473fb05fa0668617916fb
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/pcs/pcs-qcom-ipq9574.c | 109 +++++++++++++++++++++++------
+ 1 file changed, 87 insertions(+), 22 deletions(-)
+
+--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
++++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
+@@ -48,6 +48,9 @@
+ #define PCS_MII_STS_SPEED_100         1
+ #define PCS_MII_STS_SPEED_1000                2
++#define PCS_QP_USXG_OPTION            0x584
++#define PCS_QP_USXG_GMII_SRC_XPCS     BIT(0)
++
+ #define PCS_PLL_RESET                 0x780
+ #define PCS_ANA_SW_RESET              BIT(6)
+@@ -63,10 +66,23 @@
+ #define XPCS_KR_LINK_STS              BIT(12)
+ #define XPCS_DIG_CTRL                 0x38000
++#define XPCS_SOFT_RESET                       BIT(15)
+ #define XPCS_USXG_ADPT_RESET          BIT(10)
+ #define XPCS_USXG_EN                  BIT(9)
++#define XPCS_KR_CTRL                  0x38007
++#define XPCS_USXG_MODE_MASK           GENMASK(12, 10)
++#define XPCS_10G_QXGMII_MODE          FIELD_PREP(XPCS_USXG_MODE_MASK, 0x5)
++
++#define XPCS_DIG_STS                  0x3800a
++#define XPCS_DIG_STS_AM_COUNT         GENMASK(14, 0)
++
++/* DIG control for MII1 - MII3 */
++#define XPCS_MII1_DIG_CTRL(x)         (0x1a8000 + 0x10000 * ((x) - 1))
++#define XPCS_MII1_USXG_ADPT_RESET     BIT(5)
++
+ #define XPCS_MII_CTRL                 0x1f0000
++#define XPCS_MII1_CTRL(x)             (0x1a0000 + 0x10000 * ((x) - 1))
+ #define XPCS_MII_AN_EN                        BIT(12)
+ #define XPCS_DUPLEX_FULL              BIT(8)
+ #define XPCS_SPEED_MASK                       (BIT(13) | BIT(6) | BIT(5))
+@@ -78,9 +94,11 @@
+ #define XPCS_SPEED_10                 0
+ #define XPCS_MII_AN_CTRL              0x1f8001
++#define XPCS_MII1_AN_CTRL(x)          (0x1a8001 + 0x10000 * ((x) - 1))
+ #define XPCS_MII_AN_8BIT              BIT(8)
+ #define XPCS_MII_AN_INTR_STS          0x1f8002
++#define XPCS_MII1_AN_INTR_STS(x)      (0x1a8002 + 0x10000 * ((x) - 1))
+ #define XPCS_USXG_AN_LINK_STS         BIT(14)
+ #define XPCS_USXG_AN_SPEED_MASK               GENMASK(12, 10)
+ #define XPCS_USXG_AN_SPEED_10         0
+@@ -90,6 +108,10 @@
+ #define XPCS_USXG_AN_SPEED_5000               5
+ #define XPCS_USXG_AN_SPEED_10000      3
++#define XPCS_XAUI_MODE_CTRL           0x1f8004
++#define XPCS_MII1_XAUI_MODE_CTRL(x)   (0x1a8004 + 0x10000 * ((x) - 1))
++#define XPCS_TX_IPG_CHECK_DIS         BIT(0)
++
+ /* Per PCS MII private data */
+ struct ipq_pcs_mii {
+       struct ipq_pcs *qpcs;
+@@ -182,13 +204,14 @@ static void ipq_pcs_get_state_2500basex(
+       state->pause |= MLO_PAUSE_TXRX_MASK;
+ }
+-static void ipq_pcs_get_state_usxgmii(struct ipq_pcs *qpcs,
++static void ipq_pcs_get_state_usxgmii(struct ipq_pcs *qpcs, int index,
+                                     struct phylink_link_state *state)
+ {
+-      unsigned int val;
++      unsigned int reg, val;
+       int ret;
+-      ret = regmap_read(qpcs->regmap, XPCS_MII_AN_INTR_STS, &val);
++      reg = (index == 0) ? XPCS_MII_AN_INTR_STS : XPCS_MII1_AN_INTR_STS(index);
++      ret = regmap_read(qpcs->regmap, reg, &val);
+       if (ret) {
+               state->link = 0;
+               return;
+@@ -273,6 +296,7 @@ static int ipq_pcs_config_mode(struct ip
+               rate = 312500000;
+               break;
+       case PHY_INTERFACE_MODE_USXGMII:
++      case PHY_INTERFACE_MODE_10G_QXGMII:
+       case PHY_INTERFACE_MODE_10GBASER:
+               val = PCS_MODE_XPCS;
+               rate = 312500000;
+@@ -285,6 +309,13 @@ static int ipq_pcs_config_mode(struct ip
+       if (ret)
+               return ret;
++      if (interface == PHY_INTERFACE_MODE_10G_QXGMII) {
++              ret = regmap_set_bits(qpcs->regmap, PCS_QP_USXG_OPTION,
++                                    PCS_QP_USXG_GMII_SRC_XPCS);
++              if (ret)
++                      return ret;
++      }
++
+       /* PCS PLL reset */
+       ret = regmap_clear_bits(qpcs->regmap, PCS_PLL_RESET, PCS_ANA_SW_RESET);
+       if (ret)
+@@ -358,27 +389,51 @@ static int ipq_pcs_config_2500basex(stru
+       return ipq_pcs_config_mode(qpcs, PHY_INTERFACE_MODE_2500BASEX);
+ }
+-static int ipq_pcs_config_usxgmii(struct ipq_pcs *qpcs)
++static int ipq_pcs_config_usxgmii(struct ipq_pcs *qpcs,
++                                int index,
++                                phy_interface_t interface)
+ {
++      unsigned int reg;
+       int ret;
+       /* Configure the XPCS for USXGMII mode if required */
+-      if (qpcs->interface == PHY_INTERFACE_MODE_USXGMII)
+-              return 0;
+-
+-      ret = ipq_pcs_config_mode(qpcs, PHY_INTERFACE_MODE_USXGMII);
+-      if (ret)
+-              return ret;
++      if (qpcs->interface != interface) {
++              ret = ipq_pcs_config_mode(qpcs, interface);
++              if (ret)
++                      return ret;
+-      ret = regmap_set_bits(qpcs->regmap, XPCS_DIG_CTRL, XPCS_USXG_EN);
+-      if (ret)
+-              return ret;
++              if (interface == PHY_INTERFACE_MODE_10G_QXGMII) {
++                      ret = regmap_update_bits(qpcs->regmap, XPCS_KR_CTRL,
++                                               XPCS_USXG_MODE_MASK, XPCS_10G_QXGMII_MODE);
++                      if (ret)
++                              return ret;
++
++                      /* Set Alignment Marker Interval value as 0x6018 */
++                      ret = regmap_update_bits(qpcs->regmap, XPCS_DIG_STS,
++                                               XPCS_DIG_STS_AM_COUNT, 0x6018);
++                      if (ret)
++                              return ret;
++
++                      ret = regmap_set_bits(qpcs->regmap, XPCS_DIG_CTRL, XPCS_SOFT_RESET);
++                      if (ret)
++                              return ret;
++      }
++
++      /* Disable Tx IPG check for 10G_QXGMII */
++      if (interface == PHY_INTERFACE_MODE_10G_QXGMII) {
++              reg = (index == 0) ? XPCS_XAUI_MODE_CTRL : XPCS_MII1_XAUI_MODE_CTRL(index);
++              ret = regmap_set_bits(qpcs->regmap, reg, XPCS_TX_IPG_CHECK_DIS);
++              if (ret)
++                      return ret;
++      }
+-      ret = regmap_set_bits(qpcs->regmap, XPCS_MII_AN_CTRL, XPCS_MII_AN_8BIT);
++      reg = (index == 0) ? XPCS_MII_AN_CTRL : XPCS_MII1_AN_CTRL(index);
++      ret = regmap_set_bits(qpcs->regmap, reg, XPCS_MII_AN_8BIT);
+       if (ret)
+               return ret;
+-      return regmap_set_bits(qpcs->regmap, XPCS_MII_CTRL, XPCS_MII_AN_EN);
++      reg = (index == 0) ? XPCS_MII_CTRL : XPCS_MII1_CTRL(index);
++      return regmap_set_bits(qpcs->regmap, reg, XPCS_MII_AN_EN);
+ }
+ static int ipq_pcs_config_10gbaser(struct ipq_pcs *qpcs)
+@@ -448,9 +503,10 @@ static int ipq_pcs_link_up_config_2500ba
+                              PCS_MII_CTRL(0), PCS_MII_ADPT_RESET);
+ }
+-static int ipq_pcs_link_up_config_usxgmii(struct ipq_pcs *qpcs, int speed)
++static int ipq_pcs_link_up_config_usxgmii(struct ipq_pcs *qpcs,
++                                        int index, int speed)
+ {
+-      unsigned int val;
++      unsigned int reg, val;
+       int ret;
+       switch (speed) {
+@@ -478,14 +534,17 @@ static int ipq_pcs_link_up_config_usxgmi
+       }
+       /* Configure XPCS speed */
+-      ret = regmap_update_bits(qpcs->regmap, XPCS_MII_CTRL,
++      reg = (index == 0) ? XPCS_MII_CTRL : XPCS_MII1_CTRL(index);
++      ret = regmap_update_bits(qpcs->regmap, reg,
+                                XPCS_SPEED_MASK, val | XPCS_DUPLEX_FULL);
+       if (ret)
+               return ret;
+       /* XPCS adapter reset */
+-      return regmap_set_bits(qpcs->regmap,
+-                             XPCS_DIG_CTRL, XPCS_USXG_ADPT_RESET);
++      reg = (index == 0) ? XPCS_DIG_CTRL : XPCS_MII1_DIG_CTRL(index);
++      val = (index == 0) ? XPCS_USXG_ADPT_RESET : XPCS_MII1_USXG_ADPT_RESET;
++      return regmap_set_bits(qpcs->regmap, reg, val);
++
+ }
+ static int ipq_pcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
+@@ -502,6 +561,7 @@ static int ipq_pcs_validate(struct phyli
+               phylink_clear(supported, Autoneg);
+               return 0;
+       case PHY_INTERFACE_MODE_USXGMII:
++      case PHY_INTERFACE_MODE_10G_QXGMII:
+               /* USXGMII only supports full duplex mode */
+               phylink_clear(supported, 100baseT_Half);
+               phylink_clear(supported, 10baseT_Half);
+@@ -519,6 +579,7 @@ static unsigned int ipq_pcs_inband_caps(
+       case PHY_INTERFACE_MODE_QSGMII:
+       case PHY_INTERFACE_MODE_1000BASEX:
+       case PHY_INTERFACE_MODE_USXGMII:
++      case PHY_INTERFACE_MODE_10G_QXGMII:
+               return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+       case PHY_INTERFACE_MODE_2500BASEX:
+       case PHY_INTERFACE_MODE_10GBASER:
+@@ -582,7 +643,8 @@ static void ipq_pcs_get_state(struct phy
+               ipq_pcs_get_state_2500basex(qpcs, state);
+               break;
+       case PHY_INTERFACE_MODE_USXGMII:
+-              ipq_pcs_get_state_usxgmii(qpcs, state);
++      case PHY_INTERFACE_MODE_10G_QXGMII:
++              ipq_pcs_get_state_usxgmii(qpcs, index, state);
+               break;
+       case PHY_INTERFACE_MODE_10GBASER:
+               ipq_pcs_get_state_10gbaser(qpcs, state);
+@@ -617,7 +679,8 @@ static int ipq_pcs_config(struct phylink
+       case PHY_INTERFACE_MODE_2500BASEX:
+               return ipq_pcs_config_2500basex(qpcs);
+       case PHY_INTERFACE_MODE_USXGMII:
+-              return ipq_pcs_config_usxgmii(qpcs);
++      case PHY_INTERFACE_MODE_10G_QXGMII:
++              return ipq_pcs_config_usxgmii(qpcs, index, interface);
+       case PHY_INTERFACE_MODE_10GBASER:
+               return ipq_pcs_config_10gbaser(qpcs);
+       default:
+@@ -646,7 +709,8 @@ static void ipq_pcs_link_up(struct phyli
+               ret = ipq_pcs_link_up_config_2500basex(qpcs, speed);
+               break;
+       case PHY_INTERFACE_MODE_USXGMII:
+-              ret = ipq_pcs_link_up_config_usxgmii(qpcs, speed);
++      case PHY_INTERFACE_MODE_10G_QXGMII:
++              ret = ipq_pcs_link_up_config_usxgmii(qpcs, index, speed);
+               break;
+       case PHY_INTERFACE_MODE_10GBASER:
+               /* Nothing to do here */
+@@ -731,6 +795,7 @@ static unsigned long ipq_pcs_clk_rate_ge
+       switch (qpcs->interface) {
+       case PHY_INTERFACE_MODE_2500BASEX:
+       case PHY_INTERFACE_MODE_USXGMII:
++      case PHY_INTERFACE_MODE_10G_QXGMII:
+       case PHY_INTERFACE_MODE_10GBASER:
+               return 312500000;
+       default:
diff --git a/target/linux/qualcommbe/patches-6.18/0365-net-pcs-ipq-uniphy-control-MISC2-register-for-2.5G-s.patch b/target/linux/qualcommbe/patches-6.18/0365-net-pcs-ipq-uniphy-control-MISC2-register-for-2.5G-s.patch
new file mode 100644 (file)
index 0000000..6ec8f26
--- /dev/null
@@ -0,0 +1,70 @@
+From 87da3bbd25eb0a17e2c698120528e76c26b326d0 Mon Sep 17 00:00:00 2001
+From: Mantas Pucka <mantas@8devices.com>
+Date: Mon, 2 Jun 2025 17:18:13 +0300
+Subject: [PATCH] net: pcs: ipq-uniphy: control MISC2 register for 2.5G support
+
+When 2500base-x mode is enabled MISC2 regsister needs to have different
+value than for other 1G modes.
+
+Signed-off-by: Mantas Pucka <mantas@8devices.com>
+---
+ drivers/net/pcs/pcs-qcom-ipq9574.c | 17 ++++++++++++++++-
+ 1 file changed, 16 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
++++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
+@@ -22,6 +22,11 @@
+ #define PCS_CALIBRATION                       0x1e0
+ #define PCS_CALIBRATION_DONE          BIT(7)
++#define PCS_MISC2                     0x218
++#define PCS_MISC2_MODE_MASK           GENMASK(6, 5)
++#define PCS_MISC2_MODE_SGMII          FIELD_PREP(PCS_MISC2_MODE_MASK, 0x1)
++#define PCS_MISC2_MODE_SGMII_PLUS     FIELD_PREP(PCS_MISC2_MODE_MASK, 0x2)
++
+ #define PCS_MODE_CTRL                 0x46c
+ #define PCS_MODE_SEL_MASK             GENMASK(12, 8)
+ #define PCS_MODE_SGMII                        FIELD_PREP(PCS_MODE_SEL_MASK, 0x4)
+@@ -275,7 +280,7 @@ static int ipq_pcs_config_mode(struct ip
+                              phy_interface_t interface)
+ {
+       unsigned long rate = 125000000;
+-      unsigned int val, mask;
++      unsigned int val, mask, misc2 = 0;
+       int ret;
+       /* Configure PCS interface mode */
+@@ -283,6 +288,7 @@ static int ipq_pcs_config_mode(struct ip
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+               val = PCS_MODE_SGMII;
++              misc2 = PCS_MISC2_MODE_SGMII;
+               break;
+       case PHY_INTERFACE_MODE_QSGMII:
+               val = PCS_MODE_QSGMII;
+@@ -290,9 +296,11 @@ static int ipq_pcs_config_mode(struct ip
+       case PHY_INTERFACE_MODE_1000BASEX:
+               mask |= PCS_MODE_SGMII_MODE_MASK;
+               val = PCS_MODE_SGMII | PCS_MODE_SGMII_MODE_1000BASEX;
++              misc2 = PCS_MISC2_MODE_SGMII;
+               break;
+       case PHY_INTERFACE_MODE_2500BASEX:
+               val = PCS_MODE_2500BASEX;
++              misc2 = PCS_MISC2_MODE_SGMII_PLUS;
+               rate = 312500000;
+               break;
+       case PHY_INTERFACE_MODE_USXGMII:
+@@ -315,6 +323,13 @@ static int ipq_pcs_config_mode(struct ip
+               if (ret)
+                       return ret;
+       }
++
++      if (misc2) {
++              ret = regmap_update_bits(qpcs->regmap, PCS_MISC2,
++                                       PCS_MISC2_MODE_MASK, misc2);
++              if (ret)
++                      return ret;
++      }
+       /* PCS PLL reset */
+       ret = regmap_clear_bits(qpcs->regmap, PCS_PLL_RESET, PCS_ANA_SW_RESET);
diff --git a/target/linux/qualcommbe/patches-6.18/0367-net-pcs-ipq-uniphy-fix-USXGMII-link-up-failure.patch b/target/linux/qualcommbe/patches-6.18/0367-net-pcs-ipq-uniphy-fix-USXGMII-link-up-failure.patch
new file mode 100644 (file)
index 0000000..b02782e
--- /dev/null
@@ -0,0 +1,24 @@
+From bedf56b46ae53c4abb21eebb3e1d5a7483926dda Mon Sep 17 00:00:00 2001
+From: Mantas Pucka <mantas@8devices.com>
+Date: Mon, 2 Jun 2025 17:20:58 +0300
+Subject: [PATCH] net: pcs: ipq-uniphy: fix USXGMII link-up failure
+
+USXGMII link-up may fail due to too short delay after PLL reset.
+Increase the delay to fix this.
+
+Signed-off-by: Mantas Pucka <mantas@8devices.com>
+---
+ drivers/net/pcs/pcs-qcom-ipq9574.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
++++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
+@@ -336,7 +336,7 @@ static int ipq_pcs_config_mode(struct ip
+       if (ret)
+               return ret;
+-      fsleep(1000);
++      fsleep(20000);
+       ret = regmap_set_bits(qpcs->regmap, PCS_PLL_RESET, PCS_ANA_SW_RESET);
+       if (ret)
+               return ret;
diff --git a/target/linux/qualcommbe/patches-6.18/0368-net-pcs-qcom-ipq9574-Update-IPQ9574-PCS-driver.patch b/target/linux/qualcommbe/patches-6.18/0368-net-pcs-qcom-ipq9574-Update-IPQ9574-PCS-driver.patch
new file mode 100644 (file)
index 0000000..63a523a
--- /dev/null
@@ -0,0 +1,282 @@
+From b4e07a8a3ec3dc5f676238987556e2aff0b14028 Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Mon, 29 Jan 2024 11:39:36 +0800
+Subject: [PATCH] net: pcs: qcom-ipq9574: Update IPQ9574 PCS driver
+
+Keep the PCS driver synced with the latest version posted to the kernel
+community and add the XPCS reset support.
+
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ .../bindings/net/pcs/qcom,ipq9574-pcs.yaml    |  7 ++
+ drivers/net/pcs/pcs-qcom-ipq9574.c            | 68 +++++++++++++++----
+ 2 files changed, 63 insertions(+), 12 deletions(-)
+
+--- a/Documentation/devicetree/bindings/net/pcs/qcom,ipq9574-pcs.yaml
++++ b/Documentation/devicetree/bindings/net/pcs/qcom,ipq9574-pcs.yaml
+@@ -98,6 +98,10 @@ properties:
+       - const: sys
+       - const: ahb
++  resets:
++    maxItems: 1
++    description: XPCS reset
++
+   '#clock-cells':
+     const: 1
+     description: See include/dt-bindings/net/qcom,ipq9574-pcs.h for constants
+@@ -137,6 +141,7 @@ required:
+   - '#size-cells'
+   - clocks
+   - clock-names
++  - resets
+   - '#clock-cells'
+ additionalProperties: false
+@@ -144,6 +149,7 @@ additionalProperties: false
+ examples:
+   - |
+     #include <dt-bindings/clock/qcom,ipq9574-gcc.h>
++    #include <dt-bindings/reset/qcom,ipq9574-gcc.h>
+     ethernet-pcs@7a00000 {
+         compatible = "qcom,ipq9574-pcs";
+@@ -154,6 +160,7 @@ examples:
+                  <&gcc GCC_UNIPHY0_AHB_CLK>;
+         clock-names = "sys",
+                       "ahb";
++        resets = <&gcc GCC_UNIPHY0_XPCS_RESET>;
+         #clock-cells = <1>;
+         pcs-mii@0 {
+--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
++++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
+@@ -13,6 +13,7 @@
+ #include <linux/phylink.h>
+ #include <linux/platform_device.h>
+ #include <linux/regmap.h>
++#include <linux/reset.h>
+ #include <dt-bindings/net/qcom,ipq9574-pcs.h>
+@@ -31,9 +32,12 @@
+ #define PCS_MODE_SEL_MASK             GENMASK(12, 8)
+ #define PCS_MODE_SGMII                        FIELD_PREP(PCS_MODE_SEL_MASK, 0x4)
+ #define PCS_MODE_QSGMII                       FIELD_PREP(PCS_MODE_SEL_MASK, 0x1)
++#define PCS_MODE_PSGMII                       FIELD_PREP(PCS_MODE_SEL_MASK, 0x2)
+ #define PCS_MODE_2500BASEX            FIELD_PREP(PCS_MODE_SEL_MASK, 0x8)
+ #define PCS_MODE_XPCS                 FIELD_PREP(PCS_MODE_SEL_MASK, 0x10)
+ #define PCS_MODE_SGMII_MODE_MASK      GENMASK(6, 4)
++#define PCS_MODE_SGMII_MODE_MAC               FIELD_PREP(PCS_MODE_SGMII_MODE_MASK, \
++                                                 0x2)
+ #define PCS_MODE_SGMII_MODE_1000BASEX FIELD_PREP(PCS_MODE_SGMII_MODE_MASK, \
+                                                  0x0)
+@@ -52,6 +56,8 @@
+ #define PCS_MII_STS_SPEED_10          0
+ #define PCS_MII_STS_SPEED_100         1
+ #define PCS_MII_STS_SPEED_1000                2
++#define PCS_MII_STS_PAUSE_TX_EN               BIT(1)
++#define PCS_MII_STS_PAUSE_RX_EN               BIT(0)
+ #define PCS_QP_USXG_OPTION            0x584
+ #define PCS_QP_USXG_GMII_SRC_XPCS     BIT(0)
+@@ -142,6 +148,7 @@ struct ipq_pcs {
+       struct clk_hw tx_hw;
+       struct ipq_pcs_mii *qpcs_mii[PCS_MAX_MII_NRS];
++      struct reset_control *xpcs_rstc;
+ };
+ #define phylink_pcs_to_qpcs_mii(_pcs) \
+@@ -184,6 +191,11 @@ static void ipq_pcs_get_state_sgmii(stru
+               state->duplex = DUPLEX_FULL;
+       else
+               state->duplex = DUPLEX_HALF;
++
++      if (val & PCS_MII_STS_PAUSE_TX_EN)
++              state->pause |= MLO_PAUSE_TX;
++      if (val & PCS_MII_STS_PAUSE_RX_EN)
++              state->pause |= MLO_PAUSE_RX;
+ }
+ static void ipq_pcs_get_state_2500basex(struct ipq_pcs *qpcs,
+@@ -198,7 +210,6 @@ static void ipq_pcs_get_state_2500basex(
+               return;
+       }
+-
+       state->link = !!(val & PCS_MII_LINK_STS);
+       if (!state->link)
+@@ -281,17 +292,27 @@ static int ipq_pcs_config_mode(struct ip
+ {
+       unsigned long rate = 125000000;
+       unsigned int val, mask, misc2 = 0;
++      bool xpcs_mode = false;
+       int ret;
++      /* Assert XPCS reset */
++      reset_control_assert(qpcs->xpcs_rstc);
++
+       /* Configure PCS interface mode */
+       mask = PCS_MODE_SEL_MASK;
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+-              val = PCS_MODE_SGMII;
++              mask |= PCS_MODE_SGMII_MODE_MASK;
++              val = PCS_MODE_SGMII | PCS_MODE_SGMII_MODE_MAC;
+               misc2 = PCS_MISC2_MODE_SGMII;
+               break;
+       case PHY_INTERFACE_MODE_QSGMII:
+-              val = PCS_MODE_QSGMII;
++              mask |= PCS_MODE_SGMII_MODE_MASK;
++              val = PCS_MODE_QSGMII | PCS_MODE_SGMII_MODE_MAC;
++              break;
++      case PHY_INTERFACE_MODE_PSGMII:
++              mask |= PCS_MODE_SGMII_MODE_MASK;
++              val = PCS_MODE_PSGMII | PCS_MODE_SGMII_MODE_MAC;
+               break;
+       case PHY_INTERFACE_MODE_1000BASEX:
+               mask |= PCS_MODE_SGMII_MODE_MASK;
+@@ -308,6 +329,7 @@ static int ipq_pcs_config_mode(struct ip
+       case PHY_INTERFACE_MODE_10GBASER:
+               val = PCS_MODE_XPCS;
+               rate = 312500000;
++              xpcs_mode = true;
+               break;
+       default:
+               return -EOPNOTSUPP;
+@@ -367,6 +389,10 @@ static int ipq_pcs_config_mode(struct ip
+               return ret;
+       }
++      /* Deassert XPCS */
++      if (xpcs_mode)
++              reset_control_deassert(qpcs->xpcs_rstc);
++
+       return 0;
+ }
+@@ -384,15 +410,13 @@ static int ipq_pcs_config_sgmii(struct i
+                       return ret;
+       }
+-      /* Nothing to do here as in-band autoneg mode is enabled
+-       * by default for each PCS MII port.
+-       */
++      /* Set AN mode or force mode */
+       if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
+-              return 0;
+-
+-      /* Set force speed mode */
+-      return regmap_set_bits(qpcs->regmap,
+-                             PCS_MII_CTRL(index), PCS_MII_FORCE_MODE);
++              return regmap_clear_bits(qpcs->regmap,
++                                       PCS_MII_CTRL(index), PCS_MII_FORCE_MODE);
++      else
++              return regmap_set_bits(qpcs->regmap,
++                                     PCS_MII_CTRL(index), PCS_MII_FORCE_MODE);
+ }
+ static int ipq_pcs_config_2500basex(struct ipq_pcs *qpcs)
+@@ -417,6 +441,10 @@ static int ipq_pcs_config_usxgmii(struct
+               if (ret)
+                       return ret;
++              ret = regmap_set_bits(qpcs->regmap, XPCS_DIG_CTRL, XPCS_USXG_EN);
++              if (ret)
++                      return ret;
++
+               if (interface == PHY_INTERFACE_MODE_10G_QXGMII) {
+                       ret = regmap_update_bits(qpcs->regmap, XPCS_KR_CTRL,
+                                                XPCS_USXG_MODE_MASK, XPCS_10G_QXGMII_MODE);
+@@ -432,6 +460,7 @@ static int ipq_pcs_config_usxgmii(struct
+                       ret = regmap_set_bits(qpcs->regmap, XPCS_DIG_CTRL, XPCS_SOFT_RESET);
+                       if (ret)
+                               return ret;
++              }
+       }
+       /* Disable Tx IPG check for 10G_QXGMII */
+@@ -559,7 +588,6 @@ static int ipq_pcs_link_up_config_usxgmi
+       reg = (index == 0) ? XPCS_DIG_CTRL : XPCS_MII1_DIG_CTRL(index);
+       val = (index == 0) ? XPCS_USXG_ADPT_RESET : XPCS_MII1_USXG_ADPT_RESET;
+       return regmap_set_bits(qpcs->regmap, reg, val);
+-
+ }
+ static int ipq_pcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
+@@ -568,6 +596,7 @@ static int ipq_pcs_validate(struct phyli
+       switch (state->interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
++      case PHY_INTERFACE_MODE_PSGMII:
+       case PHY_INTERFACE_MODE_1000BASEX:
+       case PHY_INTERFACE_MODE_10GBASER:
+               return 0;
+@@ -592,6 +621,7 @@ static unsigned int ipq_pcs_inband_caps(
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
++      case PHY_INTERFACE_MODE_PSGMII:
+       case PHY_INTERFACE_MODE_1000BASEX:
+       case PHY_INTERFACE_MODE_USXGMII:
+       case PHY_INTERFACE_MODE_10G_QXGMII:
+@@ -648,6 +678,7 @@ static void ipq_pcs_get_state(struct phy
+       switch (state->interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
++      case PHY_INTERFACE_MODE_PSGMII:
+       case PHY_INTERFACE_MODE_1000BASEX:
+               /* SGMII and 1000BASEX in-band autoneg word format are decoded
+                * by PCS hardware and both placed to the same status register.
+@@ -689,6 +720,7 @@ static int ipq_pcs_config(struct phylink
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
++      case PHY_INTERFACE_MODE_PSGMII:
+       case PHY_INTERFACE_MODE_1000BASEX:
+               return ipq_pcs_config_sgmii(qpcs, index, neg_mode, interface);
+       case PHY_INTERFACE_MODE_2500BASEX:
+@@ -703,6 +735,11 @@ static int ipq_pcs_config(struct phylink
+       };
+ }
++static void ipq_pcs_an_restart(struct phylink_pcs *pcs)
++{
++      /* Currently not used */
++}
++
+ static void ipq_pcs_link_up(struct phylink_pcs *pcs,
+                           unsigned int neg_mode,
+                           phy_interface_t interface,
+@@ -716,6 +753,7 @@ static void ipq_pcs_link_up(struct phyli
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
++      case PHY_INTERFACE_MODE_PSGMII:
+       case PHY_INTERFACE_MODE_1000BASEX:
+               ret = ipq_pcs_link_up_config_sgmii(qpcs, index,
+                                                  neg_mode, speed);
+@@ -746,6 +784,7 @@ static const struct phylink_pcs_ops ipq_
+       .pcs_disable = ipq_pcs_disable,
+       .pcs_get_state = ipq_pcs_get_state,
+       .pcs_config = ipq_pcs_config,
++      .pcs_an_restart = ipq_pcs_an_restart,
+       .pcs_link_up = ipq_pcs_link_up,
+ };
+@@ -990,6 +1029,11 @@ static int ipq9574_pcs_probe(struct plat
+               return dev_err_probe(dev, PTR_ERR(clk),
+                                    "Failed to enable AHB clock\n");
++      qpcs->xpcs_rstc = devm_reset_control_get_optional(dev, NULL);
++      if (IS_ERR_OR_NULL(qpcs->xpcs_rstc))
++              return dev_err_probe(dev, PTR_ERR(qpcs->xpcs_rstc),
++                                   "Failed to get XPCS reset\n");
++
+       ret = ipq_pcs_clk_register(qpcs);
+       if (ret)
+               return ret;
diff --git a/target/linux/qualcommbe/patches-6.18/0370-net-phy-Add-phy_package_remove_once-helper.patch b/target/linux/qualcommbe/patches-6.18/0370-net-phy-Add-phy_package_remove_once-helper.patch
new file mode 100644 (file)
index 0000000..e46c604
--- /dev/null
@@ -0,0 +1,36 @@
+From d11eba3e178a9d42a579c656b2c9b643f4ce3e1e Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Mon, 23 Sep 2024 18:46:34 +0800
+Subject: [PATCH] net: phy: Add phy_package_remove_once helper
+
+QCA8084 PHY package needs to do the PHY package clean up,
+add phy_package_remove_once helper to support.
+
+Change-Id: I3cd73bc7be1b1d531435ef72f48db0682548decf
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ include/linux/phy.h | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -366,6 +366,7 @@ struct phy_package_shared {
+ /* used as bit number in atomic bitops */
+ #define PHY_SHARED_F_INIT_DONE  0
+ #define PHY_SHARED_F_PROBE_DONE 1
++#define PHY_SHARED_F_REMOVE_DONE 2
+ /**
+  * struct mii_bus - Represents an MDIO bus
+@@ -2245,6 +2246,11 @@ static inline bool phy_package_probe_onc
+       return __phy_package_set_once(phydev, PHY_SHARED_F_PROBE_DONE);
+ }
++static inline bool phy_package_remove_once(struct phy_device *phydev)
++{
++      return __phy_package_set_once(phydev, PHY_SHARED_F_REMOVE_DONE);
++}
++
+ extern const struct bus_type mdio_bus_type;
+ struct mdio_board_info {
diff --git a/target/linux/qualcommbe/patches-6.18/0371-net-phy-qca808x-Add-QCA8084-SerDes-probe-and-remove-.patch b/target/linux/qualcommbe/patches-6.18/0371-net-phy-qca808x-Add-QCA8084-SerDes-probe-and-remove-.patch
new file mode 100644 (file)
index 0000000..a0c84bd
--- /dev/null
@@ -0,0 +1,437 @@
+From c12b79af730116936504afe97234f9afb6ac8fc0 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Mon, 23 Sep 2024 20:28:24 +0800
+Subject: [PATCH] net: phy: qca808x: Add QCA8084 SerDes probe and remove
+ functions
+
+QCA8084 PHY package integrates the XPCS and PCS, which is used
+to support 10G-QXGMII. XPCS includes 4 channels to connect with
+Quad PHY, and PCS controls the interface mode configured.
+
+XPCS and PCS are probed and removed by PHY package.
+
+Change-Id: Ided0a5cd4c996dc2a2a0d0598e930fab060caaf8
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Alex G: Use phy_package_get_*() accessors
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/phy/qcom/Makefile         |   2 +-
+ drivers/net/phy/qcom/qca8084_serdes.c | 249 ++++++++++++++++++++++++++
+ drivers/net/phy/qcom/qca8084_serdes.h |  18 ++
+ drivers/net/phy/qcom/qca808x.c        |  53 ++++++
+ 4 files changed, 321 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/phy/qcom/qca8084_serdes.c
+ create mode 100644 drivers/net/phy/qcom/qca8084_serdes.h
+
+--- a/drivers/net/phy/qcom/Makefile
++++ b/drivers/net/phy/qcom/Makefile
+@@ -2,5 +2,5 @@
+ obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o
+ obj-$(CONFIG_AT803X_PHY)      += at803x.o
+ obj-$(CONFIG_QCA83XX_PHY)     += qca83xx.o
+-obj-$(CONFIG_QCA808X_PHY)     += qca808x.o
++obj-$(CONFIG_QCA808X_PHY)     += qca808x.o qca8084_serdes.o
+ obj-$(CONFIG_QCA807X_PHY)     += qca807x.o
+--- /dev/null
++++ b/drivers/net/phy/qcom/qca8084_serdes.c
+@@ -0,0 +1,249 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#include <linux/clk.h>
++#include <linux/dev_printk.h>
++#include <linux/mdio.h>
++#include <linux/of.h>
++#include <linux/phy.h>
++#include <linux/property.h>
++#include <linux/reset.h>
++
++#include "qca8084_serdes.h"
++
++/* XPCS includes 4 channels, each channel has the different MMD ID for
++ * configuring auto-negotiation complete interrupt, mii-4bit, auto-
++ * negotiation capabilities and TX configuration for the connected PHY.
++ *
++ * MMD31 is for channel 0;
++ * MMD26 is for channel 1;
++ * MMD27 is for channel 2;
++ * MMD28 is for channel 3;
++ */
++#define QCA8084_CHANNEL_MAX                   4
++
++enum pcs_clk_id {
++      PCS_CLK,
++      PCS_RX_ROOT_CLK,
++      PCS_TX_ROOT_CLK,
++      PCS_CLK_MAX
++};
++
++enum xpcs_clk_id {
++      XPCS_XGMII_RX_CLK,
++      XPCS_XGMII_TX_CLK,
++      XPCS_RX_CLK,
++      XPCS_TX_CLK,
++      XPCS_PORT_RX_CLK,
++      XPCS_PORT_TX_CLK,
++      XPCS_RX_SRC_CLK,
++      XPCS_TX_SRC_CLK,
++      XPCS_CLK_MAX
++};
++
++struct qca8084_xpcs_channel_priv {
++      int ch_id;
++      struct reset_control *rstcs;
++      struct clk *clks[XPCS_CLK_MAX];
++};
++
++struct qca8084_pcs_data {
++      struct reset_control *rstc;
++      struct clk *clks[PCS_CLK_MAX];
++};
++
++struct qca8084_xpcs_data {
++      struct reset_control *rstc;
++      struct qca8084_xpcs_channel_priv xpcs_ch[QCA8084_CHANNEL_MAX];
++};
++
++static const char *const xpcs_clock_names[XPCS_CLK_MAX] = {
++      [XPCS_XGMII_RX_CLK] =   "xgmii_rx",
++      [XPCS_XGMII_TX_CLK] =   "xgmii_tx",
++      [XPCS_RX_CLK] =         "xpcs_rx",
++      [XPCS_TX_CLK] =         "xpcs_tx",
++      [XPCS_PORT_RX_CLK] =    "port_rx",
++      [XPCS_PORT_TX_CLK] =    "port_tx",
++      [XPCS_RX_SRC_CLK] =     "rx_src",
++      [XPCS_TX_SRC_CLK] =     "tx_src",
++};
++
++static const char *const pcs_clock_names[PCS_CLK_MAX] = {
++      [PCS_CLK] =             "pcs",
++      [PCS_RX_ROOT_CLK] =     "pcs_rx_root",
++      [PCS_TX_ROOT_CLK] =     "pcs_tx_root",
++};
++
++struct mdio_device *qca8084_package_pcs_probe(struct device_node *pcs_np)
++{
++      struct qca8084_pcs_data *pcs_data;
++      struct mdio_device *mdiodev;
++      struct reset_control *rstc;
++      struct device *dev;
++      struct clk *clk;
++      int i;
++
++      mdiodev = fwnode_mdio_find_device(of_fwnode_handle(pcs_np));
++      if (!mdiodev)
++              return ERR_PTR(-EPROBE_DEFER);
++
++      dev = &mdiodev->dev;
++      pcs_data = devm_kzalloc(dev, sizeof(*pcs_data), GFP_KERNEL);
++      if (!pcs_data) {
++              dev_err(dev, "Allocate PCS data failed\n");
++              return ERR_PTR(-ENOMEM);
++      }
++
++      rstc = devm_reset_control_get_exclusive(dev, NULL);
++      if (IS_ERR(rstc)) {
++              dev_err(dev, "Get PCS reset failed\n");
++              return ERR_CAST(rstc);
++      }
++
++      pcs_data->rstc = rstc;
++
++      for (i = 0; i < ARRAY_SIZE(pcs_clock_names); i++) {
++              clk = devm_clk_get(dev, pcs_clock_names[i]);
++              if (IS_ERR(clk)) {
++                      dev_err(dev, "Failed to get the PCS clock ID %s\n",
++                              pcs_clock_names[i]);
++                      return ERR_CAST(clk);
++              }
++              pcs_data->clks[i] = clk;
++      }
++
++      mdiodev_set_drvdata(mdiodev, pcs_data);
++
++      return mdiodev;
++}
++
++struct mdio_device *qca8084_package_xpcs_probe(struct device_node *xpcs_np)
++{
++      struct qca8084_xpcs_data *xpcs_data;
++      struct mdio_device *mdiodev;
++      struct reset_control *rstc;
++      struct device_node *child;
++      struct device *dev;
++      struct clk *clk;
++      int i, j, node;
++
++      mdiodev = fwnode_mdio_find_device(of_fwnode_handle(xpcs_np));
++      if (!mdiodev)
++              return ERR_PTR(-EPROBE_DEFER);
++
++      dev = &mdiodev->dev;
++
++      xpcs_data = devm_kzalloc(dev, sizeof(*xpcs_data), GFP_KERNEL);
++      if (!xpcs_data) {
++              dev_err(dev, "Allocate XPCS data failed\n");
++              return ERR_PTR(-ENOMEM);
++      }
++
++      rstc = devm_reset_control_get_exclusive(dev, NULL);
++      if (IS_ERR(rstc)) {
++              dev_err(dev, "Get XPCS reset failed\n");
++              return ERR_CAST(rstc);
++      }
++
++      xpcs_data->rstc = rstc;
++
++      /* Sanity check the number of channel sub nodes */
++      node = of_get_available_child_count(xpcs_np);
++      if (node != QCA8084_CHANNEL_MAX)
++              return ERR_PTR(-EINVAL);
++
++      node = 0;
++      for_each_available_child_of_node(xpcs_np, child) {
++              struct qca8084_xpcs_channel_priv *ch_data;
++              u32 channel;
++
++              /* The subnode name must be 'channel'. */
++              if (!(of_node_name_eq(child, "channel")))
++                      continue;
++
++              if (of_property_read_u32(child, "reg", &channel)) {
++                      dev_err(dev, "%s: Failed to get reg\n",
++                              child->full_name);
++
++                      mdiodev = ERR_PTR(-EINVAL);
++                      goto put_ch_clk_rst;
++              }
++
++              if (channel >= QCA8084_CHANNEL_MAX) {
++                      dev_err(dev, "%s: Invalid reg %d\n",
++                              child->full_name, channel);
++
++                      mdiodev = ERR_PTR(-EINVAL);
++                      goto put_ch_clk_rst;
++              }
++
++              ch_data = &xpcs_data->xpcs_ch[node];
++              ch_data->ch_id = channel;
++
++              ch_data->rstcs = of_reset_control_array_get_exclusive(child);
++              if (IS_ERR(ch_data->rstcs)) {
++                      dev_err(dev, "%s: Failed to get reset\n",
++                              child->full_name);
++
++                      mdiodev = ERR_CAST(ch_data->rstcs);
++                      goto put_ch_clk_rst;
++              }
++
++              for (j = 0; j < ARRAY_SIZE(xpcs_clock_names); j++) {
++                      clk = of_clk_get_by_name(child, xpcs_clock_names[j]);
++                      if (IS_ERR(clk)) {
++                              dev_err(dev, "Failed to get the clock ID %s\n",
++                                      xpcs_clock_names[j]);
++                              mdiodev = ERR_CAST(clk);
++                              goto put_ch_child;
++                      }
++                      ch_data->clks[j] = clk;
++              }
++
++              node++;
++      }
++
++      mdiodev_set_drvdata(mdiodev, xpcs_data);
++
++      return mdiodev;
++
++put_ch_child:
++      node++;
++
++put_ch_clk_rst:
++      for (i = 0; i < node; i++) {
++              j--;
++              while (j >= 0) {
++                      clk_put(xpcs_data->xpcs_ch[i].clks[j]);
++                      j--;
++              }
++
++              j = ARRAY_SIZE(xpcs_clock_names);
++      }
++
++      for (i = 0; i < node; i++)
++              reset_control_put(xpcs_data->xpcs_ch[i].rstcs);
++
++      of_node_put(child);
++
++      return mdiodev;
++}
++
++void qca8084_package_xpcs_and_pcs_remove(struct mdio_device *xpcs_mdiodev,
++                                       struct mdio_device *pcs_mdiodev)
++{
++      struct qca8084_xpcs_data *xpcs_data = mdiodev_get_drvdata(xpcs_mdiodev);
++      int i, j;
++
++      for (i = 0; i < ARRAY_SIZE(xpcs_data->xpcs_ch); i++) {
++              reset_control_put(xpcs_data->xpcs_ch[i].rstcs);
++
++              for (j = 0; j < ARRAY_SIZE(xpcs_data->xpcs_ch[i].clks); j++)
++                      clk_put(xpcs_data->xpcs_ch[i].clks[j]);
++      }
++
++      mdio_device_put(xpcs_mdiodev);
++      mdio_device_put(pcs_mdiodev);
++}
+--- /dev/null
++++ b/drivers/net/phy/qcom/qca8084_serdes.h
+@@ -0,0 +1,18 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Driver for QCA8084 SerDes
++ *
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef _QCA8084_SERDES_H_
++#define _QCA8084_SERDES_H_
++
++#include <linux/mdio.h>
++#include <linux/of.h>
++
++struct mdio_device *qca8084_package_pcs_probe(struct device_node *pcs_np);
++struct mdio_device *qca8084_package_xpcs_probe(struct device_node *xpcs_np);
++void qca8084_package_xpcs_and_pcs_remove(struct mdio_device *xpcs_mdiodev,
++                                       struct mdio_device *pcs_mdiodev);
++#endif /* _QCA8084_SERDES_H_ */
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -8,6 +8,7 @@
+ #include <linux/clk.h>
+ #include "../phylib.h"
++#include "qca8084_serdes.h"
+ #include "qcom.h"
+ /* ADC threshold */
+@@ -172,11 +173,13 @@ enum {
+ struct qca808x_priv {
+       int led_polarity_mode;
++      int channel_id;
+ };
+ struct qca808x_shared_priv {
+       int package_mode;
+       struct clk *clk[PACKAGE_CLK_MAX];
++      struct mdio_device *mdiodev[2]; /* PCS and XPCS mdio device */
+ };
+ static const char *const qca8084_package_clk_name[PACKAGE_CLK_MAX] = {
+@@ -354,6 +357,8 @@ static int qca808x_probe(struct phy_devi
+ {
+       struct device *dev = &phydev->mdio.dev;
+       struct qca808x_priv *priv;
++      u32 ch_id = 0;
++      int ret;
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+@@ -362,6 +367,14 @@ static int qca808x_probe(struct phy_devi
+       /* Init LED polarity mode to -1 */
+       priv->led_polarity_mode = -1;
++      /* DT property qcom,xpcs-channel" is optional and only available for
++       * 10G-QXGMII mode.
++       */
++      ret = of_property_read_u32(dev->of_node, "qcom,xpcs-channel", &ch_id);
++      if (ret && ret != -EINVAL)
++              return ret;
++
++      priv->channel_id = ch_id;
+       phydev->priv = priv;
+       return 0;
+@@ -1012,6 +1025,7 @@ static int qca8084_phy_package_probe_onc
+       struct device_node *np = phy_package_get_node(phydev);
+       struct qca808x_shared_priv *shared_priv;
+       struct reset_control *rstc;
++      struct device_node *child;
+       int i, ret, clear, set;
+       struct clk *clk;
+@@ -1072,6 +1086,26 @@ static int qca8084_phy_package_probe_onc
+       if (ret && ret != -EINVAL)
+               return ret;
++      for_each_available_child_of_node(np, child) {
++              struct mdio_device *mdiodev;
++
++              if (of_node_name_eq(child, "pcs-phy")) {
++                      mdiodev = qca8084_package_pcs_probe(child);
++                      if (IS_ERR(mdiodev))
++                              return PTR_ERR(mdiodev);
++
++                      shared_priv->mdiodev[0] = mdiodev;
++              }
++
++              if (of_node_name_eq(child, "xpcs-phy")) {
++                      mdiodev = qca8084_package_xpcs_probe(child);
++                      if (IS_ERR(mdiodev))
++                              return PTR_ERR(mdiodev);
++
++                      shared_priv->mdiodev[1] = mdiodev;
++              }
++      }
++
+       rstc = of_reset_control_get_exclusive(np, NULL);
+       if (IS_ERR(rstc))
+               return dev_err_probe(&phydev->mdio.dev, PTR_ERR(rstc),
+@@ -1081,6 +1115,14 @@ static int qca8084_phy_package_probe_onc
+       return reset_control_deassert(rstc);
+ }
++static void qca8084_phy_package_remove_once(struct phy_device *phydev)
++{
++      struct qca808x_shared_priv *shared_priv = phy_package_get_priv(phydev);;
++
++      qca8084_package_xpcs_and_pcs_remove(shared_priv->mdiodev[1],
++                                          shared_priv->mdiodev[0]);
++}
++
+ static int qca8084_probe(struct phy_device *phydev)
+ {
+       struct qca808x_shared_priv *shared_priv;
+@@ -1099,6 +1141,10 @@ static int qca8084_probe(struct phy_devi
+                       return ret;
+       }
++      ret = qca808x_probe(phydev);
++      if (ret)
++              return ret;
++
+       /* Enable clock of PHY device, so that the PHY register
+        * can be accessed to get PHY features.
+        */
+@@ -1116,6 +1162,12 @@ static int qca8084_probe(struct phy_devi
+       return reset_control_deassert(rstc);
+ }
++static void qca8084_remove(struct phy_device *phydev)
++{
++      if (phy_package_remove_once(phydev))
++              qca8084_phy_package_remove_once(phydev);
++}
++
+ static struct phy_driver qca808x_driver[] = {
+ {
+       /* Qualcomm QCA8081 */
+@@ -1167,6 +1219,7 @@ static struct phy_driver qca808x_driver[
+       .config_init            = qca8084_config_init,
+       .link_change_notify     = qca8084_link_change_notify,
+       .probe                  = qca8084_probe,
++      .remove                 = qca8084_remove,
+ }, };
+ module_phy_driver(qca808x_driver);
diff --git a/target/linux/qualcommbe/patches-6.18/0372-net-phy-qca808x-Add-QCA8084-SerDes-init-function.patch b/target/linux/qualcommbe/patches-6.18/0372-net-phy-qca808x-Add-QCA8084-SerDes-init-function.patch
new file mode 100644 (file)
index 0000000..688d7ac
--- /dev/null
@@ -0,0 +1,446 @@
+From d137b725f8f4a7d49a809dcd73c5b836495ec44d Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Mon, 23 Sep 2024 20:59:40 +0800
+Subject: [PATCH] net: phy: qca808x: Add QCA8084 SerDes init function
+
+When QCA8084 works on 10G-QXGMII, the XPCS and PCS need to be
+configured in the PHY package init function.
+
+Change-Id: Iac48c44f0e80adf055fa9c2095e99a04ba24c4bb
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ drivers/net/phy/qcom/qca8084_serdes.c | 374 ++++++++++++++++++++++++++
+ drivers/net/phy/qcom/qca8084_serdes.h |   2 +
+ drivers/net/phy/qcom/qca808x.c        |  11 +
+ 3 files changed, 387 insertions(+)
+
+--- a/drivers/net/phy/qcom/qca8084_serdes.c
++++ b/drivers/net/phy/qcom/qca8084_serdes.c
+@@ -24,6 +24,92 @@
+  */
+ #define QCA8084_CHANNEL_MAX                   4
++/* MII registers */
++#define PLL_POWER_ON_AND_RESET                        0x0
++#define PCS_ANA_SW_RESET                      BIT(6)
++
++#define PLL_CONTROL                           6
++#define PLL_CONTROL_CMLDIV2_IBSEL_MASK                GENMASK(5, 4)
++
++/* MMD_PMAPMD registers */
++#define CDR_CONTRL                            0x20
++#define SSC_FIX_MODE                          BIT(3)
++
++#define CALIBRATION4                          0x78
++#define CALIBRATION_DONE                      BIT(7)
++
++#define MODE_CONTROL                          0x11b
++#define MODE_CONTROL_SEL_MASK                 GENMASK(12, 8)
++#define MODE_CONTROL_XPCS                     0x10
++#define MODE_CONTROL_SGMII_PLUS                       0x8
++#define MODE_CONTROL_SGMII                    0x4
++#define MODE_CONTROL_SGMII_SEL_MASK           GENMASK(6, 4)
++#define MODE_CONTROL_SGMII_PHY                        1
++#define MODE_CONTROL_SGMII_MAC                        2
++
++#define QP_USXG_OPTION1                               0x180
++#define QP_USXG_OPTION1_DATAPASS              BIT(0)
++#define QP_USXG_OPTION1_DATAPASS_SGMII                0
++#define QP_USXG_OPTION1_DATAPASS_USXGMII      1
++
++#define BYPASS_TUNNING_IPG                    0x189
++#define BYPASS_TUNNING_IPG_MASK                       GENMASK(11, 0)
++
++/* MDIO_MMD_PCS register */
++#define PCS_CONTROL2                          0x7
++#define PCS_TYPE_MASK                         GENMASK(3, 0)
++#define PCS_TYPE_BASER                                0
++
++#define PCS_EEE_CONTROL                               0x14
++#define EEE_CAPABILITY                                BIT(6)
++
++#define PCS_STATUS1                           0x20
++#define PCS_BASER_UP                          BIT(12)
++
++#define DIG_CTRL1                             0x8000
++#define DIG_CTRL1_USXGMII_EN                  BIT(9)
++#define DIG_CTRL1_XPCS_RESET                  BIT(15)
++#define FIFO_RESET_CH0                                BIT(10)
++#define FIFO_RESET_CH1_CH2_CH3                        BIT(5)
++
++#define EEE_MODE_CONTROL                      0x8006
++#define EEE_LCT_RES                           GENMASK(11, 8)
++#define EEE_SIGN                              BIT(6)
++#define EEE_LRX_EN                            BIT(1)
++#define EEE_LTX_EN                            BIT(0)
++
++#define PCS_TPC                                       0x8007
++#define PCS_QXGMII_MODE_MASK                  GENMASK(12, 10)
++#define PCS_QXGMII_EN                         0x5
++
++#define EEE_RX_TIMER                          0x8009
++#define EEE_RX_TIMER_100US_RES                        GENMASK(7, 0)
++#define EEE_RX_TIMER_RWR_RES                  GENMASK(12, 8)
++
++#define AM_LINK_TIMER                         0x800a
++#define AM_LINK_TIMER_VAL                     0x6018
++
++#define EEE_MODE_CONTROL1                     0x800b
++#define TRANS_LPI_MODE                                BIT(0)
++#define TRANS_RX_LPI_MODE                     BIT(8)
++
++/* QXGMII channel MMD register */
++#define MII_CONTROL                           0x0
++#define AUTO_NEGOTIATION_EN                   BIT(12)
++#define AUTO_NEGOTIATION_RESTART              BIT(9)
++#define PCS_SPEED_2500                                BIT(5)
++#define PCS_SPEED_1000                                BIT(6)
++#define PCS_SPEED_100                         BIT(13)
++#define PCS_SPEED_10                          0
++
++#define DIG_CONTROL2                          0x8001
++#define MII_BIT_CONTROL                               BIT(8)
++#define TX_CONFIG                             BIT(3)
++#define AUTO_NEGOTIATION_CMPLT_INTR           BIT(0)
++
++#define XAUI_CONTROL                          0x8004
++#define TX_IPG_CHECK_DISABLE                  BIT(0)
++
+ enum pcs_clk_id {
+       PCS_CLK,
+       PCS_RX_ROOT_CLK,
+@@ -76,6 +162,8 @@ static const char *const pcs_clock_names
+       [PCS_TX_ROOT_CLK] =     "pcs_tx_root",
+ };
++static const int qca8084_xpcs_ch_mmd[QCA8084_CHANNEL_MAX] = { 31, 26, 27, 28 };
++
+ struct mdio_device *qca8084_package_pcs_probe(struct device_node *pcs_np)
+ {
+       struct qca8084_pcs_data *pcs_data;
+@@ -247,3 +335,289 @@ void qca8084_package_xpcs_and_pcs_remove
+       mdio_device_put(xpcs_mdiodev);
+       mdio_device_put(pcs_mdiodev);
+ }
++
++static int qca8084_pcs_set_interface_mode(struct mdio_device *mdio_dev,
++                                        phy_interface_t ifmode)
++{
++      int ret, hw_ifmode, data;
++
++      switch (ifmode) {
++      case PHY_INTERFACE_MODE_SGMII:
++              hw_ifmode = MODE_CONTROL_SGMII;
++              data = QP_USXG_OPTION1_DATAPASS_SGMII;
++              break;
++      case PHY_INTERFACE_MODE_2500BASEX:
++              hw_ifmode = MODE_CONTROL_SGMII_PLUS;
++              data = QP_USXG_OPTION1_DATAPASS_SGMII;
++              break;
++      case PHY_INTERFACE_MODE_10G_QXGMII:
++              hw_ifmode = MODE_CONTROL_XPCS;
++              data = QP_USXG_OPTION1_DATAPASS_USXGMII;
++              break;
++      default:
++              return -EOPNOTSUPP;
++      }
++
++      /* For PLL stable under high temperature */
++      ret = mdiodev_modify(mdio_dev, PLL_CONTROL,
++                           PLL_CONTROL_CMLDIV2_IBSEL_MASK,
++                           FIELD_PREP(PLL_CONTROL_CMLDIV2_IBSEL_MASK, 3));
++      if (ret)
++              return ret;
++
++      /* Configure the interface mode of PCS */
++      ret = mdiodev_c45_modify(mdio_dev, MDIO_MMD_PMAPMD, MODE_CONTROL,
++                               MODE_CONTROL_SEL_MASK,
++                               FIELD_PREP(MODE_CONTROL_SEL_MASK, hw_ifmode));
++      if (ret)
++              return ret;
++
++      /* Data pass selects SGMII or USXGMII */
++      return mdiodev_c45_modify(mdio_dev, MDIO_MMD_PMAPMD, QP_USXG_OPTION1,
++                                QP_USXG_OPTION1_DATAPASS,
++                                FIELD_PREP(QP_USXG_OPTION1_DATAPASS, data));
++}
++
++static int qca8084_do_calibration(struct mdio_device *mdio_dev)
++{
++      int ret;
++
++      ret = mdiodev_modify(mdio_dev, PLL_POWER_ON_AND_RESET,
++                           PCS_ANA_SW_RESET, 0);
++      if (ret)
++              return ret;
++
++      usleep_range(10000, 11000);
++      ret = mdiodev_modify(mdio_dev, PLL_POWER_ON_AND_RESET,
++                           PCS_ANA_SW_RESET, PCS_ANA_SW_RESET);
++      if (ret)
++              return ret;
++
++      /* Wait calibration done */
++      return read_poll_timeout(mdiodev_c45_read, ret,
++                               (ret & CALIBRATION_DONE),
++                               100, 100000, true, mdio_dev,
++                               MDIO_MMD_PMAPMD, CALIBRATION4);
++}
++
++
++static int qca8084_xpcs_set_mode(struct mdio_device *xpcs_mdiodev)
++{
++      int ret, val, i;
++
++      /* Configure BaseR mode */
++      ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS, PCS_CONTROL2,
++                               PCS_TYPE_MASK,
++                               FIELD_PREP(PCS_TYPE_MASK, PCS_TYPE_BASER));
++      if (ret)
++              return ret;
++
++      /* Wait BaseR link up */
++      ret = read_poll_timeout(mdiodev_c45_read, val,
++                              (val & PCS_BASER_UP), 1000, 100000, true,
++                              xpcs_mdiodev,
++                              MDIO_MMD_PCS, PCS_STATUS1);
++      if (ret) {
++              dev_err(&xpcs_mdiodev->dev, "BaseR link failed!\n");
++              return ret;
++      }
++
++      /* Enable USXGMII mode */
++      ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS, DIG_CTRL1,
++                               DIG_CTRL1_USXGMII_EN,
++                               DIG_CTRL1_USXGMII_EN);
++      if (ret)
++              return ret;
++
++      /* Configure QXGMII mode */
++      ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS, PCS_TPC,
++                               PCS_QXGMII_MODE_MASK,
++                               FIELD_PREP(PCS_QXGMII_MODE_MASK,
++                                          PCS_QXGMII_EN));
++      if (ret)
++              return ret;
++
++      /* Configure AM interval */
++      ret = mdiodev_c45_write(xpcs_mdiodev, MDIO_MMD_PCS, AM_LINK_TIMER,
++                              AM_LINK_TIMER_VAL);
++      if (ret)
++              return ret;
++
++      /* Reset XPCS */
++      ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS, DIG_CTRL1,
++                               DIG_CTRL1_XPCS_RESET,
++                               DIG_CTRL1_XPCS_RESET);
++      if (ret)
++              return ret;
++
++      /* Wait XPCS reset done */
++      ret = read_poll_timeout(mdiodev_c45_read, val,
++                              !(val & DIG_CTRL1_XPCS_RESET),
++                              1000, 100000, true, xpcs_mdiodev,
++                              MDIO_MMD_PCS, DIG_CTRL1);
++      if (ret) {
++              dev_err(&xpcs_mdiodev->dev, "XPCS reset failed!\n");
++              return ret;
++      }
++
++      /* Enable auto-negotiation complete interrupt, using mii-4bit
++       * and TX configureation of PHY side on all XPCS channels.
++       */
++      for (i = 0; i < QCA8084_CHANNEL_MAX; i++) {
++              ret = mdiodev_c45_modify(xpcs_mdiodev, qca8084_xpcs_ch_mmd[i],
++                                       DIG_CONTROL2,
++                                       (MII_BIT_CONTROL | TX_CONFIG |
++                                       AUTO_NEGOTIATION_CMPLT_INTR),
++                                       (TX_CONFIG | AUTO_NEGOTIATION_CMPLT_INTR));
++              if (ret)
++                      return ret;
++
++              /* Enable auto-negotiation capability */
++              ret = mdiodev_c45_modify(xpcs_mdiodev, qca8084_xpcs_ch_mmd[i],
++                                       MII_CONTROL,
++                                       AUTO_NEGOTIATION_EN,
++                                       AUTO_NEGOTIATION_EN);
++              if (ret)
++                      return ret;
++
++              /* Disable TX IPG check */
++              ret = mdiodev_c45_modify(xpcs_mdiodev, qca8084_xpcs_ch_mmd[i],
++                                       XAUI_CONTROL,
++                                       TX_IPG_CHECK_DISABLE,
++                                       TX_IPG_CHECK_DISABLE);
++              if (ret)
++                      return ret;
++      }
++
++      /* Check EEE capability supported or not */
++      ret = mdiodev_c45_read(xpcs_mdiodev, MDIO_MMD_PCS, PCS_EEE_CONTROL);
++      if (ret < 0)
++              return ret;
++
++      if (ret & EEE_CAPABILITY) {
++              ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS,
++                                       EEE_MODE_CONTROL,
++                                       EEE_LCT_RES | EEE_SIGN,
++                                       FIELD_PREP(EEE_LCT_RES, 1) | EEE_SIGN);
++              if (ret)
++                      return ret;
++
++              ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS,
++                                       EEE_RX_TIMER,
++                                       EEE_RX_TIMER_100US_RES | EEE_RX_TIMER_RWR_RES,
++                                       FIELD_PREP(EEE_RX_TIMER_100US_RES, 0xc8) |
++                                       FIELD_PREP(EEE_RX_TIMER_RWR_RES, 0x1c));
++              if (ret)
++                      return ret;
++
++              /* Enable EEE LPI */
++              ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS,
++                                       EEE_MODE_CONTROL1,
++                                       TRANS_LPI_MODE | TRANS_RX_LPI_MODE,
++                                       TRANS_LPI_MODE | TRANS_RX_LPI_MODE);
++              if (ret)
++                      return ret;
++
++              /* Enable TX/RX LPI pattern */
++              ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS,
++                                       EEE_MODE_CONTROL,
++                                       EEE_LRX_EN | EEE_LTX_EN,
++                                       EEE_LRX_EN | EEE_LTX_EN);
++      }
++
++      return ret;
++}
++
++static int qca8084_pcs_set_mode(struct mdio_device *xpcs_mdiodev,
++                                     struct mdio_device *pcs_mdiodev)
++{
++      struct qca8084_xpcs_data *xpcs_data = mdiodev_get_drvdata(xpcs_mdiodev);
++      struct qca8084_pcs_data *pcs_data = mdiodev_get_drvdata(pcs_mdiodev);
++      struct qca8084_xpcs_channel_priv xpcs_ch;
++      int ret, channel;
++
++      /* Enable clock and de-assert for PCS. */
++      ret = clk_prepare_enable(pcs_data->clks[PCS_CLK]);
++      if (ret)
++              return ret;
++
++      ret = reset_control_deassert(pcs_data->rstc);
++      if (ret)
++              return ret;
++
++      /* IPG tunning selection for RX, TX and XGMII of all channels. */
++      ret = mdiodev_c45_modify(pcs_mdiodev, MDIO_MMD_PMAPMD,
++                               BYPASS_TUNNING_IPG,
++                               BYPASS_TUNNING_IPG_MASK, 0);
++      if (ret)
++              return ret;
++
++      reset_control_assert(xpcs_data->rstc);
++
++      ret = qca8084_pcs_set_interface_mode(pcs_mdiodev,
++                                           PHY_INTERFACE_MODE_10G_QXGMII);
++      if (ret)
++              return ret;
++
++      /* Reset of 4 channels */
++      for (channel = 0; channel < QCA8084_CHANNEL_MAX; channel++) {
++              xpcs_ch = xpcs_data->xpcs_ch[channel];
++              ret = reset_control_reset(xpcs_ch.rstcs);
++              if (ret)
++                      return ret;
++      }
++
++      ret = qca8084_do_calibration(pcs_mdiodev);
++      if (ret) {
++              dev_err(&pcs_mdiodev->dev, "PCS calibration timeout!\n");
++              return ret;
++      }
++
++      /* Enable PCS SSC to fix mode */
++      ret = mdiodev_c45_modify(pcs_mdiodev, MDIO_MMD_PMAPMD,
++                               CDR_CONTRL, SSC_FIX_MODE, SSC_FIX_MODE);
++      if (ret)
++              return ret;
++
++      return reset_control_deassert(xpcs_data->rstc);
++}
++
++static int qca8084_xpcs_clock_parent_set(struct mdio_device *xpcs_mdiodev,
++                                       struct mdio_device *pcs_mdiodev)
++{
++      struct qca8084_xpcs_data *xpcs_data = mdiodev_get_drvdata(xpcs_mdiodev);
++      struct qca8084_pcs_data *pcs_data = mdiodev_get_drvdata(pcs_mdiodev);
++      struct qca8084_xpcs_channel_priv xpcs_ch;
++      int ret, channel;
++
++      for (channel = 0; channel < QCA8084_CHANNEL_MAX; channel++) {
++              xpcs_ch = xpcs_data->xpcs_ch[channel];
++              ret = clk_set_parent(xpcs_ch.clks[XPCS_RX_SRC_CLK],
++                                   pcs_data->clks[PCS_RX_ROOT_CLK]);
++              if (ret)
++                      return ret;
++
++              ret = clk_set_parent(xpcs_ch.clks[XPCS_TX_SRC_CLK],
++                                   pcs_data->clks[PCS_TX_ROOT_CLK]);
++              if (ret)
++                      return ret;
++      }
++
++      return 0;
++}
++
++int qca8084_qxgmii_set_mode(struct mdio_device *xpcs_mdiodev,
++                          struct mdio_device *pcs_mdiodev)
++{
++      int ret;
++
++      ret = qca8084_xpcs_clock_parent_set(xpcs_mdiodev, pcs_mdiodev);
++      if (ret)
++              return ret;
++
++      ret = qca8084_pcs_set_mode(xpcs_mdiodev, pcs_mdiodev);
++      if (ret)
++              return ret;
++
++      return qca8084_xpcs_set_mode(xpcs_mdiodev);
++}
+--- a/drivers/net/phy/qcom/qca8084_serdes.h
++++ b/drivers/net/phy/qcom/qca8084_serdes.h
+@@ -15,4 +15,6 @@ struct mdio_device *qca8084_package_pcs_
+ struct mdio_device *qca8084_package_xpcs_probe(struct device_node *xpcs_np);
+ void qca8084_package_xpcs_and_pcs_remove(struct mdio_device *xpcs_mdiodev,
+                                        struct mdio_device *pcs_mdiodev);
++int qca8084_qxgmii_set_mode(struct mdio_device *xpcs_mdiodev,
++                          struct mdio_device *pcs_mdiodev);
+ #endif /* _QCA8084_SERDES_H_ */
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -926,6 +926,14 @@ static int qca8084_phy_package_config_in
+       usleep_range(10000, 11000);
++      /* Configure PCS working on 10G-QXGMII mode */
++      if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII) {
++              ret = qca8084_qxgmii_set_mode(shared_priv->mdiodev[1],
++                                            shared_priv->mdiodev[0]);
++              if (ret)
++                      return ret;
++      }
++
+       /* Initialize the PHY package clock and reset, which is the
+        * necessary config sequence after GPIO reset on the PHY package.
+        */
+@@ -1164,6 +1172,9 @@ static int qca8084_probe(struct phy_devi
+ static void qca8084_remove(struct phy_device *phydev)
+ {
++      if (phydev->interface != PHY_INTERFACE_MODE_10G_QXGMII)
++              return;
++
+       if (phy_package_remove_once(phydev))
+               qca8084_phy_package_remove_once(phydev);
+ }
diff --git a/target/linux/qualcommbe/patches-6.18/0373-net-phy-qca808x-Add-QCA8084-SerDes-speed-config.patch b/target/linux/qualcommbe/patches-6.18/0373-net-phy-qca808x-Add-QCA8084-SerDes-speed-config.patch
new file mode 100644 (file)
index 0000000..1244837
--- /dev/null
@@ -0,0 +1,251 @@
+From 2f5b7e167d847a5b5b74a91f991d48635453c55f Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Mon, 23 Sep 2024 21:24:56 +0800
+Subject: [PATCH] net: phy: qca808x: Add QCA8084 SerDes speed config
+
+When the link of PHY is changed, the XPCS channel needs to be
+configured to adapt the current link status.
+
+Change-Id: I50d8973691dff133fc6bec1e9a1043bb646811fc
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Alex G: Use phy_package_get_*() accessors
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/phy/qcom/qca8084_serdes.c | 159 ++++++++++++++++++++++++++
+ drivers/net/phy/qcom/qca8084_serdes.h |   3 +
+ drivers/net/phy/qcom/qca808x.c        |  15 ++-
+ 3 files changed, 175 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/qcom/qca8084_serdes.c
++++ b/drivers/net/phy/qcom/qca8084_serdes.c
+@@ -4,6 +4,7 @@
+  */
+ #include <linux/clk.h>
++#include <linux/clk-provider.h>
+ #include <linux/dev_printk.h>
+ #include <linux/mdio.h>
+ #include <linux/of.h>
+@@ -55,6 +56,13 @@
+ #define BYPASS_TUNNING_IPG                    0x189
+ #define BYPASS_TUNNING_IPG_MASK                       GENMASK(11, 0)
++#define QP_USXG_RESET                         0x18c
++#define QP_USXG_SGMII_FUNC_RESET              BIT(4)
++#define QP_USXG_P3_FUNC_RESET                 BIT(3)
++#define QP_USXG_P2_FUNC_RESET                 BIT(2)
++#define QP_USXG_P1_FUNC_RESET                 BIT(1)
++#define QP_USXG_P0_FUNC_RESET                 BIT(0)
++
+ /* MDIO_MMD_PCS register */
+ #define PCS_CONTROL2                          0x7
+ #define PCS_TYPE_MASK                         GENMASK(3, 0)
+@@ -107,6 +115,9 @@
+ #define TX_CONFIG                             BIT(3)
+ #define AUTO_NEGOTIATION_CMPLT_INTR           BIT(0)
++#define PCS_ERR_SEL                           0x8002
++#define PCS_AN_COMPLETE                               BIT(0)
++
+ #define XAUI_CONTROL                          0x8004
+ #define TX_IPG_CHECK_DISABLE                  BIT(0)
+@@ -621,3 +632,151 @@ int qca8084_qxgmii_set_mode(struct mdio_
+       return qca8084_xpcs_set_mode(xpcs_mdiodev);
+ }
++
++static int qca8084_pcs_ipg_tune_reset(struct mdio_device *mdio_dev,
++                                    int reset_function)
++{
++      int ret;
++
++      ret = mdiodev_c45_modify(mdio_dev, MDIO_MMD_PMAPMD, QP_USXG_RESET,
++                               reset_function, 0);
++      if (ret)
++              return ret;
++
++      usleep_range(1000, 1100);
++
++      return mdiodev_c45_modify(mdio_dev, MDIO_MMD_PMAPMD, QP_USXG_RESET,
++                                reset_function, reset_function);
++}
++
++static int qca8084_xpcs_an_restart(struct mdio_device *xpcs_mdiodev,
++                                 int channel)
++{
++      int ret, mmd;
++
++      mmd = qca8084_xpcs_ch_mmd[channel];
++
++      /* Restart auto-negotiation */
++      ret = mdiodev_c45_modify(xpcs_mdiodev, mmd, MII_CONTROL,
++                               AUTO_NEGOTIATION_RESTART,
++                               AUTO_NEGOTIATION_RESTART);
++      if (ret)
++              return ret;
++
++      usleep_range(1000, 1100);
++
++      /* Clear pcs auto-negotiation complete interrupt */
++      return mdiodev_c45_modify(xpcs_mdiodev, mmd, PCS_ERR_SEL,
++                                PCS_AN_COMPLETE, 0);
++}
++
++void qca8084_qxgmii_set_speed(struct mdio_device *xpcs_mdiodev,
++                            struct mdio_device *pcs_mdiodev,
++                            int channel, int speed)
++{
++      struct qca8084_xpcs_data *xpcs_data = mdiodev_get_drvdata(xpcs_mdiodev);
++      struct qca8084_xpcs_channel_priv *xpcs_ch;
++      int mmd, i, ret, xpcs_rate;
++      unsigned long rate;
++
++      for (i = 0; i < QCA8084_CHANNEL_MAX; i++) {
++              xpcs_ch = &(xpcs_data->xpcs_ch[channel]);
++              if (channel == xpcs_ch->ch_id)
++                      break;
++      }
++
++      if (i == QCA8084_CHANNEL_MAX) {
++              dev_err(&xpcs_mdiodev->dev, "Invalid channel %d\n", channel);
++              return;
++      }
++
++      mmd = qca8084_xpcs_ch_mmd[channel];
++
++      ret = qca8084_xpcs_an_restart(xpcs_mdiodev, channel);
++      if (ret)
++              return;
++
++      switch (speed) {
++      case SPEED_2500:
++              rate = 312500000;
++              xpcs_rate = PCS_SPEED_2500;
++              break;
++      case SPEED_1000:
++              rate = 125000000;
++              xpcs_rate = PCS_SPEED_1000;
++              break;
++      case SPEED_100:
++              rate = 25000000;
++              xpcs_rate = PCS_SPEED_100;
++              break;
++      case SPEED_10:
++      default:
++              rate = 2500000;
++              xpcs_rate = PCS_SPEED_10;
++              break;
++      }
++
++      clk_set_rate(xpcs_ch->clks[XPCS_RX_CLK], rate);
++      clk_set_rate(xpcs_ch->clks[XPCS_TX_CLK], rate);
++
++      /* XGMII takes the different clock rate 78.125Mhz from XPCS clock
++       * when linked at 2500M.
++       */
++      if (speed == SPEED_2500)
++              rate = 78125000;
++
++      clk_set_rate(xpcs_ch->clks[XPCS_XGMII_RX_CLK], rate);
++      clk_set_rate(xpcs_ch->clks[XPCS_XGMII_TX_CLK], rate);
++
++      ret = mdiodev_c45_modify(xpcs_mdiodev, mmd, MII_CONTROL,
++                               PCS_SPEED_2500 | PCS_SPEED_1000 |
++                               PCS_SPEED_100 | PCS_SPEED_10,
++                               xpcs_rate);
++      if (ret)
++              return;
++
++      /* Disable clocks if link down with unknown speed. The channel clocks
++       * are disabled by default, __clk_is_enabled() is used to avoid
++       * disabling the clocks that is already in the disabled status.
++       */
++      if (speed == SPEED_UNKNOWN) {
++              if (__clk_is_enabled(xpcs_ch->clks[XPCS_RX_CLK]))
++                      clk_disable_unprepare(xpcs_ch->clks[XPCS_RX_CLK]);
++              if (__clk_is_enabled(xpcs_ch->clks[XPCS_TX_CLK]))
++                      clk_disable_unprepare(xpcs_ch->clks[XPCS_TX_CLK]);
++              if (__clk_is_enabled(xpcs_ch->clks[XPCS_PORT_RX_CLK]))
++                      clk_disable_unprepare(xpcs_ch->clks[XPCS_PORT_RX_CLK]);
++              if (__clk_is_enabled(xpcs_ch->clks[XPCS_PORT_TX_CLK]))
++                      clk_disable_unprepare(xpcs_ch->clks[XPCS_PORT_TX_CLK]);
++              if (__clk_is_enabled(xpcs_ch->clks[XPCS_XGMII_RX_CLK]))
++                      clk_disable_unprepare(xpcs_ch->clks[XPCS_XGMII_RX_CLK]);
++              if (__clk_is_enabled(xpcs_ch->clks[XPCS_XGMII_TX_CLK]))
++                      clk_disable_unprepare(xpcs_ch->clks[XPCS_XGMII_TX_CLK]);
++      } else {
++              clk_prepare_enable(xpcs_ch->clks[XPCS_RX_CLK]);
++              clk_prepare_enable(xpcs_ch->clks[XPCS_TX_CLK]);
++              clk_prepare_enable(xpcs_ch->clks[XPCS_PORT_RX_CLK]);
++              clk_prepare_enable(xpcs_ch->clks[XPCS_PORT_TX_CLK]);
++              clk_prepare_enable(xpcs_ch->clks[XPCS_XGMII_RX_CLK]);
++              clk_prepare_enable(xpcs_ch->clks[XPCS_XGMII_TX_CLK]);
++      }
++
++      msleep(100);
++
++      ret = reset_control_reset(xpcs_ch->rstcs);
++      if (ret)
++              return;
++
++      /* Reset IPG tune of PCS device. */
++      ret = qca8084_pcs_ipg_tune_reset(pcs_mdiodev, BIT(channel));
++      if (ret)
++              return;
++
++      if (channel == 0)
++              mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS, DIG_CTRL1,
++                                 FIFO_RESET_CH0, FIFO_RESET_CH0);
++      else
++              mdiodev_c45_modify(xpcs_mdiodev, mmd, DIG_CTRL1,
++                                 FIFO_RESET_CH1_CH2_CH3,
++                                 FIFO_RESET_CH1_CH2_CH3);
++}
+--- a/drivers/net/phy/qcom/qca8084_serdes.h
++++ b/drivers/net/phy/qcom/qca8084_serdes.h
+@@ -17,4 +17,7 @@ void qca8084_package_xpcs_and_pcs_remove
+                                        struct mdio_device *pcs_mdiodev);
+ int qca8084_qxgmii_set_mode(struct mdio_device *xpcs_mdiodev,
+                           struct mdio_device *pcs_mdiodev);
++void qca8084_qxgmii_set_speed(struct mdio_device *xpcs_mdiodev,
++                            struct mdio_device *pcs_mdiodev,
++                            int channel, int speed);
+ #endif /* _QCA8084_SERDES_H_ */
+--- a/drivers/net/phy/qcom/qca808x.c
++++ b/drivers/net/phy/qcom/qca808x.c
+@@ -976,6 +976,7 @@ static int qca8084_config_init(struct ph
+ static void qca8084_link_change_notify(struct phy_device *phydev)
+ {
++      struct qca808x_shared_priv *shared_priv;
+       int ret;
+       /* Assert the FIFO between PHY and MAC. */
+@@ -1007,14 +1008,24 @@ static void qca8084_link_change_notify(s
+               }
+       }
+-      /* Enable IPG level 10 to 11 tuning for link speed 1000M in the
++      /* Enable IPG level 10 to 11 tuning for link speed 1000M and
++       * configure the related XPCS channel with the phydev in the
+        * 10G_QXGMII mode.
+        */
+-      if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII)
++      if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII) {
++              shared_priv = phy_package_get_priv(phydev);
++              struct qca808x_priv *priv = phydev->priv;
++
+               phy_modify_mmd(phydev, MDIO_MMD_AN, QCA8084_MMD7_IPG_OP,
+                              QCA8084_IPG_10_TO_11_EN,
+                              phydev->speed == SPEED_1000 ?
+                              QCA8084_IPG_10_TO_11_EN : 0);
++
++              qca8084_qxgmii_set_speed(shared_priv->mdiodev[1],
++                                       shared_priv->mdiodev[0],
++                                       priv->channel_id,
++                                       phydev->speed);
++      }
+ }
+ /* QCA8084 is a four-port PHY, which integrates the clock controller,